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

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
package/dist/runner.js CHANGED
@@ -1,17 +1,26 @@
1
+ import { readProvenance } from './creation.js';
2
+ import { firstManagedSessionMayStart, managedRecoveryPath, readManagedRecovery, writeManagedRecovery } from './managed-recovery.js';
3
+ import { StartupTelemetry } from './startup-telemetry.js';
4
+ import { managedStartupPrompt, managedTaskPrompt } from './startup-prompt.js';
5
+ import { createLayoutControl } from './rooms-tasks/layout-control.js';
6
+ import { TemporaryChatIdle } from './temp-idle.js';
7
+ import { taskSupervisorMayRun, taskMemberRetirementProof } from './task-supervision.js';
1
8
  import { prepareManagedAgent, releaseManagedAgent } from './agent-ours/service.js';
2
9
  import { prepareManagedHarness } from './agent-ours/harness.js';
10
+ import { prepareManagedCliLaunch } from './managed-cli.js';
3
11
  import { existsSync, readFileSync, writeFileSync, rmSync, mkdirSync, realpathSync } from 'node:fs';
4
12
  import { join } from 'node:path';
5
13
  import { fileURLToPath } from 'node:url';
6
14
  import { randomUUID } from 'node:crypto';
7
15
  import { parse } from 'yaml';
8
16
  import { agentDir, stateRoot } from './paths.js';
9
- import { loadConfig, findRole, isolationContextFor, resolveMonitorConfig, resolvePermissions, } from './config.js';
17
+ import { loadConfig, findRole, isolationContextFor, resolveSnapshotMonitor, resolvePermissions, } from './config.js';
10
18
  import { getAdapter } from './harness/registry.js';
11
19
  import { createMonitor, probeIdentityPresence, } from './monitor.js';
12
20
  import { DaemonGenerationObserver, RoleRecoveryController, probeDaemonGeneration, } from './daemon-recovery.js';
13
21
  import { realExec } from './exec.js';
14
22
  import { resolveIsolation } from './isolation/policy.js';
23
+ import { pinSubscriptionForLaunch } from './subscriptions/launch.js';
15
24
  import { selectIsolationBackend } from './isolation/registry.js';
16
25
  import { resourceArgs, cpuControllerDelegated } from './isolation/resources.js';
17
26
  import { resolveLaunchRuntime } from './isolation/runtime.js';
@@ -27,7 +36,7 @@ import { ScheduledLoopManager, } from './loops/manager.js';
27
36
  import { FLEET_PROXY_CALLER_ENV, FLEET_PROXY_STATE_DIR_ENV, } from './fleet-proxy.js';
28
37
  import { effectivePermissionMode } from './permissions.js';
29
38
  import { assertModelPinReachesChild, effectiveRoleModel, repinModelEnv } from './model-env.js';
30
- import { archiveTempState, markTempSupervisorActive, requestedTempStopReason, } from './temp-lifecycle.js';
39
+ import { archiveTempState, markTempSupervisorActive, requestedTempStopReason, readTempSupervisor, } from './temp-lifecycle.js';
31
40
  import { FleetCommandAuditStore } from './fleet-command-audit.js';
32
41
  export const SUPERVISOR_RECYCLE_REQUIRED = 'OWNER_CHANNEL_SUPERVISOR_RECYCLE_REQUIRED';
33
42
  export class SupervisorRecycleRequiredError extends Error {
@@ -75,12 +84,16 @@ const defaultDeps = () => ({
75
84
  });
76
85
  const MONITOR_OWNER_FILE = '.monitor-owner';
77
86
  const OBSOLETE_OURS_AUTOSTART_ENV = 'OURS_AUTOSTART';
78
- function localFleetAuditor(stateDir, caller, log) {
87
+ export function localFleetAuditor(stateDir, caller, log, session) {
88
+ const bindings = new Map();
79
89
  const store = new FleetCommandAuditStore(join(stateDir, '.fleet-command-audit.json'));
80
90
  return {
81
91
  async begin(requestId, argv) {
82
92
  let attempt = store.begin(requestId, caller, argv);
83
93
  if (attempt.invocation === 'sending') {
94
+ const binding = session.taskNoticeBinding?.();
95
+ if (binding)
96
+ bindings.set(attempt.correlationId, binding);
84
97
  log(`[${caller}] fleet proxy command ${attempt.correlationId} `
85
98
  + `route=${attempt.classification.route} decision=${attempt.classification.decision}`);
86
99
  attempt = store.invocation(attempt.correlationId, caller, 'delivered');
@@ -94,6 +107,19 @@ function localFleetAuditor(stateDir, caller, log) {
94
107
  ...(input.resourceIds ? { resourceIds: input.resourceIds } : {}),
95
108
  ...(input.presentations ? { presentations: input.presentations } : {}),
96
109
  });
110
+ const binding = bindings.get(attempt.correlationId);
111
+ if (binding && attempt.outcome?.class === 'success' && attempt.outcome.effect === 'completed') {
112
+ for (const p of attempt.outcome.presentations ?? [])
113
+ if (p.kind === 'task' && p.operation === 'create') {
114
+ try {
115
+ session.recordTaskCreated?.(binding, { operationId: p.eventId, taskId: p.id, title: p.title ?? p.id, state: p.newState });
116
+ }
117
+ catch {
118
+ log(`[${caller}] task-created conversation notice could not be persisted`);
119
+ }
120
+ }
121
+ bindings.delete(attempt.correlationId);
122
+ }
97
123
  if (attempt.outcome?.delivery === 'sending')
98
124
  attempt = store.outcome(attempt.correlationId, caller, 'delivered');
99
125
  return attempt;
@@ -414,7 +440,7 @@ export function loadTempRole(name) {
414
440
  // Upgrade snapshots written before monitor.mode/interrupt existed. A snapshot
415
441
  // with no monitor block keeps the historical native/no-supervisor behavior.
416
442
  if (role.monitor)
417
- role.monitor = resolveMonitorConfig(undefined, role.monitor);
443
+ role.monitor = resolveSnapshotMonitor(role.monitor);
418
444
  role.__temp = true;
419
445
  return role;
420
446
  }
@@ -427,7 +453,7 @@ export function loadTempRole(name) {
427
453
  * with "no such role". An empty/missing marker means "use the default", same as
428
454
  * no -c was ever given.
429
455
  */
430
- function resolveConfigPath(dir, explicit) {
456
+ export function resolveConfigPath(dir, explicit) {
431
457
  if (explicit)
432
458
  return explicit;
433
459
  const marker = join(dir, '.config-path');
@@ -448,6 +474,8 @@ export function isRecoverableTempStartupCancellation(temp, result) {
448
474
  /** One session lifecycle. `runSupervised` (or a one-shot caller) drives it. */
449
475
  export async function runOnce(name, opts = {}, partialDeps = {}) {
450
476
  const deps = { ...defaultDeps(), ...partialDeps };
477
+ const startupTelemetry = new StartupTelemetry(deps.now, line => deps.log(`[${name}] ${line}`));
478
+ startupTelemetry.mark('supervisor_started');
451
479
  const temp = opts.temp === true;
452
480
  const dir = agentDir(name, temp);
453
481
  const configPath = temp ? opts.configPath : resolveConfigPath(dir, opts.configPath);
@@ -486,14 +514,43 @@ export async function runOnce(name, opts = {}, partialDeps = {}) {
486
514
  deps.log(`[${name}] worklog rotation deferred: concurrent modification detected`);
487
515
  else if (rotation.rotated)
488
516
  deps.log(`[${name}] worklog rotated: ${rotation.beforeBytes} -> ${rotation.afterBytes} bytes`);
517
+ const durableTask = Boolean(readTempSupervisor(dir)?.taskOwner);
518
+ const retainedManaged = durableTask || Boolean(deps.suspendOnStop && adapter.supportsResume);
489
519
  const sidFile = join(dir, '.session-id');
520
+ const backendSidFile = join(dir, role.session === 'codex-app-server' ? '.session-id' : '.acp-session-id');
521
+ let recovery = retainedManaged ? readManagedRecovery(dir) : undefined;
522
+ // Native Codex shares .session-id with the runner's pre-launch UUID, so file
523
+ // existence alone is not evidence that its first backend thread was created.
524
+ const firstSessionRetry = retainedManaged && existsSync(join(dir, '.booted'))
525
+ && (!existsSync(backendSidFile) || (role.session === 'codex-app-server' && recovery?.session === 'pending'))
526
+ && firstManagedSessionMayStart(dir);
527
+ if (retainedManaged && existsSync(join(dir, '.booted'))
528
+ && (!existsSync(sidFile) || !existsSync(backendSidFile)) && !firstSessionRetry)
529
+ throw Error('TASK_CONTEXT_RESUME_UNAVAILABLE');
490
530
  if (!existsSync(sidFile))
491
531
  writeFileSync(sidFile, randomUUID() + '\n');
492
532
  const sessionId = readFileSync(sidFile, 'utf8').trim();
493
533
  const bootedFile = join(dir, '.booted');
494
534
  const exitFile = join(dir, '.exit-status');
495
535
  const booted = existsSync(bootedFile);
496
- const mode = booted && adapter.supportsResume ? 'resume' : 'fresh';
536
+ if (durableTask && !adapter.supportsResume)
537
+ throw Error('TASK_HARNESS_RESUME_UNSUPPORTED');
538
+ const mode = booted && adapter.supportsResume && !firstSessionRetry ? 'resume' : 'fresh';
539
+ const requireResume = retainedManaged && mode === 'resume';
540
+ if (retainedManaged && !recovery) {
541
+ // Older admitted sessions have no cursor. Continue once, without replaying
542
+ // their assignment; fresh sessions record intent before the first attempt.
543
+ recovery = { version: 1, session: booted ? 'established' : 'pending', readiness: booted,
544
+ initial: booted ? 'dispatching' : 'pending', active: booted };
545
+ writeManagedRecovery(dir, recovery);
546
+ }
547
+ const recoveryBefore = recovery ? { ...recovery } : undefined;
548
+ const saveRecovery = (update) => {
549
+ if (!recovery)
550
+ return;
551
+ recovery = { ...recovery, ...update };
552
+ writeManagedRecovery(dir, recovery);
553
+ };
497
554
  // Stamp EVERY attempt, not just the first. `.booted` used to be written only
498
555
  // on the fresh path, so after a restart — including one the supervisor never
499
556
  // saw, like an OOM-kill — its mtime still read the original boot and any
@@ -502,10 +559,22 @@ export async function runOnce(name, opts = {}, partialDeps = {}) {
502
559
  writeFileSync(bootedFile, `${new Date(deps.now()).toISOString()} ${mode}\n`);
503
560
  const runCwd = role.cwd && existsSync(role.cwd) ? role.cwd : dir;
504
561
  role = { ...role, monitor: { ...role.monitor, mode: 'fleet' } };
505
- const prep = await adapter.prepareSession(role, { stateDir: dir, runCwd });
562
+ // Native policy for Fleet commands run from the harness command sandbox. This
563
+ // is the same step for configured, spawned and task-room agents alike.
564
+ const prep = prepareManagedCliLaunch(role, {
565
+ // A temporary agent has no config path threaded through its detached
566
+ // supervisor; spawn records the one it was created from beside its snapshot.
567
+ stateDir: dir, runCwd, configPath: configPath ?? resolveConfigPath(dir), temp,
568
+ prep: await adapter.prepareSession(role, { stateDir: dir, runCwd }), log: deps.log,
569
+ });
506
570
  const sessionBackend = role.session ?? 'acp';
507
571
  const sessionLabel = sessionBackend === 'acp' ? 'ACP' : 'Codex app-server';
508
572
  let launch = adapter.agentSession.prepareLaunch(role, prep);
573
+ // Resolve the fleet-wide active subscription profile once, now: a later switch
574
+ // never changes this session (the operator restarts agents to move them).
575
+ const subscription = pinSubscriptionForLaunch(role, dir);
576
+ if (subscription?.pin.home)
577
+ deps.log(`[${name}] subscription: ${subscription.pin.provider} profile ${subscription.pin.profileId}`);
509
578
  // Preserve the role's existing isolation policy.
510
579
  let wrappedArgv = launch.argv;
511
580
  if (role.isolation) {
@@ -516,6 +585,7 @@ export async function runOnce(name, opts = {}, partialDeps = {}) {
516
585
  launch = { ...launch, argv: runtime.argv };
517
586
  const ctx = {
518
587
  ...isolationContextFor(role), stateDir: dir, runCwd,
588
+ subscriptionHome: subscription?.writablePath,
519
589
  runtimeReadPaths: [...runtime.readPaths, ...resolveLaunchRuntime([process.execPath, fileURLToPath(new URL('./agent-ours/bridge.js', import.meta.url))]).readPaths],
520
590
  };
521
591
  const policy = resolveIsolation(role.isolation, ctx);
@@ -538,9 +608,12 @@ export async function runOnce(name, opts = {}, partialDeps = {}) {
538
608
  if (rprefix.length)
539
609
  wrappedArgv = [...rprefix, ...wrappedArgv];
540
610
  }
611
+ startupTelemetry.mark('identity_prepare_started');
541
612
  const managedService = await deps.prepareAgentOurs(role, dir, opts.identityLifetime ? opts.identityLifetime === 'temporary' : temp);
613
+ startupTelemetry.mark('identity_ready');
614
+ let layoutRetiring = false;
542
615
  try {
543
- const managedHarness = prepareManagedHarness(role, dir, runCwd, managedService.descriptor, harnessChildEnv(role, launch.env, dir));
616
+ const managedHarness = prepareManagedHarness(role, dir, runCwd, managedService.descriptor, { ...harnessChildEnv(role, launch.env, dir), ...subscription?.env });
544
617
  // Start-stagger: space this launch at least start_stagger_ms after the previous
545
618
  // agent launch across the whole host, so a burst of boots (systemd starts every
546
619
  // user unit concurrently on boot; `ours-fleet up`/restart-all bulk-start) does not
@@ -556,10 +629,9 @@ export async function runOnce(name, opts = {}, partialDeps = {}) {
556
629
  }
557
630
  }
558
631
  // Prime the supervisor mail monitor's notification cursor at the
559
- // stream tip BEFORE the session launches so no arrival is missed during boot
560
- // (backlog before the tip is the SessionStart hook's job). Native-mode roles
561
- // leave wake ownership to the harness. Temp snapshots predating `monitor:` are
562
- // treated as native (monitor may be undefined on an old role.yaml).
632
+ // stream tip BEFORE the session launches so no arrival is missed during boot.
633
+ // Monitor.run checks the body-free initial unread summary after readiness;
634
+ // managed native hooks do not own backlog delivery.
563
635
  let sessionHandle;
564
636
  let modelRecovery;
565
637
  const resolvedMonitorDeps = monitorDeps(deps, role.env);
@@ -599,6 +671,16 @@ export async function runOnce(name, opts = {}, partialDeps = {}) {
599
671
  let unsubscribeRecovery;
600
672
  let monitorLoop;
601
673
  let sessionStartupComplete = false;
674
+ let startupRecoveryAllowed = false;
675
+ let successfulTurnObserved = false;
676
+ let monitorMayStart = false;
677
+ let monitorStarted = false;
678
+ const startMonitorAfterStartup = () => {
679
+ if (!monitorStarted && monitorMayStart && sessionStartupComplete && !deps.shouldStop?.()) {
680
+ monitorStarted = true;
681
+ monitorLoop = monitor?.run(pid);
682
+ }
683
+ };
602
684
  let sessionClosed = false;
603
685
  let ownerChannel;
604
686
  let ownerBinder;
@@ -617,23 +699,56 @@ export async function runOnce(name, opts = {}, partialDeps = {}) {
617
699
  if (perms.unattended === 'deny')
618
700
  deps.log(`[${name}] permission policy: unattended=deny — with no console attached, ` +
619
701
  `permission requests are automatically denied once each (reject_once) and the turn continues`);
702
+ startupTelemetry.mark('harness_starting');
620
703
  agentSession = await managedService.runtime.startHarness(() => deps.startAgentSession(adapter.agentSession, {
621
704
  managedOurs: managedHarness.ours,
622
705
  role, prep,
623
706
  launch: { ...launch, argv: wrappedArgv, env: managedHarness.env },
624
- cwd: runCwd, stateDir: dir, mode, permissions: perms,
707
+ cwd: runCwd, stateDir: dir, mode, requireResume, permissions: perms,
625
708
  permissionMode: effectivePermissionMode(role), log: deps.log,
626
709
  }));
710
+ managedService.setFileDelivery?.(agentSession.sendFileToChat?.bind(agentSession));
711
+ startupTelemetry.mark('harness_ready');
712
+ saveRecovery({ session: 'established' });
627
713
  pid = agentSession.pid;
628
714
  arbiter = new RoleTurnArbiter(agentSession);
629
715
  sessionHandle = arbiter;
630
- unsubscribeRecovery = agentSession.subscribe(event => {
716
+ const unsubscribeFailure = agentSession.subscribe(event => {
631
717
  if (event.kind !== 'error' || !event.text || event.origin?.kind === 'stall-watchdog')
632
718
  return;
633
719
  const evidence = classifyFailureText(event.text, sessionBackend, new Date(deps.now()).toISOString());
634
720
  if (evidence)
635
721
  resolvedMonitorDeps.onFailureEvidence?.(evidence);
636
722
  });
723
+ const unsubscribeTelemetry = agentSession.subscribeConversation?.(event => startupTelemetry.observe(event));
724
+ const unsubscribeStartup = agentSession.subscribeConversation?.(event => {
725
+ if (event.kind !== 'turn.completed' || !('outcome' in event.payload) || event.payload.outcome !== 'completed')
726
+ return;
727
+ successfulTurnObserved = true;
728
+ if (startupRecoveryAllowed) {
729
+ sessionStartupComplete = true;
730
+ startMonitorAfterStartup();
731
+ }
732
+ });
733
+ const activePrompts = new Set();
734
+ const unsubscribeContinuity = agentSession.subscribeConversation?.(event => {
735
+ if (!recovery?.readiness)
736
+ return;
737
+ if (event.kind === 'prompt.started') {
738
+ if (event.promptId)
739
+ activePrompts.add(event.promptId);
740
+ saveRecovery({ active: true });
741
+ }
742
+ else if (event.kind === 'turn.completed' && 'outcome' in event.payload) {
743
+ if (event.promptId)
744
+ activePrompts.delete(event.promptId);
745
+ // A shutdown/failed turn needs continuation; a successful terminal turn
746
+ // proves it may stay idle. Never store prompt bodies in this cursor.
747
+ if (event.payload.outcome === 'completed')
748
+ saveRecovery({ active: activePrompts.size > 0 });
749
+ }
750
+ });
751
+ unsubscribeRecovery = () => { unsubscribeFailure(); unsubscribeStartup?.(); unsubscribeTelemetry?.(); unsubscribeContinuity?.(); };
637
752
  if (role.owner_channel) {
638
753
  try {
639
754
  ownerBinder = await deps.acquireOwnerBinder(dir, name, role.owner_channel.identity);
@@ -670,11 +785,18 @@ export async function runOnce(name, opts = {}, partialDeps = {}) {
670
785
  ...(configPath ? { configPath } : {}),
671
786
  });
672
787
  control = deps.createControlServer(dir, arbiter, deps.log);
788
+ control.setCorrespondence?.({ contacts: () => managedService.runtime.readContacts(), history: query => managedService.runtime.readHistory(query) });
673
789
  control.setFleetAuditor(ownerChannel ? {
674
790
  begin: (requestId, argv) => ownerChannel.beginFleetCommandAudit(requestId, argv),
675
791
  finish: input => ownerChannel.finishFleetCommandAudit(input),
676
792
  present: presentations => ownerChannel.notifyFleetLifecycle(presentations),
677
- } : localFleetAuditor(dir, name, deps.log));
793
+ } : localFleetAuditor(dir, name, deps.log, agentSession));
794
+ control.setLayoutControl?.(createLayoutControl({
795
+ agent: name, temporary: temp, standalone: !role.roomMemberStartup,
796
+ runtime: managedService.runtime, session: arbiter,
797
+ stopping: () => Boolean(deps.shouldStop?.()),
798
+ retire: async () => { layoutRetiring = true; await arbiter.close(); },
799
+ }));
678
800
  await control.start();
679
801
  }
680
802
  catch (error) {
@@ -705,10 +827,8 @@ export async function runOnce(name, opts = {}, partialDeps = {}) {
705
827
  // refusal or a cancellation reached the agent and was not acted on, so
706
828
  // the monitor must keep its cursor and try again.
707
829
  submit: async (text, options) => {
708
- // Cancelling the runner-owned startup prompt makes startup look failed
709
- // and closes the session before the wake turn can run. During startup,
710
- // steer into the live turn instead; after it completes, honor the
711
- // configured interrupt policy normally.
830
+ // Delivery starts after the protected startup turn succeeds. Honor the
831
+ // configured interrupt policy for subsequent turns.
712
832
  const policy = options?.interrupt;
713
833
  const interrupt = policy === true && sessionStartupComplete;
714
834
  const promptOptions = {
@@ -716,8 +836,7 @@ export async function runOnce(name, opts = {}, partialDeps = {}) {
716
836
  ...(interrupt ? { interruptSource: 'fleet-monitor' } : {}),
717
837
  origin: { kind: 'fleet-monitor' },
718
838
  };
719
- // Startup is already a protected boundary: as with immediate mode,
720
- // steer rather than waiting on/cancelling the runner-owned first turn.
839
+ // after_tool waits for a safe boundary in subsequent active turns.
721
840
  const result = policy === 'after_tool' && sessionStartupComplete
722
841
  ? await arbiter.submitPromptAfterTool(text, promptOptions)
723
842
  : await arbiter.submitPrompt(text, promptOptions);
@@ -746,17 +865,20 @@ export async function runOnce(name, opts = {}, partialDeps = {}) {
746
865
  };
747
866
  },
748
867
  };
749
- const firstPrompt = mode === 'fresh'
750
- ? `Read and follow ${join(dir, 'briefing.md')} now.`
751
- : `Your supervisor has verified your assigned identity and room readiness. Read ${join(dir, 'WORKLOG.md')} and ${join(dir, 'briefing.md')}, then continue using the available ours tools.`;
868
+ const firstPrompt = managedStartupPrompt(dir, mode, booted);
752
869
  // Wait for the first turn's TERMINAL result. An agent that accepts the
753
870
  // startup prompt and then refuses it has not started; logging the role as
754
- // up would hide a role that never read its briefing.
755
- const starting = arbiter.submitPrompt(firstPrompt, { origin: { kind: 'startup' } });
756
- // Monitoring starts immediately. The delivery adapter above downgrades
757
- // interruption to steering until this startup turn reaches a terminal
758
- // success, so there is neither a deaf gap nor a boot-cancellation loop.
759
- monitorLoop = monitor?.run(pid);
871
+ // up would hide a role that never completed its readiness turn.
872
+ const retainedResume = retainedManaged && mode === 'resume' && recoveryBefore?.readiness === true;
873
+ if (!retainedResume)
874
+ startupTelemetry.mark('startup_submitted');
875
+ // The backend already verified the saved conversation. Resuming a managed
876
+ // member admits new wakes/console input without replaying startup or work.
877
+ const starting = retainedResume
878
+ ? Promise.resolve({ accepted: false, succeeded: true, outcome: 'completed', detail: 'retained backend resumed; no readiness prompt submitted' })
879
+ : arbiter.submitPrompt(firstPrompt, { origin: { kind: 'startup' } });
880
+ // Keep the early cursor baseline; do not deliver wakes into the protected
881
+ // first turn. Steering can pre-empt startup just like cancellation.
760
882
  // Owner traffic and agent replies must be observed during a long first
761
883
  // turn. Ordinary owner input steers until startup has proved successful.
762
884
  if (ownerChannel) {
@@ -778,6 +900,7 @@ export async function runOnce(name, opts = {}, partialDeps = {}) {
778
900
  }
779
901
  }
780
902
  const started = await starting;
903
+ startupTelemetry.finish();
781
904
  // A temporary role's first turn can be the active turn when an ours wake
782
905
  // needs immediate attention. A typed console/monitor cancellation ends
783
906
  // only that turn: the already-live agent session and any queued wake remain
@@ -816,7 +939,38 @@ export async function runOnce(name, opts = {}, partialDeps = {}) {
816
939
  else if (interruptedForWake)
817
940
  deps.log(`[${name}] ${sessionLabel} startup prompt cancelled by ${started.cancellationSource}; `
818
941
  + 'keeping temporary supervisor alive');
819
- sessionStartupComplete = true;
942
+ startupRecoveryAllowed = interruptedForWake;
943
+ sessionStartupComplete = started.succeeded || (interruptedForWake && successfulTurnObserved);
944
+ if (started.succeeded && !retainedResume)
945
+ startupTelemetry.mark('readiness_turn_completed');
946
+ if (started.succeeded)
947
+ saveRecovery({ readiness: true });
948
+ // Readiness is now complete. Configured work (or retained continuity) is
949
+ // admitted as a distinct turn; its terminal result must not block console
950
+ // readiness, mail delivery, or supervisor liveness. Empty fresh agents wait
951
+ // for authenticated console input or the monitor's unread/arrival wake.
952
+ const configuredWork = Boolean(role.mission?.trim() || role.roomMemberStartup?.task?.trim() || role.briefing_file?.trim());
953
+ const initialPending = retainedManaged && recoveryBefore?.initial === 'pending';
954
+ const interruptedWork = retainedManaged && (recoveryBefore?.active || recoveryBefore?.initial === 'dispatching');
955
+ const workPrompt = retainedManaged
956
+ ? initialPending && configuredWork ? managedTaskPrompt(dir, 'fresh', false)
957
+ : interruptedWork ? 'Fleet supervisor restarted. Continue pending work in the restored conversation; do not redo completed steps. '
958
+ + (recoveryBefore?.initial === 'dispatching' ? `If you have not yet received your assignment, read ${join(dir, 'briefing.md')} once before acting. ` : '')
959
+ + 'Read the continuation log only as needed to recover your next action. If no work remains, end this turn and wait for a concrete task or supervisor wake.' : undefined
960
+ : configuredWork || booted ? managedTaskPrompt(dir, mode, booted) : undefined;
961
+ if (started.succeeded && !deps.shouldStop?.() && workPrompt) {
962
+ saveRecovery({ initial: 'dispatching', active: true });
963
+ const queued = await arbiter.queuePrompt(workPrompt, {
964
+ origin: { kind: 'startup' },
965
+ });
966
+ void queued.completion.then(result => {
967
+ if (result.succeeded)
968
+ saveRecovery({ initial: 'completed', active: activePrompts.size > 0 });
969
+ deps.log(`[${name}] configured task turn ${result.outcome}`);
970
+ }, () => deps.log(`[${name}] configured task turn failed`));
971
+ }
972
+ else if (started.succeeded && initialPending && !configuredWork)
973
+ saveRecovery({ initial: 'completed' });
820
974
  reloadLoopConfig = async () => {
821
975
  const nextRole = findRole(loadConfig(configPath), name);
822
976
  const definitions = nextRole.loops ?? [];
@@ -873,9 +1027,10 @@ export async function runOnce(name, opts = {}, partialDeps = {}) {
873
1027
  }
874
1028
  }
875
1029
  deps.log(`[${name}] up; pid=${pid} cwd=${runCwd} harness=${role.harness} session=${sessionBackend} mode=${mode}`);
876
- // The monitor loop lives exactly as long as the session: it starts once the
877
- // pane pid is known and is stopped when that pid dies (task dies with runner).
878
- monitorLoop ??= monitor?.run(pid);
1030
+ // Keep arrivals behind the early prime() cursor; never re-prime at startup.
1031
+ // Recoverable cancellation preserves the supervisor, not monitor readiness.
1032
+ monitorMayStart = true;
1033
+ startMonitorAfterStartup();
879
1034
  recoveryController = new RoleRecoveryController({
880
1035
  role: name, identity: role.identity, stateDir: dir, now: deps.now, sleep: deps.sleep,
881
1036
  log: deps.log,
@@ -902,6 +1057,14 @@ export async function runOnce(name, opts = {}, partialDeps = {}) {
902
1057
  }
903
1058
  },
904
1059
  });
1060
+ let chatIdle;
1061
+ try {
1062
+ if (temp && !role.roomMemberStartup && !readTempSupervisor(dir)?.taskOwner)
1063
+ chatIdle = new TemporaryChatIdle(dir, deps.now());
1064
+ }
1065
+ catch {
1066
+ deps.log(`[${name}] automatic idle closure disabled: activity state is unreadable`);
1067
+ }
905
1068
  const start = deps.now();
906
1069
  let nextLoopReloadAt = deps.now() + 30_000;
907
1070
  let nextIdentityPollAt = deps.now();
@@ -925,6 +1088,19 @@ export async function runOnce(name, opts = {}, partialDeps = {}) {
925
1088
  void recoveryController.recover(observation.generation).catch(error => deps.log(`[${name}] daemon recovery controller failed: ${error?.name ?? 'Error'}`));
926
1089
  }
927
1090
  }
1091
+ let idleExpired = false;
1092
+ try {
1093
+ idleExpired = chatIdle?.observe(sessionHandle.snapshot(), sessionHandle.conversationSnapshot?.().queueDepth ?? -1, now) ?? false;
1094
+ }
1095
+ catch {
1096
+ chatIdle = undefined;
1097
+ deps.log(`[${name}] automatic idle closure disabled: activity state could not be saved`);
1098
+ }
1099
+ if (idleExpired && await arbiter.retireIfIdle((snapshot, queue) => chatIdle.observe(snapshot, queue, deps.now()))) {
1100
+ retirementReason = 'idle-timeout';
1101
+ sessionClosed = true;
1102
+ break;
1103
+ }
928
1104
  if (deps.shouldStop?.()) {
929
1105
  if (temp) {
930
1106
  retirementReason = requestedTempStopReason(dir) ?? 'supervisor-signal';
@@ -936,7 +1112,7 @@ export async function runOnce(name, opts = {}, partialDeps = {}) {
936
1112
  }
937
1113
  if (temp && now >= nextIdentityPollAt) {
938
1114
  nextIdentityPollAt = now + TEMP_IDENTITY_POLL_MS;
939
- const presence = await probeIdentityPresence(role.identity, deps.fetch, resolvedMonitorDeps.env);
1115
+ const presence = await probeIdentityPresence(role.identity, deps.fetch, resolvedMonitorDeps.env, deps.identityProbeDeps);
940
1116
  if (presence.state === 'present') {
941
1117
  identityObserved = true;
942
1118
  identityAbsentSince = undefined;
@@ -973,6 +1149,7 @@ export async function runOnce(name, opts = {}, partialDeps = {}) {
973
1149
  }
974
1150
  }
975
1151
  }
1152
+ monitorMayStart = false;
976
1153
  if (loopManager) {
977
1154
  control?.setLoopManager(undefined);
978
1155
  await loopManager.stop();
@@ -1018,11 +1195,12 @@ export async function runOnce(name, opts = {}, partialDeps = {}) {
1018
1195
  const rotate = (why) => {
1019
1196
  writeFileSync(sidFile, randomUUID() + '\n');
1020
1197
  rmSync(bootedFile, { force: true });
1198
+ rmSync(managedRecoveryPath(dir), { force: true });
1021
1199
  rotated = true;
1022
1200
  deps.log(`[${name}] ${why} -> rotated session-id; next start is FRESH`);
1023
1201
  };
1024
- if (!temp && deps.shouldStop?.())
1025
- deps.log(`[${name}] supervisor stop requested -> next start RESUMES context`);
1202
+ if (readTempSupervisor(dir)?.taskOwner || ((!temp || deps.suspendOnStop) && deps.shouldStop?.()))
1203
+ deps.log(`[${name}] supervisor/context retained -> next start RESUMES context`);
1026
1204
  else if (exitRecord.detail.includes(ACP_CANCEL_DEADLINE_EXCEEDED)
1027
1205
  || exitRecord.detail.includes(CODEX_APP_SERVER_CANCEL_DEADLINE_EXCEEDED))
1028
1206
  // This is a deliberate adapter reclamation, not evidence that resume state
@@ -1053,11 +1231,11 @@ export async function runOnce(name, opts = {}, partialDeps = {}) {
1053
1231
  throw new SupervisorRecycleRequiredError();
1054
1232
  return {
1055
1233
  elapsedSecs: elapsed, exit: exitRecord, rotated, mode, modelRecovery,
1056
- ...(retirementReason ? { retirementReason } : {}),
1234
+ ...(layoutRetiring ? { retirementReason: 'operator-stop' } : retirementReason ? { retirementReason } : {}),
1057
1235
  };
1058
1236
  }
1059
1237
  finally {
1060
- await managedService.close(false);
1238
+ await managedService.close(layoutRetiring);
1061
1239
  }
1062
1240
  }
1063
1241
  /**
@@ -1072,11 +1250,13 @@ export async function runOnce(name, opts = {}, partialDeps = {}) {
1072
1250
  */
1073
1251
  export async function runSupervised(name, opts = {}, partialDeps = {}, attempt = runOnce) {
1074
1252
  const deps = { ...defaultDeps(), ...partialDeps };
1075
- const dir = agentDir(name);
1253
+ const taskMember = opts.temp === true;
1254
+ const dir = agentDir(name, taskMember);
1076
1255
  mkdirSync(dir, { recursive: true });
1077
1256
  let stopping = false;
1078
1257
  const requestStop = () => { stopping = true; };
1079
- const shouldStop = () => stopping || (partialDeps.shouldStop?.() ?? false);
1258
+ const shouldStop = () => stopping || (partialDeps.shouldStop?.() ?? false)
1259
+ || (taskMember && requestedTempStopReason(dir) === 'operator-stop');
1080
1260
  deps.shouldStop = shouldStop;
1081
1261
  const stamp = () => new Date(deps.now()).toISOString();
1082
1262
  // Record how the PREVIOUS supervisor process ended before doing anything
@@ -1104,10 +1284,12 @@ export async function runSupervised(name, opts = {}, partialDeps = {}, attempt =
1104
1284
  process.on('SIGINT', requestStop);
1105
1285
  try {
1106
1286
  while (!shouldStop()) {
1287
+ if (taskMember && !taskSupervisorMayRun(name))
1288
+ break;
1107
1289
  let ledger = readRestartLedger(dir);
1108
1290
  try {
1109
1291
  const configPath = resolveConfigPath(dir, opts.configPath);
1110
- const role = findRole(loadConfig(configPath), name);
1292
+ const role = taskMember ? loadTempRole(name) : findRole(loadConfig(configPath), name);
1111
1293
  reconcileModelRecovery(dir, role, stamp());
1112
1294
  if (modelRecoveryHeld(dir)) {
1113
1295
  await deps.sleep(HELD_DOWN_POLL_MS);
@@ -1123,18 +1305,26 @@ export async function runSupervised(name, opts = {}, partialDeps = {}, attempt =
1123
1305
  await deps.sleep(HELD_DOWN_POLL_MS);
1124
1306
  continue;
1125
1307
  }
1308
+ if (attempt === runOnce && taskMember) {
1309
+ // Boot-time daemon unavailability must not consume the harness crash budget.
1310
+ const { waitForRoleDaemon } = await import('./startup-readiness.js');
1311
+ await waitForRoleDaemon(name, undefined, {}, true);
1312
+ }
1313
+ if (taskMember && (shouldStop() || !taskSupervisorMayRun(name)))
1314
+ break;
1126
1315
  let result;
1127
1316
  try {
1128
1317
  // Service-manager boot and automatic retry bypass the operator-facing
1129
1318
  // up/restart commands. Reconcile again immediately before every real
1130
1319
  // permanent harness attempt; the operation is idempotent and releases its
1131
1320
  // provisioning lease before the agent or owner channel binds.
1132
- if (attempt === runOnce) {
1321
+ if (attempt === runOnce && !taskMember) {
1133
1322
  const configPath = resolveConfigPath(dir, opts.configPath);
1134
1323
  const role = findRole(loadConfig(configPath), name);
1135
1324
  await reconcilePermanentRoleIdentities(role, undefined, deps.log);
1136
1325
  }
1137
- result = await attempt(name, { configPath: opts.configPath, allowResumeRotation: !ledger.resumeDiscarded }, deps);
1326
+ result = await attempt(name, { configPath: opts.configPath, ...(taskMember ? { temp: true } : {}),
1327
+ allowResumeRotation: taskMember ? false : !ledger.resumeDiscarded }, deps);
1138
1328
  }
1139
1329
  catch (e) {
1140
1330
  if (e instanceof SupervisorRecycleRequiredError) {
@@ -1156,7 +1346,7 @@ export async function runSupervised(name, opts = {}, partialDeps = {}, attempt =
1156
1346
  mode: 'fresh',
1157
1347
  };
1158
1348
  }
1159
- if (stopping)
1349
+ if (stopping || (taskMember && (shouldStop() || result.retirementReason)))
1160
1350
  break;
1161
1351
  // Re-read: the attempt itself may have taken minutes, and an operator may
1162
1352
  // have reset the ledger meanwhile.
@@ -1174,7 +1364,8 @@ export async function runSupervised(name, opts = {}, partialDeps = {}, attempt =
1174
1364
  await deps.sleep(HELD_DOWN_POLL_MS);
1175
1365
  continue;
1176
1366
  }
1177
- const fastFailSecs = fastFailSecsFor(name, opts.configPath);
1367
+ const fastFailSecs = taskMember ? getAdapter(loadTempRole(name).harness).exitPolicy.fastFailSecs
1368
+ : fastFailSecsFor(name, opts.configPath);
1178
1369
  // The fast-fail boundary starts a recovery episode; it must not also be
1179
1370
  // the boundary that declares recovery successful. Otherwise alternating
1180
1371
  // 19s and 20s deaths erase one another forever. Require the configured
@@ -1211,7 +1402,7 @@ export async function runSupervised(name, opts = {}, partialDeps = {}, attempt =
1211
1402
  next.nextDelayMs = 0;
1212
1403
  writeRestartLedger(dir, next);
1213
1404
  deps.log(`[${name}] HELD DOWN after ${failures} immediate failures at ${next.openedAt} — ` +
1214
- `${reason}; the agent will not be restarted until: ours-fleet restart ${name}`);
1405
+ `${reason}; recover with: ${taskMember ? `ours-fleet task recover-members ${readTempSupervisor(dir).taskOwner.taskId}` : `ours-fleet restart ${name}`}`);
1215
1406
  continue;
1216
1407
  }
1217
1408
  writeRestartLedger(dir, next);
@@ -1226,7 +1417,7 @@ export async function runSupervised(name, opts = {}, partialDeps = {}, attempt =
1226
1417
  process.off('SIGINT', requestStop);
1227
1418
  // An orderly shutdown clears the marker; an unhandled signal or OOM-kill
1228
1419
  // leaves it so the successor can identify an abrupt termination.
1229
- if (shouldStop())
1420
+ if (!taskMember && shouldStop())
1230
1421
  await deps.releaseAgentOurs(findRole(loadConfig(opts.configPath), name));
1231
1422
  releaseSupervisorRun(dir);
1232
1423
  }
@@ -1246,8 +1437,58 @@ function fastFailSecsFor(name, configPath) {
1246
1437
  }
1247
1438
  }
1248
1439
  /** Temp-agent entrypoint: run once, journal why it ended, then archive its evidence. */
1440
+ /** The parent uses the same adapter-relative sustained-uptime reset boundary. */
1441
+ export function stableSupervisorWindow(name, temp, configPath) {
1442
+ try {
1443
+ const role = temp ? loadTempRole(name) : findRole(loadConfig(configPath), name);
1444
+ return getAdapter(role.harness).exitPolicy.fastFailSecs * RESTART_FAIL_THRESHOLD;
1445
+ }
1446
+ catch {
1447
+ return 20 * RESTART_FAIL_THRESHOLD;
1448
+ }
1449
+ }
1249
1450
  export async function runTemp(name, deps = {}, attempt = runOnce) {
1250
1451
  const dir = agentDir(name, true);
1452
+ const metadata = readTempSupervisor(dir);
1453
+ // Non-resumable standalone temporaries retain their prior finite lifecycle:
1454
+ // a parent stop retires them rather than promising retained conversation.
1455
+ if (!metadata?.taskOwner && deps.suspendOnStop && !getAdapter(loadTempRole(name).harness).supportsResume)
1456
+ deps = { ...deps, suspendOnStop: false };
1457
+ if (!metadata?.taskOwner) {
1458
+ const role = loadTempRole(name);
1459
+ const provenance = readProvenance(dir);
1460
+ if (role.roomMemberStartup?.task_id
1461
+ || (name.startsWith('layout-') && provenance?.creationActionId?.startsWith('task-')))
1462
+ throw Error('TASK_SUPERVISOR_OWNER_MISSING');
1463
+ }
1464
+ if (metadata?.taskOwner) {
1465
+ // Serialize the entire process lifetime before metadata/session writes.
1466
+ const lease = await acquireOwnerBinderLease(join(dir, '.task-supervisor'), 'task-supervisor', name);
1467
+ let ownershipVerified = false;
1468
+ try {
1469
+ const mayRun = taskSupervisorMayRun(name);
1470
+ ownershipVerified = true;
1471
+ if (!mayRun)
1472
+ return;
1473
+ await markTempSupervisorActive(dir);
1474
+ await runSupervised(name, { temp: true }, deps, attempt);
1475
+ }
1476
+ finally {
1477
+ try {
1478
+ if (ownershipVerified && requestedTempStopReason(dir)) {
1479
+ // Parent shutdown suspends; an explicit stop retires the exact saved
1480
+ // owner even when no harness attempt ran (for example held circuits).
1481
+ await (deps.releaseAgentOurs ?? releaseManagedAgent)(loadTempRole(name), taskMemberRetirementProof(name));
1482
+ const archived = archiveTempState(name, 'operator-stop', 'retired', 'explicit task member retirement');
1483
+ deps.log?.(`[${name}] task member retired: operator-stop${archived ? `; evidence archived at ${archived}` : ''}`);
1484
+ }
1485
+ }
1486
+ finally {
1487
+ lease.release();
1488
+ }
1489
+ }
1490
+ return;
1491
+ }
1251
1492
  await markTempSupervisorActive(dir);
1252
1493
  let signal;
1253
1494
  const onTerm = () => { signal = 'SIGTERM'; };
@@ -1274,7 +1515,8 @@ export async function runTemp(name, deps = {}, attempt = runOnce) {
1274
1515
  process.off('SIGTERM', onTerm);
1275
1516
  process.off('SIGINT', onInt);
1276
1517
  // A recycle replaces the supervisor process, not the logical temporary agent.
1277
- if (!(failure instanceof SupervisorRecycleRequiredError) || signal || deps.shouldStop?.()) {
1518
+ if (!((signal || deps.shouldStop?.()) && deps.suspendOnStop && !requestedTempStopReason(dir))
1519
+ && (!(failure instanceof SupervisorRecycleRequiredError) || signal || deps.shouldStop?.())) {
1278
1520
  const requested = requestedTempStopReason(dir);
1279
1521
  const reason = requested
1280
1522
  ?? result?.retirementReason