@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,519 @@
1
+ import { relative, resolve, sep } from 'node:path';
2
+ import { agentsRoot, tmpRoot, stateRoot, defaultConfigPath, fleetDDir } from '../paths.js';
3
+ import { splitRootFor } from '../config.js';
4
+ import { setImmediate as yieldToEventLoop } from 'node:timers/promises';
5
+ import { randomUUID } from 'node:crypto';
6
+ import { homedir } from 'node:os';
7
+ import { observeFleetResources } from '../application/resource-observer.js';
8
+ export async function abortableProjection(read, signal) {
9
+ signal.throwIfAborted();
10
+ let onAbort;
11
+ const aborted = new Promise((_, reject) => {
12
+ onAbort = () => reject(signal.reason ?? new Error('resource projection aborted'));
13
+ signal.addEventListener('abort', onAbort, { once: true });
14
+ });
15
+ try {
16
+ return await Promise.race([read, aborted]);
17
+ }
18
+ finally {
19
+ signal.removeEventListener('abort', onAbort);
20
+ }
21
+ }
22
+ export const RESOURCE_FRAME_BYTES = 512 * 1024;
23
+ export const RESOURCE_PENDING_BYTES = 8 * 1024 * 1024;
24
+ export const RESOURCE_ASSEMBLY_BYTES = 16 * 1024 * 1024;
25
+ const CHUNK_BYTES = 256 * 1024;
26
+ /** Minimum spacing of activity-only follow-up captures of one view. */
27
+ export const ACTIVITY_CAPTURE_INTERVAL_MS = 1000;
28
+ /** Lazily yield byte chunks so backpressure never queues an entire large record.
29
+ * Base64 prevents splitting a Unicode scalar from corrupting JSON/SSE parsing. */
30
+ export function* resourceFrames(envelope) {
31
+ const bytes = Buffer.from(JSON.stringify(envelope));
32
+ if (bytes.length > RESOURCE_ASSEMBLY_BYTES)
33
+ throw new Error('resource assembly limit exceeded');
34
+ const ordinary = { event: 'resource', data: envelope };
35
+ if (Buffer.byteLength(`event: resource\ndata: ${bytes.toString('utf8')}\n\n`) <= RESOURCE_FRAME_BYTES) {
36
+ yield ordinary;
37
+ return;
38
+ }
39
+ const id = `${envelope.viewId}:${envelope.subscriptionEpoch}:${envelope.sequence}`;
40
+ const binding = { protocolVersion: 1, id, producerGeneration: envelope.producerGeneration,
41
+ viewId: envelope.viewId, subscriptionEpoch: envelope.subscriptionEpoch, revision: envelope.revision,
42
+ sequence: envelope.sequence };
43
+ yield { event: 'resource-chunk', data: { ...binding, type: 'start', totalBytes: bytes.length, parts: Math.ceil(bytes.length / CHUNK_BYTES) } };
44
+ for (let offset = 0, index = 0; offset < bytes.length; offset += CHUNK_BYTES, index++) {
45
+ yield { event: 'resource-chunk', data: { ...binding, type: 'chunk', index, data: bytes.subarray(offset, offset + CHUNK_BYTES).toString('base64') } };
46
+ }
47
+ yield { event: 'resource-chunk', data: { ...binding, type: 'commit', totalBytes: bytes.length } };
48
+ }
49
+ /** One Fleet HTTP writer. The active record owns the assembly budget;
50
+ * waiting live records share the smaller pending-output budget. */
51
+ export class ResourceOutput {
52
+ response;
53
+ signal;
54
+ failed;
55
+ #pending = [];
56
+ #pendingBytes = 0;
57
+ #active;
58
+ #closed = false;
59
+ #releaseDrain;
60
+ #abort = () => this.close();
61
+ constructor(response, signal, failed) {
62
+ this.response = response;
63
+ this.signal = signal;
64
+ this.failed = failed;
65
+ if (signal.aborted)
66
+ this.close();
67
+ else {
68
+ signal.addEventListener('abort', this.#abort, { once: true });
69
+ response.once('close', this.#abort);
70
+ }
71
+ }
72
+ send(envelope, admitted) {
73
+ if (this.#closed)
74
+ return Promise.reject(new Error('resource output closed'));
75
+ const bytes = Buffer.byteLength(JSON.stringify(envelope));
76
+ if (bytes > RESOURCE_ASSEMBLY_BYTES)
77
+ return this.#fail(new Error('resource assembly limit exceeded'));
78
+ if (this.#active && this.#pendingBytes + bytes > RESOURCE_PENDING_BYTES) {
79
+ return this.#fail(new Error('resource pending limit exceeded'));
80
+ }
81
+ admitted?.();
82
+ return new Promise((resolve, reject) => {
83
+ const item = { envelope, bytes, resolve, reject };
84
+ if (!this.#active) {
85
+ this.#active = item;
86
+ void this.#pump();
87
+ }
88
+ else {
89
+ this.#pending.push(item);
90
+ this.#pendingBytes += bytes;
91
+ }
92
+ });
93
+ }
94
+ #fail(error) {
95
+ this.close(error);
96
+ this.failed(error);
97
+ return Promise.reject(error);
98
+ }
99
+ close(error = new Error('resource output closed')) {
100
+ if (this.#closed)
101
+ return;
102
+ this.#closed = true;
103
+ this.signal.removeEventListener('abort', this.#abort);
104
+ this.response.removeListener('close', this.#abort);
105
+ this.#releaseDrain?.();
106
+ this.#active?.reject(error);
107
+ for (const item of this.#pending.splice(0))
108
+ item.reject(error);
109
+ this.#pendingBytes = 0;
110
+ if (!this.response.destroyed && !this.response.writableEnded)
111
+ this.response.end();
112
+ }
113
+ async #pump() {
114
+ try {
115
+ while (this.#active && !this.#closed) {
116
+ const item = this.#active;
117
+ for (const frame of ('views' in item.envelope ? [{ event: 'resource-coverage', data: item.envelope }] : resourceFrames(item.envelope))) {
118
+ if (this.#closed)
119
+ break;
120
+ const writable = this.response.write(`event: ${frame.event}\ndata: ${JSON.stringify(frame.data)}\n\n`);
121
+ if (!writable)
122
+ await this.#drain();
123
+ }
124
+ if (this.#closed)
125
+ break;
126
+ item.resolve();
127
+ this.#active = this.#pending.shift();
128
+ if (this.#active)
129
+ this.#pendingBytes -= this.#active.bytes;
130
+ }
131
+ }
132
+ catch (error) {
133
+ const failure = error instanceof Error ? error : new Error('resource output failed');
134
+ this.close(failure);
135
+ this.failed(failure);
136
+ }
137
+ }
138
+ #drain() {
139
+ if (this.#closed)
140
+ return Promise.resolve();
141
+ return new Promise(resolve => {
142
+ const release = () => {
143
+ this.response.removeListener('drain', release);
144
+ this.#releaseDrain = undefined;
145
+ resolve();
146
+ };
147
+ this.#releaseDrain = release;
148
+ this.response.once('drain', release);
149
+ });
150
+ }
151
+ }
152
+ export function canonicalResourceDescriptor(input) {
153
+ if (!input || typeof input !== 'object' || Array.isArray(input))
154
+ throw Error('invalid resource descriptor');
155
+ const value = input;
156
+ if (!/^[A-Za-z0-9_-]{1,128}$/.test(value.viewId) || !['directory', 'roles', 'tasks', 'task-lists', 'configuration', 'previews', 'task-layout', 'creation-action', 'action', 'member-addition', 'login', 'onboarding-health', 'subscriptions'].includes(value.resource)
157
+ || Object.keys(value).some(key => !['viewId', 'resource', 'includeTemporary', 'includeDefinitions', 'ids', 'state', 'list', 'groupByList', 'includeDeleting', 'entityId', 'receiptId', 'provider'].includes(key)))
158
+ throw Error('invalid resource descriptor');
159
+ for (const key of ['includeTemporary', 'includeDefinitions', 'groupByList', 'includeDeleting'])
160
+ if (value[key] !== undefined && typeof value[key] !== 'boolean')
161
+ throw Error('invalid resource descriptor');
162
+ if (value.ids !== undefined && (!Array.isArray(value.ids) || value.ids.length > 500 || value.ids.some(id => typeof id !== 'string' || !/^[A-Za-z0-9_-]{1,120}$/.test(id))))
163
+ throw Error('invalid resource ids');
164
+ if (value.state !== undefined && !['all', 'backlog', 'provisioning', 'active', 'review', 'done', 'cancelled', 'failed'].includes(value.state))
165
+ throw Error('invalid resource state');
166
+ if (value.list !== undefined && (typeof value.list !== 'string' || value.list.length > 256))
167
+ throw Error('invalid resource list');
168
+ for (const key of ['entityId', 'receiptId'])
169
+ if (value[key] !== undefined && (typeof value[key] !== 'string' || !/^[A-Za-z0-9_-]{1,120}$/.test(value[key])))
170
+ throw Error('invalid resource target');
171
+ if (['task-layout', 'creation-action', 'action', 'member-addition', 'login'].includes(value.resource) && !value.entityId)
172
+ throw Error('resource target required');
173
+ if (value.resource === 'member-addition' && !value.receiptId)
174
+ throw Error('receipt required');
175
+ if (value.resource === 'login' && !['claude', 'codex'].includes(value.provider ?? ''))
176
+ throw Error('provider required');
177
+ return { ...value, ...(value.ids ? { ids: [...new Set(value.ids)] } : {}) };
178
+ }
179
+ export class ResourceBindingError extends Error {
180
+ status;
181
+ constructor(status, message) {
182
+ super(message);
183
+ this.status = status;
184
+ }
185
+ }
186
+ /** Same committed snapshot/ready/bound-read contract as Messenger. No controllers,
187
+ * notification reads or presence are acquired by this delivery owner. */
188
+ export class FleetResourceViews {
189
+ scope;
190
+ project;
191
+ configurationInputs;
192
+ activityCaptureIntervalMs;
193
+ producerGeneration = randomUUID();
194
+ connections = new Map();
195
+ constructor(scope, project, configurationInputs = () => [], activityCaptureIntervalMs = ACTIVITY_CAPTURE_INTERVAL_MS) {
196
+ this.scope = scope;
197
+ this.project = project;
198
+ this.configurationInputs = configurationInputs;
199
+ this.activityCaptureIntervalMs = activityCaptureIntervalMs;
200
+ }
201
+ open(response, owner, authorized) {
202
+ const id = randomUUID(), controller = new AbortController();
203
+ let connection;
204
+ let timer;
205
+ let healthTimer;
206
+ const close = () => {
207
+ if (controller.signal.aborted)
208
+ return;
209
+ controller.abort();
210
+ clearInterval(timer);
211
+ clearInterval(healthTimer);
212
+ this.connections.delete(id);
213
+ if (connection) {
214
+ for (const view of connection.views.values())
215
+ this.retire(view);
216
+ connection.views.clear();
217
+ connection.output.close();
218
+ }
219
+ response.off('close', close);
220
+ };
221
+ const output = new ResourceOutput(response, controller.signal, close);
222
+ const observer = observeFleetResources(this.scope, state => {
223
+ if (!connection || controller.signal.aborted)
224
+ return;
225
+ if (state.health !== 'healthy') {
226
+ const pending = [...connection.views.values()].map(view => {
227
+ view.failed = true;
228
+ view.rejectInitial(Error('resource observation unavailable'));
229
+ view.controller.abort();
230
+ return this.emit(connection, view, 'health', { healthy: false, reason: state.health === 'unsupported' ? 'unsupported' : 'upstream_unavailable' });
231
+ });
232
+ if (!pending.length)
233
+ response.write(`event: resource-service-health\ndata: ${JSON.stringify({ protocolVersion: 1, healthy: false, reason: state.health === 'unsupported' ? 'unsupported' : 'upstream_unavailable' })}\n\n`);
234
+ void Promise.allSettled(pending).then(close);
235
+ }
236
+ else
237
+ for (const view of connection.views.values())
238
+ if (view.source.snapshot().position !== view.scheduledPosition)
239
+ this.schedule(connection, view);
240
+ }, controller.signal);
241
+ connection = { id, owner, controller, output, views: new Map(), epochs: new Map(), observer, tail: Promise.resolve(), close };
242
+ this.connections.set(id, connection);
243
+ response.once('close', close);
244
+ response.write(`event: resource-connection\ndata: ${JSON.stringify({ protocolVersion: 1, connectionId: id, producerGeneration: this.producerGeneration, continuity: 'reset', baselineRequired: true })}\n\n`);
245
+ if (observer.snapshot().health !== 'healthy') {
246
+ response.write(`event: resource-service-health\ndata: ${JSON.stringify({ protocolVersion: 1, healthy: false, reason: observer.snapshot().health === 'unsupported' ? 'unsupported' : 'upstream_unavailable' })}\n\n`);
247
+ close();
248
+ }
249
+ else {
250
+ // Preserve credential retirement timing independently of sparse wire health.
251
+ timer = setInterval(() => {
252
+ if (!authorized()) {
253
+ close();
254
+ return;
255
+ }
256
+ }, 2000);
257
+ timer.unref();
258
+ healthTimer = setInterval(() => {
259
+ if (!authorized()) {
260
+ close();
261
+ return;
262
+ }
263
+ this.coverage(connection).catch(close);
264
+ if (!connection.views.size)
265
+ response.write(': keepalive\n\n');
266
+ }, 15000);
267
+ healthTimer.unref();
268
+ }
269
+ return { connectionId: id, close };
270
+ }
271
+ register(owner, connectionId, viewId, epoch, input) {
272
+ const connection = this.get(owner, connectionId);
273
+ let descriptor;
274
+ try {
275
+ descriptor = canonicalResourceDescriptor(input);
276
+ }
277
+ catch {
278
+ throw new ResourceBindingError(400, 'invalid resource descriptor');
279
+ }
280
+ if (descriptor.viewId !== viewId || !Number.isSafeInteger(epoch) || epoch < 1)
281
+ throw new ResourceBindingError(400, 'invalid resource epoch');
282
+ if ((connection.epochs.get(viewId) ?? 0) >= epoch)
283
+ throw new ResourceBindingError(409, 'obsolete resource epoch');
284
+ if (connection.observer.snapshot().health !== 'healthy')
285
+ throw new ResourceBindingError(503, 'resource observation unavailable');
286
+ if (!connection.epochs.has(viewId) && connection.epochs.size >= 256)
287
+ throw new ResourceBindingError(503, 'resource epoch capacity exceeded');
288
+ if (!connection.views.has(viewId) && connection.views.size >= 64)
289
+ throw new ResourceBindingError(409, 'resource view capacity exceeded');
290
+ const old = connection.views.get(viewId);
291
+ if (old)
292
+ this.retire(old);
293
+ let resolveInitial, rejectInitial;
294
+ const initial = new Promise((resolve, reject) => { resolveInitial = resolve; rejectInitial = reject; });
295
+ void initial.catch(() => { });
296
+ const controller = new AbortController();
297
+ const source = connection.observer.cursor(path => this.affects(descriptor, path), controller.signal);
298
+ // Independent agents contribute ordered, last-observed statuses and completed
299
+ // preview records. Activity wakes follow-up reads without revoking structural
300
+ // capture; the read still certifies only its starting source watermark.
301
+ const captureSource = descriptor.resource === 'directory' || descriptor.resource === 'roles' || descriptor.resource === 'previews'
302
+ ? connection.observer.cursor(path => this.affects(descriptor, path, true), controller.signal)
303
+ : source;
304
+ const view = { source, captureSource, scheduledPosition: -1, descriptor, epoch, baselineId: randomUUID(), controller, revision: 0, sequence: 0, dirty: false, running: false, failed: false, initial, resolveInitial, rejectInitial };
305
+ connection.views.set(viewId, view);
306
+ connection.epochs.set(viewId, epoch);
307
+ this.schedule(connection, view);
308
+ return { subscriptionEpoch: epoch, baselineId: view.baselineId };
309
+ }
310
+ remove(owner, connectionId, viewId, epoch) {
311
+ const connection = this.get(owner, connectionId), view = connection.views.get(viewId);
312
+ if (typeof viewId !== 'string' || !/^[A-Za-z0-9_-]{1,128}$/.test(viewId) || !Number.isSafeInteger(epoch) || epoch < 1)
313
+ throw new ResourceBindingError(400, 'invalid resource epoch');
314
+ if (!connection.epochs.has(viewId) && connection.epochs.size >= 256)
315
+ throw new ResourceBindingError(503, 'resource epoch capacity exceeded');
316
+ // Independent DELETE can arrive before the POST it retires.
317
+ connection.epochs.set(viewId, Math.max(connection.epochs.get(viewId) ?? 0, epoch));
318
+ if (view && epoch >= view.epoch) {
319
+ this.retire(view);
320
+ connection.views.delete(viewId);
321
+ }
322
+ }
323
+ async bound(owner, connectionId, viewId, epoch, baselineId) {
324
+ const connection = this.get(owner, connectionId), view = connection.views.get(viewId);
325
+ if (!view || view.epoch !== epoch || view.baselineId !== baselineId)
326
+ throw new ResourceBindingError(409, 'resource binding mismatch');
327
+ const baseline = await view.initial;
328
+ if (!this.current(connection, view) || view.failed)
329
+ throw new ResourceBindingError(409, 'resource binding retired');
330
+ return baseline;
331
+ }
332
+ close() { for (const connection of this.connections.values())
333
+ connection.close(); }
334
+ get(owner, id) {
335
+ const connection = this.connections.get(id);
336
+ if (!connection || connection.owner !== owner)
337
+ throw new ResourceBindingError(409, 'unknown resource connection');
338
+ return connection;
339
+ }
340
+ current(connection, view) { return !connection.controller.signal.aborted && !view.controller.signal.aborted && connection.views.get(view.descriptor.viewId) === view; }
341
+ retire(view) { clearTimeout(view.activityTimer); view.activityTimer = undefined; view.controller.abort(); view.rejectInitial(new ResourceBindingError(409, 'resource binding retired')); }
342
+ emit(connection, view, kind, payload, source) {
343
+ return connection.output.send({ protocolVersion: 1, producerGeneration: this.producerGeneration, viewId: view.descriptor.viewId, subscriptionEpoch: view.epoch, baselineId: view.baselineId, sequence: view.sequence + 1, revision: view.revision, kind, payload, ...(source ? { source } : {}) }, () => { view.sequence++; if (source || kind === 'ready')
344
+ view.qualifiedAt = Date.now(); });
345
+ }
346
+ // Admission allocates sequences atomically; the existing writer keeps this
347
+ // complete batch behind any active chunk assembly and earlier data records.
348
+ coverage(connection) {
349
+ const now = Date.now();
350
+ const eligible = [...connection.views.values()].filter(view => this.current(connection, view) && !view.failed && !view.running && !view.dirty && view.payload !== undefined && view.covered && view.covered.health === 'healthy' && view.covered.position === view.source.snapshot().position && now - (view.qualifiedAt ?? 0) >= 15000);
351
+ if (!eligible.length)
352
+ return Promise.resolve();
353
+ const views = eligible.map(view => ({ protocolVersion: 1, producerGeneration: this.producerGeneration, viewId: view.descriptor.viewId, subscriptionEpoch: view.epoch, baselineId: view.baselineId, sequence: view.sequence + 1, revision: view.revision, source: view.covered }));
354
+ return connection.output.send({ protocolVersion: 1, producerGeneration: this.producerGeneration, views }, () => { for (const view of eligible) {
355
+ view.sequence++;
356
+ view.qualifiedAt = now;
357
+ } });
358
+ }
359
+ affects(descriptor, path, structural = false) {
360
+ if (descriptor.resource === 'configuration') {
361
+ const manifest = resolve(this.scope.configPath ?? defaultConfigPath());
362
+ const root = resolve(splitRootFor(manifest)), changed = resolve(path);
363
+ const within = (candidate, parent) => {
364
+ const local = relative(parent, candidate);
365
+ return local === '' || (local !== '..' && !local.startsWith('..' + sep));
366
+ };
367
+ // Configuration and provenance read the manifest and split definitions;
368
+ // loadConfig also rejects legacy YAML files in the global fleet.d root.
369
+ // Ancestor replacement/unknown directory wakes remain conservative.
370
+ const legacy = resolve(fleetDDir());
371
+ return within(changed, root) || within(root, changed) || within(manifest, changed)
372
+ || within(changed, legacy) || within(legacy, changed)
373
+ || this.configurationInputs().some(input => within(resolve(input), changed));
374
+ }
375
+ // An explicitly selected manifest may use any pathname, including one
376
+ // otherwise reserved for an independent task receipt.
377
+ if (this.scope.configPath && resolve(path) === resolve(this.scope.configPath))
378
+ return true;
379
+ // Adopted Codex history, telemetry and cache commits are not canonical
380
+ // Fleet preview inputs. Keep credentials, unknown paths and directory-level
381
+ // events conservative, and never override explicit config or role roots.
382
+ const roots = this.scope.roots ?? [agentsRoot(), tmpRoot()];
383
+ if (descriptor.resource === 'previews'
384
+ && /^(?:history\.jsonl|models_cache\.json|(?:thread_history|state|logs|queue|goals)_\d+\.sqlite(?:-wal|-shm|-journal)?)$/.test(relative(resolve(homedir(), '.codex'), path))
385
+ && !roots.some(root => { const local = relative(resolve(root), path); return local === '' || (local !== '..' && !local.startsWith('..' + sep)); }))
386
+ return false;
387
+ // Preview selection reads role config/state and conversation ledgers, never
388
+ // task layout run/operation/brief receipts. A worker may commit several of
389
+ // these while that independent canonical preview read is in flight.
390
+ // Unknown paths and directory-level events retain full invalidation.
391
+ if (descriptor.resource === 'previews' && /^task-[A-Za-z0-9_-]+(?:\.op\.json|\.json|\.brief\.md)$/.test(relative(resolve(stateRoot(), 'layouts'), path)))
392
+ return false;
393
+ // Only classify known activity writes under the configured role roots.
394
+ // Parent/unknown events and every other file retain full invalidation.
395
+ // The canonical web producer atomically replaces hashed checkpoint files,
396
+ // once per ledger advance of any role. No projection reads these delivery
397
+ // acknowledgements, so they neither fence a capture nor wake a follow-up.
398
+ if (/^[a-f0-9]{64}\.json$/.test(relative(resolve(stateRoot(), 'web', 'notification-outboxes'), path)))
399
+ return false;
400
+ for (const root of roots) {
401
+ const parts = relative(resolve(root), path).split(sep);
402
+ if (parts[0] === '..')
403
+ continue;
404
+ // Both session backends append diagnostic events as well as durable
405
+ // conversation events; ACP usage updates also persist observed usage.
406
+ // Monitor cursor/state and its presented status are per-agent activity.
407
+ // None changes role membership/configuration. Keep these as full wakes.
408
+ if (structural && parts.length === 2 && ['.session-events.jsonl', '.session-events.jsonl.1', '.subscription-usage.json', '.notify-cursor', '.monitor-state.json', '.monitor-status'].includes(parts[1]))
409
+ return false;
410
+ if (parts.length < 3 || parts[1] !== '.conversation')
411
+ continue;
412
+ if (structural && (descriptor.resource === 'directory' || descriptor.resource === 'roles'))
413
+ return false;
414
+ if (descriptor.resource === 'previews') {
415
+ // conversationPreview reads bounded completed records, independently per
416
+ // role. These known writer paths still wake ordered follow-up captures.
417
+ // Unknown children, parent replacement and explicit config paths retain
418
+ // their capture fence; do not waive the whole conversation subtree.
419
+ if (structural && parts.length === 3 && /^(?:events-\d{6}\.jsonl|manifest\.json)$/.test(parts[2]))
420
+ return false;
421
+ return (descriptor.ids ?? []).includes(parts[0]);
422
+ }
423
+ // Directory and roles intentionally remain conservative: live status
424
+ // includes activity. Configuration was classified by its inputs above.
425
+ }
426
+ return true;
427
+ }
428
+ schedule(connection, view, due = false) {
429
+ if (!this.current(connection, view) || view.failed)
430
+ return;
431
+ // Agent activity (ledger appends, status files) changes previews and status
432
+ // continuously while an agent works. Without a structural change since the
433
+ // latest capture began, coalesce those wakes into one trailing capture per
434
+ // interval instead of re-projecting every role after every append.
435
+ const wait = !due && view.captureSource !== view.source && view.capturedAt !== undefined
436
+ && view.captureSource.snapshot().position === view.capturedStructure
437
+ ? this.activityCaptureIntervalMs - (Date.now() - view.capturedAt) : 0;
438
+ if (wait > 0) {
439
+ view.scheduledPosition = view.source.snapshot().position;
440
+ view.dirty = true;
441
+ if (!view.running && !view.activityTimer) {
442
+ // The trailing capture runs when its timer fires, whatever the clock reads.
443
+ view.activityTimer = setTimeout(() => { view.activityTimer = undefined; this.schedule(connection, view, true); }, Math.min(wait, this.activityCaptureIntervalMs));
444
+ view.activityTimer.unref?.();
445
+ }
446
+ return;
447
+ }
448
+ clearTimeout(view.activityTimer);
449
+ view.activityTimer = undefined;
450
+ view.scheduledPosition = view.source.snapshot().position;
451
+ view.dirty = true;
452
+ if (view.running)
453
+ return;
454
+ view.running = true;
455
+ const task = connection.tail.then(async () => {
456
+ // Resolved projection promises must not starve HTTP, timers or native
457
+ // watch delivery. Preserve serialization and coalesce this event batch.
458
+ await yieldToEventLoop();
459
+ if (!this.current(connection, view) || view.failed)
460
+ return;
461
+ await this.refresh(connection, view);
462
+ });
463
+ connection.tail = task.catch(() => { });
464
+ void task.finally(() => { view.running = false; if (view.dirty)
465
+ this.schedule(connection, view); }).catch(connection.close);
466
+ }
467
+ async refresh(connection, view) {
468
+ try {
469
+ if (!view.dirty || !this.current(connection, view))
470
+ return;
471
+ view.dirty = false;
472
+ view.capturedAt = Date.now();
473
+ view.capturedStructure = view.captureSource.snapshot().position;
474
+ let payload, source;
475
+ for (let attempt = 0; attempt < 3; attempt++) {
476
+ view.controller.signal.throwIfAborted();
477
+ const before = view.captureSource.snapshot();
478
+ const covered = view.source.snapshot();
479
+ payload = await abortableProjection(this.project(view.descriptor, view.controller.signal, connection.owner), view.controller.signal);
480
+ view.controller.signal.throwIfAborted();
481
+ const after = view.captureSource.snapshot();
482
+ if (after.health !== 'healthy')
483
+ throw Error('resource observation unavailable');
484
+ // Certify only the starting wake barrier. Status commits during the
485
+ // asynchronous canonical read stay dirty and are drained in order by
486
+ // the next serialized capture, even if this read happened to see them.
487
+ if (before.position === after.position) {
488
+ source = covered;
489
+ break;
490
+ }
491
+ }
492
+ if (!source)
493
+ throw Error('resource projection unstable');
494
+ if (!this.current(connection, view))
495
+ return;
496
+ const first = view.payload === undefined, changed = first || JSON.stringify(payload, (key, value) => key === 'observedAt' ? undefined : value) !== JSON.stringify(view.payload, (key, value) => key === 'observedAt' ? undefined : value);
497
+ view.covered = source;
498
+ if (changed) {
499
+ view.revision++;
500
+ await this.emit(connection, view, 'snapshot', payload, source);
501
+ if (!this.current(connection, view))
502
+ return;
503
+ view.payload = payload;
504
+ }
505
+ if (first) {
506
+ await this.emit(connection, view, 'ready', { baselineId: view.baselineId, source });
507
+ if (this.current(connection, view))
508
+ view.resolveInitial({ payload, source, producerGeneration: this.producerGeneration, revision: view.revision, baselineId: view.baselineId, descriptor: view.descriptor });
509
+ }
510
+ }
511
+ catch (error) {
512
+ if (!this.current(connection, view))
513
+ return;
514
+ view.failed = true;
515
+ view.rejectInitial(error instanceof Error ? error : Error('projection failed'));
516
+ await this.emit(connection, view, 'resync', { reason: 'projection_unavailable' }).catch(connection.close);
517
+ }
518
+ }
519
+ }
@@ -2,8 +2,10 @@ import { type WebServer } from './server.js';
2
2
  import { type WebAccessConfig } from './access.js';
3
3
  export interface StartWebOptions {
4
4
  configPath?: string;
5
+ /** Separate console state for an independently launched gateway. */
6
+ webStateDir?: string;
7
+ control?: boolean;
5
8
  port?: number;
6
- open?: boolean;
7
9
  binPath: string;
8
10
  log?(line: string): void;
9
11
  bind?: string;