@ours.network/fleet 1.2.0-nightly.9 → 2.0.1

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 (429) hide show
  1. package/README.md +280 -68
  2. package/dist/account-origin.d.ts +5 -0
  3. package/dist/account-origin.js +31 -0
  4. package/dist/agent-ours/bridge.js +26 -9
  5. package/dist/agent-ours/correspondence.d.ts +10 -0
  6. package/dist/agent-ours/correspondence.js +1 -0
  7. package/dist/agent-ours/file-rpc.d.ts +12 -0
  8. package/dist/agent-ours/file-rpc.js +18 -4
  9. package/dist/agent-ours/harness.js +9 -1
  10. package/dist/agent-ours/mcp-endpoint.d.ts +22 -0
  11. package/dist/agent-ours/mcp-endpoint.js +101 -25
  12. package/dist/agent-ours/runtime.d.ts +9 -1
  13. package/dist/agent-ours/runtime.js +50 -2
  14. package/dist/agent-ours/service.d.ts +27 -1
  15. package/dist/agent-ours/service.js +107 -13
  16. package/dist/application/codex-models.d.ts +15 -0
  17. package/dist/application/codex-models.js +56 -0
  18. package/dist/application/conversation-history.d.ts +31 -0
  19. package/dist/application/conversation-history.js +339 -0
  20. package/dist/application/conversation-role-observer.d.ts +62 -0
  21. package/dist/application/conversation-role-observer.js +206 -0
  22. package/dist/application/fleet-query-service.d.ts +1 -1
  23. package/dist/application/fleet-query-service.js +3 -3
  24. package/dist/application/log-service.js +8 -8
  25. package/dist/application/model-catalog.d.ts +1 -1
  26. package/dist/application/model-catalog.js +5 -2
  27. package/dist/application/preset-provenance.d.ts +19 -0
  28. package/dist/application/preset-provenance.js +59 -0
  29. package/dist/application/resource-observer.d.ts +43 -0
  30. package/dist/application/resource-observer.js +323 -0
  31. package/dist/application/role-creation-service.js +3 -2
  32. package/dist/application/role-removal-service.js +17 -3
  33. package/dist/application/role-repository.d.ts +3 -1
  34. package/dist/application/role-repository.js +89 -54
  35. package/dist/application/room-layout-definitions.d.ts +45 -0
  36. package/dist/application/room-layout-definitions.js +130 -0
  37. package/dist/application/session-control.d.ts +63 -17
  38. package/dist/application/session-control.js +221 -23
  39. package/dist/application/supervisor-ours-tools.js +5 -11
  40. package/dist/application/task-layouts.d.ts +91 -0
  41. package/dist/application/task-layouts.js +261 -0
  42. package/dist/application/task-room-service.d.ts +101 -3
  43. package/dist/application/task-room-service.js +286 -33
  44. package/dist/application/types.d.ts +2 -0
  45. package/dist/atomic-file.d.ts +34 -1
  46. package/dist/atomic-file.js +161 -18
  47. package/dist/briefing.d.ts +5 -1
  48. package/dist/briefing.js +138 -31
  49. package/dist/build-info.json +10 -5
  50. package/dist/capabilities.d.ts +9 -1
  51. package/dist/capabilities.js +13 -0
  52. package/dist/cli.js +391 -38
  53. package/dist/client-profile.d.ts +5 -17
  54. package/dist/client-profile.js +4 -127
  55. package/dist/config.d.ts +45 -0
  56. package/dist/config.js +57 -6
  57. package/dist/creation.d.ts +1 -1
  58. package/dist/creation.js +4 -4
  59. package/dist/daemon-recovery.d.ts +14 -9
  60. package/dist/daemon-recovery.js +124 -115
  61. package/dist/docs.d.ts +1 -1
  62. package/dist/docs.js +402 -20
  63. package/dist/doctor.d.ts +4 -1
  64. package/dist/doctor.js +75 -60
  65. package/dist/erased-resources.d.ts +9 -0
  66. package/dist/erased-resources.js +87 -0
  67. package/dist/file-delivery/reader.d.ts +15 -0
  68. package/dist/file-delivery/reader.js +63 -0
  69. package/dist/file-delivery/store.d.ts +15 -0
  70. package/dist/file-delivery/store.js +124 -0
  71. package/dist/file-delivery/types.d.ts +19 -0
  72. package/dist/file-delivery/types.js +1 -0
  73. package/dist/fleet-command-audit.d.ts +13 -0
  74. package/dist/fleet-command-audit.js +31 -9
  75. package/dist/harness/agent-session.d.ts +4 -0
  76. package/dist/harness/claude-code-session.js +1 -1
  77. package/dist/harness/claude-code.js +21 -11
  78. package/dist/harness/claude-runtime.d.ts +12 -0
  79. package/dist/harness/claude-runtime.js +43 -0
  80. package/dist/harness/codex-app-server-proxy.d.ts +4 -1
  81. package/dist/harness/codex-app-server-proxy.js +41 -5
  82. package/dist/harness/codex-reasoning.d.ts +5 -0
  83. package/dist/harness/codex-reasoning.js +12 -0
  84. package/dist/harness/codex-runtime.d.ts +11 -1
  85. package/dist/harness/codex-runtime.js +31 -4
  86. package/dist/harness/codex-session.js +2 -2
  87. package/dist/harness/codex.js +24 -12
  88. package/dist/init-wizard.d.ts +2 -0
  89. package/dist/init-wizard.js +38 -19
  90. package/dist/isolation/policy.js +6 -1
  91. package/dist/isolation/types.d.ts +5 -0
  92. package/dist/managed-cli.d.ts +253 -0
  93. package/dist/managed-cli.js +923 -0
  94. package/dist/managed-recovery.d.ts +13 -0
  95. package/dist/managed-recovery.js +31 -0
  96. package/dist/minimal-setup.d.ts +2 -0
  97. package/dist/minimal-setup.js +44 -0
  98. package/dist/monitor.d.ts +9 -34
  99. package/dist/monitor.js +90 -153
  100. package/dist/notifications/fleet-producer.d.ts +31 -0
  101. package/dist/notifications/fleet-producer.js +351 -0
  102. package/dist/notifications/outbox.d.ts +59 -0
  103. package/dist/notifications/outbox.js +199 -0
  104. package/dist/notifications/target-cleanup.d.ts +16 -0
  105. package/dist/notifications/target-cleanup.js +75 -0
  106. package/dist/notifications/task-producer.d.ts +20 -0
  107. package/dist/notifications/task-producer.js +100 -0
  108. package/dist/ops.d.ts +2 -0
  109. package/dist/ops.js +20 -3
  110. package/dist/owner-channel/channel.js +17 -6
  111. package/dist/owner-channel/commands.d.ts +4 -0
  112. package/dist/owner-channel/commands.js +8 -0
  113. package/dist/owner-channel/lifecycle-outbox.js +4 -3
  114. package/dist/owner-channel/ours-client.js +2 -17
  115. package/dist/paths.d.ts +5 -0
  116. package/dist/paths.js +5 -0
  117. package/dist/preset-migration.js +55 -0
  118. package/dist/process-incarnation.d.ts +23 -0
  119. package/dist/process-incarnation.js +42 -0
  120. package/dist/reminders/manager.d.ts +75 -0
  121. package/dist/reminders/manager.js +278 -0
  122. package/dist/reminders/runtime.d.ts +27 -0
  123. package/dist/reminders/runtime.js +32 -0
  124. package/dist/reminders/schedule.d.ts +37 -0
  125. package/dist/reminders/schedule.js +104 -0
  126. package/dist/reminders/store.d.ts +341 -0
  127. package/dist/reminders/store.js +762 -0
  128. package/dist/reminders/tools.d.ts +20 -0
  129. package/dist/reminders/tools.js +114 -0
  130. package/dist/resolved-plan.js +1 -0
  131. package/dist/rooms-tasks/add-member.d.ts +48 -0
  132. package/dist/rooms-tasks/add-member.js +84 -0
  133. package/dist/rooms-tasks/archived-absence.d.ts +4 -0
  134. package/dist/rooms-tasks/archived-absence.js +11 -0
  135. package/dist/rooms-tasks/cli.js +182 -5
  136. package/dist/rooms-tasks/close.d.ts +5 -2
  137. package/dist/rooms-tasks/close.js +224 -36
  138. package/dist/rooms-tasks/config.d.ts +1 -1
  139. package/dist/rooms-tasks/config.js +11 -4
  140. package/dist/rooms-tasks/cowork-adapter.d.ts +12 -5
  141. package/dist/rooms-tasks/cowork-adapter.js +15 -121
  142. package/dist/rooms-tasks/cowork-http.js +56 -6
  143. package/dist/rooms-tasks/deletion.d.ts +1 -1
  144. package/dist/rooms-tasks/deletion.js +66 -10
  145. package/dist/rooms-tasks/erasure.d.ts +6 -0
  146. package/dist/rooms-tasks/erasure.js +173 -0
  147. package/dist/rooms-tasks/external-worker.d.ts +1 -1
  148. package/dist/rooms-tasks/external-worker.js +3 -4
  149. package/dist/rooms-tasks/layout-binding-client.d.ts +11 -0
  150. package/dist/rooms-tasks/layout-binding-client.js +56 -0
  151. package/dist/rooms-tasks/layout-binding-grants.d.ts +21 -0
  152. package/dist/rooms-tasks/layout-binding-grants.js +128 -0
  153. package/dist/rooms-tasks/layout-cli.d.ts +4 -0
  154. package/dist/rooms-tasks/layout-cli.js +107 -0
  155. package/dist/rooms-tasks/layout-config.d.ts +11 -0
  156. package/dist/rooms-tasks/layout-config.js +89 -0
  157. package/dist/rooms-tasks/layout-control.d.ts +21 -0
  158. package/dist/rooms-tasks/layout-control.js +52 -0
  159. package/dist/rooms-tasks/layout-member-retirement.d.ts +10 -0
  160. package/dist/rooms-tasks/layout-member-retirement.js +90 -0
  161. package/dist/rooms-tasks/layout-service.d.ts +43 -0
  162. package/dist/rooms-tasks/layout-service.js +203 -0
  163. package/dist/rooms-tasks/layout.d.ts +121 -0
  164. package/dist/rooms-tasks/layout.js +286 -0
  165. package/dist/rooms-tasks/legacy-task-member.d.ts +19 -0
  166. package/dist/rooms-tasks/legacy-task-member.js +116 -0
  167. package/dist/rooms-tasks/live-readiness.d.ts +18 -0
  168. package/dist/rooms-tasks/live-readiness.js +85 -0
  169. package/dist/rooms-tasks/member-ownership.d.ts +5 -0
  170. package/dist/rooms-tasks/member-ownership.js +40 -0
  171. package/dist/rooms-tasks/member-startup.d.ts +1 -0
  172. package/dist/rooms-tasks/member-startup.js +2 -0
  173. package/dist/rooms-tasks/provision.d.ts +3 -0
  174. package/dist/rooms-tasks/provision.js +101 -49
  175. package/dist/rooms-tasks/recovery.d.ts +19 -0
  176. package/dist/rooms-tasks/recovery.js +110 -0
  177. package/dist/rooms-tasks/room-state.d.ts +4 -2
  178. package/dist/rooms-tasks/room-state.js +47 -8
  179. package/dist/rooms-tasks/task-review.d.ts +113 -0
  180. package/dist/rooms-tasks/task-review.js +561 -0
  181. package/dist/rooms-tasks/task-service-retirement.d.ts +11 -0
  182. package/dist/rooms-tasks/task-service-retirement.js +21 -0
  183. package/dist/rooms-tasks/task-state.d.ts +51 -5
  184. package/dist/rooms-tasks/task-state.js +358 -50
  185. package/dist/rooms-tasks/task-tools.d.ts +35 -0
  186. package/dist/rooms-tasks/task-tools.js +134 -0
  187. package/dist/rooms-tasks/types.d.ts +78 -0
  188. package/dist/rooms-tasks/workspace-artifacts.d.ts +6 -0
  189. package/dist/rooms-tasks/workspace-artifacts.js +93 -0
  190. package/dist/rooms-tasks/workspace.d.ts +35 -0
  191. package/dist/rooms-tasks/workspace.js +325 -0
  192. package/dist/runner.d.ts +23 -4
  193. package/dist/runner.js +409 -61
  194. package/dist/session/acp.d.ts +75 -2
  195. package/dist/session/acp.js +345 -82
  196. package/dist/session/arbiter.d.ts +98 -0
  197. package/dist/session/arbiter.js +272 -3
  198. package/dist/session/codex-app-server.d.ts +45 -1
  199. package/dist/session/codex-app-server.js +225 -32
  200. package/dist/session/control.d.ts +17 -3
  201. package/dist/session/control.js +219 -23
  202. package/dist/session/conversation-normalizer.d.ts +2 -1
  203. package/dist/session/conversation-normalizer.js +1 -1
  204. package/dist/session/conversation-observation.d.ts +25 -0
  205. package/dist/session/conversation-observation.js +100 -0
  206. package/dist/session/conversation-replay.d.ts +50 -0
  207. package/dist/session/conversation-replay.js +203 -0
  208. package/dist/session/conversation-store.d.ts +15 -1
  209. package/dist/session/conversation-store.js +213 -26
  210. package/dist/session/conversation-types.d.ts +57 -4
  211. package/dist/session/prompt-batch.d.ts +79 -0
  212. package/dist/session/prompt-batch.js +120 -0
  213. package/dist/session/task-notice.d.ts +14 -0
  214. package/dist/session/task-notice.js +13 -0
  215. package/dist/session/types.d.ts +42 -1
  216. package/dist/setup-tunnel-preflight.d.ts +34 -0
  217. package/dist/setup-tunnel-preflight.js +62 -0
  218. package/dist/socket-path.d.ts +2 -0
  219. package/dist/socket-path.js +22 -0
  220. package/dist/spawn.d.ts +5 -1
  221. package/dist/spawn.js +40 -4
  222. package/dist/startup-prompt.d.ts +15 -0
  223. package/dist/startup-prompt.js +71 -0
  224. package/dist/startup-readiness.d.ts +20 -1
  225. package/dist/startup-readiness.js +66 -3
  226. package/dist/startup-telemetry.d.ts +16 -0
  227. package/dist/startup-telemetry.js +46 -0
  228. package/dist/subscriptions/cli.d.ts +91 -0
  229. package/dist/subscriptions/cli.js +223 -0
  230. package/dist/subscriptions/launch.d.ts +37 -0
  231. package/dist/subscriptions/launch.js +93 -0
  232. package/dist/subscriptions/login.d.ts +77 -0
  233. package/dist/subscriptions/login.js +215 -0
  234. package/dist/subscriptions/observed.d.ts +7 -0
  235. package/dist/subscriptions/observed.js +44 -0
  236. package/dist/subscriptions/probe.d.ts +10 -0
  237. package/dist/subscriptions/probe.js +73 -0
  238. package/dist/subscriptions/service.d.ts +102 -0
  239. package/dist/subscriptions/service.js +364 -0
  240. package/dist/subscriptions/store.d.ts +71 -0
  241. package/dist/subscriptions/store.js +143 -0
  242. package/dist/supervisor/adoption.d.ts +8 -0
  243. package/dist/supervisor/adoption.js +78 -0
  244. package/dist/supervisor/catalog.d.ts +62 -0
  245. package/dist/supervisor/catalog.js +254 -0
  246. package/dist/supervisor/fleet.d.ts +8 -0
  247. package/dist/supervisor/fleet.js +89 -0
  248. package/dist/supervisor/index.js +3 -6
  249. package/dist/supervisor/launchd.d.ts +1 -1
  250. package/dist/supervisor/launchd.js +26 -13
  251. package/dist/supervisor/legacy.d.ts +35 -0
  252. package/dist/supervisor/legacy.js +524 -0
  253. package/dist/supervisor/manager.d.ts +15 -0
  254. package/dist/supervisor/manager.js +270 -0
  255. package/dist/supervisor/registration-refresh.d.ts +45 -0
  256. package/dist/supervisor/registration-refresh.js +266 -0
  257. package/dist/supervisor/scope.d.ts +4 -0
  258. package/dist/supervisor/scope.js +10 -0
  259. package/dist/supervisor/systemd.d.ts +2 -2
  260. package/dist/supervisor/systemd.js +18 -37
  261. package/dist/task-supervision.d.ts +9 -0
  262. package/dist/task-supervision.js +145 -0
  263. package/dist/temp-idle.d.ts +17 -0
  264. package/dist/temp-idle.js +42 -0
  265. package/dist/temp-lifecycle.d.ts +23 -8
  266. package/dist/temp-lifecycle.js +123 -66
  267. package/dist/temp-supervisor-recovery.d.ts +1 -1
  268. package/dist/temp-supervisor-recovery.js +2 -2
  269. package/dist/tunnel-setup-completion.d.ts +7 -0
  270. package/dist/tunnel-setup-completion.js +13 -0
  271. package/dist/tunnel-setup-input.d.ts +19 -0
  272. package/dist/tunnel-setup-input.js +130 -0
  273. package/dist/watchdog/query.d.ts +3 -1
  274. package/dist/watchdog/query.js +2 -1
  275. package/dist/web/agent-attachments.d.ts +29 -0
  276. package/dist/web/agent-attachments.js +162 -0
  277. package/dist/web/auth.d.ts +65 -2
  278. package/dist/web/auth.js +201 -11
  279. package/dist/web/available-port.d.ts +2 -0
  280. package/dist/web/available-port.js +23 -0
  281. package/dist/web/control.d.ts +3 -3
  282. package/dist/web/control.js +10 -8
  283. package/dist/web/conversation-stream-codec.d.ts +23 -0
  284. package/dist/web/conversation-stream-codec.js +110 -0
  285. package/dist/web/conversation-stream.d.ts +26 -0
  286. package/dist/web/conversation-stream.js +239 -0
  287. package/dist/web/events.d.ts +2 -0
  288. package/dist/web/events.js +9 -0
  289. package/dist/web/fleet-config-service.d.ts +7 -1
  290. package/dist/web/fleet-config-service.js +61 -16
  291. package/dist/web/prefix-gateway.d.ts +32 -0
  292. package/dist/web/prefix-gateway.js +248 -0
  293. package/dist/web/resource-views.d.ts +138 -0
  294. package/dist/web/resource-views.js +519 -0
  295. package/dist/web/runtime.d.ts +3 -1
  296. package/dist/web/runtime.js +132 -34
  297. package/dist/web/server.d.ts +19 -0
  298. package/dist/web/server.js +893 -71
  299. package/dist/web/service.d.ts +11 -2
  300. package/dist/web/service.js +30 -4
  301. package/dist/web/workspace-devices.d.ts +107 -0
  302. package/dist/web/workspace-devices.js +277 -0
  303. package/dist/web/workspace-tunnel.d.ts +1 -0
  304. package/dist/web/workspace-tunnel.js +34 -0
  305. package/dist/workspace-enrollment.d.ts +83 -0
  306. package/dist/workspace-enrollment.js +605 -0
  307. package/dist/workspace-replacement.d.ts +66 -0
  308. package/dist/workspace-replacement.js +238 -0
  309. package/docs/acp-files.md +146 -0
  310. package/docs/conversation-stream.md +216 -0
  311. package/docs/fleet-migration.md +54 -0
  312. package/docs/live-voice.md +13 -0
  313. package/docs/room-layouts.md +227 -0
  314. package/docs/self-reminders.md +374 -0
  315. package/docs/task-progress-review.md +143 -0
  316. package/docs/task-workspaces.md +225 -0
  317. package/docs/validation/managed-cli-permissions.md +421 -0
  318. package/examples/room-layouts/fresh.yaml +27 -0
  319. package/examples/room-layouts/scoped.yaml +21 -0
  320. package/examples/room-layouts/shared.yaml +19 -0
  321. package/package.json +25 -20
  322. package/presets/README.md +78 -0
  323. package/presets/brain-catalog.json +8 -2
  324. package/presets/fleet/agent_templates/Critic.yaml +1 -1
  325. package/presets/fleet/agent_templates/Developer.yaml +1 -1
  326. package/presets/fleet/agent_templates/Engineer.yaml +5 -0
  327. package/presets/fleet/agent_templates/LocalCoordinator.yaml +6 -3
  328. package/presets/fleet/agents/FleetCoordinator.yaml +1 -1
  329. package/presets/fleet/brains/claude-fable-5-1-high.yaml +5 -0
  330. package/presets/fleet/brains/claude-fable-5-1-low.yaml +5 -0
  331. package/presets/fleet/brains/claude-fable-5-1-max.yaml +5 -0
  332. package/presets/fleet/brains/claude-fable-5-1-medium.yaml +5 -0
  333. package/presets/fleet/brains/claude-fable-5-1-xhigh.yaml +5 -0
  334. package/presets/fleet/brains/claude-fable-5-high.yaml +1 -1
  335. package/presets/fleet/brains/claude-fable-5-low.yaml +1 -1
  336. package/presets/fleet/brains/claude-fable-5-max.yaml +1 -1
  337. package/presets/fleet/brains/claude-fable-5-medium.yaml +1 -1
  338. package/presets/fleet/brains/claude-fable-5-xhigh.yaml +1 -1
  339. package/presets/fleet/brains/claude-haiku-4-5-20251001-default.yaml +4 -0
  340. package/presets/fleet/brains/claude-opus-5-5-high.yaml +5 -0
  341. package/presets/fleet/brains/claude-opus-5-5-low.yaml +5 -0
  342. package/presets/fleet/brains/claude-opus-5-5-max.yaml +5 -0
  343. package/presets/fleet/brains/claude-opus-5-5-medium.yaml +5 -0
  344. package/presets/fleet/brains/claude-opus-5-5-xhigh.yaml +5 -0
  345. package/presets/fleet/brains/claude-opus-5-high.yaml +1 -1
  346. package/presets/fleet/brains/claude-opus-5-low.yaml +1 -1
  347. package/presets/fleet/brains/claude-opus-5-max.yaml +1 -1
  348. package/presets/fleet/brains/claude-opus-5-medium.yaml +1 -1
  349. package/presets/fleet/brains/claude-opus-5-xhigh.yaml +1 -1
  350. package/presets/fleet/brains/claude-sonnet-5-high.yaml +1 -1
  351. package/presets/fleet/brains/claude-sonnet-5-low.yaml +1 -1
  352. package/presets/fleet/brains/claude-sonnet-5-max.yaml +1 -1
  353. package/presets/fleet/brains/claude-sonnet-5-medium.yaml +1 -1
  354. package/presets/fleet/brains/claude-sonnet-5-xhigh.yaml +1 -1
  355. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-high.yaml +1 -1
  356. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-low.yaml +1 -1
  357. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-medium.yaml +1 -1
  358. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-xhigh.yaml +1 -1
  359. package/presets/fleet/brains/codex-gpt-5-4-high.yaml +1 -1
  360. package/presets/fleet/brains/codex-gpt-5-4-low.yaml +1 -1
  361. package/presets/fleet/brains/codex-gpt-5-4-medium.yaml +1 -1
  362. package/presets/fleet/brains/codex-gpt-5-4-mini-high.yaml +1 -1
  363. package/presets/fleet/brains/codex-gpt-5-4-mini-low.yaml +1 -1
  364. package/presets/fleet/brains/codex-gpt-5-4-mini-medium.yaml +1 -1
  365. package/presets/fleet/brains/codex-gpt-5-4-mini-xhigh.yaml +1 -1
  366. package/presets/fleet/brains/codex-gpt-5-4-xhigh.yaml +1 -1
  367. package/presets/fleet/brains/codex-gpt-5-5-high.yaml +1 -1
  368. package/presets/fleet/brains/codex-gpt-5-5-low.yaml +1 -1
  369. package/presets/fleet/brains/codex-gpt-5-5-medium.yaml +1 -1
  370. package/presets/fleet/brains/codex-gpt-5-5-xhigh.yaml +1 -1
  371. package/presets/fleet/brains/codex-gpt-5-6-luna-high.yaml +1 -1
  372. package/presets/fleet/brains/codex-gpt-5-6-luna-low.yaml +1 -1
  373. package/presets/fleet/brains/codex-gpt-5-6-luna-max.yaml +1 -1
  374. package/presets/fleet/brains/codex-gpt-5-6-luna-medium.yaml +1 -1
  375. package/presets/fleet/brains/codex-gpt-5-6-luna-xhigh.yaml +1 -1
  376. package/presets/fleet/brains/codex-gpt-5-6-sol-high.yaml +1 -1
  377. package/presets/fleet/brains/codex-gpt-5-6-sol-low.yaml +1 -1
  378. package/presets/fleet/brains/codex-gpt-5-6-sol-max.yaml +1 -1
  379. package/presets/fleet/brains/codex-gpt-5-6-sol-medium.yaml +1 -1
  380. package/presets/fleet/brains/codex-gpt-5-6-sol-ultra.yaml +1 -1
  381. package/presets/fleet/brains/codex-gpt-5-6-sol-xhigh.yaml +1 -1
  382. package/presets/fleet/brains/codex-gpt-5-6-terra-high.yaml +1 -1
  383. package/presets/fleet/brains/codex-gpt-5-6-terra-low.yaml +1 -1
  384. package/presets/fleet/brains/codex-gpt-5-6-terra-max.yaml +1 -1
  385. package/presets/fleet/brains/codex-gpt-5-6-terra-medium.yaml +1 -1
  386. package/presets/fleet/brains/codex-gpt-5-6-terra-ultra.yaml +1 -1
  387. package/presets/fleet/brains/codex-gpt-5-6-terra-xhigh.yaml +1 -1
  388. package/presets/fleet/brains/codex-gpt-6-1-sol-high.yaml +5 -0
  389. package/presets/fleet/brains/codex-gpt-6-1-sol-low.yaml +5 -0
  390. package/presets/fleet/brains/codex-gpt-6-1-sol-max.yaml +5 -0
  391. package/presets/fleet/brains/codex-gpt-6-1-sol-medium.yaml +5 -0
  392. package/presets/fleet/brains/codex-gpt-6-1-sol-ultra.yaml +5 -0
  393. package/presets/fleet/brains/codex-gpt-6-1-sol-xhigh.yaml +5 -0
  394. package/presets/fleet/brains/codex-gpt-6-astra-high.yaml +1 -1
  395. package/presets/fleet/brains/codex-gpt-6-astra-low.yaml +1 -1
  396. package/presets/fleet/brains/codex-gpt-6-astra-max.yaml +1 -1
  397. package/presets/fleet/brains/codex-gpt-6-astra-medium.yaml +1 -1
  398. package/presets/fleet/brains/codex-gpt-6-astra-ultra.yaml +1 -1
  399. package/presets/fleet/brains/codex-gpt-6-astra-xhigh.yaml +1 -1
  400. package/presets/fleet/brains/codex-gpt-6-luna-high.yaml +5 -0
  401. package/presets/fleet/brains/codex-gpt-6-luna-low.yaml +5 -0
  402. package/presets/fleet/brains/codex-gpt-6-luna-max.yaml +5 -0
  403. package/presets/fleet/brains/codex-gpt-6-luna-medium.yaml +5 -0
  404. package/presets/fleet/brains/codex-gpt-6-luna-xhigh.yaml +5 -0
  405. package/presets/fleet/brains/codex-gpt-6-sol-high.yaml +5 -0
  406. package/presets/fleet/brains/codex-gpt-6-sol-low.yaml +5 -0
  407. package/presets/fleet/brains/codex-gpt-6-sol-max.yaml +5 -0
  408. package/presets/fleet/brains/codex-gpt-6-sol-medium.yaml +5 -0
  409. package/presets/fleet/brains/codex-gpt-6-sol-ultra.yaml +5 -0
  410. package/presets/fleet/brains/codex-gpt-6-sol-xhigh.yaml +5 -0
  411. package/presets/fleet/roles/Coordinator.yaml +21 -13
  412. package/presets/fleet/roles/Critic.yaml +24 -0
  413. package/presets/fleet/roles/Developer.yaml +32 -0
  414. package/presets/fleet/roles/Engineer.yaml +72 -0
  415. package/presets/fleet/roles/LocalCoordinator.yaml +17 -0
  416. package/presets/fleet/room_layouts/engineering.yaml +25 -0
  417. package/presets/fleet/room_layouts/pair.yaml +24 -0
  418. package/presets/fleet/room_layouts/single.yaml +17 -0
  419. package/presets/fleet/room_layouts/team.yaml +30 -0
  420. package/presets/fleet/room_templates/engineering.yaml +19 -0
  421. package/presets/manifest.json +2 -2
  422. package/dist/web-app/assets/index-59GF-gsZ.css +0 -1
  423. package/dist/web-app/assets/index-C0h3ALvs.js +0 -10
  424. package/dist/web-app/icons/ours-fleet-maskable.svg +0 -4
  425. package/dist/web-app/icons/ours-fleet.svg +0 -4
  426. package/dist/web-app/index.html +0 -17
  427. package/dist/web-app/manifest.webmanifest +0 -15
  428. package/dist/web-app/offline.html +0 -18
  429. package/dist/web-app/sw.js +0 -51
@@ -1,14 +1,16 @@
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';
4
5
  import { join } from 'node:path';
5
6
  import { SessionControlError } from './types.js';
7
+ import { MAX_CONVERSATION_RECORD_BYTES, startConversationReplay } from './conversation-replay.js';
6
8
  import { validateFleetAuditBegin, validateFleetAuditFinish, validateFleetAuditPresentations, } from '../fleet-command-audit.js';
7
9
  import { interruptSession, queueSessionPrompt, respondSessionPermission, respondSessionPermissionV2, } from '../application/session-mutations.js';
8
10
  const MAX_LINE_BYTES = 64 * 1024;
9
11
  /** Commands that require protocol version 3. */
10
12
  const V3_COMMANDS = new Set([
11
- 'conversation_page', 'conversation_follow', 'submit_prompt_v2', 'interrupt_v2',
13
+ 'conversation_page', 'conversation_follow', 'submit_prompt_v2', 'submit_voice_prompt', 'interrupt_v2', 'interrupt_voice_v2',
12
14
  'respond_permission_v2',
13
15
  ]);
14
16
  /** The one retained-range projection shared by polling and live-follow admission. */
@@ -99,7 +101,7 @@ export function oversightTaxonomy(name = '<Name>') {
99
101
  export function oversightTaxonomyLines(name = '<Name>') {
100
102
  return oversightTaxonomy(name).map(r => `- **${r.result}** — ${r.meaning} → ${r.action}`);
101
103
  }
102
- export const controlSocketPath = (stateDir) => join(stateDir, '.control.sock');
104
+ export const controlSocketPath = (stateDir) => privateSocketPath(join(stateDir, '.control.sock'));
103
105
  export const controlTokenPath = (stateDir) => join(stateDir, '.control-token');
104
106
  function sameToken(actual, supplied) {
105
107
  const a = Buffer.from(actual);
@@ -116,9 +118,13 @@ export class RoleControlServer {
116
118
  sockets = new Set();
117
119
  /** Interrupt idempotency: same command id returns the first receipt. */
118
120
  interruptCommands = new Map();
121
+ correspondence;
122
+ setCorrespondence(value) { this.correspondence = value; }
119
123
  ownerChannel;
120
124
  loopManager;
121
125
  reloadConfig;
126
+ layoutControl;
127
+ setLayoutControl(handler) { this.layoutControl = handler; }
122
128
  fleetSpawner;
123
129
  fleetAuditor;
124
130
  constructor(stateDir, session, log) {
@@ -189,6 +195,12 @@ export class RoleControlServer {
189
195
  continue;
190
196
  void this.handle(line, socket).then(follow => {
191
197
  if (follow) {
198
+ if (socket.destroyed) {
199
+ follow.stop();
200
+ if (follow.controller)
201
+ this.session.setControllerAttached(false);
202
+ return;
203
+ }
192
204
  if (unsubscribe) {
193
205
  unsubscribe();
194
206
  if (controllerAttached)
@@ -235,7 +247,11 @@ export class RoleControlServer {
235
247
  protocolVersion: this.session.conversationPage ? 3 : 2,
236
248
  features: [
237
249
  'events_since', 'observer_follow', 'retained_range',
250
+ ...(this.correspondence ? ['agent_correspondence'] : []),
238
251
  ...(this.session.conversationPage ? ['conversation_v3'] : []),
252
+ ...this.observationFeatures(),
253
+ ...(this.session.submitPromptBrowser ? ['generation_bound_prompts'] : []),
254
+ ...(this.session.liveVoiceSupported ? ['idle_bound_prompts', 'targeted_voice_interrupt'] : []),
239
255
  ...(this.session.capabilities?.steering ? ['steering'] : []),
240
256
  ...(this.session.capabilities?.permissions ? ['permissions'] : []),
241
257
  ...(this.session.capabilities?.messagePhases ? ['message_phases'] : []),
@@ -308,6 +324,13 @@ export class RoleControlServer {
308
324
  });
309
325
  return;
310
326
  }
327
+ case 'layout_control': {
328
+ if (request.version !== 2 || !request.layout || !this.layoutControl)
329
+ throw new SessionControlError('rejected', 'layout control unavailable');
330
+ const result = await this.layoutControl(request.layout);
331
+ this.write(socket, { version: 1, id: request.id, ok: true, result });
332
+ return;
333
+ }
311
334
  case 'fleet_spawn': {
312
335
  if (request.version !== 2 || !request.spawn || typeof request.spawn.name !== 'string')
313
336
  throw new SessionControlError('rejected', 'version 2 and typed spawn options are required');
@@ -362,6 +385,23 @@ export class RoleControlServer {
362
385
  this.write(socket, { version: 1, id: request.id, ok: true, result: { delivered: true } });
363
386
  return;
364
387
  }
388
+ case 'agent_contacts':
389
+ case 'agent_history': {
390
+ if (!this.correspondence)
391
+ throw new SessionControlError('rejected', 'Supervisor correspondence API is unavailable; this session needs an updated supervisor.');
392
+ const query = request.agentHistory;
393
+ 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))))
394
+ throw new SessionControlError('rejected', 'Valid peer_cid, limit (1–200) and before_seq are required.');
395
+ let result;
396
+ try {
397
+ result = request.command === 'agent_contacts' ? await this.correspondence.contacts() : await this.correspondence.history(query);
398
+ }
399
+ catch {
400
+ throw new SessionControlError('rejected', 'Supervisor correspondence read failed; identity or transport is unavailable.');
401
+ }
402
+ this.write(socket, { version: 1, id: request.id, ok: true, result });
403
+ return;
404
+ }
365
405
  case 'owner_channel_manage': {
366
406
  if (!request.ownerChannel || typeof request.ownerChannel.action !== 'string')
367
407
  throw new SessionControlError('rejected', 'owner-channel management action is required');
@@ -395,20 +435,104 @@ export class RoleControlServer {
395
435
  }
396
436
  case 'conversation_page': {
397
437
  this.requireConversation(request);
398
- this.write(socket, {
399
- version: 1, id: request.id, ok: true,
400
- result: this.session.conversationPage({
401
- after: request.after, limit: request.limit,
402
- }),
438
+ const page = this.session.conversationPage({
439
+ after: request.after === undefined ? undefined : String(request.after), limit: request.limit,
403
440
  });
441
+ if (this.session.observeConversation) {
442
+ try {
443
+ const observation = this.session.observeConversation(() => { });
444
+ try {
445
+ if (observation.snapshot.observationFailure) {
446
+ page.snapshot = { ...page.snapshot, historyDegraded: true, observationFailure: 'capture_failed' };
447
+ }
448
+ else
449
+ page.snapshot = observation.snapshot;
450
+ }
451
+ finally {
452
+ observation.unsubscribe();
453
+ }
454
+ }
455
+ catch {
456
+ // Canonical REST recovery remains usable without a live observation.
457
+ // Never substitute a frozen capture/barrier for the current store page.
458
+ page.snapshot = { ...page.snapshot, historyDegraded: true, observationFailure: 'capture_failed' };
459
+ }
460
+ }
461
+ this.write(socket, { version: 1, id: request.id, ok: true, result: page });
404
462
  return;
405
463
  }
406
464
  case 'conversation_follow': {
407
465
  this.requireConversation(request);
466
+ if (request.controller === false && request.expectedGeneration !== undefined) {
467
+ let stopped = false;
468
+ let heartbeat;
469
+ let writing = Promise.resolve();
470
+ let lastQualifyingOutputAt = Date.now();
471
+ const send = (message) => {
472
+ const result = writing.then(async () => {
473
+ if (message.controlHeartbeat === true && Date.now() - lastQualifyingOutputAt < 15_000)
474
+ return;
475
+ await writeMessage(message);
476
+ lastQualifyingOutputAt = Date.now();
477
+ });
478
+ writing = result.catch(() => { });
479
+ return result;
480
+ };
481
+ const writeMessage = async (message) => {
482
+ // Stream one replay record progressively; don't queue a valid 16 MiB record
483
+ // into the 8 MiB pending-output budget. Framing is owned by the transport.
484
+ const bytes = Buffer.from(JSON.stringify({ version: 1, ...message }) + '\n');
485
+ for (let offset = 0; offset < bytes.length; offset += 64 * 1024) {
486
+ if (stopped || socket.destroyed)
487
+ throw new Error('conversation socket closed');
488
+ if (!socket.write(bytes.subarray(offset, offset + 64 * 1024))) {
489
+ await new Promise((resolve, reject) => {
490
+ const cleanup = () => { socket.off('drain', drained); socket.off('close', ended); socket.off('error', failed); };
491
+ const drained = () => { cleanup(); resolve(); };
492
+ const ended = () => { cleanup(); reject(new Error('conversation socket closed')); };
493
+ const failed = (error) => { cleanup(); reject(error); };
494
+ socket.once('drain', drained);
495
+ socket.once('close', ended);
496
+ socket.once('error', failed);
497
+ });
498
+ }
499
+ }
500
+ };
501
+ const follow = startConversationReplay(this.session, {
502
+ expectedGeneration: request.expectedGeneration, after: request.after === undefined ? 0 : Number(request.after), controller: false,
503
+ }, {
504
+ replay: page => send({ conversationReplay: page }),
505
+ ready: ready => send({ conversationReady: ready }),
506
+ event: event => send({ conversationEvent: event }),
507
+ state: state => send({ conversationState: state }),
508
+ close: reason => {
509
+ stopped = true;
510
+ if (heartbeat)
511
+ clearInterval(heartbeat);
512
+ if (reason && !socket.destroyed)
513
+ socket.end(JSON.stringify({ version: 1, id: request.id, ok: false, error: reason }) + '\n');
514
+ },
515
+ });
516
+ const stop = () => { stopped = true; if (heartbeat)
517
+ clearInterval(heartbeat); follow.close(); };
518
+ let heartbeatPending = false;
519
+ if (!stopped) {
520
+ heartbeat = setInterval(() => {
521
+ if (heartbeatPending || stopped)
522
+ return;
523
+ heartbeatPending = true;
524
+ void send({ controlHeartbeat: true }).catch(() => { stop(); socket.destroy(); })
525
+ .finally(() => { heartbeatPending = false; });
526
+ }, 15_000);
527
+ heartbeat.unref();
528
+ }
529
+ socket.once('close', stop);
530
+ return { controller: false, stop: () => { socket.off('close', stop); stop(); } };
531
+ }
408
532
  this.write(socket, {
409
533
  version: 1, id: request.id, ok: true,
410
534
  result: this.session.conversationPage({
411
- after: request.after, limit: request.limit,
535
+ after: request.after === undefined ? undefined : String(request.after), limit: request.limit,
412
536
  }),
413
537
  });
414
538
  // A live conversation view is an attached controller: permission
@@ -421,7 +545,10 @@ export class RoleControlServer {
421
545
  socket.write(JSON.stringify({ version: 1, conversationEvent: event }) + '\n');
422
546
  }) };
423
547
  }
548
+ case 'submit_voice_prompt':
424
549
  case 'submit_prompt_v2': {
550
+ if (request.command === 'submit_voice_prompt' && !request.expectedSessionGeneration?.trim())
551
+ throw new SessionControlError('rejected', 'expectedSessionGeneration is required');
425
552
  this.requireConversation(request);
426
553
  if (!request.commandId?.trim() || !request.text?.trim() || !request.actor?.trim()
427
554
  || request.source !== 'owner_admin_console')
@@ -432,6 +559,7 @@ export class RoleControlServer {
432
559
  const receipt = await this.session.submitPromptBrowser({
433
560
  commandId: request.commandId, text: request.text,
434
561
  source: 'owner_admin_console', actorBrowserSession: request.actor,
562
+ expectedSessionGeneration: request.expectedSessionGeneration, requireIdle: request.requireIdle === true,
435
563
  });
436
564
  this.write(socket, { version: 1, id: request.id, ok: true, result: receipt });
437
565
  }
@@ -442,6 +570,29 @@ export class RoleControlServer {
442
570
  }
443
571
  return;
444
572
  }
573
+ case 'interrupt_voice_v2': {
574
+ this.requireConversation(request);
575
+ if (!request.commandId?.trim() || !request.expectedSessionGeneration?.trim() || !request.promptId?.trim())
576
+ throw new SessionControlError('rejected', 'commandId, expectedSessionGeneration and promptId are required');
577
+ if (!this.session.interruptPrompt)
578
+ throw new SessionControlError('rejected', 'targeted voice interrupt unavailable');
579
+ const key = 'voice:' + request.commandId;
580
+ const signature = JSON.stringify([request.expectedSessionGeneration, request.promptId]);
581
+ const existing = this.interruptCommands.get(key);
582
+ if (existing) {
583
+ if (existing.signature !== signature)
584
+ throw new SessionControlError('rejected', 'idempotency_conflict: interrupt target changed');
585
+ this.write(socket, { version: 1, id: request.id, ok: true, result: existing.receipt });
586
+ return;
587
+ }
588
+ const outcome = await this.session.interruptPrompt(request.expectedSessionGeneration, request.promptId);
589
+ const receipt = { accepted: true, commandId: request.commandId, ...outcome };
590
+ this.interruptCommands.set(key, { signature, receipt });
591
+ if (this.interruptCommands.size > 200)
592
+ this.interruptCommands.delete(this.interruptCommands.keys().next().value);
593
+ this.write(socket, { version: 1, id: request.id, ok: true, result: receipt });
594
+ return;
595
+ }
445
596
  case 'interrupt_v2': {
446
597
  this.requireConversation(request);
447
598
  if (!request.commandId?.trim())
@@ -501,6 +652,23 @@ export class RoleControlServer {
501
652
  if (!this.session.conversationPage || !this.session.subscribeConversation)
502
653
  throw new SessionControlError('rejected', 'this role has no conversation ledger');
503
654
  }
655
+ observationFeatures() {
656
+ if (!this.session.observeConversation || !this.session.replayConversation || !this.session.subscribeConversation || !this.session.subscribeConversationFailure)
657
+ return [];
658
+ try {
659
+ const observation = this.session.observeConversation(() => { });
660
+ try {
661
+ return observation.snapshot.observationFailure || !this.session.conversationReplayAvailable?.(observation.snapshot.sessionGeneration)
662
+ ? [] : ['conversation_observation', 'control_heartbeat'];
663
+ }
664
+ finally {
665
+ observation.unsubscribe();
666
+ }
667
+ }
668
+ catch {
669
+ return [];
670
+ }
671
+ }
504
672
  write(socket, response) {
505
673
  if (!socket.destroyed)
506
674
  socket.write(JSON.stringify(response) + '\n');
@@ -532,7 +700,13 @@ export async function controlRequest(stateDir, request, timeoutMs = 120_000) {
532
700
  socket.once('error', error => {
533
701
  clearTimeout(timer);
534
702
  const code = error.code;
535
- reject(new SessionControlError(code === 'ENOENT' || code === 'ECONNREFUSED' ? 'control-unavailable' : 'backend', `role control socket: ${error.message}`));
703
+ const permissionHint = code === 'EPERM' || code === 'EACCES'
704
+ ? `; the operating system denied access before '${request.command}' reached the supervisor. `
705
+ + 'Check socket permissions and the command execution sandbox. '
706
+ + 'An existing socket does not prove this process can connect; do not bypass Fleet audit. '
707
+ + 'For managed workspace sessions, see docs/validation/managed-cli-permissions.md.'
708
+ : '';
709
+ reject(new SessionControlError(code === 'ENOENT' || code === 'ECONNREFUSED' ? 'control-unavailable' : 'backend', `role control socket: ${error.message}${permissionHint}`));
536
710
  });
537
711
  socket.on('data', chunk => {
538
712
  buffer += chunk;
@@ -587,32 +761,54 @@ export async function followControl(stateDir, onMessage) {
587
761
  * controller-presence this connection contributed.
588
762
  */
589
763
  export async function followConversation(stateDir, after, onMessage) {
764
+ const options = typeof after === 'object' ? after : undefined;
765
+ options?.signal?.throwIfAborted();
590
766
  const token = readFileSync(controlTokenPath(stateDir), 'utf8').trim();
591
767
  const socket = createConnection(controlSocketPath(stateDir));
592
768
  socket.setEncoding('utf8');
593
- let buffer = '';
769
+ // Admit one maximum-size logical record plus its actual private wrapper.
770
+ // coveredThrough is a safe integer. JSON brackets/envelope are outside the record budget.
771
+ const privateEnvelopeBytes = Math.max(Buffer.byteLength(JSON.stringify({ version: 1, conversationReplay: { events: [], coveredThrough: Number.MAX_SAFE_INTEGER } })), Buffer.byteLength(JSON.stringify({ version: 1, conversationReady: { snapshot: null, coveredThrough: Number.MAX_SAFE_INTEGER } })) - 4, Buffer.byteLength(JSON.stringify({ version: 1, conversationState: null })) - 4, Buffer.byteLength(JSON.stringify({ version: 1, conversationEvent: null })) - 4);
772
+ const limit = MAX_CONVERSATION_RECORD_BYTES + privateEnvelopeBytes;
773
+ let parts = [], bytes = 0;
594
774
  socket.on('data', chunk => {
595
- buffer += chunk;
596
- for (;;) {
597
- const newline = buffer.indexOf('\n');
598
- if (newline < 0)
599
- break;
600
- const line = buffer.slice(0, newline);
601
- buffer = buffer.slice(newline + 1);
602
- if (!line.trim())
603
- continue;
604
- try {
605
- onMessage(JSON.parse(line));
775
+ for (const [index, part] of String(chunk).split('\n').entries()) {
776
+ if (index) {
777
+ const line = parts.join('');
778
+ parts = [];
779
+ bytes = 0;
780
+ if (line.trim()) {
781
+ try {
782
+ onMessage(JSON.parse(line));
783
+ }
784
+ catch {
785
+ if (options) {
786
+ socket.destroy(new Error('invalid conversation private frame'));
787
+ return;
788
+ }
789
+ }
790
+ }
791
+ }
792
+ bytes += Buffer.byteLength(part);
793
+ if (options && bytes > limit) {
794
+ socket.destroy(new Error('conversation private frame exceeds assembly limit'));
795
+ return;
606
796
  }
607
- catch { /* a torn frame ends this stream; the client resyncs over HTTP */ }
797
+ parts.push(part);
608
798
  }
609
799
  });
610
800
  await new Promise((resolve, reject) => {
801
+ const onAbort = () => { socket.destroy(); reject(new Error('conversation follow aborted')); };
802
+ options?.signal?.addEventListener('abort', onAbort, { once: true });
803
+ socket.once('close', () => options?.signal?.removeEventListener('abort', onAbort));
611
804
  socket.once('connect', resolve);
612
805
  socket.once('error', reject);
806
+ if (options?.signal?.aborted)
807
+ onAbort();
613
808
  });
614
809
  socket.write(JSON.stringify({
615
- version: 3, id: randomUUID(), token, command: 'conversation_follow', after,
810
+ version: 3, id: randomUUID(), token, command: 'conversation_follow',
811
+ ...(options ? { expectedGeneration: options.expectedGeneration, after: options.after ?? 0, controller: false } : { after }),
616
812
  }) + '\n');
617
813
  return { socket, close: () => socket.destroy() };
618
814
  }
@@ -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;
@@ -0,0 +1,25 @@
1
+ import type { ConversationObservation as Observation, ConversationObservationState } from './conversation-types.js';
2
+ type Capture = () => Omit<ConversationObservationState, 'stateRevision'>;
3
+ /** Revision/publication only: the backend remains the sole owner of snapshot construction. */
4
+ export declare class ConversationObservation {
5
+ private readonly capture;
6
+ private revision;
7
+ private fingerprint?;
8
+ private current?;
9
+ private scheduled;
10
+ private checkpointDepth;
11
+ private failed;
12
+ private readonly listeners;
13
+ constructor(capture: Capture);
14
+ observe(listener: (state: ConversationObservationState) => void): Observation;
15
+ /** Keep reentrant registration on the preceding coherent snapshot until the
16
+ * owner's synchronous durable append and inventory mutation finish together.
17
+ */
18
+ checkpoint<T>(mutation: () => T): T;
19
+ publish(): void;
20
+ private failCapture;
21
+ private deliver;
22
+ /** Fresh capture after a durable append's synchronous owner mutation completes. */
23
+ scheduleCapture(): void;
24
+ }
25
+ export {};
@@ -0,0 +1,100 @@
1
+ /** Revision/publication only: the backend remains the sole owner of snapshot construction. */
2
+ export class ConversationObservation {
3
+ capture;
4
+ revision = 0;
5
+ fingerprint;
6
+ current;
7
+ scheduled = false;
8
+ checkpointDepth = 0;
9
+ failed = false;
10
+ listeners = new Set();
11
+ constructor(capture) {
12
+ this.capture = capture;
13
+ }
14
+ observe(listener) {
15
+ // Registration and synchronous stable capture have no await gap. Inside an
16
+ // owner checkpoint, publish retains the preceding snapshot and its barrier.
17
+ // A reconnect never resets this generation's revision or implies control.
18
+ this.publish();
19
+ if (!this.current)
20
+ throw new Error('conversation observation unavailable: capture_failed');
21
+ this.listeners.add(listener);
22
+ return {
23
+ snapshot: structuredClone(this.current),
24
+ unsubscribe: () => { this.listeners.delete(listener); },
25
+ };
26
+ }
27
+ /** Keep reentrant registration on the preceding coherent snapshot until the
28
+ * owner's synchronous durable append and inventory mutation finish together.
29
+ */
30
+ checkpoint(mutation) {
31
+ if (this.checkpointDepth === 0)
32
+ this.publish();
33
+ this.checkpointDepth++;
34
+ try {
35
+ return mutation();
36
+ }
37
+ finally {
38
+ this.checkpointDepth--;
39
+ if (this.checkpointDepth === 0)
40
+ this.publish();
41
+ }
42
+ }
43
+ publish() {
44
+ if (this.checkpointDepth > 0 || this.failed)
45
+ return;
46
+ try {
47
+ const captured = this.capture();
48
+ const fingerprint = JSON.stringify(captured);
49
+ if (fingerprint === this.fingerprint)
50
+ return;
51
+ const current = structuredClone({ ...captured, stateRevision: this.revision + 1 });
52
+ this.fingerprint = fingerprint;
53
+ this.current = current;
54
+ this.revision++;
55
+ }
56
+ catch {
57
+ this.failCapture();
58
+ return;
59
+ }
60
+ this.deliver();
61
+ }
62
+ failCapture() {
63
+ if (this.failed)
64
+ return;
65
+ this.failed = true;
66
+ // Retain the entire preceding coherent state and its barrier. Consumers
67
+ // must stop healthy coverage; owner admission/receipt processing continues.
68
+ if (this.current) {
69
+ this.current = { ...this.current, stateRevision: ++this.revision, observationFailure: 'capture_failed' };
70
+ this.deliver();
71
+ }
72
+ }
73
+ deliver() {
74
+ for (const listener of this.listeners) {
75
+ let state;
76
+ try {
77
+ state = structuredClone(this.current);
78
+ }
79
+ catch {
80
+ this.failCapture();
81
+ return;
82
+ }
83
+ // An observer callback cannot break the owning backend's transition.
84
+ try {
85
+ listener(state);
86
+ }
87
+ catch { /* passive observer */ }
88
+ }
89
+ }
90
+ /** Fresh capture after a durable append's synchronous owner mutation completes. */
91
+ scheduleCapture() {
92
+ if (this.scheduled)
93
+ return;
94
+ this.scheduled = true;
95
+ queueMicrotask(() => {
96
+ this.scheduled = false;
97
+ this.publish();
98
+ });
99
+ }
100
+ }
@@ -0,0 +1,50 @@
1
+ import type { ConversationEventV1 } from './conversation-types.js';
2
+ export declare const MAX_REPLAY_BUFFER_BYTES: number;
3
+ export declare const MAX_CONVERSATION_RECORD_BYTES: number;
4
+ export interface ConversationReplayRequest {
5
+ expectedGeneration: string;
6
+ after: number;
7
+ through: number;
8
+ pageSize?: number;
9
+ signal?: AbortSignal;
10
+ }
11
+ export interface ConversationReplayPage {
12
+ events: ConversationEventV1[];
13
+ /** Storage coverage, including invisible events; never a visible-event cursor. */
14
+ coveredThrough: number;
15
+ }
16
+ export declare class ReplayCoverageError extends Error {
17
+ constructor(detail: string);
18
+ }
19
+ /** Bounded reads and explicit scheduling even when a whole segment is cached by the OS. */
20
+ export declare function replayLines(path: string, signal?: AbortSignal): AsyncGenerator<string>;
21
+ import type { ConversationObservation, ConversationObservationState } from './conversation-types.js';
22
+ export interface PassiveConversationFollowRequest {
23
+ expectedGeneration: string;
24
+ after?: number;
25
+ controller: false;
26
+ signal?: AbortSignal;
27
+ }
28
+ export interface ConversationReplayReady {
29
+ snapshot: ConversationObservationState;
30
+ coveredThrough: number;
31
+ }
32
+ interface ReplayOwner {
33
+ observeConversation?(listener: (state: ConversationObservationState) => void): ConversationObservation;
34
+ subscribeConversationFailure?(listener: (generation: string) => void): () => void;
35
+ subscribeConversation?(listener: (event: ConversationEventV1) => void): () => void;
36
+ replayConversation?(request: ConversationReplayRequest): AsyncIterable<ConversationReplayPage>;
37
+ }
38
+ export interface ConversationReplaySink {
39
+ replay(page: ConversationReplayPage): void | Promise<void>;
40
+ ready(ready: ConversationReplayReady): void | Promise<void>;
41
+ event(event: ConversationEventV1): void | Promise<void>;
42
+ state(state: ConversationObservationState): void | Promise<void>;
43
+ close(reason?: string): void;
44
+ }
45
+ /** Passive owner-turn registration, complete replay, ready, then buffered post-barrier delivery. */
46
+ export declare function startConversationReplay(owner: ReplayOwner, request: PassiveConversationFollowRequest, sink: ConversationReplaySink): {
47
+ ready: Promise<void>;
48
+ close: () => void;
49
+ };
50
+ export {};