@ours.network/fleet 1.2.0-nightly.6 → 1.2.0-nightly.60
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 +287 -65
- package/dist/account-origin.d.ts +2 -0
- package/dist/account-origin.js +6 -0
- package/dist/agent-ours/bridge.js +35 -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 +4 -0
- package/dist/agent-ours/mcp-endpoint.js +5 -0
- package/dist/agent-ours/runtime.d.ts +9 -1
- package/dist/agent-ours/runtime.js +50 -2
- package/dist/agent-ours/service.d.ts +21 -1
- package/dist/agent-ours/service.js +94 -14
- package/dist/application/codex-models.d.ts +15 -0
- package/dist/application/codex-models.js +56 -0
- package/dist/application/conversation-history.d.ts +27 -0
- package/dist/application/conversation-history.js +212 -0
- package/dist/application/fleet-query-service.d.ts +1 -1
- package/dist/application/fleet-query-service.js +2 -2
- package/dist/application/legacy-supervisor-identity.d.ts +7 -0
- package/dist/application/legacy-supervisor-identity.js +43 -0
- 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/role-creation-service.js +3 -2
- package/dist/application/role-removal-service.js +9 -1
- package/dist/application/role-repository.js +19 -2
- 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 +30 -0
- package/dist/application/session-control.js +72 -3
- package/dist/application/supervisor-ours-tools.d.ts +159 -0
- package/dist/application/supervisor-ours-tools.js +115 -0
- package/dist/application/task-layouts.d.ts +91 -0
- package/dist/application/task-layouts.js +259 -0
- package/dist/application/task-room-service.d.ts +99 -3
- package/dist/application/task-room-service.js +285 -32
- package/dist/application/types.d.ts +2 -0
- package/dist/atomic-file.d.ts +5 -0
- package/dist/atomic-file.js +62 -1
- package/dist/briefing.d.ts +3 -1
- package/dist/briefing.js +70 -30
- package/dist/build-info.json +7 -4
- package/dist/capabilities.d.ts +6 -1
- package/dist/capabilities.js +8 -0
- package/dist/cli.js +407 -37
- package/dist/client-profile.d.ts +5 -17
- package/dist/client-profile.js +4 -127
- package/dist/config.d.ts +45 -0
- package/dist/config.js +57 -6
- package/dist/creation.d.ts +1 -1
- package/dist/creation.js +4 -4
- package/dist/daemon-recovery.d.ts +1 -8
- package/dist/daemon-recovery.js +8 -113
- package/dist/docs.d.ts +1 -1
- package/dist/docs.js +123 -19
- package/dist/doctor.d.ts +1 -1
- package/dist/doctor.js +55 -60
- package/dist/erased-resources.d.ts +9 -0
- package/dist/erased-resources.js +87 -0
- package/dist/file-delivery/reader.d.ts +15 -0
- package/dist/file-delivery/reader.js +63 -0
- package/dist/file-delivery/store.d.ts +15 -0
- package/dist/file-delivery/store.js +124 -0
- package/dist/file-delivery/types.d.ts +19 -0
- package/dist/file-delivery/types.js +1 -0
- package/dist/fleet-command-audit.d.ts +7 -0
- package/dist/fleet-command-audit.js +26 -9
- package/dist/harness/agent-session.d.ts +2 -0
- package/dist/harness/claude-code-session.js +1 -1
- package/dist/harness/claude-code.js +20 -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 +23 -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 +8 -34
- package/dist/monitor.js +89 -152
- package/dist/notifications/fleet-producer.d.ts +19 -0
- package/dist/notifications/fleet-producer.js +208 -0
- package/dist/notifications/outbox.d.ts +46 -0
- package/dist/notifications/outbox.js +158 -0
- package/dist/notifications/target-cleanup.d.ts +8 -0
- package/dist/notifications/target-cleanup.js +41 -0
- package/dist/notifications/task-producer.d.ts +18 -0
- package/dist/notifications/task-producer.js +67 -0
- package/dist/ops.d.ts +2 -0
- package/dist/ops.js +20 -3
- package/dist/owner-channel/channel.js +17 -6
- package/dist/owner-channel/commands.d.ts +4 -0
- package/dist/owner-channel/commands.js +8 -0
- package/dist/owner-channel/lifecycle-outbox.js +4 -3
- package/dist/owner-channel/ours-client.js +2 -17
- package/dist/paths.d.ts +2 -0
- package/dist/paths.js +2 -0
- package/dist/preset-migration.js +25 -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 +83 -0
- package/dist/rooms-tasks/archived-absence.d.ts +8 -0
- package/dist/rooms-tasks/archived-absence.js +48 -0
- package/dist/rooms-tasks/cli.js +65 -5
- package/dist/rooms-tasks/close.d.ts +6 -2
- package/dist/rooms-tasks/close.js +246 -37
- package/dist/rooms-tasks/config.d.ts +1 -1
- package/dist/rooms-tasks/config.js +11 -4
- package/dist/rooms-tasks/cowork-adapter.d.ts +2 -4
- package/dist/rooms-tasks/cowork-adapter.js +7 -121
- package/dist/rooms-tasks/cowork-http.js +29 -1
- package/dist/rooms-tasks/deletion.d.ts +1 -1
- package/dist/rooms-tasks/deletion.js +84 -5
- package/dist/rooms-tasks/erasure.d.ts +6 -0
- package/dist/rooms-tasks/erasure.js +173 -0
- package/dist/rooms-tasks/external-worker.d.ts +1 -1
- package/dist/rooms-tasks/external-worker.js +3 -4
- package/dist/rooms-tasks/layout-binding-client.d.ts +11 -0
- package/dist/rooms-tasks/layout-binding-client.js +56 -0
- package/dist/rooms-tasks/layout-binding-grants.d.ts +21 -0
- package/dist/rooms-tasks/layout-binding-grants.js +128 -0
- package/dist/rooms-tasks/layout-cli.d.ts +4 -0
- package/dist/rooms-tasks/layout-cli.js +107 -0
- package/dist/rooms-tasks/layout-config.d.ts +11 -0
- package/dist/rooms-tasks/layout-config.js +89 -0
- package/dist/rooms-tasks/layout-control.d.ts +21 -0
- package/dist/rooms-tasks/layout-control.js +52 -0
- package/dist/rooms-tasks/layout-member-retirement.d.ts +10 -0
- package/dist/rooms-tasks/layout-member-retirement.js +90 -0
- package/dist/rooms-tasks/layout-service.d.ts +43 -0
- package/dist/rooms-tasks/layout-service.js +203 -0
- package/dist/rooms-tasks/layout.d.ts +121 -0
- package/dist/rooms-tasks/layout.js +285 -0
- package/dist/rooms-tasks/legacy-task-member.d.ts +19 -0
- package/dist/rooms-tasks/legacy-task-member.js +116 -0
- package/dist/rooms-tasks/live-readiness.d.ts +18 -0
- package/dist/rooms-tasks/live-readiness.js +85 -0
- package/dist/rooms-tasks/member-ownership.d.ts +5 -0
- package/dist/rooms-tasks/member-ownership.js +40 -0
- package/dist/rooms-tasks/member-startup.d.ts +1 -0
- package/dist/rooms-tasks/member-startup.js +2 -0
- package/dist/rooms-tasks/provision.d.ts +3 -0
- package/dist/rooms-tasks/provision.js +101 -49
- package/dist/rooms-tasks/recovery.d.ts +19 -0
- package/dist/rooms-tasks/recovery.js +110 -0
- package/dist/rooms-tasks/room-state.d.ts +4 -2
- package/dist/rooms-tasks/room-state.js +47 -8
- package/dist/rooms-tasks/task-service-retirement.d.ts +11 -0
- package/dist/rooms-tasks/task-service-retirement.js +21 -0
- package/dist/rooms-tasks/task-state.d.ts +51 -8
- package/dist/rooms-tasks/task-state.js +245 -25
- package/dist/rooms-tasks/types.d.ts +40 -0
- package/dist/rooms-tasks/workspace-artifacts.d.ts +6 -0
- package/dist/rooms-tasks/workspace-artifacts.js +93 -0
- package/dist/rooms-tasks/workspace.d.ts +35 -0
- package/dist/rooms-tasks/workspace.js +325 -0
- package/dist/runner.d.ts +25 -3
- package/dist/runner.js +291 -49
- package/dist/session/acp.d.ts +53 -1
- package/dist/session/acp.js +216 -40
- package/dist/session/arbiter.d.ts +11 -0
- package/dist/session/arbiter.js +43 -2
- package/dist/session/codex-app-server.d.ts +32 -0
- package/dist/session/codex-app-server.js +137 -23
- package/dist/session/control.d.ts +12 -1
- package/dist/session/control.js +68 -3
- package/dist/session/conversation-normalizer.d.ts +2 -1
- package/dist/session/conversation-normalizer.js +1 -1
- package/dist/session/conversation-store.d.ts +1 -1
- package/dist/session/conversation-store.js +7 -11
- package/dist/session/conversation-types.d.ts +28 -3
- package/dist/session/prompt-batch.d.ts +63 -0
- package/dist/session/prompt-batch.js +86 -0
- package/dist/session/task-notice.d.ts +14 -0
- package/dist/session/task-notice.js +13 -0
- package/dist/session/types.d.ts +14 -0
- 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 +4 -0
- package/dist/startup-prompt.js +36 -0
- package/dist/startup-readiness.d.ts +13 -0
- package/dist/startup-readiness.js +38 -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 +75 -0
- package/dist/subscriptions/login.js +182 -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 +140 -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/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 +113 -65
- package/dist/temp-supervisor-recovery.d.ts +1 -1
- package/dist/temp-supervisor-recovery.js +2 -2
- package/dist/tunnel-setup-completion.d.ts +7 -0
- package/dist/tunnel-setup-completion.js +13 -0
- package/dist/tunnel-setup-input.d.ts +19 -0
- package/dist/tunnel-setup-input.js +124 -0
- package/dist/web/agent-attachments.d.ts +29 -0
- package/dist/web/agent-attachments.js +162 -0
- package/dist/web/auth.d.ts +63 -2
- package/dist/web/auth.js +191 -10
- 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/fleet-config-service.d.ts +4 -1
- package/dist/web/fleet-config-service.js +46 -15
- package/dist/web/prefix-gateway.d.ts +32 -0
- package/dist/web/prefix-gateway.js +238 -0
- package/dist/web/runtime.d.ts +3 -1
- package/dist/web/runtime.js +116 -31
- package/dist/web/server.d.ts +16 -0
- package/dist/web/server.js +654 -55
- 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 +82 -0
- package/dist/workspace-enrollment.js +566 -0
- package/dist/workspace-replacement.d.ts +66 -0
- package/dist/workspace-replacement.js +220 -0
- package/docs/acp-files.md +146 -0
- package/docs/fleet-migration.md +54 -0
- package/docs/live-voice.md +13 -0
- package/docs/room-layouts.md +227 -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 +20 -19
- 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 +1 -1
- 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 +34 -11
- package/presets/fleet/roles/Engineer.yaml +40 -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/client-profile.js
CHANGED
|
@@ -1,130 +1,7 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const PROFILE_FIELDS = ['endpoint', 'expectedInstanceId', 'credentialPath'];
|
|
6
|
-
const LOWERCASE_UUID = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/;
|
|
7
|
-
const LEGACY_CONFIG_FIELDS = ['apiToken', 'stateDir', 'port'];
|
|
8
|
-
const LEGACY_ENV_FIELDS = ['OURS_API_TOKEN', 'OURS_STATE_DIR', 'OURS_PORT', 'OURS_DAEMON_ID'];
|
|
9
|
-
export class ClientProfileError extends Error {
|
|
10
|
-
constructor(message) {
|
|
11
|
-
super(message);
|
|
12
|
-
this.name = 'ClientProfileError';
|
|
13
|
-
}
|
|
14
|
-
}
|
|
15
|
-
export function clientConfigPath(env) {
|
|
16
|
-
if (env.OURS_CONFIG !== undefined)
|
|
17
|
-
return env.OURS_CONFIG;
|
|
18
|
-
const managedPath = join(env.HOME || homedir(), '.ours-client', 'profile.json');
|
|
19
|
-
try {
|
|
20
|
-
lstatSync(managedPath);
|
|
21
|
-
return managedPath;
|
|
22
|
-
}
|
|
23
|
-
catch (error) {
|
|
24
|
-
if (error.code !== 'ENOENT')
|
|
25
|
-
throw invalid(managedPath, 'could not be read');
|
|
26
|
-
return join(homedir(), '.ours', 'config.json');
|
|
27
|
-
}
|
|
28
|
-
}
|
|
29
|
-
function invalid(path, detail) {
|
|
30
|
-
return new ClientProfileError(`ours client profile ${JSON.stringify(path)} ${detail}`);
|
|
31
|
-
}
|
|
32
|
-
/**
|
|
33
|
-
* Read Fleet's selected host-client profile without opening its credential.
|
|
34
|
-
* A legacy daemon config remains legacy only when none of the profile fields is
|
|
35
|
-
* present. Explicit and managed selections fail closed on invalid input.
|
|
36
|
-
*/
|
|
37
|
-
export function readClientProfile(env) {
|
|
38
|
-
const configPath = clientConfigPath(env);
|
|
39
|
-
const managed = configPath === join(env.HOME || homedir(), '.ours-client', 'profile.json');
|
|
40
|
-
const named = env.OURS_CONFIG !== undefined || managed;
|
|
41
|
-
let text;
|
|
42
|
-
try {
|
|
43
|
-
text = readFileSync(configPath, 'utf8');
|
|
44
|
-
}
|
|
45
|
-
catch {
|
|
46
|
-
if (named)
|
|
47
|
-
throw invalid(configPath, 'could not be read');
|
|
48
|
-
return undefined;
|
|
49
|
-
}
|
|
50
|
-
let parsed;
|
|
51
|
-
try {
|
|
52
|
-
parsed = JSON.parse(text);
|
|
53
|
-
}
|
|
54
|
-
catch {
|
|
55
|
-
if (named)
|
|
56
|
-
throw invalid(configPath, 'is not valid JSON');
|
|
57
|
-
return undefined;
|
|
58
|
-
}
|
|
59
|
-
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
60
|
-
if (named)
|
|
61
|
-
throw invalid(configPath, 'must contain a JSON object');
|
|
62
|
-
return undefined;
|
|
63
|
-
}
|
|
64
|
-
const row = parsed;
|
|
65
|
-
if (!PROFILE_FIELDS.some(field => Object.prototype.hasOwnProperty.call(row, field))) {
|
|
66
|
-
if (managed)
|
|
67
|
-
throw invalid(configPath, 'must contain a complete client profile');
|
|
68
|
-
return undefined;
|
|
69
|
-
}
|
|
70
|
-
let metadata;
|
|
71
|
-
try {
|
|
72
|
-
metadata = statSync(configPath);
|
|
73
|
-
}
|
|
74
|
-
catch {
|
|
75
|
-
throw invalid(configPath, 'could not be inspected');
|
|
76
|
-
}
|
|
77
|
-
if (!metadata.isFile())
|
|
78
|
-
throw invalid(configPath, 'must be a regular file');
|
|
79
|
-
const getuid = process.getuid;
|
|
80
|
-
if (getuid && metadata.uid !== getuid())
|
|
81
|
-
throw invalid(configPath, 'must be owned by this user');
|
|
82
|
-
if ((metadata.mode & 0o077) !== 0)
|
|
83
|
-
throw invalid(configPath, 'must not be accessible by group or other users');
|
|
84
|
-
for (const field of PROFILE_FIELDS)
|
|
85
|
-
if (typeof row[field] !== 'string' || !row[field].trim())
|
|
86
|
-
throw invalid(configPath, `has an invalid or missing ${field}`);
|
|
87
|
-
for (const field of LEGACY_CONFIG_FIELDS)
|
|
88
|
-
if (Object.prototype.hasOwnProperty.call(row, field))
|
|
89
|
-
throw invalid(configPath, `cannot combine ${field} with explicit profile selection`);
|
|
90
|
-
for (const field of LEGACY_ENV_FIELDS)
|
|
91
|
-
if (env[field]?.trim())
|
|
92
|
-
throw invalid(configPath, `cannot combine ${field} with explicit profile selection`);
|
|
93
|
-
const endpoint = row.endpoint.trim();
|
|
94
|
-
let url;
|
|
95
|
-
try {
|
|
96
|
-
url = new URL(endpoint);
|
|
97
|
-
}
|
|
98
|
-
catch {
|
|
99
|
-
throw invalid(configPath, 'has an invalid endpoint');
|
|
100
|
-
}
|
|
101
|
-
if ((url.protocol !== 'http:' && url.protocol !== 'https:') || url.username || url.password
|
|
102
|
-
|| url.search || url.hash)
|
|
103
|
-
throw invalid(configPath, 'endpoint must be an HTTP or HTTPS base URL');
|
|
104
|
-
const expectedInstanceId = row.expectedInstanceId.trim();
|
|
105
|
-
if (!LOWERCASE_UUID.test(expectedInstanceId))
|
|
106
|
-
throw invalid(configPath, 'expectedInstanceId must be a lowercase UUID');
|
|
107
|
-
const credentialPath = row.credentialPath.trim();
|
|
108
|
-
if (!isAbsolute(credentialPath))
|
|
109
|
-
throw invalid(configPath, 'credentialPath must be absolute');
|
|
110
|
-
let serverUrl;
|
|
111
|
-
if (Object.hasOwn(row, 'serverUrl')) {
|
|
112
|
-
if (typeof row.serverUrl !== 'string' || /[\s\\?#]/.test(row.serverUrl))
|
|
113
|
-
throw invalid(configPath, 'serverUrl must be a safe HTTP or HTTPS base URL');
|
|
114
|
-
try {
|
|
115
|
-
serverUrl = normalizeDaemonEndpoint(row.serverUrl);
|
|
116
|
-
}
|
|
117
|
-
catch {
|
|
118
|
-
throw invalid(configPath, 'serverUrl must be a safe HTTP or HTTPS base URL');
|
|
119
|
-
}
|
|
120
|
-
if (normalizeDaemonEndpoint(endpoint) !== serverUrl + '/daemon')
|
|
121
|
-
throw invalid(configPath, 'endpoint must select serverUrl/daemon');
|
|
122
|
-
}
|
|
123
|
-
return Object.freeze({
|
|
124
|
-
...(serverUrl === undefined ? {} : { serverUrl }),
|
|
125
|
-
endpoint: normalizeDaemonEndpoint(endpoint), expectedInstanceId, credentialPath, configPath,
|
|
126
|
-
});
|
|
127
|
-
}
|
|
1
|
+
import { clientProfilePath, readGatewayClientProfile } from '@ours.network/sdk/client';
|
|
2
|
+
export { ClientProfileError } from '@ours.network/sdk/client';
|
|
3
|
+
export const clientConfigPath = clientProfilePath;
|
|
4
|
+
export const readClientProfile = readGatewayClientProfile;
|
|
128
5
|
export function clientProfileKey(profile) {
|
|
129
6
|
return `${profile.endpoint}#${profile.expectedInstanceId}`;
|
|
130
7
|
}
|
package/dist/config.d.ts
CHANGED
|
@@ -2,6 +2,7 @@ import { type ConfigDiagnostic, type YamlMode } from './config-yaml.js';
|
|
|
2
2
|
import type { IsolationConfig, WrapContext } from './isolation/types.js';
|
|
3
3
|
import type { ResolvedWatchdog } from './watchdog/config.js';
|
|
4
4
|
import type { AgentLoopsConfig, ResolvedLoop, ResolvedRoleLoop } from './loops/config.js';
|
|
5
|
+
import { validateRoomsConfig } from './rooms-tasks/config.js';
|
|
5
6
|
export interface OverseeEntry {
|
|
6
7
|
agent: string;
|
|
7
8
|
interval: string;
|
|
@@ -35,6 +36,23 @@ export type FilesystemMode = 'read-only' | 'workspace' | 'unrestricted';
|
|
|
35
36
|
export type UnattendedMode = 'deny' | 'wait';
|
|
36
37
|
/** Monitor wake policy: preserve legacy booleans and add one explicit safe boundary. */
|
|
37
38
|
export type MonitorInterrupt = boolean | 'after_tool';
|
|
39
|
+
/**
|
|
40
|
+
* Workflows Fleet prepares native harness execution policy for. Declared per
|
|
41
|
+
* Agent; never inferred from a role's name or persona, and never inherited.
|
|
42
|
+
*/
|
|
43
|
+
export declare const MANAGED_CLI_WORKFLOW_IDS: readonly ["task-workflow"];
|
|
44
|
+
export type ManagedCliWorkflowId = (typeof MANAGED_CLI_WORKFLOW_IDS)[number];
|
|
45
|
+
/** Retired opt-in. Both keys are accepted so existing manifests load, and neither has any effect. */
|
|
46
|
+
export interface FileDeliveryConfig {
|
|
47
|
+
enabled?: boolean;
|
|
48
|
+
directory?: string;
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Current-chat file delivery is on for every Agent and takes the path the Agent
|
|
52
|
+
* provides. A legacy block is shape-checked only: `enabled: false` does not turn
|
|
53
|
+
* delivery off and `directory` does not restrict or create anything.
|
|
54
|
+
*/
|
|
55
|
+
export declare function validateFileDelivery(value: unknown): FileDeliveryConfig | undefined;
|
|
38
56
|
export interface CommonPermissions {
|
|
39
57
|
approval: ApprovalMode;
|
|
40
58
|
filesystem: FilesystemMode;
|
|
@@ -145,6 +163,10 @@ export interface RoleConfig {
|
|
|
145
163
|
owner_channel?: OwnerChannelConfigInput;
|
|
146
164
|
worklog?: WorklogPolicyInput;
|
|
147
165
|
auth_proxy?: Partial<AuthProxyConfig>;
|
|
166
|
+
/** Fleet-prepared CLI workflows this role may run outside its command sandbox. */
|
|
167
|
+
managed_cli?: ManagedCliWorkflowId[];
|
|
168
|
+
/** Retired and ignored; current-chat file delivery needs no Agent setting. */
|
|
169
|
+
file_delivery?: FileDeliveryConfig;
|
|
148
170
|
}
|
|
149
171
|
export type AgentSelection<T extends Record<string, unknown> = Record<string, unknown>> = {
|
|
150
172
|
ref: string;
|
|
@@ -166,6 +188,10 @@ export interface AgentDefinition {
|
|
|
166
188
|
owner_channel?: OwnerChannelConfigInput;
|
|
167
189
|
worklog?: WorklogPolicyInput;
|
|
168
190
|
auth_proxy?: Partial<AuthProxyConfig>;
|
|
191
|
+
/** Fleet-prepared CLI workflows this Agent may run outside its command sandbox. */
|
|
192
|
+
managed_cli?: ManagedCliWorkflowId[];
|
|
193
|
+
/** Retired and ignored; current-chat file delivery needs no Agent setting. */
|
|
194
|
+
file_delivery?: FileDeliveryConfig;
|
|
169
195
|
/** Scheduled turns scoped only to temporary launches; persistent Agents reject this field. */
|
|
170
196
|
loops?: AgentLoopsConfig;
|
|
171
197
|
}
|
|
@@ -178,6 +204,9 @@ export interface AgentTemplateInstance {
|
|
|
178
204
|
}
|
|
179
205
|
/** Internal first-boot payload for a Fleet-provisioned Cowork room member. */
|
|
180
206
|
export interface RoomMemberStartup {
|
|
207
|
+
/** Task ownership; absent for standalone rooms and legacy snapshots. */
|
|
208
|
+
task_id?: string;
|
|
209
|
+
workspace?: import('./rooms-tasks/workspace.js').OwnedWorkspace;
|
|
181
210
|
room_id: string;
|
|
182
211
|
room_identity_cid: string;
|
|
183
212
|
identity_name: string;
|
|
@@ -298,6 +327,8 @@ export declare function validateBrainValue(value: BarePreset, file: string, poin
|
|
|
298
327
|
export declare function validateEffectiveAgentTemplate(definition: AgentTemplateDefinition, id?: string): AgentTemplateDefinition;
|
|
299
328
|
export declare function assertBareKeys(value: unknown, allowed: string[], label: string): BarePreset;
|
|
300
329
|
export declare function splitRootFor(base: string): string;
|
|
330
|
+
/** Read only room connection/owner settings for an existing layout. */
|
|
331
|
+
export declare function loadRoomsConfig(configPath?: string): ReturnType<typeof validateRoomsConfig>;
|
|
301
332
|
/** Load a v2 manifest and bare Agent/Role/Brain documents from its stem directory. */
|
|
302
333
|
export declare function loadConfig(configPath?: string, options?: {
|
|
303
334
|
yamlMode?: YamlMode;
|
|
@@ -307,6 +338,8 @@ export declare function loadConfig(configPath?: string, options?: {
|
|
|
307
338
|
temporary?: boolean;
|
|
308
339
|
};
|
|
309
340
|
skipWatchdogs?: boolean;
|
|
341
|
+
/** The one Owner invitation file that tunnel setup writes itself and that may therefore still be absent. */
|
|
342
|
+
deferredOwnerInviteFile?: string;
|
|
310
343
|
}): FleetConfig;
|
|
311
344
|
/** Full read-only Agent resolution used by launch dry-runs and room plan validation. */
|
|
312
345
|
export declare function resolveAgentDefinitionDryRun(configPath: string, id: string, definition: AgentDefinition): ResolvedRole;
|
|
@@ -331,10 +364,22 @@ export declare function resolvePermissions(defaults: unknown, role: Partial<Comm
|
|
|
331
364
|
* malformed block so a typo fails loudly rather than silently disarming a monitor.
|
|
332
365
|
* Exported so temp-spawn (which builds a ResolvedRole by hand) resolves identically.
|
|
333
366
|
*/
|
|
367
|
+
/**
|
|
368
|
+
* A monitor without an explicit `interrupt` steers wakes at the next tool
|
|
369
|
+
* boundary. Hermes cannot, so its default stays non-interrupting; sessions
|
|
370
|
+
* without tool-boundary steering fall back to non-cancelling delivery.
|
|
371
|
+
*/
|
|
372
|
+
export declare function defaultMonitorInterrupt(harness: string | undefined): MonitorInterrupt;
|
|
373
|
+
/**
|
|
374
|
+
* Resolve a persisted temporary-role monitor. A snapshot written before
|
|
375
|
+
* monitor.interrupt existed keeps the non-interrupting delivery it ran with.
|
|
376
|
+
*/
|
|
377
|
+
export declare function resolveSnapshotMonitor(monitor: unknown): MonitorConfig;
|
|
334
378
|
export declare function resolveMonitorConfig(defMonitor: unknown, roleMonitor?: Partial<MonitorConfig>, labels?: {
|
|
335
379
|
base?: string;
|
|
336
380
|
file?: string;
|
|
337
381
|
name?: string;
|
|
382
|
+
harness?: string;
|
|
338
383
|
}): MonitorConfig;
|
|
339
384
|
export declare function findRole(cfg: FleetConfig, name: string): ResolvedRole;
|
|
340
385
|
export {};
|
package/dist/config.js
CHANGED
|
@@ -24,6 +24,25 @@ export const NOTIFY_EVENT_TYPES = [
|
|
|
24
24
|
'message_received', 'file_received', 'sibling_contact_added', 'local_contact_request',
|
|
25
25
|
'pending_message', 'contact_restored', 'inbound_error', 'state_import_failed',
|
|
26
26
|
];
|
|
27
|
+
/**
|
|
28
|
+
* Workflows Fleet prepares native harness execution policy for. Declared per
|
|
29
|
+
* Agent; never inferred from a role's name or persona, and never inherited.
|
|
30
|
+
*/
|
|
31
|
+
export const MANAGED_CLI_WORKFLOW_IDS = ['task-workflow'];
|
|
32
|
+
/**
|
|
33
|
+
* Current-chat file delivery is on for every Agent and takes the path the Agent
|
|
34
|
+
* provides. A legacy block is shape-checked only: `enabled: false` does not turn
|
|
35
|
+
* delivery off and `directory` does not restrict or create anything.
|
|
36
|
+
*/
|
|
37
|
+
export function validateFileDelivery(value) {
|
|
38
|
+
if (value === undefined)
|
|
39
|
+
return undefined;
|
|
40
|
+
if (!isPlainObject(value) || Object.keys(value).some(k => !['enabled', 'directory'].includes(k))
|
|
41
|
+
|| (value.enabled !== undefined && typeof value.enabled !== 'boolean')
|
|
42
|
+
|| (value.directory !== undefined && typeof value.directory !== 'string'))
|
|
43
|
+
throw new ConfigError('file_delivery is retired and ignored; it accepts only the legacy enabled:boolean and directory:string keys');
|
|
44
|
+
return value;
|
|
45
|
+
}
|
|
27
46
|
/** Default wake sources when a role does not list its own. */
|
|
28
47
|
export const DEFAULT_WAKE_SOURCES = ['message_received', 'file_received', 'local_contact_request', 'pending_message'];
|
|
29
48
|
const MONITOR_KEYS = [
|
|
@@ -139,7 +158,7 @@ export const ROLE_NAME_RE = /^[A-Za-z0-9][A-Za-z0-9_-]{0,63}$/;
|
|
|
139
158
|
const ROLE_KEYS = [
|
|
140
159
|
'harness', 'session', 'session_options', 'permissions', 'identity', 'cwd', 'coordinator', 'mission', 'persona', 'bio',
|
|
141
160
|
'briefing_file', 'model', 'effort', 'model_chain', 'max_tokens', 'autocompact_pct', 'env', 'oversee', 'harness_options',
|
|
142
|
-
'isolation', 'monitor', 'owner_channel', 'worklog', 'auth_proxy',
|
|
161
|
+
'isolation', 'monitor', 'owner_channel', 'worklog', 'auth_proxy', 'managed_cli', 'file_delivery',
|
|
143
162
|
];
|
|
144
163
|
export const ROLE_PRESET_KEYS = ['mission', 'persona', 'bio', 'briefing_file'];
|
|
145
164
|
export const BRAIN_PRESET_KEYS = [
|
|
@@ -148,7 +167,7 @@ export const BRAIN_PRESET_KEYS = [
|
|
|
148
167
|
];
|
|
149
168
|
export const AGENT_KEYS = [
|
|
150
169
|
'role', 'brain', 'permissions', 'identity', 'cwd', 'coordinator', 'env', 'oversee',
|
|
151
|
-
'isolation', 'monitor', 'owner_channel', 'worklog', 'auth_proxy', 'loops',
|
|
170
|
+
'isolation', 'monitor', 'owner_channel', 'worklog', 'auth_proxy', 'loops', 'managed_cli', 'file_delivery',
|
|
152
171
|
];
|
|
153
172
|
const AGENT_INSTANCE_KEYS = ['template', 'overrides'];
|
|
154
173
|
const TEMPLATE_FORBIDDEN_KEYS = ['identity'];
|
|
@@ -194,6 +213,7 @@ export function validateBrainValue(value, file, pointer) {
|
|
|
194
213
|
}
|
|
195
214
|
/** Pure launch-boundary validation for a fully resolved temporary Agent definition. */
|
|
196
215
|
export function validateEffectiveAgentTemplate(definition, id = 'room-member') {
|
|
216
|
+
validateFileDelivery(definition.file_delivery);
|
|
197
217
|
const value = structuredClone(definition);
|
|
198
218
|
const bad = Object.keys(value).filter(key => !AGENT_KEYS.includes(key));
|
|
199
219
|
if (bad.length)
|
|
@@ -236,6 +256,12 @@ function validateAgentScalars(value, file, id) {
|
|
|
236
256
|
if (current !== undefined && (typeof current !== 'string' || !current.trim()))
|
|
237
257
|
schemaError(file, `/agents/${id}/${key}`, 'must be a non-blank string');
|
|
238
258
|
}
|
|
259
|
+
if (value.managed_cli !== undefined) {
|
|
260
|
+
const ids = value.managed_cli;
|
|
261
|
+
if (!Array.isArray(ids) || ids.some(entry => !MANAGED_CLI_WORKFLOW_IDS.includes(entry))
|
|
262
|
+
|| new Set(ids).size !== ids.length)
|
|
263
|
+
schemaError(file, `/agents/${id}/managed_cli`, `must be a list of distinct workflow IDs; supported: ${MANAGED_CLI_WORKFLOW_IDS.join(', ')}`);
|
|
264
|
+
}
|
|
239
265
|
if (value.coordinator !== undefined
|
|
240
266
|
&& (typeof value.coordinator !== 'string' || !ROLE_NAME_RE.test(value.coordinator)))
|
|
241
267
|
schemaError(file, `/agents/${id}/coordinator`, 'must be a valid Agent ID');
|
|
@@ -575,6 +601,13 @@ function deepSub(v, vars) {
|
|
|
575
601
|
return Object.fromEntries(Object.entries(v).map(([k, x]) => [k, deepSub(x, vars)]));
|
|
576
602
|
return v;
|
|
577
603
|
}
|
|
604
|
+
/** Read only room connection/owner settings for an existing layout. */
|
|
605
|
+
export function loadRoomsConfig(configPath = defaultConfigPath()) {
|
|
606
|
+
assertTrustedPath(configPath, 'file');
|
|
607
|
+
const doc = parseFleetDocument(configPath, readFileSync(configPath, 'utf8')).value;
|
|
608
|
+
const vars = (doc.vars ?? {});
|
|
609
|
+
return validateRoomsConfig(deepSub(doc.rooms, vars), vars, configPath);
|
|
610
|
+
}
|
|
578
611
|
/** Load a v2 manifest and bare Agent/Role/Brain documents from its stem directory. */
|
|
579
612
|
export function loadConfig(configPath, options = {}) {
|
|
580
613
|
const base = configPath ?? defaultConfigPath();
|
|
@@ -628,6 +661,7 @@ export function loadConfig(configPath, options = {}) {
|
|
|
628
661
|
const bad = Object.keys(r).filter(k => !ROLE_KEYS.includes(k));
|
|
629
662
|
if (bad.length)
|
|
630
663
|
throw new ConfigError(`${file}: role '${name}' has unknown key(s) ${bad.join(', ')}; allowed: ${ROLE_KEYS.join(', ')}`);
|
|
664
|
+
validateFileDelivery(r.file_delivery);
|
|
631
665
|
const isolation = r.isolation ?? defaults.isolation;
|
|
632
666
|
const session = resolveSession(r.session ?? defaults.session, file, name);
|
|
633
667
|
const sessionOptions = resolveSessionOptions(defaults.session_options, r.session_options, session, file, name);
|
|
@@ -649,11 +683,11 @@ export function loadConfig(configPath, options = {}) {
|
|
|
649
683
|
if (problems.length)
|
|
650
684
|
throw new ConfigError(`${file}: role '${name}' ${problems.join('; ')}`);
|
|
651
685
|
}
|
|
652
|
-
const
|
|
686
|
+
const harness = r.harness ?? defaults.harness ?? 'claude-code';
|
|
687
|
+
const monitor = resolveMonitorConfig(defaults.monitor, r.monitor, { base, file, name, harness });
|
|
653
688
|
const ownerChannel = resolveOwnerChannelConfig(defaults.owner_channel, r.owner_channel, session, file, name);
|
|
654
689
|
const worklog = resolveWorklogPolicy(defaults.worklog, r.worklog, file, name);
|
|
655
690
|
const authProxy = resolveAuthProxy(defaults.auth_proxy, r.auth_proxy, file, name);
|
|
656
|
-
const harness = r.harness ?? defaults.harness ?? 'claude-code';
|
|
657
691
|
if (session === 'codex-app-server' && harness !== 'codex')
|
|
658
692
|
throw new ConfigError(`${file}: role '${name}' session: codex-app-server requires harness: codex`);
|
|
659
693
|
const defaultHarness = defaults.harness ?? 'claude-code';
|
|
@@ -757,7 +791,7 @@ export function loadConfig(configPath, options = {}) {
|
|
|
757
791
|
if (doc.rooms !== undefined) {
|
|
758
792
|
if (rooms)
|
|
759
793
|
throw new ConfigError(`rooms: defined in multiple files; last: ${file}`);
|
|
760
|
-
const validated = validateRoomsConfig(deepSub(doc.rooms, vars), vars, file);
|
|
794
|
+
const validated = validateRoomsConfig(deepSub(doc.rooms, vars), vars, file, options.deferredOwnerInviteFile);
|
|
761
795
|
ownerInviteFingerprint = validated._invite?.fingerprint;
|
|
762
796
|
ownerInvite = validated._invite?.value;
|
|
763
797
|
const { _invite: _, ...clean } = validated;
|
|
@@ -1135,6 +1169,23 @@ function resolveStartStaggerMs(raw, base) {
|
|
|
1135
1169
|
* malformed block so a typo fails loudly rather than silently disarming a monitor.
|
|
1136
1170
|
* Exported so temp-spawn (which builds a ResolvedRole by hand) resolves identically.
|
|
1137
1171
|
*/
|
|
1172
|
+
/**
|
|
1173
|
+
* A monitor without an explicit `interrupt` steers wakes at the next tool
|
|
1174
|
+
* boundary. Hermes cannot, so its default stays non-interrupting; sessions
|
|
1175
|
+
* without tool-boundary steering fall back to non-cancelling delivery.
|
|
1176
|
+
*/
|
|
1177
|
+
export function defaultMonitorInterrupt(harness) {
|
|
1178
|
+
return harness === 'hermes' ? false : 'after_tool';
|
|
1179
|
+
}
|
|
1180
|
+
/**
|
|
1181
|
+
* Resolve a persisted temporary-role monitor. A snapshot written before
|
|
1182
|
+
* monitor.interrupt existed keeps the non-interrupting delivery it ran with.
|
|
1183
|
+
*/
|
|
1184
|
+
export function resolveSnapshotMonitor(monitor) {
|
|
1185
|
+
if (!isPlainObject(monitor))
|
|
1186
|
+
return resolveMonitorConfig(undefined, monitor);
|
|
1187
|
+
return resolveMonitorConfig(undefined, { interrupt: false, ...monitor });
|
|
1188
|
+
}
|
|
1138
1189
|
export function resolveMonitorConfig(defMonitor, roleMonitor, labels = {}) {
|
|
1139
1190
|
const where = labels.file && labels.name ? `${labels.file}: role '${labels.name}' ` : '';
|
|
1140
1191
|
if (defMonitor !== undefined && !isPlainObject(defMonitor))
|
|
@@ -1169,7 +1220,7 @@ export function resolveMonitorConfig(defMonitor, roleMonitor, labels = {}) {
|
|
|
1169
1220
|
wake_sources: merged.wake_sources ?? [...DEFAULT_WAKE_SOURCES],
|
|
1170
1221
|
batch_ms: merged.batch_ms ?? MONITOR_DEFAULT_BATCH_MS,
|
|
1171
1222
|
inject: merged.inject ?? 'notification',
|
|
1172
|
-
interrupt: merged.interrupt ??
|
|
1223
|
+
interrupt: merged.interrupt ?? defaultMonitorInterrupt(labels.harness),
|
|
1173
1224
|
...(merged.stall_recovery !== undefined ? { stall_recovery: merged.stall_recovery } : {}),
|
|
1174
1225
|
...(merged.stall_timeout_ms !== undefined ? { stall_timeout_ms: merged.stall_timeout_ms } : {}),
|
|
1175
1226
|
turn_fail_threshold: merged.turn_fail_threshold ?? MONITOR_DEFAULT_TURN_FAIL_THRESHOLD,
|
package/dist/creation.d.ts
CHANGED
|
@@ -153,7 +153,7 @@ export interface DaemonIdentityProvisionerOptions {
|
|
|
153
153
|
export declare function daemonIdentityProvisioner(env?: NodeJS.ProcessEnv, attachClient?: (options: AttachOursClientOptions) => Promise<IdentityInventoryClient>, options?: DaemonIdentityProvisionerOptions): IdentityProvisioner;
|
|
154
154
|
/** Inventory-only variant for session-owned temporary identity lifecycles. */
|
|
155
155
|
export declare function daemonIdentityInventoryProvisioner(env?: NodeJS.ProcessEnv, attachClient?: (options: AttachOursClientOptions) => Promise<IdentityInventoryClient>): IdentityProvisioner;
|
|
156
|
-
/** Atomically write
|
|
156
|
+
/** Atomically write an owner-private Agent file, journalling it for rollback. */
|
|
157
157
|
export declare function writeRoleFile(tx: CreationTransaction, file: string, contents: string): void;
|
|
158
158
|
/** Where a setting's effective value came from. */
|
|
159
159
|
export type ProvenanceSource = 'cli' | 'agent-template' | 'fleet-default' | 'caller-role' | 'built-in';
|
package/dist/creation.js
CHANGED
|
@@ -213,10 +213,10 @@ export async function ensureIdentity(name, profile, provisioner, log = () => { }
|
|
|
213
213
|
export function daemonIdentityProvisioner(env = process.env, attachClient = attachOursClient, options = {}) {
|
|
214
214
|
const connect = (leaseToken) => {
|
|
215
215
|
const profile = readClientProfile(env);
|
|
216
|
-
return attachClient(
|
|
216
|
+
return attachClient({
|
|
217
217
|
endpoint: profile.endpoint, expectedInstanceId: profile.expectedInstanceId,
|
|
218
218
|
credentialPath: profile.credentialPath, sessionMode: 'external', leaseToken, env: {},
|
|
219
|
-
}
|
|
219
|
+
});
|
|
220
220
|
};
|
|
221
221
|
const provisioner = {
|
|
222
222
|
async exists(name) {
|
|
@@ -294,10 +294,10 @@ export function daemonIdentityInventoryProvisioner(env = process.env, attachClie
|
|
|
294
294
|
acceptTemporaryExisting: true,
|
|
295
295
|
});
|
|
296
296
|
}
|
|
297
|
-
/** Atomically write
|
|
297
|
+
/** Atomically write an owner-private Agent file, journalling it for rollback. */
|
|
298
298
|
export function writeRoleFile(tx, file, contents) {
|
|
299
299
|
const existed = existsSync(file);
|
|
300
|
-
replaceFileAtomically(file, contents,
|
|
300
|
+
replaceFileAtomically(file, contents, 0o600);
|
|
301
301
|
tx.record({
|
|
302
302
|
stage: `Agent file ${file}`,
|
|
303
303
|
// Only remove what THIS transaction created; never delete a file the
|
|
@@ -34,14 +34,7 @@ interface GenerationProbeDeps {
|
|
|
34
34
|
canonicalize?(path: string): string;
|
|
35
35
|
attachClient?(options: AttachOursClientOptions): Pick<OursClient, 'version' | 'identities' | 'close'> | Promise<Pick<OursClient, 'version' | 'identities' | 'close'>>;
|
|
36
36
|
}
|
|
37
|
-
/**
|
|
38
|
-
* Corroborate the loopback daemon's unauthenticated `/info`, its credentialed
|
|
39
|
-
* identity-index readiness route, and its local boot-generation record. The
|
|
40
|
-
* index enforces auth when the daemon visibility requires it; open visibility
|
|
41
|
-
* deliberately does not. No source is sufficient
|
|
42
|
-
* alone: `/info` has no boot id, the identity index has no generation, and a
|
|
43
|
-
* stale `ready` file can outlive the process that wrote it.
|
|
44
|
-
*/
|
|
37
|
+
/** Read generation and readiness through the selected gateway. Server paths are opaque. */
|
|
45
38
|
export declare function probeDaemonGeneration(fetch: FetchLike, env: NodeJS.ProcessEnv, deps?: GenerationProbeDeps): Promise<DaemonGenerationProbe>;
|
|
46
39
|
export declare class DaemonGenerationObserver {
|
|
47
40
|
private current?;
|
package/dist/daemon-recovery.js
CHANGED
|
@@ -1,8 +1,7 @@
|
|
|
1
|
-
import { readFileSync
|
|
2
|
-
import { join
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
3
|
import { createHash, randomUUID } from 'node:crypto';
|
|
4
4
|
import { attachOursClient, } from '@ours.network/sdk/client';
|
|
5
|
-
import { readDaemonConfig, resolveEndpoint } from './monitor.js';
|
|
6
5
|
import { readClientProfile } from './client-profile.js';
|
|
7
6
|
import { replaceFileAtomically } from './atomic-file.js';
|
|
8
7
|
export const DAEMON_RECOVERY_MAX_ATTEMPTS = 6;
|
|
@@ -39,129 +38,25 @@ function startupProgress(value) {
|
|
|
39
38
|
return undefined;
|
|
40
39
|
return row;
|
|
41
40
|
}
|
|
42
|
-
|
|
43
|
-
const absolute = resolve(path);
|
|
44
|
-
return deps.canonicalize?.(absolute) ?? realpathSync.native(absolute);
|
|
45
|
-
}
|
|
46
|
-
/**
|
|
47
|
-
* Corroborate the loopback daemon's unauthenticated `/info`, its credentialed
|
|
48
|
-
* identity-index readiness route, and its local boot-generation record. The
|
|
49
|
-
* index enforces auth when the daemon visibility requires it; open visibility
|
|
50
|
-
* deliberately does not. No source is sufficient
|
|
51
|
-
* alone: `/info` has no boot id, the identity index has no generation, and a
|
|
52
|
-
* stale `ready` file can outlive the process that wrote it.
|
|
53
|
-
*/
|
|
41
|
+
/** Read generation and readiness through the selected gateway. Server paths are opaque. */
|
|
54
42
|
export async function probeDaemonGeneration(fetch, env, deps = {}) {
|
|
55
|
-
const profile = readClientProfile(env);
|
|
56
|
-
if (profile || env.OURS_DAEMON_ID)
|
|
57
|
-
return probeSelectedDaemon(env, deps, profile);
|
|
58
|
-
const endpoint = resolveEndpoint(env);
|
|
59
|
-
let response;
|
|
60
|
-
try {
|
|
61
|
-
response = await fetch(`${endpoint.origin}/info`, { headers: endpoint.headers });
|
|
62
|
-
}
|
|
63
|
-
catch {
|
|
64
|
-
return { state: 'unavailable', reason: 'DAEMON_INFO_UNREACHABLE' };
|
|
65
|
-
}
|
|
66
|
-
if (!response.ok)
|
|
67
|
-
return {
|
|
68
|
-
state: 'unavailable',
|
|
69
|
-
reason: response.status === 401 ? 'DAEMON_INFO_UNAUTHORIZED' : 'DAEMON_INFO_HTTP_ERROR',
|
|
70
|
-
};
|
|
71
|
-
let info;
|
|
72
|
-
try {
|
|
73
|
-
const value = await response.json();
|
|
74
|
-
if (!value || typeof value !== 'object' || Array.isArray(value))
|
|
75
|
-
throw new Error('shape');
|
|
76
|
-
info = value;
|
|
77
|
-
}
|
|
78
|
-
catch {
|
|
79
|
-
return { state: 'unavailable', reason: 'DAEMON_INFO_INVALID' };
|
|
80
|
-
}
|
|
81
|
-
if (info.name !== 'ours' || !positiveInteger(info.pid)
|
|
82
|
-
|| typeof info.stateDir !== 'string' || info.stateDir.length === 0)
|
|
83
|
-
return { state: 'unavailable', reason: 'DAEMON_INFO_INVALID' };
|
|
84
|
-
let reportedStateDir;
|
|
85
|
-
let expectedStateDir;
|
|
86
|
-
try {
|
|
87
|
-
reportedStateDir = canonical(info.stateDir, deps);
|
|
88
|
-
expectedStateDir = canonical(endpoint.stateDir, deps);
|
|
89
|
-
}
|
|
90
|
-
catch {
|
|
91
|
-
return { state: 'unavailable', reason: 'DAEMON_STATE_DIR_UNAVAILABLE' };
|
|
92
|
-
}
|
|
93
|
-
if (reportedStateDir !== expectedStateDir)
|
|
94
|
-
return { state: 'unavailable', reason: 'DAEMON_STATE_DIR_MISMATCH' };
|
|
95
|
-
// `/identities` is the daemon's credential-capable readiness route. A
|
|
96
|
-
// valid-but-empty index is a documented intermediate startup state, never
|
|
97
|
-
// proof of readiness.
|
|
98
|
-
let identitiesResponse;
|
|
99
|
-
try {
|
|
100
|
-
identitiesResponse = await fetch(`${endpoint.origin}/identities`, { headers: endpoint.headers });
|
|
101
|
-
}
|
|
102
|
-
catch {
|
|
103
|
-
return { state: 'unavailable', reason: 'DAEMON_IDENTITIES_UNREACHABLE' };
|
|
104
|
-
}
|
|
105
|
-
if (!identitiesResponse.ok)
|
|
106
|
-
return {
|
|
107
|
-
state: 'unavailable',
|
|
108
|
-
reason: identitiesResponse.status === 401
|
|
109
|
-
? 'DAEMON_IDENTITIES_UNAUTHORIZED' : 'DAEMON_IDENTITIES_HTTP_ERROR',
|
|
110
|
-
};
|
|
111
|
-
try {
|
|
112
|
-
const body = await identitiesResponse.json();
|
|
113
|
-
if (!body || typeof body !== 'object' || Array.isArray(body)
|
|
114
|
-
|| !Array.isArray(body.identities))
|
|
115
|
-
return { state: 'unavailable', reason: 'DAEMON_IDENTITIES_INVALID' };
|
|
116
|
-
if (body.identities.length === 0)
|
|
117
|
-
return { state: 'unavailable', reason: 'DAEMON_IDENTITIES_NOT_READY' };
|
|
118
|
-
}
|
|
119
|
-
catch {
|
|
120
|
-
return { state: 'unavailable', reason: 'DAEMON_IDENTITIES_INVALID' };
|
|
121
|
-
}
|
|
122
|
-
let progress;
|
|
123
43
|
try {
|
|
124
|
-
|
|
125
|
-
?? readFileSync(join(reportedStateDir, 'startup-progress.json'), 'utf8');
|
|
126
|
-
progress = startupProgress(JSON.parse(text));
|
|
44
|
+
return await probeSelectedDaemon(deps, readClientProfile(env));
|
|
127
45
|
}
|
|
128
46
|
catch {
|
|
129
|
-
return { state: 'unavailable', reason: '
|
|
47
|
+
return { state: 'unavailable', reason: 'CLIENT_GATEWAY_PROFILE_INVALID' };
|
|
130
48
|
}
|
|
131
|
-
if (!progress)
|
|
132
|
-
return { state: 'unavailable', reason: 'DAEMON_PROGRESS_INVALID' };
|
|
133
|
-
if (progress.phase !== 'ready')
|
|
134
|
-
return { state: 'unavailable', reason: 'DAEMON_PROGRESS_NOT_READY' };
|
|
135
|
-
if (progress.pid !== info.pid)
|
|
136
|
-
return { state: 'unavailable', reason: 'DAEMON_GENERATION_MISMATCH' };
|
|
137
|
-
return {
|
|
138
|
-
state: 'ready',
|
|
139
|
-
generation: {
|
|
140
|
-
// Keep bootId opaque. Its current writer uses `${pid}-${startedAt}`, but
|
|
141
|
-
// equality—not its formatting—is the generation contract Fleet needs.
|
|
142
|
-
bootId: progress.bootId,
|
|
143
|
-
pid: progress.pid,
|
|
144
|
-
startedAt: progress.startedAt,
|
|
145
|
-
stateDir: reportedStateDir,
|
|
146
|
-
},
|
|
147
|
-
};
|
|
148
49
|
}
|
|
149
50
|
/** Container selection uses public SDK reads; daemon paths/PIDs remain opaque metadata. */
|
|
150
|
-
async function probeSelectedDaemon(
|
|
51
|
+
async function probeSelectedDaemon(deps, profile) {
|
|
151
52
|
// Defer credential-file access to SDK selection, which checks the configured
|
|
152
53
|
// daemon identity/capability before it reads or sends the credential.
|
|
153
|
-
const endpoint = profile ? undefined : resolveEndpoint(env, false);
|
|
154
|
-
const token = profile ? undefined : env.OURS_API_TOKEN?.trim() || readDaemonConfig(env).apiToken;
|
|
155
54
|
let client;
|
|
156
55
|
try {
|
|
157
|
-
const options =
|
|
56
|
+
const options = {
|
|
158
57
|
endpoint: profile.endpoint, expectedInstanceId: profile.expectedInstanceId,
|
|
159
58
|
credentialPath: profile.credentialPath,
|
|
160
59
|
sessionMode: 'external', leaseToken: randomUUID(), env: {},
|
|
161
|
-
} : {
|
|
162
|
-
endpoint: endpoint.origin, expectedInstanceId: env.OURS_DAEMON_ID,
|
|
163
|
-
sessionMode: 'external', leaseToken: randomUUID(),
|
|
164
|
-
...(token ? { token } : { credentialPath: join(endpoint.stateDir, 'daemon-token') }),
|
|
165
60
|
};
|
|
166
61
|
client = await (deps.attachClient?.(options) ?? attachOursClient(options));
|
|
167
62
|
const info = await client.version({ startup: true });
|
|
@@ -189,7 +84,7 @@ async function probeSelectedDaemon(env, deps, profile) {
|
|
|
189
84
|
return { state: 'unavailable', reason: 'DAEMON_SELECTED_PROBE_UNAVAILABLE' };
|
|
190
85
|
}
|
|
191
86
|
finally {
|
|
192
|
-
await client?.close();
|
|
87
|
+
await client?.close().catch(() => undefined);
|
|
193
88
|
}
|
|
194
89
|
}
|
|
195
90
|
export class DaemonGenerationObserver {
|