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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (423) hide show
  1. package/README.md +279 -68
  2. package/dist/account-origin.d.ts +2 -0
  3. package/dist/account-origin.js +6 -0
  4. package/dist/agent-ours/bridge.js +26 -9
  5. package/dist/agent-ours/correspondence.d.ts +10 -0
  6. package/dist/agent-ours/correspondence.js +1 -0
  7. package/dist/agent-ours/file-rpc.d.ts +12 -0
  8. package/dist/agent-ours/file-rpc.js +18 -4
  9. package/dist/agent-ours/harness.js +9 -1
  10. package/dist/agent-ours/mcp-endpoint.d.ts +19 -0
  11. package/dist/agent-ours/mcp-endpoint.js +98 -25
  12. package/dist/agent-ours/runtime.d.ts +9 -1
  13. package/dist/agent-ours/runtime.js +50 -2
  14. package/dist/agent-ours/service.d.ts +24 -1
  15. package/dist/agent-ours/service.js +97 -13
  16. package/dist/application/codex-models.d.ts +15 -0
  17. package/dist/application/codex-models.js +56 -0
  18. package/dist/application/conversation-history.d.ts +34 -0
  19. package/dist/application/conversation-history.js +280 -0
  20. package/dist/application/conversation-role-observer.d.ts +62 -0
  21. package/dist/application/conversation-role-observer.js +206 -0
  22. package/dist/application/fleet-query-service.d.ts +1 -1
  23. package/dist/application/fleet-query-service.js +3 -3
  24. package/dist/application/log-service.js +8 -8
  25. package/dist/application/model-catalog.d.ts +1 -1
  26. package/dist/application/model-catalog.js +5 -2
  27. package/dist/application/preset-provenance.d.ts +19 -0
  28. package/dist/application/preset-provenance.js +59 -0
  29. package/dist/application/resource-observer.d.ts +40 -0
  30. package/dist/application/resource-observer.js +278 -0
  31. package/dist/application/role-creation-service.js +3 -2
  32. package/dist/application/role-removal-service.js +17 -3
  33. package/dist/application/role-repository.d.ts +1 -1
  34. package/dist/application/role-repository.js +30 -7
  35. package/dist/application/room-layout-definitions.d.ts +45 -0
  36. package/dist/application/room-layout-definitions.js +130 -0
  37. package/dist/application/session-control.d.ts +61 -17
  38. package/dist/application/session-control.js +207 -23
  39. package/dist/application/supervisor-ours-tools.js +5 -11
  40. package/dist/application/task-layouts.d.ts +91 -0
  41. package/dist/application/task-layouts.js +261 -0
  42. package/dist/application/task-room-service.d.ts +101 -3
  43. package/dist/application/task-room-service.js +286 -33
  44. package/dist/application/types.d.ts +2 -0
  45. package/dist/atomic-file.d.ts +34 -1
  46. package/dist/atomic-file.js +161 -18
  47. package/dist/briefing.d.ts +3 -1
  48. package/dist/briefing.js +97 -31
  49. package/dist/build-info.json +9 -4
  50. package/dist/capabilities.d.ts +9 -1
  51. package/dist/capabilities.js +13 -0
  52. package/dist/cli.js +400 -37
  53. package/dist/client-profile.d.ts +5 -17
  54. package/dist/client-profile.js +4 -127
  55. package/dist/config.d.ts +45 -0
  56. package/dist/config.js +57 -6
  57. package/dist/creation.d.ts +1 -1
  58. package/dist/creation.js +4 -4
  59. package/dist/daemon-recovery.d.ts +14 -9
  60. package/dist/daemon-recovery.js +124 -115
  61. package/dist/docs.d.ts +1 -1
  62. package/dist/docs.js +207 -19
  63. package/dist/doctor.d.ts +4 -1
  64. package/dist/doctor.js +75 -60
  65. package/dist/erased-resources.d.ts +9 -0
  66. package/dist/erased-resources.js +87 -0
  67. package/dist/file-delivery/reader.d.ts +15 -0
  68. package/dist/file-delivery/reader.js +63 -0
  69. package/dist/file-delivery/store.d.ts +15 -0
  70. package/dist/file-delivery/store.js +124 -0
  71. package/dist/file-delivery/types.d.ts +19 -0
  72. package/dist/file-delivery/types.js +1 -0
  73. package/dist/fleet-command-audit.d.ts +7 -0
  74. package/dist/fleet-command-audit.js +24 -8
  75. package/dist/harness/agent-session.d.ts +4 -0
  76. package/dist/harness/claude-code-session.js +1 -1
  77. package/dist/harness/claude-code.js +21 -11
  78. package/dist/harness/claude-runtime.d.ts +12 -0
  79. package/dist/harness/claude-runtime.js +43 -0
  80. package/dist/harness/codex-app-server-proxy.d.ts +4 -1
  81. package/dist/harness/codex-app-server-proxy.js +41 -5
  82. package/dist/harness/codex-reasoning.d.ts +5 -0
  83. package/dist/harness/codex-reasoning.js +12 -0
  84. package/dist/harness/codex-runtime.d.ts +11 -1
  85. package/dist/harness/codex-runtime.js +31 -4
  86. package/dist/harness/codex-session.js +2 -2
  87. package/dist/harness/codex.js +24 -12
  88. package/dist/init-wizard.d.ts +2 -0
  89. package/dist/init-wizard.js +38 -19
  90. package/dist/isolation/policy.js +6 -1
  91. package/dist/isolation/types.d.ts +5 -0
  92. package/dist/managed-cli.d.ts +253 -0
  93. package/dist/managed-cli.js +923 -0
  94. package/dist/managed-recovery.d.ts +13 -0
  95. package/dist/managed-recovery.js +31 -0
  96. package/dist/minimal-setup.d.ts +2 -0
  97. package/dist/minimal-setup.js +44 -0
  98. package/dist/monitor.d.ts +9 -34
  99. package/dist/monitor.js +90 -153
  100. package/dist/notifications/fleet-producer.d.ts +22 -0
  101. package/dist/notifications/fleet-producer.js +237 -0
  102. package/dist/notifications/outbox.d.ts +46 -0
  103. package/dist/notifications/outbox.js +158 -0
  104. package/dist/notifications/target-cleanup.d.ts +11 -0
  105. package/dist/notifications/target-cleanup.js +63 -0
  106. package/dist/notifications/task-producer.d.ts +20 -0
  107. package/dist/notifications/task-producer.js +100 -0
  108. package/dist/ops.d.ts +2 -0
  109. package/dist/ops.js +20 -3
  110. package/dist/owner-channel/channel.js +17 -6
  111. package/dist/owner-channel/commands.d.ts +4 -0
  112. package/dist/owner-channel/commands.js +8 -0
  113. package/dist/owner-channel/lifecycle-outbox.js +4 -3
  114. package/dist/owner-channel/ours-client.js +2 -17
  115. package/dist/paths.d.ts +2 -0
  116. package/dist/paths.js +2 -0
  117. package/dist/preset-migration.js +43 -0
  118. package/dist/process-incarnation.d.ts +23 -0
  119. package/dist/process-incarnation.js +42 -0
  120. package/dist/reminders/manager.d.ts +75 -0
  121. package/dist/reminders/manager.js +278 -0
  122. package/dist/reminders/runtime.d.ts +27 -0
  123. package/dist/reminders/runtime.js +32 -0
  124. package/dist/reminders/schedule.d.ts +37 -0
  125. package/dist/reminders/schedule.js +104 -0
  126. package/dist/reminders/store.d.ts +341 -0
  127. package/dist/reminders/store.js +762 -0
  128. package/dist/reminders/tools.d.ts +20 -0
  129. package/dist/reminders/tools.js +114 -0
  130. package/dist/resolved-plan.js +1 -0
  131. package/dist/rooms-tasks/add-member.d.ts +48 -0
  132. package/dist/rooms-tasks/add-member.js +84 -0
  133. package/dist/rooms-tasks/archived-absence.d.ts +8 -0
  134. package/dist/rooms-tasks/archived-absence.js +48 -0
  135. package/dist/rooms-tasks/cli.js +65 -5
  136. package/dist/rooms-tasks/close.d.ts +6 -2
  137. package/dist/rooms-tasks/close.js +246 -37
  138. package/dist/rooms-tasks/config.d.ts +1 -1
  139. package/dist/rooms-tasks/config.js +11 -4
  140. package/dist/rooms-tasks/cowork-adapter.d.ts +12 -5
  141. package/dist/rooms-tasks/cowork-adapter.js +15 -121
  142. package/dist/rooms-tasks/cowork-http.js +56 -6
  143. package/dist/rooms-tasks/deletion.d.ts +1 -1
  144. package/dist/rooms-tasks/deletion.js +84 -5
  145. package/dist/rooms-tasks/erasure.d.ts +6 -0
  146. package/dist/rooms-tasks/erasure.js +173 -0
  147. package/dist/rooms-tasks/external-worker.d.ts +1 -1
  148. package/dist/rooms-tasks/external-worker.js +3 -4
  149. package/dist/rooms-tasks/layout-binding-client.d.ts +11 -0
  150. package/dist/rooms-tasks/layout-binding-client.js +56 -0
  151. package/dist/rooms-tasks/layout-binding-grants.d.ts +21 -0
  152. package/dist/rooms-tasks/layout-binding-grants.js +128 -0
  153. package/dist/rooms-tasks/layout-cli.d.ts +4 -0
  154. package/dist/rooms-tasks/layout-cli.js +107 -0
  155. package/dist/rooms-tasks/layout-config.d.ts +11 -0
  156. package/dist/rooms-tasks/layout-config.js +89 -0
  157. package/dist/rooms-tasks/layout-control.d.ts +21 -0
  158. package/dist/rooms-tasks/layout-control.js +52 -0
  159. package/dist/rooms-tasks/layout-member-retirement.d.ts +10 -0
  160. package/dist/rooms-tasks/layout-member-retirement.js +90 -0
  161. package/dist/rooms-tasks/layout-service.d.ts +43 -0
  162. package/dist/rooms-tasks/layout-service.js +203 -0
  163. package/dist/rooms-tasks/layout.d.ts +121 -0
  164. package/dist/rooms-tasks/layout.js +286 -0
  165. package/dist/rooms-tasks/legacy-task-member.d.ts +19 -0
  166. package/dist/rooms-tasks/legacy-task-member.js +116 -0
  167. package/dist/rooms-tasks/live-readiness.d.ts +18 -0
  168. package/dist/rooms-tasks/live-readiness.js +85 -0
  169. package/dist/rooms-tasks/member-ownership.d.ts +5 -0
  170. package/dist/rooms-tasks/member-ownership.js +40 -0
  171. package/dist/rooms-tasks/member-startup.d.ts +1 -0
  172. package/dist/rooms-tasks/member-startup.js +2 -0
  173. package/dist/rooms-tasks/provision.d.ts +3 -0
  174. package/dist/rooms-tasks/provision.js +101 -49
  175. package/dist/rooms-tasks/recovery.d.ts +19 -0
  176. package/dist/rooms-tasks/recovery.js +110 -0
  177. package/dist/rooms-tasks/room-state.d.ts +4 -2
  178. package/dist/rooms-tasks/room-state.js +47 -8
  179. package/dist/rooms-tasks/task-service-retirement.d.ts +11 -0
  180. package/dist/rooms-tasks/task-service-retirement.js +21 -0
  181. package/dist/rooms-tasks/task-state.d.ts +52 -8
  182. package/dist/rooms-tasks/task-state.js +251 -26
  183. package/dist/rooms-tasks/types.d.ts +40 -0
  184. package/dist/rooms-tasks/workspace-artifacts.d.ts +6 -0
  185. package/dist/rooms-tasks/workspace-artifacts.js +93 -0
  186. package/dist/rooms-tasks/workspace.d.ts +35 -0
  187. package/dist/rooms-tasks/workspace.js +325 -0
  188. package/dist/runner.d.ts +33 -4
  189. package/dist/runner.js +410 -62
  190. package/dist/session/acp.d.ts +75 -2
  191. package/dist/session/acp.js +345 -82
  192. package/dist/session/arbiter.d.ts +98 -0
  193. package/dist/session/arbiter.js +272 -3
  194. package/dist/session/codex-app-server.d.ts +45 -1
  195. package/dist/session/codex-app-server.js +225 -32
  196. package/dist/session/control.d.ts +17 -3
  197. package/dist/session/control.js +213 -23
  198. package/dist/session/conversation-normalizer.d.ts +2 -1
  199. package/dist/session/conversation-normalizer.js +1 -1
  200. package/dist/session/conversation-observation.d.ts +25 -0
  201. package/dist/session/conversation-observation.js +100 -0
  202. package/dist/session/conversation-replay.d.ts +50 -0
  203. package/dist/session/conversation-replay.js +203 -0
  204. package/dist/session/conversation-store.d.ts +15 -1
  205. package/dist/session/conversation-store.js +206 -26
  206. package/dist/session/conversation-types.d.ts +57 -4
  207. package/dist/session/prompt-batch.d.ts +79 -0
  208. package/dist/session/prompt-batch.js +120 -0
  209. package/dist/session/task-notice.d.ts +14 -0
  210. package/dist/session/task-notice.js +13 -0
  211. package/dist/session/types.d.ts +42 -1
  212. package/dist/setup-tunnel-preflight.d.ts +34 -0
  213. package/dist/setup-tunnel-preflight.js +62 -0
  214. package/dist/socket-path.d.ts +2 -0
  215. package/dist/socket-path.js +22 -0
  216. package/dist/spawn.d.ts +5 -1
  217. package/dist/spawn.js +40 -4
  218. package/dist/startup-prompt.d.ts +15 -0
  219. package/dist/startup-prompt.js +71 -0
  220. package/dist/startup-readiness.d.ts +32 -0
  221. package/dist/startup-readiness.js +101 -0
  222. package/dist/startup-telemetry.d.ts +16 -0
  223. package/dist/startup-telemetry.js +46 -0
  224. package/dist/subscriptions/cli.d.ts +91 -0
  225. package/dist/subscriptions/cli.js +223 -0
  226. package/dist/subscriptions/launch.d.ts +37 -0
  227. package/dist/subscriptions/launch.js +93 -0
  228. package/dist/subscriptions/login.d.ts +77 -0
  229. package/dist/subscriptions/login.js +215 -0
  230. package/dist/subscriptions/observed.d.ts +7 -0
  231. package/dist/subscriptions/observed.js +44 -0
  232. package/dist/subscriptions/probe.d.ts +10 -0
  233. package/dist/subscriptions/probe.js +73 -0
  234. package/dist/subscriptions/service.d.ts +102 -0
  235. package/dist/subscriptions/service.js +364 -0
  236. package/dist/subscriptions/store.d.ts +71 -0
  237. package/dist/subscriptions/store.js +143 -0
  238. package/dist/supervisor/adoption.d.ts +8 -0
  239. package/dist/supervisor/adoption.js +78 -0
  240. package/dist/supervisor/catalog.d.ts +62 -0
  241. package/dist/supervisor/catalog.js +254 -0
  242. package/dist/supervisor/fleet.d.ts +8 -0
  243. package/dist/supervisor/fleet.js +89 -0
  244. package/dist/supervisor/index.js +3 -6
  245. package/dist/supervisor/launchd.d.ts +1 -1
  246. package/dist/supervisor/launchd.js +26 -13
  247. package/dist/supervisor/legacy.d.ts +35 -0
  248. package/dist/supervisor/legacy.js +524 -0
  249. package/dist/supervisor/manager.d.ts +15 -0
  250. package/dist/supervisor/manager.js +270 -0
  251. package/dist/supervisor/registration-refresh.d.ts +45 -0
  252. package/dist/supervisor/registration-refresh.js +266 -0
  253. package/dist/supervisor/scope.d.ts +4 -0
  254. package/dist/supervisor/scope.js +10 -0
  255. package/dist/supervisor/systemd.d.ts +2 -2
  256. package/dist/supervisor/systemd.js +19 -33
  257. package/dist/task-supervision.d.ts +9 -0
  258. package/dist/task-supervision.js +145 -0
  259. package/dist/temp-idle.d.ts +17 -0
  260. package/dist/temp-idle.js +42 -0
  261. package/dist/temp-lifecycle.d.ts +23 -8
  262. package/dist/temp-lifecycle.js +123 -66
  263. package/dist/temp-supervisor-recovery.d.ts +1 -1
  264. package/dist/temp-supervisor-recovery.js +2 -2
  265. package/dist/tunnel-setup-completion.d.ts +7 -0
  266. package/dist/tunnel-setup-completion.js +13 -0
  267. package/dist/tunnel-setup-input.d.ts +19 -0
  268. package/dist/tunnel-setup-input.js +124 -0
  269. package/dist/watchdog/query.d.ts +3 -1
  270. package/dist/watchdog/query.js +2 -1
  271. package/dist/web/agent-attachments.d.ts +29 -0
  272. package/dist/web/agent-attachments.js +162 -0
  273. package/dist/web/auth.d.ts +65 -2
  274. package/dist/web/auth.js +201 -11
  275. package/dist/web/available-port.d.ts +2 -0
  276. package/dist/web/available-port.js +23 -0
  277. package/dist/web/control.d.ts +3 -3
  278. package/dist/web/control.js +10 -8
  279. package/dist/web/conversation-stream-codec.d.ts +23 -0
  280. package/dist/web/conversation-stream-codec.js +110 -0
  281. package/dist/web/conversation-stream.d.ts +25 -0
  282. package/dist/web/conversation-stream.js +231 -0
  283. package/dist/web/events.d.ts +2 -0
  284. package/dist/web/events.js +9 -0
  285. package/dist/web/fleet-config-service.d.ts +4 -1
  286. package/dist/web/fleet-config-service.js +46 -15
  287. package/dist/web/prefix-gateway.d.ts +32 -0
  288. package/dist/web/prefix-gateway.js +248 -0
  289. package/dist/web/resource-views.d.ts +124 -0
  290. package/dist/web/resource-views.js +424 -0
  291. package/dist/web/runtime.d.ts +3 -1
  292. package/dist/web/runtime.js +132 -34
  293. package/dist/web/server.d.ts +19 -0
  294. package/dist/web/server.js +802 -67
  295. package/dist/web/service.d.ts +11 -2
  296. package/dist/web/service.js +30 -4
  297. package/dist/web/workspace-devices.d.ts +107 -0
  298. package/dist/web/workspace-devices.js +277 -0
  299. package/dist/web/workspace-tunnel.d.ts +1 -0
  300. package/dist/web/workspace-tunnel.js +34 -0
  301. package/dist/workspace-enrollment.d.ts +83 -0
  302. package/dist/workspace-enrollment.js +580 -0
  303. package/dist/workspace-replacement.d.ts +66 -0
  304. package/dist/workspace-replacement.js +220 -0
  305. package/docs/acp-files.md +146 -0
  306. package/docs/conversation-stream.md +196 -0
  307. package/docs/fleet-migration.md +54 -0
  308. package/docs/live-voice.md +13 -0
  309. package/docs/room-layouts.md +227 -0
  310. package/docs/self-reminders.md +374 -0
  311. package/docs/task-workspaces.md +225 -0
  312. package/docs/validation/managed-cli-permissions.md +421 -0
  313. package/examples/room-layouts/fresh.yaml +27 -0
  314. package/examples/room-layouts/scoped.yaml +21 -0
  315. package/examples/room-layouts/shared.yaml +19 -0
  316. package/package.json +24 -20
  317. package/presets/README.md +78 -0
  318. package/presets/brain-catalog.json +8 -2
  319. package/presets/fleet/agent_templates/Critic.yaml +1 -1
  320. package/presets/fleet/agent_templates/Developer.yaml +1 -1
  321. package/presets/fleet/agent_templates/Engineer.yaml +5 -0
  322. package/presets/fleet/agent_templates/LocalCoordinator.yaml +4 -2
  323. package/presets/fleet/agents/FleetCoordinator.yaml +1 -1
  324. package/presets/fleet/brains/claude-fable-5-1-high.yaml +5 -0
  325. package/presets/fleet/brains/claude-fable-5-1-low.yaml +5 -0
  326. package/presets/fleet/brains/claude-fable-5-1-max.yaml +5 -0
  327. package/presets/fleet/brains/claude-fable-5-1-medium.yaml +5 -0
  328. package/presets/fleet/brains/claude-fable-5-1-xhigh.yaml +5 -0
  329. package/presets/fleet/brains/claude-fable-5-high.yaml +1 -1
  330. package/presets/fleet/brains/claude-fable-5-low.yaml +1 -1
  331. package/presets/fleet/brains/claude-fable-5-max.yaml +1 -1
  332. package/presets/fleet/brains/claude-fable-5-medium.yaml +1 -1
  333. package/presets/fleet/brains/claude-fable-5-xhigh.yaml +1 -1
  334. package/presets/fleet/brains/claude-haiku-4-5-20251001-default.yaml +4 -0
  335. package/presets/fleet/brains/claude-opus-5-5-high.yaml +5 -0
  336. package/presets/fleet/brains/claude-opus-5-5-low.yaml +5 -0
  337. package/presets/fleet/brains/claude-opus-5-5-max.yaml +5 -0
  338. package/presets/fleet/brains/claude-opus-5-5-medium.yaml +5 -0
  339. package/presets/fleet/brains/claude-opus-5-5-xhigh.yaml +5 -0
  340. package/presets/fleet/brains/claude-opus-5-high.yaml +1 -1
  341. package/presets/fleet/brains/claude-opus-5-low.yaml +1 -1
  342. package/presets/fleet/brains/claude-opus-5-max.yaml +1 -1
  343. package/presets/fleet/brains/claude-opus-5-medium.yaml +1 -1
  344. package/presets/fleet/brains/claude-opus-5-xhigh.yaml +1 -1
  345. package/presets/fleet/brains/claude-sonnet-5-high.yaml +1 -1
  346. package/presets/fleet/brains/claude-sonnet-5-low.yaml +1 -1
  347. package/presets/fleet/brains/claude-sonnet-5-max.yaml +1 -1
  348. package/presets/fleet/brains/claude-sonnet-5-medium.yaml +1 -1
  349. package/presets/fleet/brains/claude-sonnet-5-xhigh.yaml +1 -1
  350. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-high.yaml +1 -1
  351. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-low.yaml +1 -1
  352. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-medium.yaml +1 -1
  353. package/presets/fleet/brains/codex-gpt-5-3-codex-spark-xhigh.yaml +1 -1
  354. package/presets/fleet/brains/codex-gpt-5-4-high.yaml +1 -1
  355. package/presets/fleet/brains/codex-gpt-5-4-low.yaml +1 -1
  356. package/presets/fleet/brains/codex-gpt-5-4-medium.yaml +1 -1
  357. package/presets/fleet/brains/codex-gpt-5-4-mini-high.yaml +1 -1
  358. package/presets/fleet/brains/codex-gpt-5-4-mini-low.yaml +1 -1
  359. package/presets/fleet/brains/codex-gpt-5-4-mini-medium.yaml +1 -1
  360. package/presets/fleet/brains/codex-gpt-5-4-mini-xhigh.yaml +1 -1
  361. package/presets/fleet/brains/codex-gpt-5-4-xhigh.yaml +1 -1
  362. package/presets/fleet/brains/codex-gpt-5-5-high.yaml +1 -1
  363. package/presets/fleet/brains/codex-gpt-5-5-low.yaml +1 -1
  364. package/presets/fleet/brains/codex-gpt-5-5-medium.yaml +1 -1
  365. package/presets/fleet/brains/codex-gpt-5-5-xhigh.yaml +1 -1
  366. package/presets/fleet/brains/codex-gpt-5-6-luna-high.yaml +1 -1
  367. package/presets/fleet/brains/codex-gpt-5-6-luna-low.yaml +1 -1
  368. package/presets/fleet/brains/codex-gpt-5-6-luna-max.yaml +1 -1
  369. package/presets/fleet/brains/codex-gpt-5-6-luna-medium.yaml +1 -1
  370. package/presets/fleet/brains/codex-gpt-5-6-luna-xhigh.yaml +1 -1
  371. package/presets/fleet/brains/codex-gpt-5-6-sol-high.yaml +1 -1
  372. package/presets/fleet/brains/codex-gpt-5-6-sol-low.yaml +1 -1
  373. package/presets/fleet/brains/codex-gpt-5-6-sol-max.yaml +1 -1
  374. package/presets/fleet/brains/codex-gpt-5-6-sol-medium.yaml +1 -1
  375. package/presets/fleet/brains/codex-gpt-5-6-sol-ultra.yaml +1 -1
  376. package/presets/fleet/brains/codex-gpt-5-6-sol-xhigh.yaml +1 -1
  377. package/presets/fleet/brains/codex-gpt-5-6-terra-high.yaml +1 -1
  378. package/presets/fleet/brains/codex-gpt-5-6-terra-low.yaml +1 -1
  379. package/presets/fleet/brains/codex-gpt-5-6-terra-max.yaml +1 -1
  380. package/presets/fleet/brains/codex-gpt-5-6-terra-medium.yaml +1 -1
  381. package/presets/fleet/brains/codex-gpt-5-6-terra-ultra.yaml +1 -1
  382. package/presets/fleet/brains/codex-gpt-5-6-terra-xhigh.yaml +1 -1
  383. package/presets/fleet/brains/codex-gpt-6-1-sol-high.yaml +5 -0
  384. package/presets/fleet/brains/codex-gpt-6-1-sol-low.yaml +5 -0
  385. package/presets/fleet/brains/codex-gpt-6-1-sol-max.yaml +5 -0
  386. package/presets/fleet/brains/codex-gpt-6-1-sol-medium.yaml +5 -0
  387. package/presets/fleet/brains/codex-gpt-6-1-sol-ultra.yaml +5 -0
  388. package/presets/fleet/brains/codex-gpt-6-1-sol-xhigh.yaml +5 -0
  389. package/presets/fleet/brains/codex-gpt-6-astra-high.yaml +1 -1
  390. package/presets/fleet/brains/codex-gpt-6-astra-low.yaml +1 -1
  391. package/presets/fleet/brains/codex-gpt-6-astra-max.yaml +1 -1
  392. package/presets/fleet/brains/codex-gpt-6-astra-medium.yaml +1 -1
  393. package/presets/fleet/brains/codex-gpt-6-astra-ultra.yaml +1 -1
  394. package/presets/fleet/brains/codex-gpt-6-astra-xhigh.yaml +1 -1
  395. package/presets/fleet/brains/codex-gpt-6-luna-high.yaml +5 -0
  396. package/presets/fleet/brains/codex-gpt-6-luna-low.yaml +5 -0
  397. package/presets/fleet/brains/codex-gpt-6-luna-max.yaml +5 -0
  398. package/presets/fleet/brains/codex-gpt-6-luna-medium.yaml +5 -0
  399. package/presets/fleet/brains/codex-gpt-6-luna-xhigh.yaml +5 -0
  400. package/presets/fleet/brains/codex-gpt-6-sol-high.yaml +5 -0
  401. package/presets/fleet/brains/codex-gpt-6-sol-low.yaml +5 -0
  402. package/presets/fleet/brains/codex-gpt-6-sol-max.yaml +5 -0
  403. package/presets/fleet/brains/codex-gpt-6-sol-medium.yaml +5 -0
  404. package/presets/fleet/brains/codex-gpt-6-sol-ultra.yaml +5 -0
  405. package/presets/fleet/brains/codex-gpt-6-sol-xhigh.yaml +5 -0
  406. package/presets/fleet/roles/Coordinator.yaml +12 -11
  407. package/presets/fleet/roles/Critic.yaml +13 -0
  408. package/presets/fleet/roles/Developer.yaml +13 -0
  409. package/presets/fleet/roles/Engineer.yaml +53 -0
  410. package/presets/fleet/room_layouts/engineering.yaml +25 -0
  411. package/presets/fleet/room_layouts/pair.yaml +24 -0
  412. package/presets/fleet/room_layouts/single.yaml +17 -0
  413. package/presets/fleet/room_layouts/team.yaml +30 -0
  414. package/presets/fleet/room_templates/engineering.yaml +19 -0
  415. package/presets/manifest.json +2 -2
  416. package/dist/web-app/assets/index-59GF-gsZ.css +0 -1
  417. package/dist/web-app/assets/index-C0h3ALvs.js +0 -10
  418. package/dist/web-app/icons/ours-fleet-maskable.svg +0 -4
  419. package/dist/web-app/icons/ours-fleet.svg +0 -4
  420. package/dist/web-app/index.html +0 -17
  421. package/dist/web-app/manifest.webmanifest +0 -15
  422. package/dist/web-app/offline.html +0 -18
  423. package/dist/web-app/sw.js +0 -51
@@ -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,98 @@ 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
+ const send = (message) => {
471
+ const result = writing.then(() => writeMessage(message));
472
+ writing = result.catch(() => { });
473
+ return result;
474
+ };
475
+ const writeMessage = async (message) => {
476
+ // Stream one replay record progressively; don't queue a valid 16 MiB record
477
+ // into the 8 MiB pending-output budget. Framing is owned by the transport.
478
+ const bytes = Buffer.from(JSON.stringify({ version: 1, ...message }) + '\n');
479
+ for (let offset = 0; offset < bytes.length; offset += 64 * 1024) {
480
+ if (stopped || socket.destroyed)
481
+ throw new Error('conversation socket closed');
482
+ if (!socket.write(bytes.subarray(offset, offset + 64 * 1024))) {
483
+ await new Promise((resolve, reject) => {
484
+ const cleanup = () => { socket.off('drain', drained); socket.off('close', ended); socket.off('error', failed); };
485
+ const drained = () => { cleanup(); resolve(); };
486
+ const ended = () => { cleanup(); reject(new Error('conversation socket closed')); };
487
+ const failed = (error) => { cleanup(); reject(error); };
488
+ socket.once('drain', drained);
489
+ socket.once('close', ended);
490
+ socket.once('error', failed);
491
+ });
492
+ }
493
+ }
494
+ };
495
+ const follow = startConversationReplay(this.session, {
496
+ expectedGeneration: request.expectedGeneration, after: request.after === undefined ? 0 : Number(request.after), controller: false,
497
+ }, {
498
+ replay: page => send({ conversationReplay: page }),
499
+ ready: ready => send({ conversationReady: ready }),
500
+ event: event => send({ conversationEvent: event }),
501
+ state: state => send({ conversationState: state }),
502
+ close: reason => {
503
+ stopped = true;
504
+ if (heartbeat)
505
+ clearInterval(heartbeat);
506
+ if (reason && !socket.destroyed)
507
+ socket.end(JSON.stringify({ version: 1, id: request.id, ok: false, error: reason }) + '\n');
508
+ },
509
+ });
510
+ const stop = () => { stopped = true; if (heartbeat)
511
+ clearInterval(heartbeat); follow.close(); };
512
+ let heartbeatPending = false;
513
+ if (!stopped) {
514
+ heartbeat = setInterval(() => {
515
+ if (heartbeatPending || stopped)
516
+ return;
517
+ heartbeatPending = true;
518
+ void send({ controlHeartbeat: true }).catch(() => { stop(); socket.destroy(); })
519
+ .finally(() => { heartbeatPending = false; });
520
+ }, 2_000);
521
+ heartbeat.unref();
522
+ }
523
+ socket.once('close', stop);
524
+ return { controller: false, stop: () => { socket.off('close', stop); stop(); } };
525
+ }
408
526
  this.write(socket, {
409
527
  version: 1, id: request.id, ok: true,
410
528
  result: this.session.conversationPage({
411
- after: request.after, limit: request.limit,
529
+ after: request.after === undefined ? undefined : String(request.after), limit: request.limit,
412
530
  }),
413
531
  });
414
532
  // A live conversation view is an attached controller: permission
@@ -421,7 +539,10 @@ export class RoleControlServer {
421
539
  socket.write(JSON.stringify({ version: 1, conversationEvent: event }) + '\n');
422
540
  }) };
423
541
  }
542
+ case 'submit_voice_prompt':
424
543
  case 'submit_prompt_v2': {
544
+ if (request.command === 'submit_voice_prompt' && !request.expectedSessionGeneration?.trim())
545
+ throw new SessionControlError('rejected', 'expectedSessionGeneration is required');
425
546
  this.requireConversation(request);
426
547
  if (!request.commandId?.trim() || !request.text?.trim() || !request.actor?.trim()
427
548
  || request.source !== 'owner_admin_console')
@@ -432,6 +553,7 @@ export class RoleControlServer {
432
553
  const receipt = await this.session.submitPromptBrowser({
433
554
  commandId: request.commandId, text: request.text,
434
555
  source: 'owner_admin_console', actorBrowserSession: request.actor,
556
+ expectedSessionGeneration: request.expectedSessionGeneration, requireIdle: request.requireIdle === true,
435
557
  });
436
558
  this.write(socket, { version: 1, id: request.id, ok: true, result: receipt });
437
559
  }
@@ -442,6 +564,29 @@ export class RoleControlServer {
442
564
  }
443
565
  return;
444
566
  }
567
+ case 'interrupt_voice_v2': {
568
+ this.requireConversation(request);
569
+ if (!request.commandId?.trim() || !request.expectedSessionGeneration?.trim() || !request.promptId?.trim())
570
+ throw new SessionControlError('rejected', 'commandId, expectedSessionGeneration and promptId are required');
571
+ if (!this.session.interruptPrompt)
572
+ throw new SessionControlError('rejected', 'targeted voice interrupt unavailable');
573
+ const key = 'voice:' + request.commandId;
574
+ const signature = JSON.stringify([request.expectedSessionGeneration, request.promptId]);
575
+ const existing = this.interruptCommands.get(key);
576
+ if (existing) {
577
+ if (existing.signature !== signature)
578
+ throw new SessionControlError('rejected', 'idempotency_conflict: interrupt target changed');
579
+ this.write(socket, { version: 1, id: request.id, ok: true, result: existing.receipt });
580
+ return;
581
+ }
582
+ const outcome = await this.session.interruptPrompt(request.expectedSessionGeneration, request.promptId);
583
+ const receipt = { accepted: true, commandId: request.commandId, ...outcome };
584
+ this.interruptCommands.set(key, { signature, receipt });
585
+ if (this.interruptCommands.size > 200)
586
+ this.interruptCommands.delete(this.interruptCommands.keys().next().value);
587
+ this.write(socket, { version: 1, id: request.id, ok: true, result: receipt });
588
+ return;
589
+ }
445
590
  case 'interrupt_v2': {
446
591
  this.requireConversation(request);
447
592
  if (!request.commandId?.trim())
@@ -501,6 +646,23 @@ export class RoleControlServer {
501
646
  if (!this.session.conversationPage || !this.session.subscribeConversation)
502
647
  throw new SessionControlError('rejected', 'this role has no conversation ledger');
503
648
  }
649
+ observationFeatures() {
650
+ if (!this.session.observeConversation || !this.session.replayConversation || !this.session.subscribeConversation || !this.session.subscribeConversationFailure)
651
+ return [];
652
+ try {
653
+ const observation = this.session.observeConversation(() => { });
654
+ try {
655
+ return observation.snapshot.observationFailure || !this.session.conversationReplayAvailable?.(observation.snapshot.sessionGeneration)
656
+ ? [] : ['conversation_observation', 'control_heartbeat'];
657
+ }
658
+ finally {
659
+ observation.unsubscribe();
660
+ }
661
+ }
662
+ catch {
663
+ return [];
664
+ }
665
+ }
504
666
  write(socket, response) {
505
667
  if (!socket.destroyed)
506
668
  socket.write(JSON.stringify(response) + '\n');
@@ -532,7 +694,13 @@ export async function controlRequest(stateDir, request, timeoutMs = 120_000) {
532
694
  socket.once('error', error => {
533
695
  clearTimeout(timer);
534
696
  const code = error.code;
535
- reject(new SessionControlError(code === 'ENOENT' || code === 'ECONNREFUSED' ? 'control-unavailable' : 'backend', `role control socket: ${error.message}`));
697
+ const permissionHint = code === 'EPERM' || code === 'EACCES'
698
+ ? `; the operating system denied access before '${request.command}' reached the supervisor. `
699
+ + 'Check socket permissions and the command execution sandbox. '
700
+ + 'An existing socket does not prove this process can connect; do not bypass Fleet audit. '
701
+ + 'For managed workspace sessions, see docs/validation/managed-cli-permissions.md.'
702
+ : '';
703
+ reject(new SessionControlError(code === 'ENOENT' || code === 'ECONNREFUSED' ? 'control-unavailable' : 'backend', `role control socket: ${error.message}${permissionHint}`));
536
704
  });
537
705
  socket.on('data', chunk => {
538
706
  buffer += chunk;
@@ -587,32 +755,54 @@ export async function followControl(stateDir, onMessage) {
587
755
  * controller-presence this connection contributed.
588
756
  */
589
757
  export async function followConversation(stateDir, after, onMessage) {
758
+ const options = typeof after === 'object' ? after : undefined;
759
+ options?.signal?.throwIfAborted();
590
760
  const token = readFileSync(controlTokenPath(stateDir), 'utf8').trim();
591
761
  const socket = createConnection(controlSocketPath(stateDir));
592
762
  socket.setEncoding('utf8');
593
- let buffer = '';
763
+ // Admit one maximum-size logical record plus its actual private wrapper.
764
+ // coveredThrough is a safe integer. JSON brackets/envelope are outside the record budget.
765
+ 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);
766
+ const limit = MAX_CONVERSATION_RECORD_BYTES + privateEnvelopeBytes;
767
+ let parts = [], bytes = 0;
594
768
  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));
769
+ for (const [index, part] of String(chunk).split('\n').entries()) {
770
+ if (index) {
771
+ const line = parts.join('');
772
+ parts = [];
773
+ bytes = 0;
774
+ if (line.trim()) {
775
+ try {
776
+ onMessage(JSON.parse(line));
777
+ }
778
+ catch {
779
+ if (options) {
780
+ socket.destroy(new Error('invalid conversation private frame'));
781
+ return;
782
+ }
783
+ }
784
+ }
785
+ }
786
+ bytes += Buffer.byteLength(part);
787
+ if (options && bytes > limit) {
788
+ socket.destroy(new Error('conversation private frame exceeds assembly limit'));
789
+ return;
606
790
  }
607
- catch { /* a torn frame ends this stream; the client resyncs over HTTP */ }
791
+ parts.push(part);
608
792
  }
609
793
  });
610
794
  await new Promise((resolve, reject) => {
795
+ const onAbort = () => { socket.destroy(); reject(new Error('conversation follow aborted')); };
796
+ options?.signal?.addEventListener('abort', onAbort, { once: true });
797
+ socket.once('close', () => options?.signal?.removeEventListener('abort', onAbort));
611
798
  socket.once('connect', resolve);
612
799
  socket.once('error', reject);
800
+ if (options?.signal?.aborted)
801
+ onAbort();
613
802
  });
614
803
  socket.write(JSON.stringify({
615
- version: 3, id: randomUUID(), token, command: 'conversation_follow', after,
804
+ version: 3, id: randomUUID(), token, command: 'conversation_follow',
805
+ ...(options ? { expectedGeneration: options.expectedGeneration, after: options.after ?? 0, controller: false } : { after }),
616
806
  }) + '\n');
617
807
  return { socket, close: () => socket.destroy() };
618
808
  }
@@ -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 {};