@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.
- package/README.md +279 -68
- package/dist/account-origin.d.ts +2 -0
- package/dist/account-origin.js +6 -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 +19 -0
- package/dist/agent-ours/mcp-endpoint.js +98 -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 +24 -1
- package/dist/agent-ours/service.js +97 -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 +34 -0
- package/dist/application/conversation-history.js +280 -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 +40 -0
- package/dist/application/resource-observer.js +278 -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 +1 -1
- package/dist/application/role-repository.js +30 -7
- 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 +61 -17
- package/dist/application/session-control.js +207 -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 +97 -31
- package/dist/build-info.json +9 -4
- package/dist/capabilities.d.ts +9 -1
- package/dist/capabilities.js +13 -0
- package/dist/cli.js +400 -37
- 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 +207 -19
- 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 +7 -0
- package/dist/fleet-command-audit.js +24 -8
- 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 +22 -0
- package/dist/notifications/fleet-producer.js +237 -0
- package/dist/notifications/outbox.d.ts +46 -0
- package/dist/notifications/outbox.js +158 -0
- package/dist/notifications/target-cleanup.d.ts +11 -0
- package/dist/notifications/target-cleanup.js +63 -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 +2 -0
- package/dist/paths.js +2 -0
- package/dist/preset-migration.js +43 -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 +8 -0
- package/dist/rooms-tasks/archived-absence.js +48 -0
- package/dist/rooms-tasks/cli.js +65 -5
- package/dist/rooms-tasks/close.d.ts +6 -2
- package/dist/rooms-tasks/close.js +246 -37
- 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 +84 -5
- 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-service-retirement.d.ts +11 -0
- package/dist/rooms-tasks/task-service-retirement.js +21 -0
- package/dist/rooms-tasks/task-state.d.ts +52 -8
- package/dist/rooms-tasks/task-state.js +251 -26
- package/dist/rooms-tasks/types.d.ts +40 -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 +33 -4
- package/dist/runner.js +410 -62
- 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 +213 -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 +206 -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 +32 -0
- package/dist/startup-readiness.js +101 -0
- 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 +19 -33
- 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 +124 -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 +25 -0
- package/dist/web/conversation-stream.js +231 -0
- package/dist/web/events.d.ts +2 -0
- package/dist/web/events.js +9 -0
- package/dist/web/fleet-config-service.d.ts +4 -1
- package/dist/web/fleet-config-service.js +46 -15
- package/dist/web/prefix-gateway.d.ts +32 -0
- package/dist/web/prefix-gateway.js +248 -0
- package/dist/web/resource-views.d.ts +124 -0
- package/dist/web/resource-views.js +424 -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 +802 -67
- 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 +580 -0
- package/dist/workspace-replacement.d.ts +66 -0
- package/dist/workspace-replacement.js +220 -0
- package/docs/acp-files.md +146 -0
- package/docs/conversation-stream.md +196 -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-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 +24 -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 +4 -2
- 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 +12 -11
- package/presets/fleet/roles/Critic.yaml +13 -0
- package/presets/fleet/roles/Developer.yaml +13 -0
- package/presets/fleet/roles/Engineer.yaml +53 -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,98 @@ 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
|
+
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
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
|
-
|
|
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',
|
|
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;
|
|
@@ -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 {};
|