@gajae-code/coding-agent 0.11.1 → 0.11.3

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 (413) hide show
  1. package/CHANGELOG.md +64 -0
  2. package/dist/types/cli/config-cli.d.ts +11 -1
  3. package/dist/types/cli/launch-disposition.d.ts +23 -0
  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 +141 -11
  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-runtime.d.ts +55 -33
  54. package/dist/types/gjc-runtime/workflow-cli-common.d.ts +8 -0
  55. package/dist/types/gjc-runtime/workflow-manifest.d.ts +3 -0
  56. package/dist/types/goals/continuation-timeout-guard.d.ts +22 -0
  57. package/dist/types/goals/runtime.d.ts +1 -0
  58. package/dist/types/goals/state.d.ts +8 -0
  59. package/dist/types/harness-control-plane/lifecycle-reconciliation.d.ts +17 -0
  60. package/dist/types/harness-control-plane/phase-rollup.d.ts +4 -7
  61. package/dist/types/harness-control-plane/receipt-ingest.d.ts +5 -0
  62. package/dist/types/harness-control-plane/receipt-spool.d.ts +4 -0
  63. package/dist/types/hindsight/state.d.ts +11 -4
  64. package/dist/types/lsp/lspmux.d.ts +5 -3
  65. package/dist/types/lsp/path-trust.d.ts +1 -0
  66. package/dist/types/main.d.ts +4 -2
  67. package/dist/types/memories/storage.d.ts +1 -0
  68. package/dist/types/modes/action-registry.d.ts +29 -0
  69. package/dist/types/modes/components/chrome.d.ts +14 -0
  70. package/dist/types/modes/components/command-palette.d.ts +27 -0
  71. package/dist/types/modes/components/custom-provider-wizard.d.ts +1 -0
  72. package/dist/types/modes/components/index.d.ts +2 -1
  73. package/dist/types/modes/components/pet-selector.d.ts +1 -1
  74. package/dist/types/modes/components/plan-preview-overlay.d.ts +43 -0
  75. package/dist/types/modes/components/queue-mode-selector.d.ts +1 -1
  76. package/dist/types/modes/components/queue-pane.d.ts +12 -0
  77. package/dist/types/modes/components/read-tool-group.d.ts +6 -0
  78. package/dist/types/modes/components/session-observer-overlay.d.ts +3 -4
  79. package/dist/types/modes/components/sessions-dashboard.d.ts +32 -0
  80. package/dist/types/modes/components/settings-selector.d.ts +1 -1
  81. package/dist/types/modes/components/show-images-selector.d.ts +1 -1
  82. package/dist/types/modes/components/status-line/types.d.ts +1 -1
  83. package/dist/types/modes/components/status-line.d.ts +1 -94
  84. package/dist/types/modes/components/tasks-pane.d.ts +15 -0
  85. package/dist/types/modes/components/theme-selector.d.ts +1 -1
  86. package/dist/types/modes/components/tool-execution.d.ts +15 -0
  87. package/dist/types/modes/components/tool-status-header.d.ts +111 -0
  88. package/dist/types/modes/components/transcript-viewer-overlay.d.ts +42 -0
  89. package/dist/types/modes/controllers/goal-mode-controller.d.ts +62 -0
  90. package/dist/types/modes/controllers/input-controller.d.ts +8 -1
  91. package/dist/types/modes/controllers/mode-gate.d.ts +8 -0
  92. package/dist/types/modes/controllers/plan-mode-controller.d.ts +65 -0
  93. package/dist/types/modes/controllers/selector-controller.d.ts +15 -3
  94. package/dist/types/modes/controllers/stt-controller.d.ts +26 -0
  95. package/dist/types/modes/interactive-mode.d.ts +19 -17
  96. package/dist/types/modes/jobs-observer.d.ts +2 -0
  97. package/dist/types/modes/prompt-action-autocomplete.d.ts +4 -0
  98. package/dist/types/modes/shared/agent-wire/deep-interview-gate.d.ts +4 -11
  99. package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +20 -2
  100. package/dist/types/modes/shared/agent-wire/workflow-gate-types.d.ts +31 -0
  101. package/dist/types/modes/tasks-aggregator.d.ts +39 -0
  102. package/dist/types/modes/transcript-item-registry.d.ts +60 -0
  103. package/dist/types/modes/types.d.ts +20 -13
  104. package/dist/types/modes/utils/ui-helpers.d.ts +2 -2
  105. package/dist/types/runtime-mcp/manager.d.ts +1 -1
  106. package/dist/types/sdk/broker/broker.d.ts +78 -12
  107. package/dist/types/sdk/broker/lifecycle-codec.d.ts +4 -0
  108. package/dist/types/sdk/broker/lifecycle-ledger.d.ts +15 -1
  109. package/dist/types/sdk/broker/lifecycle.d.ts +6 -2
  110. package/dist/types/sdk/broker/process-incarnation.d.ts +2 -0
  111. package/dist/types/sdk/broker/session-index.d.ts +0 -1
  112. package/dist/types/sdk/broker/state-version.d.ts +2 -0
  113. package/dist/types/sdk/bus/chat-effect-journal.d.ts +1 -0
  114. package/dist/types/sdk/bus/config-commands.d.ts +1 -1
  115. package/dist/types/sdk/bus/config.d.ts +11 -0
  116. package/dist/types/sdk/bus/daemon-paths.d.ts +1 -0
  117. package/dist/types/sdk/bus/html-format.d.ts +3 -2
  118. package/dist/types/sdk/bus/index.d.ts +29 -0
  119. package/dist/types/sdk/bus/lifecycle-control-runtime.d.ts +4 -0
  120. package/dist/types/sdk/bus/notification-service.d.ts +20 -0
  121. package/dist/types/sdk/bus/rate-limit-pool.d.ts +13 -2
  122. package/dist/types/sdk/bus/recent-activity.d.ts +21 -12
  123. package/dist/types/sdk/bus/rich-document.d.ts +7 -0
  124. package/dist/types/sdk/bus/rich-render.d.ts +18 -41
  125. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +9 -4
  126. package/dist/types/sdk/bus/telegram-daemon.d.ts +46 -3
  127. package/dist/types/sdk/bus/threaded-inbound.d.ts +1 -1
  128. package/dist/types/sdk/bus/threaded-render.d.ts +13 -0
  129. package/dist/types/sdk/bus/topic-registry.d.ts +24 -2
  130. package/dist/types/sdk/host/control/operations.d.ts +1 -1
  131. package/dist/types/sdk/host/host.d.ts +2 -0
  132. package/dist/types/sdk/index.d.ts +1 -0
  133. package/dist/types/sdk/session-directory.d.ts +56 -0
  134. package/dist/types/sdk/session.d.ts +2 -0
  135. package/dist/types/session/agent-session.d.ts +82 -47
  136. package/dist/types/session/agent-storage.d.ts +7 -0
  137. package/dist/types/session/fallback-chain-controller.d.ts +10 -2
  138. package/dist/types/session/internal/managed-session-scope.d.ts +102 -0
  139. package/dist/types/session/internal/managed-session-storage.d.ts +38 -0
  140. package/dist/types/session/messages.d.ts +16 -0
  141. package/dist/types/session/session-manager-internal.d.ts +14 -0
  142. package/dist/types/session/session-manager.d.ts +82 -12
  143. package/dist/types/session/session-storage.d.ts +43 -3
  144. package/dist/types/session/volatile-context-pruning.d.ts +15 -0
  145. package/dist/types/skill-state/{deep-interview-mutation-guard.d.ts → workflow-mutation-guard.d.ts} +24 -8
  146. package/dist/types/skill-state/workflow-state-contract.d.ts +1 -3
  147. package/dist/types/slash-commands/builtin-registry.d.ts +4 -1
  148. package/dist/types/system-prompt.d.ts +2 -11
  149. package/dist/types/task/executor.d.ts +3 -1
  150. package/dist/types/thinking.d.ts +7 -0
  151. package/dist/types/tool-discovery/tool-index.d.ts +1 -50
  152. package/dist/types/tools/ask.d.ts +64 -2
  153. package/dist/types/tools/bash.d.ts +3 -0
  154. package/dist/types/tools/browser/dead-tab-recovery.d.ts +24 -0
  155. package/dist/types/tools/browser/tab-supervisor.d.ts +21 -0
  156. package/dist/types/tools/cron.d.ts +2 -0
  157. package/dist/types/tools/fetch.d.ts +1 -0
  158. package/dist/types/tools/index.d.ts +30 -15
  159. package/dist/types/tools/output-meta.d.ts +1 -0
  160. package/dist/types/tools/read.d.ts +5 -0
  161. package/dist/types/tools/search-tool-bm25.d.ts +3 -4
  162. package/dist/types/tools/subagent.d.ts +1 -0
  163. package/dist/types/utils/event-bus.d.ts +1 -1
  164. package/dist/types/utils/file-mentions.d.ts +3 -3
  165. package/dist/types/web/search/index.d.ts +3 -1
  166. package/package.json +21 -9
  167. package/scripts/build-sdk-package-smoke.ts +34 -1
  168. package/scripts/generate-hotkeys-docs.ts +74 -0
  169. package/scripts/generate-sdk-operation-inventory.ts +64 -20
  170. package/scripts/verify-gjc-sdk-canonicalization.ts +182 -2
  171. package/src/async/job-manager.ts +49 -6
  172. package/src/cli/config-cli.ts +75 -2
  173. package/src/cli/launch-disposition.ts +42 -0
  174. package/src/cli/notify-cli.ts +92 -26
  175. package/src/cli/session-picker.ts +20 -3
  176. package/src/cli/setup-cli.ts +1 -1
  177. package/src/cli.ts +1 -1
  178. package/src/commands/config.ts +1 -1
  179. package/src/commands/harness.ts +34 -106
  180. package/src/commands/launch.ts +6 -4
  181. package/src/commands/mcp-serve.ts +85 -8
  182. package/src/commands/mcp.ts +2 -2
  183. package/src/commands/notify.ts +8 -4
  184. package/src/commands/sdk.ts +61 -40
  185. package/src/commands/ultragoal.ts +4 -1
  186. package/src/config/atomic-yaml-patch.ts +80 -13
  187. package/src/config/file-lock.ts +105 -43
  188. package/src/config/keybindings.ts +64 -20
  189. package/src/config/model-auth.ts +9 -0
  190. package/src/config/model-bindings-applier.ts +139 -0
  191. package/src/config/model-discovery-manager.ts +224 -0
  192. package/src/config/model-equivalence.ts +66 -0
  193. package/src/config/model-registry.ts +169 -302
  194. package/src/config/model-resolver.ts +361 -369
  195. package/src/config/models-config-schema.ts +5 -4
  196. package/src/config/settings-schema.ts +214 -12
  197. package/src/config/settings.ts +458 -482
  198. package/src/coordinator-mcp/policy.ts +76 -11
  199. package/src/coordinator-mcp/question-gate-codec.ts +274 -0
  200. package/src/coordinator-mcp/question-state.ts +778 -0
  201. package/src/coordinator-mcp/server.ts +1702 -238
  202. package/src/debug/index.ts +1 -1
  203. package/src/defaults/gjc/skills/deep-interview/SKILL.md +13 -1
  204. package/src/defaults/gjc/skills/team/SKILL.md +16 -0
  205. package/src/discovery/helpers.ts +1 -50
  206. package/src/edit/index.ts +64 -0
  207. package/src/eval/py/env.ts +26 -0
  208. package/src/eval/py/kernel.ts +5 -3
  209. package/src/extensibility/extensions/loader.ts +9 -1
  210. package/src/extensibility/extensions/runner.ts +12 -3
  211. package/src/extensibility/extensions/types.ts +39 -1
  212. package/src/extensibility/gjc-plugins/installer.ts +16 -16
  213. package/src/extensibility/hooks/runner.ts +2 -2
  214. package/src/extensibility/runtime-skill-discovery.ts +33 -13
  215. package/src/extensibility/shared-events.ts +2 -0
  216. package/src/gjc-runtime/deep-interview-recorder.ts +63 -3
  217. package/src/gjc-runtime/deep-interview-runtime.ts +76 -27
  218. package/src/gjc-runtime/deep-interview-state.ts +317 -5
  219. package/src/gjc-runtime/goal-mode-request.ts +55 -8
  220. package/src/gjc-runtime/launch-tmux.ts +2 -5
  221. package/src/gjc-runtime/launch-worktree.ts +5 -5
  222. package/src/gjc-runtime/linux-proc.ts +71 -0
  223. package/src/gjc-runtime/ralplan-runtime.ts +74 -90
  224. package/src/gjc-runtime/session-resolution.ts +18 -3
  225. package/src/gjc-runtime/session-state-sidecar.ts +6 -107
  226. package/src/gjc-runtime/state-migrations.ts +1 -1
  227. package/src/gjc-runtime/state-runtime.ts +327 -249
  228. package/src/gjc-runtime/state-writer.ts +36 -40
  229. package/src/gjc-runtime/team-launch.ts +319 -0
  230. package/src/gjc-runtime/team-notify.ts +326 -0
  231. package/src/gjc-runtime/team-runtime.ts +1099 -1482
  232. package/src/gjc-runtime/team-store.ts +828 -0
  233. package/src/gjc-runtime/team-workers.ts +962 -0
  234. package/src/gjc-runtime/tmux-owner-isolation-cli.ts +2 -11
  235. package/src/gjc-runtime/tmux-owner-isolation.ts +14 -9
  236. package/src/gjc-runtime/tmux-sessions.ts +4 -21
  237. package/src/gjc-runtime/ultragoal-change-set.ts +172 -0
  238. package/src/gjc-runtime/ultragoal-evidence.ts +874 -0
  239. package/src/gjc-runtime/ultragoal-guard.ts +29 -5
  240. package/src/gjc-runtime/ultragoal-pipeline.ts +306 -0
  241. package/src/gjc-runtime/ultragoal-runtime.ts +84 -1311
  242. package/src/gjc-runtime/workflow-cli-common.ts +30 -0
  243. package/src/gjc-runtime/workflow-manifest.generated.json +58 -0
  244. package/src/gjc-runtime/workflow-manifest.ts +18 -0
  245. package/src/goals/continuation-timeout-guard.ts +79 -0
  246. package/src/goals/runtime.ts +7 -5
  247. package/src/goals/state.ts +18 -0
  248. package/src/harness-control-plane/lifecycle-reconciliation.ts +64 -0
  249. package/src/harness-control-plane/phase-rollup.ts +4 -7
  250. package/src/harness-control-plane/receipt-ingest.ts +5 -0
  251. package/src/harness-control-plane/receipt-spool.ts +51 -7
  252. package/src/harness-control-plane/session-lease.ts +12 -130
  253. package/src/hindsight/backend.ts +26 -27
  254. package/src/hindsight/state.ts +101 -32
  255. package/src/hooks/skill-state.ts +17 -11
  256. package/src/internal-urls/docs-index.generated.ts +21 -18
  257. package/src/lsp/client.ts +1 -1
  258. package/src/lsp/config.ts +145 -26
  259. package/src/lsp/index.ts +1 -1
  260. package/src/lsp/lspmux.ts +39 -10
  261. package/src/lsp/path-trust.ts +81 -0
  262. package/src/main.ts +127 -30
  263. package/src/memories/index.ts +1 -0
  264. package/src/memories/storage.ts +6 -2
  265. package/src/modes/action-registry.ts +165 -0
  266. package/src/modes/components/chrome.ts +40 -0
  267. package/src/modes/components/command-palette.ts +168 -0
  268. package/src/modes/components/custom-editor.ts +1 -1
  269. package/src/modes/components/custom-provider-wizard.ts +51 -14
  270. package/src/modes/components/index.ts +2 -1
  271. package/src/modes/components/pet-selector.ts +11 -17
  272. package/src/modes/components/plan-preview-overlay.ts +295 -0
  273. package/src/modes/components/queue-mode-selector.ts +10 -27
  274. package/src/modes/components/queue-pane.ts +79 -0
  275. package/src/modes/components/read-tool-group.ts +13 -0
  276. package/src/modes/components/session-observer-overlay.ts +207 -772
  277. package/src/modes/components/sessions-dashboard.ts +181 -0
  278. package/src/modes/components/settings-selector.ts +1 -1
  279. package/src/modes/components/show-images-selector.ts +10 -24
  280. package/src/modes/components/status-line/types.ts +1 -1
  281. package/src/modes/components/status-line.ts +1 -969
  282. package/src/modes/components/tasks-pane.ts +73 -0
  283. package/src/modes/components/theme-selector.ts +11 -31
  284. package/src/modes/components/tool-execution.ts +22 -0
  285. package/src/modes/components/tool-status-header.ts +1056 -0
  286. package/src/modes/components/transcript-viewer-overlay.ts +384 -0
  287. package/src/modes/controllers/btw-controller.ts +1 -0
  288. package/src/modes/controllers/event-controller.ts +3 -2
  289. package/src/modes/controllers/extension-ui-controller.ts +24 -1
  290. package/src/modes/controllers/goal-mode-controller.ts +474 -0
  291. package/src/modes/controllers/input-controller.ts +764 -229
  292. package/src/modes/controllers/mode-gate.ts +24 -0
  293. package/src/modes/controllers/plan-mode-controller.ts +517 -0
  294. package/src/modes/controllers/selector-controller.ts +225 -10
  295. package/src/modes/controllers/stt-controller.ts +125 -0
  296. package/src/modes/interactive-mode.ts +289 -1150
  297. package/src/modes/jobs-observer.ts +3 -0
  298. package/src/modes/prompt-action-autocomplete.ts +7 -0
  299. package/src/modes/runtime-init.ts +8 -0
  300. package/src/modes/shared/agent-wire/deep-interview-gate.ts +31 -106
  301. package/src/modes/shared/agent-wire/workflow-gate-broker.ts +71 -16
  302. package/src/modes/shared/agent-wire/workflow-gate-types.ts +177 -0
  303. package/src/modes/tasks-aggregator.ts +199 -0
  304. package/src/modes/transcript-item-registry.ts +229 -0
  305. package/src/modes/types.ts +45 -18
  306. package/src/modes/utils/ui-helpers.ts +17 -9
  307. package/src/prompts/agent-fragments/ralplan-persistence.md +6 -0
  308. package/src/prompts/agent-fragments/restricted-bash.md +1 -0
  309. package/src/prompts/agents/architect.md +2 -8
  310. package/src/prompts/agents/critic.md +2 -8
  311. package/src/prompts/agents/executor.md +3 -0
  312. package/src/prompts/agents/planner.md +4 -12
  313. package/src/prompts/system/custom-system-prompt.md +1 -23
  314. package/src/prompts/system/plan-mode-approved.md +8 -0
  315. package/src/prompts/system/system-prompt.md +17 -96
  316. package/src/prompts/tools/bash.md +4 -4
  317. package/src/prompts/tools/browser.md +15 -52
  318. package/src/prompts/tools/find.md +2 -2
  319. package/src/prompts/tools/hashline.md +13 -55
  320. package/src/prompts/tools/read.md +3 -6
  321. package/src/prompts/tools/search-tool-bm25.md +0 -2
  322. package/src/prompts/tools/search.md +2 -4
  323. package/src/prompts/tools/skill-discovery.md +1 -2
  324. package/src/prompts/tools/task.md +1 -1
  325. package/src/runtime-mcp/manager.ts +1 -1
  326. package/src/sdk/broker/broker.ts +206 -78
  327. package/src/sdk/broker/discovery.ts +1 -1
  328. package/src/sdk/broker/ensure.ts +3 -1
  329. package/src/sdk/broker/lifecycle-codec.ts +11 -0
  330. package/src/sdk/broker/lifecycle-ledger.ts +371 -74
  331. package/src/sdk/broker/lifecycle.ts +1399 -142
  332. package/src/sdk/broker/process-incarnation.ts +5 -13
  333. package/src/sdk/broker/session-index.ts +145 -56
  334. package/src/sdk/broker/state-version.ts +21 -0
  335. package/src/sdk/broker/transport.ts +1 -1
  336. package/src/sdk/bus/chat-daemon-cli.ts +2 -0
  337. package/src/sdk/bus/chat-daemon-control.ts +8 -28
  338. package/src/sdk/bus/chat-effect-journal.ts +10 -2
  339. package/src/sdk/bus/config-commands.ts +5 -4
  340. package/src/sdk/bus/config.ts +98 -0
  341. package/src/sdk/bus/conversation-store.ts +5 -29
  342. package/src/sdk/bus/daemon-paths.ts +2 -0
  343. package/src/sdk/bus/html-format.ts +20 -3
  344. package/src/sdk/bus/index.ts +782 -94
  345. package/src/sdk/bus/lifecycle-control-runtime.ts +91 -33
  346. package/src/sdk/bus/natives-augment.d.ts +26 -1
  347. package/src/sdk/bus/notification-service.ts +168 -20
  348. package/src/sdk/bus/rate-limit-pool.ts +58 -14
  349. package/src/sdk/bus/recent-activity.ts +188 -74
  350. package/src/sdk/bus/rich-document.ts +258 -0
  351. package/src/sdk/bus/rich-render.ts +62 -70
  352. package/src/sdk/bus/telegram-daemon-cli.ts +15 -59
  353. package/src/sdk/bus/telegram-daemon-contract.ts +9 -4
  354. package/src/sdk/bus/telegram-daemon.ts +1389 -237
  355. package/src/sdk/bus/threaded-inbound.ts +8 -3
  356. package/src/sdk/bus/threaded-render.ts +57 -1
  357. package/src/sdk/bus/topic-registry.ts +120 -13
  358. package/src/sdk/host/control/dispatch.ts +14 -2
  359. package/src/sdk/host/control/operations.ts +6 -1
  360. package/src/sdk/host/host.ts +44 -2
  361. package/src/sdk/host/query/handlers.ts +20 -22
  362. package/src/sdk/host/query/revision-store.ts +80 -57
  363. package/src/sdk/index.ts +1 -0
  364. package/src/sdk/protocol/operation-inventory.generated.json +943 -1907
  365. package/src/sdk/session-directory.ts +141 -0
  366. package/src/sdk/session.ts +462 -480
  367. package/src/session/agent-session.ts +1700 -583
  368. package/src/session/agent-storage.ts +18 -0
  369. package/src/session/cache-economics.ts +25 -8
  370. package/src/session/fallback-chain-controller.ts +53 -3
  371. package/src/session/internal/managed-session-scope.ts +2226 -0
  372. package/src/session/internal/managed-session-storage.ts +453 -0
  373. package/src/session/messages.ts +91 -2
  374. package/src/session/session-manager-internal.ts +33 -0
  375. package/src/session/session-manager.ts +1112 -333
  376. package/src/session/session-storage.ts +444 -39
  377. package/src/session/volatile-context-pruning.ts +78 -0
  378. package/src/setup/credential-auto-import.ts +2 -1
  379. package/src/setup/hermes/templates/operator-instructions.v1.md +1 -1
  380. package/src/setup/provider-onboarding.ts +36 -61
  381. package/src/setup/provider-presets.json +5 -0
  382. package/src/skill-state/active-state.ts +3 -13
  383. package/src/skill-state/{deep-interview-mutation-guard.ts → workflow-mutation-guard.ts} +47 -29
  384. package/src/skill-state/workflow-state-contract.ts +6 -41
  385. package/src/slash-commands/builtin-registry.ts +46 -11
  386. package/src/system-prompt.ts +37 -44
  387. package/src/task/agents.ts +19 -1
  388. package/src/task/executor.ts +90 -6
  389. package/src/task/index.ts +36 -21
  390. package/src/thinking.ts +21 -6
  391. package/src/tool-discovery/tool-index.ts +2 -134
  392. package/src/tools/ask.ts +157 -21
  393. package/src/tools/ast-edit.ts +1 -1
  394. package/src/tools/bash.ts +38 -0
  395. package/src/tools/browser/dead-tab-recovery.ts +78 -0
  396. package/src/tools/browser/tab-supervisor.ts +282 -36
  397. package/src/tools/computer.ts +10 -3
  398. package/src/tools/cron.ts +30 -16
  399. package/src/tools/eval.ts +1 -2
  400. package/src/tools/fetch.ts +11 -4
  401. package/src/tools/index.ts +76 -24
  402. package/src/tools/output-meta.ts +19 -7
  403. package/src/tools/read.ts +219 -64
  404. package/src/tools/resource-gc.ts +2 -2
  405. package/src/tools/search-tool-bm25.ts +11 -74
  406. package/src/tools/subagent.ts +43 -2
  407. package/src/utils/event-bus.ts +9 -4
  408. package/src/utils/file-mentions.ts +4 -4
  409. package/src/web/search/index.ts +3 -3
  410. package/dist/types/discovery/plugin-dir-roots.d.ts +0 -15
  411. package/dist/types/runtime-mcp/discoverable-tool-metadata.d.ts +0 -7
  412. package/src/discovery/plugin-dir-roots.ts +0 -28
  413. package/src/runtime-mcp/discoverable-tool-metadata.ts +0 -25
@@ -45,6 +45,12 @@ export interface RateLimitItem<T = unknown> {
45
45
  payload: T;
46
46
  }
47
47
 
48
+ export type RateLimitDisposition = "queued" | "sending" | "accepted" | "rejected" | "ambiguous" | "removed" | "expired";
49
+ export interface RateLimitHandle {
50
+ itemId: string;
51
+ settled: Promise<Exclude<RateLimitDisposition, "queued" | "sending">>;
52
+ }
53
+
48
54
  /** Options for {@link RateLimitPool}. */
49
55
  export interface RateLimitPoolOptions {
50
56
  /** Burst capacity (max tokens). Default 20 (Telegram per-chat burst). */
@@ -87,6 +93,11 @@ export class RateLimitPool<T = unknown> {
87
93
 
88
94
  private tokens: number;
89
95
  private lastRefill: number;
96
+ private readonly settlements = new Map<
97
+ string,
98
+ PromiseWithResolvers<Exclude<RateLimitDisposition, "queued" | "sending">>
99
+ >();
100
+
90
101
  private seqCounter = 0;
91
102
 
92
103
  constructor(options: RateLimitPoolOptions = {}) {
@@ -116,22 +127,45 @@ export class RateLimitPool<T = unknown> {
116
127
  * item in the same `(sessionId, lane)` replace its payload (latest wins)
117
128
  * while preserving FIFO position; identified items are always appended.
118
129
  */
119
- submit(item: RateLimitItem<T>): void {
120
- const queue = this.lanes.get(item.lane);
121
- if (!queue) throw new Error(`unknown rate-limit lane: ${item.lane}`);
122
- if (item.itemId === undefined && item.coalesceKey !== undefined) {
130
+ submit(item: RateLimitItem<T>): RateLimitHandle {
131
+ const itemId = item.itemId ?? `rate-limit:${this.seqCounter++}`;
132
+ const identified = item.itemId === undefined ? { ...item, itemId } : item;
133
+ const queue = this.lanes.get(identified.lane);
134
+ if (!queue) throw new Error(`unknown rate-limit lane: ${identified.lane}`);
135
+ if (item.itemId === undefined && identified.coalesceKey !== undefined) {
123
136
  const existing = queue.find(
124
- q =>
125
- q.item.itemId === undefined &&
126
- q.item.sessionId === item.sessionId &&
127
- q.item.coalesceKey === item.coalesceKey,
137
+ q => q.item.sessionId === identified.sessionId && q.item.coalesceKey === identified.coalesceKey,
128
138
  );
129
139
  if (existing) {
130
- existing.item = item;
131
- return;
140
+ this.settle(existing.item.itemId!, "removed");
141
+ existing.item = identified;
142
+ return this.handle(itemId);
132
143
  }
133
144
  }
134
- queue.push({ item, seq: this.seqCounter++ });
145
+ queue.push({ item: identified, seq: item.itemId === undefined ? this.seqCounter - 1 : this.seqCounter++ });
146
+ return this.handle(itemId);
147
+ }
148
+
149
+ /** Settle a stable item after its external transport effect has a known outcome. */
150
+ settle(itemId: string, disposition: Exclude<RateLimitDisposition, "queued" | "sending">): void {
151
+ const deferred = this.settlements.get(itemId);
152
+ if (!deferred) return;
153
+ this.settlements.delete(itemId);
154
+ deferred.resolve(disposition);
155
+ }
156
+
157
+ /** Mark a granted item as owned by an external transport effect. */
158
+ markSending(itemId: string): void {
159
+ if (!this.settlements.has(itemId)) throw new Error(`unknown rate-limit item: ${itemId}`);
160
+ }
161
+
162
+ private handle(itemId: string): RateLimitHandle {
163
+ let deferred = this.settlements.get(itemId);
164
+ if (!deferred) {
165
+ deferred = Promise.withResolvers<Exclude<RateLimitDisposition, "queued" | "sending">>();
166
+ this.settlements.set(itemId, deferred);
167
+ }
168
+ return { itemId, settled: deferred.promise };
135
169
  }
136
170
 
137
171
  /**
@@ -149,7 +183,8 @@ export class RateLimitPool<T = unknown> {
149
183
  */
150
184
  drainWithExpired(nowMs: number = this.now()): RateLimitDrainResult<T> {
151
185
  this.refill(nowMs);
152
- const expired = this.removeWhere(item => item.deadlineAt !== undefined && item.deadlineAt <= nowMs);
186
+ const expired = this.removeWhere(item => item.deadlineAt !== undefined && item.deadlineAt <= nowMs, "expired");
187
+
153
188
  const granted: RateLimitItem<T>[] = [];
154
189
  while (this.tokens >= 1) {
155
190
  const next = this.takeNext();
@@ -161,7 +196,10 @@ export class RateLimitPool<T = unknown> {
161
196
  }
162
197
 
163
198
  /** Remove queued items matching `predicate` without consuming tokens. Returns removed items in lane/FIFO order. */
164
- removeWhere(predicate: (item: RateLimitItem<T>) => boolean): RateLimitItem<T>[] {
199
+ removeWhere(
200
+ predicate: (item: RateLimitItem<T>) => boolean,
201
+ disposition: Exclude<RateLimitDisposition, "queued" | "sending"> = "removed",
202
+ ): RateLimitItem<T>[] {
165
203
  const removed: RateLimitItem<T>[] = [];
166
204
  for (const lane of LANE_PRIORITY) {
167
205
  const queue = this.lanes.get(lane)!;
@@ -170,6 +208,7 @@ export class RateLimitPool<T = unknown> {
170
208
  const queued = queue[read]!;
171
209
  if (predicate(queued.item)) {
172
210
  removed.push(queued.item);
211
+ this.settle(queued.item.itemId!, disposition);
173
212
  } else {
174
213
  queue[write++] = queued;
175
214
  }
@@ -184,7 +223,11 @@ export class RateLimitPool<T = unknown> {
184
223
  for (const lane of LANE_PRIORITY) {
185
224
  const queue = this.lanes.get(lane)!;
186
225
  const index = queue.findIndex(queued => queued.item.itemId === itemId);
187
- if (index >= 0) return queue.splice(index, 1)[0]!.item;
226
+ if (index >= 0) {
227
+ const item = queue.splice(index, 1)[0]!.item;
228
+ this.settle(item.itemId!, "removed");
229
+ return item;
230
+ }
188
231
  }
189
232
  return undefined;
190
233
  }
@@ -203,6 +246,7 @@ export class RateLimitPool<T = unknown> {
203
246
  if (queue.length === 0) continue;
204
247
  const picked = this.pickFairIndex(lane, queue);
205
248
  const [removed] = queue.splice(picked, 1);
249
+ if (removed) this.markSending(removed.item.itemId!);
206
250
  return removed?.item;
207
251
  }
208
252
  return undefined;
@@ -6,14 +6,24 @@
6
6
  * pick a repo to create in or a recent session to resume without typing raw
7
7
  * paths. Dependency-light + injectable so it is unit-testable over a temp dir.
8
8
  */
9
- import * as fs from "node:fs";
9
+ import { createHash } from "node:crypto";
10
+ import * as fs from "node:fs/promises";
10
11
  import * as path from "node:path";
12
+ import { verifyOwnerOnlyPathSecurity } from "@gajae-code/natives";
13
+ import { getAgentDir, getSessionsDir } from "@gajae-code/utils";
14
+ import { FileSessionStorage } from "../../session/session-storage";
15
+ import {
16
+ type LogicalSessionCandidate,
17
+ listManagedSessionCandidates,
18
+ type ManagedSessionScope,
19
+ resolveManagedSessionScope,
20
+ } from "../session-directory";
11
21
 
12
22
  /** One ranked recent-session entry surfaced to the picker. */
13
23
  export interface RecentSessionEntry {
14
- /** Session id (the `.jsonl` file stem). */
24
+ /** Session id from the validated managed candidate header. */
15
25
  sessionId: string;
16
- /** Working directory / repo path, when recoverable from the header. */
26
+ /** Validated workspace path recorded by the managed candidate. */
17
27
  path?: string;
18
28
  /** Branch, when recoverable from the header. */
19
29
  branch?: string;
@@ -30,39 +40,54 @@ export interface RecentSessionEntry {
30
40
  }
31
41
 
32
42
  export interface RecentActivityDeps {
33
- /** Root holding `<encoded-cwd>/<sessionId>.jsonl` history files. */
34
- sessionsRoot: string;
43
+ /** Workspace whose managed sessions will be listed readonly. */
44
+ cwd: string;
45
+ /** Agent directory used to resolve the managed session scope. */
46
+ agentDir?: string;
47
+ /** Explicit managed root for isolated tests. */
48
+ sessionsRoot?: string;
35
49
  /** Optional breadcrumb session-file paths (current terminals). */
36
50
  breadcrumbPaths?: string[];
37
51
  /** Max entries to return (default 20). */
38
52
  limit?: number;
39
53
  /** Include internal helper/sub-agent sessions (default true). */
40
54
  includeInternal?: boolean;
55
+ /** Search every validated v2 workspace scope below the session root (default false). */
56
+ allWorkspaces?: boolean;
41
57
  /** Injection seam for tests. */
42
58
  readInitialLines?: (file: string, maxLines: number) => string[];
43
59
  }
44
60
 
45
- function defaultReadInitialLines(file: string, maxLines: number): string[] {
46
- try {
47
- const lines = fs.readFileSync(file, "utf8").split("\n");
48
- return lines.slice(0, maxLines);
49
- } catch {
50
- return [];
61
+ function readCandidateInitialLines(
62
+ candidate: LogicalSessionCandidate,
63
+ readInitialLines: ((file: string, maxLines: number) => string[]) | undefined,
64
+ ): string[] {
65
+ if (readInitialLines) return readInitialLines(candidate.path, 8);
66
+ if (candidate.provenance !== "legacy") {
67
+ const security = verifyOwnerOnlyPathSecurity(candidate.path, "file");
68
+ if (!security.ok) throw new Error(`Managed session metadata path is unsafe: ${security.code}`);
51
69
  }
70
+ const snapshot = new FileSessionStorage().readSnapshotSync(candidate.path);
71
+ const digest = createHash("sha256").update(snapshot.bytes).digest("hex");
72
+ if (
73
+ snapshot.stat.dev !== candidate.identity.dev ||
74
+ snapshot.stat.ino !== candidate.identity.ino ||
75
+ snapshot.stat.size !== candidate.identity.size ||
76
+ snapshot.stat.mtimeNs !== candidate.identity.mtimeNs ||
77
+ digest !== candidate.identity.sha256
78
+ )
79
+ throw new Error("Managed session changed after ownership was verified.");
80
+ return Buffer.from(snapshot.bytes).toString("utf8").split("\n").slice(0, 8);
52
81
  }
53
82
 
54
83
  /** Best-effort header metadata extraction from a session file's first line. */
55
- function headerMeta(line: string | undefined): { id?: string; path?: string; branch?: string; title?: string } {
84
+ function headerMeta(line: string | undefined): { branch?: string; title?: string } {
56
85
  if (!line) return {};
57
86
  try {
58
87
  const obj = JSON.parse(line) as Record<string, unknown>;
59
- // Session headers vary; pull common fields defensively.
60
- const id = typeof obj.id === "string" ? obj.id : undefined;
61
- const cwd =
62
- typeof obj.cwd === "string" ? obj.cwd : typeof obj.projectDir === "string" ? obj.projectDir : undefined;
63
88
  const branch = typeof obj.branch === "string" ? obj.branch : undefined;
64
89
  const title = typeof obj.title === "string" ? obj.title : undefined;
65
- return { id, path: cwd, branch, title };
90
+ return { branch, title };
66
91
  } catch {
67
92
  return {};
68
93
  }
@@ -84,73 +109,162 @@ function isInternalSession(lines: readonly string[]): boolean {
84
109
  return false;
85
110
  }
86
111
 
87
- /**
88
- * The authoritative session id for a history file: the header `id` when present,
89
- * else the filename stem with a leading `<timestamp>_` prefix stripped (matching
90
- * SessionManager's `<isoTimestamp>_<id>.jsonl` naming), else the bare stem.
91
- */
92
- function sessionIdForFile(stem: string, headerId: string | undefined): string {
93
- if (headerId) return headerId;
94
- const m = stem.match(/^\d{4}-\d{2}-\d{2}T[\d:.-]+Z?_(.+)$/);
95
- return m?.[1] ?? stem;
96
- }
112
+ /** Lists readonly managed candidates, optionally across every validated v2 workspace scope, ranked by history-file mtime. */
113
+ export type ListRecentSessionsResult =
114
+ | { kind: "complete"; entries: RecentSessionEntry[]; warnings: readonly string[] }
115
+ | { kind: "error"; code: "scope_unavailable" | "managed_scan_failed"; message: string };
97
116
 
98
- /**
99
- * List recent sessions ranked by history-file mtime (newest first).
100
- *
101
- * Scans `<sessionsRoot>/<encoded-cwd>/<sessionId>.jsonl`, stats each file, and
102
- * returns up to `limit` entries enriched with header metadata and a
103
- * `currentTerminal` flag for any breadcrumb-referenced session file.
104
- */
105
- export function listRecentSessions(deps: RecentActivityDeps): RecentSessionEntry[] {
106
- const limit = deps.limit ?? 20;
107
- const includeInternal = deps.includeInternal ?? true;
108
- const readInitialLines = deps.readInitialLines ?? defaultReadInitialLines;
109
- const breadcrumbs = new Set((deps.breadcrumbPaths ?? []).map(p => path.resolve(p)));
110
-
111
- let projectDirs: string[];
117
+ async function resolveRecentScopes(
118
+ deps: RecentActivityDeps,
119
+ ): Promise<
120
+ | { kind: "complete"; scopes: ManagedSessionScope[]; warnings: string[] }
121
+ | { kind: "error"; code: "scope_unavailable"; message: string }
122
+ > {
123
+ const agentDir = deps.agentDir ?? getAgentDir();
124
+ const sessionsRoot = deps.sessionsRoot ?? getSessionsDir(agentDir);
125
+ const current = await resolveManagedSessionScope({
126
+ cwd: deps.cwd,
127
+ agentDir,
128
+ sessionsRoot,
129
+ });
130
+ if (!deps.allWorkspaces) {
131
+ if (current.kind !== "resolved") return { kind: "error", code: "scope_unavailable", message: current.message };
132
+ return { kind: "complete", scopes: [current.scope], warnings: [] };
133
+ }
112
134
  try {
113
- projectDirs = fs
114
- .readdirSync(deps.sessionsRoot, { withFileTypes: true })
115
- .filter(d => d.isDirectory())
116
- .map(d => path.join(deps.sessionsRoot, d.name));
117
- } catch {
118
- return [];
135
+ const root = await fs.lstat(sessionsRoot);
136
+ if (!root.isDirectory() || root.isSymbolicLink() || !verifyOwnerOnlyPathSecurity(sessionsRoot, "directory").ok) {
137
+ return {
138
+ kind: "error",
139
+ code: "scope_unavailable",
140
+ message: "The managed sessions root is not a safe directory.",
141
+ };
142
+ }
143
+ } catch (error) {
144
+ if ((error as NodeJS.ErrnoException).code === "ENOENT") return { kind: "complete", scopes: [], warnings: [] };
145
+ return { kind: "error", code: "scope_unavailable", message: "The managed sessions root could not be inspected." };
119
146
  }
147
+ const warnings: string[] = [];
148
+ const scopes = current.kind === "resolved" ? [current.scope] : [];
149
+ const seen = new Set(scopes.map(scope => scope.directoryPath));
120
150
 
121
- const entries: RecentSessionEntry[] = [];
122
- for (const dir of projectDirs) {
123
- let files: string[];
124
- try {
125
- files = fs.readdirSync(dir).filter(name => name.endsWith(".jsonl"));
126
- } catch {
127
- continue;
151
+ const addResolvedScope = async (cwd: string, expectedDirectory?: string): Promise<void> => {
152
+ const resolved = await resolveManagedSessionScope({
153
+ cwd,
154
+ agentDir,
155
+ sessionsRoot,
156
+ });
157
+ if (
158
+ resolved.kind !== "resolved" ||
159
+ (expectedDirectory !== undefined && resolved.scope.directoryPath !== expectedDirectory)
160
+ ) {
161
+ warnings.push("Ignored invalid managed session scope binding.");
162
+ return;
128
163
  }
129
- for (const name of files) {
130
- const file = path.join(dir, name);
131
- let mtimeMs: number;
164
+ if (!seen.has(resolved.scope.directoryPath)) {
165
+ seen.add(resolved.scope.directoryPath);
166
+ scopes.push(resolved.scope);
167
+ }
168
+ };
169
+ try {
170
+ const directories = await fs.readdir(sessionsRoot, { withFileTypes: true });
171
+ for (const directory of directories) {
172
+ if (!directory.isDirectory() || directory.isSymbolicLink() || !directory.name.startsWith("v2-")) continue;
173
+ try {
174
+ const binding = JSON.parse(
175
+ await fs.readFile(path.join(sessionsRoot, directory.name, ".gjc-managed-session-scope.v2.json"), "utf8"),
176
+ ) as { canonicalPath?: unknown };
177
+ if (typeof binding.canonicalPath !== "string") {
178
+ warnings.push("Ignored invalid managed session scope binding.");
179
+ continue;
180
+ }
181
+ await addResolvedScope(binding.canonicalPath, path.join(sessionsRoot, directory.name));
182
+ } catch {
183
+ warnings.push("Ignored unreadable managed session scope binding.");
184
+ }
185
+ }
186
+ for (const directory of directories) {
187
+ if (!directory.isDirectory() || directory.isSymbolicLink() || directory.name.startsWith("v2-")) continue;
132
188
  try {
133
- mtimeMs = fs.statSync(file).mtimeMs;
189
+ const files = await fs.readdir(path.join(sessionsRoot, directory.name), {
190
+ withFileTypes: true,
191
+ });
192
+ for (const file of files) {
193
+ if (!file.isFile() || file.isSymbolicLink() || !file.name.endsWith(".jsonl")) continue;
194
+ try {
195
+ const snapshot = new FileSessionStorage().readSnapshotSync(
196
+ path.join(sessionsRoot, directory.name, file.name),
197
+ );
198
+ const newline = snapshot.bytes.indexOf(0x0a);
199
+ if (newline < 0) continue;
200
+ const header = JSON.parse(Buffer.from(snapshot.bytes.subarray(0, newline)).toString("utf8")) as {
201
+ cwd?: unknown;
202
+ type?: unknown;
203
+ };
204
+ if (header.type === "session" && typeof header.cwd === "string") await addResolvedScope(header.cwd);
205
+ } catch {
206
+ warnings.push("Ignored unreadable legacy managed session candidate.");
207
+ }
208
+ }
134
209
  } catch {
135
- continue;
210
+ warnings.push("Ignored unreadable legacy managed session directory.");
136
211
  }
137
- const initialLines = readInitialLines(file, 8);
138
- const meta = headerMeta(initialLines[0]);
139
- const internal = isInternalSession(initialLines);
140
- if (internal && !includeInternal) continue;
141
- entries.push({
142
- sessionId: sessionIdForFile(name.slice(0, -".jsonl".length), meta.id),
143
- path: meta.path,
144
- branch: meta.branch,
145
- title: meta.title,
146
- sessionStateFile: file,
147
- mtimeMs,
148
- currentTerminal: breadcrumbs.has(path.resolve(file)) || undefined,
149
- internal: internal || undefined,
150
- });
151
212
  }
213
+ } catch (error) {
214
+ if ((error as NodeJS.ErrnoException).code === "ENOENT") return { kind: "complete", scopes, warnings };
215
+ return { kind: "error", code: "scope_unavailable", message: "The managed sessions root could not be read." };
216
+ }
217
+ return { kind: "complete", scopes, warnings };
218
+ }
219
+
220
+ export async function listRecentSessions(deps: RecentActivityDeps): Promise<ListRecentSessionsResult> {
221
+ const limit = deps.limit ?? 20;
222
+ const includeInternal = deps.includeInternal ?? true;
223
+ const readInitialLines = deps.readInitialLines;
224
+ const breadcrumbs = new Set((deps.breadcrumbPaths ?? []).map(candidate => path.resolve(candidate)));
225
+ const resolved = await resolveRecentScopes(deps);
226
+ if (resolved.kind === "error") return resolved;
227
+ const warnings = [...resolved.warnings];
228
+ const candidates: LogicalSessionCandidate[] = [];
229
+ for (const scope of resolved.scopes) {
230
+ const listed = await listManagedSessionCandidates({ scope });
231
+ if (listed.kind !== "complete") {
232
+ return { kind: "error", code: "managed_scan_failed", message: listed.message };
233
+ }
234
+ candidates.push(...listed.owned);
235
+ warnings.push(...listed.invalid.map(invalid => `Ignored invalid managed session candidate: ${invalid.code}`));
152
236
  }
153
237
 
238
+ const entries: RecentSessionEntry[] = [];
239
+ for (const candidate of candidates) {
240
+ let initialLines: string[];
241
+ try {
242
+ initialLines = readCandidateInitialLines(candidate, readInitialLines);
243
+ } catch (error) {
244
+ return {
245
+ kind: "error",
246
+ code: "managed_scan_failed",
247
+ message: `Could not read managed session metadata: ${error instanceof Error ? error.message : String(error)}`,
248
+ };
249
+ }
250
+ const meta = headerMeta(initialLines[0]);
251
+ const internal = isInternalSession(initialLines);
252
+ if (internal && !includeInternal) continue;
253
+ entries.push({
254
+ sessionId: candidate.sessionId,
255
+ path: candidate.cwd,
256
+ branch: meta.branch,
257
+ title: meta.title,
258
+ sessionStateFile: candidate.path,
259
+ mtimeMs: candidate.identity.mtimeMs,
260
+ currentTerminal: breadcrumbs.has(path.resolve(candidate.path)) || undefined,
261
+ internal: internal || undefined,
262
+ });
263
+ }
154
264
  entries.sort((a, b) => b.mtimeMs - a.mtimeMs);
155
- return entries.slice(0, limit);
265
+ return {
266
+ kind: "complete",
267
+ entries: entries.slice(0, limit),
268
+ warnings,
269
+ };
156
270
  }