@moretea-labs/forge 1.6.0 → 1.7.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/AGENTS.md +2 -0
- package/CHANGELOG.md +26 -0
- package/CLAUDE.md +2 -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/03-orchestration.partial.md +1 -1
- package/assets/plugin-registry.v1.json +1 -1
- 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-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/migrate-project-template.sh +22 -45
- 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/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 +170 -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-runtime-self-healing-loop.md +1 -1
- package/docs/operations/controller-browser-plugin.md +26 -6
- package/docs/operations/controller-performance-and-502.md +124 -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 +29 -10
- package/scripts/activate-source-baseline.ts +17 -13
- 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/capture-plan.sh +3 -3
- package/scripts/check-agent-tooling.sh +8 -35
- package/scripts/check-architecture-sync.sh +17 -86
- package/scripts/check-mcp-compatibility.ts +14 -0
- package/scripts/check-runtime-architecture.mjs +107 -64
- package/scripts/check-stable-baseline.ts +92 -0
- package/scripts/check-task-workflow.sh +4 -4
- package/scripts/contract-worktree.sh +1 -21
- package/scripts/ensure-task-workflow.sh +22 -55
- package/scripts/host-temporary-cloud-mcp-e2e.sh +265 -0
- package/scripts/install-standalone-recovery.ts +24 -0
- package/scripts/lib/project-init-lib.sh +15 -44
- package/scripts/migrate-project-template.sh +23 -1179
- package/scripts/new-plan.sh +1 -1
- package/scripts/plan-to-todo.sh +11 -36
- package/scripts/run-governed-gate.ts +2 -1
- package/scripts/ship-worktrees.sh +2 -29
- 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/verify-contract.sh +1 -111
- package/scripts/verify-forge-runtime.sh +0 -1
- package/scripts/verify-sprint.sh +3 -180
- package/scripts/workflow-contract.ts +1 -5
- package/src/cli/commands/init-hook.ts +3 -3
- package/src/cli/commands/mcp.ts +9 -5
- package/src/cli/commands/plugin.ts +1 -1
- 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 +106 -3
- 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 +785 -469
- 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/github.ts +133 -0
- 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 +0 -217
- 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 +0 -513
- 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 +0 -189
- package/src/cli/mcp/multi-repository.ts +16 -5
- package/src/cli/mcp/oauth.ts +175 -26
- package/src/cli/mcp/repository-tools.ts +247 -107
- package/src/cli/mcp/server.ts +537 -111
- package/src/cli/mcp/tool-capability-cache.ts +1 -1
- package/src/cli/mcp/toolset-selection.ts +1 -1
- package/src/cli/mcp/toolset.ts +42 -5
- package/src/cli/mcp/transports/http.ts +101 -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 +101 -19
- package/src/cli/repositories/command-executor.ts +55 -521
- 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 +121 -0
- package/src/cli/repositories/selected-path-actions.ts +17 -9
- package/src/cli/repositories/structured-git.ts +61 -3
- package/src/cli/repository/inspector.ts +553 -63
- 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.ts +275 -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 +47 -8
- package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +86 -24
- 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-handle-store.ts +27 -2
- package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +258 -0
- package/src/runtime/control-plane/execution/work-remote-delivery.ts +215 -0
- package/src/runtime/control-plane/execution/work-task-receipt.ts +54 -11
- package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +143 -8
- package/src/runtime/control-plane/execution/work-validation-reconciler.ts +22 -14
- package/src/runtime/control-plane/execution/work-verification-snapshot.ts +85 -8
- package/src/runtime/control-plane/facade/capability-registry.ts +129 -25
- package/src/runtime/control-plane/facade/controller-round-relay.ts +991 -0
- package/src/runtime/control-plane/facade/controller-session-store.ts +132 -1
- package/src/runtime/control-plane/facade/goal-workloop-access.ts +2 -0
- package/src/runtime/control-plane/facade/goal-workloop.ts +686 -129
- package/src/runtime/control-plane/facade/index.ts +1 -0
- package/src/runtime/control-plane/facade/plan-contract-store.ts +185 -20
- 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 +22 -5
- package/src/runtime/control-plane/facade/work-contract-store.ts +175 -73
- 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 +145 -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 +273 -542
- 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 +50 -0
- package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +355 -116
- package/src/runtime/control-plane/launcher/launch-reservation-store.ts +40 -0
- package/src/runtime/control-plane/launcher/thin-launcher.ts +194 -10
- package/src/runtime/control-plane/persistence/controller-home-migration.ts +487 -0
- 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/routing/route-policy.ts +7 -16
- 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/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 +53 -0
- package/src/runtime/execution/process-runtime/check-scheduling.ts +114 -0
- package/src/runtime/execution/process-runtime/command-facade.ts +345 -40
- package/src/runtime/execution/process-runtime/gc.ts +38 -6
- package/src/runtime/execution/process-runtime/lightweight-managed.ts +1184 -0
- package/src/runtime/execution/process-runtime/persisted-check.ts +47 -4
- package/src/runtime/execution/process-runtime/resource-claims.ts +10 -6
- package/src/runtime/execution/process-runtime/runtime.ts +43 -3
- package/src/runtime/execution/process-runtime/store.ts +1 -0
- package/src/runtime/execution/process-runtime/types.ts +2 -0
- 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/workers/executor.ts +0 -48
- package/src/runtime/gateway/mcp/execution-tools.ts +1192 -114
- package/src/runtime/gateway/mcp/process-tools.ts +83 -37
- package/src/runtime/gateway/mcp/router.ts +184 -82
- package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +546 -0
- package/src/runtime/gateway/mcp/runtime-tools.ts +2224 -1589
- package/src/runtime/plugins/app-store-connect-adapter.ts +123 -19
- package/src/runtime/plugins/browser-adapter.ts +2901 -241
- package/src/runtime/plugins/browser-automation-service.ts +10 -0
- package/src/runtime/plugins/browser-handoff.ts +92 -2
- package/src/runtime/plugins/browser-macos-bridge.ts +582 -49
- 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 +152 -0
- package/src/runtime/plugins/browser-runtime.ts +183 -0
- package/src/runtime/plugins/browser-session-authority.ts +332 -0
- package/src/runtime/plugins/capability-authorization-grants.ts +354 -0
- package/src/runtime/plugins/desktop-operator-registration.ts +8 -2
- package/src/runtime/plugins/external-adapter.ts +324 -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 +55 -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 +269 -29
- package/src/runtime/plugins/types.ts +35 -2
- 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 +393 -28
- package/src/runtime/root/mcp-transport.ts +180 -10
- package/src/runtime/root/package-connector-service.ts +186 -8
- package/src/runtime/root/package-runtime-release.ts +143 -17
- package/src/runtime/root/package-runtime-service.ts +430 -22
- package/src/runtime/root/release-manifest.ts +7 -2
- package/src/runtime/root/release-materialize.ts +19 -0
- 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 +2 -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 +700 -92
- package/src/runtime/standalone-recovery/entry.ts +99 -23
- package/src/runtime/standalone-recovery/installer.ts +19 -3
- 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 +287 -151
- package/src/runtime/workflow/schedules/github-issue-watch.ts +158 -0
- package/src/runtime/workflow/schedules/settlement.ts +48 -4
- package/src/runtime/workflow/schedules/store.ts +131 -10
- 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
|
@@ -5,6 +5,7 @@ export * from './work-task-projection';
|
|
|
5
5
|
export * from './work-continuation';
|
|
6
6
|
export * from './controller-session-store';
|
|
7
7
|
export * from './plan-contract-store';
|
|
8
|
+
export * from './semantic-admission';
|
|
8
9
|
export * from './facade-result';
|
|
9
10
|
export * from './suggested-actions';
|
|
10
11
|
export * from './capability-registry';
|
|
@@ -3,12 +3,21 @@ import { join } from 'path';
|
|
|
3
3
|
import { repositoryControllerRoot } from '../../../cli/repositories/controller-home';
|
|
4
4
|
import { withControllerLock } from '../../../cli/repositories/locks';
|
|
5
5
|
import { readJsonFile, sanitizeFileComponent, writeJsonAtomic } from '../../shared/json-files';
|
|
6
|
+
import {
|
|
7
|
+
normalizePlanScopeKey,
|
|
8
|
+
resolvePlanAdmission,
|
|
9
|
+
withPlanAdmissionLock,
|
|
10
|
+
withPlanAdmissionLockAsync,
|
|
11
|
+
type PlanAdmissionRelation,
|
|
12
|
+
type PlanAdmissionResolution,
|
|
13
|
+
} from './semantic-admission';
|
|
6
14
|
import {
|
|
7
15
|
listControlPlaneRecords,
|
|
8
16
|
readControlPlaneRecordWithinTransaction,
|
|
9
17
|
withControlPlaneTransaction,
|
|
10
18
|
writeControlPlaneRecordWithinTransaction,
|
|
11
19
|
} from '../persistence/sqlite-store';
|
|
20
|
+
import { readRequirement } from '../persistence/requirement-store';
|
|
12
21
|
import {
|
|
13
22
|
isTerminalPlanContractStatus,
|
|
14
23
|
type EvidenceRef,
|
|
@@ -46,6 +55,13 @@ export interface CreatePlanContractInput {
|
|
|
46
55
|
evidenceRefs?: EvidenceRef[];
|
|
47
56
|
}
|
|
48
57
|
|
|
58
|
+
export interface AdmitPlanContractInput extends CreatePlanContractInput {
|
|
59
|
+
planRelation?: PlanAdmissionRelation;
|
|
60
|
+
relatedPlanId?: string;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export type AdmitPlanContractResult = PlanAdmissionResolution;
|
|
64
|
+
|
|
49
65
|
export interface PlanContractSummary {
|
|
50
66
|
planId: string;
|
|
51
67
|
repoId: string;
|
|
@@ -68,7 +84,7 @@ function bounded(values: readonly string[] | undefined, limit: number, maxLength
|
|
|
68
84
|
}
|
|
69
85
|
|
|
70
86
|
function normalizeScopeKey(value: string): string {
|
|
71
|
-
return
|
|
87
|
+
return normalizePlanScopeKey(value) || 'unknown';
|
|
72
88
|
}
|
|
73
89
|
|
|
74
90
|
function normalizeStep(step: CreatePlanContractInput['steps'][number]): PlanStep {
|
|
@@ -115,6 +131,14 @@ function updatePlanContract(
|
|
|
115
131
|
);
|
|
116
132
|
}
|
|
117
133
|
|
|
134
|
+
function assertRequirementReference(options: PlanContractStoreOptions, requirementId: string | undefined): void {
|
|
135
|
+
const normalized = requirementId?.trim();
|
|
136
|
+
if (!normalized || !options.controllerHome) return;
|
|
137
|
+
if (!readRequirement({ controllerHome: options.controllerHome, now: options.now }, normalized)) {
|
|
138
|
+
throw new Error(`PLAN_REQUIREMENT_NOT_FOUND: ${normalized}`);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
|
|
118
142
|
export function planContractRoot(location: PlanContractStoreLocation): string {
|
|
119
143
|
if (location.root) {
|
|
120
144
|
mkdirSync(location.root, { recursive: true });
|
|
@@ -205,11 +229,10 @@ function writePlanContractStore(options: PlanContractStoreOptions, store: PlanCo
|
|
|
205
229
|
return store;
|
|
206
230
|
}
|
|
207
231
|
|
|
208
|
-
|
|
209
|
-
const at = nowIso(options);
|
|
232
|
+
function buildPlanContract(input: CreatePlanContractInput, at: string): PlanContract {
|
|
210
233
|
const planId = sanitizeFileComponent(input.planId);
|
|
211
234
|
if (!String(input.planId ?? '').trim() || planId === 'unknown') throw new Error('plan_id is required');
|
|
212
|
-
|
|
235
|
+
return {
|
|
213
236
|
schemaVersion: 1,
|
|
214
237
|
planId,
|
|
215
238
|
repoId: input.repoId,
|
|
@@ -229,6 +252,12 @@ export function createPlanContract(options: PlanContractStoreOptions, input: Cre
|
|
|
229
252
|
createdAt: at,
|
|
230
253
|
updatedAt: at,
|
|
231
254
|
};
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
function createPlanContractUnlocked(options: PlanContractStoreOptions, input: CreatePlanContractInput): PlanContract {
|
|
258
|
+
assertRequirementReference(options, input.requirementId);
|
|
259
|
+
const at = nowIso(options);
|
|
260
|
+
const plan = buildPlanContract(input, at);
|
|
232
261
|
const store = readPlanContractStore(options);
|
|
233
262
|
if (store.contracts.some((existing) => existing.planId === plan.planId)) {
|
|
234
263
|
throw new Error(`plan contract already exists: ${plan.planId}`);
|
|
@@ -237,6 +266,77 @@ export function createPlanContract(options: PlanContractStoreOptions, input: Cre
|
|
|
237
266
|
return plan;
|
|
238
267
|
}
|
|
239
268
|
|
|
269
|
+
function replacePlanContractUnlocked(
|
|
270
|
+
options: PlanContractStoreOptions,
|
|
271
|
+
predecessorId: string,
|
|
272
|
+
input: CreatePlanContractInput,
|
|
273
|
+
): PlanContract {
|
|
274
|
+
assertRequirementReference(options, input.requirementId);
|
|
275
|
+
const at = nowIso(options);
|
|
276
|
+
const store = readPlanContractStore(options);
|
|
277
|
+
const predecessorKey = sanitizeFileComponent(predecessorId);
|
|
278
|
+
const predecessorIndex = store.contracts.findIndex((contract) => contract.planId === predecessorKey);
|
|
279
|
+
if (predecessorIndex < 0) throw new Error(`plan contract not found: ${predecessorKey}`);
|
|
280
|
+
const predecessor = store.contracts[predecessorIndex]!;
|
|
281
|
+
if (isTerminalPlanContractStatus(predecessor.status)) throw new Error(`plan contract ${predecessor.planId} is terminal (${predecessor.status})`);
|
|
282
|
+
const successor = buildPlanContract(input, at);
|
|
283
|
+
if (successor.planId === predecessor.planId) throw new Error('PLAN_SUCCESSOR_ID_MUST_CHANGE');
|
|
284
|
+
if (successor.repoId !== predecessor.repoId) throw new Error('PLAN_SUCCESSOR_REPOSITORY_MISMATCH');
|
|
285
|
+
if (store.contracts.some((existing) => existing.planId === successor.planId)) {
|
|
286
|
+
throw new Error(`plan contract already exists: ${successor.planId}`);
|
|
287
|
+
}
|
|
288
|
+
const conflictingScope = store.contracts.find((existing, index) => index !== predecessorIndex
|
|
289
|
+
&& !isTerminalPlanContractStatus(existing.status)
|
|
290
|
+
&& existing.scopeKey === successor.scopeKey);
|
|
291
|
+
if (conflictingScope) throw new Error(`PLAN_SCOPE_ALREADY_OWNED: ${successor.scopeKey}:${conflictingScope.planId}`);
|
|
292
|
+
const contracts = [...store.contracts];
|
|
293
|
+
contracts[predecessorIndex] = { ...predecessor, status: 'superseded', supersededBy: successor.planId, updatedAt: at };
|
|
294
|
+
writePlanContractStore(options, { schemaVersion: 1, updatedAt: at, contracts: [successor, ...contracts] });
|
|
295
|
+
return successor;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
export function createPlanContract(options: PlanContractStoreOptions, input: CreatePlanContractInput): PlanContract {
|
|
299
|
+
return withPlanAdmissionLock(options, () => {
|
|
300
|
+
const store = readPlanContractStore(options);
|
|
301
|
+
const planId = sanitizeFileComponent(input.planId);
|
|
302
|
+
if (store.contracts.some((existing) => existing.planId === planId)) {
|
|
303
|
+
throw new Error(`plan contract already exists: ${planId}`);
|
|
304
|
+
}
|
|
305
|
+
const scopeKey = normalizeScopeKey(input.scopeKey);
|
|
306
|
+
const existingScopeAuthority = scopeKey === 'unknown' ? undefined : store.contracts.find((existing) =>
|
|
307
|
+
!isTerminalPlanContractStatus(existing.status) && existing.scopeKey === scopeKey);
|
|
308
|
+
if (existingScopeAuthority) {
|
|
309
|
+
throw new Error(`PLAN_SCOPE_ALREADY_OWNED: ${scopeKey}:${existingScopeAuthority.planId}`);
|
|
310
|
+
}
|
|
311
|
+
return createPlanContractUnlocked(options, input);
|
|
312
|
+
});
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
function admitPlanContractUnlocked(options: PlanContractStoreOptions, input: AdmitPlanContractInput): AdmitPlanContractResult {
|
|
316
|
+
assertRequirementReference(options, input.requirementId);
|
|
317
|
+
const activePlans = listPlanContracts({ ...options, status: 'active', limit: 100 });
|
|
318
|
+
const resolution = resolvePlanAdmission(activePlans, {
|
|
319
|
+
requirementId: input.requirementId,
|
|
320
|
+
scopeKey: input.scopeKey,
|
|
321
|
+
planRelation: input.planRelation,
|
|
322
|
+
relatedPlanId: input.relatedPlanId,
|
|
323
|
+
});
|
|
324
|
+
if (resolution.admissionDecision === 'extend_existing' && resolution.plan) {
|
|
325
|
+
const successor = replacePlanContractUnlocked(options, resolution.plan.planId, input);
|
|
326
|
+
return { ...resolution, admissionDecision: 'create_new', plan: successor };
|
|
327
|
+
}
|
|
328
|
+
if (resolution.admissionDecision !== 'create_new') return resolution;
|
|
329
|
+
return { ...resolution, plan: createPlanContractUnlocked(options, input) };
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
export function admitPlanContract(options: PlanContractStoreOptions, input: AdmitPlanContractInput): AdmitPlanContractResult {
|
|
333
|
+
return withPlanAdmissionLock(options, () => admitPlanContractUnlocked(options, input));
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
export async function admitPlanContractAsync(options: PlanContractStoreOptions, input: AdmitPlanContractInput): Promise<AdmitPlanContractResult> {
|
|
337
|
+
return await withPlanAdmissionLockAsync(options, () => admitPlanContractUnlocked(options, input));
|
|
338
|
+
}
|
|
339
|
+
|
|
240
340
|
export function getPlanContract(options: PlanContractStoreOptions, planId: string): PlanContract | undefined {
|
|
241
341
|
return readPlanContractStore(options).contracts.find((contract) => contract.planId === sanitizeFileComponent(planId));
|
|
242
342
|
}
|
|
@@ -301,11 +401,12 @@ function approvalErrors(plan: PlanContract, allPlans: readonly PlanContract[]):
|
|
|
301
401
|
return [...new Set(errors)];
|
|
302
402
|
}
|
|
303
403
|
|
|
304
|
-
|
|
404
|
+
function approvePlanContractUnlocked(options: PlanContractStoreOptions, planId: string): PlanContract {
|
|
305
405
|
const store = readPlanContractStore(options);
|
|
306
406
|
const index = store.contracts.findIndex((contract) => contract.planId === sanitizeFileComponent(planId));
|
|
307
407
|
if (index < 0) throw new Error(`plan contract not found: ${sanitizeFileComponent(planId)}`);
|
|
308
408
|
const current = store.contracts[index];
|
|
409
|
+
assertRequirementReference(options, current.requirementId);
|
|
309
410
|
if (current.status !== 'draft' && current.status !== 'reviewing') throw new Error(`plan contract ${current.planId} cannot be approved from ${current.status}`);
|
|
310
411
|
const errors = approvalErrors(current, store.contracts);
|
|
311
412
|
if (errors.length > 0) throw new Error(`plan contract ${current.planId} cannot be approved: ${errors.join('; ')}`);
|
|
@@ -317,20 +418,33 @@ export function approvePlanContract(options: PlanContractStoreOptions, planId: s
|
|
|
317
418
|
return next;
|
|
318
419
|
}
|
|
319
420
|
|
|
421
|
+
export function approvePlanContract(options: PlanContractStoreOptions, planId: string): PlanContract {
|
|
422
|
+
return withPlanAdmissionLock(options, () => approvePlanContractUnlocked(options, planId));
|
|
423
|
+
}
|
|
424
|
+
|
|
425
|
+
export async function approvePlanContractAsync(options: PlanContractStoreOptions, planId: string): Promise<PlanContract> {
|
|
426
|
+
return await withPlanAdmissionLockAsync(options, () => approvePlanContractUnlocked(options, planId));
|
|
427
|
+
}
|
|
428
|
+
|
|
320
429
|
export function supersedePlanContract(options: PlanContractStoreOptions, planId: string, supersededBy: string): PlanContract {
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
430
|
+
return withPlanAdmissionLock(options, () => {
|
|
431
|
+
const store = readPlanContractStore(options);
|
|
432
|
+
const index = store.contracts.findIndex((contract) => contract.planId === sanitizeFileComponent(planId));
|
|
433
|
+
if (index < 0) throw new Error(`plan contract not found: ${sanitizeFileComponent(planId)}`);
|
|
434
|
+
const current = store.contracts[index]!;
|
|
435
|
+
if (isTerminalPlanContractStatus(current.status)) throw new Error(`plan contract ${current.planId} is terminal (${current.status})`);
|
|
436
|
+
const replacement = sanitizeFileComponent(supersededBy);
|
|
437
|
+
if (!replacement || replacement === 'unknown') throw new Error('superseded_by is required');
|
|
438
|
+
if (replacement === current.planId) throw new Error('PLAN_SUCCESSOR_ID_MUST_CHANGE');
|
|
439
|
+
const successor = store.contracts.find((contract) => contract.planId === replacement);
|
|
440
|
+
if (!successor) throw new Error(`PLAN_SUCCESSOR_NOT_FOUND: ${replacement}`);
|
|
441
|
+
const at = nowIso(options);
|
|
442
|
+
const next = { ...current, status: 'superseded' as const, supersededBy: successor.planId, updatedAt: at };
|
|
443
|
+
const contracts = [...store.contracts];
|
|
444
|
+
contracts[index] = next;
|
|
445
|
+
writePlanContractStore(options, { schemaVersion: 1, updatedAt: at, contracts });
|
|
446
|
+
return next;
|
|
447
|
+
});
|
|
334
448
|
}
|
|
335
449
|
|
|
336
450
|
/**
|
|
@@ -343,6 +457,7 @@ export function claimPlanStepForWork(
|
|
|
343
457
|
input: { planId: string; stepId: string; workId: string; sourceRevision: string },
|
|
344
458
|
): PlanContract {
|
|
345
459
|
return updatePlanContract(options, input.planId, (current) => {
|
|
460
|
+
assertRequirementReference(options, current.requirementId);
|
|
346
461
|
if (current.status !== 'approved' && current.status !== 'executing') {
|
|
347
462
|
throw new Error(`PLAN_NOT_EXECUTABLE: ${current.planId} is ${current.status}`);
|
|
348
463
|
}
|
|
@@ -363,6 +478,45 @@ export function claimPlanStepForWork(
|
|
|
363
478
|
});
|
|
364
479
|
}
|
|
365
480
|
|
|
481
|
+
export function repairDanglingPlanStepWorkBinding(
|
|
482
|
+
options: PlanContractStoreOptions,
|
|
483
|
+
input: { planId: string; stepId: string; expectedWorkId: string; reason: string },
|
|
484
|
+
): PlanContract {
|
|
485
|
+
const expectedWorkId = input.expectedWorkId.trim();
|
|
486
|
+
const reason = input.reason.trim();
|
|
487
|
+
if (!expectedWorkId) throw new Error('PLAN_STEP_REPAIR_EXPECTED_WORK_REQUIRED');
|
|
488
|
+
if (!reason) throw new Error('PLAN_STEP_REPAIR_REASON_REQUIRED');
|
|
489
|
+
return updatePlanContract(options, input.planId, (current) => {
|
|
490
|
+
if (isTerminalPlanContractStatus(current.status)) throw new Error(`PLAN_STEP_REPAIR_PLAN_TERMINAL: ${current.planId} is ${current.status}`);
|
|
491
|
+
const stepIndex = current.steps.findIndex((step) => step.id === sanitizeFileComponent(input.stepId));
|
|
492
|
+
if (stepIndex < 0) throw new Error(`PLAN_STEP_NOT_FOUND: ${input.stepId}`);
|
|
493
|
+
const step = current.steps[stepIndex];
|
|
494
|
+
if (step.workId !== expectedWorkId) {
|
|
495
|
+
throw new Error(`PLAN_STEP_REPAIR_BINDING_CHANGED: ${step.id} is bound to ${step.workId ?? 'no Work'}, expected ${expectedWorkId}`);
|
|
496
|
+
}
|
|
497
|
+
if (step.status !== 'executing') {
|
|
498
|
+
throw new Error(`PLAN_STEP_REPAIR_STATUS_INVALID: ${step.id} is ${step.status}`);
|
|
499
|
+
}
|
|
500
|
+
const at = nowIso(options);
|
|
501
|
+
const steps = [...current.steps];
|
|
502
|
+
steps[stepIndex] = {
|
|
503
|
+
...step,
|
|
504
|
+
status: 'ready',
|
|
505
|
+
workId: undefined,
|
|
506
|
+
evidenceRefs: [{
|
|
507
|
+
title: 'dangling Work binding repaired',
|
|
508
|
+
summary: `${expectedWorkId}: ${reason}`.slice(0, 2_000),
|
|
509
|
+
detailLevel: 'summary' as const,
|
|
510
|
+
}, ...step.evidenceRefs].slice(0, 20),
|
|
511
|
+
};
|
|
512
|
+
// This is admission-state repair, not semantic replanning: the Plan and its
|
|
513
|
+
// acceptance criteria remain authoritative. Return the exact step to ready
|
|
514
|
+
// so a later start can atomically admit one replacement for the missing
|
|
515
|
+
// record without forcing a second Plan authority.
|
|
516
|
+
return { ...current, status: 'executing', steps, updatedAt: at };
|
|
517
|
+
});
|
|
518
|
+
}
|
|
519
|
+
|
|
366
520
|
export function completePlanStepForWork(
|
|
367
521
|
options: PlanContractStoreOptions,
|
|
368
522
|
input: {
|
|
@@ -392,6 +546,10 @@ export function completePlanStepForWork(
|
|
|
392
546
|
// semantic validation until an explicit Controller acceptance records it.
|
|
393
547
|
// Failed/cancelled Work returns the slice to ready for replanning.
|
|
394
548
|
status: delivered ? 'validating' : 'ready',
|
|
549
|
+
// Successful delivery keeps the exact Work identity through semantic
|
|
550
|
+
// validation. Failed/cancelled Work releases the step authority so the
|
|
551
|
+
// replanning path cannot retain a ghost binding to a terminal Work.
|
|
552
|
+
workId: delivered ? step.workId : undefined,
|
|
395
553
|
evidenceRefs: input.work.evidenceRefs.length > 0 ? input.work.evidenceRefs.slice(0, 20) : step.evidenceRefs,
|
|
396
554
|
};
|
|
397
555
|
return { ...current, status: delivered ? 'verifying' : 'replanning', steps, updatedAt: at };
|
|
@@ -400,7 +558,7 @@ export function completePlanStepForWork(
|
|
|
400
558
|
|
|
401
559
|
export function acceptPlanStepEvidence(
|
|
402
560
|
options: PlanContractStoreOptions,
|
|
403
|
-
input: { planId: string; stepId: string; reviewer: string; rationale: string },
|
|
561
|
+
input: { planId: string; stepId: string; reviewer: string; rationale: string; acceptedSourceRevision?: string },
|
|
404
562
|
): PlanContract {
|
|
405
563
|
const reviewer = input.reviewer.trim();
|
|
406
564
|
const rationale = input.rationale.trim();
|
|
@@ -418,6 +576,13 @@ export function acceptPlanStepEvidence(
|
|
|
418
576
|
evidenceRefs: [{ title: 'semantic acceptance', summary: `${reviewer}: ${rationale}`, detailLevel: 'summary' as const }, ...step.evidenceRefs].slice(0, 20),
|
|
419
577
|
};
|
|
420
578
|
const allCompleted = steps.every((candidate) => candidate.status === 'completed');
|
|
421
|
-
|
|
579
|
+
const acceptedSourceRevision = input.acceptedSourceRevision?.trim();
|
|
580
|
+
return {
|
|
581
|
+
...current,
|
|
582
|
+
...(acceptedSourceRevision ? { sourceRevision: acceptedSourceRevision } : {}),
|
|
583
|
+
status: allCompleted ? 'finalized' : 'executing',
|
|
584
|
+
steps,
|
|
585
|
+
updatedAt: nowIso(options),
|
|
586
|
+
};
|
|
422
587
|
});
|
|
423
588
|
}
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
import { withControllerLock, withControllerLockAsync } from '../../../cli/repositories/locks';
|
|
2
|
+
import { sanitizeFileComponent } from '../../shared/json-files';
|
|
3
|
+
import { isTerminalPlanContractStatus, type PlanContract } from './types';
|
|
4
|
+
|
|
5
|
+
export const PLAN_ADMISSION_RELATIONS = ['extend', 'parallel'] as const;
|
|
6
|
+
export type PlanAdmissionRelation = (typeof PLAN_ADMISSION_RELATIONS)[number];
|
|
7
|
+
|
|
8
|
+
export type PlanAdmissionReason =
|
|
9
|
+
| 'exact_scope_authority'
|
|
10
|
+
| 'requirement_relation_required'
|
|
11
|
+
| 'extension_target_required'
|
|
12
|
+
| 'extend_existing'
|
|
13
|
+
| 'create_new';
|
|
14
|
+
|
|
15
|
+
export interface PlanAdmissionInput {
|
|
16
|
+
requirementId?: string;
|
|
17
|
+
scopeKey: string;
|
|
18
|
+
planRelation?: PlanAdmissionRelation;
|
|
19
|
+
relatedPlanId?: string;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export interface PlanAdmissionResolution {
|
|
23
|
+
admissionDecision: 'reuse_existing' | 'resolution_required' | 'extend_existing' | 'create_new';
|
|
24
|
+
resolutionRequired: boolean;
|
|
25
|
+
reason: PlanAdmissionReason;
|
|
26
|
+
normalizedScopeKey: string;
|
|
27
|
+
plan?: PlanContract;
|
|
28
|
+
candidates: PlanContract[];
|
|
29
|
+
allowedPlanRelations?: readonly PlanAdmissionRelation[];
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export function normalizePlanScopeKey(value: string): string {
|
|
33
|
+
const raw = String(value ?? '').trim();
|
|
34
|
+
if (!raw) return '';
|
|
35
|
+
const normalized = sanitizeFileComponent(raw).toLowerCase().slice(0, 160);
|
|
36
|
+
return normalized === 'unknown' ? '' : normalized;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function resolvePlanAdmission(
|
|
40
|
+
plans: readonly PlanContract[],
|
|
41
|
+
input: PlanAdmissionInput,
|
|
42
|
+
): PlanAdmissionResolution {
|
|
43
|
+
const activePlans = plans.filter((plan) => !isTerminalPlanContractStatus(plan.status));
|
|
44
|
+
const normalizedScopeKey = normalizePlanScopeKey(input.scopeKey);
|
|
45
|
+
const requirementId = input.requirementId?.trim() || undefined;
|
|
46
|
+
const requirementPlans = requirementId
|
|
47
|
+
? activePlans.filter((candidate) => candidate.requirementId === requirementId)
|
|
48
|
+
: [];
|
|
49
|
+
const relatedPlanId = input.relatedPlanId?.trim() || undefined;
|
|
50
|
+
const explicitlyRelatedPlan = relatedPlanId
|
|
51
|
+
? activePlans.find((candidate) => candidate.planId === relatedPlanId)
|
|
52
|
+
: undefined;
|
|
53
|
+
const relatedPlan = explicitlyRelatedPlan && (!requirementId || explicitlyRelatedPlan.requirementId === requirementId)
|
|
54
|
+
? explicitlyRelatedPlan
|
|
55
|
+
: !relatedPlanId && requirementPlans.length === 1 ? requirementPlans[0] : undefined;
|
|
56
|
+
const exactScopeAuthority = normalizedScopeKey
|
|
57
|
+
? activePlans.find((candidate) => candidate.scopeKey === normalizedScopeKey)
|
|
58
|
+
: undefined;
|
|
59
|
+
|
|
60
|
+
// An explicit extension is a serial replan, not a second active Plan. Resolve
|
|
61
|
+
// it before exact-scope reuse so a successor may retain the predecessor scope.
|
|
62
|
+
// If another Plan owns the requested successor scope, preserve that authority.
|
|
63
|
+
if (input.planRelation === 'extend') {
|
|
64
|
+
if (!relatedPlan) {
|
|
65
|
+
return {
|
|
66
|
+
admissionDecision: 'resolution_required',
|
|
67
|
+
resolutionRequired: true,
|
|
68
|
+
reason: 'extension_target_required',
|
|
69
|
+
normalizedScopeKey,
|
|
70
|
+
candidates: (requirementPlans.length > 0 ? requirementPlans : activePlans).slice(0, 8),
|
|
71
|
+
allowedPlanRelations: PLAN_ADMISSION_RELATIONS,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
if (exactScopeAuthority && exactScopeAuthority.planId !== relatedPlan.planId) {
|
|
75
|
+
return {
|
|
76
|
+
admissionDecision: 'reuse_existing',
|
|
77
|
+
resolutionRequired: false,
|
|
78
|
+
reason: 'exact_scope_authority',
|
|
79
|
+
normalizedScopeKey,
|
|
80
|
+
plan: exactScopeAuthority,
|
|
81
|
+
candidates: [exactScopeAuthority],
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
return {
|
|
85
|
+
admissionDecision: 'extend_existing',
|
|
86
|
+
resolutionRequired: false,
|
|
87
|
+
reason: 'extend_existing',
|
|
88
|
+
normalizedScopeKey,
|
|
89
|
+
plan: relatedPlan,
|
|
90
|
+
candidates: requirementPlans.length > 0 ? requirementPlans.slice(0, 8) : [relatedPlan],
|
|
91
|
+
};
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
if (exactScopeAuthority) {
|
|
95
|
+
return {
|
|
96
|
+
admissionDecision: 'reuse_existing',
|
|
97
|
+
resolutionRequired: false,
|
|
98
|
+
reason: 'exact_scope_authority',
|
|
99
|
+
normalizedScopeKey,
|
|
100
|
+
plan: exactScopeAuthority,
|
|
101
|
+
candidates: [exactScopeAuthority],
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
if (requirementPlans.length > 0 && !input.planRelation) {
|
|
106
|
+
return {
|
|
107
|
+
admissionDecision: 'resolution_required',
|
|
108
|
+
resolutionRequired: true,
|
|
109
|
+
reason: 'requirement_relation_required',
|
|
110
|
+
normalizedScopeKey,
|
|
111
|
+
candidates: requirementPlans.slice(0, 8),
|
|
112
|
+
allowedPlanRelations: PLAN_ADMISSION_RELATIONS,
|
|
113
|
+
};
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
admissionDecision: 'create_new',
|
|
118
|
+
resolutionRequired: false,
|
|
119
|
+
reason: 'create_new',
|
|
120
|
+
normalizedScopeKey,
|
|
121
|
+
candidates: requirementPlans.slice(0, 8),
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Short per-repository semantic admission critical section. The fixed task key
|
|
127
|
+
* keeps admission independent from repository/worktree execution locks while
|
|
128
|
+
* still coordinating competing controller processes for the same repository.
|
|
129
|
+
*/
|
|
130
|
+
const planAdmissionLockKey = (repoId: string) => ({
|
|
131
|
+
scope: 'task' as const,
|
|
132
|
+
repoId,
|
|
133
|
+
taskId: 'semantic-plan-admission',
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
export function withPlanAdmissionLock<T>(
|
|
137
|
+
options: { controllerHome?: string; repoId?: string },
|
|
138
|
+
operation: () => T,
|
|
139
|
+
): T {
|
|
140
|
+
if (!options.controllerHome?.trim() || !options.repoId?.trim()) return operation();
|
|
141
|
+
return withControllerLock(
|
|
142
|
+
options.controllerHome,
|
|
143
|
+
planAdmissionLockKey(options.repoId),
|
|
144
|
+
'semantic-plan-admission',
|
|
145
|
+
operation,
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
/** Gateway-facing admission waits briefly for a competing controller to finish
|
|
150
|
+
* its tiny create/approve decision, then re-reads canonical state under lock. */
|
|
151
|
+
export async function withPlanAdmissionLockAsync<T>(
|
|
152
|
+
options: { controllerHome?: string; repoId?: string },
|
|
153
|
+
operation: () => T | Promise<T>,
|
|
154
|
+
): Promise<T> {
|
|
155
|
+
if (!options.controllerHome?.trim() || !options.repoId?.trim()) return await operation();
|
|
156
|
+
return await withControllerLockAsync(
|
|
157
|
+
options.controllerHome,
|
|
158
|
+
planAdmissionLockKey(options.repoId),
|
|
159
|
+
'semantic-plan-admission',
|
|
160
|
+
async () => await operation(),
|
|
161
|
+
undefined,
|
|
162
|
+
500,
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
const primaryWorkAdmissionLockKey = (repoId: string) => ({
|
|
167
|
+
scope: 'task' as const,
|
|
168
|
+
repoId,
|
|
169
|
+
taskId: 'semantic-primary-work-admission',
|
|
170
|
+
});
|
|
171
|
+
|
|
172
|
+
/**
|
|
173
|
+
* Primary Work admission protects only canonical ownership resolution and
|
|
174
|
+
* persistence. It must never span workspace materialization, dependency prep,
|
|
175
|
+
* checks, worker launch, or the Work execution lifetime.
|
|
176
|
+
*/
|
|
177
|
+
export function withPrimaryWorkAdmissionLock<T>(
|
|
178
|
+
options: { controllerHome?: string; repoId?: string },
|
|
179
|
+
operation: () => T,
|
|
180
|
+
): T {
|
|
181
|
+
if (!options.controllerHome?.trim() || !options.repoId?.trim()) return operation();
|
|
182
|
+
return withControllerLock(
|
|
183
|
+
options.controllerHome,
|
|
184
|
+
primaryWorkAdmissionLockKey(options.repoId),
|
|
185
|
+
'semantic-primary-work-admission',
|
|
186
|
+
operation,
|
|
187
|
+
);
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/** Gateway callers wait briefly for a competing admission, then re-read the
|
|
191
|
+
* canonical Plan/Work state while holding the same short critical section. */
|
|
192
|
+
export async function withPrimaryWorkAdmissionLockAsync<T>(
|
|
193
|
+
options: { controllerHome?: string; repoId?: string },
|
|
194
|
+
operation: () => T | Promise<T>,
|
|
195
|
+
): Promise<T> {
|
|
196
|
+
if (!options.controllerHome?.trim() || !options.repoId?.trim()) return await operation();
|
|
197
|
+
return await withControllerLockAsync(
|
|
198
|
+
options.controllerHome,
|
|
199
|
+
primaryWorkAdmissionLockKey(options.repoId),
|
|
200
|
+
'semantic-primary-work-admission',
|
|
201
|
+
async () => await operation(),
|
|
202
|
+
undefined,
|
|
203
|
+
500,
|
|
204
|
+
);
|
|
205
|
+
}
|
|
@@ -11,10 +11,13 @@ function expectedActionRisk(action: SuggestedNextAction): SuggestedNextAction['r
|
|
|
11
11
|
case 'delegate':
|
|
12
12
|
case 'controller_claim':
|
|
13
13
|
case 'controller_release':
|
|
14
|
+
case 'controller_disposition':
|
|
14
15
|
case 'launcher_start':
|
|
16
|
+
case 'requirement_create':
|
|
15
17
|
case 'schedule_create':
|
|
16
18
|
case 'schedule_pause':
|
|
17
19
|
case 'schedule_resume':
|
|
20
|
+
case 'schedule_delete':
|
|
18
21
|
case 'schedule_trigger':
|
|
19
22
|
return 'workspace_write';
|
|
20
23
|
case 'controller_get_owner':
|
|
@@ -26,6 +29,7 @@ function expectedActionRisk(action: SuggestedNextAction): SuggestedNextAction['r
|
|
|
26
29
|
case 'plan_list':
|
|
27
30
|
return 'readonly';
|
|
28
31
|
case 'plan_approve':
|
|
32
|
+
case 'plan_accept_step':
|
|
29
33
|
case 'plan_supersede':
|
|
30
34
|
return 'workspace_write';
|
|
31
35
|
case 'finalize':
|
|
@@ -40,7 +44,7 @@ const ALLOWED_FACADE_OPERATIONS: Record<FacadeTool, readonly string[]> = {
|
|
|
40
44
|
rh_status: ['list', 'get', 'repair'],
|
|
41
45
|
rh_inbox: ['list', 'get', 'ack', 'accept', 'resolve', 'dismiss', 'create'],
|
|
42
46
|
rh_context: ['list', 'get', 'search'],
|
|
43
|
-
rh_work: ['start', 'continue', 'verify', 'repair', 'finalize', 'stop', 'delegate', 'controller_claim', 'controller_release', 'controller_get_owner', 'launcher_start', 'plan_create', 'plan_get', 'plan_list', 'plan_approve', 'plan_supersede', 'schedule_create', 'schedule_list', 'schedule_get', 'schedule_pause', 'schedule_resume', 'schedule_trigger'],
|
|
47
|
+
rh_work: ['start', 'continue', 'verify', 'repair', 'finalize', 'stop', 'delegate', 'controller_claim', 'controller_release', 'controller_disposition', 'controller_get_owner', 'launcher_start', 'requirement_create', 'plan_create', 'plan_get', 'plan_list', 'plan_approve', 'plan_accept_step', 'plan_supersede', 'schedule_create', 'schedule_list', 'schedule_get', 'schedule_pause', 'schedule_resume', 'schedule_delete', 'schedule_trigger'],
|
|
44
48
|
};
|
|
45
49
|
|
|
46
50
|
export interface SuggestedActionValidationOptions {
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import type { ProcessCheckReceiptEvidence } from '../../evidence/process-check-receipt';
|
|
2
2
|
import type { CompletionReceipt as RepositoryCompletionReceipt } from '../../../cli/controller/types';
|
|
3
3
|
import type { AccessMode } from '../governance/access-policy';
|
|
4
|
-
import { decideRoute, type RouteDecision, type RoutePolicyInput } from '../routing/route-policy';
|
|
4
|
+
import { decideRoute, type ExplicitTaskMode, type RouteDecision, type RoutePolicyInput } from '../routing/route-policy';
|
|
5
5
|
|
|
6
6
|
export const EXECUTION_MODES = ['direct_control', 'goal_workloop', 'handoff_only'] as const;
|
|
7
7
|
export type ExecutionMode = (typeof EXECUTION_MODES)[number];
|
|
8
8
|
|
|
9
9
|
export const FACADE_TOOLS = ['rh_access', 'rh_status', 'rh_inbox', 'rh_context', 'rh_work'] as const;
|
|
10
10
|
export type FacadeTool = (typeof FACADE_TOOLS)[number];
|
|
11
|
+
export type CapabilityExecutionSurface = FacadeTool | 'plugin_action_execute';
|
|
11
12
|
|
|
12
13
|
export const FACADE_STATUSES = ['ok', 'blocked', 'failed', 'approval_required', 'not_found'] as const;
|
|
13
14
|
export type FacadeStatus = (typeof FACADE_STATUSES)[number];
|
|
@@ -353,6 +354,8 @@ export interface WorkContractConstraints {
|
|
|
353
354
|
conflictsWithThinHarnessPolicy?: boolean;
|
|
354
355
|
/** True when the change alters the default routing or execution mode visible to users. */
|
|
355
356
|
changesDefaultExecutionStrategy?: boolean;
|
|
357
|
+
/** Repository-change delivery must publish the exact integrated revision before Work terminalization. */
|
|
358
|
+
remoteDeliveryRequired?: boolean;
|
|
356
359
|
}
|
|
357
360
|
|
|
358
361
|
export interface WorkContractDriverPolicy {
|
|
@@ -476,6 +479,18 @@ export interface WorkContract {
|
|
|
476
479
|
planStepId?: string;
|
|
477
480
|
planSourceRevision?: string;
|
|
478
481
|
scopeSummary?: string;
|
|
482
|
+
/**
|
|
483
|
+
* Discovery evidence, never an authorization boundary. Initial guesses,
|
|
484
|
+
* inspected paths, and observed changes remain distinct so early retrieval
|
|
485
|
+
* cannot masquerade as complete implementation scope.
|
|
486
|
+
*/
|
|
487
|
+
scopeEvidence?: {
|
|
488
|
+
initialLikelyPaths: string[];
|
|
489
|
+
inspectedPaths: string[];
|
|
490
|
+
actualChangedPaths: string[];
|
|
491
|
+
recordedAt: string;
|
|
492
|
+
};
|
|
493
|
+
/** Explicit policy fence. This is not semantic completeness evidence. */
|
|
479
494
|
allowedPaths: string[];
|
|
480
495
|
forbiddenPaths: string[];
|
|
481
496
|
checks: string[];
|
|
@@ -495,13 +510,12 @@ export interface WorkContract {
|
|
|
495
510
|
continuationPrompt?: string;
|
|
496
511
|
worktreeRef?: string;
|
|
497
512
|
workerRef?: string;
|
|
498
|
-
/** Stable idempotency key when this Work was accepted via work_submit. */
|
|
499
513
|
requestId?: string;
|
|
500
|
-
/**
|
|
514
|
+
/** Typed execution metadata; it does not imply ExecutionJob creation. */
|
|
501
515
|
submittedOperation?: SubmittedWorkOperation;
|
|
502
516
|
}
|
|
503
517
|
|
|
504
|
-
/**
|
|
518
|
+
/** Typed execution metadata retained on execution-child WorkContracts. */
|
|
505
519
|
export interface SubmittedWorkOperation {
|
|
506
520
|
name: string;
|
|
507
521
|
semanticKey: string;
|
|
@@ -630,7 +644,7 @@ export interface CapabilityDescriptor {
|
|
|
630
644
|
group: CapabilityGroup;
|
|
631
645
|
operationClass: CapabilityOperationClass;
|
|
632
646
|
risk: CapabilityRisk;
|
|
633
|
-
exposedVia:
|
|
647
|
+
exposedVia: CapabilityExecutionSurface;
|
|
634
648
|
schemaExposure: CapabilitySchemaExposure;
|
|
635
649
|
summary: string;
|
|
636
650
|
}
|
|
@@ -639,6 +653,7 @@ export interface CapabilityGroupSummary {
|
|
|
639
653
|
group: CapabilityGroup;
|
|
640
654
|
capabilityCount: number;
|
|
641
655
|
domains: CapabilityDomain[];
|
|
656
|
+
executionSurfaces: CapabilityExecutionSurface[];
|
|
642
657
|
facadeTools: FacadeTool[];
|
|
643
658
|
operationClasses: CapabilityOperationClass[];
|
|
644
659
|
risks: CapabilityRisk[];
|
|
@@ -667,6 +682,7 @@ export interface ExecutionModeSelectionInput {
|
|
|
667
682
|
requiresInvestigation?: boolean;
|
|
668
683
|
requiresLongRunningChecks?: boolean;
|
|
669
684
|
requiresParallelism?: boolean;
|
|
685
|
+
explicitMode?: ExplicitTaskMode;
|
|
670
686
|
needsDependencies?: boolean;
|
|
671
687
|
requiresRecovery?: boolean;
|
|
672
688
|
/** True only when the user explicitly requested an agent/worker executor. Complexity alone must not enable workers. */
|
|
@@ -717,6 +733,7 @@ export function selectExecutionMode(input: ExecutionModeSelectionInput): Executi
|
|
|
717
733
|
requiresInvestigation: input.requiresInvestigation,
|
|
718
734
|
requiresLongRunningChecks: input.requiresLongRunningChecks,
|
|
719
735
|
requiresParallelism: input.requiresParallelism,
|
|
736
|
+
explicitMode: input.explicitMode,
|
|
720
737
|
needsDependencies: input.needsDependencies,
|
|
721
738
|
agentRequested: input.requiresWorker,
|
|
722
739
|
},
|