{"version":3,"file":"spawn-agent.d.ts","sourceRoot":"","sources":["../../../src/components/capabilities/spawn-agent.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAErD,OAAO,KAAK,EAEX,0BAA0B,EAC1B,oBAAoB,EAGpB,MAAM,cAAc,CAAC;AAMtB,MAAM,WAAW,oBAAoB;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,eAAe,EAAE,MAAM,CAAC;IACxB,KAAK,EAAE,MAAM,EAAE,CAAC;IAChB,SAAS,CAAC,EAAE,aAAa,CAAC;CAC1B;AAED,MAAM,WAAW,uBAAuB;IACvC,sFAAsF;IACtF,MAAM,EAAE,OAAO,CAAC;IAChB,kEAAkE;IAClE,KAAK,EAAE,0BAA0B,CAAC;CAClC;AAED,MAAM,WAAW,2BAA2B;IAC3C,MAAM,EAAE,WAAW,CAAC;CACpB;AAED;;;;;;GAMG;AACH,MAAM,WAAW,oBAAoB,CAAC,MAAM,GAAG,OAAO;IACrD,YAAY,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,CAAC;IAChD;;;OAGG;IACH,mBAAmB,CAAC,KAAK,EAAE,MAAM,EAAE,OAAO,EAAE,oBAAoB,GAAG,0BAA0B,CAAC;IAC9F,kFAAkF;IAClF,QAAQ,CACP,KAAK,EAAE,MAAM,EACb,OAAO,EAAE,oBAAoB,EAC7B,OAAO,EAAE,2BAA2B,GAClC,OAAO,CAAC,uBAAuB,CAAC,CAAC;CACpC;AAsID,wBAAgB,iCAAiC,CAAC,MAAM,EAAE,IAAI,EAAE,oBAAoB,CAAC,MAAM,CAAC,GAAG,oBAAoB,CA4BlH","sourcesContent":["import type { ThinkingLevel } from \"@ch1nyzzz/pi-ai\";\nimport { canonicalJson } from \"../../storage.ts\";\nimport type {\n\tEvoCapabilityExecutionResult,\n\tEvoCapabilityResourceUsage,\n\tEvoCapabilityService,\n\tEvoCapabilityServiceContext,\n\tEvoPreparedCapabilityRequest,\n} from \"./service.ts\";\n\nconst THINKING_LEVELS = new Set<ThinkingLevel>([\"minimal\", \"low\", \"medium\", \"high\", \"xhigh\", \"max\"]);\nconst MAX_TOOLS = 256;\nconst MAX_TOOL_NAME_LENGTH = 300;\n\nexport interface EvoSpawnAgentRequest {\n\tmodel: string;\n\tprompt: string;\n\tmaxOutputTokens: number;\n\ttools: string[];\n\treasoning?: ThinkingLevel;\n}\n\nexport interface EvoSpawnAgentHostResult {\n\t/** Complete JSON-safe run result. The broker persists this value in its audit log. */\n\tresult: unknown;\n\t/** Aggregate usage across every model turn in the spawned run. */\n\tusage: EvoCapabilityResourceUsage;\n}\n\nexport interface EvoSpawnAgentHostRunOptions {\n\tsignal: AbortSignal;\n}\n\n/**\n * Trusted host adapter for creating one isolated child-agent run.\n *\n * TModel is an opaque host-only resolved-model handle. It is never serialized or\n * returned to the sandbox. The adapter must not place credentials in `result`,\n * must disable Evo/extensions in the child, and must expose only `request.tools`.\n */\nexport interface EvoRawSpawnAgentHost<TModel = unknown> {\n\tresolveModel(route: string): TModel | undefined;\n\t/**\n\t * Return a deterministic conservative bound for the entire multi-turn run,\n\t * including host prompt/tool-schema overhead and the host's fixed turn limit.\n\t */\n\testimateReservation(model: TModel, request: EvoSpawnAgentRequest): EvoCapabilityResourceUsage;\n\t/** Enforce maxOutputTokens as an aggregate run limit and honor options.signal. */\n\trunAgent(\n\t\tmodel: TModel,\n\t\trequest: EvoSpawnAgentRequest,\n\t\toptions: EvoSpawnAgentHostRunOptions,\n\t): Promise<EvoSpawnAgentHostResult>;\n}\n\nfunction asRecord(value: unknown, label: string): Record<string, unknown> {\n\tif (typeof value !== \"object\" || value === null || Array.isArray(value)) {\n\t\tthrow new Error(`${label} must be an object`);\n\t}\n\treturn value as Record<string, unknown>;\n}\n\nfunction rejectUnknownKeys(record: Record<string, unknown>, allowed: readonly string[], label: string): void {\n\tconst allowedKeys = new Set(allowed);\n\tfor (const key of Object.keys(record)) {\n\t\tif (!allowedKeys.has(key)) throw new Error(`${label} has unknown key: ${key}`);\n\t}\n}\n\nfunction parseModelRoute(value: unknown): string {\n\tif (\n\t\ttypeof value !== \"string\" ||\n\t\t!value.includes(\"/\") ||\n\t\tvalue.startsWith(\"/\") ||\n\t\tvalue.endsWith(\"/\") ||\n\t\tvalue.length > 300 ||\n\t\t/\\s/.test(value)\n\t) {\n\t\tthrow new Error(\"spawn-agent request.model must be a provider/model route\");\n\t}\n\treturn value;\n}\n\nfunction parseTools(value: unknown): string[] {\n\tif (!Array.isArray(value)) throw new Error(\"spawn-agent request.tools must be an array\");\n\tif (value.length > MAX_TOOLS) throw new Error(`spawn-agent request.tools must contain at most ${MAX_TOOLS} tools`);\n\tif (\n\t\tvalue.some(\n\t\t\t(tool) =>\n\t\t\t\ttypeof tool !== \"string\" || tool.length === 0 || tool.length > MAX_TOOL_NAME_LENGTH || tool.includes(\"\\0\"),\n\t\t)\n\t) {\n\t\tthrow new Error(\"spawn-agent request.tools must contain bounded non-empty tool names\");\n\t}\n\tconst tools = value as string[];\n\tif (new Set(tools).size !== tools.length) throw new Error(\"spawn-agent request.tools must not contain duplicates\");\n\treturn [...tools].sort();\n}\n\nfunction parseSpawnAgentRequest(value: unknown): EvoSpawnAgentRequest {\n\tconst request = asRecord(value, \"spawn-agent request\");\n\trejectUnknownKeys(request, [\"model\", \"prompt\", \"reasoning\", \"maxOutputTokens\", \"tools\"], \"spawn-agent request\");\n\tconst model = parseModelRoute(request.model);\n\tif (typeof request.prompt !== \"string\" || !request.prompt) {\n\t\tthrow new Error(\"spawn-agent request.prompt must be a non-empty string\");\n\t}\n\tif (!Number.isSafeInteger(request.maxOutputTokens) || (request.maxOutputTokens as number) <= 0) {\n\t\tthrow new Error(\"spawn-agent request.maxOutputTokens must be a positive safe integer\");\n\t}\n\tif (request.reasoning !== undefined && !THINKING_LEVELS.has(request.reasoning as ThinkingLevel)) {\n\t\tthrow new Error(\"spawn-agent request.reasoning is invalid\");\n\t}\n\treturn {\n\t\tmodel,\n\t\tprompt: request.prompt,\n\t\tmaxOutputTokens: request.maxOutputTokens as number,\n\t\ttools: parseTools(request.tools),\n\t\t...(request.reasoning === undefined ? {} : { reasoning: request.reasoning as ThinkingLevel }),\n\t};\n}\n\nfunction nonNegativeInteger(value: unknown, label: string): number {\n\tif (!Number.isSafeInteger(value) || (value as number) < 0) {\n\t\tthrow new Error(`${label} must be a non-negative safe integer`);\n\t}\n\treturn value as number;\n}\n\nfunction nonNegativeNumber(value: unknown, label: string): number {\n\tif (typeof value !== \"number\" || !Number.isFinite(value) || value < 0) {\n\t\tthrow new Error(`${label} must be a non-negative finite number`);\n\t}\n\treturn value;\n}\n\nfunction parseResourceUsage(value: unknown, label: string): EvoCapabilityResourceUsage {\n\tconst usage = asRecord(value, label);\n\trejectUnknownKeys(usage, [\"inputTokens\", \"outputTokens\", \"totalTokens\", \"costUsd\"], label);\n\tconst parsed = {\n\t\tinputTokens: nonNegativeInteger(usage.inputTokens, `${label}.inputTokens`),\n\t\toutputTokens: nonNegativeInteger(usage.outputTokens, `${label}.outputTokens`),\n\t\ttotalTokens: nonNegativeInteger(usage.totalTokens, `${label}.totalTokens`),\n\t\tcostUsd: nonNegativeNumber(usage.costUsd, `${label}.costUsd`),\n\t};\n\tif (parsed.totalTokens < parsed.inputTokens || parsed.totalTokens < parsed.outputTokens) {\n\t\tthrow new Error(`${label}.totalTokens must cover each token sub-total`);\n\t}\n\treturn parsed;\n}\n\nfunction validateReservation(\n\tvalue: EvoCapabilityResourceUsage,\n\trequest: EvoSpawnAgentRequest,\n): EvoCapabilityResourceUsage {\n\tconst reservation = parseResourceUsage(value, \"spawn-agent reservation\");\n\tif (reservation.inputTokens === 0) {\n\t\tthrow new Error(\"spawn-agent reservation.inputTokens must cover the non-empty prompt\");\n\t}\n\tif (reservation.outputTokens < request.maxOutputTokens) {\n\t\tthrow new Error(\"spawn-agent reservation.outputTokens must cover request.maxOutputTokens\");\n\t}\n\treturn reservation;\n}\n\nfunction assertWithinReservation(usage: EvoCapabilityResourceUsage, reservation: EvoCapabilityResourceUsage): void {\n\tfor (const key of [\"inputTokens\", \"outputTokens\", \"totalTokens\", \"costUsd\"] as const) {\n\t\tif (usage[key] > reservation[key]) {\n\t\t\tthrow new Error(`spawn-agent host usage exceeded reserved ${key}`);\n\t\t}\n\t}\n}\n\nfunction normalizeHostResult(value: EvoSpawnAgentHostResult): EvoSpawnAgentHostResult {\n\tconst response = asRecord(value as unknown, \"spawn-agent host result\");\n\trejectUnknownKeys(response, [\"result\", \"usage\"], \"spawn-agent host result\");\n\tif (!Object.hasOwn(response, \"result\")) throw new Error(\"spawn-agent host result.result is required\");\n\tif (!Object.hasOwn(response, \"usage\")) throw new Error(\"spawn-agent host result.usage is required\");\n\treturn {\n\t\tresult: JSON.parse(canonicalJson(response.result)) as unknown,\n\t\tusage: parseResourceUsage(response.usage, \"spawn-agent usage\"),\n\t};\n}\n\nfunction abortError(signal: AbortSignal): Error {\n\treturn signal.reason instanceof Error ? signal.reason : new Error(\"spawn-agent request aborted\");\n}\n\nexport function createSpawnAgentCapabilityService<TModel>(host: EvoRawSpawnAgentHost<TModel>): EvoCapabilityService {\n\treturn {\n\t\tprepare(payload: unknown): EvoPreparedCapabilityRequest {\n\t\t\tconst request = parseSpawnAgentRequest(payload);\n\t\t\tconst model = host.resolveModel(request.model);\n\t\t\tif (model === undefined) throw new Error(`spawn-agent model is unavailable: ${request.model}`);\n\t\t\tconst reservation = validateReservation(host.estimateReservation(model, request), request);\n\t\t\treturn {\n\t\t\t\trequest,\n\t\t\t\treservation,\n\t\t\t\tauthorization: {\n\t\t\t\t\tmodel: request.model,\n\t\t\t\t\tmaxOutputTokens: request.maxOutputTokens,\n\t\t\t\t\ttools: request.tools,\n\t\t\t\t},\n\t\t\t};\n\t\t},\n\t\tasync execute(value: unknown, context: EvoCapabilityServiceContext): Promise<EvoCapabilityExecutionResult> {\n\t\t\tconst request = parseSpawnAgentRequest(value);\n\t\t\tconst model = host.resolveModel(request.model);\n\t\t\tif (model === undefined) throw new Error(`spawn-agent model became unavailable: ${request.model}`);\n\t\t\tif (context.signal.aborted) throw abortError(context.signal);\n\t\t\tconst reservation = validateReservation(host.estimateReservation(model, request), request);\n\t\t\tconst response = normalizeHostResult(await host.runAgent(model, request, { signal: context.signal }));\n\t\t\tassertWithinReservation(response.usage, reservation);\n\t\t\treturn response;\n\t\t},\n\t};\n}\n"]}