@caupulican/pi-adaptative 0.81.38 → 0.81.39

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 (433) hide show
  1. package/CHANGELOG.md +79 -0
  2. package/dist/bundled-resources/extensions/tmux-agent-manager/README.md +18 -1
  3. package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.d.ts +131 -0
  4. package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.d.ts.map +1 -0
  5. package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.js +196 -0
  6. package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.js.map +1 -0
  7. package/dist/bundled-resources/extensions/tmux-agent-manager/dispatch-grant.ts +308 -0
  8. package/dist/bundled-resources/extensions/tmux-agent-manager/index.d.ts +22 -2
  9. package/dist/bundled-resources/extensions/tmux-agent-manager/index.d.ts.map +1 -1
  10. package/dist/bundled-resources/extensions/tmux-agent-manager/index.js +585 -24
  11. package/dist/bundled-resources/extensions/tmux-agent-manager/index.js.map +1 -1
  12. package/dist/bundled-resources/extensions/tmux-agent-manager/index.ts +749 -27
  13. package/dist/bundled-resources/runtimes/pi-shell-engine/commands/__init__.py +43 -0
  14. package/dist/bundled-resources/runtimes/pi-shell-engine/commands/fs.py +270 -0
  15. package/dist/bundled-resources/runtimes/pi-shell-engine/commands/search.py +252 -0
  16. package/dist/bundled-resources/runtimes/pi-shell-engine/commands/strings.py +399 -0
  17. package/dist/bundled-resources/runtimes/pi-shell-engine/commands/text.py +575 -0
  18. package/dist/bundled-resources/runtimes/pi-shell-engine/context.py +52 -0
  19. package/dist/bundled-resources/runtimes/pi-shell-engine/errors.py +49 -0
  20. package/dist/bundled-resources/runtimes/pi-shell-engine/exec.py +734 -0
  21. package/dist/bundled-resources/runtimes/pi-shell-engine/expand.py +238 -0
  22. package/dist/bundled-resources/runtimes/pi-shell-engine/main.py +132 -0
  23. package/dist/bundled-resources/runtimes/pi-shell-engine/nodes.py +116 -0
  24. package/dist/bundled-resources/runtimes/pi-shell-engine/parser.py +287 -0
  25. package/dist/bundled-resources/runtimes/pi-shell-engine/proc.py +137 -0
  26. package/dist/bundled-resources/runtimes/pi-shell-engine/state.py +100 -0
  27. package/dist/bundled-resources/runtimes/pi-shell-engine/tokens.py +579 -0
  28. package/dist/bundled-resources/skills/tool-call-repair/SKILL.md +19 -9
  29. package/dist/bundled-resources/skills/tool-call-repair/references/failure-grammar.md +22 -5
  30. package/dist/bundled-resources/skills/tool-call-repair/references/repair-catalogue.md +5 -5
  31. package/dist/bundled-resources/skills/tool-call-repair/references/text-protocol-grammar.md +28 -7
  32. package/dist/cli/args.d.ts +3 -0
  33. package/dist/cli/args.d.ts.map +1 -1
  34. package/dist/cli/args.js +15 -0
  35. package/dist/cli/args.js.map +1 -1
  36. package/dist/core/agent-paths.d.ts +49 -0
  37. package/dist/core/agent-paths.d.ts.map +1 -0
  38. package/dist/core/agent-paths.js +107 -0
  39. package/dist/core/agent-paths.js.map +1 -0
  40. package/dist/core/agent-session-services.d.ts.map +1 -1
  41. package/dist/core/agent-session-services.js +3 -3
  42. package/dist/core/agent-session-services.js.map +1 -1
  43. package/dist/core/agent-session.d.ts +234 -17
  44. package/dist/core/agent-session.d.ts.map +1 -1
  45. package/dist/core/agent-session.js +484 -62
  46. package/dist/core/agent-session.js.map +1 -1
  47. package/dist/core/autonomy/contracts.d.ts +9 -0
  48. package/dist/core/autonomy/contracts.d.ts.map +1 -1
  49. package/dist/core/autonomy/contracts.js.map +1 -1
  50. package/dist/core/autonomy/lane-tracker.d.ts +10 -1
  51. package/dist/core/autonomy/lane-tracker.d.ts.map +1 -1
  52. package/dist/core/autonomy/lane-tracker.js +5 -1
  53. package/dist/core/autonomy/lane-tracker.js.map +1 -1
  54. package/dist/core/background-lane-controller.d.ts +122 -6
  55. package/dist/core/background-lane-controller.d.ts.map +1 -1
  56. package/dist/core/background-lane-controller.js +446 -89
  57. package/dist/core/background-lane-controller.js.map +1 -1
  58. package/dist/core/bash-execution-controller.d.ts +4 -0
  59. package/dist/core/bash-execution-controller.d.ts.map +1 -1
  60. package/dist/core/bash-execution-controller.js +7 -1
  61. package/dist/core/bash-execution-controller.js.map +1 -1
  62. package/dist/core/compaction-support.d.ts +13 -3
  63. package/dist/core/compaction-support.d.ts.map +1 -1
  64. package/dist/core/compaction-support.js +43 -7
  65. package/dist/core/compaction-support.js.map +1 -1
  66. package/dist/core/context/context-audit.d.ts +33 -1
  67. package/dist/core/context/context-audit.d.ts.map +1 -1
  68. package/dist/core/context/context-audit.js +31 -5
  69. package/dist/core/context/context-audit.js.map +1 -1
  70. package/dist/core/context-gc.d.ts.map +1 -1
  71. package/dist/core/context-gc.js +16 -1
  72. package/dist/core/context-gc.js.map +1 -1
  73. package/dist/core/context-pipeline.d.ts +26 -4
  74. package/dist/core/context-pipeline.d.ts.map +1 -1
  75. package/dist/core/context-pipeline.js +137 -14
  76. package/dist/core/context-pipeline.js.map +1 -1
  77. package/dist/core/cost-guard.d.ts +19 -3
  78. package/dist/core/cost-guard.d.ts.map +1 -1
  79. package/dist/core/cost-guard.js +18 -3
  80. package/dist/core/cost-guard.js.map +1 -1
  81. package/dist/core/delegation/session-worker-result.d.ts +34 -4
  82. package/dist/core/delegation/session-worker-result.d.ts.map +1 -1
  83. package/dist/core/delegation/session-worker-result.js +64 -4
  84. package/dist/core/delegation/session-worker-result.js.map +1 -1
  85. package/dist/core/delegation/worker-result.d.ts +42 -1
  86. package/dist/core/delegation/worker-result.d.ts.map +1 -1
  87. package/dist/core/delegation/worker-result.js +68 -0
  88. package/dist/core/delegation/worker-result.js.map +1 -1
  89. package/dist/core/extensions/loader.d.ts.map +1 -1
  90. package/dist/core/extensions/loader.js +20 -0
  91. package/dist/core/extensions/loader.js.map +1 -1
  92. package/dist/core/extensions/runner.d.ts.map +1 -1
  93. package/dist/core/extensions/runner.js +1 -0
  94. package/dist/core/extensions/runner.js.map +1 -1
  95. package/dist/core/extensions/types.d.ts +51 -0
  96. package/dist/core/extensions/types.d.ts.map +1 -1
  97. package/dist/core/extensions/types.js.map +1 -1
  98. package/dist/core/goal-loop-controller.d.ts +17 -1
  99. package/dist/core/goal-loop-controller.d.ts.map +1 -1
  100. package/dist/core/goal-loop-controller.js +79 -11
  101. package/dist/core/goal-loop-controller.js.map +1 -1
  102. package/dist/core/goals/goal-continuation-controller.d.ts +48 -2
  103. package/dist/core/goals/goal-continuation-controller.d.ts.map +1 -1
  104. package/dist/core/goals/goal-continuation-controller.js +77 -0
  105. package/dist/core/goals/goal-continuation-controller.js.map +1 -1
  106. package/dist/core/goals/goal-continuation-defaults.d.ts +39 -0
  107. package/dist/core/goals/goal-continuation-defaults.d.ts.map +1 -1
  108. package/dist/core/goals/goal-continuation-defaults.js +42 -0
  109. package/dist/core/goals/goal-continuation-defaults.js.map +1 -1
  110. package/dist/core/goals/goal-continuation-prompt.d.ts +4 -0
  111. package/dist/core/goals/goal-continuation-prompt.d.ts.map +1 -1
  112. package/dist/core/goals/goal-continuation-prompt.js +51 -10
  113. package/dist/core/goals/goal-continuation-prompt.js.map +1 -1
  114. package/dist/core/goals/goal-runtime-snapshot.d.ts +101 -2
  115. package/dist/core/goals/goal-runtime-snapshot.d.ts.map +1 -1
  116. package/dist/core/goals/goal-runtime-snapshot.js +87 -4
  117. package/dist/core/goals/goal-runtime-snapshot.js.map +1 -1
  118. package/dist/core/goals/goal-state.d.ts +89 -1
  119. package/dist/core/goals/goal-state.d.ts.map +1 -1
  120. package/dist/core/goals/goal-state.js +71 -5
  121. package/dist/core/goals/goal-state.js.map +1 -1
  122. package/dist/core/goals/goal-tool-core.d.ts +56 -2
  123. package/dist/core/goals/goal-tool-core.d.ts.map +1 -1
  124. package/dist/core/goals/goal-tool-core.js +111 -5
  125. package/dist/core/goals/goal-tool-core.js.map +1 -1
  126. package/dist/core/goals/session-goal-state.d.ts +11 -2
  127. package/dist/core/goals/session-goal-state.d.ts.map +1 -1
  128. package/dist/core/goals/session-goal-state.js +32 -17
  129. package/dist/core/goals/session-goal-state.js.map +1 -1
  130. package/dist/core/keybindings.d.ts +10 -0
  131. package/dist/core/keybindings.d.ts.map +1 -1
  132. package/dist/core/keybindings.js +10 -2
  133. package/dist/core/keybindings.js.map +1 -1
  134. package/dist/core/learning/observation-store.d.ts +12 -3
  135. package/dist/core/learning/observation-store.d.ts.map +1 -1
  136. package/dist/core/learning/observation-store.js +30 -15
  137. package/dist/core/learning/observation-store.js.map +1 -1
  138. package/dist/core/learning/skill-curator.d.ts +5 -1
  139. package/dist/core/learning/skill-curator.d.ts.map +1 -1
  140. package/dist/core/learning/skill-curator.js +21 -19
  141. package/dist/core/learning/skill-curator.js.map +1 -1
  142. package/dist/core/local-runtime-controller.d.ts +65 -3
  143. package/dist/core/local-runtime-controller.d.ts.map +1 -1
  144. package/dist/core/local-runtime-controller.js +186 -26
  145. package/dist/core/local-runtime-controller.js.map +1 -1
  146. package/dist/core/memory/providers/file-store.d.ts +1 -1
  147. package/dist/core/memory/providers/file-store.d.ts.map +1 -1
  148. package/dist/core/memory/providers/file-store.js +6 -6
  149. package/dist/core/memory/providers/file-store.js.map +1 -1
  150. package/dist/core/model-capability.d.ts +34 -0
  151. package/dist/core/model-capability.d.ts.map +1 -1
  152. package/dist/core/model-capability.js +42 -1
  153. package/dist/core/model-capability.js.map +1 -1
  154. package/dist/core/model-router/tool-escalation.d.ts +15 -0
  155. package/dist/core/model-router/tool-escalation.d.ts.map +1 -1
  156. package/dist/core/model-router/tool-escalation.js +23 -1
  157. package/dist/core/model-router/tool-escalation.js.map +1 -1
  158. package/dist/core/model-router-controller.d.ts +34 -7
  159. package/dist/core/model-router-controller.d.ts.map +1 -1
  160. package/dist/core/model-router-controller.js +95 -16
  161. package/dist/core/model-router-controller.js.map +1 -1
  162. package/dist/core/models/adaptation-store.d.ts +7 -0
  163. package/dist/core/models/adaptation-store.d.ts.map +1 -1
  164. package/dist/core/models/adaptation-store.js +47 -11
  165. package/dist/core/models/adaptation-store.js.map +1 -1
  166. package/dist/core/models/default-model-suggestions.d.ts.map +1 -1
  167. package/dist/core/models/default-model-suggestions.js +17 -0
  168. package/dist/core/models/default-model-suggestions.js.map +1 -1
  169. package/dist/core/models/fitness-store.d.ts +3 -0
  170. package/dist/core/models/fitness-store.d.ts.map +1 -1
  171. package/dist/core/models/fitness-store.js +11 -2
  172. package/dist/core/models/fitness-store.js.map +1 -1
  173. package/dist/core/models/llamacpp-runtime.d.ts +180 -0
  174. package/dist/core/models/llamacpp-runtime.d.ts.map +1 -0
  175. package/dist/core/models/llamacpp-runtime.js +475 -0
  176. package/dist/core/models/llamacpp-runtime.js.map +1 -0
  177. package/dist/core/models/local-registration.d.ts +40 -0
  178. package/dist/core/models/local-registration.d.ts.map +1 -1
  179. package/dist/core/models/local-registration.js +94 -5
  180. package/dist/core/models/local-registration.js.map +1 -1
  181. package/dist/core/models/local-runtime.d.ts.map +1 -1
  182. package/dist/core/models/local-runtime.js +6 -5
  183. package/dist/core/models/local-runtime.js.map +1 -1
  184. package/dist/core/models/model-ref.d.ts +7 -0
  185. package/dist/core/models/model-ref.d.ts.map +1 -1
  186. package/dist/core/models/model-ref.js +26 -0
  187. package/dist/core/models/model-ref.js.map +1 -1
  188. package/dist/core/models/needle-runtime.d.ts +257 -0
  189. package/dist/core/models/needle-runtime.d.ts.map +1 -0
  190. package/dist/core/models/needle-runtime.js +519 -0
  191. package/dist/core/models/needle-runtime.js.map +1 -0
  192. package/dist/core/models/prism-llamacpp-lifecycle.d.ts +89 -0
  193. package/dist/core/models/prism-llamacpp-lifecycle.d.ts.map +1 -0
  194. package/dist/core/models/prism-llamacpp-lifecycle.js +121 -0
  195. package/dist/core/models/prism-llamacpp-lifecycle.js.map +1 -0
  196. package/dist/core/package-manager.d.ts.map +1 -1
  197. package/dist/core/package-manager.js +11 -10
  198. package/dist/core/package-manager.js.map +1 -1
  199. package/dist/core/process-matrix/codes.d.ts +72 -0
  200. package/dist/core/process-matrix/codes.d.ts.map +1 -0
  201. package/dist/core/process-matrix/codes.js +15 -0
  202. package/dist/core/process-matrix/codes.js.map +1 -0
  203. package/dist/core/process-matrix/runtime.d.ts +63 -0
  204. package/dist/core/process-matrix/runtime.d.ts.map +1 -0
  205. package/dist/core/process-matrix/runtime.js +310 -0
  206. package/dist/core/process-matrix/runtime.js.map +1 -0
  207. package/dist/core/process-matrix/store.d.ts +22 -0
  208. package/dist/core/process-matrix/store.d.ts.map +1 -0
  209. package/dist/core/process-matrix/store.js +80 -0
  210. package/dist/core/process-matrix/store.js.map +1 -0
  211. package/dist/core/process-matrix/supervisor.d.ts +72 -0
  212. package/dist/core/process-matrix/supervisor.d.ts.map +1 -0
  213. package/dist/core/process-matrix/supervisor.js +130 -0
  214. package/dist/core/process-matrix/supervisor.js.map +1 -0
  215. package/dist/core/profile-registry.d.ts +7 -0
  216. package/dist/core/profile-registry.d.ts.map +1 -1
  217. package/dist/core/profile-registry.js +20 -0
  218. package/dist/core/profile-registry.js.map +1 -1
  219. package/dist/core/python-runtime.d.ts.map +1 -1
  220. package/dist/core/python-runtime.js +3 -3
  221. package/dist/core/python-runtime.js.map +1 -1
  222. package/dist/core/reflection-controller.d.ts +29 -5
  223. package/dist/core/reflection-controller.d.ts.map +1 -1
  224. package/dist/core/reflection-controller.js +215 -126
  225. package/dist/core/reflection-controller.js.map +1 -1
  226. package/dist/core/reload-blockers.d.ts +36 -0
  227. package/dist/core/reload-blockers.d.ts.map +1 -1
  228. package/dist/core/reload-blockers.js +44 -0
  229. package/dist/core/reload-blockers.js.map +1 -1
  230. package/dist/core/resource-loader.d.ts.map +1 -1
  231. package/dist/core/resource-loader.js +8 -7
  232. package/dist/core/resource-loader.js.map +1 -1
  233. package/dist/core/runtime-builder.d.ts +58 -5
  234. package/dist/core/runtime-builder.d.ts.map +1 -1
  235. package/dist/core/runtime-builder.js +209 -25
  236. package/dist/core/runtime-builder.js.map +1 -1
  237. package/dist/core/scout-controller.d.ts +6 -0
  238. package/dist/core/scout-controller.d.ts.map +1 -1
  239. package/dist/core/scout-controller.js +66 -52
  240. package/dist/core/scout-controller.js.map +1 -1
  241. package/dist/core/sdk.d.ts.map +1 -1
  242. package/dist/core/sdk.js +3 -3
  243. package/dist/core/sdk.js.map +1 -1
  244. package/dist/core/session-role.d.ts +31 -0
  245. package/dist/core/session-role.d.ts.map +1 -0
  246. package/dist/core/session-role.js +52 -0
  247. package/dist/core/session-role.js.map +1 -0
  248. package/dist/core/settings-manager.d.ts +42 -3
  249. package/dist/core/settings-manager.d.ts.map +1 -1
  250. package/dist/core/settings-manager.js +94 -60
  251. package/dist/core/settings-manager.js.map +1 -1
  252. package/dist/core/system-prompt-builder.d.ts +12 -0
  253. package/dist/core/system-prompt-builder.d.ts.map +1 -1
  254. package/dist/core/system-prompt-builder.js +6 -0
  255. package/dist/core/system-prompt-builder.js.map +1 -1
  256. package/dist/core/system-prompt.d.ts +14 -1
  257. package/dist/core/system-prompt.d.ts.map +1 -1
  258. package/dist/core/system-prompt.js +20 -3
  259. package/dist/core/system-prompt.js.map +1 -1
  260. package/dist/core/tasks/session-task-state.d.ts +11 -2
  261. package/dist/core/tasks/session-task-state.d.ts.map +1 -1
  262. package/dist/core/tasks/session-task-state.js +27 -11
  263. package/dist/core/tasks/session-task-state.js.map +1 -1
  264. package/dist/core/tasks/task-contract-monitor.d.ts +45 -0
  265. package/dist/core/tasks/task-contract-monitor.d.ts.map +1 -0
  266. package/dist/core/tasks/task-contract-monitor.js +56 -0
  267. package/dist/core/tasks/task-contract-monitor.js.map +1 -0
  268. package/dist/core/tasks/task-state.d.ts +8 -0
  269. package/dist/core/tasks/task-state.d.ts.map +1 -1
  270. package/dist/core/tasks/task-state.js +28 -1
  271. package/dist/core/tasks/task-state.js.map +1 -1
  272. package/dist/core/tool-gate-controller.d.ts.map +1 -1
  273. package/dist/core/tool-gate-controller.js +5 -0
  274. package/dist/core/tool-gate-controller.js.map +1 -1
  275. package/dist/core/tool-recovery-log-records.d.ts +7 -0
  276. package/dist/core/tool-recovery-log-records.d.ts.map +1 -1
  277. package/dist/core/tool-recovery-log-records.js +14 -6
  278. package/dist/core/tool-recovery-log-records.js.map +1 -1
  279. package/dist/core/tool-selection/promotion.d.ts +54 -0
  280. package/dist/core/tool-selection/promotion.d.ts.map +1 -0
  281. package/dist/core/tool-selection/promotion.js +81 -0
  282. package/dist/core/tool-selection/promotion.js.map +1 -0
  283. package/dist/core/tool-selection/tool-performance-store.d.ts +37 -0
  284. package/dist/core/tool-selection/tool-performance-store.d.ts.map +1 -1
  285. package/dist/core/tool-selection/tool-performance-store.js +87 -3
  286. package/dist/core/tool-selection/tool-performance-store.js.map +1 -1
  287. package/dist/core/tool-selection/tool-selection-controller.d.ts +45 -0
  288. package/dist/core/tool-selection/tool-selection-controller.d.ts.map +1 -1
  289. package/dist/core/tool-selection/tool-selection-controller.js +96 -0
  290. package/dist/core/tool-selection/tool-selection-controller.js.map +1 -1
  291. package/dist/core/tools/bash.d.ts +18 -0
  292. package/dist/core/tools/bash.d.ts.map +1 -1
  293. package/dist/core/tools/bash.js +95 -16
  294. package/dist/core/tools/bash.js.map +1 -1
  295. package/dist/core/tools/delegate-status.d.ts +14 -0
  296. package/dist/core/tools/delegate-status.d.ts.map +1 -1
  297. package/dist/core/tools/delegate-status.js +82 -7
  298. package/dist/core/tools/delegate-status.js.map +1 -1
  299. package/dist/core/tools/delegate.d.ts.map +1 -1
  300. package/dist/core/tools/delegate.js +25 -8
  301. package/dist/core/tools/delegate.js.map +1 -1
  302. package/dist/core/tools/goal.d.ts +94 -3
  303. package/dist/core/tools/goal.d.ts.map +1 -1
  304. package/dist/core/tools/goal.js +165 -15
  305. package/dist/core/tools/goal.js.map +1 -1
  306. package/dist/core/tools/model-fitness.d.ts +7 -0
  307. package/dist/core/tools/model-fitness.d.ts.map +1 -1
  308. package/dist/core/tools/model-fitness.js +2 -2
  309. package/dist/core/tools/model-fitness.js.map +1 -1
  310. package/dist/core/tools/shell-contract-router.d.ts +6 -1
  311. package/dist/core/tools/shell-contract-router.d.ts.map +1 -1
  312. package/dist/core/tools/shell-contract-router.js +23 -2
  313. package/dist/core/tools/shell-contract-router.js.map +1 -1
  314. package/dist/core/tools/shell-session.d.ts +89 -0
  315. package/dist/core/tools/shell-session.d.ts.map +1 -0
  316. package/dist/core/tools/shell-session.js +432 -0
  317. package/dist/core/tools/shell-session.js.map +1 -0
  318. package/dist/core/tools/task-steps.d.ts +4 -0
  319. package/dist/core/tools/task-steps.d.ts.map +1 -1
  320. package/dist/core/tools/task-steps.js +63 -8
  321. package/dist/core/tools/task-steps.js.map +1 -1
  322. package/dist/core/tools/tmux-dispatch.d.ts +86 -0
  323. package/dist/core/tools/tmux-dispatch.d.ts.map +1 -0
  324. package/dist/core/tools/tmux-dispatch.js +91 -0
  325. package/dist/core/tools/tmux-dispatch.js.map +1 -0
  326. package/dist/core/tools/windows-shell-engine.d.ts +42 -0
  327. package/dist/core/tools/windows-shell-engine.d.ts.map +1 -0
  328. package/dist/core/tools/windows-shell-engine.js +153 -0
  329. package/dist/core/tools/windows-shell-engine.js.map +1 -0
  330. package/dist/core/tools/windows-shell-state.d.ts +40 -0
  331. package/dist/core/tools/windows-shell-state.d.ts.map +1 -0
  332. package/dist/core/tools/windows-shell-state.js +59 -0
  333. package/dist/core/tools/windows-shell-state.js.map +1 -0
  334. package/dist/core/tools/worktree-sync.d.ts +24 -0
  335. package/dist/core/tools/worktree-sync.d.ts.map +1 -0
  336. package/dist/core/tools/worktree-sync.js +338 -0
  337. package/dist/core/tools/worktree-sync.js.map +1 -0
  338. package/dist/core/trust-manager.d.ts +4 -1
  339. package/dist/core/trust-manager.d.ts.map +1 -1
  340. package/dist/core/trust-manager.js +20 -2
  341. package/dist/core/trust-manager.js.map +1 -1
  342. package/dist/core/util/atomic-file.d.ts +55 -0
  343. package/dist/core/util/atomic-file.d.ts.map +1 -0
  344. package/dist/core/util/atomic-file.js +255 -0
  345. package/dist/core/util/atomic-file.js.map +1 -0
  346. package/dist/core/util/minimatch-cache.d.ts +33 -0
  347. package/dist/core/util/minimatch-cache.d.ts.map +1 -0
  348. package/dist/core/util/minimatch-cache.js +0 -0
  349. package/dist/core/util/minimatch-cache.js.map +1 -0
  350. package/dist/core/worktree-sync/codes.d.ts +227 -0
  351. package/dist/core/worktree-sync/codes.d.ts.map +1 -0
  352. package/dist/core/worktree-sync/codes.js +14 -0
  353. package/dist/core/worktree-sync/codes.js.map +1 -0
  354. package/dist/core/worktree-sync/git-engine.d.ts +156 -0
  355. package/dist/core/worktree-sync/git-engine.d.ts.map +1 -0
  356. package/dist/core/worktree-sync/git-engine.js +1191 -0
  357. package/dist/core/worktree-sync/git-engine.js.map +1 -0
  358. package/dist/core/worktree-sync/lane-gate.d.ts +75 -0
  359. package/dist/core/worktree-sync/lane-gate.d.ts.map +1 -0
  360. package/dist/core/worktree-sync/lane-gate.js +360 -0
  361. package/dist/core/worktree-sync/lane-gate.js.map +1 -0
  362. package/dist/core/worktree-sync/runtime.d.ts +47 -0
  363. package/dist/core/worktree-sync/runtime.d.ts.map +1 -0
  364. package/dist/core/worktree-sync/runtime.js +96 -0
  365. package/dist/core/worktree-sync/runtime.js.map +1 -0
  366. package/dist/core/worktree-sync/store.d.ts +69 -0
  367. package/dist/core/worktree-sync/store.d.ts.map +1 -0
  368. package/dist/core/worktree-sync/store.js +247 -0
  369. package/dist/core/worktree-sync/store.js.map +1 -0
  370. package/dist/core/worktree-sync/watcher.d.ts +29 -0
  371. package/dist/core/worktree-sync/watcher.d.ts.map +1 -0
  372. package/dist/core/worktree-sync/watcher.js +93 -0
  373. package/dist/core/worktree-sync/watcher.js.map +1 -0
  374. package/dist/main.d.ts.map +1 -1
  375. package/dist/main.js +94 -0
  376. package/dist/main.js.map +1 -1
  377. package/dist/migrations.d.ts +9 -0
  378. package/dist/migrations.d.ts.map +1 -1
  379. package/dist/migrations.js +38 -0
  380. package/dist/migrations.js.map +1 -1
  381. package/dist/modes/interactive/auto-learn-controller.d.ts +16 -1
  382. package/dist/modes/interactive/auto-learn-controller.d.ts.map +1 -1
  383. package/dist/modes/interactive/auto-learn-controller.js +50 -8
  384. package/dist/modes/interactive/auto-learn-controller.js.map +1 -1
  385. package/dist/modes/interactive/components/profile-resource-editor.d.ts.map +1 -1
  386. package/dist/modes/interactive/components/profile-resource-editor.js +23 -1
  387. package/dist/modes/interactive/components/profile-resource-editor.js.map +1 -1
  388. package/dist/modes/interactive/interactive-mode.d.ts +1 -0
  389. package/dist/modes/interactive/interactive-mode.d.ts.map +1 -1
  390. package/dist/modes/interactive/interactive-mode.js +4 -0
  391. package/dist/modes/interactive/interactive-mode.js.map +1 -1
  392. package/dist/modes/interactive/local-model-commands.d.ts +43 -0
  393. package/dist/modes/interactive/local-model-commands.d.ts.map +1 -1
  394. package/dist/modes/interactive/local-model-commands.js +290 -3
  395. package/dist/modes/interactive/local-model-commands.js.map +1 -1
  396. package/dist/modes/interactive/session-flow-commands.d.ts.map +1 -1
  397. package/dist/modes/interactive/session-flow-commands.js +24 -4
  398. package/dist/modes/interactive/session-flow-commands.js.map +1 -1
  399. package/dist/utils/fs-watch.d.ts +11 -0
  400. package/dist/utils/fs-watch.d.ts.map +1 -1
  401. package/dist/utils/fs-watch.js +20 -2
  402. package/dist/utils/fs-watch.js.map +1 -1
  403. package/dist/utils/highlight-js-languages.d.ts +4 -0
  404. package/dist/utils/highlight-js-languages.d.ts.map +1 -0
  405. package/dist/utils/highlight-js-languages.js +573 -0
  406. package/dist/utils/highlight-js-languages.js.map +1 -0
  407. package/dist/utils/shell.d.ts +7 -1
  408. package/dist/utils/shell.d.ts.map +1 -1
  409. package/dist/utils/shell.js +21 -6
  410. package/dist/utils/shell.js.map +1 -1
  411. package/dist/utils/syntax-highlight.d.ts.map +1 -1
  412. package/dist/utils/syntax-highlight.js +53 -5
  413. package/dist/utils/syntax-highlight.js.map +1 -1
  414. package/dist/utils/tools-manager.d.ts.map +1 -1
  415. package/dist/utils/tools-manager.js +112 -1
  416. package/dist/utils/tools-manager.js.map +1 -1
  417. package/docs/process-matrix.md +120 -0
  418. package/docs/settings.md +3 -0
  419. package/docs/tmux-agent-manager.md +85 -2
  420. package/docs/windows.md +52 -3
  421. package/docs/work-directory.md +29 -0
  422. package/docs/worktree-sync.md +250 -0
  423. package/examples/extensions/custom-provider-anthropic/package-lock.json +2 -2
  424. package/examples/extensions/custom-provider-anthropic/package.json +1 -1
  425. package/examples/extensions/custom-provider-gitlab-duo/package.json +1 -1
  426. package/examples/extensions/sandbox/package-lock.json +2 -2
  427. package/examples/extensions/sandbox/package.json +1 -1
  428. package/examples/extensions/with-deps/package-lock.json +2 -2
  429. package/examples/extensions/with-deps/package.json +1 -1
  430. package/npm-shrinkwrap.json +12 -12
  431. package/package.json +10 -4
  432. package/docs/integration-sweep-builder-blueprint-2026-07-09.md +0 -365
  433. package/docs/integration-sweep-resume-2026-07-09.md +0 -407
@@ -0,0 +1,72 @@
1
+ /**
2
+ * Process-matrix SSOT for tagged result codes and shared record types.
3
+ *
4
+ * Every process-matrix lifecycle transition is expressed as one of the string-literal codes/status
5
+ * values below -- callers (the runtime, the master orphan scan, the worker watcher) branch on
6
+ * `code`/`status`, NEVER on a message/substring (the same doctrine as `worktree-sync/codes.ts`).
7
+ *
8
+ * The matrix tracks two roles:
9
+ * - `master`: the top-level interactive/direct session (or the root of a launch chain).
10
+ * - `worker`: a session launched with a known parent (`PI_PARENT_PID`/`--parent-pid`), e.g. a
11
+ * tmux-dispatched agent. A worker knows its parent's pid and winds down gracefully -- never
12
+ * silently -- when that parent disappears.
13
+ */
14
+ export type ProcessRole = "master" | "worker";
15
+ /**
16
+ * - `running` -- normal operation.
17
+ * - `winding_down` -- a lifecycle transition is in progress (parent lost, parent shutdown, or a
18
+ * cooperative user-requested cleanup); the process is finishing up before exit.
19
+ * - `resumable` -- wound down leaving a payload describing how to pick the task back up.
20
+ * - `adopted` -- claimed by a new parent after the original parent was lost (retained by
21
+ * `reconcileMatrix` alongside `resumable`, TTL-gated).
22
+ * - `closed` -- terminal; safe to prune.
23
+ */
24
+ export type ProcessStatus = "running" | "winding_down" | "resumable" | "adopted" | "closed";
25
+ export type WindDownReason = "parent_lost" | "parent_shutdown" | "user_cleanup";
26
+ /** What a wound-down worker leaves behind so its task can be picked back up. */
27
+ export interface ResumablePayload {
28
+ laneKey?: string;
29
+ taskSummary?: string;
30
+ lastCode: ProcessStatus;
31
+ }
32
+ /** One process-matrix entry: one file under `state/process-matrix/<entryId>.json` (see `store.ts`). */
33
+ export interface ProcessMatrixEntry {
34
+ entryId: string;
35
+ role: ProcessRole;
36
+ pid: number;
37
+ sessionId: string;
38
+ hostname: string;
39
+ startedAt: string;
40
+ heartbeatAt: string;
41
+ status: ProcessStatus;
42
+ /** Worker-only: the pid of the process that launched this one. */
43
+ parentPid?: number;
44
+ parentSessionId?: string;
45
+ laneKey?: string;
46
+ tmuxSession?: string;
47
+ tmuxPanePid?: number;
48
+ taskRef?: string;
49
+ windDownReason?: WindDownReason;
50
+ resumable?: ResumablePayload;
51
+ }
52
+ /** What a master decides to do about one orphaned worker during the startup scan. */
53
+ export type CleanupAction = "adopt" | "cleanup" | "leave";
54
+ /**
55
+ * What a worker learns by re-reading its OWN fresh matrix entry (a master may have written an
56
+ * adoption or a cooperative-cleanup request into it -- see `docs/process-matrix.md`).
57
+ */
58
+ export type WorkerDirective = {
59
+ code: "adopt";
60
+ parentPid: number;
61
+ } | {
62
+ code: "user_cleanup";
63
+ } | {
64
+ code: "none";
65
+ };
66
+ /** Outcome of a `reconcileMatrix` pass: which entries survive and which were pruned, and why. */
67
+ export interface ReconcileMatrixResult {
68
+ code: "reconciled";
69
+ kept: ProcessMatrixEntry[];
70
+ prunedEntryIds: string[];
71
+ }
72
+ //# sourceMappingURL=codes.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codes.d.ts","sourceRoot":"","sources":["../../../src/core/process-matrix/codes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AAEH,MAAM,MAAM,WAAW,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAE9C;;;;;;;;GAQG;AACH,MAAM,MAAM,aAAa,GAAG,SAAS,GAAG,cAAc,GAAG,WAAW,GAAG,SAAS,GAAG,QAAQ,CAAC;AAE5F,MAAM,MAAM,cAAc,GAAG,aAAa,GAAG,iBAAiB,GAAG,cAAc,CAAC;AAEhF,gFAAgF;AAChF,MAAM,WAAW,gBAAgB;IAChC,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,QAAQ,EAAE,aAAa,CAAC;CACxB;AAED,uGAAuG;AACvG,MAAM,WAAW,kBAAkB;IAClC,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,WAAW,CAAC;IAClB,GAAG,EAAE,MAAM,CAAC;IACZ,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,WAAW,EAAE,MAAM,CAAC;IACpB,MAAM,EAAE,aAAa,CAAC;IACtB,kEAAkE;IAClE,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,SAAS,CAAC,EAAE,gBAAgB,CAAC;CAC7B;AAED,qFAAqF;AACrF,MAAM,MAAM,aAAa,GAAG,OAAO,GAAG,SAAS,GAAG,OAAO,CAAC;AAE1D;;;GAGG;AACH,MAAM,MAAM,eAAe,GAAG;IAAE,IAAI,EAAE,OAAO,CAAC;IAAC,SAAS,EAAE,MAAM,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,cAAc,CAAA;CAAE,GAAG;IAAE,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAEjH,iGAAiG;AACjG,MAAM,WAAW,qBAAqB;IACrC,IAAI,EAAE,YAAY,CAAC;IACnB,IAAI,EAAE,kBAAkB,EAAE,CAAC;IAC3B,cAAc,EAAE,MAAM,EAAE,CAAC;CACzB","sourcesContent":["/**\n * Process-matrix SSOT for tagged result codes and shared record types.\n *\n * Every process-matrix lifecycle transition is expressed as one of the string-literal codes/status\n * values below -- callers (the runtime, the master orphan scan, the worker watcher) branch on\n * `code`/`status`, NEVER on a message/substring (the same doctrine as `worktree-sync/codes.ts`).\n *\n * The matrix tracks two roles:\n * - `master`: the top-level interactive/direct session (or the root of a launch chain).\n * - `worker`: a session launched with a known parent (`PI_PARENT_PID`/`--parent-pid`), e.g. a\n * tmux-dispatched agent. A worker knows its parent's pid and winds down gracefully -- never\n * silently -- when that parent disappears.\n */\n\nexport type ProcessRole = \"master\" | \"worker\";\n\n/**\n * - `running` -- normal operation.\n * - `winding_down` -- a lifecycle transition is in progress (parent lost, parent shutdown, or a\n * cooperative user-requested cleanup); the process is finishing up before exit.\n * - `resumable` -- wound down leaving a payload describing how to pick the task back up.\n * - `adopted` -- claimed by a new parent after the original parent was lost (retained by\n * `reconcileMatrix` alongside `resumable`, TTL-gated).\n * - `closed` -- terminal; safe to prune.\n */\nexport type ProcessStatus = \"running\" | \"winding_down\" | \"resumable\" | \"adopted\" | \"closed\";\n\nexport type WindDownReason = \"parent_lost\" | \"parent_shutdown\" | \"user_cleanup\";\n\n/** What a wound-down worker leaves behind so its task can be picked back up. */\nexport interface ResumablePayload {\n\tlaneKey?: string;\n\ttaskSummary?: string;\n\tlastCode: ProcessStatus;\n}\n\n/** One process-matrix entry: one file under `state/process-matrix/<entryId>.json` (see `store.ts`). */\nexport interface ProcessMatrixEntry {\n\tentryId: string;\n\trole: ProcessRole;\n\tpid: number;\n\tsessionId: string;\n\thostname: string;\n\tstartedAt: string;\n\theartbeatAt: string;\n\tstatus: ProcessStatus;\n\t/** Worker-only: the pid of the process that launched this one. */\n\tparentPid?: number;\n\tparentSessionId?: string;\n\tlaneKey?: string;\n\ttmuxSession?: string;\n\ttmuxPanePid?: number;\n\ttaskRef?: string;\n\twindDownReason?: WindDownReason;\n\tresumable?: ResumablePayload;\n}\n\n/** What a master decides to do about one orphaned worker during the startup scan. */\nexport type CleanupAction = \"adopt\" | \"cleanup\" | \"leave\";\n\n/**\n * What a worker learns by re-reading its OWN fresh matrix entry (a master may have written an\n * adoption or a cooperative-cleanup request into it -- see `docs/process-matrix.md`).\n */\nexport type WorkerDirective = { code: \"adopt\"; parentPid: number } | { code: \"user_cleanup\" } | { code: \"none\" };\n\n/** Outcome of a `reconcileMatrix` pass: which entries survive and which were pruned, and why. */\nexport interface ReconcileMatrixResult {\n\tcode: \"reconciled\";\n\tkept: ProcessMatrixEntry[];\n\tprunedEntryIds: string[];\n}\n"]}
@@ -0,0 +1,15 @@
1
+ /**
2
+ * Process-matrix SSOT for tagged result codes and shared record types.
3
+ *
4
+ * Every process-matrix lifecycle transition is expressed as one of the string-literal codes/status
5
+ * values below -- callers (the runtime, the master orphan scan, the worker watcher) branch on
6
+ * `code`/`status`, NEVER on a message/substring (the same doctrine as `worktree-sync/codes.ts`).
7
+ *
8
+ * The matrix tracks two roles:
9
+ * - `master`: the top-level interactive/direct session (or the root of a launch chain).
10
+ * - `worker`: a session launched with a known parent (`PI_PARENT_PID`/`--parent-pid`), e.g. a
11
+ * tmux-dispatched agent. A worker knows its parent's pid and winds down gracefully -- never
12
+ * silently -- when that parent disappears.
13
+ */
14
+ export {};
15
+ //# sourceMappingURL=codes.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"codes.js","sourceRoot":"","sources":["../../../src/core/process-matrix/codes.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG","sourcesContent":["/**\n * Process-matrix SSOT for tagged result codes and shared record types.\n *\n * Every process-matrix lifecycle transition is expressed as one of the string-literal codes/status\n * values below -- callers (the runtime, the master orphan scan, the worker watcher) branch on\n * `code`/`status`, NEVER on a message/substring (the same doctrine as `worktree-sync/codes.ts`).\n *\n * The matrix tracks two roles:\n * - `master`: the top-level interactive/direct session (or the root of a launch chain).\n * - `worker`: a session launched with a known parent (`PI_PARENT_PID`/`--parent-pid`), e.g. a\n * tmux-dispatched agent. A worker knows its parent's pid and winds down gracefully -- never\n * silently -- when that parent disappears.\n */\n\nexport type ProcessRole = \"master\" | \"worker\";\n\n/**\n * - `running` -- normal operation.\n * - `winding_down` -- a lifecycle transition is in progress (parent lost, parent shutdown, or a\n * cooperative user-requested cleanup); the process is finishing up before exit.\n * - `resumable` -- wound down leaving a payload describing how to pick the task back up.\n * - `adopted` -- claimed by a new parent after the original parent was lost (retained by\n * `reconcileMatrix` alongside `resumable`, TTL-gated).\n * - `closed` -- terminal; safe to prune.\n */\nexport type ProcessStatus = \"running\" | \"winding_down\" | \"resumable\" | \"adopted\" | \"closed\";\n\nexport type WindDownReason = \"parent_lost\" | \"parent_shutdown\" | \"user_cleanup\";\n\n/** What a wound-down worker leaves behind so its task can be picked back up. */\nexport interface ResumablePayload {\n\tlaneKey?: string;\n\ttaskSummary?: string;\n\tlastCode: ProcessStatus;\n}\n\n/** One process-matrix entry: one file under `state/process-matrix/<entryId>.json` (see `store.ts`). */\nexport interface ProcessMatrixEntry {\n\tentryId: string;\n\trole: ProcessRole;\n\tpid: number;\n\tsessionId: string;\n\thostname: string;\n\tstartedAt: string;\n\theartbeatAt: string;\n\tstatus: ProcessStatus;\n\t/** Worker-only: the pid of the process that launched this one. */\n\tparentPid?: number;\n\tparentSessionId?: string;\n\tlaneKey?: string;\n\ttmuxSession?: string;\n\ttmuxPanePid?: number;\n\ttaskRef?: string;\n\twindDownReason?: WindDownReason;\n\tresumable?: ResumablePayload;\n}\n\n/** What a master decides to do about one orphaned worker during the startup scan. */\nexport type CleanupAction = \"adopt\" | \"cleanup\" | \"leave\";\n\n/**\n * What a worker learns by re-reading its OWN fresh matrix entry (a master may have written an\n * adoption or a cooperative-cleanup request into it -- see `docs/process-matrix.md`).\n */\nexport type WorkerDirective = { code: \"adopt\"; parentPid: number } | { code: \"user_cleanup\" } | { code: \"none\" };\n\n/** Outcome of a `reconcileMatrix` pass: which entries survive and which were pruned, and why. */\nexport interface ReconcileMatrixResult {\n\tcode: \"reconciled\";\n\tkept: ProcessMatrixEntry[];\n\tprunedEntryIds: string[];\n}\n"]}
@@ -0,0 +1,63 @@
1
+ /**
2
+ * Process-matrix runtime composition: the pieces main.ts wires together for durable,
3
+ * restart-surviving master/worker process supervision.
4
+ *
5
+ * Contract:
6
+ * - A WORKER is any process launched with a known parent (`PI_PARENT_PID` -- set by the
7
+ * `--parent-pid` CLI flag, or directly by a launcher such as tmux dispatch). It self-registers
8
+ * its OWN entry (the single writer of that entry during normal operation) and watches its
9
+ * parent's liveness. On parent death it winds down GRACEFULLY -- never silently -- leaving a
10
+ * `resumable` payload, then exits on its own after a bounded grace window (during which it may
11
+ * instead be adopted by a new parent). "No new turns" after that point is automatic: a dead
12
+ * parent injects no further follow-ups, so the worker simply runs out of work to do.
13
+ * - A MASTER is everything else (no known parent). On startup it scans the matrix for orphaned
14
+ * workers (workers whose recorded parent is dead) and, when interactive, ASKS the owner before
15
+ * touching anything -- adopt, cooperative cleanup, or leave untouched. Non-interactive: report
16
+ * only, zero writes, zero kills.
17
+ *
18
+ * One sanctioned exception to "a worker's entry is written only by that worker": the master
19
+ * orphan-scan writes an adoption or a cooperative-cleanup request directly into an ORPHANED
20
+ * worker's own entry, but ONLY after explicit owner confirmation (`promptConfirm`) -- mirroring
21
+ * the worktree-sync integration lock's dead-owner takeover (a new owner may claim a PROVABLY DEAD
22
+ * owner's resource). The worker itself later confirms/applies that grant locally via
23
+ * `pollWorkerDirective` and re-writes its own entry -- see `docs/process-matrix.md`. Outside this
24
+ * one ask-gated handshake, a master NEVER writes another session's entry, and nothing here ever
25
+ * kills a process directly: every termination is either owner-confirmed or the worker's own
26
+ * cooperative self-exit.
27
+ */
28
+ import type { ResolvedProcessMatrixSettings } from "../settings-manager.ts";
29
+ export declare const PI_PARENT_PID_ENV = "PI_PARENT_PID";
30
+ export declare const PI_PARENT_SESSION_ENV = "PI_PARENT_SESSION";
31
+ /** This process's declared parent pid, from the cross-process env contract. A malformed or
32
+ * non-positive value is ignored (never a crash on bad env). */
33
+ export declare function getParentPid(env?: NodeJS.ProcessEnv): number | undefined;
34
+ /** This process's declared parent sessionId, from the cross-process env contract. */
35
+ export declare function getParentSessionId(env?: NodeJS.ProcessEnv): string | undefined;
36
+ export interface ProcessMatrixRuntimeConfig {
37
+ agentDir: string;
38
+ sessionId: string;
39
+ /** Whether an interactive UI is available to ask the owner (see `promptConfirm`). */
40
+ hasUI: boolean;
41
+ settings: ResolvedProcessMatrixSettings;
42
+ isProcessAlive: (pid: number) => boolean;
43
+ now?: () => number;
44
+ /** Structural notice injection into the running session (host `sendCustomMessage` seam). */
45
+ notify: (text: string) => void;
46
+ /** Diagnostics sink (never throws into the session). */
47
+ onDiagnostic?: (message: string) => void;
48
+ /** The ask seam: resolves false on decline AND on any non-interactive/non-TTY caller. */
49
+ promptConfirm: (message: string) => Promise<boolean>;
50
+ /** Cooperative self-exit -- called by a worker once wound down (grace expiry or a
51
+ * master-granted cleanup directive). Never called for the master's own lifecycle. */
52
+ requestExit: () => void;
53
+ }
54
+ export interface ProcessMatrixRuntimeHandle {
55
+ stop(): void;
56
+ }
57
+ /**
58
+ * Start the per-session process-matrix runtime. No-op when disabled (byte-identical to not
59
+ * calling this at all). Never throws: a broken store must surface as a diagnostic, not a startup
60
+ * crash.
61
+ */
62
+ export declare function startProcessMatrixRuntime(config: ProcessMatrixRuntimeConfig): Promise<ProcessMatrixRuntimeHandle>;
63
+ //# sourceMappingURL=runtime.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.d.ts","sourceRoot":"","sources":["../../../src/core/process-matrix/runtime.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AAGH,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,wBAAwB,CAAC;AAgB5E,eAAO,MAAM,iBAAiB,kBAAkB,CAAC;AACjD,eAAO,MAAM,qBAAqB,sBAAsB,CAAC;AAEzD;+DAC+D;AAC/D,wBAAgB,YAAY,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,GAAG,SAAS,CAKrF;AAED,qFAAqF;AACrF,wBAAgB,kBAAkB,CAAC,GAAG,GAAE,MAAM,CAAC,UAAwB,GAAG,MAAM,GAAG,SAAS,CAG3F;AAED,MAAM,WAAW,0BAA0B;IAC1C,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,qFAAqF;IACrF,KAAK,EAAE,OAAO,CAAC;IACf,QAAQ,EAAE,6BAA6B,CAAC;IACxC,cAAc,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,OAAO,CAAC;IACzC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,4FAA4F;IAC5F,MAAM,EAAE,CAAC,IAAI,EAAE,MAAM,KAAK,IAAI,CAAC;IAC/B,wDAAwD;IACxD,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,IAAI,CAAC;IACzC,yFAAyF;IACzF,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;IACrD;yFACqF;IACrF,WAAW,EAAE,MAAM,IAAI,CAAC;CACxB;AAED,MAAM,WAAW,0BAA0B;IAC1C,IAAI,IAAI,IAAI,CAAC;CACb;AAYD;;;;GAIG;AACH,wBAAsB,yBAAyB,CAC9C,MAAM,EAAE,0BAA0B,GAChC,OAAO,CAAC,0BAA0B,CAAC,CAcrC","sourcesContent":["/**\n * Process-matrix runtime composition: the pieces main.ts wires together for durable,\n * restart-surviving master/worker process supervision.\n *\n * Contract:\n * - A WORKER is any process launched with a known parent (`PI_PARENT_PID` -- set by the\n * `--parent-pid` CLI flag, or directly by a launcher such as tmux dispatch). It self-registers\n * its OWN entry (the single writer of that entry during normal operation) and watches its\n * parent's liveness. On parent death it winds down GRACEFULLY -- never silently -- leaving a\n * `resumable` payload, then exits on its own after a bounded grace window (during which it may\n * instead be adopted by a new parent). \"No new turns\" after that point is automatic: a dead\n * parent injects no further follow-ups, so the worker simply runs out of work to do.\n * - A MASTER is everything else (no known parent). On startup it scans the matrix for orphaned\n * workers (workers whose recorded parent is dead) and, when interactive, ASKS the owner before\n * touching anything -- adopt, cooperative cleanup, or leave untouched. Non-interactive: report\n * only, zero writes, zero kills.\n *\n * One sanctioned exception to \"a worker's entry is written only by that worker\": the master\n * orphan-scan writes an adoption or a cooperative-cleanup request directly into an ORPHANED\n * worker's own entry, but ONLY after explicit owner confirmation (`promptConfirm`) -- mirroring\n * the worktree-sync integration lock's dead-owner takeover (a new owner may claim a PROVABLY DEAD\n * owner's resource). The worker itself later confirms/applies that grant locally via\n * `pollWorkerDirective` and re-writes its own entry -- see `docs/process-matrix.md`. Outside this\n * one ask-gated handshake, a master NEVER writes another session's entry, and nothing here ever\n * kills a process directly: every termination is either owner-confirmed or the worker's own\n * cooperative self-exit.\n */\n\nimport { hostname as osHostname } from \"node:os\";\nimport type { ResolvedProcessMatrixSettings } from \"../settings-manager.ts\";\nimport { getBoundWorktreeLaneKey } from \"../worktree-sync/runtime.ts\";\nimport type { ProcessMatrixEntry, ResumablePayload } from \"./codes.ts\";\nimport { buildEntryId, listEntries, readEntry, writeEntry, writeEntrySync } from \"./store.ts\";\nimport {\n\tapplyAdoption,\n\tapplyHeartbeat,\n\tbeginWindDown,\n\tbuildMasterEntry,\n\tbuildWorkerEntry,\n\tdetectOrphanedWorkers,\n\tmarkClosed,\n\tmarkResumable,\n\tpollWorkerDirective,\n} from \"./supervisor.ts\";\n\nexport const PI_PARENT_PID_ENV = \"PI_PARENT_PID\";\nexport const PI_PARENT_SESSION_ENV = \"PI_PARENT_SESSION\";\n\n/** This process's declared parent pid, from the cross-process env contract. A malformed or\n * non-positive value is ignored (never a crash on bad env). */\nexport function getParentPid(env: NodeJS.ProcessEnv = process.env): number | undefined {\n\tconst raw = env[PI_PARENT_PID_ENV];\n\tif (raw === undefined) return undefined;\n\tconst value = Number.parseInt(raw, 10);\n\treturn Number.isFinite(value) && value > 0 ? value : undefined;\n}\n\n/** This process's declared parent sessionId, from the cross-process env contract. */\nexport function getParentSessionId(env: NodeJS.ProcessEnv = process.env): string | undefined {\n\tconst value = env[PI_PARENT_SESSION_ENV]?.trim();\n\treturn value && value.length > 0 ? value : undefined;\n}\n\nexport interface ProcessMatrixRuntimeConfig {\n\tagentDir: string;\n\tsessionId: string;\n\t/** Whether an interactive UI is available to ask the owner (see `promptConfirm`). */\n\thasUI: boolean;\n\tsettings: ResolvedProcessMatrixSettings;\n\tisProcessAlive: (pid: number) => boolean;\n\tnow?: () => number;\n\t/** Structural notice injection into the running session (host `sendCustomMessage` seam). */\n\tnotify: (text: string) => void;\n\t/** Diagnostics sink (never throws into the session). */\n\tonDiagnostic?: (message: string) => void;\n\t/** The ask seam: resolves false on decline AND on any non-interactive/non-TTY caller. */\n\tpromptConfirm: (message: string) => Promise<boolean>;\n\t/** Cooperative self-exit -- called by a worker once wound down (grace expiry or a\n\t * master-granted cleanup directive). Never called for the master's own lifecycle. */\n\trequestExit: () => void;\n}\n\nexport interface ProcessMatrixRuntimeHandle {\n\tstop(): void;\n}\n\nconst NOOP_HANDLE: ProcessMatrixRuntimeHandle = { stop: () => {} };\n\nfunction describeError(error: unknown): string {\n\treturn error instanceof Error ? error.message : String(error);\n}\n\nfunction nowIso(now: () => number): string {\n\treturn new Date(now()).toISOString();\n}\n\n/**\n * Start the per-session process-matrix runtime. No-op when disabled (byte-identical to not\n * calling this at all). Never throws: a broken store must surface as a diagnostic, not a startup\n * crash.\n */\nexport async function startProcessMatrixRuntime(\n\tconfig: ProcessMatrixRuntimeConfig,\n): Promise<ProcessMatrixRuntimeHandle> {\n\tif (!config.settings.enabled) return NOOP_HANDLE;\n\tconst now = config.now ?? Date.now;\n\tconst parentPid = getParentPid();\n\n\ttry {\n\t\tif (parentPid !== undefined) {\n\t\t\treturn await startWorkerBranch(config, parentPid, now);\n\t\t}\n\t\treturn await startMasterBranch(config, now);\n\t} catch (error) {\n\t\tconfig.onDiagnostic?.(`process-matrix: runtime failed to start: ${describeError(error)}`);\n\t\treturn NOOP_HANDLE;\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Master branch\n// ---------------------------------------------------------------------------\n\nasync function startMasterBranch(\n\tconfig: ProcessMatrixRuntimeConfig,\n\tnow: () => number,\n): Promise<ProcessMatrixRuntimeHandle> {\n\tlet entry = buildMasterEntry({\n\t\tsessionId: config.sessionId,\n\t\tpid: process.pid,\n\t\thostname: osHostname(),\n\t\tnow: nowIso(now),\n\t});\n\ttry {\n\t\tawait writeEntry(config.agentDir, entry);\n\t} catch (error) {\n\t\tconfig.onDiagnostic?.(`process-matrix: failed to register master entry: ${describeError(error)}`);\n\t}\n\n\tlet stopped = false;\n\tconst heartbeatTimer = setInterval(() => {\n\t\tif (stopped) return;\n\t\tentry = applyHeartbeat(entry, nowIso(now));\n\t\tvoid writeEntry(config.agentDir, entry).catch((error) => {\n\t\t\tconfig.onDiagnostic?.(`process-matrix: failed to write master heartbeat: ${describeError(error)}`);\n\t\t});\n\t}, config.settings.heartbeatMs);\n\theartbeatTimer.unref?.();\n\n\t// Best-effort close on process exit. A SIGKILLed master leaving \"running\" is fine -- reconcile's\n\t// own dead-pid detection covers it; this only makes the common clean-exit case tidy.\n\tprocess.on(\"exit\", () => {\n\t\ttry {\n\t\t\twriteEntrySync(config.agentDir, markClosed(entry, nowIso(now)));\n\t\t} catch {\n\t\t\t// Best-effort only -- see module doc.\n\t\t}\n\t});\n\n\tvoid runOrphanScan(config, now);\n\n\treturn {\n\t\tstop() {\n\t\t\tstopped = true;\n\t\t\tclearInterval(heartbeatTimer);\n\t\t},\n\t};\n}\n\nasync function runOrphanScan(config: ProcessMatrixRuntimeConfig, now: () => number): Promise<void> {\n\tlet entries: ProcessMatrixEntry[];\n\ttry {\n\t\tentries = await listEntries(config.agentDir);\n\t} catch (error) {\n\t\tconfig.onDiagnostic?.(`process-matrix: orphan scan failed to list entries: ${describeError(error)}`);\n\t\treturn;\n\t}\n\tconst orphans = detectOrphanedWorkers(entries, {\n\t\tisPidAlive: config.isProcessAlive,\n\t\townSessionId: config.sessionId,\n\t});\n\tif (orphans.length === 0) return;\n\n\tif (!config.hasUI) {\n\t\tconfig.onDiagnostic?.(\n\t\t\t`process-matrix: found ${orphans.length} orphaned worker(s) with no reachable parent (report-only, non-interactive; nothing written, nothing killed): ${orphans\n\t\t\t\t.map((orphan) => orphan.entryId)\n\t\t\t\t.join(\", \")}`,\n\t\t);\n\t\treturn;\n\t}\n\n\tfor (const orphan of orphans) {\n\t\tconst adopt = await config.promptConfirm(`adopt worker ${orphan.entryId} (lane ${orphan.laneKey ?? \"none\"})?`);\n\t\tif (adopt) {\n\t\t\tconst adopted = applyAdoption(orphan, { parentPid: process.pid, parentSessionId: config.sessionId });\n\t\t\ttry {\n\t\t\t\tawait writeEntry(config.agentDir, adopted);\n\t\t\t} catch (error) {\n\t\t\t\tconfig.onDiagnostic?.(\n\t\t\t\t\t`process-matrix: failed to write adoption for ${orphan.entryId}: ${describeError(error)}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tconst cleanup = await config.promptConfirm(`clean up worker ${orphan.entryId} gracefully?`);\n\t\tif (!cleanup) continue;\n\t\tconst windingDown = beginWindDown(orphan, \"user_cleanup\", nowIso(now));\n\t\ttry {\n\t\t\tawait writeEntry(config.agentDir, windingDown);\n\t\t} catch (error) {\n\t\t\tconfig.onDiagnostic?.(\n\t\t\t\t`process-matrix: failed to write cleanup for ${orphan.entryId}: ${describeError(error)}`,\n\t\t\t);\n\t\t}\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Worker branch\n// ---------------------------------------------------------------------------\n\nasync function startWorkerBranch(\n\tconfig: ProcessMatrixRuntimeConfig,\n\tinitialParentPid: number,\n\tnow: () => number,\n): Promise<ProcessMatrixRuntimeHandle> {\n\tconst parentSessionId = getParentSessionId();\n\tconst laneKey = getBoundWorktreeLaneKey();\n\n\tlet entry = buildWorkerEntry({\n\t\tsessionId: config.sessionId,\n\t\tpid: process.pid,\n\t\thostname: osHostname(),\n\t\tnow: nowIso(now),\n\t\tparentPid: initialParentPid,\n\t\t...(parentSessionId !== undefined ? { parentSessionId } : {}),\n\t\t...(laneKey !== undefined ? { laneKey } : {}),\n\t});\n\ttry {\n\t\tawait writeEntry(config.agentDir, entry);\n\t} catch (error) {\n\t\tconfig.onDiagnostic?.(`process-matrix: failed to register worker entry: ${describeError(error)}`);\n\t}\n\n\tlet currentParentPid = initialParentPid;\n\tlet currentParentSessionId = parentSessionId;\n\tlet stopped = false;\n\tlet timer: NodeJS.Timeout | undefined;\n\tlet ticking = false;\n\n\tconst stop = (): void => {\n\t\tstopped = true;\n\t\tif (timer) clearInterval(timer);\n\t\ttimer = undefined;\n\t};\n\n\tconst persist = async (next: ProcessMatrixEntry, failureContext: string): Promise<void> => {\n\t\tentry = next;\n\t\ttry {\n\t\t\tawait writeEntry(config.agentDir, entry);\n\t\t} catch (error) {\n\t\t\tconfig.onDiagnostic?.(`process-matrix: ${failureContext}: ${describeError(error)}`);\n\t\t}\n\t};\n\n\tconst startHealthyWatch = (): void => {\n\t\ttimer = setInterval(() => {\n\t\t\tif (ticking) return;\n\t\t\tticking = true;\n\t\t\tvoid healthyTick().finally(() => {\n\t\t\t\tticking = false;\n\t\t\t});\n\t\t}, config.settings.watcherPollMs);\n\t\ttimer.unref?.();\n\t};\n\n\tconst declaredParentIsAlive = async (): Promise<boolean> => {\n\t\t// PID liveness alone is not process identity: a reused PID could otherwise keep a worker\n\t\t// attached to an unrelated process forever. The parent session's own fresh master entry binds\n\t\t// PID to a durable identity and proves that that exact session is still heartbeating.\n\t\tif (!currentParentSessionId || !config.isProcessAlive(currentParentPid)) return false;\n\t\tconst parent = await readEntry(config.agentDir, buildEntryId(\"master\", currentParentSessionId));\n\t\tif (!parent || parent.role !== \"master\" || parent.sessionId !== currentParentSessionId) return false;\n\t\tif (parent.pid !== currentParentPid || parent.status !== \"running\") return false;\n\t\tconst heartbeatAt = Date.parse(parent.heartbeatAt);\n\t\tconst maxAge = config.settings.heartbeatMs * 2 + config.settings.watcherPollMs;\n\t\treturn Number.isFinite(heartbeatAt) && now() - heartbeatAt <= maxAge;\n\t};\n\n\tconst healthyTick = async (): Promise<void> => {\n\t\tif (stopped) return;\n\t\tif (!(await declaredParentIsAlive())) {\n\t\t\tawait enterWindDown();\n\t\t\treturn;\n\t\t}\n\t\t// Still healthy: also poll for a master-initiated cooperative-cleanup directive.\n\t\tconst fresh = await readEntry(config.agentDir, entry.entryId);\n\t\tif (!fresh) return;\n\t\tconst directive = pollWorkerDirective(fresh, currentParentPid, { isPidAlive: config.isProcessAlive });\n\t\tif (directive.code !== \"user_cleanup\") return;\n\t\tawait persist(\n\t\t\tbeginWindDown(fresh, \"user_cleanup\", nowIso(now)),\n\t\t\t\"failed to write a master-requested worker wind-down\",\n\t\t);\n\t\tconfig.notify(\"process-matrix: the parent session requested a cooperative cleanup. Winding down.\");\n\t\tstop();\n\t\tconfig.requestExit();\n\t};\n\n\tconst enterWindDown = async (): Promise<void> => {\n\t\tif (timer) {\n\t\t\tclearInterval(timer);\n\t\t\ttimer = undefined;\n\t\t}\n\t\tconst windDownAt = nowIso(now);\n\t\tconst resumable: ResumablePayload = { lastCode: \"resumable\" };\n\t\tif (laneKey !== undefined) resumable.laneKey = laneKey;\n\t\tawait persist(\n\t\t\tmarkResumable(beginWindDown(entry, \"parent_lost\", windDownAt), resumable, windDownAt),\n\t\t\t\"failed to write worker wind-down\",\n\t\t);\n\t\tconfig.notify(\n\t\t\t`process-matrix: parent process (pid ${currentParentPid}) is gone. Winding down gracefully; this task is resumable.`,\n\t\t);\n\t\tstartGraceWatch();\n\t};\n\n\tconst startGraceWatch = (): void => {\n\t\tconst graceDeadline = now() + config.settings.adoptionGraceMs;\n\t\ttimer = setInterval(() => {\n\t\t\tif (ticking) return;\n\t\t\tticking = true;\n\t\t\tvoid graceTick(graceDeadline).finally(() => {\n\t\t\t\tticking = false;\n\t\t\t});\n\t\t}, config.settings.watcherPollMs);\n\t\ttimer.unref?.();\n\t};\n\n\tconst graceTick = async (graceDeadline: number): Promise<void> => {\n\t\tif (stopped) return;\n\t\tconst fresh = await readEntry(config.agentDir, entry.entryId);\n\t\tif (fresh) {\n\t\t\tconst directive = pollWorkerDirective(fresh, currentParentPid, { isPidAlive: config.isProcessAlive });\n\t\t\tif (directive.code === \"adopt\" && fresh.parentSessionId) {\n\t\t\t\t// The adopting master persists its session id with the pid. Require both on the next\n\t\t\t\t// healthy tick; accepting a pid-only adoption would reintroduce the PID-reuse bug.\n\t\t\t\tawait persist(\n\t\t\t\t\tapplyAdoption(fresh, { parentPid: directive.parentPid, parentSessionId: fresh.parentSessionId }),\n\t\t\t\t\t\"failed to write worker adoption\",\n\t\t\t\t);\n\t\t\t\tconfig.notify(`process-matrix: adopted by a new parent (pid ${directive.parentPid}). Resuming.`);\n\t\t\t\tcurrentParentPid = directive.parentPid;\n\t\t\t\tcurrentParentSessionId = fresh.parentSessionId;\n\t\t\t\tif (timer) {\n\t\t\t\t\tclearInterval(timer);\n\t\t\t\t\ttimer = undefined;\n\t\t\t\t}\n\t\t\t\tstartHealthyWatch();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (directive.code === \"user_cleanup\") {\n\t\t\t\tstop();\n\t\t\t\tconfig.requestExit();\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tif (now() >= graceDeadline) {\n\t\t\tstop();\n\t\t\tconfig.requestExit();\n\t\t}\n\t};\n\n\tstartHealthyWatch();\n\n\treturn { stop };\n}\n"]}
@@ -0,0 +1,310 @@
1
+ /**
2
+ * Process-matrix runtime composition: the pieces main.ts wires together for durable,
3
+ * restart-surviving master/worker process supervision.
4
+ *
5
+ * Contract:
6
+ * - A WORKER is any process launched with a known parent (`PI_PARENT_PID` -- set by the
7
+ * `--parent-pid` CLI flag, or directly by a launcher such as tmux dispatch). It self-registers
8
+ * its OWN entry (the single writer of that entry during normal operation) and watches its
9
+ * parent's liveness. On parent death it winds down GRACEFULLY -- never silently -- leaving a
10
+ * `resumable` payload, then exits on its own after a bounded grace window (during which it may
11
+ * instead be adopted by a new parent). "No new turns" after that point is automatic: a dead
12
+ * parent injects no further follow-ups, so the worker simply runs out of work to do.
13
+ * - A MASTER is everything else (no known parent). On startup it scans the matrix for orphaned
14
+ * workers (workers whose recorded parent is dead) and, when interactive, ASKS the owner before
15
+ * touching anything -- adopt, cooperative cleanup, or leave untouched. Non-interactive: report
16
+ * only, zero writes, zero kills.
17
+ *
18
+ * One sanctioned exception to "a worker's entry is written only by that worker": the master
19
+ * orphan-scan writes an adoption or a cooperative-cleanup request directly into an ORPHANED
20
+ * worker's own entry, but ONLY after explicit owner confirmation (`promptConfirm`) -- mirroring
21
+ * the worktree-sync integration lock's dead-owner takeover (a new owner may claim a PROVABLY DEAD
22
+ * owner's resource). The worker itself later confirms/applies that grant locally via
23
+ * `pollWorkerDirective` and re-writes its own entry -- see `docs/process-matrix.md`. Outside this
24
+ * one ask-gated handshake, a master NEVER writes another session's entry, and nothing here ever
25
+ * kills a process directly: every termination is either owner-confirmed or the worker's own
26
+ * cooperative self-exit.
27
+ */
28
+ import { hostname as osHostname } from "node:os";
29
+ import { getBoundWorktreeLaneKey } from "../worktree-sync/runtime.js";
30
+ import { buildEntryId, listEntries, readEntry, writeEntry, writeEntrySync } from "./store.js";
31
+ import { applyAdoption, applyHeartbeat, beginWindDown, buildMasterEntry, buildWorkerEntry, detectOrphanedWorkers, markClosed, markResumable, pollWorkerDirective, } from "./supervisor.js";
32
+ export const PI_PARENT_PID_ENV = "PI_PARENT_PID";
33
+ export const PI_PARENT_SESSION_ENV = "PI_PARENT_SESSION";
34
+ /** This process's declared parent pid, from the cross-process env contract. A malformed or
35
+ * non-positive value is ignored (never a crash on bad env). */
36
+ export function getParentPid(env = process.env) {
37
+ const raw = env[PI_PARENT_PID_ENV];
38
+ if (raw === undefined)
39
+ return undefined;
40
+ const value = Number.parseInt(raw, 10);
41
+ return Number.isFinite(value) && value > 0 ? value : undefined;
42
+ }
43
+ /** This process's declared parent sessionId, from the cross-process env contract. */
44
+ export function getParentSessionId(env = process.env) {
45
+ const value = env[PI_PARENT_SESSION_ENV]?.trim();
46
+ return value && value.length > 0 ? value : undefined;
47
+ }
48
+ const NOOP_HANDLE = { stop: () => { } };
49
+ function describeError(error) {
50
+ return error instanceof Error ? error.message : String(error);
51
+ }
52
+ function nowIso(now) {
53
+ return new Date(now()).toISOString();
54
+ }
55
+ /**
56
+ * Start the per-session process-matrix runtime. No-op when disabled (byte-identical to not
57
+ * calling this at all). Never throws: a broken store must surface as a diagnostic, not a startup
58
+ * crash.
59
+ */
60
+ export async function startProcessMatrixRuntime(config) {
61
+ if (!config.settings.enabled)
62
+ return NOOP_HANDLE;
63
+ const now = config.now ?? Date.now;
64
+ const parentPid = getParentPid();
65
+ try {
66
+ if (parentPid !== undefined) {
67
+ return await startWorkerBranch(config, parentPid, now);
68
+ }
69
+ return await startMasterBranch(config, now);
70
+ }
71
+ catch (error) {
72
+ config.onDiagnostic?.(`process-matrix: runtime failed to start: ${describeError(error)}`);
73
+ return NOOP_HANDLE;
74
+ }
75
+ }
76
+ // ---------------------------------------------------------------------------
77
+ // Master branch
78
+ // ---------------------------------------------------------------------------
79
+ async function startMasterBranch(config, now) {
80
+ let entry = buildMasterEntry({
81
+ sessionId: config.sessionId,
82
+ pid: process.pid,
83
+ hostname: osHostname(),
84
+ now: nowIso(now),
85
+ });
86
+ try {
87
+ await writeEntry(config.agentDir, entry);
88
+ }
89
+ catch (error) {
90
+ config.onDiagnostic?.(`process-matrix: failed to register master entry: ${describeError(error)}`);
91
+ }
92
+ let stopped = false;
93
+ const heartbeatTimer = setInterval(() => {
94
+ if (stopped)
95
+ return;
96
+ entry = applyHeartbeat(entry, nowIso(now));
97
+ void writeEntry(config.agentDir, entry).catch((error) => {
98
+ config.onDiagnostic?.(`process-matrix: failed to write master heartbeat: ${describeError(error)}`);
99
+ });
100
+ }, config.settings.heartbeatMs);
101
+ heartbeatTimer.unref?.();
102
+ // Best-effort close on process exit. A SIGKILLed master leaving "running" is fine -- reconcile's
103
+ // own dead-pid detection covers it; this only makes the common clean-exit case tidy.
104
+ process.on("exit", () => {
105
+ try {
106
+ writeEntrySync(config.agentDir, markClosed(entry, nowIso(now)));
107
+ }
108
+ catch {
109
+ // Best-effort only -- see module doc.
110
+ }
111
+ });
112
+ void runOrphanScan(config, now);
113
+ return {
114
+ stop() {
115
+ stopped = true;
116
+ clearInterval(heartbeatTimer);
117
+ },
118
+ };
119
+ }
120
+ async function runOrphanScan(config, now) {
121
+ let entries;
122
+ try {
123
+ entries = await listEntries(config.agentDir);
124
+ }
125
+ catch (error) {
126
+ config.onDiagnostic?.(`process-matrix: orphan scan failed to list entries: ${describeError(error)}`);
127
+ return;
128
+ }
129
+ const orphans = detectOrphanedWorkers(entries, {
130
+ isPidAlive: config.isProcessAlive,
131
+ ownSessionId: config.sessionId,
132
+ });
133
+ if (orphans.length === 0)
134
+ return;
135
+ if (!config.hasUI) {
136
+ config.onDiagnostic?.(`process-matrix: found ${orphans.length} orphaned worker(s) with no reachable parent (report-only, non-interactive; nothing written, nothing killed): ${orphans
137
+ .map((orphan) => orphan.entryId)
138
+ .join(", ")}`);
139
+ return;
140
+ }
141
+ for (const orphan of orphans) {
142
+ const adopt = await config.promptConfirm(`adopt worker ${orphan.entryId} (lane ${orphan.laneKey ?? "none"})?`);
143
+ if (adopt) {
144
+ const adopted = applyAdoption(orphan, { parentPid: process.pid, parentSessionId: config.sessionId });
145
+ try {
146
+ await writeEntry(config.agentDir, adopted);
147
+ }
148
+ catch (error) {
149
+ config.onDiagnostic?.(`process-matrix: failed to write adoption for ${orphan.entryId}: ${describeError(error)}`);
150
+ }
151
+ continue;
152
+ }
153
+ const cleanup = await config.promptConfirm(`clean up worker ${orphan.entryId} gracefully?`);
154
+ if (!cleanup)
155
+ continue;
156
+ const windingDown = beginWindDown(orphan, "user_cleanup", nowIso(now));
157
+ try {
158
+ await writeEntry(config.agentDir, windingDown);
159
+ }
160
+ catch (error) {
161
+ config.onDiagnostic?.(`process-matrix: failed to write cleanup for ${orphan.entryId}: ${describeError(error)}`);
162
+ }
163
+ }
164
+ }
165
+ // ---------------------------------------------------------------------------
166
+ // Worker branch
167
+ // ---------------------------------------------------------------------------
168
+ async function startWorkerBranch(config, initialParentPid, now) {
169
+ const parentSessionId = getParentSessionId();
170
+ const laneKey = getBoundWorktreeLaneKey();
171
+ let entry = buildWorkerEntry({
172
+ sessionId: config.sessionId,
173
+ pid: process.pid,
174
+ hostname: osHostname(),
175
+ now: nowIso(now),
176
+ parentPid: initialParentPid,
177
+ ...(parentSessionId !== undefined ? { parentSessionId } : {}),
178
+ ...(laneKey !== undefined ? { laneKey } : {}),
179
+ });
180
+ try {
181
+ await writeEntry(config.agentDir, entry);
182
+ }
183
+ catch (error) {
184
+ config.onDiagnostic?.(`process-matrix: failed to register worker entry: ${describeError(error)}`);
185
+ }
186
+ let currentParentPid = initialParentPid;
187
+ let currentParentSessionId = parentSessionId;
188
+ let stopped = false;
189
+ let timer;
190
+ let ticking = false;
191
+ const stop = () => {
192
+ stopped = true;
193
+ if (timer)
194
+ clearInterval(timer);
195
+ timer = undefined;
196
+ };
197
+ const persist = async (next, failureContext) => {
198
+ entry = next;
199
+ try {
200
+ await writeEntry(config.agentDir, entry);
201
+ }
202
+ catch (error) {
203
+ config.onDiagnostic?.(`process-matrix: ${failureContext}: ${describeError(error)}`);
204
+ }
205
+ };
206
+ const startHealthyWatch = () => {
207
+ timer = setInterval(() => {
208
+ if (ticking)
209
+ return;
210
+ ticking = true;
211
+ void healthyTick().finally(() => {
212
+ ticking = false;
213
+ });
214
+ }, config.settings.watcherPollMs);
215
+ timer.unref?.();
216
+ };
217
+ const declaredParentIsAlive = async () => {
218
+ // PID liveness alone is not process identity: a reused PID could otherwise keep a worker
219
+ // attached to an unrelated process forever. The parent session's own fresh master entry binds
220
+ // PID to a durable identity and proves that that exact session is still heartbeating.
221
+ if (!currentParentSessionId || !config.isProcessAlive(currentParentPid))
222
+ return false;
223
+ const parent = await readEntry(config.agentDir, buildEntryId("master", currentParentSessionId));
224
+ if (!parent || parent.role !== "master" || parent.sessionId !== currentParentSessionId)
225
+ return false;
226
+ if (parent.pid !== currentParentPid || parent.status !== "running")
227
+ return false;
228
+ const heartbeatAt = Date.parse(parent.heartbeatAt);
229
+ const maxAge = config.settings.heartbeatMs * 2 + config.settings.watcherPollMs;
230
+ return Number.isFinite(heartbeatAt) && now() - heartbeatAt <= maxAge;
231
+ };
232
+ const healthyTick = async () => {
233
+ if (stopped)
234
+ return;
235
+ if (!(await declaredParentIsAlive())) {
236
+ await enterWindDown();
237
+ return;
238
+ }
239
+ // Still healthy: also poll for a master-initiated cooperative-cleanup directive.
240
+ const fresh = await readEntry(config.agentDir, entry.entryId);
241
+ if (!fresh)
242
+ return;
243
+ const directive = pollWorkerDirective(fresh, currentParentPid, { isPidAlive: config.isProcessAlive });
244
+ if (directive.code !== "user_cleanup")
245
+ return;
246
+ await persist(beginWindDown(fresh, "user_cleanup", nowIso(now)), "failed to write a master-requested worker wind-down");
247
+ config.notify("process-matrix: the parent session requested a cooperative cleanup. Winding down.");
248
+ stop();
249
+ config.requestExit();
250
+ };
251
+ const enterWindDown = async () => {
252
+ if (timer) {
253
+ clearInterval(timer);
254
+ timer = undefined;
255
+ }
256
+ const windDownAt = nowIso(now);
257
+ const resumable = { lastCode: "resumable" };
258
+ if (laneKey !== undefined)
259
+ resumable.laneKey = laneKey;
260
+ await persist(markResumable(beginWindDown(entry, "parent_lost", windDownAt), resumable, windDownAt), "failed to write worker wind-down");
261
+ config.notify(`process-matrix: parent process (pid ${currentParentPid}) is gone. Winding down gracefully; this task is resumable.`);
262
+ startGraceWatch();
263
+ };
264
+ const startGraceWatch = () => {
265
+ const graceDeadline = now() + config.settings.adoptionGraceMs;
266
+ timer = setInterval(() => {
267
+ if (ticking)
268
+ return;
269
+ ticking = true;
270
+ void graceTick(graceDeadline).finally(() => {
271
+ ticking = false;
272
+ });
273
+ }, config.settings.watcherPollMs);
274
+ timer.unref?.();
275
+ };
276
+ const graceTick = async (graceDeadline) => {
277
+ if (stopped)
278
+ return;
279
+ const fresh = await readEntry(config.agentDir, entry.entryId);
280
+ if (fresh) {
281
+ const directive = pollWorkerDirective(fresh, currentParentPid, { isPidAlive: config.isProcessAlive });
282
+ if (directive.code === "adopt" && fresh.parentSessionId) {
283
+ // The adopting master persists its session id with the pid. Require both on the next
284
+ // healthy tick; accepting a pid-only adoption would reintroduce the PID-reuse bug.
285
+ await persist(applyAdoption(fresh, { parentPid: directive.parentPid, parentSessionId: fresh.parentSessionId }), "failed to write worker adoption");
286
+ config.notify(`process-matrix: adopted by a new parent (pid ${directive.parentPid}). Resuming.`);
287
+ currentParentPid = directive.parentPid;
288
+ currentParentSessionId = fresh.parentSessionId;
289
+ if (timer) {
290
+ clearInterval(timer);
291
+ timer = undefined;
292
+ }
293
+ startHealthyWatch();
294
+ return;
295
+ }
296
+ if (directive.code === "user_cleanup") {
297
+ stop();
298
+ config.requestExit();
299
+ return;
300
+ }
301
+ }
302
+ if (now() >= graceDeadline) {
303
+ stop();
304
+ config.requestExit();
305
+ }
306
+ };
307
+ startHealthyWatch();
308
+ return { stop };
309
+ }
310
+ //# sourceMappingURL=runtime.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runtime.js","sourceRoot":"","sources":["../../../src/core/process-matrix/runtime.ts"],"names":[],"mappings":"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","sourcesContent":["/**\n * Process-matrix runtime composition: the pieces main.ts wires together for durable,\n * restart-surviving master/worker process supervision.\n *\n * Contract:\n * - A WORKER is any process launched with a known parent (`PI_PARENT_PID` -- set by the\n * `--parent-pid` CLI flag, or directly by a launcher such as tmux dispatch). It self-registers\n * its OWN entry (the single writer of that entry during normal operation) and watches its\n * parent's liveness. On parent death it winds down GRACEFULLY -- never silently -- leaving a\n * `resumable` payload, then exits on its own after a bounded grace window (during which it may\n * instead be adopted by a new parent). \"No new turns\" after that point is automatic: a dead\n * parent injects no further follow-ups, so the worker simply runs out of work to do.\n * - A MASTER is everything else (no known parent). On startup it scans the matrix for orphaned\n * workers (workers whose recorded parent is dead) and, when interactive, ASKS the owner before\n * touching anything -- adopt, cooperative cleanup, or leave untouched. Non-interactive: report\n * only, zero writes, zero kills.\n *\n * One sanctioned exception to \"a worker's entry is written only by that worker\": the master\n * orphan-scan writes an adoption or a cooperative-cleanup request directly into an ORPHANED\n * worker's own entry, but ONLY after explicit owner confirmation (`promptConfirm`) -- mirroring\n * the worktree-sync integration lock's dead-owner takeover (a new owner may claim a PROVABLY DEAD\n * owner's resource). The worker itself later confirms/applies that grant locally via\n * `pollWorkerDirective` and re-writes its own entry -- see `docs/process-matrix.md`. Outside this\n * one ask-gated handshake, a master NEVER writes another session's entry, and nothing here ever\n * kills a process directly: every termination is either owner-confirmed or the worker's own\n * cooperative self-exit.\n */\n\nimport { hostname as osHostname } from \"node:os\";\nimport type { ResolvedProcessMatrixSettings } from \"../settings-manager.ts\";\nimport { getBoundWorktreeLaneKey } from \"../worktree-sync/runtime.ts\";\nimport type { ProcessMatrixEntry, ResumablePayload } from \"./codes.ts\";\nimport { buildEntryId, listEntries, readEntry, writeEntry, writeEntrySync } from \"./store.ts\";\nimport {\n\tapplyAdoption,\n\tapplyHeartbeat,\n\tbeginWindDown,\n\tbuildMasterEntry,\n\tbuildWorkerEntry,\n\tdetectOrphanedWorkers,\n\tmarkClosed,\n\tmarkResumable,\n\tpollWorkerDirective,\n} from \"./supervisor.ts\";\n\nexport const PI_PARENT_PID_ENV = \"PI_PARENT_PID\";\nexport const PI_PARENT_SESSION_ENV = \"PI_PARENT_SESSION\";\n\n/** This process's declared parent pid, from the cross-process env contract. A malformed or\n * non-positive value is ignored (never a crash on bad env). */\nexport function getParentPid(env: NodeJS.ProcessEnv = process.env): number | undefined {\n\tconst raw = env[PI_PARENT_PID_ENV];\n\tif (raw === undefined) return undefined;\n\tconst value = Number.parseInt(raw, 10);\n\treturn Number.isFinite(value) && value > 0 ? value : undefined;\n}\n\n/** This process's declared parent sessionId, from the cross-process env contract. */\nexport function getParentSessionId(env: NodeJS.ProcessEnv = process.env): string | undefined {\n\tconst value = env[PI_PARENT_SESSION_ENV]?.trim();\n\treturn value && value.length > 0 ? value : undefined;\n}\n\nexport interface ProcessMatrixRuntimeConfig {\n\tagentDir: string;\n\tsessionId: string;\n\t/** Whether an interactive UI is available to ask the owner (see `promptConfirm`). */\n\thasUI: boolean;\n\tsettings: ResolvedProcessMatrixSettings;\n\tisProcessAlive: (pid: number) => boolean;\n\tnow?: () => number;\n\t/** Structural notice injection into the running session (host `sendCustomMessage` seam). */\n\tnotify: (text: string) => void;\n\t/** Diagnostics sink (never throws into the session). */\n\tonDiagnostic?: (message: string) => void;\n\t/** The ask seam: resolves false on decline AND on any non-interactive/non-TTY caller. */\n\tpromptConfirm: (message: string) => Promise<boolean>;\n\t/** Cooperative self-exit -- called by a worker once wound down (grace expiry or a\n\t * master-granted cleanup directive). Never called for the master's own lifecycle. */\n\trequestExit: () => void;\n}\n\nexport interface ProcessMatrixRuntimeHandle {\n\tstop(): void;\n}\n\nconst NOOP_HANDLE: ProcessMatrixRuntimeHandle = { stop: () => {} };\n\nfunction describeError(error: unknown): string {\n\treturn error instanceof Error ? error.message : String(error);\n}\n\nfunction nowIso(now: () => number): string {\n\treturn new Date(now()).toISOString();\n}\n\n/**\n * Start the per-session process-matrix runtime. No-op when disabled (byte-identical to not\n * calling this at all). Never throws: a broken store must surface as a diagnostic, not a startup\n * crash.\n */\nexport async function startProcessMatrixRuntime(\n\tconfig: ProcessMatrixRuntimeConfig,\n): Promise<ProcessMatrixRuntimeHandle> {\n\tif (!config.settings.enabled) return NOOP_HANDLE;\n\tconst now = config.now ?? Date.now;\n\tconst parentPid = getParentPid();\n\n\ttry {\n\t\tif (parentPid !== undefined) {\n\t\t\treturn await startWorkerBranch(config, parentPid, now);\n\t\t}\n\t\treturn await startMasterBranch(config, now);\n\t} catch (error) {\n\t\tconfig.onDiagnostic?.(`process-matrix: runtime failed to start: ${describeError(error)}`);\n\t\treturn NOOP_HANDLE;\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Master branch\n// ---------------------------------------------------------------------------\n\nasync function startMasterBranch(\n\tconfig: ProcessMatrixRuntimeConfig,\n\tnow: () => number,\n): Promise<ProcessMatrixRuntimeHandle> {\n\tlet entry = buildMasterEntry({\n\t\tsessionId: config.sessionId,\n\t\tpid: process.pid,\n\t\thostname: osHostname(),\n\t\tnow: nowIso(now),\n\t});\n\ttry {\n\t\tawait writeEntry(config.agentDir, entry);\n\t} catch (error) {\n\t\tconfig.onDiagnostic?.(`process-matrix: failed to register master entry: ${describeError(error)}`);\n\t}\n\n\tlet stopped = false;\n\tconst heartbeatTimer = setInterval(() => {\n\t\tif (stopped) return;\n\t\tentry = applyHeartbeat(entry, nowIso(now));\n\t\tvoid writeEntry(config.agentDir, entry).catch((error) => {\n\t\t\tconfig.onDiagnostic?.(`process-matrix: failed to write master heartbeat: ${describeError(error)}`);\n\t\t});\n\t}, config.settings.heartbeatMs);\n\theartbeatTimer.unref?.();\n\n\t// Best-effort close on process exit. A SIGKILLed master leaving \"running\" is fine -- reconcile's\n\t// own dead-pid detection covers it; this only makes the common clean-exit case tidy.\n\tprocess.on(\"exit\", () => {\n\t\ttry {\n\t\t\twriteEntrySync(config.agentDir, markClosed(entry, nowIso(now)));\n\t\t} catch {\n\t\t\t// Best-effort only -- see module doc.\n\t\t}\n\t});\n\n\tvoid runOrphanScan(config, now);\n\n\treturn {\n\t\tstop() {\n\t\t\tstopped = true;\n\t\t\tclearInterval(heartbeatTimer);\n\t\t},\n\t};\n}\n\nasync function runOrphanScan(config: ProcessMatrixRuntimeConfig, now: () => number): Promise<void> {\n\tlet entries: ProcessMatrixEntry[];\n\ttry {\n\t\tentries = await listEntries(config.agentDir);\n\t} catch (error) {\n\t\tconfig.onDiagnostic?.(`process-matrix: orphan scan failed to list entries: ${describeError(error)}`);\n\t\treturn;\n\t}\n\tconst orphans = detectOrphanedWorkers(entries, {\n\t\tisPidAlive: config.isProcessAlive,\n\t\townSessionId: config.sessionId,\n\t});\n\tif (orphans.length === 0) return;\n\n\tif (!config.hasUI) {\n\t\tconfig.onDiagnostic?.(\n\t\t\t`process-matrix: found ${orphans.length} orphaned worker(s) with no reachable parent (report-only, non-interactive; nothing written, nothing killed): ${orphans\n\t\t\t\t.map((orphan) => orphan.entryId)\n\t\t\t\t.join(\", \")}`,\n\t\t);\n\t\treturn;\n\t}\n\n\tfor (const orphan of orphans) {\n\t\tconst adopt = await config.promptConfirm(`adopt worker ${orphan.entryId} (lane ${orphan.laneKey ?? \"none\"})?`);\n\t\tif (adopt) {\n\t\t\tconst adopted = applyAdoption(orphan, { parentPid: process.pid, parentSessionId: config.sessionId });\n\t\t\ttry {\n\t\t\t\tawait writeEntry(config.agentDir, adopted);\n\t\t\t} catch (error) {\n\t\t\t\tconfig.onDiagnostic?.(\n\t\t\t\t\t`process-matrix: failed to write adoption for ${orphan.entryId}: ${describeError(error)}`,\n\t\t\t\t);\n\t\t\t}\n\t\t\tcontinue;\n\t\t}\n\t\tconst cleanup = await config.promptConfirm(`clean up worker ${orphan.entryId} gracefully?`);\n\t\tif (!cleanup) continue;\n\t\tconst windingDown = beginWindDown(orphan, \"user_cleanup\", nowIso(now));\n\t\ttry {\n\t\t\tawait writeEntry(config.agentDir, windingDown);\n\t\t} catch (error) {\n\t\t\tconfig.onDiagnostic?.(\n\t\t\t\t`process-matrix: failed to write cleanup for ${orphan.entryId}: ${describeError(error)}`,\n\t\t\t);\n\t\t}\n\t}\n}\n\n// ---------------------------------------------------------------------------\n// Worker branch\n// ---------------------------------------------------------------------------\n\nasync function startWorkerBranch(\n\tconfig: ProcessMatrixRuntimeConfig,\n\tinitialParentPid: number,\n\tnow: () => number,\n): Promise<ProcessMatrixRuntimeHandle> {\n\tconst parentSessionId = getParentSessionId();\n\tconst laneKey = getBoundWorktreeLaneKey();\n\n\tlet entry = buildWorkerEntry({\n\t\tsessionId: config.sessionId,\n\t\tpid: process.pid,\n\t\thostname: osHostname(),\n\t\tnow: nowIso(now),\n\t\tparentPid: initialParentPid,\n\t\t...(parentSessionId !== undefined ? { parentSessionId } : {}),\n\t\t...(laneKey !== undefined ? { laneKey } : {}),\n\t});\n\ttry {\n\t\tawait writeEntry(config.agentDir, entry);\n\t} catch (error) {\n\t\tconfig.onDiagnostic?.(`process-matrix: failed to register worker entry: ${describeError(error)}`);\n\t}\n\n\tlet currentParentPid = initialParentPid;\n\tlet currentParentSessionId = parentSessionId;\n\tlet stopped = false;\n\tlet timer: NodeJS.Timeout | undefined;\n\tlet ticking = false;\n\n\tconst stop = (): void => {\n\t\tstopped = true;\n\t\tif (timer) clearInterval(timer);\n\t\ttimer = undefined;\n\t};\n\n\tconst persist = async (next: ProcessMatrixEntry, failureContext: string): Promise<void> => {\n\t\tentry = next;\n\t\ttry {\n\t\t\tawait writeEntry(config.agentDir, entry);\n\t\t} catch (error) {\n\t\t\tconfig.onDiagnostic?.(`process-matrix: ${failureContext}: ${describeError(error)}`);\n\t\t}\n\t};\n\n\tconst startHealthyWatch = (): void => {\n\t\ttimer = setInterval(() => {\n\t\t\tif (ticking) return;\n\t\t\tticking = true;\n\t\t\tvoid healthyTick().finally(() => {\n\t\t\t\tticking = false;\n\t\t\t});\n\t\t}, config.settings.watcherPollMs);\n\t\ttimer.unref?.();\n\t};\n\n\tconst declaredParentIsAlive = async (): Promise<boolean> => {\n\t\t// PID liveness alone is not process identity: a reused PID could otherwise keep a worker\n\t\t// attached to an unrelated process forever. The parent session's own fresh master entry binds\n\t\t// PID to a durable identity and proves that that exact session is still heartbeating.\n\t\tif (!currentParentSessionId || !config.isProcessAlive(currentParentPid)) return false;\n\t\tconst parent = await readEntry(config.agentDir, buildEntryId(\"master\", currentParentSessionId));\n\t\tif (!parent || parent.role !== \"master\" || parent.sessionId !== currentParentSessionId) return false;\n\t\tif (parent.pid !== currentParentPid || parent.status !== \"running\") return false;\n\t\tconst heartbeatAt = Date.parse(parent.heartbeatAt);\n\t\tconst maxAge = config.settings.heartbeatMs * 2 + config.settings.watcherPollMs;\n\t\treturn Number.isFinite(heartbeatAt) && now() - heartbeatAt <= maxAge;\n\t};\n\n\tconst healthyTick = async (): Promise<void> => {\n\t\tif (stopped) return;\n\t\tif (!(await declaredParentIsAlive())) {\n\t\t\tawait enterWindDown();\n\t\t\treturn;\n\t\t}\n\t\t// Still healthy: also poll for a master-initiated cooperative-cleanup directive.\n\t\tconst fresh = await readEntry(config.agentDir, entry.entryId);\n\t\tif (!fresh) return;\n\t\tconst directive = pollWorkerDirective(fresh, currentParentPid, { isPidAlive: config.isProcessAlive });\n\t\tif (directive.code !== \"user_cleanup\") return;\n\t\tawait persist(\n\t\t\tbeginWindDown(fresh, \"user_cleanup\", nowIso(now)),\n\t\t\t\"failed to write a master-requested worker wind-down\",\n\t\t);\n\t\tconfig.notify(\"process-matrix: the parent session requested a cooperative cleanup. Winding down.\");\n\t\tstop();\n\t\tconfig.requestExit();\n\t};\n\n\tconst enterWindDown = async (): Promise<void> => {\n\t\tif (timer) {\n\t\t\tclearInterval(timer);\n\t\t\ttimer = undefined;\n\t\t}\n\t\tconst windDownAt = nowIso(now);\n\t\tconst resumable: ResumablePayload = { lastCode: \"resumable\" };\n\t\tif (laneKey !== undefined) resumable.laneKey = laneKey;\n\t\tawait persist(\n\t\t\tmarkResumable(beginWindDown(entry, \"parent_lost\", windDownAt), resumable, windDownAt),\n\t\t\t\"failed to write worker wind-down\",\n\t\t);\n\t\tconfig.notify(\n\t\t\t`process-matrix: parent process (pid ${currentParentPid}) is gone. Winding down gracefully; this task is resumable.`,\n\t\t);\n\t\tstartGraceWatch();\n\t};\n\n\tconst startGraceWatch = (): void => {\n\t\tconst graceDeadline = now() + config.settings.adoptionGraceMs;\n\t\ttimer = setInterval(() => {\n\t\t\tif (ticking) return;\n\t\t\tticking = true;\n\t\t\tvoid graceTick(graceDeadline).finally(() => {\n\t\t\t\tticking = false;\n\t\t\t});\n\t\t}, config.settings.watcherPollMs);\n\t\ttimer.unref?.();\n\t};\n\n\tconst graceTick = async (graceDeadline: number): Promise<void> => {\n\t\tif (stopped) return;\n\t\tconst fresh = await readEntry(config.agentDir, entry.entryId);\n\t\tif (fresh) {\n\t\t\tconst directive = pollWorkerDirective(fresh, currentParentPid, { isPidAlive: config.isProcessAlive });\n\t\t\tif (directive.code === \"adopt\" && fresh.parentSessionId) {\n\t\t\t\t// The adopting master persists its session id with the pid. Require both on the next\n\t\t\t\t// healthy tick; accepting a pid-only adoption would reintroduce the PID-reuse bug.\n\t\t\t\tawait persist(\n\t\t\t\t\tapplyAdoption(fresh, { parentPid: directive.parentPid, parentSessionId: fresh.parentSessionId }),\n\t\t\t\t\t\"failed to write worker adoption\",\n\t\t\t\t);\n\t\t\t\tconfig.notify(`process-matrix: adopted by a new parent (pid ${directive.parentPid}). Resuming.`);\n\t\t\t\tcurrentParentPid = directive.parentPid;\n\t\t\t\tcurrentParentSessionId = fresh.parentSessionId;\n\t\t\t\tif (timer) {\n\t\t\t\t\tclearInterval(timer);\n\t\t\t\t\ttimer = undefined;\n\t\t\t\t}\n\t\t\t\tstartHealthyWatch();\n\t\t\t\treturn;\n\t\t\t}\n\t\t\tif (directive.code === \"user_cleanup\") {\n\t\t\t\tstop();\n\t\t\t\tconfig.requestExit();\n\t\t\t\treturn;\n\t\t\t}\n\t\t}\n\t\tif (now() >= graceDeadline) {\n\t\t\tstop();\n\t\t\tconfig.requestExit();\n\t\t}\n\t};\n\n\tstartHealthyWatch();\n\n\treturn { stop };\n}\n"]}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Process-matrix on-disk store: one file per process (`state/process-matrix/<entryId>.json`),
3
+ * durable across restarts and crashes (see `agent-paths.ts` -- this is machine state, not
4
+ * transient `work/`).
5
+ *
6
+ * Each entry is owned by the process it describes: a master writes and heartbeats its OWN entry,
7
+ * a worker self-registers and heartbeats its OWN entry (see `runtime.ts`'s module doc for the one
8
+ * sanctioned, ask-gated exception during orphan adoption/cleanup). A missing or corrupt entry file
9
+ * reads as absent -- never an error -- matching `worktree-sync/store.ts`'s store doctrine.
10
+ */
11
+ import type { ProcessMatrixEntry, ProcessRole } from "./codes.ts";
12
+ export declare function processMatrixDir(agentDir: string): string;
13
+ export declare function entryPath(agentDir: string, entryId: string): string;
14
+ /** Stable entry identity: one entry per (role, sessionId) pair. */
15
+ export declare function buildEntryId(role: ProcessRole, sessionId: string): string;
16
+ export declare function readEntry(agentDir: string, entryId: string): Promise<ProcessMatrixEntry | undefined>;
17
+ export declare function listEntries(agentDir: string): Promise<ProcessMatrixEntry[]>;
18
+ export declare function writeEntry(agentDir: string, entry: ProcessMatrixEntry): Promise<void>;
19
+ /** Sync counterpart for the `process.on("exit")` best-effort master close -- see `runtime.ts`. */
20
+ export declare function writeEntrySync(agentDir: string, entry: ProcessMatrixEntry): void;
21
+ export declare function removeEntry(agentDir: string, entryId: string): Promise<void>;
22
+ //# sourceMappingURL=store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"store.d.ts","sourceRoot":"","sources":["../../../src/core/process-matrix/store.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAMH,OAAO,KAAK,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,YAAY,CAAC;AAElE,wBAAgB,gBAAgB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEzD;AAED,wBAAgB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,MAAM,CAEnE;AAED,mEAAmE;AACnE,wBAAgB,YAAY,CAAC,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,CAEzE;AAiBD,wBAAsB,SAAS,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,GAAG,SAAS,CAAC,CAE1G;AAED,wBAAsB,WAAW,CAAC,QAAQ,EAAE,MAAM,GAAG,OAAO,CAAC,kBAAkB,EAAE,CAAC,CAcjF;AAMD,wBAAsB,UAAU,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,GAAG,OAAO,CAAC,IAAI,CAAC,CAE3F;AAED,kGAAkG;AAClG,wBAAgB,cAAc,CAAC,QAAQ,EAAE,MAAM,EAAE,KAAK,EAAE,kBAAkB,GAAG,IAAI,CAEhF;AAED,wBAAsB,WAAW,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAMlF","sourcesContent":["/**\n * Process-matrix on-disk store: one file per process (`state/process-matrix/<entryId>.json`),\n * durable across restarts and crashes (see `agent-paths.ts` -- this is machine state, not\n * transient `work/`).\n *\n * Each entry is owned by the process it describes: a master writes and heartbeats its OWN entry,\n * a worker self-registers and heartbeats its OWN entry (see `runtime.ts`'s module doc for the one\n * sanctioned, ask-gated exception during orphan adoption/cleanup). A missing or corrupt entry file\n * reads as absent -- never an error -- matching `worktree-sync/store.ts`'s store doctrine.\n */\n\nimport { promises as fsPromises } from \"node:fs\";\nimport { join } from \"node:path\";\nimport { stateFile } from \"../agent-paths.ts\";\nimport { writeFileAtomic, writeFileAtomicSync } from \"../util/atomic-file.ts\";\nimport type { ProcessMatrixEntry, ProcessRole } from \"./codes.ts\";\n\nexport function processMatrixDir(agentDir: string): string {\n\treturn stateFile(agentDir, \"process-matrix\");\n}\n\nexport function entryPath(agentDir: string, entryId: string): string {\n\treturn join(processMatrixDir(agentDir), `${entryId}.json`);\n}\n\n/** Stable entry identity: one entry per (role, sessionId) pair. */\nexport function buildEntryId(role: ProcessRole, sessionId: string): string {\n\treturn `${role}-${sessionId}`;\n}\n\nasync function readJsonFile<T>(filePath: string): Promise<T | undefined> {\n\tlet raw: string;\n\ttry {\n\t\traw = await fsPromises.readFile(filePath, \"utf-8\");\n\t} catch {\n\t\treturn undefined;\n\t}\n\ttry {\n\t\treturn JSON.parse(raw) as T;\n\t} catch {\n\t\t// Corrupt entry files are treated as absent -- reconcile/re-registration rebuilds them.\n\t\treturn undefined;\n\t}\n}\n\nexport async function readEntry(agentDir: string, entryId: string): Promise<ProcessMatrixEntry | undefined> {\n\treturn readJsonFile<ProcessMatrixEntry>(entryPath(agentDir, entryId));\n}\n\nexport async function listEntries(agentDir: string): Promise<ProcessMatrixEntry[]> {\n\tlet names: string[];\n\ttry {\n\t\tnames = await fsPromises.readdir(processMatrixDir(agentDir));\n\t} catch {\n\t\treturn [];\n\t}\n\tconst entries: ProcessMatrixEntry[] = [];\n\tfor (const name of names.sort()) {\n\t\tif (!name.endsWith(\".json\")) continue;\n\t\tconst entry = await readJsonFile<ProcessMatrixEntry>(join(processMatrixDir(agentDir), name));\n\t\tif (entry?.entryId) entries.push(entry);\n\t}\n\treturn entries;\n}\n\nfunction serialize(entry: ProcessMatrixEntry): string {\n\treturn `${JSON.stringify(entry, null, \"\\t\")}\\n`;\n}\n\nexport async function writeEntry(agentDir: string, entry: ProcessMatrixEntry): Promise<void> {\n\tawait writeFileAtomic(entryPath(agentDir, entry.entryId), serialize(entry));\n}\n\n/** Sync counterpart for the `process.on(\"exit\")` best-effort master close -- see `runtime.ts`. */\nexport function writeEntrySync(agentDir: string, entry: ProcessMatrixEntry): void {\n\twriteFileAtomicSync(entryPath(agentDir, entry.entryId), serialize(entry));\n}\n\nexport async function removeEntry(agentDir: string, entryId: string): Promise<void> {\n\ttry {\n\t\tawait fsPromises.rm(entryPath(agentDir, entryId), { force: true });\n\t} catch {\n\t\t// Best-effort; a missing file is already the desired end state.\n\t}\n}\n"]}