@caupulican/pi-adaptative 0.80.99 → 0.80.102

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 (129) hide show
  1. package/CHANGELOG.md +203 -0
  2. package/dist/core/agent-session.d.ts +72 -0
  3. package/dist/core/agent-session.d.ts.map +1 -1
  4. package/dist/core/agent-session.js +437 -46
  5. package/dist/core/agent-session.js.map +1 -1
  6. package/dist/core/autonomy/envelope-enforcement.d.ts +5 -0
  7. package/dist/core/autonomy/envelope-enforcement.d.ts.map +1 -1
  8. package/dist/core/autonomy/envelope-enforcement.js +28 -4
  9. package/dist/core/autonomy/envelope-enforcement.js.map +1 -1
  10. package/dist/core/autonomy/path-scope.d.ts.map +1 -1
  11. package/dist/core/autonomy/path-scope.js +33 -2
  12. package/dist/core/autonomy/path-scope.js.map +1 -1
  13. package/dist/core/bash-executor.d.ts.map +1 -1
  14. package/dist/core/bash-executor.js +15 -7
  15. package/dist/core/bash-executor.js.map +1 -1
  16. package/dist/core/context/brain-curator.d.ts +10 -0
  17. package/dist/core/context/brain-curator.d.ts.map +1 -1
  18. package/dist/core/context/brain-curator.js +19 -2
  19. package/dist/core/context/brain-curator.js.map +1 -1
  20. package/dist/core/context-gc.d.ts +8 -3
  21. package/dist/core/context-gc.d.ts.map +1 -1
  22. package/dist/core/context-gc.js +5 -0
  23. package/dist/core/context-gc.js.map +1 -1
  24. package/dist/core/delegation/worker-actions.d.ts +0 -10
  25. package/dist/core/delegation/worker-actions.d.ts.map +1 -1
  26. package/dist/core/delegation/worker-actions.js +5 -7
  27. package/dist/core/delegation/worker-actions.js.map +1 -1
  28. package/dist/core/delegation/worker-result.d.ts +6 -0
  29. package/dist/core/delegation/worker-result.d.ts.map +1 -1
  30. package/dist/core/delegation/worker-result.js +10 -4
  31. package/dist/core/delegation/worker-result.js.map +1 -1
  32. package/dist/core/delegation/worker-runner.d.ts +3 -0
  33. package/dist/core/delegation/worker-runner.d.ts.map +1 -1
  34. package/dist/core/delegation/worker-runner.js +6 -1
  35. package/dist/core/delegation/worker-runner.js.map +1 -1
  36. package/dist/core/doctor.d.ts +66 -0
  37. package/dist/core/doctor.d.ts.map +1 -0
  38. package/dist/core/doctor.js +144 -0
  39. package/dist/core/doctor.js.map +1 -0
  40. package/dist/core/learning/learning-audit.d.ts +13 -1
  41. package/dist/core/learning/learning-audit.d.ts.map +1 -1
  42. package/dist/core/learning/learning-audit.js +22 -1
  43. package/dist/core/learning/learning-audit.js.map +1 -1
  44. package/dist/core/learning/learning-gate.d.ts +3 -0
  45. package/dist/core/learning/learning-gate.d.ts.map +1 -1
  46. package/dist/core/learning/learning-gate.js +13 -4
  47. package/dist/core/learning/learning-gate.js.map +1 -1
  48. package/dist/core/models/default-model-suggestions.d.ts +34 -0
  49. package/dist/core/models/default-model-suggestions.d.ts.map +1 -0
  50. package/dist/core/models/default-model-suggestions.js +58 -0
  51. package/dist/core/models/default-model-suggestions.js.map +1 -0
  52. package/dist/core/models/local-registration.d.ts +2 -0
  53. package/dist/core/models/local-registration.d.ts.map +1 -1
  54. package/dist/core/models/local-registration.js +2 -1
  55. package/dist/core/models/local-registration.js.map +1 -1
  56. package/dist/core/models/local-runtime.d.ts +76 -2
  57. package/dist/core/models/local-runtime.d.ts.map +1 -1
  58. package/dist/core/models/local-runtime.js +233 -21
  59. package/dist/core/models/local-runtime.js.map +1 -1
  60. package/dist/core/profile-resource-selection.d.ts +15 -3
  61. package/dist/core/profile-resource-selection.d.ts.map +1 -1
  62. package/dist/core/profile-resource-selection.js +19 -9
  63. package/dist/core/profile-resource-selection.js.map +1 -1
  64. package/dist/core/resource-loader.d.ts +20 -0
  65. package/dist/core/resource-loader.d.ts.map +1 -1
  66. package/dist/core/resource-loader.js +42 -16
  67. package/dist/core/resource-loader.js.map +1 -1
  68. package/dist/core/settings-manager.d.ts +30 -0
  69. package/dist/core/settings-manager.d.ts.map +1 -1
  70. package/dist/core/settings-manager.js +65 -3
  71. package/dist/core/settings-manager.js.map +1 -1
  72. package/dist/core/skills.d.ts +14 -0
  73. package/dist/core/skills.d.ts.map +1 -1
  74. package/dist/core/skills.js +8 -0
  75. package/dist/core/skills.js.map +1 -1
  76. package/dist/core/slash-commands.d.ts.map +1 -1
  77. package/dist/core/slash-commands.js +1 -1
  78. package/dist/core/slash-commands.js.map +1 -1
  79. package/dist/core/tools/fff-search-backend.d.ts +34 -1
  80. package/dist/core/tools/fff-search-backend.d.ts.map +1 -1
  81. package/dist/core/tools/fff-search-backend.js +43 -4
  82. package/dist/core/tools/fff-search-backend.js.map +1 -1
  83. package/dist/core/tools/find.d.ts +16 -0
  84. package/dist/core/tools/find.d.ts.map +1 -1
  85. package/dist/core/tools/find.js +17 -3
  86. package/dist/core/tools/find.js.map +1 -1
  87. package/dist/core/tools/grep.d.ts +20 -0
  88. package/dist/core/tools/grep.d.ts.map +1 -1
  89. package/dist/core/tools/grep.js +15 -3
  90. package/dist/core/tools/grep.js.map +1 -1
  91. package/dist/main.d.ts.map +1 -1
  92. package/dist/main.js +17 -0
  93. package/dist/main.js.map +1 -1
  94. package/dist/modes/interactive/components/fitness-role-selector.d.ts +6 -2
  95. package/dist/modes/interactive/components/fitness-role-selector.d.ts.map +1 -1
  96. package/dist/modes/interactive/components/fitness-role-selector.js +55 -45
  97. package/dist/modes/interactive/components/fitness-role-selector.js.map +1 -1
  98. package/dist/modes/interactive/components/model-suggestion-selector.d.ts +13 -0
  99. package/dist/modes/interactive/components/model-suggestion-selector.d.ts.map +1 -0
  100. package/dist/modes/interactive/components/model-suggestion-selector.js +40 -0
  101. package/dist/modes/interactive/components/model-suggestion-selector.js.map +1 -0
  102. package/dist/modes/interactive/components/profile-resource-editor.d.ts +3 -0
  103. package/dist/modes/interactive/components/profile-resource-editor.d.ts.map +1 -1
  104. package/dist/modes/interactive/components/profile-resource-editor.js +16 -2
  105. package/dist/modes/interactive/components/profile-resource-editor.js.map +1 -1
  106. package/dist/modes/interactive/components/settings-selector.d.ts.map +1 -1
  107. package/dist/modes/interactive/components/settings-selector.js +98 -0
  108. package/dist/modes/interactive/components/settings-selector.js.map +1 -1
  109. package/dist/modes/interactive/interactive-mode.d.ts +8 -1
  110. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  111. package/dist/modes/interactive/interactive-mode.js +57 -10
  112. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  113. package/dist/utils/safe-write-stream.d.ts +8 -0
  114. package/dist/utils/safe-write-stream.d.ts.map +1 -1
  115. package/dist/utils/safe-write-stream.js +32 -0
  116. package/dist/utils/safe-write-stream.js.map +1 -1
  117. package/dist/utils/tools-manager.d.ts +92 -2
  118. package/dist/utils/tools-manager.d.ts.map +1 -1
  119. package/dist/utils/tools-manager.js +172 -32
  120. package/dist/utils/tools-manager.js.map +1 -1
  121. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  122. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  123. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  124. package/examples/extensions/sandbox/package-lock.json +2 -2
  125. package/examples/extensions/sandbox/package.json +1 -1
  126. package/examples/extensions/with-deps/package-lock.json +2 -2
  127. package/examples/extensions/with-deps/package.json +1 -1
  128. package/npm-shrinkwrap.json +12 -36
  129. package/package.json +4 -4
@@ -1 +1 @@
1
- {"version":3,"file":"brain-curator.js","sourceRoot":"","sources":["../../../src/core/context/brain-curator.ts"],"names":[],"mappings":"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","sourcesContent":["import { runBoundedCompletion } from \"../autonomy/bounded-completion.ts\";\n\n/**\n * Brain-assisted context curation (see docs/model-router-rework/brain-context-curation-design.md):\n * a SIDECAR curator that consumes reports the context pipeline already produces and feeds back\n * small, typed advisories. It is never a pipeline stage: every consumer must behave byte-for-byte\n * identically when a result is absent (missing digest -> today's stub; missing relevance ->\n * today's enforcement decision). The curator itself is provider-free — the completion executor is\n * injected per drain, so it works against any registered local model and faux providers in tests.\n *\n * Memory bounds are explicit: the queue and result map are both capped, and drops are counted in\n * telemetry rather than silent. Results are keyed for idempotency (digests by the GC record's\n * content hash, relevance by the audit item id), so re-enqueueing the same work is free.\n */\n\nexport const CURATION_DIGEST_SYSTEM_PROMPT = [\n\t\"You digest tool-output chunks for a coding agent's context curator. You never solve the task.\",\n\t\"Given a chunk, respond with STRICT JSON only - no prose:\",\n\t'{\"digest\":\"<one or two sentences, max 200 characters, keeping exact identifiers>\"}',\n\t\"Keep exact file paths, symbol names, error codes, and version strings verbatim.\",\n].join(\"\\n\");\n\nexport const CURATION_RELEVANCE_SYSTEM_PROMPT = [\n\t\"You judge whether a stale tool output is still relevant to the user's current goal.\",\n\t\"You never solve the task. Respond with STRICT JSON only - no prose:\",\n\t'{\"relevant\":true|false,\"confidence\":<0..1>}',\n\t\"relevant=false means the chunk is about something the current goal no longer needs.\",\n\t\"When uncertain, answer relevant=true with low confidence - keeping content is the safe default.\",\n].join(\"\\n\");\n\nexport const CURATION_COMPACTION_DIGEST_SYSTEM_PROMPT = [\n\t\"You pre-digest a chunk of an agent conversation for compaction. You never continue the conversation.\",\n\t\"Extract ONLY durable facts: decisions made, file paths and symbols touched, errors and their causes,\",\n\t\"user requirements, and outcomes. Respond with STRICT JSON only - no prose:\",\n\t'{\"digest\":\"<bullet-style summary, max 700 characters, exact identifiers verbatim>\"}',\n].join(\"\\n\");\n\nexport function parseCompactionChunkDigest(text: string): string | undefined {\n\tconst parsed = extractJsonObject(text);\n\tif (!parsed) return undefined;\n\tconst digest = (parsed as { digest?: unknown }).digest;\n\tif (typeof digest !== \"string\") return undefined;\n\tconst trimmed = digest.trim();\n\tif (trimmed.length === 0 || trimmed.length > 800) return undefined;\n\treturn trimmed;\n}\n\nexport interface PreDigestResult {\n\ttext: string;\n\ttotalChunks: number;\n\tdigested: number;\n\tfailed: number;\n}\n\nconst PRE_DIGEST_CHUNK_CHARS = 24_000;\nconst PRE_DIGEST_KEEP_RECENT_CHARS = 16_000;\nconst PRE_DIGEST_CHUNK_WALL_CLOCK_MS = 25_000;\n\n/**\n * Compaction pre-digest (design surface 3): shrink the conversation text sent to the frontier\n * summarizer by digesting OLD chunks locally, keeping the recent tail verbatim. Chunk digestion\n * is mechanical extraction — the frontier model still writes the summary. Partial assist, never\n * partial loss: any chunk whose digest fails (parse/timeout) passes through verbatim.\n */\nexport async function preDigestConversationText(args: {\n\ttext: string;\n\tcomplete: CurationComplete;\n\tsignal?: AbortSignal;\n\tchunkChars?: number;\n\tkeepRecentChars?: number;\n}): Promise<PreDigestResult> {\n\tconst chunkChars = args.chunkChars ?? PRE_DIGEST_CHUNK_CHARS;\n\tconst keepRecentChars = args.keepRecentChars ?? PRE_DIGEST_KEEP_RECENT_CHARS;\n\tif (args.text.length <= chunkChars + keepRecentChars) {\n\t\treturn { text: args.text, totalChunks: 0, digested: 0, failed: 0 };\n\t}\n\tconst cut = args.text.length - keepRecentChars;\n\tconst prefix = args.text.slice(0, cut);\n\tconst tail = args.text.slice(cut);\n\tconst chunks: string[] = [];\n\tfor (let offset = 0; offset < prefix.length; offset += chunkChars) {\n\t\tchunks.push(prefix.slice(offset, offset + chunkChars));\n\t}\n\tlet digested = 0;\n\tlet failed = 0;\n\tconst parts: string[] = [];\n\tfor (const [index, chunk] of chunks.entries()) {\n\t\tif (args.signal?.aborted) {\n\t\t\tparts.push(chunk);\n\t\t\tfailed++;\n\t\t\tcontinue;\n\t\t}\n\t\tconst bounded = await runBoundedCompletion({\n\t\t\tmaxWallClockMs: PRE_DIGEST_CHUNK_WALL_CLOCK_MS,\n\t\t\tsignal: args.signal,\n\t\t\texecute: (signal) =>\n\t\t\t\targs.complete({ systemPrompt: CURATION_COMPACTION_DIGEST_SYSTEM_PROMPT, userPrompt: chunk, signal }),\n\t\t});\n\t\tconst digest =\n\t\t\tbounded.completion && !bounded.failure ? parseCompactionChunkDigest(bounded.completion.text) : undefined;\n\t\tif (digest !== undefined) {\n\t\t\tdigested++;\n\t\t\tparts.push(`[locally pre-digested chunk ${index + 1}/${chunks.length} (${chunk.length} chars):]\\n${digest}`);\n\t\t} else {\n\t\t\tfailed++;\n\t\t\tparts.push(chunk);\n\t\t}\n\t}\n\treturn { text: `${parts.join(\"\\n\\n\")}${tail}`, totalChunks: chunks.length, digested, failed };\n}\n\nexport interface CurationJob {\n\tkind: \"stub_digest\" | \"relevance\";\n\t/** Idempotency key: digest jobs use the GC record's content hash, relevance jobs the item id. */\n\tkey: string;\n\t/** Bounded chunk the local model must actually be able to process (sliced on enqueue). */\n\tcontent: string;\n\t/** Relevance jobs only: the goal/intent line the chunk is judged against. */\n\tgoal?: string;\n}\n\nexport interface CurationResult {\n\tkey: string;\n\tkind: CurationJob[\"kind\"];\n\tok: boolean;\n\tdigest?: string;\n\trelevant?: boolean;\n\tconfidence?: number;\n\tms: number;\n}\n\nexport interface CurationTelemetrySnapshot {\n\tjobsRun: number;\n\tparseFailures: number;\n\tdroppedJobs: number;\n\t/** Times a computed digest was actually RENDERED into a GC stub on a real turn — the\n\t * pays-for-itself proxy: every serve is packed content the frontier model got a semantic\n\t * handle on without re-running tools. */\n\tdigestsServed: number;\n\t/** Chars processed locally (an honest proxy for frontier tokens NOT spent on this work). */\n\tlocalChars: number;\n\tqueued: number;\n\tresultsHeld: number;\n}\n\nexport type CurationComplete = (input: {\n\tsystemPrompt: string;\n\tuserPrompt: string;\n\tsignal?: AbortSignal;\n}) => Promise<{ text: string; costUsd: number; stopReason: string }>;\n\nconst MAX_QUEUE = 32;\nconst MAX_RESULTS = 200;\nconst MAX_JOB_CONTENT_CHARS = 8_000;\nconst DIGEST_MAX_WALL_CLOCK_MS = 20_000;\nconst RELEVANCE_MAX_WALL_CLOCK_MS = 8_000;\nexport const CURATION_RELEVANCE_MIN_CONFIDENCE = 0.8;\n\nfunction extractJsonObject(text: string): unknown | undefined {\n\tconst trimmed = text.trim();\n\tconst candidates: string[] = [trimmed];\n\tconst fenced = /```(?:json)?\\s*([\\s\\S]*?)```/.exec(trimmed);\n\tif (fenced?.[1]) candidates.push(fenced[1].trim());\n\tconst start = trimmed.indexOf(\"{\");\n\tconst end = trimmed.lastIndexOf(\"}\");\n\tif (start >= 0 && end > start) candidates.push(trimmed.slice(start, end + 1));\n\tfor (const candidate of candidates) {\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(candidate);\n\t\t\tif (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) return parsed;\n\t\t} catch {\n\t\t\t// try next candidate\n\t\t}\n\t}\n\treturn undefined;\n}\n\nexport function parseCurationDigest(text: string): string | undefined {\n\tconst parsed = extractJsonObject(text);\n\tif (!parsed) return undefined;\n\tconst digest = (parsed as { digest?: unknown }).digest;\n\tif (typeof digest !== \"string\") return undefined;\n\tconst trimmed = digest.trim().replace(/\\s+/g, \" \");\n\tif (trimmed.length === 0 || trimmed.length > 240) return undefined;\n\treturn trimmed;\n}\n\nexport function parseCurationRelevance(text: string): { relevant: boolean; confidence: number } | undefined {\n\tconst parsed = extractJsonObject(text);\n\tif (!parsed) return undefined;\n\tconst record = parsed as { relevant?: unknown; confidence?: unknown };\n\tif (typeof record.relevant !== \"boolean\") return undefined;\n\tconst confidence =\n\t\ttypeof record.confidence === \"number\" && Number.isFinite(record.confidence)\n\t\t\t? Math.max(0, Math.min(1, record.confidence))\n\t\t\t: 0;\n\treturn { relevant: record.relevant, confidence };\n}\n\nexport class BrainCurator {\n\tprivate readonly _queue = new Map<string, CurationJob>();\n\tprivate readonly _results = new Map<string, CurationResult>();\n\tprivate _jobsRun = 0;\n\tprivate _parseFailures = 0;\n\tprivate _droppedJobs = 0;\n\tprivate _localChars = 0;\n\tprivate _digestsServed = 0;\n\tprivate _draining = false;\n\n\tenqueue(job: CurationJob): void {\n\t\tif (this._results.has(job.key) || this._queue.has(job.key)) return;\n\t\tif (this._queue.size >= MAX_QUEUE) {\n\t\t\t// Drop the OLDEST queued job (newer work reflects the current goal better) and count it.\n\t\t\tconst oldest = this._queue.keys().next().value;\n\t\t\tif (oldest !== undefined) this._queue.delete(oldest);\n\t\t\tthis._droppedJobs++;\n\t\t}\n\t\tthis._queue.set(job.key, { ...job, content: job.content.slice(0, MAX_JOB_CONTENT_CHARS) });\n\t}\n\n\tgetDigest(key: string): string | undefined {\n\t\tconst result = this._results.get(key);\n\t\treturn result?.ok && result.kind === \"stub_digest\" ? result.digest : undefined;\n\t}\n\n\t/** Callers report when a digest was rendered into a real (sent) prompt stub. */\n\tnoteDigestServed(): void {\n\t\tthis._digestsServed++;\n\t}\n\n\tgetRelevance(key: string): { relevant: boolean; confidence: number } | undefined {\n\t\tconst result = this._results.get(key);\n\t\tif (!result?.ok || result.kind !== \"relevance\" || result.relevant === undefined) return undefined;\n\t\treturn { relevant: result.relevant, confidence: result.confidence ?? 0 };\n\t}\n\n\thasWork(): boolean {\n\t\treturn this._queue.size > 0;\n\t}\n\n\tget isDraining(): boolean {\n\t\treturn this._draining;\n\t}\n\n\ttelemetry(): CurationTelemetrySnapshot {\n\t\treturn {\n\t\t\tjobsRun: this._jobsRun,\n\t\t\tparseFailures: this._parseFailures,\n\t\t\tdroppedJobs: this._droppedJobs,\n\t\t\tdigestsServed: this._digestsServed,\n\t\t\tlocalChars: this._localChars,\n\t\t\tqueued: this._queue.size,\n\t\t\tresultsHeld: this._results.size,\n\t\t};\n\t}\n\n\t/**\n\t * Run up to `maxJobs` queued jobs through the injected local-model completer. Single-flight:\n\t * a concurrent drain call returns [] immediately rather than double-running jobs. Every call\n\t * is wall-clock bounded; a failed/unparseable job is recorded as a not-ok result (so it is\n\t * not retried forever) and counted in telemetry.\n\t */\n\tasync drain(args: {\n\t\tcomplete: CurationComplete;\n\t\tmaxJobs: number;\n\t\tsignal?: AbortSignal;\n\t\tnow?: () => number;\n\t}): Promise<CurationResult[]> {\n\t\tif (this._draining) return [];\n\t\tthis._draining = true;\n\t\tconst now = args.now ?? Date.now;\n\t\tconst completed: CurationResult[] = [];\n\t\ttry {\n\t\t\tconst jobs = [...this._queue.values()].slice(0, Math.max(0, args.maxJobs));\n\t\t\tfor (const job of jobs) {\n\t\t\t\tif (args.signal?.aborted) break;\n\t\t\t\tthis._queue.delete(job.key);\n\t\t\t\tconst started = now();\n\t\t\t\tconst bounded = await runBoundedCompletion({\n\t\t\t\t\tmaxWallClockMs: job.kind === \"stub_digest\" ? DIGEST_MAX_WALL_CLOCK_MS : RELEVANCE_MAX_WALL_CLOCK_MS,\n\t\t\t\t\tsignal: args.signal,\n\t\t\t\t\texecute: (signal) =>\n\t\t\t\t\t\targs.complete({\n\t\t\t\t\t\t\tsystemPrompt:\n\t\t\t\t\t\t\t\tjob.kind === \"stub_digest\" ? CURATION_DIGEST_SYSTEM_PROMPT : CURATION_RELEVANCE_SYSTEM_PROMPT,\n\t\t\t\t\t\t\tuserPrompt:\n\t\t\t\t\t\t\t\tjob.kind === \"stub_digest\"\n\t\t\t\t\t\t\t\t\t? job.content\n\t\t\t\t\t\t\t\t\t: `Current goal: ${job.goal ?? \"(unknown)\"}\\n\\nStale chunk:\\n${job.content}`,\n\t\t\t\t\t\t\tsignal,\n\t\t\t\t\t\t}),\n\t\t\t\t});\n\t\t\t\tconst ms = now() - started;\n\t\t\t\tthis._jobsRun++;\n\t\t\t\tthis._localChars += job.content.length;\n\t\t\t\tlet result: CurationResult = { key: job.key, kind: job.kind, ok: false, ms };\n\t\t\t\tif (bounded.completion && !bounded.failure) {\n\t\t\t\t\tif (job.kind === \"stub_digest\") {\n\t\t\t\t\t\tconst digest = parseCurationDigest(bounded.completion.text);\n\t\t\t\t\t\tresult = digest !== undefined ? { ...result, ok: true, digest } : result;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconst relevance = parseCurationRelevance(bounded.completion.text);\n\t\t\t\t\t\tresult =\n\t\t\t\t\t\t\trelevance !== undefined\n\t\t\t\t\t\t\t\t? { ...result, ok: true, relevant: relevance.relevant, confidence: relevance.confidence }\n\t\t\t\t\t\t\t\t: result;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!result.ok) this._parseFailures++;\n\t\t\t\tthis._storeResult(result);\n\t\t\t\tcompleted.push(result);\n\t\t\t}\n\t\t} finally {\n\t\t\tthis._draining = false;\n\t\t}\n\t\treturn completed;\n\t}\n\n\tprivate _storeResult(result: CurationResult): void {\n\t\tif (this._results.size >= MAX_RESULTS) {\n\t\t\tconst oldest = this._results.keys().next().value;\n\t\t\tif (oldest !== undefined) this._results.delete(oldest);\n\t\t}\n\t\tthis._results.set(result.key, result);\n\t}\n}\n"]}
1
+ {"version":3,"file":"brain-curator.js","sourceRoot":"","sources":["../../../src/core/context/brain-curator.ts"],"names":[],"mappings":"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","sourcesContent":["import { runBoundedCompletion } from \"../autonomy/bounded-completion.ts\";\nimport { wrapUntrustedText } from \"../security/untrusted-boundary.ts\";\n\n/**\n * Brain-assisted context curation (see docs/model-router-rework/brain-context-curation-design.md):\n * a SIDECAR curator that consumes reports the context pipeline already produces and feeds back\n * small, typed advisories. It is never a pipeline stage: every consumer must behave byte-for-byte\n * identically when a result is absent (missing digest -> today's stub; missing relevance ->\n * today's enforcement decision). The curator itself is provider-free — the completion executor is\n * injected per drain, so it works against any registered local model and faux providers in tests.\n *\n * Memory bounds are explicit: the queue and result map are both capped, and drops are counted in\n * telemetry rather than silent. Results are keyed for idempotency (digests by the GC record's\n * content hash, relevance by the audit item id), so re-enqueueing the same work is free.\n */\n\nexport const CURATION_DIGEST_SYSTEM_PROMPT = [\n\t\"You digest tool-output chunks for a coding agent's context curator. You never solve the task.\",\n\t\"Given a chunk, respond with STRICT JSON only - no prose:\",\n\t'{\"digest\":\"<one or two sentences, max 200 characters, keeping exact identifiers>\"}',\n\t\"Keep exact file paths, symbol names, error codes, and version strings verbatim.\",\n].join(\"\\n\");\n\nexport const CURATION_RELEVANCE_SYSTEM_PROMPT = [\n\t\"You judge whether a stale tool output is still relevant to the user's current goal.\",\n\t\"You never solve the task. Respond with STRICT JSON only - no prose:\",\n\t'{\"relevant\":true|false,\"confidence\":<0..1>}',\n\t\"relevant=false means the chunk is about something the current goal no longer needs.\",\n\t\"When uncertain, answer relevant=true with low confidence - keeping content is the safe default.\",\n].join(\"\\n\");\n\nexport const CURATION_COMPACTION_DIGEST_SYSTEM_PROMPT = [\n\t\"You pre-digest a chunk of an agent conversation for compaction. You never continue the conversation.\",\n\t\"Extract ONLY durable facts: decisions made, file paths and symbols touched, errors and their causes,\",\n\t\"user requirements, and outcomes. Respond with STRICT JSON only - no prose:\",\n\t'{\"digest\":\"<bullet-style summary, max 700 characters, exact identifiers verbatim>\"}',\n].join(\"\\n\");\n\nexport function parseCompactionChunkDigest(text: string): string | undefined {\n\tconst parsed = extractJsonObject(text);\n\tif (!parsed) return undefined;\n\tconst digest = (parsed as { digest?: unknown }).digest;\n\tif (typeof digest !== \"string\") return undefined;\n\tconst trimmed = digest.trim();\n\tif (trimmed.length === 0 || trimmed.length > 800) return undefined;\n\treturn trimmed;\n}\n\nexport interface PreDigestResult {\n\ttext: string;\n\ttotalChunks: number;\n\tdigested: number;\n\tfailed: number;\n}\n\nconst PRE_DIGEST_CHUNK_CHARS = 24_000;\nconst PRE_DIGEST_KEEP_RECENT_CHARS = 16_000;\nconst PRE_DIGEST_CHUNK_WALL_CLOCK_MS = 25_000;\n\n/**\n * Compaction pre-digest (design surface 3): shrink the conversation text sent to the frontier\n * summarizer by digesting OLD chunks locally, keeping the recent tail verbatim. Chunk digestion\n * is mechanical extraction — the frontier model still writes the summary. Partial assist, never\n * partial loss: any chunk whose digest fails (parse/timeout) passes through verbatim.\n */\nexport async function preDigestConversationText(args: {\n\ttext: string;\n\tcomplete: CurationComplete;\n\tsignal?: AbortSignal;\n\tchunkChars?: number;\n\tkeepRecentChars?: number;\n}): Promise<PreDigestResult> {\n\tconst chunkChars = args.chunkChars ?? PRE_DIGEST_CHUNK_CHARS;\n\tconst keepRecentChars = args.keepRecentChars ?? PRE_DIGEST_KEEP_RECENT_CHARS;\n\tif (args.text.length <= chunkChars + keepRecentChars) {\n\t\treturn { text: args.text, totalChunks: 0, digested: 0, failed: 0 };\n\t}\n\tconst cut = args.text.length - keepRecentChars;\n\tconst prefix = args.text.slice(0, cut);\n\tconst tail = args.text.slice(cut);\n\tconst chunks: string[] = [];\n\tfor (let offset = 0; offset < prefix.length; offset += chunkChars) {\n\t\tchunks.push(prefix.slice(offset, offset + chunkChars));\n\t}\n\tlet digested = 0;\n\tlet failed = 0;\n\tconst parts: string[] = [];\n\tfor (const [index, chunk] of chunks.entries()) {\n\t\tif (args.signal?.aborted) {\n\t\t\tparts.push(chunk);\n\t\t\tfailed++;\n\t\t\tcontinue;\n\t\t}\n\t\tconst bounded = await runBoundedCompletion({\n\t\t\tmaxWallClockMs: PRE_DIGEST_CHUNK_WALL_CLOCK_MS,\n\t\t\tsignal: args.signal,\n\t\t\texecute: (signal) =>\n\t\t\t\targs.complete({ systemPrompt: CURATION_COMPACTION_DIGEST_SYSTEM_PROMPT, userPrompt: chunk, signal }),\n\t\t});\n\t\tconst digest =\n\t\t\tbounded.completion && !bounded.failure ? parseCompactionChunkDigest(bounded.completion.text) : undefined;\n\t\tif (digest !== undefined) {\n\t\t\tdigested++;\n\t\t\tparts.push(`[locally pre-digested chunk ${index + 1}/${chunks.length} (${chunk.length} chars):]\\n${digest}`);\n\t\t} else {\n\t\t\tfailed++;\n\t\t\tparts.push(chunk);\n\t\t}\n\t}\n\treturn { text: `${parts.join(\"\\n\\n\")}${tail}`, totalChunks: chunks.length, digested, failed };\n}\n\nexport interface CurationJob {\n\tkind: \"stub_digest\" | \"relevance\";\n\t/** Idempotency key: digest jobs use the GC record's content hash, relevance jobs the item id. */\n\tkey: string;\n\t/** Bounded chunk the local model must actually be able to process (sliced on enqueue). */\n\tcontent: string;\n\t/** Relevance jobs only: the goal/intent line the chunk is judged against. */\n\tgoal?: string;\n}\n\nexport interface CurationResult {\n\tkey: string;\n\tkind: CurationJob[\"kind\"];\n\tok: boolean;\n\t/** Raw model-parsed digest (stub_digest jobs only): human-readable, NOT fenced. Kept as-is for\n\t * logging/audit (e.g. the session's brain-curation entry) — never render this into a provider\n\t * prompt; use `fencedDigest` (via getDigest) for that. */\n\tdigest?: string;\n\t/** The same digest fenced in the untrusted-content boundary, wrapped exactly ONCE here at store\n\t * time (not at every context-gc render), so the fence's nonce is fixed for the life of this\n\t * result — every render of it is then byte-identical, keeping the provider's prompt-prefix cache\n\t * warm. This is what getDigest() returns and what context-gc renders verbatim into a packed stub. */\n\tfencedDigest?: string;\n\trelevant?: boolean;\n\tconfidence?: number;\n\tms: number;\n}\n\nexport interface CurationTelemetrySnapshot {\n\tjobsRun: number;\n\tparseFailures: number;\n\tdroppedJobs: number;\n\t/** Times a computed digest was actually RENDERED into a GC stub on a real turn — the\n\t * pays-for-itself proxy: every serve is packed content the frontier model got a semantic\n\t * handle on without re-running tools. */\n\tdigestsServed: number;\n\t/** Chars processed locally (an honest proxy for frontier tokens NOT spent on this work). */\n\tlocalChars: number;\n\tqueued: number;\n\tresultsHeld: number;\n}\n\nexport type CurationComplete = (input: {\n\tsystemPrompt: string;\n\tuserPrompt: string;\n\tsignal?: AbortSignal;\n}) => Promise<{ text: string; costUsd: number; stopReason: string }>;\n\nconst MAX_QUEUE = 32;\nconst MAX_RESULTS = 200;\nconst MAX_JOB_CONTENT_CHARS = 8_000;\nconst DIGEST_MAX_WALL_CLOCK_MS = 20_000;\nconst RELEVANCE_MAX_WALL_CLOCK_MS = 8_000;\nexport const CURATION_RELEVANCE_MIN_CONFIDENCE = 0.8;\n\nfunction extractJsonObject(text: string): unknown | undefined {\n\tconst trimmed = text.trim();\n\tconst candidates: string[] = [trimmed];\n\tconst fenced = /```(?:json)?\\s*([\\s\\S]*?)```/.exec(trimmed);\n\tif (fenced?.[1]) candidates.push(fenced[1].trim());\n\tconst start = trimmed.indexOf(\"{\");\n\tconst end = trimmed.lastIndexOf(\"}\");\n\tif (start >= 0 && end > start) candidates.push(trimmed.slice(start, end + 1));\n\tfor (const candidate of candidates) {\n\t\ttry {\n\t\t\tconst parsed = JSON.parse(candidate);\n\t\t\tif (parsed && typeof parsed === \"object\" && !Array.isArray(parsed)) return parsed;\n\t\t} catch {\n\t\t\t// try next candidate\n\t\t}\n\t}\n\treturn undefined;\n}\n\nexport function parseCurationDigest(text: string): string | undefined {\n\tconst parsed = extractJsonObject(text);\n\tif (!parsed) return undefined;\n\tconst digest = (parsed as { digest?: unknown }).digest;\n\tif (typeof digest !== \"string\") return undefined;\n\tconst trimmed = digest.trim().replace(/\\s+/g, \" \");\n\tif (trimmed.length === 0 || trimmed.length > 240) return undefined;\n\treturn trimmed;\n}\n\n/**\n * Fence a parsed digest in the untrusted-content boundary exactly ONCE, here at store time — the\n * digest is a machine paraphrase of (possibly attacker-influenced) tool output, so it must never\n * reach the provider as bare prose (design: untrusted-content-boundary). Wrapping at store time,\n * not at every context-gc render, fixes the fence's nonce for the life of the stored result: every\n * render of the same stored digest is then byte-identical, so the provider's prompt-prefix cache\n * stays warm — the same pattern transcript-recall already uses for its pages.\n */\nfunction fenceDigestForStorage(digest: string): string {\n\treturn wrapUntrustedText(digest, \"context-gc:auto-digest\");\n}\n\nexport function parseCurationRelevance(text: string): { relevant: boolean; confidence: number } | undefined {\n\tconst parsed = extractJsonObject(text);\n\tif (!parsed) return undefined;\n\tconst record = parsed as { relevant?: unknown; confidence?: unknown };\n\tif (typeof record.relevant !== \"boolean\") return undefined;\n\tconst confidence =\n\t\ttypeof record.confidence === \"number\" && Number.isFinite(record.confidence)\n\t\t\t? Math.max(0, Math.min(1, record.confidence))\n\t\t\t: 0;\n\treturn { relevant: record.relevant, confidence };\n}\n\nexport class BrainCurator {\n\tprivate readonly _queue = new Map<string, CurationJob>();\n\tprivate readonly _results = new Map<string, CurationResult>();\n\tprivate _jobsRun = 0;\n\tprivate _parseFailures = 0;\n\tprivate _droppedJobs = 0;\n\tprivate _localChars = 0;\n\tprivate _digestsServed = 0;\n\tprivate _draining = false;\n\n\tenqueue(job: CurationJob): void {\n\t\tif (this._results.has(job.key) || this._queue.has(job.key)) return;\n\t\tif (this._queue.size >= MAX_QUEUE) {\n\t\t\t// Drop the OLDEST queued job (newer work reflects the current goal better) and count it.\n\t\t\tconst oldest = this._queue.keys().next().value;\n\t\t\tif (oldest !== undefined) this._queue.delete(oldest);\n\t\t\tthis._droppedJobs++;\n\t\t}\n\t\tthis._queue.set(job.key, { ...job, content: job.content.slice(0, MAX_JOB_CONTENT_CHARS) });\n\t}\n\n\t/** Returns the digest already fenced in the untrusted-content boundary — callers must render it\n\t * verbatim into a provider prompt, never re-wrap it (see CurationResult.fencedDigest for why). */\n\tgetDigest(key: string): string | undefined {\n\t\tconst result = this._results.get(key);\n\t\treturn result?.ok && result.kind === \"stub_digest\" ? result.fencedDigest : undefined;\n\t}\n\n\t/** Callers report when a digest was rendered into a real (sent) prompt stub. */\n\tnoteDigestServed(): void {\n\t\tthis._digestsServed++;\n\t}\n\n\tgetRelevance(key: string): { relevant: boolean; confidence: number } | undefined {\n\t\tconst result = this._results.get(key);\n\t\tif (!result?.ok || result.kind !== \"relevance\" || result.relevant === undefined) return undefined;\n\t\treturn { relevant: result.relevant, confidence: result.confidence ?? 0 };\n\t}\n\n\thasWork(): boolean {\n\t\treturn this._queue.size > 0;\n\t}\n\n\tget isDraining(): boolean {\n\t\treturn this._draining;\n\t}\n\n\ttelemetry(): CurationTelemetrySnapshot {\n\t\treturn {\n\t\t\tjobsRun: this._jobsRun,\n\t\t\tparseFailures: this._parseFailures,\n\t\t\tdroppedJobs: this._droppedJobs,\n\t\t\tdigestsServed: this._digestsServed,\n\t\t\tlocalChars: this._localChars,\n\t\t\tqueued: this._queue.size,\n\t\t\tresultsHeld: this._results.size,\n\t\t};\n\t}\n\n\t/**\n\t * Run up to `maxJobs` queued jobs through the injected local-model completer. Single-flight:\n\t * a concurrent drain call returns [] immediately rather than double-running jobs. Every call\n\t * is wall-clock bounded; a failed/unparseable job is recorded as a not-ok result (so it is\n\t * not retried forever) and counted in telemetry.\n\t */\n\tasync drain(args: {\n\t\tcomplete: CurationComplete;\n\t\tmaxJobs: number;\n\t\tsignal?: AbortSignal;\n\t\tnow?: () => number;\n\t}): Promise<CurationResult[]> {\n\t\tif (this._draining) return [];\n\t\tthis._draining = true;\n\t\tconst now = args.now ?? Date.now;\n\t\tconst completed: CurationResult[] = [];\n\t\ttry {\n\t\t\tconst jobs = [...this._queue.values()].slice(0, Math.max(0, args.maxJobs));\n\t\t\tfor (const job of jobs) {\n\t\t\t\tif (args.signal?.aborted) break;\n\t\t\t\tthis._queue.delete(job.key);\n\t\t\t\tconst started = now();\n\t\t\t\tconst bounded = await runBoundedCompletion({\n\t\t\t\t\tmaxWallClockMs: job.kind === \"stub_digest\" ? DIGEST_MAX_WALL_CLOCK_MS : RELEVANCE_MAX_WALL_CLOCK_MS,\n\t\t\t\t\tsignal: args.signal,\n\t\t\t\t\texecute: (signal) =>\n\t\t\t\t\t\targs.complete({\n\t\t\t\t\t\t\tsystemPrompt:\n\t\t\t\t\t\t\t\tjob.kind === \"stub_digest\" ? CURATION_DIGEST_SYSTEM_PROMPT : CURATION_RELEVANCE_SYSTEM_PROMPT,\n\t\t\t\t\t\t\tuserPrompt:\n\t\t\t\t\t\t\t\tjob.kind === \"stub_digest\"\n\t\t\t\t\t\t\t\t\t? job.content\n\t\t\t\t\t\t\t\t\t: `Current goal: ${job.goal ?? \"(unknown)\"}\\n\\nStale chunk:\\n${job.content}`,\n\t\t\t\t\t\t\tsignal,\n\t\t\t\t\t\t}),\n\t\t\t\t});\n\t\t\t\tconst ms = now() - started;\n\t\t\t\tthis._jobsRun++;\n\t\t\t\tthis._localChars += job.content.length;\n\t\t\t\tlet result: CurationResult = { key: job.key, kind: job.kind, ok: false, ms };\n\t\t\t\tif (bounded.completion && !bounded.failure) {\n\t\t\t\t\tif (job.kind === \"stub_digest\") {\n\t\t\t\t\t\tconst digest = parseCurationDigest(bounded.completion.text);\n\t\t\t\t\t\tresult =\n\t\t\t\t\t\t\tdigest !== undefined\n\t\t\t\t\t\t\t\t? { ...result, ok: true, digest, fencedDigest: fenceDigestForStorage(digest) }\n\t\t\t\t\t\t\t\t: result;\n\t\t\t\t\t} else {\n\t\t\t\t\t\tconst relevance = parseCurationRelevance(bounded.completion.text);\n\t\t\t\t\t\tresult =\n\t\t\t\t\t\t\trelevance !== undefined\n\t\t\t\t\t\t\t\t? { ...result, ok: true, relevant: relevance.relevant, confidence: relevance.confidence }\n\t\t\t\t\t\t\t\t: result;\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t\tif (!result.ok) this._parseFailures++;\n\t\t\t\tthis._storeResult(result);\n\t\t\t\tcompleted.push(result);\n\t\t\t}\n\t\t} finally {\n\t\t\tthis._draining = false;\n\t\t}\n\t\treturn completed;\n\t}\n\n\tprivate _storeResult(result: CurationResult): void {\n\t\tif (this._results.size >= MAX_RESULTS) {\n\t\t\tconst oldest = this._results.keys().next().value;\n\t\t\tif (oldest !== undefined) this._results.delete(oldest);\n\t\t}\n\t\tthis._results.set(result.key, result);\n\t}\n}\n"]}
@@ -24,8 +24,11 @@ export interface NormalizedContextGcSettings extends Omit<Required<ContextGcSett
24
24
  }
25
25
  /**
26
26
  * Brain-curation hooks (both optional; absent hooks are byte-for-byte today's behavior).
27
- * `resolveDigest` is a pure lookup keyed by the record's content hash; `onPacked` lets the
28
- * caller enqueue digest work with the exact original text at the moment it is packed.
27
+ * `resolveDigest` is a pure lookup keyed by the record's content hash it must return the digest
28
+ * already fenced in the untrusted-content boundary (e.g. BrainCurator.getDigest), with the fence's
29
+ * nonce fixed once at store time, since GC re-renders the stub from raw messages on every request
30
+ * and renders the returned string VERBATIM. `onPacked` lets the caller enqueue digest work with the
31
+ * exact original text at the moment it is packed.
29
32
  */
30
33
  export interface ContextGcCurationHooks {
31
34
  resolveDigest?: (digestKey: string) => string | undefined;
@@ -49,7 +52,9 @@ export interface ContextGcPackedRecord {
49
52
  path?: string;
50
53
  command?: string;
51
54
  key?: string;
52
- /** Brain-curator semantic digest of the packed content (model-generated; advisory only). */
55
+ /** Brain-curator semantic digest of the packed content (model-generated; advisory only). Arrives
56
+ * already fenced in the untrusted-content boundary (see ContextGcCurationHooks.resolveDigest) —
57
+ * render verbatim, never re-wrap. */
53
58
  digest?: string;
54
59
  }
55
60
  export interface ContextGcReport {
@@ -1 +1 @@
1
- {"version":3,"file":"context-gc.d.ts","sourceRoot":"","sources":["../../src/core/context-gc.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAK9D,MAAM,WAAW,wBAAwB;IACxC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0EAA0E;IAC1E,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,yFAAyF;IACzF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IACjC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,oEAAoE;IACpE,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,gFAAgF;IAChF,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,oDAAoD;IACpD,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,sFAAsF;IACtF,cAAc,CAAC,EAAE,wBAAwB,CAAC;CAC1C;AAED,MAAM,WAAW,2BAA4B,SAAQ,IAAI,CAAC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,gBAAgB,CAAC;IACvG,cAAc,EAAE,QAAQ,CAAC,wBAAwB,CAAC,CAAC;CACnD;AAED;;;;GAIG;AACH,MAAM,WAAW,sBAAsB;IACtC,aAAa,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IAC1D,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;CACzE;AAED,MAAM,WAAW,gBAAiB,SAAQ,2BAA2B;IACpE,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,EAAE,sBAAsB,CAAC;CAClC;AAED,MAAM,WAAW,qBAAqB;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,iBAAiB,GAAG,mBAAmB,GAAG,uBAAuB,CAAC;IAC1E,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,4FAA4F;IAC5F,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,eAAe;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,qBAAqB,EAAE,CAAC;CACjC;AAED,MAAM,WAAW,eAAe;IAC/B,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,MAAM,EAAE,eAAe,CAAC;CACxB;AAuBD,eAAO,MAAM,2BAA2B,EAAE,2BA4BzC,CAAC;AA6CF,wBAAgB,oBAAoB,CAAC,QAAQ,CAAC,EAAE,iBAAiB,GAAG,2BAA2B,CAE9F;AAmND,wBAAgB,cAAc,CAC7B,QAAQ,EAAE,YAAY,EAAE,EACxB,WAAW,EAAE,iBAAiB,GAAG;IAChC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,EAAE,sBAAsB,CAAC;CAClC,GACC,eAAe,CAkHjB","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { existsSync, mkdirSync, writeFileSync } from \"node:fs\";\nimport { isAbsolute, resolve } from \"node:path\";\nimport type { AgentMessage } from \"@caupulican/pi-agent-core\";\nimport type { ToolResultMessage } from \"@caupulican/pi-ai\";\nimport { normalizePath } from \"../utils/paths.ts\";\nimport { estimateTokens } from \"./compaction/compaction.ts\";\n\nexport interface SemanticMemoryGcSettings {\n\tenabled?: boolean;\n\t/** Number of newest Automata/Mind injected pages to preserve verbatim. */\n\tpreserveRecentPages?: number;\n\t/** Minimum provider-visible text chars before a stale semantic memory page is packed. */\n\tminChars?: number;\n\t/** Markers that identify deterministic Automata/Mind context pages. */\n\tmarkers?: string[];\n}\n\nexport interface ContextGcSettings {\n\tenabled?: boolean;\n\t/** Number of most recent AgentMessage rows to preserve verbatim. */\n\tpreserveRecentMessages?: number;\n\t/** Minimum provider-visible text chars before a stale tool result is packed. */\n\tminToolResultChars?: number;\n\t/** Tool names eligible for stale result packing. */\n\ttools?: string[];\n\t/** Provider-context control for deterministic Automata/Mind semantic memory pages. */\n\tsemanticMemory?: SemanticMemoryGcSettings;\n}\n\nexport interface NormalizedContextGcSettings extends Omit<Required<ContextGcSettings>, \"semanticMemory\"> {\n\tsemanticMemory: Required<SemanticMemoryGcSettings>;\n}\n\n/**\n * Brain-curation hooks (both optional; absent hooks are byte-for-byte today's behavior).\n * `resolveDigest` is a pure lookup keyed by the record's content hash; `onPacked` lets the\n * caller enqueue digest work with the exact original text at the moment it is packed.\n */\nexport interface ContextGcCurationHooks {\n\tresolveDigest?: (digestKey: string) => string | undefined;\n\tonPacked?: (record: ContextGcPackedRecord, originalText: string) => void;\n}\n\nexport interface ContextGcOptions extends NormalizedContextGcSettings {\n\tcwd: string;\n\tstorageDir?: string;\n\twritePayloads?: boolean;\n\tcuration?: ContextGcCurationHooks;\n}\n\nexport interface ContextGcPackedRecord {\n\ttoolName: string;\n\ttoolCallId: string;\n\tmessageIndex: number;\n\treason: \"superseded-read\" | \"stale-tool-result\" | \"stale-semantic-memory\";\n\toriginalChars: number;\n\toriginalTokens: number;\n\tpackedTokens: number;\n\tstoragePath?: string;\n\tpath?: string;\n\tcommand?: string;\n\tkey?: string;\n\t/** Brain-curator semantic digest of the packed content (model-generated; advisory only). */\n\tdigest?: string;\n}\n\nexport interface ContextGcReport {\n\tenabled: boolean;\n\tpackedCount: number;\n\toriginalTokens: number;\n\tpackedTokens: number;\n\tsavedTokens: number;\n\trecords: ContextGcPackedRecord[];\n}\n\nexport interface ContextGcResult {\n\tmessages: AgentMessage[];\n\treport: ContextGcReport;\n}\n\nconst DEFAULT_SEMANTIC_MEMORY_GC_SETTINGS: Required<SemanticMemoryGcSettings> = {\n\tenabled: true,\n\tpreserveRecentPages: 1,\n\tminChars: 900,\n\tmarkers: [\n\t\t// Generic memory-subsystem recall page marker (brand-free). Provider-specific markers are\n\t\t// merged in dynamically at runtime via MemoryManager.getContextMarkers().\n\t\t\"<memory_context\",\n\t\t// Pre-existing provider-specific markers (to be generalized to provider-declared markers).\n\t\t\"<automata_context\",\n\t\t\"<automata_response\",\n\t\t\"<automata_query\",\n\t\t\"<automata_fetch\",\n\t\t\"<memory_lifecycle_audit\",\n\t\t\"<memory_lifecycle_purge\",\n\t\t\"<automata_doctor\",\n\t\t\"<automata_optimizer\",\n\t\t\"<automata_mesh\",\n\t],\n};\n\nexport const DEFAULT_CONTEXT_GC_SETTINGS: NormalizedContextGcSettings = {\n\tenabled: true,\n\tpreserveRecentMessages: 8,\n\tminToolResultChars: 1200,\n\ttools: [\n\t\t\"read\",\n\t\t\"bash\",\n\t\t\"rg\",\n\t\t\"grep\",\n\t\t\"find\",\n\t\t\"ls\",\n\t\t\"skill_open\",\n\t\t\"automata_graph_status\",\n\t\t\"automata_graph_search\",\n\t\t\"automata_graph_query\",\n\t\t\"automata_graph_neighbors\",\n\t\t\"automata_graph_path\",\n\t\t\"automata_graph_pointer_pack\",\n\t\t\"learning_query_memory\",\n\t\t\"subagent\",\n\t\t\"task_steps\",\n\t\t\"task_background\",\n\t\t\"task_goal\",\n\t\t\"run_ledger\",\n\t\t\"context_headroom_retrieve\",\n\t\t\"headroom_retrieve\",\n\t],\n\tsemanticMemory: DEFAULT_SEMANTIC_MEMORY_GC_SETTINGS,\n};\n\ntype ToolCallMeta = {\n\tid: string;\n\tname: string;\n\targs: Record<string, unknown>;\n\tmessageIndex: number;\n};\n\nfunction cap(text: string, limit = 220): string {\n\tconst compact = text.replace(/\\s+/g, \" \").trim();\n\treturn compact.length > limit ? `${compact.slice(0, Math.max(0, limit - 1))}…` : compact;\n}\n\nfunction normalizeSemanticMemoryGcSettings(settings?: SemanticMemoryGcSettings): Required<SemanticMemoryGcSettings> {\n\treturn {\n\t\tenabled: settings?.enabled ?? DEFAULT_SEMANTIC_MEMORY_GC_SETTINGS.enabled,\n\t\tpreserveRecentPages: Math.max(\n\t\t\t0,\n\t\t\tMath.floor(settings?.preserveRecentPages ?? DEFAULT_SEMANTIC_MEMORY_GC_SETTINGS.preserveRecentPages),\n\t\t),\n\t\tminChars: Math.max(0, Math.floor(settings?.minChars ?? DEFAULT_SEMANTIC_MEMORY_GC_SETTINGS.minChars)),\n\t\tmarkers:\n\t\t\tsettings?.markers && settings.markers.length > 0\n\t\t\t\t? settings.markers\n\t\t\t\t: DEFAULT_SEMANTIC_MEMORY_GC_SETTINGS.markers,\n\t};\n}\n\nfunction normalizeContextGcSettings(settings?: ContextGcSettings): NormalizedContextGcSettings {\n\treturn {\n\t\tenabled: settings?.enabled ?? DEFAULT_CONTEXT_GC_SETTINGS.enabled,\n\t\tpreserveRecentMessages: Math.max(\n\t\t\t0,\n\t\t\tMath.floor(settings?.preserveRecentMessages ?? DEFAULT_CONTEXT_GC_SETTINGS.preserveRecentMessages),\n\t\t),\n\t\tminToolResultChars: Math.max(\n\t\t\t0,\n\t\t\tMath.floor(settings?.minToolResultChars ?? DEFAULT_CONTEXT_GC_SETTINGS.minToolResultChars),\n\t\t),\n\t\ttools: settings?.tools && settings.tools.length > 0 ? settings.tools : DEFAULT_CONTEXT_GC_SETTINGS.tools,\n\t\tsemanticMemory: normalizeSemanticMemoryGcSettings(settings?.semanticMemory),\n\t};\n}\n\nexport function getContextGcSettings(settings?: ContextGcSettings): NormalizedContextGcSettings {\n\treturn normalizeContextGcSettings(settings);\n}\n\nfunction textContentParts(content: unknown): string[] | undefined {\n\tif (typeof content === \"string\") return [content];\n\tif (!Array.isArray(content)) return undefined;\n\tconst parts: string[] = [];\n\tfor (const part of content) {\n\t\tif (typeof part !== \"object\" || part === null) return undefined;\n\t\tconst typed = part as { type?: string; text?: string; mimeType?: string };\n\t\tif (typed.type === \"text\" && typeof typed.text === \"string\") parts.push(typed.text);\n\t\telse if (typed.type === \"image\") return undefined;\n\t\telse return undefined;\n\t}\n\treturn parts;\n}\n\nfunction contentText(content: unknown): string | undefined {\n\tif (typeof content === \"string\") return content;\n\treturn textContentParts(content)?.join(\"\\n\");\n}\n\nfunction toolResultParts(message: ToolResultMessage): string[] {\n\tconst parts: string[] = [];\n\tfor (const part of message.content) {\n\t\tif (part.type === \"text\" && part.text) parts.push(part.text);\n\t\telse if (part.type === \"image\") parts.push(`[image ${part.mimeType}]`);\n\t}\n\treturn parts;\n}\n\nfunction toolResultText(message: ToolResultMessage): string {\n\treturn toolResultParts(message).join(\"\\n\");\n}\n\nfunction smallStringSlice(value: string, start?: number, end?: number): string {\n\tconst sliced = value.slice(start, end);\n\treturn sliced ? ` ${sliced}`.slice(1) : \"\";\n}\n\nfunction joinedPartsContainMarker(parts: string[], marker: string): boolean {\n\tif (marker.length === 0) return true;\n\tconst tailLength = marker.length - 1;\n\tlet tail = \"\";\n\tlet first = true;\n\tfor (const part of parts) {\n\t\tif (part.includes(marker)) return true;\n\t\tif (!first && `${tail}\\n${smallStringSlice(part, 0, tailLength)}`.includes(marker)) return true;\n\t\tif (tailLength === 0) tail = \"\";\n\t\telse if (part.length >= tailLength) tail = smallStringSlice(part, -tailLength);\n\t\telse tail = `${tail}${first ? \"\" : \"\\n\"}${part}`.slice(-tailLength);\n\t\tfirst = false;\n\t}\n\treturn false;\n}\n\nfunction joinedPartsContainAnyMarker(parts: string[], markers: readonly string[]): boolean {\n\treturn markers.some((marker) => joinedPartsContainMarker(parts, marker));\n}\n\nfunction isSemanticMemoryCustomMessage(message: AgentMessage): boolean {\n\tif (message.role !== \"custom\") return false;\n\tconst customType = String((message as { customType?: unknown }).customType ?? \"\").toLowerCase();\n\treturn customType.includes(\"automata\") || customType.includes(\"memory\") || customType.includes(\"mind\");\n}\n\nfunction agentMessageText(message: AgentMessage): string | undefined {\n\tif (message.role === \"toolResult\") return toolResultText(message);\n\tif (isSemanticMemoryCustomMessage(message)) return contentText((message as { content?: unknown }).content);\n\treturn undefined;\n}\n\nfunction semanticMessageHasMarker(message: AgentMessage, settings: Required<SemanticMemoryGcSettings>): boolean {\n\tif (message.role === \"toolResult\") return joinedPartsContainAnyMarker(toolResultParts(message), settings.markers);\n\tif (isSemanticMemoryCustomMessage(message)) {\n\t\tconst parts = textContentParts((message as { content?: unknown }).content);\n\t\treturn parts ? joinedPartsContainAnyMarker(parts, settings.markers) : false;\n\t}\n\treturn false;\n}\n\ninterface ContextGcPlan {\n\tcalls: Map<string, ToolCallMeta>;\n\tlatestReadByPath: Map<string, string>;\n\tsemanticIndexes: number[];\n}\n\nfunction normalizeToolPath(cwd: string, value: unknown): string | undefined {\n\tif (typeof value !== \"string\" || value.trim() === \"\") return undefined;\n\tconst path = value.trim();\n\treturn normalizePath(isAbsolute(path) ? path : resolve(cwd, path));\n}\n\nfunction collectContextGcPlan(\n\tmessages: AgentMessage[],\n\tcwd: string,\n\tsemanticSettings: Required<SemanticMemoryGcSettings>,\n): ContextGcPlan {\n\tconst calls = new Map<string, ToolCallMeta>();\n\tconst readResultCallIds: string[] = [];\n\tconst semanticIndexes: number[] = [];\n\n\tfor (let messageIndex = 0; messageIndex < messages.length; messageIndex++) {\n\t\tconst message = messages[messageIndex];\n\t\tif (message.role === \"assistant\") {\n\t\t\tfor (const part of message.content) {\n\t\t\t\tif (part.type !== \"toolCall\") continue;\n\t\t\t\tcalls.set(part.id, {\n\t\t\t\t\tid: part.id,\n\t\t\t\t\tname: part.name,\n\t\t\t\t\targs: part.arguments ?? {},\n\t\t\t\t\tmessageIndex,\n\t\t\t\t});\n\t\t\t}\n\t\t} else if (message.role === \"toolResult\" && message.toolName === \"read\") {\n\t\t\treadResultCallIds.push(message.toolCallId);\n\t\t}\n\n\t\tif (semanticSettings.enabled && semanticMessageHasMarker(message, semanticSettings)) {\n\t\t\tsemanticIndexes.push(messageIndex);\n\t\t}\n\t}\n\n\tconst latestReadByPath = new Map<string, string>();\n\tfor (const toolCallId of readResultCallIds) {\n\t\tconst call = calls.get(toolCallId);\n\t\tconst path = normalizeToolPath(cwd, call?.args.path);\n\t\tif (path) latestReadByPath.set(path, toolCallId);\n\t}\n\n\treturn { calls, latestReadByPath, semanticIndexes };\n}\n\nfunction storagePathFor(storageDir: string | undefined, key: string): string | undefined {\n\tif (!storageDir || !isAbsolute(storageDir)) return undefined;\n\treturn resolve(storageDir, `${key}.txt`);\n}\n\nfunction maybeStoreOriginal(options: ContextGcOptions, key: string, original: string): string | undefined {\n\tconst path = storagePathFor(options.storageDir, key);\n\tif (!path || !options.writePayloads) return path;\n\ttry {\n\t\tmkdirSync(options.storageDir!, { recursive: true });\n\t\tif (!existsSync(path)) writeFileSync(path, original, \"utf8\");\n\t} catch {\n\t\treturn undefined;\n\t}\n\treturn path;\n}\n\nfunction reasonText(record: ContextGcPackedRecord): string {\n\tif (record.reason === \"superseded-read\") return \"older read snapshot superseded by a later read of the same file\";\n\tif (record.reason === \"stale-semantic-memory\") {\n\t\treturn \"older Automata/Mind semantic context page outside the semantic-memory freshness window\";\n\t}\n\treturn \"stale bulky tool output outside the recent context window\";\n}\n\nfunction buildSummary(record: ContextGcPackedRecord): string {\n\tconst semantic = record.reason === \"stale-semantic-memory\";\n\tconst lines = [\n\t\tsemantic ? \"[Semantic GC packed stale Automata/Mind context page]\" : \"[Context GC packed stale tool result]\",\n\t\tsemantic ? undefined : `tool: ${record.toolName}`,\n\t\trecord.path ? `path: ${record.path}` : undefined,\n\t\trecord.command ? `command: ${cap(record.command)}` : undefined,\n\t\t`reason: ${reasonText(record)}`,\n\t\t`original: ${record.originalChars} chars (~${record.originalTokens} tokens)`,\n\t\trecord.digest ? `summary (auto-digest, machine paraphrase, not authoritative): ${record.digest}` : undefined,\n\t\trecord.storagePath\n\t\t\t? `exact old provider-visible text stored at: ${record.storagePath}`\n\t\t\t: \"exact old provider-visible text retained in the session log, not inline in provider context\",\n\t\tsemantic\n\t\t\t? \"If this memory context matters, query Automata/Mind again with the same topic/filter or fetch the drawer pointers from the stored page.\"\n\t\t\t: record.path\n\t\t\t\t? \"For current file contents, use the read tool on the path above. For the exact old output, read the stored payload path if present.\"\n\t\t\t\t: \"If this exact old output matters, retrieve/read the stored payload path if present or rerun the tool command.\",\n\t\t\"Do not rely on this summary as the original content.\",\n\t].filter((line): line is string => line !== undefined);\n\treturn lines.join(\"\\n\");\n}\n\nfunction gcDetails(message: { details?: unknown }, record: ContextGcPackedRecord): Record<string, unknown> {\n\treturn {\n\t\t...(typeof message.details === \"object\" && message.details !== null ? message.details : {}),\n\t\tcontextGc: {\n\t\t\tpacked: true,\n\t\t\toriginalChars: record.originalChars,\n\t\t\toriginalTokens: record.originalTokens,\n\t\t\tstoragePath: record.storagePath,\n\t\t\treason: record.reason,\n\t\t},\n\t};\n}\n\nfunction makePackedToolResult(message: ToolResultMessage, record: ContextGcPackedRecord): ToolResultMessage {\n\tconst summary = buildSummary(record);\n\treturn {\n\t\t...message,\n\t\tcontent: [{ type: \"text\", text: summary }],\n\t\tdetails: gcDetails(message, record),\n\t};\n}\n\nfunction makePackedSemanticMemoryMessage(message: AgentMessage, record: ContextGcPackedRecord): AgentMessage {\n\tconst summary = buildSummary(record);\n\treturn {\n\t\t...(message as unknown as Record<string, unknown>),\n\t\tcontent: [{ type: \"text\", text: summary }],\n\t\tdetails: gcDetails(message as { details?: unknown }, record),\n\t} as AgentMessage;\n}\n\nexport function applyContextGc(\n\tmessages: AgentMessage[],\n\trawSettings: ContextGcSettings & {\n\t\tcwd?: string;\n\t\tstorageDir?: string;\n\t\twritePayloads?: boolean;\n\t\tcuration?: ContextGcCurationHooks;\n\t},\n): ContextGcResult {\n\tconst settings = normalizeContextGcSettings(rawSettings);\n\tconst baseReport: ContextGcReport = {\n\t\tenabled: settings.enabled,\n\t\tpackedCount: 0,\n\t\toriginalTokens: 0,\n\t\tpackedTokens: 0,\n\t\tsavedTokens: 0,\n\t\trecords: [],\n\t};\n\tif (!settings.enabled) return { messages, report: baseReport };\n\n\tconst options: ContextGcOptions = {\n\t\t...settings,\n\t\tcwd: rawSettings.cwd ?? process.cwd(),\n\t\tstorageDir: rawSettings.storageDir,\n\t\twritePayloads: rawSettings.writePayloads ?? true,\n\t\tcuration: rawSettings.curation,\n\t};\n\tconst eligibleTools = new Set(options.tools);\n\tconst plan = collectContextGcPlan(messages, options.cwd, options.semanticMemory);\n\tconst recentStart = Math.max(0, messages.length - options.preserveRecentMessages);\n\tconst semanticIndexSet = new Set(plan.semanticIndexes);\n\tconst preservedSemanticIndexes = new Set(\n\t\toptions.semanticMemory.preserveRecentPages > 0\n\t\t\t? plan.semanticIndexes.slice(-options.semanticMemory.preserveRecentPages)\n\t\t\t: [],\n\t);\n\tconst nextMessages = messages.slice();\n\tlet changed = false;\n\n\tfor (let index = 0; index < messages.length; index++) {\n\t\tconst message = messages[index];\n\t\tif (semanticIndexSet.has(index) && !preservedSemanticIndexes.has(index) && index < recentStart) {\n\t\t\tconst originalText = agentMessageText(message);\n\t\t\tif (originalText && originalText.length >= options.semanticMemory.minChars) {\n\t\t\t\tconst originalTokens = estimateTokens(message);\n\t\t\t\tconst key = createHash(\"sha256\")\n\t\t\t\t\t.update(`semantic-memory\\0${index}\\0${originalText}`)\n\t\t\t\t\t.digest(\"hex\")\n\t\t\t\t\t.slice(0, 24);\n\t\t\t\tconst storagePath = maybeStoreOriginal(options, key, originalText);\n\t\t\t\tconst record: ContextGcPackedRecord = {\n\t\t\t\t\ttoolName: \"automata-mind\",\n\t\t\t\t\ttoolCallId: `semantic-${index}`,\n\t\t\t\t\tmessageIndex: index,\n\t\t\t\t\treason: \"stale-semantic-memory\",\n\t\t\t\t\toriginalChars: originalText.length,\n\t\t\t\t\toriginalTokens,\n\t\t\t\t\tpackedTokens: 0,\n\t\t\t\t\tstoragePath,\n\t\t\t\t\tkey,\n\t\t\t\t};\n\t\t\t\trecord.digest = options.curation?.resolveDigest?.(key);\n\t\t\t\toptions.curation?.onPacked?.(record, originalText);\n\t\t\t\tconst packed = makePackedSemanticMemoryMessage(message, record);\n\t\t\t\trecord.packedTokens = estimateTokens(packed);\n\t\t\t\tnextMessages[index] = packed;\n\t\t\t\tbaseReport.records.push(record);\n\t\t\t\tbaseReport.originalTokens += record.originalTokens;\n\t\t\t\tbaseReport.packedTokens += record.packedTokens;\n\t\t\t\tchanged = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tif (message.role !== \"toolResult\") continue;\n\t\tif (!eligibleTools.has(message.toolName)) continue;\n\t\tif (index >= recentStart) continue;\n\n\t\tconst originalText = toolResultText(message);\n\t\tif (originalText.length < options.minToolResultChars) continue;\n\n\t\tconst call = plan.calls.get(message.toolCallId);\n\t\tconst path = normalizeToolPath(options.cwd, call?.args.path);\n\t\tlet reason: ContextGcPackedRecord[\"reason\"] = \"stale-tool-result\";\n\t\tif (message.toolName === \"read\" && path) {\n\t\t\tif (plan.latestReadByPath.get(path) === message.toolCallId) continue;\n\t\t\treason = \"superseded-read\";\n\t\t}\n\n\t\tconst originalTokens = estimateTokens(message);\n\t\tconst key = createHash(\"sha256\")\n\t\t\t.update(`${message.toolName}\\0${message.toolCallId}\\0${originalText}`)\n\t\t\t.digest(\"hex\")\n\t\t\t.slice(0, 24);\n\t\tconst storagePath = maybeStoreOriginal(options, key, originalText);\n\t\tconst record: ContextGcPackedRecord = {\n\t\t\ttoolName: message.toolName,\n\t\t\ttoolCallId: message.toolCallId,\n\t\t\tmessageIndex: index,\n\t\t\treason,\n\t\t\toriginalChars: originalText.length,\n\t\t\toriginalTokens,\n\t\t\tpackedTokens: 0,\n\t\t\tstoragePath,\n\t\t\tpath,\n\t\t\tcommand: typeof call?.args.command === \"string\" ? call.args.command : undefined,\n\t\t\tkey,\n\t\t};\n\t\trecord.digest = options.curation?.resolveDigest?.(key);\n\t\toptions.curation?.onPacked?.(record, originalText);\n\t\tconst packed = makePackedToolResult(message, record);\n\t\trecord.packedTokens = estimateTokens(packed);\n\t\tnextMessages[index] = packed as AgentMessage;\n\t\tbaseReport.records.push(record);\n\t\tbaseReport.originalTokens += record.originalTokens;\n\t\tbaseReport.packedTokens += record.packedTokens;\n\t\tchanged = true;\n\t}\n\n\tbaseReport.packedCount = baseReport.records.length;\n\tbaseReport.savedTokens = Math.max(0, baseReport.originalTokens - baseReport.packedTokens);\n\treturn { messages: changed ? nextMessages : messages, report: baseReport };\n}\n"]}
1
+ {"version":3,"file":"context-gc.d.ts","sourceRoot":"","sources":["../../src/core/context-gc.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAK9D,MAAM,WAAW,wBAAwB;IACxC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,0EAA0E;IAC1E,mBAAmB,CAAC,EAAE,MAAM,CAAC;IAC7B,yFAAyF;IACzF,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,uEAAuE;IACvE,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;CACnB;AAED,MAAM,WAAW,iBAAiB;IACjC,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,oEAAoE;IACpE,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,gFAAgF;IAChF,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,oDAAoD;IACpD,KAAK,CAAC,EAAE,MAAM,EAAE,CAAC;IACjB,sFAAsF;IACtF,cAAc,CAAC,EAAE,wBAAwB,CAAC;CAC1C;AAED,MAAM,WAAW,2BAA4B,SAAQ,IAAI,CAAC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,gBAAgB,CAAC;IACvG,cAAc,EAAE,QAAQ,CAAC,wBAAwB,CAAC,CAAC;CACnD;AAED;;;;;;;GAOG;AACH,MAAM,WAAW,sBAAsB;IACtC,aAAa,CAAC,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IAC1D,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,qBAAqB,EAAE,YAAY,EAAE,MAAM,KAAK,IAAI,CAAC;CACzE;AAED,MAAM,WAAW,gBAAiB,SAAQ,2BAA2B;IACpE,GAAG,EAAE,MAAM,CAAC;IACZ,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,EAAE,sBAAsB,CAAC;CAClC;AAED,MAAM,WAAW,qBAAqB;IACrC,QAAQ,EAAE,MAAM,CAAC;IACjB,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,MAAM,EAAE,iBAAiB,GAAG,mBAAmB,GAAG,uBAAuB,CAAC;IAC1E,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;yCAEqC;IACrC,MAAM,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,eAAe;IAC/B,OAAO,EAAE,OAAO,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,YAAY,EAAE,MAAM,CAAC;IACrB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,qBAAqB,EAAE,CAAC;CACjC;AAED,MAAM,WAAW,eAAe;IAC/B,QAAQ,EAAE,YAAY,EAAE,CAAC;IACzB,MAAM,EAAE,eAAe,CAAC;CACxB;AAuBD,eAAO,MAAM,2BAA2B,EAAE,2BA4BzC,CAAC;AA6CF,wBAAgB,oBAAoB,CAAC,QAAQ,CAAC,EAAE,iBAAiB,GAAG,2BAA2B,CAE9F;AAwND,wBAAgB,cAAc,CAC7B,QAAQ,EAAE,YAAY,EAAE,EACxB,WAAW,EAAE,iBAAiB,GAAG;IAChC,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,QAAQ,CAAC,EAAE,sBAAsB,CAAC;CAClC,GACC,eAAe,CAkHjB","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { existsSync, mkdirSync, writeFileSync } from \"node:fs\";\nimport { isAbsolute, resolve } from \"node:path\";\nimport type { AgentMessage } from \"@caupulican/pi-agent-core\";\nimport type { ToolResultMessage } from \"@caupulican/pi-ai\";\nimport { normalizePath } from \"../utils/paths.ts\";\nimport { estimateTokens } from \"./compaction/compaction.ts\";\n\nexport interface SemanticMemoryGcSettings {\n\tenabled?: boolean;\n\t/** Number of newest Automata/Mind injected pages to preserve verbatim. */\n\tpreserveRecentPages?: number;\n\t/** Minimum provider-visible text chars before a stale semantic memory page is packed. */\n\tminChars?: number;\n\t/** Markers that identify deterministic Automata/Mind context pages. */\n\tmarkers?: string[];\n}\n\nexport interface ContextGcSettings {\n\tenabled?: boolean;\n\t/** Number of most recent AgentMessage rows to preserve verbatim. */\n\tpreserveRecentMessages?: number;\n\t/** Minimum provider-visible text chars before a stale tool result is packed. */\n\tminToolResultChars?: number;\n\t/** Tool names eligible for stale result packing. */\n\ttools?: string[];\n\t/** Provider-context control for deterministic Automata/Mind semantic memory pages. */\n\tsemanticMemory?: SemanticMemoryGcSettings;\n}\n\nexport interface NormalizedContextGcSettings extends Omit<Required<ContextGcSettings>, \"semanticMemory\"> {\n\tsemanticMemory: Required<SemanticMemoryGcSettings>;\n}\n\n/**\n * Brain-curation hooks (both optional; absent hooks are byte-for-byte today's behavior).\n * `resolveDigest` is a pure lookup keyed by the record's content hash — it must return the digest\n * already fenced in the untrusted-content boundary (e.g. BrainCurator.getDigest), with the fence's\n * nonce fixed once at store time, since GC re-renders the stub from raw messages on every request\n * and renders the returned string VERBATIM. `onPacked` lets the caller enqueue digest work with the\n * exact original text at the moment it is packed.\n */\nexport interface ContextGcCurationHooks {\n\tresolveDigest?: (digestKey: string) => string | undefined;\n\tonPacked?: (record: ContextGcPackedRecord, originalText: string) => void;\n}\n\nexport interface ContextGcOptions extends NormalizedContextGcSettings {\n\tcwd: string;\n\tstorageDir?: string;\n\twritePayloads?: boolean;\n\tcuration?: ContextGcCurationHooks;\n}\n\nexport interface ContextGcPackedRecord {\n\ttoolName: string;\n\ttoolCallId: string;\n\tmessageIndex: number;\n\treason: \"superseded-read\" | \"stale-tool-result\" | \"stale-semantic-memory\";\n\toriginalChars: number;\n\toriginalTokens: number;\n\tpackedTokens: number;\n\tstoragePath?: string;\n\tpath?: string;\n\tcommand?: string;\n\tkey?: string;\n\t/** Brain-curator semantic digest of the packed content (model-generated; advisory only). Arrives\n\t * already fenced in the untrusted-content boundary (see ContextGcCurationHooks.resolveDigest) —\n\t * render verbatim, never re-wrap. */\n\tdigest?: string;\n}\n\nexport interface ContextGcReport {\n\tenabled: boolean;\n\tpackedCount: number;\n\toriginalTokens: number;\n\tpackedTokens: number;\n\tsavedTokens: number;\n\trecords: ContextGcPackedRecord[];\n}\n\nexport interface ContextGcResult {\n\tmessages: AgentMessage[];\n\treport: ContextGcReport;\n}\n\nconst DEFAULT_SEMANTIC_MEMORY_GC_SETTINGS: Required<SemanticMemoryGcSettings> = {\n\tenabled: true,\n\tpreserveRecentPages: 1,\n\tminChars: 900,\n\tmarkers: [\n\t\t// Generic memory-subsystem recall page marker (brand-free). Provider-specific markers are\n\t\t// merged in dynamically at runtime via MemoryManager.getContextMarkers().\n\t\t\"<memory_context\",\n\t\t// Pre-existing provider-specific markers (to be generalized to provider-declared markers).\n\t\t\"<automata_context\",\n\t\t\"<automata_response\",\n\t\t\"<automata_query\",\n\t\t\"<automata_fetch\",\n\t\t\"<memory_lifecycle_audit\",\n\t\t\"<memory_lifecycle_purge\",\n\t\t\"<automata_doctor\",\n\t\t\"<automata_optimizer\",\n\t\t\"<automata_mesh\",\n\t],\n};\n\nexport const DEFAULT_CONTEXT_GC_SETTINGS: NormalizedContextGcSettings = {\n\tenabled: true,\n\tpreserveRecentMessages: 8,\n\tminToolResultChars: 1200,\n\ttools: [\n\t\t\"read\",\n\t\t\"bash\",\n\t\t\"rg\",\n\t\t\"grep\",\n\t\t\"find\",\n\t\t\"ls\",\n\t\t\"skill_open\",\n\t\t\"automata_graph_status\",\n\t\t\"automata_graph_search\",\n\t\t\"automata_graph_query\",\n\t\t\"automata_graph_neighbors\",\n\t\t\"automata_graph_path\",\n\t\t\"automata_graph_pointer_pack\",\n\t\t\"learning_query_memory\",\n\t\t\"subagent\",\n\t\t\"task_steps\",\n\t\t\"task_background\",\n\t\t\"task_goal\",\n\t\t\"run_ledger\",\n\t\t\"context_headroom_retrieve\",\n\t\t\"headroom_retrieve\",\n\t],\n\tsemanticMemory: DEFAULT_SEMANTIC_MEMORY_GC_SETTINGS,\n};\n\ntype ToolCallMeta = {\n\tid: string;\n\tname: string;\n\targs: Record<string, unknown>;\n\tmessageIndex: number;\n};\n\nfunction cap(text: string, limit = 220): string {\n\tconst compact = text.replace(/\\s+/g, \" \").trim();\n\treturn compact.length > limit ? `${compact.slice(0, Math.max(0, limit - 1))}…` : compact;\n}\n\nfunction normalizeSemanticMemoryGcSettings(settings?: SemanticMemoryGcSettings): Required<SemanticMemoryGcSettings> {\n\treturn {\n\t\tenabled: settings?.enabled ?? DEFAULT_SEMANTIC_MEMORY_GC_SETTINGS.enabled,\n\t\tpreserveRecentPages: Math.max(\n\t\t\t0,\n\t\t\tMath.floor(settings?.preserveRecentPages ?? DEFAULT_SEMANTIC_MEMORY_GC_SETTINGS.preserveRecentPages),\n\t\t),\n\t\tminChars: Math.max(0, Math.floor(settings?.minChars ?? DEFAULT_SEMANTIC_MEMORY_GC_SETTINGS.minChars)),\n\t\tmarkers:\n\t\t\tsettings?.markers && settings.markers.length > 0\n\t\t\t\t? settings.markers\n\t\t\t\t: DEFAULT_SEMANTIC_MEMORY_GC_SETTINGS.markers,\n\t};\n}\n\nfunction normalizeContextGcSettings(settings?: ContextGcSettings): NormalizedContextGcSettings {\n\treturn {\n\t\tenabled: settings?.enabled ?? DEFAULT_CONTEXT_GC_SETTINGS.enabled,\n\t\tpreserveRecentMessages: Math.max(\n\t\t\t0,\n\t\t\tMath.floor(settings?.preserveRecentMessages ?? DEFAULT_CONTEXT_GC_SETTINGS.preserveRecentMessages),\n\t\t),\n\t\tminToolResultChars: Math.max(\n\t\t\t0,\n\t\t\tMath.floor(settings?.minToolResultChars ?? DEFAULT_CONTEXT_GC_SETTINGS.minToolResultChars),\n\t\t),\n\t\ttools: settings?.tools && settings.tools.length > 0 ? settings.tools : DEFAULT_CONTEXT_GC_SETTINGS.tools,\n\t\tsemanticMemory: normalizeSemanticMemoryGcSettings(settings?.semanticMemory),\n\t};\n}\n\nexport function getContextGcSettings(settings?: ContextGcSettings): NormalizedContextGcSettings {\n\treturn normalizeContextGcSettings(settings);\n}\n\nfunction textContentParts(content: unknown): string[] | undefined {\n\tif (typeof content === \"string\") return [content];\n\tif (!Array.isArray(content)) return undefined;\n\tconst parts: string[] = [];\n\tfor (const part of content) {\n\t\tif (typeof part !== \"object\" || part === null) return undefined;\n\t\tconst typed = part as { type?: string; text?: string; mimeType?: string };\n\t\tif (typed.type === \"text\" && typeof typed.text === \"string\") parts.push(typed.text);\n\t\telse if (typed.type === \"image\") return undefined;\n\t\telse return undefined;\n\t}\n\treturn parts;\n}\n\nfunction contentText(content: unknown): string | undefined {\n\tif (typeof content === \"string\") return content;\n\treturn textContentParts(content)?.join(\"\\n\");\n}\n\nfunction toolResultParts(message: ToolResultMessage): string[] {\n\tconst parts: string[] = [];\n\tfor (const part of message.content) {\n\t\tif (part.type === \"text\" && part.text) parts.push(part.text);\n\t\telse if (part.type === \"image\") parts.push(`[image ${part.mimeType}]`);\n\t}\n\treturn parts;\n}\n\nfunction toolResultText(message: ToolResultMessage): string {\n\treturn toolResultParts(message).join(\"\\n\");\n}\n\nfunction smallStringSlice(value: string, start?: number, end?: number): string {\n\tconst sliced = value.slice(start, end);\n\treturn sliced ? ` ${sliced}`.slice(1) : \"\";\n}\n\nfunction joinedPartsContainMarker(parts: string[], marker: string): boolean {\n\tif (marker.length === 0) return true;\n\tconst tailLength = marker.length - 1;\n\tlet tail = \"\";\n\tlet first = true;\n\tfor (const part of parts) {\n\t\tif (part.includes(marker)) return true;\n\t\tif (!first && `${tail}\\n${smallStringSlice(part, 0, tailLength)}`.includes(marker)) return true;\n\t\tif (tailLength === 0) tail = \"\";\n\t\telse if (part.length >= tailLength) tail = smallStringSlice(part, -tailLength);\n\t\telse tail = `${tail}${first ? \"\" : \"\\n\"}${part}`.slice(-tailLength);\n\t\tfirst = false;\n\t}\n\treturn false;\n}\n\nfunction joinedPartsContainAnyMarker(parts: string[], markers: readonly string[]): boolean {\n\treturn markers.some((marker) => joinedPartsContainMarker(parts, marker));\n}\n\nfunction isSemanticMemoryCustomMessage(message: AgentMessage): boolean {\n\tif (message.role !== \"custom\") return false;\n\tconst customType = String((message as { customType?: unknown }).customType ?? \"\").toLowerCase();\n\treturn customType.includes(\"automata\") || customType.includes(\"memory\") || customType.includes(\"mind\");\n}\n\nfunction agentMessageText(message: AgentMessage): string | undefined {\n\tif (message.role === \"toolResult\") return toolResultText(message);\n\tif (isSemanticMemoryCustomMessage(message)) return contentText((message as { content?: unknown }).content);\n\treturn undefined;\n}\n\nfunction semanticMessageHasMarker(message: AgentMessage, settings: Required<SemanticMemoryGcSettings>): boolean {\n\tif (message.role === \"toolResult\") return joinedPartsContainAnyMarker(toolResultParts(message), settings.markers);\n\tif (isSemanticMemoryCustomMessage(message)) {\n\t\tconst parts = textContentParts((message as { content?: unknown }).content);\n\t\treturn parts ? joinedPartsContainAnyMarker(parts, settings.markers) : false;\n\t}\n\treturn false;\n}\n\ninterface ContextGcPlan {\n\tcalls: Map<string, ToolCallMeta>;\n\tlatestReadByPath: Map<string, string>;\n\tsemanticIndexes: number[];\n}\n\nfunction normalizeToolPath(cwd: string, value: unknown): string | undefined {\n\tif (typeof value !== \"string\" || value.trim() === \"\") return undefined;\n\tconst path = value.trim();\n\treturn normalizePath(isAbsolute(path) ? path : resolve(cwd, path));\n}\n\nfunction collectContextGcPlan(\n\tmessages: AgentMessage[],\n\tcwd: string,\n\tsemanticSettings: Required<SemanticMemoryGcSettings>,\n): ContextGcPlan {\n\tconst calls = new Map<string, ToolCallMeta>();\n\tconst readResultCallIds: string[] = [];\n\tconst semanticIndexes: number[] = [];\n\n\tfor (let messageIndex = 0; messageIndex < messages.length; messageIndex++) {\n\t\tconst message = messages[messageIndex];\n\t\tif (message.role === \"assistant\") {\n\t\t\tfor (const part of message.content) {\n\t\t\t\tif (part.type !== \"toolCall\") continue;\n\t\t\t\tcalls.set(part.id, {\n\t\t\t\t\tid: part.id,\n\t\t\t\t\tname: part.name,\n\t\t\t\t\targs: part.arguments ?? {},\n\t\t\t\t\tmessageIndex,\n\t\t\t\t});\n\t\t\t}\n\t\t} else if (message.role === \"toolResult\" && message.toolName === \"read\") {\n\t\t\treadResultCallIds.push(message.toolCallId);\n\t\t}\n\n\t\tif (semanticSettings.enabled && semanticMessageHasMarker(message, semanticSettings)) {\n\t\t\tsemanticIndexes.push(messageIndex);\n\t\t}\n\t}\n\n\tconst latestReadByPath = new Map<string, string>();\n\tfor (const toolCallId of readResultCallIds) {\n\t\tconst call = calls.get(toolCallId);\n\t\tconst path = normalizeToolPath(cwd, call?.args.path);\n\t\tif (path) latestReadByPath.set(path, toolCallId);\n\t}\n\n\treturn { calls, latestReadByPath, semanticIndexes };\n}\n\nfunction storagePathFor(storageDir: string | undefined, key: string): string | undefined {\n\tif (!storageDir || !isAbsolute(storageDir)) return undefined;\n\treturn resolve(storageDir, `${key}.txt`);\n}\n\nfunction maybeStoreOriginal(options: ContextGcOptions, key: string, original: string): string | undefined {\n\tconst path = storagePathFor(options.storageDir, key);\n\tif (!path || !options.writePayloads) return path;\n\ttry {\n\t\tmkdirSync(options.storageDir!, { recursive: true });\n\t\tif (!existsSync(path)) writeFileSync(path, original, \"utf8\");\n\t} catch {\n\t\treturn undefined;\n\t}\n\treturn path;\n}\n\nfunction reasonText(record: ContextGcPackedRecord): string {\n\tif (record.reason === \"superseded-read\") return \"older read snapshot superseded by a later read of the same file\";\n\tif (record.reason === \"stale-semantic-memory\") {\n\t\treturn \"older Automata/Mind semantic context page outside the semantic-memory freshness window\";\n\t}\n\treturn \"stale bulky tool output outside the recent context window\";\n}\n\nfunction buildSummary(record: ContextGcPackedRecord): string {\n\tconst semantic = record.reason === \"stale-semantic-memory\";\n\tconst lines = [\n\t\tsemantic ? \"[Semantic GC packed stale Automata/Mind context page]\" : \"[Context GC packed stale tool result]\",\n\t\tsemantic ? undefined : `tool: ${record.toolName}`,\n\t\trecord.path ? `path: ${record.path}` : undefined,\n\t\trecord.command ? `command: ${cap(record.command)}` : undefined,\n\t\t`reason: ${reasonText(record)}`,\n\t\t`original: ${record.originalChars} chars (~${record.originalTokens} tokens)`,\n\t\t// The digest arrives PRE-FENCED from the curator (brain-curator.ts wraps it once, in the\n\t\t// standard untrusted-content boundary — the same treatment memory recall pages get — at the\n\t\t// moment it is stored, not here). Render it VERBATIM: re-wrapping on every GC render would\n\t\t// regenerate the fence's nonce each time, making the packed stub byte-different on every\n\t\t// provider request and busting prompt caching (BUG E).\n\t\trecord.digest ? `summary (auto-digest, machine paraphrase, not authoritative): ${record.digest}` : undefined,\n\t\trecord.storagePath\n\t\t\t? `exact old provider-visible text stored at: ${record.storagePath}`\n\t\t\t: \"exact old provider-visible text retained in the session log, not inline in provider context\",\n\t\tsemantic\n\t\t\t? \"If this memory context matters, query Automata/Mind again with the same topic/filter or fetch the drawer pointers from the stored page.\"\n\t\t\t: record.path\n\t\t\t\t? \"For current file contents, use the read tool on the path above. For the exact old output, read the stored payload path if present.\"\n\t\t\t\t: \"If this exact old output matters, retrieve/read the stored payload path if present or rerun the tool command.\",\n\t\t\"Do not rely on this summary as the original content.\",\n\t].filter((line): line is string => line !== undefined);\n\treturn lines.join(\"\\n\");\n}\n\nfunction gcDetails(message: { details?: unknown }, record: ContextGcPackedRecord): Record<string, unknown> {\n\treturn {\n\t\t...(typeof message.details === \"object\" && message.details !== null ? message.details : {}),\n\t\tcontextGc: {\n\t\t\tpacked: true,\n\t\t\toriginalChars: record.originalChars,\n\t\t\toriginalTokens: record.originalTokens,\n\t\t\tstoragePath: record.storagePath,\n\t\t\treason: record.reason,\n\t\t},\n\t};\n}\n\nfunction makePackedToolResult(message: ToolResultMessage, record: ContextGcPackedRecord): ToolResultMessage {\n\tconst summary = buildSummary(record);\n\treturn {\n\t\t...message,\n\t\tcontent: [{ type: \"text\", text: summary }],\n\t\tdetails: gcDetails(message, record),\n\t};\n}\n\nfunction makePackedSemanticMemoryMessage(message: AgentMessage, record: ContextGcPackedRecord): AgentMessage {\n\tconst summary = buildSummary(record);\n\treturn {\n\t\t...(message as unknown as Record<string, unknown>),\n\t\tcontent: [{ type: \"text\", text: summary }],\n\t\tdetails: gcDetails(message as { details?: unknown }, record),\n\t} as AgentMessage;\n}\n\nexport function applyContextGc(\n\tmessages: AgentMessage[],\n\trawSettings: ContextGcSettings & {\n\t\tcwd?: string;\n\t\tstorageDir?: string;\n\t\twritePayloads?: boolean;\n\t\tcuration?: ContextGcCurationHooks;\n\t},\n): ContextGcResult {\n\tconst settings = normalizeContextGcSettings(rawSettings);\n\tconst baseReport: ContextGcReport = {\n\t\tenabled: settings.enabled,\n\t\tpackedCount: 0,\n\t\toriginalTokens: 0,\n\t\tpackedTokens: 0,\n\t\tsavedTokens: 0,\n\t\trecords: [],\n\t};\n\tif (!settings.enabled) return { messages, report: baseReport };\n\n\tconst options: ContextGcOptions = {\n\t\t...settings,\n\t\tcwd: rawSettings.cwd ?? process.cwd(),\n\t\tstorageDir: rawSettings.storageDir,\n\t\twritePayloads: rawSettings.writePayloads ?? true,\n\t\tcuration: rawSettings.curation,\n\t};\n\tconst eligibleTools = new Set(options.tools);\n\tconst plan = collectContextGcPlan(messages, options.cwd, options.semanticMemory);\n\tconst recentStart = Math.max(0, messages.length - options.preserveRecentMessages);\n\tconst semanticIndexSet = new Set(plan.semanticIndexes);\n\tconst preservedSemanticIndexes = new Set(\n\t\toptions.semanticMemory.preserveRecentPages > 0\n\t\t\t? plan.semanticIndexes.slice(-options.semanticMemory.preserveRecentPages)\n\t\t\t: [],\n\t);\n\tconst nextMessages = messages.slice();\n\tlet changed = false;\n\n\tfor (let index = 0; index < messages.length; index++) {\n\t\tconst message = messages[index];\n\t\tif (semanticIndexSet.has(index) && !preservedSemanticIndexes.has(index) && index < recentStart) {\n\t\t\tconst originalText = agentMessageText(message);\n\t\t\tif (originalText && originalText.length >= options.semanticMemory.minChars) {\n\t\t\t\tconst originalTokens = estimateTokens(message);\n\t\t\t\tconst key = createHash(\"sha256\")\n\t\t\t\t\t.update(`semantic-memory\\0${index}\\0${originalText}`)\n\t\t\t\t\t.digest(\"hex\")\n\t\t\t\t\t.slice(0, 24);\n\t\t\t\tconst storagePath = maybeStoreOriginal(options, key, originalText);\n\t\t\t\tconst record: ContextGcPackedRecord = {\n\t\t\t\t\ttoolName: \"automata-mind\",\n\t\t\t\t\ttoolCallId: `semantic-${index}`,\n\t\t\t\t\tmessageIndex: index,\n\t\t\t\t\treason: \"stale-semantic-memory\",\n\t\t\t\t\toriginalChars: originalText.length,\n\t\t\t\t\toriginalTokens,\n\t\t\t\t\tpackedTokens: 0,\n\t\t\t\t\tstoragePath,\n\t\t\t\t\tkey,\n\t\t\t\t};\n\t\t\t\trecord.digest = options.curation?.resolveDigest?.(key);\n\t\t\t\toptions.curation?.onPacked?.(record, originalText);\n\t\t\t\tconst packed = makePackedSemanticMemoryMessage(message, record);\n\t\t\t\trecord.packedTokens = estimateTokens(packed);\n\t\t\t\tnextMessages[index] = packed;\n\t\t\t\tbaseReport.records.push(record);\n\t\t\t\tbaseReport.originalTokens += record.originalTokens;\n\t\t\t\tbaseReport.packedTokens += record.packedTokens;\n\t\t\t\tchanged = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tif (message.role !== \"toolResult\") continue;\n\t\tif (!eligibleTools.has(message.toolName)) continue;\n\t\tif (index >= recentStart) continue;\n\n\t\tconst originalText = toolResultText(message);\n\t\tif (originalText.length < options.minToolResultChars) continue;\n\n\t\tconst call = plan.calls.get(message.toolCallId);\n\t\tconst path = normalizeToolPath(options.cwd, call?.args.path);\n\t\tlet reason: ContextGcPackedRecord[\"reason\"] = \"stale-tool-result\";\n\t\tif (message.toolName === \"read\" && path) {\n\t\t\tif (plan.latestReadByPath.get(path) === message.toolCallId) continue;\n\t\t\treason = \"superseded-read\";\n\t\t}\n\n\t\tconst originalTokens = estimateTokens(message);\n\t\tconst key = createHash(\"sha256\")\n\t\t\t.update(`${message.toolName}\\0${message.toolCallId}\\0${originalText}`)\n\t\t\t.digest(\"hex\")\n\t\t\t.slice(0, 24);\n\t\tconst storagePath = maybeStoreOriginal(options, key, originalText);\n\t\tconst record: ContextGcPackedRecord = {\n\t\t\ttoolName: message.toolName,\n\t\t\ttoolCallId: message.toolCallId,\n\t\t\tmessageIndex: index,\n\t\t\treason,\n\t\t\toriginalChars: originalText.length,\n\t\t\toriginalTokens,\n\t\t\tpackedTokens: 0,\n\t\t\tstoragePath,\n\t\t\tpath,\n\t\t\tcommand: typeof call?.args.command === \"string\" ? call.args.command : undefined,\n\t\t\tkey,\n\t\t};\n\t\trecord.digest = options.curation?.resolveDigest?.(key);\n\t\toptions.curation?.onPacked?.(record, originalText);\n\t\tconst packed = makePackedToolResult(message, record);\n\t\trecord.packedTokens = estimateTokens(packed);\n\t\tnextMessages[index] = packed as AgentMessage;\n\t\tbaseReport.records.push(record);\n\t\tbaseReport.originalTokens += record.originalTokens;\n\t\tbaseReport.packedTokens += record.packedTokens;\n\t\tchanged = true;\n\t}\n\n\tbaseReport.packedCount = baseReport.records.length;\n\tbaseReport.savedTokens = Math.max(0, baseReport.originalTokens - baseReport.packedTokens);\n\treturn { messages: changed ? nextMessages : messages, report: baseReport };\n}\n"]}
@@ -241,6 +241,11 @@ function buildSummary(record) {
241
241
  record.command ? `command: ${cap(record.command)}` : undefined,
242
242
  `reason: ${reasonText(record)}`,
243
243
  `original: ${record.originalChars} chars (~${record.originalTokens} tokens)`,
244
+ // The digest arrives PRE-FENCED from the curator (brain-curator.ts wraps it once, in the
245
+ // standard untrusted-content boundary — the same treatment memory recall pages get — at the
246
+ // moment it is stored, not here). Render it VERBATIM: re-wrapping on every GC render would
247
+ // regenerate the fence's nonce each time, making the packed stub byte-different on every
248
+ // provider request and busting prompt caching (BUG E).
244
249
  record.digest ? `summary (auto-digest, machine paraphrase, not authoritative): ${record.digest}` : undefined,
245
250
  record.storagePath
246
251
  ? `exact old provider-visible text stored at: ${record.storagePath}`
@@ -1 +1 @@
1
- {"version":3,"file":"context-gc.js","sourceRoot":"","sources":["../../src/core/context-gc.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { existsSync, mkdirSync, writeFileSync } from \"node:fs\";\nimport { isAbsolute, resolve } from \"node:path\";\nimport type { AgentMessage } from \"@caupulican/pi-agent-core\";\nimport type { ToolResultMessage } from \"@caupulican/pi-ai\";\nimport { normalizePath } from \"../utils/paths.ts\";\nimport { estimateTokens } from \"./compaction/compaction.ts\";\n\nexport interface SemanticMemoryGcSettings {\n\tenabled?: boolean;\n\t/** Number of newest Automata/Mind injected pages to preserve verbatim. */\n\tpreserveRecentPages?: number;\n\t/** Minimum provider-visible text chars before a stale semantic memory page is packed. */\n\tminChars?: number;\n\t/** Markers that identify deterministic Automata/Mind context pages. */\n\tmarkers?: string[];\n}\n\nexport interface ContextGcSettings {\n\tenabled?: boolean;\n\t/** Number of most recent AgentMessage rows to preserve verbatim. */\n\tpreserveRecentMessages?: number;\n\t/** Minimum provider-visible text chars before a stale tool result is packed. */\n\tminToolResultChars?: number;\n\t/** Tool names eligible for stale result packing. */\n\ttools?: string[];\n\t/** Provider-context control for deterministic Automata/Mind semantic memory pages. */\n\tsemanticMemory?: SemanticMemoryGcSettings;\n}\n\nexport interface NormalizedContextGcSettings extends Omit<Required<ContextGcSettings>, \"semanticMemory\"> {\n\tsemanticMemory: Required<SemanticMemoryGcSettings>;\n}\n\n/**\n * Brain-curation hooks (both optional; absent hooks are byte-for-byte today's behavior).\n * `resolveDigest` is a pure lookup keyed by the record's content hash; `onPacked` lets the\n * caller enqueue digest work with the exact original text at the moment it is packed.\n */\nexport interface ContextGcCurationHooks {\n\tresolveDigest?: (digestKey: string) => string | undefined;\n\tonPacked?: (record: ContextGcPackedRecord, originalText: string) => void;\n}\n\nexport interface ContextGcOptions extends NormalizedContextGcSettings {\n\tcwd: string;\n\tstorageDir?: string;\n\twritePayloads?: boolean;\n\tcuration?: ContextGcCurationHooks;\n}\n\nexport interface ContextGcPackedRecord {\n\ttoolName: string;\n\ttoolCallId: string;\n\tmessageIndex: number;\n\treason: \"superseded-read\" | \"stale-tool-result\" | \"stale-semantic-memory\";\n\toriginalChars: number;\n\toriginalTokens: number;\n\tpackedTokens: number;\n\tstoragePath?: string;\n\tpath?: string;\n\tcommand?: string;\n\tkey?: string;\n\t/** Brain-curator semantic digest of the packed content (model-generated; advisory only). */\n\tdigest?: string;\n}\n\nexport interface ContextGcReport {\n\tenabled: boolean;\n\tpackedCount: number;\n\toriginalTokens: number;\n\tpackedTokens: number;\n\tsavedTokens: number;\n\trecords: ContextGcPackedRecord[];\n}\n\nexport interface ContextGcResult {\n\tmessages: AgentMessage[];\n\treport: ContextGcReport;\n}\n\nconst DEFAULT_SEMANTIC_MEMORY_GC_SETTINGS: Required<SemanticMemoryGcSettings> = {\n\tenabled: true,\n\tpreserveRecentPages: 1,\n\tminChars: 900,\n\tmarkers: [\n\t\t// Generic memory-subsystem recall page marker (brand-free). Provider-specific markers are\n\t\t// merged in dynamically at runtime via MemoryManager.getContextMarkers().\n\t\t\"<memory_context\",\n\t\t// Pre-existing provider-specific markers (to be generalized to provider-declared markers).\n\t\t\"<automata_context\",\n\t\t\"<automata_response\",\n\t\t\"<automata_query\",\n\t\t\"<automata_fetch\",\n\t\t\"<memory_lifecycle_audit\",\n\t\t\"<memory_lifecycle_purge\",\n\t\t\"<automata_doctor\",\n\t\t\"<automata_optimizer\",\n\t\t\"<automata_mesh\",\n\t],\n};\n\nexport const DEFAULT_CONTEXT_GC_SETTINGS: NormalizedContextGcSettings = {\n\tenabled: true,\n\tpreserveRecentMessages: 8,\n\tminToolResultChars: 1200,\n\ttools: [\n\t\t\"read\",\n\t\t\"bash\",\n\t\t\"rg\",\n\t\t\"grep\",\n\t\t\"find\",\n\t\t\"ls\",\n\t\t\"skill_open\",\n\t\t\"automata_graph_status\",\n\t\t\"automata_graph_search\",\n\t\t\"automata_graph_query\",\n\t\t\"automata_graph_neighbors\",\n\t\t\"automata_graph_path\",\n\t\t\"automata_graph_pointer_pack\",\n\t\t\"learning_query_memory\",\n\t\t\"subagent\",\n\t\t\"task_steps\",\n\t\t\"task_background\",\n\t\t\"task_goal\",\n\t\t\"run_ledger\",\n\t\t\"context_headroom_retrieve\",\n\t\t\"headroom_retrieve\",\n\t],\n\tsemanticMemory: DEFAULT_SEMANTIC_MEMORY_GC_SETTINGS,\n};\n\ntype ToolCallMeta = {\n\tid: string;\n\tname: string;\n\targs: Record<string, unknown>;\n\tmessageIndex: number;\n};\n\nfunction cap(text: string, limit = 220): string {\n\tconst compact = text.replace(/\\s+/g, \" \").trim();\n\treturn compact.length > limit ? `${compact.slice(0, Math.max(0, limit - 1))}…` : compact;\n}\n\nfunction normalizeSemanticMemoryGcSettings(settings?: SemanticMemoryGcSettings): Required<SemanticMemoryGcSettings> {\n\treturn {\n\t\tenabled: settings?.enabled ?? DEFAULT_SEMANTIC_MEMORY_GC_SETTINGS.enabled,\n\t\tpreserveRecentPages: Math.max(\n\t\t\t0,\n\t\t\tMath.floor(settings?.preserveRecentPages ?? DEFAULT_SEMANTIC_MEMORY_GC_SETTINGS.preserveRecentPages),\n\t\t),\n\t\tminChars: Math.max(0, Math.floor(settings?.minChars ?? DEFAULT_SEMANTIC_MEMORY_GC_SETTINGS.minChars)),\n\t\tmarkers:\n\t\t\tsettings?.markers && settings.markers.length > 0\n\t\t\t\t? settings.markers\n\t\t\t\t: DEFAULT_SEMANTIC_MEMORY_GC_SETTINGS.markers,\n\t};\n}\n\nfunction normalizeContextGcSettings(settings?: ContextGcSettings): NormalizedContextGcSettings {\n\treturn {\n\t\tenabled: settings?.enabled ?? DEFAULT_CONTEXT_GC_SETTINGS.enabled,\n\t\tpreserveRecentMessages: Math.max(\n\t\t\t0,\n\t\t\tMath.floor(settings?.preserveRecentMessages ?? DEFAULT_CONTEXT_GC_SETTINGS.preserveRecentMessages),\n\t\t),\n\t\tminToolResultChars: Math.max(\n\t\t\t0,\n\t\t\tMath.floor(settings?.minToolResultChars ?? DEFAULT_CONTEXT_GC_SETTINGS.minToolResultChars),\n\t\t),\n\t\ttools: settings?.tools && settings.tools.length > 0 ? settings.tools : DEFAULT_CONTEXT_GC_SETTINGS.tools,\n\t\tsemanticMemory: normalizeSemanticMemoryGcSettings(settings?.semanticMemory),\n\t};\n}\n\nexport function getContextGcSettings(settings?: ContextGcSettings): NormalizedContextGcSettings {\n\treturn normalizeContextGcSettings(settings);\n}\n\nfunction textContentParts(content: unknown): string[] | undefined {\n\tif (typeof content === \"string\") return [content];\n\tif (!Array.isArray(content)) return undefined;\n\tconst parts: string[] = [];\n\tfor (const part of content) {\n\t\tif (typeof part !== \"object\" || part === null) return undefined;\n\t\tconst typed = part as { type?: string; text?: string; mimeType?: string };\n\t\tif (typed.type === \"text\" && typeof typed.text === \"string\") parts.push(typed.text);\n\t\telse if (typed.type === \"image\") return undefined;\n\t\telse return undefined;\n\t}\n\treturn parts;\n}\n\nfunction contentText(content: unknown): string | undefined {\n\tif (typeof content === \"string\") return content;\n\treturn textContentParts(content)?.join(\"\\n\");\n}\n\nfunction toolResultParts(message: ToolResultMessage): string[] {\n\tconst parts: string[] = [];\n\tfor (const part of message.content) {\n\t\tif (part.type === \"text\" && part.text) parts.push(part.text);\n\t\telse if (part.type === \"image\") parts.push(`[image ${part.mimeType}]`);\n\t}\n\treturn parts;\n}\n\nfunction toolResultText(message: ToolResultMessage): string {\n\treturn toolResultParts(message).join(\"\\n\");\n}\n\nfunction smallStringSlice(value: string, start?: number, end?: number): string {\n\tconst sliced = value.slice(start, end);\n\treturn sliced ? ` ${sliced}`.slice(1) : \"\";\n}\n\nfunction joinedPartsContainMarker(parts: string[], marker: string): boolean {\n\tif (marker.length === 0) return true;\n\tconst tailLength = marker.length - 1;\n\tlet tail = \"\";\n\tlet first = true;\n\tfor (const part of parts) {\n\t\tif (part.includes(marker)) return true;\n\t\tif (!first && `${tail}\\n${smallStringSlice(part, 0, tailLength)}`.includes(marker)) return true;\n\t\tif (tailLength === 0) tail = \"\";\n\t\telse if (part.length >= tailLength) tail = smallStringSlice(part, -tailLength);\n\t\telse tail = `${tail}${first ? \"\" : \"\\n\"}${part}`.slice(-tailLength);\n\t\tfirst = false;\n\t}\n\treturn false;\n}\n\nfunction joinedPartsContainAnyMarker(parts: string[], markers: readonly string[]): boolean {\n\treturn markers.some((marker) => joinedPartsContainMarker(parts, marker));\n}\n\nfunction isSemanticMemoryCustomMessage(message: AgentMessage): boolean {\n\tif (message.role !== \"custom\") return false;\n\tconst customType = String((message as { customType?: unknown }).customType ?? \"\").toLowerCase();\n\treturn customType.includes(\"automata\") || customType.includes(\"memory\") || customType.includes(\"mind\");\n}\n\nfunction agentMessageText(message: AgentMessage): string | undefined {\n\tif (message.role === \"toolResult\") return toolResultText(message);\n\tif (isSemanticMemoryCustomMessage(message)) return contentText((message as { content?: unknown }).content);\n\treturn undefined;\n}\n\nfunction semanticMessageHasMarker(message: AgentMessage, settings: Required<SemanticMemoryGcSettings>): boolean {\n\tif (message.role === \"toolResult\") return joinedPartsContainAnyMarker(toolResultParts(message), settings.markers);\n\tif (isSemanticMemoryCustomMessage(message)) {\n\t\tconst parts = textContentParts((message as { content?: unknown }).content);\n\t\treturn parts ? joinedPartsContainAnyMarker(parts, settings.markers) : false;\n\t}\n\treturn false;\n}\n\ninterface ContextGcPlan {\n\tcalls: Map<string, ToolCallMeta>;\n\tlatestReadByPath: Map<string, string>;\n\tsemanticIndexes: number[];\n}\n\nfunction normalizeToolPath(cwd: string, value: unknown): string | undefined {\n\tif (typeof value !== \"string\" || value.trim() === \"\") return undefined;\n\tconst path = value.trim();\n\treturn normalizePath(isAbsolute(path) ? path : resolve(cwd, path));\n}\n\nfunction collectContextGcPlan(\n\tmessages: AgentMessage[],\n\tcwd: string,\n\tsemanticSettings: Required<SemanticMemoryGcSettings>,\n): ContextGcPlan {\n\tconst calls = new Map<string, ToolCallMeta>();\n\tconst readResultCallIds: string[] = [];\n\tconst semanticIndexes: number[] = [];\n\n\tfor (let messageIndex = 0; messageIndex < messages.length; messageIndex++) {\n\t\tconst message = messages[messageIndex];\n\t\tif (message.role === \"assistant\") {\n\t\t\tfor (const part of message.content) {\n\t\t\t\tif (part.type !== \"toolCall\") continue;\n\t\t\t\tcalls.set(part.id, {\n\t\t\t\t\tid: part.id,\n\t\t\t\t\tname: part.name,\n\t\t\t\t\targs: part.arguments ?? {},\n\t\t\t\t\tmessageIndex,\n\t\t\t\t});\n\t\t\t}\n\t\t} else if (message.role === \"toolResult\" && message.toolName === \"read\") {\n\t\t\treadResultCallIds.push(message.toolCallId);\n\t\t}\n\n\t\tif (semanticSettings.enabled && semanticMessageHasMarker(message, semanticSettings)) {\n\t\t\tsemanticIndexes.push(messageIndex);\n\t\t}\n\t}\n\n\tconst latestReadByPath = new Map<string, string>();\n\tfor (const toolCallId of readResultCallIds) {\n\t\tconst call = calls.get(toolCallId);\n\t\tconst path = normalizeToolPath(cwd, call?.args.path);\n\t\tif (path) latestReadByPath.set(path, toolCallId);\n\t}\n\n\treturn { calls, latestReadByPath, semanticIndexes };\n}\n\nfunction storagePathFor(storageDir: string | undefined, key: string): string | undefined {\n\tif (!storageDir || !isAbsolute(storageDir)) return undefined;\n\treturn resolve(storageDir, `${key}.txt`);\n}\n\nfunction maybeStoreOriginal(options: ContextGcOptions, key: string, original: string): string | undefined {\n\tconst path = storagePathFor(options.storageDir, key);\n\tif (!path || !options.writePayloads) return path;\n\ttry {\n\t\tmkdirSync(options.storageDir!, { recursive: true });\n\t\tif (!existsSync(path)) writeFileSync(path, original, \"utf8\");\n\t} catch {\n\t\treturn undefined;\n\t}\n\treturn path;\n}\n\nfunction reasonText(record: ContextGcPackedRecord): string {\n\tif (record.reason === \"superseded-read\") return \"older read snapshot superseded by a later read of the same file\";\n\tif (record.reason === \"stale-semantic-memory\") {\n\t\treturn \"older Automata/Mind semantic context page outside the semantic-memory freshness window\";\n\t}\n\treturn \"stale bulky tool output outside the recent context window\";\n}\n\nfunction buildSummary(record: ContextGcPackedRecord): string {\n\tconst semantic = record.reason === \"stale-semantic-memory\";\n\tconst lines = [\n\t\tsemantic ? \"[Semantic GC packed stale Automata/Mind context page]\" : \"[Context GC packed stale tool result]\",\n\t\tsemantic ? undefined : `tool: ${record.toolName}`,\n\t\trecord.path ? `path: ${record.path}` : undefined,\n\t\trecord.command ? `command: ${cap(record.command)}` : undefined,\n\t\t`reason: ${reasonText(record)}`,\n\t\t`original: ${record.originalChars} chars (~${record.originalTokens} tokens)`,\n\t\trecord.digest ? `summary (auto-digest, machine paraphrase, not authoritative): ${record.digest}` : undefined,\n\t\trecord.storagePath\n\t\t\t? `exact old provider-visible text stored at: ${record.storagePath}`\n\t\t\t: \"exact old provider-visible text retained in the session log, not inline in provider context\",\n\t\tsemantic\n\t\t\t? \"If this memory context matters, query Automata/Mind again with the same topic/filter or fetch the drawer pointers from the stored page.\"\n\t\t\t: record.path\n\t\t\t\t? \"For current file contents, use the read tool on the path above. For the exact old output, read the stored payload path if present.\"\n\t\t\t\t: \"If this exact old output matters, retrieve/read the stored payload path if present or rerun the tool command.\",\n\t\t\"Do not rely on this summary as the original content.\",\n\t].filter((line): line is string => line !== undefined);\n\treturn lines.join(\"\\n\");\n}\n\nfunction gcDetails(message: { details?: unknown }, record: ContextGcPackedRecord): Record<string, unknown> {\n\treturn {\n\t\t...(typeof message.details === \"object\" && message.details !== null ? message.details : {}),\n\t\tcontextGc: {\n\t\t\tpacked: true,\n\t\t\toriginalChars: record.originalChars,\n\t\t\toriginalTokens: record.originalTokens,\n\t\t\tstoragePath: record.storagePath,\n\t\t\treason: record.reason,\n\t\t},\n\t};\n}\n\nfunction makePackedToolResult(message: ToolResultMessage, record: ContextGcPackedRecord): ToolResultMessage {\n\tconst summary = buildSummary(record);\n\treturn {\n\t\t...message,\n\t\tcontent: [{ type: \"text\", text: summary }],\n\t\tdetails: gcDetails(message, record),\n\t};\n}\n\nfunction makePackedSemanticMemoryMessage(message: AgentMessage, record: ContextGcPackedRecord): AgentMessage {\n\tconst summary = buildSummary(record);\n\treturn {\n\t\t...(message as unknown as Record<string, unknown>),\n\t\tcontent: [{ type: \"text\", text: summary }],\n\t\tdetails: gcDetails(message as { details?: unknown }, record),\n\t} as AgentMessage;\n}\n\nexport function applyContextGc(\n\tmessages: AgentMessage[],\n\trawSettings: ContextGcSettings & {\n\t\tcwd?: string;\n\t\tstorageDir?: string;\n\t\twritePayloads?: boolean;\n\t\tcuration?: ContextGcCurationHooks;\n\t},\n): ContextGcResult {\n\tconst settings = normalizeContextGcSettings(rawSettings);\n\tconst baseReport: ContextGcReport = {\n\t\tenabled: settings.enabled,\n\t\tpackedCount: 0,\n\t\toriginalTokens: 0,\n\t\tpackedTokens: 0,\n\t\tsavedTokens: 0,\n\t\trecords: [],\n\t};\n\tif (!settings.enabled) return { messages, report: baseReport };\n\n\tconst options: ContextGcOptions = {\n\t\t...settings,\n\t\tcwd: rawSettings.cwd ?? process.cwd(),\n\t\tstorageDir: rawSettings.storageDir,\n\t\twritePayloads: rawSettings.writePayloads ?? true,\n\t\tcuration: rawSettings.curation,\n\t};\n\tconst eligibleTools = new Set(options.tools);\n\tconst plan = collectContextGcPlan(messages, options.cwd, options.semanticMemory);\n\tconst recentStart = Math.max(0, messages.length - options.preserveRecentMessages);\n\tconst semanticIndexSet = new Set(plan.semanticIndexes);\n\tconst preservedSemanticIndexes = new Set(\n\t\toptions.semanticMemory.preserveRecentPages > 0\n\t\t\t? plan.semanticIndexes.slice(-options.semanticMemory.preserveRecentPages)\n\t\t\t: [],\n\t);\n\tconst nextMessages = messages.slice();\n\tlet changed = false;\n\n\tfor (let index = 0; index < messages.length; index++) {\n\t\tconst message = messages[index];\n\t\tif (semanticIndexSet.has(index) && !preservedSemanticIndexes.has(index) && index < recentStart) {\n\t\t\tconst originalText = agentMessageText(message);\n\t\t\tif (originalText && originalText.length >= options.semanticMemory.minChars) {\n\t\t\t\tconst originalTokens = estimateTokens(message);\n\t\t\t\tconst key = createHash(\"sha256\")\n\t\t\t\t\t.update(`semantic-memory\\0${index}\\0${originalText}`)\n\t\t\t\t\t.digest(\"hex\")\n\t\t\t\t\t.slice(0, 24);\n\t\t\t\tconst storagePath = maybeStoreOriginal(options, key, originalText);\n\t\t\t\tconst record: ContextGcPackedRecord = {\n\t\t\t\t\ttoolName: \"automata-mind\",\n\t\t\t\t\ttoolCallId: `semantic-${index}`,\n\t\t\t\t\tmessageIndex: index,\n\t\t\t\t\treason: \"stale-semantic-memory\",\n\t\t\t\t\toriginalChars: originalText.length,\n\t\t\t\t\toriginalTokens,\n\t\t\t\t\tpackedTokens: 0,\n\t\t\t\t\tstoragePath,\n\t\t\t\t\tkey,\n\t\t\t\t};\n\t\t\t\trecord.digest = options.curation?.resolveDigest?.(key);\n\t\t\t\toptions.curation?.onPacked?.(record, originalText);\n\t\t\t\tconst packed = makePackedSemanticMemoryMessage(message, record);\n\t\t\t\trecord.packedTokens = estimateTokens(packed);\n\t\t\t\tnextMessages[index] = packed;\n\t\t\t\tbaseReport.records.push(record);\n\t\t\t\tbaseReport.originalTokens += record.originalTokens;\n\t\t\t\tbaseReport.packedTokens += record.packedTokens;\n\t\t\t\tchanged = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tif (message.role !== \"toolResult\") continue;\n\t\tif (!eligibleTools.has(message.toolName)) continue;\n\t\tif (index >= recentStart) continue;\n\n\t\tconst originalText = toolResultText(message);\n\t\tif (originalText.length < options.minToolResultChars) continue;\n\n\t\tconst call = plan.calls.get(message.toolCallId);\n\t\tconst path = normalizeToolPath(options.cwd, call?.args.path);\n\t\tlet reason: ContextGcPackedRecord[\"reason\"] = \"stale-tool-result\";\n\t\tif (message.toolName === \"read\" && path) {\n\t\t\tif (plan.latestReadByPath.get(path) === message.toolCallId) continue;\n\t\t\treason = \"superseded-read\";\n\t\t}\n\n\t\tconst originalTokens = estimateTokens(message);\n\t\tconst key = createHash(\"sha256\")\n\t\t\t.update(`${message.toolName}\\0${message.toolCallId}\\0${originalText}`)\n\t\t\t.digest(\"hex\")\n\t\t\t.slice(0, 24);\n\t\tconst storagePath = maybeStoreOriginal(options, key, originalText);\n\t\tconst record: ContextGcPackedRecord = {\n\t\t\ttoolName: message.toolName,\n\t\t\ttoolCallId: message.toolCallId,\n\t\t\tmessageIndex: index,\n\t\t\treason,\n\t\t\toriginalChars: originalText.length,\n\t\t\toriginalTokens,\n\t\t\tpackedTokens: 0,\n\t\t\tstoragePath,\n\t\t\tpath,\n\t\t\tcommand: typeof call?.args.command === \"string\" ? call.args.command : undefined,\n\t\t\tkey,\n\t\t};\n\t\trecord.digest = options.curation?.resolveDigest?.(key);\n\t\toptions.curation?.onPacked?.(record, originalText);\n\t\tconst packed = makePackedToolResult(message, record);\n\t\trecord.packedTokens = estimateTokens(packed);\n\t\tnextMessages[index] = packed as AgentMessage;\n\t\tbaseReport.records.push(record);\n\t\tbaseReport.originalTokens += record.originalTokens;\n\t\tbaseReport.packedTokens += record.packedTokens;\n\t\tchanged = true;\n\t}\n\n\tbaseReport.packedCount = baseReport.records.length;\n\tbaseReport.savedTokens = Math.max(0, baseReport.originalTokens - baseReport.packedTokens);\n\treturn { messages: changed ? nextMessages : messages, report: baseReport };\n}\n"]}
1
+ {"version":3,"file":"context-gc.js","sourceRoot":"","sources":["../../src/core/context-gc.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from \"node:crypto\";\nimport { existsSync, mkdirSync, writeFileSync } from \"node:fs\";\nimport { isAbsolute, resolve } from \"node:path\";\nimport type { AgentMessage } from \"@caupulican/pi-agent-core\";\nimport type { ToolResultMessage } from \"@caupulican/pi-ai\";\nimport { normalizePath } from \"../utils/paths.ts\";\nimport { estimateTokens } from \"./compaction/compaction.ts\";\n\nexport interface SemanticMemoryGcSettings {\n\tenabled?: boolean;\n\t/** Number of newest Automata/Mind injected pages to preserve verbatim. */\n\tpreserveRecentPages?: number;\n\t/** Minimum provider-visible text chars before a stale semantic memory page is packed. */\n\tminChars?: number;\n\t/** Markers that identify deterministic Automata/Mind context pages. */\n\tmarkers?: string[];\n}\n\nexport interface ContextGcSettings {\n\tenabled?: boolean;\n\t/** Number of most recent AgentMessage rows to preserve verbatim. */\n\tpreserveRecentMessages?: number;\n\t/** Minimum provider-visible text chars before a stale tool result is packed. */\n\tminToolResultChars?: number;\n\t/** Tool names eligible for stale result packing. */\n\ttools?: string[];\n\t/** Provider-context control for deterministic Automata/Mind semantic memory pages. */\n\tsemanticMemory?: SemanticMemoryGcSettings;\n}\n\nexport interface NormalizedContextGcSettings extends Omit<Required<ContextGcSettings>, \"semanticMemory\"> {\n\tsemanticMemory: Required<SemanticMemoryGcSettings>;\n}\n\n/**\n * Brain-curation hooks (both optional; absent hooks are byte-for-byte today's behavior).\n * `resolveDigest` is a pure lookup keyed by the record's content hash — it must return the digest\n * already fenced in the untrusted-content boundary (e.g. BrainCurator.getDigest), with the fence's\n * nonce fixed once at store time, since GC re-renders the stub from raw messages on every request\n * and renders the returned string VERBATIM. `onPacked` lets the caller enqueue digest work with the\n * exact original text at the moment it is packed.\n */\nexport interface ContextGcCurationHooks {\n\tresolveDigest?: (digestKey: string) => string | undefined;\n\tonPacked?: (record: ContextGcPackedRecord, originalText: string) => void;\n}\n\nexport interface ContextGcOptions extends NormalizedContextGcSettings {\n\tcwd: string;\n\tstorageDir?: string;\n\twritePayloads?: boolean;\n\tcuration?: ContextGcCurationHooks;\n}\n\nexport interface ContextGcPackedRecord {\n\ttoolName: string;\n\ttoolCallId: string;\n\tmessageIndex: number;\n\treason: \"superseded-read\" | \"stale-tool-result\" | \"stale-semantic-memory\";\n\toriginalChars: number;\n\toriginalTokens: number;\n\tpackedTokens: number;\n\tstoragePath?: string;\n\tpath?: string;\n\tcommand?: string;\n\tkey?: string;\n\t/** Brain-curator semantic digest of the packed content (model-generated; advisory only). Arrives\n\t * already fenced in the untrusted-content boundary (see ContextGcCurationHooks.resolveDigest) —\n\t * render verbatim, never re-wrap. */\n\tdigest?: string;\n}\n\nexport interface ContextGcReport {\n\tenabled: boolean;\n\tpackedCount: number;\n\toriginalTokens: number;\n\tpackedTokens: number;\n\tsavedTokens: number;\n\trecords: ContextGcPackedRecord[];\n}\n\nexport interface ContextGcResult {\n\tmessages: AgentMessage[];\n\treport: ContextGcReport;\n}\n\nconst DEFAULT_SEMANTIC_MEMORY_GC_SETTINGS: Required<SemanticMemoryGcSettings> = {\n\tenabled: true,\n\tpreserveRecentPages: 1,\n\tminChars: 900,\n\tmarkers: [\n\t\t// Generic memory-subsystem recall page marker (brand-free). Provider-specific markers are\n\t\t// merged in dynamically at runtime via MemoryManager.getContextMarkers().\n\t\t\"<memory_context\",\n\t\t// Pre-existing provider-specific markers (to be generalized to provider-declared markers).\n\t\t\"<automata_context\",\n\t\t\"<automata_response\",\n\t\t\"<automata_query\",\n\t\t\"<automata_fetch\",\n\t\t\"<memory_lifecycle_audit\",\n\t\t\"<memory_lifecycle_purge\",\n\t\t\"<automata_doctor\",\n\t\t\"<automata_optimizer\",\n\t\t\"<automata_mesh\",\n\t],\n};\n\nexport const DEFAULT_CONTEXT_GC_SETTINGS: NormalizedContextGcSettings = {\n\tenabled: true,\n\tpreserveRecentMessages: 8,\n\tminToolResultChars: 1200,\n\ttools: [\n\t\t\"read\",\n\t\t\"bash\",\n\t\t\"rg\",\n\t\t\"grep\",\n\t\t\"find\",\n\t\t\"ls\",\n\t\t\"skill_open\",\n\t\t\"automata_graph_status\",\n\t\t\"automata_graph_search\",\n\t\t\"automata_graph_query\",\n\t\t\"automata_graph_neighbors\",\n\t\t\"automata_graph_path\",\n\t\t\"automata_graph_pointer_pack\",\n\t\t\"learning_query_memory\",\n\t\t\"subagent\",\n\t\t\"task_steps\",\n\t\t\"task_background\",\n\t\t\"task_goal\",\n\t\t\"run_ledger\",\n\t\t\"context_headroom_retrieve\",\n\t\t\"headroom_retrieve\",\n\t],\n\tsemanticMemory: DEFAULT_SEMANTIC_MEMORY_GC_SETTINGS,\n};\n\ntype ToolCallMeta = {\n\tid: string;\n\tname: string;\n\targs: Record<string, unknown>;\n\tmessageIndex: number;\n};\n\nfunction cap(text: string, limit = 220): string {\n\tconst compact = text.replace(/\\s+/g, \" \").trim();\n\treturn compact.length > limit ? `${compact.slice(0, Math.max(0, limit - 1))}…` : compact;\n}\n\nfunction normalizeSemanticMemoryGcSettings(settings?: SemanticMemoryGcSettings): Required<SemanticMemoryGcSettings> {\n\treturn {\n\t\tenabled: settings?.enabled ?? DEFAULT_SEMANTIC_MEMORY_GC_SETTINGS.enabled,\n\t\tpreserveRecentPages: Math.max(\n\t\t\t0,\n\t\t\tMath.floor(settings?.preserveRecentPages ?? DEFAULT_SEMANTIC_MEMORY_GC_SETTINGS.preserveRecentPages),\n\t\t),\n\t\tminChars: Math.max(0, Math.floor(settings?.minChars ?? DEFAULT_SEMANTIC_MEMORY_GC_SETTINGS.minChars)),\n\t\tmarkers:\n\t\t\tsettings?.markers && settings.markers.length > 0\n\t\t\t\t? settings.markers\n\t\t\t\t: DEFAULT_SEMANTIC_MEMORY_GC_SETTINGS.markers,\n\t};\n}\n\nfunction normalizeContextGcSettings(settings?: ContextGcSettings): NormalizedContextGcSettings {\n\treturn {\n\t\tenabled: settings?.enabled ?? DEFAULT_CONTEXT_GC_SETTINGS.enabled,\n\t\tpreserveRecentMessages: Math.max(\n\t\t\t0,\n\t\t\tMath.floor(settings?.preserveRecentMessages ?? DEFAULT_CONTEXT_GC_SETTINGS.preserveRecentMessages),\n\t\t),\n\t\tminToolResultChars: Math.max(\n\t\t\t0,\n\t\t\tMath.floor(settings?.minToolResultChars ?? DEFAULT_CONTEXT_GC_SETTINGS.minToolResultChars),\n\t\t),\n\t\ttools: settings?.tools && settings.tools.length > 0 ? settings.tools : DEFAULT_CONTEXT_GC_SETTINGS.tools,\n\t\tsemanticMemory: normalizeSemanticMemoryGcSettings(settings?.semanticMemory),\n\t};\n}\n\nexport function getContextGcSettings(settings?: ContextGcSettings): NormalizedContextGcSettings {\n\treturn normalizeContextGcSettings(settings);\n}\n\nfunction textContentParts(content: unknown): string[] | undefined {\n\tif (typeof content === \"string\") return [content];\n\tif (!Array.isArray(content)) return undefined;\n\tconst parts: string[] = [];\n\tfor (const part of content) {\n\t\tif (typeof part !== \"object\" || part === null) return undefined;\n\t\tconst typed = part as { type?: string; text?: string; mimeType?: string };\n\t\tif (typed.type === \"text\" && typeof typed.text === \"string\") parts.push(typed.text);\n\t\telse if (typed.type === \"image\") return undefined;\n\t\telse return undefined;\n\t}\n\treturn parts;\n}\n\nfunction contentText(content: unknown): string | undefined {\n\tif (typeof content === \"string\") return content;\n\treturn textContentParts(content)?.join(\"\\n\");\n}\n\nfunction toolResultParts(message: ToolResultMessage): string[] {\n\tconst parts: string[] = [];\n\tfor (const part of message.content) {\n\t\tif (part.type === \"text\" && part.text) parts.push(part.text);\n\t\telse if (part.type === \"image\") parts.push(`[image ${part.mimeType}]`);\n\t}\n\treturn parts;\n}\n\nfunction toolResultText(message: ToolResultMessage): string {\n\treturn toolResultParts(message).join(\"\\n\");\n}\n\nfunction smallStringSlice(value: string, start?: number, end?: number): string {\n\tconst sliced = value.slice(start, end);\n\treturn sliced ? ` ${sliced}`.slice(1) : \"\";\n}\n\nfunction joinedPartsContainMarker(parts: string[], marker: string): boolean {\n\tif (marker.length === 0) return true;\n\tconst tailLength = marker.length - 1;\n\tlet tail = \"\";\n\tlet first = true;\n\tfor (const part of parts) {\n\t\tif (part.includes(marker)) return true;\n\t\tif (!first && `${tail}\\n${smallStringSlice(part, 0, tailLength)}`.includes(marker)) return true;\n\t\tif (tailLength === 0) tail = \"\";\n\t\telse if (part.length >= tailLength) tail = smallStringSlice(part, -tailLength);\n\t\telse tail = `${tail}${first ? \"\" : \"\\n\"}${part}`.slice(-tailLength);\n\t\tfirst = false;\n\t}\n\treturn false;\n}\n\nfunction joinedPartsContainAnyMarker(parts: string[], markers: readonly string[]): boolean {\n\treturn markers.some((marker) => joinedPartsContainMarker(parts, marker));\n}\n\nfunction isSemanticMemoryCustomMessage(message: AgentMessage): boolean {\n\tif (message.role !== \"custom\") return false;\n\tconst customType = String((message as { customType?: unknown }).customType ?? \"\").toLowerCase();\n\treturn customType.includes(\"automata\") || customType.includes(\"memory\") || customType.includes(\"mind\");\n}\n\nfunction agentMessageText(message: AgentMessage): string | undefined {\n\tif (message.role === \"toolResult\") return toolResultText(message);\n\tif (isSemanticMemoryCustomMessage(message)) return contentText((message as { content?: unknown }).content);\n\treturn undefined;\n}\n\nfunction semanticMessageHasMarker(message: AgentMessage, settings: Required<SemanticMemoryGcSettings>): boolean {\n\tif (message.role === \"toolResult\") return joinedPartsContainAnyMarker(toolResultParts(message), settings.markers);\n\tif (isSemanticMemoryCustomMessage(message)) {\n\t\tconst parts = textContentParts((message as { content?: unknown }).content);\n\t\treturn parts ? joinedPartsContainAnyMarker(parts, settings.markers) : false;\n\t}\n\treturn false;\n}\n\ninterface ContextGcPlan {\n\tcalls: Map<string, ToolCallMeta>;\n\tlatestReadByPath: Map<string, string>;\n\tsemanticIndexes: number[];\n}\n\nfunction normalizeToolPath(cwd: string, value: unknown): string | undefined {\n\tif (typeof value !== \"string\" || value.trim() === \"\") return undefined;\n\tconst path = value.trim();\n\treturn normalizePath(isAbsolute(path) ? path : resolve(cwd, path));\n}\n\nfunction collectContextGcPlan(\n\tmessages: AgentMessage[],\n\tcwd: string,\n\tsemanticSettings: Required<SemanticMemoryGcSettings>,\n): ContextGcPlan {\n\tconst calls = new Map<string, ToolCallMeta>();\n\tconst readResultCallIds: string[] = [];\n\tconst semanticIndexes: number[] = [];\n\n\tfor (let messageIndex = 0; messageIndex < messages.length; messageIndex++) {\n\t\tconst message = messages[messageIndex];\n\t\tif (message.role === \"assistant\") {\n\t\t\tfor (const part of message.content) {\n\t\t\t\tif (part.type !== \"toolCall\") continue;\n\t\t\t\tcalls.set(part.id, {\n\t\t\t\t\tid: part.id,\n\t\t\t\t\tname: part.name,\n\t\t\t\t\targs: part.arguments ?? {},\n\t\t\t\t\tmessageIndex,\n\t\t\t\t});\n\t\t\t}\n\t\t} else if (message.role === \"toolResult\" && message.toolName === \"read\") {\n\t\t\treadResultCallIds.push(message.toolCallId);\n\t\t}\n\n\t\tif (semanticSettings.enabled && semanticMessageHasMarker(message, semanticSettings)) {\n\t\t\tsemanticIndexes.push(messageIndex);\n\t\t}\n\t}\n\n\tconst latestReadByPath = new Map<string, string>();\n\tfor (const toolCallId of readResultCallIds) {\n\t\tconst call = calls.get(toolCallId);\n\t\tconst path = normalizeToolPath(cwd, call?.args.path);\n\t\tif (path) latestReadByPath.set(path, toolCallId);\n\t}\n\n\treturn { calls, latestReadByPath, semanticIndexes };\n}\n\nfunction storagePathFor(storageDir: string | undefined, key: string): string | undefined {\n\tif (!storageDir || !isAbsolute(storageDir)) return undefined;\n\treturn resolve(storageDir, `${key}.txt`);\n}\n\nfunction maybeStoreOriginal(options: ContextGcOptions, key: string, original: string): string | undefined {\n\tconst path = storagePathFor(options.storageDir, key);\n\tif (!path || !options.writePayloads) return path;\n\ttry {\n\t\tmkdirSync(options.storageDir!, { recursive: true });\n\t\tif (!existsSync(path)) writeFileSync(path, original, \"utf8\");\n\t} catch {\n\t\treturn undefined;\n\t}\n\treturn path;\n}\n\nfunction reasonText(record: ContextGcPackedRecord): string {\n\tif (record.reason === \"superseded-read\") return \"older read snapshot superseded by a later read of the same file\";\n\tif (record.reason === \"stale-semantic-memory\") {\n\t\treturn \"older Automata/Mind semantic context page outside the semantic-memory freshness window\";\n\t}\n\treturn \"stale bulky tool output outside the recent context window\";\n}\n\nfunction buildSummary(record: ContextGcPackedRecord): string {\n\tconst semantic = record.reason === \"stale-semantic-memory\";\n\tconst lines = [\n\t\tsemantic ? \"[Semantic GC packed stale Automata/Mind context page]\" : \"[Context GC packed stale tool result]\",\n\t\tsemantic ? undefined : `tool: ${record.toolName}`,\n\t\trecord.path ? `path: ${record.path}` : undefined,\n\t\trecord.command ? `command: ${cap(record.command)}` : undefined,\n\t\t`reason: ${reasonText(record)}`,\n\t\t`original: ${record.originalChars} chars (~${record.originalTokens} tokens)`,\n\t\t// The digest arrives PRE-FENCED from the curator (brain-curator.ts wraps it once, in the\n\t\t// standard untrusted-content boundary — the same treatment memory recall pages get — at the\n\t\t// moment it is stored, not here). Render it VERBATIM: re-wrapping on every GC render would\n\t\t// regenerate the fence's nonce each time, making the packed stub byte-different on every\n\t\t// provider request and busting prompt caching (BUG E).\n\t\trecord.digest ? `summary (auto-digest, machine paraphrase, not authoritative): ${record.digest}` : undefined,\n\t\trecord.storagePath\n\t\t\t? `exact old provider-visible text stored at: ${record.storagePath}`\n\t\t\t: \"exact old provider-visible text retained in the session log, not inline in provider context\",\n\t\tsemantic\n\t\t\t? \"If this memory context matters, query Automata/Mind again with the same topic/filter or fetch the drawer pointers from the stored page.\"\n\t\t\t: record.path\n\t\t\t\t? \"For current file contents, use the read tool on the path above. For the exact old output, read the stored payload path if present.\"\n\t\t\t\t: \"If this exact old output matters, retrieve/read the stored payload path if present or rerun the tool command.\",\n\t\t\"Do not rely on this summary as the original content.\",\n\t].filter((line): line is string => line !== undefined);\n\treturn lines.join(\"\\n\");\n}\n\nfunction gcDetails(message: { details?: unknown }, record: ContextGcPackedRecord): Record<string, unknown> {\n\treturn {\n\t\t...(typeof message.details === \"object\" && message.details !== null ? message.details : {}),\n\t\tcontextGc: {\n\t\t\tpacked: true,\n\t\t\toriginalChars: record.originalChars,\n\t\t\toriginalTokens: record.originalTokens,\n\t\t\tstoragePath: record.storagePath,\n\t\t\treason: record.reason,\n\t\t},\n\t};\n}\n\nfunction makePackedToolResult(message: ToolResultMessage, record: ContextGcPackedRecord): ToolResultMessage {\n\tconst summary = buildSummary(record);\n\treturn {\n\t\t...message,\n\t\tcontent: [{ type: \"text\", text: summary }],\n\t\tdetails: gcDetails(message, record),\n\t};\n}\n\nfunction makePackedSemanticMemoryMessage(message: AgentMessage, record: ContextGcPackedRecord): AgentMessage {\n\tconst summary = buildSummary(record);\n\treturn {\n\t\t...(message as unknown as Record<string, unknown>),\n\t\tcontent: [{ type: \"text\", text: summary }],\n\t\tdetails: gcDetails(message as { details?: unknown }, record),\n\t} as AgentMessage;\n}\n\nexport function applyContextGc(\n\tmessages: AgentMessage[],\n\trawSettings: ContextGcSettings & {\n\t\tcwd?: string;\n\t\tstorageDir?: string;\n\t\twritePayloads?: boolean;\n\t\tcuration?: ContextGcCurationHooks;\n\t},\n): ContextGcResult {\n\tconst settings = normalizeContextGcSettings(rawSettings);\n\tconst baseReport: ContextGcReport = {\n\t\tenabled: settings.enabled,\n\t\tpackedCount: 0,\n\t\toriginalTokens: 0,\n\t\tpackedTokens: 0,\n\t\tsavedTokens: 0,\n\t\trecords: [],\n\t};\n\tif (!settings.enabled) return { messages, report: baseReport };\n\n\tconst options: ContextGcOptions = {\n\t\t...settings,\n\t\tcwd: rawSettings.cwd ?? process.cwd(),\n\t\tstorageDir: rawSettings.storageDir,\n\t\twritePayloads: rawSettings.writePayloads ?? true,\n\t\tcuration: rawSettings.curation,\n\t};\n\tconst eligibleTools = new Set(options.tools);\n\tconst plan = collectContextGcPlan(messages, options.cwd, options.semanticMemory);\n\tconst recentStart = Math.max(0, messages.length - options.preserveRecentMessages);\n\tconst semanticIndexSet = new Set(plan.semanticIndexes);\n\tconst preservedSemanticIndexes = new Set(\n\t\toptions.semanticMemory.preserveRecentPages > 0\n\t\t\t? plan.semanticIndexes.slice(-options.semanticMemory.preserveRecentPages)\n\t\t\t: [],\n\t);\n\tconst nextMessages = messages.slice();\n\tlet changed = false;\n\n\tfor (let index = 0; index < messages.length; index++) {\n\t\tconst message = messages[index];\n\t\tif (semanticIndexSet.has(index) && !preservedSemanticIndexes.has(index) && index < recentStart) {\n\t\t\tconst originalText = agentMessageText(message);\n\t\t\tif (originalText && originalText.length >= options.semanticMemory.minChars) {\n\t\t\t\tconst originalTokens = estimateTokens(message);\n\t\t\t\tconst key = createHash(\"sha256\")\n\t\t\t\t\t.update(`semantic-memory\\0${index}\\0${originalText}`)\n\t\t\t\t\t.digest(\"hex\")\n\t\t\t\t\t.slice(0, 24);\n\t\t\t\tconst storagePath = maybeStoreOriginal(options, key, originalText);\n\t\t\t\tconst record: ContextGcPackedRecord = {\n\t\t\t\t\ttoolName: \"automata-mind\",\n\t\t\t\t\ttoolCallId: `semantic-${index}`,\n\t\t\t\t\tmessageIndex: index,\n\t\t\t\t\treason: \"stale-semantic-memory\",\n\t\t\t\t\toriginalChars: originalText.length,\n\t\t\t\t\toriginalTokens,\n\t\t\t\t\tpackedTokens: 0,\n\t\t\t\t\tstoragePath,\n\t\t\t\t\tkey,\n\t\t\t\t};\n\t\t\t\trecord.digest = options.curation?.resolveDigest?.(key);\n\t\t\t\toptions.curation?.onPacked?.(record, originalText);\n\t\t\t\tconst packed = makePackedSemanticMemoryMessage(message, record);\n\t\t\t\trecord.packedTokens = estimateTokens(packed);\n\t\t\t\tnextMessages[index] = packed;\n\t\t\t\tbaseReport.records.push(record);\n\t\t\t\tbaseReport.originalTokens += record.originalTokens;\n\t\t\t\tbaseReport.packedTokens += record.packedTokens;\n\t\t\t\tchanged = true;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t}\n\n\t\tif (message.role !== \"toolResult\") continue;\n\t\tif (!eligibleTools.has(message.toolName)) continue;\n\t\tif (index >= recentStart) continue;\n\n\t\tconst originalText = toolResultText(message);\n\t\tif (originalText.length < options.minToolResultChars) continue;\n\n\t\tconst call = plan.calls.get(message.toolCallId);\n\t\tconst path = normalizeToolPath(options.cwd, call?.args.path);\n\t\tlet reason: ContextGcPackedRecord[\"reason\"] = \"stale-tool-result\";\n\t\tif (message.toolName === \"read\" && path) {\n\t\t\tif (plan.latestReadByPath.get(path) === message.toolCallId) continue;\n\t\t\treason = \"superseded-read\";\n\t\t}\n\n\t\tconst originalTokens = estimateTokens(message);\n\t\tconst key = createHash(\"sha256\")\n\t\t\t.update(`${message.toolName}\\0${message.toolCallId}\\0${originalText}`)\n\t\t\t.digest(\"hex\")\n\t\t\t.slice(0, 24);\n\t\tconst storagePath = maybeStoreOriginal(options, key, originalText);\n\t\tconst record: ContextGcPackedRecord = {\n\t\t\ttoolName: message.toolName,\n\t\t\ttoolCallId: message.toolCallId,\n\t\t\tmessageIndex: index,\n\t\t\treason,\n\t\t\toriginalChars: originalText.length,\n\t\t\toriginalTokens,\n\t\t\tpackedTokens: 0,\n\t\t\tstoragePath,\n\t\t\tpath,\n\t\t\tcommand: typeof call?.args.command === \"string\" ? call.args.command : undefined,\n\t\t\tkey,\n\t\t};\n\t\trecord.digest = options.curation?.resolveDigest?.(key);\n\t\toptions.curation?.onPacked?.(record, originalText);\n\t\tconst packed = makePackedToolResult(message, record);\n\t\trecord.packedTokens = estimateTokens(packed);\n\t\tnextMessages[index] = packed as AgentMessage;\n\t\tbaseReport.records.push(record);\n\t\tbaseReport.originalTokens += record.originalTokens;\n\t\tbaseReport.packedTokens += record.packedTokens;\n\t\tchanged = true;\n\t}\n\n\tbaseReport.packedCount = baseReport.records.length;\n\tbaseReport.savedTokens = Math.max(0, baseReport.originalTokens - baseReport.packedTokens);\n\treturn { messages: changed ? nextMessages : messages, report: baseReport };\n}\n"]}
@@ -1,4 +1,3 @@
1
- import { existsSync } from "node:fs";
2
1
  import type { CapabilityEnvelope } from "../autonomy/contracts.ts";
3
2
  /**
4
3
  * Code-writing workers (G2): the worker MODEL never touches the filesystem — it emits strict-JSON
@@ -33,18 +32,9 @@ export interface AppliedActionsReport {
33
32
  reason: string;
34
33
  }>;
35
34
  }
36
- export interface WorkerActionFs {
37
- existsSync: typeof existsSync;
38
- readFileSync: (path: string, encoding: "utf-8") => string;
39
- writeFileSync: (path: string, content: string, encoding: "utf-8") => void;
40
- mkdirSync: (path: string, options: {
41
- recursive: true;
42
- }) => unknown;
43
- }
44
35
  export declare function applyWorkerActions(args: {
45
36
  actions: readonly WorkerAction[];
46
37
  envelope: CapabilityEnvelope;
47
38
  cwd: string;
48
- fs?: WorkerActionFs;
49
39
  }): AppliedActionsReport;
50
40
  //# sourceMappingURL=worker-actions.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"worker-actions.d.ts","sourceRoot":"","sources":["../../../src/core/delegation/worker-actions.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAA0C,MAAM,SAAS,CAAC;AAE7E,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAGnE;;;;;;;GAOG;AAEH,MAAM,WAAW,YAAY;IAC5B,EAAE,EAAE,OAAO,GAAG,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,gCAAgC;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8DAA8D;IAC9D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,8BAA8B;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAKD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,YAAY,EAAE,CAkB/D;AAED,MAAM,WAAW,oBAAoB;IACpC,4CAA4C;IAC5C,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,uFAAqF;IACrF,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACjD,8FAA8F;IAC9F,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAChD;AAED,MAAM,WAAW,cAAc;IAC9B,UAAU,EAAE,OAAO,UAAU,CAAC;IAC9B,YAAY,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,KAAK,MAAM,CAAC;IAC1D,aAAa,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,KAAK,IAAI,CAAC;IAC1E,SAAS,EAAE,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,EAAE;QAAE,SAAS,EAAE,IAAI,CAAA;KAAE,KAAK,OAAO,CAAC;CACnE;AAID,wBAAgB,kBAAkB,CAAC,IAAI,EAAE;IACxC,OAAO,EAAE,SAAS,YAAY,EAAE,CAAC;IACjC,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,GAAG,EAAE,MAAM,CAAC;IACZ,EAAE,CAAC,EAAE,cAAc,CAAC;CACpB,GAAG,oBAAoB,CAgCvB","sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, relative, resolve } from \"node:path\";\nimport type { CapabilityEnvelope } from \"../autonomy/contracts.ts\";\nimport { isPathWithinEnvelope } from \"../autonomy/envelope-enforcement.ts\";\n\n/**\n * Code-writing workers (G2): the worker MODEL never touches the filesystem — it emits strict-JSON\n * actions, and this RUNNER-side module applies them deterministically through the capability\n * envelope's path scope. That keeps the structural-contract philosophy (a local model without\n * tool-calling templates can still write code) and makes enforcement execution-time, not\n * validation-only: an out-of-scope action is REFUSED with a reason, never silently dropped, and\n * refusals surface as blockers on the result.\n */\n\nexport interface WorkerAction {\n\top: \"write\" | \"edit\";\n\tpath: string;\n\t/** write: full file content. */\n\tcontent?: string;\n\t/** edit: exact string to replace (must occur in the file). */\n\told?: string;\n\t/** edit: replacement text. */\n\tnew?: string;\n}\n\nconst MAX_ACTIONS = 20;\nconst MAX_CONTENT_CHARS = 512 * 1024;\n\nexport function parseWorkerActions(raw: unknown): WorkerAction[] {\n\tif (!Array.isArray(raw)) return [];\n\tconst actions: WorkerAction[] = [];\n\tfor (const entry of raw.slice(0, MAX_ACTIONS)) {\n\t\tif (!entry || typeof entry !== \"object\") continue;\n\t\tconst record = entry as Record<string, unknown>;\n\t\tif (record.op !== \"write\" && record.op !== \"edit\") continue;\n\t\tif (typeof record.path !== \"string\" || record.path.length === 0) continue;\n\t\tif (record.op === \"write\") {\n\t\t\tif (typeof record.content !== \"string\" || record.content.length > MAX_CONTENT_CHARS) continue;\n\t\t\tactions.push({ op: \"write\", path: record.path, content: record.content });\n\t\t} else {\n\t\t\tif (typeof record.old !== \"string\" || record.old.length === 0) continue;\n\t\t\tif (typeof record.new !== \"string\" || record.new.length > MAX_CONTENT_CHARS) continue;\n\t\t\tactions.push({ op: \"edit\", path: record.path, old: record.old, new: record.new });\n\t\t}\n\t}\n\treturn actions;\n}\n\nexport interface AppliedActionsReport {\n\t/** Repo-relative paths actually changed. */\n\tchangedFiles: string[];\n\t/** Envelope-scope refusals (execution-time enforcement) — surfaced, never silent. */\n\trefused: Array<{ path: string; reason: string }>;\n\t/** Actions that were in scope but could not be applied (missing file, old-text not found). */\n\tfailed: Array<{ path: string; reason: string }>;\n}\n\nexport interface WorkerActionFs {\n\texistsSync: typeof existsSync;\n\treadFileSync: (path: string, encoding: \"utf-8\") => string;\n\twriteFileSync: (path: string, content: string, encoding: \"utf-8\") => void;\n\tmkdirSync: (path: string, options: { recursive: true }) => unknown;\n}\n\nconst realFs: WorkerActionFs = { existsSync, readFileSync, writeFileSync, mkdirSync };\n\nexport function applyWorkerActions(args: {\n\tactions: readonly WorkerAction[];\n\tenvelope: CapabilityEnvelope;\n\tcwd: string;\n\tfs?: WorkerActionFs;\n}): AppliedActionsReport {\n\tconst fileSystem = args.fs ?? realFs;\n\tconst report: AppliedActionsReport = { changedFiles: [], refused: [], failed: [] };\n\tfor (const action of args.actions) {\n\t\tif (!isPathWithinEnvelope(args.envelope, action.path, args.cwd)) {\n\t\t\treport.refused.push({ path: action.path, reason: `outside envelope ${args.envelope.id} path scope` });\n\t\t\tcontinue;\n\t\t}\n\t\tconst target = resolve(args.cwd, action.path);\n\t\tconst relativePath = relative(args.cwd, target);\n\t\ttry {\n\t\t\tif (action.op === \"write\") {\n\t\t\t\tfileSystem.mkdirSync(dirname(target), { recursive: true });\n\t\t\t\tfileSystem.writeFileSync(target, action.content ?? \"\", \"utf-8\");\n\t\t\t} else {\n\t\t\t\tif (!fileSystem.existsSync(target)) {\n\t\t\t\t\treport.failed.push({ path: action.path, reason: \"file does not exist\" });\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst current = fileSystem.readFileSync(target, \"utf-8\");\n\t\t\t\tif (!action.old || !current.includes(action.old)) {\n\t\t\t\t\treport.failed.push({ path: action.path, reason: \"edit old-text not found in file\" });\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tfileSystem.writeFileSync(target, current.replace(action.old, action.new ?? \"\"), \"utf-8\");\n\t\t\t}\n\t\t\tif (!report.changedFiles.includes(relativePath)) report.changedFiles.push(relativePath);\n\t\t} catch (error) {\n\t\t\treport.failed.push({ path: action.path, reason: error instanceof Error ? error.message : String(error) });\n\t\t}\n\t}\n\treturn report;\n}\n"]}
1
+ {"version":3,"file":"worker-actions.d.ts","sourceRoot":"","sources":["../../../src/core/delegation/worker-actions.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAC;AAGnE;;;;;;;GAOG;AAEH,MAAM,WAAW,YAAY;IAC5B,EAAE,EAAE,OAAO,GAAG,MAAM,CAAC;IACrB,IAAI,EAAE,MAAM,CAAC;IACb,gCAAgC;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,8DAA8D;IAC9D,GAAG,CAAC,EAAE,MAAM,CAAC;IACb,8BAA8B;IAC9B,GAAG,CAAC,EAAE,MAAM,CAAC;CACb;AAKD,wBAAgB,kBAAkB,CAAC,GAAG,EAAE,OAAO,GAAG,YAAY,EAAE,CAkB/D;AAED,MAAM,WAAW,oBAAoB;IACpC,4CAA4C;IAC5C,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,uFAAqF;IACrF,OAAO,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IACjD,8FAA8F;IAC9F,MAAM,EAAE,KAAK,CAAC;QAAE,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;CAChD;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE;IACxC,OAAO,EAAE,SAAS,YAAY,EAAE,CAAC;IACjC,QAAQ,EAAE,kBAAkB,CAAC;IAC7B,GAAG,EAAE,MAAM,CAAC;CACZ,GAAG,oBAAoB,CA+BvB","sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, relative, resolve } from \"node:path\";\nimport type { CapabilityEnvelope } from \"../autonomy/contracts.ts\";\nimport { isPathWithinEnvelope } from \"../autonomy/envelope-enforcement.ts\";\n\n/**\n * Code-writing workers (G2): the worker MODEL never touches the filesystem — it emits strict-JSON\n * actions, and this RUNNER-side module applies them deterministically through the capability\n * envelope's path scope. That keeps the structural-contract philosophy (a local model without\n * tool-calling templates can still write code) and makes enforcement execution-time, not\n * validation-only: an out-of-scope action is REFUSED with a reason, never silently dropped, and\n * refusals surface as blockers on the result.\n */\n\nexport interface WorkerAction {\n\top: \"write\" | \"edit\";\n\tpath: string;\n\t/** write: full file content. */\n\tcontent?: string;\n\t/** edit: exact string to replace (must occur in the file). */\n\told?: string;\n\t/** edit: replacement text. */\n\tnew?: string;\n}\n\nconst MAX_ACTIONS = 20;\nconst MAX_CONTENT_CHARS = 512 * 1024;\n\nexport function parseWorkerActions(raw: unknown): WorkerAction[] {\n\tif (!Array.isArray(raw)) return [];\n\tconst actions: WorkerAction[] = [];\n\tfor (const entry of raw.slice(0, MAX_ACTIONS)) {\n\t\tif (!entry || typeof entry !== \"object\") continue;\n\t\tconst record = entry as Record<string, unknown>;\n\t\tif (record.op !== \"write\" && record.op !== \"edit\") continue;\n\t\tif (typeof record.path !== \"string\" || record.path.length === 0) continue;\n\t\tif (record.op === \"write\") {\n\t\t\tif (typeof record.content !== \"string\" || record.content.length > MAX_CONTENT_CHARS) continue;\n\t\t\tactions.push({ op: \"write\", path: record.path, content: record.content });\n\t\t} else {\n\t\t\tif (typeof record.old !== \"string\" || record.old.length === 0) continue;\n\t\t\tif (typeof record.new !== \"string\" || record.new.length > MAX_CONTENT_CHARS) continue;\n\t\t\tactions.push({ op: \"edit\", path: record.path, old: record.old, new: record.new });\n\t\t}\n\t}\n\treturn actions;\n}\n\nexport interface AppliedActionsReport {\n\t/** Repo-relative paths actually changed. */\n\tchangedFiles: string[];\n\t/** Envelope-scope refusals (execution-time enforcement) — surfaced, never silent. */\n\trefused: Array<{ path: string; reason: string }>;\n\t/** Actions that were in scope but could not be applied (missing file, old-text not found). */\n\tfailed: Array<{ path: string; reason: string }>;\n}\n\nexport function applyWorkerActions(args: {\n\tactions: readonly WorkerAction[];\n\tenvelope: CapabilityEnvelope;\n\tcwd: string;\n}): AppliedActionsReport {\n\tconst report: AppliedActionsReport = { changedFiles: [], refused: [], failed: [] };\n\tfor (const action of args.actions) {\n\t\tif (!isPathWithinEnvelope(args.envelope, action.path, args.cwd)) {\n\t\t\treport.refused.push({ path: action.path, reason: `outside envelope ${args.envelope.id} path scope` });\n\t\t\tcontinue;\n\t\t}\n\t\tconst target = resolve(args.cwd, action.path);\n\t\tconst relativePath = relative(args.cwd, target);\n\t\ttry {\n\t\t\tif (action.op === \"write\") {\n\t\t\t\tmkdirSync(dirname(target), { recursive: true });\n\t\t\t\twriteFileSync(target, action.content ?? \"\", \"utf-8\");\n\t\t\t} else {\n\t\t\t\tif (!existsSync(target)) {\n\t\t\t\t\treport.failed.push({ path: action.path, reason: \"file does not exist\" });\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst current = readFileSync(target, \"utf-8\");\n\t\t\t\tif (!action.old || !current.includes(action.old)) {\n\t\t\t\t\treport.failed.push({ path: action.path, reason: \"edit old-text not found in file\" });\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\twriteFileSync(target, current.replace(action.old, action.new ?? \"\"), \"utf-8\");\n\t\t\t}\n\t\t\tif (!report.changedFiles.includes(relativePath)) report.changedFiles.push(relativePath);\n\t\t} catch (error) {\n\t\t\treport.failed.push({ path: action.path, reason: error instanceof Error ? error.message : String(error) });\n\t\t}\n\t}\n\treturn report;\n}\n"]}
@@ -30,9 +30,7 @@ export function parseWorkerActions(raw) {
30
30
  }
31
31
  return actions;
32
32
  }
33
- const realFs = { existsSync, readFileSync, writeFileSync, mkdirSync };
34
33
  export function applyWorkerActions(args) {
35
- const fileSystem = args.fs ?? realFs;
36
34
  const report = { changedFiles: [], refused: [], failed: [] };
37
35
  for (const action of args.actions) {
38
36
  if (!isPathWithinEnvelope(args.envelope, action.path, args.cwd)) {
@@ -43,20 +41,20 @@ export function applyWorkerActions(args) {
43
41
  const relativePath = relative(args.cwd, target);
44
42
  try {
45
43
  if (action.op === "write") {
46
- fileSystem.mkdirSync(dirname(target), { recursive: true });
47
- fileSystem.writeFileSync(target, action.content ?? "", "utf-8");
44
+ mkdirSync(dirname(target), { recursive: true });
45
+ writeFileSync(target, action.content ?? "", "utf-8");
48
46
  }
49
47
  else {
50
- if (!fileSystem.existsSync(target)) {
48
+ if (!existsSync(target)) {
51
49
  report.failed.push({ path: action.path, reason: "file does not exist" });
52
50
  continue;
53
51
  }
54
- const current = fileSystem.readFileSync(target, "utf-8");
52
+ const current = readFileSync(target, "utf-8");
55
53
  if (!action.old || !current.includes(action.old)) {
56
54
  report.failed.push({ path: action.path, reason: "edit old-text not found in file" });
57
55
  continue;
58
56
  }
59
- fileSystem.writeFileSync(target, current.replace(action.old, action.new ?? ""), "utf-8");
57
+ writeFileSync(target, current.replace(action.old, action.new ?? ""), "utf-8");
60
58
  }
61
59
  if (!report.changedFiles.includes(relativePath))
62
60
  report.changedFiles.push(relativePath);
@@ -1 +1 @@
1
- {"version":3,"file":"worker-actions.js","sourceRoot":"","sources":["../../../src/core/delegation/worker-actions.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, relative, resolve } from \"node:path\";\nimport type { CapabilityEnvelope } from \"../autonomy/contracts.ts\";\nimport { isPathWithinEnvelope } from \"../autonomy/envelope-enforcement.ts\";\n\n/**\n * Code-writing workers (G2): the worker MODEL never touches the filesystem — it emits strict-JSON\n * actions, and this RUNNER-side module applies them deterministically through the capability\n * envelope's path scope. That keeps the structural-contract philosophy (a local model without\n * tool-calling templates can still write code) and makes enforcement execution-time, not\n * validation-only: an out-of-scope action is REFUSED with a reason, never silently dropped, and\n * refusals surface as blockers on the result.\n */\n\nexport interface WorkerAction {\n\top: \"write\" | \"edit\";\n\tpath: string;\n\t/** write: full file content. */\n\tcontent?: string;\n\t/** edit: exact string to replace (must occur in the file). */\n\told?: string;\n\t/** edit: replacement text. */\n\tnew?: string;\n}\n\nconst MAX_ACTIONS = 20;\nconst MAX_CONTENT_CHARS = 512 * 1024;\n\nexport function parseWorkerActions(raw: unknown): WorkerAction[] {\n\tif (!Array.isArray(raw)) return [];\n\tconst actions: WorkerAction[] = [];\n\tfor (const entry of raw.slice(0, MAX_ACTIONS)) {\n\t\tif (!entry || typeof entry !== \"object\") continue;\n\t\tconst record = entry as Record<string, unknown>;\n\t\tif (record.op !== \"write\" && record.op !== \"edit\") continue;\n\t\tif (typeof record.path !== \"string\" || record.path.length === 0) continue;\n\t\tif (record.op === \"write\") {\n\t\t\tif (typeof record.content !== \"string\" || record.content.length > MAX_CONTENT_CHARS) continue;\n\t\t\tactions.push({ op: \"write\", path: record.path, content: record.content });\n\t\t} else {\n\t\t\tif (typeof record.old !== \"string\" || record.old.length === 0) continue;\n\t\t\tif (typeof record.new !== \"string\" || record.new.length > MAX_CONTENT_CHARS) continue;\n\t\t\tactions.push({ op: \"edit\", path: record.path, old: record.old, new: record.new });\n\t\t}\n\t}\n\treturn actions;\n}\n\nexport interface AppliedActionsReport {\n\t/** Repo-relative paths actually changed. */\n\tchangedFiles: string[];\n\t/** Envelope-scope refusals (execution-time enforcement) — surfaced, never silent. */\n\trefused: Array<{ path: string; reason: string }>;\n\t/** Actions that were in scope but could not be applied (missing file, old-text not found). */\n\tfailed: Array<{ path: string; reason: string }>;\n}\n\nexport interface WorkerActionFs {\n\texistsSync: typeof existsSync;\n\treadFileSync: (path: string, encoding: \"utf-8\") => string;\n\twriteFileSync: (path: string, content: string, encoding: \"utf-8\") => void;\n\tmkdirSync: (path: string, options: { recursive: true }) => unknown;\n}\n\nconst realFs: WorkerActionFs = { existsSync, readFileSync, writeFileSync, mkdirSync };\n\nexport function applyWorkerActions(args: {\n\tactions: readonly WorkerAction[];\n\tenvelope: CapabilityEnvelope;\n\tcwd: string;\n\tfs?: WorkerActionFs;\n}): AppliedActionsReport {\n\tconst fileSystem = args.fs ?? realFs;\n\tconst report: AppliedActionsReport = { changedFiles: [], refused: [], failed: [] };\n\tfor (const action of args.actions) {\n\t\tif (!isPathWithinEnvelope(args.envelope, action.path, args.cwd)) {\n\t\t\treport.refused.push({ path: action.path, reason: `outside envelope ${args.envelope.id} path scope` });\n\t\t\tcontinue;\n\t\t}\n\t\tconst target = resolve(args.cwd, action.path);\n\t\tconst relativePath = relative(args.cwd, target);\n\t\ttry {\n\t\t\tif (action.op === \"write\") {\n\t\t\t\tfileSystem.mkdirSync(dirname(target), { recursive: true });\n\t\t\t\tfileSystem.writeFileSync(target, action.content ?? \"\", \"utf-8\");\n\t\t\t} else {\n\t\t\t\tif (!fileSystem.existsSync(target)) {\n\t\t\t\t\treport.failed.push({ path: action.path, reason: \"file does not exist\" });\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst current = fileSystem.readFileSync(target, \"utf-8\");\n\t\t\t\tif (!action.old || !current.includes(action.old)) {\n\t\t\t\t\treport.failed.push({ path: action.path, reason: \"edit old-text not found in file\" });\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tfileSystem.writeFileSync(target, current.replace(action.old, action.new ?? \"\"), \"utf-8\");\n\t\t\t}\n\t\t\tif (!report.changedFiles.includes(relativePath)) report.changedFiles.push(relativePath);\n\t\t} catch (error) {\n\t\t\treport.failed.push({ path: action.path, reason: error instanceof Error ? error.message : String(error) });\n\t\t}\n\t}\n\treturn report;\n}\n"]}
1
+ {"version":3,"file":"worker-actions.js","sourceRoot":"","sources":["../../../src/core/delegation/worker-actions.ts"],"names":[],"mappings":"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","sourcesContent":["import { existsSync, mkdirSync, readFileSync, writeFileSync } from \"node:fs\";\nimport { dirname, relative, resolve } from \"node:path\";\nimport type { CapabilityEnvelope } from \"../autonomy/contracts.ts\";\nimport { isPathWithinEnvelope } from \"../autonomy/envelope-enforcement.ts\";\n\n/**\n * Code-writing workers (G2): the worker MODEL never touches the filesystem — it emits strict-JSON\n * actions, and this RUNNER-side module applies them deterministically through the capability\n * envelope's path scope. That keeps the structural-contract philosophy (a local model without\n * tool-calling templates can still write code) and makes enforcement execution-time, not\n * validation-only: an out-of-scope action is REFUSED with a reason, never silently dropped, and\n * refusals surface as blockers on the result.\n */\n\nexport interface WorkerAction {\n\top: \"write\" | \"edit\";\n\tpath: string;\n\t/** write: full file content. */\n\tcontent?: string;\n\t/** edit: exact string to replace (must occur in the file). */\n\told?: string;\n\t/** edit: replacement text. */\n\tnew?: string;\n}\n\nconst MAX_ACTIONS = 20;\nconst MAX_CONTENT_CHARS = 512 * 1024;\n\nexport function parseWorkerActions(raw: unknown): WorkerAction[] {\n\tif (!Array.isArray(raw)) return [];\n\tconst actions: WorkerAction[] = [];\n\tfor (const entry of raw.slice(0, MAX_ACTIONS)) {\n\t\tif (!entry || typeof entry !== \"object\") continue;\n\t\tconst record = entry as Record<string, unknown>;\n\t\tif (record.op !== \"write\" && record.op !== \"edit\") continue;\n\t\tif (typeof record.path !== \"string\" || record.path.length === 0) continue;\n\t\tif (record.op === \"write\") {\n\t\t\tif (typeof record.content !== \"string\" || record.content.length > MAX_CONTENT_CHARS) continue;\n\t\t\tactions.push({ op: \"write\", path: record.path, content: record.content });\n\t\t} else {\n\t\t\tif (typeof record.old !== \"string\" || record.old.length === 0) continue;\n\t\t\tif (typeof record.new !== \"string\" || record.new.length > MAX_CONTENT_CHARS) continue;\n\t\t\tactions.push({ op: \"edit\", path: record.path, old: record.old, new: record.new });\n\t\t}\n\t}\n\treturn actions;\n}\n\nexport interface AppliedActionsReport {\n\t/** Repo-relative paths actually changed. */\n\tchangedFiles: string[];\n\t/** Envelope-scope refusals (execution-time enforcement) — surfaced, never silent. */\n\trefused: Array<{ path: string; reason: string }>;\n\t/** Actions that were in scope but could not be applied (missing file, old-text not found). */\n\tfailed: Array<{ path: string; reason: string }>;\n}\n\nexport function applyWorkerActions(args: {\n\tactions: readonly WorkerAction[];\n\tenvelope: CapabilityEnvelope;\n\tcwd: string;\n}): AppliedActionsReport {\n\tconst report: AppliedActionsReport = { changedFiles: [], refused: [], failed: [] };\n\tfor (const action of args.actions) {\n\t\tif (!isPathWithinEnvelope(args.envelope, action.path, args.cwd)) {\n\t\t\treport.refused.push({ path: action.path, reason: `outside envelope ${args.envelope.id} path scope` });\n\t\t\tcontinue;\n\t\t}\n\t\tconst target = resolve(args.cwd, action.path);\n\t\tconst relativePath = relative(args.cwd, target);\n\t\ttry {\n\t\t\tif (action.op === \"write\") {\n\t\t\t\tmkdirSync(dirname(target), { recursive: true });\n\t\t\t\twriteFileSync(target, action.content ?? \"\", \"utf-8\");\n\t\t\t} else {\n\t\t\t\tif (!existsSync(target)) {\n\t\t\t\t\treport.failed.push({ path: action.path, reason: \"file does not exist\" });\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\tconst current = readFileSync(target, \"utf-8\");\n\t\t\t\tif (!action.old || !current.includes(action.old)) {\n\t\t\t\t\treport.failed.push({ path: action.path, reason: \"edit old-text not found in file\" });\n\t\t\t\t\tcontinue;\n\t\t\t\t}\n\t\t\t\twriteFileSync(target, current.replace(action.old, action.new ?? \"\"), \"utf-8\");\n\t\t\t}\n\t\t\tif (!report.changedFiles.includes(relativePath)) report.changedFiles.push(relativePath);\n\t\t} catch (error) {\n\t\t\treport.failed.push({ path: action.path, reason: error instanceof Error ? error.message : String(error) });\n\t\t}\n\t}\n\treturn report;\n}\n"]}
@@ -5,5 +5,11 @@ export declare function requiresParentReview(result: WorkerResult): boolean;
5
5
  export declare function validateWorkerResult(args: {
6
6
  request: WorkerRequest;
7
7
  result: WorkerResult;
8
+ /**
9
+ * Baseline for relative paths — BOTH the runner's cwd-relative `changedFiles` and the
10
+ * envelope's possibly-relative path scopes resolve against this. Defaults to process.cwd()
11
+ * for callers whose session cwd is the process cwd.
12
+ */
13
+ cwd?: string;
8
14
  }): GateOutcome;
9
15
  //# sourceMappingURL=worker-result.d.ts.map