@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
@@ -1,8 +1,11 @@
1
+ import type { FileDeliveryInput, CopyChatFile, DeliveredFile } from '../file-delivery/types.js';
2
+ import { type TaskNoticeBinding, type TaskCreatedNotice } from './task-notice.js';
1
3
  import * as acp from '@agentclientprotocol/sdk';
2
4
  import type { CommonPermissions } from '../config.js';
3
5
  import type { AcpMcpServer } from '../harness/types.js';
4
6
  import { ConversationEventStore } from './conversation-store.js';
5
7
  import type { ConversationSnapshot, PromptOrigin, PromptReceipt, SubmitPromptCommand } from './conversation-types.js';
8
+ import { type PendingPrompt } from './prompt-batch.js';
6
9
  import type { ConversationHandlePage, ExitRecord, InterruptOutcome, QueuedPrompt, SessionEvent, AgentSession, RuntimeSelectorMetadata, SessionSnapshot, SubmitPromptOptions, TurnCancellationSource, TurnOutcome, TurnResult } from './types.js';
7
10
  /** Bound safe-boundary waiting without turning a hung tool into cancellation. */
8
11
  export declare const AFTER_TOOL_BOUNDARY_TIMEOUT_MS = 120000;
@@ -25,6 +28,17 @@ export declare const STEERING_OCCUPANCY_IDLE_MS = 150000;
25
28
  export declare const CODEX_DISABLE_INHERITED_MCP_ENV = "OURS_FLEET_CODEX_DISABLE_INHERITED_MCP";
26
29
  /** Server-generated typed provenance followed by the exact human-authored body. */
27
30
  export declare function promptContentBlocks(text: string, origin?: PromptOrigin): acp.ContentBlock[];
31
+ /**
32
+ * One adapter turn that carries several admitted prompts.
33
+ *
34
+ * A single prompt's provenance block describes "the accompanying text", which
35
+ * is only unambiguous when there is one text. Here every member is one text
36
+ * block, unchanged, immediately preceded by its own server-generated block
37
+ * that names its route and binds its exact bytes — so a monitor wake next to
38
+ * an owner prompt is never read under the owner's authority, and text that
39
+ * imitates a marker stays text: it cannot produce a typed block.
40
+ */
41
+ export declare function batchContentBlocks(members: ReadonlyArray<Pick<PendingPrompt, 'text' | 'origin'>>): acp.ContentBlock[];
28
42
  export declare function runtimeSelector(options: acp.SessionConfigOption[] | null | undefined, category: string): RuntimeSelectorMetadata | undefined;
29
43
  /** Fresh ACP response including the legacy model report still used by some adapters. */
30
44
  export type AcpStartupSessionResponse = acp.NewSessionResponse & {
@@ -49,6 +63,7 @@ export interface AcpSessionOptions {
49
63
  inheritEnvironment?: boolean;
50
64
  stateDir: string;
51
65
  mode: 'fresh' | 'resume';
66
+ requireResume?: boolean;
52
67
  permissions: CommonPermissions;
53
68
  /** Native permission-mode id to request via session/set_mode; undefined keeps the agent default. */
54
69
  modeId?: string;
@@ -136,6 +151,12 @@ export declare class AcpSession implements AgentSession {
136
151
  private lastUpdateAt?;
137
152
  private lastError?;
138
153
  private promptTail;
154
+ /** Admitted prompts not yet handed to the adapter, in admission order. */
155
+ private readonly pendingPrompts;
156
+ /** Admitted prompt → the adapter turn that carries it, while that turn is open. */
157
+ private readonly sharedTurns;
158
+ /** Commands the adapter currently advertises; only these are control operations. */
159
+ private advertisedCommands;
139
160
  private queueDepth;
140
161
  private exit;
141
162
  private steeringSupported;
@@ -175,6 +196,7 @@ export declare class AcpSession implements AgentSession {
175
196
  private stallTimer?;
176
197
  private stallAttempt?;
177
198
  private readonly toolBoundaryWaiters;
199
+ private fileExportBusy;
178
200
  private activeTurn?;
179
201
  private constructor();
180
202
  static start(options: AcpSessionOptions): Promise<AcpSession>;
@@ -232,6 +254,22 @@ export declare class AcpSession implements AgentSession {
232
254
  * for it is what turned a busy agent into a timeout and then into "dead".
233
255
  */
234
256
  queuePrompt(text: string, options?: SubmitPromptOptions): Promise<QueuedPrompt>;
257
+ /** The turn whose events and result answer this admitted prompt; itself unless a release shared it. */
258
+ sharedTurnId(promptId: string): string | undefined;
259
+ /**
260
+ * Queue an admitted prompt behind the current work. Each prompt keeps its
261
+ * own step in the chain, so its place in line is unchanged; what the step
262
+ * does when it is reached is release a whole snapshot, not one prompt.
263
+ */
264
+ private enqueue;
265
+ /**
266
+ * The turn boundary. The first waiting prompt to get here takes every
267
+ * pending prompt delivered with it, synchronously, and they share one adapter
268
+ * turn. A prompt admitted after that — while the turn runs, or while a
269
+ * cancellation settles — stays pending for the next boundary. A step whose
270
+ * prompt an earlier boundary already took just reports that turn's result.
271
+ */
272
+ private release;
235
273
  /**
236
274
  * Prepare the session for a prompt that asked to pre-empt current work.
237
275
  *
@@ -271,6 +309,8 @@ export declare class AcpSession implements AgentSession {
271
309
  * (`queuePrompt({ interrupt: true })`) needs the typed error, because only it
272
310
  * still owes an undelivered message a replay.
273
311
  */
312
+ readonly liveVoiceSupported = true;
313
+ interruptPrompt(generation: string, promptId: string): Promise<InterruptOutcome>;
274
314
  interrupt(source?: TurnCancellationSource): Promise<InterruptOutcome>;
275
315
  private cancelActive;
276
316
  /**
@@ -300,6 +340,8 @@ export declare class AcpSession implements AgentSession {
300
340
  */
301
341
  private settlePendingAutomatically;
302
342
  exitResult(): ExitRecord | null;
343
+ /** One attempt, tied to the managed live turn captured before any approval/read. */
344
+ sendFileToChat(input: FileDeliveryInput, signal: AbortSignal, copy: CopyChatFile, policy: CommonPermissions): Promise<DeliveredFile>;
303
345
  close(): Promise<void>;
304
346
  /** ACP requires the field. Bundled agents treat [] as no client-added servers. */
305
347
  private declaredMcpServers;
@@ -311,6 +353,11 @@ export declare class AcpSession implements AgentSession {
311
353
  private recoverStall;
312
354
  /** Keep the original queue slot (including startup) until recovery finishes. */
313
355
  private runPrompt;
356
+ /**
357
+ * One adapter turn. `members` are the admitted prompts it carries, the first
358
+ * being the turn's own id; each keeps its own started and completed records
359
+ * so none is left open and none is reported delivered that was not sent.
360
+ */
314
361
  private runSinglePrompt;
315
362
  private steerPrompt;
316
363
  private requestPermission;
@@ -338,8 +385,13 @@ export declare class AcpSession implements AgentSession {
338
385
  * signal. Never infer commentary from text, message order, or unknown meta.
339
386
  */
340
387
  private codexMessagePhase;
341
- /** Normalize every ACP update losslessly into the durable ledger. */
388
+ /**
389
+ * Normalize every ACP update losslessly into the durable ledger. The ledger is the conversation the
390
+ * owner reads, so the agent's commentary between its tools is kept as written, like its final answer.
391
+ */
342
392
  private recordConversationUpdate;
393
+ taskNoticeBinding(): TaskNoticeBinding | undefined;
394
+ recordTaskCreated(binding: TaskNoticeBinding, notice: TaskCreatedNotice): void;
343
395
  conversationPage(request?: {
344
396
  after?: string;
345
397
  limit?: number;
@@ -1,3 +1,4 @@
1
+ import { appendTaskCreated } from './task-notice.js';
1
2
  import { spawn } from 'node:child_process';
2
3
  import { createHash, randomUUID } from 'node:crypto';
3
4
  import { existsSync, lstatSync, readFileSync, readlinkSync, realpathSync, writeFileSync, } from 'node:fs';
@@ -7,6 +8,8 @@ import * as acp from '@agentclientprotocol/sdk';
7
8
  import { normalizeSessionUpdate } from './conversation-normalizer.js';
8
9
  import { ConversationEventStore } from './conversation-store.js';
9
10
  import { SessionEvents } from './events.js';
11
+ import { commandName, memberAuthority, memberBindings, pendingInventory, takeBatch, } from './prompt-batch.js';
12
+ import { recordClaudeRateLimit } from '../subscriptions/observed.js';
10
13
  import { DEFAULT_STALL_TIMEOUT_MS, STALL_RECOVERY_PROMPT, StallWatchdog, StallToolHistory, hasStallRecoveryClaim } from './stall-watchdog.js';
11
14
  import { ACP_CANCEL_DEADLINE_EXCEEDED, SessionControlError, classifyChildExit, sessionBackendCapabilities, turnResult, } from './types.js';
12
15
  const CANCEL_SETTLE_GRACE_MS = 15_000;
@@ -36,7 +39,6 @@ const TERMINAL_TOOL_STATUSES = new Set(['completed', 'failed']);
36
39
  /** Consumed only by Fleet's authenticated bundled-Codex app-server proxy. */
37
40
  export const CODEX_DISABLE_INHERITED_MCP_ENV = 'OURS_FLEET_CODEX_DISABLE_INHERITED_MCP';
38
41
  const SCHEDULED_LOOP_REDACTION = '[scheduled-loop content redacted]';
39
- const OWNER_COMMENTARY_REDACTION = '[assistant commentary redacted]';
40
42
  const MAX_CANONICAL_SYMLINK_DEPTH = 40;
41
43
  const scheduledTurn = (turn) => turn?.origin?.kind === 'scheduled-loop';
42
44
  /**
@@ -181,6 +183,38 @@ export function promptContentBlocks(text, origin) {
181
183
  mimeType: 'application/vnd.ours-fleet.prompt-provenance+json',
182
184
  }, { type: 'text', text }];
183
185
  }
186
+ /**
187
+ * One adapter turn that carries several admitted prompts.
188
+ *
189
+ * A single prompt's provenance block describes "the accompanying text", which
190
+ * is only unambiguous when there is one text. Here every member is one text
191
+ * block, unchanged, immediately preceded by its own server-generated block
192
+ * that names its route and binds its exact bytes — so a monitor wake next to
193
+ * an owner prompt is never read under the owner's authority, and text that
194
+ * imitates a marker stays text: it cannot produce a typed block.
195
+ */
196
+ export function batchContentBlocks(members) {
197
+ if (members.length === 1)
198
+ return promptContentBlocks(members[0].text, members[0].origin);
199
+ const mimeType = 'application/vnd.ours-fleet.prompt-provenance+json';
200
+ return [{
201
+ type: 'resource_link',
202
+ uri: `ours-fleet://prompt-batch?members=${members.length}`,
203
+ name: 'Fleet queued prompt batch',
204
+ description: `Server-generated. This turn delivers ${members.length} separately admitted prompts in `
205
+ + 'submission order. Each prompt is one text block immediately preceded by its own '
206
+ + 'server-generated provenance block, which describes only that one text block.',
207
+ mimeType,
208
+ }, ...memberBindings(members, origin => conversationSource(origin).source).flatMap((binding, at) => [{
209
+ type: 'resource_link',
210
+ uri: `ours-fleet://prompt-provenance?source=${binding.source}&member=${binding.index}`
211
+ + `&of=${binding.of}&bytes=${binding.bytes}&sha256=${binding.sha256}`,
212
+ name: binding.source === 'owner_admin_console' ? 'Direct owner admin console' : 'Fleet queued prompt',
213
+ description: `Server-generated provenance for prompt ${binding.index} of ${binding.of}: `
214
+ + `${memberAuthority(binding.source)}. Applies only to the text block that immediately follows.`,
215
+ mimeType,
216
+ }, { type: 'text', text: members[at].text }])];
217
+ }
184
218
  export function runtimeSelector(options, category) {
185
219
  const option = options?.find(candidate => candidate.category === category);
186
220
  if (!option || typeof option.currentValue !== 'string')
@@ -238,6 +272,12 @@ export class AcpSession {
238
272
  lastUpdateAt;
239
273
  lastError;
240
274
  promptTail = Promise.resolve();
275
+ /** Admitted prompts not yet handed to the adapter, in admission order. */
276
+ pendingPrompts = [];
277
+ /** Admitted prompt → the adapter turn that carries it, while that turn is open. */
278
+ sharedTurns = new Map();
279
+ /** Commands the adapter currently advertises; only these are control operations. */
280
+ advertisedCommands = new Set();
241
281
  queueDepth = 0;
242
282
  exit = null;
243
283
  steeringSupported = false;
@@ -277,6 +317,7 @@ export class AcpSession {
277
317
  stallTimer;
278
318
  stallAttempt;
279
319
  toolBoundaryWaiters = new Set();
320
+ fileExportBusy = false;
280
321
  activeTurn;
281
322
  constructor(options, child, connection) {
282
323
  this.options = options;
@@ -409,8 +450,7 @@ export class AcpSession {
409
450
  const text = open.text;
410
451
  const promptId = open.promptId;
411
452
  this.queueDepth++;
412
- const run = this.promptTail.then(() => this.runPrompt(text, promptId));
413
- this.promptTail = run.then(() => undefined, () => undefined);
453
+ const run = this.enqueue(promptId, text, undefined);
414
454
  void run.finally(() => { this.queueDepth = Math.max(0, this.queueDepth - 1); });
415
455
  }
416
456
  }
@@ -465,6 +505,9 @@ export class AcpSession {
465
505
  readiness: this.readiness === 'idle' && this.steeringOccupied
466
506
  ? 'running' : this.readiness,
467
507
  sessionId: this.sessionId,
508
+ // A shared turn is a human console turn if any member is: inbox traffic must not cut it short.
509
+ ...(this.activeTurn?.origin ? { activePromptSource: this.activeTurn.console
510
+ ? 'owner-admin-console' : this.activeTurn.origin.kind } : {}),
468
511
  lastError: this.lastError,
469
512
  pendingPermissionId: this.pendingPermissions.keys().next().value,
470
513
  runtimeModel: this.runtimeModel,
@@ -579,6 +622,11 @@ export class AcpSession {
579
622
  * steering response from becoming a tight replay loop.
580
623
  */
581
624
  async steerOrQueueWake(text, options) {
625
+ // A tool-boundary wait may outlive its original turn. Recheck the current
626
+ // typed source at delivery, since an Owner turn can begin during the wait.
627
+ // `console` covers a shared turn whose first member is not the console prompt.
628
+ if (options.origin?.kind === 'fleet-monitor' && this.activeTurn?.console)
629
+ return this.submitPrompt(text, { ...options, interrupt: false, steer: false });
582
630
  if (this.stallRecoveryClaimed)
583
631
  return this.submitPrompt(text, { ...options, interrupt: false, steer: false });
584
632
  const steered = await this.steerPrompt(text);
@@ -668,8 +716,7 @@ export class AcpSession {
668
716
  const queuedBehind = this.queueDepth;
669
717
  this.admitToLedger(promptId, text, queuedBehind, options);
670
718
  this.queueDepth++;
671
- const run = this.promptTail.then(() => this.runPrompt(text, promptId, options.origin));
672
- this.promptTail = run.then(() => undefined, () => undefined);
719
+ const run = this.enqueue(promptId, text, options.origin);
673
720
  const completion = run.then(result => { this.queueDepth = Math.max(0, this.queueDepth - 1); return result; }, error => {
674
721
  this.queueDepth = Math.max(0, this.queueDepth - 1);
675
722
  return turnResult(false, 'failed', error?.message ?? String(error));
@@ -679,6 +726,46 @@ export class AcpSession {
679
726
  delivery: delivery ?? (queuedBehind > 0 ? 'queued' : 'started'),
680
727
  };
681
728
  }
729
+ /** The turn whose events and result answer this admitted prompt; itself unless a release shared it. */
730
+ sharedTurnId(promptId) {
731
+ return this.sharedTurns.get(promptId);
732
+ }
733
+ /**
734
+ * Queue an admitted prompt behind the current work. Each prompt keeps its
735
+ * own step in the chain, so its place in line is unchanged; what the step
736
+ * does when it is reached is release a whole snapshot, not one prompt.
737
+ */
738
+ enqueue(promptId, text, origin) {
739
+ const entry = {
740
+ promptId, text, origin, ...conversationSource(origin), admittedAt: new Date().toISOString(),
741
+ command: this.advertisedCommands.has(commandName(text) ?? ''),
742
+ };
743
+ this.pendingPrompts.push(entry);
744
+ const run = this.promptTail.then(() => this.release(entry));
745
+ this.promptTail = run.then(() => undefined, () => undefined);
746
+ return run;
747
+ }
748
+ /**
749
+ * The turn boundary. The first waiting prompt to get here takes every
750
+ * pending prompt delivered with it, synchronously, and they share one adapter
751
+ * turn. A prompt admitted after that — while the turn runs, or while a
752
+ * cancellation settles — stays pending for the next boundary. A step whose
753
+ * prompt an earlier boundary already took just reports that turn's result.
754
+ */
755
+ release(entry) {
756
+ if (entry.released)
757
+ return entry.released;
758
+ const members = takeBatch(this.pendingPrompts, entry);
759
+ for (const member of members)
760
+ this.sharedTurns.set(member.promptId, members[0].promptId);
761
+ const run = this.runPrompt(members).finally(() => {
762
+ for (const member of members)
763
+ this.sharedTurns.delete(member.promptId);
764
+ });
765
+ for (const member of members)
766
+ member.released = run;
767
+ return run;
768
+ }
682
769
  /**
683
770
  * Prepare the session for a prompt that asked to pre-empt current work.
684
771
  *
@@ -760,6 +847,10 @@ export class AcpSession {
760
847
  const existing = this.conversation.receiptFor(command.commandId, bodyDigest);
761
848
  if (existing)
762
849
  return existing;
850
+ if (command.expectedSessionGeneration !== undefined && command.expectedSessionGeneration !== this.conversationSnapshot().sessionGeneration)
851
+ throw new Error('session_changed: Agent session changed. Record a new voice message.');
852
+ if (command.requireIdle && (this.queueDepth > 0 || this.conversationSnapshot().readiness !== 'idle'))
853
+ throw new Error('voice_busy: Agent is already working. Wait for the current turn.');
763
854
  const queued = await this.queuePrompt(command.text, {
764
855
  origin: { kind: 'owner-admin-console', commandId: command.commandId },
765
856
  actor: { browserSession: command.actorBrowserSession },
@@ -792,6 +883,14 @@ export class AcpSession {
792
883
  * (`queuePrompt({ interrupt: true })`) needs the typed error, because only it
793
884
  * still owes an undelivered message a replay.
794
885
  */
886
+ liveVoiceSupported = true;
887
+ async interruptPrompt(generation, promptId) {
888
+ if (generation !== this.sessionGeneration)
889
+ throw new Error('session_changed: Agent session changed.');
890
+ if (!this.activeTurn?.promptIds.includes(promptId))
891
+ return { state: 'settled' };
892
+ return this.interrupt('local-console');
893
+ }
795
894
  async interrupt(source = 'local-console') {
796
895
  try {
797
896
  await this.cancelActive(source);
@@ -810,6 +909,8 @@ export class AcpSession {
810
909
  if (!this.sessionId)
811
910
  return;
812
911
  const active = this.activeTurn;
912
+ if (active)
913
+ active.fileDeliveryRevoked = true;
813
914
  const previousSource = active?.cancellationSource;
814
915
  if (active && (source === 'owner' || source === 'local-console' || !previousSource))
815
916
  active.cancellationSource = source;
@@ -1013,6 +1114,54 @@ export class AcpSession {
1013
1114
  exitResult() {
1014
1115
  return this.exit;
1015
1116
  }
1117
+ /** One attempt, tied to the managed live turn captured before any approval/read. */
1118
+ async sendFileToChat(input, signal, copy, policy) {
1119
+ const active = this.activeTurn, sessionId = this.sessionId;
1120
+ const check = () => {
1121
+ signal.throwIfAborted();
1122
+ if (!active || !sessionId || this.activeTurn !== active || this.sessionId !== sessionId || active.fileDeliveryRevoked
1123
+ || active.cancellationSource || this.closing || !this.isAlive() || this.replaying || this.steeringOccupied
1124
+ || active.origin?.kind === 'scheduled-loop' || policy.approval === 'deny')
1125
+ throw Error('CURRENT_CHAT_DELIVERY_UNAVAILABLE');
1126
+ };
1127
+ check();
1128
+ if (this.fileExportBusy)
1129
+ throw Error('FILE_DELIVERY_BUSY');
1130
+ this.fileExportBusy = true;
1131
+ const toolCallId = 'fleet-file-export-' + randomUUID();
1132
+ try {
1133
+ const onAbort = () => {
1134
+ for (const [id, pending] of this.pendingPermissions)
1135
+ if (pending.toolCallId === toolCallId)
1136
+ this.settlePendingAutomatically(id, pending, 'cancelled', undefined, 'File send cancelled');
1137
+ };
1138
+ signal.addEventListener('abort', onAbort, { once: true });
1139
+ let answer;
1140
+ try {
1141
+ answer = await this.requestPermission({ sessionId: sessionId, toolCall: {
1142
+ toolCallId, title: 'Send file to this ACP chat: ' + (input.filename ?? input.path), kind: 'read', status: 'pending',
1143
+ locations: [{ path: resolve(this.options.cwd, input.path) }],
1144
+ }, options: [{ optionId: 'send', name: 'Send file', kind: 'allow_once' }, { optionId: 'deny', name: 'Do not send', kind: 'reject_once' }] }, policy);
1145
+ }
1146
+ finally {
1147
+ signal.removeEventListener('abort', onAbort);
1148
+ }
1149
+ check();
1150
+ if (answer.outcome.outcome !== 'selected' || answer.outcome.optionId !== 'send')
1151
+ throw Error('FILE_SEND_DENIED');
1152
+ const attachment = await copy({ sessionGeneration: this.sessionGeneration, acpSessionId: sessionId, turnId: active.id });
1153
+ // Synchronous check + durable append establishes order with cancellation/stop.
1154
+ // No await, pending-send journal, replay or recovery exists after this point.
1155
+ check();
1156
+ this.conversation.append({ kind: 'file.attached', source: 'agent', sessionGeneration: this.sessionGeneration,
1157
+ acpSessionId: sessionId, promptId: active.id, turnId: active.id, payload: { attachment } });
1158
+ return attachment;
1159
+ }
1160
+ finally {
1161
+ this.releaseTool(toolCallId);
1162
+ this.fileExportBusy = false;
1163
+ }
1164
+ }
1016
1165
  async close() {
1017
1166
  this.closing = true;
1018
1167
  if (this.stallTimer)
@@ -1066,6 +1215,8 @@ export class AcpSession {
1066
1215
  const persisted = this.options.mode === 'resume' && existsSync(this.sessionFile)
1067
1216
  ? readFileSync(this.sessionFile, 'utf8').trim()
1068
1217
  : '';
1218
+ if (this.options.requireResume && (!persisted || !(this.agentCapabilities?.sessionCapabilities?.resume != null || this.agentCapabilities?.loadSession)))
1219
+ throw new Error('TASK_CONTEXT_RESUME_UNAVAILABLE');
1069
1220
  let advertisedConfigOptions;
1070
1221
  let advertisedModes;
1071
1222
  let advertisedModelId;
@@ -1283,8 +1434,9 @@ export class AcpSession {
1283
1434
  }
1284
1435
  }
1285
1436
  /** Keep the original queue slot (including startup) until recovery finishes. */
1286
- async runPrompt(text, turnId = randomUUID(), origin) {
1287
- const result = await this.runSinglePrompt(text, turnId, origin);
1437
+ async runPrompt(members) {
1438
+ const { text, promptId: turnId, origin } = members[0];
1439
+ const result = await this.runSinglePrompt(text, turnId, origin, members);
1288
1440
  const attempt = this.stallAttempt;
1289
1441
  if (!attempt || attempt.turnId !== turnId)
1290
1442
  return result;
@@ -1319,7 +1471,21 @@ export class AcpSession {
1319
1471
  return turnResult(true, 'cancelled', 'diagnostic recovery requires operator attention', undefined, 'stall-watchdog');
1320
1472
  }
1321
1473
  }
1322
- async runSinglePrompt(text, turnId = randomUUID(), origin) {
1474
+ /**
1475
+ * One adapter turn. `members` are the admitted prompts it carries, the first
1476
+ * being the turn's own id; each keeps its own started and completed records
1477
+ * so none is left open and none is reported delivered that was not sent.
1478
+ */
1479
+ async runSinglePrompt(text, turnId = randomUUID(), origin, members = [{ promptId: turnId, text, origin }]) {
1480
+ const closeMembers = (payload) => {
1481
+ for (const member of members)
1482
+ this.conversation.appendSafe({
1483
+ kind: 'turn.completed', sessionGeneration: this.sessionGeneration,
1484
+ acpSessionId: this.sessionId, promptId: member.promptId, turnId, payload,
1485
+ });
1486
+ };
1487
+ // Not handed to the adapter and not closed: the members stay admitted, and
1488
+ // the next session generation dispatches them again.
1323
1489
  if (!this.sessionId || !this.isAlive())
1324
1490
  return turnResult(false, 'failed', this.lastError ?? 'ACP session is offline');
1325
1491
  this.readiness = 'running';
@@ -1327,18 +1493,19 @@ export class AcpSession {
1327
1493
  this.retryNativeTurnId = undefined;
1328
1494
  let settle;
1329
1495
  const settled = new Promise(resolve => { settle = resolve; });
1330
- this.activeTurn = { toolEvidence: new Map(), startedAt: Date.now(), toolIds: new Set(), lastProgressAt: 0, progressCount: 0, transportFailures: 0, boundaryUnknown: false, id: turnId, output: '', origin, settled, settle };
1496
+ this.activeTurn = { toolEvidence: new Map(), startedAt: Date.now(), toolIds: new Set(), lastProgressAt: 0, progressCount: 0, transportFailures: 0, boundaryUnknown: false, id: turnId, output: '', origin, settled, settle, promptIds: members.map(member => member.promptId), console: members.some(member => member.origin?.kind === 'owner-admin-console') };
1331
1497
  this.events.emit('state', { turnId, status: 'running', origin });
1332
- this.conversation.appendSafe({
1498
+ members.forEach((member, index) => this.conversation.appendSafe({
1333
1499
  kind: 'prompt.started', sessionGeneration: this.sessionGeneration,
1334
- acpSessionId: this.sessionId, promptId: turnId, turnId,
1335
- source: conversationSource(origin).source, payload: {},
1336
- });
1500
+ acpSessionId: this.sessionId, promptId: member.promptId, turnId,
1501
+ source: conversationSource(member.origin).source,
1502
+ payload: members.length > 1 ? { batch: { turnId, index, size: members.length } } : {},
1503
+ }));
1337
1504
  try {
1338
1505
  const response = await Promise.race([
1339
1506
  this.connection.agent.request(acp.methods.agent.session.prompt, {
1340
1507
  sessionId: this.sessionId,
1341
- prompt: promptContentBlocks(text, origin),
1508
+ prompt: batchContentBlocks(members),
1342
1509
  }),
1343
1510
  this.terminated,
1344
1511
  ]);
@@ -1349,14 +1516,10 @@ export class AcpSession {
1349
1516
  turnId, stopReason: response.stopReason, origin, cancellationSource,
1350
1517
  });
1351
1518
  this.events.emit('state', { status: 'idle' });
1352
- this.conversation.appendSafe({
1353
- kind: 'turn.completed', sessionGeneration: this.sessionGeneration,
1354
- acpSessionId: this.sessionId, promptId: turnId, turnId,
1355
- payload: {
1356
- outcome: classifyStopReason(response.stopReason),
1357
- stopReason: response.stopReason,
1358
- ...(cancellationSource ? { cancellationSource } : {}),
1359
- },
1519
+ closeMembers({
1520
+ outcome: classifyStopReason(response.stopReason),
1521
+ stopReason: response.stopReason,
1522
+ ...(cancellationSource ? { cancellationSource } : {}),
1360
1523
  });
1361
1524
  // The prompt was accepted either way — the agent answered. Whether the
1362
1525
  // turn SUCCEEDED is a separate question, and only `stopReason` answers it.
@@ -1377,11 +1540,7 @@ export class AcpSession {
1377
1540
  });
1378
1541
  if (this.isAlive())
1379
1542
  this.events.emit('state', { status: 'idle' });
1380
- this.conversation.appendSafe({
1381
- kind: 'turn.completed', sessionGeneration: this.sessionGeneration,
1382
- acpSessionId: this.sessionId, promptId: turnId, turnId,
1383
- payload: { outcome: 'failed', stopReason: this.lastError },
1384
- });
1543
+ closeMembers({ outcome: 'failed', stopReason: this.lastError });
1385
1544
  return turnResult(false, 'failed', this.lastError, this.activeTurn?.id === turnId ? this.activeTurn.output : undefined);
1386
1545
  }
1387
1546
  finally {
@@ -1401,6 +1560,8 @@ export class AcpSession {
1401
1560
  }
1402
1561
  async steerPrompt(text) {
1403
1562
  this.steeringWasUsed = true;
1563
+ if (this.activeTurn)
1564
+ this.activeTurn.fileDeliveryRevoked = true;
1404
1565
  if (!this.sessionId || !this.isAlive())
1405
1566
  return turnResult(false, 'failed', this.lastError ?? 'ACP session is offline');
1406
1567
  this.steeringRequests++;
@@ -1432,7 +1593,7 @@ export class AcpSession {
1432
1593
  this.steeringRequests--;
1433
1594
  }
1434
1595
  }
1435
- requestPermission(params) {
1596
+ requestPermission(params, neutralPolicy) {
1436
1597
  // `kinds` is a PRIORITY order. Scanning the agent's option array instead
1437
1598
  // (`options.find(o => kinds.includes(o.kind))`) hands the choice to whatever
1438
1599
  // order the agent happened to list, which is exactly how an automatic denial
@@ -1450,7 +1611,7 @@ export class AcpSession {
1450
1611
  // Permission is part of the tool lifecycle. Reserve before any policy or
1451
1612
  // human decision so a monitor wake cannot slip between request and answer.
1452
1613
  this.reservePermission(toolCallId, permissionId);
1453
- if (this.isEffectiveCodexProtectedMcpApproval(params)) {
1614
+ if (!neutralPolicy && this.isEffectiveCodexProtectedMcpApproval(params)) {
1454
1615
  // Protected MCP approval is already the tool's narrow gate. Never turn
1455
1616
  // this one decision into an adapter-wide standing grant.
1456
1617
  const option = choose(['allow_once']);
@@ -1461,9 +1622,9 @@ export class AcpSession {
1461
1622
  this.releasePermission(toolCallId, permissionId);
1462
1623
  return Promise.resolve(response);
1463
1624
  }
1464
- if (this.options.permissions.approval === 'allow' && this.withinAutomaticBoundary(params)) {
1625
+ if ((neutralPolicy ?? this.options.permissions).approval === 'allow' && this.withinAutomaticBoundary(params, neutralPolicy)) {
1465
1626
  const option = choose(['allow_always', 'allow_once']);
1466
- const response = this.settleAutomatically(params, option, 'allowed', 'permissions.approval=allow', `the request is inside the ${this.options.permissions.filesystem} boundary`);
1627
+ const response = this.settleAutomatically(params, option, 'allowed', 'permissions.approval=allow', `the request is inside the ${(neutralPolicy ?? this.options.permissions).filesystem} boundary`);
1467
1628
  if (option)
1468
1629
  this.allowPermission(toolCallId, permissionId);
1469
1630
  else
@@ -1473,8 +1634,8 @@ export class AcpSession {
1473
1634
  // A live grace window still counts as attended: the controller may be
1474
1635
  // mid-reconnect, and denying instantly is exactly what the grace prevents.
1475
1636
  const unattended = this.controllerCount === 0 && !this.controllerGrace
1476
- && this.options.permissions.unattended === 'deny';
1477
- if (this.options.permissions.approval === 'deny' || unattended) {
1637
+ && (neutralPolicy ?? this.options.permissions).unattended === 'deny';
1638
+ if ((neutralPolicy ?? this.options.permissions).approval === 'deny' || unattended) {
1478
1639
  // reject_once FIRST: `reject_always` teaches the agent a standing rule from
1479
1640
  // a decision no human made, so one unattended denial would silently disable
1480
1641
  // the tool for the rest of the session.
@@ -1575,8 +1736,8 @@ export class AcpSession {
1575
1736
  ? { outcome: { outcome: 'selected', optionId: option.optionId } }
1576
1737
  : { outcome: { outcome: 'cancelled' } };
1577
1738
  }
1578
- withinAutomaticBoundary(params) {
1579
- const filesystem = this.options.permissions.filesystem;
1739
+ withinAutomaticBoundary(params, policy) {
1740
+ const filesystem = (policy ?? this.options.permissions).filesystem;
1580
1741
  if (filesystem === 'unrestricted')
1581
1742
  return true;
1582
1743
  if (filesystem === 'read-only' && params.toolCall.kind !== 'read')
@@ -1707,9 +1868,17 @@ export class AcpSession {
1707
1868
  const scheduled = this.activeTurn?.origin?.kind === 'scheduled-loop';
1708
1869
  const messagePhase = update.sessionUpdate === 'agent_message_chunk'
1709
1870
  ? this.codexMessagePhase(update) : undefined;
1710
- this.recordConversationUpdate(update, scheduled, messagePhase === 'commentary');
1871
+ this.recordConversationUpdate(update, scheduled);
1711
1872
  if (update.sessionUpdate === 'config_option_update')
1712
1873
  this.captureRuntimeMetadata(update.configOptions);
1874
+ if (update.sessionUpdate === 'available_commands_update')
1875
+ this.advertisedCommands = new Set((update.availableCommands ?? []).map(command => command.name));
1876
+ if (update.sessionUpdate === 'usage_update') {
1877
+ // Claude subscription rate-limit snapshot (claude-agent-acp forwards the SDK rate_limit_event).
1878
+ const rateLimit = update._meta?.['_claude/rateLimit'];
1879
+ if (rateLimit)
1880
+ recordClaudeRateLimit(this.options.stateDir, rateLimit);
1881
+ }
1713
1882
  switch (update.sessionUpdate) {
1714
1883
  case 'agent_message_chunk': {
1715
1884
  const phase = messagePhase;
@@ -1785,13 +1954,15 @@ export class AcpSession {
1785
1954
  return undefined;
1786
1955
  return phase === 'commentary' || phase === 'final_answer' ? phase : 'ambiguous';
1787
1956
  }
1788
- /** Normalize every ACP update losslessly into the durable ledger. */
1789
- recordConversationUpdate(update, scheduled, commentary = false) {
1957
+ /**
1958
+ * Normalize every ACP update losslessly into the durable ledger. The ledger is the conversation the
1959
+ * owner reads, so the agent's commentary between its tools is kept as written, like its final answer.
1960
+ */
1961
+ recordConversationUpdate(update, scheduled) {
1790
1962
  const normalized = normalizeSessionUpdate(update, scheduled ? {
1791
1963
  redactText: SCHEDULED_LOOP_REDACTION,
1792
1964
  redactToolCallId: 'scheduled-loop-tool',
1793
- }
1794
- : commentary ? { redactText: OWNER_COMMENTARY_REDACTION } : {});
1965
+ } : {});
1795
1966
  this.conversation.appendSafe({
1796
1967
  kind: normalized.kind,
1797
1968
  sessionGeneration: this.sessionGeneration,
@@ -1805,6 +1976,10 @@ export class AcpSession {
1805
1976
  });
1806
1977
  }
1807
1978
  // ── conversation ledger access (AgentSession) ─────────────────────────────
1979
+ taskNoticeBinding() {
1980
+ return this.activeTurn && this.sessionId ? { sessionGeneration: this.sessionGeneration, acpSessionId: this.sessionId, promptId: this.activeTurn.id } : undefined;
1981
+ }
1982
+ recordTaskCreated(binding, notice) { appendTaskCreated(this.conversation, binding, notice); }
1808
1983
  conversationPage(request = {}) {
1809
1984
  const floor = Number(this.conversationStartCursor ?? 0);
1810
1985
  const requested = Number(request.after ?? 0);
@@ -1832,6 +2007,7 @@ export class AcpSession {
1832
2007
  readiness: this.isAlive() ? this.readiness : 'offline',
1833
2008
  queueDepth: this.queueDepth,
1834
2009
  pendingPermissionIds: [...this.pendingPermissions.keys()],
2010
+ ...pendingInventory(this.pendingPrompts),
1835
2011
  ...(this.conversation.degraded ? { historyDegraded: true } : {}),
1836
2012
  };
1837
2013
  }
@@ -23,6 +23,7 @@ export declare class RoleTurnArbiter implements AgentSession {
23
23
  private tail;
24
24
  private unsettled;
25
25
  private stopping;
26
+ private retiring;
26
27
  /** Bumped by `retireStalledAdmission`; every claim carries the one it was made under. */
27
28
  private generation;
28
29
  /** Resolves the instant the CURRENT generation is retired. */
@@ -53,6 +54,11 @@ export declare class RoleTurnArbiter implements AgentSession {
53
54
  */
54
55
  retireStalledAdmission(): void;
55
56
  queuePrompt(text: string, options?: SubmitPromptOptions): Promise<QueuedPrompt>;
57
+ /** Inbox traffic must not cancel or steer an active human console/voice turn.
58
+ * Queue it once; the monitor commits its cursor after that turn succeeds.
59
+ * Other origins retain their configured monitor policy.
60
+ */
61
+ private monitorAdmissionOptions;
56
62
  submitPrompt(text: string, options?: SubmitPromptOptions): Promise<TurnResult>;
57
63
  /**
58
64
  * Do not hold `exclusive` while ACP waits for a tool boundary: permission
@@ -62,6 +68,9 @@ export declare class RoleTurnArbiter implements AgentSession {
62
68
  tryScheduled(text: string, origin: Extract<PromptOrigin, {
63
69
  kind: 'scheduled-loop';
64
70
  }>, beforeQueue?: () => void | Promise<void>): Promise<ScheduledAttempt>;
71
+ /** Retire only at the same admission boundary used by all prompt producers. */
72
+ retireIfIdle(expired: (snapshot: SessionSnapshot, queueDepth: number) => boolean): Promise<boolean>;
73
+ sharedTurnId(promptId: string): string | undefined;
65
74
  stopScheduledAdmission(): void;
66
75
  isAlive(): boolean;
67
76
  snapshot(): SessionSnapshot;
@@ -72,6 +81,8 @@ export declare class RoleTurnArbiter implements AgentSession {
72
81
  conversationSnapshot(): ConversationSnapshot;
73
82
  subscribeConversation(listener: (event: ConversationEventV1) => void): () => void;
74
83
  submitPromptBrowser(command: SubmitPromptCommand): Promise<PromptReceipt>;
84
+ get liveVoiceSupported(): boolean;
85
+ interruptPrompt(generation: string, promptId: string): Promise<InterruptOutcome>;
75
86
  interrupt(source?: TurnCancellationSource): Promise<InterruptOutcome>;
76
87
  respondPermission(permissionId: string, optionId: string): boolean;
77
88
  respondPermissionV2(permissionId: string, optionId: string, sessionGeneration: string): 'accepted' | 'stale';