@moretea-labs/forge 1.6.0 → 1.7.1
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/AGENTS.md +4 -0
- package/CHANGELOG.md +39 -0
- package/CLAUDE.md +4 -0
- package/README.md +2 -2
- package/README.zh-CN.md +1 -1
- package/SKILL.md +2 -2
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/agents/openai.yaml +1 -1
- package/assets/hooks/hook-input.sh +11 -8
- package/assets/hooks/lib/session-state.sh +10 -7
- package/assets/hooks/lib/workflow-state.sh +13 -177
- package/assets/hooks/post-tool-observer.sh +8 -2
- package/assets/hooks/prompt-guard.sh +5 -81
- package/assets/hooks/session-start-context.sh +56 -1
- package/assets/partials/05-workflow.partial.md +1 -1
- package/assets/partials-agents/02-operating-mode.partial.md +2 -0
- package/assets/partials-agents/03-orchestration.partial.md +1 -1
- package/assets/plugin-registry.v1.json +3 -3
- package/assets/reference-configs/document-generation.md +1 -1
- package/assets/reference-configs/harness-overview.md +1 -1
- package/assets/reference-configs/sprint-contracts.md +3 -3
- package/assets/skill-version.json +2 -2
- package/assets/templates/contract.template.md +2 -7
- package/assets/templates/helpers/capture-plan.sh +2 -2
- package/assets/templates/helpers/check-architecture-sync.sh +17 -0
- package/assets/templates/helpers/check-task-workflow.sh +4 -4
- package/assets/templates/helpers/contract-worktree.sh +1 -21
- package/assets/templates/helpers/ensure-task-workflow.sh +11 -36
- package/assets/templates/helpers/inspect-project-state.ts +8 -6
- package/assets/templates/helpers/migrate-project-template.sh +22 -45
- package/assets/templates/helpers/migrate-workflow-docs.ts +9 -7
- package/assets/templates/helpers/new-plan.sh +1 -1
- package/assets/templates/helpers/plan-to-todo.sh +12 -37
- package/assets/templates/helpers/ship-worktrees.sh +2 -29
- package/assets/templates/helpers/verify-contract.sh +1 -111
- package/assets/templates/helpers/verify-sprint.sh +3 -180
- package/assets/templates/helpers/workflow-contract.ts +5 -1
- package/assets/templates/plan.template.md +1 -1
- package/assets/templates/review.template.md +9 -34
- package/assets/workflow-contract.v1.json +10 -19
- package/docs/README.md +10 -10
- package/docs/architecture/CURRENT.md +178 -0
- package/docs/architecture/EVOLUTION.md +42 -0
- package/docs/architecture/current/README.md +4 -156
- package/docs/architecture/decisions/20260803-release-identity-binding-and-exit-policy.md +2 -2
- package/docs/architecture/decisions/20260823-immediate-controller-relay.md +53 -0
- package/docs/architecture/decisions/20260826-browser-runtime-v2.md +73 -0
- package/docs/architecture/history/README.md +7 -0
- package/docs/architecture/{chatgpt-handoff-facade.md → history/chatgpt-handoff-facade.md} +4 -0
- package/docs/architecture/{global-hook-runtime.md → history/global-hook-runtime.md} +7 -3
- package/docs/architecture/{ios-semantic-automation-provider-v2.md → history/ios-semantic-automation-provider-v2.md} +6 -2
- package/docs/architecture/history.md +3 -34
- package/docs/architecture/index.md +15 -98
- package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +1 -1
- package/docs/architecture/reviews/2026-08-22-semantic-harness-convergence-acceptance.md +39 -0
- package/docs/architecture/versions/1.6.md +38 -0
- package/docs/architecture/versions/1.7.md +41 -0
- package/docs/forge-chatgpt-browser-engine.md +2 -2
- package/docs/forge-chatgpt-mcp-setup.md +4 -28
- package/docs/forge-chatgpt-supervised-automation.md +3 -3
- package/docs/forge-github-issue-launcher.md +1 -1
- package/docs/forge-plugin-management.md +1 -1
- package/docs/forge-runtime-self-healing-loop.md +1 -1
- package/docs/operations/controller-browser-plugin.md +26 -6
- package/docs/operations/controller-performance-and-502.md +143 -0
- package/docs/operations/controller-reliability-runbook.md +1 -1
- package/docs/operations/features.md +2 -2
- package/docs/operations/github-repository.md +1 -1
- package/docs/operations/homebrew.md +2 -2
- package/docs/operations/mcp-tool-exposure.md +5 -3
- package/docs/wiki/Architecture.md +6 -6
- package/docs/wiki/Implementation.md +2 -2
- package/docs/wiki/Integrations.md +2 -2
- package/docs/wiki/Work-Lifecycle.md +1 -1
- package/package.json +33 -11
- package/scripts/activate-source-baseline.ts +17 -13
- package/scripts/architecture-event.ts +2 -820
- package/scripts/architecture-queue.sh +2 -645
- package/scripts/archive-architecture-request.sh +2 -242
- package/scripts/archive-workflow.sh +1 -291
- package/scripts/benchmark-background-check-overlap.ts +228 -0
- package/scripts/benchmark-mcp-host.sh +117 -0
- package/scripts/benchmark-mcp-transport.ts +170 -0
- package/scripts/benchmark-quality-harness.ts +146 -0
- package/scripts/benchmark-route-session-concurrency.ts +207 -6
- package/scripts/benchmark-thin-harness-mcp-e2e.ts +7 -1
- package/scripts/benchmark-typescript-navigation.ts +186 -0
- package/scripts/benchmark-work-efficiency.ts +334 -0
- package/scripts/capability-config.ts +2 -392
- package/scripts/capability-resolver.ts +2 -503
- package/scripts/capture-plan.sh +1 -332
- package/scripts/check-agent-tooling.sh +2 -1558
- package/scripts/check-architecture-sync.sh +2 -371
- package/scripts/check-brain-manifest.sh +2 -253
- package/scripts/check-context-files.sh +2 -47
- package/scripts/check-deploy-sql-order.sh +2 -115
- package/scripts/check-mcp-compatibility.ts +14 -0
- package/scripts/check-runtime-architecture.mjs +349 -79
- package/scripts/check-skill-version.ts +3 -191
- package/scripts/check-source-duplication.mjs +61 -0
- package/scripts/check-stable-baseline.ts +92 -0
- package/scripts/check-task-sync.sh +2 -63
- package/scripts/check-task-workflow.sh +2 -1000
- package/scripts/codex-handoff-resume.sh +2 -267
- package/scripts/context-contract-sync.sh +2 -514
- package/scripts/contract-run.ts +2 -463
- package/scripts/contract-worktree.sh +1 -722
- package/scripts/ensure-task-workflow.sh +1 -1225
- package/scripts/host-temporary-cloud-mcp-e2e.sh +265 -0
- package/scripts/inspect-project-state.ts +4 -279
- package/scripts/install-standalone-recovery.ts +24 -0
- package/scripts/lib/project-init-lib.sh +22 -46
- package/scripts/lib/run-forge-helper.sh +19 -0
- package/scripts/lib/run-forge-helper.ts +26 -0
- package/scripts/migrate-project-template.sh +1 -1189
- package/scripts/migrate-workflow-docs.ts +4 -412
- package/scripts/new-plan.sh +1 -180
- package/scripts/new-spec.sh +1 -47
- package/scripts/new-sprint.sh +1 -46
- package/scripts/plan-to-todo.sh +1 -796
- package/scripts/prepare-codex-handoff.sh +2 -201
- package/scripts/prepare-handoff.sh +1 -78
- package/scripts/refresh-current-status.sh +2 -467
- package/scripts/run-governed-gate.ts +5 -1
- package/scripts/select-agent-context-blocks.sh +2 -64
- package/scripts/ship-worktrees.sh +1 -558
- package/scripts/smoke-ios-physical-device.ts +4 -2
- package/scripts/smoke-runtime-recovery.ts +1 -35
- package/scripts/smoke-schedule-engine.ts +1 -20
- package/scripts/sprint-backlog.sh +1 -834
- package/scripts/stage-runtime-release.ts +2 -1
- package/scripts/summarize-failures.sh +2 -123
- package/scripts/switch-plan.sh +1 -150
- package/scripts/sync-brain-docs.sh +2 -379
- package/scripts/sync-generated-authority.mjs +175 -0
- package/scripts/verify-contract.sh +2 -703
- package/scripts/verify-forge-runtime.sh +0 -1
- package/scripts/verify-sprint.sh +1 -677
- package/scripts/workflow-contract.ts +4 -205
- package/scripts/workstream-sync.sh +2 -261
- package/src/cli/agent-jobs/executor-health.ts +1 -1
- package/src/cli/commands/init-hook.ts +3 -3
- package/src/cli/commands/mcp.ts +9 -5
- package/src/cli/commands/plugin.ts +12 -3
- package/src/cli/commands/recovery.ts +25 -3
- package/src/cli/commands/runtime.ts +55 -3
- package/src/cli/commands/security.ts +12 -1
- package/src/cli/commands/setup-profile.ts +31 -21
- package/src/cli/controller/check-runner.ts +113 -5
- package/src/cli/controller/composite-result.ts +1 -1
- package/src/cli/controller/context/focus.ts +51 -0
- package/src/cli/controller/context/instruction-context.ts +114 -0
- package/src/cli/controller/context/known-paths.ts +76 -0
- package/src/cli/controller/context/query-planning.ts +110 -0
- package/src/cli/controller/context/rule-resolution.ts +532 -0
- package/src/cli/controller/context/source-materializer.ts +278 -0
- package/src/cli/controller/context/structural.ts +16 -0
- package/src/cli/controller/context/types.ts +286 -0
- package/src/cli/controller/context-pack.ts +828 -487
- package/src/cli/controller/launch-agents.ts +77 -2
- package/src/cli/controller/operational-plan.ts +2 -2
- package/src/cli/controller/work-mode.ts +6 -4
- package/src/cli/editing/edit-session.ts +60 -7
- package/src/cli/github/contracts.ts +16 -0
- package/src/cli/github/github.ts +135 -16
- package/src/cli/github/session.ts +2 -1
- package/src/cli/installer/managed-entries.ts +15 -1
- package/src/cli/local-bridge/console-automations.ts +54 -78
- package/src/cli/local-bridge/console-view-models.ts +1 -35
- package/src/cli/local-bridge/facade-api.ts +37 -264
- package/src/cli/local-bridge/frontend/src/App.tsx +3 -3
- package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +23 -13
- package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +37 -4
- package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +6 -4
- package/src/cli/local-bridge/frontend/src/lib/api.ts +1 -6
- package/src/cli/local-bridge/frontend/src/lib/routes.ts +1 -1
- package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +2 -2
- package/src/cli/local-bridge/frontend/src/styles.css +1 -1
- package/src/cli/local-bridge/frontend/src/types.ts +5 -8
- package/src/cli/local-bridge/job-store.ts +1 -490
- package/src/cli/local-bridge/server.ts +2 -515
- package/src/cli/local-bridge/ui-dist/app.css +1 -1
- package/src/cli/local-bridge/ui-dist/app.js +3703 -3757
- package/src/cli/mcp/auth.ts +5 -5
- package/src/cli/mcp/instructions.ts +3 -2
- package/src/cli/mcp/legacy-tool-service.ts +10 -222
- package/src/cli/mcp/multi-repository.ts +16 -5
- package/src/cli/mcp/oauth.ts +175 -26
- package/src/cli/mcp/post-finalize-work-attribution.ts +150 -0
- package/src/cli/mcp/repository-tools.ts +282 -146
- package/src/cli/mcp/server.ts +586 -116
- package/src/cli/mcp/tool-capability-cache.ts +1 -1
- package/src/cli/mcp/tool-contract.ts +14 -0
- package/src/cli/mcp/toolset-selection.ts +1 -1
- package/src/cli/mcp/toolset.ts +44 -7
- package/src/cli/mcp/transports/http.ts +124 -14
- package/src/cli/mcp/transports/session-registry.ts +43 -5
- package/src/cli/mcp/types.ts +3 -3
- package/src/cli/repositories/command-classifier.ts +103 -21
- package/src/cli/repositories/command-executor.ts +78 -526
- package/src/cli/repositories/command-process.ts +108 -0
- package/src/cli/repositories/command-scope.ts +99 -41
- package/src/cli/repositories/controller-home.ts +7 -8
- package/src/cli/repositories/git-command-executor.ts +2 -0
- package/src/cli/repositories/locks.ts +53 -16
- package/src/cli/repositories/repository-snapshot.ts +124 -0
- package/src/cli/repositories/selected-path-actions.ts +17 -9
- package/src/cli/repositories/structured-git.ts +118 -6
- package/src/cli/repository/inspector.ts +563 -65
- package/src/cli/repository/session-cache.ts +58 -17
- package/src/cli/v81-entry.ts +1 -4
- package/src/effects/fs-transaction.ts +85 -17
- package/src/runtime/context/codegraph-read-provider.ts +233 -2
- package/src/runtime/context/codegraph-sidecar.cjs +74 -40
- package/src/runtime/context/generic-lsp-provider.ts +355 -0
- package/src/runtime/context/lsp-client.ts +283 -0
- package/src/runtime/context/semantic-navigation-contract.ts +61 -0
- package/src/runtime/context/semantic-navigation.ts +223 -0
- package/src/runtime/context/swift-navigation.ts +565 -0
- package/src/runtime/context/typescript-navigation.ts +226 -0
- package/src/runtime/control-plane/dispatch-priority.ts +28 -0
- package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +48 -16
- package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +86 -24
- package/src/runtime/control-plane/execution/repository-work-attribution.ts +106 -0
- package/src/runtime/control-plane/execution/retained-work-resume.ts +203 -0
- package/src/runtime/control-plane/execution/session-store.ts +18 -0
- package/src/runtime/control-plane/execution/verification-evidence.ts +58 -3
- package/src/runtime/control-plane/execution/work-completion-authority.ts +58 -0
- package/src/runtime/control-plane/execution/work-evidence-policy.ts +200 -0
- package/src/runtime/control-plane/execution/work-execution-support.ts +386 -0
- package/src/runtime/control-plane/execution/work-finalization-service.ts +1913 -0
- package/src/runtime/control-plane/execution/work-handle-authority.ts +96 -0
- package/src/runtime/control-plane/execution/work-handle-store.ts +27 -2
- package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +258 -0
- package/src/runtime/control-plane/execution/work-operation-service.ts +468 -0
- package/src/runtime/control-plane/execution/work-path-scope.ts +37 -0
- package/src/runtime/control-plane/execution/work-preparation-service.ts +479 -0
- package/src/runtime/control-plane/execution/work-process-evidence.ts +129 -0
- package/src/runtime/control-plane/execution/work-remote-delivery.ts +215 -0
- package/src/runtime/control-plane/execution/work-task-receipt.ts +22 -18
- package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +156 -11
- package/src/runtime/control-plane/execution/work-validation-reconciler.ts +22 -14
- package/src/runtime/control-plane/execution/work-verification-context.ts +56 -0
- package/src/runtime/control-plane/execution/work-verification-service.ts +388 -0
- package/src/runtime/control-plane/execution/work-verification-snapshot.ts +90 -9
- package/src/runtime/control-plane/facade/capability-registry.ts +129 -25
- package/src/runtime/control-plane/facade/controller-round-relay.ts +1118 -0
- package/src/runtime/control-plane/facade/controller-session-store.ts +302 -12
- package/src/runtime/control-plane/facade/goal-workloop-access.ts +17 -1
- package/src/runtime/control-plane/facade/goal-workloop.ts +928 -190
- package/src/runtime/control-plane/facade/index.ts +3 -0
- package/src/runtime/control-plane/facade/plan-contract-store.ts +275 -42
- package/src/runtime/control-plane/facade/repository-work-admission.ts +147 -0
- package/src/runtime/control-plane/facade/requirement-authority.ts +83 -0
- package/src/runtime/control-plane/facade/semantic-admission.ts +205 -0
- package/src/runtime/control-plane/facade/suggested-actions.ts +5 -1
- package/src/runtime/control-plane/facade/types.ts +90 -10
- package/src/runtime/control-plane/facade/work-contract-store.ts +157 -261
- package/src/runtime/control-plane/facade/work-state-machine.ts +250 -0
- package/src/runtime/control-plane/generated-cache-retention.ts +271 -0
- package/src/runtime/control-plane/global-scheduler/config.ts +44 -0
- package/src/runtime/control-plane/global-scheduler/dispatch-capacity.ts +80 -0
- package/src/runtime/control-plane/global-scheduler/durable-admission.ts +46 -0
- package/src/runtime/control-plane/global-scheduler/maintenance.ts +152 -0
- package/src/runtime/control-plane/global-scheduler/projection-refresh.ts +84 -0
- package/src/runtime/control-plane/global-scheduler/reconciliation.ts +3 -3
- package/src/runtime/control-plane/global-scheduler/scheduler.ts +278 -543
- package/src/runtime/control-plane/global-scheduler/source-scan.ts +59 -0
- package/src/runtime/control-plane/global-scheduler/state.ts +88 -0
- package/src/runtime/control-plane/global-scheduler/worker-attachment.ts +57 -0
- package/src/runtime/control-plane/global-scheduler/worker-exit-decision.ts +38 -0
- package/src/runtime/control-plane/global-scheduler/worker-exit-reconciler.ts +175 -0
- package/src/runtime/control-plane/global-scheduler/worker-launch.ts +111 -0
- package/src/runtime/control-plane/global-scheduler/worker-lifecycle-store.ts +64 -0
- package/src/runtime/control-plane/global-scheduler/worker-lifecycle.ts +150 -0
- package/src/runtime/control-plane/global-scheduler/worker-process.ts +119 -0
- package/src/runtime/control-plane/global-scheduler/worker-stderr.ts +60 -0
- package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +63 -0
- package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +365 -117
- package/src/runtime/control-plane/launcher/launch-reservation-store.ts +89 -0
- package/src/runtime/control-plane/launcher/thin-launcher.ts +244 -11
- package/src/runtime/control-plane/persistence/controller-home-migration.ts +487 -0
- package/src/runtime/control-plane/persistence/requirement-store.ts +0 -12
- package/src/runtime/control-plane/persistence/sqlite-store.ts +33 -2
- package/src/runtime/control-plane/release-retention.ts +62 -2
- package/src/runtime/control-plane/repo-actor/registry.ts +4 -2
- package/src/runtime/control-plane/routing/route-policy.ts +24 -21
- package/src/runtime/control-plane/routing/workspace-admission.ts +70 -0
- package/src/runtime/control-plane/runtime-status-client.ts +0 -2
- package/src/runtime/diagnostics/mcp-timing.ts +13 -0
- package/src/runtime/diagnostics/mcp-transport-benchmark.ts +129 -0
- package/src/runtime/diagnostics/performance.ts +24 -1
- package/src/runtime/evidence/event-ledger.ts +1 -1
- package/src/runtime/evidence/process-check-execution.ts +18 -0
- package/src/runtime/execution/jobs/child-reference.ts +6 -2
- package/src/runtime/execution/jobs/store.ts +58 -4
- package/src/runtime/execution/jobs/types.ts +1 -1
- package/src/runtime/execution/managed-workspace.ts +203 -1
- package/src/runtime/execution/process-runtime/check-facade.ts +51 -4
- package/src/runtime/execution/process-runtime/check-result.ts +88 -0
- package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +7 -1
- package/src/runtime/execution/process-runtime/check-scheduling.ts +114 -0
- package/src/runtime/execution/process-runtime/command-facade.ts +367 -40
- package/src/runtime/execution/process-runtime/gc.ts +38 -6
- package/src/runtime/execution/process-runtime/lightweight-managed.ts +1201 -0
- package/src/runtime/execution/process-runtime/persisted-check.ts +51 -5
- package/src/runtime/execution/process-runtime/resource-claims.ts +10 -6
- package/src/runtime/execution/process-runtime/runtime.ts +53 -3
- package/src/runtime/execution/process-runtime/store.ts +1 -0
- package/src/runtime/execution/process-runtime/types.ts +4 -12
- package/src/runtime/execution/thin-harness/async-process.ts +12 -2
- package/src/runtime/execution/thin-harness/search-worker.ts +54 -5
- package/src/runtime/execution/work-activity.ts +21 -0
- package/src/runtime/execution/workers/executor.ts +18 -61
- package/src/runtime/gateway/mcp/execution-tools.ts +35 -1852
- package/src/runtime/gateway/mcp/legacy-ios-tool-adapter.ts +61 -0
- package/src/runtime/gateway/mcp/process-tools.ts +83 -37
- package/src/runtime/gateway/mcp/router.ts +186 -84
- package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +565 -0
- package/src/runtime/gateway/mcp/runtime-tools.ts +2378 -1947
- package/src/runtime/plugins/app-store-connect-adapter.ts +216 -47
- package/src/runtime/plugins/browser-adapter.ts +2662 -829
- package/src/runtime/plugins/browser-automation-service.ts +10 -0
- package/src/runtime/plugins/browser-handoff-host.ts +2 -25
- package/src/runtime/plugins/browser-handoff.ts +110 -5
- package/src/runtime/plugins/browser-macos-bridge.ts +701 -63
- package/src/runtime/plugins/browser-manifest-surface.ts +603 -0
- package/src/runtime/plugins/browser-native-session-service.ts +222 -0
- package/src/runtime/plugins/browser-node-bridge.ts +6 -34
- package/src/runtime/plugins/browser-provider-registry.ts +109 -0
- package/src/runtime/plugins/browser-runtime-contract.ts +154 -0
- package/src/runtime/plugins/browser-runtime.ts +193 -0
- package/src/runtime/plugins/browser-session-authority.ts +332 -0
- package/src/runtime/plugins/browser-session-store.ts +121 -0
- package/src/runtime/plugins/browser-session-types.ts +98 -0
- package/src/runtime/plugins/capability-authorization-grants.ts +354 -0
- package/src/runtime/plugins/desktop-operator-registration.ts +10 -4
- package/src/runtime/plugins/external-adapter.ts +358 -3
- package/src/runtime/plugins/external-unix-socket.ts +7 -1
- package/src/runtime/plugins/first-party-registry.ts +2 -0
- package/src/runtime/plugins/github-adapter.ts +125 -0
- package/src/runtime/plugins/ios/agent-device-provider.ts +1 -1
- package/src/runtime/plugins/ios/agent-device-typed-provider.ts +52 -9
- package/src/runtime/plugins/ios/remote-xpc-hid.ts +1171 -0
- package/src/runtime/plugins/ios-adapter.ts +106 -16
- package/src/runtime/plugins/ios-agent-device.ts +234 -64
- package/src/runtime/plugins/ios-physical-device.ts +1143 -392
- package/src/runtime/plugins/lightweight-action.ts +153 -0
- package/src/runtime/plugins/local-system-adapter.ts +123 -6
- package/src/runtime/plugins/macos-capability-broker.ts +47 -9
- package/src/runtime/plugins/plugin-action-sidecar.ts +59 -0
- package/src/runtime/plugins/plugin-management-adapter.ts +49 -8
- package/src/runtime/plugins/resend-adapter.ts +1 -1
- package/src/runtime/plugins/store.ts +330 -33
- package/src/runtime/plugins/types.ts +38 -3
- package/src/runtime/plugins/xiaohongshu-publish.ts +515 -0
- package/src/runtime/projections/git-status-sampler.ts +16 -16
- package/src/runtime/projections/materialized-view.ts +28 -10
- package/src/runtime/recovery/maintenance-executor.ts +426 -25
- package/src/runtime/root/mcp-transport.ts +180 -10
- package/src/runtime/root/package-connector-service.ts +230 -10
- package/src/runtime/root/package-runtime-release.ts +143 -17
- package/src/runtime/root/package-runtime-service.ts +461 -22
- package/src/runtime/root/release-manifest.ts +144 -18
- package/src/runtime/root/release-materialize.ts +125 -32
- package/src/runtime/root/release-store.ts +48 -11
- package/src/runtime/root/runtime.ts +23 -5
- package/src/runtime/root/service.ts +100 -29
- package/src/runtime/root/types.ts +12 -0
- package/src/runtime/safe-tooling/ios-development.ts +321 -63
- package/src/runtime/shared/trusted-executable.ts +62 -0
- package/src/runtime/shared/trusted-node-executable.ts +48 -0
- package/src/runtime/standalone-recovery/core.ts +1079 -240
- package/src/runtime/standalone-recovery/entry.ts +100 -24
- package/src/runtime/standalone-recovery/installer.ts +20 -4
- package/src/runtime/standalone-recovery/service-labels.ts +2 -0
- package/src/runtime/standalone-recovery/watchdog-heartbeat.ts +160 -0
- package/src/runtime/workflow/schedules/browser-probe.ts +81 -3
- package/src/runtime/workflow/schedules/engine.ts +364 -202
- package/src/runtime/workflow/schedules/github-issue-watch.ts +158 -0
- package/src/runtime/workflow/schedules/settlement.ts +64 -17
- package/src/runtime/workflow/schedules/store.ts +209 -19
- package/src/runtime/workflow/schedules/types.ts +2 -4
- package/src/runtime/workflow/schedules/work-continuation.ts +26 -13
- package/src/testing/test-governance.ts +8 -40
- package/assets/reference-configs/heartbeat-triage.md +0 -57
- package/assets/templates/helpers/harness-trace-grade.sh +0 -150
- package/assets/templates/helpers/heartbeat-triage.sh +0 -361
- package/assets/templates/helpers/maintenance-triage.sh +0 -167
- package/docs/architecture/current/approved-target-architecture.zh-CN.md +0 -1501
- package/docs/architecture/current/architecture-invariants.md +0 -331
- package/docs/architecture/current/automation-and-schedule-engine.md +0 -459
- package/docs/architecture/current/codegraph-context-provider.md +0 -238
- package/docs/architecture/current/control-plane-authority-inventory.md +0 -94
- package/docs/architecture/current/controller-repository-decoupling.md +0 -148
- package/docs/architecture/current/dispatch-and-agent-strategy.md +0 -373
- package/docs/architecture/current/entity-model.md +0 -612
- package/docs/architecture/current/external-plugin-governance.md +0 -396
- package/docs/architecture/current/failure-recovery.md +0 -447
- package/docs/architecture/current/governance.md +0 -213
- package/docs/architecture/current/human-interaction-plane.md +0 -87
- package/docs/architecture/current/implementation-status.md +0 -128
- package/docs/architecture/current/job-and-run-lifecycle.md +0 -413
- package/docs/architecture/current/legacy-governance.md +0 -58
- package/docs/architecture/current/local-workspace-target-grants.md +0 -70
- package/docs/architecture/current/migration-roadmap.md +0 -105
- package/docs/architecture/current/multi-repository-execution.md +0 -371
- package/docs/architecture/current/operations-runbook.md +0 -45
- package/docs/architecture/current/peer-supercontroller-runtime.md +0 -32
- package/docs/architecture/current/personal-assistant-plugin-baseline.md +0 -206
- package/docs/architecture/current/repository-access-modes.md +0 -102
- package/docs/architecture/current/route-policy.md +0 -15
- package/docs/architecture/current/route-session-concurrency.md +0 -85
- package/docs/architecture/current/runtime-architecture-simplification.md +0 -214
- package/docs/architecture/current/runtime-directory-map.md +0 -49
- package/docs/architecture/current/runtime-health-and-resource-lifecycle.md +0 -85
- package/docs/architecture/current/scheduler-and-resource-claims.md +0 -422
- package/docs/architecture/current/session-aware-execution-and-authorization.md +0 -77
- package/docs/architecture/current/system-overview.md +0 -169
- package/docs/architecture/current/target-requirements-traceability.md +0 -202
- package/docs/architecture/current/thin-harness-governance-policy.md +0 -32
- package/docs/architecture/current/thin-harness-v1.md +0 -321
- package/docs/architecture/current/verification-and-release-gates.md +0 -391
- package/docs/forge-autonomous-goal-loop.md +0 -9
- package/docs/forge-provider-configuration.md +0 -7
- package/docs/operations/chatgpt-personal-assistant-runtime.md +0 -93
- package/docs/operations/gmail-assistant-routines.md +0 -186
- package/docs/operations/personal-assistant-operator-guide.md +0 -61
- package/docs/operations/provider-configuration.md +0 -31
- package/scripts/harness-trace-grade.sh +0 -150
- package/scripts/heartbeat-triage.sh +0 -361
- package/scripts/maintenance-triage.sh +0 -167
- package/src/cli/local-bridge/frontend/src/features/settings/SettingsView.tsx +0 -2
- package/src/cli/repositories/goal-registry.ts +0 -239
- package/src/runtime/assistant/action-proposals.ts +0 -279
- package/src/runtime/assistant/intent.ts +0 -395
- package/src/runtime/assistant/model-provider.ts +0 -460
- package/src/runtime/assistant/openapi.ts +0 -223
- package/src/runtime/assistant/policy.ts +0 -89
- package/src/runtime/assistant/readiness.ts +0 -258
- package/src/runtime/assistant/routine-runtime.ts +0 -477
- package/src/runtime/assistant/schedule-binding.ts +0 -171
- package/src/runtime/assistant/standing-grants.ts +0 -375
- package/src/runtime/assistant/store.ts +0 -243
- package/src/runtime/assistant/types.ts +0 -135
- package/src/runtime/control-plane/goal-loop/config-facade.ts +0 -700
- package/src/runtime/control-plane/goal-loop/config-store.ts +0 -570
- package/src/runtime/control-plane/goal-loop/config-types.ts +0 -213
- package/src/runtime/control-plane/goal-loop/executor-router.ts +0 -109
- package/src/runtime/control-plane/goal-loop/failure-taxonomy.ts +0 -149
- package/src/runtime/control-plane/goal-loop/goal-contract-store.ts +0 -304
- package/src/runtime/control-plane/goal-loop/goal-loop-engine.ts +0 -1001
- package/src/runtime/control-plane/goal-loop/handoff-packet.ts +0 -160
- package/src/runtime/control-plane/goal-loop/index.ts +0 -13
- package/src/runtime/control-plane/goal-loop/local-tools.ts +0 -290
- package/src/runtime/control-plane/goal-loop/policy-gates.ts +0 -203
- package/src/runtime/control-plane/goal-loop/provider-dispatch.ts +0 -93
- package/src/runtime/control-plane/goal-loop/provider-registry.ts +0 -545
- package/src/runtime/control-plane/goal-loop/types.ts +0 -378
- package/src/runtime/control-plane/startup-recovery.ts +0 -198
- package/src/runtime/gateway/mcp/resource-policy.ts +0 -186
- package/src/runtime/personal-assistant/gmail-triage-manager.ts +0 -231
- package/src/runtime/personal-assistant/reporting-runtime.ts +0 -344
- package/src/runtime/personal-assistant/triage-runtime.ts +0 -748
- package/src/runtime/workflow/findings/store.ts +0 -204
- package/src/runtime/workflow/findings/types.ts +0 -58
- package/src/runtime/workflow/portfolio/engine.ts +0 -20
- package/src/runtime/workflow/portfolio/store.ts +0 -185
- package/src/runtime/workflow/portfolio/types.ts +0 -29
|
@@ -4,7 +4,8 @@ import { withControllerLock } from '../../../cli/repositories/locks';
|
|
|
4
4
|
import { peekExecutionSession } from '../execution/session-store';
|
|
5
5
|
import { readJsonFile } from '../../shared/json-files';
|
|
6
6
|
import { readOrImportControlPlaneRecord, writeControlPlaneRecord } from '../persistence/sqlite-store';
|
|
7
|
-
import
|
|
7
|
+
import { getWorkContract } from './work-contract-store';
|
|
8
|
+
import { isTerminalWorkContractStatus, type ControllerSession, type ControllerSessionStore, type ControllerType } from './types';
|
|
8
9
|
|
|
9
10
|
export interface ControllerSessionStoreOptions { controllerHome: string; repoId: string; now?: () => string; }
|
|
10
11
|
|
|
@@ -17,6 +18,12 @@ export interface ControllerSessionClaimInput {
|
|
|
17
18
|
controllerInstanceId?: string;
|
|
18
19
|
/** Optional compare-and-swap fence supplied by a recovering caller. */
|
|
19
20
|
expectedClaimGeneration?: number;
|
|
21
|
+
/**
|
|
22
|
+
* Permit a different authenticated controller epoch to recover this Work only
|
|
23
|
+
* after the canonical execution/activity grace has elapsed. Callers must pair
|
|
24
|
+
* this with expectedClaimGeneration from the owner they observed.
|
|
25
|
+
*/
|
|
26
|
+
allowStaleRecovery?: boolean;
|
|
20
27
|
leaseMs?: number;
|
|
21
28
|
}
|
|
22
29
|
|
|
@@ -48,9 +55,50 @@ function write(options: ControllerSessionStoreOptions, store: ControllerSessionS
|
|
|
48
55
|
});
|
|
49
56
|
}
|
|
50
57
|
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
58
|
+
const DEFAULT_CONTROLLER_RECOVERY_GRACE_MS = 5 * 60_000;
|
|
59
|
+
const MAX_CONTROLLER_RECOVERY_GRACE_MS = 60 * 60_000;
|
|
60
|
+
const DEFAULT_CONTROLLER_LEASE_MS = 60 * 60_000;
|
|
61
|
+
const MAX_CONTROLLER_LEASE_MS = 60 * 60_000;
|
|
62
|
+
|
|
63
|
+
function optionsNowMs(options: ControllerSessionStoreOptions): number {
|
|
64
|
+
const parsed = Date.parse(now(options));
|
|
65
|
+
return Number.isFinite(parsed) ? parsed : Date.now();
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function activeSession(store: ControllerSessionStore, workId: string, atMs: number = Date.now()): ControllerSession | undefined {
|
|
69
|
+
return store.sessions.find((session) => session.workId === workId && Date.parse(session.leaseExpiresAt) > atMs);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function sessionBlocksRecovery(
|
|
73
|
+
options: ControllerSessionStoreOptions,
|
|
74
|
+
owner: ControllerSession,
|
|
75
|
+
input: { nowMs: number; graceMs?: number },
|
|
76
|
+
): boolean {
|
|
77
|
+
const execution = peekExecutionSession(options.controllerHome, owner.sessionId);
|
|
78
|
+
const graceMs = Math.max(60_000, Math.min(input.graceMs ?? DEFAULT_CONTROLLER_RECOVERY_GRACE_MS, MAX_CONTROLLER_RECOVERY_GRACE_MS));
|
|
79
|
+
if (execution?.invalidatedAt) {
|
|
80
|
+
const invalidatedAtMs = Date.parse(execution.invalidatedAt);
|
|
81
|
+
if (!Number.isFinite(invalidatedAtMs)) return true;
|
|
82
|
+
return input.nowMs - invalidatedAtMs < graceMs;
|
|
83
|
+
}
|
|
84
|
+
if (execution?.activeWorkId && execution.activeWorkId !== owner.workId) return false;
|
|
85
|
+
const activityMs = [execution?.lastValidatedAt, execution?.updatedAt, owner.claimedAt]
|
|
86
|
+
.map((value) => value ? Date.parse(value) : Number.NaN)
|
|
87
|
+
.filter(Number.isFinite)
|
|
88
|
+
.reduce((latest, value) => Math.max(latest, value), Number.NEGATIVE_INFINITY);
|
|
89
|
+
if (!Number.isFinite(activityMs)) return false;
|
|
90
|
+
return input.nowMs - activityMs < graceMs;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export function controllerSessionBlocksRecovery(
|
|
94
|
+
options: ControllerSessionStoreOptions,
|
|
95
|
+
workId: string,
|
|
96
|
+
input: { nowMs?: number; graceMs?: number } = {},
|
|
97
|
+
): boolean {
|
|
98
|
+
const nowMs = input.nowMs ?? Date.now();
|
|
99
|
+
const owner = activeSession(read(options), workId, nowMs);
|
|
100
|
+
if (!owner) return false;
|
|
101
|
+
return sessionBlocksRecovery(options, owner, { nowMs, graceMs: input.graceMs });
|
|
54
102
|
}
|
|
55
103
|
|
|
56
104
|
function assertIdentity(input: ControllerSessionClaimInput): void {
|
|
@@ -59,6 +107,13 @@ function assertIdentity(input: ControllerSessionClaimInput): void {
|
|
|
59
107
|
}
|
|
60
108
|
}
|
|
61
109
|
|
|
110
|
+
function assertWorkClaimable(options: ControllerSessionStoreOptions, workId: string): void {
|
|
111
|
+
const work = getWorkContract({ controllerHome: options.controllerHome, repoId: options.repoId }, workId);
|
|
112
|
+
if (work && isTerminalWorkContractStatus(work.status)) {
|
|
113
|
+
throw new Error(`WORK_CONTROLLER_CLAIM_TERMINAL: ${work.workId}:${work.status}`);
|
|
114
|
+
}
|
|
115
|
+
}
|
|
116
|
+
|
|
62
117
|
function claimedSession(
|
|
63
118
|
options: ControllerSessionStoreOptions,
|
|
64
119
|
input: ControllerSessionClaimInput,
|
|
@@ -66,7 +121,7 @@ function claimedSession(
|
|
|
66
121
|
previous?: ControllerSession,
|
|
67
122
|
): ControllerSession {
|
|
68
123
|
const leaseExpiresAt = new Date(
|
|
69
|
-
Date.parse(claimedAt) + Math.max(1_000, Math.min(input.leaseMs ??
|
|
124
|
+
Date.parse(claimedAt) + Math.max(1_000, Math.min(input.leaseMs ?? DEFAULT_CONTROLLER_LEASE_MS, MAX_CONTROLLER_LEASE_MS)),
|
|
70
125
|
).toISOString();
|
|
71
126
|
const previousPrincipal = previous?.principalId?.trim() || previous?.controllerId;
|
|
72
127
|
const inputPrincipal = input.principalId?.trim() || input.controllerId;
|
|
@@ -105,9 +160,10 @@ function persistClaim(
|
|
|
105
160
|
previous?: ControllerSession,
|
|
106
161
|
): ControllerSession {
|
|
107
162
|
const claimedAt = now(options);
|
|
163
|
+
const claimedAtMs = Date.parse(claimedAt);
|
|
108
164
|
const session = claimedSession(options, input, claimedAt, previous);
|
|
109
165
|
const sessions = [
|
|
110
|
-
...store.sessions.filter((entry) => entry.workId !== input.workId && Date.parse(entry.leaseExpiresAt) >
|
|
166
|
+
...store.sessions.filter((entry) => entry.workId !== input.workId && Date.parse(entry.leaseExpiresAt) > claimedAtMs),
|
|
111
167
|
session,
|
|
112
168
|
];
|
|
113
169
|
write(options, { schemaVersion: 1, updatedAt: claimedAt, sessions });
|
|
@@ -115,14 +171,194 @@ function persistClaim(
|
|
|
115
171
|
}
|
|
116
172
|
|
|
117
173
|
export function getControllerSession(options: ControllerSessionStoreOptions, workId: string): ControllerSession | undefined {
|
|
118
|
-
return activeSession(read(options), workId);
|
|
174
|
+
return activeSession(read(options), workId, optionsNowMs(options));
|
|
119
175
|
}
|
|
120
176
|
|
|
121
177
|
export function listControllerSessions(options: ControllerSessionStoreOptions): ControllerSession[] {
|
|
122
|
-
const at =
|
|
178
|
+
const at = optionsNowMs(options);
|
|
123
179
|
return read(options).sessions.filter((session) => Date.parse(session.leaseExpiresAt) > at);
|
|
124
180
|
}
|
|
125
181
|
|
|
182
|
+
export interface ControllerOwnershipAuthority {
|
|
183
|
+
controllerId: string;
|
|
184
|
+
controllerType: ControllerType;
|
|
185
|
+
principalId: string;
|
|
186
|
+
controllerInstanceId: string;
|
|
187
|
+
claimGeneration: number;
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
export type ControllerTerminalizationAuthority = ControllerOwnershipAuthority;
|
|
191
|
+
|
|
192
|
+
export function controllerSessionPrincipalId(
|
|
193
|
+
owner: Pick<ControllerSession, 'principalId' | 'controllerId'>,
|
|
194
|
+
): string {
|
|
195
|
+
return owner.principalId?.trim() || owner.controllerId;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
export function requireControllerOwnershipAuthority(
|
|
199
|
+
owner: ControllerSession,
|
|
200
|
+
workId: string = owner.workId,
|
|
201
|
+
): ControllerOwnershipAuthority {
|
|
202
|
+
const principalId = controllerSessionPrincipalId(owner);
|
|
203
|
+
const controllerInstanceId = owner.controllerInstanceId?.trim() || '';
|
|
204
|
+
if (!controllerInstanceId) throw new Error(`WORK_CONTROLLER_INSTANCE_MISMATCH: ${workId}`);
|
|
205
|
+
if (typeof owner.claimGeneration !== 'number' || owner.claimGeneration < 1) {
|
|
206
|
+
throw new Error(`WORK_CONTROLLER_CLAIM_GENERATION_REQUIRED: ${workId}`);
|
|
207
|
+
}
|
|
208
|
+
return {
|
|
209
|
+
controllerId: owner.controllerId,
|
|
210
|
+
controllerType: owner.controllerType,
|
|
211
|
+
principalId,
|
|
212
|
+
controllerInstanceId,
|
|
213
|
+
claimGeneration: owner.claimGeneration,
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export function assertControllerOwnershipAuthority(
|
|
218
|
+
owner: ControllerSession,
|
|
219
|
+
expected: {
|
|
220
|
+
workId?: string;
|
|
221
|
+
controllerId: string;
|
|
222
|
+
principalId: string;
|
|
223
|
+
controllerInstanceId?: string;
|
|
224
|
+
controllerType?: ControllerType;
|
|
225
|
+
},
|
|
226
|
+
): ControllerOwnershipAuthority {
|
|
227
|
+
const workId = expected.workId ?? owner.workId;
|
|
228
|
+
const authority = requireControllerOwnershipAuthority(owner, workId);
|
|
229
|
+
if (authority.controllerId !== expected.controllerId.trim()) {
|
|
230
|
+
throw new Error(`WORK_CONTROLLER_OWNER_MISMATCH: ${workId} is owned by ${owner.controllerId}`);
|
|
231
|
+
}
|
|
232
|
+
if (expected.controllerType && authority.controllerType !== expected.controllerType) {
|
|
233
|
+
throw new Error(`WORK_CONTROLLER_TYPE_MISMATCH: ${workId} is owned by ${owner.controllerType}`);
|
|
234
|
+
}
|
|
235
|
+
if (authority.principalId !== expected.principalId.trim()) {
|
|
236
|
+
throw new Error(`WORK_CONTROLLER_PRINCIPAL_MISMATCH: ${workId}`);
|
|
237
|
+
}
|
|
238
|
+
if (expected.controllerInstanceId !== undefined
|
|
239
|
+
&& authority.controllerInstanceId !== expected.controllerInstanceId.trim()) {
|
|
240
|
+
throw new Error(`WORK_CONTROLLER_INSTANCE_MISMATCH: ${workId}`);
|
|
241
|
+
}
|
|
242
|
+
return authority;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
export type ControllerTerminalizationFenceReason =
|
|
246
|
+
| 'active_controller_claim'
|
|
247
|
+
| 'stale_controller_authority'
|
|
248
|
+
| 'controller_claim_missing';
|
|
249
|
+
|
|
250
|
+
export type ControllerTerminalizationFenceResult<T> =
|
|
251
|
+
| { allowed: true; value: T; owner?: ControllerSession }
|
|
252
|
+
| { allowed: false; reason: ControllerTerminalizationFenceReason; owner?: ControllerSession };
|
|
253
|
+
|
|
254
|
+
export function controllerTerminalizationAuthorityFromSession(
|
|
255
|
+
owner: ControllerSession,
|
|
256
|
+
): ControllerTerminalizationAuthority | undefined {
|
|
257
|
+
try {
|
|
258
|
+
return requireControllerOwnershipAuthority(owner);
|
|
259
|
+
} catch {
|
|
260
|
+
return undefined;
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
function controllerTerminalizationAuthorityMatches(
|
|
265
|
+
owner: ControllerSession,
|
|
266
|
+
authority: ControllerTerminalizationAuthority,
|
|
267
|
+
): boolean {
|
|
268
|
+
const ownerPrincipal = controllerSessionPrincipalId(owner);
|
|
269
|
+
const ownerInstanceId = owner.controllerInstanceId?.trim() || '';
|
|
270
|
+
return owner.controllerId === authority.controllerId
|
|
271
|
+
&& owner.controllerType === authority.controllerType
|
|
272
|
+
&& ownerPrincipal === authority.principalId.trim()
|
|
273
|
+
&& ownerInstanceId === authority.controllerInstanceId.trim()
|
|
274
|
+
&& owner.claimGeneration === authority.claimGeneration;
|
|
275
|
+
}
|
|
276
|
+
|
|
277
|
+
/**
|
|
278
|
+
* Serialize control-path Work terminalization against the same canonical
|
|
279
|
+
* ControllerSession task lock used by claim/resume/release. This is a fence,
|
|
280
|
+
* not a second lifecycle authority: WorkContract remains the terminal state
|
|
281
|
+
* authority and ControllerSession remains the ownership authority.
|
|
282
|
+
*
|
|
283
|
+
* A caller without Controller authority may terminalize only while the Work is
|
|
284
|
+
* unclaimed. A caller that observed a live owner must present that exact durable
|
|
285
|
+
* owner epoch. Transport session ids are intentionally not compared because MCP
|
|
286
|
+
* transports may rotate without moving Work ownership; controller instance plus
|
|
287
|
+
* claim generation fence stale controller epochs while preserving that rotation.
|
|
288
|
+
*/
|
|
289
|
+
export function withControllerSessionTerminalizationFence<T>(
|
|
290
|
+
options: ControllerSessionStoreOptions,
|
|
291
|
+
input: {
|
|
292
|
+
workId: string;
|
|
293
|
+
actor: string;
|
|
294
|
+
authority?: ControllerTerminalizationAuthority;
|
|
295
|
+
},
|
|
296
|
+
operation: () => T,
|
|
297
|
+
): ControllerTerminalizationFenceResult<T> {
|
|
298
|
+
return withControllerLock(
|
|
299
|
+
options.controllerHome,
|
|
300
|
+
{ scope: 'task', repoId: options.repoId, taskId: `controller-session-${input.workId}` },
|
|
301
|
+
input.actor,
|
|
302
|
+
() => {
|
|
303
|
+
const owner = activeSession(read(options), input.workId, optionsNowMs(options));
|
|
304
|
+
if (!owner) {
|
|
305
|
+
if (input.authority) return { allowed: false, reason: 'controller_claim_missing' };
|
|
306
|
+
return { allowed: true, value: operation() };
|
|
307
|
+
}
|
|
308
|
+
if (!input.authority) return { allowed: false, reason: 'active_controller_claim', owner };
|
|
309
|
+
|
|
310
|
+
if (!controllerTerminalizationAuthorityMatches(owner, input.authority)) {
|
|
311
|
+
return { allowed: false, reason: 'stale_controller_authority', owner };
|
|
312
|
+
}
|
|
313
|
+
return { allowed: true, value: operation(), owner };
|
|
314
|
+
},
|
|
315
|
+
);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
/**
|
|
319
|
+
* Release a live ControllerSession only when the caller still owns the exact
|
|
320
|
+
* durable controller epoch it observed. This closes the release-then-terminalize
|
|
321
|
+
* race without changing WorkContract lifecycle authority.
|
|
322
|
+
*/
|
|
323
|
+
export function releaseControllerSessionWithAuthority(
|
|
324
|
+
options: ControllerSessionStoreOptions,
|
|
325
|
+
input: {
|
|
326
|
+
workId: string;
|
|
327
|
+
actor: string;
|
|
328
|
+
authority: ControllerTerminalizationAuthority;
|
|
329
|
+
},
|
|
330
|
+
): ControllerTerminalizationFenceResult<void> {
|
|
331
|
+
return withControllerLock(
|
|
332
|
+
options.controllerHome,
|
|
333
|
+
{ scope: 'task', repoId: options.repoId, taskId: `controller-session-${input.workId}` },
|
|
334
|
+
input.actor,
|
|
335
|
+
() => {
|
|
336
|
+
const store = read(options);
|
|
337
|
+
const owner = activeSession(store, input.workId, optionsNowMs(options));
|
|
338
|
+
if (!owner) return { allowed: false, reason: 'controller_claim_missing' };
|
|
339
|
+
if (!controllerTerminalizationAuthorityMatches(owner, input.authority)) {
|
|
340
|
+
return { allowed: false, reason: 'stale_controller_authority', owner };
|
|
341
|
+
}
|
|
342
|
+
const sessions = store.sessions.filter((entry) => entry.workId !== input.workId);
|
|
343
|
+
write(options, { schemaVersion: 1, updatedAt: now(options), sessions });
|
|
344
|
+
return { allowed: true, value: undefined, owner };
|
|
345
|
+
},
|
|
346
|
+
);
|
|
347
|
+
}
|
|
348
|
+
|
|
349
|
+
export function releaseObservedControllerSession(
|
|
350
|
+
options: ControllerSessionStoreOptions,
|
|
351
|
+
input: { workId: string; actor: string; owner: ControllerSession },
|
|
352
|
+
): ControllerTerminalizationFenceResult<void> {
|
|
353
|
+
const authority = controllerTerminalizationAuthorityFromSession(input.owner);
|
|
354
|
+
if (!authority) return { allowed: false, reason: 'stale_controller_authority', owner: input.owner };
|
|
355
|
+
return releaseControllerSessionWithAuthority(options, {
|
|
356
|
+
workId: input.workId,
|
|
357
|
+
actor: input.actor,
|
|
358
|
+
authority,
|
|
359
|
+
});
|
|
360
|
+
}
|
|
361
|
+
|
|
126
362
|
/**
|
|
127
363
|
* Strict claim used for first ownership acquisition and explicit release/reclaim flows.
|
|
128
364
|
* A live claim with any different controller or transport session remains fenced.
|
|
@@ -137,8 +373,9 @@ export function claimControllerSession(
|
|
|
137
373
|
{ scope: 'task', repoId: options.repoId, taskId: `controller-session-${input.workId}` },
|
|
138
374
|
`controller-claim:${input.controllerId}:${input.sessionId}`,
|
|
139
375
|
() => {
|
|
376
|
+
assertWorkClaimable(options, input.workId);
|
|
140
377
|
const store = read(options);
|
|
141
|
-
const current = activeSession(store, input.workId);
|
|
378
|
+
const current = activeSession(store, input.workId, optionsNowMs(options));
|
|
142
379
|
assertExpectedGeneration(input, current);
|
|
143
380
|
if (current && (current.controllerId !== input.controllerId || current.sessionId !== input.sessionId)) {
|
|
144
381
|
throw new Error(`WORK_ALREADY_CLAIMED: ${input.workId} is owned by ${current.controllerId}`);
|
|
@@ -167,16 +404,21 @@ export function resumeControllerSession(
|
|
|
167
404
|
{ scope: 'task', repoId: options.repoId, taskId: `controller-session-${input.workId}` },
|
|
168
405
|
`controller-resume:${input.controllerId}:${input.sessionId}`,
|
|
169
406
|
() => {
|
|
407
|
+
assertWorkClaimable(options, input.workId);
|
|
170
408
|
const store = read(options);
|
|
171
|
-
const
|
|
409
|
+
const currentNowMs = optionsNowMs(options);
|
|
410
|
+
const current = activeSession(store, input.workId, currentNowMs);
|
|
172
411
|
assertExpectedGeneration(input, current);
|
|
173
412
|
if (!current) return persistClaim(options, store, input);
|
|
174
413
|
const priorExecution = peekExecutionSession(options.controllerHome, current.sessionId);
|
|
175
414
|
const currentPrincipal = current.principalId?.trim() || priorExecution?.principalId?.trim() || current.controllerId;
|
|
176
|
-
|
|
415
|
+
const staleRecoveryAllowed = input.allowStaleRecovery === true
|
|
416
|
+
&& input.expectedClaimGeneration !== undefined
|
|
417
|
+
&& !sessionBlocksRecovery(options, current, { nowMs: currentNowMs });
|
|
418
|
+
if (currentPrincipal !== input.principalId.trim() && !staleRecoveryAllowed) {
|
|
177
419
|
throw new Error(`WORK_CONTROLLER_PRINCIPAL_MISMATCH: ${input.workId} is owned by another authenticated principal`);
|
|
178
420
|
}
|
|
179
|
-
if (current.controllerId !== input.controllerId) {
|
|
421
|
+
if (current.controllerId !== input.controllerId && !staleRecoveryAllowed) {
|
|
180
422
|
throw new Error(`WORK_ALREADY_CLAIMED: ${input.workId} is owned by ${current.controllerId}`);
|
|
181
423
|
}
|
|
182
424
|
return persistClaim(options, store, input, current);
|
|
@@ -184,6 +426,54 @@ export function resumeControllerSession(
|
|
|
184
426
|
);
|
|
185
427
|
}
|
|
186
428
|
|
|
429
|
+
/**
|
|
430
|
+
* Rebind one active Work to the authenticated Controller transport/Runtime that
|
|
431
|
+
* is serving the current request. Same-instance transport rollover needs only
|
|
432
|
+
* principal/controller proof. A Runtime-instance change additionally requires
|
|
433
|
+
* positive proof that the requested instance is the live canonical Runtime;
|
|
434
|
+
* this prevents a stale old Runtime request from migrating ownership backwards.
|
|
435
|
+
* The observed claim generation is always supplied as a CAS fence.
|
|
436
|
+
*/
|
|
437
|
+
export function bindControllerSessionToCurrentRuntime(
|
|
438
|
+
options: ControllerSessionStoreOptions,
|
|
439
|
+
input: ControllerSessionClaimInput & {
|
|
440
|
+
principalId: string;
|
|
441
|
+
controllerInstanceId: string;
|
|
442
|
+
currentRuntimeInstanceId?: string;
|
|
443
|
+
allowClaimIfMissing?: boolean;
|
|
444
|
+
},
|
|
445
|
+
): ControllerSession {
|
|
446
|
+
assertIdentity(input);
|
|
447
|
+
const current = getControllerSession(options, input.workId);
|
|
448
|
+
if (!current) {
|
|
449
|
+
if (input.allowClaimIfMissing !== true) throw new Error(`WORK_CONTROLLER_OWNER_REQUIRED: ${input.workId}`);
|
|
450
|
+
return claimControllerSession(options, { ...input, expectedClaimGeneration: 0 });
|
|
451
|
+
}
|
|
452
|
+
const currentPrincipal = controllerSessionPrincipalId(current);
|
|
453
|
+
if (current.controllerId !== input.controllerId) {
|
|
454
|
+
throw new Error(`WORK_CONTROLLER_OWNER_MISMATCH: ${input.workId} is owned by ${current.controllerId}`);
|
|
455
|
+
}
|
|
456
|
+
if (current.controllerType !== input.controllerType) {
|
|
457
|
+
throw new Error(`WORK_CONTROLLER_TYPE_MISMATCH: ${input.workId} is owned by ${current.controllerType}`);
|
|
458
|
+
}
|
|
459
|
+
if (currentPrincipal !== input.principalId.trim()) {
|
|
460
|
+
throw new Error(`WORK_CONTROLLER_PRINCIPAL_MISMATCH: ${input.workId}`);
|
|
461
|
+
}
|
|
462
|
+
const ownerInstanceId = current.controllerInstanceId?.trim() || '';
|
|
463
|
+
const requestedInstanceId = input.controllerInstanceId.trim();
|
|
464
|
+
if (ownerInstanceId !== requestedInstanceId) {
|
|
465
|
+
const currentRuntimeInstanceId = input.currentRuntimeInstanceId?.trim() || '';
|
|
466
|
+
if (!currentRuntimeInstanceId || currentRuntimeInstanceId !== requestedInstanceId) {
|
|
467
|
+
throw new Error(`WORK_CONTROLLER_INSTANCE_MISMATCH: ${input.workId}`);
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
if (ownerInstanceId === requestedInstanceId && current.sessionId === input.sessionId) return current;
|
|
471
|
+
return resumeControllerSession(options, {
|
|
472
|
+
...input,
|
|
473
|
+
expectedClaimGeneration: current.claimGeneration,
|
|
474
|
+
});
|
|
475
|
+
}
|
|
476
|
+
|
|
187
477
|
export function releaseControllerSession(options: ControllerSessionStoreOptions, workId: string, controllerId: string): void {
|
|
188
478
|
withControllerLock(
|
|
189
479
|
options.controllerHome,
|
|
@@ -13,7 +13,7 @@ import {
|
|
|
13
13
|
type GoalWorkloopStartInput,
|
|
14
14
|
} from './goal-workloop';
|
|
15
15
|
import { getWorkContract } from './work-contract-store';
|
|
16
|
-
import type { CapabilityRisk, FacadeResult, WorkContractConstraints } from './types';
|
|
16
|
+
import type { CapabilityRisk, FacadeResult, WorkContractConstraints, WorkKind } from './types';
|
|
17
17
|
|
|
18
18
|
export {
|
|
19
19
|
continueGoalWorkloop,
|
|
@@ -47,6 +47,18 @@ function accessModeValue(value: unknown): AccessMode | undefined {
|
|
|
47
47
|
return isAccessMode(candidate) ? candidate : undefined;
|
|
48
48
|
}
|
|
49
49
|
|
|
50
|
+
function workKindValue(value: unknown): Extract<WorkKind, 'repository_change' | 'completed_no_change' | 'read_only_review' | 'investigation' | 'local_effect' | 'remote_effect' | 'reconciliation'> | undefined {
|
|
51
|
+
return value === 'repository_change'
|
|
52
|
+
|| value === 'completed_no_change'
|
|
53
|
+
|| value === 'read_only_review'
|
|
54
|
+
|| value === 'investigation'
|
|
55
|
+
|| value === 'local_effect'
|
|
56
|
+
|| value === 'remote_effect'
|
|
57
|
+
|| value === 'reconciliation'
|
|
58
|
+
? value
|
|
59
|
+
: undefined;
|
|
60
|
+
}
|
|
61
|
+
|
|
50
62
|
function constraintsValue(value: unknown): WorkContractConstraints | undefined {
|
|
51
63
|
if (!value || typeof value !== 'object' || Array.isArray(value)) return undefined;
|
|
52
64
|
const record = value as Record<string, unknown>;
|
|
@@ -65,6 +77,7 @@ function constraintsValue(value: unknown): WorkContractConstraints | undefined {
|
|
|
65
77
|
? 'isolated'
|
|
66
78
|
: record.workspaceMode === 'auto' || record.workspace_mode === 'auto' ? 'auto' : undefined,
|
|
67
79
|
requireWorktree: booleanValue(record, 'requireWorktree', 'require_worktree'),
|
|
80
|
+
directMainProhibited: booleanValue(record, 'directMainProhibited', 'direct_main_prohibited'),
|
|
68
81
|
};
|
|
69
82
|
return Object.fromEntries(
|
|
70
83
|
Object.entries(constraints).filter(([, entry]) => entry !== undefined),
|
|
@@ -139,9 +152,11 @@ export function runGoalWorkloop(
|
|
|
139
152
|
objective: String(args.objective ?? ''),
|
|
140
153
|
acceptanceCriteria: Array.isArray(args.acceptance_criteria) ? args.acceptance_criteria.map(String) : undefined,
|
|
141
154
|
allowedPaths: Array.isArray(args.allowed_paths) ? args.allowed_paths.map(String) : undefined,
|
|
155
|
+
initialLikelyPaths: Array.isArray(args.initial_likely_paths) ? args.initial_likely_paths.map(String) : undefined,
|
|
142
156
|
forbiddenPaths: Array.isArray(args.forbidden_paths) ? args.forbidden_paths.map(String) : undefined,
|
|
143
157
|
checks: Array.isArray(args.check_ids) ? args.check_ids.map(String) : undefined,
|
|
144
158
|
constraints,
|
|
159
|
+
workKind: workKindValue(args.work_kind),
|
|
145
160
|
modeInput: {
|
|
146
161
|
objective: typeof args.objective === 'string' ? args.objective : undefined,
|
|
147
162
|
expectedFiles: typeof args.expected_files === 'number' ? args.expected_files : undefined,
|
|
@@ -150,6 +165,7 @@ export function runGoalWorkloop(
|
|
|
150
165
|
requiresInvestigation: args.requires_investigation === true,
|
|
151
166
|
requiresLongRunningChecks: args.requires_long_running_checks === true,
|
|
152
167
|
requiresParallelism: args.requires_parallelism === true,
|
|
168
|
+
explicitMode: args.mode === 'scale' ? 'scale' : undefined,
|
|
153
169
|
needsDependencies: args.needs_dependencies === true,
|
|
154
170
|
requiresRecovery: args.requires_recovery === true,
|
|
155
171
|
requiresWorker: args.requires_worker === true,
|