@ours.network/fleet 1.2.0-nightly.6 → 1.2.0-nightly.60

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 (389) hide show
  1. package/README.md +287 -65
  2. package/dist/account-origin.d.ts +2 -0
  3. package/dist/account-origin.js +6 -0
  4. package/dist/agent-ours/bridge.js +35 -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 +4 -0
  11. package/dist/agent-ours/mcp-endpoint.js +5 -0
  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 +21 -1
  15. package/dist/agent-ours/service.js +94 -14
  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 +212 -0
  20. package/dist/application/fleet-query-service.d.ts +1 -1
  21. package/dist/application/fleet-query-service.js +2 -2
  22. package/dist/application/legacy-supervisor-identity.d.ts +7 -0
  23. package/dist/application/legacy-supervisor-identity.js +43 -0
  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 +9 -1
  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 +30 -0
  35. package/dist/application/session-control.js +72 -3
  36. package/dist/application/supervisor-ours-tools.d.ts +159 -0
  37. package/dist/application/supervisor-ours-tools.js +115 -0
  38. package/dist/application/task-layouts.d.ts +91 -0
  39. package/dist/application/task-layouts.js +259 -0
  40. package/dist/application/task-room-service.d.ts +99 -3
  41. package/dist/application/task-room-service.js +285 -32
  42. package/dist/application/types.d.ts +2 -0
  43. package/dist/atomic-file.d.ts +5 -0
  44. package/dist/atomic-file.js +62 -1
  45. package/dist/briefing.d.ts +3 -1
  46. package/dist/briefing.js +70 -30
  47. package/dist/build-info.json +7 -4
  48. package/dist/capabilities.d.ts +6 -1
  49. package/dist/capabilities.js +8 -0
  50. package/dist/cli.js +407 -37
  51. package/dist/client-profile.d.ts +5 -17
  52. package/dist/client-profile.js +4 -127
  53. package/dist/config.d.ts +45 -0
  54. package/dist/config.js +57 -6
  55. package/dist/creation.d.ts +1 -1
  56. package/dist/creation.js +4 -4
  57. package/dist/daemon-recovery.d.ts +1 -8
  58. package/dist/daemon-recovery.js +8 -113
  59. package/dist/docs.d.ts +1 -1
  60. package/dist/docs.js +123 -19
  61. package/dist/doctor.d.ts +1 -1
  62. package/dist/doctor.js +55 -60
  63. package/dist/erased-resources.d.ts +9 -0
  64. package/dist/erased-resources.js +87 -0
  65. package/dist/file-delivery/reader.d.ts +15 -0
  66. package/dist/file-delivery/reader.js +63 -0
  67. package/dist/file-delivery/store.d.ts +15 -0
  68. package/dist/file-delivery/store.js +124 -0
  69. package/dist/file-delivery/types.d.ts +19 -0
  70. package/dist/file-delivery/types.js +1 -0
  71. package/dist/fleet-command-audit.d.ts +7 -0
  72. package/dist/fleet-command-audit.js +26 -9
  73. package/dist/harness/agent-session.d.ts +2 -0
  74. package/dist/harness/claude-code-session.js +1 -1
  75. package/dist/harness/claude-code.js +20 -11
  76. package/dist/harness/claude-runtime.d.ts +12 -0
  77. package/dist/harness/claude-runtime.js +43 -0
  78. package/dist/harness/codex-app-server-proxy.d.ts +4 -1
  79. package/dist/harness/codex-app-server-proxy.js +41 -5
  80. package/dist/harness/codex-reasoning.d.ts +5 -0
  81. package/dist/harness/codex-reasoning.js +12 -0
  82. package/dist/harness/codex-runtime.d.ts +11 -1
  83. package/dist/harness/codex-runtime.js +31 -4
  84. package/dist/harness/codex-session.js +2 -2
  85. package/dist/harness/codex.js +23 -12
  86. package/dist/init-wizard.d.ts +2 -0
  87. package/dist/init-wizard.js +38 -19
  88. package/dist/isolation/policy.js +6 -1
  89. package/dist/isolation/types.d.ts +5 -0
  90. package/dist/managed-cli.d.ts +253 -0
  91. package/dist/managed-cli.js +923 -0
  92. package/dist/managed-recovery.d.ts +13 -0
  93. package/dist/managed-recovery.js +31 -0
  94. package/dist/minimal-setup.d.ts +2 -0
  95. package/dist/minimal-setup.js +44 -0
  96. package/dist/monitor.d.ts +8 -34
  97. package/dist/monitor.js +89 -152
  98. package/dist/notifications/fleet-producer.d.ts +19 -0
  99. package/dist/notifications/fleet-producer.js +208 -0
  100. package/dist/notifications/outbox.d.ts +46 -0
  101. package/dist/notifications/outbox.js +158 -0
  102. package/dist/notifications/target-cleanup.d.ts +8 -0
  103. package/dist/notifications/target-cleanup.js +41 -0
  104. package/dist/notifications/task-producer.d.ts +18 -0
  105. package/dist/notifications/task-producer.js +67 -0
  106. package/dist/ops.d.ts +2 -0
  107. package/dist/ops.js +20 -3
  108. package/dist/owner-channel/channel.js +17 -6
  109. package/dist/owner-channel/commands.d.ts +4 -0
  110. package/dist/owner-channel/commands.js +8 -0
  111. package/dist/owner-channel/lifecycle-outbox.js +4 -3
  112. package/dist/owner-channel/ours-client.js +2 -17
  113. package/dist/paths.d.ts +2 -0
  114. package/dist/paths.js +2 -0
  115. package/dist/preset-migration.js +25 -0
  116. package/dist/resolved-plan.js +1 -0
  117. package/dist/rooms-tasks/add-member.d.ts +48 -0
  118. package/dist/rooms-tasks/add-member.js +83 -0
  119. package/dist/rooms-tasks/archived-absence.d.ts +8 -0
  120. package/dist/rooms-tasks/archived-absence.js +48 -0
  121. package/dist/rooms-tasks/cli.js +65 -5
  122. package/dist/rooms-tasks/close.d.ts +6 -2
  123. package/dist/rooms-tasks/close.js +246 -37
  124. package/dist/rooms-tasks/config.d.ts +1 -1
  125. package/dist/rooms-tasks/config.js +11 -4
  126. package/dist/rooms-tasks/cowork-adapter.d.ts +2 -4
  127. package/dist/rooms-tasks/cowork-adapter.js +7 -121
  128. package/dist/rooms-tasks/cowork-http.js +29 -1
  129. package/dist/rooms-tasks/deletion.d.ts +1 -1
  130. package/dist/rooms-tasks/deletion.js +84 -5
  131. package/dist/rooms-tasks/erasure.d.ts +6 -0
  132. package/dist/rooms-tasks/erasure.js +173 -0
  133. package/dist/rooms-tasks/external-worker.d.ts +1 -1
  134. package/dist/rooms-tasks/external-worker.js +3 -4
  135. package/dist/rooms-tasks/layout-binding-client.d.ts +11 -0
  136. package/dist/rooms-tasks/layout-binding-client.js +56 -0
  137. package/dist/rooms-tasks/layout-binding-grants.d.ts +21 -0
  138. package/dist/rooms-tasks/layout-binding-grants.js +128 -0
  139. package/dist/rooms-tasks/layout-cli.d.ts +4 -0
  140. package/dist/rooms-tasks/layout-cli.js +107 -0
  141. package/dist/rooms-tasks/layout-config.d.ts +11 -0
  142. package/dist/rooms-tasks/layout-config.js +89 -0
  143. package/dist/rooms-tasks/layout-control.d.ts +21 -0
  144. package/dist/rooms-tasks/layout-control.js +52 -0
  145. package/dist/rooms-tasks/layout-member-retirement.d.ts +10 -0
  146. package/dist/rooms-tasks/layout-member-retirement.js +90 -0
  147. package/dist/rooms-tasks/layout-service.d.ts +43 -0
  148. package/dist/rooms-tasks/layout-service.js +203 -0
  149. package/dist/rooms-tasks/layout.d.ts +121 -0
  150. package/dist/rooms-tasks/layout.js +285 -0
  151. package/dist/rooms-tasks/legacy-task-member.d.ts +19 -0
  152. package/dist/rooms-tasks/legacy-task-member.js +116 -0
  153. package/dist/rooms-tasks/live-readiness.d.ts +18 -0
  154. package/dist/rooms-tasks/live-readiness.js +85 -0
  155. package/dist/rooms-tasks/member-ownership.d.ts +5 -0
  156. package/dist/rooms-tasks/member-ownership.js +40 -0
  157. package/dist/rooms-tasks/member-startup.d.ts +1 -0
  158. package/dist/rooms-tasks/member-startup.js +2 -0
  159. package/dist/rooms-tasks/provision.d.ts +3 -0
  160. package/dist/rooms-tasks/provision.js +101 -49
  161. package/dist/rooms-tasks/recovery.d.ts +19 -0
  162. package/dist/rooms-tasks/recovery.js +110 -0
  163. package/dist/rooms-tasks/room-state.d.ts +4 -2
  164. package/dist/rooms-tasks/room-state.js +47 -8
  165. package/dist/rooms-tasks/task-service-retirement.d.ts +11 -0
  166. package/dist/rooms-tasks/task-service-retirement.js +21 -0
  167. package/dist/rooms-tasks/task-state.d.ts +51 -8
  168. package/dist/rooms-tasks/task-state.js +245 -25
  169. package/dist/rooms-tasks/types.d.ts +40 -0
  170. package/dist/rooms-tasks/workspace-artifacts.d.ts +6 -0
  171. package/dist/rooms-tasks/workspace-artifacts.js +93 -0
  172. package/dist/rooms-tasks/workspace.d.ts +35 -0
  173. package/dist/rooms-tasks/workspace.js +325 -0
  174. package/dist/runner.d.ts +25 -3
  175. package/dist/runner.js +291 -49
  176. package/dist/session/acp.d.ts +53 -1
  177. package/dist/session/acp.js +216 -40
  178. package/dist/session/arbiter.d.ts +11 -0
  179. package/dist/session/arbiter.js +43 -2
  180. package/dist/session/codex-app-server.d.ts +32 -0
  181. package/dist/session/codex-app-server.js +137 -23
  182. package/dist/session/control.d.ts +12 -1
  183. package/dist/session/control.js +68 -3
  184. package/dist/session/conversation-normalizer.d.ts +2 -1
  185. package/dist/session/conversation-normalizer.js +1 -1
  186. package/dist/session/conversation-store.d.ts +1 -1
  187. package/dist/session/conversation-store.js +7 -11
  188. package/dist/session/conversation-types.d.ts +28 -3
  189. package/dist/session/prompt-batch.d.ts +63 -0
  190. package/dist/session/prompt-batch.js +86 -0
  191. package/dist/session/task-notice.d.ts +14 -0
  192. package/dist/session/task-notice.js +13 -0
  193. package/dist/session/types.d.ts +14 -0
  194. package/dist/setup-tunnel-preflight.d.ts +34 -0
  195. package/dist/setup-tunnel-preflight.js +62 -0
  196. package/dist/socket-path.d.ts +2 -0
  197. package/dist/socket-path.js +22 -0
  198. package/dist/spawn.d.ts +5 -1
  199. package/dist/spawn.js +40 -4
  200. package/dist/startup-prompt.d.ts +4 -0
  201. package/dist/startup-prompt.js +36 -0
  202. package/dist/startup-readiness.d.ts +13 -0
  203. package/dist/startup-readiness.js +38 -0
  204. package/dist/startup-telemetry.d.ts +16 -0
  205. package/dist/startup-telemetry.js +46 -0
  206. package/dist/subscriptions/cli.d.ts +91 -0
  207. package/dist/subscriptions/cli.js +223 -0
  208. package/dist/subscriptions/launch.d.ts +37 -0
  209. package/dist/subscriptions/launch.js +93 -0
  210. package/dist/subscriptions/login.d.ts +75 -0
  211. package/dist/subscriptions/login.js +182 -0
  212. package/dist/subscriptions/observed.d.ts +7 -0
  213. package/dist/subscriptions/observed.js +44 -0
  214. package/dist/subscriptions/probe.d.ts +10 -0
  215. package/dist/subscriptions/probe.js +73 -0
  216. package/dist/subscriptions/service.d.ts +102 -0
  217. package/dist/subscriptions/service.js +364 -0
  218. package/dist/subscriptions/store.d.ts +71 -0
  219. package/dist/subscriptions/store.js +140 -0
  220. package/dist/supervisor/adoption.d.ts +8 -0
  221. package/dist/supervisor/adoption.js +78 -0
  222. package/dist/supervisor/catalog.d.ts +62 -0
  223. package/dist/supervisor/catalog.js +254 -0
  224. package/dist/supervisor/fleet.d.ts +8 -0
  225. package/dist/supervisor/fleet.js +89 -0
  226. package/dist/supervisor/index.js +3 -6
  227. package/dist/supervisor/launchd.d.ts +1 -1
  228. package/dist/supervisor/launchd.js +26 -13
  229. package/dist/supervisor/legacy.d.ts +35 -0
  230. package/dist/supervisor/legacy.js +524 -0
  231. package/dist/supervisor/manager.d.ts +15 -0
  232. package/dist/supervisor/manager.js +270 -0
  233. package/dist/supervisor/scope.d.ts +4 -0
  234. package/dist/supervisor/scope.js +10 -0
  235. package/dist/supervisor/systemd.d.ts +2 -2
  236. package/dist/supervisor/systemd.js +19 -33
  237. package/dist/task-supervision.d.ts +9 -0
  238. package/dist/task-supervision.js +145 -0
  239. package/dist/temp-idle.d.ts +17 -0
  240. package/dist/temp-idle.js +42 -0
  241. package/dist/temp-lifecycle.d.ts +23 -8
  242. package/dist/temp-lifecycle.js +113 -65
  243. package/dist/temp-supervisor-recovery.d.ts +1 -1
  244. package/dist/temp-supervisor-recovery.js +2 -2
  245. package/dist/tunnel-setup-completion.d.ts +7 -0
  246. package/dist/tunnel-setup-completion.js +13 -0
  247. package/dist/tunnel-setup-input.d.ts +19 -0
  248. package/dist/tunnel-setup-input.js +124 -0
  249. package/dist/web/agent-attachments.d.ts +29 -0
  250. package/dist/web/agent-attachments.js +162 -0
  251. package/dist/web/auth.d.ts +63 -2
  252. package/dist/web/auth.js +191 -10
  253. package/dist/web/available-port.d.ts +2 -0
  254. package/dist/web/available-port.js +23 -0
  255. package/dist/web/control.d.ts +3 -3
  256. package/dist/web/control.js +10 -8
  257. package/dist/web/fleet-config-service.d.ts +4 -1
  258. package/dist/web/fleet-config-service.js +46 -15
  259. package/dist/web/prefix-gateway.d.ts +32 -0
  260. package/dist/web/prefix-gateway.js +238 -0
  261. package/dist/web/runtime.d.ts +3 -1
  262. package/dist/web/runtime.js +116 -31
  263. package/dist/web/server.d.ts +16 -0
  264. package/dist/web/server.js +654 -55
  265. package/dist/web/service.d.ts +11 -2
  266. package/dist/web/service.js +30 -4
  267. package/dist/web/workspace-devices.d.ts +107 -0
  268. package/dist/web/workspace-devices.js +277 -0
  269. package/dist/web/workspace-tunnel.d.ts +1 -0
  270. package/dist/web/workspace-tunnel.js +34 -0
  271. package/dist/workspace-enrollment.d.ts +82 -0
  272. package/dist/workspace-enrollment.js +566 -0
  273. package/dist/workspace-replacement.d.ts +66 -0
  274. package/dist/workspace-replacement.js +220 -0
  275. package/docs/acp-files.md +146 -0
  276. package/docs/fleet-migration.md +54 -0
  277. package/docs/live-voice.md +13 -0
  278. package/docs/room-layouts.md +227 -0
  279. package/docs/task-workspaces.md +225 -0
  280. package/docs/validation/managed-cli-permissions.md +421 -0
  281. package/examples/room-layouts/fresh.yaml +27 -0
  282. package/examples/room-layouts/scoped.yaml +21 -0
  283. package/examples/room-layouts/shared.yaml +19 -0
  284. package/package.json +20 -19
  285. package/presets/README.md +78 -0
  286. package/presets/brain-catalog.json +8 -2
  287. package/presets/fleet/agent_templates/Critic.yaml +1 -1
  288. package/presets/fleet/agent_templates/Developer.yaml +1 -1
  289. package/presets/fleet/agent_templates/Engineer.yaml +5 -0
  290. package/presets/fleet/agent_templates/LocalCoordinator.yaml +1 -1
  291. package/presets/fleet/agents/FleetCoordinator.yaml +1 -1
  292. package/presets/fleet/brains/claude-fable-5-1-high.yaml +5 -0
  293. package/presets/fleet/brains/claude-fable-5-1-low.yaml +5 -0
  294. package/presets/fleet/brains/claude-fable-5-1-max.yaml +5 -0
  295. package/presets/fleet/brains/claude-fable-5-1-medium.yaml +5 -0
  296. package/presets/fleet/brains/claude-fable-5-1-xhigh.yaml +5 -0
  297. package/presets/fleet/brains/claude-fable-5-high.yaml +1 -1
  298. package/presets/fleet/brains/claude-fable-5-low.yaml +1 -1
  299. package/presets/fleet/brains/claude-fable-5-max.yaml +1 -1
  300. package/presets/fleet/brains/claude-fable-5-medium.yaml +1 -1
  301. package/presets/fleet/brains/claude-fable-5-xhigh.yaml +1 -1
  302. package/presets/fleet/brains/claude-haiku-4-5-20251001-default.yaml +4 -0
  303. package/presets/fleet/brains/claude-opus-5-5-high.yaml +5 -0
  304. package/presets/fleet/brains/claude-opus-5-5-low.yaml +5 -0
  305. package/presets/fleet/brains/claude-opus-5-5-max.yaml +5 -0
  306. package/presets/fleet/brains/claude-opus-5-5-medium.yaml +5 -0
  307. package/presets/fleet/brains/claude-opus-5-5-xhigh.yaml +5 -0
  308. package/presets/fleet/brains/claude-opus-5-high.yaml +1 -1
  309. package/presets/fleet/brains/claude-opus-5-low.yaml +1 -1
  310. package/presets/fleet/brains/claude-opus-5-max.yaml +1 -1
  311. package/presets/fleet/brains/claude-opus-5-medium.yaml +1 -1
  312. package/presets/fleet/brains/claude-opus-5-xhigh.yaml +1 -1
  313. package/presets/fleet/brains/claude-sonnet-5-high.yaml +1 -1
  314. package/presets/fleet/brains/claude-sonnet-5-low.yaml +1 -1
  315. package/presets/fleet/brains/claude-sonnet-5-max.yaml +1 -1
  316. package/presets/fleet/brains/claude-sonnet-5-medium.yaml +1 -1
  317. package/presets/fleet/brains/claude-sonnet-5-xhigh.yaml +1 -1
  318. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-high.yaml +1 -1
  319. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-low.yaml +1 -1
  320. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-medium.yaml +1 -1
  321. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-xhigh.yaml +1 -1
  322. package/presets/fleet/brains/codex-gpt-5-4-high.yaml +1 -1
  323. package/presets/fleet/brains/codex-gpt-5-4-low.yaml +1 -1
  324. package/presets/fleet/brains/codex-gpt-5-4-medium.yaml +1 -1
  325. package/presets/fleet/brains/codex-gpt-5-4-mini-high.yaml +1 -1
  326. package/presets/fleet/brains/codex-gpt-5-4-mini-low.yaml +1 -1
  327. package/presets/fleet/brains/codex-gpt-5-4-mini-medium.yaml +1 -1
  328. package/presets/fleet/brains/codex-gpt-5-4-mini-xhigh.yaml +1 -1
  329. package/presets/fleet/brains/codex-gpt-5-4-xhigh.yaml +1 -1
  330. package/presets/fleet/brains/codex-gpt-5-5-high.yaml +1 -1
  331. package/presets/fleet/brains/codex-gpt-5-5-low.yaml +1 -1
  332. package/presets/fleet/brains/codex-gpt-5-5-medium.yaml +1 -1
  333. package/presets/fleet/brains/codex-gpt-5-5-xhigh.yaml +1 -1
  334. package/presets/fleet/brains/codex-gpt-5-6-luna-high.yaml +1 -1
  335. package/presets/fleet/brains/codex-gpt-5-6-luna-low.yaml +1 -1
  336. package/presets/fleet/brains/codex-gpt-5-6-luna-max.yaml +1 -1
  337. package/presets/fleet/brains/codex-gpt-5-6-luna-medium.yaml +1 -1
  338. package/presets/fleet/brains/codex-gpt-5-6-luna-xhigh.yaml +1 -1
  339. package/presets/fleet/brains/codex-gpt-5-6-sol-high.yaml +1 -1
  340. package/presets/fleet/brains/codex-gpt-5-6-sol-low.yaml +1 -1
  341. package/presets/fleet/brains/codex-gpt-5-6-sol-max.yaml +1 -1
  342. package/presets/fleet/brains/codex-gpt-5-6-sol-medium.yaml +1 -1
  343. package/presets/fleet/brains/codex-gpt-5-6-sol-ultra.yaml +1 -1
  344. package/presets/fleet/brains/codex-gpt-5-6-sol-xhigh.yaml +1 -1
  345. package/presets/fleet/brains/codex-gpt-5-6-terra-high.yaml +1 -1
  346. package/presets/fleet/brains/codex-gpt-5-6-terra-low.yaml +1 -1
  347. package/presets/fleet/brains/codex-gpt-5-6-terra-max.yaml +1 -1
  348. package/presets/fleet/brains/codex-gpt-5-6-terra-medium.yaml +1 -1
  349. package/presets/fleet/brains/codex-gpt-5-6-terra-ultra.yaml +1 -1
  350. package/presets/fleet/brains/codex-gpt-5-6-terra-xhigh.yaml +1 -1
  351. package/presets/fleet/brains/codex-gpt-6-1-sol-high.yaml +5 -0
  352. package/presets/fleet/brains/codex-gpt-6-1-sol-low.yaml +5 -0
  353. package/presets/fleet/brains/codex-gpt-6-1-sol-max.yaml +5 -0
  354. package/presets/fleet/brains/codex-gpt-6-1-sol-medium.yaml +5 -0
  355. package/presets/fleet/brains/codex-gpt-6-1-sol-ultra.yaml +5 -0
  356. package/presets/fleet/brains/codex-gpt-6-1-sol-xhigh.yaml +5 -0
  357. package/presets/fleet/brains/codex-gpt-6-astra-high.yaml +1 -1
  358. package/presets/fleet/brains/codex-gpt-6-astra-low.yaml +1 -1
  359. package/presets/fleet/brains/codex-gpt-6-astra-max.yaml +1 -1
  360. package/presets/fleet/brains/codex-gpt-6-astra-medium.yaml +1 -1
  361. package/presets/fleet/brains/codex-gpt-6-astra-ultra.yaml +1 -1
  362. package/presets/fleet/brains/codex-gpt-6-astra-xhigh.yaml +1 -1
  363. package/presets/fleet/brains/codex-gpt-6-luna-high.yaml +5 -0
  364. package/presets/fleet/brains/codex-gpt-6-luna-low.yaml +5 -0
  365. package/presets/fleet/brains/codex-gpt-6-luna-max.yaml +5 -0
  366. package/presets/fleet/brains/codex-gpt-6-luna-medium.yaml +5 -0
  367. package/presets/fleet/brains/codex-gpt-6-luna-xhigh.yaml +5 -0
  368. package/presets/fleet/brains/codex-gpt-6-sol-high.yaml +5 -0
  369. package/presets/fleet/brains/codex-gpt-6-sol-low.yaml +5 -0
  370. package/presets/fleet/brains/codex-gpt-6-sol-max.yaml +5 -0
  371. package/presets/fleet/brains/codex-gpt-6-sol-medium.yaml +5 -0
  372. package/presets/fleet/brains/codex-gpt-6-sol-ultra.yaml +5 -0
  373. package/presets/fleet/brains/codex-gpt-6-sol-xhigh.yaml +5 -0
  374. package/presets/fleet/roles/Coordinator.yaml +34 -11
  375. package/presets/fleet/roles/Engineer.yaml +40 -0
  376. package/presets/fleet/room_layouts/engineering.yaml +25 -0
  377. package/presets/fleet/room_layouts/pair.yaml +24 -0
  378. package/presets/fleet/room_layouts/single.yaml +17 -0
  379. package/presets/fleet/room_layouts/team.yaml +30 -0
  380. package/presets/fleet/room_templates/engineering.yaml +19 -0
  381. package/presets/manifest.json +2 -2
  382. package/dist/web-app/assets/index-59GF-gsZ.css +0 -1
  383. package/dist/web-app/assets/index-C0h3ALvs.js +0 -10
  384. package/dist/web-app/icons/ours-fleet-maskable.svg +0 -4
  385. package/dist/web-app/icons/ours-fleet.svg +0 -4
  386. package/dist/web-app/index.html +0 -17
  387. package/dist/web-app/manifest.webmanifest +0 -15
  388. package/dist/web-app/offline.html +0 -18
  389. package/dist/web-app/sw.js +0 -51
@@ -29,6 +29,7 @@ export class RoleTurnArbiter {
29
29
  tail = Promise.resolve();
30
30
  unsettled = 0;
31
31
  stopping = false;
32
+ retiring = false;
32
33
  /** Bumped by `retireStalledAdmission`; every claim carries the one it was made under. */
33
34
  generation = 0;
34
35
  /** Resolves the instant the CURRENT generation is retired. */
@@ -89,7 +90,20 @@ export class RoleTurnArbiter {
89
90
  retired.retire();
90
91
  }
91
92
  queuePrompt(text, options = {}) {
92
- return this.exclusive(async () => this.track(await this.session.queuePrompt(text, options)));
93
+ return this.exclusive(async () => {
94
+ if (this.retiring)
95
+ throw new Error('Chat is closing');
96
+ return this.track(await this.session.queuePrompt(text, this.monitorAdmissionOptions(options)));
97
+ });
98
+ }
99
+ /** Inbox traffic must not cancel or steer an active human console/voice turn.
100
+ * Queue it once; the monitor commits its cursor after that turn succeeds.
101
+ * Other origins retain their configured monitor policy.
102
+ */
103
+ monitorAdmissionOptions(options) {
104
+ return options.origin?.kind === 'fleet-monitor'
105
+ && this.session.snapshot().activePromptSource === 'owner-admin-console'
106
+ ? { ...options, interrupt: false, steer: false } : options;
93
107
  }
94
108
  async submitPrompt(text, options = {}) {
95
109
  return (await this.queuePrompt(text, options)).completion;
@@ -99,6 +113,10 @@ export class RoleTurnArbiter {
99
113
  * answers and explicit interrupts must remain able to pass immediately.
100
114
  */
101
115
  submitPromptAfterTool(text, options = {}) {
116
+ if (this.retiring)
117
+ return Promise.reject(new Error('Chat is closing'));
118
+ if (this.monitorAdmissionOptions(options) !== options)
119
+ return this.submitPrompt(text, options);
102
120
  return this.session.submitPromptAfterTool?.(text, options)
103
121
  ?? this.session.submitPrompt(text, { ...options, interrupt: false, steer: false });
104
122
  }
@@ -136,6 +154,20 @@ export class RoleTurnArbiter {
136
154
  throw error;
137
155
  });
138
156
  }
157
+ /** Retire only at the same admission boundary used by all prompt producers. */
158
+ retireIfIdle(expired) {
159
+ return this.exclusive(async () => {
160
+ const snapshot = this.session.snapshot();
161
+ const queue = this.session.conversationSnapshot?.().queueDepth ?? -1;
162
+ if (this.retiring || this.unsettled > 0 || snapshot.readiness !== 'idle' || !expired(snapshot, queue))
163
+ return false;
164
+ this.retiring = true;
165
+ this.stopping = true;
166
+ await this.session.close();
167
+ return true;
168
+ });
169
+ }
170
+ sharedTurnId(promptId) { return this.session.sharedTurnId?.(promptId); }
139
171
  stopScheduledAdmission() { this.stopping = true; }
140
172
  isAlive() { return this.session.isAlive(); }
141
173
  snapshot() { return this.session.snapshot(); }
@@ -157,7 +189,16 @@ export class RoleTurnArbiter {
157
189
  submitPromptBrowser(command) {
158
190
  if (!this.session.submitPromptBrowser)
159
191
  return Promise.reject(new Error('browser prompt admission is unavailable'));
160
- return this.exclusive(() => this.session.submitPromptBrowser(command));
192
+ return this.exclusive(() => { if (this.retiring)
193
+ throw new Error('Chat is closing'); return this.session.submitPromptBrowser(command); });
194
+ }
195
+ get liveVoiceSupported() { return this.session.liveVoiceSupported === true; }
196
+ interruptPrompt(generation, promptId) {
197
+ return this.exclusive(async () => {
198
+ if (!this.session.interruptPrompt)
199
+ throw new Error('targeted voice interrupt is unavailable');
200
+ return interruptOutcome(await this.session.interruptPrompt(generation, promptId));
201
+ });
161
202
  }
162
203
  interrupt(source = 'local-console') {
163
204
  return this.exclusive(async () => interruptOutcome(await this.session.interrupt(source)));
@@ -1,5 +1,7 @@
1
+ import { type TaskNoticeBinding, type TaskCreatedNotice } from './task-notice.js';
1
2
  import type { CommonPermissions } from '../config.js';
2
3
  import type { ConversationEventV1, ConversationSnapshot, PromptOrigin, PromptReceipt, SubmitPromptCommand } from './conversation-types.js';
4
+ import { type PendingPrompt } from './prompt-batch.js';
3
5
  import type { CodexAppServerTransportFactory } from './codex-app-server-transport.js';
4
6
  import type { AgentSession, ConversationHandlePage, ExitRecord, InterruptOutcome, QueuedPrompt, SessionEvent, SessionSnapshot, SubmitPromptOptions, TurnCancellationSource, TurnResult } from './types.js';
5
7
  type JsonObject = Record<string, unknown>;
@@ -10,6 +12,7 @@ export interface CodexAppServerSessionOptions {
10
12
  env: Record<string, string>;
11
13
  stateDir: string;
12
14
  mode: 'fresh' | 'resume';
15
+ requireResume?: boolean;
13
16
  permissions: CommonPermissions;
14
17
  permissionMode: NonNullable<SessionSnapshot['permissionMode']>;
15
18
  model?: string | null;
@@ -28,6 +31,16 @@ export interface CodexAppServerSessionOptions {
28
31
  }
29
32
  /** Server-generated application provenance for authenticated browser prompts only. */
30
33
  export declare function nativePromptAdditionalContext(origin: PromptOrigin | undefined): JsonObject | undefined;
34
+ /**
35
+ * Application context for a turn that carries several admitted prompts.
36
+ *
37
+ * The single-prompt context says "the accompanying user text", which would
38
+ * cover every member. A shared turn therefore carries this one entry instead:
39
+ * each member is its own user text item, in order, and its route and exact
40
+ * bytes are stated here by the server. Text imitating this wording is still
41
+ * only user text; it cannot add an application-context entry.
42
+ */
43
+ export declare function nativeBatchAdditionalContext(members: ReadonlyArray<Pick<PendingPrompt, 'text' | 'origin'>>): JsonObject | undefined;
31
44
  /** Native Codex session implementing Fleet's provider-neutral live-session contract. */
32
45
  export declare class CodexAppServerSession implements AgentSession {
33
46
  private readonly options;
@@ -54,6 +67,10 @@ export declare class CodexAppServerSession implements AgentSession {
54
67
  private exit;
55
68
  private queueDepth;
56
69
  private promptTail;
70
+ /** Admitted prompts not yet handed to the app-server, in admission order. */
71
+ private readonly pendingPrompts;
72
+ /** Admitted prompt → the turn that carries it, while that turn is open. */
73
+ private readonly sharedTurns;
57
74
  private activeTurn?;
58
75
  private controllerCount;
59
76
  private controllerGrace?;
@@ -67,7 +84,20 @@ export declare class CodexAppServerSession implements AgentSession {
67
84
  isAlive(): boolean;
68
85
  snapshot(): SessionSnapshot;
69
86
  queuePrompt(text: string, options?: SubmitPromptOptions): Promise<QueuedPrompt>;
87
+ /** The turn whose events and result answer this admitted prompt; itself unless a release shared it. */
88
+ sharedTurnId(promptId: string): string | undefined;
89
+ /** Each prompt keeps its own step in the chain; the step it reaches releases a whole snapshot. */
90
+ private enqueue;
91
+ /**
92
+ * The turn boundary: the first waiting prompt to get here synchronously
93
+ * takes every pending prompt delivered with it into one turn. A prompt
94
+ * admitted afterwards stays pending for the next boundary, and a step whose
95
+ * prompt was already taken reports that turn's result.
96
+ */
97
+ private release;
70
98
  submitPrompt(text: string, options?: SubmitPromptOptions): Promise<TurnResult>;
99
+ readonly liveVoiceSupported = true;
100
+ interruptPrompt(generation: string, promptId: string): Promise<InterruptOutcome>;
71
101
  interrupt(source?: TurnCancellationSource): Promise<InterruptOutcome>;
72
102
  respondPermission(permissionId: string, optionId: string): boolean;
73
103
  respondPermissionV2(permissionId: string, optionId: string, sessionGeneration: string): 'accepted' | 'stale';
@@ -76,6 +106,8 @@ export declare class CodexAppServerSession implements AgentSession {
76
106
  setControllerAttached(attached: boolean): void;
77
107
  exitResult(): ExitRecord | null;
78
108
  close(): Promise<void>;
109
+ taskNoticeBinding(): TaskNoticeBinding | undefined;
110
+ recordTaskCreated(binding: TaskNoticeBinding, notice: TaskCreatedNotice): void;
79
111
  conversationPage(request?: {
80
112
  after?: string;
81
113
  limit?: number;
@@ -1,8 +1,11 @@
1
+ import { appendTaskCreated } from './task-notice.js';
1
2
  import { createHash, randomUUID } from 'node:crypto';
2
3
  import { existsSync, readFileSync, writeFileSync } from 'node:fs';
3
4
  import { isAbsolute, join, resolve } from 'node:path';
5
+ import { boundedJson } from './conversation-normalizer.js';
4
6
  import { ConversationEventStore } from './conversation-store.js';
5
7
  import { SessionEvents } from './events.js';
8
+ import { memberAuthority, memberBindings, pendingInventory, takeBatch, } from './prompt-batch.js';
6
9
  import { CodexAppServerTransport } from './codex-app-server-transport.js';
7
10
  import { CODEX_APP_SERVER_CANCEL_DEADLINE_EXCEEDED, SessionControlError, sessionBackendCapabilities, turnResult, } from './types.js';
8
11
  const PERMISSION_TIMEOUT_MS = 10 * 60_000;
@@ -12,7 +15,6 @@ const INTERRUPT_TERMINATE_GRACE_MS = 5_000;
12
15
  const TERMINAL_RECONCILE_QUIET_MS = 2_000;
13
16
  const TERMINAL_RECONCILE_REQUEST_TIMEOUT_MS = 5_000;
14
17
  const SCHEDULED_REDACTION = '[scheduled-loop content redacted]';
15
- const COMMENTARY_REDACTION = '[assistant commentary redacted]';
16
18
  const OWNER_ADMIN_CONTEXT_KEY = 'ours-fleet://prompt-provenance?source=owner_admin_console';
17
19
  const OWNER_CHANNEL_CONTEXT_KEY = 'ours-fleet://prompt-provenance?source=owner_channel';
18
20
  const isObject = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
@@ -74,6 +76,31 @@ export function nativePromptAdditionalContext(origin) {
74
76
  };
75
77
  return undefined;
76
78
  }
79
+ const PROMPT_BATCH_CONTEXT_KEY = 'ours-fleet://prompt-batch';
80
+ /**
81
+ * Application context for a turn that carries several admitted prompts.
82
+ *
83
+ * The single-prompt context says "the accompanying user text", which would
84
+ * cover every member. A shared turn therefore carries this one entry instead:
85
+ * each member is its own user text item, in order, and its route and exact
86
+ * bytes are stated here by the server. Text imitating this wording is still
87
+ * only user text; it cannot add an application-context entry.
88
+ */
89
+ export function nativeBatchAdditionalContext(members) {
90
+ if (members.length === 1)
91
+ return nativePromptAdditionalContext(members[0].origin);
92
+ const bindings = memberBindings(members, origin => sourceFor(origin).source);
93
+ return {
94
+ [PROMPT_BATCH_CONTEXT_KEY]: {
95
+ kind: 'application',
96
+ value: `Fleet queued prompt batch: ${members.length} separately admitted prompts in submission order, `
97
+ + 'one user text item each. Server-generated provenance per item, describing only that item: '
98
+ + bindings.map(binding => `item ${binding.index} of ${binding.of} `
99
+ + `(source=${binding.source}, bytes=${binding.bytes}, sha256=${binding.sha256}): `
100
+ + `${memberAuthority(binding.source)}`).join('; ') + '.',
101
+ },
102
+ };
103
+ }
77
104
  function toolItem(item) {
78
105
  switch (item.type) {
79
106
  case 'commandExecution': return {
@@ -122,6 +149,10 @@ export class CodexAppServerSession {
122
149
  exit = null;
123
150
  queueDepth = 0;
124
151
  promptTail = Promise.resolve();
152
+ /** Admitted prompts not yet handed to the app-server, in admission order. */
153
+ pendingPrompts = [];
154
+ /** Admitted prompt → the turn that carries it, while that turn is open. */
155
+ sharedTurns = new Map();
125
156
  activeTurn;
126
157
  controllerCount = 0;
127
158
  controllerGrace;
@@ -172,6 +203,9 @@ export class CodexAppServerSession {
172
203
  alive: this.isAlive(),
173
204
  readiness: this.readiness,
174
205
  sessionId: this.threadId,
206
+ // A shared turn is a human console turn if any member is: inbox traffic must not cut it short.
207
+ ...(this.activeTurn?.origin ? { activePromptSource: this.activeTurn.console
208
+ ? 'owner-admin-console' : this.activeTurn.origin.kind } : {}),
175
209
  lastError: this.lastError,
176
210
  pendingPermissionId: this.pendingPermissions.keys().next().value,
177
211
  runtimeModel: this.runtimeModel,
@@ -202,8 +236,7 @@ export class CodexAppServerSession {
202
236
  const queuedBehind = this.queueDepth;
203
237
  this.admit(promptId, text, queuedBehind, options);
204
238
  this.queueDepth++;
205
- const run = this.promptTail.then(() => this.runPrompt(text, promptId, options.origin));
206
- this.promptTail = run.then(() => undefined, () => undefined);
239
+ const run = this.enqueue(promptId, text, options.origin);
207
240
  const completion = run.then(result => {
208
241
  this.queueDepth = Math.max(0, this.queueDepth - 1);
209
242
  return result;
@@ -216,6 +249,42 @@ export class CodexAppServerSession {
216
249
  delivery: delivery ?? (queuedBehind ? 'queued' : 'started'),
217
250
  };
218
251
  }
252
+ /** The turn whose events and result answer this admitted prompt; itself unless a release shared it. */
253
+ sharedTurnId(promptId) {
254
+ return this.sharedTurns.get(promptId);
255
+ }
256
+ /** Each prompt keeps its own step in the chain; the step it reaches releases a whole snapshot. */
257
+ enqueue(promptId, text, origin) {
258
+ const entry = {
259
+ promptId, text, origin, ...sourceFor(origin), admittedAt: new Date().toISOString(),
260
+ // The one text the native session runs as an operation instead of a turn.
261
+ command: text.trim() === '/compact',
262
+ };
263
+ this.pendingPrompts.push(entry);
264
+ const run = this.promptTail.then(() => this.release(entry));
265
+ this.promptTail = run.then(() => undefined, () => undefined);
266
+ return run;
267
+ }
268
+ /**
269
+ * The turn boundary: the first waiting prompt to get here synchronously
270
+ * takes every pending prompt delivered with it into one turn. A prompt
271
+ * admitted afterwards stays pending for the next boundary, and a step whose
272
+ * prompt was already taken reports that turn's result.
273
+ */
274
+ release(entry) {
275
+ if (entry.released)
276
+ return entry.released;
277
+ const members = takeBatch(this.pendingPrompts, entry);
278
+ for (const member of members)
279
+ this.sharedTurns.set(member.promptId, members[0].promptId);
280
+ const run = this.runPrompt(members).finally(() => {
281
+ for (const member of members)
282
+ this.sharedTurns.delete(member.promptId);
283
+ });
284
+ for (const member of members)
285
+ member.released = run;
286
+ return run;
287
+ }
219
288
  async submitPrompt(text, options = {}) {
220
289
  try {
221
290
  return await (await this.queuePrompt(text, options)).completion;
@@ -226,10 +295,20 @@ export class CodexAppServerSession {
226
295
  throw error;
227
296
  }
228
297
  }
298
+ liveVoiceSupported = true;
299
+ async interruptPrompt(generation, promptId) {
300
+ if (generation !== this.sessionGeneration)
301
+ throw new Error('session_changed: Agent session changed.');
302
+ if (!this.activeTurn?.promptIds.includes(promptId))
303
+ return { state: 'settled' };
304
+ return this.interrupt('local-console');
305
+ }
229
306
  async interrupt(source = 'local-console') {
230
307
  const active = this.activeTurn;
231
- if (!active || !this.threadId || !active.nativeTurnId)
308
+ if (!active || !this.threadId)
232
309
  return { state: 'settled' };
310
+ if (!active.nativeTurnId)
311
+ throw new SessionControlError('backend', 'The operation is still pending without a native turn ID. Cancellation is not confirmed; inspect or restart the agent if it does not progress.');
233
312
  active.cancellationSource ??= source;
234
313
  this.events.emit('state', { turnId: active.promptId, status: 'running', origin: active.origin });
235
314
  this.conversation.appendSafe({
@@ -335,6 +414,10 @@ export class CodexAppServerSession {
335
414
  this.conversation.close();
336
415
  await this.transport.close();
337
416
  }
417
+ taskNoticeBinding() {
418
+ return this.activeTurn && this.threadId ? { sessionGeneration: this.sessionGeneration, acpSessionId: this.threadId, promptId: this.activeTurn.promptId } : undefined;
419
+ }
420
+ recordTaskCreated(binding, notice) { appendTaskCreated(this.conversation, binding, notice); }
338
421
  conversationPage(request = {}) {
339
422
  const floor = Number(this.conversationStartCursor ?? 0);
340
423
  const requested = Number(request.after ?? 0);
@@ -354,6 +437,7 @@ export class CodexAppServerSession {
354
437
  sessionGeneration: this.sessionGeneration,
355
438
  readiness: this.isAlive() ? this.readiness : 'offline',
356
439
  queueDepth: this.queueDepth,
440
+ ...pendingInventory(this.pendingPrompts),
357
441
  pendingPermissionIds: [...this.pendingPermissions.keys()],
358
442
  ...(this.conversation.degraded ? { historyDegraded: true } : {}),
359
443
  };
@@ -367,6 +451,10 @@ export class CodexAppServerSession {
367
451
  const existing = this.conversation.receiptFor(command.commandId, bodyDigest);
368
452
  if (existing)
369
453
  return existing;
454
+ if (command.expectedSessionGeneration !== undefined && command.expectedSessionGeneration !== this.conversationSnapshot().sessionGeneration)
455
+ throw new Error('session_changed: Agent session changed. Record a new voice message.');
456
+ if (command.requireIdle && (this.queueDepth > 0 || this.conversationSnapshot().readiness !== 'idle'))
457
+ throw new Error('voice_busy: Agent is already working. Wait for the current turn.');
370
458
  const queued = await this.queuePrompt(command.text, {
371
459
  origin: { kind: 'owner-admin-console', commandId: command.commandId },
372
460
  actor: { browserSession: command.actorBrowserSession },
@@ -387,6 +475,8 @@ export class CodexAppServerSession {
387
475
  this.transport.notify('initialized');
388
476
  const persisted = this.options.mode === 'resume' && existsSync(this.threadFile)
389
477
  ? readFileSync(this.threadFile, 'utf8').trim() : '';
478
+ if (this.options.requireResume && !persisted)
479
+ throw new Error('TASK_CONTEXT_RESUME_UNAVAILABLE');
390
480
  const common = {
391
481
  cwd: this.options.cwd,
392
482
  approvalPolicy: this.options.approvalPolicy,
@@ -406,6 +496,8 @@ export class CodexAppServerSession {
406
496
  this.threadId = string(thread?.id);
407
497
  if (!this.threadId)
408
498
  throw new Error('Codex app-server did not return a thread id');
499
+ if (this.options.requireResume && this.threadId !== persisted)
500
+ throw new Error('TASK_CONTEXT_RESUME_MISMATCH');
409
501
  writeFileSync(this.threadFile, this.threadId + '\n', { mode: 0o600 });
410
502
  const model = string(response.model) ?? this.options.model ?? undefined;
411
503
  if (model)
@@ -415,6 +507,10 @@ export class CodexAppServerSession {
415
507
  this.reasoningEffort = { value: effort };
416
508
  this.readiness = 'idle';
417
509
  this.events.emit('state', { status: 'idle', text: `Codex thread ${this.threadId}` });
510
+ this.conversation.appendSafe({
511
+ kind: 'capabilities.updated', sessionGeneration: this.sessionGeneration,
512
+ payload: { commands: [{ name: 'compact', description: textBlock('Compact session context') }] },
513
+ });
418
514
  this.conversation.appendSafe({
419
515
  kind: 'session.state', sessionGeneration: this.sessionGeneration,
420
516
  payload: { status: 'idle' },
@@ -437,8 +533,7 @@ export class CodexAppServerSession {
437
533
  const origin = open.commandId
438
534
  ? { kind: 'owner-admin-console', commandId: open.commandId }
439
535
  : undefined;
440
- const run = this.promptTail.then(() => this.runPrompt(open.text, open.promptId, origin));
441
- this.promptTail = run.then(() => undefined, () => undefined);
536
+ const run = this.enqueue(open.promptId, open.text, origin);
442
537
  void run.finally(() => { this.queueDepth = Math.max(0, this.queueDepth - 1); });
443
538
  }
444
539
  }
@@ -470,30 +565,43 @@ export class CodexAppServerSession {
470
565
  else
471
566
  this.conversation.appendSafe(draft);
472
567
  }
473
- async runPrompt(text, promptId, origin) {
568
+ async runPrompt(members) {
569
+ const { text, promptId, origin } = members[0];
474
570
  if (!this.threadId || !this.transport.isAlive())
475
571
  return turnResult(false, 'failed', this.lastError ?? 'Codex app-server session is offline');
476
572
  let settle;
477
573
  const settled = new Promise(resolveResult => { settle = resolveResult; });
478
- const active = { promptId, origin, output: '', settled, settle };
574
+ const active = {
575
+ promptId, origin, output: '', settled, settle,
576
+ promptIds: members.map(member => member.promptId),
577
+ console: members.some(member => member.origin?.kind === 'owner-admin-console'),
578
+ };
479
579
  this.activeTurn = active;
480
580
  this.finalAnswerObserved = false;
481
581
  this.activeItems.clear();
482
582
  this.readiness = 'running';
483
583
  this.events.emit('state', { turnId: promptId, status: 'running', origin });
484
- this.conversation.appendSafe({
584
+ members.forEach((member, index) => this.conversation.appendSafe({
485
585
  kind: 'prompt.started', sessionGeneration: this.sessionGeneration,
486
- promptId, turnId: promptId, source: sourceFor(origin).source, payload: {},
487
- });
586
+ promptId: member.promptId, turnId: promptId, source: sourceFor(member.origin).source,
587
+ payload: members.length > 1 ? { batch: { turnId: promptId, index, size: members.length } } : {},
588
+ }));
488
589
  try {
590
+ if (text.trim() === '/compact') {
591
+ // The acknowledgement is not completion. Standard turn notifications
592
+ // settle the same queued prompt and durable browser receipt.
593
+ await this.transport.request('thread/compact/start', { threadId: this.threadId });
594
+ return await settled;
595
+ }
489
596
  const response = await this.transport.request('turn/start', {
490
597
  threadId: this.threadId,
491
- input: [{ type: 'text', text, text_elements: [] }],
598
+ // One unchanged user text item per admitted prompt, in admission order.
599
+ input: members.map(member => ({ type: 'text', text: member.text, text_elements: [] })),
492
600
  approvalPolicy: this.options.approvalPolicy,
493
601
  model: this.options.model ?? null,
494
602
  effort: this.options.effort ?? null,
495
- ...(nativePromptAdditionalContext(origin)
496
- ? { additionalContext: nativePromptAdditionalContext(origin) } : {}),
603
+ ...(nativeBatchAdditionalContext(members)
604
+ ? { additionalContext: nativeBatchAdditionalContext(members) } : {}),
497
605
  });
498
606
  const turn = isObject(response.turn) ? response.turn : undefined;
499
607
  active.nativeTurnId ??= string(turn?.id);
@@ -699,7 +807,7 @@ export class CodexAppServerSession {
699
807
  promptId: this.activeTurn?.promptId, turnId: this.activeTurn?.promptId,
700
808
  messageId: id, source: 'agent',
701
809
  payload: {
702
- role: 'assistant', content: textBlock(delta, scheduled ? SCHEDULED_REDACTION : phase === 'commentary' ? COMMENTARY_REDACTION : undefined),
810
+ role: 'assistant', content: textBlock(delta, scheduled ? SCHEDULED_REDACTION : undefined),
703
811
  },
704
812
  });
705
813
  }
@@ -746,6 +854,10 @@ export class CodexAppServerSession {
746
854
  toolCallId: scheduled ? 'scheduled-loop-tool' : id, snapshot,
747
855
  title: scheduled ? 'scheduled-loop tool' : tool.title, status: tool.status,
748
856
  kind: string(item.type),
857
+ ...(!scheduled && item.arguments !== undefined ? { rawInput: boundedJson(item.arguments) } : {}),
858
+ ...(!scheduled && item.type === 'commandExecution' ? { rawInput: boundedJson({ command: item.command, cwd: item.cwd }), ...(item.aggregatedOutput !== undefined ? { rawOutput: boundedJson({ output: item.aggregatedOutput, exitCode: item.exitCode }) } : {}) } : {}),
859
+ ...(!scheduled && item.type === 'fileChange' && item.changes !== undefined ? { rawOutput: boundedJson(item.changes) } : {}),
860
+ ...(!scheduled && (item.result !== undefined || item.error !== undefined) ? { rawOutput: boundedJson({ result: item.result, error: item.error }) } : {}),
749
861
  },
750
862
  });
751
863
  }
@@ -768,14 +880,16 @@ export class CodexAppServerSession {
768
880
  cancellationSource: active.cancellationSource,
769
881
  });
770
882
  this.events.emit('state', { status: this.readiness });
771
- this.conversation.appendSafe({
772
- kind: 'turn.completed', sessionGeneration: this.sessionGeneration,
773
- promptId: active.promptId, turnId: active.promptId,
774
- payload: {
775
- outcome, ...(detail ? { stopReason: detail } : {}),
776
- ...(active.cancellationSource ? { cancellationSource: active.cancellationSource } : {}),
777
- },
778
- });
883
+ // Every member closes with the shared outcome, so none is left waiting.
884
+ for (const member of active.promptIds)
885
+ this.conversation.appendSafe({
886
+ kind: 'turn.completed', sessionGeneration: this.sessionGeneration,
887
+ promptId: member, turnId: active.promptId,
888
+ payload: {
889
+ outcome, ...(detail ? { stopReason: detail } : {}),
890
+ ...(active.cancellationSource ? { cancellationSource: active.cancellationSource } : {}),
891
+ },
892
+ });
779
893
  active.settle(result);
780
894
  }
781
895
  /**
@@ -1,3 +1,5 @@
1
+ import type { LayoutControlRequest } from '../rooms-tasks/layout-control.js';
2
+ import type { SupervisorCorrespondence, AgentHistoryQuery } from '../agent-ours/correspondence.js';
1
3
  import { type Socket } from 'node:net';
2
4
  import type { AgentSession, ControlFailureKind, SessionEvent, SessionSnapshot } from './types.js';
3
5
  import type { OwnerChannelHandle, OwnerChannelManagementRequest } from '../owner-channel/channel.js';
@@ -9,8 +11,13 @@ export interface ControlRequest {
9
11
  version: 1 | 2 | 3;
10
12
  id: string;
11
13
  token: string;
12
- command: 'status' | 'snapshot' | 'submit_prompt' | 'respond_permission' | 'interrupt' | 'follow' | 'events_since' | 'owner_channel_manage' | 'loop_status' | 'loop_run_now' | 'loop_disable' | 'loop_enable' | 'reload_config' | 'conversation_page' | 'conversation_follow' | 'submit_prompt_v2' | 'interrupt_v2' | 'respond_permission_v2' | 'fleet_spawn' | 'fleet_audit_begin' | 'fleet_audit_present' | 'fleet_audit_finish';
14
+ command: 'status' | 'snapshot' | 'submit_prompt' | 'respond_permission' | 'interrupt' | 'follow' | 'events_since' | 'owner_channel_manage' | 'loop_status' | 'loop_run_now' | 'loop_disable' | 'loop_enable' | 'reload_config' | 'conversation_page' | 'conversation_follow' | 'submit_prompt_v2' | 'submit_voice_prompt' | 'interrupt_v2' | 'interrupt_voice_v2' | 'layout_control' | 'agent_contacts' | 'agent_history' | 'respond_permission_v2' | 'fleet_spawn' | 'fleet_audit_begin' | 'fleet_audit_present' | 'fleet_audit_finish';
15
+ layout?: LayoutControlRequest;
16
+ agentHistory?: AgentHistoryQuery;
13
17
  text?: string;
18
+ expectedSessionGeneration?: string;
19
+ requireIdle?: boolean;
20
+ promptId?: string;
14
21
  permissionId?: string;
15
22
  optionId?: string;
16
23
  since?: number;
@@ -112,9 +119,13 @@ export declare class RoleControlServer {
112
119
  private readonly sockets;
113
120
  /** Interrupt idempotency: same command id returns the first receipt. */
114
121
  private readonly interruptCommands;
122
+ private correspondence?;
123
+ setCorrespondence(value: SupervisorCorrespondence): void;
115
124
  private ownerChannel?;
116
125
  private loopManager?;
117
126
  private reloadConfig?;
127
+ private layoutControl?;
128
+ setLayoutControl(handler: (request: LayoutControlRequest) => Promise<unknown>): void;
118
129
  private fleetSpawner?;
119
130
  private fleetAuditor?;
120
131
  constructor(stateDir: string, session: AgentSession, log: (line: string) => void);
@@ -1,3 +1,4 @@
1
+ import { socketPath as privateSocketPath } from '../socket-path.js';
1
2
  import { randomBytes, randomUUID, timingSafeEqual } from 'node:crypto';
2
3
  import { chmodSync, existsSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
3
4
  import { createConnection, createServer } from 'node:net';
@@ -8,7 +9,7 @@ import { interruptSession, queueSessionPrompt, respondSessionPermission, respond
8
9
  const MAX_LINE_BYTES = 64 * 1024;
9
10
  /** Commands that require protocol version 3. */
10
11
  const V3_COMMANDS = new Set([
11
- 'conversation_page', 'conversation_follow', 'submit_prompt_v2', 'interrupt_v2',
12
+ 'conversation_page', 'conversation_follow', 'submit_prompt_v2', 'submit_voice_prompt', 'interrupt_v2', 'interrupt_voice_v2',
12
13
  'respond_permission_v2',
13
14
  ]);
14
15
  /** The one retained-range projection shared by polling and live-follow admission. */
@@ -99,7 +100,7 @@ export function oversightTaxonomy(name = '<Name>') {
99
100
  export function oversightTaxonomyLines(name = '<Name>') {
100
101
  return oversightTaxonomy(name).map(r => `- **${r.result}** — ${r.meaning} → ${r.action}`);
101
102
  }
102
- export const controlSocketPath = (stateDir) => join(stateDir, '.control.sock');
103
+ export const controlSocketPath = (stateDir) => privateSocketPath(join(stateDir, '.control.sock'));
103
104
  export const controlTokenPath = (stateDir) => join(stateDir, '.control-token');
104
105
  function sameToken(actual, supplied) {
105
106
  const a = Buffer.from(actual);
@@ -116,9 +117,13 @@ export class RoleControlServer {
116
117
  sockets = new Set();
117
118
  /** Interrupt idempotency: same command id returns the first receipt. */
118
119
  interruptCommands = new Map();
120
+ correspondence;
121
+ setCorrespondence(value) { this.correspondence = value; }
119
122
  ownerChannel;
120
123
  loopManager;
121
124
  reloadConfig;
125
+ layoutControl;
126
+ setLayoutControl(handler) { this.layoutControl = handler; }
122
127
  fleetSpawner;
123
128
  fleetAuditor;
124
129
  constructor(stateDir, session, log) {
@@ -235,7 +240,10 @@ export class RoleControlServer {
235
240
  protocolVersion: this.session.conversationPage ? 3 : 2,
236
241
  features: [
237
242
  'events_since', 'observer_follow', 'retained_range',
243
+ ...(this.correspondence ? ['agent_correspondence'] : []),
238
244
  ...(this.session.conversationPage ? ['conversation_v3'] : []),
245
+ ...(this.session.submitPromptBrowser ? ['generation_bound_prompts'] : []),
246
+ ...(this.session.liveVoiceSupported ? ['idle_bound_prompts', 'targeted_voice_interrupt'] : []),
239
247
  ...(this.session.capabilities?.steering ? ['steering'] : []),
240
248
  ...(this.session.capabilities?.permissions ? ['permissions'] : []),
241
249
  ...(this.session.capabilities?.messagePhases ? ['message_phases'] : []),
@@ -308,6 +316,13 @@ export class RoleControlServer {
308
316
  });
309
317
  return;
310
318
  }
319
+ case 'layout_control': {
320
+ if (request.version !== 2 || !request.layout || !this.layoutControl)
321
+ throw new SessionControlError('rejected', 'layout control unavailable');
322
+ const result = await this.layoutControl(request.layout);
323
+ this.write(socket, { version: 1, id: request.id, ok: true, result });
324
+ return;
325
+ }
311
326
  case 'fleet_spawn': {
312
327
  if (request.version !== 2 || !request.spawn || typeof request.spawn.name !== 'string')
313
328
  throw new SessionControlError('rejected', 'version 2 and typed spawn options are required');
@@ -362,6 +377,23 @@ export class RoleControlServer {
362
377
  this.write(socket, { version: 1, id: request.id, ok: true, result: { delivered: true } });
363
378
  return;
364
379
  }
380
+ case 'agent_contacts':
381
+ case 'agent_history': {
382
+ if (!this.correspondence)
383
+ throw new SessionControlError('rejected', 'Supervisor correspondence API is unavailable; this session needs an updated supervisor.');
384
+ const query = request.agentHistory;
385
+ if (request.command === 'agent_history' && (!query || !/^[a-f0-9]{64}$/i.test(query.peer_cid) || (query.limit !== undefined && (!Number.isInteger(query.limit) || query.limit < 1 || query.limit > 200)) || (query.before_seq !== undefined && (!Number.isSafeInteger(query.before_seq) || query.before_seq < 1))))
386
+ throw new SessionControlError('rejected', 'Valid peer_cid, limit (1–200) and before_seq are required.');
387
+ let result;
388
+ try {
389
+ result = request.command === 'agent_contacts' ? await this.correspondence.contacts() : await this.correspondence.history(query);
390
+ }
391
+ catch {
392
+ throw new SessionControlError('rejected', 'Supervisor correspondence read failed; identity or transport is unavailable.');
393
+ }
394
+ this.write(socket, { version: 1, id: request.id, ok: true, result });
395
+ return;
396
+ }
365
397
  case 'owner_channel_manage': {
366
398
  if (!request.ownerChannel || typeof request.ownerChannel.action !== 'string')
367
399
  throw new SessionControlError('rejected', 'owner-channel management action is required');
@@ -421,7 +453,10 @@ export class RoleControlServer {
421
453
  socket.write(JSON.stringify({ version: 1, conversationEvent: event }) + '\n');
422
454
  }) };
423
455
  }
456
+ case 'submit_voice_prompt':
424
457
  case 'submit_prompt_v2': {
458
+ if (request.command === 'submit_voice_prompt' && !request.expectedSessionGeneration?.trim())
459
+ throw new SessionControlError('rejected', 'expectedSessionGeneration is required');
425
460
  this.requireConversation(request);
426
461
  if (!request.commandId?.trim() || !request.text?.trim() || !request.actor?.trim()
427
462
  || request.source !== 'owner_admin_console')
@@ -432,6 +467,7 @@ export class RoleControlServer {
432
467
  const receipt = await this.session.submitPromptBrowser({
433
468
  commandId: request.commandId, text: request.text,
434
469
  source: 'owner_admin_console', actorBrowserSession: request.actor,
470
+ expectedSessionGeneration: request.expectedSessionGeneration, requireIdle: request.requireIdle === true,
435
471
  });
436
472
  this.write(socket, { version: 1, id: request.id, ok: true, result: receipt });
437
473
  }
@@ -442,6 +478,29 @@ export class RoleControlServer {
442
478
  }
443
479
  return;
444
480
  }
481
+ case 'interrupt_voice_v2': {
482
+ this.requireConversation(request);
483
+ if (!request.commandId?.trim() || !request.expectedSessionGeneration?.trim() || !request.promptId?.trim())
484
+ throw new SessionControlError('rejected', 'commandId, expectedSessionGeneration and promptId are required');
485
+ if (!this.session.interruptPrompt)
486
+ throw new SessionControlError('rejected', 'targeted voice interrupt unavailable');
487
+ const key = 'voice:' + request.commandId;
488
+ const signature = JSON.stringify([request.expectedSessionGeneration, request.promptId]);
489
+ const existing = this.interruptCommands.get(key);
490
+ if (existing) {
491
+ if (existing.signature !== signature)
492
+ throw new SessionControlError('rejected', 'idempotency_conflict: interrupt target changed');
493
+ this.write(socket, { version: 1, id: request.id, ok: true, result: existing.receipt });
494
+ return;
495
+ }
496
+ const outcome = await this.session.interruptPrompt(request.expectedSessionGeneration, request.promptId);
497
+ const receipt = { accepted: true, commandId: request.commandId, ...outcome };
498
+ this.interruptCommands.set(key, { signature, receipt });
499
+ if (this.interruptCommands.size > 200)
500
+ this.interruptCommands.delete(this.interruptCommands.keys().next().value);
501
+ this.write(socket, { version: 1, id: request.id, ok: true, result: receipt });
502
+ return;
503
+ }
445
504
  case 'interrupt_v2': {
446
505
  this.requireConversation(request);
447
506
  if (!request.commandId?.trim())
@@ -532,7 +591,13 @@ export async function controlRequest(stateDir, request, timeoutMs = 120_000) {
532
591
  socket.once('error', error => {
533
592
  clearTimeout(timer);
534
593
  const code = error.code;
535
- reject(new SessionControlError(code === 'ENOENT' || code === 'ECONNREFUSED' ? 'control-unavailable' : 'backend', `role control socket: ${error.message}`));
594
+ const permissionHint = code === 'EPERM' || code === 'EACCES'
595
+ ? `; the operating system denied access before '${request.command}' reached the supervisor. `
596
+ + 'Check socket permissions and the command execution sandbox. '
597
+ + 'An existing socket does not prove this process can connect; do not bypass Fleet audit. '
598
+ + 'For managed workspace sessions, see docs/validation/managed-cli-permissions.md.'
599
+ : '';
600
+ reject(new SessionControlError(code === 'ENOENT' || code === 'ECONNREFUSED' ? 'control-unavailable' : 'backend', `role control socket: ${error.message}${permissionHint}`));
536
601
  });
537
602
  socket.on('data', chunk => {
538
603
  buffer += chunk;