@moretea-labs/forge 1.8.0 → 1.8.2
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 +73 -7
- package/CHANGELOG.md +34 -0
- package/CLAUDE.md +17 -2
- package/CONTRIBUTING.md +3 -2
- package/README.en.md +24 -3
- package/README.md +10 -6
- package/README.zh-CN.md +5 -3
- package/THIRD_PARTY_NOTICES.md +1 -2
- package/adapters/chatgpt/browser-delivery-host.ts +7 -1
- package/adapters/chatgpt/browser-delivery-runtime.ts +531 -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 +229 -2
- package/adapters/chatgpt/work-conversation-binding-store.ts +99 -54
- package/adapters/computer/chatgpt-extension-target.ts +708 -0
- package/adapters/computer/chrome-extension/background.js +541 -0
- package/adapters/computer/chrome-extension/content.js +402 -0
- package/adapters/computer/chrome-extension/core.js +24 -0
- package/adapters/computer/chrome-extension/manifest.json +32 -0
- package/adapters/computer/desktop-operator-discovery.ts +11 -31
- package/adapters/computer/desktop-operator-provider.ts +18 -6
- package/adapters/computer/interaction-target-authority.ts +178 -24
- package/adapters/mcp/access-mode.ts +18 -39
- package/adapters/mcp/auth.ts +2 -7
- package/adapters/mcp/instructions.ts +6 -5
- package/adapters/mcp/multi-repository.ts +10 -47
- package/adapters/mcp/oauth.ts +73 -0
- package/adapters/mcp/paths.ts +4 -1
- 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 +490 -113
- package/adapters/mcp/runtime-gateway/context-pack-sidecar.ts +83 -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 +14 -54
- package/adapters/mcp/runtime-gateway/gateway-execution-adapter.ts +2 -1
- package/adapters/mcp/runtime-gateway/plugin-adapter.ts +132 -47
- package/adapters/mcp/runtime-gateway/process-tools.ts +173 -23
- 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 +19 -507
- package/adapters/mcp/runtime-gateway/routing-policy.ts +506 -0
- package/adapters/mcp/runtime-gateway/runtime-observation-adapter.ts +15 -66
- package/adapters/mcp/runtime-gateway/runtime-readiness-observation.ts +218 -0
- package/adapters/mcp/runtime-gateway/runtime-tool-definitions.ts +123 -178
- package/adapters/mcp/runtime-gateway/runtime-tools.ts +11 -5
- package/adapters/mcp/runtime-gateway/scheduler-adapter.ts +37 -1
- package/adapters/mcp/runtime-gateway/shared-adapter.ts +20 -0
- package/adapters/mcp/runtime-gateway/status-inbox-adapter.ts +416 -270
- package/adapters/mcp/runtime-gateway/typescript-navigation-sidecar.ts +140 -0
- package/adapters/mcp/runtime-gateway/work-adapter.ts +262 -3123
- 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 +229 -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 +320 -0
- package/adapters/mcp/runtime-gateway/work-workflow-operations.ts +113 -0
- package/adapters/mcp/runtime-gateway/workflow-supervisor-adapter.ts +180 -0
- package/adapters/mcp/server.ts +91 -26
- package/adapters/mcp/setup.ts +5 -13
- package/adapters/mcp/tool-mapping/access-tools.ts +0 -6
- package/adapters/mcp/tool-mapping/legacy-tool-service.ts +703 -3092
- package/adapters/mcp/tool-mapping/post-finalize-work-attribution.ts +5 -10
- package/adapters/mcp/tool-mapping/repository-tools.ts +170 -213
- package/adapters/mcp/toolset-names.ts +11 -37
- package/adapters/mcp/toolset.ts +19 -92
- package/{src/cli → adapters}/mcp/transports/AGENTS.md +5 -5
- package/{src/cli → adapters}/mcp/transports/CLAUDE.md +5 -5
- package/adapters/mcp/transports/http-observation.ts +377 -0
- package/adapters/mcp/transports/http-post-lifecycle.ts +19 -0
- package/adapters/mcp/transports/http.ts +200 -683
- 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/adapters/mcp/types.ts +0 -6
- package/assets/hooks/lib/session-state.sh +0 -38
- package/assets/hooks/post-bash.sh +1 -1
- package/assets/hooks/post-tool-observer.sh +0 -5
- package/assets/hooks/prompt-guard.sh +0 -55
- package/assets/partials/04-project-structure.partial.md +1 -1
- package/assets/partials/07-footer.partial.md +1 -1
- package/assets/partials-agents/02-operating-mode.partial.md +1 -1
- package/assets/partials-agents/08-deep-docs.partial.md +1 -1
- package/assets/plugin-registry.v1.json +6 -4
- package/assets/reference-configs/agentic-development-flow.md +26 -1
- package/assets/reference-configs/external-tooling.md +0 -76
- package/assets/reference-configs/global-working-rules.md +19 -0
- package/assets/reference-configs/release-deploy.md +1 -1
- package/assets/templates/helpers/architecture-queue.sh +1 -1
- package/assets/templates/helpers/check-agent-tooling.sh +1 -327
- package/assets/templates/helpers/check-task-workflow.sh +1 -1
- package/assets/templates/helpers/ensure-task-workflow.sh +0 -14
- package/assets/templates/helpers/migrate-project-template.sh +2 -2
- package/assets/workflow-contract.v1.json +0 -2
- package/docs/README.md +5 -2
- package/docs/architecture/CURRENT.md +205 -51
- 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/20260904-ios-device-migration-baseline.md +42 -0
- 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 +234 -0
- package/docs/architecture/decisions/20260919-autonomous-work-liveness.md +62 -0
- package/docs/architecture/decisions/20260924-thin-semantic-working-context.md +74 -0
- package/docs/architecture/decisions/20261001-supervisor-turn-authority.md +342 -0
- package/docs/architecture/decisions/20261005-forge-v3-desktop-client-boundary.md +77 -0
- package/docs/architecture/decisions/20261009-exact-code-context-discovery.md +19 -0
- package/docs/architecture/decisions/20261009-generated-xcode-cache-retention.md +37 -0
- package/docs/architecture/decisions/20261011-autonomous-continuation-convergence.md +597 -0
- package/docs/architecture/decisions/20261017-edit-session-finalization-reconciliation.md +17 -0
- package/docs/architecture/decisions/20261019-retire-host-skills.md +16 -0
- package/docs/architecture/diagrams/agentic-dev-plugin-architecture.html +1 -1
- package/docs/architecture/domains/public-surface.md +3 -5
- package/docs/architecture/domains/verification.md +6 -5
- package/docs/architecture/domains/workflow-engine.md +6 -0
- package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +3 -3
- package/docs/architecture/requests/2026-09-04-computer-plugin-workflow-taxonomy.md +4 -4
- package/docs/architecture/reviews/20261015-code-search-runtime-acceptance.md +75 -0
- package/docs/forge-plugin-management.md +16 -4
- package/docs/operations/controller-browser-plugin.md +23 -0
- package/docs/operations/controller-performance-and-502.md +17 -0
- package/docs/operations/controller-reliability-runbook.md +2 -2
- package/docs/operations/features.md +6 -7
- package/docs/operations/features.zh-CN.md +1 -1
- package/docs/operations/github-repository.md +4 -4
- package/docs/operations/github-semantic-release-projection.md +20 -0
- package/docs/operations/homebrew.md +2 -2
- package/docs/operations/mcp-tool-exposure.md +13 -52
- package/docs/operations/releasing.md +2 -0
- package/docs/operations/runtime-performance-diagnostics.md +8 -0
- package/docs/operations/standalone-disaster-recovery.md +15 -7
- package/docs/tutorials/01-install-and-start.zh-CN.md +1 -1
- package/docs/tutorials/03-first-repository-task.md +2 -0
- package/docs/wiki/Computer-and-Plugins.md +78 -0
- package/docs/wiki/Computer-and-Plugins.zh-CN.md +88 -0
- package/docs/wiki/Home.md +16 -2
- package/docs/wiki/Implementation.md +25 -13
- package/docs/wiki/Installation.md +28 -32
- package/docs/wiki/Integrations.md +4 -0
- package/docs/wiki/Quick-Start.md +35 -22
- package/docs/wiki/Releases-and-Upgrades.md +15 -25
- package/docs/wiki/_Sidebar.md +5 -1
- package/package.json +25 -25
- 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 +348 -97
- 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 +87 -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/eligibility.ts +4 -1
- 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 +166 -143
- package/packages/kernel/work/api/index.ts +3 -2
- package/packages/kernel/work/application/work-service.ts +18 -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 +34 -65
- package/packages/kernel/work/domain/types.ts +57 -51
- package/packages/kernel/work/infrastructure/work-contract-store.ts +847 -729
- package/packages/kernel/work/ports/work-contract-store.ts +5 -2
- package/packages/plugin-runtime/browser/provider-registry.ts +12 -36
- 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 +153 -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/assemble-template.ts +11 -72
- package/scripts/canary-controller-semantic-wait.ts +12 -24
- package/scripts/check-mcp-compatibility.ts +94 -201
- package/scripts/check-package-identity.mjs +0 -5
- package/scripts/check-runtime-architecture.mjs +921 -239
- package/scripts/forge-local-recovery-helper.mjs +93 -15
- package/scripts/init-project.sh +0 -46
- package/scripts/install-ios-hid-toolchain.sh +60 -0
- package/scripts/install-standalone-recovery.ts +1 -1
- package/scripts/lib/project-init-lib.sh +6 -3
- package/scripts/migrate-windows-wsl-controller-home.ts +2 -0
- package/scripts/open-source-audit-allowlist.txt +1 -3
- package/scripts/run-bounded-command.mjs +5 -0
- package/scripts/run-governed-gate.ts +2 -1
- package/scripts/run-mobile-monitor-bridge.ts +0 -1
- package/scripts/smoke-control-plane-failure-domain.ts +94 -0
- package/scripts/smoke-ios-physical-device.ts +38 -10
- package/scripts/smoke-mcp-http-runtime.ts +12 -8
- package/scripts/smoke-mcp-tool-surface.ts +27 -41
- 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 +95 -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/sync-generated-authority.mjs +0 -1
- package/scripts/verify-forge-runtime.sh +2 -1
- package/src/cli/agent-jobs/executor-health.ts +12 -35
- package/src/cli/agent-jobs/integration.ts +20 -51
- package/src/cli/agent-jobs/job-manager.ts +7 -3
- package/src/cli/chatgpt-browser/file-policy.ts +2 -2
- 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/doctor.ts +1 -143
- package/src/cli/commands/global-runtime.ts +3 -196
- package/src/cli/commands/init-hook.ts +6 -23
- package/src/cli/commands/init.ts +3 -252
- package/src/cli/commands/mcp.ts +0 -5
- package/src/cli/commands/prompt-guard-decision.ts +0 -2
- package/src/cli/commands/recovery.ts +137 -135
- package/src/cli/commands/repository.ts +17 -1
- package/src/cli/commands/runtime.ts +3 -3
- package/src/cli/commands/setup-execution.ts +41 -0
- 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 +5 -44
- package/src/cli/controller/completion-target.ts +3 -72
- package/src/cli/controller/composite-operations.ts +1 -1
- package/src/cli/controller/context/instruction-context.ts +1 -1
- package/src/cli/controller/context/known-paths.ts +2 -2
- package/src/cli/controller/context/query-planning.ts +11 -4
- package/src/cli/controller/context/rule-resolution.ts +2 -2
- package/src/cli/controller/context/source-materializer.ts +83 -102
- package/src/cli/controller/context/structural-provider.ts +84 -0
- package/src/cli/controller/context/structural.ts +6 -6
- package/src/cli/controller/context/types.ts +8 -9
- package/src/cli/controller/context-pack.ts +107 -84
- 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 +25 -52
- 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 +42 -10
- package/src/cli/editing/edit-session.ts +18 -12
- package/src/cli/github/github.ts +4 -4
- package/src/cli/github/semantic-projection.ts +174 -0
- package/src/cli/hook/prompt-intents.ts +0 -9
- package/src/cli/index.ts +1 -43
- package/src/cli/local-bridge/connector-freshness.ts +1 -12
- package/src/cli/local-bridge/console-automations.ts +19 -14
- package/src/cli/local-bridge/console-view-models.ts +4 -13
- package/src/cli/local-bridge/facade-api.ts +64 -193
- 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-classifier.ts +0 -9
- package/src/cli/repositories/command-executor.ts +29 -23
- package/src/cli/repositories/command-scope.ts +37 -18
- 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/locks.ts +20 -0
- package/src/cli/repositories/registry.ts +79 -12
- package/src/cli/repositories/safe-patch.ts +2 -12
- package/src/cli/repositories/structured-git.ts +101 -5
- package/src/cli/repository/inspector.ts +185 -343
- package/src/cli/repository/session-cache.ts +92 -6
- package/src/effects/process-runner.ts +17 -1
- 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/generic-lsp-provider.ts +1 -1
- package/src/runtime/context/semantic-navigation-contract.ts +16 -0
- package/src/runtime/context/semantic-navigation.ts +7 -35
- 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 +215 -12
- 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 +616 -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 +197 -110
- package/src/runtime/control-plane/execution/work-handle-store.ts +72 -44
- 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 +60 -34
- package/src/runtime/control-plane/execution/work-preparation-service.ts +231 -267
- package/src/runtime/control-plane/execution/work-process-evidence.ts +2 -2
- 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 +201 -199
- package/src/runtime/control-plane/execution/work-validation-reconciler.ts +33 -85
- package/src/runtime/control-plane/execution/work-verification-service.ts +406 -50
- package/src/runtime/control-plane/execution/work-verification-snapshot.ts +6 -2
- package/src/runtime/control-plane/facade/capability-registry.ts +364 -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 +9 -9
- package/src/runtime/control-plane/generated-cache-retention.ts +206 -59
- 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 +256 -117
- package/src/runtime/control-plane/global-scheduler/reconciliation.ts +3 -12
- 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/governance/external-effects.ts +1 -1
- package/src/runtime/control-plane/launcher/chatgpt-round-continuation.ts +63 -300
- package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +29 -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 +295 -107
- 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/evidence/sensitive-output.ts +22 -2
- package/src/runtime/execution/jobs/child-reference.ts +11 -0
- package/src/runtime/execution/jobs/receipt-recovery.ts +1 -11
- 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 +82 -30
- 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 +55 -18
- package/src/runtime/execution/process-runtime/store.ts +30 -2
- package/src/runtime/execution/process-runtime/types.ts +6 -1
- package/src/runtime/execution/thin-harness/execution-router.ts +16 -16
- package/src/runtime/execution/thin-harness/fast-executor.ts +2 -2
- package/src/runtime/execution/thin-harness/search-worker.ts +2 -2
- package/src/runtime/execution/workers/executor.ts +3 -8
- package/src/runtime/maintenance/cleanup.ts +41 -5
- 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 +97 -29
- package/src/runtime/plugins/app-store-connect-adapter.ts +290 -18
- package/src/runtime/plugins/browser-adapter.ts +1124 -333
- package/src/runtime/plugins/browser-automation-service.ts +23 -3
- package/src/runtime/plugins/browser-macos-bridge.ts +431 -137
- package/src/runtime/plugins/browser-manifest-surface.ts +47 -12
- package/src/runtime/plugins/browser-native-session-service.ts +63 -13
- package/src/runtime/plugins/browser-registration.ts +18 -0
- package/src/runtime/plugins/browser-runtime.ts +40 -36
- 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 +228 -0
- package/src/runtime/plugins/computer-registration.ts +298 -20
- 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/github-adapter.ts +30 -3
- package/src/runtime/plugins/gmail-adapter.ts +189 -3
- package/src/runtime/plugins/google-calendar-adapter.ts +2 -2
- package/src/runtime/plugins/google-shared.ts +24 -12
- package/src/runtime/plugins/google-tasks-adapter.ts +2 -2
- package/src/runtime/plugins/ios/remote-xpc-hid.ts +70 -30
- package/src/runtime/plugins/ios-adapter.ts +46 -41
- package/src/runtime/plugins/ios-agent-device.ts +54 -4
- package/src/runtime/plugins/ios-device-ownership.ts +103 -0
- package/src/runtime/plugins/ios-physical-device.ts +497 -79
- package/src/runtime/plugins/lightweight-action.ts +208 -2
- package/src/runtime/plugins/local-system-adapter.ts +12 -100
- 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 +682 -435
- package/src/runtime/plugins/types.ts +24 -0
- package/src/runtime/plugins/xiaohongshu-publish.ts +16 -0
- package/src/runtime/projections/git-status-sampler.ts +2 -19
- package/src/runtime/projections/invalidation.ts +7 -3
- package/src/runtime/projections/materialized-view.ts +80 -36
- package/src/runtime/recovery/actions.ts +4 -4
- package/src/runtime/recovery/capability-action-application.ts +46 -45
- package/src/runtime/recovery/lifecycle-inventory.ts +30 -40
- package/src/runtime/recovery/maintenance-executor.ts +374 -123
- package/src/runtime/recovery/probe.ts +2 -2
- package/src/runtime/recovery/store.ts +29 -11
- package/src/runtime/release/release-coordinator.ts +312 -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 +254 -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 +227 -29
- package/src/runtime/root/package-runtime-release.ts +117 -30
- package/src/runtime/root/package-runtime-service.ts +24 -24
- package/src/runtime/root/release-execution-canary.ts +57 -12
- package/src/runtime/root/release-loader.ts +90 -8
- package/src/runtime/root/release-manifest.ts +80 -40
- package/src/runtime/root/release-materialize.ts +372 -106
- package/src/runtime/root/release-store.ts +395 -36
- package/src/runtime/root/runtime-lane.ts +191 -0
- package/src/runtime/root/runtime.ts +160 -29
- package/src/runtime/root/scheduled-controller-composition.ts +29 -3
- package/src/runtime/root/service-runner.ts +2 -2
- package/src/runtime/root/service.ts +126 -44
- package/src/runtime/root/status.ts +48 -0
- package/src/runtime/root/types.ts +32 -9
- package/src/runtime/root/workflow-supervisor-composition.ts +681 -0
- package/src/runtime/root/workflow-supervisor-runtime.ts +564 -0
- package/src/runtime/root/write-fence.ts +50 -2
- package/src/runtime/safe-tooling/google-credential-store.ts +67 -22
- package/src/runtime/safe-tooling/google-oauth-broker.ts +25 -7
- package/src/runtime/safe-tooling/ios-development.ts +22 -8
- package/src/runtime/safe-tooling/job-summary.ts +1 -1
- package/src/runtime/safe-tooling/workspace-auth.ts +5 -2
- package/src/runtime/shared/atomic-text-file.ts +21 -0
- package/src/runtime/shared/bounded-child-supervisor.ts +9 -1
- package/src/runtime/shared/json-files.ts +8 -3
- 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 +3792 -417
- package/src/runtime/standalone-recovery/entry.ts +507 -45
- 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 +12 -2
- 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 +11 -3
- package/src/runtime/workflow/schedules/engine.ts +166 -178
- package/src/runtime/workflow/schedules/pending-activation.ts +11 -4
- package/src/runtime/workflow/schedules/work-continuation.ts +95 -11
- 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 +218 -0
- package/supervisor/control-plane.ts +963 -0
- package/supervisor/entry.ts +15 -0
- package/supervisor/forge-validators.ts +147 -0
- package/supervisor/index.ts +5 -0
- package/supervisor/native-browser-adapter.ts +1151 -0
- package/supervisor/native-messaging/host.ts +58 -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 +250 -0
- package/supervisor/server.ts +412 -0
- package/supervisor/store.ts +1870 -0
- package/supervisor/tsconfig.json +8 -0
- package/supervisor/types.ts +261 -0
- package/SKILL.md +0 -427
- 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/adapters/mcp/runtime-gateway/work-validation-reconciler.ts +0 -1
- package/adapters/mcp/toolset-selection.ts +0 -41
- package/agents/openai.yaml +0 -4
- package/assets/skill-hooks.json +0 -41
- package/assets/skill-version.json +0 -189
- package/assets/skills/claude-review/SKILL.md +0 -231
- package/assets/skills/codex-review/SKILL.md +0 -103
- package/assets/templates/helpers/check-skill-version.ts +0 -193
- 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-skill-version.ts +0 -5
- package/scripts/check-tarball-install-smoke.sh +0 -150
- package/scripts/ensure-codegraph.sh +0 -16
- 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/run-skill-hook.ts +0 -244
- package/scripts/stage-runtime-release.ts +0 -44
- package/scripts/sync-codex-installed-copies.sh +0 -182
- package/src/cli/commands/tools.ts +0 -167
- 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/cli/mcp/access-mode.ts +0 -2
- package/src/cli/mcp/access-tools.ts +0 -2
- package/src/cli/mcp/audit.ts +0 -2
- package/src/cli/mcp/auth.ts +0 -2
- package/src/cli/mcp/instructions.ts +0 -2
- package/src/cli/mcp/legacy-context.ts +0 -2
- package/src/cli/mcp/legacy-tool-service.ts +0 -2
- package/src/cli/mcp/multi-repository.ts +0 -2
- package/src/cli/mcp/oauth.ts +0 -2
- package/src/cli/mcp/openai-secure-tunnel.ts +0 -2
- package/src/cli/mcp/paths.ts +0 -2
- package/src/cli/mcp/policy.ts +0 -2
- package/src/cli/mcp/post-finalize-work-attribution.ts +0 -2
- package/src/cli/mcp/proxy-env.ts +0 -2
- package/src/cli/mcp/readiness-projection.ts +0 -2
- package/src/cli/mcp/redaction.ts +0 -2
- package/src/cli/mcp/repo.ts +0 -2
- package/src/cli/mcp/repository-tools.ts +0 -2
- package/src/cli/mcp/server.ts +0 -2
- package/src/cli/mcp/setup.ts +0 -2
- package/src/cli/mcp/tool-capability-cache.ts +0 -2
- package/src/cli/mcp/tool-contract.ts +0 -2
- package/src/cli/mcp/tools.ts +0 -2
- package/src/cli/mcp/toolset-names.ts +0 -2
- package/src/cli/mcp/toolset-selection.ts +0 -2
- package/src/cli/mcp/toolset.ts +0 -2
- package/src/cli/mcp/transports/http.ts +0 -2
- package/src/cli/mcp/transports/session-registry.ts +0 -2
- package/src/cli/mcp/transports/stdio.ts +0 -2
- package/src/cli/mcp/types.ts +0 -2
- package/src/cli/mcp/umbrella-tools.ts +0 -2
- package/src/cli/tools/codegraph.ts +0 -695
- package/src/core/adoption/helper-wrapper-plan.ts +0 -159
- package/src/runtime/context/codegraph-cache-boundary.ts +0 -255
- package/src/runtime/context/codegraph-read-provider.ts +0 -611
- package/src/runtime/context/codegraph-sidecar.cjs +0 -248
- package/src/runtime/control-plane/codegraph-cache-retention.ts +0 -164
- 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
- package/src/runtime/gateway/mcp/execution-tools.ts +0 -2
- package/src/runtime/gateway/mcp/legacy-ios-tool-adapter.ts +0 -2
- package/src/runtime/gateway/mcp/persisted-check-process.ts +0 -2
- package/src/runtime/gateway/mcp/process-tools.ts +0 -2
- package/src/runtime/gateway/mcp/router.ts +0 -2
- package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +0 -2
- package/src/runtime/gateway/mcp/runtime-tools.ts +0 -2
- package/src/runtime/gateway/mcp/work-validation-reconciler.ts +0 -2
- package/src/runtime/plugins/browser-provider-registry.ts +0 -2
|
@@ -0,0 +1,541 @@
|
|
|
1
|
+
importScripts('core.js');
|
|
2
|
+
const core = globalThis.ForgeComputerChatgptCore;
|
|
3
|
+
const NATIVE_HOST = 'com.moretea.forge.workflow_supervisor';
|
|
4
|
+
const ALARM = 'forge-computer-chatgpt-provider';
|
|
5
|
+
const PROVIDER_ID = 'browser.chrome-extension';
|
|
6
|
+
const PROVIDER_TICK_MS = 750;
|
|
7
|
+
const DISCOVERY_MIN_INTERVAL_MS = 2_000;
|
|
8
|
+
const RPC_TIMEOUT_MS = 30_000;
|
|
9
|
+
// The broker budgets 12 seconds for a claimed command; a hung content
|
|
10
|
+
// callback must settle earlier without claiming a Send never happened.
|
|
11
|
+
const TAB_MESSAGE_TIMEOUT_MS = 7_000;
|
|
12
|
+
const bootstrapTabs = new Map();
|
|
13
|
+
const randomId = () => globalThis.crypto?.randomUUID?.() ?? `${Date.now()}-${Math.random().toString(16).slice(2)}`;
|
|
14
|
+
let providerInstancePromise;
|
|
15
|
+
function providerInstanceId() {
|
|
16
|
+
// Profile-local storage survives worker and browser restarts. It identifies
|
|
17
|
+
// this Chrome profile, never a task or an outbound effect.
|
|
18
|
+
// A fresh worker UUID would invalidate every Controller-pinned tab.
|
|
19
|
+
providerInstancePromise ??= (async () => {
|
|
20
|
+
const saved = await chrome.storage.local.get('forgeComputerProviderInstanceId');
|
|
21
|
+
if (typeof saved.forgeComputerProviderInstanceId === 'string' && saved.forgeComputerProviderInstanceId) return saved.forgeComputerProviderInstanceId;
|
|
22
|
+
const instanceId = randomId();
|
|
23
|
+
await chrome.storage.local.set({ forgeComputerProviderInstanceId: instanceId });
|
|
24
|
+
return instanceId;
|
|
25
|
+
})().catch((error) => { providerInstancePromise = undefined; throw error; });
|
|
26
|
+
return providerInstancePromise;
|
|
27
|
+
}
|
|
28
|
+
const sleep = (ms) => new Promise((resolve) => setTimeout(resolve, ms));
|
|
29
|
+
function browserProduct() {
|
|
30
|
+
const ua = String(globalThis.navigator?.userAgent ?? '');
|
|
31
|
+
if (/Vivaldi/i.test(ua)) return 'vivaldi';
|
|
32
|
+
if (/Chromium/i.test(ua)) return 'chromium';
|
|
33
|
+
return 'chrome';
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
let nativePort;
|
|
37
|
+
const pendingNative = new Map();
|
|
38
|
+
function rejectNativePending(error) {
|
|
39
|
+
for (const pending of pendingNative.values()) { clearTimeout(pending.timer); pending.reject(error); }
|
|
40
|
+
pendingNative.clear();
|
|
41
|
+
}
|
|
42
|
+
function ensureNativePort() {
|
|
43
|
+
if (nativePort) return nativePort;
|
|
44
|
+
const port = chrome.runtime.connectNative(NATIVE_HOST);
|
|
45
|
+
nativePort = port;
|
|
46
|
+
port.onMessage.addListener((response) => {
|
|
47
|
+
const pending = pendingNative.get(response?.id);
|
|
48
|
+
if (!pending) return;
|
|
49
|
+
pendingNative.delete(response.id);
|
|
50
|
+
clearTimeout(pending.timer);
|
|
51
|
+
if (response?.ok === true) pending.resolve(response.result ?? {});
|
|
52
|
+
else pending.reject(new Error(response?.error?.message ?? 'COMPUTER_CHATGPT_NATIVE_RPC_FAILED'));
|
|
53
|
+
});
|
|
54
|
+
port.onDisconnect.addListener(() => {
|
|
55
|
+
if (nativePort !== port) return;
|
|
56
|
+
nativePort = undefined;
|
|
57
|
+
rejectNativePending(new Error(chrome.runtime.lastError?.message ?? 'COMPUTER_CHATGPT_NATIVE_PORT_DISCONNECTED'));
|
|
58
|
+
setTimeout(() => { void providerTick().catch(() => undefined); }, 500);
|
|
59
|
+
});
|
|
60
|
+
return port;
|
|
61
|
+
}
|
|
62
|
+
function nativeRpc(method, params = {}) {
|
|
63
|
+
return new Promise((resolve, reject) => {
|
|
64
|
+
const id = randomId();
|
|
65
|
+
const timer = setTimeout(() => {
|
|
66
|
+
pendingNative.delete(id);
|
|
67
|
+
reject(new Error(`COMPUTER_CHATGPT_NATIVE_RPC_TIMEOUT:${method}`));
|
|
68
|
+
}, RPC_TIMEOUT_MS);
|
|
69
|
+
pendingNative.set(id, { resolve, reject, timer });
|
|
70
|
+
try { ensureNativePort().postMessage({ id, method, params }); }
|
|
71
|
+
catch (error) { clearTimeout(timer); pendingNative.delete(id); reject(error); }
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
function tabMessage(tabId, message) {
|
|
75
|
+
return new Promise((resolve, reject) => {
|
|
76
|
+
let settled = false;
|
|
77
|
+
const finish = (error, response) => {
|
|
78
|
+
if (settled) return;
|
|
79
|
+
settled = true;
|
|
80
|
+
clearTimeout(timer);
|
|
81
|
+
if (error) reject(error); else resolve(response ?? {});
|
|
82
|
+
};
|
|
83
|
+
const timer = setTimeout(() => finish(new Error('COMPUTER_CHATGPT_EXTENSION_TAB_MESSAGE_TIMEOUT')), TAB_MESSAGE_TIMEOUT_MS);
|
|
84
|
+
try {
|
|
85
|
+
chrome.tabs.sendMessage(tabId, message, (response) => {
|
|
86
|
+
// Read lastError inside the callback, even if the request timed out,
|
|
87
|
+
// so Chrome does not report an unhandled transport error.
|
|
88
|
+
const error = chrome.runtime.lastError;
|
|
89
|
+
finish(error ? new Error(error.message) : undefined, response);
|
|
90
|
+
});
|
|
91
|
+
} catch (error) { finish(error); }
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
function tabGet(tabId) {
|
|
95
|
+
return new Promise((resolve, reject) => chrome.tabs.get(tabId, (tab) => {
|
|
96
|
+
const error = chrome.runtime.lastError;
|
|
97
|
+
if (error) { reject(new Error(error.message)); return; }
|
|
98
|
+
resolve(tab);
|
|
99
|
+
}));
|
|
100
|
+
}
|
|
101
|
+
function tabCreate(url) {
|
|
102
|
+
return new Promise((resolve, reject) => chrome.tabs.create({ url, active: false }, (tab) => {
|
|
103
|
+
const error = chrome.runtime.lastError;
|
|
104
|
+
if (error || !tab?.id) { reject(new Error(error?.message ?? 'COMPUTER_CHATGPT_TAB_CREATE_FAILED')); return; }
|
|
105
|
+
resolve(tab);
|
|
106
|
+
}));
|
|
107
|
+
}
|
|
108
|
+
function tabRemove(tabId) { return new Promise((resolve, reject) => chrome.tabs.remove(tabId, () => {
|
|
109
|
+
const error = chrome.runtime.lastError;
|
|
110
|
+
if (error) reject(new Error(error.message)); else resolve();
|
|
111
|
+
})); }
|
|
112
|
+
function tabReload(tabId) { return new Promise((resolve) => chrome.tabs.reload(tabId, () => resolve())); }
|
|
113
|
+
async function chatgptTabs() { return await chrome.tabs.query({ url: 'https://chatgpt.com/*' }); }
|
|
114
|
+
// Controller binding owns the durable source identity. This browser-session
|
|
115
|
+
// attestation proves that the exact physical tab was created by this extension;
|
|
116
|
+
// browser restarts invalidate it rather than permitting tab-id reuse to close
|
|
117
|
+
// an unrelated restored/user tab.
|
|
118
|
+
function ownedTabKey(tabId) { return `forgeComputerOwnedTabV1:${tabId}`; }
|
|
119
|
+
async function attestedOwnerToken(tab, pinnedBinding) {
|
|
120
|
+
if (!pinnedBinding?.ownerToken || String(pinnedBinding.tabId) !== String(tab.id)
|
|
121
|
+
|| String(pinnedBinding.windowId) !== String(tab.windowId)) return;
|
|
122
|
+
try {
|
|
123
|
+
const key = ownedTabKey(tab.id);
|
|
124
|
+
const stored = await chrome.storage.session.get(key);
|
|
125
|
+
if (stored[key] === pinnedBinding.ownerToken) return pinnedBinding.ownerToken;
|
|
126
|
+
} catch { /* missing browser-session provenance is never closure authority */ }
|
|
127
|
+
}
|
|
128
|
+
function binding(tab, instanceId, ownerToken) {
|
|
129
|
+
return {
|
|
130
|
+
providerId: PROVIDER_ID,
|
|
131
|
+
providerSessionId: instanceId,
|
|
132
|
+
observedAt: new Date().toISOString(),
|
|
133
|
+
browserProduct: browserProduct(),
|
|
134
|
+
windowId: String(tab.windowId),
|
|
135
|
+
tabId: String(tab.id),
|
|
136
|
+
...(ownerToken ? { ownerToken } : {}),
|
|
137
|
+
};
|
|
138
|
+
}
|
|
139
|
+
async function snapshot(tabId, options = {}) {
|
|
140
|
+
return await tabMessage(tabId, { type: 'forge-computer-chatgpt-snapshot', options });
|
|
141
|
+
}
|
|
142
|
+
async function restoreExactContentReceiver(tabId, identity) {
|
|
143
|
+
// The physical tab is already resolved by the one Computer target authority.
|
|
144
|
+
// A missing MV3 message receiver is not permission to reload/navigate that tab,
|
|
145
|
+
// replace its draft, or create another tab. Inject only read-only listeners on
|
|
146
|
+
// this SAME proven conversation/Project, then verify its identity again.
|
|
147
|
+
const matches = (tab) => identity?.namespace === 'chatgpt.conversation'
|
|
148
|
+
? core.parseConversation(tab.url ?? '')?.conversationId === identity.conversationId
|
|
149
|
+
: identity?.namespace === 'chatgpt.bootstrap'
|
|
150
|
+
&& core.projectId(tab.url ?? '') === core.projectId(identity.projectUrl)
|
|
151
|
+
&& !core.parseConversation(tab.url ?? '');
|
|
152
|
+
const before = await tabGet(tabId);
|
|
153
|
+
if (before.discarded || !matches(before)) throw new Error('COMPUTER_CHATGPT_EXTENSION_RECEIVER_TARGET_CHANGED');
|
|
154
|
+
await new Promise((resolve, reject) => chrome.scripting.executeScript({
|
|
155
|
+
target: { tabId }, files: ['core.js', 'content.js'],
|
|
156
|
+
}, () => {
|
|
157
|
+
const error = chrome.runtime.lastError;
|
|
158
|
+
if (error) reject(new Error(error.message)); else resolve();
|
|
159
|
+
}));
|
|
160
|
+
const after = await tabGet(tabId);
|
|
161
|
+
if (!matches(after)) throw new Error('COMPUTER_CHATGPT_EXTENSION_RECEIVER_TARGET_CHANGED');
|
|
162
|
+
}
|
|
163
|
+
async function waitForContent(tabId, requireBootstrapUi = false, recoverReceiver) {
|
|
164
|
+
let lastError;
|
|
165
|
+
let recoveryAttempted = false;
|
|
166
|
+
// Bootstrap is a new provider page: seeing its URL proves location, not
|
|
167
|
+
// that the React composer is hydrated. The Supervisor uses the user's
|
|
168
|
+
// exact target page readiness. Bound this wait below the
|
|
169
|
+
// broker's 12-second timeout; missing composer never authorizes Send.
|
|
170
|
+
const attempts = requireBootstrapUi ? 20 : 40;
|
|
171
|
+
for (let attempt = 0; attempt < attempts; attempt += 1) {
|
|
172
|
+
try {
|
|
173
|
+
const tab = await tabGet(tabId);
|
|
174
|
+
if (tab?.discarded) await tabReload(tabId);
|
|
175
|
+
const observation = await snapshot(tabId);
|
|
176
|
+
if (typeof observation?.url === 'string' && observation.url
|
|
177
|
+
&& (!requireBootstrapUi || typeof observation.composerText === 'string')) return observation;
|
|
178
|
+
} catch (error) {
|
|
179
|
+
lastError = error;
|
|
180
|
+
if (!recoveryAttempted && recoverReceiver
|
|
181
|
+
&& /Receiving end does not exist|Could not establish connection/i.test(String(error?.message ?? error))) {
|
|
182
|
+
recoveryAttempted = true;
|
|
183
|
+
// Reattach the receiver only after exact tab/Project revalidation.
|
|
184
|
+
// Never retry the original Send: this is an observation-only preflight.
|
|
185
|
+
await recoverReceiver();
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
await sleep(250);
|
|
189
|
+
}
|
|
190
|
+
throw lastError ?? new Error('COMPUTER_CHATGPT_TAB_NOT_READY');
|
|
191
|
+
}
|
|
192
|
+
function matchingTabs(tabs, identity, pinnedBinding) {
|
|
193
|
+
if (identity?.namespace === 'chatgpt.conversation') {
|
|
194
|
+
const exact = tabs.filter((tab) => core.parseConversation(tab.url ?? '')?.conversationId === identity.conversationId);
|
|
195
|
+
if (pinnedBinding?.providerId === PROVIDER_ID && pinnedBinding.tabId && pinnedBinding.windowId) {
|
|
196
|
+
const pinned = exact.filter((tab) => String(tab.id) === String(pinnedBinding.tabId));
|
|
197
|
+
if (pinned.length === 1) return pinned;
|
|
198
|
+
}
|
|
199
|
+
// Duplicate exact URLs without a verifiable pin must never select an
|
|
200
|
+
// arbitrary foreground tab (or silently create another one).
|
|
201
|
+
return exact;
|
|
202
|
+
}
|
|
203
|
+
if (identity?.namespace === 'chatgpt.bootstrap') {
|
|
204
|
+
// The Project is a scope, not a tab identity. Multiple tasks share the
|
|
205
|
+
// same Project; adopting its arbitrary new-chat tab steals another task's
|
|
206
|
+
// binding and violates the Controller's unique Computer surface index.
|
|
207
|
+
const projectId = core.projectId(identity.projectUrl);
|
|
208
|
+
const belongsToProject = (tab) => String(tab.url ?? '') === identity.projectUrl
|
|
209
|
+
|| Boolean(projectId && core.projectId(tab.url ?? '') === projectId);
|
|
210
|
+
if (pinnedBinding?.providerId === PROVIDER_ID && pinnedBinding.tabId && pinnedBinding.windowId) {
|
|
211
|
+
const exact = tabs.filter((tab) => String(tab.id) === String(pinnedBinding.tabId));
|
|
212
|
+
if (exact.length && !belongsToProject(exact[0])) throw new Error('COMPUTER_CHATGPT_BOOTSTRAP_PINNED_TAB_IDENTITY_CHANGED');
|
|
213
|
+
return exact;
|
|
214
|
+
}
|
|
215
|
+
const mapped = bootstrapTabs.get(identity.bootstrapKey);
|
|
216
|
+
return mapped ? tabs.filter((tab) => tab.id === mapped && belongsToProject(tab)) : [];
|
|
217
|
+
}
|
|
218
|
+
return [];
|
|
219
|
+
}
|
|
220
|
+
async function resolveTarget(identity, createIfMissing, pinnedBinding) {
|
|
221
|
+
const tabs = await chatgptTabs();
|
|
222
|
+
if (identity?.namespace === 'chatgpt.conversation' && pinnedBinding?.providerId === PROVIDER_ID && pinnedBinding.tabId) {
|
|
223
|
+
// Window moves preserve Chrome's profile-local tab id. A vanished id may
|
|
224
|
+
// be recovered by exact conversation, but an existing tab at another URL
|
|
225
|
+
// is identity drift and must never be silently replaced.
|
|
226
|
+
let pinned;
|
|
227
|
+
try { pinned = await tabGet(Number(pinnedBinding.tabId)); }
|
|
228
|
+
catch (error) {
|
|
229
|
+
if (!/No tab with id/i.test(String(error?.message ?? error))) throw error;
|
|
230
|
+
}
|
|
231
|
+
if (pinned) {
|
|
232
|
+
if (core.parseConversation(pinned.url ?? '')?.conversationId !== identity.conversationId) {
|
|
233
|
+
throw new Error('COMPUTER_CHATGPT_EXTENSION_PINNED_TAB_IDENTITY_CHANGED');
|
|
234
|
+
}
|
|
235
|
+
return { tab: pinned, created: false, ownerToken: await attestedOwnerToken(pinned, pinnedBinding) };
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
const matches = matchingTabs(tabs, identity, pinnedBinding);
|
|
239
|
+
if (matches.length > 1) throw new Error('COMPUTER_CHATGPT_EXTENSION_EXACT_TARGET_AMBIGUOUS');
|
|
240
|
+
if (matches.length === 1) {
|
|
241
|
+
const tab = matches[0];
|
|
242
|
+
if (identity.namespace === 'chatgpt.bootstrap') bootstrapTabs.set(identity.bootstrapKey, tab.id);
|
|
243
|
+
return { tab, created: false, ownerToken: await attestedOwnerToken(tab, pinnedBinding) };
|
|
244
|
+
}
|
|
245
|
+
if (identity?.namespace === 'chatgpt.bootstrap' && pinnedBinding?.providerId === PROVIDER_ID
|
|
246
|
+
&& pinnedBinding.tabId && pinnedBinding.windowId) {
|
|
247
|
+
throw new Error('COMPUTER_CHATGPT_BOOTSTRAP_PINNED_TAB_MISSING');
|
|
248
|
+
}
|
|
249
|
+
if (!createIfMissing) return undefined;
|
|
250
|
+
const url = identity.namespace === 'chatgpt.conversation' ? identity.canonicalUrl : identity.projectUrl;
|
|
251
|
+
const tab = await tabCreate(url);
|
|
252
|
+
if (identity.namespace === 'chatgpt.bootstrap') bootstrapTabs.set(identity.bootstrapKey, tab.id);
|
|
253
|
+
const token = randomId();
|
|
254
|
+
let ownerToken;
|
|
255
|
+
try {
|
|
256
|
+
await chrome.storage.session.set({ [ownedTabKey(tab.id)]: token });
|
|
257
|
+
ownerToken = token;
|
|
258
|
+
} catch { /* still preserve the created tab's exact binding, but never auto-close without provenance */ }
|
|
259
|
+
return { tab, created: true, ownerToken };
|
|
260
|
+
}
|
|
261
|
+
function dispatchTransportNotReached(error) {
|
|
262
|
+
const text = String(error?.message ?? error);
|
|
263
|
+
return /Receiving end does not exist|Could not establish connection|No tab with id/i.test(text);
|
|
264
|
+
}
|
|
265
|
+
async function executeEnsure(command, instanceId) {
|
|
266
|
+
let target;
|
|
267
|
+
try {
|
|
268
|
+
target = await resolveTarget(command.identity, true, command.providerBinding);
|
|
269
|
+
// Created and already-bound tabs share one exact-identity receiver repair.
|
|
270
|
+
// The editor must hydrate before 'ensured'; on failure the existing typed
|
|
271
|
+
// failed receipt still carries this tab's binding without permitting Send.
|
|
272
|
+
const observation = await waitForContent(target.tab.id, command.identity.namespace === 'chatgpt.bootstrap',
|
|
273
|
+
() => restoreExactContentReceiver(target.tab.id, command.identity));
|
|
274
|
+
const refreshed = await tabGet(target.tab.id);
|
|
275
|
+
return { kind: 'ensured', providerBinding: binding(refreshed, instanceId, target.ownerToken), observation };
|
|
276
|
+
} catch (error) {
|
|
277
|
+
return {
|
|
278
|
+
kind: 'failed',
|
|
279
|
+
code: String(error?.message ?? error).split(':')[0] || 'COMPUTER_CHATGPT_EXTENSION_ENSURE_FAILED',
|
|
280
|
+
retryable: true,
|
|
281
|
+
// A hydration failure must retain the created tab's physical attachment.
|
|
282
|
+
// This receipt grants neither a ready page nor permission to Send.
|
|
283
|
+
...(target ? { providerBinding: binding(target.tab, instanceId, target.ownerToken) } : {}),
|
|
284
|
+
// Bootstrap tabs can exist before this command even when no new tab was
|
|
285
|
+
// created now. A worker failure cannot prove that creating a different
|
|
286
|
+
// native tab would preserve the one-task/one-tab binding.
|
|
287
|
+
failoverSafe: false,
|
|
288
|
+
};
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
async function executeObserve(command, instanceId) {
|
|
292
|
+
try {
|
|
293
|
+
const target = await resolveTarget(command.identity, false, command.providerBinding);
|
|
294
|
+
if (!target) return { kind: 'failed', code: 'COMPUTER_CHATGPT_EXTENSION_TARGET_MISSING', retryable: true, failoverSafe: true };
|
|
295
|
+
const observation = await snapshot(target.tab.id, command.options ?? {});
|
|
296
|
+
const refreshed = await tabGet(target.tab.id);
|
|
297
|
+
return { kind: 'observation', providerBinding: binding(refreshed, instanceId, await attestedOwnerToken(refreshed, command.providerBinding)), observation };
|
|
298
|
+
} catch (error) {
|
|
299
|
+
return { kind: 'failed', code: String(error?.message ?? error).split(':')[0] || 'COMPUTER_CHATGPT_EXTENSION_OBSERVE_FAILED', retryable: true, failoverSafe: true };
|
|
300
|
+
}
|
|
301
|
+
}
|
|
302
|
+
async function executePrepare(command) {
|
|
303
|
+
const target = await resolveTarget(command.identity, false, command.providerBinding).catch(() => undefined);
|
|
304
|
+
if (!target) return { kind: 'prepared', ready: false, reasonCode: 'COMPUTER_CHATGPT_EXTENSION_TARGET_MISSING' };
|
|
305
|
+
const expectedProjectId = command.identity?.namespace === 'chatgpt.bootstrap' ? core.projectId(command.identity.projectUrl) : undefined;
|
|
306
|
+
if (command.identity?.namespace === 'chatgpt.bootstrap' && !expectedProjectId)
|
|
307
|
+
return { kind: 'prepared', ready: false, reasonCode: 'COMPUTER_CHATGPT_BOOTSTRAP_PROJECT_UNPROVEN' };
|
|
308
|
+
const requiresDraftReplacement = typeof command.prompt === 'string' && command.prompt.startsWith('<<<FORGE_DRAFT_REPLACE_V1:');
|
|
309
|
+
if (expectedProjectId || requiresDraftReplacement) {
|
|
310
|
+
let capability;
|
|
311
|
+
try { capability = await tabMessage(target.tab.id, { type: 'forge-computer-chatgpt-capabilities' }); }
|
|
312
|
+
catch { return { kind: 'prepared', ready: false, reasonCode: 'COMPUTER_CHATGPT_CONTENT_CAPABILITIES_UNAVAILABLE' }; }
|
|
313
|
+
// Older content scripts ignore expectedProjectId. No bootstrap draft/send
|
|
314
|
+
// may cross this version boundary without target verification.
|
|
315
|
+
if (expectedProjectId && capability?.bootstrapTargetGuard !== 'project-checked-before-send-v1')
|
|
316
|
+
return { kind: 'prepared', ready: false, reasonCode: 'COMPUTER_CHATGPT_BOOTSTRAP_PROJECT_GUARD_UNSUPPORTED' };
|
|
317
|
+
if (requiresDraftReplacement && capability?.composerReplacementContract !== 'exact-backed-up-project-draft-v1')
|
|
318
|
+
return { kind: 'prepared', ready: false, reasonCode: 'COMPUTER_CHATGPT_BOOTSTRAP_LOADED_SCRIPT_STALE' };
|
|
319
|
+
}
|
|
320
|
+
try {
|
|
321
|
+
const result = await tabMessage(target.tab.id, {
|
|
322
|
+
type: 'forge-computer-chatgpt-prepare', prompt: command.prompt, mode: command.mode,
|
|
323
|
+
...(command.identity?.namespace === 'chatgpt.conversation' ? { expectedConversationId: command.identity.conversationId } : {}),
|
|
324
|
+
...(expectedProjectId ? { expectedProjectId } : {}),
|
|
325
|
+
});
|
|
326
|
+
if (result?.prepared !== true) return { kind: 'prepared', ready: false, reasonCode: String(result?.reason ?? 'COMPUTER_CHATGPT_EXTENSION_PREPARE_REJECTED') };
|
|
327
|
+
return { kind: 'prepared', ready: true };
|
|
328
|
+
} catch {
|
|
329
|
+
// A prepare command contains no Send action. Failure is not a submitted effect.
|
|
330
|
+
return { kind: 'prepared', ready: false, reasonCode: 'COMPUTER_CHATGPT_EXTENSION_PREPARE_UNAVAILABLE' };
|
|
331
|
+
}
|
|
332
|
+
}
|
|
333
|
+
async function executeDispatch(command) {
|
|
334
|
+
const target = await resolveTarget(command.identity, false, command.providerBinding).catch(() => undefined);
|
|
335
|
+
if (!target) return { kind: 'dispatch', mutation: 'not_attempted', reasonCode: 'COMPUTER_CHATGPT_EXTENSION_TARGET_MISSING' };
|
|
336
|
+
const expectedProjectId = command.identity?.namespace === 'chatgpt.bootstrap' ? core.projectId(command.identity.projectUrl) : undefined;
|
|
337
|
+
if (command.identity?.namespace === 'chatgpt.bootstrap' && !expectedProjectId)
|
|
338
|
+
return { kind: 'dispatch', mutation: 'not_attempted', reasonCode: 'COMPUTER_CHATGPT_BOOTSTRAP_PROJECT_UNPROVEN' };
|
|
339
|
+
// An old tab may still contain a pre-upgrade content script after the
|
|
340
|
+
// extension/runtime has been upgraded. Probe the *exact target tab* before
|
|
341
|
+
// submitting; a background/version heartbeat alone cannot prove it.
|
|
342
|
+
if (expectedProjectId) {
|
|
343
|
+
let capability;
|
|
344
|
+
try { capability = await tabMessage(target.tab.id, { type: 'forge-computer-chatgpt-capabilities' }); }
|
|
345
|
+
catch { return { kind: 'dispatch', mutation: 'not_attempted', reasonCode: 'COMPUTER_CHATGPT_CONTENT_CAPABILITIES_UNAVAILABLE' }; }
|
|
346
|
+
if (expectedProjectId && capability?.bootstrapTargetGuard !== 'project-checked-before-send-v1')
|
|
347
|
+
return { kind: 'dispatch', mutation: 'not_attempted', reasonCode: 'COMPUTER_CHATGPT_BOOTSTRAP_PROJECT_GUARD_UNSUPPORTED' };
|
|
348
|
+
}
|
|
349
|
+
let result;
|
|
350
|
+
try { result = await tabMessage(target.tab.id, {
|
|
351
|
+
type: 'forge-computer-chatgpt-dispatch', prompt: command.prompt, mode: command.mode,
|
|
352
|
+
...(command.identity?.namespace === 'chatgpt.conversation' ? { expectedConversationId: command.identity.conversationId } : {}),
|
|
353
|
+
...(expectedProjectId ? { expectedProjectId } : {}),
|
|
354
|
+
}); }
|
|
355
|
+
catch (error) {
|
|
356
|
+
return dispatchTransportNotReached(error)
|
|
357
|
+
? { kind: 'dispatch', mutation: 'not_attempted', reasonCode: 'COMPUTER_CHATGPT_EXTENSION_CONTENT_UNAVAILABLE' }
|
|
358
|
+
: { kind: 'dispatch', mutation: 'attempted' };
|
|
359
|
+
}
|
|
360
|
+
if (result?.dispatched === false) return { kind: 'dispatch', mutation: 'not_attempted', reasonCode: String(result?.reason ?? 'COMPUTER_CHATGPT_EXTENSION_PRE_MUTATION_REJECTION') };
|
|
361
|
+
// A missing/malformed reply after dispatch crossed the content-script boundary
|
|
362
|
+
// is not an attestation that Send never ran.
|
|
363
|
+
if (result?.dispatched !== true) return { kind: 'dispatch', mutation: 'attempted', confirmed: false };
|
|
364
|
+
// The content script observes the committed user turn within the same page.
|
|
365
|
+
// A missing/negative receipt is an outcome-unknown submission, not a retry grant.
|
|
366
|
+
return { kind: 'dispatch', mutation: 'attempted', confirmed: result.confirmed === true };
|
|
367
|
+
}
|
|
368
|
+
async function executeFindMarker(command, instanceId) {
|
|
369
|
+
const matches = [];
|
|
370
|
+
for (const tab of await chatgptTabs()) {
|
|
371
|
+
if (!tab.id) continue;
|
|
372
|
+
try {
|
|
373
|
+
const observation = await snapshot(tab.id, { includeUserHistory: true, includePageText: false });
|
|
374
|
+
const identity = core.parseConversation(observation?.url ?? tab.url ?? '');
|
|
375
|
+
if (!identity) continue;
|
|
376
|
+
const texts = [observation.latestUserText, ...(Array.isArray(observation.userMessages) ? observation.userMessages : [])];
|
|
377
|
+
if (!texts.some((text) => String(text ?? '').includes(command.marker))) continue;
|
|
378
|
+
matches.push({ identity: { namespace: 'chatgpt.conversation', conversationId: identity.conversationId, canonicalUrl: identity.canonicalUrl }, providerBinding: binding(tab, instanceId), observation });
|
|
379
|
+
} catch { /* an unreadable tab is not negative proof */ }
|
|
380
|
+
}
|
|
381
|
+
return { kind: 'marker_matches', matches };
|
|
382
|
+
}
|
|
383
|
+
async function executeClose(command, instanceId) {
|
|
384
|
+
const pinned = command.providerBinding;
|
|
385
|
+
if (!pinned?.ownerToken || pinned.providerId !== PROVIDER_ID
|
|
386
|
+
|| pinned.providerSessionId !== instanceId || !pinned.tabId || !pinned.windowId)
|
|
387
|
+
return { kind: 'failed', code: 'COMPUTER_CHATGPT_TAB_OWNERSHIP_UNPROVEN', retryable: false };
|
|
388
|
+
const tabId = Number(pinned.tabId);
|
|
389
|
+
if (!Number.isSafeInteger(tabId) || tabId <= 0)
|
|
390
|
+
return { kind: 'failed', code: 'COMPUTER_CHATGPT_TAB_ID_INVALID', retryable: false };
|
|
391
|
+
let tab;
|
|
392
|
+
try { tab = await tabGet(tabId); }
|
|
393
|
+
catch (error) {
|
|
394
|
+
// This exact Chrome profile has already proved the physical tab absent.
|
|
395
|
+
if (/No tab with id/i.test(String(error?.message ?? error))) return { kind: 'closed' };
|
|
396
|
+
return { kind: 'failed', code: 'COMPUTER_CHATGPT_TAB_CLOSE_UNVERIFIED', retryable: true };
|
|
397
|
+
}
|
|
398
|
+
if (String(tab.windowId) !== String(pinned.windowId)
|
|
399
|
+
|| await attestedOwnerToken(tab, pinned) !== pinned.ownerToken)
|
|
400
|
+
return { kind: 'failed', code: 'COMPUTER_CHATGPT_TAB_OWNERSHIP_UNPROVEN', retryable: false };
|
|
401
|
+
const identity = command.identity;
|
|
402
|
+
const exactConversation = identity?.namespace === 'chatgpt.conversation'
|
|
403
|
+
&& core.parseConversation(tab.url ?? '')?.conversationId === identity.conversationId;
|
|
404
|
+
const exactBootstrap = identity?.namespace === 'chatgpt.bootstrap'
|
|
405
|
+
&& (tab.url === identity.projectUrl || (core.projectId(identity.projectUrl)
|
|
406
|
+
&& core.projectId(tab.url ?? '') === core.projectId(identity.projectUrl)));
|
|
407
|
+
if (!exactConversation && !exactBootstrap)
|
|
408
|
+
return { kind: 'failed', code: 'COMPUTER_CHATGPT_TAB_IDENTITY_CHANGED', retryable: false };
|
|
409
|
+
try { await tabRemove(tabId); }
|
|
410
|
+
catch { return { kind: 'failed', code: 'COMPUTER_CHATGPT_TAB_CLOSE_UNVERIFIED', retryable: true }; }
|
|
411
|
+
await chrome.storage.session.remove(ownedTabKey(tabId)).catch(() => undefined);
|
|
412
|
+
if (identity.namespace === 'chatgpt.bootstrap' && bootstrapTabs.get(identity.bootstrapKey) === tabId)
|
|
413
|
+
bootstrapTabs.delete(identity.bootstrapKey);
|
|
414
|
+
return { kind: 'closed' };
|
|
415
|
+
}
|
|
416
|
+
async function executeCommand(command, instanceId) {
|
|
417
|
+
if (!command || typeof command.commandId !== 'string') return { kind: 'failed', code: 'COMPUTER_CHATGPT_EXTENSION_COMMAND_INVALID', retryable: false, failoverSafe: true };
|
|
418
|
+
if (command.kind === 'ensure') return await executeEnsure(command, instanceId);
|
|
419
|
+
if (command.kind === 'observe') return await executeObserve(command, instanceId);
|
|
420
|
+
if (command.kind === 'prepare') return await executePrepare(command);
|
|
421
|
+
if (command.kind === 'dispatch') return await executeDispatch(command);
|
|
422
|
+
if (command.kind === 'find_marker') return await executeFindMarker(command, instanceId);
|
|
423
|
+
if (command.kind === 'close') return await executeClose(command, instanceId);
|
|
424
|
+
return { kind: 'failed', code: 'COMPUTER_CHATGPT_EXTENSION_COMMAND_UNKNOWN', retryable: false, failoverSafe: true };
|
|
425
|
+
}
|
|
426
|
+
async function publishHeartbeat(instanceId) {
|
|
427
|
+
const tabs = await chatgptTabs();
|
|
428
|
+
const [current] = await chrome.tabs.query({ active: true, lastFocusedWindow: true, url: 'https://chatgpt.com/*' });
|
|
429
|
+
const conversations = [];
|
|
430
|
+
for (const tab of tabs) {
|
|
431
|
+
const identity = core.parseConversation(tab.url ?? '');
|
|
432
|
+
if (!identity || !tab.id) continue;
|
|
433
|
+
conversations.push({
|
|
434
|
+
conversationId: identity.conversationId,
|
|
435
|
+
canonicalUrl: identity.canonicalUrl,
|
|
436
|
+
...(String(tab.title ?? '').trim() ? { title: String(tab.title).trim().slice(0, 512) } : {}),
|
|
437
|
+
...(tab.id === current?.id ? { isCurrent: true } : {}),
|
|
438
|
+
providerBinding: binding(tab, instanceId),
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
await nativeRpc('computer_extension_heartbeat', { providerId: PROVIDER_ID, providerInstanceId: instanceId, observedAt: new Date().toISOString(), conversations });
|
|
442
|
+
}
|
|
443
|
+
let providerTickInFlight;
|
|
444
|
+
let providerHeartbeatInFlight;
|
|
445
|
+
function heartbeatTick() {
|
|
446
|
+
providerHeartbeatInFlight ??= providerInstanceId().then(publishHeartbeat)
|
|
447
|
+
.finally(() => { providerHeartbeatInFlight = undefined; });
|
|
448
|
+
return providerHeartbeatInFlight;
|
|
449
|
+
}
|
|
450
|
+
async function providerTick() {
|
|
451
|
+
// One claimed command retains the sole mutation lane; it must not also
|
|
452
|
+
// suppress profile heartbeat when a page/Chrome callback is slow or hangs.
|
|
453
|
+
if (providerTickInFlight) { await heartbeatTick(); return; }
|
|
454
|
+
providerTickInFlight = (async () => {
|
|
455
|
+
const instanceId = await providerInstanceId();
|
|
456
|
+
await heartbeatTick();
|
|
457
|
+
// One command per tick keeps heartbeats and other tasks fair. The broker
|
|
458
|
+
// remains the only authority for a claimed Send and its unknown outcome.
|
|
459
|
+
const claim = await nativeRpc('computer_extension_claim', { provider_instance_id: instanceId });
|
|
460
|
+
const command = claim?.command;
|
|
461
|
+
if (!command) return;
|
|
462
|
+
const result = await executeCommand(command, instanceId);
|
|
463
|
+
await nativeRpc('computer_extension_complete', { provider_instance_id: instanceId, command_id: command.commandId, result });
|
|
464
|
+
})().finally(() => { providerTickInFlight = undefined; });
|
|
465
|
+
return await providerTickInFlight;
|
|
466
|
+
}
|
|
467
|
+
async function discoveryScan(tabId, projectTitles) {
|
|
468
|
+
try { return await tabMessage(tabId, { type: 'forge-computer-chatgpt-discovery-scan', projectTitles }); }
|
|
469
|
+
catch { return {}; }
|
|
470
|
+
}
|
|
471
|
+
async function refreshDiscovery() {
|
|
472
|
+
const tabs = await chatgptTabs();
|
|
473
|
+
const [current] = await chrome.tabs.query({ active: true, lastFocusedWindow: true, url: 'https://chatgpt.com/*' });
|
|
474
|
+
const scopeResult = await nativeRpc('browser_project_scopes').catch(() => ({ projects: [] }));
|
|
475
|
+
const projectScopes = Array.isArray(scopeResult?.projects) ? scopeResult.projects.filter((entry) => typeof entry?.title === 'string' && entry.title.trim()) : [];
|
|
476
|
+
const projectTitles = [...new Set(projectScopes.map((entry) => entry.title.trim()))];
|
|
477
|
+
const projectLinks = new Map();
|
|
478
|
+
const scans = [];
|
|
479
|
+
for (const tab of tabs) if (tab.id) scans.push({ tab, scan: await discoveryScan(tab.id, projectTitles) });
|
|
480
|
+
for (const { scan } of scans) for (const project of Array.isArray(scan?.projects) ? scan.projects : []) {
|
|
481
|
+
const title = String(project?.title ?? '').trim();
|
|
482
|
+
const url = String(project?.url ?? '').trim();
|
|
483
|
+
if (title && url) projectLinks.set(title.toLocaleLowerCase(), { title, url });
|
|
484
|
+
}
|
|
485
|
+
const projectByConversation = new Map();
|
|
486
|
+
for (const scope of projectScopes) {
|
|
487
|
+
const project = projectLinks.get(scope.title.trim().toLocaleLowerCase());
|
|
488
|
+
if (!project) continue;
|
|
489
|
+
const projectId = core.projectId(project.url);
|
|
490
|
+
if (!projectId) continue;
|
|
491
|
+
const projectTab = tabs.find((tab) => String(tab.url ?? '') === project.url);
|
|
492
|
+
if (!projectTab?.id) continue;
|
|
493
|
+
const scan = await discoveryScan(projectTab.id, projectTitles);
|
|
494
|
+
for (const conversation of Array.isArray(scan?.conversations) ? scan.conversations : []) {
|
|
495
|
+
const identity = core.parseConversation(conversation?.canonicalUrl ?? '');
|
|
496
|
+
if (!identity || core.projectId(identity.canonicalUrl) !== projectId) continue;
|
|
497
|
+
projectByConversation.set(identity.conversationId, { project_title: scope.title.trim(), project_url: project.url });
|
|
498
|
+
}
|
|
499
|
+
}
|
|
500
|
+
const conversations = [];
|
|
501
|
+
const seen = new Set();
|
|
502
|
+
for (const tab of tabs) {
|
|
503
|
+
const identity = core.parseConversation(tab.url ?? '');
|
|
504
|
+
if (!identity || seen.has(identity.conversationId)) continue;
|
|
505
|
+
seen.add(identity.conversationId);
|
|
506
|
+
const title = String(tab.title ?? '').trim();
|
|
507
|
+
conversations.push({ conversation_id: identity.conversationId, canonical_url: identity.canonicalUrl, ...projectByConversation.get(identity.conversationId), ...(title ? { title: title.slice(0, 512) } : {}), ...(tab.id === current?.id ? { is_current: true } : {}) });
|
|
508
|
+
}
|
|
509
|
+
// Discovery and command routing share one persisted Chrome-profile identity.
|
|
510
|
+
const instanceId = await providerInstanceId();
|
|
511
|
+
await nativeRpc('browser_discovery_update', { source: `chrome-extension:${instanceId}`, conversations }).catch(() => undefined);
|
|
512
|
+
}
|
|
513
|
+
let discoveryInFlight;
|
|
514
|
+
let discoveryTimer;
|
|
515
|
+
let lastDiscoveryAt = 0;
|
|
516
|
+
function scheduleDiscovery() {
|
|
517
|
+
if (discoveryInFlight || discoveryTimer !== undefined) return;
|
|
518
|
+
const delay = Math.max(0, DISCOVERY_MIN_INTERVAL_MS - (Date.now() - lastDiscoveryAt));
|
|
519
|
+
discoveryTimer = setTimeout(() => {
|
|
520
|
+
discoveryTimer = undefined;
|
|
521
|
+
discoveryInFlight = refreshDiscovery().catch(() => undefined).finally(() => { lastDiscoveryAt = Date.now(); discoveryInFlight = undefined; });
|
|
522
|
+
}, delay);
|
|
523
|
+
}
|
|
524
|
+
chrome.runtime.onMessage.addListener((message, _sender, sendResponse) => {
|
|
525
|
+
if (message?.type !== 'forge-computer-chatgpt-page') return false;
|
|
526
|
+
void providerTick().catch(() => undefined);
|
|
527
|
+
scheduleDiscovery();
|
|
528
|
+
sendResponse({ ok: true });
|
|
529
|
+
return false;
|
|
530
|
+
});
|
|
531
|
+
chrome.runtime.onInstalled.addListener(() => { chrome.alarms.create(ALARM, { periodInMinutes: 1 }); void providerTick().catch(() => undefined); scheduleDiscovery(); });
|
|
532
|
+
chrome.runtime.onStartup.addListener(() => { chrome.alarms.create(ALARM, { periodInMinutes: 1 }); void providerTick().catch(() => undefined); scheduleDiscovery(); });
|
|
533
|
+
chrome.alarms.onAlarm.addListener((alarm) => { if (alarm.name === ALARM) { void providerTick().catch(() => undefined); scheduleDiscovery(); } });
|
|
534
|
+
chrome.tabs.onUpdated.addListener((_tabId, changeInfo, tab) => { if (changeInfo.status === 'complete' && String(tab.url ?? '').startsWith('https://chatgpt.com/')) { void providerTick().catch(() => undefined); scheduleDiscovery(); } });
|
|
535
|
+
chrome.tabs.onActivated.addListener(() => { void providerTick().catch(() => undefined); scheduleDiscovery(); });
|
|
536
|
+
chrome.tabs.onRemoved.addListener(() => { void providerTick().catch(() => undefined); scheduleDiscovery(); });
|
|
537
|
+
chrome.windows.onFocusChanged.addListener(() => { void providerTick().catch(() => undefined); scheduleDiscovery(); });
|
|
538
|
+
chrome.alarms.create(ALARM, { periodInMinutes: 1 });
|
|
539
|
+
setInterval(() => { void providerTick().catch(() => undefined); }, PROVIDER_TICK_MS);
|
|
540
|
+
void providerTick().catch(() => undefined);
|
|
541
|
+
scheduleDiscovery();
|