@ours.network/fleet 1.2.0-nightly.8 → 1.2.0-nightly.81
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 +34 -0
- package/dist/application/conversation-history.js +294 -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 +1 -1
- package/dist/application/role-repository.js +30 -7
- package/dist/application/room-layout-definitions.d.ts +45 -0
- package/dist/application/room-layout-definitions.js +130 -0
- package/dist/application/session-control.d.ts +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 +9 -4
- package/dist/capabilities.d.ts +9 -1
- package/dist/capabilities.js +13 -0
- package/dist/cli.js +402 -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 +227 -19
- package/dist/doctor.d.ts +4 -1
- package/dist/doctor.js +75 -60
- package/dist/erased-resources.d.ts +9 -0
- package/dist/erased-resources.js +87 -0
- package/dist/file-delivery/reader.d.ts +15 -0
- package/dist/file-delivery/reader.js +63 -0
- package/dist/file-delivery/store.d.ts +15 -0
- package/dist/file-delivery/store.js +124 -0
- package/dist/file-delivery/types.d.ts +19 -0
- package/dist/file-delivery/types.js +1 -0
- package/dist/fleet-command-audit.d.ts +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 +33 -4
- package/dist/runner.js +410 -62
- package/dist/session/acp.d.ts +75 -2
- package/dist/session/acp.js +345 -82
- package/dist/session/arbiter.d.ts +98 -0
- package/dist/session/arbiter.js +272 -3
- package/dist/session/codex-app-server.d.ts +45 -1
- package/dist/session/codex-app-server.js +225 -32
- package/dist/session/control.d.ts +17 -3
- package/dist/session/control.js +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 +32 -0
- package/dist/startup-readiness.js +101 -0
- package/dist/startup-telemetry.d.ts +16 -0
- package/dist/startup-telemetry.js +46 -0
- package/dist/subscriptions/cli.d.ts +91 -0
- package/dist/subscriptions/cli.js +223 -0
- package/dist/subscriptions/launch.d.ts +37 -0
- package/dist/subscriptions/launch.js +93 -0
- package/dist/subscriptions/login.d.ts +77 -0
- package/dist/subscriptions/login.js +215 -0
- package/dist/subscriptions/observed.d.ts +7 -0
- package/dist/subscriptions/observed.js +44 -0
- package/dist/subscriptions/probe.d.ts +10 -0
- package/dist/subscriptions/probe.js +73 -0
- package/dist/subscriptions/service.d.ts +102 -0
- package/dist/subscriptions/service.js +364 -0
- package/dist/subscriptions/store.d.ts +71 -0
- package/dist/subscriptions/store.js +143 -0
- package/dist/supervisor/adoption.d.ts +8 -0
- package/dist/supervisor/adoption.js +78 -0
- package/dist/supervisor/catalog.d.ts +62 -0
- package/dist/supervisor/catalog.js +254 -0
- package/dist/supervisor/fleet.d.ts +8 -0
- package/dist/supervisor/fleet.js +89 -0
- package/dist/supervisor/index.js +3 -6
- package/dist/supervisor/launchd.d.ts +1 -1
- package/dist/supervisor/launchd.js +26 -13
- package/dist/supervisor/legacy.d.ts +35 -0
- package/dist/supervisor/legacy.js +524 -0
- package/dist/supervisor/manager.d.ts +15 -0
- package/dist/supervisor/manager.js +270 -0
- package/dist/supervisor/registration-refresh.d.ts +45 -0
- package/dist/supervisor/registration-refresh.js +266 -0
- package/dist/supervisor/scope.d.ts +4 -0
- package/dist/supervisor/scope.js +10 -0
- package/dist/supervisor/systemd.d.ts +2 -2
- package/dist/supervisor/systemd.js +19 -33
- package/dist/task-supervision.d.ts +9 -0
- package/dist/task-supervision.js +145 -0
- package/dist/temp-idle.d.ts +17 -0
- package/dist/temp-idle.js +42 -0
- package/dist/temp-lifecycle.d.ts +23 -8
- package/dist/temp-lifecycle.js +123 -66
- package/dist/temp-supervisor-recovery.d.ts +1 -1
- package/dist/temp-supervisor-recovery.js +2 -2
- package/dist/tunnel-setup-completion.d.ts +7 -0
- package/dist/tunnel-setup-completion.js +13 -0
- package/dist/tunnel-setup-input.d.ts +19 -0
- package/dist/tunnel-setup-input.js +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 +135 -0
- package/dist/web/resource-views.js +494 -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
|
@@ -1,14 +1,23 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { detachDeletedRoom } from './task-state.js';
|
|
2
|
+
import { binderKey } from '../agent-ours/state.js';
|
|
3
|
+
import { assertMemberNotPermanent } from './member-ownership.js';
|
|
4
|
+
import { retireTaskMemberService } from './task-service-retirement.js';
|
|
5
|
+
import { eraseMemberArtifacts } from './erasure.js';
|
|
6
|
+
import { deleteWorkspace, assertWorkspaceDeletable } from './workspace.js';
|
|
7
|
+
import { collectWorkspaceArchives } from './workspace-artifacts.js';
|
|
8
|
+
import { existsSync, lstatSync, readFileSync } from 'node:fs';
|
|
2
9
|
import { join } from 'node:path';
|
|
3
10
|
import { randomUUID } from 'node:crypto';
|
|
11
|
+
import { parse } from 'yaml';
|
|
12
|
+
import { privateRuntimeRoot, releaseManagedAgent } from '../agent-ours/service.js';
|
|
4
13
|
import { attachOursClient } from '@ours.network/sdk/client';
|
|
5
14
|
import { withFileLock } from '../atomic-file.js';
|
|
6
15
|
import { agentDir, stateRoot } from '../paths.js';
|
|
7
16
|
import { readClientProfile } from '../client-profile.js';
|
|
8
17
|
import { readTempSupervisor, secureStoppedTempArchive, stopTempSupervisor, tempSupervisorLiveness, tempArchiveForLaunch, tempArchiveForCreationAction, } from '../temp-lifecycle.js';
|
|
9
18
|
import { CoworkProtocolError } from './cowork-adapter.js';
|
|
10
|
-
import { advanceMemberRetirement, advanceRoomClose, beginRoomClose, closeRoom, deleteRoomRecord, getRoomRecord, listRoomRecords, setRoomCloseError, } from './room-state.js';
|
|
11
|
-
const CLOSE_LOCK_STALE_MS = 5 * 60_000;
|
|
19
|
+
import { advanceMemberRetirement, advanceRoomClose, beginRoomClose, closeRoom, deleteRoomRecord, getRoomRecord, listRoomRecords, setRoomCloseError, recordRetirementMemberCids, } from './room-state.js';
|
|
20
|
+
export const CLOSE_LOCK_STALE_MS = 5 * 60_000;
|
|
12
21
|
const STOP_POLLS = 50;
|
|
13
22
|
const STOP_POLL_MS = 100;
|
|
14
23
|
export function roomCloseLockPath(roomId) {
|
|
@@ -20,6 +29,20 @@ function errorText(error) {
|
|
|
20
29
|
function exactMemberIdentity(seat) {
|
|
21
30
|
const dir = agentDir(seat.role_name, true);
|
|
22
31
|
const identityPath = join(dir, '.identity');
|
|
32
|
+
if (existsSync(dir) && !existsSync(identityPath) && seat.launch?.action_id && seat.launch.launch_id) {
|
|
33
|
+
const supervisor = readTempSupervisor(dir);
|
|
34
|
+
let creation;
|
|
35
|
+
try {
|
|
36
|
+
creation = JSON.parse(readFileSync(join(dir, 'creation.json'), 'utf8'));
|
|
37
|
+
}
|
|
38
|
+
catch (error) {
|
|
39
|
+
if (error.code !== 'ENOENT')
|
|
40
|
+
throw error;
|
|
41
|
+
}
|
|
42
|
+
if (supervisor?.role === seat.role_name && supervisor.launchId === seat.launch.launch_id
|
|
43
|
+
&& creation?.role === seat.role_name && creation.creationActionId === seat.launch.action_id)
|
|
44
|
+
return;
|
|
45
|
+
}
|
|
23
46
|
if (!existsSync(dir) || !existsSync(identityPath)) {
|
|
24
47
|
throw new Error(`room member '${seat.role_name}' has no live Fleet temp-state identity proof; refusing retirement`);
|
|
25
48
|
}
|
|
@@ -29,9 +52,11 @@ function exactMemberIdentity(seat) {
|
|
|
29
52
|
}
|
|
30
53
|
}
|
|
31
54
|
export async function inspectMember(seat) {
|
|
55
|
+
assertMemberNotPermanent(seat.role_name);
|
|
32
56
|
exactMemberIdentity(seat);
|
|
33
57
|
const supervisor = readTempSupervisor(agentDir(seat.role_name, true));
|
|
34
|
-
if (!supervisor || supervisor.role !== seat.role_name
|
|
58
|
+
if (!supervisor || supervisor.role !== seat.role_name
|
|
59
|
+
|| (seat.launch?.launch_id && supervisor.launchId !== seat.launch.launch_id)) {
|
|
35
60
|
throw new Error(`room member '${seat.role_name}' has no exact Fleet supervisor ownership proof`);
|
|
36
61
|
}
|
|
37
62
|
return { launchId: supervisor.launchId };
|
|
@@ -57,15 +82,11 @@ export async function waitForLivenessAbsent(role, launchId, lifecycleDeps = {})
|
|
|
57
82
|
async function withIdentityClient(work) {
|
|
58
83
|
const profile = readClientProfile(process.env);
|
|
59
84
|
const leaseToken = `ours-fleet-room-close-${process.pid}-${randomUUID()}`;
|
|
60
|
-
const client = await attachOursClient(
|
|
85
|
+
const client = await attachOursClient({
|
|
61
86
|
endpoint: profile.endpoint,
|
|
62
87
|
expectedInstanceId: profile.expectedInstanceId,
|
|
63
88
|
credentialPath: profile.credentialPath,
|
|
64
89
|
sessionMode: 'external', env: {}, leaseToken,
|
|
65
|
-
} : {
|
|
66
|
-
env: process.env,
|
|
67
|
-
leaseToken,
|
|
68
|
-
clientPid: process.pid,
|
|
69
90
|
});
|
|
70
91
|
try {
|
|
71
92
|
return await work(client);
|
|
@@ -89,18 +110,69 @@ export async function identityCidPresent(cid) {
|
|
|
89
110
|
return rows.some(row => 'cid' in row && row.cid.toLowerCase() === cid.toLowerCase());
|
|
90
111
|
});
|
|
91
112
|
}
|
|
113
|
+
/** A managed temporary identity belongs to its durable external lease even
|
|
114
|
+
* after worker exit. Retire that exact logical owner before generic removal. */
|
|
115
|
+
async function releaseRetainedMemberRuntime(seat) {
|
|
116
|
+
const live = agentDir(seat.role_name, true);
|
|
117
|
+
const source = existsSync(live) ? live
|
|
118
|
+
: (seat.launch?.launch_id ? tempArchiveForLaunch(seat.role_name, seat.launch.launch_id) : undefined);
|
|
119
|
+
if (!source) {
|
|
120
|
+
if (seat.launch?.task_supervised)
|
|
121
|
+
throw Error('MEMBER_RUNTIME_RETIREMENT_SOURCE_MISSING');
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
const path = join(source, 'role.yaml');
|
|
125
|
+
if (!existsSync(path))
|
|
126
|
+
return; // pre-managed compatibility evidence has no runtime
|
|
127
|
+
const stat = lstatSync(path);
|
|
128
|
+
if (!stat.isFile() || stat.isSymbolicLink() || stat.size > 256 * 1024)
|
|
129
|
+
throw Error('MEMBER_RUNTIME_UNSAFE_PROOF');
|
|
130
|
+
const role = parse(readFileSync(path, 'utf8'));
|
|
131
|
+
const profile = readClientProfile({ ...process.env, ...role.env });
|
|
132
|
+
const runtime = join(privateRuntimeRoot(), binderKey(profile.expectedInstanceId, seat.role_name));
|
|
133
|
+
const metadata = readTempSupervisor(source);
|
|
134
|
+
if (!existsSync(join(runtime, 'instance.json'))) {
|
|
135
|
+
if (metadata?.kind === 'fleet-managed')
|
|
136
|
+
throw Error('MEMBER_RUNTIME_RETIREMENT_MISSING');
|
|
137
|
+
return;
|
|
138
|
+
}
|
|
139
|
+
const creationPath = join(source, 'creation.json'), creationStat = lstatSync(creationPath);
|
|
140
|
+
if (!creationStat.isFile() || creationStat.isSymbolicLink() || creationStat.size > 256 * 1024)
|
|
141
|
+
throw Error('MEMBER_RUNTIME_UNSAFE_PROOF');
|
|
142
|
+
const creation = JSON.parse(readFileSync(creationPath, 'utf8'));
|
|
143
|
+
if (role.name !== seat.role_name || role.identity !== seat.role_name || !seat.identity_cid
|
|
144
|
+
|| !seat.launch?.action_id || !seat.launch.launch_id || creation.role !== seat.role_name
|
|
145
|
+
|| creation.creationActionId !== seat.launch.action_id || metadata?.role !== seat.role_name
|
|
146
|
+
|| metadata.launchId !== seat.launch.launch_id
|
|
147
|
+
|| (metadata.taskOwner && metadata.taskOwner.creationActionId !== seat.launch.action_id))
|
|
148
|
+
throw Error('MEMBER_RUNTIME_RETIREMENT_MISMATCH');
|
|
149
|
+
await releaseManagedAgent(role, { cid: seat.identity_cid, action: seat.launch.action_id });
|
|
150
|
+
}
|
|
92
151
|
export async function removeExactMemberIdentity(seat) {
|
|
152
|
+
assertMemberNotPermanent(seat.role_name);
|
|
93
153
|
await withIdentityClient(async (client) => {
|
|
94
|
-
const
|
|
95
|
-
|
|
154
|
+
const rows = await client.listIdentities();
|
|
155
|
+
const before = listedIdentity(rows, seat.role_name);
|
|
156
|
+
if (!before) {
|
|
157
|
+
if (rows.some(row => row.name === seat.role_name || (seat.identity_cid && 'cid' in row
|
|
158
|
+
&& row.cid.toLowerCase() === seat.identity_cid.toLowerCase())))
|
|
159
|
+
throw new Error(`room member '${seat.role_name}' identity absence is not proven`);
|
|
160
|
+
// A terminal release can remove the SDK identity before its response is
|
|
161
|
+
// recorded. Finish the exact retained launch's release journal on retry.
|
|
162
|
+
if (existsSync(agentDir(seat.role_name, true)) || (seat.launch?.launch_id
|
|
163
|
+
&& tempArchiveForLaunch(seat.role_name, seat.launch.launch_id)))
|
|
164
|
+
await releaseRetainedMemberRuntime(seat);
|
|
96
165
|
return;
|
|
166
|
+
}
|
|
97
167
|
if (!seat.identity_cid) {
|
|
98
|
-
throw new Error(`room member '${seat.role_name}' exists without a recorded authenticated CID; refusing removal`);
|
|
168
|
+
throw new Error(`room member '${seat.role_name}' exists without a recorded authenticated CID; identity absence is not proven; refusing removal`);
|
|
99
169
|
}
|
|
100
170
|
if (before.cid?.toLowerCase() !== seat.identity_cid.toLowerCase()) {
|
|
101
|
-
throw new Error(`room member '${seat.role_name}' identity CID mismatch: recorded ${seat.identity_cid}, found ${before.cid ?? 'none'}`);
|
|
171
|
+
throw new Error(`room member '${seat.role_name}' identity absence is not proven: CID mismatch: recorded ${seat.identity_cid}, found ${before.cid ?? 'none'}`);
|
|
102
172
|
}
|
|
173
|
+
await releaseRetainedMemberRuntime(seat);
|
|
103
174
|
try {
|
|
175
|
+
assertMemberNotPermanent(seat.role_name);
|
|
104
176
|
await client.removeIdentity({ name: seat.role_name });
|
|
105
177
|
}
|
|
106
178
|
catch (error) {
|
|
@@ -113,8 +185,9 @@ export async function removeExactMemberIdentity(seat) {
|
|
|
113
185
|
if (after)
|
|
114
186
|
throw error;
|
|
115
187
|
}
|
|
116
|
-
const after =
|
|
117
|
-
if (after
|
|
188
|
+
const after = await client.listIdentities();
|
|
189
|
+
if (after.some(row => row.name === seat.role_name || (seat.identity_cid && 'cid' in row
|
|
190
|
+
&& row.cid.toLowerCase() === seat.identity_cid.toLowerCase()))) {
|
|
118
191
|
throw new Error(`room member '${seat.role_name}' identity still exists after remove_identity`);
|
|
119
192
|
}
|
|
120
193
|
});
|
|
@@ -128,14 +201,24 @@ export async function assertMemberIdentityAbsent(seat) {
|
|
|
128
201
|
}
|
|
129
202
|
});
|
|
130
203
|
}
|
|
204
|
+
async function retireMemberRegistration(room, current, deps) {
|
|
205
|
+
if (room.task_id)
|
|
206
|
+
await (deps.retireTaskService ?? retireTaskMemberService)(current.role_name, {
|
|
207
|
+
taskId: room.task_id, creationActionId: current.launch?.action_id,
|
|
208
|
+
taskSupervised: current.launch?.task_supervised,
|
|
209
|
+
...(current.launch?.launch_id ? { launchId: current.launch.launch_id } : {}),
|
|
210
|
+
});
|
|
211
|
+
}
|
|
131
212
|
async function retireMember(roomId, seat, deps) {
|
|
213
|
+
assertMemberNotPermanent(seat.role_name);
|
|
132
214
|
let room = getRoomRecord(roomId);
|
|
133
215
|
let current = room.member_seats.find(candidate => candidate.role_name === seat.role_name);
|
|
216
|
+
await retireMemberRegistration(room, current, deps);
|
|
134
217
|
let retirement = current.retirement;
|
|
135
218
|
if (retirement?.phase === 'identity_absent') {
|
|
136
|
-
if (
|
|
219
|
+
if (!deps.inspectMember) {
|
|
137
220
|
if (existsSync(agentDir(current.role_name, true)))
|
|
138
|
-
throw new Error(
|
|
221
|
+
throw new Error('Retired member has replacement launch live state');
|
|
139
222
|
await assertMemberIdentityAbsent(current);
|
|
140
223
|
}
|
|
141
224
|
return;
|
|
@@ -144,6 +227,8 @@ async function retireMember(roomId, seat, deps) {
|
|
|
144
227
|
if (!existsSync(agentDir(current.role_name, true)) && current.launch?.launch_id && current.launch.action_id) {
|
|
145
228
|
const archived = tempArchiveForLaunch(current.role_name, current.launch.launch_id);
|
|
146
229
|
const created = tempArchiveForCreationAction(current.role_name, current.launch.action_id);
|
|
230
|
+
if (archived && (!created || created.path !== archived || created.launchId !== current.launch.launch_id))
|
|
231
|
+
throw new Error('Archived member creation ownership mismatch');
|
|
147
232
|
if (archived && created?.path === archived && created.launchId === current.launch.launch_id) {
|
|
148
233
|
if (readFileSync(join(archived, '.identity'), 'utf8').trim() !== current.role_name)
|
|
149
234
|
throw new Error(`room member '${current.role_name}' archive identity mismatch`);
|
|
@@ -151,6 +236,7 @@ async function retireMember(roomId, seat, deps) {
|
|
|
151
236
|
throw new Error(`room member '${current.role_name}' archived supervisor is not proven stopped`);
|
|
152
237
|
// A terminated launch can archive itself before room retirement begins.
|
|
153
238
|
// Accept its exact durable provenance only when no identity needs removal.
|
|
239
|
+
await (deps.removeIdentity ?? removeExactMemberIdentity)(current);
|
|
154
240
|
await assertMemberIdentityAbsent(current);
|
|
155
241
|
const latest = getRoomRecord(roomId)?.member_seats.find(seat => seat.role_name === current.role_name);
|
|
156
242
|
if (existsSync(agentDir(current.role_name, true))
|
|
@@ -162,14 +248,6 @@ async function retireMember(roomId, seat, deps) {
|
|
|
162
248
|
return;
|
|
163
249
|
}
|
|
164
250
|
}
|
|
165
|
-
if (current.launch?.state === 'failed' && !existsSync(agentDir(current.role_name, true))) {
|
|
166
|
-
// A failure before applyRole (for example invite-secret validation) has
|
|
167
|
-
// no supervisor to stop or archive. Settle only proven absence; this
|
|
168
|
-
// path never removes an identity based on missing local evidence.
|
|
169
|
-
await assertMemberIdentityAbsent(current);
|
|
170
|
-
advanceMemberRetirement(roomId, current.role_name, 'identity_absent', 'failed-launch-absent');
|
|
171
|
-
return;
|
|
172
|
-
}
|
|
173
251
|
if (current.launch?.state === 'pending' && current.launch.attempt === 0) {
|
|
174
252
|
if (existsSync(agentDir(current.role_name, true))) {
|
|
175
253
|
throw new Error(`never-launched room member '${current.role_name}' unexpectedly has Fleet temp state`);
|
|
@@ -178,12 +256,31 @@ async function retireMember(roomId, seat, deps) {
|
|
|
178
256
|
advanceMemberRetirement(roomId, current.role_name, 'identity_absent', 'never-launched');
|
|
179
257
|
return;
|
|
180
258
|
}
|
|
259
|
+
if (!deps.inspectMember && !existsSync(agentDir(current.role_name, true))) {
|
|
260
|
+
await (deps.removeIdentity ?? removeExactMemberIdentity)(current);
|
|
261
|
+
if (existsSync(agentDir(current.role_name, true)))
|
|
262
|
+
throw new Error(`room member '${current.role_name}' acquired replacement state during retirement`);
|
|
263
|
+
advanceMemberRetirement(roomId, current.role_name, 'identity_absent', 'absent-verified');
|
|
264
|
+
return;
|
|
265
|
+
}
|
|
181
266
|
const ownership = await (deps.inspectMember ?? inspectMember)(current);
|
|
182
267
|
room = advanceMemberRetirement(roomId, current.role_name, 'stop_requested', ownership.launchId);
|
|
183
268
|
current = room.member_seats.find(candidate => candidate.role_name === seat.role_name);
|
|
184
269
|
retirement = current.retirement;
|
|
185
270
|
}
|
|
271
|
+
if (['stop_requested', 'liveness_absent', 'archive_secured'].includes(retirement.phase) && !deps.requestStop
|
|
272
|
+
&& !existsSync(agentDir(current.role_name, true))) {
|
|
273
|
+
const archive = tempArchiveForLaunch(current.role_name, retirement.launch_id);
|
|
274
|
+
if (archive && await tempSupervisorLiveness(archive) !== 'stopped')
|
|
275
|
+
throw new Error('Archived supervisor is not proven stopped');
|
|
276
|
+
await (deps.removeIdentity ?? removeExactMemberIdentity)(current);
|
|
277
|
+
await assertMemberIdentityAbsent(current);
|
|
278
|
+
advanceMemberRetirement(roomId, current.role_name, 'identity_absent', retirement.launch_id, archive, !archive);
|
|
279
|
+
return;
|
|
280
|
+
}
|
|
186
281
|
if (retirement.phase === 'stop_requested') {
|
|
282
|
+
if (!deps.inspectMember && (await inspectMember(current)).launchId !== retirement.launch_id)
|
|
283
|
+
throw new Error('Member launch changed before stop');
|
|
187
284
|
await (deps.requestStop ?? (async (role) => { await stopTempSupervisor(role); }))(current.role_name);
|
|
188
285
|
await (deps.waitForLivenessAbsent ?? waitForLivenessAbsent)(current.role_name, retirement.launch_id);
|
|
189
286
|
room = advanceMemberRetirement(roomId, current.role_name, 'liveness_absent', retirement.launch_id);
|
|
@@ -213,17 +310,35 @@ export async function closeManagedRoom(input) {
|
|
|
213
310
|
const lock = deps.lock ?? withFileLock;
|
|
214
311
|
return lock(roomCloseLockPath(input.roomId), async () => {
|
|
215
312
|
let room = beginRoomClose(input.roomId);
|
|
216
|
-
if (room.state === 'closed')
|
|
313
|
+
if (room.state === 'closed') {
|
|
314
|
+
for (const seat of room.member_seats) {
|
|
315
|
+
assertMemberNotPermanent(seat.role_name);
|
|
316
|
+
await retireMemberRegistration(room, seat, deps);
|
|
317
|
+
}
|
|
318
|
+
if (!deps.inspectMember)
|
|
319
|
+
for (const seat of room.member_seats) {
|
|
320
|
+
if (existsSync(agentDir(seat.role_name, true)))
|
|
321
|
+
throw new Error('Closed room has replacement live state');
|
|
322
|
+
await assertMemberIdentityAbsent(seat);
|
|
323
|
+
}
|
|
217
324
|
return room;
|
|
325
|
+
}
|
|
218
326
|
try {
|
|
219
327
|
if (room.close?.phase === 'retire_members') {
|
|
328
|
+
room = await recoverMemberCids(room, input.cowork);
|
|
220
329
|
for (const seat of room.member_seats) {
|
|
221
330
|
await retireMember(input.roomId, seat, deps);
|
|
222
331
|
}
|
|
223
332
|
room = advanceRoomClose(input.roomId, 'close_cowork');
|
|
224
333
|
}
|
|
225
334
|
if (room.close?.phase === 'close_cowork') {
|
|
226
|
-
|
|
335
|
+
try {
|
|
336
|
+
await input.cowork.closeRoom(input.roomId);
|
|
337
|
+
}
|
|
338
|
+
catch (error) {
|
|
339
|
+
if (!(error instanceof CoworkProtocolError && error.code === 'not_found'))
|
|
340
|
+
throw error;
|
|
341
|
+
}
|
|
227
342
|
}
|
|
228
343
|
return closeRoom(input.roomId);
|
|
229
344
|
}
|
|
@@ -237,6 +352,9 @@ export async function closeManagedRoom(input) {
|
|
|
237
352
|
export async function deleteLegacyClosedRooms(input) {
|
|
238
353
|
const deleted = [];
|
|
239
354
|
for (const room of listRoomRecords({ state: 'closed' })) {
|
|
355
|
+
// This compatibility sweep is retirement, never explicit workspace deletion.
|
|
356
|
+
if (room.workspace)
|
|
357
|
+
continue;
|
|
240
358
|
try {
|
|
241
359
|
await input.cowork.deleteRoom(room.room_id);
|
|
242
360
|
}
|
|
@@ -253,14 +371,84 @@ export async function deleteLegacyClosedRooms(input) {
|
|
|
253
371
|
}
|
|
254
372
|
/** Retire live resources through the existing cursor, then delete retained state. */
|
|
255
373
|
export async function deleteManagedRoom(input) {
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
374
|
+
if (getRoomRecord(input.roomId))
|
|
375
|
+
await closeManagedRoom(input);
|
|
376
|
+
return withFileLock(roomCloseLockPath(input.roomId), async () => {
|
|
377
|
+
try {
|
|
378
|
+
try {
|
|
379
|
+
await input.cowork.deleteRoom(input.roomId);
|
|
380
|
+
}
|
|
381
|
+
catch (error) {
|
|
382
|
+
// An earlier explicit delete may have removed the remote room before
|
|
383
|
+
// local filesystem cleanup failed. Never restore it or repeat creation.
|
|
384
|
+
if (!(error instanceof CoworkProtocolError && error.code === 'not_found'))
|
|
385
|
+
throw error;
|
|
386
|
+
}
|
|
387
|
+
const retained = getRoomRecord(input.roomId);
|
|
388
|
+
if (retained?.workspace && !retained.task_id) {
|
|
389
|
+
assertWorkspaceDeletable(retained.workspace, 'room', input.roomId);
|
|
390
|
+
collectWorkspaceArchives(retained.workspace);
|
|
391
|
+
deleteWorkspace(retained.workspace, 'room', input.roomId);
|
|
392
|
+
}
|
|
393
|
+
if (retained)
|
|
394
|
+
await eraseMemberArtifacts('room', input.roomId, retained.member_seats, [input.roomId]);
|
|
395
|
+
if (retained?.task_id)
|
|
396
|
+
detachDeletedRoom(retained.task_id, input.roomId);
|
|
397
|
+
deleteRoomRecord(input.roomId);
|
|
398
|
+
return { room_id: input.roomId, deleted: true };
|
|
399
|
+
}
|
|
400
|
+
catch (error) {
|
|
401
|
+
if (getRoomRecord(input.roomId))
|
|
402
|
+
setRoomCloseError(input.roomId, errorText(error), `Fix the cleanup error and explicitly retry room delete ${input.roomId} ${input.roomId}.`);
|
|
403
|
+
throw error;
|
|
404
|
+
}
|
|
405
|
+
}, {}, CLOSE_LOCK_STALE_MS);
|
|
406
|
+
}
|
|
407
|
+
/** Recover only an authenticated seat tied to this exact room and issued invite.
|
|
408
|
+
* Display names and the current daemon name inventory are never binding proof.
|
|
409
|
+
*/
|
|
410
|
+
async function recoverMemberCids(room, cowork) {
|
|
411
|
+
if (!room.member_seats.some(seat => !seat.identity_cid))
|
|
412
|
+
return room;
|
|
413
|
+
const profile = room.member_seats.some(seat => seat.launch?.action_id) ? readClientProfile(process.env) : undefined;
|
|
414
|
+
let remote;
|
|
415
|
+
if (cowork.getRoom && room.room_identity_cid) {
|
|
416
|
+
try {
|
|
417
|
+
remote = await cowork.getRoom(room.room_id);
|
|
418
|
+
}
|
|
419
|
+
catch (error) {
|
|
420
|
+
if (!(error instanceof CoworkProtocolError && error.code === 'not_found'))
|
|
421
|
+
throw error;
|
|
422
|
+
}
|
|
423
|
+
if (remote && (remote.room_id !== room.room_id || remote.identity_cid.toLowerCase() !== room.room_identity_cid.toLowerCase()))
|
|
424
|
+
throw new Error('Room identity mismatch during deletion recovery');
|
|
263
425
|
}
|
|
264
|
-
|
|
265
|
-
|
|
426
|
+
const seats = room.member_seats.map(seat => {
|
|
427
|
+
let runtimeCid;
|
|
428
|
+
if (profile && seat.launch?.action_id) {
|
|
429
|
+
const dir = join(stateRoot(), 'private-ours', binderKey(profile.expectedInstanceId, seat.role_name));
|
|
430
|
+
try {
|
|
431
|
+
const state = JSON.parse(readFileSync(join(dir, 'state.json'), 'utf8'));
|
|
432
|
+
const instance = JSON.parse(readFileSync(join(dir, 'instance.json'), 'utf8'));
|
|
433
|
+
if (state.name === seat.role_name && state.daemon === profile.expectedInstanceId
|
|
434
|
+
&& state.lifetime === 'temporary' && state.action === seat.launch.action_id
|
|
435
|
+
&& instance.role === seat.role_name && instance.temporary === true && instance.instance === state.instance)
|
|
436
|
+
runtimeCid = state.cid;
|
|
437
|
+
}
|
|
438
|
+
catch (error) {
|
|
439
|
+
if (error.code !== 'ENOENT')
|
|
440
|
+
throw error;
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
const matches = seat.invite_id ? remote?.seats.filter(value => value.invite_id === seat.invite_id && value.role === seat.cowork_role) ?? [] : [];
|
|
444
|
+
if (matches.length > 1)
|
|
445
|
+
throw new Error('Ambiguous authenticated member identity during deletion recovery');
|
|
446
|
+
const candidates = [seat.identity_cid, runtimeCid, matches[0]?.identity_cid].filter((v) => !!v);
|
|
447
|
+
if (candidates.some(cid => !/^[a-f0-9]{64}$/i.test(cid)))
|
|
448
|
+
throw new Error('Invalid authenticated member CID');
|
|
449
|
+
if (new Set(candidates.map(cid => cid.toLowerCase())).size > 1)
|
|
450
|
+
throw new Error('Contradictory authenticated member CID evidence');
|
|
451
|
+
return candidates.length ? { ...seat, identity_cid: candidates[0] } : seat;
|
|
452
|
+
});
|
|
453
|
+
return recordRetirementMemberCids(room.room_id, seats);
|
|
266
454
|
}
|
|
@@ -4,7 +4,7 @@ export declare class RoomsTasksConfigError extends Error {
|
|
|
4
4
|
constructor(path: string, message: string);
|
|
5
5
|
}
|
|
6
6
|
export declare function fingerprint(invite: string): string;
|
|
7
|
-
export declare function validateRoomsConfig(raw: unknown, vars: Record<string, string>, path: string): RoomsConfig & {
|
|
7
|
+
export declare function validateRoomsConfig(raw: unknown, vars: Record<string, string>, path: string, deferredInviteFile?: string): RoomsConfig & {
|
|
8
8
|
_invite?: {
|
|
9
9
|
value: string;
|
|
10
10
|
fingerprint: string;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { existsSync, readFileSync } from 'node:fs';
|
|
2
|
+
import { isAbsolute } from 'node:path';
|
|
2
3
|
import { createHash } from 'node:crypto';
|
|
3
4
|
import { ROOMS_KEYS as RK, ROOMS_OWNER_KEYS as ROK, ROOMS_COWORK_KEYS as RCK, ROOMS_DEFAULTS_KEYS as RDK, TASKS_KEYS as TK, TEMPLATE_KEYS as TPK, TEMPLATE_MEMBER_KEYS as TMK, TEMPLATE_ROOM_KEYS as TRK, } from './types.js';
|
|
4
5
|
const isPlainObject = (v) => v !== null && typeof v === 'object' && !Array.isArray(v);
|
|
@@ -18,7 +19,7 @@ function rejectUnknown(obj, allowed, path, section) {
|
|
|
18
19
|
export function fingerprint(invite) {
|
|
19
20
|
return createHash('sha256').update(invite.trim()).digest('hex');
|
|
20
21
|
}
|
|
21
|
-
function resolveInvite(owner, vars, path) {
|
|
22
|
+
function resolveInvite(owner, vars, path, deferredInviteFile) {
|
|
22
23
|
const inline = owner.public_invite;
|
|
23
24
|
const file = owner.public_invite_file;
|
|
24
25
|
if (inline && file)
|
|
@@ -28,6 +29,10 @@ function resolveInvite(owner, vars, path) {
|
|
|
28
29
|
let raw;
|
|
29
30
|
if (file) {
|
|
30
31
|
const resolved = file.replace(/\$\{(\w+)\}/g, (m, k) => (k in vars ? String(vars[k]) : m));
|
|
32
|
+
// Tunnel setup writes this one file itself after enrollment; until then it may be absent.
|
|
33
|
+
// Only the literal absolute path counts: a relative spelling that happens to reach it does not.
|
|
34
|
+
if (!existsSync(resolved) && deferredInviteFile !== undefined && isAbsolute(deferredInviteFile) && resolved === deferredInviteFile)
|
|
35
|
+
return undefined;
|
|
31
36
|
if (!existsSync(resolved))
|
|
32
37
|
throw new RoomsTasksConfigError(path, `rooms.owner.public_invite_file: not found: ${resolved}`);
|
|
33
38
|
raw = readFileSync(resolved, 'utf8').trim();
|
|
@@ -39,7 +44,7 @@ function resolveInvite(owner, vars, path) {
|
|
|
39
44
|
throw new RoomsTasksConfigError(path, 'rooms.owner: invite is empty');
|
|
40
45
|
return { value: raw, fingerprint: fingerprint(raw) };
|
|
41
46
|
}
|
|
42
|
-
export function validateRoomsConfig(raw, vars, path) {
|
|
47
|
+
export function validateRoomsConfig(raw, vars, path, deferredInviteFile) {
|
|
43
48
|
if (!isPlainObject(raw))
|
|
44
49
|
throw new RoomsTasksConfigError(path, 'rooms: must be a mapping');
|
|
45
50
|
// Migration-only input: provider selection was exposed before rooms shipped,
|
|
@@ -54,7 +59,9 @@ export function validateRoomsConfig(raw, vars, path) {
|
|
|
54
59
|
if (!isPlainObject(raw.cowork))
|
|
55
60
|
throw new RoomsTasksConfigError(path, 'rooms.cowork: must be a mapping');
|
|
56
61
|
rejectUnknown(raw.cowork, RCK, path, 'rooms.cowork');
|
|
57
|
-
|
|
62
|
+
if (raw.cowork.config !== undefined)
|
|
63
|
+
throw new RoomsTasksConfigError(path, 'rooms.cowork.config is no longer supported; remove it and configure ~/.ours-client/profile.json for all services');
|
|
64
|
+
cowork = {};
|
|
58
65
|
}
|
|
59
66
|
if (!isPlainObject(raw.owner))
|
|
60
67
|
throw new RoomsTasksConfigError(path, 'rooms.owner: required and must be a mapping');
|
|
@@ -66,7 +73,7 @@ export function validateRoomsConfig(raw, vars, path) {
|
|
|
66
73
|
if (!CID_RE.test(expectedCid))
|
|
67
74
|
throw new RoomsTasksConfigError(path, 'rooms.owner.expected_cid: must be exactly 64 hexadecimal characters');
|
|
68
75
|
const ownerRole = raw.owner.role ?? 'Owner';
|
|
69
|
-
const invite = resolveInvite(raw.owner, vars, path);
|
|
76
|
+
const invite = resolveInvite(raw.owner, vars, path, deferredInviteFile);
|
|
70
77
|
const owner = {
|
|
71
78
|
provider: ownerProvider,
|
|
72
79
|
expected_cid: expectedCid.toLowerCase(),
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Typed client for the ours-cowork v1
|
|
2
|
+
* Typed client for the ours-cowork v1 gateway management protocol.
|
|
3
3
|
*
|
|
4
4
|
* Cowork owns room state. Fleet only invokes the versioned JSON RPC exposed
|
|
5
|
-
*
|
|
5
|
+
* through the shared HTTP gateway and projects the response into orchestration data.
|
|
6
6
|
*/
|
|
7
7
|
import type { Socket } from 'node:net';
|
|
8
8
|
import type { RoomHistoryEvidence } from './types.js';
|
|
@@ -93,7 +93,16 @@ export interface CoworkAdapter {
|
|
|
93
93
|
recoverRoom(roomId: string): Promise<CoworkRoomInfo>;
|
|
94
94
|
}
|
|
95
95
|
export declare class CoworkUnavailableError extends Error {
|
|
96
|
-
|
|
96
|
+
/**
|
|
97
|
+
* True only when the failure is a dependency that may still be starting
|
|
98
|
+
* (connection refused/reset, timeout, a gateway 502/503/504 without an RPC
|
|
99
|
+
* body). Selection, capability, credential and permission failures are
|
|
100
|
+
* never retryable, whatever transport reported them.
|
|
101
|
+
*/
|
|
102
|
+
readonly retryable: boolean;
|
|
103
|
+
constructor(message?: string, options?: ErrorOptions & {
|
|
104
|
+
retryable?: boolean;
|
|
105
|
+
});
|
|
97
106
|
}
|
|
98
107
|
export declare class CoworkProtocolError extends Error {
|
|
99
108
|
readonly operation: string;
|
|
@@ -108,6 +117,4 @@ export interface CoworkAdapterOptions {
|
|
|
108
117
|
home?: string;
|
|
109
118
|
connect?: (path: string) => Socket;
|
|
110
119
|
}
|
|
111
|
-
/** Resolve the same config/state inputs as ours-cowork and return its Unix socket. */
|
|
112
|
-
export declare function resolveCoworkSocketPath(options?: CoworkAdapterOptions): string;
|
|
113
120
|
export declare function createCoworkAdapter(options?: CoworkAdapterOptions): CoworkAdapter;
|
|
@@ -1,22 +1,24 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* Typed client for the ours-cowork v1
|
|
2
|
+
* Typed client for the ours-cowork v1 gateway management protocol.
|
|
3
3
|
*
|
|
4
4
|
* Cowork owns room state. Fleet only invokes the versioned JSON RPC exposed
|
|
5
|
-
*
|
|
5
|
+
* through the shared HTTP gateway and projects the response into orchestration data.
|
|
6
6
|
*/
|
|
7
|
-
import { existsSync, readFileSync } from 'node:fs';
|
|
8
|
-
import { homedir } from 'node:os';
|
|
9
|
-
import { join, resolve } from 'node:path';
|
|
10
|
-
import { createConnection } from 'node:net';
|
|
11
7
|
import { readClientProfile } from '../client-profile.js';
|
|
12
8
|
import { coworkHttpCall } from './cowork-http.js';
|
|
13
|
-
const RPC_VERSION = 1;
|
|
14
9
|
const DEFAULT_TIMEOUT_MS = 30_000;
|
|
15
|
-
const MAX_RESPONSE_BYTES = 4 * 1024 * 1024;
|
|
16
10
|
export class CoworkUnavailableError extends Error {
|
|
17
|
-
|
|
11
|
+
/**
|
|
12
|
+
* True only when the failure is a dependency that may still be starting
|
|
13
|
+
* (connection refused/reset, timeout, a gateway 502/503/504 without an RPC
|
|
14
|
+
* body). Selection, capability, credential and permission failures are
|
|
15
|
+
* never retryable, whatever transport reported them.
|
|
16
|
+
*/
|
|
17
|
+
retryable;
|
|
18
|
+
constructor(message = 'Cowork gateway management is not reachable', options) {
|
|
18
19
|
super(message, options);
|
|
19
20
|
this.name = 'CoworkUnavailableError';
|
|
21
|
+
this.retryable = options?.retryable === true;
|
|
20
22
|
}
|
|
21
23
|
}
|
|
22
24
|
export class CoworkProtocolError extends Error {
|
|
@@ -171,121 +173,13 @@ function projectHistoryRecord(value) {
|
|
|
171
173
|
}
|
|
172
174
|
return undefined;
|
|
173
175
|
}
|
|
174
|
-
/** Resolve the same config/state inputs as ours-cowork and return its Unix socket. */
|
|
175
|
-
export function resolveCoworkSocketPath(options = {}) {
|
|
176
|
-
if (options.socketPath)
|
|
177
|
-
return resolve(options.socketPath);
|
|
178
|
-
const env = options.env ?? process.env;
|
|
179
|
-
const home = options.home ?? homedir();
|
|
180
|
-
const configuredPath = options.configPath ?? env.OURS_COWORK_CONFIG;
|
|
181
|
-
const configPath = resolve(configuredPath ?? join(home, '.ours-cowork', 'config.json'));
|
|
182
|
-
let stateDir = resolve(home, '.ours-cowork');
|
|
183
|
-
if (existsSync(configPath)) {
|
|
184
|
-
let parsed;
|
|
185
|
-
try {
|
|
186
|
-
parsed = JSON.parse(readFileSync(configPath, 'utf8'));
|
|
187
|
-
}
|
|
188
|
-
catch (error) {
|
|
189
|
-
throw new CoworkProtocolError('config', `malformed config at ${configPath}`, 'invalid_config', { cause: error });
|
|
190
|
-
}
|
|
191
|
-
const config = object(parsed, 'config', 'config');
|
|
192
|
-
if (config.version !== 1 || typeof config.stateDir !== 'string' || !config.stateDir)
|
|
193
|
-
throw new CoworkProtocolError('config', `invalid Cowork v1 config at ${configPath}`, 'invalid_config');
|
|
194
|
-
stateDir = resolve(config.stateDir);
|
|
195
|
-
}
|
|
196
|
-
else if (configuredPath !== undefined) {
|
|
197
|
-
throw new CoworkProtocolError('config', `configured Cowork config does not exist: ${configPath}`, 'invalid_config');
|
|
198
|
-
}
|
|
199
|
-
if (env.OURS_COWORK_STATE_DIR)
|
|
200
|
-
stateDir = resolve(env.OURS_COWORK_STATE_DIR);
|
|
201
|
-
return join(stateDir, 'management.sock');
|
|
202
|
-
}
|
|
203
|
-
function rpcCall(socketPath, method, params, timeoutMs, connect) {
|
|
204
|
-
const id = `${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
205
|
-
const request = `${JSON.stringify({ version: RPC_VERSION, id, method, params })}\n`;
|
|
206
|
-
return new Promise((resolveCall, rejectCall) => {
|
|
207
|
-
const socket = connect(socketPath);
|
|
208
|
-
let bytes = '';
|
|
209
|
-
let size = 0;
|
|
210
|
-
let settled = false;
|
|
211
|
-
let connected = false;
|
|
212
|
-
const finishError = (error) => {
|
|
213
|
-
if (settled)
|
|
214
|
-
return;
|
|
215
|
-
settled = true;
|
|
216
|
-
clearTimeout(timer);
|
|
217
|
-
socket.destroy();
|
|
218
|
-
rejectCall(error);
|
|
219
|
-
};
|
|
220
|
-
const timer = setTimeout(() => finishError(new CoworkUnavailableError(connected ? 'Cowork did not answer the management socket' : 'Cowork management socket is not reachable')), timeoutMs);
|
|
221
|
-
socket.setEncoding('utf8');
|
|
222
|
-
socket.once('connect', () => {
|
|
223
|
-
connected = true;
|
|
224
|
-
socket.write(request);
|
|
225
|
-
});
|
|
226
|
-
socket.on('data', (chunk) => {
|
|
227
|
-
if (settled)
|
|
228
|
-
return;
|
|
229
|
-
const text = typeof chunk === 'string' ? chunk : chunk.toString('utf8');
|
|
230
|
-
size += Buffer.byteLength(text);
|
|
231
|
-
if (size > MAX_RESPONSE_BYTES) {
|
|
232
|
-
finishError(new CoworkProtocolError(method, 'response exceeded 4 MiB'));
|
|
233
|
-
return;
|
|
234
|
-
}
|
|
235
|
-
bytes += text;
|
|
236
|
-
const newline = bytes.indexOf('\n');
|
|
237
|
-
if (newline < 0)
|
|
238
|
-
return;
|
|
239
|
-
if (bytes.slice(newline + 1).trim() !== '') {
|
|
240
|
-
finishError(new CoworkProtocolError(method, 'daemon returned more than one response'));
|
|
241
|
-
return;
|
|
242
|
-
}
|
|
243
|
-
let response;
|
|
244
|
-
try {
|
|
245
|
-
response = object(JSON.parse(bytes.slice(0, newline)), method, 'RPC response');
|
|
246
|
-
}
|
|
247
|
-
catch (error) {
|
|
248
|
-
finishError(error instanceof CoworkProtocolError
|
|
249
|
-
? error : new CoworkProtocolError(method, 'daemon returned malformed JSON', 'protocol_error', { cause: error }));
|
|
250
|
-
return;
|
|
251
|
-
}
|
|
252
|
-
if (response.version !== RPC_VERSION || response.id !== id
|
|
253
|
-
|| (Object.hasOwn(response, 'error') === Object.hasOwn(response, 'result'))) {
|
|
254
|
-
finishError(new CoworkProtocolError(method, 'daemon returned an invalid RPC response'));
|
|
255
|
-
return;
|
|
256
|
-
}
|
|
257
|
-
settled = true;
|
|
258
|
-
clearTimeout(timer);
|
|
259
|
-
socket.destroy();
|
|
260
|
-
if (Object.hasOwn(response, 'error')) {
|
|
261
|
-
const error = object(response.error, method, 'RPC error');
|
|
262
|
-
rejectCall(new CoworkProtocolError(method, typeof error.message === 'string' ? error.message : 'unknown RPC error', typeof error.code === 'string' ? error.code : 'rpc_error'));
|
|
263
|
-
}
|
|
264
|
-
else {
|
|
265
|
-
resolveCall(response.result);
|
|
266
|
-
}
|
|
267
|
-
});
|
|
268
|
-
socket.once('end', () => {
|
|
269
|
-
if (!settled)
|
|
270
|
-
finishError(new CoworkProtocolError(method, 'daemon closed without a complete response'));
|
|
271
|
-
});
|
|
272
|
-
socket.once('error', (error) => {
|
|
273
|
-
if (!settled)
|
|
274
|
-
finishError(new CoworkUnavailableError(error.code === 'EACCES' || error.code === 'EPERM'
|
|
275
|
-
? 'Cowork management socket access denied' : 'Cowork management socket is not reachable', { cause: error }));
|
|
276
|
-
});
|
|
277
|
-
});
|
|
278
|
-
}
|
|
279
176
|
export function createCoworkAdapter(options = {}) {
|
|
280
177
|
const env = options.env ?? process.env;
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
const profile =
|
|
284
|
-
const remote = profile?.serverUrl ? profile : undefined;
|
|
285
|
-
const socketPath = remote ? undefined : resolveCoworkSocketPath(options);
|
|
178
|
+
if (options.socketPath !== undefined || options.configPath !== undefined)
|
|
179
|
+
throw new CoworkProtocolError('config', 'Local Cowork selectors are unsupported; configure the shared gateway client profile', 'invalid_config');
|
|
180
|
+
const profile = readClientProfile({ ...env, HOME: options.home ?? env.HOME });
|
|
286
181
|
const timeoutMs = options.timeoutMs ?? DEFAULT_TIMEOUT_MS;
|
|
287
|
-
const
|
|
288
|
-
const call = (method, params) => remote ? coworkHttpCall(remote, method, params, timeoutMs) : rpcCall(socketPath, method, params, timeoutMs, connect);
|
|
182
|
+
const call = (method, params) => coworkHttpCall(profile, method, params, timeoutMs);
|
|
289
183
|
return {
|
|
290
184
|
async available() {
|
|
291
185
|
try {
|