@ours.network/fleet 1.2.0-nightly.6 → 1.2.0-nightly.61

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 (389) hide show
  1. package/README.md +287 -65
  2. package/dist/account-origin.d.ts +2 -0
  3. package/dist/account-origin.js +6 -0
  4. package/dist/agent-ours/bridge.js +35 -9
  5. package/dist/agent-ours/correspondence.d.ts +10 -0
  6. package/dist/agent-ours/correspondence.js +1 -0
  7. package/dist/agent-ours/file-rpc.d.ts +12 -0
  8. package/dist/agent-ours/file-rpc.js +18 -4
  9. package/dist/agent-ours/harness.js +9 -1
  10. package/dist/agent-ours/mcp-endpoint.d.ts +4 -0
  11. package/dist/agent-ours/mcp-endpoint.js +5 -0
  12. package/dist/agent-ours/runtime.d.ts +9 -1
  13. package/dist/agent-ours/runtime.js +50 -2
  14. package/dist/agent-ours/service.d.ts +21 -1
  15. package/dist/agent-ours/service.js +94 -14
  16. package/dist/application/codex-models.d.ts +15 -0
  17. package/dist/application/codex-models.js +56 -0
  18. package/dist/application/conversation-history.d.ts +27 -0
  19. package/dist/application/conversation-history.js +212 -0
  20. package/dist/application/fleet-query-service.d.ts +1 -1
  21. package/dist/application/fleet-query-service.js +2 -2
  22. package/dist/application/legacy-supervisor-identity.d.ts +7 -0
  23. package/dist/application/legacy-supervisor-identity.js +43 -0
  24. package/dist/application/log-service.js +8 -8
  25. package/dist/application/model-catalog.d.ts +1 -1
  26. package/dist/application/model-catalog.js +5 -2
  27. package/dist/application/preset-provenance.d.ts +19 -0
  28. package/dist/application/preset-provenance.js +59 -0
  29. package/dist/application/role-creation-service.js +3 -2
  30. package/dist/application/role-removal-service.js +9 -1
  31. package/dist/application/role-repository.js +19 -2
  32. package/dist/application/room-layout-definitions.d.ts +45 -0
  33. package/dist/application/room-layout-definitions.js +130 -0
  34. package/dist/application/session-control.d.ts +30 -0
  35. package/dist/application/session-control.js +72 -3
  36. package/dist/application/supervisor-ours-tools.d.ts +159 -0
  37. package/dist/application/supervisor-ours-tools.js +115 -0
  38. package/dist/application/task-layouts.d.ts +91 -0
  39. package/dist/application/task-layouts.js +259 -0
  40. package/dist/application/task-room-service.d.ts +99 -3
  41. package/dist/application/task-room-service.js +285 -32
  42. package/dist/application/types.d.ts +2 -0
  43. package/dist/atomic-file.d.ts +5 -0
  44. package/dist/atomic-file.js +62 -1
  45. package/dist/briefing.d.ts +3 -1
  46. package/dist/briefing.js +70 -30
  47. package/dist/build-info.json +7 -4
  48. package/dist/capabilities.d.ts +6 -1
  49. package/dist/capabilities.js +8 -0
  50. package/dist/cli.js +407 -37
  51. package/dist/client-profile.d.ts +5 -17
  52. package/dist/client-profile.js +4 -127
  53. package/dist/config.d.ts +45 -0
  54. package/dist/config.js +57 -6
  55. package/dist/creation.d.ts +1 -1
  56. package/dist/creation.js +4 -4
  57. package/dist/daemon-recovery.d.ts +1 -8
  58. package/dist/daemon-recovery.js +8 -113
  59. package/dist/docs.d.ts +1 -1
  60. package/dist/docs.js +123 -19
  61. package/dist/doctor.d.ts +1 -1
  62. package/dist/doctor.js +55 -60
  63. package/dist/erased-resources.d.ts +9 -0
  64. package/dist/erased-resources.js +87 -0
  65. package/dist/file-delivery/reader.d.ts +15 -0
  66. package/dist/file-delivery/reader.js +63 -0
  67. package/dist/file-delivery/store.d.ts +15 -0
  68. package/dist/file-delivery/store.js +124 -0
  69. package/dist/file-delivery/types.d.ts +19 -0
  70. package/dist/file-delivery/types.js +1 -0
  71. package/dist/fleet-command-audit.d.ts +7 -0
  72. package/dist/fleet-command-audit.js +26 -9
  73. package/dist/harness/agent-session.d.ts +2 -0
  74. package/dist/harness/claude-code-session.js +1 -1
  75. package/dist/harness/claude-code.js +20 -11
  76. package/dist/harness/claude-runtime.d.ts +12 -0
  77. package/dist/harness/claude-runtime.js +43 -0
  78. package/dist/harness/codex-app-server-proxy.d.ts +4 -1
  79. package/dist/harness/codex-app-server-proxy.js +41 -5
  80. package/dist/harness/codex-reasoning.d.ts +5 -0
  81. package/dist/harness/codex-reasoning.js +12 -0
  82. package/dist/harness/codex-runtime.d.ts +11 -1
  83. package/dist/harness/codex-runtime.js +31 -4
  84. package/dist/harness/codex-session.js +2 -2
  85. package/dist/harness/codex.js +23 -12
  86. package/dist/init-wizard.d.ts +2 -0
  87. package/dist/init-wizard.js +38 -19
  88. package/dist/isolation/policy.js +6 -1
  89. package/dist/isolation/types.d.ts +5 -0
  90. package/dist/managed-cli.d.ts +253 -0
  91. package/dist/managed-cli.js +923 -0
  92. package/dist/managed-recovery.d.ts +13 -0
  93. package/dist/managed-recovery.js +31 -0
  94. package/dist/minimal-setup.d.ts +2 -0
  95. package/dist/minimal-setup.js +44 -0
  96. package/dist/monitor.d.ts +8 -34
  97. package/dist/monitor.js +89 -152
  98. package/dist/notifications/fleet-producer.d.ts +19 -0
  99. package/dist/notifications/fleet-producer.js +208 -0
  100. package/dist/notifications/outbox.d.ts +46 -0
  101. package/dist/notifications/outbox.js +158 -0
  102. package/dist/notifications/target-cleanup.d.ts +8 -0
  103. package/dist/notifications/target-cleanup.js +41 -0
  104. package/dist/notifications/task-producer.d.ts +18 -0
  105. package/dist/notifications/task-producer.js +67 -0
  106. package/dist/ops.d.ts +2 -0
  107. package/dist/ops.js +20 -3
  108. package/dist/owner-channel/channel.js +17 -6
  109. package/dist/owner-channel/commands.d.ts +4 -0
  110. package/dist/owner-channel/commands.js +8 -0
  111. package/dist/owner-channel/lifecycle-outbox.js +4 -3
  112. package/dist/owner-channel/ours-client.js +2 -17
  113. package/dist/paths.d.ts +2 -0
  114. package/dist/paths.js +2 -0
  115. package/dist/preset-migration.js +25 -0
  116. package/dist/resolved-plan.js +1 -0
  117. package/dist/rooms-tasks/add-member.d.ts +48 -0
  118. package/dist/rooms-tasks/add-member.js +83 -0
  119. package/dist/rooms-tasks/archived-absence.d.ts +8 -0
  120. package/dist/rooms-tasks/archived-absence.js +48 -0
  121. package/dist/rooms-tasks/cli.js +65 -5
  122. package/dist/rooms-tasks/close.d.ts +6 -2
  123. package/dist/rooms-tasks/close.js +246 -37
  124. package/dist/rooms-tasks/config.d.ts +1 -1
  125. package/dist/rooms-tasks/config.js +11 -4
  126. package/dist/rooms-tasks/cowork-adapter.d.ts +2 -4
  127. package/dist/rooms-tasks/cowork-adapter.js +7 -121
  128. package/dist/rooms-tasks/cowork-http.js +29 -1
  129. package/dist/rooms-tasks/deletion.d.ts +1 -1
  130. package/dist/rooms-tasks/deletion.js +84 -5
  131. package/dist/rooms-tasks/erasure.d.ts +6 -0
  132. package/dist/rooms-tasks/erasure.js +173 -0
  133. package/dist/rooms-tasks/external-worker.d.ts +1 -1
  134. package/dist/rooms-tasks/external-worker.js +3 -4
  135. package/dist/rooms-tasks/layout-binding-client.d.ts +11 -0
  136. package/dist/rooms-tasks/layout-binding-client.js +56 -0
  137. package/dist/rooms-tasks/layout-binding-grants.d.ts +21 -0
  138. package/dist/rooms-tasks/layout-binding-grants.js +128 -0
  139. package/dist/rooms-tasks/layout-cli.d.ts +4 -0
  140. package/dist/rooms-tasks/layout-cli.js +107 -0
  141. package/dist/rooms-tasks/layout-config.d.ts +11 -0
  142. package/dist/rooms-tasks/layout-config.js +89 -0
  143. package/dist/rooms-tasks/layout-control.d.ts +21 -0
  144. package/dist/rooms-tasks/layout-control.js +52 -0
  145. package/dist/rooms-tasks/layout-member-retirement.d.ts +10 -0
  146. package/dist/rooms-tasks/layout-member-retirement.js +90 -0
  147. package/dist/rooms-tasks/layout-service.d.ts +43 -0
  148. package/dist/rooms-tasks/layout-service.js +203 -0
  149. package/dist/rooms-tasks/layout.d.ts +121 -0
  150. package/dist/rooms-tasks/layout.js +285 -0
  151. package/dist/rooms-tasks/legacy-task-member.d.ts +19 -0
  152. package/dist/rooms-tasks/legacy-task-member.js +116 -0
  153. package/dist/rooms-tasks/live-readiness.d.ts +18 -0
  154. package/dist/rooms-tasks/live-readiness.js +85 -0
  155. package/dist/rooms-tasks/member-ownership.d.ts +5 -0
  156. package/dist/rooms-tasks/member-ownership.js +40 -0
  157. package/dist/rooms-tasks/member-startup.d.ts +1 -0
  158. package/dist/rooms-tasks/member-startup.js +2 -0
  159. package/dist/rooms-tasks/provision.d.ts +3 -0
  160. package/dist/rooms-tasks/provision.js +101 -49
  161. package/dist/rooms-tasks/recovery.d.ts +19 -0
  162. package/dist/rooms-tasks/recovery.js +110 -0
  163. package/dist/rooms-tasks/room-state.d.ts +4 -2
  164. package/dist/rooms-tasks/room-state.js +47 -8
  165. package/dist/rooms-tasks/task-service-retirement.d.ts +11 -0
  166. package/dist/rooms-tasks/task-service-retirement.js +21 -0
  167. package/dist/rooms-tasks/task-state.d.ts +51 -8
  168. package/dist/rooms-tasks/task-state.js +245 -25
  169. package/dist/rooms-tasks/types.d.ts +40 -0
  170. package/dist/rooms-tasks/workspace-artifacts.d.ts +6 -0
  171. package/dist/rooms-tasks/workspace-artifacts.js +93 -0
  172. package/dist/rooms-tasks/workspace.d.ts +35 -0
  173. package/dist/rooms-tasks/workspace.js +325 -0
  174. package/dist/runner.d.ts +25 -3
  175. package/dist/runner.js +291 -49
  176. package/dist/session/acp.d.ts +53 -1
  177. package/dist/session/acp.js +216 -40
  178. package/dist/session/arbiter.d.ts +11 -0
  179. package/dist/session/arbiter.js +43 -2
  180. package/dist/session/codex-app-server.d.ts +32 -0
  181. package/dist/session/codex-app-server.js +137 -23
  182. package/dist/session/control.d.ts +12 -1
  183. package/dist/session/control.js +68 -3
  184. package/dist/session/conversation-normalizer.d.ts +2 -1
  185. package/dist/session/conversation-normalizer.js +1 -1
  186. package/dist/session/conversation-store.d.ts +1 -1
  187. package/dist/session/conversation-store.js +7 -11
  188. package/dist/session/conversation-types.d.ts +28 -3
  189. package/dist/session/prompt-batch.d.ts +63 -0
  190. package/dist/session/prompt-batch.js +86 -0
  191. package/dist/session/task-notice.d.ts +14 -0
  192. package/dist/session/task-notice.js +13 -0
  193. package/dist/session/types.d.ts +14 -0
  194. package/dist/setup-tunnel-preflight.d.ts +34 -0
  195. package/dist/setup-tunnel-preflight.js +62 -0
  196. package/dist/socket-path.d.ts +2 -0
  197. package/dist/socket-path.js +22 -0
  198. package/dist/spawn.d.ts +5 -1
  199. package/dist/spawn.js +40 -4
  200. package/dist/startup-prompt.d.ts +4 -0
  201. package/dist/startup-prompt.js +36 -0
  202. package/dist/startup-readiness.d.ts +13 -0
  203. package/dist/startup-readiness.js +38 -0
  204. package/dist/startup-telemetry.d.ts +16 -0
  205. package/dist/startup-telemetry.js +46 -0
  206. package/dist/subscriptions/cli.d.ts +91 -0
  207. package/dist/subscriptions/cli.js +223 -0
  208. package/dist/subscriptions/launch.d.ts +37 -0
  209. package/dist/subscriptions/launch.js +93 -0
  210. package/dist/subscriptions/login.d.ts +75 -0
  211. package/dist/subscriptions/login.js +182 -0
  212. package/dist/subscriptions/observed.d.ts +7 -0
  213. package/dist/subscriptions/observed.js +44 -0
  214. package/dist/subscriptions/probe.d.ts +10 -0
  215. package/dist/subscriptions/probe.js +73 -0
  216. package/dist/subscriptions/service.d.ts +102 -0
  217. package/dist/subscriptions/service.js +364 -0
  218. package/dist/subscriptions/store.d.ts +71 -0
  219. package/dist/subscriptions/store.js +140 -0
  220. package/dist/supervisor/adoption.d.ts +8 -0
  221. package/dist/supervisor/adoption.js +78 -0
  222. package/dist/supervisor/catalog.d.ts +62 -0
  223. package/dist/supervisor/catalog.js +254 -0
  224. package/dist/supervisor/fleet.d.ts +8 -0
  225. package/dist/supervisor/fleet.js +89 -0
  226. package/dist/supervisor/index.js +3 -6
  227. package/dist/supervisor/launchd.d.ts +1 -1
  228. package/dist/supervisor/launchd.js +26 -13
  229. package/dist/supervisor/legacy.d.ts +35 -0
  230. package/dist/supervisor/legacy.js +524 -0
  231. package/dist/supervisor/manager.d.ts +15 -0
  232. package/dist/supervisor/manager.js +270 -0
  233. package/dist/supervisor/scope.d.ts +4 -0
  234. package/dist/supervisor/scope.js +10 -0
  235. package/dist/supervisor/systemd.d.ts +2 -2
  236. package/dist/supervisor/systemd.js +19 -33
  237. package/dist/task-supervision.d.ts +9 -0
  238. package/dist/task-supervision.js +145 -0
  239. package/dist/temp-idle.d.ts +17 -0
  240. package/dist/temp-idle.js +42 -0
  241. package/dist/temp-lifecycle.d.ts +23 -8
  242. package/dist/temp-lifecycle.js +113 -65
  243. package/dist/temp-supervisor-recovery.d.ts +1 -1
  244. package/dist/temp-supervisor-recovery.js +2 -2
  245. package/dist/tunnel-setup-completion.d.ts +7 -0
  246. package/dist/tunnel-setup-completion.js +13 -0
  247. package/dist/tunnel-setup-input.d.ts +19 -0
  248. package/dist/tunnel-setup-input.js +124 -0
  249. package/dist/web/agent-attachments.d.ts +29 -0
  250. package/dist/web/agent-attachments.js +162 -0
  251. package/dist/web/auth.d.ts +63 -2
  252. package/dist/web/auth.js +191 -10
  253. package/dist/web/available-port.d.ts +2 -0
  254. package/dist/web/available-port.js +23 -0
  255. package/dist/web/control.d.ts +3 -3
  256. package/dist/web/control.js +10 -8
  257. package/dist/web/fleet-config-service.d.ts +4 -1
  258. package/dist/web/fleet-config-service.js +46 -15
  259. package/dist/web/prefix-gateway.d.ts +32 -0
  260. package/dist/web/prefix-gateway.js +238 -0
  261. package/dist/web/runtime.d.ts +3 -1
  262. package/dist/web/runtime.js +116 -31
  263. package/dist/web/server.d.ts +16 -0
  264. package/dist/web/server.js +654 -55
  265. package/dist/web/service.d.ts +11 -2
  266. package/dist/web/service.js +30 -4
  267. package/dist/web/workspace-devices.d.ts +107 -0
  268. package/dist/web/workspace-devices.js +277 -0
  269. package/dist/web/workspace-tunnel.d.ts +1 -0
  270. package/dist/web/workspace-tunnel.js +34 -0
  271. package/dist/workspace-enrollment.d.ts +82 -0
  272. package/dist/workspace-enrollment.js +566 -0
  273. package/dist/workspace-replacement.d.ts +66 -0
  274. package/dist/workspace-replacement.js +220 -0
  275. package/docs/acp-files.md +146 -0
  276. package/docs/fleet-migration.md +54 -0
  277. package/docs/live-voice.md +13 -0
  278. package/docs/room-layouts.md +227 -0
  279. package/docs/task-workspaces.md +225 -0
  280. package/docs/validation/managed-cli-permissions.md +421 -0
  281. package/examples/room-layouts/fresh.yaml +27 -0
  282. package/examples/room-layouts/scoped.yaml +21 -0
  283. package/examples/room-layouts/shared.yaml +19 -0
  284. package/package.json +20 -19
  285. package/presets/README.md +78 -0
  286. package/presets/brain-catalog.json +8 -2
  287. package/presets/fleet/agent_templates/Critic.yaml +1 -1
  288. package/presets/fleet/agent_templates/Developer.yaml +1 -1
  289. package/presets/fleet/agent_templates/Engineer.yaml +5 -0
  290. package/presets/fleet/agent_templates/LocalCoordinator.yaml +1 -1
  291. package/presets/fleet/agents/FleetCoordinator.yaml +1 -1
  292. package/presets/fleet/brains/claude-fable-5-1-high.yaml +5 -0
  293. package/presets/fleet/brains/claude-fable-5-1-low.yaml +5 -0
  294. package/presets/fleet/brains/claude-fable-5-1-max.yaml +5 -0
  295. package/presets/fleet/brains/claude-fable-5-1-medium.yaml +5 -0
  296. package/presets/fleet/brains/claude-fable-5-1-xhigh.yaml +5 -0
  297. package/presets/fleet/brains/claude-fable-5-high.yaml +1 -1
  298. package/presets/fleet/brains/claude-fable-5-low.yaml +1 -1
  299. package/presets/fleet/brains/claude-fable-5-max.yaml +1 -1
  300. package/presets/fleet/brains/claude-fable-5-medium.yaml +1 -1
  301. package/presets/fleet/brains/claude-fable-5-xhigh.yaml +1 -1
  302. package/presets/fleet/brains/claude-haiku-4-5-20251001-default.yaml +4 -0
  303. package/presets/fleet/brains/claude-opus-5-5-high.yaml +5 -0
  304. package/presets/fleet/brains/claude-opus-5-5-low.yaml +5 -0
  305. package/presets/fleet/brains/claude-opus-5-5-max.yaml +5 -0
  306. package/presets/fleet/brains/claude-opus-5-5-medium.yaml +5 -0
  307. package/presets/fleet/brains/claude-opus-5-5-xhigh.yaml +5 -0
  308. package/presets/fleet/brains/claude-opus-5-high.yaml +1 -1
  309. package/presets/fleet/brains/claude-opus-5-low.yaml +1 -1
  310. package/presets/fleet/brains/claude-opus-5-max.yaml +1 -1
  311. package/presets/fleet/brains/claude-opus-5-medium.yaml +1 -1
  312. package/presets/fleet/brains/claude-opus-5-xhigh.yaml +1 -1
  313. package/presets/fleet/brains/claude-sonnet-5-high.yaml +1 -1
  314. package/presets/fleet/brains/claude-sonnet-5-low.yaml +1 -1
  315. package/presets/fleet/brains/claude-sonnet-5-max.yaml +1 -1
  316. package/presets/fleet/brains/claude-sonnet-5-medium.yaml +1 -1
  317. package/presets/fleet/brains/claude-sonnet-5-xhigh.yaml +1 -1
  318. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-high.yaml +1 -1
  319. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-low.yaml +1 -1
  320. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-medium.yaml +1 -1
  321. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-xhigh.yaml +1 -1
  322. package/presets/fleet/brains/codex-gpt-5-4-high.yaml +1 -1
  323. package/presets/fleet/brains/codex-gpt-5-4-low.yaml +1 -1
  324. package/presets/fleet/brains/codex-gpt-5-4-medium.yaml +1 -1
  325. package/presets/fleet/brains/codex-gpt-5-4-mini-high.yaml +1 -1
  326. package/presets/fleet/brains/codex-gpt-5-4-mini-low.yaml +1 -1
  327. package/presets/fleet/brains/codex-gpt-5-4-mini-medium.yaml +1 -1
  328. package/presets/fleet/brains/codex-gpt-5-4-mini-xhigh.yaml +1 -1
  329. package/presets/fleet/brains/codex-gpt-5-4-xhigh.yaml +1 -1
  330. package/presets/fleet/brains/codex-gpt-5-5-high.yaml +1 -1
  331. package/presets/fleet/brains/codex-gpt-5-5-low.yaml +1 -1
  332. package/presets/fleet/brains/codex-gpt-5-5-medium.yaml +1 -1
  333. package/presets/fleet/brains/codex-gpt-5-5-xhigh.yaml +1 -1
  334. package/presets/fleet/brains/codex-gpt-5-6-luna-high.yaml +1 -1
  335. package/presets/fleet/brains/codex-gpt-5-6-luna-low.yaml +1 -1
  336. package/presets/fleet/brains/codex-gpt-5-6-luna-max.yaml +1 -1
  337. package/presets/fleet/brains/codex-gpt-5-6-luna-medium.yaml +1 -1
  338. package/presets/fleet/brains/codex-gpt-5-6-luna-xhigh.yaml +1 -1
  339. package/presets/fleet/brains/codex-gpt-5-6-sol-high.yaml +1 -1
  340. package/presets/fleet/brains/codex-gpt-5-6-sol-low.yaml +1 -1
  341. package/presets/fleet/brains/codex-gpt-5-6-sol-max.yaml +1 -1
  342. package/presets/fleet/brains/codex-gpt-5-6-sol-medium.yaml +1 -1
  343. package/presets/fleet/brains/codex-gpt-5-6-sol-ultra.yaml +1 -1
  344. package/presets/fleet/brains/codex-gpt-5-6-sol-xhigh.yaml +1 -1
  345. package/presets/fleet/brains/codex-gpt-5-6-terra-high.yaml +1 -1
  346. package/presets/fleet/brains/codex-gpt-5-6-terra-low.yaml +1 -1
  347. package/presets/fleet/brains/codex-gpt-5-6-terra-max.yaml +1 -1
  348. package/presets/fleet/brains/codex-gpt-5-6-terra-medium.yaml +1 -1
  349. package/presets/fleet/brains/codex-gpt-5-6-terra-ultra.yaml +1 -1
  350. package/presets/fleet/brains/codex-gpt-5-6-terra-xhigh.yaml +1 -1
  351. package/presets/fleet/brains/codex-gpt-6-1-sol-high.yaml +5 -0
  352. package/presets/fleet/brains/codex-gpt-6-1-sol-low.yaml +5 -0
  353. package/presets/fleet/brains/codex-gpt-6-1-sol-max.yaml +5 -0
  354. package/presets/fleet/brains/codex-gpt-6-1-sol-medium.yaml +5 -0
  355. package/presets/fleet/brains/codex-gpt-6-1-sol-ultra.yaml +5 -0
  356. package/presets/fleet/brains/codex-gpt-6-1-sol-xhigh.yaml +5 -0
  357. package/presets/fleet/brains/codex-gpt-6-astra-high.yaml +1 -1
  358. package/presets/fleet/brains/codex-gpt-6-astra-low.yaml +1 -1
  359. package/presets/fleet/brains/codex-gpt-6-astra-max.yaml +1 -1
  360. package/presets/fleet/brains/codex-gpt-6-astra-medium.yaml +1 -1
  361. package/presets/fleet/brains/codex-gpt-6-astra-ultra.yaml +1 -1
  362. package/presets/fleet/brains/codex-gpt-6-astra-xhigh.yaml +1 -1
  363. package/presets/fleet/brains/codex-gpt-6-luna-high.yaml +5 -0
  364. package/presets/fleet/brains/codex-gpt-6-luna-low.yaml +5 -0
  365. package/presets/fleet/brains/codex-gpt-6-luna-max.yaml +5 -0
  366. package/presets/fleet/brains/codex-gpt-6-luna-medium.yaml +5 -0
  367. package/presets/fleet/brains/codex-gpt-6-luna-xhigh.yaml +5 -0
  368. package/presets/fleet/brains/codex-gpt-6-sol-high.yaml +5 -0
  369. package/presets/fleet/brains/codex-gpt-6-sol-low.yaml +5 -0
  370. package/presets/fleet/brains/codex-gpt-6-sol-max.yaml +5 -0
  371. package/presets/fleet/brains/codex-gpt-6-sol-medium.yaml +5 -0
  372. package/presets/fleet/brains/codex-gpt-6-sol-ultra.yaml +5 -0
  373. package/presets/fleet/brains/codex-gpt-6-sol-xhigh.yaml +5 -0
  374. package/presets/fleet/roles/Coordinator.yaml +34 -11
  375. package/presets/fleet/roles/Engineer.yaml +40 -0
  376. package/presets/fleet/room_layouts/engineering.yaml +25 -0
  377. package/presets/fleet/room_layouts/pair.yaml +24 -0
  378. package/presets/fleet/room_layouts/single.yaml +17 -0
  379. package/presets/fleet/room_layouts/team.yaml +30 -0
  380. package/presets/fleet/room_templates/engineering.yaml +19 -0
  381. package/presets/manifest.json +2 -2
  382. package/dist/web-app/assets/index-59GF-gsZ.css +0 -1
  383. package/dist/web-app/assets/index-C0h3ALvs.js +0 -10
  384. package/dist/web-app/icons/ours-fleet-maskable.svg +0 -4
  385. package/dist/web-app/icons/ours-fleet.svg +0 -4
  386. package/dist/web-app/index.html +0 -17
  387. package/dist/web-app/manifest.webmanifest +0 -15
  388. package/dist/web-app/offline.html +0 -18
  389. package/dist/web-app/sw.js +0 -51
@@ -0,0 +1,270 @@
1
+ import { spawn } from 'node:child_process';
2
+ import { closeSync, existsSync, openSync, rmSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { randomUUID } from 'node:crypto';
5
+ import { producerConfig } from '../notifications/outbox.js';
6
+ import { TaskNotificationProducer } from '../notifications/task-producer.js';
7
+ import { acquireOwnerBinderLease } from '../owner-channel/binder.js';
8
+ import { stateRoot } from '../paths.js';
9
+ import { readRestartLedger, writeRestartLedger, backoffFor, RESTART_FAIL_THRESHOLD } from '../runner.js';
10
+ import { realExec } from '../exec.js';
11
+ import { listMemberKeys, managedMemberEnvironment, memberPath, memberProcess, readMember, withMemberLock, writeMember } from './catalog.js';
12
+ /** One parent owns process creation for every kind of Fleet member. */
13
+ export async function runFleetManager(entrypoint, deps = {}) {
14
+ const lease = await acquireOwnerBinderLease(join(stateRoot(), 'supervisor'), 'fleet-manager', 'fleet-manager');
15
+ const children = new Map();
16
+ const now = deps.now ?? Date.now;
17
+ const log = deps.log ?? (message => console.error(message));
18
+ const exec = deps.exec ?? realExec;
19
+ const sleep = deps.sleep ?? (ms => new Promise(resolve => setTimeout(resolve, ms)));
20
+ let stopping = false;
21
+ const stop = () => { stopping = true; };
22
+ process.on('SIGTERM', stop);
23
+ process.on('SIGINT', stop);
24
+ const spawnChild = deps.spawnChild ?? ((member, generation) => {
25
+ if (!existsSync(member.dir))
26
+ throw Error('Fleet member state missing');
27
+ const fd = openSync(join(member.dir, 'supervisor.log'), 'a', 0o600);
28
+ try {
29
+ return spawn(process.execPath, [entrypoint, '_run-managed', member.key, generation], {
30
+ // A process group permits bounded cleanup of a hung runner and its harness.
31
+ // IPC couples its lifetime to this parent even if the parent is SIGKILLed.
32
+ detached: true, stdio: ['ignore', fd, fd, 'ipc'], env: managedMemberEnvironment(member),
33
+ });
34
+ }
35
+ finally {
36
+ closeSync(fd);
37
+ }
38
+ });
39
+ let notifications;
40
+ async function reconcile(key) {
41
+ await withMemberLock(key, async () => {
42
+ const member = readMember(key);
43
+ const owned = children.get(key);
44
+ if (!member) {
45
+ if (owned && !owned.stoppingAt) {
46
+ owned.stoppingAt = Date.now();
47
+ owned.child.kill('SIGTERM');
48
+ }
49
+ return;
50
+ }
51
+ // Our ChildProcess supplies exit notification for steady owned members.
52
+ // Probe argv only for predecessor discovery or before an actual stop;
53
+ // macOS otherwise spawned one ps process per member every 250ms.
54
+ if (owned && owned.generation === member.generation && member.desired === 'running' && !stopping && !owned.stoppingAt)
55
+ return;
56
+ const live = await memberProcess(member, exec);
57
+ if (member.desired === 'stopped' || stopping) {
58
+ if (live.state === 'running' && live.pid) {
59
+ if (owned && owned.generation === member.generation) {
60
+ if (!owned.stoppingAt) {
61
+ owned.stoppingAt = Date.now();
62
+ owned.child.kill('SIGTERM');
63
+ }
64
+ else if (Date.now() - owned.stoppingAt >= 15_000) {
65
+ // Re-check exact generation immediately before a group signal.
66
+ try {
67
+ process.kill(-live.pid, 'SIGKILL');
68
+ }
69
+ catch { /* next probe proves absence */ }
70
+ }
71
+ }
72
+ else {
73
+ // A predecessor's child has lost IPC and is shutting down. Never adopt
74
+ // or launch alongside it; its worker also holds the member lifetime lease.
75
+ log(`[fleet] waiting for predecessor ${key}`);
76
+ }
77
+ }
78
+ return;
79
+ }
80
+ if (live.state !== 'stopped' || owned || (member.retryAt ?? 0) > Date.now() || readRestartLedger(member.dir).circuit === 'open')
81
+ return;
82
+ const generation = randomUUID();
83
+ writeMember({ ...member, generation, pid: undefined });
84
+ let child;
85
+ try {
86
+ child = spawnChild(member, generation);
87
+ }
88
+ catch {
89
+ if (!existsSync(member.dir)) {
90
+ writeMember({ ...member, generation: undefined, pid: undefined, retryAt: Date.now() + 30_000 });
91
+ return;
92
+ }
93
+ const ledger = readRestartLedger(member.dir);
94
+ const failures = ledger.consecutiveImmediateFailures + 1;
95
+ writeRestartLedger(member.dir, { ...ledger, consecutiveImmediateFailures: failures, circuit: failures >= RESTART_FAIL_THRESHOLD ? 'open' : 'closed', nextDelayMs: backoffFor(failures), lastReason: 'Fleet child spawn failed', updatedAt: new Date().toISOString() });
96
+ writeMember({ ...member, generation: undefined, pid: undefined, retryAt: Date.now() + backoffFor(failures) });
97
+ return;
98
+ }
99
+ const childState = { child, generation, startedAt: now() };
100
+ children.set(key, childState);
101
+ writeMember({ ...member, generation, pid: child.pid });
102
+ let finished = false;
103
+ const finish = (failed) => {
104
+ if (finished)
105
+ return;
106
+ finished = true;
107
+ if (children.get(key) === childState)
108
+ children.delete(key);
109
+ void withMemberLock(key, async () => {
110
+ const current = readMember(key);
111
+ if (!current || current.generation !== generation)
112
+ return;
113
+ if (current.kind === 'temporary' && !existsSync(current.dir)) {
114
+ rmSync(memberPath(key));
115
+ return;
116
+ }
117
+ let taskCompleted = false;
118
+ if (!failed && !stopping && !childState.stoppingAt && current.kind === 'task') {
119
+ try {
120
+ taskCompleted = !(await import('../task-supervision.js')).taskSupervisorMayRun(current.name);
121
+ }
122
+ catch {
123
+ failed = true;
124
+ } // uncertainty consumes the existing bounded failure budget
125
+ }
126
+ const ledger = readRestartLedger(current.dir);
127
+ let previousFailures = ledger.consecutiveImmediateFailures;
128
+ if (failed && !stopping && !childState.stoppingAt) {
129
+ const { stableSupervisorWindow } = await import('../runner.js');
130
+ if (now() - childState.startedAt >= stableSupervisorWindow(current.name, current.kind !== 'permanent', current.configPath) * 1000)
131
+ previousFailures = 0;
132
+ }
133
+ const failures = failed && !stopping && !childState.stoppingAt ? previousFailures + 1 : ledger.consecutiveImmediateFailures;
134
+ if (failed && !stopping && !childState.stoppingAt && existsSync(current.dir))
135
+ writeRestartLedger(current.dir, {
136
+ ...ledger, consecutiveImmediateFailures: failures, lastReason: 'Fleet member runner exited unexpectedly',
137
+ circuit: failures >= RESTART_FAIL_THRESHOLD ? 'open' : 'closed', nextDelayMs: backoffFor(failures),
138
+ updatedAt: new Date().toISOString(), ...(failures >= RESTART_FAIL_THRESHOLD ? { openedAt: new Date().toISOString() } : {}),
139
+ });
140
+ writeMember({ ...current, pid: undefined, generation: undefined,
141
+ retryAt: Date.now() + backoffFor(failures),
142
+ // A completed task/transient worker must not become an endless launch loop.
143
+ desired: !failed && !stopping && !childState.stoppingAt && (current.kind === 'temporary' || taskCompleted) ? 'stopped' : current.desired });
144
+ }).catch(() => log(`[fleet] child completion record unavailable: ${key}`));
145
+ };
146
+ child.once('error', () => finish(true));
147
+ child.once('exit', (code, signal) => {
148
+ // The tracked child created this private process group; reclaim any harness
149
+ // descendants even when the runner itself crashed before cleaning them.
150
+ if (child.pid) {
151
+ try {
152
+ process.kill(-child.pid, 'SIGKILL');
153
+ }
154
+ catch { /* already absent */ }
155
+ }
156
+ finish(code !== 0 || signal !== null);
157
+ });
158
+ });
159
+ }
160
+ try {
161
+ try {
162
+ const config = producerConfig(process.env, undefined, log);
163
+ if (config)
164
+ notifications = new TaskNotificationProducer(config, log);
165
+ }
166
+ catch {
167
+ log('[fleet] task notifications unavailable; committed events retained');
168
+ }
169
+ try {
170
+ const { resumeFleetTransfers } = await import('./adoption.js');
171
+ await resumeFleetTransfers(entrypoint, exec);
172
+ }
173
+ catch (error) {
174
+ // Existing catalog members continue; uncertain transfers remain held with
175
+ // their receipts. Operator init/up reports the same actionable failure.
176
+ log(`[fleet] transfer pending: ${error instanceof Error ? error.message : 'proof unavailable'}`);
177
+ }
178
+ while (!stopping && !deps.shouldStop?.()) {
179
+ for (const key of listMemberKeys()) {
180
+ try {
181
+ await reconcile(key);
182
+ }
183
+ catch {
184
+ log(`[fleet] reconciliation held: ${key}`);
185
+ }
186
+ }
187
+ await sleep(250);
188
+ }
189
+ }
190
+ finally {
191
+ stopping = true;
192
+ await notifications?.close();
193
+ // Shutdown preserves desired intent; the successor resumes the same catalog.
194
+ const deadline = Date.now() + 17_000;
195
+ while (children.size && Date.now() < deadline) {
196
+ for (const key of children.keys())
197
+ await reconcile(key);
198
+ await sleep(100);
199
+ }
200
+ process.off('SIGTERM', stop);
201
+ process.off('SIGINT', stop);
202
+ lease.release();
203
+ }
204
+ }
205
+ /** Workers cannot outlive their parent or overlap another generation of this member. */
206
+ export async function runManagedMember(key, generation) {
207
+ let stopping = false;
208
+ const stop = () => { stopping = true; };
209
+ const disconnected = () => {
210
+ stopping = true;
211
+ process.kill(process.pid, 'SIGTERM');
212
+ setTimeout(() => process.kill(-process.pid, 'SIGKILL'), 15_000).unref();
213
+ };
214
+ process.on('SIGTERM', stop);
215
+ process.on('SIGINT', stop);
216
+ process.on('disconnect', disconnected);
217
+ if (!process.connected)
218
+ throw Error('FLEET_MEMBER_PARENT_MISSING');
219
+ const lease = await acquireOwnerBinderLease(join(stateRoot(), 'supervisor', 'workers', key), 'fleet-member', key);
220
+ try {
221
+ const member = readMember(key);
222
+ if (!member || member.generation !== generation || member.desired !== 'running' || stopping)
223
+ return;
224
+ const { runSupervised } = await import('../runner.js');
225
+ if (member.kind === 'permanent') {
226
+ const { waitForRoleDaemon } = await import('../startup-readiness.js');
227
+ // Waiting for a daemon is readiness, not a failed harness attempt.
228
+ while (!stopping) {
229
+ try {
230
+ await waitForRoleDaemon(member.name, member.configPath);
231
+ break;
232
+ }
233
+ catch {
234
+ if (!stopping)
235
+ await new Promise(resolve => setTimeout(resolve, 5000));
236
+ }
237
+ }
238
+ if (!stopping)
239
+ await runSupervised(member.name, { configPath: member.configPath }, { shouldStop: () => stopping });
240
+ }
241
+ else if (member.kind === 'watchdogs') {
242
+ const { runScheduler } = await import('../watchdog/scheduler.js');
243
+ await runScheduler(member.configPath, {
244
+ now: () => new Date(), sleep: ms => new Promise(resolve => setTimeout(resolve, ms)),
245
+ log: message => console.log(message), binPath: process.argv[1], shouldStop: () => stopping,
246
+ });
247
+ }
248
+ else {
249
+ const { runTempSupervisor } = await import('../temp-supervisor-recovery.js');
250
+ const { runTemp } = await import('../runner.js');
251
+ await runTempSupervisor(member.name, process.argv[1], () => runTemp(member.name, {
252
+ shouldStop: () => stopping, suspendOnStop: true,
253
+ }), true);
254
+ }
255
+ }
256
+ finally {
257
+ lease.release();
258
+ process.off('SIGTERM', stop);
259
+ process.off('SIGINT', stop);
260
+ process.off('disconnect', disconnected);
261
+ if (process.connected)
262
+ process.disconnect();
263
+ else if (stopping) {
264
+ try {
265
+ process.kill(-process.pid, 'SIGKILL');
266
+ }
267
+ catch { /* no private group */ }
268
+ }
269
+ }
270
+ }
@@ -0,0 +1,4 @@
1
+ import { type Exec } from '../exec.js';
2
+ /** OS user services have one namespace. Isolated homes must not hijack it.
3
+ * Injected executors exercise isolated definitions without touching a user manager. */
4
+ export declare function assertNativeFleetScope(exec: Exec): void;
@@ -0,0 +1,10 @@
1
+ import { homedir } from 'node:os';
2
+ import { resolve } from 'node:path';
3
+ import { home } from '../paths.js';
4
+ import { realExec } from '../exec.js';
5
+ /** OS user services have one namespace. Isolated homes must not hijack it.
6
+ * Injected executors exercise isolated definitions without touching a user manager. */
7
+ export function assertNativeFleetScope(exec) {
8
+ if (exec === realExec && resolve(home()) !== resolve(homedir()))
9
+ throw Error('FLEET_SERVICE_HOME_CONFLICT: managed OS supervision requires the OS user home; use OURS_FLEET_SUPERVISOR=none for isolated fleets');
10
+ }
@@ -1,6 +1,6 @@
1
1
  import { type Exec } from '../exec.js';
2
2
  import type { LivenessState, SupervisorBackend } from './types.js';
3
- export declare const UNIT_TEMPLATE = "ours-fleet-agent@.service";
3
+ export declare const UNIT_TEMPLATE = "ours-fleet.service";
4
4
  /**
5
5
  * Actionable hint when systemctl cannot reach the user bus. After the cli.ts
6
6
  * XDG_RUNTIME_DIR fallback this branch stays reachable only when
@@ -8,7 +8,7 @@ export declare const UNIT_TEMPLATE = "ours-fleet-agent@.service";
8
8
  * active — so pointing at linger is the correct first hint. (#9)
9
9
  */
10
10
  export declare const busHint: (stderr: string) => string;
11
- export declare const unitFor: (name: string) => string;
11
+ export declare const unitFor: (_name: string) => string;
12
12
  /**
13
13
  * systemd's own ActiveState vocabulary, classified. `activating` covers
14
14
  * `auto-restart` — the unit is mid-restart, not stopped, so its context stands.
@@ -1,9 +1,11 @@
1
+ import { assertNativeFleetScope } from './scope.js';
2
+ import { clientConfigPath } from '../client-profile.js';
1
3
  import { mkdirSync, writeFileSync } from 'node:fs';
2
4
  import { delimiter, dirname, join } from 'node:path';
3
5
  import { userInfo } from 'node:os';
4
6
  import { home } from '../paths.js';
5
7
  import { realExec } from '../exec.js';
6
- export const UNIT_TEMPLATE = 'ours-fleet-agent@.service';
8
+ export const UNIT_TEMPLATE = 'ours-fleet.service';
7
9
  /**
8
10
  * Actionable hint when systemctl cannot reach the user bus. After the cli.ts
9
11
  * XDG_RUNTIME_DIR fallback this branch stays reachable only when
@@ -14,7 +16,7 @@ export const busHint = (stderr) => /user scope bus|XDG_RUNTIME_DIR/.test(stderr)
14
16
  ? `\nhint: no user runtime dir — enable linger: sudo loginctl enable-linger ${userInfo().username}` +
15
17
  `\n (if linger is already on: export XDG_RUNTIME_DIR=/run/user/$(id -u))`
16
18
  : '';
17
- export const unitFor = (name) => `ours-fleet-agent@${name}.service`;
19
+ export const unitFor = (_name) => UNIT_TEMPLATE;
18
20
  /** Quote a systemd unit argument and escape its specifier marker. */
19
21
  const unitArg = (value) => `"${value.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/%/g, '%%')}"`;
20
22
  /**
@@ -56,10 +58,11 @@ async function probeLiveness(ctl, name) {
56
58
  };
57
59
  }
58
60
  export function makeSystemdBackend(exec = realExec) {
59
- const ctl = (...args) => exec('systemctl', ['--user', ...args]);
61
+ const ctl = (...args) => { assertNativeFleetScope(exec); return exec('systemctl', ['--user', ...args]); };
60
62
  return {
61
63
  id: 'systemd',
62
64
  async init(binPath) {
65
+ assertNativeFleetScope(exec);
63
66
  const msgs = [];
64
67
  const unitDir = join(home(), '.config', 'systemd', 'user');
65
68
  // Lingering user units often start before a login shell imports its PATH.
@@ -69,49 +72,32 @@ export function makeSystemdBackend(exec = realExec) {
69
72
  dirname(process.execPath),
70
73
  ...(process.env.PATH ?? '').split(delimiter),
71
74
  ].filter(Boolean))].join(delimiter);
72
- // Same reason as PATH, for the other thing a lingering unit cannot inherit:
73
- // WHICH ours daemon this fleet was set up against.
74
- //
75
- // ours-fleet resolves its daemon from OURS_CONFIG / OURS_PORT / OURS_STATE_DIR
76
- // and otherwise falls back to ~/.ours and port 3050 (src/monitor.ts). A host
77
- // set up against a non-default daemon — the installer's multi-daemon profiles
78
- // do exactly this — passes that selection to `ours-fleet init` in the
79
- // environment, and it died there: nothing persisted it, so every runner
80
- // systemd started at boot resolved the default daemon again, and on a host
81
- // where only the non-default daemon exists that is a daemon that is not there.
82
- //
83
- // ONLY OURS_CONFIG is baked, deliberately. It names which daemon, and leaves
84
- // that daemon's own config file authoritative for port and state directory, so
85
- // a later edit to it still wins. Baking OURS_PORT/OURS_STATE_DIR would freeze
86
- // those into a unit file that outranks the config for ever after — the failure
87
- // mode @ours.network/cli avoids for the same reason (packages/cli/src/
88
- // service.ts: "The port is deliberately NOT baked").
89
- //
90
- // Absent from init's environment ⇒ no line, and the unit is byte-for-byte what
91
- // it has always been. This teaches fleet nothing about installer profiles; it
92
- // persists the selection fleet was initialised with.
75
+ // Persist one profile path, never a URL or service port. Profile updates
76
+ // are picked up by every fresh process, including lingering user services.
93
77
  const unitEnv = ['PATH=' + servicePath];
94
- if (process.env.OURS_CONFIG)
95
- unitEnv.push('OURS_CONFIG=' + process.env.OURS_CONFIG);
78
+ unitEnv.push('OURS_CONFIG=' + clientConfigPath(process.env));
79
+ for (const key of ['OURS_FLEET_HOME', 'OURS_FLEET_SOCKET_ROOT', 'CODEX_HOME', 'CODEX_PATH']) {
80
+ if (process.env[key] !== undefined)
81
+ unitEnv.push(key + '=' + process.env[key]);
82
+ }
96
83
  const environmentLines = unitEnv
97
84
  .map(value => `Environment="${value.replace(/\\/g, '\\\\').replace(/"/g, '\\"').replace(/%/g, '%%')}"`)
98
85
  .join('\n');
99
86
  mkdirSync(unitDir, { recursive: true });
100
87
  writeFileSync(join(unitDir, UNIT_TEMPLATE), `[Unit]
101
- Description=ours-fleet agent %i
88
+ Description=ours-fleet process manager
102
89
  After=default.target
90
+ StartLimitIntervalSec=0
103
91
 
104
92
  [Service]
105
93
  Type=simple
106
94
  ${environmentLines}
107
- ExecStart=${unitArg(process.execPath)} ${unitArg(binPath)} _run %i
108
- # The RUNNER owns the child-session restart loop, with a counted, backed-off
109
- # circuit breaker. systemd must only recover the runner PROCESS crashing —
110
- # Restart=always here would resume the uncounted two-second relaunch loop, and
111
- # would also restart a runner that is deliberately holding a failing agent down.
95
+ ExecStart=${unitArg(process.execPath)} ${unitArg(binPath)} _run-fleet
96
+ # Fleet owns agent startup and restart; the OS recovers only the Fleet parent.
97
+ KillMode=control-group
112
98
  Restart=on-failure
113
99
  RestartSec=5
114
- TimeoutStopSec=15
100
+ TimeoutStopSec=25
115
101
 
116
102
  [Install]
117
103
  WantedBy=default.target
@@ -0,0 +1,9 @@
1
+ /** Read-only ownership fence: absence/mismatch never permits a new identity or launch. */
2
+ export declare function taskSupervisorMayRun(name: string): boolean;
3
+ /** Resolve the externally recorded CID/action for explicit terminal retirement.
4
+ * Run only after the same launch passed taskSupervisorMayRun's ownership fence.
5
+ * An unpublished failed launch stays for its seat's retry/retirement saga. */
6
+ export declare function taskMemberRetirementProof(name: string): {
7
+ cid: string;
8
+ action: string;
9
+ };
@@ -0,0 +1,145 @@
1
+ import { readFileSync, lstatSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { parse } from 'yaml';
4
+ import { agentDir, stateRoot } from './paths.js';
5
+ import { readTempSupervisor } from './temp-lifecycle.js';
6
+ import { createHash } from 'node:crypto';
7
+ import { readProvenance } from './creation.js';
8
+ import { getTask } from './rooms-tasks/task-state.js';
9
+ import { getRoomRecord } from './rooms-tasks/room-state.js';
10
+ import { assertWorkspacePresent } from './rooms-tasks/workspace.js';
11
+ import { privateRuntimeRoot } from './agent-ours/service.js';
12
+ import { binderKey, RuntimeJournal } from './agent-ours/state.js';
13
+ import { assertMemberNotPermanent } from './rooms-tasks/member-ownership.js';
14
+ import { readClientProfile } from './client-profile.js';
15
+ import { firstManagedSessionMayStart } from './managed-recovery.js';
16
+ function readProof(path) {
17
+ const stat = lstatSync(path);
18
+ if (!stat.isFile() || stat.isSymbolicLink() || stat.size > 256 * 1024)
19
+ throw Error('TASK_SUPERVISOR_UNSAFE_PROOF');
20
+ return readFileSync(path, 'utf8');
21
+ }
22
+ /** An admitted task member must resume its retained instance, never allocate a successor. */
23
+ function assertRetainedRuntime(role, action, cid, launch, session) {
24
+ const profile = readClientProfile({ ...process.env, ...role.env });
25
+ if (!profile)
26
+ throw Error('TASK_RUNTIME_PROFILE_MISSING');
27
+ const dir = join(privateRuntimeRoot(), binderKey(profile.expectedInstanceId, role.identity));
28
+ const instance = JSON.parse(readProof(join(dir, 'instance.json')));
29
+ const owner = JSON.parse(readProof(join(dir, 'owner.json')));
30
+ const pin = JSON.parse(readProof(join(dir, 'identity-pin.json')));
31
+ readProof(join(dir, 'state.json'));
32
+ const state = new RuntimeJournal(dir).read();
33
+ const creation = JSON.parse(readProof(join(privateRuntimeRoot(), 'launches', binderKey('temporary', role.name) + '.json')));
34
+ const sessionDir = agentDir(role.name, true);
35
+ let retainedSession = '';
36
+ try {
37
+ retainedSession = readProof(join(sessionDir, role.session === 'codex-app-server' ? '.session-id' : '.acp-session-id')).trim();
38
+ }
39
+ catch (error) {
40
+ if (error.code !== 'ENOENT' || session || !firstManagedSessionMayStart(sessionDir))
41
+ throw error;
42
+ }
43
+ readProof(join(sessionDir, '.booted')); // presence is the runner's resume marker, including legacy empty files
44
+ if (creation.role !== role.name || creation.identity !== role.identity || creation.action !== action
45
+ || !state || instance.role !== role.name || instance.temporary !== true || !instance.instance
46
+ || owner.instance !== instance.instance || typeof owner.token !== 'string' || !owner.token
47
+ || pin.daemon !== profile.expectedInstanceId || pin.name !== role.identity || pin.cid !== cid
48
+ || state.daemon !== profile.expectedInstanceId || state.name !== role.identity || state.cid !== cid
49
+ || state.instance !== instance.instance || state.lifetime !== 'temporary' || state.action !== action
50
+ || ['QUIESCING', 'TERMINAL_INTENT', 'RELEASED', 'CLEANUP_PENDING', 'FAILED'].includes(state.phase)
51
+ || (!retainedSession && !firstManagedSessionMayStart(sessionDir)) || (launch && launch !== instance.instance) || (session && session !== retainedSession))
52
+ throw Error('TASK_RETAINED_RUNTIME_MISMATCH');
53
+ }
54
+ /** Read-only ownership fence: absence/mismatch never permits a new identity or launch. */
55
+ export function taskSupervisorMayRun(name) {
56
+ assertMemberNotPermanent(name);
57
+ const dir = agentDir(name, true);
58
+ readProof(join(dir, '.temp-supervisor.json'));
59
+ readProof(join(dir, 'creation.json'));
60
+ const supervisor = readTempSupervisor(dir), owner = supervisor?.taskOwner;
61
+ if (!owner || supervisor.role !== name || !owner.taskId || !owner.creationActionId)
62
+ throw Error('TASK_SUPERVISOR_OWNER_MISSING');
63
+ const role = parse(readProof(join(dir, 'role.yaml')));
64
+ const startup = role.roomMemberStartup, provenance = readProvenance(dir);
65
+ const task = getTask(owner.taskId);
66
+ if (role.name !== name || role.identity !== name || provenance?.role !== name
67
+ || provenance.creationActionId !== owner.creationActionId)
68
+ throw Error('TASK_SUPERVISOR_OWNERSHIP_MISMATCH');
69
+ if (!task.deletion && !task.terminal_intent && ['provisioning', 'active', 'review'].includes(task.state) && task.workspace) {
70
+ if (role.cwd !== task.workspace.path)
71
+ throw Error('TASK_WORKSPACE_MISMATCH');
72
+ assertWorkspacePresent(task.workspace, 'task', task.task_id);
73
+ }
74
+ if (owner.layout) {
75
+ const { runId, participant } = owner.layout;
76
+ const expectedName = `layout-${createHash('sha256').update(runId + ':' + participant).digest('hex').slice(0, 16)}`;
77
+ if (!task.layout || task.layout.run_id !== runId || runId !== `task-${owner.taskId}`
78
+ || !/^[A-Za-z0-9_-]+$/.test(runId) || expectedName !== name
79
+ || owner.creationActionId !== `${runId}:${participant}`)
80
+ throw Error('TASK_LAYOUT_OWNER_MISMATCH');
81
+ const state = JSON.parse(readProof(join(stateRoot(), 'layouts', `${runId}.json`)));
82
+ const member = state.participants[participant];
83
+ if (!member?.owned || member.instance?.remote || (member.instance?.agent && member.instance.agent !== name)
84
+ || (member.instance && member.instance.temporary !== true))
85
+ throw Error('TASK_LAYOUT_PARTICIPANT_MISMATCH');
86
+ if (task.deletion || task.terminal_intent || state.closed || state.closing || member.retired
87
+ || !['provisioning', 'active', 'review'].includes(task.state))
88
+ return false;
89
+ if (member.instance)
90
+ assertRetainedRuntime(role, owner.creationActionId, member.instance.cid, member.instance.launch, member.instance.session);
91
+ return true;
92
+ }
93
+ if (!owner.roomId || !owner.roomIdentityCid)
94
+ throw Error('TASK_SUPERVISOR_OWNER_MISSING');
95
+ const room = getRoomRecord(owner.roomId);
96
+ const seat = room?.member_seats.find(member => member.role_name === name);
97
+ if (!task || !room || room.task_id !== task.task_id || room.room_identity_cid !== owner.roomIdentityCid
98
+ || (task.room_id !== undefined && task.room_id !== owner.roomId)
99
+ || (task.room_identity_cid !== undefined && task.room_identity_cid !== owner.roomIdentityCid)
100
+ || role.name !== name || role.identity !== name || startup?.task_id !== owner.taskId
101
+ || startup.room_id !== owner.roomId || startup.room_identity_cid !== owner.roomIdentityCid
102
+ || startup.identity_name !== name || !seat || seat.invite_id !== startup.invite_id
103
+ || seat.cowork_role !== startup.role || provenance?.role !== name
104
+ || provenance.creationActionId !== owner.creationActionId
105
+ || seat.launch?.action_id !== owner.creationActionId
106
+ || (seat.launch.launch_id !== undefined && seat.launch.launch_id !== supervisor.launchId))
107
+ throw Error('TASK_SUPERVISOR_OWNERSHIP_MISMATCH');
108
+ if (task.deletion || task.terminal_intent || room.close || seat.retirement
109
+ || !['provisioning', 'active', 'review'].includes(task.state)
110
+ || !['provisioning', 'active'].includes(room.state))
111
+ return false;
112
+ if (task.workspace && (role.cwd !== task.workspace.path
113
+ || JSON.stringify(startup.workspace) !== JSON.stringify(task.workspace)
114
+ || JSON.stringify(room.workspace) !== JSON.stringify(task.workspace)))
115
+ throw Error('TASK_WORKSPACE_MISMATCH');
116
+ if (seat.identity_cid)
117
+ assertRetainedRuntime(role, owner.creationActionId, seat.identity_cid);
118
+ return true;
119
+ }
120
+ /** Resolve the externally recorded CID/action for explicit terminal retirement.
121
+ * Run only after the same launch passed taskSupervisorMayRun's ownership fence.
122
+ * An unpublished failed launch stays for its seat's retry/retirement saga. */
123
+ export function taskMemberRetirementProof(name) {
124
+ const dir = agentDir(name, true), metadata = readTempSupervisor(dir), owner = metadata?.taskOwner;
125
+ if (!owner || metadata.role !== name)
126
+ throw Error('TASK_SUPERVISOR_OWNER_MISSING');
127
+ let cid;
128
+ if (owner.layout) {
129
+ const state = JSON.parse(readProof(join(stateRoot(), 'layouts', `${owner.layout.runId}.json`)));
130
+ const participant = state.participants[owner.layout.participant];
131
+ if (!participant?.owned || participant.instance?.agent !== name || participant.instance?.remote
132
+ || participant.instance?.temporary !== true)
133
+ throw Error('TASK_LAYOUT_PARTICIPANT_MISMATCH');
134
+ cid = participant.instance.cid;
135
+ }
136
+ else {
137
+ const seat = owner.roomId && getRoomRecord(owner.roomId)?.member_seats.find(member => member.role_name === name);
138
+ if (!seat || seat.launch?.action_id !== owner.creationActionId || seat.launch.launch_id !== metadata.launchId)
139
+ throw Error('TASK_SUPERVISOR_OWNERSHIP_MISMATCH');
140
+ cid = seat.identity_cid;
141
+ }
142
+ if (!cid)
143
+ throw Error('TASK_MEMBER_RETIREMENT_CID_MISSING');
144
+ return { cid, action: owner.creationActionId };
145
+ }
@@ -0,0 +1,17 @@
1
+ import type { SessionSnapshot } from './session/types.js';
2
+ export declare const TEMP_CHAT_IDLE_MS: number;
3
+ export type ChatIdlePolicy = {
4
+ version: 1;
5
+ timeoutMs: number;
6
+ lastActivityAt: number;
7
+ };
8
+ export declare function readChatIdle(dir: string): ChatIdlePolicy | undefined;
9
+ /** Only a standalone temporary supervisor owns this clock; browser reads never touch it. */
10
+ export declare class TemporaryChatIdle {
11
+ private dir;
12
+ private value;
13
+ private busy;
14
+ constructor(dir: string, now: number);
15
+ private save;
16
+ observe(snapshot: SessionSnapshot, queueDepth: number, now: number): boolean;
17
+ }
@@ -0,0 +1,42 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { replaceFileAtomically } from './atomic-file.js';
4
+ export const TEMP_CHAT_IDLE_MS = 24 * 60 * 60_000;
5
+ const FILE = 'chat-idle.json';
6
+ export function readChatIdle(dir) {
7
+ if (!existsSync(join(dir, FILE)))
8
+ return undefined;
9
+ const value = JSON.parse(readFileSync(join(dir, FILE), 'utf8'));
10
+ if (value.version !== 1 || !Number.isSafeInteger(value.lastActivityAt) || value.timeoutMs !== TEMP_CHAT_IDLE_MS)
11
+ throw new Error('Invalid persisted chat idle policy');
12
+ return value;
13
+ }
14
+ /** Only a standalone temporary supervisor owns this clock; browser reads never touch it. */
15
+ export class TemporaryChatIdle {
16
+ dir;
17
+ value;
18
+ busy = false;
19
+ constructor(dir, now) {
20
+ this.dir = dir;
21
+ this.value = readChatIdle(dir) ?? { version: 1, timeoutMs: TEMP_CHAT_IDLE_MS, lastActivityAt: now };
22
+ this.save();
23
+ }
24
+ save() { replaceFileAtomically(join(this.dir, FILE), JSON.stringify(this.value) + '\n', 0o600); }
25
+ observe(snapshot, queueDepth, now) {
26
+ // Unknown activity evidence never grants permission to retire a session.
27
+ const idle = snapshot.alive && snapshot.readiness === 'idle' && !snapshot.pendingPermissionId && queueDepth === 0 && snapshot.activity?.activeToolCalls === 0;
28
+ const update = Date.parse(snapshot.activity?.lastUpdateAt ?? '');
29
+ let next = this.value.lastActivityAt;
30
+ if (Number.isFinite(update) && update <= now)
31
+ next = Math.max(next, update);
32
+ if (!idle || this.busy)
33
+ next = Math.max(next, now);
34
+ this.busy = !idle;
35
+ // Active work checkpoints at most once a minute, with an immediate idle boundary write.
36
+ if (next > this.value.lastActivityAt && (idle || next - this.value.lastActivityAt >= 60_000)) {
37
+ this.value.lastActivityAt = next;
38
+ this.save();
39
+ }
40
+ return idle && now >= this.value.lastActivityAt && now - this.value.lastActivityAt >= this.value.timeoutMs;
41
+ }
42
+ }