@bastani/atomic 0.9.5-alpha.8 → 0.9.5

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 (530) hide show
  1. package/CHANGELOG.md +122 -0
  2. package/README.md +2 -2
  3. package/dist/builtin/cursor/CHANGELOG.md +12 -0
  4. package/dist/builtin/cursor/package.json +3 -3
  5. package/dist/builtin/cursor/src/model-mapper.ts +6 -3
  6. package/dist/builtin/intercom/CHANGELOG.md +37 -0
  7. package/dist/builtin/intercom/README.md +4 -2
  8. package/dist/builtin/intercom/broker/broker.ts +6 -114
  9. package/dist/builtin/intercom/broker/client.ts +29 -54
  10. package/dist/builtin/intercom/broker/delivered-message-cache.ts +44 -0
  11. package/dist/builtin/intercom/broker/pending-send-registry.ts +142 -0
  12. package/dist/builtin/intercom/broker/send-handler.ts +106 -0
  13. package/dist/builtin/intercom/broker/send-signature.ts +37 -0
  14. package/dist/builtin/intercom/foreground-detach-handoff.ts +136 -0
  15. package/dist/builtin/intercom/index-heavy.ts +49 -53
  16. package/dist/builtin/intercom/index.ts +278 -262
  17. package/dist/builtin/intercom/lazy-heavy-proxy.ts +114 -0
  18. package/dist/builtin/intercom/lazy-subagent-ack.ts +20 -0
  19. package/dist/builtin/intercom/lazy-tool-execution.ts +39 -0
  20. package/dist/builtin/intercom/lifecycle-lease.ts +51 -0
  21. package/dist/builtin/intercom/lifecycle.ts +37 -20
  22. package/dist/builtin/intercom/package.json +3 -6
  23. package/dist/builtin/intercom/reply-routing.ts +17 -0
  24. package/dist/builtin/intercom/subagent-relay.ts +58 -12
  25. package/dist/builtin/intercom/types.ts +3 -3
  26. package/dist/builtin/mcp/CHANGELOG.md +38 -0
  27. package/dist/builtin/mcp/OAUTH.md +1 -0
  28. package/dist/builtin/mcp/README.md +10 -6
  29. package/dist/builtin/mcp/apps-cancellation.ts +32 -0
  30. package/dist/builtin/mcp/call-tool-result.ts +9 -0
  31. package/dist/builtin/mcp/caller-wait.ts +50 -0
  32. package/dist/builtin/mcp/command-registration.ts +82 -0
  33. package/dist/builtin/mcp/direct-tool-executor.ts +279 -0
  34. package/dist/builtin/mcp/direct-tools.ts +40 -255
  35. package/dist/builtin/mcp/host-html-template.ts +4 -2
  36. package/dist/builtin/mcp/index.ts +274 -239
  37. package/dist/builtin/mcp/init.ts +96 -126
  38. package/dist/builtin/mcp/mcp-auth-flow.ts +247 -237
  39. package/dist/builtin/mcp/mcp-callback-server.ts +89 -68
  40. package/dist/builtin/mcp/mcp-oauth-provider.ts +23 -1
  41. package/dist/builtin/mcp/metadata-hydration.ts +52 -0
  42. package/dist/builtin/mcp/package.json +3 -3
  43. package/dist/builtin/mcp/proxy-auth.ts +4 -4
  44. package/dist/builtin/mcp/proxy-call.ts +111 -40
  45. package/dist/builtin/mcp/proxy-connect.ts +35 -15
  46. package/dist/builtin/mcp/proxy-info-modes.ts +106 -23
  47. package/dist/builtin/mcp/proxy-modes.ts +3 -3
  48. package/dist/builtin/mcp/session-cleanup-barrier.ts +43 -0
  49. package/dist/builtin/mcp/startup-warmup.ts +98 -0
  50. package/dist/builtin/mcp/state-lease.ts +12 -0
  51. package/dist/builtin/mcp/tool-result-renderer.ts +3 -3
  52. package/dist/builtin/mcp/ui-server.ts +8 -8
  53. package/dist/builtin/mcp/ui-session.ts +9 -18
  54. package/dist/builtin/subagents/CHANGELOG.md +78 -0
  55. package/dist/builtin/subagents/README.md +30 -29
  56. package/dist/builtin/subagents/agents/code-simplifier.md +2 -2
  57. package/dist/builtin/subagents/agents/codebase-analyzer.md +2 -2
  58. package/dist/builtin/subagents/agents/codebase-locator.md +2 -2
  59. package/dist/builtin/subagents/agents/codebase-online-researcher.md +2 -2
  60. package/dist/builtin/subagents/agents/codebase-pattern-finder.md +2 -2
  61. package/dist/builtin/subagents/agents/codebase-research-analyzer.md +2 -2
  62. package/dist/builtin/subagents/agents/codebase-research-locator.md +2 -2
  63. package/dist/builtin/subagents/agents/debugger.md +2 -2
  64. package/dist/builtin/subagents/agents/worker.md +2 -2
  65. package/dist/builtin/subagents/package.json +5 -5
  66. package/dist/builtin/subagents/prompts/review-loop.md +1 -1
  67. package/dist/builtin/subagents/skills/subagent/SKILL.md +37 -36
  68. package/dist/builtin/subagents/src/extension/api-lifecycle.ts +64 -0
  69. package/dist/builtin/subagents/src/extension/fanout-child.ts +44 -51
  70. package/dist/builtin/subagents/src/extension/index.ts +289 -320
  71. package/dist/builtin/subagents/src/extension/prompt-guidance.ts +7 -10
  72. package/dist/builtin/subagents/src/extension/schemas.ts +22 -5
  73. package/dist/builtin/subagents/src/extension/startup-maintenance.ts +89 -0
  74. package/dist/builtin/subagents/src/extension/tool-description.ts +29 -0
  75. package/dist/builtin/subagents/src/intercom/intercom-bridge.ts +13 -2
  76. package/dist/builtin/subagents/src/intercom/result-intercom.ts +4 -4
  77. package/dist/builtin/subagents/src/runs/background/async-event-journal.ts +106 -0
  78. package/dist/builtin/subagents/src/runs/background/async-execution-common.ts +26 -20
  79. package/dist/builtin/subagents/src/runs/background/async-execution-single.ts +13 -2
  80. package/dist/builtin/subagents/src/runs/background/async-execution-types.ts +3 -0
  81. package/dist/builtin/subagents/src/runs/background/async-job-tracker.ts +1 -1
  82. package/dist/builtin/subagents/src/runs/background/completion-claims.ts +189 -0
  83. package/dist/builtin/subagents/src/runs/background/completion-dedupe.ts +44 -7
  84. package/dist/builtin/subagents/src/runs/background/completion-notification.ts +34 -0
  85. package/dist/builtin/subagents/src/runs/background/notify.ts +72 -22
  86. package/dist/builtin/subagents/src/runs/background/result-delivery-processor.ts +232 -0
  87. package/dist/builtin/subagents/src/runs/background/result-file-claims.ts +151 -0
  88. package/dist/builtin/subagents/src/runs/background/result-quarantine.ts +72 -0
  89. package/dist/builtin/subagents/src/runs/background/result-retry-scheduler.ts +48 -0
  90. package/dist/builtin/subagents/src/runs/background/result-status.ts +81 -0
  91. package/dist/builtin/subagents/src/runs/background/result-watcher-data.ts +59 -0
  92. package/dist/builtin/subagents/src/runs/background/result-watcher.ts +177 -233
  93. package/dist/builtin/subagents/src/runs/background/run-id-resolver.ts +3 -2
  94. package/dist/builtin/subagents/src/runs/background/stale-run-reconciler.ts +73 -14
  95. package/dist/builtin/subagents/src/runs/background/subagent-runner-streaming.ts +29 -15
  96. package/dist/builtin/subagents/src/runs/background/top-level-async.ts +1 -2
  97. package/dist/builtin/subagents/src/runs/foreground/chain-execution-dynamic-step.ts +1 -0
  98. package/dist/builtin/subagents/src/runs/foreground/chain-execution-parallel-runner.ts +1 -0
  99. package/dist/builtin/subagents/src/runs/foreground/chain-execution-parallel-step.ts +1 -0
  100. package/dist/builtin/subagents/src/runs/foreground/chain-execution-sequential-step.ts +7 -9
  101. package/dist/builtin/subagents/src/runs/foreground/chain-execution-types.ts +3 -13
  102. package/dist/builtin/subagents/src/runs/foreground/chain-execution.ts +2 -15
  103. package/dist/builtin/subagents/src/runs/foreground/execution-attempt-finalize.ts +2 -2
  104. package/dist/builtin/subagents/src/runs/foreground/execution-attempt.ts +63 -46
  105. package/dist/builtin/subagents/src/runs/foreground/execution-detach-reservations.ts +48 -0
  106. package/dist/builtin/subagents/src/runs/foreground/execution-detach-route.ts +17 -0
  107. package/dist/builtin/subagents/src/runs/foreground/execution-run-sync.ts +39 -26
  108. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-async.ts +3 -1
  109. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-chain.ts +3 -51
  110. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-context.ts +6 -11
  111. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-input.ts +11 -2
  112. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel-task.ts +2 -0
  113. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-parallel.ts +5 -112
  114. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-resume.ts +2 -0
  115. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-single.ts +68 -118
  116. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-status.ts +79 -7
  117. package/dist/builtin/subagents/src/runs/foreground/subagent-executor-types.ts +2 -2
  118. package/dist/builtin/subagents/src/runs/foreground/subagent-executor.ts +3 -0
  119. package/dist/builtin/subagents/src/runs/shared/pi-args.ts +2 -2
  120. package/dist/builtin/subagents/src/runs/shared/pi-spawn.ts +55 -12
  121. package/dist/builtin/subagents/src/shared/artifacts.ts +22 -11
  122. package/dist/builtin/subagents/src/shared/event-jsonl-writer.ts +294 -0
  123. package/dist/builtin/subagents/src/shared/exclusive-file-publication.ts +44 -0
  124. package/dist/builtin/subagents/src/shared/jsonl-writer.ts +1 -0
  125. package/dist/builtin/subagents/src/shared/model-info.ts +2 -2
  126. package/dist/builtin/subagents/src/shared/settings.ts +20 -10
  127. package/dist/builtin/subagents/src/shared/types-async.ts +3 -1
  128. package/dist/builtin/subagents/src/shared/types-config.ts +2 -0
  129. package/dist/builtin/subagents/src/shared/types-runtime.ts +3 -2
  130. package/dist/builtin/subagents/src/slash/slash-commands.ts +9 -12
  131. package/dist/builtin/subagents/src/slash/slash-live-state.ts +63 -32
  132. package/dist/builtin/subagents/src/tui/render-result.ts +7 -0
  133. package/dist/builtin/subagents/src/tui/render-stable-output.ts +1 -0
  134. package/dist/builtin/subagents/src/tui/render-widget.ts +157 -89
  135. package/dist/builtin/web-access/CHANGELOG.md +28 -0
  136. package/dist/builtin/web-access/README.md +4 -0
  137. package/dist/builtin/web-access/content-tools.ts +8 -3
  138. package/dist/builtin/web-access/index-heavy.ts +1 -1
  139. package/dist/builtin/web-access/index.ts +190 -46
  140. package/dist/builtin/web-access/lifecycle-lease.ts +38 -0
  141. package/dist/builtin/web-access/package.json +2 -2
  142. package/dist/builtin/web-access/result-renderers.ts +1 -1
  143. package/dist/builtin/web-access/summary-review.ts +1 -1
  144. package/dist/builtin/web-access/web-search-activity.ts +1 -1
  145. package/dist/builtin/web-access/web-search-return.ts +7 -0
  146. package/dist/builtin/web-access/web-search-summary.ts +1 -1
  147. package/dist/builtin/web-access/web-search-tool.ts +4 -2
  148. package/dist/builtin/workflows/CHANGELOG.md +86 -0
  149. package/dist/builtin/workflows/README.md +5 -5
  150. package/dist/builtin/workflows/builtin/deep-research-codebase-utils.ts +27 -4
  151. package/dist/builtin/workflows/builtin/goal-runner.ts +86 -27
  152. package/dist/builtin/workflows/builtin/open-claude-design-runner.ts +10 -0
  153. package/dist/builtin/workflows/builtin/ralph-core.ts +9 -9
  154. package/dist/builtin/workflows/builtin/ralph-models.ts +71 -36
  155. package/dist/builtin/workflows/builtin/ralph-runner.ts +20 -31
  156. package/dist/builtin/workflows/package.json +2 -2
  157. package/dist/builtin/workflows/skills/impeccable/SKILL.md +16 -13
  158. package/dist/builtin/workflows/skills/impeccable/reference/adapt.md +1 -0
  159. package/dist/builtin/workflows/skills/impeccable/reference/adapt.native.md +58 -0
  160. package/dist/builtin/workflows/skills/impeccable/reference/android.md +40 -0
  161. package/dist/builtin/workflows/skills/impeccable/reference/animate.md +2 -0
  162. package/dist/builtin/workflows/skills/impeccable/reference/audit.md +2 -0
  163. package/dist/builtin/workflows/skills/impeccable/reference/audit.native.md +139 -0
  164. package/dist/builtin/workflows/skills/impeccable/reference/bolder.md +69 -62
  165. package/dist/builtin/workflows/skills/impeccable/reference/critique.md +21 -7
  166. package/dist/builtin/workflows/skills/impeccable/reference/document.md +1 -1
  167. package/dist/builtin/workflows/skills/impeccable/reference/hooks.md +92 -0
  168. package/dist/builtin/workflows/skills/impeccable/reference/init.md +29 -7
  169. package/dist/builtin/workflows/skills/impeccable/reference/ios.md +45 -0
  170. package/dist/builtin/workflows/skills/impeccable/reference/layout.md +25 -1
  171. package/dist/builtin/workflows/skills/impeccable/reference/typeset.md +23 -1
  172. package/dist/builtin/workflows/skills/impeccable/scripts/context-signals.mjs +2 -1
  173. package/dist/builtin/workflows/skills/impeccable/scripts/context.mjs +75 -14
  174. package/dist/builtin/workflows/skills/impeccable/scripts/detector/browser/injected/index.mjs +1 -1
  175. package/dist/builtin/workflows/skills/impeccable/scripts/detector/cli/main.mjs +31 -0
  176. package/dist/builtin/workflows/skills/impeccable/scripts/detector/design-system.mjs +64 -0
  177. package/dist/builtin/workflows/skills/impeccable/scripts/detector/detect-antipatterns-browser.js +118 -11
  178. package/dist/builtin/workflows/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs +16 -11
  179. package/dist/builtin/workflows/skills/impeccable/scripts/detector/registry/antipatterns.mjs +66 -0
  180. package/dist/builtin/workflows/skills/impeccable/scripts/detector/rules/checks.mjs +42 -10
  181. package/dist/builtin/workflows/skills/impeccable/scripts/detector/shared/fonts.mjs +30 -0
  182. package/dist/builtin/workflows/skills/impeccable/scripts/detector/shared/page.mjs +4 -6
  183. package/dist/builtin/workflows/skills/impeccable/scripts/hook-admin.mjs +660 -0
  184. package/dist/builtin/workflows/skills/impeccable/scripts/hook-before-edit.mjs +516 -0
  185. package/dist/builtin/workflows/skills/impeccable/scripts/hook-lib.mjs +1764 -0
  186. package/dist/builtin/workflows/skills/impeccable/scripts/hook.mjs +61 -0
  187. package/dist/builtin/workflows/skills/impeccable/scripts/lib/impeccable-config.mjs +4 -2
  188. package/dist/builtin/workflows/skills/impeccable/scripts/lib/is-generated.mjs +2 -2
  189. package/dist/builtin/workflows/skills/impeccable/scripts/live/svelte-component.mjs +13 -16
  190. package/dist/builtin/workflows/skills/impeccable/scripts/live-accept.mjs +17 -11
  191. package/dist/builtin/workflows/skills/impeccable/scripts/live-browser.js +104 -37
  192. package/dist/builtin/workflows/skills/impeccable/scripts/live-wrap.mjs +9 -0
  193. package/dist/builtin/workflows/src/durable/backend.ts +12 -0
  194. package/dist/builtin/workflows/src/durable/dbos-backend.ts +17 -1
  195. package/dist/builtin/workflows/src/durable/resume-catalog.ts +12 -0
  196. package/dist/builtin/workflows/src/durable/resume-runtime.ts +5 -2
  197. package/dist/builtin/workflows/src/durable/stage-primitive.ts +2 -1
  198. package/dist/builtin/workflows/src/durable/types.ts +16 -0
  199. package/dist/builtin/workflows/src/engine/primitives/task.ts +3 -1
  200. package/dist/builtin/workflows/src/engine/run-durable-finalize.ts +1 -2
  201. package/dist/builtin/workflows/src/engine/run-returned-status.ts +50 -16
  202. package/dist/builtin/workflows/src/engine/run.ts +20 -19
  203. package/dist/builtin/workflows/src/engine/runtime.ts +5 -0
  204. package/dist/builtin/workflows/src/extension/extension-factory.ts +3 -0
  205. package/dist/builtin/workflows/src/extension/extension-lifecycle.ts +9 -2
  206. package/dist/builtin/workflows/src/extension/extension-runtime-state.ts +137 -27
  207. package/dist/builtin/workflows/src/extension/lifecycle-notifications.ts +22 -6
  208. package/dist/builtin/workflows/src/extension/public-types.ts +1 -1
  209. package/dist/builtin/workflows/src/extension/workflow-command-completions.ts +13 -1
  210. package/dist/builtin/workflows/src/extension/workflow-command-registration.ts +19 -4
  211. package/dist/builtin/workflows/src/extension/workflow-command-surfaces.ts +9 -0
  212. package/dist/builtin/workflows/src/extension/workflow-prompts.ts +17 -22
  213. package/dist/builtin/workflows/src/extension/workflow-run-control-command.ts +48 -23
  214. package/dist/builtin/workflows/src/extension/workflow-schema.ts +15 -2
  215. package/dist/builtin/workflows/src/extension/workflow-tool-control.ts +20 -8
  216. package/dist/builtin/workflows/src/extension/workflow-tool.ts +24 -8
  217. package/dist/builtin/workflows/src/runs/background/status.ts +4 -3
  218. package/dist/builtin/workflows/src/runs/foreground/executor-direct-helpers.ts +29 -8
  219. package/dist/builtin/workflows/src/runs/foreground/executor-hil.ts +1 -1
  220. package/dist/builtin/workflows/src/runs/foreground/executor-stage-factory.ts +3 -2
  221. package/dist/builtin/workflows/src/runs/shared/model-fallback-candidates.ts +1 -1
  222. package/dist/builtin/workflows/src/runs/shared/worktree-git.ts +155 -20
  223. package/dist/builtin/workflows/src/runs/shared/worktree-types.ts +6 -0
  224. package/dist/builtin/workflows/src/runs/shared/worktree.ts +2 -1
  225. package/dist/builtin/workflows/src/shared/authoring-contract-stage.d.ts +2 -2
  226. package/dist/builtin/workflows/src/shared/authoring-contract-stage.ts +2 -2
  227. package/dist/builtin/workflows/src/shared/resume-continuation.ts +2 -0
  228. package/dist/builtin/workflows/src/shared/returned-run-status.ts +116 -0
  229. package/dist/builtin/workflows/src/shared/workflow-failures-decisions.ts +2 -0
  230. package/dist/builtin/workflows/src/tui/graph-view-render-helpers.ts +21 -3
  231. package/dist/builtin/workflows/src/tui/graph-view-state.ts +3 -0
  232. package/dist/builtin/workflows/src/tui/graph-view-types.ts +3 -0
  233. package/dist/builtin/workflows/src/tui/stage-chat-view-archive-history.ts +1 -23
  234. package/dist/builtin/workflows/src/tui/stage-chat-view-footer-status.ts +54 -9
  235. package/dist/builtin/workflows/src/tui/stage-chat-view.ts +5 -2
  236. package/dist/builtin/workflows/src/tui/status-list.ts +22 -15
  237. package/dist/builtin/workflows/src/tui/workflow-attach-pane.ts +4 -4
  238. package/dist/cli/args.d.ts.map +1 -1
  239. package/dist/cli/args.js +2 -2
  240. package/dist/cli/args.js.map +1 -1
  241. package/dist/core/agent-session-auto-compaction.d.ts +23 -3
  242. package/dist/core/agent-session-auto-compaction.d.ts.map +1 -1
  243. package/dist/core/agent-session-auto-compaction.js +129 -17
  244. package/dist/core/agent-session-auto-compaction.js.map +1 -1
  245. package/dist/core/agent-session-events.d.ts.map +1 -1
  246. package/dist/core/agent-session-events.js +9 -1
  247. package/dist/core/agent-session-events.js.map +1 -1
  248. package/dist/core/agent-session-message-queue.d.ts.map +1 -1
  249. package/dist/core/agent-session-message-queue.js +1 -1
  250. package/dist/core/agent-session-message-queue.js.map +1 -1
  251. package/dist/core/agent-session-methods.d.ts +6 -3
  252. package/dist/core/agent-session-methods.d.ts.map +1 -1
  253. package/dist/core/agent-session-methods.js.map +1 -1
  254. package/dist/core/agent-session-prompt.d.ts.map +1 -1
  255. package/dist/core/agent-session-prompt.js +2 -2
  256. package/dist/core/agent-session-prompt.js.map +1 -1
  257. package/dist/core/agent-session-retry.d.ts.map +1 -1
  258. package/dist/core/agent-session-retry.js +1 -1
  259. package/dist/core/agent-session-retry.js.map +1 -1
  260. package/dist/core/agent-session-services.d.ts.map +1 -1
  261. package/dist/core/agent-session-services.js +3 -2
  262. package/dist/core/agent-session-services.js.map +1 -1
  263. package/dist/core/agent-session-types.d.ts.map +1 -1
  264. package/dist/core/agent-session-types.js +1 -1
  265. package/dist/core/agent-session-types.js.map +1 -1
  266. package/dist/core/agent-session.d.ts +4 -2
  267. package/dist/core/agent-session.d.ts.map +1 -1
  268. package/dist/core/agent-session.js +4 -2
  269. package/dist/core/agent-session.js.map +1 -1
  270. package/dist/core/anthropic-thinking-guard.d.ts +5 -5
  271. package/dist/core/anthropic-thinking-guard.d.ts.map +1 -1
  272. package/dist/core/anthropic-thinking-guard.js +42 -32
  273. package/dist/core/anthropic-thinking-guard.js.map +1 -1
  274. package/dist/core/atomic-guide-command.d.ts.map +1 -1
  275. package/dist/core/atomic-guide-command.js +15 -15
  276. package/dist/core/atomic-guide-command.js.map +1 -1
  277. package/dist/core/auth-storage.d.ts.map +1 -1
  278. package/dist/core/auth-storage.js +14 -9
  279. package/dist/core/auth-storage.js.map +1 -1
  280. package/dist/core/compaction/compaction.d.ts +2 -0
  281. package/dist/core/compaction/compaction.d.ts.map +1 -1
  282. package/dist/core/compaction/compaction.js +79 -46
  283. package/dist/core/compaction/compaction.js.map +1 -1
  284. package/dist/core/compaction/context-assistant-turns.d.ts +42 -0
  285. package/dist/core/compaction/context-assistant-turns.d.ts.map +1 -0
  286. package/dist/core/compaction/context-assistant-turns.js +87 -0
  287. package/dist/core/compaction/context-assistant-turns.js.map +1 -0
  288. package/dist/core/compaction/context-compaction-critical.d.ts +1 -1
  289. package/dist/core/compaction/context-compaction-critical.d.ts.map +1 -1
  290. package/dist/core/compaction/context-compaction-critical.js +2 -2
  291. package/dist/core/compaction/context-compaction-critical.js.map +1 -1
  292. package/dist/core/compaction/context-compaction-eviction-alternates.d.ts +18 -0
  293. package/dist/core/compaction/context-compaction-eviction-alternates.d.ts.map +1 -0
  294. package/dist/core/compaction/context-compaction-eviction-alternates.js +186 -0
  295. package/dist/core/compaction/context-compaction-eviction-alternates.js.map +1 -0
  296. package/dist/core/compaction/context-compaction-eviction.d.ts +10 -2
  297. package/dist/core/compaction/context-compaction-eviction.d.ts.map +1 -1
  298. package/dist/core/compaction/context-compaction-eviction.js +196 -146
  299. package/dist/core/compaction/context-compaction-eviction.js.map +1 -1
  300. package/dist/core/compaction/context-compaction-prompt.d.ts.map +1 -1
  301. package/dist/core/compaction/context-compaction-prompt.js +1 -1
  302. package/dist/core/compaction/context-compaction-prompt.js.map +1 -1
  303. package/dist/core/compaction/context-compaction-types.d.ts +2 -0
  304. package/dist/core/compaction/context-compaction-types.d.ts.map +1 -1
  305. package/dist/core/compaction/context-compaction-types.js.map +1 -1
  306. package/dist/core/compaction/context-deletion-application.d.ts +5 -4
  307. package/dist/core/compaction/context-deletion-application.d.ts.map +1 -1
  308. package/dist/core/compaction/context-deletion-application.js +17 -15
  309. package/dist/core/compaction/context-deletion-application.js.map +1 -1
  310. package/dist/core/compaction/context-deletion-store.d.ts.map +1 -1
  311. package/dist/core/compaction/context-deletion-store.js +1 -1
  312. package/dist/core/compaction/context-deletion-store.js.map +1 -1
  313. package/dist/core/compaction/context-deletion-targets.d.ts +2 -1
  314. package/dist/core/compaction/context-deletion-targets.d.ts.map +1 -1
  315. package/dist/core/compaction/context-deletion-targets.js +36 -31
  316. package/dist/core/compaction/context-deletion-targets.js.map +1 -1
  317. package/dist/core/compaction/context-deletion-tool-helpers.d.ts.map +1 -1
  318. package/dist/core/compaction/context-deletion-tool-helpers.js +116 -43
  319. package/dist/core/compaction/context-deletion-tool-helpers.js.map +1 -1
  320. package/dist/core/compaction/context-transcript-analysis.d.ts +1 -2
  321. package/dist/core/compaction/context-transcript-analysis.d.ts.map +1 -1
  322. package/dist/core/compaction/context-transcript-analysis.js +33 -37
  323. package/dist/core/compaction/context-transcript-analysis.js.map +1 -1
  324. package/dist/core/copilot-model-synthesis.d.ts.map +1 -1
  325. package/dist/core/copilot-model-synthesis.js +3 -1
  326. package/dist/core/copilot-model-synthesis.js.map +1 -1
  327. package/dist/core/extensions/loader-virtual-modules.d.ts +8 -1
  328. package/dist/core/extensions/loader-virtual-modules.d.ts.map +1 -1
  329. package/dist/core/extensions/loader-virtual-modules.js +2 -1
  330. package/dist/core/extensions/loader-virtual-modules.js.map +1 -1
  331. package/dist/core/extensions/provider-types.d.ts +2 -7
  332. package/dist/core/extensions/provider-types.d.ts.map +1 -1
  333. package/dist/core/extensions/provider-types.js.map +1 -1
  334. package/dist/core/footer-data-provider.d.ts +6 -0
  335. package/dist/core/footer-data-provider.d.ts.map +1 -1
  336. package/dist/core/footer-data-provider.js +72 -29
  337. package/dist/core/footer-data-provider.js.map +1 -1
  338. package/dist/core/messages.d.ts +9 -0
  339. package/dist/core/messages.d.ts.map +1 -1
  340. package/dist/core/messages.js +100 -18
  341. package/dist/core/messages.js.map +1 -1
  342. package/dist/core/model-registry-builtins.d.ts.map +1 -1
  343. package/dist/core/model-registry-builtins.js +5 -0
  344. package/dist/core/model-registry-builtins.js.map +1 -1
  345. package/dist/core/model-registry-custom-loader.d.ts.map +1 -1
  346. package/dist/core/model-registry-custom-loader.js +49 -8
  347. package/dist/core/model-registry-custom-loader.js.map +1 -1
  348. package/dist/core/model-registry-dynamic.d.ts.map +1 -1
  349. package/dist/core/model-registry-dynamic.js +11 -5
  350. package/dist/core/model-registry-dynamic.js.map +1 -1
  351. package/dist/core/model-registry-loader.d.ts.map +1 -1
  352. package/dist/core/model-registry-loader.js +8 -0
  353. package/dist/core/model-registry-loader.js.map +1 -1
  354. package/dist/core/model-registry-schemas.d.ts +93 -8
  355. package/dist/core/model-registry-schemas.d.ts.map +1 -1
  356. package/dist/core/model-registry-schemas.js +24 -12
  357. package/dist/core/model-registry-schemas.js.map +1 -1
  358. package/dist/core/model-registry-types.d.ts +7 -6
  359. package/dist/core/model-registry-types.d.ts.map +1 -1
  360. package/dist/core/model-registry-types.js.map +1 -1
  361. package/dist/core/model-registry.d.ts +5 -0
  362. package/dist/core/model-registry.d.ts.map +1 -1
  363. package/dist/core/model-registry.js +23 -0
  364. package/dist/core/model-registry.js.map +1 -1
  365. package/dist/core/model-resolver-cli.d.ts.map +1 -1
  366. package/dist/core/model-resolver-cli.js +33 -5
  367. package/dist/core/model-resolver-cli.js.map +1 -1
  368. package/dist/core/model-resolver-initial.d.ts +6 -1
  369. package/dist/core/model-resolver-initial.d.ts.map +1 -1
  370. package/dist/core/model-resolver-initial.js +12 -7
  371. package/dist/core/model-resolver-initial.js.map +1 -1
  372. package/dist/core/model-resolver.d.ts +1 -1
  373. package/dist/core/model-resolver.d.ts.map +1 -1
  374. package/dist/core/model-resolver.js +1 -1
  375. package/dist/core/model-resolver.js.map +1 -1
  376. package/dist/core/openai-responses-payload-sanitizer.d.ts +1 -0
  377. package/dist/core/openai-responses-payload-sanitizer.d.ts.map +1 -1
  378. package/dist/core/openai-responses-payload-sanitizer.js +24 -9
  379. package/dist/core/openai-responses-payload-sanitizer.js.map +1 -1
  380. package/dist/core/resource-loader-context-files.d.ts +1 -0
  381. package/dist/core/resource-loader-context-files.d.ts.map +1 -1
  382. package/dist/core/resource-loader-context-files.js +13 -10
  383. package/dist/core/resource-loader-context-files.js.map +1 -1
  384. package/dist/core/sdk.d.ts.map +1 -1
  385. package/dist/core/sdk.js +2 -2
  386. package/dist/core/sdk.js.map +1 -1
  387. package/dist/core/session-entry-normalization.d.ts +7 -0
  388. package/dist/core/session-entry-normalization.d.ts.map +1 -0
  389. package/dist/core/session-entry-normalization.js +14 -0
  390. package/dist/core/session-entry-normalization.js.map +1 -0
  391. package/dist/core/session-manager-core.d.ts.map +1 -1
  392. package/dist/core/session-manager-core.js +1 -0
  393. package/dist/core/session-manager-core.js.map +1 -1
  394. package/dist/core/session-manager-history.d.ts +2 -2
  395. package/dist/core/session-manager-history.d.ts.map +1 -1
  396. package/dist/core/session-manager-history.js +127 -108
  397. package/dist/core/session-manager-history.js.map +1 -1
  398. package/dist/core/settings-manager-basic-accessors.d.ts +2 -2
  399. package/dist/core/settings-manager-basic-accessors.d.ts.map +1 -1
  400. package/dist/core/settings-manager-basic-accessors.js.map +1 -1
  401. package/dist/core/settings-types.d.ts +1 -1
  402. package/dist/core/settings-types.d.ts.map +1 -1
  403. package/dist/core/settings-types.js.map +1 -1
  404. package/dist/core/slash-commands.d.ts +2 -0
  405. package/dist/core/slash-commands.d.ts.map +1 -1
  406. package/dist/core/slash-commands.js +141 -0
  407. package/dist/core/slash-commands.js.map +1 -1
  408. package/dist/core/tools/bash.d.ts.map +1 -1
  409. package/dist/core/tools/bash.js +11 -8
  410. package/dist/core/tools/bash.js.map +1 -1
  411. package/dist/index.d.ts +1 -0
  412. package/dist/index.d.ts.map +1 -1
  413. package/dist/index.js +1 -0
  414. package/dist/index.js.map +1 -1
  415. package/dist/main-deferred-startup.d.ts.map +1 -1
  416. package/dist/main-deferred-startup.js +2 -2
  417. package/dist/main-deferred-startup.js.map +1 -1
  418. package/dist/main-early-input.d.ts.map +1 -1
  419. package/dist/main-early-input.js +7 -0
  420. package/dist/main-early-input.js.map +1 -1
  421. package/dist/main-session.d.ts.map +1 -1
  422. package/dist/main-session.js +1 -0
  423. package/dist/main-session.js.map +1 -1
  424. package/dist/main.d.ts.map +1 -1
  425. package/dist/main.js +1 -1
  426. package/dist/main.js.map +1 -1
  427. package/dist/modes/interactive/components/chat-session-host-actions.d.ts.map +1 -1
  428. package/dist/modes/interactive/components/chat-session-host-actions.js +0 -1
  429. package/dist/modes/interactive/components/chat-session-host-actions.js.map +1 -1
  430. package/dist/modes/interactive/components/settings-selector-options.d.ts.map +1 -1
  431. package/dist/modes/interactive/components/settings-selector-options.js +3 -1
  432. package/dist/modes/interactive/components/settings-selector-options.js.map +1 -1
  433. package/dist/modes/interactive/components/thinking-selector.d.ts.map +1 -1
  434. package/dist/modes/interactive/components/thinking-selector.js +2 -1
  435. package/dist/modes/interactive/components/thinking-selector.js.map +1 -1
  436. package/dist/modes/interactive/interactive-agent-events.d.ts.map +1 -1
  437. package/dist/modes/interactive/interactive-agent-events.js +3 -1
  438. package/dist/modes/interactive/interactive-agent-events.js.map +1 -1
  439. package/dist/modes/interactive/interactive-autocomplete.d.ts.map +1 -1
  440. package/dist/modes/interactive/interactive-autocomplete.js +21 -9
  441. package/dist/modes/interactive/interactive-autocomplete.js.map +1 -1
  442. package/dist/modes/interactive/interactive-child-ordering.d.ts +7 -0
  443. package/dist/modes/interactive/interactive-child-ordering.d.ts.map +1 -0
  444. package/dist/modes/interactive/interactive-child-ordering.js +26 -0
  445. package/dist/modes/interactive/interactive-child-ordering.js.map +1 -0
  446. package/dist/modes/interactive/interactive-deferred-startup.d.ts.map +1 -1
  447. package/dist/modes/interactive/interactive-deferred-startup.js +43 -30
  448. package/dist/modes/interactive/interactive-deferred-startup.js.map +1 -1
  449. package/dist/modes/interactive/interactive-hotkeys-debug.d.ts.map +1 -1
  450. package/dist/modes/interactive/interactive-hotkeys-debug.js +1 -0
  451. package/dist/modes/interactive/interactive-hotkeys-debug.js.map +1 -1
  452. package/dist/modes/interactive/interactive-input-handling.d.ts.map +1 -1
  453. package/dist/modes/interactive/interactive-input-handling.js +41 -6
  454. package/dist/modes/interactive/interactive-input-handling.js.map +1 -1
  455. package/dist/modes/interactive/interactive-mode-base.d.ts +3 -0
  456. package/dist/modes/interactive/interactive-mode-base.d.ts.map +1 -1
  457. package/dist/modes/interactive/interactive-mode-base.js +3 -0
  458. package/dist/modes/interactive/interactive-mode-base.js.map +1 -1
  459. package/dist/modes/interactive/interactive-mode-deps.d.ts +2 -2
  460. package/dist/modes/interactive/interactive-mode-deps.d.ts.map +1 -1
  461. package/dist/modes/interactive/interactive-mode-deps.js +2 -2
  462. package/dist/modes/interactive/interactive-mode-deps.js.map +1 -1
  463. package/dist/modes/interactive/interactive-mode-surface.d.ts +2 -2
  464. package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
  465. package/dist/modes/interactive/interactive-mode-surface.js.map +1 -1
  466. package/dist/modes/interactive/interactive-model-routing.d.ts.map +1 -1
  467. package/dist/modes/interactive/interactive-model-routing.js +11 -4
  468. package/dist/modes/interactive/interactive-model-routing.js.map +1 -1
  469. package/dist/modes/interactive/interactive-process-lifecycle.d.ts.map +1 -1
  470. package/dist/modes/interactive/interactive-process-lifecycle.js +0 -3
  471. package/dist/modes/interactive/interactive-process-lifecycle.js.map +1 -1
  472. package/dist/modes/interactive/interactive-render-chat.d.ts.map +1 -1
  473. package/dist/modes/interactive/interactive-render-chat.js +15 -1
  474. package/dist/modes/interactive/interactive-render-chat.js.map +1 -1
  475. package/dist/modes/interactive/interactive-session-routing.d.ts.map +1 -1
  476. package/dist/modes/interactive/interactive-session-routing.js +5 -1
  477. package/dist/modes/interactive/interactive-session-routing.js.map +1 -1
  478. package/dist/modes/interactive/interactive-startup.d.ts.map +1 -1
  479. package/dist/modes/interactive/interactive-startup.js +20 -21
  480. package/dist/modes/interactive/interactive-startup.js.map +1 -1
  481. package/dist/modes/interactive/theme/global-theme.d.ts.map +1 -1
  482. package/dist/modes/interactive/theme/global-theme.js +26 -2
  483. package/dist/modes/interactive/theme/global-theme.js.map +1 -1
  484. package/dist/modes/interactive/theme/theme-class.d.ts +1 -1
  485. package/dist/modes/interactive/theme/theme-class.d.ts.map +1 -1
  486. package/dist/modes/interactive/theme/theme-class.js +2 -0
  487. package/dist/modes/interactive/theme/theme-class.js.map +1 -1
  488. package/dist/utils/clipboard-image.d.ts.map +1 -1
  489. package/dist/utils/clipboard-image.js +3 -0
  490. package/dist/utils/clipboard-image.js.map +1 -1
  491. package/dist/utils/fs-watch.d.ts +24 -1
  492. package/dist/utils/fs-watch.d.ts.map +1 -1
  493. package/dist/utils/fs-watch.js +60 -5
  494. package/dist/utils/fs-watch.js.map +1 -1
  495. package/docs/changelog.mdx +19 -0
  496. package/docs/compaction.md +60 -49
  497. package/docs/custom-provider.md +18 -5
  498. package/docs/development.md +4 -0
  499. package/docs/extensions.md +11 -2
  500. package/docs/json.md +1 -1
  501. package/docs/models.md +69 -10
  502. package/docs/providers.md +1 -1
  503. package/docs/quickstart.md +12 -8
  504. package/docs/rpc.md +2 -2
  505. package/docs/sdk.md +1 -1
  506. package/docs/settings.md +4 -4
  507. package/docs/subagents.md +40 -5
  508. package/docs/tools.md +1 -1
  509. package/docs/usage.md +4 -2
  510. package/docs/windows.md +4 -0
  511. package/docs/workflows.md +33 -48
  512. package/examples/extensions/preset.ts +2 -2
  513. package/examples/extensions/subagent/README.md +2 -2
  514. package/examples/extensions/subagent/index.ts +1 -2
  515. package/examples/extensions/subagent/schemas.ts +4 -1
  516. package/examples/sdk/12-full-control.ts +1 -1
  517. package/npm-shrinkwrap.json +48 -51
  518. package/package.json +7 -7
  519. package/dist/builtin/subagents/src/runs/foreground/chain-clarify-behavior.ts +0 -75
  520. package/dist/builtin/subagents/src/runs/foreground/chain-clarify-component.ts +0 -202
  521. package/dist/builtin/subagents/src/runs/foreground/chain-clarify-edit.ts +0 -97
  522. package/dist/builtin/subagents/src/runs/foreground/chain-clarify-editor.ts +0 -160
  523. package/dist/builtin/subagents/src/runs/foreground/chain-clarify-frame.ts +0 -72
  524. package/dist/builtin/subagents/src/runs/foreground/chain-clarify-render-modes.ts +0 -161
  525. package/dist/builtin/subagents/src/runs/foreground/chain-clarify-render-selectors.ts +0 -203
  526. package/dist/builtin/subagents/src/runs/foreground/chain-clarify-selectors.ts +0 -234
  527. package/dist/builtin/subagents/src/runs/foreground/chain-clarify-state.ts +0 -103
  528. package/dist/builtin/subagents/src/runs/foreground/chain-clarify-types.ts +0 -29
  529. package/dist/builtin/subagents/src/runs/foreground/chain-clarify.ts +0 -9
  530. package/dist/builtin/subagents/src/runs/foreground/chain-execution-clarify.ts +0 -117
@@ -1,6 +1,7 @@
1
1
  /**
2
2
  * Neutral context-usage metrics for deciding when a session needs compaction.
3
3
  */
4
+ import { messageIsLlmVisible, userLikeContentBlockIsLlmVisible } from "../messages.js";
4
5
  export const DEFAULT_COMPACTION_SETTINGS = {
5
6
  enabled: true,
6
7
  reserveTokens: 16384,
@@ -58,12 +59,17 @@ export function getLastAssistantUsage(entries) {
58
59
  return undefined;
59
60
  }
60
61
  function getLastAssistantUsageInfo(messages) {
61
- for (let i = messages.length - 1; i >= 0; i--) {
62
- const usage = getAssistantUsage(messages[i]);
63
- if (usage)
64
- return { usage, index: i };
62
+ let latestPrefixTimestamp = Number.NEGATIVE_INFINITY;
63
+ let usageInfo;
64
+ for (let i = 0; i < messages.length; i++) {
65
+ const message = messages[i];
66
+ const usage = getAssistantUsage(message);
67
+ if (usage && message.timestamp >= latestPrefixTimestamp && calculateContextTokens(usage) > 0) {
68
+ usageInfo = { usage, index: i };
69
+ }
70
+ latestPrefixTimestamp = Math.max(latestPrefixTimestamp, message.timestamp);
65
71
  }
66
- return undefined;
72
+ return usageInfo;
67
73
  }
68
74
  /**
69
75
  * Estimate context tokens from messages, using the last assistant usage when available.
@@ -114,20 +120,54 @@ export function shouldCompact(contextTokens, contextWindow, settings) {
114
120
  */
115
121
  export const ESTIMATED_IMAGE_CHARS = 4800;
116
122
  export const ESTIMATED_IMAGE_TOKENS = Math.ceil(ESTIMATED_IMAGE_CHARS / 4);
117
- function estimateTextAndImageContentChars(content) {
118
- if (typeof content === "string") {
119
- return content.length;
123
+ function safeSerializedPayloadLength(value, fallback) {
124
+ try {
125
+ const serialized = JSON.stringify(value);
126
+ if (typeof serialized === "string")
127
+ return serialized.length;
120
128
  }
121
- let chars = 0;
122
- for (const block of content) {
123
- if (block.type === "text" && block.text) {
124
- chars += block.text.length;
125
- }
126
- else if (block.type === "image") {
127
- chars += ESTIMATED_IMAGE_CHARS;
128
- }
129
+ catch {
130
+ // Cyclic and otherwise unserializable future blocks use a conservative marker below.
129
131
  }
130
- return chars;
132
+ return fallback.length;
133
+ }
134
+ function nonEmptyBlockType(block) {
135
+ if (!block || typeof block !== "object")
136
+ return undefined;
137
+ const type = block.type;
138
+ return typeof type === "string" && type.trim().length > 0 ? type : undefined;
139
+ }
140
+ function estimateContentBlockChars(block) {
141
+ const type = nonEmptyBlockType(block);
142
+ if (!type)
143
+ return 0;
144
+ const record = block;
145
+ if (type === "image")
146
+ return ESTIMATED_IMAGE_CHARS;
147
+ if (type === "text")
148
+ return typeof record.text === "string" ? record.text.length : 0;
149
+ if (type === "thinking")
150
+ return typeof record.thinking === "string" ? record.thinking.length : 0;
151
+ if (type === "toolCall") {
152
+ const nameLength = typeof record.name === "string" ? record.name.length : 0;
153
+ return nameLength + safeSerializedPayloadLength(record.arguments, "[unserializable tool arguments]");
154
+ }
155
+ return safeSerializedPayloadLength(block, `[unserializable ${type} block]`);
156
+ }
157
+ /** Estimate one raw content block after the caller applies provider visibility. */
158
+ export function estimateContentBlockTokens(block, visible = true) {
159
+ if (!visible)
160
+ return 0;
161
+ const chars = estimateContentBlockChars(block);
162
+ return chars > 0 ? Math.ceil(chars / 4) : 0;
163
+ }
164
+ function estimateUserLikeContentTokens(content) {
165
+ if (typeof content === "string")
166
+ return content.trim().length > 0 ? Math.ceil(content.length / 4) : 0;
167
+ if (!Array.isArray(content))
168
+ return 0;
169
+ const chars = content.reduce((total, block) => total + (userLikeContentBlockIsLlmVisible(block) ? estimateContentBlockChars(block) : 0), 0);
170
+ return chars > 0 ? Math.ceil(chars / 4) : 0;
131
171
  }
132
172
  /**
133
173
  * Count image content blocks in a message content array (text or block array).
@@ -136,11 +176,11 @@ function estimateTextAndImageContentChars(content) {
136
176
  * heuristic independently of the transcript-based estimation used in production.
137
177
  */
138
178
  export function countImageContentBlocks(content) {
139
- if (typeof content === "string")
179
+ if (typeof content === "string" || !Array.isArray(content))
140
180
  return 0;
141
181
  let count = 0;
142
182
  for (const block of content) {
143
- if (block.type === "image")
183
+ if (block && typeof block === "object" && block.type === "image")
144
184
  count += 1;
145
185
  }
146
186
  return count;
@@ -159,40 +199,33 @@ export function estimateImageContentTokens(content) {
159
199
  * This is conservative (overestimates tokens).
160
200
  */
161
201
  export function estimateTokens(message) {
162
- let chars = 0;
202
+ if (!message || typeof message !== "object" || !messageIsLlmVisible(message))
203
+ return 0;
163
204
  switch (message.role) {
164
- case "user": {
165
- chars = estimateTextAndImageContentChars(message.content);
166
- return Math.ceil(chars / 4);
167
- }
205
+ case "user":
206
+ case "custom":
207
+ return estimateUserLikeContentTokens(message.content);
168
208
  case "assistant": {
169
- const assistant = message;
170
- for (const block of assistant.content) {
171
- if (block.type === "text") {
172
- chars += block.text.length;
173
- }
174
- else if (block.type === "thinking") {
175
- chars += block.thinking.length;
176
- }
177
- else if (block.type === "toolCall") {
178
- chars += block.name.length + JSON.stringify(block.arguments).length;
179
- }
180
- }
181
- return Math.ceil(chars / 4);
209
+ const content = message.content;
210
+ if (!Array.isArray(content))
211
+ return 0;
212
+ const chars = content.reduce((total, block) => total + estimateContentBlockChars(block), 0);
213
+ return chars > 0 ? Math.ceil(chars / 4) : 0;
182
214
  }
183
- case "custom":
184
215
  case "toolResult": {
185
- chars = estimateTextAndImageContentChars(message.content);
186
- return Math.ceil(chars / 4);
216
+ const content = message.content;
217
+ if (!Array.isArray(content))
218
+ return 0;
219
+ const chars = content.reduce((total, block) => total + estimateContentBlockChars(block), 0);
220
+ return chars > 0 ? Math.ceil(chars / 4) : 0;
187
221
  }
188
222
  case "bashExecution": {
189
- chars = message.command.length + message.output.length;
190
- return Math.ceil(chars / 4);
191
- }
192
- case "branchSummary": {
193
- chars = message.summary.length;
194
- return Math.ceil(chars / 4);
223
+ const command = typeof message.command === "string" ? message.command : "";
224
+ const output = typeof message.output === "string" ? message.output : "";
225
+ return Math.ceil((command.length + output.length) / 4);
195
226
  }
227
+ case "branchSummary":
228
+ return typeof message.summary === "string" ? Math.ceil(message.summary.length / 4) : 0;
196
229
  }
197
230
  return 0;
198
231
  }
@@ -1 +1 @@
1
- {"version":3,"file":"compaction.js","sourceRoot":"","sources":["../../../src/core/compaction/compaction.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Neutral context-usage metrics for deciding when a session needs compaction.\n */\n\nimport type { AgentMessage } from \"@earendil-works/pi-agent-core\";\nimport type { AssistantMessage, Usage } from \"@earendil-works/pi-ai/compat\";\nimport type { SessionEntry } from \"../session-manager.ts\";\n\nexport interface CompactionSettings {\n\tenabled: boolean;\n\treserveTokens: number;\n\t/** Fraction of compactable context to keep. 0.3 is aggressive, 0.7 is light. */\n\tcompression_ratio: number;\n\t/** Number of recent context-eligible messages to preserve in standard mode. */\n\tpreserve_recent: number;\n\t/** Focus query for relevance-based pruning; auto-detected when omitted in settings/options. */\n\tquery?: string;\n}\n\nexport const DEFAULT_COMPACTION_SETTINGS: CompactionSettings = {\n\tenabled: true,\n\treserveTokens: 16384,\n\tcompression_ratio: 0.5,\n\tpreserve_recent: 2,\n};\n\n/**\n * Calculate active context-window tokens from provider usage.\n *\n * Prefer normalized component fields over `totalTokens`: some providers expose\n * `totalTokens` as a billing/cumulative total, while the footer needs an active\n * context estimate. Anthropic-compatible endpoints can also mirror cached input\n * in both `input` and `cacheRead`/`cacheWrite`; when cache buckets are nearly the\n * same size as `input`, treat `input` as the full prompt instead of counting the\n * same cached prompt twice.\n */\nexport function calculateContextTokens(usage: Usage): number {\n\tconst input = Math.max(0, usage.input || 0);\n\tconst output = Math.max(0, usage.output || 0);\n\tconst cacheRead = Math.max(0, usage.cacheRead || 0);\n\tconst cacheWrite = Math.max(0, usage.cacheWrite || 0);\n\tconst cacheTokens = cacheRead + cacheWrite;\n\tconst hasComponents = input > 0 || output > 0 || cacheTokens > 0;\n\tif (!hasComponents) return Math.max(0, usage.totalTokens || 0);\n\n\tconst cacheMirrorsInput = input > 0 && cacheTokens > 0 && cacheTokens >= input * 0.9 && cacheTokens <= input * 1.1;\n\tconst promptTokens = cacheMirrorsInput ? input : input + cacheTokens;\n\treturn promptTokens + output;\n}\n\n/**\n * Get usage from an assistant message if available.\n * Skips aborted and error messages as they don't have valid usage data.\n */\nfunction getAssistantUsage(msg: AgentMessage): Usage | undefined {\n\tif (msg.role === \"assistant\" && \"usage\" in msg) {\n\t\tconst assistantMsg = msg as AssistantMessage;\n\t\tif (assistantMsg.stopReason !== \"aborted\" && assistantMsg.stopReason !== \"error\" && assistantMsg.usage) {\n\t\t\treturn assistantMsg.usage;\n\t\t}\n\t}\n\treturn undefined;\n}\n\n/**\n * Find the last non-aborted assistant message usage from session entries.\n */\nexport function getLastAssistantUsage(entries: SessionEntry[]): Usage | undefined {\n\tfor (let i = entries.length - 1; i >= 0; i--) {\n\t\tconst entry = entries[i];\n\t\tif (entry.type === \"message\") {\n\t\t\tconst usage = getAssistantUsage(entry.message);\n\t\t\tif (usage) return usage;\n\t\t}\n\t}\n\treturn undefined;\n}\n\nexport interface ContextUsageEstimate {\n\ttokens: number;\n\tusageTokens: number;\n\ttrailingTokens: number;\n\tlastUsageIndex: number | null;\n}\n\nfunction getLastAssistantUsageInfo(messages: AgentMessage[]): { usage: Usage; index: number } | undefined {\n\tfor (let i = messages.length - 1; i >= 0; i--) {\n\t\tconst usage = getAssistantUsage(messages[i]);\n\t\tif (usage) return { usage, index: i };\n\t}\n\treturn undefined;\n}\n\n/**\n * Estimate context tokens from messages, using the last assistant usage when available.\n * If there are messages after the last usage, estimate their tokens with estimateTokens.\n */\nexport function estimateContextTokens(messages: AgentMessage[]): ContextUsageEstimate {\n\tconst usageInfo = getLastAssistantUsageInfo(messages);\n\n\tif (!usageInfo) {\n\t\tlet estimated = 0;\n\t\tfor (const message of messages) {\n\t\t\testimated += estimateTokens(message);\n\t\t}\n\t\treturn {\n\t\t\ttokens: estimated,\n\t\t\tusageTokens: 0,\n\t\t\ttrailingTokens: estimated,\n\t\t\tlastUsageIndex: null,\n\t\t};\n\t}\n\n\tconst usageTokens = calculateContextTokens(usageInfo.usage);\n\tlet trailingTokens = 0;\n\tfor (let i = usageInfo.index + 1; i < messages.length; i++) {\n\t\ttrailingTokens += estimateTokens(messages[i]);\n\t}\n\n\treturn {\n\t\ttokens: usageTokens + trailingTokens,\n\t\tusageTokens,\n\t\ttrailingTokens,\n\t\tlastUsageIndex: usageInfo.index,\n\t};\n}\n\n/**\n * Check if compaction should trigger based on context usage.\n */\nexport function shouldCompact(contextTokens: number, contextWindow: number, settings: CompactionSettings): boolean {\n\tif (!settings.enabled) return false;\n\treturn contextTokens > contextWindow - settings.reserveTokens;\n}\n\n/**\n * Shared image-token estimation used by every compaction/context-accounting path.\n *\n * Providers fold image tokens into their reported prompt/input usage, so usage-based\n * accounting already captures actual image cost. For heuristic paths (trailing\n * messages without usage, transcript content-block estimates) a single conservative\n * fixed estimate keeps both the context-window threshold check and the transcript\n * planner consistent.\n */\nexport const ESTIMATED_IMAGE_CHARS = 4800;\nexport const ESTIMATED_IMAGE_TOKENS = Math.ceil(ESTIMATED_IMAGE_CHARS / 4);\n\nfunction estimateTextAndImageContentChars(content: string | Array<{ type: string; text?: string }>): number {\n\tif (typeof content === \"string\") {\n\t\treturn content.length;\n\t}\n\n\tlet chars = 0;\n\tfor (const block of content) {\n\t\tif (block.type === \"text\" && block.text) {\n\t\t\tchars += block.text.length;\n\t\t} else if (block.type === \"image\") {\n\t\t\tchars += ESTIMATED_IMAGE_CHARS;\n\t\t}\n\t}\n\treturn chars;\n}\n\n/**\n * Count image content blocks in a message content array (text or block array).\n *\n * Exported as the canonical image-counting contract so tests can verify the\n * heuristic independently of the transcript-based estimation used in production.\n */\nexport function countImageContentBlocks(content: string | Array<{ type: string }>): number {\n\tif (typeof content === \"string\") return 0;\n\tlet count = 0;\n\tfor (const block of content) {\n\t\tif (block.type === \"image\") count += 1;\n\t}\n\treturn count;\n}\n\n/**\n * Estimate the token cost of only the image content blocks in a message content array.\n *\n * Exported as the canonical image-token-estimation contract so tests can verify\n * the heuristic independently of the transcript-based estimation used in production.\n */\nexport function estimateImageContentTokens(content: string | Array<{ type: string }>): number {\n\treturn countImageContentBlocks(content) * ESTIMATED_IMAGE_TOKENS;\n}\n\n/**\n * Estimate token count for a message using chars/4 heuristic.\n * This is conservative (overestimates tokens).\n */\nexport function estimateTokens(message: AgentMessage): number {\n\tlet chars = 0;\n\n\tswitch (message.role) {\n\t\tcase \"user\": {\n\t\t\tchars = estimateTextAndImageContentChars(\n\t\t\t\t(message as { content: string | Array<{ type: string; text?: string }> }).content,\n\t\t\t);\n\t\t\treturn Math.ceil(chars / 4);\n\t\t}\n\t\tcase \"assistant\": {\n\t\t\tconst assistant = message as AssistantMessage;\n\t\t\tfor (const block of assistant.content) {\n\t\t\t\tif (block.type === \"text\") {\n\t\t\t\t\tchars += block.text.length;\n\t\t\t\t} else if (block.type === \"thinking\") {\n\t\t\t\t\tchars += block.thinking.length;\n\t\t\t\t} else if (block.type === \"toolCall\") {\n\t\t\t\t\tchars += block.name.length + JSON.stringify(block.arguments).length;\n\t\t\t\t}\n\t\t\t}\n\t\t\treturn Math.ceil(chars / 4);\n\t\t}\n\t\tcase \"custom\":\n\t\tcase \"toolResult\": {\n\t\t\tchars = estimateTextAndImageContentChars(message.content);\n\t\t\treturn Math.ceil(chars / 4);\n\t\t}\n\t\tcase \"bashExecution\": {\n\t\t\tchars = message.command.length + message.output.length;\n\t\t\treturn Math.ceil(chars / 4);\n\t\t}\n\t\tcase \"branchSummary\": {\n\t\t\tchars = message.summary.length;\n\t\t\treturn Math.ceil(chars / 4);\n\t\t}\n\t}\n\n\treturn 0;\n}\n"]}
1
+ {"version":3,"file":"compaction.js","sourceRoot":"","sources":["../../../src/core/compaction/compaction.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Neutral context-usage metrics for deciding when a session needs compaction.\n */\n\nimport type { AgentMessage } from \"@earendil-works/pi-agent-core\";\nimport type { AssistantMessage, Usage } from \"@earendil-works/pi-ai/compat\";\nimport type { SessionEntry } from \"../session-manager.ts\";\nimport { messageIsLlmVisible, userLikeContentBlockIsLlmVisible } from \"../messages.ts\";\n\nexport interface CompactionSettings {\n\tenabled: boolean;\n\treserveTokens: number;\n\t/** Fraction of compactable context to keep. 0.3 is aggressive, 0.7 is light. */\n\tcompression_ratio: number;\n\t/** Number of recent context-eligible messages to preserve in standard mode. */\n\tpreserve_recent: number;\n\t/** Focus query for relevance-based pruning; auto-detected when omitted in settings/options. */\n\tquery?: string;\n}\n\nexport const DEFAULT_COMPACTION_SETTINGS: CompactionSettings = {\n\tenabled: true,\n\treserveTokens: 16384,\n\tcompression_ratio: 0.5,\n\tpreserve_recent: 2,\n};\n\n/**\n * Calculate active context-window tokens from provider usage.\n *\n * Prefer normalized component fields over `totalTokens`: some providers expose\n * `totalTokens` as a billing/cumulative total, while the footer needs an active\n * context estimate. Anthropic-compatible endpoints can also mirror cached input\n * in both `input` and `cacheRead`/`cacheWrite`; when cache buckets are nearly the\n * same size as `input`, treat `input` as the full prompt instead of counting the\n * same cached prompt twice.\n */\nexport function calculateContextTokens(usage: Usage): number {\n\tconst input = Math.max(0, usage.input || 0);\n\tconst output = Math.max(0, usage.output || 0);\n\tconst cacheRead = Math.max(0, usage.cacheRead || 0);\n\tconst cacheWrite = Math.max(0, usage.cacheWrite || 0);\n\tconst cacheTokens = cacheRead + cacheWrite;\n\tconst hasComponents = input > 0 || output > 0 || cacheTokens > 0;\n\tif (!hasComponents) return Math.max(0, usage.totalTokens || 0);\n\n\tconst cacheMirrorsInput = input > 0 && cacheTokens > 0 && cacheTokens >= input * 0.9 && cacheTokens <= input * 1.1;\n\tconst promptTokens = cacheMirrorsInput ? input : input + cacheTokens;\n\treturn promptTokens + output;\n}\n\n/**\n * Get usage from an assistant message if available.\n * Skips aborted and error messages as they don't have valid usage data.\n */\nfunction getAssistantUsage(msg: AgentMessage): Usage | undefined {\n\tif (msg.role === \"assistant\" && \"usage\" in msg) {\n\t\tconst assistantMsg = msg as AssistantMessage;\n\t\tif (assistantMsg.stopReason !== \"aborted\" && assistantMsg.stopReason !== \"error\" && assistantMsg.usage) {\n\t\t\treturn assistantMsg.usage;\n\t\t}\n\t}\n\treturn undefined;\n}\n\n/**\n * Find the last non-aborted assistant message usage from session entries.\n */\nexport function getLastAssistantUsage(entries: SessionEntry[]): Usage | undefined {\n\tfor (let i = entries.length - 1; i >= 0; i--) {\n\t\tconst entry = entries[i];\n\t\tif (entry.type === \"message\") {\n\t\t\tconst usage = getAssistantUsage(entry.message);\n\t\t\tif (usage) return usage;\n\t\t}\n\t}\n\treturn undefined;\n}\n\nexport interface ContextUsageEstimate {\n\ttokens: number;\n\tusageTokens: number;\n\ttrailingTokens: number;\n\tlastUsageIndex: number | null;\n}\n\nfunction getLastAssistantUsageInfo(messages: AgentMessage[]): { usage: Usage; index: number } | undefined {\n\tlet latestPrefixTimestamp = Number.NEGATIVE_INFINITY;\n\tlet usageInfo: { usage: Usage; index: number } | undefined;\n\n\tfor (let i = 0; i < messages.length; i++) {\n\t\tconst message = messages[i];\n\t\tconst usage = getAssistantUsage(message);\n\t\tif (usage && message.timestamp >= latestPrefixTimestamp && calculateContextTokens(usage) > 0) {\n\t\t\tusageInfo = { usage, index: i };\n\t\t}\n\t\tlatestPrefixTimestamp = Math.max(latestPrefixTimestamp, message.timestamp);\n\t}\n\n\treturn usageInfo;\n}\n\n/**\n * Estimate context tokens from messages, using the last assistant usage when available.\n * If there are messages after the last usage, estimate their tokens with estimateTokens.\n */\nexport function estimateContextTokens(messages: AgentMessage[]): ContextUsageEstimate {\n\tconst usageInfo = getLastAssistantUsageInfo(messages);\n\n\tif (!usageInfo) {\n\t\tlet estimated = 0;\n\t\tfor (const message of messages) {\n\t\t\testimated += estimateTokens(message);\n\t\t}\n\t\treturn {\n\t\t\ttokens: estimated,\n\t\t\tusageTokens: 0,\n\t\t\ttrailingTokens: estimated,\n\t\t\tlastUsageIndex: null,\n\t\t};\n\t}\n\n\tconst usageTokens = calculateContextTokens(usageInfo.usage);\n\tlet trailingTokens = 0;\n\tfor (let i = usageInfo.index + 1; i < messages.length; i++) {\n\t\ttrailingTokens += estimateTokens(messages[i]);\n\t}\n\n\treturn {\n\t\ttokens: usageTokens + trailingTokens,\n\t\tusageTokens,\n\t\ttrailingTokens,\n\t\tlastUsageIndex: usageInfo.index,\n\t};\n}\n\n/**\n * Check if compaction should trigger based on context usage.\n */\nexport function shouldCompact(contextTokens: number, contextWindow: number, settings: CompactionSettings): boolean {\n\tif (!settings.enabled) return false;\n\treturn contextTokens > contextWindow - settings.reserveTokens;\n}\n\n/**\n * Shared image-token estimation used by every compaction/context-accounting path.\n *\n * Providers fold image tokens into their reported prompt/input usage, so usage-based\n * accounting already captures actual image cost. For heuristic paths (trailing\n * messages without usage, transcript content-block estimates) a single conservative\n * fixed estimate keeps both the context-window threshold check and the transcript\n * planner consistent.\n */\nexport const ESTIMATED_IMAGE_CHARS = 4800;\nexport const ESTIMATED_IMAGE_TOKENS = Math.ceil(ESTIMATED_IMAGE_CHARS / 4);\n\nfunction safeSerializedPayloadLength(value: unknown, fallback: string): number {\n\ttry {\n\t\tconst serialized = JSON.stringify(value);\n\t\tif (typeof serialized === \"string\") return serialized.length;\n\t} catch {\n\t\t// Cyclic and otherwise unserializable future blocks use a conservative marker below.\n\t}\n\treturn fallback.length;\n}\n\nfunction nonEmptyBlockType(block: unknown): string | undefined {\n\tif (!block || typeof block !== \"object\") return undefined;\n\tconst type = (block as { type?: unknown }).type;\n\treturn typeof type === \"string\" && type.trim().length > 0 ? type : undefined;\n}\n\nfunction estimateContentBlockChars(block: unknown): number {\n\tconst type = nonEmptyBlockType(block);\n\tif (!type) return 0;\n\tconst record = block as Record<string, unknown>;\n\tif (type === \"image\") return ESTIMATED_IMAGE_CHARS;\n\tif (type === \"text\") return typeof record.text === \"string\" ? record.text.length : 0;\n\tif (type === \"thinking\") return typeof record.thinking === \"string\" ? record.thinking.length : 0;\n\tif (type === \"toolCall\") {\n\t\tconst nameLength = typeof record.name === \"string\" ? record.name.length : 0;\n\t\treturn nameLength + safeSerializedPayloadLength(record.arguments, \"[unserializable tool arguments]\");\n\t}\n\treturn safeSerializedPayloadLength(block, `[unserializable ${type} block]`);\n}\n\n/** Estimate one raw content block after the caller applies provider visibility. */\nexport function estimateContentBlockTokens(block: unknown, visible = true): number {\n\tif (!visible) return 0;\n\tconst chars = estimateContentBlockChars(block);\n\treturn chars > 0 ? Math.ceil(chars / 4) : 0;\n}\n\nfunction estimateUserLikeContentTokens(content: unknown): number {\n\tif (typeof content === \"string\") return content.trim().length > 0 ? Math.ceil(content.length / 4) : 0;\n\tif (!Array.isArray(content)) return 0;\n\tconst chars = content.reduce(\n\t\t(total, block) => total + (userLikeContentBlockIsLlmVisible(block) ? estimateContentBlockChars(block) : 0),\n\t\t0,\n\t);\n\treturn chars > 0 ? Math.ceil(chars / 4) : 0;\n}\n\n/**\n * Count image content blocks in a message content array (text or block array).\n *\n * Exported as the canonical image-counting contract so tests can verify the\n * heuristic independently of the transcript-based estimation used in production.\n */\nexport function countImageContentBlocks(content: string | Array<{ type: string }>): number {\n\tif (typeof content === \"string\" || !Array.isArray(content)) return 0;\n\tlet count = 0;\n\tfor (const block of content) {\n\t\tif (block && typeof block === \"object\" && block.type === \"image\") count += 1;\n\t}\n\treturn count;\n}\n\n/**\n * Estimate the token cost of only the image content blocks in a message content array.\n *\n * Exported as the canonical image-token-estimation contract so tests can verify\n * the heuristic independently of the transcript-based estimation used in production.\n */\nexport function estimateImageContentTokens(content: string | Array<{ type: string }>): number {\n\treturn countImageContentBlocks(content) * ESTIMATED_IMAGE_TOKENS;\n}\n\n/**\n * Estimate token count for a message using chars/4 heuristic.\n * This is conservative (overestimates tokens).\n */\nexport function estimateTokens(message: AgentMessage): number {\n\tif (!message || typeof message !== \"object\" || !messageIsLlmVisible(message)) return 0;\n\n\tswitch (message.role) {\n\t\tcase \"user\":\n\t\tcase \"custom\":\n\t\t\treturn estimateUserLikeContentTokens((message as { content?: unknown }).content);\n\t\tcase \"assistant\": {\n\t\t\tconst content = (message as { content?: unknown }).content;\n\t\t\tif (!Array.isArray(content)) return 0;\n\t\t\tconst chars = content.reduce((total, block) => total + estimateContentBlockChars(block), 0);\n\t\t\treturn chars > 0 ? Math.ceil(chars / 4) : 0;\n\t\t}\n\t\tcase \"toolResult\": {\n\t\t\tconst content = (message as { content?: unknown }).content;\n\t\t\tif (!Array.isArray(content)) return 0;\n\t\t\tconst chars = content.reduce((total, block) => total + estimateContentBlockChars(block), 0);\n\t\t\treturn chars > 0 ? Math.ceil(chars / 4) : 0;\n\t\t}\n\t\tcase \"bashExecution\": {\n\t\t\tconst command = typeof message.command === \"string\" ? message.command : \"\";\n\t\t\tconst output = typeof message.output === \"string\" ? message.output : \"\";\n\t\t\treturn Math.ceil((command.length + output.length) / 4);\n\t\t}\n\t\tcase \"branchSummary\":\n\t\t\treturn typeof message.summary === \"string\" ? Math.ceil(message.summary.length / 4) : 0;\n\t}\n\n\treturn 0;\n}\n"]}
@@ -0,0 +1,42 @@
1
+ import type { CompactableContentBlock, CompactableTranscript, CompactableTranscriptEntry } from "./context-compaction-types.js";
2
+ export declare function assistantEntryHasThinkingContentBlock(entry: CompactableTranscriptEntry): boolean;
3
+ export interface AssistantTurnEntry {
4
+ entryId: string;
5
+ role: string;
6
+ hasSignedThinking: boolean;
7
+ startsNewTurn: boolean;
8
+ }
9
+ export interface AssistantToolUseTurn {
10
+ entryIds: string[];
11
+ assistantEntryIds: string[];
12
+ signedThinkingEntryIds: string[];
13
+ active: boolean;
14
+ }
15
+ export type CompactableTranscriptDeletionTarget = {
16
+ readonly kind: "entry";
17
+ readonly entryId: string;
18
+ } | {
19
+ readonly kind: "content_block";
20
+ readonly entryId: string;
21
+ readonly blockIndex: number;
22
+ };
23
+ export interface CompactableAssistantTurnAnalysis {
24
+ deletedEntryIds: ReadonlySet<string>;
25
+ deletedContentBlocks: ReadonlyMap<string, ReadonlySet<number>>;
26
+ turns: AssistantToolUseTurn[];
27
+ }
28
+ export declare function compactableContentBlockIsLlmVisible(entry: CompactableTranscriptEntry, block: CompactableContentBlock): boolean;
29
+ export declare function transcriptEntryStartsNewTurn(entry: CompactableTranscriptEntry, deletedContentBlocks?: ReadonlySet<number>): boolean;
30
+ export declare function isTranscriptEntryEffectivelyDeleted(entry: CompactableTranscriptEntry, deletedEntryIds: ReadonlySet<string>, deletedContentBlocks: ReadonlyMap<string, ReadonlySet<number>>): boolean;
31
+ /** Shared adaptation from compactable transcript entries to logical assistant-turn inputs. */
32
+ export declare function compactableTranscriptToAssistantTurnEntries(transcript: CompactableTranscript, deletedEntryIds: ReadonlySet<string>, deletedContentBlocks: ReadonlyMap<string, ReadonlySet<number>>): AssistantTurnEntry[];
33
+ /** Analyze assistant turns after projecting a compactable transcript through deletion targets. */
34
+ export declare function analyzeCompactableAssistantTurns(transcript: CompactableTranscript, targets: readonly CompactableTranscriptDeletionTarget[]): CompactableAssistantTurnAnalysis;
35
+ /**
36
+ * Analyze chronological LLM-visible entries as logical assistant tool-use turns.
37
+ * Callers adapt each context-visible user-like message into `startsNewTurn`;
38
+ * tool results remain in the current turn. A trailing input-only turn is omitted,
39
+ * intentionally leaving the preceding assistant turn historical rather than active.
40
+ */
41
+ export declare function analyzeAssistantToolUseTurns(entries: readonly AssistantTurnEntry[]): AssistantToolUseTurn[];
42
+ //# sourceMappingURL=context-assistant-turns.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-assistant-turns.d.ts","sourceRoot":"","sources":["../../../src/core/compaction/context-assistant-turns.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACX,uBAAuB,EACvB,qBAAqB,EACrB,0BAA0B,EAC1B,MAAM,+BAA+B,CAAC;AAGvC,wBAAgB,qCAAqC,CAAC,KAAK,EAAE,0BAA0B,GAAG,OAAO,CAMhG;AAED,MAAM,WAAW,kBAAkB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IACb,iBAAiB,EAAE,OAAO,CAAC;IAC3B,aAAa,EAAE,OAAO,CAAC;CACvB;AAED,MAAM,WAAW,oBAAoB;IACpC,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,iBAAiB,EAAE,MAAM,EAAE,CAAC;IAC5B,sBAAsB,EAAE,MAAM,EAAE,CAAC;IACjC,MAAM,EAAE,OAAO,CAAC;CAChB;AAED,MAAM,MAAM,mCAAmC,GAC5C;IAAE,QAAQ,CAAC,IAAI,EAAE,OAAO,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAA;CAAE,GACpD;IAAE,QAAQ,CAAC,IAAI,EAAE,eAAe,CAAC;IAAC,QAAQ,CAAC,OAAO,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7F,MAAM,WAAW,gCAAgC;IAChD,eAAe,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC;IACrC,oBAAoB,EAAE,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC,CAAC;IAC/D,KAAK,EAAE,oBAAoB,EAAE,CAAC;CAC9B;AAED,wBAAgB,mCAAmC,CAClD,KAAK,EAAE,0BAA0B,EACjC,KAAK,EAAE,uBAAuB,GAC5B,OAAO,CAIT;AAED,wBAAgB,4BAA4B,CAC3C,KAAK,EAAE,0BAA0B,EACjC,oBAAoB,GAAE,WAAW,CAAC,MAAM,CAAqB,GAC3D,OAAO,CAWT;AAED,wBAAgB,mCAAmC,CAClD,KAAK,EAAE,0BAA0B,EACjC,eAAe,EAAE,WAAW,CAAC,MAAM,CAAC,EACpC,oBAAoB,EAAE,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC,GAC5D,OAAO,CAQT;AAED,8FAA8F;AAC9F,wBAAgB,2CAA2C,CAC1D,UAAU,EAAE,qBAAqB,EACjC,eAAe,EAAE,WAAW,CAAC,MAAM,CAAC,EACpC,oBAAoB,EAAE,WAAW,CAAC,MAAM,EAAE,WAAW,CAAC,MAAM,CAAC,CAAC,GAC5D,kBAAkB,EAAE,CAStB;AAED,kGAAkG;AAClG,wBAAgB,gCAAgC,CAC/C,UAAU,EAAE,qBAAqB,EACjC,OAAO,EAAE,SAAS,mCAAmC,EAAE,GACrD,gCAAgC,CAmBlC;AAED;;;;;GAKG;AACH,wBAAgB,4BAA4B,CAAC,OAAO,EAAE,SAAS,kBAAkB,EAAE,GAAG,oBAAoB,EAAE,CAmB3G","sourcesContent":["import { messageStartsLlmUserTurn, userLikeContentBlockIsLlmVisible } from \"../messages.js\";\nimport type {\n\tCompactableContentBlock,\n\tCompactableTranscript,\n\tCompactableTranscriptEntry,\n} from \"./context-compaction-types.js\";\nimport { isAssistantThinkingBlockType, messageHasAssistantThinkingContentBlock } from \"../thinking-blocks.js\";\n\nexport function assistantEntryHasThinkingContentBlock(entry: CompactableTranscriptEntry): boolean {\n\treturn (\n\t\tentry.role === \"assistant\" &&\n\t\t(entry.contentBlocks.some((block) => isAssistantThinkingBlockType(block.type)) ||\n\t\t\tmessageHasAssistantThinkingContentBlock(entry.message))\n\t);\n}\n\nexport interface AssistantTurnEntry {\n\tentryId: string;\n\trole: string;\n\thasSignedThinking: boolean;\n\tstartsNewTurn: boolean;\n}\n\nexport interface AssistantToolUseTurn {\n\tentryIds: string[];\n\tassistantEntryIds: string[];\n\tsignedThinkingEntryIds: string[];\n\tactive: boolean;\n}\n\nexport type CompactableTranscriptDeletionTarget =\n\t| { readonly kind: \"entry\"; readonly entryId: string }\n\t| { readonly kind: \"content_block\"; readonly entryId: string; readonly blockIndex: number };\n\nexport interface CompactableAssistantTurnAnalysis {\n\tdeletedEntryIds: ReadonlySet<string>;\n\tdeletedContentBlocks: ReadonlyMap<string, ReadonlySet<number>>;\n\tturns: AssistantToolUseTurn[];\n}\n\nexport function compactableContentBlockIsLlmVisible(\n\tentry: CompactableTranscriptEntry,\n\tblock: CompactableContentBlock,\n): boolean {\n\tif (typeof block.llmVisible === \"boolean\") return block.llmVisible;\n\tconst rawContent = (entry.message as { content?: unknown }).content;\n\treturn Array.isArray(rawContent) && userLikeContentBlockIsLlmVisible(rawContent[block.blockIndex]);\n}\n\nexport function transcriptEntryStartsNewTurn(\n\tentry: CompactableTranscriptEntry,\n\tdeletedContentBlocks: ReadonlySet<number> = new Set<number>(),\n): boolean {\n\tif (entry.role === \"user\" || entry.role === \"custom\") {\n\t\tconst content = (entry.message as { content?: unknown }).content;\n\t\tif (typeof content === \"string\" || entry.contentBlocks.length === 0) {\n\t\t\treturn messageStartsLlmUserTurn(entry.message, deletedContentBlocks);\n\t\t}\n\t\treturn entry.contentBlocks.some(\n\t\t\t(block) => !deletedContentBlocks.has(block.blockIndex) && compactableContentBlockIsLlmVisible(entry, block),\n\t\t);\n\t}\n\treturn messageStartsLlmUserTurn(entry.message, deletedContentBlocks);\n}\n\nexport function isTranscriptEntryEffectivelyDeleted(\n\tentry: CompactableTranscriptEntry,\n\tdeletedEntryIds: ReadonlySet<string>,\n\tdeletedContentBlocks: ReadonlyMap<string, ReadonlySet<number>>,\n): boolean {\n\tif (deletedEntryIds.has(entry.entryId)) return true;\n\tconst deletedBlocks = deletedContentBlocks.get(entry.entryId);\n\treturn (\n\t\tentry.contentBlocks.length > 0 &&\n\t\tdeletedBlocks !== undefined &&\n\t\tentry.contentBlocks.every((block) => deletedBlocks.has(block.blockIndex))\n\t);\n}\n\n/** Shared adaptation from compactable transcript entries to logical assistant-turn inputs. */\nexport function compactableTranscriptToAssistantTurnEntries(\n\ttranscript: CompactableTranscript,\n\tdeletedEntryIds: ReadonlySet<string>,\n\tdeletedContentBlocks: ReadonlyMap<string, ReadonlySet<number>>,\n): AssistantTurnEntry[] {\n\treturn transcript.entries.map((entry) => ({\n\t\tentryId: entry.entryId,\n\t\trole: entry.role,\n\t\thasSignedThinking: assistantEntryHasThinkingContentBlock(entry),\n\t\tstartsNewTurn:\n\t\t\t!isTranscriptEntryEffectivelyDeleted(entry, deletedEntryIds, deletedContentBlocks) &&\n\t\t\ttranscriptEntryStartsNewTurn(entry, deletedContentBlocks.get(entry.entryId)),\n\t}));\n}\n\n/** Analyze assistant turns after projecting a compactable transcript through deletion targets. */\nexport function analyzeCompactableAssistantTurns(\n\ttranscript: CompactableTranscript,\n\ttargets: readonly CompactableTranscriptDeletionTarget[],\n): CompactableAssistantTurnAnalysis {\n\tconst deletedEntryIds = new Set<string>();\n\tconst deletedContentBlocks = new Map<string, Set<number>>();\n\tfor (const target of targets) {\n\t\tif (target.kind === \"entry\") {\n\t\t\tdeletedEntryIds.add(target.entryId);\n\t\t\tcontinue;\n\t\t}\n\t\tconst blockIndexes = deletedContentBlocks.get(target.entryId) ?? new Set<number>();\n\t\tblockIndexes.add(target.blockIndex);\n\t\tdeletedContentBlocks.set(target.entryId, blockIndexes);\n\t}\n\treturn {\n\t\tdeletedEntryIds,\n\t\tdeletedContentBlocks,\n\t\tturns: analyzeAssistantToolUseTurns(\n\t\t\tcompactableTranscriptToAssistantTurnEntries(transcript, deletedEntryIds, deletedContentBlocks),\n\t\t),\n\t};\n}\n\n/**\n * Analyze chronological LLM-visible entries as logical assistant tool-use turns.\n * Callers adapt each context-visible user-like message into `startsNewTurn`;\n * tool results remain in the current turn. A trailing input-only turn is omitted,\n * intentionally leaving the preceding assistant turn historical rather than active.\n */\nexport function analyzeAssistantToolUseTurns(entries: readonly AssistantTurnEntry[]): AssistantToolUseTurn[] {\n\tconst groups: Array<Omit<AssistantToolUseTurn, \"active\">> = [];\n\tlet current: Omit<AssistantToolUseTurn, \"active\"> | undefined;\n\n\tfor (const entry of entries) {\n\t\tif (entry.startsNewTurn || current === undefined) {\n\t\t\tcurrent = { entryIds: [], assistantEntryIds: [], signedThinkingEntryIds: [] };\n\t\t\tgroups.push(current);\n\t\t}\n\t\tcurrent.entryIds.push(entry.entryId);\n\t\tif (entry.role !== \"assistant\") continue;\n\t\tcurrent.assistantEntryIds.push(entry.entryId);\n\t\tif (entry.hasSignedThinking) current.signedThinkingEntryIds.push(entry.entryId);\n\t}\n\n\tconst activeIndex = groups.length - 1;\n\treturn groups\n\t\t.map((turn, index) => ({ ...turn, active: index === activeIndex }))\n\t\t.filter((turn) => turn.assistantEntryIds.length > 0);\n}\n"]}
@@ -0,0 +1,87 @@
1
+ import { messageStartsLlmUserTurn, userLikeContentBlockIsLlmVisible } from "../messages.js";
2
+ import { isAssistantThinkingBlockType, messageHasAssistantThinkingContentBlock } from "../thinking-blocks.js";
3
+ export function assistantEntryHasThinkingContentBlock(entry) {
4
+ return (entry.role === "assistant" &&
5
+ (entry.contentBlocks.some((block) => isAssistantThinkingBlockType(block.type)) ||
6
+ messageHasAssistantThinkingContentBlock(entry.message)));
7
+ }
8
+ export function compactableContentBlockIsLlmVisible(entry, block) {
9
+ if (typeof block.llmVisible === "boolean")
10
+ return block.llmVisible;
11
+ const rawContent = entry.message.content;
12
+ return Array.isArray(rawContent) && userLikeContentBlockIsLlmVisible(rawContent[block.blockIndex]);
13
+ }
14
+ export function transcriptEntryStartsNewTurn(entry, deletedContentBlocks = new Set()) {
15
+ if (entry.role === "user" || entry.role === "custom") {
16
+ const content = entry.message.content;
17
+ if (typeof content === "string" || entry.contentBlocks.length === 0) {
18
+ return messageStartsLlmUserTurn(entry.message, deletedContentBlocks);
19
+ }
20
+ return entry.contentBlocks.some((block) => !deletedContentBlocks.has(block.blockIndex) && compactableContentBlockIsLlmVisible(entry, block));
21
+ }
22
+ return messageStartsLlmUserTurn(entry.message, deletedContentBlocks);
23
+ }
24
+ export function isTranscriptEntryEffectivelyDeleted(entry, deletedEntryIds, deletedContentBlocks) {
25
+ if (deletedEntryIds.has(entry.entryId))
26
+ return true;
27
+ const deletedBlocks = deletedContentBlocks.get(entry.entryId);
28
+ return (entry.contentBlocks.length > 0 &&
29
+ deletedBlocks !== undefined &&
30
+ entry.contentBlocks.every((block) => deletedBlocks.has(block.blockIndex)));
31
+ }
32
+ /** Shared adaptation from compactable transcript entries to logical assistant-turn inputs. */
33
+ export function compactableTranscriptToAssistantTurnEntries(transcript, deletedEntryIds, deletedContentBlocks) {
34
+ return transcript.entries.map((entry) => ({
35
+ entryId: entry.entryId,
36
+ role: entry.role,
37
+ hasSignedThinking: assistantEntryHasThinkingContentBlock(entry),
38
+ startsNewTurn: !isTranscriptEntryEffectivelyDeleted(entry, deletedEntryIds, deletedContentBlocks) &&
39
+ transcriptEntryStartsNewTurn(entry, deletedContentBlocks.get(entry.entryId)),
40
+ }));
41
+ }
42
+ /** Analyze assistant turns after projecting a compactable transcript through deletion targets. */
43
+ export function analyzeCompactableAssistantTurns(transcript, targets) {
44
+ const deletedEntryIds = new Set();
45
+ const deletedContentBlocks = new Map();
46
+ for (const target of targets) {
47
+ if (target.kind === "entry") {
48
+ deletedEntryIds.add(target.entryId);
49
+ continue;
50
+ }
51
+ const blockIndexes = deletedContentBlocks.get(target.entryId) ?? new Set();
52
+ blockIndexes.add(target.blockIndex);
53
+ deletedContentBlocks.set(target.entryId, blockIndexes);
54
+ }
55
+ return {
56
+ deletedEntryIds,
57
+ deletedContentBlocks,
58
+ turns: analyzeAssistantToolUseTurns(compactableTranscriptToAssistantTurnEntries(transcript, deletedEntryIds, deletedContentBlocks)),
59
+ };
60
+ }
61
+ /**
62
+ * Analyze chronological LLM-visible entries as logical assistant tool-use turns.
63
+ * Callers adapt each context-visible user-like message into `startsNewTurn`;
64
+ * tool results remain in the current turn. A trailing input-only turn is omitted,
65
+ * intentionally leaving the preceding assistant turn historical rather than active.
66
+ */
67
+ export function analyzeAssistantToolUseTurns(entries) {
68
+ const groups = [];
69
+ let current;
70
+ for (const entry of entries) {
71
+ if (entry.startsNewTurn || current === undefined) {
72
+ current = { entryIds: [], assistantEntryIds: [], signedThinkingEntryIds: [] };
73
+ groups.push(current);
74
+ }
75
+ current.entryIds.push(entry.entryId);
76
+ if (entry.role !== "assistant")
77
+ continue;
78
+ current.assistantEntryIds.push(entry.entryId);
79
+ if (entry.hasSignedThinking)
80
+ current.signedThinkingEntryIds.push(entry.entryId);
81
+ }
82
+ const activeIndex = groups.length - 1;
83
+ return groups
84
+ .map((turn, index) => ({ ...turn, active: index === activeIndex }))
85
+ .filter((turn) => turn.assistantEntryIds.length > 0);
86
+ }
87
+ //# sourceMappingURL=context-assistant-turns.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-assistant-turns.js","sourceRoot":"","sources":["../../../src/core/compaction/context-assistant-turns.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,wBAAwB,EAAE,gCAAgC,EAAE,MAAM,gBAAgB,CAAC;AAM5F,OAAO,EAAE,4BAA4B,EAAE,uCAAuC,EAAE,MAAM,uBAAuB,CAAC;AAE9G,MAAM,UAAU,qCAAqC,CAAC,KAAiC;IACtF,OAAO,CACN,KAAK,CAAC,IAAI,KAAK,WAAW;QAC1B,CAAC,KAAK,CAAC,aAAa,CAAC,IAAI,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,4BAA4B,CAAC,KAAK,CAAC,IAAI,CAAC,CAAC;YAC7E,uCAAuC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CACxD,CAAC;AACH,CAAC;AA0BD,MAAM,UAAU,mCAAmC,CAClD,KAAiC,EACjC,KAA8B;IAE9B,IAAI,OAAO,KAAK,CAAC,UAAU,KAAK,SAAS;QAAE,OAAO,KAAK,CAAC,UAAU,CAAC;IACnE,MAAM,UAAU,GAAI,KAAK,CAAC,OAAiC,CAAC,OAAO,CAAC;IACpE,OAAO,KAAK,CAAC,OAAO,CAAC,UAAU,CAAC,IAAI,gCAAgC,CAAC,UAAU,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC;AACpG,CAAC;AAED,MAAM,UAAU,4BAA4B,CAC3C,KAAiC,EACjC,oBAAoB,GAAwB,IAAI,GAAG,EAAU;IAE7D,IAAI,KAAK,CAAC,IAAI,KAAK,MAAM,IAAI,KAAK,CAAC,IAAI,KAAK,QAAQ,EAAE,CAAC;QACtD,MAAM,OAAO,GAAI,KAAK,CAAC,OAAiC,CAAC,OAAO,CAAC;QACjE,IAAI,OAAO,OAAO,KAAK,QAAQ,IAAI,KAAK,CAAC,aAAa,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACrE,OAAO,wBAAwB,CAAC,KAAK,CAAC,OAAO,EAAE,oBAAoB,CAAC,CAAC;QACtE,CAAC;QACD,OAAO,KAAK,CAAC,aAAa,CAAC,IAAI,CAC9B,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,oBAAoB,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,IAAI,mCAAmC,CAAC,KAAK,EAAE,KAAK,CAAC,CAC3G,CAAC;IACH,CAAC;IACD,OAAO,wBAAwB,CAAC,KAAK,CAAC,OAAO,EAAE,oBAAoB,CAAC,CAAC;AACtE,CAAC;AAED,MAAM,UAAU,mCAAmC,CAClD,KAAiC,EACjC,eAAoC,EACpC,oBAA8D;IAE9D,IAAI,eAAe,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC;QAAE,OAAO,IAAI,CAAC;IACpD,MAAM,aAAa,GAAG,oBAAoB,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IAC9D,OAAO,CACN,KAAK,CAAC,aAAa,CAAC,MAAM,GAAG,CAAC;QAC9B,aAAa,KAAK,SAAS;QAC3B,KAAK,CAAC,aAAa,CAAC,KAAK,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,aAAa,CAAC,GAAG,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CACzE,CAAC;AACH,CAAC;AAED,8FAA8F;AAC9F,MAAM,UAAU,2CAA2C,CAC1D,UAAiC,EACjC,eAAoC,EACpC,oBAA8D;IAE9D,OAAO,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC;QACzC,OAAO,EAAE,KAAK,CAAC,OAAO;QACtB,IAAI,EAAE,KAAK,CAAC,IAAI;QAChB,iBAAiB,EAAE,qCAAqC,CAAC,KAAK,CAAC;QAC/D,aAAa,EACZ,CAAC,mCAAmC,CAAC,KAAK,EAAE,eAAe,EAAE,oBAAoB,CAAC;YAClF,4BAA4B,CAAC,KAAK,EAAE,oBAAoB,CAAC,GAAG,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;KAC7E,CAAC,CAAC,CAAC;AACL,CAAC;AAED,kGAAkG;AAClG,MAAM,UAAU,gCAAgC,CAC/C,UAAiC,EACjC,OAAuD;IAEvD,MAAM,eAAe,GAAG,IAAI,GAAG,EAAU,CAAC;IAC1C,MAAM,oBAAoB,GAAG,IAAI,GAAG,EAAuB,CAAC;IAC5D,KAAK,MAAM,MAAM,IAAI,OAAO,EAAE,CAAC;QAC9B,IAAI,MAAM,CAAC,IAAI,KAAK,OAAO,EAAE,CAAC;YAC7B,eAAe,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC;YACpC,SAAS;QACV,CAAC;QACD,MAAM,YAAY,GAAG,oBAAoB,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,CAAC,IAAI,IAAI,GAAG,EAAU,CAAC;QACnF,YAAY,CAAC,GAAG,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;QACpC,oBAAoB,CAAC,GAAG,CAAC,MAAM,CAAC,OAAO,EAAE,YAAY,CAAC,CAAC;IACxD,CAAC;IACD,OAAO;QACN,eAAe;QACf,oBAAoB;QACpB,KAAK,EAAE,4BAA4B,CAClC,2CAA2C,CAAC,UAAU,EAAE,eAAe,EAAE,oBAAoB,CAAC,CAC9F;KACD,CAAC;AACH,CAAC;AAED;;;;;GAKG;AACH,MAAM,UAAU,4BAA4B,CAAC,OAAsC;IAClF,MAAM,MAAM,GAAgD,EAAE,CAAC;IAC/D,IAAI,OAAyD,CAAC;IAE9D,KAAK,MAAM,KAAK,IAAI,OAAO,EAAE,CAAC;QAC7B,IAAI,KAAK,CAAC,aAAa,IAAI,OAAO,KAAK,SAAS,EAAE,CAAC;YAClD,OAAO,GAAG,EAAE,QAAQ,EAAE,EAAE,EAAE,iBAAiB,EAAE,EAAE,EAAE,sBAAsB,EAAE,EAAE,EAAE,CAAC;YAC9E,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;QACtB,CAAC;QACD,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QACrC,IAAI,KAAK,CAAC,IAAI,KAAK,WAAW;YAAE,SAAS;QACzC,OAAO,CAAC,iBAAiB,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;QAC9C,IAAI,KAAK,CAAC,iBAAiB;YAAE,OAAO,CAAC,sBAAsB,CAAC,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC;IACjF,CAAC;IAED,MAAM,WAAW,GAAG,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;IACtC,OAAO,MAAM;SACX,GAAG,CAAC,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,IAAI,EAAE,MAAM,EAAE,KAAK,KAAK,WAAW,EAAE,CAAC,CAAC;SAClE,MAAM,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,IAAI,CAAC,iBAAiB,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;AACvD,CAAC","sourcesContent":["import { messageStartsLlmUserTurn, userLikeContentBlockIsLlmVisible } from \"../messages.js\";\nimport type {\n\tCompactableContentBlock,\n\tCompactableTranscript,\n\tCompactableTranscriptEntry,\n} from \"./context-compaction-types.js\";\nimport { isAssistantThinkingBlockType, messageHasAssistantThinkingContentBlock } from \"../thinking-blocks.js\";\n\nexport function assistantEntryHasThinkingContentBlock(entry: CompactableTranscriptEntry): boolean {\n\treturn (\n\t\tentry.role === \"assistant\" &&\n\t\t(entry.contentBlocks.some((block) => isAssistantThinkingBlockType(block.type)) ||\n\t\t\tmessageHasAssistantThinkingContentBlock(entry.message))\n\t);\n}\n\nexport interface AssistantTurnEntry {\n\tentryId: string;\n\trole: string;\n\thasSignedThinking: boolean;\n\tstartsNewTurn: boolean;\n}\n\nexport interface AssistantToolUseTurn {\n\tentryIds: string[];\n\tassistantEntryIds: string[];\n\tsignedThinkingEntryIds: string[];\n\tactive: boolean;\n}\n\nexport type CompactableTranscriptDeletionTarget =\n\t| { readonly kind: \"entry\"; readonly entryId: string }\n\t| { readonly kind: \"content_block\"; readonly entryId: string; readonly blockIndex: number };\n\nexport interface CompactableAssistantTurnAnalysis {\n\tdeletedEntryIds: ReadonlySet<string>;\n\tdeletedContentBlocks: ReadonlyMap<string, ReadonlySet<number>>;\n\tturns: AssistantToolUseTurn[];\n}\n\nexport function compactableContentBlockIsLlmVisible(\n\tentry: CompactableTranscriptEntry,\n\tblock: CompactableContentBlock,\n): boolean {\n\tif (typeof block.llmVisible === \"boolean\") return block.llmVisible;\n\tconst rawContent = (entry.message as { content?: unknown }).content;\n\treturn Array.isArray(rawContent) && userLikeContentBlockIsLlmVisible(rawContent[block.blockIndex]);\n}\n\nexport function transcriptEntryStartsNewTurn(\n\tentry: CompactableTranscriptEntry,\n\tdeletedContentBlocks: ReadonlySet<number> = new Set<number>(),\n): boolean {\n\tif (entry.role === \"user\" || entry.role === \"custom\") {\n\t\tconst content = (entry.message as { content?: unknown }).content;\n\t\tif (typeof content === \"string\" || entry.contentBlocks.length === 0) {\n\t\t\treturn messageStartsLlmUserTurn(entry.message, deletedContentBlocks);\n\t\t}\n\t\treturn entry.contentBlocks.some(\n\t\t\t(block) => !deletedContentBlocks.has(block.blockIndex) && compactableContentBlockIsLlmVisible(entry, block),\n\t\t);\n\t}\n\treturn messageStartsLlmUserTurn(entry.message, deletedContentBlocks);\n}\n\nexport function isTranscriptEntryEffectivelyDeleted(\n\tentry: CompactableTranscriptEntry,\n\tdeletedEntryIds: ReadonlySet<string>,\n\tdeletedContentBlocks: ReadonlyMap<string, ReadonlySet<number>>,\n): boolean {\n\tif (deletedEntryIds.has(entry.entryId)) return true;\n\tconst deletedBlocks = deletedContentBlocks.get(entry.entryId);\n\treturn (\n\t\tentry.contentBlocks.length > 0 &&\n\t\tdeletedBlocks !== undefined &&\n\t\tentry.contentBlocks.every((block) => deletedBlocks.has(block.blockIndex))\n\t);\n}\n\n/** Shared adaptation from compactable transcript entries to logical assistant-turn inputs. */\nexport function compactableTranscriptToAssistantTurnEntries(\n\ttranscript: CompactableTranscript,\n\tdeletedEntryIds: ReadonlySet<string>,\n\tdeletedContentBlocks: ReadonlyMap<string, ReadonlySet<number>>,\n): AssistantTurnEntry[] {\n\treturn transcript.entries.map((entry) => ({\n\t\tentryId: entry.entryId,\n\t\trole: entry.role,\n\t\thasSignedThinking: assistantEntryHasThinkingContentBlock(entry),\n\t\tstartsNewTurn:\n\t\t\t!isTranscriptEntryEffectivelyDeleted(entry, deletedEntryIds, deletedContentBlocks) &&\n\t\t\ttranscriptEntryStartsNewTurn(entry, deletedContentBlocks.get(entry.entryId)),\n\t}));\n}\n\n/** Analyze assistant turns after projecting a compactable transcript through deletion targets. */\nexport function analyzeCompactableAssistantTurns(\n\ttranscript: CompactableTranscript,\n\ttargets: readonly CompactableTranscriptDeletionTarget[],\n): CompactableAssistantTurnAnalysis {\n\tconst deletedEntryIds = new Set<string>();\n\tconst deletedContentBlocks = new Map<string, Set<number>>();\n\tfor (const target of targets) {\n\t\tif (target.kind === \"entry\") {\n\t\t\tdeletedEntryIds.add(target.entryId);\n\t\t\tcontinue;\n\t\t}\n\t\tconst blockIndexes = deletedContentBlocks.get(target.entryId) ?? new Set<number>();\n\t\tblockIndexes.add(target.blockIndex);\n\t\tdeletedContentBlocks.set(target.entryId, blockIndexes);\n\t}\n\treturn {\n\t\tdeletedEntryIds,\n\t\tdeletedContentBlocks,\n\t\tturns: analyzeAssistantToolUseTurns(\n\t\t\tcompactableTranscriptToAssistantTurnEntries(transcript, deletedEntryIds, deletedContentBlocks),\n\t\t),\n\t};\n}\n\n/**\n * Analyze chronological LLM-visible entries as logical assistant tool-use turns.\n * Callers adapt each context-visible user-like message into `startsNewTurn`;\n * tool results remain in the current turn. A trailing input-only turn is omitted,\n * intentionally leaving the preceding assistant turn historical rather than active.\n */\nexport function analyzeAssistantToolUseTurns(entries: readonly AssistantTurnEntry[]): AssistantToolUseTurn[] {\n\tconst groups: Array<Omit<AssistantToolUseTurn, \"active\">> = [];\n\tlet current: Omit<AssistantToolUseTurn, \"active\"> | undefined;\n\n\tfor (const entry of entries) {\n\t\tif (entry.startsNewTurn || current === undefined) {\n\t\t\tcurrent = { entryIds: [], assistantEntryIds: [], signedThinkingEntryIds: [] };\n\t\t\tgroups.push(current);\n\t\t}\n\t\tcurrent.entryIds.push(entry.entryId);\n\t\tif (entry.role !== \"assistant\") continue;\n\t\tcurrent.assistantEntryIds.push(entry.entryId);\n\t\tif (entry.hasSignedThinking) current.signedThinkingEntryIds.push(entry.entryId);\n\t}\n\n\tconst activeIndex = groups.length - 1;\n\treturn groups\n\t\t.map((turn, index) => ({ ...turn, active: index === activeIndex }))\n\t\t.filter((turn) => turn.assistantEntryIds.length > 0);\n}\n"]}
@@ -11,5 +11,5 @@ export declare function isCriticalOverflowProtectedEntryDeletable(entry: Compact
11
11
  * persisted results/events stay disjoint from deleted targets.
12
12
  */
13
13
  export declare function relaxTranscriptForCriticalEviction(transcript: CompactableTranscript): CompactableTranscript;
14
- export declare const CONTEXT_COMPACTION_CRITICAL_OVERFLOW_PROMPT = "\n<critical-overflow-mode>\nThe previous model request overflowed its context window. This is a critical LRU-style compaction pass. First delete stale unprotected context. If that is not enough, you may also delete the earliest formerly-protected entries or protected content shown in the manifest. Evict old low-signal context first, including old reasoning/thinking traces when they are not part of the latest retained assistant message, then older user/custom/summary context while preserving recent entries, unresolved errors, failed commands, and enough task-bearing context for the assistant to continue.\n\nSafety invariant: the latest retained assistant message cannot be modified when it contains content blocks with type \"thinking\" or \"redacted_thinking\". Do not delete, target, or suggest any content block in that latest thinking-bearing assistant message, including sibling text or tool-call blocks. Older non-latest thinking/redacted_thinking blocks may be deleted during critical overflow when validation allows it.\n</critical-overflow-mode>";
14
+ export declare const CONTEXT_COMPACTION_CRITICAL_OVERFLOW_PROMPT = "\n<critical-overflow-mode>\nThe previous model request overflowed its context window. This is a critical LRU-style compaction pass. First delete stale unprotected context. If that is not enough, you may also delete the earliest formerly-protected entries or protected content shown in the manifest. Evict old low-signal context first, then older user/custom/summary context while preserving recent entries, unresolved errors, failed commands, and enough task-bearing context for the assistant to continue.\n\nSafety invariant: each provider-visible user-like input starts a logical assistant tool-use turn: a non-empty user/custom input, a context-eligible bash execution, or a non-empty branch summary. Empty or whitespace-only user/custom inputs and empty branch summaries do not split turns; whitespace-only branch summaries remain visible through their wrapper. Assistant messages and intervening tool results remain in the turn until the next visible input. Never delete a thinking/redacted_thinking-bearing assistant entry from the current final logical turn; a trailing visible input without an assistant response makes the preceding assistant turn historical. For a completed historical turn, retain every thinking/redacted_thinking-bearing assistant entry or delete all of them as whole entries. Never retain a proper subset or partially delete a retained thinking-bearing assistant message.\n</critical-overflow-mode>";
15
15
  //# sourceMappingURL=context-compaction-critical.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"context-compaction-critical.d.ts","sourceRoot":"","sources":["../../../src/core/compaction/context-compaction-critical.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,0BAA0B,EAAE,2BAA2B,EAAE,MAAM,+BAA+B,CAAC;AAKpI,eAAO,MAAM,4CAA4C,IAAI,CAAC;AAE9D,wBAAgB,4BAA4B,CAAC,UAAU,EAAE,2BAA2B,GAAG,2BAA2B,CAKjH;AAED,wBAAgB,yCAAyC,CACxD,KAAK,EAAE,0BAA0B,EACjC,UAAU,EAAE,qBAAqB,GAC/B,OAAO,CAUT;AAED;;;;;;;GAOG;AACH,wBAAgB,kCAAkC,CAAC,UAAU,EAAE,qBAAqB,GAAG,qBAAqB,CAgB3G;AAED,eAAO,MAAM,2CAA2C,0iCAK9B,CAAC","sourcesContent":["import type { CompactableTranscript, CompactableTranscriptEntry, ContextCompactionParameters } from \"./context-compaction-types.ts\";\nimport { getTranscriptCompactionParameters } from \"./context-compaction-strategy.ts\";\nimport { isTaskBearingEntry } from \"./context-deletion-targets.ts\";\nimport { hasAssistantError, hasFailedBashExecution, hasToolResultError } from \"./context-transcript-analysis.ts\";\n\nexport const CONTEXT_CRITICAL_OVERFLOW_RECENT_ENTRY_COUNT = 5;\n\nexport function criticalCompactionParameters(parameters: ContextCompactionParameters): ContextCompactionParameters {\n\treturn {\n\t\t...parameters,\n\t\tpreserve_recent: Math.max(parameters.preserve_recent, CONTEXT_CRITICAL_OVERFLOW_RECENT_ENTRY_COUNT),\n\t};\n}\n\nexport function isCriticalOverflowProtectedEntryDeletable(\n\tentry: CompactableTranscriptEntry,\n\ttranscript: CompactableTranscript,\n): boolean {\n\tif (!entry.protected) return true;\n\tconst entryIndex = transcript.entries.findIndex((candidate) => candidate.entryId === entry.entryId);\n\tif (entryIndex < 0) return false;\n\tconst recentBoundary = Math.max(0, transcript.entries.length - CONTEXT_CRITICAL_OVERFLOW_RECENT_ENTRY_COUNT);\n\tif (entryIndex >= recentBoundary) return false;\n\tif (hasAssistantError(entry.message) || hasToolResultError(entry.message) || hasFailedBashExecution(entry.message)) {\n\t\treturn false;\n\t}\n\treturn isTaskBearingEntry(entry);\n}\n\n/**\n * Return a transcript for critical overflow passes. The effective recent guard is\n * widened to max(configured preserve_recent, 5) over all entries, restoring the\n * pre-#1399 critical-overflow last-5 floor through the existing recent-target\n * validation path. Only stale protected task-bearing entries outside that floor\n * are relaxed; `protectedEntryIds` is rebuilt from still-protected entries so\n * persisted results/events stay disjoint from deleted targets.\n */\nexport function relaxTranscriptForCriticalEviction(transcript: CompactableTranscript): CompactableTranscript {\n\tconst entries = transcript.entries.map((entry) => {\n\t\tif (!entry.protected || !isCriticalOverflowProtectedEntryDeletable(entry, transcript)) return entry;\n\t\treturn {\n\t\t\t...entry,\n\t\t\tprotected: false,\n\t\t\tcontentBlocks: entry.contentBlocks.map((block) => ({ ...block, protected: false })),\n\t\t};\n\t});\n\tconst parameters = criticalCompactionParameters(getTranscriptCompactionParameters(transcript));\n\treturn {\n\t\t...transcript,\n\t\tentries,\n\t\tparameters,\n\t\tprotectedEntryIds: entries.filter((entry) => entry.protected).map((entry) => entry.entryId),\n\t};\n}\n\nexport const CONTEXT_COMPACTION_CRITICAL_OVERFLOW_PROMPT = `\n<critical-overflow-mode>\nThe previous model request overflowed its context window. This is a critical LRU-style compaction pass. First delete stale unprotected context. If that is not enough, you may also delete the earliest formerly-protected entries or protected content shown in the manifest. Evict old low-signal context first, including old reasoning/thinking traces when they are not part of the latest retained assistant message, then older user/custom/summary context while preserving recent entries, unresolved errors, failed commands, and enough task-bearing context for the assistant to continue.\n\nSafety invariant: the latest retained assistant message cannot be modified when it contains content blocks with type \"thinking\" or \"redacted_thinking\". Do not delete, target, or suggest any content block in that latest thinking-bearing assistant message, including sibling text or tool-call blocks. Older non-latest thinking/redacted_thinking blocks may be deleted during critical overflow when validation allows it.\n</critical-overflow-mode>`;\n"]}
1
+ {"version":3,"file":"context-compaction-critical.d.ts","sourceRoot":"","sources":["../../../src/core/compaction/context-compaction-critical.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,0BAA0B,EAAE,2BAA2B,EAAE,MAAM,+BAA+B,CAAC;AAKpI,eAAO,MAAM,4CAA4C,IAAI,CAAC;AAE9D,wBAAgB,4BAA4B,CAAC,UAAU,EAAE,2BAA2B,GAAG,2BAA2B,CAKjH;AAED,wBAAgB,yCAAyC,CACxD,KAAK,EAAE,0BAA0B,EACjC,UAAU,EAAE,qBAAqB,GAC/B,OAAO,CAUT;AAED;;;;;;;GAOG;AACH,wBAAgB,kCAAkC,CAAC,UAAU,EAAE,qBAAqB,GAAG,qBAAqB,CAgB3G;AAED,eAAO,MAAM,2CAA2C,w5CAK9B,CAAC","sourcesContent":["import type { CompactableTranscript, CompactableTranscriptEntry, ContextCompactionParameters } from \"./context-compaction-types.ts\";\nimport { getTranscriptCompactionParameters } from \"./context-compaction-strategy.ts\";\nimport { isTaskBearingEntry } from \"./context-deletion-targets.ts\";\nimport { hasAssistantError, hasFailedBashExecution, hasToolResultError } from \"./context-transcript-analysis.ts\";\n\nexport const CONTEXT_CRITICAL_OVERFLOW_RECENT_ENTRY_COUNT = 5;\n\nexport function criticalCompactionParameters(parameters: ContextCompactionParameters): ContextCompactionParameters {\n\treturn {\n\t\t...parameters,\n\t\tpreserve_recent: Math.max(parameters.preserve_recent, CONTEXT_CRITICAL_OVERFLOW_RECENT_ENTRY_COUNT),\n\t};\n}\n\nexport function isCriticalOverflowProtectedEntryDeletable(\n\tentry: CompactableTranscriptEntry,\n\ttranscript: CompactableTranscript,\n): boolean {\n\tif (!entry.protected) return true;\n\tconst entryIndex = transcript.entries.findIndex((candidate) => candidate.entryId === entry.entryId);\n\tif (entryIndex < 0) return false;\n\tconst recentBoundary = Math.max(0, transcript.entries.length - CONTEXT_CRITICAL_OVERFLOW_RECENT_ENTRY_COUNT);\n\tif (entryIndex >= recentBoundary) return false;\n\tif (hasAssistantError(entry.message) || hasToolResultError(entry.message) || hasFailedBashExecution(entry.message)) {\n\t\treturn false;\n\t}\n\treturn isTaskBearingEntry(entry);\n}\n\n/**\n * Return a transcript for critical overflow passes. The effective recent guard is\n * widened to max(configured preserve_recent, 5) over all entries, restoring the\n * pre-#1399 critical-overflow last-5 floor through the existing recent-target\n * validation path. Only stale protected task-bearing entries outside that floor\n * are relaxed; `protectedEntryIds` is rebuilt from still-protected entries so\n * persisted results/events stay disjoint from deleted targets.\n */\nexport function relaxTranscriptForCriticalEviction(transcript: CompactableTranscript): CompactableTranscript {\n\tconst entries = transcript.entries.map((entry) => {\n\t\tif (!entry.protected || !isCriticalOverflowProtectedEntryDeletable(entry, transcript)) return entry;\n\t\treturn {\n\t\t\t...entry,\n\t\t\tprotected: false,\n\t\t\tcontentBlocks: entry.contentBlocks.map((block) => ({ ...block, protected: false })),\n\t\t};\n\t});\n\tconst parameters = criticalCompactionParameters(getTranscriptCompactionParameters(transcript));\n\treturn {\n\t\t...transcript,\n\t\tentries,\n\t\tparameters,\n\t\tprotectedEntryIds: entries.filter((entry) => entry.protected).map((entry) => entry.entryId),\n\t};\n}\n\nexport const CONTEXT_COMPACTION_CRITICAL_OVERFLOW_PROMPT = `\n<critical-overflow-mode>\nThe previous model request overflowed its context window. This is a critical LRU-style compaction pass. First delete stale unprotected context. If that is not enough, you may also delete the earliest formerly-protected entries or protected content shown in the manifest. Evict old low-signal context first, then older user/custom/summary context while preserving recent entries, unresolved errors, failed commands, and enough task-bearing context for the assistant to continue.\n\nSafety invariant: each provider-visible user-like input starts a logical assistant tool-use turn: a non-empty user/custom input, a context-eligible bash execution, or a non-empty branch summary. Empty or whitespace-only user/custom inputs and empty branch summaries do not split turns; whitespace-only branch summaries remain visible through their wrapper. Assistant messages and intervening tool results remain in the turn until the next visible input. Never delete a thinking/redacted_thinking-bearing assistant entry from the current final logical turn; a trailing visible input without an assistant response makes the preceding assistant turn historical. For a completed historical turn, retain every thinking/redacted_thinking-bearing assistant entry or delete all of them as whole entries. Never retain a proper subset or partially delete a retained thinking-bearing assistant message.\n</critical-overflow-mode>`;\n"]}
@@ -50,8 +50,8 @@ export function relaxTranscriptForCriticalEviction(transcript) {
50
50
  }
51
51
  export const CONTEXT_COMPACTION_CRITICAL_OVERFLOW_PROMPT = `
52
52
  <critical-overflow-mode>
53
- The previous model request overflowed its context window. This is a critical LRU-style compaction pass. First delete stale unprotected context. If that is not enough, you may also delete the earliest formerly-protected entries or protected content shown in the manifest. Evict old low-signal context first, including old reasoning/thinking traces when they are not part of the latest retained assistant message, then older user/custom/summary context while preserving recent entries, unresolved errors, failed commands, and enough task-bearing context for the assistant to continue.
53
+ The previous model request overflowed its context window. This is a critical LRU-style compaction pass. First delete stale unprotected context. If that is not enough, you may also delete the earliest formerly-protected entries or protected content shown in the manifest. Evict old low-signal context first, then older user/custom/summary context while preserving recent entries, unresolved errors, failed commands, and enough task-bearing context for the assistant to continue.
54
54
 
55
- Safety invariant: the latest retained assistant message cannot be modified when it contains content blocks with type "thinking" or "redacted_thinking". Do not delete, target, or suggest any content block in that latest thinking-bearing assistant message, including sibling text or tool-call blocks. Older non-latest thinking/redacted_thinking blocks may be deleted during critical overflow when validation allows it.
55
+ Safety invariant: each provider-visible user-like input starts a logical assistant tool-use turn: a non-empty user/custom input, a context-eligible bash execution, or a non-empty branch summary. Empty or whitespace-only user/custom inputs and empty branch summaries do not split turns; whitespace-only branch summaries remain visible through their wrapper. Assistant messages and intervening tool results remain in the turn until the next visible input. Never delete a thinking/redacted_thinking-bearing assistant entry from the current final logical turn; a trailing visible input without an assistant response makes the preceding assistant turn historical. For a completed historical turn, retain every thinking/redacted_thinking-bearing assistant entry or delete all of them as whole entries. Never retain a proper subset or partially delete a retained thinking-bearing assistant message.
56
56
  </critical-overflow-mode>`;
57
57
  //# sourceMappingURL=context-compaction-critical.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"context-compaction-critical.js","sourceRoot":"","sources":["../../../src/core/compaction/context-compaction-critical.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iCAAiC,EAAE,MAAM,kCAAkC,CAAC;AACrF,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AAEjH,MAAM,CAAC,MAAM,4CAA4C,GAAG,CAAC,CAAC;AAE9D,MAAM,UAAU,4BAA4B,CAAC,UAAuC;IACnF,OAAO;QACN,GAAG,UAAU;QACb,eAAe,EAAE,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,eAAe,EAAE,4CAA4C,CAAC;KACnG,CAAC;AACH,CAAC;AAED,MAAM,UAAU,yCAAyC,CACxD,KAAiC,EACjC,UAAiC;IAEjC,IAAI,CAAC,KAAK,CAAC,SAAS;QAAE,OAAO,IAAI,CAAC;IAClC,MAAM,UAAU,GAAG,UAAU,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,OAAO,KAAK,KAAK,CAAC,OAAO,CAAC,CAAC;IACpG,IAAI,UAAU,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IACjC,MAAM,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,MAAM,GAAG,4CAA4C,CAAC,CAAC;IAC7G,IAAI,UAAU,IAAI,cAAc;QAAE,OAAO,KAAK,CAAC;IAC/C,IAAI,iBAAiB,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,kBAAkB,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,sBAAsB,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QACpH,OAAO,KAAK,CAAC;IACd,CAAC;IACD,OAAO,kBAAkB,CAAC,KAAK,CAAC,CAAC;AAClC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,kCAAkC,CAAC,UAAiC;IACnF,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QAChD,IAAI,CAAC,KAAK,CAAC,SAAS,IAAI,CAAC,yCAAyC,CAAC,KAAK,EAAE,UAAU,CAAC;YAAE,OAAO,KAAK,CAAC;QACpG,OAAO;YACN,GAAG,KAAK;YACR,SAAS,EAAE,KAAK;YAChB,aAAa,EAAE,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;SACnF,CAAC;IACH,CAAC,CAAC,CAAC;IACH,MAAM,UAAU,GAAG,4BAA4B,CAAC,iCAAiC,CAAC,UAAU,CAAC,CAAC,CAAC;IAC/F,OAAO;QACN,GAAG,UAAU;QACb,OAAO;QACP,UAAU;QACV,iBAAiB,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC;KAC3F,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,2CAA2C,GAAG;;;;;0BAKjC,CAAC","sourcesContent":["import type { CompactableTranscript, CompactableTranscriptEntry, ContextCompactionParameters } from \"./context-compaction-types.ts\";\nimport { getTranscriptCompactionParameters } from \"./context-compaction-strategy.ts\";\nimport { isTaskBearingEntry } from \"./context-deletion-targets.ts\";\nimport { hasAssistantError, hasFailedBashExecution, hasToolResultError } from \"./context-transcript-analysis.ts\";\n\nexport const CONTEXT_CRITICAL_OVERFLOW_RECENT_ENTRY_COUNT = 5;\n\nexport function criticalCompactionParameters(parameters: ContextCompactionParameters): ContextCompactionParameters {\n\treturn {\n\t\t...parameters,\n\t\tpreserve_recent: Math.max(parameters.preserve_recent, CONTEXT_CRITICAL_OVERFLOW_RECENT_ENTRY_COUNT),\n\t};\n}\n\nexport function isCriticalOverflowProtectedEntryDeletable(\n\tentry: CompactableTranscriptEntry,\n\ttranscript: CompactableTranscript,\n): boolean {\n\tif (!entry.protected) return true;\n\tconst entryIndex = transcript.entries.findIndex((candidate) => candidate.entryId === entry.entryId);\n\tif (entryIndex < 0) return false;\n\tconst recentBoundary = Math.max(0, transcript.entries.length - CONTEXT_CRITICAL_OVERFLOW_RECENT_ENTRY_COUNT);\n\tif (entryIndex >= recentBoundary) return false;\n\tif (hasAssistantError(entry.message) || hasToolResultError(entry.message) || hasFailedBashExecution(entry.message)) {\n\t\treturn false;\n\t}\n\treturn isTaskBearingEntry(entry);\n}\n\n/**\n * Return a transcript for critical overflow passes. The effective recent guard is\n * widened to max(configured preserve_recent, 5) over all entries, restoring the\n * pre-#1399 critical-overflow last-5 floor through the existing recent-target\n * validation path. Only stale protected task-bearing entries outside that floor\n * are relaxed; `protectedEntryIds` is rebuilt from still-protected entries so\n * persisted results/events stay disjoint from deleted targets.\n */\nexport function relaxTranscriptForCriticalEviction(transcript: CompactableTranscript): CompactableTranscript {\n\tconst entries = transcript.entries.map((entry) => {\n\t\tif (!entry.protected || !isCriticalOverflowProtectedEntryDeletable(entry, transcript)) return entry;\n\t\treturn {\n\t\t\t...entry,\n\t\t\tprotected: false,\n\t\t\tcontentBlocks: entry.contentBlocks.map((block) => ({ ...block, protected: false })),\n\t\t};\n\t});\n\tconst parameters = criticalCompactionParameters(getTranscriptCompactionParameters(transcript));\n\treturn {\n\t\t...transcript,\n\t\tentries,\n\t\tparameters,\n\t\tprotectedEntryIds: entries.filter((entry) => entry.protected).map((entry) => entry.entryId),\n\t};\n}\n\nexport const CONTEXT_COMPACTION_CRITICAL_OVERFLOW_PROMPT = `\n<critical-overflow-mode>\nThe previous model request overflowed its context window. This is a critical LRU-style compaction pass. First delete stale unprotected context. If that is not enough, you may also delete the earliest formerly-protected entries or protected content shown in the manifest. Evict old low-signal context first, including old reasoning/thinking traces when they are not part of the latest retained assistant message, then older user/custom/summary context while preserving recent entries, unresolved errors, failed commands, and enough task-bearing context for the assistant to continue.\n\nSafety invariant: the latest retained assistant message cannot be modified when it contains content blocks with type \"thinking\" or \"redacted_thinking\". Do not delete, target, or suggest any content block in that latest thinking-bearing assistant message, including sibling text or tool-call blocks. Older non-latest thinking/redacted_thinking blocks may be deleted during critical overflow when validation allows it.\n</critical-overflow-mode>`;\n"]}
1
+ {"version":3,"file":"context-compaction-critical.js","sourceRoot":"","sources":["../../../src/core/compaction/context-compaction-critical.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,iCAAiC,EAAE,MAAM,kCAAkC,CAAC;AACrF,OAAO,EAAE,kBAAkB,EAAE,MAAM,+BAA+B,CAAC;AACnE,OAAO,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,kBAAkB,EAAE,MAAM,kCAAkC,CAAC;AAEjH,MAAM,CAAC,MAAM,4CAA4C,GAAG,CAAC,CAAC;AAE9D,MAAM,UAAU,4BAA4B,CAAC,UAAuC;IACnF,OAAO;QACN,GAAG,UAAU;QACb,eAAe,EAAE,IAAI,CAAC,GAAG,CAAC,UAAU,CAAC,eAAe,EAAE,4CAA4C,CAAC;KACnG,CAAC;AACH,CAAC;AAED,MAAM,UAAU,yCAAyC,CACxD,KAAiC,EACjC,UAAiC;IAEjC,IAAI,CAAC,KAAK,CAAC,SAAS;QAAE,OAAO,IAAI,CAAC;IAClC,MAAM,UAAU,GAAG,UAAU,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC,SAAS,EAAE,EAAE,CAAC,SAAS,CAAC,OAAO,KAAK,KAAK,CAAC,OAAO,CAAC,CAAC;IACpG,IAAI,UAAU,GAAG,CAAC;QAAE,OAAO,KAAK,CAAC;IACjC,MAAM,cAAc,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,UAAU,CAAC,OAAO,CAAC,MAAM,GAAG,4CAA4C,CAAC,CAAC;IAC7G,IAAI,UAAU,IAAI,cAAc;QAAE,OAAO,KAAK,CAAC;IAC/C,IAAI,iBAAiB,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,kBAAkB,CAAC,KAAK,CAAC,OAAO,CAAC,IAAI,sBAAsB,CAAC,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC;QACpH,OAAO,KAAK,CAAC;IACd,CAAC;IACD,OAAO,kBAAkB,CAAC,KAAK,CAAC,CAAC;AAClC,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,kCAAkC,CAAC,UAAiC;IACnF,MAAM,OAAO,GAAG,UAAU,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE;QAChD,IAAI,CAAC,KAAK,CAAC,SAAS,IAAI,CAAC,yCAAyC,CAAC,KAAK,EAAE,UAAU,CAAC;YAAE,OAAO,KAAK,CAAC;QACpG,OAAO;YACN,GAAG,KAAK;YACR,SAAS,EAAE,KAAK;YAChB,aAAa,EAAE,KAAK,CAAC,aAAa,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CAAC,EAAE,GAAG,KAAK,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAAC;SACnF,CAAC;IACH,CAAC,CAAC,CAAC;IACH,MAAM,UAAU,GAAG,4BAA4B,CAAC,iCAAiC,CAAC,UAAU,CAAC,CAAC,CAAC;IAC/F,OAAO;QACN,GAAG,UAAU;QACb,OAAO;QACP,UAAU;QACV,iBAAiB,EAAE,OAAO,CAAC,MAAM,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,KAAK,CAAC,OAAO,CAAC;KAC3F,CAAC;AACH,CAAC;AAED,MAAM,CAAC,MAAM,2CAA2C,GAAG;;;;;0BAKjC,CAAC","sourcesContent":["import type { CompactableTranscript, CompactableTranscriptEntry, ContextCompactionParameters } from \"./context-compaction-types.ts\";\nimport { getTranscriptCompactionParameters } from \"./context-compaction-strategy.ts\";\nimport { isTaskBearingEntry } from \"./context-deletion-targets.ts\";\nimport { hasAssistantError, hasFailedBashExecution, hasToolResultError } from \"./context-transcript-analysis.ts\";\n\nexport const CONTEXT_CRITICAL_OVERFLOW_RECENT_ENTRY_COUNT = 5;\n\nexport function criticalCompactionParameters(parameters: ContextCompactionParameters): ContextCompactionParameters {\n\treturn {\n\t\t...parameters,\n\t\tpreserve_recent: Math.max(parameters.preserve_recent, CONTEXT_CRITICAL_OVERFLOW_RECENT_ENTRY_COUNT),\n\t};\n}\n\nexport function isCriticalOverflowProtectedEntryDeletable(\n\tentry: CompactableTranscriptEntry,\n\ttranscript: CompactableTranscript,\n): boolean {\n\tif (!entry.protected) return true;\n\tconst entryIndex = transcript.entries.findIndex((candidate) => candidate.entryId === entry.entryId);\n\tif (entryIndex < 0) return false;\n\tconst recentBoundary = Math.max(0, transcript.entries.length - CONTEXT_CRITICAL_OVERFLOW_RECENT_ENTRY_COUNT);\n\tif (entryIndex >= recentBoundary) return false;\n\tif (hasAssistantError(entry.message) || hasToolResultError(entry.message) || hasFailedBashExecution(entry.message)) {\n\t\treturn false;\n\t}\n\treturn isTaskBearingEntry(entry);\n}\n\n/**\n * Return a transcript for critical overflow passes. The effective recent guard is\n * widened to max(configured preserve_recent, 5) over all entries, restoring the\n * pre-#1399 critical-overflow last-5 floor through the existing recent-target\n * validation path. Only stale protected task-bearing entries outside that floor\n * are relaxed; `protectedEntryIds` is rebuilt from still-protected entries so\n * persisted results/events stay disjoint from deleted targets.\n */\nexport function relaxTranscriptForCriticalEviction(transcript: CompactableTranscript): CompactableTranscript {\n\tconst entries = transcript.entries.map((entry) => {\n\t\tif (!entry.protected || !isCriticalOverflowProtectedEntryDeletable(entry, transcript)) return entry;\n\t\treturn {\n\t\t\t...entry,\n\t\t\tprotected: false,\n\t\t\tcontentBlocks: entry.contentBlocks.map((block) => ({ ...block, protected: false })),\n\t\t};\n\t});\n\tconst parameters = criticalCompactionParameters(getTranscriptCompactionParameters(transcript));\n\treturn {\n\t\t...transcript,\n\t\tentries,\n\t\tparameters,\n\t\tprotectedEntryIds: entries.filter((entry) => entry.protected).map((entry) => entry.entryId),\n\t};\n}\n\nexport const CONTEXT_COMPACTION_CRITICAL_OVERFLOW_PROMPT = `\n<critical-overflow-mode>\nThe previous model request overflowed its context window. This is a critical LRU-style compaction pass. First delete stale unprotected context. If that is not enough, you may also delete the earliest formerly-protected entries or protected content shown in the manifest. Evict old low-signal context first, then older user/custom/summary context while preserving recent entries, unresolved errors, failed commands, and enough task-bearing context for the assistant to continue.\n\nSafety invariant: each provider-visible user-like input starts a logical assistant tool-use turn: a non-empty user/custom input, a context-eligible bash execution, or a non-empty branch summary. Empty or whitespace-only user/custom inputs and empty branch summaries do not split turns; whitespace-only branch summaries remain visible through their wrapper. Assistant messages and intervening tool results remain in the turn until the next visible input. Never delete a thinking/redacted_thinking-bearing assistant entry from the current final logical turn; a trailing visible input without an assistant response makes the preceding assistant turn historical. For a completed historical turn, retain every thinking/redacted_thinking-bearing assistant entry or delete all of them as whole entries. Never retain a proper subset or partially delete a retained thinking-bearing assistant message.\n</critical-overflow-mode>`;\n"]}
@@ -0,0 +1,18 @@
1
+ import type { ContextDeletionTarget } from "../session-manager.js";
2
+ import type { CompactableTranscript, ValidatedContextDeletionResult } from "./context-compaction-types.js";
3
+ export interface EvictionGroup {
4
+ entryIds: string[];
5
+ order: number;
6
+ tokens: number;
7
+ boundary: boolean;
8
+ }
9
+ export declare function hasFitBudget(result: ValidatedContextDeletionResult | undefined, tokenBudget: number): result is ValidatedContextDeletionResult;
10
+ export declare function assistantTurnsForTargets(transcript: CompactableTranscript, targets: readonly ContextDeletionTarget[]): import("./context-assistant-turns.js").AssistantToolUseTurn[];
11
+ export declare function repairSignedTurnTargets(transcript: CompactableTranscript, targets: readonly ContextDeletionTarget[]): ContextDeletionTarget[];
12
+ export declare function targetsWithGroup(planned: readonly ContextDeletionTarget[], entryIds: readonly string[]): ContextDeletionTarget[];
13
+ export declare function validateTargets(transcript: CompactableTranscript, targets: readonly ContextDeletionTarget[]): ValidatedContextDeletionResult | undefined;
14
+ export declare function tryGroup(transcript: CompactableTranscript, planned: readonly ContextDeletionTarget[], group: EvictionGroup, repairBoundary: boolean): ValidatedContextDeletionResult | undefined;
15
+ export declare function currentHistoricalSignedGroups(transcript: CompactableTranscript, planned: readonly ContextDeletionTarget[]): EvictionGroup[];
16
+ export declare function skippedBoundaryRestorationGroups(transcript: CompactableTranscript, planned: readonly ContextDeletionTarget[], skipped: readonly EvictionGroup[]): EvictionGroup[][];
17
+ export declare function alternateBoundaryPlan(transcript: CompactableTranscript, initialTargets: readonly ContextDeletionTarget[], initialResult: ValidatedContextDeletionResult | undefined, groups: readonly EvictionGroup[], tokenBudget: number): ValidatedContextDeletionResult | undefined;
18
+ //# sourceMappingURL=context-compaction-eviction-alternates.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"context-compaction-eviction-alternates.d.ts","sourceRoot":"","sources":["../../../src/core/compaction/context-compaction-eviction-alternates.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,qBAAqB,EAAE,MAAM,uBAAuB,CAAC;AAEnE,OAAO,KAAK,EAAE,qBAAqB,EAAE,8BAA8B,EAAE,MAAM,+BAA+B,CAAC;AAQ3G,MAAM,WAAW,aAAa;IAC7B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,OAAO,CAAC;CAClB;AAED,wBAAgB,YAAY,CAC3B,MAAM,EAAE,8BAA8B,GAAG,SAAS,EAClD,WAAW,EAAE,MAAM,GACjB,MAAM,IAAI,8BAA8B,CAE1C;AAED,wBAAgB,wBAAwB,CACvC,UAAU,EAAE,qBAAqB,EACjC,OAAO,EAAE,SAAS,qBAAqB,EAAE,iEAGzC;AAED,wBAAgB,uBAAuB,CACtC,UAAU,EAAE,qBAAqB,EACjC,OAAO,EAAE,SAAS,qBAAqB,EAAE,GACvC,qBAAqB,EAAE,CA0BzB;AAED,wBAAgB,gBAAgB,CAC/B,OAAO,EAAE,SAAS,qBAAqB,EAAE,EACzC,QAAQ,EAAE,SAAS,MAAM,EAAE,GACzB,qBAAqB,EAAE,CAQzB;AAED,wBAAgB,eAAe,CAC9B,UAAU,EAAE,qBAAqB,EACjC,OAAO,EAAE,SAAS,qBAAqB,EAAE,GACvC,8BAA8B,GAAG,SAAS,CAmB5C;AAED,wBAAgB,QAAQ,CACvB,UAAU,EAAE,qBAAqB,EACjC,OAAO,EAAE,SAAS,qBAAqB,EAAE,EACzC,KAAK,EAAE,aAAa,EACpB,cAAc,EAAE,OAAO,GACrB,8BAA8B,GAAG,SAAS,CAU5C;AAED,wBAAgB,6BAA6B,CAC5C,UAAU,EAAE,qBAAqB,EACjC,OAAO,EAAE,SAAS,qBAAqB,EAAE,GACvC,aAAa,EAAE,CAejB;AA0BD,wBAAgB,gCAAgC,CAC/C,UAAU,EAAE,qBAAqB,EACjC,OAAO,EAAE,SAAS,qBAAqB,EAAE,EACzC,OAAO,EAAE,SAAS,aAAa,EAAE,GAC/B,aAAa,EAAE,EAAE,CAUnB;AA8BD,wBAAgB,qBAAqB,CACpC,UAAU,EAAE,qBAAqB,EACjC,cAAc,EAAE,SAAS,qBAAqB,EAAE,EAChD,aAAa,EAAE,8BAA8B,GAAG,SAAS,EACzD,MAAM,EAAE,SAAS,aAAa,EAAE,EAChC,WAAW,EAAE,MAAM,GACjB,8BAA8B,GAAG,SAAS,CAuC5C","sourcesContent":["import type { ContextDeletionTarget } from \"../session-manager.js\";\nimport { analyzeCompactableAssistantTurns } from \"./context-assistant-turns.js\";\nimport type { CompactableTranscript, ValidatedContextDeletionResult } from \"./context-compaction-types.js\";\nimport { reconcileToolDependencies, validateContextDeletionRequest } from \"./context-deletion-application.js\";\nimport {\n\tcanDeleteTarget,\n\tdeletionRequestFromTargets,\n\tgetDeletedEntryIds,\n} from \"./context-deletion-targets.js\";\n\nexport interface EvictionGroup {\n\tentryIds: string[];\n\torder: number;\n\ttokens: number;\n\tboundary: boolean;\n}\n\nexport function hasFitBudget(\n\tresult: ValidatedContextDeletionResult | undefined,\n\ttokenBudget: number,\n): result is ValidatedContextDeletionResult {\n\treturn result !== undefined && result.deletedTargets.length > 0 && result.stats.tokensAfter <= tokenBudget;\n}\n\nexport function assistantTurnsForTargets(\n\ttranscript: CompactableTranscript,\n\ttargets: readonly ContextDeletionTarget[],\n) {\n\treturn analyzeCompactableAssistantTurns(transcript, targets).turns;\n}\n\nexport function repairSignedTurnTargets(\n\ttranscript: CompactableTranscript,\n\ttargets: readonly ContextDeletionTarget[],\n): ContextDeletionTarget[] {\n\tconst reconciled = reconcileToolDependencies(transcript, targets);\n\tconst deletedEntryIds = getDeletedEntryIds(reconciled);\n\tconst restoredSignedIds = new Set<string>();\n\tfor (const turn of assistantTurnsForTargets(transcript, reconciled)) {\n\t\tconst deleted = turn.signedThinkingEntryIds.filter((entryId) => deletedEntryIds.has(entryId));\n\t\tif (deleted.length === 0) continue;\n\t\tif (!turn.active && deleted.length === turn.signedThinkingEntryIds.length) continue;\n\t\tfor (const entryId of turn.signedThinkingEntryIds) restoredSignedIds.add(entryId);\n\t}\n\tif (restoredSignedIds.size === 0) return reconciled;\n\n\tconst restoredCallIds = new Set<string>();\n\tfor (const entry of transcript.entries) {\n\t\tif (!restoredSignedIds.has(entry.entryId)) continue;\n\t\tfor (const callId of entry.toolCallIds) restoredCallIds.add(callId);\n\t}\n\tconst restoredResultIds = new Set(\n\t\ttranscript.entries\n\t\t\t.filter((entry) => entry.toolResultFor && restoredCallIds.has(entry.toolResultFor))\n\t\t\t.map((entry) => entry.entryId),\n\t);\n\treturn reconcileToolDependencies(\n\t\ttranscript,\n\t\treconciled.filter((target) => !restoredSignedIds.has(target.entryId) && !restoredResultIds.has(target.entryId)),\n\t);\n}\n\nexport function targetsWithGroup(\n\tplanned: readonly ContextDeletionTarget[],\n\tentryIds: readonly string[],\n): ContextDeletionTarget[] {\n\tconst deleted = getDeletedEntryIds(planned);\n\treturn [\n\t\t...planned,\n\t\t...entryIds\n\t\t\t.filter((entryId) => !deleted.has(entryId))\n\t\t\t.map((entryId): ContextDeletionTarget => ({ kind: \"entry\", entryId })),\n\t];\n}\n\nexport function validateTargets(\n\ttranscript: CompactableTranscript,\n\ttargets: readonly ContextDeletionTarget[],\n): ValidatedContextDeletionResult | undefined {\n\ttry {\n\t\tconst result = validateContextDeletionRequest(deletionRequestFromTargets(targets), transcript);\n\t\tconst order = new Map(transcript.entries.map((entry, index) => [entry.entryId, index]));\n\t\treturn {\n\t\t\t...result,\n\t\t\tdeletedTargets: [...result.deletedTargets].sort((left, right) => {\n\t\t\t\tconst entryDelta =\n\t\t\t\t\t(order.get(left.entryId) ?? Number.MAX_SAFE_INTEGER) -\n\t\t\t\t\t(order.get(right.entryId) ?? Number.MAX_SAFE_INTEGER);\n\t\t\t\tif (entryDelta !== 0) return entryDelta;\n\t\t\t\tconst leftBlock = left.kind === \"content_block\" ? left.blockIndex : -1;\n\t\t\t\tconst rightBlock = right.kind === \"content_block\" ? right.blockIndex : -1;\n\t\t\t\treturn leftBlock - rightBlock;\n\t\t\t}),\n\t\t};\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nexport function tryGroup(\n\ttranscript: CompactableTranscript,\n\tplanned: readonly ContextDeletionTarget[],\n\tgroup: EvictionGroup,\n\trepairBoundary: boolean,\n): ValidatedContextDeletionResult | undefined {\n\tconst directTargets = targetsWithGroup(planned, group.entryIds);\n\tconst direct = validateTargets(transcript, directTargets);\n\tif (direct) return direct;\n\tif (!repairBoundary) return undefined;\n\tconst repaired = repairSignedTurnTargets(transcript, directTargets);\n\tconst result = validateTargets(transcript, repaired);\n\tif (!result) return undefined;\n\tconst deleted = getDeletedEntryIds(result.deletedTargets);\n\treturn group.entryIds.every((entryId) => deleted.has(entryId)) ? result : undefined;\n}\n\nexport function currentHistoricalSignedGroups(\n\ttranscript: CompactableTranscript,\n\tplanned: readonly ContextDeletionTarget[],\n): EvictionGroup[] {\n\tconst deleted = getDeletedEntryIds(planned);\n\tconst order = new Map(transcript.entries.map((entry, index) => [entry.entryId, index]));\n\tconst tokens = new Map(transcript.entries.map((entry) => [entry.entryId, entry.tokenEstimate]));\n\treturn assistantTurnsForTargets(transcript, planned)\n\t\t.filter((turn) => !turn.active)\n\t\t.map((turn) => turn.signedThinkingEntryIds.filter((entryId) => !deleted.has(entryId)))\n\t\t.filter((entryIds) => entryIds.length > 0)\n\t\t.filter((entryIds) => entryIds.every((entryId) => canDeleteTarget(transcript, { kind: \"entry\", entryId })))\n\t\t.map((entryIds) => ({\n\t\t\tentryIds,\n\t\t\torder: order.get(entryIds[0]!) ?? Number.MAX_SAFE_INTEGER,\n\t\t\ttokens: entryIds.reduce((sum, entryId) => sum + (tokens.get(entryId) ?? 0), 0),\n\t\t\tboundary: false,\n\t\t}));\n}\n\nfunction targetIdentity(target: ContextDeletionTarget): string {\n\treturn target.kind === \"entry\"\n\t\t? `entry:${target.entryId}`\n\t\t: `content_block:${target.entryId}:${target.blockIndex}`;\n}\n\nfunction boundaryRestorationSignature(\n\ttranscript: CompactableTranscript,\n\tplanned: readonly ContextDeletionTarget[],\n\tgroup: EvictionGroup,\n): string | undefined {\n\ttry {\n\t\tconst repaired = repairSignedTurnTargets(transcript, targetsWithGroup(planned, group.entryIds));\n\t\tconst retainedKeys = new Set(repaired.map(targetIdentity));\n\t\tconst restoredKeys = planned\n\t\t\t.map(targetIdentity)\n\t\t\t.filter((key) => !retainedKeys.has(key))\n\t\t\t.sort();\n\t\treturn restoredKeys.length > 0 ? restoredKeys.join(\"|\") : undefined;\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nexport function skippedBoundaryRestorationGroups(\n\ttranscript: CompactableTranscript,\n\tplanned: readonly ContextDeletionTarget[],\n\tskipped: readonly EvictionGroup[],\n): EvictionGroup[][] {\n\tconst bySignature = new Map<string, EvictionGroup[]>();\n\tfor (const group of skipped) {\n\t\tconst signature = boundaryRestorationSignature(transcript, planned, group);\n\t\tif (!signature) continue;\n\t\tconst matches = bySignature.get(signature) ?? [];\n\t\tmatches.push(group);\n\t\tbySignature.set(signature, matches);\n\t}\n\treturn [...bySignature.values()].filter((groups) => groups.length > 1);\n}\n\ninterface AlternateBoundaryState {\n\ttargets: ContextDeletionTarget[];\n\ttokensAfter: number;\n\tsignature: string;\n}\n\nfunction deletionPlanSignature(targets: readonly ContextDeletionTarget[]): string {\n\treturn targets.map(targetIdentity).sort().join(\"|\");\n}\n\nconst ALTERNATE_BOUNDARY_STATE_LIMIT = 16;\n\nfunction retainBoundedAlternateStates(states: readonly AlternateBoundaryState[]): AlternateBoundaryState[] {\n\tconst deduplicated = [...new Map(states.map((state) => [state.signature, state])).values()].sort(\n\t\t(left, right) => left.tokensAfter - right.tokensAfter || left.signature.localeCompare(right.signature),\n\t);\n\t// Retain both near-fit and less-committed plans so different turn topologies survive the bounded search.\n\tif (deduplicated.length <= ALTERNATE_BOUNDARY_STATE_LIMIT) return deduplicated;\n\n\tconst retained: AlternateBoundaryState[] = [];\n\tfor (let index = 0; index < ALTERNATE_BOUNDARY_STATE_LIMIT / 2; index++) {\n\t\tretained.push(deduplicated[index]!, deduplicated[deduplicated.length - 1 - index]!);\n\t}\n\treturn retained.sort(\n\t\t(left, right) => left.tokensAfter - right.tokensAfter || left.signature.localeCompare(right.signature),\n\t);\n}\n\nexport function alternateBoundaryPlan(\n\ttranscript: CompactableTranscript,\n\tinitialTargets: readonly ContextDeletionTarget[],\n\tinitialResult: ValidatedContextDeletionResult | undefined,\n\tgroups: readonly EvictionGroup[],\n\ttokenBudget: number,\n): ValidatedContextDeletionResult | undefined {\n\tlet states: AlternateBoundaryState[] = [\n\t\t{\n\t\t\ttargets: [...initialTargets],\n\t\t\ttokensAfter: initialResult?.stats.tokensAfter ?? transcript.tokensBefore,\n\t\t\tsignature: deletionPlanSignature(initialTargets),\n\t\t},\n\t];\n\n\tfor (const group of groups) {\n\t\tconst expanded = [...states];\n\t\tlet fitting: ValidatedContextDeletionResult | undefined;\n\t\tfor (const state of states) {\n\t\t\tconst result = tryGroup(transcript, state.targets, group, true);\n\t\t\tif (!result) continue;\n\t\t\tif (result.deletedTargets.length > 0 && result.stats.tokensAfter <= tokenBudget) {\n\t\t\t\tif (!fitting || result.stats.tokensAfter > fitting.stats.tokensAfter) fitting = result;\n\t\t\t\tcontinue;\n\t\t\t}\n\t\t\texpanded.push({\n\t\t\t\ttargets: [...result.deletedTargets],\n\t\t\t\ttokensAfter: result.stats.tokensAfter,\n\t\t\t\tsignature: deletionPlanSignature(result.deletedTargets),\n\t\t\t});\n\t\t}\n\t\tif (fitting) return fitting;\n\t\tstates = retainBoundedAlternateStates(expanded);\n\t}\n\n\tfor (const state of states) {\n\t\tlet targets = state.targets;\n\t\tfor (const group of currentHistoricalSignedGroups(transcript, targets)) {\n\t\t\tconst result = tryGroup(transcript, targets, group, false);\n\t\t\tif (!result) continue;\n\t\t\ttargets = [...result.deletedTargets];\n\t\t\tif (hasFitBudget(result, tokenBudget)) return result;\n\t\t}\n\t}\n\treturn undefined;\n}\n"]}