@bastani/atomic 0.9.5-alpha.7 → 0.9.5-alpha.9

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 (317) hide show
  1. package/CHANGELOG.md +33 -2
  2. package/dist/builtin/cursor/package.json +2 -2
  3. package/dist/builtin/intercom/CHANGELOG.md +17 -0
  4. package/dist/builtin/intercom/README.md +27 -21
  5. package/dist/builtin/intercom/broker/broker.ts +3 -6
  6. package/dist/builtin/intercom/broker/paths.ts +42 -5
  7. package/dist/builtin/intercom/broker/spawn.ts +78 -10
  8. package/dist/builtin/intercom/config.ts +6 -3
  9. package/dist/builtin/intercom/contact-supervisor-tool.ts +6 -4
  10. package/dist/builtin/intercom/index-heavy.ts +1 -1
  11. package/dist/builtin/intercom/index.ts +162 -130
  12. package/dist/builtin/intercom/intercom-tool.ts +9 -3
  13. package/dist/builtin/intercom/package.json +10 -1
  14. package/dist/builtin/intercom/result-renderers.ts +1 -1
  15. package/dist/builtin/intercom/ui/compose.ts +2 -2
  16. package/dist/builtin/intercom/ui/inline-message.ts +2 -2
  17. package/dist/builtin/intercom/ui/session-list.ts +2 -2
  18. package/dist/builtin/mcp/CHANGELOG.md +11 -0
  19. package/dist/builtin/mcp/README.md +6 -6
  20. package/dist/builtin/mcp/direct-tools.ts +41 -23
  21. package/dist/builtin/mcp/index.ts +31 -4
  22. package/dist/builtin/mcp/init.ts +4 -43
  23. package/dist/builtin/mcp/metadata-hydration.ts +37 -0
  24. package/dist/builtin/mcp/package.json +1 -1
  25. package/dist/builtin/mcp/proxy-info-modes.ts +67 -14
  26. package/dist/builtin/mcp/startup-warmup.ts +98 -0
  27. package/dist/builtin/mcp/tool-result-renderer.ts +3 -3
  28. package/dist/builtin/subagents/CHANGELOG.md +18 -0
  29. package/dist/builtin/subagents/package.json +1 -1
  30. package/dist/builtin/subagents/src/extension/index.ts +14 -25
  31. package/dist/builtin/subagents/src/extension/startup-maintenance.ts +89 -0
  32. package/dist/builtin/subagents/src/runs/background/async-execution-common.ts +23 -18
  33. package/dist/builtin/subagents/src/runs/background/result-watcher.ts +20 -10
  34. package/dist/builtin/subagents/src/runs/background/subagent-runner-streaming.ts +13 -3
  35. package/dist/builtin/subagents/src/runs/foreground/execution-attempt.ts +10 -2
  36. package/dist/builtin/subagents/src/runs/shared/pi-spawn.ts +28 -1
  37. package/dist/builtin/subagents/src/tui/render-event-formatting.ts +15 -48
  38. package/dist/builtin/subagents/src/tui/render-layout.ts +9 -0
  39. package/dist/builtin/subagents/src/tui/render-widget-graph.ts +27 -25
  40. package/dist/builtin/subagents/src/tui/render-widget.ts +72 -66
  41. package/dist/builtin/subagents/src/tui/render.ts +1 -1
  42. package/dist/builtin/web-access/package.json +1 -1
  43. package/dist/builtin/workflows/CHANGELOG.md +29 -0
  44. package/dist/builtin/workflows/README.md +4 -2
  45. package/dist/builtin/workflows/builtin/goal-artifacts.ts +3 -1
  46. package/dist/builtin/workflows/builtin/goal-prompts.ts +23 -2
  47. package/dist/builtin/workflows/builtin/goal-reducer.ts +21 -0
  48. package/dist/builtin/workflows/builtin/goal-review.ts +60 -6
  49. package/dist/builtin/workflows/builtin/goal-runner.ts +71 -22
  50. package/dist/builtin/workflows/builtin/goal-types.ts +7 -1
  51. package/dist/builtin/workflows/builtin/ralph-core.ts +52 -4
  52. package/dist/builtin/workflows/builtin/ralph-review-gate.ts +10 -3
  53. package/dist/builtin/workflows/builtin/ralph-reviewer-prompt.ts +23 -2
  54. package/dist/builtin/workflows/builtin/ralph-runner.ts +38 -16
  55. package/dist/builtin/workflows/builtin/review-convergence.ts +111 -0
  56. package/dist/builtin/workflows/package.json +1 -1
  57. package/dist/builtin/workflows/skills/create-spec/SKILL.md +2 -0
  58. package/dist/builtin/workflows/skills/impeccable/SKILL.md +16 -13
  59. package/dist/builtin/workflows/skills/impeccable/reference/adapt.md +1 -0
  60. package/dist/builtin/workflows/skills/impeccable/reference/adapt.native.md +58 -0
  61. package/dist/builtin/workflows/skills/impeccable/reference/android.md +40 -0
  62. package/dist/builtin/workflows/skills/impeccable/reference/animate.md +2 -0
  63. package/dist/builtin/workflows/skills/impeccable/reference/audit.md +2 -0
  64. package/dist/builtin/workflows/skills/impeccable/reference/audit.native.md +139 -0
  65. package/dist/builtin/workflows/skills/impeccable/reference/bolder.md +69 -62
  66. package/dist/builtin/workflows/skills/impeccable/reference/critique.md +21 -7
  67. package/dist/builtin/workflows/skills/impeccable/reference/document.md +1 -1
  68. package/dist/builtin/workflows/skills/impeccable/reference/hooks.md +92 -0
  69. package/dist/builtin/workflows/skills/impeccable/reference/init.md +29 -7
  70. package/dist/builtin/workflows/skills/impeccable/reference/ios.md +45 -0
  71. package/dist/builtin/workflows/skills/impeccable/reference/layout.md +25 -1
  72. package/dist/builtin/workflows/skills/impeccable/reference/typeset.md +23 -1
  73. package/dist/builtin/workflows/skills/impeccable/scripts/context-signals.mjs +2 -1
  74. package/dist/builtin/workflows/skills/impeccable/scripts/context.mjs +75 -14
  75. package/dist/builtin/workflows/skills/impeccable/scripts/detector/browser/injected/index.mjs +1 -1
  76. package/dist/builtin/workflows/skills/impeccable/scripts/detector/cli/main.mjs +31 -0
  77. package/dist/builtin/workflows/skills/impeccable/scripts/detector/design-system.mjs +64 -0
  78. package/dist/builtin/workflows/skills/impeccable/scripts/detector/detect-antipatterns-browser.js +118 -11
  79. package/dist/builtin/workflows/skills/impeccable/scripts/detector/engines/regex/detect-text.mjs +16 -11
  80. package/dist/builtin/workflows/skills/impeccable/scripts/detector/registry/antipatterns.mjs +66 -0
  81. package/dist/builtin/workflows/skills/impeccable/scripts/detector/rules/checks.mjs +42 -10
  82. package/dist/builtin/workflows/skills/impeccable/scripts/detector/shared/fonts.mjs +30 -0
  83. package/dist/builtin/workflows/skills/impeccable/scripts/detector/shared/page.mjs +4 -6
  84. package/dist/builtin/workflows/skills/impeccable/scripts/hook-admin.mjs +660 -0
  85. package/dist/builtin/workflows/skills/impeccable/scripts/hook-before-edit.mjs +516 -0
  86. package/dist/builtin/workflows/skills/impeccable/scripts/hook-lib.mjs +1764 -0
  87. package/dist/builtin/workflows/skills/impeccable/scripts/hook.mjs +61 -0
  88. package/dist/builtin/workflows/skills/impeccable/scripts/lib/impeccable-config.mjs +4 -2
  89. package/dist/builtin/workflows/skills/impeccable/scripts/lib/is-generated.mjs +2 -2
  90. package/dist/builtin/workflows/skills/impeccable/scripts/live/svelte-component.mjs +13 -16
  91. package/dist/builtin/workflows/skills/impeccable/scripts/live-accept.mjs +17 -11
  92. package/dist/builtin/workflows/skills/impeccable/scripts/live-browser.js +104 -37
  93. package/dist/builtin/workflows/skills/impeccable/scripts/live-wrap.mjs +9 -0
  94. package/dist/builtin/workflows/src/durable/backend.ts +12 -0
  95. package/dist/builtin/workflows/src/durable/child-primitive.ts +12 -8
  96. package/dist/builtin/workflows/src/durable/dbos-backend.ts +63 -2
  97. package/dist/builtin/workflows/src/durable/dbos-envelope.ts +28 -0
  98. package/dist/builtin/workflows/src/durable/resume-catalog.ts +12 -0
  99. package/dist/builtin/workflows/src/durable/resume-runtime.ts +5 -2
  100. package/dist/builtin/workflows/src/durable/stage-primitive.ts +165 -27
  101. package/dist/builtin/workflows/src/durable/types.ts +30 -1
  102. package/dist/builtin/workflows/src/engine/primitives/task.ts +1 -0
  103. package/dist/builtin/workflows/src/engine/run-durable-finalize.ts +1 -2
  104. package/dist/builtin/workflows/src/engine/run-returned-status.ts +50 -16
  105. package/dist/builtin/workflows/src/engine/run.ts +32 -32
  106. package/dist/builtin/workflows/src/engine/runtime.ts +5 -0
  107. package/dist/builtin/workflows/src/extension/extension-factory.ts +3 -0
  108. package/dist/builtin/workflows/src/extension/extension-lifecycle.ts +9 -2
  109. package/dist/builtin/workflows/src/extension/extension-runtime-state.ts +137 -27
  110. package/dist/builtin/workflows/src/extension/lifecycle-notifications.ts +22 -6
  111. package/dist/builtin/workflows/src/extension/public-types.ts +1 -1
  112. package/dist/builtin/workflows/src/extension/workflow-command-completions.ts +13 -1
  113. package/dist/builtin/workflows/src/extension/workflow-command-registration.ts +19 -4
  114. package/dist/builtin/workflows/src/extension/workflow-command-surfaces.ts +9 -0
  115. package/dist/builtin/workflows/src/extension/workflow-prompts.ts +2 -0
  116. package/dist/builtin/workflows/src/extension/workflow-run-control-command.ts +48 -23
  117. package/dist/builtin/workflows/src/extension/workflow-tool-control.ts +20 -8
  118. package/dist/builtin/workflows/src/extension/workflow-tool.ts +24 -8
  119. package/dist/builtin/workflows/src/runs/background/status.ts +4 -3
  120. package/dist/builtin/workflows/src/runs/foreground/executor-continuation.ts +13 -3
  121. package/dist/builtin/workflows/src/runs/foreground/executor-direct-helpers.ts +29 -8
  122. package/dist/builtin/workflows/src/runs/foreground/executor-hil.ts +2 -1
  123. package/dist/builtin/workflows/src/runs/foreground/executor-stage-factory.ts +4 -3
  124. package/dist/builtin/workflows/src/runs/foreground/stage-runner-controller.ts +18 -19
  125. package/dist/builtin/workflows/src/runs/foreground/stage-runner-unresolved-overflow.ts +38 -0
  126. package/dist/builtin/workflows/src/runs/shared/worktree-git.ts +155 -20
  127. package/dist/builtin/workflows/src/runs/shared/worktree-types.ts +6 -0
  128. package/dist/builtin/workflows/src/runs/shared/worktree.ts +2 -1
  129. package/dist/builtin/workflows/src/shared/persistence-restore-helpers.ts +18 -1
  130. package/dist/builtin/workflows/src/shared/persistence-restore.ts +23 -5
  131. package/dist/builtin/workflows/src/shared/persistence-session-entries.ts +4 -0
  132. package/dist/builtin/workflows/src/shared/returned-run-status.ts +116 -0
  133. package/dist/builtin/workflows/src/shared/workflow-failures-decisions.ts +2 -0
  134. package/dist/builtin/workflows/src/tui/graph-view-render-helpers.ts +21 -3
  135. package/dist/builtin/workflows/src/tui/graph-view-state.ts +3 -0
  136. package/dist/builtin/workflows/src/tui/graph-view-types.ts +3 -0
  137. package/dist/builtin/workflows/src/tui/status-list.ts +22 -15
  138. package/dist/builtin/workflows/src/tui/workflow-attach-pane.ts +4 -4
  139. package/dist/bun/internal-intercom-broker.d.ts +6 -0
  140. package/dist/bun/internal-intercom-broker.d.ts.map +1 -0
  141. package/dist/bun/internal-intercom-broker.js +39 -0
  142. package/dist/bun/internal-intercom-broker.js.map +1 -0
  143. package/dist/bun/split-loader.d.ts.map +1 -1
  144. package/dist/bun/split-loader.js +21 -8
  145. package/dist/bun/split-loader.js.map +1 -1
  146. package/dist/core/agent-session-auto-compaction.d.ts +21 -2
  147. package/dist/core/agent-session-auto-compaction.d.ts.map +1 -1
  148. package/dist/core/agent-session-auto-compaction.js +163 -11
  149. package/dist/core/agent-session-auto-compaction.js.map +1 -1
  150. package/dist/core/agent-session-events.d.ts.map +1 -1
  151. package/dist/core/agent-session-events.js +10 -1
  152. package/dist/core/agent-session-events.js.map +1 -1
  153. package/dist/core/agent-session-methods.d.ts +12 -3
  154. package/dist/core/agent-session-methods.d.ts.map +1 -1
  155. package/dist/core/agent-session-methods.js.map +1 -1
  156. package/dist/core/agent-session-models.d.ts +2 -2
  157. package/dist/core/agent-session-models.d.ts.map +1 -1
  158. package/dist/core/agent-session-models.js.map +1 -1
  159. package/dist/core/agent-session-prompt.d.ts.map +1 -1
  160. package/dist/core/agent-session-prompt.js +1 -0
  161. package/dist/core/agent-session-prompt.js.map +1 -1
  162. package/dist/core/agent-session-retry.d.ts +2 -0
  163. package/dist/core/agent-session-retry.d.ts.map +1 -1
  164. package/dist/core/agent-session-retry.js +152 -4
  165. package/dist/core/agent-session-retry.js.map +1 -1
  166. package/dist/core/agent-session-services.d.ts +1 -0
  167. package/dist/core/agent-session-services.d.ts.map +1 -1
  168. package/dist/core/agent-session-services.js +1 -0
  169. package/dist/core/agent-session-services.js.map +1 -1
  170. package/dist/core/agent-session-types.d.ts +15 -1
  171. package/dist/core/agent-session-types.d.ts.map +1 -1
  172. package/dist/core/agent-session-types.js.map +1 -1
  173. package/dist/core/agent-session.d.ts +6 -0
  174. package/dist/core/agent-session.d.ts.map +1 -1
  175. package/dist/core/agent-session.js +6 -0
  176. package/dist/core/agent-session.js.map +1 -1
  177. package/dist/core/compaction/context-compaction-runner.d.ts.map +1 -1
  178. package/dist/core/compaction/context-compaction-runner.js +36 -20
  179. package/dist/core/compaction/context-compaction-runner.js.map +1 -1
  180. package/dist/core/copilot-anthropic-sse-repair.d.ts +23 -0
  181. package/dist/core/copilot-anthropic-sse-repair.d.ts.map +1 -0
  182. package/dist/core/copilot-anthropic-sse-repair.js +340 -0
  183. package/dist/core/copilot-anthropic-sse-repair.js.map +1 -0
  184. package/dist/core/copilot-gemini-reasoning.d.ts +16 -8
  185. package/dist/core/copilot-gemini-reasoning.d.ts.map +1 -1
  186. package/dist/core/copilot-gemini-reasoning.js +27 -22
  187. package/dist/core/copilot-gemini-reasoning.js.map +1 -1
  188. package/dist/core/copilot-hosts.d.ts +12 -0
  189. package/dist/core/copilot-hosts.d.ts.map +1 -0
  190. package/dist/core/copilot-hosts.js +33 -0
  191. package/dist/core/copilot-hosts.js.map +1 -0
  192. package/dist/core/extensions/agent-events.d.ts +1 -1
  193. package/dist/core/extensions/agent-events.d.ts.map +1 -1
  194. package/dist/core/extensions/agent-events.js.map +1 -1
  195. package/dist/core/extensions/loader-virtual-modules.d.ts +8 -1
  196. package/dist/core/extensions/loader-virtual-modules.d.ts.map +1 -1
  197. package/dist/core/extensions/loader-virtual-modules.js +2 -1
  198. package/dist/core/extensions/loader-virtual-modules.js.map +1 -1
  199. package/dist/core/footer-data-provider.d.ts +6 -0
  200. package/dist/core/footer-data-provider.d.ts.map +1 -1
  201. package/dist/core/footer-data-provider.js +72 -29
  202. package/dist/core/footer-data-provider.js.map +1 -1
  203. package/dist/core/http-dispatcher.d.ts.map +1 -1
  204. package/dist/core/http-dispatcher.js +5 -5
  205. package/dist/core/http-dispatcher.js.map +1 -1
  206. package/dist/core/openai-responses-payload-sanitizer.d.ts +1 -0
  207. package/dist/core/openai-responses-payload-sanitizer.d.ts.map +1 -1
  208. package/dist/core/openai-responses-payload-sanitizer.js +24 -9
  209. package/dist/core/openai-responses-payload-sanitizer.js.map +1 -1
  210. package/dist/core/sdk-types.d.ts +2 -0
  211. package/dist/core/sdk-types.d.ts.map +1 -1
  212. package/dist/core/sdk-types.js.map +1 -1
  213. package/dist/core/sdk.d.ts.map +1 -1
  214. package/dist/core/sdk.js +1 -0
  215. package/dist/core/sdk.js.map +1 -1
  216. package/dist/core/settings-manager-basic-accessors.d.ts +1 -0
  217. package/dist/core/settings-manager-basic-accessors.d.ts.map +1 -1
  218. package/dist/core/settings-manager-basic-accessors.js +6 -0
  219. package/dist/core/settings-manager-basic-accessors.js.map +1 -1
  220. package/dist/core/settings-types.d.ts +1 -0
  221. package/dist/core/settings-types.d.ts.map +1 -1
  222. package/dist/core/settings-types.js.map +1 -1
  223. package/dist/core/slash-commands.d.ts +2 -0
  224. package/dist/core/slash-commands.d.ts.map +1 -1
  225. package/dist/core/slash-commands.js +141 -0
  226. package/dist/core/slash-commands.js.map +1 -1
  227. package/dist/index-extensions.d.ts +1 -1
  228. package/dist/index-extensions.d.ts.map +1 -1
  229. package/dist/index-extensions.js.map +1 -1
  230. package/dist/index.d.ts +1 -0
  231. package/dist/index.d.ts.map +1 -1
  232. package/dist/index.js +1 -0
  233. package/dist/index.js.map +1 -1
  234. package/dist/main-deferred-startup.d.ts +12 -0
  235. package/dist/main-deferred-startup.d.ts.map +1 -1
  236. package/dist/main-deferred-startup.js +23 -2
  237. package/dist/main-deferred-startup.js.map +1 -1
  238. package/dist/main-early-input.d.ts +41 -0
  239. package/dist/main-early-input.d.ts.map +1 -0
  240. package/dist/main-early-input.js +154 -0
  241. package/dist/main-early-input.js.map +1 -0
  242. package/dist/main.d.ts +0 -6
  243. package/dist/main.d.ts.map +1 -1
  244. package/dist/main.js +20 -14
  245. package/dist/main.js.map +1 -1
  246. package/dist/modes/interactive/components/chat-session-host-actions.d.ts.map +1 -1
  247. package/dist/modes/interactive/components/chat-session-host-actions.js +0 -1
  248. package/dist/modes/interactive/components/chat-session-host-actions.js.map +1 -1
  249. package/dist/modes/interactive/components/chat-session-host-events.d.ts.map +1 -1
  250. package/dist/modes/interactive/components/chat-session-host-events.js +15 -0
  251. package/dist/modes/interactive/components/chat-session-host-events.js.map +1 -1
  252. package/dist/modes/interactive/interactive-agent-events.d.ts.map +1 -1
  253. package/dist/modes/interactive/interactive-agent-events.js +25 -0
  254. package/dist/modes/interactive/interactive-agent-events.js.map +1 -1
  255. package/dist/modes/interactive/interactive-autocomplete.d.ts.map +1 -1
  256. package/dist/modes/interactive/interactive-autocomplete.js +21 -9
  257. package/dist/modes/interactive/interactive-autocomplete.js.map +1 -1
  258. package/dist/modes/interactive/interactive-deferred-startup.d.ts.map +1 -1
  259. package/dist/modes/interactive/interactive-deferred-startup.js +41 -28
  260. package/dist/modes/interactive/interactive-deferred-startup.js.map +1 -1
  261. package/dist/modes/interactive/interactive-hotkeys-debug.d.ts.map +1 -1
  262. package/dist/modes/interactive/interactive-hotkeys-debug.js +1 -0
  263. package/dist/modes/interactive/interactive-hotkeys-debug.js.map +1 -1
  264. package/dist/modes/interactive/interactive-input-handling.d.ts.map +1 -1
  265. package/dist/modes/interactive/interactive-input-handling.js +318 -185
  266. package/dist/modes/interactive/interactive-input-handling.js.map +1 -1
  267. package/dist/modes/interactive/interactive-mode-base.d.ts +12 -0
  268. package/dist/modes/interactive/interactive-mode-base.d.ts.map +1 -1
  269. package/dist/modes/interactive/interactive-mode-base.js +39 -0
  270. package/dist/modes/interactive/interactive-mode-base.js.map +1 -1
  271. package/dist/modes/interactive/interactive-mode-deps.d.ts +1 -1
  272. package/dist/modes/interactive/interactive-mode-deps.d.ts.map +1 -1
  273. package/dist/modes/interactive/interactive-mode-deps.js +1 -1
  274. package/dist/modes/interactive/interactive-mode-deps.js.map +1 -1
  275. package/dist/modes/interactive/interactive-mode-surface.d.ts +6 -2
  276. package/dist/modes/interactive/interactive-mode-surface.d.ts.map +1 -1
  277. package/dist/modes/interactive/interactive-mode-surface.js.map +1 -1
  278. package/dist/modes/interactive/interactive-mode-types.d.ts +3 -0
  279. package/dist/modes/interactive/interactive-mode-types.d.ts.map +1 -1
  280. package/dist/modes/interactive/interactive-mode-types.js.map +1 -1
  281. package/dist/modes/interactive/interactive-model-routing.d.ts.map +1 -1
  282. package/dist/modes/interactive/interactive-model-routing.js +3 -1
  283. package/dist/modes/interactive/interactive-model-routing.js.map +1 -1
  284. package/dist/modes/interactive/interactive-process-lifecycle.d.ts.map +1 -1
  285. package/dist/modes/interactive/interactive-process-lifecycle.js +0 -3
  286. package/dist/modes/interactive/interactive-process-lifecycle.js.map +1 -1
  287. package/dist/modes/interactive/interactive-render-chat.d.ts.map +1 -1
  288. package/dist/modes/interactive/interactive-render-chat.js +36 -10
  289. package/dist/modes/interactive/interactive-render-chat.js.map +1 -1
  290. package/dist/modes/interactive/interactive-session-routing.d.ts.map +1 -1
  291. package/dist/modes/interactive/interactive-session-routing.js +5 -1
  292. package/dist/modes/interactive/interactive-session-routing.js.map +1 -1
  293. package/dist/modes/interactive/interactive-startup.d.ts.map +1 -1
  294. package/dist/modes/interactive/interactive-startup.js +26 -22
  295. package/dist/modes/interactive/interactive-startup.js.map +1 -1
  296. package/dist/modes/interactive/theme/global-theme.d.ts.map +1 -1
  297. package/dist/modes/interactive/theme/global-theme.js +26 -2
  298. package/dist/modes/interactive/theme/global-theme.js.map +1 -1
  299. package/dist/utils/fs-watch.d.ts +24 -1
  300. package/dist/utils/fs-watch.d.ts.map +1 -1
  301. package/dist/utils/fs-watch.js +60 -5
  302. package/dist/utils/fs-watch.js.map +1 -1
  303. package/docs/compaction.md +8 -0
  304. package/docs/development.md +4 -0
  305. package/docs/extensions.md +5 -0
  306. package/docs/json.md +3 -1
  307. package/docs/providers.md +1 -0
  308. package/docs/quickstart.md +1 -1
  309. package/docs/rpc.md +3 -1
  310. package/docs/settings.md +27 -1
  311. package/docs/subagents.md +4 -0
  312. package/docs/terminal-setup.md +4 -0
  313. package/docs/usage.md +2 -2
  314. package/docs/windows.md +4 -0
  315. package/docs/workflows.md +21 -9
  316. package/npm-shrinkwrap.json +513 -23
  317. package/package.json +3 -2
@@ -27,6 +27,7 @@
27
27
  */
28
28
 
29
29
  import type { RunSnapshot, StageSnapshot, StageStatus } from "../shared/store-types.js";
30
+ import { effectiveRunStatus } from "../shared/returned-run-status.js";
30
31
  import { elapsedRunMs, elapsedStageMs } from "../shared/timing.js";
31
32
  import type { GraphTheme } from "./graph-theme.js";
32
33
  import { fmtDuration } from "./status-helpers.js";
@@ -162,7 +163,7 @@ function renderRunEntry(
162
163
  function runAccent(run: RunSnapshot, theme?: GraphTheme): string {
163
164
  if (!theme) return "#000000";
164
165
  if (isQuitRun(run)) return theme.warning;
165
- switch (run.status) {
166
+ switch (effectiveRunStatus(run)) {
166
167
  case "completed": return theme.success;
167
168
  case "running": return theme.warning;
168
169
  case "paused": return theme.warning;
@@ -178,7 +179,7 @@ function runAccent(run: RunSnapshot, theme?: GraphTheme): string {
178
179
 
179
180
  function runTrailing(run: RunSnapshot, theme?: GraphTheme): { text: string; fg?: string } | undefined {
180
181
  if (isQuitRun(run)) return { text: "○ quit", fg: theme?.warning };
181
- switch (run.status) {
182
+ switch (effectiveRunStatus(run)) {
182
183
  case "completed": return { text: "✓ completed", fg: theme?.success };
183
184
  case "running": return { text: "● running", fg: theme?.warning };
184
185
  case "paused": return { text: "❚❚ paused", fg: theme?.warning };
@@ -212,7 +213,7 @@ function runCardMeta(run: RunSnapshot, now: number): string {
212
213
  : undefined;
213
214
 
214
215
  if (isQuitRun(run)) return "resumable via /workflow resume";
215
- if (run.status === "running") {
216
+ if (effectiveRunStatus(run) === "running") {
216
217
  if (isChain) parts.push(`${done}/${total}`);
217
218
  const labels = runningStageLabels(run);
218
219
  if (labels) parts.push(labels);
@@ -220,7 +221,7 @@ function runCardMeta(run: RunSnapshot, now: number): string {
220
221
  return parts.join(" · ");
221
222
  }
222
223
 
223
- if (run.status === "paused") {
224
+ if (effectiveRunStatus(run) === "paused") {
224
225
  if (isChain) parts.push(`${done}/${total}`);
225
226
  const labels = pausedStageLabels(run);
226
227
  if (labels) parts.push(labels);
@@ -228,7 +229,7 @@ function runCardMeta(run: RunSnapshot, now: number): string {
228
229
  return parts.join(" · ");
229
230
  }
230
231
 
231
- if (run.status === "failed" || run.status === "killed") {
232
+ if (effectiveRunStatus(run) === "failed" || effectiveRunStatus(run) === "killed") {
232
233
  const failed = run.stages.find((s) => s.status === "failed");
233
234
  if (failed && isChain) parts.push(`failed at ${failed.name}`);
234
235
  else if (failed) parts.push(failed.name);
@@ -239,7 +240,7 @@ function runCardMeta(run: RunSnapshot, now: number): string {
239
240
  return parts.join(" · ");
240
241
  }
241
242
 
242
- if (run.status === "completed" || run.status === "skipped" || run.status === "cancelled" || run.status === "blocked") {
243
+ if (["completed", "skipped", "cancelled", "blocked"].includes(effectiveRunStatus(run))) {
243
244
  if (run.exitReason !== undefined && run.exitReason.length > 0) parts.push(run.exitReason);
244
245
  if (!isChain && run.stages[0]) parts.push(run.stages[0].name);
245
246
  const dur = lastStageDuration(run, now);
@@ -292,7 +293,7 @@ function stageCells(run: RunSnapshot): Array<{ status: StageStatus }> {
292
293
  }
293
294
 
294
295
  function stageStatusFromRun(run: RunSnapshot): StageStatus {
295
- switch (run.status) {
296
+ switch (effectiveRunStatus(run)) {
296
297
  case "completed": return "completed";
297
298
  case "skipped": return "skipped";
298
299
  case "cancelled": return "skipped";
@@ -324,22 +325,26 @@ interface Counts {
324
325
  paused: number;
325
326
  quit: number;
326
327
  completed: number;
328
+ blocked: number;
327
329
  failed: number;
328
330
  pending: number;
329
331
  }
330
332
 
331
333
  function countBuckets(runs: readonly RunSnapshot[]): Counts {
332
- const c: Counts = { active: 0, paused: 0, quit: 0, completed: 0, failed: 0, pending: 0 };
334
+ const c: Counts = { active: 0, paused: 0, quit: 0, completed: 0, blocked: 0, failed: 0, pending: 0 };
333
335
  for (const r of runs) {
336
+ const status = effectiveRunStatus(r);
334
337
  if (isQuitRun(r)) c.quit++;
335
338
  else if (r.endedAt === undefined) {
336
- if (r.status === "pending") c.pending++;
337
- else if (r.status === "paused") c.paused++;
338
- else if (r.status === "running") c.active++;
339
- else if (r.status === "completed") c.completed++;
339
+ if (status === "pending") c.pending++;
340
+ else if (status === "paused") c.paused++;
341
+ else if (status === "running") c.active++;
342
+ else if (status === "completed") c.completed++;
343
+ else if (status === "blocked") c.blocked++;
340
344
  else c.failed++;
341
- } else if (r.status === "completed") c.completed++;
342
- else if (r.status === "skipped" || r.status === "cancelled" || r.status === "blocked") c.completed++;
345
+ } else if (status === "completed") c.completed++;
346
+ else if (status === "blocked") c.blocked++;
347
+ else if (status === "skipped" || status === "cancelled") c.completed++;
343
348
  else c.failed++;
344
349
  }
345
350
  return c;
@@ -348,6 +353,7 @@ function countBuckets(runs: readonly RunSnapshot[]): Counts {
348
353
  function themedBadges(c: Counts, theme: GraphTheme): FlatBandBadge[] {
349
354
  const out: FlatBandBadge[] = [];
350
355
  if (c.completed > 0) out.push({ text: `✓ ${c.completed}`, fg: theme.success });
356
+ if (c.blocked > 0) out.push({ text: `↑ ${c.blocked} blocked`, fg: theme.warning });
351
357
  if (c.active > 0) out.push({ text: `● ${c.active}`, fg: theme.warning });
352
358
  // Keep the word label: the pause glyph is less familiar than the other
353
359
  // status glyphs, so this intentional asymmetry improves scanability.
@@ -361,6 +367,7 @@ function themedBadges(c: Counts, theme: GraphTheme): FlatBandBadge[] {
361
367
  function plainBadges(c: Counts): FlatBandBadge[] {
362
368
  const out: FlatBandBadge[] = [];
363
369
  if (c.completed > 0) out.push({ text: `✓ ${c.completed}` });
370
+ if (c.blocked > 0) out.push({ text: `↑ ${c.blocked} blocked` });
364
371
  if (c.active > 0) out.push({ text: `● ${c.active}` });
365
372
  // Keep the word label: the pause glyph is less familiar than the other
366
373
  // status glyphs, so this intentional asymmetry improves scanability.
@@ -393,7 +400,7 @@ function emptyStateLine(theme?: GraphTheme): string {
393
400
 
394
401
  function statusIconForRun(run: RunSnapshot): string {
395
402
  if (isQuitRun(run)) return "○";
396
- switch (run.status) {
403
+ switch (effectiveRunStatus(run)) {
397
404
  case "completed": return "✓";
398
405
  case "skipped": return "⊘";
399
406
  case "cancelled": return "⊘";
@@ -32,10 +32,9 @@ import type { StageSnapshot, StoreSnapshot } from "../shared/store-types.js";
32
32
  import { expandWorkflowGraph } from "../shared/expanded-workflow-graph.js";
33
33
  import { WORKFLOW_STATUS_KEY } from "./workflow-status.js";
34
34
  /**
35
- * Surface used to write Pi's footer/status tag while the attach pane is
36
- * mounted. Passing `undefined` clears the slot required on dispose so
37
- * the `pi-workflows/<workflow>[/<stage>]` tag does NOT linger in every
38
- * subsequent chat message after the overlay is closed.
35
+ * Surface for the overlay footer/status tag. Passing `undefined` on dispose
36
+ * prevents `pi-workflows/<workflow>[/<stage>]` from lingering in later chat
37
+ * messages after the overlay closes.
39
38
  * cross-ref: @bastani/atomic docs/extensions.md
40
39
  * §Widgets, Status, and Footer (`ctx.ui.setStatus`).
41
40
  */
@@ -134,6 +133,7 @@ export class WorkflowAttachPane implements Component {
134
133
  initialFocusedStageId,
135
134
  getViewportRows: this.getViewportRows,
136
135
  piKeybindings: this.piKeybindings,
136
+ footerData: this.footerData,
137
137
  requestRender: () => {
138
138
  if (this.mode !== "graph") return;
139
139
  this.hostRequestRender?.();
@@ -0,0 +1,6 @@
1
+ export declare const INTERNAL_INTERCOM_BROKER_ARG = "--atomic-internal-intercom-broker";
2
+ export declare function getBundledIntercomBrokerPath(executablePath?: string): string;
3
+ export declare function isBundledIntercomBrokerPath(modulePath: string, executablePath?: string, platform?: NodeJS.Platform): boolean;
4
+ export declare function validateInternalIntercomBrokerPath(modulePath: string | undefined, executablePath?: string, platform?: NodeJS.Platform): string;
5
+ export declare function importInternalIntercomBroker(modulePath: string, executablePath?: string, platform?: NodeJS.Platform): Promise<void>;
6
+ //# sourceMappingURL=internal-intercom-broker.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"internal-intercom-broker.d.ts","sourceRoot":"","sources":["../../src/bun/internal-intercom-broker.ts"],"names":[],"mappings":"AAIA,eAAO,MAAM,4BAA4B,sCAAsC,CAAC;AAEhF,wBAAgB,4BAA4B,CAAC,cAAc,GAAE,MAAyB,GAAG,MAAM,CAE9F;AAOD,wBAAgB,2BAA2B,CAC1C,UAAU,EAAE,MAAM,EAClB,cAAc,GAAE,MAAyB,EACzC,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC1C,OAAO,CAET;AAED,wBAAgB,kCAAkC,CACjD,UAAU,EAAE,MAAM,GAAG,SAAS,EAC9B,cAAc,GAAE,MAAyB,EACzC,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC1C,MAAM,CAQR;AAED,wBAAsB,4BAA4B,CACjD,UAAU,EAAE,MAAM,EAClB,cAAc,GAAE,MAAyB,EACzC,QAAQ,GAAE,MAAM,CAAC,QAA2B,GAC1C,OAAO,CAAC,IAAI,CAAC,CAMf","sourcesContent":["import { existsSync } from \"node:fs\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\n\nexport const INTERNAL_INTERCOM_BROKER_ARG = \"--atomic-internal-intercom-broker\";\n\nexport function getBundledIntercomBrokerPath(executablePath: string = process.execPath): string {\n\treturn join(dirname(executablePath), \"builtin\", \"intercom\", \"broker\", \"broker.ts\");\n}\n\nfunction comparablePath(path: string, platform: NodeJS.Platform): string {\n\tconst resolved = resolve(path);\n\treturn platform === \"win32\" ? resolved.toLowerCase() : resolved;\n}\n\nexport function isBundledIntercomBrokerPath(\n\tmodulePath: string,\n\texecutablePath: string = process.execPath,\n\tplatform: NodeJS.Platform = process.platform,\n): boolean {\n\treturn comparablePath(modulePath, platform) === comparablePath(getBundledIntercomBrokerPath(executablePath), platform);\n}\n\nexport function validateInternalIntercomBrokerPath(\n\tmodulePath: string | undefined,\n\texecutablePath: string = process.execPath,\n\tplatform: NodeJS.Platform = process.platform,\n): string {\n\tif (!modulePath) {\n\t\tthrow new Error(\"Atomic internal intercom broker module path is required\");\n\t}\n\tif (!isBundledIntercomBrokerPath(modulePath, executablePath, platform)) {\n\t\tthrow new Error(\"Atomic internal intercom broker path must resolve to the bundled intercom broker module\");\n\t}\n\treturn resolve(modulePath);\n}\n\nexport async function importInternalIntercomBroker(\n\tmodulePath: string,\n\texecutablePath: string = process.execPath,\n\tplatform: NodeJS.Platform = process.platform,\n): Promise<void> {\n\tconst brokerPath = validateInternalIntercomBrokerPath(modulePath, executablePath, platform);\n\tif (!existsSync(brokerPath)) {\n\t\tthrow new Error(`Atomic internal intercom broker module not found at ${brokerPath}`);\n\t}\n\tawait import(pathToFileURL(brokerPath).href);\n}\n"]}
@@ -0,0 +1,39 @@
1
+ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExtension) || function (path, preserveJsx) {
2
+ if (typeof path === "string" && /^\.\.?\//.test(path)) {
3
+ return path.replace(/\.(tsx)$|((?:\.d)?)((?:\.[^./]+?)?)\.([cm]?)ts$/i, function (m, tsx, d, ext, cm) {
4
+ return tsx ? preserveJsx ? ".jsx" : ".js" : d && (!ext || !cm) ? m : (d + ext + "." + cm.toLowerCase() + "js");
5
+ });
6
+ }
7
+ return path;
8
+ };
9
+ import { existsSync } from "node:fs";
10
+ import { dirname, join, resolve } from "node:path";
11
+ import { pathToFileURL } from "node:url";
12
+ export const INTERNAL_INTERCOM_BROKER_ARG = "--atomic-internal-intercom-broker";
13
+ export function getBundledIntercomBrokerPath(executablePath = process.execPath) {
14
+ return join(dirname(executablePath), "builtin", "intercom", "broker", "broker.ts");
15
+ }
16
+ function comparablePath(path, platform) {
17
+ const resolved = resolve(path);
18
+ return platform === "win32" ? resolved.toLowerCase() : resolved;
19
+ }
20
+ export function isBundledIntercomBrokerPath(modulePath, executablePath = process.execPath, platform = process.platform) {
21
+ return comparablePath(modulePath, platform) === comparablePath(getBundledIntercomBrokerPath(executablePath), platform);
22
+ }
23
+ export function validateInternalIntercomBrokerPath(modulePath, executablePath = process.execPath, platform = process.platform) {
24
+ if (!modulePath) {
25
+ throw new Error("Atomic internal intercom broker module path is required");
26
+ }
27
+ if (!isBundledIntercomBrokerPath(modulePath, executablePath, platform)) {
28
+ throw new Error("Atomic internal intercom broker path must resolve to the bundled intercom broker module");
29
+ }
30
+ return resolve(modulePath);
31
+ }
32
+ export async function importInternalIntercomBroker(modulePath, executablePath = process.execPath, platform = process.platform) {
33
+ const brokerPath = validateInternalIntercomBrokerPath(modulePath, executablePath, platform);
34
+ if (!existsSync(brokerPath)) {
35
+ throw new Error(`Atomic internal intercom broker module not found at ${brokerPath}`);
36
+ }
37
+ await import(__rewriteRelativeImportExtension(pathToFileURL(brokerPath).href));
38
+ }
39
+ //# sourceMappingURL=internal-intercom-broker.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"internal-intercom-broker.js","sourceRoot":"","sources":["../../src/bun/internal-intercom-broker.ts"],"names":[],"mappings":";;;;;;;;AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,WAAW,CAAC;AACnD,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,CAAC,MAAM,4BAA4B,GAAG,mCAAmC,CAAC;AAEhF,MAAM,UAAU,4BAA4B,CAAC,cAAc,GAAW,OAAO,CAAC,QAAQ;IACrF,OAAO,IAAI,CAAC,OAAO,CAAC,cAAc,CAAC,EAAE,SAAS,EAAE,UAAU,EAAE,QAAQ,EAAE,WAAW,CAAC,CAAC;AACpF,CAAC;AAED,SAAS,cAAc,CAAC,IAAY,EAAE,QAAyB;IAC9D,MAAM,QAAQ,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IAC/B,OAAO,QAAQ,KAAK,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,WAAW,EAAE,CAAC,CAAC,CAAC,QAAQ,CAAC;AACjE,CAAC;AAED,MAAM,UAAU,2BAA2B,CAC1C,UAAkB,EAClB,cAAc,GAAW,OAAO,CAAC,QAAQ,EACzC,QAAQ,GAAoB,OAAO,CAAC,QAAQ;IAE5C,OAAO,cAAc,CAAC,UAAU,EAAE,QAAQ,CAAC,KAAK,cAAc,CAAC,4BAA4B,CAAC,cAAc,CAAC,EAAE,QAAQ,CAAC,CAAC;AACxH,CAAC;AAED,MAAM,UAAU,kCAAkC,CACjD,UAA8B,EAC9B,cAAc,GAAW,OAAO,CAAC,QAAQ,EACzC,QAAQ,GAAoB,OAAO,CAAC,QAAQ;IAE5C,IAAI,CAAC,UAAU,EAAE,CAAC;QACjB,MAAM,IAAI,KAAK,CAAC,yDAAyD,CAAC,CAAC;IAC5E,CAAC;IACD,IAAI,CAAC,2BAA2B,CAAC,UAAU,EAAE,cAAc,EAAE,QAAQ,CAAC,EAAE,CAAC;QACxE,MAAM,IAAI,KAAK,CAAC,yFAAyF,CAAC,CAAC;IAC5G,CAAC;IACD,OAAO,OAAO,CAAC,UAAU,CAAC,CAAC;AAC5B,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,4BAA4B,CACjD,UAAkB,EAClB,cAAc,GAAW,OAAO,CAAC,QAAQ,EACzC,QAAQ,GAAoB,OAAO,CAAC,QAAQ;IAE5C,MAAM,UAAU,GAAG,kCAAkC,CAAC,UAAU,EAAE,cAAc,EAAE,QAAQ,CAAC,CAAC;IAC5F,IAAI,CAAC,UAAU,CAAC,UAAU,CAAC,EAAE,CAAC;QAC7B,MAAM,IAAI,KAAK,CAAC,uDAAuD,UAAU,EAAE,CAAC,CAAC;IACtF,CAAC;IACD,MAAM,MAAM,kCAAC,aAAa,CAAC,UAAU,CAAC,CAAC,IAAI,EAAC,CAAC;AAC9C,CAAC","sourcesContent":["import { existsSync } from \"node:fs\";\nimport { dirname, join, resolve } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\n\nexport const INTERNAL_INTERCOM_BROKER_ARG = \"--atomic-internal-intercom-broker\";\n\nexport function getBundledIntercomBrokerPath(executablePath: string = process.execPath): string {\n\treturn join(dirname(executablePath), \"builtin\", \"intercom\", \"broker\", \"broker.ts\");\n}\n\nfunction comparablePath(path: string, platform: NodeJS.Platform): string {\n\tconst resolved = resolve(path);\n\treturn platform === \"win32\" ? resolved.toLowerCase() : resolved;\n}\n\nexport function isBundledIntercomBrokerPath(\n\tmodulePath: string,\n\texecutablePath: string = process.execPath,\n\tplatform: NodeJS.Platform = process.platform,\n): boolean {\n\treturn comparablePath(modulePath, platform) === comparablePath(getBundledIntercomBrokerPath(executablePath), platform);\n}\n\nexport function validateInternalIntercomBrokerPath(\n\tmodulePath: string | undefined,\n\texecutablePath: string = process.execPath,\n\tplatform: NodeJS.Platform = process.platform,\n): string {\n\tif (!modulePath) {\n\t\tthrow new Error(\"Atomic internal intercom broker module path is required\");\n\t}\n\tif (!isBundledIntercomBrokerPath(modulePath, executablePath, platform)) {\n\t\tthrow new Error(\"Atomic internal intercom broker path must resolve to the bundled intercom broker module\");\n\t}\n\treturn resolve(modulePath);\n}\n\nexport async function importInternalIntercomBroker(\n\tmodulePath: string,\n\texecutablePath: string = process.execPath,\n\tplatform: NodeJS.Platform = process.platform,\n): Promise<void> {\n\tconst brokerPath = validateInternalIntercomBrokerPath(modulePath, executablePath, platform);\n\tif (!existsSync(brokerPath)) {\n\t\tthrow new Error(`Atomic internal intercom broker module not found at ${brokerPath}`);\n\t}\n\tawait import(pathToFileURL(brokerPath).href);\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"split-loader.d.ts","sourceRoot":"","sources":["../../src/bun/split-loader.ts"],"names":[],"mappings":"","sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\n\nconst APP_NAME = \"atomic\";\n\nprocess.title = APP_NAME;\nprocess.env.ATOMIC_CODING_AGENT = \"true\";\nprocess.emitWarning = (() => {}) as typeof process.emitWarning;\n\nconst args = process.argv.slice(2);\n\nfunction readVersion(): string {\n\ttry {\n\t\tconst packageJsonPath = join(dirname(process.execPath), \"package.json\");\n\t\tconst pkg = JSON.parse(readFileSync(packageJsonPath, \"utf8\")) as { version?: unknown };\n\t\treturn typeof pkg.version === \"string\" ? pkg.version : \"0.0.0\";\n\t} catch {\n\t\treturn \"0.0.0\";\n\t}\n}\n\nif (args[0] === \"--version\" || args[0] === \"-v\") {\n\tconsole.log(readVersion());\n\tprocess.exit(0);\n}\n\nconst appPath = join(dirname(process.execPath), \"app.js\");\nif (!existsSync(appPath)) {\n\tconsole.error(`Atomic startup error: missing app bundle at ${appPath}`);\n\tprocess.exit(1);\n}\n\nvoid import(pathToFileURL(appPath).href);\n"]}
1
+ {"version":3,"file":"split-loader.d.ts","sourceRoot":"","sources":["../../src/bun/split-loader.ts"],"names":[],"mappings":"","sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { INTERNAL_INTERCOM_BROKER_ARG, importInternalIntercomBroker } from \"./internal-intercom-broker.ts\";\n\nconst APP_NAME = \"atomic\";\n\nprocess.title = APP_NAME;\nprocess.env.ATOMIC_CODING_AGENT = \"true\";\nprocess.emitWarning = (() => {}) as typeof process.emitWarning;\n\nconst args = process.argv.slice(2);\n\nfunction readVersion(): string {\n\ttry {\n\t\tconst packageJsonPath = join(dirname(process.execPath), \"package.json\");\n\t\tconst pkg = JSON.parse(readFileSync(packageJsonPath, \"utf8\")) as { version?: unknown };\n\t\treturn typeof pkg.version === \"string\" ? pkg.version : \"0.0.0\";\n\t} catch {\n\t\treturn \"0.0.0\";\n\t}\n}\n\nif (args[0] === INTERNAL_INTERCOM_BROKER_ARG) {\n\tif (args.length !== 2) {\n\t\tconsole.error(`Atomic startup error: ${INTERNAL_INTERCOM_BROKER_ARG} requires exactly one broker module path`);\n\t\tprocess.exit(1);\n\t}\n\tvoid importInternalIntercomBroker(args[1]).catch((error: Error) => {\n\t\tconsole.error(`Atomic startup error: ${error.message}`);\n\t\tprocess.exit(1);\n\t});\n} else {\n\tif (args[0] === \"--version\" || args[0] === \"-v\") {\n\t\tconsole.log(readVersion());\n\t\tprocess.exit(0);\n\t}\n\n\tconst appPath = join(dirname(process.execPath), \"app.js\");\n\tif (!existsSync(appPath)) {\n\t\tconsole.error(`Atomic startup error: missing app bundle at ${appPath}`);\n\t\tprocess.exit(1);\n\t}\n\n\tvoid import(pathToFileURL(appPath).href);\n}\n"]}
@@ -9,6 +9,7 @@ var __rewriteRelativeImportExtension = (this && this.__rewriteRelativeImportExte
9
9
  import { existsSync, readFileSync } from "node:fs";
10
10
  import { dirname, join } from "node:path";
11
11
  import { pathToFileURL } from "node:url";
12
+ import { INTERNAL_INTERCOM_BROKER_ARG, importInternalIntercomBroker } from "./internal-intercom-broker.js";
12
13
  const APP_NAME = "atomic";
13
14
  process.title = APP_NAME;
14
15
  process.env.ATOMIC_CODING_AGENT = "true";
@@ -24,14 +25,26 @@ function readVersion() {
24
25
  return "0.0.0";
25
26
  }
26
27
  }
27
- if (args[0] === "--version" || args[0] === "-v") {
28
- console.log(readVersion());
29
- process.exit(0);
28
+ if (args[0] === INTERNAL_INTERCOM_BROKER_ARG) {
29
+ if (args.length !== 2) {
30
+ console.error(`Atomic startup error: ${INTERNAL_INTERCOM_BROKER_ARG} requires exactly one broker module path`);
31
+ process.exit(1);
32
+ }
33
+ void importInternalIntercomBroker(args[1]).catch((error) => {
34
+ console.error(`Atomic startup error: ${error.message}`);
35
+ process.exit(1);
36
+ });
30
37
  }
31
- const appPath = join(dirname(process.execPath), "app.js");
32
- if (!existsSync(appPath)) {
33
- console.error(`Atomic startup error: missing app bundle at ${appPath}`);
34
- process.exit(1);
38
+ else {
39
+ if (args[0] === "--version" || args[0] === "-v") {
40
+ console.log(readVersion());
41
+ process.exit(0);
42
+ }
43
+ const appPath = join(dirname(process.execPath), "app.js");
44
+ if (!existsSync(appPath)) {
45
+ console.error(`Atomic startup error: missing app bundle at ${appPath}`);
46
+ process.exit(1);
47
+ }
48
+ void import(__rewriteRelativeImportExtension(pathToFileURL(appPath).href));
35
49
  }
36
- void import(__rewriteRelativeImportExtension(pathToFileURL(appPath).href));
37
50
  //# sourceMappingURL=split-loader.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"split-loader.js","sourceRoot":"","sources":["../../src/bun/split-loader.ts"],"names":[],"mappings":";;;;;;;;AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AAEzC,MAAM,QAAQ,GAAG,QAAQ,CAAC;AAE1B,OAAO,CAAC,KAAK,GAAG,QAAQ,CAAC;AACzB,OAAO,CAAC,GAAG,CAAC,mBAAmB,GAAG,MAAM,CAAC;AACzC,OAAO,CAAC,WAAW,GAAG,CAAC,GAAG,EAAE,GAAE,CAAC,CAA+B,CAAC;AAE/D,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAEnC,SAAS,WAAW;IACnB,IAAI,CAAC;QACJ,MAAM,eAAe,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,cAAc,CAAC,CAAC;QACxE,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,eAAe,EAAE,MAAM,CAAC,CAA0B,CAAC;QACvF,OAAO,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;IAChE,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,OAAO,CAAC;IAChB,CAAC;AACF,CAAC;AAED,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,WAAW,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;IACjD,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC;IAC3B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACjB,CAAC;AAED,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,CAAC;AAC1D,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;IAC1B,OAAO,CAAC,KAAK,CAAC,+CAA+C,OAAO,EAAE,CAAC,CAAC;IACxE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AACjB,CAAC;AAED,KAAK,MAAM,kCAAC,aAAa,CAAC,OAAO,CAAC,CAAC,IAAI,EAAC,CAAC","sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\n\nconst APP_NAME = \"atomic\";\n\nprocess.title = APP_NAME;\nprocess.env.ATOMIC_CODING_AGENT = \"true\";\nprocess.emitWarning = (() => {}) as typeof process.emitWarning;\n\nconst args = process.argv.slice(2);\n\nfunction readVersion(): string {\n\ttry {\n\t\tconst packageJsonPath = join(dirname(process.execPath), \"package.json\");\n\t\tconst pkg = JSON.parse(readFileSync(packageJsonPath, \"utf8\")) as { version?: unknown };\n\t\treturn typeof pkg.version === \"string\" ? pkg.version : \"0.0.0\";\n\t} catch {\n\t\treturn \"0.0.0\";\n\t}\n}\n\nif (args[0] === \"--version\" || args[0] === \"-v\") {\n\tconsole.log(readVersion());\n\tprocess.exit(0);\n}\n\nconst appPath = join(dirname(process.execPath), \"app.js\");\nif (!existsSync(appPath)) {\n\tconsole.error(`Atomic startup error: missing app bundle at ${appPath}`);\n\tprocess.exit(1);\n}\n\nvoid import(pathToFileURL(appPath).href);\n"]}
1
+ {"version":3,"file":"split-loader.js","sourceRoot":"","sources":["../../src/bun/split-loader.ts"],"names":[],"mappings":";;;;;;;;AAAA,OAAO,EAAE,UAAU,EAAE,YAAY,EAAE,MAAM,SAAS,CAAC;AACnD,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,WAAW,CAAC;AAC1C,OAAO,EAAE,aAAa,EAAE,MAAM,UAAU,CAAC;AACzC,OAAO,EAAE,4BAA4B,EAAE,4BAA4B,EAAE,MAAM,+BAA+B,CAAC;AAE3G,MAAM,QAAQ,GAAG,QAAQ,CAAC;AAE1B,OAAO,CAAC,KAAK,GAAG,QAAQ,CAAC;AACzB,OAAO,CAAC,GAAG,CAAC,mBAAmB,GAAG,MAAM,CAAC;AACzC,OAAO,CAAC,WAAW,GAAG,CAAC,GAAG,EAAE,GAAE,CAAC,CAA+B,CAAC;AAE/D,MAAM,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;AAEnC,SAAS,WAAW;IACnB,IAAI,CAAC;QACJ,MAAM,eAAe,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,cAAc,CAAC,CAAC;QACxE,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,YAAY,CAAC,eAAe,EAAE,MAAM,CAAC,CAA0B,CAAC;QACvF,OAAO,OAAO,GAAG,CAAC,OAAO,KAAK,QAAQ,CAAC,CAAC,CAAC,GAAG,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC;IAChE,CAAC;IAAC,MAAM,CAAC;QACR,OAAO,OAAO,CAAC;IAChB,CAAC;AACF,CAAC;AAED,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,4BAA4B,EAAE,CAAC;IAC9C,IAAI,IAAI,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACvB,OAAO,CAAC,KAAK,CAAC,yBAAyB,4BAA4B,0CAA0C,CAAC,CAAC;QAC/G,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC;IACD,KAAK,4BAA4B,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,KAAY,EAAE,EAAE;QACjE,OAAO,CAAC,KAAK,CAAC,yBAAyB,KAAK,CAAC,OAAO,EAAE,CAAC,CAAC;QACxD,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC,CAAC,CAAC;AACJ,CAAC;KAAM,CAAC;IACP,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,WAAW,IAAI,IAAI,CAAC,CAAC,CAAC,KAAK,IAAI,EAAE,CAAC;QACjD,OAAO,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,CAAC;QAC3B,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC;IAED,MAAM,OAAO,GAAG,IAAI,CAAC,OAAO,CAAC,OAAO,CAAC,QAAQ,CAAC,EAAE,QAAQ,CAAC,CAAC;IAC1D,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC;QAC1B,OAAO,CAAC,KAAK,CAAC,+CAA+C,OAAO,EAAE,CAAC,CAAC;QACxE,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IACjB,CAAC;IAED,KAAK,MAAM,kCAAC,aAAa,CAAC,OAAO,CAAC,CAAC,IAAI,EAAC,CAAC;AAC1C,CAAC","sourcesContent":["import { existsSync, readFileSync } from \"node:fs\";\nimport { dirname, join } from \"node:path\";\nimport { pathToFileURL } from \"node:url\";\nimport { INTERNAL_INTERCOM_BROKER_ARG, importInternalIntercomBroker } from \"./internal-intercom-broker.ts\";\n\nconst APP_NAME = \"atomic\";\n\nprocess.title = APP_NAME;\nprocess.env.ATOMIC_CODING_AGENT = \"true\";\nprocess.emitWarning = (() => {}) as typeof process.emitWarning;\n\nconst args = process.argv.slice(2);\n\nfunction readVersion(): string {\n\ttry {\n\t\tconst packageJsonPath = join(dirname(process.execPath), \"package.json\");\n\t\tconst pkg = JSON.parse(readFileSync(packageJsonPath, \"utf8\")) as { version?: unknown };\n\t\treturn typeof pkg.version === \"string\" ? pkg.version : \"0.0.0\";\n\t} catch {\n\t\treturn \"0.0.0\";\n\t}\n}\n\nif (args[0] === INTERNAL_INTERCOM_BROKER_ARG) {\n\tif (args.length !== 2) {\n\t\tconsole.error(`Atomic startup error: ${INTERNAL_INTERCOM_BROKER_ARG} requires exactly one broker module path`);\n\t\tprocess.exit(1);\n\t}\n\tvoid importInternalIntercomBroker(args[1]).catch((error: Error) => {\n\t\tconsole.error(`Atomic startup error: ${error.message}`);\n\t\tprocess.exit(1);\n\t});\n} else {\n\tif (args[0] === \"--version\" || args[0] === \"-v\") {\n\t\tconsole.log(readVersion());\n\t\tprocess.exit(0);\n\t}\n\n\tconst appPath = join(dirname(process.execPath), \"app.js\");\n\tif (!existsSync(appPath)) {\n\t\tconsole.error(`Atomic startup error: missing app bundle at ${appPath}`);\n\t\tprocess.exit(1);\n\t}\n\n\tvoid import(pathToFileURL(appPath).href);\n}\n"]}
@@ -1,7 +1,18 @@
1
1
  import type { AssistantMessage } from "@earendil-works/pi-ai/compat";
2
2
  import type { AgentSessionInternalSurface as AgentSession } from "./agent-session-methods.ts";
3
+ /**
4
+ * Upper bound on consecutive automatic continuations of a response that was
5
+ * truncated at the output-token cap ("length") while the context is still
6
+ * below the compaction budget. Each continuation regenerates the cut-off turn,
7
+ * so a model that insists on emitting more than its per-turn output cap can
8
+ * still terminate instead of looping forever.
9
+ */
10
+ export declare const MAX_LENGTH_CONTINUATION_ATTEMPTS = 3;
11
+ export declare const MAX_OUTPUT_BUDGET_ERROR_CONTINUATION_ATTEMPTS = 1;
3
12
  export declare function _checkCompaction(this: AgentSession, assistantMessage: AssistantMessage, skipAbortedCheck?: boolean): Promise<void>;
4
13
  export declare function _isCopilotServerCapBelowSelectedContextWindow(this: AgentSession, assistantMessage: AssistantMessage): boolean;
14
+ export declare function isRetryWorthyLengthStop(assistantMessage: AssistantMessage): boolean;
15
+ export declare function isRetryWorthyOutputBudgetError(assistantMessage: AssistantMessage): boolean;
5
16
  export declare function shouldRetryAfterThresholdCompaction(assistantMessage: AssistantMessage): boolean;
6
17
  /**
7
18
  * Internal: remove an incomplete assistant from retry context before auto-continuing after compaction.
@@ -11,23 +22,31 @@ export declare function _dropTrailingAutoCompactionRetryAssistantIfPresent(this:
11
22
  * Internal: schedule a live post-event continuation probe after compaction_end listeners can flush queues.
12
23
  */
13
24
  export declare function _schedulePostAutoCompactionContinuationProbe(this: AgentSession, _reason: "overflow" | "threshold", willRetry: boolean): void;
25
+ export declare function _awaitPendingPostCompactionContinuation(this: AgentSession): Promise<void>;
14
26
  /**
15
27
  * Internal: resume generation after successful auto-compaction only when active work remains.
16
28
  */
17
- export declare function _resumeAfterAutoCompaction(this: AgentSession): void;
29
+ export declare function _resumeAfterAutoCompaction(this: AgentSession): Promise<void>;
18
30
  /**
19
- * Internal: Run auto-compaction with events.
31
+ * Internal: resume a response that was truncated at the output-token cap
32
+ * ("length") when the context does not warrant compaction. The generation is
33
+ * continued directly so the model finishes the work it was cut off from, rather
34
+ * than dead-ending on the truncation. Bounded by MAX_LENGTH_CONTINUATION_ATTEMPTS
35
+ * so a turn that keeps exceeding the per-turn output cap can still terminate.
20
36
  */
37
+ export declare function _resumeAfterLengthTruncation(this: AgentSession): void;
21
38
  export declare function _runAutoCompaction(this: AgentSession, reason: "overflow" | "threshold", willRetry: boolean): Promise<void>;
22
39
  /**
23
40
  * Toggle auto-compaction setting.
24
41
  */
25
42
  export declare const agentSessionAutoCompactionMethods: {
43
+ _awaitPendingPostCompactionContinuation: typeof _awaitPendingPostCompactionContinuation;
26
44
  _checkCompaction: typeof _checkCompaction;
27
45
  _isCopilotServerCapBelowSelectedContextWindow: typeof _isCopilotServerCapBelowSelectedContextWindow;
28
46
  _dropTrailingAutoCompactionRetryAssistantIfPresent: typeof _dropTrailingAutoCompactionRetryAssistantIfPresent;
29
47
  _schedulePostAutoCompactionContinuationProbe: typeof _schedulePostAutoCompactionContinuationProbe;
30
48
  _resumeAfterAutoCompaction: typeof _resumeAfterAutoCompaction;
49
+ _resumeAfterLengthTruncation: typeof _resumeAfterLengthTruncation;
31
50
  _runAutoCompaction: typeof _runAutoCompaction;
32
51
  };
33
52
  //# sourceMappingURL=agent-session-auto-compaction.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"agent-session-auto-compaction.d.ts","sourceRoot":"","sources":["../../src/core/agent-session-auto-compaction.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAMrE,OAAO,KAAK,EAAE,2BAA2B,IAAI,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAE9F,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,gBAAgB,UAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CA+FrI;AAGD,wBAAgB,6CAA6C,CAAC,IAAI,EAAE,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,GAAG,OAAO,CAQ7H;AAED,wBAAgB,mCAAmC,CAAC,gBAAgB,EAAE,gBAAgB,GAAG,OAAO,CAE/F;AAED;;GAEG;AAEH,wBAAgB,kDAAkD,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CAQ3F;AAED;;GAEG;AAEH,wBAAgB,4CAA4C,CAAC,IAAI,EAAE,YAAY,EAC9E,OAAO,EAAE,UAAU,GAAG,WAAW,EACjC,SAAS,EAAE,OAAO,GAChB,IAAI,CAiBN;AAED;;GAEG;AAEH,wBAAgB,0BAA0B,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CASnE;AAED;;GAEG;AAEH,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,GAAG,WAAW,EAAE,SAAS,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAsEhI;AAED;;GAEG;AAEH,eAAO,MAAM,iCAAiC;;;;;;;CAO7C,CAAC","sourcesContent":["import type { AssistantMessage } from \"@earendil-works/pi-ai/compat\";\nimport { isContextOverflow } from \"@earendil-works/pi-ai/compat\";\nimport { getEffectiveInputBudget } from \"./context-window.ts\";\nimport { parseCopilotPromptLimitError } from \"./copilot-errors.ts\";\nimport { calculateContextTokens, estimateContextTokens, shouldCompact } from \"./compaction/index.ts\";\nimport { getLatestCompactionBoundaryEntry } from \"./session-manager.ts\";\nimport type { AgentSessionInternalSurface as AgentSession } from \"./agent-session-methods.ts\";\n\nexport async function _checkCompaction(this: AgentSession, assistantMessage: AssistantMessage, skipAbortedCheck = true): Promise<void> {\n\tconst settings = this.settingsManager.getCompactionSettings();\n\tif (!settings.enabled) return;\n\n\t// Skip if message was aborted (user cancelled) - unless skipAbortedCheck is false\n\tif (skipAbortedCheck && assistantMessage.stopReason === \"aborted\") return;\n\n\tconst contextWindow = this.model?.contextWindow ?? 0;\n\n\t// Skip overflow check if the message came from a different model.\n\t// This handles the case where user switched from a smaller-context model (e.g. opus)\n\t// to a larger-context model (e.g. codex) - the overflow error from the old model\n\t// shouldn't trigger compaction for the new model.\n\tconst sameModel =\n\t\tthis.model && assistantMessage.provider === this.model.provider && assistantMessage.model === this.model.id;\n\n\t// Skip compaction checks if this assistant message is older than the latest\n\t// compaction boundary. This prevents a stale pre-compaction usage/error\n\t// from retriggering compaction on the first prompt after compaction.\n\tconst compactionBoundaryEntry = getLatestCompactionBoundaryEntry(this.sessionManager.getBranch());\n\tconst assistantIsFromBeforeCompactionBoundary =\n\t\tcompactionBoundaryEntry !== null &&\n\t\tassistantMessage.timestamp <= new Date(compactionBoundaryEntry.timestamp).getTime();\n\tif (assistantIsFromBeforeCompactionBoundary) {\n\t\treturn;\n\t}\n\n\t// Case 1: Overflow - LLM returned context overflow error\n\t// When Copilot rejects a 1m client-budget prompt at a lower server cap (for example\n\t// because long-context/usage-based billing entitlement is missing), leave the friendly\n\t// error visible instead of auto-compacting down to a smaller server tier silently.\n\tif (sameModel && this._isCopilotServerCapBelowSelectedContextWindow(assistantMessage)) {\n\t\treturn;\n\t}\n\tif (sameModel && isContextOverflow(assistantMessage, contextWindow)) {\n\t\tconst willRetry = assistantMessage.stopReason !== \"stop\";\n\t\tif (!willRetry) {\n\t\t\tawait this._runAutoCompaction(\"overflow\", false);\n\t\t\treturn;\n\t\t}\n\n\t\tif (this._overflowRecoveryAttempted) {\n\t\t\tthis._emit({\n\t\t\t\ttype: \"compaction_end\",\n\t\t\t\treason: \"overflow\",\n\t\t\t\tresult: undefined,\n\t\t\t\taborted: false,\n\t\t\t\twillRetry: false,\n\t\t\t\terrorMessage:\n\t\t\t\t\t\"Context overflow recovery failed after one compact-and-retry attempt. Try reducing context or switching to a larger-context model.\",\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tthis._overflowRecoveryAttempted = true;\n\t\t// Remove the error message from agent state (it IS saved to session for history,\n\t\t// but we don't want it in context for the retry)\n\t\tconst messages = this.agent.state.messages;\n\t\tif (messages.length > 0 && messages[messages.length - 1].role === \"assistant\") {\n\t\t\tthis.agent.state.messages = messages.slice(0, -1);\n\t\t}\n\t\tawait this._runAutoCompaction(\"overflow\", willRetry);\n\t\treturn;\n\t}\n\n\t// Case 2: Threshold - context is getting large\n\t// For error messages (no usage data), estimate from last successful response.\n\t// This ensures sessions that hit persistent API errors (e.g. 529) can still compact.\n\tlet contextTokens: number;\n\tif (assistantMessage.stopReason === \"error\") {\n\t\tconst messages = this.agent.state.messages;\n\t\tconst estimate = estimateContextTokens(messages);\n\t\tif (estimate.lastUsageIndex === null) return; // No usage data at all\n\t\t// Verify the usage source is post-compaction. Kept pre-compaction messages\n\t\t// have stale usage reflecting the old (larger) context and would falsely\n\t\t// trigger compaction right after one just finished.\n\t\tconst usageMsg = messages[estimate.lastUsageIndex];\n\t\tif (\n\t\t\tcompactionBoundaryEntry &&\n\t\t\tusageMsg.role === \"assistant\" &&\n\t\t\t(usageMsg as AssistantMessage).timestamp <= new Date(compactionBoundaryEntry.timestamp).getTime()\n\t\t) {\n\t\t\treturn;\n\t\t}\n\t\tcontextTokens = estimate.tokens;\n\t} else {\n\t\tcontextTokens = calculateContextTokens(assistantMessage.usage);\n\t}\n\t// Compact against the effective input budget (the hard prompt cap for providers like Copilot\n\t// that advertise a larger total window) so we compact before overrunning the server-side limit\n\t// rather than relying on reactive overflow recovery near the cap.\n\tconst compactionBudget = this.model ? getEffectiveInputBudget(this.model) : contextWindow;\n\tif (shouldCompact(contextTokens, compactionBudget, settings)) {\n\t\tawait this._runAutoCompaction(\"threshold\", shouldRetryAfterThresholdCompaction(assistantMessage));\n\t}\n}\n\n\nexport function _isCopilotServerCapBelowSelectedContextWindow(this: AgentSession, assistantMessage: AssistantMessage): boolean {\n\tif (!this.model || this.model.provider !== \"github-copilot\" || !assistantMessage.errorMessage) return false;\n\tconst promptLimitError = parseCopilotPromptLimitError(assistantMessage.errorMessage);\n\t// Compare against the effective input budget (the model's real prompt cap), not the displayed\n\t// total window. A rejection at the prompt cap is a normal overflow we should compact-and-retry;\n\t// only a rejection *below* the cap (e.g. a missing long-context entitlement dropping the account\n\t// to a lower server tier) keeps the friendly error visible instead of silently compacting down.\n\treturn promptLimitError !== undefined && getEffectiveInputBudget(this.model) > promptLimitError.limitTokens;\n}\n\nexport function shouldRetryAfterThresholdCompaction(assistantMessage: AssistantMessage): boolean {\n\treturn assistantMessage.stopReason === \"length\" && assistantMessage.usage.output > 0;\n}\n\n/**\n * Internal: remove an incomplete assistant from retry context before auto-continuing after compaction.\n */\n\nexport function _dropTrailingAutoCompactionRetryAssistantIfPresent(this: AgentSession): void {\n\tconst messages = this.agent.state.messages;\n\tconst lastMsg = messages[messages.length - 1];\n\tif (lastMsg?.role !== \"assistant\") return;\n\tconst stopReason = (lastMsg as AssistantMessage).stopReason;\n\tif (stopReason === \"error\" || stopReason === \"length\") {\n\t\tthis.agent.state.messages = messages.slice(0, -1);\n\t}\n}\n\n/**\n * Internal: schedule a live post-event continuation probe after compaction_end listeners can flush queues.\n */\n\nexport function _schedulePostAutoCompactionContinuationProbe(this: AgentSession,\n\t_reason: \"overflow\" | \"threshold\",\n\twillRetry: boolean,\n): void {\n\tsetTimeout(() => {\n\t\tif (this.isCompacting || this.isStreaming) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (willRetry) {\n\t\t\tthis._resumeAfterAutoCompaction();\n\t\t\treturn;\n\t\t}\n\n\t\tif (!this.agent.hasQueuedMessages()) {\n\t\t\treturn;\n\t\t}\n\n\t\tthis._resumeAfterAutoCompaction();\n\t}, 100);\n}\n\n/**\n * Internal: resume generation after successful auto-compaction only when active work remains.\n */\n\nexport function _resumeAfterAutoCompaction(this: AgentSession): void {\n\tvoid this._runAgentContinue().catch((error) => {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tthis._emit({\n\t\t\ttype: \"agent_continue_error\",\n\t\t\tsource: \"post_compaction\",\n\t\t\terrorMessage: `Post-compaction continuation failed: ${message}`,\n\t\t});\n\t});\n}\n\n/**\n * Internal: Run auto-compaction with events.\n */\n\nexport async function _runAutoCompaction(this: AgentSession, reason: \"overflow\" | \"threshold\", willRetry: boolean): Promise<void> {\n\tthis._emit({ type: \"compaction_start\", reason });\n\tthis._autoCompactionAbortController = new AbortController();\n\n\ttry {\n\t\tif (!this.model) {\n\t\t\tthis._emit({\n\t\t\t\ttype: \"compaction_end\",\n\t\t\t\treason,\n\t\t\t\tresult: undefined,\n\t\t\t\taborted: false,\n\t\t\t\twillRetry: false,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\t// Auth is resolved lazily: only called when the planner fallback is needed.\n\t\t// This allows extension-provided deletion requests to run before auth is checked,\n\t\t// enabling local extension compaction even when API credentials are unavailable.\n\t\t// Auto-mode resolver returns undefined (rather than throwing) when auth is missing;\n\t\t// overflow recovery then falls back to deterministic no-auth eviction, while\n\t\t// threshold compaction keeps the previous no-op behavior.\n\t\tconst model = this.model;\n\t\tconst result = await this._applyContextVerbatimCompaction({\n\t\t\tresolvePlannerAuth: async () => {\n\t\t\t\tconst authResult = await this._modelRegistry.getApiKeyAndHeaders(model);\n\t\t\t\tif (!authResult.ok || !authResult.apiKey) {\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\t\t\t\treturn { apiKey: authResult.apiKey, headers: authResult.headers };\n\t\t\t},\n\t\t\tabortController: this._autoCompactionAbortController,\n\t\t\tbackupLabel: reason === \"overflow\" ? \"overflow-auto-compact\" : \"auto-compact\",\n\t\t\treason,\n\t\t});\n\t\tif (!result) {\n\t\t\tthis._emit({\n\t\t\t\ttype: \"compaction_end\",\n\t\t\t\treason,\n\t\t\t\tresult: undefined,\n\t\t\t\taborted: false,\n\t\t\t\twillRetry: false,\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tif (willRetry) {\n\t\t\tthis._dropTrailingAutoCompactionRetryAssistantIfPresent();\n\t\t}\n\n\t\tthis._emit({ type: \"compaction_end\", reason, result, aborted: false, willRetry });\n\t\tthis._schedulePostAutoCompactionContinuationProbe(reason, willRetry);\n\t} catch (error) {\n\t\tconst errorMessage = error instanceof Error ? error.message : \"compaction failed\";\n\t\tconst aborted = errorMessage === \"Compaction cancelled\" || (error instanceof Error && error.name === \"AbortError\");\n\t\tthis._emit({\n\t\t\ttype: \"compaction_end\",\n\t\t\treason,\n\t\t\tresult: undefined,\n\t\t\taborted,\n\t\t\twillRetry: false,\n\t\t\terrorMessage: aborted\n\t\t\t\t? undefined\n\t\t\t\t: reason === \"overflow\"\n\t\t\t\t\t? `Context overflow recovery failed: ${errorMessage}`\n\t\t\t\t\t: `Auto-compaction failed: ${errorMessage}`,\n\t\t});\n\t} finally {\n\t\tthis._autoCompactionAbortController = undefined;\n\t}\n}\n\n/**\n * Toggle auto-compaction setting.\n */\n\nexport const agentSessionAutoCompactionMethods = {\n\t_checkCompaction,\n\t_isCopilotServerCapBelowSelectedContextWindow,\n\t_dropTrailingAutoCompactionRetryAssistantIfPresent,\n\t_schedulePostAutoCompactionContinuationProbe,\n\t_resumeAfterAutoCompaction,\n\t_runAutoCompaction,\n};\n"]}
1
+ {"version":3,"file":"agent-session-auto-compaction.d.ts","sourceRoot":"","sources":["../../src/core/agent-session-auto-compaction.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAOrE,OAAO,KAAK,EAAE,2BAA2B,IAAI,YAAY,EAAE,MAAM,4BAA4B,CAAC;AAE9F;;;;;;GAMG;AACH,eAAO,MAAM,gCAAgC,IAAI,CAAC;AAElD,eAAO,MAAM,6CAA6C,IAAI,CAAC;AAc/D,wBAAsB,gBAAgB,CAAC,IAAI,EAAE,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,gBAAgB,UAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CA8HrI;AAGD,wBAAgB,6CAA6C,CAAC,IAAI,EAAE,YAAY,EAAE,gBAAgB,EAAE,gBAAgB,GAAG,OAAO,CAQ7H;AAED,wBAAgB,uBAAuB,CAAC,gBAAgB,EAAE,gBAAgB,GAAG,OAAO,CAEnF;AAmDD,wBAAgB,8BAA8B,CAAC,gBAAgB,EAAE,gBAAgB,GAAG,OAAO,CAQ1F;AAED,wBAAgB,mCAAmC,CAAC,gBAAgB,EAAE,gBAAgB,GAAG,OAAO,CAE/F;AAED;;GAEG;AAEH,wBAAgB,kDAAkD,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CAQ3F;AAED;;GAEG;AAEH,wBAAgB,4CAA4C,CAAC,IAAI,EAAE,YAAY,EAC9E,OAAO,EAAE,UAAU,GAAG,WAAW,EACjC,SAAS,EAAE,OAAO,GAChB,IAAI,CAoCN;AAED,wBAAsB,uCAAuC,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAI/F;AAED;;GAEG;AAEH,wBAAsB,0BAA0B,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,IAAI,CAAC,CAWlF;AAED;;;;;;GAMG;AAEH,wBAAgB,4BAA4B,CAAC,IAAI,EAAE,YAAY,GAAG,IAAI,CAQrE;AAOD,wBAAsB,kBAAkB,CAAC,IAAI,EAAE,YAAY,EAAE,MAAM,EAAE,UAAU,GAAG,WAAW,EAAE,SAAS,EAAE,OAAO,GAAG,OAAO,CAAC,IAAI,CAAC,CAyEhI;AAED;;GAEG;AAEH,eAAO,MAAM,iCAAiC;;;;;;;;;CAS7C,CAAC","sourcesContent":["import type { AssistantMessage } from \"@earendil-works/pi-ai/compat\";\nimport { isContextOverflow } from \"@earendil-works/pi-ai/compat\";\nimport { getEffectiveInputBudget } from \"./context-window.ts\";\nimport { parseCopilotPromptLimitError } from \"./copilot-errors.ts\";\nimport { calculateContextTokens, estimateContextTokens, shouldCompact } from \"./compaction/index.ts\";\nimport { getLatestCompactionBoundaryEntry } from \"./session-manager.ts\";\nimport { MIN_RESPONSES_MAX_OUTPUT_TOKENS } from \"./openai-responses-payload-sanitizer.ts\";\nimport type { AgentSessionInternalSurface as AgentSession } from \"./agent-session-methods.ts\";\n\n/**\n * Upper bound on consecutive automatic continuations of a response that was\n * truncated at the output-token cap (\"length\") while the context is still\n * below the compaction budget. Each continuation regenerates the cut-off turn,\n * so a model that insists on emitting more than its per-turn output cap can\n * still terminate instead of looping forever.\n */\nexport const MAX_LENGTH_CONTINUATION_ATTEMPTS = 3;\n\nexport const MAX_OUTPUT_BUDGET_ERROR_CONTINUATION_ATTEMPTS = 1;\n\ntype JsonValue = string | number | boolean | null | JsonValue[] | { [key: string]: JsonValue };\ntype ProviderErrorDetails = {\n\tmessage?: string;\n\tcode?: string;\n\tparam?: string;\n};\n\nconst OUTPUT_BUDGET_PARAMETER_PATTERN = /\\bmax_output_tokens\\b/;\nconst OUTPUT_BUDGET_UNDERFLOW_PATTERN = new RegExp(\n\t`(?:integer\\\\s+below\\\\s+minimum\\\\s+value|expected\\\\s+(?:a\\\\s+)?value\\\\s*>=\\\\s*${MIN_RESPONSES_MAX_OUTPUT_TOKENS}|got\\\\s+1\\\\s+instead)`,\n);\n\nexport async function _checkCompaction(this: AgentSession, assistantMessage: AssistantMessage, skipAbortedCheck = true): Promise<void> {\n\t// The agent_end path passes skipAbortedCheck=true; the pre-prompt path passes\n\t// false. Only the live turn-completion path may auto-continue a truncated\n\t// response — before a fresh user prompt we must not resume the old turn.\n\tconst isLiveTurnCompletion = skipAbortedCheck;\n\tconst settings = this.settingsManager.getCompactionSettings();\n\tif (!settings.enabled) return;\n\n\t// Skip if message was aborted (user cancelled) - unless skipAbortedCheck is false\n\tif (skipAbortedCheck && assistantMessage.stopReason === \"aborted\") return;\n\n\tconst contextWindow = this.model?.contextWindow ?? 0;\n\n\t// Skip overflow check if the message came from a different model.\n\t// This handles the case where user switched from a smaller-context model (e.g. opus)\n\t// to a larger-context model (e.g. codex) - the overflow error from the old model\n\t// shouldn't trigger compaction for the new model.\n\tconst sameModel =\n\t\tthis.model && assistantMessage.provider === this.model.provider && assistantMessage.model === this.model.id;\n\n\t// Skip compaction checks if this assistant message is older than the latest\n\t// compaction boundary. This prevents a stale pre-compaction usage/error\n\t// from retriggering compaction on the first prompt after compaction.\n\tconst compactionBoundaryEntry = getLatestCompactionBoundaryEntry(this.sessionManager.getBranch());\n\tconst assistantIsFromBeforeCompactionBoundary =\n\t\tcompactionBoundaryEntry !== null &&\n\t\tassistantMessage.timestamp <= new Date(compactionBoundaryEntry.timestamp).getTime();\n\tif (assistantIsFromBeforeCompactionBoundary) {\n\t\treturn;\n\t}\n\n\t// Case 1: Overflow - LLM returned context overflow error\n\t// When Copilot rejects a 1m client-budget prompt at a lower server cap (for example\n\t// because long-context/usage-based billing entitlement is missing), leave the friendly\n\t// error visible instead of auto-compacting down to a smaller server tier silently.\n\tif (sameModel && this._isCopilotServerCapBelowSelectedContextWindow(assistantMessage)) {\n\t\treturn;\n\t}\n\tif (sameModel && isContextOverflow(assistantMessage, contextWindow)) {\n\t\tconst willRetry = assistantMessage.stopReason !== \"stop\";\n\t\tif (!willRetry) {\n\t\t\tawait this._runAutoCompaction(\"overflow\", false);\n\t\t\treturn;\n\t\t}\n\n\t\tif (this._overflowRecoveryAttempted) {\n\t\t\tthis._emit({\n\t\t\t\ttype: \"compaction_end\",\n\t\t\t\treason: \"overflow\",\n\t\t\t\tresult: undefined,\n\t\t\t\taborted: false,\n\t\t\t\twillRetry: false,\n\t\t\t\tunresolvedOverflow: true,\n\t\t\t\terrorMessage:\n\t\t\t\t\t\"Context overflow recovery failed after one compact-and-retry attempt. Try reducing context or switching to a larger-context model.\",\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tthis._overflowRecoveryAttempted = true;\n\t\t// Remove the error message from agent state (it IS saved to session for history,\n\t\t// but we don't want it in context for the retry)\n\t\tconst messages = this.agent.state.messages;\n\t\tif (messages.length > 0 && messages[messages.length - 1].role === \"assistant\") {\n\t\t\tthis.agent.state.messages = messages.slice(0, -1);\n\t\t}\n\t\tawait this._runAutoCompaction(\"overflow\", willRetry);\n\t\treturn;\n\t}\n\n\t// Case 2: Threshold - context is getting large\n\t// For error messages (no usage data), estimate from last successful response.\n\t// This ensures sessions that hit persistent API errors (e.g. 529) can still compact.\n\tlet contextTokens: number;\n\tif (assistantMessage.stopReason === \"error\") {\n\t\tconst messages = this.agent.state.messages;\n\t\tconst estimate = estimateContextTokens(messages);\n\t\tif (estimate.lastUsageIndex === null) return; // No usage data at all\n\t\t// Verify the usage source is post-compaction. Kept pre-compaction messages\n\t\t// have stale usage reflecting the old (larger) context and would falsely\n\t\t// trigger compaction right after one just finished.\n\t\tconst usageMsg = messages[estimate.lastUsageIndex];\n\t\tif (\n\t\t\tcompactionBoundaryEntry &&\n\t\t\tusageMsg.role === \"assistant\" &&\n\t\t\t(usageMsg as AssistantMessage).timestamp <= new Date(compactionBoundaryEntry.timestamp).getTime()\n\t\t) {\n\t\t\treturn;\n\t\t}\n\t\tcontextTokens = estimate.tokens;\n\t} else {\n\t\tcontextTokens = calculateContextTokens(assistantMessage.usage);\n\t}\n\t// Compact against the effective input budget (the hard prompt cap for providers like Copilot\n\t// that advertise a larger total window) so we compact before overrunning the server-side limit\n\t// rather than relying on reactive overflow recovery near the cap.\n\tconst compactionBudget = this.model ? getEffectiveInputBudget(this.model) : contextWindow;\n\tif (shouldCompact(contextTokens, compactionBudget, settings)) {\n\t\tconst willRetry = shouldRetryAfterThresholdCompaction(assistantMessage);\n\t\tif (willRetry && isRetryWorthyOutputBudgetError(assistantMessage)) {\n\t\t\tif (this._outputBudgetErrorContinuationAttempts >= MAX_OUTPUT_BUDGET_ERROR_CONTINUATION_ATTEMPTS) {\n\t\t\t\tthis._emit({\n\t\t\t\t\ttype: \"compaction_end\",\n\t\t\t\t\treason: \"threshold\",\n\t\t\t\t\tresult: undefined,\n\t\t\t\t\taborted: false,\n\t\t\t\t\twillRetry: false,\n\t\t\t\t\terrorMessage:\n\t\t\t\t\t\t\"Output-budget recovery stopped after a compact-and-retry attempt. Try reducing context or switching to a larger-context model.\",\n\t\t\t\t});\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tthis._outputBudgetErrorContinuationAttempts += 1;\n\t\t}\n\t\tawait this._runAutoCompaction(\"threshold\", willRetry);\n\t\treturn;\n\t}\n\n\t// A response truncated at the output-token cap (\"length\") with the context\n\t// still below the compaction budget is genuine work cut off mid-flight, not a\n\t// context overflow. Compaction would not free any room, so continue the\n\t// generation directly instead of dead-ending on the truncation and leaving\n\t// the task half-finished.\n\tif (isLiveTurnCompletion && isRetryWorthyLengthStop(assistantMessage)) {\n\t\tthis._resumeAfterLengthTruncation();\n\t}\n}\n\n\nexport function _isCopilotServerCapBelowSelectedContextWindow(this: AgentSession, assistantMessage: AssistantMessage): boolean {\n\tif (!this.model || this.model.provider !== \"github-copilot\" || !assistantMessage.errorMessage) return false;\n\tconst promptLimitError = parseCopilotPromptLimitError(assistantMessage.errorMessage);\n\t// Compare against the effective input budget (the model's real prompt cap), not the displayed\n\t// total window. A rejection at the prompt cap is a normal overflow we should compact-and-retry;\n\t// only a rejection *below* the cap (e.g. a missing long-context entitlement dropping the account\n\t// to a lower server tier) keeps the friendly error visible instead of silently compacting down.\n\treturn promptLimitError !== undefined && getEffectiveInputBudget(this.model) > promptLimitError.limitTokens;\n}\n\nexport function isRetryWorthyLengthStop(assistantMessage: AssistantMessage): boolean {\n\treturn assistantMessage.stopReason === \"length\" && assistantMessage.usage.output > 0;\n}\n\nfunction isJsonRecord(value: JsonValue): value is { [key: string]: JsonValue } {\n\treturn typeof value === \"object\" && value !== null && !Array.isArray(value);\n}\n\nfunction stringField(record: { [key: string]: JsonValue }, key: string): string | undefined {\n\tconst value = record[key];\n\treturn typeof value === \"string\" ? value : undefined;\n}\n\nfunction parseProviderErrorDetails(errorMessage: string): ProviderErrorDetails | undefined {\n\tconst start = errorMessage.indexOf(\"{\");\n\tconst end = errorMessage.lastIndexOf(\"}\");\n\tif (start === -1 || end <= start) return undefined;\n\n\ttry {\n\t\tconst parsed = JSON.parse(errorMessage.slice(start, end + 1)) as JsonValue;\n\t\tif (!isJsonRecord(parsed)) return undefined;\n\n\t\tconst nestedError = parsed.error;\n\t\tif (nestedError !== undefined && isJsonRecord(nestedError)) {\n\t\t\treturn {\n\t\t\t\tmessage: stringField(nestedError, \"message\"),\n\t\t\t\tcode: stringField(nestedError, \"code\") ?? stringField(parsed, \"code\"),\n\t\t\t\tparam: stringField(nestedError, \"param\") ?? stringField(parsed, \"param\"),\n\t\t\t};\n\t\t}\n\n\t\treturn {\n\t\t\tmessage: stringField(parsed, \"message\"),\n\t\t\tcode: stringField(parsed, \"code\"),\n\t\t\tparam: stringField(parsed, \"param\"),\n\t\t};\n\t} catch {\n\t\treturn undefined;\n\t}\n}\n\nfunction isOutputBudgetUnderflowText(text: string): boolean {\n\tconst message = text.toLowerCase();\n\treturn OUTPUT_BUDGET_PARAMETER_PATTERN.test(message) && OUTPUT_BUDGET_UNDERFLOW_PATTERN.test(message);\n}\n\nfunction isStructuredOutputBudgetUnderflow(details: ProviderErrorDetails): boolean {\n\tconst message = details.message?.toLowerCase() ?? \"\";\n\tconst param = details.param?.toLowerCase();\n\tif (!OUTPUT_BUDGET_UNDERFLOW_PATTERN.test(message)) return false;\n\treturn param !== undefined ? OUTPUT_BUDGET_PARAMETER_PATTERN.test(param) : OUTPUT_BUDGET_PARAMETER_PATTERN.test(message);\n}\n\nexport function isRetryWorthyOutputBudgetError(assistantMessage: AssistantMessage): boolean {\n\tif (assistantMessage.stopReason !== \"error\" || !assistantMessage.errorMessage) return false;\n\tif (assistantMessage.api !== \"openai-responses\") return false;\n\n\tconst structuredDetails = parseProviderErrorDetails(assistantMessage.errorMessage);\n\tif (structuredDetails && isStructuredOutputBudgetUnderflow(structuredDetails)) return true;\n\n\treturn isOutputBudgetUnderflowText(assistantMessage.errorMessage);\n}\n\nexport function shouldRetryAfterThresholdCompaction(assistantMessage: AssistantMessage): boolean {\n\treturn isRetryWorthyLengthStop(assistantMessage) || isRetryWorthyOutputBudgetError(assistantMessage);\n}\n\n/**\n * Internal: remove an incomplete assistant from retry context before auto-continuing after compaction.\n */\n\nexport function _dropTrailingAutoCompactionRetryAssistantIfPresent(this: AgentSession): void {\n\tconst messages = this.agent.state.messages;\n\tconst lastMsg = messages[messages.length - 1];\n\tif (lastMsg?.role !== \"assistant\") return;\n\tconst stopReason = (lastMsg as AssistantMessage).stopReason;\n\tif (stopReason === \"error\" || stopReason === \"length\") {\n\t\tthis.agent.state.messages = messages.slice(0, -1);\n\t}\n}\n\n/**\n * Internal: schedule a live post-event continuation probe after compaction_end listeners can flush queues.\n */\n\nexport function _schedulePostAutoCompactionContinuationProbe(this: AgentSession,\n\t_reason: \"overflow\" | \"threshold\",\n\twillRetry: boolean,\n): void {\n\tif (willRetry) {\n\t\tconst token = this._postCompactionContinuationToken + 1;\n\t\tthis._postCompactionContinuationToken = token;\n\t\tlet pending: Promise<void>;\n\t\tpending = new Promise<void>((resolve) => {\n\t\t\tsetTimeout(() => {\n\t\t\t\tvoid (async () => {\n\t\t\t\t\ttry {\n\t\t\t\t\t\tif (this._postCompactionContinuationToken !== token) return;\n\t\t\t\t\t\tif (this.isCompacting || this.isStreaming) return;\n\t\t\t\t\t\tawait this._resumeAfterAutoCompaction();\n\t\t\t\t\t} finally {\n\t\t\t\t\t\tif (this._pendingPostCompactionContinuation === pending) {\n\t\t\t\t\t\t\tthis._pendingPostCompactionContinuation = undefined;\n\t\t\t\t\t\t}\n\t\t\t\t\t\tresolve();\n\t\t\t\t\t}\n\t\t\t\t})();\n\t\t\t}, 100);\n\t\t});\n\t\tthis._pendingPostCompactionContinuation = pending;\n\t\treturn;\n\t}\n\n\tsetTimeout(() => {\n\t\tif (this.isCompacting || this.isStreaming) {\n\t\t\treturn;\n\t\t}\n\n\t\tif (!this.agent.hasQueuedMessages()) {\n\t\t\treturn;\n\t\t}\n\n\t\tvoid this._resumeAfterAutoCompaction();\n\t}, 100);\n}\n\nexport async function _awaitPendingPostCompactionContinuation(this: AgentSession): Promise<void> {\n\tconst pending = this._pendingPostCompactionContinuation;\n\tif (pending === undefined) return;\n\tawait pending;\n}\n\n/**\n * Internal: resume generation after successful auto-compaction only when active work remains.\n */\n\nexport async function _resumeAfterAutoCompaction(this: AgentSession): Promise<void> {\n\ttry {\n\t\tawait this._runAgentContinue();\n\t} catch (error) {\n\t\tconst message = error instanceof Error ? error.message : String(error);\n\t\tthis._emit({\n\t\t\ttype: \"agent_continue_error\",\n\t\t\tsource: \"post_compaction\",\n\t\t\terrorMessage: `Post-compaction continuation failed: ${message}`,\n\t\t});\n\t}\n}\n\n/**\n * Internal: resume a response that was truncated at the output-token cap\n * (\"length\") when the context does not warrant compaction. The generation is\n * continued directly so the model finishes the work it was cut off from, rather\n * than dead-ending on the truncation. Bounded by MAX_LENGTH_CONTINUATION_ATTEMPTS\n * so a turn that keeps exceeding the per-turn output cap can still terminate.\n */\n\nexport function _resumeAfterLengthTruncation(this: AgentSession): void {\n\tif (this._lengthContinuationAttempts >= MAX_LENGTH_CONTINUATION_ATTEMPTS) return;\n\tthis._lengthContinuationAttempts += 1;\n\t// agent.continue() rejects an assistant tail; drop the incomplete\n\t// length-stopped message so the preceding user/tool-result anchors the\n\t// continuation. It remains persisted in session history.\n\tthis._dropTrailingAutoCompactionRetryAssistantIfPresent();\n\tthis._schedulePostAutoCompactionContinuationProbe(\"threshold\", true);\n}\n\n\nfunction overflowUnresolved(reason: \"overflow\" | \"threshold\", aborted = false): boolean | undefined {\n\treturn reason === \"overflow\" && !aborted ? true : undefined;\n}\n\nexport async function _runAutoCompaction(this: AgentSession, reason: \"overflow\" | \"threshold\", willRetry: boolean): Promise<void> {\n\tthis._emit({ type: \"compaction_start\", reason });\n\tthis._autoCompactionAbortController = new AbortController();\n\n\ttry {\n\t\tif (!this.model) {\n\t\t\tthis._emit({\n\t\t\t\ttype: \"compaction_end\",\n\t\t\t\treason,\n\t\t\t\tresult: undefined,\n\t\t\t\taborted: false,\n\t\t\t\twillRetry: false,\n\t\t\t\tunresolvedOverflow: overflowUnresolved(reason),\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\t// Auth is resolved lazily: only called when the planner fallback is needed.\n\t\t// This allows extension-provided deletion requests to run before auth is checked,\n\t\t// enabling local extension compaction even when API credentials are unavailable.\n\t\t// Auto-mode resolver returns undefined (rather than throwing) when auth is missing;\n\t\t// overflow recovery then falls back to deterministic no-auth eviction, while\n\t\t// threshold compaction keeps the previous no-op behavior.\n\t\tconst model = this.model;\n\t\tconst result = await this._applyContextVerbatimCompaction({\n\t\t\tresolvePlannerAuth: async () => {\n\t\t\t\tconst authResult = await this._modelRegistry.getApiKeyAndHeaders(model);\n\t\t\t\tif (!authResult.ok || !authResult.apiKey) {\n\t\t\t\t\treturn undefined;\n\t\t\t\t}\n\t\t\t\treturn { apiKey: authResult.apiKey, headers: authResult.headers };\n\t\t\t},\n\t\t\tabortController: this._autoCompactionAbortController,\n\t\t\tbackupLabel: reason === \"overflow\" ? \"overflow-auto-compact\" : \"auto-compact\",\n\t\t\treason,\n\t\t});\n\t\tif (!result) {\n\t\t\tthis._emit({\n\t\t\t\ttype: \"compaction_end\",\n\t\t\t\treason,\n\t\t\t\tresult: undefined,\n\t\t\t\taborted: false,\n\t\t\t\twillRetry: false,\n\t\t\t\tunresolvedOverflow: overflowUnresolved(reason),\n\t\t\t});\n\t\t\treturn;\n\t\t}\n\n\t\tif (willRetry) {\n\t\t\tthis._dropTrailingAutoCompactionRetryAssistantIfPresent();\n\t\t}\n\n\t\tthis._emit({ type: \"compaction_end\", reason, result, aborted: false, willRetry });\n\t\tthis._schedulePostAutoCompactionContinuationProbe(reason, willRetry);\n\t} catch (error) {\n\t\tconst errorMessage = error instanceof Error ? error.message : \"compaction failed\";\n\t\tconst aborted = errorMessage === \"Compaction cancelled\" || (error instanceof Error && error.name === \"AbortError\");\n\t\tthis._emit({\n\t\t\ttype: \"compaction_end\",\n\t\t\treason,\n\t\t\tresult: undefined,\n\t\t\taborted,\n\t\t\twillRetry: false,\n\t\t\tunresolvedOverflow: overflowUnresolved(reason, aborted),\n\t\t\terrorMessage: aborted\n\t\t\t\t? undefined\n\t\t\t\t: reason === \"overflow\"\n\t\t\t\t\t? `Context overflow recovery failed: ${errorMessage}`\n\t\t\t\t\t: `Auto-compaction failed: ${errorMessage}`,\n\t\t});\n\t} finally {\n\t\tthis._autoCompactionAbortController = undefined;\n\t}\n}\n\n/**\n * Toggle auto-compaction setting.\n */\n\nexport const agentSessionAutoCompactionMethods = {\n\t_awaitPendingPostCompactionContinuation,\n\t_checkCompaction,\n\t_isCopilotServerCapBelowSelectedContextWindow,\n\t_dropTrailingAutoCompactionRetryAssistantIfPresent,\n\t_schedulePostAutoCompactionContinuationProbe,\n\t_resumeAfterAutoCompaction,\n\t_resumeAfterLengthTruncation,\n\t_runAutoCompaction,\n};\n"]}