@gajae-code/coding-agent 0.18.7 → 0.18.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (344) hide show
  1. package/CHANGELOG.md +377 -1
  2. package/dist/types/async/endpoint-id.d.ts +16 -0
  3. package/dist/types/async/job-manager.d.ts +1 -1
  4. package/dist/types/async/support.d.ts +0 -14
  5. package/dist/types/capability/mcp.d.ts +2 -0
  6. package/dist/types/capability/skill.d.ts +2 -0
  7. package/dist/types/capability/types.d.ts +2 -0
  8. package/dist/types/cli/public-command-errors.d.ts +3 -1
  9. package/dist/types/cli/setup-cli.d.ts +1 -1
  10. package/dist/types/commands/deep-interview.d.ts +3 -0
  11. package/dist/types/commands/mcp-serve.d.ts +4 -0
  12. package/dist/types/config/autorouting-tier-map.d.ts +348 -9
  13. package/dist/types/config/file-lock.d.ts +1 -0
  14. package/dist/types/config/model-bindings-applier.d.ts +2 -0
  15. package/dist/types/config/model-profile-activation.d.ts +11 -3
  16. package/dist/types/config/model-registry.d.ts +4 -9
  17. package/dist/types/config/skill-settings-defaults.d.ts +2 -0
  18. package/dist/types/cursor-delete-path.d.ts +1 -0
  19. package/dist/types/cursor-mcp-name.d.ts +8 -0
  20. package/dist/types/customization/inventory.d.ts +2 -0
  21. package/dist/types/debug/report-bundle-parent.d.ts +2 -0
  22. package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/provider/stream.d.ts +1 -0
  23. package/dist/types/defaults/gjc/extensions/grok-cli-vendor/src/provider/version-manager.d.ts +31 -0
  24. package/dist/types/defaults/gjc-skills.generated.d.ts +5 -6
  25. package/dist/types/discovery/helpers.d.ts +6 -1
  26. package/dist/types/discovery/mcp-json.d.ts +1 -0
  27. package/dist/types/eval/py/kernel.d.ts +18 -0
  28. package/dist/types/export/html/css-color.d.ts +2 -0
  29. package/dist/types/export/html/html-export-destination.d.ts +2 -0
  30. package/dist/types/export/html/template.generated.d.ts +1 -1
  31. package/dist/types/extensibility/extensions/function-hooks-internal.d.ts +5 -0
  32. package/dist/types/extensibility/extensions/types.d.ts +7 -0
  33. package/dist/types/extensibility/gjc-plugins/runtime-adapters.d.ts +14 -1
  34. package/dist/types/extensibility/plugins/marketplace/cache.d.ts +2 -0
  35. package/dist/types/extensibility/plugins/plugin-link-path.d.ts +2 -0
  36. package/dist/types/extensibility/runtime-skill-discovery.d.ts +3 -1
  37. package/dist/types/extensibility/skill-management.d.ts +4 -1
  38. package/dist/types/extensibility/skills.d.ts +2 -0
  39. package/dist/types/gjc-runtime/deep-interview-crystallize.d.ts +74 -0
  40. package/dist/types/gjc-runtime/deep-interview-runtime.d.ts +11 -0
  41. package/dist/types/gjc-runtime/gc-runtime.d.ts +5 -0
  42. package/dist/types/gjc-runtime/psmux-detect.d.ts +3 -3
  43. package/dist/types/gjc-runtime/ralplan-runtime.d.ts +0 -5
  44. package/dist/types/gjc-runtime/sidecar-bootstrap-env.d.ts +6 -0
  45. package/dist/types/gjc-runtime/state-argv.d.ts +1 -1
  46. package/dist/types/gjc-runtime/state-migrations.d.ts +8 -0
  47. package/dist/types/gjc-runtime/state-runtime.d.ts +95 -0
  48. package/dist/types/gjc-runtime/state-writer.d.ts +14 -1
  49. package/dist/types/gjc-runtime/tmux-common.d.ts +8 -5
  50. package/dist/types/gjc-runtime/ultragoal-runtime.d.ts +8 -0
  51. package/dist/types/internal-urls/skill-path.d.ts +1 -0
  52. package/dist/types/lsp/config.d.ts +8 -5
  53. package/dist/types/lsp/workspace-path.d.ts +1 -0
  54. package/dist/types/modes/acp/acp-agent.d.ts +7 -0
  55. package/dist/types/modes/components/custom-editor.d.ts +6 -0
  56. package/dist/types/modes/components/customization/customization-dashboard.d.ts +1 -0
  57. package/dist/types/modes/components/extensions/state-manager.d.ts +4 -3
  58. package/dist/types/modes/controllers/input-controller.d.ts +9 -0
  59. package/dist/types/modes/interactive-mode.d.ts +1 -0
  60. package/dist/types/modes/utils/interrupt-ownership.d.ts +3 -0
  61. package/dist/types/modes/utils/keybinding-matchers.d.ts +1 -0
  62. package/dist/types/modes/utils/macos-option-forwarding.d.ts +17 -0
  63. package/dist/types/progress/collect-project-progress.d.ts +38 -0
  64. package/dist/types/progress/progress-contract.d.ts +154 -0
  65. package/dist/types/progress/progress-snapshot.d.ts +11 -0
  66. package/dist/types/progress/project-progress.d.ts +141 -0
  67. package/dist/types/progress/render-progress.d.ts +28 -0
  68. package/dist/types/runtime-mcp/manager.d.ts +2 -1
  69. package/dist/types/runtime-mcp/mcp-redirect.d.ts +9 -0
  70. package/dist/types/runtime-mcp/oauth-public-url.d.ts +3 -0
  71. package/dist/types/runtime-mcp/plugin-network-boundary.d.ts +1 -0
  72. package/dist/types/runtime-mcp/project-host-values.d.ts +27 -0
  73. package/dist/types/runtime-mcp/smithery-registry.d.ts +24 -0
  74. package/dist/types/runtime-mcp/types.d.ts +6 -0
  75. package/dist/types/sdk/acp/adapter.d.ts +1 -1
  76. package/dist/types/sdk/broker/broker-exit.d.ts +6 -2
  77. package/dist/types/sdk/broker/broker.d.ts +24 -2
  78. package/dist/types/sdk/broker/doctor-restart.d.ts +5 -0
  79. package/dist/types/sdk/broker/ensure.d.ts +14 -1
  80. package/dist/types/sdk/broker/session-index.d.ts +12 -6
  81. package/dist/types/sdk/bus/index.d.ts +12 -1
  82. package/dist/types/sdk/bus/telegram-daemon-contract.d.ts +6 -1
  83. package/dist/types/sdk/client/client.d.ts +2 -0
  84. package/dist/types/sdk/host/query/handlers.d.ts +3 -0
  85. package/dist/types/sdk/index.d.ts +1 -0
  86. package/dist/types/sdk/router/session-router.d.ts +2 -0
  87. package/dist/types/sdk/session-list.d.ts +1 -1
  88. package/dist/types/sdk/session.d.ts +8 -0
  89. package/dist/types/session/agent-session.d.ts +38 -9
  90. package/dist/types/session/artifacts.d.ts +2 -2
  91. package/dist/types/session/internal/managed-gc-retirement-codec.d.ts +32 -0
  92. package/dist/types/session/internal/managed-session-scope.d.ts +79 -2
  93. package/dist/types/session/internal/managed-session-storage.d.ts +26 -1
  94. package/dist/types/session/internal/managed-task-owner-cleanup.d.ts +62 -0
  95. package/dist/types/session/internal/task-artifact-owner-access.d.ts +2 -0
  96. package/dist/types/session/internal/task-artifact-owner-transcript.d.ts +9 -1
  97. package/dist/types/session/session-manager.d.ts +31 -5
  98. package/dist/types/session/session-retirement.d.ts +31 -7
  99. package/dist/types/session/session-storage.d.ts +69 -2
  100. package/dist/types/session/task-artifact-owner-codec.d.ts +2 -0
  101. package/dist/types/setup/credential-auto-import.d.ts +1 -1
  102. package/dist/types/setup/credential-import.d.ts +6 -4
  103. package/dist/types/skill-state/active-state.d.ts +38 -0
  104. package/dist/types/skill-state/workflow-mutation-guard.d.ts +3 -0
  105. package/dist/types/slash-commands/helpers/progress-report.d.ts +8 -0
  106. package/dist/types/task/executor.d.ts +3 -0
  107. package/dist/types/task/isolation-dir.d.ts +26 -0
  108. package/dist/types/tools/ast-edit-write-path.d.ts +13 -0
  109. package/dist/types/tools/bash-skill-urls.d.ts +1 -1
  110. package/dist/types/tools/browser/cdp-url.d.ts +2 -0
  111. package/dist/types/tools/github-side-effect.d.ts +22 -0
  112. package/dist/types/tools/index.d.ts +3 -0
  113. package/dist/types/tools/monitor-mutation.d.ts +9 -0
  114. package/dist/types/tools/python.d.ts +9 -1
  115. package/dist/types/tools/recipe/recipe-guard.d.ts +7 -0
  116. package/dist/types/tools/restricted-cwd.d.ts +1 -0
  117. package/dist/types/tools/todo-write.d.ts +6 -0
  118. package/dist/types/utils/tools-checksum.d.ts +7 -0
  119. package/package.json +7 -7
  120. package/scripts/generate-sdk-operation-inventory.ts +21 -0
  121. package/scripts/verify-managed-empty-stop.ts +28 -0
  122. package/src/async/endpoint-id.ts +26 -0
  123. package/src/async/job-manager.ts +247 -132
  124. package/src/async/support.ts +0 -25
  125. package/src/capability/index.ts +15 -0
  126. package/src/capability/mcp.ts +2 -0
  127. package/src/capability/skill.ts +2 -0
  128. package/src/capability/types.ts +2 -0
  129. package/src/cli/auth-broker-cli.ts +1 -1
  130. package/src/cli/customize-doctor.ts +15 -2
  131. package/src/cli/doctor/repairs.ts +4 -1
  132. package/src/cli/doctor-supervisor.ts +3 -0
  133. package/src/cli/public-command-errors.ts +21 -1
  134. package/src/cli/public-command-registry.ts +28 -6
  135. package/src/cli/setup-cli.ts +4 -4
  136. package/src/cli/skills-cli.ts +5 -0
  137. package/src/commands/deep-interview.ts +47 -8
  138. package/src/commands/mcp-serve.ts +7 -0
  139. package/src/commands/sdk.ts +455 -397
  140. package/src/config/autorouting-tier-map.ts +117 -7
  141. package/src/config/file-lock.ts +316 -79
  142. package/src/config/mcp-schema.json +4 -0
  143. package/src/config/model-bindings-applier.ts +20 -0
  144. package/src/config/model-profile-activation.ts +240 -46
  145. package/src/config/model-registry.ts +47 -153
  146. package/src/config/skill-settings-defaults.ts +7 -0
  147. package/src/coordinator-mcp/policy.ts +3 -2
  148. package/src/coordinator-mcp/server.ts +2 -0
  149. package/src/cursor-delete-path.ts +10 -0
  150. package/src/cursor-mcp-name.ts +15 -0
  151. package/src/cursor.ts +13 -2
  152. package/src/customization/inventory.ts +4 -13
  153. package/src/dap/config.ts +2 -2
  154. package/src/debug/report-bundle-parent.ts +22 -0
  155. package/src/debug/report-bundle.ts +3 -0
  156. package/src/debug/system-info.ts +13 -1
  157. package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/register.ts +10 -6
  158. package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/stream.ts +67 -4
  159. package/src/defaults/gjc/extensions/grok-cli-vendor/src/provider/version-manager.ts +80 -0
  160. package/src/defaults/gjc/skills/deep-interview/SKILL.md +105 -10
  161. package/src/defaults/gjc/skills/ralplan/SKILL.md +6 -4
  162. package/src/defaults/gjc-defaults.test.ts +18 -1
  163. package/src/defaults/gjc-defaults.ts +3 -0
  164. package/src/defaults/gjc-skills.generated.ts +58 -36
  165. package/src/discovery/agents.ts +6 -1
  166. package/src/discovery/builtin.ts +13 -1
  167. package/src/discovery/helpers.ts +171 -29
  168. package/src/discovery/mcp-json.ts +26 -12
  169. package/src/discovery/ssh.ts +1 -1
  170. package/src/discovery/substitute-plugin-root.ts +3 -1
  171. package/src/edit/file-read-cache.ts +1 -1
  172. package/src/eval/py/executor.ts +796 -154
  173. package/src/eval/py/kernel.ts +79 -13
  174. package/src/eval/py/tool-bridge.ts +4 -0
  175. package/src/export/html/css-color.ts +9 -0
  176. package/src/export/html/html-export-destination.ts +16 -0
  177. package/src/export/html/index.ts +7 -10
  178. package/src/export/html/template.generated.ts +1 -1
  179. package/src/extensibility/extensions/function-hooks-internal.ts +27 -0
  180. package/src/extensibility/extensions/runner.ts +15 -1
  181. package/src/extensibility/extensions/types.ts +7 -0
  182. package/src/extensibility/gjc-plugins/mcp-policy.ts +2 -1
  183. package/src/extensibility/gjc-plugins/runtime-adapters.ts +85 -8
  184. package/src/extensibility/plugins/manager.ts +3 -2
  185. package/src/extensibility/plugins/marketplace/cache.ts +15 -0
  186. package/src/extensibility/plugins/marketplace/manager.ts +11 -3
  187. package/src/extensibility/plugins/plugin-link-path.ts +12 -0
  188. package/src/extensibility/runtime-skill-discovery.ts +20 -2
  189. package/src/extensibility/skill-management.ts +9 -2
  190. package/src/extensibility/skills.ts +6 -1
  191. package/src/gjc-runtime/deep-interview-crystallize.ts +1814 -0
  192. package/src/gjc-runtime/deep-interview-recorder.ts +20 -2
  193. package/src/gjc-runtime/deep-interview-runtime.ts +1415 -13
  194. package/src/gjc-runtime/deep-interview-stage.ts +98 -21
  195. package/src/gjc-runtime/gc-runtime.ts +206 -9
  196. package/src/gjc-runtime/launch-worktree.ts +70 -9
  197. package/src/gjc-runtime/psmux-detect.ts +28 -4
  198. package/src/gjc-runtime/ralplan-runtime.ts +447 -52
  199. package/src/gjc-runtime/session-state-sidecar.ts +3 -2
  200. package/src/gjc-runtime/sidecar-bootstrap-env.ts +19 -0
  201. package/src/gjc-runtime/state-argv.ts +2 -0
  202. package/src/gjc-runtime/state-migrations.ts +91 -29
  203. package/src/gjc-runtime/state-runtime.ts +3716 -135
  204. package/src/gjc-runtime/state-writer.ts +105 -16
  205. package/src/gjc-runtime/tmux-common.ts +8 -5
  206. package/src/gjc-runtime/ultragoal-runtime.ts +16 -4
  207. package/src/gjc-runtime/workflow-manifest.generated.json +70 -42
  208. package/src/gjc-runtime/workflow-manifest.ts +54 -21
  209. package/src/harness-control-plane/operate.ts +12 -4
  210. package/src/harness-control-plane/owner.ts +5 -1
  211. package/src/hashline/parser.ts +48 -9
  212. package/src/hashline/recovery.ts +13 -4
  213. package/src/internal-urls/docs-index.generated.ts +20 -19
  214. package/src/internal-urls/skill-path.ts +19 -0
  215. package/src/internal-urls/skill-protocol.ts +2 -0
  216. package/src/lsp/config.ts +12 -63
  217. package/src/lsp/edits.ts +6 -0
  218. package/src/lsp/workspace-path.ts +51 -0
  219. package/src/main.ts +195 -31
  220. package/src/memories/index.ts +88 -7
  221. package/src/modes/acp/acp-agent.ts +88 -4
  222. package/src/modes/components/custom-editor.ts +14 -0
  223. package/src/modes/components/customization/customization-dashboard.ts +12 -0
  224. package/src/modes/components/extensions/extension-dashboard.ts +2 -2
  225. package/src/modes/components/extensions/state-manager.ts +15 -5
  226. package/src/modes/components/hook-editor.ts +1 -1
  227. package/src/modes/components/hook-selector.ts +1 -1
  228. package/src/modes/components/model-selector.ts +1 -4
  229. package/src/modes/components/provider-onboarding-selector.ts +1 -1
  230. package/src/modes/components/status-line/segments.ts +4 -0
  231. package/src/modes/controllers/event-controller.ts +11 -4
  232. package/src/modes/controllers/input-controller.ts +76 -31
  233. package/src/modes/controllers/runtime-mcp-command-controller.ts +29 -16
  234. package/src/modes/controllers/selector-controller.ts +24 -15
  235. package/src/modes/controllers/todo-command-controller.ts +1 -1
  236. package/src/modes/interactive-mode.ts +5 -1
  237. package/src/modes/runtime-init.ts +4 -0
  238. package/src/modes/utils/abort-message.ts +3 -1
  239. package/src/modes/utils/interrupt-ownership.ts +12 -0
  240. package/src/modes/utils/keybinding-matchers.ts +4 -0
  241. package/src/modes/utils/macos-option-forwarding.ts +80 -0
  242. package/src/progress/collect-project-progress.ts +153 -0
  243. package/src/progress/progress-contract.ts +172 -0
  244. package/src/progress/progress-snapshot.ts +140 -0
  245. package/src/progress/project-progress.ts +446 -0
  246. package/src/progress/render-progress.ts +209 -0
  247. package/src/prompts/system/master-mode.md +1 -0
  248. package/src/prompts/tools/skill-discovery.md +1 -1
  249. package/src/runtime-mcp/client.ts +4 -2
  250. package/src/runtime-mcp/config-writer.ts +10 -6
  251. package/src/runtime-mcp/config.ts +1 -0
  252. package/src/runtime-mcp/json-rpc.ts +2 -1
  253. package/src/runtime-mcp/manager.ts +29 -11
  254. package/src/runtime-mcp/mcp-redirect.ts +73 -0
  255. package/src/runtime-mcp/oauth-flow.ts +14 -2
  256. package/src/runtime-mcp/oauth-public-url.ts +14 -0
  257. package/src/runtime-mcp/plugin-network-boundary.ts +4 -0
  258. package/src/runtime-mcp/pool.ts +6 -0
  259. package/src/runtime-mcp/project-host-values.ts +46 -0
  260. package/src/runtime-mcp/smithery-registry.ts +22 -3
  261. package/src/runtime-mcp/transports/http.ts +4 -3
  262. package/src/runtime-mcp/types.ts +6 -0
  263. package/src/sdk/acp/adapter.ts +95 -8
  264. package/src/sdk/broker/broker-exit.ts +52 -23
  265. package/src/sdk/broker/broker.ts +81 -6
  266. package/src/sdk/broker/doctor-restart.ts +4 -0
  267. package/src/sdk/broker/ensure.ts +31 -2
  268. package/src/sdk/broker/lifecycle.ts +711 -66
  269. package/src/sdk/broker/session-index.ts +191 -81
  270. package/src/sdk/bus/chat-daemon-cli.ts +38 -1
  271. package/src/sdk/bus/chat-daemon-control.ts +2 -2
  272. package/src/sdk/bus/index.ts +238 -43
  273. package/src/sdk/bus/telegram-daemon-contract.ts +6 -1
  274. package/src/sdk/cli/session-cli.ts +17 -3
  275. package/src/sdk/client/client.ts +28 -8
  276. package/src/sdk/host/query/handlers.ts +9 -1
  277. package/src/sdk/host/session-runtime.test.ts +2705 -303
  278. package/src/sdk/host/session-runtime.ts +490 -143
  279. package/src/sdk/host/surface-policy.ts +1 -0
  280. package/src/sdk/index.ts +8 -0
  281. package/src/sdk/lifecycle-session.ts +3 -1
  282. package/src/sdk/prompt-deadline-manager.ts +15 -0
  283. package/src/sdk/protocol/operation-inventory.generated.json +146 -0
  284. package/src/sdk/protocol/operation-registry.ts +9 -2
  285. package/src/sdk/router/session-router.ts +82 -18
  286. package/src/sdk/session-list.ts +1 -0
  287. package/src/sdk/session.ts +69 -45
  288. package/src/session/agent-session.ts +679 -238
  289. package/src/session/artifacts.ts +176 -71
  290. package/src/session/internal/managed-gc-retirement-codec.ts +319 -0
  291. package/src/session/internal/managed-session-scope.ts +3779 -462
  292. package/src/session/internal/managed-session-storage.ts +501 -78
  293. package/src/session/internal/managed-task-owner-cleanup.ts +473 -0
  294. package/src/session/internal/task-artifact-owner-access.ts +19 -3
  295. package/src/session/internal/task-artifact-owner-transcript.ts +547 -1
  296. package/src/session/session-manager.ts +951 -133
  297. package/src/session/session-retirement.ts +573 -14
  298. package/src/session/session-storage.ts +447 -15
  299. package/src/session/task-artifact-owner-codec.ts +71 -8
  300. package/src/session/task-artifact-owner-retirement.ts +21 -19
  301. package/src/setup/credential-auto-import.ts +12 -3
  302. package/src/setup/credential-import.ts +176 -5
  303. package/src/setup/paseo/announce.ts +6 -1
  304. package/src/skill-state/active-state.ts +145 -2
  305. package/src/skill-state/workflow-mutation-guard.ts +102 -10
  306. package/src/slash-commands/builtin-registry.ts +39 -9
  307. package/src/slash-commands/helpers/mcp.ts +8 -4
  308. package/src/slash-commands/helpers/progress-report.ts +23 -0
  309. package/src/stt/setup.ts +3 -5
  310. package/src/task/commands.ts +3 -1
  311. package/src/task/executor.ts +4 -0
  312. package/src/task/index.ts +6 -0
  313. package/src/task/isolation-dir.ts +143 -0
  314. package/src/task/worktree.ts +16 -7
  315. package/src/tools/ask.ts +205 -6
  316. package/src/tools/ast-edit-write-path.ts +68 -0
  317. package/src/tools/ast-edit.ts +10 -1
  318. package/src/tools/bash-allowed-prefixes.ts +15 -0
  319. package/src/tools/bash-skill-urls.ts +56 -3
  320. package/src/tools/bash.ts +48 -79
  321. package/src/tools/browser/cdp-url.ts +18 -0
  322. package/src/tools/browser.ts +8 -1
  323. package/src/tools/descriptors.ts +5 -1
  324. package/src/tools/eval.ts +56 -36
  325. package/src/tools/gh.ts +10 -0
  326. package/src/tools/github-side-effect.ts +112 -0
  327. package/src/tools/image-gen.ts +1 -1
  328. package/src/tools/index.ts +3 -0
  329. package/src/tools/monitor-mutation.ts +27 -0
  330. package/src/tools/monitor.ts +7 -0
  331. package/src/tools/python.ts +188 -66
  332. package/src/tools/recipe/index.ts +9 -0
  333. package/src/tools/recipe/recipe-guard.ts +19 -0
  334. package/src/tools/restricted-cwd.ts +10 -0
  335. package/src/tools/skill-discovery.ts +5 -0
  336. package/src/tools/skill.ts +28 -1
  337. package/src/tools/todo-write.ts +10 -1
  338. package/src/tools/tool-catalog.generated.ts +1 -1
  339. package/src/tools/write.ts +1 -0
  340. package/src/utils/external-editor.ts +3 -2
  341. package/src/utils/tools-checksum.ts +34 -0
  342. package/src/utils/tools-manager.ts +24 -6
  343. package/src/web/insane/url-guard.ts +2 -1
  344. package/src/web/search/providers/codex.ts +3 -2
@@ -2,6 +2,7 @@ import { createHash, randomUUID } from "node:crypto";
2
2
 
3
3
  import * as fs from "node:fs";
4
4
  import * as path from "node:path";
5
+ import * as util from "node:util";
5
6
 
6
7
  import type * as native from "@gajae-code/natives";
7
8
 
@@ -15,6 +16,7 @@ function nativeSessionStorage(): typeof import("@gajae-code/natives") {
15
16
  }
16
17
 
17
18
  import { isEnoent, pathIsWithin, peekFile, toError } from "@gajae-code/utils";
19
+ import type { ManagedGcProtocolScopeInspector } from "./internal/managed-session-scope";
18
20
  import {
19
21
  assertManagedDirectoryRoot,
20
22
  type ManagedDirectoryRoot,
@@ -28,7 +30,26 @@ import {
28
30
  type NativePublishOutcome,
29
31
  } from "./internal/native-publish-outcome";
30
32
  import { isDerivedSessionMemoryFile } from "./internal/session-memory-sidecar";
33
+ import { captureTaskArtifactOwnerDeletionEvidence } from "./internal/task-artifact-owner-access";
34
+ import {
35
+ hasSiblingTaskArtifactOwnerTranscript,
36
+ taskArtifactOwnerLocatorFromTranscriptBytes,
37
+ } from "./internal/task-artifact-owner-transcript";
31
38
  import { parseFirstJsonlLine } from "./session-transcript-header";
39
+ import {
40
+ parseTaskArtifactOwnerDeletionEvidence,
41
+ parseTaskArtifactOwnerRetirementOutcome,
42
+ type TaskArtifactOwnerDeletionEvidence,
43
+ type TaskArtifactOwnerLocator,
44
+ type TaskArtifactOwnerRetirementContinuation,
45
+ type TaskArtifactOwnerRetirementOutcome,
46
+ type TaskArtifactOwnerStorageContext,
47
+ } from "./task-artifact-owner-codec";
48
+ import {
49
+ retireTaskArtifactOwner,
50
+ verifyTaskArtifactOwnerPhysicalRetirement,
51
+ verifyTaskArtifactOwnerRetirementContinuation,
52
+ } from "./task-artifact-owner-retirement";
32
53
 
33
54
  const utf8Decoder = new TextDecoder("utf-8");
34
55
  const newlineBuffer = Buffer.from("\n", "utf8");
@@ -443,8 +464,24 @@ export interface VerifiedSessionDeleteTarget {
443
464
  retainedTranscriptSuccessorPath?: string;
444
465
  /** Native-retained exchange placeholder observed during transcript cleanup. */
445
466
  retainedTranscriptPlaceholderPath?: string;
446
- /** Native-retained transcript entry whose identity could not be verified. */
467
+ /** Native-retained entry whose identity could not be verified. */
447
468
  retainedTranscriptUnknownPath?: string;
469
+ /** Trusted managed authority for the path-free persisted task-artifact locator. */
470
+ taskArtifactOwnerStorageContext?: TaskArtifactOwnerStorageContext;
471
+ /** Exact owner-tree evidence captured before deletion and retained across retries. */
472
+ taskArtifactOwnerDeletionEvidence?: TaskArtifactOwnerDeletionEvidence;
473
+ /** Owning journal persists the full native disposition across delete effects. */
474
+ taskArtifactOwnerRetirementOutcome?: TaskArtifactOwnerRetirementOutcome;
475
+ /** Native terminal evidence of logical transcript deletion; pathname absence alone is insufficient. */
476
+ taskArtifactOwnerTranscriptDeleted?: true;
477
+ /** Explicitly leave owner retirement to its owning coordinator. */
478
+ deferTaskArtifactOwnerRetirement?: true;
479
+ /** Durable evidence that exact owner retirement completed. */
480
+ taskArtifactOwnerRetired?: true;
481
+ /** Exact native remnant, separate from immutable original authority. */
482
+ taskArtifactOwnerRetirementContinuation?: TaskArtifactOwnerRetirementContinuation;
483
+ taskArtifactOwnerPayloadRetired?: true;
484
+ taskArtifactOwnerNamespaceRetained?: true;
448
485
  /** Native-retained publisher successor observed during cleanup. */
449
486
  retainedArtifactsSuccessorPath?: string;
450
487
  /** Native-retained exchange placeholder observed during cleanup. */
@@ -467,8 +504,28 @@ export interface VerifiedSessionDeleteTarget {
467
504
  * `deleted` and never `{}`.
468
505
  */
469
506
  export type VerifiedSessionDeleteResult =
470
- | { kind: "artifacts_removed"; phase: "artifacts"; transcriptIdentity: SessionStorageFileIdentity }
471
- | { kind: "deleted" }
507
+ | {
508
+ kind: "artifacts_removed";
509
+ phase: "artifacts";
510
+ transcriptIdentity: SessionStorageFileIdentity;
511
+ taskArtifactOwnerDeletionEvidence?: TaskArtifactOwnerDeletionEvidence;
512
+ taskArtifactOwnerRetirementOutcome?: TaskArtifactOwnerRetirementOutcome;
513
+ taskArtifactOwnerTranscriptDeleted?: true;
514
+ taskArtifactOwnerRetired?: true;
515
+ taskArtifactOwnerRetirementContinuation?: TaskArtifactOwnerRetirementContinuation;
516
+ taskArtifactOwnerPayloadRetired?: true;
517
+ taskArtifactOwnerNamespaceRetained?: true;
518
+ }
519
+ | {
520
+ kind: "deleted";
521
+ taskArtifactOwnerDeletionEvidence?: TaskArtifactOwnerDeletionEvidence;
522
+ taskArtifactOwnerRetirementOutcome?: TaskArtifactOwnerRetirementOutcome;
523
+ taskArtifactOwnerTranscriptDeleted?: true;
524
+ taskArtifactOwnerRetired?: true;
525
+ taskArtifactOwnerRetirementContinuation?: TaskArtifactOwnerRetirementContinuation;
526
+ taskArtifactOwnerPayloadRetired?: true;
527
+ taskArtifactOwnerNamespaceRetained?: true;
528
+ }
472
529
  | {
473
530
  kind: "cleanup_pending";
474
531
  phase: "artifacts";
@@ -485,6 +542,28 @@ export type VerifiedSessionDeleteResult =
485
542
  retainedSuccessorPath?: string;
486
543
  retainedPlaceholderPath?: string;
487
544
  retainedUnknownPath?: string;
545
+ taskArtifactOwnerDeletionEvidence?: TaskArtifactOwnerDeletionEvidence;
546
+ taskArtifactOwnerRetirementOutcome?: TaskArtifactOwnerRetirementOutcome;
547
+ taskArtifactOwnerTranscriptDeleted?: true;
548
+ taskArtifactOwnerRetired?: true;
549
+ taskArtifactOwnerRetirementContinuation?: TaskArtifactOwnerRetirementContinuation;
550
+ taskArtifactOwnerPayloadRetired?: true;
551
+ taskArtifactOwnerNamespaceRetained?: true;
552
+ }
553
+ | {
554
+ kind: "cleanup_pending";
555
+ phase: "task_artifact_owner";
556
+ /** True only after verified artifact completion, never inferred from the failure phase. */
557
+ artifactsRemoved: boolean;
558
+ error: Error;
559
+ transcriptIdentity: SessionStorageFileIdentity;
560
+ taskArtifactOwnerDeletionEvidence: TaskArtifactOwnerDeletionEvidence;
561
+ taskArtifactOwnerRetirementOutcome?: TaskArtifactOwnerRetirementOutcome;
562
+ taskArtifactOwnerTranscriptDeleted?: true;
563
+ taskArtifactOwnerRetired?: true;
564
+ taskArtifactOwnerRetirementContinuation?: TaskArtifactOwnerRetirementContinuation;
565
+ taskArtifactOwnerPayloadRetired?: true;
566
+ taskArtifactOwnerNamespaceRetained?: true;
488
567
  }
489
568
  | {
490
569
  kind: "cleanup_pending";
@@ -498,6 +577,13 @@ export type VerifiedSessionDeleteResult =
498
577
  retainedSuccessorPath?: string;
499
578
  retainedPlaceholderPath?: string;
500
579
  retainedUnknownPath?: string;
580
+ taskArtifactOwnerDeletionEvidence?: TaskArtifactOwnerDeletionEvidence;
581
+ taskArtifactOwnerRetirementOutcome?: TaskArtifactOwnerRetirementOutcome;
582
+ taskArtifactOwnerTranscriptDeleted?: true;
583
+ taskArtifactOwnerRetired?: true;
584
+ taskArtifactOwnerRetirementContinuation?: TaskArtifactOwnerRetirementContinuation;
585
+ taskArtifactOwnerPayloadRetired?: true;
586
+ taskArtifactOwnerNamespaceRetained?: true;
501
587
  };
502
588
 
503
589
  /** Default OS-close dispatcher: a direct `fs.closeSync`. */
@@ -736,6 +822,34 @@ function secureOwnerOnlyFileDescriptor(
736
822
  if (!result.ok) throw new Error(`Owner-only security rejected ${pathname}: ${result.code}`);
737
823
  }
738
824
 
825
+ /**
826
+ * Replace an explicit session file. Creation mode `0600` does not install a
827
+ * Windows owner-only DACL, so this applies and verifies the same native path a
828
+ * primary transcript uses when it has no managed security context. POSIX mode
829
+ * stays `0600`.
830
+ */
831
+ export function writeExplicitOwnerOnlyTextSync(pathname: string, content: string): void {
832
+ const fd = fs.openSync(
833
+ pathname,
834
+ fs.constants.O_WRONLY | fs.constants.O_CREAT | (fs.constants.O_NOFOLLOW ?? 0),
835
+ 0o600,
836
+ );
837
+ try {
838
+ secureOwnerOnlyFileDescriptor(pathname, fd, "apply", undefined);
839
+ fs.ftruncateSync(fd, 0);
840
+ const bytes = Buffer.from(content, "utf8");
841
+ let offset = 0;
842
+ while (offset < bytes.byteLength) {
843
+ const written = fs.writeSync(fd, bytes, offset, bytes.byteLength - offset);
844
+ if (written === 0) throw new Error("Short write");
845
+ offset += written;
846
+ }
847
+ secureOwnerOnlyFileDescriptor(pathname, fd, "verify", undefined);
848
+ } finally {
849
+ fs.closeSync(fd);
850
+ }
851
+ }
852
+
739
853
  /** Reject a symlink/junction/reparse component before a storage path is created or opened. */
740
854
  function assertNoReparsePath(pathname: string): void {
741
855
  const resolved = path.resolve(pathname);
@@ -1987,7 +2101,8 @@ export class FileSessionStorage implements SessionStorage {
1987
2101
 
1988
2102
  writeTextSync(fpath: string, content: string): void {
1989
2103
  this.ensureDirSync(path.dirname(fpath));
1990
- fs.writeFileSync(fpath, content);
2104
+ // New fork and branch transcripts are created here.
2105
+ writeExplicitOwnerOnlyTextSync(fpath, content);
1991
2106
  }
1992
2107
 
1993
2108
  readTextSync(fpath: string): string {
@@ -2265,7 +2380,10 @@ export class FileSessionStorage implements SessionStorage {
2265
2380
  * revalidate, transcript last. Partial deletion returns typed cleanup_pending
2266
2381
  * evidence; identity/symlink/containment/header/cwd mismatch throws.
2267
2382
  */
2268
- async deleteSessionVerified(target: VerifiedSessionDeleteTarget): Promise<VerifiedSessionDeleteResult> {
2383
+ async deleteSessionVerified(
2384
+ target: VerifiedSessionDeleteTarget,
2385
+ inspectProtocol?: ManagedGcProtocolScopeInspector,
2386
+ ): Promise<VerifiedSessionDeleteResult> {
2269
2387
  const {
2270
2388
  sessionsRoot,
2271
2389
  transcriptPath,
@@ -2287,6 +2405,15 @@ export class FileSessionStorage implements SessionStorage {
2287
2405
  plannedArtifactsPath,
2288
2406
  plannedTranscriptPath,
2289
2407
  artifactsRemoved,
2408
+ taskArtifactOwnerStorageContext,
2409
+ taskArtifactOwnerDeletionEvidence: ownerEvidenceValue,
2410
+ deferTaskArtifactOwnerRetirement,
2411
+ taskArtifactOwnerRetirementOutcome: ownerOutcomeValue,
2412
+ taskArtifactOwnerTranscriptDeleted: ownerTranscriptDeletedValue,
2413
+ taskArtifactOwnerRetired: ownerRetiredValue,
2414
+ taskArtifactOwnerRetirementContinuation: ownerContinuationValue,
2415
+ taskArtifactOwnerPayloadRetired: ownerPayloadRetiredValue,
2416
+ taskArtifactOwnerNamespaceRetained: ownerNamespaceRetainedValue,
2290
2417
  } = target;
2291
2418
  try {
2292
2419
  assertNoReparsePath(sessionsRoot);
@@ -2302,6 +2429,16 @@ export class FileSessionStorage implements SessionStorage {
2302
2429
  if (!pathIsWithin(sessionsRoot, transcriptPath)) {
2303
2430
  throw new SessionDeleteVerificationError("containment", "Transcript is outside the sessions root");
2304
2431
  }
2432
+ if (
2433
+ taskArtifactOwnerStorageContext &&
2434
+ path.resolve(taskArtifactOwnerStorageContext.sessionsRoot) !== path.resolve(sessionsRoot)
2435
+ )
2436
+ throw new SessionDeleteVerificationError("artifacts", "task_artifact_owner_root_mismatch");
2437
+ if (
2438
+ (ownerTranscriptDeletedValue !== undefined && ownerTranscriptDeletedValue !== true) ||
2439
+ (deferTaskArtifactOwnerRetirement !== undefined && deferTaskArtifactOwnerRetirement !== true)
2440
+ )
2441
+ throw new SessionDeleteVerificationError("artifacts", "task_artifact_owner_flag_invalid");
2305
2442
  if (
2306
2443
  !plannedArtifactsPath ||
2307
2444
  !plannedTranscriptPath ||
@@ -2355,6 +2492,243 @@ export class FileSessionStorage implements SessionStorage {
2355
2492
  "Transcript parent identity does not match authorization",
2356
2493
  );
2357
2494
  const authorizedTranscriptParentIdentity = transcriptParentIdentity ?? parentIdentity;
2495
+ let initial: { snapshot: SessionStorageSnapshot } | undefined;
2496
+ if (ownerTranscriptDeletedValue === true && fs.existsSync(transcriptPath))
2497
+ throw new SessionDeleteVerificationError("artifacts", "task_artifact_owner_transcript_deleted_mismatch");
2498
+ if (!hasDetachedTranscript && ownerTranscriptDeletedValue !== true) {
2499
+ try {
2500
+ initial = this.#verifiedReadAndHeader(transcriptPath, sessionId, cwd);
2501
+ } catch (error) {
2502
+ const absent =
2503
+ error instanceof SessionDeleteVerificationError &&
2504
+ error.kind === "stat" &&
2505
+ (error.cause as NodeJS.ErrnoException | undefined)?.code === "ENOENT";
2506
+ if (!absent || !detachedArtifactsPath || artifactsRemoved) throw error;
2507
+ }
2508
+ }
2509
+ const ownerTranscript =
2510
+ ownerTranscriptDeletedValue === true
2511
+ ? undefined
2512
+ : (initial ??
2513
+ (hasDetachedTranscript && fs.existsSync(cleanupTranscriptPath)
2514
+ ? this.#verifiedReadAndHeader(cleanupTranscriptPath, sessionId, cwd)
2515
+ : undefined));
2516
+ let ownerLocator: TaskArtifactOwnerLocator | undefined;
2517
+ try {
2518
+ ownerLocator = ownerTranscript
2519
+ ? taskArtifactOwnerLocatorFromTranscriptBytes(ownerTranscript.snapshot.bytes, sessionId)
2520
+ : undefined;
2521
+ } catch (error) {
2522
+ const failure = toError(error);
2523
+ if (failure.message.startsWith("task_artifact_owner_")) throw failure;
2524
+ throw new SessionDeleteVerificationError("artifacts", `task_artifact_owner_patch_invalid:${failure.message}`, {
2525
+ cause: failure,
2526
+ });
2527
+ }
2528
+ const withTaskArtifactOwnerFailure = <T>(operation: () => T): T => {
2529
+ try {
2530
+ return operation();
2531
+ } catch (error) {
2532
+ const failure = toError(error);
2533
+ if (failure.message.startsWith("task_artifact_owner_")) throw failure;
2534
+ throw new SessionDeleteVerificationError(
2535
+ "artifacts",
2536
+ `task_artifact_owner_cleanup_failed:${failure.message}`,
2537
+ { cause: failure },
2538
+ );
2539
+ }
2540
+ };
2541
+ let ownerEvidence = ownerEvidenceValue
2542
+ ? withTaskArtifactOwnerFailure(() => parseTaskArtifactOwnerDeletionEvidence(ownerEvidenceValue))
2543
+ : undefined;
2544
+ if (ownerEvidence && ownerEvidence.sessionId !== sessionId)
2545
+ throw new SessionDeleteVerificationError("artifacts", "task_artifact_owner_session_mismatch");
2546
+ if (ownerLocator && ownerEvidence && !util.isDeepStrictEqual(ownerLocator, ownerEvidence.locator))
2547
+ throw new SessionDeleteVerificationError("artifacts", "task_artifact_owner_locator_mismatch");
2548
+ if (ownerLocator && !ownerEvidence && taskArtifactOwnerStorageContext)
2549
+ ownerEvidence = withTaskArtifactOwnerFailure(() =>
2550
+ captureTaskArtifactOwnerDeletionEvidence(taskArtifactOwnerStorageContext, sessionId, ownerLocator),
2551
+ );
2552
+ if (ownerLocator && !ownerEvidence)
2553
+ throw new SessionDeleteVerificationError("artifacts", "task_artifact_owner_locator_missing");
2554
+ if (ownerEvidence && ownerTranscript && !ownerLocator)
2555
+ throw new SessionDeleteVerificationError("artifacts", "task_artifact_owner_evidence_not_in_transcript");
2556
+ if (ownerEvidence && !taskArtifactOwnerStorageContext)
2557
+ throw new SessionDeleteVerificationError("artifacts", "task_artifact_owner_context_missing");
2558
+ if (ownerLocator && deferTaskArtifactOwnerRetirement && (!ownerEvidence || !taskArtifactOwnerStorageContext))
2559
+ throw new SessionDeleteVerificationError("artifacts", "task_artifact_owner_defer_evidence_missing");
2560
+ if (ownerTranscriptDeletedValue === true && !ownerEvidence)
2561
+ throw new SessionDeleteVerificationError(
2562
+ "artifacts",
2563
+ "task_artifact_owner_transcript_deleted_evidence_missing",
2564
+ );
2565
+ let ownerOutcome =
2566
+ ownerOutcomeValue !== undefined && ownerEvidence && taskArtifactOwnerStorageContext
2567
+ ? withTaskArtifactOwnerFailure(() =>
2568
+ parseTaskArtifactOwnerRetirementOutcome(
2569
+ taskArtifactOwnerStorageContext,
2570
+ ownerEvidence!,
2571
+ ownerOutcomeValue,
2572
+ ),
2573
+ )
2574
+ : undefined;
2575
+ if (ownerOutcomeValue !== undefined && !ownerOutcome)
2576
+ throw new SessionDeleteVerificationError("artifacts", "task_artifact_owner_retirement_outcome_missing");
2577
+ if (
2578
+ ownerTranscriptDeletedValue === true &&
2579
+ ownerOutcome?.kind !== "completed" &&
2580
+ ownerOutcome?.kind !== "payload_retired"
2581
+ )
2582
+ throw new SessionDeleteVerificationError(
2583
+ "artifacts",
2584
+ "task_artifact_owner_transcript_deleted_outcome_missing",
2585
+ );
2586
+ if ((ownerContinuationValue !== undefined) !== (ownerOutcome !== undefined && ownerOutcome.kind !== "completed"))
2587
+ throw new SessionDeleteVerificationError("artifacts", "task_artifact_owner_retirement_continuation_mismatch");
2588
+ if (
2589
+ ownerOutcome &&
2590
+ ownerOutcome.kind !== "completed" &&
2591
+ !util.isDeepStrictEqual(ownerContinuationValue, ownerOutcome.continuation)
2592
+ )
2593
+ throw new SessionDeleteVerificationError("artifacts", "task_artifact_owner_retirement_continuation_mismatch");
2594
+ const ownerContinuationRecord =
2595
+ ownerOutcome && ownerOutcome.kind !== "completed" ? ownerOutcome.continuation : undefined;
2596
+ let ownerContinuation = ownerContinuationRecord
2597
+ ? withTaskArtifactOwnerFailure(() =>
2598
+ verifyTaskArtifactOwnerRetirementContinuation(
2599
+ taskArtifactOwnerStorageContext!,
2600
+ ownerEvidence!,
2601
+ ownerContinuationRecord,
2602
+ ),
2603
+ )
2604
+ : undefined;
2605
+ const payloadRetired = ownerOutcome?.kind === "payload_retired";
2606
+ if (
2607
+ (ownerRetiredValue !== undefined && ownerRetiredValue !== true) ||
2608
+ (ownerPayloadRetiredValue !== undefined && ownerPayloadRetiredValue !== true) ||
2609
+ (ownerNamespaceRetainedValue !== undefined && ownerNamespaceRetainedValue !== true) ||
2610
+ (ownerRetiredValue === true) !== (ownerOutcome?.kind === "completed") ||
2611
+ (ownerPayloadRetiredValue === true) !== payloadRetired ||
2612
+ (ownerNamespaceRetainedValue === true) !== payloadRetired
2613
+ )
2614
+ throw new SessionDeleteVerificationError("artifacts", "task_artifact_owner_retirement_outcome_mismatch");
2615
+ if (ownerRetiredValue === true) {
2616
+ if (!ownerEvidence || !taskArtifactOwnerStorageContext || !ownerOutcome)
2617
+ throw new SessionDeleteVerificationError("artifacts", "task_artifact_owner_retired_evidence_missing");
2618
+ withTaskArtifactOwnerFailure(() =>
2619
+ verifyTaskArtifactOwnerPhysicalRetirement(taskArtifactOwnerStorageContext, ownerEvidence!, ownerOutcome),
2620
+ );
2621
+ }
2622
+ if (
2623
+ ownerEvidence &&
2624
+ ownerLocator &&
2625
+ taskArtifactOwnerStorageContext &&
2626
+ !ownerContinuation &&
2627
+ !ownerRetiredValue
2628
+ ) {
2629
+ const current = withTaskArtifactOwnerFailure(() =>
2630
+ captureTaskArtifactOwnerDeletionEvidence(taskArtifactOwnerStorageContext, sessionId, ownerLocator),
2631
+ );
2632
+ if (!current || !util.isDeepStrictEqual(current, ownerEvidence))
2633
+ throw new SessionDeleteVerificationError("artifacts", "task_artifact_owner_changed_since_capture");
2634
+ }
2635
+ let ownerRetired = ownerRetiredValue === true;
2636
+ let ownerPayloadRetired = ownerPayloadRetiredValue === true;
2637
+ let ownerNamespaceRetained = ownerNamespaceRetainedValue === true;
2638
+ let ownerTranscriptDeleted = ownerTranscriptDeletedValue === true;
2639
+ const ownerSiblingRefusal = async (): Promise<Error | undefined> => {
2640
+ // Deferral changes who retires the owner, not the pre-artifact sibling authority boundary.
2641
+ if (!ownerEvidence) return undefined;
2642
+ if (!taskArtifactOwnerStorageContext)
2643
+ return new SessionDeleteVerificationError("artifacts", "task_artifact_owner_context_missing");
2644
+ try {
2645
+ if (
2646
+ await hasSiblingTaskArtifactOwnerTranscript(
2647
+ this,
2648
+ transcriptPath,
2649
+ ownerEvidence.locator,
2650
+ taskArtifactOwnerStorageContext,
2651
+ inspectProtocol,
2652
+ )
2653
+ )
2654
+ return new SessionDeleteVerificationError(
2655
+ "artifacts",
2656
+ "task_artifact_owner_shared_with_sibling_transcript",
2657
+ );
2658
+ return undefined;
2659
+ } catch (error) {
2660
+ return new SessionDeleteVerificationError(
2661
+ "artifacts",
2662
+ `task_artifact_owner_cleanup_failed:${toError(error).message}`,
2663
+ { cause: toError(error) },
2664
+ );
2665
+ }
2666
+ };
2667
+ const retireOwnerIfRequired = async (): Promise<Error | undefined> => {
2668
+ const refusal = await ownerSiblingRefusal();
2669
+ if (refusal) return refusal;
2670
+ if (deferTaskArtifactOwnerRetirement || ownerRetired || !ownerEvidence || !taskArtifactOwnerStorageContext)
2671
+ return undefined;
2672
+ try {
2673
+ ownerOutcome = withTaskArtifactOwnerFailure(() =>
2674
+ retireTaskArtifactOwner(taskArtifactOwnerStorageContext, ownerEvidence!, ownerContinuation),
2675
+ );
2676
+ if (ownerOutcome.kind === "completed") {
2677
+ ownerRetired = true;
2678
+ ownerContinuation = undefined;
2679
+ ownerPayloadRetired = false;
2680
+ ownerNamespaceRetained = false;
2681
+ return undefined;
2682
+ }
2683
+ ownerContinuation = ownerOutcome.continuation;
2684
+ ownerPayloadRetired = ownerOutcome.kind === "payload_retired";
2685
+ ownerNamespaceRetained = ownerOutcome.kind === "payload_retired";
2686
+ return new SessionDeleteVerificationError(
2687
+ "artifacts",
2688
+ ownerOutcome.kind === "uncertain"
2689
+ ? ownerOutcome.reason
2690
+ : "task_artifact_owner_namespace_cleanup_pending",
2691
+ );
2692
+ } catch (error) {
2693
+ return new SessionDeleteVerificationError(
2694
+ "artifacts",
2695
+ `task_artifact_owner_cleanup_failed:${toError(error).message}`,
2696
+ { cause: toError(error) },
2697
+ );
2698
+ }
2699
+ };
2700
+ const ownerResultFields = () => ({
2701
+ ...(ownerEvidence ? { taskArtifactOwnerDeletionEvidence: ownerEvidence } : {}),
2702
+ ...(ownerOutcome ? { taskArtifactOwnerRetirementOutcome: ownerOutcome } : {}),
2703
+ ...(ownerTranscriptDeleted ? { taskArtifactOwnerTranscriptDeleted: true as const } : {}),
2704
+ ...(ownerRetired ? { taskArtifactOwnerRetired: true as const } : {}),
2705
+ ...(ownerContinuation ? { taskArtifactOwnerRetirementContinuation: ownerContinuation } : {}),
2706
+ ...(ownerPayloadRetired ? { taskArtifactOwnerPayloadRetired: true as const } : {}),
2707
+ ...(ownerNamespaceRetained ? { taskArtifactOwnerNamespaceRetained: true as const } : {}),
2708
+ });
2709
+ const ownerCleanupPending = (error: Error, completedArtifacts: boolean): VerifiedSessionDeleteResult => ({
2710
+ kind: "cleanup_pending",
2711
+ phase: "task_artifact_owner",
2712
+ artifactsRemoved: completedArtifacts,
2713
+ error,
2714
+ transcriptIdentity,
2715
+ taskArtifactOwnerDeletionEvidence: ownerEvidence!,
2716
+ ...ownerResultFields(),
2717
+ });
2718
+ const finishArtifactPhase = async (): Promise<VerifiedSessionDeleteResult> => {
2719
+ const ownerError = await retireOwnerIfRequired();
2720
+ if (ownerError && ownerEvidence) return ownerCleanupPending(ownerError, true);
2721
+ if (ownerTranscriptDeleted) return { kind: "deleted", ...ownerResultFields() };
2722
+ return {
2723
+ kind: "artifacts_removed",
2724
+ phase: "artifacts",
2725
+ transcriptIdentity,
2726
+ ...ownerResultFields(),
2727
+ };
2728
+ };
2729
+ const ownerPreflightError = await ownerSiblingRefusal();
2730
+ if (ownerPreflightError && ownerEvidence)
2731
+ return ownerCleanupPending(ownerPreflightError, artifactsRemoved === true);
2358
2732
  if (detachedArtifactsPath && !artifactsRemoved) {
2359
2733
  if (
2360
2734
  !expectedArtifactsIdentity ||
@@ -2427,16 +2801,16 @@ export class FileSessionStorage implements SessionStorage {
2427
2801
  ? { retainedUnknownPath: removal.retainedUnknownPath ?? retainedArtifactsUnknownPath }
2428
2802
  : {}),
2429
2803
  transcriptIdentity,
2804
+ ...ownerResultFields(),
2430
2805
  };
2431
2806
  }
2432
- return { kind: "artifacts_removed", phase: "artifacts", transcriptIdentity };
2807
+ return await finishArtifactPhase();
2433
2808
  }
2434
2809
  if (transcriptIdentity.nlink === undefined || transcriptIdentity.nlink !== 1n)
2435
2810
  throw new SessionDeleteVerificationError(
2436
2811
  "identity",
2437
2812
  "Single-link transcript authority is required for exact deletion",
2438
2813
  );
2439
- const initial = hasDetachedTranscript ? undefined : this.#verifiedReadAndHeader(transcriptPath, sessionId, cwd);
2440
2814
  const initialStat = initial?.snapshot.stat;
2441
2815
  const initialDigest = initial ? createHash("sha256").update(initial.snapshot.bytes).digest("hex") : undefined;
2442
2816
  if (
@@ -2491,7 +2865,7 @@ export class FileSessionStorage implements SessionStorage {
2491
2865
  "artifacts",
2492
2866
  "Authorized artifact removal root remains after restart",
2493
2867
  );
2494
- return { kind: "artifacts_removed", phase: "artifacts", transcriptIdentity };
2868
+ return await finishArtifactPhase();
2495
2869
  }
2496
2870
 
2497
2871
  if (artifactsIdentity && !artifactsRemoved) {
@@ -2564,6 +2938,7 @@ export class FileSessionStorage implements SessionStorage {
2564
2938
  detachedArtifactsPath: detach.detachedPath,
2565
2939
  artifactsTree,
2566
2940
  transcriptIdentity,
2941
+ ...ownerResultFields(),
2567
2942
  };
2568
2943
  } finally {
2569
2944
  if (descriptor !== undefined) fs.closeSync(descriptor);
@@ -2581,6 +2956,7 @@ export class FileSessionStorage implements SessionStorage {
2581
2956
  ...(detach.retainedPlaceholderPath ? { retainedPlaceholderPath: detach.retainedPlaceholderPath } : {}),
2582
2957
  ...(detach.retainedUnknownPath ? { retainedUnknownPath: detach.retainedUnknownPath } : {}),
2583
2958
  transcriptIdentity,
2959
+ ...ownerResultFields(),
2584
2960
  };
2585
2961
  }
2586
2962
  const removal = removeDirectoryTreeExact(
@@ -2633,6 +3009,7 @@ export class FileSessionStorage implements SessionStorage {
2633
3009
  ? { retainedUnknownPath: removal.retainedUnknownPath ?? retainedArtifactsUnknownPath }
2634
3010
  : {}),
2635
3011
  transcriptIdentity,
3012
+ ...ownerResultFields(),
2636
3013
  };
2637
3014
  }
2638
3015
  }
@@ -2658,8 +3035,19 @@ export class FileSessionStorage implements SessionStorage {
2658
3035
  if (descriptor !== undefined) fs.closeSync(descriptor);
2659
3036
  }
2660
3037
  }
2661
- return { kind: "artifacts_removed", phase: "artifacts", transcriptIdentity };
3038
+ return await finishArtifactPhase();
2662
3039
  }
3040
+ if (deferTaskArtifactOwnerRetirement && ownerEvidence && !ownerRetired && !ownerPayloadRetired) {
3041
+ return {
3042
+ kind: "artifacts_removed",
3043
+ phase: "artifacts",
3044
+ transcriptIdentity,
3045
+ ...ownerResultFields(),
3046
+ };
3047
+ }
3048
+ const ownerError = await retireOwnerIfRequired();
3049
+ if (ownerError && ownerEvidence) return ownerCleanupPending(ownerError, true);
3050
+ if (ownerTranscriptDeleted) return { kind: "deleted", ...ownerResultFields() };
2663
3051
  if (hasDetachedTranscript) {
2664
3052
  const deletion = nativeExactUnlink(cleanupTranscriptPath, {
2665
3053
  dev: transcriptIdentity.dev,
@@ -2673,7 +3061,10 @@ export class FileSessionStorage implements SessionStorage {
2673
3061
  quarantineName: path.basename(plannedTranscriptPath),
2674
3062
  });
2675
3063
  if (!deletion.ok) {
2676
- if (transcriptDeletionTerminal(deletion)) return { kind: "deleted" };
3064
+ if (transcriptDeletionTerminal(deletion)) {
3065
+ if (ownerEvidence) ownerTranscriptDeleted = true;
3066
+ return { kind: "deleted", ...ownerResultFields() };
3067
+ }
2677
3068
  const error = exactUnlinkFailure(deletion);
2678
3069
  const retainedAuthority =
2679
3070
  deletion.detachedPath ||
@@ -2689,6 +3080,7 @@ export class FileSessionStorage implements SessionStorage {
2689
3080
  ? { transcriptPayloadDurable: true as const }
2690
3081
  : {}),
2691
3082
  transcriptIdentity,
3083
+ ...ownerResultFields(),
2692
3084
  detachedTranscriptPath: deletion.detachedPath ?? detachedTranscriptPath,
2693
3085
  ...((deletion.retainedSuccessorPath ?? retainedTranscriptSuccessorPath)
2694
3086
  ? { retainedSuccessorPath: deletion.retainedSuccessorPath ?? retainedTranscriptSuccessorPath }
@@ -2701,7 +3093,8 @@ export class FileSessionStorage implements SessionStorage {
2701
3093
  : {}),
2702
3094
  };
2703
3095
  }
2704
- return { kind: "deleted" };
3096
+ if (ownerEvidence) ownerTranscriptDeleted = true;
3097
+ return { kind: "deleted", ...ownerResultFields() };
2705
3098
  }
2706
3099
  if (!initialStat || !initialDigest)
2707
3100
  throw new SessionDeleteVerificationError("stat", "Transcript cleanup state is invalid");
@@ -2742,7 +3135,10 @@ export class FileSessionStorage implements SessionStorage {
2742
3135
  quarantineName: path.basename(plannedTranscriptPath),
2743
3136
  });
2744
3137
  if (!deletion.ok) {
2745
- if (transcriptDeletionTerminal(deletion)) return { kind: "deleted" };
3138
+ if (transcriptDeletionTerminal(deletion)) {
3139
+ if (ownerEvidence) ownerTranscriptDeleted = true;
3140
+ return { kind: "deleted", ...ownerResultFields() };
3141
+ }
2746
3142
  const error = exactUnlinkFailure(deletion);
2747
3143
  const retainedAuthority =
2748
3144
  deletion.detachedPath ||
@@ -2758,6 +3154,7 @@ export class FileSessionStorage implements SessionStorage {
2758
3154
  ? { transcriptPayloadDurable: true as const }
2759
3155
  : {}),
2760
3156
  transcriptIdentity,
3157
+ ...ownerResultFields(),
2761
3158
  detachedTranscriptPath: deletion.detachedPath,
2762
3159
  ...((deletion.retainedSuccessorPath ?? retainedTranscriptSuccessorPath)
2763
3160
  ? { retainedSuccessorPath: deletion.retainedSuccessorPath ?? retainedTranscriptSuccessorPath }
@@ -2770,7 +3167,8 @@ export class FileSessionStorage implements SessionStorage {
2770
3167
  : {}),
2771
3168
  };
2772
3169
  }
2773
- return { kind: "deleted" };
3170
+ if (ownerEvidence) ownerTranscriptDeleted = true;
3171
+ return { kind: "deleted", ...ownerResultFields() };
2774
3172
  }
2775
3173
 
2776
3174
  #verifiedReadAndHeader(
@@ -3085,9 +3483,11 @@ class MemorySessionStorageWriter implements SessionStorageBufferedWriter {
3085
3483
  this.#length = nextLength;
3086
3484
  }
3087
3485
 
3486
+ // Memory storage retains this view, so expose only visible bytes, never capacity.
3487
+ // Appends write after each published prefix; growth switches backing buffers.
3088
3488
  #writeCurrent(bytesWritten: number): void {
3089
3489
  this.#writeCalls++;
3090
- this.#storage.writeBytesOwnedSync(this.#path, Buffer.from(this.#bytes.subarray(0, this.#length)));
3490
+ this.#storage.writeBytesOwnedSync(this.#path, this.#bytes.subarray(0, this.#length));
3091
3491
  this.#bytesWritten += bytesWritten;
3092
3492
  }
3093
3493
 
@@ -3103,7 +3503,7 @@ class MemorySessionStorageWriter implements SessionStorageBufferedWriter {
3103
3503
  }
3104
3504
  this.#buffer.copy(this.#bytes, this.#length, 0, pendingBytes);
3105
3505
  this.#writeCalls++;
3106
- this.#storage.writeBytesOwnedSync(this.#path, Buffer.from(this.#bytes.subarray(0, nextLength)));
3506
+ this.#storage.writeBytesOwnedSync(this.#path, this.#bytes.subarray(0, nextLength));
3107
3507
  this.#length = nextLength;
3108
3508
  this.#bufferedBytes = 0;
3109
3509
  this.#bytesWritten += pendingBytes;
@@ -3453,6 +3853,23 @@ export class MemorySessionStorage implements SessionStorage {
3453
3853
  new SessionDeleteVerificationError("containment", "Transcript is outside the sessions root"),
3454
3854
  );
3455
3855
  }
3856
+ if (
3857
+ [
3858
+ target.taskArtifactOwnerStorageContext,
3859
+ target.taskArtifactOwnerDeletionEvidence,
3860
+ target.taskArtifactOwnerRetirementOutcome,
3861
+ target.taskArtifactOwnerTranscriptDeleted,
3862
+ target.deferTaskArtifactOwnerRetirement,
3863
+ target.taskArtifactOwnerRetired,
3864
+ target.taskArtifactOwnerRetirementContinuation,
3865
+ target.taskArtifactOwnerPayloadRetired,
3866
+ target.taskArtifactOwnerNamespaceRetained,
3867
+ ].some(value => value !== undefined)
3868
+ ) {
3869
+ return Promise.reject(
3870
+ new SessionDeleteVerificationError("artifacts", "task_artifact_owner_memory_backend_unsupported"),
3871
+ );
3872
+ }
3456
3873
  const entry = this.#files.get(transcriptPath);
3457
3874
  if (!entry) return Promise.resolve({ kind: "deleted" });
3458
3875
  const snapshot = this.readSnapshotSync(transcriptPath);
@@ -3485,6 +3902,21 @@ export class MemorySessionStorage implements SessionStorage {
3485
3902
  if (path.resolve(header.cwd) !== path.resolve(cwd)) {
3486
3903
  return Promise.reject(new SessionDeleteVerificationError("cwd", "Transcript header cwd mismatch"));
3487
3904
  }
3905
+ let ownerLocator: TaskArtifactOwnerLocator | undefined;
3906
+ try {
3907
+ ownerLocator = taskArtifactOwnerLocatorFromTranscriptBytes(snapshot.bytes, sessionId);
3908
+ } catch (error) {
3909
+ return Promise.reject(
3910
+ new SessionDeleteVerificationError("artifacts", "task_artifact_owner_memory_backend_unsupported", {
3911
+ cause: toError(error),
3912
+ }),
3913
+ );
3914
+ }
3915
+ if (ownerLocator) {
3916
+ return Promise.reject(
3917
+ new SessionDeleteVerificationError("artifacts", "task_artifact_owner_memory_backend_unsupported"),
3918
+ );
3919
+ }
3488
3920
  // Compatible artifact semantics: the memory backend models no directories, so
3489
3921
  // a key at the artifact path is a non-directory sibling that must fail closed
3490
3922
  // rather than be silently treated as an absent artifacts directory.