
import { __decorate, __param } from '../../../../../../../../external/tslib/tslib.es6.js';
import { findLast } from '../../../../../base/common/arraysFind.js';
import { timeout } from '../../../../../base/common/async.js';
import { CancellationToken } from '../../../../../base/common/cancellation.js';
import { Emitter } from '../../../../../base/common/event.js';
import { Iterable } from '../../../../../base/common/iterator.js';
import { Disposable, toDisposable } from '../../../../../base/common/lifecycle.js';
import { revive } from '../../../../../base/common/marshalling.js';
import '../../../../../base/common/observableInternal/index.js';
import { equalsIgnoreCase } from '../../../../../base/common/strings.js';
import { IConfigurationService } from '../../../../../platform/configuration/common/configuration.service.js';
import { ContextKeyExpr } from '../../../../../platform/contextkey/common/contextkey.js';
import { IContextKeyService } from '../../../../../platform/contextkey/common/contextkey.service.js';
import { ExtensionIdentifier } from '../../../../../platform/extensions/common/extensions.js';
import { ILogService } from '../../../../../platform/log/common/log.service.js';
import { IProductService } from '../../../../../platform/product/common/productService.service.js';
import { asJson } from '../../../../../platform/request/common/request.js';
import { IRequestService } from '../../../../../platform/request/common/request.service.js';
import { StorageScope, StorageTarget } from '../../../../../platform/storage/common/storage.js';
import { IStorageService } from '../../../../../platform/storage/common/storage.service.js';
import { ChatContextKeys } from '../actions/chatContextKeys.js';
import { ChatAgentLocation, ChatModeKind, ChatConfiguration } from '../constants.js';
import { observableValue } from '../../../../../base/common/observableInternal/observables/observableValue.js';

var ChatAgentNameService_1;
let ChatAgentService = class ChatAgentService extends Disposable {
    static {
        this.AGENT_LEADER = "@";
    }
    constructor(contextKeyService, configurationService) {
        super();
        this.contextKeyService = contextKeyService;
        this.configurationService = configurationService;
        this._agents = ( new Map());
        this._onDidChangeAgents = ( new Emitter());
        this.onDidChangeAgents = this._onDidChangeAgents.event;
        this._agentsContextKeys = ( new Set());
        this._hasToolsAgent = false;
        this._chatParticipantDetectionProviders = ( new Map());
        this._agentCompletionProviders = ( new Map());
        this._hasDefaultAgent = ChatContextKeys.enabled.bindTo(this.contextKeyService);
        this._extensionAgentRegistered = ChatContextKeys.extensionParticipantRegistered.bindTo(this.contextKeyService);
        this._defaultAgentRegistered = ChatContextKeys.panelParticipantRegistered.bindTo(this.contextKeyService);
        this._register(contextKeyService.onDidChangeContext(e => {
            if (e.affectsSome(this._agentsContextKeys)) {
                this._updateContextKeys();
            }
        }));
    }
    registerAgent(id, data) {
        const existingAgent = this.getAgent(id);
        if (existingAgent) {
            throw ( new Error(`Agent already registered: ${JSON.stringify(id)}`));
        }
        const that = this;
        const commands = data.slashCommands;
        data = {
            ...data,
            get slashCommands() {
                return commands.filter(
                    c => !c.when || that.contextKeyService.contextMatchesRules(ContextKeyExpr.deserialize(c.when))
                );
            }
        };
        const entry = {
            data
        };
        this._agents.set(id, entry);
        this._updateAgentsContextKeys();
        this._updateContextKeys();
        this._onDidChangeAgents.fire(undefined);
        return toDisposable(() => {
            this._agents.delete(id);
            this._updateAgentsContextKeys();
            this._updateContextKeys();
            this._onDidChangeAgents.fire(undefined);
        });
    }
    _updateAgentsContextKeys() {
        this._agentsContextKeys.clear();
        for (const agent of ( this._agents.values())) {
            if (agent.data.when) {
                const expr = ContextKeyExpr.deserialize(agent.data.when);
                for (const key of expr?.keys() || []) {
                    this._agentsContextKeys.add(key);
                }
            }
        }
    }
    _updateContextKeys() {
        let extensionAgentRegistered = false;
        let defaultAgentRegistered = false;
        let toolsAgentRegistered = false;
        for (const agent of this.getAgents()) {
            if (agent.isDefault) {
                if (!agent.isCore) {
                    extensionAgentRegistered = true;
                }
                if (agent.id === "chat.setup" || agent.id === "github.copilot.editsAgent") {
                    toolsAgentRegistered = true;
                } else {
                    defaultAgentRegistered = true;
                }
            }
        }
        this._defaultAgentRegistered.set(defaultAgentRegistered);
        this._extensionAgentRegistered.set(extensionAgentRegistered);
        if (toolsAgentRegistered !== this._hasToolsAgent) {
            this._hasToolsAgent = toolsAgentRegistered;
            this._onDidChangeAgents.fire(this.getDefaultAgent(ChatAgentLocation.Chat, ChatModeKind.Agent));
        }
    }
    registerAgentImplementation(id, agentImpl) {
        const entry = this._agents.get(id);
        if (!entry) {
            throw ( new Error(`Unknown agent: ${JSON.stringify(id)}`));
        }
        if (entry.impl) {
            throw ( new Error(`Agent already has implementation: ${JSON.stringify(id)}`));
        }
        if (entry.data.isDefault) {
            this._hasDefaultAgent.set(true);
        }
        entry.impl = agentImpl;
        this._onDidChangeAgents.fire(( new MergedChatAgent(entry.data, agentImpl)));
        return toDisposable(() => {
            entry.impl = undefined;
            this._onDidChangeAgents.fire(undefined);
            if (entry.data.isDefault) {
                this._hasDefaultAgent.set(( Iterable.some(( this._agents.values()), agent => agent.data.isDefault && !!agent.impl)));
            }
        });
    }
    registerDynamicAgent(data, agentImpl) {
        data.isDynamic = true;
        const agent = {
            data,
            impl: agentImpl
        };
        this._agents.set(data.id, agent);
        this._onDidChangeAgents.fire(( new MergedChatAgent(data, agentImpl)));
        return toDisposable(() => {
            this._agents.delete(data.id);
            this._onDidChangeAgents.fire(undefined);
        });
    }
    registerAgentCompletionProvider(id, provider) {
        this._agentCompletionProviders.set(id, provider);
        return {
            dispose: () => {
                this._agentCompletionProviders.delete(id);
            }
        };
    }
    async getAgentCompletionItems(id, query, token) {
        return (await this._agentCompletionProviders.get(id)?.(query, token)) ?? [];
    }
    updateAgent(id, updateMetadata) {
        const agent = this._agents.get(id);
        if (!agent?.impl) {
            throw ( new Error(`No activated agent with id ${JSON.stringify(id)} registered`));
        }
        agent.data.metadata = {
            ...agent.data.metadata,
            ...updateMetadata
        };
        this._onDidChangeAgents.fire(( new MergedChatAgent(agent.data, agent.impl)));
    }
    getDefaultAgent(location, mode = ChatModeKind.Ask) {
        return this._preferExtensionAgent(this.getActivatedAgents().filter(a => {
            if (mode && !a.modes.includes(mode)) {
                return false;
            }
            return !!a.isDefault && a.locations.includes(location);
        }));
    }
    get hasToolsAgent() {
        return !!this.configurationService.getValue(ChatConfiguration.AgentEnabled);
    }
    getContributedDefaultAgent(location) {
        return this._preferExtensionAgent(
            this.getAgents().filter(a => !!a.isDefault && a.locations.includes(location))
        );
    }
    _preferExtensionAgent(agents) {
        return findLast(agents, agent => !agent.isCore) ?? agents.at(-1);
    }
    getAgent(id, includeDisabled = false) {
        if (!this._agentIsEnabled(id) && !includeDisabled) {
            return;
        }
        return this._agents.get(id)?.data;
    }
    _agentIsEnabled(idOrAgent) {
        const entry = typeof idOrAgent === "string" ? this._agents.get(idOrAgent) : idOrAgent;
        return !entry?.data.when || this.contextKeyService.contextMatchesRules(ContextKeyExpr.deserialize(entry.data.when));
    }
    getAgentByFullyQualifiedId(id) {
        const agent = Iterable.find(( this._agents.values()), a => getFullyQualifiedId(a.data) === id)?.data;
        if (agent && !this._agentIsEnabled(agent.id)) {
            return;
        }
        return agent;
    }
    getAgents() {
        return ( Array.from(( this._agents.values())).map(entry => entry.data)).filter(a => this._agentIsEnabled(a.id));
    }
    getActivatedAgents() {
        return ( Array.from(( this._agents.values())).filter(a => !!a.impl).filter(a => this._agentIsEnabled(a.data.id)).map(a => ( new MergedChatAgent(a.data, a.impl))));
    }
    getAgentsByName(name) {
        return this._preferExtensionAgents(this.getAgents().filter(a => a.name === name));
    }
    _preferExtensionAgents(agents) {
        const extensionAgents = agents.filter(a => !a.isCore);
        return extensionAgents.length > 0 ? extensionAgents : agents;
    }
    agentHasDupeName(id) {
        const agent = this.getAgent(id);
        if (!agent) {
            return false;
        }
        return this.getAgentsByName(agent.name).filter(a => a.extensionId.value !== agent.extensionId.value).length > 0;
    }
    async invokeAgent(id, request, progress, history, token) {
        const data = this._agents.get(id);
        if (!data?.impl) {
            throw ( new Error(`No activated agent with id "${id}"`));
        }
        return await data.impl.invoke(request, progress, history, token);
    }
    setRequestTools(id, requestId, tools) {
        const data = this._agents.get(id);
        if (!data?.impl) {
            throw ( new Error(`No activated agent with id "${id}"`));
        }
        data.impl.setRequestTools?.(requestId, tools);
    }
    setYieldRequested(id, requestId) {
        const data = this._agents.get(id);
        if (!data?.impl) {
            return;
        }
        data.impl.setYieldRequested?.(requestId);
    }
    async getFollowups(id, request, result, history, token) {
        const data = this._agents.get(id);
        if (!data?.impl?.provideFollowups) {
            return [];
        }
        return data.impl.provideFollowups(request, result, history, token);
    }
    async getChatTitle(id, history, token) {
        const data = this._agents.get(id);
        if (!data?.impl?.provideChatTitle) {
            return undefined;
        }
        return data.impl.provideChatTitle(history, token);
    }
    async getChatSummary(id, history, token) {
        const data = this._agents.get(id);
        if (!data?.impl?.provideChatSummary) {
            return undefined;
        }
        return data.impl.provideChatSummary(history, token);
    }
    registerChatParticipantDetectionProvider(handle, provider) {
        this._chatParticipantDetectionProviders.set(handle, provider);
        return toDisposable(() => {
            this._chatParticipantDetectionProviders.delete(handle);
        });
    }
    hasChatParticipantDetectionProviders() {
        return this._chatParticipantDetectionProviders.size > 0;
    }
    async detectAgentOrCommand(request, history, options, token) {
        const provider = Iterable.first(( this._chatParticipantDetectionProviders.values()));
        if (!provider) {
            return;
        }
        const participants = this.getAgents().reduce((acc, a) => {
            if (a.locations.includes(options.location)) {
                acc.push({
                    participant: a.id,
                    disambiguation: a.disambiguation ?? []
                });
                for (const command of a.slashCommands) {
                    acc.push({
                        participant: a.id,
                        command: command.name,
                        disambiguation: command.disambiguation ?? []
                    });
                }
            }
            return acc;
        }, []);
        const result = await provider.provideParticipantDetection(request, history, {
            ...options,
            participants
        }, token);
        if (!result) {
            return;
        }
        const agent = this.getAgent(result.participant);
        if (!agent) {
            return;
        }
        if (!result.command) {
            return {
                agent
            };
        }
        const command = agent?.slashCommands.find(c => c.name === result.command);
        if (!command) {
            return;
        }
        return {
            agent,
            command
        };
    }
};
ChatAgentService = ( __decorate([( __param(0, IContextKeyService)), ( __param(1, IConfigurationService))], ChatAgentService));
class MergedChatAgent {
    constructor(data, impl) {
        this.data = data;
        this.impl = impl;
    }
    get id() {
        return this.data.id;
    }
    get name() {
        return this.data.name ?? "";
    }
    get fullName() {
        return this.data.fullName ?? "";
    }
    get description() {
        return this.data.description ?? "";
    }
    get extensionId() {
        return this.data.extensionId;
    }
    get extensionVersion() {
        return this.data.extensionVersion;
    }
    get extensionPublisherId() {
        return this.data.extensionPublisherId;
    }
    get extensionPublisherDisplayName() {
        return this.data.publisherDisplayName;
    }
    get extensionDisplayName() {
        return this.data.extensionDisplayName;
    }
    get isDefault() {
        return this.data.isDefault;
    }
    get isCore() {
        return this.data.isCore;
    }
    get metadata() {
        return this.data.metadata;
    }
    get slashCommands() {
        return this.data.slashCommands;
    }
    get locations() {
        return this.data.locations;
    }
    get modes() {
        return this.data.modes;
    }
    get disambiguation() {
        return this.data.disambiguation;
    }
    async invoke(request, progress, history, token) {
        return this.impl.invoke(request, progress, history, token);
    }
    setRequestTools(requestId, tools) {
        this.impl.setRequestTools?.(requestId, tools);
    }
    setYieldRequested(requestId) {
        this.impl.setYieldRequested?.(requestId);
    }
    async provideFollowups(request, result, history, token) {
        if (this.impl.provideFollowups) {
            return this.impl.provideFollowups(request, result, history, token);
        }
        return [];
    }
    toJSON() {
        return this.data;
    }
}
let ChatAgentNameService = class ChatAgentNameService {
    static {
        ChatAgentNameService_1 = this;
    }
    static {
        this.StorageKey = "chat.participantNameRegistry";
    }
    constructor(productService, requestService, logService, storageService) {
        this.requestService = requestService;
        this.logService = logService;
        this.storageService = storageService;
        this.registry = observableValue(this, Object.create(null));
        this.disposed = false;
        if (!productService.chatParticipantRegistry) {
            return;
        }
        this.url = productService.chatParticipantRegistry;
        const raw = storageService.get(ChatAgentNameService_1.StorageKey, StorageScope.APPLICATION);
        try {
            this.registry.set(JSON.parse(raw ?? "{}"), undefined);
        } catch (err) {
            storageService.remove(ChatAgentNameService_1.StorageKey, StorageScope.APPLICATION);
        }
        this.refresh();
    }
    refresh() {
        if (this.disposed) {
            return;
        }
        this.update().catch(
            err => this.logService.warn("Failed to fetch chat participant registry", err)
        ).then(() => timeout(5 * 60 * 1000)).then(() => this.refresh());
    }
    async update() {
        const context = await this.requestService.request({
            type: "GET",
            url: this.url
        }, CancellationToken.None);
        if (context.res.statusCode !== 200) {
            throw ( new Error("Could not get extensions report."));
        }
        const result = await asJson(context);
        if (!result || result.version !== 1) {
            throw ( new Error("Unexpected chat participant registry response."));
        }
        const registry = result.restrictedChatParticipants;
        this.registry.set(registry, undefined);
        this.storageService.store(
            ChatAgentNameService_1.StorageKey,
            JSON.stringify(registry),
            StorageScope.APPLICATION,
            StorageTarget.MACHINE
        );
    }
    getAgentNameRestriction(chatAgentData) {
        if (chatAgentData.isCore) {
            return true;
        }
        const nameAllowed = this.checkAgentNameRestriction(chatAgentData.name, chatAgentData).get();
        const fullNameAllowed = !chatAgentData.fullName || this.checkAgentNameRestriction(chatAgentData.fullName.replace(/\s/g, ""), chatAgentData).get();
        return nameAllowed && fullNameAllowed;
    }
    checkAgentNameRestriction(name, chatAgentData) {
        const allowList = ( this.registry.map(registry => registry[name.toLowerCase()]));
        return ( allowList.map(allowList => {
            if (!allowList) {
                return true;
            }
            return ( allowList.some(id => equalsIgnoreCase(
                id,
                id.includes(".") ? chatAgentData.extensionId.value : chatAgentData.extensionPublisherId
            )));
        }));
    }
    dispose() {
        this.disposed = true;
    }
};
ChatAgentNameService = ChatAgentNameService_1 = ( __decorate([( __param(0, IProductService)), ( __param(1, IRequestService)), ( __param(2, ILogService)), ( __param(3, IStorageService))], ChatAgentNameService));
function getFullyQualifiedId(chatAgentData) {
    return `${chatAgentData.extensionId.value}.${chatAgentData.id}`;
}
function isSerializableChatAgentData(obj) {
    return obj.name !== undefined;
}
function reviveSerializedAgent(raw) {
    const normalized = isSerializableChatAgentData(raw) ? raw : {
        ...raw,
        name: raw.id
    };
    if (!normalized.extensionPublisherId) {
        normalized.extensionPublisherId = raw.extensionPublisher ?? "";
    }
    if (!normalized.extensionDisplayName) {
        normalized.extensionDisplayName = "";
    }
    if (!normalized.extensionId) {
        normalized.extensionId = ( new ExtensionIdentifier(""));
    }
    return revive(normalized);
}

export { ChatAgentNameService, ChatAgentService, MergedChatAgent, getFullyQualifiedId, reviveSerializedAgent };
