@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,8 +1,190 @@
1
+ import { createHash } from "node:crypto";
2
+ import * as fs from "node:fs";
3
+ import * as path from "node:path";
4
+ import * as util from "node:util";
5
+ import type * as natives from "@gajae-code/natives";
6
+ import type { FileSessionStorage, SessionStorageSnapshot } from "../session-storage";
1
7
  import { parseFirstJsonlLine } from "../session-transcript-header";
2
- import { parseTaskArtifactOwnerLocator, type TaskArtifactOwnerLocator } from "../task-artifact-owner-codec";
8
+ import {
9
+ OWNER_DIRECTORY,
10
+ OWNER_LOCK_DIRECTORY,
11
+ parseTaskArtifactOwnerLocator,
12
+ type TaskArtifactOwnerLocator,
13
+ type TaskArtifactOwnerStorageContext,
14
+ } from "../task-artifact-owner-codec";
15
+ import type {
16
+ ManagedGcProtocolFileSnapshot,
17
+ ManagedGcProtocolScopeInput,
18
+ ManagedGcProtocolScopeInspector,
19
+ ManagedGcProtocolScopeSnapshot,
20
+ } from "./managed-session-scope";
21
+ import {
22
+ assertManagedDirectoryRoot,
23
+ type ManagedDirectoryRoot,
24
+ validateNativeSecurityResult,
25
+ } from "./managed-session-storage";
26
+ import { isDerivedSessionMemoryFile } from "./session-memory-sidecar";
3
27
 
28
+ type NativeScopeSecurity = Pick<typeof natives, "verifyOwnerOnlyPathSecurity" | "verifyOwnerOnlyPathSecurityExpected">;
29
+
30
+ let nativeScopeSecurityBindings: NativeScopeSecurity | undefined;
31
+
32
+ function nativeScopeSecurity(): NativeScopeSecurity {
33
+ if (!nativeScopeSecurityBindings)
34
+ nativeScopeSecurityBindings = require("@gajae-code/natives") as NativeScopeSecurity;
35
+ return nativeScopeSecurityBindings;
36
+ }
37
+
38
+ const MAX_SCOPE_DIRECTORIES = 50_000;
39
+ const MAX_TRANSCRIPTS = 50_000;
40
+ const MANAGED_SCOPE_BINDING = ".gjc-managed-session-scope.v2.json";
41
+ const MANAGED_PROTOCOL_DIRECTORY = ".gjc-managed-session-internal";
42
+ const MANAGED_PROTOCOL_ROLES = ["locks", "receipts", "tombstones"] as const;
4
43
  const transcriptDecoder = new TextDecoder("utf-8", { fatal: true });
5
44
 
45
+ function sameDirectoryIdentity(left: fs.BigIntStats, right: fs.BigIntStats): boolean {
46
+ return (
47
+ left.isDirectory() &&
48
+ !left.isSymbolicLink() &&
49
+ left.dev === right.dev &&
50
+ left.ino === right.ino &&
51
+ left.mtimeNs === right.mtimeNs &&
52
+ left.ctimeNs === right.ctimeNs
53
+ );
54
+ }
55
+
56
+ function sameFileSnapshot(snapshot: SessionStorageSnapshot, stat: fs.BigIntStats): boolean {
57
+ return (
58
+ snapshot.stat.isFile &&
59
+ snapshot.stat.dev === stat.dev &&
60
+ snapshot.stat.ino === stat.ino &&
61
+ snapshot.stat.nlink === stat.nlink &&
62
+ snapshot.stat.size === Number(stat.size) &&
63
+ snapshot.stat.mtimeNs === stat.mtimeNs &&
64
+ snapshot.stat.ctimeNs === stat.ctimeNs
65
+ );
66
+ }
67
+
68
+ function verifyManagedDirectory(pathname: string, root: ManagedDirectoryRoot, stat: fs.BigIntStats): boolean {
69
+ if (!stat.isDirectory() || stat.isSymbolicLink()) return false;
70
+ try {
71
+ const native = nativeScopeSecurity();
72
+ const security =
73
+ process.platform === "win32"
74
+ ? native.verifyOwnerOnlyPathSecurityExpected(pathname, "directory", stat.dev, stat.ino)
75
+ : native.verifyOwnerOnlyPathSecurity(pathname, "directory");
76
+ if (!validateNativeSecurityResult(security, "verify", "directory").ok) return false;
77
+ assertManagedDirectoryRoot(root);
78
+ const current = fs.lstatSync(pathname, { bigint: true });
79
+ return sameDirectoryIdentity(current, stat);
80
+ } catch {
81
+ return false;
82
+ }
83
+ }
84
+
85
+ function verifyManagedFile(pathname: string, root: ManagedDirectoryRoot, stat: fs.BigIntStats): boolean {
86
+ if (!stat.isFile() || stat.isSymbolicLink() || stat.nlink !== 1n) return false;
87
+ try {
88
+ const native = nativeScopeSecurity();
89
+ const security =
90
+ process.platform === "win32"
91
+ ? native.verifyOwnerOnlyPathSecurityExpected(pathname, "file", stat.dev, stat.ino)
92
+ : native.verifyOwnerOnlyPathSecurity(pathname, "file");
93
+ if (!validateNativeSecurityResult(security, "verify", "file").ok) return false;
94
+ assertManagedDirectoryRoot(root);
95
+ const current = fs.lstatSync(pathname, { bigint: true });
96
+ return (
97
+ current.isFile() &&
98
+ !current.isSymbolicLink() &&
99
+ current.dev === stat.dev &&
100
+ current.ino === stat.ino &&
101
+ current.nlink === stat.nlink &&
102
+ current.size === stat.size &&
103
+ current.mtimeNs === stat.mtimeNs &&
104
+ current.ctimeNs === stat.ctimeNs &&
105
+ current.mode === stat.mode
106
+ );
107
+ } catch {
108
+ return false;
109
+ }
110
+ }
111
+
112
+ function inventoryRecoveryTree(
113
+ pathname: string,
114
+ root: ManagedDirectoryRoot,
115
+ depth: number,
116
+ directories: Array<{ pathname: string; stat: fs.BigIntStats; names: string[] }>,
117
+ transcripts: string[],
118
+ budget: { entries: number },
119
+ ): boolean {
120
+ if (depth > 32) return false;
121
+ try {
122
+ const stat = fs.lstatSync(pathname, { bigint: true });
123
+ if (!verifyManagedDirectory(pathname, root, stat)) return false;
124
+ const names = fs.readdirSync(pathname);
125
+ budget.entries += names.length;
126
+ if (budget.entries > MAX_TRANSCRIPTS) return false;
127
+ directories.push({ pathname, stat, names });
128
+ for (const name of names) {
129
+ const child = path.join(pathname, name);
130
+ const childStat = fs.lstatSync(child, { bigint: true });
131
+ if (childStat.isSymbolicLink()) return false;
132
+ if (childStat.isDirectory()) {
133
+ if (!inventoryRecoveryTree(child, root, depth + 1, directories, transcripts, budget)) return false;
134
+ } else if (childStat.isFile()) {
135
+ if (name.endsWith(".jsonl") || name.includes(".jsonl.")) transcripts.push(child);
136
+ } else return false;
137
+ }
138
+ return true;
139
+ } catch {
140
+ return false;
141
+ }
142
+ }
143
+
144
+ function parseV2ScopeBinding(
145
+ storage: Pick<FileSessionStorage, "readSnapshotSync">,
146
+ directory: string,
147
+ name: string,
148
+ ): boolean {
149
+ const bindingPath = path.join(directory, ".gjc-managed-session-scope.v2.json");
150
+ try {
151
+ const before = fs.lstatSync(bindingPath, { bigint: true });
152
+ if (!before.isFile() || before.isSymbolicLink()) return false;
153
+ const snapshot = storage.readSnapshotSync(bindingPath);
154
+ const after = fs.lstatSync(bindingPath, { bigint: true });
155
+ if (!sameFileSnapshot(snapshot, before) || !sameFileSnapshot(snapshot, after)) return false;
156
+ const value: unknown = JSON.parse(Buffer.from(snapshot.bytes).toString("utf8"));
157
+ if (typeof value !== "object" || value === null || Array.isArray(value)) return false;
158
+ const binding = value as Record<string, unknown>;
159
+ const keys = ["schemaVersion", "layoutVersion", "identityVersion", "platform", "canonicalPath", "identityDigest"];
160
+ if (
161
+ Object.keys(binding).length !== keys.length ||
162
+ !keys.every(key => Object.hasOwn(binding, key)) ||
163
+ binding.schemaVersion !== 1 ||
164
+ binding.layoutVersion !== 2 ||
165
+ binding.identityVersion !== 1 ||
166
+ (binding.platform !== "posix" && binding.platform !== "win32") ||
167
+ typeof binding.canonicalPath !== "string" ||
168
+ !path.isAbsolute(binding.canonicalPath) ||
169
+ typeof binding.identityDigest !== "string" ||
170
+ binding.identityDigest !== name.slice("v2-".length) ||
171
+ !/^[a-z2-7]{52}$/u.test(binding.identityDigest)
172
+ )
173
+ return false;
174
+ const canonical = `${JSON.stringify({
175
+ schemaVersion: binding.schemaVersion,
176
+ layoutVersion: binding.layoutVersion,
177
+ identityVersion: binding.identityVersion,
178
+ platform: binding.platform,
179
+ canonicalPath: binding.canonicalPath,
180
+ identityDigest: binding.identityDigest,
181
+ })}\n`;
182
+ return Buffer.from(snapshot.bytes).toString("utf8") === canonical;
183
+ } catch {
184
+ return false;
185
+ }
186
+ }
187
+
6
188
  /** Resolve the path-free owner locator using transcript replay's header-patch semantics. */
7
189
  export function taskArtifactOwnerLocatorFromTranscriptBytes(
8
190
  bytes: Uint8Array,
@@ -66,3 +248,367 @@ export function taskArtifactOwnerLocatorFromTranscriptBytes(
66
248
  }
67
249
  return locator;
68
250
  }
251
+
252
+ function protocolFileIdentity(
253
+ storage: Pick<FileSessionStorage, "readSnapshotSync">,
254
+ pathname: string,
255
+ root: ManagedDirectoryRoot,
256
+ ): { readonly identity: ManagedGcProtocolFileSnapshot } | undefined {
257
+ try {
258
+ const before = fs.lstatSync(pathname, { bigint: true });
259
+ if (!verifyManagedFile(pathname, root, before)) return undefined;
260
+ const snapshot = storage.readSnapshotSync(pathname);
261
+ const after = fs.lstatSync(pathname, { bigint: true });
262
+ if (
263
+ !verifyManagedFile(pathname, root, after) ||
264
+ !sameFileSnapshot(snapshot, before) ||
265
+ !sameFileSnapshot(snapshot, after) ||
266
+ before.mode !== after.mode
267
+ )
268
+ return undefined;
269
+ return {
270
+ identity: {
271
+ name: path.basename(pathname),
272
+ dev: snapshot.stat.dev.toString(),
273
+ ino: snapshot.stat.ino.toString(),
274
+ nlink: snapshot.stat.nlink!.toString(),
275
+ size: snapshot.stat.size,
276
+ mtimeNs: snapshot.stat.mtimeNs.toString(),
277
+ ctimeNs: snapshot.stat.ctimeNs.toString(),
278
+ mode: Number(before.mode & 0o777n),
279
+ sha256: createHash("sha256").update(snapshot.bytes).digest("hex"),
280
+ },
281
+ };
282
+ } catch {
283
+ return undefined;
284
+ }
285
+ }
286
+
287
+ /**
288
+ * Read-only inventory of every authenticated managed cwd directory under one profile's
289
+ * sessions root. Any incomplete, replaced, or unrecognized inventory conservatively
290
+ * blocks owner retirement.
291
+ */
292
+ export async function hasSiblingTaskArtifactOwnerTranscript(
293
+ storage: Pick<FileSessionStorage, "readSnapshotSync">,
294
+ transcriptPath: string,
295
+ locator: TaskArtifactOwnerLocator,
296
+ context: TaskArtifactOwnerStorageContext,
297
+ inspectProtocol?: ManagedGcProtocolScopeInspector,
298
+ ): Promise<boolean> {
299
+ const root = path.resolve(context.sessionsRoot);
300
+ const authorityRoot = context.rootAuthority;
301
+ if (
302
+ root !== context.sessionsRoot ||
303
+ !pathIsWithin(authorityRoot.canonicalPath, root) ||
304
+ path.resolve(context.profileAgentDir) !== context.profileAgentDir
305
+ )
306
+ return true;
307
+ const directorySnapshots: Array<{ pathname: string; stat: fs.BigIntStats; names: string[] }> = [];
308
+ const directTranscripts: string[] = [];
309
+ const protocolObservations: {
310
+ input: ManagedGcProtocolScopeInput;
311
+ scopeStat: fs.BigIntStats;
312
+ scopeNames: string[];
313
+ bindingIdentity: ManagedGcProtocolFileSnapshot;
314
+ protocolStat: fs.BigIntStats;
315
+ protocolNames: string[];
316
+ roleDirectories: { pathname: string; stat: fs.BigIntStats; names: string[] }[];
317
+ }[] = [];
318
+ let totalManagedEntries = 0;
319
+ let rootBefore: fs.BigIntStats;
320
+ let rootNames: string[];
321
+ let verifiedProtocolSnapshots: readonly ManagedGcProtocolScopeSnapshot[] = [];
322
+ let profileBefore: fs.BigIntStats;
323
+ try {
324
+ assertManagedDirectoryRoot(authorityRoot);
325
+ for (let parent = root; parent !== authorityRoot.canonicalPath; parent = path.dirname(parent)) {
326
+ const stat = fs.lstatSync(parent, { bigint: true });
327
+ if (!stat.isDirectory() || stat.isSymbolicLink()) return true;
328
+ }
329
+ rootBefore = fs.lstatSync(root, { bigint: true });
330
+ profileBefore = fs.lstatSync(context.profileAgentDir, { bigint: true });
331
+ if (
332
+ !verifyManagedDirectory(context.profileAgentDir, authorityRoot, profileBefore) ||
333
+ !verifyManagedDirectory(root, authorityRoot, rootBefore)
334
+ )
335
+ return true;
336
+ if (!rootBefore.isDirectory() || rootBefore.isSymbolicLink()) return true;
337
+ const entries = fs.readdirSync(root, { withFileTypes: true });
338
+ rootNames = entries.map(entry => entry.name).sort();
339
+ if (entries.length > MAX_TRANSCRIPTS) return true;
340
+ let scopeCount = 0;
341
+ totalManagedEntries = entries.length;
342
+ const recoveryBudget = { entries: totalManagedEntries };
343
+ for (const entry of entries) {
344
+ const pathname = path.join(root, entry.name);
345
+ if (entry.isFile() && (entry.name.endsWith(".jsonl") || entry.name.includes(".jsonl."))) {
346
+ directTranscripts.push(pathname);
347
+ continue;
348
+ }
349
+ if (!entry.isDirectory()) {
350
+ if (entry.isSymbolicLink()) return true;
351
+ continue;
352
+ }
353
+ if ([OWNER_DIRECTORY, OWNER_LOCK_DIRECTORY].includes(entry.name)) continue;
354
+ if (entry.name === ".gjc-recovery") {
355
+ if (
356
+ !inventoryRecoveryTree(pathname, authorityRoot, 0, directorySnapshots, directTranscripts, recoveryBudget)
357
+ )
358
+ return true;
359
+ totalManagedEntries = recoveryBudget.entries;
360
+ continue;
361
+ }
362
+ if (!entry.name.startsWith("v2-") && !entry.name.startsWith("-")) return true;
363
+ if (++scopeCount > MAX_SCOPE_DIRECTORIES) return true;
364
+ if (entry.name.startsWith("v2-") && !/^v2-[a-z2-7]{52}$/u.test(entry.name)) return true;
365
+ const stat = fs.lstatSync(pathname, { bigint: true });
366
+ if (!verifyManagedDirectory(pathname, authorityRoot, stat)) return true;
367
+ if (entry.name.startsWith("v2-") && !parseV2ScopeBinding(storage, pathname, entry.name)) return true;
368
+ const names = fs.readdirSync(pathname).sort();
369
+ if (names.some(name => name.startsWith(MANAGED_PROTOCOL_DIRECTORY) && name !== MANAGED_PROTOCOL_DIRECTORY))
370
+ return true;
371
+ totalManagedEntries += names.length;
372
+ if (totalManagedEntries > MAX_TRANSCRIPTS) return true;
373
+ recoveryBudget.entries = totalManagedEntries;
374
+ directorySnapshots.push({ pathname, stat, names });
375
+ const protocolNameIndex = names.indexOf(MANAGED_PROTOCOL_DIRECTORY);
376
+ if (protocolNameIndex >= 0) {
377
+ if (!entry.name.startsWith("v2-") || !inspectProtocol) return true;
378
+ const protocolPath = path.join(pathname, MANAGED_PROTOCOL_DIRECTORY);
379
+ const protocolStat = fs.lstatSync(protocolPath, { bigint: true });
380
+ if (!verifyManagedDirectory(protocolPath, authorityRoot, protocolStat)) return true;
381
+ const protocolNames = fs.readdirSync(protocolPath).sort();
382
+ if (protocolNames.join("\0") !== MANAGED_PROTOCOL_ROLES.join("\0")) return true;
383
+ const roleDirectories: { pathname: string; stat: fs.BigIntStats; names: string[] }[] = [];
384
+ for (const role of MANAGED_PROTOCOL_ROLES) {
385
+ const rolePath = path.join(protocolPath, role);
386
+ const roleStat = fs.lstatSync(rolePath, { bigint: true });
387
+ if (!verifyManagedDirectory(rolePath, authorityRoot, roleStat)) return true;
388
+ const roleNames = fs.readdirSync(rolePath).sort();
389
+ roleDirectories.push({ pathname: rolePath, stat: roleStat, names: roleNames });
390
+ totalManagedEntries += 1 + roleNames.length;
391
+ }
392
+ if (totalManagedEntries > MAX_TRANSCRIPTS) return true;
393
+ recoveryBudget.entries = totalManagedEntries;
394
+ const bindingPath = path.join(pathname, MANAGED_SCOPE_BINDING);
395
+ const binding = protocolFileIdentity(storage, bindingPath, authorityRoot);
396
+ if (!binding) return true;
397
+ const input: ManagedGcProtocolScopeInput = {
398
+ scopePath: pathname,
399
+ scopeIdentity: { path: pathname, dev: stat.dev.toString(), ino: stat.ino.toString() },
400
+ bindingIdentity: binding.identity,
401
+ protocolIdentity: {
402
+ path: protocolPath,
403
+ dev: protocolStat.dev.toString(),
404
+ ino: protocolStat.ino.toString(),
405
+ mtimeNs: protocolStat.mtimeNs.toString(),
406
+ ctimeNs: protocolStat.ctimeNs.toString(),
407
+ mode: Number(protocolStat.mode & 0o777n),
408
+ },
409
+ };
410
+ protocolObservations.push({
411
+ input,
412
+ scopeStat: stat,
413
+ scopeNames: names,
414
+ bindingIdentity: binding.identity,
415
+ protocolStat,
416
+ protocolNames,
417
+ roleDirectories,
418
+ });
419
+ directorySnapshots.push({ pathname: protocolPath, stat: protocolStat, names: protocolNames });
420
+ directorySnapshots.push(...roleDirectories);
421
+ }
422
+ const stagingPath = path.join(pathname, ".staging");
423
+ let stagingStat: fs.BigIntStats | undefined;
424
+ try {
425
+ stagingStat = fs.lstatSync(stagingPath, { bigint: true });
426
+ } catch (error) {
427
+ if ((error as NodeJS.ErrnoException).code !== "ENOENT") return true;
428
+ }
429
+ if (stagingStat) {
430
+ if (!verifyManagedDirectory(stagingPath, authorityRoot, stagingStat)) return true;
431
+ const stagedNames = fs.readdirSync(stagingPath);
432
+ totalManagedEntries += stagedNames.length;
433
+ if (totalManagedEntries > MAX_TRANSCRIPTS) return true;
434
+ recoveryBudget.entries = totalManagedEntries;
435
+ directorySnapshots.push({ pathname: stagingPath, stat: stagingStat, names: stagedNames });
436
+ }
437
+ }
438
+ } catch {
439
+ return true;
440
+ }
441
+
442
+ if (protocolObservations.length > 0) {
443
+ try {
444
+ const protocolSnapshots = await inspectProtocol!(protocolObservations.map(observation => observation.input));
445
+ if (protocolSnapshots.length !== protocolObservations.length) return true;
446
+ for (let index = 0; index < protocolObservations.length; index++) {
447
+ const observation = protocolObservations[index]!;
448
+ const snapshot = protocolSnapshots[index]!;
449
+ const { input } = observation;
450
+ const identityMatches = (
451
+ actual: { readonly path: string; readonly dev: string; readonly ino: string },
452
+ pathname: string,
453
+ stat: fs.BigIntStats,
454
+ ): boolean =>
455
+ actual.path === pathname && actual.dev === stat.dev.toString() && actual.ino === stat.ino.toString();
456
+ const configuredRootStat = fs.lstatSync(authorityRoot.canonicalPath, { bigint: true });
457
+ if (
458
+ !identityMatches(snapshot.configuredRoot, authorityRoot.canonicalPath, configuredRootStat) ||
459
+ !identityMatches(snapshot.profile, context.profileAgentDir, profileBefore) ||
460
+ !identityMatches(snapshot.sessionsRoot, root, rootBefore) ||
461
+ !identityMatches(snapshot.scope, input.scopePath, observation.scopeStat) ||
462
+ !identityMatches(snapshot.protocol, input.protocolIdentity.path, observation.protocolStat) ||
463
+ !util.isDeepStrictEqual(snapshot.binding, observation.bindingIdentity) ||
464
+ snapshot.protocolMtimeNs !== observation.protocolStat.mtimeNs.toString() ||
465
+ snapshot.protocolCtimeNs !== observation.protocolStat.ctimeNs.toString() ||
466
+ snapshot.protocolMode !== Number(observation.protocolStat.mode & 0o777n) ||
467
+ snapshot.directories.length !== MANAGED_PROTOCOL_ROLES.length ||
468
+ MANAGED_PROTOCOL_ROLES.some((role, roleIndex) => snapshot.directories[roleIndex]?.role !== role)
469
+ )
470
+ return true;
471
+ const scopeNow = fs.lstatSync(input.scopePath, { bigint: true });
472
+ if (
473
+ !verifyManagedDirectory(input.scopePath, authorityRoot, scopeNow) ||
474
+ !sameDirectoryIdentity(scopeNow, observation.scopeStat) ||
475
+ fs.readdirSync(input.scopePath).sort().join("\0") !== observation.scopeNames.join("\0")
476
+ )
477
+ return true;
478
+ const bindingNow = protocolFileIdentity(
479
+ storage,
480
+ path.join(input.scopePath, MANAGED_SCOPE_BINDING),
481
+ authorityRoot,
482
+ );
483
+ if (!bindingNow || !util.isDeepStrictEqual(bindingNow.identity, input.bindingIdentity)) return true;
484
+ const protocolNow = fs.lstatSync(input.protocolIdentity.path, { bigint: true });
485
+ if (
486
+ !verifyManagedDirectory(input.protocolIdentity.path, authorityRoot, protocolNow) ||
487
+ !sameDirectoryIdentity(protocolNow, observation.protocolStat) ||
488
+ protocolNow.mode !== observation.protocolStat.mode ||
489
+ fs.readdirSync(input.protocolIdentity.path).sort().join("\0") !== observation.protocolNames.join("\0") ||
490
+ observation.protocolNames.join("\0") !== MANAGED_PROTOCOL_ROLES.join("\0")
491
+ )
492
+ return true;
493
+ for (const directory of snapshot.directories) {
494
+ const rolePath = path.join(input.protocolIdentity.path, directory.role);
495
+ const roleObservation = observation.roleDirectories[MANAGED_PROTOCOL_ROLES.indexOf(directory.role)];
496
+ const roleStat = fs.lstatSync(rolePath, { bigint: true });
497
+ const roleNames = fs.readdirSync(rolePath).sort();
498
+ if (
499
+ directory.path !== rolePath ||
500
+ !roleObservation ||
501
+ !verifyManagedDirectory(rolePath, authorityRoot, roleStat) ||
502
+ !sameDirectoryIdentity(roleStat, roleObservation.stat) ||
503
+ roleStat.mode !== roleObservation.stat.mode ||
504
+ roleNames.join("\0") !== roleObservation.names.join("\0") ||
505
+ roleStat.dev.toString() !== directory.dev ||
506
+ roleStat.ino.toString() !== directory.ino ||
507
+ roleStat.mtimeNs.toString() !== directory.mtimeNs ||
508
+ roleStat.ctimeNs.toString() !== directory.ctimeNs ||
509
+ Number(roleStat.mode & 0o777n) !== directory.mode ||
510
+ roleNames.length !== directory.files.length ||
511
+ roleNames.join("\0") !== directory.files.map(file => file.name).join("\0")
512
+ )
513
+ return true;
514
+ for (const expected of directory.files) {
515
+ const current = protocolFileIdentity(storage, path.join(rolePath, expected.name), authorityRoot);
516
+ if (!current || !util.isDeepStrictEqual(current.identity, expected)) return true;
517
+ }
518
+ }
519
+ if (totalManagedEntries > MAX_TRANSCRIPTS) return true;
520
+ }
521
+ verifiedProtocolSnapshots = protocolSnapshots;
522
+ } catch {
523
+ return true;
524
+ }
525
+ }
526
+
527
+ const candidatePaths = new Set(
528
+ directTranscripts.filter(pathname => !pathname.includes(".jsonl.") || !isDerivedSessionMemoryFile(pathname)),
529
+ );
530
+ for (const directory of directorySnapshots) {
531
+ for (const name of directory.names) {
532
+ if (
533
+ name.endsWith(".jsonl") ||
534
+ (name.includes(".jsonl.") && !isDerivedSessionMemoryFile(path.join(directory.pathname, name)))
535
+ )
536
+ candidatePaths.add(path.join(directory.pathname, name));
537
+ }
538
+ }
539
+ if (candidatePaths.size > MAX_TRANSCRIPTS) return true;
540
+ for (const siblingPath of candidatePaths) {
541
+ if (path.resolve(siblingPath) === path.resolve(transcriptPath)) continue;
542
+ try {
543
+ const before = fs.lstatSync(siblingPath, { bigint: true });
544
+ if (before.isSymbolicLink() || !before.isFile()) return true;
545
+ const sibling = storage.readSnapshotSync(siblingPath);
546
+ const after = fs.lstatSync(siblingPath, { bigint: true });
547
+ if (
548
+ after.isSymbolicLink() ||
549
+ !after.isFile() ||
550
+ after.dev !== before.dev ||
551
+ after.ino !== before.ino ||
552
+ after.nlink !== before.nlink ||
553
+ after.size !== before.size ||
554
+ after.mtimeNs !== before.mtimeNs ||
555
+ after.ctimeNs !== before.ctimeNs ||
556
+ !sameFileSnapshot(sibling, before)
557
+ )
558
+ return true;
559
+ const header = parseFirstJsonlLine(sibling.bytes);
560
+ if (header?.type !== "session" || typeof header.id !== "string") return true;
561
+ const siblingLocator = taskArtifactOwnerLocatorFromTranscriptBytes(sibling.bytes, header.id);
562
+ if (siblingLocator && JSON.stringify(siblingLocator) === JSON.stringify(locator)) return true;
563
+ } catch {
564
+ return true;
565
+ }
566
+ }
567
+ try {
568
+ assertManagedDirectoryRoot(authorityRoot);
569
+ const currentProfile = fs.lstatSync(context.profileAgentDir, { bigint: true });
570
+ const currentRoot = fs.lstatSync(root, { bigint: true });
571
+ if (
572
+ !verifyManagedDirectory(context.profileAgentDir, authorityRoot, currentProfile) ||
573
+ !sameDirectoryIdentity(currentProfile, profileBefore) ||
574
+ !verifyManagedDirectory(root, authorityRoot, currentRoot) ||
575
+ !sameDirectoryIdentity(currentRoot, rootBefore) ||
576
+ fs.readdirSync(root).sort().join("\0") !== rootNames.join("\0")
577
+ )
578
+ return true;
579
+ for (const candidate of directorySnapshots) {
580
+ const current = fs.lstatSync(candidate.pathname, { bigint: true });
581
+ if (
582
+ !verifyManagedDirectory(candidate.pathname, authorityRoot, current) ||
583
+ !sameDirectoryIdentity(current, candidate.stat) ||
584
+ current.mode !== candidate.stat.mode ||
585
+ fs.readdirSync(candidate.pathname).sort().join("\0") !== candidate.names.join("\0")
586
+ )
587
+ return true;
588
+ }
589
+ for (const observation of protocolObservations) {
590
+ const binding = protocolFileIdentity(
591
+ storage,
592
+ path.join(observation.input.scopePath, MANAGED_SCOPE_BINDING),
593
+ authorityRoot,
594
+ );
595
+ if (!binding || !util.isDeepStrictEqual(binding.identity, observation.bindingIdentity)) return true;
596
+ }
597
+ for (const snapshot of verifiedProtocolSnapshots) {
598
+ for (const directory of snapshot.directories) {
599
+ for (const expected of directory.files) {
600
+ const current = protocolFileIdentity(storage, path.join(directory.path, expected.name), authorityRoot);
601
+ if (!current || !util.isDeepStrictEqual(current.identity, expected)) return true;
602
+ }
603
+ }
604
+ }
605
+ } catch {
606
+ return true;
607
+ }
608
+ return false;
609
+ }
610
+
611
+ function pathIsWithin(root: string, target: string): boolean {
612
+ const relative = path.relative(root, target);
613
+ return relative === "" || (!path.isAbsolute(relative) && relative !== ".." && !relative.startsWith(`..${path.sep}`));
614
+ }