@ours.network/fleet 1.2.0-nightly.9 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +280 -68
- package/dist/account-origin.d.ts +5 -0
- package/dist/account-origin.js +31 -0
- package/dist/agent-ours/bridge.js +26 -9
- package/dist/agent-ours/correspondence.d.ts +10 -0
- package/dist/agent-ours/correspondence.js +1 -0
- package/dist/agent-ours/file-rpc.d.ts +12 -0
- package/dist/agent-ours/file-rpc.js +18 -4
- package/dist/agent-ours/harness.js +9 -1
- package/dist/agent-ours/mcp-endpoint.d.ts +22 -0
- package/dist/agent-ours/mcp-endpoint.js +101 -25
- package/dist/agent-ours/runtime.d.ts +9 -1
- package/dist/agent-ours/runtime.js +50 -2
- package/dist/agent-ours/service.d.ts +27 -1
- package/dist/agent-ours/service.js +107 -13
- package/dist/application/codex-models.d.ts +15 -0
- package/dist/application/codex-models.js +56 -0
- package/dist/application/conversation-history.d.ts +31 -0
- package/dist/application/conversation-history.js +339 -0
- package/dist/application/conversation-role-observer.d.ts +62 -0
- package/dist/application/conversation-role-observer.js +206 -0
- package/dist/application/fleet-query-service.d.ts +1 -1
- package/dist/application/fleet-query-service.js +3 -3
- package/dist/application/log-service.js +8 -8
- package/dist/application/model-catalog.d.ts +1 -1
- package/dist/application/model-catalog.js +5 -2
- package/dist/application/preset-provenance.d.ts +19 -0
- package/dist/application/preset-provenance.js +59 -0
- package/dist/application/resource-observer.d.ts +43 -0
- package/dist/application/resource-observer.js +323 -0
- package/dist/application/role-creation-service.js +3 -2
- package/dist/application/role-removal-service.js +17 -3
- package/dist/application/role-repository.d.ts +3 -1
- package/dist/application/role-repository.js +89 -54
- package/dist/application/room-layout-definitions.d.ts +45 -0
- package/dist/application/room-layout-definitions.js +130 -0
- package/dist/application/session-control.d.ts +63 -17
- package/dist/application/session-control.js +221 -23
- package/dist/application/supervisor-ours-tools.js +5 -11
- package/dist/application/task-layouts.d.ts +91 -0
- package/dist/application/task-layouts.js +261 -0
- package/dist/application/task-room-service.d.ts +101 -3
- package/dist/application/task-room-service.js +286 -33
- package/dist/application/types.d.ts +2 -0
- package/dist/atomic-file.d.ts +34 -1
- package/dist/atomic-file.js +161 -18
- package/dist/briefing.d.ts +3 -1
- package/dist/briefing.js +120 -31
- package/dist/build-info.json +10 -5
- package/dist/capabilities.d.ts +9 -1
- package/dist/capabilities.js +13 -0
- package/dist/cli.js +391 -38
- package/dist/client-profile.d.ts +5 -17
- package/dist/client-profile.js +4 -127
- package/dist/config.d.ts +45 -0
- package/dist/config.js +57 -6
- package/dist/creation.d.ts +1 -1
- package/dist/creation.js +4 -4
- package/dist/daemon-recovery.d.ts +14 -9
- package/dist/daemon-recovery.js +124 -115
- package/dist/docs.d.ts +1 -1
- package/dist/docs.js +214 -20
- package/dist/doctor.d.ts +4 -1
- package/dist/doctor.js +75 -60
- package/dist/erased-resources.d.ts +9 -0
- package/dist/erased-resources.js +87 -0
- package/dist/file-delivery/reader.d.ts +15 -0
- package/dist/file-delivery/reader.js +63 -0
- package/dist/file-delivery/store.d.ts +15 -0
- package/dist/file-delivery/store.js +124 -0
- package/dist/file-delivery/types.d.ts +19 -0
- package/dist/file-delivery/types.js +1 -0
- package/dist/fleet-command-audit.d.ts +13 -0
- package/dist/fleet-command-audit.js +31 -9
- package/dist/harness/agent-session.d.ts +4 -0
- package/dist/harness/claude-code-session.js +1 -1
- package/dist/harness/claude-code.js +21 -11
- package/dist/harness/claude-runtime.d.ts +12 -0
- package/dist/harness/claude-runtime.js +43 -0
- package/dist/harness/codex-app-server-proxy.d.ts +4 -1
- package/dist/harness/codex-app-server-proxy.js +41 -5
- package/dist/harness/codex-reasoning.d.ts +5 -0
- package/dist/harness/codex-reasoning.js +12 -0
- package/dist/harness/codex-runtime.d.ts +11 -1
- package/dist/harness/codex-runtime.js +31 -4
- package/dist/harness/codex-session.js +2 -2
- package/dist/harness/codex.js +24 -12
- package/dist/init-wizard.d.ts +2 -0
- package/dist/init-wizard.js +38 -19
- package/dist/isolation/policy.js +6 -1
- package/dist/isolation/types.d.ts +5 -0
- package/dist/managed-cli.d.ts +253 -0
- package/dist/managed-cli.js +923 -0
- package/dist/managed-recovery.d.ts +13 -0
- package/dist/managed-recovery.js +31 -0
- package/dist/minimal-setup.d.ts +2 -0
- package/dist/minimal-setup.js +44 -0
- package/dist/monitor.d.ts +9 -34
- package/dist/monitor.js +90 -153
- package/dist/notifications/fleet-producer.d.ts +31 -0
- package/dist/notifications/fleet-producer.js +351 -0
- package/dist/notifications/outbox.d.ts +59 -0
- package/dist/notifications/outbox.js +199 -0
- package/dist/notifications/target-cleanup.d.ts +16 -0
- package/dist/notifications/target-cleanup.js +75 -0
- package/dist/notifications/task-producer.d.ts +20 -0
- package/dist/notifications/task-producer.js +100 -0
- package/dist/ops.d.ts +2 -0
- package/dist/ops.js +20 -3
- package/dist/owner-channel/channel.js +17 -6
- package/dist/owner-channel/commands.d.ts +4 -0
- package/dist/owner-channel/commands.js +8 -0
- package/dist/owner-channel/lifecycle-outbox.js +4 -3
- package/dist/owner-channel/ours-client.js +2 -17
- package/dist/paths.d.ts +5 -0
- package/dist/paths.js +5 -0
- package/dist/preset-migration.js +55 -0
- package/dist/process-incarnation.d.ts +23 -0
- package/dist/process-incarnation.js +42 -0
- package/dist/reminders/manager.d.ts +75 -0
- package/dist/reminders/manager.js +278 -0
- package/dist/reminders/runtime.d.ts +27 -0
- package/dist/reminders/runtime.js +32 -0
- package/dist/reminders/schedule.d.ts +37 -0
- package/dist/reminders/schedule.js +104 -0
- package/dist/reminders/store.d.ts +341 -0
- package/dist/reminders/store.js +762 -0
- package/dist/reminders/tools.d.ts +20 -0
- package/dist/reminders/tools.js +114 -0
- package/dist/resolved-plan.js +1 -0
- package/dist/rooms-tasks/add-member.d.ts +48 -0
- package/dist/rooms-tasks/add-member.js +84 -0
- package/dist/rooms-tasks/archived-absence.d.ts +4 -0
- package/dist/rooms-tasks/archived-absence.js +11 -0
- package/dist/rooms-tasks/cli.js +182 -5
- package/dist/rooms-tasks/close.d.ts +5 -2
- package/dist/rooms-tasks/close.js +224 -36
- package/dist/rooms-tasks/config.d.ts +1 -1
- package/dist/rooms-tasks/config.js +11 -4
- package/dist/rooms-tasks/cowork-adapter.d.ts +12 -5
- package/dist/rooms-tasks/cowork-adapter.js +15 -121
- package/dist/rooms-tasks/cowork-http.js +56 -6
- package/dist/rooms-tasks/deletion.d.ts +1 -1
- package/dist/rooms-tasks/deletion.js +66 -10
- package/dist/rooms-tasks/erasure.d.ts +6 -0
- package/dist/rooms-tasks/erasure.js +173 -0
- package/dist/rooms-tasks/external-worker.d.ts +1 -1
- package/dist/rooms-tasks/external-worker.js +3 -4
- package/dist/rooms-tasks/layout-binding-client.d.ts +11 -0
- package/dist/rooms-tasks/layout-binding-client.js +56 -0
- package/dist/rooms-tasks/layout-binding-grants.d.ts +21 -0
- package/dist/rooms-tasks/layout-binding-grants.js +128 -0
- package/dist/rooms-tasks/layout-cli.d.ts +4 -0
- package/dist/rooms-tasks/layout-cli.js +107 -0
- package/dist/rooms-tasks/layout-config.d.ts +11 -0
- package/dist/rooms-tasks/layout-config.js +89 -0
- package/dist/rooms-tasks/layout-control.d.ts +21 -0
- package/dist/rooms-tasks/layout-control.js +52 -0
- package/dist/rooms-tasks/layout-member-retirement.d.ts +10 -0
- package/dist/rooms-tasks/layout-member-retirement.js +90 -0
- package/dist/rooms-tasks/layout-service.d.ts +43 -0
- package/dist/rooms-tasks/layout-service.js +203 -0
- package/dist/rooms-tasks/layout.d.ts +121 -0
- package/dist/rooms-tasks/layout.js +286 -0
- package/dist/rooms-tasks/legacy-task-member.d.ts +19 -0
- package/dist/rooms-tasks/legacy-task-member.js +116 -0
- package/dist/rooms-tasks/live-readiness.d.ts +18 -0
- package/dist/rooms-tasks/live-readiness.js +85 -0
- package/dist/rooms-tasks/member-ownership.d.ts +5 -0
- package/dist/rooms-tasks/member-ownership.js +40 -0
- package/dist/rooms-tasks/member-startup.d.ts +1 -0
- package/dist/rooms-tasks/member-startup.js +2 -0
- package/dist/rooms-tasks/provision.d.ts +3 -0
- package/dist/rooms-tasks/provision.js +101 -49
- package/dist/rooms-tasks/recovery.d.ts +19 -0
- package/dist/rooms-tasks/recovery.js +110 -0
- package/dist/rooms-tasks/room-state.d.ts +4 -2
- package/dist/rooms-tasks/room-state.js +47 -8
- package/dist/rooms-tasks/task-review.d.ts +113 -0
- package/dist/rooms-tasks/task-review.js +561 -0
- package/dist/rooms-tasks/task-service-retirement.d.ts +11 -0
- package/dist/rooms-tasks/task-service-retirement.js +21 -0
- package/dist/rooms-tasks/task-state.d.ts +51 -5
- package/dist/rooms-tasks/task-state.js +358 -50
- package/dist/rooms-tasks/task-tools.d.ts +35 -0
- package/dist/rooms-tasks/task-tools.js +134 -0
- package/dist/rooms-tasks/types.d.ts +78 -0
- package/dist/rooms-tasks/workspace-artifacts.d.ts +6 -0
- package/dist/rooms-tasks/workspace-artifacts.js +93 -0
- package/dist/rooms-tasks/workspace.d.ts +35 -0
- package/dist/rooms-tasks/workspace.js +325 -0
- package/dist/runner.d.ts +23 -4
- package/dist/runner.js +409 -61
- package/dist/session/acp.d.ts +75 -2
- package/dist/session/acp.js +345 -82
- package/dist/session/arbiter.d.ts +98 -0
- package/dist/session/arbiter.js +272 -3
- package/dist/session/codex-app-server.d.ts +45 -1
- package/dist/session/codex-app-server.js +225 -32
- package/dist/session/control.d.ts +17 -3
- package/dist/session/control.js +219 -23
- package/dist/session/conversation-normalizer.d.ts +2 -1
- package/dist/session/conversation-normalizer.js +1 -1
- package/dist/session/conversation-observation.d.ts +25 -0
- package/dist/session/conversation-observation.js +100 -0
- package/dist/session/conversation-replay.d.ts +50 -0
- package/dist/session/conversation-replay.js +203 -0
- package/dist/session/conversation-store.d.ts +15 -1
- package/dist/session/conversation-store.js +213 -26
- package/dist/session/conversation-types.d.ts +57 -4
- package/dist/session/prompt-batch.d.ts +79 -0
- package/dist/session/prompt-batch.js +120 -0
- package/dist/session/task-notice.d.ts +14 -0
- package/dist/session/task-notice.js +13 -0
- package/dist/session/types.d.ts +42 -1
- package/dist/setup-tunnel-preflight.d.ts +34 -0
- package/dist/setup-tunnel-preflight.js +62 -0
- package/dist/socket-path.d.ts +2 -0
- package/dist/socket-path.js +22 -0
- package/dist/spawn.d.ts +5 -1
- package/dist/spawn.js +40 -4
- package/dist/startup-prompt.d.ts +15 -0
- package/dist/startup-prompt.js +71 -0
- package/dist/startup-readiness.d.ts +20 -1
- package/dist/startup-readiness.js +66 -3
- package/dist/startup-telemetry.d.ts +16 -0
- package/dist/startup-telemetry.js +46 -0
- package/dist/subscriptions/cli.d.ts +91 -0
- package/dist/subscriptions/cli.js +223 -0
- package/dist/subscriptions/launch.d.ts +37 -0
- package/dist/subscriptions/launch.js +93 -0
- package/dist/subscriptions/login.d.ts +77 -0
- package/dist/subscriptions/login.js +215 -0
- package/dist/subscriptions/observed.d.ts +7 -0
- package/dist/subscriptions/observed.js +44 -0
- package/dist/subscriptions/probe.d.ts +10 -0
- package/dist/subscriptions/probe.js +73 -0
- package/dist/subscriptions/service.d.ts +102 -0
- package/dist/subscriptions/service.js +364 -0
- package/dist/subscriptions/store.d.ts +71 -0
- package/dist/subscriptions/store.js +143 -0
- package/dist/supervisor/adoption.d.ts +8 -0
- package/dist/supervisor/adoption.js +78 -0
- package/dist/supervisor/catalog.d.ts +62 -0
- package/dist/supervisor/catalog.js +254 -0
- package/dist/supervisor/fleet.d.ts +8 -0
- package/dist/supervisor/fleet.js +89 -0
- package/dist/supervisor/index.js +3 -6
- package/dist/supervisor/launchd.d.ts +1 -1
- package/dist/supervisor/launchd.js +26 -13
- package/dist/supervisor/legacy.d.ts +35 -0
- package/dist/supervisor/legacy.js +524 -0
- package/dist/supervisor/manager.d.ts +15 -0
- package/dist/supervisor/manager.js +270 -0
- package/dist/supervisor/registration-refresh.d.ts +45 -0
- package/dist/supervisor/registration-refresh.js +266 -0
- package/dist/supervisor/scope.d.ts +4 -0
- package/dist/supervisor/scope.js +10 -0
- package/dist/supervisor/systemd.d.ts +2 -2
- package/dist/supervisor/systemd.js +18 -37
- package/dist/task-supervision.d.ts +9 -0
- package/dist/task-supervision.js +145 -0
- package/dist/temp-idle.d.ts +17 -0
- package/dist/temp-idle.js +42 -0
- package/dist/temp-lifecycle.d.ts +23 -8
- package/dist/temp-lifecycle.js +123 -66
- package/dist/temp-supervisor-recovery.d.ts +1 -1
- package/dist/temp-supervisor-recovery.js +2 -2
- package/dist/tunnel-setup-completion.d.ts +7 -0
- package/dist/tunnel-setup-completion.js +13 -0
- package/dist/tunnel-setup-input.d.ts +19 -0
- package/dist/tunnel-setup-input.js +130 -0
- package/dist/watchdog/query.d.ts +3 -1
- package/dist/watchdog/query.js +2 -1
- package/dist/web/agent-attachments.d.ts +29 -0
- package/dist/web/agent-attachments.js +162 -0
- package/dist/web/auth.d.ts +65 -2
- package/dist/web/auth.js +201 -11
- package/dist/web/available-port.d.ts +2 -0
- package/dist/web/available-port.js +23 -0
- package/dist/web/control.d.ts +3 -3
- package/dist/web/control.js +10 -8
- package/dist/web/conversation-stream-codec.d.ts +23 -0
- package/dist/web/conversation-stream-codec.js +110 -0
- package/dist/web/conversation-stream.d.ts +26 -0
- package/dist/web/conversation-stream.js +239 -0
- package/dist/web/events.d.ts +2 -0
- package/dist/web/events.js +9 -0
- package/dist/web/fleet-config-service.d.ts +7 -1
- package/dist/web/fleet-config-service.js +61 -16
- package/dist/web/prefix-gateway.d.ts +32 -0
- package/dist/web/prefix-gateway.js +248 -0
- package/dist/web/resource-views.d.ts +138 -0
- package/dist/web/resource-views.js +519 -0
- package/dist/web/runtime.d.ts +3 -1
- package/dist/web/runtime.js +132 -34
- package/dist/web/server.d.ts +19 -0
- package/dist/web/server.js +893 -71
- package/dist/web/service.d.ts +11 -2
- package/dist/web/service.js +30 -4
- package/dist/web/workspace-devices.d.ts +107 -0
- package/dist/web/workspace-devices.js +277 -0
- package/dist/web/workspace-tunnel.d.ts +1 -0
- package/dist/web/workspace-tunnel.js +34 -0
- package/dist/workspace-enrollment.d.ts +83 -0
- package/dist/workspace-enrollment.js +605 -0
- package/dist/workspace-replacement.d.ts +66 -0
- package/dist/workspace-replacement.js +238 -0
- package/docs/acp-files.md +146 -0
- package/docs/conversation-stream.md +216 -0
- package/docs/fleet-migration.md +54 -0
- package/docs/live-voice.md +13 -0
- package/docs/room-layouts.md +227 -0
- package/docs/self-reminders.md +374 -0
- package/docs/task-progress-review.md +143 -0
- package/docs/task-workspaces.md +225 -0
- package/docs/validation/managed-cli-permissions.md +421 -0
- package/examples/room-layouts/fresh.yaml +27 -0
- package/examples/room-layouts/scoped.yaml +21 -0
- package/examples/room-layouts/shared.yaml +19 -0
- package/package.json +25 -20
- package/presets/README.md +78 -0
- package/presets/brain-catalog.json +8 -2
- package/presets/fleet/agent_templates/Critic.yaml +1 -1
- package/presets/fleet/agent_templates/Developer.yaml +1 -1
- package/presets/fleet/agent_templates/Engineer.yaml +5 -0
- package/presets/fleet/agent_templates/LocalCoordinator.yaml +6 -3
- package/presets/fleet/agents/FleetCoordinator.yaml +1 -1
- package/presets/fleet/brains/claude-fable-5-1-high.yaml +5 -0
- package/presets/fleet/brains/claude-fable-5-1-low.yaml +5 -0
- package/presets/fleet/brains/claude-fable-5-1-max.yaml +5 -0
- package/presets/fleet/brains/claude-fable-5-1-medium.yaml +5 -0
- package/presets/fleet/brains/claude-fable-5-1-xhigh.yaml +5 -0
- package/presets/fleet/brains/claude-fable-5-high.yaml +1 -1
- package/presets/fleet/brains/claude-fable-5-low.yaml +1 -1
- package/presets/fleet/brains/claude-fable-5-max.yaml +1 -1
- package/presets/fleet/brains/claude-fable-5-medium.yaml +1 -1
- package/presets/fleet/brains/claude-fable-5-xhigh.yaml +1 -1
- package/presets/fleet/brains/claude-haiku-4-5-20251001-default.yaml +4 -0
- package/presets/fleet/brains/claude-opus-5-5-high.yaml +5 -0
- package/presets/fleet/brains/claude-opus-5-5-low.yaml +5 -0
- package/presets/fleet/brains/claude-opus-5-5-max.yaml +5 -0
- package/presets/fleet/brains/claude-opus-5-5-medium.yaml +5 -0
- package/presets/fleet/brains/claude-opus-5-5-xhigh.yaml +5 -0
- package/presets/fleet/brains/claude-opus-5-high.yaml +1 -1
- package/presets/fleet/brains/claude-opus-5-low.yaml +1 -1
- package/presets/fleet/brains/claude-opus-5-max.yaml +1 -1
- package/presets/fleet/brains/claude-opus-5-medium.yaml +1 -1
- package/presets/fleet/brains/claude-opus-5-xhigh.yaml +1 -1
- package/presets/fleet/brains/claude-sonnet-5-high.yaml +1 -1
- package/presets/fleet/brains/claude-sonnet-5-low.yaml +1 -1
- package/presets/fleet/brains/claude-sonnet-5-max.yaml +1 -1
- package/presets/fleet/brains/claude-sonnet-5-medium.yaml +1 -1
- package/presets/fleet/brains/claude-sonnet-5-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-3-codex-spark-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-3-codex-spark-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-3-codex-spark-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-3-codex-spark-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-mini-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-mini-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-mini-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-mini-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-4-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-5-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-5-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-5-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-5-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-luna-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-luna-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-luna-max.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-luna-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-luna-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-sol-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-sol-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-sol-max.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-sol-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-sol-ultra.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-sol-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-terra-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-terra-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-terra-max.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-terra-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-terra-ultra.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-5-6-terra-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-6-1-sol-high.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-1-sol-low.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-1-sol-max.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-1-sol-medium.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-1-sol-ultra.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-1-sol-xhigh.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-astra-high.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-6-astra-low.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-6-astra-max.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-6-astra-medium.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-6-astra-ultra.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-6-astra-xhigh.yaml +1 -1
- package/presets/fleet/brains/codex-gpt-6-luna-high.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-luna-low.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-luna-max.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-luna-medium.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-luna-xhigh.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-sol-high.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-sol-low.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-sol-max.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-sol-medium.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-sol-ultra.yaml +5 -0
- package/presets/fleet/brains/codex-gpt-6-sol-xhigh.yaml +5 -0
- package/presets/fleet/roles/Coordinator.yaml +21 -13
- package/presets/fleet/roles/Critic.yaml +24 -0
- package/presets/fleet/roles/Developer.yaml +32 -0
- package/presets/fleet/roles/Engineer.yaml +72 -0
- package/presets/fleet/roles/LocalCoordinator.yaml +17 -0
- package/presets/fleet/room_layouts/engineering.yaml +25 -0
- package/presets/fleet/room_layouts/pair.yaml +24 -0
- package/presets/fleet/room_layouts/single.yaml +17 -0
- package/presets/fleet/room_layouts/team.yaml +30 -0
- package/presets/fleet/room_templates/engineering.yaml +19 -0
- package/presets/manifest.json +2 -2
- package/dist/web-app/assets/index-59GF-gsZ.css +0 -1
- package/dist/web-app/assets/index-C0h3ALvs.js +0 -10
- package/dist/web-app/icons/ours-fleet-maskable.svg +0 -4
- package/dist/web-app/icons/ours-fleet.svg +0 -4
- package/dist/web-app/index.html +0 -17
- package/dist/web-app/manifest.webmanifest +0 -15
- package/dist/web-app/offline.html +0 -18
- package/dist/web-app/sw.js +0 -51
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { createHash } from 'node:crypto';
|
|
2
|
+
import { existsSync, mkdirSync, readdirSync, readFileSync, rmSync } from 'node:fs';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { replaceFileAtomically } from '../atomic-file.js';
|
|
5
|
+
import { stateRoot } from '../paths.js';
|
|
6
|
+
import { producerConfig } from './outbox.js';
|
|
7
|
+
export const cleanupDir = () => join(stateRoot(), 'notifications', 'removed-targets');
|
|
8
|
+
/** Only a closed agent identity authorizes this producer's role-target cleanup. */
|
|
9
|
+
export function notificationAgentId(target) {
|
|
10
|
+
if (!target || typeof target !== 'object' || Array.isArray(target))
|
|
11
|
+
return null;
|
|
12
|
+
const value = target;
|
|
13
|
+
return Object.keys(value).every(key => ['kind', 'agentId'].includes(key)) && value.kind === 'agent'
|
|
14
|
+
&& typeof value.agentId === 'string' && /^[A-Za-z0-9_-]{1,120}$/.test(value.agentId) ? value.agentId : null;
|
|
15
|
+
}
|
|
16
|
+
export function hasPendingNotificationTargetCleanup(dir = cleanupDir()) {
|
|
17
|
+
if (!existsSync(dir))
|
|
18
|
+
return false;
|
|
19
|
+
return readdirSync(dir).filter(file => /^[a-f0-9]{64}\.json$/.test(file)).some(file => {
|
|
20
|
+
try {
|
|
21
|
+
const value = JSON.parse(readFileSync(join(dir, file), 'utf8'));
|
|
22
|
+
return !!value && typeof value === 'object' && Object.keys(value).length === 1 && !!notificationAgentId(value.target);
|
|
23
|
+
}
|
|
24
|
+
catch (error) {
|
|
25
|
+
return !(error instanceof SyntaxError);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
}
|
|
29
|
+
/** Queue only after the owning lifecycle has established deletion, never on a failed inventory read. */
|
|
30
|
+
export async function retireNotificationTarget(target, config = producerConfig(), dir = cleanupDir(), request = fetch) {
|
|
31
|
+
if (!config)
|
|
32
|
+
return;
|
|
33
|
+
if (!notificationAgentId(target))
|
|
34
|
+
throw new Error('Invalid notification target');
|
|
35
|
+
mkdirSync(dir, { recursive: true, mode: 0o700 });
|
|
36
|
+
replaceFileAtomically(join(dir, createHash('sha256').update(JSON.stringify({ kind: 'agent', agentId: target.agentId })).digest('hex') + '.json'), JSON.stringify({ target }) + '\n', 0o600);
|
|
37
|
+
await drainNotificationTargetCleanup(config, dir, request, { retain: true });
|
|
38
|
+
}
|
|
39
|
+
/** Persisted retries also run from the ordinary web notification producer after restart. */
|
|
40
|
+
export async function drainNotificationTargetCleanup(config, dir = cleanupDir(), request = fetch, options = {}) {
|
|
41
|
+
if (!existsSync(dir))
|
|
42
|
+
return;
|
|
43
|
+
let attempts = 0;
|
|
44
|
+
for (const file of readdirSync(dir).filter(file => /^[a-f0-9]{64}\.json$/.test(file))) {
|
|
45
|
+
if (attempts >= 32)
|
|
46
|
+
break;
|
|
47
|
+
const path = join(dir, file);
|
|
48
|
+
let value;
|
|
49
|
+
try {
|
|
50
|
+
value = JSON.parse(readFileSync(path, 'utf8'));
|
|
51
|
+
}
|
|
52
|
+
catch {
|
|
53
|
+
continue;
|
|
54
|
+
}
|
|
55
|
+
if (!value || typeof value !== 'object' || Array.isArray(value) || Object.keys(value).length !== 1 || !notificationAgentId(value.target))
|
|
56
|
+
continue;
|
|
57
|
+
try {
|
|
58
|
+
attempts++;
|
|
59
|
+
if (options.beforeDelete && !(await options.beforeDelete(value.target))) {
|
|
60
|
+
rmSync(path, { force: true });
|
|
61
|
+
continue;
|
|
62
|
+
}
|
|
63
|
+
const response = await request(config.origin + '/api/v1/delete-target', { method: 'POST', redirect: 'error', signal: AbortSignal.timeout(10000),
|
|
64
|
+
headers: { ...(config.gatewayCredential ? { 'X-Ours-Api-Token': config.gatewayCredential, 'X-Ours-Notifications-Producer': config.token } : { Authorization: `Bearer ${config.token}` }), 'Content-Type': 'application/json' }, body: JSON.stringify(value) });
|
|
65
|
+
await response.body?.cancel();
|
|
66
|
+
if (!response.ok)
|
|
67
|
+
break;
|
|
68
|
+
if (!options.retain)
|
|
69
|
+
rmSync(path, { force: true });
|
|
70
|
+
}
|
|
71
|
+
catch {
|
|
72
|
+
break; /* No credentials or response bodies are included in logs. */
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { ProducerConfig } from './outbox.js';
|
|
2
|
+
/** The Fleet manager owns this sender even when the web console is stopped.
|
|
3
|
+
* Events are already durable in their owning task commit, so no second outbox
|
|
4
|
+
* admission/crash window is needed. A failed task does not block other tasks. */
|
|
5
|
+
export declare class TaskNotificationProducer {
|
|
6
|
+
private readonly config;
|
|
7
|
+
private readonly warn;
|
|
8
|
+
private readonly request;
|
|
9
|
+
private timer?;
|
|
10
|
+
private dirty;
|
|
11
|
+
private running?;
|
|
12
|
+
private offset;
|
|
13
|
+
private stopped;
|
|
14
|
+
private readonly shutdown;
|
|
15
|
+
constructor(config: ProducerConfig, warn?: (line: string) => void, request?: typeof fetch);
|
|
16
|
+
drain(): Promise<void>;
|
|
17
|
+
private retry;
|
|
18
|
+
private drainOnce;
|
|
19
|
+
close(): Promise<void>;
|
|
20
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
import { observeFleetResources } from '../application/resource-observer.js';
|
|
2
|
+
import { tasksDir } from '../rooms-tasks/task-state.js';
|
|
3
|
+
import { acknowledgeTaskNotification, pendingTaskNotifications } from '../rooms-tasks/task-state.js';
|
|
4
|
+
/** The Fleet manager owns this sender even when the web console is stopped.
|
|
5
|
+
* Events are already durable in their owning task commit, so no second outbox
|
|
6
|
+
* admission/crash window is needed. A failed task does not block other tasks. */
|
|
7
|
+
export class TaskNotificationProducer {
|
|
8
|
+
config;
|
|
9
|
+
warn;
|
|
10
|
+
request;
|
|
11
|
+
timer;
|
|
12
|
+
dirty = false;
|
|
13
|
+
running;
|
|
14
|
+
offset = 0;
|
|
15
|
+
stopped = false;
|
|
16
|
+
shutdown = new AbortController();
|
|
17
|
+
constructor(config, warn = () => { }, request = fetch) {
|
|
18
|
+
this.config = config;
|
|
19
|
+
this.warn = warn;
|
|
20
|
+
this.request = request;
|
|
21
|
+
// Subscribe before startup replay: a commit while fetch is pending needs a second pass.
|
|
22
|
+
observeFleetResources({ retryWatchFailures: true, paths: [tasksDir()], roots: [] }, state => {
|
|
23
|
+
if (state.health !== 'healthy')
|
|
24
|
+
this.warn('Task notification observation unavailable; committed events retained');
|
|
25
|
+
else {
|
|
26
|
+
this.dirty = true;
|
|
27
|
+
void this.drain();
|
|
28
|
+
}
|
|
29
|
+
}, this.shutdown.signal);
|
|
30
|
+
void this.drain();
|
|
31
|
+
}
|
|
32
|
+
drain() {
|
|
33
|
+
if (this.stopped)
|
|
34
|
+
return Promise.resolve();
|
|
35
|
+
if (this.running)
|
|
36
|
+
return this.running;
|
|
37
|
+
this.dirty = false;
|
|
38
|
+
clearTimeout(this.timer);
|
|
39
|
+
this.timer = undefined;
|
|
40
|
+
this.running = this.drainOnce().catch(() => {
|
|
41
|
+
this.warn('Task notification snapshot unavailable; committed events retained');
|
|
42
|
+
this.retry();
|
|
43
|
+
}).finally(() => {
|
|
44
|
+
this.running = undefined;
|
|
45
|
+
if (this.dirty && !this.stopped) {
|
|
46
|
+
this.dirty = false;
|
|
47
|
+
void this.drain();
|
|
48
|
+
}
|
|
49
|
+
});
|
|
50
|
+
return this.running;
|
|
51
|
+
}
|
|
52
|
+
retry() {
|
|
53
|
+
if (this.stopped || this.timer)
|
|
54
|
+
return;
|
|
55
|
+
this.timer = setTimeout(() => { this.timer = undefined; void this.drain(); }, 3000);
|
|
56
|
+
this.timer.unref();
|
|
57
|
+
}
|
|
58
|
+
async drainOnce() {
|
|
59
|
+
if (this.stopped)
|
|
60
|
+
return;
|
|
61
|
+
const pending = pendingTaskNotifications(this.warn);
|
|
62
|
+
if (!pending.length)
|
|
63
|
+
return;
|
|
64
|
+
const start = this.offset % pending.length;
|
|
65
|
+
const batch = [...pending.slice(start), ...pending.slice(0, start)].slice(0, 32);
|
|
66
|
+
this.offset = start + batch.length;
|
|
67
|
+
// Retry only outstanding work; never run an idle discovery timer.
|
|
68
|
+
this.retry();
|
|
69
|
+
for (let index = 0; index < batch.length; index += 4) {
|
|
70
|
+
if (this.stopped)
|
|
71
|
+
break;
|
|
72
|
+
await Promise.all(batch.slice(index, index + 4).map(async (task) => {
|
|
73
|
+
for (const event of task.events.slice(0, 32)) {
|
|
74
|
+
if (this.stopped)
|
|
75
|
+
break;
|
|
76
|
+
try {
|
|
77
|
+
const response = await this.request(this.config.origin + '/api/v1/send', {
|
|
78
|
+
method: 'POST', redirect: 'error', signal: AbortSignal.any([this.shutdown.signal, AbortSignal.timeout(10_000)]),
|
|
79
|
+
headers: { ...(this.config.gatewayCredential
|
|
80
|
+
? { 'X-Ours-Api-Token': this.config.gatewayCredential, 'X-Ours-Notifications-Producer': this.config.token }
|
|
81
|
+
: { Authorization: `Bearer ${this.config.token}` }), 'Content-Type': 'application/json' },
|
|
82
|
+
body: JSON.stringify(event),
|
|
83
|
+
});
|
|
84
|
+
await response.body?.cancel();
|
|
85
|
+
if (!response.ok) {
|
|
86
|
+
this.warn(`Task notification delivery pending (HTTP ${response.status}); committed event retained`);
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
acknowledgeTaskNotification(task.taskId, event.eventId);
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
this.warn('Task notification delivery pending; committed event retained');
|
|
93
|
+
break;
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
}));
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
async close() { this.stopped = true; clearTimeout(this.timer); this.shutdown.abort(); await this.running; }
|
|
100
|
+
}
|
package/dist/ops.d.ts
CHANGED
|
@@ -54,5 +54,7 @@ export declare function up(cfg: FleetConfig, names: string[], deps: OpsDeps, con
|
|
|
54
54
|
export declare function down(cfg: FleetConfig, names: string[], deps: OpsDeps): Promise<void>;
|
|
55
55
|
/** Re-sync from config + bounce. mode 'keep' resumes context; 'fresh' wipes it. */
|
|
56
56
|
export declare function restartRoles(cfg: FleetConfig, names: string[], deps: OpsDeps, mode: 'keep' | 'fresh', configPath?: string): Promise<void>;
|
|
57
|
+
/** Read-only admission check shared by CLI removal and web preview, before any archive. */
|
|
58
|
+
export declare function assertStandaloneRoleRemoval(cfg: FleetConfig, name: string): void;
|
|
57
59
|
/** Stop + forget a role: unit, state dir, and its fleet.d file when spawned. */
|
|
58
60
|
export declare function rmRole(cfg: FleetConfig, name: string, deps: OpsDeps): Promise<void>;
|
package/dist/ops.js
CHANGED
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { retireNotificationTarget } from './notifications/target-cleanup.js';
|
|
1
2
|
import { chmodSync, existsSync, mkdirSync, readFileSync, writeFileSync, rmSync, unlinkSync, } from 'node:fs';
|
|
2
3
|
import { join } from 'node:path';
|
|
3
4
|
import { randomUUID, createHash } from 'node:crypto';
|
|
@@ -5,8 +6,9 @@ import { agentDir, watchdogsRoot } from './paths.js';
|
|
|
5
6
|
import { findRole } from './config.js';
|
|
6
7
|
import { getAdapter } from './harness/registry.js';
|
|
7
8
|
import { generateBriefing } from './briefing.js';
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
9
|
+
import { managedCliBriefing } from './managed-cli.js';
|
|
10
|
+
import { resetRestartLedger, loadTempRole } from './runner.js';
|
|
11
|
+
import { archiveTempState, stopTempSupervisor, tempSupervisorLiveness, readTempSupervisor, } from './temp-lifecycle.js';
|
|
10
12
|
import { realExec } from './exec.js';
|
|
11
13
|
import { daemonIdentityProvisioner, reconcilePermanentRoleIdentities, } from './creation.js';
|
|
12
14
|
import { provesGeneratedAgentSource } from './generated-agent-source.js';
|
|
@@ -28,7 +30,7 @@ export function applyRole(role, opts = {}) {
|
|
|
28
30
|
// brought up from so the supervised process can reload the SAME file instead of
|
|
29
31
|
// silently falling back to the default ~/fleet.yaml. Temp roles snapshot their
|
|
30
32
|
// whole resolved role into role.yaml instead and don't need this.
|
|
31
|
-
if (!opts.temp)
|
|
33
|
+
if (!opts.temp || opts.configPath)
|
|
32
34
|
writeFileSync(join(dir, '.config-path'), (opts.configPath ?? '') + '\n');
|
|
33
35
|
writeFileSync(join(dir, '.identity'), role.identity + '\n');
|
|
34
36
|
if (role.cwd)
|
|
@@ -43,6 +45,7 @@ export function applyRole(role, opts = {}) {
|
|
|
43
45
|
routinesPath: join(dir, 'ROUTINES.md'), briefingBody,
|
|
44
46
|
identityGuarantee: opts.identityGuarantee,
|
|
45
47
|
temporaryIdentity: opts.temp === true,
|
|
48
|
+
managedCli: managedCliBriefing(role, opts.configPath, opts.temp === true),
|
|
46
49
|
}));
|
|
47
50
|
if (opts.fresh)
|
|
48
51
|
for (const f of ['.booted', '.session-id', '.exit-status'])
|
|
@@ -220,8 +223,21 @@ export async function restartRoles(cfg, names, deps, mode, configPath) {
|
|
|
220
223
|
: `↻ ${role.name} — restarted (resumes; briefing re-synced)`);
|
|
221
224
|
}
|
|
222
225
|
}
|
|
226
|
+
/** Read-only admission check shared by CLI removal and web preview, before any archive. */
|
|
227
|
+
export function assertStandaloneRoleRemoval(cfg, name) {
|
|
228
|
+
if (cfg.roles.some(role => role.name === name) || !/^[A-Za-z0-9_-]+$/.test(name))
|
|
229
|
+
return;
|
|
230
|
+
const dir = agentDir(name, true);
|
|
231
|
+
if (!existsSync(dir))
|
|
232
|
+
return;
|
|
233
|
+
const taskId = readTempSupervisor(dir)?.taskOwner?.taskId
|
|
234
|
+
?? (existsSync(join(dir, 'role.yaml')) ? loadTempRole(name).roomMemberStartup?.task_id : undefined);
|
|
235
|
+
if (taskId)
|
|
236
|
+
throw Error(`TASK_MEMBER_IS_TASK_OWNED: ${name} belongs to task ${taskId}; use the owning task lifecycle; if its record is missing or corrupt, preserve evidence and restore the original records before retrying task delete`);
|
|
237
|
+
}
|
|
223
238
|
/** Stop + forget a role: unit, state dir, and its fleet.d file when spawned. */
|
|
224
239
|
export async function rmRole(cfg, name, deps) {
|
|
240
|
+
assertStandaloneRoleRemoval(cfg, name);
|
|
225
241
|
const temporaryDir = /^[A-Za-z0-9_-]+$/.test(name) ? agentDir(name, true) : '';
|
|
226
242
|
const configured = cfg.roles.find(role => role.name === name);
|
|
227
243
|
if (!configured && temporaryDir && existsSync(temporaryDir)) {
|
|
@@ -255,6 +271,7 @@ export async function rmRole(cfg, name, deps) {
|
|
|
255
271
|
rmSync(agentDir(name), { recursive: true, force: true });
|
|
256
272
|
if (generatedSource && existsSync(role.sourceFile)) {
|
|
257
273
|
unlinkSync(role.sourceFile);
|
|
274
|
+
await retireNotificationTarget({ kind: 'agent', agentId: name }).catch(() => { });
|
|
258
275
|
deps.log(`removed ${role.sourceFile}`);
|
|
259
276
|
}
|
|
260
277
|
deps.log(`removed '${name}' (its ours identity is left intact)`);
|
|
@@ -883,8 +883,15 @@ export class OwnerChannel {
|
|
|
883
883
|
const recovered = [];
|
|
884
884
|
for (const claim of this.messageRecovery.list()) {
|
|
885
885
|
const item = await this.client.getHistoryItem(claim.wireId);
|
|
886
|
-
if (!item)
|
|
887
|
-
|
|
886
|
+
if (!item) {
|
|
887
|
+
// The daemon has definitively lost this body. Retrying the claim would
|
|
888
|
+
// block every later owner message, including after a supervisor restart.
|
|
889
|
+
// Keep transient lookup errors retryable; only an explicit absence skips.
|
|
890
|
+
this.messageRecovery.pruneHandled(wireId => wireId === claim.wireId);
|
|
891
|
+
this.options.log(`[${this.options.role}] skipped journaled owner message ${claim.wireId} `
|
|
892
|
+
+ `(seq=${claim.seq}): missing from persistent history`);
|
|
893
|
+
continue;
|
|
894
|
+
}
|
|
888
895
|
recovered.push(this.historyMessage(item, claim));
|
|
889
896
|
}
|
|
890
897
|
const listed = await this.client.listIncomingMessages();
|
|
@@ -1391,6 +1398,7 @@ export class OwnerChannel {
|
|
|
1391
1398
|
await this.fleetOps.provisionTask(task.task_id);
|
|
1392
1399
|
return outcome;
|
|
1393
1400
|
}),
|
|
1401
|
+
observeTaskReadiness: taskId => new TaskRoomApplicationService(this.options.configPath).observeTaskReadiness(taskId),
|
|
1394
1402
|
taskProvisioningOutcome: taskId => new TaskRoomApplicationService(this.options.configPath).taskProvisioningOutcome(taskId),
|
|
1395
1403
|
listTasks: filter => new TaskRoomApplicationService(this.options.configPath).listTasks(filter),
|
|
1396
1404
|
groupedTasks: filter => new TaskRoomApplicationService(this.options.configPath).groupedTasks(filter),
|
|
@@ -1977,6 +1985,9 @@ export class OwnerChannel {
|
|
|
1977
1985
|
const progressMs = this.options.config.progress_interval_ms;
|
|
1978
1986
|
let lastSeq = activityCursor;
|
|
1979
1987
|
let startedAt;
|
|
1988
|
+
// When one turn delivers several waiting prompts, its events carry the turn's
|
|
1989
|
+
// id, not this request's: follow the turn that actually carries this request.
|
|
1990
|
+
const inTurn = (turnId) => turnId === (this.options.session.sharedTurnId?.(queued.promptId) ?? queued.promptId);
|
|
1980
1991
|
let phase = 'starting request';
|
|
1981
1992
|
let timer;
|
|
1982
1993
|
const flushCommentary = () => {
|
|
@@ -2033,7 +2044,7 @@ export class OwnerChannel {
|
|
|
2033
2044
|
};
|
|
2034
2045
|
const acceptCommentary = (event) => {
|
|
2035
2046
|
if (!this.commentsEnabled
|
|
2036
|
-
|| active.commentaryDisabled || active.finalizing || event.turnId
|
|
2047
|
+
|| active.commentaryDisabled || active.finalizing || !inTurn(event.turnId)
|
|
2037
2048
|
|| event.kind !== 'agent_text' || event.messagePhase !== 'commentary'
|
|
2038
2049
|
|| event.replayed || event.origin?.kind !== 'owner'
|
|
2039
2050
|
|| event.origin.requestId !== active.requestId
|
|
@@ -2074,7 +2085,7 @@ export class OwnerChannel {
|
|
|
2074
2085
|
const reportProgress = () => {
|
|
2075
2086
|
const events = this.options.session.eventsSince(lastSeq);
|
|
2076
2087
|
lastSeq = Math.max(lastSeq, this.latestEventSeq(events));
|
|
2077
|
-
const activity = events.filter(event => event.turnId
|
|
2088
|
+
const activity = events.filter(event => inTurn(event.turnId));
|
|
2078
2089
|
if (!activity.length)
|
|
2079
2090
|
return;
|
|
2080
2091
|
startedAt ??= Date.parse(activity[0].at) || Date.now();
|
|
@@ -2099,10 +2110,10 @@ export class OwnerChannel {
|
|
|
2099
2110
|
const startProgress = (event) => {
|
|
2100
2111
|
if (timer || progressMs <= 0)
|
|
2101
2112
|
return;
|
|
2102
|
-
if (event && event.turnId
|
|
2113
|
+
if (event && !inTurn(event.turnId))
|
|
2103
2114
|
return;
|
|
2104
2115
|
const observed = event ? [event] : this.options.session.eventsSince(activityCursor)
|
|
2105
|
-
.filter(item => item.turnId
|
|
2116
|
+
.filter(item => inTurn(item.turnId));
|
|
2106
2117
|
if (!observed.length)
|
|
2107
2118
|
return;
|
|
2108
2119
|
startedAt = Date.parse(observed[0].at) || Date.now();
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { TaskReadinessIssue } from '../rooms-tasks/live-readiness.js';
|
|
1
2
|
import type { InterruptOutcome, SessionEvent, SessionSnapshot } from '../session/types.js';
|
|
2
3
|
import type { RoomOrchestrationRecord, TaskOutcome, TaskRecord, TaskTerminalIntent, TaskListRecord } from '../rooms-tasks/types.js';
|
|
3
4
|
import { type OwnerCommentsState } from './notices.js';
|
|
@@ -58,6 +59,9 @@ export interface OwnerCommandContext {
|
|
|
58
59
|
terminalTask(taskId: string, kind: TaskTerminalIntent['kind'], outcome?: TaskOutcome): Promise<void>;
|
|
59
60
|
createTask(input: Omit<CreateTaskRequest, 'actor'>): Promise<TaskRecord>;
|
|
60
61
|
startTask(taskId: string): Promise<TaskProvisioningOutcome | TaskRecord>;
|
|
62
|
+
observeTaskReadiness?(taskId: string): Promise<{
|
|
63
|
+
state: 'ready';
|
|
64
|
+
} | TaskReadinessIssue>;
|
|
61
65
|
taskProvisioningOutcome?(taskId: string): TaskProvisioningOutcome;
|
|
62
66
|
listTasks(filter?: {
|
|
63
67
|
state?: TaskState | TaskState[];
|
|
@@ -47,6 +47,11 @@ function isKnownOwnerRoomState(message) {
|
|
|
47
47
|
].some(pattern => pattern.test(message));
|
|
48
48
|
}
|
|
49
49
|
function ownerTaskFailure(error) {
|
|
50
|
+
if (error instanceof TaskRoomApplicationError && error.code === 'task_not_ready')
|
|
51
|
+
return {
|
|
52
|
+
kind: 'state', detail: error.message,
|
|
53
|
+
action: 'Ask Fleet Coordinator to inspect room and member status and choose supported recovery.',
|
|
54
|
+
};
|
|
50
55
|
if (error instanceof TaskListError)
|
|
51
56
|
return {
|
|
52
57
|
kind: error.code === 'list_not_found' ? 'not_found'
|
|
@@ -301,6 +306,7 @@ export const ownerCommands = [
|
|
|
301
306
|
const trailingLines = argLines.slice(1).join('\n').trim() || undefined;
|
|
302
307
|
const showTask = async (id) => {
|
|
303
308
|
const { task: t } = ctx.getTask(id);
|
|
309
|
+
const readiness = t.state === 'active' ? await ctx.observeTaskReadiness?.(id) : undefined;
|
|
304
310
|
await ctx.reply(renderMarkdownResult({
|
|
305
311
|
icon: '📋', title: 'Task details',
|
|
306
312
|
fields: [
|
|
@@ -308,6 +314,8 @@ export const ownerCommands = [
|
|
|
308
314
|
{ label: 'Title', value: t.title },
|
|
309
315
|
{ label: 'Status', value: taskStatus(t.state), kind: 'markdown' },
|
|
310
316
|
{ label: 'List', value: t.list_name ?? 'default', kind: 'code' },
|
|
317
|
+
...(readiness ? [{ label: 'Readiness', value: readiness.state },
|
|
318
|
+
...('reason' in readiness ? [{ label: 'Reason', value: readiness.reason }] : [])] : []),
|
|
311
319
|
...(t.blocked ? [{ label: 'Blocked', value: t.blocked.reason }] : []),
|
|
312
320
|
...(t.template ? [{ label: 'Template', value: `${t.template.name}@${t.template.version}`, kind: 'code' }] : []),
|
|
313
321
|
...(t.room_id ? [{ label: 'Room', value: t.room_id, kind: 'code' }] : []),
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
+
import { redactErasedContent, writePrivacyFilteredLedger } from '../erased-resources.js';
|
|
1
2
|
import { createHash } from 'node:crypto';
|
|
2
3
|
import { chmodSync, existsSync, readFileSync } from 'node:fs';
|
|
3
|
-
import { replaceFileAtomically } from '../atomic-file.js';
|
|
4
4
|
import { lifecycleEventDigestBasis, validateFleetAuditPresentations, } from '../fleet-command-audit.js';
|
|
5
5
|
const DIGEST = /^[a-f0-9]{64}$/u;
|
|
6
6
|
const LIMIT = 5_000;
|
|
@@ -63,7 +63,7 @@ export class FleetLifecycleOutbox {
|
|
|
63
63
|
}
|
|
64
64
|
pending() {
|
|
65
65
|
this.assertHealthy();
|
|
66
|
-
return this.entries.filter(entry => entry.delivery === 'pending').map(entry => structuredClone(entry));
|
|
66
|
+
return redactErasedContent(this.entries).filter(entry => entry.delivery === 'pending').map(entry => structuredClone(entry));
|
|
67
67
|
}
|
|
68
68
|
finish(digest, delivery) {
|
|
69
69
|
this.assertHealthy();
|
|
@@ -82,6 +82,7 @@ export class FleetLifecycleOutbox {
|
|
|
82
82
|
throw new Error(this.corruptReason);
|
|
83
83
|
}
|
|
84
84
|
persist() {
|
|
85
|
-
|
|
85
|
+
this.entries = redactErasedContent(this.entries);
|
|
86
|
+
writePrivacyFilteredLedger(this.path, { version: 1, entries: this.entries });
|
|
86
87
|
}
|
|
87
88
|
}
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { randomUUID } from 'node:crypto';
|
|
2
2
|
import { readFile } from 'node:fs/promises';
|
|
3
|
-
import { extname
|
|
4
|
-
import { readDaemonConfig, resolveEndpoint } from '../monitor.js';
|
|
3
|
+
import { extname } from 'node:path';
|
|
5
4
|
import { readClientProfile } from '../client-profile.js';
|
|
6
5
|
import { OursError, attachOursClient, } from '@ours.network/sdk/client';
|
|
7
6
|
/** Any failure of a daemon operation. Never carries a message body or a token. */
|
|
@@ -131,24 +130,10 @@ export class OursSdkClient {
|
|
|
131
130
|
return;
|
|
132
131
|
const environment = { ...process.env, ...this.env };
|
|
133
132
|
const profile = readClientProfile(environment);
|
|
134
|
-
|
|
135
|
-
// daemon capability before reading the client's protected token delivery file.
|
|
136
|
-
const endpoint = !profile && environment.OURS_DAEMON_ID
|
|
137
|
-
? resolveEndpoint(environment, false) : undefined;
|
|
138
|
-
const token = endpoint ? environment.OURS_API_TOKEN?.trim() || readDaemonConfig(environment).apiToken : undefined;
|
|
139
|
-
const options = profile ? {
|
|
133
|
+
const options = {
|
|
140
134
|
endpoint: profile.endpoint, expectedInstanceId: profile.expectedInstanceId,
|
|
141
135
|
credentialPath: profile.credentialPath,
|
|
142
136
|
sessionMode: 'external', leaseToken: this.leaseToken, env: {},
|
|
143
|
-
} : endpoint ? {
|
|
144
|
-
endpoint: endpoint.origin, expectedInstanceId: environment.OURS_DAEMON_ID,
|
|
145
|
-
sessionMode: 'external', leaseToken: this.leaseToken,
|
|
146
|
-
...(token ? { token } : { credentialPath: join(endpoint.stateDir, 'daemon-token') }),
|
|
147
|
-
} : {
|
|
148
|
-
env: environment,
|
|
149
|
-
leaseToken: this.leaseToken,
|
|
150
|
-
clientPid: process.pid,
|
|
151
|
-
fetch: notificationDeadlineFetch(this.deps.fetch ?? globalThis.fetch, this.deps.notificationRequestDeadlineMs ?? NOTIFICATION_REQUEST_DEADLINE_MS),
|
|
152
137
|
};
|
|
153
138
|
this.client = await (this.deps.attachClient?.(options) ?? attachOursClient(options));
|
|
154
139
|
this.terminalReleasePending = true;
|
package/dist/paths.d.ts
CHANGED
|
@@ -11,6 +11,11 @@ export declare const home: () => string;
|
|
|
11
11
|
*/
|
|
12
12
|
export declare function deriveXdgRuntimeDir(env?: NodeJS.ProcessEnv, uid?: number | undefined, exists?: (p: string) => boolean): string | undefined;
|
|
13
13
|
export declare const stateRoot: () => string;
|
|
14
|
+
/** The Owner invitation file that tunnel setup writes itself after enrollment. */
|
|
15
|
+
export declare const workspaceOwnerInvite: () => string;
|
|
16
|
+
/** Serializes writers of the workspace binding and of its unfinished replacement record. */
|
|
17
|
+
export declare const workspaceBindingLock: () => string;
|
|
18
|
+
export declare const workspaceReplacementFile: () => string;
|
|
14
19
|
export declare const agentsRoot: () => string;
|
|
15
20
|
export declare const tmpRoot: () => string;
|
|
16
21
|
export declare const logsRoot: () => string;
|
package/dist/paths.js
CHANGED
|
@@ -21,6 +21,11 @@ export function deriveXdgRuntimeDir(env = process.env, uid = process.getuid?.(),
|
|
|
21
21
|
return env.XDG_RUNTIME_DIR;
|
|
22
22
|
}
|
|
23
23
|
export const stateRoot = () => join(home(), '.ours-fleet');
|
|
24
|
+
/** The Owner invitation file that tunnel setup writes itself after enrollment. */
|
|
25
|
+
export const workspaceOwnerInvite = () => join(stateRoot(), 'workspace', 'owner.invite');
|
|
26
|
+
/** Serializes writers of the workspace binding and of its unfinished replacement record. */
|
|
27
|
+
export const workspaceBindingLock = () => join(stateRoot(), 'workspace', '.binding.lock');
|
|
28
|
+
export const workspaceReplacementFile = () => join(stateRoot(), 'workspace', 'replacement.json');
|
|
24
29
|
export const agentsRoot = () => join(stateRoot(), 'agents');
|
|
25
30
|
export const tmpRoot = () => join(stateRoot(), 'tmp');
|
|
26
31
|
export const logsRoot = () => join(stateRoot(), 'logs');
|
package/dist/preset-migration.js
CHANGED
|
@@ -67,10 +67,45 @@ const V5_GENERATED_ROLE_DEFAULT_FINGERPRINTS = Object.freeze({
|
|
|
67
67
|
'agent_templates/Developer.yaml': 'fdd953aab72d042e62780dd273598745aae59521b39edf4876d7676bb7189c83',
|
|
68
68
|
'agent_templates/Critic.yaml': '3e98a28f17876061cfadf984351aa59fdbe19629e514eff52c74546545c64a91',
|
|
69
69
|
});
|
|
70
|
+
// The packaged Coordinator role of revisions 8 and 9 addressed a named owner channel that a
|
|
71
|
+
// fresh installation does not have. Untouched copies adopt the current role; edited ones stay.
|
|
72
|
+
const OWNER_CHANNEL_COORDINATOR_FINGERPRINTS = Object.freeze({
|
|
73
|
+
'roles/Coordinator.yaml': [
|
|
74
|
+
'8c8ad28d9bb9ddf9dba8c5c9c7859f25edd8fa10ebf833e2340124a6e49583ec',
|
|
75
|
+
'f0d8b169ad0a18051b57e5280b93cf01e05faa5c12f4e12bdf89973edf2ef58a',
|
|
76
|
+
],
|
|
77
|
+
});
|
|
78
|
+
// The packaged revision-10 LocalCoordinator continuity loop reread ROUTINES.md and the WORKLOG
|
|
79
|
+
// checkpoint on every 15-minute pass. Untouched copies adopt the current template; edited ones stay.
|
|
80
|
+
const REREADING_CONTINUITY_LOOP_FINGERPRINTS = Object.freeze({
|
|
81
|
+
'agent_templates/LocalCoordinator.yaml': [
|
|
82
|
+
'53889cd3987e25ca42be64ddcfa1cf46a73ab769070db144f5b7d86085734178',
|
|
83
|
+
],
|
|
84
|
+
});
|
|
85
|
+
// The packaged Developer, Critic and Engineer charters before they described one bounded
|
|
86
|
+
// self-reminder per peer wait. Untouched copies adopt the current text; edited ones stay.
|
|
87
|
+
const PEER_WAIT_REMINDER_FINGERPRINTS = Object.freeze({
|
|
88
|
+
'roles/Developer.yaml': ['2884366ce02760d66d3edbaed2adc83f2959fd7906a7cd3b5a8b90cf601bc3e6'],
|
|
89
|
+
'roles/Critic.yaml': ['998abdbc5159e8e1c26eb1ca09f9707b005ba1930bd5b7a4989c4d6893804c4a'],
|
|
90
|
+
'roles/Engineer.yaml': ['a361453ad32cde08797cf90fff17a35bde1871ccea17389448b44398b45ccfa5'],
|
|
91
|
+
});
|
|
92
|
+
// The packaged revision-11 charters before task Progress and the self-contained To review handoff.
|
|
93
|
+
// Untouched copies adopt the current text; edited ones stay.
|
|
94
|
+
const REVIEW_PACKAGE_FINGERPRINTS = Object.freeze({
|
|
95
|
+
'roles/Developer.yaml': ['cd2da8c9fb4d56b08c618221e38b9c8a2568a59b7d7ee6e44d0f6f55f9dc42e9'],
|
|
96
|
+
'roles/Critic.yaml': ['b2576fe4b9501581e874daa40b77e0379220c855a76d0fd80945e9fc42e08131'],
|
|
97
|
+
'roles/Engineer.yaml': ['dc99a42f64c4bc2c5073917f4552a366a0ab95c24bdde9e13d66320d68edac1b'],
|
|
98
|
+
'roles/LocalCoordinator.yaml': ['1a4c7613de0c374ac49ede31b312fcfd3f4a08d31316cf8dda10e46f25844e03'],
|
|
99
|
+
'roles/Coordinator.yaml': ['1fa72470f37330a96debdfa6a64004280edcf318fafd5f6a5065a06ee7e87acc'],
|
|
100
|
+
'agent_templates/LocalCoordinator.yaml': ['931908da43df7c85dd2b106c99c765a3aeb4a97361fa00ce110e3ac93221ce32'],
|
|
101
|
+
});
|
|
70
102
|
const CURRENT_ROLE_DEFAULTS = new Set([
|
|
71
103
|
'roles/Coordinator.yaml', 'roles/LocalCoordinator.yaml', 'roles/Developer.yaml', 'roles/Critic.yaml',
|
|
104
|
+
'roles/Engineer.yaml',
|
|
72
105
|
'agent_templates/LocalCoordinator.yaml', 'agent_templates/Developer.yaml', 'agent_templates/Critic.yaml',
|
|
106
|
+
'agent_templates/Engineer.yaml',
|
|
73
107
|
'room_templates/single.yaml', 'room_templates/pair.yaml', 'room_templates/team.yaml',
|
|
108
|
+
'room_templates/engineering.yaml',
|
|
74
109
|
'agents/FleetCoordinator.yaml',
|
|
75
110
|
]);
|
|
76
111
|
export const legacyStarterMigrationManifest = Object.freeze({
|
|
@@ -193,6 +228,10 @@ export function migratePackagedRoleDefaults(configuration, options = {}, hooks =
|
|
|
193
228
|
...Object.keys(V3_ROLE_DEFAULT_FINGERPRINTS),
|
|
194
229
|
...Object.keys(V4_ROLE_DEFAULT_FINGERPRINTS),
|
|
195
230
|
...Object.keys(V5_ROLE_DEFAULT_FINGERPRINTS),
|
|
231
|
+
...Object.keys(OWNER_CHANNEL_COORDINATOR_FINGERPRINTS),
|
|
232
|
+
...Object.keys(REREADING_CONTINUITY_LOOP_FINGERPRINTS),
|
|
233
|
+
...Object.keys(PEER_WAIT_REMINDER_FINGERPRINTS),
|
|
234
|
+
...Object.keys(REVIEW_PACKAGE_FINGERPRINTS),
|
|
196
235
|
]);
|
|
197
236
|
for (const relative of known) {
|
|
198
237
|
const path = join(root, relative);
|
|
@@ -206,6 +245,10 @@ export function migratePackagedRoleDefaults(configuration, options = {}, hooks =
|
|
|
206
245
|
V4_GENERATED_ROLE_DEFAULT_FINGERPRINTS[relative],
|
|
207
246
|
V5_ROLE_DEFAULT_FINGERPRINTS[relative],
|
|
208
247
|
V5_GENERATED_ROLE_DEFAULT_FINGERPRINTS[relative],
|
|
248
|
+
...(OWNER_CHANNEL_COORDINATOR_FINGERPRINTS[relative] ?? []),
|
|
249
|
+
...(REREADING_CONTINUITY_LOOP_FINGERPRINTS[relative] ?? []),
|
|
250
|
+
...(PEER_WAIT_REMINDER_FINGERPRINTS[relative] ?? []),
|
|
251
|
+
...(REVIEW_PACKAGE_FINGERPRINTS[relative] ?? []),
|
|
209
252
|
].some(expected => expected !== undefined && actual === expected);
|
|
210
253
|
if (recognized)
|
|
211
254
|
(CURRENT_ROLE_DEFAULTS.has(relative) ? replacements : removals).push(path);
|
|
@@ -258,6 +301,18 @@ export function migratePackagedRoleDefaults(configuration, options = {}, hooks =
|
|
|
258
301
|
unlinkSync(target);
|
|
259
302
|
copyFileSync(join(source, relative), target, constants.COPYFILE_EXCL);
|
|
260
303
|
chmodSync(target, 0o600);
|
|
304
|
+
// New Engineer seats use the existing Developer's selected brain. This
|
|
305
|
+
// preserves wizard/custom provider choices without copying permissions.
|
|
306
|
+
if (relative === 'agent_templates/Engineer.yaml' && classified.additions.includes(path)) {
|
|
307
|
+
const developer = join(root, 'agent_templates/Developer.yaml');
|
|
308
|
+
if (existsSync(developer)) {
|
|
309
|
+
const value = parseFleetDocument(developer, readFileSync(developer, 'utf8'), 'strict').value;
|
|
310
|
+
if (value.brain !== undefined && JSON.stringify(value.brain) !== JSON.stringify({ ref: 'claude-default' })) {
|
|
311
|
+
const bytes = readFileSync(target, 'utf8').replace('brain: { ref: claude-default }', () => `brain: ${JSON.stringify(value.brain)}`);
|
|
312
|
+
writeFileSync(target, bytes, { mode: 0o600 });
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
261
316
|
fsyncPath(target);
|
|
262
317
|
}
|
|
263
318
|
for (const kind of ['roles', 'agent_templates', 'room_templates', 'agents'])
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Which run of a process a PID names. A PID alone is not an identity: after a
|
|
3
|
+
* reboot or wraparound the same number belongs to an unrelated process, and a
|
|
4
|
+
* lock or claim keyed on it would then look held forever. The kernel boot id
|
|
5
|
+
* and the process start time (in clock ticks since boot) pin one incarnation.
|
|
6
|
+
* Both are Linux-only; elsewhere they are absent and callers stay PID-only.
|
|
7
|
+
*/
|
|
8
|
+
export interface ProcessIncarnation {
|
|
9
|
+
boot?: string;
|
|
10
|
+
start?: string;
|
|
11
|
+
}
|
|
12
|
+
export interface IncarnationProbe {
|
|
13
|
+
bootId(): string | undefined;
|
|
14
|
+
startTime(pid: number): string | undefined;
|
|
15
|
+
}
|
|
16
|
+
export declare const procIncarnationProbe: IncarnationProbe;
|
|
17
|
+
export declare function processIncarnation(pid: number, probe?: IncarnationProbe): ProcessIncarnation;
|
|
18
|
+
/**
|
|
19
|
+
* True only when a recorded marker positively proves the live PID is a later
|
|
20
|
+
* process: a different boot, or a different start time. A marker that was not
|
|
21
|
+
* recorded, or cannot be read now, proves nothing and keeps the PID check.
|
|
22
|
+
*/
|
|
23
|
+
export declare function incarnationEnded(pid: number, recorded: ProcessIncarnation, probe?: IncarnationProbe): boolean;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { readFileSync } from 'node:fs';
|
|
2
|
+
export const procIncarnationProbe = {
|
|
3
|
+
bootId() {
|
|
4
|
+
try {
|
|
5
|
+
return readFileSync('/proc/sys/kernel/random/boot_id', 'utf8').trim() || undefined;
|
|
6
|
+
}
|
|
7
|
+
catch {
|
|
8
|
+
return undefined;
|
|
9
|
+
}
|
|
10
|
+
},
|
|
11
|
+
startTime(pid) {
|
|
12
|
+
try {
|
|
13
|
+
const stat = readFileSync(`/proc/${pid}/stat`, 'utf8');
|
|
14
|
+
return stat.slice(stat.lastIndexOf(')') + 2).split(/\s+/)[19] || undefined;
|
|
15
|
+
}
|
|
16
|
+
catch {
|
|
17
|
+
return undefined;
|
|
18
|
+
}
|
|
19
|
+
},
|
|
20
|
+
};
|
|
21
|
+
export function processIncarnation(pid, probe = procIncarnationProbe) {
|
|
22
|
+
const boot = probe.bootId(), start = probe.startTime(pid);
|
|
23
|
+
return { ...(boot ? { boot } : {}), ...(start ? { start } : {}) };
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* True only when a recorded marker positively proves the live PID is a later
|
|
27
|
+
* process: a different boot, or a different start time. A marker that was not
|
|
28
|
+
* recorded, or cannot be read now, proves nothing and keeps the PID check.
|
|
29
|
+
*/
|
|
30
|
+
export function incarnationEnded(pid, recorded, probe = procIncarnationProbe) {
|
|
31
|
+
if (typeof recorded.boot === 'string') {
|
|
32
|
+
const boot = probe.bootId();
|
|
33
|
+
if (boot && boot !== recorded.boot)
|
|
34
|
+
return true;
|
|
35
|
+
}
|
|
36
|
+
if (typeof recorded.start === 'string') {
|
|
37
|
+
const start = probe.startTime(pid);
|
|
38
|
+
if (start && start !== recorded.start)
|
|
39
|
+
return true;
|
|
40
|
+
}
|
|
41
|
+
return false;
|
|
42
|
+
}
|