@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
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
2
|
import { MAX_CHUNK_BYTES } from './wire.js';
|
|
3
|
+
/** Bridge refusals of a current-chat file that are safe to relay verbatim. */
|
|
4
|
+
const CHAT_FILE_REFUSAL = /^(INVALID_CHAT_FILE_PATH|CHAT_FILE_[A-Z_]{1,40}|FILE_EXCEEDS_20_MIB)$/;
|
|
3
5
|
/** Call-scoped callbacks carry no daemon credentials or upload receipts. */
|
|
4
6
|
export class FileCallbacks {
|
|
5
7
|
wire;
|
|
@@ -16,7 +18,7 @@ export class FileCallbacks {
|
|
|
16
18
|
this.pending.delete(frame.id);
|
|
17
19
|
clearTimeout(p.timer);
|
|
18
20
|
if (frame.error)
|
|
19
|
-
p.reject(Error('AGENT_FILE_OPERATION_FAILED'));
|
|
21
|
+
p.reject(Error(typeof frame.error === 'string' && CHAT_FILE_REFUSAL.test(frame.error) ? frame.error : 'AGENT_FILE_OPERATION_FAILED'));
|
|
20
22
|
else
|
|
21
23
|
p.resolve(frame.value);
|
|
22
24
|
}
|
|
@@ -59,14 +61,26 @@ export class FileCallbacks {
|
|
|
59
61
|
return (await this.rpc(String(extra.requestId), 'probe', { path }, extra.signal)) === true;
|
|
60
62
|
}
|
|
61
63
|
async read(path, extra) {
|
|
64
|
+
return this.openRead(path, extra);
|
|
65
|
+
}
|
|
66
|
+
/** Current-chat source: integrity-checked by the bridge, with its real path. */
|
|
67
|
+
async readChatFile(path, extra) {
|
|
68
|
+
return this.openRead(path, extra, true);
|
|
69
|
+
}
|
|
70
|
+
async openRead(path, extra, chat = false) {
|
|
62
71
|
const call = String(extra.requestId), handle = randomUUID();
|
|
63
|
-
const meta = await this.rpc(call, 'read-open', { path, handle }, extra.signal);
|
|
72
|
+
const meta = await this.rpc(call, chat ? 'export-open' : 'read-open', { path, handle }, extra.signal);
|
|
64
73
|
if (!meta ||
|
|
65
74
|
typeof meta.filename !== 'string' ||
|
|
66
75
|
!Number.isSafeInteger(meta.size) ||
|
|
67
|
-
meta.size < 0
|
|
76
|
+
meta.size < 0 ||
|
|
77
|
+
(chat && typeof meta.path !== 'string'))
|
|
68
78
|
throw Error('INVALID_FILE_METADATA');
|
|
79
|
+
let closed = false;
|
|
69
80
|
const close = async () => {
|
|
81
|
+
if (closed)
|
|
82
|
+
return;
|
|
83
|
+
closed = true;
|
|
70
84
|
await this.rpc(call, 'close', { handle }, new AbortController().signal).catch(() => { });
|
|
71
85
|
};
|
|
72
86
|
const body = new ReadableStream({
|
|
@@ -91,7 +105,7 @@ export class FileCallbacks {
|
|
|
91
105
|
},
|
|
92
106
|
cancel: close,
|
|
93
107
|
});
|
|
94
|
-
return { filename: meta.filename, size: meta.size, body, close };
|
|
108
|
+
return { filename: meta.filename, size: meta.size, body, close, ...(chat ? { path: meta.path } : {}) };
|
|
95
109
|
}
|
|
96
110
|
async write(path, body, extra) {
|
|
97
111
|
const call = String(extra.requestId), handle = randomUUID();
|
|
@@ -13,6 +13,7 @@ export function managedChildEnvironment(input) {
|
|
|
13
13
|
}
|
|
14
14
|
/** Replace ours only; retain the user's ordinary harness configuration and plugins. */
|
|
15
15
|
export function prepareManagedHarness(role, stateDir, cwd, descriptor, env) {
|
|
16
|
+
const child = managedChildEnvironment({ ...process.env, ...env });
|
|
16
17
|
const bridge = fileURLToPath(new URL('./bridge.js', import.meta.url));
|
|
17
18
|
const server = {
|
|
18
19
|
name: 'ours',
|
|
@@ -20,21 +21,28 @@ export function prepareManagedHarness(role, stateDir, cwd, descriptor, env) {
|
|
|
20
21
|
args: [bridge],
|
|
21
22
|
env: [{ name: 'FLEET_OURS_BRIDGE_DESCRIPTOR', value: descriptor }],
|
|
22
23
|
};
|
|
24
|
+
// Portable allow covers bound identity messaging, not all tools on the server.
|
|
25
|
+
const tools = role.harness === 'codex' && role.permissions?.approval === 'allow'
|
|
26
|
+
? Object.fromEntries([
|
|
27
|
+
'current_identity', 'get_messages', 'list_history', 'list_contacts', 'send_message',
|
|
28
|
+
].map(name => [name, { approval_mode: 'approve' }]))
|
|
29
|
+
: undefined;
|
|
23
30
|
const native = {
|
|
24
31
|
mcp_servers: {
|
|
25
32
|
ours: {
|
|
26
33
|
command: process.execPath,
|
|
27
34
|
args: [bridge],
|
|
28
35
|
env: { FLEET_OURS_BRIDGE_DESCRIPTOR: descriptor },
|
|
36
|
+
...(tools ? { tools } : {}),
|
|
29
37
|
},
|
|
30
38
|
},
|
|
31
39
|
plugins: { 'ours@ours-codex-marketplace': { enabled: false } },
|
|
32
40
|
};
|
|
33
|
-
const child = managedChildEnvironment({ ...process.env, ...env });
|
|
34
41
|
if (role.harness === 'codex') {
|
|
35
42
|
const config = child.CODEX_CONFIG ? JSON.parse(child.CODEX_CONFIG) : {};
|
|
36
43
|
child.CODEX_CONFIG = JSON.stringify({
|
|
37
44
|
...config,
|
|
45
|
+
mcp_servers: { ...config.mcp_servers, ...native.mcp_servers },
|
|
38
46
|
plugins: { ...config.plugins, ...native.plugins },
|
|
39
47
|
});
|
|
40
48
|
// codex-acp otherwise prefers a same-name connector from the user's config.
|
|
@@ -1,5 +1,8 @@
|
|
|
1
|
+
import type { FileDeliveryInput, DeliveredFile } from '../file-delivery/types.js';
|
|
2
|
+
import type { ToolRequestExtra } from '@ours.network/mcp/dist/types/mcp/tool.js';
|
|
1
3
|
import type { ApplicationIdentityStore } from '@ours.network/mcp/application-identities';
|
|
2
4
|
import type { OursClient } from '@ours.network/sdk/client';
|
|
5
|
+
import { FileCallbacks } from './file-rpc.js';
|
|
3
6
|
import type { AgentOursRuntime } from './runtime.js';
|
|
4
7
|
export interface EndpointOptions {
|
|
5
8
|
socket: string;
|
|
@@ -9,6 +12,7 @@ export interface EndpointOptions {
|
|
|
9
12
|
client: OursClient;
|
|
10
13
|
identities: ApplicationIdentityStore;
|
|
11
14
|
remoteDaemonFiles: boolean;
|
|
15
|
+
currentChatFile?: (input: FileDeliveryInput, extra: ToolRequestExtra, files: FileCallbacks) => Promise<DeliveredFile>;
|
|
12
16
|
}
|
|
13
17
|
export declare function startMcpEndpoint(options: EndpointOptions): Promise<{
|
|
14
18
|
close(): Promise<void>;
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import * as managedMcp from '@ours.network/mcp/server';
|
|
1
2
|
import { createServer } from 'node:net';
|
|
2
3
|
import { chmod, unlink } from 'node:fs/promises';
|
|
3
4
|
import { timingSafeEqual } from 'node:crypto';
|
|
@@ -6,6 +7,9 @@ import { JSONRPCMessageSchema } from '@modelcontextprotocol/sdk/types.js';
|
|
|
6
7
|
import { FileCallbacks } from './file-rpc.js';
|
|
7
8
|
import { Wire } from './wire.js';
|
|
8
9
|
export async function startMcpEndpoint(options) {
|
|
10
|
+
// Every managed Agent offers current-chat delivery, so a package without the contract is a broken install, not a quiet downgrade.
|
|
11
|
+
if (options.currentChatFile && managedMcp.MANAGED_FILE_DELIVERY_VERSION !== 1)
|
|
12
|
+
throw Error('INCOMPATIBLE_MANAGED_FILE_DELIVERY: installed @ours.network/mcp lacks MANAGED_FILE_DELIVERY_VERSION 1; reinstall Fleet with its pinned @ours.network/mcp');
|
|
9
13
|
const sockets = new Set();
|
|
10
14
|
const server = createServer((socket) => {
|
|
11
15
|
if (sockets.size >= 4) {
|
|
@@ -33,6 +37,7 @@ export async function startMcpEndpoint(options) {
|
|
|
33
37
|
};
|
|
34
38
|
const mcp = createManagedOursMcpServer(options.client, 'managed-v1', options.identities, {
|
|
35
39
|
remoteDaemonFiles: options.remoteDaemonFiles,
|
|
40
|
+
...(options.currentChatFile ? { currentChatFile: (input, extra) => options.currentChatFile(input, extra, files) } : {}),
|
|
36
41
|
fileContext: files,
|
|
37
42
|
admit: async () => {
|
|
38
43
|
if (!authenticated || inFlight >= 32)
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { AgentHistoryQuery } from './correspondence.js';
|
|
1
2
|
import type { OursClient } from '@ours.network/sdk/client';
|
|
2
3
|
import { RuntimeJournal, type RuntimeState } from './state.js';
|
|
3
4
|
export interface AssignedIdentity {
|
|
@@ -9,7 +10,9 @@ export interface AssignedIdentity {
|
|
|
9
10
|
action: string;
|
|
10
11
|
expectedCid?: string;
|
|
11
12
|
allowCreate: boolean;
|
|
12
|
-
|
|
13
|
+
/** Omitted fields preserve an existing identity's custom profile. */
|
|
14
|
+
bio?: string;
|
|
15
|
+
persona?: string;
|
|
13
16
|
}
|
|
14
17
|
export interface RoomAdmission {
|
|
15
18
|
id: string;
|
|
@@ -47,6 +50,11 @@ export declare class AgentOursRuntime {
|
|
|
47
50
|
prepare(room?: RoomAdmission, timeoutMs?: number): Promise<void>;
|
|
48
51
|
/** Admission covers SDK calls, response rendering and agent filesystem callbacks. */
|
|
49
52
|
admit(): Promise<() => void>;
|
|
53
|
+
/** Trusted supervisor-only admission for a later room; keeps the live harness
|
|
54
|
+
* and original startup assignment. Not exposed as an agent lifecycle tool. */
|
|
55
|
+
joinAdditionalRoom(invite: string, expectedRoomCid: string): Promise<void>;
|
|
56
|
+
readContacts(): Promise<unknown>;
|
|
57
|
+
readHistory(request: AgentHistoryQuery): Promise<unknown>;
|
|
50
58
|
startHarness<T>(start: () => Promise<T>): Promise<T>;
|
|
51
59
|
suspend(): Promise<void>;
|
|
52
60
|
terminal(): Promise<void>;
|
|
@@ -62,6 +62,7 @@ export class AgentOursRuntime {
|
|
|
62
62
|
actual.isRoot)
|
|
63
63
|
throw Error('IDENTITY_PROOF_MISMATCH');
|
|
64
64
|
this.deps.assertFence();
|
|
65
|
+
return actual;
|
|
65
66
|
}
|
|
66
67
|
async prepare(room, timeoutMs = 30_000) {
|
|
67
68
|
if (['TERMINAL_INTENT', 'CLEANUP_PENDING', 'RELEASED', 'FAILED', 'QUIESCING'].includes(this.state.phase))
|
|
@@ -92,7 +93,7 @@ export class AgentOursRuntime {
|
|
|
92
93
|
this.transition('RECOVERING');
|
|
93
94
|
const args = {
|
|
94
95
|
name: this.state.name,
|
|
95
|
-
bio: this.assignment.bio,
|
|
96
|
+
bio: this.assignment.bio ?? '',
|
|
96
97
|
exposeLocal: false,
|
|
97
98
|
localAutoAccept: true,
|
|
98
99
|
};
|
|
@@ -104,7 +105,18 @@ export class AgentOursRuntime {
|
|
|
104
105
|
this.transition('OWNED', { cid: created.info.cid });
|
|
105
106
|
}
|
|
106
107
|
}
|
|
107
|
-
await this.verify();
|
|
108
|
+
const profile = await this.verify();
|
|
109
|
+
// The supervisor owns profile setup, just like identity binding. Apply only
|
|
110
|
+
// explicitly configured fields, under the verified owner, before publishing
|
|
111
|
+
// readiness or admitting the harness. Repeating a prepare is idempotent.
|
|
112
|
+
if (this.assignment.bio !== undefined && profile.bio !== this.assignment.bio) {
|
|
113
|
+
this.deps.assertFence();
|
|
114
|
+
await this.deps.client.setBio({ bio: this.assignment.bio });
|
|
115
|
+
}
|
|
116
|
+
if (this.assignment.persona !== undefined && profile.persona !== this.assignment.persona) {
|
|
117
|
+
this.deps.assertFence();
|
|
118
|
+
await this.deps.client.setPersona({ persona: this.assignment.persona });
|
|
119
|
+
}
|
|
108
120
|
if (!room && (this.state.room || this.state.admissionIntent))
|
|
109
121
|
throw Error('ROOM_DESCRIPTOR_REQUIRED');
|
|
110
122
|
if (room) {
|
|
@@ -201,6 +213,42 @@ export class AgentOursRuntime {
|
|
|
201
213
|
throw error;
|
|
202
214
|
}
|
|
203
215
|
}
|
|
216
|
+
/** Trusted supervisor-only admission for a later room; keeps the live harness
|
|
217
|
+
* and original startup assignment. Not exposed as an agent lifecycle tool. */
|
|
218
|
+
async joinAdditionalRoom(invite, expectedRoomCid) {
|
|
219
|
+
const release = await this.admit();
|
|
220
|
+
try {
|
|
221
|
+
const contact = await this.deps.client.addContact({ invite });
|
|
222
|
+
if (contact.cid !== expectedRoomCid)
|
|
223
|
+
throw Error('ADDITIONAL_ROOM_CID_MISMATCH');
|
|
224
|
+
await this.verify();
|
|
225
|
+
}
|
|
226
|
+
finally {
|
|
227
|
+
release();
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
async readContacts() {
|
|
231
|
+
const release = await this.admit();
|
|
232
|
+
try {
|
|
233
|
+
const data = await this.deps.client.listContacts();
|
|
234
|
+
await this.verify();
|
|
235
|
+
return { identity: { name: this.state.name, cid: this.state.cid, generation: this.state.generation }, ...data };
|
|
236
|
+
}
|
|
237
|
+
finally {
|
|
238
|
+
release();
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
async readHistory(request) {
|
|
242
|
+
const release = await this.admit();
|
|
243
|
+
try {
|
|
244
|
+
const data = await this.deps.client.listHistory(request);
|
|
245
|
+
await this.verify();
|
|
246
|
+
return { identity: { name: this.state.name, cid: this.state.cid, generation: this.state.generation }, ...data };
|
|
247
|
+
}
|
|
248
|
+
finally {
|
|
249
|
+
release();
|
|
250
|
+
}
|
|
251
|
+
}
|
|
204
252
|
async startHarness(start) {
|
|
205
253
|
if (!this.accepting || this.state.phase !== 'READY')
|
|
206
254
|
throw Error('RUNTIME_NOT_READY');
|
|
@@ -1,12 +1,29 @@
|
|
|
1
|
+
import type { AgentSession } from '../session/types.js';
|
|
2
|
+
import { ArtifactStore } from '../file-delivery/store.js';
|
|
1
3
|
import type { ResolvedRole } from '../config.js';
|
|
2
4
|
import { AgentOursRuntime } from './runtime.js';
|
|
5
|
+
import { startMcpEndpoint } from './mcp-endpoint.js';
|
|
3
6
|
export declare const privateRuntimeRoot: () => string;
|
|
4
7
|
export interface ManagedAgentService {
|
|
5
8
|
runtime: AgentOursRuntime;
|
|
6
9
|
descriptor: string;
|
|
7
10
|
privatePaths: string[];
|
|
11
|
+
setFileDelivery?(sender: AgentSession['sendFileToChat']): void;
|
|
8
12
|
close(terminal: boolean): Promise<void>;
|
|
9
13
|
}
|
|
14
|
+
/**
|
|
15
|
+
* `send_file` without a contact, offered to every managed Agent with no Agent
|
|
16
|
+
* setting and no export directory. A backend without a tracked ACP turn gets a
|
|
17
|
+
* precise refusal instead of a missing tool.
|
|
18
|
+
*/
|
|
19
|
+
export declare function currentChatFileHandler(options: {
|
|
20
|
+
role: Pick<ResolvedRole, 'harness' | 'session' | 'permissions'>;
|
|
21
|
+
cwd: string;
|
|
22
|
+
store: Pick<ArtifactStore, 'copy'>;
|
|
23
|
+
sender: () => AgentSession['sendFileToChat'];
|
|
24
|
+
/** Fleet's own credentials and stored attachments are never chat content. */
|
|
25
|
+
privateState: string[];
|
|
26
|
+
}): NonNullable<Parameters<typeof startMcpEndpoint>[0]['currentChatFile']>;
|
|
10
27
|
/** Only trusted launch orchestration may write this descriptor, never the child. */
|
|
11
28
|
export declare function storeRoomSecret(role: ResolvedRole): void;
|
|
12
29
|
export declare function storeTemporaryLaunch(role: ResolvedRole, action: string): void;
|
|
@@ -20,4 +37,7 @@ export declare function readRoomReadiness(roomCid: string, name: string): {
|
|
|
20
37
|
generation: number;
|
|
21
38
|
} | undefined;
|
|
22
39
|
/** Explicit logical-instance termination, independent of harness/bridge transport. */
|
|
23
|
-
export declare function releaseManagedAgent(role: ResolvedRole
|
|
40
|
+
export declare function releaseManagedAgent(role: ResolvedRole, expected?: {
|
|
41
|
+
cid: string;
|
|
42
|
+
action: string;
|
|
43
|
+
}): Promise<void>;
|
|
@@ -1,8 +1,13 @@
|
|
|
1
|
+
import { chatFilePath } from '../file-delivery/reader.js';
|
|
2
|
+
import { ArtifactStore } from '../file-delivery/store.js';
|
|
3
|
+
import { resolve, basename, extname } from 'node:path';
|
|
4
|
+
import { socketPath as privateSocketPath } from '../socket-path.js';
|
|
1
5
|
import { randomBytes, randomUUID } from 'node:crypto';
|
|
2
|
-
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
6
|
+
import { existsSync, lstatSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
3
7
|
import { join } from 'node:path';
|
|
4
8
|
import { attachOursClient } from '@ours.network/sdk/client';
|
|
5
9
|
import { ApplicationIdentityStore } from '@ours.network/mcp/application-identities';
|
|
10
|
+
import { assertSafeAncestors } from '../rooms-tasks/workspace.js';
|
|
6
11
|
import { readClientProfile } from '../client-profile.js';
|
|
7
12
|
import { stateRoot } from '../paths.js';
|
|
8
13
|
import { createCoworkAdapter } from '../rooms-tasks/cowork-adapter.js';
|
|
@@ -11,6 +16,42 @@ import { atomicPrivateWrite, binderKey } from './state.js';
|
|
|
11
16
|
import { AgentOursRuntime } from './runtime.js';
|
|
12
17
|
import { startMcpEndpoint } from './mcp-endpoint.js';
|
|
13
18
|
export const privateRuntimeRoot = () => join(stateRoot(), 'private-ours');
|
|
19
|
+
/**
|
|
20
|
+
* `send_file` without a contact, offered to every managed Agent with no Agent
|
|
21
|
+
* setting and no export directory. A backend without a tracked ACP turn gets a
|
|
22
|
+
* precise refusal instead of a missing tool.
|
|
23
|
+
*/
|
|
24
|
+
export function currentChatFileHandler(options) {
|
|
25
|
+
const { role, cwd, store } = options, privateState = options.privateState.map(p => resolve(p));
|
|
26
|
+
const isPrivate = (path) => privateState.some(p => path === p || path.startsWith(p + '/'));
|
|
27
|
+
return async (input, extra, files) => {
|
|
28
|
+
if (role.session !== 'acp' || !['codex', 'claude-code'].includes(role.harness))
|
|
29
|
+
throw Error(`CURRENT_CHAT_DELIVERY_UNAVAILABLE: sending a file to this chat requires a Codex or Claude ACP session; this Agent runs ${role.harness}/${role.session}. Pass contact to send through Ours instead`);
|
|
30
|
+
const sender = options.sender();
|
|
31
|
+
if (!sender)
|
|
32
|
+
throw Error('CURRENT_CHAT_DELIVERY_UNAVAILABLE: this session has no ACP chat to attach to');
|
|
33
|
+
if (isPrivate(chatFilePath(cwd, input.path)))
|
|
34
|
+
throw Error('PRIVATE_STATE_CANNOT_BE_SENT'); // before approval/read
|
|
35
|
+
const name = input.filename ?? basename(input.path);
|
|
36
|
+
if (!name || Buffer.byteLength(name) > 240 || /[\/\\\x00-\x1f]/.test(name) || name === '.' || name === '..')
|
|
37
|
+
throw Error('INVALID_FILENAME');
|
|
38
|
+
const mime = input.mime ?? { '.txt': 'text/plain', '.md': 'text/markdown', '.pdf': 'application/pdf', '.json': 'application/json', '.html': 'text/html', '.png': 'image/png' }[extname(name).toLowerCase()] ?? 'application/octet-stream';
|
|
39
|
+
if (mime.length > 200 || !/^[A-Za-z0-9.+-]+\/[A-Za-z0-9.+-]+$/.test(mime))
|
|
40
|
+
throw Error('INVALID_MIME');
|
|
41
|
+
return sender(input, extra.signal, async (binding) => {
|
|
42
|
+
const source = await files.readChatFile(input.path, extra);
|
|
43
|
+
try {
|
|
44
|
+
// The bridge reports the opened file's real path, so a symlinked ancestor cannot reach private state.
|
|
45
|
+
if (isPrivate(source.path))
|
|
46
|
+
throw Error('PRIVATE_STATE_CANNOT_BE_SENT');
|
|
47
|
+
return await store.copy(source, name, mime, binding, extra.signal);
|
|
48
|
+
}
|
|
49
|
+
finally {
|
|
50
|
+
await source.close();
|
|
51
|
+
}
|
|
52
|
+
}, role.permissions);
|
|
53
|
+
};
|
|
54
|
+
}
|
|
14
55
|
function roomSecretPath(role) {
|
|
15
56
|
const startup = role.roomMemberStartup;
|
|
16
57
|
return join(privateRuntimeRoot(), 'room-inputs', binderKey(startup.room_identity_cid, JSON.stringify([role.name, startup.invite_id])) + '.json');
|
|
@@ -93,6 +134,7 @@ export async function prepareManagedAgent(role, stateDir, temporary) {
|
|
|
93
134
|
}
|
|
94
135
|
const controller = await RuntimeController.acquire(root, profile.expectedInstanceId, role.identity, instance);
|
|
95
136
|
let closeClient;
|
|
137
|
+
let fileSender;
|
|
96
138
|
let partialEndpoint;
|
|
97
139
|
try {
|
|
98
140
|
let prior = controller.journal.read();
|
|
@@ -145,7 +187,8 @@ export async function prepareManagedAgent(role, stateDir, temporary) {
|
|
|
145
187
|
action: launch?.action ?? prior?.action ?? randomUUID(),
|
|
146
188
|
expectedCid: pin?.cid,
|
|
147
189
|
allowCreate: true,
|
|
148
|
-
bio: role.bio
|
|
190
|
+
bio: role.bio,
|
|
191
|
+
persona: role.persona,
|
|
149
192
|
}, {
|
|
150
193
|
client,
|
|
151
194
|
journal: controller.journal,
|
|
@@ -207,7 +250,8 @@ export async function prepareManagedAgent(role, stateDir, temporary) {
|
|
|
207
250
|
await identities.add(role.identity);
|
|
208
251
|
const bridgeDir = join(stateDir, '.ours-bridge');
|
|
209
252
|
mkdirSync(bridgeDir, { recursive: true, mode: 0o700 });
|
|
210
|
-
const capability = randomBytes(32).toString('hex'), socket = join(bridgeDir, `g${generation}.sock`);
|
|
253
|
+
const capability = randomBytes(32).toString('hex'), socket = privateSocketPath(join(bridgeDir, `g${generation}.sock`));
|
|
254
|
+
const cwd = role.cwd && existsSync(role.cwd) ? resolve(role.cwd) : resolve(stateDir);
|
|
211
255
|
const endpoint = await startMcpEndpoint({
|
|
212
256
|
socket,
|
|
213
257
|
capability,
|
|
@@ -216,16 +260,22 @@ export async function prepareManagedAgent(role, stateDir, temporary) {
|
|
|
216
260
|
client,
|
|
217
261
|
identities,
|
|
218
262
|
remoteDaemonFiles: true,
|
|
263
|
+
currentChatFile: currentChatFileHandler({
|
|
264
|
+
role, cwd, store: new ArtifactStore(stateDir), sender: () => fileSender,
|
|
265
|
+
privateState: [privateRuntimeRoot(), join(stateRoot(), 'private-file-delivery'), profile.configPath, profile.credentialPath],
|
|
266
|
+
}),
|
|
219
267
|
});
|
|
220
268
|
partialEndpoint = endpoint;
|
|
221
269
|
const descriptor = join(bridgeDir, 'descriptor.json');
|
|
222
|
-
atomicPrivateWrite(descriptor, { socket, capability, generation });
|
|
270
|
+
atomicPrivateWrite(descriptor, { socket, capability, generation, role: role.name, identity: role.identity, cid: runtime.snapshot.cid });
|
|
223
271
|
return {
|
|
224
272
|
runtime,
|
|
225
273
|
descriptor,
|
|
226
|
-
privatePaths: [root, profile.configPath, profile.credentialPath],
|
|
274
|
+
privatePaths: [root, profile.configPath, profile.credentialPath, join(stateRoot(), 'private-file-delivery')],
|
|
275
|
+
setFileDelivery: sender => { fileSender = sender; },
|
|
227
276
|
close: async (terminal) => {
|
|
228
277
|
try {
|
|
278
|
+
fileSender = undefined;
|
|
229
279
|
await endpoint.close();
|
|
230
280
|
if (terminal)
|
|
231
281
|
await runtime.terminal();
|
|
@@ -267,8 +317,10 @@ export async function preparePermanentAssignment(role) {
|
|
|
267
317
|
endpoint: profile.endpoint,
|
|
268
318
|
expectedInstanceId: profile.expectedInstanceId,
|
|
269
319
|
credentialPath: profile.credentialPath,
|
|
270
|
-
sessionMode: '
|
|
271
|
-
|
|
320
|
+
sessionMode: 'external',
|
|
321
|
+
leaseToken: randomUUID(),
|
|
322
|
+
requiredCapabilities: ['external-sessions-v1', 'root-first-identities-v1'],
|
|
323
|
+
env: {},
|
|
272
324
|
});
|
|
273
325
|
try {
|
|
274
326
|
const rows = await client.listIdentities(), row = rows.find((r) => r.name === role.identity);
|
|
@@ -295,7 +347,12 @@ export async function preparePermanentAssignment(role) {
|
|
|
295
347
|
return 'verified';
|
|
296
348
|
}
|
|
297
349
|
finally {
|
|
298
|
-
|
|
350
|
+
try {
|
|
351
|
+
await client.releaseLease();
|
|
352
|
+
}
|
|
353
|
+
finally {
|
|
354
|
+
await client.close();
|
|
355
|
+
}
|
|
299
356
|
}
|
|
300
357
|
}
|
|
301
358
|
export function readRoomReadiness(roomCid, name) {
|
|
@@ -309,24 +366,47 @@ export function readRoomReadiness(roomCid, name) {
|
|
|
309
366
|
}
|
|
310
367
|
}
|
|
311
368
|
/** Explicit logical-instance termination, independent of harness/bridge transport. */
|
|
312
|
-
export async function releaseManagedAgent(role) {
|
|
369
|
+
export async function releaseManagedAgent(role, expected) {
|
|
313
370
|
const profile = readClientProfile({ ...process.env, ...role.env });
|
|
314
371
|
if (!profile)
|
|
315
372
|
throw Error('MANAGED_OURS_REQUIRES_EXPLICIT_DAEMON_PROFILE');
|
|
316
373
|
const root = privateRuntimeRoot(), dir = join(root, binderKey(profile.expectedInstanceId, role.identity));
|
|
317
|
-
if (!existsSync(join(dir, 'instance.json')))
|
|
374
|
+
if (!existsSync(join(dir, 'instance.json'))) {
|
|
375
|
+
if (expected)
|
|
376
|
+
throw Error('MEMBER_RUNTIME_RETIREMENT_MISSING');
|
|
318
377
|
return;
|
|
319
|
-
|
|
378
|
+
}
|
|
379
|
+
const read = (file) => {
|
|
380
|
+
const path = join(dir, file);
|
|
381
|
+
if (expected) {
|
|
382
|
+
assertSafeAncestors(dir);
|
|
383
|
+
const stat = lstatSync(path);
|
|
384
|
+
if (!stat.isFile() || stat.isSymbolicLink() || stat.size > 256 * 1024)
|
|
385
|
+
throw Error('MEMBER_RUNTIME_UNSAFE_PROOF');
|
|
386
|
+
}
|
|
387
|
+
return JSON.parse(readFileSync(path, 'utf8'));
|
|
388
|
+
};
|
|
389
|
+
const instance = read('instance.json');
|
|
390
|
+
if (expected) {
|
|
391
|
+
read('state.json');
|
|
392
|
+
read('owner.json');
|
|
393
|
+
}
|
|
320
394
|
if (instance.role !== role.name)
|
|
321
395
|
throw Error('IDENTITY_ASSIGNED_TO_OTHER_ROLE');
|
|
322
396
|
const controller = await RuntimeController.acquire(root, profile.expectedInstanceId, role.identity, instance.instance);
|
|
323
397
|
try {
|
|
324
398
|
const state = controller.journal.read();
|
|
325
|
-
if (!state || state.
|
|
399
|
+
if (expected && (!state || state.name !== role.identity || state.daemon !== profile.expectedInstanceId
|
|
400
|
+
|| state.instance !== instance.instance || state.lifetime !== 'temporary' || instance.temporary !== true
|
|
401
|
+
|| state.cid !== expected.cid || state.action !== expected.action))
|
|
402
|
+
throw Error('MEMBER_RUNTIME_RETIREMENT_MISMATCH');
|
|
403
|
+
if (!state)
|
|
326
404
|
return;
|
|
327
|
-
const owner =
|
|
328
|
-
if (owner.instance !== state.instance)
|
|
405
|
+
const owner = read('owner.json');
|
|
406
|
+
if (owner.instance !== state.instance || typeof owner.token !== 'string' || !owner.token)
|
|
329
407
|
throw Error('OWNER_RECORD_MISMATCH');
|
|
408
|
+
if (state.phase === 'RELEASED')
|
|
409
|
+
return;
|
|
330
410
|
const client = await attachOursClient({
|
|
331
411
|
endpoint: profile.endpoint,
|
|
332
412
|
expectedInstanceId: profile.expectedInstanceId,
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { type OfferedModels } from '../subscriptions/cli.js';
|
|
2
|
+
export interface CodexModelDiscoveryDeps {
|
|
3
|
+
/** The Codex executable ACP sessions will run, and its version. */
|
|
4
|
+
runtime(): Promise<{
|
|
5
|
+
executable: string;
|
|
6
|
+
version: string;
|
|
7
|
+
}>;
|
|
8
|
+
ask(executable: string, env: NodeJS.ProcessEnv): Promise<OfferedModels>;
|
|
9
|
+
now(): number;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* The models offered to the active Codex account by the runtime that sessions will actually use.
|
|
13
|
+
* An answer is reused only for the same executable, version, profile and sign-in.
|
|
14
|
+
*/
|
|
15
|
+
export declare function createCodexModelDiscovery(overrides?: Partial<CodexModelDiscoveryDeps>): () => Promise<OfferedModels>;
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { statSync } from 'node:fs';
|
|
2
|
+
import { join } from 'node:path';
|
|
3
|
+
import { resolveBundledAcpAgent } from '../harness/acp-agent.js';
|
|
4
|
+
import { probeCodexRuntime } from '../harness/codex-runtime.js';
|
|
5
|
+
import { codexOfferedModels, profileEnv } from '../subscriptions/cli.js';
|
|
6
|
+
import { profileHome, readSubscriptionState } from '../subscriptions/store.js';
|
|
7
|
+
const KNOWN_FOR_MS = 5 * 60_000;
|
|
8
|
+
const UNKNOWN_FOR_MS = 10_000;
|
|
9
|
+
/** When the profile's sign-in last changed; a new sign-in must not reuse another account's answer. */
|
|
10
|
+
function signInStamp(home) {
|
|
11
|
+
try {
|
|
12
|
+
return statSync(join(home, 'auth.json')).mtimeMs;
|
|
13
|
+
}
|
|
14
|
+
catch {
|
|
15
|
+
return 0;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* The models offered to the active Codex account by the runtime that sessions will actually use.
|
|
20
|
+
* An answer is reused only for the same executable, version, profile and sign-in.
|
|
21
|
+
*/
|
|
22
|
+
export function createCodexModelDiscovery(overrides = {}) {
|
|
23
|
+
const deps = {
|
|
24
|
+
runtime: () => probeCodexRuntime(resolveBundledAcpAgent('@agentclientprotocol/codex-acp', 'codex-acp', 'codex-acp'), process.env),
|
|
25
|
+
ask: codexOfferedModels,
|
|
26
|
+
now: Date.now,
|
|
27
|
+
...overrides,
|
|
28
|
+
};
|
|
29
|
+
let cached;
|
|
30
|
+
let pending;
|
|
31
|
+
return async () => {
|
|
32
|
+
let key, executable, env;
|
|
33
|
+
try {
|
|
34
|
+
const profileId = readSubscriptionState().providers.codex.activeProfileId;
|
|
35
|
+
const profile = profileHome('codex', profileId);
|
|
36
|
+
const runtime = await deps.runtime();
|
|
37
|
+
executable = runtime.executable;
|
|
38
|
+
env = profileEnv('CODEX_HOME', profileId, profile);
|
|
39
|
+
key = [runtime.executable, runtime.version, profileId, signInStamp(profile)].join('\n');
|
|
40
|
+
}
|
|
41
|
+
catch (error) {
|
|
42
|
+
return { state: 'unknown', reason: error instanceof Error ? error.message : String(error) };
|
|
43
|
+
}
|
|
44
|
+
if (cached && cached.key === key && cached.until > deps.now())
|
|
45
|
+
return cached.value;
|
|
46
|
+
if (pending?.key === key)
|
|
47
|
+
return pending.answer;
|
|
48
|
+
const answer = deps.ask(executable, env).then(value => {
|
|
49
|
+
cached = { key, until: deps.now() + (value.state === 'known' ? KNOWN_FOR_MS : UNKNOWN_FOR_MS), value };
|
|
50
|
+
return value;
|
|
51
|
+
}).finally(() => { if (pending?.key === key)
|
|
52
|
+
pending = undefined; });
|
|
53
|
+
pending = { key, answer };
|
|
54
|
+
return answer;
|
|
55
|
+
};
|
|
56
|
+
}
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
import type { ConversationEventV1 } from '../session/conversation-types.js';
|
|
2
|
+
import type { ConversationPageView } from './session-control.js';
|
|
3
|
+
/** Whether a ledger event belongs in the conversation: the live generation, or history of the resumed ACP session. */
|
|
4
|
+
export declare function conversationEventVisible(event: ConversationEventV1, sessionGeneration: string, resumedSessionId?: string): boolean;
|
|
5
|
+
export interface ConversationTailPage extends ConversationPageView {
|
|
6
|
+
/** Pass as `before` to read the next older page. */
|
|
7
|
+
olderCursor?: string;
|
|
8
|
+
hasOlder: boolean;
|
|
9
|
+
/** Latest page only: older events the view still needs, the current session's latest capabilities and still-pending permission requests. */
|
|
10
|
+
context: ConversationEventV1[];
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* The newest `limit` visible events before `before` (or the newest overall),
|
|
14
|
+
* read backwards from the ledger so opening a long session costs one page.
|
|
15
|
+
* `nextCursor` is the newest ledger position seen, for forward `after` paging.
|
|
16
|
+
*/
|
|
17
|
+
export declare function conversationTailPage(stateDir: string, request: {
|
|
18
|
+
before?: string;
|
|
19
|
+
limit?: number;
|
|
20
|
+
}, snapshot: ConversationPageView['snapshot'], resumedSessionId?: string): Promise<ConversationTailPage>;
|
|
21
|
+
/** Read the existing ACP session ledger without opening its writer/recovery store. */
|
|
22
|
+
export declare function resumedConversationPage(stateDir: string, request: {
|
|
23
|
+
after?: string;
|
|
24
|
+
limit?: number;
|
|
25
|
+
}, live: ConversationPageView, expectedSessionId: string): Promise<ConversationPageView>;
|
|
26
|
+
/** Download authorization requires an actual published attachment, never an orphan copy. */
|
|
27
|
+
export declare function conversationHasAttachment(stateDir: string, attachment: import('../file-delivery/types.js').DeliveredFile): Promise<boolean>;
|