@gajae-code/coding-agent 0.11.1 → 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 (404) hide show
  1. package/CHANGELOG.md +54 -0
  2. package/dist/types/cli/config-cli.d.ts +11 -1
  3. package/dist/types/cli/notify-cli.d.ts +1 -0
  4. package/dist/types/cli/session-picker.d.ts +2 -1
  5. package/dist/types/cli/setup-cli.d.ts +1 -1
  6. package/dist/types/commands/mcp-serve.d.ts +33 -0
  7. package/dist/types/commands/mcp.d.ts +1 -1
  8. package/dist/types/commands/notify.d.ts +0 -2
  9. package/dist/types/commands/sdk.d.ts +1 -1
  10. package/dist/types/config/atomic-yaml-patch.d.ts +18 -0
  11. package/dist/types/config/file-lock.d.ts +14 -4
  12. package/dist/types/config/keybindings.d.ts +42 -1
  13. package/dist/types/config/model-auth.d.ts +3 -0
  14. package/dist/types/config/model-bindings-applier.d.ts +13 -0
  15. package/dist/types/config/model-discovery-manager.d.ts +49 -0
  16. package/dist/types/config/model-equivalence.d.ts +20 -0
  17. package/dist/types/config/model-registry.d.ts +16 -15
  18. package/dist/types/config/model-resolver.d.ts +28 -18
  19. package/dist/types/config/models-config-schema.d.ts +11 -0
  20. package/dist/types/config/settings-schema.d.ts +56 -1
  21. package/dist/types/config/settings.d.ts +14 -11
  22. package/dist/types/coordinator-mcp/policy.d.ts +5 -0
  23. package/dist/types/coordinator-mcp/question-gate-codec.d.ts +129 -0
  24. package/dist/types/coordinator-mcp/question-state.d.ts +356 -0
  25. package/dist/types/coordinator-mcp/server.d.ts +4 -0
  26. package/dist/types/discovery/helpers.d.ts +0 -6
  27. package/dist/types/edit/index.d.ts +3 -0
  28. package/dist/types/eval/py/env.d.ts +9 -0
  29. package/dist/types/extensibility/extensions/loader.d.ts +2 -1
  30. package/dist/types/extensibility/extensions/runner.d.ts +2 -2
  31. package/dist/types/extensibility/extensions/types.d.ts +34 -3
  32. package/dist/types/extensibility/hooks/runner.d.ts +1 -1
  33. package/dist/types/extensibility/shared-events.d.ts +2 -0
  34. package/dist/types/gjc-runtime/deep-interview-recorder.d.ts +10 -1
  35. package/dist/types/gjc-runtime/deep-interview-state.d.ts +44 -0
  36. package/dist/types/gjc-runtime/goal-mode-request.d.ts +11 -1
  37. package/dist/types/gjc-runtime/launch-worktree.d.ts +4 -2
  38. package/dist/types/gjc-runtime/linux-proc.d.ts +29 -0
  39. package/dist/types/gjc-runtime/session-resolution.d.ts +2 -2
  40. package/dist/types/gjc-runtime/state-migrations.d.ts +1 -1
  41. package/dist/types/gjc-runtime/state-writer.d.ts +2 -1
  42. package/dist/types/gjc-runtime/team-launch.d.ts +59 -0
  43. package/dist/types/gjc-runtime/team-notify.d.ts +45 -0
  44. package/dist/types/gjc-runtime/team-runtime.d.ts +29 -121
  45. package/dist/types/gjc-runtime/team-store.d.ts +160 -0
  46. package/dist/types/gjc-runtime/team-workers.d.ts +78 -0
  47. package/dist/types/gjc-runtime/tmux-owner-isolation.d.ts +8 -0
  48. package/dist/types/gjc-runtime/ultragoal-change-set.d.ts +13 -0
  49. package/dist/types/gjc-runtime/ultragoal-evidence.d.ts +38 -0
  50. package/dist/types/gjc-runtime/ultragoal-guard.d.ts +7 -0
  51. package/dist/types/gjc-runtime/ultragoal-pipeline.d.ts +23 -0
  52. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +55 -33
  53. package/dist/types/gjc-runtime/workflow-cli-common.d.ts +8 -0
  54. package/dist/types/gjc-runtime/workflow-manifest.d.ts +3 -0
  55. package/dist/types/goals/continuation-timeout-guard.d.ts +22 -0
  56. package/dist/types/goals/runtime.d.ts +1 -0
  57. package/dist/types/goals/state.d.ts +8 -0
  58. package/dist/types/harness-control-plane/lifecycle-reconciliation.d.ts +17 -0
  59. package/dist/types/harness-control-plane/phase-rollup.d.ts +4 -7
  60. package/dist/types/harness-control-plane/receipt-ingest.d.ts +5 -0
  61. package/dist/types/harness-control-plane/receipt-spool.d.ts +4 -0
  62. package/dist/types/hindsight/state.d.ts +11 -4
  63. package/dist/types/lsp/lspmux.d.ts +5 -3
  64. package/dist/types/lsp/path-trust.d.ts +1 -0
  65. package/dist/types/main.d.ts +3 -2
  66. package/dist/types/memories/storage.d.ts +1 -0
  67. package/dist/types/modes/action-registry.d.ts +29 -0
  68. package/dist/types/modes/components/chrome.d.ts +14 -0
  69. package/dist/types/modes/components/command-palette.d.ts +27 -0
  70. package/dist/types/modes/components/custom-provider-wizard.d.ts +1 -0
  71. package/dist/types/modes/components/index.d.ts +2 -1
  72. package/dist/types/modes/components/pet-selector.d.ts +1 -1
  73. package/dist/types/modes/components/plan-preview-overlay.d.ts +43 -0
  74. package/dist/types/modes/components/queue-mode-selector.d.ts +1 -1
  75. package/dist/types/modes/components/queue-pane.d.ts +12 -0
  76. package/dist/types/modes/components/read-tool-group.d.ts +6 -0
  77. package/dist/types/modes/components/session-observer-overlay.d.ts +3 -4
  78. package/dist/types/modes/components/sessions-dashboard.d.ts +32 -0
  79. package/dist/types/modes/components/settings-selector.d.ts +1 -1
  80. package/dist/types/modes/components/show-images-selector.d.ts +1 -1
  81. package/dist/types/modes/components/status-line/types.d.ts +1 -1
  82. package/dist/types/modes/components/status-line.d.ts +1 -94
  83. package/dist/types/modes/components/tasks-pane.d.ts +15 -0
  84. package/dist/types/modes/components/theme-selector.d.ts +1 -1
  85. package/dist/types/modes/components/tool-execution.d.ts +15 -0
  86. package/dist/types/modes/components/tool-status-header.d.ts +111 -0
  87. package/dist/types/modes/components/transcript-viewer-overlay.d.ts +42 -0
  88. package/dist/types/modes/controllers/goal-mode-controller.d.ts +62 -0
  89. package/dist/types/modes/controllers/input-controller.d.ts +8 -1
  90. package/dist/types/modes/controllers/mode-gate.d.ts +8 -0
  91. package/dist/types/modes/controllers/plan-mode-controller.d.ts +65 -0
  92. package/dist/types/modes/controllers/selector-controller.d.ts +15 -3
  93. package/dist/types/modes/controllers/stt-controller.d.ts +26 -0
  94. package/dist/types/modes/interactive-mode.d.ts +19 -17
  95. package/dist/types/modes/jobs-observer.d.ts +2 -0
  96. package/dist/types/modes/shared/agent-wire/deep-interview-gate.d.ts +4 -11
  97. package/dist/types/modes/shared/agent-wire/workflow-gate-broker.d.ts +20 -2
  98. package/dist/types/modes/shared/agent-wire/workflow-gate-types.d.ts +31 -0
  99. package/dist/types/modes/tasks-aggregator.d.ts +39 -0
  100. package/dist/types/modes/transcript-item-registry.d.ts +60 -0
  101. package/dist/types/modes/types.d.ts +20 -13
  102. package/dist/types/modes/utils/ui-helpers.d.ts +2 -2
  103. package/dist/types/runtime-mcp/manager.d.ts +1 -1
  104. package/dist/types/sdk/broker/broker.d.ts +78 -12
  105. package/dist/types/sdk/broker/lifecycle-codec.d.ts +4 -0
  106. package/dist/types/sdk/broker/lifecycle-ledger.d.ts +15 -1
  107. package/dist/types/sdk/broker/lifecycle.d.ts +6 -2
  108. package/dist/types/sdk/broker/process-incarnation.d.ts +2 -0
  109. package/dist/types/sdk/broker/session-index.d.ts +0 -1
  110. package/dist/types/sdk/broker/state-version.d.ts +2 -0
  111. package/dist/types/sdk/bus/chat-effect-journal.d.ts +1 -0
  112. package/dist/types/sdk/bus/config-commands.d.ts +1 -1
  113. package/dist/types/sdk/bus/config.d.ts +11 -0
  114. package/dist/types/sdk/bus/daemon-paths.d.ts +1 -0
  115. package/dist/types/sdk/bus/html-format.d.ts +3 -2
  116. package/dist/types/sdk/bus/index.d.ts +27 -0
  117. package/dist/types/sdk/bus/lifecycle-control-runtime.d.ts +4 -0
  118. package/dist/types/sdk/bus/notification-service.d.ts +20 -0
  119. package/dist/types/sdk/bus/rate-limit-pool.d.ts +13 -2
  120. package/dist/types/sdk/bus/recent-activity.d.ts +21 -12
  121. package/dist/types/sdk/bus/rich-document.d.ts +7 -0
  122. package/dist/types/sdk/bus/rich-render.d.ts +18 -41
  123. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +9 -4
  124. package/dist/types/sdk/bus/telegram-daemon.d.ts +46 -3
  125. package/dist/types/sdk/bus/threaded-inbound.d.ts +1 -1
  126. package/dist/types/sdk/bus/threaded-render.d.ts +13 -0
  127. package/dist/types/sdk/bus/topic-registry.d.ts +24 -2
  128. package/dist/types/sdk/host/control/operations.d.ts +1 -1
  129. package/dist/types/sdk/host/host.d.ts +2 -0
  130. package/dist/types/sdk/index.d.ts +1 -0
  131. package/dist/types/sdk/session-directory.d.ts +56 -0
  132. package/dist/types/session/agent-session.d.ts +82 -47
  133. package/dist/types/session/agent-storage.d.ts +7 -0
  134. package/dist/types/session/fallback-chain-controller.d.ts +10 -2
  135. package/dist/types/session/internal/managed-session-scope.d.ts +102 -0
  136. package/dist/types/session/internal/managed-session-storage.d.ts +38 -0
  137. package/dist/types/session/messages.d.ts +16 -0
  138. package/dist/types/session/session-manager-internal.d.ts +14 -0
  139. package/dist/types/session/session-manager.d.ts +82 -12
  140. package/dist/types/session/session-storage.d.ts +43 -3
  141. package/dist/types/session/volatile-context-pruning.d.ts +15 -0
  142. package/dist/types/skill-state/{deep-interview-mutation-guard.d.ts → workflow-mutation-guard.d.ts} +24 -8
  143. package/dist/types/skill-state/workflow-state-contract.d.ts +1 -3
  144. package/dist/types/slash-commands/builtin-registry.d.ts +4 -1
  145. package/dist/types/system-prompt.d.ts +2 -11
  146. package/dist/types/task/executor.d.ts +3 -1
  147. package/dist/types/thinking.d.ts +7 -0
  148. package/dist/types/tool-discovery/tool-index.d.ts +1 -50
  149. package/dist/types/tools/ask.d.ts +64 -2
  150. package/dist/types/tools/bash.d.ts +3 -0
  151. package/dist/types/tools/browser/dead-tab-recovery.d.ts +24 -0
  152. package/dist/types/tools/browser/tab-supervisor.d.ts +21 -0
  153. package/dist/types/tools/cron.d.ts +2 -0
  154. package/dist/types/tools/fetch.d.ts +1 -0
  155. package/dist/types/tools/index.d.ts +30 -15
  156. package/dist/types/tools/output-meta.d.ts +1 -0
  157. package/dist/types/tools/read.d.ts +3 -0
  158. package/dist/types/tools/search-tool-bm25.d.ts +3 -4
  159. package/dist/types/utils/event-bus.d.ts +1 -1
  160. package/dist/types/web/search/index.d.ts +3 -1
  161. package/package.json +21 -9
  162. package/scripts/build-sdk-package-smoke.ts +34 -1
  163. package/scripts/generate-hotkeys-docs.ts +74 -0
  164. package/scripts/generate-sdk-operation-inventory.ts +64 -20
  165. package/scripts/verify-gjc-sdk-canonicalization.ts +182 -2
  166. package/src/async/job-manager.ts +49 -6
  167. package/src/cli/config-cli.ts +75 -2
  168. package/src/cli/notify-cli.ts +92 -26
  169. package/src/cli/session-picker.ts +20 -3
  170. package/src/cli/setup-cli.ts +1 -1
  171. package/src/cli.ts +1 -1
  172. package/src/commands/config.ts +1 -1
  173. package/src/commands/harness.ts +34 -106
  174. package/src/commands/launch.ts +6 -4
  175. package/src/commands/mcp-serve.ts +85 -8
  176. package/src/commands/mcp.ts +2 -2
  177. package/src/commands/notify.ts +8 -4
  178. package/src/commands/sdk.ts +61 -40
  179. package/src/commands/ultragoal.ts +4 -1
  180. package/src/config/atomic-yaml-patch.ts +80 -13
  181. package/src/config/file-lock.ts +105 -43
  182. package/src/config/keybindings.ts +64 -20
  183. package/src/config/model-auth.ts +9 -0
  184. package/src/config/model-bindings-applier.ts +139 -0
  185. package/src/config/model-discovery-manager.ts +224 -0
  186. package/src/config/model-equivalence.ts +66 -0
  187. package/src/config/model-registry.ts +169 -302
  188. package/src/config/model-resolver.ts +361 -369
  189. package/src/config/models-config-schema.ts +5 -4
  190. package/src/config/settings-schema.ts +159 -2
  191. package/src/config/settings.ts +458 -482
  192. package/src/coordinator-mcp/policy.ts +76 -11
  193. package/src/coordinator-mcp/question-gate-codec.ts +274 -0
  194. package/src/coordinator-mcp/question-state.ts +778 -0
  195. package/src/coordinator-mcp/server.ts +1702 -238
  196. package/src/debug/index.ts +1 -1
  197. package/src/defaults/gjc/skills/deep-interview/SKILL.md +13 -1
  198. package/src/defaults/gjc/skills/team/SKILL.md +16 -0
  199. package/src/discovery/helpers.ts +1 -50
  200. package/src/edit/index.ts +64 -0
  201. package/src/eval/py/env.ts +26 -0
  202. package/src/eval/py/kernel.ts +5 -3
  203. package/src/extensibility/extensions/loader.ts +9 -1
  204. package/src/extensibility/extensions/runner.ts +12 -3
  205. package/src/extensibility/extensions/types.ts +39 -1
  206. package/src/extensibility/gjc-plugins/installer.ts +16 -16
  207. package/src/extensibility/hooks/runner.ts +2 -2
  208. package/src/extensibility/runtime-skill-discovery.ts +33 -13
  209. package/src/extensibility/shared-events.ts +2 -0
  210. package/src/gjc-runtime/deep-interview-recorder.ts +63 -3
  211. package/src/gjc-runtime/deep-interview-runtime.ts +76 -27
  212. package/src/gjc-runtime/deep-interview-state.ts +317 -5
  213. package/src/gjc-runtime/goal-mode-request.ts +55 -8
  214. package/src/gjc-runtime/launch-tmux.ts +2 -5
  215. package/src/gjc-runtime/launch-worktree.ts +5 -5
  216. package/src/gjc-runtime/linux-proc.ts +71 -0
  217. package/src/gjc-runtime/ralplan-runtime.ts +74 -90
  218. package/src/gjc-runtime/session-resolution.ts +18 -3
  219. package/src/gjc-runtime/session-state-sidecar.ts +6 -107
  220. package/src/gjc-runtime/state-migrations.ts +1 -1
  221. package/src/gjc-runtime/state-runtime.ts +320 -249
  222. package/src/gjc-runtime/state-writer.ts +36 -40
  223. package/src/gjc-runtime/team-launch.ts +319 -0
  224. package/src/gjc-runtime/team-notify.ts +326 -0
  225. package/src/gjc-runtime/team-runtime.ts +1099 -1482
  226. package/src/gjc-runtime/team-store.ts +828 -0
  227. package/src/gjc-runtime/team-workers.ts +962 -0
  228. package/src/gjc-runtime/tmux-owner-isolation-cli.ts +2 -11
  229. package/src/gjc-runtime/tmux-owner-isolation.ts +14 -9
  230. package/src/gjc-runtime/tmux-sessions.ts +4 -21
  231. package/src/gjc-runtime/ultragoal-change-set.ts +172 -0
  232. package/src/gjc-runtime/ultragoal-evidence.ts +874 -0
  233. package/src/gjc-runtime/ultragoal-guard.ts +29 -5
  234. package/src/gjc-runtime/ultragoal-pipeline.ts +306 -0
  235. package/src/gjc-runtime/ultragoal-runtime.ts +84 -1311
  236. package/src/gjc-runtime/workflow-cli-common.ts +30 -0
  237. package/src/gjc-runtime/workflow-manifest.generated.json +58 -0
  238. package/src/gjc-runtime/workflow-manifest.ts +18 -0
  239. package/src/goals/continuation-timeout-guard.ts +79 -0
  240. package/src/goals/runtime.ts +7 -5
  241. package/src/goals/state.ts +18 -0
  242. package/src/harness-control-plane/lifecycle-reconciliation.ts +64 -0
  243. package/src/harness-control-plane/phase-rollup.ts +4 -7
  244. package/src/harness-control-plane/receipt-ingest.ts +5 -0
  245. package/src/harness-control-plane/receipt-spool.ts +51 -7
  246. package/src/harness-control-plane/session-lease.ts +12 -130
  247. package/src/hindsight/backend.ts +26 -27
  248. package/src/hindsight/state.ts +101 -32
  249. package/src/hooks/skill-state.ts +17 -11
  250. package/src/internal-urls/docs-index.generated.ts +21 -18
  251. package/src/lsp/client.ts +1 -1
  252. package/src/lsp/config.ts +145 -26
  253. package/src/lsp/index.ts +1 -1
  254. package/src/lsp/lspmux.ts +39 -10
  255. package/src/lsp/path-trust.ts +81 -0
  256. package/src/main.ts +28 -8
  257. package/src/memories/index.ts +1 -0
  258. package/src/memories/storage.ts +6 -2
  259. package/src/modes/action-registry.ts +165 -0
  260. package/src/modes/components/chrome.ts +40 -0
  261. package/src/modes/components/command-palette.ts +168 -0
  262. package/src/modes/components/custom-editor.ts +1 -1
  263. package/src/modes/components/custom-provider-wizard.ts +51 -14
  264. package/src/modes/components/index.ts +2 -1
  265. package/src/modes/components/pet-selector.ts +11 -17
  266. package/src/modes/components/plan-preview-overlay.ts +295 -0
  267. package/src/modes/components/queue-mode-selector.ts +10 -27
  268. package/src/modes/components/queue-pane.ts +79 -0
  269. package/src/modes/components/read-tool-group.ts +13 -0
  270. package/src/modes/components/session-observer-overlay.ts +207 -772
  271. package/src/modes/components/sessions-dashboard.ts +181 -0
  272. package/src/modes/components/settings-selector.ts +1 -1
  273. package/src/modes/components/show-images-selector.ts +10 -24
  274. package/src/modes/components/status-line/types.ts +1 -1
  275. package/src/modes/components/status-line.ts +1 -969
  276. package/src/modes/components/tasks-pane.ts +73 -0
  277. package/src/modes/components/theme-selector.ts +11 -31
  278. package/src/modes/components/tool-execution.ts +22 -0
  279. package/src/modes/components/tool-status-header.ts +1056 -0
  280. package/src/modes/components/transcript-viewer-overlay.ts +384 -0
  281. package/src/modes/controllers/btw-controller.ts +1 -0
  282. package/src/modes/controllers/event-controller.ts +3 -2
  283. package/src/modes/controllers/extension-ui-controller.ts +24 -1
  284. package/src/modes/controllers/goal-mode-controller.ts +474 -0
  285. package/src/modes/controllers/input-controller.ts +764 -229
  286. package/src/modes/controllers/mode-gate.ts +24 -0
  287. package/src/modes/controllers/plan-mode-controller.ts +517 -0
  288. package/src/modes/controllers/selector-controller.ts +225 -10
  289. package/src/modes/controllers/stt-controller.ts +125 -0
  290. package/src/modes/interactive-mode.ts +289 -1150
  291. package/src/modes/jobs-observer.ts +3 -0
  292. package/src/modes/runtime-init.ts +8 -0
  293. package/src/modes/shared/agent-wire/deep-interview-gate.ts +31 -106
  294. package/src/modes/shared/agent-wire/workflow-gate-broker.ts +71 -16
  295. package/src/modes/shared/agent-wire/workflow-gate-types.ts +177 -0
  296. package/src/modes/tasks-aggregator.ts +199 -0
  297. package/src/modes/transcript-item-registry.ts +229 -0
  298. package/src/modes/types.ts +45 -18
  299. package/src/modes/utils/ui-helpers.ts +17 -9
  300. package/src/prompts/agent-fragments/ralplan-persistence.md +6 -0
  301. package/src/prompts/agent-fragments/restricted-bash.md +1 -0
  302. package/src/prompts/agents/architect.md +2 -8
  303. package/src/prompts/agents/critic.md +2 -8
  304. package/src/prompts/agents/executor.md +3 -0
  305. package/src/prompts/agents/planner.md +4 -12
  306. package/src/prompts/system/custom-system-prompt.md +1 -23
  307. package/src/prompts/system/plan-mode-approved.md +8 -0
  308. package/src/prompts/system/system-prompt.md +17 -96
  309. package/src/prompts/tools/bash.md +4 -4
  310. package/src/prompts/tools/browser.md +15 -52
  311. package/src/prompts/tools/find.md +2 -2
  312. package/src/prompts/tools/hashline.md +13 -55
  313. package/src/prompts/tools/read.md +3 -6
  314. package/src/prompts/tools/search-tool-bm25.md +0 -2
  315. package/src/prompts/tools/search.md +2 -4
  316. package/src/prompts/tools/skill-discovery.md +1 -2
  317. package/src/prompts/tools/task.md +1 -1
  318. package/src/runtime-mcp/manager.ts +1 -1
  319. package/src/sdk/broker/broker.ts +206 -78
  320. package/src/sdk/broker/discovery.ts +1 -1
  321. package/src/sdk/broker/ensure.ts +3 -1
  322. package/src/sdk/broker/lifecycle-codec.ts +11 -0
  323. package/src/sdk/broker/lifecycle-ledger.ts +371 -74
  324. package/src/sdk/broker/lifecycle.ts +1399 -142
  325. package/src/sdk/broker/process-incarnation.ts +5 -13
  326. package/src/sdk/broker/session-index.ts +145 -56
  327. package/src/sdk/broker/state-version.ts +21 -0
  328. package/src/sdk/broker/transport.ts +1 -1
  329. package/src/sdk/bus/chat-daemon-cli.ts +2 -0
  330. package/src/sdk/bus/chat-daemon-control.ts +8 -28
  331. package/src/sdk/bus/chat-effect-journal.ts +10 -2
  332. package/src/sdk/bus/config-commands.ts +5 -4
  333. package/src/sdk/bus/config.ts +98 -0
  334. package/src/sdk/bus/conversation-store.ts +5 -29
  335. package/src/sdk/bus/daemon-paths.ts +2 -0
  336. package/src/sdk/bus/html-format.ts +20 -3
  337. package/src/sdk/bus/index.ts +778 -93
  338. package/src/sdk/bus/lifecycle-control-runtime.ts +91 -33
  339. package/src/sdk/bus/natives-augment.d.ts +26 -1
  340. package/src/sdk/bus/notification-service.ts +168 -20
  341. package/src/sdk/bus/rate-limit-pool.ts +58 -14
  342. package/src/sdk/bus/recent-activity.ts +188 -74
  343. package/src/sdk/bus/rich-document.ts +258 -0
  344. package/src/sdk/bus/rich-render.ts +62 -70
  345. package/src/sdk/bus/telegram-daemon-cli.ts +15 -59
  346. package/src/sdk/bus/telegram-daemon-contract.ts +9 -4
  347. package/src/sdk/bus/telegram-daemon.ts +1389 -237
  348. package/src/sdk/bus/threaded-inbound.ts +8 -3
  349. package/src/sdk/bus/threaded-render.ts +57 -1
  350. package/src/sdk/bus/topic-registry.ts +120 -13
  351. package/src/sdk/host/control/dispatch.ts +14 -2
  352. package/src/sdk/host/control/operations.ts +6 -1
  353. package/src/sdk/host/host.ts +31 -1
  354. package/src/sdk/host/query/handlers.ts +20 -22
  355. package/src/sdk/host/query/revision-store.ts +80 -57
  356. package/src/sdk/index.ts +1 -0
  357. package/src/sdk/protocol/operation-inventory.generated.json +943 -1907
  358. package/src/sdk/session-directory.ts +141 -0
  359. package/src/sdk/session.ts +458 -479
  360. package/src/session/agent-session.ts +1700 -583
  361. package/src/session/agent-storage.ts +18 -0
  362. package/src/session/cache-economics.ts +25 -8
  363. package/src/session/fallback-chain-controller.ts +53 -3
  364. package/src/session/internal/managed-session-scope.ts +2226 -0
  365. package/src/session/internal/managed-session-storage.ts +453 -0
  366. package/src/session/messages.ts +91 -2
  367. package/src/session/session-manager-internal.ts +33 -0
  368. package/src/session/session-manager.ts +1112 -333
  369. package/src/session/session-storage.ts +444 -39
  370. package/src/session/volatile-context-pruning.ts +78 -0
  371. package/src/setup/credential-auto-import.ts +2 -1
  372. package/src/setup/hermes/templates/operator-instructions.v1.md +1 -1
  373. package/src/setup/provider-onboarding.ts +36 -61
  374. package/src/setup/provider-presets.json +5 -0
  375. package/src/skill-state/active-state.ts +3 -13
  376. package/src/skill-state/{deep-interview-mutation-guard.ts → workflow-mutation-guard.ts} +47 -29
  377. package/src/skill-state/workflow-state-contract.ts +6 -41
  378. package/src/slash-commands/builtin-registry.ts +46 -11
  379. package/src/system-prompt.ts +37 -44
  380. package/src/task/agents.ts +19 -1
  381. package/src/task/executor.ts +90 -6
  382. package/src/task/index.ts +36 -21
  383. package/src/thinking.ts +21 -6
  384. package/src/tool-discovery/tool-index.ts +2 -134
  385. package/src/tools/ask.ts +157 -21
  386. package/src/tools/ast-edit.ts +1 -1
  387. package/src/tools/bash.ts +38 -0
  388. package/src/tools/browser/dead-tab-recovery.ts +78 -0
  389. package/src/tools/browser/tab-supervisor.ts +282 -36
  390. package/src/tools/computer.ts +10 -3
  391. package/src/tools/cron.ts +30 -16
  392. package/src/tools/eval.ts +1 -2
  393. package/src/tools/fetch.ts +11 -4
  394. package/src/tools/index.ts +76 -24
  395. package/src/tools/output-meta.ts +13 -5
  396. package/src/tools/read.ts +37 -1
  397. package/src/tools/resource-gc.ts +2 -2
  398. package/src/tools/search-tool-bm25.ts +11 -74
  399. package/src/utils/event-bus.ts +9 -4
  400. package/src/web/search/index.ts +3 -3
  401. package/dist/types/discovery/plugin-dir-roots.d.ts +0 -15
  402. package/dist/types/runtime-mcp/discoverable-tool-metadata.d.ts +0 -7
  403. package/src/discovery/plugin-dir-roots.ts +0 -28
  404. package/src/runtime-mcp/discoverable-tool-metadata.ts +0 -25
@@ -1,7 +1,12 @@
1
1
  import { createHash, randomBytes } from "node:crypto";
2
2
  import * as fs from "node:fs/promises";
3
3
  import path from "node:path";
4
- import { resolveResumableSession, SessionManager } from "../../session/session-manager";
4
+ import type { NativeDirectoryTreeSnapshot } from "@gajae-code/natives";
5
+ import {
6
+ type DirectoryMigrationPolicy,
7
+ listManagedSessionCandidates,
8
+ resolveManagedSessionScope,
9
+ } from "../session-directory";
5
10
  import {
6
11
  BROKER_HEARTBEAT_TTL_MS,
7
12
  type BrokerDiscovery,
@@ -9,6 +14,7 @@ import {
9
14
  brokerProcessIncarnation,
10
15
  isPidAlive,
11
16
  newBrokerToken,
17
+ type RedactedBrokerDiscovery,
12
18
  readBrokerDiscovery,
13
19
  redactBrokerDiscovery,
14
20
  writeBrokerDiscovery,
@@ -20,6 +26,7 @@ import {
20
26
  type LifecycleDurableEffectsReceipt,
21
27
  LifecycleLedger,
22
28
  type LifecycleStartupFailureReceipt,
29
+ type LifecycleState,
23
30
  } from "./lifecycle-ledger";
24
31
  import { type IndexedSession, SessionIndex } from "./session-index";
25
32
  import { BrokerTransport } from "./transport";
@@ -29,7 +36,18 @@ export interface BrokerSettings {
29
36
  packageGeneration?: string;
30
37
  port?: number;
31
38
  heartbeatTtlMs?: number;
39
+ /** Broker-owned migration policy. Client lifecycle frames cannot select it. */
40
+ resolveDirectoryMigration?: (_cwd: string) => Promise<DirectoryMigrationPolicy>;
32
41
  }
42
+
43
+ type ResolvedBrokerSettings = {
44
+ agentDir: string;
45
+ packageGeneration: string;
46
+ port: number;
47
+ heartbeatTtlMs: number;
48
+ resolveDirectoryMigration: (_cwd: string) => Promise<DirectoryMigrationPolicy>;
49
+ };
50
+
33
51
  export type BrokerErrorCode =
34
52
  | "idempotency_conflict"
35
53
  | "terminal_uncertain"
@@ -46,10 +64,73 @@ export type BrokerErrorCode =
46
64
  | "cleanup_pending"
47
65
  | (string & {});
48
66
 
67
+ export type BrokerCleanupIdentity = {
68
+ dev: string;
69
+ ino: string;
70
+ size: number;
71
+ mtimeNs: string;
72
+ sha256: string;
73
+ };
74
+
75
+ /** Exact retry evidence; detached paths are managed-receipt references, never caller authority. */
76
+ export type BrokerLifecycleCleanupFile = {
77
+ /** Original lifecycle-owned path, retained only for exact identity validation. */
78
+ path: string;
79
+ identity: BrokerCleanupIdentity;
80
+ /** Monotonic append-only cleanup attempt. */
81
+ attempt?: number;
82
+ /** Immutable no-replace quarantine destination persisted before native detach. */
83
+ plannedPath: string;
84
+ /** Native-returned detached path, persisted after a failed post-detach cleanup. */
85
+ detachedPath?: string;
86
+ /** Append-only terminal proof for this exact artifact; completed entries are never retried. */
87
+ completed?: true;
88
+ };
89
+
90
+ /** Durable root-tree authority for broker artifact cleanup. */
91
+ export type BrokerArtifactTree = {
92
+ identity: BrokerCleanupIdentity;
93
+ snapshot: NativeDirectoryTreeSnapshot;
94
+ plannedPath: string;
95
+ detachedPath?: string;
96
+ completed?: true;
97
+ };
98
+
49
99
  export type BrokerCleanupEvidence = {
50
- phase: "artifacts" | "transcript" | "metadata";
51
- artifactsIdentity?: { dev: string; ino: string };
52
- transcriptIdentity?: { dev: string; ino: string };
100
+ phase: "artifacts" | "transcript" | "metadata" | "lifecycle";
101
+ /** Ledger-bound deletion target; never reconstructed from a retry request. */
102
+ sessionsRoot?: string;
103
+ transcriptPath?: string;
104
+ cwd?: string;
105
+ metadataRoot?: string;
106
+ sessionId?: string;
107
+ artifactsIdentity?: BrokerCleanupIdentity;
108
+ transcriptIdentity?: BrokerCleanupIdentity;
109
+ /** Identity-bound lifecycle metadata marker retained when exact cleanup is deferred. */
110
+ metadataIdentity?: BrokerCleanupIdentity;
111
+ metadataPath?: string;
112
+ /** Monotonic append-only cleanup attempt. */
113
+ metadataAttempt?: number;
114
+ /** No-replace quarantine destination persisted before lifecycle metadata detach. */
115
+ plannedMetadataPath?: string;
116
+ /** Native-returned metadata quarantine path retained until identity-bound reconciliation succeeds. */
117
+ detachedMetadataPath?: string;
118
+ /** Append-only terminal proof for lifecycle metadata cleanup. */
119
+ metadataCompleted?: true;
120
+ detachedArtifactsPath?: string;
121
+ detachedTranscriptPath?: string;
122
+ /** Durable proof that artifact cleanup completed before transcript mutation. */
123
+ artifactsRemoved?: boolean;
124
+ /** Preauthorized no-replace artifact quarantine path persisted before detach. */
125
+ plannedArtifactsPath?: string;
126
+ /** Identity-bound artifact tree authority persisted before broker detach and replayed exactly. */
127
+ artifactTree?: BrokerArtifactTree;
128
+ /** Preauthorized no-replace transcript quarantine path persisted before detach. */
129
+ plannedTranscriptPath?: string;
130
+ /** Fully identity-bound startup-failure cleanup plan, persisted before any detach. */
131
+ lifecycleFiles?: BrokerLifecycleCleanupFile[];
132
+ /** Delete metadata receipts authorize only the canonical marker/ready sibling pair. */
133
+ lifecycleDeleteMetadata?: true;
53
134
  };
54
135
  export type BrokerResponse =
55
136
  | { ok: true; result?: unknown; indexSeq?: number }
@@ -67,6 +148,16 @@ export type BrokerResponse =
67
148
  };
68
149
  const error = (code: BrokerErrorCode, message: string): BrokerResponse => ({ ok: false, error: { code, message } });
69
150
 
151
+ function isCleanupPending(response: BrokerResponse): boolean {
152
+ return !response.ok && response.error.code === "cleanup_pending" && response.error.cleanup !== undefined;
153
+ }
154
+
155
+ function lifecycleResponseState(response: BrokerResponse): LifecycleState {
156
+ if (response.ok) return "terminal_ok";
157
+ if (isCleanupPending(response)) return "effect_started";
158
+ return response.error.code === "terminal_uncertain" ? "terminal_uncertain" : "terminal_error";
159
+ }
160
+
70
161
  type InputNormalization = { input: Record<string, unknown> } | BrokerResponse;
71
162
 
72
163
  function isBrokerResponse(value: InputNormalization): value is BrokerResponse {
@@ -106,6 +197,8 @@ function normalizeBrokerInput(operation: string, input: Record<string, unknown>)
106
197
  normalized.sourceSessionId = source.value;
107
198
  delete normalized.sourceId;
108
199
  }
200
+ if (input.directoryMigration !== undefined)
201
+ return error("invalid_input", "directoryMigration is broker-managed and cannot be selected by clients.");
109
202
 
110
203
  if (operation === "session.list") {
111
204
  const resolved = input.resolveSessionId;
@@ -272,7 +365,7 @@ type BrokerLockSnapshot = {
272
365
  const terminalPersistenceHooksForTest = new WeakMap<Broker, () => void>();
273
366
 
274
367
  export class Broker {
275
- readonly settings: Required<BrokerSettings>;
368
+ readonly settings: ResolvedBrokerSettings;
276
369
  readonly index: SessionIndex;
277
370
  readonly ledger: LifecycleLedger;
278
371
  discovery: BrokerDiscovery | null = null;
@@ -281,10 +374,16 @@ export class Broker {
281
374
  #chains = new Map<string, Promise<void>>();
282
375
  #stopping = false;
283
376
  #transport: BrokerTransport | null = null;
284
- #heartbeatTimer: ReturnType<typeof setInterval> | null = null;
377
+ #heartbeatTimer: NodeJS.Timeout | null = null;
285
378
  #heartbeatWrite: Promise<void> = Promise.resolve();
286
379
  constructor(settings: BrokerSettings) {
287
- this.settings = { packageGeneration: "unknown", port: 0, heartbeatTtlMs: BROKER_HEARTBEAT_TTL_MS, ...settings };
380
+ this.settings = {
381
+ agentDir: settings.agentDir,
382
+ packageGeneration: settings.packageGeneration ?? "unknown",
383
+ port: settings.port ?? 0,
384
+ heartbeatTtlMs: settings.heartbeatTtlMs ?? BROKER_HEARTBEAT_TTL_MS,
385
+ resolveDirectoryMigration: settings.resolveDirectoryMigration ?? (async () => "copy-retain"),
386
+ };
288
387
  this.index = new SessionIndex(settings.agentDir);
289
388
  this.ledger = new LifecycleLedger(settings.agentDir);
290
389
  this.#lock = path.join(settings.agentDir, "sdk", "broker.lock");
@@ -379,13 +478,20 @@ export class Broker {
379
478
  throw e;
380
479
  }
381
480
  }
481
+ async #releaseOwnedLock(): Promise<void> {
482
+ try {
483
+ const lock = await this.#readLock();
484
+ if (lock?.ownerId !== this.#owner) return;
485
+ await fs.unlink(this.#lockRecordPath());
486
+ await fs.rmdir(this.#lock);
487
+ } catch (error) {
488
+ if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
489
+ }
490
+ }
491
+
382
492
  async start(): Promise<BrokerDiscovery> {
383
493
  this.#stopping = false;
384
- await Promise.all([
385
- this.index.assertSupportedStateVersions(),
386
- this.ledger.assertSupportedStateVersions(),
387
- readBrokerDiscovery(this.settings.agentDir),
388
- ]);
494
+ await Promise.all([this.ledger.assertSupportedStateVersions(), readBrokerDiscovery(this.settings.agentDir)]);
389
495
  await fs.mkdir(path.dirname(this.#lock), { recursive: true, mode: 0o700 });
390
496
  for (;;) {
391
497
  try {
@@ -415,39 +521,47 @@ export class Broker {
415
521
  }
416
522
  await this.#reclaimStaleLock(snapshot);
417
523
  }
418
- await this.index.open();
419
- await this.ledger.open();
420
- const now = Date.now();
421
- const incarnation = brokerProcessIncarnation(process.pid);
422
- if (!incarnation) throw new Error("Broker process incarnation is unavailable.");
423
- const token = newBrokerToken();
424
- this.#transport = new BrokerTransport(this, token, this.settings.port);
425
- const port = await this.#transport.start();
426
- this.discovery = {
427
- version: 1,
428
- protocolVersion: 3,
429
- packageGeneration: this.settings.packageGeneration,
430
- ownerId: this.#owner,
431
- pid: process.pid,
432
- incarnation,
433
- host: "127.0.0.1",
434
- port,
435
- url: `ws://127.0.0.1:${port}`,
436
- token,
437
- startedAt: now,
438
- heartbeatAt: now,
439
- };
440
- await writeBrokerDiscovery(this.settings.agentDir, this.discovery);
441
- this.#heartbeatTimer = setInterval(
442
- () => void this.heartbeat(),
443
- Math.max(1, Math.floor(this.settings.heartbeatTtlMs / 3)),
444
- );
445
- return this.discovery;
524
+ try {
525
+ await this.index.open();
526
+ await this.ledger.open();
527
+ const now = Date.now();
528
+ const incarnation = brokerProcessIncarnation(process.pid);
529
+ if (!incarnation) throw new Error("Broker process incarnation is unavailable.");
530
+ const token = newBrokerToken();
531
+ this.#transport = new BrokerTransport(this, token, this.settings.port);
532
+ const port = await this.#transport.start();
533
+ this.discovery = {
534
+ version: 1,
535
+ protocolVersion: 3,
536
+ packageGeneration: this.settings.packageGeneration,
537
+ ownerId: this.#owner,
538
+ pid: process.pid,
539
+ incarnation,
540
+ host: "127.0.0.1",
541
+ port,
542
+ url: `ws://127.0.0.1:${port}`,
543
+ token,
544
+ startedAt: now,
545
+ heartbeatAt: now,
546
+ };
547
+ await writeBrokerDiscovery(this.settings.agentDir, this.discovery);
548
+ this.#heartbeatTimer = setInterval(
549
+ () => void this.heartbeat(),
550
+ Math.max(1, Math.floor(this.settings.heartbeatTtlMs / 3)),
551
+ );
552
+ return this.discovery;
553
+ } catch (error) {
554
+ await this.#transport?.stop();
555
+ this.#transport = null;
556
+ this.discovery = null;
557
+ await this.#releaseOwnedLock();
558
+ throw error;
559
+ }
446
560
  }
447
561
  get ownsDiscovery(): boolean {
448
562
  return this.discovery?.ownerId === this.#owner;
449
563
  }
450
- status(): ReturnType<typeof redactBrokerDiscovery> | null {
564
+ status(): RedactedBrokerDiscovery | null {
451
565
  return this.discovery ? redactBrokerDiscovery(this.discovery) : null;
452
566
  }
453
567
  async heartbeat(): Promise<void> {
@@ -476,15 +590,7 @@ export class Broker {
476
590
  } catch (e) {
477
591
  if ((e as NodeJS.ErrnoException).code !== "ENOENT") throw e;
478
592
  }
479
- try {
480
- const lock = await this.#readLock();
481
- if (lock?.ownerId === this.#owner) {
482
- await fs.unlink(this.#lockRecordPath());
483
- await fs.rmdir(this.#lock);
484
- }
485
- } catch (e) {
486
- if ((e as NodeJS.ErrnoException).code !== "ENOENT") throw e;
487
- }
593
+ await this.#releaseOwnedLock();
488
594
  }
489
595
  this.discovery = null;
490
596
  }
@@ -543,15 +649,21 @@ export class Broker {
543
649
  const resolveSessionId = typeof input.resolveSessionId === "string" ? input.resolveSessionId : undefined;
544
650
  const cwd = typeof input.cwd === "string" ? input.cwd : undefined;
545
651
  if (resolveSessionId && cwd) {
546
- const sessionDir = SessionManager.getDefaultSessionDir(cwd, this.settings.agentDir);
547
- const match = await resolveResumableSession(resolveSessionId, cwd, sessionDir);
652
+ const scope = await resolveManagedSessionScope({ cwd, agentDir: this.settings.agentDir });
653
+ const listed =
654
+ scope.kind === "resolved" ? await listManagedSessionCandidates({ scope: scope.scope }) : undefined;
655
+ const matches =
656
+ listed?.kind === "complete"
657
+ ? listed.owned.filter(candidate => candidate.sessionId === resolveSessionId)
658
+ : [];
659
+ const match = matches.length === 1 ? matches[0] : undefined;
548
660
  return {
549
661
  ok: true,
550
662
  result: {
551
663
  ...result,
552
664
  savedSession:
553
- match && match.session.id === resolveSessionId
554
- ? { id: match.session.id, path: match.session.path }
665
+ match && match.sessionId === resolveSessionId
666
+ ? { id: match.sessionId, path: match.path }
555
667
  : undefined,
556
668
  },
557
669
  indexSeq: result.indexSeq,
@@ -575,37 +687,53 @@ export class Broker {
575
687
  );
576
688
  await prev;
577
689
  try {
690
+ const beforeBegin = this.ledger.get(identity);
578
691
  const begun = await this.ledger.begin(identity, requestHash);
579
- if (begun.kind === "replay") return begun.entry.response as BrokerResponse;
692
+ if (begun.kind === "replay") {
693
+ const replay = begun.entry.response as BrokerResponse;
694
+ if (!(!replay.ok && replay.error.cleanup)) return replay;
695
+ const cleanup = replay.error.cleanup;
696
+ const outcome = await executeLifecycle(this, operation, input, identity, cleanup);
697
+ const response = outcome.response;
698
+ await this.ledger.transition(identity, lifecycleResponseState(response), {
699
+ response,
700
+ responseDigest: createHash("sha256").update(canonicalJson(response)).digest("hex"),
701
+ ...(outcome.durableEffects ? { durableEffects: outcome.durableEffects } : {}),
702
+ ...(outcome.startupFailure ? { startupFailure: outcome.startupFailure } : {}),
703
+ });
704
+ return response;
705
+ }
580
706
  if (begun.kind === "idempotency_conflict")
581
707
  return error("idempotency_conflict", "idempotency key was used with a different request");
582
- if (begun.kind === "terminal_uncertain")
583
- return begun.entry.response
584
- ? (begun.entry.response as BrokerResponse)
585
- : error("terminal_uncertain", "prior lifecycle operation outcome is uncertain");
586
- if (begun.kind === "in_progress") return error("broker_restarting", "lifecycle operation is in progress");
587
- const outcome = await executeLifecycle(this, operation, input, identity);
588
- const response = outcome.response;
589
- await this.ledger.transition(
590
- identity,
591
- response.ok
592
- ? "terminal_ok"
593
- : response.error.code === "terminal_uncertain"
594
- ? "terminal_uncertain"
595
- : "terminal_error",
596
- {
597
- resultSessionId:
598
- response.ok && typeof (response.result as { sessionId?: unknown } | undefined)?.sessionId === "string"
599
- ? (response.result as { sessionId: string }).sessionId
600
- : undefined,
708
+ if (begun.kind === "terminal_uncertain") {
709
+ const replay = (begun.entry.response ?? beforeBegin?.response) as BrokerResponse | undefined;
710
+ if (!replay || replay.ok || !replay.error.cleanup)
711
+ return replay ?? error("terminal_uncertain", "prior lifecycle operation outcome is uncertain");
712
+ const outcome = await executeLifecycle(this, operation, input, identity, replay.error.cleanup);
713
+ const response = outcome.response;
714
+ await this.ledger.transition(identity, lifecycleResponseState(response), {
601
715
  response,
602
716
  responseDigest: createHash("sha256").update(canonicalJson(response)).digest("hex"),
603
717
  ...(outcome.durableEffects ? { durableEffects: outcome.durableEffects } : {}),
604
718
  ...(outcome.startupFailure ? { startupFailure: outcome.startupFailure } : {}),
605
- },
606
- );
607
- const reopenedLedger = await new LifecycleLedger(this.settings.agentDir).open();
608
- const persisted = reopenedLedger.get(identity);
719
+ });
720
+ return response;
721
+ }
722
+ if (begun.kind === "in_progress") return error("broker_restarting", "lifecycle operation is in progress");
723
+ const outcome = await executeLifecycle(this, operation, input, identity);
724
+ const response = outcome.response;
725
+ await this.ledger.transition(identity, lifecycleResponseState(response), {
726
+ resultSessionId:
727
+ response.ok && typeof (response.result as { sessionId?: unknown } | undefined)?.sessionId === "string"
728
+ ? (response.result as { sessionId: string }).sessionId
729
+ : undefined,
730
+ response,
731
+ responseDigest: createHash("sha256").update(canonicalJson(response)).digest("hex"),
732
+ ...(outcome.durableEffects ? { durableEffects: outcome.durableEffects } : {}),
733
+ ...(outcome.startupFailure ? { startupFailure: outcome.startupFailure } : {}),
734
+ });
735
+ if (isCleanupPending(response)) return response;
736
+ const persisted = await this.ledger.readTerminal(identity, requestHash);
609
737
  const expectedResponseDigest = createHash("sha256").update(canonicalJson(response)).digest("hex");
610
738
  const persistenceVerified =
611
739
  persisted?.responseDigest === expectedResponseDigest &&
@@ -33,7 +33,7 @@ export function isPidAlive(pid: number): boolean {
33
33
  }
34
34
  }
35
35
  async function syncFile(file: string): Promise<void> {
36
- const handle = await fs.open(file, "r");
36
+ const handle = await fs.open(file, "r+");
37
37
  try {
38
38
  await handle.sync();
39
39
  } finally {
@@ -13,6 +13,7 @@ export interface EnsureBrokerSettings {
13
13
  }
14
14
 
15
15
  const DISCOVERY_TIMEOUT_MS = 10_000;
16
+ const FIXTURE_DISCOVERY_TIMEOUT_MS = 30_000;
16
17
  // Bounded grace windows for reaping a spawned broker on failure, mirroring the
17
18
  // owned-process teardown convention (SIGTERM -> grace -> SIGKILL -> hard cap).
18
19
  const REAP_GRACEFUL_MS = 2_000;
@@ -215,7 +216,8 @@ async function ensureBrokerOnce(settings: EnsureBrokerSettings, initiator: Ensur
215
216
  spawnError = error;
216
217
  });
217
218
  const owner = registerBrokerOwner(settings.agentDir, child);
218
- const deadline = Date.now() + DISCOVERY_TIMEOUT_MS;
219
+ const discoveryTimeoutMs = initiator === "fixture-lease" ? FIXTURE_DISCOVERY_TIMEOUT_MS : DISCOVERY_TIMEOUT_MS;
220
+ const deadline = Date.now() + discoveryTimeoutMs;
219
221
  let discoveryError: unknown;
220
222
  while (Date.now() < deadline) {
221
223
  if (spawnError || child.exitCode !== null || child.signalCode !== null) break;
@@ -0,0 +1,11 @@
1
+ const lifecycleUtf8Decoder = new TextDecoder("utf-8", { fatal: true });
2
+
3
+ /** Decodes persisted lifecycle bytes without replacement characters before JSON parsing. */
4
+ export function decodeLifecycleUtf8(bytes: Uint8Array): string {
5
+ return lifecycleUtf8Decoder.decode(bytes);
6
+ }
7
+
8
+ /** Parses persisted lifecycle JSON only after fatal UTF-8 decoding. */
9
+ export function parseLifecycleJson(bytes: Uint8Array): unknown {
10
+ return JSON.parse(decodeLifecycleUtf8(bytes));
11
+ }