@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,453 @@
1
+ import { randomUUID } from "node:crypto";
2
+ import * as fs from "node:fs";
3
+ import * as fsp from "node:fs/promises";
4
+ import * as path from "node:path";
5
+ import { applyOwnerOnlyPathSecurity, renameNoReplacePath, verifyOwnerOnlyPathSecurity } from "@gajae-code/natives";
6
+
7
+ export const MANAGED_ARTIFACT_MAX_DEPTH = 32;
8
+ export const MANAGED_ARTIFACT_MAX_FILES = 10_000;
9
+ export const MANAGED_ARTIFACT_MAX_FILE_BYTES = 64 * 1024 * 1024;
10
+ export const MANAGED_ARTIFACT_MAX_TOTAL_BYTES = 512 * 1024 * 1024;
11
+ const LOCK_LEASE_MS = 60_000;
12
+ const LOCK_HEARTBEAT_MS = 10_000;
13
+ const LOCK_WAIT_MS = 5_000;
14
+
15
+ const LOCK_STALE_RECHECK_MS = 100;
16
+
17
+ export type ManagedStorageFailure =
18
+ | "migration_busy"
19
+ | "binding_conflict"
20
+ | "binding_invalid"
21
+ | "destination_conflict"
22
+ | "source_changed"
23
+ | "unsafe_artifacts"
24
+ | "durability_failed"
25
+ | "migration_retired"
26
+ | "managed_storage_unsupported";
27
+
28
+ export interface ManagedStorageLock {
29
+ path: string;
30
+ attemptId: string;
31
+ assertOwned(): void;
32
+ release(): Promise<void>;
33
+ }
34
+
35
+ export interface ManagedFileSnapshot {
36
+ bytes: Buffer;
37
+ identity: { dev: bigint; ino: bigint; size: number; mtimeNs: bigint };
38
+ }
39
+
40
+ type NativeSecurity = { ok: true } | { ok: false; code: string };
41
+ type LockRecord = {
42
+ attemptId: string;
43
+ pid: number;
44
+ bootId?: string;
45
+ processStartId: string;
46
+ createdAt: number;
47
+ heartbeatAt: number;
48
+ leaseExpiresAt: number;
49
+ };
50
+
51
+ const PROCESS_START_ID = randomUUID();
52
+
53
+ function securityError(pathname: string, result: NativeSecurity): Error {
54
+ return new Error(
55
+ result.ok
56
+ ? `Unexpected security state for ${pathname}`
57
+ : `Owner-only security rejected ${pathname}: ${result.code}`,
58
+ );
59
+ }
60
+
61
+ function secure(pathname: string, kind: "directory" | "file"): void {
62
+ const applied = applyOwnerOnlyPathSecurity(pathname, kind) as NativeSecurity;
63
+ if (!applied.ok) throw securityError(pathname, applied);
64
+ const verified = verifyOwnerOnlyPathSecurity(pathname, kind) as NativeSecurity;
65
+ if (!verified.ok) throw securityError(pathname, verified);
66
+ }
67
+
68
+ function assertSafeDirectory(pathname: string): void {
69
+ const stat = fs.lstatSync(pathname);
70
+ if (!stat.isDirectory() || stat.isSymbolicLink()) throw new Error(`Unsafe managed directory: ${pathname}`);
71
+ }
72
+
73
+ export function shouldFsyncManagedDirectory(platform: NodeJS.Platform = process.platform): boolean {
74
+ return platform !== "win32";
75
+ }
76
+
77
+ function fsyncDirectory(pathname: string): void {
78
+ if (!shouldFsyncManagedDirectory()) return;
79
+ const fd = fs.openSync(pathname, fs.constants.O_RDONLY | fs.constants.O_DIRECTORY);
80
+ try {
81
+ fs.fsyncSync(fd);
82
+ } finally {
83
+ fs.closeSync(fd);
84
+ }
85
+ }
86
+
87
+ function bootId(): string | undefined {
88
+ try {
89
+ return fs.readFileSync("/proc/sys/kernel/random/boot_id", "utf8").trim();
90
+ } catch {
91
+ return undefined;
92
+ }
93
+ }
94
+
95
+ function identity(stat: fs.BigIntStats): ManagedFileSnapshot["identity"] {
96
+ return { dev: stat.dev, ino: stat.ino, size: Number(stat.size), mtimeNs: stat.mtimeNs };
97
+ }
98
+
99
+ function sameIdentity(left: ManagedFileSnapshot["identity"], right: ManagedFileSnapshot["identity"]): boolean {
100
+ return (
101
+ left.dev === right.dev && left.ino === right.ino && left.size === right.size && left.mtimeNs === right.mtimeNs
102
+ );
103
+ }
104
+
105
+ function parseLock(pathname: string): LockRecord | undefined {
106
+ try {
107
+ const stat = fs.lstatSync(pathname);
108
+ if (!stat.isFile() || stat.isSymbolicLink()) return undefined;
109
+ const value: unknown = JSON.parse(fs.readFileSync(pathname, "utf8"));
110
+ if (!value || typeof value !== "object") return undefined;
111
+ const record = value as Partial<LockRecord>;
112
+ return typeof record.attemptId === "string" &&
113
+ typeof record.pid === "number" &&
114
+ typeof record.processStartId === "string" &&
115
+ typeof record.leaseExpiresAt === "number" &&
116
+ typeof record.heartbeatAt === "number" &&
117
+ typeof record.createdAt === "number"
118
+ ? (record as LockRecord)
119
+ : undefined;
120
+ } catch {
121
+ return undefined;
122
+ }
123
+ }
124
+
125
+ function ownerDefinitelyGone(record: LockRecord): boolean {
126
+ if (record.bootId && bootId() && record.bootId !== bootId()) return true;
127
+ try {
128
+ process.kill(record.pid, 0);
129
+ return false;
130
+ } catch (error) {
131
+ return (error as NodeJS.ErrnoException).code === "ESRCH";
132
+ }
133
+ }
134
+
135
+ function writeLockDescriptor(fd: number, record: LockRecord): void {
136
+ const encoded = Buffer.from(`${JSON.stringify(record)}\n`);
137
+ const written = fs.writeSync(fd, encoded, 0, encoded.byteLength, 0);
138
+ if (written !== encoded.byteLength) throw new Error("durability_failed");
139
+ fs.fsyncSync(fd);
140
+ }
141
+
142
+ function sameFileIdentity(left: fs.BigIntStats, right: fs.BigIntStats): boolean {
143
+ return left.dev === right.dev && left.ino === right.ino;
144
+ }
145
+
146
+ /** Create a managed directory and fail closed unless its owner-only mode/ACL verifies. */
147
+ export function ensureManagedDirectory(pathname: string): void {
148
+ fs.mkdirSync(pathname, { recursive: true, mode: 0o700 });
149
+ assertSafeDirectory(pathname);
150
+ secure(pathname, "directory");
151
+ }
152
+
153
+ /** Captures header/hash/copy input from one no-follow descriptor and rechecks the pathname before use. */
154
+ export function captureManagedFileNoFollow(pathname: string): ManagedFileSnapshot {
155
+ const fd = fs.openSync(pathname, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);
156
+ try {
157
+ const before = fs.fstatSync(fd, { bigint: true });
158
+ if (!before.isFile() || before.nlink > 1) throw new Error("source_changed");
159
+ const bytes = Buffer.alloc(Number(before.size));
160
+ let offset = 0;
161
+ while (offset < bytes.byteLength) {
162
+ const count = fs.readSync(fd, bytes, offset, bytes.byteLength - offset, offset);
163
+ if (count === 0) throw new Error("source_changed");
164
+ offset += count;
165
+ }
166
+ const after = fs.fstatSync(fd, { bigint: true });
167
+ if (!sameIdentity(identity(before), identity(after))) throw new Error("source_changed");
168
+ const named = fs.lstatSync(pathname, { bigint: true });
169
+ if (!named.isFile() || named.isSymbolicLink() || !sameIdentity(identity(before), identity(named)))
170
+ throw new Error("source_changed");
171
+ return { bytes, identity: identity(before) };
172
+ } finally {
173
+ fs.closeSync(fd);
174
+ }
175
+ }
176
+
177
+ /** Atomically publishes bytes without replacing an existing destination. */
178
+ export async function publishManagedFileNoReplace(
179
+ destination: string,
180
+ bytes: Uint8Array,
181
+ assertOwned?: () => void,
182
+ ): Promise<void> {
183
+ const parent = path.dirname(destination);
184
+ ensureManagedDirectory(parent);
185
+ const staging = path.join(parent, `.${path.basename(destination)}.${randomUUID()}.staging`);
186
+ let fd: number | undefined;
187
+ try {
188
+ assertOwned?.();
189
+ fd = fs.openSync(
190
+ staging,
191
+ fs.constants.O_CREAT | fs.constants.O_EXCL | fs.constants.O_WRONLY | fs.constants.O_NOFOLLOW,
192
+ 0o600,
193
+ );
194
+ let offset = 0;
195
+ while (offset < bytes.byteLength) offset += fs.writeSync(fd, bytes, offset, bytes.byteLength - offset);
196
+ fs.fsyncSync(fd);
197
+ fs.closeSync(fd);
198
+ fd = undefined;
199
+ secure(staging, "file");
200
+ assertOwned?.();
201
+ const published = renameNoReplacePath(staging, destination);
202
+ if (!published.ok) {
203
+ if (published.code === "quarantine_collision") throw new Error("destination_conflict");
204
+ throw new Error("durability_failed");
205
+ }
206
+ secure(destination, "file");
207
+ fsyncDirectory(parent);
208
+ } finally {
209
+ if (fd !== undefined) fs.closeSync(fd);
210
+ await fsp.unlink(staging).catch(error => {
211
+ if ((error as NodeJS.ErrnoException).code !== "ENOENT") throw error;
212
+ });
213
+ }
214
+ }
215
+
216
+ export function publishManagedFileNoReplaceSync(destination: string, bytes: Uint8Array): void {
217
+ const parent = path.dirname(destination);
218
+ ensureManagedDirectory(parent);
219
+ const staging = path.join(parent, `.${path.basename(destination)}.${randomUUID()}.staging`);
220
+ let failure: unknown;
221
+ try {
222
+ const fd = fs.openSync(
223
+ staging,
224
+ fs.constants.O_CREAT | fs.constants.O_EXCL | fs.constants.O_WRONLY | fs.constants.O_NOFOLLOW,
225
+ 0o600,
226
+ );
227
+ try {
228
+ let offset = 0;
229
+ while (offset < bytes.byteLength) offset += fs.writeSync(fd, bytes, offset, bytes.byteLength - offset);
230
+ fs.fsyncSync(fd);
231
+ } finally {
232
+ fs.closeSync(fd);
233
+ }
234
+ secure(staging, "file");
235
+ const published = renameNoReplacePath(staging, destination);
236
+ if (!published.ok) {
237
+ if (published.code === "quarantine_collision") throw new Error("destination_conflict");
238
+ throw new Error("durability_failed");
239
+ }
240
+ secure(destination, "file");
241
+ fsyncDirectory(parent);
242
+ } catch (error) {
243
+ failure = error;
244
+ }
245
+ try {
246
+ fs.unlinkSync(staging);
247
+ } catch (error) {
248
+ if ((error as NodeJS.ErrnoException).code !== "ENOENT") failure ??= error;
249
+ }
250
+ if (failure !== undefined) throw failure;
251
+ }
252
+
253
+ /** Copy the exact bytes captured from one no-follow source descriptor. */
254
+ export async function copyManagedFileNoReplace(
255
+ source: string,
256
+ destination: string,
257
+ snapshot = captureManagedFileNoFollow(source),
258
+ ): Promise<void> {
259
+ const named = captureManagedFileNoFollow(source);
260
+ if (!sameIdentity(snapshot.identity, named.identity) || !snapshot.bytes.equals(named.bytes))
261
+ throw new Error("source_changed");
262
+ await publishManagedFileNoReplace(destination, snapshot.bytes);
263
+ const destinationSnapshot = captureManagedFileNoFollow(destination);
264
+ if (!destinationSnapshot.bytes.equals(snapshot.bytes)) throw new Error("durability_failed");
265
+ }
266
+
267
+ /** Acquire a lease lock with bounded wait, heartbeats, conservative stale reclaim, and fencing. */
268
+ export async function acquireManagedLock(locksDirectory: string, name: string): Promise<ManagedStorageLock> {
269
+ ensureManagedDirectory(locksDirectory);
270
+ const lockPath = path.join(locksDirectory, `${name}.lock`);
271
+ const deadline = Date.now() + LOCK_WAIT_MS;
272
+ let staleObservedAt: number | undefined;
273
+ while (true) {
274
+ const attemptId = randomUUID();
275
+ const now = Date.now();
276
+ const record: LockRecord = {
277
+ attemptId,
278
+ pid: process.pid,
279
+ bootId: bootId(),
280
+ processStartId: PROCESS_START_ID,
281
+ createdAt: now,
282
+ heartbeatAt: now,
283
+ leaseExpiresAt: now + LOCK_LEASE_MS,
284
+ };
285
+ try {
286
+ const fd = fs.openSync(
287
+ lockPath,
288
+ fs.constants.O_CREAT | fs.constants.O_EXCL | fs.constants.O_WRONLY | fs.constants.O_NOFOLLOW,
289
+ 0o600,
290
+ );
291
+ try {
292
+ fs.writeFileSync(fd, `${JSON.stringify(record)}\n`);
293
+ fs.fsyncSync(fd);
294
+ secure(lockPath, "file");
295
+ fsyncDirectory(locksDirectory);
296
+ } catch (error) {
297
+ fs.closeSync(fd);
298
+ throw error;
299
+ }
300
+ const lockIdentity = fs.fstatSync(fd, { bigint: true });
301
+ let released = false;
302
+ let descriptorClosed = false;
303
+ const closeDescriptor = (): void => {
304
+ if (!descriptorClosed) {
305
+ fs.closeSync(fd);
306
+ descriptorClosed = true;
307
+ }
308
+ };
309
+ const assertOwned = (): void => {
310
+ const current = parseLock(lockPath);
311
+ let named: fs.BigIntStats;
312
+ try {
313
+ named = fs.lstatSync(lockPath, { bigint: true });
314
+ } catch {
315
+ throw new Error("migration_busy");
316
+ }
317
+ if (
318
+ released ||
319
+ descriptorClosed ||
320
+ !current ||
321
+ !sameFileIdentity(lockIdentity, named) ||
322
+ current.attemptId !== attemptId ||
323
+ current.leaseExpiresAt < Date.now()
324
+ )
325
+ throw new Error("migration_busy");
326
+ };
327
+ const heartbeat = setInterval(() => {
328
+ try {
329
+ assertOwned();
330
+ const now = Date.now();
331
+ writeLockDescriptor(fd, { ...record, heartbeatAt: now, leaseExpiresAt: now + LOCK_LEASE_MS });
332
+ } catch {
333
+ /* fencing rejects later publication */
334
+ }
335
+ }, LOCK_HEARTBEAT_MS);
336
+ return {
337
+ path: lockPath,
338
+ attemptId,
339
+ assertOwned,
340
+ async release(): Promise<void> {
341
+ clearInterval(heartbeat);
342
+ try {
343
+ assertOwned();
344
+ const now = Date.now();
345
+ // Do not unlink by pathname: a stale owner could otherwise remove a successor.
346
+ // The lease is retired through the verified inode-bound descriptor instead.
347
+ writeLockDescriptor(fd, { ...record, heartbeatAt: now, leaseExpiresAt: now });
348
+ fsyncDirectory(locksDirectory);
349
+ } finally {
350
+ released = true;
351
+ closeDescriptor();
352
+ }
353
+ },
354
+ };
355
+ } catch (error) {
356
+ if ((error as NodeJS.ErrnoException).code !== "EEXIST") throw error;
357
+ const owner = parseLock(lockPath);
358
+ if (owner && owner.leaseExpiresAt < Date.now()) {
359
+ const ownerGone = ownerDefinitelyGone(owner);
360
+ if (staleObservedAt === undefined) staleObservedAt = Date.now();
361
+ if (ownerGone || Date.now() - staleObservedAt >= LOCK_STALE_RECHECK_MS) {
362
+ const quarantine = `${lockPath}.${randomUUID()}.stale`;
363
+ try {
364
+ fs.renameSync(lockPath, quarantine);
365
+ const quarantined = parseLock(quarantine);
366
+ if (!quarantined || quarantined.attemptId !== owner.attemptId) throw new Error("migration_busy");
367
+ fs.unlinkSync(quarantine);
368
+ fsyncDirectory(locksDirectory);
369
+ } catch {
370
+ /* retry owner observation */
371
+ }
372
+ staleObservedAt = undefined;
373
+ }
374
+ } else staleObservedAt = undefined;
375
+ if (Date.now() >= deadline) throw new Error("migration_busy");
376
+ await new Promise<void>(resolve => setTimeout(resolve, 50));
377
+ }
378
+ }
379
+ }
380
+
381
+ /** Bounds a no-follow artifact tree before it can be copied or deleted. */
382
+ export function validateManagedArtifactTree(root: string): void {
383
+ let files = 0;
384
+ let bytes = 0;
385
+ const visit = (directory: string, depth: number): void => {
386
+ if (depth > MANAGED_ARTIFACT_MAX_DEPTH) throw new Error("unsafe_artifacts");
387
+ for (const entry of fs.readdirSync(directory, { withFileTypes: true })) {
388
+ const entryPath = path.join(directory, entry.name);
389
+ const stat = fs.lstatSync(entryPath);
390
+ if (stat.isSymbolicLink()) throw new Error("unsafe_artifacts");
391
+ if (stat.isDirectory()) {
392
+ visit(entryPath, depth + 1);
393
+ continue;
394
+ }
395
+ if (stat.nlink > 1) throw new Error("unsafe_artifacts");
396
+ if (!stat.isFile() || stat.size > MANAGED_ARTIFACT_MAX_FILE_BYTES) throw new Error("unsafe_artifacts");
397
+ files++;
398
+ bytes += stat.size;
399
+ if (files > MANAGED_ARTIFACT_MAX_FILES || bytes > MANAGED_ARTIFACT_MAX_TOTAL_BYTES)
400
+ throw new Error("unsafe_artifacts");
401
+ }
402
+ };
403
+ const rootStat = fs.lstatSync(root);
404
+ if (!rootStat.isDirectory() || rootStat.isSymbolicLink()) throw new Error("unsafe_artifacts");
405
+ visit(root, 0);
406
+ }
407
+
408
+ /** Flush a copied managed artifact tree, including empty directories, before publishing its receipt. */
409
+ export function fsyncManagedArtifactTree(root: string): void {
410
+ const visit = (pathname: string): void => {
411
+ const stat = fs.lstatSync(pathname);
412
+ if (stat.isSymbolicLink()) throw new Error("unsafe_artifacts");
413
+ if (stat.isFile()) {
414
+ const fd = fs.openSync(pathname, fs.constants.O_RDONLY | fs.constants.O_NOFOLLOW);
415
+ try {
416
+ fs.fsyncSync(fd);
417
+ } finally {
418
+ fs.closeSync(fd);
419
+ }
420
+ return;
421
+ }
422
+ if (!stat.isDirectory()) throw new Error("unsafe_artifacts");
423
+ for (const entry of fs.readdirSync(pathname, { withFileTypes: true })) visit(path.join(pathname, entry.name));
424
+ fsyncDirectory(pathname);
425
+ };
426
+ validateManagedArtifactTree(root);
427
+ visit(root);
428
+ validateManagedArtifactTree(root);
429
+ }
430
+
431
+ export async function publishManagedTombstone(
432
+ destination: string,
433
+ record: Record<string, unknown>,
434
+ assertOwned?: () => void,
435
+ ): Promise<void> {
436
+ await publishManagedFileNoReplace(
437
+ destination,
438
+ new TextEncoder().encode(
439
+ `${JSON.stringify(record, (_key, value: unknown) => (typeof value === "bigint" ? value.toString() : value))}\n`,
440
+ ),
441
+ assertOwned,
442
+ );
443
+ }
444
+
445
+ export async function unlinkManagedFileVerified(
446
+ pathname: string,
447
+ expected: ManagedFileSnapshot["identity"],
448
+ ): Promise<void> {
449
+ const snapshot = captureManagedFileNoFollow(pathname);
450
+ if (!sameIdentity(snapshot.identity, expected)) throw new Error("source_changed");
451
+ await fsp.unlink(pathname);
452
+ fsyncDirectory(path.dirname(pathname));
453
+ }
@@ -31,6 +31,44 @@ import type { LoadedSubskillActivation } from "../extensibility/gjc-plugins";
31
31
  import type { OutputMeta } from "../tools/output-meta";
32
32
  import { formatOutputNotice } from "../tools/output-meta";
33
33
 
34
+ /**
35
+ * Encode untrusted values embedded in prompt markup. Control and bidi characters become visible
36
+ * escape sequences so they cannot alter markup structure or display. Isolated UTF-16 surrogates
37
+ * are also escaped; valid Unicode pairs remain intact.
38
+ */
39
+ export function escapePromptMetadata(value: string, options: { preserveNewlines?: boolean } = {}): string {
40
+ return value.replace(
41
+ /[&<>"\u0000-\u001f\u007f-\u009f\u061c\u200e-\u200f\u202a-\u202e\u2066-\u2069\ud800-\udfff]/g,
42
+ (char, offset) => {
43
+ const code = char.charCodeAt(0);
44
+ if (
45
+ (code >= 0xd800 &&
46
+ code <= 0xdbff &&
47
+ value.charCodeAt(offset + 1) >= 0xdc00 &&
48
+ value.charCodeAt(offset + 1) <= 0xdfff) ||
49
+ (code >= 0xdc00 &&
50
+ code <= 0xdfff &&
51
+ value.charCodeAt(offset - 1) >= 0xd800 &&
52
+ value.charCodeAt(offset - 1) <= 0xdbff)
53
+ ) {
54
+ return char;
55
+ }
56
+ if (options.preserveNewlines && (char === "\n" || char === "\t")) return char;
57
+ switch (char) {
58
+ case "&":
59
+ return "&amp;";
60
+ case "<":
61
+ return "&lt;";
62
+ case ">":
63
+ return "&gt;";
64
+ case '"':
65
+ return "&quot;";
66
+ default:
67
+ return `\\u${code.toString(16).padStart(4, "0")}`;
68
+ }
69
+ },
70
+ );
71
+ }
34
72
  export const SKILL_PROMPT_MESSAGE_TYPE = "skill-prompt";
35
73
 
36
74
  export interface SkillPromptDetails {
@@ -117,6 +155,55 @@ function getPrunedToolResultContent(message: ToolResultMessage): (TextContent |
117
155
  return [{ type: "text", text }];
118
156
  }
119
157
 
158
+ export const PRE_ADMISSION_ARTIFACT_SPILL_HEAD_BYTES = 4096;
159
+ export const PRE_ADMISSION_ARTIFACT_SPILL_TAIL_BYTES = 4096;
160
+
161
+ function utf8Prefix(text: string, maxBytes: number): string {
162
+ if (Buffer.byteLength(text, "utf-8") <= maxBytes) return text;
163
+ let bytes = 0;
164
+ let end = 0;
165
+ for (const character of text) {
166
+ const characterBytes = Buffer.byteLength(character, "utf-8");
167
+ if (bytes + characterBytes > maxBytes) break;
168
+ bytes += characterBytes;
169
+ end += character.length;
170
+ }
171
+ return text.slice(0, end);
172
+ }
173
+
174
+ function utf8Suffix(text: string, maxBytes: number): string {
175
+ if (Buffer.byteLength(text, "utf-8") <= maxBytes) return text;
176
+ let bytes = 0;
177
+ let retainedStart = text.length;
178
+ for (let index = text.length; index > 0; ) {
179
+ let characterStart = --index;
180
+ const codeUnit = text.charCodeAt(characterStart);
181
+ if (codeUnit >= 0xdc00 && codeUnit <= 0xdfff && characterStart > 0) characterStart--;
182
+ const character = text.slice(characterStart, index + 1);
183
+ const characterBytes = Buffer.byteLength(character, "utf-8");
184
+ if (bytes + characterBytes > maxBytes) break;
185
+ bytes += characterBytes;
186
+ retainedStart = characterStart;
187
+ index = characterStart;
188
+ }
189
+ return text.slice(retainedStart);
190
+ }
191
+
192
+ /**
193
+ * Build the deterministic inline receipt for a tool result saved before the
194
+ * next provider context is constructed. Byte boundaries never split UTF-8
195
+ * code points, so head/tail recovery remains readable for emoji and CJK text.
196
+ */
197
+ export function createPreAdmissionArtifactSpillPreview(fullText: string, artifactId: string, digest: string): string {
198
+ const totalBytes = Buffer.byteLength(fullText, "utf-8");
199
+ const head = utf8Prefix(fullText, PRE_ADMISSION_ARTIFACT_SPILL_HEAD_BYTES);
200
+ const tail = utf8Suffix(fullText, PRE_ADMISSION_ARTIFACT_SPILL_TAIL_BYTES);
201
+ const retainedBytes = Buffer.byteLength(head, "utf-8") + Buffer.byteLength(tail, "utf-8");
202
+ const omittedBytes = Math.max(0, totalBytes - retainedBytes);
203
+ const receipt = `[${omittedBytes} bytes omitted; sha256:${digest}; full output: artifact://${artifactId}]`;
204
+ return `${head}\n\n${receipt}\n\n${tail}`;
205
+ }
206
+
120
207
  /**
121
208
  * Message type for bash executions via the ! command.
122
209
  */
@@ -370,8 +457,10 @@ export function convertToLlm(messages: AgentMessage[]): Message[] {
370
457
  case "fileMention": {
371
458
  const fileContents = m.files
372
459
  .map(file => {
373
- const inner = file.content ? `\n${file.content}\n` : "\n";
374
- return `<file path="${file.path}">${inner}</file>`;
460
+ const inner = file.content
461
+ ? `\n${escapePromptMetadata(file.content, { preserveNewlines: true })}\n`
462
+ : "\n";
463
+ return `<file path="${escapePromptMetadata(file.path)}">${inner}</file>`;
375
464
  })
376
465
  .join("\n\n");
377
466
  const content: (TextContent | ImageContent)[] = [
@@ -0,0 +1,33 @@
1
+ import type { SessionContext, SessionEntry, SessionManager, SessionTreeNode } from "./session-manager";
2
+
3
+ /**
4
+ * Opaque capability for zero-copy session snapshots. This module is deliberately
5
+ * excluded from package exports; only trusted in-package readers may use it.
6
+ */
7
+ export const sessionManagerReadCapability: unique symbol = Symbol("SessionManager internal read capability");
8
+
9
+ export interface SessionManagerReadAccess {
10
+ getEntries(): readonly SessionEntry[];
11
+ getSessionContext(): Readonly<SessionContext>;
12
+ getTree(): SessionTreeNode[];
13
+ }
14
+
15
+ type InternalReadSessionManager = SessionManager & {
16
+ [sessionManagerReadCapability](): SessionManagerReadAccess;
17
+ };
18
+
19
+ function access(manager: SessionManager): SessionManagerReadAccess {
20
+ return (manager as InternalReadSessionManager)[sessionManagerReadCapability]();
21
+ }
22
+
23
+ export function getEntriesForInternalRead(manager: SessionManager): readonly SessionEntry[] {
24
+ return access(manager).getEntries();
25
+ }
26
+
27
+ export function getSessionContextForInternalRead(manager: SessionManager): Readonly<SessionContext> {
28
+ return access(manager).getSessionContext();
29
+ }
30
+
31
+ export function getTreeForInternalRead(manager: SessionManager): SessionTreeNode[] {
32
+ return access(manager).getTree();
33
+ }