@ours.network/fleet 1.2.0-nightly.9 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +280 -68
- package/dist/account-origin.d.ts +5 -0
- package/dist/account-origin.js +31 -0
- package/dist/agent-ours/bridge.js +26 -9
- package/dist/agent-ours/correspondence.d.ts +10 -0
- package/dist/agent-ours/correspondence.js +1 -0
- package/dist/agent-ours/file-rpc.d.ts +12 -0
- package/dist/agent-ours/file-rpc.js +18 -4
- package/dist/agent-ours/harness.js +9 -1
- package/dist/agent-ours/mcp-endpoint.d.ts +22 -0
- package/dist/agent-ours/mcp-endpoint.js +101 -25
- package/dist/agent-ours/runtime.d.ts +9 -1
- package/dist/agent-ours/runtime.js +50 -2
- package/dist/agent-ours/service.d.ts +27 -1
- package/dist/agent-ours/service.js +107 -13
- package/dist/application/codex-models.d.ts +15 -0
- package/dist/application/codex-models.js +56 -0
- package/dist/application/conversation-history.d.ts +31 -0
- package/dist/application/conversation-history.js +339 -0
- package/dist/application/conversation-role-observer.d.ts +62 -0
- package/dist/application/conversation-role-observer.js +206 -0
- package/dist/application/fleet-query-service.d.ts +1 -1
- package/dist/application/fleet-query-service.js +3 -3
- package/dist/application/log-service.js +8 -8
- package/dist/application/model-catalog.d.ts +1 -1
- package/dist/application/model-catalog.js +5 -2
- package/dist/application/preset-provenance.d.ts +19 -0
- package/dist/application/preset-provenance.js +59 -0
- package/dist/application/resource-observer.d.ts +43 -0
- package/dist/application/resource-observer.js +323 -0
- package/dist/application/role-creation-service.js +3 -2
- package/dist/application/role-removal-service.js +17 -3
- package/dist/application/role-repository.d.ts +3 -1
- package/dist/application/role-repository.js +89 -54
- package/dist/application/room-layout-definitions.d.ts +45 -0
- package/dist/application/room-layout-definitions.js +130 -0
- package/dist/application/session-control.d.ts +63 -17
- package/dist/application/session-control.js +221 -23
- package/dist/application/supervisor-ours-tools.js +5 -11
- package/dist/application/task-layouts.d.ts +91 -0
- package/dist/application/task-layouts.js +261 -0
- package/dist/application/task-room-service.d.ts +101 -3
- package/dist/application/task-room-service.js +286 -33
- package/dist/application/types.d.ts +2 -0
- package/dist/atomic-file.d.ts +34 -1
- package/dist/atomic-file.js +161 -18
- package/dist/briefing.d.ts +3 -1
- package/dist/briefing.js +120 -31
- package/dist/build-info.json +10 -5
- package/dist/capabilities.d.ts +9 -1
- package/dist/capabilities.js +13 -0
- package/dist/cli.js +391 -38
- package/dist/client-profile.d.ts +5 -17
- package/dist/client-profile.js +4 -127
- package/dist/config.d.ts +45 -0
- package/dist/config.js +57 -6
- package/dist/creation.d.ts +1 -1
- package/dist/creation.js +4 -4
- package/dist/daemon-recovery.d.ts +14 -9
- package/dist/daemon-recovery.js +124 -115
- package/dist/docs.d.ts +1 -1
- package/dist/docs.js +214 -20
- package/dist/doctor.d.ts +4 -1
- package/dist/doctor.js +75 -60
- package/dist/erased-resources.d.ts +9 -0
- package/dist/erased-resources.js +87 -0
- package/dist/file-delivery/reader.d.ts +15 -0
- package/dist/file-delivery/reader.js +63 -0
- package/dist/file-delivery/store.d.ts +15 -0
- package/dist/file-delivery/store.js +124 -0
- package/dist/file-delivery/types.d.ts +19 -0
- package/dist/file-delivery/types.js +1 -0
- package/dist/fleet-command-audit.d.ts +13 -0
- package/dist/fleet-command-audit.js +31 -9
- package/dist/harness/agent-session.d.ts +4 -0
- package/dist/harness/claude-code-session.js +1 -1
- package/dist/harness/claude-code.js +21 -11
- package/dist/harness/claude-runtime.d.ts +12 -0
- package/dist/harness/claude-runtime.js +43 -0
- package/dist/harness/codex-app-server-proxy.d.ts +4 -1
- package/dist/harness/codex-app-server-proxy.js +41 -5
- package/dist/harness/codex-reasoning.d.ts +5 -0
- package/dist/harness/codex-reasoning.js +12 -0
- package/dist/harness/codex-runtime.d.ts +11 -1
- package/dist/harness/codex-runtime.js +31 -4
- package/dist/harness/codex-session.js +2 -2
- package/dist/harness/codex.js +24 -12
- package/dist/init-wizard.d.ts +2 -0
- package/dist/init-wizard.js +38 -19
- package/dist/isolation/policy.js +6 -1
- package/dist/isolation/types.d.ts +5 -0
- package/dist/managed-cli.d.ts +253 -0
- package/dist/managed-cli.js +923 -0
- package/dist/managed-recovery.d.ts +13 -0
- package/dist/managed-recovery.js +31 -0
- package/dist/minimal-setup.d.ts +2 -0
- package/dist/minimal-setup.js +44 -0
- package/dist/monitor.d.ts +9 -34
- package/dist/monitor.js +90 -153
- package/dist/notifications/fleet-producer.d.ts +31 -0
- package/dist/notifications/fleet-producer.js +351 -0
- package/dist/notifications/outbox.d.ts +59 -0
- package/dist/notifications/outbox.js +199 -0
- package/dist/notifications/target-cleanup.d.ts +16 -0
- package/dist/notifications/target-cleanup.js +75 -0
- package/dist/notifications/task-producer.d.ts +20 -0
- package/dist/notifications/task-producer.js +100 -0
- package/dist/ops.d.ts +2 -0
- package/dist/ops.js +20 -3
- package/dist/owner-channel/channel.js +17 -6
- package/dist/owner-channel/commands.d.ts +4 -0
- package/dist/owner-channel/commands.js +8 -0
- package/dist/owner-channel/lifecycle-outbox.js +4 -3
- package/dist/owner-channel/ours-client.js +2 -17
- package/dist/paths.d.ts +5 -0
- package/dist/paths.js +5 -0
- package/dist/preset-migration.js +55 -0
- package/dist/process-incarnation.d.ts +23 -0
- package/dist/process-incarnation.js +42 -0
- package/dist/reminders/manager.d.ts +75 -0
- package/dist/reminders/manager.js +278 -0
- package/dist/reminders/runtime.d.ts +27 -0
- package/dist/reminders/runtime.js +32 -0
- package/dist/reminders/schedule.d.ts +37 -0
- package/dist/reminders/schedule.js +104 -0
- package/dist/reminders/store.d.ts +341 -0
- package/dist/reminders/store.js +762 -0
- package/dist/reminders/tools.d.ts +20 -0
- package/dist/reminders/tools.js +114 -0
- package/dist/resolved-plan.js +1 -0
- package/dist/rooms-tasks/add-member.d.ts +48 -0
- package/dist/rooms-tasks/add-member.js +84 -0
- package/dist/rooms-tasks/archived-absence.d.ts +4 -0
- package/dist/rooms-tasks/archived-absence.js +11 -0
- package/dist/rooms-tasks/cli.js +182 -5
- package/dist/rooms-tasks/close.d.ts +5 -2
- package/dist/rooms-tasks/close.js +224 -36
- package/dist/rooms-tasks/config.d.ts +1 -1
- package/dist/rooms-tasks/config.js +11 -4
- package/dist/rooms-tasks/cowork-adapter.d.ts +12 -5
- package/dist/rooms-tasks/cowork-adapter.js +15 -121
- package/dist/rooms-tasks/cowork-http.js +56 -6
- package/dist/rooms-tasks/deletion.d.ts +1 -1
- package/dist/rooms-tasks/deletion.js +66 -10
- package/dist/rooms-tasks/erasure.d.ts +6 -0
- package/dist/rooms-tasks/erasure.js +173 -0
- package/dist/rooms-tasks/external-worker.d.ts +1 -1
- package/dist/rooms-tasks/external-worker.js +3 -4
- package/dist/rooms-tasks/layout-binding-client.d.ts +11 -0
- package/dist/rooms-tasks/layout-binding-client.js +56 -0
- package/dist/rooms-tasks/layout-binding-grants.d.ts +21 -0
- package/dist/rooms-tasks/layout-binding-grants.js +128 -0
- package/dist/rooms-tasks/layout-cli.d.ts +4 -0
- package/dist/rooms-tasks/layout-cli.js +107 -0
- package/dist/rooms-tasks/layout-config.d.ts +11 -0
- package/dist/rooms-tasks/layout-config.js +89 -0
- package/dist/rooms-tasks/layout-control.d.ts +21 -0
- package/dist/rooms-tasks/layout-control.js +52 -0
- package/dist/rooms-tasks/layout-member-retirement.d.ts +10 -0
- package/dist/rooms-tasks/layout-member-retirement.js +90 -0
- package/dist/rooms-tasks/layout-service.d.ts +43 -0
- package/dist/rooms-tasks/layout-service.js +203 -0
- package/dist/rooms-tasks/layout.d.ts +121 -0
- package/dist/rooms-tasks/layout.js +286 -0
- package/dist/rooms-tasks/legacy-task-member.d.ts +19 -0
- package/dist/rooms-tasks/legacy-task-member.js +116 -0
- package/dist/rooms-tasks/live-readiness.d.ts +18 -0
- package/dist/rooms-tasks/live-readiness.js +85 -0
- package/dist/rooms-tasks/member-ownership.d.ts +5 -0
- package/dist/rooms-tasks/member-ownership.js +40 -0
- package/dist/rooms-tasks/member-startup.d.ts +1 -0
- package/dist/rooms-tasks/member-startup.js +2 -0
- package/dist/rooms-tasks/provision.d.ts +3 -0
- package/dist/rooms-tasks/provision.js +101 -49
- package/dist/rooms-tasks/recovery.d.ts +19 -0
- package/dist/rooms-tasks/recovery.js +110 -0
- package/dist/rooms-tasks/room-state.d.ts +4 -2
- package/dist/rooms-tasks/room-state.js +47 -8
- package/dist/rooms-tasks/task-review.d.ts +113 -0
- package/dist/rooms-tasks/task-review.js +561 -0
- package/dist/rooms-tasks/task-service-retirement.d.ts +11 -0
- package/dist/rooms-tasks/task-service-retirement.js +21 -0
- package/dist/rooms-tasks/task-state.d.ts +51 -5
- package/dist/rooms-tasks/task-state.js +358 -50
- package/dist/rooms-tasks/task-tools.d.ts +35 -0
- package/dist/rooms-tasks/task-tools.js +134 -0
- package/dist/rooms-tasks/types.d.ts +78 -0
- package/dist/rooms-tasks/workspace-artifacts.d.ts +6 -0
- package/dist/rooms-tasks/workspace-artifacts.js +93 -0
- package/dist/rooms-tasks/workspace.d.ts +35 -0
- package/dist/rooms-tasks/workspace.js +325 -0
- package/dist/runner.d.ts +23 -4
- package/dist/runner.js +409 -61
- package/dist/session/acp.d.ts +75 -2
- package/dist/session/acp.js +345 -82
- package/dist/session/arbiter.d.ts +98 -0
- package/dist/session/arbiter.js +272 -3
- package/dist/session/codex-app-server.d.ts +45 -1
- package/dist/session/codex-app-server.js +225 -32
- package/dist/session/control.d.ts +17 -3
- package/dist/session/control.js +219 -23
- package/dist/session/conversation-normalizer.d.ts +2 -1
- package/dist/session/conversation-normalizer.js +1 -1
- package/dist/session/conversation-observation.d.ts +25 -0
- package/dist/session/conversation-observation.js +100 -0
- package/dist/session/conversation-replay.d.ts +50 -0
- package/dist/session/conversation-replay.js +203 -0
- package/dist/session/conversation-store.d.ts +15 -1
- package/dist/session/conversation-store.js +213 -26
- package/dist/session/conversation-types.d.ts +57 -4
- package/dist/session/prompt-batch.d.ts +79 -0
- package/dist/session/prompt-batch.js +120 -0
- package/dist/session/task-notice.d.ts +14 -0
- package/dist/session/task-notice.js +13 -0
- package/dist/session/types.d.ts +42 -1
- package/dist/setup-tunnel-preflight.d.ts +34 -0
- package/dist/setup-tunnel-preflight.js +62 -0
- package/dist/socket-path.d.ts +2 -0
- package/dist/socket-path.js +22 -0
- package/dist/spawn.d.ts +5 -1
- package/dist/spawn.js +40 -4
- package/dist/startup-prompt.d.ts +15 -0
- package/dist/startup-prompt.js +71 -0
- package/dist/startup-readiness.d.ts +20 -1
- package/dist/startup-readiness.js +66 -3
- package/dist/startup-telemetry.d.ts +16 -0
- package/dist/startup-telemetry.js +46 -0
- package/dist/subscriptions/cli.d.ts +91 -0
- package/dist/subscriptions/cli.js +223 -0
- package/dist/subscriptions/launch.d.ts +37 -0
- package/dist/subscriptions/launch.js +93 -0
- package/dist/subscriptions/login.d.ts +77 -0
- package/dist/subscriptions/login.js +215 -0
- package/dist/subscriptions/observed.d.ts +7 -0
- package/dist/subscriptions/observed.js +44 -0
- package/dist/subscriptions/probe.d.ts +10 -0
- package/dist/subscriptions/probe.js +73 -0
- package/dist/subscriptions/service.d.ts +102 -0
- package/dist/subscriptions/service.js +364 -0
- package/dist/subscriptions/store.d.ts +71 -0
- package/dist/subscriptions/store.js +143 -0
- package/dist/supervisor/adoption.d.ts +8 -0
- package/dist/supervisor/adoption.js +78 -0
- package/dist/supervisor/catalog.d.ts +62 -0
- package/dist/supervisor/catalog.js +254 -0
- package/dist/supervisor/fleet.d.ts +8 -0
- package/dist/supervisor/fleet.js +89 -0
- package/dist/supervisor/index.js +3 -6
- package/dist/supervisor/launchd.d.ts +1 -1
- package/dist/supervisor/launchd.js +26 -13
- package/dist/supervisor/legacy.d.ts +35 -0
- package/dist/supervisor/legacy.js +524 -0
- package/dist/supervisor/manager.d.ts +15 -0
- package/dist/supervisor/manager.js +270 -0
- package/dist/supervisor/registration-refresh.d.ts +45 -0
- package/dist/supervisor/registration-refresh.js +266 -0
- package/dist/supervisor/scope.d.ts +4 -0
- package/dist/supervisor/scope.js +10 -0
- package/dist/supervisor/systemd.d.ts +2 -2
- package/dist/supervisor/systemd.js +18 -37
- package/dist/task-supervision.d.ts +9 -0
- package/dist/task-supervision.js +145 -0
- package/dist/temp-idle.d.ts +17 -0
- package/dist/temp-idle.js +42 -0
- package/dist/temp-lifecycle.d.ts +23 -8
- package/dist/temp-lifecycle.js +123 -66
- package/dist/temp-supervisor-recovery.d.ts +1 -1
- package/dist/temp-supervisor-recovery.js +2 -2
- package/dist/tunnel-setup-completion.d.ts +7 -0
- package/dist/tunnel-setup-completion.js +13 -0
- package/dist/tunnel-setup-input.d.ts +19 -0
- package/dist/tunnel-setup-input.js +130 -0
- package/dist/watchdog/query.d.ts +3 -1
- package/dist/watchdog/query.js +2 -1
- package/dist/web/agent-attachments.d.ts +29 -0
- package/dist/web/agent-attachments.js +162 -0
- package/dist/web/auth.d.ts +65 -2
- package/dist/web/auth.js +201 -11
- package/dist/web/available-port.d.ts +2 -0
- package/dist/web/available-port.js +23 -0
- package/dist/web/control.d.ts +3 -3
- package/dist/web/control.js +10 -8
- package/dist/web/conversation-stream-codec.d.ts +23 -0
- package/dist/web/conversation-stream-codec.js +110 -0
- package/dist/web/conversation-stream.d.ts +26 -0
- package/dist/web/conversation-stream.js +239 -0
- package/dist/web/events.d.ts +2 -0
- package/dist/web/events.js +9 -0
- package/dist/web/fleet-config-service.d.ts +7 -1
- package/dist/web/fleet-config-service.js +61 -16
- package/dist/web/prefix-gateway.d.ts +32 -0
- package/dist/web/prefix-gateway.js +248 -0
- package/dist/web/resource-views.d.ts +138 -0
- package/dist/web/resource-views.js +519 -0
- package/dist/web/runtime.d.ts +3 -1
- package/dist/web/runtime.js +132 -34
- package/dist/web/server.d.ts +19 -0
- package/dist/web/server.js +893 -71
- package/dist/web/service.d.ts +11 -2
- package/dist/web/service.js +30 -4
- package/dist/web/workspace-devices.d.ts +107 -0
- package/dist/web/workspace-devices.js +277 -0
- package/dist/web/workspace-tunnel.d.ts +1 -0
- package/dist/web/workspace-tunnel.js +34 -0
- package/dist/workspace-enrollment.d.ts +83 -0
- package/dist/workspace-enrollment.js +605 -0
- package/dist/workspace-replacement.d.ts +66 -0
- package/dist/workspace-replacement.js +238 -0
- package/docs/acp-files.md +146 -0
- package/docs/conversation-stream.md +216 -0
- package/docs/fleet-migration.md +54 -0
- package/docs/live-voice.md +13 -0
- package/docs/room-layouts.md +227 -0
- package/docs/self-reminders.md +374 -0
- package/docs/task-progress-review.md +143 -0
- package/docs/task-workspaces.md +225 -0
- package/docs/validation/managed-cli-permissions.md +421 -0
- package/examples/room-layouts/fresh.yaml +27 -0
- package/examples/room-layouts/scoped.yaml +21 -0
- package/examples/room-layouts/shared.yaml +19 -0
- package/package.json +25 -20
- package/presets/README.md +78 -0
- package/presets/brain-catalog.json +8 -2
- package/presets/fleet/agent_templates/Critic.yaml +1 -1
- package/presets/fleet/agent_templates/Developer.yaml +1 -1
- package/presets/fleet/agent_templates/Engineer.yaml +5 -0
- package/presets/fleet/agent_templates/LocalCoordinator.yaml +6 -3
- package/presets/fleet/agents/FleetCoordinator.yaml +1 -1
- package/presets/fleet/brains/claude-fable-5-1-high.yaml +5 -0
- package/presets/fleet/brains/claude-fable-5-1-low.yaml +5 -0
- package/presets/fleet/brains/claude-fable-5-1-max.yaml +5 -0
- package/presets/fleet/brains/claude-fable-5-1-medium.yaml +5 -0
- package/presets/fleet/brains/claude-fable-5-1-xhigh.yaml +5 -0
- package/presets/fleet/brains/claude-fable-5-high.yaml +1 -1
- package/presets/fleet/brains/claude-fable-5-low.yaml +1 -1
- package/presets/fleet/brains/claude-fable-5-max.yaml +1 -1
- package/presets/fleet/brains/claude-fable-5-medium.yaml +1 -1
- package/presets/fleet/brains/claude-fable-5-xhigh.yaml +1 -1
- package/presets/fleet/brains/claude-haiku-4-5-20251001-default.yaml +4 -0
- package/presets/fleet/brains/claude-opus-5-5-high.yaml +5 -0
- package/presets/fleet/brains/claude-opus-5-5-low.yaml +5 -0
- package/presets/fleet/brains/claude-opus-5-5-max.yaml +5 -0
- package/presets/fleet/brains/claude-opus-5-5-medium.yaml +5 -0
- package/presets/fleet/brains/claude-opus-5-5-xhigh.yaml +5 -0
- package/presets/fleet/brains/claude-opus-5-high.yaml +1 -1
- package/presets/fleet/brains/claude-opus-5-low.yaml +1 -1
- package/presets/fleet/brains/claude-opus-5-max.yaml +1 -1
- package/presets/fleet/brains/claude-opus-5-medium.yaml +1 -1
- package/presets/fleet/brains/claude-opus-5-xhigh.yaml +1 -1
- package/presets/fleet/brains/claude-sonnet-5-high.yaml +1 -1
- package/presets/fleet/brains/claude-sonnet-5-low.yaml +1 -1
- package/presets/fleet/brains/claude-sonnet-5-max.yaml +1 -1
- package/presets/fleet/brains/claude-sonnet-5-medium.yaml +1 -1
- package/presets/fleet/brains/claude-sonnet-5-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-3-codex-spark-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-3-codex-spark-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-3-codex-spark-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-3-codex-spark-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-mini-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-mini-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-mini-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-mini-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-5-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-5-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-5-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-5-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-luna-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-luna-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-luna-max.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-luna-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-luna-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-sol-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-sol-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-sol-max.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-sol-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-sol-ultra.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-sol-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-terra-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-terra-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-terra-max.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-terra-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-terra-ultra.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-terra-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-6-1-sol-high.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-1-sol-low.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-1-sol-max.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-1-sol-medium.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-1-sol-ultra.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-1-sol-xhigh.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-astra-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-6-astra-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-6-astra-max.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-6-astra-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-6-astra-ultra.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-6-astra-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-6-luna-high.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-luna-low.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-luna-max.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-luna-medium.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-luna-xhigh.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-sol-high.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-sol-low.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-sol-max.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-sol-medium.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-sol-ultra.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-sol-xhigh.yaml +5 -0
- package/presets/fleet/roles/Coordinator.yaml +21 -13
- package/presets/fleet/roles/Critic.yaml +24 -0
- package/presets/fleet/roles/Developer.yaml +32 -0
- package/presets/fleet/roles/Engineer.yaml +72 -0
- package/presets/fleet/roles/LocalCoordinator.yaml +17 -0
- package/presets/fleet/room_layouts/engineering.yaml +25 -0
- package/presets/fleet/room_layouts/pair.yaml +24 -0
- package/presets/fleet/room_layouts/single.yaml +17 -0
- package/presets/fleet/room_layouts/team.yaml +30 -0
- package/presets/fleet/room_templates/engineering.yaml +19 -0
- package/presets/manifest.json +2 -2
- package/dist/web-app/assets/index-59GF-gsZ.css +0 -1
- package/dist/web-app/assets/index-C0h3ALvs.js +0 -10
- package/dist/web-app/icons/ours-fleet-maskable.svg +0 -4
- package/dist/web-app/icons/ours-fleet.svg +0 -4
- package/dist/web-app/index.html +0 -17
- package/dist/web-app/manifest.webmanifest +0 -15
- package/dist/web-app/offline.html +0 -18
- package/dist/web-app/sw.js +0 -51
package/dist/web/control.js
CHANGED
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { socketPath as privateSocketPath } from '../socket-path.js';
|
|
1
2
|
import { chmodSync, mkdirSync, rmSync } from 'node:fs';
|
|
2
3
|
import { createConnection, createServer } from 'node:net';
|
|
3
4
|
import { join } from 'node:path';
|
|
4
5
|
import { FleetError, safeLine } from '../application/errors.js';
|
|
5
6
|
import { stateRoot } from '../paths.js';
|
|
6
|
-
export const webControlPath = (dir = join(stateRoot(), 'web')) => join(dir, 'control.sock');
|
|
7
|
+
export const webControlPath = (dir = join(stateRoot(), 'web')) => privateSocketPath(join(dir, 'control.sock'));
|
|
7
8
|
export async function startWebControlServer(options) {
|
|
8
9
|
const dir = options.dir ?? join(stateRoot(), 'web');
|
|
9
10
|
const path = webControlPath(dir);
|
|
@@ -34,13 +35,14 @@ export async function startWebControlServer(options) {
|
|
|
34
35
|
if (used > rateLimit)
|
|
35
36
|
throw new FleetError('rate_limited', 'local web control rate limit exceeded');
|
|
36
37
|
const parsed = JSON.parse(body.slice(0, body.indexOf('\n')));
|
|
37
|
-
|
|
38
|
-
|
|
38
|
+
let result;
|
|
39
|
+
if (parsed.command === 'link-device' && options.onLinkDevice)
|
|
40
|
+
result = await options.onLinkDevice();
|
|
39
41
|
else if (parsed.command === 'revoke-all')
|
|
40
42
|
await options.onRevokeAll();
|
|
41
43
|
else
|
|
42
44
|
throw new FleetError('invalid_request', 'unknown local web control command');
|
|
43
|
-
socket.end(JSON.stringify({ ok: true }) + '\n');
|
|
45
|
+
socket.end(JSON.stringify({ ok: true, result }) + '\n');
|
|
44
46
|
}
|
|
45
47
|
catch (error) {
|
|
46
48
|
const code = error instanceof FleetError ? error.code : 'internal';
|
|
@@ -65,7 +67,7 @@ export async function startWebControlServer(options) {
|
|
|
65
67
|
};
|
|
66
68
|
}
|
|
67
69
|
export async function requestWebControl(command, path = webControlPath(), timeoutMs = 5_000) {
|
|
68
|
-
await new Promise((resolve, reject) => {
|
|
70
|
+
return await new Promise((resolve, reject) => {
|
|
69
71
|
const socket = createConnection(path);
|
|
70
72
|
let response = '';
|
|
71
73
|
let settled = false;
|
|
@@ -73,7 +75,7 @@ export async function requestWebControl(command, path = webControlPath(), timeou
|
|
|
73
75
|
socket.destroy();
|
|
74
76
|
reject(new FleetError('timeout', 'local web control request timed out'));
|
|
75
77
|
}, timeoutMs);
|
|
76
|
-
const finish = (error) => {
|
|
78
|
+
const finish = (error, result) => {
|
|
77
79
|
if (settled)
|
|
78
80
|
return;
|
|
79
81
|
settled = true;
|
|
@@ -81,7 +83,7 @@ export async function requestWebControl(command, path = webControlPath(), timeou
|
|
|
81
83
|
if (error)
|
|
82
84
|
reject(error);
|
|
83
85
|
else
|
|
84
|
-
resolve();
|
|
86
|
+
resolve(result);
|
|
85
87
|
};
|
|
86
88
|
socket.once('connect', () => socket.write(JSON.stringify({ command }) + '\n'));
|
|
87
89
|
socket.on('data', chunk => { response += chunk.toString('utf8'); });
|
|
@@ -91,7 +93,7 @@ export async function requestWebControl(command, path = webControlPath(), timeou
|
|
|
91
93
|
const parsed = JSON.parse(response);
|
|
92
94
|
if (!parsed.ok)
|
|
93
95
|
throw new FleetError(parsed.error?.code === 'rate_limited' ? 'rate_limited' : 'rejected', parsed.error?.message ?? 'local web control request was rejected');
|
|
94
|
-
finish();
|
|
96
|
+
finish(undefined, parsed.result);
|
|
95
97
|
}
|
|
96
98
|
catch (error) {
|
|
97
99
|
finish(error);
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/** Public v1 wire contract. Kept free of Node APIs so browser consumers can share it. */
|
|
2
|
+
export declare const MAX_FRAME_BYTES: number;
|
|
3
|
+
export declare const MAX_ASSEMBLY_BYTES: number;
|
|
4
|
+
export declare const MAX_PENDING_BYTES: number;
|
|
5
|
+
export type ConversationRecordType = 'ready' | 'ledger' | 'state' | 'role' | 'resync' | 'keepalive';
|
|
6
|
+
export interface ConversationStreamRecord {
|
|
7
|
+
version: 1;
|
|
8
|
+
type: ConversationRecordType;
|
|
9
|
+
payload: unknown;
|
|
10
|
+
}
|
|
11
|
+
/** A payload is the logical record; SSE/JSON envelopes do not consume its assembly budget. */
|
|
12
|
+
export declare function encodeConversationRecord(type: ConversationRecordType, payload: unknown, id: string): Generator<string>;
|
|
13
|
+
/** Strict incremental decoder: one assembly, no partial publication, UTF-8 split safe. */
|
|
14
|
+
export declare class ConversationStreamDecoder {
|
|
15
|
+
private readonly publish;
|
|
16
|
+
private decoder;
|
|
17
|
+
private pending;
|
|
18
|
+
private assembly?;
|
|
19
|
+
constructor(publish: (record: ConversationStreamRecord) => void);
|
|
20
|
+
push(bytes: Uint8Array): void;
|
|
21
|
+
finish(): void;
|
|
22
|
+
private accept;
|
|
23
|
+
}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
/** Public v1 wire contract. Kept free of Node APIs so browser consumers can share it. */
|
|
2
|
+
export const MAX_FRAME_BYTES = 512 * 1024;
|
|
3
|
+
export const MAX_ASSEMBLY_BYTES = 16 * 1024 * 1024;
|
|
4
|
+
export const MAX_PENDING_BYTES = 8 * 1024 * 1024;
|
|
5
|
+
const utf8 = new TextEncoder();
|
|
6
|
+
const types = new Set(['ready', 'ledger', 'state', 'role', 'resync', 'keepalive']);
|
|
7
|
+
const frame = (value) => `event: conversation\ndata: ${JSON.stringify(value)}\n\n`;
|
|
8
|
+
const identity = (payload) => ({ generation: payload?.sessionGeneration ?? payload?.snapshot?.sessionGeneration,
|
|
9
|
+
revision: payload?.stateRevision ?? payload?.snapshot?.stateRevision ?? payload?.roleRevision });
|
|
10
|
+
/** A payload is the logical record; SSE/JSON envelopes do not consume its assembly budget. */
|
|
11
|
+
export function* encodeConversationRecord(type, payload, id) {
|
|
12
|
+
const json = JSON.stringify(payload), totalBytes = utf8.encode(json).byteLength;
|
|
13
|
+
if (totalBytes > MAX_ASSEMBLY_BYTES)
|
|
14
|
+
throw new Error('conversation record exceeds assembly limit');
|
|
15
|
+
const direct = frame({ version: 1, type, payload });
|
|
16
|
+
if (utf8.encode(direct).byteLength <= MAX_FRAME_BYTES) {
|
|
17
|
+
yield direct;
|
|
18
|
+
return;
|
|
19
|
+
}
|
|
20
|
+
const position = identity(payload);
|
|
21
|
+
yield frame({ version: 1, type: 'start', id, recordType: type, totalBytes, ...position });
|
|
22
|
+
// At most 48K UTF-16 units: even worst-case JSON escapes fit a 512 KiB frame.
|
|
23
|
+
let index = 0;
|
|
24
|
+
for (let offset = 0; offset < json.length;) {
|
|
25
|
+
let end = Math.min(json.length, offset + 48 * 1024);
|
|
26
|
+
if (end < json.length && /[\uD800-\uDBFF]/.test(json[end - 1]))
|
|
27
|
+
end--;
|
|
28
|
+
yield frame({ version: 1, type: 'chunk', id, index: index++, data: json.slice(offset, end) });
|
|
29
|
+
offset = end;
|
|
30
|
+
}
|
|
31
|
+
yield frame({ version: 1, type: 'commit', id, totalBytes, ...position });
|
|
32
|
+
}
|
|
33
|
+
/** Strict incremental decoder: one assembly, no partial publication, UTF-8 split safe. */
|
|
34
|
+
export class ConversationStreamDecoder {
|
|
35
|
+
publish;
|
|
36
|
+
decoder = new TextDecoder('utf-8', { fatal: true });
|
|
37
|
+
pending = '';
|
|
38
|
+
assembly;
|
|
39
|
+
constructor(publish) {
|
|
40
|
+
this.publish = publish;
|
|
41
|
+
}
|
|
42
|
+
push(bytes) {
|
|
43
|
+
try {
|
|
44
|
+
this.pending += this.decoder.decode(bytes, { stream: true });
|
|
45
|
+
for (;;) {
|
|
46
|
+
const end = this.pending.indexOf('\n\n');
|
|
47
|
+
if (end < 0)
|
|
48
|
+
break;
|
|
49
|
+
const text = this.pending.slice(0, end + 2);
|
|
50
|
+
this.pending = this.pending.slice(end + 2);
|
|
51
|
+
if (utf8.encode(text).byteLength > MAX_FRAME_BYTES)
|
|
52
|
+
throw new Error('frame limit exceeded');
|
|
53
|
+
if (!text.startsWith('event: conversation\ndata: '))
|
|
54
|
+
throw new Error('invalid conversation frame');
|
|
55
|
+
this.accept(JSON.parse(text.slice(26, -2)));
|
|
56
|
+
}
|
|
57
|
+
if (utf8.encode(this.pending).byteLength > MAX_FRAME_BYTES)
|
|
58
|
+
throw new Error('frame limit exceeded');
|
|
59
|
+
}
|
|
60
|
+
catch (error) {
|
|
61
|
+
this.pending = '';
|
|
62
|
+
this.assembly = undefined;
|
|
63
|
+
throw error;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
finish() {
|
|
67
|
+
const rest = this.decoder.decode();
|
|
68
|
+
if (rest || this.pending || this.assembly)
|
|
69
|
+
throw new Error('incomplete conversation stream');
|
|
70
|
+
}
|
|
71
|
+
accept(value) {
|
|
72
|
+
if (value?.version !== 1)
|
|
73
|
+
throw new Error('unsupported conversation version');
|
|
74
|
+
if (types.has(value.type)) {
|
|
75
|
+
if (this.assembly)
|
|
76
|
+
throw new Error('overlapping assembly');
|
|
77
|
+
this.publish(value);
|
|
78
|
+
return;
|
|
79
|
+
}
|
|
80
|
+
if (value.type === 'start') {
|
|
81
|
+
if (this.assembly)
|
|
82
|
+
throw new Error('overlapping assembly');
|
|
83
|
+
if (typeof value.id !== 'string' || !types.has(value.recordType) || !Number.isSafeInteger(value.totalBytes) || value.totalBytes < 1 || value.totalBytes > MAX_ASSEMBLY_BYTES)
|
|
84
|
+
throw new Error('invalid assembly length/type');
|
|
85
|
+
this.assembly = { id: value.id, type: value.recordType, total: value.totalBytes, bytes: 0, index: 0, parts: [], generation: value.generation, revision: value.revision };
|
|
86
|
+
return;
|
|
87
|
+
}
|
|
88
|
+
const assembly = this.assembly;
|
|
89
|
+
if (!assembly || value.id !== assembly.id)
|
|
90
|
+
throw new Error('assembly identity mismatch');
|
|
91
|
+
if (value.type === 'chunk') {
|
|
92
|
+
if (value.index !== assembly.index++ || typeof value.data !== 'string')
|
|
93
|
+
throw new Error('assembly chunk order');
|
|
94
|
+
if (!value.data.length)
|
|
95
|
+
throw new Error('assembly empty chunk');
|
|
96
|
+
assembly.bytes += utf8.encode(value.data).byteLength;
|
|
97
|
+
if (assembly.bytes > assembly.total)
|
|
98
|
+
throw new Error('assembly length exceeded');
|
|
99
|
+
assembly.parts.push(value.data);
|
|
100
|
+
return;
|
|
101
|
+
}
|
|
102
|
+
if (value.type !== 'commit' || value.totalBytes !== assembly.total || assembly.bytes !== assembly.total)
|
|
103
|
+
throw new Error('assembly length mismatch');
|
|
104
|
+
const payload = JSON.parse(assembly.parts.join('')), position = identity(payload);
|
|
105
|
+
if (value.generation !== assembly.generation || value.revision !== assembly.revision || position.generation !== assembly.generation || position.revision !== assembly.revision)
|
|
106
|
+
throw new Error('assembly revision mismatch');
|
|
107
|
+
this.assembly = undefined;
|
|
108
|
+
this.publish({ version: 1, type: assembly.type, payload });
|
|
109
|
+
}
|
|
110
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import type { Writable } from 'node:stream';
|
|
2
|
+
import { type ConversationRecordType } from './conversation-stream-codec.js';
|
|
3
|
+
/** One active logical record; only waiting encoded output consumes the pending budget. */
|
|
4
|
+
export declare class ConversationStreamWriter {
|
|
5
|
+
private readonly output;
|
|
6
|
+
private readonly failed;
|
|
7
|
+
private queue;
|
|
8
|
+
private active?;
|
|
9
|
+
private waiting;
|
|
10
|
+
private ended;
|
|
11
|
+
private sequence;
|
|
12
|
+
private lastQualifyingOutputAt;
|
|
13
|
+
pendingBytes: number;
|
|
14
|
+
get busy(): boolean;
|
|
15
|
+
constructor(output: Writable, failed: (reason: string) => void);
|
|
16
|
+
private error;
|
|
17
|
+
private drain;
|
|
18
|
+
send(type: ConversationRecordType, payload: unknown): boolean;
|
|
19
|
+
private flush;
|
|
20
|
+
private fail;
|
|
21
|
+
close(): void;
|
|
22
|
+
}
|
|
23
|
+
import type { FastifyInstance } from 'fastify';
|
|
24
|
+
import type { WebServices } from './server.js';
|
|
25
|
+
import type { WebAuth } from './auth.js';
|
|
26
|
+
export declare function registerConversationStream(app: FastifyInstance, services: WebServices, auth: WebAuth): void;
|
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { encodeConversationRecord, MAX_PENDING_BYTES } from './conversation-stream-codec.js';
|
|
2
|
+
/** One active logical record; only waiting encoded output consumes the pending budget. */
|
|
3
|
+
export class ConversationStreamWriter {
|
|
4
|
+
output;
|
|
5
|
+
failed;
|
|
6
|
+
queue = [];
|
|
7
|
+
active;
|
|
8
|
+
waiting = false;
|
|
9
|
+
ended = false;
|
|
10
|
+
sequence = 0;
|
|
11
|
+
lastQualifyingOutputAt = Date.now();
|
|
12
|
+
pendingBytes = 0;
|
|
13
|
+
get busy() { return Boolean(this.active); }
|
|
14
|
+
constructor(output, failed) {
|
|
15
|
+
this.output = output;
|
|
16
|
+
this.failed = failed;
|
|
17
|
+
output.on('drain', this.drain);
|
|
18
|
+
output.once('error', this.error);
|
|
19
|
+
}
|
|
20
|
+
error = () => this.fail('transport-error');
|
|
21
|
+
drain = () => { this.waiting = false; this.flush(); };
|
|
22
|
+
send(type, payload) {
|
|
23
|
+
if (this.ended)
|
|
24
|
+
return false;
|
|
25
|
+
if (type === 'keepalive' && Date.now() - this.lastQualifyingOutputAt < 15_000)
|
|
26
|
+
return true;
|
|
27
|
+
const id = String(++this.sequence);
|
|
28
|
+
try {
|
|
29
|
+
if (!this.active) {
|
|
30
|
+
this.active = encodeConversationRecord(type, payload, id);
|
|
31
|
+
this.flush();
|
|
32
|
+
}
|
|
33
|
+
else {
|
|
34
|
+
let bytes = 0;
|
|
35
|
+
for (const frame of encodeConversationRecord(type, payload, id))
|
|
36
|
+
bytes += Buffer.byteLength(frame);
|
|
37
|
+
if (this.pendingBytes + bytes > MAX_PENDING_BYTES) {
|
|
38
|
+
this.fail('output-overflow');
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
41
|
+
this.queue.push({ type, payload, id, bytes });
|
|
42
|
+
this.pendingBytes += bytes;
|
|
43
|
+
}
|
|
44
|
+
if (!this.ended)
|
|
45
|
+
this.lastQualifyingOutputAt = Date.now();
|
|
46
|
+
return !this.ended;
|
|
47
|
+
}
|
|
48
|
+
catch {
|
|
49
|
+
this.fail('record-too-large');
|
|
50
|
+
return false;
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
flush() {
|
|
54
|
+
if (this.ended || this.waiting)
|
|
55
|
+
return;
|
|
56
|
+
try {
|
|
57
|
+
while (this.active) {
|
|
58
|
+
const next = this.active.next();
|
|
59
|
+
if (!next.done) {
|
|
60
|
+
if (!this.output.write(next.value)) {
|
|
61
|
+
this.waiting = true;
|
|
62
|
+
return;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
else {
|
|
66
|
+
const queued = this.queue.shift();
|
|
67
|
+
this.active = queued ? encodeConversationRecord(queued.type, queued.payload, queued.id) : undefined;
|
|
68
|
+
if (queued)
|
|
69
|
+
this.pendingBytes -= queued.bytes;
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
this.fail('record-too-large');
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
fail(reason) {
|
|
78
|
+
if (this.ended)
|
|
79
|
+
return;
|
|
80
|
+
// A resync is only useful at a frame boundary with writable capacity.
|
|
81
|
+
if (!this.waiting && !this.output.destroyed) {
|
|
82
|
+
const frame = encodeConversationRecord('resync', { reason, capability: 'degraded', retry: reason !== 'record-too-large' }, 'terminal').next().value;
|
|
83
|
+
if (frame)
|
|
84
|
+
this.output.write(frame);
|
|
85
|
+
}
|
|
86
|
+
const blocked = this.waiting;
|
|
87
|
+
this.close();
|
|
88
|
+
this.failed(reason);
|
|
89
|
+
if (blocked)
|
|
90
|
+
this.output.destroy();
|
|
91
|
+
}
|
|
92
|
+
close() {
|
|
93
|
+
if (this.ended)
|
|
94
|
+
return;
|
|
95
|
+
this.ended = true;
|
|
96
|
+
this.active = undefined;
|
|
97
|
+
this.queue = [];
|
|
98
|
+
this.pendingBytes = 0;
|
|
99
|
+
this.output.off('drain', this.drain);
|
|
100
|
+
this.output.off('error', this.error);
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
import { FleetError } from '../application/errors.js';
|
|
104
|
+
import { presentAttachmentEvent } from './agent-attachments.js';
|
|
105
|
+
export function registerConversationStream(app, services, auth) {
|
|
106
|
+
const connections = new Set();
|
|
107
|
+
app.addHook('preClose', async () => { for (const close of connections)
|
|
108
|
+
close(); });
|
|
109
|
+
app.get('/api/v1/roles/:id/conversation/events', async (request, reply) => {
|
|
110
|
+
const browser = auth.authenticate(request);
|
|
111
|
+
const generation = request.query.expectedGeneration, rawAfter = request.query.after ?? '0', after = Number(rawAfter);
|
|
112
|
+
if (typeof generation !== 'string' || !generation.trim() || generation.length > 512 || !/^\d+$/.test(rawAfter) || !Number.isSafeInteger(after))
|
|
113
|
+
throw new FleetError('invalid_request', 'expectedGeneration and a nonnegative integer after cursor are required');
|
|
114
|
+
if (!services.observeRole)
|
|
115
|
+
throw new FleetError('capability_unavailable', 'Complete conversation observation is unavailable');
|
|
116
|
+
let ended = false, owner, follow;
|
|
117
|
+
let binding, timer;
|
|
118
|
+
let epoch = 0, revision = -1, lifetime, available = false;
|
|
119
|
+
let abort = new AbortController();
|
|
120
|
+
const close = () => {
|
|
121
|
+
if (ended)
|
|
122
|
+
return;
|
|
123
|
+
ended = true;
|
|
124
|
+
epoch++;
|
|
125
|
+
abort.abort();
|
|
126
|
+
if (timer)
|
|
127
|
+
clearInterval(timer);
|
|
128
|
+
binding?.();
|
|
129
|
+
writer.close();
|
|
130
|
+
follow?.close();
|
|
131
|
+
follow = undefined;
|
|
132
|
+
owner?.dispose();
|
|
133
|
+
connections.delete(close);
|
|
134
|
+
reply.raw.end();
|
|
135
|
+
};
|
|
136
|
+
const writer = new ConversationStreamWriter(reply.raw, close);
|
|
137
|
+
const resync = (reason, retry = true) => writer.send('resync', { reason, retry, capability: 'degraded' });
|
|
138
|
+
const valid = () => { if (auth.sessionValid(browser))
|
|
139
|
+
return true; writer.send('resync', { reason: 'auth-expired', retry: browser.rotating ? 'authenticate' : 'reauthenticate', capability: 'auth-expired' }); close(); return false; };
|
|
140
|
+
const stopFollow = () => { epoch++; abort.abort(); abort = new AbortController(); follow?.close(); follow = undefined; };
|
|
141
|
+
const open = async () => {
|
|
142
|
+
const current = ++epoch, signal = abort.signal;
|
|
143
|
+
const active = () => !ended && current === epoch && !signal.aborted;
|
|
144
|
+
try {
|
|
145
|
+
const control = await services.session(request.params.id);
|
|
146
|
+
if (!active() || !valid())
|
|
147
|
+
return;
|
|
148
|
+
const descriptor = await control.describe(owner);
|
|
149
|
+
if (!active() || !valid())
|
|
150
|
+
return;
|
|
151
|
+
if (!control.followConversation || !descriptor.features.includes('conversation_live_complete')) {
|
|
152
|
+
resync('capability-unavailable', false);
|
|
153
|
+
return;
|
|
154
|
+
}
|
|
155
|
+
const stateDir = owner?.snapshot().stateDir;
|
|
156
|
+
const present = (event) => presentAttachmentEvent(stateDir, request.params.id, event);
|
|
157
|
+
const handle = await control.followConversation({ controller: false, expectedGeneration: generation, after, signal, roleObservation: owner,
|
|
158
|
+
onReplay: page => { if (active() && valid()) {
|
|
159
|
+
for (const event of page.events)
|
|
160
|
+
writer.send('ledger', present(event));
|
|
161
|
+
writer.send('ledger', { coveredThrough: page.coveredThrough });
|
|
162
|
+
} },
|
|
163
|
+
onReady: ready => {
|
|
164
|
+
if (active() && valid()) {
|
|
165
|
+
writer.send('state', ready.snapshot);
|
|
166
|
+
writer.send('ready', { sessionGeneration: ready.snapshot.sessionGeneration, stateRevision: ready.snapshot.stateRevision,
|
|
167
|
+
ledgerBarrier: ready.snapshot.ledgerBarrier, coveredThrough: ready.coveredThrough, capability: 'complete', authorizedUntil: browser.absoluteExpiresAt });
|
|
168
|
+
}
|
|
169
|
+
},
|
|
170
|
+
onEvent: event => { if (active() && valid()) {
|
|
171
|
+
writer.send('ledger', present(event));
|
|
172
|
+
writer.send('ledger', { coveredThrough: event.seq });
|
|
173
|
+
} },
|
|
174
|
+
onState: state => { if (active() && valid())
|
|
175
|
+
writer.send('state', state); },
|
|
176
|
+
onClose: reason => { if (active()) {
|
|
177
|
+
epoch++;
|
|
178
|
+
abort.abort();
|
|
179
|
+
follow = undefined;
|
|
180
|
+
resync(reason ?? 'control-closed', !reason?.includes('assembly limit'));
|
|
181
|
+
} },
|
|
182
|
+
});
|
|
183
|
+
if (!active())
|
|
184
|
+
handle.close();
|
|
185
|
+
else
|
|
186
|
+
follow = handle;
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
if (active())
|
|
190
|
+
resync('observation-unavailable');
|
|
191
|
+
}
|
|
192
|
+
};
|
|
193
|
+
const changed = (state) => {
|
|
194
|
+
if (ended || state.roleRevision <= revision)
|
|
195
|
+
return;
|
|
196
|
+
revision = state.roleRevision;
|
|
197
|
+
const { stateDir: _private, ...publicState } = state;
|
|
198
|
+
writer.send('role', publicState);
|
|
199
|
+
const replaced = lifetime !== undefined && lifetime !== state.roleLifetime;
|
|
200
|
+
const nextAvailable = state.availability === 'available' && state.health === 'healthy';
|
|
201
|
+
if (replaced || !nextAvailable)
|
|
202
|
+
stopFollow();
|
|
203
|
+
if (state.health !== 'healthy')
|
|
204
|
+
resync(state.failure ?? 'role-observation-unavailable', state.health !== 'unsupported');
|
|
205
|
+
else if (replaced)
|
|
206
|
+
resync('role-replaced');
|
|
207
|
+
const shouldOpen = nextAvailable && (!available || replaced);
|
|
208
|
+
lifetime = state.roleLifetime;
|
|
209
|
+
available = nextAvailable;
|
|
210
|
+
if (shouldOpen)
|
|
211
|
+
void open();
|
|
212
|
+
};
|
|
213
|
+
reply.hijack();
|
|
214
|
+
for (const [name, value] of Object.entries(reply.getHeaders()))
|
|
215
|
+
if (value !== undefined)
|
|
216
|
+
reply.raw.setHeader(name, Array.isArray(value) ? value : String(value));
|
|
217
|
+
reply.raw.writeHead(200, { 'Content-Type': 'text/event-stream; charset=utf-8', 'Cache-Control': 'no-store', 'X-Accel-Buffering': 'no' });
|
|
218
|
+
reply.raw.flushHeaders();
|
|
219
|
+
reply.raw.once('close', close);
|
|
220
|
+
connections.add(close);
|
|
221
|
+
binding = auth.bindTransport(browser, () => { writer.send('resync', { reason: 'auth-expired', capability: 'auth-expired', retry: browser.rotating ? 'authenticate' : 'reauthenticate' }); close(); });
|
|
222
|
+
try {
|
|
223
|
+
// Factory callbacks can be synchronous; defer handling until the owner can be borrowed.
|
|
224
|
+
owner = services.observeRole(request.params.id, state => queueMicrotask(() => changed(state)));
|
|
225
|
+
if (ended) {
|
|
226
|
+
owner.dispose();
|
|
227
|
+
return;
|
|
228
|
+
}
|
|
229
|
+
changed(owner.snapshot());
|
|
230
|
+
timer = setInterval(() => { if (valid())
|
|
231
|
+
writer.send('keepalive', { at: Date.now() }); }, 1_000);
|
|
232
|
+
timer.unref();
|
|
233
|
+
}
|
|
234
|
+
catch {
|
|
235
|
+
resync('role-observation-unavailable');
|
|
236
|
+
close();
|
|
237
|
+
}
|
|
238
|
+
});
|
|
239
|
+
}
|
package/dist/web/events.d.ts
CHANGED
|
@@ -8,6 +8,8 @@ export interface FleetEvent {
|
|
|
8
8
|
}
|
|
9
9
|
export declare class FleetEventBus {
|
|
10
10
|
private readonly events;
|
|
11
|
+
private readonly resourceListeners;
|
|
12
|
+
subscribeResources(listener: () => void): () => void;
|
|
11
13
|
private readonly clients;
|
|
12
14
|
publish(kind: string, payload: unknown, roleId?: string): FleetEvent;
|
|
13
15
|
attach(socket: WebSocket, lastEventId?: string): () => void;
|
package/dist/web/events.js
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
2
|
export class FleetEventBus {
|
|
3
3
|
events = [];
|
|
4
|
+
resourceListeners = new Set();
|
|
5
|
+
subscribeResources(listener) { this.resourceListeners.add(listener); return () => { this.resourceListeners.delete(listener); }; }
|
|
4
6
|
clients = new Set();
|
|
5
7
|
publish(kind, payload, roleId) {
|
|
6
8
|
const event = { eventId: randomUUID(), at: new Date().toISOString(), roleId, kind, payload };
|
|
@@ -12,6 +14,12 @@ export class FleetEventBus {
|
|
|
12
14
|
if (socket.readyState === socket.OPEN && socket.bufferedAmount < 1024 * 1024)
|
|
13
15
|
socket.send(frame);
|
|
14
16
|
}
|
|
17
|
+
for (const listener of this.resourceListeners) {
|
|
18
|
+
try {
|
|
19
|
+
listener();
|
|
20
|
+
}
|
|
21
|
+
catch { }
|
|
22
|
+
}
|
|
15
23
|
return event;
|
|
16
24
|
}
|
|
17
25
|
attach(socket, lastEventId) {
|
|
@@ -30,5 +38,6 @@ export class FleetEventBus {
|
|
|
30
38
|
for (const socket of this.clients)
|
|
31
39
|
socket.close(1001, 'server shutdown');
|
|
32
40
|
this.clients.clear();
|
|
41
|
+
this.resourceListeners.clear();
|
|
33
42
|
}
|
|
34
43
|
}
|
|
@@ -4,6 +4,9 @@ export interface EditableFleetModel {
|
|
|
4
4
|
manifest: Record<string, unknown>;
|
|
5
5
|
agents: Record<string, Record<string, unknown>>;
|
|
6
6
|
agent_templates: Record<string, Record<string, unknown>>;
|
|
7
|
+
roles?: Record<string, Record<string, unknown>>;
|
|
8
|
+
brains?: Record<string, Record<string, unknown>>;
|
|
9
|
+
room_templates?: Record<string, Record<string, unknown>>;
|
|
7
10
|
}
|
|
8
11
|
export interface ConfigReadResult {
|
|
9
12
|
path: string;
|
|
@@ -44,8 +47,11 @@ export declare class FleetConfigService {
|
|
|
44
47
|
readonly path: string;
|
|
45
48
|
private readonly preflight;
|
|
46
49
|
private readonly beforeMutation?;
|
|
50
|
+
private validationInputs;
|
|
51
|
+
/** Cached during canonical reads; observer filtering must never parse YAML. */
|
|
52
|
+
resourceValidationInputs(): readonly string[];
|
|
47
53
|
constructor(options?: FleetConfigServiceOptions);
|
|
48
|
-
read(): ConfigReadResult;
|
|
54
|
+
read(includeDefinitions?: boolean): ConfigReadResult;
|
|
49
55
|
preview(baseRevision: string, input: unknown): Promise<ConfigPreviewResult>;
|
|
50
56
|
write(baseRevision: string, input: unknown): Promise<ConfigWriteResult>;
|
|
51
57
|
}
|