@warlock.js/ai 5.2.3 → 5.2.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/cjs/index.cjs +1 -1
- package/cjs/index.cjs.map +1 -1
- package/cjs/{magic-string.es-BQeqHJ-a.cjs → magic-string.es-G6Xl81ni.cjs} +2 -2
- package/cjs/{magic-string.es-BQeqHJ-a.cjs.map → magic-string.es-G6Xl81ni.cjs.map} +1 -1
- package/cjs/matcher-logic-07fFOz7r.cjs.map +1 -1
- package/cjs/{matchers-CINm4ojZ.cjs → matchers-D7PBk4ut.cjs} +7 -7
- package/cjs/{matchers-CINm4ojZ.cjs.map → matchers-D7PBk4ut.cjs.map} +1 -1
- package/esm/agent/agent-input-builder.mjs.map +1 -1
- package/esm/agent/agent-stream.d.mts.map +1 -1
- package/esm/agent/agent-stream.mjs.map +1 -1
- package/esm/agent/agent.d.mts.map +1 -1
- package/esm/agent/agent.mjs.map +1 -1
- package/esm/agent/json-stream-guard.mjs.map +1 -1
- package/esm/agent/signature.mjs.map +1 -1
- package/esm/agent/snapshot.mjs.map +1 -1
- package/esm/agent/spawn-sub-agent.d.mts.map +1 -1
- package/esm/batch/batch.d.mts.map +1 -1
- package/esm/batch/batch.mjs.map +1 -1
- package/esm/checkpoint/memory.d.mts.map +1 -1
- package/esm/checkpoint/pg.mjs.map +1 -1
- package/esm/checkpoint/redis.mjs.map +1 -1
- package/esm/config.d.mts.map +1 -1
- package/esm/eval/dataset.d.mts.map +1 -1
- package/esm/eval/dataset.mjs.map +1 -1
- package/esm/eval/eval-runner.d.mts.map +1 -1
- package/esm/eval/eval-runner.mjs.map +1 -1
- package/esm/eval/judge-scorer.d.mts.map +1 -1
- package/esm/eval/regression.d.mts.map +1 -1
- package/esm/eval/regression.mjs.map +1 -1
- package/esm/eval/report-json.d.mts.map +1 -1
- package/esm/eval/report-junit.mjs.map +1 -1
- package/esm/eval/scorers.d.mts.map +1 -1
- package/esm/eval/scorers.mjs.map +1 -1
- package/esm/guard/detectors/injection.mjs.map +1 -1
- package/esm/guard/detectors/moderation.mjs.map +1 -1
- package/esm/guard/detectors/pii.mjs.map +1 -1
- package/esm/guard/detectors/topic.mjs.map +1 -1
- package/esm/human/human-approval.mjs.map +1 -1
- package/esm/human/resume.d.mts.map +1 -1
- package/esm/human/stores/memory.d.mts.map +1 -1
- package/esm/human/stores/pg.mjs.map +1 -1
- package/esm/human/stores/redis.mjs.map +1 -1
- package/esm/image/image.mjs.map +1 -1
- package/esm/memory/derive-id.mjs.map +1 -1
- package/esm/memory/episodic-memory.mjs.map +1 -1
- package/esm/memory/memory.mjs.map +1 -1
- package/esm/memory/procedural-memory.mjs.map +1 -1
- package/esm/memory/semantic-memory.mjs.map +1 -1
- package/esm/memory/working-memory.mjs.map +1 -1
- package/esm/middleware/builtins/budget.mjs.map +1 -1
- package/esm/middleware/builtins/semantic-cache.mjs.map +1 -1
- package/esm/middleware/helpers/compose.d.mts.map +1 -1
- package/esm/middleware/helpers/for-tool.mjs.map +1 -1
- package/esm/middleware/pipeline.d.mts.map +1 -1
- package/esm/middleware/utils/extract-user-text.mjs.map +1 -1
- package/esm/middleware/utils/namespaced-state.d.mts.map +1 -1
- package/esm/mock/mock-agent.d.mts.map +1 -1
- package/esm/mock/mock-agent.mjs.map +1 -1
- package/esm/mock/mock-model.d.mts.map +1 -1
- package/esm/mock/mock-model.mjs.map +1 -1
- package/esm/mock/mock-router.d.mts.map +1 -1
- package/esm/model/fallback-model.mjs.map +1 -1
- package/esm/node_modules/.pnpm/@jridgewell_sourcemap-codec@1.6.0/node_modules/@jridgewell/sourcemap-codec/dist/sourcemap-codec.mjs.map +1 -1
- package/esm/node_modules/.pnpm/@vitest_expect@4.1.10/node_modules/@vitest/expect/dist/index.mjs.map +1 -1
- package/esm/node_modules/.pnpm/@vitest_pretty-format@4.1.10/node_modules/@vitest/pretty-format/dist/index.mjs +2 -2
- package/esm/node_modules/.pnpm/@vitest_pretty-format@4.1.10/node_modules/@vitest/pretty-format/dist/index.mjs.map +1 -1
- package/esm/node_modules/.pnpm/@vitest_runner@4.1.10/node_modules/@vitest/runner/dist/chunk-artifact.mjs.map +1 -1
- package/esm/node_modules/.pnpm/@vitest_snapshot@4.1.10/node_modules/@vitest/snapshot/dist/index.mjs +1 -1
- package/esm/node_modules/.pnpm/@vitest_snapshot@4.1.10/node_modules/@vitest/snapshot/dist/index.mjs.map +1 -1
- package/esm/node_modules/.pnpm/@vitest_spy@4.1.10/node_modules/@vitest/spy/dist/index.mjs.map +1 -1
- package/esm/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/chunk-pathe.M-eThtNZ.mjs.map +1 -1
- package/esm/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/diff.mjs +1 -1
- package/esm/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/diff.mjs.map +1 -1
- package/esm/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/display.mjs +1 -1
- package/esm/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/display.mjs.map +1 -1
- package/esm/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/helpers.mjs.map +1 -1
- package/esm/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/offset.mjs.map +1 -1
- package/esm/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/serialize.mjs.map +1 -1
- package/esm/node_modules/.pnpm/@vitest_utils@4.1.10/node_modules/@vitest/utils/dist/source-map.mjs.map +1 -1
- package/esm/node_modules/.pnpm/chai@6.2.2/node_modules/chai/index.mjs.map +1 -1
- package/esm/node_modules/.pnpm/magic-string@0.30.21/node_modules/magic-string/dist/magic-string.es.mjs +1 -1
- package/esm/node_modules/.pnpm/magic-string@0.30.21/node_modules/magic-string/dist/magic-string.es.mjs.map +1 -1
- package/esm/node_modules/.pnpm/vitest@4.1.10_@opentelemetr_3b60e89b8b51a25e87011ae54ec49250/node_modules/vitest/dist/chunks/rpc.MzXet3jl.mjs.map +1 -1
- package/esm/node_modules/.pnpm/vitest@4.1.10_@opentelemetr_3b60e89b8b51a25e87011ae54ec49250/node_modules/vitest/dist/chunks/test.DNmyFkvJ.mjs.map +1 -1
- package/esm/object-stream/parse-partial-json.mjs.map +1 -1
- package/esm/object-stream/stream-object.d.mts.map +1 -1
- package/esm/object-stream/stream-object.mjs.map +1 -1
- package/esm/observe/observer-registry.d.mts.map +1 -1
- package/esm/orchestrator/as-tool.d.mts.map +1 -1
- package/esm/orchestrator/checkpoint.mjs.map +1 -1
- package/esm/orchestrator/compaction.mjs.map +1 -1
- package/esm/orchestrator/emitter.d.mts.map +1 -1
- package/esm/orchestrator/emitter.mjs.map +1 -1
- package/esm/orchestrator/execution.d.mts.map +1 -1
- package/esm/orchestrator/execution.mjs.map +1 -1
- package/esm/orchestrator/memory.mjs.map +1 -1
- package/esm/orchestrator/orchestrator-stream.d.mts.map +1 -1
- package/esm/orchestrator/orchestrator-stream.mjs.map +1 -1
- package/esm/orchestrator/orchestrator.d.mts.map +1 -1
- package/esm/orchestrator/orchestrator.mjs.map +1 -1
- package/esm/orchestrator/session-lock.d.mts.map +1 -1
- package/esm/orchestrator/signature.mjs.map +1 -1
- package/esm/planner/dag-scheduler.mjs.map +1 -1
- package/esm/planner/plan-prompt.mjs.map +1 -1
- package/esm/planner/planner-run.mjs.map +1 -1
- package/esm/planner/planner.d.mts.map +1 -1
- package/esm/planner/planner.mjs.map +1 -1
- package/esm/planner/signature.mjs.map +1 -1
- package/esm/planner/snapshot.mjs.map +1 -1
- package/esm/prompt/prompt-langfuse-sync.mjs.map +1 -1
- package/esm/prompt/prompt-validate.mjs.map +1 -1
- package/esm/prompt/prompt.mjs.map +1 -1
- package/esm/prompts/prompts-manager.d.mts.map +1 -1
- package/esm/prompts/prompts-manager.mjs.map +1 -1
- package/esm/prompts/prompts-validate.mjs.map +1 -1
- package/esm/rag/chunk/chunk.mjs.map +1 -1
- package/esm/rag/chunk/markdown.mjs.map +1 -1
- package/esm/rag/chunk/sentence.mjs.map +1 -1
- package/esm/rag/hybrid/bm25.mjs.map +1 -1
- package/esm/rag/hybrid/hybrid-rank.mjs.map +1 -1
- package/esm/rag/hybrid/rrf.mjs.map +1 -1
- package/esm/rag/loaders/load-html.mjs.map +1 -1
- package/esm/rag/loaders/load-pdf.mjs.map +1 -1
- package/esm/rag/loaders/load-text.mjs.map +1 -1
- package/esm/rag/rerank/keyword-reranker.mjs.map +1 -1
- package/esm/rag/rerank/llm-reranker.mjs.map +1 -1
- package/esm/rag/retrieve.mjs.map +1 -1
- package/esm/rag/store/cache-vector-store.mjs.map +1 -1
- package/esm/rag/store/pg-vector-store.mjs.map +1 -1
- package/esm/rag/transforms/multi-query.mjs.map +1 -1
- package/esm/security/outbound-policy.mjs.map +1 -1
- package/esm/security/private-ip.mjs.map +1 -1
- package/esm/security/redact.d.mts.map +1 -1
- package/esm/security/redact.mjs.map +1 -1
- package/esm/serve/serve.d.mts.map +1 -1
- package/esm/serve/serve.mjs.map +1 -1
- package/esm/serve/stream-to-sse.d.mts.map +1 -1
- package/esm/skills/catalog.mjs.map +1 -1
- package/esm/skills/skills.mjs.map +1 -1
- package/esm/skills/sources/directory-source.mjs.map +1 -1
- package/esm/skills/sources/parse-frontmatter.mjs.map +1 -1
- package/esm/skills/sources/url-source.mjs.map +1 -1
- package/esm/skills/store/mock-skills-store.mjs.map +1 -1
- package/esm/skills/store/procedural-skill-store.mjs.map +1 -1
- package/esm/snapshot/memory.d.mts.map +1 -1
- package/esm/snapshot/pg.mjs.map +1 -1
- package/esm/speech/speech.mjs.map +1 -1
- package/esm/supervisor/as-tool.d.mts.map +1 -1
- package/esm/supervisor/cancellation.mjs.map +1 -1
- package/esm/supervisor/emitter.d.mts.map +1 -1
- package/esm/supervisor/emitter.mjs.map +1 -1
- package/esm/supervisor/entries.mjs.map +1 -1
- package/esm/supervisor/execution.d.mts.map +1 -1
- package/esm/supervisor/execution.mjs.map +1 -1
- package/esm/supervisor/fan-out.mjs.map +1 -1
- package/esm/supervisor/router-factory.mjs.map +1 -1
- package/esm/supervisor/router-prompt.mjs.map +1 -1
- package/esm/supervisor/signature.mjs.map +1 -1
- package/esm/supervisor/snapshot.mjs.map +1 -1
- package/esm/supervisor/supervisor-stream.d.mts.map +1 -1
- package/esm/supervisor/supervisor-stream.mjs.map +1 -1
- package/esm/supervisor/supervisor.d.mts.map +1 -1
- package/esm/supervisor/supervisor.mjs.map +1 -1
- package/esm/system-prompt/refined-system-prompt.d.mts.map +1 -1
- package/esm/system-prompt/refined-system-prompt.mjs.map +1 -1
- package/esm/system-prompt/system-prompt.d.mts.map +1 -1
- package/esm/system-prompt/system-prompt.mjs.map +1 -1
- package/esm/team/team.d.mts.map +1 -1
- package/esm/testing/matcher-logic.mjs.map +1 -1
- package/esm/testing/register-lazy.d.mts.map +1 -1
- package/esm/tool/executable-as-tool.d.mts.map +1 -1
- package/esm/tool/tool.d.mts.map +1 -1
- package/esm/tool/tool.mjs.map +1 -1
- package/esm/transcribe/audio-input.mjs.map +1 -1
- package/esm/transcribe/transcribe.mjs.map +1 -1
- package/esm/utils/extract-json-payload.mjs.map +1 -1
- package/esm/utils/generate-run-id.mjs.map +1 -1
- package/esm/utils/prepare-attachment-part.mjs.map +1 -1
- package/esm/utils/run-context.d.mts.map +1 -1
- package/esm/utils/safe-json-parse.d.mts.map +1 -1
- package/esm/vcr/cassette-io.mjs.map +1 -1
- package/esm/vcr/hash-request.mjs.map +1 -1
- package/esm/vcr/vcr.mjs.map +1 -1
- package/esm/workflow/cancellation.mjs.map +1 -1
- package/esm/workflow/emitter.mjs.map +1 -1
- package/esm/workflow/engine.mjs.map +1 -1
- package/esm/workflow/retry.mjs.map +1 -1
- package/esm/workflow/router.mjs.map +1 -1
- package/esm/workflow/signature.d.mts.map +1 -1
- package/esm/workflow/signature.mjs.map +1 -1
- package/esm/workflow/snapshot.mjs.map +1 -1
- package/esm/workflow/step-runner.mjs.map +1 -1
- package/esm/workflow/step.d.mts.map +1 -1
- package/esm/workflow/step.mjs.map +1 -1
- package/esm/workflow/workflow.d.mts.map +1 -1
- package/esm/workflow/workflow.mjs.map +1 -1
- package/package.json +4 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"parse-partial-json.mjs","names":[],"sources":["../../../../../../../ai/src/object-stream/parse-partial-json.ts"],"sourcesContent":["/**\n * Best-effort parse of a possibly-truncated JSON string into the value it\n * is \"on its way to\" becoming. Used by {@link streamObject} to emit a\n * partial object snapshot from each streamed delta before the full reply\n * has arrived.\n *\n * Returns `undefined` when the prefix can't yet be coerced into a value\n * (so the caller simply waits for more text). The FINAL parse in\n * `streamObject` is always strict `JSON.parse` — this tolerant parser only\n * powers the in-flight snapshots, so a too-clever completion never affects\n * the authoritative result.\n *\n * @example\n * parsePartialJson('{\"name\":\"Al'); // → { name: \"Al\" }\n * parsePartialJson('{\"items\":[1,2,'); // → { items: [1, 2] }\n * parsePartialJson('{\"a\":1,\"b\"'); // → { a: 1 } (drops the dangling key)\n */\nexport function parsePartialJson(text: string): unknown | undefined {\n const trimmed = text.trim();\n if (!trimmed) return undefined;\n\n // Fast path: already-valid JSON.\n const direct = tryParse(trimmed);\n if (direct.ok) return direct.value;\n\n const completed = completePartialJson(trimmed);\n if (completed === undefined) return undefined;\n\n const parsed = tryParse(completed);\n return parsed.ok ? parsed.value : undefined;\n}\n\nfunction tryParse(text: string): { ok: true; value: unknown } | { ok: false } {\n try {\n return { ok: true, value: JSON.parse(text) };\n } catch {\n return { ok: false };\n }\n}\n\n/**\n * Reconstruct a parseable JSON string from a truncated prefix by closing\n * open strings/containers and trimming dangling separators, keys, and\n * partial literals. Tries the most faithful completion first, then falls\n * back to dropping the unfinished tail.\n */\nfunction completePartialJson(text: string): string | undefined {\n const stack: Array<\"{\" | \"[\"> = [];\n let inString = false;\n let escaped = false;\n\n for (const ch of text) {\n if (inString) {\n if (escaped) escaped = false;\n else if (ch === \"\\\\\") escaped = true;\n else if (ch === '\"') inString = false;\n continue;\n }\n if (ch === '\"') inString = true;\n else if (ch === \"{\" || ch === \"[\") stack.push(ch);\n else if (ch === \"}\" || ch === \"]\") stack.pop();\n }\n\n const closers = () =>\n stack\n .map(c => (c === \"{\" ? \"}\" : \"]\"))\n .reverse()\n .join(\"\");\n\n // Faithful completion: close an open string, drop a trailing comma,\n // fill a dangling `key:` with null, then close containers.\n let core = text;\n if (inString) core += '\"';\n core = core.replace(/\\s+$/, \"\");\n if (core.endsWith(\",\")) core = core.slice(0, -1);\n if (core.endsWith(\":\")) core += \"null\";\n\n const attempts: string[] = [core + closers()];\n\n // Fallback 1: drop a dangling object key (a `\"...\"` with no value yet).\n if (stack[stack.length - 1] === \"{\") {\n const droppedKey = core.replace(/,?\\s*\"(?:[^\"\\\\]|\\\\.)*\"\\s*$/, \"\");\n attempts.push(droppedKey.replace(/,\\s*$/, \"\") + closers());\n }\n\n // Fallback 2: drop a partial trailing literal / number (e.g. `tr`, `12.`).\n const droppedLiteral = core.replace(/[:,]?\\s*[A-Za-z0-9.+\\-eE]+$/, match =>\n match.trimStart().startsWith(\":\") ? \":null\" : \"\",\n );\n attempts.push(droppedLiteral.replace(/,\\s*$/, \"\") + closers());\n\n for (const candidate of attempts) {\n if (tryParse(candidate).ok) return candidate;\n }\n\n return undefined;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAiBA,SAAgB,iBAAiB,MAAmC;CAClE,MAAM,UAAU,KAAK,KAAK;CAC1B,IAAI,CAAC,SAAS,OAAO;CAGrB,MAAM,SAAS,SAAS,OAAO;CAC/B,IAAI,OAAO,IAAI,OAAO,OAAO;CAE7B,MAAM,YAAY,oBAAoB,OAAO;CAC7C,IAAI,cAAc,QAAW,OAAO;CAEpC,MAAM,SAAS,SAAS,SAAS;CACjC,OAAO,OAAO,KAAK,OAAO,QAAQ;AACpC;AAEA,SAAS,SAAS,MAA4D;CAC5E,IAAI;EACF,OAAO;GAAE,IAAI;GAAM,OAAO,KAAK,MAAM,IAAI;EAAE;CAC7C,QAAQ;EACN,OAAO,EAAE,IAAI,MAAM;CACrB;AACF;;;;;;;AAQA,SAAS,oBAAoB,MAAkC;CAC7D,MAAM,QAA0B,CAAC;CACjC,IAAI,WAAW;CACf,IAAI,UAAU;CAEd,KAAK,MAAM,MAAM,MAAM;EACrB,IAAI,UAAU;GACZ,IAAI,SAAS,UAAU;QAClB,IAAI,OAAO,MAAM,UAAU;QAC3B,IAAI,OAAO,MAAK,WAAW;GAChC;EACF;EACA,IAAI,OAAO,MAAK,WAAW;OACtB,IAAI,OAAO,OAAO,OAAO,KAAK,MAAM,KAAK,EAAE;OAC3C,IAAI,OAAO,OAAO,OAAO,KAAK,MAAM,IAAI;CAC/C;CAEA,MAAM,gBACJ,MACG,KAAI,MAAM,MAAM,MAAM,MAAM,GAAI,
|
|
1
|
+
{"version":3,"file":"parse-partial-json.mjs","names":[],"sources":["../../../../../../../ai/src/object-stream/parse-partial-json.ts"],"sourcesContent":["/**\n * Best-effort parse of a possibly-truncated JSON string into the value it\n * is \"on its way to\" becoming. Used by {@link streamObject} to emit a\n * partial object snapshot from each streamed delta before the full reply\n * has arrived.\n *\n * Returns `undefined` when the prefix can't yet be coerced into a value\n * (so the caller simply waits for more text). The FINAL parse in\n * `streamObject` is always strict `JSON.parse` — this tolerant parser only\n * powers the in-flight snapshots, so a too-clever completion never affects\n * the authoritative result.\n *\n * @example\n * parsePartialJson('{\"name\":\"Al'); // → { name: \"Al\" }\n * parsePartialJson('{\"items\":[1,2,'); // → { items: [1, 2] }\n * parsePartialJson('{\"a\":1,\"b\"'); // → { a: 1 } (drops the dangling key)\n */\nexport function parsePartialJson(text: string): unknown | undefined {\n const trimmed = text.trim();\n if (!trimmed) return undefined;\n\n // Fast path: already-valid JSON.\n const direct = tryParse(trimmed);\n if (direct.ok) return direct.value;\n\n const completed = completePartialJson(trimmed);\n if (completed === undefined) return undefined;\n\n const parsed = tryParse(completed);\n return parsed.ok ? parsed.value : undefined;\n}\n\nfunction tryParse(text: string): { ok: true; value: unknown } | { ok: false } {\n try {\n return { ok: true, value: JSON.parse(text) };\n } catch {\n return { ok: false };\n }\n}\n\n/**\n * Reconstruct a parseable JSON string from a truncated prefix by closing\n * open strings/containers and trimming dangling separators, keys, and\n * partial literals. Tries the most faithful completion first, then falls\n * back to dropping the unfinished tail.\n */\nfunction completePartialJson(text: string): string | undefined {\n const stack: Array<\"{\" | \"[\"> = [];\n let inString = false;\n let escaped = false;\n\n for (const ch of text) {\n if (inString) {\n if (escaped) escaped = false;\n else if (ch === \"\\\\\") escaped = true;\n else if (ch === '\"') inString = false;\n continue;\n }\n if (ch === '\"') inString = true;\n else if (ch === \"{\" || ch === \"[\") stack.push(ch);\n else if (ch === \"}\" || ch === \"]\") stack.pop();\n }\n\n const closers = () =>\n stack\n .map(c => (c === \"{\" ? \"}\" : \"]\"))\n .reverse()\n .join(\"\");\n\n // Faithful completion: close an open string, drop a trailing comma,\n // fill a dangling `key:` with null, then close containers.\n let core = text;\n if (inString) core += '\"';\n core = core.replace(/\\s+$/, \"\");\n if (core.endsWith(\",\")) core = core.slice(0, -1);\n if (core.endsWith(\":\")) core += \"null\";\n\n const attempts: string[] = [core + closers()];\n\n // Fallback 1: drop a dangling object key (a `\"...\"` with no value yet).\n if (stack[stack.length - 1] === \"{\") {\n const droppedKey = core.replace(/,?\\s*\"(?:[^\"\\\\]|\\\\.)*\"\\s*$/, \"\");\n attempts.push(droppedKey.replace(/,\\s*$/, \"\") + closers());\n }\n\n // Fallback 2: drop a partial trailing literal / number (e.g. `tr`, `12.`).\n const droppedLiteral = core.replace(/[:,]?\\s*[A-Za-z0-9.+\\-eE]+$/, match =>\n match.trimStart().startsWith(\":\") ? \":null\" : \"\",\n );\n attempts.push(droppedLiteral.replace(/,\\s*$/, \"\") + closers());\n\n for (const candidate of attempts) {\n if (tryParse(candidate).ok) return candidate;\n }\n\n return undefined;\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;AAiBA,SAAgB,iBAAiB,MAAmC;CAClE,MAAM,UAAU,KAAK,KAAK;CAC1B,IAAI,CAAC,SAAS,OAAO;CAGrB,MAAM,SAAS,SAAS,OAAO;CAC/B,IAAI,OAAO,IAAI,OAAO,OAAO;CAE7B,MAAM,YAAY,oBAAoB,OAAO;CAC7C,IAAI,cAAc,QAAW,OAAO;CAEpC,MAAM,SAAS,SAAS,SAAS;CACjC,OAAO,OAAO,KAAK,OAAO,QAAQ;AACpC;AAEA,SAAS,SAAS,MAA4D;CAC5E,IAAI;EACF,OAAO;GAAE,IAAI;GAAM,OAAO,KAAK,MAAM,IAAI;EAAE;CAC7C,QAAQ;EACN,OAAO,EAAE,IAAI,MAAM;CACrB;AACF;;;;;;;AAQA,SAAS,oBAAoB,MAAkC;CAC7D,MAAM,QAA0B,CAAC;CACjC,IAAI,WAAW;CACf,IAAI,UAAU;CAEd,KAAK,MAAM,MAAM,MAAM;EACrB,IAAI,UAAU;GACZ,IAAI,SAAS,UAAU;QAClB,IAAI,OAAO,MAAM,UAAU;QAC3B,IAAI,OAAO,MAAK,WAAW;GAChC;EACF;EACA,IAAI,OAAO,MAAK,WAAW;OACtB,IAAI,OAAO,OAAO,OAAO,KAAK,MAAM,KAAK,EAAE;OAC3C,IAAI,OAAO,OAAO,OAAO,KAAK,MAAM,IAAI;CAC/C;CAEA,MAAM,gBACJ,MACG,KAAI,MAAM,MAAM,MAAM,MAAM,GAAI,CAAC,CACjC,QAAQ,CAAC,CACT,KAAK,EAAE;CAIZ,IAAI,OAAO;CACX,IAAI,UAAU,QAAQ;CACtB,OAAO,KAAK,QAAQ,QAAQ,EAAE;CAC9B,IAAI,KAAK,SAAS,GAAG,GAAG,OAAO,KAAK,MAAM,GAAG,EAAE;CAC/C,IAAI,KAAK,SAAS,GAAG,GAAG,QAAQ;CAEhC,MAAM,WAAqB,CAAC,OAAO,QAAQ,CAAC;CAG5C,IAAI,MAAM,MAAM,SAAS,OAAO,KAAK;EACnC,MAAM,aAAa,KAAK,QAAQ,8BAA8B,EAAE;EAChE,SAAS,KAAK,WAAW,QAAQ,SAAS,EAAE,IAAI,QAAQ,CAAC;CAC3D;CAGA,MAAM,iBAAiB,KAAK,QAAQ,gCAA+B,UACjE,MAAM,UAAU,CAAC,CAAC,WAAW,GAAG,IAAI,UAAU,EAChD;CACA,SAAS,KAAK,eAAe,QAAQ,SAAS,EAAE,IAAI,QAAQ,CAAC;CAE7D,KAAK,MAAM,aAAa,UACtB,IAAI,SAAS,SAAS,CAAC,CAAC,IAAI,OAAO;AAIvC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stream-object.d.mts","names":[],"sources":["../../../../../../../ai/src/object-stream/stream-object.ts"],"mappings":";;;;;;;;;;AAiBA;;;;;;;KAAY,iBAAA;EACN,IAAA;EAAoB,KAAA;AAAA;EACpB,IAAA;EAAiB,KAAA;AAAA;EACjB,IAAA;EAAc,KAAA;EAAa,KAAA,EAAO,CAAA;EAAG,KAAA,EAAO,KAAA;AAAA;EAC5C,IAAA;EAAc,KAAA;EAAc,KAAA,EAAO,OAAA;EAAS,KAAA,EAAO,KAAA;AAAA;;KAG7C,kBAAA;EAHkD,6DAK5D,KAAA,EAAO,aAAA,EAFG;EAIV,QAAA,EAAU,OAAA,IAJkB;EAM5B,MAAA,EAAQ,gBAAA,CAAiB,CAAA,GAFf;EAIV,OAAA,GAAU,gBAAA;AAAA;;;;;;;;;;;;;;;AAAgB;AAsB5B;;;;iBAAuB,YAAA,
|
|
1
|
+
{"version":3,"file":"stream-object.d.mts","names":[],"sources":["../../../../../../../ai/src/object-stream/stream-object.ts"],"mappings":";;;;;;;;;;AAiBA;;;;;;;KAAY,iBAAA;EACN,IAAA;EAAoB,KAAA;AAAA;EACpB,IAAA;EAAiB,KAAA;AAAA;EACjB,IAAA;EAAc,KAAA;EAAa,KAAA,EAAO,CAAA;EAAG,KAAA,EAAO,KAAA;AAAA;EAC5C,IAAA;EAAc,KAAA;EAAc,KAAA,EAAO,OAAA;EAAS,KAAA,EAAO,KAAA;AAAA;;KAG7C,kBAAA;EAHkD,6DAK5D,KAAA,EAAO,aAAA,EAFG;EAIV,QAAA,EAAU,OAAA,IAJkB;EAM5B,MAAA,EAAQ,gBAAA,CAAiB,CAAA,GAFf;EAIV,OAAA,GAAU,gBAAA;AAAA;;;;;;;;;;;;;;;AAAgB;AAsB5B;;;;iBAAuB,YAAA,IACrB,MAAA,EAAQ,kBAAA,CAAmB,CAAA,IAC1B,aAAA,CAAc,iBAAA,CAAkB,CAAA;;iBAoEb,mBAAA,IACpB,MAAA,EAAQ,aAAA,CAAc,iBAAA,CAAkB,CAAA,KACvC,OAAA,CAAQ,OAAA,CAAQ,iBAAA,CAAkB,CAAA;EAAM,IAAA;AAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"stream-object.mjs","names":[],"sources":["../../../../../../../ai/src/object-stream/stream-object.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport type { Message } from \"../contracts/conversation-message.type\";\nimport type { ModelCallOptions, ModelContract } from \"../contracts/model.contract\";\nimport type { Usage } from \"../contracts/result/usage.type\";\nimport { AIError, SchemaValidationError } from \"../errors\";\nimport { parsePartialJson } from \"./parse-partial-json\";\n\n/**\n * One event in a {@link streamObject} run.\n *\n * - `text-delta` — the raw token text as it streams (for a \"typing\" view).\n * - `partial` — a best-effort snapshot of the object so far, re-parsed\n * from the accumulated text on each delta (only emitted when it changed).\n * - `done` — terminal: the final text is strictly parsed and validated\n * against the schema. `valid` + `value` on success; `valid: false` +\n * `error` when the output wasn't valid JSON or failed the schema.\n */\nexport type ObjectStreamEvent<T> =\n | { type: \"text-delta\"; delta: string }\n | { type: \"partial\"; value: unknown }\n | { type: \"done\"; valid: true; value: T; usage: Usage }\n | { type: \"done\"; valid: false; error: AIError; usage: Usage };\n\n/** Parameters for {@link streamObject}. */\nexport type StreamObjectParams<T> = {\n /** The model to stream from (e.g. `sdk.model({ name })`). */\n model: ModelContract;\n /** The prompt messages. */\n messages: Message[];\n /** Standard Schema the final object is validated against. */\n schema: StandardSchemaV1<T>;\n /** Extra model call options (e.g. `responseSchema`, `temperature`). */\n options?: ModelCallOptions;\n};\n\n/**\n * Stream a structured object: emit raw token deltas, progressively-parsed\n * partial-object snapshots, and a final strictly-validated object — the\n * first-class structured-output streaming primitive (A1). Reuses the\n * model's existing `stream()` seam; the partial snapshots come from a\n * tolerant {@link parsePartialJson}, while the terminal `done` event is a\n * strict `JSON.parse` + schema validation, so an over-eager partial parse\n * never affects the authoritative result.\n *\n * Pair it with a `structuredOutput`-capable model and a `responseSchema`\n * (via `options`) for the cleanest JSON; otherwise prompt the model to\n * reply with JSON only.\n *\n * @example\n * for await (const event of streamObject({ model, messages, schema })) {\n * if (event.type === \"partial\") render(event.value); // live UI\n * if (event.type === \"done\" && event.valid) save(event.value); // final\n * }\n */\nexport async function* streamObject<T>(\n params: StreamObjectParams<T>,\n): AsyncIterable<ObjectStreamEvent<T>> {\n const { model, messages, schema, options } = params;\n\n let accumulated = \"\";\n let lastPartialKey: string | undefined;\n let usage: Usage = { input: 0, output: 0, total: 0 };\n\n for await (const chunk of model.stream(messages, options)) {\n if (chunk.type === \"delta\") {\n accumulated += chunk.content;\n yield { type: \"text-delta\", delta: chunk.content };\n\n const partial = parsePartialJson(accumulated);\n if (partial !== undefined) {\n const key = safeStringify(partial);\n if (key !== lastPartialKey) {\n lastPartialKey = key;\n yield { type: \"partial\", value: partial };\n }\n }\n } else if (chunk.type === \"done\") {\n usage = chunk.usage;\n }\n }\n\n yield await finalize(accumulated, schema, usage);\n}\n\n/** Strict parse + schema validation of the complete streamed text. */\nasync function finalize<T>(\n text: string,\n schema: StandardSchemaV1<T>,\n usage: Usage,\n): Promise<ObjectStreamEvent<T>> {\n const cleaned = stripJsonFences(text).trim();\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(cleaned);\n } catch (cause) {\n return {\n type: \"done\",\n valid: false,\n error: new SchemaValidationError(\n \"streamObject: the final streamed output was not valid JSON\",\n { cause },\n ),\n usage,\n };\n }\n\n const result = await schema[\"~standard\"].validate(parsed);\n if (\"issues\" in result && result.issues) {\n return {\n type: \"done\",\n valid: false,\n error: new SchemaValidationError(\n \"streamObject: the streamed object failed schema validation\",\n { issues: result.issues },\n ),\n usage,\n };\n }\n\n return { type: \"done\", valid: true, value: (result as { value: T }).value, usage };\n}\n\n/** Collect a {@link streamObject} run down to just its terminal event. */\nexport async function collectStreamObject<T>(\n stream: AsyncIterable<ObjectStreamEvent<T>>,\n): Promise<Extract<ObjectStreamEvent<T>, { type: \"done\" }>> {\n let done: Extract<ObjectStreamEvent<T>, { type: \"done\" }> | undefined;\n for await (const event of stream) {\n if (event.type === \"done\") done = event;\n }\n if (!done) {\n throw new SchemaValidationError(\"streamObject: stream ended without a terminal event\");\n }\n return done;\n}\n\n/** Strip a leading/trailing ```json fence the model may wrap output in. */\nfunction stripJsonFences(text: string): string {\n const fenced = text.match(/```(?:json)?\\s*([\\s\\S]*?)\\s*```/i);\n return fenced ? fenced[1] : text;\n}\n\nfunction safeStringify(value: unknown): string {\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAsDA,gBAAuB,aACrB,QACqC;CACrC,MAAM,EAAE,OAAO,UAAU,QAAQ,YAAY;CAE7C,IAAI,cAAc;CAClB,IAAI;CACJ,IAAI,QAAe;EAAE,OAAO;EAAG,QAAQ;EAAG,OAAO;CAAE;CAEnD,WAAW,MAAM,SAAS,MAAM,OAAO,UAAU,OAAO,GACtD,IAAI,MAAM,SAAS,SAAS;EAC1B,eAAe,MAAM;EACrB,MAAM;GAAE,MAAM;GAAc,OAAO,MAAM;EAAQ;EAEjD,MAAM,UAAU,iBAAiB,WAAW;EAC5C,IAAI,YAAY,QAAW;GACzB,MAAM,MAAM,cAAc,OAAO;GACjC,IAAI,QAAQ,gBAAgB;IAC1B,iBAAiB;IACjB,MAAM;KAAE,MAAM;KAAW,OAAO;IAAQ;GAC1C;EACF;CACF,OAAO,IAAI,MAAM,SAAS,QACxB,QAAQ,MAAM;CAIlB,MAAM,MAAM,SAAS,aAAa,QAAQ,KAAK;AACjD;;AAGA,eAAe,SACb,MACA,QACA,OAC+B;CAC/B,MAAM,UAAU,gBAAgB,IAAI,
|
|
1
|
+
{"version":3,"file":"stream-object.mjs","names":[],"sources":["../../../../../../../ai/src/object-stream/stream-object.ts"],"sourcesContent":["import type { StandardSchemaV1 } from \"@standard-schema/spec\";\nimport type { Message } from \"../contracts/conversation-message.type\";\nimport type { ModelCallOptions, ModelContract } from \"../contracts/model.contract\";\nimport type { Usage } from \"../contracts/result/usage.type\";\nimport { AIError, SchemaValidationError } from \"../errors\";\nimport { parsePartialJson } from \"./parse-partial-json\";\n\n/**\n * One event in a {@link streamObject} run.\n *\n * - `text-delta` — the raw token text as it streams (for a \"typing\" view).\n * - `partial` — a best-effort snapshot of the object so far, re-parsed\n * from the accumulated text on each delta (only emitted when it changed).\n * - `done` — terminal: the final text is strictly parsed and validated\n * against the schema. `valid` + `value` on success; `valid: false` +\n * `error` when the output wasn't valid JSON or failed the schema.\n */\nexport type ObjectStreamEvent<T> =\n | { type: \"text-delta\"; delta: string }\n | { type: \"partial\"; value: unknown }\n | { type: \"done\"; valid: true; value: T; usage: Usage }\n | { type: \"done\"; valid: false; error: AIError; usage: Usage };\n\n/** Parameters for {@link streamObject}. */\nexport type StreamObjectParams<T> = {\n /** The model to stream from (e.g. `sdk.model({ name })`). */\n model: ModelContract;\n /** The prompt messages. */\n messages: Message[];\n /** Standard Schema the final object is validated against. */\n schema: StandardSchemaV1<T>;\n /** Extra model call options (e.g. `responseSchema`, `temperature`). */\n options?: ModelCallOptions;\n};\n\n/**\n * Stream a structured object: emit raw token deltas, progressively-parsed\n * partial-object snapshots, and a final strictly-validated object — the\n * first-class structured-output streaming primitive (A1). Reuses the\n * model's existing `stream()` seam; the partial snapshots come from a\n * tolerant {@link parsePartialJson}, while the terminal `done` event is a\n * strict `JSON.parse` + schema validation, so an over-eager partial parse\n * never affects the authoritative result.\n *\n * Pair it with a `structuredOutput`-capable model and a `responseSchema`\n * (via `options`) for the cleanest JSON; otherwise prompt the model to\n * reply with JSON only.\n *\n * @example\n * for await (const event of streamObject({ model, messages, schema })) {\n * if (event.type === \"partial\") render(event.value); // live UI\n * if (event.type === \"done\" && event.valid) save(event.value); // final\n * }\n */\nexport async function* streamObject<T>(\n params: StreamObjectParams<T>,\n): AsyncIterable<ObjectStreamEvent<T>> {\n const { model, messages, schema, options } = params;\n\n let accumulated = \"\";\n let lastPartialKey: string | undefined;\n let usage: Usage = { input: 0, output: 0, total: 0 };\n\n for await (const chunk of model.stream(messages, options)) {\n if (chunk.type === \"delta\") {\n accumulated += chunk.content;\n yield { type: \"text-delta\", delta: chunk.content };\n\n const partial = parsePartialJson(accumulated);\n if (partial !== undefined) {\n const key = safeStringify(partial);\n if (key !== lastPartialKey) {\n lastPartialKey = key;\n yield { type: \"partial\", value: partial };\n }\n }\n } else if (chunk.type === \"done\") {\n usage = chunk.usage;\n }\n }\n\n yield await finalize(accumulated, schema, usage);\n}\n\n/** Strict parse + schema validation of the complete streamed text. */\nasync function finalize<T>(\n text: string,\n schema: StandardSchemaV1<T>,\n usage: Usage,\n): Promise<ObjectStreamEvent<T>> {\n const cleaned = stripJsonFences(text).trim();\n\n let parsed: unknown;\n try {\n parsed = JSON.parse(cleaned);\n } catch (cause) {\n return {\n type: \"done\",\n valid: false,\n error: new SchemaValidationError(\n \"streamObject: the final streamed output was not valid JSON\",\n { cause },\n ),\n usage,\n };\n }\n\n const result = await schema[\"~standard\"].validate(parsed);\n if (\"issues\" in result && result.issues) {\n return {\n type: \"done\",\n valid: false,\n error: new SchemaValidationError(\n \"streamObject: the streamed object failed schema validation\",\n { issues: result.issues },\n ),\n usage,\n };\n }\n\n return { type: \"done\", valid: true, value: (result as { value: T }).value, usage };\n}\n\n/** Collect a {@link streamObject} run down to just its terminal event. */\nexport async function collectStreamObject<T>(\n stream: AsyncIterable<ObjectStreamEvent<T>>,\n): Promise<Extract<ObjectStreamEvent<T>, { type: \"done\" }>> {\n let done: Extract<ObjectStreamEvent<T>, { type: \"done\" }> | undefined;\n for await (const event of stream) {\n if (event.type === \"done\") done = event;\n }\n if (!done) {\n throw new SchemaValidationError(\"streamObject: stream ended without a terminal event\");\n }\n return done;\n}\n\n/** Strip a leading/trailing ```json fence the model may wrap output in. */\nfunction stripJsonFences(text: string): string {\n const fenced = text.match(/```(?:json)?\\s*([\\s\\S]*?)\\s*```/i);\n return fenced ? fenced[1] : text;\n}\n\nfunction safeStringify(value: unknown): string {\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAsDA,gBAAuB,aACrB,QACqC;CACrC,MAAM,EAAE,OAAO,UAAU,QAAQ,YAAY;CAE7C,IAAI,cAAc;CAClB,IAAI;CACJ,IAAI,QAAe;EAAE,OAAO;EAAG,QAAQ;EAAG,OAAO;CAAE;CAEnD,WAAW,MAAM,SAAS,MAAM,OAAO,UAAU,OAAO,GACtD,IAAI,MAAM,SAAS,SAAS;EAC1B,eAAe,MAAM;EACrB,MAAM;GAAE,MAAM;GAAc,OAAO,MAAM;EAAQ;EAEjD,MAAM,UAAU,iBAAiB,WAAW;EAC5C,IAAI,YAAY,QAAW;GACzB,MAAM,MAAM,cAAc,OAAO;GACjC,IAAI,QAAQ,gBAAgB;IAC1B,iBAAiB;IACjB,MAAM;KAAE,MAAM;KAAW,OAAO;IAAQ;GAC1C;EACF;CACF,OAAO,IAAI,MAAM,SAAS,QACxB,QAAQ,MAAM;CAIlB,MAAM,MAAM,SAAS,aAAa,QAAQ,KAAK;AACjD;;AAGA,eAAe,SACb,MACA,QACA,OAC+B;CAC/B,MAAM,UAAU,gBAAgB,IAAI,CAAC,CAAC,KAAK;CAE3C,IAAI;CACJ,IAAI;EACF,SAAS,KAAK,MAAM,OAAO;CAC7B,SAAS,OAAO;EACd,OAAO;GACL,MAAM;GACN,OAAO;GACP,OAAO,IAAI,sBACT,8DACA,EAAE,MAAM,CACV;GACA;EACF;CACF;CAEA,MAAM,SAAS,MAAM,OAAO,YAAY,CAAC,SAAS,MAAM;CACxD,IAAI,YAAY,UAAU,OAAO,QAC/B,OAAO;EACL,MAAM;EACN,OAAO;EACP,OAAO,IAAI,sBACT,8DACA,EAAE,QAAQ,OAAO,OAAO,CAC1B;EACA;CACF;CAGF,OAAO;EAAE,MAAM;EAAQ,OAAO;EAAM,OAAQ,OAAwB;EAAO;CAAM;AACnF;;AAGA,eAAsB,oBACpB,QAC0D;CAC1D,IAAI;CACJ,WAAW,MAAM,SAAS,QACxB,IAAI,MAAM,SAAS,QAAQ,OAAO;CAEpC,IAAI,CAAC,MACH,MAAM,IAAI,sBAAsB,qDAAqD;CAEvF,OAAO;AACT;;AAGA,SAAS,gBAAgB,MAAsB;CAC7C,MAAM,SAAS,KAAK,MAAM,kCAAkC;CAC5D,OAAO,SAAS,OAAO,KAAK;AAC9B;AAEA,SAAS,cAAc,OAAwB;CAC7C,IAAI;EACF,OAAO,KAAK,UAAU,KAAK;CAC7B,QAAQ;EACN,OAAO,OAAO,KAAK;CACrB;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"observer-registry.d.mts","names":[],"sources":["../../../../../../../ai/src/observe/observer-registry.ts"],"mappings":";;;;;AAwBA;;;;iBAAgB,gBAAA,CAAiB,QAAkB,EAAR,QAAQ;AAUnD;;;;AAAiD;AAQjD;AARA,iBAAgB,YAAA,
|
|
1
|
+
{"version":3,"file":"observer-registry.d.mts","names":[],"sources":["../../../../../../../ai/src/observe/observer-registry.ts"],"mappings":";;;;;AAwBA;;;;iBAAgB,gBAAA,CAAiB,QAAkB,EAAR,QAAQ;AAUnD;;;;AAAiD;AAQjD;AARA,iBAAgB,YAAA,aAAyB,QAAQ;;;AAQL;AAQ5C;iBARgB,aAAA,CAAc,KAAc;;;AAQhB;;iBAAZ,YAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"as-tool.d.mts","names":[],"sources":["../../../../../../../ai/src/orchestrator/as-tool.ts"],"mappings":";;;;;;AA2FA;;;;;;;;;;;;;;;;;;;;;;;;;;;AAGmC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAHnB,MAAA,
|
|
1
|
+
{"version":3,"file":"as-tool.d.mts","names":[],"sources":["../../../../../../../ai/src/orchestrator/as-tool.ts"],"mappings":";;;;;;AA2FA;;;;;;;;;;;;;;;;;;;;;;;;;;;AAGmC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAHnB,MAAA,8BACd,YAAA,EAAc,oBAAA,CAAqB,OAAA,EAAS,MAAA,GAC5C,OAAA,EAAS,yBAAA,CAA0B,UAAA,IAClC,YAAA,CAAa,UAAA,EAAY,OAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"checkpoint.mjs","names":[],"sources":["../../../../../../../ai/src/orchestrator/checkpoint.ts"],"sourcesContent":["import type { CheckpointRecord } from \"../contracts/orchestrator/checkpoint-store.contract\";\nimport type { Next } from \"../contracts/supervisor/next.type\";\nimport type { OrchestratorEngineContext } from \"./engine-context.type\";\n\n/** Framework default for snapshot retention per session (§4 Phase 6). */\nexport const DEFAULT_KEEP_SNAPSHOTS = 100;\n\n/** Fields the engine supplies to build a fresh checkpoint row. */\nexport type PersistParams<TState> = {\n ctx: OrchestratorEngineContext<unknown, TState>;\n sessionId: string;\n turnIndex: number;\n state: unknown;\n /** Dispatch decision summary — a single intent, fan-out list, or null. */\n lastRoute: string | string[] | null;\n /** Carried forward from the loaded checkpoint (compaction progress). */\n summarizedThrough: number | null;\n};\n\n/**\n * Summarize a supervisor dispatch decision into the `last_route`\n * checkpoint column (§4 Phase 6). A bare string passes through; a\n * fan-out array passes through; the `END` sentinel and anything else\n * collapse to `null` (the turn routed nowhere worth recording).\n */\nexport function summarizeRoute(next: Next | undefined): string | string[] | null {\n if (typeof next === \"string\") {\n return next === \"__warlock:end__\" ? null : next;\n }\n\n if (Array.isArray(next)) {\n return next;\n }\n\n return null;\n}\n\n/**\n * Phase 6 — persist checkpoint (orchestrator.md §3 / §4 Phase 6).\n * Writes a fresh append-only row at `turn_index = N` carrying the\n * post-merge `state`, the current `signature` (read by the next call's\n * Phase 2), the informational `version`, the dispatch `last_route`, and\n * the carried-forward `summarized_through`. Lock columns are written\n * `null` here — they are populated only by an in-flight Phase 7\n * compaction.\n *\n * Emits `orchestrator.checkpoint.persisted` after the row lands, then\n * prunes to `keepSnapshots` (default 100; `\"all\"` opts out) — §4 Phase\n * 6 pruning (Q20). Pruning runs only when the store exposes the\n * optional `prune` hook (memory/pg/redis own their own pruning); when\n * absent the write still succeeds.\n *\n * Returns the persisted record so the engine can fold it into the\n * turn's report.\n */\nexport async function persistCheckpoint<TState>(\n params: PersistParams<TState>,\n): Promise<CheckpointRecord> {\n const { ctx, sessionId, turnIndex, state, lastRoute, summarizedThrough } = params;\n\n const record: CheckpointRecord = {\n orchestrator_name: ctx.config.name,\n session_id: sessionId,\n turn_index: turnIndex,\n state,\n last_route: lastRoute,\n signature: ctx.signature,\n version: ctx.config.version ?? null,\n summarized_through: summarizedThrough,\n lock_acquired_at: null,\n lock_expires_at: null,\n saved_at: new Date().toISOString(),\n };\n\n await ctx.checkpointStore.save(record);\n\n ctx.emitter.emit(\"orchestrator.checkpoint.persisted\", {\n sessionId,\n turnIndex,\n });\n\n await prune(ctx, sessionId, turnIndex);\n\n return record;\n}\n\n/**\n * Prune session rows older than `max_turn_index - keepSnapshots` (§4\n * Phase 6). Synchronous-after-save; opted out with\n * `keepSnapshots: \"all\"`. Delegated to an optional store-side `prune`\n * hook so each driver implements deletion in its own dialect — the\n * engine never reaches into store internals.\n */\nasync function prune<TState>(\n ctx: OrchestratorEngineContext<unknown, TState>,\n sessionId: string,\n latestTurnIndex: number,\n): Promise<void> {\n const keep = ctx.config.keepSnapshots ?? DEFAULT_KEEP_SNAPSHOTS;\n\n if (keep === \"all\") {\n return;\n }\n\n const store = ctx.checkpointStore as {\n prune?: (\n orchestratorName: string,\n sessionId: string,\n keepBeforeTurnIndex: number,\n ) => Promise<void>;\n };\n\n if (typeof store.prune !== \"function\") {\n return;\n }\n\n const keepBeforeTurnIndex = latestTurnIndex - keep + 1;\n\n if (keepBeforeTurnIndex <= 0) {\n return;\n }\n\n await store.prune(ctx.config.name, sessionId, keepBeforeTurnIndex);\n}\n"],"mappings":";;AAKA,MAAa,yBAAyB;;;;;;;AAoBtC,SAAgB,eAAe,MAAkD;CAC/E,IAAI,OAAO,SAAS,UAClB,OAAO,SAAS,oBAAoB,OAAO;CAG7C,IAAI,MAAM,QAAQ,IAAI,GACpB,OAAO;CAGT,OAAO;AACT;;;;;;;;;;;;;;;;;;;AAoBA,eAAsB,kBACpB,QAC2B;CAC3B,MAAM,EAAE,KAAK,WAAW,WAAW,OAAO,WAAW,sBAAsB;CAE3E,MAAM,SAA2B;EAC/B,mBAAmB,IAAI,OAAO;EAC9B,YAAY;EACZ,YAAY;EACZ;EACA,YAAY;EACZ,WAAW,IAAI;EACf,SAAS,IAAI,OAAO,WAAW;EAC/B,oBAAoB;EACpB,kBAAkB;EAClB,iBAAiB;EACjB,2BAAU,IAAI,KAAK,
|
|
1
|
+
{"version":3,"file":"checkpoint.mjs","names":[],"sources":["../../../../../../../ai/src/orchestrator/checkpoint.ts"],"sourcesContent":["import type { CheckpointRecord } from \"../contracts/orchestrator/checkpoint-store.contract\";\nimport type { Next } from \"../contracts/supervisor/next.type\";\nimport type { OrchestratorEngineContext } from \"./engine-context.type\";\n\n/** Framework default for snapshot retention per session (§4 Phase 6). */\nexport const DEFAULT_KEEP_SNAPSHOTS = 100;\n\n/** Fields the engine supplies to build a fresh checkpoint row. */\nexport type PersistParams<TState> = {\n ctx: OrchestratorEngineContext<unknown, TState>;\n sessionId: string;\n turnIndex: number;\n state: unknown;\n /** Dispatch decision summary — a single intent, fan-out list, or null. */\n lastRoute: string | string[] | null;\n /** Carried forward from the loaded checkpoint (compaction progress). */\n summarizedThrough: number | null;\n};\n\n/**\n * Summarize a supervisor dispatch decision into the `last_route`\n * checkpoint column (§4 Phase 6). A bare string passes through; a\n * fan-out array passes through; the `END` sentinel and anything else\n * collapse to `null` (the turn routed nowhere worth recording).\n */\nexport function summarizeRoute(next: Next | undefined): string | string[] | null {\n if (typeof next === \"string\") {\n return next === \"__warlock:end__\" ? null : next;\n }\n\n if (Array.isArray(next)) {\n return next;\n }\n\n return null;\n}\n\n/**\n * Phase 6 — persist checkpoint (orchestrator.md §3 / §4 Phase 6).\n * Writes a fresh append-only row at `turn_index = N` carrying the\n * post-merge `state`, the current `signature` (read by the next call's\n * Phase 2), the informational `version`, the dispatch `last_route`, and\n * the carried-forward `summarized_through`. Lock columns are written\n * `null` here — they are populated only by an in-flight Phase 7\n * compaction.\n *\n * Emits `orchestrator.checkpoint.persisted` after the row lands, then\n * prunes to `keepSnapshots` (default 100; `\"all\"` opts out) — §4 Phase\n * 6 pruning (Q20). Pruning runs only when the store exposes the\n * optional `prune` hook (memory/pg/redis own their own pruning); when\n * absent the write still succeeds.\n *\n * Returns the persisted record so the engine can fold it into the\n * turn's report.\n */\nexport async function persistCheckpoint<TState>(\n params: PersistParams<TState>,\n): Promise<CheckpointRecord> {\n const { ctx, sessionId, turnIndex, state, lastRoute, summarizedThrough } = params;\n\n const record: CheckpointRecord = {\n orchestrator_name: ctx.config.name,\n session_id: sessionId,\n turn_index: turnIndex,\n state,\n last_route: lastRoute,\n signature: ctx.signature,\n version: ctx.config.version ?? null,\n summarized_through: summarizedThrough,\n lock_acquired_at: null,\n lock_expires_at: null,\n saved_at: new Date().toISOString(),\n };\n\n await ctx.checkpointStore.save(record);\n\n ctx.emitter.emit(\"orchestrator.checkpoint.persisted\", {\n sessionId,\n turnIndex,\n });\n\n await prune(ctx, sessionId, turnIndex);\n\n return record;\n}\n\n/**\n * Prune session rows older than `max_turn_index - keepSnapshots` (§4\n * Phase 6). Synchronous-after-save; opted out with\n * `keepSnapshots: \"all\"`. Delegated to an optional store-side `prune`\n * hook so each driver implements deletion in its own dialect — the\n * engine never reaches into store internals.\n */\nasync function prune<TState>(\n ctx: OrchestratorEngineContext<unknown, TState>,\n sessionId: string,\n latestTurnIndex: number,\n): Promise<void> {\n const keep = ctx.config.keepSnapshots ?? DEFAULT_KEEP_SNAPSHOTS;\n\n if (keep === \"all\") {\n return;\n }\n\n const store = ctx.checkpointStore as {\n prune?: (\n orchestratorName: string,\n sessionId: string,\n keepBeforeTurnIndex: number,\n ) => Promise<void>;\n };\n\n if (typeof store.prune !== \"function\") {\n return;\n }\n\n const keepBeforeTurnIndex = latestTurnIndex - keep + 1;\n\n if (keepBeforeTurnIndex <= 0) {\n return;\n }\n\n await store.prune(ctx.config.name, sessionId, keepBeforeTurnIndex);\n}\n"],"mappings":";;AAKA,MAAa,yBAAyB;;;;;;;AAoBtC,SAAgB,eAAe,MAAkD;CAC/E,IAAI,OAAO,SAAS,UAClB,OAAO,SAAS,oBAAoB,OAAO;CAG7C,IAAI,MAAM,QAAQ,IAAI,GACpB,OAAO;CAGT,OAAO;AACT;;;;;;;;;;;;;;;;;;;AAoBA,eAAsB,kBACpB,QAC2B;CAC3B,MAAM,EAAE,KAAK,WAAW,WAAW,OAAO,WAAW,sBAAsB;CAE3E,MAAM,SAA2B;EAC/B,mBAAmB,IAAI,OAAO;EAC9B,YAAY;EACZ,YAAY;EACZ;EACA,YAAY;EACZ,WAAW,IAAI;EACf,SAAS,IAAI,OAAO,WAAW;EAC/B,oBAAoB;EACpB,kBAAkB;EAClB,iBAAiB;EACjB,2BAAU,IAAI,KAAK,EAAC,CAAC,YAAY;CACnC;CAEA,MAAM,IAAI,gBAAgB,KAAK,MAAM;CAErC,IAAI,QAAQ,KAAK,qCAAqC;EACpD;EACA;CACF,CAAC;CAED,MAAM,MAAM,KAAK,WAAW,SAAS;CAErC,OAAO;AACT;;;;;;;;AASA,eAAe,MACb,KACA,WACA,iBACe;CACf,MAAM,OAAO,IAAI,OAAO;CAExB,IAAI,SAAS,OACX;CAGF,MAAM,QAAQ,IAAI;CAQlB,IAAI,OAAO,MAAM,UAAU,YACzB;CAGF,MAAM,sBAAsB,kBAAkB,OAAO;CAErD,IAAI,uBAAuB,GACzB;CAGF,MAAM,MAAM,MAAM,IAAI,OAAO,MAAM,WAAW,mBAAmB;AACnE"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"compaction.mjs","names":[],"sources":["../../../../../../../ai/src/orchestrator/compaction.ts"],"sourcesContent":["import type { Message } from \"../contracts/conversation-message.type\";\nimport type {\n SummarizeCallback,\n SummarizeConfig,\n} from \"../contracts/orchestrator/orchestrator-config.type\";\nimport type { CompactionResult } from \"../contracts/result/orchestrator-result.type\";\nimport type { OrchestratorEngineContext } from \"./engine-context.type\";\nimport { DEFAULT_LOCK_MAX_WAIT } from \"./lock\";\n\n/**\n * Most-recent messages kept verbatim when `summarize.keep` is omitted.\n * Defaulting to 0 would compact the entire history into one memo (losing\n * the live tail every turn), so the object form keeps a small recent\n * window by default — matching the canonical doc examples (`keep: 8`).\n */\nexport const DEFAULT_COMPACTION_KEEP = 8;\n\n/** Whether the configured `summarize` is the fully-pluggable callback form. */\nfunction isCallbackForm(\n summarize: SummarizeConfig | SummarizeCallback | undefined,\n): summarize is SummarizeCallback {\n return typeof summarize === \"function\";\n}\n\n/**\n * Decide whether Phase 7 compaction should fire this turn (orchestrator\n * .md §4 Phase 7 / §12.1). v1 trigger is count-based:\n * `summarize.afterTurns` fires once `turnIndex >= afterTurns`. The\n * callback form has no threshold and never auto-fires — it is driven\n * exclusively by `command(\"compact\")` (the manual path). Returns false\n * when `summarize` is unset.\n */\nexport function shouldCompact(\n ctx: OrchestratorEngineContext,\n turnIndex: number,\n): boolean {\n const summarize = ctx.config.summarize;\n\n if (summarize === undefined || isCallbackForm(summarize)) {\n return false;\n }\n\n const afterTurns = summarize.afterTurns;\n\n return afterTurns !== undefined && turnIndex >= afterTurns;\n}\n\n/** Result of a Phase-7 run — what the engine folds into the turn. */\nexport type CompactionOutcome = {\n /** The produced compaction, surfaced on `result.compaction`. */\n compaction: CompactionResult;\n /**\n * Whether `summarize.onCompact` ran AND succeeded — when true the\n * engine advances `summarized_through` to `replacesToIndex` (§12.2\n * step 4); when false it leaves it unchanged (§12.2 step 5).\n */\n applied: boolean;\n};\n\n/**\n * Run the configured summarizer against the session history and build\n * a {@link CompactionResult} (§12.2 step 2–3). Three resolution paths:\n *\n * - callback form — `summarize(history)` returns the result directly.\n * - config + `summarizer` model — summarize the slice (history minus\n * the most-recent `keep`) into one synthetic memo turn.\n * - config without a `summarizer` — produce a trivial degenerate memo\n * (no model available); the dev is expected to supply a summarizer\n * for real compaction. The range still reflects the kept tail.\n */\nasync function produceCompaction(\n summarize: SummarizeConfig | SummarizeCallback,\n history: Message[],\n): Promise<CompactionResult> {\n if (isCallbackForm(summarize)) {\n return summarize(history);\n }\n\n const keep = summarize.keep ?? DEFAULT_COMPACTION_KEEP;\n const replacesFromIndex = 0;\n const replacesToIndex = Math.max(-1, history.length - keep - 1);\n const slice = history.slice(0, replacesToIndex + 1);\n\n const summaryText = await summarizeSlice(summarize, slice);\n\n return {\n summary: { role: \"system\", content: summaryText },\n replacesFromIndex,\n replacesToIndex,\n };\n}\n\n/**\n * Summarize a slice of history into text. Uses the configured\n * `summarizer` model when present (a single non-streaming completion);\n * otherwise returns a placeholder memo. The summarizer is intentionally\n * the cheap model — never the specialists (§12.4).\n */\nasync function summarizeSlice(\n summarize: SummarizeConfig,\n slice: Message[],\n): Promise<string> {\n if (slice.length === 0) {\n return \"\";\n }\n\n if (!summarize.summarizer) {\n return `Summary of ${slice.length} prior message(s).`;\n }\n\n const transcript = slice\n .map((message) => `${message.role}: ${stringifyContent(message.content)}`)\n .join(\"\\n\");\n\n const response = await summarize.summarizer.complete([\n {\n role: \"system\",\n content:\n \"Summarize the following conversation slice into a concise memo \" +\n \"that preserves the facts, decisions, and open threads a later \" +\n \"turn would need. Reply with the memo only.\",\n },\n { role: \"user\", content: transcript },\n ]);\n\n return response.content;\n}\n\n/** Coerce a message's content into a flat string for the summarizer prompt. */\nfunction stringifyContent(content: Message[\"content\"]): string {\n if (typeof content === \"string\") {\n return content;\n }\n\n return JSON.stringify(content);\n}\n\n/**\n * Phase 7 — post-turn compaction (orchestrator.md §3 / §4 Phase 7 /\n * §12.2). Runs AFTER the turn settles; never blocks the caller's\n * resolution path beyond this phase.\n *\n * Acquires the cooperative session lock (writes `lock_acquired_at` /\n * `lock_expires_at` onto a fresh row), runs the summarizer, builds the\n * compaction, then either invokes `summarize.onCompact` (framework-\n * driven apply) or surfaces the compaction on the result for the dev to\n * apply. Releases the lock on settle. Emits\n * `orchestrator.compaction.suggested` always,\n * `orchestrator.compaction.applied` only when `onCompact` succeeded, and\n * `orchestrator.compaction.failed` (carrying the thrown `error` and the\n * `phase` that failed) when the summarizer or `onCompact` throws.\n *\n * Retry policy is skip-and-log (§4 Phase 7): a summarizer failure\n * leaves the session running with unchanged history — the engine\n * treats a thrown summarizer / `onCompact` as \"no compaction this\n * turn\", emits `orchestrator.compaction.failed`, and returns `undefined`\n * (summarizer) or surfaces the unapplied compaction (`onCompact`).\n */\nexport async function runCompaction<TState>(\n ctx: OrchestratorEngineContext<unknown, TState>,\n sessionId: string,\n history: Message[],\n): Promise<CompactionOutcome | undefined> {\n const summarize = ctx.config.summarize;\n\n if (summarize === undefined) {\n return undefined;\n }\n\n await acquireCompactionLock(ctx, sessionId);\n\n try {\n const compaction = await produceCompaction(summarize, history);\n\n ctx.emitter.emit(\"orchestrator.compaction.suggested\", {\n sessionId,\n compaction,\n });\n\n const onCompact = isCallbackForm(summarize) ? undefined : summarize.onCompact;\n\n if (!onCompact) {\n return { compaction, applied: false };\n }\n\n try {\n await onCompact(compaction, { sessionId });\n\n ctx.emitter.emit(\"orchestrator.compaction.applied\", {\n sessionId,\n compaction,\n });\n\n return { compaction, applied: true };\n } catch (error) {\n // onCompact threw — surface the compaction for the dev to apply,\n // leave summarized_through unchanged (§12.2 step 4 failure path).\n ctx.emitter.emit(\"orchestrator.compaction.failed\", {\n sessionId,\n phase: \"onCompact\",\n error,\n });\n\n return { compaction, applied: false };\n }\n } catch (error) {\n // Summarizer failed — skip-and-log; session keeps running unchanged.\n ctx.emitter.emit(\"orchestrator.compaction.failed\", {\n sessionId,\n phase: \"summarize\",\n error,\n });\n\n return undefined;\n } finally {\n await releaseCompactionLock(ctx, sessionId);\n }\n}\n\n/**\n * Run a manual compaction for `command(\"compact\", ...)` (§11 / §12.1).\n * Same code path as the post-turn trigger but driven on demand against\n * the supplied history, returning the raw {@link CompactionResult}. The\n * callback form is honored; the config form without a `summarizer`\n * produces the degenerate memo. Does not apply `onCompact` — the\n * command surface returns the compaction for the caller to handle.\n */\nexport async function runManualCompaction<TState>(\n ctx: OrchestratorEngineContext<unknown, TState>,\n history: Message[],\n): Promise<CompactionResult> {\n const summarize = ctx.config.summarize;\n\n if (summarize === undefined) {\n // No summarize policy configured — produce a degenerate memo over\n // the full supplied history so the command always resolves.\n return {\n summary: { role: \"system\", content: `Summary of ${history.length} message(s).` },\n replacesFromIndex: 0,\n replacesToIndex: Math.max(-1, history.length - 1),\n };\n }\n\n return produceCompaction(summarize, history);\n}\n\n/** Resolve the lock TTL from the summarize config (config form only). */\nfunction resolveLockMaxWait(ctx: OrchestratorEngineContext): number {\n const summarize = ctx.config.summarize;\n\n if (summarize === undefined || isCallbackForm(summarize)) {\n return DEFAULT_LOCK_MAX_WAIT;\n }\n\n return summarize.lock?.maxWait ?? DEFAULT_LOCK_MAX_WAIT;\n}\n\n/**\n * Write the cooperative compaction lock onto a fresh checkpoint row\n * (§12.2 step 1). The lock lives on the latest persisted row; we load\n * it, stamp the lock columns, and re-save (append-only) so the next\n * turn's Phase 3 observes it.\n */\nasync function acquireCompactionLock<TState>(\n ctx: OrchestratorEngineContext<unknown, TState>,\n sessionId: string,\n): Promise<void> {\n const latest = await ctx.checkpointStore.load(ctx.config.name, sessionId);\n\n if (!latest) {\n return;\n }\n\n const now = Date.now();\n const maxWait = resolveLockMaxWait(ctx as OrchestratorEngineContext);\n\n await ctx.checkpointStore.save({\n ...latest,\n lock_acquired_at: new Date(now).toISOString(),\n lock_expires_at: new Date(now + maxWait).toISOString(),\n saved_at: new Date(now).toISOString(),\n });\n}\n\n/**\n * Clear the compaction lock columns (§12.2 step 6) by re-saving the\n * latest row with the lock fields nulled.\n */\nasync function releaseCompactionLock<TState>(\n ctx: OrchestratorEngineContext<unknown, TState>,\n sessionId: string,\n): Promise<void> {\n const latest = await ctx.checkpointStore.load(ctx.config.name, sessionId);\n\n if (!latest) {\n return;\n }\n\n await ctx.checkpointStore.save({\n ...latest,\n lock_acquired_at: null,\n lock_expires_at: null,\n saved_at: new Date().toISOString(),\n });\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"compaction.mjs","names":[],"sources":["../../../../../../../ai/src/orchestrator/compaction.ts"],"sourcesContent":["import type { Message } from \"../contracts/conversation-message.type\";\nimport type {\n SummarizeCallback,\n SummarizeConfig,\n} from \"../contracts/orchestrator/orchestrator-config.type\";\nimport type { CompactionResult } from \"../contracts/result/orchestrator-result.type\";\nimport type { OrchestratorEngineContext } from \"./engine-context.type\";\nimport { DEFAULT_LOCK_MAX_WAIT } from \"./lock\";\n\n/**\n * Most-recent messages kept verbatim when `summarize.keep` is omitted.\n * Defaulting to 0 would compact the entire history into one memo (losing\n * the live tail every turn), so the object form keeps a small recent\n * window by default — matching the canonical doc examples (`keep: 8`).\n */\nexport const DEFAULT_COMPACTION_KEEP = 8;\n\n/** Whether the configured `summarize` is the fully-pluggable callback form. */\nfunction isCallbackForm(\n summarize: SummarizeConfig | SummarizeCallback | undefined,\n): summarize is SummarizeCallback {\n return typeof summarize === \"function\";\n}\n\n/**\n * Decide whether Phase 7 compaction should fire this turn (orchestrator\n * .md §4 Phase 7 / §12.1). v1 trigger is count-based:\n * `summarize.afterTurns` fires once `turnIndex >= afterTurns`. The\n * callback form has no threshold and never auto-fires — it is driven\n * exclusively by `command(\"compact\")` (the manual path). Returns false\n * when `summarize` is unset.\n */\nexport function shouldCompact(\n ctx: OrchestratorEngineContext,\n turnIndex: number,\n): boolean {\n const summarize = ctx.config.summarize;\n\n if (summarize === undefined || isCallbackForm(summarize)) {\n return false;\n }\n\n const afterTurns = summarize.afterTurns;\n\n return afterTurns !== undefined && turnIndex >= afterTurns;\n}\n\n/** Result of a Phase-7 run — what the engine folds into the turn. */\nexport type CompactionOutcome = {\n /** The produced compaction, surfaced on `result.compaction`. */\n compaction: CompactionResult;\n /**\n * Whether `summarize.onCompact` ran AND succeeded — when true the\n * engine advances `summarized_through` to `replacesToIndex` (§12.2\n * step 4); when false it leaves it unchanged (§12.2 step 5).\n */\n applied: boolean;\n};\n\n/**\n * Run the configured summarizer against the session history and build\n * a {@link CompactionResult} (§12.2 step 2–3). Three resolution paths:\n *\n * - callback form — `summarize(history)` returns the result directly.\n * - config + `summarizer` model — summarize the slice (history minus\n * the most-recent `keep`) into one synthetic memo turn.\n * - config without a `summarizer` — produce a trivial degenerate memo\n * (no model available); the dev is expected to supply a summarizer\n * for real compaction. The range still reflects the kept tail.\n */\nasync function produceCompaction(\n summarize: SummarizeConfig | SummarizeCallback,\n history: Message[],\n): Promise<CompactionResult> {\n if (isCallbackForm(summarize)) {\n return summarize(history);\n }\n\n const keep = summarize.keep ?? DEFAULT_COMPACTION_KEEP;\n const replacesFromIndex = 0;\n const replacesToIndex = Math.max(-1, history.length - keep - 1);\n const slice = history.slice(0, replacesToIndex + 1);\n\n const summaryText = await summarizeSlice(summarize, slice);\n\n return {\n summary: { role: \"system\", content: summaryText },\n replacesFromIndex,\n replacesToIndex,\n };\n}\n\n/**\n * Summarize a slice of history into text. Uses the configured\n * `summarizer` model when present (a single non-streaming completion);\n * otherwise returns a placeholder memo. The summarizer is intentionally\n * the cheap model — never the specialists (§12.4).\n */\nasync function summarizeSlice(\n summarize: SummarizeConfig,\n slice: Message[],\n): Promise<string> {\n if (slice.length === 0) {\n return \"\";\n }\n\n if (!summarize.summarizer) {\n return `Summary of ${slice.length} prior message(s).`;\n }\n\n const transcript = slice\n .map((message) => `${message.role}: ${stringifyContent(message.content)}`)\n .join(\"\\n\");\n\n const response = await summarize.summarizer.complete([\n {\n role: \"system\",\n content:\n \"Summarize the following conversation slice into a concise memo \" +\n \"that preserves the facts, decisions, and open threads a later \" +\n \"turn would need. Reply with the memo only.\",\n },\n { role: \"user\", content: transcript },\n ]);\n\n return response.content;\n}\n\n/** Coerce a message's content into a flat string for the summarizer prompt. */\nfunction stringifyContent(content: Message[\"content\"]): string {\n if (typeof content === \"string\") {\n return content;\n }\n\n return JSON.stringify(content);\n}\n\n/**\n * Phase 7 — post-turn compaction (orchestrator.md §3 / §4 Phase 7 /\n * §12.2). Runs AFTER the turn settles; never blocks the caller's\n * resolution path beyond this phase.\n *\n * Acquires the cooperative session lock (writes `lock_acquired_at` /\n * `lock_expires_at` onto a fresh row), runs the summarizer, builds the\n * compaction, then either invokes `summarize.onCompact` (framework-\n * driven apply) or surfaces the compaction on the result for the dev to\n * apply. Releases the lock on settle. Emits\n * `orchestrator.compaction.suggested` always,\n * `orchestrator.compaction.applied` only when `onCompact` succeeded, and\n * `orchestrator.compaction.failed` (carrying the thrown `error` and the\n * `phase` that failed) when the summarizer or `onCompact` throws.\n *\n * Retry policy is skip-and-log (§4 Phase 7): a summarizer failure\n * leaves the session running with unchanged history — the engine\n * treats a thrown summarizer / `onCompact` as \"no compaction this\n * turn\", emits `orchestrator.compaction.failed`, and returns `undefined`\n * (summarizer) or surfaces the unapplied compaction (`onCompact`).\n */\nexport async function runCompaction<TState>(\n ctx: OrchestratorEngineContext<unknown, TState>,\n sessionId: string,\n history: Message[],\n): Promise<CompactionOutcome | undefined> {\n const summarize = ctx.config.summarize;\n\n if (summarize === undefined) {\n return undefined;\n }\n\n await acquireCompactionLock(ctx, sessionId);\n\n try {\n const compaction = await produceCompaction(summarize, history);\n\n ctx.emitter.emit(\"orchestrator.compaction.suggested\", {\n sessionId,\n compaction,\n });\n\n const onCompact = isCallbackForm(summarize) ? undefined : summarize.onCompact;\n\n if (!onCompact) {\n return { compaction, applied: false };\n }\n\n try {\n await onCompact(compaction, { sessionId });\n\n ctx.emitter.emit(\"orchestrator.compaction.applied\", {\n sessionId,\n compaction,\n });\n\n return { compaction, applied: true };\n } catch (error) {\n // onCompact threw — surface the compaction for the dev to apply,\n // leave summarized_through unchanged (§12.2 step 4 failure path).\n ctx.emitter.emit(\"orchestrator.compaction.failed\", {\n sessionId,\n phase: \"onCompact\",\n error,\n });\n\n return { compaction, applied: false };\n }\n } catch (error) {\n // Summarizer failed — skip-and-log; session keeps running unchanged.\n ctx.emitter.emit(\"orchestrator.compaction.failed\", {\n sessionId,\n phase: \"summarize\",\n error,\n });\n\n return undefined;\n } finally {\n await releaseCompactionLock(ctx, sessionId);\n }\n}\n\n/**\n * Run a manual compaction for `command(\"compact\", ...)` (§11 / §12.1).\n * Same code path as the post-turn trigger but driven on demand against\n * the supplied history, returning the raw {@link CompactionResult}. The\n * callback form is honored; the config form without a `summarizer`\n * produces the degenerate memo. Does not apply `onCompact` — the\n * command surface returns the compaction for the caller to handle.\n */\nexport async function runManualCompaction<TState>(\n ctx: OrchestratorEngineContext<unknown, TState>,\n history: Message[],\n): Promise<CompactionResult> {\n const summarize = ctx.config.summarize;\n\n if (summarize === undefined) {\n // No summarize policy configured — produce a degenerate memo over\n // the full supplied history so the command always resolves.\n return {\n summary: { role: \"system\", content: `Summary of ${history.length} message(s).` },\n replacesFromIndex: 0,\n replacesToIndex: Math.max(-1, history.length - 1),\n };\n }\n\n return produceCompaction(summarize, history);\n}\n\n/** Resolve the lock TTL from the summarize config (config form only). */\nfunction resolveLockMaxWait(ctx: OrchestratorEngineContext): number {\n const summarize = ctx.config.summarize;\n\n if (summarize === undefined || isCallbackForm(summarize)) {\n return DEFAULT_LOCK_MAX_WAIT;\n }\n\n return summarize.lock?.maxWait ?? DEFAULT_LOCK_MAX_WAIT;\n}\n\n/**\n * Write the cooperative compaction lock onto a fresh checkpoint row\n * (§12.2 step 1). The lock lives on the latest persisted row; we load\n * it, stamp the lock columns, and re-save (append-only) so the next\n * turn's Phase 3 observes it.\n */\nasync function acquireCompactionLock<TState>(\n ctx: OrchestratorEngineContext<unknown, TState>,\n sessionId: string,\n): Promise<void> {\n const latest = await ctx.checkpointStore.load(ctx.config.name, sessionId);\n\n if (!latest) {\n return;\n }\n\n const now = Date.now();\n const maxWait = resolveLockMaxWait(ctx as OrchestratorEngineContext);\n\n await ctx.checkpointStore.save({\n ...latest,\n lock_acquired_at: new Date(now).toISOString(),\n lock_expires_at: new Date(now + maxWait).toISOString(),\n saved_at: new Date(now).toISOString(),\n });\n}\n\n/**\n * Clear the compaction lock columns (§12.2 step 6) by re-saving the\n * latest row with the lock fields nulled.\n */\nasync function releaseCompactionLock<TState>(\n ctx: OrchestratorEngineContext<unknown, TState>,\n sessionId: string,\n): Promise<void> {\n const latest = await ctx.checkpointStore.load(ctx.config.name, sessionId);\n\n if (!latest) {\n return;\n }\n\n await ctx.checkpointStore.save({\n ...latest,\n lock_acquired_at: null,\n lock_expires_at: null,\n saved_at: new Date().toISOString(),\n });\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"emitter.d.mts","names":[],"sources":["../../../../../../../ai/src/orchestrator/emitter.ts"],"mappings":";;;;;KAYK,YAAA,WAAuB,qBAAA,IAAyB,oBAAA,CAAqB,CAAA,IACxE,aAAA;AAJyD;;;;;;;;;;;;;;;AAI5C;AAoCf;;;;;;;;;AAxC2D,cAwC9C,mBAAA;EAAA,iBACM,eAAA;EAAA,iBACA,gBAAA;cAKE,eAAA,GAAkB,yBAAA;EA0C5B;;;;EAlCF,EAAA,WAAa,qBAAA,
|
|
1
|
+
{"version":3,"file":"emitter.d.mts","names":[],"sources":["../../../../../../../ai/src/orchestrator/emitter.ts"],"mappings":";;;;;KAYK,YAAA,WAAuB,qBAAA,IAAyB,oBAAA,CAAqB,CAAA,IACxE,aAAA;AAJyD;;;;;;;;;;;;;;;AAI5C;AAoCf;;;;;;;;;AAxC2D,cAwC9C,mBAAA;EAAA,iBACM,eAAA;EAAA,iBACA,gBAAA;cAKE,eAAA,GAAkB,yBAAA;EA0C5B;;;;EAlCF,EAAA,WAAa,qBAAA,EAClB,KAAA,EAAO,CAAA,EACP,OAAA,EAAS,wBAAA,CAAyB,CAAA;EAmChB;;;;EAjBb,GAAA,WAAc,qBAAA,EACnB,KAAA,EAAO,CAAA,EACP,OAAA,EAAS,wBAAA,CAAyB,CAAA;EApCnB;;;;;;EA+CV,IAAA,WAAe,qBAAA,EACpB,KAAA,EAAO,CAAA,EACP,OAAA,EAAS,eAAA,CAAgB,oBAAA,CAAqB,CAAA,IAC9C,QAAA,EAAU,aAAA,EACV,eAAA,GAAkB,yBAAA,GACjB,YAAA,CAAa,CAAA;AAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"emitter.mjs","names":[],"sources":["../../../../../../../ai/src/orchestrator/emitter.ts"],"sourcesContent":["import type {\n EventIdentity,\n WithoutIdentity,\n} from \"../contracts/events/event-identity.type\";\nimport type {\n OrchestratorEventHandler,\n OrchestratorEventHandlers,\n OrchestratorEventMap,\n OrchestratorEventName,\n} from \"../contracts/orchestrator/orchestrator-event.type\";\n\n/** Full identity-stamped payload an orchestrator handler receives. */\ntype EventPayload<K extends OrchestratorEventName> = OrchestratorEventMap[K] &\n EventIdentity;\n\n/**\n * Erased handler shape for the instance registry. Per-event handlers\n * are contravariant in their payload, so the registry stores them under\n * a single structural `(payload) => void` and re-narrows at the call\n * site — the `on` / `off` public surface keeps the precise per-event\n * typing.\n */\ntype AnyHandler = (payload: EventPayload<OrchestratorEventName>) => void;\n\n/**\n * Three-tier orchestrator event emitter — definition (factory) →\n * instance → per-call — for the `orchestrator.*` namespace (design\n * §14.3). Mirrors {@link import(\"../supervisor/emitter\").SupervisorEmitter}\n * structurally; the differences are the event map and a central\n * identity-injection chokepoint (`emit` accepts an identity-less\n * payload and stamps {@link EventIdentity} once so all three tiers, and\n * any mirror like the stream, see the same value).\n *\n * Owns: the instance-handler registry and the fan-out order. Does NOT\n * own: child `supervisor.*` / `agent.*` events — those bubble up\n * unmodified under their own identity (§14.2) and never pass through\n * this emitter. All matching handlers fire in tier order; a handler\n * throwing never derails the turn.\n *\n * @example\n * const emitter = new OrchestratorEmitter(config.on);\n * const off = emitter.on(\"orchestrator.turn.completed\", (event) => log(event));\n * emitter.emit(\n * \"orchestrator.turn.starting\",\n * { sessionId, turnIndex },\n * { runId, rootRunId },\n * perCallHandlers,\n * );\n */\nexport class OrchestratorEmitter {\n private readonly factoryHandlers?: OrchestratorEventHandlers;\n private readonly instanceHandlers = new Map<\n OrchestratorEventName,\n Set<AnyHandler>\n >();\n\n public constructor(factoryHandlers?: OrchestratorEventHandlers) {\n this.factoryHandlers = factoryHandlers;\n }\n\n /**\n * Subscribe an instance-level handler (tier 2). Returns an\n * unsubscribe function equivalent to `off(event, handler)`.\n */\n public on<K extends OrchestratorEventName>(\n event: K,\n handler: OrchestratorEventHandler<K>,\n ): () => void {\n let bucket = this.instanceHandlers.get(event);\n\n if (!bucket) {\n bucket = new Set();\n this.instanceHandlers.set(event, bucket);\n }\n\n bucket.add(handler as unknown as AnyHandler);\n\n return () => this.off(event, handler);\n }\n\n /**\n * Remove a previously-subscribed instance handler. No-op when the\n * handler was never registered or already removed.\n */\n public off<K extends OrchestratorEventName>(\n event: K,\n handler: OrchestratorEventHandler<K>,\n ): void {\n this.instanceHandlers.get(event)?.delete(handler as unknown as AnyHandler);\n }\n\n /**\n * Stamp run identity onto the payload, then fan out through all three\n * tiers in order: definition → instance → per-call. Returns the full\n * identity-stamped payload so the caller can mirror the same value\n * into the stream controller (keeping `.on()` and iteration in lockstep).\n */\n public emit<K extends OrchestratorEventName>(\n event: K,\n payload: WithoutIdentity<OrchestratorEventMap[K]>,\n identity: EventIdentity,\n perCallHandlers?: OrchestratorEventHandlers,\n ): EventPayload<K> {\n const fullPayload = { ...payload, ...identity } as EventPayload<K>;\n\n invoke(this.factoryHandlers?.[event], fullPayload);\n\n const bucket = this.instanceHandlers.get(event);\n\n if (bucket) {\n for (const handler of bucket) {\n invoke(handler as unknown as (payload: EventPayload<K>) => void, fullPayload);\n }\n }\n\n invoke(perCallHandlers?.[event], fullPayload);\n\n return fullPayload;\n }\n}\n\n/**\n * Invoke a single handler, swallowing any throw. A listener bug must\n * never derail the orchestrator turn. Typed structurally (a plain\n * `(payload) => void`) so the identity-stamped payload flows in without\n * fighting the contravariant `OrchestratorEventHandler<K>` union.\n */\nfunction invoke<K extends OrchestratorEventName>(\n handler: ((payload: EventPayload<K>) => void) | undefined,\n payload: EventPayload<K>,\n): void {\n if (typeof handler !== \"function\") {\n return;\n }\n\n try {\n handler(payload);\n } catch {\n // Listener bugs must not derail the orchestrator.\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAiDA,IAAa,sBAAb,MAAiC;CAO/B,AAAO,YAAY,iBAA6C;0CAL5B,IAAI,IAGtC;EAGA,KAAK,kBAAkB;CACzB;;;;;CAMA,AAAO,GACL,OACA,SACY;EACZ,IAAI,SAAS,KAAK,iBAAiB,IAAI,KAAK;EAE5C,IAAI,CAAC,QAAQ;GACX,yBAAS,IAAI,IAAI;GACjB,KAAK,iBAAiB,IAAI,OAAO,MAAM;EACzC;EAEA,OAAO,IAAI,OAAgC;EAE3C,aAAa,KAAK,IAAI,OAAO,OAAO;CACtC;;;;;CAMA,AAAO,IACL,OACA,SACM;EACN,KAAK,iBAAiB,IAAI,KAAK,
|
|
1
|
+
{"version":3,"file":"emitter.mjs","names":[],"sources":["../../../../../../../ai/src/orchestrator/emitter.ts"],"sourcesContent":["import type {\n EventIdentity,\n WithoutIdentity,\n} from \"../contracts/events/event-identity.type\";\nimport type {\n OrchestratorEventHandler,\n OrchestratorEventHandlers,\n OrchestratorEventMap,\n OrchestratorEventName,\n} from \"../contracts/orchestrator/orchestrator-event.type\";\n\n/** Full identity-stamped payload an orchestrator handler receives. */\ntype EventPayload<K extends OrchestratorEventName> = OrchestratorEventMap[K] &\n EventIdentity;\n\n/**\n * Erased handler shape for the instance registry. Per-event handlers\n * are contravariant in their payload, so the registry stores them under\n * a single structural `(payload) => void` and re-narrows at the call\n * site — the `on` / `off` public surface keeps the precise per-event\n * typing.\n */\ntype AnyHandler = (payload: EventPayload<OrchestratorEventName>) => void;\n\n/**\n * Three-tier orchestrator event emitter — definition (factory) →\n * instance → per-call — for the `orchestrator.*` namespace (design\n * §14.3). Mirrors {@link import(\"../supervisor/emitter\").SupervisorEmitter}\n * structurally; the differences are the event map and a central\n * identity-injection chokepoint (`emit` accepts an identity-less\n * payload and stamps {@link EventIdentity} once so all three tiers, and\n * any mirror like the stream, see the same value).\n *\n * Owns: the instance-handler registry and the fan-out order. Does NOT\n * own: child `supervisor.*` / `agent.*` events — those bubble up\n * unmodified under their own identity (§14.2) and never pass through\n * this emitter. All matching handlers fire in tier order; a handler\n * throwing never derails the turn.\n *\n * @example\n * const emitter = new OrchestratorEmitter(config.on);\n * const off = emitter.on(\"orchestrator.turn.completed\", (event) => log(event));\n * emitter.emit(\n * \"orchestrator.turn.starting\",\n * { sessionId, turnIndex },\n * { runId, rootRunId },\n * perCallHandlers,\n * );\n */\nexport class OrchestratorEmitter {\n private readonly factoryHandlers?: OrchestratorEventHandlers;\n private readonly instanceHandlers = new Map<\n OrchestratorEventName,\n Set<AnyHandler>\n >();\n\n public constructor(factoryHandlers?: OrchestratorEventHandlers) {\n this.factoryHandlers = factoryHandlers;\n }\n\n /**\n * Subscribe an instance-level handler (tier 2). Returns an\n * unsubscribe function equivalent to `off(event, handler)`.\n */\n public on<K extends OrchestratorEventName>(\n event: K,\n handler: OrchestratorEventHandler<K>,\n ): () => void {\n let bucket = this.instanceHandlers.get(event);\n\n if (!bucket) {\n bucket = new Set();\n this.instanceHandlers.set(event, bucket);\n }\n\n bucket.add(handler as unknown as AnyHandler);\n\n return () => this.off(event, handler);\n }\n\n /**\n * Remove a previously-subscribed instance handler. No-op when the\n * handler was never registered or already removed.\n */\n public off<K extends OrchestratorEventName>(\n event: K,\n handler: OrchestratorEventHandler<K>,\n ): void {\n this.instanceHandlers.get(event)?.delete(handler as unknown as AnyHandler);\n }\n\n /**\n * Stamp run identity onto the payload, then fan out through all three\n * tiers in order: definition → instance → per-call. Returns the full\n * identity-stamped payload so the caller can mirror the same value\n * into the stream controller (keeping `.on()` and iteration in lockstep).\n */\n public emit<K extends OrchestratorEventName>(\n event: K,\n payload: WithoutIdentity<OrchestratorEventMap[K]>,\n identity: EventIdentity,\n perCallHandlers?: OrchestratorEventHandlers,\n ): EventPayload<K> {\n const fullPayload = { ...payload, ...identity } as EventPayload<K>;\n\n invoke(this.factoryHandlers?.[event], fullPayload);\n\n const bucket = this.instanceHandlers.get(event);\n\n if (bucket) {\n for (const handler of bucket) {\n invoke(handler as unknown as (payload: EventPayload<K>) => void, fullPayload);\n }\n }\n\n invoke(perCallHandlers?.[event], fullPayload);\n\n return fullPayload;\n }\n}\n\n/**\n * Invoke a single handler, swallowing any throw. A listener bug must\n * never derail the orchestrator turn. Typed structurally (a plain\n * `(payload) => void`) so the identity-stamped payload flows in without\n * fighting the contravariant `OrchestratorEventHandler<K>` union.\n */\nfunction invoke<K extends OrchestratorEventName>(\n handler: ((payload: EventPayload<K>) => void) | undefined,\n payload: EventPayload<K>,\n): void {\n if (typeof handler !== \"function\") {\n return;\n }\n\n try {\n handler(payload);\n } catch {\n // Listener bugs must not derail the orchestrator.\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AAiDA,IAAa,sBAAb,MAAiC;CAO/B,AAAO,YAAY,iBAA6C;0CAL5B,IAAI,IAGtC;EAGA,KAAK,kBAAkB;CACzB;;;;;CAMA,AAAO,GACL,OACA,SACY;EACZ,IAAI,SAAS,KAAK,iBAAiB,IAAI,KAAK;EAE5C,IAAI,CAAC,QAAQ;GACX,yBAAS,IAAI,IAAI;GACjB,KAAK,iBAAiB,IAAI,OAAO,MAAM;EACzC;EAEA,OAAO,IAAI,OAAgC;EAE3C,aAAa,KAAK,IAAI,OAAO,OAAO;CACtC;;;;;CAMA,AAAO,IACL,OACA,SACM;EACN,KAAK,iBAAiB,IAAI,KAAK,CAAC,EAAE,OAAO,OAAgC;CAC3E;;;;;;;CAQA,AAAO,KACL,OACA,SACA,UACA,iBACiB;EACjB,MAAM,cAAc;GAAE,GAAG;GAAS,GAAG;EAAS;EAE9C,OAAO,KAAK,kBAAkB,QAAQ,WAAW;EAEjD,MAAM,SAAS,KAAK,iBAAiB,IAAI,KAAK;EAE9C,IAAI,QACF,KAAK,MAAM,WAAW,QACpB,OAAO,SAA0D,WAAW;EAIhF,OAAO,kBAAkB,QAAQ,WAAW;EAE5C,OAAO;CACT;AACF;;;;;;;AAQA,SAAS,OACP,SACA,SACM;CACN,IAAI,OAAO,YAAY,YACrB;CAGF,IAAI;EACF,QAAQ,OAAO;CACjB,QAAQ,CAER;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"execution.d.mts","names":[],"sources":["../../../../../../../ai/src/orchestrator/execution.ts"],"mappings":";;;;;;;;;;;;;;AAqEA;;;;;;;;;;;KAAY,2BAAA;EACV,MAAA,EAAQ,kBAAA,CAAmB,OAAA,EAAS,MAAA,GAOY;EALhD,OAAA,GAAU,GAAA,SAAY,mBAAA;EACtB,SAAA;EACA,OAAA,EAAS,mBAAA;EACT,KAAA,GAAQ,eAAA;EACR,OAAA,GAAU,0BAAA,CAA2B,MAAA;EACrC,gBAAA,GAAmB,4BAAA,CAA6B,kBAAA,CAAmB,OAAA;EACnE,eAAA;EACA,aAAA,GAAgB,yBAAA;AAAA;;;;;;;;;;;;;;;;;;;;cAsBL,qBAAA;EAAA,iBACM,MAAA;EAAA,iBACA,GAAA;EAAA,iBACA,gBAAA;cAIE,MAAA,EAAQ,2BAAA,CAA4B,OAAA,EAAS,MAAA;EAAT;;;;;;EAyB1C,GAAA,
|
|
1
|
+
{"version":3,"file":"execution.d.mts","names":[],"sources":["../../../../../../../ai/src/orchestrator/execution.ts"],"mappings":";;;;;;;;;;;;;;AAqEA;;;;;;;;;;;KAAY,2BAAA;EACV,MAAA,EAAQ,kBAAA,CAAmB,OAAA,EAAS,MAAA,GAOY;EALhD,OAAA,GAAU,GAAA,SAAY,mBAAA;EACtB,SAAA;EACA,OAAA,EAAS,mBAAA;EACT,KAAA,GAAQ,eAAA;EACR,OAAA,GAAU,0BAAA,CAA2B,MAAA;EACrC,gBAAA,GAAmB,4BAAA,CAA6B,kBAAA,CAAmB,OAAA;EACnE,eAAA;EACA,aAAA,GAAgB,yBAAA;AAAA;;;;;;;;;;;;;;;;;;;;cAsBL,qBAAA;EAAA,iBACM,MAAA;EAAA,iBACA,GAAA;EAAA,iBACA,gBAAA;cAIE,MAAA,EAAQ,2BAAA,CAA4B,OAAA,EAAS,MAAA;EAAT;;;;;;EAyB1C,GAAA,IAAO,OAAA,CAAQ,kBAAA,CAAmB,OAAA;EAiChB;;;;EAAlB,MAAA,IAAU,OAAA,CAAQ,kBAAA,CAAmB,OAAA;EAiBxC;;;;;EAFG,OAAA,CACX,IAAA,EAAM,oBAAA,sBACL,OAAA,CAAQ,oBAAA;AAAA;;;;;;;;;;;;iBAuPS,OAAA,kBACpB,GAAA,EAAK,yBAAA,CAA0B,OAAA,EAAS,MAAA,GACxC,KAAA,EAAO,eAAA,EACP,OAAA,EAAS,0BAAA,CAA2B,MAAA,IACnC,OAAA,CAAQ,kBAAA,CAAmB,OAAA;;;;;;;;AA3PG;AAuPjC;;iBAiNsB,SAAA,kBACpB,GAAA,EAAK,yBAAA,CAA0B,OAAA,EAAS,MAAA,GACxC,SAAA,UACA,OAAA,GAAU,yBAAA,GACT,OAAA,CAAQ,kBAAA,CAAmB,OAAA;;;;;;;;;;;iBAiDR,UAAA,kBACpB,GAAA,EAAK,yBAAA,CAA0B,OAAA,EAAS,MAAA,GACxC,KAAA,EAAO,eAAA,EACP,OAAA,EAAS,0BAAA,CAA2B,MAAA,IACnC,OAAA,CAAQ,kBAAA,CAAmB,OAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"execution.mjs","names":[],"sources":["../../../../../../../ai/src/orchestrator/execution.ts"],"sourcesContent":["import type { Message } from \"../contracts/conversation-message.type\";\nimport type { OrchestratorCommands } from \"../contracts/orchestrator/orchestrator-commands.type\";\nimport type { OrchestratorConfig } from \"../contracts/orchestrator/orchestrator-config.type\";\nimport type {\n OrchestratorEvent,\n OrchestratorEventHandlers,\n OrchestratorEventMap,\n OrchestratorEventName,\n} from \"../contracts/orchestrator/orchestrator-event.type\";\nimport type {\n OrchestratorExecuteOptions,\n OrchestratorResumeOptions,\n} from \"../contracts/orchestrator/orchestrator-execute-options.type\";\nimport type {\n CompactionResult,\n OrchestratorReport,\n OrchestratorReportStatus,\n OrchestratorResult,\n TurnSnapshot,\n} from \"../contracts/result/orchestrator-result.type\";\nimport { REPORT_SCHEMA_VERSION } from \"../contracts/result/base-report.type\";\nimport type { BaseReport } from \"../contracts/result/base-report.type\";\nimport type { Usage } from \"../contracts/result/usage.type\";\nimport type { SupervisorInput } from \"../contracts/supervisor/supervisor-input.type\";\nimport type { EventIdentity } from \"../contracts/events/event-identity.type\";\nimport {\n resolveDefaultCheckpointStore,\n resolveDefaultSnapshotStore,\n} from \"../config\";\nimport type { AIError } from \"../errors/ai-error\";\nimport { OrchestratorConfigError, OrchestratorDriftError } from \"../errors\";\nimport { notifyObservers } from \"../observe/resolve-observers\";\nimport type { ResolvedIntentEntry } from \"../supervisor/entries\";\nimport { generateRunId } from \"../utils/generate-run-id\";\nimport { persistCheckpoint, summarizeRoute } from \"./checkpoint\";\nimport { runCompaction, runManualCompaction, shouldCompact } from \"./compaction\";\nimport { deriveRunId, dispatchTurn } from \"./dispatch\";\nimport type { OrchestratorEmitter } from \"./emitter\";\nimport type { OrchestratorEmitterLike } from \"./emitter-port.type\";\nimport type { OrchestratorEngineContext } from \"./engine-context.type\";\nimport { acquireLock } from \"./lock\";\nimport { loadSession } from \"./load\";\nimport {\n injectMemories,\n outcomeTextFromTurn,\n recallForTurn,\n rememberTurnOutcome,\n resolveOrchestratorMemory,\n} from \"./memory\";\nimport type { OrchestratorStreamController } from \"./orchestrator-stream\";\nimport { resolveResume } from \"./resume\";\nimport { windowHistory } from \"./window\";\n\n/** Empty rolled-up usage for turns that never dispatched (drift/seed). */\nconst ZERO_USAGE: Usage = { input: 0, output: 0, total: 0 };\n\n/**\n * Constructor params the C1 factory passes when building an\n * {@link OrchestratorExecution} per call. The factory owns author-time\n * validation, intent-entry resolution, and signature computation; it\n * hands the engine the validated `config`, the resolved `entries`, the\n * computed `signature`, and the shared three-tier `emitter`. The\n * per-call inputs vary by entry point:\n *\n * - `execute` / `stream` — `input` + `options` (and `streamController`\n * for `stream`).\n * - `resume` — `resumeSessionId` + `resumeOptions`.\n * - `command(\"compact\")` — neither; `compact(args)` carries its own.\n */\nexport type OrchestratorExecutionParams<TOutput, TState> = {\n config: OrchestratorConfig<TOutput, TState>;\n /** Resolved intent entries (validated by C1; the engine delegates dispatch to the supervisor). */\n entries?: Map<string, ResolvedIntentEntry>;\n signature: string;\n emitter: OrchestratorEmitter;\n input?: SupervisorInput;\n options?: OrchestratorExecuteOptions<TState>;\n streamController?: OrchestratorStreamController<OrchestratorResult<TOutput>>;\n resumeSessionId?: string;\n resumeOptions?: OrchestratorResumeOptions;\n};\n\n/**\n * Per-call lifecycle engine — the single object the C1 factory\n * constructs and drives. Owns the 7-phase lifecycle (orchestrator.md §3:\n * load → drift → lock → window → dispatch → persist → compaction),\n * resolving the durable stores (own config field → `ai.config` default)\n * and adapting C1's three-tier {@link OrchestratorEmitter} to the\n * {@link OrchestratorEmitterLike} port the phase modules call.\n *\n * The factory creates a fresh instance per `execute` / `stream` /\n * `resume` / `command` call (single-call lifecycle invariant — §18.8);\n * the heavy lifting lives in the standalone phase functions\n * ({@link runTurn} / {@link runResume}) which this class delegates to.\n *\n * @example\n * const execution = new OrchestratorExecution({\n * config, entries, signature, emitter, input, options,\n * });\n * const result = await execution.run();\n */\nexport class OrchestratorExecution<TOutput, TState> {\n private readonly params: OrchestratorExecutionParams<TOutput, TState>;\n private readonly ctx: OrchestratorEngineContext<TOutput, TState>;\n private readonly streamController?: OrchestratorStreamController<\n OrchestratorResult<TOutput>\n >;\n\n public constructor(params: OrchestratorExecutionParams<TOutput, TState>) {\n this.params = params;\n this.streamController = params.streamController;\n this.ctx = {\n config: params.config,\n signature: params.signature,\n checkpointStore: resolveCheckpointStore(params.config),\n snapshotStore: resolveSnapshotStore(params.config),\n emitter: adaptEmitter(\n params.emitter,\n generateRunId(\"orchestrator\"),\n this.streamController as\n | OrchestratorStreamController<unknown>\n | undefined,\n ),\n memory: resolveOrchestratorMemory(params.config.memory),\n };\n }\n\n /**\n * `execute()` / `stream()` entry — run one turn through the 7-phase\n * lifecycle. When a `streamController` was supplied, the adapter mirrors\n * every emitted event into the stream and the controller is settled\n * (`end` / `fail`) once the result resolves.\n */\n public async run(): Promise<OrchestratorResult<TOutput>> {\n if (this.params.input === undefined || !this.params.options) {\n throw new OrchestratorConfigError(\n `ai.orchestrator(\"${this.params.config.name}\"): internal — run() invoked without input/options`,\n );\n }\n\n try {\n const result = await runTurn(\n this.ctx,\n this.params.input,\n this.params.options,\n );\n\n // Route the orchestrator's report to observers (per-flow `observe` +\n // the global observe-all gate) — parity with agent/workflow/supervisor,\n // so a durable session root no longer needs a manual observe.collect().\n await notifyObservers(this.ctx.config.observe, result.report);\n\n this.streamController?.end(result);\n\n return result;\n } catch (error) {\n this.streamController?.fail(error as Error);\n\n throw error;\n }\n }\n\n /**\n * `resume()` entry — drain an interrupted `iterate: true` turn (§9).\n * Returns `null` when nothing is in flight.\n */\n public async resume(): Promise<OrchestratorResult<TOutput> | null> {\n if (!this.params.resumeSessionId) {\n throw new OrchestratorConfigError(\n `ai.orchestrator(\"${this.params.config.name}\"): internal — resume() invoked without a sessionId`,\n );\n }\n\n return runResume(this.ctx, this.params.resumeSessionId, this.params.resumeOptions);\n }\n\n /**\n * `command(\"compact\")` entry — run a manual compaction on demand (§11 /\n * §12.1). Reuses the post-turn compaction code path against the\n * caller-supplied history and returns the raw {@link CompactionResult}.\n */\n public async compact(\n args: OrchestratorCommands[\"compact\"][\"args\"],\n ): Promise<OrchestratorCommands[\"compact\"][\"result\"]> {\n return runManualCompaction(\n this.ctx as OrchestratorEngineContext<unknown, TState>,\n args.history,\n );\n }\n}\n\n/**\n * Resolve the durable checkpoint store: the config's own field, falling\n * back to `ai.config({ defaultCheckpointStore })`. Throws\n * {@link OrchestratorConfigError} when neither resolves — persistence is\n * always on (§8.1), so a turn can never run without a checkpoint store.\n */\nfunction resolveCheckpointStore<TOutput, TState>(\n config: OrchestratorConfig<TOutput, TState>,\n) {\n const store = config.checkpointStore ?? resolveDefaultCheckpointStore();\n\n if (!store) {\n throw new OrchestratorConfigError(\n `ai.orchestrator(\"${config.name}\"): a \\`checkpointStore\\` is required ` +\n `(set one on the config or via \\`ai.config({ defaultCheckpointStore })\\`)`,\n );\n }\n\n return store;\n}\n\n/**\n * Resolve the internal-supervisor snapshot store for `iterate: true`\n * turns: the config's own field, falling back to\n * `ai.config({ defaultSnapshotStore })`. Returns `undefined` for\n * `iterate: false` orchestrators (no mid-turn resume — nothing to\n * snapshot). The factory already guarantees presence when\n * `iterate: true`, so the engine never asserts here.\n */\nfunction resolveSnapshotStore<TOutput, TState>(\n config: OrchestratorConfig<TOutput, TState>,\n) {\n if (config.iterate !== true) {\n return undefined;\n }\n\n return config.snapshotStore ?? resolveDefaultSnapshotStore();\n}\n\n/**\n * Adapt C1's three-tier {@link OrchestratorEmitter} (whose `emit` takes\n * `event, payload, identity, perCallHandlers?`) to the\n * {@link OrchestratorEmitterLike} port the phase modules call (a 2-arg\n * `emit(event, payload)` plus `bindPerCall`).\n *\n * The adapter injects the run identity centrally and, when a stream\n * controller is present, mirrors every fully-stamped event into the\n * stream pipe (§14.1 — the orchestrator's own events surface on the\n * stream alongside the bubbled child events). `bindPerCall` registers\n * the per-call `options.on` bag for the turn's duration and returns a\n * disposer that clears it.\n */\nfunction adaptEmitter(\n emitter: OrchestratorEmitter,\n runId: string,\n streamController: OrchestratorStreamController<unknown> | undefined,\n): OrchestratorEmitterLike {\n // `rootRunId === runId` for a standalone run; nested propagation lands\n // in a follow-up (see `EventIdentity`).\n const fullIdentity: EventIdentity = { runId, rootRunId: runId };\n\n let perCall: OrchestratorEventHandlers | undefined;\n\n return {\n emit<K extends OrchestratorEventName>(\n event: K,\n payload: OrchestratorEventMap[K],\n ): void {\n const fullPayload = emitter.emit(event, payload, fullIdentity, perCall);\n\n // The discriminated-union correlation between `type` and the\n // matching payload variant can't be expressed structurally — the\n // cast mirrors the supervisor stream's established pattern.\n streamController?.push({ type: event, ...fullPayload } as OrchestratorEvent);\n },\n bindPerCall(handlers: OrchestratorEventHandlers | undefined): () => void {\n perCall = handlers;\n\n return () => {\n perCall = undefined;\n };\n },\n };\n}\n\n/**\n * Phase 2 — drift check (orchestrator.md §3 / §4 Phase 2). Compares the\n * loaded checkpoint's `signature` against the current definition's.\n * Mismatch throws `OrchestratorDriftError` synchronously unless\n * `force` is set. Emits `orchestrator.drift.checked` either way. A new\n * session (no loaded signature) never drifts.\n */\nfunction assertNoDrift(\n ctx: OrchestratorEngineContext,\n sessionId: string,\n loadedSignature: string | undefined,\n force: boolean | undefined,\n): void {\n const drifted =\n loadedSignature !== undefined && loadedSignature !== ctx.signature;\n\n ctx.emitter.emit(\"orchestrator.drift.checked\", {\n sessionId,\n signature: ctx.signature,\n drifted,\n });\n\n if (drifted && !force) {\n throw new OrchestratorDriftError(\n `orchestrator \"${ctx.config.name}\": signature drift on session \"${sessionId}\" — ` +\n `the definition changed since this session was last persisted. ` +\n `Pass { force: true } only after reviewing the change, or discard / migrate the session.`,\n {\n savedSignature: loadedSignature as string,\n currentSignature: ctx.signature,\n sessionId,\n },\n );\n }\n}\n\n/**\n * Shallow-merge the per-call `state` patch (§5 — partial state\n * override) over the loaded session-state seed. The merged value\n * becomes the supervisor's seed for this turn.\n */\nfunction applyStatePatch<TState>(\n seed: TState,\n patch: Partial<TState> | undefined,\n): TState {\n if (!patch) {\n return seed;\n }\n\n return { ...seed, ...patch } as TState;\n}\n\n/**\n * Assemble the orchestrator-scope {@link OrchestratorReport} from the\n * dispatched turn's child report and the turn snapshot. Wraps the\n * child supervisor/agent report tree as `children[0]` (§15.6 —\n * `children[]` carries only the CURRENT turn's dispatched primitive\n * reports) while the per-turn forensic record lives on `turns[]`.\n */\nfunction buildReport(\n ctx: OrchestratorEngineContext,\n sessionId: string,\n turnIndex: number,\n status: OrchestratorReportStatus,\n turnSnapshot: TurnSnapshot | undefined,\n childReport: BaseReport | undefined,\n error?: AIError,\n): OrchestratorReport {\n const now = new Date().toISOString();\n const usage = turnSnapshot?.usage ?? childReport?.usage ?? ZERO_USAGE;\n\n return {\n runId: deriveRunId(sessionId, ctx.config.version, turnIndex),\n rootRunId: deriveRunId(sessionId, ctx.config.version, turnIndex),\n name: ctx.config.name,\n version: ctx.config.version,\n sessionId,\n type: \"orchestrator\",\n status,\n // Stamp the terminal error so the observe path surfaces it on the\n // orchestrator span (an observer never sees the result envelope).\n // Absent on a clean turn.\n ...(error ? { error } : {}),\n startedAt: turnSnapshot?.startedAt ?? now,\n endedAt: turnSnapshot?.endedAt ?? now,\n duration: turnSnapshot?.duration ?? 0,\n usage,\n children: childReport ? [childReport] : [],\n reportSchemaVersion: REPORT_SCHEMA_VERSION,\n turnIndex,\n signature: ctx.signature,\n turns: turnSnapshot ? [turnSnapshot] : [],\n };\n}\n\n/**\n * Map the dispatched supervisor result's report status onto the\n * orchestrator's status surface (§15.6). A clean completion that is\n * still mid-conversation reports `\"awaiting-input\"` (the session\n * continues) rather than `\"completed\"`; failures and cancellations\n * pass through.\n */\nfunction deriveStatus(childStatus: BaseReport[\"status\"]): OrchestratorReportStatus {\n if (childStatus === \"completed\") {\n return \"awaiting-input\";\n }\n\n return childStatus;\n}\n\n/**\n * Emit the terminal turn event matching the report status (§14.1).\n */\nfunction emitTerminal(\n ctx: OrchestratorEngineContext,\n sessionId: string,\n turnIndex: number,\n status: OrchestratorReportStatus,\n): void {\n if (status === \"cancelled\") {\n ctx.emitter.emit(\"orchestrator.turn.cancelled\", { sessionId, turnIndex });\n\n return;\n }\n\n if (status === \"failed\" || status === \"max-iterations\") {\n ctx.emitter.emit(\"orchestrator.turn.failed\", { sessionId, turnIndex });\n\n return;\n }\n\n if (status === \"awaiting-input\") {\n ctx.emitter.emit(\"orchestrator.turn.awaiting-input\", {\n sessionId,\n turnIndex,\n });\n\n return;\n }\n\n ctx.emitter.emit(\"orchestrator.turn.completed\", { sessionId, turnIndex });\n}\n\n/**\n * Run one turn end-to-end through the 7-phase lifecycle (orchestrator\n * .md §3). The single entry the C1 factory's `execute()` delegates to.\n *\n * Phase order is the diagram's contract: load → drift → lock → window\n * → dispatch → persist → compaction. Drift / config misuse throw;\n * every other failure surfaces on `result.error` (the contract: the\n * orchestrator never throws on runtime failure). Cancellation and\n * failure do NOT persist a fresh checkpoint (§17 — state reverts to the\n * pre-turn checkpoint).\n */\nexport async function runTurn<TOutput, TState>(\n ctx: OrchestratorEngineContext<TOutput, TState>,\n input: SupervisorInput,\n options: OrchestratorExecuteOptions<TState>,\n): Promise<OrchestratorResult<TOutput>> {\n const sessionId = options.sessionId;\n const disposePerCall = ctx.emitter.bindPerCall(options.on);\n\n try {\n // Phase 1 — load session.\n const loaded = await loadSession(ctx, sessionId);\n\n ctx.emitter.emit(\"orchestrator.turn.starting\", {\n sessionId,\n turnIndex: loaded.turnIndex,\n });\n\n ctx.emitter.emit(\"orchestrator.session.loaded\", {\n sessionId,\n turnIndex: loaded.turnIndex,\n found: loaded.found,\n });\n\n // Phase 2 — drift check.\n assertNoDrift(\n ctx as OrchestratorEngineContext,\n sessionId,\n loaded.record?.signature,\n options.force,\n );\n\n // Phase 3 — lock check (cooperative, fail-open).\n await acquireLock(ctx, sessionId, loaded.record);\n\n // Phase 4 — window history.\n const windowed = windowHistory(\n ctx as OrchestratorEngineContext,\n sessionId,\n options.history,\n );\n\n // Phase 5 — dispatch. When memory is configured, recall the\n // turn-relevant memories and inject them into the request-scoped\n // context bag so every route / router / evaluate / dispatch callback\n // surfaces them at `ctx.context[injectKey]` before routing runs.\n const seedState = applyStatePatch(loaded.state, options.state);\n\n let turnContext = options.context;\n\n // Recall is scoped to THIS session (`memory.scope`, default\n // `\"session\"`): the store is shared by every session of this\n // orchestrator instance, so the scope — not the store — is what keeps\n // another session's remembered turns out of this turn's context.\n if (ctx.memory) {\n const recalled = await recallForTurn(ctx.memory, input, sessionId);\n turnContext = injectMemories(turnContext, ctx.memory, recalled);\n }\n\n const { result, state, turnSnapshot } = await dispatchTurn<TOutput, TState>({\n ctx,\n sessionId,\n input,\n seedState,\n turnIndex: loaded.turnIndex,\n history: windowed.agents,\n context: turnContext,\n signal: options.signal,\n });\n\n ctx.emitter.emit(\"orchestrator.turn.routed\", {\n sessionId,\n turnIndex: loaded.turnIndex,\n source: turnSnapshot.decision.source,\n raw: turnSnapshot.decision.raw,\n });\n\n const status = result.error\n ? deriveStatus(result.report.status)\n : \"awaiting-input\";\n\n // Cancelled / failed turns revert: no fresh checkpoint, no compaction.\n if (result.error) {\n const report = buildReport(\n ctx as OrchestratorEngineContext,\n sessionId,\n loaded.turnIndex,\n status,\n turnSnapshot,\n result.report,\n result.error,\n );\n\n emitTerminal(ctx as OrchestratorEngineContext, sessionId, loaded.turnIndex, status);\n\n return {\n data: result.data,\n error: result.error,\n usage: result.usage,\n report,\n sessionId,\n turnIndex: loaded.turnIndex,\n };\n }\n\n // Phase 6 — persist checkpoint.\n await persistCheckpoint({\n ctx,\n sessionId,\n turnIndex: loaded.turnIndex,\n state,\n lastRoute: summarizeRoute(turnSnapshot.decision.raw as never),\n summarizedThrough: loaded.record?.summarized_through ?? null,\n });\n\n // Memory write-back (memory core M2). The turn settled cleanly (the\n // `result.error` branch above already returned for cancelled /\n // failed turns, which revert and never remember — §17), so remember\n // the input + its outcome for later recall.\n if (ctx.memory) {\n await rememberTurnOutcome(\n ctx.memory,\n input,\n outcomeTextFromTurn(result.data, turnSnapshot),\n sessionId,\n );\n }\n\n // Phase 7 — post-turn compaction (only when triggered).\n let compaction: CompactionResult | undefined;\n\n if (shouldCompact(ctx as OrchestratorEngineContext, loaded.turnIndex)) {\n const outcome = await runCompaction(\n ctx as OrchestratorEngineContext<unknown, TState>,\n sessionId,\n options.history,\n );\n\n if (outcome) {\n compaction = outcome.compaction;\n\n if (outcome.applied) {\n await advanceSummarizedThrough(\n ctx as OrchestratorEngineContext<unknown, TState>,\n sessionId,\n outcome.compaction.replacesToIndex,\n );\n }\n }\n }\n\n const report = buildReport(\n ctx as OrchestratorEngineContext,\n sessionId,\n loaded.turnIndex,\n \"awaiting-input\",\n turnSnapshot,\n result.report,\n );\n\n emitTerminal(ctx as OrchestratorEngineContext, sessionId, loaded.turnIndex, \"awaiting-input\");\n\n return {\n data: result.data,\n error: undefined,\n usage: result.usage,\n report,\n sessionId,\n turnIndex: loaded.turnIndex,\n compaction,\n };\n } finally {\n disposePerCall();\n }\n}\n\n/**\n * After a framework-applied compaction (`onCompact` succeeded), advance\n * the persisted `summarized_through` to the compaction's\n * `replacesToIndex` (§12.2 step 4). Re-saves the latest row with the\n * updated marker (append-only stores keep the prior row).\n */\nasync function advanceSummarizedThrough<TState>(\n ctx: OrchestratorEngineContext<unknown, TState>,\n sessionId: string,\n replacesToIndex: number,\n): Promise<void> {\n const latest = await ctx.checkpointStore.load(ctx.config.name, sessionId);\n\n if (!latest) {\n return;\n }\n\n await ctx.checkpointStore.save({\n ...latest,\n summarized_through: replacesToIndex,\n saved_at: new Date().toISOString(),\n });\n}\n\n/**\n * §9 resume protocol entry the C1 factory's `resume()` delegates to.\n * Returns `null` when no in-flight `iterate: true` turn is detected;\n * otherwise drains the interrupted supervisor run, persists a fresh\n * checkpoint for the resumed turn, and returns the completed result.\n *\n * Runs the same Phase 2 drift check as `runTurn` (§9.4). The heavy\n * lifting lives in `resume.ts`; this wrapper threads the engine\n * context.\n */\nexport async function runResume<TOutput, TState>(\n ctx: OrchestratorEngineContext<TOutput, TState>,\n sessionId: string,\n options?: OrchestratorResumeOptions,\n): Promise<OrchestratorResult<TOutput> | null> {\n const disposePerCall = ctx.emitter.bindPerCall(options?.on);\n\n try {\n return await resolveResume(ctx, sessionId, options, {\n assertNoDrift: (loadedSignature) =>\n assertNoDrift(\n ctx as OrchestratorEngineContext,\n sessionId,\n loadedSignature,\n options?.force,\n ),\n buildReport: (turnIndex, status, turnSnapshot, childReport) =>\n buildReport(\n ctx as OrchestratorEngineContext,\n sessionId,\n turnIndex,\n status,\n turnSnapshot,\n childReport,\n ),\n deriveStatus,\n emitTerminal: (turnIndex, status) =>\n emitTerminal(ctx as OrchestratorEngineContext, sessionId, turnIndex, status),\n persist: (turnIndex, state, lastRoute, summarizedThrough) =>\n persistCheckpoint({\n ctx,\n sessionId,\n turnIndex,\n state,\n lastRoute,\n summarizedThrough,\n }),\n });\n } finally {\n disposePerCall();\n }\n}\n\n/**\n * The `stream()` entry. The orchestrator's streaming surface bubbles\n * child agent/supervisor events under their own namespace (§14.2); the\n * C1 stream controller owns the `StreamContract` wiring. This engine\n * entry runs the same lifecycle as `runTurn` — the C1 factory passes a\n * per-call `on` bag wired to the stream controller, so the engine needs\n * no streaming-specific branch. Exposed as a distinct name for the\n * factory to call, returning the same `OrchestratorResult` the stream's\n * `.result` resolves to.\n */\nexport async function streamTurn<TOutput, TState>(\n ctx: OrchestratorEngineContext<TOutput, TState>,\n input: SupervisorInput,\n options: OrchestratorExecuteOptions<TState>,\n): Promise<OrchestratorResult<TOutput>> {\n return runTurn(ctx, input, options);\n}\n\nexport type { OrchestratorEngineContext } from \"./engine-context.type\";\nexport type { Message };\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"execution.mjs","names":[],"sources":["../../../../../../../ai/src/orchestrator/execution.ts"],"sourcesContent":["import type { Message } from \"../contracts/conversation-message.type\";\nimport type { OrchestratorCommands } from \"../contracts/orchestrator/orchestrator-commands.type\";\nimport type { OrchestratorConfig } from \"../contracts/orchestrator/orchestrator-config.type\";\nimport type {\n OrchestratorEvent,\n OrchestratorEventHandlers,\n OrchestratorEventMap,\n OrchestratorEventName,\n} from \"../contracts/orchestrator/orchestrator-event.type\";\nimport type {\n OrchestratorExecuteOptions,\n OrchestratorResumeOptions,\n} from \"../contracts/orchestrator/orchestrator-execute-options.type\";\nimport type {\n CompactionResult,\n OrchestratorReport,\n OrchestratorReportStatus,\n OrchestratorResult,\n TurnSnapshot,\n} from \"../contracts/result/orchestrator-result.type\";\nimport { REPORT_SCHEMA_VERSION } from \"../contracts/result/base-report.type\";\nimport type { BaseReport } from \"../contracts/result/base-report.type\";\nimport type { Usage } from \"../contracts/result/usage.type\";\nimport type { SupervisorInput } from \"../contracts/supervisor/supervisor-input.type\";\nimport type { EventIdentity } from \"../contracts/events/event-identity.type\";\nimport {\n resolveDefaultCheckpointStore,\n resolveDefaultSnapshotStore,\n} from \"../config\";\nimport type { AIError } from \"../errors/ai-error\";\nimport { OrchestratorConfigError, OrchestratorDriftError } from \"../errors\";\nimport { notifyObservers } from \"../observe/resolve-observers\";\nimport type { ResolvedIntentEntry } from \"../supervisor/entries\";\nimport { generateRunId } from \"../utils/generate-run-id\";\nimport { persistCheckpoint, summarizeRoute } from \"./checkpoint\";\nimport { runCompaction, runManualCompaction, shouldCompact } from \"./compaction\";\nimport { deriveRunId, dispatchTurn } from \"./dispatch\";\nimport type { OrchestratorEmitter } from \"./emitter\";\nimport type { OrchestratorEmitterLike } from \"./emitter-port.type\";\nimport type { OrchestratorEngineContext } from \"./engine-context.type\";\nimport { acquireLock } from \"./lock\";\nimport { loadSession } from \"./load\";\nimport {\n injectMemories,\n outcomeTextFromTurn,\n recallForTurn,\n rememberTurnOutcome,\n resolveOrchestratorMemory,\n} from \"./memory\";\nimport type { OrchestratorStreamController } from \"./orchestrator-stream\";\nimport { resolveResume } from \"./resume\";\nimport { windowHistory } from \"./window\";\n\n/** Empty rolled-up usage for turns that never dispatched (drift/seed). */\nconst ZERO_USAGE: Usage = { input: 0, output: 0, total: 0 };\n\n/**\n * Constructor params the C1 factory passes when building an\n * {@link OrchestratorExecution} per call. The factory owns author-time\n * validation, intent-entry resolution, and signature computation; it\n * hands the engine the validated `config`, the resolved `entries`, the\n * computed `signature`, and the shared three-tier `emitter`. The\n * per-call inputs vary by entry point:\n *\n * - `execute` / `stream` — `input` + `options` (and `streamController`\n * for `stream`).\n * - `resume` — `resumeSessionId` + `resumeOptions`.\n * - `command(\"compact\")` — neither; `compact(args)` carries its own.\n */\nexport type OrchestratorExecutionParams<TOutput, TState> = {\n config: OrchestratorConfig<TOutput, TState>;\n /** Resolved intent entries (validated by C1; the engine delegates dispatch to the supervisor). */\n entries?: Map<string, ResolvedIntentEntry>;\n signature: string;\n emitter: OrchestratorEmitter;\n input?: SupervisorInput;\n options?: OrchestratorExecuteOptions<TState>;\n streamController?: OrchestratorStreamController<OrchestratorResult<TOutput>>;\n resumeSessionId?: string;\n resumeOptions?: OrchestratorResumeOptions;\n};\n\n/**\n * Per-call lifecycle engine — the single object the C1 factory\n * constructs and drives. Owns the 7-phase lifecycle (orchestrator.md §3:\n * load → drift → lock → window → dispatch → persist → compaction),\n * resolving the durable stores (own config field → `ai.config` default)\n * and adapting C1's three-tier {@link OrchestratorEmitter} to the\n * {@link OrchestratorEmitterLike} port the phase modules call.\n *\n * The factory creates a fresh instance per `execute` / `stream` /\n * `resume` / `command` call (single-call lifecycle invariant — §18.8);\n * the heavy lifting lives in the standalone phase functions\n * ({@link runTurn} / {@link runResume}) which this class delegates to.\n *\n * @example\n * const execution = new OrchestratorExecution({\n * config, entries, signature, emitter, input, options,\n * });\n * const result = await execution.run();\n */\nexport class OrchestratorExecution<TOutput, TState> {\n private readonly params: OrchestratorExecutionParams<TOutput, TState>;\n private readonly ctx: OrchestratorEngineContext<TOutput, TState>;\n private readonly streamController?: OrchestratorStreamController<\n OrchestratorResult<TOutput>\n >;\n\n public constructor(params: OrchestratorExecutionParams<TOutput, TState>) {\n this.params = params;\n this.streamController = params.streamController;\n this.ctx = {\n config: params.config,\n signature: params.signature,\n checkpointStore: resolveCheckpointStore(params.config),\n snapshotStore: resolveSnapshotStore(params.config),\n emitter: adaptEmitter(\n params.emitter,\n generateRunId(\"orchestrator\"),\n this.streamController as\n | OrchestratorStreamController<unknown>\n | undefined,\n ),\n memory: resolveOrchestratorMemory(params.config.memory),\n };\n }\n\n /**\n * `execute()` / `stream()` entry — run one turn through the 7-phase\n * lifecycle. When a `streamController` was supplied, the adapter mirrors\n * every emitted event into the stream and the controller is settled\n * (`end` / `fail`) once the result resolves.\n */\n public async run(): Promise<OrchestratorResult<TOutput>> {\n if (this.params.input === undefined || !this.params.options) {\n throw new OrchestratorConfigError(\n `ai.orchestrator(\"${this.params.config.name}\"): internal — run() invoked without input/options`,\n );\n }\n\n try {\n const result = await runTurn(\n this.ctx,\n this.params.input,\n this.params.options,\n );\n\n // Route the orchestrator's report to observers (per-flow `observe` +\n // the global observe-all gate) — parity with agent/workflow/supervisor,\n // so a durable session root no longer needs a manual observe.collect().\n await notifyObservers(this.ctx.config.observe, result.report);\n\n this.streamController?.end(result);\n\n return result;\n } catch (error) {\n this.streamController?.fail(error as Error);\n\n throw error;\n }\n }\n\n /**\n * `resume()` entry — drain an interrupted `iterate: true` turn (§9).\n * Returns `null` when nothing is in flight.\n */\n public async resume(): Promise<OrchestratorResult<TOutput> | null> {\n if (!this.params.resumeSessionId) {\n throw new OrchestratorConfigError(\n `ai.orchestrator(\"${this.params.config.name}\"): internal — resume() invoked without a sessionId`,\n );\n }\n\n return runResume(this.ctx, this.params.resumeSessionId, this.params.resumeOptions);\n }\n\n /**\n * `command(\"compact\")` entry — run a manual compaction on demand (§11 /\n * §12.1). Reuses the post-turn compaction code path against the\n * caller-supplied history and returns the raw {@link CompactionResult}.\n */\n public async compact(\n args: OrchestratorCommands[\"compact\"][\"args\"],\n ): Promise<OrchestratorCommands[\"compact\"][\"result\"]> {\n return runManualCompaction(\n this.ctx as OrchestratorEngineContext<unknown, TState>,\n args.history,\n );\n }\n}\n\n/**\n * Resolve the durable checkpoint store: the config's own field, falling\n * back to `ai.config({ defaultCheckpointStore })`. Throws\n * {@link OrchestratorConfigError} when neither resolves — persistence is\n * always on (§8.1), so a turn can never run without a checkpoint store.\n */\nfunction resolveCheckpointStore<TOutput, TState>(\n config: OrchestratorConfig<TOutput, TState>,\n) {\n const store = config.checkpointStore ?? resolveDefaultCheckpointStore();\n\n if (!store) {\n throw new OrchestratorConfigError(\n `ai.orchestrator(\"${config.name}\"): a \\`checkpointStore\\` is required ` +\n `(set one on the config or via \\`ai.config({ defaultCheckpointStore })\\`)`,\n );\n }\n\n return store;\n}\n\n/**\n * Resolve the internal-supervisor snapshot store for `iterate: true`\n * turns: the config's own field, falling back to\n * `ai.config({ defaultSnapshotStore })`. Returns `undefined` for\n * `iterate: false` orchestrators (no mid-turn resume — nothing to\n * snapshot). The factory already guarantees presence when\n * `iterate: true`, so the engine never asserts here.\n */\nfunction resolveSnapshotStore<TOutput, TState>(\n config: OrchestratorConfig<TOutput, TState>,\n) {\n if (config.iterate !== true) {\n return undefined;\n }\n\n return config.snapshotStore ?? resolveDefaultSnapshotStore();\n}\n\n/**\n * Adapt C1's three-tier {@link OrchestratorEmitter} (whose `emit` takes\n * `event, payload, identity, perCallHandlers?`) to the\n * {@link OrchestratorEmitterLike} port the phase modules call (a 2-arg\n * `emit(event, payload)` plus `bindPerCall`).\n *\n * The adapter injects the run identity centrally and, when a stream\n * controller is present, mirrors every fully-stamped event into the\n * stream pipe (§14.1 — the orchestrator's own events surface on the\n * stream alongside the bubbled child events). `bindPerCall` registers\n * the per-call `options.on` bag for the turn's duration and returns a\n * disposer that clears it.\n */\nfunction adaptEmitter(\n emitter: OrchestratorEmitter,\n runId: string,\n streamController: OrchestratorStreamController<unknown> | undefined,\n): OrchestratorEmitterLike {\n // `rootRunId === runId` for a standalone run; nested propagation lands\n // in a follow-up (see `EventIdentity`).\n const fullIdentity: EventIdentity = { runId, rootRunId: runId };\n\n let perCall: OrchestratorEventHandlers | undefined;\n\n return {\n emit<K extends OrchestratorEventName>(\n event: K,\n payload: OrchestratorEventMap[K],\n ): void {\n const fullPayload = emitter.emit(event, payload, fullIdentity, perCall);\n\n // The discriminated-union correlation between `type` and the\n // matching payload variant can't be expressed structurally — the\n // cast mirrors the supervisor stream's established pattern.\n streamController?.push({ type: event, ...fullPayload } as OrchestratorEvent);\n },\n bindPerCall(handlers: OrchestratorEventHandlers | undefined): () => void {\n perCall = handlers;\n\n return () => {\n perCall = undefined;\n };\n },\n };\n}\n\n/**\n * Phase 2 — drift check (orchestrator.md §3 / §4 Phase 2). Compares the\n * loaded checkpoint's `signature` against the current definition's.\n * Mismatch throws `OrchestratorDriftError` synchronously unless\n * `force` is set. Emits `orchestrator.drift.checked` either way. A new\n * session (no loaded signature) never drifts.\n */\nfunction assertNoDrift(\n ctx: OrchestratorEngineContext,\n sessionId: string,\n loadedSignature: string | undefined,\n force: boolean | undefined,\n): void {\n const drifted =\n loadedSignature !== undefined && loadedSignature !== ctx.signature;\n\n ctx.emitter.emit(\"orchestrator.drift.checked\", {\n sessionId,\n signature: ctx.signature,\n drifted,\n });\n\n if (drifted && !force) {\n throw new OrchestratorDriftError(\n `orchestrator \"${ctx.config.name}\": signature drift on session \"${sessionId}\" — ` +\n `the definition changed since this session was last persisted. ` +\n `Pass { force: true } only after reviewing the change, or discard / migrate the session.`,\n {\n savedSignature: loadedSignature as string,\n currentSignature: ctx.signature,\n sessionId,\n },\n );\n }\n}\n\n/**\n * Shallow-merge the per-call `state` patch (§5 — partial state\n * override) over the loaded session-state seed. The merged value\n * becomes the supervisor's seed for this turn.\n */\nfunction applyStatePatch<TState>(\n seed: TState,\n patch: Partial<TState> | undefined,\n): TState {\n if (!patch) {\n return seed;\n }\n\n return { ...seed, ...patch } as TState;\n}\n\n/**\n * Assemble the orchestrator-scope {@link OrchestratorReport} from the\n * dispatched turn's child report and the turn snapshot. Wraps the\n * child supervisor/agent report tree as `children[0]` (§15.6 —\n * `children[]` carries only the CURRENT turn's dispatched primitive\n * reports) while the per-turn forensic record lives on `turns[]`.\n */\nfunction buildReport(\n ctx: OrchestratorEngineContext,\n sessionId: string,\n turnIndex: number,\n status: OrchestratorReportStatus,\n turnSnapshot: TurnSnapshot | undefined,\n childReport: BaseReport | undefined,\n error?: AIError,\n): OrchestratorReport {\n const now = new Date().toISOString();\n const usage = turnSnapshot?.usage ?? childReport?.usage ?? ZERO_USAGE;\n\n return {\n runId: deriveRunId(sessionId, ctx.config.version, turnIndex),\n rootRunId: deriveRunId(sessionId, ctx.config.version, turnIndex),\n name: ctx.config.name,\n version: ctx.config.version,\n sessionId,\n type: \"orchestrator\",\n status,\n // Stamp the terminal error so the observe path surfaces it on the\n // orchestrator span (an observer never sees the result envelope).\n // Absent on a clean turn.\n ...(error ? { error } : {}),\n startedAt: turnSnapshot?.startedAt ?? now,\n endedAt: turnSnapshot?.endedAt ?? now,\n duration: turnSnapshot?.duration ?? 0,\n usage,\n children: childReport ? [childReport] : [],\n reportSchemaVersion: REPORT_SCHEMA_VERSION,\n turnIndex,\n signature: ctx.signature,\n turns: turnSnapshot ? [turnSnapshot] : [],\n };\n}\n\n/**\n * Map the dispatched supervisor result's report status onto the\n * orchestrator's status surface (§15.6). A clean completion that is\n * still mid-conversation reports `\"awaiting-input\"` (the session\n * continues) rather than `\"completed\"`; failures and cancellations\n * pass through.\n */\nfunction deriveStatus(childStatus: BaseReport[\"status\"]): OrchestratorReportStatus {\n if (childStatus === \"completed\") {\n return \"awaiting-input\";\n }\n\n return childStatus;\n}\n\n/**\n * Emit the terminal turn event matching the report status (§14.1).\n */\nfunction emitTerminal(\n ctx: OrchestratorEngineContext,\n sessionId: string,\n turnIndex: number,\n status: OrchestratorReportStatus,\n): void {\n if (status === \"cancelled\") {\n ctx.emitter.emit(\"orchestrator.turn.cancelled\", { sessionId, turnIndex });\n\n return;\n }\n\n if (status === \"failed\" || status === \"max-iterations\") {\n ctx.emitter.emit(\"orchestrator.turn.failed\", { sessionId, turnIndex });\n\n return;\n }\n\n if (status === \"awaiting-input\") {\n ctx.emitter.emit(\"orchestrator.turn.awaiting-input\", {\n sessionId,\n turnIndex,\n });\n\n return;\n }\n\n ctx.emitter.emit(\"orchestrator.turn.completed\", { sessionId, turnIndex });\n}\n\n/**\n * Run one turn end-to-end through the 7-phase lifecycle (orchestrator\n * .md §3). The single entry the C1 factory's `execute()` delegates to.\n *\n * Phase order is the diagram's contract: load → drift → lock → window\n * → dispatch → persist → compaction. Drift / config misuse throw;\n * every other failure surfaces on `result.error` (the contract: the\n * orchestrator never throws on runtime failure). Cancellation and\n * failure do NOT persist a fresh checkpoint (§17 — state reverts to the\n * pre-turn checkpoint).\n */\nexport async function runTurn<TOutput, TState>(\n ctx: OrchestratorEngineContext<TOutput, TState>,\n input: SupervisorInput,\n options: OrchestratorExecuteOptions<TState>,\n): Promise<OrchestratorResult<TOutput>> {\n const sessionId = options.sessionId;\n const disposePerCall = ctx.emitter.bindPerCall(options.on);\n\n try {\n // Phase 1 — load session.\n const loaded = await loadSession(ctx, sessionId);\n\n ctx.emitter.emit(\"orchestrator.turn.starting\", {\n sessionId,\n turnIndex: loaded.turnIndex,\n });\n\n ctx.emitter.emit(\"orchestrator.session.loaded\", {\n sessionId,\n turnIndex: loaded.turnIndex,\n found: loaded.found,\n });\n\n // Phase 2 — drift check.\n assertNoDrift(\n ctx as OrchestratorEngineContext,\n sessionId,\n loaded.record?.signature,\n options.force,\n );\n\n // Phase 3 — lock check (cooperative, fail-open).\n await acquireLock(ctx, sessionId, loaded.record);\n\n // Phase 4 — window history.\n const windowed = windowHistory(\n ctx as OrchestratorEngineContext,\n sessionId,\n options.history,\n );\n\n // Phase 5 — dispatch. When memory is configured, recall the\n // turn-relevant memories and inject them into the request-scoped\n // context bag so every route / router / evaluate / dispatch callback\n // surfaces them at `ctx.context[injectKey]` before routing runs.\n const seedState = applyStatePatch(loaded.state, options.state);\n\n let turnContext = options.context;\n\n // Recall is scoped to THIS session (`memory.scope`, default\n // `\"session\"`): the store is shared by every session of this\n // orchestrator instance, so the scope — not the store — is what keeps\n // another session's remembered turns out of this turn's context.\n if (ctx.memory) {\n const recalled = await recallForTurn(ctx.memory, input, sessionId);\n turnContext = injectMemories(turnContext, ctx.memory, recalled);\n }\n\n const { result, state, turnSnapshot } = await dispatchTurn<TOutput, TState>({\n ctx,\n sessionId,\n input,\n seedState,\n turnIndex: loaded.turnIndex,\n history: windowed.agents,\n context: turnContext,\n signal: options.signal,\n });\n\n ctx.emitter.emit(\"orchestrator.turn.routed\", {\n sessionId,\n turnIndex: loaded.turnIndex,\n source: turnSnapshot.decision.source,\n raw: turnSnapshot.decision.raw,\n });\n\n const status = result.error\n ? deriveStatus(result.report.status)\n : \"awaiting-input\";\n\n // Cancelled / failed turns revert: no fresh checkpoint, no compaction.\n if (result.error) {\n const report = buildReport(\n ctx as OrchestratorEngineContext,\n sessionId,\n loaded.turnIndex,\n status,\n turnSnapshot,\n result.report,\n result.error,\n );\n\n emitTerminal(ctx as OrchestratorEngineContext, sessionId, loaded.turnIndex, status);\n\n return {\n data: result.data,\n error: result.error,\n usage: result.usage,\n report,\n sessionId,\n turnIndex: loaded.turnIndex,\n };\n }\n\n // Phase 6 — persist checkpoint.\n await persistCheckpoint({\n ctx,\n sessionId,\n turnIndex: loaded.turnIndex,\n state,\n lastRoute: summarizeRoute(turnSnapshot.decision.raw as never),\n summarizedThrough: loaded.record?.summarized_through ?? null,\n });\n\n // Memory write-back (memory core M2). The turn settled cleanly (the\n // `result.error` branch above already returned for cancelled /\n // failed turns, which revert and never remember — §17), so remember\n // the input + its outcome for later recall.\n if (ctx.memory) {\n await rememberTurnOutcome(\n ctx.memory,\n input,\n outcomeTextFromTurn(result.data, turnSnapshot),\n sessionId,\n );\n }\n\n // Phase 7 — post-turn compaction (only when triggered).\n let compaction: CompactionResult | undefined;\n\n if (shouldCompact(ctx as OrchestratorEngineContext, loaded.turnIndex)) {\n const outcome = await runCompaction(\n ctx as OrchestratorEngineContext<unknown, TState>,\n sessionId,\n options.history,\n );\n\n if (outcome) {\n compaction = outcome.compaction;\n\n if (outcome.applied) {\n await advanceSummarizedThrough(\n ctx as OrchestratorEngineContext<unknown, TState>,\n sessionId,\n outcome.compaction.replacesToIndex,\n );\n }\n }\n }\n\n const report = buildReport(\n ctx as OrchestratorEngineContext,\n sessionId,\n loaded.turnIndex,\n \"awaiting-input\",\n turnSnapshot,\n result.report,\n );\n\n emitTerminal(ctx as OrchestratorEngineContext, sessionId, loaded.turnIndex, \"awaiting-input\");\n\n return {\n data: result.data,\n error: undefined,\n usage: result.usage,\n report,\n sessionId,\n turnIndex: loaded.turnIndex,\n compaction,\n };\n } finally {\n disposePerCall();\n }\n}\n\n/**\n * After a framework-applied compaction (`onCompact` succeeded), advance\n * the persisted `summarized_through` to the compaction's\n * `replacesToIndex` (§12.2 step 4). Re-saves the latest row with the\n * updated marker (append-only stores keep the prior row).\n */\nasync function advanceSummarizedThrough<TState>(\n ctx: OrchestratorEngineContext<unknown, TState>,\n sessionId: string,\n replacesToIndex: number,\n): Promise<void> {\n const latest = await ctx.checkpointStore.load(ctx.config.name, sessionId);\n\n if (!latest) {\n return;\n }\n\n await ctx.checkpointStore.save({\n ...latest,\n summarized_through: replacesToIndex,\n saved_at: new Date().toISOString(),\n });\n}\n\n/**\n * §9 resume protocol entry the C1 factory's `resume()` delegates to.\n * Returns `null` when no in-flight `iterate: true` turn is detected;\n * otherwise drains the interrupted supervisor run, persists a fresh\n * checkpoint for the resumed turn, and returns the completed result.\n *\n * Runs the same Phase 2 drift check as `runTurn` (§9.4). The heavy\n * lifting lives in `resume.ts`; this wrapper threads the engine\n * context.\n */\nexport async function runResume<TOutput, TState>(\n ctx: OrchestratorEngineContext<TOutput, TState>,\n sessionId: string,\n options?: OrchestratorResumeOptions,\n): Promise<OrchestratorResult<TOutput> | null> {\n const disposePerCall = ctx.emitter.bindPerCall(options?.on);\n\n try {\n return await resolveResume(ctx, sessionId, options, {\n assertNoDrift: (loadedSignature) =>\n assertNoDrift(\n ctx as OrchestratorEngineContext,\n sessionId,\n loadedSignature,\n options?.force,\n ),\n buildReport: (turnIndex, status, turnSnapshot, childReport) =>\n buildReport(\n ctx as OrchestratorEngineContext,\n sessionId,\n turnIndex,\n status,\n turnSnapshot,\n childReport,\n ),\n deriveStatus,\n emitTerminal: (turnIndex, status) =>\n emitTerminal(ctx as OrchestratorEngineContext, sessionId, turnIndex, status),\n persist: (turnIndex, state, lastRoute, summarizedThrough) =>\n persistCheckpoint({\n ctx,\n sessionId,\n turnIndex,\n state,\n lastRoute,\n summarizedThrough,\n }),\n });\n } finally {\n disposePerCall();\n }\n}\n\n/**\n * The `stream()` entry. The orchestrator's streaming surface bubbles\n * child agent/supervisor events under their own namespace (§14.2); the\n * C1 stream controller owns the `StreamContract` wiring. This engine\n * entry runs the same lifecycle as `runTurn` — the C1 factory passes a\n * per-call `on` bag wired to the stream controller, so the engine needs\n * no streaming-specific branch. Exposed as a distinct name for the\n * factory to call, returning the same `OrchestratorResult` the stream's\n * `.result` resolves to.\n */\nexport async function streamTurn<TOutput, TState>(\n ctx: OrchestratorEngineContext<TOutput, TState>,\n input: SupervisorInput,\n options: OrchestratorExecuteOptions<TState>,\n): Promise<OrchestratorResult<TOutput>> {\n return runTurn(ctx, input, options);\n}\n\nexport type { OrchestratorEngineContext } from \"./engine-context.type\";\nexport type { Message };\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"memory.mjs","names":[],"sources":["../../../../../../../ai/src/orchestrator/memory.ts"],"sourcesContent":["import type { MemoryContract } from \"../contracts/memory/memory.contract\";\nimport type {\n MemoryItem,\n RecalledMemory,\n} from \"../contracts/memory/memory-item.type\";\nimport type {\n OrchestratorMemoryConfig,\n OrchestratorMemoryScope,\n} from \"../contracts/orchestrator/orchestrator-config.type\";\nimport type { TurnSnapshot } from \"../contracts/result/orchestrator-result.type\";\nimport type { SupervisorInput } from \"../contracts/supervisor/supervisor-input.type\";\n\n/** Default key the recalled memories are injected under in the context bag. */\nconst DEFAULT_INJECT_KEY = \"memories\";\n\n/**\n * Default isolation boundary: a turn recalls only what its own session\n * remembered. Cross-session pooling is opt-in (`scope: \"shared\"`) — the\n * default must not leak one user's remembered turns into another's\n * context, since one memory store backs every session of an\n * orchestrator instance.\n */\nconst DEFAULT_SCOPE = \"session\" as const;\n\n/**\n * Memory wiring resolved once per turn from `OrchestratorConfig.memory`\n * (memory core M2). Normalizes the two accepted config shapes — a bare\n * {@link MemoryContract} or the richer {@link OrchestratorMemoryConfig} —\n * into a single flat record the lifecycle phase reads, so `runTurn` never\n * branches on which form the dev supplied.\n */\nexport type ResolvedOrchestratorMemory = {\n /** The store recalled-from before dispatch and remembered-into after. */\n store: MemoryContract;\n /** Recall count cap; `0` disables recall (write-only memory). */\n k?: number;\n /** Semantic-similarity floor for recall. */\n threshold?: number;\n /** Single-tier recall restriction. */\n tier?: ResolvedTier;\n /** Whether a clean turn writes its outcome back. Default `true`. */\n remember: boolean;\n /** Tier the remembered outcome lands in. Omit for the memory's `defaultTier`. */\n rememberTier?: ResolvedTier;\n /**\n * Isolation boundary for recall + write-back. Default `\"session\"` —\n * the turn's `sessionId` keys every read and write, so one session\n * cannot recall another's memories out of the shared store.\n */\n scope: OrchestratorMemoryScope;\n /** Context-bag key the recalled memories are injected under. */\n injectKey: string;\n};\n\ntype ResolvedTier = NonNullable<OrchestratorMemoryConfig[\"recall\"]>[\"tier\"];\n\n/**\n * A `MemoryContract` is the bare-store form; anything carrying a `store`\n * is the {@link OrchestratorMemoryConfig} wrapper. Distinguished by the\n * presence of `recall` — a method on the contract, absent on the config\n * (whose own `recall` is a plain options object, never a function).\n */\nfunction isBareMemory(\n value: MemoryContract | OrchestratorMemoryConfig,\n): value is MemoryContract {\n return typeof (value as MemoryContract).recall === \"function\";\n}\n\n/**\n * Normalize `OrchestratorConfig.memory` into {@link ResolvedOrchestratorMemory},\n * or `undefined` when no memory is configured. Centralizes the\n * bare-store-vs-config distinction so the engine context carries one\n * shape and the lifecycle phase stays branch-free.\n */\nexport function resolveOrchestratorMemory(\n memory: MemoryContract | OrchestratorMemoryConfig | undefined,\n): ResolvedOrchestratorMemory | undefined {\n if (!memory) {\n return undefined;\n }\n\n if (isBareMemory(memory)) {\n return {\n store: memory,\n remember: true,\n scope: DEFAULT_SCOPE,\n injectKey: DEFAULT_INJECT_KEY,\n };\n }\n\n return {\n store: memory.store,\n k: memory.recall?.k,\n threshold: memory.recall?.threshold,\n tier: memory.recall?.tier,\n remember: memory.remember ?? true,\n rememberTier: memory.rememberTier,\n scope: memory.scope ?? DEFAULT_SCOPE,\n injectKey: memory.injectKey ?? DEFAULT_INJECT_KEY,\n };\n}\n\n/**\n * Resolve the isolation key a turn reads and writes memories under\n * (4.15.0 — security fix for cross-session recall).\n *\n * The memory store is resolved once per orchestrator instance and reused\n * by every session, so this — not the store — is what keeps one session's\n * remembered turns out of another's recall. It is derived from the\n * execute-time `sessionId` by the engine and handed to every tier as an\n * exact-match filter; the model, the tool payload, and the per-call\n * `context` bag have no say in it.\n *\n * `\"shared\"` resolves to `undefined`, i.e. the store's unscoped pool —\n * the explicit opt-in back to pre-4.15.0 cross-session behavior, which\n * also keeps memories written before this release readable.\n */\nexport function memoryScopeFor(\n memory: ResolvedOrchestratorMemory,\n sessionId: string,\n): string | undefined {\n if (memory.scope === \"shared\") {\n return undefined;\n }\n\n if (typeof memory.scope === \"function\") {\n return memory.scope(sessionId);\n }\n\n return sessionMemoryScope(sessionId);\n}\n\n/**\n * The default `\"session\"` scope key: the session id under a reserved\n * prefix, so a custom `scope` callback returning a bare tenant id can\n * never accidentally collide with a session-scoped pool.\n */\nexport function sessionMemoryScope(sessionId: string): string {\n return `session:${sessionId}`;\n}\n\n/**\n * Coerce a turn's {@link SupervisorInput} (string or structured object)\n * into the natural-language query the memory store recalls / embeds\n * against. Strings pass through; objects are JSON-serialized — the same\n * coercion the supervisor applies when forwarding an object input to a\n * child agent without an explicit `input(ctx)` override.\n */\nexport function memoryQueryFromInput(input: SupervisorInput): string {\n return typeof input === \"string\" ? input : JSON.stringify(input);\n}\n\n/**\n * Recall the memories relevant to a turn's input (memory core M2 — the\n * pre-dispatch half). Returns the scored {@link RecalledMemory}[] the\n * lifecycle injects into the turn's `context` bag under\n * `memory.injectKey`. Returns an empty array — never throws on \"no hits\"\n * — and short-circuits when `k === 0` (recall disabled / write-only\n * memory) so a write-only config never round-trips the embedder.\n *\n * The recall is confined to the calling session's scope (see\n * {@link memoryScopeFor}) — `sessionId` is required, not optional, so a\n * new call site cannot silently recall across every session.\n */\nexport async function recallForTurn(\n memory: ResolvedOrchestratorMemory,\n input: SupervisorInput,\n sessionId: string,\n): Promise<RecalledMemory[]> {\n if (memory.k === 0) {\n return [];\n }\n\n return memory.store.recall(memoryQueryFromInput(input), {\n k: memory.k,\n threshold: memory.threshold,\n tier: memory.tier,\n scope: memoryScopeFor(memory, sessionId),\n });\n}\n\n/**\n * Merge the recalled memories into a fresh per-turn context bag under\n * `memory.injectKey` (memory core M2 — the injection half). Never\n * mutates the caller's `context` object — returns a new bag (or the\n * original when there is nothing to inject) so the request-scoped input\n * stays immutable, and the supervisor's intake (which freezes a\n * shallow copy) sees the recalled set on every `ctx.context[injectKey]`.\n *\n * A pre-existing value at `injectKey` is preserved when recall produced\n * nothing, and overwritten with the recalled set otherwise — the\n * orchestrator owns that key once memory is configured.\n */\nexport function injectMemories(\n context: Record<string, unknown> | undefined,\n memory: ResolvedOrchestratorMemory,\n recalled: RecalledMemory[],\n): Record<string, unknown> | undefined {\n if (recalled.length === 0) {\n return context;\n }\n\n return { ...(context ?? {}), [memory.injectKey]: recalled };\n}\n\n/**\n * Remember a settled turn's outcome (memory core M2 — the post-dispatch\n * half). Called only after a clean turn (cancelled / failed turns revert\n * and never remember — §17). No-ops when `remember` is `false`\n * (read-only memory) or when the produced text is empty.\n *\n * The remembered text is the turn input followed by the model's textual\n * outcome when one is available, so a later `recall` keyed on a similar\n * input surfaces both the prior question and its answer.\n *\n * The write is tagged with the calling session's scope (see\n * {@link memoryScopeFor}) so only that session recalls it later —\n * turn text routinely contains one user's private content.\n */\nexport async function rememberTurnOutcome(\n memory: ResolvedOrchestratorMemory,\n input: SupervisorInput,\n outcomeText: string | undefined,\n sessionId: string,\n): Promise<void> {\n if (!memory.remember) {\n return;\n }\n\n const text = buildOutcomeText(input, outcomeText);\n\n if (!text) {\n return;\n }\n\n const item: MemoryItem = {\n text,\n tier: memory.rememberTier,\n scope: memoryScopeFor(memory, sessionId),\n };\n\n await memory.store.remember(item);\n}\n\n/**\n * Compose the text written to memory for a turn: the input query, plus\n * the outcome text on a following line when the dispatch produced one.\n * Returns `undefined` when neither side carries content so an empty turn\n * never pollutes the store.\n */\nfunction buildOutcomeText(\n input: SupervisorInput,\n outcomeText: string | undefined,\n): string | undefined {\n const query = memoryQueryFromInput(input).trim();\n const outcome = outcomeText?.trim();\n\n if (query && outcome) {\n return `${query}\\n${outcome}`;\n }\n\n return query || outcome || undefined;\n}\n\n/**\n * Derive a turn's textual outcome for remembering (memory core M2).\n * Prefers the validated `result.data` (an `output` schema reshaped it);\n * otherwise stringifies the dispatched intents' branch outputs from the\n * turn snapshot, joined newline-wise so a multi-branch fan-out\n * contributes every output. Returns `undefined` when the turn produced\n * no usable text — the caller then remembers the input alone.\n */\nexport function outcomeTextFromTurn(\n data: unknown,\n turnSnapshot: TurnSnapshot,\n): string | undefined {\n const fromData = stringifyOutcome(data);\n\n if (fromData) {\n return fromData;\n }\n\n const outputs = Object.values(turnSnapshot.result)\n .map((branch) => stringifyOutcome(branch.output))\n .filter((text): text is string => Boolean(text));\n\n return outputs.length > 0 ? outputs.join(\"\\n\") : undefined;\n}\n\n/**\n * Coerce one outcome value to text: strings pass through; everything\n * else (objects, numbers) is JSON-serialized. `undefined` / `null` and\n * empty strings collapse to `undefined` so they don't masquerade as\n * content.\n */\nfunction stringifyOutcome(value: unknown): string | undefined {\n if (value === undefined || value === null) {\n return undefined;\n }\n\n const text = typeof value === \"string\" ? value : JSON.stringify(value);\n\n return text.trim() ? text : undefined;\n}\n"],"mappings":";;AAaA,MAAM,qBAAqB;;;;;;;;AAS3B,MAAM,gBAAgB;;;;;;;AAwCtB,SAAS,aACP,OACyB;CACzB,OAAO,OAAQ,MAAyB,WAAW;AACrD;;;;;;;AAQA,SAAgB,0BACd,QACwC;CACxC,IAAI,CAAC,QACH;CAGF,IAAI,aAAa,MAAM,GACrB,OAAO;EACL,OAAO;EACP,UAAU;EACV,OAAO;EACP,WAAW;CACb;CAGF,OAAO;EACL,OAAO,OAAO;EACd,GAAG,OAAO,QAAQ;EAClB,WAAW,OAAO,QAAQ;EAC1B,MAAM,OAAO,QAAQ;EACrB,UAAU,OAAO,YAAY;EAC7B,cAAc,OAAO;EACrB,OAAO,OAAO,SAAS;EACvB,WAAW,OAAO,aAAa;CACjC;AACF;;;;;;;;;;;;;;;;AAiBA,SAAgB,eACd,QACA,WACoB;CACpB,IAAI,OAAO,UAAU,UACnB;CAGF,IAAI,OAAO,OAAO,UAAU,YAC1B,OAAO,OAAO,MAAM,SAAS;CAG/B,OAAO,mBAAmB,SAAS;AACrC;;;;;;AAOA,SAAgB,mBAAmB,WAA2B;CAC5D,OAAO,WAAW;AACpB;;;;;;;;AASA,SAAgB,qBAAqB,OAAgC;CACnE,OAAO,OAAO,UAAU,WAAW,QAAQ,KAAK,UAAU,KAAK;AACjE;;;;;;;;;;;;;AAcA,eAAsB,cACpB,QACA,OACA,WAC2B;CAC3B,IAAI,OAAO,MAAM,GACf,OAAO,CAAC;CAGV,OAAO,OAAO,MAAM,OAAO,qBAAqB,KAAK,GAAG;EACtD,GAAG,OAAO;EACV,WAAW,OAAO;EAClB,MAAM,OAAO;EACb,OAAO,eAAe,QAAQ,SAAS;CACzC,CAAC;AACH;;;;;;;;;;;;;AAcA,SAAgB,eACd,SACA,QACA,UACqC;CACrC,IAAI,SAAS,WAAW,GACtB,OAAO;CAGT,OAAO;EAAE,GAAI,WAAW,CAAC;GAAK,OAAO,YAAY;CAAS;AAC5D;;;;;;;;;;;;;;;AAgBA,eAAsB,oBACpB,QACA,OACA,aACA,WACe;CACf,IAAI,CAAC,OAAO,UACV;CAGF,MAAM,OAAO,iBAAiB,OAAO,WAAW;CAEhD,IAAI,CAAC,MACH;CAGF,MAAM,OAAmB;EACvB;EACA,MAAM,OAAO;EACb,OAAO,eAAe,QAAQ,SAAS;CACzC;CAEA,MAAM,OAAO,MAAM,SAAS,IAAI;AAClC;;;;;;;AAQA,SAAS,iBACP,OACA,aACoB;CACpB,MAAM,QAAQ,qBAAqB,KAAK,EAAE,KAAK;CAC/C,MAAM,UAAU,aAAa,KAAK;CAElC,IAAI,SAAS,SACX,OAAO,GAAG,MAAM,IAAI;CAGtB,OAAO,SAAS,WAAW;AAC7B;;;;;;;;;AAUA,SAAgB,oBACd,MACA,cACoB;CACpB,MAAM,WAAW,iBAAiB,IAAI;CAEtC,IAAI,UACF,OAAO;CAGT,MAAM,UAAU,OAAO,OAAO,aAAa,MAAM,EAC9C,KAAK,WAAW,iBAAiB,OAAO,MAAM,CAAC,EAC/C,QAAQ,SAAyB,QAAQ,IAAI,CAAC;CAEjD,OAAO,QAAQ,SAAS,IAAI,QAAQ,KAAK,IAAI,IAAI;AACnD;;;;;;;AAQA,SAAS,iBAAiB,OAAoC;CAC5D,IAAI,UAAU,UAAa,UAAU,MACnC;CAGF,MAAM,OAAO,OAAO,UAAU,WAAW,QAAQ,KAAK,UAAU,KAAK;CAErE,OAAO,KAAK,KAAK,IAAI,OAAO;AAC9B"}
|
|
1
|
+
{"version":3,"file":"memory.mjs","names":[],"sources":["../../../../../../../ai/src/orchestrator/memory.ts"],"sourcesContent":["import type { MemoryContract } from \"../contracts/memory/memory.contract\";\nimport type {\n MemoryItem,\n RecalledMemory,\n} from \"../contracts/memory/memory-item.type\";\nimport type {\n OrchestratorMemoryConfig,\n OrchestratorMemoryScope,\n} from \"../contracts/orchestrator/orchestrator-config.type\";\nimport type { TurnSnapshot } from \"../contracts/result/orchestrator-result.type\";\nimport type { SupervisorInput } from \"../contracts/supervisor/supervisor-input.type\";\n\n/** Default key the recalled memories are injected under in the context bag. */\nconst DEFAULT_INJECT_KEY = \"memories\";\n\n/**\n * Default isolation boundary: a turn recalls only what its own session\n * remembered. Cross-session pooling is opt-in (`scope: \"shared\"`) — the\n * default must not leak one user's remembered turns into another's\n * context, since one memory store backs every session of an\n * orchestrator instance.\n */\nconst DEFAULT_SCOPE = \"session\" as const;\n\n/**\n * Memory wiring resolved once per turn from `OrchestratorConfig.memory`\n * (memory core M2). Normalizes the two accepted config shapes — a bare\n * {@link MemoryContract} or the richer {@link OrchestratorMemoryConfig} —\n * into a single flat record the lifecycle phase reads, so `runTurn` never\n * branches on which form the dev supplied.\n */\nexport type ResolvedOrchestratorMemory = {\n /** The store recalled-from before dispatch and remembered-into after. */\n store: MemoryContract;\n /** Recall count cap; `0` disables recall (write-only memory). */\n k?: number;\n /** Semantic-similarity floor for recall. */\n threshold?: number;\n /** Single-tier recall restriction. */\n tier?: ResolvedTier;\n /** Whether a clean turn writes its outcome back. Default `true`. */\n remember: boolean;\n /** Tier the remembered outcome lands in. Omit for the memory's `defaultTier`. */\n rememberTier?: ResolvedTier;\n /**\n * Isolation boundary for recall + write-back. Default `\"session\"` —\n * the turn's `sessionId` keys every read and write, so one session\n * cannot recall another's memories out of the shared store.\n */\n scope: OrchestratorMemoryScope;\n /** Context-bag key the recalled memories are injected under. */\n injectKey: string;\n};\n\ntype ResolvedTier = NonNullable<OrchestratorMemoryConfig[\"recall\"]>[\"tier\"];\n\n/**\n * A `MemoryContract` is the bare-store form; anything carrying a `store`\n * is the {@link OrchestratorMemoryConfig} wrapper. Distinguished by the\n * presence of `recall` — a method on the contract, absent on the config\n * (whose own `recall` is a plain options object, never a function).\n */\nfunction isBareMemory(\n value: MemoryContract | OrchestratorMemoryConfig,\n): value is MemoryContract {\n return typeof (value as MemoryContract).recall === \"function\";\n}\n\n/**\n * Normalize `OrchestratorConfig.memory` into {@link ResolvedOrchestratorMemory},\n * or `undefined` when no memory is configured. Centralizes the\n * bare-store-vs-config distinction so the engine context carries one\n * shape and the lifecycle phase stays branch-free.\n */\nexport function resolveOrchestratorMemory(\n memory: MemoryContract | OrchestratorMemoryConfig | undefined,\n): ResolvedOrchestratorMemory | undefined {\n if (!memory) {\n return undefined;\n }\n\n if (isBareMemory(memory)) {\n return {\n store: memory,\n remember: true,\n scope: DEFAULT_SCOPE,\n injectKey: DEFAULT_INJECT_KEY,\n };\n }\n\n return {\n store: memory.store,\n k: memory.recall?.k,\n threshold: memory.recall?.threshold,\n tier: memory.recall?.tier,\n remember: memory.remember ?? true,\n rememberTier: memory.rememberTier,\n scope: memory.scope ?? DEFAULT_SCOPE,\n injectKey: memory.injectKey ?? DEFAULT_INJECT_KEY,\n };\n}\n\n/**\n * Resolve the isolation key a turn reads and writes memories under\n * (4.15.0 — security fix for cross-session recall).\n *\n * The memory store is resolved once per orchestrator instance and reused\n * by every session, so this — not the store — is what keeps one session's\n * remembered turns out of another's recall. It is derived from the\n * execute-time `sessionId` by the engine and handed to every tier as an\n * exact-match filter; the model, the tool payload, and the per-call\n * `context` bag have no say in it.\n *\n * `\"shared\"` resolves to `undefined`, i.e. the store's unscoped pool —\n * the explicit opt-in back to pre-4.15.0 cross-session behavior, which\n * also keeps memories written before this release readable.\n */\nexport function memoryScopeFor(\n memory: ResolvedOrchestratorMemory,\n sessionId: string,\n): string | undefined {\n if (memory.scope === \"shared\") {\n return undefined;\n }\n\n if (typeof memory.scope === \"function\") {\n return memory.scope(sessionId);\n }\n\n return sessionMemoryScope(sessionId);\n}\n\n/**\n * The default `\"session\"` scope key: the session id under a reserved\n * prefix, so a custom `scope` callback returning a bare tenant id can\n * never accidentally collide with a session-scoped pool.\n */\nexport function sessionMemoryScope(sessionId: string): string {\n return `session:${sessionId}`;\n}\n\n/**\n * Coerce a turn's {@link SupervisorInput} (string or structured object)\n * into the natural-language query the memory store recalls / embeds\n * against. Strings pass through; objects are JSON-serialized — the same\n * coercion the supervisor applies when forwarding an object input to a\n * child agent without an explicit `input(ctx)` override.\n */\nexport function memoryQueryFromInput(input: SupervisorInput): string {\n return typeof input === \"string\" ? input : JSON.stringify(input);\n}\n\n/**\n * Recall the memories relevant to a turn's input (memory core M2 — the\n * pre-dispatch half). Returns the scored {@link RecalledMemory}[] the\n * lifecycle injects into the turn's `context` bag under\n * `memory.injectKey`. Returns an empty array — never throws on \"no hits\"\n * — and short-circuits when `k === 0` (recall disabled / write-only\n * memory) so a write-only config never round-trips the embedder.\n *\n * The recall is confined to the calling session's scope (see\n * {@link memoryScopeFor}) — `sessionId` is required, not optional, so a\n * new call site cannot silently recall across every session.\n */\nexport async function recallForTurn(\n memory: ResolvedOrchestratorMemory,\n input: SupervisorInput,\n sessionId: string,\n): Promise<RecalledMemory[]> {\n if (memory.k === 0) {\n return [];\n }\n\n return memory.store.recall(memoryQueryFromInput(input), {\n k: memory.k,\n threshold: memory.threshold,\n tier: memory.tier,\n scope: memoryScopeFor(memory, sessionId),\n });\n}\n\n/**\n * Merge the recalled memories into a fresh per-turn context bag under\n * `memory.injectKey` (memory core M2 — the injection half). Never\n * mutates the caller's `context` object — returns a new bag (or the\n * original when there is nothing to inject) so the request-scoped input\n * stays immutable, and the supervisor's intake (which freezes a\n * shallow copy) sees the recalled set on every `ctx.context[injectKey]`.\n *\n * A pre-existing value at `injectKey` is preserved when recall produced\n * nothing, and overwritten with the recalled set otherwise — the\n * orchestrator owns that key once memory is configured.\n */\nexport function injectMemories(\n context: Record<string, unknown> | undefined,\n memory: ResolvedOrchestratorMemory,\n recalled: RecalledMemory[],\n): Record<string, unknown> | undefined {\n if (recalled.length === 0) {\n return context;\n }\n\n return { ...(context ?? {}), [memory.injectKey]: recalled };\n}\n\n/**\n * Remember a settled turn's outcome (memory core M2 — the post-dispatch\n * half). Called only after a clean turn (cancelled / failed turns revert\n * and never remember — §17). No-ops when `remember` is `false`\n * (read-only memory) or when the produced text is empty.\n *\n * The remembered text is the turn input followed by the model's textual\n * outcome when one is available, so a later `recall` keyed on a similar\n * input surfaces both the prior question and its answer.\n *\n * The write is tagged with the calling session's scope (see\n * {@link memoryScopeFor}) so only that session recalls it later —\n * turn text routinely contains one user's private content.\n */\nexport async function rememberTurnOutcome(\n memory: ResolvedOrchestratorMemory,\n input: SupervisorInput,\n outcomeText: string | undefined,\n sessionId: string,\n): Promise<void> {\n if (!memory.remember) {\n return;\n }\n\n const text = buildOutcomeText(input, outcomeText);\n\n if (!text) {\n return;\n }\n\n const item: MemoryItem = {\n text,\n tier: memory.rememberTier,\n scope: memoryScopeFor(memory, sessionId),\n };\n\n await memory.store.remember(item);\n}\n\n/**\n * Compose the text written to memory for a turn: the input query, plus\n * the outcome text on a following line when the dispatch produced one.\n * Returns `undefined` when neither side carries content so an empty turn\n * never pollutes the store.\n */\nfunction buildOutcomeText(\n input: SupervisorInput,\n outcomeText: string | undefined,\n): string | undefined {\n const query = memoryQueryFromInput(input).trim();\n const outcome = outcomeText?.trim();\n\n if (query && outcome) {\n return `${query}\\n${outcome}`;\n }\n\n return query || outcome || undefined;\n}\n\n/**\n * Derive a turn's textual outcome for remembering (memory core M2).\n * Prefers the validated `result.data` (an `output` schema reshaped it);\n * otherwise stringifies the dispatched intents' branch outputs from the\n * turn snapshot, joined newline-wise so a multi-branch fan-out\n * contributes every output. Returns `undefined` when the turn produced\n * no usable text — the caller then remembers the input alone.\n */\nexport function outcomeTextFromTurn(\n data: unknown,\n turnSnapshot: TurnSnapshot,\n): string | undefined {\n const fromData = stringifyOutcome(data);\n\n if (fromData) {\n return fromData;\n }\n\n const outputs = Object.values(turnSnapshot.result)\n .map((branch) => stringifyOutcome(branch.output))\n .filter((text): text is string => Boolean(text));\n\n return outputs.length > 0 ? outputs.join(\"\\n\") : undefined;\n}\n\n/**\n * Coerce one outcome value to text: strings pass through; everything\n * else (objects, numbers) is JSON-serialized. `undefined` / `null` and\n * empty strings collapse to `undefined` so they don't masquerade as\n * content.\n */\nfunction stringifyOutcome(value: unknown): string | undefined {\n if (value === undefined || value === null) {\n return undefined;\n }\n\n const text = typeof value === \"string\" ? value : JSON.stringify(value);\n\n return text.trim() ? text : undefined;\n}\n"],"mappings":";;AAaA,MAAM,qBAAqB;;;;;;;;AAS3B,MAAM,gBAAgB;;;;;;;AAwCtB,SAAS,aACP,OACyB;CACzB,OAAO,OAAQ,MAAyB,WAAW;AACrD;;;;;;;AAQA,SAAgB,0BACd,QACwC;CACxC,IAAI,CAAC,QACH;CAGF,IAAI,aAAa,MAAM,GACrB,OAAO;EACL,OAAO;EACP,UAAU;EACV,OAAO;EACP,WAAW;CACb;CAGF,OAAO;EACL,OAAO,OAAO;EACd,GAAG,OAAO,QAAQ;EAClB,WAAW,OAAO,QAAQ;EAC1B,MAAM,OAAO,QAAQ;EACrB,UAAU,OAAO,YAAY;EAC7B,cAAc,OAAO;EACrB,OAAO,OAAO,SAAS;EACvB,WAAW,OAAO,aAAa;CACjC;AACF;;;;;;;;;;;;;;;;AAiBA,SAAgB,eACd,QACA,WACoB;CACpB,IAAI,OAAO,UAAU,UACnB;CAGF,IAAI,OAAO,OAAO,UAAU,YAC1B,OAAO,OAAO,MAAM,SAAS;CAG/B,OAAO,mBAAmB,SAAS;AACrC;;;;;;AAOA,SAAgB,mBAAmB,WAA2B;CAC5D,OAAO,WAAW;AACpB;;;;;;;;AASA,SAAgB,qBAAqB,OAAgC;CACnE,OAAO,OAAO,UAAU,WAAW,QAAQ,KAAK,UAAU,KAAK;AACjE;;;;;;;;;;;;;AAcA,eAAsB,cACpB,QACA,OACA,WAC2B;CAC3B,IAAI,OAAO,MAAM,GACf,OAAO,CAAC;CAGV,OAAO,OAAO,MAAM,OAAO,qBAAqB,KAAK,GAAG;EACtD,GAAG,OAAO;EACV,WAAW,OAAO;EAClB,MAAM,OAAO;EACb,OAAO,eAAe,QAAQ,SAAS;CACzC,CAAC;AACH;;;;;;;;;;;;;AAcA,SAAgB,eACd,SACA,QACA,UACqC;CACrC,IAAI,SAAS,WAAW,GACtB,OAAO;CAGT,OAAO;EAAE,GAAI,WAAW,CAAC;GAAK,OAAO,YAAY;CAAS;AAC5D;;;;;;;;;;;;;;;AAgBA,eAAsB,oBACpB,QACA,OACA,aACA,WACe;CACf,IAAI,CAAC,OAAO,UACV;CAGF,MAAM,OAAO,iBAAiB,OAAO,WAAW;CAEhD,IAAI,CAAC,MACH;CAGF,MAAM,OAAmB;EACvB;EACA,MAAM,OAAO;EACb,OAAO,eAAe,QAAQ,SAAS;CACzC;CAEA,MAAM,OAAO,MAAM,SAAS,IAAI;AAClC;;;;;;;AAQA,SAAS,iBACP,OACA,aACoB;CACpB,MAAM,QAAQ,qBAAqB,KAAK,CAAC,CAAC,KAAK;CAC/C,MAAM,UAAU,aAAa,KAAK;CAElC,IAAI,SAAS,SACX,OAAO,GAAG,MAAM,IAAI;CAGtB,OAAO,SAAS,WAAW;AAC7B;;;;;;;;;AAUA,SAAgB,oBACd,MACA,cACoB;CACpB,MAAM,WAAW,iBAAiB,IAAI;CAEtC,IAAI,UACF,OAAO;CAGT,MAAM,UAAU,OAAO,OAAO,aAAa,MAAM,CAAC,CAC/C,KAAK,WAAW,iBAAiB,OAAO,MAAM,CAAC,CAAC,CAChD,QAAQ,SAAyB,QAAQ,IAAI,CAAC;CAEjD,OAAO,QAAQ,SAAS,IAAI,QAAQ,KAAK,IAAI,IAAI;AACnD;;;;;;;AAQA,SAAS,iBAAiB,OAAoC;CAC5D,IAAI,UAAU,UAAa,UAAU,MACnC;CAGF,MAAM,OAAO,OAAO,UAAU,WAAW,QAAQ,KAAK,UAAU,KAAK;CAErE,OAAO,KAAK,KAAK,IAAI,OAAO;AAC9B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orchestrator-stream.d.mts","names":[],"sources":["../../../../../../../ai/src/orchestrator/orchestrator-stream.ts"],"mappings":";;;;;;AAaA;;;;;;;;KAAY,4BAAA;EACV,IAAA,CAAK,KAAA,EAAO,iBAAA;EACZ,GAAA,CAAI,MAAA,EAAQ,OAAA;EACZ,IAAA,CAAK,KAAA,EAAO,KAAA;AAAA;;;;;;;AAAK;AAwBnB;;;;;;;;;iBAAgB,wBAAA
|
|
1
|
+
{"version":3,"file":"orchestrator-stream.d.mts","names":[],"sources":["../../../../../../../ai/src/orchestrator/orchestrator-stream.ts"],"mappings":";;;;;;AAaA;;;;;;;;KAAY,4BAAA;EACV,IAAA,CAAK,KAAA,EAAO,iBAAA;EACZ,GAAA,CAAI,MAAA,EAAQ,OAAA;EACZ,IAAA,CAAK,KAAA,EAAO,KAAA;AAAA;;;;;;;AAAK;AAwBnB;;;;;;;;;iBAAgB,wBAAA;EACd,UAAA,EAAY,4BAAA,CAA6B,OAAA;EACzC,MAAA,EAAQ,cAAA,CAAe,OAAA,EAAS,iBAAA;AAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orchestrator-stream.mjs","names":[],"sources":["../../../../../../../ai/src/orchestrator/orchestrator-stream.ts"],"sourcesContent":["import type { OrchestratorEvent } from \"../contracts/orchestrator/orchestrator-event.type\";\nimport type { StreamContract } from \"../contracts/stream/stream.contract\";\n\n/**\n * Internal async-queue controller driving `orchestrator.stream()`.\n * Mirrors {@link import(\"../supervisor/supervisor-stream\").createSupervisorStream}'s\n * controller — same producer/consumer pipe, parameterized by the\n * orchestrator event union and the terminal result type.\n *\n * The turn pushes events as it advances through the lifecycle phases,\n * then settles with `end(result)` (the same `OrchestratorResult` that\n * `execute()` resolves) or `fail(error)` on an authoring/drift throw.\n */\nexport type OrchestratorStreamController<TResult> = {\n push(event: OrchestratorEvent): void;\n end(result: TResult): void;\n fail(error: Error): void;\n};\n\ntype PendingRead = {\n resolve(value: IteratorResult<OrchestratorEvent>): void;\n reject(error: Error): void;\n};\n\n/**\n * Factory mirroring `createSupervisorStream`. Returns a paired\n * `{ controller, stream }` — the turn pushes events into the controller\n * while the caller iterates (or awaits `.result`) on the stream side.\n *\n * The `result` promise resolves to the same `OrchestratorResult` value\n * `execute()` produces; it rejects only when the turn throws before\n * producing a result (drift / config misuse) — runtime failures ride on\n * `result.error` and still settle via `end()`.\n *\n * Child `supervisor.*` / `agent.*` events bubble through this same pipe\n * unmodified (the turn forwards them as it observes them on the\n * delegated run); they share the `{ type, ...payload }` shape with the\n * orchestrator's own events so iteration narrows uniformly on\n * `event.type`.\n */\nexport function createOrchestratorStream<TResult>(): {\n controller: OrchestratorStreamController<TResult>;\n stream: StreamContract<TResult, OrchestratorEvent>;\n} {\n const queue: OrchestratorEvent[] = [];\n const pending: PendingRead[] = [];\n const handlers = new Map<string, (event: OrchestratorEvent) => void>();\n\n let closed = false;\n let failure: Error | undefined;\n let resolveResult!: (value: TResult) => void;\n let rejectResult!: (error: Error) => void;\n\n const result = new Promise<TResult>((resolve, reject) => {\n resolveResult = resolve;\n rejectResult = reject;\n });\n\n const controller: OrchestratorStreamController<TResult> = {\n push(event) {\n const handler = handlers.get(event.type);\n\n if (handler) {\n try {\n handler(event);\n } catch {\n // Stream handlers must never crash the orchestrator.\n }\n }\n\n const reader = pending.shift();\n\n if (reader) {\n reader.resolve({ value: event, done: false });\n return;\n }\n\n queue.push(event);\n },\n\n end(finalResult) {\n closed = true;\n resolveResult(finalResult);\n\n while (pending.length > 0) {\n pending.shift()?.resolve({ value: undefined, done: true });\n }\n },\n\n fail(error) {\n closed = true;\n failure = error;\n rejectResult(error);\n\n while (pending.length > 0) {\n pending.shift()?.reject(error);\n }\n },\n };\n\n const iterator: AsyncIterator<OrchestratorEvent> = {\n next() {\n if (queue.length > 0) {\n return Promise.resolve({ value: queue.shift()!, done: false });\n }\n\n if (closed) {\n if (failure) {\n return Promise.reject(failure);\n }\n\n return Promise.resolve({ value: undefined, done: true });\n }\n\n return new Promise<IteratorResult<OrchestratorEvent>>(\n (resolve, reject) => {\n pending.push({ resolve, reject });\n },\n );\n },\n };\n\n const stream = {\n result,\n on(handlerMap) {\n for (const [key, handler] of Object.entries(handlerMap)) {\n if (handler) {\n handlers.set(key, handler as (event: OrchestratorEvent) => void);\n }\n }\n\n return stream;\n },\n [Symbol.asyncIterator]() {\n return iterator;\n },\n } as StreamContract<TResult, OrchestratorEvent>;\n\n return { controller, stream };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAwCA,SAAgB,2BAGd;CACA,MAAM,QAA6B,CAAC;CACpC,MAAM,UAAyB,CAAC;CAChC,MAAM,2BAAW,IAAI,IAAgD;CAErE,IAAI,SAAS;CACb,IAAI;CACJ,IAAI;CACJ,IAAI;CAEJ,MAAM,SAAS,IAAI,SAAkB,SAAS,WAAW;EACvD,gBAAgB;EAChB,eAAe;CACjB,CAAC;CAED,MAAM,aAAoD;EACxD,KAAK,OAAO;GACV,MAAM,UAAU,SAAS,IAAI,MAAM,IAAI;GAEvC,IAAI,SACF,IAAI;IACF,QAAQ,KAAK;GACf,QAAQ,CAER;GAGF,MAAM,SAAS,QAAQ,MAAM;GAE7B,IAAI,QAAQ;IACV,OAAO,QAAQ;KAAE,OAAO;KAAO,MAAM;IAAM,CAAC;IAC5C;GACF;GAEA,MAAM,KAAK,KAAK;EAClB;EAEA,IAAI,aAAa;GACf,SAAS;GACT,cAAc,WAAW;GAEzB,OAAO,QAAQ,SAAS,GACtB,QAAQ,MAAM,
|
|
1
|
+
{"version":3,"file":"orchestrator-stream.mjs","names":[],"sources":["../../../../../../../ai/src/orchestrator/orchestrator-stream.ts"],"sourcesContent":["import type { OrchestratorEvent } from \"../contracts/orchestrator/orchestrator-event.type\";\nimport type { StreamContract } from \"../contracts/stream/stream.contract\";\n\n/**\n * Internal async-queue controller driving `orchestrator.stream()`.\n * Mirrors {@link import(\"../supervisor/supervisor-stream\").createSupervisorStream}'s\n * controller — same producer/consumer pipe, parameterized by the\n * orchestrator event union and the terminal result type.\n *\n * The turn pushes events as it advances through the lifecycle phases,\n * then settles with `end(result)` (the same `OrchestratorResult` that\n * `execute()` resolves) or `fail(error)` on an authoring/drift throw.\n */\nexport type OrchestratorStreamController<TResult> = {\n push(event: OrchestratorEvent): void;\n end(result: TResult): void;\n fail(error: Error): void;\n};\n\ntype PendingRead = {\n resolve(value: IteratorResult<OrchestratorEvent>): void;\n reject(error: Error): void;\n};\n\n/**\n * Factory mirroring `createSupervisorStream`. Returns a paired\n * `{ controller, stream }` — the turn pushes events into the controller\n * while the caller iterates (or awaits `.result`) on the stream side.\n *\n * The `result` promise resolves to the same `OrchestratorResult` value\n * `execute()` produces; it rejects only when the turn throws before\n * producing a result (drift / config misuse) — runtime failures ride on\n * `result.error` and still settle via `end()`.\n *\n * Child `supervisor.*` / `agent.*` events bubble through this same pipe\n * unmodified (the turn forwards them as it observes them on the\n * delegated run); they share the `{ type, ...payload }` shape with the\n * orchestrator's own events so iteration narrows uniformly on\n * `event.type`.\n */\nexport function createOrchestratorStream<TResult>(): {\n controller: OrchestratorStreamController<TResult>;\n stream: StreamContract<TResult, OrchestratorEvent>;\n} {\n const queue: OrchestratorEvent[] = [];\n const pending: PendingRead[] = [];\n const handlers = new Map<string, (event: OrchestratorEvent) => void>();\n\n let closed = false;\n let failure: Error | undefined;\n let resolveResult!: (value: TResult) => void;\n let rejectResult!: (error: Error) => void;\n\n const result = new Promise<TResult>((resolve, reject) => {\n resolveResult = resolve;\n rejectResult = reject;\n });\n\n const controller: OrchestratorStreamController<TResult> = {\n push(event) {\n const handler = handlers.get(event.type);\n\n if (handler) {\n try {\n handler(event);\n } catch {\n // Stream handlers must never crash the orchestrator.\n }\n }\n\n const reader = pending.shift();\n\n if (reader) {\n reader.resolve({ value: event, done: false });\n return;\n }\n\n queue.push(event);\n },\n\n end(finalResult) {\n closed = true;\n resolveResult(finalResult);\n\n while (pending.length > 0) {\n pending.shift()?.resolve({ value: undefined, done: true });\n }\n },\n\n fail(error) {\n closed = true;\n failure = error;\n rejectResult(error);\n\n while (pending.length > 0) {\n pending.shift()?.reject(error);\n }\n },\n };\n\n const iterator: AsyncIterator<OrchestratorEvent> = {\n next() {\n if (queue.length > 0) {\n return Promise.resolve({ value: queue.shift()!, done: false });\n }\n\n if (closed) {\n if (failure) {\n return Promise.reject(failure);\n }\n\n return Promise.resolve({ value: undefined, done: true });\n }\n\n return new Promise<IteratorResult<OrchestratorEvent>>(\n (resolve, reject) => {\n pending.push({ resolve, reject });\n },\n );\n },\n };\n\n const stream = {\n result,\n on(handlerMap) {\n for (const [key, handler] of Object.entries(handlerMap)) {\n if (handler) {\n handlers.set(key, handler as (event: OrchestratorEvent) => void);\n }\n }\n\n return stream;\n },\n [Symbol.asyncIterator]() {\n return iterator;\n },\n } as StreamContract<TResult, OrchestratorEvent>;\n\n return { controller, stream };\n}\n"],"mappings":";;;;;;;;;;;;;;;;;AAwCA,SAAgB,2BAGd;CACA,MAAM,QAA6B,CAAC;CACpC,MAAM,UAAyB,CAAC;CAChC,MAAM,2BAAW,IAAI,IAAgD;CAErE,IAAI,SAAS;CACb,IAAI;CACJ,IAAI;CACJ,IAAI;CAEJ,MAAM,SAAS,IAAI,SAAkB,SAAS,WAAW;EACvD,gBAAgB;EAChB,eAAe;CACjB,CAAC;CAED,MAAM,aAAoD;EACxD,KAAK,OAAO;GACV,MAAM,UAAU,SAAS,IAAI,MAAM,IAAI;GAEvC,IAAI,SACF,IAAI;IACF,QAAQ,KAAK;GACf,QAAQ,CAER;GAGF,MAAM,SAAS,QAAQ,MAAM;GAE7B,IAAI,QAAQ;IACV,OAAO,QAAQ;KAAE,OAAO;KAAO,MAAM;IAAM,CAAC;IAC5C;GACF;GAEA,MAAM,KAAK,KAAK;EAClB;EAEA,IAAI,aAAa;GACf,SAAS;GACT,cAAc,WAAW;GAEzB,OAAO,QAAQ,SAAS,GACtB,QAAQ,MAAM,CAAC,EAAE,QAAQ;IAAE,OAAO;IAAW,MAAM;GAAK,CAAC;EAE7D;EAEA,KAAK,OAAO;GACV,SAAS;GACT,UAAU;GACV,aAAa,KAAK;GAElB,OAAO,QAAQ,SAAS,GACtB,QAAQ,MAAM,CAAC,EAAE,OAAO,KAAK;EAEjC;CACF;CAEA,MAAM,WAA6C,EACjD,OAAO;EACL,IAAI,MAAM,SAAS,GACjB,OAAO,QAAQ,QAAQ;GAAE,OAAO,MAAM,MAAM;GAAI,MAAM;EAAM,CAAC;EAG/D,IAAI,QAAQ;GACV,IAAI,SACF,OAAO,QAAQ,OAAO,OAAO;GAG/B,OAAO,QAAQ,QAAQ;IAAE,OAAO;IAAW,MAAM;GAAK,CAAC;EACzD;EAEA,OAAO,IAAI,SACR,SAAS,WAAW;GACnB,QAAQ,KAAK;IAAE;IAAS;GAAO,CAAC;EAClC,CACF;CACF,EACF;CAEA,MAAM,SAAS;EACb;EACA,GAAG,YAAY;GACb,KAAK,MAAM,CAAC,KAAK,YAAY,OAAO,QAAQ,UAAU,GACpD,IAAI,SACF,SAAS,IAAI,KAAK,OAA6C;GAInE,OAAO;EACT;EACA,CAAC,OAAO,iBAAiB;GACvB,OAAO;EACT;CACF;CAEA,OAAO;EAAE;EAAY;CAAO;AAC9B"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orchestrator.d.mts","names":[],"sources":["../../../../../../../ai/src/orchestrator/orchestrator.ts"],"mappings":";;;;;;;AA8DA;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAgB,YAAA,6BAEL,OAAA,mBACQ,MAAA,SAAe,qBAAA,IAAyB,MAAA,SAEvD,qBAAA,
|
|
1
|
+
{"version":3,"file":"orchestrator.d.mts","names":[],"sources":["../../../../../../../ai/src/orchestrator/orchestrator.ts"],"mappings":";;;;;;;AA8DA;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAgB,YAAA,6BAEL,OAAA,mBACQ,MAAA,SAAe,qBAAA,IAAyB,MAAA,SAEvD,qBAAA,GAGF,MAAA,EAAQ,kBAAA,CAAmB,OAAA,EAAS,MAAA,EAAQ,QAAA,IAC3C,oBAAA,CAAqB,OAAA,EAAS,MAAA"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"orchestrator.mjs","names":["orchestratorAsTool"],"sources":["../../../../../../../ai/src/orchestrator/orchestrator.ts"],"sourcesContent":["import type { OrchestratorConfig } from \"../contracts/orchestrator/orchestrator-config.type\";\nimport type {\n OrchestratorEventHandler,\n OrchestratorEventName,\n} from \"../contracts/orchestrator/orchestrator-event.type\";\nimport type {\n OrchestratorAsToolOptions,\n OrchestratorContract,\n} from \"../contracts/orchestrator/orchestrator.contract\";\nimport type { OrchestratorCommands } from \"../contracts/orchestrator/orchestrator-commands.type\";\nimport type {\n OrchestratorExecuteOptions,\n OrchestratorResumeOptions,\n} from \"../contracts/orchestrator/orchestrator-execute-options.type\";\nimport type { OrchestratorEvent } from \"../contracts/orchestrator/orchestrator-event.type\";\nimport type { OrchestratorResult } from \"../contracts/result/orchestrator-result.type\";\nimport type { StreamContract } from \"../contracts/stream/stream.contract\";\nimport type { SupervisorIntentValue } from \"../contracts/supervisor/intent-entry.type\";\nimport type { SupervisorInput } from \"../contracts/supervisor/supervisor-input.type\";\nimport { resolveDefaultSnapshotStore } from \"../config\";\nimport { OrchestratorConfigError } from \"../errors/orchestrator-config-error\";\nimport { resolveIntentEntries, type ResolvedIntentEntry } from \"../supervisor/entries\";\nimport { SupervisorFailedError } from \"../errors\";\nimport type { ToolContract } from \"../tool/tool\";\nimport type { SessionLock } from \"../contracts/orchestrator/session-lock.contract\";\nimport { asTool as orchestratorAsTool } from \"./as-tool\";\nimport { createCommandDispatcher } from \"./commands\";\nimport { OrchestratorEmitter } from \"./emitter\";\nimport { OrchestratorExecution } from \"./execution\";\nimport { createOrchestratorStream } from \"./orchestrator-stream\";\nimport { inProcessSessionLock, noopSessionLock } from \"./session-lock\";\nimport { computeOrchestratorSignature } from \"./signature\";\n\n/**\n * `ai.orchestrator(config)` — construct an {@link OrchestratorContract}:\n * a session-state manager wrapped around a supervisor (orchestrator.md\n * §1, §15). Validates the config at author time (throws\n * {@link OrchestratorConfigError} on bad shape), resolves the intent\n * entries, computes a stable structural signature for drift detection\n * (§10.1), wires the three-tier event emitter, and returns a handle that\n * runs one durable session turn per `execute` / `stream` call, resumes\n * an interrupted `iterate: true` turn via `resume`, and exposes typed\n * built-in commands plus an `asTool` wrapper.\n *\n * The \"what runs\" fields (`intents`, `route` / `router`, `evaluate`,\n * `state`, `output`, `initialAgent`, `maxIterations`) are the\n * supervisor's surface spread directly — the lifecycle builds the\n * supervisor lazily per turn and delegates to it (§3 Phase 5). Users\n * never see the supervisor object.\n *\n * @example\n * const supportBot = ai.orchestrator<SessionState>({\n * name: \"refund-support\",\n * intents: { classify, lookup, process, compose },\n * route: (ctx) => (ctx.iteration === 0 ? \"classify\" : END),\n * iterate: true,\n * checkpointStore: ai.checkpoint.pg({ client: pg }),\n * snapshotStore: ai.snapshot.pg({ client: pg }),\n * });\n *\n * const result = await supportBot.execute(message, { sessionId, history });\n */\nexport function orchestrator<\n TOutput = unknown,\n TState = TOutput,\n TIntents extends Record<string, SupervisorIntentValue> = Record<\n string,\n SupervisorIntentValue\n >,\n>(\n config: OrchestratorConfig<TOutput, TState, TIntents>,\n): OrchestratorContract<TOutput, TState> {\n validateFactoryConfig(config as unknown as OrchestratorConfig<unknown>);\n\n const entries = resolveEntries(config as unknown as OrchestratorConfig<unknown>);\n\n assertInitialAgent(config as unknown as OrchestratorConfig<unknown>, entries);\n\n const signature = computeOrchestratorSignature(\n config as unknown as OrchestratorConfig<unknown>,\n entries,\n );\n const emitter = new OrchestratorEmitter(config.on);\n\n // Per-session turn serialization (C4). Resolved ONCE so every turn on\n // this orchestrator shares the same lock — that's what lets the\n // in-process default actually serialize concurrent same-session calls.\n const sessionLock = resolveSessionLock(config as unknown as OrchestratorConfig<unknown>);\n warnOnUnlockedDurableStore(config as unknown as OrchestratorConfig<unknown>);\n\n async function execute(\n input: SupervisorInput,\n options: OrchestratorExecuteOptions<TState>,\n ): Promise<OrchestratorResult<TOutput>> {\n const execution = new OrchestratorExecution<TOutput, TState>({\n config: config as unknown as OrchestratorConfig<TOutput, TState>,\n entries,\n signature,\n emitter,\n input,\n options,\n });\n\n // Serialize the whole turn (load → dispatch → persist) against any\n // concurrent turn for the same session, so the checkpoint's\n // read-modify-write can't lose an update.\n return sessionLock.withLock(options.sessionId, () => execution.run(), {\n signal: options.signal,\n });\n }\n\n function stream(\n input: SupervisorInput,\n options: OrchestratorExecuteOptions<TState>,\n ): StreamContract<OrchestratorResult<TOutput>, OrchestratorEvent> {\n const { controller, stream: contract } = createOrchestratorStream<\n OrchestratorResult<TOutput>\n >();\n\n const execution = new OrchestratorExecution<TOutput, TState>({\n config: config as unknown as OrchestratorConfig<TOutput, TState>,\n entries,\n signature,\n emitter,\n input,\n options,\n streamController: controller,\n });\n\n // The background run waits for the session lock before it starts —\n // same serialization guarantee as `execute`; the stream contract is\n // still returned synchronously.\n void sessionLock.withLock(options.sessionId, () => execution.run(), {\n signal: options.signal,\n });\n\n return contract;\n }\n\n async function resume(\n sessionId: string,\n options?: OrchestratorResumeOptions,\n ): Promise<OrchestratorResult<TOutput> | null> {\n const execution = new OrchestratorExecution<TOutput, TState>({\n config: config as unknown as OrchestratorConfig<TOutput, TState>,\n entries,\n signature,\n emitter,\n resumeSessionId: sessionId,\n resumeOptions: options,\n });\n\n return sessionLock.withLock(sessionId, () => execution.resume(), {\n signal: options?.signal,\n });\n }\n\n // The dispatcher owns command ROUTING only; the `compact` handler\n // delegates to the shared compaction code path on the lifecycle engine\n // (§11 / §12.2 — manual compact reuses the post-turn compaction path).\n const command = createCommandDispatcher({\n compact: (args: OrchestratorCommands[\"compact\"][\"args\"]) => {\n const execution = new OrchestratorExecution<TOutput, TState>({\n config: config as unknown as OrchestratorConfig<TOutput, TState>,\n entries,\n signature,\n emitter,\n });\n\n return execution.compact(args);\n },\n });\n\n const instance: OrchestratorContract<TOutput, TState> = {\n name: config.name,\n signature,\n version: config.version,\n execute,\n stream,\n resume,\n command,\n asTool<TToolInput = string>(\n options: OrchestratorAsToolOptions<TToolInput>,\n ): ToolContract<TToolInput, TOutput> {\n return orchestratorAsTool<TOutput, TState, TToolInput>(instance, options);\n },\n on<K extends OrchestratorEventName>(\n event: K,\n handler: OrchestratorEventHandler<K>,\n ): () => void {\n return emitter.on(event, handler);\n },\n off<K extends OrchestratorEventName>(\n event: K,\n handler: OrchestratorEventHandler<K>,\n ): void {\n emitter.off(event, handler);\n },\n };\n\n return instance;\n}\n\n/**\n * Author-time validation (orchestrator.md §17). Enforces the rules that\n * must fail at construction rather than on the first turn:\n *\n * - `name` present and a string.\n * - `intents` present.\n * - `route` XOR `router` (mutually exclusive; at least one required) —\n * the supervisor's dispatch-source rule, surfaced as an orchestrator\n * config error.\n * - `router` is a valid agent contract or `{ agent, ... }` entry.\n * - `maxIterations >= 1` when set.\n * - `snapshotStore` resolvable when `iterate: true` — explicit field or\n * the global `ai.config({ defaultSnapshotStore })` fallback.\n *\n * `initialAgent` membership is checked separately once the intent\n * entries are resolved.\n */\nfunction validateFactoryConfig(config: OrchestratorConfig<unknown>): void {\n if (!config.name || typeof config.name !== \"string\") {\n throw new OrchestratorConfigError(\n \"ai.orchestrator: `name` is required and must be a string\",\n { context: { authoring: true } },\n );\n }\n\n if (!config.intents || typeof config.intents !== \"object\") {\n throw new OrchestratorConfigError(\n `ai.orchestrator(\"${config.name}\"): \\`intents\\` is required`,\n { context: { authoring: true } },\n );\n }\n\n const hasRoute = typeof config.route === \"function\";\n const hasRouter = Boolean(config.router);\n\n if (hasRouter) {\n const router = config.router;\n const isBareAgent = typeof (router as { execute?: unknown }).execute === \"function\";\n const isEntryForm =\n !isBareAgent &&\n typeof (router as { agent?: { execute?: unknown } }).agent === \"object\" &&\n typeof (router as { agent?: { execute?: unknown } }).agent?.execute === \"function\";\n\n if (!isBareAgent && !isEntryForm) {\n throw new OrchestratorConfigError(\n `ai.orchestrator(\"${config.name}\"): \\`router\\` must be an agent contract or a \\`{ agent, placeholders?, input? }\\` entry`,\n { context: { authoring: true } },\n );\n }\n }\n\n if (hasRoute && hasRouter) {\n throw new OrchestratorConfigError(\n `ai.orchestrator(\"${config.name}\"): \\`route\\` and \\`router\\` are mutually exclusive — configure exactly one`,\n { context: { authoring: true } },\n );\n }\n\n if (!hasRoute && !hasRouter) {\n throw new OrchestratorConfigError(\n `ai.orchestrator(\"${config.name}\"): one of \\`route\\` or \\`router\\` is required`,\n { context: { authoring: true } },\n );\n }\n\n if (config.maxIterations !== undefined && config.maxIterations < 1) {\n throw new OrchestratorConfigError(\n `ai.orchestrator(\"${config.name}\"): \\`maxIterations\\` must be >= 1`,\n { context: { authoring: true, maxIterations: config.maxIterations } },\n );\n }\n\n if (config.iterate && !config.snapshotStore && !resolveDefaultSnapshotStore()) {\n throw new OrchestratorConfigError(\n `ai.orchestrator(\"${config.name}\"): \\`iterate: true\\` requires a \\`snapshotStore\\` (or \\`ai.config({ defaultSnapshotStore })\\`) for mid-turn resume`,\n { context: { authoring: true } },\n );\n }\n}\n\n/**\n * Resolve the `intents` map into the supervisor's internal entry shape,\n * re-wrapping the supervisor's authoring failure as an\n * {@link OrchestratorConfigError} so misuse surfaces under the\n * orchestrator's error family rather than the supervisor's.\n */\nfunction resolveEntries(\n config: OrchestratorConfig<unknown>,\n): Map<string, ResolvedIntentEntry> {\n try {\n return resolveIntentEntries(config.intents, config.name);\n } catch (error) {\n if (error instanceof SupervisorFailedError) {\n throw new OrchestratorConfigError(error.message, {\n context: { authoring: true },\n cause: error,\n });\n }\n\n throw error;\n }\n}\n\n/**\n * Enforce the `initialAgent` membership rule (§17) once entries are\n * resolved — `initialAgent`, when set, must name a key in `intents`.\n */\nfunction assertInitialAgent(\n config: OrchestratorConfig<unknown>,\n entries: Map<string, ResolvedIntentEntry>,\n): void {\n if (config.initialAgent && !entries.has(config.initialAgent)) {\n throw new OrchestratorConfigError(\n `ai.orchestrator(\"${config.name}\"): \\`initialAgent\\` \"${config.initialAgent}\" is not a key in \\`intents\\``,\n { context: { authoring: true } },\n );\n }\n}\n\n/**\n * Resolve the per-session lock (C4): an explicit {@link SessionLock} when\n * supplied, a no-op when `sessionLock: false`, otherwise the framework\n * default in-process mutex.\n */\nfunction resolveSessionLock(config: OrchestratorConfig<unknown>): SessionLock {\n if (config.sessionLock === false) return noopSessionLock();\n if (config.sessionLock) return config.sessionLock;\n return inProcessSessionLock();\n}\n\n/** Orchestrator names already warned about an unlocked durable store. */\nconst warnedUnlockedStores = new Set<string>();\n\n/**\n * Warn once when a durable `checkpointStore` is configured but no explicit\n * `sessionLock` was supplied (C4). The in-process default serializes\n * same-session turns within one process only — a horizontally-scaled\n * deployment needs a distributed lock or sticky routing. Suppressed in\n * tests and when the dev explicitly chose a lock (or `sessionLock: false`).\n */\nfunction warnOnUnlockedDurableStore(config: OrchestratorConfig<unknown>): void {\n if (config.sessionLock !== undefined) return;\n if (!config.checkpointStore) return;\n if (process.env.NODE_ENV === \"test\" || process.env.VITEST) return;\n if (warnedUnlockedStores.has(config.name)) return;\n warnedUnlockedStores.add(config.name);\n\n console.warn(\n `[warlock-ai] orchestrator \"${config.name}\" uses a durable checkpointStore with the default in-process sessionLock. ` +\n \"That serializes same-session turns within ONE process only; in a horizontally-scaled deployment supply a distributed \" +\n \"`sessionLock` (Redis/Postgres advisory locks) or use sticky routing. Pass `sessionLock: false` to silence this.\",\n );\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"orchestrator.mjs","names":["orchestratorAsTool"],"sources":["../../../../../../../ai/src/orchestrator/orchestrator.ts"],"sourcesContent":["import type { OrchestratorConfig } from \"../contracts/orchestrator/orchestrator-config.type\";\nimport type {\n OrchestratorEventHandler,\n OrchestratorEventName,\n} from \"../contracts/orchestrator/orchestrator-event.type\";\nimport type {\n OrchestratorAsToolOptions,\n OrchestratorContract,\n} from \"../contracts/orchestrator/orchestrator.contract\";\nimport type { OrchestratorCommands } from \"../contracts/orchestrator/orchestrator-commands.type\";\nimport type {\n OrchestratorExecuteOptions,\n OrchestratorResumeOptions,\n} from \"../contracts/orchestrator/orchestrator-execute-options.type\";\nimport type { OrchestratorEvent } from \"../contracts/orchestrator/orchestrator-event.type\";\nimport type { OrchestratorResult } from \"../contracts/result/orchestrator-result.type\";\nimport type { StreamContract } from \"../contracts/stream/stream.contract\";\nimport type { SupervisorIntentValue } from \"../contracts/supervisor/intent-entry.type\";\nimport type { SupervisorInput } from \"../contracts/supervisor/supervisor-input.type\";\nimport { resolveDefaultSnapshotStore } from \"../config\";\nimport { OrchestratorConfigError } from \"../errors/orchestrator-config-error\";\nimport { resolveIntentEntries, type ResolvedIntentEntry } from \"../supervisor/entries\";\nimport { SupervisorFailedError } from \"../errors\";\nimport type { ToolContract } from \"../tool/tool\";\nimport type { SessionLock } from \"../contracts/orchestrator/session-lock.contract\";\nimport { asTool as orchestratorAsTool } from \"./as-tool\";\nimport { createCommandDispatcher } from \"./commands\";\nimport { OrchestratorEmitter } from \"./emitter\";\nimport { OrchestratorExecution } from \"./execution\";\nimport { createOrchestratorStream } from \"./orchestrator-stream\";\nimport { inProcessSessionLock, noopSessionLock } from \"./session-lock\";\nimport { computeOrchestratorSignature } from \"./signature\";\n\n/**\n * `ai.orchestrator(config)` — construct an {@link OrchestratorContract}:\n * a session-state manager wrapped around a supervisor (orchestrator.md\n * §1, §15). Validates the config at author time (throws\n * {@link OrchestratorConfigError} on bad shape), resolves the intent\n * entries, computes a stable structural signature for drift detection\n * (§10.1), wires the three-tier event emitter, and returns a handle that\n * runs one durable session turn per `execute` / `stream` call, resumes\n * an interrupted `iterate: true` turn via `resume`, and exposes typed\n * built-in commands plus an `asTool` wrapper.\n *\n * The \"what runs\" fields (`intents`, `route` / `router`, `evaluate`,\n * `state`, `output`, `initialAgent`, `maxIterations`) are the\n * supervisor's surface spread directly — the lifecycle builds the\n * supervisor lazily per turn and delegates to it (§3 Phase 5). Users\n * never see the supervisor object.\n *\n * @example\n * const supportBot = ai.orchestrator<SessionState>({\n * name: \"refund-support\",\n * intents: { classify, lookup, process, compose },\n * route: (ctx) => (ctx.iteration === 0 ? \"classify\" : END),\n * iterate: true,\n * checkpointStore: ai.checkpoint.pg({ client: pg }),\n * snapshotStore: ai.snapshot.pg({ client: pg }),\n * });\n *\n * const result = await supportBot.execute(message, { sessionId, history });\n */\nexport function orchestrator<\n TOutput = unknown,\n TState = TOutput,\n TIntents extends Record<string, SupervisorIntentValue> = Record<\n string,\n SupervisorIntentValue\n >,\n>(\n config: OrchestratorConfig<TOutput, TState, TIntents>,\n): OrchestratorContract<TOutput, TState> {\n validateFactoryConfig(config as unknown as OrchestratorConfig<unknown>);\n\n const entries = resolveEntries(config as unknown as OrchestratorConfig<unknown>);\n\n assertInitialAgent(config as unknown as OrchestratorConfig<unknown>, entries);\n\n const signature = computeOrchestratorSignature(\n config as unknown as OrchestratorConfig<unknown>,\n entries,\n );\n const emitter = new OrchestratorEmitter(config.on);\n\n // Per-session turn serialization (C4). Resolved ONCE so every turn on\n // this orchestrator shares the same lock — that's what lets the\n // in-process default actually serialize concurrent same-session calls.\n const sessionLock = resolveSessionLock(config as unknown as OrchestratorConfig<unknown>);\n warnOnUnlockedDurableStore(config as unknown as OrchestratorConfig<unknown>);\n\n async function execute(\n input: SupervisorInput,\n options: OrchestratorExecuteOptions<TState>,\n ): Promise<OrchestratorResult<TOutput>> {\n const execution = new OrchestratorExecution<TOutput, TState>({\n config: config as unknown as OrchestratorConfig<TOutput, TState>,\n entries,\n signature,\n emitter,\n input,\n options,\n });\n\n // Serialize the whole turn (load → dispatch → persist) against any\n // concurrent turn for the same session, so the checkpoint's\n // read-modify-write can't lose an update.\n return sessionLock.withLock(options.sessionId, () => execution.run(), {\n signal: options.signal,\n });\n }\n\n function stream(\n input: SupervisorInput,\n options: OrchestratorExecuteOptions<TState>,\n ): StreamContract<OrchestratorResult<TOutput>, OrchestratorEvent> {\n const { controller, stream: contract } = createOrchestratorStream<\n OrchestratorResult<TOutput>\n >();\n\n const execution = new OrchestratorExecution<TOutput, TState>({\n config: config as unknown as OrchestratorConfig<TOutput, TState>,\n entries,\n signature,\n emitter,\n input,\n options,\n streamController: controller,\n });\n\n // The background run waits for the session lock before it starts —\n // same serialization guarantee as `execute`; the stream contract is\n // still returned synchronously.\n void sessionLock.withLock(options.sessionId, () => execution.run(), {\n signal: options.signal,\n });\n\n return contract;\n }\n\n async function resume(\n sessionId: string,\n options?: OrchestratorResumeOptions,\n ): Promise<OrchestratorResult<TOutput> | null> {\n const execution = new OrchestratorExecution<TOutput, TState>({\n config: config as unknown as OrchestratorConfig<TOutput, TState>,\n entries,\n signature,\n emitter,\n resumeSessionId: sessionId,\n resumeOptions: options,\n });\n\n return sessionLock.withLock(sessionId, () => execution.resume(), {\n signal: options?.signal,\n });\n }\n\n // The dispatcher owns command ROUTING only; the `compact` handler\n // delegates to the shared compaction code path on the lifecycle engine\n // (§11 / §12.2 — manual compact reuses the post-turn compaction path).\n const command = createCommandDispatcher({\n compact: (args: OrchestratorCommands[\"compact\"][\"args\"]) => {\n const execution = new OrchestratorExecution<TOutput, TState>({\n config: config as unknown as OrchestratorConfig<TOutput, TState>,\n entries,\n signature,\n emitter,\n });\n\n return execution.compact(args);\n },\n });\n\n const instance: OrchestratorContract<TOutput, TState> = {\n name: config.name,\n signature,\n version: config.version,\n execute,\n stream,\n resume,\n command,\n asTool<TToolInput = string>(\n options: OrchestratorAsToolOptions<TToolInput>,\n ): ToolContract<TToolInput, TOutput> {\n return orchestratorAsTool<TOutput, TState, TToolInput>(instance, options);\n },\n on<K extends OrchestratorEventName>(\n event: K,\n handler: OrchestratorEventHandler<K>,\n ): () => void {\n return emitter.on(event, handler);\n },\n off<K extends OrchestratorEventName>(\n event: K,\n handler: OrchestratorEventHandler<K>,\n ): void {\n emitter.off(event, handler);\n },\n };\n\n return instance;\n}\n\n/**\n * Author-time validation (orchestrator.md §17). Enforces the rules that\n * must fail at construction rather than on the first turn:\n *\n * - `name` present and a string.\n * - `intents` present.\n * - `route` XOR `router` (mutually exclusive; at least one required) —\n * the supervisor's dispatch-source rule, surfaced as an orchestrator\n * config error.\n * - `router` is a valid agent contract or `{ agent, ... }` entry.\n * - `maxIterations >= 1` when set.\n * - `snapshotStore` resolvable when `iterate: true` — explicit field or\n * the global `ai.config({ defaultSnapshotStore })` fallback.\n *\n * `initialAgent` membership is checked separately once the intent\n * entries are resolved.\n */\nfunction validateFactoryConfig(config: OrchestratorConfig<unknown>): void {\n if (!config.name || typeof config.name !== \"string\") {\n throw new OrchestratorConfigError(\n \"ai.orchestrator: `name` is required and must be a string\",\n { context: { authoring: true } },\n );\n }\n\n if (!config.intents || typeof config.intents !== \"object\") {\n throw new OrchestratorConfigError(\n `ai.orchestrator(\"${config.name}\"): \\`intents\\` is required`,\n { context: { authoring: true } },\n );\n }\n\n const hasRoute = typeof config.route === \"function\";\n const hasRouter = Boolean(config.router);\n\n if (hasRouter) {\n const router = config.router;\n const isBareAgent = typeof (router as { execute?: unknown }).execute === \"function\";\n const isEntryForm =\n !isBareAgent &&\n typeof (router as { agent?: { execute?: unknown } }).agent === \"object\" &&\n typeof (router as { agent?: { execute?: unknown } }).agent?.execute === \"function\";\n\n if (!isBareAgent && !isEntryForm) {\n throw new OrchestratorConfigError(\n `ai.orchestrator(\"${config.name}\"): \\`router\\` must be an agent contract or a \\`{ agent, placeholders?, input? }\\` entry`,\n { context: { authoring: true } },\n );\n }\n }\n\n if (hasRoute && hasRouter) {\n throw new OrchestratorConfigError(\n `ai.orchestrator(\"${config.name}\"): \\`route\\` and \\`router\\` are mutually exclusive — configure exactly one`,\n { context: { authoring: true } },\n );\n }\n\n if (!hasRoute && !hasRouter) {\n throw new OrchestratorConfigError(\n `ai.orchestrator(\"${config.name}\"): one of \\`route\\` or \\`router\\` is required`,\n { context: { authoring: true } },\n );\n }\n\n if (config.maxIterations !== undefined && config.maxIterations < 1) {\n throw new OrchestratorConfigError(\n `ai.orchestrator(\"${config.name}\"): \\`maxIterations\\` must be >= 1`,\n { context: { authoring: true, maxIterations: config.maxIterations } },\n );\n }\n\n if (config.iterate && !config.snapshotStore && !resolveDefaultSnapshotStore()) {\n throw new OrchestratorConfigError(\n `ai.orchestrator(\"${config.name}\"): \\`iterate: true\\` requires a \\`snapshotStore\\` (or \\`ai.config({ defaultSnapshotStore })\\`) for mid-turn resume`,\n { context: { authoring: true } },\n );\n }\n}\n\n/**\n * Resolve the `intents` map into the supervisor's internal entry shape,\n * re-wrapping the supervisor's authoring failure as an\n * {@link OrchestratorConfigError} so misuse surfaces under the\n * orchestrator's error family rather than the supervisor's.\n */\nfunction resolveEntries(\n config: OrchestratorConfig<unknown>,\n): Map<string, ResolvedIntentEntry> {\n try {\n return resolveIntentEntries(config.intents, config.name);\n } catch (error) {\n if (error instanceof SupervisorFailedError) {\n throw new OrchestratorConfigError(error.message, {\n context: { authoring: true },\n cause: error,\n });\n }\n\n throw error;\n }\n}\n\n/**\n * Enforce the `initialAgent` membership rule (§17) once entries are\n * resolved — `initialAgent`, when set, must name a key in `intents`.\n */\nfunction assertInitialAgent(\n config: OrchestratorConfig<unknown>,\n entries: Map<string, ResolvedIntentEntry>,\n): void {\n if (config.initialAgent && !entries.has(config.initialAgent)) {\n throw new OrchestratorConfigError(\n `ai.orchestrator(\"${config.name}\"): \\`initialAgent\\` \"${config.initialAgent}\" is not a key in \\`intents\\``,\n { context: { authoring: true } },\n );\n }\n}\n\n/**\n * Resolve the per-session lock (C4): an explicit {@link SessionLock} when\n * supplied, a no-op when `sessionLock: false`, otherwise the framework\n * default in-process mutex.\n */\nfunction resolveSessionLock(config: OrchestratorConfig<unknown>): SessionLock {\n if (config.sessionLock === false) return noopSessionLock();\n if (config.sessionLock) return config.sessionLock;\n return inProcessSessionLock();\n}\n\n/** Orchestrator names already warned about an unlocked durable store. */\nconst warnedUnlockedStores = new Set<string>();\n\n/**\n * Warn once when a durable `checkpointStore` is configured but no explicit\n * `sessionLock` was supplied (C4). The in-process default serializes\n * same-session turns within one process only — a horizontally-scaled\n * deployment needs a distributed lock or sticky routing. Suppressed in\n * tests and when the dev explicitly chose a lock (or `sessionLock: false`).\n */\nfunction warnOnUnlockedDurableStore(config: OrchestratorConfig<unknown>): void {\n if (config.sessionLock !== undefined) return;\n if (!config.checkpointStore) return;\n if (process.env.NODE_ENV === \"test\" || process.env.VITEST) return;\n if (warnedUnlockedStores.has(config.name)) return;\n warnedUnlockedStores.add(config.name);\n\n console.warn(\n `[warlock-ai] orchestrator \"${config.name}\" uses a durable checkpointStore with the default in-process sessionLock. ` +\n \"That serializes same-session turns within ONE process only; in a horizontally-scaled deployment supply a distributed \" +\n \"`sessionLock` (Redis/Postgres advisory locks) or use sticky routing. Pass `sessionLock: false` to silence this.\",\n );\n}\n"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"session-lock.d.mts","names":[],"sources":["../../../../../../../ai/src/orchestrator/session-lock.ts"],"mappings":";;;;;AAqBA;;;;AAAmD;AA4FnD;;;;iBA5FgB,oBAAA,
|
|
1
|
+
{"version":3,"file":"session-lock.d.mts","names":[],"sources":["../../../../../../../ai/src/orchestrator/session-lock.ts"],"mappings":";;;;;AAqBA;;;;AAAmD;AA4FnD;;;;iBA5FgB,oBAAA,IAAwB,WAAW;;;;;;;iBA4FnC,eAAA,IAAmB,WAAW"}
|