@ours.network/fleet 1.2.0-nightly.7 → 1.2.0-nightly.71

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 +2 -0
  3. package/dist/account-origin.js +6 -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 +280 -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 +40 -0
  30. package/dist/application/resource-observer.js +278 -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 +61 -17
  38. package/dist/application/session-control.js +207 -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 +22 -0
  101. package/dist/notifications/fleet-producer.js +237 -0
  102. package/dist/notifications/outbox.d.ts +46 -0
  103. package/dist/notifications/outbox.js +158 -0
  104. package/dist/notifications/target-cleanup.d.ts +11 -0
  105. package/dist/notifications/target-cleanup.js +63 -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 +2 -0
  116. package/dist/paths.js +2 -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 +8 -0
  134. package/dist/rooms-tasks/archived-absence.js +48 -0
  135. package/dist/rooms-tasks/cli.js +65 -5
  136. package/dist/rooms-tasks/close.d.ts +6 -2
  137. package/dist/rooms-tasks/close.js +246 -37
  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 +84 -5
  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 +52 -8
  182. package/dist/rooms-tasks/task-state.js +251 -26
  183. package/dist/rooms-tasks/types.d.ts +40 -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 +213 -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 +206 -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 +124 -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 +25 -0
  282. package/dist/web/conversation-stream.js +231 -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 +4 -1
  286. package/dist/web/fleet-config-service.js +46 -15
  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 +124 -0
  290. package/dist/web/resource-views.js +424 -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 +802 -67
  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 +580 -0
  303. package/dist/workspace-replacement.d.ts +66 -0
  304. package/dist/workspace-replacement.js +220 -0
  305. package/docs/acp-files.md +146 -0
  306. package/docs/conversation-stream.md +196 -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
@@ -3,7 +3,6 @@ import { createHmac, randomBytes } from 'node:crypto';
3
3
  import { join } from 'node:path';
4
4
  import { realExec } from '../exec.js';
5
5
  import { logsRoot } from '../paths.js';
6
- import { unitFor } from '../supervisor/systemd.js';
7
6
  import { ROLE_NAME_RE } from '../config.js';
8
7
  import { FleetError } from './errors.js';
9
8
  const STANDARD_REDACTIONS = [
@@ -78,12 +77,13 @@ export class StructuredLogService {
78
77
  }
79
78
  async tail(roleId, requested, cursor) {
80
79
  const limit = Math.min(Math.max(requested, 1), 1_000);
81
- const args = this.backend.id === 'systemd'
82
- ? {
83
- cmd: 'journalctl',
84
- args: ['--user', '-u', unitFor(roleId), '-o', 'json', '--no-pager', '-n', String(limit)],
85
- }
86
- : this.backend.logsArgs(roleId, false);
80
+ const source = this.backend.logsArgs(roleId, false);
81
+ const args = { cmd: source.cmd, args: [...source.args] };
82
+ const count = args.args.indexOf('-n');
83
+ if (count >= 0)
84
+ args.args[count + 1] = String(limit);
85
+ if (args.cmd === 'journalctl')
86
+ args.args.push('-o', 'json', '--no-pager', '-n', String(limit));
87
87
  if (!['journalctl', 'tail', 'tmux'].includes(args.cmd))
88
88
  throw new FleetError('capability_unavailable', 'log backend is unsupported');
89
89
  const result = await this.exec(args.cmd, args.args);
@@ -93,7 +93,7 @@ export class StructuredLogService {
93
93
  const parsed = lines.map((line, index) => {
94
94
  let text = line;
95
95
  let at;
96
- if (this.backend.id === 'systemd') {
96
+ if (args.cmd === 'journalctl') {
97
97
  try {
98
98
  const json = JSON.parse(line);
99
99
  text = typeof json.MESSAGE === 'string' ? json.MESSAGE : line;
@@ -14,7 +14,7 @@ export interface HarnessModelCatalog {
14
14
  export declare function codexModelCatalog(path?: string): HarnessModelCatalog;
15
15
  /**
16
16
  * Claude Code 2.1 has no typed model-list endpoint. These exact IDs are the
17
- * versioned adapter contract shipped by the installed 2.1 CLI, not aliases and
17
+ * versioned adapter contract verified with CLI 2.1.281, not aliases and
18
18
  * not a claim about account entitlement (which Claude validates at launch).
19
19
  */
20
20
  export declare function claudeModelCatalog(): HarnessModelCatalog;
@@ -41,17 +41,20 @@ export function codexModelCatalog(path = join(homedir(), '.codex', 'models_cache
41
41
  }
42
42
  /**
43
43
  * Claude Code 2.1 has no typed model-list endpoint. These exact IDs are the
44
- * versioned adapter contract shipped by the installed 2.1 CLI, not aliases and
44
+ * versioned adapter contract verified with CLI 2.1.281, not aliases and
45
45
  * not a claim about account entitlement (which Claude validates at launch).
46
46
  */
47
47
  export function claudeModelCatalog() {
48
48
  const reasoningEfforts = ['low', 'medium', 'high', 'xhigh', 'max'];
49
49
  return {
50
50
  models: [
51
+ { id: 'claude-fable-5-1', label: 'Claude Fable 5.1', reasoningEfforts, source: 'claude-adapter-2.1' },
52
+ { id: 'claude-opus-5-5', label: 'Claude Opus 5.5', reasoningEfforts, source: 'claude-adapter-2.1' },
53
+ { id: 'claude-haiku-4-5-20251001', label: 'Claude Haiku 4.5', reasoningEfforts: [], source: 'claude-adapter-2.1' },
51
54
  { id: 'claude-fable-5', label: 'Claude Fable 5', reasoningEfforts, source: 'claude-adapter-2.1' },
52
55
  { id: 'claude-opus-5', label: 'Claude Opus 5', reasoningEfforts, source: 'claude-adapter-2.1' },
53
56
  { id: 'claude-sonnet-5', label: 'Claude Sonnet 5', reasoningEfforts, source: 'claude-adapter-2.1' },
54
57
  ],
55
- warnings: ['Claude Code exposes no typed model-list endpoint; choices are exact 2.1 adapter IDs and entitlement is validated at launch.'],
58
+ warnings: ['Claude Code exposes no typed model-list endpoint; choices require a current Claude runtime (verified with 2.1.281); older bundled SDKs need CLAUDE_CODE_EXECUTABLE. Entitlement is validated at launch.'],
56
59
  };
57
60
  }
@@ -0,0 +1,19 @@
1
+ export declare const PROVENANCE_KINDS: {
2
+ readonly role: "roles";
3
+ readonly brain: "brains";
4
+ readonly template: "agent_templates";
5
+ readonly layout: "room_layouts";
6
+ };
7
+ export type ProvenanceKind = keyof typeof PROVENANCE_KINDS;
8
+ export type ProvenanceStatus = 'predefined' | 'changed' | 'custom';
9
+ export interface ProvenanceEntry {
10
+ status: ProvenanceStatus;
11
+ preset?: string;
12
+ }
13
+ export type Provenance = Record<ProvenanceKind, Record<string, ProvenanceEntry>>;
14
+ export declare class PresetProvenance {
15
+ private configPath?;
16
+ private presetRoot;
17
+ constructor(configPath?: string | undefined, presetRoot?: string);
18
+ read(): Provenance;
19
+ }
@@ -0,0 +1,59 @@
1
+ /** Where each configured definition came from, compared with the packaged presets.
2
+ * A definition is `predefined` when it has the same value as the shipped preset of
3
+ * the same name, `changed` when a preset of that name exists but the value differs,
4
+ * and `custom` otherwise. Values are compared after parsing, so the formatting the
5
+ * configuration editor writes does not count as a change. Preset text is returned
6
+ * so users can see exactly what ships; it contains no host data.
7
+ */
8
+ import { isDeepStrictEqual } from 'node:util';
9
+ import { existsSync, lstatSync, readFileSync, readdirSync } from 'node:fs';
10
+ import { basename, extname, join } from 'node:path';
11
+ import { parse } from 'yaml';
12
+ import { defaultConfigPath } from '../paths.js';
13
+ import { splitRootFor } from '../config.js';
14
+ import { packagedPresetRoot } from '../preset-bootstrap.js';
15
+ export const PROVENANCE_KINDS = { role: 'roles', brain: 'brains', template: 'agent_templates', layout: 'room_layouts' };
16
+ const MAX_BYTES = 256 * 1024;
17
+ /** Regular, bounded YAML files of one directory, by definition name. Anything else is skipped. */
18
+ function yamlFiles(dir) {
19
+ const files = new Map();
20
+ if (!existsSync(dir) || !lstatSync(dir).isDirectory())
21
+ return files;
22
+ for (const file of readdirSync(dir).sort()) {
23
+ if (!/\.ya?ml$/.test(file))
24
+ continue;
25
+ const path = join(dir, file), stat = lstatSync(path);
26
+ if (!stat.isFile() || stat.size > MAX_BYTES)
27
+ continue;
28
+ files.set(basename(file, extname(file)), readFileSync(path, 'utf8'));
29
+ }
30
+ return files;
31
+ }
32
+ const parsed = (text) => { try {
33
+ return parse(text);
34
+ }
35
+ catch {
36
+ return Symbol('unparseable');
37
+ } };
38
+ export class PresetProvenance {
39
+ configPath;
40
+ presetRoot;
41
+ constructor(configPath, presetRoot = join(packagedPresetRoot(), 'fleet')) {
42
+ this.configPath = configPath;
43
+ this.presetRoot = presetRoot;
44
+ }
45
+ read() {
46
+ const split = splitRootFor(this.configPath ?? defaultConfigPath());
47
+ const result = {};
48
+ for (const [kind, dir] of Object.entries(PROVENANCE_KINDS)) {
49
+ const presets = yamlFiles(join(this.presetRoot, dir)), installed = yamlFiles(join(split, dir));
50
+ result[kind] = Object.fromEntries([...installed].map(([name, text]) => {
51
+ const preset = presets.get(name);
52
+ if (preset === undefined)
53
+ return [name, { status: 'custom' }];
54
+ return [name, { status: isDeepStrictEqual(parsed(text), parsed(preset)) ? 'predefined' : 'changed', preset }];
55
+ }));
56
+ }
57
+ return result;
58
+ }
59
+ }
@@ -0,0 +1,40 @@
1
+ import { type FSWatcher } from 'node:fs';
2
+ export interface FleetResourceState {
3
+ protocolVersion: 1;
4
+ producerGeneration: string;
5
+ position: number;
6
+ health: 'healthy' | 'failed' | 'unsupported';
7
+ }
8
+ export interface FleetResourceScope {
9
+ configPath?: string;
10
+ subscribeMutations?: (listener: () => void) => () => void;
11
+ /** Durable notification owners may reinstall degraded watches without idle content polling. */
12
+ retryWatchFailures?: boolean;
13
+ roots?: string[];
14
+ /** Overrides full Fleet discovery for narrow owners such as task delivery. */
15
+ paths?: string[];
16
+ watch?: (path: string, listener: (event: string, filename: string | null) => void) => Pick<FSWatcher, 'on' | 'close'>;
17
+ }
18
+ /** Only call after durable persistence succeeds. Observers cannot fail a commit. */
19
+ export declare function publishFleetResourceCommit(path: string): void;
20
+ /** Native notifications cover supported local mounts. No idle content scan or
21
+ * silent claim of coverage after watch failure. Parents survive atomic rename.
22
+ * Discovery reads directory metadata only, never opens a controller or transcript. */
23
+ export declare function observeFleetResources(scope: FleetResourceScope, onCommitted: (state: FleetResourceState) => void, signal: AbortSignal): {
24
+ snapshot: () => {
25
+ health: "healthy" | "failed" | "unsupported";
26
+ protocolVersion: 1;
27
+ producerGeneration: string;
28
+ position: number;
29
+ };
30
+ cursor: (accepts: (path: string) => boolean, lifetime: AbortSignal) => {
31
+ snapshot: () => {
32
+ position: number;
33
+ health: "healthy" | "failed" | "unsupported";
34
+ protocolVersion: 1;
35
+ producerGeneration: string;
36
+ };
37
+ dispose: () => void;
38
+ };
39
+ dispose: () => void;
40
+ };
@@ -0,0 +1,278 @@
1
+ import { EventEmitter } from 'node:events';
2
+ import { randomUUID } from 'node:crypto';
3
+ import { lstatSync, statSync, readdirSync, watch } from 'node:fs';
4
+ import { homedir } from 'node:os';
5
+ import { dirname, join, resolve, sep } from 'node:path';
6
+ import { splitRootFor } from '../config.js';
7
+ import { agentsRoot, tmpRoot, stateRoot, defaultConfigPath } from '../paths.js';
8
+ // Directory discovery is shared by payload and notification owners. Each owner
9
+ // has its own lifetime/coverage; the underlying OS watch is reference counted.
10
+ const sharedWatches = new Map();
11
+ function sharedWatch(path, listener) {
12
+ const key = `${path}:${identity(path, true)}`;
13
+ let entry = sharedWatches.get(key);
14
+ if (!entry) {
15
+ const clients = new Set(), listeners = new Map();
16
+ const handle = watch(path, { persistent: false }, (event, name) => { for (const fn of [...listeners.values()])
17
+ fn(event, name); });
18
+ entry = { handle, clients, listeners };
19
+ sharedWatches.set(key, entry);
20
+ handle.on('error', error => { for (const client of [...clients])
21
+ client.emit('error', error); });
22
+ handle.on('close', () => { for (const client of [...clients])
23
+ client.emit('close'); });
24
+ }
25
+ const current = entry, client = new EventEmitter();
26
+ let closed = false;
27
+ current.clients.add(client);
28
+ current.listeners.set(client, listener);
29
+ return Object.assign(client, { close() {
30
+ if (closed)
31
+ return;
32
+ closed = true;
33
+ current.clients.delete(client);
34
+ current.listeners.delete(client);
35
+ if (!current.clients.size) {
36
+ sharedWatches.delete(key);
37
+ current.handle.close();
38
+ }
39
+ } });
40
+ }
41
+ const commits = new Set();
42
+ /** Only call after durable persistence succeeds. Observers cannot fail a commit. */
43
+ export function publishFleetResourceCommit(path) {
44
+ for (const listener of [...commits]) {
45
+ try {
46
+ listener(resolve(path));
47
+ }
48
+ catch { }
49
+ }
50
+ }
51
+ function directoryGone(error) {
52
+ return ['ENOENT', 'ENOTDIR'].includes(error.code ?? '');
53
+ }
54
+ function identity(path, followLink = false) {
55
+ try {
56
+ const value = followLink ? statSync(path) : lstatSync(path);
57
+ if (value.isSymbolicLink())
58
+ throw Error('symbolic resource directory');
59
+ return value.isDirectory() ? `${value.dev}:${value.ino}:${value.birthtimeMs}` : undefined;
60
+ }
61
+ catch (error) {
62
+ if (directoryGone(error))
63
+ return;
64
+ throw error;
65
+ }
66
+ }
67
+ function within(path, root) { return path === root || path.startsWith(root + sep); }
68
+ /** Native notifications cover supported local mounts. No idle content scan or
69
+ * silent claim of coverage after watch failure. Parents survive atomic rename.
70
+ * Discovery reads directory metadata only, never opens a controller or transcript. */
71
+ export function observeFleetResources(scope, onCommitted, signal) {
72
+ const config = resolve(scope.configPath ?? defaultConfigPath());
73
+ const roots = (scope.roots ?? [agentsRoot(), tmpRoot()]).map(p => resolve(p));
74
+ const paths = (scope.paths ?? (scope.roots && !scope.configPath ? roots : [...roots, splitRootFor(config), join(stateRoot(), 'tasks'), join(stateRoot(), 'rooms'), join(stateRoot(), 'watchdogs'), join(stateRoot(), 'layouts'), join(stateRoot(), 'member-additions'), join(stateRoot(), 'subscriptions')])).map(p => resolve(p));
75
+ // Explicit roots without config select discovery only (notification producers).
76
+ const configEnabled = scope.configPath !== undefined || (!scope.paths && !scope.roots);
77
+ const adoptedHomes = configEnabled ? [join(homedir(), '.claude'), join(homedir(), '.codex')] : [];
78
+ if (configEnabled)
79
+ paths.push(dirname(config), resolve(stateRoot()), ...adoptedHomes);
80
+ // Explicit resource locations and their ancestors already belong to the
81
+ // caller's read scope (e.g. /home -> /var/home or a linked state root).
82
+ // Discovered descendants keep lstat validation and never traverse child links.
83
+ const trustedPaths = new Set(paths);
84
+ for (const path of paths) {
85
+ let parent = path;
86
+ for (let depth = 0; depth < 4 && dirname(parent) !== parent; depth++, parent = dirname(parent))
87
+ trustedPaths.add(parent);
88
+ }
89
+ const resourceIdentity = (path) => identity(path, trustedPaths.has(path));
90
+ let state = { protocolVersion: 1, producerGeneration: randomUUID(), position: 0, health: 'healthy' };
91
+ let disposed = signal.aborted, queued = false;
92
+ let recovery;
93
+ let installing = false, installFailure;
94
+ const cursors = new Set();
95
+ const watchers = new Map();
96
+ const release = () => { const old = [...watchers.values()]; watchers.clear(); for (const entry of old)
97
+ entry.handle.close(); };
98
+ const fail = (error) => {
99
+ if (installing) {
100
+ installFailure = error;
101
+ return;
102
+ }
103
+ if (disposed || state.health !== 'healthy')
104
+ return;
105
+ state = { ...state, position: state.position + 1, health: ['ENOSYS', 'ENOTSUP', 'ERR_FEATURE_UNAVAILABLE_ON_PLATFORM'].includes(error.code ?? '') ? 'unsupported' : 'failed' };
106
+ release();
107
+ onCommitted({ ...state });
108
+ recover();
109
+ };
110
+ const install = () => {
111
+ installing = true;
112
+ installFailure = undefined;
113
+ try {
114
+ const wanted = new Set();
115
+ const cover = (dir) => {
116
+ const id = resourceIdentity(dir);
117
+ if (!id)
118
+ return;
119
+ wanted.add(dir);
120
+ const existing = watchers.get(dir);
121
+ if (existing && existing.identity === id)
122
+ return;
123
+ if (existing) {
124
+ watchers.delete(dir);
125
+ existing.handle.close();
126
+ }
127
+ let handle;
128
+ try {
129
+ handle = (scope.watch ?? sharedWatch)(dir, (_event, filename) => {
130
+ if (watchers.get(dir)?.handle !== handle)
131
+ return;
132
+ // Ignore sibling noise from ancestor coverage, lock churn and delivery acknowledgements.
133
+ const changed = filename === null ? dir : resolve(dir, String(filename));
134
+ if (changed.split(sep).some(name => name === '.locks' || name.endsWith('.lock') || name.endsWith('.tmp')))
135
+ return;
136
+ if (paths.some(path => within(changed, path) || within(path, changed)) || (configEnabled && changed === config) || filename === null)
137
+ wake(filename === null ? undefined : changed);
138
+ });
139
+ }
140
+ catch (error) {
141
+ // Parent coverage owns removal/recreation. Only verified disappearance
142
+ // is benign; permissions, unsupported watches and other errors fail.
143
+ if (directoryGone(error) && !resourceIdentity(dir))
144
+ return;
145
+ throw error;
146
+ }
147
+ watchers.set(dir, { identity: id, handle });
148
+ handle.on('error', (error) => { if (watchers.get(dir)?.handle === handle)
149
+ fail(error); });
150
+ handle.on('close', () => { if (watchers.get(dir)?.handle === handle)
151
+ fail(Error('resource watch closed')); });
152
+ if (installFailure)
153
+ throw installFailure;
154
+ };
155
+ const visit = (dir, depth) => {
156
+ const id = resourceIdentity(dir);
157
+ if (!id)
158
+ return;
159
+ // Watch before enumerating children: creation before watch is covered by
160
+ // this scan; creation after watch schedules another discovery pass.
161
+ cover(dir);
162
+ if (!depth)
163
+ return;
164
+ let entries;
165
+ try {
166
+ entries = readdirSync(dir, { withFileTypes: true });
167
+ }
168
+ catch (error) {
169
+ if (directoryGone(error) && !resourceIdentity(dir))
170
+ return;
171
+ throw error;
172
+ }
173
+ for (const entry of entries) {
174
+ if (!entry.isDirectory() || entry.isSymbolicLink() || entry.name === '.locks' || entry.name.endsWith('.lock'))
175
+ continue;
176
+ visit(join(dir, entry.name), depth - 1);
177
+ }
178
+ };
179
+ for (const path of paths) {
180
+ // Enough ancestors to retain the subscription when its state root is replaced.
181
+ let parent = path;
182
+ for (let depth = 0; depth < 4 && dirname(parent) !== parent; depth++, parent = dirname(parent)) {
183
+ if (resourceIdentity(parent)) {
184
+ wanted.add(parent);
185
+ cover(parent);
186
+ }
187
+ }
188
+ visit(path, configEnabled && (path === dirname(config) || adoptedHomes.includes(path)) ? 0 : 3);
189
+ }
190
+ for (const [dir, entry] of watchers) {
191
+ if (!wanted.has(dir) || resourceIdentity(dir) !== entry.identity) {
192
+ watchers.delete(dir);
193
+ entry.handle.close();
194
+ }
195
+ }
196
+ if (installFailure)
197
+ throw installFailure;
198
+ }
199
+ finally {
200
+ installing = false;
201
+ }
202
+ };
203
+ const recover = () => {
204
+ if (disposed || state.health === 'healthy' || !scope.retryWatchFailures || recovery)
205
+ return;
206
+ recovery = setTimeout(() => {
207
+ recovery = undefined;
208
+ if (disposed)
209
+ return;
210
+ try {
211
+ install();
212
+ }
213
+ catch {
214
+ release();
215
+ recover();
216
+ return;
217
+ }
218
+ state = { ...state, position: state.position + 1, health: 'healthy' };
219
+ // Reinstallation precedes one checkpoint recovery pass; later work is
220
+ // event driven again. Degraded retries inspect directory metadata only.
221
+ onCommitted({ ...state });
222
+ }, 3000);
223
+ recovery.unref();
224
+ };
225
+ const wake = (path) => {
226
+ if (disposed || state.health !== 'healthy')
227
+ return;
228
+ state = { ...state, position: state.position + 1 };
229
+ for (const cursor of cursors)
230
+ if (path === undefined || cursor.accepts(path))
231
+ cursor.position = state.position;
232
+ if (queued)
233
+ return;
234
+ queued = true;
235
+ queueMicrotask(() => {
236
+ queued = false;
237
+ if (disposed || state.health !== 'healthy')
238
+ return;
239
+ try {
240
+ install();
241
+ }
242
+ catch (error) {
243
+ fail(error);
244
+ return;
245
+ }
246
+ onCommitted({ ...state });
247
+ });
248
+ };
249
+ const direct = (path) => { if (paths.some(root => within(path, root)) || (configEnabled && path === config))
250
+ wake(path); };
251
+ let detach;
252
+ const dispose = () => { if (disposed)
253
+ return; disposed = true; clearTimeout(recovery); recovery = undefined; detach?.(); for (const cursor of [...cursors])
254
+ cursor.dispose(); commits.delete(direct); signal.removeEventListener('abort', dispose); release(); };
255
+ if (!disposed) {
256
+ commits.add(direct);
257
+ detach = scope.subscribeMutations?.(wake);
258
+ signal.addEventListener('abort', dispose, { once: true });
259
+ try {
260
+ install();
261
+ }
262
+ catch (error) {
263
+ fail(error);
264
+ }
265
+ }
266
+ const snapshot = () => ({ ...state, ...(disposed ? { health: 'failed' } : {}) });
267
+ // View-local cursors share coverage and health, but unrelated file commits
268
+ // cannot invalidate a capture. Unknown wakes remain conservative.
269
+ const cursor = (accepts, lifetime) => {
270
+ const entry = { accepts, position: state.position, dispose() { cursors.delete(entry); lifetime.removeEventListener('abort', entry.dispose); } };
271
+ if (!disposed && !lifetime.aborted) {
272
+ cursors.add(entry);
273
+ lifetime.addEventListener('abort', entry.dispose, { once: true });
274
+ }
275
+ return { snapshot: () => ({ ...snapshot(), position: entry.position }), dispose: entry.dispose };
276
+ };
277
+ return { snapshot, cursor, dispose };
278
+ }
@@ -59,7 +59,8 @@ export class RoleCreationService {
59
59
  }
60
60
  async createManaged(caller, requested) {
61
61
  const plan = this.previewSpawn({ origin: 'managed', caller, options: requested });
62
- const creationActionId = randomUUID();
62
+ const creationActionId = requested.roomMemberStartup?.task_id
63
+ ? requested.creationActionId ?? randomUUID() : randomUUID();
63
64
  plan.options.creationActionId = creationActionId;
64
65
  const statePath = await this.launchSync(plan.options);
65
66
  const selectionSummary = (kind) => {
@@ -138,7 +139,7 @@ export class RoleCreationService {
138
139
  reasoningEffort: resolved.effort,
139
140
  cwd, lifetime: request.lifetime,
140
141
  permissions: resolvePermissions(defaults.permissions, request.permissions),
141
- monitor: resolveMonitorConfig(defaults.monitor, request.monitor),
142
+ monitor: resolveMonitorConfig(defaults.monitor, request.monitor, { harness: resolved.harness }),
142
143
  };
143
144
  const warnings = [];
144
145
  if (request.permissions.approval === 'allow')
@@ -1,9 +1,11 @@
1
+ import { retireNotificationTarget } from '../notifications/target-cleanup.js';
1
2
  import { cpSync, existsSync, mkdirSync, readdirSync, renameSync } from 'node:fs';
2
3
  import { basename, dirname, join } from 'node:path';
3
4
  import { loadConfig, ROLE_NAME_RE } from '../config.js';
4
5
  import { agentDir, stateRoot } from '../paths.js';
5
- import { rmRole } from '../ops.js';
6
+ import { assertStandaloneRoleRemoval, rmRole } from '../ops.js';
6
7
  import { FleetError } from './errors.js';
8
+ import { removeReminderState } from '../reminders/store.js';
7
9
  import { provesGeneratedAgentSource } from '../generated-agent-source.js';
8
10
  export class RoleRemovalService {
9
11
  options;
@@ -18,6 +20,12 @@ export class RoleRemovalService {
18
20
  if (!ROLE_NAME_RE.test(requested))
19
21
  throw new FleetError('invalid_request', 'invalid role name');
20
22
  const cfg = loadConfig(this.options.configPath);
23
+ try {
24
+ assertStandaloneRoleRemoval(cfg, requested);
25
+ }
26
+ catch (error) {
27
+ throw new FleetError('conflict', error instanceof Error ? error.message : String(error));
28
+ }
21
29
  const names = new Set(cfg.roles.map(role => role.name));
22
30
  for (const root of [dirname(agentDir('_')), dirname(agentDir('_', true))]) {
23
31
  try {
@@ -76,8 +84,11 @@ export class RoleRemovalService {
76
84
  const stamp = new Date().toISOString().replaceAll(/[:.]/g, '-');
77
85
  const recoveryPath = join(stateRoot(), 'recovery', 'removed', `${stamp}-${preview.role}`);
78
86
  mkdirSync(recoveryPath, { recursive: true, mode: 0o700 });
79
- if (existsSync(sourceState))
87
+ if (existsSync(sourceState)) {
80
88
  cpSync(sourceState, join(recoveryPath, 'state'), { recursive: true });
89
+ // Recovery evidence is not a schedule: a removed agent's reminders end here.
90
+ removeReminderState(join(recoveryPath, 'state'));
91
+ }
81
92
  if (role && provesGeneratedAgentSource(agentDir(preview.role), cfg.files[0], role.sourceFile)
82
93
  && existsSync(role.sourceFile))
83
94
  cpSync(role.sourceFile, join(recoveryPath, basename(role.sourceFile)));
@@ -85,8 +96,11 @@ export class RoleRemovalService {
85
96
  await rmRole(cfg, preview.role, this.options.ops);
86
97
  else {
87
98
  await this.options.ops.backend.uninstall(preview.role);
88
- if (existsSync(sourceState))
99
+ if (existsSync(sourceState)) {
89
100
  renameSync(sourceState, join(recoveryPath, 'orphan-state'));
101
+ removeReminderState(join(recoveryPath, 'orphan-state'));
102
+ }
103
+ await retireNotificationTarget(`/fleet/chats/agents/${encodeURIComponent(preview.role)}`).catch(() => { });
90
104
  this.options.ops.log(`removed orphaned backend/state '${preview.role}'`);
91
105
  }
92
106
  return { ...preview, removed: true, recoveryPath };
@@ -14,7 +14,7 @@ export interface RoleRepositoryOptions {
14
14
  export declare class RoleRepository {
15
15
  private readonly options;
16
16
  constructor(options?: RoleRepositoryOptions);
17
- list(): Promise<RoleRecord[]>;
17
+ list(strict?: boolean): Promise<RoleRecord[]>;
18
18
  get(id: string): Promise<RoleRecord | undefined>;
19
19
  stateDir(record: RoleRecord): string | undefined;
20
20
  private detect;
@@ -1,17 +1,34 @@
1
1
  import { lstatSync, readFileSync, readdirSync, realpathSync } from 'node:fs';
2
2
  import { join, relative } from 'node:path';
3
3
  import { parse } from 'yaml';
4
- import { loadConfig, resolveMonitorConfig, resolvePermissions, ROLE_NAME_RE, } from '../config.js';
4
+ import { loadConfig, resolveSnapshotMonitor, resolvePermissions, ROLE_NAME_RE, } from '../config.js';
5
5
  import { agentsRoot, tmpRoot } from '../paths.js';
6
6
  import { readSpawnLineage } from '../web/topology.js';
7
7
  const MAX_SNAPSHOT_BYTES = 256 * 1024;
8
- function safeDirs(root) {
8
+ function retainedTaskId(dir, name) {
9
+ try {
10
+ const path = join(dir, '.temp-supervisor.json');
11
+ const stat = lstatSync(path);
12
+ if (!stat.isFile() || stat.isSymbolicLink() || stat.size > 16 * 1024)
13
+ return undefined;
14
+ const record = JSON.parse(readFileSync(path, 'utf8'));
15
+ const id = record?.taskOwner?.taskId;
16
+ return record?.version === 1 && record?.role === name && typeof id === 'string'
17
+ && /^[0-9a-z]{9}[0-9a-f]{8}$/.test(id) ? id : undefined;
18
+ }
19
+ catch {
20
+ return undefined;
21
+ }
22
+ }
23
+ function safeDirs(root, strict = false) {
9
24
  try {
10
25
  return readdirSync(root, { withFileTypes: true })
11
26
  .filter(entry => entry.isDirectory() && !entry.isSymbolicLink() && ROLE_NAME_RE.test(entry.name))
12
27
  .map(entry => entry.name);
13
28
  }
14
- catch {
29
+ catch (error) {
30
+ if (strict && error.code !== 'ENOENT')
31
+ throw error;
15
32
  return [];
16
33
  }
17
34
  }
@@ -54,7 +71,7 @@ function snapshot(path, name) {
54
71
  session, identity: typeof raw.identity === 'string' ? raw.identity : name,
55
72
  permissions: resolvePermissions(undefined, raw.permissions),
56
73
  permissionsDeclared: raw.permissions !== undefined,
57
- monitor: resolveMonitorConfig(undefined, raw.monitor),
74
+ monitor: resolveSnapshotMonitor(raw.monitor),
58
75
  sourceFile: '(temp)',
59
76
  },
60
77
  };
@@ -86,7 +103,7 @@ export class RoleRepository {
86
103
  constructor(options = {}) {
87
104
  this.options = options;
88
105
  }
89
- async list() {
106
+ async list(strict = false) {
90
107
  const permanentRoot = this.options.permanentRoot ?? agentsRoot();
91
108
  const temporaryRoot = this.options.temporaryRoot ?? tmpRoot();
92
109
  let configured = [];
@@ -95,10 +112,12 @@ export class RoleRepository {
95
112
  configured = loadConfig(this.options.configPath).roles;
96
113
  }
97
114
  catch (error) {
115
+ if (strict)
116
+ throw error;
98
117
  configProblem = { code: 'config_invalid', severity: 'error', source: 'config', detail: error.message };
99
118
  }
100
- const permanent = new Set(safeDirs(permanentRoot));
101
- const temporary = new Set(safeDirs(temporaryRoot));
119
+ const permanent = new Set(safeDirs(permanentRoot, strict));
120
+ const temporary = new Set(safeDirs(temporaryRoot, strict));
102
121
  const configuredByName = new Map(configured.map(role => [role.name, role]));
103
122
  const names = [...new Set([...configuredByName.keys(), ...permanent, ...temporary])].sort();
104
123
  return boundedMap(names, this.options.concurrency ?? 8, async (name) => {
@@ -111,6 +130,8 @@ export class RoleRepository {
111
130
  if (inTemporary) {
112
131
  const read = snapshot(join(temporaryRoot, name, 'role.yaml'), name);
113
132
  tempRole = read.role;
133
+ if (strict && read.problem)
134
+ throw new Error('temporary role snapshot unavailable');
114
135
  if (read.problem) {
115
136
  problems.push(read.problem);
116
137
  stateHealth = 'corrupt';
@@ -128,10 +149,12 @@ export class RoleRepository {
128
149
  const stateRef = inTemporary ? { lifetime: 'temporary' }
129
150
  : inPermanent ? { lifetime: 'permanent' } : undefined;
130
151
  const tempStateDir = inTemporary ? join(temporaryRoot, name) : undefined;
152
+ const taskId = !config && tempStateDir ? retainedTaskId(tempStateDir, name) : undefined;
131
153
  return {
132
154
  id: name,
133
155
  lifetime: config ? 'permanent' : inTemporary ? 'temporary' : 'orphan',
134
156
  configured: Boolean(config),
157
+ ...(taskId ? { taskId } : {}),
135
158
  config: config ? view(config) : tempRole ? view(tempRole) : undefined,
136
159
  stateRef,
137
160
  stateHealth,