@moretea-labs/forge 1.5.1 → 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 +46 -0
- package/CLAUDE.md +2 -0
- package/DESIGN.md +7 -2
- package/README.md +4 -4
- package/README.zh-CN.md +2 -2
- 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/pre-edit-guard.sh +8 -39
- package/assets/hooks/prompt-guard.sh +7 -83
- 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 +54 -3
- package/assets/reference-configs/document-generation.md +1 -1
- package/assets/reference-configs/harness-overview.md +1 -1
- package/assets/reference-configs/hook-operations.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 +13 -38
- 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/sprint-backlog.sh +6 -6
- 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 +38 -22
- package/docs/operations/controller-performance-and-502.md +124 -0
- package/docs/operations/controller-reliability-runbook.md +1 -1
- package/docs/operations/features.md +3 -3
- 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/tutorials/01-install-and-start.md +1 -1
- package/docs/tutorials/01-install-and-start.zh-CN.md +1 -1
- package/docs/wiki/Architecture.md +6 -6
- package/docs/wiki/Implementation.md +2 -2
- package/docs/wiki/Installation.md +1 -1
- package/docs/wiki/Integrations.md +2 -2
- package/docs/wiki/Quick-Start.md +1 -1
- 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-public-docs.sh +14 -1
- 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-chatgpt-automation-schedules.ts +64 -0
- 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/sprint-backlog.sh +1 -1
- 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/chatgpt.ts +31 -2
- package/src/cli/commands/init-hook.ts +3 -3
- package/src/cli/commands/mcp.ts +9 -5
- package/src/cli/commands/plugin.ts +37 -10
- 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 +898 -430
- 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/hook/prompt-guard-decision.ts +17 -18
- package/src/cli/hook/prompt-intents.ts +1 -1
- package/src/cli/installer/managed-entries.ts +15 -1
- package/src/cli/local-bridge/console-automations.ts +225 -67
- package/src/cli/local-bridge/console-view-models.ts +3 -35
- package/src/cli/local-bridge/facade-api.ts +2 -217
- package/src/cli/local-bridge/frontend/src/App.tsx +3 -3
- package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +153 -3
- package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +37 -4
- package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +183 -3
- package/src/cli/local-bridge/frontend/src/features/repositories/RepositoriesView.tsx +1 -1
- package/src/cli/local-bridge/frontend/src/features/work/WorkView.tsx +4 -3
- package/src/cli/local-bridge/frontend/src/lib/api.ts +18 -11
- package/src/cli/local-bridge/frontend/src/lib/routes.ts +1 -1
- package/src/cli/local-bridge/frontend/src/shell/CommandBar.tsx +1 -1
- package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +4 -3
- package/src/cli/local-bridge/frontend/src/styles.css +27 -11
- package/src/cli/local-bridge/frontend/src/types.ts +8 -7
- package/src/cli/local-bridge/job-store.ts +1 -490
- package/src/cli/local-bridge/server.ts +29 -532
- package/src/cli/local-bridge/ui-dist/app.css +1 -1
- package/src/cli/local-bridge/ui-dist/app.js +5101 -4967
- 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/paths.ts +8 -2
- package/src/cli/mcp/repository-tools.ts +356 -101
- package/src/cli/mcp/server.ts +625 -54
- 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 +125 -15
- package/src/cli/mcp/transports/session-registry.ts +67 -5
- package/src/cli/mcp/types.ts +3 -3
- package/src/cli/repositories/command-classifier.ts +209 -14
- 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/safe-patch.ts +6 -4
- package/src/cli/repositories/selected-path-actions.ts +17 -9
- package/src/cli/repositories/structured-git.ts +110 -10
- package/src/cli/repository/inspector.ts +565 -62
- 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 +196 -2
- package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +86 -24
- package/src/runtime/control-plane/execution/execution-identity.ts +65 -14
- package/src/runtime/control-plane/execution/session-store.ts +18 -0
- package/src/runtime/control-plane/execution/validation.ts +15 -2
- 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 +175 -8
- package/src/runtime/control-plane/execution/work-validation-reconciler.ts +28 -21
- package/src/runtime/control-plane/execution/work-verification-snapshot.ts +87 -9
- 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 +144 -14
- package/src/runtime/control-plane/facade/goal-workloop-access.ts +9 -2
- package/src/runtime/control-plane/facade/goal-workloop.ts +885 -105
- package/src/runtime/control-plane/facade/index.ts +1 -0
- package/src/runtime/control-plane/facade/plan-contract-store.ts +211 -26
- package/src/runtime/control-plane/facade/requirement-board.ts +24 -8
- 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 +55 -11
- package/src/runtime/control-plane/facade/work-contract-store.ts +204 -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 +642 -51
- 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/requirement-store.ts +15 -11
- 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 +19 -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 +52 -14
- package/src/runtime/execution/process-runtime/check-result.ts +63 -2
- package/src/runtime/execution/process-runtime/check-scheduling.ts +114 -0
- package/src/runtime/execution/process-runtime/command-facade.ts +346 -40
- package/src/runtime/execution/process-runtime/gc.ts +98 -12
- package/src/runtime/execution/process-runtime/lightweight-managed.ts +1184 -0
- package/src/runtime/execution/process-runtime/persisted-check.ts +57 -6
- package/src/runtime/execution/process-runtime/resource-claims.ts +97 -8
- package/src/runtime/execution/process-runtime/runtime.ts +59 -4
- package/src/runtime/execution/process-runtime/store.ts +7 -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 +1219 -126
- package/src/runtime/gateway/mcp/process-tools.ts +83 -37
- package/src/runtime/gateway/mcp/router.ts +185 -83
- package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +546 -0
- package/src/runtime/gateway/mcp/runtime-tools.ts +2555 -1616
- package/src/runtime/model-clients/deepseek-adapter.ts +0 -32
- package/src/runtime/plugins/app-store-connect-adapter.ts +123 -19
- package/src/runtime/plugins/browser-adapter.ts +2925 -318
- package/src/runtime/plugins/browser-automation-service.ts +10 -0
- package/src/runtime/plugins/browser-handoff-host.ts +6 -31
- package/src/runtime/plugins/browser-handoff.ts +92 -5
- package/src/runtime/plugins/browser-macos-bridge.ts +586 -45
- 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 +15 -12
- package/src/runtime/plugins/external-adapter.ts +324 -3
- package/src/runtime/plugins/external-registration.ts +84 -5
- package/src/runtime/plugins/external-unix-socket.ts +7 -1
- package/src/runtime/plugins/first-party-registry.ts +4 -2
- package/src/runtime/plugins/github-adapter.ts +125 -0
- package/src/runtime/plugins/gmail-adapter.ts +28 -2
- 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 +125 -8
- 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 +181 -0
- package/src/runtime/plugins/resend-adapter.ts +1 -1
- package/src/runtime/plugins/store.ts +366 -24
- package/src/runtime/plugins/types.ts +39 -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/actions.ts +0 -10
- package/src/runtime/recovery/classifier.ts +0 -9
- package/src/runtime/recovery/maintenance-executor.ts +415 -18
- package/src/runtime/recovery/probe.ts +0 -1
- package/src/runtime/recovery/types.ts +0 -1
- 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 +162 -19
- package/src/runtime/root/release-store.ts +48 -11
- package/src/runtime/root/runtime.ts +23 -5
- package/src/runtime/root/service.ts +100 -27
- package/src/runtime/root/types.ts +2 -0
- package/src/runtime/safe-tooling/index.ts +0 -1
- package/src/runtime/safe-tooling/ios-development.ts +321 -63
- package/src/runtime/safe-tooling/types.ts +0 -30
- 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 +702 -94
- 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 +258 -0
- package/src/runtime/workflow/schedules/chatgpt-automation-migration.ts +40 -0
- package/src/runtime/workflow/schedules/engine.ts +509 -80
- 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 +6 -3
- package/src/runtime/workflow/schedules/work-continuation.ts +120 -34
- 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/bin/forge-desktop-helper.mjs +0 -204
- 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 -453
- package/docs/architecture/current/codegraph-context-provider.md +0 -226
- 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 -354
- package/docs/architecture/current/entity-model.md +0 -592
- 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 -144
- 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 -311
- 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 -83
- package/docs/operations/controller-desktop-plugin.md +0 -112
- 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/plugins/desktop-adapter.ts +0 -374
- package/src/runtime/safe-tooling/browser-safe.ts +0 -133
- 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
package/src/cli/mcp/auth.ts
CHANGED
|
@@ -25,7 +25,7 @@ export interface McpLocalConfig {
|
|
|
25
25
|
localEndpoint?: string;
|
|
26
26
|
};
|
|
27
27
|
profile?: string;
|
|
28
|
-
toolset?: 'core' | 'advanced' | 'full';
|
|
28
|
+
toolset?: 'facade' | 'core' | 'advanced' | 'full';
|
|
29
29
|
/** Compatibility marker retained for previously persisted toolset choices. */
|
|
30
30
|
toolsetExplicit?: boolean;
|
|
31
31
|
accessMode?: AccessMode;
|
|
@@ -88,7 +88,7 @@ export interface McpRuntimeState {
|
|
|
88
88
|
forgeVersion?: string;
|
|
89
89
|
toolSurfaceFingerprint?: string;
|
|
90
90
|
runtimeToolSurfaceFingerprint?: string;
|
|
91
|
-
toolset?: 'core' | 'advanced' | 'full';
|
|
91
|
+
toolset?: 'facade' | 'core' | 'advanced' | 'full';
|
|
92
92
|
configuredAccessMode?: AccessMode;
|
|
93
93
|
effectiveAccessMode?: AccessMode;
|
|
94
94
|
effectiveToolset?: McpToolset;
|
|
@@ -131,7 +131,7 @@ export interface McpRuntimeState {
|
|
|
131
131
|
}>;
|
|
132
132
|
}
|
|
133
133
|
|
|
134
|
-
export type McpHttpAuthMode = 'oauth' | 'bearer';
|
|
134
|
+
export type McpHttpAuthMode = 'oauth' | 'bearer' | 'none';
|
|
135
135
|
|
|
136
136
|
export function mcpLocalConfigPath(repoRoot: string): string {
|
|
137
137
|
return join(repoRoot, '.forge', 'mcp.local.json');
|
|
@@ -356,8 +356,8 @@ export function syncMcpControllerHomeBearerToken(
|
|
|
356
356
|
|
|
357
357
|
export function parseMcpHttpAuthMode(value: string | undefined): McpHttpAuthMode {
|
|
358
358
|
const mode = (value ?? 'oauth').trim().toLowerCase();
|
|
359
|
-
if (mode === 'oauth' || mode === 'bearer') return mode;
|
|
360
|
-
throw new Error(`invalid --auth "${value}" (expected: oauth, bearer)`);
|
|
359
|
+
if (mode === 'oauth' || mode === 'bearer' || mode === 'none') return mode;
|
|
360
|
+
throw new Error(`invalid --auth "${value}" (expected: oauth, bearer, none)`);
|
|
361
361
|
}
|
|
362
362
|
|
|
363
363
|
export function readMcpOAuthPassphrase(repoRoot: string): string | null {
|
|
@@ -10,10 +10,11 @@ export function mcpServerInstructions(profile: McpProfileName): string {
|
|
|
10
10
|
return [
|
|
11
11
|
...common,
|
|
12
12
|
'Use the shortest valid execution path. Investigation or parallelism alone must not create durable Work, Issues, Plans, Agents, isolation, or worktrees; escalate only for actual recovery, dependency, risk, scope, external-effect, or long-running requirements.',
|
|
13
|
-
'For bounded implementation,
|
|
13
|
+
'For bounded implementation, start with rh_context(operation=search, retrieval_mode=implementation) as the default code retrieval router when the exact path is unknown, then repeat it with exact paths, symbols, tests, relationships, or impact domains whenever more evidence can materially improve correctness. Its current raw snippets and file SHA identities are valid implementation evidence; ChatGPT alone decides semantic sufficiency. For cross-cutting state/time/schema/cache/API/event/concurrency changes, ChatGPT should select relevant impact_domains so Forge expands those evidence dimensions mechanically; Forge never infers that the selected domains are semantically complete. Use retrieval_mode=plan|debug|review when the task intentionally needs a broader evidence surface. Do not fall back to shell grep unless retrieval is unavailable or raw Git semantics are explicitly needed. The low-level search_repository handler is compatibility/internal surface, not a second default search path. Prefer repository_command_execute for ordinary local Git and shell operations, repository_safe_patch_apply for bounded source changes, and rh_work only when the task genuinely requires durable orchestration.',
|
|
14
14
|
'Do not preflight unrelated Git history, prior Work, plugins, permissions, TCC, browser mode, maintenance, or recovery state. Inspect them only when the requested operation depends on them or a real failure points there.',
|
|
15
|
+
'Minimize mechanically redundant MCP round trips. For an ongoing Forge-controlled task, prefer one rh_status(summary) as the initial controller snapshot: it includes bounded readiness, repository identity, top active Work, active Plans, and pending handoffs. Do not immediately follow it with rh_work(plan_list) or rh_context(list/get) unless the next decision needs details that the snapshot does not contain. Use rh_context(work_id=..., detail_level=summary) for one Work, and request detail/raw or capability hydration only when that extra evidence can change the decision.',
|
|
15
16
|
'Run independent reads, searches, diagnostics, checks, repositories, checkouts, and tasks concurrently by default. Serialize only on a real dependency or Resource Claim conflict, and reuse existing projections, process handles, and check evidence instead of repeating successful observations.',
|
|
16
|
-
'Validation must be proportional to the change: cheap patch-safety checks belong to every coherent edit batch, focused tests run after that batch is stable, and expensive full-suite checks belong at candidate/release or explicitly high-risk boundaries. When a Direct Edit batch is stable, repository_safe_patch_apply may receive explicit check_ids to start revision-bound validation; omit check_ids during intermediate edits. Long checks return managed Processes: continue independent read/review work,
|
|
17
|
+
'Validation must be proportional to the change: cheap patch-safety checks belong to every coherent edit batch, focused tests run after that batch is stable, and expensive full-suite checks belong at candidate/release or explicitly high-risk boundaries. When a Direct Edit batch is stable, repository_safe_patch_apply may receive explicit check_ids to start revision-bound validation; omit check_ids during intermediate edits. Long checks return managed Processes: continue independent read/review work. Do not call process_wait while useful independent work remains. Use process_get only when a non-blocking observation can change the next decision; when the exact check result becomes a dependency, attach with process_wait (or validation_only with the returned edit-session/validation ids). An attach is transport-bounded: if it returns a running handle, resume useful work and attach again only at a later real dependency boundary, never as periodic polling. Do not perform maintenance or deep runtime diagnosis before ordinary work unless execution is actually blocked.',
|
|
17
18
|
'repository_safe_patch_apply returns bounded edit-session diff evidence for semantic review. Review that evidence directly instead of issuing a mandatory follow-up diff/status call; expand it only when truncated or semantically ambiguous. After a bounded successful change, finish the requested delivery directly. Avoid additional investigation whose result cannot change implementation or acceptance, and do not invoke hidden atomic compatibility tools merely because they exist in the full profile.',
|
|
18
19
|
].join(' ');
|
|
19
20
|
}
|
|
@@ -113,7 +113,6 @@ import type {
|
|
|
113
113
|
import {
|
|
114
114
|
gitDiff,
|
|
115
115
|
gitSnapshot,
|
|
116
|
-
readRepositoryRange,
|
|
117
116
|
searchRepository,
|
|
118
117
|
} from "../repository/inspector";
|
|
119
118
|
import {
|
|
@@ -1634,29 +1633,6 @@ export function buildMcpToolDefinitions(
|
|
|
1634
1633
|
},
|
|
1635
1634
|
annotations: readOnly,
|
|
1636
1635
|
},
|
|
1637
|
-
{
|
|
1638
|
-
name: "controller_context_pack",
|
|
1639
|
-
description:
|
|
1640
|
-
"Build a bounded code context pack for a task: relevant files, search-hit line ranges, and raw snippets. This is for scoped investigation, not blind implementation.",
|
|
1641
|
-
inputSchema: {
|
|
1642
|
-
type: "object",
|
|
1643
|
-
properties: {
|
|
1644
|
-
description: { type: "string" },
|
|
1645
|
-
issue_id: { type: "string" },
|
|
1646
|
-
task_id: { type: "string" },
|
|
1647
|
-
known_paths: { type: "array", items: { type: "string" } },
|
|
1648
|
-
include_globs: { type: "array", items: { type: "string" } },
|
|
1649
|
-
exclude_globs: { type: "array", items: { type: "string" } },
|
|
1650
|
-
search_terms: { type: "array", items: { type: "string" } },
|
|
1651
|
-
max_files: { type: "number" },
|
|
1652
|
-
max_snippets: { type: "number" },
|
|
1653
|
-
max_chars_per_snippet: { type: "number" },
|
|
1654
|
-
structural_context: { type: "string", enum: ["off", "auto", "required"], description: "Optional CodeGraph structural context. Defaults to off so ordinary Direct work pays no graph startup cost." },
|
|
1655
|
-
},
|
|
1656
|
-
additionalProperties: false,
|
|
1657
|
-
},
|
|
1658
|
-
annotations: readOnly,
|
|
1659
|
-
},
|
|
1660
1636
|
{
|
|
1661
1637
|
name: "quick_agent_session",
|
|
1662
1638
|
description:
|
|
@@ -1807,22 +1783,6 @@ export function buildMcpToolDefinitions(
|
|
|
1807
1783
|
},
|
|
1808
1784
|
annotations: readOnly,
|
|
1809
1785
|
},
|
|
1810
|
-
{
|
|
1811
|
-
name: "read_repository_file",
|
|
1812
|
-
description:
|
|
1813
|
-
"Read a line range from one policy-readable repository file.",
|
|
1814
|
-
inputSchema: {
|
|
1815
|
-
type: "object",
|
|
1816
|
-
properties: {
|
|
1817
|
-
path: { type: "string" },
|
|
1818
|
-
start_line: { type: "number" },
|
|
1819
|
-
end_line: { type: "number" },
|
|
1820
|
-
},
|
|
1821
|
-
required: ["path"],
|
|
1822
|
-
additionalProperties: false,
|
|
1823
|
-
},
|
|
1824
|
-
annotations: readOnly,
|
|
1825
|
-
},
|
|
1826
1786
|
{
|
|
1827
1787
|
name: "get_git_diff",
|
|
1828
1788
|
description:
|
|
@@ -1844,25 +1804,6 @@ export function buildMcpToolDefinitions(
|
|
|
1844
1804
|
inputSchema: EMPTY_SCHEMA,
|
|
1845
1805
|
annotations: readOnly,
|
|
1846
1806
|
},
|
|
1847
|
-
{
|
|
1848
|
-
name: "run_check",
|
|
1849
|
-
description:
|
|
1850
|
-
"Start one named focused repository check as a Local Job and return immediately; inspect it with get_local_job.",
|
|
1851
|
-
inputSchema: {
|
|
1852
|
-
type: "object",
|
|
1853
|
-
properties: {
|
|
1854
|
-
check_id: { type: "string" },
|
|
1855
|
-
timeout_ms: { type: "number" },
|
|
1856
|
-
},
|
|
1857
|
-
required: ["check_id"],
|
|
1858
|
-
additionalProperties: false,
|
|
1859
|
-
},
|
|
1860
|
-
annotations: {
|
|
1861
|
-
readOnlyHint: false,
|
|
1862
|
-
openWorldHint: false,
|
|
1863
|
-
destructiveHint: false,
|
|
1864
|
-
},
|
|
1865
|
-
},
|
|
1866
1807
|
{
|
|
1867
1808
|
name: "list_issues",
|
|
1868
1809
|
description: "List controller-managed issues and task summaries.",
|
|
@@ -3306,28 +3247,6 @@ export async function callMcpTool(
|
|
|
3306
3247
|
audit(ctx, name, "ok", args);
|
|
3307
3248
|
return textResult(payload);
|
|
3308
3249
|
}
|
|
3309
|
-
case "controller_context_pack": {
|
|
3310
|
-
if (ctx.policy.profile !== "controller")
|
|
3311
|
-
return errorResult(
|
|
3312
|
-
"TOOL_DISABLED",
|
|
3313
|
-
"controller_context_pack requires the controller profile",
|
|
3314
|
-
);
|
|
3315
|
-
const pack = buildControllerContextPack(ctx.repoRoot, ctx.policy, {
|
|
3316
|
-
description: typeof args.description === "string" ? args.description : undefined,
|
|
3317
|
-
issueId: typeof args.issue_id === "string" ? args.issue_id : undefined,
|
|
3318
|
-
taskId: typeof args.task_id === "string" ? args.task_id : undefined,
|
|
3319
|
-
knownPaths: stringList(args.known_paths),
|
|
3320
|
-
includeGlobs: stringList(args.include_globs),
|
|
3321
|
-
excludeGlobs: stringList(args.exclude_globs),
|
|
3322
|
-
searchTerms: stringList(args.search_terms),
|
|
3323
|
-
maxFiles: typeof args.max_files === "number" ? args.max_files : undefined,
|
|
3324
|
-
maxSnippets: typeof args.max_snippets === "number" ? args.max_snippets : undefined,
|
|
3325
|
-
maxCharsPerSnippet: typeof args.max_chars_per_snippet === "number" ? args.max_chars_per_snippet : undefined,
|
|
3326
|
-
structuralContext: args.structural_context === "auto" || args.structural_context === "required" ? args.structural_context : "off",
|
|
3327
|
-
});
|
|
3328
|
-
audit(ctx, name, "ok", args);
|
|
3329
|
-
return textResult(pack);
|
|
3330
|
-
}
|
|
3331
3250
|
case "controller_context": {
|
|
3332
3251
|
if (ctx.policy.profile !== "controller")
|
|
3333
3252
|
return errorResult(
|
|
@@ -3563,36 +3482,6 @@ export async function callMcpTool(
|
|
|
3563
3482
|
audit(ctx, name, "ok", args);
|
|
3564
3483
|
return textResult(result);
|
|
3565
3484
|
}
|
|
3566
|
-
case "read_repository_file": {
|
|
3567
|
-
if (ctx.policy.profile !== "controller")
|
|
3568
|
-
return errorResult(
|
|
3569
|
-
"TOOL_DISABLED",
|
|
3570
|
-
"read_repository_file requires the controller profile",
|
|
3571
|
-
);
|
|
3572
|
-
const path = String(args.path ?? "");
|
|
3573
|
-
const session = ctx.sessionId && ctx.repoId
|
|
3574
|
-
? {
|
|
3575
|
-
sessionId: ctx.sessionId,
|
|
3576
|
-
repoId: ctx.repoId,
|
|
3577
|
-
checkoutId: ctx.checkoutId ?? ctx.repoId,
|
|
3578
|
-
}
|
|
3579
|
-
: undefined;
|
|
3580
|
-
const result = readRepositoryRange(
|
|
3581
|
-
ctx.repoRoot,
|
|
3582
|
-
ctx.policy,
|
|
3583
|
-
path,
|
|
3584
|
-
typeof args.start_line === "number" ? args.start_line : 1,
|
|
3585
|
-
typeof args.end_line === "number" ? args.end_line : 200,
|
|
3586
|
-
session,
|
|
3587
|
-
);
|
|
3588
|
-
const redacted = redactMcpText(result.content);
|
|
3589
|
-
audit(ctx, name, "ok", args, result.path);
|
|
3590
|
-
return textResult({
|
|
3591
|
-
...result,
|
|
3592
|
-
content: redacted.text,
|
|
3593
|
-
redactions: redacted.redactions,
|
|
3594
|
-
});
|
|
3595
|
-
}
|
|
3596
3485
|
case "get_git_diff": {
|
|
3597
3486
|
if (ctx.policy.profile !== "controller")
|
|
3598
3487
|
return errorResult(
|
|
@@ -3640,84 +3529,6 @@ export async function callMcpTool(
|
|
|
3640
3529
|
audit(ctx, name, "ok", args);
|
|
3641
3530
|
return textResult({ checks });
|
|
3642
3531
|
}
|
|
3643
|
-
case "run_check": {
|
|
3644
|
-
if (ctx.policy.profile !== "controller")
|
|
3645
|
-
return errorResult(
|
|
3646
|
-
"TOOL_DISABLED",
|
|
3647
|
-
"run_check requires the controller profile",
|
|
3648
|
-
);
|
|
3649
|
-
// Prefer Unified Process Runtime for ordinary checks. Multi-phase and
|
|
3650
|
-
// release checks require an explicit external Controller instead.
|
|
3651
|
-
const checkId = String(args.check_id ?? "").trim();
|
|
3652
|
-
const forceDurable = args.force_durable === true
|
|
3653
|
-
|| args.apply_mode === "async"
|
|
3654
|
-
|| args.mode === "durable";
|
|
3655
|
-
if (!forceDurable && checkId) {
|
|
3656
|
-
try {
|
|
3657
|
-
const { checkRequiresDurableWorkflow, runCheckViaProcessRuntime } = await import(
|
|
3658
|
-
"../../runtime/execution/process-runtime"
|
|
3659
|
-
);
|
|
3660
|
-
const { resolveRepoPreferredControllerHome } = await import("../repositories/controller-home");
|
|
3661
|
-
const { resolveRepositorySelection } = await import("../repositories/registry");
|
|
3662
|
-
if (!checkRequiresDurableWorkflow(checkId)) {
|
|
3663
|
-
const controllerHome = resolveRepoPreferredControllerHome(ctx.repoRoot);
|
|
3664
|
-
const repository = resolveRepositorySelection({
|
|
3665
|
-
explicitPath: ctx.repoRoot,
|
|
3666
|
-
controllerHome,
|
|
3667
|
-
allowSoleRepository: true,
|
|
3668
|
-
});
|
|
3669
|
-
if (repository) {
|
|
3670
|
-
const facade = await runCheckViaProcessRuntime({
|
|
3671
|
-
controllerHome,
|
|
3672
|
-
repoId: repository.repoId,
|
|
3673
|
-
checkoutId: repository.activeCheckoutId,
|
|
3674
|
-
repoRoot: repository.canonicalRoot,
|
|
3675
|
-
checkId,
|
|
3676
|
-
timeoutMs: typeof args.timeout_ms === "number" ? args.timeout_ms : undefined,
|
|
3677
|
-
requestId: typeof args.request_id === "string" ? args.request_id.trim() || undefined : undefined,
|
|
3678
|
-
executionIdentity: {
|
|
3679
|
-
schemaVersion: 1,
|
|
3680
|
-
repositoryId: repository.repoId,
|
|
3681
|
-
checkoutId: repository.activeCheckoutId,
|
|
3682
|
-
canonicalRoot: repository.canonicalRoot,
|
|
3683
|
-
},
|
|
3684
|
-
});
|
|
3685
|
-
if (facade.mode !== "durable" && facade.process) {
|
|
3686
|
-
audit(ctx, name, "ok", args);
|
|
3687
|
-
return textResult({
|
|
3688
|
-
mode: facade.mode,
|
|
3689
|
-
path: facade.mode === "direct" ? "process_direct" : "process_managed",
|
|
3690
|
-
checkId: facade.checkId,
|
|
3691
|
-
processId: facade.process.processId,
|
|
3692
|
-
status: facade.process.status,
|
|
3693
|
-
completed: facade.process.completed === true,
|
|
3694
|
-
ok: facade.process.ok,
|
|
3695
|
-
exitCode: facade.process.exitCode,
|
|
3696
|
-
timedOut: facade.process.timedOut,
|
|
3697
|
-
stdout: facade.process.stdout,
|
|
3698
|
-
stderr: facade.process.stderr,
|
|
3699
|
-
durableSideEffects: facade.durableSideEffects,
|
|
3700
|
-
next: facade.process.completed
|
|
3701
|
-
? "Check finished on Process Runtime without LocalBridgeJob / ExecutionJob / Worker."
|
|
3702
|
-
: `Managed process ${facade.process.processId} still running; poll process_get / process_wait / process_logs.`,
|
|
3703
|
-
});
|
|
3704
|
-
}
|
|
3705
|
-
}
|
|
3706
|
-
}
|
|
3707
|
-
} catch (error) {
|
|
3708
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
3709
|
-
const code = message.includes(":") ? message.slice(0, message.indexOf(":")) : "PROCESS_RUNTIME_FAILED";
|
|
3710
|
-
audit(ctx, name, "failed", args, undefined, message);
|
|
3711
|
-
return errorResult(code, message, { checkId });
|
|
3712
|
-
}
|
|
3713
|
-
}
|
|
3714
|
-
audit(ctx, name, "blocked", args);
|
|
3715
|
-
return errorResult(
|
|
3716
|
-
"PROCESS_RUNTIME_REQUIRED",
|
|
3717
|
-
"This check cannot create a Local Bridge Job. Run a registered ordinary check through Process Runtime or use external Controller handling for durable checks.",
|
|
3718
|
-
{ checkId },
|
|
3719
|
-
);
|
|
3720
|
-
}
|
|
3721
3532
|
case "list_issues": {
|
|
3722
3533
|
if (ctx.policy.profile !== "controller")
|
|
3723
3534
|
return errorResult(
|
|
@@ -87,8 +87,8 @@ export function parseMcpToolset(value: unknown, profile: string): McpToolset {
|
|
|
87
87
|
if (profile !== 'controller') return 'full';
|
|
88
88
|
const normalized = typeof value === 'string' ? value.trim().toLowerCase() : '';
|
|
89
89
|
if (!normalized) return 'advanced';
|
|
90
|
-
if (normalized === 'core' || normalized === 'advanced' || normalized === 'full') return normalized;
|
|
91
|
-
throw new Error(`invalid MCP toolset "${String(value)}" (expected: core, advanced, or full)`);
|
|
90
|
+
if (normalized === 'facade' || normalized === 'core' || normalized === 'advanced' || normalized === 'full') return normalized;
|
|
91
|
+
throw new Error(`invalid MCP toolset "${String(value)}" (expected: facade, core, advanced, or full)`);
|
|
92
92
|
}
|
|
93
93
|
|
|
94
94
|
function repositoryForExplicitPath(path: string, controllerHome: string): RepositoryRecord | undefined {
|
|
@@ -295,13 +295,24 @@ export function repositoryScopedToolArgs(
|
|
|
295
295
|
input: Record<string, unknown>,
|
|
296
296
|
repository: RepositoryRecord,
|
|
297
297
|
): Record<string, unknown> {
|
|
298
|
-
// Preserve resolved repository
|
|
299
|
-
//
|
|
298
|
+
// Preserve resolved repository identity for every downstream call. Exact
|
|
299
|
+
// claimed Work calls are the one exception for checkout injection: when the
|
|
300
|
+
// caller omitted checkout_id, repository tools must resolve the Work-owned
|
|
301
|
+
// checkout after validating the authenticated principal instead of inheriting
|
|
302
|
+
// the repository's currently active checkout.
|
|
303
|
+
const workOwnedCheckoutResolution = [
|
|
304
|
+
'repository_command_preview',
|
|
305
|
+
'repository_command_execute',
|
|
306
|
+
'repository_safe_patch_apply',
|
|
307
|
+
].includes(name)
|
|
308
|
+
&& typeof input.work_id === 'string'
|
|
309
|
+
&& input.work_id.trim().length > 0
|
|
310
|
+
&& !(typeof input.checkout_id === 'string' && input.checkout_id.trim().length > 0);
|
|
300
311
|
const args: Record<string, unknown> = {
|
|
301
312
|
...input,
|
|
302
313
|
repo_id: repository.repoId,
|
|
303
|
-
checkout_id: repository.activeCheckoutId,
|
|
304
314
|
};
|
|
315
|
+
if (!workOwnedCheckoutResolution) args.checkout_id = repository.activeCheckoutId;
|
|
305
316
|
|
|
306
317
|
const github = repository.github;
|
|
307
318
|
const githubRepo = github ? `${github.owner}/${github.repo}` : undefined;
|
package/src/cli/mcp/oauth.ts
CHANGED
|
@@ -1,5 +1,16 @@
|
|
|
1
1
|
import { randomUUID } from 'crypto';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
chmodSync,
|
|
4
|
+
closeSync,
|
|
5
|
+
existsSync,
|
|
6
|
+
fsyncSync,
|
|
7
|
+
mkdirSync,
|
|
8
|
+
openSync,
|
|
9
|
+
readFileSync,
|
|
10
|
+
renameSync,
|
|
11
|
+
rmSync,
|
|
12
|
+
writeFileSync,
|
|
13
|
+
} from 'fs';
|
|
3
14
|
import { dirname } from 'path';
|
|
4
15
|
import type { OAuthRegisteredClientsStore } from '@modelcontextprotocol/sdk/server/auth/clients.js';
|
|
5
16
|
import { InvalidGrantError, InvalidTokenError } from '@modelcontextprotocol/sdk/server/auth/errors.js';
|
|
@@ -25,6 +36,20 @@ function issueToken(): string {
|
|
|
25
36
|
return randomUUID();
|
|
26
37
|
}
|
|
27
38
|
|
|
39
|
+
function fsyncFile(path: string): void {
|
|
40
|
+
const fd = openSync(path, 'r');
|
|
41
|
+
try { fsyncSync(fd); } finally { closeSync(fd); }
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
function fsyncDirectory(path: string): void {
|
|
45
|
+
try {
|
|
46
|
+
const fd = openSync(path, 'r');
|
|
47
|
+
try { fsyncSync(fd); } finally { closeSync(fd); }
|
|
48
|
+
} catch {
|
|
49
|
+
// Directory fsync is unavailable on some supported platforms/filesystems.
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
|
|
28
53
|
export class McpOAuthTokenStore implements OAuthRegisteredClientsStore {
|
|
29
54
|
private accessTokens = new Map<string, AuthInfo>();
|
|
30
55
|
private refreshTokens = new Map<string, string>();
|
|
@@ -33,8 +58,12 @@ export class McpOAuthTokenStore implements OAuthRegisteredClientsStore {
|
|
|
33
58
|
constructor(private readonly path: string, private readonly fallbackPaths: string[] = []) {}
|
|
34
59
|
|
|
35
60
|
load(): void {
|
|
61
|
+
this.accessTokens.clear();
|
|
62
|
+
this.refreshTokens.clear();
|
|
63
|
+
this.clients.clear();
|
|
36
64
|
let loaded = false;
|
|
37
65
|
let mergedFallback = false;
|
|
66
|
+
let primaryCorrupt = false;
|
|
38
67
|
for (const [index, path] of [this.path, ...this.fallbackPaths].entries()) {
|
|
39
68
|
if (!existsSync(path)) continue;
|
|
40
69
|
try {
|
|
@@ -59,22 +88,35 @@ export class McpOAuthTokenStore implements OAuthRegisteredClientsStore {
|
|
|
59
88
|
}
|
|
60
89
|
loaded = true;
|
|
61
90
|
} catch (_error) {
|
|
62
|
-
|
|
91
|
+
if (index === 0) primaryCorrupt = true;
|
|
63
92
|
}
|
|
64
93
|
}
|
|
65
|
-
if (
|
|
94
|
+
if (primaryCorrupt && !loaded) {
|
|
95
|
+
throw new Error('MCP_OAUTH_STORE_CORRUPT: primary OAuth state is unreadable and no valid fallback is available');
|
|
96
|
+
}
|
|
97
|
+
if (loaded && (mergedFallback || primaryCorrupt)) this.flush();
|
|
66
98
|
}
|
|
67
99
|
|
|
68
100
|
flush(): void {
|
|
69
|
-
|
|
70
|
-
|
|
101
|
+
const directory = dirname(this.path);
|
|
102
|
+
mkdirSync(directory, { recursive: true, mode: 0o700 });
|
|
103
|
+
chmodSync(directory, 0o700);
|
|
71
104
|
const data: TokenData = {
|
|
72
105
|
accessTokens: Object.fromEntries(this.accessTokens),
|
|
73
106
|
refreshTokens: Object.fromEntries(this.refreshTokens),
|
|
74
107
|
clients: Object.fromEntries(this.clients),
|
|
75
108
|
};
|
|
76
|
-
|
|
77
|
-
|
|
109
|
+
const temporary = `${this.path}.${process.pid}.${randomUUID()}.tmp`;
|
|
110
|
+
try {
|
|
111
|
+
writeFileSync(temporary, `${JSON.stringify(data, null, 2)}\n`, { encoding: 'utf-8', mode: 0o600 });
|
|
112
|
+
chmodSync(temporary, 0o600);
|
|
113
|
+
fsyncFile(temporary);
|
|
114
|
+
renameSync(temporary, this.path);
|
|
115
|
+
chmodSync(this.path, 0o600);
|
|
116
|
+
fsyncDirectory(directory);
|
|
117
|
+
} finally {
|
|
118
|
+
rmSync(temporary, { force: true });
|
|
119
|
+
}
|
|
78
120
|
}
|
|
79
121
|
|
|
80
122
|
getClient(clientId: string): OAuthClientInformationFull | undefined {
|
|
@@ -127,10 +169,115 @@ export class McpOAuthTokenStore implements OAuthRegisteredClientsStore {
|
|
|
127
169
|
}
|
|
128
170
|
return undefined;
|
|
129
171
|
}
|
|
172
|
+
|
|
173
|
+
setTokenPair(accessToken: string, info: AuthInfo, refreshToken: string): void {
|
|
174
|
+
this.accessTokens.set(accessToken, info);
|
|
175
|
+
this.refreshTokens.set(refreshToken, accessToken);
|
|
176
|
+
this.flush();
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
rotateRefreshToken(
|
|
180
|
+
previousRefreshToken: string,
|
|
181
|
+
nextRefreshToken: string,
|
|
182
|
+
accessToken: string,
|
|
183
|
+
info: AuthInfo,
|
|
184
|
+
): void {
|
|
185
|
+
this.refreshTokens.delete(previousRefreshToken);
|
|
186
|
+
this.accessTokens.set(accessToken, info);
|
|
187
|
+
this.refreshTokens.set(nextRefreshToken, accessToken);
|
|
188
|
+
this.flush();
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
revokeToken(token: string): void {
|
|
192
|
+
const linkedAccessToken = this.refreshTokens.get(token);
|
|
193
|
+
if (linkedAccessToken) {
|
|
194
|
+
this.refreshTokens.delete(token);
|
|
195
|
+
this.accessTokens.delete(linkedAccessToken);
|
|
196
|
+
for (const [refreshToken, accessToken] of this.refreshTokens) {
|
|
197
|
+
if (accessToken === linkedAccessToken) this.refreshTokens.delete(refreshToken);
|
|
198
|
+
}
|
|
199
|
+
this.flush();
|
|
200
|
+
return;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
this.accessTokens.delete(token);
|
|
204
|
+
for (const [refreshToken, accessToken] of this.refreshTokens) {
|
|
205
|
+
if (accessToken === token) this.refreshTokens.delete(refreshToken);
|
|
206
|
+
}
|
|
207
|
+
this.flush();
|
|
208
|
+
}
|
|
130
209
|
}
|
|
131
210
|
|
|
132
|
-
|
|
133
|
-
|
|
211
|
+
interface PendingAuthorizationCode {
|
|
212
|
+
challenge: string;
|
|
213
|
+
clientId: string;
|
|
214
|
+
issuedAt: number;
|
|
215
|
+
expiresAt: number;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
export interface McpOAuthProviderOptions {
|
|
219
|
+
now?: () => number;
|
|
220
|
+
authorizationCodeTtlSeconds?: number;
|
|
221
|
+
maxPendingAuthorizationCodes?: number;
|
|
222
|
+
maxPendingAuthorizationCodesPerClient?: number;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const DEFAULT_AUTHORIZATION_CODE_TTL_SECONDS = 10 * 60;
|
|
226
|
+
const DEFAULT_MAX_PENDING_AUTHORIZATION_CODES = 512;
|
|
227
|
+
const DEFAULT_MAX_PENDING_AUTHORIZATION_CODES_PER_CLIENT = 32;
|
|
228
|
+
|
|
229
|
+
export function createMcpOAuthProvider(
|
|
230
|
+
store: McpOAuthTokenStore,
|
|
231
|
+
options: McpOAuthProviderOptions = {},
|
|
232
|
+
): OAuthServerProvider {
|
|
233
|
+
const authCodes = new Map<string, PendingAuthorizationCode>();
|
|
234
|
+
const currentTime = options.now ?? nowSeconds;
|
|
235
|
+
const ttlSeconds = Math.max(30, Math.trunc(options.authorizationCodeTtlSeconds ?? DEFAULT_AUTHORIZATION_CODE_TTL_SECONDS));
|
|
236
|
+
const globalLimit = Math.max(8, Math.trunc(options.maxPendingAuthorizationCodes ?? DEFAULT_MAX_PENDING_AUTHORIZATION_CODES));
|
|
237
|
+
const clientLimit = Math.max(2, Math.min(globalLimit, Math.trunc(
|
|
238
|
+
options.maxPendingAuthorizationCodesPerClient ?? DEFAULT_MAX_PENDING_AUTHORIZATION_CODES_PER_CLIENT,
|
|
239
|
+
)));
|
|
240
|
+
|
|
241
|
+
const purgeExpiredCodes = (now = currentTime()): void => {
|
|
242
|
+
for (const [code, pending] of authCodes) {
|
|
243
|
+
if (pending.expiresAt <= now) authCodes.delete(code);
|
|
244
|
+
}
|
|
245
|
+
};
|
|
246
|
+
|
|
247
|
+
const evictOldest = (predicate: (entry: PendingAuthorizationCode) => boolean): boolean => {
|
|
248
|
+
let oldest: { code: string; issuedAt: number } | undefined;
|
|
249
|
+
for (const [code, pending] of authCodes) {
|
|
250
|
+
if (!predicate(pending)) continue;
|
|
251
|
+
if (!oldest || pending.issuedAt < oldest.issuedAt) oldest = { code, issuedAt: pending.issuedAt };
|
|
252
|
+
}
|
|
253
|
+
if (!oldest) return false;
|
|
254
|
+
authCodes.delete(oldest.code);
|
|
255
|
+
return true;
|
|
256
|
+
};
|
|
257
|
+
|
|
258
|
+
const reserveAuthorizationCodeCapacity = (clientId: string, now: number): void => {
|
|
259
|
+
purgeExpiredCodes(now);
|
|
260
|
+
while ([...authCodes.values()].filter((pending) => pending.clientId === clientId).length >= clientLimit) {
|
|
261
|
+
if (!evictOldest((pending) => pending.clientId === clientId)) break;
|
|
262
|
+
}
|
|
263
|
+
while (authCodes.size >= globalLimit) {
|
|
264
|
+
if (!evictOldest(() => true)) break;
|
|
265
|
+
}
|
|
266
|
+
};
|
|
267
|
+
|
|
268
|
+
const pendingAuthorizationCode = (
|
|
269
|
+
client: OAuthClientInformationFull,
|
|
270
|
+
authorizationCode: string,
|
|
271
|
+
): PendingAuthorizationCode => {
|
|
272
|
+
const now = currentTime();
|
|
273
|
+
purgeExpiredCodes(now);
|
|
274
|
+
const stored = authCodes.get(authorizationCode);
|
|
275
|
+
if (!stored || stored.clientId !== client.client_id || stored.expiresAt <= now) {
|
|
276
|
+
if (stored?.expiresAt !== undefined && stored.expiresAt <= now) authCodes.delete(authorizationCode);
|
|
277
|
+
throw new InvalidGrantError('Invalid authorization code');
|
|
278
|
+
}
|
|
279
|
+
return stored;
|
|
280
|
+
};
|
|
134
281
|
|
|
135
282
|
return {
|
|
136
283
|
get clientsStore(): OAuthRegisteredClientsStore {
|
|
@@ -138,10 +285,14 @@ export function createMcpOAuthProvider(store: McpOAuthTokenStore): OAuthServerPr
|
|
|
138
285
|
},
|
|
139
286
|
|
|
140
287
|
async authorize(client: OAuthClientInformationFull, params: AuthorizationParams, res): Promise<void> {
|
|
288
|
+
const now = currentTime();
|
|
289
|
+
reserveAuthorizationCodeCapacity(client.client_id, now);
|
|
141
290
|
const code = issueToken();
|
|
142
291
|
authCodes.set(code, {
|
|
143
292
|
challenge: params.codeChallenge,
|
|
144
293
|
clientId: client.client_id,
|
|
294
|
+
issuedAt: now,
|
|
295
|
+
expiresAt: now + ttlSeconds,
|
|
145
296
|
});
|
|
146
297
|
const redirectUrl = new URL(params.redirectUri);
|
|
147
298
|
redirectUrl.searchParams.set('code', code);
|
|
@@ -149,10 +300,8 @@ export function createMcpOAuthProvider(store: McpOAuthTokenStore): OAuthServerPr
|
|
|
149
300
|
res.redirect(302, redirectUrl.toString());
|
|
150
301
|
},
|
|
151
302
|
|
|
152
|
-
async challengeForAuthorizationCode(
|
|
153
|
-
|
|
154
|
-
if (!stored) throw new InvalidGrantError('Invalid authorization code');
|
|
155
|
-
return stored.challenge;
|
|
303
|
+
async challengeForAuthorizationCode(client: OAuthClientInformationFull, authorizationCode: string): Promise<string> {
|
|
304
|
+
return pendingAuthorizationCode(client, authorizationCode).challenge;
|
|
156
305
|
},
|
|
157
306
|
|
|
158
307
|
async exchangeAuthorizationCode(
|
|
@@ -160,22 +309,21 @@ export function createMcpOAuthProvider(store: McpOAuthTokenStore): OAuthServerPr
|
|
|
160
309
|
authorizationCode: string,
|
|
161
310
|
_codeVerifier?: string,
|
|
162
311
|
): Promise<OAuthTokens> {
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
312
|
+
pendingAuthorizationCode(client, authorizationCode);
|
|
313
|
+
// Delete before issuing credentials. A failed downstream persistence is
|
|
314
|
+
// retried as a new OAuth flow rather than making an authorization code
|
|
315
|
+
// replayable after a partial token response.
|
|
167
316
|
authCodes.delete(authorizationCode);
|
|
168
317
|
const accessToken = issueToken();
|
|
169
318
|
const refreshToken = issueToken();
|
|
170
319
|
const expiresIn = 30 * 24 * 60 * 60;
|
|
171
320
|
const expiresAt = nowSeconds() + expiresIn;
|
|
172
|
-
store.
|
|
321
|
+
store.setTokenPair(accessToken, {
|
|
173
322
|
token: accessToken,
|
|
174
323
|
clientId: client.client_id,
|
|
175
324
|
scopes: ['forge'],
|
|
176
325
|
expiresAt,
|
|
177
|
-
});
|
|
178
|
-
store.setRefreshToken(refreshToken, accessToken);
|
|
326
|
+
}, refreshToken);
|
|
179
327
|
return {
|
|
180
328
|
access_token: accessToken,
|
|
181
329
|
token_type: 'Bearer',
|
|
@@ -191,11 +339,14 @@ export function createMcpOAuthProvider(store: McpOAuthTokenStore): OAuthServerPr
|
|
|
191
339
|
if (!accessToken || !existing || existing.clientId !== client.client_id) {
|
|
192
340
|
throw new InvalidGrantError('Invalid refresh token');
|
|
193
341
|
}
|
|
194
|
-
store.deleteRefreshToken(refreshToken);
|
|
195
342
|
const nextRefreshToken = issueToken();
|
|
196
343
|
const expiresIn = 30 * 24 * 60 * 60;
|
|
197
|
-
store.
|
|
198
|
-
|
|
344
|
+
store.rotateRefreshToken(
|
|
345
|
+
refreshToken,
|
|
346
|
+
nextRefreshToken,
|
|
347
|
+
accessToken,
|
|
348
|
+
{ ...existing, expiresAt: nowSeconds() + expiresIn },
|
|
349
|
+
);
|
|
199
350
|
return {
|
|
200
351
|
access_token: accessToken,
|
|
201
352
|
token_type: 'Bearer',
|
|
@@ -218,9 +369,7 @@ export function createMcpOAuthProvider(store: McpOAuthTokenStore): OAuthServerPr
|
|
|
218
369
|
},
|
|
219
370
|
|
|
220
371
|
async revokeToken(_client: OAuthClientInformationFull, request: OAuthTokenRevocationRequest): Promise<void> {
|
|
221
|
-
store.
|
|
222
|
-
const refreshToken = store.findRefreshTokenByAccessToken(request.token);
|
|
223
|
-
if (refreshToken) store.deleteRefreshToken(refreshToken);
|
|
372
|
+
store.revokeToken(request.token);
|
|
224
373
|
},
|
|
225
374
|
};
|
|
226
375
|
}
|
package/src/cli/mcp/paths.ts
CHANGED
|
@@ -93,7 +93,13 @@ function nearestExistingPath(path: string): string | undefined {
|
|
|
93
93
|
return current;
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
export function resolveMcpPath(
|
|
96
|
+
export function resolveMcpPath(
|
|
97
|
+
repoRoot: string,
|
|
98
|
+
inputPath: string,
|
|
99
|
+
policy: McpPolicy,
|
|
100
|
+
intent: McpPathIntent,
|
|
101
|
+
canonicalRepoRoot?: string,
|
|
102
|
+
): McpPathDecision {
|
|
97
103
|
const normalized = normalizeMcpRelativePath(inputPath);
|
|
98
104
|
if (!normalized.ok || !normalized.relativePath) return normalized;
|
|
99
105
|
|
|
@@ -107,7 +113,7 @@ export function resolveMcpPath(repoRoot: string, inputPath: string, policy: McpP
|
|
|
107
113
|
return { ok: false, relativePath, reason: `path is not allowed for ${intent}: ${relativePath}` };
|
|
108
114
|
}
|
|
109
115
|
|
|
110
|
-
const repoRealpath = realpathSync(repoRoot);
|
|
116
|
+
const repoRealpath = canonicalRepoRoot ?? realpathSync(repoRoot);
|
|
111
117
|
const absolutePath = resolve(repoRealpath, relativePath);
|
|
112
118
|
if (intent === 'read' && !existsSync(absolutePath)) {
|
|
113
119
|
return { ok: false, relativePath, reason: `path does not exist: ${relativePath}` };
|