@zergai/ztc 0.4.0-beta.1 → 0.5.0-beta.1

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 (718) hide show
  1. package/README.md +398 -45
  2. package/RUST_THIRD_PARTY_LICENSES.txt +1080 -822
  3. package/THIRD_PARTY_NOTICES.md +273 -12
  4. package/bin/zmux-darwin-arm64 +0 -0
  5. package/bin/ztc-audio-darwin-arm64 +0 -0
  6. package/bin/ztc-capture-darwin-arm64 +0 -0
  7. package/bin/ztc-computer-darwin-arm64 +0 -0
  8. package/bin/ztc-llm-darwin-arm64 +0 -0
  9. package/bin/ztc-tts-darwin-arm64 +0 -0
  10. package/bin/ztc-tui-darwin-arm64 +0 -0
  11. package/bin/ztc-zlicky-overlay-darwin-arm64 +0 -0
  12. package/bin/zterm-darwin-arm64 +0 -0
  13. package/dist/App.js +1206 -209
  14. package/dist/agent/agent.d.ts +74 -8
  15. package/dist/agent/agent.js +1073 -184
  16. package/dist/agent/backends/account_rotation.d.ts +4 -0
  17. package/dist/agent/backends/account_rotation.js +8 -2
  18. package/dist/agent/backends/anthropic.d.ts +13 -0
  19. package/dist/agent/backends/anthropic.js +169 -28
  20. package/dist/agent/backends/claude_cli.js +18 -8
  21. package/dist/agent/backends/codex_app_server.d.ts +1 -0
  22. package/dist/agent/backends/codex_app_server.js +55 -3
  23. package/dist/agent/backends/codex_auth.d.ts +1 -0
  24. package/dist/agent/backends/codex_auth.js +67 -13
  25. package/dist/agent/backends/codex_buffered_recovery.d.ts +27 -0
  26. package/dist/agent/backends/codex_buffered_recovery.js +293 -0
  27. package/dist/agent/backends/gemini.js +3 -1
  28. package/dist/agent/backends/http_error.d.ts +26 -0
  29. package/dist/agent/backends/http_error.js +221 -0
  30. package/dist/agent/backends/index.d.ts +9 -2
  31. package/dist/agent/backends/index.js +22 -2
  32. package/dist/agent/backends/openai.d.ts +0 -6
  33. package/dist/agent/backends/openai.js +5 -2
  34. package/dist/agent/backends/openai_compatible.d.ts +5 -4
  35. package/dist/agent/backends/openai_compatible.js +26 -19
  36. package/dist/agent/backends/openai_responses.d.ts +8 -3
  37. package/dist/agent/backends/openai_responses.js +77 -33
  38. package/dist/agent/backends/provider_ids.d.ts +1 -0
  39. package/dist/agent/backends/provider_ids.js +4 -0
  40. package/dist/agent/backends/request_metrics.js +12 -2
  41. package/dist/agent/backends/types.d.ts +109 -6
  42. package/dist/agent/backends/types.js +5 -1
  43. package/dist/agent/backends/zrouter_stream.d.ts +6 -1
  44. package/dist/agent/backends/zrouter_stream.js +160 -42
  45. package/dist/agent/background_registry.d.ts +2 -0
  46. package/dist/agent/background_registry.js +1 -0
  47. package/dist/agent/claim_observation.d.ts +6 -0
  48. package/dist/agent/claim_observation.js +54 -0
  49. package/dist/agent/commands/accounts.js +46 -177
  50. package/dist/agent/commands/attention.d.ts +2 -0
  51. package/dist/agent/commands/attention.js +67 -0
  52. package/dist/agent/commands/auth.js +18 -2
  53. package/dist/agent/commands/board.d.ts +2 -0
  54. package/dist/agent/commands/board.js +169 -0
  55. package/dist/agent/commands/clipboard.js +50 -1
  56. package/dist/agent/commands/compact.js +21 -13
  57. package/dist/agent/commands/config.js +4 -1
  58. package/dist/agent/commands/context.js +6 -1
  59. package/dist/agent/commands/desktop.d.ts +2 -0
  60. package/dist/agent/commands/desktop.js +29 -0
  61. package/dist/agent/commands/effort.js +14 -6
  62. package/dist/agent/commands/environment.d.ts +3 -0
  63. package/dist/agent/commands/environment.js +109 -0
  64. package/dist/agent/commands/evidence.d.ts +2 -0
  65. package/dist/agent/commands/evidence.js +9 -0
  66. package/dist/agent/commands/fork.js +4 -3
  67. package/dist/agent/commands/goal.d.ts +33 -1
  68. package/dist/agent/commands/goal.js +174 -14
  69. package/dist/agent/commands/goal_exit.d.ts +36 -0
  70. package/dist/agent/commands/goal_exit.js +120 -0
  71. package/dist/agent/commands/help.js +3 -1
  72. package/dist/agent/commands/history.js +3 -1
  73. package/dist/agent/commands/inbox.d.ts +2 -0
  74. package/dist/agent/commands/inbox.js +148 -0
  75. package/dist/agent/commands/index.js +16 -1
  76. package/dist/agent/commands/input_mode.js +8 -6
  77. package/dist/agent/commands/job.d.ts +2 -0
  78. package/dist/agent/commands/job.js +93 -0
  79. package/dist/agent/commands/keybindings.js +4 -1
  80. package/dist/agent/commands/label.d.ts +2 -0
  81. package/dist/agent/commands/label.js +77 -0
  82. package/dist/agent/commands/layout.d.ts +2 -0
  83. package/dist/agent/commands/layout.js +20 -0
  84. package/dist/agent/commands/mirror.js +16 -1
  85. package/dist/agent/commands/model.js +12 -2
  86. package/dist/agent/commands/permissions.d.ts +3 -1
  87. package/dist/agent/commands/permissions.js +79 -89
  88. package/dist/agent/commands/plan.js +5 -15
  89. package/dist/agent/commands/profile.js +21 -2
  90. package/dist/agent/commands/provider_selection.js +20 -1
  91. package/dist/agent/commands/rename.js +1 -1
  92. package/dist/agent/commands/resources.d.ts +2 -0
  93. package/dist/agent/commands/resources.js +67 -0
  94. package/dist/agent/commands/rewind.d.ts +3 -0
  95. package/dist/agent/commands/rewind.js +16 -0
  96. package/dist/agent/commands/runbooks.d.ts +2 -0
  97. package/dist/agent/commands/runbooks.js +73 -0
  98. package/dist/agent/commands/runbooks_lazy.d.ts +2 -0
  99. package/dist/agent/commands/runbooks_lazy.js +8 -0
  100. package/dist/agent/commands/sessions.d.ts +1 -1
  101. package/dist/agent/commands/sessions.js +33 -5
  102. package/dist/agent/commands/settings.js +185 -39
  103. package/dist/agent/commands/setup.d.ts +2 -0
  104. package/dist/agent/commands/setup.js +78 -0
  105. package/dist/agent/commands/share.d.ts +2 -0
  106. package/dist/agent/commands/share.js +64 -0
  107. package/dist/agent/commands/status.js +14 -2
  108. package/dist/agent/commands/types.d.ts +99 -6
  109. package/dist/agent/commands/update.js +8 -0
  110. package/dist/agent/commands/usage.d.ts +2 -0
  111. package/dist/agent/commands/usage.js +8 -0
  112. package/dist/agent/factory.d.ts +11 -0
  113. package/dist/agent/factory.js +113 -88
  114. package/dist/agent/loop_policy.d.ts +6 -3
  115. package/dist/agent/loop_policy.js +12 -3
  116. package/dist/agent/measurement_guidance.d.ts +2 -0
  117. package/dist/agent/measurement_guidance.js +5 -0
  118. package/dist/agent/request_encoder.d.ts +8 -1
  119. package/dist/agent/request_encoder.js +95 -23
  120. package/dist/agent/runtime/benchmark_comparison.d.ts +30 -0
  121. package/dist/agent/runtime/benchmark_comparison.js +94 -0
  122. package/dist/agent/runtime/benchmark_contract.d.ts +48 -0
  123. package/dist/agent/runtime/benchmark_contract.js +90 -0
  124. package/dist/agent/runtime/checked_commands.d.ts +32 -0
  125. package/dist/agent/runtime/checked_commands.js +119 -0
  126. package/dist/agent/runtime/command_execution_policy.d.ts +26 -0
  127. package/dist/agent/runtime/command_execution_policy.js +91 -0
  128. package/dist/agent/runtime/command_jobs.d.ts +36 -18
  129. package/dist/agent/runtime/command_jobs.js +151 -13
  130. package/dist/agent/runtime/command_panes.d.ts +59 -0
  131. package/dist/agent/runtime/command_panes.js +398 -0
  132. package/dist/agent/runtime/command_wait.d.ts +19 -0
  133. package/dist/agent/runtime/command_wait.js +167 -0
  134. package/dist/agent/runtime/cpu_benchmark.d.ts +30 -0
  135. package/dist/agent/runtime/cpu_benchmark.js +126 -0
  136. package/dist/agent/runtime/delegated_evidence.d.ts +26 -0
  137. package/dist/agent/runtime/delegated_evidence.js +80 -0
  138. package/dist/agent/runtime/evidence.d.ts +127 -0
  139. package/dist/agent/runtime/evidence.js +468 -0
  140. package/dist/agent/runtime/execution_context.d.ts +7 -0
  141. package/dist/agent/runtime/execution_context.js +39 -3
  142. package/dist/agent/runtime/json_verifier_recipe.d.ts +8 -0
  143. package/dist/agent/runtime/json_verifier_recipe.js +66 -0
  144. package/dist/agent/runtime/measurements.d.ts +110 -0
  145. package/dist/agent/runtime/measurements.js +317 -0
  146. package/dist/agent/runtime/policy.d.ts +4 -0
  147. package/dist/agent/runtime/policy.js +31 -20
  148. package/dist/agent/runtime/provider_native_evidence.d.ts +20 -0
  149. package/dist/agent/runtime/provider_native_evidence.js +86 -0
  150. package/dist/agent/runtime/recovery.d.ts +2 -0
  151. package/dist/agent/runtime/recovery.js +45 -2
  152. package/dist/agent/runtime/remote_broker_transport.d.ts +10 -0
  153. package/dist/agent/runtime/remote_broker_transport.js +84 -0
  154. package/dist/agent/runtime/remote_deletion_receipts.d.ts +3 -0
  155. package/dist/agent/runtime/remote_deletion_receipts.js +43 -0
  156. package/dist/agent/runtime/remote_journal_replay.d.ts +13 -0
  157. package/dist/agent/runtime/remote_journal_replay.js +233 -0
  158. package/dist/agent/runtime/remote_mutation_journal.d.ts +25 -0
  159. package/dist/agent/runtime/remote_mutation_journal.js +208 -0
  160. package/dist/agent/runtime/remote_resource_endpoint.d.ts +7 -0
  161. package/dist/agent/runtime/remote_resource_endpoint.js +6 -0
  162. package/dist/agent/runtime/remote_resource_errors.d.ts +5 -0
  163. package/dist/agent/runtime/remote_resource_errors.js +9 -0
  164. package/dist/agent/runtime/remote_resource_script.d.ts +2 -0
  165. package/dist/agent/runtime/remote_resource_script.js +1009 -0
  166. package/dist/agent/runtime/remote_resources.d.ts +29 -0
  167. package/dist/agent/runtime/remote_resources.js +160 -0
  168. package/dist/agent/runtime/source_verification.d.ts +12 -0
  169. package/dist/agent/runtime/source_verification.js +210 -0
  170. package/dist/agent/runtime/source_verification_contract.d.ts +75 -0
  171. package/dist/agent/runtime/source_verification_contract.js +203 -0
  172. package/dist/agent/runtime/tool_checkpoints.d.ts +11 -0
  173. package/dist/agent/runtime/tool_checkpoints.js +26 -0
  174. package/dist/agent/runtime/tool_effects.d.ts +5 -0
  175. package/dist/agent/runtime/tool_progress.d.ts +12 -0
  176. package/dist/agent/runtime/tool_progress.js +76 -0
  177. package/dist/agent/runtime/tool_runtime.d.ts +16 -1
  178. package/dist/agent/runtime/tool_runtime.js +204 -10
  179. package/dist/agent/runtime/tracing.d.ts +1 -1
  180. package/dist/agent/runtime/verifier_capture.d.ts +9 -0
  181. package/dist/agent/runtime/verifier_capture.js +100 -0
  182. package/dist/agent/runtime/verifier_contract.d.ts +24 -0
  183. package/dist/agent/runtime/verifier_contract.js +41 -0
  184. package/dist/agent/subagent.d.ts +20 -10
  185. package/dist/agent/subagent.js +266 -29
  186. package/dist/agent/tool_response_directory.d.ts +24 -0
  187. package/dist/agent/tool_response_directory.js +61 -0
  188. package/dist/agent/tool_response_source.d.ts +8 -0
  189. package/dist/agent/tool_response_source.js +49 -0
  190. package/dist/agent/tool_response_store.d.ts +75 -4
  191. package/dist/agent/tool_response_store.js +583 -64
  192. package/dist/agent/tools/ask_user.d.ts +3 -1
  193. package/dist/agent/tools/ask_user.js +30 -11
  194. package/dist/agent/tools/background.js +1 -0
  195. package/dist/agent/tools/benchmark_cpu.d.ts +2 -0
  196. package/dist/agent/tools/benchmark_cpu.js +102 -0
  197. package/dist/agent/tools/compare_benchmarks.d.ts +2 -0
  198. package/dist/agent/tools/compare_benchmarks.js +53 -0
  199. package/dist/agent/tools/deferred.js +1 -0
  200. package/dist/agent/tools/delegate.js +18 -2
  201. package/dist/agent/tools/helper_requirements.d.ts +7 -0
  202. package/dist/agent/tools/helper_requirements.js +19 -0
  203. package/dist/agent/tools/index.d.ts +1 -0
  204. package/dist/agent/tools/index.js +3 -0
  205. package/dist/agent/tools/measurement_report.d.ts +22 -0
  206. package/dist/agent/tools/measurement_report.js +190 -0
  207. package/dist/agent/tools/pane.d.ts +0 -7
  208. package/dist/agent/tools/pane.js +5 -4
  209. package/dist/agent/tools/project_context.d.ts +3 -0
  210. package/dist/agent/tools/project_context.js +45 -0
  211. package/dist/agent/tools/recall_tool_response.d.ts +3 -5
  212. package/dist/agent/tools/recall_tool_response.js +19 -29
  213. package/dist/agent/tools/registry.d.ts +1 -0
  214. package/dist/agent/tools/registry.js +23 -0
  215. package/dist/agent/tools/related_sessions.d.ts +2 -0
  216. package/dist/agent/tools/related_sessions.js +156 -0
  217. package/dist/agent/tools/routine.js +13 -3
  218. package/dist/agent/tools/session_evidence.d.ts +3 -0
  219. package/dist/agent/tools/session_evidence.js +72 -0
  220. package/dist/agent/tools/shell.d.ts +4 -3
  221. package/dist/agent/tools/shell.js +174 -47
  222. package/dist/agent/tools/tool_search.js +3 -2
  223. package/dist/agent/tools/types.d.ts +28 -0
  224. package/dist/agent/tools/verify_artifact.d.ts +2 -0
  225. package/dist/agent/tools/verify_artifact.js +60 -0
  226. package/dist/agent/tools/workflow.js +1 -0
  227. package/dist/app/activity.d.ts +6 -1
  228. package/dist/app/activity.js +81 -12
  229. package/dist/app/agent_runner.d.ts +6 -1
  230. package/dist/app/agent_runner.js +34 -19
  231. package/dist/app/command_context.js +106 -10
  232. package/dist/app/controller.d.ts +15 -4
  233. package/dist/app/controller.js +29 -3
  234. package/dist/app/escape_stop.d.ts +11 -0
  235. package/dist/app/escape_stop.js +15 -0
  236. package/dist/app/liveness.d.ts +6 -4
  237. package/dist/app/liveness.js +13 -3
  238. package/dist/app/model_catalog_cache.d.ts +5 -0
  239. package/dist/app/model_catalog_cache.js +138 -0
  240. package/dist/app/ordered_transcript.js +43 -4
  241. package/dist/app/prompt_turn.d.ts +14 -0
  242. package/dist/app/prompt_turn.js +53 -0
  243. package/dist/app/recap.d.ts +3 -0
  244. package/dist/app/recap.js +9 -3
  245. package/dist/app/restart.d.ts +3 -2
  246. package/dist/app/restart.js +22 -7
  247. package/dist/app/rewind.d.ts +3 -3
  248. package/dist/app/rewind.js +3 -3
  249. package/dist/app/selection_completion.d.ts +17 -2
  250. package/dist/app/selection_completion.js +84 -9
  251. package/dist/app/session_manager.d.ts +38 -6
  252. package/dist/app/session_manager.js +263 -60
  253. package/dist/app/session_messages.d.ts +3 -0
  254. package/dist/app/session_messages.js +60 -0
  255. package/dist/app/task_scope_router.d.ts +20 -0
  256. package/dist/app/task_scope_router.js +173 -0
  257. package/dist/app/transcript_window.d.ts +2 -0
  258. package/dist/app/transcript_window.js +4 -0
  259. package/dist/app/workspace_admission_details.d.ts +3 -0
  260. package/dist/app/workspace_admission_details.js +26 -0
  261. package/dist/app/workspace_layout_controller.d.ts +11 -0
  262. package/dist/app/workspace_layout_controller.js +88 -0
  263. package/dist/app/workspace_monitor.d.ts +1 -0
  264. package/dist/app/workspace_monitor.js +216 -0
  265. package/dist/app/workspace_presets.d.ts +29 -0
  266. package/dist/app/workspace_presets.js +192 -0
  267. package/dist/attention/queue.d.ts +37 -0
  268. package/dist/attention/queue.js +183 -0
  269. package/dist/bench/rf_shared.d.ts +2 -0
  270. package/dist/board/binding.d.ts +42 -0
  271. package/dist/board/binding.js +38 -0
  272. package/dist/board/client.d.ts +42 -0
  273. package/dist/board/client.js +60 -0
  274. package/dist/cli.js +1 -1
  275. package/dist/cli_args.js +19 -17
  276. package/dist/cli_main.js +110 -13
  277. package/dist/cli_ratatui.d.ts +183 -16
  278. package/dist/cli_ratatui.js +3989 -1255
  279. package/dist/components/ActivityLine.d.ts +3 -2
  280. package/dist/components/ActivityLine.js +2 -2
  281. package/dist/components/InputArea.d.ts +19 -1
  282. package/dist/components/InputArea.js +264 -190
  283. package/dist/components/MessageList.d.ts +2 -0
  284. package/dist/components/MessageList.js +3 -1
  285. package/dist/components/SingleMessage.d.ts +2 -0
  286. package/dist/components/SingleMessage.js +4 -2
  287. package/dist/components/StatusBar.d.ts +4 -2
  288. package/dist/components/StatusBar.js +3 -1
  289. package/dist/conductor_branch.js +3 -0
  290. package/dist/config/api_retry.d.ts +6 -1
  291. package/dist/config/api_retry.js +19 -1
  292. package/dist/config/defaults.d.ts +2 -1
  293. package/dist/config/defaults.js +9 -2
  294. package/dist/config/message_display.d.ts +1 -0
  295. package/dist/config/message_display.js +17 -2
  296. package/dist/config/permissions.d.ts +21 -0
  297. package/dist/config/permissions.js +89 -0
  298. package/dist/config/resolved.d.ts +2 -2
  299. package/dist/config/types.d.ts +32 -5
  300. package/dist/config.d.ts +38 -4
  301. package/dist/config.js +290 -42
  302. package/dist/context/project_intelligence.d.ts +44 -0
  303. package/dist/context/project_intelligence.js +246 -0
  304. package/dist/desktop/cli_launch.d.ts +8 -0
  305. package/dist/desktop/cli_launch.js +6 -0
  306. package/dist/desktop/cli_shim.d.ts +8 -0
  307. package/dist/desktop/cli_shim.js +120 -0
  308. package/dist/desktop/runtime.d.ts +16 -0
  309. package/dist/desktop/runtime.js +161 -0
  310. package/dist/desktop/worker_launch.d.ts +7 -0
  311. package/dist/desktop/worker_launch.js +7 -0
  312. package/dist/diagnostics/cargo_mutants_selection.d.ts +27 -0
  313. package/dist/diagnostics/cargo_mutants_selection.js +48 -0
  314. package/dist/diagnostics/native_terminal_benchmark.d.ts +58 -0
  315. package/dist/diagnostics/native_terminal_benchmark.js +1247 -0
  316. package/dist/diagnostics/session_evidence_package.d.ts +40 -0
  317. package/dist/diagnostics/session_evidence_package.js +234 -0
  318. package/dist/diagnostics/session_export.d.ts +93 -0
  319. package/dist/diagnostics/session_export.js +634 -0
  320. package/dist/emulation/tool_adapters.js +48 -18
  321. package/dist/emulation/ztc_addendum.d.ts +1 -0
  322. package/dist/emulation/ztc_addendum.js +13 -12
  323. package/dist/environment/service_runner.d.ts +1 -0
  324. package/dist/environment/service_runner.js +68 -0
  325. package/dist/environment/store.d.ts +120 -0
  326. package/dist/environment/store.js +387 -0
  327. package/dist/events.d.ts +40 -1
  328. package/dist/events.js +10 -17
  329. package/dist/goal_contract.d.ts +25 -0
  330. package/dist/goal_contract.js +44 -0
  331. package/dist/headless.d.ts +31 -0
  332. package/dist/headless.js +578 -70
  333. package/dist/headless_cli.js +8 -0
  334. package/dist/headless_cli_args.d.ts +1 -0
  335. package/dist/headless_cli_args.js +1 -0
  336. package/dist/intake/cli.d.ts +1 -0
  337. package/dist/intake/cli.js +33 -0
  338. package/dist/intake/format.d.ts +2 -0
  339. package/dist/intake/format.js +14 -0
  340. package/dist/intake/notifications.d.ts +16 -0
  341. package/dist/intake/notifications.js +132 -0
  342. package/dist/intake/store.d.ts +56 -0
  343. package/dist/intake/store.js +238 -0
  344. package/dist/library.d.ts +8 -0
  345. package/dist/library.js +19 -10
  346. package/dist/mcp/client.d.ts +1 -0
  347. package/dist/mcp/client.js +20 -0
  348. package/dist/modes/builtin/plan.yaml +2 -2
  349. package/dist/modes/handlers.js +1 -1
  350. package/dist/onboarding/first_run.d.ts +3 -0
  351. package/dist/onboarding/first_run.js +33 -25
  352. package/dist/onboarding/setup.d.ts +32 -0
  353. package/dist/onboarding/setup.js +133 -0
  354. package/dist/onboarding/setup_agent_health.d.ts +17 -0
  355. package/dist/onboarding/setup_agent_health.js +79 -0
  356. package/dist/onboarding/setup_environment.d.ts +66 -0
  357. package/dist/onboarding/setup_environment.js +219 -0
  358. package/dist/onboarding/setup_workspace.d.ts +17 -0
  359. package/dist/onboarding/setup_workspace.js +129 -0
  360. package/dist/optimize/adapters/index.d.ts +4 -1
  361. package/dist/optimize/adapters/index.js +7 -0
  362. package/dist/optimize/adapters/kernelbench.js +14 -5
  363. package/dist/optimize/adapters/popcorn.js +3 -2
  364. package/dist/optimize/adapters/profile_benchmark.js +13 -7
  365. package/dist/optimize/adapters/rfbench.js +25 -14
  366. package/dist/optimize/adapters/types.d.ts +3 -0
  367. package/dist/optimize/adapters/zergpod.js +5 -2
  368. package/dist/optimize/baseline.js +10 -21
  369. package/dist/optimize/branch.js +9 -12
  370. package/dist/optimize/combine.js +2 -3
  371. package/dist/optimize/executor.js +34 -48
  372. package/dist/optimize/measurements.d.ts +55 -0
  373. package/dist/optimize/measurements.js +173 -0
  374. package/dist/optimize/results.d.ts +1 -0
  375. package/dist/optimize/results.js +11 -33
  376. package/dist/optimize/tge.js +2 -1
  377. package/dist/panes/initial_input.d.ts +19 -0
  378. package/dist/panes/initial_input.js +206 -0
  379. package/dist/panes/labels.d.ts +16 -0
  380. package/dist/panes/labels.js +42 -0
  381. package/dist/panes/model_catalog.d.ts +3 -3
  382. package/dist/panes/model_catalog.js +28 -4
  383. package/dist/panes/pane_registry.js +6 -2
  384. package/dist/panes/types.d.ts +3 -0
  385. package/dist/panes/ztc_launch.js +19 -0
  386. package/dist/plugin/optimization.d.ts +2 -0
  387. package/dist/plugin/optimization.js +1 -0
  388. package/dist/plugin/runtime.d.ts +2 -0
  389. package/dist/plugin/simulation.d.ts +6 -1
  390. package/dist/plugin/simulation_contract.d.ts +836 -1
  391. package/dist/plugin/simulation_documents.d.ts +22 -0
  392. package/dist/plugin/simulation_documents.js +1 -0
  393. package/dist/plugin/simulation_interventions.d.ts +103 -0
  394. package/dist/plugin/simulation_interventions.js +1 -0
  395. package/dist/plugin/simulation_living.d.ts +1 -0
  396. package/dist/plugin/simulation_living.js +3 -0
  397. package/dist/plugin/simulation_meetings.d.ts +121 -0
  398. package/dist/plugin/simulation_meetings.js +1 -0
  399. package/dist/plugin/simulation_story_review.d.ts +111 -0
  400. package/dist/plugin/simulation_story_review.js +1 -0
  401. package/dist/plugin/simulation_storyteller.d.ts +91 -0
  402. package/dist/plugin/simulation_storyteller.js +1 -0
  403. package/dist/plugin/simulation_structured.d.ts +2 -0
  404. package/dist/plugin/simulation_structured.js +2 -0
  405. package/dist/plugin/simulation_toolkit.d.ts +21 -0
  406. package/dist/plugin/simulation_toolkit.js +24 -0
  407. package/dist/prompts/tool_delegate_task.j2 +1 -1
  408. package/dist/remote-broker/Dockerfile +10 -0
  409. package/dist/remote-broker/archive.py +1227 -0
  410. package/dist/remote-broker/cutover.py +761 -0
  411. package/dist/remote-broker/requirements.txt +5 -0
  412. package/dist/remote-broker/resources.json +6 -0
  413. package/dist/remote-broker/resources.write-staging.example.json +18 -0
  414. package/dist/remote-broker/retained.py +686 -0
  415. package/dist/remote-broker/retained_cleanup.py +550 -0
  416. package/dist/remote-broker/server.py +648 -0
  417. package/dist/remote-broker/worker.py +1007 -0
  418. package/dist/router_auth.d.ts +31 -0
  419. package/dist/router_auth.js +155 -9
  420. package/dist/runbooks/catalog.d.ts +3 -0
  421. package/dist/runbooks/catalog.js +26 -0
  422. package/dist/runbooks/library.d.ts +11 -0
  423. package/dist/runbooks/library.js +95 -0
  424. package/dist/runbooks/package.d.ts +43 -0
  425. package/dist/runbooks/package.js +102 -0
  426. package/dist/session.d.ts +65 -13
  427. package/dist/session.js +548 -43
  428. package/dist/session_handoff.d.ts +25 -0
  429. package/dist/session_handoff.js +322 -0
  430. package/dist/session_handoff_contract.d.ts +89 -0
  431. package/dist/session_handoff_contract.js +179 -0
  432. package/dist/session_media.d.ts +41 -0
  433. package/dist/session_media.js +357 -0
  434. package/dist/session_ownership.d.ts +16 -0
  435. package/dist/session_ownership.js +221 -0
  436. package/dist/session_public.d.ts +10 -2
  437. package/dist/session_public.js +6 -1
  438. package/dist/simulation/dynamic_history.d.ts +42 -0
  439. package/dist/simulation/dynamic_history.js +173 -0
  440. package/dist/simulation/engines/swarm/pane_manager.js +7 -6
  441. package/dist/simulation/inference.d.ts +3 -1
  442. package/dist/simulation/inference.js +19 -1
  443. package/dist/simulation/intervention_generation.d.ts +27 -0
  444. package/dist/simulation/intervention_generation.js +246 -0
  445. package/dist/simulation/intervention_material.d.ts +4 -0
  446. package/dist/simulation/intervention_material.js +81 -0
  447. package/dist/simulation/living/context.d.ts +3130 -0
  448. package/dist/simulation/living/context.js +944 -0
  449. package/dist/simulation/living/definer.d.ts +12 -0
  450. package/dist/simulation/living/definer.js +223 -0
  451. package/dist/simulation/living/definition.d.ts +257 -0
  452. package/dist/simulation/living/definition.js +341 -0
  453. package/dist/simulation/living/frame.d.ts +259 -0
  454. package/dist/simulation/living/frame.js +223 -0
  455. package/dist/simulation/living/identifiers.d.ts +2 -0
  456. package/dist/simulation/living/identifiers.js +12 -0
  457. package/dist/simulation/living/index.d.ts +15 -0
  458. package/dist/simulation/living/index.js +15 -0
  459. package/dist/simulation/living/inference.d.ts +219 -0
  460. package/dist/simulation/living/inference.js +576 -0
  461. package/dist/simulation/living/mechanics.d.ts +54 -0
  462. package/dist/simulation/living/mechanics.js +588 -0
  463. package/dist/simulation/living/model-input.d.ts +25 -0
  464. package/dist/simulation/living/model-input.js +215 -0
  465. package/dist/simulation/living/open-world.d.ts +257 -0
  466. package/dist/simulation/living/open-world.js +102 -0
  467. package/dist/simulation/living/projection.d.ts +7 -0
  468. package/dist/simulation/living/projection.js +31 -0
  469. package/dist/simulation/living/recall.d.ts +17 -0
  470. package/dist/simulation/living/recall.js +25 -0
  471. package/dist/simulation/living/reduce.d.ts +15 -0
  472. package/dist/simulation/living/reduce.js +1853 -0
  473. package/dist/simulation/living/replay.d.ts +6 -0
  474. package/dist/simulation/living/replay.js +22 -0
  475. package/dist/simulation/living/runtime.d.ts +140 -0
  476. package/dist/simulation/living/runtime.js +3020 -0
  477. package/dist/simulation/living/shared-kinds.d.ts +24 -0
  478. package/dist/simulation/living/shared-kinds.js +96 -0
  479. package/dist/simulation/living/types.d.ts +2785 -0
  480. package/dist/simulation/living/types.js +669 -0
  481. package/dist/simulation/llm.d.ts +3 -1
  482. package/dist/simulation/llm.js +90 -30
  483. package/dist/simulation/memory/embedding.d.ts +1 -1
  484. package/dist/simulation/memory/embedding.js +9 -3
  485. package/dist/simulation/memory/filesystem.d.ts +2 -0
  486. package/dist/simulation/memory/filesystem.js +5 -1
  487. package/dist/simulation/memory/filesystem_file_tree.d.ts +4 -1
  488. package/dist/simulation/memory/filesystem_file_tree.js +41 -10
  489. package/dist/simulation/memory/filesystem_invariants.js +1 -2
  490. package/dist/simulation/memory/filesystem_types.d.ts +1 -0
  491. package/dist/simulation/memory/memory.d.ts +2 -0
  492. package/dist/simulation/memory/memory.js +5 -1
  493. package/dist/simulation/orchestration/intent.js +0 -2
  494. package/dist/simulation/orchestration/lifecycle.js +0 -2
  495. package/dist/simulation/plugin_host.d.ts +25 -1
  496. package/dist/simulation/plugin_host.js +334 -17
  497. package/dist/simulation/session_documents.d.ts +20 -0
  498. package/dist/simulation/session_documents.js +62 -0
  499. package/dist/simulation/session_input_validation.d.ts +43 -0
  500. package/dist/simulation/session_input_validation.js +364 -0
  501. package/dist/simulation/session_inputs.d.ts +32 -0
  502. package/dist/simulation/session_inputs.js +154 -0
  503. package/dist/simulation/telemetry.d.ts +11 -14
  504. package/dist/simulation/telemetry.js +177 -54
  505. package/dist/simulation/toolkit/bounded_turns.d.ts +13 -0
  506. package/dist/simulation/toolkit/bounded_turns.js +80 -0
  507. package/dist/simulation/toolkit/dynamic_agents.d.ts +25 -0
  508. package/dist/simulation/toolkit/dynamic_agents.js +218 -0
  509. package/dist/simulation/toolkit/dynamic_digest.d.ts +1 -0
  510. package/dist/simulation/toolkit/dynamic_digest.js +8 -0
  511. package/dist/simulation/toolkit/dynamic_documents.d.ts +7 -0
  512. package/dist/simulation/toolkit/dynamic_documents.js +71 -0
  513. package/dist/simulation/toolkit/dynamic_driver.d.ts +8 -0
  514. package/dist/simulation/toolkit/dynamic_driver.js +269 -0
  515. package/dist/simulation/toolkit/dynamic_game.d.ts +53 -0
  516. package/dist/simulation/toolkit/dynamic_game.js +805 -0
  517. package/dist/simulation/toolkit/dynamic_knowledge.d.ts +15 -0
  518. package/dist/simulation/toolkit/dynamic_knowledge.js +91 -0
  519. package/dist/simulation/toolkit/dynamic_mechanics.d.ts +22 -0
  520. package/dist/simulation/toolkit/dynamic_mechanics.js +910 -0
  521. package/dist/simulation/toolkit/dynamic_meetings.d.ts +239 -0
  522. package/dist/simulation/toolkit/dynamic_meetings.js +350 -0
  523. package/dist/simulation/toolkit/dynamic_progress.d.ts +131 -0
  524. package/dist/simulation/toolkit/dynamic_progress.js +214 -0
  525. package/dist/simulation/toolkit/dynamic_projection.d.ts +9 -0
  526. package/dist/simulation/toolkit/dynamic_projection.js +219 -0
  527. package/dist/simulation/toolkit/dynamic_runtime.d.ts +412 -0
  528. package/dist/simulation/toolkit/dynamic_runtime.js +6536 -0
  529. package/dist/simulation/toolkit/dynamic_state.d.ts +111 -0
  530. package/dist/simulation/toolkit/dynamic_state.js +1967 -0
  531. package/dist/simulation/toolkit/dynamic_story_review.d.ts +508 -0
  532. package/dist/simulation/toolkit/dynamic_story_review.js +333 -0
  533. package/dist/simulation/toolkit/dynamic_storyteller.d.ts +77 -0
  534. package/dist/simulation/toolkit/dynamic_storyteller.js +114 -0
  535. package/dist/simulation/toolkit/dynamic_storyteller_runtime.d.ts +26 -0
  536. package/dist/simulation/toolkit/dynamic_storyteller_runtime.js +126 -0
  537. package/dist/simulation/toolkit/dynamic_world.d.ts +22 -0
  538. package/dist/simulation/toolkit/dynamic_world.js +377 -0
  539. package/dist/simulation/toolkit/structured_output.d.ts +5 -0
  540. package/dist/simulation/toolkit/structured_output.js +321 -0
  541. package/dist/simulation/types/world.d.ts +2 -0
  542. package/dist/simulation/world/artifact_store.js +1 -1
  543. package/dist/simulation/world/memory_manager.d.ts +5 -0
  544. package/dist/simulation/world/memory_manager.js +17 -2
  545. package/dist/simulation/world/state.d.ts +3 -2
  546. package/dist/simulation/world/state.js +15 -5
  547. package/dist/skills/bundled/measurement-validation/SKILL.md +144 -0
  548. package/dist/terminal_attach.d.ts +1 -0
  549. package/dist/terminal_attach.js +113 -0
  550. package/dist/tool_progress.d.ts +3 -0
  551. package/dist/tool_progress.js +27 -0
  552. package/dist/types.d.ts +145 -10
  553. package/dist/types.js +3 -0
  554. package/dist/ui/core/input.d.ts +3 -0
  555. package/dist/ui/core/input_segments.d.ts +18 -1
  556. package/dist/ui/core/input_segments.js +131 -47
  557. package/dist/ui/core/input_state.d.ts +12 -0
  558. package/dist/ui/core/layout_yoga.d.ts +1 -1
  559. package/dist/ui/core/turn_metadata.d.ts +5 -0
  560. package/dist/ui/core/turn_metadata.js +22 -0
  561. package/dist/ui/ink/render.d.ts +2 -1
  562. package/dist/ui/ink/render.js +6 -5
  563. package/dist/ui/ratatui/headless_pane_manager.d.ts +14 -1
  564. package/dist/ui/ratatui/headless_pane_manager.js +293 -104
  565. package/dist/ui/ratatui/index.d.ts +1 -0
  566. package/dist/ui/ratatui/input.d.ts +4 -0
  567. package/dist/ui/ratatui/input.js +15 -0
  568. package/dist/ui/ratatui/ipc_path.d.ts +2 -0
  569. package/dist/ui/ratatui/ipc_path.js +29 -1
  570. package/dist/ui/ratatui/mux_persistence.d.ts +3 -1
  571. package/dist/ui/ratatui/mux_persistence.js +139 -1
  572. package/dist/ui/ratatui/pane_relay.d.ts +1 -1
  573. package/dist/ui/ratatui/pane_relay.js +55 -27
  574. package/dist/ui/ratatui/process.d.ts +17 -1
  575. package/dist/ui/ratatui/process.js +80 -7
  576. package/dist/ui/ratatui/protocol.d.ts +236 -1
  577. package/dist/ui/ratatui/protocol.js +90 -0
  578. package/dist/ui/scrollback.d.ts +2 -1
  579. package/dist/ui/scrollback.js +9 -4
  580. package/dist/ui/stream_typewriter.d.ts +8 -3
  581. package/dist/ui/stream_typewriter.js +30 -1
  582. package/dist/ui/streaming_scrollback.d.ts +1 -0
  583. package/dist/ui/streaming_scrollback.js +1 -1
  584. package/dist/ui/transcript_render_cache.js +10 -0
  585. package/dist/ui/views/activity_line.d.ts +5 -3
  586. package/dist/ui/views/activity_line.js +65 -7
  587. package/dist/ui/views/app.d.ts +6 -3
  588. package/dist/ui/views/app.js +17 -6
  589. package/dist/ui/views/completion_menu.js +10 -5
  590. package/dist/ui/views/input_area.d.ts +7 -3
  591. package/dist/ui/views/input_area.js +15 -9
  592. package/dist/ui/views/message_list.d.ts +4 -1
  593. package/dist/ui/views/message_list.js +89 -19
  594. package/dist/ui/views/queue_panel.d.ts +3 -1
  595. package/dist/ui/views/queue_panel.js +9 -2
  596. package/dist/ui/views/status_bar.d.ts +4 -2
  597. package/dist/ui/views/status_bar.js +28 -13
  598. package/dist/ui/views/theme.d.ts +2 -0
  599. package/dist/ui/views/theme.js +2 -0
  600. package/dist/ui/widgets/color_panel.js +18 -0
  601. package/dist/ui/widgets/form.js +8 -0
  602. package/dist/ui/widgets/log_view.js +7 -0
  603. package/dist/ui/widgets/navigator_panel.js +8 -0
  604. package/dist/ui/widgets/prompt_viewer.js +2 -0
  605. package/dist/ui/widgets/questionnaire.d.ts +2 -0
  606. package/dist/ui/widgets/questionnaire.js +79 -16
  607. package/dist/ui/widgets/resume_panel.d.ts +2 -0
  608. package/dist/ui/widgets/resume_panel.js +26 -15
  609. package/dist/ui/widgets/rewind_panel.d.ts +2 -2
  610. package/dist/ui/widgets/rewind_panel.js +4 -2
  611. package/dist/ui/widgets/select.js +16 -27
  612. package/dist/ui/widgets/settings_panel.js +16 -0
  613. package/dist/ui/widgets/status_line_panel.d.ts +1 -0
  614. package/dist/ui/widgets/status_line_panel.js +30 -4
  615. package/dist/ui/widgets/text_input.d.ts +2 -0
  616. package/dist/ui/widgets/text_input.js +15 -1
  617. package/dist/ui/widgets/types.d.ts +2 -0
  618. package/dist/usage_projection.d.ts +24 -0
  619. package/dist/usage_projection.js +130 -0
  620. package/dist/usage_snapshot.d.ts +36 -0
  621. package/dist/usage_snapshot.js +131 -0
  622. package/dist/utils/assistant_message.d.ts +2 -0
  623. package/dist/utils/assistant_message.js +5 -0
  624. package/dist/utils/atomic_write.d.ts +6 -2
  625. package/dist/utils/atomic_write.js +29 -4
  626. package/dist/utils/checked_shell.d.ts +25 -0
  627. package/dist/utils/checked_shell.js +107 -0
  628. package/dist/utils/compaction.d.ts +4 -1
  629. package/dist/utils/compaction.js +31 -7
  630. package/dist/utils/context.d.ts +4 -4
  631. package/dist/utils/context.js +14 -12
  632. package/dist/utils/deepseek.d.ts +2 -2
  633. package/dist/utils/deepseek.js +13 -5
  634. package/dist/utils/ephemeral.js +4 -0
  635. package/dist/utils/ephemeral_tools.d.ts +18 -0
  636. package/dist/utils/ephemeral_tools.js +97 -0
  637. package/dist/utils/jev_client.d.ts +4 -0
  638. package/dist/utils/jev_client.js +15 -0
  639. package/dist/utils/model_cache.d.ts +15 -0
  640. package/dist/utils/model_cache.js +143 -0
  641. package/dist/utils/models.d.ts +4 -2
  642. package/dist/utils/models.js +12 -16
  643. package/dist/utils/reasoning.d.ts +8 -1
  644. package/dist/utils/reasoning.js +31 -11
  645. package/dist/utils/router_catalog.d.ts +49 -0
  646. package/dist/utils/router_catalog.js +271 -0
  647. package/dist/utils/sandbox.d.ts +9 -0
  648. package/dist/utils/sandbox.js +118 -15
  649. package/dist/utils/shell.d.ts +7 -0
  650. package/dist/utils/shell.js +98 -34
  651. package/dist/utils/subprocess_env.d.ts +3 -0
  652. package/dist/utils/subprocess_env.js +27 -1
  653. package/dist/web/accounts_mirror.d.ts +3 -1
  654. package/dist/web/accounts_mirror.js +13 -9
  655. package/dist/web/child_publication.d.ts +17 -0
  656. package/dist/web/child_publication.js +45 -0
  657. package/dist/web/mirror_authenticator.d.ts +31 -11
  658. package/dist/web/mirror_authenticator.js +67 -17
  659. package/dist/web/mirror_client.d.ts +31 -4
  660. package/dist/web/mirror_client.js +189 -23
  661. package/dist/web/mirror_stream.d.ts +84 -22
  662. package/dist/web/mirror_stream.js +676 -95
  663. package/dist/web/observer_query_redaction.d.ts +6 -0
  664. package/dist/web/observer_query_redaction.js +58 -0
  665. package/dist/web/pane_registry.d.ts +2 -0
  666. package/dist/web/pane_registry.js +86 -22
  667. package/dist/web/tool_image.d.ts +6 -0
  668. package/dist/web/tool_image.js +17 -0
  669. package/dist/web/view_plugin.d.ts +14 -0
  670. package/dist/web/view_plugin.js +23 -1
  671. package/dist/worker/executor.js +31 -12
  672. package/dist/worker/index.d.ts +1 -0
  673. package/dist/worker/toolsets.js +3 -1
  674. package/dist/worker/types.d.ts +12 -1
  675. package/dist/workflows/registry.js +1 -0
  676. package/dist/workflows/registry_handle.d.ts +2 -0
  677. package/dist/workflows/runtime.d.ts +2 -0
  678. package/dist/workflows/runtime.js +1 -0
  679. package/dist/workspace_agent.d.ts +7 -0
  680. package/dist/workspace_agent.js +36 -0
  681. package/dist/workspace_collision.d.ts +76 -0
  682. package/dist/workspace_collision.js +688 -0
  683. package/dist/workspace_layout.d.ts +18 -0
  684. package/dist/workspace_layout.js +121 -0
  685. package/dist/workspace_runtime.d.ts +44 -0
  686. package/dist/workspace_runtime.js +82 -0
  687. package/dist/workspace_session_context.d.ts +62 -0
  688. package/dist/workspace_session_context.js +160 -0
  689. package/dist/zlm-embed.mjs +32518 -9615
  690. package/dist/zlm.js +1 -0
  691. package/node_modules/@zerg/model-contracts/LICENSE.md +375 -0
  692. package/node_modules/@zerg/model-contracts/README.md +90 -0
  693. package/node_modules/@zerg/model-contracts/index.d.ts +20 -0
  694. package/node_modules/@zerg/model-contracts/index.js +223 -0
  695. package/node_modules/@zerg/model-contracts/package.json +8 -0
  696. package/node_modules/glob/node_modules/brace-expansion/index.js +78 -22
  697. package/node_modules/glob/node_modules/brace-expansion/package.json +1 -1
  698. package/node_modules/readdir-glob/node_modules/brace-expansion/index.js +78 -22
  699. package/node_modules/readdir-glob/node_modules/brace-expansion/package.json +1 -1
  700. package/package.json +45 -24
  701. package/packages/model-contracts/LICENSE.md +375 -0
  702. package/packages/model-contracts/README.md +90 -0
  703. package/packages/model-contracts/index.d.ts +20 -0
  704. package/packages/model-contracts/index.js +223 -0
  705. package/packages/model-contracts/package.json +8 -0
  706. package/remote-broker/Dockerfile +10 -0
  707. package/remote-broker/README.md +308 -0
  708. package/remote-broker/archive.py +1227 -0
  709. package/remote-broker/cutover.py +761 -0
  710. package/remote-broker/requirements.txt +5 -0
  711. package/remote-broker/resources.example.json +6 -0
  712. package/remote-broker/resources.write-staging.example.json +18 -0
  713. package/remote-broker/retained.py +686 -0
  714. package/remote-broker/retained_cleanup.py +550 -0
  715. package/remote-broker/server.py +648 -0
  716. package/scripts/build-remote-broker.mjs +13 -0
  717. package/scripts/ensure-local-packages.mjs +6 -0
  718. package/scripts/node-pty-helpers.mjs +103 -0
@@ -0,0 +1,41 @@
1
+ import type { Message, MessageAttachment } from './types.js';
2
+ export declare const SESSION_MEDIA_LIMITS: Readonly<{
3
+ imageBytes: number;
4
+ sessionBytes: number;
5
+ assetCount: 128;
6
+ /** Bound standalone pane-input metadata, not a session's text transcript. */
7
+ checkpointBytes: number;
8
+ }>;
9
+ export type SessionMediaErrorCode = 'invalid_reference' | 'unavailable' | 'quota_exceeded' | 'write_failed';
10
+ export declare class SessionMediaError extends Error {
11
+ readonly code: SessionMediaErrorCode;
12
+ constructor(code: SessionMediaErrorCode, message: string, cause?: unknown);
13
+ }
14
+ export interface DurableSessionImage {
15
+ localPath: string;
16
+ mediaType: string;
17
+ data: Buffer;
18
+ }
19
+ export interface PreparedSessionMedia {
20
+ /** Detached messages: the caller's live transcript is never modified during preparation. */
21
+ messages: Message[];
22
+ /** Call immediately after the canonical checkpoint rename succeeds. */
23
+ markCommitted(): void;
24
+ /** Idempotent; after commitment this deliberately retains every owned image. */
25
+ rollback(): Promise<void>;
26
+ }
27
+ /**
28
+ * A durable ref is authority only when its actual checkpoint contains that exact
29
+ * attachment. Paths are derived from the owner sidecar; localPath is never trusted.
30
+ */
31
+ export declare function readDurableSessionImageSync(attachment: MessageAttachment): DurableSessionImage | null;
32
+ /**
33
+ * Stage immutable owner copies before the JSON rename. Existing generations are
34
+ * retained for the session lifetime; another checkpoint/queue source may use them.
35
+ * Public session writers serialize this transaction in-process with withPathLock.
36
+ */
37
+ export declare function prepareSessionMedia(options: {
38
+ sessionFile: string;
39
+ sessionId: string;
40
+ messages: readonly Message[];
41
+ }): Promise<PreparedSessionMedia>;
@@ -0,0 +1,357 @@
1
+ import { createHash, randomUUID } from 'node:crypto';
2
+ import { closeSync, constants, fstatSync, lstatSync, openSync, readSync, realpathSync, } from 'node:fs';
3
+ import { mkdir, open, opendir, rm } from 'node:fs/promises';
4
+ import { basename, dirname, isAbsolute, join, resolve, sep } from 'node:path';
5
+ import { isMirrorMediaPath, MIRROR_MEDIA_ROOT } from './utils/mirror_media.js';
6
+ export const SESSION_MEDIA_LIMITS = Object.freeze({
7
+ imageBytes: 10 * 1024 * 1024,
8
+ sessionBytes: 100 * 1024 * 1024,
9
+ assetCount: 128,
10
+ /** Bound standalone pane-input metadata, not a session's text transcript. */
11
+ checkpointBytes: 4 * 1024 * 1024,
12
+ });
13
+ export class SessionMediaError extends Error {
14
+ code;
15
+ constructor(code, message, cause) {
16
+ super(message, { cause });
17
+ this.code = code;
18
+ this.name = 'SessionMediaError';
19
+ }
20
+ }
21
+ const GENERATION = /^g-[0-9a-f]{32}$/u;
22
+ const BLOB = /^([0-9a-f]{64})\.(png|jpg|gif|webp)$/u;
23
+ const FILE = /^(g-[0-9a-f]{32})\/([0-9a-f]{64})\.(png|jpg|gif|webp)$/u;
24
+ const MAX_DIRECTORY_ENTRIES = 4096;
25
+ function record(value) {
26
+ return value !== null && typeof value === 'object' && !Array.isArray(value);
27
+ }
28
+ function canonicalCheckpoint(path) {
29
+ if (!isAbsolute(path) || /[\u0000-\u001f\u007f]/u.test(path)) {
30
+ throw new SessionMediaError('invalid_reference', 'Image checkpoint must be an absolute canonical file path.');
31
+ }
32
+ return join(realpathSync(dirname(path)), basename(path));
33
+ }
34
+ function imageType(data) {
35
+ if (data.length >= 8 && data.subarray(0, 8).equals(Buffer.from([0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a]))) {
36
+ return { mediaType: 'image/png', extension: 'png' };
37
+ }
38
+ if (data.length >= 3 && data[0] === 0xff && data[1] === 0xd8 && data[2] === 0xff) {
39
+ return { mediaType: 'image/jpeg', extension: 'jpg' };
40
+ }
41
+ if (data.length >= 6 && ['GIF87a', 'GIF89a'].includes(data.subarray(0, 6).toString('ascii'))) {
42
+ return { mediaType: 'image/gif', extension: 'gif' };
43
+ }
44
+ if (data.length >= 12 && data.subarray(0, 4).toString('ascii') === 'RIFF'
45
+ && data.subarray(8, 12).toString('ascii') === 'WEBP') {
46
+ return { mediaType: 'image/webp', extension: 'webp' };
47
+ }
48
+ return null;
49
+ }
50
+ function sha256(data) {
51
+ return createHash('sha256').update(data).digest('hex');
52
+ }
53
+ function checkedDirectory(path) {
54
+ const stat = lstatSync(path);
55
+ if (!stat.isDirectory() || stat.isSymbolicLink()) {
56
+ throw new SessionMediaError('invalid_reference', 'Image storage directory is not an owned regular directory.');
57
+ }
58
+ }
59
+ /** Reads use one regular file; transcript reads stop at its admitted size. */
60
+ function boundedRead(path, maximum) {
61
+ const before = lstatSync(path);
62
+ if (!before.isFile() || before.isSymbolicLink()) {
63
+ throw new SessionMediaError('invalid_reference', 'Image or checkpoint reference is not a regular nonsymlink file.');
64
+ }
65
+ const fd = openSync(path, constants.O_RDONLY | (constants.O_NOFOLLOW ?? 0));
66
+ try {
67
+ const stat = fstatSync(fd);
68
+ if (!stat.isFile() || stat.dev !== before.dev || stat.ino !== before.ino) {
69
+ throw new SessionMediaError('invalid_reference', 'Image reference changed while it was being opened.');
70
+ }
71
+ // Session loading already admits the complete JSON transcript. Media proof
72
+ // must read that same checkpoint, without charging text against asset quotas.
73
+ // Snapshot the opened file size so concurrent growth cannot make this read
74
+ // unbounded. Individual images retain their explicit byte limit.
75
+ maximum ??= stat.size;
76
+ if (stat.size > maximum) {
77
+ throw new SessionMediaError('quota_exceeded', `Image or checkpoint exceeds its ${maximum}-byte read limit.`);
78
+ }
79
+ const parts = [];
80
+ let length = 0;
81
+ while (length <= maximum) {
82
+ const chunk = Buffer.alloc(Math.min(64 * 1024, maximum + 1 - length));
83
+ const count = readSync(fd, chunk, 0, chunk.length, null);
84
+ if (count === 0)
85
+ break;
86
+ parts.push(chunk.subarray(0, count));
87
+ length += count;
88
+ }
89
+ if (length > maximum) {
90
+ throw new SessionMediaError('quota_exceeded', `Image or checkpoint exceeds its ${maximum}-byte read limit.`);
91
+ }
92
+ return Buffer.concat(parts, length);
93
+ }
94
+ finally {
95
+ closeSync(fd);
96
+ }
97
+ }
98
+ function checkpointAttachments(path, sessionId) {
99
+ const parsed = JSON.parse(boundedRead(path).toString('utf8'));
100
+ if (!record(parsed) || !record(parsed.metadata) || typeof parsed.metadata.id !== 'string'
101
+ || !Array.isArray(parsed.messages)) {
102
+ throw new SessionMediaError('invalid_reference', 'Image reference checkpoint is malformed.');
103
+ }
104
+ // A caller replacing an export with a different logical session must not
105
+ // inherit the former session's missing-upload compatibility exception.
106
+ if (sessionId !== undefined && parsed.metadata.id !== sessionId)
107
+ return [];
108
+ const attachments = [];
109
+ for (const message of parsed.messages) {
110
+ if (!record(message) || !Array.isArray(message.attachments))
111
+ continue;
112
+ for (const attachment of message.attachments) {
113
+ if (record(attachment))
114
+ attachments.push(attachment);
115
+ }
116
+ }
117
+ return attachments;
118
+ }
119
+ function validReference(value) {
120
+ return record(value) && Object.keys(value).length === 4 && value.version === 1
121
+ && typeof value.sessionFile === 'string' && typeof value.file === 'string'
122
+ && typeof value.sha256 === 'string' && /^[0-9a-f]{64}$/u.test(value.sha256)
123
+ && FILE.test(value.file) && value.file.split('/')[1].startsWith(value.sha256 + '.');
124
+ }
125
+ function sameReference(first, second) {
126
+ return validReference(second) && first.version === second.version && first.sessionFile === second.sessionFile
127
+ && first.file === second.file && first.sha256 === second.sha256;
128
+ }
129
+ function sameAttachment(first, second) {
130
+ return first.id === second.id && first.kind === second.kind && first.mediaType === second.mediaType
131
+ && first.size === second.size;
132
+ }
133
+ /**
134
+ * A durable ref is authority only when its actual checkpoint contains that exact
135
+ * attachment. Paths are derived from the owner sidecar; localPath is never trusted.
136
+ */
137
+ export function readDurableSessionImageSync(attachment) {
138
+ if (attachment.durable === undefined)
139
+ return null;
140
+ try {
141
+ const ref = attachment.durable;
142
+ if (!validReference(ref) || attachment.kind !== 'image' || typeof attachment.id !== 'string'
143
+ || !Number.isSafeInteger(attachment.size) || attachment.size <= 0
144
+ || attachment.size > SESSION_MEDIA_LIMITS.imageBytes) {
145
+ throw new SessionMediaError('invalid_reference', 'Saved image reference is malformed or exceeds its bounds.');
146
+ }
147
+ const checkpoint = canonicalCheckpoint(ref.sessionFile);
148
+ if (checkpoint !== ref.sessionFile) {
149
+ throw new SessionMediaError('invalid_reference', 'Saved image checkpoint identity is not canonical.');
150
+ }
151
+ if (!checkpointAttachments(checkpoint).some(saved => sameAttachment(attachment, saved)
152
+ && sameReference(ref, saved.durable))) {
153
+ throw new SessionMediaError('invalid_reference', 'Saved image does not match its original checkpoint attachment.');
154
+ }
155
+ const root = checkpoint + '.media';
156
+ const [generation, file] = ref.file.split('/');
157
+ checkedDirectory(root);
158
+ checkedDirectory(join(root, generation));
159
+ const localPath = join(root, generation, file);
160
+ const data = boundedRead(localPath, SESSION_MEDIA_LIMITS.imageBytes);
161
+ const detected = imageType(data);
162
+ if (!detected || detected.mediaType !== attachment.mediaType || data.length !== attachment.size
163
+ || sha256(data) !== ref.sha256 || !file.endsWith('.' + detected.extension)) {
164
+ throw new SessionMediaError('invalid_reference', 'Saved image bytes, MIME, size or checksum no longer match its checkpoint.');
165
+ }
166
+ return { localPath, mediaType: detected.mediaType, data };
167
+ }
168
+ catch (error) {
169
+ if (error instanceof SessionMediaError)
170
+ throw error;
171
+ throw new SessionMediaError('unavailable', 'Saved image is unavailable; restore its owned sidecar or attach the image again.', error);
172
+ }
173
+ }
174
+ /** Only the private generation/blob grammar is counted; unrelated entries are never deleted. */
175
+ async function physicalImages(root) {
176
+ let directory;
177
+ try {
178
+ checkedDirectory(root);
179
+ directory = await opendir(root);
180
+ }
181
+ catch (error) {
182
+ if (error.code === 'ENOENT')
183
+ return [];
184
+ throw error;
185
+ }
186
+ let observed = 0;
187
+ const countEntry = () => {
188
+ observed += 1;
189
+ if (observed > MAX_DIRECTORY_ENTRIES) {
190
+ throw new SessionMediaError('quota_exceeded', 'Session image directory exceeds its bounded entry scan.');
191
+ }
192
+ };
193
+ const images = [];
194
+ for await (const entry of directory) {
195
+ countEntry();
196
+ if (!GENERATION.test(entry.name))
197
+ continue;
198
+ checkedDirectory(join(root, entry.name));
199
+ const files = await opendir(join(root, entry.name));
200
+ for await (const file of files) {
201
+ countEntry();
202
+ if (!BLOB.test(file.name))
203
+ continue;
204
+ const stat = lstatSync(join(root, entry.name, file.name));
205
+ if (!stat.isFile() || stat.isSymbolicLink()) {
206
+ throw new SessionMediaError('invalid_reference', 'Owned image entry is not a regular nonsymlink file.');
207
+ }
208
+ images.push({ file: entry.name + '/' + file.name, size: stat.size });
209
+ }
210
+ }
211
+ return images;
212
+ }
213
+ function quota(count, size) {
214
+ if (count > SESSION_MEDIA_LIMITS.assetCount) {
215
+ throw new SessionMediaError('quota_exceeded', `Session image count exceeds its ${SESSION_MEDIA_LIMITS.assetCount}-asset quota. Remove an unneeded session or use a fresh session.`);
216
+ }
217
+ if (size > SESSION_MEDIA_LIMITS.sessionBytes) {
218
+ throw new SessionMediaError('quota_exceeded', `Session images exceed their ${SESSION_MEDIA_LIMITS.sessionBytes}-byte quota. Remove an unneeded session or use a fresh session.`);
219
+ }
220
+ }
221
+ function rawMirrorImage(attachment) {
222
+ const path = resolve(attachment.localPath);
223
+ const root = realpathSync(MIRROR_MEDIA_ROOT);
224
+ const actual = realpathSync(path);
225
+ if (!actual.startsWith(root + sep)) {
226
+ throw new SessionMediaError('invalid_reference', 'Uploaded image is outside its admitted mirror storage.');
227
+ }
228
+ // Inspect every component below the known upload root, including the file.
229
+ const relative = path.slice(resolve(MIRROR_MEDIA_ROOT).length + 1).split(sep);
230
+ let cursor = resolve(MIRROR_MEDIA_ROOT);
231
+ for (const segment of relative.slice(0, -1)) {
232
+ cursor = join(cursor, segment);
233
+ checkedDirectory(cursor);
234
+ }
235
+ const data = boundedRead(path, SESSION_MEDIA_LIMITS.imageBytes);
236
+ const detected = imageType(data);
237
+ if (!detected || detected.mediaType !== attachment.mediaType || data.length !== attachment.size) {
238
+ throw new SessionMediaError('invalid_reference', 'Uploaded image bytes do not match their accepted MIME and size.');
239
+ }
240
+ return { localPath: actual, mediaType: detected.mediaType, data };
241
+ }
242
+ /**
243
+ * Stage immutable owner copies before the JSON rename. Existing generations are
244
+ * retained for the session lifetime; another checkpoint/queue source may use them.
245
+ * Public session writers serialize this transaction in-process with withPathLock.
246
+ */
247
+ export async function prepareSessionMedia(options) {
248
+ const checkpoint = canonicalCheckpoint(resolve(options.sessionFile));
249
+ const root = checkpoint + '.media';
250
+ const messages = structuredClone(options.messages);
251
+ let committed = false;
252
+ let generation = null;
253
+ const prepared = {
254
+ messages,
255
+ markCommitted() { committed = true; },
256
+ async rollback() {
257
+ if (committed || generation === null)
258
+ return;
259
+ const owned = generation;
260
+ checkedDirectory(root);
261
+ checkedDirectory(join(root, owned));
262
+ await rm(join(root, owned), { recursive: true });
263
+ generation = null;
264
+ },
265
+ };
266
+ try {
267
+ const applicable = messages.flatMap(message => message.attachments ?? [])
268
+ .filter(attachment => attachment.kind === 'image'
269
+ && (attachment.durable !== undefined || isMirrorMediaPath(attachment.localPath)));
270
+ if (applicable.length === 0)
271
+ return prepared;
272
+ const physical = await physicalImages(root);
273
+ let prior = [];
274
+ try {
275
+ prior = checkpointAttachments(checkpoint, options.sessionId);
276
+ }
277
+ catch (error) {
278
+ if (error.code !== 'ENOENT')
279
+ throw error;
280
+ }
281
+ let physicalCount = physical.length;
282
+ let physicalBytes = physical.reduce((total, asset) => total + asset.size, 0);
283
+ quota(physicalCount, physicalBytes);
284
+ const byBlob = new Map();
285
+ for (const asset of physical)
286
+ byBlob.set(basename(asset.file), asset.file);
287
+ for (const attachment of applicable) {
288
+ let source;
289
+ try {
290
+ if (attachment.durable !== undefined) {
291
+ source = readDurableSessionImageSync(attachment);
292
+ }
293
+ else {
294
+ // A live caller may still carry its pre-adoption path. Reuse the exact
295
+ // previously committed owner instead of reading an expired upload.
296
+ const previous = prior.find(saved => sameAttachment(attachment, saved) && saved.durable !== undefined);
297
+ source = previous ? readDurableSessionImageSync(previous) : rawMirrorImage(attachment);
298
+ }
299
+ }
300
+ catch (error) {
301
+ const legacy = prior.some(saved => sameAttachment(attachment, saved)
302
+ && saved.durable === undefined && saved.localPath === attachment.localPath);
303
+ if (attachment.durable === undefined && legacy && error.code === 'ENOENT')
304
+ continue;
305
+ if (error instanceof SessionMediaError)
306
+ throw error;
307
+ throw new SessionMediaError('unavailable', `Accepted image ${attachment.name} is unavailable; attach it again. The checkpoint was not changed.`, error);
308
+ }
309
+ const detected = imageType(source.data);
310
+ const hash = sha256(source.data);
311
+ const blobName = hash + '.' + detected.extension;
312
+ let file = byBlob.get(blobName);
313
+ if (file) {
314
+ const data = boundedRead(join(root, ...file.split('/')), SESSION_MEDIA_LIMITS.imageBytes);
315
+ if (data.length !== source.data.length || sha256(data) !== hash) {
316
+ throw new SessionMediaError('invalid_reference', 'Existing owned image bytes changed; the checkpoint was not changed.');
317
+ }
318
+ }
319
+ else {
320
+ quota(physicalCount + 1, physicalBytes + source.data.length);
321
+ if (generation === null) {
322
+ await mkdir(root, { recursive: true, mode: 0o700 });
323
+ checkedDirectory(root);
324
+ const next = 'g-' + randomUUID().replace(/-/gu, '');
325
+ await mkdir(join(root, next), { mode: 0o700 });
326
+ generation = next;
327
+ }
328
+ file = generation + '/' + blobName;
329
+ const handle = await open(join(root, generation, blobName), 'wx', 0o600);
330
+ try {
331
+ await handle.writeFile(source.data);
332
+ }
333
+ finally {
334
+ await handle.close();
335
+ }
336
+ const verify = boundedRead(join(root, generation, blobName), SESSION_MEDIA_LIMITS.imageBytes);
337
+ if (verify.length !== source.data.length || sha256(verify) !== hash) {
338
+ throw new SessionMediaError('write_failed', 'Owned image copy could not be verified; the checkpoint was not changed.');
339
+ }
340
+ physicalCount += 1;
341
+ physicalBytes += source.data.length;
342
+ byBlob.set(blobName, file);
343
+ }
344
+ attachment.localPath = join(root, ...file.split('/'));
345
+ attachment.durable = { version: 1, sessionFile: checkpoint, file, sha256: hash };
346
+ attachment.mediaType = detected.mediaType;
347
+ attachment.size = source.data.length;
348
+ }
349
+ return prepared;
350
+ }
351
+ catch (error) {
352
+ await prepared.rollback();
353
+ if (error instanceof SessionMediaError)
354
+ throw error;
355
+ throw new SessionMediaError('write_failed', 'Could not preserve session image bytes; the checkpoint was not changed. Retry the save or attach the image again.', error);
356
+ }
357
+ }
@@ -0,0 +1,16 @@
1
+ /** Pin before use; the pin remains valid until this process exits. */
2
+ export declare function retainSessionForProcess(sessionDirectory: string, sessionId: string): Promise<void>;
3
+ /**
4
+ * Publish before scanning: two contenders cannot both see no other live writer.
5
+ * They may both refuse during simultaneous admission; callers can retry. Each
6
+ * candidate is an independently atomically published file, so process exit never
7
+ * leaves a partially initialized exclusive lock that needs an age-based steal.
8
+ * Retention pins/barriers continue to protect the transcript from cleanup.
9
+ */
10
+ export declare function acquireSessionWriter(sessionDirectory: string, sessionId: string): Promise<() => Promise<void>>;
11
+ /** Retention readers protect cleanup; only admitted writers block another writer. */
12
+ export declare function hasActiveSessionWriter(sessionDirectory: string, sessionId: string): Promise<boolean>;
13
+ /** Reclaim exited pins under a barrier; keep the pre-upgrade registry read-only. */
14
+ export declare function retainedSessionIds(sessionDirectory: string): Promise<Set<string>>;
15
+ /** A barrier only blocks new users; already pinned users are never candidates. */
16
+ export declare function withSessionCleanupBarrier<T>(sessionDirectory: string, run: () => Promise<T>): Promise<T>;
@@ -0,0 +1,221 @@
1
+ /**
2
+ * Conservative, process-lifetime pins for automatic session retention.
3
+ *
4
+ * Owners publish a pin before reading or writing a session. Cleaners publish a
5
+ * barrier before enumerating candidates. A new owner waits for existing
6
+ * barriers before using its session; a new cleaner sees already published pins.
7
+ * Independent files avoid the lost updates of a shared read/modify/write list.
8
+ * PID liveness deliberately ignores heartbeat age: a paused process still owns
9
+ * its history. Unreadable ownership evidence fails closed.
10
+ */
11
+ import { createHash, randomUUID } from 'node:crypto';
12
+ import { link, mkdir, opendir, readFile, rename, unlink } from 'node:fs/promises';
13
+ import { homedir } from 'node:os';
14
+ import { join } from 'node:path';
15
+ import { setTimeout as delay } from 'node:timers/promises';
16
+ import { writeFileAtomic } from './utils/atomic_write.js';
17
+ const pins = new Map();
18
+ const MARKER_SCAN_LIMIT = 512;
19
+ function isProcessAlive(pid) {
20
+ try {
21
+ process.kill(pid, 0);
22
+ return true;
23
+ }
24
+ catch (error) {
25
+ // Only ESRCH proves the owner is gone. EPERM and other uncertainty retain it.
26
+ return error.code !== 'ESRCH';
27
+ }
28
+ }
29
+ async function readMarker(path, requireSession) {
30
+ let marker;
31
+ try {
32
+ marker = JSON.parse(await readFile(path, 'utf8'));
33
+ }
34
+ catch (error) {
35
+ if (error.code === 'ENOENT')
36
+ return undefined;
37
+ throw error;
38
+ }
39
+ if (!Number.isSafeInteger(marker?.pid) || marker.pid <= 0
40
+ || (requireSession && (typeof marker.sessionId !== 'string' || !marker.sessionId))) {
41
+ throw new Error(`Invalid session ownership marker: ${path}`);
42
+ }
43
+ return marker;
44
+ }
45
+ async function restoreMarker(captured, original) {
46
+ try {
47
+ // Never overwrite a newer publication. On conflict both JSON files remain
48
+ // visible ownership evidence for subsequent scans.
49
+ await link(captured, original);
50
+ }
51
+ catch (error) {
52
+ const code = error.code;
53
+ if (code === 'EEXIST' || code === 'ENOENT')
54
+ return;
55
+ throw error;
56
+ }
57
+ await unlink(captured);
58
+ }
59
+ async function reclaimExitedMarker(path, requireSession) {
60
+ // A legacy owner pathname can be atomically replaced after our first read.
61
+ // Capture the current file, then revalidate its identity/liveness before
62
+ // deleting. The capture remains discoverable JSON if interrupted or uncertain.
63
+ const captured = `${path}.${randomUUID()}.json`;
64
+ try {
65
+ await rename(path, captured);
66
+ }
67
+ catch (error) {
68
+ if (error.code === 'ENOENT')
69
+ return undefined;
70
+ throw error;
71
+ }
72
+ let preserve = true;
73
+ try {
74
+ const marker = await readMarker(captured, requireSession);
75
+ if (marker && isProcessAlive(marker.pid))
76
+ return marker;
77
+ if (marker)
78
+ await unlink(captured);
79
+ preserve = false;
80
+ return undefined;
81
+ }
82
+ finally {
83
+ if (preserve)
84
+ await restoreMarker(captured, path);
85
+ }
86
+ }
87
+ async function liveMarkers(directory, requireSession) {
88
+ let entries;
89
+ try {
90
+ entries = await opendir(directory);
91
+ }
92
+ catch (error) {
93
+ if (error.code === 'ENOENT')
94
+ return [];
95
+ throw error;
96
+ }
97
+ const live = [];
98
+ let scanned = 0;
99
+ for await (const entry of entries) {
100
+ if (++scanned > MARKER_SCAN_LIMIT) {
101
+ throw new Error(`Session ownership scan limit ${MARKER_SCAN_LIMIT} reached; retry maintenance before opening or cleaning sessions.`);
102
+ }
103
+ if (!entry.name.endsWith('.json'))
104
+ continue;
105
+ const path = join(directory, entry.name);
106
+ const marker = await readMarker(path, requireSession);
107
+ if (!marker)
108
+ continue;
109
+ if (isProcessAlive(marker.pid))
110
+ live.push(marker);
111
+ else {
112
+ const replacement = await reclaimExitedMarker(path, requireSession);
113
+ if (replacement)
114
+ live.push(replacement);
115
+ }
116
+ }
117
+ return live;
118
+ }
119
+ /** Pin before use; the pin remains valid until this process exits. */
120
+ export function retainSessionForProcess(sessionDirectory, sessionId) {
121
+ const directory = join(sessionDirectory, '.retention', 'owners');
122
+ const key = join(directory, `${process.pid}-${createHash('sha256').update(sessionId).digest('hex')}.json`);
123
+ const existing = pins.get(key);
124
+ if (existing)
125
+ return existing;
126
+ const pending = (async () => {
127
+ await mkdir(directory, { recursive: true });
128
+ await writeFileAtomic(key, JSON.stringify({ pid: process.pid, sessionId }));
129
+ const barriers = join(sessionDirectory, '.retention', 'cleaners');
130
+ const deadline = Date.now() + 30_000;
131
+ while ((await liveMarkers(barriers, false)).length > 0) {
132
+ if (Date.now() >= deadline)
133
+ throw new Error('Timed out waiting for session retention cleanup. Retry opening the session.');
134
+ await delay(10);
135
+ }
136
+ })();
137
+ pins.set(key, pending);
138
+ void pending.catch(() => { pins.delete(key); });
139
+ return pending;
140
+ }
141
+ /**
142
+ * Publish before scanning: two contenders cannot both see no other live writer.
143
+ * They may both refuse during simultaneous admission; callers can retry. Each
144
+ * candidate is an independently atomically published file, so process exit never
145
+ * leaves a partially initialized exclusive lock that needs an age-based steal.
146
+ * Retention pins/barriers continue to protect the transcript from cleanup.
147
+ */
148
+ export async function acquireSessionWriter(sessionDirectory, sessionId) {
149
+ await retainSessionForProcess(sessionDirectory, sessionId);
150
+ const directory = join(sessionDirectory, '.retention', 'writers', createHash('sha256').update(sessionId).digest('hex'));
151
+ await mkdir(directory, { recursive: true });
152
+ const writerId = randomUUID();
153
+ const marker = join(directory, `${process.pid}-${writerId}.json`);
154
+ await writeFileAtomic(marker, JSON.stringify({ pid: process.pid, sessionId, writerId }));
155
+ let admitted = false;
156
+ try {
157
+ const writers = await liveMarkers(directory, true);
158
+ if (writers.some(writer => writer.sessionId !== sessionId || typeof writer.writerId !== 'string'
159
+ || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(writer.writerId))) {
160
+ throw new Error('Invalid session writer ownership marker');
161
+ }
162
+ if (writers.some(writer => writer.writerId !== writerId)) {
163
+ throw new Error('Delegated session is already being written; retry after its current writer stops.');
164
+ }
165
+ admitted = true;
166
+ return async () => { await unlink(marker); };
167
+ }
168
+ finally {
169
+ if (!admitted)
170
+ await unlink(marker);
171
+ }
172
+ }
173
+ /** Retention readers protect cleanup; only admitted writers block another writer. */
174
+ export async function hasActiveSessionWriter(sessionDirectory, sessionId) {
175
+ const directory = join(sessionDirectory, '.retention', 'writers', createHash('sha256').update(sessionId).digest('hex'));
176
+ const writers = await liveMarkers(directory, true);
177
+ if (writers.some(writer => writer.sessionId !== sessionId || typeof writer.writerId !== 'string'
178
+ || !/^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/.test(writer.writerId))) {
179
+ throw new Error('Invalid session writer ownership marker');
180
+ }
181
+ return writers.length > 0;
182
+ }
183
+ /** Reclaim exited pins under a barrier; keep the pre-upgrade registry read-only. */
184
+ export async function retainedSessionIds(sessionDirectory) {
185
+ return withSessionCleanupBarrier(sessionDirectory, async () => {
186
+ const owned = await liveMarkers(join(sessionDirectory, '.retention', 'owners'), true);
187
+ const ids = new Set(owned.map(marker => marker.sessionId));
188
+ let registry;
189
+ try {
190
+ registry = JSON.parse(await readFile(join(homedir(), '.ztc', 'instances.json'), 'utf8'));
191
+ }
192
+ catch (error) {
193
+ if (error.code === 'ENOENT')
194
+ return ids;
195
+ throw error;
196
+ }
197
+ if (!Array.isArray(registry))
198
+ throw new Error('Invalid ZTC instance registry; automatic session retention skipped.');
199
+ for (const marker of registry) {
200
+ if (!Number.isSafeInteger(marker?.pid) || marker.pid <= 0 || typeof marker.sessionId !== 'string') {
201
+ throw new Error('Invalid ZTC instance registry; automatic session retention skipped.');
202
+ }
203
+ if (isProcessAlive(marker.pid))
204
+ ids.add(marker.sessionId);
205
+ }
206
+ return ids;
207
+ });
208
+ }
209
+ /** A barrier only blocks new users; already pinned users are never candidates. */
210
+ export async function withSessionCleanupBarrier(sessionDirectory, run) {
211
+ const directory = join(sessionDirectory, '.retention', 'cleaners');
212
+ await mkdir(directory, { recursive: true });
213
+ const marker = join(directory, `${process.pid}-${randomUUID()}.json`);
214
+ await writeFileAtomic(marker, JSON.stringify({ pid: process.pid }));
215
+ try {
216
+ return await run();
217
+ }
218
+ finally {
219
+ await unlink(marker);
220
+ }
221
+ }
@@ -12,6 +12,14 @@
12
12
  * This file re-exports exactly those symbols under the `zerg-ztc/session`
13
13
  * package export. The default `~/.ztc/sessions/` path helpers remain internal.
14
14
  */
15
- export { loadSessionFromFile, saveSessionToFile, generateSessionId, getProjectHash, hasSessionSelector, ensureSessionSelector, } from './session.js';
16
- export type { Session, SessionMetadata, SessionSummary, } from './session.js';
15
+ export { loadSessionFromFile, saveSessionToFile, generateSessionId, getProjectHash, hasSessionSelector, ensureSessionSelector, findSessionHandoffOffers, updateSessionBoardBinding, } from './session.js';
16
+ export { admitBoardSessionBinding } from './board/binding.js';
17
+ export type { BoardSessionBinding } from './board/binding.js';
18
+ export type { Session, SessionMetadata, SessionSummary, SessionRuntimeState, SessionHandoffLookupOptions, } from './session.js';
19
+ export type { TaskHandoffInput, TaskHandoffCorrection, TaskHandoffSnapshot, TaskHandoffProjection } from './session_handoff_contract.js';
20
+ export { parseTaskHandoffSnapshot, parseTaskHandoffProjection, projectTaskHandoff, formatTaskHandoffContext } from './session_handoff_contract.js';
17
21
  export type { Message, MessageRole, ToolCall } from './types.js';
22
+ export { buildSessionDiagnosticDocument, exportSessionDiagnostics, parseDiagnosticExport, formatSessionDiagnosticsMarkdown, parseDiagnosticSnapshot, DiagnosticExportError, DIAGNOSTIC_LIMITS } from './diagnostics/session_export.js';
23
+ export type { DiagnosticSnapshot, DiagnosticEvent, DiagnosticExportHeader, DiagnosticDocument, DiagnosticJsonValue, DiagnosticJsonObject, DiagnosticTruncation, SessionDiagnosticInput } from './diagnostics/session_export.js';
24
+ export { buildSessionEvidencePackage, saveSessionEvidencePackage, SESSION_EVIDENCE_INDEX_MAX_BYTES } from './diagnostics/session_evidence_package.js';
25
+ export type { SessionEvidencePackageInput, SessionEvidencePackageSource, SessionEvidencePackageEntry, SessionEvidencePackageManifestEntry, SessionEvidencePackage } from './diagnostics/session_evidence_package.js';
@@ -12,4 +12,9 @@
12
12
  * This file re-exports exactly those symbols under the `zerg-ztc/session`
13
13
  * package export. The default `~/.ztc/sessions/` path helpers remain internal.
14
14
  */
15
- export { loadSessionFromFile, saveSessionToFile, generateSessionId, getProjectHash, hasSessionSelector, ensureSessionSelector, } from './session.js';
15
+ export { loadSessionFromFile, saveSessionToFile, generateSessionId, getProjectHash, hasSessionSelector, ensureSessionSelector, findSessionHandoffOffers, updateSessionBoardBinding, } from './session.js';
16
+ export { admitBoardSessionBinding } from './board/binding.js';
17
+ export { parseTaskHandoffSnapshot, parseTaskHandoffProjection, projectTaskHandoff, formatTaskHandoffContext } from './session_handoff_contract.js';
18
+ // Canonical, redacted diagnostic artifacts are shared by terminal and desktop consumers.
19
+ export { buildSessionDiagnosticDocument, exportSessionDiagnostics, parseDiagnosticExport, formatSessionDiagnosticsMarkdown, parseDiagnosticSnapshot, DiagnosticExportError, DIAGNOSTIC_LIMITS } from './diagnostics/session_export.js';
20
+ export { buildSessionEvidencePackage, saveSessionEvidencePackage, SESSION_EVIDENCE_INDEX_MAX_BYTES } from './diagnostics/session_evidence_package.js';