@moretea-labs/forge 1.8.0 → 1.8.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +70 -7
- package/CHANGELOG.md +14 -0
- package/CLAUDE.md +17 -2
- package/CONTRIBUTING.md +3 -2
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/adapters/chatgpt/browser-delivery-host.ts +7 -1
- package/adapters/chatgpt/browser-delivery-runtime.ts +529 -56
- package/adapters/chatgpt/controller-binding-store.ts +101 -22
- package/adapters/chatgpt/controller-round-host.ts +15 -25
- package/adapters/chatgpt/controller-round-settlement-store.ts +58 -4
- package/adapters/chatgpt/provider-delivery.ts +228 -1
- package/adapters/chatgpt/work-conversation-binding-store.ts +79 -53
- package/adapters/computer/chatgpt-extension-target.ts +463 -0
- package/adapters/computer/chatgpt-target-router.ts +103 -0
- package/adapters/computer/chrome-extension/background.js +385 -0
- package/adapters/computer/chrome-extension/content.js +378 -0
- package/adapters/computer/chrome-extension/core.js +24 -0
- package/adapters/computer/chrome-extension/manifest.json +30 -0
- package/adapters/computer/desktop-operator-discovery.ts +11 -31
- package/adapters/computer/interaction-target-authority.ts +142 -18
- package/adapters/mcp/auth.ts +0 -1
- package/adapters/mcp/instructions.ts +5 -4
- package/adapters/mcp/multi-repository.ts +3 -19
- package/adapters/mcp/oauth.ts +73 -0
- package/adapters/mcp/paths.ts +4 -1
- package/adapters/mcp/runtime-gateway/automation-receipt-adapter.ts +87 -0
- package/adapters/mcp/runtime-gateway/bounded-result-runtime.ts +62 -0
- package/adapters/mcp/runtime-gateway/cognition-settlement.ts +201 -0
- package/adapters/mcp/runtime-gateway/context-adapter.ts +411 -73
- package/adapters/mcp/runtime-gateway/context-pack-sidecar.ts +91 -0
- package/adapters/mcp/runtime-gateway/controller-authority-adapter.ts +20 -5
- package/adapters/mcp/runtime-gateway/core-capability-adapter.ts +258 -0
- package/adapters/mcp/runtime-gateway/engineering-preconditions.ts +10 -27
- package/adapters/mcp/runtime-gateway/engineering-tool-contract.ts +15 -0
- package/adapters/mcp/runtime-gateway/execution-tools.ts +8 -42
- package/adapters/mcp/runtime-gateway/gateway-execution-adapter.ts +2 -1
- package/adapters/mcp/runtime-gateway/plugin-adapter.ts +117 -45
- package/adapters/mcp/runtime-gateway/process-tools.ts +190 -22
- package/adapters/mcp/runtime-gateway/protected-computer-adapter.ts +16 -4
- package/adapters/mcp/runtime-gateway/recovery-adapter.ts +69 -52
- package/adapters/mcp/runtime-gateway/repository-compat-adapter.ts +4 -35
- package/adapters/mcp/runtime-gateway/result-adapter.ts +10 -4
- package/adapters/mcp/runtime-gateway/router.ts +18 -504
- package/adapters/mcp/runtime-gateway/routing-policy.ts +499 -0
- package/adapters/mcp/runtime-gateway/runtime-observation-adapter.ts +14 -65
- package/adapters/mcp/runtime-gateway/runtime-readiness-observation.ts +218 -0
- package/adapters/mcp/runtime-gateway/runtime-tool-definitions.ts +113 -173
- package/adapters/mcp/runtime-gateway/runtime-tools.ts +15 -5
- package/adapters/mcp/runtime-gateway/scheduler-adapter.ts +1 -0
- package/adapters/mcp/runtime-gateway/shared-adapter.ts +20 -0
- package/adapters/mcp/runtime-gateway/status-inbox-adapter.ts +358 -266
- package/adapters/mcp/runtime-gateway/typescript-navigation-sidecar.ts +136 -0
- package/adapters/mcp/runtime-gateway/work-adapter.ts +261 -3122
- package/adapters/mcp/runtime-gateway/work-controller-operations.ts +257 -0
- package/adapters/mcp/runtime-gateway/work-detail-projection.ts +60 -0
- package/adapters/mcp/runtime-gateway/work-input-wire-migration.ts +246 -0
- package/adapters/mcp/runtime-gateway/work-learning-operations.ts +145 -0
- package/adapters/mcp/runtime-gateway/work-list-projection.ts +38 -0
- package/adapters/mcp/runtime-gateway/work-plan-operations.ts +382 -0
- package/adapters/mcp/runtime-gateway/work-repair-adapter.ts +308 -0
- package/adapters/mcp/runtime-gateway/work-requirement-operations.ts +148 -0
- package/adapters/mcp/runtime-gateway/work-semantic-operations.ts +263 -0
- package/adapters/mcp/runtime-gateway/work-workflow-operations.ts +113 -0
- package/adapters/mcp/runtime-gateway/workflow-supervisor-adapter.ts +156 -0
- package/adapters/mcp/server.ts +67 -23
- package/adapters/mcp/setup.ts +1 -3
- package/adapters/mcp/tool-mapping/legacy-tool-service.ts +44 -170
- package/adapters/mcp/tool-mapping/post-finalize-work-attribution.ts +4 -9
- package/adapters/mcp/tool-mapping/repository-tools.ts +133 -142
- package/adapters/mcp/toolset-names.ts +5 -0
- package/adapters/mcp/toolset.ts +6 -5
- package/adapters/mcp/transports/http-observation.ts +365 -0
- package/adapters/mcp/transports/http.ts +154 -677
- package/adapters/mcp/transports/oauth-http.ts +322 -0
- package/adapters/mcp/transports/session-registry.ts +6 -0
- package/adapters/mcp/tunnels/openai-secure-tunnel.ts +89 -5
- package/assets/plugin-registry.v1.json +6 -4
- package/assets/reference-configs/agentic-development-flow.md +25 -0
- package/assets/reference-configs/global-working-rules.md +19 -0
- package/assets/skill-version.json +2 -2
- package/docs/architecture/CURRENT.md +111 -41
- package/docs/architecture/decisions/20260803-release-identity-binding-and-exit-policy.md +14 -5
- package/docs/architecture/decisions/20260823-immediate-controller-relay.md +6 -3
- package/docs/architecture/decisions/20260907-assistant-learning-loop.md +2 -0
- package/docs/architecture/decisions/20260907-controller-round-transition-policy.md +19 -4
- package/docs/architecture/decisions/20260916-workflow-supervisor-authority.md +249 -0
- package/docs/architecture/decisions/20260917-cognitive-memory-foundation.md +82 -0
- package/docs/architecture/decisions/20260918-stable-candidate-release-lifecycle.md +221 -0
- package/docs/architecture/decisions/20260919-autonomous-work-liveness.md +62 -0
- package/docs/architecture/decisions/20260924-thin-semantic-working-context.md +72 -0
- package/docs/architecture/decisions/20261001-supervisor-turn-authority.md +237 -0
- package/docs/architecture/decisions/20261005-forge-v3-desktop-client-boundary.md +77 -0
- package/docs/architecture/domains/verification.md +1 -0
- package/docs/architecture/domains/workflow-engine.md +6 -0
- package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +1 -1
- package/docs/forge-plugin-management.md +2 -2
- package/docs/operations/controller-performance-and-502.md +17 -0
- package/docs/operations/controller-reliability-runbook.md +2 -2
- package/docs/operations/features.md +1 -1
- package/docs/operations/features.zh-CN.md +1 -1
- package/docs/operations/homebrew.md +2 -2
- package/docs/operations/mcp-tool-exposure.md +2 -0
- package/docs/operations/releasing.md +2 -0
- package/docs/operations/standalone-disaster-recovery.md +14 -6
- package/docs/wiki/Implementation.md +1 -1
- package/package.json +25 -14
- package/packages/kernel/cognition/api/index.ts +6 -0
- package/packages/kernel/cognition/application/activation-service.ts +451 -0
- package/packages/kernel/cognition/application/consolidation-service.ts +185 -0
- package/packages/kernel/cognition/application/legacy-experience-adapter.ts +38 -0
- package/packages/kernel/cognition/application/memory-service.ts +64 -0
- package/packages/kernel/cognition/domain/learning.ts +106 -0
- package/packages/kernel/cognition/domain/memory.ts +254 -0
- package/packages/kernel/controller/api/index.ts +1 -0
- package/packages/kernel/controller/application/continuation-service.ts +281 -0
- package/packages/kernel/controller/application/controller-round-service.ts +12 -0
- package/packages/kernel/controller/domain/controller-round-transition-policy.ts +242 -21
- package/packages/kernel/controller/domain/controller-round.ts +4 -1
- package/packages/kernel/controller/domain/execution-quality.ts +3 -1
- package/packages/kernel/controller/infrastructure/controller-round-store.ts +689 -103
- package/packages/kernel/controller/infrastructure/controller-session-store.ts +297 -71
- package/packages/kernel/controller/ports/controller-host.ts +2 -0
- package/packages/kernel/identity/api/index.ts +10 -0
- package/packages/kernel/identity/domain/activity-projection.ts +43 -0
- package/packages/kernel/identity/domain/capability-broker.ts +60 -0
- package/packages/kernel/identity/domain/grant.ts +72 -0
- package/packages/kernel/identity/domain/owned-resource.ts +116 -0
- package/packages/kernel/identity/domain/user-request.ts +93 -0
- package/packages/kernel/identity/infrastructure/activity-projection-store.ts +77 -0
- package/packages/kernel/identity/infrastructure/capability-broker.ts +81 -0
- package/packages/kernel/identity/infrastructure/grant-store.ts +284 -0
- package/packages/kernel/identity/infrastructure/owned-resource-store.ts +259 -0
- package/packages/kernel/identity/infrastructure/user-request-store.ts +182 -0
- package/packages/kernel/scheduler/api/index.ts +0 -2
- package/packages/kernel/scheduler/application/schedule-service.ts +3 -0
- package/packages/kernel/scheduler/application/settlement.ts +0 -15
- package/packages/kernel/scheduler/domain/schedule.ts +87 -0
- package/packages/kernel/scheduler/infrastructure/schedule-store.ts +153 -133
- package/packages/kernel/work/api/index.ts +3 -2
- package/packages/kernel/work/application/work-service.ts +17 -42
- package/packages/kernel/work/domain/current-task-lineage.ts +104 -0
- package/packages/kernel/work/domain/engineering-design.ts +10 -5
- package/packages/kernel/work/domain/engineering-profile.ts +8 -12
- package/packages/kernel/work/domain/evaluation-promotion-receipt.ts +186 -0
- package/packages/kernel/work/domain/execution-concurrency.ts +19 -26
- package/packages/kernel/work/domain/execution-snapshot.ts +0 -31
- package/packages/kernel/work/domain/implementation-review.ts +14 -331
- package/packages/kernel/work/domain/objective-graph.ts +119 -0
- package/packages/kernel/work/domain/project-engineering-contract.ts +23 -1
- package/packages/kernel/work/domain/repository-completion-receipt.ts +1 -1
- package/packages/kernel/work/domain/state-machine.ts +25 -63
- package/packages/kernel/work/domain/types.ts +57 -51
- package/packages/kernel/work/infrastructure/work-contract-store.ts +819 -728
- package/packages/kernel/work/ports/work-contract-store.ts +5 -2
- package/packages/plugin-runtime/browser/session-authority.ts +2 -1
- package/packages/plugin-runtime/computer/chatgpt-identity.ts +24 -0
- package/packages/plugin-runtime/computer/conversation-target.ts +143 -0
- package/packages/plugin-runtime/computer/index.ts +2 -0
- package/packages/plugin-runtime/computer/target-authority.ts +26 -1
- package/packages/plugin-runtime/external/unix-jsonl-transport.ts +142 -18
- package/packages/protocols/computer/contract.ts +27 -1
- package/scripts/activate-source-baseline.ts +7 -9
- package/scripts/canary-controller-semantic-wait.ts +12 -24
- package/scripts/check-mcp-compatibility.ts +48 -136
- package/scripts/check-runtime-architecture.mjs +622 -217
- package/scripts/forge-local-recovery-helper.mjs +93 -15
- package/scripts/lib/project-init-lib.sh +3 -0
- package/scripts/open-source-audit-allowlist.txt +1 -2
- package/scripts/run-bounded-command.mjs +5 -0
- package/scripts/run-governed-gate.ts +0 -1
- package/scripts/run-mobile-monitor-bridge.ts +0 -1
- package/scripts/smoke-control-plane-failure-domain.ts +94 -0
- package/scripts/smoke-mcp-http-runtime.ts +5 -0
- package/scripts/smoke-runtime-recovery.ts +12 -43
- package/scripts/smoke-schedule-engine.ts +23 -15
- package/scripts/smoke-unattended-transport-recovery.ts +223 -0
- package/scripts/smoke-user-request-continuation-restart.ts +232 -0
- package/scripts/smoke-workflow-supervisor-chrome-adapter.ts +92 -0
- package/scripts/smoke-workflow-supervisor-process-restart.ts +276 -0
- package/scripts/smoke-workflow-supervisor-restart-recovery.ts +150 -0
- package/scripts/smoke-workflow-supervisor.ts +103 -0
- package/scripts/verify-forge-runtime.sh +2 -1
- package/src/cli/agent-jobs/executor-health.ts +11 -34
- package/src/cli/agent-jobs/integration.ts +18 -49
- package/src/cli/agent-jobs/job-manager.ts +7 -3
- package/src/cli/commands/brain-assistant.ts +27 -13
- package/src/cli/commands/brain.ts +13 -15
- package/src/cli/commands/chatgpt.ts +1 -163
- package/src/cli/commands/recovery.ts +135 -121
- package/src/cli/commands/runtime.ts +3 -3
- package/src/cli/commands/setup-profile.ts +8 -0
- package/src/cli/controller/check-runner.ts +13 -2
- package/src/cli/controller/completion-backlog.ts +3 -3
- package/src/cli/controller/completion-orchestrator.ts +4 -43
- package/src/cli/controller/completion-target.ts +3 -72
- package/src/cli/controller/context/source-materializer.ts +81 -100
- package/src/cli/controller/context-pack.ts +10 -8
- package/src/cli/controller/execution-completion.ts +4 -7
- package/src/cli/controller/execution-policy.ts +36 -153
- package/src/cli/controller/governance.ts +2 -3
- package/src/cli/controller/issue-store.ts +4 -99
- package/src/cli/controller/launch-agents.ts +59 -18
- package/src/cli/controller/operational-plan.ts +24 -51
- package/src/cli/controller/runtime-config.ts +5 -1
- package/src/cli/controller/stuck-state-migration.ts +2 -3
- package/src/cli/controller/systemd-user.ts +39 -1
- package/src/cli/editing/edit-session.ts +1 -7
- package/src/cli/local-bridge/console-automations.ts +18 -13
- package/src/cli/local-bridge/console-view-models.ts +3 -12
- package/src/cli/local-bridge/facade-api.ts +64 -191
- package/src/cli/local-bridge/server.ts +24 -147
- package/src/cli/local-bridge/types.ts +0 -1
- package/src/cli/mcp/oauth-client-retirement.ts +74 -0
- package/src/cli/repositories/command-executor.ts +29 -23
- package/src/cli/repositories/command-scope.ts +35 -16
- package/src/cli/repositories/controller-home.ts +48 -0
- package/src/cli/repositories/ephemeral-workspace.ts +20 -30
- package/src/cli/repositories/git-command-executor.ts +2 -2
- package/src/cli/repositories/registry.ts +77 -11
- package/src/cli/repositories/structured-git.ts +62 -2
- package/src/cli/repository/session-cache.ts +92 -6
- package/src/runtime/context/assistant-context.ts +20 -7
- package/src/runtime/context/assistant-work-context.ts +89 -27
- package/src/runtime/context/automatic-learning.ts +565 -0
- package/src/runtime/context/cognitive-attention.ts +173 -0
- package/src/runtime/context/cognitive-skill-canary.ts +158 -0
- package/src/runtime/context/context-closure.ts +46 -9
- package/src/runtime/context/context-pack-process.ts +103 -0
- package/src/runtime/context/context-plane.ts +1 -9
- package/src/runtime/context/direct-controller-learning.ts +298 -0
- package/src/runtime/context/semantic-navigation-contract.ts +9 -0
- package/src/runtime/context/semantic-navigation.ts +6 -34
- package/src/runtime/context/typescript-navigation-process.ts +145 -0
- package/src/runtime/context/typescript-navigation.ts +89 -3
- package/src/runtime/control-plane/cleanup-artifact-retention.ts +212 -9
- package/src/runtime/control-plane/concurrency/work-execution-concurrency.ts +9 -47
- package/src/runtime/control-plane/execution/controller-authority-recovery.ts +34 -4
- package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +29 -20
- package/src/runtime/control-plane/execution/execution-identity.ts +23 -3
- package/src/runtime/control-plane/execution/repository-work-attribution.ts +7 -30
- package/src/runtime/control-plane/execution/retained-work-resume.ts +206 -199
- package/src/runtime/control-plane/execution/session-store.ts +31 -1
- package/src/runtime/control-plane/execution/validation.ts +6 -8
- package/src/runtime/control-plane/execution/work-authority-reconciler.ts +13 -42
- package/src/runtime/control-plane/execution/work-check-evidence.ts +137 -0
- package/src/runtime/control-plane/execution/work-completion-authority.ts +30 -85
- package/src/runtime/control-plane/execution/work-delivery-service.ts +497 -0
- package/src/runtime/control-plane/execution/work-evidence-policy.ts +12 -50
- package/src/runtime/control-plane/execution/work-execution-support.ts +50 -149
- package/src/runtime/control-plane/execution/work-handle-authority.ts +188 -109
- package/src/runtime/control-plane/execution/work-handle-store.ts +19 -43
- package/src/runtime/control-plane/execution/work-head-settlement.ts +53 -20
- package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +52 -29
- package/src/runtime/control-plane/execution/work-operation-service.ts +43 -27
- package/src/runtime/control-plane/execution/work-preparation-service.ts +126 -266
- package/src/runtime/control-plane/execution/work-remote-delivery.ts +2 -3
- package/src/runtime/control-plane/execution/work-revision-diff.ts +39 -0
- package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +173 -186
- package/src/runtime/control-plane/execution/work-validation-reconciler.ts +28 -84
- package/src/runtime/control-plane/execution/work-verification-service.ts +406 -50
- package/src/runtime/control-plane/execution/work-verification-snapshot.ts +5 -1
- package/src/runtime/control-plane/facade/capability-registry.ts +362 -37
- package/src/runtime/control-plane/facade/check-normalization.ts +3 -9
- package/src/runtime/control-plane/facade/codex-delegation.ts +10 -11
- package/src/runtime/control-plane/facade/controller-connection.ts +135 -0
- package/src/runtime/control-plane/facade/goal-workloop-access.ts +1 -20
- package/src/runtime/control-plane/facade/goal-workloop.ts +281 -1753
- package/src/runtime/control-plane/facade/handoff-inbox-application.ts +12 -25
- package/src/runtime/control-plane/facade/handoff-inbox-store.ts +256 -52
- package/src/runtime/control-plane/facade/index.ts +0 -1
- package/src/runtime/control-plane/facade/operation-digest.ts +12 -15
- package/src/runtime/control-plane/facade/plan-contract-store.ts +451 -991
- package/src/runtime/control-plane/facade/policy-gate.ts +0 -23
- package/src/runtime/control-plane/facade/repository-work-admission.ts +12 -22
- package/src/runtime/control-plane/facade/requirement-authority.ts +102 -199
- package/src/runtime/control-plane/facade/requirement-board.ts +3 -3
- package/src/runtime/control-plane/facade/rh-work-operation-contract.ts +51 -14
- package/src/runtime/control-plane/facade/self-healing-loop.ts +18 -53
- package/src/runtime/control-plane/facade/suggested-actions.ts +15 -18
- package/src/runtime/control-plane/facade/types.ts +49 -108
- package/src/runtime/control-plane/facade/work-continuation.ts +8 -8
- package/src/runtime/control-plane/generated-cache-retention.ts +97 -1
- package/src/runtime/control-plane/global-scheduler/autonomous-continuation.ts +248 -0
- package/src/runtime/control-plane/global-scheduler/durable-admission.ts +1 -1
- package/src/runtime/control-plane/global-scheduler/maintenance.ts +239 -112
- package/src/runtime/control-plane/global-scheduler/reconciliation.ts +2 -1
- package/src/runtime/control-plane/global-scheduler/scheduler.ts +66 -40
- package/src/runtime/control-plane/global-scheduler/wake-signal.ts +42 -2
- package/src/runtime/control-plane/governance/access-policy.ts +19 -2
- package/src/runtime/control-plane/governance/authorization.ts +20 -3
- package/src/runtime/control-plane/launcher/chatgpt-round-continuation.ts +63 -300
- package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +28 -300
- package/src/runtime/control-plane/launcher/local-conversation-controller.ts +238 -0
- package/src/runtime/control-plane/launcher/provider-mcp-bootstrap.ts +5 -2
- package/src/runtime/control-plane/launcher/thin-launcher.ts +96 -240
- package/src/runtime/control-plane/persistence/cognition-store.ts +820 -0
- package/src/runtime/control-plane/persistence/experience-store.ts +162 -29
- package/src/runtime/control-plane/persistence/requirement-store.ts +216 -74
- package/src/runtime/control-plane/persistence/sqlite-store.ts +113 -6
- package/src/runtime/control-plane/release-retention.ts +135 -47
- package/src/runtime/control-plane/repository-namespace-retention.ts +2 -2
- package/src/runtime/control-plane/runtime-cleanup.ts +290 -21
- package/src/runtime/control-plane/workspace/project-onboarding.ts +80 -0
- package/src/runtime/control-plane/workspace/semantic-sync-service.ts +19 -5
- package/src/runtime/control-plane/workspace/workspace-store.ts +169 -0
- package/src/runtime/diagnostics/incident-repair.ts +313 -146
- package/src/runtime/diagnostics/mcp-timing.ts +114 -0
- package/src/runtime/evidence/artifact-store.ts +158 -40
- package/src/runtime/evidence/evidence-store.ts +112 -8
- package/src/runtime/evidence/output-handle-index.ts +225 -0
- package/src/runtime/evidence/result-store.ts +158 -44
- package/src/runtime/execution/jobs/store.ts +3 -4
- package/src/runtime/execution/managed-workspace.ts +57 -0
- package/src/runtime/execution/process-runtime/check-facade.ts +1 -1
- package/src/runtime/execution/process-runtime/check-result-retention.ts +2 -2
- package/src/runtime/execution/process-runtime/check-result.ts +105 -1
- package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +18 -8
- package/src/runtime/execution/process-runtime/command-facade.ts +79 -27
- package/src/runtime/execution/process-runtime/gc.ts +15 -2
- package/src/runtime/execution/process-runtime/handle-index.ts +176 -0
- package/src/runtime/execution/process-runtime/host-command-lane.ts +139 -0
- package/src/runtime/execution/process-runtime/index.ts +3 -0
- package/src/runtime/execution/process-runtime/lightweight-managed.ts +251 -72
- package/src/runtime/execution/process-runtime/persisted-check.ts +3 -3
- package/src/runtime/execution/process-runtime/process-runner-entry.ts +47 -5
- package/src/runtime/execution/process-runtime/process-scope.ts +160 -0
- package/src/runtime/execution/process-runtime/resource-claims.ts +65 -61
- package/src/runtime/execution/process-runtime/runtime.ts +35 -2
- package/src/runtime/execution/process-runtime/store.ts +30 -2
- package/src/runtime/execution/process-runtime/types.ts +6 -1
- package/src/runtime/execution/workers/executor.ts +2 -6
- package/src/runtime/platform/service-inventory.ts +130 -0
- package/src/runtime/platform/service-manager.ts +8 -0
- package/src/runtime/plugins/action-application.ts +85 -28
- package/src/runtime/plugins/app-store-connect-adapter.ts +38 -14
- package/src/runtime/plugins/browser-adapter.ts +757 -34
- package/src/runtime/plugins/browser-automation-service.ts +23 -3
- package/src/runtime/plugins/browser-macos-bridge.ts +321 -29
- package/src/runtime/plugins/browser-manifest-surface.ts +45 -11
- package/src/runtime/plugins/browser-native-session-service.ts +63 -13
- package/src/runtime/plugins/browser-registration.ts +16 -0
- package/src/runtime/plugins/browser-runtime.ts +14 -1
- package/src/runtime/plugins/browser-session-authority.ts +2 -2
- package/src/runtime/plugins/browser-session-store.ts +46 -76
- package/src/runtime/plugins/capability-authorization-grants.ts +258 -62
- package/src/runtime/plugins/computer-chatgpt-browser-adapter-installer.ts +219 -0
- package/src/runtime/plugins/computer-chatgpt-macos-target.ts +891 -0
- package/src/runtime/plugins/computer-registration.ts +222 -11
- package/src/runtime/plugins/desktop-operator-external-policy.ts +36 -4
- package/src/runtime/plugins/desktop-operator-registration.ts +12 -4
- package/src/runtime/plugins/external-adapter.ts +42 -6
- package/src/runtime/plugins/external-registration.ts +21 -1
- package/src/runtime/plugins/gmail-adapter.ts +23 -2
- package/src/runtime/plugins/google-calendar-adapter.ts +2 -2
- package/src/runtime/plugins/google-shared.ts +14 -7
- package/src/runtime/plugins/google-tasks-adapter.ts +2 -2
- package/src/runtime/plugins/ios-adapter.ts +1 -1
- package/src/runtime/plugins/lightweight-action.ts +208 -2
- package/src/runtime/plugins/local-system-adapter.ts +12 -2
- package/src/runtime/plugins/managed-process-adapter.ts +12 -5
- package/src/runtime/plugins/plugin-action-sidecar.ts +17 -13
- package/src/runtime/plugins/plugin-management-adapter.ts +1 -1
- package/src/runtime/plugins/store.ts +602 -428
- package/src/runtime/plugins/types.ts +20 -0
- package/src/runtime/plugins/xiaohongshu-publish.ts +16 -0
- package/src/runtime/projections/invalidation.ts +7 -3
- package/src/runtime/projections/materialized-view.ts +80 -36
- package/src/runtime/recovery/actions.ts +3 -3
- package/src/runtime/recovery/capability-action-application.ts +46 -45
- package/src/runtime/recovery/lifecycle-inventory.ts +29 -29
- package/src/runtime/recovery/maintenance-executor.ts +374 -123
- package/src/runtime/recovery/store.ts +29 -11
- package/src/runtime/release/release-coordinator.ts +296 -0
- package/src/runtime/release/release-session.ts +716 -0
- package/src/runtime/release/release-state-migration.ts +25 -0
- package/src/runtime/resources/leases/store.ts +56 -25
- package/src/runtime/root/computer-composition.ts +191 -6
- package/src/runtime/root/controller-progression-composition.ts +192 -0
- package/src/runtime/root/controller-round-composition.ts +20 -1
- package/src/runtime/root/deployment-topology.ts +106 -0
- package/src/runtime/root/entry.ts +4 -3
- package/src/runtime/root/known-good-recovery.ts +184 -0
- package/src/runtime/root/local-bridge.ts +0 -1
- package/src/runtime/root/mcp-transport.ts +111 -18
- package/src/runtime/root/ownership.ts +261 -9
- package/src/runtime/root/package-connector-service.ts +105 -19
- package/src/runtime/root/package-runtime-release.ts +117 -18
- package/src/runtime/root/package-runtime-service.ts +17 -11
- package/src/runtime/root/release-execution-canary.ts +44 -12
- package/src/runtime/root/release-loader.ts +90 -8
- package/src/runtime/root/release-manifest.ts +80 -6
- package/src/runtime/root/release-materialize.ts +372 -28
- package/src/runtime/root/release-store.ts +395 -36
- package/src/runtime/root/runtime-lane.ts +191 -0
- package/src/runtime/root/runtime.ts +159 -27
- package/src/runtime/root/scheduled-controller-composition.ts +28 -3
- package/src/runtime/root/service-runner.ts +2 -2
- package/src/runtime/root/service.ts +36 -33
- package/src/runtime/root/status.ts +48 -0
- package/src/runtime/root/types.ts +32 -3
- package/src/runtime/root/workflow-supervisor-composition.ts +493 -0
- package/src/runtime/root/workflow-supervisor-runtime.ts +209 -0
- package/src/runtime/root/write-fence.ts +50 -2
- package/src/runtime/safe-tooling/google-credential-store.ts +76 -21
- package/src/runtime/safe-tooling/google-oauth-broker.ts +25 -7
- package/src/runtime/safe-tooling/workspace-auth.ts +1 -1
- package/src/runtime/shared/bounded-child-supervisor.ts +9 -1
- package/src/runtime/shared/process-environment.ts +20 -9
- package/src/runtime/shared/response-budget.ts +1 -3
- package/src/runtime/standalone-recovery/controller-home-migration.ts +16 -24
- package/src/runtime/standalone-recovery/core.ts +3702 -342
- package/src/runtime/standalone-recovery/entry.ts +469 -39
- package/src/runtime/standalone-recovery/installer.ts +262 -148
- package/src/runtime/standalone-recovery/mcp-server.ts +29 -193
- package/src/runtime/standalone-recovery/operation-lock.ts +1 -1
- package/src/runtime/standalone-recovery/performance.ts +54 -16
- package/src/runtime/standalone-recovery/release.ts +53 -5
- package/src/runtime/standalone-recovery/service-labels.ts +5 -2
- package/src/runtime/standalone-recovery/watchdog-heartbeat.ts +5 -2
- package/src/runtime/workflow/schedules/browser-probe.ts +17 -6
- package/src/runtime/workflow/schedules/chatgpt-automation-migration.ts +10 -3
- package/src/runtime/workflow/schedules/engine.ts +138 -172
- package/src/runtime/workflow/schedules/work-continuation.ts +44 -5
- package/src/runtime/workflows/runtime.ts +15 -19
- package/src/testing/test-governance.ts +5 -1
- package/supervisor/chatgpt-conversation.ts +23 -0
- package/supervisor/client.ts +201 -0
- package/supervisor/control-plane.ts +868 -0
- package/supervisor/entry.ts +15 -0
- package/supervisor/forge-validators.ts +80 -0
- package/supervisor/index.ts +5 -0
- package/supervisor/native-browser-adapter.ts +786 -0
- package/supervisor/native-messaging/host.ts +39 -0
- package/supervisor/native-messaging/manifest.ts +26 -0
- package/supervisor/native-messaging/protocol.ts +27 -0
- package/supervisor/paths.ts +18 -0
- package/supervisor/protocol.ts +202 -0
- package/supervisor/server.ts +404 -0
- package/supervisor/store.ts +1451 -0
- package/supervisor/tsconfig.json +8 -0
- package/supervisor/types.ts +255 -0
- package/adapters/chatgpt/controller-host.ts +0 -119
- package/adapters/mcp/controller-round-compatibility.ts +0 -184
- package/adapters/mcp/frozen-client-semantic-compatibility.ts +0 -292
- package/adapters/mcp/runtime-gateway/work-compat-adapter.ts +0 -502
- package/packages/kernel/progression/api/index.ts +0 -2
- package/packages/kernel/progression/application/projector.ts +0 -211
- package/packages/kernel/progression/domain/types.ts +0 -116
- package/packages/kernel/progression/index.ts +0 -1
- package/packages/kernel/scheduler/application/continuation-service.ts +0 -212
- package/packages/kernel/scheduler/domain/continuation.ts +0 -38
- package/packages/kernel/scheduler/infrastructure/continuation-dispatch-store.ts +0 -38
- package/packages/kernel/work/domain/admission-policy.ts +0 -34
- package/scripts/benchmark-background-check-overlap.ts +0 -228
- package/scripts/benchmark-computer-hotpath.ts +0 -255
- package/scripts/benchmark-controller-hotpath.ts +0 -212
- package/scripts/benchmark-controller-round2.ts +0 -191
- package/scripts/benchmark-ios-agent-device.ts +0 -511
- package/scripts/benchmark-mcp-host.sh +0 -117
- package/scripts/benchmark-mcp-transport.ts +0 -169
- package/scripts/benchmark-operational-memory-activation.ts +0 -235
- package/scripts/benchmark-operational-shadow.ts +0 -28
- package/scripts/benchmark-quality-harness.ts +0 -146
- package/scripts/benchmark-route-session-concurrency.ts +0 -915
- package/scripts/benchmark-thin-harness-gateway-ab.ts +0 -632
- package/scripts/benchmark-thin-harness-mcp-e2e.ts +0 -498
- package/scripts/benchmark-thin-harness.ts +0 -251
- package/scripts/benchmark-typescript-navigation.ts +0 -186
- package/scripts/benchmark-work-efficiency.ts +0 -334
- package/scripts/build-controller-ui.ts +0 -28
- package/scripts/check-npm-release.sh +0 -32
- package/scripts/check-public-release-surface.sh +0 -19
- package/scripts/check-release-published.sh +0 -57
- package/scripts/check-release-readiness.sh +0 -37
- package/scripts/check-release-version.mjs +0 -74
- package/scripts/check-tarball-install-smoke.sh +0 -150
- package/scripts/package-source-archive.sh +0 -147
- package/scripts/public-release-files.txt +0 -39
- package/scripts/public-release.ts +0 -174
- package/scripts/publish-release-tarball.sh +0 -27
- package/scripts/route-nl-vs-ts-eval.ts +0 -679
- package/scripts/stage-runtime-release.ts +0 -44
- package/src/cli/controller/progress.ts +0 -656
- package/src/cli/controller/work-mode.ts +0 -181
- package/src/cli/local-bridge/console-assets.ts +0 -17
- package/src/cli/local-bridge/dashboard.ts +0 -17
- package/src/cli/local-bridge/frontend/src/App.tsx +0 -10
- package/src/cli/local-bridge/frontend/src/components/Button.tsx +0 -1
- package/src/cli/local-bridge/frontend/src/components/DetailPane.tsx +0 -3
- package/src/cli/local-bridge/frontend/src/components/SearchField.tsx +0 -1
- package/src/cli/local-bridge/frontend/src/components/SectionHeader.tsx +0 -1
- package/src/cli/local-bridge/frontend/src/components/Segmented.tsx +0 -1
- package/src/cli/local-bridge/frontend/src/components/StatusText.tsx +0 -3
- package/src/cli/local-bridge/frontend/src/design/icons.tsx +0 -11
- package/src/cli/local-bridge/frontend/src/design/tokens.css +0 -8
- package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +0 -153
- package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +0 -37
- package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +0 -183
- package/src/cli/local-bridge/frontend/src/features/repositories/RepositoriesView.tsx +0 -2
- package/src/cli/local-bridge/frontend/src/features/system/SystemView.tsx +0 -2
- package/src/cli/local-bridge/frontend/src/features/work/WorkView.tsx +0 -4
- package/src/cli/local-bridge/frontend/src/lib/api.ts +0 -30
- package/src/cli/local-bridge/frontend/src/lib/format.ts +0 -4
- package/src/cli/local-bridge/frontend/src/lib/routes.ts +0 -7
- package/src/cli/local-bridge/frontend/src/main.tsx +0 -1
- package/src/cli/local-bridge/frontend/src/shell/AppShell.tsx +0 -1
- package/src/cli/local-bridge/frontend/src/shell/CommandBar.tsx +0 -2
- package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +0 -4
- package/src/cli/local-bridge/frontend/src/styles.css +0 -28
- package/src/cli/local-bridge/frontend/src/types.ts +0 -15
- package/src/cli/local-bridge/frontend/src/vite-env.d.ts +0 -1
- package/src/cli/local-bridge/frontend/vite.config.ts +0 -27
- package/src/cli/local-bridge/ui-dist/app.css +0 -1
- package/src/cli/local-bridge/ui-dist/app.js +0 -11228
- package/src/runtime/control-plane/execution/content-equivalent-commit-authority.ts +0 -169
- package/src/runtime/control-plane/execution/direct-canonical-work-reconciliation.ts +0 -85
- package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +0 -655
- package/src/runtime/control-plane/execution/work-finalization-service.ts +0 -2850
- package/src/runtime/control-plane/execution/work-path-scope.ts +0 -37
- package/src/runtime/control-plane/execution/work-task-receipt.ts +0 -468
- package/src/runtime/control-plane/facade/controller-round-relay.ts +0 -3
- package/src/runtime/control-plane/facade/work-admission-policy.ts +0 -142
- package/src/runtime/control-plane/facade/work-implementation-review.ts +0 -2
- package/src/runtime/control-plane/facade/work-state-machine.ts +0 -2
- package/src/runtime/control-plane/facade/work-task-projection.ts +0 -66
- package/src/runtime/control-plane/routing/index.ts +0 -1
- package/src/runtime/control-plane/routing/route-policy.ts +0 -401
- package/src/runtime/control-plane/routing/workspace-admission.ts +0 -70
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
import { timingSafeEqual } from 'crypto';
|
|
2
|
+
import express, { type Express, type NextFunction, type Request, type Response } from 'express';
|
|
3
|
+
import type { OAuthClientInformationFull } from '@modelcontextprotocol/server';
|
|
4
|
+
import { clientRegistrationHandler, redirectUriMatches, revocationHandler, tokenHandler } from '@modelcontextprotocol/server-legacy/auth';
|
|
5
|
+
import type { McpLocalConfig } from '../auth';
|
|
6
|
+
import { createMcpOAuthProvider } from '../oauth';
|
|
7
|
+
|
|
8
|
+
export function getConfiguredPublicOrigin(config: McpLocalConfig | null): string | undefined {
|
|
9
|
+
const configured = process.env.FORGE_MCP_PUBLIC_ORIGIN?.trim();
|
|
10
|
+
if (configured) {
|
|
11
|
+
try {
|
|
12
|
+
return new URL(configured).origin;
|
|
13
|
+
} catch (_error) {
|
|
14
|
+
// Fall through to service or legacy config.
|
|
15
|
+
}
|
|
16
|
+
}
|
|
17
|
+
const endpoint = config?.chatgpt?.endpoint?.trim();
|
|
18
|
+
if (!endpoint) return undefined;
|
|
19
|
+
try {
|
|
20
|
+
return new URL(endpoint).origin;
|
|
21
|
+
} catch (_error) {
|
|
22
|
+
return undefined;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function getPublicOrigin(req: Request, configuredOrigin: string | undefined): string {
|
|
27
|
+
if (configuredOrigin) return configuredOrigin;
|
|
28
|
+
const proto = (req.headers['x-forwarded-proto'] as string | undefined) ?? 'https';
|
|
29
|
+
const host = (req.headers['x-forwarded-host'] as string | undefined) ?? req.headers.host ?? '127.0.0.1:8765';
|
|
30
|
+
return `${proto}://${host}`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function isAllowedMcpOAuthRedirectUri(redirectUri: string): boolean {
|
|
34
|
+
try {
|
|
35
|
+
const url = new URL(redirectUri);
|
|
36
|
+
if (url.protocol === 'http:' && ['localhost', '127.0.0.1', '[::1]'].includes(url.hostname)) return true;
|
|
37
|
+
return url.protocol === 'https:'
|
|
38
|
+
&& (url.origin === 'https://chatgpt.com' || url.origin === 'https://chat.openai.com')
|
|
39
|
+
&& url.pathname.startsWith('/connector/oauth/');
|
|
40
|
+
} catch (_error) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function isRegisteredRedirectUri(redirectUri: string, client: { redirect_uris?: string[] }): boolean {
|
|
46
|
+
return (client.redirect_uris ?? []).some((registered) => redirectUriMatches(redirectUri, registered));
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function isRegisteredExternalHttpsRedirectUri(redirectUri: string, client: { redirect_uris?: string[] }): boolean {
|
|
50
|
+
try {
|
|
51
|
+
const url = new URL(redirectUri);
|
|
52
|
+
return url.protocol === 'https:' && !url.username && !url.password && isRegisteredRedirectUri(redirectUri, client);
|
|
53
|
+
} catch (_error) {
|
|
54
|
+
return false;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
function isSafeOAuthFallbackRedirectUri(redirectUri: string): boolean {
|
|
59
|
+
try {
|
|
60
|
+
const url = new URL(redirectUri);
|
|
61
|
+
if (url.username || url.password) return false;
|
|
62
|
+
if (url.protocol === 'http:' && ['localhost', '127.0.0.1', '[::1]'].includes(url.hostname)) return true;
|
|
63
|
+
return url.protocol === 'https:';
|
|
64
|
+
} catch (_error) {
|
|
65
|
+
return false;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function oauthAuthorizeParamSource(req: Request): Record<string, unknown> {
|
|
70
|
+
if (req.method === 'POST' && req.body && typeof req.body === 'object' && !Buffer.isBuffer(req.body)) {
|
|
71
|
+
return req.body as Record<string, unknown>;
|
|
72
|
+
}
|
|
73
|
+
return req.query as Record<string, unknown>;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function readOAuthAuthorizeString(source: Record<string, unknown>, key: string): string {
|
|
77
|
+
const value = source[key];
|
|
78
|
+
return typeof value === 'string' ? value.trim() : '';
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export function isIncompleteOAuthAuthorizeRequest(source: Record<string, unknown>): boolean {
|
|
82
|
+
const clientId = readOAuthAuthorizeString(source, 'client_id');
|
|
83
|
+
const responseType = readOAuthAuthorizeString(source, 'response_type');
|
|
84
|
+
const codeChallenge = readOAuthAuthorizeString(source, 'code_challenge');
|
|
85
|
+
const redirectUri = readOAuthAuthorizeString(source, 'redirect_uri');
|
|
86
|
+
const hasRedirectContext = Boolean(redirectUri) || Boolean(clientId);
|
|
87
|
+
return !clientId || !responseType || !codeChallenge || !hasRedirectContext;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function incompleteOAuthAuthorizeResponseBody() {
|
|
91
|
+
return {
|
|
92
|
+
error: 'invalid_request' as const,
|
|
93
|
+
error_description: 'OAuth authorization request is incomplete. Required: client_id, response_type, code_challenge, and a usable redirect context (redirect_uri or a registered client).',
|
|
94
|
+
message: 'This endpoint expects a complete OAuth authorization request (PKCE). Non-OAuth MCP clients should use /mcp-bearer with Authorization: Bearer <token> instead of /authorize.',
|
|
95
|
+
hint: 'Use POST/GET /mcp-bearer with a forge bearer token for clients that cannot complete OAuth dynamic registration and PKCE.',
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function isOAuthDebugTraceEnabled(): boolean {
|
|
100
|
+
return process.env.FORGE_MCP_OAUTH_TRACE === '1' || process.env.FORGE_MCP_OAUTH_TRACE === 'true';
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
const SENSITIVE_OAUTH_FIELDS = new Set(['passphrase', 'code', 'code_verifier', 'client_secret', 'access_token', 'refresh_token', 'token', 'authorization']);
|
|
104
|
+
|
|
105
|
+
function safeOAuthFieldNames(source: Record<string, unknown>): string[] {
|
|
106
|
+
return Object.keys(source).filter((key) => !SENSITIVE_OAUTH_FIELDS.has(key.toLowerCase())).sort();
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
function oauthTrace(req: Request, event: string, extra: Record<string, unknown> = {}): void {
|
|
110
|
+
if (!isOAuthDebugTraceEnabled()) return;
|
|
111
|
+
const source = oauthAuthorizeParamSource(req);
|
|
112
|
+
const userAgent = typeof req.headers['user-agent'] === 'string' ? req.headers['user-agent'].split(/[\s/]/)[0] : undefined;
|
|
113
|
+
console.error(`[forge:mcp-oauth] ${JSON.stringify({
|
|
114
|
+
event,
|
|
115
|
+
method: req.method,
|
|
116
|
+
path: req.path,
|
|
117
|
+
fieldNames: safeOAuthFieldNames(source),
|
|
118
|
+
hasClientId: Boolean(readOAuthAuthorizeString(source, 'client_id')),
|
|
119
|
+
hasRedirectUri: Boolean(readOAuthAuthorizeString(source, 'redirect_uri')),
|
|
120
|
+
hasCodeChallenge: Boolean(readOAuthAuthorizeString(source, 'code_challenge')),
|
|
121
|
+
responseType: readOAuthAuthorizeString(source, 'response_type') || undefined,
|
|
122
|
+
codeChallengeMethod: readOAuthAuthorizeString(source, 'code_challenge_method') || undefined,
|
|
123
|
+
grantType: readOAuthAuthorizeString(source, 'grant_type') || undefined,
|
|
124
|
+
hasResource: Boolean(readOAuthAuthorizeString(source, 'resource')),
|
|
125
|
+
userAgent,
|
|
126
|
+
...extra,
|
|
127
|
+
})}`);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
function oauthTraceMiddleware(event: string): (req: Request, res: Response, next: NextFunction) => void {
|
|
131
|
+
return (req, res, next) => {
|
|
132
|
+
oauthTrace(req, `${event}:request`);
|
|
133
|
+
res.once('finish', () => oauthTrace(req, `${event}:response`, { statusCode: res.statusCode }));
|
|
134
|
+
next();
|
|
135
|
+
};
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function rejectIncompleteOAuthAuthorize(req: Request, res: Response, next: NextFunction): void {
|
|
139
|
+
const source = oauthAuthorizeParamSource(req);
|
|
140
|
+
if (isIncompleteOAuthAuthorizeRequest(source)) {
|
|
141
|
+
oauthTrace(req, 'authorize:incomplete');
|
|
142
|
+
res.status(400).json(incompleteOAuthAuthorizeResponseBody());
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
oauthTrace(req, 'authorize:complete');
|
|
146
|
+
next();
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
function escapeHtmlAttribute(value: string): string {
|
|
150
|
+
return value.replace(/&/g, '&').replace(/"/g, '"').replace(/'/g, ''').replace(/</g, '<').replace(/>/g, '>');
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function renderPassphrasePage(params: URLSearchParams): string {
|
|
154
|
+
const hiddenFields = Array.from(params.entries())
|
|
155
|
+
.filter(([key]) => key !== 'passphrase')
|
|
156
|
+
.map(([key, value]) => `<input type="hidden" name="${escapeHtmlAttribute(key)}" value="${escapeHtmlAttribute(value)}">`)
|
|
157
|
+
.join('\n');
|
|
158
|
+
return `<!doctype html>
|
|
159
|
+
<html lang="en">
|
|
160
|
+
<head><meta charset="utf-8"><title>Authorize forge</title>
|
|
161
|
+
<meta name="viewport" content="width=device-width,initial-scale=1">
|
|
162
|
+
<style>
|
|
163
|
+
body{font-family:-apple-system,BlinkMacSystemFont,sans-serif;display:flex;align-items:center;justify-content:center;min-height:100vh;margin:0;background:#f6f6f3;color:#1f2328}
|
|
164
|
+
.card{width:min(420px,92vw);background:#fff;border:1px solid #d8d8d0;border-radius:12px;padding:32px;box-shadow:0 12px 40px rgba(0,0,0,.08)}
|
|
165
|
+
h1{font-size:20px;margin:0 0 8px}p{margin:0 0 20px;color:#60666d;line-height:1.45}
|
|
166
|
+
input{width:100%;box-sizing:border-box;border:1px solid #bfc4c9;border-radius:8px;padding:12px;font-size:16px}
|
|
167
|
+
button{width:100%;margin-top:14px;border:0;border-radius:8px;padding:12px;background:#1f2328;color:#fff;font-size:16px;font-weight:600}
|
|
168
|
+
</style></head>
|
|
169
|
+
<body><main class="card">
|
|
170
|
+
<h1>Authorize forge</h1>
|
|
171
|
+
<p>Enter the local MCP passphrase to let this MCP client use this workflow-scoped connector.</p>
|
|
172
|
+
<form method="POST" action="/authorize">
|
|
173
|
+
${hiddenFields}
|
|
174
|
+
<input type="password" name="passphrase" placeholder="Passphrase" autofocus>
|
|
175
|
+
<button type="submit">Authorize</button>
|
|
176
|
+
</form>
|
|
177
|
+
</main></body></html>`;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function requirePassphrase(passphrase: string): (req: Request, res: Response, next: NextFunction) => void {
|
|
181
|
+
return (req, res, next) => {
|
|
182
|
+
const provided = typeof req.body?.passphrase === 'string' ? req.body.passphrase : undefined;
|
|
183
|
+
if (provided) {
|
|
184
|
+
const a = Buffer.from(provided);
|
|
185
|
+
const b = Buffer.from(passphrase);
|
|
186
|
+
if (a.length === b.length && timingSafeEqual(a, b)) {
|
|
187
|
+
next();
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
const source = oauthAuthorizeParamSource(req);
|
|
192
|
+
const params = new URLSearchParams();
|
|
193
|
+
for (const [key, value] of Object.entries(source)) {
|
|
194
|
+
if (key !== 'passphrase' && typeof value === 'string') params.set(key, value);
|
|
195
|
+
}
|
|
196
|
+
if ([...params.keys()].length === 0 && req.url.includes('?')) {
|
|
197
|
+
const fromUrl = new URLSearchParams(req.url.slice(req.url.indexOf('?')));
|
|
198
|
+
for (const [key, value] of fromUrl.entries()) if (key !== 'passphrase') params.set(key, value);
|
|
199
|
+
}
|
|
200
|
+
res.type('html').send(renderPassphrasePage(params));
|
|
201
|
+
};
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
async function getOrRegisterPublicOAuthClient(
|
|
205
|
+
provider: ReturnType<typeof createMcpOAuthProvider>,
|
|
206
|
+
clientId: string,
|
|
207
|
+
redirectUri: string | undefined,
|
|
208
|
+
req: Request,
|
|
209
|
+
): Promise<OAuthClientInformationFull | undefined> {
|
|
210
|
+
const existing = await provider.clientsStore.getClient(clientId);
|
|
211
|
+
if (existing) return existing as OAuthClientInformationFull;
|
|
212
|
+
if (!redirectUri || !isSafeOAuthFallbackRedirectUri(redirectUri) || !provider.clientsStore.registerClient) return undefined;
|
|
213
|
+
oauthTrace(req, 'authorize:auto_register_public_client', {
|
|
214
|
+
redirectScheme: new URL(redirectUri).protocol,
|
|
215
|
+
redirectHost: new URL(redirectUri).hostname,
|
|
216
|
+
});
|
|
217
|
+
return provider.clientsStore.registerClient({
|
|
218
|
+
client_id: clientId,
|
|
219
|
+
client_id_issued_at: Math.floor(Date.now() / 1000),
|
|
220
|
+
client_name: 'forge OAuth fallback client',
|
|
221
|
+
redirect_uris: [redirectUri],
|
|
222
|
+
token_endpoint_auth_method: 'none',
|
|
223
|
+
grant_types: ['authorization_code', 'refresh_token'],
|
|
224
|
+
response_types: ['code'],
|
|
225
|
+
} as unknown as Omit<OAuthClientInformationFull, 'client_id' | 'client_id_issued_at'>) as OAuthClientInformationFull;
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function oauthAuthorizationHandler(provider: ReturnType<typeof createMcpOAuthProvider>) {
|
|
229
|
+
return async (req: Request, res: Response) => {
|
|
230
|
+
const query = req.method === 'POST' ? req.body : req.query;
|
|
231
|
+
const clientId = typeof query.client_id === 'string' ? query.client_id : '';
|
|
232
|
+
const responseType = typeof query.response_type === 'string' ? query.response_type : '';
|
|
233
|
+
const codeChallenge = typeof query.code_challenge === 'string' ? query.code_challenge : '';
|
|
234
|
+
const codeChallengeMethod = typeof query.code_challenge_method === 'string' ? query.code_challenge_method : '';
|
|
235
|
+
const state = typeof query.state === 'string' ? query.state : undefined;
|
|
236
|
+
const scope = typeof query.scope === 'string' ? query.scope : undefined;
|
|
237
|
+
let redirectUri = typeof query.redirect_uri === 'string' ? query.redirect_uri : undefined;
|
|
238
|
+
if (responseType !== 'code') {
|
|
239
|
+
res.status(400).json({ error: 'unsupported_response_type', error_description: 'Only code response type is supported' });
|
|
240
|
+
return;
|
|
241
|
+
}
|
|
242
|
+
if (!codeChallenge || codeChallengeMethod !== 'S256') {
|
|
243
|
+
res.status(400).json({ error: 'invalid_request', error_description: 'PKCE S256 is required' });
|
|
244
|
+
return;
|
|
245
|
+
}
|
|
246
|
+
const client = await getOrRegisterPublicOAuthClient(provider, clientId, redirectUri, req);
|
|
247
|
+
if (!client) {
|
|
248
|
+
res.status(400).json({ error: 'invalid_client', error_description: 'Unknown client_id' });
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
if (!redirectUri && client.redirect_uris.length === 1) redirectUri = client.redirect_uris[0];
|
|
252
|
+
if (!redirectUri || (!isAllowedMcpOAuthRedirectUri(redirectUri) && !isRegisteredExternalHttpsRedirectUri(redirectUri, client))) {
|
|
253
|
+
res.status(400).json({ error: 'invalid_request', error_description: 'redirect_uri must be localhost, a ChatGPT connector callback URL, or a registered HTTPS client redirect_uri' });
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
if (!isRegisteredRedirectUri(redirectUri, client)) {
|
|
257
|
+
res.status(400).json({ error: 'invalid_request', error_description: 'redirect_uri must match a registered client redirect_uri' });
|
|
258
|
+
return;
|
|
259
|
+
}
|
|
260
|
+
await provider.authorize(client as OAuthClientInformationFull, {
|
|
261
|
+
state,
|
|
262
|
+
scopes: scope ? scope.split(' ') : [],
|
|
263
|
+
redirectUri,
|
|
264
|
+
codeChallenge,
|
|
265
|
+
}, res);
|
|
266
|
+
};
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
export function registerMcpOAuthHttpRoutes(
|
|
270
|
+
app: Express,
|
|
271
|
+
provider: ReturnType<typeof createMcpOAuthProvider>,
|
|
272
|
+
passphrase: string,
|
|
273
|
+
configuredOrigin: string | undefined,
|
|
274
|
+
): void {
|
|
275
|
+
app.use('/authorize', express.urlencoded({ extended: false, limit: '10kb' }));
|
|
276
|
+
app.use('/authorize', oauthTraceMiddleware('authorize'));
|
|
277
|
+
app.use('/authorize', rejectIncompleteOAuthAuthorize);
|
|
278
|
+
app.use('/authorize', requirePassphrase(passphrase));
|
|
279
|
+
app.use('/authorize', oauthAuthorizationHandler(provider));
|
|
280
|
+
app.use('/token', oauthTraceMiddleware('token'));
|
|
281
|
+
app.use('/token', tokenHandler({ provider, rateLimit: false }));
|
|
282
|
+
app.use('/revoke', oauthTraceMiddleware('revoke'));
|
|
283
|
+
app.use('/revoke', revocationHandler({ provider, rateLimit: false }));
|
|
284
|
+
app.use('/register', oauthTraceMiddleware('register'));
|
|
285
|
+
app.use('/register', clientRegistrationHandler({ clientsStore: provider.clientsStore, rateLimit: false }));
|
|
286
|
+
app.get('/.well-known/oauth-authorization-server', (req, res) => {
|
|
287
|
+
const origin = getPublicOrigin(req, configuredOrigin);
|
|
288
|
+
res.json({
|
|
289
|
+
issuer: origin,
|
|
290
|
+
authorization_endpoint: `${origin}/authorize`,
|
|
291
|
+
token_endpoint: `${origin}/token`,
|
|
292
|
+
revocation_endpoint: `${origin}/revoke`,
|
|
293
|
+
registration_endpoint: `${origin}/register`,
|
|
294
|
+
response_types_supported: ['code'],
|
|
295
|
+
grant_types_supported: ['authorization_code', 'refresh_token'],
|
|
296
|
+
code_challenge_methods_supported: ['S256'],
|
|
297
|
+
token_endpoint_auth_methods_supported: ['client_secret_post', 'client_secret_basic', 'none'],
|
|
298
|
+
scopes_supported: ['forge'],
|
|
299
|
+
});
|
|
300
|
+
});
|
|
301
|
+
app.get('/.well-known/openid-configuration', (req, res) => {
|
|
302
|
+
const origin = getPublicOrigin(req, configuredOrigin);
|
|
303
|
+
res.json({
|
|
304
|
+
issuer: origin,
|
|
305
|
+
authorization_endpoint: `${origin}/authorize`,
|
|
306
|
+
token_endpoint: `${origin}/token`,
|
|
307
|
+
registration_endpoint: `${origin}/register`,
|
|
308
|
+
response_types_supported: ['code'],
|
|
309
|
+
grant_types_supported: ['authorization_code', 'refresh_token'],
|
|
310
|
+
code_challenge_methods_supported: ['S256'],
|
|
311
|
+
token_endpoint_auth_methods_supported: ['client_secret_post', 'client_secret_basic', 'none'],
|
|
312
|
+
scopes_supported: ['forge'],
|
|
313
|
+
});
|
|
314
|
+
});
|
|
315
|
+
const protectedResourceMetadata = (resourcePath: '/mcp' | '/mcp-grok' | '/mcp-bearer') => (req: Request, res: Response): void => {
|
|
316
|
+
const origin = getPublicOrigin(req, configuredOrigin);
|
|
317
|
+
res.json({ resource: `${origin}${resourcePath}`, authorization_servers: [origin], scopes_supported: ['forge'], bearer_methods_supported: ['header'] });
|
|
318
|
+
};
|
|
319
|
+
app.get('/.well-known/oauth-protected-resource/mcp', protectedResourceMetadata('/mcp'));
|
|
320
|
+
app.get('/.well-known/oauth-protected-resource/mcp-grok', protectedResourceMetadata('/mcp-grok'));
|
|
321
|
+
app.get('/.well-known/oauth-protected-resource/mcp-bearer', protectedResourceMetadata('/mcp-bearer'));
|
|
322
|
+
}
|
|
@@ -76,6 +76,8 @@ export interface McpSessionSnapshot {
|
|
|
76
76
|
protected: number;
|
|
77
77
|
activePosts: number;
|
|
78
78
|
activeStreams: number;
|
|
79
|
+
/** Time since the most recent transport activity among live MCP sessions. */
|
|
80
|
+
latestActivityAgeMs?: number;
|
|
79
81
|
oldestStreamAgeMs: number;
|
|
80
82
|
oldestPostAgeMs: number;
|
|
81
83
|
recoveryRecommended: boolean;
|
|
@@ -445,6 +447,9 @@ export class McpSessionRegistry<
|
|
|
445
447
|
const reserved = reservations.length;
|
|
446
448
|
const protectedCount = sessions.length - evictable + reserved;
|
|
447
449
|
const capacityAvailable = Math.max(0, this.maximumSessions - sessions.length - reserved);
|
|
450
|
+
const latestActivityAt = sessions.length > 0
|
|
451
|
+
? Math.max(...sessions.map((session) => session.lastActivityAt))
|
|
452
|
+
: undefined;
|
|
448
453
|
const streamOpenedAt = sessions
|
|
449
454
|
.filter((session) => session.inFlightGets > 0 && session.streamOpenedAt !== undefined)
|
|
450
455
|
.map((session) => session.streamOpenedAt!);
|
|
@@ -467,6 +472,7 @@ export class McpSessionRegistry<
|
|
|
467
472
|
protected: protectedCount,
|
|
468
473
|
activePosts,
|
|
469
474
|
activeStreams,
|
|
475
|
+
...(latestActivityAt !== undefined ? { latestActivityAgeMs: Math.max(0, this.now() - latestActivityAt) } : {}),
|
|
470
476
|
oldestStreamAgeMs,
|
|
471
477
|
oldestPostAgeMs,
|
|
472
478
|
recoveryRecommended: !acceptingNewSessions && oldestPostAgeMs >= this.activePostStallMs,
|
|
@@ -21,6 +21,14 @@ export interface OpenAiSecureTunnelRuntimeStatusPayload {
|
|
|
21
21
|
profile_path?: string;
|
|
22
22
|
runtime_state?: string;
|
|
23
23
|
error?: string;
|
|
24
|
+
remote_lookup_attempted?: boolean;
|
|
25
|
+
remote_error?: string;
|
|
26
|
+
remote?: unknown;
|
|
27
|
+
control_plane_poll_health?: { state?: string; reason?: string };
|
|
28
|
+
local?: {
|
|
29
|
+
control_plane_poll_health?: { state?: string; reason?: string };
|
|
30
|
+
log?: { tail?: string };
|
|
31
|
+
};
|
|
24
32
|
}
|
|
25
33
|
|
|
26
34
|
export interface OpenAiSecureTunnelRuntimeObservation {
|
|
@@ -30,9 +38,13 @@ export interface OpenAiSecureTunnelRuntimeObservation {
|
|
|
30
38
|
ready: boolean;
|
|
31
39
|
tunnelMatches: boolean;
|
|
32
40
|
endpointMatches: boolean;
|
|
41
|
+
controlPlaneState: 'ready' | 'degraded' | 'unknown';
|
|
42
|
+
controlPlaneDetail: string;
|
|
33
43
|
alias: string;
|
|
34
44
|
tunnelId: string;
|
|
35
45
|
observedTunnelId?: string;
|
|
46
|
+
clientInstanceId?: string;
|
|
47
|
+
remoteLookupAttempted?: boolean;
|
|
36
48
|
detail: string;
|
|
37
49
|
profilePath?: string;
|
|
38
50
|
}
|
|
@@ -90,6 +102,65 @@ export function openAiSecureTunnelConnectArgs(config: OpenAiSecureTunnelRuntimeC
|
|
|
90
102
|
return args;
|
|
91
103
|
}
|
|
92
104
|
|
|
105
|
+
function controlPlaneEvidence(value: OpenAiSecureTunnelRuntimeStatusPayload): {
|
|
106
|
+
state: OpenAiSecureTunnelRuntimeObservation['controlPlaneState'];
|
|
107
|
+
detail: string;
|
|
108
|
+
clientInstanceId?: string;
|
|
109
|
+
} {
|
|
110
|
+
let clientInstanceId: string | undefined;
|
|
111
|
+
let latestControlPlane: { level?: string; msg?: string; error?: string } | undefined;
|
|
112
|
+
const tail = value.local?.log?.tail;
|
|
113
|
+
if (typeof tail === 'string') {
|
|
114
|
+
for (const line of tail.trim().split('\n').reverse()) {
|
|
115
|
+
try {
|
|
116
|
+
const event = JSON.parse(line) as Record<string, unknown>;
|
|
117
|
+
if (!clientInstanceId && typeof event.client_instance_id === 'string') clientInstanceId = event.client_instance_id;
|
|
118
|
+
if (!latestControlPlane && event.component === 'controlplane') {
|
|
119
|
+
latestControlPlane = {
|
|
120
|
+
level: typeof event.level === 'string' ? event.level : undefined,
|
|
121
|
+
msg: typeof event.msg === 'string' ? event.msg : undefined,
|
|
122
|
+
error: typeof event.error === 'string' ? event.error : undefined,
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
if (clientInstanceId && latestControlPlane) break;
|
|
126
|
+
} catch { /* bounded diagnostic tail may contain non-JSON lines */ }
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
const remoteError = typeof value.remote_error === 'string' ? value.remote_error.trim() : '';
|
|
131
|
+
if (value.remote_lookup_attempted === true) {
|
|
132
|
+
if (remoteError) return { state: 'degraded', detail: `remote lookup failed: ${remoteError}`, ...(clientInstanceId ? { clientInstanceId } : {}) };
|
|
133
|
+
if (value.remote && typeof value.remote === 'object') {
|
|
134
|
+
return { state: 'ready', detail: 'remote tunnel lookup succeeded', ...(clientInstanceId ? { clientInstanceId } : {}) };
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
const pollHealth = value.control_plane_poll_health ?? value.local?.control_plane_poll_health;
|
|
139
|
+
const pollState = pollHealth?.state?.trim().toLowerCase();
|
|
140
|
+
if (pollState === 'ready' || pollState === 'healthy') {
|
|
141
|
+
return { state: 'ready', detail: pollHealth?.reason?.trim() || `control-plane poll state is ${pollState}`, ...(clientInstanceId ? { clientInstanceId } : {}) };
|
|
142
|
+
}
|
|
143
|
+
if (pollState === 'degraded' || pollState === 'error' || pollState === 'failed') {
|
|
144
|
+
return { state: 'degraded', detail: pollHealth?.reason?.trim() || `control-plane poll state is ${pollState}`, ...(clientInstanceId ? { clientInstanceId } : {}) };
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (latestControlPlane) {
|
|
148
|
+
const message = [latestControlPlane.msg, latestControlPlane.error].filter(Boolean).join(': ');
|
|
149
|
+
if (/poller recovered|polling operational/i.test(message)) {
|
|
150
|
+
return { state: 'ready', detail: message, ...(clientInstanceId ? { clientInstanceId } : {}) };
|
|
151
|
+
}
|
|
152
|
+
if (latestControlPlane.level === 'WARN' || latestControlPlane.level === 'ERROR' || /timed out|timeout|deadline|reset by peer|lookup .*i\/o timeout|context canceled/i.test(message)) {
|
|
153
|
+
return { state: 'degraded', detail: message || 'latest control-plane event is degraded', ...(clientInstanceId ? { clientInstanceId } : {}) };
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
return {
|
|
158
|
+
state: 'unknown',
|
|
159
|
+
detail: pollHealth?.reason?.trim() || 'remote control-plane reachability was not established',
|
|
160
|
+
...(clientInstanceId ? { clientInstanceId } : {}),
|
|
161
|
+
};
|
|
162
|
+
}
|
|
163
|
+
|
|
93
164
|
export function parseOpenAiSecureTunnelRuntimeStatus(
|
|
94
165
|
stdout: string,
|
|
95
166
|
expected: Pick<OpenAiSecureTunnelRuntimeConfig, 'alias' | 'tunnelId' | 'mcpServerUrl'>,
|
|
@@ -105,6 +176,8 @@ export function parseOpenAiSecureTunnelRuntimeStatus(
|
|
|
105
176
|
ready: false,
|
|
106
177
|
tunnelMatches: false,
|
|
107
178
|
endpointMatches: false,
|
|
179
|
+
controlPlaneState: 'unknown',
|
|
180
|
+
controlPlaneDetail: 'status payload was not valid JSON',
|
|
108
181
|
alias: expected.alias,
|
|
109
182
|
tunnelId: expected.tunnelId,
|
|
110
183
|
detail: 'OpenAI tunnel runtime status was not valid JSON',
|
|
@@ -115,14 +188,21 @@ export function parseOpenAiSecureTunnelRuntimeStatus(
|
|
|
115
188
|
const ready = value.ready === true;
|
|
116
189
|
const tunnelMatches = value.tunnel_id === expected.tunnelId;
|
|
117
190
|
const endpointMatches = tunnelRuntimeProfileTargetsEndpoint(value.profile_path, expected.mcpServerUrl);
|
|
118
|
-
const
|
|
191
|
+
const controlPlane = controlPlaneEvidence(value);
|
|
192
|
+
// `process_running` only describes runtimes that tunnel-client supervises
|
|
193
|
+
// itself. An externally owned service manager (launchd/systemd) reports the
|
|
194
|
+
// runtime as not running while the tunnel is healthy, reachable, and bound to
|
|
195
|
+
// the expected identity. Health, readiness, tunnel id, and endpoint binding are
|
|
196
|
+
// the transport facts; process registry state is diagnostic only.
|
|
197
|
+
const localReady = healthy && ready && tunnelMatches && endpointMatches;
|
|
198
|
+
const ok = localReady && controlPlane.state === 'ready';
|
|
119
199
|
const mismatches: string[] = [];
|
|
120
200
|
if (!tunnelMatches && value.tunnel_id) mismatches.push(`tunnel id mismatch (${value.tunnel_id})`);
|
|
121
201
|
if (!endpointMatches && value.profile_path) mismatches.push('runtime profile targets a different MCP endpoint');
|
|
122
|
-
if (!
|
|
123
|
-
if (
|
|
124
|
-
if (running && !ready) mismatches.push('runtime is not ready');
|
|
202
|
+
if (!healthy) mismatches.push(`runtime is ${value.runtime_state ?? 'not running'}`);
|
|
203
|
+
if (healthy && !ready) mismatches.push('runtime is not ready');
|
|
125
204
|
if (value.error) mismatches.push(value.error);
|
|
205
|
+
if (controlPlane.state !== 'ready') mismatches.push(`control plane ${controlPlane.state}: ${controlPlane.detail}`);
|
|
126
206
|
return {
|
|
127
207
|
ok,
|
|
128
208
|
running,
|
|
@@ -130,10 +210,14 @@ export function parseOpenAiSecureTunnelRuntimeStatus(
|
|
|
130
210
|
ready,
|
|
131
211
|
tunnelMatches,
|
|
132
212
|
endpointMatches,
|
|
213
|
+
controlPlaneState: controlPlane.state,
|
|
214
|
+
controlPlaneDetail: controlPlane.detail,
|
|
133
215
|
alias: expected.alias,
|
|
134
216
|
tunnelId: expected.tunnelId,
|
|
135
217
|
...(value.tunnel_id ? { observedTunnelId: value.tunnel_id } : {}),
|
|
136
|
-
|
|
218
|
+
...(controlPlane.clientInstanceId ? { clientInstanceId: controlPlane.clientInstanceId } : {}),
|
|
219
|
+
...(value.remote_lookup_attempted === undefined ? {} : { remoteLookupAttempted: value.remote_lookup_attempted }),
|
|
220
|
+
detail: ok ? `managed runtime ${expected.alias} is locally ready and its OpenAI control plane is reachable for ${expected.tunnelId}` : (mismatches.join('; ') || 'OpenAI tunnel runtime is not ready'),
|
|
137
221
|
...(value.profile_path ? { profilePath: value.profile_path } : {}),
|
|
138
222
|
};
|
|
139
223
|
}
|
|
@@ -4,12 +4,12 @@
|
|
|
4
4
|
{
|
|
5
5
|
"id": "desktop_operator",
|
|
6
6
|
"name": "Forge Desktop Operator",
|
|
7
|
-
"version": "0.
|
|
7
|
+
"version": "0.4.5",
|
|
8
8
|
"description": "Native macOS desktop automation provider for Forge.",
|
|
9
9
|
"repository": "https://github.com/moretea-labs/forge-desktop-operator.git",
|
|
10
|
-
"ref": "v0.
|
|
10
|
+
"ref": "v0.4.5",
|
|
11
11
|
"installer": "forge-plugin-install.mjs",
|
|
12
|
-
"providerVersion": "0.
|
|
12
|
+
"providerVersion": "0.4.5",
|
|
13
13
|
"protocolVersion": "1.0",
|
|
14
14
|
"platforms": [
|
|
15
15
|
"darwin"
|
|
@@ -17,8 +17,10 @@
|
|
|
17
17
|
"semanticCapabilities": [
|
|
18
18
|
"computer.observe.v1",
|
|
19
19
|
"computer.input.v1",
|
|
20
|
+
"computer.console.unlock.v1",
|
|
20
21
|
"computer.capture.v1",
|
|
21
|
-
"computer.
|
|
22
|
+
"computer.element.observe.v2",
|
|
23
|
+
"computer.element.action.v2"
|
|
22
24
|
]
|
|
23
25
|
},
|
|
24
26
|
{
|
|
@@ -16,6 +16,31 @@ Forge owns the workflow contract. External host skills may improve reasoning qua
|
|
|
16
16
|
|
|
17
17
|
Missing external skills must degrade only the named enhancement. Hooks may suggest Forge modes, but must not route correctness through a third-party skill.
|
|
18
18
|
|
|
19
|
+
## Architecture Migration Delivery Transaction
|
|
20
|
+
|
|
21
|
+
### Current-Task Lineage Fence
|
|
22
|
+
|
|
23
|
+
Every controller round has one semantic task lineage. Resolve it from the user's current objective and the exact Work/Requirement/Plan relation before consulting repository-wide status. Global active Work is conflict metadata, not a work queue.
|
|
24
|
+
|
|
25
|
+
The controller may inspect unrelated active Work only to answer four bounded questions: does it own an overlapping write path, does it hold an authority/resource needed by the current lineage, does it create a merge/release admission blocker, or does it prove the current lineage was superseded by an explicit typed transition? If none apply, the unrelated Work is ignored for the rest of the round.
|
|
26
|
+
|
|
27
|
+
Never pivot into another Work's implementation, verification, review, release, cleanup, or progress accounting without explicit user intent or an explicit typed lineage transition. This prevents autonomous continuation from turning repository activity into accidental scope expansion.
|
|
28
|
+
|
|
29
|
+
For Kernel/V2 migrations and similarly cross-cutting work, the primary unit of execution is a coherent delivery slice, not an issue, file, failing check, or incidental symptom.
|
|
30
|
+
|
|
31
|
+
1. **Batch discovery**: gather the relevant architecture, ownership, runtime, persistence, and evidence facts in one bounded pass.
|
|
32
|
+
2. **Root-cause decision**: identify the owning invariant/module and decide whether new observations are same-root scope or an independent delivery boundary.
|
|
33
|
+
3. **Freeze the slice**: establish one candidate scope and acceptance contract. Same-root discoveries extend this candidate; they do not create sibling Works by default.
|
|
34
|
+
4. **Coherent implementation**: implement the whole slice before broad validation. Use edit-session/savepoint state rather than per-file Git commits.
|
|
35
|
+
5. **Whole-candidate review**: review authority, dependency, migration, lifecycle, recovery, and diff coherence once the implementation is source-complete.
|
|
36
|
+
6. **Validation wave**: run focused behavior checks first, then canonical gates once. After repair, rerun only affected checks unless evidence invalidates the architecture.
|
|
37
|
+
7. **Delivery boundary**: commit/merge once for the coherent candidate, then perform Candidate/canary/release once. Release must not begin while same-delivery source-changing Works remain active.
|
|
38
|
+
8. **Terminal cleanup**: retire superseded Work/Plan/check/release state so the Controller's active inventory represents only real remaining work.
|
|
39
|
+
|
|
40
|
+
Do not turn concrete incidents into an issue-by-issue patch campaign. Cluster symptoms by architecture owner and invariant before implementation. One-file commits are acceptable only when that file is itself the complete coherent delivery; file count is not the boundary.
|
|
41
|
+
|
|
42
|
+
If execution exposes a defect in Forge lifecycle machinery, decide whether it is required for the current version to converge safely. If not, record a bounded post-version Plan with the violated invariant, evidence, intended owner, and acceptance criteria, then continue the current release without implementing that machinery refactor.
|
|
43
|
+
|
|
19
44
|
## Public CLI Boundary
|
|
20
45
|
|
|
21
46
|
The user-facing CLI is intentionally smaller than the implementation surface:
|
|
@@ -9,6 +9,25 @@ Use this content for user-level `~/.codex/AGENTS.md` and `~/.claude/CLAUDE.md` a
|
|
|
9
9
|
- Act as an engineering collaborator: finish the concrete task, verify it, then report conclusion, actual change, reason, verification, and residual risk.
|
|
10
10
|
- Prefer direct execution over repeated confirmation. Stop to ask only when continuing would likely produce output contrary to the user's intent.
|
|
11
11
|
|
|
12
|
+
## Delivery Transaction Discipline
|
|
13
|
+
|
|
14
|
+
### Current Task Lineage Isolation
|
|
15
|
+
|
|
16
|
+
- Select the current task from explicit user intent plus the exact Work/Requirement/Plan lineage. Never treat repository-wide active Work as the current task queue.
|
|
17
|
+
- Global status/inventory exists for conflict, ownership, blocker, and release-admission checks only. Unrelated active Work must not trigger discovery, edits, validation, review, release, cleanup, or progress accounting in the current controller round.
|
|
18
|
+
- Switch tasks only on explicit user intent or an explicit typed lineage transition. Similar files, shared broad Requirements, urgency, or mere visibility in status are not lineage.
|
|
19
|
+
- Report current-task progress from the current lineage only; label unrelated repository activity separately if it materially blocks the task.
|
|
20
|
+
|
|
21
|
+
For architecture migrations, self-hosting work, and other multi-file system changes, optimize for one bounded delivery transaction instead of repeated micro-lifecycles.
|
|
22
|
+
|
|
23
|
+
- Batch facts first, decide the root cause/owner once, freeze the delivery scope, implement the coherent candidate, review the whole diff, run one focused validation wave, run canonical gates once, then deliver and clean up.
|
|
24
|
+
- A newly observed symptom or affected file does not justify a new Work. Extend the current candidate when the same architecture invariant/root cause still owns the change; create a sibling Work only for a genuinely independent authority, security boundary, delivery, or architecture decision.
|
|
25
|
+
- Do not use Git commits as scratch savepoints. Prefer Work/edit-session/savepoint state during implementation and one coherent candidate commit at the delivery boundary.
|
|
26
|
+
- Do not repeatedly run baseline checks, broad suites, Candidate builds, canaries, or Runtime activation while source-changing work for the same delivery remains active. Release begins only after the source candidate is frozen.
|
|
27
|
+
- After a check failure, repair the same candidate and rerun the affected check set unless the evidence invalidates the architecture; do not restart the whole lifecycle by default.
|
|
28
|
+
- Retire superseded/covered Work and Plan state promptly. Active lifecycle records must describe real remaining work, not historical debris.
|
|
29
|
+
- When the workflow machinery itself has a design problem, capture a bounded follow-up Plan. If the current version can still converge safely, defer that machinery refactor until after the release instead of expanding the release scope.
|
|
30
|
+
|
|
12
31
|
## Progressive Due Diligence
|
|
13
32
|
|
|
14
33
|
For non-trivial engineering work, do P1/P2/P3 before design decisions or code edits.
|