@ours.network/fleet 1.2.0-nightly.9 → 2.0.1

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 (429) hide show
  1. package/README.md +280 -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 +22 -0
  11. package/dist/agent-ours/mcp-endpoint.js +101 -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 +27 -1
  15. package/dist/agent-ours/service.js +107 -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 +31 -0
  19. package/dist/application/conversation-history.js +339 -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 +3 -1
  34. package/dist/application/role-repository.js +89 -54
  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 +5 -1
  48. package/dist/briefing.js +138 -31
  49. package/dist/build-info.json +10 -5
  50. package/dist/capabilities.d.ts +9 -1
  51. package/dist/capabilities.js +13 -0
  52. package/dist/cli.js +391 -38
  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 +402 -20
  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 +13 -0
  74. package/dist/fleet-command-audit.js +31 -9
  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 +55 -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 +182 -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 +66 -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-review.d.ts +113 -0
  180. package/dist/rooms-tasks/task-review.js +561 -0
  181. package/dist/rooms-tasks/task-service-retirement.d.ts +11 -0
  182. package/dist/rooms-tasks/task-service-retirement.js +21 -0
  183. package/dist/rooms-tasks/task-state.d.ts +51 -5
  184. package/dist/rooms-tasks/task-state.js +358 -50
  185. package/dist/rooms-tasks/task-tools.d.ts +35 -0
  186. package/dist/rooms-tasks/task-tools.js +134 -0
  187. package/dist/rooms-tasks/types.d.ts +78 -0
  188. package/dist/rooms-tasks/workspace-artifacts.d.ts +6 -0
  189. package/dist/rooms-tasks/workspace-artifacts.js +93 -0
  190. package/dist/rooms-tasks/workspace.d.ts +35 -0
  191. package/dist/rooms-tasks/workspace.js +325 -0
  192. package/dist/runner.d.ts +23 -4
  193. package/dist/runner.js +409 -61
  194. package/dist/session/acp.d.ts +75 -2
  195. package/dist/session/acp.js +345 -82
  196. package/dist/session/arbiter.d.ts +98 -0
  197. package/dist/session/arbiter.js +272 -3
  198. package/dist/session/codex-app-server.d.ts +45 -1
  199. package/dist/session/codex-app-server.js +225 -32
  200. package/dist/session/control.d.ts +17 -3
  201. package/dist/session/control.js +219 -23
  202. package/dist/session/conversation-normalizer.d.ts +2 -1
  203. package/dist/session/conversation-normalizer.js +1 -1
  204. package/dist/session/conversation-observation.d.ts +25 -0
  205. package/dist/session/conversation-observation.js +100 -0
  206. package/dist/session/conversation-replay.d.ts +50 -0
  207. package/dist/session/conversation-replay.js +203 -0
  208. package/dist/session/conversation-store.d.ts +15 -1
  209. package/dist/session/conversation-store.js +213 -26
  210. package/dist/session/conversation-types.d.ts +57 -4
  211. package/dist/session/prompt-batch.d.ts +79 -0
  212. package/dist/session/prompt-batch.js +120 -0
  213. package/dist/session/task-notice.d.ts +14 -0
  214. package/dist/session/task-notice.js +13 -0
  215. package/dist/session/types.d.ts +42 -1
  216. package/dist/setup-tunnel-preflight.d.ts +34 -0
  217. package/dist/setup-tunnel-preflight.js +62 -0
  218. package/dist/socket-path.d.ts +2 -0
  219. package/dist/socket-path.js +22 -0
  220. package/dist/spawn.d.ts +5 -1
  221. package/dist/spawn.js +40 -4
  222. package/dist/startup-prompt.d.ts +15 -0
  223. package/dist/startup-prompt.js +71 -0
  224. package/dist/startup-readiness.d.ts +20 -1
  225. package/dist/startup-readiness.js +66 -3
  226. package/dist/startup-telemetry.d.ts +16 -0
  227. package/dist/startup-telemetry.js +46 -0
  228. package/dist/subscriptions/cli.d.ts +91 -0
  229. package/dist/subscriptions/cli.js +223 -0
  230. package/dist/subscriptions/launch.d.ts +37 -0
  231. package/dist/subscriptions/launch.js +93 -0
  232. package/dist/subscriptions/login.d.ts +77 -0
  233. package/dist/subscriptions/login.js +215 -0
  234. package/dist/subscriptions/observed.d.ts +7 -0
  235. package/dist/subscriptions/observed.js +44 -0
  236. package/dist/subscriptions/probe.d.ts +10 -0
  237. package/dist/subscriptions/probe.js +73 -0
  238. package/dist/subscriptions/service.d.ts +102 -0
  239. package/dist/subscriptions/service.js +364 -0
  240. package/dist/subscriptions/store.d.ts +71 -0
  241. package/dist/subscriptions/store.js +143 -0
  242. package/dist/supervisor/adoption.d.ts +8 -0
  243. package/dist/supervisor/adoption.js +78 -0
  244. package/dist/supervisor/catalog.d.ts +62 -0
  245. package/dist/supervisor/catalog.js +254 -0
  246. package/dist/supervisor/fleet.d.ts +8 -0
  247. package/dist/supervisor/fleet.js +89 -0
  248. package/dist/supervisor/index.js +3 -6
  249. package/dist/supervisor/launchd.d.ts +1 -1
  250. package/dist/supervisor/launchd.js +26 -13
  251. package/dist/supervisor/legacy.d.ts +35 -0
  252. package/dist/supervisor/legacy.js +524 -0
  253. package/dist/supervisor/manager.d.ts +15 -0
  254. package/dist/supervisor/manager.js +270 -0
  255. package/dist/supervisor/registration-refresh.d.ts +45 -0
  256. package/dist/supervisor/registration-refresh.js +266 -0
  257. package/dist/supervisor/scope.d.ts +4 -0
  258. package/dist/supervisor/scope.js +10 -0
  259. package/dist/supervisor/systemd.d.ts +2 -2
  260. package/dist/supervisor/systemd.js +18 -37
  261. package/dist/task-supervision.d.ts +9 -0
  262. package/dist/task-supervision.js +145 -0
  263. package/dist/temp-idle.d.ts +17 -0
  264. package/dist/temp-idle.js +42 -0
  265. package/dist/temp-lifecycle.d.ts +23 -8
  266. package/dist/temp-lifecycle.js +123 -66
  267. package/dist/temp-supervisor-recovery.d.ts +1 -1
  268. package/dist/temp-supervisor-recovery.js +2 -2
  269. package/dist/tunnel-setup-completion.d.ts +7 -0
  270. package/dist/tunnel-setup-completion.js +13 -0
  271. package/dist/tunnel-setup-input.d.ts +19 -0
  272. package/dist/tunnel-setup-input.js +130 -0
  273. package/dist/watchdog/query.d.ts +3 -1
  274. package/dist/watchdog/query.js +2 -1
  275. package/dist/web/agent-attachments.d.ts +29 -0
  276. package/dist/web/agent-attachments.js +162 -0
  277. package/dist/web/auth.d.ts +65 -2
  278. package/dist/web/auth.js +201 -11
  279. package/dist/web/available-port.d.ts +2 -0
  280. package/dist/web/available-port.js +23 -0
  281. package/dist/web/control.d.ts +3 -3
  282. package/dist/web/control.js +10 -8
  283. package/dist/web/conversation-stream-codec.d.ts +23 -0
  284. package/dist/web/conversation-stream-codec.js +110 -0
  285. package/dist/web/conversation-stream.d.ts +26 -0
  286. package/dist/web/conversation-stream.js +239 -0
  287. package/dist/web/events.d.ts +2 -0
  288. package/dist/web/events.js +9 -0
  289. package/dist/web/fleet-config-service.d.ts +7 -1
  290. package/dist/web/fleet-config-service.js +61 -16
  291. package/dist/web/prefix-gateway.d.ts +32 -0
  292. package/dist/web/prefix-gateway.js +248 -0
  293. package/dist/web/resource-views.d.ts +138 -0
  294. package/dist/web/resource-views.js +519 -0
  295. package/dist/web/runtime.d.ts +3 -1
  296. package/dist/web/runtime.js +132 -34
  297. package/dist/web/server.d.ts +19 -0
  298. package/dist/web/server.js +893 -71
  299. package/dist/web/service.d.ts +11 -2
  300. package/dist/web/service.js +30 -4
  301. package/dist/web/workspace-devices.d.ts +107 -0
  302. package/dist/web/workspace-devices.js +277 -0
  303. package/dist/web/workspace-tunnel.d.ts +1 -0
  304. package/dist/web/workspace-tunnel.js +34 -0
  305. package/dist/workspace-enrollment.d.ts +83 -0
  306. package/dist/workspace-enrollment.js +605 -0
  307. package/dist/workspace-replacement.d.ts +66 -0
  308. package/dist/workspace-replacement.js +238 -0
  309. package/docs/acp-files.md +146 -0
  310. package/docs/conversation-stream.md +216 -0
  311. package/docs/fleet-migration.md +54 -0
  312. package/docs/live-voice.md +13 -0
  313. package/docs/room-layouts.md +227 -0
  314. package/docs/self-reminders.md +374 -0
  315. package/docs/task-progress-review.md +143 -0
  316. package/docs/task-workspaces.md +225 -0
  317. package/docs/validation/managed-cli-permissions.md +421 -0
  318. package/examples/room-layouts/fresh.yaml +27 -0
  319. package/examples/room-layouts/scoped.yaml +21 -0
  320. package/examples/room-layouts/shared.yaml +19 -0
  321. package/package.json +25 -20
  322. package/presets/README.md +78 -0
  323. package/presets/brain-catalog.json +8 -2
  324. package/presets/fleet/agent_templates/Critic.yaml +1 -1
  325. package/presets/fleet/agent_templates/Developer.yaml +1 -1
  326. package/presets/fleet/agent_templates/Engineer.yaml +5 -0
  327. package/presets/fleet/agent_templates/LocalCoordinator.yaml +6 -3
  328. package/presets/fleet/agents/FleetCoordinator.yaml +1 -1
  329. package/presets/fleet/brains/claude-fable-5-1-high.yaml +5 -0
  330. package/presets/fleet/brains/claude-fable-5-1-low.yaml +5 -0
  331. package/presets/fleet/brains/claude-fable-5-1-max.yaml +5 -0
  332. package/presets/fleet/brains/claude-fable-5-1-medium.yaml +5 -0
  333. package/presets/fleet/brains/claude-fable-5-1-xhigh.yaml +5 -0
  334. package/presets/fleet/brains/claude-fable-5-high.yaml +1 -1
  335. package/presets/fleet/brains/claude-fable-5-low.yaml +1 -1
  336. package/presets/fleet/brains/claude-fable-5-max.yaml +1 -1
  337. package/presets/fleet/brains/claude-fable-5-medium.yaml +1 -1
  338. package/presets/fleet/brains/claude-fable-5-xhigh.yaml +1 -1
  339. package/presets/fleet/brains/claude-haiku-4-5-20251001-default.yaml +4 -0
  340. package/presets/fleet/brains/claude-opus-5-5-high.yaml +5 -0
  341. package/presets/fleet/brains/claude-opus-5-5-low.yaml +5 -0
  342. package/presets/fleet/brains/claude-opus-5-5-max.yaml +5 -0
  343. package/presets/fleet/brains/claude-opus-5-5-medium.yaml +5 -0
  344. package/presets/fleet/brains/claude-opus-5-5-xhigh.yaml +5 -0
  345. package/presets/fleet/brains/claude-opus-5-high.yaml +1 -1
  346. package/presets/fleet/brains/claude-opus-5-low.yaml +1 -1
  347. package/presets/fleet/brains/claude-opus-5-max.yaml +1 -1
  348. package/presets/fleet/brains/claude-opus-5-medium.yaml +1 -1
  349. package/presets/fleet/brains/claude-opus-5-xhigh.yaml +1 -1
  350. package/presets/fleet/brains/claude-sonnet-5-high.yaml +1 -1
  351. package/presets/fleet/brains/claude-sonnet-5-low.yaml +1 -1
  352. package/presets/fleet/brains/claude-sonnet-5-max.yaml +1 -1
  353. package/presets/fleet/brains/claude-sonnet-5-medium.yaml +1 -1
  354. package/presets/fleet/brains/claude-sonnet-5-xhigh.yaml +1 -1
  355. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-high.yaml +1 -1
  356. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-low.yaml +1 -1
  357. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-medium.yaml +1 -1
  358. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-xhigh.yaml +1 -1
  359. package/presets/fleet/brains/codex-gpt-5-4-high.yaml +1 -1
  360. package/presets/fleet/brains/codex-gpt-5-4-low.yaml +1 -1
  361. package/presets/fleet/brains/codex-gpt-5-4-medium.yaml +1 -1
  362. package/presets/fleet/brains/codex-gpt-5-4-mini-high.yaml +1 -1
  363. package/presets/fleet/brains/codex-gpt-5-4-mini-low.yaml +1 -1
  364. package/presets/fleet/brains/codex-gpt-5-4-mini-medium.yaml +1 -1
  365. package/presets/fleet/brains/codex-gpt-5-4-mini-xhigh.yaml +1 -1
  366. package/presets/fleet/brains/codex-gpt-5-4-xhigh.yaml +1 -1
  367. package/presets/fleet/brains/codex-gpt-5-5-high.yaml +1 -1
  368. package/presets/fleet/brains/codex-gpt-5-5-low.yaml +1 -1
  369. package/presets/fleet/brains/codex-gpt-5-5-medium.yaml +1 -1
  370. package/presets/fleet/brains/codex-gpt-5-5-xhigh.yaml +1 -1
  371. package/presets/fleet/brains/codex-gpt-5-6-luna-high.yaml +1 -1
  372. package/presets/fleet/brains/codex-gpt-5-6-luna-low.yaml +1 -1
  373. package/presets/fleet/brains/codex-gpt-5-6-luna-max.yaml +1 -1
  374. package/presets/fleet/brains/codex-gpt-5-6-luna-medium.yaml +1 -1
  375. package/presets/fleet/brains/codex-gpt-5-6-luna-xhigh.yaml +1 -1
  376. package/presets/fleet/brains/codex-gpt-5-6-sol-high.yaml +1 -1
  377. package/presets/fleet/brains/codex-gpt-5-6-sol-low.yaml +1 -1
  378. package/presets/fleet/brains/codex-gpt-5-6-sol-max.yaml +1 -1
  379. package/presets/fleet/brains/codex-gpt-5-6-sol-medium.yaml +1 -1
  380. package/presets/fleet/brains/codex-gpt-5-6-sol-ultra.yaml +1 -1
  381. package/presets/fleet/brains/codex-gpt-5-6-sol-xhigh.yaml +1 -1
  382. package/presets/fleet/brains/codex-gpt-5-6-terra-high.yaml +1 -1
  383. package/presets/fleet/brains/codex-gpt-5-6-terra-low.yaml +1 -1
  384. package/presets/fleet/brains/codex-gpt-5-6-terra-max.yaml +1 -1
  385. package/presets/fleet/brains/codex-gpt-5-6-terra-medium.yaml +1 -1
  386. package/presets/fleet/brains/codex-gpt-5-6-terra-ultra.yaml +1 -1
  387. package/presets/fleet/brains/codex-gpt-5-6-terra-xhigh.yaml +1 -1
  388. package/presets/fleet/brains/codex-gpt-6-1-sol-high.yaml +5 -0
  389. package/presets/fleet/brains/codex-gpt-6-1-sol-low.yaml +5 -0
  390. package/presets/fleet/brains/codex-gpt-6-1-sol-max.yaml +5 -0
  391. package/presets/fleet/brains/codex-gpt-6-1-sol-medium.yaml +5 -0
  392. package/presets/fleet/brains/codex-gpt-6-1-sol-ultra.yaml +5 -0
  393. package/presets/fleet/brains/codex-gpt-6-1-sol-xhigh.yaml +5 -0
  394. package/presets/fleet/brains/codex-gpt-6-astra-high.yaml +1 -1
  395. package/presets/fleet/brains/codex-gpt-6-astra-low.yaml +1 -1
  396. package/presets/fleet/brains/codex-gpt-6-astra-max.yaml +1 -1
  397. package/presets/fleet/brains/codex-gpt-6-astra-medium.yaml +1 -1
  398. package/presets/fleet/brains/codex-gpt-6-astra-ultra.yaml +1 -1
  399. package/presets/fleet/brains/codex-gpt-6-astra-xhigh.yaml +1 -1
  400. package/presets/fleet/brains/codex-gpt-6-luna-high.yaml +5 -0
  401. package/presets/fleet/brains/codex-gpt-6-luna-low.yaml +5 -0
  402. package/presets/fleet/brains/codex-gpt-6-luna-max.yaml +5 -0
  403. package/presets/fleet/brains/codex-gpt-6-luna-medium.yaml +5 -0
  404. package/presets/fleet/brains/codex-gpt-6-luna-xhigh.yaml +5 -0
  405. package/presets/fleet/brains/codex-gpt-6-sol-high.yaml +5 -0
  406. package/presets/fleet/brains/codex-gpt-6-sol-low.yaml +5 -0
  407. package/presets/fleet/brains/codex-gpt-6-sol-max.yaml +5 -0
  408. package/presets/fleet/brains/codex-gpt-6-sol-medium.yaml +5 -0
  409. package/presets/fleet/brains/codex-gpt-6-sol-ultra.yaml +5 -0
  410. package/presets/fleet/brains/codex-gpt-6-sol-xhigh.yaml +5 -0
  411. package/presets/fleet/roles/Coordinator.yaml +21 -13
  412. package/presets/fleet/roles/Critic.yaml +24 -0
  413. package/presets/fleet/roles/Developer.yaml +32 -0
  414. package/presets/fleet/roles/Engineer.yaml +72 -0
  415. package/presets/fleet/roles/LocalCoordinator.yaml +17 -0
  416. package/presets/fleet/room_layouts/engineering.yaml +25 -0
  417. package/presets/fleet/room_layouts/pair.yaml +24 -0
  418. package/presets/fleet/room_layouts/single.yaml +17 -0
  419. package/presets/fleet/room_layouts/team.yaml +30 -0
  420. package/presets/fleet/room_templates/engineering.yaml +19 -0
  421. package/presets/manifest.json +2 -2
  422. package/dist/web-app/assets/index-59GF-gsZ.css +0 -1
  423. package/dist/web-app/assets/index-C0h3ALvs.js +0 -10
  424. package/dist/web-app/icons/ours-fleet-maskable.svg +0 -4
  425. package/dist/web-app/icons/ours-fleet.svg +0 -4
  426. package/dist/web-app/index.html +0 -17
  427. package/dist/web-app/manifest.webmanifest +0 -15
  428. package/dist/web-app/offline.html +0 -18
  429. package/dist/web-app/sw.js +0 -51
@@ -0,0 +1,323 @@
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
+ /** Lock directories and the claim/reclaim entries derived from their names
69
+ * (`<lock>.claim.<pid>.<uuid>`, `<lock>.reclaim`) are never resources. */
70
+ const lockClaim = /\.claim\.\d+\.[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$|\.reclaim$/;
71
+ function lockName(name) { return name === '.locks' || name.endsWith('.lock'); }
72
+ function lockArtifact(name) { return lockName(name) || (lockClaim.test(name) && lockName(name.replace(lockClaim, ''))); }
73
+ /** Full discovery is synchronous and lstats every covered directory. Wakes are
74
+ * still delivered promptly, but discovery runs at most once per window. */
75
+ const DISCOVERY_WINDOW_MS = 100;
76
+ /** Native notifications cover supported local mounts. No idle content scan or
77
+ * silent claim of coverage after watch failure. Parents survive atomic rename.
78
+ * Discovery reads directory metadata only, never opens a controller or transcript. */
79
+ export function observeFleetResources(scope, onCommitted, signal) {
80
+ const config = resolve(scope.configPath ?? defaultConfigPath());
81
+ const roots = (scope.roots ?? [agentsRoot(), tmpRoot()]).map(p => resolve(p));
82
+ 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));
83
+ // Explicit roots without config select discovery only (notification producers).
84
+ const configEnabled = scope.configPath !== undefined || (!scope.paths && !scope.roots);
85
+ const adoptedHomes = configEnabled ? [join(homedir(), '.claude'), join(homedir(), '.codex')] : [];
86
+ if (configEnabled)
87
+ paths.push(dirname(config), resolve(stateRoot()), ...adoptedHomes);
88
+ // Explicit resource locations and their ancestors already belong to the
89
+ // caller's read scope (e.g. /home -> /var/home or a linked state root).
90
+ // Discovered descendants keep lstat validation and never traverse child links.
91
+ const trustedPaths = new Set(paths);
92
+ for (const path of paths) {
93
+ let parent = path;
94
+ for (let depth = 0; depth < 4 && dirname(parent) !== parent; depth++, parent = dirname(parent))
95
+ trustedPaths.add(parent);
96
+ }
97
+ const resourceIdentity = (path) => identity(path, trustedPaths.has(path));
98
+ const locks = resolve(stateRoot(), 'locks');
99
+ const ignored = (path) => within(path, locks) || path.split(sep).some(name => lockArtifact(name) || name.endsWith('.tmp'));
100
+ let state = { protocolVersion: 1, producerGeneration: randomUUID(), position: 0, health: 'healthy' };
101
+ let disposed = signal.aborted, queued = false, discoveredAt = -Infinity;
102
+ let recovery, discovery;
103
+ let installing = false, installFailure;
104
+ const cursors = new Set();
105
+ const watchers = new Map();
106
+ const release = () => { const old = [...watchers.values()]; watchers.clear(); for (const entry of old)
107
+ entry.handle.close(); };
108
+ const fail = (error) => {
109
+ if (installing) {
110
+ installFailure = error;
111
+ return;
112
+ }
113
+ if (disposed || state.health !== 'healthy')
114
+ return;
115
+ state = { ...state, position: state.position + 1, health: ['ENOSYS', 'ENOTSUP', 'ERR_FEATURE_UNAVAILABLE_ON_PLATFORM'].includes(error.code ?? '') ? 'unsupported' : 'failed' };
116
+ release();
117
+ onCommitted({ ...state });
118
+ recover();
119
+ };
120
+ const install = () => {
121
+ installing = true;
122
+ installFailure = undefined;
123
+ let added = 0;
124
+ try {
125
+ const wanted = new Set();
126
+ const cover = (dir) => {
127
+ const id = resourceIdentity(dir);
128
+ if (!id)
129
+ return;
130
+ wanted.add(dir);
131
+ const existing = watchers.get(dir);
132
+ if (existing && existing.identity === id)
133
+ return;
134
+ if (existing) {
135
+ watchers.delete(dir);
136
+ existing.handle.close();
137
+ }
138
+ let handle;
139
+ try {
140
+ handle = (scope.watch ?? sharedWatch)(dir, (_event, filename) => {
141
+ if (watchers.get(dir)?.handle !== handle)
142
+ return;
143
+ // Ignore sibling noise from ancestor coverage, lock churn and delivery acknowledgements.
144
+ const changed = filename === null ? dir : resolve(dir, String(filename));
145
+ if (ignored(changed))
146
+ return;
147
+ if (paths.some(path => within(changed, path) || within(path, changed)) || (configEnabled && changed === config) || filename === null)
148
+ wake(filename === null ? undefined : changed);
149
+ });
150
+ }
151
+ catch (error) {
152
+ // Parent coverage owns removal/recreation. Only verified disappearance
153
+ // is benign; permissions, unsupported watches and other errors fail.
154
+ if (directoryGone(error) && !resourceIdentity(dir))
155
+ return;
156
+ throw error;
157
+ }
158
+ watchers.set(dir, { identity: id, handle });
159
+ added++;
160
+ handle.on('error', (error) => { if (watchers.get(dir)?.handle === handle)
161
+ fail(error); });
162
+ handle.on('close', () => { if (watchers.get(dir)?.handle === handle)
163
+ fail(Error('resource watch closed')); });
164
+ if (installFailure)
165
+ throw installFailure;
166
+ };
167
+ const visit = (dir, depth) => {
168
+ if (within(dir, locks))
169
+ return;
170
+ const id = resourceIdentity(dir);
171
+ if (!id)
172
+ return;
173
+ // Watch before enumerating children: creation before watch is covered by
174
+ // this scan; creation after watch schedules another discovery pass.
175
+ cover(dir);
176
+ if (!depth)
177
+ return;
178
+ let entries;
179
+ try {
180
+ entries = readdirSync(dir, { withFileTypes: true });
181
+ }
182
+ catch (error) {
183
+ if (directoryGone(error) && !resourceIdentity(dir))
184
+ return;
185
+ throw error;
186
+ }
187
+ for (const entry of entries) {
188
+ if (!entry.isDirectory() || entry.isSymbolicLink() || lockArtifact(entry.name))
189
+ continue;
190
+ visit(join(dir, entry.name), depth - 1);
191
+ }
192
+ };
193
+ for (const path of paths) {
194
+ // Enough ancestors to retain the subscription when its state root is replaced.
195
+ let parent = path;
196
+ for (let depth = 0; depth < 4 && dirname(parent) !== parent; depth++, parent = dirname(parent)) {
197
+ if (resourceIdentity(parent)) {
198
+ wanted.add(parent);
199
+ cover(parent);
200
+ }
201
+ }
202
+ visit(path, configEnabled && (path === dirname(config) || adoptedHomes.includes(path)) ? 0 : 3);
203
+ }
204
+ for (const [dir, entry] of watchers) {
205
+ if (!wanted.has(dir) || resourceIdentity(dir) !== entry.identity) {
206
+ watchers.delete(dir);
207
+ entry.handle.close();
208
+ }
209
+ }
210
+ if (installFailure)
211
+ throw installFailure;
212
+ return added;
213
+ }
214
+ finally {
215
+ installing = false;
216
+ }
217
+ };
218
+ const recover = () => {
219
+ if (disposed || state.health === 'healthy' || !scope.retryWatchFailures || recovery)
220
+ return;
221
+ recovery = setTimeout(() => {
222
+ recovery = undefined;
223
+ if (disposed)
224
+ return;
225
+ try {
226
+ install();
227
+ }
228
+ catch {
229
+ release();
230
+ recover();
231
+ return;
232
+ }
233
+ state = { ...state, position: state.position + 1, health: 'healthy' };
234
+ scope.onInvalidated?.(undefined);
235
+ // Reinstallation precedes one checkpoint recovery pass; later work is
236
+ // event driven again. Degraded retries inspect directory metadata only.
237
+ onCommitted({ ...state });
238
+ }, 3000);
239
+ recovery.unref();
240
+ };
241
+ const wake = (path) => {
242
+ if (disposed || state.health !== 'healthy')
243
+ return;
244
+ scope.onInvalidated?.(path);
245
+ state = { ...state, position: state.position + 1 };
246
+ for (const cursor of cursors)
247
+ if (path === undefined || cursor.accepts(path))
248
+ cursor.position = state.position;
249
+ if (queued)
250
+ return;
251
+ queued = true;
252
+ queueMicrotask(() => {
253
+ queued = false;
254
+ if (disposed || state.health !== 'healthy')
255
+ return;
256
+ if (!discovery) {
257
+ const wait = discoveredAt + DISCOVERY_WINDOW_MS - performance.now();
258
+ if (wait <= 0) {
259
+ discover(false);
260
+ return;
261
+ }
262
+ discovery = setTimeout(() => { discovery = undefined; discover(true); }, wait);
263
+ discovery.unref();
264
+ }
265
+ // Covered directories were watched before this wake; directories created
266
+ // since the last pass are covered by the pending one.
267
+ onCommitted({ ...state });
268
+ });
269
+ };
270
+ const discover = (deferred) => {
271
+ if (disposed || state.health !== 'healthy')
272
+ return;
273
+ discoveredAt = performance.now();
274
+ let added;
275
+ try {
276
+ added = install();
277
+ }
278
+ catch (error) {
279
+ fail(error);
280
+ return;
281
+ }
282
+ // Every wake before a deferred pass was already delivered. Writes into
283
+ // directories it newly covers have no event of their own: treat as unknown.
284
+ if (deferred) {
285
+ if (!added)
286
+ return;
287
+ state = { ...state, position: state.position + 1 };
288
+ scope.onInvalidated?.(undefined);
289
+ for (const cursor of cursors)
290
+ cursor.position = state.position;
291
+ }
292
+ onCommitted({ ...state });
293
+ };
294
+ const direct = (path) => { if (paths.some(root => within(path, root)) || (configEnabled && path === config))
295
+ wake(path); };
296
+ let detach;
297
+ const dispose = () => { if (disposed)
298
+ return; disposed = true; clearTimeout(recovery); recovery = undefined; clearTimeout(discovery); discovery = undefined; detach?.(); for (const cursor of [...cursors])
299
+ cursor.dispose(); commits.delete(direct); signal.removeEventListener('abort', dispose); release(); };
300
+ if (!disposed) {
301
+ commits.add(direct);
302
+ detach = scope.subscribeMutations?.(wake);
303
+ signal.addEventListener('abort', dispose, { once: true });
304
+ try {
305
+ install();
306
+ }
307
+ catch (error) {
308
+ fail(error);
309
+ }
310
+ }
311
+ const snapshot = () => ({ ...state, ...(disposed ? { health: 'failed' } : {}) });
312
+ // View-local cursors share coverage and health, but unrelated file commits
313
+ // cannot invalidate a capture. Unknown wakes remain conservative.
314
+ const cursor = (accepts, lifetime) => {
315
+ const entry = { accepts, position: state.position, dispose() { cursors.delete(entry); lifetime.removeEventListener('abort', entry.dispose); } };
316
+ if (!disposed && !lifetime.aborted) {
317
+ cursors.add(entry);
318
+ lifetime.addEventListener('abort', entry.dispose, { once: true });
319
+ }
320
+ return { snapshot: () => ({ ...snapshot(), position: entry.position }), dispose: entry.dispose };
321
+ };
322
+ return { snapshot, cursor, dispose };
323
+ }
@@ -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({ kind: 'agent', agentId: 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,8 +14,10 @@ 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
+ private scan;
20
+ private record;
19
21
  stateDir(record: RoleRecord): string | undefined;
20
22
  private detect;
21
23
  }
@@ -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,21 @@ export class RoleRepository {
86
103
  constructor(options = {}) {
87
104
  this.options = options;
88
105
  }
89
- async list() {
106
+ async list(strict = false) {
107
+ const scan = this.scan(strict);
108
+ const names = [...new Set([...scan.configuredByName.keys(), ...scan.permanent, ...scan.temporary])].sort();
109
+ return boundedMap(names, this.options.concurrency ?? 8, name => this.record(scan, name, strict));
110
+ }
111
+ // One role's record is built from its own state and probe; the other roles are never probed.
112
+ async get(id) {
113
+ if (!ROLE_NAME_RE.test(id))
114
+ return undefined;
115
+ const scan = this.scan(false);
116
+ if (!scan.configuredByName.has(id) && !scan.permanent.has(id) && !scan.temporary.has(id))
117
+ return undefined;
118
+ return this.record(scan, id, false);
119
+ }
120
+ scan(strict) {
90
121
  const permanentRoot = this.options.permanentRoot ?? agentsRoot();
91
122
  const temporaryRoot = this.options.temporaryRoot ?? tmpRoot();
92
123
  let configured = [];
@@ -95,58 +126,62 @@ export class RoleRepository {
95
126
  configured = loadConfig(this.options.configPath).roles;
96
127
  }
97
128
  catch (error) {
129
+ if (strict)
130
+ throw error;
98
131
  configProblem = { code: 'config_invalid', severity: 'error', source: 'config', detail: error.message };
99
132
  }
100
- const permanent = new Set(safeDirs(permanentRoot));
101
- const temporary = new Set(safeDirs(temporaryRoot));
102
- const configuredByName = new Map(configured.map(role => [role.name, role]));
103
- const names = [...new Set([...configuredByName.keys(), ...permanent, ...temporary])].sort();
104
- return boundedMap(names, this.options.concurrency ?? 8, async (name) => {
105
- const config = configuredByName.get(name);
106
- const inPermanent = permanent.has(name);
107
- const inTemporary = temporary.has(name);
108
- const problems = configProblem ? [configProblem] : [];
109
- let tempRole;
110
- let stateHealth = inPermanent || inTemporary ? 'present' : 'missing';
111
- if (inTemporary) {
112
- const read = snapshot(join(temporaryRoot, name, 'role.yaml'), name);
113
- tempRole = read.role;
114
- if (read.problem) {
115
- problems.push(read.problem);
116
- stateHealth = 'corrupt';
117
- }
118
- }
119
- if (inPermanent && inTemporary)
120
- problems.push({ code: 'duplicate_state', severity: 'error', detail: 'both permanent and temporary state exist' });
121
- const intended = config?.session ?? tempRole?.session;
122
- const detected = await this.detect(name, intended);
123
- if (intended && detected !== 'none' && detected !== intended && detected !== 'ambiguous')
124
- problems.push({
125
- code: 'backend_mismatch', severity: 'warning',
126
- detail: `configured ${intended}, detected ${detected}`,
127
- });
128
- const stateRef = inTemporary ? { lifetime: 'temporary' }
129
- : inPermanent ? { lifetime: 'permanent' } : undefined;
130
- const tempStateDir = inTemporary ? join(temporaryRoot, name) : undefined;
131
- return {
132
- id: name,
133
- lifetime: config ? 'permanent' : inTemporary ? 'temporary' : 'orphan',
134
- configured: Boolean(config),
135
- config: config ? view(config) : tempRole ? view(tempRole) : undefined,
136
- stateRef,
137
- stateHealth,
138
- configuredBackend: intended,
139
- detectedBackend: detected,
140
- compatibility: { compatible: true },
141
- problems,
142
- lineage: tempStateDir ? readSpawnLineage(tempStateDir) : undefined,
143
- };
144
- });
133
+ return {
134
+ temporaryRoot, configProblem,
135
+ permanent: new Set(safeDirs(permanentRoot, strict)),
136
+ temporary: new Set(safeDirs(temporaryRoot, strict)),
137
+ configuredByName: new Map(configured.map(role => [role.name, role])),
138
+ };
145
139
  }
146
- async get(id) {
147
- if (!ROLE_NAME_RE.test(id))
148
- return undefined;
149
- return (await this.list()).find(role => role.id === id);
140
+ async record(scan, name, strict) {
141
+ const { temporaryRoot, configProblem } = scan;
142
+ const config = scan.configuredByName.get(name);
143
+ const inPermanent = scan.permanent.has(name);
144
+ const inTemporary = scan.temporary.has(name);
145
+ const problems = configProblem ? [configProblem] : [];
146
+ let tempRole;
147
+ let stateHealth = inPermanent || inTemporary ? 'present' : 'missing';
148
+ if (inTemporary) {
149
+ const read = snapshot(join(temporaryRoot, name, 'role.yaml'), name);
150
+ tempRole = read.role;
151
+ if (strict && read.problem)
152
+ throw new Error('temporary role snapshot unavailable');
153
+ if (read.problem) {
154
+ problems.push(read.problem);
155
+ stateHealth = 'corrupt';
156
+ }
157
+ }
158
+ if (inPermanent && inTemporary)
159
+ problems.push({ code: 'duplicate_state', severity: 'error', detail: 'both permanent and temporary state exist' });
160
+ const intended = config?.session ?? tempRole?.session;
161
+ const detected = await this.detect(name, intended);
162
+ if (intended && detected !== 'none' && detected !== intended && detected !== 'ambiguous')
163
+ problems.push({
164
+ code: 'backend_mismatch', severity: 'warning',
165
+ detail: `configured ${intended}, detected ${detected}`,
166
+ });
167
+ const stateRef = inTemporary ? { lifetime: 'temporary' }
168
+ : inPermanent ? { lifetime: 'permanent' } : undefined;
169
+ const tempStateDir = inTemporary ? join(temporaryRoot, name) : undefined;
170
+ const taskId = !config && tempStateDir ? retainedTaskId(tempStateDir, name) : undefined;
171
+ return {
172
+ id: name,
173
+ lifetime: config ? 'permanent' : inTemporary ? 'temporary' : 'orphan',
174
+ configured: Boolean(config),
175
+ ...(taskId ? { taskId } : {}),
176
+ config: config ? view(config) : tempRole ? view(tempRole) : undefined,
177
+ stateRef,
178
+ stateHealth,
179
+ configuredBackend: intended,
180
+ detectedBackend: detected,
181
+ compatibility: { compatible: true },
182
+ problems,
183
+ lineage: tempStateDir ? readSpawnLineage(tempStateDir) : undefined,
184
+ };
150
185
  }
151
186
  stateDir(record) {
152
187
  if (!record.stateRef)
@@ -0,0 +1,45 @@
1
+ import { loadConfig } from '../config.js';
2
+ import { type RoomLayoutDefinition } from '../rooms-tasks/layout-config.js';
3
+ export declare const ABSENT_REVISION = "absent";
4
+ export type LayoutDefinitionBody = Omit<RoomLayoutDefinition, 'sourceFile'>;
5
+ export type LayoutListEntry = {
6
+ name: string;
7
+ revision: string;
8
+ file: string;
9
+ definition: LayoutDefinitionBody;
10
+ issues: string[];
11
+ definition_hash: string;
12
+ } | {
13
+ name: string;
14
+ revision: string;
15
+ file: string;
16
+ error: string;
17
+ };
18
+ export declare class RoomLayoutDefinitions {
19
+ private configPath?;
20
+ private deps;
21
+ constructor(configPath?: string | undefined, deps?: {
22
+ loadConfiguration?: typeof loadConfig;
23
+ });
24
+ private root;
25
+ private pathFor;
26
+ private currentRevision;
27
+ /** Agent-template references that do not resolve in the current Fleet configuration. */
28
+ private templateIssues;
29
+ /** The configured default (`rooms.defaults.template`, else `tasks.default_room_template`) when it names a usable layout. */
30
+ defaultLayout(entries?: LayoutListEntry[]): string | undefined;
31
+ list(): LayoutListEntry[];
32
+ validate(raw: unknown): {
33
+ definition?: LayoutDefinitionBody;
34
+ issues: string[];
35
+ };
36
+ save(name: string, expectedRevision: string, raw: unknown): Promise<{
37
+ name: string;
38
+ revision: string;
39
+ definition: LayoutDefinitionBody;
40
+ }>;
41
+ remove(name: string, expectedRevision: string): Promise<{
42
+ name: string;
43
+ deleted: boolean;
44
+ }>;
45
+ }