@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
@@ -1,14 +1,49 @@
1
1
  import * as crypto from "node:crypto";
2
2
  import * as path from "node:path";
3
+ import * as util from "node:util";
3
4
 
4
5
  import { toError } from "@gajae-code/utils";
6
+ import type {
7
+ ManagedGcSessionRetirementReceipt,
8
+ ManagedGcSessionRetirementTarget,
9
+ } from "./internal/managed-gc-retirement-codec";
10
+ import {
11
+ bindManagedGcSessionRetirementTarget,
12
+ type ManagedScope,
13
+ managedGcProtocolScopeInspectorForScope,
14
+ publishManagedGcSessionRetirementReceipt,
15
+ readManagedGcSessionRetirementReceiptReadOnly,
16
+ taskArtifactOwnerStorageContextForScope,
17
+ } from "./internal/managed-session-scope";
18
+ import { captureTaskArtifactOwnerDeletionEvidence } from "./internal/task-artifact-owner-access";
19
+ import {
20
+ hasSiblingTaskArtifactOwnerTranscript,
21
+ taskArtifactOwnerLocatorFromTranscriptBytes,
22
+ } from "./internal/task-artifact-owner-transcript";
5
23
  import {
6
24
  type FileSessionStorage,
7
25
  SessionDeleteVerificationError,
26
+ type SessionStorageFileIdentity,
8
27
  type SessionStorageSnapshot,
9
28
  type VerifiedSessionDeleteTarget,
10
29
  } from "./session-storage";
11
30
  import { parseFirstJsonlLine } from "./session-transcript-header";
31
+ import type {
32
+ TaskArtifactOwnerDeletionEvidence,
33
+ TaskArtifactOwnerLocator,
34
+ TaskArtifactOwnerRetirementContinuation,
35
+ TaskArtifactOwnerRetirementOutcome,
36
+ TaskArtifactOwnerStorageContext,
37
+ } from "./task-artifact-owner-codec";
38
+ import {
39
+ retireTaskArtifactOwner,
40
+ verifyTaskArtifactOwnerPhysicalRetirement,
41
+ verifyTaskArtifactOwnerRetirementContinuation,
42
+ } from "./task-artifact-owner-retirement";
43
+
44
+ export interface SessionRetirementContinuation {
45
+ readonly managedScope: ManagedScope;
46
+ }
12
47
 
13
48
  /**
14
49
  * Outcome of a disk-retention retirement attempt.
@@ -21,25 +56,143 @@ import { parseFirstJsonlLine } from "./session-transcript-header";
21
56
  * as an ordinary keep.
22
57
  */
23
58
  export type SessionRetirementOutcome =
24
- | { kind: "retired" }
59
+ | { kind: "retired"; taskArtifactOwnerRetired?: true }
25
60
  | { kind: "kept"; reason: string }
26
- | { kind: "cleanup_pending"; reason: string };
61
+ | {
62
+ kind: "cleanup_pending";
63
+ reason: string;
64
+ phase?: "artifacts" | "transcript";
65
+ taskArtifactOwnerDeletionEvidence?: TaskArtifactOwnerDeletionEvidence;
66
+ taskArtifactOwnerRetirementOutcome?: TaskArtifactOwnerRetirementOutcome;
67
+ taskArtifactOwnerRetirementContinuation?: TaskArtifactOwnerRetirementContinuation;
68
+ taskArtifactOwnerPayloadRetired?: true;
69
+ taskArtifactOwnerNamespaceRetained?: true;
70
+ taskArtifactOwnerRetired?: true;
71
+ };
27
72
 
28
73
  /** A retirement that cleared every precondition the retention pass itself owns. */
29
74
  type SessionRetirementPlan =
30
- | { kind: "retirable"; target: VerifiedSessionDeleteTarget; artifactsPresent: boolean }
75
+ | {
76
+ kind: "retirable";
77
+ target: VerifiedSessionDeleteTarget;
78
+ artifactsPresent: boolean;
79
+ taskArtifactOwnerLocator?: TaskArtifactOwnerLocator;
80
+ gcContinuationReceipt?: ManagedGcSessionRetirementReceipt;
81
+ }
82
+ | { kind: "owner_shared"; reason: string; gcContinuationReceipt?: ManagedGcSessionRetirementReceipt }
83
+ | { kind: "cleanup_pending"; reason: string; gcContinuationReceipt: ManagedGcSessionRetirementReceipt }
31
84
  | { kind: "kept"; reason: string };
32
85
 
86
+ type OwnerRetirementFields = {
87
+ taskArtifactOwnerDeletionEvidence?: TaskArtifactOwnerDeletionEvidence;
88
+ taskArtifactOwnerRetirementOutcome?: TaskArtifactOwnerRetirementOutcome;
89
+ taskArtifactOwnerRetirementContinuation?: TaskArtifactOwnerRetirementContinuation;
90
+ taskArtifactOwnerPayloadRetired?: true;
91
+ taskArtifactOwnerNamespaceRetained?: true;
92
+ taskArtifactOwnerRetired?: true;
93
+ };
94
+
95
+ export type SessionRetirementProbeOutcome =
96
+ | Extract<SessionRetirementOutcome, { kind: "cleanup_pending" }>
97
+ | {
98
+ kind: "retirable";
99
+ taskArtifactOwnerDeletionEvidence?: TaskArtifactOwnerDeletionEvidence;
100
+ taskArtifactOwnerRetirementOutcome?: TaskArtifactOwnerRetirementOutcome;
101
+ taskArtifactOwnerRetired?: true;
102
+ taskArtifactOwnerRetirementContinuation?: TaskArtifactOwnerRetirementContinuation;
103
+ taskArtifactOwnerPayloadRetired?: true;
104
+ taskArtifactOwnerNamespaceRetained?: true;
105
+ }
106
+ | { kind: "kept"; reason: string };
107
+
108
+ function isWithinPath(root: string, target: string): boolean {
109
+ const relative = path.relative(path.resolve(root), path.resolve(target));
110
+ return relative !== "" && relative !== ".." && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative);
111
+ }
112
+
113
+ function sameTranscriptIdentity(left: SessionStorageFileIdentity, right: SessionStorageFileIdentity): boolean {
114
+ return (
115
+ left.dev === right.dev &&
116
+ left.ino === right.ino &&
117
+ left.nlink === right.nlink &&
118
+ left.size === right.size &&
119
+ left.mtimeNs === right.mtimeNs &&
120
+ left.sha256 === right.sha256
121
+ );
122
+ }
123
+
124
+ function sameManagedGcTarget(left: ManagedGcSessionRetirementTarget, right: ManagedGcSessionRetirementTarget): boolean {
125
+ return (
126
+ left.transcriptPath === right.transcriptPath &&
127
+ left.sessionId === right.sessionId &&
128
+ left.cwd === right.cwd &&
129
+ sameTranscriptIdentity(left.transcriptIdentity, right.transcriptIdentity) &&
130
+ util.isDeepStrictEqual(left.taskArtifactOwnerLocator, right.taskArtifactOwnerLocator)
131
+ );
132
+ }
133
+
134
+ function sameEvidence(left: TaskArtifactOwnerDeletionEvidence, right: TaskArtifactOwnerDeletionEvidence): boolean {
135
+ return util.isDeepStrictEqual(left, right);
136
+ }
137
+
138
+ function targetFromReceipt(receipt: ManagedGcSessionRetirementReceipt): ManagedGcSessionRetirementTarget {
139
+ return {
140
+ transcriptPath: receipt.transcriptPath,
141
+ sessionId: receipt.sessionId,
142
+ cwd: receipt.cwd,
143
+ transcriptIdentity: receipt.transcriptIdentity,
144
+ taskArtifactOwnerLocator: receipt.taskArtifactOwnerLocator,
145
+ };
146
+ }
147
+
148
+ function ownerOutcomeFields(
149
+ evidence: TaskArtifactOwnerDeletionEvidence,
150
+ outcome: TaskArtifactOwnerRetirementOutcome | undefined,
151
+ ownerRetired: boolean,
152
+ ): OwnerRetirementFields {
153
+ return {
154
+ taskArtifactOwnerDeletionEvidence: evidence,
155
+ ...(outcome ? { taskArtifactOwnerRetirementOutcome: outcome } : {}),
156
+ ...(outcome && outcome.kind !== "completed"
157
+ ? { taskArtifactOwnerRetirementContinuation: outcome.continuation }
158
+ : {}),
159
+ ...(outcome?.kind === "payload_retired"
160
+ ? { taskArtifactOwnerPayloadRetired: true as const, taskArtifactOwnerNamespaceRetained: true as const }
161
+ : {}),
162
+ ...(ownerRetired ? { taskArtifactOwnerRetired: true as const } : {}),
163
+ };
164
+ }
165
+
166
+ function planRetirementOutcome(
167
+ plan: Extract<SessionRetirementPlan, { kind: "retirable" }>,
168
+ ): SessionRetirementProbeOutcome {
169
+ const receipt = plan.gcContinuationReceipt;
170
+ const outcome = receipt?.taskArtifactOwnerRetirementOutcome;
171
+ return {
172
+ kind: "retirable",
173
+ ...(receipt ? { taskArtifactOwnerDeletionEvidence: receipt.taskArtifactOwnerDeletionEvidence } : {}),
174
+ ...(outcome ? { taskArtifactOwnerRetirementOutcome: outcome } : {}),
175
+ ...(receipt?.state === "owner_retired" ? { taskArtifactOwnerRetired: true as const } : {}),
176
+ ...(outcome && outcome.kind !== "completed"
177
+ ? { taskArtifactOwnerRetirementContinuation: outcome.continuation }
178
+ : {}),
179
+ ...(outcome?.kind === "payload_retired"
180
+ ? { taskArtifactOwnerPayloadRetired: true as const, taskArtifactOwnerNamespaceRetained: true as const }
181
+ : {}),
182
+ };
183
+ }
184
+
33
185
  /**
34
186
  * Evaluate the preconditions retirement owns — the transcript must be readable
35
187
  * and carry a usable session header — and bind the delete target to the exact
36
188
  * bytes just read. Nothing here mutates the store.
37
189
  */
38
- function planSessionRetirement(
190
+ async function planSessionRetirement(
39
191
  storage: FileSessionStorage,
40
192
  sessionsRoot: string,
41
193
  transcriptPath: string,
42
- ): SessionRetirementPlan {
194
+ continuation?: SessionRetirementContinuation,
195
+ ): Promise<SessionRetirementPlan> {
43
196
  let snapshot: SessionStorageSnapshot;
44
197
  try {
45
198
  snapshot = storage.readSnapshotSync(transcriptPath);
@@ -55,17 +208,107 @@ function planSessionRetirement(
55
208
  // The verified delete authority only unlinks a single-link transcript, so a
56
209
  // hard-linked one can never be retired. Refusing here keeps the dry run from
57
210
  // promising bytes prune cannot release, and — because this runs before the
58
- // artifact phase — keeps a doomed retirement from destroying the artifact
59
- // tree of a session whose transcript will survive anyway.
211
+ // artifact phase — keeps a doomed retirement from destroying its artifacts.
60
212
  if (snapshot.stat.nlink === undefined || snapshot.stat.nlink !== 1n) {
61
213
  return { kind: "kept", reason: "transcript_not_single_link" };
62
214
  }
63
215
 
216
+ let ownerLocator: TaskArtifactOwnerLocator | undefined;
217
+ try {
218
+ ownerLocator = taskArtifactOwnerLocatorFromTranscriptBytes(snapshot.bytes, header.id);
219
+ } catch (error) {
220
+ return { kind: "kept", reason: `task_artifact_owner_locator_invalid: ${toError(error).message}` };
221
+ }
222
+
223
+ let ownerContext: TaskArtifactOwnerStorageContext | undefined;
224
+ let ownerEvidence: TaskArtifactOwnerDeletionEvidence | undefined;
225
+ let gcContinuationReceipt: ManagedGcSessionRetirementReceipt | undefined;
226
+ let managedTarget: ManagedGcSessionRetirementTarget | undefined;
227
+ let partialReceipt: ManagedGcSessionRetirementReceipt | undefined;
228
+ const continuationRefusal = (reason: string): SessionRetirementPlan =>
229
+ partialReceipt
230
+ ? { kind: "cleanup_pending", reason, gcContinuationReceipt: partialReceipt }
231
+ : { kind: "kept", reason };
232
+ if (ownerLocator) {
233
+ if (!continuation) return { kind: "kept", reason: "task_artifact_owner_context_missing" };
234
+ try {
235
+ ownerContext = taskArtifactOwnerStorageContextForScope(continuation.managedScope);
236
+ if (path.resolve(ownerContext.sessionsRoot) !== path.resolve(sessionsRoot))
237
+ return { kind: "kept", reason: "task_artifact_owner_root_mismatch" };
238
+ managedTarget = bindManagedGcSessionRetirementTarget(continuation.managedScope, transcriptPath);
239
+ const snapshotIdentity: SessionStorageFileIdentity = {
240
+ dev: snapshot.stat.dev,
241
+ ino: snapshot.stat.ino,
242
+ nlink: snapshot.stat.nlink,
243
+ size: snapshot.stat.size,
244
+ mtimeNs: snapshot.stat.mtimeNs,
245
+ sha256: crypto.createHash("sha256").update(snapshot.bytes).digest("hex"),
246
+ };
247
+ if (
248
+ managedTarget.sessionId !== header.id ||
249
+ managedTarget.cwd !== header.cwd ||
250
+ !sameTranscriptIdentity(managedTarget.transcriptIdentity, snapshotIdentity) ||
251
+ !util.isDeepStrictEqual(managedTarget.taskArtifactOwnerLocator, ownerLocator)
252
+ )
253
+ return { kind: "kept", reason: "task_artifact_owner_continuation_transcript_replaced" };
254
+ gcContinuationReceipt = await readManagedGcSessionRetirementReceiptReadOnly(
255
+ continuation.managedScope,
256
+ transcriptPath,
257
+ );
258
+ if (gcContinuationReceipt) {
259
+ if (!sameManagedGcTarget(gcContinuationReceipt, managedTarget))
260
+ return { kind: "kept", reason: "task_artifact_owner_continuation_transcript_replaced" };
261
+ if (gcContinuationReceipt.artifactsRemoved) partialReceipt = gcContinuationReceipt;
262
+ ownerEvidence = gcContinuationReceipt.taskArtifactOwnerDeletionEvidence;
263
+ if (!ownerEvidence) return continuationRefusal("task_artifact_owner_continuation_evidence_missing");
264
+ if (gcContinuationReceipt.state === "prepared" || gcContinuationReceipt.state === "artifacts_removed") {
265
+ const currentEvidence = captureTaskArtifactOwnerDeletionEvidence(ownerContext, header.id, ownerLocator);
266
+ if (!currentEvidence || !sameEvidence(currentEvidence, ownerEvidence))
267
+ return continuationRefusal("task_artifact_owner_continuation_evidence_mismatch");
268
+ } else if (gcContinuationReceipt.state === "owner_pending") {
269
+ const outcome = gcContinuationReceipt.taskArtifactOwnerRetirementOutcome;
270
+ if (!outcome || outcome.kind === "completed")
271
+ return continuationRefusal("task_artifact_owner_continuation_outcome_mismatch");
272
+ verifyTaskArtifactOwnerRetirementContinuation(ownerContext, ownerEvidence, outcome.continuation);
273
+ } else {
274
+ const outcome = gcContinuationReceipt.taskArtifactOwnerRetirementOutcome;
275
+ if (outcome?.kind !== "completed")
276
+ return continuationRefusal("task_artifact_owner_continuation_outcome_mismatch");
277
+ verifyTaskArtifactOwnerPhysicalRetirement(ownerContext, ownerEvidence, outcome);
278
+ }
279
+ } else {
280
+ ownerEvidence = captureTaskArtifactOwnerDeletionEvidence(ownerContext, header.id, ownerLocator);
281
+ if (!ownerEvidence) return { kind: "kept", reason: "task_artifact_owner_continuation_evidence_missing" };
282
+ }
283
+ if (
284
+ await hasSiblingTaskArtifactOwnerTranscript(
285
+ storage,
286
+ transcriptPath,
287
+ ownerLocator,
288
+ ownerContext,
289
+ managedGcProtocolScopeInspectorForScope(continuation.managedScope),
290
+ )
291
+ )
292
+ return {
293
+ kind: "owner_shared",
294
+ reason: "task_artifact_owner_shared_with_sibling_transcript",
295
+ ...(gcContinuationReceipt ? { gcContinuationReceipt } : {}),
296
+ };
297
+ } catch (error) {
298
+ return continuationRefusal(`task_artifact_owner_continuation_invalid: ${toError(error).message}`);
299
+ }
300
+ }
301
+
64
302
  const directory = path.dirname(transcriptPath);
65
303
  return {
66
304
  kind: "retirable",
67
305
  artifactsPresent: storage.existsSync(transcriptPath.slice(0, -".jsonl".length)),
306
+ ...(ownerLocator ? { taskArtifactOwnerLocator: ownerLocator } : {}),
307
+ ...(gcContinuationReceipt ? { gcContinuationReceipt } : {}),
68
308
  target: {
309
+ ...(ownerContext ? { taskArtifactOwnerStorageContext: ownerContext } : {}),
310
+ ...(ownerEvidence ? { taskArtifactOwnerDeletionEvidence: ownerEvidence } : {}),
311
+ ...(ownerLocator ? { deferTaskArtifactOwnerRetirement: true as const } : {}),
69
312
  sessionsRoot,
70
313
  transcriptPath,
71
314
  sessionId: header.id,
@@ -93,13 +336,25 @@ function planSessionRetirement(
93
336
  * authority's verdict (containment, identity, artifact tree) is deliberately
94
337
  * not predicted here — it is re-derived against live state at delete time.
95
338
  */
96
- export function probeSessionRetirement(
339
+ export async function probeSessionRetirement(
97
340
  storage: FileSessionStorage,
98
341
  sessionsRoot: string,
99
342
  transcriptPath: string,
100
- ): { kind: "retirable" } | { kind: "kept"; reason: string } {
101
- const plan = planSessionRetirement(storage, sessionsRoot, transcriptPath);
102
- return plan.kind === "retirable" ? { kind: "retirable" } : plan;
343
+ continuation?: SessionRetirementContinuation,
344
+ ): Promise<SessionRetirementProbeOutcome> {
345
+ const plan = await planSessionRetirement(storage, sessionsRoot, transcriptPath, continuation);
346
+ if (plan.kind === "kept") return plan;
347
+ if (plan.kind === "owner_shared" || plan.kind === "cleanup_pending") {
348
+ const receipt = plan.gcContinuationReceipt;
349
+ if (!receipt?.artifactsRemoved) return { kind: "kept", reason: plan.reason };
350
+ return cleanupPending(
351
+ plan.reason,
352
+ receipt.taskArtifactOwnerDeletionEvidence,
353
+ receipt.taskArtifactOwnerRetirementOutcome,
354
+ receipt.state === "owner_retired",
355
+ );
356
+ }
357
+ return planRetirementOutcome(plan);
103
358
  }
104
359
 
105
360
  /**
@@ -110,6 +365,300 @@ function retirementIncomplete(artifactsRemoved: boolean, reason: string): Sessio
110
365
  return artifactsRemoved ? { kind: "cleanup_pending", reason } : { kind: "kept", reason };
111
366
  }
112
367
 
368
+ function cleanupPending(
369
+ reason: string,
370
+ evidence: TaskArtifactOwnerDeletionEvidence,
371
+ outcome: TaskArtifactOwnerRetirementOutcome | undefined,
372
+ ownerRetired: boolean,
373
+ phase: "artifacts" | "transcript" = "artifacts",
374
+ ): Extract<SessionRetirementOutcome, { kind: "cleanup_pending" }> {
375
+ return { kind: "cleanup_pending", reason, phase, ...ownerOutcomeFields(evidence, outcome, ownerRetired) };
376
+ }
377
+
378
+ function errorMessage(error: unknown): string {
379
+ if (!(error instanceof AggregateError)) return toError(error).message;
380
+ const details = [...error.errors].map(cause => toError(cause).message).join("; ");
381
+ return details ? `${error.message}: ${details}` : error.message;
382
+ }
383
+
384
+ type ManagedGcOwnerProgress =
385
+ | { kind: "owner_retired"; receipt: ManagedGcSessionRetirementReceipt; outcome: TaskArtifactOwnerRetirementOutcome }
386
+ | { kind: "owner_pending"; receipt: ManagedGcSessionRetirementReceipt; outcome: TaskArtifactOwnerRetirementOutcome };
387
+
388
+ async function advanceManagedGcOwnerRetirement(
389
+ scope: ManagedScope,
390
+ receipt: ManagedGcSessionRetirementReceipt,
391
+ context: TaskArtifactOwnerStorageContext,
392
+ evidence: TaskArtifactOwnerDeletionEvidence,
393
+ ): Promise<ManagedGcOwnerProgress> {
394
+ if (receipt.state === "owner_retired") {
395
+ const outcome = receipt.taskArtifactOwnerRetirementOutcome;
396
+ if (outcome?.kind !== "completed") throw new Error("task_artifact_owner_continuation_outcome_mismatch");
397
+ verifyTaskArtifactOwnerPhysicalRetirement(context, evidence, outcome);
398
+ return { kind: "owner_retired", receipt, outcome };
399
+ }
400
+ if (receipt.state === "prepared") throw new Error("task_artifact_owner_continuation_state_order_invalid");
401
+ // `artifacts_removed` is the durable authorization for the first native
402
+ // scrub. Persist owner_pending only after that scrub returns its real proof;
403
+ // a replay from owner_pending revalidates the prior continuation first.
404
+ let previousContinuation: TaskArtifactOwnerRetirementContinuation | undefined;
405
+ if (receipt.state === "owner_pending") {
406
+ const previous = receipt.taskArtifactOwnerRetirementOutcome;
407
+ if (!previous || previous.kind === "completed")
408
+ throw new Error("task_artifact_owner_continuation_outcome_mismatch");
409
+ previousContinuation = verifyTaskArtifactOwnerRetirementContinuation(context, evidence, previous.continuation);
410
+ }
411
+ const outcome = retireTaskArtifactOwner(context, evidence, previousContinuation);
412
+ if (outcome.kind === "completed") {
413
+ const retired = await publishManagedGcSessionRetirementReceipt(scope, {
414
+ ...targetFromReceipt(receipt),
415
+ state: "owner_retired",
416
+ taskArtifactOwnerDeletionEvidence: evidence,
417
+ artifactsRemoved: true,
418
+ taskArtifactOwnerRetirementOutcome: outcome,
419
+ taskArtifactOwnerRetired: true,
420
+ });
421
+ return { kind: "owner_retired", receipt: retired, outcome: retired.taskArtifactOwnerRetirementOutcome! };
422
+ }
423
+ const pending = await publishManagedGcSessionRetirementReceipt(scope, {
424
+ ...targetFromReceipt(receipt),
425
+ state: "owner_pending",
426
+ taskArtifactOwnerDeletionEvidence: evidence,
427
+ artifactsRemoved: true,
428
+ taskArtifactOwnerRetirementOutcome: outcome,
429
+ taskArtifactOwnerRetirementContinuation: outcome.continuation,
430
+ ...(outcome.kind === "payload_retired"
431
+ ? { taskArtifactOwnerPayloadRetired: true as const, taskArtifactOwnerNamespaceRetained: true as const }
432
+ : {}),
433
+ });
434
+ return { kind: "owner_pending", receipt: pending, outcome: pending.taskArtifactOwnerRetirementOutcome! };
435
+ }
436
+
437
+ /** Resume journaled owner cleanup without broadening the captured tree authority. */
438
+ async function retireGcOwnerTranscript(
439
+ storage: FileSessionStorage,
440
+ plan: Extract<SessionRetirementPlan, { kind: "retirable" }>,
441
+ continuation: SessionRetirementContinuation,
442
+ ): Promise<SessionRetirementOutcome> {
443
+ const target = plan.target;
444
+ const ownerContext = target.taskArtifactOwnerStorageContext;
445
+ const evidence = target.taskArtifactOwnerDeletionEvidence;
446
+ const locator = plan.taskArtifactOwnerLocator;
447
+ if (!ownerContext || !evidence || !locator)
448
+ return { kind: "kept", reason: "task_artifact_owner_continuation_evidence_missing" };
449
+ const receiptTarget: ManagedGcSessionRetirementTarget = {
450
+ transcriptPath: target.transcriptPath,
451
+ sessionId: target.sessionId,
452
+ cwd: target.cwd,
453
+ transcriptIdentity: target.transcriptIdentity,
454
+ taskArtifactOwnerLocator: locator,
455
+ };
456
+ let receipt = plan.gcContinuationReceipt;
457
+ let artifactsRemoved = receipt?.artifactsRemoved === true;
458
+ let ownerOutcome = receipt?.taskArtifactOwnerRetirementOutcome;
459
+ let ownerRetired = receipt?.state === "owner_retired";
460
+ try {
461
+ if (
462
+ await hasSiblingTaskArtifactOwnerTranscript(
463
+ storage,
464
+ target.transcriptPath,
465
+ locator,
466
+ ownerContext,
467
+ managedGcProtocolScopeInspectorForScope(continuation.managedScope),
468
+ )
469
+ )
470
+ return artifactsRemoved
471
+ ? cleanupPending("task_artifact_owner_shared_with_sibling_transcript", evidence, ownerOutcome, ownerRetired)
472
+ : { kind: "kept", reason: "task_artifact_owner_shared_with_sibling_transcript" };
473
+
474
+ if (!receipt) {
475
+ receipt = await publishManagedGcSessionRetirementReceipt(continuation.managedScope, {
476
+ ...receiptTarget,
477
+ state: "prepared",
478
+ taskArtifactOwnerDeletionEvidence: evidence,
479
+ });
480
+ }
481
+ if (receipt.state === "prepared") {
482
+ const artifactPhase = await storage.deleteSessionVerified(
483
+ {
484
+ ...target,
485
+ taskArtifactOwnerStorageContext: ownerContext,
486
+ taskArtifactOwnerDeletionEvidence: evidence,
487
+ deferTaskArtifactOwnerRetirement: true,
488
+ },
489
+ managedGcProtocolScopeInspectorForScope(continuation.managedScope),
490
+ );
491
+ if (artifactPhase.kind === "cleanup_pending") {
492
+ if (artifactPhase.phase === "task_artifact_owner" && artifactPhase.artifactsRemoved === false)
493
+ return { kind: "kept", reason: artifactPhase.error.message };
494
+ return cleanupPending(
495
+ `cleanup_pending_${artifactPhase.phase}: ${artifactPhase.error.message}`,
496
+ evidence,
497
+ ownerOutcome,
498
+ ownerRetired,
499
+ "artifacts",
500
+ );
501
+ }
502
+ if (artifactPhase.kind !== "artifacts_removed") {
503
+ return { kind: "kept", reason: "task_artifact_owner_continuation_artifact_phase_incomplete" };
504
+ }
505
+ artifactsRemoved = true;
506
+ receipt = await publishManagedGcSessionRetirementReceipt(continuation.managedScope, {
507
+ ...receiptTarget,
508
+ state: "artifacts_removed",
509
+ taskArtifactOwnerDeletionEvidence: evidence,
510
+ artifactsRemoved: true,
511
+ });
512
+ }
513
+ if (
514
+ await hasSiblingTaskArtifactOwnerTranscript(
515
+ storage,
516
+ target.transcriptPath,
517
+ locator,
518
+ ownerContext,
519
+ managedGcProtocolScopeInspectorForScope(continuation.managedScope),
520
+ )
521
+ )
522
+ return cleanupPending(
523
+ "task_artifact_owner_shared_with_sibling_transcript",
524
+ evidence,
525
+ ownerOutcome,
526
+ ownerRetired,
527
+ );
528
+
529
+ const progress = await advanceManagedGcOwnerRetirement(
530
+ continuation.managedScope,
531
+ receipt,
532
+ ownerContext,
533
+ evidence,
534
+ );
535
+ receipt = progress.receipt;
536
+ ownerOutcome = progress.outcome;
537
+ ownerRetired = progress.kind === "owner_retired";
538
+ if (progress.kind === "owner_pending")
539
+ return cleanupPending(
540
+ progress.outcome.kind === "uncertain"
541
+ ? progress.outcome.reason
542
+ : "task_artifact_owner_namespace_cleanup_pending",
543
+ evidence,
544
+ progress.outcome,
545
+ false,
546
+ );
547
+ if (
548
+ await hasSiblingTaskArtifactOwnerTranscript(
549
+ storage,
550
+ target.transcriptPath,
551
+ locator,
552
+ ownerContext,
553
+ managedGcProtocolScopeInspectorForScope(continuation.managedScope),
554
+ )
555
+ )
556
+ return cleanupPending("task_artifact_owner_shared_with_sibling_transcript", evidence, ownerOutcome, true);
557
+
558
+ const deletion = await storage.deleteSessionVerified(
559
+ {
560
+ ...target,
561
+ taskArtifactOwnerStorageContext: ownerContext,
562
+ taskArtifactOwnerDeletionEvidence: evidence,
563
+ taskArtifactOwnerRetirementOutcome: ownerOutcome,
564
+ taskArtifactOwnerRetired: true,
565
+ deferTaskArtifactOwnerRetirement: true,
566
+ artifactsRemoved: true,
567
+ },
568
+ managedGcProtocolScopeInspectorForScope(continuation.managedScope),
569
+ );
570
+ if (deletion.kind === "deleted") return { kind: "retired", taskArtifactOwnerRetired: true };
571
+ if (deletion.kind === "cleanup_pending")
572
+ return cleanupPending(
573
+ `cleanup_pending_${deletion.phase}: ${deletion.error.message}`,
574
+ evidence,
575
+ ownerOutcome,
576
+ true,
577
+ deletion.phase === "transcript" ? "transcript" : "artifacts",
578
+ );
579
+ return cleanupPending("transcript_not_deleted", evidence, ownerOutcome, true, "transcript");
580
+ } catch (error) {
581
+ const reason = `gc_task_artifact_owner_continuation_failed: ${errorMessage(error)}`;
582
+ return artifactsRemoved ? cleanupPending(reason, evidence, ownerOutcome, ownerRetired) : { kind: "kept", reason };
583
+ }
584
+ }
585
+
586
+ export async function retireOrphanSessionTranscript(
587
+ storage: FileSessionStorage,
588
+ sessionsRoot: string,
589
+ continuation: SessionRetirementContinuation,
590
+ receipt: ManagedGcSessionRetirementReceipt,
591
+ ): Promise<SessionRetirementOutcome> {
592
+ const evidence = receipt.taskArtifactOwnerDeletionEvidence;
593
+ let ownerOutcome = receipt.taskArtifactOwnerRetirementOutcome;
594
+ let ownerRetired = receipt.state === "owner_retired";
595
+ const artifactsRemoved = receipt.artifactsRemoved === true;
596
+ try {
597
+ const ownerContext = taskArtifactOwnerStorageContextForScope(continuation.managedScope);
598
+ if (
599
+ path.resolve(ownerContext.sessionsRoot) !== path.resolve(sessionsRoot) ||
600
+ !path.isAbsolute(receipt.transcriptPath) ||
601
+ path.resolve(receipt.transcriptPath) !== receipt.transcriptPath ||
602
+ !isWithinPath(sessionsRoot, receipt.transcriptPath) ||
603
+ path.dirname(receipt.transcriptPath) !== continuation.managedScope.directoryPath
604
+ )
605
+ return { kind: "kept", reason: "task_artifact_owner_continuation_authority_mismatch" };
606
+ const currentReceipt = await readManagedGcSessionRetirementReceiptReadOnly(
607
+ continuation.managedScope,
608
+ receipt.transcriptPath,
609
+ );
610
+ if (!currentReceipt || !util.isDeepStrictEqual(currentReceipt, receipt))
611
+ return { kind: "kept", reason: "task_artifact_owner_continuation_state_changed" };
612
+ if (storage.existsSync(receipt.transcriptPath))
613
+ return { kind: "kept", reason: "task_artifact_owner_orphan_transcript_reappeared" };
614
+ if (receipt.state === "prepared")
615
+ return cleanupPending("transcript_missing_before_artifact_phase", evidence, ownerOutcome, false, "transcript");
616
+ if (
617
+ await hasSiblingTaskArtifactOwnerTranscript(
618
+ storage,
619
+ receipt.transcriptPath,
620
+ evidence.locator,
621
+ ownerContext,
622
+ managedGcProtocolScopeInspectorForScope(continuation.managedScope),
623
+ )
624
+ )
625
+ return cleanupPending(
626
+ "task_artifact_owner_shared_with_sibling_transcript",
627
+ evidence,
628
+ ownerOutcome,
629
+ ownerRetired,
630
+ );
631
+
632
+ const progress = await advanceManagedGcOwnerRetirement(
633
+ continuation.managedScope,
634
+ receipt,
635
+ ownerContext,
636
+ evidence,
637
+ );
638
+ ownerOutcome = progress.outcome;
639
+ ownerRetired = progress.kind === "owner_retired";
640
+ if (progress.kind === "owner_pending")
641
+ return cleanupPending(
642
+ progress.outcome.kind === "uncertain"
643
+ ? progress.outcome.reason
644
+ : "task_artifact_owner_namespace_cleanup_pending",
645
+ evidence,
646
+ progress.outcome,
647
+ false,
648
+ );
649
+ return cleanupPending(
650
+ "transcript_missing_without_verified_retirement",
651
+ evidence,
652
+ ownerOutcome,
653
+ ownerRetired,
654
+ "transcript",
655
+ );
656
+ } catch (error) {
657
+ const reason = `gc_task_artifact_owner_continuation_failed: ${errorMessage(error)}`;
658
+ return artifactsRemoved ? cleanupPending(reason, evidence, ownerOutcome, ownerRetired) : { kind: "kept", reason };
659
+ }
660
+ }
661
+
113
662
  /**
114
663
  * Retire one managed session transcript through the verified hard-delete
115
664
  * authority.
@@ -125,9 +674,21 @@ export async function retireSessionTranscript(
125
674
  storage: FileSessionStorage,
126
675
  sessionsRoot: string,
127
676
  transcriptPath: string,
677
+ continuation?: SessionRetirementContinuation,
128
678
  ): Promise<SessionRetirementOutcome> {
129
- const plan = planSessionRetirement(storage, sessionsRoot, transcriptPath);
679
+ const plan = await planSessionRetirement(storage, sessionsRoot, transcriptPath, continuation);
130
680
  if (plan.kind === "kept") return plan;
681
+ if (plan.kind === "owner_shared" || plan.kind === "cleanup_pending") {
682
+ const receipt = plan.gcContinuationReceipt;
683
+ if (!receipt?.artifactsRemoved) return { kind: "kept", reason: plan.reason };
684
+ return cleanupPending(
685
+ plan.reason,
686
+ receipt.taskArtifactOwnerDeletionEvidence,
687
+ receipt.taskArtifactOwnerRetirementOutcome,
688
+ receipt.state === "owner_retired",
689
+ );
690
+ }
691
+ if (plan.taskArtifactOwnerLocator && continuation) return retireGcOwnerTranscript(storage, plan, continuation);
131
692
 
132
693
  let artifactsRemoved = false;
133
694
  try {
@@ -138,8 +699,6 @@ export async function retireSessionTranscript(
138
699
  if (artifacts.kind === "cleanup_pending") {
139
700
  return { kind: "cleanup_pending", reason: `cleanup_pending_${artifacts.phase}: ${artifacts.error.message}` };
140
701
  }
141
- // Phase 1 is durable: whatever artifact tree the session had is gone, so
142
- // every later refusal leaves a record without its artifacts.
143
702
  artifactsRemoved = plan.artifactsPresent;
144
703
  const deletion = await storage.deleteSessionVerified({ ...plan.target, artifactsRemoved: true });
145
704
  if (deletion.kind === "deleted") return { kind: "retired" };