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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (423) hide show
  1. package/README.md +279 -68
  2. package/dist/account-origin.d.ts +2 -0
  3. package/dist/account-origin.js +6 -0
  4. package/dist/agent-ours/bridge.js +26 -9
  5. package/dist/agent-ours/correspondence.d.ts +10 -0
  6. package/dist/agent-ours/correspondence.js +1 -0
  7. package/dist/agent-ours/file-rpc.d.ts +12 -0
  8. package/dist/agent-ours/file-rpc.js +18 -4
  9. package/dist/agent-ours/harness.js +9 -1
  10. package/dist/agent-ours/mcp-endpoint.d.ts +19 -0
  11. package/dist/agent-ours/mcp-endpoint.js +98 -25
  12. package/dist/agent-ours/runtime.d.ts +9 -1
  13. package/dist/agent-ours/runtime.js +50 -2
  14. package/dist/agent-ours/service.d.ts +24 -1
  15. package/dist/agent-ours/service.js +97 -13
  16. package/dist/application/codex-models.d.ts +15 -0
  17. package/dist/application/codex-models.js +56 -0
  18. package/dist/application/conversation-history.d.ts +34 -0
  19. package/dist/application/conversation-history.js +280 -0
  20. package/dist/application/conversation-role-observer.d.ts +62 -0
  21. package/dist/application/conversation-role-observer.js +206 -0
  22. package/dist/application/fleet-query-service.d.ts +1 -1
  23. package/dist/application/fleet-query-service.js +3 -3
  24. package/dist/application/log-service.js +8 -8
  25. package/dist/application/model-catalog.d.ts +1 -1
  26. package/dist/application/model-catalog.js +5 -2
  27. package/dist/application/preset-provenance.d.ts +19 -0
  28. package/dist/application/preset-provenance.js +59 -0
  29. package/dist/application/resource-observer.d.ts +40 -0
  30. package/dist/application/resource-observer.js +278 -0
  31. package/dist/application/role-creation-service.js +3 -2
  32. package/dist/application/role-removal-service.js +17 -3
  33. package/dist/application/role-repository.d.ts +1 -1
  34. package/dist/application/role-repository.js +30 -7
  35. package/dist/application/room-layout-definitions.d.ts +45 -0
  36. package/dist/application/room-layout-definitions.js +130 -0
  37. package/dist/application/session-control.d.ts +61 -17
  38. package/dist/application/session-control.js +207 -23
  39. package/dist/application/supervisor-ours-tools.js +5 -11
  40. package/dist/application/task-layouts.d.ts +91 -0
  41. package/dist/application/task-layouts.js +261 -0
  42. package/dist/application/task-room-service.d.ts +101 -3
  43. package/dist/application/task-room-service.js +286 -33
  44. package/dist/application/types.d.ts +2 -0
  45. package/dist/atomic-file.d.ts +34 -1
  46. package/dist/atomic-file.js +161 -18
  47. package/dist/briefing.d.ts +3 -1
  48. package/dist/briefing.js +97 -31
  49. package/dist/build-info.json +9 -4
  50. package/dist/capabilities.d.ts +9 -1
  51. package/dist/capabilities.js +13 -0
  52. package/dist/cli.js +400 -37
  53. package/dist/client-profile.d.ts +5 -17
  54. package/dist/client-profile.js +4 -127
  55. package/dist/config.d.ts +45 -0
  56. package/dist/config.js +57 -6
  57. package/dist/creation.d.ts +1 -1
  58. package/dist/creation.js +4 -4
  59. package/dist/daemon-recovery.d.ts +14 -9
  60. package/dist/daemon-recovery.js +124 -115
  61. package/dist/docs.d.ts +1 -1
  62. package/dist/docs.js +207 -19
  63. package/dist/doctor.d.ts +4 -1
  64. package/dist/doctor.js +75 -60
  65. package/dist/erased-resources.d.ts +9 -0
  66. package/dist/erased-resources.js +87 -0
  67. package/dist/file-delivery/reader.d.ts +15 -0
  68. package/dist/file-delivery/reader.js +63 -0
  69. package/dist/file-delivery/store.d.ts +15 -0
  70. package/dist/file-delivery/store.js +124 -0
  71. package/dist/file-delivery/types.d.ts +19 -0
  72. package/dist/file-delivery/types.js +1 -0
  73. package/dist/fleet-command-audit.d.ts +7 -0
  74. package/dist/fleet-command-audit.js +24 -8
  75. package/dist/harness/agent-session.d.ts +4 -0
  76. package/dist/harness/claude-code-session.js +1 -1
  77. package/dist/harness/claude-code.js +21 -11
  78. package/dist/harness/claude-runtime.d.ts +12 -0
  79. package/dist/harness/claude-runtime.js +43 -0
  80. package/dist/harness/codex-app-server-proxy.d.ts +4 -1
  81. package/dist/harness/codex-app-server-proxy.js +41 -5
  82. package/dist/harness/codex-reasoning.d.ts +5 -0
  83. package/dist/harness/codex-reasoning.js +12 -0
  84. package/dist/harness/codex-runtime.d.ts +11 -1
  85. package/dist/harness/codex-runtime.js +31 -4
  86. package/dist/harness/codex-session.js +2 -2
  87. package/dist/harness/codex.js +24 -12
  88. package/dist/init-wizard.d.ts +2 -0
  89. package/dist/init-wizard.js +38 -19
  90. package/dist/isolation/policy.js +6 -1
  91. package/dist/isolation/types.d.ts +5 -0
  92. package/dist/managed-cli.d.ts +253 -0
  93. package/dist/managed-cli.js +923 -0
  94. package/dist/managed-recovery.d.ts +13 -0
  95. package/dist/managed-recovery.js +31 -0
  96. package/dist/minimal-setup.d.ts +2 -0
  97. package/dist/minimal-setup.js +44 -0
  98. package/dist/monitor.d.ts +9 -34
  99. package/dist/monitor.js +90 -153
  100. package/dist/notifications/fleet-producer.d.ts +22 -0
  101. package/dist/notifications/fleet-producer.js +237 -0
  102. package/dist/notifications/outbox.d.ts +46 -0
  103. package/dist/notifications/outbox.js +158 -0
  104. package/dist/notifications/target-cleanup.d.ts +11 -0
  105. package/dist/notifications/target-cleanup.js +63 -0
  106. package/dist/notifications/task-producer.d.ts +20 -0
  107. package/dist/notifications/task-producer.js +100 -0
  108. package/dist/ops.d.ts +2 -0
  109. package/dist/ops.js +20 -3
  110. package/dist/owner-channel/channel.js +17 -6
  111. package/dist/owner-channel/commands.d.ts +4 -0
  112. package/dist/owner-channel/commands.js +8 -0
  113. package/dist/owner-channel/lifecycle-outbox.js +4 -3
  114. package/dist/owner-channel/ours-client.js +2 -17
  115. package/dist/paths.d.ts +2 -0
  116. package/dist/paths.js +2 -0
  117. package/dist/preset-migration.js +43 -0
  118. package/dist/process-incarnation.d.ts +23 -0
  119. package/dist/process-incarnation.js +42 -0
  120. package/dist/reminders/manager.d.ts +75 -0
  121. package/dist/reminders/manager.js +278 -0
  122. package/dist/reminders/runtime.d.ts +27 -0
  123. package/dist/reminders/runtime.js +32 -0
  124. package/dist/reminders/schedule.d.ts +37 -0
  125. package/dist/reminders/schedule.js +104 -0
  126. package/dist/reminders/store.d.ts +341 -0
  127. package/dist/reminders/store.js +762 -0
  128. package/dist/reminders/tools.d.ts +20 -0
  129. package/dist/reminders/tools.js +114 -0
  130. package/dist/resolved-plan.js +1 -0
  131. package/dist/rooms-tasks/add-member.d.ts +48 -0
  132. package/dist/rooms-tasks/add-member.js +84 -0
  133. package/dist/rooms-tasks/archived-absence.d.ts +8 -0
  134. package/dist/rooms-tasks/archived-absence.js +48 -0
  135. package/dist/rooms-tasks/cli.js +65 -5
  136. package/dist/rooms-tasks/close.d.ts +6 -2
  137. package/dist/rooms-tasks/close.js +246 -37
  138. package/dist/rooms-tasks/config.d.ts +1 -1
  139. package/dist/rooms-tasks/config.js +11 -4
  140. package/dist/rooms-tasks/cowork-adapter.d.ts +12 -5
  141. package/dist/rooms-tasks/cowork-adapter.js +15 -121
  142. package/dist/rooms-tasks/cowork-http.js +56 -6
  143. package/dist/rooms-tasks/deletion.d.ts +1 -1
  144. package/dist/rooms-tasks/deletion.js +84 -5
  145. package/dist/rooms-tasks/erasure.d.ts +6 -0
  146. package/dist/rooms-tasks/erasure.js +173 -0
  147. package/dist/rooms-tasks/external-worker.d.ts +1 -1
  148. package/dist/rooms-tasks/external-worker.js +3 -4
  149. package/dist/rooms-tasks/layout-binding-client.d.ts +11 -0
  150. package/dist/rooms-tasks/layout-binding-client.js +56 -0
  151. package/dist/rooms-tasks/layout-binding-grants.d.ts +21 -0
  152. package/dist/rooms-tasks/layout-binding-grants.js +128 -0
  153. package/dist/rooms-tasks/layout-cli.d.ts +4 -0
  154. package/dist/rooms-tasks/layout-cli.js +107 -0
  155. package/dist/rooms-tasks/layout-config.d.ts +11 -0
  156. package/dist/rooms-tasks/layout-config.js +89 -0
  157. package/dist/rooms-tasks/layout-control.d.ts +21 -0
  158. package/dist/rooms-tasks/layout-control.js +52 -0
  159. package/dist/rooms-tasks/layout-member-retirement.d.ts +10 -0
  160. package/dist/rooms-tasks/layout-member-retirement.js +90 -0
  161. package/dist/rooms-tasks/layout-service.d.ts +43 -0
  162. package/dist/rooms-tasks/layout-service.js +203 -0
  163. package/dist/rooms-tasks/layout.d.ts +121 -0
  164. package/dist/rooms-tasks/layout.js +286 -0
  165. package/dist/rooms-tasks/legacy-task-member.d.ts +19 -0
  166. package/dist/rooms-tasks/legacy-task-member.js +116 -0
  167. package/dist/rooms-tasks/live-readiness.d.ts +18 -0
  168. package/dist/rooms-tasks/live-readiness.js +85 -0
  169. package/dist/rooms-tasks/member-ownership.d.ts +5 -0
  170. package/dist/rooms-tasks/member-ownership.js +40 -0
  171. package/dist/rooms-tasks/member-startup.d.ts +1 -0
  172. package/dist/rooms-tasks/member-startup.js +2 -0
  173. package/dist/rooms-tasks/provision.d.ts +3 -0
  174. package/dist/rooms-tasks/provision.js +101 -49
  175. package/dist/rooms-tasks/recovery.d.ts +19 -0
  176. package/dist/rooms-tasks/recovery.js +110 -0
  177. package/dist/rooms-tasks/room-state.d.ts +4 -2
  178. package/dist/rooms-tasks/room-state.js +47 -8
  179. package/dist/rooms-tasks/task-service-retirement.d.ts +11 -0
  180. package/dist/rooms-tasks/task-service-retirement.js +21 -0
  181. package/dist/rooms-tasks/task-state.d.ts +52 -8
  182. package/dist/rooms-tasks/task-state.js +251 -26
  183. package/dist/rooms-tasks/types.d.ts +40 -0
  184. package/dist/rooms-tasks/workspace-artifacts.d.ts +6 -0
  185. package/dist/rooms-tasks/workspace-artifacts.js +93 -0
  186. package/dist/rooms-tasks/workspace.d.ts +35 -0
  187. package/dist/rooms-tasks/workspace.js +325 -0
  188. package/dist/runner.d.ts +33 -4
  189. package/dist/runner.js +410 -62
  190. package/dist/session/acp.d.ts +75 -2
  191. package/dist/session/acp.js +345 -82
  192. package/dist/session/arbiter.d.ts +98 -0
  193. package/dist/session/arbiter.js +272 -3
  194. package/dist/session/codex-app-server.d.ts +45 -1
  195. package/dist/session/codex-app-server.js +225 -32
  196. package/dist/session/control.d.ts +17 -3
  197. package/dist/session/control.js +213 -23
  198. package/dist/session/conversation-normalizer.d.ts +2 -1
  199. package/dist/session/conversation-normalizer.js +1 -1
  200. package/dist/session/conversation-observation.d.ts +25 -0
  201. package/dist/session/conversation-observation.js +100 -0
  202. package/dist/session/conversation-replay.d.ts +50 -0
  203. package/dist/session/conversation-replay.js +203 -0
  204. package/dist/session/conversation-store.d.ts +15 -1
  205. package/dist/session/conversation-store.js +206 -26
  206. package/dist/session/conversation-types.d.ts +57 -4
  207. package/dist/session/prompt-batch.d.ts +79 -0
  208. package/dist/session/prompt-batch.js +120 -0
  209. package/dist/session/task-notice.d.ts +14 -0
  210. package/dist/session/task-notice.js +13 -0
  211. package/dist/session/types.d.ts +42 -1
  212. package/dist/setup-tunnel-preflight.d.ts +34 -0
  213. package/dist/setup-tunnel-preflight.js +62 -0
  214. package/dist/socket-path.d.ts +2 -0
  215. package/dist/socket-path.js +22 -0
  216. package/dist/spawn.d.ts +5 -1
  217. package/dist/spawn.js +40 -4
  218. package/dist/startup-prompt.d.ts +15 -0
  219. package/dist/startup-prompt.js +71 -0
  220. package/dist/startup-readiness.d.ts +32 -0
  221. package/dist/startup-readiness.js +101 -0
  222. package/dist/startup-telemetry.d.ts +16 -0
  223. package/dist/startup-telemetry.js +46 -0
  224. package/dist/subscriptions/cli.d.ts +91 -0
  225. package/dist/subscriptions/cli.js +223 -0
  226. package/dist/subscriptions/launch.d.ts +37 -0
  227. package/dist/subscriptions/launch.js +93 -0
  228. package/dist/subscriptions/login.d.ts +77 -0
  229. package/dist/subscriptions/login.js +215 -0
  230. package/dist/subscriptions/observed.d.ts +7 -0
  231. package/dist/subscriptions/observed.js +44 -0
  232. package/dist/subscriptions/probe.d.ts +10 -0
  233. package/dist/subscriptions/probe.js +73 -0
  234. package/dist/subscriptions/service.d.ts +102 -0
  235. package/dist/subscriptions/service.js +364 -0
  236. package/dist/subscriptions/store.d.ts +71 -0
  237. package/dist/subscriptions/store.js +143 -0
  238. package/dist/supervisor/adoption.d.ts +8 -0
  239. package/dist/supervisor/adoption.js +78 -0
  240. package/dist/supervisor/catalog.d.ts +62 -0
  241. package/dist/supervisor/catalog.js +254 -0
  242. package/dist/supervisor/fleet.d.ts +8 -0
  243. package/dist/supervisor/fleet.js +89 -0
  244. package/dist/supervisor/index.js +3 -6
  245. package/dist/supervisor/launchd.d.ts +1 -1
  246. package/dist/supervisor/launchd.js +26 -13
  247. package/dist/supervisor/legacy.d.ts +35 -0
  248. package/dist/supervisor/legacy.js +524 -0
  249. package/dist/supervisor/manager.d.ts +15 -0
  250. package/dist/supervisor/manager.js +270 -0
  251. package/dist/supervisor/registration-refresh.d.ts +45 -0
  252. package/dist/supervisor/registration-refresh.js +266 -0
  253. package/dist/supervisor/scope.d.ts +4 -0
  254. package/dist/supervisor/scope.js +10 -0
  255. package/dist/supervisor/systemd.d.ts +2 -2
  256. package/dist/supervisor/systemd.js +19 -33
  257. package/dist/task-supervision.d.ts +9 -0
  258. package/dist/task-supervision.js +145 -0
  259. package/dist/temp-idle.d.ts +17 -0
  260. package/dist/temp-idle.js +42 -0
  261. package/dist/temp-lifecycle.d.ts +23 -8
  262. package/dist/temp-lifecycle.js +123 -66
  263. package/dist/temp-supervisor-recovery.d.ts +1 -1
  264. package/dist/temp-supervisor-recovery.js +2 -2
  265. package/dist/tunnel-setup-completion.d.ts +7 -0
  266. package/dist/tunnel-setup-completion.js +13 -0
  267. package/dist/tunnel-setup-input.d.ts +19 -0
  268. package/dist/tunnel-setup-input.js +124 -0
  269. package/dist/watchdog/query.d.ts +3 -1
  270. package/dist/watchdog/query.js +2 -1
  271. package/dist/web/agent-attachments.d.ts +29 -0
  272. package/dist/web/agent-attachments.js +162 -0
  273. package/dist/web/auth.d.ts +65 -2
  274. package/dist/web/auth.js +201 -11
  275. package/dist/web/available-port.d.ts +2 -0
  276. package/dist/web/available-port.js +23 -0
  277. package/dist/web/control.d.ts +3 -3
  278. package/dist/web/control.js +10 -8
  279. package/dist/web/conversation-stream-codec.d.ts +23 -0
  280. package/dist/web/conversation-stream-codec.js +110 -0
  281. package/dist/web/conversation-stream.d.ts +25 -0
  282. package/dist/web/conversation-stream.js +231 -0
  283. package/dist/web/events.d.ts +2 -0
  284. package/dist/web/events.js +9 -0
  285. package/dist/web/fleet-config-service.d.ts +4 -1
  286. package/dist/web/fleet-config-service.js +46 -15
  287. package/dist/web/prefix-gateway.d.ts +32 -0
  288. package/dist/web/prefix-gateway.js +248 -0
  289. package/dist/web/resource-views.d.ts +124 -0
  290. package/dist/web/resource-views.js +424 -0
  291. package/dist/web/runtime.d.ts +3 -1
  292. package/dist/web/runtime.js +132 -34
  293. package/dist/web/server.d.ts +19 -0
  294. package/dist/web/server.js +802 -67
  295. package/dist/web/service.d.ts +11 -2
  296. package/dist/web/service.js +30 -4
  297. package/dist/web/workspace-devices.d.ts +107 -0
  298. package/dist/web/workspace-devices.js +277 -0
  299. package/dist/web/workspace-tunnel.d.ts +1 -0
  300. package/dist/web/workspace-tunnel.js +34 -0
  301. package/dist/workspace-enrollment.d.ts +83 -0
  302. package/dist/workspace-enrollment.js +580 -0
  303. package/dist/workspace-replacement.d.ts +66 -0
  304. package/dist/workspace-replacement.js +220 -0
  305. package/docs/acp-files.md +146 -0
  306. package/docs/conversation-stream.md +196 -0
  307. package/docs/fleet-migration.md +54 -0
  308. package/docs/live-voice.md +13 -0
  309. package/docs/room-layouts.md +227 -0
  310. package/docs/self-reminders.md +374 -0
  311. package/docs/task-workspaces.md +225 -0
  312. package/docs/validation/managed-cli-permissions.md +421 -0
  313. package/examples/room-layouts/fresh.yaml +27 -0
  314. package/examples/room-layouts/scoped.yaml +21 -0
  315. package/examples/room-layouts/shared.yaml +19 -0
  316. package/package.json +24 -20
  317. package/presets/README.md +78 -0
  318. package/presets/brain-catalog.json +8 -2
  319. package/presets/fleet/agent_templates/Critic.yaml +1 -1
  320. package/presets/fleet/agent_templates/Developer.yaml +1 -1
  321. package/presets/fleet/agent_templates/Engineer.yaml +5 -0
  322. package/presets/fleet/agent_templates/LocalCoordinator.yaml +4 -2
  323. package/presets/fleet/agents/FleetCoordinator.yaml +1 -1
  324. package/presets/fleet/brains/claude-fable-5-1-high.yaml +5 -0
  325. package/presets/fleet/brains/claude-fable-5-1-low.yaml +5 -0
  326. package/presets/fleet/brains/claude-fable-5-1-max.yaml +5 -0
  327. package/presets/fleet/brains/claude-fable-5-1-medium.yaml +5 -0
  328. package/presets/fleet/brains/claude-fable-5-1-xhigh.yaml +5 -0
  329. package/presets/fleet/brains/claude-fable-5-high.yaml +1 -1
  330. package/presets/fleet/brains/claude-fable-5-low.yaml +1 -1
  331. package/presets/fleet/brains/claude-fable-5-max.yaml +1 -1
  332. package/presets/fleet/brains/claude-fable-5-medium.yaml +1 -1
  333. package/presets/fleet/brains/claude-fable-5-xhigh.yaml +1 -1
  334. package/presets/fleet/brains/claude-haiku-4-5-20251001-default.yaml +4 -0
  335. package/presets/fleet/brains/claude-opus-5-5-high.yaml +5 -0
  336. package/presets/fleet/brains/claude-opus-5-5-low.yaml +5 -0
  337. package/presets/fleet/brains/claude-opus-5-5-max.yaml +5 -0
  338. package/presets/fleet/brains/claude-opus-5-5-medium.yaml +5 -0
  339. package/presets/fleet/brains/claude-opus-5-5-xhigh.yaml +5 -0
  340. package/presets/fleet/brains/claude-opus-5-high.yaml +1 -1
  341. package/presets/fleet/brains/claude-opus-5-low.yaml +1 -1
  342. package/presets/fleet/brains/claude-opus-5-max.yaml +1 -1
  343. package/presets/fleet/brains/claude-opus-5-medium.yaml +1 -1
  344. package/presets/fleet/brains/claude-opus-5-xhigh.yaml +1 -1
  345. package/presets/fleet/brains/claude-sonnet-5-high.yaml +1 -1
  346. package/presets/fleet/brains/claude-sonnet-5-low.yaml +1 -1
  347. package/presets/fleet/brains/claude-sonnet-5-max.yaml +1 -1
  348. package/presets/fleet/brains/claude-sonnet-5-medium.yaml +1 -1
  349. package/presets/fleet/brains/claude-sonnet-5-xhigh.yaml +1 -1
  350. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-high.yaml +1 -1
  351. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-low.yaml +1 -1
  352. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-medium.yaml +1 -1
  353. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-xhigh.yaml +1 -1
  354. package/presets/fleet/brains/codex-gpt-5-4-high.yaml +1 -1
  355. package/presets/fleet/brains/codex-gpt-5-4-low.yaml +1 -1
  356. package/presets/fleet/brains/codex-gpt-5-4-medium.yaml +1 -1
  357. package/presets/fleet/brains/codex-gpt-5-4-mini-high.yaml +1 -1
  358. package/presets/fleet/brains/codex-gpt-5-4-mini-low.yaml +1 -1
  359. package/presets/fleet/brains/codex-gpt-5-4-mini-medium.yaml +1 -1
  360. package/presets/fleet/brains/codex-gpt-5-4-mini-xhigh.yaml +1 -1
  361. package/presets/fleet/brains/codex-gpt-5-4-xhigh.yaml +1 -1
  362. package/presets/fleet/brains/codex-gpt-5-5-high.yaml +1 -1
  363. package/presets/fleet/brains/codex-gpt-5-5-low.yaml +1 -1
  364. package/presets/fleet/brains/codex-gpt-5-5-medium.yaml +1 -1
  365. package/presets/fleet/brains/codex-gpt-5-5-xhigh.yaml +1 -1
  366. package/presets/fleet/brains/codex-gpt-5-6-luna-high.yaml +1 -1
  367. package/presets/fleet/brains/codex-gpt-5-6-luna-low.yaml +1 -1
  368. package/presets/fleet/brains/codex-gpt-5-6-luna-max.yaml +1 -1
  369. package/presets/fleet/brains/codex-gpt-5-6-luna-medium.yaml +1 -1
  370. package/presets/fleet/brains/codex-gpt-5-6-luna-xhigh.yaml +1 -1
  371. package/presets/fleet/brains/codex-gpt-5-6-sol-high.yaml +1 -1
  372. package/presets/fleet/brains/codex-gpt-5-6-sol-low.yaml +1 -1
  373. package/presets/fleet/brains/codex-gpt-5-6-sol-max.yaml +1 -1
  374. package/presets/fleet/brains/codex-gpt-5-6-sol-medium.yaml +1 -1
  375. package/presets/fleet/brains/codex-gpt-5-6-sol-ultra.yaml +1 -1
  376. package/presets/fleet/brains/codex-gpt-5-6-sol-xhigh.yaml +1 -1
  377. package/presets/fleet/brains/codex-gpt-5-6-terra-high.yaml +1 -1
  378. package/presets/fleet/brains/codex-gpt-5-6-terra-low.yaml +1 -1
  379. package/presets/fleet/brains/codex-gpt-5-6-terra-max.yaml +1 -1
  380. package/presets/fleet/brains/codex-gpt-5-6-terra-medium.yaml +1 -1
  381. package/presets/fleet/brains/codex-gpt-5-6-terra-ultra.yaml +1 -1
  382. package/presets/fleet/brains/codex-gpt-5-6-terra-xhigh.yaml +1 -1
  383. package/presets/fleet/brains/codex-gpt-6-1-sol-high.yaml +5 -0
  384. package/presets/fleet/brains/codex-gpt-6-1-sol-low.yaml +5 -0
  385. package/presets/fleet/brains/codex-gpt-6-1-sol-max.yaml +5 -0
  386. package/presets/fleet/brains/codex-gpt-6-1-sol-medium.yaml +5 -0
  387. package/presets/fleet/brains/codex-gpt-6-1-sol-ultra.yaml +5 -0
  388. package/presets/fleet/brains/codex-gpt-6-1-sol-xhigh.yaml +5 -0
  389. package/presets/fleet/brains/codex-gpt-6-astra-high.yaml +1 -1
  390. package/presets/fleet/brains/codex-gpt-6-astra-low.yaml +1 -1
  391. package/presets/fleet/brains/codex-gpt-6-astra-max.yaml +1 -1
  392. package/presets/fleet/brains/codex-gpt-6-astra-medium.yaml +1 -1
  393. package/presets/fleet/brains/codex-gpt-6-astra-ultra.yaml +1 -1
  394. package/presets/fleet/brains/codex-gpt-6-astra-xhigh.yaml +1 -1
  395. package/presets/fleet/brains/codex-gpt-6-luna-high.yaml +5 -0
  396. package/presets/fleet/brains/codex-gpt-6-luna-low.yaml +5 -0
  397. package/presets/fleet/brains/codex-gpt-6-luna-max.yaml +5 -0
  398. package/presets/fleet/brains/codex-gpt-6-luna-medium.yaml +5 -0
  399. package/presets/fleet/brains/codex-gpt-6-luna-xhigh.yaml +5 -0
  400. package/presets/fleet/brains/codex-gpt-6-sol-high.yaml +5 -0
  401. package/presets/fleet/brains/codex-gpt-6-sol-low.yaml +5 -0
  402. package/presets/fleet/brains/codex-gpt-6-sol-max.yaml +5 -0
  403. package/presets/fleet/brains/codex-gpt-6-sol-medium.yaml +5 -0
  404. package/presets/fleet/brains/codex-gpt-6-sol-ultra.yaml +5 -0
  405. package/presets/fleet/brains/codex-gpt-6-sol-xhigh.yaml +5 -0
  406. package/presets/fleet/roles/Coordinator.yaml +12 -11
  407. package/presets/fleet/roles/Critic.yaml +13 -0
  408. package/presets/fleet/roles/Developer.yaml +13 -0
  409. package/presets/fleet/roles/Engineer.yaml +53 -0
  410. package/presets/fleet/room_layouts/engineering.yaml +25 -0
  411. package/presets/fleet/room_layouts/pair.yaml +24 -0
  412. package/presets/fleet/room_layouts/single.yaml +17 -0
  413. package/presets/fleet/room_layouts/team.yaml +30 -0
  414. package/presets/fleet/room_templates/engineering.yaml +19 -0
  415. package/presets/manifest.json +2 -2
  416. package/dist/web-app/assets/index-59GF-gsZ.css +0 -1
  417. package/dist/web-app/assets/index-C0h3ALvs.js +0 -10
  418. package/dist/web-app/icons/ours-fleet-maskable.svg +0 -4
  419. package/dist/web-app/icons/ours-fleet.svg +0 -4
  420. package/dist/web-app/index.html +0 -17
  421. package/dist/web-app/manifest.webmanifest +0 -15
  422. package/dist/web-app/offline.html +0 -18
  423. package/dist/web-app/sw.js +0 -51
@@ -0,0 +1,45 @@
1
+ import { loadConfig } from '../config.js';
2
+ import { type RoomLayoutDefinition } from '../rooms-tasks/layout-config.js';
3
+ export declare const ABSENT_REVISION = "absent";
4
+ export type LayoutDefinitionBody = Omit<RoomLayoutDefinition, 'sourceFile'>;
5
+ export type LayoutListEntry = {
6
+ name: string;
7
+ revision: string;
8
+ file: string;
9
+ definition: LayoutDefinitionBody;
10
+ issues: string[];
11
+ definition_hash: string;
12
+ } | {
13
+ name: string;
14
+ revision: string;
15
+ file: string;
16
+ error: string;
17
+ };
18
+ export declare class RoomLayoutDefinitions {
19
+ private configPath?;
20
+ private deps;
21
+ constructor(configPath?: string | undefined, deps?: {
22
+ loadConfiguration?: typeof loadConfig;
23
+ });
24
+ private root;
25
+ private pathFor;
26
+ private currentRevision;
27
+ /** Agent-template references that do not resolve in the current Fleet configuration. */
28
+ private templateIssues;
29
+ /** The configured default (`rooms.defaults.template`, else `tasks.default_room_template`) when it names a usable layout. */
30
+ defaultLayout(entries?: LayoutListEntry[]): string | undefined;
31
+ list(): LayoutListEntry[];
32
+ validate(raw: unknown): {
33
+ definition?: LayoutDefinitionBody;
34
+ issues: string[];
35
+ };
36
+ save(name: string, expectedRevision: string, raw: unknown): Promise<{
37
+ name: string;
38
+ revision: string;
39
+ definition: LayoutDefinitionBody;
40
+ }>;
41
+ remove(name: string, expectedRevision: string): Promise<{
42
+ name: string;
43
+ deleted: boolean;
44
+ }>;
45
+ }
@@ -0,0 +1,130 @@
1
+ /** Web authoring of `fleet/room_layouts/<name>.yaml`.
2
+ * Writes are revision-guarded (sha256 of the file bytes) and validated with the
3
+ * same parser the CLI uses. Retained layout runs keep their own snapshots, so
4
+ * editing or deleting a definition never changes an existing run.
5
+ */
6
+ import { createHash } from 'node:crypto';
7
+ import { existsSync, mkdirSync, readFileSync, readdirSync, unlinkSync } from 'node:fs';
8
+ import { basename, extname, join } from 'node:path';
9
+ import { stringify } from 'yaml';
10
+ import { replaceFileAtomically, withFileLock } from '../atomic-file.js';
11
+ import { parseFleetDocument } from '../config-yaml.js';
12
+ import { defaultConfigPath } from '../paths.js';
13
+ import { loadConfig, splitRootFor } from '../config.js';
14
+ import { assertLayoutFile, validLayoutKey, validateRoomLayout } from '../rooms-tasks/layout-config.js';
15
+ import { FleetError } from './errors.js';
16
+ import { layoutDefinitionHash } from './task-layouts.js';
17
+ export const ABSENT_REVISION = 'absent';
18
+ const revisionOf = (bytes) => createHash('sha256').update(bytes).digest('hex');
19
+ export class RoomLayoutDefinitions {
20
+ configPath;
21
+ deps;
22
+ constructor(configPath, deps = {}) {
23
+ this.configPath = configPath;
24
+ this.deps = deps;
25
+ }
26
+ root() { return join(splitRootFor(this.configPath ?? defaultConfigPath()), 'room_layouts'); }
27
+ pathFor(name) {
28
+ if (!validLayoutKey(name))
29
+ throw new FleetError('invalid_request', 'layout name must start with a letter and use letters, digits, - or _');
30
+ return join(this.root(), `${name}.yaml`);
31
+ }
32
+ currentRevision(path) {
33
+ if (!existsSync(path))
34
+ return ABSENT_REVISION;
35
+ assertLayoutFile(path);
36
+ return revisionOf(readFileSync(path));
37
+ }
38
+ /** Agent-template references that do not resolve in the current Fleet configuration. */
39
+ templateIssues(definition) {
40
+ let templates = {};
41
+ try {
42
+ templates = (this.deps.loadConfiguration ?? loadConfig)(this.configPath).agentTemplates ?? {};
43
+ }
44
+ catch (error) {
45
+ return [`fleet configuration unavailable: ${error.message}`];
46
+ }
47
+ return Object.entries(definition.participants).flatMap(([key, participant]) => participant.agent_template && !Object.hasOwn(templates, participant.agent_template)
48
+ ? [`participant ${key}: agent template not found: ${participant.agent_template}`] : []);
49
+ }
50
+ /** The configured default (`rooms.defaults.template`, else `tasks.default_room_template`) when it names a usable layout. */
51
+ defaultLayout(entries = this.list()) {
52
+ let name;
53
+ try {
54
+ const config = (this.deps.loadConfiguration ?? loadConfig)(this.configPath);
55
+ name = config.rooms?.defaults?.template ?? config.tasks?.default_room_template;
56
+ }
57
+ catch {
58
+ return undefined;
59
+ }
60
+ const entry = entries.find(e => e.name === name);
61
+ return entry && !('error' in entry) && !entry.issues.length ? entry.name : undefined;
62
+ }
63
+ list() {
64
+ const root = this.root();
65
+ if (!existsSync(root))
66
+ return [];
67
+ assertLayoutFile(root, true);
68
+ return readdirSync(root).filter(file => /\.ya?ml$/.test(file)).sort().map(file => {
69
+ const name = basename(file, extname(file)), path = join(root, file);
70
+ try {
71
+ if (!validLayoutKey(name))
72
+ throw Error(`invalid room layout id: ${name}`);
73
+ assertLayoutFile(path);
74
+ const bytes = readFileSync(path);
75
+ const definition = validateRoomLayout(parseFleetDocument(path, bytes.toString('utf8'), 'strict').value, file);
76
+ return { name, file, revision: revisionOf(bytes), definition, issues: this.templateIssues(definition), definition_hash: layoutDefinitionHash(definition) };
77
+ }
78
+ catch (error) {
79
+ // Only a trusted regular file may be read to offer an overwrite revision.
80
+ let revision = ABSENT_REVISION;
81
+ try {
82
+ assertLayoutFile(path);
83
+ revision = revisionOf(readFileSync(path));
84
+ }
85
+ catch { /* not trusted: never read */ }
86
+ return { name, file, revision, error: error.message };
87
+ }
88
+ });
89
+ }
90
+ validate(raw) {
91
+ let definition;
92
+ try {
93
+ definition = validateRoomLayout(raw, 'room layout');
94
+ }
95
+ catch (error) {
96
+ return { issues: [error.message] };
97
+ }
98
+ return { definition, issues: this.templateIssues(definition) };
99
+ }
100
+ async save(name, expectedRevision, raw) {
101
+ const path = this.pathFor(name);
102
+ const { definition, issues } = this.validate(raw);
103
+ if (!definition || issues.length)
104
+ throw new FleetError('invalid_request', issues.join('; '));
105
+ const root = this.root();
106
+ mkdirSync(root, { recursive: true, mode: 0o700 });
107
+ assertLayoutFile(root, true);
108
+ return withFileLock(`${path}.lock`, async () => {
109
+ const current = this.currentRevision(path);
110
+ if (current !== expectedRevision)
111
+ throw new FleetError('stale_state', 'room layout changed since it was loaded; reload before saving');
112
+ const { description, participants, rooms } = definition;
113
+ const text = stringify({ version: 1, ...(description ? { description } : {}), participants, rooms });
114
+ replaceFileAtomically(path, text, 0o600);
115
+ return { name, revision: revisionOf(text), definition };
116
+ });
117
+ }
118
+ async remove(name, expectedRevision) {
119
+ const path = this.pathFor(name);
120
+ return withFileLock(`${path}.lock`, async () => {
121
+ const current = this.currentRevision(path);
122
+ if (current === ABSENT_REVISION)
123
+ return { name, deleted: false };
124
+ if (current !== expectedRevision)
125
+ throw new FleetError('stale_state', 'room layout changed since it was loaded; reload before deleting');
126
+ unlinkSync(path);
127
+ return { name, deleted: true };
128
+ });
129
+ }
130
+ }
@@ -1,6 +1,10 @@
1
- import { controlRequest } from '../session/control.js';
2
- import type { ConversationEventV1, ConversationSnapshot, PromptReceipt } from '../session/conversation-types.js';
1
+ import type { ConversationRoleObservation, ConversationRoleState, ObserveConversationRole } from './conversation-role-observer.js';
2
+ import { type ConversationTailPage } from './conversation-history.js';
3
+ import type { AgentHistoryQuery } from '../agent-ours/correspondence.js';
4
+ import { controlRequest, followConversation } from '../session/control.js';
5
+ import type { ConversationEventV1, ConversationSnapshot, ConversationObservationState, PromptReceipt } from '../session/conversation-types.js';
3
6
  import type { SessionSnapshot } from '../session/types.js';
7
+ import type { PassiveConversationFollowRequest, ConversationReplayPage, ConversationReplayReady } from '../session/conversation-replay.js';
4
8
  import type { OutputPage, SendReceipt, SessionDescriptor } from './types.js';
5
9
  export interface ConversationPageView {
6
10
  events: ConversationEventV1[];
@@ -12,6 +16,23 @@ export interface ConversationPageView {
12
16
  export interface ConversationFollowHandle {
13
17
  close(): void;
14
18
  }
19
+ export interface LegacyConversationFollowRequest {
20
+ after?: string;
21
+ onPage(page: ConversationPageView): void;
22
+ onEvent(event: ConversationEventV1): void;
23
+ onClose(reason?: string): void;
24
+ }
25
+ export interface PassiveConversationFollow extends PassiveConversationFollowRequest {
26
+ /** Reports role updates from the owned or borrowed observer; never attaches a controller. */
27
+ onRole?(state: ConversationRoleState): void;
28
+ /** Application-only borrowed owner; never sent over the private wire. */
29
+ roleObservation?: ConversationRoleObservation;
30
+ onReplay(page: ConversationReplayPage): void;
31
+ onReady(ready: ConversationReplayReady): void;
32
+ onEvent(event: ConversationEventV1): void;
33
+ onState(state: ConversationObservationState): void;
34
+ onClose(reason?: string): void;
35
+ }
15
36
  /**
16
37
  * An interrupt that reached the session. `state` says whether the turn stopped
17
38
  * cooperatively or through bounded forced recovery — both are accepted, so no
@@ -23,7 +44,11 @@ export interface InterruptReceipt {
23
44
  reasonCode?: string;
24
45
  }
25
46
  export interface RoleSessionControl {
26
- describe(): Promise<SessionDescriptor>;
47
+ /** Standalone owner for role-only observation; dispose with that connection. */
48
+ observeRole?: ObserveConversationRole;
49
+ agentContacts?(): Promise<unknown>;
50
+ agentHistory?(request: AgentHistoryQuery): Promise<unknown>;
51
+ describe(roleObservation?: ConversationRoleObservation): Promise<SessionDescriptor>;
27
52
  snapshot(): Promise<SessionSnapshot>;
28
53
  recentOutput(request?: {
29
54
  since?: number;
@@ -41,12 +66,24 @@ export interface RoleSessionControl {
41
66
  after?: string;
42
67
  limit?: number;
43
68
  }): Promise<ConversationPageView>;
69
+ /** The newest visible events (or those before `before`), read backwards from the ledger. */
70
+ conversationTail?(request: {
71
+ before?: string;
72
+ limit?: number;
73
+ }): Promise<ConversationTailPage>;
44
74
  submitPromptV2?(request: {
45
75
  commandId: string;
46
76
  text: string;
47
77
  actorBrowserSession: string;
78
+ expectedSessionGeneration?: string;
79
+ requireIdle?: boolean;
48
80
  source: 'owner_admin_console';
49
81
  }): Promise<PromptReceipt>;
82
+ interruptPromptV2?(request: {
83
+ commandId: string;
84
+ expectedSessionGeneration: string;
85
+ promptId: string;
86
+ }): Promise<InterruptReceipt>;
50
87
  interruptV2?(commandId: string): Promise<InterruptReceipt & {
51
88
  commandId: string;
52
89
  }>;
@@ -64,18 +101,19 @@ export interface RoleSessionControl {
64
101
  * replay page; `onEvent` every subsequent durable event; `onClose` fires
65
102
  * exactly once when the underlying control stream ends.
66
103
  */
67
- followConversation?(request: {
68
- after?: string;
69
- onPage(page: ConversationPageView): void;
70
- onEvent(event: ConversationEventV1): void;
71
- onClose(reason?: string): void;
72
- }): Promise<ConversationFollowHandle>;
104
+ followConversation?(request: LegacyConversationFollowRequest | PassiveConversationFollow): Promise<ConversationFollowHandle>;
73
105
  }
74
106
  export declare class RoleSessionControlAdapter implements RoleSessionControl {
75
107
  private readonly stateDir;
76
108
  private readonly request;
77
- constructor(stateDir: string, request?: typeof controlRequest);
78
- describe(): Promise<SessionDescriptor>;
109
+ private readonly openFollow;
110
+ readonly observeRole?: ObserveConversationRole | undefined;
111
+ private controlHealthy;
112
+ constructor(stateDir: string, request?: typeof controlRequest, openFollow?: typeof followConversation, observeRole?: ObserveConversationRole | undefined);
113
+ agentContacts(): Promise<unknown>;
114
+ agentHistory(request: AgentHistoryQuery): Promise<unknown>;
115
+ private requireCorrespondence;
116
+ describe(roleObservation?: ConversationRoleObservation): Promise<SessionDescriptor>;
79
117
  snapshot(): Promise<SessionSnapshot>;
80
118
  recentOutput(request?: {
81
119
  since?: number;
@@ -87,12 +125,23 @@ export declare class RoleSessionControlAdapter implements RoleSessionControl {
87
125
  after?: string;
88
126
  limit?: number;
89
127
  }): Promise<ConversationPageView>;
128
+ conversationTail(request?: {
129
+ before?: string;
130
+ limit?: number;
131
+ }): Promise<ConversationTailPage>;
90
132
  submitPromptV2(request: {
91
133
  commandId: string;
92
134
  text: string;
93
135
  actorBrowserSession: string;
136
+ expectedSessionGeneration?: string;
137
+ requireIdle?: boolean;
94
138
  source: 'owner_admin_console';
95
139
  }): Promise<PromptReceipt>;
140
+ interruptPromptV2(request: {
141
+ commandId: string;
142
+ expectedSessionGeneration: string;
143
+ promptId: string;
144
+ }): Promise<InterruptReceipt>;
96
145
  interruptV2(commandId: string): Promise<InterruptReceipt & {
97
146
  commandId: string;
98
147
  }>;
@@ -105,12 +154,7 @@ export declare class RoleSessionControlAdapter implements RoleSessionControl {
105
154
  accepted: true;
106
155
  commandId: string;
107
156
  }>;
108
- followConversation(request: {
109
- after?: string;
110
- onPage(page: ConversationPageView): void;
111
- onEvent(event: ConversationEventV1): void;
112
- onClose(reason?: string): void;
113
- }): Promise<ConversationFollowHandle>;
157
+ followConversation(request: LegacyConversationFollowRequest | PassiveConversationFollow): Promise<ConversationFollowHandle>;
114
158
  respondPermission(request: {
115
159
  permissionId: string;
116
160
  optionId: string;
@@ -1,20 +1,42 @@
1
+ import { readFile } from 'node:fs/promises';
2
+ import { join } from 'node:path';
3
+ import { conversationTailPage, resumedConversationPage } from './conversation-history.js';
1
4
  import { controlRequest, followConversation } from '../session/control.js';
2
5
  import { FleetError, normalizeError } from './errors.js';
3
6
  const visibleEvents = (events) => events.filter(event => event.kind !== 'thought');
4
7
  export class RoleSessionControlAdapter {
5
8
  stateDir;
6
9
  request;
7
- constructor(stateDir, request = controlRequest) {
10
+ openFollow;
11
+ observeRole;
12
+ controlHealthy = true;
13
+ constructor(stateDir, request = controlRequest, openFollow = followConversation, observeRole) {
8
14
  this.stateDir = stateDir;
9
15
  this.request = request;
16
+ this.openFollow = openFollow;
17
+ this.observeRole = observeRole;
10
18
  }
11
- async describe() {
12
- const response = await this.call('snapshot');
13
- const result = response;
14
- return {
15
- backend: result.backend, protocolVersion: result.protocolVersion ?? 1,
16
- features: result.features ?? [], snapshot: result,
17
- };
19
+ async agentContacts() { await this.requireCorrespondence(); return this.call('agent_contacts'); }
20
+ async agentHistory(request) { await this.requireCorrespondence(); return this.call('agent_history', { agentHistory: request }); }
21
+ async requireCorrespondence() { if (!(await this.describe()).features.includes('agent_correspondence'))
22
+ throw new FleetError('capability_unavailable', 'This session needs an updated supervisor to show contacts and history.'); }
23
+ async describe(roleObservation) {
24
+ // A describe probe owns and always disposes its temporary watcher. Follow uses
25
+ // a separate connection-owned observer and publishes updates via onRole.
26
+ const role = roleObservation ?? this.observeRole?.(() => { });
27
+ try {
28
+ const response = await this.call('snapshot');
29
+ const result = response;
30
+ const features = (result.features ?? []).filter(feature => feature !== 'conversation_live_complete');
31
+ if (this.controlHealthy && role?.snapshot().health === 'healthy' && role.snapshot().availability === 'available'
32
+ && features.includes('conversation_observation') && features.includes('control_heartbeat'))
33
+ features.push('conversation_live_complete');
34
+ return { backend: result.backend, protocolVersion: result.protocolVersion ?? 1, features, snapshot: result };
35
+ }
36
+ finally {
37
+ if (!roleObservation)
38
+ role?.dispose();
39
+ }
18
40
  }
19
41
  async snapshot() {
20
42
  return await this.call('snapshot');
@@ -58,28 +80,98 @@ export class RoleSessionControlAdapter {
58
80
  };
59
81
  }
60
82
  async conversationPage(request = {}) {
61
- return await this.call('conversation_page', {
83
+ const live = await this.call('conversation_page', {
62
84
  after: request.after, limit: request.limit,
63
85
  });
86
+ const snapshot = await this.snapshot();
87
+ if (snapshot.backend !== 'acp' || !snapshot.sessionId)
88
+ return live;
89
+ try {
90
+ const history = await resumedConversationPage(this.stateDir, request, live, snapshot.sessionId);
91
+ const check = await this.call('conversation_page', { limit: 1 });
92
+ return check.snapshot.sessionGeneration === live.snapshot.sessionGeneration ? history : await this.call('conversation_page', request);
93
+ }
94
+ catch {
95
+ return live;
96
+ }
97
+ }
98
+ async conversationTail(request = {}) {
99
+ // A replacement while reading disk invalidates both the tail and its snapshot.
100
+ for (let attempt = 0; attempt < 3; attempt++) {
101
+ const live = await this.call('conversation_page', { limit: 1 });
102
+ const snapshot = await this.snapshot();
103
+ let resumed;
104
+ if (snapshot.backend === 'acp' && snapshot.sessionId) {
105
+ try {
106
+ if ((await readFile(join(this.stateDir, '.acp-session-id'), 'utf8')).trim() === snapshot.sessionId)
107
+ resumed = snapshot.sessionId;
108
+ }
109
+ catch { /* no resumed history: live generation only */ }
110
+ }
111
+ let page;
112
+ try {
113
+ page = await conversationTailPage(this.stateDir, request, live.snapshot, resumed);
114
+ }
115
+ catch (error) {
116
+ throw new FleetError('invalid_request', error.message);
117
+ }
118
+ const check = await this.call('conversation_page', { limit: 1 });
119
+ if (check.snapshot.sessionGeneration === live.snapshot.sessionGeneration)
120
+ return page;
121
+ }
122
+ throw new FleetError('stale_state', 'Session generation changed during conversation baseline; explicit recovery required.');
64
123
  }
65
124
  async submitPromptV2(request) {
66
125
  if (!request.text.trim())
67
126
  throw new FleetError('invalid_request', 'text is required');
68
127
  if (Buffer.byteLength(request.text) > 32 * 1024)
69
128
  throw new FleetError('invalid_request', 'text exceeds 32 KiB');
129
+ // A prompt bound to a session generation (voice or attachments) is checked atomically by supervisors that
130
+ // support it. Older supervisors do not know that command and never answer, so check the generation here
131
+ // and submit an ordinary prompt; its command id keeps retries idempotent either way.
132
+ const features = request.requireIdle ? (await this.describe()).features : undefined;
133
+ if (request.requireIdle && (!request.expectedSessionGeneration || !features?.includes('idle_bound_prompts')))
134
+ throw new FleetError('capability_unavailable', 'Live voice requires an updated supervisor.');
135
+ let command = 'submit_prompt_v2';
136
+ if (request.expectedSessionGeneration) {
137
+ if ((await this.describe()).features.includes('generation_bound_prompts'))
138
+ command = 'submit_voice_prompt';
139
+ else {
140
+ const live = await this.call('conversation_page', { limit: 1 });
141
+ if (live.snapshot?.sessionGeneration !== request.expectedSessionGeneration)
142
+ throw new FleetError('stale_state', 'Agent session changed. Record a new voice message.');
143
+ }
144
+ }
70
145
  try {
71
- return await this.call('submit_prompt_v2', {
146
+ return await this.call(command, {
72
147
  commandId: request.commandId, text: request.text, actor: request.actorBrowserSession,
73
- source: request.source,
148
+ source: request.source, expectedSessionGeneration: request.expectedSessionGeneration, requireIdle: request.requireIdle,
74
149
  });
75
150
  }
76
151
  catch (error) {
77
152
  const fleetError = normalizeError(error);
153
+ if (fleetError.message.includes('session_changed:'))
154
+ throw new FleetError('stale_state', 'Agent session changed. Record a new voice message.');
155
+ if (fleetError.message.includes('voice_busy:'))
156
+ throw new FleetError('conflict', 'Agent is already working. Wait for the current turn.');
78
157
  if (fleetError.message.includes('idempotency_conflict'))
79
158
  throw new FleetError('idempotency_conflict', 'this command id was already used with a different prompt body');
80
159
  throw fleetError;
81
160
  }
82
161
  }
162
+ async interruptPromptV2(request) {
163
+ if (!(await this.describe()).features.includes('targeted_voice_interrupt'))
164
+ throw new FleetError('capability_unavailable', 'Live voice requires an updated supervisor.');
165
+ try {
166
+ return await this.call('interrupt_voice_v2', request);
167
+ }
168
+ catch (error) {
169
+ const failure = normalizeError(error);
170
+ if (failure.message.includes('session_changed:'))
171
+ throw new FleetError('stale_state', 'Agent session changed.');
172
+ throw failure;
173
+ }
174
+ }
83
175
  async interruptV2(commandId) {
84
176
  const receipt = await this.call('interrupt_v2', { commandId });
85
177
  return {
@@ -101,32 +193,124 @@ export class RoleSessionControlAdapter {
101
193
  }
102
194
  }
103
195
  async followConversation(request) {
196
+ const passive = 'controller' in request ? request : undefined;
104
197
  let sawPage = false;
105
198
  let closed = false;
199
+ let role;
200
+ let unsubscribeRole;
201
+ let lifetime;
202
+ let roleRevision = -1;
203
+ let silence;
204
+ let lastControlHeartbeat = Date.now();
205
+ const armSilence = () => {
206
+ if (silence)
207
+ clearTimeout(silence);
208
+ silence = setTimeout(() => {
209
+ // Liveness only: no role/session snapshots, regardless of web traffic.
210
+ if (Date.now() - lastControlHeartbeat >= 6_000) {
211
+ this.controlHealthy = false;
212
+ finish('control-silent');
213
+ }
214
+ else
215
+ armSilence();
216
+ }, Math.max(1, 6_000 - (Date.now() - lastControlHeartbeat)));
217
+ silence.unref?.();
218
+ };
219
+ let follow;
106
220
  const finish = (reason) => {
107
221
  if (closed)
108
222
  return;
109
223
  closed = true;
224
+ passive?.signal?.removeEventListener('abort', aborted);
225
+ if (silence)
226
+ clearTimeout(silence);
227
+ unsubscribeRole?.();
228
+ if (!passive?.roleObservation)
229
+ role?.dispose();
230
+ follow?.close();
110
231
  request.onClose(reason);
111
232
  };
112
- const follow = await followConversation(this.stateDir, request.after, message => {
113
- if (message.conversationEvent) {
114
- request.onEvent(message.conversationEvent);
115
- return;
233
+ const aborted = () => finish('conversation follow aborted');
234
+ passive?.signal?.addEventListener('abort', aborted, { once: true });
235
+ if (passive?.signal?.aborted)
236
+ aborted();
237
+ if (closed)
238
+ return { close: () => { } };
239
+ try {
240
+ if (passive) {
241
+ const changed = (state) => {
242
+ if (closed || state.roleRevision <= roleRevision)
243
+ return;
244
+ roleRevision = state.roleRevision;
245
+ passive.onRole?.(state);
246
+ if (state.health !== 'healthy') {
247
+ this.controlHealthy = false;
248
+ finish(state.failure ?? 'role-observation-unavailable');
249
+ }
250
+ else if (lifetime !== undefined && (state.roleLifetime !== lifetime || state.availability !== 'available' || state.stateDir !== this.stateDir))
251
+ finish('role-replaced');
252
+ lifetime = state.roleLifetime;
253
+ };
254
+ role = passive.roleObservation ?? this.observeRole?.(() => { });
255
+ if (role) {
256
+ const registration = role.subscribe(changed);
257
+ unsubscribeRole = registration.unsubscribe;
258
+ const initial = registration.snapshot;
259
+ changed(initial);
260
+ if (initial.availability !== 'available' || initial.stateDir !== this.stateDir)
261
+ finish('role-unavailable');
262
+ if (closed)
263
+ return { close: () => { } };
264
+ }
265
+ armSilence();
116
266
  }
117
- if (!sawPage) {
118
- sawPage = true;
267
+ follow = await this.openFollow(this.stateDir, passive ? { controller: false, expectedGeneration: passive.expectedGeneration, after: passive.after, signal: passive.signal } : request.after, message => {
268
+ if (closed)
269
+ return;
270
+ if (passive && message.controlHeartbeat === true) {
271
+ lastControlHeartbeat = Date.now();
272
+ this.controlHealthy = true;
273
+ armSilence();
274
+ return;
275
+ }
119
276
  if (message.ok === false) {
120
277
  finish(String(message.error ?? 'conversation follow refused'));
121
- follow.close();
122
278
  return;
123
279
  }
124
- request.onPage(message.result);
280
+ if (message.conversationEvent) {
281
+ request.onEvent(message.conversationEvent);
282
+ return;
283
+ }
284
+ if (passive) {
285
+ if (message.conversationReplay)
286
+ passive.onReplay(message.conversationReplay);
287
+ else if (message.conversationReady)
288
+ passive.onReady(message.conversationReady);
289
+ else if (message.conversationState)
290
+ passive.onState(message.conversationState);
291
+ }
292
+ else if (!sawPage) {
293
+ sawPage = true;
294
+ request.onPage(message.result);
295
+ }
296
+ });
297
+ // Abort may win while the connector is still creating its handle.
298
+ if (closed || passive?.signal?.aborted) {
299
+ follow.close();
300
+ finish('conversation follow aborted');
125
301
  }
126
- });
127
- follow.socket.once('close', () => finish());
128
- follow.socket.once('error', error => finish(error.message));
129
- return { close: () => { follow.close(); finish(); } };
302
+ else {
303
+ follow.socket.once('close', () => finish());
304
+ follow.socket.once('error', error => finish(error.message));
305
+ if (follow.socket.destroyed)
306
+ finish();
307
+ }
308
+ return { close: () => finish() };
309
+ }
310
+ catch (error) {
311
+ finish(error.message);
312
+ throw error;
313
+ }
130
314
  }
131
315
  async respondPermission(request) {
132
316
  await this.call('respond_permission', request);
@@ -1,3 +1,4 @@
1
+ import { socketPath } from '../socket-path.js';
1
2
  import { createHash } from 'node:crypto';
2
3
  import { existsSync, readFileSync } from 'node:fs';
3
4
  import { join } from 'node:path';
@@ -35,7 +36,8 @@ export class SupervisorOursTools {
35
36
  const identity = legacy
36
37
  ? legacySupervisorIdentity(role, identityName, selected.temporary, descriptor.generation)
37
38
  : { name: identityName, cid: descriptor.cid, generation: descriptor.generation };
38
- if (legacy && descriptor.socket !== join(selected.dir, '.ours-bridge', `g${descriptor.generation}.sock`))
39
+ const legacySocket = join(selected.dir, '.ours-bridge', `g${descriptor.generation}.sock`);
40
+ if (legacy && descriptor.socket !== legacySocket && descriptor.socket !== socketPath(legacySocket))
39
41
  throw new FleetError('capability_unavailable', 'legacy supervisor socket does not match the selected agent');
40
42
  if ((!legacy && (descriptor.role !== role || descriptor.identity !== identityName))
41
43
  || !/^[a-f0-9]{64}$/i.test(identity.cid ?? '') || !Number.isSafeInteger(descriptor.generation) || descriptor.generation < 1)
@@ -71,16 +73,8 @@ export class SupervisorOursTools {
71
73
  verifySelection();
72
74
  return;
73
75
  }
74
- const actual = await client.callTool({ name: 'current_identity', arguments: {} });
75
- const blocks = actual.content;
76
- const lines = blocks?.length === 1 && blocks[0].type === 'text' ? blocks[0].text?.split('\n') : undefined;
77
- const prefix = `Bound to "${identity.name}" (${identity.cid})`;
78
- const suffix = lines?.[0].startsWith(prefix) ? lines[0].slice(prefix.length) : undefined;
79
- const roleSuffix = typeof suffix === 'string' && /^ — role "[^"\r\n]+" under root "[^"\r\n]+"\.$/.test(suffix);
80
- const temporaryLine = 'TEMPORARY identity owned by the Fleet supervisor for this logical agent instance. Bridge or harness disconnect retains it; terminal supervisor release deletes local state with best-effort peer notices.';
81
- if (actual.isError || !(roleSuffix || (selected.temporary && suffix === '.'))
82
- || (selected.temporary ? lines?.[1] !== temporaryLine : lines?.[1]?.startsWith('TEMPORARY')))
83
- throw new FleetError('capability_unavailable', 'connected supervisor identity does not match legacy ownership proof');
76
+ // The supervisor owns the binding. The authenticated bridge and pinned local
77
+ // assignment are authoritative; current_identity is display text, not a protocol.
84
78
  const fresh = legacySupervisorIdentity(role, identityName, selected.temporary, descriptor.generation);
85
79
  if (!('proof' in identity) || fresh.proof !== identity.proof)
86
80
  throw new FleetError('capability_unavailable', 'legacy supervisor identity changed');