@caupulican/pi-adaptative 0.81.2 → 0.81.3

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.
Files changed (81) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/core/agent-session.d.ts +1 -23
  3. package/dist/core/agent-session.d.ts.map +1 -1
  4. package/dist/core/agent-session.js +117 -131
  5. package/dist/core/agent-session.js.map +1 -1
  6. package/dist/core/background-lane-controller.d.ts +2 -0
  7. package/dist/core/background-lane-controller.d.ts.map +1 -1
  8. package/dist/core/background-lane-controller.js +3 -0
  9. package/dist/core/background-lane-controller.js.map +1 -1
  10. package/dist/core/billing-failover-controller.d.ts +31 -0
  11. package/dist/core/billing-failover-controller.d.ts.map +1 -0
  12. package/dist/core/billing-failover-controller.js +72 -0
  13. package/dist/core/billing-failover-controller.js.map +1 -0
  14. package/dist/core/billing-failover.d.ts +25 -0
  15. package/dist/core/billing-failover.d.ts.map +1 -0
  16. package/dist/core/billing-failover.js +21 -0
  17. package/dist/core/billing-failover.js.map +1 -0
  18. package/dist/core/context-pipeline.d.ts +1 -0
  19. package/dist/core/context-pipeline.d.ts.map +1 -1
  20. package/dist/core/context-pipeline.js +51 -14
  21. package/dist/core/context-pipeline.js.map +1 -1
  22. package/dist/core/model-router/config-diagnostics.d.ts +1 -1
  23. package/dist/core/model-router/config-diagnostics.d.ts.map +1 -1
  24. package/dist/core/model-router/config-diagnostics.js +29 -1
  25. package/dist/core/model-router/config-diagnostics.js.map +1 -1
  26. package/dist/core/model-router/fitness-gate.d.ts +23 -0
  27. package/dist/core/model-router/fitness-gate.d.ts.map +1 -0
  28. package/dist/core/model-router/fitness-gate.js +33 -0
  29. package/dist/core/model-router/fitness-gate.js.map +1 -0
  30. package/dist/core/model-router/status.d.ts +25 -1
  31. package/dist/core/model-router/status.d.ts.map +1 -1
  32. package/dist/core/model-router/status.js +27 -4
  33. package/dist/core/model-router/status.js.map +1 -1
  34. package/dist/core/model-router-controller.d.ts +10 -1
  35. package/dist/core/model-router-controller.d.ts.map +1 -1
  36. package/dist/core/model-router-controller.js +108 -27
  37. package/dist/core/model-router-controller.js.map +1 -1
  38. package/dist/core/runtime-builder.d.ts +16 -0
  39. package/dist/core/runtime-builder.d.ts.map +1 -1
  40. package/dist/core/runtime-builder.js +88 -1
  41. package/dist/core/runtime-builder.js.map +1 -1
  42. package/dist/core/scout-controller.d.ts +44 -0
  43. package/dist/core/scout-controller.d.ts.map +1 -0
  44. package/dist/core/scout-controller.js +165 -0
  45. package/dist/core/scout-controller.js.map +1 -0
  46. package/dist/core/settings-manager.d.ts +16 -0
  47. package/dist/core/settings-manager.d.ts.map +1 -1
  48. package/dist/core/settings-manager.js +35 -3
  49. package/dist/core/settings-manager.js.map +1 -1
  50. package/dist/core/tools/context-scout.d.ts +19 -0
  51. package/dist/core/tools/context-scout.d.ts.map +1 -0
  52. package/dist/core/tools/context-scout.js +47 -0
  53. package/dist/core/tools/context-scout.js.map +1 -0
  54. package/dist/core/tools/index.d.ts +1 -0
  55. package/dist/core/tools/index.d.ts.map +1 -1
  56. package/dist/core/tools/index.js +1 -0
  57. package/dist/core/tools/index.js.map +1 -1
  58. package/dist/main.d.ts.map +1 -1
  59. package/dist/main.js +1 -1
  60. package/dist/main.js.map +1 -1
  61. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  62. package/dist/modes/interactive/components/settings-selector.js +77 -7
  63. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  64. package/dist/modes/interactive/local-model-commands.d.ts +5 -2
  65. package/dist/modes/interactive/local-model-commands.d.ts.map +1 -1
  66. package/dist/modes/interactive/local-model-commands.js +115 -1
  67. package/dist/modes/interactive/local-model-commands.js.map +1 -1
  68. package/docs/index.md +1 -0
  69. package/docs/providers.md +2 -0
  70. package/docs/scout.md +71 -0
  71. package/docs/settings.md +14 -3
  72. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  73. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  74. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  75. package/examples/extensions/sandbox/package-lock.json +2 -2
  76. package/examples/extensions/sandbox/package.json +1 -1
  77. package/examples/extensions/with-deps/package-lock.json +2 -2
  78. package/examples/extensions/with-deps/package.json +1 -1
  79. package/examples/sdk/12-full-control.ts +1 -1
  80. package/npm-shrinkwrap.json +12 -12
  81. package/package.json +4 -4
@@ -1 +1 @@
1
- {"version":3,"file":"context-pipeline.js","sourceRoot":"","sources":["../../src/core/context-pipeline.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Context pipeline: the session's per-turn context-shaping subsystem — the observe-only context\n * audit, the shadow prompt-policy plan and its correlation with legacy context-gc, the enforcement\n * pilot, the relevance-curation queue + the fitness-gated curation model resolver / brain-curation\n * drain + compaction pre-digest, the legacy context-gc pass and its packed-artifact reference\n * release, the tool-output artifact store, and the current-context token estimate.\n *\n * Extracted verbatim from agent-session.ts (god-file decomposition). Owns the latest\n * audit/policy/correlation/enforcement/gc reports, the {@link BrainCurator} sidecar and its last\n * skip reasons, and the lazily-built tool-artifact store. Everything else it needs — the turn\n * index, the session/settings managers, the model registry, agent/workspace dirs, the active tool\n * names, the disposed flag, the isolated-completion primitive, spawned-usage accounting, and the\n * live {@link MemoryManager} — is reached through narrow deps accessors rather than the whole\n * AgentSession.\n *\n * Context-transform boundary (deliberate): the per-turn stages ({@link estimateCurrentContextTokens},\n * {@link runContextAudit}, {@link runPromptPolicyPlanning}, {@link applyContextGc},\n * {@link correlatePromptPolicyWithContextGc}, {@link runPromptEnforcement},\n * {@link enqueueRelevanceCuration}, {@link maybeDrainBrainCuration}) are invoked from the session's\n * context transform as one-line delegations, so the transform stays the single owner of the pass\n * ordering. This controller reaches {@link MemoryController} functionality only through\n * {@link ContextPipelineDeps.getMemoryManager} (never imports it), and MemoryController never imports\n * the pipeline — keeping the transform the one place the two subsystems meet.\n */\n\nimport { join } from \"node:path\";\nimport type { AgentMessage } from \"@caupulican/pi-agent-core\";\nimport { estimateContextTokens, getLatestCompactionEntry, type SessionManager } from \"@caupulican/pi-agent-core/node\";\nimport type { Api, AssistantMessage, Model, Usage } from \"@caupulican/pi-ai\";\nimport type { IsolatedCompletionOptions, IsolatedCompletionResult } from \"./agent-session.ts\";\nimport { BrainCurator, type CurationTelemetrySnapshot, preDigestConversationText } from \"./context/brain-curator.ts\";\nimport { type ArtifactStore, createFileArtifactStore } from \"./context/context-artifacts.ts\";\nimport { type ContextAuditReport, runContextAudit } from \"./context/context-audit.ts\";\nimport { enforcePromptPolicy, type PromptEnforcementReport } from \"./context/context-prompt-enforcement.ts\";\nimport {\n\tcorrelateWithContextGc,\n\ttype PromptPolicyGcCorrelationReport,\n\ttype PromptPolicyShadowReport,\n\tplanPromptPolicy,\n} from \"./context/context-prompt-policy.ts\";\nimport { applyContextGc, type ContextGcReport } from \"./context-gc.ts\";\nimport type { MemoryManager } from \"./memory/memory-manager.ts\";\nimport type { ModelRegistry } from \"./model-registry.ts\";\nimport { resolveCliModel } from \"./model-resolver.ts\";\nimport { FitnessStore } from \"./models/fitness-store.ts\";\nimport type { SettingsManager } from \"./settings-manager.ts\";\n\n/** Latest user prompt text in the provider-visible array (curation goal line; bounded by caller). */\nexport function latestUserPromptText(messages: AgentMessage[]): string {\n\tfor (let index = messages.length - 1; index >= 0; index--) {\n\t\tconst message = messages[index];\n\t\tif (!message || message.role !== \"user\") continue;\n\t\tif (typeof message.content === \"string\") return message.content;\n\t\tconst text = message.content\n\t\t\t.filter((part): part is { type: \"text\"; text: string } => (part as { type?: string }).type === \"text\")\n\t\t\t.map((part) => part.text)\n\t\t\t.join(\"\\n\");\n\t\tif (text.length > 0) return text;\n\t}\n\treturn \"\";\n}\n\n/** Read a packed grep/find tool result's `details.artifactId`, if present, without `any`. */\nfunction extractArtifactId(message: AgentMessage | undefined): string | undefined {\n\tif (!message || message.role !== \"toolResult\") return undefined;\n\tconst details = (message as { details?: unknown }).details;\n\tif (typeof details !== \"object\" || details === null) return undefined;\n\tconst artifactId = (details as { artifactId?: unknown }).artifactId;\n\treturn typeof artifactId === \"string\" ? artifactId : undefined;\n}\n\nexport interface ContextPipelineDeps {\n\t/** Current turn index, stamped into audit/policy/enforcement reports. */\n\tgetTurnIndex(): number;\n\t/** Session log: audit lookup, gc/artifact storage dirs, curation entries, token-estimate compaction anchor. */\n\tgetSessionManager(): SessionManager;\n\t/** Context-gc / prompt-enforcement / curation settings (all opt-in gates). */\n\tgetSettingsManager(): SettingsManager;\n\t/** Resolves a configured curation model pattern against configured auth. */\n\tgetModelRegistry(): ModelRegistry;\n\t/** Root dir the host-keyed {@link FitnessStore} and per-session gc/artifact storage live under. */\n\tgetAgentDir(): string;\n\t/** Workspace root, passed to the context-gc pass. */\n\tgetCwd(): string;\n\t/** Currently-active tool names — enforcement checks whether artifact_retrieve is a live affordance. */\n\tgetActiveToolNames(): string[];\n\t/** A disposed session must never persist a curation/pre-digest entry. */\n\tisDisposed(): boolean;\n\t/** The live memory manager — the active providers' page markers feed the semantic-gc scan. */\n\tgetMemoryManager(): MemoryManager;\n\t/** Roll a curation drain's spawned usage into session accounting (idempotent per reportId). */\n\taddSpawnedUsage(\n\t\tusage: Usage,\n\t\topts?: { label?: string; sourceSessionId?: string; reportId?: string },\n\t): string | undefined;\n\t/** One-shot LLM call fully isolated from the main session — the curation/pre-digest execution primitive. */\n\trunIsolatedCompletion(opts: IsolatedCompletionOptions): Promise<IsolatedCompletionResult>;\n}\n\nexport class ContextPipeline {\n\tprivate _latestContextGcReport: ContextGcReport | undefined = undefined;\n\t/** Brain-curation sidecar (design: brain-context-curation-design.md). Inert unless the\n\t * contextPolicy.curation setting is enabled AND the model passes the digest fitness gate. */\n\tprivate readonly _brainCurator = new BrainCurator();\n\tprivate _lastCurationSkipReason: string | undefined = undefined;\n\tprivate _lastPreDigestSkipReason: string | undefined = undefined;\n\tprivate _toolArtifactStore: ArtifactStore | undefined = undefined;\n\tprivate _latestContextAuditReport: ContextAuditReport | undefined = undefined;\n\tprivate _latestPromptPolicyReport: PromptPolicyShadowReport | undefined = undefined;\n\tprivate _latestPromptPolicyGcCorrelation: PromptPolicyGcCorrelationReport | undefined = undefined;\n\tprivate _latestPromptEnforcementReport: PromptEnforcementReport | undefined = undefined;\n\n\tprivate readonly deps: ContextPipelineDeps;\n\n\tconstructor(deps: ContextPipelineDeps) {\n\t\tthis.deps = deps;\n\t}\n\n\tprivate _contextGcStorageDir(): string {\n\t\treturn join(this.deps.getAgentDir(), \"context-gc\", this.deps.getSessionManager().getSessionId());\n\t}\n\n\tprivate _toolArtifactsDir(): string {\n\t\treturn join(this.deps.getAgentDir(), \"context-artifacts\", this.deps.getSessionManager().getSessionId());\n\t}\n\n\t/**\n\t * Session-scoped, filesystem-backed artifact store for first-capture-then-bound tool\n\t * output (grep/find only, for now -- see tool-output-artifacts.md). Lazily created and\n\t * cached so every tool construction in this session shares one store instance.\n\t *\n\t * `packToolOutput()` registers a reference (the packing tool call's id) at pack time\n\t * and fails closed, so packed artifacts are never prematurely collected.\n\t * `_releaseGcPackedArtifactReferences()` (called from `applyContextGc()`) releases\n\t * that reference once context-gc packs the result out of live context, and\n\t * opportunistically reclaims now-unreferenced artifacts via `cleanup()`.\n\t * Remaining carry-forward gap: cleanup() now also runs at dispose(), but only reclaims\n\t * already-released (zero-reference) artifacts. A session that ends before context-gc\n\t * ever evicts a result never releases that reference, so its artifact stays on disk by\n\t * design (resolvable on resume). Reclaiming those requires an explicit cross-session\n\t * expiry/liveness policy, not just a sweep.\n\t */\n\tgetToolArtifactStore(): ArtifactStore {\n\t\tthis._toolArtifactStore ??= createFileArtifactStore({ baseDir: this._toolArtifactsDir() });\n\t\treturn this._toolArtifactStore;\n\t}\n\n\t/**\n\t * Best-effort final sweep of any already-released (zero-reference) tool-output artifact at\n\t * session dispose. Reads the field (not the getter) so a session that never packed anything\n\t * doesn't force-create a store/dir just to sweep it.\n\t */\n\tcleanupToolArtifactStoreOnDispose(): void {\n\t\tthis._toolArtifactStore?.cleanup();\n\t}\n\n\t/**\n\t * One pass over the current branch, mapping each toolResult's toolCallId to its\n\t * persisted session-entry id. Rebuilt every audit pass (O(branch) per turn), so this is\n\t * O(n^2) over a long session. Fine at current scale; after the artifact-read fix this is\n\t * the next per-turn audit cost to optimize if it ever matters (e.g. cache/incrementally\n\t * update instead of a full rebuild).\n\t */\n\tprivate _buildSessionEntryIdLookup(): (toolCallId: string) => string | undefined {\n\t\tconst map = new Map<string, string>();\n\t\tfor (const entry of this.deps.getSessionManager().getBranch()) {\n\t\t\tif (entry.type === \"message\" && entry.message.role === \"toolResult\") {\n\t\t\t\tmap.set(entry.message.toolCallId, entry.id);\n\t\t\t}\n\t\t}\n\t\treturn (toolCallId: string) => map.get(toolCallId);\n\t}\n\n\t/**\n\t * Phase 1 observe-only audit pass (see context/context-audit.ts): converts live\n\t * toolResult messages into ContextItems and runs the existing retention/hard-constraint\n\t * evaluators over them, storing the latest deterministic report for tests/debugging.\n\t * Read-only with respect to messages, the transcript, and artifact references -- uses\n\t * `_toolArtifactStore` (the field), not `getToolArtifactStore()` (the getter), so a\n\t * session that never packed anything doesn't force-create a store/dir just to audit.\n\t * Never throws into a live turn: any failure degrades to an empty report.\n\t */\n\trunContextAudit(messages: AgentMessage[]): ContextAuditReport {\n\t\ttry {\n\t\t\tconst report = runContextAudit(messages, {\n\t\t\t\tturnIndex: this.deps.getTurnIndex(),\n\t\t\t\tartifactStore: this._toolArtifactStore,\n\t\t\t\tsessionEntryIdForToolCallId: this._buildSessionEntryIdLookup(),\n\t\t\t});\n\t\t\tthis._latestContextAuditReport = report;\n\t\t\treturn report;\n\t\t} catch {\n\t\t\tconst report: ContextAuditReport = { turnIndex: this.deps.getTurnIndex(), items: [] };\n\t\t\tthis._latestContextAuditReport = report;\n\t\t\treturn report;\n\t\t}\n\t}\n\n\t/**\n\t * Read-only inspection of the context audit. With `messages`, recomputes fresh against\n\t * the given array (still no mutation of messages/transcript/artifact refs); without,\n\t * returns the last report computed during a real transform pass.\n\t */\n\tgetContextAuditReport(messages?: AgentMessage[]): ContextAuditReport {\n\t\tif (messages) return this.runContextAudit(messages);\n\t\treturn this._latestContextAuditReport ?? { turnIndex: this.deps.getTurnIndex(), items: [] };\n\t}\n\n\t/**\n\t * Observe-first shadow/planning pass (see context/context-prompt-policy.ts): re-shapes\n\t * the audit report into a per-item policy plan whose `appliedAction` is always\n\t * \"keep_raw\" -- this never enforces anything, it only records what the policy engine\n\t * would say. Never throws into a live turn: any failure degrades to an empty report.\n\t */\n\trunPromptPolicyPlanning(auditReport: ContextAuditReport): PromptPolicyShadowReport {\n\t\ttry {\n\t\t\tconst report = planPromptPolicy(auditReport);\n\t\t\tthis._latestPromptPolicyReport = report;\n\t\t\treturn report;\n\t\t} catch {\n\t\t\tconst report: PromptPolicyShadowReport = { turnIndex: this.deps.getTurnIndex(), items: [] };\n\t\t\tthis._latestPromptPolicyReport = report;\n\t\t\treturn report;\n\t\t}\n\t}\n\n\t/**\n\t * Read-only inspection of the shadow policy plan. With `messages`, recomputes fresh\n\t * (audit + plan) against the given array; without, returns the last plan computed\n\t * during a real transform pass. Never mutates messages/transcript/artifact refs.\n\t */\n\tgetPromptPolicyReport(messages?: AgentMessage[]): PromptPolicyShadowReport {\n\t\tif (messages) return this.runPromptPolicyPlanning(this.runContextAudit(messages));\n\t\treturn this._latestPromptPolicyReport ?? { turnIndex: this.deps.getTurnIndex(), items: [] };\n\t}\n\n\t/**\n\t * Report-only correlation between the shadow plan just computed this turn and what the\n\t * legacy context-gc pass actually packed. Runs after `applyContextGc()` has already\n\t * produced its report; never influences context-gc itself. Never throws into a live\n\t * turn: any failure degrades to an empty correlation.\n\t */\n\tcorrelatePromptPolicyWithContextGc(gcReport: ContextGcReport): void {\n\t\tconst shadowReport = this._latestPromptPolicyReport ?? { turnIndex: this.deps.getTurnIndex(), items: [] };\n\t\ttry {\n\t\t\tthis._latestPromptPolicyGcCorrelation = correlateWithContextGc(shadowReport, gcReport);\n\t\t} catch {\n\t\t\tthis._latestPromptPolicyGcCorrelation = { turnIndex: this.deps.getTurnIndex(), entries: [] };\n\t\t}\n\t}\n\n\t/** Read-only inspection of the latest shadow-plan/legacy-gc correlation, for tests/debugging. */\n\tgetPromptPolicyGcCorrelation(): PromptPolicyGcCorrelationReport {\n\t\treturn this._latestPromptPolicyGcCorrelation ?? { turnIndex: this.deps.getTurnIndex(), entries: [] };\n\t}\n\n\t/**\n\t * First enforcement pilot (see context/context-prompt-enforcement.ts): opt-in,\n\t * default-disabled stub-in-place of stale artifact-backed tool_output results in the\n\t * provider-visible message array only. Runs on `messages` AFTER context-gc has already\n\t * produced its own result, so legacy context-gc's own packing/reporting is completely\n\t * unaffected by this pass -- it only ever acts on messages gc left untouched this turn.\n\t * Never throws into a live turn: any failure degrades to returning `messages` unchanged.\n\t */\n\trunPromptEnforcement(\n\t\tmessages: AgentMessage[],\n\t\tshadowReport: PromptPolicyShadowReport,\n\t): { messages: AgentMessage[]; report: PromptEnforcementReport } {\n\t\ttry {\n\t\t\tconst persistedSettings = this.deps.getSettingsManager().getContextPromptEnforcementSettings();\n\t\t\tconst curationEnabled = this.deps.getSettingsManager().getContextCurationSettings().enabled;\n\t\t\tconst settings = {\n\t\t\t\t...persistedSettings,\n\t\t\t\t// Runtime fact, never assumed: artifact_retrieve is a companion affordance\n\t\t\t\t// (auto-activated alongside grep/find), not a default/global tool, so active\n\t\t\t\t// tools can differ turn to turn -- see context-prompt-enforcement.ts's doc\n\t\t\t\t// comment on why this is checked separately from hasAvailableRetrievalPath.\n\t\t\t\tretrievalToolAvailable: this.deps.getActiveToolNames().includes(\"artifact_retrieve\"),\n\t\t\t\tbrainRelevance: curationEnabled ? (itemId: string) => this._brainCurator.getRelevance(itemId) : undefined,\n\t\t\t};\n\t\t\tconst result = enforcePromptPolicy(messages, shadowReport, settings);\n\t\t\tthis._latestPromptEnforcementReport = result.report;\n\t\t\treturn result;\n\t\t} catch {\n\t\t\tconst report: PromptEnforcementReport = { turnIndex: this.deps.getTurnIndex(), items: [] };\n\t\t\tthis._latestPromptEnforcementReport = report;\n\t\t\treturn { messages, report };\n\t\t}\n\t}\n\n\t/**\n\t * Enqueue relevance-scoring jobs for stale, artifact-backed tool outputs the enforcement\n\t * pilot could act on. Pure queueing — the verdicts only ever take effect through the\n\t * asymmetric advisory lever inside enforcePromptPolicy. Never throws into a turn.\n\t */\n\tenqueueRelevanceCuration(messages: AgentMessage[], shadowReport: PromptPolicyShadowReport): void {\n\t\ttry {\n\t\t\tconst settings = this.deps.getSettingsManager().getContextCurationSettings();\n\t\t\tif (!settings.enabled) return;\n\t\t\tconst goal = latestUserPromptText(messages).slice(0, 400);\n\t\t\tfor (const item of shadowReport.items) {\n\t\t\t\tif (!item.hasAvailableRetrievalPath) continue;\n\t\t\t\tconst message = messages[item.messageIndex];\n\t\t\t\tif (!message || message.role !== \"toolResult\" || message.toolCallId !== item.toolCallId) continue;\n\t\t\t\tif (message.isError) continue;\n\t\t\t\tconst details = message.details as\n\t\t\t\t\t| { contextGc?: { packed?: unknown }; promptPolicy?: { enforced?: unknown } }\n\t\t\t\t\t| undefined;\n\t\t\t\tif (details?.contextGc?.packed === true || details?.promptPolicy?.enforced === true) continue;\n\t\t\t\tconst text = message.content\n\t\t\t\t\t.filter((part): part is { type: \"text\"; text: string } => part.type === \"text\")\n\t\t\t\t\t.map((part) => part.text)\n\t\t\t\t\t.join(\"\\n\");\n\t\t\t\tif (text.length === 0) continue;\n\t\t\t\tthis._brainCurator.enqueue({ kind: \"relevance\", key: item.itemId, content: text.slice(0, 4000), goal });\n\t\t\t}\n\t\t} catch {\n\t\t\t// curation is a sidecar; it must never disrupt a turn\n\t\t}\n\t}\n\n\t/**\n\t * Drain gate: settings on, model configured+authed, and the model has PASSED the digest\n\t * fitness probe on THIS host (design: unfit or unprobed models are refused with a visible\n\t * reason, never silently degraded). Fire-and-forget; never throws into a turn.\n\t */\n\t/**\n\t * Resolve the curation model IFF every gate passes: setting enabled, model configured,\n\t * resolvable+authed, and digest-fitness-proven on THIS host (canonical \"provider/id\" ref —\n\t * runModelFitness stores reports under it, while settings.model may be a bare id or pattern).\n\t * Sets _lastCurationSkipReason on refusal; never throws.\n\t */\n\tresolveCurationModelIfFit(): Model<Api> | undefined {\n\t\tconst settings = this.deps.getSettingsManager().getContextCurationSettings();\n\t\tif (!settings.enabled) {\n\t\t\t// Never surface a stale refusal reason for a feature the user has since disabled.\n\t\t\tthis._lastCurationSkipReason = undefined;\n\t\t\treturn undefined;\n\t\t}\n\t\tif (!settings.model) {\n\t\t\tthis._lastCurationSkipReason = \"curation_model_unset\";\n\t\t\treturn undefined;\n\t\t}\n\t\tconst resolved = resolveCliModel({ cliModel: settings.model, modelRegistry: this.deps.getModelRegistry() });\n\t\tif (!resolved.model || !this.deps.getModelRegistry().hasConfiguredAuth(resolved.model)) {\n\t\t\tthis._lastCurationSkipReason = \"curation_model_unresolved\";\n\t\t\treturn undefined;\n\t\t}\n\t\tconst canonicalRef = `${resolved.model.provider}/${resolved.model.id}`;\n\t\tconst fitness = FitnessStore.forAgentDir(this.deps.getAgentDir())\n\t\t\t.getForHost()\n\t\t\t.find((entry) => entry.model === canonicalRef);\n\t\tconst digestScore = fitness?.report.digest;\n\t\tif (!digestScore) {\n\t\t\tthis._lastCurationSkipReason = \"curation_model_unprobed\";\n\t\t\treturn undefined;\n\t\t}\n\t\tif (digestScore.succeeded < Math.ceil(digestScore.total * (2 / 3))) {\n\t\t\tthis._lastCurationSkipReason = \"curation_model_digest_unfit\";\n\t\t\treturn undefined;\n\t\t}\n\t\tthis._lastCurationSkipReason = undefined;\n\t\treturn resolved.model;\n\t}\n\n\tmaybeDrainBrainCuration(): void {\n\t\ttry {\n\t\t\tif (!this._brainCurator.hasWork() || this._brainCurator.isDraining) return;\n\t\t\tconst model = this.resolveCurationModelIfFit();\n\t\t\tif (!model) return;\n\t\t\tconst settings = this.deps.getSettingsManager().getContextCurationSettings();\n\t\t\tvoid this._drainBrainCuration(model, settings.maxJobsPerTurn);\n\t\t} catch {\n\t\t\t// curation is a sidecar; it must never disrupt a turn\n\t\t}\n\t}\n\n\t/**\n\t * Compaction pre-digest gate (design surface 3): everything the drain gate requires PLUS a\n\t * RUNTIME reliability proof — the curator must have run >=5 jobs on this session with a parse\n\t * failure rate <=5% before it is trusted to pre-digest compaction input. Returns undefined\n\t * (verbatim compaction, byte-for-byte today's behavior) whenever any gate refuses.\n\t */\n\tbuildCompactionPreDigest(): ((text: string, signal?: AbortSignal) => Promise<string>) | undefined {\n\t\ttry {\n\t\t\tconst model = this.resolveCurationModelIfFit();\n\t\t\tif (!model) return undefined;\n\t\t\tconst telemetry = this._brainCurator.telemetry();\n\t\t\tif (telemetry.jobsRun < 5 || telemetry.parseFailures / telemetry.jobsRun > 0.05) {\n\t\t\t\tthis._lastPreDigestSkipReason = \"curation_predigest_reliability_unproven\";\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\tthis._lastPreDigestSkipReason = undefined;\n\t\t\treturn async (text, signal) => {\n\t\t\t\tconst result = await preDigestConversationText({\n\t\t\t\t\ttext,\n\t\t\t\t\tsignal,\n\t\t\t\t\tcomplete: async ({ systemPrompt, userPrompt, signal: chunkSignal }) => {\n\t\t\t\t\t\tconst completion = await this.deps.runIsolatedCompletion({\n\t\t\t\t\t\t\tsystemPrompt,\n\t\t\t\t\t\t\tmessages: [{ role: \"user\", content: [{ type: \"text\", text: userPrompt }], timestamp: Date.now() }],\n\t\t\t\t\t\t\tmodel,\n\t\t\t\t\t\t\tthinkingLevel: \"off\",\n\t\t\t\t\t\t\tmaxTokens: 512,\n\t\t\t\t\t\t\tsignal: chunkSignal,\n\t\t\t\t\t\t\tcacheRetention: \"short\",\n\t\t\t\t\t\t});\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\ttext: completion.text,\n\t\t\t\t\t\t\tcostUsd: completion.usage.cost.total,\n\t\t\t\t\t\t\tstopReason: String(completion.stopReason),\n\t\t\t\t\t\t};\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\tif (!this.deps.isDisposed() && result.totalChunks > 0) {\n\t\t\t\t\tthis.deps.getSessionManager().appendCustomEntry(\"brain-curation-predigest\", {\n\t\t\t\t\t\tversion: 1,\n\t\t\t\t\t\ttotalChunks: result.totalChunks,\n\t\t\t\t\t\tdigested: result.digested,\n\t\t\t\t\t\tfailed: result.failed,\n\t\t\t\t\t\tcharsBefore: text.length,\n\t\t\t\t\t\tcharsAfter: result.text.length,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn result.text;\n\t\t\t};\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\tprivate async _drainBrainCuration(model: Model<Api>, maxJobs: number): Promise<void> {\n\t\ttry {\n\t\t\t// ACCUMULATE across all drained jobs (the drain runs the completer once PER job) —\n\t\t\t// keeping only the last job's usage would under-report every multi-job drain.\n\t\t\tlet spentUsage: AssistantMessage[\"usage\"] | undefined;\n\t\t\tconst results = await this._brainCurator.drain({\n\t\t\t\tmaxJobs,\n\t\t\t\tcomplete: async ({ systemPrompt, userPrompt, signal }) => {\n\t\t\t\t\tconst completion = await this.deps.runIsolatedCompletion({\n\t\t\t\t\t\tsystemPrompt,\n\t\t\t\t\t\tmessages: [{ role: \"user\", content: [{ type: \"text\", text: userPrompt }], timestamp: Date.now() }],\n\t\t\t\t\t\tmodel,\n\t\t\t\t\t\tthinkingLevel: \"off\",\n\t\t\t\t\t\tmaxTokens: 256,\n\t\t\t\t\t\tsignal,\n\t\t\t\t\t\t// Both curation system prompts are static — the provider can cache the prefix.\n\t\t\t\t\t\tcacheRetention: \"short\",\n\t\t\t\t\t});\n\t\t\t\t\tconst usage = completion.usage;\n\t\t\t\t\tif (!spentUsage) {\n\t\t\t\t\t\tspentUsage = structuredClone(usage);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tspentUsage.input += usage.input;\n\t\t\t\t\t\tspentUsage.output += usage.output;\n\t\t\t\t\t\tspentUsage.cacheRead += usage.cacheRead;\n\t\t\t\t\t\tspentUsage.cacheWrite += usage.cacheWrite;\n\t\t\t\t\t\tspentUsage.totalTokens += usage.totalTokens;\n\t\t\t\t\t\tspentUsage.cost.input += usage.cost.input;\n\t\t\t\t\t\tspentUsage.cost.output += usage.cost.output;\n\t\t\t\t\t\tspentUsage.cost.cacheRead += usage.cost.cacheRead;\n\t\t\t\t\t\tspentUsage.cost.cacheWrite += usage.cost.cacheWrite;\n\t\t\t\t\t\tspentUsage.cost.total += usage.cost.total;\n\t\t\t\t\t}\n\t\t\t\t\treturn {\n\t\t\t\t\t\ttext: completion.text,\n\t\t\t\t\t\tcostUsd: completion.usage.cost.total,\n\t\t\t\t\t\tstopReason: String(completion.stopReason),\n\t\t\t\t\t};\n\t\t\t\t},\n\t\t\t});\n\t\t\t// Honest accounting even for free local models: token visibility is the contract.\n\t\t\tif (spentUsage && (spentUsage.cost.total > 0 || spentUsage.totalTokens > 0)) {\n\t\t\t\tthis.deps.addSpawnedUsage(spentUsage, { label: \"context-curator\" });\n\t\t\t}\n\t\t\tif (this.deps.isDisposed() || results.length === 0) return;\n\t\t\tthis.deps.getSessionManager().appendCustomEntry(\"brain-curation\", {\n\t\t\t\tversion: 1,\n\t\t\t\tresults: results.map((result) => ({\n\t\t\t\t\tkey: result.key,\n\t\t\t\t\tkind: result.kind,\n\t\t\t\t\tok: result.ok,\n\t\t\t\t\tms: result.ms,\n\t\t\t\t\t...(result.digest !== undefined ? { digest: result.digest } : {}),\n\t\t\t\t\t...(result.relevant !== undefined ? { relevant: result.relevant, confidence: result.confidence } : {}),\n\t\t\t\t})),\n\t\t\t\ttelemetry: this._brainCurator.telemetry(),\n\t\t\t});\n\t\t} catch {\n\t\t\t// curation is a sidecar; it must never disrupt a turn\n\t\t}\n\t}\n\n\t/** Curation status for diagnostics/dashboard: settings, live telemetry, last refusal reason. */\n\tgetContextCurationStatus(): {\n\t\tenabled: boolean;\n\t\tmodel?: string;\n\t\ttelemetry: CurationTelemetrySnapshot;\n\t\tlastSkipReason?: string;\n\t\tlastPreDigestSkipReason?: string;\n\t} {\n\t\tconst settings = this.deps.getSettingsManager().getContextCurationSettings();\n\t\treturn {\n\t\t\tenabled: settings.enabled,\n\t\t\tmodel: settings.model,\n\t\t\ttelemetry: this._brainCurator.telemetry(),\n\t\t\tlastSkipReason: this._lastCurationSkipReason,\n\t\t\tlastPreDigestSkipReason: this._lastPreDigestSkipReason,\n\t\t};\n\t}\n\n\t/** Read-only inspection of the latest prompt-enforcement report, for tests/debugging. */\n\tgetPromptEnforcementReport(): PromptEnforcementReport {\n\t\treturn this._latestPromptEnforcementReport ?? { turnIndex: this.deps.getTurnIndex(), items: [] };\n\t}\n\n\tapplyContextGc(\n\t\tmessages: AgentMessage[],\n\t\twritePayloads: boolean,\n\t): { messages: AgentMessage[]; report: ContextGcReport } {\n\t\ttry {\n\t\t\tconst settings = this.deps.getSettingsManager().getContextGcSettings();\n\t\t\t// Merge the ACTIVE memory providers' own page markers (e.g. transcript-recall's\n\t\t\t// \"<memory_context\") into the semantic-memory marker list. The settings default is\n\t\t\t// provider-agnostic and non-empty, so without this merge the recall pages the bundled\n\t\t\t// default provider actually emits are never recognized as semantic-memory pages and\n\t\t\t// accumulate raw for the life of the session — the exact growth Bug #7 GC exists to stop.\n\t\t\tconst providerMarkers = this.deps.getMemoryManager().getContextMarkers();\n\t\t\tconst curationSettings = this.deps.getSettingsManager().getContextCurationSettings();\n\t\t\tconst result = applyContextGc(messages, {\n\t\t\t\t...settings,\n\t\t\t\tsemanticMemory: {\n\t\t\t\t\t...settings.semanticMemory,\n\t\t\t\t\tmarkers: [...new Set([...settings.semanticMemory.markers, ...providerMarkers])],\n\t\t\t\t},\n\t\t\t\tcwd: this.deps.getCwd(),\n\t\t\t\tstorageDir: this._contextGcStorageDir(),\n\t\t\t\twritePayloads,\n\t\t\t\tcuration: curationSettings.enabled\n\t\t\t\t\t? {\n\t\t\t\t\t\t\tresolveDigest: (digestKey) => {\n\t\t\t\t\t\t\t\tconst digest = this._brainCurator.getDigest(digestKey);\n\t\t\t\t\t\t\t\t// Count serves on the REAL per-turn pass only, never the report path.\n\t\t\t\t\t\t\t\tif (digest !== undefined && writePayloads) this._brainCurator.noteDigestServed();\n\t\t\t\t\t\t\t\treturn digest;\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t// Only the real per-turn pass enqueues work; the read-only report path\n\t\t\t\t\t\t\t// (writePayloads=false) stays side-effect free.\n\t\t\t\t\t\t\tonPacked: writePayloads\n\t\t\t\t\t\t\t\t? (record, originalText) => {\n\t\t\t\t\t\t\t\t\t\tthis._brainCurator.enqueue({\n\t\t\t\t\t\t\t\t\t\t\tkind: \"stub_digest\",\n\t\t\t\t\t\t\t\t\t\t\tkey: record.key ?? record.toolCallId,\n\t\t\t\t\t\t\t\t\t\t\tcontent: originalText,\n\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\t}\n\t\t\t\t\t: undefined,\n\t\t\t});\n\t\t\tthis._latestContextGcReport = result.report;\n\t\t\t// Only release/reclaim on the real per-turn pass (writePayloads=true), never on\n\t\t\t// the read-only status-report path (getContextGcReport with writePayloads=false),\n\t\t\t// so merely inspecting the report can't have side effects.\n\t\t\tif (writePayloads && result.report.packedCount > 0) {\n\t\t\t\tthis._releaseGcPackedArtifactReferences(messages, result.report);\n\t\t\t}\n\t\t\treturn result;\n\t\t} catch {\n\t\t\tconst report: ContextGcReport = {\n\t\t\t\tenabled: false,\n\t\t\t\tpackedCount: 0,\n\t\t\t\toriginalTokens: 0,\n\t\t\t\tpackedTokens: 0,\n\t\t\t\tsavedTokens: 0,\n\t\t\t\trecords: [],\n\t\t\t};\n\t\t\tthis._latestContextGcReport = report;\n\t\t\treturn { messages, report };\n\t\t}\n\t}\n\n\t/**\n\t * Reference-release + cleanup lifecycle: once context-gc has packed a grep/find tool\n\t * result out of the live prompt (the message is no longer current/active working\n\t * context -- see contracts-and-retention.md's \"ephemeral\"/\"expired\" retention\n\t * classes), release the pack-time reference `packToolOutput()` registered for it, and\n\t * opportunistically reclaim now-unreferenced artifacts. This is the other half of the\n\t * D2b-1 gate: artifacts were being registered but never released, so they accumulated\n\t * for the life of the session.\n\t *\n\t * `record.toolCallId` (from context-gc's packed record) is exactly the holder id\n\t * `packToolOutput()` used when it called `addReference()` -- both trace back to the\n\t * same tool call's id -- so no separate bookkeeping is needed to find it.\n\t */\n\tprivate _releaseGcPackedArtifactReferences(messages: AgentMessage[], report: ContextGcReport): void {\n\t\tconst store = this._toolArtifactStore;\n\t\tif (!store) return; // no store was ever constructed, so nothing could have been packed to one\n\n\t\tlet releasedAny = false;\n\t\tfor (const record of report.records) {\n\t\t\tif (record.toolName !== \"grep\" && record.toolName !== \"find\") continue;\n\t\t\tconst artifactId = extractArtifactId(messages[record.messageIndex]);\n\t\t\tif (!artifactId) continue;\n\t\t\tif (store.removeReference(artifactId, record.toolCallId)) releasedAny = true;\n\t\t}\n\t\t// Cleanup only runs immediately after a release actually happened in this pass, so\n\t\t// a long session doesn't re-scan the artifact directory on every turn once nothing\n\t\t// new became eligible for release.\n\t\tif (releasedAny) store.cleanup();\n\t}\n\n\tgetContextGcReport(messages?: AgentMessage[]): ContextGcReport {\n\t\tif (messages) return this.applyContextGc(messages, false).report;\n\t\treturn (\n\t\t\tthis._latestContextGcReport ?? {\n\t\t\t\tenabled: this.deps.getSettingsManager().getContextGcSettings().enabled,\n\t\t\t\tpackedCount: 0,\n\t\t\t\toriginalTokens: 0,\n\t\t\t\tpackedTokens: 0,\n\t\t\t\tsavedTokens: 0,\n\t\t\t\trecords: [],\n\t\t\t}\n\t\t);\n\t}\n\n\testimateCurrentContextTokens(messages: AgentMessage[]): number {\n\t\tconst estimate = estimateContextTokens(messages);\n\t\tconst compactionEntry = getLatestCompactionEntry(this.deps.getSessionManager().getBranch());\n\t\tif (estimate.lastUsageIndex === null || !compactionEntry) {\n\t\t\treturn estimate.tokens;\n\t\t}\n\t\tconst usageMessage = messages[estimate.lastUsageIndex];\n\t\tif (usageMessage?.role !== \"assistant\") {\n\t\t\treturn estimate.tokens;\n\t\t}\n\t\tconst usageTimestamp = (usageMessage as AssistantMessage).timestamp;\n\t\tconst compactionTimestamp = new Date(compactionEntry.timestamp).getTime();\n\t\tif (usageTimestamp <= compactionTimestamp) {\n\t\t\treturn estimate.trailingTokens;\n\t\t}\n\t\treturn estimate.tokens;\n\t}\n}\n"]}
1
+ {"version":3,"file":"context-pipeline.js","sourceRoot":"","sources":["../../src/core/context-pipeline.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Context pipeline: the session's per-turn context-shaping subsystem — the observe-only context\n * audit, the shadow prompt-policy plan and its correlation with legacy context-gc, the enforcement\n * pilot, the relevance-curation queue + the fitness-gated curation model resolver / brain-curation\n * drain + compaction pre-digest, the legacy context-gc pass and its packed-artifact reference\n * release, the tool-output artifact store, and the current-context token estimate.\n *\n * Extracted verbatim from agent-session.ts (god-file decomposition). Owns the latest\n * audit/policy/correlation/enforcement/gc reports, the {@link BrainCurator} sidecar and its last\n * skip reasons, and the lazily-built tool-artifact store. Everything else it needs — the turn\n * index, the session/settings managers, the model registry, agent/workspace dirs, the active tool\n * names, the disposed flag, the isolated-completion primitive, spawned-usage accounting, and the\n * live {@link MemoryManager} — is reached through narrow deps accessors rather than the whole\n * AgentSession.\n *\n * Context-transform boundary (deliberate): the per-turn stages ({@link estimateCurrentContextTokens},\n * {@link runContextAudit}, {@link runPromptPolicyPlanning}, {@link applyContextGc},\n * {@link correlatePromptPolicyWithContextGc}, {@link runPromptEnforcement},\n * {@link enqueueRelevanceCuration}, {@link maybeDrainBrainCuration}) are invoked from the session's\n * context transform as one-line delegations, so the transform stays the single owner of the pass\n * ordering. This controller reaches {@link MemoryController} functionality only through\n * {@link ContextPipelineDeps.getMemoryManager} (never imports it), and MemoryController never imports\n * the pipeline — keeping the transform the one place the two subsystems meet.\n */\n\nimport { join } from \"node:path\";\nimport type { AgentMessage } from \"@caupulican/pi-agent-core\";\nimport {\n\testimateContextTokens,\n\tgetLatestCompactionEntry,\n\ttype SessionManager,\n\tTokenBudget,\n} from \"@caupulican/pi-agent-core/node\";\nimport type { Api, AssistantMessage, Model, Usage } from \"@caupulican/pi-ai\";\nimport type { IsolatedCompletionOptions, IsolatedCompletionResult } from \"./agent-session.ts\";\nimport { BrainCurator, type CurationTelemetrySnapshot, preDigestConversationText } from \"./context/brain-curator.ts\";\nimport { type ArtifactStore, createFileArtifactStore } from \"./context/context-artifacts.ts\";\nimport { type ContextAuditReport, runContextAudit } from \"./context/context-audit.ts\";\nimport { enforcePromptPolicy, type PromptEnforcementReport } from \"./context/context-prompt-enforcement.ts\";\nimport {\n\tcorrelateWithContextGc,\n\ttype PromptPolicyGcCorrelationReport,\n\ttype PromptPolicyShadowReport,\n\tplanPromptPolicy,\n} from \"./context/context-prompt-policy.ts\";\nimport { applyContextGc, type ContextGcReport } from \"./context-gc.ts\";\nimport type { MemoryManager } from \"./memory/memory-manager.ts\";\nimport type { ModelRegistry } from \"./model-registry.ts\";\nimport { resolveCliModel } from \"./model-resolver.ts\";\nimport { evaluateSurfaceFitness } from \"./model-router/fitness-gate.ts\";\nimport { FitnessStore } from \"./models/fitness-store.ts\";\nimport type { SettingsManager } from \"./settings-manager.ts\";\n\n/** Latest user prompt text in the provider-visible array (curation goal line; bounded by caller). */\nexport function latestUserPromptText(messages: AgentMessage[]): string {\n\tfor (let index = messages.length - 1; index >= 0; index--) {\n\t\tconst message = messages[index];\n\t\tif (!message || message.role !== \"user\") continue;\n\t\tif (typeof message.content === \"string\") return message.content;\n\t\tconst text = message.content\n\t\t\t.filter((part): part is { type: \"text\"; text: string } => (part as { type?: string }).type === \"text\")\n\t\t\t.map((part) => part.text)\n\t\t\t.join(\"\\n\");\n\t\tif (text.length > 0) return text;\n\t}\n\treturn \"\";\n}\n\n/** Read a packed grep/find tool result's `details.artifactId`, if present, without `any`. */\nfunction extractArtifactId(message: AgentMessage | undefined): string | undefined {\n\tif (!message || message.role !== \"toolResult\") return undefined;\n\tconst details = (message as { details?: unknown }).details;\n\tif (typeof details !== \"object\" || details === null) return undefined;\n\tconst artifactId = (details as { artifactId?: unknown }).artifactId;\n\treturn typeof artifactId === \"string\" ? artifactId : undefined;\n}\n\nexport interface ContextPipelineDeps {\n\t/** Current turn index, stamped into audit/policy/enforcement reports. */\n\tgetTurnIndex(): number;\n\t/** Session log: audit lookup, gc/artifact storage dirs, curation entries, token-estimate compaction anchor. */\n\tgetSessionManager(): SessionManager;\n\t/** Context-gc / prompt-enforcement / curation settings (all opt-in gates). */\n\tgetSettingsManager(): SettingsManager;\n\t/** Resolves a configured curation model pattern against configured auth. */\n\tgetModelRegistry(): ModelRegistry;\n\t/** Root dir the host-keyed {@link FitnessStore} and per-session gc/artifact storage live under. */\n\tgetAgentDir(): string;\n\t/** Workspace root, passed to the context-gc pass. */\n\tgetCwd(): string;\n\t/** Currently-active tool names — enforcement checks whether artifact_retrieve is a live affordance. */\n\tgetActiveToolNames(): string[];\n\t/** A disposed session must never persist a curation/pre-digest entry. */\n\tisDisposed(): boolean;\n\t/** The live memory manager — the active providers' page markers feed the semantic-gc scan. */\n\tgetMemoryManager(): MemoryManager;\n\t/** Roll a curation drain's spawned usage into session accounting (idempotent per reportId). */\n\taddSpawnedUsage(\n\t\tusage: Usage,\n\t\topts?: { label?: string; sourceSessionId?: string; reportId?: string },\n\t): string | undefined;\n\t/** One-shot LLM call fully isolated from the main session — the curation/pre-digest execution primitive. */\n\trunIsolatedCompletion(opts: IsolatedCompletionOptions): Promise<IsolatedCompletionResult>;\n}\n\nexport class ContextPipeline {\n\tprivate _latestContextGcReport: ContextGcReport | undefined = undefined;\n\t/** Brain-curation sidecar (design: brain-context-curation-design.md). Inert unless the\n\t * contextPolicy.curation setting is enabled AND the model passes the digest fitness gate. */\n\tprivate readonly _brainCurator = new BrainCurator();\n\tprivate _lastCurationSkipReason: string | undefined = undefined;\n\tprivate _lastPreDigestSkipReason: string | undefined = undefined;\n\tprivate _toolArtifactStore: ArtifactStore | undefined = undefined;\n\tprivate _latestContextAuditReport: ContextAuditReport | undefined = undefined;\n\tprivate _latestPromptPolicyReport: PromptPolicyShadowReport | undefined = undefined;\n\tprivate _latestPromptPolicyGcCorrelation: PromptPolicyGcCorrelationReport | undefined = undefined;\n\tprivate _latestPromptEnforcementReport: PromptEnforcementReport | undefined = undefined;\n\tprivate readonly _tokenBudget = new TokenBudget();\n\n\tprivate readonly deps: ContextPipelineDeps;\n\n\tconstructor(deps: ContextPipelineDeps) {\n\t\tthis.deps = deps;\n\t}\n\n\tprivate _contextGcStorageDir(): string {\n\t\treturn join(this.deps.getAgentDir(), \"context-gc\", this.deps.getSessionManager().getSessionId());\n\t}\n\n\tprivate _toolArtifactsDir(): string {\n\t\treturn join(this.deps.getAgentDir(), \"context-artifacts\", this.deps.getSessionManager().getSessionId());\n\t}\n\n\t/**\n\t * Session-scoped, filesystem-backed artifact store for first-capture-then-bound tool\n\t * output (grep/find only, for now -- see tool-output-artifacts.md). Lazily created and\n\t * cached so every tool construction in this session shares one store instance.\n\t *\n\t * `packToolOutput()` registers a reference (the packing tool call's id) at pack time\n\t * and fails closed, so packed artifacts are never prematurely collected.\n\t * `_releaseGcPackedArtifactReferences()` (called from `applyContextGc()`) releases\n\t * that reference once context-gc packs the result out of live context, and\n\t * opportunistically reclaims now-unreferenced artifacts via `cleanup()`.\n\t * Remaining carry-forward gap: cleanup() now also runs at dispose(), but only reclaims\n\t * already-released (zero-reference) artifacts. A session that ends before context-gc\n\t * ever evicts a result never releases that reference, so its artifact stays on disk by\n\t * design (resolvable on resume). Reclaiming those requires an explicit cross-session\n\t * expiry/liveness policy, not just a sweep.\n\t */\n\tgetToolArtifactStore(): ArtifactStore {\n\t\tthis._toolArtifactStore ??= createFileArtifactStore({ baseDir: this._toolArtifactsDir() });\n\t\treturn this._toolArtifactStore;\n\t}\n\n\t/**\n\t * Best-effort final sweep of any already-released (zero-reference) tool-output artifact at\n\t * session dispose. Reads the field (not the getter) so a session that never packed anything\n\t * doesn't force-create a store/dir just to sweep it.\n\t */\n\tcleanupToolArtifactStoreOnDispose(): void {\n\t\tthis._toolArtifactStore?.cleanup();\n\t}\n\n\t/**\n\t * One pass over the current branch, mapping each toolResult's toolCallId to its\n\t * persisted session-entry id. Rebuilt every audit pass (O(branch) per turn), so this is\n\t * O(n^2) over a long session. Fine at current scale; after the artifact-read fix this is\n\t * the next per-turn audit cost to optimize if it ever matters (e.g. cache/incrementally\n\t * update instead of a full rebuild).\n\t */\n\tprivate _buildSessionEntryIdLookup(): (toolCallId: string) => string | undefined {\n\t\tconst map = new Map<string, string>();\n\t\tfor (const entry of this.deps.getSessionManager().getBranch()) {\n\t\t\tif (entry.type === \"message\" && entry.message.role === \"toolResult\") {\n\t\t\t\tmap.set(entry.message.toolCallId, entry.id);\n\t\t\t}\n\t\t}\n\t\treturn (toolCallId: string) => map.get(toolCallId);\n\t}\n\n\t/**\n\t * Phase 1 observe-only audit pass (see context/context-audit.ts): converts live\n\t * toolResult messages into ContextItems and runs the existing retention/hard-constraint\n\t * evaluators over them, storing the latest deterministic report for tests/debugging.\n\t * Read-only with respect to messages, the transcript, and artifact references -- uses\n\t * `_toolArtifactStore` (the field), not `getToolArtifactStore()` (the getter), so a\n\t * session that never packed anything doesn't force-create a store/dir just to audit.\n\t * Never throws into a live turn: any failure degrades to an empty report.\n\t */\n\trunContextAudit(messages: AgentMessage[]): ContextAuditReport {\n\t\ttry {\n\t\t\tconst report = runContextAudit(messages, {\n\t\t\t\tturnIndex: this.deps.getTurnIndex(),\n\t\t\t\tartifactStore: this._toolArtifactStore,\n\t\t\t\tsessionEntryIdForToolCallId: this._buildSessionEntryIdLookup(),\n\t\t\t});\n\t\t\tthis._latestContextAuditReport = report;\n\t\t\treturn report;\n\t\t} catch {\n\t\t\tconst report: ContextAuditReport = { turnIndex: this.deps.getTurnIndex(), items: [] };\n\t\t\tthis._latestContextAuditReport = report;\n\t\t\treturn report;\n\t\t}\n\t}\n\n\t/**\n\t * Read-only inspection of the context audit. With `messages`, recomputes fresh against\n\t * the given array (still no mutation of messages/transcript/artifact refs); without,\n\t * returns the last report computed during a real transform pass.\n\t */\n\tgetContextAuditReport(messages?: AgentMessage[]): ContextAuditReport {\n\t\tif (messages) return this.runContextAudit(messages);\n\t\treturn this._latestContextAuditReport ?? { turnIndex: this.deps.getTurnIndex(), items: [] };\n\t}\n\n\t/**\n\t * Observe-first shadow/planning pass (see context/context-prompt-policy.ts): re-shapes\n\t * the audit report into a per-item policy plan whose `appliedAction` is always\n\t * \"keep_raw\" -- this never enforces anything, it only records what the policy engine\n\t * would say. Never throws into a live turn: any failure degrades to an empty report.\n\t */\n\trunPromptPolicyPlanning(auditReport: ContextAuditReport): PromptPolicyShadowReport {\n\t\ttry {\n\t\t\tconst report = planPromptPolicy(auditReport);\n\t\t\tthis._latestPromptPolicyReport = report;\n\t\t\treturn report;\n\t\t} catch {\n\t\t\tconst report: PromptPolicyShadowReport = { turnIndex: this.deps.getTurnIndex(), items: [] };\n\t\t\tthis._latestPromptPolicyReport = report;\n\t\t\treturn report;\n\t\t}\n\t}\n\n\t/**\n\t * Read-only inspection of the shadow policy plan. With `messages`, recomputes fresh\n\t * (audit + plan) against the given array; without, returns the last plan computed\n\t * during a real transform pass. Never mutates messages/transcript/artifact refs.\n\t */\n\tgetPromptPolicyReport(messages?: AgentMessage[]): PromptPolicyShadowReport {\n\t\tif (messages) return this.runPromptPolicyPlanning(this.runContextAudit(messages));\n\t\treturn this._latestPromptPolicyReport ?? { turnIndex: this.deps.getTurnIndex(), items: [] };\n\t}\n\n\t/**\n\t * Report-only correlation between the shadow plan just computed this turn and what the\n\t * legacy context-gc pass actually packed. Runs after `applyContextGc()` has already\n\t * produced its report; never influences context-gc itself. Never throws into a live\n\t * turn: any failure degrades to an empty correlation.\n\t */\n\tcorrelatePromptPolicyWithContextGc(gcReport: ContextGcReport): void {\n\t\tconst shadowReport = this._latestPromptPolicyReport ?? { turnIndex: this.deps.getTurnIndex(), items: [] };\n\t\ttry {\n\t\t\tthis._latestPromptPolicyGcCorrelation = correlateWithContextGc(shadowReport, gcReport);\n\t\t} catch {\n\t\t\tthis._latestPromptPolicyGcCorrelation = { turnIndex: this.deps.getTurnIndex(), entries: [] };\n\t\t}\n\t}\n\n\t/** Read-only inspection of the latest shadow-plan/legacy-gc correlation, for tests/debugging. */\n\tgetPromptPolicyGcCorrelation(): PromptPolicyGcCorrelationReport {\n\t\treturn this._latestPromptPolicyGcCorrelation ?? { turnIndex: this.deps.getTurnIndex(), entries: [] };\n\t}\n\n\t/**\n\t * First enforcement pilot (see context/context-prompt-enforcement.ts): opt-in,\n\t * default-disabled stub-in-place of stale artifact-backed tool_output results in the\n\t * provider-visible message array only. Runs on `messages` AFTER context-gc has already\n\t * produced its own result, so legacy context-gc's own packing/reporting is completely\n\t * unaffected by this pass -- it only ever acts on messages gc left untouched this turn.\n\t * Never throws into a live turn: any failure degrades to returning `messages` unchanged.\n\t */\n\trunPromptEnforcement(\n\t\tmessages: AgentMessage[],\n\t\tshadowReport: PromptPolicyShadowReport,\n\t): { messages: AgentMessage[]; report: PromptEnforcementReport } {\n\t\ttry {\n\t\t\tconst persistedSettings = this.deps.getSettingsManager().getContextPromptEnforcementSettings();\n\t\t\tconst curationEnabled = this.deps.getSettingsManager().getContextCurationSettings().enabled;\n\t\t\tconst settings = {\n\t\t\t\t...persistedSettings,\n\t\t\t\t// Runtime fact, never assumed: artifact_retrieve is a companion affordance\n\t\t\t\t// (auto-activated alongside grep/find), not a default/global tool, so active\n\t\t\t\t// tools can differ turn to turn -- see context-prompt-enforcement.ts's doc\n\t\t\t\t// comment on why this is checked separately from hasAvailableRetrievalPath.\n\t\t\t\tretrievalToolAvailable: this.deps.getActiveToolNames().includes(\"artifact_retrieve\"),\n\t\t\t\tbrainRelevance: curationEnabled ? (itemId: string) => this._brainCurator.getRelevance(itemId) : undefined,\n\t\t\t};\n\t\t\tconst result = enforcePromptPolicy(messages, shadowReport, settings);\n\t\t\tthis._latestPromptEnforcementReport = result.report;\n\t\t\treturn result;\n\t\t} catch {\n\t\t\tconst report: PromptEnforcementReport = { turnIndex: this.deps.getTurnIndex(), items: [] };\n\t\t\tthis._latestPromptEnforcementReport = report;\n\t\t\treturn { messages, report };\n\t\t}\n\t}\n\n\t/**\n\t * Enqueue relevance-scoring jobs for stale, artifact-backed tool outputs the enforcement\n\t * pilot could act on. Pure queueing — the verdicts only ever take effect through the\n\t * asymmetric advisory lever inside enforcePromptPolicy. Never throws into a turn.\n\t */\n\tenqueueRelevanceCuration(messages: AgentMessage[], shadowReport: PromptPolicyShadowReport): void {\n\t\ttry {\n\t\t\tconst settings = this.deps.getSettingsManager().getContextCurationSettings();\n\t\t\tif (!settings.enabled) return;\n\t\t\tconst goal = latestUserPromptText(messages).slice(0, 400);\n\t\t\tfor (const item of shadowReport.items) {\n\t\t\t\tif (!item.hasAvailableRetrievalPath) continue;\n\t\t\t\tconst message = messages[item.messageIndex];\n\t\t\t\tif (!message || message.role !== \"toolResult\" || message.toolCallId !== item.toolCallId) continue;\n\t\t\t\tif (message.isError) continue;\n\t\t\t\tconst details = message.details as\n\t\t\t\t\t| { contextGc?: { packed?: unknown }; promptPolicy?: { enforced?: unknown } }\n\t\t\t\t\t| undefined;\n\t\t\t\tif (details?.contextGc?.packed === true || details?.promptPolicy?.enforced === true) continue;\n\t\t\t\tconst text = message.content\n\t\t\t\t\t.filter((part): part is { type: \"text\"; text: string } => part.type === \"text\")\n\t\t\t\t\t.map((part) => part.text)\n\t\t\t\t\t.join(\"\\n\");\n\t\t\t\tif (text.length === 0) continue;\n\t\t\t\tthis._brainCurator.enqueue({ kind: \"relevance\", key: item.itemId, content: text.slice(0, 4000), goal });\n\t\t\t}\n\t\t} catch {\n\t\t\t// curation is a sidecar; it must never disrupt a turn\n\t\t}\n\t}\n\n\t/**\n\t * Drain gate: settings on, model configured+authed, and the model has PASSED the digest\n\t * fitness probe on THIS host (design: unfit or unprobed models are refused with a visible\n\t * reason, never silently degraded). Fire-and-forget; never throws into a turn.\n\t */\n\t/**\n\t * Resolve the curation model IFF every gate passes: setting enabled, model configured,\n\t * resolvable+authed, and digest-fitness-proven on THIS host (canonical \"provider/id\" ref —\n\t * runModelFitness stores reports under it, while settings.model may be a bare id or pattern).\n\t * Sets _lastCurationSkipReason on refusal; never throws.\n\t */\n\tresolveCurationModelIfFit(): Model<Api> | undefined {\n\t\tconst settings = this.deps.getSettingsManager().getContextCurationSettings();\n\t\tif (!settings.enabled) {\n\t\t\t// Never surface a stale refusal reason for a feature the user has since disabled.\n\t\t\tthis._lastCurationSkipReason = undefined;\n\t\t\treturn undefined;\n\t\t}\n\t\tif (!settings.model) {\n\t\t\tthis._lastCurationSkipReason = \"curation_model_unset\";\n\t\t\treturn undefined;\n\t\t}\n\t\tconst resolved = resolveCliModel({ cliModel: settings.model, modelRegistry: this.deps.getModelRegistry() });\n\t\tif (!resolved.model || !this.deps.getModelRegistry().hasConfiguredAuth(resolved.model)) {\n\t\t\tthis._lastCurationSkipReason = \"curation_model_unresolved\";\n\t\t\treturn undefined;\n\t\t}\n\t\tconst canonicalRef = `${resolved.model.provider}/${resolved.model.id}`;\n\t\tconst fitness = FitnessStore.forAgentDir(this.deps.getAgentDir())\n\t\t\t.getForHost()\n\t\t\t.find((entry) => entry.model === canonicalRef);\n\t\tconst verdict = evaluateSurfaceFitness(\"curation\", fitness?.report);\n\t\tif (!verdict.fit) {\n\t\t\tthis._lastCurationSkipReason =\n\t\t\t\tverdict.reason === \"unprobed\" ? \"curation_model_unprobed\" : \"curation_model_digest_unfit\";\n\t\t\treturn undefined;\n\t\t}\n\t\tthis._lastCurationSkipReason = undefined;\n\t\treturn resolved.model;\n\t}\n\n\tmaybeDrainBrainCuration(): void {\n\t\ttry {\n\t\t\tif (!this._brainCurator.hasWork() || this._brainCurator.isDraining) return;\n\t\t\tconst model = this.resolveCurationModelIfFit();\n\t\t\tif (!model) return;\n\t\t\tconst settings = this.deps.getSettingsManager().getContextCurationSettings();\n\t\t\tvoid this._drainBrainCuration(model, settings.maxJobsPerTurn);\n\t\t} catch {\n\t\t\t// curation is a sidecar; it must never disrupt a turn\n\t\t}\n\t}\n\n\t/**\n\t * Compaction pre-digest gate (design surface 3): everything the drain gate requires PLUS a\n\t * RUNTIME reliability proof — the curator must have run >=5 jobs on this session with a parse\n\t * failure rate <=5% before it is trusted to pre-digest compaction input. Returns undefined\n\t * (verbatim compaction, byte-for-byte today's behavior) whenever any gate refuses.\n\t */\n\tbuildCompactionPreDigest(): ((text: string, signal?: AbortSignal) => Promise<string>) | undefined {\n\t\ttry {\n\t\t\tconst model = this.resolveCurationModelIfFit();\n\t\t\tif (!model) return undefined;\n\t\t\tconst telemetry = this._brainCurator.telemetry();\n\t\t\tif (telemetry.jobsRun < 5 || telemetry.parseFailures / telemetry.jobsRun > 0.05) {\n\t\t\t\tthis._lastPreDigestSkipReason = \"curation_predigest_reliability_unproven\";\n\t\t\t\treturn undefined;\n\t\t\t}\n\t\t\tthis._lastPreDigestSkipReason = undefined;\n\t\t\treturn async (text, signal) => {\n\t\t\t\tconst result = await preDigestConversationText({\n\t\t\t\t\ttext,\n\t\t\t\t\tsignal,\n\t\t\t\t\tcomplete: async ({ systemPrompt, userPrompt, signal: chunkSignal }) => {\n\t\t\t\t\t\tconst completion = await this.deps.runIsolatedCompletion({\n\t\t\t\t\t\t\tsystemPrompt,\n\t\t\t\t\t\t\tmessages: [{ role: \"user\", content: [{ type: \"text\", text: userPrompt }], timestamp: Date.now() }],\n\t\t\t\t\t\t\tmodel,\n\t\t\t\t\t\t\tthinkingLevel: \"off\",\n\t\t\t\t\t\t\tmaxTokens: 512,\n\t\t\t\t\t\t\tsignal: chunkSignal,\n\t\t\t\t\t\t\tcacheRetention: \"short\",\n\t\t\t\t\t\t});\n\t\t\t\t\t\treturn {\n\t\t\t\t\t\t\ttext: completion.text,\n\t\t\t\t\t\t\tcostUsd: completion.usage.cost.total,\n\t\t\t\t\t\t\tstopReason: String(completion.stopReason),\n\t\t\t\t\t\t};\n\t\t\t\t\t},\n\t\t\t\t});\n\t\t\t\tif (!this.deps.isDisposed() && result.totalChunks > 0) {\n\t\t\t\t\tthis.deps.getSessionManager().appendCustomEntry(\"brain-curation-predigest\", {\n\t\t\t\t\t\tversion: 1,\n\t\t\t\t\t\ttotalChunks: result.totalChunks,\n\t\t\t\t\t\tdigested: result.digested,\n\t\t\t\t\t\tfailed: result.failed,\n\t\t\t\t\t\tcharsBefore: text.length,\n\t\t\t\t\t\tcharsAfter: result.text.length,\n\t\t\t\t\t});\n\t\t\t\t}\n\t\t\t\treturn result.text;\n\t\t\t};\n\t\t} catch {\n\t\t\treturn undefined;\n\t\t}\n\t}\n\n\tprivate async _drainBrainCuration(model: Model<Api>, maxJobs: number): Promise<void> {\n\t\ttry {\n\t\t\t// ACCUMULATE across all drained jobs (the drain runs the completer once PER job) —\n\t\t\t// keeping only the last job's usage would under-report every multi-job drain.\n\t\t\tlet spentUsage: AssistantMessage[\"usage\"] | undefined;\n\t\t\tconst results = await this._brainCurator.drain({\n\t\t\t\tmaxJobs,\n\t\t\t\tcomplete: async ({ systemPrompt, userPrompt, signal }) => {\n\t\t\t\t\tconst completion = await this.deps.runIsolatedCompletion({\n\t\t\t\t\t\tsystemPrompt,\n\t\t\t\t\t\tmessages: [{ role: \"user\", content: [{ type: \"text\", text: userPrompt }], timestamp: Date.now() }],\n\t\t\t\t\t\tmodel,\n\t\t\t\t\t\tthinkingLevel: \"off\",\n\t\t\t\t\t\tmaxTokens: 256,\n\t\t\t\t\t\tsignal,\n\t\t\t\t\t\t// Both curation system prompts are static — the provider can cache the prefix.\n\t\t\t\t\t\tcacheRetention: \"short\",\n\t\t\t\t\t});\n\t\t\t\t\tconst usage = completion.usage;\n\t\t\t\t\tif (!spentUsage) {\n\t\t\t\t\t\tspentUsage = structuredClone(usage);\n\t\t\t\t\t} else {\n\t\t\t\t\t\tspentUsage.input += usage.input;\n\t\t\t\t\t\tspentUsage.output += usage.output;\n\t\t\t\t\t\tspentUsage.cacheRead += usage.cacheRead;\n\t\t\t\t\t\tspentUsage.cacheWrite += usage.cacheWrite;\n\t\t\t\t\t\tspentUsage.totalTokens += usage.totalTokens;\n\t\t\t\t\t\tspentUsage.cost.input += usage.cost.input;\n\t\t\t\t\t\tspentUsage.cost.output += usage.cost.output;\n\t\t\t\t\t\tspentUsage.cost.cacheRead += usage.cost.cacheRead;\n\t\t\t\t\t\tspentUsage.cost.cacheWrite += usage.cost.cacheWrite;\n\t\t\t\t\t\tspentUsage.cost.total += usage.cost.total;\n\t\t\t\t\t}\n\t\t\t\t\treturn {\n\t\t\t\t\t\ttext: completion.text,\n\t\t\t\t\t\tcostUsd: completion.usage.cost.total,\n\t\t\t\t\t\tstopReason: String(completion.stopReason),\n\t\t\t\t\t};\n\t\t\t\t},\n\t\t\t});\n\t\t\t// Honest accounting even for free local models: token visibility is the contract.\n\t\t\tif (spentUsage && (spentUsage.cost.total > 0 || spentUsage.totalTokens > 0)) {\n\t\t\t\tthis.deps.addSpawnedUsage(spentUsage, { label: \"context-curator\" });\n\t\t\t}\n\t\t\tif (this.deps.isDisposed() || results.length === 0) return;\n\t\t\tthis.deps.getSessionManager().appendCustomEntry(\"brain-curation\", {\n\t\t\t\tversion: 1,\n\t\t\t\tresults: results.map((result) => ({\n\t\t\t\t\tkey: result.key,\n\t\t\t\t\tkind: result.kind,\n\t\t\t\t\tok: result.ok,\n\t\t\t\t\tms: result.ms,\n\t\t\t\t\t...(result.digest !== undefined ? { digest: result.digest } : {}),\n\t\t\t\t\t...(result.relevant !== undefined ? { relevant: result.relevant, confidence: result.confidence } : {}),\n\t\t\t\t})),\n\t\t\t\ttelemetry: this._brainCurator.telemetry(),\n\t\t\t});\n\t\t} catch {\n\t\t\t// curation is a sidecar; it must never disrupt a turn\n\t\t}\n\t}\n\n\t/** Curation status for diagnostics/dashboard: settings, live telemetry, last refusal reason. */\n\tgetContextCurationStatus(): {\n\t\tenabled: boolean;\n\t\tmodel?: string;\n\t\ttelemetry: CurationTelemetrySnapshot;\n\t\tlastSkipReason?: string;\n\t\tlastPreDigestSkipReason?: string;\n\t} {\n\t\tconst settings = this.deps.getSettingsManager().getContextCurationSettings();\n\t\treturn {\n\t\t\tenabled: settings.enabled,\n\t\t\tmodel: settings.model,\n\t\t\ttelemetry: this._brainCurator.telemetry(),\n\t\t\tlastSkipReason: this._lastCurationSkipReason,\n\t\t\tlastPreDigestSkipReason: this._lastPreDigestSkipReason,\n\t\t};\n\t}\n\n\t/** Read-only inspection of the latest prompt-enforcement report, for tests/debugging. */\n\tgetPromptEnforcementReport(): PromptEnforcementReport {\n\t\treturn this._latestPromptEnforcementReport ?? { turnIndex: this.deps.getTurnIndex(), items: [] };\n\t}\n\n\tapplyContextGc(\n\t\tmessages: AgentMessage[],\n\t\twritePayloads: boolean,\n\t): { messages: AgentMessage[]; report: ContextGcReport } {\n\t\ttry {\n\t\t\tconst settings = this.deps.getSettingsManager().getContextGcSettings();\n\t\t\t// Merge the ACTIVE memory providers' own page markers (e.g. transcript-recall's\n\t\t\t// \"<memory_context\") into the semantic-memory marker list. The settings default is\n\t\t\t// provider-agnostic and non-empty, so without this merge the recall pages the bundled\n\t\t\t// default provider actually emits are never recognized as semantic-memory pages and\n\t\t\t// accumulate raw for the life of the session — the exact growth Bug #7 GC exists to stop.\n\t\t\tconst providerMarkers = this.deps.getMemoryManager().getContextMarkers();\n\t\t\tconst curationSettings = this.deps.getSettingsManager().getContextCurationSettings();\n\t\t\tconst result = applyContextGc(messages, {\n\t\t\t\t...settings,\n\t\t\t\tsemanticMemory: {\n\t\t\t\t\t...settings.semanticMemory,\n\t\t\t\t\tmarkers: [...new Set([...settings.semanticMemory.markers, ...providerMarkers])],\n\t\t\t\t},\n\t\t\t\tcwd: this.deps.getCwd(),\n\t\t\t\tstorageDir: this._contextGcStorageDir(),\n\t\t\t\twritePayloads,\n\t\t\t\tcuration: curationSettings.enabled\n\t\t\t\t\t? {\n\t\t\t\t\t\t\tresolveDigest: (digestKey) => {\n\t\t\t\t\t\t\t\tconst digest = this._brainCurator.getDigest(digestKey);\n\t\t\t\t\t\t\t\t// Count serves on the REAL per-turn pass only, never the report path.\n\t\t\t\t\t\t\t\tif (digest !== undefined && writePayloads) this._brainCurator.noteDigestServed();\n\t\t\t\t\t\t\t\treturn digest;\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t\t// Only the real per-turn pass enqueues work; the read-only report path\n\t\t\t\t\t\t\t// (writePayloads=false) stays side-effect free.\n\t\t\t\t\t\t\tonPacked: writePayloads\n\t\t\t\t\t\t\t\t? (record, originalText) => {\n\t\t\t\t\t\t\t\t\t\tthis._brainCurator.enqueue({\n\t\t\t\t\t\t\t\t\t\t\tkind: \"stub_digest\",\n\t\t\t\t\t\t\t\t\t\t\tkey: record.key ?? record.toolCallId,\n\t\t\t\t\t\t\t\t\t\t\tcontent: originalText,\n\t\t\t\t\t\t\t\t\t\t});\n\t\t\t\t\t\t\t\t\t}\n\t\t\t\t\t\t\t\t: undefined,\n\t\t\t\t\t\t}\n\t\t\t\t\t: undefined,\n\t\t\t});\n\t\t\tthis._latestContextGcReport = result.report;\n\t\t\t// Only release/reclaim on the real per-turn pass (writePayloads=true), never on\n\t\t\t// the read-only status-report path (getContextGcReport with writePayloads=false),\n\t\t\t// so merely inspecting the report can't have side effects.\n\t\t\tif (writePayloads && result.report.packedCount > 0) {\n\t\t\t\tthis._releaseGcPackedArtifactReferences(messages, result.report);\n\t\t\t}\n\t\t\treturn result;\n\t\t} catch {\n\t\t\tconst report: ContextGcReport = {\n\t\t\t\tenabled: false,\n\t\t\t\tpackedCount: 0,\n\t\t\t\toriginalTokens: 0,\n\t\t\t\tpackedTokens: 0,\n\t\t\t\tsavedTokens: 0,\n\t\t\t\trecords: [],\n\t\t\t};\n\t\t\tthis._latestContextGcReport = report;\n\t\t\treturn { messages, report };\n\t\t}\n\t}\n\n\t/**\n\t * Reference-release + cleanup lifecycle: once context-gc has packed a grep/find tool\n\t * result out of the live prompt (the message is no longer current/active working\n\t * context -- see contracts-and-retention.md's \"ephemeral\"/\"expired\" retention\n\t * classes), release the pack-time reference `packToolOutput()` registered for it, and\n\t * opportunistically reclaim now-unreferenced artifacts. This is the other half of the\n\t * D2b-1 gate: artifacts were being registered but never released, so they accumulated\n\t * for the life of the session.\n\t *\n\t * `record.toolCallId` (from context-gc's packed record) is exactly the holder id\n\t * `packToolOutput()` used when it called `addReference()` -- both trace back to the\n\t * same tool call's id -- so no separate bookkeeping is needed to find it.\n\t */\n\tprivate _releaseGcPackedArtifactReferences(messages: AgentMessage[], report: ContextGcReport): void {\n\t\tconst store = this._toolArtifactStore;\n\t\tif (!store) return; // no store was ever constructed, so nothing could have been packed to one\n\n\t\tlet releasedAny = false;\n\t\tfor (const record of report.records) {\n\t\t\tif (record.toolName !== \"grep\" && record.toolName !== \"find\") continue;\n\t\t\tconst artifactId = extractArtifactId(messages[record.messageIndex]);\n\t\t\tif (!artifactId) continue;\n\t\t\tif (store.removeReference(artifactId, record.toolCallId)) releasedAny = true;\n\t\t}\n\t\t// Cleanup only runs immediately after a release actually happened in this pass, so\n\t\t// a long session doesn't re-scan the artifact directory on every turn once nothing\n\t\t// new became eligible for release.\n\t\tif (releasedAny) store.cleanup();\n\t}\n\n\tgetContextGcReport(messages?: AgentMessage[]): ContextGcReport {\n\t\tif (messages) return this.applyContextGc(messages, false).report;\n\t\treturn (\n\t\t\tthis._latestContextGcReport ?? {\n\t\t\t\tenabled: this.deps.getSettingsManager().getContextGcSettings().enabled,\n\t\t\t\tpackedCount: 0,\n\t\t\t\toriginalTokens: 0,\n\t\t\t\tpackedTokens: 0,\n\t\t\t\tsavedTokens: 0,\n\t\t\t\trecords: [],\n\t\t\t}\n\t\t);\n\t}\n\n\testimateCurrentContextTokens(messages: AgentMessage[]): number {\n\t\tconst estimate = estimateContextTokens(messages);\n\t\tif (estimate.lastUsageIndex === null) {\n\t\t\treturn this._tokenBudget.estimateDelta(estimateConversationChars(messages));\n\t\t}\n\n\t\tconst usageMessage = messages[estimate.lastUsageIndex];\n\t\tconst compactionEntry = getLatestCompactionEntry(this.deps.getSessionManager().getBranch());\n\t\tif (usageMessage?.role !== \"assistant\" || !compactionEntry) {\n\t\t\treturn estimate.tokens;\n\t\t}\n\t\tconst usageTimestamp = (usageMessage as AssistantMessage).timestamp;\n\t\tconst compactionTimestamp = new Date(compactionEntry.timestamp).getTime();\n\t\tif (usageTimestamp <= compactionTimestamp) {\n\t\t\treturn this._tokenBudget.estimateDelta(estimateConversationChars(messages));\n\t\t}\n\n\t\tconst coveredChars = estimateConversationChars(messages, 0, estimate.lastUsageIndex + 1);\n\t\tconst deltaChars = estimateConversationChars(messages, estimate.lastUsageIndex + 1, messages.length);\n\t\tthis._tokenBudget.anchor(estimate.usageTokens, coveredChars);\n\t\treturn this._tokenBudget.current(deltaChars, estimate.tokens);\n\t}\n}\n\n/**\n * Estimate covered characters from the provider-visible context messages.\n */\nfunction estimateConversationChars(messages: AgentMessage[], start = 0, end = messages.length): number {\n\tlet total = 0;\n\tfor (let index = Math.max(0, start); index < Math.min(messages.length, end); index++) {\n\t\tconst message = messages[index];\n\t\tif (!message) continue;\n\t\tconst rawContent = (message as { content?: unknown }).content;\n\t\tif (typeof rawContent === \"string\") {\n\t\t\ttotal += rawContent.length;\n\t\t\tcontinue;\n\t\t}\n\t\tif (!Array.isArray(rawContent)) {\n\t\t\tcontinue;\n\t\t}\n\t\tfor (const block of rawContent) {\n\t\t\tif (typeof block === \"string\") {\n\t\t\t\ttotal += block.length;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (!block || typeof block !== \"object\") {\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\tif (typeof (block as { text?: unknown }).text === \"string\") {\n\t\t\t\ttotal += (block as { text?: string }).text?.length ?? 0;\n\t\t\t}\n\t\t\tif (typeof (block as { thinking?: unknown }).thinking === \"string\") {\n\t\t\t\ttotal += (block as { thinking?: string }).thinking?.length ?? 0;\n\t\t\t}\n\t\t}\n\t}\n\treturn total;\n}\n"]}
@@ -1,4 +1,4 @@
1
1
  import type { ModelRegistry } from "../model-registry.ts";
2
2
  import type { ModelRouterStatusSettings } from "./status.ts";
3
- export declare function collectModelRouterConfigDiagnostics(settings: ModelRouterStatusSettings, modelRegistry: ModelRegistry): string[];
3
+ export declare function collectModelRouterConfigDiagnostics(settings: ModelRouterStatusSettings, modelRegistry: ModelRegistry, agentDir?: string): string[];
4
4
  //# sourceMappingURL=config-diagnostics.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"config-diagnostics.d.ts","sourceRoot":"","sources":["../../../src/core/model-router/config-diagnostics.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAE1D,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AAyB7D,wBAAgB,mCAAmC,CAClD,QAAQ,EAAE,yBAAyB,EACnC,aAAa,EAAE,aAAa,GAC1B,MAAM,EAAE,CAsBV","sourcesContent":["import type { Model } from \"@caupulican/pi-ai\";\nimport type { ModelRegistry } from \"../model-registry.ts\";\nimport { resolveCliModel } from \"../model-resolver.ts\";\nimport type { ModelRouterStatusSettings } from \"./status.ts\";\n\nfunction formatModel(model: Model<any>): string {\n\treturn `${model.provider}/${model.id}`;\n}\n\nfunction collectModelRouterModelDiagnostics(\n\tlabel: \"cheap model\" | \"medium model\" | \"expensive model\",\n\tsettingKey: \"modelRouter.cheapModel\" | \"modelRouter.mediumModel\" | \"modelRouter.expensiveModel\",\n\tmodelPattern: string | undefined,\n\tmodelRegistry: ModelRegistry,\n): string[] {\n\tif (!modelPattern) {\n\t\treturn [`Model router ${label} is unset; configure ${settingKey} or disable modelRouter.enabled.`];\n\t}\n\tconst resolved = resolveCliModel({ cliModel: modelPattern, modelRegistry });\n\tif (!resolved.model) {\n\t\treturn [`Model router ${label} is unresolved: ${modelPattern}.`];\n\t}\n\tif (!modelRegistry.hasConfiguredAuth(resolved.model)) {\n\t\treturn [`Model router ${label} is missing auth: ${formatModel(resolved.model)}.`];\n\t}\n\treturn [];\n}\n\nexport function collectModelRouterConfigDiagnostics(\n\tsettings: ModelRouterStatusSettings,\n\tmodelRegistry: ModelRegistry,\n): string[] {\n\tif (!settings.enabled) return [];\n\treturn [\n\t\t...collectModelRouterModelDiagnostics(\n\t\t\t\"cheap model\",\n\t\t\t\"modelRouter.cheapModel\",\n\t\t\tsettings.cheapModel,\n\t\t\tmodelRegistry,\n\t\t),\n\t\t...collectModelRouterModelDiagnostics(\n\t\t\t\"medium model\",\n\t\t\t\"modelRouter.mediumModel\",\n\t\t\tsettings.mediumModel,\n\t\t\tmodelRegistry,\n\t\t),\n\t\t...collectModelRouterModelDiagnostics(\n\t\t\t\"expensive model\",\n\t\t\t\"modelRouter.expensiveModel\",\n\t\t\tsettings.expensiveModel,\n\t\t\tmodelRegistry,\n\t\t),\n\t];\n}\n"]}
1
+ {"version":3,"file":"config-diagnostics.d.ts","sourceRoot":"","sources":["../../../src/core/model-router/config-diagnostics.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AAI1D,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,aAAa,CAAC;AA+C7D,wBAAgB,mCAAmC,CAClD,QAAQ,EAAE,yBAAyB,EACnC,aAAa,EAAE,aAAa,EAC5B,QAAQ,CAAC,EAAE,MAAM,GACf,MAAM,EAAE,CA+CV","sourcesContent":["import type { Model } from \"@caupulican/pi-ai\";\nimport type { ModelRegistry } from \"../model-registry.ts\";\nimport { resolveCliModel } from \"../model-resolver.ts\";\nimport { FitnessStore } from \"../models/fitness-store.ts\";\nimport { evaluateSurfaceFitness, type FitnessGatedSurface } from \"./fitness-gate.ts\";\nimport type { ModelRouterStatusSettings } from \"./status.ts\";\n\nfunction formatModel(model: Model<any>): string {\n\treturn `${model.provider}/${model.id}`;\n}\n\nfunction collectModelRouterModelDiagnostics(\n\tlabel: \"cheap model\" | \"medium model\" | \"expensive model\",\n\tsettingKey: \"modelRouter.cheapModel\" | \"modelRouter.mediumModel\" | \"modelRouter.expensiveModel\",\n\tmodelPattern: string | undefined,\n\tmodelRegistry: ModelRegistry,\n): string[] {\n\tif (!modelPattern) {\n\t\treturn [`Model router ${label} is unset; configure ${settingKey} or disable modelRouter.enabled.`];\n\t}\n\tconst resolved = resolveCliModel({ cliModel: modelPattern, modelRegistry });\n\tif (!resolved.model) {\n\t\treturn [`Model router ${label} is unresolved: ${modelPattern}.`];\n\t}\n\tif (!modelRegistry.hasConfiguredAuth(resolved.model)) {\n\t\treturn [`Model router ${label} is missing auth: ${formatModel(resolved.model)}.`];\n\t}\n\treturn [];\n}\n\nfunction collectFitnessGateDiagnostic(\n\tlabel: \"cheap model\" | \"medium model\" | \"expensive model\",\n\tsurface: FitnessGatedSurface,\n\tmodelPattern: string | undefined,\n\tmodelRegistry: ModelRegistry,\n\tagentDir: string | undefined,\n): string[] {\n\tif (!agentDir || !modelPattern) return [];\n\tconst resolved = resolveCliModel({ cliModel: modelPattern, modelRegistry });\n\tif (!resolved.model || !modelRegistry.hasConfiguredAuth(resolved.model)) return [];\n\tconst modelRef = formatModel(resolved.model);\n\tconst fitness = FitnessStore.forAgentDir(agentDir)\n\t\t.getForHost()\n\t\t.find((entry) => entry.model === modelRef);\n\tconst verdict = evaluateSurfaceFitness(surface, fitness?.report);\n\tif (verdict.fit) return [];\n\tif (verdict.reason === \"unprobed\") return [`Model router ${label} is unprobed for the fitness gate: ${modelRef}.`];\n\treturn [\n\t\t`Model router ${label} is unfit for the fitness gate: ${modelRef} (${verdict.lane} ${verdict.succeeded}/${verdict.total}).`,\n\t];\n}\n\nexport function collectModelRouterConfigDiagnostics(\n\tsettings: ModelRouterStatusSettings,\n\tmodelRegistry: ModelRegistry,\n\tagentDir?: string,\n): string[] {\n\tif (!settings.enabled) return [];\n\treturn [\n\t\t...collectModelRouterModelDiagnostics(\n\t\t\t\"cheap model\",\n\t\t\t\"modelRouter.cheapModel\",\n\t\t\tsettings.cheapModel,\n\t\t\tmodelRegistry,\n\t\t),\n\t\t...collectModelRouterModelDiagnostics(\n\t\t\t\"medium model\",\n\t\t\t\"modelRouter.mediumModel\",\n\t\t\tsettings.mediumModel,\n\t\t\tmodelRegistry,\n\t\t),\n\t\t...collectModelRouterModelDiagnostics(\n\t\t\t\"expensive model\",\n\t\t\t\"modelRouter.expensiveModel\",\n\t\t\tsettings.expensiveModel,\n\t\t\tmodelRegistry,\n\t\t),\n\t\t...(settings.fitnessGate\n\t\t\t? [\n\t\t\t\t\t...collectFitnessGateDiagnostic(\n\t\t\t\t\t\t\"cheap model\",\n\t\t\t\t\t\t\"router_cheap\",\n\t\t\t\t\t\tsettings.cheapModel,\n\t\t\t\t\t\tmodelRegistry,\n\t\t\t\t\t\tagentDir,\n\t\t\t\t\t),\n\t\t\t\t\t...collectFitnessGateDiagnostic(\n\t\t\t\t\t\t\"medium model\",\n\t\t\t\t\t\t\"router_medium\",\n\t\t\t\t\t\tsettings.mediumModel,\n\t\t\t\t\t\tmodelRegistry,\n\t\t\t\t\t\tagentDir,\n\t\t\t\t\t),\n\t\t\t\t\t...collectFitnessGateDiagnostic(\n\t\t\t\t\t\t\"expensive model\",\n\t\t\t\t\t\t\"router_expensive\",\n\t\t\t\t\t\tsettings.expensiveModel,\n\t\t\t\t\t\tmodelRegistry,\n\t\t\t\t\t\tagentDir,\n\t\t\t\t\t),\n\t\t\t\t]\n\t\t\t: []),\n\t];\n}\n"]}
@@ -1,4 +1,6 @@
1
1
  import { resolveCliModel } from "../model-resolver.js";
2
+ import { FitnessStore } from "../models/fitness-store.js";
3
+ import { evaluateSurfaceFitness } from "./fitness-gate.js";
2
4
  function formatModel(model) {
3
5
  return `${model.provider}/${model.id}`;
4
6
  }
@@ -15,13 +17,39 @@ function collectModelRouterModelDiagnostics(label, settingKey, modelPattern, mod
15
17
  }
16
18
  return [];
17
19
  }
18
- export function collectModelRouterConfigDiagnostics(settings, modelRegistry) {
20
+ function collectFitnessGateDiagnostic(label, surface, modelPattern, modelRegistry, agentDir) {
21
+ if (!agentDir || !modelPattern)
22
+ return [];
23
+ const resolved = resolveCliModel({ cliModel: modelPattern, modelRegistry });
24
+ if (!resolved.model || !modelRegistry.hasConfiguredAuth(resolved.model))
25
+ return [];
26
+ const modelRef = formatModel(resolved.model);
27
+ const fitness = FitnessStore.forAgentDir(agentDir)
28
+ .getForHost()
29
+ .find((entry) => entry.model === modelRef);
30
+ const verdict = evaluateSurfaceFitness(surface, fitness?.report);
31
+ if (verdict.fit)
32
+ return [];
33
+ if (verdict.reason === "unprobed")
34
+ return [`Model router ${label} is unprobed for the fitness gate: ${modelRef}.`];
35
+ return [
36
+ `Model router ${label} is unfit for the fitness gate: ${modelRef} (${verdict.lane} ${verdict.succeeded}/${verdict.total}).`,
37
+ ];
38
+ }
39
+ export function collectModelRouterConfigDiagnostics(settings, modelRegistry, agentDir) {
19
40
  if (!settings.enabled)
20
41
  return [];
21
42
  return [
22
43
  ...collectModelRouterModelDiagnostics("cheap model", "modelRouter.cheapModel", settings.cheapModel, modelRegistry),
23
44
  ...collectModelRouterModelDiagnostics("medium model", "modelRouter.mediumModel", settings.mediumModel, modelRegistry),
24
45
  ...collectModelRouterModelDiagnostics("expensive model", "modelRouter.expensiveModel", settings.expensiveModel, modelRegistry),
46
+ ...(settings.fitnessGate
47
+ ? [
48
+ ...collectFitnessGateDiagnostic("cheap model", "router_cheap", settings.cheapModel, modelRegistry, agentDir),
49
+ ...collectFitnessGateDiagnostic("medium model", "router_medium", settings.mediumModel, modelRegistry, agentDir),
50
+ ...collectFitnessGateDiagnostic("expensive model", "router_expensive", settings.expensiveModel, modelRegistry, agentDir),
51
+ ]
52
+ : []),
25
53
  ];
26
54
  }
27
55
  //# sourceMappingURL=config-diagnostics.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"config-diagnostics.js","sourceRoot":"","sources":["../../../src/core/model-router/config-diagnostics.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,eAAe,EAAE,MAAM,sBAAsB,CAAC;AAGvD,SAAS,WAAW,CAAC,KAAiB,EAAU;IAC/C,OAAO,GAAG,KAAK,CAAC,QAAQ,IAAI,KAAK,CAAC,EAAE,EAAE,CAAC;AAAA,CACvC;AAED,SAAS,kCAAkC,CAC1C,KAAyD,EACzD,UAA+F,EAC/F,YAAgC,EAChC,aAA4B,EACjB;IACX,IAAI,CAAC,YAAY,EAAE,CAAC;QACnB,OAAO,CAAC,gBAAgB,KAAK,wBAAwB,UAAU,kCAAkC,CAAC,CAAC;IACpG,CAAC;IACD,MAAM,QAAQ,GAAG,eAAe,CAAC,EAAE,QAAQ,EAAE,YAAY,EAAE,aAAa,EAAE,CAAC,CAAC;IAC5E,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;QACrB,OAAO,CAAC,gBAAgB,KAAK,mBAAmB,YAAY,GAAG,CAAC,CAAC;IAClE,CAAC;IACD,IAAI,CAAC,aAAa,CAAC,iBAAiB,CAAC,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;QACtD,OAAO,CAAC,gBAAgB,KAAK,qBAAqB,WAAW,CAAC,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC;IACnF,CAAC;IACD,OAAO,EAAE,CAAC;AAAA,CACV;AAED,MAAM,UAAU,mCAAmC,CAClD,QAAmC,EACnC,aAA4B,EACjB;IACX,IAAI,CAAC,QAAQ,CAAC,OAAO;QAAE,OAAO,EAAE,CAAC;IACjC,OAAO;QACN,GAAG,kCAAkC,CACpC,aAAa,EACb,wBAAwB,EACxB,QAAQ,CAAC,UAAU,EACnB,aAAa,CACb;QACD,GAAG,kCAAkC,CACpC,cAAc,EACd,yBAAyB,EACzB,QAAQ,CAAC,WAAW,EACpB,aAAa,CACb;QACD,GAAG,kCAAkC,CACpC,iBAAiB,EACjB,4BAA4B,EAC5B,QAAQ,CAAC,cAAc,EACvB,aAAa,CACb;KACD,CAAC;AAAA,CACF","sourcesContent":["import type { Model } from \"@caupulican/pi-ai\";\nimport type { ModelRegistry } from \"../model-registry.ts\";\nimport { resolveCliModel } from \"../model-resolver.ts\";\nimport type { ModelRouterStatusSettings } from \"./status.ts\";\n\nfunction formatModel(model: Model<any>): string {\n\treturn `${model.provider}/${model.id}`;\n}\n\nfunction collectModelRouterModelDiagnostics(\n\tlabel: \"cheap model\" | \"medium model\" | \"expensive model\",\n\tsettingKey: \"modelRouter.cheapModel\" | \"modelRouter.mediumModel\" | \"modelRouter.expensiveModel\",\n\tmodelPattern: string | undefined,\n\tmodelRegistry: ModelRegistry,\n): string[] {\n\tif (!modelPattern) {\n\t\treturn [`Model router ${label} is unset; configure ${settingKey} or disable modelRouter.enabled.`];\n\t}\n\tconst resolved = resolveCliModel({ cliModel: modelPattern, modelRegistry });\n\tif (!resolved.model) {\n\t\treturn [`Model router ${label} is unresolved: ${modelPattern}.`];\n\t}\n\tif (!modelRegistry.hasConfiguredAuth(resolved.model)) {\n\t\treturn [`Model router ${label} is missing auth: ${formatModel(resolved.model)}.`];\n\t}\n\treturn [];\n}\n\nexport function collectModelRouterConfigDiagnostics(\n\tsettings: ModelRouterStatusSettings,\n\tmodelRegistry: ModelRegistry,\n): string[] {\n\tif (!settings.enabled) return [];\n\treturn [\n\t\t...collectModelRouterModelDiagnostics(\n\t\t\t\"cheap model\",\n\t\t\t\"modelRouter.cheapModel\",\n\t\t\tsettings.cheapModel,\n\t\t\tmodelRegistry,\n\t\t),\n\t\t...collectModelRouterModelDiagnostics(\n\t\t\t\"medium model\",\n\t\t\t\"modelRouter.mediumModel\",\n\t\t\tsettings.mediumModel,\n\t\t\tmodelRegistry,\n\t\t),\n\t\t...collectModelRouterModelDiagnostics(\n\t\t\t\"expensive model\",\n\t\t\t\"modelRouter.expensiveModel\",\n\t\t\tsettings.expensiveModel,\n\t\t\tmodelRegistry,\n\t\t),\n\t];\n}\n"]}
1
+ {"version":3,"file":"config-diagnostics.js","sourceRoot":"","sources":["../../../src/core/model-router/config-diagnostics.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Model } from \"@caupulican/pi-ai\";\nimport type { ModelRegistry } from \"../model-registry.ts\";\nimport { resolveCliModel } from \"../model-resolver.ts\";\nimport { FitnessStore } from \"../models/fitness-store.ts\";\nimport { evaluateSurfaceFitness, type FitnessGatedSurface } from \"./fitness-gate.ts\";\nimport type { ModelRouterStatusSettings } from \"./status.ts\";\n\nfunction formatModel(model: Model<any>): string {\n\treturn `${model.provider}/${model.id}`;\n}\n\nfunction collectModelRouterModelDiagnostics(\n\tlabel: \"cheap model\" | \"medium model\" | \"expensive model\",\n\tsettingKey: \"modelRouter.cheapModel\" | \"modelRouter.mediumModel\" | \"modelRouter.expensiveModel\",\n\tmodelPattern: string | undefined,\n\tmodelRegistry: ModelRegistry,\n): string[] {\n\tif (!modelPattern) {\n\t\treturn [`Model router ${label} is unset; configure ${settingKey} or disable modelRouter.enabled.`];\n\t}\n\tconst resolved = resolveCliModel({ cliModel: modelPattern, modelRegistry });\n\tif (!resolved.model) {\n\t\treturn [`Model router ${label} is unresolved: ${modelPattern}.`];\n\t}\n\tif (!modelRegistry.hasConfiguredAuth(resolved.model)) {\n\t\treturn [`Model router ${label} is missing auth: ${formatModel(resolved.model)}.`];\n\t}\n\treturn [];\n}\n\nfunction collectFitnessGateDiagnostic(\n\tlabel: \"cheap model\" | \"medium model\" | \"expensive model\",\n\tsurface: FitnessGatedSurface,\n\tmodelPattern: string | undefined,\n\tmodelRegistry: ModelRegistry,\n\tagentDir: string | undefined,\n): string[] {\n\tif (!agentDir || !modelPattern) return [];\n\tconst resolved = resolveCliModel({ cliModel: modelPattern, modelRegistry });\n\tif (!resolved.model || !modelRegistry.hasConfiguredAuth(resolved.model)) return [];\n\tconst modelRef = formatModel(resolved.model);\n\tconst fitness = FitnessStore.forAgentDir(agentDir)\n\t\t.getForHost()\n\t\t.find((entry) => entry.model === modelRef);\n\tconst verdict = evaluateSurfaceFitness(surface, fitness?.report);\n\tif (verdict.fit) return [];\n\tif (verdict.reason === \"unprobed\") return [`Model router ${label} is unprobed for the fitness gate: ${modelRef}.`];\n\treturn [\n\t\t`Model router ${label} is unfit for the fitness gate: ${modelRef} (${verdict.lane} ${verdict.succeeded}/${verdict.total}).`,\n\t];\n}\n\nexport function collectModelRouterConfigDiagnostics(\n\tsettings: ModelRouterStatusSettings,\n\tmodelRegistry: ModelRegistry,\n\tagentDir?: string,\n): string[] {\n\tif (!settings.enabled) return [];\n\treturn [\n\t\t...collectModelRouterModelDiagnostics(\n\t\t\t\"cheap model\",\n\t\t\t\"modelRouter.cheapModel\",\n\t\t\tsettings.cheapModel,\n\t\t\tmodelRegistry,\n\t\t),\n\t\t...collectModelRouterModelDiagnostics(\n\t\t\t\"medium model\",\n\t\t\t\"modelRouter.mediumModel\",\n\t\t\tsettings.mediumModel,\n\t\t\tmodelRegistry,\n\t\t),\n\t\t...collectModelRouterModelDiagnostics(\n\t\t\t\"expensive model\",\n\t\t\t\"modelRouter.expensiveModel\",\n\t\t\tsettings.expensiveModel,\n\t\t\tmodelRegistry,\n\t\t),\n\t\t...(settings.fitnessGate\n\t\t\t? [\n\t\t\t\t\t...collectFitnessGateDiagnostic(\n\t\t\t\t\t\t\"cheap model\",\n\t\t\t\t\t\t\"router_cheap\",\n\t\t\t\t\t\tsettings.cheapModel,\n\t\t\t\t\t\tmodelRegistry,\n\t\t\t\t\t\tagentDir,\n\t\t\t\t\t),\n\t\t\t\t\t...collectFitnessGateDiagnostic(\n\t\t\t\t\t\t\"medium model\",\n\t\t\t\t\t\t\"router_medium\",\n\t\t\t\t\t\tsettings.mediumModel,\n\t\t\t\t\t\tmodelRegistry,\n\t\t\t\t\t\tagentDir,\n\t\t\t\t\t),\n\t\t\t\t\t...collectFitnessGateDiagnostic(\n\t\t\t\t\t\t\"expensive model\",\n\t\t\t\t\t\t\"router_expensive\",\n\t\t\t\t\t\tsettings.expensiveModel,\n\t\t\t\t\t\tmodelRegistry,\n\t\t\t\t\t\tagentDir,\n\t\t\t\t\t),\n\t\t\t\t]\n\t\t\t: []),\n\t];\n}\n"]}
@@ -0,0 +1,23 @@
1
+ import type { ModelFitnessReport } from "../research/model-fitness.ts";
2
+ /**
3
+ * Fitness applicability doctrine: Class A autonomous adoption requires proof; Class B routed turns
4
+ * are subtractive-only and pass unprobed models. Class C explicit user choices are deliberately not
5
+ * selection-gated here. The `search` lane is probed and reported but has no selection-time consumer yet.
6
+ */
7
+ export type FitnessGatedSurface = "router_cheap" | "router_medium" | "router_expensive" | "router_judge" | "executor" | "curation" | "scout_auto";
8
+ export type FitnessGateVerdict = {
9
+ fit: true;
10
+ probed: boolean;
11
+ } | {
12
+ fit: false;
13
+ reason: "unprobed";
14
+ } | {
15
+ fit: false;
16
+ reason: "lane_failed";
17
+ lane: string;
18
+ succeeded: number;
19
+ total: number;
20
+ };
21
+ export declare function laneMeetsFitnessBar(succeeded: number, total: number): boolean;
22
+ export declare function evaluateSurfaceFitness(surface: FitnessGatedSurface, report: ModelFitnessReport | undefined): FitnessGateVerdict;
23
+ //# sourceMappingURL=fitness-gate.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fitness-gate.d.ts","sourceRoot":"","sources":["../../../src/core/model-router/fitness-gate.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,8BAA8B,CAAC;AAEvE;;;;GAIG;AACH,MAAM,MAAM,mBAAmB,GAC5B,cAAc,GACd,eAAe,GACf,kBAAkB,GAClB,cAAc,GACd,UAAU,GACV,UAAU,GACV,YAAY,CAAC;AAEhB,MAAM,MAAM,kBAAkB,GAC3B;IAAE,GAAG,EAAE,IAAI,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,GAC9B;IAAE,GAAG,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,UAAU,CAAA;CAAE,GAClC;IAAE,GAAG,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,aAAa,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAgBzF,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAE7E;AAED,wBAAgB,sBAAsB,CACrC,OAAO,EAAE,mBAAmB,EAC5B,MAAM,EAAE,kBAAkB,GAAG,SAAS,GACpC,kBAAkB,CAmBpB","sourcesContent":["import type { ModelFitnessReport } from \"../research/model-fitness.ts\";\n\n/**\n * Fitness applicability doctrine: Class A autonomous adoption requires proof; Class B routed turns\n * are subtractive-only and pass unprobed models. Class C explicit user choices are deliberately not\n * selection-gated here. The `search` lane is probed and reported but has no selection-time consumer yet.\n */\nexport type FitnessGatedSurface =\n\t| \"router_cheap\"\n\t| \"router_medium\"\n\t| \"router_expensive\"\n\t| \"router_judge\"\n\t| \"executor\"\n\t| \"curation\"\n\t| \"scout_auto\";\n\nexport type FitnessGateVerdict =\n\t| { fit: true; probed: boolean }\n\t| { fit: false; reason: \"unprobed\" }\n\t| { fit: false; reason: \"lane_failed\"; lane: string; succeeded: number; total: number };\n\ntype FitnessLane = \"research\" | \"worker\" | \"judge\" | \"toolCall\" | \"digest\";\n\nconst CLASS_A_SURFACES = new Set<FitnessGatedSurface>([\"executor\", \"curation\", \"scout_auto\"]);\n\nconst SURFACE_LANES: Record<FitnessGatedSurface, readonly FitnessLane[]> = {\n\trouter_cheap: [\"research\", \"toolCall\"],\n\trouter_medium: [\"worker\", \"toolCall\"],\n\trouter_expensive: [\"worker\", \"toolCall\"],\n\trouter_judge: [\"judge\"],\n\texecutor: [\"toolCall\"],\n\tcuration: [\"digest\"],\n\tscout_auto: [\"research\", \"toolCall\"],\n};\n\nexport function laneMeetsFitnessBar(succeeded: number, total: number): boolean {\n\treturn total > 0 && succeeded >= Math.ceil(total * (2 / 3));\n}\n\nexport function evaluateSurfaceFitness(\n\tsurface: FitnessGatedSurface,\n\treport: ModelFitnessReport | undefined,\n): FitnessGateVerdict {\n\tif (!report) {\n\t\treturn CLASS_A_SURFACES.has(surface) ? { fit: false, reason: \"unprobed\" } : { fit: true, probed: false };\n\t}\n\n\tfor (const lane of SURFACE_LANES[surface]) {\n\t\tif (lane === \"judge\") {\n\t\t\tconst score = report.judge;\n\t\t\tif (!laneMeetsFitnessBar(score.parsed, score.total)) {\n\t\t\t\treturn { fit: false, reason: \"lane_failed\", lane, succeeded: score.parsed, total: score.total };\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tconst score = report[lane];\n\t\tif (!laneMeetsFitnessBar(score.succeeded, score.total)) {\n\t\t\treturn { fit: false, reason: \"lane_failed\", lane, succeeded: score.succeeded, total: score.total };\n\t\t}\n\t}\n\treturn { fit: true, probed: true };\n}\n"]}
@@ -0,0 +1,33 @@
1
+ const CLASS_A_SURFACES = new Set(["executor", "curation", "scout_auto"]);
2
+ const SURFACE_LANES = {
3
+ router_cheap: ["research", "toolCall"],
4
+ router_medium: ["worker", "toolCall"],
5
+ router_expensive: ["worker", "toolCall"],
6
+ router_judge: ["judge"],
7
+ executor: ["toolCall"],
8
+ curation: ["digest"],
9
+ scout_auto: ["research", "toolCall"],
10
+ };
11
+ export function laneMeetsFitnessBar(succeeded, total) {
12
+ return total > 0 && succeeded >= Math.ceil(total * (2 / 3));
13
+ }
14
+ export function evaluateSurfaceFitness(surface, report) {
15
+ if (!report) {
16
+ return CLASS_A_SURFACES.has(surface) ? { fit: false, reason: "unprobed" } : { fit: true, probed: false };
17
+ }
18
+ for (const lane of SURFACE_LANES[surface]) {
19
+ if (lane === "judge") {
20
+ const score = report.judge;
21
+ if (!laneMeetsFitnessBar(score.parsed, score.total)) {
22
+ return { fit: false, reason: "lane_failed", lane, succeeded: score.parsed, total: score.total };
23
+ }
24
+ continue;
25
+ }
26
+ const score = report[lane];
27
+ if (!laneMeetsFitnessBar(score.succeeded, score.total)) {
28
+ return { fit: false, reason: "lane_failed", lane, succeeded: score.succeeded, total: score.total };
29
+ }
30
+ }
31
+ return { fit: true, probed: true };
32
+ }
33
+ //# sourceMappingURL=fitness-gate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fitness-gate.js","sourceRoot":"","sources":["../../../src/core/model-router/fitness-gate.ts"],"names":[],"mappings":"AAuBA,MAAM,gBAAgB,GAAG,IAAI,GAAG,CAAsB,CAAC,UAAU,EAAE,UAAU,EAAE,YAAY,CAAC,CAAC,CAAC;AAE9F,MAAM,aAAa,GAAwD;IAC1E,YAAY,EAAE,CAAC,UAAU,EAAE,UAAU,CAAC;IACtC,aAAa,EAAE,CAAC,QAAQ,EAAE,UAAU,CAAC;IACrC,gBAAgB,EAAE,CAAC,QAAQ,EAAE,UAAU,CAAC;IACxC,YAAY,EAAE,CAAC,OAAO,CAAC;IACvB,QAAQ,EAAE,CAAC,UAAU,CAAC;IACtB,QAAQ,EAAE,CAAC,QAAQ,CAAC;IACpB,UAAU,EAAE,CAAC,UAAU,EAAE,UAAU,CAAC;CACpC,CAAC;AAEF,MAAM,UAAU,mBAAmB,CAAC,SAAiB,EAAE,KAAa,EAAW;IAC9E,OAAO,KAAK,GAAG,CAAC,IAAI,SAAS,IAAI,IAAI,CAAC,IAAI,CAAC,KAAK,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;AAAA,CAC5D;AAED,MAAM,UAAU,sBAAsB,CACrC,OAA4B,EAC5B,MAAsC,EACjB;IACrB,IAAI,CAAC,MAAM,EAAE,CAAC;QACb,OAAO,gBAAgB,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,CAAC;IAC1G,CAAC;IAED,KAAK,MAAM,IAAI,IAAI,aAAa,CAAC,OAAO,CAAC,EAAE,CAAC;QAC3C,IAAI,IAAI,KAAK,OAAO,EAAE,CAAC;YACtB,MAAM,KAAK,GAAG,MAAM,CAAC,KAAK,CAAC;YAC3B,IAAI,CAAC,mBAAmB,CAAC,KAAK,CAAC,MAAM,EAAE,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;gBACrD,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,CAAC,MAAM,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC;YACjG,CAAC;YACD,SAAS;QACV,CAAC;QACD,MAAM,KAAK,GAAG,MAAM,CAAC,IAAI,CAAC,CAAC;QAC3B,IAAI,CAAC,mBAAmB,CAAC,KAAK,CAAC,SAAS,EAAE,KAAK,CAAC,KAAK,CAAC,EAAE,CAAC;YACxD,OAAO,EAAE,GAAG,EAAE,KAAK,EAAE,MAAM,EAAE,aAAa,EAAE,IAAI,EAAE,SAAS,EAAE,KAAK,CAAC,SAAS,EAAE,KAAK,EAAE,KAAK,CAAC,KAAK,EAAE,CAAC;QACpG,CAAC;IACF,CAAC;IACD,OAAO,EAAE,GAAG,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,CAAC;AAAA,CACnC","sourcesContent":["import type { ModelFitnessReport } from \"../research/model-fitness.ts\";\n\n/**\n * Fitness applicability doctrine: Class A autonomous adoption requires proof; Class B routed turns\n * are subtractive-only and pass unprobed models. Class C explicit user choices are deliberately not\n * selection-gated here. The `search` lane is probed and reported but has no selection-time consumer yet.\n */\nexport type FitnessGatedSurface =\n\t| \"router_cheap\"\n\t| \"router_medium\"\n\t| \"router_expensive\"\n\t| \"router_judge\"\n\t| \"executor\"\n\t| \"curation\"\n\t| \"scout_auto\";\n\nexport type FitnessGateVerdict =\n\t| { fit: true; probed: boolean }\n\t| { fit: false; reason: \"unprobed\" }\n\t| { fit: false; reason: \"lane_failed\"; lane: string; succeeded: number; total: number };\n\ntype FitnessLane = \"research\" | \"worker\" | \"judge\" | \"toolCall\" | \"digest\";\n\nconst CLASS_A_SURFACES = new Set<FitnessGatedSurface>([\"executor\", \"curation\", \"scout_auto\"]);\n\nconst SURFACE_LANES: Record<FitnessGatedSurface, readonly FitnessLane[]> = {\n\trouter_cheap: [\"research\", \"toolCall\"],\n\trouter_medium: [\"worker\", \"toolCall\"],\n\trouter_expensive: [\"worker\", \"toolCall\"],\n\trouter_judge: [\"judge\"],\n\texecutor: [\"toolCall\"],\n\tcuration: [\"digest\"],\n\tscout_auto: [\"research\", \"toolCall\"],\n};\n\nexport function laneMeetsFitnessBar(succeeded: number, total: number): boolean {\n\treturn total > 0 && succeeded >= Math.ceil(total * (2 / 3));\n}\n\nexport function evaluateSurfaceFitness(\n\tsurface: FitnessGatedSurface,\n\treport: ModelFitnessReport | undefined,\n): FitnessGateVerdict {\n\tif (!report) {\n\t\treturn CLASS_A_SURFACES.has(surface) ? { fit: false, reason: \"unprobed\" } : { fit: true, probed: false };\n\t}\n\n\tfor (const lane of SURFACE_LANES[surface]) {\n\t\tif (lane === \"judge\") {\n\t\t\tconst score = report.judge;\n\t\t\tif (!laneMeetsFitnessBar(score.parsed, score.total)) {\n\t\t\t\treturn { fit: false, reason: \"lane_failed\", lane, succeeded: score.parsed, total: score.total };\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tconst score = report[lane];\n\t\tif (!laneMeetsFitnessBar(score.succeeded, score.total)) {\n\t\t\treturn { fit: false, reason: \"lane_failed\", lane, succeeded: score.succeeded, total: score.total };\n\t\t}\n\t}\n\treturn { fit: true, probed: true };\n}\n"]}
@@ -1,13 +1,37 @@
1
+ import type { ThinkingLevel } from "@caupulican/pi-agent-core";
1
2
  import type { SessionEntry } from "@caupulican/pi-agent-core/node";
2
3
  import type { RouteDecision } from "../autonomy/contracts.ts";
3
4
  import type { ModelRouterIntent } from "./intent-classifier.ts";
4
5
  export declare const MODEL_ROUTER_DECISION_CUSTOM_TYPE = "model_router_decision";
6
+ export type ModelRouterTierFitnessStatus = {
7
+ status: "fit";
8
+ } | {
9
+ status: "unprobed";
10
+ } | {
11
+ status: "unfit";
12
+ lane: string;
13
+ succeeded: number;
14
+ total: number;
15
+ };
16
+ export type ModelRouterFitnessStatuses = Partial<Record<"cheap" | "medium" | "expensive", ModelRouterTierFitnessStatus>>;
17
+ export type ModelRouterFailoverStatus = {
18
+ exhausted: string[];
19
+ lastNotice?: string;
20
+ };
5
21
  export type ModelRouterStatusSettings = {
6
22
  enabled: boolean;
23
+ fitnessGate?: boolean;
7
24
  cheapModel?: string;
25
+ cheapThinking?: ThinkingLevel;
8
26
  mediumModel?: string;
27
+ mediumThinking?: ThinkingLevel;
9
28
  expensiveModel?: string;
29
+ expensiveThinking?: ThinkingLevel;
10
30
  learningModel?: string;
31
+ executorModel?: string;
32
+ executorThinking?: ThinkingLevel;
33
+ judgeModel?: string;
34
+ judgeThinking?: ThinkingLevel;
11
35
  };
12
36
  export type ModelRouterDecisionStatus = {
13
37
  route: RouteDecision;
@@ -17,5 +41,5 @@ export type ModelRouterDecisionStatus = {
17
41
  intent?: ModelRouterIntent;
18
42
  };
19
43
  export declare function getRecentModelRouterDecisions(entries: SessionEntry[], limit?: number): ModelRouterDecisionStatus[];
20
- export declare function formatModelRouterStatus(settings: ModelRouterStatusSettings, lastDecision?: ModelRouterDecisionStatus, formatLabel?: (label: string) => string, recentDecisions?: ModelRouterDecisionStatus[], lastSkipReason?: string, latestIntent?: ModelRouterIntent): string;
44
+ export declare function formatModelRouterStatus(settings: ModelRouterStatusSettings, lastDecision?: ModelRouterDecisionStatus, formatLabel?: (label: string) => string, recentDecisions?: ModelRouterDecisionStatus[], lastSkipReason?: string, latestIntent?: ModelRouterIntent, fitnessStatuses?: ModelRouterFitnessStatuses, failoverStatus?: ModelRouterFailoverStatus): string;
21
45
  //# sourceMappingURL=status.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../../src/core/model-router/status.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,KAAK,EAAa,aAAa,EAAa,MAAM,0BAA0B,CAAC;AACpF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAEhE,eAAO,MAAM,iCAAiC,0BAA0B,CAAC;AAEzE,MAAM,MAAM,yBAAyB,GAAG;IACvC,OAAO,EAAE,OAAO,CAAC;IACjB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,aAAa,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IACvC,KAAK,EAAE,aAAa,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,QAAQ,GAAG,WAAW,GAAG,QAAQ,CAAC;IAC3C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,iBAAiB,CAAC;CAC3B,CAAC;AAiDF,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,YAAY,EAAE,EAAE,KAAK,SAAI,GAAG,yBAAyB,EAAE,CAQ7G;AAED,wBAAgB,uBAAuB,CACtC,QAAQ,EAAE,yBAAyB,EACnC,YAAY,CAAC,EAAE,yBAAyB,EACxC,WAAW,GAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAyB,EACzD,eAAe,GAAE,yBAAyB,EAAO,EACjD,cAAc,CAAC,EAAE,MAAM,EACvB,YAAY,CAAC,EAAE,iBAAiB,GAC9B,MAAM,CA+BR","sourcesContent":["import type { SessionEntry } from \"@caupulican/pi-agent-core/node\";\nimport type { ModelTier, RouteDecision, RouteRisk } from \"../autonomy/contracts.ts\";\nimport type { ModelRouterIntent } from \"./intent-classifier.ts\";\n\nexport const MODEL_ROUTER_DECISION_CUSTOM_TYPE = \"model_router_decision\";\n\nexport type ModelRouterStatusSettings = {\n\tenabled: boolean;\n\tcheapModel?: string;\n\tmediumModel?: string;\n\texpensiveModel?: string;\n\tlearningModel?: string;\n};\n\nexport type ModelRouterDecisionStatus = {\n\troute: RouteDecision;\n\troutedModel: string;\n\toutcome: \"routed\" | \"escalated\" | \"failed\";\n\tretryModel?: string;\n\tintent?: ModelRouterIntent;\n};\n\nfunction isRouteDecision(value: unknown): value is RouteDecision {\n\tif (!value || typeof value !== \"object\") return false;\n\tconst route = value as Partial<RouteDecision>;\n\tconst validTiers = new Set<ModelTier>([\"cheap\", \"medium\", \"expensive\", \"learning\"]);\n\tconst validRisks = new Set<RouteRisk>([\"read-only\", \"scoped-write\", \"high-impact\", \"approval-required\"]);\n\n\treturn (\n\t\ttypeof route.confidence === \"number\" &&\n\t\ttypeof route.reasonCode === \"string\" &&\n\t\tArray.isArray(route.reasons) &&\n\t\troute.reasons.every((r) => typeof r === \"string\") &&\n\t\tvalidTiers.has(route.tier as ModelTier) &&\n\t\tvalidRisks.has(route.risk as RouteRisk) &&\n\t\t(route.model === undefined || typeof route.model === \"string\") &&\n\t\t(route.fallbackFrom === undefined ||\n\t\t\troute.fallbackFrom === \"cheap\" ||\n\t\t\troute.fallbackFrom === \"medium\" ||\n\t\t\troute.fallbackFrom === \"expensive\" ||\n\t\t\troute.fallbackFrom === \"learning\") &&\n\t\t(route.createdAt === undefined || typeof route.createdAt === \"string\")\n\t);\n}\n\nfunction isModelRouterDecisionStatus(data: unknown): data is ModelRouterDecisionStatus {\n\tif (!data || typeof data !== \"object\") return false;\n\tconst record = data as Partial<ModelRouterDecisionStatus>;\n\treturn (\n\t\tisRouteDecision(record.route) &&\n\t\trecord.route.tier !== \"learning\" && // Validate user prompt decisions never need learning tier in runtime\n\t\ttypeof record.routedModel === \"string\" &&\n\t\t(record.outcome === \"routed\" || record.outcome === \"escalated\" || record.outcome === \"failed\") &&\n\t\t(record.retryModel === undefined || typeof record.retryModel === \"string\") &&\n\t\t(record.intent === undefined || record.intent === \"research\" || record.intent === \"modify\")\n\t);\n}\n\nfunction formatDecision(decision: ModelRouterDecisionStatus): string {\n\tconst { tier, risk, reasonCode } = decision.route;\n\tlet outcomeText: string = decision.outcome;\n\tif (decision.outcome === \"escalated\" && decision.retryModel) {\n\t\toutcomeText = `escalated -> ${decision.retryModel}`;\n\t} else if (decision.outcome === \"failed\") {\n\t\toutcomeText = \"failed\";\n\t}\n\treturn `${tier}/${risk} -> ${decision.routedModel} (${reasonCode}, ${outcomeText})`;\n}\n\nexport function getRecentModelRouterDecisions(entries: SessionEntry[], limit = 3): ModelRouterDecisionStatus[] {\n\tconst decisions: ModelRouterDecisionStatus[] = [];\n\tfor (const entry of entries) {\n\t\tif (entry.type !== \"custom\" || entry.customType !== MODEL_ROUTER_DECISION_CUSTOM_TYPE) continue;\n\t\tif (!isModelRouterDecisionStatus(entry.data)) continue;\n\t\tdecisions.push(entry.data);\n\t}\n\treturn decisions.slice(-limit);\n}\n\nexport function formatModelRouterStatus(\n\tsettings: ModelRouterStatusSettings,\n\tlastDecision?: ModelRouterDecisionStatus,\n\tformatLabel: (label: string) => string = (label) => label,\n\trecentDecisions: ModelRouterDecisionStatus[] = [],\n\tlastSkipReason?: string,\n\tlatestIntent?: ModelRouterIntent,\n): string {\n\tconst effectiveLastDecision = lastSkipReason ? undefined : lastDecision;\n\tconst lines = [\n\t\t`${formatLabel(\"Status:\")} ${settings.enabled ? \"enabled\" : \"disabled\"}`,\n\t\t`${formatLabel(\"Cheap model:\")} ${settings.cheapModel ?? \"unset\"}`,\n\t\t`${formatLabel(\"Medium model:\")} ${settings.mediumModel ?? \"unset\"}`,\n\t\t`${formatLabel(\"Expensive model:\")} ${settings.expensiveModel ?? \"unset\"}`,\n\t\t`${formatLabel(\"Learning model:\")} ${settings.learningModel ?? \"active\"}`,\n\t];\n\tif (!settings.enabled) {\n\t\tlines.push(`${formatLabel(\"Routing:\")} inactive (disabled)`);\n\t} else if (lastSkipReason) {\n\t\tlines.push(`${formatLabel(\"Routing:\")} skipped (${lastSkipReason})`);\n\t} else if (effectiveLastDecision) {\n\t\tlines.push(`${formatLabel(\"Routing:\")} active`);\n\t} else {\n\t\tlines.push(`${formatLabel(\"Routing:\")} waiting for prompt`);\n\t}\n\tlines.push(`${formatLabel(\"Latest intent:\")} ${latestIntent ?? \"none\"}`);\n\tif (!effectiveLastDecision) {\n\t\tlines.push(`${formatLabel(\"Last decision:\")} none`);\n\t} else {\n\t\tlines.push(`${formatLabel(\"Last decision:\")} ${formatDecision(effectiveLastDecision)}`);\n\t}\n\tif (recentDecisions.length > 0) {\n\t\tlines.push(formatLabel(\"Recent decisions:\"));\n\t\tfor (const decision of recentDecisions) {\n\t\t\tlines.push(`- ${formatDecision(decision)}`);\n\t\t}\n\t}\n\treturn lines.join(\"\\n\");\n}\n"]}
1
+ {"version":3,"file":"status.d.ts","sourceRoot":"","sources":["../../../src/core/model-router/status.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,2BAA2B,CAAC;AAC/D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gCAAgC,CAAC;AACnE,OAAO,KAAK,EAAa,aAAa,EAAa,MAAM,0BAA0B,CAAC;AACpF,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAEhE,eAAO,MAAM,iCAAiC,0BAA0B,CAAC;AAEzE,MAAM,MAAM,4BAA4B,GACrC;IAAE,MAAM,EAAE,KAAK,CAAA;CAAE,GACjB;IAAE,MAAM,EAAE,UAAU,CAAA;CAAE,GACtB;IAAE,MAAM,EAAE,OAAO,CAAC;IAAC,IAAI,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,MAAM,CAAA;CAAE,CAAC;AAEvE,MAAM,MAAM,0BAA0B,GAAG,OAAO,CAC/C,MAAM,CAAC,OAAO,GAAG,QAAQ,GAAG,WAAW,EAAE,4BAA4B,CAAC,CACtE,CAAC;AAEF,MAAM,MAAM,yBAAyB,GAAG;IAAE,SAAS,EAAE,MAAM,EAAE,CAAC;IAAC,UAAU,CAAC,EAAE,MAAM,CAAA;CAAE,CAAC;AAErF,MAAM,MAAM,yBAAyB,GAAG;IACvC,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,cAAc,CAAC,EAAE,aAAa,CAAC;IAC/B,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,iBAAiB,CAAC,EAAE,aAAa,CAAC;IAClC,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,gBAAgB,CAAC,EAAE,aAAa,CAAC;IACjC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,aAAa,CAAC;CAC9B,CAAC;AAwBF,MAAM,MAAM,yBAAyB,GAAG;IACvC,KAAK,EAAE,aAAa,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,QAAQ,GAAG,WAAW,GAAG,QAAQ,CAAC;IAC3C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,MAAM,CAAC,EAAE,iBAAiB,CAAC;CAC3B,CAAC;AAiDF,wBAAgB,6BAA6B,CAAC,OAAO,EAAE,YAAY,EAAE,EAAE,KAAK,SAAI,GAAG,yBAAyB,EAAE,CAQ7G;AAED,wBAAgB,uBAAuB,CACtC,QAAQ,EAAE,yBAAyB,EACnC,YAAY,CAAC,EAAE,yBAAyB,EACxC,WAAW,GAAE,CAAC,KAAK,EAAE,MAAM,KAAK,MAAyB,EACzD,eAAe,GAAE,yBAAyB,EAAO,EACjD,cAAc,CAAC,EAAE,MAAM,EACvB,YAAY,CAAC,EAAE,iBAAiB,EAChC,eAAe,CAAC,EAAE,0BAA0B,EAC5C,cAAc,CAAC,EAAE,yBAAyB,GACxC,MAAM,CAsDR","sourcesContent":["import type { ThinkingLevel } from \"@caupulican/pi-agent-core\";\nimport type { SessionEntry } from \"@caupulican/pi-agent-core/node\";\nimport type { ModelTier, RouteDecision, RouteRisk } from \"../autonomy/contracts.ts\";\nimport type { ModelRouterIntent } from \"./intent-classifier.ts\";\n\nexport const MODEL_ROUTER_DECISION_CUSTOM_TYPE = \"model_router_decision\";\n\nexport type ModelRouterTierFitnessStatus =\n\t| { status: \"fit\" }\n\t| { status: \"unprobed\" }\n\t| { status: \"unfit\"; lane: string; succeeded: number; total: number };\n\nexport type ModelRouterFitnessStatuses = Partial<\n\tRecord<\"cheap\" | \"medium\" | \"expensive\", ModelRouterTierFitnessStatus>\n>;\n\nexport type ModelRouterFailoverStatus = { exhausted: string[]; lastNotice?: string };\n\nexport type ModelRouterStatusSettings = {\n\tenabled: boolean;\n\tfitnessGate?: boolean;\n\tcheapModel?: string;\n\tcheapThinking?: ThinkingLevel;\n\tmediumModel?: string;\n\tmediumThinking?: ThinkingLevel;\n\texpensiveModel?: string;\n\texpensiveThinking?: ThinkingLevel;\n\tlearningModel?: string;\n\texecutorModel?: string;\n\texecutorThinking?: ThinkingLevel;\n\tjudgeModel?: string;\n\tjudgeThinking?: ThinkingLevel;\n};\n\nconst MODEL_ROUTER_INHERIT_THINKING_LABEL = \"(inherit)\";\n\nfunction getThinkingLabel(level: ThinkingLevel | undefined): string {\n\treturn level ?? MODEL_ROUTER_INHERIT_THINKING_LABEL;\n}\n\nfunction formatModelWithThinking(\n\tlabel: string,\n\tmodel: string | undefined,\n\tlevel: ThinkingLevel | undefined,\n\tfitness?: ModelRouterTierFitnessStatus,\n): string {\n\tconst fitnessSuffix = fitness ? ` · fitness ${formatFitness(fitness)}` : \"\";\n\treturn `${label} ${model ?? \"unset\"} · thinking ${getThinkingLabel(level)}${fitnessSuffix}`;\n}\n\nfunction formatFitness(fitness: ModelRouterTierFitnessStatus): string {\n\tif (fitness.status === \"fit\") return \"fit\";\n\tif (fitness.status === \"unprobed\") return \"unprobed\";\n\treturn `UNFIT (${fitness.lane} ${fitness.succeeded}/${fitness.total})`;\n}\n\nexport type ModelRouterDecisionStatus = {\n\troute: RouteDecision;\n\troutedModel: string;\n\toutcome: \"routed\" | \"escalated\" | \"failed\";\n\tretryModel?: string;\n\tintent?: ModelRouterIntent;\n};\n\nfunction isRouteDecision(value: unknown): value is RouteDecision {\n\tif (!value || typeof value !== \"object\") return false;\n\tconst route = value as Partial<RouteDecision>;\n\tconst validTiers = new Set<ModelTier>([\"cheap\", \"medium\", \"expensive\", \"learning\"]);\n\tconst validRisks = new Set<RouteRisk>([\"read-only\", \"scoped-write\", \"high-impact\", \"approval-required\"]);\n\n\treturn (\n\t\ttypeof route.confidence === \"number\" &&\n\t\ttypeof route.reasonCode === \"string\" &&\n\t\tArray.isArray(route.reasons) &&\n\t\troute.reasons.every((r) => typeof r === \"string\") &&\n\t\tvalidTiers.has(route.tier as ModelTier) &&\n\t\tvalidRisks.has(route.risk as RouteRisk) &&\n\t\t(route.model === undefined || typeof route.model === \"string\") &&\n\t\t(route.fallbackFrom === undefined ||\n\t\t\troute.fallbackFrom === \"cheap\" ||\n\t\t\troute.fallbackFrom === \"medium\" ||\n\t\t\troute.fallbackFrom === \"expensive\" ||\n\t\t\troute.fallbackFrom === \"learning\") &&\n\t\t(route.createdAt === undefined || typeof route.createdAt === \"string\")\n\t);\n}\n\nfunction isModelRouterDecisionStatus(data: unknown): data is ModelRouterDecisionStatus {\n\tif (!data || typeof data !== \"object\") return false;\n\tconst record = data as Partial<ModelRouterDecisionStatus>;\n\treturn (\n\t\tisRouteDecision(record.route) &&\n\t\trecord.route.tier !== \"learning\" && // Validate user prompt decisions never need learning tier in runtime\n\t\ttypeof record.routedModel === \"string\" &&\n\t\t(record.outcome === \"routed\" || record.outcome === \"escalated\" || record.outcome === \"failed\") &&\n\t\t(record.retryModel === undefined || typeof record.retryModel === \"string\") &&\n\t\t(record.intent === undefined || record.intent === \"research\" || record.intent === \"modify\")\n\t);\n}\n\nfunction formatDecision(decision: ModelRouterDecisionStatus): string {\n\tconst { tier, risk, reasonCode } = decision.route;\n\tlet outcomeText: string = decision.outcome;\n\tif (decision.outcome === \"escalated\" && decision.retryModel) {\n\t\toutcomeText = `escalated -> ${decision.retryModel}`;\n\t} else if (decision.outcome === \"failed\") {\n\t\toutcomeText = \"failed\";\n\t}\n\treturn `${tier}/${risk} -> ${decision.routedModel} (${reasonCode}, ${outcomeText})`;\n}\n\nexport function getRecentModelRouterDecisions(entries: SessionEntry[], limit = 3): ModelRouterDecisionStatus[] {\n\tconst decisions: ModelRouterDecisionStatus[] = [];\n\tfor (const entry of entries) {\n\t\tif (entry.type !== \"custom\" || entry.customType !== MODEL_ROUTER_DECISION_CUSTOM_TYPE) continue;\n\t\tif (!isModelRouterDecisionStatus(entry.data)) continue;\n\t\tdecisions.push(entry.data);\n\t}\n\treturn decisions.slice(-limit);\n}\n\nexport function formatModelRouterStatus(\n\tsettings: ModelRouterStatusSettings,\n\tlastDecision?: ModelRouterDecisionStatus,\n\tformatLabel: (label: string) => string = (label) => label,\n\trecentDecisions: ModelRouterDecisionStatus[] = [],\n\tlastSkipReason?: string,\n\tlatestIntent?: ModelRouterIntent,\n\tfitnessStatuses?: ModelRouterFitnessStatuses,\n\tfailoverStatus?: ModelRouterFailoverStatus,\n): string {\n\tconst effectiveLastDecision = lastSkipReason ? undefined : lastDecision;\n\tconst lines = [\n\t\t`${formatLabel(\"Status:\")} ${settings.enabled ? \"enabled\" : \"disabled\"}`,\n\t\tformatModelWithThinking(\n\t\t\tformatLabel(\"Cheap model:\"),\n\t\t\tsettings.cheapModel,\n\t\t\tsettings.cheapThinking,\n\t\t\tsettings.fitnessGate ? fitnessStatuses?.cheap : undefined,\n\t\t),\n\t\tformatModelWithThinking(\n\t\t\tformatLabel(\"Medium model:\"),\n\t\t\tsettings.mediumModel,\n\t\t\tsettings.mediumThinking,\n\t\t\tsettings.fitnessGate ? fitnessStatuses?.medium : undefined,\n\t\t),\n\t\tformatModelWithThinking(\n\t\t\tformatLabel(\"Expensive model:\"),\n\t\t\tsettings.expensiveModel,\n\t\t\tsettings.expensiveThinking,\n\t\t\tsettings.fitnessGate ? fitnessStatuses?.expensive : undefined,\n\t\t),\n\t\tformatModelWithThinking(formatLabel(\"Executor model:\"), settings.executorModel, settings.executorThinking),\n\t\tformatModelWithThinking(formatLabel(\"Judge model:\"), settings.judgeModel, settings.judgeThinking),\n\t\t`${formatLabel(\"Learning model:\")} ${settings.learningModel ?? \"active\"}`,\n\t];\n\tif (!settings.enabled) {\n\t\tlines.push(`${formatLabel(\"Routing:\")} inactive (disabled)`);\n\t} else if (lastSkipReason) {\n\t\tlines.push(`${formatLabel(\"Routing:\")} skipped (${lastSkipReason})`);\n\t} else if (effectiveLastDecision) {\n\t\tlines.push(`${formatLabel(\"Routing:\")} active`);\n\t} else {\n\t\tlines.push(`${formatLabel(\"Routing:\")} waiting for prompt`);\n\t}\n\tlines.push(`${formatLabel(\"Latest intent:\")} ${latestIntent ?? \"none\"}`);\n\tif (!effectiveLastDecision) {\n\t\tlines.push(`${formatLabel(\"Last decision:\")} none`);\n\t} else {\n\t\tlines.push(`${formatLabel(\"Last decision:\")} ${formatDecision(effectiveLastDecision)}`);\n\t}\n\tif (failoverStatus?.exhausted.length) {\n\t\tlines.push(`${formatLabel(\"Exhausted models:\")} ${failoverStatus.exhausted.join(\", \")}`);\n\t}\n\tif (failoverStatus?.lastNotice) {\n\t\tlines.push(`${formatLabel(\"Last failover:\")} ${failoverStatus.lastNotice}`);\n\t}\n\tif (recentDecisions.length > 0) {\n\t\tlines.push(formatLabel(\"Recent decisions:\"));\n\t\tfor (const decision of recentDecisions) {\n\t\t\tlines.push(`- ${formatDecision(decision)}`);\n\t\t}\n\t}\n\treturn lines.join(\"\\n\");\n}\n"]}
@@ -1,4 +1,19 @@
1
1
  export const MODEL_ROUTER_DECISION_CUSTOM_TYPE = "model_router_decision";
2
+ const MODEL_ROUTER_INHERIT_THINKING_LABEL = "(inherit)";
3
+ function getThinkingLabel(level) {
4
+ return level ?? MODEL_ROUTER_INHERIT_THINKING_LABEL;
5
+ }
6
+ function formatModelWithThinking(label, model, level, fitness) {
7
+ const fitnessSuffix = fitness ? ` · fitness ${formatFitness(fitness)}` : "";
8
+ return `${label} ${model ?? "unset"} · thinking ${getThinkingLabel(level)}${fitnessSuffix}`;
9
+ }
10
+ function formatFitness(fitness) {
11
+ if (fitness.status === "fit")
12
+ return "fit";
13
+ if (fitness.status === "unprobed")
14
+ return "unprobed";
15
+ return `UNFIT (${fitness.lane} ${fitness.succeeded}/${fitness.total})`;
16
+ }
2
17
  function isRouteDecision(value) {
3
18
  if (!value || typeof value !== "object")
4
19
  return false;
@@ -52,13 +67,15 @@ export function getRecentModelRouterDecisions(entries, limit = 3) {
52
67
  }
53
68
  return decisions.slice(-limit);
54
69
  }
55
- export function formatModelRouterStatus(settings, lastDecision, formatLabel = (label) => label, recentDecisions = [], lastSkipReason, latestIntent) {
70
+ export function formatModelRouterStatus(settings, lastDecision, formatLabel = (label) => label, recentDecisions = [], lastSkipReason, latestIntent, fitnessStatuses, failoverStatus) {
56
71
  const effectiveLastDecision = lastSkipReason ? undefined : lastDecision;
57
72
  const lines = [
58
73
  `${formatLabel("Status:")} ${settings.enabled ? "enabled" : "disabled"}`,
59
- `${formatLabel("Cheap model:")} ${settings.cheapModel ?? "unset"}`,
60
- `${formatLabel("Medium model:")} ${settings.mediumModel ?? "unset"}`,
61
- `${formatLabel("Expensive model:")} ${settings.expensiveModel ?? "unset"}`,
74
+ formatModelWithThinking(formatLabel("Cheap model:"), settings.cheapModel, settings.cheapThinking, settings.fitnessGate ? fitnessStatuses?.cheap : undefined),
75
+ formatModelWithThinking(formatLabel("Medium model:"), settings.mediumModel, settings.mediumThinking, settings.fitnessGate ? fitnessStatuses?.medium : undefined),
76
+ formatModelWithThinking(formatLabel("Expensive model:"), settings.expensiveModel, settings.expensiveThinking, settings.fitnessGate ? fitnessStatuses?.expensive : undefined),
77
+ formatModelWithThinking(formatLabel("Executor model:"), settings.executorModel, settings.executorThinking),
78
+ formatModelWithThinking(formatLabel("Judge model:"), settings.judgeModel, settings.judgeThinking),
62
79
  `${formatLabel("Learning model:")} ${settings.learningModel ?? "active"}`,
63
80
  ];
64
81
  if (!settings.enabled) {
@@ -80,6 +97,12 @@ export function formatModelRouterStatus(settings, lastDecision, formatLabel = (l
80
97
  else {
81
98
  lines.push(`${formatLabel("Last decision:")} ${formatDecision(effectiveLastDecision)}`);
82
99
  }
100
+ if (failoverStatus?.exhausted.length) {
101
+ lines.push(`${formatLabel("Exhausted models:")} ${failoverStatus.exhausted.join(", ")}`);
102
+ }
103
+ if (failoverStatus?.lastNotice) {
104
+ lines.push(`${formatLabel("Last failover:")} ${failoverStatus.lastNotice}`);
105
+ }
83
106
  if (recentDecisions.length > 0) {
84
107
  lines.push(formatLabel("Recent decisions:"));
85
108
  for (const decision of recentDecisions) {