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

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 (414) hide show
  1. package/README.md +279 -68
  2. package/dist/account-origin.d.ts +2 -0
  3. package/dist/account-origin.js +6 -0
  4. package/dist/agent-ours/bridge.js +26 -9
  5. package/dist/agent-ours/correspondence.d.ts +10 -0
  6. package/dist/agent-ours/correspondence.js +1 -0
  7. package/dist/agent-ours/file-rpc.d.ts +12 -0
  8. package/dist/agent-ours/file-rpc.js +18 -4
  9. package/dist/agent-ours/harness.js +9 -1
  10. package/dist/agent-ours/mcp-endpoint.d.ts +19 -0
  11. package/dist/agent-ours/mcp-endpoint.js +98 -25
  12. package/dist/agent-ours/runtime.d.ts +9 -1
  13. package/dist/agent-ours/runtime.js +50 -2
  14. package/dist/agent-ours/service.d.ts +24 -1
  15. package/dist/agent-ours/service.js +97 -13
  16. package/dist/application/codex-models.d.ts +15 -0
  17. package/dist/application/codex-models.js +56 -0
  18. package/dist/application/conversation-history.d.ts +27 -0
  19. package/dist/application/conversation-history.js +215 -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 +2 -2
  24. package/dist/application/log-service.js +8 -8
  25. package/dist/application/model-catalog.d.ts +1 -1
  26. package/dist/application/model-catalog.js +5 -2
  27. package/dist/application/preset-provenance.d.ts +19 -0
  28. package/dist/application/preset-provenance.js +59 -0
  29. package/dist/application/role-creation-service.js +3 -2
  30. package/dist/application/role-removal-service.js +17 -3
  31. package/dist/application/role-repository.js +19 -2
  32. package/dist/application/room-layout-definitions.d.ts +45 -0
  33. package/dist/application/room-layout-definitions.js +130 -0
  34. package/dist/application/session-control.d.ts +61 -17
  35. package/dist/application/session-control.js +207 -23
  36. package/dist/application/supervisor-ours-tools.js +5 -11
  37. package/dist/application/task-layouts.d.ts +91 -0
  38. package/dist/application/task-layouts.js +259 -0
  39. package/dist/application/task-room-service.d.ts +99 -3
  40. package/dist/application/task-room-service.js +285 -32
  41. package/dist/application/types.d.ts +2 -0
  42. package/dist/atomic-file.d.ts +34 -1
  43. package/dist/atomic-file.js +161 -18
  44. package/dist/briefing.d.ts +3 -1
  45. package/dist/briefing.js +97 -31
  46. package/dist/build-info.json +9 -4
  47. package/dist/capabilities.d.ts +9 -1
  48. package/dist/capabilities.js +13 -0
  49. package/dist/cli.js +400 -37
  50. package/dist/client-profile.d.ts +5 -17
  51. package/dist/client-profile.js +4 -127
  52. package/dist/config.d.ts +45 -0
  53. package/dist/config.js +57 -6
  54. package/dist/creation.d.ts +1 -1
  55. package/dist/creation.js +4 -4
  56. package/dist/daemon-recovery.d.ts +1 -8
  57. package/dist/daemon-recovery.js +8 -113
  58. package/dist/docs.d.ts +1 -1
  59. package/dist/docs.js +207 -19
  60. package/dist/doctor.d.ts +4 -1
  61. package/dist/doctor.js +75 -60
  62. package/dist/erased-resources.d.ts +9 -0
  63. package/dist/erased-resources.js +87 -0
  64. package/dist/file-delivery/reader.d.ts +15 -0
  65. package/dist/file-delivery/reader.js +63 -0
  66. package/dist/file-delivery/store.d.ts +15 -0
  67. package/dist/file-delivery/store.js +124 -0
  68. package/dist/file-delivery/types.d.ts +19 -0
  69. package/dist/file-delivery/types.js +1 -0
  70. package/dist/fleet-command-audit.d.ts +7 -0
  71. package/dist/fleet-command-audit.js +24 -8
  72. package/dist/harness/agent-session.d.ts +4 -0
  73. package/dist/harness/claude-code-session.js +1 -1
  74. package/dist/harness/claude-code.js +21 -11
  75. package/dist/harness/claude-runtime.d.ts +12 -0
  76. package/dist/harness/claude-runtime.js +43 -0
  77. package/dist/harness/codex-app-server-proxy.d.ts +4 -1
  78. package/dist/harness/codex-app-server-proxy.js +41 -5
  79. package/dist/harness/codex-reasoning.d.ts +5 -0
  80. package/dist/harness/codex-reasoning.js +12 -0
  81. package/dist/harness/codex-runtime.d.ts +11 -1
  82. package/dist/harness/codex-runtime.js +31 -4
  83. package/dist/harness/codex-session.js +2 -2
  84. package/dist/harness/codex.js +24 -12
  85. package/dist/init-wizard.d.ts +2 -0
  86. package/dist/init-wizard.js +38 -19
  87. package/dist/isolation/policy.js +6 -1
  88. package/dist/isolation/types.d.ts +5 -0
  89. package/dist/managed-cli.d.ts +253 -0
  90. package/dist/managed-cli.js +923 -0
  91. package/dist/managed-recovery.d.ts +13 -0
  92. package/dist/managed-recovery.js +31 -0
  93. package/dist/minimal-setup.d.ts +2 -0
  94. package/dist/minimal-setup.js +44 -0
  95. package/dist/monitor.d.ts +8 -34
  96. package/dist/monitor.js +89 -152
  97. package/dist/notifications/fleet-producer.d.ts +19 -0
  98. package/dist/notifications/fleet-producer.js +208 -0
  99. package/dist/notifications/outbox.d.ts +46 -0
  100. package/dist/notifications/outbox.js +158 -0
  101. package/dist/notifications/target-cleanup.d.ts +10 -0
  102. package/dist/notifications/target-cleanup.js +63 -0
  103. package/dist/notifications/task-producer.d.ts +18 -0
  104. package/dist/notifications/task-producer.js +67 -0
  105. package/dist/ops.d.ts +2 -0
  106. package/dist/ops.js +20 -3
  107. package/dist/owner-channel/channel.js +17 -6
  108. package/dist/owner-channel/commands.d.ts +4 -0
  109. package/dist/owner-channel/commands.js +8 -0
  110. package/dist/owner-channel/lifecycle-outbox.js +4 -3
  111. package/dist/owner-channel/ours-client.js +2 -17
  112. package/dist/paths.d.ts +2 -0
  113. package/dist/paths.js +2 -0
  114. package/dist/preset-migration.js +43 -0
  115. package/dist/process-incarnation.d.ts +23 -0
  116. package/dist/process-incarnation.js +42 -0
  117. package/dist/reminders/manager.d.ts +75 -0
  118. package/dist/reminders/manager.js +278 -0
  119. package/dist/reminders/runtime.d.ts +27 -0
  120. package/dist/reminders/runtime.js +32 -0
  121. package/dist/reminders/schedule.d.ts +37 -0
  122. package/dist/reminders/schedule.js +104 -0
  123. package/dist/reminders/store.d.ts +341 -0
  124. package/dist/reminders/store.js +762 -0
  125. package/dist/reminders/tools.d.ts +20 -0
  126. package/dist/reminders/tools.js +114 -0
  127. package/dist/resolved-plan.js +1 -0
  128. package/dist/rooms-tasks/add-member.d.ts +48 -0
  129. package/dist/rooms-tasks/add-member.js +83 -0
  130. package/dist/rooms-tasks/archived-absence.d.ts +8 -0
  131. package/dist/rooms-tasks/archived-absence.js +48 -0
  132. package/dist/rooms-tasks/cli.js +65 -5
  133. package/dist/rooms-tasks/close.d.ts +6 -2
  134. package/dist/rooms-tasks/close.js +246 -37
  135. package/dist/rooms-tasks/config.d.ts +1 -1
  136. package/dist/rooms-tasks/config.js +11 -4
  137. package/dist/rooms-tasks/cowork-adapter.d.ts +12 -5
  138. package/dist/rooms-tasks/cowork-adapter.js +15 -121
  139. package/dist/rooms-tasks/cowork-http.js +56 -6
  140. package/dist/rooms-tasks/deletion.d.ts +1 -1
  141. package/dist/rooms-tasks/deletion.js +84 -5
  142. package/dist/rooms-tasks/erasure.d.ts +6 -0
  143. package/dist/rooms-tasks/erasure.js +173 -0
  144. package/dist/rooms-tasks/external-worker.d.ts +1 -1
  145. package/dist/rooms-tasks/external-worker.js +3 -4
  146. package/dist/rooms-tasks/layout-binding-client.d.ts +11 -0
  147. package/dist/rooms-tasks/layout-binding-client.js +56 -0
  148. package/dist/rooms-tasks/layout-binding-grants.d.ts +21 -0
  149. package/dist/rooms-tasks/layout-binding-grants.js +128 -0
  150. package/dist/rooms-tasks/layout-cli.d.ts +4 -0
  151. package/dist/rooms-tasks/layout-cli.js +107 -0
  152. package/dist/rooms-tasks/layout-config.d.ts +11 -0
  153. package/dist/rooms-tasks/layout-config.js +89 -0
  154. package/dist/rooms-tasks/layout-control.d.ts +21 -0
  155. package/dist/rooms-tasks/layout-control.js +52 -0
  156. package/dist/rooms-tasks/layout-member-retirement.d.ts +10 -0
  157. package/dist/rooms-tasks/layout-member-retirement.js +90 -0
  158. package/dist/rooms-tasks/layout-service.d.ts +43 -0
  159. package/dist/rooms-tasks/layout-service.js +203 -0
  160. package/dist/rooms-tasks/layout.d.ts +121 -0
  161. package/dist/rooms-tasks/layout.js +285 -0
  162. package/dist/rooms-tasks/legacy-task-member.d.ts +19 -0
  163. package/dist/rooms-tasks/legacy-task-member.js +116 -0
  164. package/dist/rooms-tasks/live-readiness.d.ts +18 -0
  165. package/dist/rooms-tasks/live-readiness.js +85 -0
  166. package/dist/rooms-tasks/member-ownership.d.ts +5 -0
  167. package/dist/rooms-tasks/member-ownership.js +40 -0
  168. package/dist/rooms-tasks/member-startup.d.ts +1 -0
  169. package/dist/rooms-tasks/member-startup.js +2 -0
  170. package/dist/rooms-tasks/provision.d.ts +3 -0
  171. package/dist/rooms-tasks/provision.js +101 -49
  172. package/dist/rooms-tasks/recovery.d.ts +19 -0
  173. package/dist/rooms-tasks/recovery.js +110 -0
  174. package/dist/rooms-tasks/room-state.d.ts +4 -2
  175. package/dist/rooms-tasks/room-state.js +47 -8
  176. package/dist/rooms-tasks/task-service-retirement.d.ts +11 -0
  177. package/dist/rooms-tasks/task-service-retirement.js +21 -0
  178. package/dist/rooms-tasks/task-state.d.ts +51 -8
  179. package/dist/rooms-tasks/task-state.js +245 -25
  180. package/dist/rooms-tasks/types.d.ts +40 -0
  181. package/dist/rooms-tasks/workspace-artifacts.d.ts +6 -0
  182. package/dist/rooms-tasks/workspace-artifacts.js +93 -0
  183. package/dist/rooms-tasks/workspace.d.ts +35 -0
  184. package/dist/rooms-tasks/workspace.js +325 -0
  185. package/dist/runner.d.ts +33 -4
  186. package/dist/runner.js +389 -56
  187. package/dist/session/acp.d.ts +75 -2
  188. package/dist/session/acp.js +345 -82
  189. package/dist/session/arbiter.d.ts +98 -0
  190. package/dist/session/arbiter.js +272 -3
  191. package/dist/session/codex-app-server.d.ts +45 -1
  192. package/dist/session/codex-app-server.js +225 -32
  193. package/dist/session/control.d.ts +17 -3
  194. package/dist/session/control.js +213 -23
  195. package/dist/session/conversation-normalizer.d.ts +2 -1
  196. package/dist/session/conversation-normalizer.js +1 -1
  197. package/dist/session/conversation-observation.d.ts +25 -0
  198. package/dist/session/conversation-observation.js +100 -0
  199. package/dist/session/conversation-replay.d.ts +50 -0
  200. package/dist/session/conversation-replay.js +203 -0
  201. package/dist/session/conversation-store.d.ts +15 -1
  202. package/dist/session/conversation-store.js +206 -26
  203. package/dist/session/conversation-types.d.ts +57 -4
  204. package/dist/session/prompt-batch.d.ts +79 -0
  205. package/dist/session/prompt-batch.js +120 -0
  206. package/dist/session/task-notice.d.ts +14 -0
  207. package/dist/session/task-notice.js +13 -0
  208. package/dist/session/types.d.ts +42 -1
  209. package/dist/setup-tunnel-preflight.d.ts +34 -0
  210. package/dist/setup-tunnel-preflight.js +62 -0
  211. package/dist/socket-path.d.ts +2 -0
  212. package/dist/socket-path.js +22 -0
  213. package/dist/spawn.d.ts +5 -1
  214. package/dist/spawn.js +40 -4
  215. package/dist/startup-prompt.d.ts +15 -0
  216. package/dist/startup-prompt.js +71 -0
  217. package/dist/startup-readiness.d.ts +32 -0
  218. package/dist/startup-readiness.js +101 -0
  219. package/dist/startup-telemetry.d.ts +16 -0
  220. package/dist/startup-telemetry.js +46 -0
  221. package/dist/subscriptions/cli.d.ts +91 -0
  222. package/dist/subscriptions/cli.js +223 -0
  223. package/dist/subscriptions/launch.d.ts +37 -0
  224. package/dist/subscriptions/launch.js +93 -0
  225. package/dist/subscriptions/login.d.ts +76 -0
  226. package/dist/subscriptions/login.js +207 -0
  227. package/dist/subscriptions/observed.d.ts +7 -0
  228. package/dist/subscriptions/observed.js +44 -0
  229. package/dist/subscriptions/probe.d.ts +10 -0
  230. package/dist/subscriptions/probe.js +73 -0
  231. package/dist/subscriptions/service.d.ts +102 -0
  232. package/dist/subscriptions/service.js +364 -0
  233. package/dist/subscriptions/store.d.ts +71 -0
  234. package/dist/subscriptions/store.js +140 -0
  235. package/dist/supervisor/adoption.d.ts +8 -0
  236. package/dist/supervisor/adoption.js +78 -0
  237. package/dist/supervisor/catalog.d.ts +62 -0
  238. package/dist/supervisor/catalog.js +254 -0
  239. package/dist/supervisor/fleet.d.ts +8 -0
  240. package/dist/supervisor/fleet.js +89 -0
  241. package/dist/supervisor/index.js +3 -6
  242. package/dist/supervisor/launchd.d.ts +1 -1
  243. package/dist/supervisor/launchd.js +26 -13
  244. package/dist/supervisor/legacy.d.ts +35 -0
  245. package/dist/supervisor/legacy.js +524 -0
  246. package/dist/supervisor/manager.d.ts +15 -0
  247. package/dist/supervisor/manager.js +270 -0
  248. package/dist/supervisor/registration-refresh.d.ts +45 -0
  249. package/dist/supervisor/registration-refresh.js +266 -0
  250. package/dist/supervisor/scope.d.ts +4 -0
  251. package/dist/supervisor/scope.js +10 -0
  252. package/dist/supervisor/systemd.d.ts +2 -2
  253. package/dist/supervisor/systemd.js +19 -33
  254. package/dist/task-supervision.d.ts +9 -0
  255. package/dist/task-supervision.js +145 -0
  256. package/dist/temp-idle.d.ts +17 -0
  257. package/dist/temp-idle.js +42 -0
  258. package/dist/temp-lifecycle.d.ts +23 -8
  259. package/dist/temp-lifecycle.js +123 -66
  260. package/dist/temp-supervisor-recovery.d.ts +1 -1
  261. package/dist/temp-supervisor-recovery.js +2 -2
  262. package/dist/tunnel-setup-completion.d.ts +7 -0
  263. package/dist/tunnel-setup-completion.js +13 -0
  264. package/dist/tunnel-setup-input.d.ts +19 -0
  265. package/dist/tunnel-setup-input.js +124 -0
  266. package/dist/web/agent-attachments.d.ts +29 -0
  267. package/dist/web/agent-attachments.js +162 -0
  268. package/dist/web/auth.d.ts +65 -2
  269. package/dist/web/auth.js +201 -11
  270. package/dist/web/available-port.d.ts +2 -0
  271. package/dist/web/available-port.js +23 -0
  272. package/dist/web/control.d.ts +3 -3
  273. package/dist/web/control.js +10 -8
  274. package/dist/web/conversation-stream-codec.d.ts +23 -0
  275. package/dist/web/conversation-stream-codec.js +110 -0
  276. package/dist/web/conversation-stream.d.ts +25 -0
  277. package/dist/web/conversation-stream.js +231 -0
  278. package/dist/web/fleet-config-service.d.ts +4 -1
  279. package/dist/web/fleet-config-service.js +46 -15
  280. package/dist/web/prefix-gateway.d.ts +32 -0
  281. package/dist/web/prefix-gateway.js +238 -0
  282. package/dist/web/runtime.d.ts +3 -1
  283. package/dist/web/runtime.js +128 -33
  284. package/dist/web/server.d.ts +16 -0
  285. package/dist/web/server.js +639 -55
  286. package/dist/web/service.d.ts +11 -2
  287. package/dist/web/service.js +30 -4
  288. package/dist/web/workspace-devices.d.ts +107 -0
  289. package/dist/web/workspace-devices.js +277 -0
  290. package/dist/web/workspace-tunnel.d.ts +1 -0
  291. package/dist/web/workspace-tunnel.js +34 -0
  292. package/dist/workspace-enrollment.d.ts +83 -0
  293. package/dist/workspace-enrollment.js +580 -0
  294. package/dist/workspace-replacement.d.ts +66 -0
  295. package/dist/workspace-replacement.js +220 -0
  296. package/docs/acp-files.md +146 -0
  297. package/docs/conversation-stream.md +190 -0
  298. package/docs/fleet-migration.md +54 -0
  299. package/docs/live-voice.md +13 -0
  300. package/docs/room-layouts.md +227 -0
  301. package/docs/self-reminders.md +374 -0
  302. package/docs/task-workspaces.md +225 -0
  303. package/docs/validation/managed-cli-permissions.md +421 -0
  304. package/examples/room-layouts/fresh.yaml +27 -0
  305. package/examples/room-layouts/scoped.yaml +21 -0
  306. package/examples/room-layouts/shared.yaml +19 -0
  307. package/package.json +24 -20
  308. package/presets/README.md +78 -0
  309. package/presets/brain-catalog.json +8 -2
  310. package/presets/fleet/agent_templates/Critic.yaml +1 -1
  311. package/presets/fleet/agent_templates/Developer.yaml +1 -1
  312. package/presets/fleet/agent_templates/Engineer.yaml +5 -0
  313. package/presets/fleet/agent_templates/LocalCoordinator.yaml +4 -2
  314. package/presets/fleet/agents/FleetCoordinator.yaml +1 -1
  315. package/presets/fleet/brains/claude-fable-5-1-high.yaml +5 -0
  316. package/presets/fleet/brains/claude-fable-5-1-low.yaml +5 -0
  317. package/presets/fleet/brains/claude-fable-5-1-max.yaml +5 -0
  318. package/presets/fleet/brains/claude-fable-5-1-medium.yaml +5 -0
  319. package/presets/fleet/brains/claude-fable-5-1-xhigh.yaml +5 -0
  320. package/presets/fleet/brains/claude-fable-5-high.yaml +1 -1
  321. package/presets/fleet/brains/claude-fable-5-low.yaml +1 -1
  322. package/presets/fleet/brains/claude-fable-5-max.yaml +1 -1
  323. package/presets/fleet/brains/claude-fable-5-medium.yaml +1 -1
  324. package/presets/fleet/brains/claude-fable-5-xhigh.yaml +1 -1
  325. package/presets/fleet/brains/claude-haiku-4-5-20251001-default.yaml +4 -0
  326. package/presets/fleet/brains/claude-opus-5-5-high.yaml +5 -0
  327. package/presets/fleet/brains/claude-opus-5-5-low.yaml +5 -0
  328. package/presets/fleet/brains/claude-opus-5-5-max.yaml +5 -0
  329. package/presets/fleet/brains/claude-opus-5-5-medium.yaml +5 -0
  330. package/presets/fleet/brains/claude-opus-5-5-xhigh.yaml +5 -0
  331. package/presets/fleet/brains/claude-opus-5-high.yaml +1 -1
  332. package/presets/fleet/brains/claude-opus-5-low.yaml +1 -1
  333. package/presets/fleet/brains/claude-opus-5-max.yaml +1 -1
  334. package/presets/fleet/brains/claude-opus-5-medium.yaml +1 -1
  335. package/presets/fleet/brains/claude-opus-5-xhigh.yaml +1 -1
  336. package/presets/fleet/brains/claude-sonnet-5-high.yaml +1 -1
  337. package/presets/fleet/brains/claude-sonnet-5-low.yaml +1 -1
  338. package/presets/fleet/brains/claude-sonnet-5-max.yaml +1 -1
  339. package/presets/fleet/brains/claude-sonnet-5-medium.yaml +1 -1
  340. package/presets/fleet/brains/claude-sonnet-5-xhigh.yaml +1 -1
  341. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-high.yaml +1 -1
  342. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-low.yaml +1 -1
  343. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-medium.yaml +1 -1
  344. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-xhigh.yaml +1 -1
  345. package/presets/fleet/brains/codex-gpt-5-4-high.yaml +1 -1
  346. package/presets/fleet/brains/codex-gpt-5-4-low.yaml +1 -1
  347. package/presets/fleet/brains/codex-gpt-5-4-medium.yaml +1 -1
  348. package/presets/fleet/brains/codex-gpt-5-4-mini-high.yaml +1 -1
  349. package/presets/fleet/brains/codex-gpt-5-4-mini-low.yaml +1 -1
  350. package/presets/fleet/brains/codex-gpt-5-4-mini-medium.yaml +1 -1
  351. package/presets/fleet/brains/codex-gpt-5-4-mini-xhigh.yaml +1 -1
  352. package/presets/fleet/brains/codex-gpt-5-4-xhigh.yaml +1 -1
  353. package/presets/fleet/brains/codex-gpt-5-5-high.yaml +1 -1
  354. package/presets/fleet/brains/codex-gpt-5-5-low.yaml +1 -1
  355. package/presets/fleet/brains/codex-gpt-5-5-medium.yaml +1 -1
  356. package/presets/fleet/brains/codex-gpt-5-5-xhigh.yaml +1 -1
  357. package/presets/fleet/brains/codex-gpt-5-6-luna-high.yaml +1 -1
  358. package/presets/fleet/brains/codex-gpt-5-6-luna-low.yaml +1 -1
  359. package/presets/fleet/brains/codex-gpt-5-6-luna-max.yaml +1 -1
  360. package/presets/fleet/brains/codex-gpt-5-6-luna-medium.yaml +1 -1
  361. package/presets/fleet/brains/codex-gpt-5-6-luna-xhigh.yaml +1 -1
  362. package/presets/fleet/brains/codex-gpt-5-6-sol-high.yaml +1 -1
  363. package/presets/fleet/brains/codex-gpt-5-6-sol-low.yaml +1 -1
  364. package/presets/fleet/brains/codex-gpt-5-6-sol-max.yaml +1 -1
  365. package/presets/fleet/brains/codex-gpt-5-6-sol-medium.yaml +1 -1
  366. package/presets/fleet/brains/codex-gpt-5-6-sol-ultra.yaml +1 -1
  367. package/presets/fleet/brains/codex-gpt-5-6-sol-xhigh.yaml +1 -1
  368. package/presets/fleet/brains/codex-gpt-5-6-terra-high.yaml +1 -1
  369. package/presets/fleet/brains/codex-gpt-5-6-terra-low.yaml +1 -1
  370. package/presets/fleet/brains/codex-gpt-5-6-terra-max.yaml +1 -1
  371. package/presets/fleet/brains/codex-gpt-5-6-terra-medium.yaml +1 -1
  372. package/presets/fleet/brains/codex-gpt-5-6-terra-ultra.yaml +1 -1
  373. package/presets/fleet/brains/codex-gpt-5-6-terra-xhigh.yaml +1 -1
  374. package/presets/fleet/brains/codex-gpt-6-1-sol-high.yaml +5 -0
  375. package/presets/fleet/brains/codex-gpt-6-1-sol-low.yaml +5 -0
  376. package/presets/fleet/brains/codex-gpt-6-1-sol-max.yaml +5 -0
  377. package/presets/fleet/brains/codex-gpt-6-1-sol-medium.yaml +5 -0
  378. package/presets/fleet/brains/codex-gpt-6-1-sol-ultra.yaml +5 -0
  379. package/presets/fleet/brains/codex-gpt-6-1-sol-xhigh.yaml +5 -0
  380. package/presets/fleet/brains/codex-gpt-6-astra-high.yaml +1 -1
  381. package/presets/fleet/brains/codex-gpt-6-astra-low.yaml +1 -1
  382. package/presets/fleet/brains/codex-gpt-6-astra-max.yaml +1 -1
  383. package/presets/fleet/brains/codex-gpt-6-astra-medium.yaml +1 -1
  384. package/presets/fleet/brains/codex-gpt-6-astra-ultra.yaml +1 -1
  385. package/presets/fleet/brains/codex-gpt-6-astra-xhigh.yaml +1 -1
  386. package/presets/fleet/brains/codex-gpt-6-luna-high.yaml +5 -0
  387. package/presets/fleet/brains/codex-gpt-6-luna-low.yaml +5 -0
  388. package/presets/fleet/brains/codex-gpt-6-luna-max.yaml +5 -0
  389. package/presets/fleet/brains/codex-gpt-6-luna-medium.yaml +5 -0
  390. package/presets/fleet/brains/codex-gpt-6-luna-xhigh.yaml +5 -0
  391. package/presets/fleet/brains/codex-gpt-6-sol-high.yaml +5 -0
  392. package/presets/fleet/brains/codex-gpt-6-sol-low.yaml +5 -0
  393. package/presets/fleet/brains/codex-gpt-6-sol-max.yaml +5 -0
  394. package/presets/fleet/brains/codex-gpt-6-sol-medium.yaml +5 -0
  395. package/presets/fleet/brains/codex-gpt-6-sol-ultra.yaml +5 -0
  396. package/presets/fleet/brains/codex-gpt-6-sol-xhigh.yaml +5 -0
  397. package/presets/fleet/roles/Coordinator.yaml +12 -11
  398. package/presets/fleet/roles/Critic.yaml +13 -0
  399. package/presets/fleet/roles/Developer.yaml +13 -0
  400. package/presets/fleet/roles/Engineer.yaml +53 -0
  401. package/presets/fleet/room_layouts/engineering.yaml +25 -0
  402. package/presets/fleet/room_layouts/pair.yaml +24 -0
  403. package/presets/fleet/room_layouts/single.yaml +17 -0
  404. package/presets/fleet/room_layouts/team.yaml +30 -0
  405. package/presets/fleet/room_templates/engineering.yaml +19 -0
  406. package/presets/manifest.json +2 -2
  407. package/dist/web-app/assets/index-59GF-gsZ.css +0 -1
  408. package/dist/web-app/assets/index-C0h3ALvs.js +0 -10
  409. package/dist/web-app/icons/ours-fleet-maskable.svg +0 -4
  410. package/dist/web-app/icons/ours-fleet.svg +0 -4
  411. package/dist/web-app/index.html +0 -17
  412. package/dist/web-app/manifest.webmanifest +0 -15
  413. package/dist/web-app/offline.html +0 -18
  414. package/dist/web-app/sw.js +0 -51
@@ -1,9 +1,10 @@
1
+ import { socketPath as privateSocketPath } from '../socket-path.js';
1
2
  import { chmodSync, mkdirSync, rmSync } from 'node:fs';
2
3
  import { createConnection, createServer } from 'node:net';
3
4
  import { join } from 'node:path';
4
5
  import { FleetError, safeLine } from '../application/errors.js';
5
6
  import { stateRoot } from '../paths.js';
6
- export const webControlPath = (dir = join(stateRoot(), 'web')) => join(dir, 'control.sock');
7
+ export const webControlPath = (dir = join(stateRoot(), 'web')) => privateSocketPath(join(dir, 'control.sock'));
7
8
  export async function startWebControlServer(options) {
8
9
  const dir = options.dir ?? join(stateRoot(), 'web');
9
10
  const path = webControlPath(dir);
@@ -34,13 +35,14 @@ export async function startWebControlServer(options) {
34
35
  if (used > rateLimit)
35
36
  throw new FleetError('rate_limited', 'local web control rate limit exceeded');
36
37
  const parsed = JSON.parse(body.slice(0, body.indexOf('\n')));
37
- if (parsed.command === 'open')
38
- await options.onOpen();
38
+ let result;
39
+ if (parsed.command === 'link-device' && options.onLinkDevice)
40
+ result = await options.onLinkDevice();
39
41
  else if (parsed.command === 'revoke-all')
40
42
  await options.onRevokeAll();
41
43
  else
42
44
  throw new FleetError('invalid_request', 'unknown local web control command');
43
- socket.end(JSON.stringify({ ok: true }) + '\n');
45
+ socket.end(JSON.stringify({ ok: true, result }) + '\n');
44
46
  }
45
47
  catch (error) {
46
48
  const code = error instanceof FleetError ? error.code : 'internal';
@@ -65,7 +67,7 @@ export async function startWebControlServer(options) {
65
67
  };
66
68
  }
67
69
  export async function requestWebControl(command, path = webControlPath(), timeoutMs = 5_000) {
68
- await new Promise((resolve, reject) => {
70
+ return await new Promise((resolve, reject) => {
69
71
  const socket = createConnection(path);
70
72
  let response = '';
71
73
  let settled = false;
@@ -73,7 +75,7 @@ export async function requestWebControl(command, path = webControlPath(), timeou
73
75
  socket.destroy();
74
76
  reject(new FleetError('timeout', 'local web control request timed out'));
75
77
  }, timeoutMs);
76
- const finish = (error) => {
78
+ const finish = (error, result) => {
77
79
  if (settled)
78
80
  return;
79
81
  settled = true;
@@ -81,7 +83,7 @@ export async function requestWebControl(command, path = webControlPath(), timeou
81
83
  if (error)
82
84
  reject(error);
83
85
  else
84
- resolve();
86
+ resolve(result);
85
87
  };
86
88
  socket.once('connect', () => socket.write(JSON.stringify({ command }) + '\n'));
87
89
  socket.on('data', chunk => { response += chunk.toString('utf8'); });
@@ -91,7 +93,7 @@ export async function requestWebControl(command, path = webControlPath(), timeou
91
93
  const parsed = JSON.parse(response);
92
94
  if (!parsed.ok)
93
95
  throw new FleetError(parsed.error?.code === 'rate_limited' ? 'rate_limited' : 'rejected', parsed.error?.message ?? 'local web control request was rejected');
94
- finish();
96
+ finish(undefined, parsed.result);
95
97
  }
96
98
  catch (error) {
97
99
  finish(error);
@@ -0,0 +1,23 @@
1
+ /** Public v1 wire contract. Kept free of Node APIs so browser consumers can share it. */
2
+ export declare const MAX_FRAME_BYTES: number;
3
+ export declare const MAX_ASSEMBLY_BYTES: number;
4
+ export declare const MAX_PENDING_BYTES: number;
5
+ export type ConversationRecordType = 'ready' | 'ledger' | 'state' | 'role' | 'resync' | 'keepalive';
6
+ export interface ConversationStreamRecord {
7
+ version: 1;
8
+ type: ConversationRecordType;
9
+ payload: unknown;
10
+ }
11
+ /** A payload is the logical record; SSE/JSON envelopes do not consume its assembly budget. */
12
+ export declare function encodeConversationRecord(type: ConversationRecordType, payload: unknown, id: string): Generator<string>;
13
+ /** Strict incremental decoder: one assembly, no partial publication, UTF-8 split safe. */
14
+ export declare class ConversationStreamDecoder {
15
+ private readonly publish;
16
+ private decoder;
17
+ private pending;
18
+ private assembly?;
19
+ constructor(publish: (record: ConversationStreamRecord) => void);
20
+ push(bytes: Uint8Array): void;
21
+ finish(): void;
22
+ private accept;
23
+ }
@@ -0,0 +1,110 @@
1
+ /** Public v1 wire contract. Kept free of Node APIs so browser consumers can share it. */
2
+ export const MAX_FRAME_BYTES = 512 * 1024;
3
+ export const MAX_ASSEMBLY_BYTES = 16 * 1024 * 1024;
4
+ export const MAX_PENDING_BYTES = 8 * 1024 * 1024;
5
+ const utf8 = new TextEncoder();
6
+ const types = new Set(['ready', 'ledger', 'state', 'role', 'resync', 'keepalive']);
7
+ const frame = (value) => `event: conversation\ndata: ${JSON.stringify(value)}\n\n`;
8
+ const identity = (payload) => ({ generation: payload?.sessionGeneration ?? payload?.snapshot?.sessionGeneration,
9
+ revision: payload?.stateRevision ?? payload?.snapshot?.stateRevision ?? payload?.roleRevision });
10
+ /** A payload is the logical record; SSE/JSON envelopes do not consume its assembly budget. */
11
+ export function* encodeConversationRecord(type, payload, id) {
12
+ const json = JSON.stringify(payload), totalBytes = utf8.encode(json).byteLength;
13
+ if (totalBytes > MAX_ASSEMBLY_BYTES)
14
+ throw new Error('conversation record exceeds assembly limit');
15
+ const direct = frame({ version: 1, type, payload });
16
+ if (utf8.encode(direct).byteLength <= MAX_FRAME_BYTES) {
17
+ yield direct;
18
+ return;
19
+ }
20
+ const position = identity(payload);
21
+ yield frame({ version: 1, type: 'start', id, recordType: type, totalBytes, ...position });
22
+ // At most 48K UTF-16 units: even worst-case JSON escapes fit a 512 KiB frame.
23
+ let index = 0;
24
+ for (let offset = 0; offset < json.length;) {
25
+ let end = Math.min(json.length, offset + 48 * 1024);
26
+ if (end < json.length && /[\uD800-\uDBFF]/.test(json[end - 1]))
27
+ end--;
28
+ yield frame({ version: 1, type: 'chunk', id, index: index++, data: json.slice(offset, end) });
29
+ offset = end;
30
+ }
31
+ yield frame({ version: 1, type: 'commit', id, totalBytes, ...position });
32
+ }
33
+ /** Strict incremental decoder: one assembly, no partial publication, UTF-8 split safe. */
34
+ export class ConversationStreamDecoder {
35
+ publish;
36
+ decoder = new TextDecoder('utf-8', { fatal: true });
37
+ pending = '';
38
+ assembly;
39
+ constructor(publish) {
40
+ this.publish = publish;
41
+ }
42
+ push(bytes) {
43
+ try {
44
+ this.pending += this.decoder.decode(bytes, { stream: true });
45
+ for (;;) {
46
+ const end = this.pending.indexOf('\n\n');
47
+ if (end < 0)
48
+ break;
49
+ const text = this.pending.slice(0, end + 2);
50
+ this.pending = this.pending.slice(end + 2);
51
+ if (utf8.encode(text).byteLength > MAX_FRAME_BYTES)
52
+ throw new Error('frame limit exceeded');
53
+ if (!text.startsWith('event: conversation\ndata: '))
54
+ throw new Error('invalid conversation frame');
55
+ this.accept(JSON.parse(text.slice(26, -2)));
56
+ }
57
+ if (utf8.encode(this.pending).byteLength > MAX_FRAME_BYTES)
58
+ throw new Error('frame limit exceeded');
59
+ }
60
+ catch (error) {
61
+ this.pending = '';
62
+ this.assembly = undefined;
63
+ throw error;
64
+ }
65
+ }
66
+ finish() {
67
+ const rest = this.decoder.decode();
68
+ if (rest || this.pending || this.assembly)
69
+ throw new Error('incomplete conversation stream');
70
+ }
71
+ accept(value) {
72
+ if (value?.version !== 1)
73
+ throw new Error('unsupported conversation version');
74
+ if (types.has(value.type)) {
75
+ if (this.assembly)
76
+ throw new Error('overlapping assembly');
77
+ this.publish(value);
78
+ return;
79
+ }
80
+ if (value.type === 'start') {
81
+ if (this.assembly)
82
+ throw new Error('overlapping assembly');
83
+ if (typeof value.id !== 'string' || !types.has(value.recordType) || !Number.isSafeInteger(value.totalBytes) || value.totalBytes < 1 || value.totalBytes > MAX_ASSEMBLY_BYTES)
84
+ throw new Error('invalid assembly length/type');
85
+ this.assembly = { id: value.id, type: value.recordType, total: value.totalBytes, bytes: 0, index: 0, parts: [], generation: value.generation, revision: value.revision };
86
+ return;
87
+ }
88
+ const assembly = this.assembly;
89
+ if (!assembly || value.id !== assembly.id)
90
+ throw new Error('assembly identity mismatch');
91
+ if (value.type === 'chunk') {
92
+ if (value.index !== assembly.index++ || typeof value.data !== 'string')
93
+ throw new Error('assembly chunk order');
94
+ if (!value.data.length)
95
+ throw new Error('assembly empty chunk');
96
+ assembly.bytes += utf8.encode(value.data).byteLength;
97
+ if (assembly.bytes > assembly.total)
98
+ throw new Error('assembly length exceeded');
99
+ assembly.parts.push(value.data);
100
+ return;
101
+ }
102
+ if (value.type !== 'commit' || value.totalBytes !== assembly.total || assembly.bytes !== assembly.total)
103
+ throw new Error('assembly length mismatch');
104
+ const payload = JSON.parse(assembly.parts.join('')), position = identity(payload);
105
+ if (value.generation !== assembly.generation || value.revision !== assembly.revision || position.generation !== assembly.generation || position.revision !== assembly.revision)
106
+ throw new Error('assembly revision mismatch');
107
+ this.assembly = undefined;
108
+ this.publish({ version: 1, type: assembly.type, payload });
109
+ }
110
+ }
@@ -0,0 +1,25 @@
1
+ import type { Writable } from 'node:stream';
2
+ import { type ConversationRecordType } from './conversation-stream-codec.js';
3
+ /** One active logical record; only waiting encoded output consumes the pending budget. */
4
+ export declare class ConversationStreamWriter {
5
+ private readonly output;
6
+ private readonly failed;
7
+ private queue;
8
+ private active?;
9
+ private waiting;
10
+ private ended;
11
+ private sequence;
12
+ pendingBytes: number;
13
+ get busy(): boolean;
14
+ constructor(output: Writable, failed: (reason: string) => void);
15
+ private error;
16
+ private drain;
17
+ send(type: ConversationRecordType, payload: unknown): boolean;
18
+ private flush;
19
+ private fail;
20
+ close(): void;
21
+ }
22
+ import type { FastifyInstance } from 'fastify';
23
+ import type { WebServices } from './server.js';
24
+ import type { WebAuth } from './auth.js';
25
+ export declare function registerConversationStream(app: FastifyInstance, services: WebServices, auth: WebAuth): void;
@@ -0,0 +1,231 @@
1
+ import { encodeConversationRecord, MAX_PENDING_BYTES } from './conversation-stream-codec.js';
2
+ /** One active logical record; only waiting encoded output consumes the pending budget. */
3
+ export class ConversationStreamWriter {
4
+ output;
5
+ failed;
6
+ queue = [];
7
+ active;
8
+ waiting = false;
9
+ ended = false;
10
+ sequence = 0;
11
+ pendingBytes = 0;
12
+ get busy() { return Boolean(this.active); }
13
+ constructor(output, failed) {
14
+ this.output = output;
15
+ this.failed = failed;
16
+ output.on('drain', this.drain);
17
+ output.once('error', this.error);
18
+ }
19
+ error = () => this.fail('transport-error');
20
+ drain = () => { this.waiting = false; this.flush(); };
21
+ send(type, payload) {
22
+ if (this.ended)
23
+ return false;
24
+ const id = String(++this.sequence);
25
+ try {
26
+ if (!this.active) {
27
+ this.active = encodeConversationRecord(type, payload, id);
28
+ this.flush();
29
+ }
30
+ else {
31
+ let bytes = 0;
32
+ for (const frame of encodeConversationRecord(type, payload, id))
33
+ bytes += Buffer.byteLength(frame);
34
+ if (this.pendingBytes + bytes > MAX_PENDING_BYTES) {
35
+ this.fail('output-overflow');
36
+ return false;
37
+ }
38
+ this.queue.push({ type, payload, id, bytes });
39
+ this.pendingBytes += bytes;
40
+ }
41
+ return !this.ended;
42
+ }
43
+ catch {
44
+ this.fail('record-too-large');
45
+ return false;
46
+ }
47
+ }
48
+ flush() {
49
+ if (this.ended || this.waiting)
50
+ return;
51
+ try {
52
+ while (this.active) {
53
+ const next = this.active.next();
54
+ if (!next.done) {
55
+ if (!this.output.write(next.value)) {
56
+ this.waiting = true;
57
+ return;
58
+ }
59
+ }
60
+ else {
61
+ const queued = this.queue.shift();
62
+ this.active = queued ? encodeConversationRecord(queued.type, queued.payload, queued.id) : undefined;
63
+ if (queued)
64
+ this.pendingBytes -= queued.bytes;
65
+ }
66
+ }
67
+ }
68
+ catch {
69
+ this.fail('record-too-large');
70
+ }
71
+ }
72
+ fail(reason) {
73
+ if (this.ended)
74
+ return;
75
+ // A resync is only useful at a frame boundary with writable capacity.
76
+ if (!this.waiting && !this.output.destroyed) {
77
+ const frame = encodeConversationRecord('resync', { reason, capability: 'degraded', retry: reason !== 'record-too-large' }, 'terminal').next().value;
78
+ if (frame)
79
+ this.output.write(frame);
80
+ }
81
+ const blocked = this.waiting;
82
+ this.close();
83
+ this.failed(reason);
84
+ if (blocked)
85
+ this.output.destroy();
86
+ }
87
+ close() {
88
+ if (this.ended)
89
+ return;
90
+ this.ended = true;
91
+ this.active = undefined;
92
+ this.queue = [];
93
+ this.pendingBytes = 0;
94
+ this.output.off('drain', this.drain);
95
+ this.output.off('error', this.error);
96
+ }
97
+ }
98
+ import { FleetError } from '../application/errors.js';
99
+ export function registerConversationStream(app, services, auth) {
100
+ const connections = new Set();
101
+ app.addHook('preClose', async () => { for (const close of connections)
102
+ close(); });
103
+ app.get('/api/v1/roles/:id/conversation/events', async (request, reply) => {
104
+ const browser = auth.authenticate(request);
105
+ const generation = request.query.expectedGeneration, rawAfter = request.query.after ?? '0', after = Number(rawAfter);
106
+ if (typeof generation !== 'string' || !generation.trim() || generation.length > 512 || !/^\d+$/.test(rawAfter) || !Number.isSafeInteger(after))
107
+ throw new FleetError('invalid_request', 'expectedGeneration and a nonnegative integer after cursor are required');
108
+ if (!services.observeRole)
109
+ throw new FleetError('capability_unavailable', 'Complete conversation observation is unavailable');
110
+ let ended = false, owner, follow;
111
+ let binding, timer;
112
+ let epoch = 0, revision = -1, lifetime, available = false;
113
+ let abort = new AbortController();
114
+ const close = () => {
115
+ if (ended)
116
+ return;
117
+ ended = true;
118
+ epoch++;
119
+ abort.abort();
120
+ if (timer)
121
+ clearInterval(timer);
122
+ binding?.();
123
+ writer.close();
124
+ follow?.close();
125
+ follow = undefined;
126
+ owner?.dispose();
127
+ connections.delete(close);
128
+ reply.raw.end();
129
+ };
130
+ const writer = new ConversationStreamWriter(reply.raw, close);
131
+ const resync = (reason, retry = true) => writer.send('resync', { reason, retry, capability: 'degraded' });
132
+ const valid = () => { if (auth.sessionValid(browser))
133
+ return true; writer.send('resync', { reason: 'auth-expired', retry: browser.rotating ? 'authenticate' : 'reauthenticate', capability: 'auth-expired' }); close(); return false; };
134
+ const stopFollow = () => { epoch++; abort.abort(); abort = new AbortController(); follow?.close(); follow = undefined; };
135
+ const open = async () => {
136
+ const current = ++epoch, signal = abort.signal;
137
+ const active = () => !ended && current === epoch && !signal.aborted;
138
+ try {
139
+ const control = await services.session(request.params.id);
140
+ if (!active() || !valid())
141
+ return;
142
+ const descriptor = await control.describe(owner);
143
+ if (!active() || !valid())
144
+ return;
145
+ if (!control.followConversation || !descriptor.features.includes('conversation_live_complete')) {
146
+ resync('capability-unavailable', false);
147
+ return;
148
+ }
149
+ const handle = await control.followConversation({ controller: false, expectedGeneration: generation, after, signal, roleObservation: owner,
150
+ onReplay: page => { if (active() && valid()) {
151
+ for (const event of page.events)
152
+ writer.send('ledger', event);
153
+ writer.send('ledger', { coveredThrough: page.coveredThrough });
154
+ } },
155
+ onReady: ready => {
156
+ if (active() && valid()) {
157
+ writer.send('state', ready.snapshot);
158
+ writer.send('ready', { sessionGeneration: ready.snapshot.sessionGeneration, stateRevision: ready.snapshot.stateRevision,
159
+ ledgerBarrier: ready.snapshot.ledgerBarrier, coveredThrough: ready.coveredThrough, capability: 'complete', authorizedUntil: browser.absoluteExpiresAt });
160
+ }
161
+ },
162
+ onEvent: event => { if (active() && valid()) {
163
+ writer.send('ledger', event);
164
+ writer.send('ledger', { coveredThrough: event.seq });
165
+ } },
166
+ onState: state => { if (active() && valid())
167
+ writer.send('state', state); },
168
+ onClose: reason => { if (active()) {
169
+ epoch++;
170
+ abort.abort();
171
+ follow = undefined;
172
+ resync(reason ?? 'control-closed', !reason?.includes('assembly limit'));
173
+ } },
174
+ });
175
+ if (!active())
176
+ handle.close();
177
+ else
178
+ follow = handle;
179
+ }
180
+ catch {
181
+ if (active())
182
+ resync('observation-unavailable');
183
+ }
184
+ };
185
+ const changed = (state) => {
186
+ if (ended || state.roleRevision <= revision)
187
+ return;
188
+ revision = state.roleRevision;
189
+ const { stateDir: _private, ...publicState } = state;
190
+ writer.send('role', publicState);
191
+ const replaced = lifetime !== undefined && lifetime !== state.roleLifetime;
192
+ const nextAvailable = state.availability === 'available' && state.health === 'healthy';
193
+ if (replaced || !nextAvailable)
194
+ stopFollow();
195
+ if (state.health !== 'healthy')
196
+ resync(state.failure ?? 'role-observation-unavailable', state.health !== 'unsupported');
197
+ else if (replaced)
198
+ resync('role-replaced');
199
+ const shouldOpen = nextAvailable && (!available || replaced);
200
+ lifetime = state.roleLifetime;
201
+ available = nextAvailable;
202
+ if (shouldOpen)
203
+ void open();
204
+ };
205
+ reply.hijack();
206
+ for (const [name, value] of Object.entries(reply.getHeaders()))
207
+ if (value !== undefined)
208
+ reply.raw.setHeader(name, Array.isArray(value) ? value : String(value));
209
+ reply.raw.writeHead(200, { 'Content-Type': 'text/event-stream; charset=utf-8', 'Cache-Control': 'no-store', 'X-Accel-Buffering': 'no' });
210
+ reply.raw.flushHeaders();
211
+ reply.raw.once('close', close);
212
+ connections.add(close);
213
+ binding = auth.bindTransport(browser, () => { writer.send('resync', { reason: 'auth-expired', capability: 'auth-expired', retry: browser.rotating ? 'authenticate' : 'reauthenticate' }); close(); });
214
+ try {
215
+ // Factory callbacks can be synchronous; defer handling until the owner can be borrowed.
216
+ owner = services.observeRole(request.params.id, state => queueMicrotask(() => changed(state)));
217
+ if (ended) {
218
+ owner.dispose();
219
+ return;
220
+ }
221
+ changed(owner.snapshot());
222
+ timer = setInterval(() => { if (valid())
223
+ writer.send('keepalive', { at: Date.now() }); }, 1_000);
224
+ timer.unref();
225
+ }
226
+ catch {
227
+ resync('role-observation-unavailable');
228
+ close();
229
+ }
230
+ });
231
+ }
@@ -4,6 +4,9 @@ export interface EditableFleetModel {
4
4
  manifest: Record<string, unknown>;
5
5
  agents: Record<string, Record<string, unknown>>;
6
6
  agent_templates: Record<string, Record<string, unknown>>;
7
+ roles?: Record<string, Record<string, unknown>>;
8
+ brains?: Record<string, Record<string, unknown>>;
9
+ room_templates?: Record<string, Record<string, unknown>>;
7
10
  }
8
11
  export interface ConfigReadResult {
9
12
  path: string;
@@ -45,7 +48,7 @@ export declare class FleetConfigService {
45
48
  private readonly preflight;
46
49
  private readonly beforeMutation?;
47
50
  constructor(options?: FleetConfigServiceOptions);
48
- read(): ConfigReadResult;
51
+ read(includeDefinitions?: boolean): ConfigReadResult;
49
52
  preview(baseRevision: string, input: unknown): Promise<ConfigPreviewResult>;
50
53
  write(baseRevision: string, input: unknown): Promise<ConfigWriteResult>;
51
54
  }
@@ -23,7 +23,7 @@ export class FleetConfigService {
23
23
  this.preflight = options.preflight ?? (async () => emptyReport());
24
24
  this.beforeMutation = options.beforeMutation;
25
25
  }
26
- read() {
26
+ read(includeDefinitions = false) {
27
27
  const sources = readSources(this.path);
28
28
  try {
29
29
  loadConfig(this.path, { yamlMode: 'strict' });
@@ -31,7 +31,7 @@ export class FleetConfigService {
31
31
  catch (error) {
32
32
  throw safeError(error, sourceSensitiveValues(sources));
33
33
  }
34
- const raw = modelFrom(sources);
34
+ const raw = modelFrom(sources, includeDefinitions);
35
35
  const redacted = redactModel(raw);
36
36
  return {
37
37
  path: basename(this.path), exists: true, firstRun: false,
@@ -41,7 +41,7 @@ export class FleetConfigService {
41
41
  async preview(baseRevision, input) {
42
42
  const current = readSources(this.path);
43
43
  assertRevision(baseRevision, current);
44
- const currentModel = modelFrom(current);
44
+ const currentModel = modelFrom(current, !!input && typeof input === 'object' && 'roles' in input);
45
45
  const next = restoreRedactions(assertModel(input), currentModel, new Set(redactModel(currentModel).paths));
46
46
  const proposal = renderProposal(current, next);
47
47
  const staged = stageProposal(this.path, current, proposal);
@@ -63,7 +63,7 @@ export class FleetConfigService {
63
63
  return withFileLock(`${splitRootFor(this.path)}.web-edit.lock`, async () => {
64
64
  const current = readSources(this.path);
65
65
  assertRevision(baseRevision, current);
66
- const currentModel = modelFrom(current);
66
+ const currentModel = modelFrom(current, !!input && typeof input === 'object' && 'roles' in input);
67
67
  const next = restoreRedactions(assertModel(input), currentModel, new Set(redactModel(currentModel).paths));
68
68
  const proposal = renderProposal(current, next);
69
69
  const changed = changedDocuments(current, proposal);
@@ -105,6 +105,8 @@ export class FleetConfigService {
105
105
  rmSync(target, { force: true });
106
106
  else {
107
107
  assertWritableTarget(target, current.documents.has(rel));
108
+ mkdirSync(dirname(target), { recursive: true, mode: 0o700 });
109
+ trustedDirectory(dirname(target));
108
110
  replaceFileAtomically(target, contents, 0o600);
109
111
  chmodSync(target, 0o600);
110
112
  }
@@ -184,7 +186,7 @@ function readSources(manifest) {
184
186
  }
185
187
  return { manifest, root, documents };
186
188
  }
187
- function modelFrom(sources) {
189
+ function modelFrom(sources, includeDefinitions = false) {
188
190
  const manifestKey = basename(sources.manifest);
189
191
  const manifest = parseFleetDocument(sources.manifest, sources.documents.get(manifestKey), 'strict').value;
190
192
  const agents = {};
@@ -199,15 +201,24 @@ function modelFrom(sources) {
199
201
  const id = basename(rel, extname(rel));
200
202
  agent_templates[id] = parseFleetDocument(absoluteFor(sources.manifest, rel), source, 'strict').value;
201
203
  }
202
- return { manifest, agents, agent_templates };
204
+ const model = { manifest, agents, agent_templates };
205
+ if (includeDefinitions)
206
+ for (const kind of ['roles', 'brains', 'room_templates']) {
207
+ model[kind] = {};
208
+ for (const [rel, source] of sources.documents)
209
+ if (rel.startsWith(kind + '/') && ['.yaml', '.yml'].includes(extname(rel))) {
210
+ model[kind][basename(rel, extname(rel))] = parseFleetDocument(absoluteFor(sources.manifest, rel), source, 'strict').value;
211
+ }
212
+ }
213
+ return model;
203
214
  }
204
215
  function assertModel(value) {
205
216
  if (!value || typeof value !== 'object' || Array.isArray(value))
206
217
  throw new FleetError('invalid_request', 'model must be an object');
207
218
  const input = value;
208
- if (Object.keys(input).sort().join(',') !== 'agent_templates,agents,manifest')
209
- throw new FleetError('invalid_request', 'model allows exactly manifest, agents, and agent_templates');
210
- for (const key of ['manifest', 'agents', 'agent_templates'])
219
+ if (!['agent_templates,agents,manifest', 'agent_templates,agents,brains,manifest,roles,room_templates'].includes(Object.keys(input).sort().join(',')))
220
+ throw new FleetError('invalid_request', 'model requires manifest, agents, agent_templates and optionally all definition collections');
221
+ for (const key of Object.keys(input))
211
222
  if (!input[key] || typeof input[key] !== 'object' || Array.isArray(input[key]))
212
223
  throw new FleetError('invalid_request', `${key} must be an object`);
213
224
  for (const [id, doc] of Object.entries(input.agents)) {
@@ -222,6 +233,14 @@ function assertModel(value) {
222
233
  if (!doc || typeof doc !== 'object' || Array.isArray(doc))
223
234
  throw new FleetError('invalid_request', `Agent Template '${id}' must be an object`);
224
235
  }
236
+ for (const kind of ['roles', 'brains', 'room_templates']) {
237
+ for (const [id, doc] of Object.entries((input[kind] ?? {}))) {
238
+ if (!ROLE_NAME_RE.test(id))
239
+ throw new FleetError('invalid_request', `invalid definition ID '${id}'`);
240
+ if (!doc || typeof doc !== 'object' || Array.isArray(doc))
241
+ throw new FleetError('invalid_request', `definition '${id}' must be an object`);
242
+ }
243
+ }
225
244
  return structuredClone(input);
226
245
  }
227
246
  function renderProposal(current, model) {
@@ -240,9 +259,21 @@ function renderProposal(current, model) {
240
259
  next.set(oldRel ?? join('agent_templates', `${id}.yaml`), oldRel
241
260
  ? render(current.documents.get(oldRel), doc) : stringify(doc));
242
261
  }
243
- for (const [rel, source] of current.documents)
244
- if (rel.startsWith('roles/') || rel.startsWith('brains/') || rel.startsWith('room_templates/'))
245
- next.set(rel, source);
262
+ for (const kind of ['roles', 'brains', 'room_templates']) {
263
+ if (model[kind] === undefined) {
264
+ for (const [rel, source] of current.documents)
265
+ if (rel.startsWith(kind + '/'))
266
+ next.set(rel, source);
267
+ continue;
268
+ }
269
+ for (const [rel, source] of current.documents)
270
+ if (rel.startsWith(kind + '/') && !['.yaml', '.yml'].includes(extname(rel)))
271
+ next.set(rel, source);
272
+ for (const [id, doc] of Object.entries(model[kind])) {
273
+ const oldRel = [...current.documents.keys()].find(rel => rel.startsWith(kind + '/') && ['.yaml', '.yml'].includes(extname(rel)) && basename(rel, extname(rel)) === id);
274
+ next.set(oldRel ?? join(kind, `${id}.yaml`), oldRel ? render(current.documents.get(oldRel), doc) : stringify(doc));
275
+ }
276
+ }
246
277
  return next;
247
278
  }
248
279
  function render(source, model) {
@@ -395,9 +426,9 @@ function sourceDiff(before, after, label, secrets) {
395
426
  ...left.slice(left.length - tail, left.length - tail + trailing).map(x => ` ${x}`), ''].join('\n');
396
427
  }
397
428
  function proposalDiff(current, proposal) {
398
- const secrets = sensitiveValues(modelFrom(current));
429
+ const secrets = sensitiveValues(modelFrom(current, true));
399
430
  try {
400
- secrets.push(...sensitiveValues(modelFrom({ ...current, documents: proposal })));
431
+ secrets.push(...sensitiveValues(modelFrom({ ...current, documents: proposal }, true)));
401
432
  }
402
433
  catch { /* validation reports later */ }
403
434
  return [...new Set([...current.documents.keys(), ...proposal.keys()])].sort().map(rel => sourceDiff(current.documents.get(rel) ?? '', proposal.get(rel) ?? '', rel, secrets)).filter(Boolean).join('\n');
@@ -405,7 +436,7 @@ function proposalDiff(current, proposal) {
405
436
  function splitLines(source) { return source.replace(/\n$/, '').split('\n'); }
406
437
  function restartImpact(before, after) {
407
438
  const roles = [...new Set([...Object.keys(before.agents), ...Object.keys(after.agents)])]
408
- .filter(id => JSON.stringify(before.agents[id]) !== JSON.stringify(after.agents[id])).sort();
439
+ .filter(id => JSON.stringify(before.agents[id]) !== JSON.stringify(after.agents[id]) || JSON.stringify(before.roles) !== JSON.stringify(after.roles) || JSON.stringify(before.brains) !== JSON.stringify(after.brains)).sort();
409
440
  const watchdogScheduler = JSON.stringify(before.manifest.watchdogs) !== JSON.stringify(after.manifest.watchdogs);
410
441
  const scheduledLoops = JSON.stringify(before.manifest.loops) !== JSON.stringify(after.manifest.loops);
411
442
  const required = roles.length > 0 || watchdogScheduler || scheduledLoops;
@@ -0,0 +1,32 @@
1
+ import type { AuditSink } from './audit.js';
2
+ import { type IncomingMessage } from 'node:http';
3
+ import type { WebAuth } from './auth.js';
4
+ /** Each entry exposes one dedicated backend listener. Endpoint dispatch stays upstream. */
5
+ export interface ServiceTarget {
6
+ prefix: string;
7
+ origin: string;
8
+ upstreamPrefix?: string;
9
+ /** Server-side service credentials; never taken from browser headers. */
10
+ headers?: Record<string, string>;
11
+ /** Dedicated credential-authenticated services reject direct browser context. */
12
+ stripBrowserContext?: boolean;
13
+ /**
14
+ * Service paths (below the prefix) where the account App may open a WebSocket with its device
15
+ * credential. A browser cannot set Authorization on a WebSocket, so only there it is accepted
16
+ * as a subprotocol.
17
+ */
18
+ deviceSocketPaths?: string[];
19
+ }
20
+ export interface GatewayOptions {
21
+ /** Backend mode is for a separate loopback machine-client listener; browsers are rejected. */
22
+ auth: WebAuth | 'backend';
23
+ fleetOrigin: string;
24
+ services: ServiceTarget[];
25
+ /** Bounded unauthorized-response diagnostics: fixed service/action enums only. */
26
+ audit?: Pick<AuditSink, 'record'>;
27
+ }
28
+ /** Raw transport deliberately runs outside Fastify's JSON parser and body limits. */
29
+ export declare function createPrefixGateway(options: GatewayOptions): {
30
+ server: import("http").Server<typeof IncomingMessage, typeof import("http").ServerResponse>;
31
+ close(): Promise<void>;
32
+ };