@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
@@ -1,14 +1,23 @@
1
- import { existsSync, readFileSync } from 'node:fs';
1
+ import { detachDeletedRoom } from './task-state.js';
2
+ import { binderKey } from '../agent-ours/state.js';
3
+ import { assertMemberNotPermanent } from './member-ownership.js';
4
+ import { retireTaskMemberService } from './task-service-retirement.js';
5
+ import { eraseMemberArtifacts } from './erasure.js';
6
+ import { deleteWorkspace, assertWorkspaceDeletable } from './workspace.js';
7
+ import { collectWorkspaceArchives } from './workspace-artifacts.js';
8
+ import { existsSync, lstatSync, readFileSync } from 'node:fs';
2
9
  import { join } from 'node:path';
3
10
  import { randomUUID } from 'node:crypto';
11
+ import { parse } from 'yaml';
12
+ import { privateRuntimeRoot, releaseManagedAgent } from '../agent-ours/service.js';
4
13
  import { attachOursClient } from '@ours.network/sdk/client';
5
14
  import { withFileLock } from '../atomic-file.js';
6
15
  import { agentDir, stateRoot } from '../paths.js';
7
16
  import { readClientProfile } from '../client-profile.js';
8
- import { readTempSupervisor, secureStoppedTempArchive, stopTempSupervisor, tempSupervisorLiveness, } from '../temp-lifecycle.js';
17
+ import { readTempSupervisor, secureStoppedTempArchive, stopTempSupervisor, tempSupervisorLiveness, tempArchiveForLaunch, tempArchiveForCreationAction, } from '../temp-lifecycle.js';
9
18
  import { CoworkProtocolError } from './cowork-adapter.js';
10
- import { advanceMemberRetirement, advanceRoomClose, beginRoomClose, closeRoom, deleteRoomRecord, getRoomRecord, listRoomRecords, setRoomCloseError, } from './room-state.js';
11
- const CLOSE_LOCK_STALE_MS = 5 * 60_000;
19
+ import { advanceMemberRetirement, advanceRoomClose, beginRoomClose, closeRoom, deleteRoomRecord, getRoomRecord, listRoomRecords, setRoomCloseError, recordRetirementMemberCids, } from './room-state.js';
20
+ export const CLOSE_LOCK_STALE_MS = 5 * 60_000;
12
21
  const STOP_POLLS = 50;
13
22
  const STOP_POLL_MS = 100;
14
23
  export function roomCloseLockPath(roomId) {
@@ -20,6 +29,20 @@ function errorText(error) {
20
29
  function exactMemberIdentity(seat) {
21
30
  const dir = agentDir(seat.role_name, true);
22
31
  const identityPath = join(dir, '.identity');
32
+ if (existsSync(dir) && !existsSync(identityPath) && seat.launch?.action_id && seat.launch.launch_id) {
33
+ const supervisor = readTempSupervisor(dir);
34
+ let creation;
35
+ try {
36
+ creation = JSON.parse(readFileSync(join(dir, 'creation.json'), 'utf8'));
37
+ }
38
+ catch (error) {
39
+ if (error.code !== 'ENOENT')
40
+ throw error;
41
+ }
42
+ if (supervisor?.role === seat.role_name && supervisor.launchId === seat.launch.launch_id
43
+ && creation?.role === seat.role_name && creation.creationActionId === seat.launch.action_id)
44
+ return;
45
+ }
23
46
  if (!existsSync(dir) || !existsSync(identityPath)) {
24
47
  throw new Error(`room member '${seat.role_name}' has no live Fleet temp-state identity proof; refusing retirement`);
25
48
  }
@@ -29,9 +52,11 @@ function exactMemberIdentity(seat) {
29
52
  }
30
53
  }
31
54
  export async function inspectMember(seat) {
55
+ assertMemberNotPermanent(seat.role_name);
32
56
  exactMemberIdentity(seat);
33
57
  const supervisor = readTempSupervisor(agentDir(seat.role_name, true));
34
- if (!supervisor || supervisor.role !== seat.role_name) {
58
+ if (!supervisor || supervisor.role !== seat.role_name
59
+ || (seat.launch?.launch_id && supervisor.launchId !== seat.launch.launch_id)) {
35
60
  throw new Error(`room member '${seat.role_name}' has no exact Fleet supervisor ownership proof`);
36
61
  }
37
62
  return { launchId: supervisor.launchId };
@@ -57,15 +82,11 @@ export async function waitForLivenessAbsent(role, launchId, lifecycleDeps = {})
57
82
  async function withIdentityClient(work) {
58
83
  const profile = readClientProfile(process.env);
59
84
  const leaseToken = `ours-fleet-room-close-${process.pid}-${randomUUID()}`;
60
- const client = await attachOursClient(profile ? {
85
+ const client = await attachOursClient({
61
86
  endpoint: profile.endpoint,
62
87
  expectedInstanceId: profile.expectedInstanceId,
63
88
  credentialPath: profile.credentialPath,
64
89
  sessionMode: 'external', env: {}, leaseToken,
65
- } : {
66
- env: process.env,
67
- leaseToken,
68
- clientPid: process.pid,
69
90
  });
70
91
  try {
71
92
  return await work(client);
@@ -89,18 +110,69 @@ export async function identityCidPresent(cid) {
89
110
  return rows.some(row => 'cid' in row && row.cid.toLowerCase() === cid.toLowerCase());
90
111
  });
91
112
  }
113
+ /** A managed temporary identity belongs to its durable external lease even
114
+ * after worker exit. Retire that exact logical owner before generic removal. */
115
+ async function releaseRetainedMemberRuntime(seat) {
116
+ const live = agentDir(seat.role_name, true);
117
+ const source = existsSync(live) ? live
118
+ : (seat.launch?.launch_id ? tempArchiveForLaunch(seat.role_name, seat.launch.launch_id) : undefined);
119
+ if (!source) {
120
+ if (seat.launch?.task_supervised)
121
+ throw Error('MEMBER_RUNTIME_RETIREMENT_SOURCE_MISSING');
122
+ return;
123
+ }
124
+ const path = join(source, 'role.yaml');
125
+ if (!existsSync(path))
126
+ return; // pre-managed compatibility evidence has no runtime
127
+ const stat = lstatSync(path);
128
+ if (!stat.isFile() || stat.isSymbolicLink() || stat.size > 256 * 1024)
129
+ throw Error('MEMBER_RUNTIME_UNSAFE_PROOF');
130
+ const role = parse(readFileSync(path, 'utf8'));
131
+ const profile = readClientProfile({ ...process.env, ...role.env });
132
+ const runtime = join(privateRuntimeRoot(), binderKey(profile.expectedInstanceId, seat.role_name));
133
+ const metadata = readTempSupervisor(source);
134
+ if (!existsSync(join(runtime, 'instance.json'))) {
135
+ if (metadata?.kind === 'fleet-managed')
136
+ throw Error('MEMBER_RUNTIME_RETIREMENT_MISSING');
137
+ return;
138
+ }
139
+ const creationPath = join(source, 'creation.json'), creationStat = lstatSync(creationPath);
140
+ if (!creationStat.isFile() || creationStat.isSymbolicLink() || creationStat.size > 256 * 1024)
141
+ throw Error('MEMBER_RUNTIME_UNSAFE_PROOF');
142
+ const creation = JSON.parse(readFileSync(creationPath, 'utf8'));
143
+ if (role.name !== seat.role_name || role.identity !== seat.role_name || !seat.identity_cid
144
+ || !seat.launch?.action_id || !seat.launch.launch_id || creation.role !== seat.role_name
145
+ || creation.creationActionId !== seat.launch.action_id || metadata?.role !== seat.role_name
146
+ || metadata.launchId !== seat.launch.launch_id
147
+ || (metadata.taskOwner && metadata.taskOwner.creationActionId !== seat.launch.action_id))
148
+ throw Error('MEMBER_RUNTIME_RETIREMENT_MISMATCH');
149
+ await releaseManagedAgent(role, { cid: seat.identity_cid, action: seat.launch.action_id });
150
+ }
92
151
  export async function removeExactMemberIdentity(seat) {
152
+ assertMemberNotPermanent(seat.role_name);
93
153
  await withIdentityClient(async (client) => {
94
- const before = listedIdentity(await client.listIdentities(), seat.role_name);
95
- if (!before)
154
+ const rows = await client.listIdentities();
155
+ const before = listedIdentity(rows, seat.role_name);
156
+ if (!before) {
157
+ if (rows.some(row => row.name === seat.role_name || (seat.identity_cid && 'cid' in row
158
+ && row.cid.toLowerCase() === seat.identity_cid.toLowerCase())))
159
+ throw new Error(`room member '${seat.role_name}' identity absence is not proven`);
160
+ // A terminal release can remove the SDK identity before its response is
161
+ // recorded. Finish the exact retained launch's release journal on retry.
162
+ if (existsSync(agentDir(seat.role_name, true)) || (seat.launch?.launch_id
163
+ && tempArchiveForLaunch(seat.role_name, seat.launch.launch_id)))
164
+ await releaseRetainedMemberRuntime(seat);
96
165
  return;
166
+ }
97
167
  if (!seat.identity_cid) {
98
- throw new Error(`room member '${seat.role_name}' exists without a recorded authenticated CID; refusing removal`);
168
+ throw new Error(`room member '${seat.role_name}' exists without a recorded authenticated CID; identity absence is not proven; refusing removal`);
99
169
  }
100
170
  if (before.cid?.toLowerCase() !== seat.identity_cid.toLowerCase()) {
101
- throw new Error(`room member '${seat.role_name}' identity CID mismatch: recorded ${seat.identity_cid}, found ${before.cid ?? 'none'}`);
171
+ throw new Error(`room member '${seat.role_name}' identity absence is not proven: CID mismatch: recorded ${seat.identity_cid}, found ${before.cid ?? 'none'}`);
102
172
  }
173
+ await releaseRetainedMemberRuntime(seat);
103
174
  try {
175
+ assertMemberNotPermanent(seat.role_name);
104
176
  await client.removeIdentity({ name: seat.role_name });
105
177
  }
106
178
  catch (error) {
@@ -113,13 +185,14 @@ export async function removeExactMemberIdentity(seat) {
113
185
  if (after)
114
186
  throw error;
115
187
  }
116
- const after = listedIdentity(await client.listIdentities(), seat.role_name);
117
- if (after) {
188
+ const after = await client.listIdentities();
189
+ if (after.some(row => row.name === seat.role_name || (seat.identity_cid && 'cid' in row
190
+ && row.cid.toLowerCase() === seat.identity_cid.toLowerCase()))) {
118
191
  throw new Error(`room member '${seat.role_name}' identity still exists after remove_identity`);
119
192
  }
120
193
  });
121
194
  }
122
- async function assertMemberIdentityAbsent(seat) {
195
+ export async function assertMemberIdentityAbsent(seat) {
123
196
  await withIdentityClient(async (client) => {
124
197
  const rows = await client.listIdentities();
125
198
  if (rows.some(row => row.name === seat.role_name ||
@@ -128,26 +201,52 @@ async function assertMemberIdentityAbsent(seat) {
128
201
  }
129
202
  });
130
203
  }
204
+ async function retireMemberRegistration(room, current, deps) {
205
+ if (room.task_id)
206
+ await (deps.retireTaskService ?? retireTaskMemberService)(current.role_name, {
207
+ taskId: room.task_id, creationActionId: current.launch?.action_id,
208
+ taskSupervised: current.launch?.task_supervised,
209
+ ...(current.launch?.launch_id ? { launchId: current.launch.launch_id } : {}),
210
+ });
211
+ }
131
212
  async function retireMember(roomId, seat, deps) {
213
+ assertMemberNotPermanent(seat.role_name);
132
214
  let room = getRoomRecord(roomId);
133
215
  let current = room.member_seats.find(candidate => candidate.role_name === seat.role_name);
216
+ await retireMemberRegistration(room, current, deps);
134
217
  let retirement = current.retirement;
135
218
  if (retirement?.phase === 'identity_absent') {
136
- if (current.launch?.launch_id && current.launch.launch_id !== retirement.launch_id) {
219
+ if (!deps.inspectMember) {
137
220
  if (existsSync(agentDir(current.role_name, true)))
138
- throw new Error(`room member '${current.role_name}' has a replacement launch after retirement; retire that exact temporary role before retrying`);
221
+ throw new Error('Retired member has replacement launch live state');
139
222
  await assertMemberIdentityAbsent(current);
140
223
  }
141
224
  return;
142
225
  }
143
226
  if (!retirement) {
144
- if (current.launch?.state === 'failed' && !existsSync(agentDir(current.role_name, true))) {
145
- // A failure before applyRole (for example invite-secret validation) has
146
- // no supervisor to stop or archive. Settle only proven absence; this
147
- // path never removes an identity based on missing local evidence.
148
- await assertMemberIdentityAbsent(current);
149
- advanceMemberRetirement(roomId, current.role_name, 'identity_absent', 'failed-launch-absent');
150
- return;
227
+ if (!existsSync(agentDir(current.role_name, true)) && current.launch?.launch_id && current.launch.action_id) {
228
+ const archived = tempArchiveForLaunch(current.role_name, current.launch.launch_id);
229
+ const created = tempArchiveForCreationAction(current.role_name, current.launch.action_id);
230
+ if (archived && (!created || created.path !== archived || created.launchId !== current.launch.launch_id))
231
+ throw new Error('Archived member creation ownership mismatch');
232
+ if (archived && created?.path === archived && created.launchId === current.launch.launch_id) {
233
+ if (readFileSync(join(archived, '.identity'), 'utf8').trim() !== current.role_name)
234
+ throw new Error(`room member '${current.role_name}' archive identity mismatch`);
235
+ if (await tempSupervisorLiveness(archived) !== 'stopped')
236
+ throw new Error(`room member '${current.role_name}' archived supervisor is not proven stopped`);
237
+ // A terminated launch can archive itself before room retirement begins.
238
+ // Accept its exact durable provenance only when no identity needs removal.
239
+ await (deps.removeIdentity ?? removeExactMemberIdentity)(current);
240
+ await assertMemberIdentityAbsent(current);
241
+ const latest = getRoomRecord(roomId)?.member_seats.find(seat => seat.role_name === current.role_name);
242
+ if (existsSync(agentDir(current.role_name, true))
243
+ || latest?.launch?.launch_id !== current.launch.launch_id
244
+ || latest?.launch?.action_id !== current.launch.action_id
245
+ || latest?.identity_cid !== current.identity_cid)
246
+ throw new Error(`room member '${current.role_name}' changed during archived retirement proof`);
247
+ advanceMemberRetirement(roomId, current.role_name, 'identity_absent', current.launch.launch_id, archived);
248
+ return;
249
+ }
151
250
  }
152
251
  if (current.launch?.state === 'pending' && current.launch.attempt === 0) {
153
252
  if (existsSync(agentDir(current.role_name, true))) {
@@ -157,12 +256,31 @@ async function retireMember(roomId, seat, deps) {
157
256
  advanceMemberRetirement(roomId, current.role_name, 'identity_absent', 'never-launched');
158
257
  return;
159
258
  }
259
+ if (!deps.inspectMember && !existsSync(agentDir(current.role_name, true))) {
260
+ await (deps.removeIdentity ?? removeExactMemberIdentity)(current);
261
+ if (existsSync(agentDir(current.role_name, true)))
262
+ throw new Error(`room member '${current.role_name}' acquired replacement state during retirement`);
263
+ advanceMemberRetirement(roomId, current.role_name, 'identity_absent', 'absent-verified');
264
+ return;
265
+ }
160
266
  const ownership = await (deps.inspectMember ?? inspectMember)(current);
161
267
  room = advanceMemberRetirement(roomId, current.role_name, 'stop_requested', ownership.launchId);
162
268
  current = room.member_seats.find(candidate => candidate.role_name === seat.role_name);
163
269
  retirement = current.retirement;
164
270
  }
271
+ if (['stop_requested', 'liveness_absent', 'archive_secured'].includes(retirement.phase) && !deps.requestStop
272
+ && !existsSync(agentDir(current.role_name, true))) {
273
+ const archive = tempArchiveForLaunch(current.role_name, retirement.launch_id);
274
+ if (archive && await tempSupervisorLiveness(archive) !== 'stopped')
275
+ throw new Error('Archived supervisor is not proven stopped');
276
+ await (deps.removeIdentity ?? removeExactMemberIdentity)(current);
277
+ await assertMemberIdentityAbsent(current);
278
+ advanceMemberRetirement(roomId, current.role_name, 'identity_absent', retirement.launch_id, archive, !archive);
279
+ return;
280
+ }
165
281
  if (retirement.phase === 'stop_requested') {
282
+ if (!deps.inspectMember && (await inspectMember(current)).launchId !== retirement.launch_id)
283
+ throw new Error('Member launch changed before stop');
166
284
  await (deps.requestStop ?? (async (role) => { await stopTempSupervisor(role); }))(current.role_name);
167
285
  await (deps.waitForLivenessAbsent ?? waitForLivenessAbsent)(current.role_name, retirement.launch_id);
168
286
  room = advanceMemberRetirement(roomId, current.role_name, 'liveness_absent', retirement.launch_id);
@@ -192,17 +310,35 @@ export async function closeManagedRoom(input) {
192
310
  const lock = deps.lock ?? withFileLock;
193
311
  return lock(roomCloseLockPath(input.roomId), async () => {
194
312
  let room = beginRoomClose(input.roomId);
195
- if (room.state === 'closed')
313
+ if (room.state === 'closed') {
314
+ for (const seat of room.member_seats) {
315
+ assertMemberNotPermanent(seat.role_name);
316
+ await retireMemberRegistration(room, seat, deps);
317
+ }
318
+ if (!deps.inspectMember)
319
+ for (const seat of room.member_seats) {
320
+ if (existsSync(agentDir(seat.role_name, true)))
321
+ throw new Error('Closed room has replacement live state');
322
+ await assertMemberIdentityAbsent(seat);
323
+ }
196
324
  return room;
325
+ }
197
326
  try {
198
327
  if (room.close?.phase === 'retire_members') {
328
+ room = await recoverMemberCids(room, input.cowork);
199
329
  for (const seat of room.member_seats) {
200
330
  await retireMember(input.roomId, seat, deps);
201
331
  }
202
332
  room = advanceRoomClose(input.roomId, 'close_cowork');
203
333
  }
204
334
  if (room.close?.phase === 'close_cowork') {
205
- await input.cowork.closeRoom(input.roomId);
335
+ try {
336
+ await input.cowork.closeRoom(input.roomId);
337
+ }
338
+ catch (error) {
339
+ if (!(error instanceof CoworkProtocolError && error.code === 'not_found'))
340
+ throw error;
341
+ }
206
342
  }
207
343
  return closeRoom(input.roomId);
208
344
  }
@@ -216,6 +352,9 @@ export async function closeManagedRoom(input) {
216
352
  export async function deleteLegacyClosedRooms(input) {
217
353
  const deleted = [];
218
354
  for (const room of listRoomRecords({ state: 'closed' })) {
355
+ // This compatibility sweep is retirement, never explicit workspace deletion.
356
+ if (room.workspace)
357
+ continue;
219
358
  try {
220
359
  await input.cowork.deleteRoom(room.room_id);
221
360
  }
@@ -232,14 +371,84 @@ export async function deleteLegacyClosedRooms(input) {
232
371
  }
233
372
  /** Retire live resources through the existing cursor, then delete retained state. */
234
373
  export async function deleteManagedRoom(input) {
235
- await closeManagedRoom(input);
236
- try {
237
- await input.cowork.deleteRoom(input.roomId);
238
- }
239
- catch (error) {
240
- setRoomCloseError(input.roomId, errorText(error), `Retry 'ours-fleet room delete ${input.roomId} ${input.roomId}'.`);
241
- throw error;
374
+ if (getRoomRecord(input.roomId))
375
+ await closeManagedRoom(input);
376
+ return withFileLock(roomCloseLockPath(input.roomId), async () => {
377
+ try {
378
+ try {
379
+ await input.cowork.deleteRoom(input.roomId);
380
+ }
381
+ catch (error) {
382
+ // An earlier explicit delete may have removed the remote room before
383
+ // local filesystem cleanup failed. Never restore it or repeat creation.
384
+ if (!(error instanceof CoworkProtocolError && error.code === 'not_found'))
385
+ throw error;
386
+ }
387
+ const retained = getRoomRecord(input.roomId);
388
+ if (retained?.workspace && !retained.task_id) {
389
+ assertWorkspaceDeletable(retained.workspace, 'room', input.roomId);
390
+ collectWorkspaceArchives(retained.workspace);
391
+ deleteWorkspace(retained.workspace, 'room', input.roomId);
392
+ }
393
+ if (retained)
394
+ await eraseMemberArtifacts('room', input.roomId, retained.member_seats, [input.roomId]);
395
+ if (retained?.task_id)
396
+ detachDeletedRoom(retained.task_id, input.roomId);
397
+ deleteRoomRecord(input.roomId);
398
+ return { room_id: input.roomId, deleted: true };
399
+ }
400
+ catch (error) {
401
+ if (getRoomRecord(input.roomId))
402
+ setRoomCloseError(input.roomId, errorText(error), `Fix the cleanup error and explicitly retry room delete ${input.roomId} ${input.roomId}.`);
403
+ throw error;
404
+ }
405
+ }, {}, CLOSE_LOCK_STALE_MS);
406
+ }
407
+ /** Recover only an authenticated seat tied to this exact room and issued invite.
408
+ * Display names and the current daemon name inventory are never binding proof.
409
+ */
410
+ async function recoverMemberCids(room, cowork) {
411
+ if (!room.member_seats.some(seat => !seat.identity_cid))
412
+ return room;
413
+ const profile = room.member_seats.some(seat => seat.launch?.action_id) ? readClientProfile(process.env) : undefined;
414
+ let remote;
415
+ if (cowork.getRoom && room.room_identity_cid) {
416
+ try {
417
+ remote = await cowork.getRoom(room.room_id);
418
+ }
419
+ catch (error) {
420
+ if (!(error instanceof CoworkProtocolError && error.code === 'not_found'))
421
+ throw error;
422
+ }
423
+ if (remote && (remote.room_id !== room.room_id || remote.identity_cid.toLowerCase() !== room.room_identity_cid.toLowerCase()))
424
+ throw new Error('Room identity mismatch during deletion recovery');
242
425
  }
243
- deleteRoomRecord(input.roomId);
244
- return { room_id: input.roomId, deleted: true };
426
+ const seats = room.member_seats.map(seat => {
427
+ let runtimeCid;
428
+ if (profile && seat.launch?.action_id) {
429
+ const dir = join(stateRoot(), 'private-ours', binderKey(profile.expectedInstanceId, seat.role_name));
430
+ try {
431
+ const state = JSON.parse(readFileSync(join(dir, 'state.json'), 'utf8'));
432
+ const instance = JSON.parse(readFileSync(join(dir, 'instance.json'), 'utf8'));
433
+ if (state.name === seat.role_name && state.daemon === profile.expectedInstanceId
434
+ && state.lifetime === 'temporary' && state.action === seat.launch.action_id
435
+ && instance.role === seat.role_name && instance.temporary === true && instance.instance === state.instance)
436
+ runtimeCid = state.cid;
437
+ }
438
+ catch (error) {
439
+ if (error.code !== 'ENOENT')
440
+ throw error;
441
+ }
442
+ }
443
+ const matches = seat.invite_id ? remote?.seats.filter(value => value.invite_id === seat.invite_id && value.role === seat.cowork_role) ?? [] : [];
444
+ if (matches.length > 1)
445
+ throw new Error('Ambiguous authenticated member identity during deletion recovery');
446
+ const candidates = [seat.identity_cid, runtimeCid, matches[0]?.identity_cid].filter((v) => !!v);
447
+ if (candidates.some(cid => !/^[a-f0-9]{64}$/i.test(cid)))
448
+ throw new Error('Invalid authenticated member CID');
449
+ if (new Set(candidates.map(cid => cid.toLowerCase())).size > 1)
450
+ throw new Error('Contradictory authenticated member CID evidence');
451
+ return candidates.length ? { ...seat, identity_cid: candidates[0] } : seat;
452
+ });
453
+ return recordRetirementMemberCids(room.room_id, seats);
245
454
  }
@@ -4,7 +4,7 @@ export declare class RoomsTasksConfigError extends Error {
4
4
  constructor(path: string, message: string);
5
5
  }
6
6
  export declare function fingerprint(invite: string): string;
7
- export declare function validateRoomsConfig(raw: unknown, vars: Record<string, string>, path: string): RoomsConfig & {
7
+ export declare function validateRoomsConfig(raw: unknown, vars: Record<string, string>, path: string, deferredInviteFile?: string): RoomsConfig & {
8
8
  _invite?: {
9
9
  value: string;
10
10
  fingerprint: string;
@@ -1,4 +1,5 @@
1
1
  import { existsSync, readFileSync } from 'node:fs';
2
+ import { isAbsolute } from 'node:path';
2
3
  import { createHash } from 'node:crypto';
3
4
  import { ROOMS_KEYS as RK, ROOMS_OWNER_KEYS as ROK, ROOMS_COWORK_KEYS as RCK, ROOMS_DEFAULTS_KEYS as RDK, TASKS_KEYS as TK, TEMPLATE_KEYS as TPK, TEMPLATE_MEMBER_KEYS as TMK, TEMPLATE_ROOM_KEYS as TRK, } from './types.js';
4
5
  const isPlainObject = (v) => v !== null && typeof v === 'object' && !Array.isArray(v);
@@ -18,7 +19,7 @@ function rejectUnknown(obj, allowed, path, section) {
18
19
  export function fingerprint(invite) {
19
20
  return createHash('sha256').update(invite.trim()).digest('hex');
20
21
  }
21
- function resolveInvite(owner, vars, path) {
22
+ function resolveInvite(owner, vars, path, deferredInviteFile) {
22
23
  const inline = owner.public_invite;
23
24
  const file = owner.public_invite_file;
24
25
  if (inline && file)
@@ -28,6 +29,10 @@ function resolveInvite(owner, vars, path) {
28
29
  let raw;
29
30
  if (file) {
30
31
  const resolved = file.replace(/\$\{(\w+)\}/g, (m, k) => (k in vars ? String(vars[k]) : m));
32
+ // Tunnel setup writes this one file itself after enrollment; until then it may be absent.
33
+ // Only the literal absolute path counts: a relative spelling that happens to reach it does not.
34
+ if (!existsSync(resolved) && deferredInviteFile !== undefined && isAbsolute(deferredInviteFile) && resolved === deferredInviteFile)
35
+ return undefined;
31
36
  if (!existsSync(resolved))
32
37
  throw new RoomsTasksConfigError(path, `rooms.owner.public_invite_file: not found: ${resolved}`);
33
38
  raw = readFileSync(resolved, 'utf8').trim();
@@ -39,7 +44,7 @@ function resolveInvite(owner, vars, path) {
39
44
  throw new RoomsTasksConfigError(path, 'rooms.owner: invite is empty');
40
45
  return { value: raw, fingerprint: fingerprint(raw) };
41
46
  }
42
- export function validateRoomsConfig(raw, vars, path) {
47
+ export function validateRoomsConfig(raw, vars, path, deferredInviteFile) {
43
48
  if (!isPlainObject(raw))
44
49
  throw new RoomsTasksConfigError(path, 'rooms: must be a mapping');
45
50
  // Migration-only input: provider selection was exposed before rooms shipped,
@@ -54,7 +59,9 @@ export function validateRoomsConfig(raw, vars, path) {
54
59
  if (!isPlainObject(raw.cowork))
55
60
  throw new RoomsTasksConfigError(path, 'rooms.cowork: must be a mapping');
56
61
  rejectUnknown(raw.cowork, RCK, path, 'rooms.cowork');
57
- cowork = { config: raw.cowork.config };
62
+ if (raw.cowork.config !== undefined)
63
+ throw new RoomsTasksConfigError(path, 'rooms.cowork.config is no longer supported; remove it and configure ~/.ours-client/profile.json for all services');
64
+ cowork = {};
58
65
  }
59
66
  if (!isPlainObject(raw.owner))
60
67
  throw new RoomsTasksConfigError(path, 'rooms.owner: required and must be a mapping');
@@ -66,7 +73,7 @@ export function validateRoomsConfig(raw, vars, path) {
66
73
  if (!CID_RE.test(expectedCid))
67
74
  throw new RoomsTasksConfigError(path, 'rooms.owner.expected_cid: must be exactly 64 hexadecimal characters');
68
75
  const ownerRole = raw.owner.role ?? 'Owner';
69
- const invite = resolveInvite(raw.owner, vars, path);
76
+ const invite = resolveInvite(raw.owner, vars, path, deferredInviteFile);
70
77
  const owner = {
71
78
  provider: ownerProvider,
72
79
  expected_cid: expectedCid.toLowerCase(),
@@ -1,8 +1,8 @@
1
1
  /**
2
- * Typed client for the ours-cowork v1 private management-socket protocol.
2
+ * Typed client for the ours-cowork v1 gateway management protocol.
3
3
  *
4
4
  * Cowork owns room state. Fleet only invokes the versioned JSON RPC exposed
5
- * by Cowork's Unix socket and projects the response into orchestration data.
5
+ * through the shared HTTP gateway and projects the response into orchestration data.
6
6
  */
7
7
  import type { Socket } from 'node:net';
8
8
  import type { RoomHistoryEvidence } from './types.js';
@@ -108,6 +108,4 @@ export interface CoworkAdapterOptions {
108
108
  home?: string;
109
109
  connect?: (path: string) => Socket;
110
110
  }
111
- /** Resolve the same config/state inputs as ours-cowork and return its Unix socket. */
112
- export declare function resolveCoworkSocketPath(options?: CoworkAdapterOptions): string;
113
111
  export declare function createCoworkAdapter(options?: CoworkAdapterOptions): CoworkAdapter;
@@ -1,20 +1,14 @@
1
1
  /**
2
- * Typed client for the ours-cowork v1 private management-socket protocol.
2
+ * Typed client for the ours-cowork v1 gateway management protocol.
3
3
  *
4
4
  * Cowork owns room state. Fleet only invokes the versioned JSON RPC exposed
5
- * by Cowork's Unix socket and projects the response into orchestration data.
5
+ * through the shared HTTP gateway and projects the response into orchestration data.
6
6
  */
7
- import { existsSync, readFileSync } from 'node:fs';
8
- import { homedir } from 'node:os';
9
- import { join, resolve } from 'node:path';
10
- import { createConnection } from 'node:net';
11
7
  import { readClientProfile } from '../client-profile.js';
12
8
  import { coworkHttpCall } from './cowork-http.js';
13
- const RPC_VERSION = 1;
14
9
  const DEFAULT_TIMEOUT_MS = 30_000;
15
- const MAX_RESPONSE_BYTES = 4 * 1024 * 1024;
16
10
  export class CoworkUnavailableError extends Error {
17
- constructor(message = 'Cowork management socket is not reachable', options) {
11
+ constructor(message = 'Cowork gateway management is not reachable', options) {
18
12
  super(message, options);
19
13
  this.name = 'CoworkUnavailableError';
20
14
  }
@@ -171,121 +165,13 @@ function projectHistoryRecord(value) {
171
165
  }
172
166
  return undefined;
173
167
  }
174
- /** Resolve the same config/state inputs as ours-cowork and return its Unix socket. */
175
- export function resolveCoworkSocketPath(options = {}) {
176
- if (options.socketPath)
177
- return resolve(options.socketPath);
178
- const env = options.env ?? process.env;
179
- const home = options.home ?? homedir();
180
- const configuredPath = options.configPath ?? env.OURS_COWORK_CONFIG;
181
- const configPath = resolve(configuredPath ?? join(home, '.ours-cowork', 'config.json'));
182
- let stateDir = resolve(home, '.ours-cowork');
183
- if (existsSync(configPath)) {
184
- let parsed;
185
- try {
186
- parsed = JSON.parse(readFileSync(configPath, 'utf8'));
187
- }
188
- catch (error) {
189
- throw new CoworkProtocolError('config', `malformed config at ${configPath}`, 'invalid_config', { cause: error });
190
- }
191
- const config = object(parsed, 'config', 'config');
192
- if (config.version !== 1 || typeof config.stateDir !== 'string' || !config.stateDir)
193
- throw new CoworkProtocolError('config', `invalid Cowork v1 config at ${configPath}`, 'invalid_config');
194
- stateDir = resolve(config.stateDir);
195
- }
196
- else if (configuredPath !== undefined) {
197
- throw new CoworkProtocolError('config', `configured Cowork config does not exist: ${configPath}`, 'invalid_config');
198
- }
199
- if (env.OURS_COWORK_STATE_DIR)
200
- stateDir = resolve(env.OURS_COWORK_STATE_DIR);
201
- return join(stateDir, 'management.sock');
202
- }
203
- function rpcCall(socketPath, method, params, timeoutMs, connect) {
204
- const id = `${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`;
205
- const request = `${JSON.stringify({ version: RPC_VERSION, id, method, params })}\n`;
206
- return new Promise((resolveCall, rejectCall) => {
207
- const socket = connect(socketPath);
208
- let bytes = '';
209
- let size = 0;
210
- let settled = false;
211
- let connected = false;
212
- const finishError = (error) => {
213
- if (settled)
214
- return;
215
- settled = true;
216
- clearTimeout(timer);
217
- socket.destroy();
218
- rejectCall(error);
219
- };
220
- const timer = setTimeout(() => finishError(new CoworkUnavailableError(connected ? 'Cowork did not answer the management socket' : 'Cowork management socket is not reachable')), timeoutMs);
221
- socket.setEncoding('utf8');
222
- socket.once('connect', () => {
223
- connected = true;
224
- socket.write(request);
225
- });
226
- socket.on('data', (chunk) => {
227
- if (settled)
228
- return;
229
- const text = typeof chunk === 'string' ? chunk : chunk.toString('utf8');
230
- size += Buffer.byteLength(text);
231
- if (size > MAX_RESPONSE_BYTES) {
232
- finishError(new CoworkProtocolError(method, 'response exceeded 4 MiB'));
233
- return;
234
- }
235
- bytes += text;
236
- const newline = bytes.indexOf('\n');
237
- if (newline < 0)
238
- return;
239
- if (bytes.slice(newline + 1).trim() !== '') {
240
- finishError(new CoworkProtocolError(method, 'daemon returned more than one response'));
241
- return;
242
- }
243
- let response;
244
- try {
245
- response = object(JSON.parse(bytes.slice(0, newline)), method, 'RPC response');
246
- }
247
- catch (error) {
248
- finishError(error instanceof CoworkProtocolError
249
- ? error : new CoworkProtocolError(method, 'daemon returned malformed JSON', 'protocol_error', { cause: error }));
250
- return;
251
- }
252
- if (response.version !== RPC_VERSION || response.id !== id
253
- || (Object.hasOwn(response, 'error') === Object.hasOwn(response, 'result'))) {
254
- finishError(new CoworkProtocolError(method, 'daemon returned an invalid RPC response'));
255
- return;
256
- }
257
- settled = true;
258
- clearTimeout(timer);
259
- socket.destroy();
260
- if (Object.hasOwn(response, 'error')) {
261
- const error = object(response.error, method, 'RPC error');
262
- rejectCall(new CoworkProtocolError(method, typeof error.message === 'string' ? error.message : 'unknown RPC error', typeof error.code === 'string' ? error.code : 'rpc_error'));
263
- }
264
- else {
265
- resolveCall(response.result);
266
- }
267
- });
268
- socket.once('end', () => {
269
- if (!settled)
270
- finishError(new CoworkProtocolError(method, 'daemon closed without a complete response'));
271
- });
272
- socket.once('error', (error) => {
273
- if (!settled)
274
- finishError(new CoworkUnavailableError(error.code === 'EACCES' || error.code === 'EPERM'
275
- ? 'Cowork management socket access denied' : 'Cowork management socket is not reachable', { cause: error }));
276
- });
277
- });
278
- }
279
168
  export function createCoworkAdapter(options = {}) {
280
169
  const env = options.env ?? process.env;
281
- const explicitLocal = options.socketPath !== undefined || options.configPath !== undefined
282
- || env.OURS_COWORK_CONFIG !== undefined || !!env.OURS_COWORK_STATE_DIR;
283
- const profile = explicitLocal ? undefined : readClientProfile({ ...env, HOME: options.home ?? env.HOME });
284
- const remote = profile?.serverUrl ? profile : undefined;
285
- const socketPath = remote ? undefined : resolveCoworkSocketPath(options);
170
+ if (options.socketPath !== undefined || options.configPath !== undefined)
171
+ throw new CoworkProtocolError('config', 'Local Cowork selectors are unsupported; configure the shared gateway client profile', 'invalid_config');
172
+ const profile = readClientProfile({ ...env, HOME: options.home ?? env.HOME });
286
173
  const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
287
- const connect = options.connect ?? ((path) => createConnection(path));
288
- const call = (method, params) => remote ? coworkHttpCall(remote, method, params, timeoutMs) : rpcCall(socketPath, method, params, timeoutMs, connect);
174
+ const call = (method, params) => coworkHttpCall(profile, method, params, timeoutMs);
289
175
  return {
290
176
  async available() {
291
177
  try {