@ours.network/fleet 1.2.0-nightly.4 → 1.2.0-nightly.41
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 +165 -53
- package/dist/account-origin.d.ts +2 -0
- package/dist/account-origin.js +6 -0
- package/dist/agent-ours/bridge.js +9 -0
- package/dist/agent-ours/correspondence.d.ts +10 -0
- package/dist/agent-ours/correspondence.js +1 -0
- package/dist/agent-ours/harness.js +8 -0
- package/dist/agent-ours/runtime.d.ts +6 -0
- package/dist/agent-ours/runtime.js +36 -0
- package/dist/agent-ours/service.js +33 -13
- package/dist/application/codex-models.d.ts +15 -0
- package/dist/application/codex-models.js +56 -0
- package/dist/application/conversation-history.d.ts +25 -0
- package/dist/application/conversation-history.js +189 -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/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 +1 -1
- package/dist/application/role-repository.js +2 -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 +18 -0
- package/dist/application/session-control.js +54 -3
- package/dist/application/supervisor-ours-tools.d.ts +159 -0
- package/dist/application/supervisor-ours-tools.js +113 -0
- package/dist/application/task-layouts.d.ts +91 -0
- package/dist/application/task-layouts.js +249 -0
- package/dist/application/task-room-service.d.ts +99 -3
- package/dist/application/task-room-service.js +296 -31
- package/dist/atomic-file.d.ts +5 -0
- package/dist/atomic-file.js +62 -1
- package/dist/build-info.json +6 -4
- package/dist/capabilities.d.ts +3 -1
- package/dist/capabilities.js +4 -0
- package/dist/cli.js +186 -34
- package/dist/client-profile.d.ts +5 -16
- package/dist/client-profile.js +4 -112
- package/dist/config.d.ts +18 -0
- package/dist/config.js +28 -4
- package/dist/creation.js +2 -2
- package/dist/daemon-recovery.d.ts +1 -8
- package/dist/daemon-recovery.js +8 -113
- package/dist/docs.d.ts +3 -3
- package/dist/docs.js +44 -22
- package/dist/doctor.d.ts +1 -1
- package/dist/doctor.js +46 -59
- package/dist/erased-resources.d.ts +9 -0
- package/dist/erased-resources.js +87 -0
- package/dist/fleet-command-audit.d.ts +1 -0
- package/dist/fleet-command-audit.js +11 -6
- package/dist/harness/claude-code.js +9 -1
- 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-runtime.d.ts +11 -1
- package/dist/harness/codex-runtime.js +31 -4
- package/dist/harness/codex.d.ts +3 -2
- package/dist/harness/codex.js +8 -5
- package/dist/init-wizard.d.ts +2 -0
- package/dist/init-wizard.js +38 -19
- package/dist/isolation/policy.js +2 -0
- package/dist/isolation/types.d.ts +5 -0
- package/dist/minimal-setup.d.ts +2 -0
- package/dist/minimal-setup.js +44 -0
- package/dist/monitor.d.ts +1 -34
- package/dist/monitor.js +24 -151
- package/dist/notifications/fleet-producer.d.ts +18 -0
- package/dist/notifications/fleet-producer.js +129 -0
- package/dist/notifications/outbox.d.ts +42 -0
- package/dist/notifications/outbox.js +149 -0
- package/dist/owner-channel/channel.d.ts +3 -0
- package/dist/owner-channel/channel.js +34 -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/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 +34 -5
- package/dist/rooms-tasks/close.d.ts +5 -2
- package/dist/rooms-tasks/close.js +208 -28
- 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 +6 -4
- package/dist/rooms-tasks/cowork-adapter.js +11 -117
- package/dist/rooms-tasks/cowork-http.d.ts +3 -0
- package/dist/rooms-tasks/cowork-http.js +70 -0
- package/dist/rooms-tasks/deletion.d.ts +1 -1
- package/dist/rooms-tasks/deletion.js +74 -5
- package/dist/rooms-tasks/erasure.d.ts +6 -0
- package/dist/rooms-tasks/erasure.js +169 -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-service.d.ts +42 -0
- package/dist/rooms-tasks/layout-service.js +185 -0
- package/dist/rooms-tasks/layout.d.ts +114 -0
- package/dist/rooms-tasks/layout.js +250 -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-startup.d.ts +1 -0
- package/dist/rooms-tasks/member-startup.js +2 -0
- package/dist/rooms-tasks/provision.d.ts +14 -0
- package/dist/rooms-tasks/provision.js +173 -46
- package/dist/rooms-tasks/room-state.d.ts +4 -2
- package/dist/rooms-tasks/room-state.js +53 -8
- package/dist/rooms-tasks/task-state.d.ts +42 -7
- package/dist/rooms-tasks/task-state.js +153 -23
- package/dist/rooms-tasks/types.d.ts +28 -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 +26 -0
- package/dist/rooms-tasks/workspace.js +195 -0
- package/dist/runner.d.ts +19 -3
- package/dist/runner.js +86 -29
- package/dist/session/acp.d.ts +7 -1
- package/dist/session/acp.js +21 -6
- package/dist/session/arbiter.d.ts +3 -0
- package/dist/session/arbiter.js +20 -2
- package/dist/session/codex-app-server.d.ts +3 -0
- package/dist/session/codex-app-server.js +26 -3
- package/dist/session/control.d.ts +10 -1
- package/dist/session/control.js +37 -2
- package/dist/session/conversation-normalizer.d.ts +2 -1
- package/dist/session/conversation-normalizer.js +1 -1
- package/dist/session/conversation-types.d.ts +5 -3
- package/dist/session/task-notice.d.ts +14 -0
- package/dist/session/task-notice.js +13 -0
- package/dist/session/types.d.ts +3 -0
- package/dist/setup-tunnel-preflight.d.ts +32 -0
- package/dist/setup-tunnel-preflight.js +58 -0
- package/dist/socket-path.d.ts +2 -0
- package/dist/socket-path.js +22 -0
- package/dist/spawn.js +7 -0
- package/dist/startup-readiness.d.ts +13 -0
- package/dist/startup-readiness.js +38 -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/launchd.js +2 -1
- package/dist/supervisor/systemd.js +9 -23
- package/dist/temp-idle.d.ts +17 -0
- package/dist/temp-idle.js +42 -0
- package/dist/temp-lifecycle.d.ts +6 -1
- package/dist/temp-lifecycle.js +29 -13
- package/dist/web/agent-attachments.d.ts +29 -0
- package/dist/web/agent-attachments.js +162 -0
- package/dist/web/auth.d.ts +29 -1
- package/dist/web/auth.js +81 -7
- 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 +29 -0
- package/dist/web/prefix-gateway.js +206 -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 +501 -53
- package/dist/web/service.d.ts +11 -2
- package/dist/web/service.js +30 -4
- package/dist/web/workspace-devices.d.ts +29 -0
- package/dist/web/workspace-devices.js +84 -0
- package/dist/web/workspace-tunnel.d.ts +1 -0
- package/dist/web/workspace-tunnel.js +32 -0
- package/dist/workspace-enrollment.d.ts +53 -0
- package/dist/workspace-enrollment.js +370 -0
- package/docs/room-layouts.md +227 -0
- package/docs/task-workspaces.md +130 -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 +13 -18
- 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/README.md
CHANGED
|
@@ -79,7 +79,7 @@ The state dir contract:
|
|
|
79
79
|
|---|---|---|
|
|
80
80
|
| Node ≥ 22 | runs `ours-fleet` and its maintained adapters | nodejs.org, `apt`, or `brew` |
|
|
81
81
|
| a harness CLI, logged in | the agent itself | e.g. Claude Code (`claude`) or Codex CLI (`codex`) |
|
|
82
|
-
| `ours` CLI + shared daemon | identity + agent-to-agent messaging | `
|
|
82
|
+
| `ours` CLI + shared daemon | identity + agent-to-agent messaging | `ours-install client --config /absolute/private/profile.json --integrations fleet --fleet-settings /absolute/fleet-settings.json` |
|
|
83
83
|
|
|
84
84
|
Linux only: `ours-fleet init` enables *linger* so roles run without a login session
|
|
85
85
|
and survive reboots. macOS: launchd agents start **at login** (no linger
|
|
@@ -139,72 +139,55 @@ the caller's owner channel with the caller and spawned-role details. This is an
|
|
|
139
139
|
honest-actor convenience and attribution path, not a security boundary; host
|
|
140
140
|
shells and deliberately bypassed absolute binaries retain direct behavior.
|
|
141
141
|
|
|
142
|
-
## Local
|
|
142
|
+
## Local HTTP service
|
|
143
143
|
|
|
144
|
-
|
|
145
|
-
|
|
144
|
+
Fleet is an HTTP backend. It serves no pages, bundles no frontend and opens no
|
|
145
|
+
browser: every screen belongs to the App, which calls this API with a device
|
|
146
|
+
credential. Any address outside `/api/` answers JSON `404`.
|
|
146
147
|
|
|
147
148
|
```sh
|
|
148
|
-
|
|
149
|
-
ours-fleet
|
|
149
|
+
ours-fleet setup-tunnel # connect this host to your App workspace
|
|
150
|
+
ours-fleet link-device # connect another browser or device
|
|
150
151
|
# choose a free port for an isolated test:
|
|
151
|
-
ours-fleet web serve --port 0
|
|
152
|
-
# nginx/TLS terminates at the declared
|
|
152
|
+
ours-fleet web serve --port 0
|
|
153
|
+
# nginx/TLS terminates at the declared origin; fleet remains loopback-bound
|
|
153
154
|
ours-fleet web install --public-origin https://fleet.example.com --password-file /secure/fleet-password
|
|
154
|
-
# Intentional no-password mode, for example when nginx already authenticates:
|
|
155
|
-
ours-fleet web install --public-origin https://fleet.example.com --no-password
|
|
156
155
|
```
|
|
157
156
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
157
|
+
`ours-fleet web` installs or updates an owner-level native service and starts
|
|
158
|
+
it on stable `127.0.0.1:49271`. Linux uses a systemd user unit and macOS uses a
|
|
159
|
+
LaunchAgent; both restart after process failures and send logs to the native
|
|
160
|
+
supervisor. Service management is explicit through
|
|
161
|
+
`ours-fleet web install|start|stop|restart|status|uninstall`. Use
|
|
162
|
+
`ours-fleet web serve` for a foreground development process. `--no-open` is
|
|
163
|
+
still accepted because installed service units pass it; it has no effect. Linux
|
|
164
|
+
login persistence requires linger; the installer reports when it is missing but
|
|
165
165
|
never enables it or requests root privileges.
|
|
166
166
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
browser, or `ours-fleet web revoke-all` to revoke every trusted browser and
|
|
174
|
-
active web session.
|
|
175
|
-
|
|
176
|
-
Only a domain-separated SHA-256 device-secret hash and bounded timestamps are
|
|
177
|
-
stored in the owner-private fleet state directory (`0700` directory, `0600`
|
|
178
|
-
atomic file). The re-pair and revoke controls use an owner-private Unix socket;
|
|
179
|
-
local processes running as the same OS user are therefore inside the trust
|
|
180
|
-
boundary.
|
|
167
|
+
Use `ours-fleet web revoke-all` to revoke every linked device and active
|
|
168
|
+
session. Only a domain-separated SHA-256 device-secret hash and bounded
|
|
169
|
+
timestamps are stored in the owner-private fleet state directory (`0700`
|
|
170
|
+
directory, `0600` atomic file). The link and revoke controls use an
|
|
171
|
+
owner-private Unix socket; local processes running as the same OS user are
|
|
172
|
+
therefore inside the trust boundary.
|
|
181
173
|
|
|
182
174
|
On first setup, the CLI requires an explicit access choice: `--password-file`
|
|
183
175
|
or `--pairing` for protected access, or `--no-password` for intentional
|
|
184
176
|
unprotected access. `--password-file` stores only a salted scrypt verifier in
|
|
185
|
-
the owner-private web state; the source file remains operator-managed.
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
who can reach that origin can control the fleet.
|
|
177
|
+
the owner-private web state; the source file remains operator-managed.
|
|
178
|
+
`--no-password` is deliberately named and prints a warning: anyone who can
|
|
179
|
+
reach that origin can control the fleet.
|
|
189
180
|
|
|
190
181
|
For nginx on a VPS, keep the default loopback bind and set the exact external
|
|
191
182
|
`--public-origin` (scheme, hostname, optional port). nginx should proxy HTTP and
|
|
192
183
|
WebSocket upgrades to `127.0.0.1:49271` and provide TLS; rewriting the upstream
|
|
193
|
-
Host is not required because the declared
|
|
184
|
+
Host is not required because the declared Origin remains authoritative. To listen beyond
|
|
194
185
|
loopback, add an explicit `--bind`; fleet refuses a non-loopback bind without a
|
|
195
186
|
public origin. Host and Origin validation use that declaration rather than
|
|
196
187
|
trusting forwarded headers. `localhost` and `127.0.0.1` both work in normal
|
|
197
|
-
local mode; an unconfigured hostname
|
|
198
|
-
of raw internal Host-header JSON.
|
|
199
|
-
|
|
200
|
-
The console is installable as a standalone PWA. Its service worker caches only
|
|
201
|
-
the data-free offline page and successful content-hashed JavaScript/CSS assets.
|
|
202
|
-
HTML stays network-first and no API, authentication, bootstrap, device,
|
|
203
|
-
WebSocket, terminal, event, audit, log, session, query-bearing, or error response
|
|
204
|
-
is cached. When the local daemon is unavailable, the PWA shows an explicit
|
|
205
|
-
offline shell and no stale fleet state.
|
|
188
|
+
local mode; an unconfigured hostname is refused with a JSON error.
|
|
206
189
|
|
|
207
|
-
The
|
|
190
|
+
The API provides evidence-separated inventory and status, ACP activity and
|
|
208
191
|
permission controls, redacted logs, typed text send, confirmed lifecycle
|
|
209
192
|
actions, and transactional permanent/temporary creation. Identity is fixed to
|
|
210
193
|
the role name. Creation uses the authenticated
|
|
@@ -376,9 +359,9 @@ preserve at most one recent late occurrence. The trusted authoring file and
|
|
|
376
359
|
private sealed role snapshot retain exact prompt text; resolved launch, task,
|
|
377
360
|
room, provenance, and audit presentation surfaces show only bytes and SHA-256.
|
|
378
361
|
|
|
379
|
-
Every packaged `Developer`, `Critic`, and `LocalCoordinator` Agent Template uses
|
|
362
|
+
Every packaged `Developer`, `Engineer`, `Critic`, and `LocalCoordinator` Agent Template uses
|
|
380
363
|
`monitor.mode: fleet` with `monitor.interrupt: after_tool`. Consequently every
|
|
381
|
-
member of the standard `single`, `pair`, and `
|
|
364
|
+
member of the standard `single`, `pair`, `team`, and `engineering` Room Templates resolves to
|
|
382
365
|
that policy. A custom Agent Template or explicit per-member override remains
|
|
383
366
|
authoritative and is merged key by key without rewriting unrelated values.
|
|
384
367
|
|
|
@@ -524,7 +507,7 @@ ours-fleet up
|
|
|
524
507
|
|
|
525
508
|
### Rooms and tasks
|
|
526
509
|
|
|
527
|
-
Init installs editable `single`, `pair`, and `
|
|
510
|
+
Init installs editable `single`, `pair`, `team`, and `engineering` definitions under
|
|
528
511
|
`~/fleet/room_templates/`, plus every exact-cased Agent, Role, and Brain they
|
|
529
512
|
reference. Inspect them with `ours-fleet template list` and
|
|
530
513
|
`ours-fleet template show team`. After configuring the authenticated owner below:
|
|
@@ -533,8 +516,25 @@ reference. Inspect them with `ours-fleet template list` and
|
|
|
533
516
|
ours-fleet task create --title "Solo task" --template single
|
|
534
517
|
ours-fleet task create --title "Reviewed change" --template pair
|
|
535
518
|
ours-fleet task create --title "Phased delivery" --template team
|
|
519
|
+
ours-fleet task create --title "Gated feature build" --template engineering
|
|
536
520
|
```
|
|
537
521
|
|
|
522
|
+
`engineering` pairs a gate-pipeline `Engineer` with the standard `Critic`. The
|
|
523
|
+
Engineer works every non-trivial task through ordered gates — scope and a numbered
|
|
524
|
+
Definition of Done before code, an over-engineering check, spec before
|
|
525
|
+
implementation, test-first, review, a sized security pass, docs, and evidence-backed
|
|
526
|
+
DoD verification — invoking an installed gate-pipeline skill when one is present and
|
|
527
|
+
walking the same gates by hand otherwise. The seat is labelled `Engineer`, so a
|
|
528
|
+
gated seat is never mistaken for a plain `Developer`. To make it the default for
|
|
529
|
+
development tasks, set `tasks: { default_room_template: engineering }` in
|
|
530
|
+
`fleet.yaml`.
|
|
531
|
+
|
|
532
|
+
The `engineering` starter expresses a prompt-level workflow mandate; Fleet does not enforce
|
|
533
|
+
the gates at runtime or install pipeline skills. The Engineer label identifies that mandate,
|
|
534
|
+
not a completed review. When migrating an existing install, a newly added Engineer uses
|
|
535
|
+
the existing Developer template's brain selection and keeps the packaged restrictive permissions.
|
|
536
|
+
Existing customized Engineer files are preserved.
|
|
537
|
+
|
|
538
538
|
The default `team` gives only its LocalCoordinator Agent Template a 15-minute
|
|
539
539
|
continuity loop. Each idle-only pass uses authenticated assigned-room evidence,
|
|
540
540
|
posts at most one missing interval update or status request, and reports `STALLED`
|
|
@@ -588,8 +588,9 @@ non-owner-controlled parent fail closed before host setup and are rechecked unde
|
|
|
588
588
|
per-setup init lock before publication.
|
|
589
589
|
|
|
590
590
|
The generated task experiences are fixed consequences, not extra questions: `single`
|
|
591
|
-
uses `Developer`; `pair` uses `Developer` with an independent `Critic`;
|
|
592
|
-
task-local `LocalCoordinator`, `Developer`, and `Critic
|
|
591
|
+
uses `Developer`; `pair` uses `Developer` with an independent `Critic`; `team` uses
|
|
592
|
+
task-local `LocalCoordinator`, `Developer`, and `Critic`; and `engineering` uses the
|
|
593
|
+
gate-pipeline `Engineer` with an independent `Critic`. The persistent
|
|
593
594
|
`FleetCoordinator` uses the separate packaged `Coordinator` contract and coordination
|
|
594
595
|
model.
|
|
595
596
|
|
|
@@ -666,6 +667,29 @@ continuation is serialized per Task and remains alive until convergence or an
|
|
|
666
667
|
Owner-action blocker. Re-running `task start <id>` safely resumes the same durable
|
|
667
668
|
provisioning operation after the blocker is corrected or a process restarts.
|
|
668
669
|
|
|
670
|
+
Live health is distinct from durable lifecycle. `task show TASK --json` adds a
|
|
671
|
+
`readiness` observation for active tasks (`ready`, `degraded`, or `unknown`); human
|
|
672
|
+
and authenticated Owner task-show output display the same observation. Task start/work
|
|
673
|
+
continue to reject unavailable health with `task_not_ready`. A same-role untracked
|
|
674
|
+
seat or a changed recorded launch cannot satisfy an original slot. Each control,
|
|
675
|
+
liveness, and production Cowork probe is bounded; members are checked sequentially,
|
|
676
|
+
so total latency grows with room size. These are point-in-time observations, not
|
|
677
|
+
proof that a timed-out session is dead or permission to respawn it.
|
|
678
|
+
|
|
679
|
+
Before deliberate recovery, Fleet Coordinator must inspect existing room seats and
|
|
680
|
+
the exact old launch's termination record and worklog under `recovery/temporary`.
|
|
681
|
+
Keep original slot records and archives intact; record verified old/new identity
|
|
682
|
+
and launch provenance privately. Never restore archived identity state or reuse
|
|
683
|
+
consumed invites. A same-role seat is only a possible replacement, not proven ownership.
|
|
684
|
+
|
|
685
|
+
A replacement's empty inbox does not establish that no work is pending. Paginate
|
|
686
|
+
scoped room/operator history to the end, retain the cursor privately, authenticate
|
|
687
|
+
historical Owner instructions from identity metadata, and compare replies, worklogs,
|
|
688
|
+
and already-completed actions before resuming. Ask the Owner when authority or
|
|
689
|
+
completion is uncertain. Transfer only necessary non-secret context; never replay
|
|
690
|
+
instructions blindly. Observation does not adopt seats, launch replacements, change
|
|
691
|
+
lifecycle, or modify retained recovery evidence.
|
|
692
|
+
|
|
669
693
|
Set `room.anonymous: true` on a room template, or pass `--anonymous` to
|
|
670
694
|
`task create`, `task start`, `task work`, or `room create`, to create an
|
|
671
695
|
anonymous Cowork room. `--no-anonymous` explicitly overrides an anonymous
|
|
@@ -1348,8 +1372,9 @@ The command override is exact: Fleet does not append `--profile`, `--search`, or
|
|
|
1348
1372
|
|
|
1349
1373
|
One-off/permanent spawn selects a Brain that owns model, reasoning, native permission,
|
|
1350
1374
|
sandbox, profile, launcher, search, monitor consent, native config, and additional roots.
|
|
1351
|
-
|
|
1352
|
-
|
|
1375
|
+
All roles use the shared gateway profile `~/.ours-client/profile.json`.
|
|
1376
|
+
`OURS_CONFIG` selects an alternate whole profile. Legacy port/token/state-dir
|
|
1377
|
+
overrides are rejected. See the [gateway setup guide](https://github.com/adapt-toolkit/ours-network/blob/prerelease/packages/installer/GATEWAY_SETUP.md).
|
|
1353
1378
|
|
|
1354
1379
|
## Agent isolation
|
|
1355
1380
|
|
|
@@ -1694,3 +1719,90 @@ provisioning or automatic reverse-proxy configuration. Existing local HTTP and
|
|
|
1694
1719
|
SSH-tunnel profiles continue to work.
|
|
1695
1720
|
|
|
1696
1721
|
See [supervisor-owned ours and migration](docs/supervisor-ours.md) for the runtime contract and review build instructions.
|
|
1722
|
+
|
|
1723
|
+
### One-server gateway profile
|
|
1724
|
+
|
|
1725
|
+
A private client profile may include `serverUrl`, for example
|
|
1726
|
+
`https://ours.example/base`, together with `endpoint` set to
|
|
1727
|
+
`https://ours.example/base/daemon`, the existing `expectedInstanceId`, and the
|
|
1728
|
+
absolute private `credentialPath`. Fleet preserves that daemon prefix for doctor
|
|
1729
|
+
and monitoring and derives `/cowork/management/rpc` from `serverUrl` for room
|
|
1730
|
+
management. Requests use the issued server credential, reject redirects, have
|
|
1731
|
+
bounded bodies/deadlines, and never replay mutations. This capability is declared
|
|
1732
|
+
as `cowork.http-management-v1` in the built artifact.
|
|
1733
|
+
|
|
1734
|
+
Explicit Cowork socket, config or state-directory overrides continue to select
|
|
1735
|
+
local Unix management. Profiles without `serverUrl` retain legacy behavior. The
|
|
1736
|
+
HTTP API grants operator room authority; use the installer's supported private
|
|
1737
|
+
or authenticated external gateway entry, and keep its backend ports private.
|
|
1738
|
+
|
|
1739
|
+
### Agent ours tools through the supervisor
|
|
1740
|
+
|
|
1741
|
+
Operators can invoke the running agent's ordinary ours MCP tools without sending
|
|
1742
|
+
an LLM prompt or binding another session to its identity:
|
|
1743
|
+
|
|
1744
|
+
```sh
|
|
1745
|
+
ours-fleet ours tools Critic1
|
|
1746
|
+
ours-fleet ours call Critic1 current_identity
|
|
1747
|
+
ours-fleet ours call Critic1 list_contacts
|
|
1748
|
+
ours-fleet ours call Critic1 generate_invite --args-file /private/invite-options.json
|
|
1749
|
+
ours-fleet ours call Critic1 add_contact --args-file /private/contact.json
|
|
1750
|
+
```
|
|
1751
|
+
|
|
1752
|
+
`tools` returns the managed MCP tool names and argument schemas, plus the selected
|
|
1753
|
+
agent's identity name, CID, and supervisor generation. `call` returns the same
|
|
1754
|
+
identity metadata and the MCP result. The private JSON file contains the tool's
|
|
1755
|
+
arguments (for example `{"invite":"…"}` for `add_contact`); omitted arguments mean
|
|
1756
|
+
`{}`. Protect files containing invites and protect command output containing a
|
|
1757
|
+
new invite. Tool errors set a nonzero CLI exit status.
|
|
1758
|
+
|
|
1759
|
+
The authenticated REST equivalents are `GET /api/v1/roles/:id/ours/tools` and
|
|
1760
|
+
`POST /api/v1/roles/:id/ours/call`, with the normal Fleet session and CSRF token.
|
|
1761
|
+
The POST body is `{"tool":"list_contacts","arguments":{}}`. MCP tool errors
|
|
1762
|
+
remain in `result.isError`; transport failures use the normal Fleet error envelope.
|
|
1763
|
+
Request arguments and results are excluded from the Fleet audit log.
|
|
1764
|
+
|
|
1765
|
+
Both interfaces use the supervisor's existing fixed-identity MCP server and tool
|
|
1766
|
+
policy. Identity creation, removal, switching, and binding are not exposed. The
|
|
1767
|
+
supervisor must be running. Older descriptors are supported only when a unique
|
|
1768
|
+
existing runtime journal, instance record, and identity pin prove the selected
|
|
1769
|
+
agent and generation, and the same MCP connection confirms its identity and
|
|
1770
|
+
lifetime before the operation. This reads existing state without rewriting the
|
|
1771
|
+
descriptor or restarting the agent. Missing, ambiguous, stale, or mismatched
|
|
1772
|
+
proofs fail closed. Unknown identity descriptions also fail closed. Closing an operator call
|
|
1773
|
+
retains the supervisor's identity. An interrupted mutation can have an unknown
|
|
1774
|
+
outcome: inspect state before retrying, because there is no automatic retry or
|
|
1775
|
+
exactly-once guarantee. Contact acceptance alone does not prove peer verification
|
|
1776
|
+
or message delivery. File tools resolve paths in the invoking CLI process or
|
|
1777
|
+
Fleet web server's filesystem context, with that process's access permissions.
|
|
1778
|
+
|
|
1779
|
+
Task workspace ownership, retention, safe deletion and legacy migration are documented in [Task workspaces](docs/task-workspaces.md).
|
|
1780
|
+
|
|
1781
|
+
### Active task readiness after interruption
|
|
1782
|
+
|
|
1783
|
+
Task and room list states, and `taskProvisioningOutcome`, describe durable lifecycle
|
|
1784
|
+
and provisioning progress; they are not live health checks. Retrying `task start`
|
|
1785
|
+
or `task work` on an active task checks the current Cowork room identity and seats,
|
|
1786
|
+
member launch ownership, supervisor liveness, and authenticated session status
|
|
1787
|
+
before reporting success. Waiting for provisioning completion also checks live
|
|
1788
|
+
readiness. This is a point-in-time observation, not proof of an agent-authored reply.
|
|
1789
|
+
|
|
1790
|
+
A `task_not_ready` result distinguishes confirmed degradation from unknown health
|
|
1791
|
+
(for example, an unavailable control socket). Ask Fleet Coordinator to inspect room
|
|
1792
|
+
and member status and choose supported recovery. These checks do not restart agents,
|
|
1793
|
+
recreate identities, redeem invites, or rewrite lifecycle state. Temporary room agents
|
|
1794
|
+
remain transient across host reboot; retained launch records do not restore them.
|
|
1795
|
+
|
|
1796
|
+
See [predefined Brain models and runtime requirements](presets/README.md) for the verified model/effort matrix and regeneration instructions.
|
|
1797
|
+
|
|
1798
|
+
### YAML room layouts
|
|
1799
|
+
|
|
1800
|
+
Room layouts describe room composition with shared or fresh participant sessions and independent room closure.
|
|
1801
|
+
See [Room layouts](docs/room-layouts.md) for YAML examples and CLI commands.
|
|
1802
|
+
|
|
1803
|
+
|
|
1804
|
+
### Dedicated notification service
|
|
1805
|
+
|
|
1806
|
+
Configure the web backend with `OURS_NOTIFICATIONS_ORIGIN` and a Fleet-scoped `OURS_NOTIFICATIONS_PRODUCER_TOKEN`. It reads each local supervisor ledger incrementally with persistent checkpoints under web state, correlating owner browser/channel prompts with terminal completion. Monitor/startup turns do not notify. First installation skips existing history; no agent restart or conversation controller is needed. Persist web state across deployment/restarts. Reads are bounded to 256 KiB per role per tick, and oversized model/media records are skipped across chunks. Expose `/notifications` through the generic authenticated prefix gateway with a server-side user token and `stripBrowserContext: true`; producer tokens stay server-side. All browser sessions of this single-owner console map to one stable notification account. See [ours-notifications](https://github.com/adapt-toolkit/ours-notifications) for configuration/API/release details.
|
|
1807
|
+
|
|
1808
|
+
Existing-host enrollment: `ours-fleet workspace-enroll --file <private-file> --preserve-profile` requires Messenger to advertise preservation support before any enrollment POST. It omits Name/Surname and leaves existing/absent profile untouched. Existing Fleet models and agents are retained; the installer must skip init when its configuration exists. Signed root proof, tunnel setup and device QR issuance remain the normal enrollment flow. Older Messenger fails closed and requires its preservation-capable release.
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
export const accountOrigins = ['https://app.ours.network', 'https://app.ours-tunnel.com'];
|
|
2
|
+
export function validateAccountOrigin(value) {
|
|
3
|
+
if (typeof value !== 'string' || !accountOrigins.some(origin => origin === value))
|
|
4
|
+
throw Error('Unsupported account origin');
|
|
5
|
+
return value;
|
|
6
|
+
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import { createHash } from 'node:crypto';
|
|
2
3
|
import { connect } from 'node:net';
|
|
3
4
|
import { access, mkdir, open, readFile } from 'node:fs/promises';
|
|
4
5
|
import { constants } from 'node:fs';
|
|
@@ -12,6 +13,14 @@ export async function runBridge(descriptorPath) {
|
|
|
12
13
|
typeof descriptor.capability !== 'string' ||
|
|
13
14
|
!Number.isInteger(descriptor.generation))
|
|
14
15
|
throw Error('INVALID_BRIDGE_DESCRIPTOR');
|
|
16
|
+
if (process.env.FLEET_OURS_BRIDGE_EXPECTED) {
|
|
17
|
+
const expected = JSON.parse(process.env.FLEET_OURS_BRIDGE_EXPECTED);
|
|
18
|
+
const digest = createHash('sha256').update(JSON.stringify([descriptor.socket, descriptor.capability, descriptor.generation])).digest('hex');
|
|
19
|
+
if (expected.transportDigest !== digest)
|
|
20
|
+
throw Error('SUPERVISOR_TRANSPORT_CHANGED');
|
|
21
|
+
if (['role', 'identity', 'cid', 'generation'].some(key => descriptor[key] !== expected[key]))
|
|
22
|
+
throw Error('SUPERVISOR_SELECTION_CHANGED');
|
|
23
|
+
}
|
|
15
24
|
const wire = new Wire(connect(descriptor.socket));
|
|
16
25
|
const handles = new Map();
|
|
17
26
|
const cleanup = async () => {
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/** Read-only operations exposed by the identity-owning supervisor. */
|
|
2
|
+
export interface AgentHistoryQuery {
|
|
3
|
+
peer_cid: string;
|
|
4
|
+
limit?: number;
|
|
5
|
+
before_seq?: number;
|
|
6
|
+
}
|
|
7
|
+
export interface SupervisorCorrespondence {
|
|
8
|
+
contacts(): Promise<unknown>;
|
|
9
|
+
history(request: AgentHistoryQuery): Promise<unknown>;
|
|
10
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -20,12 +20,19 @@ export function prepareManagedHarness(role, stateDir, cwd, descriptor, env) {
|
|
|
20
20
|
args: [bridge],
|
|
21
21
|
env: [{ name: 'FLEET_OURS_BRIDGE_DESCRIPTOR', value: descriptor }],
|
|
22
22
|
};
|
|
23
|
+
// Portable allow covers bound identity messaging, not all tools on the server.
|
|
24
|
+
const tools = role.harness === 'codex' && role.permissions?.approval === 'allow'
|
|
25
|
+
? Object.fromEntries([
|
|
26
|
+
'current_identity', 'get_messages', 'list_history', 'list_contacts', 'send_message',
|
|
27
|
+
].map(name => [name, { approval_mode: 'approve' }]))
|
|
28
|
+
: undefined;
|
|
23
29
|
const native = {
|
|
24
30
|
mcp_servers: {
|
|
25
31
|
ours: {
|
|
26
32
|
command: process.execPath,
|
|
27
33
|
args: [bridge],
|
|
28
34
|
env: { FLEET_OURS_BRIDGE_DESCRIPTOR: descriptor },
|
|
35
|
+
...(tools ? { tools } : {}),
|
|
29
36
|
},
|
|
30
37
|
},
|
|
31
38
|
plugins: { 'ours@ours-codex-marketplace': { enabled: false } },
|
|
@@ -35,6 +42,7 @@ export function prepareManagedHarness(role, stateDir, cwd, descriptor, env) {
|
|
|
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,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 {
|
|
@@ -47,6 +48,11 @@ export declare class AgentOursRuntime {
|
|
|
47
48
|
prepare(room?: RoomAdmission, timeoutMs?: number): Promise<void>;
|
|
48
49
|
/** Admission covers SDK calls, response rendering and agent filesystem callbacks. */
|
|
49
50
|
admit(): Promise<() => void>;
|
|
51
|
+
/** Trusted supervisor-only admission for a later room; keeps the live harness
|
|
52
|
+
* and original startup assignment. Not exposed as an agent lifecycle tool. */
|
|
53
|
+
joinAdditionalRoom(invite: string, expectedRoomCid: string): Promise<void>;
|
|
54
|
+
readContacts(): Promise<unknown>;
|
|
55
|
+
readHistory(request: AgentHistoryQuery): Promise<unknown>;
|
|
50
56
|
startHarness<T>(start: () => Promise<T>): Promise<T>;
|
|
51
57
|
suspend(): Promise<void>;
|
|
52
58
|
terminal(): Promise<void>;
|
|
@@ -201,6 +201,42 @@ export class AgentOursRuntime {
|
|
|
201
201
|
throw error;
|
|
202
202
|
}
|
|
203
203
|
}
|
|
204
|
+
/** Trusted supervisor-only admission for a later room; keeps the live harness
|
|
205
|
+
* and original startup assignment. Not exposed as an agent lifecycle tool. */
|
|
206
|
+
async joinAdditionalRoom(invite, expectedRoomCid) {
|
|
207
|
+
const release = await this.admit();
|
|
208
|
+
try {
|
|
209
|
+
const contact = await this.deps.client.addContact({ invite });
|
|
210
|
+
if (contact.cid !== expectedRoomCid)
|
|
211
|
+
throw Error('ADDITIONAL_ROOM_CID_MISMATCH');
|
|
212
|
+
await this.verify();
|
|
213
|
+
}
|
|
214
|
+
finally {
|
|
215
|
+
release();
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
async readContacts() {
|
|
219
|
+
const release = await this.admit();
|
|
220
|
+
try {
|
|
221
|
+
const data = await this.deps.client.listContacts();
|
|
222
|
+
await this.verify();
|
|
223
|
+
return { identity: { name: this.state.name, cid: this.state.cid, generation: this.state.generation }, ...data };
|
|
224
|
+
}
|
|
225
|
+
finally {
|
|
226
|
+
release();
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
async readHistory(request) {
|
|
230
|
+
const release = await this.admit();
|
|
231
|
+
try {
|
|
232
|
+
const data = await this.deps.client.listHistory(request);
|
|
233
|
+
await this.verify();
|
|
234
|
+
return { identity: { name: this.state.name, cid: this.state.cid, generation: this.state.generation }, ...data };
|
|
235
|
+
}
|
|
236
|
+
finally {
|
|
237
|
+
release();
|
|
238
|
+
}
|
|
239
|
+
}
|
|
204
240
|
async startHarness(start) {
|
|
205
241
|
if (!this.accepting || this.state.phase !== 'READY')
|
|
206
242
|
throw Error('RUNTIME_NOT_READY');
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { socketPath as privateSocketPath } from '../socket-path.js';
|
|
1
2
|
import { randomBytes, randomUUID } from 'node:crypto';
|
|
2
3
|
import { existsSync, mkdirSync, readFileSync, unlinkSync, writeFileSync } from 'node:fs';
|
|
3
4
|
import { join } from 'node:path';
|
|
@@ -11,19 +12,31 @@ import { atomicPrivateWrite, binderKey } from './state.js';
|
|
|
11
12
|
import { AgentOursRuntime } from './runtime.js';
|
|
12
13
|
import { startMcpEndpoint } from './mcp-endpoint.js';
|
|
13
14
|
export const privateRuntimeRoot = () => join(stateRoot(), 'private-ours');
|
|
15
|
+
function roomSecretPath(role) {
|
|
16
|
+
const startup = role.roomMemberStartup;
|
|
17
|
+
return join(privateRuntimeRoot(), 'room-inputs', binderKey(startup.room_identity_cid, JSON.stringify([role.name, startup.invite_id])) + '.json');
|
|
18
|
+
}
|
|
19
|
+
function matchesRoomSecret(secret, role) {
|
|
20
|
+
const startup = role.roomMemberStartup;
|
|
21
|
+
return secret.room_id === startup.room_id && secret.room_identity_cid === startup.room_identity_cid
|
|
22
|
+
&& secret.invite_id === startup.invite_id && secret.identity_name === role.identity
|
|
23
|
+
&& secret.role === startup.role;
|
|
24
|
+
}
|
|
14
25
|
/** Only trusted launch orchestration may write this descriptor, never the child. */
|
|
15
26
|
export function storeRoomSecret(role) {
|
|
16
27
|
const startup = role.roomMemberStartup;
|
|
17
28
|
if (!startup?.invite)
|
|
18
29
|
return;
|
|
30
|
+
if (startup.identity_name !== role.identity)
|
|
31
|
+
throw Error('ROOM_SECRET_MISMATCH');
|
|
19
32
|
const root = join(privateRuntimeRoot(), 'room-inputs');
|
|
20
33
|
mkdirSync(root, { recursive: true, mode: 0o700 });
|
|
21
|
-
|
|
34
|
+
// Failed attempts retain private evidence. A new invite must never overwrite
|
|
35
|
+
// or consume a previous attempt's descriptor.
|
|
36
|
+
const path = roomSecretPath(role);
|
|
22
37
|
if (existsSync(path)) {
|
|
23
38
|
const old = JSON.parse(readFileSync(path, 'utf8'));
|
|
24
|
-
if (old.
|
|
25
|
-
old.invite_id !== startup.invite_id ||
|
|
26
|
-
old.identity_name !== startup.identity_name)
|
|
39
|
+
if (!matchesRoomSecret(old, role) || old.invite !== startup.invite)
|
|
27
40
|
throw Error('ROOM_SECRET_COLLISION');
|
|
28
41
|
return;
|
|
29
42
|
}
|
|
@@ -144,7 +157,9 @@ export async function prepareManagedAgent(role, stateDir, temporary) {
|
|
|
144
157
|
let room;
|
|
145
158
|
if (role.roomMemberStartup) {
|
|
146
159
|
const startup = role.roomMemberStartup;
|
|
147
|
-
const
|
|
160
|
+
const legacyPath = join(root, 'room-inputs', binderKey(startup.room_identity_cid, role.name) + '.json');
|
|
161
|
+
const currentPath = roomSecretPath(role);
|
|
162
|
+
const path = existsSync(currentPath) ? currentPath : legacyPath;
|
|
148
163
|
const cowork = createCoworkAdapter();
|
|
149
164
|
room = {
|
|
150
165
|
id: startup.room_id,
|
|
@@ -153,9 +168,7 @@ export async function prepareManagedAgent(role, stateDir, temporary) {
|
|
|
153
168
|
action: startup.invite_id,
|
|
154
169
|
redeem: async (attached) => {
|
|
155
170
|
const secret = JSON.parse(readFileSync(path, 'utf8'));
|
|
156
|
-
if (secret
|
|
157
|
-
secret.invite_id !== startup.invite_id ||
|
|
158
|
-
secret.identity_name !== role.identity)
|
|
171
|
+
if (!matchesRoomSecret(secret, role))
|
|
159
172
|
throw Error('ROOM_SECRET_MISMATCH');
|
|
160
173
|
return attached.addContact({ invite: secret.invite });
|
|
161
174
|
},
|
|
@@ -195,7 +208,7 @@ export async function prepareManagedAgent(role, stateDir, temporary) {
|
|
|
195
208
|
await identities.add(role.identity);
|
|
196
209
|
const bridgeDir = join(stateDir, '.ours-bridge');
|
|
197
210
|
mkdirSync(bridgeDir, { recursive: true, mode: 0o700 });
|
|
198
|
-
const capability = randomBytes(32).toString('hex'), socket = join(bridgeDir, `g${generation}.sock`);
|
|
211
|
+
const capability = randomBytes(32).toString('hex'), socket = privateSocketPath(join(bridgeDir, `g${generation}.sock`));
|
|
199
212
|
const endpoint = await startMcpEndpoint({
|
|
200
213
|
socket,
|
|
201
214
|
capability,
|
|
@@ -207,7 +220,7 @@ export async function prepareManagedAgent(role, stateDir, temporary) {
|
|
|
207
220
|
});
|
|
208
221
|
partialEndpoint = endpoint;
|
|
209
222
|
const descriptor = join(bridgeDir, 'descriptor.json');
|
|
210
|
-
atomicPrivateWrite(descriptor, { socket, capability, generation });
|
|
223
|
+
atomicPrivateWrite(descriptor, { socket, capability, generation, role: role.name, identity: role.identity, cid: runtime.snapshot.cid });
|
|
211
224
|
return {
|
|
212
225
|
runtime,
|
|
213
226
|
descriptor,
|
|
@@ -255,8 +268,10 @@ export async function preparePermanentAssignment(role) {
|
|
|
255
268
|
endpoint: profile.endpoint,
|
|
256
269
|
expectedInstanceId: profile.expectedInstanceId,
|
|
257
270
|
credentialPath: profile.credentialPath,
|
|
258
|
-
sessionMode: '
|
|
259
|
-
|
|
271
|
+
sessionMode: 'external',
|
|
272
|
+
leaseToken: randomUUID(),
|
|
273
|
+
requiredCapabilities: ['external-sessions-v1', 'root-first-identities-v1'],
|
|
274
|
+
env: {},
|
|
260
275
|
});
|
|
261
276
|
try {
|
|
262
277
|
const rows = await client.listIdentities(), row = rows.find((r) => r.name === role.identity);
|
|
@@ -283,7 +298,12 @@ export async function preparePermanentAssignment(role) {
|
|
|
283
298
|
return 'verified';
|
|
284
299
|
}
|
|
285
300
|
finally {
|
|
286
|
-
|
|
301
|
+
try {
|
|
302
|
+
await client.releaseLease();
|
|
303
|
+
}
|
|
304
|
+
finally {
|
|
305
|
+
await client.close();
|
|
306
|
+
}
|
|
287
307
|
}
|
|
288
308
|
}
|
|
289
309
|
export function readRoomReadiness(roomCid, name) {
|
|
@@ -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
|
+
}
|