@ours.network/fleet 1.2.0-nightly.5 → 1.2.0-nightly.51

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 (330) hide show
  1. package/README.md +263 -53
  2. package/dist/account-origin.d.ts +2 -0
  3. package/dist/account-origin.js +6 -0
  4. package/dist/agent-ours/bridge.js +9 -0
  5. package/dist/agent-ours/correspondence.d.ts +10 -0
  6. package/dist/agent-ours/correspondence.js +1 -0
  7. package/dist/agent-ours/harness.js +8 -0
  8. package/dist/agent-ours/runtime.d.ts +6 -0
  9. package/dist/agent-ours/runtime.js +36 -0
  10. package/dist/agent-ours/service.js +33 -13
  11. package/dist/application/codex-models.d.ts +15 -0
  12. package/dist/application/codex-models.js +56 -0
  13. package/dist/application/conversation-history.d.ts +25 -0
  14. package/dist/application/conversation-history.js +189 -0
  15. package/dist/application/fleet-query-service.d.ts +1 -1
  16. package/dist/application/fleet-query-service.js +2 -2
  17. package/dist/application/legacy-supervisor-identity.d.ts +7 -0
  18. package/dist/application/legacy-supervisor-identity.js +43 -0
  19. package/dist/application/model-catalog.d.ts +1 -1
  20. package/dist/application/model-catalog.js +5 -2
  21. package/dist/application/preset-provenance.d.ts +19 -0
  22. package/dist/application/preset-provenance.js +59 -0
  23. package/dist/application/role-creation-service.js +1 -1
  24. package/dist/application/role-removal-service.js +2 -0
  25. package/dist/application/role-repository.js +2 -2
  26. package/dist/application/room-layout-definitions.d.ts +45 -0
  27. package/dist/application/room-layout-definitions.js +130 -0
  28. package/dist/application/session-control.d.ts +30 -0
  29. package/dist/application/session-control.js +72 -3
  30. package/dist/application/supervisor-ours-tools.d.ts +159 -0
  31. package/dist/application/supervisor-ours-tools.js +113 -0
  32. package/dist/application/task-layouts.d.ts +91 -0
  33. package/dist/application/task-layouts.js +249 -0
  34. package/dist/application/task-room-service.d.ts +99 -3
  35. package/dist/application/task-room-service.js +296 -31
  36. package/dist/atomic-file.d.ts +5 -0
  37. package/dist/atomic-file.js +62 -1
  38. package/dist/briefing.d.ts +2 -0
  39. package/dist/briefing.js +8 -1
  40. package/dist/build-info.json +7 -4
  41. package/dist/capabilities.d.ts +6 -1
  42. package/dist/capabilities.js +8 -0
  43. package/dist/cli.js +376 -34
  44. package/dist/client-profile.d.ts +5 -17
  45. package/dist/client-profile.js +4 -127
  46. package/dist/config.d.ts +28 -0
  47. package/dist/config.js +41 -6
  48. package/dist/creation.js +2 -2
  49. package/dist/daemon-recovery.d.ts +1 -8
  50. package/dist/daemon-recovery.js +8 -113
  51. package/dist/docs.d.ts +3 -3
  52. package/dist/docs.js +126 -22
  53. package/dist/doctor.d.ts +1 -1
  54. package/dist/doctor.js +55 -60
  55. package/dist/erased-resources.d.ts +9 -0
  56. package/dist/erased-resources.js +87 -0
  57. package/dist/fleet-command-audit.d.ts +7 -0
  58. package/dist/fleet-command-audit.js +26 -9
  59. package/dist/harness/claude-code.js +9 -1
  60. package/dist/harness/claude-runtime.d.ts +12 -0
  61. package/dist/harness/claude-runtime.js +43 -0
  62. package/dist/harness/codex-app-server-proxy.d.ts +4 -1
  63. package/dist/harness/codex-app-server-proxy.js +41 -5
  64. package/dist/harness/codex-reasoning.d.ts +5 -0
  65. package/dist/harness/codex-reasoning.js +12 -0
  66. package/dist/harness/codex-runtime.d.ts +11 -1
  67. package/dist/harness/codex-runtime.js +31 -4
  68. package/dist/harness/codex.d.ts +3 -2
  69. package/dist/harness/codex.js +20 -11
  70. package/dist/init-wizard.d.ts +2 -0
  71. package/dist/init-wizard.js +38 -19
  72. package/dist/isolation/policy.js +2 -0
  73. package/dist/isolation/types.d.ts +5 -0
  74. package/dist/managed-cli.d.ts +253 -0
  75. package/dist/managed-cli.js +923 -0
  76. package/dist/minimal-setup.d.ts +2 -0
  77. package/dist/minimal-setup.js +44 -0
  78. package/dist/monitor.d.ts +1 -34
  79. package/dist/monitor.js +24 -151
  80. package/dist/notifications/fleet-producer.d.ts +19 -0
  81. package/dist/notifications/fleet-producer.js +208 -0
  82. package/dist/notifications/outbox.d.ts +46 -0
  83. package/dist/notifications/outbox.js +158 -0
  84. package/dist/notifications/target-cleanup.d.ts +8 -0
  85. package/dist/notifications/target-cleanup.js +41 -0
  86. package/dist/ops.js +5 -1
  87. package/dist/owner-channel/channel.d.ts +3 -0
  88. package/dist/owner-channel/channel.js +34 -6
  89. package/dist/owner-channel/commands.d.ts +4 -0
  90. package/dist/owner-channel/commands.js +8 -0
  91. package/dist/owner-channel/lifecycle-outbox.js +4 -3
  92. package/dist/owner-channel/ours-client.js +2 -17
  93. package/dist/paths.d.ts +2 -0
  94. package/dist/paths.js +2 -0
  95. package/dist/preset-migration.js +25 -0
  96. package/dist/resolved-plan.js +1 -0
  97. package/dist/rooms-tasks/add-member.d.ts +48 -0
  98. package/dist/rooms-tasks/add-member.js +83 -0
  99. package/dist/rooms-tasks/archived-absence.d.ts +8 -0
  100. package/dist/rooms-tasks/archived-absence.js +48 -0
  101. package/dist/rooms-tasks/cli.js +34 -5
  102. package/dist/rooms-tasks/close.d.ts +5 -2
  103. package/dist/rooms-tasks/close.js +208 -28
  104. package/dist/rooms-tasks/config.d.ts +1 -1
  105. package/dist/rooms-tasks/config.js +11 -4
  106. package/dist/rooms-tasks/cowork-adapter.d.ts +6 -4
  107. package/dist/rooms-tasks/cowork-adapter.js +8 -121
  108. package/dist/rooms-tasks/deletion.d.ts +1 -1
  109. package/dist/rooms-tasks/deletion.js +74 -5
  110. package/dist/rooms-tasks/erasure.d.ts +6 -0
  111. package/dist/rooms-tasks/erasure.js +169 -0
  112. package/dist/rooms-tasks/external-worker.d.ts +1 -1
  113. package/dist/rooms-tasks/external-worker.js +3 -4
  114. package/dist/rooms-tasks/layout-binding-client.d.ts +11 -0
  115. package/dist/rooms-tasks/layout-binding-client.js +56 -0
  116. package/dist/rooms-tasks/layout-binding-grants.d.ts +21 -0
  117. package/dist/rooms-tasks/layout-binding-grants.js +128 -0
  118. package/dist/rooms-tasks/layout-cli.d.ts +4 -0
  119. package/dist/rooms-tasks/layout-cli.js +107 -0
  120. package/dist/rooms-tasks/layout-config.d.ts +11 -0
  121. package/dist/rooms-tasks/layout-config.js +89 -0
  122. package/dist/rooms-tasks/layout-control.d.ts +21 -0
  123. package/dist/rooms-tasks/layout-control.js +52 -0
  124. package/dist/rooms-tasks/layout-service.d.ts +42 -0
  125. package/dist/rooms-tasks/layout-service.js +185 -0
  126. package/dist/rooms-tasks/layout.d.ts +114 -0
  127. package/dist/rooms-tasks/layout.js +250 -0
  128. package/dist/rooms-tasks/live-readiness.d.ts +18 -0
  129. package/dist/rooms-tasks/live-readiness.js +85 -0
  130. package/dist/rooms-tasks/member-startup.d.ts +1 -0
  131. package/dist/rooms-tasks/member-startup.js +2 -0
  132. package/dist/rooms-tasks/provision.d.ts +14 -0
  133. package/dist/rooms-tasks/provision.js +173 -46
  134. package/dist/rooms-tasks/room-state.d.ts +4 -2
  135. package/dist/rooms-tasks/room-state.js +53 -8
  136. package/dist/rooms-tasks/task-state.d.ts +42 -7
  137. package/dist/rooms-tasks/task-state.js +153 -23
  138. package/dist/rooms-tasks/types.d.ts +28 -0
  139. package/dist/rooms-tasks/workspace-artifacts.d.ts +6 -0
  140. package/dist/rooms-tasks/workspace-artifacts.js +93 -0
  141. package/dist/rooms-tasks/workspace.d.ts +26 -0
  142. package/dist/rooms-tasks/workspace.js +195 -0
  143. package/dist/runner.d.ts +19 -3
  144. package/dist/runner.js +128 -45
  145. package/dist/session/acp.d.ts +9 -1
  146. package/dist/session/acp.js +31 -6
  147. package/dist/session/arbiter.d.ts +5 -0
  148. package/dist/session/arbiter.js +28 -2
  149. package/dist/session/codex-app-server.d.ts +5 -0
  150. package/dist/session/codex-app-server.js +36 -3
  151. package/dist/session/control.d.ts +12 -1
  152. package/dist/session/control.js +68 -3
  153. package/dist/session/conversation-normalizer.d.ts +2 -1
  154. package/dist/session/conversation-normalizer.js +1 -1
  155. package/dist/session/conversation-store.d.ts +1 -1
  156. package/dist/session/conversation-store.js +7 -11
  157. package/dist/session/conversation-types.d.ts +7 -3
  158. package/dist/session/task-notice.d.ts +14 -0
  159. package/dist/session/task-notice.js +13 -0
  160. package/dist/session/types.d.ts +6 -0
  161. package/dist/setup-tunnel-preflight.d.ts +34 -0
  162. package/dist/setup-tunnel-preflight.js +62 -0
  163. package/dist/socket-path.d.ts +2 -0
  164. package/dist/socket-path.js +22 -0
  165. package/dist/spawn.js +8 -1
  166. package/dist/startup-readiness.d.ts +13 -0
  167. package/dist/startup-readiness.js +38 -0
  168. package/dist/subscriptions/cli.d.ts +91 -0
  169. package/dist/subscriptions/cli.js +223 -0
  170. package/dist/subscriptions/launch.d.ts +37 -0
  171. package/dist/subscriptions/launch.js +93 -0
  172. package/dist/subscriptions/login.d.ts +75 -0
  173. package/dist/subscriptions/login.js +182 -0
  174. package/dist/subscriptions/observed.d.ts +7 -0
  175. package/dist/subscriptions/observed.js +44 -0
  176. package/dist/subscriptions/probe.d.ts +10 -0
  177. package/dist/subscriptions/probe.js +73 -0
  178. package/dist/subscriptions/service.d.ts +102 -0
  179. package/dist/subscriptions/service.js +364 -0
  180. package/dist/subscriptions/store.d.ts +71 -0
  181. package/dist/subscriptions/store.js +140 -0
  182. package/dist/supervisor/launchd.js +2 -1
  183. package/dist/supervisor/systemd.js +9 -23
  184. package/dist/temp-idle.d.ts +17 -0
  185. package/dist/temp-idle.js +42 -0
  186. package/dist/temp-lifecycle.d.ts +6 -1
  187. package/dist/temp-lifecycle.js +29 -13
  188. package/dist/tunnel-setup-completion.d.ts +7 -0
  189. package/dist/tunnel-setup-completion.js +13 -0
  190. package/dist/tunnel-setup-input.d.ts +19 -0
  191. package/dist/tunnel-setup-input.js +124 -0
  192. package/dist/web/agent-attachments.d.ts +29 -0
  193. package/dist/web/agent-attachments.js +162 -0
  194. package/dist/web/auth.d.ts +29 -1
  195. package/dist/web/auth.js +82 -7
  196. package/dist/web/available-port.d.ts +2 -0
  197. package/dist/web/available-port.js +23 -0
  198. package/dist/web/control.d.ts +3 -3
  199. package/dist/web/control.js +10 -8
  200. package/dist/web/fleet-config-service.d.ts +4 -1
  201. package/dist/web/fleet-config-service.js +46 -15
  202. package/dist/web/prefix-gateway.d.ts +32 -0
  203. package/dist/web/prefix-gateway.js +234 -0
  204. package/dist/web/runtime.d.ts +3 -1
  205. package/dist/web/runtime.js +116 -31
  206. package/dist/web/server.d.ts +16 -0
  207. package/dist/web/server.js +592 -53
  208. package/dist/web/service.d.ts +11 -2
  209. package/dist/web/service.js +30 -4
  210. package/dist/web/workspace-devices.d.ts +36 -0
  211. package/dist/web/workspace-devices.js +101 -0
  212. package/dist/web/workspace-tunnel.d.ts +1 -0
  213. package/dist/web/workspace-tunnel.js +34 -0
  214. package/dist/workspace-enrollment.d.ts +82 -0
  215. package/dist/workspace-enrollment.js +566 -0
  216. package/dist/workspace-replacement.d.ts +66 -0
  217. package/dist/workspace-replacement.js +220 -0
  218. package/docs/live-voice.md +13 -0
  219. package/docs/room-layouts.md +227 -0
  220. package/docs/task-workspaces.md +130 -0
  221. package/docs/validation/managed-cli-permissions.md +421 -0
  222. package/examples/room-layouts/fresh.yaml +27 -0
  223. package/examples/room-layouts/scoped.yaml +21 -0
  224. package/examples/room-layouts/shared.yaml +19 -0
  225. package/package.json +15 -18
  226. package/presets/README.md +78 -0
  227. package/presets/brain-catalog.json +8 -2
  228. package/presets/fleet/agent_templates/Critic.yaml +1 -1
  229. package/presets/fleet/agent_templates/Developer.yaml +1 -1
  230. package/presets/fleet/agent_templates/Engineer.yaml +5 -0
  231. package/presets/fleet/agent_templates/LocalCoordinator.yaml +1 -1
  232. package/presets/fleet/agents/FleetCoordinator.yaml +1 -1
  233. package/presets/fleet/brains/claude-fable-5-1-high.yaml +5 -0
  234. package/presets/fleet/brains/claude-fable-5-1-low.yaml +5 -0
  235. package/presets/fleet/brains/claude-fable-5-1-max.yaml +5 -0
  236. package/presets/fleet/brains/claude-fable-5-1-medium.yaml +5 -0
  237. package/presets/fleet/brains/claude-fable-5-1-xhigh.yaml +5 -0
  238. package/presets/fleet/brains/claude-fable-5-high.yaml +1 -1
  239. package/presets/fleet/brains/claude-fable-5-low.yaml +1 -1
  240. package/presets/fleet/brains/claude-fable-5-max.yaml +1 -1
  241. package/presets/fleet/brains/claude-fable-5-medium.yaml +1 -1
  242. package/presets/fleet/brains/claude-fable-5-xhigh.yaml +1 -1
  243. package/presets/fleet/brains/claude-haiku-4-5-20251001-default.yaml +4 -0
  244. package/presets/fleet/brains/claude-opus-5-5-high.yaml +5 -0
  245. package/presets/fleet/brains/claude-opus-5-5-low.yaml +5 -0
  246. package/presets/fleet/brains/claude-opus-5-5-max.yaml +5 -0
  247. package/presets/fleet/brains/claude-opus-5-5-medium.yaml +5 -0
  248. package/presets/fleet/brains/claude-opus-5-5-xhigh.yaml +5 -0
  249. package/presets/fleet/brains/claude-opus-5-high.yaml +1 -1
  250. package/presets/fleet/brains/claude-opus-5-low.yaml +1 -1
  251. package/presets/fleet/brains/claude-opus-5-max.yaml +1 -1
  252. package/presets/fleet/brains/claude-opus-5-medium.yaml +1 -1
  253. package/presets/fleet/brains/claude-opus-5-xhigh.yaml +1 -1
  254. package/presets/fleet/brains/claude-sonnet-5-high.yaml +1 -1
  255. package/presets/fleet/brains/claude-sonnet-5-low.yaml +1 -1
  256. package/presets/fleet/brains/claude-sonnet-5-max.yaml +1 -1
  257. package/presets/fleet/brains/claude-sonnet-5-medium.yaml +1 -1
  258. package/presets/fleet/brains/claude-sonnet-5-xhigh.yaml +1 -1
  259. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-high.yaml +1 -1
  260. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-low.yaml +1 -1
  261. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-medium.yaml +1 -1
  262. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-xhigh.yaml +1 -1
  263. package/presets/fleet/brains/codex-gpt-5-4-high.yaml +1 -1
  264. package/presets/fleet/brains/codex-gpt-5-4-low.yaml +1 -1
  265. package/presets/fleet/brains/codex-gpt-5-4-medium.yaml +1 -1
  266. package/presets/fleet/brains/codex-gpt-5-4-mini-high.yaml +1 -1
  267. package/presets/fleet/brains/codex-gpt-5-4-mini-low.yaml +1 -1
  268. package/presets/fleet/brains/codex-gpt-5-4-mini-medium.yaml +1 -1
  269. package/presets/fleet/brains/codex-gpt-5-4-mini-xhigh.yaml +1 -1
  270. package/presets/fleet/brains/codex-gpt-5-4-xhigh.yaml +1 -1
  271. package/presets/fleet/brains/codex-gpt-5-5-high.yaml +1 -1
  272. package/presets/fleet/brains/codex-gpt-5-5-low.yaml +1 -1
  273. package/presets/fleet/brains/codex-gpt-5-5-medium.yaml +1 -1
  274. package/presets/fleet/brains/codex-gpt-5-5-xhigh.yaml +1 -1
  275. package/presets/fleet/brains/codex-gpt-5-6-luna-high.yaml +1 -1
  276. package/presets/fleet/brains/codex-gpt-5-6-luna-low.yaml +1 -1
  277. package/presets/fleet/brains/codex-gpt-5-6-luna-max.yaml +1 -1
  278. package/presets/fleet/brains/codex-gpt-5-6-luna-medium.yaml +1 -1
  279. package/presets/fleet/brains/codex-gpt-5-6-luna-xhigh.yaml +1 -1
  280. package/presets/fleet/brains/codex-gpt-5-6-sol-high.yaml +1 -1
  281. package/presets/fleet/brains/codex-gpt-5-6-sol-low.yaml +1 -1
  282. package/presets/fleet/brains/codex-gpt-5-6-sol-max.yaml +1 -1
  283. package/presets/fleet/brains/codex-gpt-5-6-sol-medium.yaml +1 -1
  284. package/presets/fleet/brains/codex-gpt-5-6-sol-ultra.yaml +1 -1
  285. package/presets/fleet/brains/codex-gpt-5-6-sol-xhigh.yaml +1 -1
  286. package/presets/fleet/brains/codex-gpt-5-6-terra-high.yaml +1 -1
  287. package/presets/fleet/brains/codex-gpt-5-6-terra-low.yaml +1 -1
  288. package/presets/fleet/brains/codex-gpt-5-6-terra-max.yaml +1 -1
  289. package/presets/fleet/brains/codex-gpt-5-6-terra-medium.yaml +1 -1
  290. package/presets/fleet/brains/codex-gpt-5-6-terra-ultra.yaml +1 -1
  291. package/presets/fleet/brains/codex-gpt-5-6-terra-xhigh.yaml +1 -1
  292. package/presets/fleet/brains/codex-gpt-6-1-sol-high.yaml +5 -0
  293. package/presets/fleet/brains/codex-gpt-6-1-sol-low.yaml +5 -0
  294. package/presets/fleet/brains/codex-gpt-6-1-sol-max.yaml +5 -0
  295. package/presets/fleet/brains/codex-gpt-6-1-sol-medium.yaml +5 -0
  296. package/presets/fleet/brains/codex-gpt-6-1-sol-ultra.yaml +5 -0
  297. package/presets/fleet/brains/codex-gpt-6-1-sol-xhigh.yaml +5 -0
  298. package/presets/fleet/brains/codex-gpt-6-astra-high.yaml +1 -1
  299. package/presets/fleet/brains/codex-gpt-6-astra-low.yaml +1 -1
  300. package/presets/fleet/brains/codex-gpt-6-astra-max.yaml +1 -1
  301. package/presets/fleet/brains/codex-gpt-6-astra-medium.yaml +1 -1
  302. package/presets/fleet/brains/codex-gpt-6-astra-ultra.yaml +1 -1
  303. package/presets/fleet/brains/codex-gpt-6-astra-xhigh.yaml +1 -1
  304. package/presets/fleet/brains/codex-gpt-6-luna-high.yaml +5 -0
  305. package/presets/fleet/brains/codex-gpt-6-luna-low.yaml +5 -0
  306. package/presets/fleet/brains/codex-gpt-6-luna-max.yaml +5 -0
  307. package/presets/fleet/brains/codex-gpt-6-luna-medium.yaml +5 -0
  308. package/presets/fleet/brains/codex-gpt-6-luna-xhigh.yaml +5 -0
  309. package/presets/fleet/brains/codex-gpt-6-sol-high.yaml +5 -0
  310. package/presets/fleet/brains/codex-gpt-6-sol-low.yaml +5 -0
  311. package/presets/fleet/brains/codex-gpt-6-sol-max.yaml +5 -0
  312. package/presets/fleet/brains/codex-gpt-6-sol-medium.yaml +5 -0
  313. package/presets/fleet/brains/codex-gpt-6-sol-ultra.yaml +5 -0
  314. package/presets/fleet/brains/codex-gpt-6-sol-xhigh.yaml +5 -0
  315. package/presets/fleet/roles/Coordinator.yaml +34 -11
  316. package/presets/fleet/roles/Engineer.yaml +40 -0
  317. package/presets/fleet/room_layouts/engineering.yaml +25 -0
  318. package/presets/fleet/room_layouts/pair.yaml +24 -0
  319. package/presets/fleet/room_layouts/single.yaml +17 -0
  320. package/presets/fleet/room_layouts/team.yaml +30 -0
  321. package/presets/fleet/room_templates/engineering.yaml +19 -0
  322. package/presets/manifest.json +2 -2
  323. package/dist/web-app/assets/index-59GF-gsZ.css +0 -1
  324. package/dist/web-app/assets/index-C0h3ALvs.js +0 -10
  325. package/dist/web-app/icons/ours-fleet-maskable.svg +0 -4
  326. package/dist/web-app/icons/ours-fleet.svg +0 -4
  327. package/dist/web-app/index.html +0 -17
  328. package/dist/web-app/manifest.webmanifest +0 -15
  329. package/dist/web-app/offline.html +0 -18
  330. package/dist/web-app/sw.js +0 -51
@@ -1,3 +1,4 @@
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';
3
4
  import type { CodexAppServerTransportFactory } from './codex-app-server-transport.js';
@@ -68,6 +69,8 @@ export declare class CodexAppServerSession implements AgentSession {
68
69
  snapshot(): SessionSnapshot;
69
70
  queuePrompt(text: string, options?: SubmitPromptOptions): Promise<QueuedPrompt>;
70
71
  submitPrompt(text: string, options?: SubmitPromptOptions): Promise<TurnResult>;
72
+ readonly liveVoiceSupported = true;
73
+ interruptPrompt(generation: string, promptId: string): Promise<InterruptOutcome>;
71
74
  interrupt(source?: TurnCancellationSource): Promise<InterruptOutcome>;
72
75
  respondPermission(permissionId: string, optionId: string): boolean;
73
76
  respondPermissionV2(permissionId: string, optionId: string, sessionGeneration: string): 'accepted' | 'stale';
@@ -76,6 +79,8 @@ export declare class CodexAppServerSession implements AgentSession {
76
79
  setControllerAttached(attached: boolean): void;
77
80
  exitResult(): ExitRecord | null;
78
81
  close(): Promise<void>;
82
+ taskNoticeBinding(): TaskNoticeBinding | undefined;
83
+ recordTaskCreated(binding: TaskNoticeBinding, notice: TaskCreatedNotice): void;
79
84
  conversationPage(request?: {
80
85
  after?: string;
81
86
  limit?: number;
@@ -1,6 +1,8 @@
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';
6
8
  import { CodexAppServerTransport } from './codex-app-server-transport.js';
@@ -12,7 +14,6 @@ const INTERRUPT_TERMINATE_GRACE_MS = 5_000;
12
14
  const TERMINAL_RECONCILE_QUIET_MS = 2_000;
13
15
  const TERMINAL_RECONCILE_REQUEST_TIMEOUT_MS = 5_000;
14
16
  const SCHEDULED_REDACTION = '[scheduled-loop content redacted]';
15
- const COMMENTARY_REDACTION = '[assistant commentary redacted]';
16
17
  const OWNER_ADMIN_CONTEXT_KEY = 'ours-fleet://prompt-provenance?source=owner_admin_console';
17
18
  const OWNER_CHANNEL_CONTEXT_KEY = 'ours-fleet://prompt-provenance?source=owner_channel';
18
19
  const isObject = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
@@ -226,10 +227,20 @@ export class CodexAppServerSession {
226
227
  throw error;
227
228
  }
228
229
  }
230
+ liveVoiceSupported = true;
231
+ async interruptPrompt(generation, promptId) {
232
+ if (generation !== this.sessionGeneration)
233
+ throw new Error('session_changed: Agent session changed.');
234
+ if (this.activeTurn?.promptId !== promptId)
235
+ return { state: 'settled' };
236
+ return this.interrupt('local-console');
237
+ }
229
238
  async interrupt(source = 'local-console') {
230
239
  const active = this.activeTurn;
231
- if (!active || !this.threadId || !active.nativeTurnId)
240
+ if (!active || !this.threadId)
232
241
  return { state: 'settled' };
242
+ if (!active.nativeTurnId)
243
+ 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
244
  active.cancellationSource ??= source;
234
245
  this.events.emit('state', { turnId: active.promptId, status: 'running', origin: active.origin });
235
246
  this.conversation.appendSafe({
@@ -335,6 +346,10 @@ export class CodexAppServerSession {
335
346
  this.conversation.close();
336
347
  await this.transport.close();
337
348
  }
349
+ taskNoticeBinding() {
350
+ return this.activeTurn && this.threadId ? { sessionGeneration: this.sessionGeneration, acpSessionId: this.threadId, promptId: this.activeTurn.promptId } : undefined;
351
+ }
352
+ recordTaskCreated(binding, notice) { appendTaskCreated(this.conversation, binding, notice); }
338
353
  conversationPage(request = {}) {
339
354
  const floor = Number(this.conversationStartCursor ?? 0);
340
355
  const requested = Number(request.after ?? 0);
@@ -367,6 +382,10 @@ export class CodexAppServerSession {
367
382
  const existing = this.conversation.receiptFor(command.commandId, bodyDigest);
368
383
  if (existing)
369
384
  return existing;
385
+ if (command.expectedSessionGeneration !== undefined && command.expectedSessionGeneration !== this.conversationSnapshot().sessionGeneration)
386
+ throw new Error('session_changed: Agent session changed. Record a new voice message.');
387
+ if (command.requireIdle && (this.queueDepth > 0 || this.conversationSnapshot().readiness !== 'idle'))
388
+ throw new Error('voice_busy: Agent is already working. Wait for the current turn.');
370
389
  const queued = await this.queuePrompt(command.text, {
371
390
  origin: { kind: 'owner-admin-console', commandId: command.commandId },
372
391
  actor: { browserSession: command.actorBrowserSession },
@@ -415,6 +434,10 @@ export class CodexAppServerSession {
415
434
  this.reasoningEffort = { value: effort };
416
435
  this.readiness = 'idle';
417
436
  this.events.emit('state', { status: 'idle', text: `Codex thread ${this.threadId}` });
437
+ this.conversation.appendSafe({
438
+ kind: 'capabilities.updated', sessionGeneration: this.sessionGeneration,
439
+ payload: { commands: [{ name: 'compact', description: textBlock('Compact session context') }] },
440
+ });
418
441
  this.conversation.appendSafe({
419
442
  kind: 'session.state', sessionGeneration: this.sessionGeneration,
420
443
  payload: { status: 'idle' },
@@ -486,6 +509,12 @@ export class CodexAppServerSession {
486
509
  promptId, turnId: promptId, source: sourceFor(origin).source, payload: {},
487
510
  });
488
511
  try {
512
+ if (text.trim() === '/compact') {
513
+ // The acknowledgement is not completion. Standard turn notifications
514
+ // settle the same queued prompt and durable browser receipt.
515
+ await this.transport.request('thread/compact/start', { threadId: this.threadId });
516
+ return await settled;
517
+ }
489
518
  const response = await this.transport.request('turn/start', {
490
519
  threadId: this.threadId,
491
520
  input: [{ type: 'text', text, text_elements: [] }],
@@ -699,7 +728,7 @@ export class CodexAppServerSession {
699
728
  promptId: this.activeTurn?.promptId, turnId: this.activeTurn?.promptId,
700
729
  messageId: id, source: 'agent',
701
730
  payload: {
702
- role: 'assistant', content: textBlock(delta, scheduled ? SCHEDULED_REDACTION : phase === 'commentary' ? COMMENTARY_REDACTION : undefined),
731
+ role: 'assistant', content: textBlock(delta, scheduled ? SCHEDULED_REDACTION : undefined),
703
732
  },
704
733
  });
705
734
  }
@@ -746,6 +775,10 @@ export class CodexAppServerSession {
746
775
  toolCallId: scheduled ? 'scheduled-loop-tool' : id, snapshot,
747
776
  title: scheduled ? 'scheduled-loop tool' : tool.title, status: tool.status,
748
777
  kind: string(item.type),
778
+ ...(!scheduled && item.arguments !== undefined ? { rawInput: boundedJson(item.arguments) } : {}),
779
+ ...(!scheduled && item.type === 'commandExecution' ? { rawInput: boundedJson({ command: item.command, cwd: item.cwd }), ...(item.aggregatedOutput !== undefined ? { rawOutput: boundedJson({ output: item.aggregatedOutput, exitCode: item.exitCode }) } : {}) } : {}),
780
+ ...(!scheduled && item.type === 'fileChange' && item.changes !== undefined ? { rawOutput: boundedJson(item.changes) } : {}),
781
+ ...(!scheduled && (item.result !== undefined || item.error !== undefined) ? { rawOutput: boundedJson({ result: item.result, error: item.error }) } : {}),
749
782
  },
750
783
  });
751
784
  }
@@ -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;
@@ -1,5 +1,5 @@
1
1
  import type * as acp from '@agentclientprotocol/sdk';
2
- import type { AdapterMeta, ConversationEventKind, ConversationPayload } from './conversation-types.js';
2
+ import type { AdapterMeta, BoundedJson, ConversationEventKind, ConversationPayload } from './conversation-types.js';
3
3
  /**
4
4
  * Reduce one ACP v1 `session/update` into one conversation-domain event draft.
5
5
  *
@@ -43,4 +43,5 @@ export interface NormalizedUpdate {
43
43
  toolCallId?: string;
44
44
  adapterMeta?: AdapterMeta[];
45
45
  }
46
+ export declare function boundedJson(value: unknown): BoundedJson;
46
47
  export declare function normalizeSessionUpdate(update: acp.SessionUpdate, options?: NormalizeOptions): NormalizedUpdate;
@@ -246,7 +246,7 @@ function redactSensitiveJson(value) {
246
246
  };
247
247
  return { value: visit(value, 0), redacted };
248
248
  }
249
- function boundedJson(value) {
249
+ export function boundedJson(value) {
250
250
  let serialized;
251
251
  let safe;
252
252
  let redacted = false;
@@ -29,6 +29,7 @@ type EventDraft = Omit<ConversationEventV1, 'schemaVersion' | 'roleId' | 'eventI
29
29
  export declare class ConversationEventStore {
30
30
  private readonly dir;
31
31
  private nextSeq;
32
+ private firstAvailableSeq?;
32
33
  private segments;
33
34
  private tail;
34
35
  private readonly listeners;
@@ -81,7 +82,6 @@ export declare class ConversationEventStore {
81
82
  private trackPromptState;
82
83
  private segmentFd;
83
84
  private writeManifest;
84
- private firstStoredSeq;
85
85
  private eventsAfter;
86
86
  private markDegraded;
87
87
  }
@@ -24,6 +24,7 @@ export class IdempotencyConflictError extends Error {
24
24
  export class ConversationEventStore {
25
25
  dir;
26
26
  nextSeq = 1;
27
+ firstAvailableSeq;
27
28
  segments = [];
28
29
  tail = [];
29
30
  listeners = new Set();
@@ -72,6 +73,7 @@ export class ConversationEventStore {
72
73
  const fd = this.segmentFd(Buffer.byteLength(line));
73
74
  writeSync(fd, line);
74
75
  fsyncSync(fd);
76
+ this.firstAvailableSeq ??= event.seq;
75
77
  this.nextSeq++;
76
78
  this.activeBytes += Buffer.byteLength(line);
77
79
  this.tail.push(event);
@@ -101,7 +103,10 @@ export class ConversationEventStore {
101
103
  const events = this.eventsAfter(after, limit + 1);
102
104
  const hasMore = events.length > limit;
103
105
  const pageEvents = hasMore ? events.slice(0, limit) : events;
104
- const firstStored = this.firstStoredSeq();
106
+ // This single-writer store never removes retained segments. Recovery and
107
+ // append already know the first readable sequence; paging the memory tail
108
+ // must not parse the first segment again, especially while skipping replay.
109
+ const firstStored = this.firstAvailableSeq;
105
110
  return {
106
111
  events: pageEvents,
107
112
  ...(firstStored !== undefined ? { firstAvailableCursor: String(firstStored) } : {}),
@@ -159,6 +164,7 @@ export class ConversationEventStore {
159
164
  let maxSeq = 0;
160
165
  for (const segment of this.segments) {
161
166
  for (const event of this.readSegment(segment)) {
167
+ this.firstAvailableSeq ??= event.seq;
162
168
  maxSeq = Math.max(maxSeq, event.seq);
163
169
  this.tail.push(event);
164
170
  if (this.tail.length > TAIL_EVENTS)
@@ -308,16 +314,6 @@ export class ConversationEventStore {
308
314
  }
309
315
  }
310
316
  // ── reads ──────────────────────────────────────────────────────────────────
311
- firstStoredSeq() {
312
- if (this.tail.length && this.tail[0].seq === 1)
313
- return 1;
314
- for (const segment of this.segments) {
315
- const events = this.readSegment(segment);
316
- if (events.length)
317
- return events[0].seq;
318
- }
319
- return this.tail[0]?.seq;
320
- }
321
317
  eventsAfter(after, limit) {
322
318
  // Serve from the in-memory tail whenever the range allows it.
323
319
  if (this.tail.length && after >= this.tail[0].seq - 1)
@@ -1,3 +1,4 @@
1
+ import type { TaskCreatedNotice } from './task-notice.js';
1
2
  import type { PromptOrigin, TurnCancellationSource, TurnOutcome } from './types.js';
2
3
  /**
3
4
  * Durable conversation domain schema for managed-session consoles.
@@ -7,11 +8,11 @@ import type { PromptOrigin, TurnCancellationSource, TurnOutcome } from './types.
7
8
  * file touches the wire: transport updates are reduced into these shapes by a
8
9
  * session adapter, and the store assigns `seq`/`eventId`/timestamps.
9
10
  */
10
- export type ConversationEventKind = 'prompt.admitted' | 'prompt.started' | 'prompt.interrupt_requested' | 'message.chunk' | 'message.replace' | 'thought.chunk' | 'thought.replace' | 'plan.replace' | 'tool.upsert' | 'tool.content_chunk' | 'permission.requested' | 'permission.resolved' | 'monitor.delivery' | 'usage.updated' | 'turn.state' | 'turn.completed' | 'session.state' | 'session.info' | 'capabilities.updated' | 'error'
11
+ export type ConversationEventKind = 'prompt.admitted' | 'prompt.started' | 'prompt.interrupt_requested' | 'message.chunk' | 'message.replace' | 'thought.chunk' | 'thought.replace' | 'plan.replace' | 'tool.upsert' | 'tool.content_chunk' | 'permission.requested' | 'permission.resolved' | 'fleet.task_created' | 'monitor.delivery' | 'usage.updated' | 'turn.state' | 'turn.completed' | 'session.state' | 'session.info' | 'capabilities.updated' | 'error'
11
12
  /** A well-formed transport update this version cannot represent. Bounded, never a crash. */
12
13
  | 'unsupported';
13
14
  /** Where a conversation record came from. Typed provenance, never prompt text. */
14
- export type ConversationSource = 'owner_admin_console' | 'owner_channel' | 'fleet_monitor' | 'scheduled_loop' | 'startup' | 'local_console' | 'agent' | 'agent_replay';
15
+ export type ConversationSource = 'owner_admin_console' | 'owner_channel' | 'fleet_monitor' | 'scheduled_loop' | 'startup' | 'local_console' | 'agent' | 'agent_replay' | 'fleet_lifecycle';
15
16
  export interface NormalizedText {
16
17
  type: 'text';
17
18
  text: string;
@@ -227,7 +228,7 @@ export interface BoundedJson {
227
228
  digest?: string;
228
229
  redacted?: true;
229
230
  }
230
- export type ConversationPayload = MessageChunkPayload | ThoughtChunkPayload | PlanReplacePayload | ToolUpsertPayload | UsageUpdatedPayload | SessionStatePayload | SessionInfoPayload | CapabilitiesUpdatedPayload | UnsupportedPayload | PromptAdmittedPayload | PromptStartedPayload | PromptInterruptRequestedPayload | PermissionRequestedPayload | PermissionResolvedPayload | TurnStatePayload | TurnCompletedPayload | MonitorDeliveryPayload | SessionLifecyclePayload | ErrorPayload;
231
+ export type ConversationPayload = MessageChunkPayload | ThoughtChunkPayload | PlanReplacePayload | ToolUpsertPayload | UsageUpdatedPayload | SessionStatePayload | SessionInfoPayload | CapabilitiesUpdatedPayload | UnsupportedPayload | PromptAdmittedPayload | PromptStartedPayload | PromptInterruptRequestedPayload | PermissionRequestedPayload | PermissionResolvedPayload | TurnStatePayload | TurnCompletedPayload | MonitorDeliveryPayload | SessionLifecyclePayload | ErrorPayload | TaskCreatedNotice;
231
232
  export interface ConversationEventV1 {
232
233
  schemaVersion: 1;
233
234
  roleId: string;
@@ -262,6 +263,9 @@ export interface ConversationEventV1 {
262
263
  adapterMeta?: AdapterMeta[];
263
264
  }
264
265
  export interface SubmitPromptCommand {
266
+ expectedSessionGeneration?: string;
267
+ /** Live speech must not silently queue behind an unrelated turn. */
268
+ requireIdle?: boolean;
265
269
  /** Idempotency-Key / clientRequestId. Reuse with a different body is a conflict. */
266
270
  commandId: string;
267
271
  text: string;
@@ -0,0 +1,14 @@
1
+ import type { ConversationEventStore } from './conversation-store.js';
2
+ export interface TaskNoticeBinding {
3
+ sessionGeneration: string;
4
+ acpSessionId: string;
5
+ promptId: string;
6
+ }
7
+ export interface TaskCreatedNotice {
8
+ operationId: string;
9
+ taskId: string;
10
+ title: string;
11
+ state: string;
12
+ }
13
+ /** Only the trusted Fleet audit completion calls this, never model output. */
14
+ export declare function appendTaskCreated(store: ConversationEventStore, binding: TaskNoticeBinding, notice: TaskCreatedNotice): void;
@@ -0,0 +1,13 @@
1
+ /** Only the trusted Fleet audit completion calls this, never model output. */
2
+ export function appendTaskCreated(store, binding, notice) {
3
+ let after;
4
+ do {
5
+ const page = store.page({ after, limit: 1000 });
6
+ if (page.events.some(e => e.kind === 'fleet.task_created' && e.payload.operationId === notice.operationId))
7
+ return;
8
+ if (!page.hasMore)
9
+ break;
10
+ after = page.nextCursor;
11
+ } while (after);
12
+ store.append({ kind: 'fleet.task_created', source: 'fleet_lifecycle', ...binding, turnId: binding.promptId, payload: notice });
13
+ }
@@ -1,3 +1,4 @@
1
+ import type { TaskCreatedNotice, TaskNoticeBinding } from './task-notice.js';
1
2
  import type { SessionBackendId } from '../config.js';
2
3
  import type { ConversationEventV1, ConversationSnapshot, PromptReceipt, SubmitPromptCommand } from './conversation-types.js';
3
4
  /**
@@ -289,6 +290,8 @@ export interface AgentSession {
289
290
  readonly capabilities?: AgentSessionCapabilities;
290
291
  isAlive(): boolean;
291
292
  snapshot(): SessionSnapshot;
293
+ taskNoticeBinding?(): TaskNoticeBinding | undefined;
294
+ recordTaskCreated?(binding: TaskNoticeBinding, notice: TaskCreatedNotice): void;
292
295
  conversationPage?(request: {
293
296
  after?: string;
294
297
  limit?: number;
@@ -316,6 +319,9 @@ export interface AgentSession {
316
319
  * keep compiling. Read it through `interruptOutcome`, never directly.
317
320
  */
318
321
  interrupt(source?: TurnCancellationSource): Promise<InterruptResult>;
322
+ /** Atomically bind cancellation to one voice-owned turn. */
323
+ readonly liveVoiceSupported?: boolean;
324
+ interruptPrompt?(generation: string, promptId: string): Promise<InterruptResult>;
319
325
  respondPermission(permissionId: string, optionId: string): boolean;
320
326
  /** Generation-bound browser decision; stale/settled/invalid all fail closed. */
321
327
  respondPermissionV2?(permissionId: string, optionId: string, sessionGeneration: string): 'accepted' | 'stale';
@@ -0,0 +1,34 @@
1
+ import { type Exec } from './exec.js';
2
+ import type { WorkspacePayload } from './workspace-enrollment.js';
3
+ /**
4
+ * Check what the steps after the one-time root proof depend on, before that
5
+ * proof is sent: a failure afterwards cannot be retried with a fresh command.
6
+ */
7
+ export declare function setupTunnelPreflight({ exec, platform, node }?: {
8
+ exec?: Exec;
9
+ platform?: NodeJS.Platform;
10
+ node?: string;
11
+ }): Promise<void>;
12
+ /** What the steps after the proof need, kept so they can be finished without a new command. */
13
+ export interface PendingTunnelSetup {
14
+ appOrigin: string;
15
+ origin: string;
16
+ hostWorkspaceId: string;
17
+ rootCid: string;
18
+ challenge: WorkspacePayload['challenge'];
19
+ /** Fleet configuration the setup started with; a resume must use the same one. */
20
+ configuration: string;
21
+ /** Every port sent to the account as the tunnel target. Without confirmedPort, the remote target may be any of these or the default. */
22
+ requestedPorts?: number[];
23
+ /** Last positively confirmed target; cleared before any new retarget request. */
24
+ confirmedPort?: number;
25
+ }
26
+ export declare function savePendingTunnelSetup(pending: PendingTunnelSetup): void;
27
+ export declare function clearPendingTunnelSetup(): void;
28
+ export declare function readPendingTunnelSetup(): PendingTunnelSetup;
29
+ export declare const DEFAULT_TUNNEL_PORT = 49271;
30
+ /**
31
+ * The port the web service must bind when the account can no longer retarget
32
+ * the tunnel. Unknown when a different port was requested and never confirmed.
33
+ */
34
+ export declare function requiredPortAfterExpiry(pending: PendingTunnelSetup): number;
@@ -0,0 +1,62 @@
1
+ import { existsSync, lstatSync, readFileSync, rmSync } from 'node:fs';
2
+ import { userInfo } from 'node:os';
3
+ import { join } from 'node:path';
4
+ import { replaceFileAtomically } from './atomic-file.js';
5
+ import { realExec } from './exec.js';
6
+ import { stateRoot } from './paths.js';
7
+ const UNCHANGED = 'Nothing was changed and this host was not bound.';
8
+ /**
9
+ * Check what the steps after the one-time root proof depend on, before that
10
+ * proof is sent: a failure afterwards cannot be retried with a fresh command.
11
+ */
12
+ export async function setupTunnelPreflight({ exec = realExec, platform = process.platform, node = process.versions.node } = {}) {
13
+ const [major, minor] = node.split('.').map(Number);
14
+ if (major < 22 || (major === 22 && minor < 13))
15
+ throw Error(`Tunnel setup requires Node.js 22.13 or newer; this is ${node}. Update Node.js, then run a fresh tunnel setup command. ${UNCHANGED}`);
16
+ if (platform === 'linux') {
17
+ const manager = await exec('systemctl', ['--user', 'show-environment'], { timeout: 10_000 });
18
+ if (manager.code !== 0)
19
+ throw Error(`The user service manager is not reachable from this shell (${manager.stderr.trim().split('\n')[0] || `systemctl --user exited ${manager.code}`}). `
20
+ + `Log in directly as ${userInfo().username} (not through su or sudo), or run: sudo loginctl enable-linger ${userInfo().username} && export XDG_RUNTIME_DIR=/run/user/${process.getuid?.()} `
21
+ + `Then run a fresh tunnel setup command. ${UNCHANGED}`);
22
+ }
23
+ const help = await exec('cloudflared', ['tunnel', 'run', '--help'], { timeout: 10_000 });
24
+ if (help.code === 127)
25
+ throw Error(`cloudflared is not installed or not on PATH. Install it, then run a fresh tunnel setup command. ${UNCHANGED}`);
26
+ if (help.code !== 0 || !`${help.stdout}\n${help.stderr}`.includes('--token-file'))
27
+ throw Error(`This cloudflared is too old for tunnel setup (no --token-file support). Update cloudflared, then run a fresh tunnel setup command. ${UNCHANGED}`);
28
+ }
29
+ const pendingPath = () => join(stateRoot(), 'workspace', 'pending-setup.json');
30
+ export function savePendingTunnelSetup(pending) {
31
+ replaceFileAtomically(pendingPath(), JSON.stringify(pending) + '\n', 0o600);
32
+ }
33
+ export function clearPendingTunnelSetup() { rmSync(pendingPath(), { force: true }); }
34
+ export function readPendingTunnelSetup() {
35
+ const path = pendingPath();
36
+ if (!existsSync(path))
37
+ throw Error('No unfinished tunnel setup on this host. Run the tunnel setup command from the App.');
38
+ const stat = lstatSync(path);
39
+ if (!stat.isFile() || stat.isSymbolicLink() || stat.uid !== process.getuid?.() || (stat.mode & 0o077) !== 0)
40
+ throw Error('Unfinished tunnel setup record must be an owned private file');
41
+ const pending = JSON.parse(readFileSync(path, 'utf8'));
42
+ if (typeof pending?.appOrigin !== 'string' || typeof pending.origin !== 'string' || typeof pending.hostWorkspaceId !== 'string'
43
+ || typeof pending.rootCid !== 'string' || !pending.challenge || !Number.isSafeInteger(pending.challenge.expiresAt) || typeof pending.configuration !== 'string'
44
+ || (pending.requestedPorts !== undefined && (!Array.isArray(pending.requestedPorts) || !pending.requestedPorts.every(Number.isInteger))))
45
+ throw Error('Unfinished tunnel setup record is invalid');
46
+ if (pending.confirmedPort !== undefined && (!Number.isInteger(pending.confirmedPort) || pending.confirmedPort < 1024 || pending.confirmedPort > 65535))
47
+ throw Error('Unfinished tunnel setup record is invalid');
48
+ return pending;
49
+ }
50
+ export const DEFAULT_TUNNEL_PORT = 49_271;
51
+ /**
52
+ * The port the web service must bind when the account can no longer retarget
53
+ * the tunnel. Unknown when a different port was requested and never confirmed.
54
+ */
55
+ export function requiredPortAfterExpiry(pending) {
56
+ if (pending.confirmedPort !== undefined)
57
+ return pending.confirmedPort;
58
+ const other = [...new Set(pending.requestedPorts ?? [])].filter(port => port !== DEFAULT_TUNNEL_PORT);
59
+ if (!other.length)
60
+ return DEFAULT_TUNNEL_PORT;
61
+ throw Error(`The setup window has expired and it is unknown whether the tunnel points at port ${[...other, DEFAULT_TUNNEL_PORT].join(' or ')}: the account's answer to a port request was not received. This setup cannot be finished automatically`);
62
+ }
@@ -0,0 +1,2 @@
1
+ /** Optional private socket directory for deeply nested, isolated Fleet homes. */
2
+ export declare function socketPath(logicalPath: string): string;
@@ -0,0 +1,22 @@
1
+ import { createHash } from 'node:crypto';
2
+ import { lstatSync, mkdirSync } from 'node:fs';
3
+ import { isAbsolute, join, resolve } from 'node:path';
4
+ /** Optional private socket directory for deeply nested, isolated Fleet homes. */
5
+ export function socketPath(logicalPath) {
6
+ const root = process.env.OURS_FLEET_SOCKET_ROOT;
7
+ if (!root) {
8
+ if (Buffer.byteLength(logicalPath) > 103)
9
+ throw new Error('Unix socket path is too long; configure a short private OURS_FLEET_SOCKET_ROOT');
10
+ return logicalPath;
11
+ }
12
+ if (!isAbsolute(root) || resolve(root) !== root)
13
+ throw new Error('OURS_FLEET_SOCKET_ROOT must be an absolute normalized path');
14
+ mkdirSync(root, { recursive: true, mode: 0o700 });
15
+ const stat = lstatSync(root);
16
+ if (!stat.isDirectory() || stat.isSymbolicLink() || stat.uid !== process.getuid?.() || (stat.mode & 0o777) !== 0o700)
17
+ throw new Error('OURS_FLEET_SOCKET_ROOT must be an owner-only 0700 directory');
18
+ const path = join(root, createHash('sha256').update(resolve(logicalPath)).digest('hex').slice(0, 24) + '.sock');
19
+ if (Buffer.byteLength(path) > 103)
20
+ throw new Error('OURS_FLEET_SOCKET_ROOT is too long');
21
+ return path;
22
+ }