@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
@@ -0,0 +1,473 @@
1
+ import * as path from "node:path";
2
+ import * as util from "node:util";
3
+ import type { ResumeSessionIdentity } from "../session-manager";
4
+ import {
5
+ FileSessionStorage,
6
+ type SessionStorageSnapshot,
7
+ type VerifiedSessionDeleteResult,
8
+ type VerifiedSessionDeleteTarget,
9
+ } from "../session-storage";
10
+ import type { TaskArtifactOwnerDeletionEvidence, TaskArtifactOwnerStorageContext } from "../task-artifact-owner-codec";
11
+ import {
12
+ parseTaskArtifactOwnerDeletionEvidence,
13
+ parseTaskArtifactOwnerRetirementOutcome,
14
+ } from "../task-artifact-owner-codec";
15
+ import { retireTaskArtifactOwner, verifyTaskArtifactOwnerPhysicalRetirement } from "../task-artifact-owner-retirement";
16
+ import type {
17
+ ManagedGcSessionRetirementReceipt,
18
+ ManagedGcSessionRetirementTarget,
19
+ } from "./managed-gc-retirement-codec";
20
+ import type { ManagedGcProtocolScopeInspector } from "./managed-session-scope";
21
+ import { captureTaskArtifactOwnerDeletionEvidence } from "./task-artifact-owner-access";
22
+ import {
23
+ hasSiblingTaskArtifactOwnerTranscript,
24
+ taskArtifactOwnerLocatorFromTranscriptBytes,
25
+ } from "./task-artifact-owner-transcript";
26
+
27
+ export interface ManagedGcOwnerCleanupTarget {
28
+ readonly path: string;
29
+ readonly sessionId: string;
30
+ readonly cwd: string;
31
+ readonly identity: ResumeSessionIdentity;
32
+ readonly taskArtifactOwnerDeletionEvidence?: TaskArtifactOwnerDeletionEvidence;
33
+ }
34
+
35
+ /** Authenticated receipt operations and fencing supplied by the prepared managed scope. */
36
+ export interface ManagedGcOwnerCleanupAuthority {
37
+ readonly agentDir: string;
38
+ readonly sessionsRoot: string;
39
+ readonly directoryPath: string;
40
+ readonly storageContext: TaskArtifactOwnerStorageContext;
41
+ readonly inspectProtocol: ManagedGcProtocolScopeInspector;
42
+ readonly assertOwned: () => void;
43
+ readonly bindTarget: (transcriptPath: string) => ManagedGcSessionRetirementTarget;
44
+ readonly readReceipt: (transcriptPath: string) => Promise<ManagedGcSessionRetirementReceipt | undefined>;
45
+ readonly publishReceipt: (receipt: ManagedGcSessionRetirementReceipt) => Promise<ManagedGcSessionRetirementReceipt>;
46
+ readonly findCompletedRetirement: (
47
+ evidence: TaskArtifactOwnerDeletionEvidence,
48
+ exceptTranscriptPath: string,
49
+ ) => Promise<
50
+ | {
51
+ scope: { readonly agentDir: string; readonly sessionsRoot: string; readonly directoryPath: string };
52
+ receipt: ManagedGcSessionRetirementReceipt;
53
+ }
54
+ | undefined
55
+ >;
56
+ }
57
+
58
+ export type ManagedGcOwnerProgress = {
59
+ readonly state: "none" | "owner_retired" | "payload_retired" | "pending";
60
+ readonly message?: string;
61
+ };
62
+
63
+ export type ManagedGcOwnerDeleteFields = Pick<
64
+ VerifiedSessionDeleteTarget,
65
+ | "taskArtifactOwnerStorageContext"
66
+ | "taskArtifactOwnerDeletionEvidence"
67
+ | "taskArtifactOwnerRetirementOutcome"
68
+ | "taskArtifactOwnerRetirementContinuation"
69
+ | "taskArtifactOwnerPayloadRetired"
70
+ | "taskArtifactOwnerNamespaceRetained"
71
+ | "deferTaskArtifactOwnerRetirement"
72
+ | "taskArtifactOwnerRetired"
73
+ >;
74
+
75
+ function deepSame(left: unknown, right: unknown): boolean {
76
+ return util.isDeepStrictEqual(left, right);
77
+ }
78
+
79
+ function managedGcTargetMatchesCandidate(
80
+ target: ManagedGcSessionRetirementTarget,
81
+ candidate: ManagedGcOwnerCleanupTarget,
82
+ ): boolean {
83
+ return (
84
+ target.transcriptPath === candidate.path &&
85
+ target.sessionId === candidate.sessionId &&
86
+ target.cwd === candidate.cwd &&
87
+ target.transcriptIdentity.nlink === 1n &&
88
+ target.transcriptIdentity.dev === candidate.identity.dev &&
89
+ target.transcriptIdentity.ino === candidate.identity.ino &&
90
+ target.transcriptIdentity.size === candidate.identity.size &&
91
+ target.transcriptIdentity.mtimeNs === candidate.identity.mtimeNs &&
92
+ target.transcriptIdentity.sha256 === candidate.identity.sha256
93
+ );
94
+ }
95
+
96
+ function managedGcReceiptTarget(receipt: ManagedGcSessionRetirementReceipt): ManagedGcSessionRetirementTarget {
97
+ return {
98
+ transcriptPath: receipt.transcriptPath,
99
+ sessionId: receipt.sessionId,
100
+ cwd: receipt.cwd,
101
+ transcriptIdentity: receipt.transcriptIdentity,
102
+ taskArtifactOwnerLocator: receipt.taskArtifactOwnerDeletionEvidence.locator,
103
+ };
104
+ }
105
+
106
+ export function hasUnsupportedLegacyOwnerTarget(scopeDirectory: string, target: ManagedGcOwnerCleanupTarget): boolean {
107
+ if (path.dirname(target.path) === scopeDirectory) return false;
108
+ if (target.taskArtifactOwnerDeletionEvidence) return true;
109
+ let snapshot: SessionStorageSnapshot;
110
+ try {
111
+ snapshot = new FileSessionStorage().readSnapshotSync(target.path);
112
+ } catch (error) {
113
+ if ((error as NodeJS.ErrnoException).code === "ENOENT") return false;
114
+ throw error;
115
+ }
116
+ return taskArtifactOwnerLocatorFromTranscriptBytes(snapshot.bytes, target.sessionId) !== undefined;
117
+ }
118
+
119
+ export async function prepareManagedGcOwnerTarget<T extends ManagedGcOwnerCleanupTarget>(
120
+ authority: ManagedGcOwnerCleanupAuthority,
121
+ target: T,
122
+ ): Promise<T & ManagedGcOwnerCleanupTarget> {
123
+ if (path.dirname(target.path) !== authority.directoryPath) {
124
+ if (hasUnsupportedLegacyOwnerTarget(authority.directoryPath, target))
125
+ throw new Error("task_artifact_owner_legacy_scope_unsupported");
126
+ return target;
127
+ }
128
+ const receipt = await authority.readReceipt(target.path);
129
+ if (receipt) {
130
+ if (
131
+ !managedGcTargetMatchesCandidate(receipt, target) ||
132
+ (target.taskArtifactOwnerDeletionEvidence &&
133
+ !deepSame(target.taskArtifactOwnerDeletionEvidence, receipt.taskArtifactOwnerDeletionEvidence))
134
+ )
135
+ throw new Error("task_artifact_owner_continuation_evidence_mismatch");
136
+ return { ...target, taskArtifactOwnerDeletionEvidence: receipt.taskArtifactOwnerDeletionEvidence };
137
+ }
138
+ if (target.taskArtifactOwnerDeletionEvidence) throw new Error("task_artifact_owner_continuation_state_missing");
139
+ let snapshot: SessionStorageSnapshot;
140
+ try {
141
+ snapshot = new FileSessionStorage().readSnapshotSync(target.path);
142
+ } catch (error) {
143
+ if ((error as NodeJS.ErrnoException).code === "ENOENT") return target;
144
+ throw error;
145
+ }
146
+ const locator = taskArtifactOwnerLocatorFromTranscriptBytes(snapshot.bytes, target.sessionId);
147
+ if (!locator) return target;
148
+ const bound = authority.bindTarget(target.path);
149
+ if (!managedGcTargetMatchesCandidate(bound, target) || !deepSame(locator, bound.taskArtifactOwnerLocator))
150
+ throw new Error("task_artifact_owner_continuation_transcript_replaced");
151
+ const evidence = captureTaskArtifactOwnerDeletionEvidence(authority.storageContext, target.sessionId, locator);
152
+ if (!evidence) throw new Error("task_artifact_owner_deletion_evidence_missing");
153
+ const prepared = await authority.publishReceipt({
154
+ ...bound,
155
+ state: "prepared",
156
+ taskArtifactOwnerDeletionEvidence: evidence,
157
+ });
158
+ if (
159
+ !deepSame(prepared.taskArtifactOwnerDeletionEvidence, evidence) ||
160
+ !managedGcTargetMatchesCandidate(prepared, target)
161
+ )
162
+ throw new Error("task_artifact_owner_continuation_evidence_mismatch");
163
+ return { ...target, taskArtifactOwnerDeletionEvidence: evidence };
164
+ }
165
+
166
+ export async function prepareManagedGcOwnerTargets<T extends ManagedGcOwnerCleanupTarget>(
167
+ authority: ManagedGcOwnerCleanupAuthority,
168
+ targets: readonly T[],
169
+ ): Promise<(T & ManagedGcOwnerCleanupTarget)[]> {
170
+ const prepared: (T & ManagedGcOwnerCleanupTarget)[] = [];
171
+ for (const target of targets) prepared.push(await prepareManagedGcOwnerTarget(authority, target));
172
+ return prepared;
173
+ }
174
+
175
+ export async function managedGcOwnerDeleteFields(
176
+ authority: ManagedGcOwnerCleanupAuthority,
177
+ target: ManagedGcOwnerCleanupTarget,
178
+ ): Promise<ManagedGcOwnerDeleteFields> {
179
+ if (path.dirname(target.path) !== authority.directoryPath) {
180
+ if (hasUnsupportedLegacyOwnerTarget(authority.directoryPath, target))
181
+ throw new Error("task_artifact_owner_legacy_scope_unsupported");
182
+ return {};
183
+ }
184
+ const receipt = await authority.readReceipt(target.path);
185
+ if (!receipt) {
186
+ if (target.taskArtifactOwnerDeletionEvidence) throw new Error("task_artifact_owner_continuation_state_missing");
187
+ return {};
188
+ }
189
+ if (
190
+ !managedGcTargetMatchesCandidate(receipt, target) ||
191
+ !deepSame(receipt.taskArtifactOwnerDeletionEvidence, target.taskArtifactOwnerDeletionEvidence)
192
+ )
193
+ throw new Error("task_artifact_owner_continuation_evidence_mismatch");
194
+ const outcome = receipt.taskArtifactOwnerRetirementOutcome;
195
+ return {
196
+ taskArtifactOwnerStorageContext: authority.storageContext,
197
+ taskArtifactOwnerDeletionEvidence: receipt.taskArtifactOwnerDeletionEvidence,
198
+ deferTaskArtifactOwnerRetirement: true,
199
+ ...(outcome ? { taskArtifactOwnerRetirementOutcome: outcome } : {}),
200
+ ...(outcome && outcome.kind !== "completed"
201
+ ? { taskArtifactOwnerRetirementContinuation: outcome.continuation }
202
+ : {}),
203
+ ...(outcome?.kind === "payload_retired"
204
+ ? { taskArtifactOwnerPayloadRetired: true as const, taskArtifactOwnerNamespaceRetained: true as const }
205
+ : {}),
206
+ ...(receipt.state === "owner_retired" ? { taskArtifactOwnerRetired: true as const } : {}),
207
+ };
208
+ }
209
+
210
+ export async function publishManagedGcArtifactsRemoved(
211
+ authority: ManagedGcOwnerCleanupAuthority,
212
+ target: ManagedGcOwnerCleanupTarget,
213
+ ): Promise<ManagedGcSessionRetirementReceipt | undefined> {
214
+ if (!target.taskArtifactOwnerDeletionEvidence) return undefined;
215
+ const receipt = await authority.readReceipt(target.path);
216
+ if (!receipt) throw new Error("task_artifact_owner_continuation_state_missing");
217
+ if (!deepSame(receipt.taskArtifactOwnerDeletionEvidence, target.taskArtifactOwnerDeletionEvidence))
218
+ throw new Error("task_artifact_owner_continuation_evidence_mismatch");
219
+ if (receipt.state !== "prepared") return receipt;
220
+ return authority.publishReceipt({
221
+ ...managedGcReceiptTarget(receipt),
222
+ state: "artifacts_removed",
223
+ taskArtifactOwnerDeletionEvidence: receipt.taskArtifactOwnerDeletionEvidence,
224
+ artifactsRemoved: true,
225
+ });
226
+ }
227
+
228
+ export async function retireManagedGcOwnerAfterArtifacts(
229
+ authority: ManagedGcOwnerCleanupAuthority,
230
+ target: ManagedGcOwnerCleanupTarget,
231
+ tombstoneTargets: readonly ManagedGcOwnerCleanupTarget[],
232
+ ): Promise<ManagedGcOwnerProgress> {
233
+ if (!target.taskArtifactOwnerDeletionEvidence) return { state: "none" };
234
+ const receipt = await publishManagedGcArtifactsRemoved(authority, target);
235
+ if (!receipt) return { state: "none" };
236
+ const evidence = receipt.taskArtifactOwnerDeletionEvidence;
237
+ if (!evidence) throw new Error("task_artifact_owner_continuation_evidence_missing");
238
+ authority.assertOwned();
239
+ const sharedBefore = await hasSiblingTaskArtifactOwnerTranscript(
240
+ new FileSessionStorage(),
241
+ target.path,
242
+ evidence.locator,
243
+ authority.storageContext,
244
+ authority.inspectProtocol,
245
+ );
246
+ authority.assertOwned();
247
+ if (sharedBefore) return { state: "pending", message: "task_artifact_owner_shared_with_sibling_transcript" };
248
+ if (receipt.state === "owner_retired") return { state: "owner_retired" };
249
+ for (const sibling of tombstoneTargets) {
250
+ if (
251
+ sibling.path === target.path ||
252
+ !sibling.taskArtifactOwnerDeletionEvidence ||
253
+ !deepSame(sibling.taskArtifactOwnerDeletionEvidence, evidence)
254
+ )
255
+ continue;
256
+ const siblingReceipt = await authority.readReceipt(sibling.path);
257
+ const siblingOutcome = siblingReceipt?.taskArtifactOwnerRetirementOutcome;
258
+ if (
259
+ siblingReceipt?.state !== "owner_retired" ||
260
+ siblingOutcome?.kind !== "completed" ||
261
+ !deepSame(siblingReceipt.taskArtifactOwnerDeletionEvidence, evidence)
262
+ )
263
+ continue;
264
+ const inherited = await authority.publishReceipt({
265
+ ...managedGcReceiptTarget(receipt),
266
+ state: "owner_retired",
267
+ taskArtifactOwnerDeletionEvidence: evidence,
268
+ artifactsRemoved: true,
269
+ taskArtifactOwnerRetirementOutcome: siblingOutcome,
270
+ taskArtifactOwnerRetired: true,
271
+ });
272
+ if (inherited.state !== "owner_retired") throw new Error("task_artifact_owner_inheritance_pending");
273
+ return { state: "owner_retired" };
274
+ }
275
+ const inherited = await authority.findCompletedRetirement(evidence, target.path);
276
+ if (inherited) {
277
+ if (
278
+ inherited.scope.agentDir !== authority.agentDir ||
279
+ inherited.scope.sessionsRoot !== authority.sessionsRoot ||
280
+ inherited.scope.directoryPath === authority.directoryPath ||
281
+ path.dirname(inherited.receipt.transcriptPath) !== inherited.scope.directoryPath ||
282
+ inherited.receipt.transcriptPath === target.path ||
283
+ inherited.receipt.state !== "owner_retired"
284
+ )
285
+ throw new Error("task_artifact_owner_continuation_authority_mismatch");
286
+ const inheritedEvidence = parseTaskArtifactOwnerDeletionEvidence(
287
+ inherited.receipt.taskArtifactOwnerDeletionEvidence,
288
+ );
289
+ if (!deepSame(inheritedEvidence, evidence)) throw new Error("task_artifact_owner_continuation_evidence_mismatch");
290
+ const inheritedOutcome = parseTaskArtifactOwnerRetirementOutcome(
291
+ authority.storageContext,
292
+ evidence,
293
+ inherited.receipt.taskArtifactOwnerRetirementOutcome,
294
+ );
295
+ if (inheritedOutcome.kind !== "completed") throw new Error("task_artifact_owner_physical_retirement_unverified");
296
+ verifyTaskArtifactOwnerPhysicalRetirement(authority.storageContext, evidence, inheritedOutcome);
297
+ authority.assertOwned();
298
+ const sharedAfterInheritance = await hasSiblingTaskArtifactOwnerTranscript(
299
+ new FileSessionStorage(),
300
+ target.path,
301
+ evidence.locator,
302
+ authority.storageContext,
303
+ authority.inspectProtocol,
304
+ );
305
+ authority.assertOwned();
306
+ if (sharedAfterInheritance)
307
+ return { state: "pending", message: "task_artifact_owner_shared_with_sibling_transcript" };
308
+ const published = await authority.publishReceipt({
309
+ ...managedGcReceiptTarget(receipt),
310
+ state: "owner_retired",
311
+ taskArtifactOwnerDeletionEvidence: evidence,
312
+ artifactsRemoved: true,
313
+ taskArtifactOwnerRetirementOutcome: inheritedOutcome,
314
+ taskArtifactOwnerRetired: true,
315
+ });
316
+ if (published.state !== "owner_retired") throw new Error("task_artifact_owner_inheritance_pending");
317
+ return { state: "owner_retired" };
318
+ }
319
+ const previous = receipt.taskArtifactOwnerRetirementOutcome;
320
+ const continuation = previous && previous.kind !== "completed" ? previous.continuation : undefined;
321
+ authority.assertOwned();
322
+ const outcome = retireTaskArtifactOwner(authority.storageContext, evidence, continuation);
323
+ authority.assertOwned();
324
+ if (outcome.kind === "completed") {
325
+ await authority.publishReceipt({
326
+ ...managedGcReceiptTarget(receipt),
327
+ state: "owner_retired",
328
+ taskArtifactOwnerDeletionEvidence: evidence,
329
+ artifactsRemoved: true,
330
+ taskArtifactOwnerRetirementOutcome: outcome,
331
+ taskArtifactOwnerRetired: true,
332
+ });
333
+ return { state: "owner_retired" };
334
+ }
335
+ await authority.publishReceipt({
336
+ ...managedGcReceiptTarget(receipt),
337
+ state: "owner_pending",
338
+ taskArtifactOwnerDeletionEvidence: evidence,
339
+ artifactsRemoved: true,
340
+ taskArtifactOwnerRetirementOutcome: outcome,
341
+ taskArtifactOwnerRetirementContinuation: outcome.continuation,
342
+ ...(outcome.kind === "payload_retired"
343
+ ? { taskArtifactOwnerPayloadRetired: true as const, taskArtifactOwnerNamespaceRetained: true as const }
344
+ : {}),
345
+ });
346
+ return outcome.kind === "payload_retired"
347
+ ? { state: "payload_retired", message: "task_artifact_owner_namespace_cleanup_pending" }
348
+ : {
349
+ state: "pending",
350
+ message: outcome.kind === "uncertain" ? outcome.reason : "task_artifact_owner_namespace_cleanup_pending",
351
+ };
352
+ }
353
+
354
+ export async function persistManagedGcStorageOwnerDisposition(
355
+ authority: ManagedGcOwnerCleanupAuthority,
356
+ target: ManagedGcOwnerCleanupTarget,
357
+ deletion: Extract<VerifiedSessionDeleteResult, { kind: "cleanup_pending"; phase: "task_artifact_owner" }>,
358
+ ): Promise<ManagedGcOwnerProgress> {
359
+ const evidence = parseTaskArtifactOwnerDeletionEvidence(deletion.taskArtifactOwnerDeletionEvidence);
360
+ if (!target.taskArtifactOwnerDeletionEvidence || !deepSame(evidence, target.taskArtifactOwnerDeletionEvidence))
361
+ throw new Error("task_artifact_owner_continuation_evidence_mismatch");
362
+ if (!deletion.artifactsRemoved) return { state: "pending", message: deletion.error.message };
363
+ const receipt = await publishManagedGcArtifactsRemoved(authority, target);
364
+ if (!receipt) throw new Error("task_artifact_owner_continuation_state_missing");
365
+ const outcomeValue = deletion.taskArtifactOwnerRetirementOutcome;
366
+ if (!outcomeValue) return { state: "pending", message: deletion.error.message };
367
+ const outcome = parseTaskArtifactOwnerRetirementOutcome(authority.storageContext, evidence, outcomeValue);
368
+ if (
369
+ (deletion.taskArtifactOwnerRetired === true) !== (outcome.kind === "completed") ||
370
+ (deletion.taskArtifactOwnerPayloadRetired === true) !== (outcome.kind === "payload_retired") ||
371
+ (deletion.taskArtifactOwnerNamespaceRetained === true) !== (outcome.kind === "payload_retired") ||
372
+ (outcome.kind !== "completed" &&
373
+ !deepSame(deletion.taskArtifactOwnerRetirementContinuation, outcome.continuation))
374
+ )
375
+ throw new Error("task_artifact_owner_retirement_outcome_mismatch");
376
+ if (outcome.kind === "completed") {
377
+ await authority.publishReceipt({
378
+ ...managedGcReceiptTarget(receipt),
379
+ state: "owner_retired",
380
+ taskArtifactOwnerDeletionEvidence: evidence,
381
+ artifactsRemoved: true,
382
+ taskArtifactOwnerRetirementOutcome: outcome,
383
+ taskArtifactOwnerRetired: true,
384
+ });
385
+ return { state: "owner_retired" };
386
+ }
387
+ await authority.publishReceipt({
388
+ ...managedGcReceiptTarget(receipt),
389
+ state: "owner_pending",
390
+ taskArtifactOwnerDeletionEvidence: evidence,
391
+ artifactsRemoved: true,
392
+ taskArtifactOwnerRetirementOutcome: outcome,
393
+ taskArtifactOwnerRetirementContinuation: outcome.continuation,
394
+ ...(outcome.kind === "payload_retired"
395
+ ? { taskArtifactOwnerPayloadRetired: true as const, taskArtifactOwnerNamespaceRetained: true as const }
396
+ : {}),
397
+ });
398
+ return outcome.kind === "payload_retired"
399
+ ? { state: "payload_retired", message: "task_artifact_owner_namespace_cleanup_pending" }
400
+ : {
401
+ state: "pending",
402
+ message: outcome.kind === "uncertain" ? outcome.reason : "task_artifact_owner_namespace_cleanup_pending",
403
+ };
404
+ }
405
+
406
+ export async function managedGcOwnerProgressBeforeDelete(
407
+ authority: ManagedGcOwnerCleanupAuthority,
408
+ target: ManagedGcOwnerCleanupTarget,
409
+ tombstoneTargets: readonly ManagedGcOwnerCleanupTarget[],
410
+ artifactsRemoved: boolean,
411
+ ): Promise<ManagedGcOwnerProgress> {
412
+ const evidence = target.taskArtifactOwnerDeletionEvidence;
413
+ if (!evidence) return { state: "none" };
414
+ const receipt = await authority.readReceipt(target.path);
415
+ if (!receipt || !deepSame(receipt.taskArtifactOwnerDeletionEvidence, evidence))
416
+ throw new Error("task_artifact_owner_continuation_state_missing");
417
+ if (receipt.state === "prepared" && !artifactsRemoved) {
418
+ authority.assertOwned();
419
+ const shared = await hasSiblingTaskArtifactOwnerTranscript(
420
+ new FileSessionStorage(),
421
+ target.path,
422
+ evidence.locator,
423
+ authority.storageContext,
424
+ authority.inspectProtocol,
425
+ );
426
+ authority.assertOwned();
427
+ return shared
428
+ ? { state: "pending", message: "task_artifact_owner_shared_with_sibling_transcript" }
429
+ : { state: "none" };
430
+ }
431
+ return retireManagedGcOwnerAfterArtifacts(authority, target, tombstoneTargets);
432
+ }
433
+
434
+ export async function taskArtifactOwnerTranscriptResult(
435
+ authority: ManagedGcOwnerCleanupAuthority,
436
+ target: ManagedGcOwnerCleanupTarget,
437
+ ): Promise<"none" | "retired" | "payload_retired"> {
438
+ if (path.dirname(target.path) !== authority.directoryPath) {
439
+ if (hasUnsupportedLegacyOwnerTarget(authority.directoryPath, target))
440
+ throw new Error("task_artifact_owner_legacy_scope_unsupported");
441
+ return "none";
442
+ }
443
+ const receipt = await authority.readReceipt(target.path);
444
+ if (!receipt) {
445
+ if (target.taskArtifactOwnerDeletionEvidence) throw new Error("task_artifact_owner_continuation_state_missing");
446
+ return "none";
447
+ }
448
+ if (!deepSame(receipt.taskArtifactOwnerDeletionEvidence, target.taskArtifactOwnerDeletionEvidence))
449
+ throw new Error("task_artifact_owner_continuation_evidence_mismatch");
450
+ if (receipt.state === "owner_retired") return "retired";
451
+ return receipt.taskArtifactOwnerRetirementOutcome?.kind === "payload_retired" ? "payload_retired" : "none";
452
+ }
453
+
454
+ export interface ManagedGcOwnerTranscriptDeleteReceipt {
455
+ readonly taskArtifactOwnerTranscriptDeleted?: true;
456
+ }
457
+
458
+ export async function persistManagedGcOwnerTranscriptDeletion<T extends ManagedGcOwnerTranscriptDeleteReceipt>(input: {
459
+ target: ManagedGcOwnerCleanupTarget;
460
+ fallback: T;
461
+ deletion: Extract<VerifiedSessionDeleteResult, { kind: "deleted" }>;
462
+ readLatest: () => T | undefined;
463
+ nextReceipt: (latest: T) => T;
464
+ publishPending: (receipt: T) => Promise<void>;
465
+ }): Promise<void> {
466
+ if (!input.target.taskArtifactOwnerDeletionEvidence) return;
467
+ if (input.deletion.taskArtifactOwnerTranscriptDeleted !== true)
468
+ throw new Error("task_artifact_owner_transcript_deletion_unverified");
469
+ const latest = input.readLatest() ?? input.fallback;
470
+ if (latest.taskArtifactOwnerTranscriptDeleted) return;
471
+ const next = input.nextReceipt(latest);
472
+ await input.publishPending({ ...next, taskArtifactOwnerTranscriptDeleted: true as const });
473
+ }
@@ -26,7 +26,10 @@ export interface OwnerManifest extends TaskArtifactOwnerLocator {
26
26
  readonly sessionId: string;
27
27
  }
28
28
 
29
- export function newSessionRootStore(context: TaskArtifactOwnerStorageContext): ManagedSessionDescendantStore {
29
+ function sessionRootStore(
30
+ context: TaskArtifactOwnerStorageContext,
31
+ access: "read-only" | "read-write",
32
+ ): ManagedSessionDescendantStore {
30
33
  assertSessionRoot(context);
31
34
  if (path.resolve(context.profileAgentDir) !== context.profileAgentDir)
32
35
  throw new Error("task_artifact_owner_profile_invalid");
@@ -45,9 +48,19 @@ export function newSessionRootStore(context: TaskArtifactOwnerStorageContext): M
45
48
  dev: BigInt.asUintN(64, stat.dev),
46
49
  ino: BigInt.asUintN(64, stat.ino),
47
50
  },
51
+ access,
48
52
  );
49
53
  }
50
54
 
55
+ export function newSessionRootStore(context: TaskArtifactOwnerStorageContext): ManagedSessionDescendantStore {
56
+ return sessionRootStore(context, "read-write");
57
+ }
58
+
59
+ /** Path-backed reader with no retained or recovery-mutable native authority. */
60
+ export function newSessionRootReaderStore(context: TaskArtifactOwnerStorageContext): ManagedSessionDescendantStore {
61
+ return sessionRootStore(context, "read-only");
62
+ }
63
+
51
64
  function parseOwnerManifest(bytes: Uint8Array): OwnerManifest {
52
65
  let parsed: unknown;
53
66
  try {
@@ -141,7 +154,8 @@ export function openOwnerStore(
141
154
  );
142
155
  try {
143
156
  assertOwnerIdentity(ownerStore, locator);
144
- readOwnerManifest(ownerStore, locator, OWNER_MANIFEST);
157
+ const finalManifest = readOwnerManifest(ownerStore, locator, OWNER_MANIFEST);
158
+ if (finalManifest.sessionId !== sessionId) throw new Error("task_artifact_owner_session_mismatch");
145
159
  return ownerStore;
146
160
  } catch (error) {
147
161
  ownerStore.close();
@@ -211,7 +225,7 @@ export function captureTaskArtifactOwnerDeletionEvidence(
211
225
  ): TaskArtifactOwnerDeletionEvidence | undefined {
212
226
  const locator = parseTaskArtifactOwnerLocator(locatorValue);
213
227
  if (!locator) return undefined;
214
- const rootStore = newSessionRootStore(context);
228
+ const rootStore = newSessionRootReaderStore(context);
215
229
  try {
216
230
  rootStore.verifyRootSecurity();
217
231
  const parentIdentity: TaskArtifactOwnerParentIdentity = rootStore.captureDirectoryIdentity(OWNER_DIRECTORY);
@@ -219,6 +233,8 @@ export function captureTaskArtifactOwnerDeletionEvidence(
219
233
  // This refuses to capture during publication; it does not revoke independent open descriptors.
220
234
  if (ownerTreeHasPendingManagedPublication(treeSnapshot))
221
235
  throw new Error("task_artifact_owner_writer_not_quiescent");
236
+ const finalManifest = readOwnerManifest(rootStore, locator);
237
+ if (finalManifest.sessionId !== sessionId) throw new Error("task_artifact_owner_session_mismatch");
222
238
  const parentAfter = rootStore.captureDirectoryIdentity(OWNER_DIRECTORY);
223
239
  if (!sameOwnerParentIdentity(parentIdentity, parentAfter))
224
240
  throw new Error("task_artifact_owner_parent_changed_during_capture");