import { BestAgentError, isRecord } from "@best-agent/foundation";
const NO_INPUT_SCHEMA = {
    additionalProperties: false,
    properties: {},
    required: [],
    type: "object",
};
const HOSTED_TOOL_JSON_SCHEMA_KEYS = new Set([
    "additionalProperties",
    "enum",
    "items",
    "properties",
    "required",
    "type",
]);
const HOSTED_TOOL_JSON_SCHEMA_TYPES = new Set([
    "array",
    "boolean",
    "integer",
    "number",
    "object",
    "string",
]);
export function createHostedContributionBundle(contributions, bridge) {
    const agentProfiles = [];
    const postToolUseHooks = [];
    const preToolUseHooks = [];
    const skills = [];
    const systemPromptHooks = [];
    const toolDiscoveryHooks = [];
    const tools = [];
    for (const contribution of contributions) {
        const localToolNames = new Map((contribution.tools ?? []).map((tool) => [
            tool.name,
            qualifyHostedToolName(contribution.name, tool.name),
        ]));
        const localSkillIds = new Map((contribution.skills ?? []).map((skill) => [
            skill.name,
            qualifyHostedSkillId(contribution.name, skill.name),
        ]));
        for (const tool of contribution.tools ?? []) {
            tools.push({
                approvalClass: tool.approvalClass,
                concurrencyGroup: tool.concurrencyGroup,
                description: tool.description,
                executionClass: tool.executionClass ?? "external-side-effect",
                expectedDurationClass: tool.expectedDurationClass,
                heartbeatPolicy: tool.heartbeatPolicy,
                idempotent: tool.idempotent ?? false,
                inputSchema: parseHostedToolInputSchema(contribution.name, tool.name, tool.inputSchema),
                name: qualifyHostedToolName(contribution.name, tool.name),
                replayPolicy: tool.replayPolicy,
                source: "plugin",
                sourcePlugin: contribution.name,
                capabilities: async (input, context) => (await bridge.getCapabilities({
                    context: toHostedToolContextSnapshot(context),
                    contributionName: contribution.name,
                    input,
                    toolName: tool.name,
                })).map((request) => ({
                    ...request,
                    kind: request.kind ?? "plugin-hook",
                })),
                execute: (input, context) => bridge.invoke({
                    context: toHostedToolContextSnapshot(context),
                    contributionName: contribution.name,
                    input,
                    toolName: tool.name,
                }),
            });
        }
        for (const skill of contribution.skills ?? []) {
            skills.push({
                allowedTools: skill.allowedTools,
                content: skill.content,
                contextMode: skill.contextMode,
                description: skill.description,
                effort: skill.effort,
                executionMode: skill.executionMode,
                id: qualifyHostedSkillId(contribution.name, skill.name),
                model: skill.model,
                name: skill.name,
                pathScope: skill.pathScope,
                source: "plugin",
                sourcePlugin: contribution.name,
                whenToUse: skill.whenToUse,
            });
        }
        for (const profile of contribution.agentProfiles ?? []) {
            agentProfiles.push({
                allowedTools: normalizeToolRefs(profile.allowedTools ?? [], localToolNames),
                defaultSkills: normalizeSkillRefs(profile.defaultSkills ?? [], localSkillIds),
                description: profile.description,
                name: profile.name,
                source: "plugin",
                sourcePlugin: contribution.name,
                systemPrompt: profile.systemPrompt,
            });
        }
        if (contribution.hooks?.systemPromptTransform) {
            systemPromptHooks.push({
                apply: async (request) => (await bridge.transformSystemPrompt({
                    ...request,
                    contributionName: contribution.name,
                })).systemPrompt,
                contributionName: contribution.name,
            });
        }
        if (contribution.hooks?.toolDiscovery) {
            toolDiscoveryHooks.push({
                apply: async (request) => (await bridge.discoverTools({
                    ...request,
                    contributionName: contribution.name,
                })).toolNames,
                contributionName: contribution.name,
            });
        }
        if (contribution.hooks?.preToolUse) {
            preToolUseHooks.push({
                apply: async (request) => (await bridge.preToolUse?.({
                    ...request,
                    contributionName: contribution.name,
                })) ?? {},
                contributionName: contribution.name,
            });
        }
        if (contribution.hooks?.postToolUse) {
            postToolUseHooks.push({
                apply: async (request) => (await bridge.postToolUse?.({
                    ...request,
                    contributionName: contribution.name,
                })) ?? {},
                contributionName: contribution.name,
            });
        }
    }
    return {
        agentProfiles,
        postToolUseHooks,
        preToolUseHooks,
        skills,
        systemPromptHooks,
        toolDiscoveryHooks,
        tools,
    };
}
function parseHostedToolInputSchema(contributionName, toolName, inputSchema) {
    if (inputSchema === undefined) {
        return {
            additionalProperties: NO_INPUT_SCHEMA.additionalProperties,
            properties: {},
            required: [],
            type: NO_INPUT_SCHEMA.type,
        };
    }
    const schema = parseHostedToolJsonSchema(inputSchema, `Hosted tool ${contributionName}:${toolName} inputSchema`);
    if (schema.type !== "object") {
        throw new BestAgentError("PLUGIN_VALIDATION", `Hosted tool ${contributionName}:${toolName} inputSchema root must set type: "object".`);
    }
    return schema;
}
function parseHostedToolJsonSchema(value, path) {
    if (!isRecord(value)) {
        throw new BestAgentError("PLUGIN_VALIDATION", `${path} must be a JSON schema object.`);
    }
    for (const key of Object.keys(value)) {
        if (!HOSTED_TOOL_JSON_SCHEMA_KEYS.has(key)) {
            throw new BestAgentError("PLUGIN_VALIDATION", `${path}.${key} is not supported by hosted tool schemas.`);
        }
    }
    const hasObjectKeywords = "additionalProperties" in value || "properties" in value || "required" in value;
    const hasArrayKeywords = "items" in value;
    if (hasObjectKeywords && value.type !== "object") {
        throw new BestAgentError("PLUGIN_VALIDATION", `${path} uses object schema keywords and must set type: "object".`);
    }
    if (hasArrayKeywords && value.type !== "array") {
        throw new BestAgentError("PLUGIN_VALIDATION", `${path} uses items and must set type: "array".`);
    }
    if (!("type" in value) && !("enum" in value)) {
        throw new BestAgentError("PLUGIN_VALIDATION", `${path} must set type or enum.`);
    }
    const schema = {};
    if ("type" in value) {
        schema.type = parseHostedToolJsonSchemaType(value.type, `${path}.type`);
    }
    if ("additionalProperties" in value) {
        if (typeof value.additionalProperties !== "boolean") {
            throw new BestAgentError("PLUGIN_VALIDATION", `${path}.additionalProperties must be boolean.`);
        }
        schema.additionalProperties = value.additionalProperties;
    }
    if (schema.type === "object" && schema.additionalProperties !== false) {
        throw new BestAgentError("PLUGIN_VALIDATION", `${path} object schemas must set additionalProperties: false.`);
    }
    if (schema.type === "array" && !("items" in value)) {
        throw new BestAgentError("PLUGIN_VALIDATION", `${path} array schemas must set items.`);
    }
    if ("required" in value) {
        if (!Array.isArray(value.required) ||
            !value.required.every((entry) => typeof entry === "string")) {
            throw new BestAgentError("PLUGIN_VALIDATION", `${path}.required must be a string array.`);
        }
        schema.required = [...value.required];
    }
    if ("enum" in value) {
        if (!Array.isArray(value.enum) || !value.enum.every(isToolInputValue)) {
            throw new BestAgentError("PLUGIN_VALIDATION", `${path}.enum must contain ToolInputValue entries.`);
        }
        schema.enum = [...value.enum];
    }
    if ("items" in value) {
        schema.items = parseHostedToolJsonSchema(value.items, `${path}.items`);
    }
    if ("properties" in value) {
        if (!isRecord(value.properties)) {
            throw new BestAgentError("PLUGIN_VALIDATION", `${path}.properties must be an object.`);
        }
        schema.properties = Object.fromEntries(Object.entries(value.properties).map(([propertyName, propertySchema]) => [
            propertyName,
            parseHostedToolJsonSchema(propertySchema, `${path}.properties.${propertyName}`),
        ]));
    }
    if (schema.required) {
        const propertyNames = new Set(Object.keys(schema.properties ?? {}));
        for (const requiredName of schema.required) {
            if (!propertyNames.has(requiredName)) {
                throw new BestAgentError("PLUGIN_VALIDATION", `${path}.required contains "${requiredName}" but properties.${requiredName} is not defined.`);
            }
        }
    }
    return schema;
}
function parseHostedToolJsonSchemaType(value, path) {
    if (typeof value === "string" && HOSTED_TOOL_JSON_SCHEMA_TYPES.has(value)) {
        return value;
    }
    throw new BestAgentError("PLUGIN_VALIDATION", `${path} must be a supported JSON schema type.`);
}
function isToolInputValue(value) {
    if (value === null) {
        return true;
    }
    if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
        return true;
    }
    if (Array.isArray(value)) {
        return value.every(isToolInputValue);
    }
    if (isRecord(value)) {
        return Object.values(value).every(isToolInputValue);
    }
    return false;
}
export function qualifyHostedToolName(contributionName, toolName) {
    return `plugin:${contributionName}:${toolName}`;
}
export function qualifyHostedSkillId(contributionName, skillName) {
    return `skill:${contributionName}:${skillName}`;
}
function normalizeToolRefs(refs, localToolNames) {
    return Array.from(new Set(refs.map((ref) => localToolNames.get(ref) ?? ref)));
}
function normalizeSkillRefs(refs, localSkillIds) {
    return Array.from(new Set(refs.map((ref) => localSkillIds.get(ref) ?? ref)));
}
function toHostedToolContextSnapshot(context) {
    return {
        cwd: context.cwd,
        threadId: context.threadId,
        threadPaths: context.threadPaths,
        turnId: context.turnId,
    };
}
//# sourceMappingURL=runtime-contribution.js.map