@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
@@ -883,8 +883,15 @@ export class OwnerChannel {
883
883
  const recovered = [];
884
884
  for (const claim of this.messageRecovery.list()) {
885
885
  const item = await this.client.getHistoryItem(claim.wireId);
886
- if (!item)
887
- throw new Error(`journaled owner message ${claim.wireId} is missing from persistent history`);
886
+ if (!item) {
887
+ // The daemon has definitively lost this body. Retrying the claim would
888
+ // block every later owner message, including after a supervisor restart.
889
+ // Keep transient lookup errors retryable; only an explicit absence skips.
890
+ this.messageRecovery.pruneHandled(wireId => wireId === claim.wireId);
891
+ this.options.log(`[${this.options.role}] skipped journaled owner message ${claim.wireId} `
892
+ + `(seq=${claim.seq}): missing from persistent history`);
893
+ continue;
894
+ }
888
895
  recovered.push(this.historyMessage(item, claim));
889
896
  }
890
897
  const listed = await this.client.listIncomingMessages();
@@ -1391,6 +1398,7 @@ export class OwnerChannel {
1391
1398
  await this.fleetOps.provisionTask(task.task_id);
1392
1399
  return outcome;
1393
1400
  }),
1401
+ observeTaskReadiness: taskId => new TaskRoomApplicationService(this.options.configPath).observeTaskReadiness(taskId),
1394
1402
  taskProvisioningOutcome: taskId => new TaskRoomApplicationService(this.options.configPath).taskProvisioningOutcome(taskId),
1395
1403
  listTasks: filter => new TaskRoomApplicationService(this.options.configPath).listTasks(filter),
1396
1404
  groupedTasks: filter => new TaskRoomApplicationService(this.options.configPath).groupedTasks(filter),
@@ -1977,6 +1985,9 @@ export class OwnerChannel {
1977
1985
  const progressMs = this.options.config.progress_interval_ms;
1978
1986
  let lastSeq = activityCursor;
1979
1987
  let startedAt;
1988
+ // When one turn delivers several waiting prompts, its events carry the turn's
1989
+ // id, not this request's: follow the turn that actually carries this request.
1990
+ const inTurn = (turnId) => turnId === (this.options.session.sharedTurnId?.(queued.promptId) ?? queued.promptId);
1980
1991
  let phase = 'starting request';
1981
1992
  let timer;
1982
1993
  const flushCommentary = () => {
@@ -2033,7 +2044,7 @@ export class OwnerChannel {
2033
2044
  };
2034
2045
  const acceptCommentary = (event) => {
2035
2046
  if (!this.commentsEnabled
2036
- || active.commentaryDisabled || active.finalizing || event.turnId !== queued.promptId
2047
+ || active.commentaryDisabled || active.finalizing || !inTurn(event.turnId)
2037
2048
  || event.kind !== 'agent_text' || event.messagePhase !== 'commentary'
2038
2049
  || event.replayed || event.origin?.kind !== 'owner'
2039
2050
  || event.origin.requestId !== active.requestId
@@ -2074,7 +2085,7 @@ export class OwnerChannel {
2074
2085
  const reportProgress = () => {
2075
2086
  const events = this.options.session.eventsSince(lastSeq);
2076
2087
  lastSeq = Math.max(lastSeq, this.latestEventSeq(events));
2077
- const activity = events.filter(event => event.turnId === queued.promptId);
2088
+ const activity = events.filter(event => inTurn(event.turnId));
2078
2089
  if (!activity.length)
2079
2090
  return;
2080
2091
  startedAt ??= Date.parse(activity[0].at) || Date.now();
@@ -2099,10 +2110,10 @@ export class OwnerChannel {
2099
2110
  const startProgress = (event) => {
2100
2111
  if (timer || progressMs <= 0)
2101
2112
  return;
2102
- if (event && event.turnId !== queued.promptId)
2113
+ if (event && !inTurn(event.turnId))
2103
2114
  return;
2104
2115
  const observed = event ? [event] : this.options.session.eventsSince(activityCursor)
2105
- .filter(item => item.turnId === queued.promptId);
2116
+ .filter(item => inTurn(item.turnId));
2106
2117
  if (!observed.length)
2107
2118
  return;
2108
2119
  startedAt = Date.parse(observed[0].at) || Date.now();
@@ -1,3 +1,4 @@
1
+ import type { TaskReadinessIssue } from '../rooms-tasks/live-readiness.js';
1
2
  import type { InterruptOutcome, SessionEvent, SessionSnapshot } from '../session/types.js';
2
3
  import type { RoomOrchestrationRecord, TaskOutcome, TaskRecord, TaskTerminalIntent, TaskListRecord } from '../rooms-tasks/types.js';
3
4
  import { type OwnerCommentsState } from './notices.js';
@@ -58,6 +59,9 @@ export interface OwnerCommandContext {
58
59
  terminalTask(taskId: string, kind: TaskTerminalIntent['kind'], outcome?: TaskOutcome): Promise<void>;
59
60
  createTask(input: Omit<CreateTaskRequest, 'actor'>): Promise<TaskRecord>;
60
61
  startTask(taskId: string): Promise<TaskProvisioningOutcome | TaskRecord>;
62
+ observeTaskReadiness?(taskId: string): Promise<{
63
+ state: 'ready';
64
+ } | TaskReadinessIssue>;
61
65
  taskProvisioningOutcome?(taskId: string): TaskProvisioningOutcome;
62
66
  listTasks(filter?: {
63
67
  state?: TaskState | TaskState[];
@@ -47,6 +47,11 @@ function isKnownOwnerRoomState(message) {
47
47
  ].some(pattern => pattern.test(message));
48
48
  }
49
49
  function ownerTaskFailure(error) {
50
+ if (error instanceof TaskRoomApplicationError && error.code === 'task_not_ready')
51
+ return {
52
+ kind: 'state', detail: error.message,
53
+ action: 'Ask Fleet Coordinator to inspect room and member status and choose supported recovery.',
54
+ };
50
55
  if (error instanceof TaskListError)
51
56
  return {
52
57
  kind: error.code === 'list_not_found' ? 'not_found'
@@ -301,6 +306,7 @@ export const ownerCommands = [
301
306
  const trailingLines = argLines.slice(1).join('\n').trim() || undefined;
302
307
  const showTask = async (id) => {
303
308
  const { task: t } = ctx.getTask(id);
309
+ const readiness = t.state === 'active' ? await ctx.observeTaskReadiness?.(id) : undefined;
304
310
  await ctx.reply(renderMarkdownResult({
305
311
  icon: '📋', title: 'Task details',
306
312
  fields: [
@@ -308,6 +314,8 @@ export const ownerCommands = [
308
314
  { label: 'Title', value: t.title },
309
315
  { label: 'Status', value: taskStatus(t.state), kind: 'markdown' },
310
316
  { label: 'List', value: t.list_name ?? 'default', kind: 'code' },
317
+ ...(readiness ? [{ label: 'Readiness', value: readiness.state },
318
+ ...('reason' in readiness ? [{ label: 'Reason', value: readiness.reason }] : [])] : []),
311
319
  ...(t.blocked ? [{ label: 'Blocked', value: t.blocked.reason }] : []),
312
320
  ...(t.template ? [{ label: 'Template', value: `${t.template.name}@${t.template.version}`, kind: 'code' }] : []),
313
321
  ...(t.room_id ? [{ label: 'Room', value: t.room_id, kind: 'code' }] : []),
@@ -1,6 +1,6 @@
1
+ import { redactErasedContent, writePrivacyFilteredLedger } from '../erased-resources.js';
1
2
  import { createHash } from 'node:crypto';
2
3
  import { chmodSync, existsSync, readFileSync } from 'node:fs';
3
- import { replaceFileAtomically } from '../atomic-file.js';
4
4
  import { lifecycleEventDigestBasis, validateFleetAuditPresentations, } from '../fleet-command-audit.js';
5
5
  const DIGEST = /^[a-f0-9]{64}$/u;
6
6
  const LIMIT = 5_000;
@@ -63,7 +63,7 @@ export class FleetLifecycleOutbox {
63
63
  }
64
64
  pending() {
65
65
  this.assertHealthy();
66
- return this.entries.filter(entry => entry.delivery === 'pending').map(entry => structuredClone(entry));
66
+ return redactErasedContent(this.entries).filter(entry => entry.delivery === 'pending').map(entry => structuredClone(entry));
67
67
  }
68
68
  finish(digest, delivery) {
69
69
  this.assertHealthy();
@@ -82,6 +82,7 @@ export class FleetLifecycleOutbox {
82
82
  throw new Error(this.corruptReason);
83
83
  }
84
84
  persist() {
85
- replaceFileAtomically(this.path, `${JSON.stringify({ version: 1, entries: this.entries })}\n`, 0o600);
85
+ this.entries = redactErasedContent(this.entries);
86
+ writePrivacyFilteredLedger(this.path, { version: 1, entries: this.entries });
86
87
  }
87
88
  }
@@ -1,7 +1,6 @@
1
1
  import { randomUUID } from 'node:crypto';
2
2
  import { readFile } from 'node:fs/promises';
3
- import { extname, join } from 'node:path';
4
- import { readDaemonConfig, resolveEndpoint } from '../monitor.js';
3
+ import { extname } from 'node:path';
5
4
  import { readClientProfile } from '../client-profile.js';
6
5
  import { OursError, attachOursClient, } from '@ours.network/sdk/client';
7
6
  /** Any failure of a daemon operation. Never carries a message body or a token. */
@@ -131,24 +130,10 @@ export class OursSdkClient {
131
130
  return;
132
131
  const environment = { ...process.env, ...this.env };
133
132
  const profile = readClientProfile(environment);
134
- // Reuse existing connection configuration. Identity selection checks the
135
- // daemon capability before reading the client's protected token delivery file.
136
- const endpoint = !profile && environment.OURS_DAEMON_ID
137
- ? resolveEndpoint(environment, false) : undefined;
138
- const token = endpoint ? environment.OURS_API_TOKEN?.trim() || readDaemonConfig(environment).apiToken : undefined;
139
- const options = profile ? {
133
+ const options = {
140
134
  endpoint: profile.endpoint, expectedInstanceId: profile.expectedInstanceId,
141
135
  credentialPath: profile.credentialPath,
142
136
  sessionMode: 'external', leaseToken: this.leaseToken, env: {},
143
- } : endpoint ? {
144
- endpoint: endpoint.origin, expectedInstanceId: environment.OURS_DAEMON_ID,
145
- sessionMode: 'external', leaseToken: this.leaseToken,
146
- ...(token ? { token } : { credentialPath: join(endpoint.stateDir, 'daemon-token') }),
147
- } : {
148
- env: environment,
149
- leaseToken: this.leaseToken,
150
- clientPid: process.pid,
151
- fetch: notificationDeadlineFetch(this.deps.fetch ?? globalThis.fetch, this.deps.notificationRequestDeadlineMs ?? NOTIFICATION_REQUEST_DEADLINE_MS),
152
137
  };
153
138
  this.client = await (this.deps.attachClient?.(options) ?? attachOursClient(options));
154
139
  this.terminalReleasePending = true;
package/dist/paths.d.ts CHANGED
@@ -11,6 +11,8 @@ export declare const home: () => string;
11
11
  */
12
12
  export declare function deriveXdgRuntimeDir(env?: NodeJS.ProcessEnv, uid?: number | undefined, exists?: (p: string) => boolean): string | undefined;
13
13
  export declare const stateRoot: () => string;
14
+ /** The Owner invitation file that tunnel setup writes itself after enrollment. */
15
+ export declare const workspaceOwnerInvite: () => string;
14
16
  export declare const agentsRoot: () => string;
15
17
  export declare const tmpRoot: () => string;
16
18
  export declare const logsRoot: () => string;
package/dist/paths.js CHANGED
@@ -21,6 +21,8 @@ export function deriveXdgRuntimeDir(env = process.env, uid = process.getuid?.(),
21
21
  return env.XDG_RUNTIME_DIR;
22
22
  }
23
23
  export const stateRoot = () => join(home(), '.ours-fleet');
24
+ /** The Owner invitation file that tunnel setup writes itself after enrollment. */
25
+ export const workspaceOwnerInvite = () => join(stateRoot(), 'workspace', 'owner.invite');
24
26
  export const agentsRoot = () => join(stateRoot(), 'agents');
25
27
  export const tmpRoot = () => join(stateRoot(), 'tmp');
26
28
  export const logsRoot = () => join(stateRoot(), 'logs');
@@ -67,10 +67,21 @@ const V5_GENERATED_ROLE_DEFAULT_FINGERPRINTS = Object.freeze({
67
67
  'agent_templates/Developer.yaml': 'fdd953aab72d042e62780dd273598745aae59521b39edf4876d7676bb7189c83',
68
68
  'agent_templates/Critic.yaml': '3e98a28f17876061cfadf984351aa59fdbe19629e514eff52c74546545c64a91',
69
69
  });
70
+ // The packaged Coordinator role of revisions 8 and 9 addressed a named owner channel that a
71
+ // fresh installation does not have. Untouched copies adopt the current role; edited ones stay.
72
+ const OWNER_CHANNEL_COORDINATOR_FINGERPRINTS = Object.freeze({
73
+ 'roles/Coordinator.yaml': [
74
+ '8c8ad28d9bb9ddf9dba8c5c9c7859f25edd8fa10ebf833e2340124a6e49583ec',
75
+ 'f0d8b169ad0a18051b57e5280b93cf01e05faa5c12f4e12bdf89973edf2ef58a',
76
+ ],
77
+ });
70
78
  const CURRENT_ROLE_DEFAULTS = new Set([
71
79
  'roles/Coordinator.yaml', 'roles/LocalCoordinator.yaml', 'roles/Developer.yaml', 'roles/Critic.yaml',
80
+ 'roles/Engineer.yaml',
72
81
  'agent_templates/LocalCoordinator.yaml', 'agent_templates/Developer.yaml', 'agent_templates/Critic.yaml',
82
+ 'agent_templates/Engineer.yaml',
73
83
  'room_templates/single.yaml', 'room_templates/pair.yaml', 'room_templates/team.yaml',
84
+ 'room_templates/engineering.yaml',
74
85
  'agents/FleetCoordinator.yaml',
75
86
  ]);
76
87
  export const legacyStarterMigrationManifest = Object.freeze({
@@ -193,6 +204,7 @@ export function migratePackagedRoleDefaults(configuration, options = {}, hooks =
193
204
  ...Object.keys(V3_ROLE_DEFAULT_FINGERPRINTS),
194
205
  ...Object.keys(V4_ROLE_DEFAULT_FINGERPRINTS),
195
206
  ...Object.keys(V5_ROLE_DEFAULT_FINGERPRINTS),
207
+ ...Object.keys(OWNER_CHANNEL_COORDINATOR_FINGERPRINTS),
196
208
  ]);
197
209
  for (const relative of known) {
198
210
  const path = join(root, relative);
@@ -206,6 +218,7 @@ export function migratePackagedRoleDefaults(configuration, options = {}, hooks =
206
218
  V4_GENERATED_ROLE_DEFAULT_FINGERPRINTS[relative],
207
219
  V5_ROLE_DEFAULT_FINGERPRINTS[relative],
208
220
  V5_GENERATED_ROLE_DEFAULT_FINGERPRINTS[relative],
221
+ ...(OWNER_CHANNEL_COORDINATOR_FINGERPRINTS[relative] ?? []),
209
222
  ].some(expected => expected !== undefined && actual === expected);
210
223
  if (recognized)
211
224
  (CURRENT_ROLE_DEFAULTS.has(relative) ? replacements : removals).push(path);
@@ -258,6 +271,18 @@ export function migratePackagedRoleDefaults(configuration, options = {}, hooks =
258
271
  unlinkSync(target);
259
272
  copyFileSync(join(source, relative), target, constants.COPYFILE_EXCL);
260
273
  chmodSync(target, 0o600);
274
+ // New Engineer seats use the existing Developer's selected brain. This
275
+ // preserves wizard/custom provider choices without copying permissions.
276
+ if (relative === 'agent_templates/Engineer.yaml' && classified.additions.includes(path)) {
277
+ const developer = join(root, 'agent_templates/Developer.yaml');
278
+ if (existsSync(developer)) {
279
+ const value = parseFleetDocument(developer, readFileSync(developer, 'utf8'), 'strict').value;
280
+ if (value.brain !== undefined && JSON.stringify(value.brain) !== JSON.stringify({ ref: 'claude-default' })) {
281
+ const bytes = readFileSync(target, 'utf8').replace('brain: { ref: claude-default }', () => `brain: ${JSON.stringify(value.brain)}`);
282
+ writeFileSync(target, bytes, { mode: 0o600 });
283
+ }
284
+ }
285
+ }
261
286
  fsyncPath(target);
262
287
  }
263
288
  for (const kind of ['roles', 'agent_templates', 'room_templates', 'agents'])
@@ -103,6 +103,7 @@ export function resolvedRolePlan(role, manifestDir = dirname(role.sourceFile)) {
103
103
  isolation: role.isolation ?? null,
104
104
  worklog: role.worklog ?? null,
105
105
  authProxy: role.auth_proxy ?? null,
106
+ managedCli: role.managed_cli ?? [],
106
107
  oversee: role.oversee ?? [],
107
108
  harnessOptions: redactSensitive(role.harness_options ?? null),
108
109
  env: {
@@ -0,0 +1,48 @@
1
+ import type { FleetConfig } from '../config.js';
2
+ import { TaskStateError } from './task-state.js';
3
+ import { provisionMembers } from './provision.js';
4
+ import type { CoworkAdapter } from './cowork-adapter.js';
5
+ export declare class MemberAdditionRejected extends TaskStateError {
6
+ readonly accepted = false;
7
+ }
8
+ export interface AddMemberRequest {
9
+ requestId: string;
10
+ slot: string;
11
+ role: string;
12
+ brain: string;
13
+ agentTemplate: string;
14
+ }
15
+ export declare function memberAddition(taskId: string, requestId: string): {
16
+ requestId: string;
17
+ taskId: string;
18
+ roleId: string;
19
+ state: "running" | "succeeded" | "attention" | "failed";
20
+ error?: string;
21
+ } | {
22
+ state: string;
23
+ error: string;
24
+ requestId: string;
25
+ taskId: string;
26
+ roleId: string;
27
+ };
28
+ export declare function requestMemberAddition(input: {
29
+ taskId: string;
30
+ request: AddMemberRequest;
31
+ cfg: FleetConfig;
32
+ cowork: CoworkAdapter;
33
+ configPath?: string;
34
+ binPath: string;
35
+ provision?: typeof provisionMembers;
36
+ }): Promise<{
37
+ requestId: string;
38
+ taskId: string;
39
+ roleId: string;
40
+ state: "running" | "succeeded" | "attention" | "failed";
41
+ error?: string;
42
+ } | {
43
+ state: string;
44
+ error: string;
45
+ requestId: string;
46
+ taskId: string;
47
+ roleId: string;
48
+ }>;
@@ -0,0 +1,83 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { existsSync, mkdirSync, readFileSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { replaceFileAtomically, withFileLock } from '../atomic-file.js';
5
+ import { canonicalJson } from '../canonical-json.js';
6
+ import { stateRoot } from '../paths.js';
7
+ import { getTask, TaskStateError } from './task-state.js';
8
+ import { getRoomRecord } from './room-state.js';
9
+ import { prepareExecutionPlan } from './member-overrides.js';
10
+ import { sealTemplateSnapshot } from './templates.js';
11
+ import { provisionMembers } from './provision.js';
12
+ export class MemberAdditionRejected extends TaskStateError {
13
+ accepted = false;
14
+ }
15
+ const running = new Set();
16
+ function pathFor(taskId, requestId) {
17
+ if (!/^[a-z0-9]{10,40}$/.test(taskId) || !/^[a-zA-Z0-9_-]{8,80}$/.test(requestId))
18
+ throw new TaskStateError('Invalid task or request ID');
19
+ return join(stateRoot(), 'member-additions', taskId, requestId + '.json');
20
+ }
21
+ export function memberAddition(taskId, requestId) {
22
+ getTask(taskId);
23
+ const path = pathFor(taskId, requestId);
24
+ if (!existsSync(path))
25
+ throw new TaskStateError('Member request not found');
26
+ const receipt = JSON.parse(readFileSync(path, 'utf8'));
27
+ const { hash, ...view } = receipt;
28
+ return receipt.state === 'running' && !running.has(path) ? { ...view, state: 'attention', error: 'The server stopped observing this launch. Inspect the existing agent before any recovery.' } : view;
29
+ }
30
+ export async function requestMemberAddition(input) {
31
+ const { request: r, taskId } = input;
32
+ if (!r || typeof r.slot !== 'string' || !/^[A-Za-z][A-Za-z0-9_-]{0,31}$/.test(r.slot)
33
+ || ![r.role, r.brain, r.agentTemplate].every(v => typeof v === 'string' && v.length > 0 && v.length <= 128))
34
+ // No receipt lookup has happened yet: this request ID may already be accepted.
35
+ throw new TaskStateError('Slot, Role, Brain and Agent Template are required');
36
+ const path = pathFor(taskId, r.requestId);
37
+ const hash = createHash('sha256').update(canonicalJson(r)).digest('hex');
38
+ mkdirSync(join(stateRoot(), 'member-additions', taskId), { recursive: true, mode: 0o700 });
39
+ return withFileLock(path + '.lock', async () => {
40
+ if (existsSync(path)) {
41
+ const old = JSON.parse(readFileSync(path, 'utf8'));
42
+ if (old.hash !== hash)
43
+ throw new TaskStateError('Request ID was already used with different settings');
44
+ return memberAddition(taskId, r.requestId);
45
+ }
46
+ const task = getTask(taskId);
47
+ const room = task.room_id && getRoomRecord(task.room_id);
48
+ if (!room || !['active', 'review'].includes(task.state) || task.terminal_intent || room.state !== 'active'
49
+ || room.task_id !== taskId || room.room_identity_cid !== task.room_identity_cid)
50
+ throw new MemberAdditionRejected('Adding an agent requires an active task room');
51
+ const roleId = `${taskId.slice(0, 8)}-${r.slot}-1`;
52
+ if (room.member_seats.some(s => s.role_name === roleId))
53
+ throw new MemberAdditionRejected('This member slot already exists');
54
+ const plan = (() => {
55
+ try {
56
+ return prepareExecutionPlan({ name: 'additional-member', version: 1, description: 'Additional task participant',
57
+ contract: room.template_snapshot?.contract, members: [{ slot: r.slot, role: r.role, count: 1, agent_template: r.agentTemplate }] }, input.cfg, { [r.slot]: { role: r.role, brain: r.brain } });
58
+ }
59
+ catch (error) {
60
+ throw new MemberAdditionRejected(error instanceof Error ? error.message : 'Invalid member configuration');
61
+ }
62
+ })();
63
+ const template = sealTemplateSnapshot(plan.snapshot, input.cfg.agentTemplates ?? {}, plan.launchDefinitions);
64
+ const receipt = { taskId, requestId: r.requestId, roleId, hash, state: 'running' };
65
+ const save = () => replaceFileAtomically(path, JSON.stringify(receipt) + '\n', 0o600);
66
+ save();
67
+ running.add(path);
68
+ void (input.provision ?? provisionMembers)({ cfg: input.cfg, cowork: input.cowork, configPath: input.configPath, binPath: input.binPath,
69
+ taskId, roomId: room.room_id, template, goal: task.title, brief: task.brief, append: true }).then(result => {
70
+ const seat = result.member_seats.find(s => s.role_name === roleId);
71
+ receipt.state = seat?.seat_state === 'active' && getTask(taskId).member_roles.some(m => m.name === roleId) ? 'succeeded' : 'attention';
72
+ if (receipt.state === 'attention')
73
+ receipt.error = 'Admission is not confirmed. Inspect the existing launch; no automatic retry was made.';
74
+ }).catch(() => { receipt.state = 'failed'; receipt.error = 'Member launch failed. Inspect the task and agent logs before recovery.'; })
75
+ .finally(() => { try {
76
+ save();
77
+ }
78
+ finally {
79
+ running.delete(path);
80
+ } }).catch(() => { running.delete(path); });
81
+ return memberAddition(taskId, r.requestId);
82
+ });
83
+ }
@@ -0,0 +1,8 @@
1
+ import type { ArchivedMemberAbsence, RoomMemberSeat } from './types.js';
2
+ /** Revalidate live facts; a persisted timestamp is never cleanup authority. */
3
+ export declare function verifyArchivedAbsence(proof: ArchivedMemberAbsence): Promise<void>;
4
+ /** After the verified archive has been consumed by explicit workspace deletion,
5
+ * still fence replacement live state and a recreated daemon identity on every retry.
6
+ */
7
+ export declare function verifyArchivedMemberStillAbsent(proof: ArchivedMemberAbsence): Promise<void>;
8
+ export declare function proveArchivedAbsence(seat: RoomMemberSeat): Promise<ArchivedMemberAbsence>;
@@ -0,0 +1,48 @@
1
+ import { existsSync, readFileSync } from 'node:fs';
2
+ import { join } from 'node:path';
3
+ import { agentDir } from '../paths.js';
4
+ import { tempArchiveForLaunch, tempArchiveForCreationAction, tempSupervisorLiveness } from '../temp-lifecycle.js';
5
+ import { assertMemberIdentityAbsent } from './close.js';
6
+ /** Revalidate live facts; a persisted timestamp is never cleanup authority. */
7
+ export async function verifyArchivedAbsence(proof) {
8
+ const absent = () => {
9
+ if (existsSync(agentDir(proof.name, true)))
10
+ throw new Error('Archived member has replacement live state');
11
+ };
12
+ absent();
13
+ const archive = tempArchiveForLaunch(proof.name, proof.launch_id);
14
+ const action = tempArchiveForCreationAction(proof.name, proof.action_id);
15
+ if (!archive || archive !== proof.archive_path || action?.path !== archive
16
+ || action.launchId !== proof.launch_id
17
+ || readFileSync(join(archive, '.identity'), 'utf8').trim() !== proof.name)
18
+ throw new Error('Archived member absence ownership proof mismatch');
19
+ if (await tempSupervisorLiveness(archive) !== 'stopped')
20
+ throw new Error('Archived member supervisor absence is not proven');
21
+ await verifyArchivedMemberStillAbsent(proof);
22
+ }
23
+ /** After the verified archive has been consumed by explicit workspace deletion,
24
+ * still fence replacement live state and a recreated daemon identity on every retry.
25
+ */
26
+ export async function verifyArchivedMemberStillAbsent(proof) {
27
+ const absent = () => {
28
+ if (existsSync(agentDir(proof.name, true)))
29
+ throw new Error('Archived member has replacement live state');
30
+ };
31
+ absent();
32
+ await assertMemberIdentityAbsent({
33
+ role_name: proof.name, slot: 'archived', cowork_role: 'archived', seat_state: 'removed',
34
+ });
35
+ absent();
36
+ }
37
+ export async function proveArchivedAbsence(seat) {
38
+ if (seat.identity_cid || seat.retirement?.phase !== 'identity_absent'
39
+ || !seat.launch?.launch_id || !seat.launch.action_id
40
+ || seat.launch.launch_id !== seat.retirement.launch_id || !seat.retirement.archive_path)
41
+ throw new Error('Missing exact archived member absence evidence');
42
+ const proof = {
43
+ name: seat.role_name, launch_id: seat.launch.launch_id, action_id: seat.launch.action_id,
44
+ archive_path: seat.retirement.archive_path, checked_at: new Date().toISOString(),
45
+ };
46
+ await verifyArchivedAbsence(proof);
47
+ return proof;
48
+ }
@@ -75,6 +75,11 @@ async function presentDetachedProvisioningOutcome(outcome) {
75
75
  function taskRoomPublicFailure(error) {
76
76
  const f = error.fields;
77
77
  switch (error.code) {
78
+ case 'task_not_ready': return {
79
+ legacy: `task readiness ${f.readiness}: ${f.reason}`, kind: 'state',
80
+ detail: `Task ${f.task} readiness is ${f.readiness}: ${f.reason}${f.member ? ` (${f.member})` : ''}.`,
81
+ action: 'Ask Fleet Coordinator to inspect room and member status and choose supported recovery. Do not blindly relaunch agents or reuse invites.',
82
+ };
78
83
  case 'task_confirmation_mismatch': return {
79
84
  legacy: 'confirmation ID must match task ID', kind: 'usage',
80
85
  detail: 'The two task IDs must match.', action: 'Repeat the same task ID twice.',
@@ -242,6 +247,8 @@ const taskListMarkdown = (tasks, title = 'Tasks') => renderMarkdownList({
242
247
  icon: '📋', title, empty: 'No tasks found.',
243
248
  records: tasks.map(t => `${taskStatus(t.state)} ${markdownCode(t.task_id)} — ${markdownProse(t.title)}`
244
249
  + ` — List ${markdownCode(t.list_name ?? 'default')}`
250
+ + (t.deletion?.status === 'pending' ? ' — Deletion pending'
251
+ + (t.deletion.error ? `: ${markdownProse(t.deletion.error)}` : '') : '')
245
252
  + (t.blocked ? ` — 🚧 Blocked: ${markdownProse(t.blocked.reason)}` : '')),
246
253
  });
247
254
  const taskActionMarkdown = (title, task, fields = []) => {
@@ -515,10 +522,10 @@ function taskRoomService(configuration) {
515
522
  function allTemplates(cfg) {
516
523
  return cfg.roomTemplates ?? {};
517
524
  }
518
- function coworkFor(cfg) {
525
+ function coworkFor(cfg, options) {
519
526
  if (!cfg.rooms)
520
527
  throw new ConfigError('rooms: configuration is required before creating or querying rooms');
521
- return createCoworkAdapter({ configPath: cfg.rooms.cowork?.config });
528
+ return createCoworkAdapter({ configPath: cfg.rooms.cowork?.config, ...options });
522
529
  }
523
530
  function resolveRoomTemplate(cfg, name) {
524
531
  if (!name)
@@ -709,6 +716,7 @@ export function registerTaskCommands(parent, cOpt) {
709
716
  .option('--state <state>', 'filter by state (backlog|active|provisioning|review|done|cancelled|failed|all)')
710
717
  .option('--list <name>', 'filter by task list')
711
718
  .option('--group-by-list', 'group deterministic results by list (JSON)')
719
+ .option('--include-deleting', 'include tasks whose accepted deletion is still pending')
712
720
  .option('--json', 'JSON output')
713
721
  .action(async (opts) => {
714
722
  try {
@@ -717,7 +725,8 @@ export function registerTaskCommands(parent, cOpt) {
717
725
  stateFilter = opts.state;
718
726
  }
719
727
  const service = taskRoomService(opts.configuration);
720
- const filter = { ...(stateFilter ? { state: stateFilter } : {}), ...(opts.list ? { list: opts.list } : {}) };
728
+ const filter = { ...(stateFilter ? { state: stateFilter } : {}), ...(opts.list ? { list: opts.list } : {}),
729
+ ...(opts.includeDeleting ? { includeDeleting: true } : {}) };
721
730
  const tasks = service.listTasks(filter);
722
731
  if (opts.json) {
723
732
  console.log(JSON.stringify(opts.groupByList
@@ -827,16 +836,19 @@ export function registerTaskCommands(parent, cOpt) {
827
836
  dieTaskRoom(e);
828
837
  }
829
838
  });
830
- taskCmd.command('show <id>')
839
+ cOpt(taskCmd.command('show <id>'))
831
840
  .description('show task details')
832
841
  .option('--json', 'JSON output')
833
842
  .action(async (id, opts) => {
834
843
  try {
835
- const service = taskRoomService();
844
+ const service = taskRoomService(opts.configuration);
836
845
  const { task: t, orchestration: room } = service.getTask(id);
846
+ const readiness = t.state === 'active' && t.deletion?.status !== 'pending'
847
+ ? await service.observeTaskReadiness(id) : undefined;
837
848
  if (opts.json) {
838
849
  console.log(JSON.stringify({
839
850
  schema_version: 1, task: t, orchestration: room ?? null,
851
+ ...(readiness ? { readiness } : {}),
840
852
  }, null, 2));
841
853
  return;
842
854
  }
@@ -846,6 +858,13 @@ export function registerTaskCommands(parent, cOpt) {
846
858
  { label: 'ID', value: t.task_id, kind: 'code' },
847
859
  { label: 'Title', value: t.title },
848
860
  { label: 'Status', value: taskStatus(t.state), kind: 'markdown' },
861
+ ...(t.deletion?.status === 'pending' ? [
862
+ { label: 'Deletion', value: 'Pending — cleanup is not complete' },
863
+ ...(t.deletion.error ? [{ label: 'Cleanup error', value: t.deletion.error }] : []),
864
+ { label: 'Retry', value: `ours-fleet task delete ${t.task_id} ${t.task_id}`, kind: 'code' },
865
+ ] : []),
866
+ ...(readiness ? [{ label: 'Readiness', value: readiness.state },
867
+ ...('reason' in readiness ? [{ label: 'Reason', value: readiness.reason }] : [])] : []),
849
868
  ...(t.blocked ? [{ label: 'Blocked', value: t.blocked.reason }] : []),
850
869
  ...(t.template ? [{ label: 'Template', value: `${t.template.name}@${t.template.version}`, kind: 'code' }] : []),
851
870
  ...(t.room_id ? [{ label: 'Room', value: t.room_id, kind: 'code' }] : []),
@@ -872,6 +891,27 @@ export function registerTaskCommands(parent, cOpt) {
872
891
  dieTaskRoom(e);
873
892
  }
874
893
  });
894
+ cOpt(taskCmd.command('recover-members <id>'))
895
+ .description('resume proven stopped task members in-place; never create identities or replay invites')
896
+ .option('--json', 'JSON output')
897
+ .action(async (id, opts) => {
898
+ try {
899
+ const { recoverTaskMembers } = await import('./recovery.js');
900
+ const task = getTask(id), cfg = loadConfig(opts.configuration);
901
+ const members = await recoverTaskMembers({ taskId: id, binPath: getBinPath(),
902
+ cowork: task.layout ? undefined : createCoworkAdapter({ configPath: cfg.rooms?.cowork?.config }) });
903
+ if (opts.json)
904
+ console.log(JSON.stringify({ schema_version: 1, task_id: id, members }, null, 2));
905
+ else
906
+ for (const member of members)
907
+ console.log(`${member.name}: ${member.status}`);
908
+ }
909
+ catch (e) {
910
+ if (opts.json)
911
+ die(e);
912
+ dieTaskRoom(e);
913
+ }
914
+ });
875
915
  cOpt(taskCmd.command('start <id>'))
876
916
  .description('idempotently select a plan, provision, and start a task')
877
917
  .option('--template <name>', 'room template')
@@ -1176,6 +1216,26 @@ export function registerTaskCommands(parent, cOpt) {
1176
1216
  dieTaskRoom(e);
1177
1217
  }
1178
1218
  });
1219
+ cOpt(taskCmd.command('_layout <id> <operation> [room]', { hidden: true }))
1220
+ .description('internal: run an accepted layout room operation (open, close-room, close)')
1221
+ .option('--json', 'JSON output')
1222
+ .option('--operation-id <id>', 'accepted operation ID; status updates apply only while it is current')
1223
+ .action(async (id, operation, room, opts) => {
1224
+ try {
1225
+ if (!['open', 'close-room', 'close'].includes(operation))
1226
+ throw new Error(`unknown layout operation: ${operation}`);
1227
+ const view = await taskRoomService(opts.configuration).runTaskLayoutOperation({
1228
+ actor: { kind: 'internal_worker', surface: 'cli' }, taskId: id,
1229
+ operation: operation, room, operationId: opts.operationId,
1230
+ });
1231
+ console.log(JSON.stringify({ schema_version: 1, layout: view }, null, 2));
1232
+ }
1233
+ catch (e) {
1234
+ if (opts.json)
1235
+ die(e);
1236
+ dieTaskRoom(e);
1237
+ }
1238
+ });
1179
1239
  cOpt(taskCmd.command('_settle_delete <id>', { hidden: true }))
1180
1240
  .description('internal: settle a previously accepted task deletion')
1181
1241
  .option('--json', 'JSON output')
@@ -1,9 +1,12 @@
1
+ import { retireTaskMemberService } from './task-service-retirement.js';
1
2
  import { withFileLock } from '../atomic-file.js';
2
3
  import { type TempLifecycleDeps } from '../temp-lifecycle.js';
3
4
  import { type CoworkAdapter } from './cowork-adapter.js';
4
5
  import type { RoomMemberSeat, RoomOrchestrationRecord } from './types.js';
6
+ export declare const CLOSE_LOCK_STALE_MS: number;
5
7
  export declare function roomCloseLockPath(roomId: string): string;
6
8
  export interface RoomCloseDeps {
9
+ retireTaskService?: typeof retireTaskMemberService;
7
10
  inspectMember?(seat: RoomMemberSeat): Promise<{
8
11
  launchId: string;
9
12
  }>;
@@ -20,12 +23,13 @@ export declare function waitForLivenessAbsent(role: string, launchId: string, li
20
23
  /** Report whether any daemon identity — under any name — carries this exact CID. */
21
24
  export declare function identityCidPresent(cid: string): Promise<boolean>;
22
25
  export declare function removeExactMemberIdentity(seat: RoomMemberSeat): Promise<void>;
26
+ export declare function assertMemberIdentityAbsent(seat: RoomMemberSeat): Promise<void>;
23
27
  /** One forward-only room close saga shared by every Fleet entry point. */
24
28
  export declare function acceptManagedRoomClose(roomId: string): Promise<RoomOrchestrationRecord>;
25
29
  export declare function recordManagedRoomCloseError(roomId: string, error: string, recoveryHint: string): Promise<RoomOrchestrationRecord>;
26
30
  export declare function closeManagedRoom(input: {
27
31
  roomId: string;
28
- cowork: Pick<CoworkAdapter, 'closeRoom'>;
32
+ cowork: Pick<CoworkAdapter, 'closeRoom'> & Partial<Pick<CoworkAdapter, 'getRoom'>>;
29
33
  deps?: RoomCloseDeps;
30
34
  }): Promise<RoomOrchestrationRecord>;
31
35
  export interface ManagedRoomDeleteResult {
@@ -39,6 +43,6 @@ export declare function deleteLegacyClosedRooms(input: {
39
43
  /** Retire live resources through the existing cursor, then delete retained state. */
40
44
  export declare function deleteManagedRoom(input: {
41
45
  roomId: string;
42
- cowork: Pick<CoworkAdapter, 'closeRoom' | 'deleteRoom'>;
46
+ cowork: Pick<CoworkAdapter, 'closeRoom' | 'deleteRoom'> & Partial<Pick<CoworkAdapter, 'getRoom'>>;
43
47
  deps?: RoomCloseDeps;
44
48
  }): Promise<ManagedRoomDeleteResult>;