@ours.network/fleet 1.2.0-nightly.8 → 1.2.0-nightly.80

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 (423) hide show
  1. package/README.md +279 -68
  2. package/dist/account-origin.d.ts +5 -0
  3. package/dist/account-origin.js +31 -0
  4. package/dist/agent-ours/bridge.js +26 -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 +19 -0
  11. package/dist/agent-ours/mcp-endpoint.js +98 -25
  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 +24 -1
  15. package/dist/agent-ours/service.js +97 -13
  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 +34 -0
  19. package/dist/application/conversation-history.js +294 -0
  20. package/dist/application/conversation-role-observer.d.ts +62 -0
  21. package/dist/application/conversation-role-observer.js +206 -0
  22. package/dist/application/fleet-query-service.d.ts +1 -1
  23. package/dist/application/fleet-query-service.js +3 -3
  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/resource-observer.d.ts +43 -0
  30. package/dist/application/resource-observer.js +323 -0
  31. package/dist/application/role-creation-service.js +3 -2
  32. package/dist/application/role-removal-service.js +17 -3
  33. package/dist/application/role-repository.d.ts +1 -1
  34. package/dist/application/role-repository.js +30 -7
  35. package/dist/application/room-layout-definitions.d.ts +45 -0
  36. package/dist/application/room-layout-definitions.js +130 -0
  37. package/dist/application/session-control.d.ts +63 -17
  38. package/dist/application/session-control.js +221 -23
  39. package/dist/application/supervisor-ours-tools.js +5 -11
  40. package/dist/application/task-layouts.d.ts +91 -0
  41. package/dist/application/task-layouts.js +261 -0
  42. package/dist/application/task-room-service.d.ts +101 -3
  43. package/dist/application/task-room-service.js +286 -33
  44. package/dist/application/types.d.ts +2 -0
  45. package/dist/atomic-file.d.ts +34 -1
  46. package/dist/atomic-file.js +161 -18
  47. package/dist/briefing.d.ts +3 -1
  48. package/dist/briefing.js +97 -31
  49. package/dist/build-info.json +9 -4
  50. package/dist/capabilities.d.ts +9 -1
  51. package/dist/capabilities.js +13 -0
  52. package/dist/cli.js +400 -37
  53. package/dist/client-profile.d.ts +5 -17
  54. package/dist/client-profile.js +4 -127
  55. package/dist/config.d.ts +45 -0
  56. package/dist/config.js +57 -6
  57. package/dist/creation.d.ts +1 -1
  58. package/dist/creation.js +4 -4
  59. package/dist/daemon-recovery.d.ts +14 -9
  60. package/dist/daemon-recovery.js +124 -115
  61. package/dist/docs.d.ts +1 -1
  62. package/dist/docs.js +207 -19
  63. package/dist/doctor.d.ts +4 -1
  64. package/dist/doctor.js +75 -60
  65. package/dist/erased-resources.d.ts +9 -0
  66. package/dist/erased-resources.js +87 -0
  67. package/dist/file-delivery/reader.d.ts +15 -0
  68. package/dist/file-delivery/reader.js +63 -0
  69. package/dist/file-delivery/store.d.ts +15 -0
  70. package/dist/file-delivery/store.js +124 -0
  71. package/dist/file-delivery/types.d.ts +19 -0
  72. package/dist/file-delivery/types.js +1 -0
  73. package/dist/fleet-command-audit.d.ts +7 -0
  74. package/dist/fleet-command-audit.js +24 -8
  75. package/dist/harness/agent-session.d.ts +4 -0
  76. package/dist/harness/claude-code-session.js +1 -1
  77. package/dist/harness/claude-code.js +21 -11
  78. package/dist/harness/claude-runtime.d.ts +12 -0
  79. package/dist/harness/claude-runtime.js +43 -0
  80. package/dist/harness/codex-app-server-proxy.d.ts +4 -1
  81. package/dist/harness/codex-app-server-proxy.js +41 -5
  82. package/dist/harness/codex-reasoning.d.ts +5 -0
  83. package/dist/harness/codex-reasoning.js +12 -0
  84. package/dist/harness/codex-runtime.d.ts +11 -1
  85. package/dist/harness/codex-runtime.js +31 -4
  86. package/dist/harness/codex-session.js +2 -2
  87. package/dist/harness/codex.js +24 -12
  88. package/dist/init-wizard.d.ts +2 -0
  89. package/dist/init-wizard.js +38 -19
  90. package/dist/isolation/policy.js +6 -1
  91. package/dist/isolation/types.d.ts +5 -0
  92. package/dist/managed-cli.d.ts +253 -0
  93. package/dist/managed-cli.js +923 -0
  94. package/dist/managed-recovery.d.ts +13 -0
  95. package/dist/managed-recovery.js +31 -0
  96. package/dist/minimal-setup.d.ts +2 -0
  97. package/dist/minimal-setup.js +44 -0
  98. package/dist/monitor.d.ts +9 -34
  99. package/dist/monitor.js +90 -153
  100. package/dist/notifications/fleet-producer.d.ts +31 -0
  101. package/dist/notifications/fleet-producer.js +351 -0
  102. package/dist/notifications/outbox.d.ts +59 -0
  103. package/dist/notifications/outbox.js +199 -0
  104. package/dist/notifications/target-cleanup.d.ts +16 -0
  105. package/dist/notifications/target-cleanup.js +75 -0
  106. package/dist/notifications/task-producer.d.ts +20 -0
  107. package/dist/notifications/task-producer.js +100 -0
  108. package/dist/ops.d.ts +2 -0
  109. package/dist/ops.js +20 -3
  110. package/dist/owner-channel/channel.js +17 -6
  111. package/dist/owner-channel/commands.d.ts +4 -0
  112. package/dist/owner-channel/commands.js +8 -0
  113. package/dist/owner-channel/lifecycle-outbox.js +4 -3
  114. package/dist/owner-channel/ours-client.js +2 -17
  115. package/dist/paths.d.ts +5 -0
  116. package/dist/paths.js +5 -0
  117. package/dist/preset-migration.js +43 -0
  118. package/dist/process-incarnation.d.ts +23 -0
  119. package/dist/process-incarnation.js +42 -0
  120. package/dist/reminders/manager.d.ts +75 -0
  121. package/dist/reminders/manager.js +278 -0
  122. package/dist/reminders/runtime.d.ts +27 -0
  123. package/dist/reminders/runtime.js +32 -0
  124. package/dist/reminders/schedule.d.ts +37 -0
  125. package/dist/reminders/schedule.js +104 -0
  126. package/dist/reminders/store.d.ts +341 -0
  127. package/dist/reminders/store.js +762 -0
  128. package/dist/reminders/tools.d.ts +20 -0
  129. package/dist/reminders/tools.js +114 -0
  130. package/dist/resolved-plan.js +1 -0
  131. package/dist/rooms-tasks/add-member.d.ts +48 -0
  132. package/dist/rooms-tasks/add-member.js +84 -0
  133. package/dist/rooms-tasks/archived-absence.d.ts +4 -0
  134. package/dist/rooms-tasks/archived-absence.js +11 -0
  135. package/dist/rooms-tasks/cli.js +65 -5
  136. package/dist/rooms-tasks/close.d.ts +5 -2
  137. package/dist/rooms-tasks/close.js +224 -36
  138. package/dist/rooms-tasks/config.d.ts +1 -1
  139. package/dist/rooms-tasks/config.js +11 -4
  140. package/dist/rooms-tasks/cowork-adapter.d.ts +12 -5
  141. package/dist/rooms-tasks/cowork-adapter.js +15 -121
  142. package/dist/rooms-tasks/cowork-http.js +56 -6
  143. package/dist/rooms-tasks/deletion.d.ts +1 -1
  144. package/dist/rooms-tasks/deletion.js +63 -10
  145. package/dist/rooms-tasks/erasure.d.ts +6 -0
  146. package/dist/rooms-tasks/erasure.js +173 -0
  147. package/dist/rooms-tasks/external-worker.d.ts +1 -1
  148. package/dist/rooms-tasks/external-worker.js +3 -4
  149. package/dist/rooms-tasks/layout-binding-client.d.ts +11 -0
  150. package/dist/rooms-tasks/layout-binding-client.js +56 -0
  151. package/dist/rooms-tasks/layout-binding-grants.d.ts +21 -0
  152. package/dist/rooms-tasks/layout-binding-grants.js +128 -0
  153. package/dist/rooms-tasks/layout-cli.d.ts +4 -0
  154. package/dist/rooms-tasks/layout-cli.js +107 -0
  155. package/dist/rooms-tasks/layout-config.d.ts +11 -0
  156. package/dist/rooms-tasks/layout-config.js +89 -0
  157. package/dist/rooms-tasks/layout-control.d.ts +21 -0
  158. package/dist/rooms-tasks/layout-control.js +52 -0
  159. package/dist/rooms-tasks/layout-member-retirement.d.ts +10 -0
  160. package/dist/rooms-tasks/layout-member-retirement.js +90 -0
  161. package/dist/rooms-tasks/layout-service.d.ts +43 -0
  162. package/dist/rooms-tasks/layout-service.js +203 -0
  163. package/dist/rooms-tasks/layout.d.ts +121 -0
  164. package/dist/rooms-tasks/layout.js +286 -0
  165. package/dist/rooms-tasks/legacy-task-member.d.ts +19 -0
  166. package/dist/rooms-tasks/legacy-task-member.js +116 -0
  167. package/dist/rooms-tasks/live-readiness.d.ts +18 -0
  168. package/dist/rooms-tasks/live-readiness.js +85 -0
  169. package/dist/rooms-tasks/member-ownership.d.ts +5 -0
  170. package/dist/rooms-tasks/member-ownership.js +40 -0
  171. package/dist/rooms-tasks/member-startup.d.ts +1 -0
  172. package/dist/rooms-tasks/member-startup.js +2 -0
  173. package/dist/rooms-tasks/provision.d.ts +3 -0
  174. package/dist/rooms-tasks/provision.js +101 -49
  175. package/dist/rooms-tasks/recovery.d.ts +19 -0
  176. package/dist/rooms-tasks/recovery.js +110 -0
  177. package/dist/rooms-tasks/room-state.d.ts +4 -2
  178. package/dist/rooms-tasks/room-state.js +47 -8
  179. package/dist/rooms-tasks/task-service-retirement.d.ts +11 -0
  180. package/dist/rooms-tasks/task-service-retirement.js +21 -0
  181. package/dist/rooms-tasks/task-state.d.ts +43 -5
  182. package/dist/rooms-tasks/task-state.js +244 -22
  183. package/dist/rooms-tasks/types.d.ts +35 -0
  184. package/dist/rooms-tasks/workspace-artifacts.d.ts +6 -0
  185. package/dist/rooms-tasks/workspace-artifacts.js +93 -0
  186. package/dist/rooms-tasks/workspace.d.ts +35 -0
  187. package/dist/rooms-tasks/workspace.js +325 -0
  188. package/dist/runner.d.ts +33 -4
  189. package/dist/runner.js +410 -62
  190. package/dist/session/acp.d.ts +75 -2
  191. package/dist/session/acp.js +345 -82
  192. package/dist/session/arbiter.d.ts +98 -0
  193. package/dist/session/arbiter.js +272 -3
  194. package/dist/session/codex-app-server.d.ts +45 -1
  195. package/dist/session/codex-app-server.js +225 -32
  196. package/dist/session/control.d.ts +17 -3
  197. package/dist/session/control.js +219 -23
  198. package/dist/session/conversation-normalizer.d.ts +2 -1
  199. package/dist/session/conversation-normalizer.js +1 -1
  200. package/dist/session/conversation-observation.d.ts +25 -0
  201. package/dist/session/conversation-observation.js +100 -0
  202. package/dist/session/conversation-replay.d.ts +50 -0
  203. package/dist/session/conversation-replay.js +203 -0
  204. package/dist/session/conversation-store.d.ts +15 -1
  205. package/dist/session/conversation-store.js +213 -26
  206. package/dist/session/conversation-types.d.ts +57 -4
  207. package/dist/session/prompt-batch.d.ts +79 -0
  208. package/dist/session/prompt-batch.js +120 -0
  209. package/dist/session/task-notice.d.ts +14 -0
  210. package/dist/session/task-notice.js +13 -0
  211. package/dist/session/types.d.ts +42 -1
  212. package/dist/setup-tunnel-preflight.d.ts +34 -0
  213. package/dist/setup-tunnel-preflight.js +62 -0
  214. package/dist/socket-path.d.ts +2 -0
  215. package/dist/socket-path.js +22 -0
  216. package/dist/spawn.d.ts +5 -1
  217. package/dist/spawn.js +40 -4
  218. package/dist/startup-prompt.d.ts +15 -0
  219. package/dist/startup-prompt.js +71 -0
  220. package/dist/startup-readiness.d.ts +32 -0
  221. package/dist/startup-readiness.js +101 -0
  222. package/dist/startup-telemetry.d.ts +16 -0
  223. package/dist/startup-telemetry.js +46 -0
  224. package/dist/subscriptions/cli.d.ts +91 -0
  225. package/dist/subscriptions/cli.js +223 -0
  226. package/dist/subscriptions/launch.d.ts +37 -0
  227. package/dist/subscriptions/launch.js +93 -0
  228. package/dist/subscriptions/login.d.ts +77 -0
  229. package/dist/subscriptions/login.js +215 -0
  230. package/dist/subscriptions/observed.d.ts +7 -0
  231. package/dist/subscriptions/observed.js +44 -0
  232. package/dist/subscriptions/probe.d.ts +10 -0
  233. package/dist/subscriptions/probe.js +73 -0
  234. package/dist/subscriptions/service.d.ts +102 -0
  235. package/dist/subscriptions/service.js +364 -0
  236. package/dist/subscriptions/store.d.ts +71 -0
  237. package/dist/subscriptions/store.js +143 -0
  238. package/dist/supervisor/adoption.d.ts +8 -0
  239. package/dist/supervisor/adoption.js +78 -0
  240. package/dist/supervisor/catalog.d.ts +62 -0
  241. package/dist/supervisor/catalog.js +254 -0
  242. package/dist/supervisor/fleet.d.ts +8 -0
  243. package/dist/supervisor/fleet.js +89 -0
  244. package/dist/supervisor/index.js +3 -6
  245. package/dist/supervisor/launchd.d.ts +1 -1
  246. package/dist/supervisor/launchd.js +26 -13
  247. package/dist/supervisor/legacy.d.ts +35 -0
  248. package/dist/supervisor/legacy.js +524 -0
  249. package/dist/supervisor/manager.d.ts +15 -0
  250. package/dist/supervisor/manager.js +270 -0
  251. package/dist/supervisor/registration-refresh.d.ts +45 -0
  252. package/dist/supervisor/registration-refresh.js +266 -0
  253. package/dist/supervisor/scope.d.ts +4 -0
  254. package/dist/supervisor/scope.js +10 -0
  255. package/dist/supervisor/systemd.d.ts +2 -2
  256. package/dist/supervisor/systemd.js +19 -33
  257. package/dist/task-supervision.d.ts +9 -0
  258. package/dist/task-supervision.js +145 -0
  259. package/dist/temp-idle.d.ts +17 -0
  260. package/dist/temp-idle.js +42 -0
  261. package/dist/temp-lifecycle.d.ts +23 -8
  262. package/dist/temp-lifecycle.js +123 -66
  263. package/dist/temp-supervisor-recovery.d.ts +1 -1
  264. package/dist/temp-supervisor-recovery.js +2 -2
  265. package/dist/tunnel-setup-completion.d.ts +7 -0
  266. package/dist/tunnel-setup-completion.js +13 -0
  267. package/dist/tunnel-setup-input.d.ts +19 -0
  268. package/dist/tunnel-setup-input.js +130 -0
  269. package/dist/watchdog/query.d.ts +3 -1
  270. package/dist/watchdog/query.js +2 -1
  271. package/dist/web/agent-attachments.d.ts +29 -0
  272. package/dist/web/agent-attachments.js +162 -0
  273. package/dist/web/auth.d.ts +65 -2
  274. package/dist/web/auth.js +201 -11
  275. package/dist/web/available-port.d.ts +2 -0
  276. package/dist/web/available-port.js +23 -0
  277. package/dist/web/control.d.ts +3 -3
  278. package/dist/web/control.js +10 -8
  279. package/dist/web/conversation-stream-codec.d.ts +23 -0
  280. package/dist/web/conversation-stream-codec.js +110 -0
  281. package/dist/web/conversation-stream.d.ts +26 -0
  282. package/dist/web/conversation-stream.js +239 -0
  283. package/dist/web/events.d.ts +2 -0
  284. package/dist/web/events.js +9 -0
  285. package/dist/web/fleet-config-service.d.ts +7 -1
  286. package/dist/web/fleet-config-service.js +61 -16
  287. package/dist/web/prefix-gateway.d.ts +32 -0
  288. package/dist/web/prefix-gateway.js +248 -0
  289. package/dist/web/resource-views.d.ts +135 -0
  290. package/dist/web/resource-views.js +486 -0
  291. package/dist/web/runtime.d.ts +3 -1
  292. package/dist/web/runtime.js +132 -34
  293. package/dist/web/server.d.ts +19 -0
  294. package/dist/web/server.js +821 -68
  295. package/dist/web/service.d.ts +11 -2
  296. package/dist/web/service.js +30 -4
  297. package/dist/web/workspace-devices.d.ts +107 -0
  298. package/dist/web/workspace-devices.js +277 -0
  299. package/dist/web/workspace-tunnel.d.ts +1 -0
  300. package/dist/web/workspace-tunnel.js +34 -0
  301. package/dist/workspace-enrollment.d.ts +83 -0
  302. package/dist/workspace-enrollment.js +605 -0
  303. package/dist/workspace-replacement.d.ts +66 -0
  304. package/dist/workspace-replacement.js +238 -0
  305. package/docs/acp-files.md +146 -0
  306. package/docs/conversation-stream.md +216 -0
  307. package/docs/fleet-migration.md +54 -0
  308. package/docs/live-voice.md +13 -0
  309. package/docs/room-layouts.md +227 -0
  310. package/docs/self-reminders.md +374 -0
  311. package/docs/task-workspaces.md +225 -0
  312. package/docs/validation/managed-cli-permissions.md +421 -0
  313. package/examples/room-layouts/fresh.yaml +27 -0
  314. package/examples/room-layouts/scoped.yaml +21 -0
  315. package/examples/room-layouts/shared.yaml +19 -0
  316. package/package.json +24 -20
  317. package/presets/README.md +78 -0
  318. package/presets/brain-catalog.json +8 -2
  319. package/presets/fleet/agent_templates/Critic.yaml +1 -1
  320. package/presets/fleet/agent_templates/Developer.yaml +1 -1
  321. package/presets/fleet/agent_templates/Engineer.yaml +5 -0
  322. package/presets/fleet/agent_templates/LocalCoordinator.yaml +4 -2
  323. package/presets/fleet/agents/FleetCoordinator.yaml +1 -1
  324. package/presets/fleet/brains/claude-fable-5-1-high.yaml +5 -0
  325. package/presets/fleet/brains/claude-fable-5-1-low.yaml +5 -0
  326. package/presets/fleet/brains/claude-fable-5-1-max.yaml +5 -0
  327. package/presets/fleet/brains/claude-fable-5-1-medium.yaml +5 -0
  328. package/presets/fleet/brains/claude-fable-5-1-xhigh.yaml +5 -0
  329. package/presets/fleet/brains/claude-fable-5-high.yaml +1 -1
  330. package/presets/fleet/brains/claude-fable-5-low.yaml +1 -1
  331. package/presets/fleet/brains/claude-fable-5-max.yaml +1 -1
  332. package/presets/fleet/brains/claude-fable-5-medium.yaml +1 -1
  333. package/presets/fleet/brains/claude-fable-5-xhigh.yaml +1 -1
  334. package/presets/fleet/brains/claude-haiku-4-5-20251001-default.yaml +4 -0
  335. package/presets/fleet/brains/claude-opus-5-5-high.yaml +5 -0
  336. package/presets/fleet/brains/claude-opus-5-5-low.yaml +5 -0
  337. package/presets/fleet/brains/claude-opus-5-5-max.yaml +5 -0
  338. package/presets/fleet/brains/claude-opus-5-5-medium.yaml +5 -0
  339. package/presets/fleet/brains/claude-opus-5-5-xhigh.yaml +5 -0
  340. package/presets/fleet/brains/claude-opus-5-high.yaml +1 -1
  341. package/presets/fleet/brains/claude-opus-5-low.yaml +1 -1
  342. package/presets/fleet/brains/claude-opus-5-max.yaml +1 -1
  343. package/presets/fleet/brains/claude-opus-5-medium.yaml +1 -1
  344. package/presets/fleet/brains/claude-opus-5-xhigh.yaml +1 -1
  345. package/presets/fleet/brains/claude-sonnet-5-high.yaml +1 -1
  346. package/presets/fleet/brains/claude-sonnet-5-low.yaml +1 -1
  347. package/presets/fleet/brains/claude-sonnet-5-max.yaml +1 -1
  348. package/presets/fleet/brains/claude-sonnet-5-medium.yaml +1 -1
  349. package/presets/fleet/brains/claude-sonnet-5-xhigh.yaml +1 -1
  350. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-high.yaml +1 -1
  351. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-low.yaml +1 -1
  352. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-medium.yaml +1 -1
  353. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-xhigh.yaml +1 -1
  354. package/presets/fleet/brains/codex-gpt-5-4-high.yaml +1 -1
  355. package/presets/fleet/brains/codex-gpt-5-4-low.yaml +1 -1
  356. package/presets/fleet/brains/codex-gpt-5-4-medium.yaml +1 -1
  357. package/presets/fleet/brains/codex-gpt-5-4-mini-high.yaml +1 -1
  358. package/presets/fleet/brains/codex-gpt-5-4-mini-low.yaml +1 -1
  359. package/presets/fleet/brains/codex-gpt-5-4-mini-medium.yaml +1 -1
  360. package/presets/fleet/brains/codex-gpt-5-4-mini-xhigh.yaml +1 -1
  361. package/presets/fleet/brains/codex-gpt-5-4-xhigh.yaml +1 -1
  362. package/presets/fleet/brains/codex-gpt-5-5-high.yaml +1 -1
  363. package/presets/fleet/brains/codex-gpt-5-5-low.yaml +1 -1
  364. package/presets/fleet/brains/codex-gpt-5-5-medium.yaml +1 -1
  365. package/presets/fleet/brains/codex-gpt-5-5-xhigh.yaml +1 -1
  366. package/presets/fleet/brains/codex-gpt-5-6-luna-high.yaml +1 -1
  367. package/presets/fleet/brains/codex-gpt-5-6-luna-low.yaml +1 -1
  368. package/presets/fleet/brains/codex-gpt-5-6-luna-max.yaml +1 -1
  369. package/presets/fleet/brains/codex-gpt-5-6-luna-medium.yaml +1 -1
  370. package/presets/fleet/brains/codex-gpt-5-6-luna-xhigh.yaml +1 -1
  371. package/presets/fleet/brains/codex-gpt-5-6-sol-high.yaml +1 -1
  372. package/presets/fleet/brains/codex-gpt-5-6-sol-low.yaml +1 -1
  373. package/presets/fleet/brains/codex-gpt-5-6-sol-max.yaml +1 -1
  374. package/presets/fleet/brains/codex-gpt-5-6-sol-medium.yaml +1 -1
  375. package/presets/fleet/brains/codex-gpt-5-6-sol-ultra.yaml +1 -1
  376. package/presets/fleet/brains/codex-gpt-5-6-sol-xhigh.yaml +1 -1
  377. package/presets/fleet/brains/codex-gpt-5-6-terra-high.yaml +1 -1
  378. package/presets/fleet/brains/codex-gpt-5-6-terra-low.yaml +1 -1
  379. package/presets/fleet/brains/codex-gpt-5-6-terra-max.yaml +1 -1
  380. package/presets/fleet/brains/codex-gpt-5-6-terra-medium.yaml +1 -1
  381. package/presets/fleet/brains/codex-gpt-5-6-terra-ultra.yaml +1 -1
  382. package/presets/fleet/brains/codex-gpt-5-6-terra-xhigh.yaml +1 -1
  383. package/presets/fleet/brains/codex-gpt-6-1-sol-high.yaml +5 -0
  384. package/presets/fleet/brains/codex-gpt-6-1-sol-low.yaml +5 -0
  385. package/presets/fleet/brains/codex-gpt-6-1-sol-max.yaml +5 -0
  386. package/presets/fleet/brains/codex-gpt-6-1-sol-medium.yaml +5 -0
  387. package/presets/fleet/brains/codex-gpt-6-1-sol-ultra.yaml +5 -0
  388. package/presets/fleet/brains/codex-gpt-6-1-sol-xhigh.yaml +5 -0
  389. package/presets/fleet/brains/codex-gpt-6-astra-high.yaml +1 -1
  390. package/presets/fleet/brains/codex-gpt-6-astra-low.yaml +1 -1
  391. package/presets/fleet/brains/codex-gpt-6-astra-max.yaml +1 -1
  392. package/presets/fleet/brains/codex-gpt-6-astra-medium.yaml +1 -1
  393. package/presets/fleet/brains/codex-gpt-6-astra-ultra.yaml +1 -1
  394. package/presets/fleet/brains/codex-gpt-6-astra-xhigh.yaml +1 -1
  395. package/presets/fleet/brains/codex-gpt-6-luna-high.yaml +5 -0
  396. package/presets/fleet/brains/codex-gpt-6-luna-low.yaml +5 -0
  397. package/presets/fleet/brains/codex-gpt-6-luna-max.yaml +5 -0
  398. package/presets/fleet/brains/codex-gpt-6-luna-medium.yaml +5 -0
  399. package/presets/fleet/brains/codex-gpt-6-luna-xhigh.yaml +5 -0
  400. package/presets/fleet/brains/codex-gpt-6-sol-high.yaml +5 -0
  401. package/presets/fleet/brains/codex-gpt-6-sol-low.yaml +5 -0
  402. package/presets/fleet/brains/codex-gpt-6-sol-max.yaml +5 -0
  403. package/presets/fleet/brains/codex-gpt-6-sol-medium.yaml +5 -0
  404. package/presets/fleet/brains/codex-gpt-6-sol-ultra.yaml +5 -0
  405. package/presets/fleet/brains/codex-gpt-6-sol-xhigh.yaml +5 -0
  406. package/presets/fleet/roles/Coordinator.yaml +12 -11
  407. package/presets/fleet/roles/Critic.yaml +13 -0
  408. package/presets/fleet/roles/Developer.yaml +13 -0
  409. package/presets/fleet/roles/Engineer.yaml +53 -0
  410. package/presets/fleet/room_layouts/engineering.yaml +25 -0
  411. package/presets/fleet/room_layouts/pair.yaml +24 -0
  412. package/presets/fleet/room_layouts/single.yaml +17 -0
  413. package/presets/fleet/room_layouts/team.yaml +30 -0
  414. package/presets/fleet/room_templates/engineering.yaml +19 -0
  415. package/presets/manifest.json +2 -2
  416. package/dist/web-app/assets/index-59GF-gsZ.css +0 -1
  417. package/dist/web-app/assets/index-C0h3ALvs.js +0 -10
  418. package/dist/web-app/icons/ours-fleet-maskable.svg +0 -4
  419. package/dist/web-app/icons/ours-fleet.svg +0 -4
  420. package/dist/web-app/index.html +0 -17
  421. package/dist/web-app/manifest.webmanifest +0 -15
  422. package/dist/web-app/offline.html +0 -18
  423. package/dist/web-app/sw.js +0 -51
@@ -0,0 +1,75 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync } from 'node:fs';
3
+ import { join } from 'node:path';
4
+ import { replaceFileAtomically } from '../atomic-file.js';
5
+ import { stateRoot } from '../paths.js';
6
+ import { producerConfig } from './outbox.js';
7
+ export const cleanupDir = () => join(stateRoot(), 'notifications', 'removed-targets');
8
+ /** Only a closed agent identity authorizes this producer's role-target cleanup. */
9
+ export function notificationAgentId(target) {
10
+ if (!target || typeof target !== 'object' || Array.isArray(target))
11
+ return null;
12
+ const value = target;
13
+ return Object.keys(value).every(key => ['kind', 'agentId'].includes(key)) && value.kind === 'agent'
14
+ && typeof value.agentId === 'string' && /^[A-Za-z0-9_-]{1,120}$/.test(value.agentId) ? value.agentId : null;
15
+ }
16
+ export function hasPendingNotificationTargetCleanup(dir = cleanupDir()) {
17
+ if (!existsSync(dir))
18
+ return false;
19
+ return readdirSync(dir).filter(file => /^[a-f0-9]{64}\.json$/.test(file)).some(file => {
20
+ try {
21
+ const value = JSON.parse(readFileSync(join(dir, file), 'utf8'));
22
+ return !!value && typeof value === 'object' && Object.keys(value).length === 1 && !!notificationAgentId(value.target);
23
+ }
24
+ catch (error) {
25
+ return !(error instanceof SyntaxError);
26
+ }
27
+ });
28
+ }
29
+ /** Queue only after the owning lifecycle has established deletion, never on a failed inventory read. */
30
+ export async function retireNotificationTarget(target, config = producerConfig(), dir = cleanupDir(), request = fetch) {
31
+ if (!config)
32
+ return;
33
+ if (!notificationAgentId(target))
34
+ throw new Error('Invalid notification target');
35
+ mkdirSync(dir, { recursive: true, mode: 0o700 });
36
+ replaceFileAtomically(join(dir, createHash('sha256').update(JSON.stringify({ kind: 'agent', agentId: target.agentId })).digest('hex') + '.json'), JSON.stringify({ target }) + '\n', 0o600);
37
+ await drainNotificationTargetCleanup(config, dir, request, { retain: true });
38
+ }
39
+ /** Persisted retries also run from the ordinary web notification producer after restart. */
40
+ export async function drainNotificationTargetCleanup(config, dir = cleanupDir(), request = fetch, options = {}) {
41
+ if (!existsSync(dir))
42
+ return;
43
+ let attempts = 0;
44
+ for (const file of readdirSync(dir).filter(file => /^[a-f0-9]{64}\.json$/.test(file))) {
45
+ if (attempts >= 32)
46
+ break;
47
+ const path = join(dir, file);
48
+ let value;
49
+ try {
50
+ value = JSON.parse(readFileSync(path, 'utf8'));
51
+ }
52
+ catch {
53
+ continue;
54
+ }
55
+ if (!value || typeof value !== 'object' || Array.isArray(value) || Object.keys(value).length !== 1 || !notificationAgentId(value.target))
56
+ continue;
57
+ try {
58
+ attempts++;
59
+ if (options.beforeDelete && !(await options.beforeDelete(value.target))) {
60
+ rmSync(path, { force: true });
61
+ continue;
62
+ }
63
+ const response = await request(config.origin + '/api/v1/delete-target', { method: 'POST', redirect: 'error', signal: AbortSignal.timeout(10000),
64
+ headers: { ...(config.gatewayCredential ? { 'X-Ours-Api-Token': config.gatewayCredential, 'X-Ours-Notifications-Producer': config.token } : { Authorization: `Bearer ${config.token}` }), 'Content-Type': 'application/json' }, body: JSON.stringify(value) });
65
+ await response.body?.cancel();
66
+ if (!response.ok)
67
+ break;
68
+ if (!options.retain)
69
+ rmSync(path, { force: true });
70
+ }
71
+ catch {
72
+ break; /* No credentials or response bodies are included in logs. */
73
+ }
74
+ }
75
+ }
@@ -0,0 +1,20 @@
1
+ import type { ProducerConfig } from './outbox.js';
2
+ /** The Fleet manager owns this sender even when the web console is stopped.
3
+ * Events are already durable in their owning task commit, so no second outbox
4
+ * admission/crash window is needed. A failed task does not block other tasks. */
5
+ export declare class TaskNotificationProducer {
6
+ private readonly config;
7
+ private readonly warn;
8
+ private readonly request;
9
+ private timer?;
10
+ private dirty;
11
+ private running?;
12
+ private offset;
13
+ private stopped;
14
+ private readonly shutdown;
15
+ constructor(config: ProducerConfig, warn?: (line: string) => void, request?: typeof fetch);
16
+ drain(): Promise<void>;
17
+ private retry;
18
+ private drainOnce;
19
+ close(): Promise<void>;
20
+ }
@@ -0,0 +1,100 @@
1
+ import { observeFleetResources } from '../application/resource-observer.js';
2
+ import { tasksDir } from '../rooms-tasks/task-state.js';
3
+ import { acknowledgeTaskNotification, pendingTaskNotifications } from '../rooms-tasks/task-state.js';
4
+ /** The Fleet manager owns this sender even when the web console is stopped.
5
+ * Events are already durable in their owning task commit, so no second outbox
6
+ * admission/crash window is needed. A failed task does not block other tasks. */
7
+ export class TaskNotificationProducer {
8
+ config;
9
+ warn;
10
+ request;
11
+ timer;
12
+ dirty = false;
13
+ running;
14
+ offset = 0;
15
+ stopped = false;
16
+ shutdown = new AbortController();
17
+ constructor(config, warn = () => { }, request = fetch) {
18
+ this.config = config;
19
+ this.warn = warn;
20
+ this.request = request;
21
+ // Subscribe before startup replay: a commit while fetch is pending needs a second pass.
22
+ observeFleetResources({ retryWatchFailures: true, paths: [tasksDir()], roots: [] }, state => {
23
+ if (state.health !== 'healthy')
24
+ this.warn('Task notification observation unavailable; committed events retained');
25
+ else {
26
+ this.dirty = true;
27
+ void this.drain();
28
+ }
29
+ }, this.shutdown.signal);
30
+ void this.drain();
31
+ }
32
+ drain() {
33
+ if (this.stopped)
34
+ return Promise.resolve();
35
+ if (this.running)
36
+ return this.running;
37
+ this.dirty = false;
38
+ clearTimeout(this.timer);
39
+ this.timer = undefined;
40
+ this.running = this.drainOnce().catch(() => {
41
+ this.warn('Task notification snapshot unavailable; committed events retained');
42
+ this.retry();
43
+ }).finally(() => {
44
+ this.running = undefined;
45
+ if (this.dirty && !this.stopped) {
46
+ this.dirty = false;
47
+ void this.drain();
48
+ }
49
+ });
50
+ return this.running;
51
+ }
52
+ retry() {
53
+ if (this.stopped || this.timer)
54
+ return;
55
+ this.timer = setTimeout(() => { this.timer = undefined; void this.drain(); }, 3000);
56
+ this.timer.unref();
57
+ }
58
+ async drainOnce() {
59
+ if (this.stopped)
60
+ return;
61
+ const pending = pendingTaskNotifications(this.warn);
62
+ if (!pending.length)
63
+ return;
64
+ const start = this.offset % pending.length;
65
+ const batch = [...pending.slice(start), ...pending.slice(0, start)].slice(0, 32);
66
+ this.offset = start + batch.length;
67
+ // Retry only outstanding work; never run an idle discovery timer.
68
+ this.retry();
69
+ for (let index = 0; index < batch.length; index += 4) {
70
+ if (this.stopped)
71
+ break;
72
+ await Promise.all(batch.slice(index, index + 4).map(async (task) => {
73
+ for (const event of task.events.slice(0, 32)) {
74
+ if (this.stopped)
75
+ break;
76
+ try {
77
+ const response = await this.request(this.config.origin + '/api/v1/send', {
78
+ method: 'POST', redirect: 'error', signal: AbortSignal.any([this.shutdown.signal, AbortSignal.timeout(10_000)]),
79
+ headers: { ...(this.config.gatewayCredential
80
+ ? { 'X-Ours-Api-Token': this.config.gatewayCredential, 'X-Ours-Notifications-Producer': this.config.token }
81
+ : { Authorization: `Bearer ${this.config.token}` }), 'Content-Type': 'application/json' },
82
+ body: JSON.stringify(event),
83
+ });
84
+ await response.body?.cancel();
85
+ if (!response.ok) {
86
+ this.warn(`Task notification delivery pending (HTTP ${response.status}); committed event retained`);
87
+ break;
88
+ }
89
+ acknowledgeTaskNotification(task.taskId, event.eventId);
90
+ }
91
+ catch {
92
+ this.warn('Task notification delivery pending; committed event retained');
93
+ break;
94
+ }
95
+ }
96
+ }));
97
+ }
98
+ }
99
+ async close() { this.stopped = true; clearTimeout(this.timer); this.shutdown.abort(); await this.running; }
100
+ }
package/dist/ops.d.ts CHANGED
@@ -54,5 +54,7 @@ export declare function up(cfg: FleetConfig, names: string[], deps: OpsDeps, con
54
54
  export declare function down(cfg: FleetConfig, names: string[], deps: OpsDeps): Promise<void>;
55
55
  /** Re-sync from config + bounce. mode 'keep' resumes context; 'fresh' wipes it. */
56
56
  export declare function restartRoles(cfg: FleetConfig, names: string[], deps: OpsDeps, mode: 'keep' | 'fresh', configPath?: string): Promise<void>;
57
+ /** Read-only admission check shared by CLI removal and web preview, before any archive. */
58
+ export declare function assertStandaloneRoleRemoval(cfg: FleetConfig, name: string): void;
57
59
  /** Stop + forget a role: unit, state dir, and its fleet.d file when spawned. */
58
60
  export declare function rmRole(cfg: FleetConfig, name: string, deps: OpsDeps): Promise<void>;
package/dist/ops.js CHANGED
@@ -1,3 +1,4 @@
1
+ import { retireNotificationTarget } from './notifications/target-cleanup.js';
1
2
  import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync, rmSync, unlinkSync, } from 'node:fs';
2
3
  import { join } from 'node:path';
3
4
  import { randomUUID, createHash } from 'node:crypto';
@@ -5,8 +6,9 @@ import { agentDir, watchdogsRoot } from './paths.js';
5
6
  import { findRole } from './config.js';
6
7
  import { getAdapter } from './harness/registry.js';
7
8
  import { generateBriefing } from './briefing.js';
8
- import { resetRestartLedger } from './runner.js';
9
- import { archiveTempState, stopTempSupervisor, tempSupervisorLiveness, } from './temp-lifecycle.js';
9
+ import { managedCliBriefing } from './managed-cli.js';
10
+ import { resetRestartLedger, loadTempRole } from './runner.js';
11
+ import { archiveTempState, stopTempSupervisor, tempSupervisorLiveness, readTempSupervisor, } from './temp-lifecycle.js';
10
12
  import { realExec } from './exec.js';
11
13
  import { daemonIdentityProvisioner, reconcilePermanentRoleIdentities, } from './creation.js';
12
14
  import { provesGeneratedAgentSource } from './generated-agent-source.js';
@@ -28,7 +30,7 @@ export function applyRole(role, opts = {}) {
28
30
  // brought up from so the supervised process can reload the SAME file instead of
29
31
  // silently falling back to the default ~/fleet.yaml. Temp roles snapshot their
30
32
  // whole resolved role into role.yaml instead and don't need this.
31
- if (!opts.temp)
33
+ if (!opts.temp || opts.configPath)
32
34
  writeFileSync(join(dir, '.config-path'), (opts.configPath ?? '') + '\n');
33
35
  writeFileSync(join(dir, '.identity'), role.identity + '\n');
34
36
  if (role.cwd)
@@ -43,6 +45,7 @@ export function applyRole(role, opts = {}) {
43
45
  routinesPath: join(dir, 'ROUTINES.md'), briefingBody,
44
46
  identityGuarantee: opts.identityGuarantee,
45
47
  temporaryIdentity: opts.temp === true,
48
+ managedCli: managedCliBriefing(role, opts.configPath, opts.temp === true),
46
49
  }));
47
50
  if (opts.fresh)
48
51
  for (const f of ['.booted', '.session-id', '.exit-status'])
@@ -220,8 +223,21 @@ export async function restartRoles(cfg, names, deps, mode, configPath) {
220
223
  : `↻ ${role.name} — restarted (resumes; briefing re-synced)`);
221
224
  }
222
225
  }
226
+ /** Read-only admission check shared by CLI removal and web preview, before any archive. */
227
+ export function assertStandaloneRoleRemoval(cfg, name) {
228
+ if (cfg.roles.some(role => role.name === name) || !/^[A-Za-z0-9_-]+$/.test(name))
229
+ return;
230
+ const dir = agentDir(name, true);
231
+ if (!existsSync(dir))
232
+ return;
233
+ const taskId = readTempSupervisor(dir)?.taskOwner?.taskId
234
+ ?? (existsSync(join(dir, 'role.yaml')) ? loadTempRole(name).roomMemberStartup?.task_id : undefined);
235
+ if (taskId)
236
+ throw Error(`TASK_MEMBER_IS_TASK_OWNED: ${name} belongs to task ${taskId}; use the owning task lifecycle; if its record is missing or corrupt, preserve evidence and restore the original records before retrying task delete`);
237
+ }
223
238
  /** Stop + forget a role: unit, state dir, and its fleet.d file when spawned. */
224
239
  export async function rmRole(cfg, name, deps) {
240
+ assertStandaloneRoleRemoval(cfg, name);
225
241
  const temporaryDir = /^[A-Za-z0-9_-]+$/.test(name) ? agentDir(name, true) : '';
226
242
  const configured = cfg.roles.find(role => role.name === name);
227
243
  if (!configured && temporaryDir && existsSync(temporaryDir)) {
@@ -255,6 +271,7 @@ export async function rmRole(cfg, name, deps) {
255
271
  rmSync(agentDir(name), { recursive: true, force: true });
256
272
  if (generatedSource && existsSync(role.sourceFile)) {
257
273
  unlinkSync(role.sourceFile);
274
+ await retireNotificationTarget({ kind: 'agent', agentId: name }).catch(() => { });
258
275
  deps.log(`removed ${role.sourceFile}`);
259
276
  }
260
277
  deps.log(`removed '${name}' (its ours identity is left intact)`);
@@ -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,11 @@ 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;
16
+ /** Serializes writers of the workspace binding and of its unfinished replacement record. */
17
+ export declare const workspaceBindingLock: () => string;
18
+ export declare const workspaceReplacementFile: () => string;
14
19
  export declare const agentsRoot: () => string;
15
20
  export declare const tmpRoot: () => string;
16
21
  export declare const logsRoot: () => string;
package/dist/paths.js CHANGED
@@ -21,6 +21,11 @@ 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');
26
+ /** Serializes writers of the workspace binding and of its unfinished replacement record. */
27
+ export const workspaceBindingLock = () => join(stateRoot(), 'workspace', '.binding.lock');
28
+ export const workspaceReplacementFile = () => join(stateRoot(), 'workspace', 'replacement.json');
24
29
  export const agentsRoot = () => join(stateRoot(), 'agents');
25
30
  export const tmpRoot = () => join(stateRoot(), 'tmp');
26
31
  export const logsRoot = () => join(stateRoot(), 'logs');
@@ -67,10 +67,35 @@ 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
+ });
78
+ // The packaged revision-10 LocalCoordinator continuity loop reread ROUTINES.md and the WORKLOG
79
+ // checkpoint on every 15-minute pass. Untouched copies adopt the current template; edited ones stay.
80
+ const REREADING_CONTINUITY_LOOP_FINGERPRINTS = Object.freeze({
81
+ 'agent_templates/LocalCoordinator.yaml': [
82
+ '53889cd3987e25ca42be64ddcfa1cf46a73ab769070db144f5b7d86085734178',
83
+ ],
84
+ });
85
+ // The packaged Developer, Critic and Engineer charters before they described one bounded
86
+ // self-reminder per peer wait. Untouched copies adopt the current text; edited ones stay.
87
+ const PEER_WAIT_REMINDER_FINGERPRINTS = Object.freeze({
88
+ 'roles/Developer.yaml': ['2884366ce02760d66d3edbaed2adc83f2959fd7906a7cd3b5a8b90cf601bc3e6'],
89
+ 'roles/Critic.yaml': ['998abdbc5159e8e1c26eb1ca09f9707b005ba1930bd5b7a4989c4d6893804c4a'],
90
+ 'roles/Engineer.yaml': ['a361453ad32cde08797cf90fff17a35bde1871ccea17389448b44398b45ccfa5'],
91
+ });
70
92
  const CURRENT_ROLE_DEFAULTS = new Set([
71
93
  'roles/Coordinator.yaml', 'roles/LocalCoordinator.yaml', 'roles/Developer.yaml', 'roles/Critic.yaml',
94
+ 'roles/Engineer.yaml',
72
95
  'agent_templates/LocalCoordinator.yaml', 'agent_templates/Developer.yaml', 'agent_templates/Critic.yaml',
96
+ 'agent_templates/Engineer.yaml',
73
97
  'room_templates/single.yaml', 'room_templates/pair.yaml', 'room_templates/team.yaml',
98
+ 'room_templates/engineering.yaml',
74
99
  'agents/FleetCoordinator.yaml',
75
100
  ]);
76
101
  export const legacyStarterMigrationManifest = Object.freeze({
@@ -193,6 +218,9 @@ export function migratePackagedRoleDefaults(configuration, options = {}, hooks =
193
218
  ...Object.keys(V3_ROLE_DEFAULT_FINGERPRINTS),
194
219
  ...Object.keys(V4_ROLE_DEFAULT_FINGERPRINTS),
195
220
  ...Object.keys(V5_ROLE_DEFAULT_FINGERPRINTS),
221
+ ...Object.keys(OWNER_CHANNEL_COORDINATOR_FINGERPRINTS),
222
+ ...Object.keys(REREADING_CONTINUITY_LOOP_FINGERPRINTS),
223
+ ...Object.keys(PEER_WAIT_REMINDER_FINGERPRINTS),
196
224
  ]);
197
225
  for (const relative of known) {
198
226
  const path = join(root, relative);
@@ -206,6 +234,9 @@ export function migratePackagedRoleDefaults(configuration, options = {}, hooks =
206
234
  V4_GENERATED_ROLE_DEFAULT_FINGERPRINTS[relative],
207
235
  V5_ROLE_DEFAULT_FINGERPRINTS[relative],
208
236
  V5_GENERATED_ROLE_DEFAULT_FINGERPRINTS[relative],
237
+ ...(OWNER_CHANNEL_COORDINATOR_FINGERPRINTS[relative] ?? []),
238
+ ...(REREADING_CONTINUITY_LOOP_FINGERPRINTS[relative] ?? []),
239
+ ...(PEER_WAIT_REMINDER_FINGERPRINTS[relative] ?? []),
209
240
  ].some(expected => expected !== undefined && actual === expected);
210
241
  if (recognized)
211
242
  (CURRENT_ROLE_DEFAULTS.has(relative) ? replacements : removals).push(path);
@@ -258,6 +289,18 @@ export function migratePackagedRoleDefaults(configuration, options = {}, hooks =
258
289
  unlinkSync(target);
259
290
  copyFileSync(join(source, relative), target, constants.COPYFILE_EXCL);
260
291
  chmodSync(target, 0o600);
292
+ // New Engineer seats use the existing Developer's selected brain. This
293
+ // preserves wizard/custom provider choices without copying permissions.
294
+ if (relative === 'agent_templates/Engineer.yaml' && classified.additions.includes(path)) {
295
+ const developer = join(root, 'agent_templates/Developer.yaml');
296
+ if (existsSync(developer)) {
297
+ const value = parseFleetDocument(developer, readFileSync(developer, 'utf8'), 'strict').value;
298
+ if (value.brain !== undefined && JSON.stringify(value.brain) !== JSON.stringify({ ref: 'claude-default' })) {
299
+ const bytes = readFileSync(target, 'utf8').replace('brain: { ref: claude-default }', () => `brain: ${JSON.stringify(value.brain)}`);
300
+ writeFileSync(target, bytes, { mode: 0o600 });
301
+ }
302
+ }
303
+ }
261
304
  fsyncPath(target);
262
305
  }
263
306
  for (const kind of ['roles', 'agent_templates', 'room_templates', 'agents'])
@@ -0,0 +1,23 @@
1
+ /**
2
+ * Which run of a process a PID names. A PID alone is not an identity: after a
3
+ * reboot or wraparound the same number belongs to an unrelated process, and a
4
+ * lock or claim keyed on it would then look held forever. The kernel boot id
5
+ * and the process start time (in clock ticks since boot) pin one incarnation.
6
+ * Both are Linux-only; elsewhere they are absent and callers stay PID-only.
7
+ */
8
+ export interface ProcessIncarnation {
9
+ boot?: string;
10
+ start?: string;
11
+ }
12
+ export interface IncarnationProbe {
13
+ bootId(): string | undefined;
14
+ startTime(pid: number): string | undefined;
15
+ }
16
+ export declare const procIncarnationProbe: IncarnationProbe;
17
+ export declare function processIncarnation(pid: number, probe?: IncarnationProbe): ProcessIncarnation;
18
+ /**
19
+ * True only when a recorded marker positively proves the live PID is a later
20
+ * process: a different boot, or a different start time. A marker that was not
21
+ * recorded, or cannot be read now, proves nothing and keeps the PID check.
22
+ */
23
+ export declare function incarnationEnded(pid: number, recorded: ProcessIncarnation, probe?: IncarnationProbe): boolean;
@@ -0,0 +1,42 @@
1
+ import { readFileSync } from 'node:fs';
2
+ export const procIncarnationProbe = {
3
+ bootId() {
4
+ try {
5
+ return readFileSync('/proc/sys/kernel/random/boot_id', 'utf8').trim() || undefined;
6
+ }
7
+ catch {
8
+ return undefined;
9
+ }
10
+ },
11
+ startTime(pid) {
12
+ try {
13
+ const stat = readFileSync(`/proc/${pid}/stat`, 'utf8');
14
+ return stat.slice(stat.lastIndexOf(')') + 2).split(/\s+/)[19] || undefined;
15
+ }
16
+ catch {
17
+ return undefined;
18
+ }
19
+ },
20
+ };
21
+ export function processIncarnation(pid, probe = procIncarnationProbe) {
22
+ const boot = probe.bootId(), start = probe.startTime(pid);
23
+ return { ...(boot ? { boot } : {}), ...(start ? { start } : {}) };
24
+ }
25
+ /**
26
+ * True only when a recorded marker positively proves the live PID is a later
27
+ * process: a different boot, or a different start time. A marker that was not
28
+ * recorded, or cannot be read now, proves nothing and keeps the PID check.
29
+ */
30
+ export function incarnationEnded(pid, recorded, probe = procIncarnationProbe) {
31
+ if (typeof recorded.boot === 'string') {
32
+ const boot = probe.bootId();
33
+ if (boot && boot !== recorded.boot)
34
+ return true;
35
+ }
36
+ if (typeof recorded.start === 'string') {
37
+ const start = probe.startTime(pid);
38
+ if (start && start !== recorded.start)
39
+ return true;
40
+ }
41
+ return false;
42
+ }