@moretea-labs/forge 1.7.2 → 1.8.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 +58 -62
- package/CHANGELOG.md +26 -0
- package/CLAUDE.md +42 -62
- package/README.md +2 -2
- package/SKILL.md +0 -1
- package/THIRD_PARTY_NOTICES.md +5 -2
- package/adapters/browser/index.ts +2 -0
- package/adapters/chatgpt/browser-delivery-host.ts +71 -0
- package/adapters/chatgpt/browser-delivery-runtime.ts +783 -0
- package/adapters/chatgpt/controller-binding-store.ts +55 -0
- package/adapters/chatgpt/controller-host.ts +119 -0
- package/adapters/chatgpt/controller-round-host.ts +78 -0
- package/adapters/chatgpt/controller-round-settlement-store.ts +45 -0
- package/adapters/chatgpt/provider-delivery.ts +113 -0
- package/adapters/chatgpt/work-conversation-binding-store.ts +192 -0
- package/adapters/chatgpt/wsl-bridge-delivery-host.ts +63 -0
- package/adapters/computer/desktop-operator-contract.ts +7 -0
- package/adapters/computer/desktop-operator-discovery.ts +201 -0
- package/adapters/computer/desktop-operator-negotiation.ts +174 -0
- package/adapters/computer/desktop-operator-provider.ts +312 -0
- package/adapters/computer/index.ts +1 -0
- package/adapters/computer/interaction-target-authority.ts +944 -0
- package/adapters/controller-process/binding-store.ts +34 -0
- package/adapters/controller-process/controller-host.ts +26 -0
- package/adapters/mcp/access-mode.ts +132 -0
- package/adapters/mcp/audit.ts +32 -0
- package/adapters/mcp/auth.ts +407 -0
- package/adapters/mcp/controller-round-compatibility.ts +184 -0
- package/adapters/mcp/frozen-client-semantic-compatibility.ts +292 -0
- package/adapters/mcp/instructions.ts +36 -0
- package/adapters/mcp/legacy-context.ts +18 -0
- package/adapters/mcp/multi-repository.ts +437 -0
- package/adapters/mcp/oauth.ts +391 -0
- package/adapters/mcp/paths.ts +140 -0
- package/adapters/mcp/policy.ts +232 -0
- package/adapters/mcp/proxy-env.ts +62 -0
- package/adapters/mcp/readiness-projection.ts +24 -0
- package/adapters/mcp/redaction.ts +21 -0
- package/adapters/mcp/repo.ts +24 -0
- package/adapters/mcp/runtime-gateway/artifact-adapter.ts +52 -0
- package/adapters/mcp/runtime-gateway/context-adapter.ts +829 -0
- package/adapters/mcp/runtime-gateway/controller-authority-adapter.ts +191 -0
- package/adapters/mcp/runtime-gateway/engineering-preconditions.ts +147 -0
- package/adapters/mcp/runtime-gateway/execution-control-adapter.ts +28 -0
- package/adapters/mcp/runtime-gateway/execution-tools.ts +207 -0
- package/adapters/mcp/runtime-gateway/filesystem-adapter.ts +38 -0
- package/adapters/mcp/runtime-gateway/gateway-execution-adapter.ts +411 -0
- package/adapters/mcp/runtime-gateway/legacy-ios-tool-adapter.ts +105 -0
- package/adapters/mcp/runtime-gateway/maintenance-adapter.ts +44 -0
- package/adapters/mcp/runtime-gateway/model-adapter.ts +63 -0
- package/adapters/mcp/runtime-gateway/persisted-check-process.ts +3 -0
- package/adapters/mcp/runtime-gateway/plugin-adapter.ts +308 -0
- package/adapters/mcp/runtime-gateway/process-tools.ts +289 -0
- package/adapters/mcp/runtime-gateway/protected-computer-adapter.ts +76 -0
- package/adapters/mcp/runtime-gateway/recovery-adapter.ts +268 -0
- package/adapters/mcp/runtime-gateway/recovery-client-adapter.ts +70 -0
- package/adapters/mcp/runtime-gateway/recovery-tool-contract.ts +99 -0
- package/adapters/mcp/runtime-gateway/repository-compat-adapter.ts +110 -0
- package/adapters/mcp/runtime-gateway/result-adapter.ts +108 -0
- package/adapters/mcp/runtime-gateway/router.ts +728 -0
- package/adapters/mcp/runtime-gateway/runtime-observation-adapter.ts +1251 -0
- package/adapters/mcp/runtime-gateway/runtime-tool-definitions.ts +634 -0
- package/adapters/mcp/runtime-gateway/runtime-tool-shared.ts +27 -0
- package/adapters/mcp/runtime-gateway/runtime-tools.ts +68 -0
- package/adapters/mcp/runtime-gateway/scheduler-adapter.ts +186 -0
- package/adapters/mcp/runtime-gateway/shared-adapter.ts +25 -0
- package/adapters/mcp/runtime-gateway/status-inbox-adapter.ts +870 -0
- package/adapters/mcp/runtime-gateway/work-adapter.ts +3186 -0
- package/adapters/mcp/runtime-gateway/work-compat-adapter.ts +502 -0
- package/adapters/mcp/runtime-gateway/work-validation-reconciler.ts +1 -0
- package/adapters/mcp/server.ts +1048 -0
- package/adapters/mcp/setup.ts +791 -0
- package/adapters/mcp/tool-capability-cache.ts +87 -0
- package/adapters/mcp/tool-mapping/access-tools.ts +387 -0
- package/adapters/mcp/tool-mapping/legacy-tool-service.ts +5265 -0
- package/adapters/mcp/tool-mapping/post-finalize-work-attribution.ts +150 -0
- package/adapters/mcp/tool-mapping/repository-tools.ts +1691 -0
- package/adapters/mcp/tool-mapping/tools.ts +9 -0
- package/adapters/mcp/tool-mapping/umbrella-tools.ts +117 -0
- package/adapters/mcp/toolset-names.ts +74 -0
- package/adapters/mcp/toolset-selection.ts +41 -0
- package/adapters/mcp/toolset.ts +336 -0
- package/adapters/mcp/transports/http.ts +1456 -0
- package/adapters/mcp/transports/session-registry.ts +513 -0
- package/adapters/mcp/transports/stdio.ts +8 -0
- package/adapters/mcp/tunnels/openai-secure-tunnel.ts +139 -0
- package/adapters/mcp/types.ts +41 -0
- package/assets/AGENTS.md +2 -16
- package/assets/CLAUDE.md +2 -16
- package/assets/hooks/AGENTS.md +3 -14
- package/assets/hooks/CLAUDE.md +3 -14
- package/assets/hooks/hook-input.sh +9 -2
- package/assets/hooks/lib/workflow-state.sh +28 -10
- package/assets/hooks/prompt-guard.sh +63 -37
- package/assets/hooks/security-sentinel.sh +2 -1
- package/assets/hooks/session-start-context.sh +7 -5
- package/assets/hooks/stop-orchestrator.sh +13 -3
- package/assets/initializer-question-pack.v4.json +11 -7
- package/assets/partials/05-workflow.partial.md +18 -73
- package/assets/partials/07-footer.partial.md +1 -1
- package/assets/partials-agents/02-operating-mode.partial.md +1 -2
- package/assets/partials-agents/04-task-protocol.partial.md +14 -59
- package/assets/partials-agents/06-quality-safety.partial.md +11 -16
- package/assets/partials-agents/08-deep-docs.partial.md +1 -2
- package/assets/plan-map.json +11 -11
- package/assets/plugin-registry.v1.json +9 -3
- package/assets/plugin-trust-policy.v1.json +6 -0
- package/assets/recovery/ForgeRecovery.ps1 +100 -0
- package/assets/recovery/forge-wsl-rescue +271 -0
- package/assets/reference-configs/development-protocol.md +11 -15
- package/assets/reference-configs/document-generation.md +1 -1
- package/assets/reference-configs/global-working-rules.md +9 -1
- package/assets/reference-configs/harness-overview.md +22 -112
- package/assets/reference-configs/hook-operations.md +1 -1
- package/assets/reference-configs/sprint-contracts.md +2 -2
- package/assets/skill-version.json +2 -2
- package/assets/templates/helpers/architecture-event.ts +3 -34
- package/assets/templates/helpers/architecture-queue.sh +61 -10
- package/assets/templates/helpers/archive-workflow.sh +90 -180
- package/assets/templates/helpers/capability-config.ts +8 -11
- package/assets/templates/helpers/capability-resolver.ts +37 -19
- package/assets/templates/helpers/capture-plan.sh +2 -2
- package/assets/templates/helpers/check-architecture-sync.sh +1 -9
- package/assets/templates/helpers/check-task-sync.sh +82 -11
- package/assets/templates/helpers/check-task-workflow.sh +1 -3
- package/assets/templates/helpers/context-contract-sync.sh +1 -12
- package/assets/templates/helpers/contract-worktree.sh +26 -24
- package/assets/templates/helpers/ensure-task-workflow.sh +6 -8
- package/assets/templates/helpers/inspect-project-state.ts +1 -1
- package/assets/templates/helpers/migrate-project-template.sh +1 -1
- package/assets/templates/helpers/migrate-workflow-docs.ts +53 -239
- package/assets/templates/helpers/new-plan.sh +2 -2
- package/assets/templates/helpers/plan-to-todo.sh +3 -31
- package/assets/templates/helpers/sprint-backlog.sh +1 -1
- package/assets/templates/helpers/switch-plan.sh +1 -1
- package/assets/templates/helpers/workflow-contract.ts +1 -0
- package/assets/workflow-contract.v1.json +63 -171
- package/assets/workflows/xiaohongshu/xiaohongshu.collect-metrics.v1.draft.json +118 -0
- package/assets/workflows/xiaohongshu/xiaohongshu.publish-image-note.v1.draft.json +316 -0
- package/assets/workflows/xiaohongshu/xiaohongshu.publish-long-text.v1.draft.json +324 -0
- package/docs/architecture/CURRENT.md +148 -13
- package/docs/architecture/EVOLUTION.md +13 -0
- package/docs/architecture/decisions/20260801-controller-home-sqlite-state.md +2 -1
- package/docs/architecture/decisions/20260831-independent-windows-wsl-host-rescue.md +59 -0
- package/docs/architecture/decisions/20260904-ios-device-migration-baseline.md +120 -0
- package/docs/architecture/decisions/20260904-repository-runtime-data-boundary.md +42 -0
- package/docs/architecture/decisions/20260907-assistant-learning-loop.md +35 -0
- package/docs/architecture/decisions/20260907-controller-round-transition-policy.md +296 -0
- package/docs/architecture/decisions/20260907-maintenance-diagnosis-bounded-read.md +39 -0
- package/docs/architecture/domains/verification.md +7 -0
- package/docs/architecture/domains/workflow-engine.md +1 -1
- package/docs/architecture/index.md +3 -3
- package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +19 -8
- package/docs/architecture/requests/2026-09-04-computer-plugin-workflow-taxonomy.md +124 -0
- package/docs/forge-chatgpt-mcp-setup.md +5 -3
- package/docs/forge-plugin-management.md +20 -4
- package/docs/forge-runtime-self-healing-loop.md +2 -0
- package/docs/operations/benchmark-cross-version-freeze.md +38 -0
- package/docs/operations/controller-browser-plugin.md +3 -1
- package/docs/operations/homebrew.md +2 -2
- package/docs/operations/platform-support.md +16 -8
- package/docs/operations/platform-support.zh-CN.md +2 -2
- package/docs/operations/standalone-disaster-recovery.md +26 -1
- package/docs/operations/uu-remote-rescue.md +41 -0
- package/docs/operations/windows-wsl-host-rescue.md +133 -0
- package/docs/public-usage-guide.md +1 -1
- package/docs/public-usage-guide.zh-CN.md +1 -1
- package/docs/tutorials/01-install-and-start.md +1 -1
- package/docs/tutorials/01-install-and-start.zh-CN.md +1 -1
- package/docs/tutorials/02-connect-chatgpt.md +4 -4
- package/docs/tutorials/02-connect-chatgpt.zh-CN.md +4 -4
- package/docs/wiki/Implementation.md +1 -1
- package/package.json +23 -14
- package/packages/kernel/controller/api/index.ts +2 -0
- package/packages/kernel/controller/application/controller-round-service.ts +29 -0
- package/packages/kernel/controller/application/controller-service.ts +30 -0
- package/packages/kernel/controller/domain/controller-round-transition-policy.ts +308 -0
- package/packages/kernel/controller/domain/controller-round.ts +107 -0
- package/packages/kernel/controller/domain/execution-quality.ts +133 -0
- package/packages/kernel/controller/domain/types.ts +56 -0
- package/packages/kernel/controller/index.ts +1 -0
- package/packages/kernel/controller/infrastructure/controller-binding-store.ts +84 -0
- package/packages/kernel/controller/infrastructure/controller-round-store.ts +1384 -0
- package/packages/kernel/controller/infrastructure/controller-session-store.ts +578 -0
- package/packages/kernel/controller/ports/controller-host.ts +16 -0
- package/packages/kernel/goal/api/index.ts +1 -0
- package/packages/kernel/goal/domain/types.ts +40 -0
- package/packages/kernel/goal/index.ts +1 -0
- package/packages/kernel/identity/api/index.ts +5 -0
- package/packages/kernel/identity/application/identity-service.ts +67 -0
- package/packages/kernel/identity/domain/scope.ts +63 -0
- package/packages/kernel/identity/domain/semantic-sync.ts +81 -0
- package/packages/kernel/identity/domain/types.ts +44 -0
- package/packages/kernel/identity/domain/workspace.ts +56 -0
- package/packages/kernel/identity/index.ts +1 -0
- package/packages/kernel/identity/infrastructure/identity-store.ts +75 -0
- package/packages/kernel/memory/api/index.ts +5 -0
- package/packages/kernel/memory/application/experience-service.ts +118 -0
- package/packages/kernel/memory/application/outcome-service.ts +77 -0
- package/packages/kernel/memory/domain/experience.ts +103 -0
- package/packages/kernel/memory/domain/operational-prior.ts +92 -0
- package/packages/kernel/memory/index.ts +1 -0
- package/packages/kernel/progression/api/index.ts +2 -0
- package/packages/kernel/progression/application/projector.ts +211 -0
- package/packages/kernel/progression/domain/types.ts +116 -0
- package/packages/kernel/progression/index.ts +1 -0
- package/packages/kernel/scheduler/api/index.ts +6 -0
- package/packages/kernel/scheduler/application/continuation-service.ts +212 -0
- package/packages/kernel/scheduler/application/eligibility.ts +252 -0
- package/packages/kernel/scheduler/application/schedule-service.ts +24 -0
- package/packages/kernel/scheduler/application/settlement.ts +197 -0
- package/packages/kernel/scheduler/domain/continuation.ts +38 -0
- package/packages/kernel/scheduler/domain/schedule.ts +149 -0
- package/packages/kernel/scheduler/index.ts +1 -0
- package/packages/kernel/scheduler/infrastructure/continuation-dispatch-store.ts +38 -0
- package/packages/kernel/scheduler/infrastructure/schedule-store.ts +785 -0
- package/packages/kernel/work/api/index.ts +15 -0
- package/packages/kernel/work/application/work-service.ts +148 -0
- package/packages/kernel/work/domain/admission-policy.ts +34 -0
- package/packages/kernel/work/domain/check-receipt.ts +43 -0
- package/packages/kernel/work/domain/context-closure-receipt.ts +32 -0
- package/packages/kernel/work/domain/engineering-contracts.ts +157 -0
- package/packages/kernel/work/domain/engineering-design.ts +281 -0
- package/packages/kernel/work/domain/engineering-profile.ts +242 -0
- package/packages/kernel/work/domain/execution-concurrency.ts +254 -0
- package/packages/kernel/work/domain/execution-snapshot.ts +33 -0
- package/packages/kernel/work/domain/implementation-review.ts +510 -0
- package/packages/kernel/work/domain/project-engineering-contract.ts +202 -0
- package/packages/kernel/work/domain/repository-completion-receipt.ts +73 -0
- package/packages/kernel/work/domain/state-machine.ts +259 -0
- package/packages/kernel/work/domain/types.ts +528 -0
- package/packages/kernel/work/index.ts +1 -0
- package/packages/kernel/work/infrastructure/work-contract-store.ts +1876 -0
- package/packages/kernel/work/ports/work-contract-store.ts +18 -0
- package/packages/plugin-runtime/browser/index.ts +3 -0
- package/packages/plugin-runtime/browser/provider-registry.ts +110 -0
- package/packages/plugin-runtime/browser/runtime-contract.ts +150 -0
- package/packages/plugin-runtime/browser/session-authority.ts +61 -0
- package/packages/plugin-runtime/computer/index.ts +6 -0
- package/packages/plugin-runtime/computer/provider-error.ts +26 -0
- package/packages/plugin-runtime/computer/provider-registration.ts +52 -0
- package/packages/plugin-runtime/computer/provider-registry.ts +63 -0
- package/packages/plugin-runtime/computer/provider.ts +19 -0
- package/packages/plugin-runtime/computer/target-authority.ts +190 -0
- package/packages/plugin-runtime/computer/target-persistence.ts +54 -0
- package/packages/plugin-runtime/external/index.ts +1 -0
- package/packages/plugin-runtime/external/unix-jsonl-transport.ts +457 -0
- package/packages/plugin-runtime/index.ts +3 -0
- package/packages/protocols/browser/index.ts +1 -0
- package/packages/protocols/browser/session.ts +98 -0
- package/packages/protocols/computer/contract.ts +199 -0
- package/packages/protocols/computer/index.ts +1 -0
- package/packages/protocols/failure/contract.ts +158 -0
- package/packages/protocols/failure/index.ts +1 -0
- package/packages/protocols/handoff/index.ts +1 -0
- package/packages/protocols/handoff/status.ts +22 -0
- package/packages/protocols/mcp/execution-context.ts +11 -0
- package/packages/protocols/mcp/index.ts +2 -0
- package/packages/protocols/mcp/tool-contract.ts +42 -0
- package/packages/workflow-runtime/api/index.ts +2 -0
- package/packages/workflow-runtime/application/interpreter.ts +342 -0
- package/packages/workflow-runtime/domain/workflow-asset.ts +223 -0
- package/packages/workflow-runtime/index.ts +1 -0
- package/scripts/assemble-template.ts +1 -1
- package/scripts/benchmark-computer-hotpath.ts +255 -0
- package/scripts/benchmark-controller-round2.ts +54 -2
- package/scripts/benchmark-ios-agent-device.ts +3 -6
- package/scripts/benchmark-mcp-transport.ts +3 -4
- package/scripts/benchmark-operational-memory-activation.ts +235 -0
- package/scripts/benchmark-operational-shadow.ts +28 -0
- package/scripts/bootstrap-runtime-maintenance-recovery.sh +4 -1
- package/scripts/canary-chatgpt-provider-delivery.ts +97 -0
- package/scripts/canary-controller-semantic-wait.ts +195 -0
- package/scripts/canary-global-hook.sh +2 -2
- package/scripts/check-mcp-compatibility.ts +173 -7
- package/scripts/check-platform-support.mjs +28 -2
- package/scripts/check-public-docs.sh +1 -1
- package/scripts/check-release-readiness.sh +1 -1
- package/scripts/check-repository-hygiene.mjs +92 -0
- package/scripts/check-runtime-architecture.mjs +1206 -95
- package/scripts/check-source-duplication.mjs +9 -1
- package/scripts/check-tarball-install-smoke.sh +98 -15
- package/scripts/controller-ngrok-rotation.sh +5 -4
- package/scripts/create-project-dirs.sh +10 -74
- package/scripts/forge-local-recovery-helper.mjs +204 -0
- package/scripts/forge-uu-remote-rescue-helper.d.mts +31 -0
- package/scripts/forge-uu-remote-rescue-helper.mjs +409 -0
- package/scripts/forge-windows-host-recovery-helper.mjs +136 -0
- package/scripts/hook-shim.sh +2 -2
- package/scripts/init-project.sh +12 -70
- package/scripts/install-desktop-operator-registration.ts +4 -2
- package/scripts/install-independent-windows-wsl-recovery.ts +125 -0
- package/scripts/install-standalone-recovery.ts +122 -10
- package/scripts/install-uu-remote-rescue-registration.ts +74 -0
- package/scripts/install-windows-host-recovery-registration.ts +22 -0
- package/scripts/lib/controller-home-env.sh +6 -3
- package/scripts/lib/project-init-lib.sh +39 -582
- package/scripts/migrate-chatgpt-automation-schedules.ts +2 -2
- package/scripts/migrate-windows-wsl-controller-home.ts +306 -0
- package/scripts/open-source-audit-allowlist.txt +12 -1
- package/scripts/portability-bindings.mjs +70 -0
- package/scripts/run-bounded-command.mjs +55 -0
- package/scripts/run-bun-test-file.ts +22 -6
- package/scripts/run-governed-gate.ts +4 -1
- package/scripts/smoke-mcp-http-runtime.ts +1 -1
- package/scripts/smoke-runtime-recovery.ts +34 -1
- package/scripts/smoke-schedule-engine.ts +1 -1
- package/scripts/stage-runtime-release.ts +6 -1
- package/scripts/test-governance.ts +51 -2
- package/src/cli/agent-jobs/job-manager.ts +10 -1
- package/src/cli/chatgpt-browser/bind-server.ts +2 -1
- package/src/cli/chatgpt-browser/binding.ts +20 -10
- package/src/cli/chatgpt-browser/bridge-extension.ts +75 -0
- package/src/cli/chatgpt-browser/bridge-provider.ts +376 -11
- package/src/cli/chatgpt-browser/engine.ts +10 -11
- package/src/cli/chatgpt-browser/native-provider.ts +24 -83
- package/src/cli/chatgpt-browser/oracle-provider.ts +5 -3
- package/src/cli/chatgpt-browser/types.ts +1 -0
- package/src/cli/commands/bootstrap-control.ts +334 -0
- package/src/cli/commands/brain-assistant.ts +38 -0
- package/src/cli/commands/brain.ts +193 -40
- package/src/cli/commands/chatgpt.ts +127 -0
- package/src/cli/commands/computer.ts +437 -0
- package/src/cli/commands/doctor.ts +18 -1
- package/src/cli/commands/init-hook.ts +71 -61
- package/src/cli/commands/init.ts +3 -1
- package/src/cli/commands/mcp.ts +16 -10
- package/src/cli/commands/plugin.ts +38 -12
- package/src/cli/commands/recovery.ts +493 -80
- package/src/cli/commands/runtime.ts +9 -4
- package/src/cli/commands/setup-profile.ts +89 -39
- package/src/cli/commands/status.ts +2 -1
- package/src/cli/controller/check-runner.ts +487 -98
- package/src/cli/controller/context/types.ts +7 -1
- package/src/cli/controller/context-pack.ts +23 -3
- package/src/cli/controller/issue-store.ts +1 -1
- package/src/cli/controller/systemd-user.ts +110 -0
- package/src/cli/controller/types.ts +10 -70
- package/src/cli/controller/worklog.ts +67 -13
- package/src/cli/editing/edit-operation-contract.ts +92 -0
- package/src/cli/editing/edit-session.ts +289 -13
- package/src/cli/github/plugin.ts +17 -12
- package/src/cli/hook/route-registry.ts +3 -5
- package/src/cli/hook/runtime.ts +49 -3
- package/src/cli/index.ts +3 -0
- package/src/cli/local-bridge/connector-freshness.ts +2 -2
- package/src/cli/local-bridge/console-automations.ts +2 -2
- package/src/cli/local-bridge/facade-api.ts +41 -14
- package/src/cli/local-bridge/job-store.ts +1 -1
- package/src/cli/local-bridge/server.ts +24 -27
- package/src/cli/mcp/access-mode.ts +2 -132
- package/src/cli/mcp/access-tools.ts +2 -387
- package/src/cli/mcp/audit.ts +2 -32
- package/src/cli/mcp/auth.ts +2 -404
- package/src/cli/mcp/instructions.ts +2 -34
- package/src/cli/mcp/legacy-context.ts +2 -16
- package/src/cli/mcp/legacy-tool-service.ts +2 -5308
- package/src/cli/mcp/multi-repository.ts +2 -436
- package/src/cli/mcp/oauth.ts +2 -375
- package/src/cli/mcp/openai-secure-tunnel.ts +2 -0
- package/src/cli/mcp/paths.ts +2 -140
- package/src/cli/mcp/policy.ts +2 -232
- package/src/cli/mcp/post-finalize-work-attribution.ts +2 -150
- package/src/cli/mcp/proxy-env.ts +2 -62
- package/src/cli/mcp/readiness-projection.ts +2 -24
- package/src/cli/mcp/redaction.ts +2 -21
- package/src/cli/mcp/repo.ts +2 -24
- package/src/cli/mcp/repository-tools.ts +2 -1613
- package/src/cli/mcp/server.ts +2 -951
- package/src/cli/mcp/setup.ts +2 -737
- package/src/cli/mcp/tool-capability-cache.ts +2 -87
- package/src/cli/mcp/tool-contract.ts +2 -14
- package/src/cli/mcp/tools.ts +2 -9
- package/src/cli/mcp/toolset-names.ts +2 -70
- package/src/cli/mcp/toolset-selection.ts +2 -41
- package/src/cli/mcp/toolset.ts +2 -336
- package/src/cli/mcp/transports/AGENTS.md +1 -10
- package/src/cli/mcp/transports/CLAUDE.md +1 -10
- package/src/cli/mcp/transports/http.ts +2 -1414
- package/src/cli/mcp/transports/session-registry.ts +2 -490
- package/src/cli/mcp/transports/stdio.ts +2 -8
- package/src/cli/mcp/types.ts +2 -41
- package/src/cli/mcp/umbrella-tools.ts +2 -117
- package/src/cli/repositories/command-executor.ts +64 -18
- package/src/cli/repositories/command-scope.ts +117 -1
- package/src/cli/repositories/controller-home.ts +119 -1
- package/src/cli/repositories/identity.ts +13 -0
- package/src/cli/repositories/registry.ts +68 -49
- package/src/cli/repositories/repository-snapshot.ts +53 -7
- package/src/cli/repositories/runtime-storage.ts +27 -2
- package/src/cli/repositories/safe-patch.ts +34 -38
- package/src/cli/repositories/selected-path-actions.ts +22 -2
- package/src/cli/repositories/structured-git.ts +105 -25
- package/src/cli/runtime/helper-runner.ts +7 -0
- package/src/cli/runtime-invocation.ts +8 -1
- package/src/cli/tools/codegraph.ts +44 -25
- package/src/core/adoption/manifest-templates.ts +1 -1
- package/src/core/adoption/plan.ts +3 -26
- package/src/core/adoption/workflow-contract-plan.ts +17 -9
- package/src/effects/process-runner.ts +8 -14
- package/src/runtime/context/assistant-context.ts +154 -0
- package/src/runtime/context/assistant-work-context.ts +147 -0
- package/src/runtime/context/codegraph-cache-boundary.ts +255 -0
- package/src/runtime/context/codegraph-read-provider.ts +55 -22
- package/src/runtime/context/codegraph-sidecar.cjs +8 -1
- package/src/runtime/context/context-closure.ts +268 -0
- package/src/runtime/context/context-plane.ts +211 -0
- package/src/runtime/context/project-engineering-contract.ts +60 -0
- package/src/runtime/control-plane/bootstrap/capability-provider.ts +74 -0
- package/src/runtime/control-plane/bootstrap/control-api.ts +57 -0
- package/src/runtime/control-plane/bootstrap/index.ts +5 -0
- package/src/runtime/control-plane/bootstrap/state-machine.ts +141 -0
- package/src/runtime/control-plane/bootstrap/store.ts +21 -0
- package/src/runtime/control-plane/bootstrap/types.ts +87 -0
- package/src/runtime/control-plane/cleanup-artifact-retention.ts +328 -0
- package/src/runtime/control-plane/codegraph-cache-retention.ts +164 -0
- package/src/runtime/control-plane/concurrency/work-execution-concurrency.ts +320 -0
- package/src/runtime/control-plane/execution/content-equivalent-commit-authority.ts +169 -0
- package/src/runtime/control-plane/execution/controller-authority-recovery.ts +350 -0
- package/src/runtime/control-plane/execution/direct-canonical-work-reconciliation.ts +85 -0
- package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +369 -99
- package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +103 -15
- package/src/runtime/control-plane/execution/implementation-review-content.ts +95 -0
- package/src/runtime/control-plane/execution/repository-work-attribution.ts +2 -2
- package/src/runtime/control-plane/execution/retained-work-resume.ts +336 -6
- package/src/runtime/control-plane/execution/validation.ts +28 -3
- package/src/runtime/control-plane/execution/work-authority-reconciler.ts +116 -0
- package/src/runtime/control-plane/execution/work-completion-authority.ts +44 -2
- package/src/runtime/control-plane/execution/work-evidence-policy.ts +34 -3
- package/src/runtime/control-plane/execution/work-execution-support.ts +42 -22
- package/src/runtime/control-plane/execution/work-finalization-service.ts +1103 -166
- package/src/runtime/control-plane/execution/work-handle-authority.ts +219 -22
- package/src/runtime/control-plane/execution/work-handle-store.ts +77 -0
- package/src/runtime/control-plane/execution/work-head-settlement.ts +75 -0
- package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +145 -23
- package/src/runtime/control-plane/execution/work-operation-service.ts +49 -13
- package/src/runtime/control-plane/execution/work-preparation-service.ts +31 -15
- package/src/runtime/control-plane/execution/work-remote-delivery.ts +1 -1
- package/src/runtime/control-plane/execution/work-task-receipt.ts +20 -1
- package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +407 -76
- package/src/runtime/control-plane/execution/work-validation-reconciler.ts +88 -18
- package/src/runtime/control-plane/execution/work-verification-context.ts +1 -1
- package/src/runtime/control-plane/execution/work-verification-service.ts +258 -4
- package/src/runtime/control-plane/execution/work-verification-snapshot.ts +200 -15
- package/src/runtime/control-plane/facade/capability-registry.ts +21 -10
- package/src/runtime/control-plane/facade/controller-round-relay.ts +3 -1118
- package/src/runtime/control-plane/facade/controller-session-store.ts +2 -488
- package/src/runtime/control-plane/facade/goal-workloop-access.ts +6 -3
- package/src/runtime/control-plane/facade/goal-workloop.ts +972 -137
- package/src/runtime/control-plane/facade/handoff-inbox-application.ts +158 -0
- package/src/runtime/control-plane/facade/handoff-inbox-store.ts +10 -0
- package/src/runtime/control-plane/facade/index.ts +4 -2
- package/src/runtime/control-plane/facade/operation-digest.ts +22 -58
- package/src/runtime/control-plane/facade/plan-contract-store.ts +1004 -38
- package/src/runtime/control-plane/facade/repository-work-admission.ts +19 -1
- package/src/runtime/control-plane/facade/requirement-authority.ts +218 -0
- package/src/runtime/control-plane/facade/requirement-board.ts +6 -1
- package/src/runtime/control-plane/facade/rh-work-operation-contract.ts +48 -0
- package/src/runtime/control-plane/facade/semantic-admission.ts +8 -16
- package/src/runtime/control-plane/facade/suggested-actions.ts +4 -1
- package/src/runtime/control-plane/facade/types.ts +189 -532
- package/src/runtime/control-plane/facade/work-admission-policy.ts +84 -35
- package/src/runtime/control-plane/facade/work-contract-store.ts +2 -1043
- package/src/runtime/control-plane/facade/work-implementation-review.ts +2 -0
- package/src/runtime/control-plane/facade/work-state-machine.ts +2 -250
- package/src/runtime/control-plane/generated-cache-retention.ts +239 -18
- package/src/runtime/control-plane/global-scheduler/durable-admission.ts +12 -1
- package/src/runtime/control-plane/global-scheduler/maintenance.ts +76 -13
- package/src/runtime/control-plane/global-scheduler/periodic-cleanup-entry.ts +55 -0
- package/src/runtime/control-plane/global-scheduler/periodic-cleanup-process.ts +98 -0
- package/src/runtime/control-plane/global-scheduler/reconciliation.ts +1 -1
- package/src/runtime/control-plane/global-scheduler/scheduler-worker-entry.ts +10 -0
- package/src/runtime/control-plane/global-scheduler/scheduler.ts +173 -81
- package/src/runtime/control-plane/global-scheduler/worker-launch.ts +18 -6
- package/src/runtime/control-plane/launcher/chatgpt-round-continuation.ts +397 -0
- package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +2 -177
- package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +301 -560
- package/src/runtime/control-plane/launcher/provider-mcp-bootstrap.ts +20 -4
- package/src/runtime/control-plane/launcher/thin-launcher.ts +50 -4
- package/src/runtime/control-plane/lifecycle-retention-metrics.ts +60 -0
- package/src/runtime/control-plane/persistence/experience-store.ts +169 -0
- package/src/runtime/control-plane/persistence/operational-prior-store.ts +348 -0
- package/src/runtime/control-plane/persistence/requirement-store.ts +30 -22
- package/src/runtime/control-plane/persistence/sqlite-store.ts +545 -76
- package/src/runtime/control-plane/persistence/workflow-content-store.ts +98 -0
- package/src/runtime/control-plane/persistence/workflow-registry-store.ts +186 -0
- package/src/runtime/control-plane/persistence/workflow-run-store.ts +123 -0
- package/src/runtime/control-plane/release-retention.ts +56 -16
- package/src/runtime/control-plane/repository-namespace-retention.ts +208 -0
- package/src/runtime/control-plane/routing/route-policy.ts +19 -0
- package/src/runtime/control-plane/runtime-cleanup.ts +574 -37
- package/src/runtime/control-plane/runtime-generation.ts +6 -0
- package/src/runtime/control-plane/workspace/semantic-sync-service.ts +465 -0
- package/src/runtime/control-plane/workspace/workspace-store.ts +80 -0
- package/src/runtime/diagnostics/incident-repair.ts +406 -0
- package/src/runtime/diagnostics/mcp-timing.ts +125 -6
- package/src/runtime/diagnostics/performance.ts +6 -14
- package/src/runtime/diagnostics/read-only-tool.ts +1 -1
- package/src/runtime/evidence/artifact-store.ts +65 -2
- package/src/runtime/evidence/operational-shadow.ts +38 -0
- package/src/runtime/evidence/process-check-receipt.ts +5 -43
- package/src/runtime/evidence/sensitive-output.ts +4 -4
- package/src/runtime/execution/jobs/receipt-recovery.ts +1 -1
- package/src/runtime/execution/jobs/store.ts +121 -2
- package/src/runtime/execution/managed-workspace.ts +55 -5
- package/src/runtime/execution/process-runtime/canary.ts +19 -5
- package/src/runtime/execution/process-runtime/check-classification.ts +48 -0
- package/src/runtime/execution/process-runtime/check-facade.ts +23 -9
- package/src/runtime/execution/process-runtime/check-receipt.ts +1 -1
- package/src/runtime/execution/process-runtime/check-result-retention.ts +132 -0
- package/src/runtime/execution/process-runtime/check-result.ts +73 -2
- package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +42 -3
- package/src/runtime/execution/process-runtime/check-storage.ts +187 -0
- package/src/runtime/execution/process-runtime/command-facade.ts +38 -87
- package/src/runtime/execution/process-runtime/index.ts +1 -0
- package/src/runtime/execution/process-runtime/interactive-admission.ts +2 -1
- package/src/runtime/execution/process-runtime/lightweight-managed.ts +32 -1
- package/src/runtime/execution/process-runtime/persisted-check.ts +128 -21
- package/src/runtime/execution/process-runtime/resource-claims.ts +60 -3
- package/src/runtime/execution/process-runtime/runtime.ts +132 -15
- package/src/runtime/execution/process-runtime/types.ts +22 -0
- package/src/runtime/execution/workers/executor.ts +16 -13
- package/src/runtime/execution/workers/worker-entry.ts +1 -1
- package/src/runtime/gateway/mcp/execution-tools.ts +2 -207
- package/src/runtime/gateway/mcp/legacy-ios-tool-adapter.ts +2 -61
- package/src/runtime/gateway/mcp/persisted-check-process.ts +2 -3
- package/src/runtime/gateway/mcp/process-tools.ts +2 -289
- package/src/runtime/gateway/mcp/router.ts +2 -1125
- package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +2 -565
- package/src/runtime/gateway/mcp/runtime-tools.ts +2 -6655
- package/src/runtime/gateway/mcp/work-validation-reconciler.ts +2 -1
- package/src/runtime/maintenance/cleanup.ts +38 -24
- package/src/runtime/platform/browser-product-discovery.ts +174 -0
- package/src/runtime/platform/computer-platform.ts +14 -0
- package/src/runtime/platform/executable-discovery.ts +54 -0
- package/src/runtime/platform/fetch-http-server.ts +90 -0
- package/src/runtime/platform/service-manager.ts +138 -0
- package/src/runtime/platform/windows-host-discovery.ts +118 -0
- package/src/runtime/platform/windows-recovery-deployment.ts +67 -0
- package/src/runtime/plugins/action-application.ts +135 -0
- package/src/runtime/plugins/app-store-connect-adapter.ts +9 -21
- package/src/runtime/plugins/browser-adapter.ts +140 -100
- package/src/runtime/plugins/browser-automation-service.ts +15 -36
- package/src/runtime/plugins/browser-macos-bridge.ts +69 -21
- package/src/runtime/plugins/browser-manifest-surface.ts +30 -6
- package/src/runtime/plugins/browser-native-session-service.ts +1 -1
- package/src/runtime/plugins/browser-provider-registry.ts +2 -109
- package/src/runtime/plugins/browser-registration.ts +16 -0
- package/src/runtime/plugins/browser-runtime-contract.ts +2 -154
- package/src/runtime/plugins/browser-runtime.ts +41 -9
- package/src/runtime/plugins/browser-session-authority.ts +101 -291
- package/src/runtime/plugins/browser-session-legacy-migration.ts +203 -0
- package/src/runtime/plugins/browser-session-store.ts +229 -36
- package/src/runtime/plugins/browser-session-types.ts +2 -98
- package/src/runtime/plugins/capability-authorization-grants.ts +17 -0
- package/src/runtime/plugins/catalog-trust.ts +82 -0
- package/src/runtime/plugins/computer-registration.ts +839 -0
- package/src/runtime/plugins/config-store.ts +119 -0
- package/src/runtime/plugins/desktop-operator-external-policy.ts +932 -0
- package/src/runtime/plugins/desktop-operator-registration.ts +111 -8
- package/src/runtime/plugins/errors.ts +8 -1
- package/src/runtime/plugins/external-adapter.ts +45 -322
- package/src/runtime/plugins/external-provider-policy.ts +48 -0
- package/src/runtime/plugins/external-registration.ts +9 -1
- package/src/runtime/plugins/external-unix-socket-probe.cjs +9 -3
- package/src/runtime/plugins/external-unix-socket.ts +64 -193
- package/src/runtime/plugins/first-party-external-registration.ts +163 -0
- package/src/runtime/plugins/first-party-registry.ts +3 -1
- package/src/runtime/plugins/github-adapter.ts +1 -2
- package/src/runtime/plugins/gmail-adapter.ts +6 -6
- package/src/runtime/plugins/google-calendar-adapter.ts +6 -6
- package/src/runtime/plugins/google-shared.ts +31 -46
- package/src/runtime/plugins/google-tasks-adapter.ts +6 -6
- package/src/runtime/plugins/ios-adapter.ts +1 -2
- package/src/runtime/plugins/local-system-adapter.ts +180 -0
- package/src/runtime/plugins/macos-capability-broker.ts +15 -105
- package/src/runtime/plugins/resend-adapter.ts +15 -24
- package/src/runtime/plugins/store.ts +493 -51
- package/src/runtime/plugins/types.ts +8 -1
- package/src/runtime/plugins/uu-remote-rescue-registration.ts +155 -0
- package/src/runtime/plugins/windows-host-recovery-install.ts +68 -0
- package/src/runtime/plugins/windows-host-recovery-registration.ts +85 -0
- package/src/runtime/plugins/xiaohongshu-publish.ts +86 -430
- package/src/runtime/projections/controller-context.ts +12 -6
- package/src/runtime/projections/git-status-sampler.ts +73 -22
- package/src/runtime/projections/materialized-view.ts +14 -8
- package/src/runtime/recovery/capability-action-application.ts +228 -0
- package/src/runtime/recovery/index.ts +3 -0
- package/src/runtime/recovery/lifecycle-inventory.ts +338 -0
- package/src/runtime/recovery/local-jobs-repair.ts +14 -14
- package/src/runtime/recovery/maintenance-executor.ts +358 -76
- package/src/runtime/recovery/quarantine-retention.ts +256 -0
- package/src/runtime/release/identity.ts +6 -0
- package/src/runtime/resources/leases/store.ts +1 -1
- package/src/runtime/root/active-execution-power-assertion.ts +139 -0
- package/src/runtime/root/assistant-learning-loop.ts +45 -0
- package/src/runtime/root/browser-session-composition.ts +59 -0
- package/src/runtime/root/computer-composition.ts +191 -0
- package/src/runtime/root/computer-target-composition.ts +16 -0
- package/src/runtime/root/computer-target-persistence.ts +39 -0
- package/src/runtime/root/controller-round-composition.ts +107 -0
- package/src/runtime/root/controller-services.ts +2 -0
- package/src/runtime/root/entry.ts +4 -3
- package/src/runtime/root/gateway-adapter.ts +3 -3
- package/src/runtime/root/local-bridge.ts +5 -2
- package/src/runtime/root/mcp-transport.ts +5 -5
- package/src/runtime/root/package-connector-service.ts +108 -56
- package/src/runtime/root/package-runtime-release.ts +85 -18
- package/src/runtime/root/package-runtime-service.ts +60 -97
- package/src/runtime/root/release-execution-canary.ts +71 -0
- package/src/runtime/root/release-loader.ts +68 -0
- package/src/runtime/root/release-manifest.ts +106 -2
- package/src/runtime/root/release-materialize.ts +103 -4
- package/src/runtime/root/release-store.ts +31 -2
- package/src/runtime/root/runtime.ts +240 -25
- package/src/runtime/root/scheduled-controller-composition.ts +51 -0
- package/src/runtime/root/scheduler.ts +12 -4
- package/src/runtime/root/service-runner.ts +1 -1
- package/src/runtime/root/service.ts +41 -16
- package/src/runtime/root/status.ts +1 -0
- package/src/runtime/root/types.ts +24 -1
- package/src/runtime/shared/local-bridge-surface.ts +1 -1
- package/src/runtime/shared/process-environment.ts +19 -4
- package/src/runtime/shared/storage-capacity.ts +68 -0
- package/src/runtime/standalone-recovery/controller-home-migration.ts +1116 -0
- package/src/runtime/standalone-recovery/core.ts +984 -230
- package/src/runtime/standalone-recovery/entry.ts +182 -51
- package/src/runtime/standalone-recovery/independent-host-rescue.ts +172 -0
- package/src/runtime/standalone-recovery/installer.ts +212 -104
- package/src/runtime/standalone-recovery/mcp-server.ts +243 -0
- package/src/runtime/standalone-recovery/mutation-identity-contract.ts +21 -0
- package/src/runtime/standalone-recovery/operation-lock.ts +160 -0
- package/src/runtime/standalone-recovery/performance.ts +135 -0
- package/src/runtime/standalone-recovery/release.ts +5 -0
- package/src/runtime/watchdog/workflow-watchdog.ts +1 -1
- package/src/runtime/workflow/schedules/chatgpt-automation-migration.ts +1 -1
- package/src/runtime/workflow/schedules/engine.ts +191 -343
- package/src/runtime/workflow/schedules/pending-activation.ts +1 -1
- package/src/runtime/workflow/schedules/settlement.ts +2 -191
- package/src/runtime/workflow/schedules/store.ts +2 -607
- package/src/runtime/workflow/schedules/types.ts +2 -127
- package/src/runtime/workflow/schedules/work-continuation.ts +180 -15
- package/src/runtime/workflows/first-party/xiaohongshu.ts +122 -0
- package/src/runtime/workflows/runtime.ts +486 -0
- package/src/testing/test-governance.ts +90 -29
- package/assets/templates/helpers/archive-architecture-request.sh +0 -244
- package/docs/architecture/history/README.md +0 -7
- package/docs/architecture/history/chatgpt-handoff-facade.md +0 -397
- package/docs/architecture/history/global-hook-runtime.md +0 -166
- package/docs/architecture/history/ios-semantic-automation-provider-v2.md +0 -170
- package/docs/architecture/requests/archive/2026/20260528-154740-ai-hooks-ai-hooks-hook-input-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-154816-assets-hooks-assets-hooks-hook-input-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-161851-ai-harness-workflow-contract-json-ai-harness-workflow-contract-json.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-161853-scripts-check-task-workflow-sh-scripts-check-task-workflow-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-161911-assets-workflow-contract-v1-json-assets-workflow-contract-v1-json.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-222416-ai-hooks-ai-hooks-hook-input-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-222444-ai-hooks-ai-hooks-pre-edit-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-222457-ai-hooks-ai-hooks-pre-edit-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-222648-ai-hooks-ai-hooks-pre-edit-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-222705-ai-hooks-ai-hooks-worktree-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-222726-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-222727-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-224736-ai-hooks-ai-hooks-hook-input-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-233259-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-233335-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-233456-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-233457-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-233637-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-233638-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260529-000322-root-package-json.md +0 -59
- package/docs/architecture/requests/archive/2026/20260529-004238-ai-hooks-ai-hooks-lib-workflow-state-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260529-020654-ai-harness-policy-json-ai-harness-policy-json.md +0 -63
- package/docs/architecture/requests/archive/2026/20260529-094236-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -60
- package/docs/architecture/requests/archive/2026/20260529-094323-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -60
- package/docs/architecture/requests/archive/2026/20260529-094331-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -60
- package/docs/architecture/requests/archive/2026/20260529-094339-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -60
- package/docs/architecture/requests/archive/2026/20260529-094430-assets-hooks-assets-hooks-session-start-context-sh.md +0 -60
- package/docs/architecture/requests/archive/2026/20260529-094446-ai-hooks-ai-hooks-session-start-context-sh.md +0 -60
- package/docs/architecture/requests/archive/2026/20260613-002040-runtime-harness-hook-adapters.md +0 -68
- package/docs/architecture/requests/archive/2026/root.md +0 -68
- package/docs/architecture/requests/archive/2026/runtime-harness-hook-adapters.md +0 -76
- package/docs/architecture/requests/archive/2026/verification-evals-checks.md +0 -68
- package/docs/architecture/requests/archive/2026/workflow-engine-contract-assets.md +0 -69
- package/scripts/archive-architecture-request.sh +0 -4
- package/scripts/run-skill-evals.ts +0 -1022
|
@@ -2,10 +2,13 @@ import { createHash, randomUUID } from 'crypto';
|
|
|
2
2
|
import { createServer, type IncomingMessage, type ServerResponse } from 'http';
|
|
3
3
|
import { basename, join, resolve } from 'path';
|
|
4
4
|
import { pathToFileURL } from 'url';
|
|
5
|
-
import { readMcpServiceOAuthPassphrase } from '
|
|
5
|
+
import { readMcpServiceOAuthPassphrase } from '../../../adapters/mcp/auth';
|
|
6
6
|
import { FORGE_VERSION } from '../../version';
|
|
7
|
+
import type { Tool } from '@modelcontextprotocol/server';
|
|
8
|
+
import { RecoveryMcpSessionServer } from './mcp-server';
|
|
7
9
|
import {
|
|
8
10
|
activateRuntimeRelease,
|
|
11
|
+
assertRecoveryMutationIdentity,
|
|
9
12
|
attestKnownGood,
|
|
10
13
|
diagnose,
|
|
11
14
|
gatewayToken,
|
|
@@ -15,6 +18,7 @@ import {
|
|
|
15
18
|
saveWatchdogState,
|
|
16
19
|
reconnectMain,
|
|
17
20
|
recoverPrimaryRuntime,
|
|
21
|
+
recoveryMachineIdentity,
|
|
18
22
|
repairPublicTunnel,
|
|
19
23
|
restartPrimaryConnector,
|
|
20
24
|
restartPrimaryRuntime,
|
|
@@ -27,6 +31,7 @@ import {
|
|
|
27
31
|
watchdogTick,
|
|
28
32
|
type WatchdogState,
|
|
29
33
|
type RecoveryConfig,
|
|
34
|
+
type RecoveryMachineIdentity,
|
|
30
35
|
} from './core';
|
|
31
36
|
import {
|
|
32
37
|
createRecoveryWatchdogHeartbeat,
|
|
@@ -40,6 +45,7 @@ import {
|
|
|
40
45
|
type RecoveryRuntimeIdentity,
|
|
41
46
|
type RecoveryRuntimeRole,
|
|
42
47
|
} from './release';
|
|
48
|
+
import { RECOVERY_MUTATION_IDENTITY_CONTRACT, RECOVERY_MUTATION_IDENTITY_FIELDS } from './mutation-identity-contract';
|
|
43
49
|
|
|
44
50
|
function option(name: string): string | undefined {
|
|
45
51
|
const index = process.argv.indexOf(name);
|
|
@@ -54,6 +60,8 @@ function controllerHome(): string {
|
|
|
54
60
|
|
|
55
61
|
function output(value: unknown): void { process.stdout.write(`${JSON.stringify(value, null, 2)}\n`); }
|
|
56
62
|
|
|
63
|
+
import { runRecoveryControllerHomeMigrationWorker, scheduleRecoveryControllerHomeMigration } from './controller-home-migration';
|
|
64
|
+
|
|
57
65
|
export const RECOVERY_CLI_COMMANDS = [
|
|
58
66
|
'status',
|
|
59
67
|
'verify',
|
|
@@ -66,6 +74,7 @@ export const RECOVERY_CLI_COMMANDS = [
|
|
|
66
74
|
'recover-primary-runtime',
|
|
67
75
|
'activate-runtime-release',
|
|
68
76
|
'stage-and-activate-runtime-release',
|
|
77
|
+
'migrate-controller-home-worker',
|
|
69
78
|
'restart-public-tunnel',
|
|
70
79
|
'diagnose',
|
|
71
80
|
'reconnect-main',
|
|
@@ -128,6 +137,20 @@ async function cli(): Promise<void> {
|
|
|
128
137
|
return;
|
|
129
138
|
}
|
|
130
139
|
case 'stage-and-activate-runtime-release': output(await stageAndActivateConfiguredRuntimeRelease(config, {}, `recovery-cli:${process.pid}:${Date.now()}`)); return;
|
|
140
|
+
case 'migrate-controller-home-worker': {
|
|
141
|
+
const canonicalSourceRoot = option('--canonical-source-root');
|
|
142
|
+
const expectedSourceRevision = option('--expected-source-revision');
|
|
143
|
+
const migrationRequestId = option('--request-id');
|
|
144
|
+
if (!canonicalSourceRoot) throw new Error('RECOVERY_CONTROLLER_HOME_MIGRATION_SOURCE_REQUIRED');
|
|
145
|
+
if (!expectedSourceRevision) throw new Error('RECOVERY_CONTROLLER_HOME_MIGRATION_SOURCE_REVISION_REQUIRED');
|
|
146
|
+
if (!migrationRequestId) throw new Error('RECOVERY_REQUEST_ID_REQUIRED');
|
|
147
|
+
output(await runRecoveryControllerHomeMigrationWorker(config, {
|
|
148
|
+
requestId: migrationRequestId,
|
|
149
|
+
canonicalSourceRoot,
|
|
150
|
+
expectedSourceRevision,
|
|
151
|
+
}));
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
131
154
|
case 'restart-public-tunnel': output(await repairPublicTunnel(config)); return;
|
|
132
155
|
case 'diagnose': output(await diagnose(config)); return;
|
|
133
156
|
case 'reconnect-main': output(await reconnectMain(config)); return;
|
|
@@ -224,7 +247,7 @@ function html(response: ServerResponse, status: number, payload: string): void {
|
|
|
224
247
|
|
|
225
248
|
function setCorsHeaders(response: ServerResponse): void {
|
|
226
249
|
response.setHeader('access-control-allow-origin', '*');
|
|
227
|
-
response.setHeader('access-control-allow-methods', 'GET, POST, OPTIONS');
|
|
250
|
+
response.setHeader('access-control-allow-methods', 'GET, POST, DELETE, OPTIONS');
|
|
228
251
|
response.setHeader('access-control-allow-headers', 'authorization, content-type, mcp-session-id, mcp-protocol-version');
|
|
229
252
|
response.setHeader('access-control-expose-headers', 'www-authenticate, mcp-session-id');
|
|
230
253
|
}
|
|
@@ -245,23 +268,46 @@ function matchesAnyPath(url: string | undefined, paths: string[]): boolean {
|
|
|
245
268
|
return paths.some((path) => matchesPath(url, path));
|
|
246
269
|
}
|
|
247
270
|
|
|
271
|
+
function mutationInputSchema(
|
|
272
|
+
extraProperties: Record<string, unknown> = {},
|
|
273
|
+
extraRequired: string[] = [],
|
|
274
|
+
): Record<string, unknown> {
|
|
275
|
+
return {
|
|
276
|
+
type: 'object',
|
|
277
|
+
properties: {
|
|
278
|
+
request_id: { type: 'string', minLength: 8, maxLength: 120 },
|
|
279
|
+
...RECOVERY_MUTATION_IDENTITY_CONTRACT,
|
|
280
|
+
...extraProperties,
|
|
281
|
+
},
|
|
282
|
+
required: ['request_id', ...RECOVERY_MUTATION_IDENTITY_FIELDS, ...extraRequired],
|
|
283
|
+
additionalProperties: false,
|
|
284
|
+
};
|
|
285
|
+
}
|
|
286
|
+
|
|
248
287
|
export const RECOVERY_TOOLS = [
|
|
249
|
-
{ name: 'runtime_status', description: 'Read canonical Runtime ownership, readiness, endpoint, and
|
|
288
|
+
{ name: 'runtime_status', description: 'Read canonical Runtime ownership, readiness, endpoint, release observation, and exact Recovery machine identity.', inputSchema: { type: 'object', additionalProperties: false } },
|
|
250
289
|
{ name: 'list_releases', description: 'Read active, previous, and known-good whole-Runtime release evidence.', inputSchema: { type: 'object', additionalProperties: false } },
|
|
251
290
|
{ name: 'verify_stable_runtime', description: 'Run independent stable runtime verification.', inputSchema: { type: 'object', additionalProperties: false } },
|
|
252
291
|
{ name: 'verify_external_runtime', description: 'Verify the external primary MCP endpoint.', inputSchema: { type: 'object', additionalProperties: false } },
|
|
253
|
-
{ name: 'attest_known_good', description: 'Record the active release as known-good only after full independent verification succeeds.', inputSchema:
|
|
254
|
-
{ name: 'rollback_previous', description: 'While Canonical Runtime is stopped, atomically restore its attested previous whole-Runtime release and SQLite backup.', inputSchema:
|
|
255
|
-
{ name: 'restart_primary_runtime', description: 'Restart the installed canonical Forge Runtime service
|
|
256
|
-
{ name: 'restart_primary_connector', description: 'Restart the explicitly configured primary OAuth/Connector
|
|
257
|
-
{ name: 'recover_primary_runtime', description: 'Stop the canonical Runtime, restore the attested previous whole release and SQLite backup, restart it, and require verification.', inputSchema:
|
|
258
|
-
{ name: 'activate_runtime_release', description: 'Activate an already staged immutable Runtime release only if
|
|
259
|
-
{ name: 'stage_and_activate_runtime_release', description: 'Build one immutable Runtime release from the fixed Recovery-configured source root, then activate it transactionally with rollback protection. No arbitrary source path is accepted.', inputSchema:
|
|
260
|
-
{ name: '
|
|
292
|
+
{ name: 'attest_known_good', description: 'Record the active release as known-good only after full independent verification succeeds.', inputSchema: mutationInputSchema() },
|
|
293
|
+
{ name: 'rollback_previous', description: 'While Canonical Runtime is stopped, atomically restore its attested previous whole-Runtime release and SQLite backup.', inputSchema: mutationInputSchema() },
|
|
294
|
+
{ name: 'restart_primary_runtime', description: 'Restart the installed canonical Forge Runtime service only after exact Recovery machine identity matches.', inputSchema: mutationInputSchema() },
|
|
295
|
+
{ name: 'restart_primary_connector', description: 'Restart the explicitly configured primary OAuth/Connector service only after exact Recovery machine identity and local Canonical Runtime verification succeed.', inputSchema: mutationInputSchema() },
|
|
296
|
+
{ name: 'recover_primary_runtime', description: 'Stop the canonical Runtime, restore the attested previous whole-Runtime release and SQLite backup, restart it, and require verification.', inputSchema: mutationInputSchema() },
|
|
297
|
+
{ name: 'activate_runtime_release', description: 'Activate an already staged immutable Runtime release only if machine identity and caller-observed active release/authority revision are still current. Reverse activation of current.previous is rejected; use rollback_previous/recover_primary_runtime instead.', inputSchema: mutationInputSchema({ release_path: { type: 'string', minLength: 8, maxLength: 1024, description: 'Absolute path to the staged immutable Runtime release directory.' }, expected_active_release_id: { type: 'string', minLength: 1, maxLength: 256 }, expected_authority_revision: { type: 'integer', minimum: 1 } }, ['release_path', 'expected_active_release_id', 'expected_authority_revision']) },
|
|
298
|
+
{ name: 'stage_and_activate_runtime_release', description: 'Build one immutable Runtime release from the fixed Recovery-configured source root, then activate it transactionally with rollback protection. No arbitrary source path is accepted.', inputSchema: mutationInputSchema() },
|
|
299
|
+
{ name: 'migrate_controller_home', description: 'Schedule a Linux-only standalone Recovery transaction that relocates this Forge installation to the stable user-level Controller Home, reinstalls immutable Runtime/Connector/Recovery owners, verifies them, and rolls back on failure.', inputSchema: mutationInputSchema({ canonical_source_root: { type: 'string', minLength: 1, maxLength: 1024 }, expected_source_revision: { type: 'string', minLength: 7, maxLength: 80 } }, ['canonical_source_root', 'expected_source_revision']) },
|
|
300
|
+
{ name: 'restart_public_tunnel', description: 'Restart the explicitly configured public tunnel only after exact Recovery machine identity and local runtime verification succeeds and the external endpoint is unavailable.', inputSchema: mutationInputSchema() },
|
|
261
301
|
{ name: 'reconnect_primary_connector', description: 'Check canonical Runtime Gateway and primary MCP reconnection readiness without publishing a release.', inputSchema: { type: 'object', additionalProperties: false } },
|
|
262
302
|
] as const;
|
|
263
303
|
|
|
264
304
|
const RECOVERY_OAUTH_SCOPE = 'forge';
|
|
305
|
+
export const RECOVERY_VERIFIER_OAUTH_CLIENT_ID = 'forge-recovery-verifier-v1';
|
|
306
|
+
export const RECOVERY_VERIFIER_OAUTH_CLIENT_NAME = 'Forge Recovery Verification';
|
|
307
|
+
export const RECOVERY_VERIFIER_OAUTH_REDIRECT_URI = 'http://127.0.0.1/forge-recovery-oauth-callback';
|
|
308
|
+
const RECOVERY_OAUTH_CODE_TTL_MS = 10 * 60_000;
|
|
309
|
+
const RECOVERY_MAX_PENDING_OAUTH_CODES = 512;
|
|
310
|
+
const RECOVERY_MAX_PENDING_OAUTH_CODES_PER_CLIENT = 32;
|
|
265
311
|
const RECOVERY_ACCESS_TOKEN_EXPIRES_IN_SECONDS = 10 * 365 * 24 * 60 * 60;
|
|
266
312
|
const TOOL_SECURITY_SCHEMES = [{ type: 'oauth2', scopes: [RECOVERY_OAUTH_SCOPE] }] as const;
|
|
267
313
|
const SUPPORTED_TOKEN_AUTH_METHODS = ['client_secret_basic', 'client_secret_post', 'none'] as const;
|
|
@@ -281,9 +327,55 @@ type OAuthClient = {
|
|
|
281
327
|
clientId: string;
|
|
282
328
|
clientSecret?: string;
|
|
283
329
|
tokenEndpointAuthMethod: TokenAuthMethod;
|
|
330
|
+
owner: 'recovery_verifier' | 'external';
|
|
284
331
|
createdAt: number;
|
|
285
332
|
};
|
|
286
333
|
|
|
334
|
+
function stringArray(value: unknown): string[] {
|
|
335
|
+
return Array.isArray(value) ? value.filter((entry): entry is string => typeof entry === 'string') : [];
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
export function recoveryOAuthClientRegistrationIdentity(
|
|
339
|
+
body: Record<string, unknown>,
|
|
340
|
+
generatedClientId: string = randomUUID(),
|
|
341
|
+
): { clientId: string; owner: 'recovery_verifier' | 'external' } {
|
|
342
|
+
const requestedClientId = typeof body.client_id === 'string' && body.client_id.trim() ? body.client_id.trim() : '';
|
|
343
|
+
if (requestedClientId === RECOVERY_VERIFIER_OAUTH_CLIENT_ID) {
|
|
344
|
+
const validVerifierMetadata = body.client_name === RECOVERY_VERIFIER_OAUTH_CLIENT_NAME
|
|
345
|
+
&& stringArray(body.redirect_uris).includes(RECOVERY_VERIFIER_OAUTH_REDIRECT_URI)
|
|
346
|
+
&& body.token_endpoint_auth_method === 'none'
|
|
347
|
+
&& stringArray(body.grant_types).includes('authorization_code')
|
|
348
|
+
&& stringArray(body.response_types).includes('code');
|
|
349
|
+
if (!validVerifierMetadata) throw new Error('RECOVERY_OAUTH_VERIFIER_CLIENT_METADATA_INVALID');
|
|
350
|
+
return { clientId: RECOVERY_VERIFIER_OAUTH_CLIENT_ID, owner: 'recovery_verifier' };
|
|
351
|
+
}
|
|
352
|
+
return { clientId: requestedClientId || generatedClientId, owner: 'external' };
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
function reserveRecoveryOAuthCodeCapacity(
|
|
356
|
+
codes: Map<string, PendingOAuthCode>,
|
|
357
|
+
clientId: string,
|
|
358
|
+
now = Date.now(),
|
|
359
|
+
): void {
|
|
360
|
+
for (const [code, pending] of codes) {
|
|
361
|
+
if (now - pending.createdAt > RECOVERY_OAUTH_CODE_TTL_MS) codes.delete(code);
|
|
362
|
+
}
|
|
363
|
+
const evictOldest = (predicate: (pending: PendingOAuthCode) => boolean): boolean => {
|
|
364
|
+
let oldest: { code: string; createdAt: number } | undefined;
|
|
365
|
+
for (const [code, pending] of codes) {
|
|
366
|
+
if (!predicate(pending)) continue;
|
|
367
|
+
if (!oldest || pending.createdAt < oldest.createdAt) oldest = { code, createdAt: pending.createdAt };
|
|
368
|
+
}
|
|
369
|
+
return oldest ? codes.delete(oldest.code) : false;
|
|
370
|
+
};
|
|
371
|
+
while ([...codes.values()].filter((pending) => pending.clientId === clientId).length >= RECOVERY_MAX_PENDING_OAUTH_CODES_PER_CLIENT) {
|
|
372
|
+
if (!evictOldest((pending) => pending.clientId === clientId)) break;
|
|
373
|
+
}
|
|
374
|
+
while (codes.size >= RECOVERY_MAX_PENDING_OAUTH_CODES) {
|
|
375
|
+
if (!evictOldest(() => true)) break;
|
|
376
|
+
}
|
|
377
|
+
}
|
|
378
|
+
|
|
287
379
|
function readBody(request: IncomingMessage): Promise<string> {
|
|
288
380
|
return new Promise((resolveBody, reject) => {
|
|
289
381
|
let body = '';
|
|
@@ -365,7 +457,7 @@ export function classifyRecoveryMcpRequest(
|
|
|
365
457
|
if (!matchesAnyPath(request.url, ['/mcp', '/recovery/mcp'])) return 'not_mcp';
|
|
366
458
|
const supplied = request.headers.authorization?.replace(/^Bearer\s+/i, '').trim();
|
|
367
459
|
if (!expectedToken || !supplied || !secureEqual(supplied, expectedToken)) return 'auth_required';
|
|
368
|
-
if (request.method !== 'POST') return 'method_not_supported';
|
|
460
|
+
if (request.method !== 'POST' && request.method !== 'GET' && request.method !== 'DELETE') return 'method_not_supported';
|
|
369
461
|
return 'mcp';
|
|
370
462
|
}
|
|
371
463
|
|
|
@@ -485,6 +577,17 @@ function requestId(value: unknown): string | undefined {
|
|
|
485
577
|
return typeof value === 'string' && /^[A-Za-z0-9._:-]{8,120}$/.test(value) ? value : undefined;
|
|
486
578
|
}
|
|
487
579
|
|
|
580
|
+
function mutationResponse(config: RecoveryConfig, payload: unknown): Record<string, unknown> {
|
|
581
|
+
const result = payload && typeof payload === 'object' && !Array.isArray(payload) ? payload as Record<string, unknown> : { result: payload };
|
|
582
|
+
return { ...result, identity: recoveryMachineIdentity(config) };
|
|
583
|
+
}
|
|
584
|
+
|
|
585
|
+
function assertRecoveryGatewayMutationIdentity(config: RecoveryConfig, args: Record<string, unknown>): RecoveryMachineIdentity {
|
|
586
|
+
const suppliedFields = RECOVERY_MUTATION_IDENTITY_FIELDS.filter((field) => typeof args[field] === 'string' && String(args[field]).trim());
|
|
587
|
+
if (suppliedFields.length === 0) return recoveryMachineIdentity(config);
|
|
588
|
+
return assertRecoveryMutationIdentity(config, args);
|
|
589
|
+
}
|
|
590
|
+
|
|
488
591
|
export async function dispatchRecoveryTool(config: RecoveryConfig, name: string, args: Record<string, unknown>): Promise<unknown> {
|
|
489
592
|
switch (name) {
|
|
490
593
|
case 'runtime_status': return runtimeStatus(config);
|
|
@@ -496,44 +599,64 @@ export async function dispatchRecoveryTool(config: RecoveryConfig, name: string,
|
|
|
496
599
|
}
|
|
497
600
|
case 'attest_known_good': {
|
|
498
601
|
if (!requestId(args.request_id)) throw new Error('RECOVERY_REQUEST_ID_REQUIRED');
|
|
499
|
-
|
|
602
|
+
assertRecoveryGatewayMutationIdentity(config, args);
|
|
603
|
+
return mutationResponse(config, await attestKnownGood(config));
|
|
500
604
|
}
|
|
501
605
|
case 'rollback_previous': {
|
|
502
606
|
if (!requestId(args.request_id)) throw new Error('RECOVERY_REQUEST_ID_REQUIRED');
|
|
503
|
-
|
|
607
|
+
assertRecoveryGatewayMutationIdentity(config, args);
|
|
608
|
+
return mutationResponse(config, await rollbackPrevious(config, `recovery-gateway:${args.request_id}`));
|
|
504
609
|
}
|
|
505
610
|
case 'restart_primary_runtime': {
|
|
506
611
|
if (!requestId(args.request_id)) throw new Error('RECOVERY_REQUEST_ID_REQUIRED');
|
|
507
|
-
|
|
612
|
+
assertRecoveryGatewayMutationIdentity(config, args);
|
|
613
|
+
return mutationResponse(config, await restartPrimaryRuntime(config));
|
|
508
614
|
}
|
|
509
615
|
case 'restart_primary_connector': {
|
|
510
616
|
if (!requestId(args.request_id)) throw new Error('RECOVERY_REQUEST_ID_REQUIRED');
|
|
511
|
-
|
|
617
|
+
assertRecoveryGatewayMutationIdentity(config, args);
|
|
618
|
+
return mutationResponse(config, await restartPrimaryConnector(config, { requestId: `recovery-gateway:${args.request_id}` }));
|
|
512
619
|
}
|
|
513
620
|
case 'recover_primary_runtime': {
|
|
514
621
|
if (!requestId(args.request_id)) throw new Error('RECOVERY_REQUEST_ID_REQUIRED');
|
|
515
|
-
|
|
622
|
+
assertRecoveryGatewayMutationIdentity(config, args);
|
|
623
|
+
return mutationResponse(config, await recoverPrimaryRuntime(config, `recovery-gateway:${args.request_id}`));
|
|
516
624
|
}
|
|
517
625
|
case 'activate_runtime_release': {
|
|
518
626
|
if (!requestId(args.request_id)) throw new Error('RECOVERY_REQUEST_ID_REQUIRED');
|
|
627
|
+
assertRecoveryGatewayMutationIdentity(config, args);
|
|
519
628
|
if (typeof args.release_path !== 'string' || !args.release_path.trim()) throw new Error('RECOVERY_RELEASE_PATH_REQUIRED');
|
|
520
629
|
if (typeof args.expected_active_release_id !== 'string' || !args.expected_active_release_id.trim()) throw new Error('RECOVERY_EXPECTED_ACTIVE_RELEASE_REQUIRED');
|
|
521
630
|
if (!Number.isInteger(args.expected_authority_revision) || Number(args.expected_authority_revision) < 1) throw new Error('RECOVERY_EXPECTED_AUTHORITY_REVISION_REQUIRED');
|
|
522
631
|
const releasePath = args.release_path.trim();
|
|
523
632
|
const manifestPath = basename(releasePath) === 'manifest.json' ? releasePath : join(releasePath, 'manifest.json');
|
|
524
|
-
return activateRuntimeRelease(config, manifestPath, {}, {
|
|
633
|
+
return mutationResponse(config, await activateRuntimeRelease(config, manifestPath, {}, {
|
|
525
634
|
requestId: `recovery-gateway:${args.request_id}`,
|
|
526
635
|
expectedActiveReleaseId: args.expected_active_release_id.trim(),
|
|
527
636
|
expectedAuthorityRevision: Number(args.expected_authority_revision),
|
|
528
|
-
});
|
|
637
|
+
}));
|
|
529
638
|
}
|
|
530
639
|
case 'stage_and_activate_runtime_release': {
|
|
531
640
|
if (!requestId(args.request_id)) throw new Error('RECOVERY_REQUEST_ID_REQUIRED');
|
|
532
|
-
|
|
641
|
+
assertRecoveryGatewayMutationIdentity(config, args);
|
|
642
|
+
return mutationResponse(config, await stageAndActivateConfiguredRuntimeRelease(config, {}, `recovery-gateway:${args.request_id}`));
|
|
643
|
+
}
|
|
644
|
+
case 'migrate_controller_home': {
|
|
645
|
+
const migrationRequestId = requestId(args.request_id);
|
|
646
|
+
if (!migrationRequestId) throw new Error('RECOVERY_REQUEST_ID_REQUIRED');
|
|
647
|
+
assertRecoveryGatewayMutationIdentity(config, args);
|
|
648
|
+
if (typeof args.canonical_source_root !== 'string' || !args.canonical_source_root.trim()) throw new Error('RECOVERY_CONTROLLER_HOME_MIGRATION_SOURCE_REQUIRED');
|
|
649
|
+
if (typeof args.expected_source_revision !== 'string' || !args.expected_source_revision.trim()) throw new Error('RECOVERY_CONTROLLER_HOME_MIGRATION_SOURCE_REVISION_REQUIRED');
|
|
650
|
+
return scheduleRecoveryControllerHomeMigration(config, {
|
|
651
|
+
requestId: migrationRequestId,
|
|
652
|
+
canonicalSourceRoot: args.canonical_source_root.trim(),
|
|
653
|
+
expectedSourceRevision: args.expected_source_revision.trim(),
|
|
654
|
+
});
|
|
533
655
|
}
|
|
534
656
|
case 'restart_public_tunnel': {
|
|
535
657
|
if (!requestId(args.request_id)) throw new Error('RECOVERY_REQUEST_ID_REQUIRED');
|
|
536
|
-
|
|
658
|
+
assertRecoveryGatewayMutationIdentity(config, args);
|
|
659
|
+
return mutationResponse(config, await repairPublicTunnel(config));
|
|
537
660
|
}
|
|
538
661
|
case 'reconnect_primary_connector': return reconnectMain(config);
|
|
539
662
|
default: throw new Error('RECOVERY_TOOL_NOT_FOUND');
|
|
@@ -550,6 +673,24 @@ async function startGateway(config: RecoveryConfig): Promise<void> {
|
|
|
550
673
|
const recentMutations = new Map<string, number[]>();
|
|
551
674
|
const oauthCodes = new Map<string, PendingOAuthCode>();
|
|
552
675
|
const oauthClients = new Map<string, OAuthClient>();
|
|
676
|
+
const recoveryTools = RECOVERY_TOOLS.map((tool) => ({
|
|
677
|
+
...tool,
|
|
678
|
+
securitySchemes: TOOL_SECURITY_SCHEMES,
|
|
679
|
+
_meta: { securitySchemes: TOOL_SECURITY_SCHEMES },
|
|
680
|
+
})) as unknown as Tool[];
|
|
681
|
+
const recoveryMcp = new RecoveryMcpSessionServer({
|
|
682
|
+
tools: recoveryTools,
|
|
683
|
+
dispatchTool: async (name, args, context) => {
|
|
684
|
+
if (name === 'attest_known_good' || name === 'rollback_previous' || name === 'restart_primary_runtime' || name === 'restart_primary_connector' || name === 'recover_primary_runtime' || name === 'activate_runtime_release' || name === 'stage_and_activate_runtime_release' || name === 'migrate_controller_home' || name === 'restart_public_tunnel') {
|
|
685
|
+
const now = Date.now();
|
|
686
|
+
const window = (recentMutations.get(context.remoteAddress) ?? []).filter((at) => now - at < 60_000);
|
|
687
|
+
if (window.length >= 3) throw new Error('Recovery mutation rate limit exceeded.');
|
|
688
|
+
window.push(now);
|
|
689
|
+
recentMutations.set(context.remoteAddress, window);
|
|
690
|
+
}
|
|
691
|
+
return await dispatchRecoveryTool(config, name, args);
|
|
692
|
+
},
|
|
693
|
+
});
|
|
553
694
|
const server = createServer(async (request, response) => {
|
|
554
695
|
if (request.method === 'OPTIONS') {
|
|
555
696
|
response.statusCode = 204;
|
|
@@ -597,6 +738,7 @@ async function startGateway(config: RecoveryConfig): Promise<void> {
|
|
|
597
738
|
html(response, 401, renderAuthorizeForm(params, 'Invalid passphrase.'));
|
|
598
739
|
return;
|
|
599
740
|
}
|
|
741
|
+
reserveRecoveryOAuthCodeCapacity(oauthCodes, clientId);
|
|
600
742
|
const code = randomUUID();
|
|
601
743
|
oauthCodes.set(code, {
|
|
602
744
|
clientId,
|
|
@@ -622,16 +764,26 @@ async function startGateway(config: RecoveryConfig): Promise<void> {
|
|
|
622
764
|
const raw = await readBody(request);
|
|
623
765
|
body = raw.trim() ? JSON.parse(raw) as Record<string, unknown> : {};
|
|
624
766
|
} catch { /* tolerate minimal DCR clients */ }
|
|
625
|
-
|
|
767
|
+
let registrationIdentity: ReturnType<typeof recoveryOAuthClientRegistrationIdentity>;
|
|
768
|
+
try {
|
|
769
|
+
registrationIdentity = recoveryOAuthClientRegistrationIdentity(body);
|
|
770
|
+
} catch (error) {
|
|
771
|
+
auditGateway({ oauth: 'register', outcome: 'invalid_client_metadata' });
|
|
772
|
+
json(response, 400, { error: 'invalid_client_metadata', error_description: error instanceof Error ? error.message : String(error) });
|
|
773
|
+
return;
|
|
774
|
+
}
|
|
775
|
+
const { clientId, owner } = registrationIdentity;
|
|
776
|
+
const reused = oauthClients.has(clientId);
|
|
626
777
|
const clientSecret = randomUUID();
|
|
627
778
|
const authMethod = tokenAuthMethod(body.token_endpoint_auth_method);
|
|
628
779
|
oauthClients.set(clientId, {
|
|
629
780
|
clientId,
|
|
630
781
|
clientSecret: authMethod === 'none' ? undefined : clientSecret,
|
|
631
782
|
tokenEndpointAuthMethod: authMethod,
|
|
783
|
+
owner,
|
|
632
784
|
createdAt: Date.now(),
|
|
633
785
|
});
|
|
634
|
-
auditGateway({ oauth: 'register', outcome: 'created', token_endpoint_auth_method: authMethod });
|
|
786
|
+
auditGateway({ oauth: 'register', outcome: reused ? 'reused' : 'created', client_owner: owner, token_endpoint_auth_method: authMethod });
|
|
635
787
|
json(response, 201, {
|
|
636
788
|
...body,
|
|
637
789
|
client_id: clientId,
|
|
@@ -641,6 +793,7 @@ async function startGateway(config: RecoveryConfig): Promise<void> {
|
|
|
641
793
|
token_endpoint_auth_method: authMethod,
|
|
642
794
|
grant_types: ['authorization_code'],
|
|
643
795
|
response_types: ['code'],
|
|
796
|
+
...(owner === 'recovery_verifier' ? { forge_client_owner: owner, forge_client_reused: reused } : {}),
|
|
644
797
|
});
|
|
645
798
|
return;
|
|
646
799
|
}
|
|
@@ -665,7 +818,7 @@ async function startGateway(config: RecoveryConfig): Promise<void> {
|
|
|
665
818
|
const redirectUri = params.get('redirect_uri') ?? '';
|
|
666
819
|
const verifier = params.get('code_verifier') ?? '';
|
|
667
820
|
const pending = code ? oauthCodes.get(code) : undefined;
|
|
668
|
-
if (!code || !pending || pending.clientId !== clientId || pending.redirectUri !== redirectUri || Date.now() - pending.createdAt >
|
|
821
|
+
if (!code || !pending || pending.clientId !== clientId || pending.redirectUri !== redirectUri || Date.now() - pending.createdAt > RECOVERY_OAUTH_CODE_TTL_MS) {
|
|
669
822
|
auditGateway({ oauth: 'token', outcome: 'invalid_grant' });
|
|
670
823
|
json(response, 400, { error: 'invalid_grant' });
|
|
671
824
|
return;
|
|
@@ -701,34 +854,12 @@ async function startGateway(config: RecoveryConfig): Promise<void> {
|
|
|
701
854
|
const mcpRequest = classifyRecoveryMcpRequest(request, gatewayToken(config));
|
|
702
855
|
if (mcpRequest === 'not_mcp' || mcpRequest === 'method_not_supported') { json(response, 404, { error: 'NOT_FOUND' }); return; }
|
|
703
856
|
if (mcpRequest === 'auth_required') { response.setHeader('www-authenticate', recoveryWwwAuthenticate(request, config)); json(response, 401, recoveryUnauthorizedBody()); return; }
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
if (message.method === 'initialize') { json(response, 200, { jsonrpc: '2.0', id, result: { protocolVersion: '2025-06-18', capabilities: { tools: {} }, serverInfo: { name: 'forge-standalone-recovery', version: FORGE_VERSION } } }); return; }
|
|
709
|
-
if (message.method === 'notifications/initialized') { response.statusCode = 202; response.end(); return; }
|
|
710
|
-
if (message.method === 'tools/list') {
|
|
711
|
-
const tools = RECOVERY_TOOLS.map((tool) => ({
|
|
712
|
-
...tool,
|
|
713
|
-
securitySchemes: TOOL_SECURITY_SCHEMES,
|
|
714
|
-
_meta: { securitySchemes: TOOL_SECURITY_SCHEMES },
|
|
715
|
-
}));
|
|
716
|
-
json(response, 200, { jsonrpc: '2.0', id, result: { tools } });
|
|
717
|
-
return;
|
|
857
|
+
let body: unknown;
|
|
858
|
+
if (request.method === 'POST') {
|
|
859
|
+
if (!/^application\/json(?:\s*;|$)/i.test(String(request.headers['content-type'] ?? ''))) { json(response, 415, { error: 'RECOVERY_CONTENT_TYPE_REQUIRED' }); return; }
|
|
860
|
+
try { body = JSON.parse(await readBody(request)); } catch { json(response, 400, rpcError(null, -32700, 'Invalid JSON.')); return; }
|
|
718
861
|
}
|
|
719
|
-
|
|
720
|
-
const name = message.params.name;
|
|
721
|
-
const args = message.params.arguments && typeof message.params.arguments === 'object' && !Array.isArray(message.params.arguments) ? message.params.arguments as Record<string, unknown> : {};
|
|
722
|
-
if (name === 'attest_known_good' || name === 'rollback_previous' || name === 'restart_primary_runtime' || name === 'restart_primary_connector' || name === 'recover_primary_runtime' || name === 'activate_runtime_release' || name === 'stage_and_activate_runtime_release' || name === 'restart_public_tunnel') {
|
|
723
|
-
const address = request.socket.remoteAddress ?? 'unknown'; const now = Date.now();
|
|
724
|
-
const window = (recentMutations.get(address) ?? []).filter((at) => now - at < 60_000);
|
|
725
|
-
if (window.length >= 3) { json(response, 429, rpcError(id, -32029, 'Recovery mutation rate limit exceeded.')); return; }
|
|
726
|
-
window.push(now); recentMutations.set(address, window);
|
|
727
|
-
}
|
|
728
|
-
try {
|
|
729
|
-
const payload = await dispatchRecoveryTool(config, name, args);
|
|
730
|
-
json(response, 200, { jsonrpc: '2.0', id, result: { content: [{ type: 'text', text: JSON.stringify(payload) }], structuredContent: payload } });
|
|
731
|
-
} catch (error) { json(response, 200, rpcError(id, -32602, error instanceof Error ? error.message : 'Recovery request rejected')); }
|
|
862
|
+
await recoveryMcp.handle(request, response, body);
|
|
732
863
|
});
|
|
733
864
|
await new Promise<void>((resolveListen, reject) => { server.once('error', reject); server.listen(gateway.port, gateway.host, () => resolveListen()); });
|
|
734
865
|
runtimeIdentity = writeRecoveryRuntimeIdentity(config.controllerHome, 'gateway');
|
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
import { createHash } from 'crypto';
|
|
2
|
+
import { isAbsolute, resolve } from 'path';
|
|
3
|
+
|
|
4
|
+
export const INDEPENDENT_HOST_RESCUE_ACTIONS = [
|
|
5
|
+
'host_status',
|
|
6
|
+
'wsl_status', 'wsl_start',
|
|
7
|
+
'forge_source_status', 'controller_status',
|
|
8
|
+
'runtime_status', 'runtime_start', 'runtime_restart',
|
|
9
|
+
'connector_status', 'connector_start', 'connector_restart',
|
|
10
|
+
'recovery_status', 'recovery_start', 'recovery_restart',
|
|
11
|
+
'tunnel_status', 'tunnel_start', 'tunnel_restart',
|
|
12
|
+
'forge_cloud_verify', 'full_recover',
|
|
13
|
+
] as const;
|
|
14
|
+
|
|
15
|
+
export interface IndependentHostRescueConfig {
|
|
16
|
+
schemaVersion: 1;
|
|
17
|
+
wslDistro: string;
|
|
18
|
+
controllerHome: string;
|
|
19
|
+
sourceRoot: string;
|
|
20
|
+
rescueRoot: string;
|
|
21
|
+
runtimeUnit: string;
|
|
22
|
+
connectorUnit: string;
|
|
23
|
+
recoveryUnit: string;
|
|
24
|
+
tunnelClient: string;
|
|
25
|
+
tunnelAlias: string;
|
|
26
|
+
tunnelId: string;
|
|
27
|
+
tunnelRuntimeApiKeyRef: string;
|
|
28
|
+
tunnelProfile: string;
|
|
29
|
+
tunnelProfileDir: string;
|
|
30
|
+
tunnelAdminProfile: string;
|
|
31
|
+
localMcpUrl: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function required(value: string | undefined, code: string): string {
|
|
35
|
+
const normalized = value?.trim();
|
|
36
|
+
if (!normalized) throw new Error(code);
|
|
37
|
+
return normalized;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function safeToken(value: string, code: string): string {
|
|
41
|
+
if (/[\r\n'"`$;&|<>\\]/.test(value)) throw new Error(code);
|
|
42
|
+
return value;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function safeAbsolutePath(value: string | undefined, code: string): string {
|
|
46
|
+
const path = required(value, code);
|
|
47
|
+
if (!isAbsolute(path) || /[\r\n'"`$;&|<>]/.test(path)) throw new Error(code);
|
|
48
|
+
return resolve(path);
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function controllerServiceUnit(prefix: 'runtime' | 'connector', controllerHome: string): string {
|
|
52
|
+
const suffix = createHash('sha256').update(resolve(controllerHome)).digest('hex').slice(0, 12);
|
|
53
|
+
return prefix === 'runtime'
|
|
54
|
+
? `com.moretea.forge.runtime.${suffix}.service`
|
|
55
|
+
: `com.moretea.forge.mcp-gateway.${suffix}.service`;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function createIndependentHostRescueConfig(input: {
|
|
59
|
+
wslDistro: string;
|
|
60
|
+
controllerHome: string;
|
|
61
|
+
sourceRoot: string;
|
|
62
|
+
rescueRoot: string;
|
|
63
|
+
tunnelClient: string;
|
|
64
|
+
tunnelAlias: string;
|
|
65
|
+
tunnelId: string;
|
|
66
|
+
tunnelRuntimeApiKeyRef: string;
|
|
67
|
+
tunnelProfile?: string;
|
|
68
|
+
tunnelProfileDir: string;
|
|
69
|
+
tunnelAdminProfile?: string;
|
|
70
|
+
localMcpUrl?: string;
|
|
71
|
+
}): IndependentHostRescueConfig {
|
|
72
|
+
const wslDistro = safeToken(required(input.wslDistro, 'HOST_RESCUE_WSL_DISTRO_REQUIRED'), 'HOST_RESCUE_WSL_DISTRO_INVALID');
|
|
73
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,127}$/.test(wslDistro)) throw new Error('HOST_RESCUE_WSL_DISTRO_INVALID');
|
|
74
|
+
const controllerHome = safeAbsolutePath(input.controllerHome, 'HOST_RESCUE_CONTROLLER_HOME_INVALID');
|
|
75
|
+
if (!/\/\.forge\/controller$/.test(controllerHome)) throw new Error('HOST_RESCUE_CONTROLLER_HOME_CANONICAL_REQUIRED');
|
|
76
|
+
const sourceRoot = safeAbsolutePath(input.sourceRoot, 'HOST_RESCUE_SOURCE_ROOT_INVALID');
|
|
77
|
+
const rescueRoot = safeAbsolutePath(input.rescueRoot, 'HOST_RESCUE_ROOT_INVALID');
|
|
78
|
+
if (!/\/\.forge-recovery$/.test(rescueRoot)) throw new Error('HOST_RESCUE_ROOT_CANONICAL_REQUIRED');
|
|
79
|
+
const tunnelClient = safeAbsolutePath(input.tunnelClient, 'HOST_RESCUE_TUNNEL_CLIENT_INVALID');
|
|
80
|
+
const tunnelAlias = safeToken(required(input.tunnelAlias, 'HOST_RESCUE_TUNNEL_ALIAS_REQUIRED'), 'HOST_RESCUE_TUNNEL_ALIAS_INVALID');
|
|
81
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._-]{0,79}$/.test(tunnelAlias)) throw new Error('HOST_RESCUE_TUNNEL_ALIAS_INVALID');
|
|
82
|
+
const tunnelId = safeToken(required(input.tunnelId, 'HOST_RESCUE_TUNNEL_ID_REQUIRED'), 'HOST_RESCUE_TUNNEL_ID_INVALID');
|
|
83
|
+
if (!/^tunnel_[0-9a-f]{32}$/.test(tunnelId)) throw new Error('HOST_RESCUE_TUNNEL_ID_INVALID');
|
|
84
|
+
const tunnelRuntimeApiKeyRef = safeToken(required(input.tunnelRuntimeApiKeyRef, 'HOST_RESCUE_TUNNEL_RUNTIME_KEY_REF_REQUIRED'), 'HOST_RESCUE_TUNNEL_RUNTIME_KEY_REF_INVALID');
|
|
85
|
+
if (!tunnelRuntimeApiKeyRef.startsWith(`file:${rescueRoot}/secrets/`)) throw new Error('HOST_RESCUE_TUNNEL_RUNTIME_KEY_REF_EXTERNAL_REQUIRED');
|
|
86
|
+
const tunnelProfile = safeToken(input.tunnelProfile?.trim() || tunnelAlias, 'HOST_RESCUE_TUNNEL_PROFILE_INVALID');
|
|
87
|
+
const tunnelProfileDir = safeAbsolutePath(input.tunnelProfileDir, 'HOST_RESCUE_TUNNEL_PROFILE_DIR_INVALID');
|
|
88
|
+
const tunnelAdminProfile = safeToken(input.tunnelAdminProfile?.trim() || 'default', 'HOST_RESCUE_TUNNEL_ADMIN_PROFILE_INVALID');
|
|
89
|
+
const localMcpUrl = input.localMcpUrl?.trim() || 'http://127.0.0.1:8767/mcp';
|
|
90
|
+
if (!/^http:\/\/127\.0\.0\.1:\d{1,5}\/mcp$/.test(localMcpUrl)) throw new Error('HOST_RESCUE_LOCAL_MCP_URL_INVALID');
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
schemaVersion: 1,
|
|
94
|
+
wslDistro,
|
|
95
|
+
controllerHome,
|
|
96
|
+
sourceRoot,
|
|
97
|
+
rescueRoot,
|
|
98
|
+
runtimeUnit: controllerServiceUnit('runtime', controllerHome),
|
|
99
|
+
connectorUnit: controllerServiceUnit('connector', controllerHome),
|
|
100
|
+
recoveryUnit: 'com.moretea.forge.independent-recovery.service',
|
|
101
|
+
tunnelClient,
|
|
102
|
+
tunnelAlias,
|
|
103
|
+
tunnelId,
|
|
104
|
+
tunnelRuntimeApiKeyRef,
|
|
105
|
+
tunnelProfile,
|
|
106
|
+
tunnelProfileDir,
|
|
107
|
+
tunnelAdminProfile,
|
|
108
|
+
localMcpUrl,
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
function shellQuote(value: string): string {
|
|
113
|
+
return `'${value.replaceAll("'", "'\\''")}'`;
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
/** This config contains identities and paths only; tunnel credentials remain in tunnel-client's own profile store. */
|
|
117
|
+
export function renderIndependentHostRescueEnv(config: IndependentHostRescueConfig): string {
|
|
118
|
+
const values: Record<string, string> = {
|
|
119
|
+
WSL_DISTRO: config.wslDistro,
|
|
120
|
+
CONTROLLER_HOME: config.controllerHome,
|
|
121
|
+
SOURCE_ROOT: config.sourceRoot,
|
|
122
|
+
RUNTIME_UNIT: config.runtimeUnit,
|
|
123
|
+
CONNECTOR_UNIT: config.connectorUnit,
|
|
124
|
+
RECOVERY_UNIT: config.recoveryUnit,
|
|
125
|
+
TUNNEL_CLIENT: config.tunnelClient,
|
|
126
|
+
TUNNEL_ALIAS: config.tunnelAlias,
|
|
127
|
+
TUNNEL_ID: config.tunnelId,
|
|
128
|
+
TUNNEL_RUNTIME_API_KEY_REF: config.tunnelRuntimeApiKeyRef,
|
|
129
|
+
TUNNEL_PROFILE: config.tunnelProfile,
|
|
130
|
+
TUNNEL_PROFILE_DIR: config.tunnelProfileDir,
|
|
131
|
+
TUNNEL_ADMIN_PROFILE: config.tunnelAdminProfile,
|
|
132
|
+
MCP_LOCAL_URL: config.localMcpUrl,
|
|
133
|
+
};
|
|
134
|
+
return `${Object.entries(values).map(([key, value]) => `${key}=${shellQuote(value)}`).join('\n')}\n`;
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
export function renderIndependentHostRescueSystemdUnit(config: IndependentHostRescueConfig): string {
|
|
138
|
+
const executable = `${config.rescueRoot}/bin/forge-wsl-rescue`;
|
|
139
|
+
return [
|
|
140
|
+
'[Unit]',
|
|
141
|
+
'Description=Forge independent Windows/WSL host rescue',
|
|
142
|
+
'After=network-online.target',
|
|
143
|
+
'',
|
|
144
|
+
'[Service]',
|
|
145
|
+
'Type=simple',
|
|
146
|
+
`Environment="FORGE_RESCUE_ROOT=${config.rescueRoot}"`,
|
|
147
|
+
`ExecStart=${executable} watch`,
|
|
148
|
+
// tunnel-client deliberately owns its managed tmux process. It must not
|
|
149
|
+
// keep this observer unit in deactivating state or be killed solely
|
|
150
|
+
// because the rescue watchdog itself is restarted.
|
|
151
|
+
'KillMode=process',
|
|
152
|
+
'Restart=always',
|
|
153
|
+
'RestartSec=10',
|
|
154
|
+
'',
|
|
155
|
+
'[Install]',
|
|
156
|
+
'WantedBy=default.target',
|
|
157
|
+
'',
|
|
158
|
+
].join('\n');
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
export function renderWindowsHostRescueConfig(config: IndependentHostRescueConfig): string {
|
|
162
|
+
return `${JSON.stringify({
|
|
163
|
+
schemaVersion: config.schemaVersion,
|
|
164
|
+
distro: config.wslDistro,
|
|
165
|
+
wslRescuePath: `${config.rescueRoot}/bin/forge-wsl-rescue`,
|
|
166
|
+
controllerHome: config.controllerHome,
|
|
167
|
+
runtimeUnit: config.runtimeUnit,
|
|
168
|
+
connectorUnit: config.connectorUnit,
|
|
169
|
+
recoveryUnit: config.recoveryUnit,
|
|
170
|
+
tunnel: { alias: config.tunnelAlias, id: config.tunnelId, localMcpUrl: config.localMcpUrl },
|
|
171
|
+
}, null, 2)}\n`;
|
|
172
|
+
}
|