@code-yeongyu/senpi 2026.9.6 → 2026.9.7-2

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 (194) hide show
  1. package/CHANGELOG.md +56 -0
  2. package/dist/core/agent-session.d.ts +51 -5
  3. package/dist/core/agent-session.d.ts.map +1 -1
  4. package/dist/core/agent-session.js +168 -48
  5. package/dist/core/agent-session.js.map +1 -1
  6. package/dist/core/compaction/compaction.d.ts.map +1 -1
  7. package/dist/core/compaction/compaction.js +2 -1
  8. package/dist/core/compaction/compaction.js.map +1 -1
  9. package/dist/core/compaction/stuck-overflow.d.ts +11 -0
  10. package/dist/core/compaction/stuck-overflow.d.ts.map +1 -0
  11. package/dist/core/compaction/stuck-overflow.js +17 -0
  12. package/dist/core/compaction/stuck-overflow.js.map +1 -0
  13. package/dist/core/extensions/builtin/cache-keepalive/index.d.ts.map +1 -1
  14. package/dist/core/extensions/builtin/cache-keepalive/index.js +3 -21
  15. package/dist/core/extensions/builtin/cache-keepalive/index.js.map +1 -1
  16. package/dist/core/extensions/builtin/claude-sdk-oauth/session-binding.d.ts.map +1 -1
  17. package/dist/core/extensions/builtin/claude-sdk-oauth/session-binding.js +36 -19
  18. package/dist/core/extensions/builtin/claude-sdk-oauth/session-binding.js.map +1 -1
  19. package/dist/core/extensions/builtin/claude-sdk-oauth/session-commit-boundary.d.ts.map +1 -1
  20. package/dist/core/extensions/builtin/claude-sdk-oauth/session-commit-boundary.js +21 -10
  21. package/dist/core/extensions/builtin/claude-sdk-oauth/session-commit-boundary.js.map +1 -1
  22. package/dist/core/extensions/builtin/claude-sdk-oauth/session-continuity.d.ts +4 -17
  23. package/dist/core/extensions/builtin/claude-sdk-oauth/session-continuity.d.ts.map +1 -1
  24. package/dist/core/extensions/builtin/claude-sdk-oauth/session-continuity.js +23 -33
  25. package/dist/core/extensions/builtin/claude-sdk-oauth/session-continuity.js.map +1 -1
  26. package/dist/core/extensions/builtin/claude-sdk-oauth/session-stream.d.ts.map +1 -1
  27. package/dist/core/extensions/builtin/claude-sdk-oauth/session-stream.js +1 -0
  28. package/dist/core/extensions/builtin/claude-sdk-oauth/session-stream.js.map +1 -1
  29. package/dist/core/extensions/builtin/claude-sdk-oauth/session-sync.d.ts.map +1 -1
  30. package/dist/core/extensions/builtin/claude-sdk-oauth/session-sync.js +5 -3
  31. package/dist/core/extensions/builtin/claude-sdk-oauth/session-sync.js.map +1 -1
  32. package/dist/core/extensions/builtin/claude-sdk-oauth/session-turn-attempt.d.ts.map +1 -1
  33. package/dist/core/extensions/builtin/claude-sdk-oauth/session-turn-attempt.js +8 -3
  34. package/dist/core/extensions/builtin/claude-sdk-oauth/session-turn-attempt.js.map +1 -1
  35. package/dist/core/extensions/builtin/claude-sdk-oauth/tools.d.ts +6 -5
  36. package/dist/core/extensions/builtin/claude-sdk-oauth/tools.d.ts.map +1 -1
  37. package/dist/core/extensions/builtin/claude-sdk-oauth/tools.js +6 -5
  38. package/dist/core/extensions/builtin/claude-sdk-oauth/tools.js.map +1 -1
  39. package/dist/core/extensions/builtin/compaction/circuit-breaker.d.ts +8 -0
  40. package/dist/core/extensions/builtin/compaction/circuit-breaker.d.ts.map +1 -1
  41. package/dist/core/extensions/builtin/compaction/circuit-breaker.js +8 -0
  42. package/dist/core/extensions/builtin/compaction/circuit-breaker.js.map +1 -1
  43. package/dist/core/extensions/builtin/compaction/deterministic-fallback.d.ts.map +1 -1
  44. package/dist/core/extensions/builtin/compaction/deterministic-fallback.js +24 -1
  45. package/dist/core/extensions/builtin/compaction/deterministic-fallback.js.map +1 -1
  46. package/dist/core/extensions/builtin/compaction/fallback-failed-turn-normalization.d.ts +18 -0
  47. package/dist/core/extensions/builtin/compaction/fallback-failed-turn-normalization.d.ts.map +1 -0
  48. package/dist/core/extensions/builtin/compaction/fallback-failed-turn-normalization.js +20 -0
  49. package/dist/core/extensions/builtin/compaction/fallback-failed-turn-normalization.js.map +1 -0
  50. package/dist/core/extensions/builtin/compaction/index.js +2 -2
  51. package/dist/core/extensions/builtin/compaction/index.js.map +1 -1
  52. package/dist/core/extensions/builtin/compaction/lane-policy.d.ts +3 -0
  53. package/dist/core/extensions/builtin/compaction/lane-policy.d.ts.map +1 -1
  54. package/dist/core/extensions/builtin/compaction/lane-policy.js +47 -23
  55. package/dist/core/extensions/builtin/compaction/lane-policy.js.map +1 -1
  56. package/dist/core/extensions/builtin/goal/cache-warm-renderer.js +6 -4
  57. package/dist/core/extensions/builtin/goal/cache-warm-renderer.js.map +1 -1
  58. package/dist/core/extensions/builtin/goal/cache-warm.d.ts +19 -1
  59. package/dist/core/extensions/builtin/goal/cache-warm.d.ts.map +1 -1
  60. package/dist/core/extensions/builtin/goal/cache-warm.js +23 -10
  61. package/dist/core/extensions/builtin/goal/cache-warm.js.map +1 -1
  62. package/dist/core/extensions/builtin/goal/continuation-recovery.d.ts +1 -0
  63. package/dist/core/extensions/builtin/goal/continuation-recovery.d.ts.map +1 -1
  64. package/dist/core/extensions/builtin/goal/continuation-recovery.js +2 -0
  65. package/dist/core/extensions/builtin/goal/continuation-recovery.js.map +1 -1
  66. package/dist/core/extensions/builtin/goal/continuation.d.ts +3 -1
  67. package/dist/core/extensions/builtin/goal/continuation.d.ts.map +1 -1
  68. package/dist/core/extensions/builtin/goal/continuation.js +4 -0
  69. package/dist/core/extensions/builtin/goal/continuation.js.map +1 -1
  70. package/dist/core/extensions/builtin/goal/lifecycle-helpers.d.ts +3 -0
  71. package/dist/core/extensions/builtin/goal/lifecycle-helpers.d.ts.map +1 -1
  72. package/dist/core/extensions/builtin/goal/lifecycle-helpers.js +14 -3
  73. package/dist/core/extensions/builtin/goal/lifecycle-helpers.js.map +1 -1
  74. package/dist/core/extensions/builtin/goal/monitor-continuation.d.ts +1 -1
  75. package/dist/core/extensions/builtin/goal/monitor-continuation.d.ts.map +1 -1
  76. package/dist/core/extensions/builtin/goal/monitor-continuation.js +7 -3
  77. package/dist/core/extensions/builtin/goal/monitor-continuation.js.map +1 -1
  78. package/dist/core/extensions/builtin/hooks/plugin-manifest.js +2 -2
  79. package/dist/core/extensions/builtin/hooks/plugin-manifest.js.map +1 -1
  80. package/dist/core/extensions/builtin/hooks/safety.js +2 -2
  81. package/dist/core/extensions/builtin/hooks/safety.js.map +1 -1
  82. package/dist/core/extensions/builtin/permission-system/external-dir.d.ts.map +1 -1
  83. package/dist/core/extensions/builtin/permission-system/external-dir.js +5 -22
  84. package/dist/core/extensions/builtin/permission-system/external-dir.js.map +1 -1
  85. package/dist/core/extensions/builtin/permission-system/parsers.d.ts.map +1 -1
  86. package/dist/core/extensions/builtin/permission-system/parsers.js +5 -7
  87. package/dist/core/extensions/builtin/permission-system/parsers.js.map +1 -1
  88. package/dist/core/extensions/builtin/terminal/monitor-registry.d.ts.map +1 -1
  89. package/dist/core/extensions/builtin/terminal/monitor-registry.js +14 -7
  90. package/dist/core/extensions/builtin/terminal/monitor-registry.js.map +1 -1
  91. package/dist/core/package-identity.d.ts +12 -0
  92. package/dist/core/package-identity.d.ts.map +1 -0
  93. package/dist/core/package-identity.js +57 -0
  94. package/dist/core/package-identity.js.map +1 -0
  95. package/dist/core/resource-loader.d.ts +0 -2
  96. package/dist/core/resource-loader.d.ts.map +1 -1
  97. package/dist/core/resource-loader.js +8 -60
  98. package/dist/core/resource-loader.js.map +1 -1
  99. package/dist/core/tools/bounded-realpath.d.ts +28 -0
  100. package/dist/core/tools/bounded-realpath.d.ts.map +1 -0
  101. package/dist/core/tools/bounded-realpath.js +53 -0
  102. package/dist/core/tools/bounded-realpath.js.map +1 -0
  103. package/dist/core/tools/file-mutation-queue.d.ts.map +1 -1
  104. package/dist/core/tools/file-mutation-queue.js +15 -13
  105. package/dist/core/tools/file-mutation-queue.js.map +1 -1
  106. package/dist/core/tools/filesystem-policy.d.ts +7 -0
  107. package/dist/core/tools/filesystem-policy.d.ts.map +1 -1
  108. package/dist/core/tools/filesystem-policy.js +25 -15
  109. package/dist/core/tools/filesystem-policy.js.map +1 -1
  110. package/dist/core/trust-manager.d.ts.map +1 -1
  111. package/dist/core/trust-manager.js +13 -2
  112. package/dist/core/trust-manager.js.map +1 -1
  113. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  114. package/dist/modes/interactive/interactive-mode.js +1 -0
  115. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  116. package/dist/modes/interactive/tips/catalog/memory-tips.d.ts.map +1 -1
  117. package/dist/modes/interactive/tips/catalog/memory-tips.js +6 -0
  118. package/dist/modes/interactive/tips/catalog/memory-tips.js.map +1 -1
  119. package/dist/modes/interactive/tips/catalog/subagent-tips.d.ts.map +1 -1
  120. package/dist/modes/interactive/tips/catalog/subagent-tips.js +6 -0
  121. package/dist/modes/interactive/tips/catalog/subagent-tips.js.map +1 -1
  122. package/dist/modes/rpc/host-lifecycle.d.ts.map +1 -1
  123. package/dist/modes/rpc/host-lifecycle.js +31 -5
  124. package/dist/modes/rpc/host-lifecycle.js.map +1 -1
  125. package/dist/modes/rpc/host-watchdog.d.ts +11 -0
  126. package/dist/modes/rpc/host-watchdog.d.ts.map +1 -1
  127. package/dist/modes/rpc/host-watchdog.js +16 -2
  128. package/dist/modes/rpc/host-watchdog.js.map +1 -1
  129. package/dist/modes/rpc/multi-session-host.d.ts.map +1 -1
  130. package/dist/modes/rpc/multi-session-host.js +54 -16
  131. package/dist/modes/rpc/multi-session-host.js.map +1 -1
  132. package/dist/modes/rpc/socket-ownership.d.ts +46 -0
  133. package/dist/modes/rpc/socket-ownership.d.ts.map +1 -0
  134. package/dist/modes/rpc/socket-ownership.js +178 -0
  135. package/dist/modes/rpc/socket-ownership.js.map +1 -0
  136. package/dist/utils/fs-watch.d.ts +17 -0
  137. package/dist/utils/fs-watch.d.ts.map +1 -1
  138. package/dist/utils/fs-watch.js +38 -10
  139. package/dist/utils/fs-watch.js.map +1 -1
  140. package/dist/utils/paths.d.ts +33 -0
  141. package/dist/utils/paths.d.ts.map +1 -1
  142. package/dist/utils/paths.js +109 -3
  143. package/dist/utils/paths.js.map +1 -1
  144. package/docs/compaction-guide.md +2 -2
  145. package/docs/compaction.md +2 -2
  146. package/docs/development.md +3 -2
  147. package/docs/providers.md +1 -1
  148. package/docs/rpc.md +1 -1
  149. package/node_modules/@code-yeongyu/senpi-codemode/CHANGELOG.md +29 -0
  150. package/node_modules/@code-yeongyu/senpi-codemode/README.md +14 -1
  151. package/node_modules/@code-yeongyu/senpi-codemode/package.json +4 -4
  152. package/node_modules/@code-yeongyu/senpi-codemode/src/bridge/protocol.ts +1 -0
  153. package/node_modules/@code-yeongyu/senpi-codemode/src/extension/runtime-factory.ts +3 -0
  154. package/node_modules/@code-yeongyu/senpi-codemode/src/extension/session-manager.ts +7 -0
  155. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/AGENTS.md +7 -0
  156. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/jl/kernel.ts +6 -2
  157. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/inline-worker.ts +2 -2
  158. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/kernel-contract.ts +3 -0
  159. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-core.js +29 -0
  160. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-indirect-eval.js +10 -6
  161. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-shell-capture.d.ts +16 -0
  162. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-shell-capture.js +44 -8
  163. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/js/worker-startup.ts +3 -2
  164. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/kernel-contract.ts +3 -0
  165. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/py/transport.ts +11 -3
  166. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/rb/kernel.ts +6 -2
  167. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/session-env.ts +56 -0
  168. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/shared/runtime-asset.ts +40 -14
  169. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/shared/subprocess-contract.ts +3 -0
  170. package/node_modules/@code-yeongyu/senpi-codemode/src/kernels/shared/subprocess-kernel.ts +9 -1
  171. package/node_modules/@code-yeongyu/senpi-codemode/src/output/output-meta.ts +26 -2
  172. package/node_modules/@code-yeongyu/senpi-codemode/src/tool/image.ts +12 -3
  173. package/node_modules/@earendil-works/pi-agent-core/package.json +3 -3
  174. package/node_modules/@earendil-works/pi-ai/dist/providers/data/.manifest.json +1 -1
  175. package/node_modules/@earendil-works/pi-ai/dist/providers/data/amazon-bedrock.json +1 -1
  176. package/node_modules/@earendil-works/pi-ai/dist/providers/data/azure-openai-responses.json +1 -1
  177. package/node_modules/@earendil-works/pi-ai/dist/providers/data/cloudflare-ai-gateway.json +1 -1
  178. package/node_modules/@earendil-works/pi-ai/dist/providers/data/github-copilot.json +1 -1
  179. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openai-codex.json +1 -1
  180. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openai.json +1 -1
  181. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode-go.json +1 -1
  182. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opencode.json +1 -1
  183. package/node_modules/@earendil-works/pi-ai/dist/providers/data/opengateway.json +1 -1
  184. package/node_modules/@earendil-works/pi-ai/dist/providers/data/openrouter.json +1 -1
  185. package/node_modules/@earendil-works/pi-ai/dist/providers/data/vercel-ai-gateway.json +1 -1
  186. package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.d.ts +1 -1
  187. package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.d.ts.map +1 -1
  188. package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.js +14 -3
  189. package/node_modules/@earendil-works/pi-ai/dist/utils/overflow.js.map +1 -1
  190. package/node_modules/@earendil-works/pi-ai/package.json +2 -2
  191. package/node_modules/@earendil-works/pi-pty/package.json +1 -1
  192. package/node_modules/@earendil-works/pi-telemetry/package.json +1 -1
  193. package/node_modules/@earendil-works/pi-tui/package.json +1 -1
  194. package/package.json +7 -7
@@ -1 +1 @@
1
- {"version":3,"file":"session-sync.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/claude-sdk-oauth/session-sync.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from \"node:crypto\";\nimport type { Context, Message } from \"@earendil-works/pi-ai\";\nimport { appendSdkContentBlocks } from \"./content-blocks.ts\";\nimport type { ClaudeSdkOauthAuthLane } from \"./options.ts\";\nimport type { ContentBlockParam, Options } from \"./sdk-boundary.ts\";\nimport type { ClaudeSdkOauthSessionEntry } from \"./session-registry.ts\";\nimport { HOST_TOOL_POLICY_FINGERPRINT, mapPiToolNameToSdk } from \"./tools.ts\";\n\nexport type SentMessage = Extract<Message, { role: \"user\" | \"toolResult\" }>;\n\nexport type SessionConfigFingerprint = {\n\tsystemPromptHash: string;\n\ttoolsetHash: string;\n};\n\nconst sentHashesByEntry = new WeakMap<ClaudeSdkOauthSessionEntry, string[]>();\n\nfunction stableValue(value: unknown, seen = new WeakSet<object>()): unknown {\n\tif (typeof value === \"function\") return `[function:${value.name}:${value.toString()}]`;\n\tif (typeof value === \"bigint\") return value.toString();\n\tif (value === null || typeof value !== \"object\") return value;\n\tif (seen.has(value)) return \"[circular]\";\n\tseen.add(value);\n\tconst normalized = Array.isArray(value)\n\t\t? value.map((item) => stableValue(item, seen))\n\t\t: Object.fromEntries(\n\t\t\t\tObject.entries(value as Record<string, unknown>)\n\t\t\t\t\t.filter(([, item]) => item !== undefined)\n\t\t\t\t\t.sort(([left], [right]) => left.localeCompare(right))\n\t\t\t\t\t.map(([key, item]) => [key, stableValue(item, seen)]),\n\t\t\t);\n\tseen.delete(value);\n\treturn normalized;\n}\n\nfunction digest(value: unknown): string {\n\treturn createHash(\"sha256\")\n\t\t.update(JSON.stringify(stableValue(value)))\n\t\t.digest(\"hex\");\n}\n\nexport function sessionSyncDigest(value: unknown): string {\n\treturn digest(value);\n}\n\n/**\n * A user message with no content blocks carries nothing to transmit and is\n * transient: it is present for a single provider call and gone by the next.\n * Hashing one shifts every later index, so the following turn reports\n * `sent_stream_diverged` and re-sends the whole history even though the\n * conversation never changed. Tool results are never filtered - an empty tool\n * result is a real observation, and its id and name stay hash-significant.\n */\nfunction isContentlessUserMessage(message: SentMessage): boolean {\n\tif (message.role !== \"user\") return false;\n\t// Only a literal zero-block array is content-less. Whitespace-only text and\n\t// explicit empty-text blocks still emit transport blocks, so they must stay\n\t// hash-significant to keep divergence detection fail-closed.\n\treturn Array.isArray(message.content) && message.content.length === 0;\n}\n\nexport function sentMessages(context: Context): SentMessage[] {\n\treturn context.messages.filter(isTransmittedMessage);\n}\n\n/**\n * The one selection rule for \"message the provider was sent\". Branch-derived and\n * context-derived hashes MUST share it: a content-less user message that only one\n * side skips shifts every later index and reports a false divergence.\n */\nexport function isTransmittedMessage(message: { role: string }): message is SentMessage {\n\tif (message.role !== \"user\" && message.role !== \"toolResult\") return false;\n\treturn !isContentlessUserMessage(message as SentMessage);\n}\n\n/**\n * Applies the transmitted-message rule itself, so no caller can produce a hash\n * list that disagrees with another caller's by forgetting the filter.\n */\nexport function sentMessageHashes(messages: readonly SentMessage[]): string[] {\n\tconst hashes = messages.filter(isTransmittedMessage).map((message) =>\n\t\tdigest(\n\t\t\tmessage.role === \"user\"\n\t\t\t\t? { role: message.role, content: message.content }\n\t\t\t\t: {\n\t\t\t\t\t\trole: message.role,\n\t\t\t\t\t\ttoolCallId: message.toolCallId,\n\t\t\t\t\t\ttoolName: message.toolName,\n\t\t\t\t\t\tcontent: message.content,\n\t\t\t\t\t},\n\t\t),\n\t);\n\treturn hashes;\n}\n\nexport function sentHashPrefixDigest(hashes: readonly string[], count = hashes.length): string {\n\treturn digest(hashes.slice(0, count));\n}\n\n/** Pure divergence guard: every non-proven continuation resolves to cold-seed. */\n\nexport function sentHashesForEntry(entry: ClaudeSdkOauthSessionEntry): readonly string[] | undefined {\n\treturn sentHashesByEntry.get(entry);\n}\n\nexport function recordSyncedStream(entry: ClaudeSdkOauthSessionEntry, hashes: readonly string[]): void {\n\tconst copy = [...hashes];\n\tsentHashesByEntry.set(entry, copy);\n\tentry.sentCount = copy.length;\n\tentry.syncedPrefixHash = sentHashPrefixDigest(copy);\n\tentry.branchInfo = null;\n}\n\nconst GENERATED_DATE_LINE = /\\nCurrent date: \\d{4}-\\d{2}-\\d{2}(?=\\nCurrent working directory: [^\\n]*$)/;\n\n/**\n * The generated date line advances at UTC midnight while the conversation is\n * unchanged; hashing it verbatim retires a live session at midnight for no\n * semantic reason. Only that exact terminal line is neutralized - cwd and every\n * other prompt region stay fail-closed.\n */\nfunction fingerprintSystemPrompt(systemPrompt: Options[\"systemPrompt\"]): unknown {\n\tif (typeof systemPrompt !== \"string\") return systemPrompt ?? null;\n\treturn systemPrompt.replace(GENERATED_DATE_LINE, \"\\nCurrent date: <session-date>\");\n}\n\nexport function configFingerprint(\n\toptions: Options,\n\tcontext: Context,\n\tauthLane: ClaudeSdkOauthAuthLane,\n\taccountName: string,\n): SessionConfigFingerprint {\n\treturn {\n\t\tsystemPromptHash: digest(fingerprintSystemPrompt(options.systemPrompt)),\n\t\ttoolsetHash: digest({\n\t\t\ttools: options.tools ?? [],\n\t\t\treasoning: {\n\t\t\t\tthinking: options.thinking,\n\t\t\t\teffort: options.effort,\n\t\t\t\tmaxThinkingTokens: options.maxThinkingTokens,\n\t\t\t},\n\t\t\tcontextTools: (context.tools ?? []).map((tool) => ({\n\t\t\t\tname: tool.name,\n\t\t\t\tdescription: tool.description,\n\t\t\t\tparameters: tool.parameters,\n\t\t\t})),\n\t\t\tcwd: options.cwd,\n\t\t\tauthLane,\n\t\t\taccountName,\n\t\t\tpermissionMode: options.permissionMode,\n\t\t\t// Bump HOST_TOOL_POLICY_FINGERPRINT in tools.ts when denial copy or hooks change.\n\t\t\thostToolPolicy: HOST_TOOL_POLICY_FINGERPRINT,\n\t\t\tsettingSources: options.settingSources,\n\t\t\textraArgs: options.extraArgs,\n\t\t\tpathToClaudeCodeExecutable: options.pathToClaudeCodeExecutable,\n\t\t\tincludePartialMessages: options.includePartialMessages,\n\t\t}),\n\t};\n}\n\nfunction appendContent(blocks: ContentBlockParam[], content: string | readonly unknown[]): void {\n\t// The delta path has always transmitted a whole-content empty string as an\n\t// empty text block; keep that wire shape so resident sessions see the same\n\t// payload as before the shared mapper (prompt-bridge intentionally skips it).\n\tif (content === \"\") {\n\t\tblocks.push({ type: \"text\", text: \"\" });\n\t\treturn;\n\t}\n\tappendSdkContentBlocks(blocks, content);\n}\n\nexport function buildDeltaPromptBlocks(\n\tmessages: readonly SentMessage[],\n\tcustomToolNameToSdk?: ReadonlyMap<string, string>,\n): ContentBlockParam[] {\n\tconst blocks: ContentBlockParam[] = [];\n\tfor (const [index, message] of messages.entries()) {\n\t\tif (index > 0) blocks.push({ type: \"text\", text: \"\\n\\n\" });\n\t\tif (message.role === \"toolResult\") {\n\t\t\tblocks.push({\n\t\t\t\ttype: \"text\",\n\t\t\t\ttext: `Tool result (${mapPiToolNameToSdk(message.toolName, customToolNameToSdk)}, id=${message.toolCallId}):\\n`,\n\t\t\t});\n\t\t}\n\t\tappendContent(blocks, message.content);\n\t}\n\treturn blocks;\n}\n"]}
1
+ {"version":3,"file":"session-sync.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/claude-sdk-oauth/session-sync.ts"],"names":[],"mappings":"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","sourcesContent":["import { createHash } from \"node:crypto\";\nimport type { Context, Message } from \"@earendil-works/pi-ai\";\nimport { appendSdkContentBlocks } from \"./content-blocks.ts\";\nimport type { ClaudeSdkOauthAuthLane } from \"./options.ts\";\nimport type { ContentBlockParam, Options } from \"./sdk-boundary.ts\";\nimport type { ClaudeSdkOauthSessionEntry } from \"./session-registry.ts\";\nimport { HOST_TOOL_POLICY_FINGERPRINT, mapPiToolNameToSdk } from \"./tools.ts\";\n\nexport type SentMessage = Extract<Message, { role: \"user\" | \"toolResult\" }>;\n\nexport type SessionConfigFingerprint = {\n\tsystemPromptHash: string;\n\ttoolsetHash: string;\n};\n\nconst sentHashesByEntry = new WeakMap<ClaudeSdkOauthSessionEntry, string[]>();\n\nfunction stableValue(value: unknown, seen = new WeakSet<object>()): unknown {\n\tif (typeof value === \"function\") return `[function:${value.name}:${value.toString()}]`;\n\tif (typeof value === \"bigint\") return value.toString();\n\tif (value === null || typeof value !== \"object\") return value;\n\tif (seen.has(value)) return \"[circular]\";\n\tseen.add(value);\n\tconst normalized = Array.isArray(value)\n\t\t? value.map((item) => stableValue(item, seen))\n\t\t: Object.fromEntries(\n\t\t\t\tObject.entries(value as Record<string, unknown>)\n\t\t\t\t\t.filter(([, item]) => item !== undefined)\n\t\t\t\t\t.sort(([left], [right]) => left.localeCompare(right))\n\t\t\t\t\t.map(([key, item]) => [key, stableValue(item, seen)]),\n\t\t\t);\n\tseen.delete(value);\n\treturn normalized;\n}\n\nfunction digest(value: unknown): string {\n\treturn createHash(\"sha256\")\n\t\t.update(JSON.stringify(stableValue(value)))\n\t\t.digest(\"hex\");\n}\n\nexport function sessionSyncDigest(value: unknown): string {\n\treturn digest(value);\n}\n\n/**\n * A user message with no content blocks carries nothing to transmit and is\n * transient: it is present for a single provider call and gone by the next.\n * Hashing one shifts every later index, so the following turn reports\n * `sent_stream_diverged` and re-sends the whole history even though the\n * conversation never changed. Tool results are never filtered - an empty tool\n * result is a real observation, and its id and name stay hash-significant.\n */\nfunction isContentlessUserMessage(message: SentMessage): boolean {\n\tif (message.role !== \"user\") return false;\n\t// Only a literal zero-block array is content-less. Whitespace-only text and\n\t// explicit empty-text blocks still emit transport blocks, so they must stay\n\t// hash-significant to keep divergence detection fail-closed.\n\treturn Array.isArray(message.content) && message.content.length === 0;\n}\n\nexport function sentMessages(context: Context): SentMessage[] {\n\treturn context.messages.filter(isTransmittedMessage);\n}\n\n/**\n * The one selection rule for \"message the provider was sent\". Branch-derived and\n * context-derived hashes MUST share it: a content-less user message that only one\n * side skips shifts every later index and reports a false divergence.\n */\nexport function isTransmittedMessage(message: { role: string }): message is SentMessage {\n\tif (message.role !== \"user\" && message.role !== \"toolResult\") return false;\n\treturn !isContentlessUserMessage(message as SentMessage);\n}\n\n/**\n * Applies the transmitted-message rule itself, so no caller can produce a hash\n * list that disagrees with another caller's by forgetting the filter.\n */\nexport function sentMessageHashes(messages: readonly SentMessage[]): string[] {\n\tconst hashes = messages.filter(isTransmittedMessage).map((message) =>\n\t\tdigest(\n\t\t\tmessage.role === \"user\"\n\t\t\t\t? { role: message.role, content: message.content }\n\t\t\t\t: {\n\t\t\t\t\t\trole: message.role,\n\t\t\t\t\t\ttoolCallId: message.toolCallId,\n\t\t\t\t\t\ttoolName: message.toolName,\n\t\t\t\t\t\tcontent: message.content,\n\t\t\t\t\t},\n\t\t),\n\t);\n\treturn hashes;\n}\n\nexport function sentHashPrefixDigest(hashes: readonly string[], count = hashes.length): string {\n\treturn digest(hashes.slice(0, count));\n}\n\n/** Pure divergence guard: every non-proven continuation resolves to cold-seed. */\n\nexport function sentHashesForEntry(entry: ClaudeSdkOauthSessionEntry): readonly string[] | undefined {\n\treturn sentHashesByEntry.get(entry);\n}\n\nexport function recordSyncedStream(entry: ClaudeSdkOauthSessionEntry, hashes: readonly string[]): void {\n\tconst copy = [...hashes];\n\tsentHashesByEntry.set(entry, copy);\n\tentry.sentCount = copy.length;\n\tentry.syncedPrefixHash = sentHashPrefixDigest(copy);\n\tentry.branchInfo = null;\n}\n\nconst GENERATED_DATE_LINE = /\\nCurrent date: \\d{4}-\\d{2}-\\d{2}(?=\\nCurrent working directory: [^\\n]*)/;\n\n/**\n * The generated date line advances at UTC midnight while the conversation is\n * unchanged; hashing it verbatim retires a live session at midnight for no\n * semantic reason. Only that exact date-plus-cwd pair is neutralized - cwd and\n * every other prompt region stay fail-closed. Extension appends legitimately\n * follow the cwd line (oh-my-openagent#7884), so the pair is matched wherever\n * it appears, not only at the end of the prompt.\n */\nfunction fingerprintSystemPrompt(systemPrompt: Options[\"systemPrompt\"]): unknown {\n\tif (typeof systemPrompt !== \"string\") return systemPrompt ?? null;\n\treturn systemPrompt.replace(GENERATED_DATE_LINE, \"\\nCurrent date: <session-date>\");\n}\n\nexport function configFingerprint(\n\toptions: Options,\n\tcontext: Context,\n\tauthLane: ClaudeSdkOauthAuthLane,\n\taccountName: string,\n): SessionConfigFingerprint {\n\treturn {\n\t\tsystemPromptHash: digest(fingerprintSystemPrompt(options.systemPrompt)),\n\t\ttoolsetHash: digest({\n\t\t\ttools: options.tools ?? [],\n\t\t\treasoning: {\n\t\t\t\tthinking: options.thinking,\n\t\t\t\teffort: options.effort,\n\t\t\t\tmaxThinkingTokens: options.maxThinkingTokens,\n\t\t\t},\n\t\t\tcontextTools: (context.tools ?? []).map((tool) => ({\n\t\t\t\tname: tool.name,\n\t\t\t\tdescription: tool.description,\n\t\t\t\tparameters: tool.parameters,\n\t\t\t})),\n\t\t\tcwd: options.cwd,\n\t\t\tauthLane,\n\t\t\taccountName,\n\t\t\tpermissionMode: options.permissionMode,\n\t\t\t// Bump HOST_TOOL_POLICY_FINGERPRINT in tools.ts when denial copy or hooks change.\n\t\t\thostToolPolicy: HOST_TOOL_POLICY_FINGERPRINT,\n\t\t\tsettingSources: options.settingSources,\n\t\t\textraArgs: options.extraArgs,\n\t\t\tpathToClaudeCodeExecutable: options.pathToClaudeCodeExecutable,\n\t\t\tincludePartialMessages: options.includePartialMessages,\n\t\t}),\n\t};\n}\n\nfunction appendContent(blocks: ContentBlockParam[], content: string | readonly unknown[]): void {\n\t// The delta path has always transmitted a whole-content empty string as an\n\t// empty text block; keep that wire shape so resident sessions see the same\n\t// payload as before the shared mapper (prompt-bridge intentionally skips it).\n\tif (content === \"\") {\n\t\tblocks.push({ type: \"text\", text: \"\" });\n\t\treturn;\n\t}\n\tappendSdkContentBlocks(blocks, content);\n}\n\nexport function buildDeltaPromptBlocks(\n\tmessages: readonly SentMessage[],\n\tcustomToolNameToSdk?: ReadonlyMap<string, string>,\n): ContentBlockParam[] {\n\tconst blocks: ContentBlockParam[] = [];\n\tfor (const [index, message] of messages.entries()) {\n\t\tif (index > 0) blocks.push({ type: \"text\", text: \"\\n\\n\" });\n\t\tif (message.role === \"toolResult\") {\n\t\t\tblocks.push({\n\t\t\t\ttype: \"text\",\n\t\t\t\ttext: `Tool result (${mapPiToolNameToSdk(message.toolName, customToolNameToSdk)}, id=${message.toolCallId}):\\n`,\n\t\t\t});\n\t\t}\n\t\tappendContent(blocks, message.content);\n\t}\n\treturn blocks;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"session-turn-attempt.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/claude-sdk-oauth/session-turn-attempt.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEpE,OAAO,EACN,KAAK,0BAA0B,EAI/B,MAAM,uBAAuB,CAAC;AAI/B,KAAK,wBAAwB,GAAG;IAAE,IAAI,IAAI,IAAI,CAAA;CAAE,CAAC;AAsCjD,wBAAgB,wBAAwB,CACvC,KAAK,EAAE,0BAA0B,EACjC,OAAO,EAAE,cAAc,CAAC,SAAS,CAAC,EAClC,MAAM,EAAE,SAAS,MAAM,EAAE,EACzB,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,MAAM,EAAE,wBAAwB;;mBA4ClB,IAAI;EAQlB"}
1
+ {"version":3,"file":"session-turn-attempt.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/claude-sdk-oauth/session-turn-attempt.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,UAAU,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAEpE,OAAO,EACN,KAAK,0BAA0B,EAI/B,MAAM,uBAAuB,CAAC;AAI/B,KAAK,wBAAwB,GAAG;IAAE,IAAI,IAAI,IAAI,CAAA;CAAE,CAAC;AAsCjD,wBAAgB,wBAAwB,CACvC,KAAK,EAAE,0BAA0B,EACjC,OAAO,EAAE,cAAc,CAAC,SAAS,CAAC,EAClC,MAAM,EAAE,SAAS,MAAM,EAAE,EACzB,MAAM,EAAE,WAAW,GAAG,SAAS,EAC/B,MAAM,EAAE,wBAAwB;;mBAkDlB,IAAI;EAQlB"}
@@ -60,6 +60,14 @@ export function createSessionTurnAttempt(entry, message, hashes, signal, staged)
60
60
  else {
61
61
  rememberRetryCheckpoint(entry, hashes);
62
62
  }
63
+ // Emit only when this attempt was consumed to completion (retained). A
64
+ // discarded attempt (failover to another account) unwinds through the
65
+ // generator's return() and never reaches here; an internally-failed
66
+ // attempt throws to the catch below. Both stay silent so the turn yields
67
+ // exactly one continuity observation - this retained attempt, or the
68
+ // single terminal observation residentSessionMessages emits when every
69
+ // attempt fails.
70
+ staged.emit();
63
71
  }
64
72
  catch (error) {
65
73
  // The queue failed (completion rejected: pump failure, query end,
@@ -74,9 +82,6 @@ export function createSessionTurnAttempt(entry, message, hashes, signal, staged)
74
82
  }
75
83
  throw error;
76
84
  }
77
- finally {
78
- staged.emit();
79
- }
80
85
  })(),
81
86
  discard: () => {
82
87
  if (resumeTargetMissing)
@@ -1 +1 @@
1
- {"version":3,"file":"session-turn-attempt.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/claude-sdk-oauth/session-turn-attempt.ts"],"names":[],"mappings":"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","sourcesContent":["import { BoundedAsyncQueue, SESSION_STREAM_QUEUE_CAPACITY } from \"./bounded-queue.ts\";\nimport { sdkResultFailure } from \"./errors.ts\";\nimport type { SDKMessage, SDKUserMessage } from \"./sdk-boundary.ts\";\nimport { bindingFromEntry, forgetBinding, rememberBinding } from \"./session-reattach.ts\";\nimport {\n\ttype ClaudeSdkOauthSessionEntry,\n\tcloseSession,\n\tisCurrentGeneration,\n\tsessionRegistry,\n} from \"./session-registry.ts\";\nimport { submitSessionTurn } from \"./session-registry-pump.ts\";\nimport { recordSyncedStream, sentHashPrefixDigest } from \"./session-sync.ts\";\n\ntype StagedContinuityDecision = { emit(): void };\n\nfunction successfulTurn(messages: readonly SDKMessage[]): boolean {\n\treturn messages.some(\n\t\t(message) =>\n\t\t\tmessage.type === \"result\" && message.subtype === \"success\" && sdkResultFailure(message) === undefined,\n\t);\n}\n\nfunction recordAssistantUuid(entry: ClaudeSdkOauthSessionEntry, sentCount: number, message: SDKMessage): void {\n\tif (message.type === \"assistant\" && message.parent_tool_use_id === null) {\n\t\tentry.assistantUuidByIndex.set(sentCount, message.uuid);\n\t}\n}\n\n/**\n * An attempt that pushed its user payload and then aborted or failed leaves that\n * message on the lineage un-answered: `recordSyncedStream` never ran, so the\n * entry still points at the PRE-TURN boundary. Remembering the binding at that\n * boundary, tagged with the attempted turn's full sent-stream digest, lets the\n * SAME turn's retry fork past the orphaned message instead of appending it\n * twice (issue #723 retry storm). In-memory only — nothing here is persisted.\n */\n/** Claude Code answered \"No conversation found with session ID\": the bound id is dead, never resume it again. */\nconst RESUME_TARGET_MISSING = /no conversation found with session id/i;\n\nfunction publishBinding(entry: ClaudeSdkOauthSessionEntry, binding: Parameters<typeof rememberBinding>[0]): void {\n\trememberBinding({ ...binding, sdkSessionIdConfirmed: entry.sdkSessionIdConfirmed });\n}\n\nfunction rememberRetryCheckpoint(entry: ClaudeSdkOauthSessionEntry, hashes: readonly string[]): void {\n\tif (entry.sentCount < 0 || entry.sentCount > hashes.length) return;\n\tpublishBinding(entry, {\n\t\t...bindingFromEntry(entry, hashes.slice(0, entry.sentCount)),\n\t\tunansweredTurnDigest: sentHashPrefixDigest(hashes, hashes.length),\n\t});\n}\n\nexport function createSessionTurnAttempt(\n\tentry: ClaudeSdkOauthSessionEntry,\n\tmessage: SDKUserMessage[\"message\"],\n\thashes: readonly string[],\n\tsignal: AbortSignal | undefined,\n\tstaged: StagedContinuityDecision,\n) {\n\tconst generation = entry.generation;\n\t// Claude Code declared the bound id dead: no later cleanup may re-publish it.\n\tlet resumeTargetMissing = false;\n\treturn {\n\t\tmessages: (async function* (): AsyncGenerator<SDKMessage> {\n\t\t\tconst queue = new BoundedAsyncQueue<SDKMessage>(SESSION_STREAM_QUEUE_CAPACITY);\n\t\t\tconst completion = submitSessionTurn(sessionRegistry, entry, {\n\t\t\t\tmessage,\n\t\t\t\tsignal,\n\t\t\t\tonMessage: (sdkMessage) => {\n\t\t\t\t\trecordAssistantUuid(entry, hashes.length, sdkMessage);\n\t\t\t\t\tqueue.push(sdkMessage);\n\t\t\t\t},\n\t\t\t});\n\t\t\tvoid completion.then(\n\t\t\t\t() => queue.close(),\n\t\t\t\t(error: unknown) => queue.fail(error),\n\t\t\t);\n\t\t\ttry {\n\t\t\t\tfor await (const sdkMessage of queue) yield sdkMessage;\n\t\t\t\tconst turn = await completion;\n\t\t\t\tif (!turn.aborted && successfulTurn(turn.messages)) {\n\t\t\t\t\trecordSyncedStream(entry, hashes);\n\t\t\t\t\tpublishBinding(entry, bindingFromEntry(entry, hashes));\n\t\t\t\t} else {\n\t\t\t\t\trememberRetryCheckpoint(entry, hashes);\n\t\t\t\t}\n\t\t\t} catch (error) {\n\t\t\t\t// The queue failed (completion rejected: pump failure, query end,\n\t\t\t\t// attribution error). The payload was still pushed, so the retry needs\n\t\t\t\t// the same checkpoint the aborted path records.\n\t\t\t\tif (error instanceof Error && RESUME_TARGET_MISSING.test(error.message)) {\n\t\t\t\t\tresumeTargetMissing = true;\n\t\t\t\t\tforgetBinding(entry.senpiSessionId);\n\t\t\t\t} else {\n\t\t\t\t\trememberRetryCheckpoint(entry, hashes);\n\t\t\t\t}\n\t\t\t\tthrow error;\n\t\t\t} finally {\n\t\t\t\tstaged.emit();\n\t\t\t}\n\t\t})(),\n\t\tdiscard: (): void => {\n\t\t\tif (resumeTargetMissing) forgetBinding(entry.senpiSessionId);\n\t\t\telse rememberRetryCheckpoint(entry, hashes);\n\t\t\tif (isCurrentGeneration(entry.senpiSessionId, generation)) {\n\t\t\t\tcloseSession(entry.senpiSessionId, \"attempt_discarded\");\n\t\t\t}\n\t\t},\n\t};\n}\n"]}
1
+ {"version":3,"file":"session-turn-attempt.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/claude-sdk-oauth/session-turn-attempt.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,iBAAiB,EAAE,6BAA6B,EAAE,MAAM,oBAAoB,CAAC;AACtF,OAAO,EAAE,gBAAgB,EAAE,MAAM,aAAa,CAAC;AAE/C,OAAO,EAAE,gBAAgB,EAAE,aAAa,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACzF,OAAO,EAEN,YAAY,EACZ,mBAAmB,EACnB,eAAe,GACf,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAC/D,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,mBAAmB,CAAC;AAI7E,SAAS,cAAc,CAAC,QAA+B;IACtD,OAAO,QAAQ,CAAC,IAAI,CACnB,CAAC,OAAO,EAAE,EAAE,CACX,OAAO,CAAC,IAAI,KAAK,QAAQ,IAAI,OAAO,CAAC,OAAO,KAAK,SAAS,IAAI,gBAAgB,CAAC,OAAO,CAAC,KAAK,SAAS,CACtG,CAAC;AACH,CAAC;AAED,SAAS,mBAAmB,CAAC,KAAiC,EAAE,SAAiB,EAAE,OAAmB;IACrG,IAAI,OAAO,CAAC,IAAI,KAAK,WAAW,IAAI,OAAO,CAAC,kBAAkB,KAAK,IAAI,EAAE,CAAC;QACzE,KAAK,CAAC,oBAAoB,CAAC,GAAG,CAAC,SAAS,EAAE,OAAO,CAAC,IAAI,CAAC,CAAC;IACzD,CAAC;AACF,CAAC;AAED;;;;;;;GAOG;AACH,iHAAiH;AACjH,MAAM,qBAAqB,GAAG,wCAAwC,CAAC;AAEvE,SAAS,cAAc,CAAC,KAAiC,EAAE,OAA8C;IACxG,eAAe,CAAC,EAAE,GAAG,OAAO,EAAE,qBAAqB,EAAE,KAAK,CAAC,qBAAqB,EAAE,CAAC,CAAC;AACrF,CAAC;AAED,SAAS,uBAAuB,CAAC,KAAiC,EAAE,MAAyB;IAC5F,IAAI,KAAK,CAAC,SAAS,GAAG,CAAC,IAAI,KAAK,CAAC,SAAS,GAAG,MAAM,CAAC,MAAM;QAAE,OAAO;IACnE,cAAc,CAAC,KAAK,EAAE;QACrB,GAAG,gBAAgB,CAAC,KAAK,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC;QAC5D,oBAAoB,EAAE,oBAAoB,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,CAAC;KACjE,CAAC,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,wBAAwB,CACvC,KAAiC,EACjC,OAAkC,EAClC,MAAyB,EACzB,MAA+B,EAC/B,MAAgC;IAEhC,MAAM,UAAU,GAAG,KAAK,CAAC,UAAU,CAAC;IACpC,8EAA8E;IAC9E,IAAI,mBAAmB,GAAG,KAAK,CAAC;IAChC,OAAO;QACN,QAAQ,EAAE,CAAC,KAAK,SAAS,CAAC;YACzB,MAAM,KAAK,GAAG,IAAI,iBAAiB,CAAa,6BAA6B,CAAC,CAAC;YAC/E,MAAM,UAAU,GAAG,iBAAiB,CAAC,eAAe,EAAE,KAAK,EAAE;gBAC5D,OAAO;gBACP,MAAM;gBACN,SAAS,EAAE,CAAC,UAAU,EAAE,EAAE;oBACzB,mBAAmB,CAAC,KAAK,EAAE,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;oBACtD,KAAK,CAAC,IAAI,CAAC,UAAU,CAAC,CAAC;gBACxB,CAAC;aACD,CAAC,CAAC;YACH,KAAK,UAAU,CAAC,IAAI,CACnB,GAAG,EAAE,CAAC,KAAK,CAAC,KAAK,EAAE,EACnB,CAAC,KAAc,EAAE,EAAE,CAAC,KAAK,CAAC,IAAI,CAAC,KAAK,CAAC,CACrC,CAAC;YACF,IAAI,CAAC;gBACJ,IAAI,KAAK,EAAE,MAAM,UAAU,IAAI,KAAK;oBAAE,MAAM,UAAU,CAAC;gBACvD,MAAM,IAAI,GAAG,MAAM,UAAU,CAAC;gBAC9B,IAAI,CAAC,IAAI,CAAC,OAAO,IAAI,cAAc,CAAC,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC;oBACpD,kBAAkB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;oBAClC,cAAc,CAAC,KAAK,EAAE,gBAAgB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,CAAC;gBACxD,CAAC;qBAAM,CAAC;oBACP,uBAAuB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;gBACxC,CAAC;gBACD,uEAAuE;gBACvE,sEAAsE;gBACtE,oEAAoE;gBACpE,yEAAyE;gBACzE,qEAAqE;gBACrE,uEAAuE;gBACvE,iBAAiB;gBACjB,MAAM,CAAC,IAAI,EAAE,CAAC;YACf,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBAChB,kEAAkE;gBAClE,uEAAuE;gBACvE,gDAAgD;gBAChD,IAAI,KAAK,YAAY,KAAK,IAAI,qBAAqB,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;oBACzE,mBAAmB,GAAG,IAAI,CAAC;oBAC3B,aAAa,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;gBACrC,CAAC;qBAAM,CAAC;oBACP,uBAAuB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;gBACxC,CAAC;gBACD,MAAM,KAAK,CAAC;YACb,CAAC;QACF,CAAC,CAAC,EAAE;QACJ,OAAO,EAAE,GAAS,EAAE;YACnB,IAAI,mBAAmB;gBAAE,aAAa,CAAC,KAAK,CAAC,cAAc,CAAC,CAAC;;gBACxD,uBAAuB,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC;YAC5C,IAAI,mBAAmB,CAAC,KAAK,CAAC,cAAc,EAAE,UAAU,CAAC,EAAE,CAAC;gBAC3D,YAAY,CAAC,KAAK,CAAC,cAAc,EAAE,mBAAmB,CAAC,CAAC;YACzD,CAAC;QACF,CAAC;KACD,CAAC;AACH,CAAC","sourcesContent":["import { BoundedAsyncQueue, SESSION_STREAM_QUEUE_CAPACITY } from \"./bounded-queue.ts\";\nimport { sdkResultFailure } from \"./errors.ts\";\nimport type { SDKMessage, SDKUserMessage } from \"./sdk-boundary.ts\";\nimport { bindingFromEntry, forgetBinding, rememberBinding } from \"./session-reattach.ts\";\nimport {\n\ttype ClaudeSdkOauthSessionEntry,\n\tcloseSession,\n\tisCurrentGeneration,\n\tsessionRegistry,\n} from \"./session-registry.ts\";\nimport { submitSessionTurn } from \"./session-registry-pump.ts\";\nimport { recordSyncedStream, sentHashPrefixDigest } from \"./session-sync.ts\";\n\ntype StagedContinuityDecision = { emit(): void };\n\nfunction successfulTurn(messages: readonly SDKMessage[]): boolean {\n\treturn messages.some(\n\t\t(message) =>\n\t\t\tmessage.type === \"result\" && message.subtype === \"success\" && sdkResultFailure(message) === undefined,\n\t);\n}\n\nfunction recordAssistantUuid(entry: ClaudeSdkOauthSessionEntry, sentCount: number, message: SDKMessage): void {\n\tif (message.type === \"assistant\" && message.parent_tool_use_id === null) {\n\t\tentry.assistantUuidByIndex.set(sentCount, message.uuid);\n\t}\n}\n\n/**\n * An attempt that pushed its user payload and then aborted or failed leaves that\n * message on the lineage un-answered: `recordSyncedStream` never ran, so the\n * entry still points at the PRE-TURN boundary. Remembering the binding at that\n * boundary, tagged with the attempted turn's full sent-stream digest, lets the\n * SAME turn's retry fork past the orphaned message instead of appending it\n * twice (issue #723 retry storm). In-memory only — nothing here is persisted.\n */\n/** Claude Code answered \"No conversation found with session ID\": the bound id is dead, never resume it again. */\nconst RESUME_TARGET_MISSING = /no conversation found with session id/i;\n\nfunction publishBinding(entry: ClaudeSdkOauthSessionEntry, binding: Parameters<typeof rememberBinding>[0]): void {\n\trememberBinding({ ...binding, sdkSessionIdConfirmed: entry.sdkSessionIdConfirmed });\n}\n\nfunction rememberRetryCheckpoint(entry: ClaudeSdkOauthSessionEntry, hashes: readonly string[]): void {\n\tif (entry.sentCount < 0 || entry.sentCount > hashes.length) return;\n\tpublishBinding(entry, {\n\t\t...bindingFromEntry(entry, hashes.slice(0, entry.sentCount)),\n\t\tunansweredTurnDigest: sentHashPrefixDigest(hashes, hashes.length),\n\t});\n}\n\nexport function createSessionTurnAttempt(\n\tentry: ClaudeSdkOauthSessionEntry,\n\tmessage: SDKUserMessage[\"message\"],\n\thashes: readonly string[],\n\tsignal: AbortSignal | undefined,\n\tstaged: StagedContinuityDecision,\n) {\n\tconst generation = entry.generation;\n\t// Claude Code declared the bound id dead: no later cleanup may re-publish it.\n\tlet resumeTargetMissing = false;\n\treturn {\n\t\tmessages: (async function* (): AsyncGenerator<SDKMessage> {\n\t\t\tconst queue = new BoundedAsyncQueue<SDKMessage>(SESSION_STREAM_QUEUE_CAPACITY);\n\t\t\tconst completion = submitSessionTurn(sessionRegistry, entry, {\n\t\t\t\tmessage,\n\t\t\t\tsignal,\n\t\t\t\tonMessage: (sdkMessage) => {\n\t\t\t\t\trecordAssistantUuid(entry, hashes.length, sdkMessage);\n\t\t\t\t\tqueue.push(sdkMessage);\n\t\t\t\t},\n\t\t\t});\n\t\t\tvoid completion.then(\n\t\t\t\t() => queue.close(),\n\t\t\t\t(error: unknown) => queue.fail(error),\n\t\t\t);\n\t\t\ttry {\n\t\t\t\tfor await (const sdkMessage of queue) yield sdkMessage;\n\t\t\t\tconst turn = await completion;\n\t\t\t\tif (!turn.aborted && successfulTurn(turn.messages)) {\n\t\t\t\t\trecordSyncedStream(entry, hashes);\n\t\t\t\t\tpublishBinding(entry, bindingFromEntry(entry, hashes));\n\t\t\t\t} else {\n\t\t\t\t\trememberRetryCheckpoint(entry, hashes);\n\t\t\t\t}\n\t\t\t\t// Emit only when this attempt was consumed to completion (retained). A\n\t\t\t\t// discarded attempt (failover to another account) unwinds through the\n\t\t\t\t// generator's return() and never reaches here; an internally-failed\n\t\t\t\t// attempt throws to the catch below. Both stay silent so the turn yields\n\t\t\t\t// exactly one continuity observation - this retained attempt, or the\n\t\t\t\t// single terminal observation residentSessionMessages emits when every\n\t\t\t\t// attempt fails.\n\t\t\t\tstaged.emit();\n\t\t\t} catch (error) {\n\t\t\t\t// The queue failed (completion rejected: pump failure, query end,\n\t\t\t\t// attribution error). The payload was still pushed, so the retry needs\n\t\t\t\t// the same checkpoint the aborted path records.\n\t\t\t\tif (error instanceof Error && RESUME_TARGET_MISSING.test(error.message)) {\n\t\t\t\t\tresumeTargetMissing = true;\n\t\t\t\t\tforgetBinding(entry.senpiSessionId);\n\t\t\t\t} else {\n\t\t\t\t\trememberRetryCheckpoint(entry, hashes);\n\t\t\t\t}\n\t\t\t\tthrow error;\n\t\t\t}\n\t\t})(),\n\t\tdiscard: (): void => {\n\t\t\tif (resumeTargetMissing) forgetBinding(entry.senpiSessionId);\n\t\t\telse rememberRetryCheckpoint(entry, hashes);\n\t\t\tif (isCurrentGeneration(entry.senpiSessionId, generation)) {\n\t\t\t\tcloseSession(entry.senpiSessionId, \"attempt_discarded\");\n\t\t\t}\n\t\t},\n\t};\n}\n"]}
@@ -4,11 +4,12 @@ export declare const SDK_TO_PI_TOOL_NAME: Readonly<Record<string, string>>;
4
4
  export declare const PI_TO_SDK_TOOL_NAME: Readonly<Record<string, string>>;
5
5
  /**
6
6
  * Versioned host-tool denial policy. `configFingerprint` in session-sync.ts hashes this as
7
- * `hostToolPolicy` into `toolsetHash`; a mismatch is `options_changed` and retires the live
8
- * resident query. The same hash is persisted in the restart sidecar, so a bump also makes
9
- * the first admission after an upgrade cold-seed (`flatten` / `options_changed`) instead of
10
- * resuming the pre-bump SDK session - once per session, intended. Bump when denial copy or
11
- * hooks change so wording-only edits cannot keep serving the old reason.
7
+ * `hostToolPolicy` into `toolsetHash`; a mismatch is `toolset_changed` and retires the live
8
+ * resident query (reattach with the current hooks). The same hash is persisted in the
9
+ * restart sidecar, so the first admission after an upgrade also reattaches with
10
+ * `toolset_changed` - the resumed query carries the current hooks, so the pre-bump
11
+ * denial text is never served again. Bump when denial copy or hooks change so
12
+ * wording-only edits cannot keep serving the old reason.
12
13
  */
13
14
  export declare const HOST_TOOL_POLICY_FINGERPRINT = "host-tool-denial-v2";
14
15
  export declare const BUILTIN_SDK_TOOLS: readonly ["Read", "Write", "Edit", "Bash", "Grep", "Glob"];
@@ -1 +1 @@
1
- {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/claude-sdk-oauth/tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAEjD,eAAO,MAAM,mBAAmB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAOhE,CAAC;AAEF,eAAO,MAAM,mBAAmB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAQhE,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,4BAA4B,wBAAwB,CAAC;AAElE,eAAO,MAAM,iBAAiB,4DAA6D,CAAC;AAC5F,eAAO,MAAM,6BAA6B,QAEY,CAAC;AACvD,eAAO,MAAM,kCAAkC,QAAgC,CAAC;AAChF,eAAO,MAAM,4BAA4B,iBAAiB,CAAC;AAC3D,eAAO,MAAM,uBAAuB,wBAA2C,CAAC;AAChF,eAAO,MAAM,8BAA8B,yDAAyD,CAAC;AACrG,eAAO,MAAM,sBAAsB,EAAE,WAAW,CAAC,OAAO,CAAC,OAAO,CAAC,CAgBhE,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC9B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,WAAW,EAAE,IAAI,EAAE,CAAC;IACpB,mBAAmB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,kBAAkB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACxC,CAAC;AAUF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,mBAAmB,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAQ1G;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,kBAAkB,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAQzG;AAED,qFAAqF;AACrF,wBAAgB,WAAW,CAC1B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,SAAS,GACjD,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAuCzB;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,gBAAgB,CA6BlE;AAED,eAAO,MAAM,UAAU,EAAE,WAAW,CAAC,OAAO,CAAC,YAAY,CAAC,CAGxD,CAAC"}
1
+ {"version":3,"file":"tools.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/claude-sdk-oauth/tools.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,uBAAuB,CAAC;AAC3D,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,mBAAmB,CAAC;AAEjD,eAAO,MAAM,mBAAmB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAOhE,CAAC;AAEF,eAAO,MAAM,mBAAmB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAQhE,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,4BAA4B,wBAAwB,CAAC;AAElE,eAAO,MAAM,iBAAiB,4DAA6D,CAAC;AAC5F,eAAO,MAAM,6BAA6B,QAEY,CAAC;AACvD,eAAO,MAAM,kCAAkC,QAAgC,CAAC;AAChF,eAAO,MAAM,4BAA4B,iBAAiB,CAAC;AAC3D,eAAO,MAAM,uBAAuB,wBAA2C,CAAC;AAChF,eAAO,MAAM,8BAA8B,yDAAyD,CAAC;AACrG,eAAO,MAAM,sBAAsB,EAAE,WAAW,CAAC,OAAO,CAAC,OAAO,CAAC,CAgBhE,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC9B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,WAAW,EAAE,IAAI,EAAE,CAAC;IACpB,mBAAmB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IACzC,kBAAkB,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;CACxC,CAAC;AAUF,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,mBAAmB,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAQ1G;AAED,wBAAgB,kBAAkB,CAAC,IAAI,EAAE,MAAM,EAAE,kBAAkB,CAAC,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,MAAM,CAQzG;AAED,qFAAqF;AACrF,wBAAgB,WAAW,CAC1B,QAAQ,EAAE,MAAM,EAChB,IAAI,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAAG,SAAS,GACjD,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAuCzB;AAED,wBAAgB,eAAe,CAAC,OAAO,EAAE,OAAO,GAAG,gBAAgB,CA6BlE;AAED,eAAO,MAAM,UAAU,EAAE,WAAW,CAAC,OAAO,CAAC,YAAY,CAAC,CAGxD,CAAC"}
@@ -17,11 +17,12 @@ export const PI_TO_SDK_TOOL_NAME = {
17
17
  };
18
18
  /**
19
19
  * Versioned host-tool denial policy. `configFingerprint` in session-sync.ts hashes this as
20
- * `hostToolPolicy` into `toolsetHash`; a mismatch is `options_changed` and retires the live
21
- * resident query. The same hash is persisted in the restart sidecar, so a bump also makes
22
- * the first admission after an upgrade cold-seed (`flatten` / `options_changed`) instead of
23
- * resuming the pre-bump SDK session - once per session, intended. Bump when denial copy or
24
- * hooks change so wording-only edits cannot keep serving the old reason.
20
+ * `hostToolPolicy` into `toolsetHash`; a mismatch is `toolset_changed` and retires the live
21
+ * resident query (reattach with the current hooks). The same hash is persisted in the
22
+ * restart sidecar, so the first admission after an upgrade also reattaches with
23
+ * `toolset_changed` - the resumed query carries the current hooks, so the pre-bump
24
+ * denial text is never served again. Bump when denial copy or hooks change so
25
+ * wording-only edits cannot keep serving the old reason.
25
26
  */
26
27
  export const HOST_TOOL_POLICY_FINGERPRINT = "host-tool-denial-v2";
27
28
  export const BUILTIN_SDK_TOOLS = ["Read", "Write", "Edit", "Bash", "Grep", "Glob"];
@@ -1 +1 @@
1
- {"version":3,"file":"tools.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/claude-sdk-oauth/tools.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Context, Tool } from \"@earendil-works/pi-ai\";\nimport type { Options } from \"./sdk-boundary.ts\";\n\nexport const SDK_TO_PI_TOOL_NAME: Readonly<Record<string, string>> = {\n\tread: \"read\",\n\twrite: \"write\",\n\tedit: \"edit\",\n\tbash: \"bash\",\n\tgrep: \"grep\",\n\tglob: \"find\",\n};\n\nexport const PI_TO_SDK_TOOL_NAME: Readonly<Record<string, string>> = {\n\tread: \"Read\",\n\twrite: \"Write\",\n\tedit: \"Edit\",\n\tbash: \"Bash\",\n\tgrep: \"Grep\",\n\tfind: \"Glob\",\n\tglob: \"Glob\",\n};\n\n/**\n * Versioned host-tool denial policy. `configFingerprint` in session-sync.ts hashes this as\n * `hostToolPolicy` into `toolsetHash`; a mismatch is `options_changed` and retires the live\n * resident query. The same hash is persisted in the restart sidecar, so a bump also makes\n * the first admission after an upgrade cold-seed (`flatten` / `options_changed`) instead of\n * resuming the pre-bump SDK session - once per session, intended. Bump when denial copy or\n * hooks change so wording-only edits cannot keep serving the old reason.\n */\nexport const HOST_TOOL_POLICY_FINGERPRINT = \"host-tool-denial-v2\";\n\nexport const BUILTIN_SDK_TOOLS = [\"Read\", \"Write\", \"Edit\", \"Bash\", \"Grep\", \"Glob\"] as const;\nexport const TOOL_EXECUTION_DENIED_MESSAGE =\n\t\"Senpi executes this tool on the host and returns its result as the next user message. \" +\n\t\"Wait for that result; this denial is not a failure.\";\nexport const HOST_TOOL_EXECUTION_DENIED_MESSAGE = TOOL_EXECUTION_DENIED_MESSAGE;\nexport const CUSTOM_TOOLS_MCP_SERVER_NAME = \"custom-tools\";\nexport const CUSTOM_TOOLS_MCP_PREFIX = `mcp__${CUSTOM_TOOLS_MCP_SERVER_NAME}__`;\nexport const HOST_CAPTURED_SDK_TOOL_MATCHER = \"Bash|Write|Edit|Read|Grep|Glob|mcp__custom-tools__.*\";\nexport const HOST_TOOL_DENIAL_HOOKS: NonNullable<Options[\"hooks\"]> = {\n\tPreToolUse: [\n\t\t{\n\t\t\tmatcher: HOST_CAPTURED_SDK_TOOL_MATCHER,\n\t\t\thooks: [\n\t\t\t\tasync () => ({\n\t\t\t\t\tcontinue: false,\n\t\t\t\t\thookSpecificOutput: {\n\t\t\t\t\t\thookEventName: \"PreToolUse\",\n\t\t\t\t\t\tpermissionDecision: \"deny\",\n\t\t\t\t\t\tpermissionDecisionReason: HOST_TOOL_EXECUTION_DENIED_MESSAGE,\n\t\t\t\t\t},\n\t\t\t\t}),\n\t\t\t],\n\t\t},\n\t],\n};\n\nexport type ResolvedSdkTools = {\n\tsdkTools: string[];\n\tcustomTools: Tool[];\n\tcustomToolNameToSdk: Map<string, string>;\n\tcustomToolNameToPi: Map<string, string>;\n};\n\nfunction pascalCase(value: string): string {\n\treturn value\n\t\t.split(/[^a-zA-Z0-9]+/)\n\t\t.filter(Boolean)\n\t\t.map((part) => `${part[0]?.toUpperCase() ?? \"\"}${part.slice(1)}`)\n\t\t.join(\"\");\n}\n\nexport function mapPiToolNameToSdk(name: string, customToolNameToSdk?: ReadonlyMap<string, string>): string {\n\tconst normalized = name.toLowerCase();\n\treturn (\n\t\tcustomToolNameToSdk?.get(name) ??\n\t\tcustomToolNameToSdk?.get(normalized) ??\n\t\tPI_TO_SDK_TOOL_NAME[normalized] ??\n\t\tpascalCase(name)\n\t);\n}\n\nexport function mapSdkToolNameToPi(name: string, customToolNameToPi?: ReadonlyMap<string, string>): string {\n\tconst normalized = name.toLowerCase();\n\treturn (\n\t\tSDK_TO_PI_TOOL_NAME[normalized] ??\n\t\tcustomToolNameToPi?.get(name) ??\n\t\tcustomToolNameToPi?.get(normalized) ??\n\t\t(normalized.startsWith(CUSTOM_TOOLS_MCP_PREFIX) ? name.slice(CUSTOM_TOOLS_MCP_PREFIX.length) : name)\n\t);\n}\n\n/** Translate Claude Code's built-in-tool inputs into senpi's active tool schemas. */\nexport function mapToolArgs(\n\ttoolName: string,\n\targs: Readonly<Record<string, unknown>> | undefined,\n): Record<string, unknown> {\n\tconst input = args ?? {};\n\tswitch (toolName.toLowerCase()) {\n\t\tcase \"read\":\n\t\t\treturn { path: input.file_path ?? input.path, offset: input.offset, limit: input.limit };\n\t\tcase \"write\":\n\t\t\treturn { path: input.file_path ?? input.path, content: input.content };\n\t\tcase \"edit\":\n\t\t\treturn {\n\t\t\t\tpath: input.file_path ?? input.path,\n\t\t\t\tedits: Array.isArray(input.edits)\n\t\t\t\t\t? input.edits\n\t\t\t\t\t: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\toldText: input.old_string ?? input.oldText ?? input.old_text,\n\t\t\t\t\t\t\t\tnewText: input.new_string ?? input.newText ?? input.new_text,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t};\n\t\tcase \"bash\":\n\t\t\treturn {\n\t\t\t\tcommand: input.command,\n\t\t\t\ttimeout: typeof input.timeout === \"number\" ? input.timeout / 1_000 : input.timeout,\n\t\t\t};\n\t\tcase \"grep\":\n\t\t\treturn {\n\t\t\t\tpattern: input.pattern,\n\t\t\t\tpath: input.path,\n\t\t\t\tglob: input.glob,\n\t\t\t\tignoreCase: input[\"-i\"] ?? input.ignoreCase,\n\t\t\t\tcontext: input.context ?? input[\"-C\"],\n\t\t\t\tlimit: input.head_limit ?? input.limit,\n\t\t\t};\n\t\tcase \"glob\":\n\t\tcase \"find\":\n\t\t\treturn { pattern: input.pattern, path: input.path, limit: input.limit };\n\t\tdefault:\n\t\t\treturn { ...input };\n\t}\n}\n\nexport function resolveSdkTools(context: Context): ResolvedSdkTools {\n\tif (!context.tools) {\n\t\treturn {\n\t\t\tsdkTools: [...BUILTIN_SDK_TOOLS],\n\t\t\tcustomTools: [],\n\t\t\tcustomToolNameToSdk: new Map(),\n\t\t\tcustomToolNameToPi: new Map(),\n\t\t};\n\t}\n\n\tconst sdkTools = new Set<string>();\n\tconst customTools: Tool[] = [];\n\tconst customToolNameToSdk = new Map<string, string>();\n\tconst customToolNameToPi = new Map<string, string>();\n\tfor (const tool of context.tools) {\n\t\tconst normalized = tool.name.toLowerCase();\n\t\tconst sdkName = PI_TO_SDK_TOOL_NAME[normalized];\n\t\tif (sdkName) {\n\t\t\tsdkTools.add(sdkName);\n\t\t\tcontinue;\n\t\t}\n\t\tconst customSdkName = `${CUSTOM_TOOLS_MCP_PREFIX}${tool.name}`;\n\t\tcustomTools.push(tool);\n\t\tcustomToolNameToSdk.set(tool.name, customSdkName);\n\t\tcustomToolNameToSdk.set(normalized, customSdkName);\n\t\tcustomToolNameToPi.set(customSdkName, tool.name);\n\t\tcustomToolNameToPi.set(customSdkName.toLowerCase(), tool.name);\n\t}\n\treturn { sdkTools: [...sdkTools], customTools, customToolNameToSdk, customToolNameToPi };\n}\n\nexport const canUseTool: NonNullable<Options[\"canUseTool\"]> = async () => ({\n\tbehavior: \"deny\",\n\tmessage: TOOL_EXECUTION_DENIED_MESSAGE,\n});\n"]}
1
+ {"version":3,"file":"tools.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/claude-sdk-oauth/tools.ts"],"names":[],"mappings":"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","sourcesContent":["import type { Context, Tool } from \"@earendil-works/pi-ai\";\nimport type { Options } from \"./sdk-boundary.ts\";\n\nexport const SDK_TO_PI_TOOL_NAME: Readonly<Record<string, string>> = {\n\tread: \"read\",\n\twrite: \"write\",\n\tedit: \"edit\",\n\tbash: \"bash\",\n\tgrep: \"grep\",\n\tglob: \"find\",\n};\n\nexport const PI_TO_SDK_TOOL_NAME: Readonly<Record<string, string>> = {\n\tread: \"Read\",\n\twrite: \"Write\",\n\tedit: \"Edit\",\n\tbash: \"Bash\",\n\tgrep: \"Grep\",\n\tfind: \"Glob\",\n\tglob: \"Glob\",\n};\n\n/**\n * Versioned host-tool denial policy. `configFingerprint` in session-sync.ts hashes this as\n * `hostToolPolicy` into `toolsetHash`; a mismatch is `toolset_changed` and retires the live\n * resident query (reattach with the current hooks). The same hash is persisted in the\n * restart sidecar, so the first admission after an upgrade also reattaches with\n * `toolset_changed` - the resumed query carries the current hooks, so the pre-bump\n * denial text is never served again. Bump when denial copy or hooks change so\n * wording-only edits cannot keep serving the old reason.\n */\nexport const HOST_TOOL_POLICY_FINGERPRINT = \"host-tool-denial-v2\";\n\nexport const BUILTIN_SDK_TOOLS = [\"Read\", \"Write\", \"Edit\", \"Bash\", \"Grep\", \"Glob\"] as const;\nexport const TOOL_EXECUTION_DENIED_MESSAGE =\n\t\"Senpi executes this tool on the host and returns its result as the next user message. \" +\n\t\"Wait for that result; this denial is not a failure.\";\nexport const HOST_TOOL_EXECUTION_DENIED_MESSAGE = TOOL_EXECUTION_DENIED_MESSAGE;\nexport const CUSTOM_TOOLS_MCP_SERVER_NAME = \"custom-tools\";\nexport const CUSTOM_TOOLS_MCP_PREFIX = `mcp__${CUSTOM_TOOLS_MCP_SERVER_NAME}__`;\nexport const HOST_CAPTURED_SDK_TOOL_MATCHER = \"Bash|Write|Edit|Read|Grep|Glob|mcp__custom-tools__.*\";\nexport const HOST_TOOL_DENIAL_HOOKS: NonNullable<Options[\"hooks\"]> = {\n\tPreToolUse: [\n\t\t{\n\t\t\tmatcher: HOST_CAPTURED_SDK_TOOL_MATCHER,\n\t\t\thooks: [\n\t\t\t\tasync () => ({\n\t\t\t\t\tcontinue: false,\n\t\t\t\t\thookSpecificOutput: {\n\t\t\t\t\t\thookEventName: \"PreToolUse\",\n\t\t\t\t\t\tpermissionDecision: \"deny\",\n\t\t\t\t\t\tpermissionDecisionReason: HOST_TOOL_EXECUTION_DENIED_MESSAGE,\n\t\t\t\t\t},\n\t\t\t\t}),\n\t\t\t],\n\t\t},\n\t],\n};\n\nexport type ResolvedSdkTools = {\n\tsdkTools: string[];\n\tcustomTools: Tool[];\n\tcustomToolNameToSdk: Map<string, string>;\n\tcustomToolNameToPi: Map<string, string>;\n};\n\nfunction pascalCase(value: string): string {\n\treturn value\n\t\t.split(/[^a-zA-Z0-9]+/)\n\t\t.filter(Boolean)\n\t\t.map((part) => `${part[0]?.toUpperCase() ?? \"\"}${part.slice(1)}`)\n\t\t.join(\"\");\n}\n\nexport function mapPiToolNameToSdk(name: string, customToolNameToSdk?: ReadonlyMap<string, string>): string {\n\tconst normalized = name.toLowerCase();\n\treturn (\n\t\tcustomToolNameToSdk?.get(name) ??\n\t\tcustomToolNameToSdk?.get(normalized) ??\n\t\tPI_TO_SDK_TOOL_NAME[normalized] ??\n\t\tpascalCase(name)\n\t);\n}\n\nexport function mapSdkToolNameToPi(name: string, customToolNameToPi?: ReadonlyMap<string, string>): string {\n\tconst normalized = name.toLowerCase();\n\treturn (\n\t\tSDK_TO_PI_TOOL_NAME[normalized] ??\n\t\tcustomToolNameToPi?.get(name) ??\n\t\tcustomToolNameToPi?.get(normalized) ??\n\t\t(normalized.startsWith(CUSTOM_TOOLS_MCP_PREFIX) ? name.slice(CUSTOM_TOOLS_MCP_PREFIX.length) : name)\n\t);\n}\n\n/** Translate Claude Code's built-in-tool inputs into senpi's active tool schemas. */\nexport function mapToolArgs(\n\ttoolName: string,\n\targs: Readonly<Record<string, unknown>> | undefined,\n): Record<string, unknown> {\n\tconst input = args ?? {};\n\tswitch (toolName.toLowerCase()) {\n\t\tcase \"read\":\n\t\t\treturn { path: input.file_path ?? input.path, offset: input.offset, limit: input.limit };\n\t\tcase \"write\":\n\t\t\treturn { path: input.file_path ?? input.path, content: input.content };\n\t\tcase \"edit\":\n\t\t\treturn {\n\t\t\t\tpath: input.file_path ?? input.path,\n\t\t\t\tedits: Array.isArray(input.edits)\n\t\t\t\t\t? input.edits\n\t\t\t\t\t: [\n\t\t\t\t\t\t\t{\n\t\t\t\t\t\t\t\toldText: input.old_string ?? input.oldText ?? input.old_text,\n\t\t\t\t\t\t\t\tnewText: input.new_string ?? input.newText ?? input.new_text,\n\t\t\t\t\t\t\t},\n\t\t\t\t\t\t],\n\t\t\t};\n\t\tcase \"bash\":\n\t\t\treturn {\n\t\t\t\tcommand: input.command,\n\t\t\t\ttimeout: typeof input.timeout === \"number\" ? input.timeout / 1_000 : input.timeout,\n\t\t\t};\n\t\tcase \"grep\":\n\t\t\treturn {\n\t\t\t\tpattern: input.pattern,\n\t\t\t\tpath: input.path,\n\t\t\t\tglob: input.glob,\n\t\t\t\tignoreCase: input[\"-i\"] ?? input.ignoreCase,\n\t\t\t\tcontext: input.context ?? input[\"-C\"],\n\t\t\t\tlimit: input.head_limit ?? input.limit,\n\t\t\t};\n\t\tcase \"glob\":\n\t\tcase \"find\":\n\t\t\treturn { pattern: input.pattern, path: input.path, limit: input.limit };\n\t\tdefault:\n\t\t\treturn { ...input };\n\t}\n}\n\nexport function resolveSdkTools(context: Context): ResolvedSdkTools {\n\tif (!context.tools) {\n\t\treturn {\n\t\t\tsdkTools: [...BUILTIN_SDK_TOOLS],\n\t\t\tcustomTools: [],\n\t\t\tcustomToolNameToSdk: new Map(),\n\t\t\tcustomToolNameToPi: new Map(),\n\t\t};\n\t}\n\n\tconst sdkTools = new Set<string>();\n\tconst customTools: Tool[] = [];\n\tconst customToolNameToSdk = new Map<string, string>();\n\tconst customToolNameToPi = new Map<string, string>();\n\tfor (const tool of context.tools) {\n\t\tconst normalized = tool.name.toLowerCase();\n\t\tconst sdkName = PI_TO_SDK_TOOL_NAME[normalized];\n\t\tif (sdkName) {\n\t\t\tsdkTools.add(sdkName);\n\t\t\tcontinue;\n\t\t}\n\t\tconst customSdkName = `${CUSTOM_TOOLS_MCP_PREFIX}${tool.name}`;\n\t\tcustomTools.push(tool);\n\t\tcustomToolNameToSdk.set(tool.name, customSdkName);\n\t\tcustomToolNameToSdk.set(normalized, customSdkName);\n\t\tcustomToolNameToPi.set(customSdkName, tool.name);\n\t\tcustomToolNameToPi.set(customSdkName.toLowerCase(), tool.name);\n\t}\n\treturn { sdkTools: [...sdkTools], customTools, customToolNameToSdk, customToolNameToPi };\n}\n\nexport const canUseTool: NonNullable<Options[\"canUseTool\"]> = async () => ({\n\tbehavior: \"deny\",\n\tmessage: TOOL_EXECUTION_DENIED_MESSAGE,\n});\n"]}
@@ -19,5 +19,13 @@ export interface ShouldBypassOptions {
19
19
  export declare function recordSuccess(state: CompactionExtensionState): CompactionExtensionState;
20
20
  export declare function recordFailure(state: CompactionExtensionState, now: number, opts?: RecordFailureOptions): CompactionExtensionState;
21
21
  export declare function isTripped(state: CompactionExtensionState, now: number): boolean;
22
+ /**
23
+ * A tripped breaker halts *automatic* compaction only. An explicit `/compact` is the
24
+ * user's escape hatch - and on an SDK-owned lane it is the documented recovery from a
25
+ * rejected model downswitch - so refusing it during the cooldown would strand the very
26
+ * session the recovery exists for. Manual failures are still recorded (see the
27
+ * `ownsCompaction` failure-accounting site in `index.ts`), so they count toward the
28
+ * trip that protects the automatic routes.
29
+ */
22
30
  export declare function shouldBypass(_state: CompactionExtensionState, opts?: ShouldBypassOptions): boolean;
23
31
  //# sourceMappingURL=circuit-breaker.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"circuit-breaker.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/compaction/circuit-breaker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAE3D,eAAO,MAAM,sBAAsB,IAAI,CAAC;AACxC,eAAO,MAAM,WAAW,QAAS,CAAC;AAElC,MAAM,WAAW,mBAAmB;IACnC,OAAO,EAAE,IAAI,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,gBAAgB,GAAG,WAAW,CAAC;CACvC;AAED,MAAM,WAAW,oBAAoB;IACpC,MAAM,CAAC,EAAE,CAAC,YAAY,EAAE,mBAAmB,KAAK,IAAI,CAAC;IACrD,KAAK,CAAC,EAAE,gBAAgB,CAAC;CACzB;AAED,MAAM,WAAW,mBAAmB;IACnC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,gBAAgB,CAAC;CAC1B;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,wBAAwB,GAAG,wBAAwB,CAEvF;AAED,wBAAgB,aAAa,CAC5B,KAAK,EAAE,wBAAwB,EAC/B,GAAG,EAAE,MAAM,EACX,IAAI,CAAC,EAAE,oBAAoB,GACzB,wBAAwB,CAmB1B;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,wBAAwB,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAE/E;AAED,wBAAgB,YAAY,CAAC,MAAM,EAAE,wBAAwB,EAAE,IAAI,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAIlG"}
1
+ {"version":3,"file":"circuit-breaker.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/compaction/circuit-breaker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,gBAAgB,CAAC;AACvD,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,YAAY,CAAC;AAE3D,eAAO,MAAM,sBAAsB,IAAI,CAAC;AACxC,eAAO,MAAM,WAAW,QAAS,CAAC;AAElC,MAAM,WAAW,mBAAmB;IACnC,OAAO,EAAE,IAAI,CAAC;IACd,YAAY,EAAE,MAAM,CAAC;IACrB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,gBAAgB,GAAG,WAAW,CAAC;CACvC;AAED,MAAM,WAAW,oBAAoB;IACpC,MAAM,CAAC,EAAE,CAAC,YAAY,EAAE,mBAAmB,KAAK,IAAI,CAAC;IACrD,KAAK,CAAC,EAAE,gBAAgB,CAAC;CACzB;AAED,MAAM,WAAW,mBAAmB;IACnC,MAAM,CAAC,EAAE,OAAO,CAAC;IACjB,MAAM,CAAC,EAAE,gBAAgB,CAAC;CAC1B;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,wBAAwB,GAAG,wBAAwB,CAEvF;AAED,wBAAgB,aAAa,CAC5B,KAAK,EAAE,wBAAwB,EAC/B,GAAG,EAAE,MAAM,EACX,IAAI,CAAC,EAAE,oBAAoB,GACzB,wBAAwB,CAmB1B;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,wBAAwB,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO,CAE/E;AAED;;;;;;;GAOG;AACH,wBAAgB,YAAY,CAAC,MAAM,EAAE,wBAAwB,EAAE,IAAI,CAAC,EAAE,mBAAmB,GAAG,OAAO,CAIlG"}
@@ -26,6 +26,14 @@ export function recordFailure(state, now, opts) {
26
26
  export function isTripped(state, now) {
27
27
  return state.trippedAt !== null && now < state.trippedAt + COOLDOWN_MS;
28
28
  }
29
+ /**
30
+ * A tripped breaker halts *automatic* compaction only. An explicit `/compact` is the
31
+ * user's escape hatch - and on an SDK-owned lane it is the documented recovery from a
32
+ * rejected model downswitch - so refusing it during the cooldown would strand the very
33
+ * session the recovery exists for. Manual failures are still recorded (see the
34
+ * `ownsCompaction` failure-accounting site in `index.ts`), so they count toward the
35
+ * trip that protects the automatic routes.
36
+ */
29
37
  export function shouldBypass(_state, opts) {
30
38
  if (opts?.manual === true)
31
39
  return true;
@@ -1 +1 @@
1
- {"version":3,"file":"circuit-breaker.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/compaction/circuit-breaker.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC;AACxC,MAAM,CAAC,MAAM,WAAW,GAAG,MAAM,CAAC;AAmBlC,MAAM,UAAU,aAAa,CAAC,KAA+B;IAC5D,OAAO,EAAE,GAAG,KAAK,EAAE,mBAAmB,EAAE,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;AAC9D,CAAC;AAED,MAAM,UAAU,aAAa,CAC5B,KAA+B,EAC/B,GAAW,EACX,IAA2B;IAE3B,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,IAAI,OAAO,CAAC,SAAS,KAAK,IAAI,IAAI,GAAG,IAAI,OAAO,CAAC,SAAS,GAAG,WAAW,EAAE,CAAC;QAC1E,OAAO,GAAG,EAAE,GAAG,OAAO,EAAE,mBAAmB,EAAE,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;IACnE,CAAC;IACD,MAAM,IAAI,GAA6B;QACtC,GAAG,OAAO;QACV,mBAAmB,EAAE,OAAO,CAAC,mBAAmB,GAAG,CAAC;KACpD,CAAC;IACF,IAAI,IAAI,CAAC,mBAAmB,IAAI,sBAAsB,IAAI,IAAI,CAAC,SAAS,KAAK,IAAI,EAAE,CAAC;QACnF,IAAI,CAAC,SAAS,GAAG,GAAG,CAAC;QACrB,IAAI,EAAE,MAAM,EAAE,CAAC;YACd,OAAO,EAAE,IAAI;YACb,YAAY,EAAE,IAAI,CAAC,mBAAmB;YACtC,SAAS,EAAE,GAAG;YACd,MAAM,EAAE,IAAI,CAAC,KAAK,IAAI,WAAW;SACjC,CAAC,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,CAAC;AACb,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,KAA+B,EAAE,GAAW;IACrE,OAAO,KAAK,CAAC,SAAS,KAAK,IAAI,IAAI,GAAG,GAAG,KAAK,CAAC,SAAS,GAAG,WAAW,CAAC;AACxE,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,MAAgC,EAAE,IAA0B;IACxF,IAAI,IAAI,EAAE,MAAM,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACvC,IAAI,IAAI,EAAE,MAAM,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3C,OAAO,KAAK,CAAC;AACd,CAAC","sourcesContent":["import type { CompactionReason } from \"../../types.ts\";\nimport type { CompactionExtensionState } from \"./state.ts\";\n\nexport const FAILURE_TRIP_THRESHOLD = 3;\nexport const COOLDOWN_MS = 60_000;\n\nexport interface BreakerNotification {\n\ttripped: true;\n\tfailureCount: number;\n\ttrippedAt: number;\n\treason: CompactionReason | \"threshold\";\n}\n\nexport interface RecordFailureOptions {\n\tonTrip?: (notification: BreakerNotification) => void;\n\troute?: CompactionReason;\n}\n\nexport interface ShouldBypassOptions {\n\tmanual?: boolean;\n\treason?: CompactionReason;\n}\n\nexport function recordSuccess(state: CompactionExtensionState): CompactionExtensionState {\n\treturn { ...state, consecutiveFailures: 0, trippedAt: null };\n}\n\nexport function recordFailure(\n\tstate: CompactionExtensionState,\n\tnow: number,\n\topts?: RecordFailureOptions,\n): CompactionExtensionState {\n\tlet working = state;\n\tif (working.trippedAt !== null && now >= working.trippedAt + COOLDOWN_MS) {\n\t\tworking = { ...working, consecutiveFailures: 0, trippedAt: null };\n\t}\n\tconst next: CompactionExtensionState = {\n\t\t...working,\n\t\tconsecutiveFailures: working.consecutiveFailures + 1,\n\t};\n\tif (next.consecutiveFailures >= FAILURE_TRIP_THRESHOLD && next.trippedAt === null) {\n\t\tnext.trippedAt = now;\n\t\topts?.onTrip?.({\n\t\t\ttripped: true,\n\t\t\tfailureCount: next.consecutiveFailures,\n\t\t\ttrippedAt: now,\n\t\t\treason: opts.route ?? \"threshold\",\n\t\t});\n\t}\n\treturn next;\n}\n\nexport function isTripped(state: CompactionExtensionState, now: number): boolean {\n\treturn state.trippedAt !== null && now < state.trippedAt + COOLDOWN_MS;\n}\n\nexport function shouldBypass(_state: CompactionExtensionState, opts?: ShouldBypassOptions): boolean {\n\tif (opts?.manual === true) return true;\n\tif (opts?.reason === \"manual\") return true;\n\treturn false;\n}\n"]}
1
+ {"version":3,"file":"circuit-breaker.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/compaction/circuit-breaker.ts"],"names":[],"mappings":"AAGA,MAAM,CAAC,MAAM,sBAAsB,GAAG,CAAC,CAAC;AACxC,MAAM,CAAC,MAAM,WAAW,GAAG,MAAM,CAAC;AAmBlC,MAAM,UAAU,aAAa,CAAC,KAA+B;IAC5D,OAAO,EAAE,GAAG,KAAK,EAAE,mBAAmB,EAAE,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;AAC9D,CAAC;AAED,MAAM,UAAU,aAAa,CAC5B,KAA+B,EAC/B,GAAW,EACX,IAA2B;IAE3B,IAAI,OAAO,GAAG,KAAK,CAAC;IACpB,IAAI,OAAO,CAAC,SAAS,KAAK,IAAI,IAAI,GAAG,IAAI,OAAO,CAAC,SAAS,GAAG,WAAW,EAAE,CAAC;QAC1E,OAAO,GAAG,EAAE,GAAG,OAAO,EAAE,mBAAmB,EAAE,CAAC,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;IACnE,CAAC;IACD,MAAM,IAAI,GAA6B;QACtC,GAAG,OAAO;QACV,mBAAmB,EAAE,OAAO,CAAC,mBAAmB,GAAG,CAAC;KACpD,CAAC;IACF,IAAI,IAAI,CAAC,mBAAmB,IAAI,sBAAsB,IAAI,IAAI,CAAC,SAAS,KAAK,IAAI,EAAE,CAAC;QACnF,IAAI,CAAC,SAAS,GAAG,GAAG,CAAC;QACrB,IAAI,EAAE,MAAM,EAAE,CAAC;YACd,OAAO,EAAE,IAAI;YACb,YAAY,EAAE,IAAI,CAAC,mBAAmB;YACtC,SAAS,EAAE,GAAG;YACd,MAAM,EAAE,IAAI,CAAC,KAAK,IAAI,WAAW;SACjC,CAAC,CAAC;IACJ,CAAC;IACD,OAAO,IAAI,CAAC;AACb,CAAC;AAED,MAAM,UAAU,SAAS,CAAC,KAA+B,EAAE,GAAW;IACrE,OAAO,KAAK,CAAC,SAAS,KAAK,IAAI,IAAI,GAAG,GAAG,KAAK,CAAC,SAAS,GAAG,WAAW,CAAC;AACxE,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,YAAY,CAAC,MAAgC,EAAE,IAA0B;IACxF,IAAI,IAAI,EAAE,MAAM,KAAK,IAAI;QAAE,OAAO,IAAI,CAAC;IACvC,IAAI,IAAI,EAAE,MAAM,KAAK,QAAQ;QAAE,OAAO,IAAI,CAAC;IAC3C,OAAO,KAAK,CAAC;AACd,CAAC","sourcesContent":["import type { CompactionReason } from \"../../types.ts\";\nimport type { CompactionExtensionState } from \"./state.ts\";\n\nexport const FAILURE_TRIP_THRESHOLD = 3;\nexport const COOLDOWN_MS = 60_000;\n\nexport interface BreakerNotification {\n\ttripped: true;\n\tfailureCount: number;\n\ttrippedAt: number;\n\treason: CompactionReason | \"threshold\";\n}\n\nexport interface RecordFailureOptions {\n\tonTrip?: (notification: BreakerNotification) => void;\n\troute?: CompactionReason;\n}\n\nexport interface ShouldBypassOptions {\n\tmanual?: boolean;\n\treason?: CompactionReason;\n}\n\nexport function recordSuccess(state: CompactionExtensionState): CompactionExtensionState {\n\treturn { ...state, consecutiveFailures: 0, trippedAt: null };\n}\n\nexport function recordFailure(\n\tstate: CompactionExtensionState,\n\tnow: number,\n\topts?: RecordFailureOptions,\n): CompactionExtensionState {\n\tlet working = state;\n\tif (working.trippedAt !== null && now >= working.trippedAt + COOLDOWN_MS) {\n\t\tworking = { ...working, consecutiveFailures: 0, trippedAt: null };\n\t}\n\tconst next: CompactionExtensionState = {\n\t\t...working,\n\t\tconsecutiveFailures: working.consecutiveFailures + 1,\n\t};\n\tif (next.consecutiveFailures >= FAILURE_TRIP_THRESHOLD && next.trippedAt === null) {\n\t\tnext.trippedAt = now;\n\t\topts?.onTrip?.({\n\t\t\ttripped: true,\n\t\t\tfailureCount: next.consecutiveFailures,\n\t\t\ttrippedAt: now,\n\t\t\treason: opts.route ?? \"threshold\",\n\t\t});\n\t}\n\treturn next;\n}\n\nexport function isTripped(state: CompactionExtensionState, now: number): boolean {\n\treturn state.trippedAt !== null && now < state.trippedAt + COOLDOWN_MS;\n}\n\n/**\n * A tripped breaker halts *automatic* compaction only. An explicit `/compact` is the\n * user's escape hatch - and on an SDK-owned lane it is the documented recovery from a\n * rejected model downswitch - so refusing it during the cooldown would strand the very\n * session the recovery exists for. Manual failures are still recorded (see the\n * `ownsCompaction` failure-accounting site in `index.ts`), so they count toward the\n * trip that protects the automatic routes.\n */\nexport function shouldBypass(_state: CompactionExtensionState, opts?: ShouldBypassOptions): boolean {\n\tif (opts?.manual === true) return true;\n\tif (opts?.reason === \"manual\") return true;\n\treturn false;\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"deterministic-fallback.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/compaction/deterministic-fallback.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,qBAAqB,EAAE,KAAK,gBAAgB,EAAkB,MAAM,8BAA8B,CAAC;AAGjH,OAAO,EAIN,KAAK,YAAY,EACjB,MAAM,6BAA6B,CAAC;AAOrC,MAAM,MAAM,iCAAiC,GAC1C,uBAAuB,GACvB,2BAA2B,GAC3B,kCAAkC,GAClC,6BAA6B,CAAC;AAEjC,UAAU,gBAAgB;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,UAAU,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,MAAM,oCAAoC,GAC7C,8BAA8B,GAC9B,yBAAyB,GACzB,gCAAgC,GAChC,uBAAuB,GACvB,+BAA+B,CAAC;AAEnC,UAAU,4BAA4B;IACrC,MAAM,EAAE,4CAA4C,CAAC;IACrD,MAAM,EAAE,8BAA8B,CAAC;IACvC,WAAW,EAAE,iCAAiC,CAAC;IAC/C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,UAAU,GAAG,kBAAkB,GAAG,uBAAuB,CAAC;CAC3E;AAED,MAAM,WAAW,+BAA+B;IAC/C,eAAe,CAAC,EAAE,oCAAoC,CAAC;IACvD,mBAAmB,CAAC,EAAE,KAAK,CAAC;QAC3B,gBAAgB,EAAE,MAAM,CAAC;QACzB,eAAe,EAAE,oCAAoC,CAAC;KACtD,CAAC,CAAC;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,OAAO,CAAC;CACzB;AA8CD,wBAAgB,yCAAyC,CACxD,KAAK,EAAE,OAAO,GACZ,iCAAiC,GAAG,SAAS,CAc/C;AAED,wBAAgB,gCAAgC,CAC/C,WAAW,EAAE,qBAAqB,EAClC,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,iCAAiC,EAC9C,QAAQ,EAAE,gBAAgB,EAC1B,aAAa,GAAE,YAAY,EAAO,EAClC,WAAW,CAAC,EAAE,+BAA+B,GAC3C,gBAAgB,CAAC,4BAA4B,CAAC,GAAG,SAAS,CA+M5D"}
1
+ {"version":3,"file":"deterministic-fallback.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/compaction/deterministic-fallback.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,qBAAqB,EAAE,KAAK,gBAAgB,EAAkB,MAAM,8BAA8B,CAAC;AAGjH,OAAO,EAIN,KAAK,YAAY,EACjB,MAAM,6BAA6B,CAAC;AAQrC,MAAM,MAAM,iCAAiC,GAC1C,uBAAuB,GACvB,2BAA2B,GAC3B,kCAAkC,GAClC,6BAA6B,CAAC;AAEjC,UAAU,gBAAgB;IACzB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,UAAU,CAAC,EAAE,OAAO,CAAC;CACrB;AAED,MAAM,MAAM,oCAAoC,GAC7C,8BAA8B,GAC9B,yBAAyB,GACzB,gCAAgC,GAChC,uBAAuB,GACvB,+BAA+B,CAAC;AAEnC,UAAU,4BAA4B;IACrC,MAAM,EAAE,4CAA4C,CAAC;IACrD,MAAM,EAAE,8BAA8B,CAAC;IACvC,WAAW,EAAE,iCAAiC,CAAC;IAC/C,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,cAAc,CAAC,EAAE,UAAU,GAAG,kBAAkB,GAAG,uBAAuB,CAAC;CAC3E;AAED,MAAM,WAAW,+BAA+B;IAC/C,eAAe,CAAC,EAAE,oCAAoC,CAAC;IACvD,mBAAmB,CAAC,EAAE,KAAK,CAAC;QAC3B,gBAAgB,EAAE,MAAM,CAAC;QACzB,eAAe,EAAE,oCAAoC,CAAC;KACtD,CAAC,CAAC;IACH,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,cAAc,CAAC,EAAE,OAAO,CAAC;CACzB;AA8CD,wBAAgB,yCAAyC,CACxD,KAAK,EAAE,OAAO,GACZ,iCAAiC,GAAG,SAAS,CAc/C;AAED,wBAAgB,gCAAgC,CAC/C,WAAW,EAAE,qBAAqB,EAClC,aAAa,EAAE,MAAM,EACrB,WAAW,EAAE,iCAAiC,EAC9C,QAAQ,EAAE,gBAAgB,EAC1B,aAAa,GAAE,YAAY,EAAO,EAClC,WAAW,CAAC,EAAE,+BAA+B,GAC3C,gBAAgB,CAAC,4BAA4B,CAAC,GAAG,SAAS,CAqO5D"}
@@ -2,6 +2,7 @@ import { estimateTokens } from "../../../compaction/index.js";
2
2
  import { StreamDurationBudgetError, StreamIdleTimeoutError } from "../../../compaction/stream-watchdog.js";
3
3
  import { filterContextExcludedMessages } from "../../../messages.js";
4
4
  import { buildSessionContext, getSessionContextEntryId, } from "../../../session-manager.js";
5
+ import { markFailedTurnFragments } from "./fallback-failed-turn-normalization.js";
5
6
  import { SummarizationOverflowExhaustedError } from "./overflow-retry.js";
6
7
  import { resolveEffectiveReserveTokens } from "./policy.js";
7
8
  import { hasUnsafeRetainedContent } from "./retained-message-safety.js";
@@ -127,6 +128,12 @@ export function createRequiredCompactionFallback(preparation, contextWindow, fai
127
128
  diagnostics.rejectionReason = "context-reconstruction-failed";
128
129
  return undefined;
129
130
  }
131
+ // Evaluate the candidate the provider would actually receive: `convertToLlm`
132
+ // drops failed/aborted assistant turns (and their orphaned tool results) before
133
+ // every request, so their dangling toolCall blocks are neither incomplete calls
134
+ // nor retained tokens here. Raw session history stays untouched - this mask is
135
+ // local to the fallback projection (code-yeongyu/oh-my-openagent#7921).
136
+ const droppedByFailedTurnNormalization = markFailedTurnFragments(projectedMessages);
130
137
  const messageIndexesByEntryId = new Map();
131
138
  for (const [index, message] of projectedMessages.entries()) {
132
139
  const entryId = getSessionContextEntryId(message);
@@ -141,15 +148,31 @@ export function createRequiredCompactionFallback(preparation, contextWindow, fai
141
148
  for (let index = projectedMessages.length - 1; index >= 0; index--) {
142
149
  const message = projectedMessages[index];
143
150
  tokenSuffix[index] = tokenSuffix[index + 1];
151
+ if (droppedByFailedTurnNormalization[index]) {
152
+ unsafeSuffix[index] = unsafeSuffix[index + 1];
153
+ continue;
154
+ }
144
155
  let messageUnsafe = false;
145
156
  let serialized;
146
157
  try {
147
158
  messageUnsafe = hasUnsafeRetainedContent([message]);
148
159
  serialized = isSafeBoundedValue(message) ? JSON.stringify(message) : undefined;
160
+ let imagePayloadBytes = 0;
161
+ let imageTokens = 0;
162
+ if (!messageUnsafe && serialized !== undefined && message.role === "toolResult") {
163
+ for (const block of message.content) {
164
+ if (block.type !== "image")
165
+ continue;
166
+ // Keep the JSON delimiters and metadata charged, but replace only
167
+ // this validated image's escaped payload bytes with its token cost.
168
+ imagePayloadBytes += Buffer.byteLength(JSON.stringify(block.data)) - 2;
169
+ imageTokens += estimateTokens({ ...message, content: [block] });
170
+ }
171
+ }
149
172
  tokenSuffix[index] +=
150
173
  serialized === undefined
151
174
  ? Number.POSITIVE_INFINITY
152
- : Math.max(estimateTokens(message), Buffer.byteLength(serialized));
175
+ : Math.max(estimateTokens(message), Buffer.byteLength(serialized) - imagePayloadBytes + imageTokens);
153
176
  }
154
177
  catch {
155
178
  messageUnsafe = true;
@@ -1 +1 @@
1
- {"version":3,"file":"deterministic-fallback.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/compaction/deterministic-fallback.ts"],"names":[],"mappings":"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","sourcesContent":["import { type CompactionPreparation, type CompactionResult, estimateTokens } from \"../../../compaction/index.ts\";\nimport { StreamDurationBudgetError, StreamIdleTimeoutError } from \"../../../compaction/stream-watchdog.ts\";\nimport { filterContextExcludedMessages } from \"../../../messages.ts\";\nimport {\n\tbuildSessionContext,\n\ttype CompactionEntry,\n\tgetSessionContextEntryId,\n\ttype SessionEntry,\n} from \"../../../session-manager.ts\";\nimport { SummarizationOverflowExhaustedError } from \"./overflow-retry.ts\";\nimport { resolveEffectiveReserveTokens } from \"./policy.ts\";\nimport { hasUnsafeRetainedContent } from \"./retained-message-safety.ts\";\nimport { SummaryGenerationError, SummaryRequestError } from \"./speculative.ts\";\nimport { capUtf8Bytes } from \"./task-intent.ts\";\n\nexport type RequiredCompactionFallbackFailure =\n\t| \"summarization-timeout\"\n\t| \"upstream-stream-truncated\"\n\t| \"summarization-overflow-exhausted\"\n\t| \"summarization-empty-summary\";\n\ninterface RecoveryMetadata {\n\ttaskIntent?: string;\n\ttodoSnapshot?: unknown;\n\tcheckpoint?: unknown;\n}\n\nexport type DeterministicFallbackRejectionReason =\n\t| \"missing-preparation-boundary\"\n\t| \"unsafe-retained-content\"\n\t| \"retained-token-budget-exceeded\"\n\t| \"atomic-tool-chain-cut\"\n\t| \"context-reconstruction-failed\";\n\ninterface DeterministicFallbackDetails {\n\tschema: \"senpi.compaction.deterministic-fallback.v1\";\n\torigin: \"required-compaction-recovery\";\n\tfailureKind: RequiredCompactionFallbackFailure;\n\ttaskIntent?: string;\n\tretainedSuffix?: \"prepared\" | \"latest-user-turn\" | \"earlier-safe-boundary\";\n}\n\nexport interface DeterministicFallbackDiagnostic {\n\trejectionReason?: DeterministicFallbackRejectionReason;\n\tcandidateRejections?: Array<{\n\t\tfirstKeptEntryId: string;\n\t\trejectionReason: DeterministicFallbackRejectionReason;\n\t}>;\n\tcandidatesChecked?: number;\n\tbudgetExceeded?: boolean;\n}\n\nconst NON_VISIBLE_USER_TEXT = /[\\p{White_Space}\\p{Default_Ignorable_Code_Point}]/gu;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null;\n}\n\nfunction hasMeaningfulUserText(entry: SessionEntry): boolean {\n\tif (entry.type !== \"message\" || !isRecord(entry.message) || entry.message.role !== \"user\") return false;\n\tconst content = entry.message.content;\n\tconst hasVisibleText = (text: unknown): boolean =>\n\t\ttypeof text === \"string\" && text.normalize(\"NFKC\").replace(NON_VISIBLE_USER_TEXT, \"\").length > 0;\n\tif (typeof content === \"string\") return hasVisibleText(content);\n\tif (!Array.isArray(content)) return false;\n\treturn content.some((block) => isRecord(block) && block.type === \"text\" && hasVisibleText(block.text));\n}\n\n/**\n * Bound a value graph that came from persisted, potentially hostile session data.\n * Returns false when the graph contains anything we must not read (accessors,\n * non-plain prototypes, cycles, functions, symbols) so callers fail closed\n * instead of executing persisted code or walking unbounded structures.\n */\nfunction isSafeBoundedValue(value: unknown, seen = new Set<object>(), depth = 0): boolean {\n\tif (depth > 32) return false;\n\tif (value === null || value === undefined) return true;\n\tconst kind = typeof value;\n\tif (kind === \"string\" || kind === \"number\" || kind === \"boolean\") return true;\n\tif (kind !== \"object\") return false;\n\tif (seen.has(value as object)) return false;\n\tseen.add(value as object);\n\tif (Array.isArray(value)) {\n\t\tfor (const item of value) if (!isSafeBoundedValue(item, seen, depth + 1)) return false;\n\t\treturn true;\n\t}\n\tconst proto = Object.getPrototypeOf(value);\n\tif (proto !== Object.prototype && proto !== null) return false;\n\tfor (const descriptor of Object.values(Object.getOwnPropertyDescriptors(value))) {\n\t\tif (descriptor.get !== undefined || descriptor.set !== undefined) return false;\n\t\tif (typeof descriptor.value === \"function\" || typeof descriptor.value === \"symbol\") return false;\n\t\tif (!isSafeBoundedValue(descriptor.value, seen, depth + 1)) return false;\n\t}\n\treturn true;\n}\n\nexport function classifyRequiredCompactionFallbackFailure(\n\terror: unknown,\n): RequiredCompactionFallbackFailure | undefined {\n\tif (error instanceof StreamDurationBudgetError || error instanceof StreamIdleTimeoutError) {\n\t\treturn \"summarization-timeout\";\n\t}\n\tif (error instanceof SummaryRequestError && error.transient && error.failureKind === \"upstream-stream-truncated\") {\n\t\treturn \"upstream-stream-truncated\";\n\t}\n\tif (error instanceof SummarizationOverflowExhaustedError) {\n\t\treturn \"summarization-overflow-exhausted\";\n\t}\n\tif (error instanceof SummaryGenerationError && error.kind === \"empty-summary\") {\n\t\treturn \"summarization-empty-summary\";\n\t}\n\treturn undefined;\n}\n\nexport function createRequiredCompactionFallback(\n\tpreparation: CompactionPreparation,\n\tcontextWindow: number,\n\tfailureKind: RequiredCompactionFallbackFailure,\n\tmetadata: RecoveryMetadata,\n\tbranchEntries: SessionEntry[] = [],\n\tdiagnostics?: DeterministicFallbackDiagnostic,\n): CompactionResult<DeterministicFallbackDetails> | undefined {\n\tconst preparedBoundaryIndex = branchEntries.findIndex((entry) => entry.id === preparation.firstKeptEntryId);\n\tif (!preparation.firstKeptEntryId || preparedBoundaryIndex === -1) {\n\t\tif (diagnostics) diagnostics.rejectionReason = \"missing-preparation-boundary\";\n\t\treturn undefined;\n\t}\n\n\tconst marker = [\n\t\t\"[Deterministic compaction recovery checkpoint]\",\n\t\t\"Generated summarization did not complete, so older context was reduced without another provider request.\",\n\t\t\"Continue from the retained messages after this checkpoint. Treat omitted transcript details as unknown.\",\n\t].join(\"\\n\");\n\tconst taskIntent = metadata.taskIntent?.trim();\n\tconst fixedText = taskIntent ? `${marker}\\n\\nTask intent:\\n${taskIntent}` : marker;\n\tconst maxSummaryBytes = Math.max(1_024, Math.floor(contextWindow * 0.4));\n\tconst previousSummary = preparation.previousSummary?.trim();\n\tlet summary = fixedText;\n\tif (previousSummary) {\n\t\tconst availableBytes = Math.max(0, maxSummaryBytes - Buffer.byteLength(`${fixedText}\\n\\nPrevious checkpoint:\\n`));\n\t\tconst truncationMarker = \"\\n[Older checkpoint truncated]\";\n\t\tconst boundedPrevious =\n\t\t\tBuffer.byteLength(previousSummary) <= availableBytes\n\t\t\t\t? previousSummary\n\t\t\t\t: `${capUtf8Bytes(previousSummary, Math.max(0, availableBytes - Buffer.byteLength(truncationMarker)))}${truncationMarker}`;\n\t\tsummary = `${fixedText}\\n\\nPrevious checkpoint:\\n${boundedPrevious}`;\n\t}\n\n\tconst baseDetails: DeterministicFallbackDetails = {\n\t\tschema: \"senpi.compaction.deterministic-fallback.v1\",\n\t\torigin: \"required-compaction-recovery\",\n\t\tfailureKind,\n\t\t...(taskIntent ? { taskIntent } : {}),\n\t};\n\tlet candidateCount = 0;\n\n\tconst syntheticCompaction: CompactionEntry = {\n\t\ttype: \"compaction\",\n\t\tid: \"__senpi_deterministic_fallback_preview__\",\n\t\tparentId: branchEntries.at(-1)?.id ?? null,\n\t\ttimestamp: new Date(0).toISOString(),\n\t\tsummary,\n\t\tfirstKeptEntryId: branchEntries[0]?.id ?? preparation.firstKeptEntryId,\n\t\ttokensBefore: preparation.tokensBefore,\n\t\tdetails: { ...baseDetails, retainedSuffix: \"prepared\" },\n\t\tfromHook: true,\n\t};\n\n\tlet projectedMessages: ReturnType<typeof filterContextExcludedMessages>;\n\ttry {\n\t\tprojectedMessages = filterContextExcludedMessages(\n\t\t\tbuildSessionContext([...branchEntries, syntheticCompaction]).messages,\n\t\t);\n\t} catch {\n\t\tif (diagnostics) diagnostics.rejectionReason = \"context-reconstruction-failed\";\n\t\treturn undefined;\n\t}\n\n\tconst messageIndexesByEntryId = new Map<string, number>();\n\tfor (const [index, message] of projectedMessages.entries()) {\n\t\tconst entryId = getSessionContextEntryId(message);\n\t\tif (entryId !== undefined) messageIndexesByEntryId.set(entryId, index);\n\t}\n\tconst summaryIndex = messageIndexesByEntryId.get(syntheticCompaction.id);\n\tconst unsafeSuffix = new Array(projectedMessages.length + 1).fill(false);\n\tconst tokenSuffix = new Array(projectedMessages.length + 1).fill(0);\n\tconst toolCalls = new Map<string, { indexes: number[]; incomplete: boolean }>();\n\tconst toolResults = new Map<string, number[]>();\n\tfor (let index = projectedMessages.length - 1; index >= 0; index--) {\n\t\tconst message = projectedMessages[index];\n\t\ttokenSuffix[index] = tokenSuffix[index + 1];\n\t\tlet messageUnsafe = false;\n\t\tlet serialized: string | undefined;\n\t\ttry {\n\t\t\tmessageUnsafe = hasUnsafeRetainedContent([message]);\n\t\t\tserialized = isSafeBoundedValue(message) ? JSON.stringify(message) : undefined;\n\t\t\ttokenSuffix[index] +=\n\t\t\t\tserialized === undefined\n\t\t\t\t\t? Number.POSITIVE_INFINITY\n\t\t\t\t\t: Math.max(estimateTokens(message), Buffer.byteLength(serialized));\n\t\t} catch {\n\t\t\tmessageUnsafe = true;\n\t\t\ttokenSuffix[index] = Number.POSITIVE_INFINITY;\n\t\t}\n\t\tunsafeSuffix[index] = unsafeSuffix[index + 1] || messageUnsafe;\n\t\tif (!isRecord(message)) continue;\n\t\tif (message.role === \"toolResult\" && typeof message.toolCallId === \"string\") {\n\t\t\tconst indexes = toolResults.get(message.toolCallId) ?? [];\n\t\t\tindexes.push(index);\n\t\t\ttoolResults.set(message.toolCallId, indexes);\n\t\t} else if (message.role === \"assistant\" && Array.isArray(message.content)) {\n\t\t\tfor (const block of message.content) {\n\t\t\t\tif (!isRecord(block) || block.type !== \"toolCall\" || typeof block.id !== \"string\") continue;\n\t\t\t\tconst call = toolCalls.get(block.id) ?? { indexes: [], incomplete: false };\n\t\t\t\tcall.indexes.push(index);\n\t\t\t\tcall.incomplete ||= block.incomplete === true;\n\t\t\t\ttoolCalls.set(block.id, call);\n\t\t\t}\n\t\t}\n\t}\n\tconst invalidToolChainDiff = new Int32Array(projectedMessages.length + 2);\n\tconst markInvalidRange = (start: number, endExclusive: number) => {\n\t\tconst boundedStart = Math.max(0, start);\n\t\tconst boundedEnd = Math.min(projectedMessages.length + 1, endExclusive);\n\t\tif (boundedStart >= boundedEnd) return;\n\t\tinvalidToolChainDiff[boundedStart]++;\n\t\tinvalidToolChainDiff[boundedEnd]--;\n\t};\n\tfor (const [id, call] of toolCalls) {\n\t\tconst results = toolResults.get(id) ?? [];\n\t\tif (call.indexes.length !== 1 || results.length !== 1) {\n\t\t\tmarkInvalidRange(0, Math.max(call.indexes[0] ?? -1, results[0] ?? -1) + 1);\n\t\t\tcontinue;\n\t\t}\n\t\tconst callIndex = call.indexes[0];\n\t\tconst resultIndex = results[0];\n\t\tif (call.incomplete) markInvalidRange(0, Math.max(callIndex, resultIndex) + 1);\n\t\telse if (callIndex < resultIndex) markInvalidRange(callIndex + 1, resultIndex + 1);\n\t\telse markInvalidRange(0, Math.max(callIndex, resultIndex) + 1);\n\t}\n\tfor (const [id, results] of toolResults) {\n\t\tif (toolCalls.has(id)) continue;\n\t\tmarkInvalidRange(0, results[0] + 1);\n\t}\n\tconst toolChainValidAt = new Array(projectedMessages.length + 1).fill(true);\n\tlet invalidToolChainCount = 0;\n\tfor (let index = 0; index < toolChainValidAt.length; index++) {\n\t\tinvalidToolChainCount += invalidToolChainDiff[index];\n\t\ttoolChainValidAt[index] = invalidToolChainCount === 0;\n\t}\n\n\tconst projectCandidate = (\n\t\tfirstKeptEntryId: string,\n\t\tretainedSuffix: NonNullable<DeterministicFallbackDetails[\"retainedSuffix\"]>,\n\t): CompactionResult<DeterministicFallbackDetails> | undefined => {\n\t\tcandidateCount++;\n\t\tconst details = { ...baseDetails, retainedSuffix };\n\t\tconst reject = (rejectionReason: DeterministicFallbackRejectionReason): undefined => {\n\t\t\tif (diagnostics) {\n\t\t\t\tdiagnostics.rejectionReason = rejectionReason;\n\t\t\t\tdiagnostics.candidateRejections ??= [];\n\t\t\t\tdiagnostics.candidateRejections.push({ firstKeptEntryId, rejectionReason });\n\t\t\t}\n\t\t\treturn undefined;\n\t\t};\n\t\tconst result: CompactionResult<DeterministicFallbackDetails> = {\n\t\t\tsummary,\n\t\t\tfirstKeptEntryId,\n\t\t\ttokensBefore: preparation.tokensBefore,\n\t\t\tdetails,\n\t\t};\n\t\tconst startIndex = messageIndexesByEntryId.get(firstKeptEntryId);\n\t\tif (startIndex === undefined) return reject(\"context-reconstruction-failed\");\n\t\tconst retainedStart = Math.min(startIndex, projectedMessages.length);\n\t\tif (unsafeSuffix[retainedStart]) return reject(\"unsafe-retained-content\");\n\t\tif (!toolChainValidAt[retainedStart]) return reject(\"atomic-tool-chain-cut\");\n\t\t// The hard-limit valve reserves the scaled budget, so accepting against the raw\n\t\t// configured reserve would admit a context that valve immediately compacts again.\n\t\tconst budget = contextWindow - resolveEffectiveReserveTokens(contextWindow, preparation.settings);\n\t\tconst summaryTokens =\n\t\t\tsummaryIndex === undefined\n\t\t\t\t? Number.POSITIVE_INFINITY\n\t\t\t\t: Math.max(\n\t\t\t\t\t\testimateTokens(projectedMessages[summaryIndex]),\n\t\t\t\t\t\tBuffer.byteLength(JSON.stringify(projectedMessages[summaryIndex])),\n\t\t\t\t\t);\n\t\tconst retainedTokens = (tokenSuffix[retainedStart] ?? Number.POSITIVE_INFINITY) + summaryTokens;\n\t\tif (retainedTokens > budget) {\n\t\t\tif (diagnostics) diagnostics.budgetExceeded = true;\n\t\t\treturn reject(\"retained-token-budget-exceeded\");\n\t\t}\n\t\treturn { ...result, estimatedTokensAfter: retainedTokens };\n\t};\n\n\t// 1. Try prepared boundary\n\tconst prepared = projectCandidate(preparation.firstKeptEntryId, \"prepared\");\n\tif (prepared) {\n\t\tif (diagnostics) diagnostics.candidatesChecked = candidateCount;\n\t\treturn prepared;\n\t}\n\n\t// 2. If prepared boundary cut an atomic signed chain or was rejected, walk backward\n\t// to the actual chain boundary. The chain length is determined by the session.\n\tfor (let index = preparedBoundaryIndex - 1; index >= 0; index--) {\n\t\tconst entry = branchEntries[index];\n\t\tif (entry.type === \"compaction\") break;\n\t\t// Only consider message turn starts or assistant call starts\n\t\tconst earlier = projectCandidate(entry.id, \"earlier-safe-boundary\");\n\t\tif (earlier) {\n\t\t\tif (diagnostics) diagnostics.candidatesChecked = candidateCount;\n\t\t\treturn earlier;\n\t\t}\n\t}\n\n\t// 3. Try latest meaningful user turn\n\tfor (let index = branchEntries.length - 1; index > preparedBoundaryIndex; index--) {\n\t\tconst entry = branchEntries[index];\n\t\tif (!hasMeaningfulUserText(entry)) continue;\n\t\tconst latestUser = projectCandidate(entry.id, \"latest-user-turn\");\n\t\tif (latestUser) {\n\t\t\tif (diagnostics) diagnostics.candidatesChecked = candidateCount;\n\t\t\treturn latestUser;\n\t\t}\n\t\tbreak;\n\t}\n\n\tif (diagnostics) diagnostics.candidatesChecked = candidateCount;\n\treturn undefined;\n}\n"]}
1
+ {"version":3,"file":"deterministic-fallback.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/compaction/deterministic-fallback.ts"],"names":[],"mappings":"AAAA,OAAO,EAAqD,cAAc,EAAE,MAAM,8BAA8B,CAAC;AACjH,OAAO,EAAE,yBAAyB,EAAE,sBAAsB,EAAE,MAAM,wCAAwC,CAAC;AAC3G,OAAO,EAAE,6BAA6B,EAAE,MAAM,sBAAsB,CAAC;AACrE,OAAO,EACN,mBAAmB,EAEnB,wBAAwB,GAExB,MAAM,6BAA6B,CAAC;AACrC,OAAO,EAAE,uBAAuB,EAAE,MAAM,yCAAyC,CAAC;AAClF,OAAO,EAAE,mCAAmC,EAAE,MAAM,qBAAqB,CAAC;AAC1E,OAAO,EAAE,6BAA6B,EAAE,MAAM,aAAa,CAAC;AAC5D,OAAO,EAAE,wBAAwB,EAAE,MAAM,8BAA8B,CAAC;AACxE,OAAO,EAAE,sBAAsB,EAAE,mBAAmB,EAAE,MAAM,kBAAkB,CAAC;AAC/E,OAAO,EAAE,YAAY,EAAE,MAAM,kBAAkB,CAAC;AAuChD,MAAM,qBAAqB,GAAG,qDAAqD,CAAC;AAEpF,SAAS,QAAQ,CAAC,KAAc;IAC/B,OAAO,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI,CAAC;AACpD,CAAC;AAED,SAAS,qBAAqB,CAAC,KAAmB;IACjD,IAAI,KAAK,CAAC,IAAI,KAAK,SAAS,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,KAAK,CAAC,OAAO,CAAC,IAAI,KAAK,MAAM;QAAE,OAAO,KAAK,CAAC;IACxG,MAAM,OAAO,GAAG,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;IACtC,MAAM,cAAc,GAAG,CAAC,IAAa,EAAW,EAAE,CACjD,OAAO,IAAI,KAAK,QAAQ,IAAI,IAAI,CAAC,SAAS,CAAC,MAAM,CAAC,CAAC,OAAO,CAAC,qBAAqB,EAAE,EAAE,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC;IAClG,IAAI,OAAO,OAAO,KAAK,QAAQ;QAAE,OAAO,cAAc,CAAC,OAAO,CAAC,CAAC;IAChE,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC;QAAE,OAAO,KAAK,CAAC;IAC1C,OAAO,OAAO,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,IAAI,cAAc,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,CAAC;AACxG,CAAC;AAED;;;;;GAKG;AACH,SAAS,kBAAkB,CAAC,KAAc,EAAE,OAAO,IAAI,GAAG,EAAU,EAAE,KAAK,GAAG,CAAC;IAC9E,IAAI,KAAK,GAAG,EAAE;QAAE,OAAO,KAAK,CAAC;IAC7B,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IACvD,MAAM,IAAI,GAAG,OAAO,KAAK,CAAC;IAC1B,IAAI,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,QAAQ,IAAI,IAAI,KAAK,SAAS;QAAE,OAAO,IAAI,CAAC;IAC9E,IAAI,IAAI,KAAK,QAAQ;QAAE,OAAO,KAAK,CAAC;IACpC,IAAI,IAAI,CAAC,GAAG,CAAC,KAAe,CAAC;QAAE,OAAO,KAAK,CAAC;IAC5C,IAAI,CAAC,GAAG,CAAC,KAAe,CAAC,CAAC;IAC1B,IAAI,KAAK,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAC;QAC1B,KAAK,MAAM,IAAI,IAAI,KAAK;YAAE,IAAI,CAAC,kBAAkB,CAAC,IAAI,EAAE,IAAI,EAAE,KAAK,GAAG,CAAC,CAAC;gBAAE,OAAO,KAAK,CAAC;QACvF,OAAO,IAAI,CAAC;IACb,CAAC;IACD,MAAM,KAAK,GAAG,MAAM,CAAC,cAAc,CAAC,KAAK,CAAC,CAAC;IAC3C,IAAI,KAAK,KAAK,MAAM,CAAC,SAAS,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAC;IAC/D,KAAK,MAAM,UAAU,IAAI,MAAM,CAAC,MAAM,CAAC,MAAM,CAAC,yBAAyB,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC;QACjF,IAAI,UAAU,CAAC,GAAG,KAAK,SAAS,IAAI,UAAU,CAAC,GAAG,KAAK,SAAS;YAAE,OAAO,KAAK,CAAC;QAC/E,IAAI,OAAO,UAAU,CAAC,KAAK,KAAK,UAAU,IAAI,OAAO,UAAU,CAAC,KAAK,KAAK,QAAQ;YAAE,OAAO,KAAK,CAAC;QACjG,IAAI,CAAC,kBAAkB,CAAC,UAAU,CAAC,KAAK,EAAE,IAAI,EAAE,KAAK,GAAG,CAAC,CAAC;YAAE,OAAO,KAAK,CAAC;IAC1E,CAAC;IACD,OAAO,IAAI,CAAC;AACb,CAAC;AAED,MAAM,UAAU,yCAAyC,CACxD,KAAc;IAEd,IAAI,KAAK,YAAY,yBAAyB,IAAI,KAAK,YAAY,sBAAsB,EAAE,CAAC;QAC3F,OAAO,uBAAuB,CAAC;IAChC,CAAC;IACD,IAAI,KAAK,YAAY,mBAAmB,IAAI,KAAK,CAAC,SAAS,IAAI,KAAK,CAAC,WAAW,KAAK,2BAA2B,EAAE,CAAC;QAClH,OAAO,2BAA2B,CAAC;IACpC,CAAC;IACD,IAAI,KAAK,YAAY,mCAAmC,EAAE,CAAC;QAC1D,OAAO,kCAAkC,CAAC;IAC3C,CAAC;IACD,IAAI,KAAK,YAAY,sBAAsB,IAAI,KAAK,CAAC,IAAI,KAAK,eAAe,EAAE,CAAC;QAC/E,OAAO,6BAA6B,CAAC;IACtC,CAAC;IACD,OAAO,SAAS,CAAC;AAClB,CAAC;AAED,MAAM,UAAU,gCAAgC,CAC/C,WAAkC,EAClC,aAAqB,EACrB,WAA8C,EAC9C,QAA0B,EAC1B,gBAAgC,EAAE,EAClC,WAA6C;IAE7C,MAAM,qBAAqB,GAAG,aAAa,CAAC,SAAS,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,EAAE,KAAK,WAAW,CAAC,gBAAgB,CAAC,CAAC;IAC5G,IAAI,CAAC,WAAW,CAAC,gBAAgB,IAAI,qBAAqB,KAAK,CAAC,CAAC,EAAE,CAAC;QACnE,IAAI,WAAW;YAAE,WAAW,CAAC,eAAe,GAAG,8BAA8B,CAAC;QAC9E,OAAO,SAAS,CAAC;IAClB,CAAC;IAED,MAAM,MAAM,GAAG;QACd,gDAAgD;QAChD,0GAA0G;QAC1G,yGAAyG;KACzG,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IACb,MAAM,UAAU,GAAG,QAAQ,CAAC,UAAU,EAAE,IAAI,EAAE,CAAC;IAC/C,MAAM,SAAS,GAAG,UAAU,CAAC,CAAC,CAAC,GAAG,MAAM,qBAAqB,UAAU,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC;IACnF,MAAM,eAAe,GAAG,IAAI,CAAC,GAAG,CAAC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,aAAa,GAAG,GAAG,CAAC,CAAC,CAAC;IACzE,MAAM,eAAe,GAAG,WAAW,CAAC,eAAe,EAAE,IAAI,EAAE,CAAC;IAC5D,IAAI,OAAO,GAAG,SAAS,CAAC;IACxB,IAAI,eAAe,EAAE,CAAC;QACrB,MAAM,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,eAAe,GAAG,MAAM,CAAC,UAAU,CAAC,GAAG,SAAS,4BAA4B,CAAC,CAAC,CAAC;QAClH,MAAM,gBAAgB,GAAG,gCAAgC,CAAC;QAC1D,MAAM,eAAe,GACpB,MAAM,CAAC,UAAU,CAAC,eAAe,CAAC,IAAI,cAAc;YACnD,CAAC,CAAC,eAAe;YACjB,CAAC,CAAC,GAAG,YAAY,CAAC,eAAe,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,cAAc,GAAG,MAAM,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC,CAAC,GAAG,gBAAgB,EAAE,CAAC;QAC7H,OAAO,GAAG,GAAG,SAAS,6BAA6B,eAAe,EAAE,CAAC;IACtE,CAAC;IAED,MAAM,WAAW,GAAiC;QACjD,MAAM,EAAE,4CAA4C;QACpD,MAAM,EAAE,8BAA8B;QACtC,WAAW;QACX,GAAG,CAAC,UAAU,CAAC,CAAC,CAAC,EAAE,UAAU,EAAE,CAAC,CAAC,CAAC,EAAE,CAAC;KACrC,CAAC;IACF,IAAI,cAAc,GAAG,CAAC,CAAC;IAEvB,MAAM,mBAAmB,GAAoB;QAC5C,IAAI,EAAE,YAAY;QAClB,EAAE,EAAE,0CAA0C;QAC9C,QAAQ,EAAE,aAAa,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,IAAI;QAC1C,SAAS,EAAE,IAAI,IAAI,CAAC,CAAC,CAAC,CAAC,WAAW,EAAE;QACpC,OAAO;QACP,gBAAgB,EAAE,aAAa,CAAC,CAAC,CAAC,EAAE,EAAE,IAAI,WAAW,CAAC,gBAAgB;QACtE,YAAY,EAAE,WAAW,CAAC,YAAY;QACtC,OAAO,EAAE,EAAE,GAAG,WAAW,EAAE,cAAc,EAAE,UAAU,EAAE;QACvD,QAAQ,EAAE,IAAI;KACd,CAAC;IAEF,IAAI,iBAAmE,CAAC;IACxE,IAAI,CAAC;QACJ,iBAAiB,GAAG,6BAA6B,CAChD,mBAAmB,CAAC,CAAC,GAAG,aAAa,EAAE,mBAAmB,CAAC,CAAC,CAAC,QAAQ,CACrE,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACR,IAAI,WAAW;YAAE,WAAW,CAAC,eAAe,GAAG,+BAA+B,CAAC;QAC/E,OAAO,SAAS,CAAC;IAClB,CAAC;IAED,6EAA6E;IAC7E,gFAAgF;IAChF,gFAAgF;IAChF,+EAA+E;IAC/E,wEAAwE;IACxE,MAAM,gCAAgC,GAAG,uBAAuB,CAAC,iBAAiB,CAAC,CAAC;IAEpF,MAAM,uBAAuB,GAAG,IAAI,GAAG,EAAkB,CAAC;IAC1D,KAAK,MAAM,CAAC,KAAK,EAAE,OAAO,CAAC,IAAI,iBAAiB,CAAC,OAAO,EAAE,EAAE,CAAC;QAC5D,MAAM,OAAO,GAAG,wBAAwB,CAAC,OAAO,CAAC,CAAC;QAClD,IAAI,OAAO,KAAK,SAAS;YAAE,uBAAuB,CAAC,GAAG,CAAC,OAAO,EAAE,KAAK,CAAC,CAAC;IACxE,CAAC;IACD,MAAM,YAAY,GAAG,uBAAuB,CAAC,GAAG,CAAC,mBAAmB,CAAC,EAAE,CAAC,CAAC;IACzE,MAAM,YAAY,GAAG,IAAI,KAAK,CAAC,iBAAiB,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACzE,MAAM,WAAW,GAAG,IAAI,KAAK,CAAC,iBAAiB,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACpE,MAAM,SAAS,GAAG,IAAI,GAAG,EAAsD,CAAC;IAChF,MAAM,WAAW,GAAG,IAAI,GAAG,EAAoB,CAAC;IAChD,KAAK,IAAI,KAAK,GAAG,iBAAiB,CAAC,MAAM,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC;QACpE,MAAM,OAAO,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;QACzC,WAAW,CAAC,KAAK,CAAC,GAAG,WAAW,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;QAC5C,IAAI,gCAAgC,CAAC,KAAK,CAAC,EAAE,CAAC;YAC7C,YAAY,CAAC,KAAK,CAAC,GAAG,YAAY,CAAC,KAAK,GAAG,CAAC,CAAC,CAAC;YAC9C,SAAS;QACV,CAAC;QACD,IAAI,aAAa,GAAG,KAAK,CAAC;QAC1B,IAAI,UAA8B,CAAC;QACnC,IAAI,CAAC;YACJ,aAAa,GAAG,wBAAwB,CAAC,CAAC,OAAO,CAAC,CAAC,CAAC;YACpD,UAAU,GAAG,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAC;YAC/E,IAAI,iBAAiB,GAAG,CAAC,CAAC;YAC1B,IAAI,WAAW,GAAG,CAAC,CAAC;YACpB,IAAI,CAAC,aAAa,IAAI,UAAU,KAAK,SAAS,IAAI,OAAO,CAAC,IAAI,KAAK,YAAY,EAAE,CAAC;gBACjF,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;oBACrC,IAAI,KAAK,CAAC,IAAI,KAAK,OAAO;wBAAE,SAAS;oBACrC,kEAAkE;oBAClE,oEAAoE;oBACpE,iBAAiB,IAAI,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC,GAAG,CAAC,CAAC;oBACvE,WAAW,IAAI,cAAc,CAAC,EAAE,GAAG,OAAO,EAAE,OAAO,EAAE,CAAC,KAAK,CAAC,EAAE,CAAC,CAAC;gBACjE,CAAC;YACF,CAAC;YACD,WAAW,CAAC,KAAK,CAAC;gBACjB,UAAU,KAAK,SAAS;oBACvB,CAAC,CAAC,MAAM,CAAC,iBAAiB;oBAC1B,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,cAAc,CAAC,OAAO,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC,UAAU,CAAC,GAAG,iBAAiB,GAAG,WAAW,CAAC,CAAC;QACxG,CAAC;QAAC,MAAM,CAAC;YACR,aAAa,GAAG,IAAI,CAAC;YACrB,WAAW,CAAC,KAAK,CAAC,GAAG,MAAM,CAAC,iBAAiB,CAAC;QAC/C,CAAC;QACD,YAAY,CAAC,KAAK,CAAC,GAAG,YAAY,CAAC,KAAK,GAAG,CAAC,CAAC,IAAI,aAAa,CAAC;QAC/D,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;YAAE,SAAS;QACjC,IAAI,OAAO,CAAC,IAAI,KAAK,YAAY,IAAI,OAAO,OAAO,CAAC,UAAU,KAAK,QAAQ,EAAE,CAAC;YAC7E,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,EAAE,CAAC;YAC1D,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;YACpB,WAAW,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,EAAE,OAAO,CAAC,CAAC;QAC9C,CAAC;aAAM,IAAI,OAAO,CAAC,IAAI,KAAK,WAAW,IAAI,KAAK,CAAC,OAAO,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC;YAC3E,KAAK,MAAM,KAAK,IAAI,OAAO,CAAC,OAAO,EAAE,CAAC;gBACrC,IAAI,CAAC,QAAQ,CAAC,KAAK,CAAC,IAAI,KAAK,CAAC,IAAI,KAAK,UAAU,IAAI,OAAO,KAAK,CAAC,EAAE,KAAK,QAAQ;oBAAE,SAAS;gBAC5F,MAAM,IAAI,GAAG,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,EAAE,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC;gBAC3E,IAAI,CAAC,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;gBACzB,IAAI,CAAC,UAAU,KAAK,KAAK,CAAC,UAAU,KAAK,IAAI,CAAC;gBAC9C,SAAS,CAAC,GAAG,CAAC,KAAK,CAAC,EAAE,EAAE,IAAI,CAAC,CAAC;YAC/B,CAAC;QACF,CAAC;IACF,CAAC;IACD,MAAM,oBAAoB,GAAG,IAAI,UAAU,CAAC,iBAAiB,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC1E,MAAM,gBAAgB,GAAG,CAAC,KAAa,EAAE,YAAoB,EAAE,EAAE;QAChE,MAAM,YAAY,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,KAAK,CAAC,CAAC;QACxC,MAAM,UAAU,GAAG,IAAI,CAAC,GAAG,CAAC,iBAAiB,CAAC,MAAM,GAAG,CAAC,EAAE,YAAY,CAAC,CAAC;QACxE,IAAI,YAAY,IAAI,UAAU;YAAE,OAAO;QACvC,oBAAoB,CAAC,YAAY,CAAC,EAAE,CAAC;QACrC,oBAAoB,CAAC,UAAU,CAAC,EAAE,CAAC;IACpC,CAAC,CAAC;IACF,KAAK,MAAM,CAAC,EAAE,EAAE,IAAI,CAAC,IAAI,SAAS,EAAE,CAAC;QACpC,MAAM,OAAO,GAAG,WAAW,CAAC,GAAG,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC;QAC1C,IAAI,IAAI,CAAC,OAAO,CAAC,MAAM,KAAK,CAAC,IAAI,OAAO,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACvD,gBAAgB,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YAC3E,SAAS;QACV,CAAC;QACD,MAAM,SAAS,GAAG,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC;QAClC,MAAM,WAAW,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;QAC/B,IAAI,IAAI,CAAC,UAAU;YAAE,gBAAgB,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;aAC1E,IAAI,SAAS,GAAG,WAAW;YAAE,gBAAgB,CAAC,SAAS,GAAG,CAAC,EAAE,WAAW,GAAG,CAAC,CAAC,CAAC;;YAC9E,gBAAgB,CAAC,CAAC,EAAE,IAAI,CAAC,GAAG,CAAC,SAAS,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC;IAChE,CAAC;IACD,KAAK,MAAM,CAAC,EAAE,EAAE,OAAO,CAAC,IAAI,WAAW,EAAE,CAAC;QACzC,IAAI,SAAS,CAAC,GAAG,CAAC,EAAE,CAAC;YAAE,SAAS;QAChC,gBAAgB,CAAC,CAAC,EAAE,OAAO,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;IACrC,CAAC;IACD,MAAM,gBAAgB,GAAG,IAAI,KAAK,CAAC,iBAAiB,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAC5E,IAAI,qBAAqB,GAAG,CAAC,CAAC;IAC9B,KAAK,IAAI,KAAK,GAAG,CAAC,EAAE,KAAK,GAAG,gBAAgB,CAAC,MAAM,EAAE,KAAK,EAAE,EAAE,CAAC;QAC9D,qBAAqB,IAAI,oBAAoB,CAAC,KAAK,CAAC,CAAC;QACrD,gBAAgB,CAAC,KAAK,CAAC,GAAG,qBAAqB,KAAK,CAAC,CAAC;IACvD,CAAC;IAED,MAAM,gBAAgB,GAAG,CACxB,gBAAwB,EACxB,cAA2E,EACd,EAAE;QAC/D,cAAc,EAAE,CAAC;QACjB,MAAM,OAAO,GAAG,EAAE,GAAG,WAAW,EAAE,cAAc,EAAE,CAAC;QACnD,MAAM,MAAM,GAAG,CAAC,eAAqD,EAAa,EAAE;YACnF,IAAI,WAAW,EAAE,CAAC;gBACjB,WAAW,CAAC,eAAe,GAAG,eAAe,CAAC;gBAC9C,WAAW,CAAC,mBAAmB,KAAK,EAAE,CAAC;gBACvC,WAAW,CAAC,mBAAmB,CAAC,IAAI,CAAC,EAAE,gBAAgB,EAAE,eAAe,EAAE,CAAC,CAAC;YAC7E,CAAC;YACD,OAAO,SAAS,CAAC;QAClB,CAAC,CAAC;QACF,MAAM,MAAM,GAAmD;YAC9D,OAAO;YACP,gBAAgB;YAChB,YAAY,EAAE,WAAW,CAAC,YAAY;YACtC,OAAO;SACP,CAAC;QACF,MAAM,UAAU,GAAG,uBAAuB,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;QACjE,IAAI,UAAU,KAAK,SAAS;YAAE,OAAO,MAAM,CAAC,+BAA+B,CAAC,CAAC;QAC7E,MAAM,aAAa,GAAG,IAAI,CAAC,GAAG,CAAC,UAAU,EAAE,iBAAiB,CAAC,MAAM,CAAC,CAAC;QACrE,IAAI,YAAY,CAAC,aAAa,CAAC;YAAE,OAAO,MAAM,CAAC,yBAAyB,CAAC,CAAC;QAC1E,IAAI,CAAC,gBAAgB,CAAC,aAAa,CAAC;YAAE,OAAO,MAAM,CAAC,uBAAuB,CAAC,CAAC;QAC7E,gFAAgF;QAChF,kFAAkF;QAClF,MAAM,MAAM,GAAG,aAAa,GAAG,6BAA6B,CAAC,aAAa,EAAE,WAAW,CAAC,QAAQ,CAAC,CAAC;QAClG,MAAM,aAAa,GAClB,YAAY,KAAK,SAAS;YACzB,CAAC,CAAC,MAAM,CAAC,iBAAiB;YAC1B,CAAC,CAAC,IAAI,CAAC,GAAG,CACR,cAAc,CAAC,iBAAiB,CAAC,YAAY,CAAC,CAAC,EAC/C,MAAM,CAAC,UAAU,CAAC,IAAI,CAAC,SAAS,CAAC,iBAAiB,CAAC,YAAY,CAAC,CAAC,CAAC,CAClE,CAAC;QACL,MAAM,cAAc,GAAG,CAAC,WAAW,CAAC,aAAa,CAAC,IAAI,MAAM,CAAC,iBAAiB,CAAC,GAAG,aAAa,CAAC;QAChG,IAAI,cAAc,GAAG,MAAM,EAAE,CAAC;YAC7B,IAAI,WAAW;gBAAE,WAAW,CAAC,cAAc,GAAG,IAAI,CAAC;YACnD,OAAO,MAAM,CAAC,gCAAgC,CAAC,CAAC;QACjD,CAAC;QACD,OAAO,EAAE,GAAG,MAAM,EAAE,oBAAoB,EAAE,cAAc,EAAE,CAAC;IAC5D,CAAC,CAAC;IAEF,2BAA2B;IAC3B,MAAM,QAAQ,GAAG,gBAAgB,CAAC,WAAW,CAAC,gBAAgB,EAAE,UAAU,CAAC,CAAC;IAC5E,IAAI,QAAQ,EAAE,CAAC;QACd,IAAI,WAAW;YAAE,WAAW,CAAC,iBAAiB,GAAG,cAAc,CAAC;QAChE,OAAO,QAAQ,CAAC;IACjB,CAAC;IAED,oFAAoF;IACpF,+EAA+E;IAC/E,KAAK,IAAI,KAAK,GAAG,qBAAqB,GAAG,CAAC,EAAE,KAAK,IAAI,CAAC,EAAE,KAAK,EAAE,EAAE,CAAC;QACjE,MAAM,KAAK,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;QACnC,IAAI,KAAK,CAAC,IAAI,KAAK,YAAY;YAAE,MAAM;QACvC,6DAA6D;QAC7D,MAAM,OAAO,GAAG,gBAAgB,CAAC,KAAK,CAAC,EAAE,EAAE,uBAAuB,CAAC,CAAC;QACpE,IAAI,OAAO,EAAE,CAAC;YACb,IAAI,WAAW;gBAAE,WAAW,CAAC,iBAAiB,GAAG,cAAc,CAAC;YAChE,OAAO,OAAO,CAAC;QAChB,CAAC;IACF,CAAC;IAED,qCAAqC;IACrC,KAAK,IAAI,KAAK,GAAG,aAAa,CAAC,MAAM,GAAG,CAAC,EAAE,KAAK,GAAG,qBAAqB,EAAE,KAAK,EAAE,EAAE,CAAC;QACnF,MAAM,KAAK,GAAG,aAAa,CAAC,KAAK,CAAC,CAAC;QACnC,IAAI,CAAC,qBAAqB,CAAC,KAAK,CAAC;YAAE,SAAS;QAC5C,MAAM,UAAU,GAAG,gBAAgB,CAAC,KAAK,CAAC,EAAE,EAAE,kBAAkB,CAAC,CAAC;QAClE,IAAI,UAAU,EAAE,CAAC;YAChB,IAAI,WAAW;gBAAE,WAAW,CAAC,iBAAiB,GAAG,cAAc,CAAC;YAChE,OAAO,UAAU,CAAC;QACnB,CAAC;QACD,MAAM;IACP,CAAC;IAED,IAAI,WAAW;QAAE,WAAW,CAAC,iBAAiB,GAAG,cAAc,CAAC;IAChE,OAAO,SAAS,CAAC;AAClB,CAAC","sourcesContent":["import { type CompactionPreparation, type CompactionResult, estimateTokens } from \"../../../compaction/index.ts\";\nimport { StreamDurationBudgetError, StreamIdleTimeoutError } from \"../../../compaction/stream-watchdog.ts\";\nimport { filterContextExcludedMessages } from \"../../../messages.ts\";\nimport {\n\tbuildSessionContext,\n\ttype CompactionEntry,\n\tgetSessionContextEntryId,\n\ttype SessionEntry,\n} from \"../../../session-manager.ts\";\nimport { markFailedTurnFragments } from \"./fallback-failed-turn-normalization.ts\";\nimport { SummarizationOverflowExhaustedError } from \"./overflow-retry.ts\";\nimport { resolveEffectiveReserveTokens } from \"./policy.ts\";\nimport { hasUnsafeRetainedContent } from \"./retained-message-safety.ts\";\nimport { SummaryGenerationError, SummaryRequestError } from \"./speculative.ts\";\nimport { capUtf8Bytes } from \"./task-intent.ts\";\n\nexport type RequiredCompactionFallbackFailure =\n\t| \"summarization-timeout\"\n\t| \"upstream-stream-truncated\"\n\t| \"summarization-overflow-exhausted\"\n\t| \"summarization-empty-summary\";\n\ninterface RecoveryMetadata {\n\ttaskIntent?: string;\n\ttodoSnapshot?: unknown;\n\tcheckpoint?: unknown;\n}\n\nexport type DeterministicFallbackRejectionReason =\n\t| \"missing-preparation-boundary\"\n\t| \"unsafe-retained-content\"\n\t| \"retained-token-budget-exceeded\"\n\t| \"atomic-tool-chain-cut\"\n\t| \"context-reconstruction-failed\";\n\ninterface DeterministicFallbackDetails {\n\tschema: \"senpi.compaction.deterministic-fallback.v1\";\n\torigin: \"required-compaction-recovery\";\n\tfailureKind: RequiredCompactionFallbackFailure;\n\ttaskIntent?: string;\n\tretainedSuffix?: \"prepared\" | \"latest-user-turn\" | \"earlier-safe-boundary\";\n}\n\nexport interface DeterministicFallbackDiagnostic {\n\trejectionReason?: DeterministicFallbackRejectionReason;\n\tcandidateRejections?: Array<{\n\t\tfirstKeptEntryId: string;\n\t\trejectionReason: DeterministicFallbackRejectionReason;\n\t}>;\n\tcandidatesChecked?: number;\n\tbudgetExceeded?: boolean;\n}\n\nconst NON_VISIBLE_USER_TEXT = /[\\p{White_Space}\\p{Default_Ignorable_Code_Point}]/gu;\n\nfunction isRecord(value: unknown): value is Record<string, unknown> {\n\treturn typeof value === \"object\" && value !== null;\n}\n\nfunction hasMeaningfulUserText(entry: SessionEntry): boolean {\n\tif (entry.type !== \"message\" || !isRecord(entry.message) || entry.message.role !== \"user\") return false;\n\tconst content = entry.message.content;\n\tconst hasVisibleText = (text: unknown): boolean =>\n\t\ttypeof text === \"string\" && text.normalize(\"NFKC\").replace(NON_VISIBLE_USER_TEXT, \"\").length > 0;\n\tif (typeof content === \"string\") return hasVisibleText(content);\n\tif (!Array.isArray(content)) return false;\n\treturn content.some((block) => isRecord(block) && block.type === \"text\" && hasVisibleText(block.text));\n}\n\n/**\n * Bound a value graph that came from persisted, potentially hostile session data.\n * Returns false when the graph contains anything we must not read (accessors,\n * non-plain prototypes, cycles, functions, symbols) so callers fail closed\n * instead of executing persisted code or walking unbounded structures.\n */\nfunction isSafeBoundedValue(value: unknown, seen = new Set<object>(), depth = 0): boolean {\n\tif (depth > 32) return false;\n\tif (value === null || value === undefined) return true;\n\tconst kind = typeof value;\n\tif (kind === \"string\" || kind === \"number\" || kind === \"boolean\") return true;\n\tif (kind !== \"object\") return false;\n\tif (seen.has(value as object)) return false;\n\tseen.add(value as object);\n\tif (Array.isArray(value)) {\n\t\tfor (const item of value) if (!isSafeBoundedValue(item, seen, depth + 1)) return false;\n\t\treturn true;\n\t}\n\tconst proto = Object.getPrototypeOf(value);\n\tif (proto !== Object.prototype && proto !== null) return false;\n\tfor (const descriptor of Object.values(Object.getOwnPropertyDescriptors(value))) {\n\t\tif (descriptor.get !== undefined || descriptor.set !== undefined) return false;\n\t\tif (typeof descriptor.value === \"function\" || typeof descriptor.value === \"symbol\") return false;\n\t\tif (!isSafeBoundedValue(descriptor.value, seen, depth + 1)) return false;\n\t}\n\treturn true;\n}\n\nexport function classifyRequiredCompactionFallbackFailure(\n\terror: unknown,\n): RequiredCompactionFallbackFailure | undefined {\n\tif (error instanceof StreamDurationBudgetError || error instanceof StreamIdleTimeoutError) {\n\t\treturn \"summarization-timeout\";\n\t}\n\tif (error instanceof SummaryRequestError && error.transient && error.failureKind === \"upstream-stream-truncated\") {\n\t\treturn \"upstream-stream-truncated\";\n\t}\n\tif (error instanceof SummarizationOverflowExhaustedError) {\n\t\treturn \"summarization-overflow-exhausted\";\n\t}\n\tif (error instanceof SummaryGenerationError && error.kind === \"empty-summary\") {\n\t\treturn \"summarization-empty-summary\";\n\t}\n\treturn undefined;\n}\n\nexport function createRequiredCompactionFallback(\n\tpreparation: CompactionPreparation,\n\tcontextWindow: number,\n\tfailureKind: RequiredCompactionFallbackFailure,\n\tmetadata: RecoveryMetadata,\n\tbranchEntries: SessionEntry[] = [],\n\tdiagnostics?: DeterministicFallbackDiagnostic,\n): CompactionResult<DeterministicFallbackDetails> | undefined {\n\tconst preparedBoundaryIndex = branchEntries.findIndex((entry) => entry.id === preparation.firstKeptEntryId);\n\tif (!preparation.firstKeptEntryId || preparedBoundaryIndex === -1) {\n\t\tif (diagnostics) diagnostics.rejectionReason = \"missing-preparation-boundary\";\n\t\treturn undefined;\n\t}\n\n\tconst marker = [\n\t\t\"[Deterministic compaction recovery checkpoint]\",\n\t\t\"Generated summarization did not complete, so older context was reduced without another provider request.\",\n\t\t\"Continue from the retained messages after this checkpoint. Treat omitted transcript details as unknown.\",\n\t].join(\"\\n\");\n\tconst taskIntent = metadata.taskIntent?.trim();\n\tconst fixedText = taskIntent ? `${marker}\\n\\nTask intent:\\n${taskIntent}` : marker;\n\tconst maxSummaryBytes = Math.max(1_024, Math.floor(contextWindow * 0.4));\n\tconst previousSummary = preparation.previousSummary?.trim();\n\tlet summary = fixedText;\n\tif (previousSummary) {\n\t\tconst availableBytes = Math.max(0, maxSummaryBytes - Buffer.byteLength(`${fixedText}\\n\\nPrevious checkpoint:\\n`));\n\t\tconst truncationMarker = \"\\n[Older checkpoint truncated]\";\n\t\tconst boundedPrevious =\n\t\t\tBuffer.byteLength(previousSummary) <= availableBytes\n\t\t\t\t? previousSummary\n\t\t\t\t: `${capUtf8Bytes(previousSummary, Math.max(0, availableBytes - Buffer.byteLength(truncationMarker)))}${truncationMarker}`;\n\t\tsummary = `${fixedText}\\n\\nPrevious checkpoint:\\n${boundedPrevious}`;\n\t}\n\n\tconst baseDetails: DeterministicFallbackDetails = {\n\t\tschema: \"senpi.compaction.deterministic-fallback.v1\",\n\t\torigin: \"required-compaction-recovery\",\n\t\tfailureKind,\n\t\t...(taskIntent ? { taskIntent } : {}),\n\t};\n\tlet candidateCount = 0;\n\n\tconst syntheticCompaction: CompactionEntry = {\n\t\ttype: \"compaction\",\n\t\tid: \"__senpi_deterministic_fallback_preview__\",\n\t\tparentId: branchEntries.at(-1)?.id ?? null,\n\t\ttimestamp: new Date(0).toISOString(),\n\t\tsummary,\n\t\tfirstKeptEntryId: branchEntries[0]?.id ?? preparation.firstKeptEntryId,\n\t\ttokensBefore: preparation.tokensBefore,\n\t\tdetails: { ...baseDetails, retainedSuffix: \"prepared\" },\n\t\tfromHook: true,\n\t};\n\n\tlet projectedMessages: ReturnType<typeof filterContextExcludedMessages>;\n\ttry {\n\t\tprojectedMessages = filterContextExcludedMessages(\n\t\t\tbuildSessionContext([...branchEntries, syntheticCompaction]).messages,\n\t\t);\n\t} catch {\n\t\tif (diagnostics) diagnostics.rejectionReason = \"context-reconstruction-failed\";\n\t\treturn undefined;\n\t}\n\n\t// Evaluate the candidate the provider would actually receive: `convertToLlm`\n\t// drops failed/aborted assistant turns (and their orphaned tool results) before\n\t// every request, so their dangling toolCall blocks are neither incomplete calls\n\t// nor retained tokens here. Raw session history stays untouched - this mask is\n\t// local to the fallback projection (code-yeongyu/oh-my-openagent#7921).\n\tconst droppedByFailedTurnNormalization = markFailedTurnFragments(projectedMessages);\n\n\tconst messageIndexesByEntryId = new Map<string, number>();\n\tfor (const [index, message] of projectedMessages.entries()) {\n\t\tconst entryId = getSessionContextEntryId(message);\n\t\tif (entryId !== undefined) messageIndexesByEntryId.set(entryId, index);\n\t}\n\tconst summaryIndex = messageIndexesByEntryId.get(syntheticCompaction.id);\n\tconst unsafeSuffix = new Array(projectedMessages.length + 1).fill(false);\n\tconst tokenSuffix = new Array(projectedMessages.length + 1).fill(0);\n\tconst toolCalls = new Map<string, { indexes: number[]; incomplete: boolean }>();\n\tconst toolResults = new Map<string, number[]>();\n\tfor (let index = projectedMessages.length - 1; index >= 0; index--) {\n\t\tconst message = projectedMessages[index];\n\t\ttokenSuffix[index] = tokenSuffix[index + 1];\n\t\tif (droppedByFailedTurnNormalization[index]) {\n\t\t\tunsafeSuffix[index] = unsafeSuffix[index + 1];\n\t\t\tcontinue;\n\t\t}\n\t\tlet messageUnsafe = false;\n\t\tlet serialized: string | undefined;\n\t\ttry {\n\t\t\tmessageUnsafe = hasUnsafeRetainedContent([message]);\n\t\t\tserialized = isSafeBoundedValue(message) ? JSON.stringify(message) : undefined;\n\t\t\tlet imagePayloadBytes = 0;\n\t\t\tlet imageTokens = 0;\n\t\t\tif (!messageUnsafe && serialized !== undefined && message.role === \"toolResult\") {\n\t\t\t\tfor (const block of message.content) {\n\t\t\t\t\tif (block.type !== \"image\") continue;\n\t\t\t\t\t// Keep the JSON delimiters and metadata charged, but replace only\n\t\t\t\t\t// this validated image's escaped payload bytes with its token cost.\n\t\t\t\t\timagePayloadBytes += Buffer.byteLength(JSON.stringify(block.data)) - 2;\n\t\t\t\t\timageTokens += estimateTokens({ ...message, content: [block] });\n\t\t\t\t}\n\t\t\t}\n\t\t\ttokenSuffix[index] +=\n\t\t\t\tserialized === undefined\n\t\t\t\t\t? Number.POSITIVE_INFINITY\n\t\t\t\t\t: Math.max(estimateTokens(message), Buffer.byteLength(serialized) - imagePayloadBytes + imageTokens);\n\t\t} catch {\n\t\t\tmessageUnsafe = true;\n\t\t\ttokenSuffix[index] = Number.POSITIVE_INFINITY;\n\t\t}\n\t\tunsafeSuffix[index] = unsafeSuffix[index + 1] || messageUnsafe;\n\t\tif (!isRecord(message)) continue;\n\t\tif (message.role === \"toolResult\" && typeof message.toolCallId === \"string\") {\n\t\t\tconst indexes = toolResults.get(message.toolCallId) ?? [];\n\t\t\tindexes.push(index);\n\t\t\ttoolResults.set(message.toolCallId, indexes);\n\t\t} else if (message.role === \"assistant\" && Array.isArray(message.content)) {\n\t\t\tfor (const block of message.content) {\n\t\t\t\tif (!isRecord(block) || block.type !== \"toolCall\" || typeof block.id !== \"string\") continue;\n\t\t\t\tconst call = toolCalls.get(block.id) ?? { indexes: [], incomplete: false };\n\t\t\t\tcall.indexes.push(index);\n\t\t\t\tcall.incomplete ||= block.incomplete === true;\n\t\t\t\ttoolCalls.set(block.id, call);\n\t\t\t}\n\t\t}\n\t}\n\tconst invalidToolChainDiff = new Int32Array(projectedMessages.length + 2);\n\tconst markInvalidRange = (start: number, endExclusive: number) => {\n\t\tconst boundedStart = Math.max(0, start);\n\t\tconst boundedEnd = Math.min(projectedMessages.length + 1, endExclusive);\n\t\tif (boundedStart >= boundedEnd) return;\n\t\tinvalidToolChainDiff[boundedStart]++;\n\t\tinvalidToolChainDiff[boundedEnd]--;\n\t};\n\tfor (const [id, call] of toolCalls) {\n\t\tconst results = toolResults.get(id) ?? [];\n\t\tif (call.indexes.length !== 1 || results.length !== 1) {\n\t\t\tmarkInvalidRange(0, Math.max(call.indexes[0] ?? -1, results[0] ?? -1) + 1);\n\t\t\tcontinue;\n\t\t}\n\t\tconst callIndex = call.indexes[0];\n\t\tconst resultIndex = results[0];\n\t\tif (call.incomplete) markInvalidRange(0, Math.max(callIndex, resultIndex) + 1);\n\t\telse if (callIndex < resultIndex) markInvalidRange(callIndex + 1, resultIndex + 1);\n\t\telse markInvalidRange(0, Math.max(callIndex, resultIndex) + 1);\n\t}\n\tfor (const [id, results] of toolResults) {\n\t\tif (toolCalls.has(id)) continue;\n\t\tmarkInvalidRange(0, results[0] + 1);\n\t}\n\tconst toolChainValidAt = new Array(projectedMessages.length + 1).fill(true);\n\tlet invalidToolChainCount = 0;\n\tfor (let index = 0; index < toolChainValidAt.length; index++) {\n\t\tinvalidToolChainCount += invalidToolChainDiff[index];\n\t\ttoolChainValidAt[index] = invalidToolChainCount === 0;\n\t}\n\n\tconst projectCandidate = (\n\t\tfirstKeptEntryId: string,\n\t\tretainedSuffix: NonNullable<DeterministicFallbackDetails[\"retainedSuffix\"]>,\n\t): CompactionResult<DeterministicFallbackDetails> | undefined => {\n\t\tcandidateCount++;\n\t\tconst details = { ...baseDetails, retainedSuffix };\n\t\tconst reject = (rejectionReason: DeterministicFallbackRejectionReason): undefined => {\n\t\t\tif (diagnostics) {\n\t\t\t\tdiagnostics.rejectionReason = rejectionReason;\n\t\t\t\tdiagnostics.candidateRejections ??= [];\n\t\t\t\tdiagnostics.candidateRejections.push({ firstKeptEntryId, rejectionReason });\n\t\t\t}\n\t\t\treturn undefined;\n\t\t};\n\t\tconst result: CompactionResult<DeterministicFallbackDetails> = {\n\t\t\tsummary,\n\t\t\tfirstKeptEntryId,\n\t\t\ttokensBefore: preparation.tokensBefore,\n\t\t\tdetails,\n\t\t};\n\t\tconst startIndex = messageIndexesByEntryId.get(firstKeptEntryId);\n\t\tif (startIndex === undefined) return reject(\"context-reconstruction-failed\");\n\t\tconst retainedStart = Math.min(startIndex, projectedMessages.length);\n\t\tif (unsafeSuffix[retainedStart]) return reject(\"unsafe-retained-content\");\n\t\tif (!toolChainValidAt[retainedStart]) return reject(\"atomic-tool-chain-cut\");\n\t\t// The hard-limit valve reserves the scaled budget, so accepting against the raw\n\t\t// configured reserve would admit a context that valve immediately compacts again.\n\t\tconst budget = contextWindow - resolveEffectiveReserveTokens(contextWindow, preparation.settings);\n\t\tconst summaryTokens =\n\t\t\tsummaryIndex === undefined\n\t\t\t\t? Number.POSITIVE_INFINITY\n\t\t\t\t: Math.max(\n\t\t\t\t\t\testimateTokens(projectedMessages[summaryIndex]),\n\t\t\t\t\t\tBuffer.byteLength(JSON.stringify(projectedMessages[summaryIndex])),\n\t\t\t\t\t);\n\t\tconst retainedTokens = (tokenSuffix[retainedStart] ?? Number.POSITIVE_INFINITY) + summaryTokens;\n\t\tif (retainedTokens > budget) {\n\t\t\tif (diagnostics) diagnostics.budgetExceeded = true;\n\t\t\treturn reject(\"retained-token-budget-exceeded\");\n\t\t}\n\t\treturn { ...result, estimatedTokensAfter: retainedTokens };\n\t};\n\n\t// 1. Try prepared boundary\n\tconst prepared = projectCandidate(preparation.firstKeptEntryId, \"prepared\");\n\tif (prepared) {\n\t\tif (diagnostics) diagnostics.candidatesChecked = candidateCount;\n\t\treturn prepared;\n\t}\n\n\t// 2. If prepared boundary cut an atomic signed chain or was rejected, walk backward\n\t// to the actual chain boundary. The chain length is determined by the session.\n\tfor (let index = preparedBoundaryIndex - 1; index >= 0; index--) {\n\t\tconst entry = branchEntries[index];\n\t\tif (entry.type === \"compaction\") break;\n\t\t// Only consider message turn starts or assistant call starts\n\t\tconst earlier = projectCandidate(entry.id, \"earlier-safe-boundary\");\n\t\tif (earlier) {\n\t\t\tif (diagnostics) diagnostics.candidatesChecked = candidateCount;\n\t\t\treturn earlier;\n\t\t}\n\t}\n\n\t// 3. Try latest meaningful user turn\n\tfor (let index = branchEntries.length - 1; index > preparedBoundaryIndex; index--) {\n\t\tconst entry = branchEntries[index];\n\t\tif (!hasMeaningfulUserText(entry)) continue;\n\t\tconst latestUser = projectCandidate(entry.id, \"latest-user-turn\");\n\t\tif (latestUser) {\n\t\t\tif (diagnostics) diagnostics.candidatesChecked = candidateCount;\n\t\t\treturn latestUser;\n\t\t}\n\t\tbreak;\n\t}\n\n\tif (diagnostics) diagnostics.candidatesChecked = candidateCount;\n\treturn undefined;\n}\n"]}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Mark the messages that the transport's canonical failed-turn normalization
3
+ * removes before a provider request.
4
+ *
5
+ * `convertToLlm` (`core/messages.ts`) ends with `dropFailedAssistantTurns`, so an
6
+ * assistant turn that stopped with `error` or `aborted` - together with every tool
7
+ * result orphaned by that drop - never reaches the provider. The deterministic
8
+ * compaction fallback evaluates the same projection, so those fragments must not be
9
+ * counted as incomplete tool calls or charged against the retained budget.
10
+ *
11
+ * The shared function is reused directly (no rule mirroring) and the input array is
12
+ * never mutated: the result is a positional mask over the caller's own messages.
13
+ * Index `i` is `true` when message `i` is dropped by that normalization.
14
+ */
15
+ export declare function markFailedTurnFragments<T extends {
16
+ role: string;
17
+ }>(messages: readonly T[]): boolean[];
18
+ //# sourceMappingURL=fallback-failed-turn-normalization.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fallback-failed-turn-normalization.d.ts","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/compaction/fallback-failed-turn-normalization.ts"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;GAaG;AACH,wBAAgB,uBAAuB,CAAC,CAAC,SAAS;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,EAAE,QAAQ,EAAE,SAAS,CAAC,EAAE,GAAG,OAAO,EAAE,CAGrG"}
@@ -0,0 +1,20 @@
1
+ import { dropFailedAssistantTurns } from "@earendil-works/pi-ai";
2
+ /**
3
+ * Mark the messages that the transport's canonical failed-turn normalization
4
+ * removes before a provider request.
5
+ *
6
+ * `convertToLlm` (`core/messages.ts`) ends with `dropFailedAssistantTurns`, so an
7
+ * assistant turn that stopped with `error` or `aborted` - together with every tool
8
+ * result orphaned by that drop - never reaches the provider. The deterministic
9
+ * compaction fallback evaluates the same projection, so those fragments must not be
10
+ * counted as incomplete tool calls or charged against the retained budget.
11
+ *
12
+ * The shared function is reused directly (no rule mirroring) and the input array is
13
+ * never mutated: the result is a positional mask over the caller's own messages.
14
+ * Index `i` is `true` when message `i` is dropped by that normalization.
15
+ */
16
+ export function markFailedTurnFragments(messages) {
17
+ const retained = new Set(dropFailedAssistantTurns(messages));
18
+ return messages.map((message) => !retained.has(message));
19
+ }
20
+ //# sourceMappingURL=fallback-failed-turn-normalization.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fallback-failed-turn-normalization.js","sourceRoot":"","sources":["../../../../../src/core/extensions/builtin/compaction/fallback-failed-turn-normalization.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AAEjE;;;;;;;;;;;;;GAaG;AACH,MAAM,UAAU,uBAAuB,CAA6B,QAAsB;IACzF,MAAM,QAAQ,GAAG,IAAI,GAAG,CAAI,wBAAwB,CAAC,QAAQ,CAAC,CAAC,CAAC;IAChE,OAAO,QAAQ,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC;AAC1D,CAAC","sourcesContent":["import { dropFailedAssistantTurns } from \"@earendil-works/pi-ai\";\n\n/**\n * Mark the messages that the transport's canonical failed-turn normalization\n * removes before a provider request.\n *\n * `convertToLlm` (`core/messages.ts`) ends with `dropFailedAssistantTurns`, so an\n * assistant turn that stopped with `error` or `aborted` - together with every tool\n * result orphaned by that drop - never reaches the provider. The deterministic\n * compaction fallback evaluates the same projection, so those fragments must not be\n * counted as incomplete tool calls or charged against the retained budget.\n *\n * The shared function is reused directly (no rule mirroring) and the input array is\n * never mutated: the result is a positional mask over the caller's own messages.\n * Index `i` is `true` when message `i` is dropped by that normalization.\n */\nexport function markFailedTurnFragments<T extends { role: string }>(messages: readonly T[]): boolean[] {\n\tconst retained = new Set<T>(dropFailedAssistantTurns(messages));\n\treturn messages.map((message) => !retained.has(message));\n}\n"]}
@@ -458,7 +458,7 @@ export default function compactionExtension(pi, remoteCompactionDependencies = {
458
458
  let warmJobConsumed = false;
459
459
  invalidateSpeculativeCompaction(ctx);
460
460
  try {
461
- if (lanePolicy.disablesSenpiCompaction(ctx)) {
461
+ if (!lanePolicy.ownsCompaction(ctx, event.reason)) {
462
462
  return {
463
463
  cancel: true,
464
464
  rejectionCause: "external-owner",
@@ -641,7 +641,7 @@ export default function compactionExtension(pi, remoteCompactionDependencies = {
641
641
  }
642
642
  if (compactEvent.rejectionCause === "external-owner")
643
643
  return;
644
- if (!lanePolicy.disablesSenpiCompaction(ctx)) {
644
+ if (lanePolicy.ownsCompaction(ctx, compactEvent.reason)) {
645
645
  state = breaker.recordFailure(state, Date.now(), { route: compactEvent.reason });
646
646
  }
647
647
  });