@gajae-code/coding-agent 0.11.0 → 0.11.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (418) hide show
  1. package/CHANGELOG.md +63 -1
  2. package/dist/types/async/job-manager.d.ts +46 -7
  3. package/dist/types/cli/config-cli.d.ts +11 -1
  4. package/dist/types/cli/notify-cli.d.ts +1 -0
  5. package/dist/types/cli/session-picker.d.ts +2 -1
  6. package/dist/types/cli/setup-cli.d.ts +1 -1
  7. package/dist/types/commands/mcp-serve.d.ts +33 -0
  8. package/dist/types/commands/mcp.d.ts +1 -1
  9. package/dist/types/commands/notify.d.ts +0 -2
  10. package/dist/types/commands/sdk.d.ts +1 -1
  11. package/dist/types/config/atomic-yaml-patch.d.ts +18 -0
  12. package/dist/types/config/file-lock.d.ts +14 -4
  13. package/dist/types/config/keybindings.d.ts +42 -1
  14. package/dist/types/config/model-auth.d.ts +3 -0
  15. package/dist/types/config/model-bindings-applier.d.ts +13 -0
  16. package/dist/types/config/model-discovery-manager.d.ts +49 -0
  17. package/dist/types/config/model-equivalence.d.ts +20 -0
  18. package/dist/types/config/model-registry.d.ts +16 -15
  19. package/dist/types/config/model-resolver.d.ts +28 -18
  20. package/dist/types/config/models-config-schema.d.ts +11 -0
  21. package/dist/types/config/settings-schema.d.ts +56 -1
  22. package/dist/types/config/settings.d.ts +14 -11
  23. package/dist/types/coordinator-mcp/policy.d.ts +5 -0
  24. package/dist/types/coordinator-mcp/question-gate-codec.d.ts +129 -0
  25. package/dist/types/coordinator-mcp/question-state.d.ts +356 -0
  26. package/dist/types/coordinator-mcp/server.d.ts +4 -0
  27. package/dist/types/discovery/helpers.d.ts +0 -6
  28. package/dist/types/edit/index.d.ts +3 -0
  29. package/dist/types/eval/py/env.d.ts +9 -0
  30. package/dist/types/extensibility/extensions/loader.d.ts +2 -1
  31. package/dist/types/extensibility/extensions/runner.d.ts +2 -2
  32. package/dist/types/extensibility/extensions/types.d.ts +34 -3
  33. package/dist/types/extensibility/hooks/runner.d.ts +1 -1
  34. package/dist/types/extensibility/shared-events.d.ts +2 -0
  35. package/dist/types/gjc-runtime/deep-interview-recorder.d.ts +10 -1
  36. package/dist/types/gjc-runtime/deep-interview-state.d.ts +44 -0
  37. package/dist/types/gjc-runtime/goal-mode-request.d.ts +11 -1
  38. package/dist/types/gjc-runtime/launch-worktree.d.ts +4 -2
  39. package/dist/types/gjc-runtime/linux-proc.d.ts +29 -0
  40. package/dist/types/gjc-runtime/session-resolution.d.ts +2 -2
  41. package/dist/types/gjc-runtime/state-migrations.d.ts +1 -1
  42. package/dist/types/gjc-runtime/state-writer.d.ts +2 -1
  43. package/dist/types/gjc-runtime/team-launch.d.ts +59 -0
  44. package/dist/types/gjc-runtime/team-notify.d.ts +45 -0
  45. package/dist/types/gjc-runtime/team-runtime.d.ts +29 -121
  46. package/dist/types/gjc-runtime/team-store.d.ts +160 -0
  47. package/dist/types/gjc-runtime/team-workers.d.ts +78 -0
  48. package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +8 -0
  49. package/dist/types/gjc-runtime/ultragoal-change-set.d.ts +13 -0
  50. package/dist/types/gjc-runtime/ultragoal-evidence.d.ts +38 -0
  51. package/dist/types/gjc-runtime/ultragoal-guard.d.ts +7 -0
  52. package/dist/types/gjc-runtime/ultragoal-pipeline.d.ts +23 -0
  53. package/dist/types/gjc-runtime/ultragoal-receipt-freshness.d.ts +11 -0
  54. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +55 -33
  55. package/dist/types/gjc-runtime/workflow-cli-common.d.ts +8 -0
  56. package/dist/types/gjc-runtime/workflow-manifest.d.ts +3 -0
  57. package/dist/types/goals/continuation-timeout-guard.d.ts +22 -0
  58. package/dist/types/goals/runtime.d.ts +1 -0
  59. package/dist/types/goals/state.d.ts +8 -0
  60. package/dist/types/harness-control-plane/lifecycle-reconciliation.d.ts +17 -0
  61. package/dist/types/harness-control-plane/phase-rollup.d.ts +4 -7
  62. package/dist/types/harness-control-plane/receipt-ingest.d.ts +5 -0
  63. package/dist/types/harness-control-plane/receipt-spool.d.ts +4 -0
  64. package/dist/types/hindsight/state.d.ts +11 -4
  65. package/dist/types/lsp/lspmux.d.ts +5 -3
  66. package/dist/types/lsp/path-trust.d.ts +1 -0
  67. package/dist/types/main.d.ts +3 -2
  68. package/dist/types/memories/storage.d.ts +1 -0
  69. package/dist/types/modes/action-registry.d.ts +29 -0
  70. package/dist/types/modes/components/chrome.d.ts +14 -0
  71. package/dist/types/modes/components/command-palette.d.ts +27 -0
  72. package/dist/types/modes/components/custom-provider-wizard.d.ts +1 -0
  73. package/dist/types/modes/components/index.d.ts +2 -1
  74. package/dist/types/modes/components/pet-selector.d.ts +1 -1
  75. package/dist/types/modes/components/plan-preview-overlay.d.ts +43 -0
  76. package/dist/types/modes/components/queue-mode-selector.d.ts +1 -1
  77. package/dist/types/modes/components/queue-pane.d.ts +12 -0
  78. package/dist/types/modes/components/read-tool-group.d.ts +6 -0
  79. package/dist/types/modes/components/session-observer-overlay.d.ts +3 -4
  80. package/dist/types/modes/components/sessions-dashboard.d.ts +32 -0
  81. package/dist/types/modes/components/settings-selector.d.ts +1 -1
  82. package/dist/types/modes/components/show-images-selector.d.ts +1 -1
  83. package/dist/types/modes/components/status-line/types.d.ts +1 -1
  84. package/dist/types/modes/components/status-line.d.ts +1 -94
  85. package/dist/types/modes/components/tasks-pane.d.ts +15 -0
  86. package/dist/types/modes/components/theme-selector.d.ts +1 -1
  87. package/dist/types/modes/components/tool-execution.d.ts +15 -0
  88. package/dist/types/modes/components/tool-status-header.d.ts +111 -0
  89. package/dist/types/modes/components/transcript-viewer-overlay.d.ts +42 -0
  90. package/dist/types/modes/controllers/command-controller.d.ts +2 -2
  91. package/dist/types/modes/controllers/extension-ui-controller.d.ts +1 -0
  92. package/dist/types/modes/controllers/goal-mode-controller.d.ts +62 -0
  93. package/dist/types/modes/controllers/input-controller.d.ts +8 -1
  94. package/dist/types/modes/controllers/mode-gate.d.ts +8 -0
  95. package/dist/types/modes/controllers/plan-mode-controller.d.ts +65 -0
  96. package/dist/types/modes/controllers/selector-controller.d.ts +15 -3
  97. package/dist/types/modes/controllers/stt-controller.d.ts +26 -0
  98. package/dist/types/modes/interactive-mode.d.ts +21 -19
  99. package/dist/types/modes/jobs-observer.d.ts +2 -0
  100. package/dist/types/modes/shared/agent-wire/deep-interview-gate.d.ts +4 -11
  101. package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +21 -2
  102. package/dist/types/modes/shared/agent-wire/workflow-gate-types.d.ts +31 -0
  103. package/dist/types/modes/tasks-aggregator.d.ts +39 -0
  104. package/dist/types/modes/transcript-item-registry.d.ts +60 -0
  105. package/dist/types/modes/types.d.ts +22 -15
  106. package/dist/types/modes/utils/ui-helpers.d.ts +2 -2
  107. package/dist/types/runtime-mcp/manager.d.ts +1 -1
  108. package/dist/types/sdk/broker/broker.d.ts +78 -12
  109. package/dist/types/sdk/broker/ensure.d.ts +9 -0
  110. package/dist/types/sdk/broker/lifecycle-codec.d.ts +4 -0
  111. package/dist/types/sdk/broker/lifecycle-ledger.d.ts +15 -1
  112. package/dist/types/sdk/broker/lifecycle.d.ts +6 -2
  113. package/dist/types/sdk/broker/process-incarnation.d.ts +2 -0
  114. package/dist/types/sdk/broker/session-index.d.ts +0 -1
  115. package/dist/types/sdk/broker/state-version.d.ts +2 -0
  116. package/dist/types/sdk/bus/chat-effect-journal.d.ts +1 -0
  117. package/dist/types/sdk/bus/config-commands.d.ts +1 -1
  118. package/dist/types/sdk/bus/config.d.ts +11 -0
  119. package/dist/types/sdk/bus/daemon-paths.d.ts +1 -0
  120. package/dist/types/sdk/bus/discord-daemon.d.ts +5 -0
  121. package/dist/types/sdk/bus/html-format.d.ts +3 -2
  122. package/dist/types/sdk/bus/index.d.ts +27 -0
  123. package/dist/types/sdk/bus/lifecycle-control-runtime.d.ts +4 -0
  124. package/dist/types/sdk/bus/notification-service.d.ts +20 -0
  125. package/dist/types/sdk/bus/rate-limit-pool.d.ts +13 -2
  126. package/dist/types/sdk/bus/recent-activity.d.ts +21 -12
  127. package/dist/types/sdk/bus/rich-document.d.ts +7 -0
  128. package/dist/types/sdk/bus/rich-render.d.ts +18 -41
  129. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +9 -4
  130. package/dist/types/sdk/bus/telegram-daemon.d.ts +69 -3
  131. package/dist/types/sdk/bus/threaded-inbound.d.ts +1 -1
  132. package/dist/types/sdk/bus/threaded-render.d.ts +13 -0
  133. package/dist/types/sdk/bus/topic-registry.d.ts +24 -2
  134. package/dist/types/sdk/host/control/operations.d.ts +1 -1
  135. package/dist/types/sdk/host/host.d.ts +2 -0
  136. package/dist/types/sdk/index.d.ts +1 -0
  137. package/dist/types/sdk/session-directory.d.ts +56 -0
  138. package/dist/types/session/agent-session.d.ts +82 -47
  139. package/dist/types/session/agent-storage.d.ts +7 -0
  140. package/dist/types/session/fallback-chain-controller.d.ts +10 -2
  141. package/dist/types/session/internal/managed-session-scope.d.ts +102 -0
  142. package/dist/types/session/internal/managed-session-storage.d.ts +38 -0
  143. package/dist/types/session/messages.d.ts +16 -0
  144. package/dist/types/session/session-manager-internal.d.ts +14 -0
  145. package/dist/types/session/session-manager.d.ts +82 -12
  146. package/dist/types/session/session-storage.d.ts +43 -3
  147. package/dist/types/session/volatile-context-pruning.d.ts +15 -0
  148. package/dist/types/skill-state/{deep-interview-mutation-guard.d.ts → workflow-mutation-guard.d.ts} +24 -8
  149. package/dist/types/skill-state/workflow-state-contract.d.ts +1 -3
  150. package/dist/types/slash-commands/builtin-registry.d.ts +4 -1
  151. package/dist/types/system-prompt.d.ts +2 -11
  152. package/dist/types/task/executor.d.ts +3 -1
  153. package/dist/types/thinking.d.ts +7 -0
  154. package/dist/types/tool-discovery/tool-index.d.ts +1 -50
  155. package/dist/types/tools/ask.d.ts +64 -2
  156. package/dist/types/tools/bash.d.ts +3 -0
  157. package/dist/types/tools/browser/dead-tab-recovery.d.ts +24 -0
  158. package/dist/types/tools/browser/tab-supervisor.d.ts +21 -0
  159. package/dist/types/tools/cron.d.ts +2 -0
  160. package/dist/types/tools/fetch.d.ts +1 -0
  161. package/dist/types/tools/index.d.ts +30 -15
  162. package/dist/types/tools/output-meta.d.ts +1 -0
  163. package/dist/types/tools/read.d.ts +3 -0
  164. package/dist/types/tools/search-tool-bm25.d.ts +3 -4
  165. package/dist/types/tools/subagent-render.d.ts +1 -0
  166. package/dist/types/tools/subagent.d.ts +6 -1
  167. package/dist/types/utils/event-bus.d.ts +1 -1
  168. package/dist/types/web/search/index.d.ts +3 -1
  169. package/package.json +21 -9
  170. package/scripts/build-sdk-package-smoke.ts +39 -1
  171. package/scripts/generate-hotkeys-docs.ts +74 -0
  172. package/scripts/generate-sdk-operation-inventory.ts +64 -20
  173. package/scripts/verify-gjc-sdk-canonicalization.ts +182 -2
  174. package/src/async/job-manager.ts +455 -69
  175. package/src/cli/config-cli.ts +75 -2
  176. package/src/cli/notify-cli.ts +92 -26
  177. package/src/cli/session-picker.ts +20 -3
  178. package/src/cli/setup-cli.ts +1 -1
  179. package/src/cli.ts +1 -1
  180. package/src/commands/config.ts +1 -1
  181. package/src/commands/harness.ts +34 -106
  182. package/src/commands/launch.ts +6 -4
  183. package/src/commands/mcp-serve.ts +85 -8
  184. package/src/commands/mcp.ts +2 -2
  185. package/src/commands/notify.ts +8 -4
  186. package/src/commands/sdk.ts +61 -40
  187. package/src/commands/ultragoal.ts +4 -1
  188. package/src/config/atomic-yaml-patch.ts +80 -13
  189. package/src/config/file-lock.ts +105 -43
  190. package/src/config/keybindings.ts +64 -20
  191. package/src/config/model-auth.ts +9 -0
  192. package/src/config/model-bindings-applier.ts +139 -0
  193. package/src/config/model-discovery-manager.ts +224 -0
  194. package/src/config/model-equivalence.ts +66 -0
  195. package/src/config/model-registry.ts +169 -302
  196. package/src/config/model-resolver.ts +361 -369
  197. package/src/config/models-config-schema.ts +5 -4
  198. package/src/config/settings-schema.ts +159 -2
  199. package/src/config/settings.ts +458 -482
  200. package/src/coordinator-mcp/policy.ts +76 -11
  201. package/src/coordinator-mcp/question-gate-codec.ts +274 -0
  202. package/src/coordinator-mcp/question-state.ts +778 -0
  203. package/src/coordinator-mcp/server.ts +1702 -238
  204. package/src/debug/index.ts +1 -1
  205. package/src/defaults/gjc/skills/deep-interview/SKILL.md +13 -1
  206. package/src/defaults/gjc/skills/team/SKILL.md +16 -0
  207. package/src/discovery/helpers.ts +1 -50
  208. package/src/edit/index.ts +64 -0
  209. package/src/eval/py/env.ts +26 -0
  210. package/src/eval/py/kernel.ts +5 -3
  211. package/src/extensibility/extensions/loader.ts +9 -1
  212. package/src/extensibility/extensions/runner.ts +12 -3
  213. package/src/extensibility/extensions/types.ts +39 -1
  214. package/src/extensibility/gjc-plugins/installer.ts +16 -16
  215. package/src/extensibility/hooks/runner.ts +2 -2
  216. package/src/extensibility/runtime-skill-discovery.ts +33 -13
  217. package/src/extensibility/shared-events.ts +2 -0
  218. package/src/gjc-runtime/deep-interview-recorder.ts +63 -3
  219. package/src/gjc-runtime/deep-interview-runtime.ts +76 -27
  220. package/src/gjc-runtime/deep-interview-state.ts +317 -5
  221. package/src/gjc-runtime/goal-mode-request.ts +55 -8
  222. package/src/gjc-runtime/launch-tmux.ts +2 -5
  223. package/src/gjc-runtime/launch-worktree.ts +5 -5
  224. package/src/gjc-runtime/linux-proc.ts +71 -0
  225. package/src/gjc-runtime/ralplan-runtime.ts +74 -90
  226. package/src/gjc-runtime/session-resolution.ts +18 -3
  227. package/src/gjc-runtime/session-state-sidecar.ts +6 -107
  228. package/src/gjc-runtime/state-migrations.ts +1 -1
  229. package/src/gjc-runtime/state-runtime.ts +320 -249
  230. package/src/gjc-runtime/state-writer.ts +36 -40
  231. package/src/gjc-runtime/team-launch.ts +319 -0
  232. package/src/gjc-runtime/team-notify.ts +326 -0
  233. package/src/gjc-runtime/team-runtime.ts +1099 -1482
  234. package/src/gjc-runtime/team-store.ts +828 -0
  235. package/src/gjc-runtime/team-workers.ts +962 -0
  236. package/src/gjc-runtime/tmux-owner-isolation-cli.ts +2 -11
  237. package/src/gjc-runtime/tmux-owner-isolation.ts +14 -9
  238. package/src/gjc-runtime/tmux-sessions.ts +4 -21
  239. package/src/gjc-runtime/ultragoal-change-set.ts +172 -0
  240. package/src/gjc-runtime/ultragoal-evidence.ts +874 -0
  241. package/src/gjc-runtime/ultragoal-guard.ts +83 -12
  242. package/src/gjc-runtime/ultragoal-pipeline.ts +306 -0
  243. package/src/gjc-runtime/ultragoal-receipt-freshness.ts +102 -1
  244. package/src/gjc-runtime/ultragoal-runtime.ts +156 -1319
  245. package/src/gjc-runtime/workflow-cli-common.ts +30 -0
  246. package/src/gjc-runtime/workflow-manifest.generated.json +58 -0
  247. package/src/gjc-runtime/workflow-manifest.ts +18 -0
  248. package/src/goals/continuation-timeout-guard.ts +79 -0
  249. package/src/goals/runtime.ts +7 -5
  250. package/src/goals/state.ts +18 -0
  251. package/src/harness-control-plane/lifecycle-reconciliation.ts +64 -0
  252. package/src/harness-control-plane/phase-rollup.ts +4 -7
  253. package/src/harness-control-plane/receipt-ingest.ts +5 -0
  254. package/src/harness-control-plane/receipt-spool.ts +51 -7
  255. package/src/harness-control-plane/session-lease.ts +12 -130
  256. package/src/hindsight/backend.ts +26 -27
  257. package/src/hindsight/state.ts +101 -32
  258. package/src/hooks/skill-state.ts +17 -11
  259. package/src/internal-urls/docs-index.generated.ts +21 -18
  260. package/src/lsp/client.ts +1 -1
  261. package/src/lsp/config.ts +145 -26
  262. package/src/lsp/index.ts +1 -1
  263. package/src/lsp/lspmux.ts +39 -10
  264. package/src/lsp/path-trust.ts +81 -0
  265. package/src/main.ts +28 -8
  266. package/src/memories/index.ts +1 -0
  267. package/src/memories/storage.ts +6 -2
  268. package/src/modes/action-registry.ts +165 -0
  269. package/src/modes/components/chrome.ts +40 -0
  270. package/src/modes/components/command-palette.ts +168 -0
  271. package/src/modes/components/custom-editor.ts +1 -1
  272. package/src/modes/components/custom-provider-wizard.ts +51 -14
  273. package/src/modes/components/index.ts +2 -1
  274. package/src/modes/components/pet-selector.ts +11 -17
  275. package/src/modes/components/plan-preview-overlay.ts +295 -0
  276. package/src/modes/components/queue-mode-selector.ts +10 -27
  277. package/src/modes/components/queue-pane.ts +79 -0
  278. package/src/modes/components/read-tool-group.ts +13 -0
  279. package/src/modes/components/session-observer-overlay.ts +207 -772
  280. package/src/modes/components/sessions-dashboard.ts +181 -0
  281. package/src/modes/components/settings-selector.ts +1 -1
  282. package/src/modes/components/show-images-selector.ts +10 -24
  283. package/src/modes/components/status-line/types.ts +1 -1
  284. package/src/modes/components/status-line.ts +1 -969
  285. package/src/modes/components/tasks-pane.ts +73 -0
  286. package/src/modes/components/theme-selector.ts +11 -31
  287. package/src/modes/components/tool-execution.ts +22 -0
  288. package/src/modes/components/tool-status-header.ts +1056 -0
  289. package/src/modes/components/transcript-viewer-overlay.ts +384 -0
  290. package/src/modes/controllers/btw-controller.ts +1 -0
  291. package/src/modes/controllers/command-controller.ts +9 -15
  292. package/src/modes/controllers/event-controller.ts +3 -2
  293. package/src/modes/controllers/extension-ui-controller.ts +72 -25
  294. package/src/modes/controllers/goal-mode-controller.ts +474 -0
  295. package/src/modes/controllers/input-controller.ts +764 -229
  296. package/src/modes/controllers/mode-gate.ts +24 -0
  297. package/src/modes/controllers/plan-mode-controller.ts +517 -0
  298. package/src/modes/controllers/selector-controller.ts +225 -10
  299. package/src/modes/controllers/stt-controller.ts +125 -0
  300. package/src/modes/interactive-mode.ts +302 -1151
  301. package/src/modes/jobs-observer.ts +3 -0
  302. package/src/modes/runtime-init.ts +8 -0
  303. package/src/modes/shared/agent-wire/deep-interview-gate.ts +31 -106
  304. package/src/modes/shared/agent-wire/workflow-gate-broker.ts +78 -19
  305. package/src/modes/shared/agent-wire/workflow-gate-types.ts +177 -0
  306. package/src/modes/tasks-aggregator.ts +199 -0
  307. package/src/modes/transcript-item-registry.ts +229 -0
  308. package/src/modes/types.ts +47 -20
  309. package/src/modes/utils/ui-helpers.ts +17 -9
  310. package/src/prompts/agent-fragments/ralplan-persistence.md +6 -0
  311. package/src/prompts/agent-fragments/restricted-bash.md +1 -0
  312. package/src/prompts/agents/architect.md +2 -8
  313. package/src/prompts/agents/critic.md +2 -8
  314. package/src/prompts/agents/executor.md +3 -0
  315. package/src/prompts/agents/planner.md +4 -12
  316. package/src/prompts/system/custom-system-prompt.md +1 -23
  317. package/src/prompts/system/plan-mode-approved.md +8 -0
  318. package/src/prompts/system/system-prompt.md +17 -96
  319. package/src/prompts/tools/bash.md +4 -4
  320. package/src/prompts/tools/browser.md +15 -52
  321. package/src/prompts/tools/find.md +2 -2
  322. package/src/prompts/tools/hashline.md +13 -55
  323. package/src/prompts/tools/read.md +3 -6
  324. package/src/prompts/tools/search-tool-bm25.md +0 -2
  325. package/src/prompts/tools/search.md +2 -4
  326. package/src/prompts/tools/skill-discovery.md +1 -2
  327. package/src/prompts/tools/task.md +1 -1
  328. package/src/runtime-mcp/manager.ts +1 -1
  329. package/src/sdk/broker/broker.ts +206 -78
  330. package/src/sdk/broker/discovery.ts +21 -2
  331. package/src/sdk/broker/ensure.ts +85 -25
  332. package/src/sdk/broker/lifecycle-codec.ts +11 -0
  333. package/src/sdk/broker/lifecycle-ledger.ts +371 -74
  334. package/src/sdk/broker/lifecycle.ts +1399 -142
  335. package/src/sdk/broker/process-incarnation.ts +5 -13
  336. package/src/sdk/broker/session-index.ts +145 -56
  337. package/src/sdk/broker/state-version.ts +21 -0
  338. package/src/sdk/broker/transport.ts +1 -1
  339. package/src/sdk/bus/chat-daemon-cli.ts +2 -0
  340. package/src/sdk/bus/chat-daemon-control.ts +8 -28
  341. package/src/sdk/bus/chat-effect-journal.ts +14 -4
  342. package/src/sdk/bus/config-commands.ts +5 -4
  343. package/src/sdk/bus/config.ts +98 -0
  344. package/src/sdk/bus/conversation-store.ts +5 -29
  345. package/src/sdk/bus/daemon-paths.ts +2 -0
  346. package/src/sdk/bus/discord-daemon.ts +25 -8
  347. package/src/sdk/bus/html-format.ts +20 -3
  348. package/src/sdk/bus/index.ts +834 -113
  349. package/src/sdk/bus/lifecycle-control-runtime.ts +91 -33
  350. package/src/sdk/bus/native-runtime-compatibility.ts +1 -1
  351. package/src/sdk/bus/natives-augment.d.ts +26 -1
  352. package/src/sdk/bus/notification-service.ts +168 -20
  353. package/src/sdk/bus/rate-limit-pool.ts +58 -14
  354. package/src/sdk/bus/recent-activity.ts +188 -74
  355. package/src/sdk/bus/rich-document.ts +258 -0
  356. package/src/sdk/bus/rich-render.ts +62 -70
  357. package/src/sdk/bus/telegram-daemon-cli.ts +15 -59
  358. package/src/sdk/bus/telegram-daemon-contract.ts +9 -4
  359. package/src/sdk/bus/telegram-daemon.ts +1491 -247
  360. package/src/sdk/bus/threaded-inbound.ts +8 -3
  361. package/src/sdk/bus/threaded-render.ts +57 -1
  362. package/src/sdk/bus/topic-registry.ts +120 -13
  363. package/src/sdk/host/control/dispatch.ts +14 -2
  364. package/src/sdk/host/control/operations.ts +6 -1
  365. package/src/sdk/host/host.ts +31 -1
  366. package/src/sdk/host/query/handlers.ts +20 -22
  367. package/src/sdk/host/query/revision-store.ts +80 -57
  368. package/src/sdk/index.ts +1 -0
  369. package/src/sdk/protocol/operation-inventory.generated.json +943 -1907
  370. package/src/sdk/session-directory.ts +141 -0
  371. package/src/sdk/session.ts +458 -479
  372. package/src/session/agent-session.ts +1901 -637
  373. package/src/session/agent-storage.ts +18 -0
  374. package/src/session/cache-economics.ts +25 -8
  375. package/src/session/fallback-chain-controller.ts +53 -3
  376. package/src/session/internal/managed-session-scope.ts +2226 -0
  377. package/src/session/internal/managed-session-storage.ts +453 -0
  378. package/src/session/messages.ts +91 -2
  379. package/src/session/session-manager-internal.ts +33 -0
  380. package/src/session/session-manager.ts +1112 -333
  381. package/src/session/session-storage.ts +444 -39
  382. package/src/session/volatile-context-pruning.ts +78 -0
  383. package/src/setup/credential-auto-import.ts +2 -1
  384. package/src/setup/hermes/templates/operator-instructions.v1.md +1 -1
  385. package/src/setup/provider-onboarding.ts +36 -61
  386. package/src/setup/provider-presets.json +5 -0
  387. package/src/skill-state/active-state.ts +3 -13
  388. package/src/skill-state/{deep-interview-mutation-guard.ts → workflow-mutation-guard.ts} +47 -29
  389. package/src/skill-state/workflow-state-contract.ts +6 -41
  390. package/src/slash-commands/builtin-registry.ts +46 -11
  391. package/src/system-prompt.ts +37 -44
  392. package/src/task/agents.ts +19 -1
  393. package/src/task/executor.ts +90 -6
  394. package/src/task/index.ts +62 -37
  395. package/src/thinking.ts +21 -6
  396. package/src/tool-discovery/tool-index.ts +2 -134
  397. package/src/tools/ask.ts +157 -21
  398. package/src/tools/ast-edit.ts +1 -1
  399. package/src/tools/bash.ts +38 -0
  400. package/src/tools/browser/dead-tab-recovery.ts +78 -0
  401. package/src/tools/browser/tab-supervisor.ts +282 -36
  402. package/src/tools/computer.ts +10 -3
  403. package/src/tools/cron.ts +30 -16
  404. package/src/tools/eval.ts +1 -2
  405. package/src/tools/fetch.ts +11 -4
  406. package/src/tools/index.ts +76 -24
  407. package/src/tools/output-meta.ts +13 -5
  408. package/src/tools/read.ts +37 -1
  409. package/src/tools/resource-gc.ts +2 -2
  410. package/src/tools/search-tool-bm25.ts +11 -74
  411. package/src/tools/subagent-render.ts +19 -8
  412. package/src/tools/subagent.ts +84 -32
  413. package/src/utils/event-bus.ts +9 -4
  414. package/src/web/search/index.ts +3 -3
  415. package/dist/types/discovery/plugin-dir-roots.d.ts +0 -15
  416. package/dist/types/runtime-mcp/discoverable-tool-metadata.d.ts +0 -7
  417. package/src/discovery/plugin-dir-roots.ts +0 -28
  418. package/src/runtime-mcp/discoverable-tool-metadata.ts +0 -25
@@ -0,0 +1,384 @@
1
+ import {
2
+ Container,
3
+ Markdown,
4
+ type MarkdownTheme,
5
+ type MouseEvent,
6
+ matchesKey,
7
+ truncateToWidth,
8
+ wrapTextWithAnsi,
9
+ } from "@gajae-code/tui";
10
+ import { sanitizeText } from "@gajae-code/utils";
11
+ import { getMarkdownTheme, theme } from "../theme/theme";
12
+ import type { TranscriptItemRegistry, TranscriptSourcePayload } from "../transcript-item-registry";
13
+ import { DynamicBorder } from "./dynamic-border";
14
+
15
+ const INDENT = " ";
16
+ const PAGE_SIZE = 15;
17
+ const PREVIEW_LINES = 4;
18
+
19
+ export type TranscriptViewerEntry = {
20
+ id: string;
21
+ kind: string;
22
+ label?: string;
23
+ payload: TranscriptSourcePayload;
24
+ copyable?: boolean;
25
+ foldable?: boolean;
26
+ rawViewable?: boolean;
27
+ getDisplayText?: (expanded: boolean) => string;
28
+ };
29
+
30
+ export type TranscriptViewerOverlayOptions = {
31
+ title?: string;
32
+ getEntries: () => readonly TranscriptViewerEntry[];
33
+ onClose: () => void;
34
+ requestRender?: () => void;
35
+ copyToClipboard?: (text: string) => void;
36
+ onError?: (message: string) => void;
37
+ enterExpands?: boolean;
38
+ initialSelection?: "first" | "latest";
39
+ followTail?: boolean;
40
+ getHeaderLines?: () => readonly string[];
41
+ getFooterLines?: () => readonly string[];
42
+ maxExpandedLines?: number;
43
+ footerControls?: string;
44
+ getEntryText?: (entry: TranscriptViewerEntry, expanded: boolean) => string;
45
+ };
46
+
47
+ type RenderedEntry = { lineStart: number; lineCount: number };
48
+
49
+ /** Message-agnostic, modal transcript browser over resolved projection entries. */
50
+ export class TranscriptViewerOverlay extends Container {
51
+ #options: TranscriptViewerOverlayOptions;
52
+ #entries: readonly TranscriptViewerEntry[] = [];
53
+ #renderedEntries: RenderedEntry[] = [];
54
+ #expanded = new Set<string>();
55
+ #raw = new Set<string>();
56
+ #selected = 0;
57
+ #scrollOffset = 0;
58
+ #viewportHeight = 20;
59
+ #lines: string[] = [];
60
+ #fullscreen = false;
61
+ #mdTheme: MarkdownTheme = getMarkdownTheme();
62
+ #width = 80;
63
+ #initialized = false;
64
+ #contentOrigin = 3;
65
+ #followTailPending = false;
66
+ #skipFollowTailOnce = false;
67
+ #followTailActive = false;
68
+
69
+ constructor(options: TranscriptViewerOverlayOptions) {
70
+ super();
71
+ this.#options = options;
72
+ this.#followTailActive = options.followTail === true;
73
+ this.refresh();
74
+ }
75
+
76
+ get selectedEntryId(): string | undefined {
77
+ return this.#entries[this.#selected]?.id;
78
+ }
79
+ get isFullscreen(): boolean {
80
+ return this.#fullscreen;
81
+ }
82
+ refresh(identityMap?: ReadonlyMap<string, string>): void {
83
+ const previous = this.selectedEntryId;
84
+ const previousPosition = this.#selected;
85
+ const reconciledPrevious = previous ? (identityMap?.get(previous) ?? previous) : undefined;
86
+ const wasAtTail = this.#selected >= this.#entries.length - 1;
87
+ this.#entries = this.#options.getEntries();
88
+ const reconciledIndex = reconciledPrevious
89
+ ? this.#entries.findIndex(entry => entry.id === reconciledPrevious)
90
+ : -1;
91
+ if (this.#options.initialSelection === "latest" && !this.#initialized)
92
+ this.#selected = Math.max(0, this.#entries.length - 1);
93
+ else if (reconciledIndex >= 0) this.#selected = reconciledIndex;
94
+ else this.#selected = Math.min(previousPosition, Math.max(0, this.#entries.length - 1));
95
+ this.#followTailPending = Boolean(
96
+ this.#followTailActive && !this.#skipFollowTailOnce && (wasAtTail || !this.#initialized),
97
+ );
98
+ if (this.#initialized && this.#followTailActive && wasAtTail && !this.#skipFollowTailOnce)
99
+ this.#selected = Math.max(0, this.#entries.length - 1);
100
+ this.#skipFollowTailOnce = false;
101
+ this.#initialized = true;
102
+ this.#rebuild();
103
+ }
104
+ override render(width: number): string[] {
105
+ this.#width = Math.max(1, width);
106
+ this.refresh();
107
+ const defaultHeader = this.#fullscreen
108
+ ? [theme.fg("accent", "Transcript block")]
109
+ : [theme.fg("accent", sanitizeText(this.#options.title ?? "Transcript"))];
110
+ const header = this.#fullscreen
111
+ ? defaultHeader
112
+ : [...defaultHeader, ...(this.#options.getHeaderLines?.() ?? []).map(sanitizeText)];
113
+ const defaultFooter = this.#fullscreen
114
+ ? [theme.fg("dim", "Esc:back j/k:scroll PgUp/PgDn:page")]
115
+ : [
116
+ theme.fg(
117
+ "dim",
118
+ sanitizeText(
119
+ this.#options.footerControls ??
120
+ "j/k:select Space:expand Enter:fullscreen y:copy Y:metadata r:raw g/G:top/bottom Esc:close",
121
+ ),
122
+ ),
123
+ ];
124
+ const scrollRange =
125
+ this.#lines.length > this.#viewportHeight
126
+ ? theme.fg(
127
+ "dim",
128
+ `[${this.#scrollOffset + 1}-${Math.min(this.#scrollOffset + this.#viewportHeight, this.#lines.length)}/${this.#lines.length}]`,
129
+ )
130
+ : "";
131
+ const footer = this.#fullscreen
132
+ ? defaultFooter
133
+ : [...(this.#options.getFooterLines?.() ?? []).map(sanitizeText), scrollRange, ...defaultFooter].filter(
134
+ Boolean,
135
+ );
136
+ this.#viewportHeight = Math.max(5, (process.stdout.rows || 40) - header.length - footer.length - 5);
137
+ this.#contentOrigin = header.length + 2;
138
+ const maxScroll = Math.max(0, this.#lines.length - this.#viewportHeight);
139
+ if (this.#followTailPending) {
140
+ this.#scrollOffset = maxScroll;
141
+ this.#followTailPending = false;
142
+ }
143
+ this.#scrollOffset = Math.max(0, Math.min(this.#scrollOffset, maxScroll));
144
+ const visible = this.#lines.slice(this.#scrollOffset, this.#scrollOffset + this.#viewportHeight);
145
+ const lines = [
146
+ ...new DynamicBorder().render(this.#width),
147
+ ...header.map(line => ` ${line}`),
148
+ ...new DynamicBorder().render(this.#width),
149
+ ];
150
+ lines.push(...visible.map(line => ` ${line}`));
151
+ for (let i = visible.length; i < this.#viewportHeight; i++) lines.push("");
152
+ lines.push("", ...footer.map(line => ` ${line}`), ...new DynamicBorder().render(this.#width));
153
+ return lines.map(line => truncateToWidth(line, this.#width));
154
+ }
155
+
156
+ handleMouse(event: MouseEvent): void {
157
+ if (event.kind !== "click" || this.#fullscreen) return;
158
+ // Render has a border, header, and border before the first viewport row.
159
+ const contentLine = this.#scrollOffset + (event.localY ?? event.y) - this.#contentOrigin;
160
+ const index = this.#renderedEntries.findIndex(
161
+ entry => contentLine >= entry.lineStart && contentLine < entry.lineStart + entry.lineCount,
162
+ );
163
+ if (index < 0) return;
164
+ this.#followTailActive = false;
165
+ this.#selected = index;
166
+ this.#move(0);
167
+ this.#requestRender();
168
+ }
169
+ handleInput(keyData: string): void {
170
+ if (matchesKey(keyData, "escape")) {
171
+ if (this.#fullscreen) {
172
+ this.#fullscreen = false;
173
+ this.#scrollOffset = 0;
174
+ this.#requestRender();
175
+ } else this.#options.onClose();
176
+ return;
177
+ }
178
+ if (this.#fullscreen) {
179
+ this.#scroll(keyData);
180
+ return;
181
+ }
182
+ const count = this.#entries.length;
183
+ if (keyData === "j" || matchesKey(keyData, "down")) {
184
+ this.#move(1);
185
+ return;
186
+ }
187
+ if (keyData === "k" || matchesKey(keyData, "up")) {
188
+ this.#move(-1);
189
+ return;
190
+ }
191
+ if (matchesKey(keyData, "pageDown")) {
192
+ this.#page(1);
193
+ return;
194
+ }
195
+ if (matchesKey(keyData, "pageUp")) {
196
+ this.#page(-1);
197
+ return;
198
+ }
199
+ if (keyData === "g") {
200
+ this.#followTailActive = false;
201
+ this.#followTailPending = false;
202
+ this.#selected = 0;
203
+ this.#scrollOffset = 0;
204
+ this.#requestRender();
205
+ return;
206
+ }
207
+ if (keyData === "G") {
208
+ this.#followTailActive = true;
209
+ this.#followTailPending = true;
210
+ this.#selected = Math.max(0, count - 1);
211
+ this.#scrollOffset = this.#lines.length;
212
+ this.#requestRender();
213
+ return;
214
+ }
215
+ if (keyData === " " || (this.#options.enterExpands && (matchesKey(keyData, "enter") || keyData === "\r"))) {
216
+ this.#toggleExpand();
217
+ return;
218
+ }
219
+ if (!this.#options.enterExpands && (matchesKey(keyData, "enter") || keyData === "\r")) {
220
+ this.#followTailActive = false;
221
+ this.#fullscreen = true;
222
+ this.#scrollOffset = 0;
223
+ this.#requestRender();
224
+ return;
225
+ }
226
+ if (keyData === "y") {
227
+ this.#copy(false);
228
+ return;
229
+ }
230
+ if (keyData === "Y") {
231
+ this.#copy(true);
232
+ return;
233
+ }
234
+ if (keyData === "r") {
235
+ const entry = this.#entries[this.#selected];
236
+ if (entry?.rawViewable !== false) {
237
+ this.#raw.has(entry.id) ? this.#raw.delete(entry.id) : this.#raw.add(entry.id);
238
+ this.#requestRender();
239
+ }
240
+ }
241
+ }
242
+
243
+ #scroll(keyData: string): void {
244
+ this.#followTailActive = false;
245
+ this.#followTailPending = false;
246
+ if (keyData === "j" || matchesKey(keyData, "down") || matchesKey(keyData, "pageDown"))
247
+ this.#scrollOffset += PAGE_SIZE;
248
+ if (keyData === "k" || matchesKey(keyData, "up") || matchesKey(keyData, "pageUp"))
249
+ this.#scrollOffset -= PAGE_SIZE;
250
+ this.#requestRender();
251
+ }
252
+ #move(delta: number): void {
253
+ this.#selected = Math.max(0, Math.min(this.#selected + delta, this.#entries.length - 1));
254
+ if (delta !== 0) {
255
+ this.#followTailActive = false;
256
+ this.#followTailPending = false;
257
+ }
258
+ this.#rebuild();
259
+ const entry = this.#renderedEntries[this.#selected];
260
+ if (entry) {
261
+ const entryBottom = entry.lineStart + entry.lineCount;
262
+ if (entry.lineCount >= this.#viewportHeight) {
263
+ if (this.#scrollOffset + this.#viewportHeight <= entry.lineStart)
264
+ this.#scrollOffset = Math.max(0, entry.lineStart - 1);
265
+ else if (this.#scrollOffset >= entryBottom)
266
+ this.#scrollOffset = Math.max(0, entryBottom - this.#viewportHeight);
267
+ } else {
268
+ if (entry.lineStart < this.#scrollOffset) this.#scrollOffset = Math.max(0, entry.lineStart - 1);
269
+ if (entryBottom > this.#scrollOffset + this.#viewportHeight)
270
+ this.#scrollOffset = Math.max(0, entryBottom - this.#viewportHeight + 1);
271
+ }
272
+ }
273
+ this.#requestRender();
274
+ }
275
+ #page(direction: 1 | -1): void {
276
+ this.#followTailActive = false;
277
+ this.#followTailPending = false;
278
+ this.#selected = Math.max(0, Math.min(this.#selected + direction * 5, this.#entries.length - 1));
279
+ this.#scrollOffset += direction * PAGE_SIZE;
280
+ this.#requestRender();
281
+ }
282
+ #toggleExpand(): void {
283
+ this.#followTailActive = false;
284
+ const entry = this.#entries[this.#selected];
285
+ if (!entry || entry.foldable === false) return;
286
+ const expanding = !this.#expanded.has(entry.id);
287
+ if (expanding) this.#expanded.add(entry.id);
288
+ else this.#expanded.delete(entry.id);
289
+ this.#rebuild();
290
+ if (expanding) {
291
+ this.#scrollOffset = this.#renderedEntries[this.#selected]?.lineStart ?? 0;
292
+ this.#skipFollowTailOnce = true;
293
+ }
294
+ this.#requestRender();
295
+ }
296
+ resetSourceState(): void {
297
+ this.#expanded.clear();
298
+ this.#raw.clear();
299
+ this.#scrollOffset = 0;
300
+ this.#initialized = false;
301
+ this.#fullscreen = false;
302
+ this.#skipFollowTailOnce = false;
303
+ this.#followTailPending = false;
304
+ this.#followTailActive = this.#options.followTail === true;
305
+ }
306
+
307
+ #copy(metadata: boolean): void {
308
+ const entry = this.#entries[this.#selected];
309
+ if (!entry || entry.copyable === false) return;
310
+ try {
311
+ this.#options.copyToClipboard?.(
312
+ metadata ? JSON.stringify(entry.payload.metadata, null, 2) : entry.payload.text,
313
+ );
314
+ } catch {
315
+ this.#options.onError?.("Failed to copy transcript entry to clipboard.");
316
+ }
317
+ }
318
+ #requestRender(): void {
319
+ this.#options.requestRender?.();
320
+ }
321
+ #rebuild(): void {
322
+ const lines: string[] = [];
323
+ this.#renderedEntries = [];
324
+ const display = this.#fullscreen ? this.#entries.slice(this.#selected, this.#selected + 1) : this.#entries;
325
+ const contentWidth = Math.max(1, this.#width - INDENT.length - 1);
326
+ for (const entry of display) {
327
+ const start = lines.length;
328
+ const selected = entry.id === this.selectedEntryId;
329
+ const expanded = this.#fullscreen || this.#expanded.has(entry.id);
330
+ const raw = this.#raw.has(entry.id);
331
+ lines.push("");
332
+ lines.push(
333
+ `${selected ? theme.fg("accent", "▶") : " "} ${theme.fg("muted", `[${sanitizeText(entry.label ?? entry.kind)}]`)}`,
334
+ );
335
+ const text = sanitizeText(
336
+ (raw
337
+ ? entry.payload.text
338
+ : (this.#options.getEntryText?.(entry, expanded) ??
339
+ entry.getDisplayText?.(expanded) ??
340
+ entry.payload.text)
341
+ ).trim(),
342
+ );
343
+ if (raw)
344
+ for (const line of text.split("\n"))
345
+ for (const wrapped of wrapTextWithAnsi(line, contentWidth)) lines.push(`${INDENT}${wrapped}`);
346
+ else if (expanded) {
347
+ const rendered = this.#markdown(text, contentWidth);
348
+ const limit = this.#options.maxExpandedLines ?? Number.POSITIVE_INFINITY;
349
+ lines.push(...rendered.slice(0, limit));
350
+ if (rendered.length > limit)
351
+ lines.push(`${INDENT}${theme.fg("dim", `... ${rendered.length - limit} more lines`)}`);
352
+ } else {
353
+ const preview = text.split("\n").slice(0, PREVIEW_LINES);
354
+ for (const line of preview) lines.push(`${INDENT}${truncateToWidth(line, contentWidth)}`);
355
+ if (text.split("\n").length > PREVIEW_LINES) lines.push(`${INDENT}${theme.fg("dim", "... more (Space)")}`);
356
+ }
357
+ this.#renderedEntries.push({ lineStart: start, lineCount: lines.length - start });
358
+ }
359
+ this.#lines = lines.length ? lines : [theme.fg("dim", "No transcript entries yet.")];
360
+ }
361
+ #markdown(text: string, width: number): string[] {
362
+ return new Markdown(text, 0, 0, this.#mdTheme).render(width).map(line => `${INDENT}${line.trimEnd()}`);
363
+ }
364
+ }
365
+
366
+ /** Main-session adapter over the registry's canonical payload resolver. */
367
+ export function transcriptViewerEntries(registry: TranscriptItemRegistry): TranscriptViewerEntry[] {
368
+ return registry.items().flatMap(item => {
369
+ const payload = registry.resolveSourcePayload(item.id);
370
+ if (!payload) return [];
371
+ const capabilities = registry.capabilities(item.id);
372
+ const label =
373
+ item.kind === "assistant-text"
374
+ ? "Response"
375
+ : item.kind === "assistant-thinking"
376
+ ? "Thinking"
377
+ : item.kind === "tool"
378
+ ? "Tool"
379
+ : item.kind === "user"
380
+ ? "User"
381
+ : item.kind;
382
+ return [{ id: item.id, kind: item.kind, label, payload, ...capabilities }];
383
+ });
384
+ }
@@ -59,6 +59,7 @@ export class BtwController {
59
59
  try {
60
60
  const promptText = prompt.render(btwUserPrompt, { question: request.question });
61
61
  const { replyText } = await this.ctx.session.runEphemeralTurn({
62
+ purpose: "btw",
62
63
  promptText,
63
64
  onTextDelta: delta => {
64
65
  if (this.#isActiveRequest(request)) {
@@ -916,22 +916,15 @@ export class CommandController {
916
916
  }
917
917
  }
918
918
 
919
- async #runNewSessionFlow(options?: NewSessionOptions, label: string = "New session started"): Promise<void> {
919
+ async #runNewSessionFlow(options?: NewSessionOptions, label: string = "New session started"): Promise<boolean> {
920
+ if (!(await this.ctx.session.newSession(options))) return false;
921
+
920
922
  if (this.ctx.loadingAnimation) {
921
923
  this.ctx.loadingAnimation.stop();
922
924
  this.ctx.loadingAnimation = undefined;
923
925
  }
924
926
  this.ctx.statusContainer.clear();
925
-
926
- if (this.ctx.session.isCompacting) {
927
- this.ctx.session.abortCompaction();
928
- while (this.ctx.session.isCompacting) {
929
- await Bun.sleep(10);
930
- }
931
- }
932
- if (!(await this.ctx.session.newSession(options))) return;
933
927
  this.ctx.resetIrcSidebarSession();
934
-
935
928
  this.ctx.resetObserverRegistry();
936
929
  setSessionTerminalTitle(this.ctx.sessionManager.getSessionName(), this.ctx.sessionManager.getCwd());
937
930
 
@@ -952,10 +945,11 @@ export class CommandController {
952
945
  this.ctx.chatContainer.addChild(new Text(`${theme.fg("accent", `${theme.status.success} ${label}`)}`, 1, 0));
953
946
  await this.ctx.reloadTodos();
954
947
  this.ctx.ui.requestRender();
948
+ return true;
955
949
  }
956
950
 
957
- async handleClearCommand(): Promise<void> {
958
- await this.#runNewSessionFlow();
951
+ async handleClearCommand(): Promise<boolean> {
952
+ return this.#runNewSessionFlow();
959
953
  }
960
954
 
961
955
  async handleContextClearCommand(): Promise<void> {
@@ -995,12 +989,12 @@ export class CommandController {
995
989
  this.ctx.ui.requestRender();
996
990
  }
997
991
 
998
- async handleDropCommand(): Promise<void> {
992
+ async handleDropCommand(): Promise<boolean> {
999
993
  if (!this.ctx.sessionManager.getSessionFile()) {
1000
994
  this.ctx.showError("Nothing to drop (in-memory session)");
1001
- return;
995
+ return false;
1002
996
  }
1003
- await this.#runNewSessionFlow({ drop: true }, "Session dropped");
997
+ return this.#runNewSessionFlow({ drop: true }, "Session dropped");
1004
998
  }
1005
999
 
1006
1000
  async handleForkCommand(): Promise<void> {
@@ -238,6 +238,7 @@ export class EventController {
238
238
 
239
239
  const run = this.#handlers[event.type] as (e: AgentSessionEvent) => Promise<void>;
240
240
  await run(event);
241
+ if (this.ctx.isTranscriptViewerOpen?.()) this.ctx.refreshTranscriptViewer?.();
241
242
  }
242
243
 
243
244
  async #handleAgentStart(_event: Extract<AgentSessionEvent, { type: "agent_start" }>): Promise<void> {
@@ -802,7 +803,7 @@ export class EventController {
802
803
  if (details?.sourceToolName === "plan_approval" && details.action === "apply") {
803
804
  const planDetails = details.sourceResultDetails as PlanApprovalDetails | undefined;
804
805
  if (planDetails) {
805
- await this.ctx.handlePlanApproval(planDetails);
806
+ await this.ctx.planModeController.handleApproval(planDetails);
806
807
  }
807
808
  }
808
809
  }
@@ -819,7 +820,7 @@ export class EventController {
819
820
  this.ctx.streamingComponent = undefined;
820
821
  this.ctx.streamingMessage = undefined;
821
822
  }
822
- await this.ctx.flushPendingModelSwitch();
823
+ await this.ctx.planModeController.flushPendingModelSwitch();
823
824
  for (const toolCallId of Array.from(this.ctx.pendingTools.keys())) {
824
825
  if (!this.#backgroundToolCallIds.has(toolCallId)) {
825
826
  this.ctx.pendingTools.delete(toolCallId);
@@ -23,6 +23,7 @@ import { HookInputComponent } from "../../modes/components/hook-input";
23
23
  import { HookSelectorComponent } from "../../modes/components/hook-selector";
24
24
  import { getAvailableThemesWithPaths, getThemeByName, setTheme, type Theme, theme } from "../../modes/theme/theme";
25
25
  import type { InteractiveModeContext } from "../../modes/types";
26
+ import { createReadonlySessionManager } from "../../session/session-manager";
26
27
  import { parseThinkingLevel } from "../../thinking";
27
28
  import type { TodoPhase } from "../../tools/todo-write";
28
29
  import { setSessionTerminalTitle, setTerminalTitle } from "../../utils/title-generator";
@@ -53,6 +54,40 @@ export class ExtensionUiController {
53
54
  overlay?.hide();
54
55
  }
55
56
 
57
+ captureSessionUiCleanup(): () => void {
58
+ const terminalInputUnsubscribers = [...this.#extensionTerminalInputUnsubscribers];
59
+ const widgetsAbove = [...this.#hookWidgetsAbove.entries()];
60
+ const widgetsBelow = [...this.#hookWidgetsBelow.entries()];
61
+ const activeHookCustomComponent = this.#activeHookCustomComponent;
62
+ const activeHookCustomOverlay = this.#activeHookCustomOverlay;
63
+ return () => {
64
+ for (const unsubscribe of terminalInputUnsubscribers) {
65
+ unsubscribe();
66
+ this.#extensionTerminalInputUnsubscribers.delete(unsubscribe);
67
+ }
68
+ let widgetsChanged = false;
69
+ for (const [key, widget] of widgetsAbove) {
70
+ if (this.#hookWidgetsAbove.get(key) !== widget) continue;
71
+ this.#hookWidgetsAbove.delete(key);
72
+ widget.dispose?.();
73
+ widgetsChanged = true;
74
+ }
75
+ for (const [key, widget] of widgetsBelow) {
76
+ if (this.#hookWidgetsBelow.get(key) !== widget) continue;
77
+ this.#hookWidgetsBelow.delete(key);
78
+ widget.dispose?.();
79
+ widgetsChanged = true;
80
+ }
81
+ if (
82
+ this.#activeHookCustomComponent === activeHookCustomComponent &&
83
+ this.#activeHookCustomOverlay === activeHookCustomOverlay
84
+ ) {
85
+ this.#clearActiveHookCustom();
86
+ }
87
+ if (widgetsChanged) this.#rebuildHookWidgets();
88
+ };
89
+ }
90
+
56
91
  #sdkControl = async (operation: string, input: Record<string, unknown>): Promise<unknown> => {
57
92
  const session = this.ctx.session;
58
93
  switch (operation) {
@@ -322,6 +357,10 @@ export class ExtensionUiController {
322
357
  },
323
358
  getActiveTools: () => this.ctx.session.getActiveToolNames(),
324
359
  getAllTools: () => this.ctx.session.getAllToolNames(),
360
+ resolveTool: name => {
361
+ const tool = this.ctx.session.getToolByName(name);
362
+ return tool ? { safeSummary: tool.safeSummary, safeSummaryFields: tool.safeSummaryFields } : undefined;
363
+ },
325
364
  setActiveTools: toolNames => this.ctx.session.setActiveToolsByName(toolNames),
326
365
  setModel: async model => {
327
366
  const key = await this.ctx.session.modelRegistry.getApiKey(model);
@@ -358,6 +397,10 @@ export class ExtensionUiController {
358
397
  getQueuedMessages: () => this.ctx.session.getQueuedMessageEntries(),
359
398
  getActiveTools: () => this.ctx.session.getActiveToolNames(),
360
399
  getAllTools: () => this.ctx.session.getAllToolNames(),
400
+ resolveTool: name => {
401
+ const tool = this.ctx.session.getToolByName(name);
402
+ return tool ? { safeSummary: tool.safeSummary, safeSummaryFields: tool.safeSummaryFields } : undefined;
403
+ },
361
404
 
362
405
  shutdown: () => {
363
406
  // Defer the actual teardown to the main loop, which calls
@@ -414,36 +457,30 @@ export class ExtensionUiController {
414
457
  this.ctx.showStatus("Reloaded session");
415
458
  },
416
459
  newSession: async options => {
417
- // Stop any loading animation
460
+ const cleanupPreviousSessionUi = this.captureSessionUiCleanup();
461
+ const success = await this.ctx.session.newSession({ parentSession: options?.parentSession });
462
+ if (!success) {
463
+ return { cancelled: true };
464
+ }
465
+ cleanupPreviousSessionUi();
466
+
418
467
  if (this.ctx.loadingAnimation) {
419
468
  this.ctx.loadingAnimation.stop();
420
469
  this.ctx.loadingAnimation = undefined;
421
470
  }
422
471
  this.ctx.statusContainer.clear();
423
-
424
- // Create new session
425
- this.clearExtensionTerminalInputListeners();
426
- this.clearHookWidgets();
427
- const success = await this.ctx.session.newSession({ parentSession: options?.parentSession });
428
- if (!success) {
429
- return { cancelled: true };
430
- }
431
472
  this.ctx.resetIrcSidebarSession();
432
-
433
473
  setSessionTerminalTitle(this.ctx.sessionManager.getSessionName(), this.ctx.sessionManager.getCwd());
434
474
 
435
- // Call setup callback if provided
436
475
  if (options?.setup) {
437
476
  await options.setup(this.ctx.sessionManager);
438
477
  }
439
478
 
440
- // Reset and update status line
441
479
  this.ctx.statusLine.invalidate();
442
480
  this.ctx.statusLine.setSessionStartTime(Date.now());
443
481
  this.ctx.updateEditorTopBorder();
444
482
  this.ctx.ui.requestRender();
445
483
 
446
- // Clear UI state
447
484
  prepareTranscriptRebuild(this.ctx.ui, "replace-identity");
448
485
  this.ctx.chatContainer.clear();
449
486
  this.ctx.pendingMessagesContainer.clear();
@@ -622,6 +659,10 @@ export class ExtensionUiController {
622
659
  },
623
660
  getActiveTools: () => this.ctx.session.getActiveToolNames(),
624
661
  getAllTools: () => this.ctx.session.getAllToolNames(),
662
+ resolveTool: name => {
663
+ const tool = this.ctx.session.getToolByName(name);
664
+ return tool ? { safeSummary: tool.safeSummary, safeSummaryFields: tool.safeSummaryFields } : undefined;
665
+ },
625
666
  setActiveTools: toolNames => this.ctx.session.setActiveToolsByName(toolNames),
626
667
  setModel: async model => {
627
668
  const key = await this.ctx.session.modelRegistry.getApiKey(model);
@@ -658,6 +699,10 @@ export class ExtensionUiController {
658
699
  getQueuedMessages: () => this.ctx.session.getQueuedMessageEntries(),
659
700
  getActiveTools: () => this.ctx.session.getActiveToolNames(),
660
701
  getAllTools: () => this.ctx.session.getAllToolNames(),
702
+ resolveTool: name => {
703
+ const tool = this.ctx.session.getToolByName(name);
704
+ return tool ? { safeSummary: tool.safeSummary, safeSummaryFields: tool.safeSummaryFields } : undefined;
705
+ },
661
706
 
662
707
  shutdown: () => {
663
708
  // Defer the actual teardown to the main loop, which calls
@@ -717,28 +762,24 @@ export class ExtensionUiController {
717
762
  if (this.ctx.isBackgrounded) {
718
763
  return { cancelled: true };
719
764
  }
720
- // Stop any loading animation
765
+ const cleanupPreviousSessionUi = this.captureSessionUiCleanup();
766
+ const success = await this.ctx.session.newSession({ parentSession: options?.parentSession });
767
+ if (!success) {
768
+ return { cancelled: true };
769
+ }
770
+ cleanupPreviousSessionUi();
771
+
721
772
  if (this.ctx.loadingAnimation) {
722
773
  this.ctx.loadingAnimation.stop();
723
774
  this.ctx.loadingAnimation = undefined;
724
775
  }
725
776
  this.ctx.statusContainer.clear();
726
-
727
- // Create new session
728
- this.clearExtensionTerminalInputListeners();
729
- this.clearHookWidgets();
730
- const success = await this.ctx.session.newSession({ parentSession: options?.parentSession });
731
- if (!success) {
732
- return { cancelled: true };
733
- }
734
777
  this.ctx.resetIrcSidebarSession();
735
778
 
736
- // Call setup callback if provided
737
779
  if (options?.setup) {
738
780
  await options.setup(this.ctx.sessionManager);
739
781
  }
740
782
 
741
- // Clear UI state
742
783
  prepareTranscriptRebuild(this.ctx.ui, "replace-identity");
743
784
  this.ctx.chatContainer.clear();
744
785
  this.ctx.pendingMessagesContainer.clear();
@@ -868,7 +909,7 @@ export class ExtensionUiController {
868
909
  compact: instructionsOrOptions => this.#compactSession(instructionsOrOptions),
869
910
  hasUI: !this.ctx.isBackgrounded,
870
911
  cwd: this.ctx.sessionManager.getCwd(),
871
- sessionManager: this.ctx.session.sessionManager,
912
+ sessionManager: createReadonlySessionManager(this.ctx.session.sessionManager),
872
913
  modelRegistry: this.ctx.session.modelRegistry,
873
914
  model: this.ctx.session.model,
874
915
  isIdle: () => !this.ctx.session.isStreaming,
@@ -881,6 +922,12 @@ export class ExtensionUiController {
881
922
  getQueuedMessages: () => this.ctx.session.getQueuedMessageEntries(),
882
923
  getActiveTools: () => this.ctx.session.getActiveToolNames(),
883
924
  getAllTools: () => this.ctx.session.getAllToolNames(),
925
+ resolveTool: name => {
926
+ const tool = this.ctx.session.getToolByName(name);
927
+ return tool
928
+ ? { safeSummary: tool.safeSummary, safeSummaryFields: tool.safeSummaryFields }
929
+ : undefined;
930
+ },
884
931
  hasQueuedMessages: () => this.ctx.session.queuedMessageCount > 0,
885
932
  abort: () => {
886
933
  this.ctx.session.abort();