@ours.network/fleet 1.2.0-nightly.9 → 2.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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 +5 -1
- package/dist/briefing.js +138 -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 +402 -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
|
@@ -0,0 +1,130 @@
|
|
|
1
|
+
/** Web authoring of `fleet/room_layouts/<name>.yaml`.
|
|
2
|
+
* Writes are revision-guarded (sha256 of the file bytes) and validated with the
|
|
3
|
+
* same parser the CLI uses. Retained layout runs keep their own snapshots, so
|
|
4
|
+
* editing or deleting a definition never changes an existing run.
|
|
5
|
+
*/
|
|
6
|
+
import { createHash } from 'node:crypto';
|
|
7
|
+
import { existsSync, mkdirSync, readFileSync, readdirSync, unlinkSync } from 'node:fs';
|
|
8
|
+
import { basename, extname, join } from 'node:path';
|
|
9
|
+
import { stringify } from 'yaml';
|
|
10
|
+
import { replaceFileAtomically, withFileLock } from '../atomic-file.js';
|
|
11
|
+
import { parseFleetDocument } from '../config-yaml.js';
|
|
12
|
+
import { defaultConfigPath } from '../paths.js';
|
|
13
|
+
import { loadConfig, splitRootFor } from '../config.js';
|
|
14
|
+
import { assertLayoutFile, validLayoutKey, validateRoomLayout } from '../rooms-tasks/layout-config.js';
|
|
15
|
+
import { FleetError } from './errors.js';
|
|
16
|
+
import { layoutDefinitionHash } from './task-layouts.js';
|
|
17
|
+
export const ABSENT_REVISION = 'absent';
|
|
18
|
+
const revisionOf = (bytes) => createHash('sha256').update(bytes).digest('hex');
|
|
19
|
+
export class RoomLayoutDefinitions {
|
|
20
|
+
configPath;
|
|
21
|
+
deps;
|
|
22
|
+
constructor(configPath, deps = {}) {
|
|
23
|
+
this.configPath = configPath;
|
|
24
|
+
this.deps = deps;
|
|
25
|
+
}
|
|
26
|
+
root() { return join(splitRootFor(this.configPath ?? defaultConfigPath()), 'room_layouts'); }
|
|
27
|
+
pathFor(name) {
|
|
28
|
+
if (!validLayoutKey(name))
|
|
29
|
+
throw new FleetError('invalid_request', 'layout name must start with a letter and use letters, digits, - or _');
|
|
30
|
+
return join(this.root(), `${name}.yaml`);
|
|
31
|
+
}
|
|
32
|
+
currentRevision(path) {
|
|
33
|
+
if (!existsSync(path))
|
|
34
|
+
return ABSENT_REVISION;
|
|
35
|
+
assertLayoutFile(path);
|
|
36
|
+
return revisionOf(readFileSync(path));
|
|
37
|
+
}
|
|
38
|
+
/** Agent-template references that do not resolve in the current Fleet configuration. */
|
|
39
|
+
templateIssues(definition) {
|
|
40
|
+
let templates = {};
|
|
41
|
+
try {
|
|
42
|
+
templates = (this.deps.loadConfiguration ?? loadConfig)(this.configPath).agentTemplates ?? {};
|
|
43
|
+
}
|
|
44
|
+
catch (error) {
|
|
45
|
+
return [`fleet configuration unavailable: ${error.message}`];
|
|
46
|
+
}
|
|
47
|
+
return Object.entries(definition.participants).flatMap(([key, participant]) => participant.agent_template && !Object.hasOwn(templates, participant.agent_template)
|
|
48
|
+
? [`participant ${key}: agent template not found: ${participant.agent_template}`] : []);
|
|
49
|
+
}
|
|
50
|
+
/** The configured default (`rooms.defaults.template`, else `tasks.default_room_template`) when it names a usable layout. */
|
|
51
|
+
defaultLayout(entries = this.list()) {
|
|
52
|
+
let name;
|
|
53
|
+
try {
|
|
54
|
+
const config = (this.deps.loadConfiguration ?? loadConfig)(this.configPath);
|
|
55
|
+
name = config.rooms?.defaults?.template ?? config.tasks?.default_room_template;
|
|
56
|
+
}
|
|
57
|
+
catch {
|
|
58
|
+
return undefined;
|
|
59
|
+
}
|
|
60
|
+
const entry = entries.find(e => e.name === name);
|
|
61
|
+
return entry && !('error' in entry) && !entry.issues.length ? entry.name : undefined;
|
|
62
|
+
}
|
|
63
|
+
list() {
|
|
64
|
+
const root = this.root();
|
|
65
|
+
if (!existsSync(root))
|
|
66
|
+
return [];
|
|
67
|
+
assertLayoutFile(root, true);
|
|
68
|
+
return readdirSync(root).filter(file => /\.ya?ml$/.test(file)).sort().map(file => {
|
|
69
|
+
const name = basename(file, extname(file)), path = join(root, file);
|
|
70
|
+
try {
|
|
71
|
+
if (!validLayoutKey(name))
|
|
72
|
+
throw Error(`invalid room layout id: ${name}`);
|
|
73
|
+
assertLayoutFile(path);
|
|
74
|
+
const bytes = readFileSync(path);
|
|
75
|
+
const definition = validateRoomLayout(parseFleetDocument(path, bytes.toString('utf8'), 'strict').value, file);
|
|
76
|
+
return { name, file, revision: revisionOf(bytes), definition, issues: this.templateIssues(definition), definition_hash: layoutDefinitionHash(definition) };
|
|
77
|
+
}
|
|
78
|
+
catch (error) {
|
|
79
|
+
// Only a trusted regular file may be read to offer an overwrite revision.
|
|
80
|
+
let revision = ABSENT_REVISION;
|
|
81
|
+
try {
|
|
82
|
+
assertLayoutFile(path);
|
|
83
|
+
revision = revisionOf(readFileSync(path));
|
|
84
|
+
}
|
|
85
|
+
catch { /* not trusted: never read */ }
|
|
86
|
+
return { name, file, revision, error: error.message };
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
validate(raw) {
|
|
91
|
+
let definition;
|
|
92
|
+
try {
|
|
93
|
+
definition = validateRoomLayout(raw, 'room layout');
|
|
94
|
+
}
|
|
95
|
+
catch (error) {
|
|
96
|
+
return { issues: [error.message] };
|
|
97
|
+
}
|
|
98
|
+
return { definition, issues: this.templateIssues(definition) };
|
|
99
|
+
}
|
|
100
|
+
async save(name, expectedRevision, raw) {
|
|
101
|
+
const path = this.pathFor(name);
|
|
102
|
+
const { definition, issues } = this.validate(raw);
|
|
103
|
+
if (!definition || issues.length)
|
|
104
|
+
throw new FleetError('invalid_request', issues.join('; '));
|
|
105
|
+
const root = this.root();
|
|
106
|
+
mkdirSync(root, { recursive: true, mode: 0o700 });
|
|
107
|
+
assertLayoutFile(root, true);
|
|
108
|
+
return withFileLock(`${path}.lock`, async () => {
|
|
109
|
+
const current = this.currentRevision(path);
|
|
110
|
+
if (current !== expectedRevision)
|
|
111
|
+
throw new FleetError('stale_state', 'room layout changed since it was loaded; reload before saving');
|
|
112
|
+
const { description, participants, rooms } = definition;
|
|
113
|
+
const text = stringify({ version: 1, ...(description ? { description } : {}), participants, rooms });
|
|
114
|
+
replaceFileAtomically(path, text, 0o600);
|
|
115
|
+
return { name, revision: revisionOf(text), definition };
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
async remove(name, expectedRevision) {
|
|
119
|
+
const path = this.pathFor(name);
|
|
120
|
+
return withFileLock(`${path}.lock`, async () => {
|
|
121
|
+
const current = this.currentRevision(path);
|
|
122
|
+
if (current === ABSENT_REVISION)
|
|
123
|
+
return { name, deleted: false };
|
|
124
|
+
if (current !== expectedRevision)
|
|
125
|
+
throw new FleetError('stale_state', 'room layout changed since it was loaded; reload before deleting');
|
|
126
|
+
unlinkSync(path);
|
|
127
|
+
return { name, deleted: true };
|
|
128
|
+
});
|
|
129
|
+
}
|
|
130
|
+
}
|
|
@@ -1,6 +1,10 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import
|
|
1
|
+
import type { ConversationRoleObservation, ConversationRoleState, ObserveConversationRole } from './conversation-role-observer.js';
|
|
2
|
+
import { type ConversationTailPage } from './conversation-history.js';
|
|
3
|
+
import type { AgentHistoryQuery } from '../agent-ours/correspondence.js';
|
|
4
|
+
import { controlRequest, followConversation } from '../session/control.js';
|
|
5
|
+
import type { ConversationEventV1, ConversationSnapshot, ConversationObservationState, PromptReceipt } from '../session/conversation-types.js';
|
|
3
6
|
import type { SessionSnapshot } from '../session/types.js';
|
|
7
|
+
import type { PassiveConversationFollowRequest, ConversationReplayPage, ConversationReplayReady } from '../session/conversation-replay.js';
|
|
4
8
|
import type { OutputPage, SendReceipt, SessionDescriptor } from './types.js';
|
|
5
9
|
export interface ConversationPageView {
|
|
6
10
|
events: ConversationEventV1[];
|
|
@@ -12,6 +16,23 @@ export interface ConversationPageView {
|
|
|
12
16
|
export interface ConversationFollowHandle {
|
|
13
17
|
close(): void;
|
|
14
18
|
}
|
|
19
|
+
export interface LegacyConversationFollowRequest {
|
|
20
|
+
after?: string;
|
|
21
|
+
onPage(page: ConversationPageView): void;
|
|
22
|
+
onEvent(event: ConversationEventV1): void;
|
|
23
|
+
onClose(reason?: string): void;
|
|
24
|
+
}
|
|
25
|
+
export interface PassiveConversationFollow extends PassiveConversationFollowRequest {
|
|
26
|
+
/** Reports role updates from the owned or borrowed observer; never attaches a controller. */
|
|
27
|
+
onRole?(state: ConversationRoleState): void;
|
|
28
|
+
/** Application-only borrowed owner; never sent over the private wire. */
|
|
29
|
+
roleObservation?: ConversationRoleObservation;
|
|
30
|
+
onReplay(page: ConversationReplayPage): void;
|
|
31
|
+
onReady(ready: ConversationReplayReady): void;
|
|
32
|
+
onEvent(event: ConversationEventV1): void;
|
|
33
|
+
onState(state: ConversationObservationState): void;
|
|
34
|
+
onClose(reason?: string): void;
|
|
35
|
+
}
|
|
15
36
|
/**
|
|
16
37
|
* An interrupt that reached the session. `state` says whether the turn stopped
|
|
17
38
|
* cooperatively or through bounded forced recovery — both are accepted, so no
|
|
@@ -23,7 +44,11 @@ export interface InterruptReceipt {
|
|
|
23
44
|
reasonCode?: string;
|
|
24
45
|
}
|
|
25
46
|
export interface RoleSessionControl {
|
|
26
|
-
|
|
47
|
+
/** Standalone owner for role-only observation; dispose with that connection. */
|
|
48
|
+
observeRole?: ObserveConversationRole;
|
|
49
|
+
agentContacts?(): Promise<unknown>;
|
|
50
|
+
agentHistory?(request: AgentHistoryQuery): Promise<unknown>;
|
|
51
|
+
describe(roleObservation?: ConversationRoleObservation): Promise<SessionDescriptor>;
|
|
27
52
|
snapshot(): Promise<SessionSnapshot>;
|
|
28
53
|
recentOutput(request?: {
|
|
29
54
|
since?: number;
|
|
@@ -41,12 +66,25 @@ export interface RoleSessionControl {
|
|
|
41
66
|
after?: string;
|
|
42
67
|
limit?: number;
|
|
43
68
|
}): Promise<ConversationPageView>;
|
|
69
|
+
/** The newest visible events (or those before `before`), read backwards from the ledger. */
|
|
70
|
+
conversationTail?(request: {
|
|
71
|
+
before?: string;
|
|
72
|
+
limit?: number;
|
|
73
|
+
signal?: AbortSignal;
|
|
74
|
+
}): Promise<ConversationTailPage>;
|
|
44
75
|
submitPromptV2?(request: {
|
|
45
76
|
commandId: string;
|
|
46
77
|
text: string;
|
|
47
78
|
actorBrowserSession: string;
|
|
79
|
+
expectedSessionGeneration?: string;
|
|
80
|
+
requireIdle?: boolean;
|
|
48
81
|
source: 'owner_admin_console';
|
|
49
82
|
}): Promise<PromptReceipt>;
|
|
83
|
+
interruptPromptV2?(request: {
|
|
84
|
+
commandId: string;
|
|
85
|
+
expectedSessionGeneration: string;
|
|
86
|
+
promptId: string;
|
|
87
|
+
}): Promise<InterruptReceipt>;
|
|
50
88
|
interruptV2?(commandId: string): Promise<InterruptReceipt & {
|
|
51
89
|
commandId: string;
|
|
52
90
|
}>;
|
|
@@ -64,18 +102,19 @@ export interface RoleSessionControl {
|
|
|
64
102
|
* replay page; `onEvent` every subsequent durable event; `onClose` fires
|
|
65
103
|
* exactly once when the underlying control stream ends.
|
|
66
104
|
*/
|
|
67
|
-
followConversation?(request:
|
|
68
|
-
after?: string;
|
|
69
|
-
onPage(page: ConversationPageView): void;
|
|
70
|
-
onEvent(event: ConversationEventV1): void;
|
|
71
|
-
onClose(reason?: string): void;
|
|
72
|
-
}): Promise<ConversationFollowHandle>;
|
|
105
|
+
followConversation?(request: LegacyConversationFollowRequest | PassiveConversationFollow): Promise<ConversationFollowHandle>;
|
|
73
106
|
}
|
|
74
107
|
export declare class RoleSessionControlAdapter implements RoleSessionControl {
|
|
75
108
|
private readonly stateDir;
|
|
76
109
|
private readonly request;
|
|
77
|
-
|
|
78
|
-
|
|
110
|
+
private readonly openFollow;
|
|
111
|
+
readonly observeRole?: ObserveConversationRole | undefined;
|
|
112
|
+
private controlHealthy;
|
|
113
|
+
constructor(stateDir: string, request?: typeof controlRequest, openFollow?: typeof followConversation, observeRole?: ObserveConversationRole | undefined);
|
|
114
|
+
agentContacts(): Promise<unknown>;
|
|
115
|
+
agentHistory(request: AgentHistoryQuery): Promise<unknown>;
|
|
116
|
+
private requireCorrespondence;
|
|
117
|
+
describe(roleObservation?: ConversationRoleObservation): Promise<SessionDescriptor>;
|
|
79
118
|
snapshot(): Promise<SessionSnapshot>;
|
|
80
119
|
recentOutput(request?: {
|
|
81
120
|
since?: number;
|
|
@@ -87,12 +126,24 @@ export declare class RoleSessionControlAdapter implements RoleSessionControl {
|
|
|
87
126
|
after?: string;
|
|
88
127
|
limit?: number;
|
|
89
128
|
}): Promise<ConversationPageView>;
|
|
129
|
+
conversationTail(request?: {
|
|
130
|
+
before?: string;
|
|
131
|
+
limit?: number;
|
|
132
|
+
signal?: AbortSignal;
|
|
133
|
+
}): Promise<ConversationTailPage>;
|
|
90
134
|
submitPromptV2(request: {
|
|
91
135
|
commandId: string;
|
|
92
136
|
text: string;
|
|
93
137
|
actorBrowserSession: string;
|
|
138
|
+
expectedSessionGeneration?: string;
|
|
139
|
+
requireIdle?: boolean;
|
|
94
140
|
source: 'owner_admin_console';
|
|
95
141
|
}): Promise<PromptReceipt>;
|
|
142
|
+
interruptPromptV2(request: {
|
|
143
|
+
commandId: string;
|
|
144
|
+
expectedSessionGeneration: string;
|
|
145
|
+
promptId: string;
|
|
146
|
+
}): Promise<InterruptReceipt>;
|
|
96
147
|
interruptV2(commandId: string): Promise<InterruptReceipt & {
|
|
97
148
|
commandId: string;
|
|
98
149
|
}>;
|
|
@@ -105,12 +156,7 @@ export declare class RoleSessionControlAdapter implements RoleSessionControl {
|
|
|
105
156
|
accepted: true;
|
|
106
157
|
commandId: string;
|
|
107
158
|
}>;
|
|
108
|
-
followConversation(request:
|
|
109
|
-
after?: string;
|
|
110
|
-
onPage(page: ConversationPageView): void;
|
|
111
|
-
onEvent(event: ConversationEventV1): void;
|
|
112
|
-
onClose(reason?: string): void;
|
|
113
|
-
}): Promise<ConversationFollowHandle>;
|
|
159
|
+
followConversation(request: LegacyConversationFollowRequest | PassiveConversationFollow): Promise<ConversationFollowHandle>;
|
|
114
160
|
respondPermission(request: {
|
|
115
161
|
permissionId: string;
|
|
116
162
|
optionId: string;
|
|
@@ -1,20 +1,42 @@
|
|
|
1
|
+
import { readFile } from 'node:fs/promises';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { conversationTailPage, resumedConversationPage } from './conversation-history.js';
|
|
1
4
|
import { controlRequest, followConversation } from '../session/control.js';
|
|
2
5
|
import { FleetError, normalizeError } from './errors.js';
|
|
3
6
|
const visibleEvents = (events) => events.filter(event => event.kind !== 'thought');
|
|
4
7
|
export class RoleSessionControlAdapter {
|
|
5
8
|
stateDir;
|
|
6
9
|
request;
|
|
7
|
-
|
|
10
|
+
openFollow;
|
|
11
|
+
observeRole;
|
|
12
|
+
controlHealthy = true;
|
|
13
|
+
constructor(stateDir, request = controlRequest, openFollow = followConversation, observeRole) {
|
|
8
14
|
this.stateDir = stateDir;
|
|
9
15
|
this.request = request;
|
|
16
|
+
this.openFollow = openFollow;
|
|
17
|
+
this.observeRole = observeRole;
|
|
10
18
|
}
|
|
11
|
-
async
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
19
|
+
async agentContacts() { await this.requireCorrespondence(); return this.call('agent_contacts'); }
|
|
20
|
+
async agentHistory(request) { await this.requireCorrespondence(); return this.call('agent_history', { agentHistory: request }); }
|
|
21
|
+
async requireCorrespondence() { if (!(await this.describe()).features.includes('agent_correspondence'))
|
|
22
|
+
throw new FleetError('capability_unavailable', 'This session needs an updated supervisor to show contacts and history.'); }
|
|
23
|
+
async describe(roleObservation) {
|
|
24
|
+
// A describe probe owns and always disposes its temporary watcher. Follow uses
|
|
25
|
+
// a separate connection-owned observer and publishes updates via onRole.
|
|
26
|
+
const role = roleObservation ?? this.observeRole?.(() => { });
|
|
27
|
+
try {
|
|
28
|
+
const response = await this.call('snapshot');
|
|
29
|
+
const result = response;
|
|
30
|
+
const features = (result.features ?? []).filter(feature => feature !== 'conversation_live_complete');
|
|
31
|
+
if (this.controlHealthy && role?.snapshot().health === 'healthy' && role.snapshot().availability === 'available'
|
|
32
|
+
&& features.includes('conversation_observation') && features.includes('control_heartbeat'))
|
|
33
|
+
features.push('conversation_live_complete');
|
|
34
|
+
return { backend: result.backend, protocolVersion: result.protocolVersion ?? 1, features, snapshot: result };
|
|
35
|
+
}
|
|
36
|
+
finally {
|
|
37
|
+
if (!roleObservation)
|
|
38
|
+
role?.dispose();
|
|
39
|
+
}
|
|
18
40
|
}
|
|
19
41
|
async snapshot() {
|
|
20
42
|
return await this.call('snapshot');
|
|
@@ -58,28 +80,103 @@ export class RoleSessionControlAdapter {
|
|
|
58
80
|
};
|
|
59
81
|
}
|
|
60
82
|
async conversationPage(request = {}) {
|
|
61
|
-
|
|
83
|
+
const live = await this.call('conversation_page', {
|
|
62
84
|
after: request.after, limit: request.limit,
|
|
63
85
|
});
|
|
86
|
+
const snapshot = await this.snapshot();
|
|
87
|
+
if (snapshot.backend !== 'acp' || !snapshot.sessionId)
|
|
88
|
+
return live;
|
|
89
|
+
try {
|
|
90
|
+
const history = await resumedConversationPage(this.stateDir, request, live, snapshot.sessionId);
|
|
91
|
+
const check = await this.call('conversation_page', { limit: 1 });
|
|
92
|
+
return check.snapshot.sessionGeneration === live.snapshot.sessionGeneration ? history : await this.call('conversation_page', request);
|
|
93
|
+
}
|
|
94
|
+
catch {
|
|
95
|
+
return live;
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
async conversationTail(request = {}) {
|
|
99
|
+
// A replacement while reading disk invalidates both the tail and its snapshot.
|
|
100
|
+
for (let attempt = 0; attempt < 3; attempt++) {
|
|
101
|
+
request.signal?.throwIfAborted();
|
|
102
|
+
const live = await this.call('conversation_page', { limit: 1 });
|
|
103
|
+
request.signal?.throwIfAborted();
|
|
104
|
+
const snapshot = await this.snapshot();
|
|
105
|
+
let resumed;
|
|
106
|
+
if (snapshot.backend === 'acp' && snapshot.sessionId) {
|
|
107
|
+
try {
|
|
108
|
+
if ((await readFile(join(this.stateDir, '.acp-session-id'), 'utf8')).trim() === snapshot.sessionId)
|
|
109
|
+
resumed = snapshot.sessionId;
|
|
110
|
+
}
|
|
111
|
+
catch { /* no resumed history: live generation only */ }
|
|
112
|
+
}
|
|
113
|
+
let page;
|
|
114
|
+
try {
|
|
115
|
+
page = await conversationTailPage(this.stateDir, request, live.snapshot, resumed, request.signal);
|
|
116
|
+
}
|
|
117
|
+
catch (error) {
|
|
118
|
+
request.signal?.throwIfAborted();
|
|
119
|
+
throw new FleetError('invalid_request', error.message);
|
|
120
|
+
}
|
|
121
|
+
request.signal?.throwIfAborted();
|
|
122
|
+
const check = await this.call('conversation_page', { limit: 1 });
|
|
123
|
+
request.signal?.throwIfAborted();
|
|
124
|
+
if (check.snapshot.sessionGeneration === live.snapshot.sessionGeneration)
|
|
125
|
+
return page;
|
|
126
|
+
}
|
|
127
|
+
throw new FleetError('stale_state', 'Session generation changed during conversation baseline; explicit recovery required.');
|
|
64
128
|
}
|
|
65
129
|
async submitPromptV2(request) {
|
|
66
130
|
if (!request.text.trim())
|
|
67
131
|
throw new FleetError('invalid_request', 'text is required');
|
|
68
132
|
if (Buffer.byteLength(request.text) > 32 * 1024)
|
|
69
133
|
throw new FleetError('invalid_request', 'text exceeds 32 KiB');
|
|
134
|
+
// A prompt bound to a session generation (voice or attachments) is checked atomically by supervisors that
|
|
135
|
+
// support it. Older supervisors do not know that command and never answer, so check the generation here
|
|
136
|
+
// and submit an ordinary prompt; its command id keeps retries idempotent either way.
|
|
137
|
+
const features = request.requireIdle ? (await this.describe()).features : undefined;
|
|
138
|
+
if (request.requireIdle && (!request.expectedSessionGeneration || !features?.includes('idle_bound_prompts')))
|
|
139
|
+
throw new FleetError('capability_unavailable', 'Live voice requires an updated supervisor.');
|
|
140
|
+
let command = 'submit_prompt_v2';
|
|
141
|
+
if (request.expectedSessionGeneration) {
|
|
142
|
+
if ((await this.describe()).features.includes('generation_bound_prompts'))
|
|
143
|
+
command = 'submit_voice_prompt';
|
|
144
|
+
else {
|
|
145
|
+
const live = await this.call('conversation_page', { limit: 1 });
|
|
146
|
+
if (live.snapshot?.sessionGeneration !== request.expectedSessionGeneration)
|
|
147
|
+
throw new FleetError('stale_state', 'Agent session changed. Record a new voice message.');
|
|
148
|
+
}
|
|
149
|
+
}
|
|
70
150
|
try {
|
|
71
|
-
return await this.call(
|
|
151
|
+
return await this.call(command, {
|
|
72
152
|
commandId: request.commandId, text: request.text, actor: request.actorBrowserSession,
|
|
73
|
-
source: request.source,
|
|
153
|
+
source: request.source, expectedSessionGeneration: request.expectedSessionGeneration, requireIdle: request.requireIdle,
|
|
74
154
|
});
|
|
75
155
|
}
|
|
76
156
|
catch (error) {
|
|
77
157
|
const fleetError = normalizeError(error);
|
|
158
|
+
if (fleetError.message.includes('session_changed:'))
|
|
159
|
+
throw new FleetError('stale_state', 'Agent session changed. Record a new voice message.');
|
|
160
|
+
if (fleetError.message.includes('voice_busy:'))
|
|
161
|
+
throw new FleetError('conflict', 'Agent is already working. Wait for the current turn.');
|
|
78
162
|
if (fleetError.message.includes('idempotency_conflict'))
|
|
79
163
|
throw new FleetError('idempotency_conflict', 'this command id was already used with a different prompt body');
|
|
80
164
|
throw fleetError;
|
|
81
165
|
}
|
|
82
166
|
}
|
|
167
|
+
async interruptPromptV2(request) {
|
|
168
|
+
if (!(await this.describe()).features.includes('targeted_voice_interrupt'))
|
|
169
|
+
throw new FleetError('capability_unavailable', 'Live voice requires an updated supervisor.');
|
|
170
|
+
try {
|
|
171
|
+
return await this.call('interrupt_voice_v2', request);
|
|
172
|
+
}
|
|
173
|
+
catch (error) {
|
|
174
|
+
const failure = normalizeError(error);
|
|
175
|
+
if (failure.message.includes('session_changed:'))
|
|
176
|
+
throw new FleetError('stale_state', 'Agent session changed.');
|
|
177
|
+
throw failure;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
83
180
|
async interruptV2(commandId) {
|
|
84
181
|
const receipt = await this.call('interrupt_v2', { commandId });
|
|
85
182
|
return {
|
|
@@ -101,32 +198,133 @@ export class RoleSessionControlAdapter {
|
|
|
101
198
|
}
|
|
102
199
|
}
|
|
103
200
|
async followConversation(request) {
|
|
201
|
+
const passive = 'controller' in request ? request : undefined;
|
|
104
202
|
let sawPage = false;
|
|
105
203
|
let closed = false;
|
|
204
|
+
let role;
|
|
205
|
+
let unsubscribeRole;
|
|
206
|
+
let lifetime;
|
|
207
|
+
let roleRevision = -1;
|
|
208
|
+
let silence;
|
|
209
|
+
let lastControlHeartbeat = Date.now();
|
|
210
|
+
const armSilence = () => {
|
|
211
|
+
if (silence)
|
|
212
|
+
clearTimeout(silence);
|
|
213
|
+
silence = setTimeout(() => {
|
|
214
|
+
// Liveness only: no role/session snapshots, regardless of web traffic.
|
|
215
|
+
if (Date.now() - lastControlHeartbeat >= 45_000) {
|
|
216
|
+
this.controlHealthy = false;
|
|
217
|
+
finish('control-silent');
|
|
218
|
+
}
|
|
219
|
+
else
|
|
220
|
+
armSilence();
|
|
221
|
+
}, Math.max(1, 45_000 - (Date.now() - lastControlHeartbeat)));
|
|
222
|
+
silence.unref?.();
|
|
223
|
+
};
|
|
224
|
+
let follow;
|
|
106
225
|
const finish = (reason) => {
|
|
107
226
|
if (closed)
|
|
108
227
|
return;
|
|
109
228
|
closed = true;
|
|
229
|
+
passive?.signal?.removeEventListener('abort', aborted);
|
|
230
|
+
if (silence)
|
|
231
|
+
clearTimeout(silence);
|
|
232
|
+
unsubscribeRole?.();
|
|
233
|
+
if (!passive?.roleObservation)
|
|
234
|
+
role?.dispose();
|
|
235
|
+
follow?.close();
|
|
110
236
|
request.onClose(reason);
|
|
111
237
|
};
|
|
112
|
-
const
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
238
|
+
const aborted = () => finish('conversation follow aborted');
|
|
239
|
+
passive?.signal?.addEventListener('abort', aborted, { once: true });
|
|
240
|
+
if (passive?.signal?.aborted)
|
|
241
|
+
aborted();
|
|
242
|
+
if (closed)
|
|
243
|
+
return { close: () => { } };
|
|
244
|
+
try {
|
|
245
|
+
if (passive) {
|
|
246
|
+
const changed = (state) => {
|
|
247
|
+
if (closed || state.roleRevision <= roleRevision)
|
|
248
|
+
return;
|
|
249
|
+
roleRevision = state.roleRevision;
|
|
250
|
+
passive.onRole?.(state);
|
|
251
|
+
if (state.health !== 'healthy') {
|
|
252
|
+
this.controlHealthy = false;
|
|
253
|
+
finish(state.failure ?? 'role-observation-unavailable');
|
|
254
|
+
}
|
|
255
|
+
else if (lifetime !== undefined && (state.roleLifetime !== lifetime || state.availability !== 'available' || state.stateDir !== this.stateDir))
|
|
256
|
+
finish('role-replaced');
|
|
257
|
+
lifetime = state.roleLifetime;
|
|
258
|
+
};
|
|
259
|
+
role = passive.roleObservation ?? this.observeRole?.(() => { });
|
|
260
|
+
if (role) {
|
|
261
|
+
const registration = role.subscribe(changed);
|
|
262
|
+
unsubscribeRole = registration.unsubscribe;
|
|
263
|
+
const initial = registration.snapshot;
|
|
264
|
+
changed(initial);
|
|
265
|
+
if (initial.availability !== 'available' || initial.stateDir !== this.stateDir)
|
|
266
|
+
finish('role-unavailable');
|
|
267
|
+
if (closed)
|
|
268
|
+
return { close: () => { } };
|
|
269
|
+
}
|
|
270
|
+
armSilence();
|
|
116
271
|
}
|
|
117
|
-
|
|
118
|
-
|
|
272
|
+
follow = await this.openFollow(this.stateDir, passive ? { controller: false, expectedGeneration: passive.expectedGeneration, after: passive.after, signal: passive.signal } : request.after, message => {
|
|
273
|
+
if (closed)
|
|
274
|
+
return;
|
|
275
|
+
if (passive) {
|
|
276
|
+
// A delayed callback must not revive an already elapsed source deadline.
|
|
277
|
+
if (Date.now() - lastControlHeartbeat >= 45_000) {
|
|
278
|
+
this.controlHealthy = false;
|
|
279
|
+
finish('control-silent');
|
|
280
|
+
return;
|
|
281
|
+
}
|
|
282
|
+
if (message.controlHeartbeat === true || message.conversationReplay || message.conversationReady || message.conversationEvent || message.conversationState) {
|
|
283
|
+
lastControlHeartbeat = Date.now();
|
|
284
|
+
this.controlHealthy = true;
|
|
285
|
+
armSilence();
|
|
286
|
+
}
|
|
287
|
+
if (message.controlHeartbeat === true)
|
|
288
|
+
return;
|
|
289
|
+
}
|
|
119
290
|
if (message.ok === false) {
|
|
120
291
|
finish(String(message.error ?? 'conversation follow refused'));
|
|
121
|
-
follow.close();
|
|
122
292
|
return;
|
|
123
293
|
}
|
|
124
|
-
|
|
294
|
+
if (message.conversationEvent) {
|
|
295
|
+
request.onEvent(message.conversationEvent);
|
|
296
|
+
return;
|
|
297
|
+
}
|
|
298
|
+
if (passive) {
|
|
299
|
+
if (message.conversationReplay)
|
|
300
|
+
passive.onReplay(message.conversationReplay);
|
|
301
|
+
else if (message.conversationReady)
|
|
302
|
+
passive.onReady(message.conversationReady);
|
|
303
|
+
else if (message.conversationState)
|
|
304
|
+
passive.onState(message.conversationState);
|
|
305
|
+
}
|
|
306
|
+
else if (!sawPage) {
|
|
307
|
+
sawPage = true;
|
|
308
|
+
request.onPage(message.result);
|
|
309
|
+
}
|
|
310
|
+
});
|
|
311
|
+
// Abort may win while the connector is still creating its handle.
|
|
312
|
+
if (closed || passive?.signal?.aborted) {
|
|
313
|
+
follow.close();
|
|
314
|
+
finish('conversation follow aborted');
|
|
125
315
|
}
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
316
|
+
else {
|
|
317
|
+
follow.socket.once('close', () => finish());
|
|
318
|
+
follow.socket.once('error', error => finish(error.message));
|
|
319
|
+
if (follow.socket.destroyed)
|
|
320
|
+
finish();
|
|
321
|
+
}
|
|
322
|
+
return { close: () => finish() };
|
|
323
|
+
}
|
|
324
|
+
catch (error) {
|
|
325
|
+
finish(error.message);
|
|
326
|
+
throw error;
|
|
327
|
+
}
|
|
130
328
|
}
|
|
131
329
|
async respondPermission(request) {
|
|
132
330
|
await this.call('respond_permission', request);
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { socketPath } from '../socket-path.js';
|
|
1
2
|
import { createHash } from 'node:crypto';
|
|
2
3
|
import { existsSync, readFileSync } from 'node:fs';
|
|
3
4
|
import { join } from 'node:path';
|
|
@@ -35,7 +36,8 @@ export class SupervisorOursTools {
|
|
|
35
36
|
const identity = legacy
|
|
36
37
|
? legacySupervisorIdentity(role, identityName, selected.temporary, descriptor.generation)
|
|
37
38
|
: { name: identityName, cid: descriptor.cid, generation: descriptor.generation };
|
|
38
|
-
|
|
39
|
+
const legacySocket = join(selected.dir, '.ours-bridge', `g${descriptor.generation}.sock`);
|
|
40
|
+
if (legacy && descriptor.socket !== legacySocket && descriptor.socket !== socketPath(legacySocket))
|
|
39
41
|
throw new FleetError('capability_unavailable', 'legacy supervisor socket does not match the selected agent');
|
|
40
42
|
if ((!legacy && (descriptor.role !== role || descriptor.identity !== identityName))
|
|
41
43
|
|| !/^[a-f0-9]{64}$/i.test(identity.cid ?? '') || !Number.isSafeInteger(descriptor.generation) || descriptor.generation < 1)
|
|
@@ -71,16 +73,8 @@ export class SupervisorOursTools {
|
|
|
71
73
|
verifySelection();
|
|
72
74
|
return;
|
|
73
75
|
}
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
const lines = blocks?.length === 1 && blocks[0].type === 'text' ? blocks[0].text?.split('\n') : undefined;
|
|
77
|
-
const prefix = `Bound to "${identity.name}" (${identity.cid})`;
|
|
78
|
-
const suffix = lines?.[0].startsWith(prefix) ? lines[0].slice(prefix.length) : undefined;
|
|
79
|
-
const roleSuffix = typeof suffix === 'string' && /^ — role "[^"\r\n]+" under root "[^"\r\n]+"\.$/.test(suffix);
|
|
80
|
-
const temporaryLine = 'TEMPORARY identity owned by the Fleet supervisor for this logical agent instance. Bridge or harness disconnect retains it; terminal supervisor release deletes local state with best-effort peer notices.';
|
|
81
|
-
if (actual.isError || !(roleSuffix || (selected.temporary && suffix === '.'))
|
|
82
|
-
|| (selected.temporary ? lines?.[1] !== temporaryLine : lines?.[1]?.startsWith('TEMPORARY')))
|
|
83
|
-
throw new FleetError('capability_unavailable', 'connected supervisor identity does not match legacy ownership proof');
|
|
76
|
+
// The supervisor owns the binding. The authenticated bridge and pinned local
|
|
77
|
+
// assignment are authoritative; current_identity is display text, not a protocol.
|
|
84
78
|
const fresh = legacySupervisorIdentity(role, identityName, selected.temporary, descriptor.generation);
|
|
85
79
|
if (!('proof' in identity) || fresh.proof !== identity.proof)
|
|
86
80
|
throw new FleetError('capability_unavailable', 'legacy supervisor identity changed');
|