@ours.network/fleet 1.2.0-nightly.6 → 1.2.0-nightly.60
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +287 -65
- package/dist/account-origin.d.ts +2 -0
- package/dist/account-origin.js +6 -0
- package/dist/agent-ours/bridge.js +35 -9
- package/dist/agent-ours/correspondence.d.ts +10 -0
- package/dist/agent-ours/correspondence.js +1 -0
- package/dist/agent-ours/file-rpc.d.ts +12 -0
- package/dist/agent-ours/file-rpc.js +18 -4
- package/dist/agent-ours/harness.js +9 -1
- package/dist/agent-ours/mcp-endpoint.d.ts +4 -0
- package/dist/agent-ours/mcp-endpoint.js +5 -0
- package/dist/agent-ours/runtime.d.ts +9 -1
- package/dist/agent-ours/runtime.js +50 -2
- package/dist/agent-ours/service.d.ts +21 -1
- package/dist/agent-ours/service.js +94 -14
- package/dist/application/codex-models.d.ts +15 -0
- package/dist/application/codex-models.js +56 -0
- package/dist/application/conversation-history.d.ts +27 -0
- package/dist/application/conversation-history.js +212 -0
- package/dist/application/fleet-query-service.d.ts +1 -1
- package/dist/application/fleet-query-service.js +2 -2
- package/dist/application/legacy-supervisor-identity.d.ts +7 -0
- package/dist/application/legacy-supervisor-identity.js +43 -0
- package/dist/application/log-service.js +8 -8
- package/dist/application/model-catalog.d.ts +1 -1
- package/dist/application/model-catalog.js +5 -2
- package/dist/application/preset-provenance.d.ts +19 -0
- package/dist/application/preset-provenance.js +59 -0
- package/dist/application/role-creation-service.js +3 -2
- package/dist/application/role-removal-service.js +9 -1
- package/dist/application/role-repository.js +19 -2
- package/dist/application/room-layout-definitions.d.ts +45 -0
- package/dist/application/room-layout-definitions.js +130 -0
- package/dist/application/session-control.d.ts +30 -0
- package/dist/application/session-control.js +72 -3
- package/dist/application/supervisor-ours-tools.d.ts +159 -0
- package/dist/application/supervisor-ours-tools.js +115 -0
- package/dist/application/task-layouts.d.ts +91 -0
- package/dist/application/task-layouts.js +259 -0
- package/dist/application/task-room-service.d.ts +99 -3
- package/dist/application/task-room-service.js +285 -32
- package/dist/application/types.d.ts +2 -0
- package/dist/atomic-file.d.ts +5 -0
- package/dist/atomic-file.js +62 -1
- package/dist/briefing.d.ts +3 -1
- package/dist/briefing.js +70 -30
- package/dist/build-info.json +7 -4
- package/dist/capabilities.d.ts +6 -1
- package/dist/capabilities.js +8 -0
- package/dist/cli.js +407 -37
- package/dist/client-profile.d.ts +5 -17
- package/dist/client-profile.js +4 -127
- package/dist/config.d.ts +45 -0
- package/dist/config.js +57 -6
- package/dist/creation.d.ts +1 -1
- package/dist/creation.js +4 -4
- package/dist/daemon-recovery.d.ts +1 -8
- package/dist/daemon-recovery.js +8 -113
- package/dist/docs.d.ts +1 -1
- package/dist/docs.js +123 -19
- package/dist/doctor.d.ts +1 -1
- package/dist/doctor.js +55 -60
- package/dist/erased-resources.d.ts +9 -0
- package/dist/erased-resources.js +87 -0
- package/dist/file-delivery/reader.d.ts +15 -0
- package/dist/file-delivery/reader.js +63 -0
- package/dist/file-delivery/store.d.ts +15 -0
- package/dist/file-delivery/store.js +124 -0
- package/dist/file-delivery/types.d.ts +19 -0
- package/dist/file-delivery/types.js +1 -0
- package/dist/fleet-command-audit.d.ts +7 -0
- package/dist/fleet-command-audit.js +26 -9
- package/dist/harness/agent-session.d.ts +2 -0
- package/dist/harness/claude-code-session.js +1 -1
- package/dist/harness/claude-code.js +20 -11
- package/dist/harness/claude-runtime.d.ts +12 -0
- package/dist/harness/claude-runtime.js +43 -0
- package/dist/harness/codex-app-server-proxy.d.ts +4 -1
- package/dist/harness/codex-app-server-proxy.js +41 -5
- package/dist/harness/codex-reasoning.d.ts +5 -0
- package/dist/harness/codex-reasoning.js +12 -0
- package/dist/harness/codex-runtime.d.ts +11 -1
- package/dist/harness/codex-runtime.js +31 -4
- package/dist/harness/codex-session.js +2 -2
- package/dist/harness/codex.js +23 -12
- package/dist/init-wizard.d.ts +2 -0
- package/dist/init-wizard.js +38 -19
- package/dist/isolation/policy.js +6 -1
- package/dist/isolation/types.d.ts +5 -0
- package/dist/managed-cli.d.ts +253 -0
- package/dist/managed-cli.js +923 -0
- package/dist/managed-recovery.d.ts +13 -0
- package/dist/managed-recovery.js +31 -0
- package/dist/minimal-setup.d.ts +2 -0
- package/dist/minimal-setup.js +44 -0
- package/dist/monitor.d.ts +8 -34
- package/dist/monitor.js +89 -152
- package/dist/notifications/fleet-producer.d.ts +19 -0
- package/dist/notifications/fleet-producer.js +208 -0
- package/dist/notifications/outbox.d.ts +46 -0
- package/dist/notifications/outbox.js +158 -0
- package/dist/notifications/target-cleanup.d.ts +8 -0
- package/dist/notifications/target-cleanup.js +41 -0
- package/dist/notifications/task-producer.d.ts +18 -0
- package/dist/notifications/task-producer.js +67 -0
- package/dist/ops.d.ts +2 -0
- package/dist/ops.js +20 -3
- package/dist/owner-channel/channel.js +17 -6
- package/dist/owner-channel/commands.d.ts +4 -0
- package/dist/owner-channel/commands.js +8 -0
- package/dist/owner-channel/lifecycle-outbox.js +4 -3
- package/dist/owner-channel/ours-client.js +2 -17
- package/dist/paths.d.ts +2 -0
- package/dist/paths.js +2 -0
- package/dist/preset-migration.js +25 -0
- package/dist/resolved-plan.js +1 -0
- package/dist/rooms-tasks/add-member.d.ts +48 -0
- package/dist/rooms-tasks/add-member.js +83 -0
- package/dist/rooms-tasks/archived-absence.d.ts +8 -0
- package/dist/rooms-tasks/archived-absence.js +48 -0
- package/dist/rooms-tasks/cli.js +65 -5
- package/dist/rooms-tasks/close.d.ts +6 -2
- package/dist/rooms-tasks/close.js +246 -37
- package/dist/rooms-tasks/config.d.ts +1 -1
- package/dist/rooms-tasks/config.js +11 -4
- package/dist/rooms-tasks/cowork-adapter.d.ts +2 -4
- package/dist/rooms-tasks/cowork-adapter.js +7 -121
- package/dist/rooms-tasks/cowork-http.js +29 -1
- package/dist/rooms-tasks/deletion.d.ts +1 -1
- package/dist/rooms-tasks/deletion.js +84 -5
- package/dist/rooms-tasks/erasure.d.ts +6 -0
- package/dist/rooms-tasks/erasure.js +173 -0
- package/dist/rooms-tasks/external-worker.d.ts +1 -1
- package/dist/rooms-tasks/external-worker.js +3 -4
- package/dist/rooms-tasks/layout-binding-client.d.ts +11 -0
- package/dist/rooms-tasks/layout-binding-client.js +56 -0
- package/dist/rooms-tasks/layout-binding-grants.d.ts +21 -0
- package/dist/rooms-tasks/layout-binding-grants.js +128 -0
- package/dist/rooms-tasks/layout-cli.d.ts +4 -0
- package/dist/rooms-tasks/layout-cli.js +107 -0
- package/dist/rooms-tasks/layout-config.d.ts +11 -0
- package/dist/rooms-tasks/layout-config.js +89 -0
- package/dist/rooms-tasks/layout-control.d.ts +21 -0
- package/dist/rooms-tasks/layout-control.js +52 -0
- package/dist/rooms-tasks/layout-member-retirement.d.ts +10 -0
- package/dist/rooms-tasks/layout-member-retirement.js +90 -0
- package/dist/rooms-tasks/layout-service.d.ts +43 -0
- package/dist/rooms-tasks/layout-service.js +203 -0
- package/dist/rooms-tasks/layout.d.ts +121 -0
- package/dist/rooms-tasks/layout.js +285 -0
- package/dist/rooms-tasks/legacy-task-member.d.ts +19 -0
- package/dist/rooms-tasks/legacy-task-member.js +116 -0
- package/dist/rooms-tasks/live-readiness.d.ts +18 -0
- package/dist/rooms-tasks/live-readiness.js +85 -0
- package/dist/rooms-tasks/member-ownership.d.ts +5 -0
- package/dist/rooms-tasks/member-ownership.js +40 -0
- package/dist/rooms-tasks/member-startup.d.ts +1 -0
- package/dist/rooms-tasks/member-startup.js +2 -0
- package/dist/rooms-tasks/provision.d.ts +3 -0
- package/dist/rooms-tasks/provision.js +101 -49
- package/dist/rooms-tasks/recovery.d.ts +19 -0
- package/dist/rooms-tasks/recovery.js +110 -0
- package/dist/rooms-tasks/room-state.d.ts +4 -2
- package/dist/rooms-tasks/room-state.js +47 -8
- package/dist/rooms-tasks/task-service-retirement.d.ts +11 -0
- package/dist/rooms-tasks/task-service-retirement.js +21 -0
- package/dist/rooms-tasks/task-state.d.ts +51 -8
- package/dist/rooms-tasks/task-state.js +245 -25
- package/dist/rooms-tasks/types.d.ts +40 -0
- package/dist/rooms-tasks/workspace-artifacts.d.ts +6 -0
- package/dist/rooms-tasks/workspace-artifacts.js +93 -0
- package/dist/rooms-tasks/workspace.d.ts +35 -0
- package/dist/rooms-tasks/workspace.js +325 -0
- package/dist/runner.d.ts +25 -3
- package/dist/runner.js +291 -49
- package/dist/session/acp.d.ts +53 -1
- package/dist/session/acp.js +216 -40
- package/dist/session/arbiter.d.ts +11 -0
- package/dist/session/arbiter.js +43 -2
- package/dist/session/codex-app-server.d.ts +32 -0
- package/dist/session/codex-app-server.js +137 -23
- package/dist/session/control.d.ts +12 -1
- package/dist/session/control.js +68 -3
- package/dist/session/conversation-normalizer.d.ts +2 -1
- package/dist/session/conversation-normalizer.js +1 -1
- package/dist/session/conversation-store.d.ts +1 -1
- package/dist/session/conversation-store.js +7 -11
- package/dist/session/conversation-types.d.ts +28 -3
- package/dist/session/prompt-batch.d.ts +63 -0
- package/dist/session/prompt-batch.js +86 -0
- package/dist/session/task-notice.d.ts +14 -0
- package/dist/session/task-notice.js +13 -0
- package/dist/session/types.d.ts +14 -0
- package/dist/setup-tunnel-preflight.d.ts +34 -0
- package/dist/setup-tunnel-preflight.js +62 -0
- package/dist/socket-path.d.ts +2 -0
- package/dist/socket-path.js +22 -0
- package/dist/spawn.d.ts +5 -1
- package/dist/spawn.js +40 -4
- package/dist/startup-prompt.d.ts +4 -0
- package/dist/startup-prompt.js +36 -0
- package/dist/startup-readiness.d.ts +13 -0
- package/dist/startup-readiness.js +38 -0
- package/dist/startup-telemetry.d.ts +16 -0
- package/dist/startup-telemetry.js +46 -0
- package/dist/subscriptions/cli.d.ts +91 -0
- package/dist/subscriptions/cli.js +223 -0
- package/dist/subscriptions/launch.d.ts +37 -0
- package/dist/subscriptions/launch.js +93 -0
- package/dist/subscriptions/login.d.ts +75 -0
- package/dist/subscriptions/login.js +182 -0
- package/dist/subscriptions/observed.d.ts +7 -0
- package/dist/subscriptions/observed.js +44 -0
- package/dist/subscriptions/probe.d.ts +10 -0
- package/dist/subscriptions/probe.js +73 -0
- package/dist/subscriptions/service.d.ts +102 -0
- package/dist/subscriptions/service.js +364 -0
- package/dist/subscriptions/store.d.ts +71 -0
- package/dist/subscriptions/store.js +140 -0
- package/dist/supervisor/adoption.d.ts +8 -0
- package/dist/supervisor/adoption.js +78 -0
- package/dist/supervisor/catalog.d.ts +62 -0
- package/dist/supervisor/catalog.js +254 -0
- package/dist/supervisor/fleet.d.ts +8 -0
- package/dist/supervisor/fleet.js +89 -0
- package/dist/supervisor/index.js +3 -6
- package/dist/supervisor/launchd.d.ts +1 -1
- package/dist/supervisor/launchd.js +26 -13
- package/dist/supervisor/legacy.d.ts +35 -0
- package/dist/supervisor/legacy.js +524 -0
- package/dist/supervisor/manager.d.ts +15 -0
- package/dist/supervisor/manager.js +270 -0
- package/dist/supervisor/scope.d.ts +4 -0
- package/dist/supervisor/scope.js +10 -0
- package/dist/supervisor/systemd.d.ts +2 -2
- package/dist/supervisor/systemd.js +19 -33
- package/dist/task-supervision.d.ts +9 -0
- package/dist/task-supervision.js +145 -0
- package/dist/temp-idle.d.ts +17 -0
- package/dist/temp-idle.js +42 -0
- package/dist/temp-lifecycle.d.ts +23 -8
- package/dist/temp-lifecycle.js +113 -65
- package/dist/temp-supervisor-recovery.d.ts +1 -1
- package/dist/temp-supervisor-recovery.js +2 -2
- package/dist/tunnel-setup-completion.d.ts +7 -0
- package/dist/tunnel-setup-completion.js +13 -0
- package/dist/tunnel-setup-input.d.ts +19 -0
- package/dist/tunnel-setup-input.js +124 -0
- package/dist/web/agent-attachments.d.ts +29 -0
- package/dist/web/agent-attachments.js +162 -0
- package/dist/web/auth.d.ts +63 -2
- package/dist/web/auth.js +191 -10
- package/dist/web/available-port.d.ts +2 -0
- package/dist/web/available-port.js +23 -0
- package/dist/web/control.d.ts +3 -3
- package/dist/web/control.js +10 -8
- package/dist/web/fleet-config-service.d.ts +4 -1
- package/dist/web/fleet-config-service.js +46 -15
- package/dist/web/prefix-gateway.d.ts +32 -0
- package/dist/web/prefix-gateway.js +238 -0
- package/dist/web/runtime.d.ts +3 -1
- package/dist/web/runtime.js +116 -31
- package/dist/web/server.d.ts +16 -0
- package/dist/web/server.js +654 -55
- package/dist/web/service.d.ts +11 -2
- package/dist/web/service.js +30 -4
- package/dist/web/workspace-devices.d.ts +107 -0
- package/dist/web/workspace-devices.js +277 -0
- package/dist/web/workspace-tunnel.d.ts +1 -0
- package/dist/web/workspace-tunnel.js +34 -0
- package/dist/workspace-enrollment.d.ts +82 -0
- package/dist/workspace-enrollment.js +566 -0
- package/dist/workspace-replacement.d.ts +66 -0
- package/dist/workspace-replacement.js +220 -0
- package/docs/acp-files.md +146 -0
- package/docs/fleet-migration.md +54 -0
- package/docs/live-voice.md +13 -0
- package/docs/room-layouts.md +227 -0
- package/docs/task-workspaces.md +225 -0
- package/docs/validation/managed-cli-permissions.md +421 -0
- package/examples/room-layouts/fresh.yaml +27 -0
- package/examples/room-layouts/scoped.yaml +21 -0
- package/examples/room-layouts/shared.yaml +19 -0
- package/package.json +20 -19
- package/presets/README.md +78 -0
- package/presets/brain-catalog.json +8 -2
- package/presets/fleet/agent_templates/Critic.yaml +1 -1
- package/presets/fleet/agent_templates/Developer.yaml +1 -1
- package/presets/fleet/agent_templates/Engineer.yaml +5 -0
- package/presets/fleet/agent_templates/LocalCoordinator.yaml +1 -1
- package/presets/fleet/agents/FleetCoordinator.yaml +1 -1
- package/presets/fleet/brains/claude-fable-5-1-high.yaml +5 -0
- package/presets/fleet/brains/claude-fable-5-1-low.yaml +5 -0
- package/presets/fleet/brains/claude-fable-5-1-max.yaml +5 -0
- package/presets/fleet/brains/claude-fable-5-1-medium.yaml +5 -0
- package/presets/fleet/brains/claude-fable-5-1-xhigh.yaml +5 -0
- package/presets/fleet/brains/claude-fable-5-high.yaml +1 -1
- package/presets/fleet/brains/claude-fable-5-low.yaml +1 -1
- package/presets/fleet/brains/claude-fable-5-max.yaml +1 -1
- package/presets/fleet/brains/claude-fable-5-medium.yaml +1 -1
- package/presets/fleet/brains/claude-fable-5-xhigh.yaml +1 -1
- package/presets/fleet/brains/claude-haiku-4-5-20251001-default.yaml +4 -0
- package/presets/fleet/brains/claude-opus-5-5-high.yaml +5 -0
- package/presets/fleet/brains/claude-opus-5-5-low.yaml +5 -0
- package/presets/fleet/brains/claude-opus-5-5-max.yaml +5 -0
- package/presets/fleet/brains/claude-opus-5-5-medium.yaml +5 -0
- package/presets/fleet/brains/claude-opus-5-5-xhigh.yaml +5 -0
- package/presets/fleet/brains/claude-opus-5-high.yaml +1 -1
- package/presets/fleet/brains/claude-opus-5-low.yaml +1 -1
- package/presets/fleet/brains/claude-opus-5-max.yaml +1 -1
- package/presets/fleet/brains/claude-opus-5-medium.yaml +1 -1
- package/presets/fleet/brains/claude-opus-5-xhigh.yaml +1 -1
- package/presets/fleet/brains/claude-sonnet-5-high.yaml +1 -1
- package/presets/fleet/brains/claude-sonnet-5-low.yaml +1 -1
- package/presets/fleet/brains/claude-sonnet-5-max.yaml +1 -1
- package/presets/fleet/brains/claude-sonnet-5-medium.yaml +1 -1
- package/presets/fleet/brains/claude-sonnet-5-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-3-codex-spark-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-3-codex-spark-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-3-codex-spark-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-3-codex-spark-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-mini-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-mini-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-mini-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-mini-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-5-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-5-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-5-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-5-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-luna-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-luna-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-luna-max.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-luna-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-luna-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-sol-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-sol-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-sol-max.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-sol-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-sol-ultra.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-sol-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-terra-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-terra-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-terra-max.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-terra-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-terra-ultra.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-terra-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-6-1-sol-high.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-1-sol-low.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-1-sol-max.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-1-sol-medium.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-1-sol-ultra.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-1-sol-xhigh.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-astra-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-6-astra-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-6-astra-max.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-6-astra-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-6-astra-ultra.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-6-astra-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-6-luna-high.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-luna-low.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-luna-max.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-luna-medium.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-luna-xhigh.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-sol-high.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-sol-low.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-sol-max.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-sol-medium.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-sol-ultra.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-sol-xhigh.yaml +5 -0
- package/presets/fleet/roles/Coordinator.yaml +34 -11
- package/presets/fleet/roles/Engineer.yaml +40 -0
- package/presets/fleet/room_layouts/engineering.yaml +25 -0
- package/presets/fleet/room_layouts/pair.yaml +24 -0
- package/presets/fleet/room_layouts/single.yaml +17 -0
- package/presets/fleet/room_layouts/team.yaml +30 -0
- package/presets/fleet/room_templates/engineering.yaml +19 -0
- package/presets/manifest.json +2 -2
- package/dist/web-app/assets/index-59GF-gsZ.css +0 -1
- package/dist/web-app/assets/index-C0h3ALvs.js +0 -10
- package/dist/web-app/icons/ours-fleet-maskable.svg +0 -4
- package/dist/web-app/icons/ours-fleet.svg +0 -4
- package/dist/web-app/index.html +0 -17
- package/dist/web-app/manifest.webmanifest +0 -15
- package/dist/web-app/offline.html +0 -18
- package/dist/web-app/sw.js +0 -51
package/dist/session/arbiter.js
CHANGED
|
@@ -29,6 +29,7 @@ export class RoleTurnArbiter {
|
|
|
29
29
|
tail = Promise.resolve();
|
|
30
30
|
unsettled = 0;
|
|
31
31
|
stopping = false;
|
|
32
|
+
retiring = false;
|
|
32
33
|
/** Bumped by `retireStalledAdmission`; every claim carries the one it was made under. */
|
|
33
34
|
generation = 0;
|
|
34
35
|
/** Resolves the instant the CURRENT generation is retired. */
|
|
@@ -89,7 +90,20 @@ export class RoleTurnArbiter {
|
|
|
89
90
|
retired.retire();
|
|
90
91
|
}
|
|
91
92
|
queuePrompt(text, options = {}) {
|
|
92
|
-
return this.exclusive(async () =>
|
|
93
|
+
return this.exclusive(async () => {
|
|
94
|
+
if (this.retiring)
|
|
95
|
+
throw new Error('Chat is closing');
|
|
96
|
+
return this.track(await this.session.queuePrompt(text, this.monitorAdmissionOptions(options)));
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
/** Inbox traffic must not cancel or steer an active human console/voice turn.
|
|
100
|
+
* Queue it once; the monitor commits its cursor after that turn succeeds.
|
|
101
|
+
* Other origins retain their configured monitor policy.
|
|
102
|
+
*/
|
|
103
|
+
monitorAdmissionOptions(options) {
|
|
104
|
+
return options.origin?.kind === 'fleet-monitor'
|
|
105
|
+
&& this.session.snapshot().activePromptSource === 'owner-admin-console'
|
|
106
|
+
? { ...options, interrupt: false, steer: false } : options;
|
|
93
107
|
}
|
|
94
108
|
async submitPrompt(text, options = {}) {
|
|
95
109
|
return (await this.queuePrompt(text, options)).completion;
|
|
@@ -99,6 +113,10 @@ export class RoleTurnArbiter {
|
|
|
99
113
|
* answers and explicit interrupts must remain able to pass immediately.
|
|
100
114
|
*/
|
|
101
115
|
submitPromptAfterTool(text, options = {}) {
|
|
116
|
+
if (this.retiring)
|
|
117
|
+
return Promise.reject(new Error('Chat is closing'));
|
|
118
|
+
if (this.monitorAdmissionOptions(options) !== options)
|
|
119
|
+
return this.submitPrompt(text, options);
|
|
102
120
|
return this.session.submitPromptAfterTool?.(text, options)
|
|
103
121
|
?? this.session.submitPrompt(text, { ...options, interrupt: false, steer: false });
|
|
104
122
|
}
|
|
@@ -136,6 +154,20 @@ export class RoleTurnArbiter {
|
|
|
136
154
|
throw error;
|
|
137
155
|
});
|
|
138
156
|
}
|
|
157
|
+
/** Retire only at the same admission boundary used by all prompt producers. */
|
|
158
|
+
retireIfIdle(expired) {
|
|
159
|
+
return this.exclusive(async () => {
|
|
160
|
+
const snapshot = this.session.snapshot();
|
|
161
|
+
const queue = this.session.conversationSnapshot?.().queueDepth ?? -1;
|
|
162
|
+
if (this.retiring || this.unsettled > 0 || snapshot.readiness !== 'idle' || !expired(snapshot, queue))
|
|
163
|
+
return false;
|
|
164
|
+
this.retiring = true;
|
|
165
|
+
this.stopping = true;
|
|
166
|
+
await this.session.close();
|
|
167
|
+
return true;
|
|
168
|
+
});
|
|
169
|
+
}
|
|
170
|
+
sharedTurnId(promptId) { return this.session.sharedTurnId?.(promptId); }
|
|
139
171
|
stopScheduledAdmission() { this.stopping = true; }
|
|
140
172
|
isAlive() { return this.session.isAlive(); }
|
|
141
173
|
snapshot() { return this.session.snapshot(); }
|
|
@@ -157,7 +189,16 @@ export class RoleTurnArbiter {
|
|
|
157
189
|
submitPromptBrowser(command) {
|
|
158
190
|
if (!this.session.submitPromptBrowser)
|
|
159
191
|
return Promise.reject(new Error('browser prompt admission is unavailable'));
|
|
160
|
-
return this.exclusive(() => this.
|
|
192
|
+
return this.exclusive(() => { if (this.retiring)
|
|
193
|
+
throw new Error('Chat is closing'); return this.session.submitPromptBrowser(command); });
|
|
194
|
+
}
|
|
195
|
+
get liveVoiceSupported() { return this.session.liveVoiceSupported === true; }
|
|
196
|
+
interruptPrompt(generation, promptId) {
|
|
197
|
+
return this.exclusive(async () => {
|
|
198
|
+
if (!this.session.interruptPrompt)
|
|
199
|
+
throw new Error('targeted voice interrupt is unavailable');
|
|
200
|
+
return interruptOutcome(await this.session.interruptPrompt(generation, promptId));
|
|
201
|
+
});
|
|
161
202
|
}
|
|
162
203
|
interrupt(source = 'local-console') {
|
|
163
204
|
return this.exclusive(async () => interruptOutcome(await this.session.interrupt(source)));
|
|
@@ -1,5 +1,7 @@
|
|
|
1
|
+
import { type TaskNoticeBinding, type TaskCreatedNotice } from './task-notice.js';
|
|
1
2
|
import type { CommonPermissions } from '../config.js';
|
|
2
3
|
import type { ConversationEventV1, ConversationSnapshot, PromptOrigin, PromptReceipt, SubmitPromptCommand } from './conversation-types.js';
|
|
4
|
+
import { type PendingPrompt } from './prompt-batch.js';
|
|
3
5
|
import type { CodexAppServerTransportFactory } from './codex-app-server-transport.js';
|
|
4
6
|
import type { AgentSession, ConversationHandlePage, ExitRecord, InterruptOutcome, QueuedPrompt, SessionEvent, SessionSnapshot, SubmitPromptOptions, TurnCancellationSource, TurnResult } from './types.js';
|
|
5
7
|
type JsonObject = Record<string, unknown>;
|
|
@@ -10,6 +12,7 @@ export interface CodexAppServerSessionOptions {
|
|
|
10
12
|
env: Record<string, string>;
|
|
11
13
|
stateDir: string;
|
|
12
14
|
mode: 'fresh' | 'resume';
|
|
15
|
+
requireResume?: boolean;
|
|
13
16
|
permissions: CommonPermissions;
|
|
14
17
|
permissionMode: NonNullable<SessionSnapshot['permissionMode']>;
|
|
15
18
|
model?: string | null;
|
|
@@ -28,6 +31,16 @@ export interface CodexAppServerSessionOptions {
|
|
|
28
31
|
}
|
|
29
32
|
/** Server-generated application provenance for authenticated browser prompts only. */
|
|
30
33
|
export declare function nativePromptAdditionalContext(origin: PromptOrigin | undefined): JsonObject | undefined;
|
|
34
|
+
/**
|
|
35
|
+
* Application context for a turn that carries several admitted prompts.
|
|
36
|
+
*
|
|
37
|
+
* The single-prompt context says "the accompanying user text", which would
|
|
38
|
+
* cover every member. A shared turn therefore carries this one entry instead:
|
|
39
|
+
* each member is its own user text item, in order, and its route and exact
|
|
40
|
+
* bytes are stated here by the server. Text imitating this wording is still
|
|
41
|
+
* only user text; it cannot add an application-context entry.
|
|
42
|
+
*/
|
|
43
|
+
export declare function nativeBatchAdditionalContext(members: ReadonlyArray<Pick<PendingPrompt, 'text' | 'origin'>>): JsonObject | undefined;
|
|
31
44
|
/** Native Codex session implementing Fleet's provider-neutral live-session contract. */
|
|
32
45
|
export declare class CodexAppServerSession implements AgentSession {
|
|
33
46
|
private readonly options;
|
|
@@ -54,6 +67,10 @@ export declare class CodexAppServerSession implements AgentSession {
|
|
|
54
67
|
private exit;
|
|
55
68
|
private queueDepth;
|
|
56
69
|
private promptTail;
|
|
70
|
+
/** Admitted prompts not yet handed to the app-server, in admission order. */
|
|
71
|
+
private readonly pendingPrompts;
|
|
72
|
+
/** Admitted prompt → the turn that carries it, while that turn is open. */
|
|
73
|
+
private readonly sharedTurns;
|
|
57
74
|
private activeTurn?;
|
|
58
75
|
private controllerCount;
|
|
59
76
|
private controllerGrace?;
|
|
@@ -67,7 +84,20 @@ export declare class CodexAppServerSession implements AgentSession {
|
|
|
67
84
|
isAlive(): boolean;
|
|
68
85
|
snapshot(): SessionSnapshot;
|
|
69
86
|
queuePrompt(text: string, options?: SubmitPromptOptions): Promise<QueuedPrompt>;
|
|
87
|
+
/** The turn whose events and result answer this admitted prompt; itself unless a release shared it. */
|
|
88
|
+
sharedTurnId(promptId: string): string | undefined;
|
|
89
|
+
/** Each prompt keeps its own step in the chain; the step it reaches releases a whole snapshot. */
|
|
90
|
+
private enqueue;
|
|
91
|
+
/**
|
|
92
|
+
* The turn boundary: the first waiting prompt to get here synchronously
|
|
93
|
+
* takes every pending prompt delivered with it into one turn. A prompt
|
|
94
|
+
* admitted afterwards stays pending for the next boundary, and a step whose
|
|
95
|
+
* prompt was already taken reports that turn's result.
|
|
96
|
+
*/
|
|
97
|
+
private release;
|
|
70
98
|
submitPrompt(text: string, options?: SubmitPromptOptions): Promise<TurnResult>;
|
|
99
|
+
readonly liveVoiceSupported = true;
|
|
100
|
+
interruptPrompt(generation: string, promptId: string): Promise<InterruptOutcome>;
|
|
71
101
|
interrupt(source?: TurnCancellationSource): Promise<InterruptOutcome>;
|
|
72
102
|
respondPermission(permissionId: string, optionId: string): boolean;
|
|
73
103
|
respondPermissionV2(permissionId: string, optionId: string, sessionGeneration: string): 'accepted' | 'stale';
|
|
@@ -76,6 +106,8 @@ export declare class CodexAppServerSession implements AgentSession {
|
|
|
76
106
|
setControllerAttached(attached: boolean): void;
|
|
77
107
|
exitResult(): ExitRecord | null;
|
|
78
108
|
close(): Promise<void>;
|
|
109
|
+
taskNoticeBinding(): TaskNoticeBinding | undefined;
|
|
110
|
+
recordTaskCreated(binding: TaskNoticeBinding, notice: TaskCreatedNotice): void;
|
|
79
111
|
conversationPage(request?: {
|
|
80
112
|
after?: string;
|
|
81
113
|
limit?: number;
|
|
@@ -1,8 +1,11 @@
|
|
|
1
|
+
import { appendTaskCreated } from './task-notice.js';
|
|
1
2
|
import { createHash, randomUUID } from 'node:crypto';
|
|
2
3
|
import { existsSync, readFileSync, writeFileSync } from 'node:fs';
|
|
3
4
|
import { isAbsolute, join, resolve } from 'node:path';
|
|
5
|
+
import { boundedJson } from './conversation-normalizer.js';
|
|
4
6
|
import { ConversationEventStore } from './conversation-store.js';
|
|
5
7
|
import { SessionEvents } from './events.js';
|
|
8
|
+
import { memberAuthority, memberBindings, pendingInventory, takeBatch, } from './prompt-batch.js';
|
|
6
9
|
import { CodexAppServerTransport } from './codex-app-server-transport.js';
|
|
7
10
|
import { CODEX_APP_SERVER_CANCEL_DEADLINE_EXCEEDED, SessionControlError, sessionBackendCapabilities, turnResult, } from './types.js';
|
|
8
11
|
const PERMISSION_TIMEOUT_MS = 10 * 60_000;
|
|
@@ -12,7 +15,6 @@ const INTERRUPT_TERMINATE_GRACE_MS = 5_000;
|
|
|
12
15
|
const TERMINAL_RECONCILE_QUIET_MS = 2_000;
|
|
13
16
|
const TERMINAL_RECONCILE_REQUEST_TIMEOUT_MS = 5_000;
|
|
14
17
|
const SCHEDULED_REDACTION = '[scheduled-loop content redacted]';
|
|
15
|
-
const COMMENTARY_REDACTION = '[assistant commentary redacted]';
|
|
16
18
|
const OWNER_ADMIN_CONTEXT_KEY = 'ours-fleet://prompt-provenance?source=owner_admin_console';
|
|
17
19
|
const OWNER_CHANNEL_CONTEXT_KEY = 'ours-fleet://prompt-provenance?source=owner_channel';
|
|
18
20
|
const isObject = (value) => typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
@@ -74,6 +76,31 @@ export function nativePromptAdditionalContext(origin) {
|
|
|
74
76
|
};
|
|
75
77
|
return undefined;
|
|
76
78
|
}
|
|
79
|
+
const PROMPT_BATCH_CONTEXT_KEY = 'ours-fleet://prompt-batch';
|
|
80
|
+
/**
|
|
81
|
+
* Application context for a turn that carries several admitted prompts.
|
|
82
|
+
*
|
|
83
|
+
* The single-prompt context says "the accompanying user text", which would
|
|
84
|
+
* cover every member. A shared turn therefore carries this one entry instead:
|
|
85
|
+
* each member is its own user text item, in order, and its route and exact
|
|
86
|
+
* bytes are stated here by the server. Text imitating this wording is still
|
|
87
|
+
* only user text; it cannot add an application-context entry.
|
|
88
|
+
*/
|
|
89
|
+
export function nativeBatchAdditionalContext(members) {
|
|
90
|
+
if (members.length === 1)
|
|
91
|
+
return nativePromptAdditionalContext(members[0].origin);
|
|
92
|
+
const bindings = memberBindings(members, origin => sourceFor(origin).source);
|
|
93
|
+
return {
|
|
94
|
+
[PROMPT_BATCH_CONTEXT_KEY]: {
|
|
95
|
+
kind: 'application',
|
|
96
|
+
value: `Fleet queued prompt batch: ${members.length} separately admitted prompts in submission order, `
|
|
97
|
+
+ 'one user text item each. Server-generated provenance per item, describing only that item: '
|
|
98
|
+
+ bindings.map(binding => `item ${binding.index} of ${binding.of} `
|
|
99
|
+
+ `(source=${binding.source}, bytes=${binding.bytes}, sha256=${binding.sha256}): `
|
|
100
|
+
+ `${memberAuthority(binding.source)}`).join('; ') + '.',
|
|
101
|
+
},
|
|
102
|
+
};
|
|
103
|
+
}
|
|
77
104
|
function toolItem(item) {
|
|
78
105
|
switch (item.type) {
|
|
79
106
|
case 'commandExecution': return {
|
|
@@ -122,6 +149,10 @@ export class CodexAppServerSession {
|
|
|
122
149
|
exit = null;
|
|
123
150
|
queueDepth = 0;
|
|
124
151
|
promptTail = Promise.resolve();
|
|
152
|
+
/** Admitted prompts not yet handed to the app-server, in admission order. */
|
|
153
|
+
pendingPrompts = [];
|
|
154
|
+
/** Admitted prompt → the turn that carries it, while that turn is open. */
|
|
155
|
+
sharedTurns = new Map();
|
|
125
156
|
activeTurn;
|
|
126
157
|
controllerCount = 0;
|
|
127
158
|
controllerGrace;
|
|
@@ -172,6 +203,9 @@ export class CodexAppServerSession {
|
|
|
172
203
|
alive: this.isAlive(),
|
|
173
204
|
readiness: this.readiness,
|
|
174
205
|
sessionId: this.threadId,
|
|
206
|
+
// A shared turn is a human console turn if any member is: inbox traffic must not cut it short.
|
|
207
|
+
...(this.activeTurn?.origin ? { activePromptSource: this.activeTurn.console
|
|
208
|
+
? 'owner-admin-console' : this.activeTurn.origin.kind } : {}),
|
|
175
209
|
lastError: this.lastError,
|
|
176
210
|
pendingPermissionId: this.pendingPermissions.keys().next().value,
|
|
177
211
|
runtimeModel: this.runtimeModel,
|
|
@@ -202,8 +236,7 @@ export class CodexAppServerSession {
|
|
|
202
236
|
const queuedBehind = this.queueDepth;
|
|
203
237
|
this.admit(promptId, text, queuedBehind, options);
|
|
204
238
|
this.queueDepth++;
|
|
205
|
-
const run = this.
|
|
206
|
-
this.promptTail = run.then(() => undefined, () => undefined);
|
|
239
|
+
const run = this.enqueue(promptId, text, options.origin);
|
|
207
240
|
const completion = run.then(result => {
|
|
208
241
|
this.queueDepth = Math.max(0, this.queueDepth - 1);
|
|
209
242
|
return result;
|
|
@@ -216,6 +249,42 @@ export class CodexAppServerSession {
|
|
|
216
249
|
delivery: delivery ?? (queuedBehind ? 'queued' : 'started'),
|
|
217
250
|
};
|
|
218
251
|
}
|
|
252
|
+
/** The turn whose events and result answer this admitted prompt; itself unless a release shared it. */
|
|
253
|
+
sharedTurnId(promptId) {
|
|
254
|
+
return this.sharedTurns.get(promptId);
|
|
255
|
+
}
|
|
256
|
+
/** Each prompt keeps its own step in the chain; the step it reaches releases a whole snapshot. */
|
|
257
|
+
enqueue(promptId, text, origin) {
|
|
258
|
+
const entry = {
|
|
259
|
+
promptId, text, origin, ...sourceFor(origin), admittedAt: new Date().toISOString(),
|
|
260
|
+
// The one text the native session runs as an operation instead of a turn.
|
|
261
|
+
command: text.trim() === '/compact',
|
|
262
|
+
};
|
|
263
|
+
this.pendingPrompts.push(entry);
|
|
264
|
+
const run = this.promptTail.then(() => this.release(entry));
|
|
265
|
+
this.promptTail = run.then(() => undefined, () => undefined);
|
|
266
|
+
return run;
|
|
267
|
+
}
|
|
268
|
+
/**
|
|
269
|
+
* The turn boundary: the first waiting prompt to get here synchronously
|
|
270
|
+
* takes every pending prompt delivered with it into one turn. A prompt
|
|
271
|
+
* admitted afterwards stays pending for the next boundary, and a step whose
|
|
272
|
+
* prompt was already taken reports that turn's result.
|
|
273
|
+
*/
|
|
274
|
+
release(entry) {
|
|
275
|
+
if (entry.released)
|
|
276
|
+
return entry.released;
|
|
277
|
+
const members = takeBatch(this.pendingPrompts, entry);
|
|
278
|
+
for (const member of members)
|
|
279
|
+
this.sharedTurns.set(member.promptId, members[0].promptId);
|
|
280
|
+
const run = this.runPrompt(members).finally(() => {
|
|
281
|
+
for (const member of members)
|
|
282
|
+
this.sharedTurns.delete(member.promptId);
|
|
283
|
+
});
|
|
284
|
+
for (const member of members)
|
|
285
|
+
member.released = run;
|
|
286
|
+
return run;
|
|
287
|
+
}
|
|
219
288
|
async submitPrompt(text, options = {}) {
|
|
220
289
|
try {
|
|
221
290
|
return await (await this.queuePrompt(text, options)).completion;
|
|
@@ -226,10 +295,20 @@ export class CodexAppServerSession {
|
|
|
226
295
|
throw error;
|
|
227
296
|
}
|
|
228
297
|
}
|
|
298
|
+
liveVoiceSupported = true;
|
|
299
|
+
async interruptPrompt(generation, promptId) {
|
|
300
|
+
if (generation !== this.sessionGeneration)
|
|
301
|
+
throw new Error('session_changed: Agent session changed.');
|
|
302
|
+
if (!this.activeTurn?.promptIds.includes(promptId))
|
|
303
|
+
return { state: 'settled' };
|
|
304
|
+
return this.interrupt('local-console');
|
|
305
|
+
}
|
|
229
306
|
async interrupt(source = 'local-console') {
|
|
230
307
|
const active = this.activeTurn;
|
|
231
|
-
if (!active || !this.threadId
|
|
308
|
+
if (!active || !this.threadId)
|
|
232
309
|
return { state: 'settled' };
|
|
310
|
+
if (!active.nativeTurnId)
|
|
311
|
+
throw new SessionControlError('backend', 'The operation is still pending without a native turn ID. Cancellation is not confirmed; inspect or restart the agent if it does not progress.');
|
|
233
312
|
active.cancellationSource ??= source;
|
|
234
313
|
this.events.emit('state', { turnId: active.promptId, status: 'running', origin: active.origin });
|
|
235
314
|
this.conversation.appendSafe({
|
|
@@ -335,6 +414,10 @@ export class CodexAppServerSession {
|
|
|
335
414
|
this.conversation.close();
|
|
336
415
|
await this.transport.close();
|
|
337
416
|
}
|
|
417
|
+
taskNoticeBinding() {
|
|
418
|
+
return this.activeTurn && this.threadId ? { sessionGeneration: this.sessionGeneration, acpSessionId: this.threadId, promptId: this.activeTurn.promptId } : undefined;
|
|
419
|
+
}
|
|
420
|
+
recordTaskCreated(binding, notice) { appendTaskCreated(this.conversation, binding, notice); }
|
|
338
421
|
conversationPage(request = {}) {
|
|
339
422
|
const floor = Number(this.conversationStartCursor ?? 0);
|
|
340
423
|
const requested = Number(request.after ?? 0);
|
|
@@ -354,6 +437,7 @@ export class CodexAppServerSession {
|
|
|
354
437
|
sessionGeneration: this.sessionGeneration,
|
|
355
438
|
readiness: this.isAlive() ? this.readiness : 'offline',
|
|
356
439
|
queueDepth: this.queueDepth,
|
|
440
|
+
...pendingInventory(this.pendingPrompts),
|
|
357
441
|
pendingPermissionIds: [...this.pendingPermissions.keys()],
|
|
358
442
|
...(this.conversation.degraded ? { historyDegraded: true } : {}),
|
|
359
443
|
};
|
|
@@ -367,6 +451,10 @@ export class CodexAppServerSession {
|
|
|
367
451
|
const existing = this.conversation.receiptFor(command.commandId, bodyDigest);
|
|
368
452
|
if (existing)
|
|
369
453
|
return existing;
|
|
454
|
+
if (command.expectedSessionGeneration !== undefined && command.expectedSessionGeneration !== this.conversationSnapshot().sessionGeneration)
|
|
455
|
+
throw new Error('session_changed: Agent session changed. Record a new voice message.');
|
|
456
|
+
if (command.requireIdle && (this.queueDepth > 0 || this.conversationSnapshot().readiness !== 'idle'))
|
|
457
|
+
throw new Error('voice_busy: Agent is already working. Wait for the current turn.');
|
|
370
458
|
const queued = await this.queuePrompt(command.text, {
|
|
371
459
|
origin: { kind: 'owner-admin-console', commandId: command.commandId },
|
|
372
460
|
actor: { browserSession: command.actorBrowserSession },
|
|
@@ -387,6 +475,8 @@ export class CodexAppServerSession {
|
|
|
387
475
|
this.transport.notify('initialized');
|
|
388
476
|
const persisted = this.options.mode === 'resume' && existsSync(this.threadFile)
|
|
389
477
|
? readFileSync(this.threadFile, 'utf8').trim() : '';
|
|
478
|
+
if (this.options.requireResume && !persisted)
|
|
479
|
+
throw new Error('TASK_CONTEXT_RESUME_UNAVAILABLE');
|
|
390
480
|
const common = {
|
|
391
481
|
cwd: this.options.cwd,
|
|
392
482
|
approvalPolicy: this.options.approvalPolicy,
|
|
@@ -406,6 +496,8 @@ export class CodexAppServerSession {
|
|
|
406
496
|
this.threadId = string(thread?.id);
|
|
407
497
|
if (!this.threadId)
|
|
408
498
|
throw new Error('Codex app-server did not return a thread id');
|
|
499
|
+
if (this.options.requireResume && this.threadId !== persisted)
|
|
500
|
+
throw new Error('TASK_CONTEXT_RESUME_MISMATCH');
|
|
409
501
|
writeFileSync(this.threadFile, this.threadId + '\n', { mode: 0o600 });
|
|
410
502
|
const model = string(response.model) ?? this.options.model ?? undefined;
|
|
411
503
|
if (model)
|
|
@@ -415,6 +507,10 @@ export class CodexAppServerSession {
|
|
|
415
507
|
this.reasoningEffort = { value: effort };
|
|
416
508
|
this.readiness = 'idle';
|
|
417
509
|
this.events.emit('state', { status: 'idle', text: `Codex thread ${this.threadId}` });
|
|
510
|
+
this.conversation.appendSafe({
|
|
511
|
+
kind: 'capabilities.updated', sessionGeneration: this.sessionGeneration,
|
|
512
|
+
payload: { commands: [{ name: 'compact', description: textBlock('Compact session context') }] },
|
|
513
|
+
});
|
|
418
514
|
this.conversation.appendSafe({
|
|
419
515
|
kind: 'session.state', sessionGeneration: this.sessionGeneration,
|
|
420
516
|
payload: { status: 'idle' },
|
|
@@ -437,8 +533,7 @@ export class CodexAppServerSession {
|
|
|
437
533
|
const origin = open.commandId
|
|
438
534
|
? { kind: 'owner-admin-console', commandId: open.commandId }
|
|
439
535
|
: undefined;
|
|
440
|
-
const run = this.
|
|
441
|
-
this.promptTail = run.then(() => undefined, () => undefined);
|
|
536
|
+
const run = this.enqueue(open.promptId, open.text, origin);
|
|
442
537
|
void run.finally(() => { this.queueDepth = Math.max(0, this.queueDepth - 1); });
|
|
443
538
|
}
|
|
444
539
|
}
|
|
@@ -470,30 +565,43 @@ export class CodexAppServerSession {
|
|
|
470
565
|
else
|
|
471
566
|
this.conversation.appendSafe(draft);
|
|
472
567
|
}
|
|
473
|
-
async runPrompt(
|
|
568
|
+
async runPrompt(members) {
|
|
569
|
+
const { text, promptId, origin } = members[0];
|
|
474
570
|
if (!this.threadId || !this.transport.isAlive())
|
|
475
571
|
return turnResult(false, 'failed', this.lastError ?? 'Codex app-server session is offline');
|
|
476
572
|
let settle;
|
|
477
573
|
const settled = new Promise(resolveResult => { settle = resolveResult; });
|
|
478
|
-
const active = {
|
|
574
|
+
const active = {
|
|
575
|
+
promptId, origin, output: '', settled, settle,
|
|
576
|
+
promptIds: members.map(member => member.promptId),
|
|
577
|
+
console: members.some(member => member.origin?.kind === 'owner-admin-console'),
|
|
578
|
+
};
|
|
479
579
|
this.activeTurn = active;
|
|
480
580
|
this.finalAnswerObserved = false;
|
|
481
581
|
this.activeItems.clear();
|
|
482
582
|
this.readiness = 'running';
|
|
483
583
|
this.events.emit('state', { turnId: promptId, status: 'running', origin });
|
|
484
|
-
this.conversation.appendSafe({
|
|
584
|
+
members.forEach((member, index) => this.conversation.appendSafe({
|
|
485
585
|
kind: 'prompt.started', sessionGeneration: this.sessionGeneration,
|
|
486
|
-
promptId, turnId: promptId, source: sourceFor(origin).source,
|
|
487
|
-
|
|
586
|
+
promptId: member.promptId, turnId: promptId, source: sourceFor(member.origin).source,
|
|
587
|
+
payload: members.length > 1 ? { batch: { turnId: promptId, index, size: members.length } } : {},
|
|
588
|
+
}));
|
|
488
589
|
try {
|
|
590
|
+
if (text.trim() === '/compact') {
|
|
591
|
+
// The acknowledgement is not completion. Standard turn notifications
|
|
592
|
+
// settle the same queued prompt and durable browser receipt.
|
|
593
|
+
await this.transport.request('thread/compact/start', { threadId: this.threadId });
|
|
594
|
+
return await settled;
|
|
595
|
+
}
|
|
489
596
|
const response = await this.transport.request('turn/start', {
|
|
490
597
|
threadId: this.threadId,
|
|
491
|
-
|
|
598
|
+
// One unchanged user text item per admitted prompt, in admission order.
|
|
599
|
+
input: members.map(member => ({ type: 'text', text: member.text, text_elements: [] })),
|
|
492
600
|
approvalPolicy: this.options.approvalPolicy,
|
|
493
601
|
model: this.options.model ?? null,
|
|
494
602
|
effort: this.options.effort ?? null,
|
|
495
|
-
...(
|
|
496
|
-
? { additionalContext:
|
|
603
|
+
...(nativeBatchAdditionalContext(members)
|
|
604
|
+
? { additionalContext: nativeBatchAdditionalContext(members) } : {}),
|
|
497
605
|
});
|
|
498
606
|
const turn = isObject(response.turn) ? response.turn : undefined;
|
|
499
607
|
active.nativeTurnId ??= string(turn?.id);
|
|
@@ -699,7 +807,7 @@ export class CodexAppServerSession {
|
|
|
699
807
|
promptId: this.activeTurn?.promptId, turnId: this.activeTurn?.promptId,
|
|
700
808
|
messageId: id, source: 'agent',
|
|
701
809
|
payload: {
|
|
702
|
-
role: 'assistant', content: textBlock(delta, scheduled ? SCHEDULED_REDACTION :
|
|
810
|
+
role: 'assistant', content: textBlock(delta, scheduled ? SCHEDULED_REDACTION : undefined),
|
|
703
811
|
},
|
|
704
812
|
});
|
|
705
813
|
}
|
|
@@ -746,6 +854,10 @@ export class CodexAppServerSession {
|
|
|
746
854
|
toolCallId: scheduled ? 'scheduled-loop-tool' : id, snapshot,
|
|
747
855
|
title: scheduled ? 'scheduled-loop tool' : tool.title, status: tool.status,
|
|
748
856
|
kind: string(item.type),
|
|
857
|
+
...(!scheduled && item.arguments !== undefined ? { rawInput: boundedJson(item.arguments) } : {}),
|
|
858
|
+
...(!scheduled && item.type === 'commandExecution' ? { rawInput: boundedJson({ command: item.command, cwd: item.cwd }), ...(item.aggregatedOutput !== undefined ? { rawOutput: boundedJson({ output: item.aggregatedOutput, exitCode: item.exitCode }) } : {}) } : {}),
|
|
859
|
+
...(!scheduled && item.type === 'fileChange' && item.changes !== undefined ? { rawOutput: boundedJson(item.changes) } : {}),
|
|
860
|
+
...(!scheduled && (item.result !== undefined || item.error !== undefined) ? { rawOutput: boundedJson({ result: item.result, error: item.error }) } : {}),
|
|
749
861
|
},
|
|
750
862
|
});
|
|
751
863
|
}
|
|
@@ -768,14 +880,16 @@ export class CodexAppServerSession {
|
|
|
768
880
|
cancellationSource: active.cancellationSource,
|
|
769
881
|
});
|
|
770
882
|
this.events.emit('state', { status: this.readiness });
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
883
|
+
// Every member closes with the shared outcome, so none is left waiting.
|
|
884
|
+
for (const member of active.promptIds)
|
|
885
|
+
this.conversation.appendSafe({
|
|
886
|
+
kind: 'turn.completed', sessionGeneration: this.sessionGeneration,
|
|
887
|
+
promptId: member, turnId: active.promptId,
|
|
888
|
+
payload: {
|
|
889
|
+
outcome, ...(detail ? { stopReason: detail } : {}),
|
|
890
|
+
...(active.cancellationSource ? { cancellationSource: active.cancellationSource } : {}),
|
|
891
|
+
},
|
|
892
|
+
});
|
|
779
893
|
active.settle(result);
|
|
780
894
|
}
|
|
781
895
|
/**
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import type { LayoutControlRequest } from '../rooms-tasks/layout-control.js';
|
|
2
|
+
import type { SupervisorCorrespondence, AgentHistoryQuery } from '../agent-ours/correspondence.js';
|
|
1
3
|
import { type Socket } from 'node:net';
|
|
2
4
|
import type { AgentSession, ControlFailureKind, SessionEvent, SessionSnapshot } from './types.js';
|
|
3
5
|
import type { OwnerChannelHandle, OwnerChannelManagementRequest } from '../owner-channel/channel.js';
|
|
@@ -9,8 +11,13 @@ export interface ControlRequest {
|
|
|
9
11
|
version: 1 | 2 | 3;
|
|
10
12
|
id: string;
|
|
11
13
|
token: string;
|
|
12
|
-
command: 'status' | 'snapshot' | 'submit_prompt' | 'respond_permission' | 'interrupt' | 'follow' | 'events_since' | 'owner_channel_manage' | 'loop_status' | 'loop_run_now' | 'loop_disable' | 'loop_enable' | 'reload_config' | 'conversation_page' | 'conversation_follow' | 'submit_prompt_v2' | 'interrupt_v2' | 'respond_permission_v2' | 'fleet_spawn' | 'fleet_audit_begin' | 'fleet_audit_present' | 'fleet_audit_finish';
|
|
14
|
+
command: 'status' | 'snapshot' | 'submit_prompt' | 'respond_permission' | 'interrupt' | 'follow' | 'events_since' | 'owner_channel_manage' | 'loop_status' | 'loop_run_now' | 'loop_disable' | 'loop_enable' | 'reload_config' | 'conversation_page' | 'conversation_follow' | 'submit_prompt_v2' | 'submit_voice_prompt' | 'interrupt_v2' | 'interrupt_voice_v2' | 'layout_control' | 'agent_contacts' | 'agent_history' | 'respond_permission_v2' | 'fleet_spawn' | 'fleet_audit_begin' | 'fleet_audit_present' | 'fleet_audit_finish';
|
|
15
|
+
layout?: LayoutControlRequest;
|
|
16
|
+
agentHistory?: AgentHistoryQuery;
|
|
13
17
|
text?: string;
|
|
18
|
+
expectedSessionGeneration?: string;
|
|
19
|
+
requireIdle?: boolean;
|
|
20
|
+
promptId?: string;
|
|
14
21
|
permissionId?: string;
|
|
15
22
|
optionId?: string;
|
|
16
23
|
since?: number;
|
|
@@ -112,9 +119,13 @@ export declare class RoleControlServer {
|
|
|
112
119
|
private readonly sockets;
|
|
113
120
|
/** Interrupt idempotency: same command id returns the first receipt. */
|
|
114
121
|
private readonly interruptCommands;
|
|
122
|
+
private correspondence?;
|
|
123
|
+
setCorrespondence(value: SupervisorCorrespondence): void;
|
|
115
124
|
private ownerChannel?;
|
|
116
125
|
private loopManager?;
|
|
117
126
|
private reloadConfig?;
|
|
127
|
+
private layoutControl?;
|
|
128
|
+
setLayoutControl(handler: (request: LayoutControlRequest) => Promise<unknown>): void;
|
|
118
129
|
private fleetSpawner?;
|
|
119
130
|
private fleetAuditor?;
|
|
120
131
|
constructor(stateDir: string, session: AgentSession, log: (line: string) => void);
|
package/dist/session/control.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { socketPath as privateSocketPath } from '../socket-path.js';
|
|
1
2
|
import { randomBytes, randomUUID, timingSafeEqual } from 'node:crypto';
|
|
2
3
|
import { chmodSync, existsSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
|
3
4
|
import { createConnection, createServer } from 'node:net';
|
|
@@ -8,7 +9,7 @@ import { interruptSession, queueSessionPrompt, respondSessionPermission, respond
|
|
|
8
9
|
const MAX_LINE_BYTES = 64 * 1024;
|
|
9
10
|
/** Commands that require protocol version 3. */
|
|
10
11
|
const V3_COMMANDS = new Set([
|
|
11
|
-
'conversation_page', 'conversation_follow', 'submit_prompt_v2', 'interrupt_v2',
|
|
12
|
+
'conversation_page', 'conversation_follow', 'submit_prompt_v2', 'submit_voice_prompt', 'interrupt_v2', 'interrupt_voice_v2',
|
|
12
13
|
'respond_permission_v2',
|
|
13
14
|
]);
|
|
14
15
|
/** The one retained-range projection shared by polling and live-follow admission. */
|
|
@@ -99,7 +100,7 @@ export function oversightTaxonomy(name = '<Name>') {
|
|
|
99
100
|
export function oversightTaxonomyLines(name = '<Name>') {
|
|
100
101
|
return oversightTaxonomy(name).map(r => `- **${r.result}** — ${r.meaning} → ${r.action}`);
|
|
101
102
|
}
|
|
102
|
-
export const controlSocketPath = (stateDir) => join(stateDir, '.control.sock');
|
|
103
|
+
export const controlSocketPath = (stateDir) => privateSocketPath(join(stateDir, '.control.sock'));
|
|
103
104
|
export const controlTokenPath = (stateDir) => join(stateDir, '.control-token');
|
|
104
105
|
function sameToken(actual, supplied) {
|
|
105
106
|
const a = Buffer.from(actual);
|
|
@@ -116,9 +117,13 @@ export class RoleControlServer {
|
|
|
116
117
|
sockets = new Set();
|
|
117
118
|
/** Interrupt idempotency: same command id returns the first receipt. */
|
|
118
119
|
interruptCommands = new Map();
|
|
120
|
+
correspondence;
|
|
121
|
+
setCorrespondence(value) { this.correspondence = value; }
|
|
119
122
|
ownerChannel;
|
|
120
123
|
loopManager;
|
|
121
124
|
reloadConfig;
|
|
125
|
+
layoutControl;
|
|
126
|
+
setLayoutControl(handler) { this.layoutControl = handler; }
|
|
122
127
|
fleetSpawner;
|
|
123
128
|
fleetAuditor;
|
|
124
129
|
constructor(stateDir, session, log) {
|
|
@@ -235,7 +240,10 @@ export class RoleControlServer {
|
|
|
235
240
|
protocolVersion: this.session.conversationPage ? 3 : 2,
|
|
236
241
|
features: [
|
|
237
242
|
'events_since', 'observer_follow', 'retained_range',
|
|
243
|
+
...(this.correspondence ? ['agent_correspondence'] : []),
|
|
238
244
|
...(this.session.conversationPage ? ['conversation_v3'] : []),
|
|
245
|
+
...(this.session.submitPromptBrowser ? ['generation_bound_prompts'] : []),
|
|
246
|
+
...(this.session.liveVoiceSupported ? ['idle_bound_prompts', 'targeted_voice_interrupt'] : []),
|
|
239
247
|
...(this.session.capabilities?.steering ? ['steering'] : []),
|
|
240
248
|
...(this.session.capabilities?.permissions ? ['permissions'] : []),
|
|
241
249
|
...(this.session.capabilities?.messagePhases ? ['message_phases'] : []),
|
|
@@ -308,6 +316,13 @@ export class RoleControlServer {
|
|
|
308
316
|
});
|
|
309
317
|
return;
|
|
310
318
|
}
|
|
319
|
+
case 'layout_control': {
|
|
320
|
+
if (request.version !== 2 || !request.layout || !this.layoutControl)
|
|
321
|
+
throw new SessionControlError('rejected', 'layout control unavailable');
|
|
322
|
+
const result = await this.layoutControl(request.layout);
|
|
323
|
+
this.write(socket, { version: 1, id: request.id, ok: true, result });
|
|
324
|
+
return;
|
|
325
|
+
}
|
|
311
326
|
case 'fleet_spawn': {
|
|
312
327
|
if (request.version !== 2 || !request.spawn || typeof request.spawn.name !== 'string')
|
|
313
328
|
throw new SessionControlError('rejected', 'version 2 and typed spawn options are required');
|
|
@@ -362,6 +377,23 @@ export class RoleControlServer {
|
|
|
362
377
|
this.write(socket, { version: 1, id: request.id, ok: true, result: { delivered: true } });
|
|
363
378
|
return;
|
|
364
379
|
}
|
|
380
|
+
case 'agent_contacts':
|
|
381
|
+
case 'agent_history': {
|
|
382
|
+
if (!this.correspondence)
|
|
383
|
+
throw new SessionControlError('rejected', 'Supervisor correspondence API is unavailable; this session needs an updated supervisor.');
|
|
384
|
+
const query = request.agentHistory;
|
|
385
|
+
if (request.command === 'agent_history' && (!query || !/^[a-f0-9]{64}$/i.test(query.peer_cid) || (query.limit !== undefined && (!Number.isInteger(query.limit) || query.limit < 1 || query.limit > 200)) || (query.before_seq !== undefined && (!Number.isSafeInteger(query.before_seq) || query.before_seq < 1))))
|
|
386
|
+
throw new SessionControlError('rejected', 'Valid peer_cid, limit (1–200) and before_seq are required.');
|
|
387
|
+
let result;
|
|
388
|
+
try {
|
|
389
|
+
result = request.command === 'agent_contacts' ? await this.correspondence.contacts() : await this.correspondence.history(query);
|
|
390
|
+
}
|
|
391
|
+
catch {
|
|
392
|
+
throw new SessionControlError('rejected', 'Supervisor correspondence read failed; identity or transport is unavailable.');
|
|
393
|
+
}
|
|
394
|
+
this.write(socket, { version: 1, id: request.id, ok: true, result });
|
|
395
|
+
return;
|
|
396
|
+
}
|
|
365
397
|
case 'owner_channel_manage': {
|
|
366
398
|
if (!request.ownerChannel || typeof request.ownerChannel.action !== 'string')
|
|
367
399
|
throw new SessionControlError('rejected', 'owner-channel management action is required');
|
|
@@ -421,7 +453,10 @@ export class RoleControlServer {
|
|
|
421
453
|
socket.write(JSON.stringify({ version: 1, conversationEvent: event }) + '\n');
|
|
422
454
|
}) };
|
|
423
455
|
}
|
|
456
|
+
case 'submit_voice_prompt':
|
|
424
457
|
case 'submit_prompt_v2': {
|
|
458
|
+
if (request.command === 'submit_voice_prompt' && !request.expectedSessionGeneration?.trim())
|
|
459
|
+
throw new SessionControlError('rejected', 'expectedSessionGeneration is required');
|
|
425
460
|
this.requireConversation(request);
|
|
426
461
|
if (!request.commandId?.trim() || !request.text?.trim() || !request.actor?.trim()
|
|
427
462
|
|| request.source !== 'owner_admin_console')
|
|
@@ -432,6 +467,7 @@ export class RoleControlServer {
|
|
|
432
467
|
const receipt = await this.session.submitPromptBrowser({
|
|
433
468
|
commandId: request.commandId, text: request.text,
|
|
434
469
|
source: 'owner_admin_console', actorBrowserSession: request.actor,
|
|
470
|
+
expectedSessionGeneration: request.expectedSessionGeneration, requireIdle: request.requireIdle === true,
|
|
435
471
|
});
|
|
436
472
|
this.write(socket, { version: 1, id: request.id, ok: true, result: receipt });
|
|
437
473
|
}
|
|
@@ -442,6 +478,29 @@ export class RoleControlServer {
|
|
|
442
478
|
}
|
|
443
479
|
return;
|
|
444
480
|
}
|
|
481
|
+
case 'interrupt_voice_v2': {
|
|
482
|
+
this.requireConversation(request);
|
|
483
|
+
if (!request.commandId?.trim() || !request.expectedSessionGeneration?.trim() || !request.promptId?.trim())
|
|
484
|
+
throw new SessionControlError('rejected', 'commandId, expectedSessionGeneration and promptId are required');
|
|
485
|
+
if (!this.session.interruptPrompt)
|
|
486
|
+
throw new SessionControlError('rejected', 'targeted voice interrupt unavailable');
|
|
487
|
+
const key = 'voice:' + request.commandId;
|
|
488
|
+
const signature = JSON.stringify([request.expectedSessionGeneration, request.promptId]);
|
|
489
|
+
const existing = this.interruptCommands.get(key);
|
|
490
|
+
if (existing) {
|
|
491
|
+
if (existing.signature !== signature)
|
|
492
|
+
throw new SessionControlError('rejected', 'idempotency_conflict: interrupt target changed');
|
|
493
|
+
this.write(socket, { version: 1, id: request.id, ok: true, result: existing.receipt });
|
|
494
|
+
return;
|
|
495
|
+
}
|
|
496
|
+
const outcome = await this.session.interruptPrompt(request.expectedSessionGeneration, request.promptId);
|
|
497
|
+
const receipt = { accepted: true, commandId: request.commandId, ...outcome };
|
|
498
|
+
this.interruptCommands.set(key, { signature, receipt });
|
|
499
|
+
if (this.interruptCommands.size > 200)
|
|
500
|
+
this.interruptCommands.delete(this.interruptCommands.keys().next().value);
|
|
501
|
+
this.write(socket, { version: 1, id: request.id, ok: true, result: receipt });
|
|
502
|
+
return;
|
|
503
|
+
}
|
|
445
504
|
case 'interrupt_v2': {
|
|
446
505
|
this.requireConversation(request);
|
|
447
506
|
if (!request.commandId?.trim())
|
|
@@ -532,7 +591,13 @@ export async function controlRequest(stateDir, request, timeoutMs = 120_000) {
|
|
|
532
591
|
socket.once('error', error => {
|
|
533
592
|
clearTimeout(timer);
|
|
534
593
|
const code = error.code;
|
|
535
|
-
|
|
594
|
+
const permissionHint = code === 'EPERM' || code === 'EACCES'
|
|
595
|
+
? `; the operating system denied access before '${request.command}' reached the supervisor. `
|
|
596
|
+
+ 'Check socket permissions and the command execution sandbox. '
|
|
597
|
+
+ 'An existing socket does not prove this process can connect; do not bypass Fleet audit. '
|
|
598
|
+
+ 'For managed workspace sessions, see docs/validation/managed-cli-permissions.md.'
|
|
599
|
+
: '';
|
|
600
|
+
reject(new SessionControlError(code === 'ENOENT' || code === 'ECONNREFUSED' ? 'control-unavailable' : 'backend', `role control socket: ${error.message}${permissionHint}`));
|
|
536
601
|
});
|
|
537
602
|
socket.on('data', chunk => {
|
|
538
603
|
buffer += chunk;
|