import { AgentSession } from './agentSession.js';
export class AgentHost {
    context;
    toolObserver;
    modules = new Map();
    contributions = [];
    contributionOwners = new Map();
    moduleContributions = new Map();
    session = null;
    disposed = false;
    constructor(options) {
        this.context = {
            profile: options.profile,
            workspaceContext: options.workspaceContext,
            workingDir: options.workingDir,
            env: options.env ? { ...options.env } : { ...process.env },
        };
        this.toolObserver = options.toolObserver;
    }
    get profile() {
        return this.context.profile;
    }
    get workspaceContext() {
        return this.context.workspaceContext ?? null;
    }
    get workingDir() {
        return this.context.workingDir;
    }
    refreshWorkspaceContext(workspaceContext) {
        this.context.workspaceContext = workspaceContext;
        this.session?.refreshWorkspaceContext(workspaceContext);
    }
    async loadModules(modules) {
        for (const module of modules) {
            await this.registerModule(module);
        }
    }
    async registerModule(module) {
        this.assertMutable();
        this.removeModuleContributions(module.id);
        this.modules.set(module.id, module);
        const result = await module.create(this.context);
        const normalized = normalizeContributions(result);
        for (const contribution of normalized) {
            this.validateContribution(contribution, module.id);
            this.contributions.push(contribution);
            this.contributionOwners.set(contribution.id, module.id);
            const contributionSet = this.moduleContributions.get(module.id) ?? new Set();
            contributionSet.add(contribution.id);
            this.moduleContributions.set(module.id, contributionSet);
        }
    }
    async getSession() {
        if (this.session) {
            return this.session;
        }
        const toolSuites = this.collectToolSuites();
        this.session = new AgentSession({
            profile: this.context.profile,
            workspaceContext: this.context.workspaceContext,
            toolSuites,
            toolObserver: this.toolObserver,
        });
        return this.session;
    }
    describeCapabilities() {
        return this.contributions.map((contribution) => ({
            id: contribution.id,
            moduleId: this.contributionOwners.get(contribution.id) ?? 'unknown',
            description: contribution.description,
            metadata: contribution.metadata ?? undefined,
        }));
    }
    async dispose() {
        if (this.disposed) {
            return;
        }
        this.disposed = true;
        const tasks = this.contributions
            .map((contribution) => contribution.dispose)
            .filter((fn) => typeof fn === 'function')
            .map(async (dispose) => {
            try {
                await dispose();
            }
            catch {
                // Ignore disposal errors to avoid masking upstream teardown.
            }
        });
        await Promise.all(tasks);
    }
    collectToolSuites() {
        const suites = [];
        const seen = new Set();
        for (const contribution of this.contributions) {
            const records = normalizeSuites(contribution);
            for (const suite of records) {
                if (seen.has(suite.id)) {
                    throw new Error(`Duplicate tool suite id "${suite.id}" detected while composing capabilities. ` +
                        'Ensure every capability module emits unique suite identifiers.');
                }
                suites.push(suite);
                seen.add(suite.id);
            }
        }
        return suites;
    }
    validateContribution(contribution, moduleId) {
        if (!contribution?.id?.trim()) {
            throw new Error(`Capability module "${moduleId}" emitted a contribution without an id.`);
        }
        const suites = normalizeSuites(contribution);
        for (const suite of suites) {
            if (!suite?.id?.trim()) {
                throw new Error(`Capability contribution "${contribution.id}" from module "${moduleId}" ` +
                    'emitted a tool suite without an id.');
            }
        }
    }
    assertMutable() {
        if (this.session) {
            throw new Error('Cannot register an additional capability module after the runtime session has been created. ' +
                'Instantiate AgentHost earlier in the boot sequence or create a new host instance.');
        }
    }
    removeModuleContributions(moduleId) {
        const ids = this.moduleContributions.get(moduleId);
        if (!ids?.size) {
            return;
        }
        this.moduleContributions.delete(moduleId);
        for (const id of ids) {
            const index = this.contributions.findIndex((entry) => entry.id === id);
            if (index >= 0) {
                this.contributions.splice(index, 1);
            }
            this.contributionOwners.delete(id);
        }
    }
}
function normalizeContributions(value) {
    if (!value) {
        return [];
    }
    return Array.isArray(value) ? value : [value];
}
function normalizeSuites(contribution) {
    const suites = [];
    if (contribution.toolSuite) {
        suites.push(contribution.toolSuite);
    }
    if (contribution.toolSuites?.length) {
        suites.push(...contribution.toolSuites);
    }
    return suites;
}
//# sourceMappingURL=agentHost.js.map