@ours.network/fleet 1.2.0-nightly.9 → 2.0.0
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.
- package/README.md +280 -68
- package/dist/account-origin.d.ts +5 -0
- package/dist/account-origin.js +31 -0
- package/dist/agent-ours/bridge.js +26 -9
- package/dist/agent-ours/correspondence.d.ts +10 -0
- package/dist/agent-ours/correspondence.js +1 -0
- package/dist/agent-ours/file-rpc.d.ts +12 -0
- package/dist/agent-ours/file-rpc.js +18 -4
- package/dist/agent-ours/harness.js +9 -1
- package/dist/agent-ours/mcp-endpoint.d.ts +22 -0
- package/dist/agent-ours/mcp-endpoint.js +101 -25
- package/dist/agent-ours/runtime.d.ts +9 -1
- package/dist/agent-ours/runtime.js +50 -2
- package/dist/agent-ours/service.d.ts +27 -1
- package/dist/agent-ours/service.js +107 -13
- package/dist/application/codex-models.d.ts +15 -0
- package/dist/application/codex-models.js +56 -0
- package/dist/application/conversation-history.d.ts +31 -0
- package/dist/application/conversation-history.js +339 -0
- package/dist/application/conversation-role-observer.d.ts +62 -0
- package/dist/application/conversation-role-observer.js +206 -0
- package/dist/application/fleet-query-service.d.ts +1 -1
- package/dist/application/fleet-query-service.js +3 -3
- package/dist/application/log-service.js +8 -8
- package/dist/application/model-catalog.d.ts +1 -1
- package/dist/application/model-catalog.js +5 -2
- package/dist/application/preset-provenance.d.ts +19 -0
- package/dist/application/preset-provenance.js +59 -0
- package/dist/application/resource-observer.d.ts +43 -0
- package/dist/application/resource-observer.js +323 -0
- package/dist/application/role-creation-service.js +3 -2
- package/dist/application/role-removal-service.js +17 -3
- package/dist/application/role-repository.d.ts +3 -1
- package/dist/application/role-repository.js +89 -54
- package/dist/application/room-layout-definitions.d.ts +45 -0
- package/dist/application/room-layout-definitions.js +130 -0
- package/dist/application/session-control.d.ts +63 -17
- package/dist/application/session-control.js +221 -23
- package/dist/application/supervisor-ours-tools.js +5 -11
- package/dist/application/task-layouts.d.ts +91 -0
- package/dist/application/task-layouts.js +261 -0
- package/dist/application/task-room-service.d.ts +101 -3
- package/dist/application/task-room-service.js +286 -33
- package/dist/application/types.d.ts +2 -0
- package/dist/atomic-file.d.ts +34 -1
- package/dist/atomic-file.js +161 -18
- package/dist/briefing.d.ts +3 -1
- package/dist/briefing.js +120 -31
- package/dist/build-info.json +10 -5
- package/dist/capabilities.d.ts +9 -1
- package/dist/capabilities.js +13 -0
- package/dist/cli.js +391 -38
- package/dist/client-profile.d.ts +5 -17
- package/dist/client-profile.js +4 -127
- package/dist/config.d.ts +45 -0
- package/dist/config.js +57 -6
- package/dist/creation.d.ts +1 -1
- package/dist/creation.js +4 -4
- package/dist/daemon-recovery.d.ts +14 -9
- package/dist/daemon-recovery.js +124 -115
- package/dist/docs.d.ts +1 -1
- package/dist/docs.js +214 -20
- package/dist/doctor.d.ts +4 -1
- package/dist/doctor.js +75 -60
- package/dist/erased-resources.d.ts +9 -0
- package/dist/erased-resources.js +87 -0
- package/dist/file-delivery/reader.d.ts +15 -0
- package/dist/file-delivery/reader.js +63 -0
- package/dist/file-delivery/store.d.ts +15 -0
- package/dist/file-delivery/store.js +124 -0
- package/dist/file-delivery/types.d.ts +19 -0
- package/dist/file-delivery/types.js +1 -0
- package/dist/fleet-command-audit.d.ts +13 -0
- package/dist/fleet-command-audit.js +31 -9
- package/dist/harness/agent-session.d.ts +4 -0
- package/dist/harness/claude-code-session.js +1 -1
- package/dist/harness/claude-code.js +21 -11
- package/dist/harness/claude-runtime.d.ts +12 -0
- package/dist/harness/claude-runtime.js +43 -0
- package/dist/harness/codex-app-server-proxy.d.ts +4 -1
- package/dist/harness/codex-app-server-proxy.js +41 -5
- package/dist/harness/codex-reasoning.d.ts +5 -0
- package/dist/harness/codex-reasoning.js +12 -0
- package/dist/harness/codex-runtime.d.ts +11 -1
- package/dist/harness/codex-runtime.js +31 -4
- package/dist/harness/codex-session.js +2 -2
- package/dist/harness/codex.js +24 -12
- package/dist/init-wizard.d.ts +2 -0
- package/dist/init-wizard.js +38 -19
- package/dist/isolation/policy.js +6 -1
- package/dist/isolation/types.d.ts +5 -0
- package/dist/managed-cli.d.ts +253 -0
- package/dist/managed-cli.js +923 -0
- package/dist/managed-recovery.d.ts +13 -0
- package/dist/managed-recovery.js +31 -0
- package/dist/minimal-setup.d.ts +2 -0
- package/dist/minimal-setup.js +44 -0
- package/dist/monitor.d.ts +9 -34
- package/dist/monitor.js +90 -153
- package/dist/notifications/fleet-producer.d.ts +31 -0
- package/dist/notifications/fleet-producer.js +351 -0
- package/dist/notifications/outbox.d.ts +59 -0
- package/dist/notifications/outbox.js +199 -0
- package/dist/notifications/target-cleanup.d.ts +16 -0
- package/dist/notifications/target-cleanup.js +75 -0
- package/dist/notifications/task-producer.d.ts +20 -0
- package/dist/notifications/task-producer.js +100 -0
- package/dist/ops.d.ts +2 -0
- package/dist/ops.js +20 -3
- package/dist/owner-channel/channel.js +17 -6
- package/dist/owner-channel/commands.d.ts +4 -0
- package/dist/owner-channel/commands.js +8 -0
- package/dist/owner-channel/lifecycle-outbox.js +4 -3
- package/dist/owner-channel/ours-client.js +2 -17
- package/dist/paths.d.ts +5 -0
- package/dist/paths.js +5 -0
- package/dist/preset-migration.js +55 -0
- package/dist/process-incarnation.d.ts +23 -0
- package/dist/process-incarnation.js +42 -0
- package/dist/reminders/manager.d.ts +75 -0
- package/dist/reminders/manager.js +278 -0
- package/dist/reminders/runtime.d.ts +27 -0
- package/dist/reminders/runtime.js +32 -0
- package/dist/reminders/schedule.d.ts +37 -0
- package/dist/reminders/schedule.js +104 -0
- package/dist/reminders/store.d.ts +341 -0
- package/dist/reminders/store.js +762 -0
- package/dist/reminders/tools.d.ts +20 -0
- package/dist/reminders/tools.js +114 -0
- package/dist/resolved-plan.js +1 -0
- package/dist/rooms-tasks/add-member.d.ts +48 -0
- package/dist/rooms-tasks/add-member.js +84 -0
- package/dist/rooms-tasks/archived-absence.d.ts +4 -0
- package/dist/rooms-tasks/archived-absence.js +11 -0
- package/dist/rooms-tasks/cli.js +182 -5
- package/dist/rooms-tasks/close.d.ts +5 -2
- package/dist/rooms-tasks/close.js +224 -36
- package/dist/rooms-tasks/config.d.ts +1 -1
- package/dist/rooms-tasks/config.js +11 -4
- package/dist/rooms-tasks/cowork-adapter.d.ts +12 -5
- package/dist/rooms-tasks/cowork-adapter.js +15 -121
- package/dist/rooms-tasks/cowork-http.js +56 -6
- package/dist/rooms-tasks/deletion.d.ts +1 -1
- package/dist/rooms-tasks/deletion.js +66 -10
- package/dist/rooms-tasks/erasure.d.ts +6 -0
- package/dist/rooms-tasks/erasure.js +173 -0
- package/dist/rooms-tasks/external-worker.d.ts +1 -1
- package/dist/rooms-tasks/external-worker.js +3 -4
- package/dist/rooms-tasks/layout-binding-client.d.ts +11 -0
- package/dist/rooms-tasks/layout-binding-client.js +56 -0
- package/dist/rooms-tasks/layout-binding-grants.d.ts +21 -0
- package/dist/rooms-tasks/layout-binding-grants.js +128 -0
- package/dist/rooms-tasks/layout-cli.d.ts +4 -0
- package/dist/rooms-tasks/layout-cli.js +107 -0
- package/dist/rooms-tasks/layout-config.d.ts +11 -0
- package/dist/rooms-tasks/layout-config.js +89 -0
- package/dist/rooms-tasks/layout-control.d.ts +21 -0
- package/dist/rooms-tasks/layout-control.js +52 -0
- package/dist/rooms-tasks/layout-member-retirement.d.ts +10 -0
- package/dist/rooms-tasks/layout-member-retirement.js +90 -0
- package/dist/rooms-tasks/layout-service.d.ts +43 -0
- package/dist/rooms-tasks/layout-service.js +203 -0
- package/dist/rooms-tasks/layout.d.ts +121 -0
- package/dist/rooms-tasks/layout.js +286 -0
- package/dist/rooms-tasks/legacy-task-member.d.ts +19 -0
- package/dist/rooms-tasks/legacy-task-member.js +116 -0
- package/dist/rooms-tasks/live-readiness.d.ts +18 -0
- package/dist/rooms-tasks/live-readiness.js +85 -0
- package/dist/rooms-tasks/member-ownership.d.ts +5 -0
- package/dist/rooms-tasks/member-ownership.js +40 -0
- package/dist/rooms-tasks/member-startup.d.ts +1 -0
- package/dist/rooms-tasks/member-startup.js +2 -0
- package/dist/rooms-tasks/provision.d.ts +3 -0
- package/dist/rooms-tasks/provision.js +101 -49
- package/dist/rooms-tasks/recovery.d.ts +19 -0
- package/dist/rooms-tasks/recovery.js +110 -0
- package/dist/rooms-tasks/room-state.d.ts +4 -2
- package/dist/rooms-tasks/room-state.js +47 -8
- package/dist/rooms-tasks/task-review.d.ts +113 -0
- package/dist/rooms-tasks/task-review.js +561 -0
- package/dist/rooms-tasks/task-service-retirement.d.ts +11 -0
- package/dist/rooms-tasks/task-service-retirement.js +21 -0
- package/dist/rooms-tasks/task-state.d.ts +51 -5
- package/dist/rooms-tasks/task-state.js +358 -50
- package/dist/rooms-tasks/task-tools.d.ts +35 -0
- package/dist/rooms-tasks/task-tools.js +134 -0
- package/dist/rooms-tasks/types.d.ts +78 -0
- package/dist/rooms-tasks/workspace-artifacts.d.ts +6 -0
- package/dist/rooms-tasks/workspace-artifacts.js +93 -0
- package/dist/rooms-tasks/workspace.d.ts +35 -0
- package/dist/rooms-tasks/workspace.js +325 -0
- package/dist/runner.d.ts +23 -4
- package/dist/runner.js +409 -61
- package/dist/session/acp.d.ts +75 -2
- package/dist/session/acp.js +345 -82
- package/dist/session/arbiter.d.ts +98 -0
- package/dist/session/arbiter.js +272 -3
- package/dist/session/codex-app-server.d.ts +45 -1
- package/dist/session/codex-app-server.js +225 -32
- package/dist/session/control.d.ts +17 -3
- package/dist/session/control.js +219 -23
- package/dist/session/conversation-normalizer.d.ts +2 -1
- package/dist/session/conversation-normalizer.js +1 -1
- package/dist/session/conversation-observation.d.ts +25 -0
- package/dist/session/conversation-observation.js +100 -0
- package/dist/session/conversation-replay.d.ts +50 -0
- package/dist/session/conversation-replay.js +203 -0
- package/dist/session/conversation-store.d.ts +15 -1
- package/dist/session/conversation-store.js +213 -26
- package/dist/session/conversation-types.d.ts +57 -4
- package/dist/session/prompt-batch.d.ts +79 -0
- package/dist/session/prompt-batch.js +120 -0
- package/dist/session/task-notice.d.ts +14 -0
- package/dist/session/task-notice.js +13 -0
- package/dist/session/types.d.ts +42 -1
- package/dist/setup-tunnel-preflight.d.ts +34 -0
- package/dist/setup-tunnel-preflight.js +62 -0
- package/dist/socket-path.d.ts +2 -0
- package/dist/socket-path.js +22 -0
- package/dist/spawn.d.ts +5 -1
- package/dist/spawn.js +40 -4
- package/dist/startup-prompt.d.ts +15 -0
- package/dist/startup-prompt.js +71 -0
- package/dist/startup-readiness.d.ts +20 -1
- package/dist/startup-readiness.js +66 -3
- package/dist/startup-telemetry.d.ts +16 -0
- package/dist/startup-telemetry.js +46 -0
- package/dist/subscriptions/cli.d.ts +91 -0
- package/dist/subscriptions/cli.js +223 -0
- package/dist/subscriptions/launch.d.ts +37 -0
- package/dist/subscriptions/launch.js +93 -0
- package/dist/subscriptions/login.d.ts +77 -0
- package/dist/subscriptions/login.js +215 -0
- package/dist/subscriptions/observed.d.ts +7 -0
- package/dist/subscriptions/observed.js +44 -0
- package/dist/subscriptions/probe.d.ts +10 -0
- package/dist/subscriptions/probe.js +73 -0
- package/dist/subscriptions/service.d.ts +102 -0
- package/dist/subscriptions/service.js +364 -0
- package/dist/subscriptions/store.d.ts +71 -0
- package/dist/subscriptions/store.js +143 -0
- package/dist/supervisor/adoption.d.ts +8 -0
- package/dist/supervisor/adoption.js +78 -0
- package/dist/supervisor/catalog.d.ts +62 -0
- package/dist/supervisor/catalog.js +254 -0
- package/dist/supervisor/fleet.d.ts +8 -0
- package/dist/supervisor/fleet.js +89 -0
- package/dist/supervisor/index.js +3 -6
- package/dist/supervisor/launchd.d.ts +1 -1
- package/dist/supervisor/launchd.js +26 -13
- package/dist/supervisor/legacy.d.ts +35 -0
- package/dist/supervisor/legacy.js +524 -0
- package/dist/supervisor/manager.d.ts +15 -0
- package/dist/supervisor/manager.js +270 -0
- package/dist/supervisor/registration-refresh.d.ts +45 -0
- package/dist/supervisor/registration-refresh.js +266 -0
- package/dist/supervisor/scope.d.ts +4 -0
- package/dist/supervisor/scope.js +10 -0
- package/dist/supervisor/systemd.d.ts +2 -2
- package/dist/supervisor/systemd.js +18 -37
- package/dist/task-supervision.d.ts +9 -0
- package/dist/task-supervision.js +145 -0
- package/dist/temp-idle.d.ts +17 -0
- package/dist/temp-idle.js +42 -0
- package/dist/temp-lifecycle.d.ts +23 -8
- package/dist/temp-lifecycle.js +123 -66
- package/dist/temp-supervisor-recovery.d.ts +1 -1
- package/dist/temp-supervisor-recovery.js +2 -2
- package/dist/tunnel-setup-completion.d.ts +7 -0
- package/dist/tunnel-setup-completion.js +13 -0
- package/dist/tunnel-setup-input.d.ts +19 -0
- package/dist/tunnel-setup-input.js +130 -0
- package/dist/watchdog/query.d.ts +3 -1
- package/dist/watchdog/query.js +2 -1
- package/dist/web/agent-attachments.d.ts +29 -0
- package/dist/web/agent-attachments.js +162 -0
- package/dist/web/auth.d.ts +65 -2
- package/dist/web/auth.js +201 -11
- package/dist/web/available-port.d.ts +2 -0
- package/dist/web/available-port.js +23 -0
- package/dist/web/control.d.ts +3 -3
- package/dist/web/control.js +10 -8
- package/dist/web/conversation-stream-codec.d.ts +23 -0
- package/dist/web/conversation-stream-codec.js +110 -0
- package/dist/web/conversation-stream.d.ts +26 -0
- package/dist/web/conversation-stream.js +239 -0
- package/dist/web/events.d.ts +2 -0
- package/dist/web/events.js +9 -0
- package/dist/web/fleet-config-service.d.ts +7 -1
- package/dist/web/fleet-config-service.js +61 -16
- package/dist/web/prefix-gateway.d.ts +32 -0
- package/dist/web/prefix-gateway.js +248 -0
- package/dist/web/resource-views.d.ts +138 -0
- package/dist/web/resource-views.js +519 -0
- package/dist/web/runtime.d.ts +3 -1
- package/dist/web/runtime.js +132 -34
- package/dist/web/server.d.ts +19 -0
- package/dist/web/server.js +893 -71
- package/dist/web/service.d.ts +11 -2
- package/dist/web/service.js +30 -4
- package/dist/web/workspace-devices.d.ts +107 -0
- package/dist/web/workspace-devices.js +277 -0
- package/dist/web/workspace-tunnel.d.ts +1 -0
- package/dist/web/workspace-tunnel.js +34 -0
- package/dist/workspace-enrollment.d.ts +83 -0
- package/dist/workspace-enrollment.js +605 -0
- package/dist/workspace-replacement.d.ts +66 -0
- package/dist/workspace-replacement.js +238 -0
- package/docs/acp-files.md +146 -0
- package/docs/conversation-stream.md +216 -0
- package/docs/fleet-migration.md +54 -0
- package/docs/live-voice.md +13 -0
- package/docs/room-layouts.md +227 -0
- package/docs/self-reminders.md +374 -0
- package/docs/task-progress-review.md +143 -0
- package/docs/task-workspaces.md +225 -0
- package/docs/validation/managed-cli-permissions.md +421 -0
- package/examples/room-layouts/fresh.yaml +27 -0
- package/examples/room-layouts/scoped.yaml +21 -0
- package/examples/room-layouts/shared.yaml +19 -0
- package/package.json +25 -20
- package/presets/README.md +78 -0
- package/presets/brain-catalog.json +8 -2
- package/presets/fleet/agent_templates/Critic.yaml +1 -1
- package/presets/fleet/agent_templates/Developer.yaml +1 -1
- package/presets/fleet/agent_templates/Engineer.yaml +5 -0
- package/presets/fleet/agent_templates/LocalCoordinator.yaml +6 -3
- package/presets/fleet/agents/FleetCoordinator.yaml +1 -1
- package/presets/fleet/brains/claude-fable-5-1-high.yaml +5 -0
- package/presets/fleet/brains/claude-fable-5-1-low.yaml +5 -0
- package/presets/fleet/brains/claude-fable-5-1-max.yaml +5 -0
- package/presets/fleet/brains/claude-fable-5-1-medium.yaml +5 -0
- package/presets/fleet/brains/claude-fable-5-1-xhigh.yaml +5 -0
- package/presets/fleet/brains/claude-fable-5-high.yaml +1 -1
- package/presets/fleet/brains/claude-fable-5-low.yaml +1 -1
- package/presets/fleet/brains/claude-fable-5-max.yaml +1 -1
- package/presets/fleet/brains/claude-fable-5-medium.yaml +1 -1
- package/presets/fleet/brains/claude-fable-5-xhigh.yaml +1 -1
- package/presets/fleet/brains/claude-haiku-4-5-20251001-default.yaml +4 -0
- package/presets/fleet/brains/claude-opus-5-5-high.yaml +5 -0
- package/presets/fleet/brains/claude-opus-5-5-low.yaml +5 -0
- package/presets/fleet/brains/claude-opus-5-5-max.yaml +5 -0
- package/presets/fleet/brains/claude-opus-5-5-medium.yaml +5 -0
- package/presets/fleet/brains/claude-opus-5-5-xhigh.yaml +5 -0
- package/presets/fleet/brains/claude-opus-5-high.yaml +1 -1
- package/presets/fleet/brains/claude-opus-5-low.yaml +1 -1
- package/presets/fleet/brains/claude-opus-5-max.yaml +1 -1
- package/presets/fleet/brains/claude-opus-5-medium.yaml +1 -1
- package/presets/fleet/brains/claude-opus-5-xhigh.yaml +1 -1
- package/presets/fleet/brains/claude-sonnet-5-high.yaml +1 -1
- package/presets/fleet/brains/claude-sonnet-5-low.yaml +1 -1
- package/presets/fleet/brains/claude-sonnet-5-max.yaml +1 -1
- package/presets/fleet/brains/claude-sonnet-5-medium.yaml +1 -1
- package/presets/fleet/brains/claude-sonnet-5-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-3-codex-spark-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-3-codex-spark-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-3-codex-spark-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-3-codex-spark-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-mini-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-mini-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-mini-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-mini-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-5-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-5-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-5-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-5-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-luna-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-luna-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-luna-max.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-luna-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-luna-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-sol-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-sol-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-sol-max.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-sol-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-sol-ultra.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-sol-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-terra-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-terra-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-terra-max.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-terra-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-terra-ultra.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-terra-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-6-1-sol-high.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-1-sol-low.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-1-sol-max.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-1-sol-medium.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-1-sol-ultra.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-1-sol-xhigh.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-astra-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-6-astra-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-6-astra-max.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-6-astra-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-6-astra-ultra.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-6-astra-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-6-luna-high.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-luna-low.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-luna-max.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-luna-medium.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-luna-xhigh.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-sol-high.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-sol-low.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-sol-max.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-sol-medium.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-sol-ultra.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-sol-xhigh.yaml +5 -0
- package/presets/fleet/roles/Coordinator.yaml +21 -13
- package/presets/fleet/roles/Critic.yaml +24 -0
- package/presets/fleet/roles/Developer.yaml +32 -0
- package/presets/fleet/roles/Engineer.yaml +72 -0
- package/presets/fleet/roles/LocalCoordinator.yaml +17 -0
- package/presets/fleet/room_layouts/engineering.yaml +25 -0
- package/presets/fleet/room_layouts/pair.yaml +24 -0
- package/presets/fleet/room_layouts/single.yaml +17 -0
- package/presets/fleet/room_layouts/team.yaml +30 -0
- package/presets/fleet/room_templates/engineering.yaml +19 -0
- package/presets/manifest.json +2 -2
- package/dist/web-app/assets/index-59GF-gsZ.css +0 -1
- package/dist/web-app/assets/index-C0h3ALvs.js +0 -10
- package/dist/web-app/icons/ours-fleet-maskable.svg +0 -4
- package/dist/web-app/icons/ours-fleet.svg +0 -4
- package/dist/web-app/index.html +0 -17
- package/dist/web-app/manifest.webmanifest +0 -15
- package/dist/web-app/offline.html +0 -18
- package/dist/web-app/sw.js +0 -51
package/dist/session/control.js
CHANGED
|
@@ -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.
|
|
399
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
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
|
-
|
|
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',
|
|
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;
|
|
@@ -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 {};
|