@moretea-labs/forge 1.7.1 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +58 -62
- package/CHANGELOG.md +39 -0
- package/CLAUDE.md +42 -62
- package/README.md +2 -2
- package/SKILL.md +0 -1
- package/THIRD_PARTY_NOTICES.md +5 -2
- package/adapters/browser/index.ts +2 -0
- package/adapters/chatgpt/browser-delivery-host.ts +71 -0
- package/adapters/chatgpt/browser-delivery-runtime.ts +783 -0
- package/adapters/chatgpt/controller-binding-store.ts +55 -0
- package/adapters/chatgpt/controller-host.ts +119 -0
- package/adapters/chatgpt/controller-round-host.ts +78 -0
- package/adapters/chatgpt/controller-round-settlement-store.ts +45 -0
- package/adapters/chatgpt/provider-delivery.ts +113 -0
- package/adapters/chatgpt/work-conversation-binding-store.ts +192 -0
- package/adapters/chatgpt/wsl-bridge-delivery-host.ts +63 -0
- package/adapters/computer/desktop-operator-contract.ts +7 -0
- package/adapters/computer/desktop-operator-discovery.ts +201 -0
- package/adapters/computer/desktop-operator-negotiation.ts +174 -0
- package/adapters/computer/desktop-operator-provider.ts +312 -0
- package/adapters/computer/index.ts +1 -0
- package/adapters/computer/interaction-target-authority.ts +944 -0
- package/adapters/controller-process/binding-store.ts +34 -0
- package/adapters/controller-process/controller-host.ts +26 -0
- package/adapters/mcp/access-mode.ts +132 -0
- package/adapters/mcp/audit.ts +32 -0
- package/adapters/mcp/auth.ts +407 -0
- package/adapters/mcp/controller-round-compatibility.ts +184 -0
- package/adapters/mcp/frozen-client-semantic-compatibility.ts +292 -0
- package/adapters/mcp/instructions.ts +36 -0
- package/adapters/mcp/legacy-context.ts +18 -0
- package/adapters/mcp/multi-repository.ts +437 -0
- package/adapters/mcp/oauth.ts +391 -0
- package/adapters/mcp/paths.ts +140 -0
- package/adapters/mcp/policy.ts +232 -0
- package/adapters/mcp/proxy-env.ts +62 -0
- package/adapters/mcp/readiness-projection.ts +24 -0
- package/adapters/mcp/redaction.ts +21 -0
- package/adapters/mcp/repo.ts +24 -0
- package/adapters/mcp/runtime-gateway/artifact-adapter.ts +52 -0
- package/adapters/mcp/runtime-gateway/context-adapter.ts +829 -0
- package/adapters/mcp/runtime-gateway/controller-authority-adapter.ts +191 -0
- package/adapters/mcp/runtime-gateway/engineering-preconditions.ts +147 -0
- package/adapters/mcp/runtime-gateway/execution-control-adapter.ts +28 -0
- package/adapters/mcp/runtime-gateway/execution-tools.ts +207 -0
- package/adapters/mcp/runtime-gateway/filesystem-adapter.ts +38 -0
- package/adapters/mcp/runtime-gateway/gateway-execution-adapter.ts +411 -0
- package/adapters/mcp/runtime-gateway/legacy-ios-tool-adapter.ts +105 -0
- package/adapters/mcp/runtime-gateway/maintenance-adapter.ts +44 -0
- package/adapters/mcp/runtime-gateway/model-adapter.ts +63 -0
- package/adapters/mcp/runtime-gateway/persisted-check-process.ts +3 -0
- package/adapters/mcp/runtime-gateway/plugin-adapter.ts +308 -0
- package/adapters/mcp/runtime-gateway/process-tools.ts +289 -0
- package/adapters/mcp/runtime-gateway/protected-computer-adapter.ts +76 -0
- package/adapters/mcp/runtime-gateway/recovery-adapter.ts +268 -0
- package/adapters/mcp/runtime-gateway/recovery-client-adapter.ts +70 -0
- package/adapters/mcp/runtime-gateway/recovery-tool-contract.ts +99 -0
- package/adapters/mcp/runtime-gateway/repository-compat-adapter.ts +110 -0
- package/adapters/mcp/runtime-gateway/result-adapter.ts +108 -0
- package/adapters/mcp/runtime-gateway/router.ts +728 -0
- package/adapters/mcp/runtime-gateway/runtime-observation-adapter.ts +1251 -0
- package/adapters/mcp/runtime-gateway/runtime-tool-definitions.ts +634 -0
- package/adapters/mcp/runtime-gateway/runtime-tool-shared.ts +27 -0
- package/adapters/mcp/runtime-gateway/runtime-tools.ts +68 -0
- package/adapters/mcp/runtime-gateway/scheduler-adapter.ts +186 -0
- package/adapters/mcp/runtime-gateway/shared-adapter.ts +25 -0
- package/adapters/mcp/runtime-gateway/status-inbox-adapter.ts +870 -0
- package/adapters/mcp/runtime-gateway/work-adapter.ts +3186 -0
- package/adapters/mcp/runtime-gateway/work-compat-adapter.ts +502 -0
- package/adapters/mcp/runtime-gateway/work-validation-reconciler.ts +1 -0
- package/adapters/mcp/server.ts +1048 -0
- package/adapters/mcp/setup.ts +791 -0
- package/adapters/mcp/tool-capability-cache.ts +87 -0
- package/adapters/mcp/tool-mapping/access-tools.ts +387 -0
- package/adapters/mcp/tool-mapping/legacy-tool-service.ts +5265 -0
- package/adapters/mcp/tool-mapping/post-finalize-work-attribution.ts +150 -0
- package/adapters/mcp/tool-mapping/repository-tools.ts +1691 -0
- package/adapters/mcp/tool-mapping/tools.ts +9 -0
- package/adapters/mcp/tool-mapping/umbrella-tools.ts +117 -0
- package/adapters/mcp/toolset-names.ts +74 -0
- package/adapters/mcp/toolset-selection.ts +41 -0
- package/adapters/mcp/toolset.ts +336 -0
- package/adapters/mcp/transports/http.ts +1456 -0
- package/adapters/mcp/transports/session-registry.ts +513 -0
- package/adapters/mcp/transports/stdio.ts +8 -0
- package/adapters/mcp/tunnels/openai-secure-tunnel.ts +139 -0
- package/adapters/mcp/types.ts +41 -0
- package/assets/AGENTS.md +2 -16
- package/assets/CLAUDE.md +2 -16
- package/assets/hooks/AGENTS.md +3 -14
- package/assets/hooks/CLAUDE.md +3 -14
- package/assets/hooks/hook-input.sh +9 -2
- package/assets/hooks/lib/workflow-state.sh +28 -10
- package/assets/hooks/prompt-guard.sh +63 -37
- package/assets/hooks/security-sentinel.sh +2 -1
- package/assets/hooks/session-start-context.sh +7 -5
- package/assets/hooks/stop-orchestrator.sh +13 -3
- package/assets/initializer-question-pack.v4.json +11 -7
- package/assets/partials/05-workflow.partial.md +18 -73
- package/assets/partials/07-footer.partial.md +1 -1
- package/assets/partials-agents/02-operating-mode.partial.md +1 -2
- package/assets/partials-agents/04-task-protocol.partial.md +14 -59
- package/assets/partials-agents/06-quality-safety.partial.md +11 -16
- package/assets/partials-agents/08-deep-docs.partial.md +1 -2
- package/assets/plan-map.json +11 -11
- package/assets/plugin-registry.v1.json +9 -3
- package/assets/plugin-trust-policy.v1.json +6 -0
- package/assets/recovery/ForgeRecovery.ps1 +100 -0
- package/assets/recovery/forge-wsl-rescue +271 -0
- package/assets/reference-configs/development-protocol.md +11 -15
- package/assets/reference-configs/document-generation.md +1 -1
- package/assets/reference-configs/global-working-rules.md +9 -1
- package/assets/reference-configs/harness-overview.md +22 -112
- package/assets/reference-configs/hook-operations.md +1 -1
- package/assets/reference-configs/sprint-contracts.md +2 -2
- package/assets/skill-version.json +2 -2
- package/assets/templates/helpers/architecture-event.ts +3 -34
- package/assets/templates/helpers/architecture-queue.sh +61 -10
- package/assets/templates/helpers/archive-workflow.sh +90 -180
- package/assets/templates/helpers/capability-config.ts +8 -11
- package/assets/templates/helpers/capability-resolver.ts +37 -19
- package/assets/templates/helpers/capture-plan.sh +2 -2
- package/assets/templates/helpers/check-architecture-sync.sh +1 -9
- package/assets/templates/helpers/check-task-sync.sh +82 -11
- package/assets/templates/helpers/check-task-workflow.sh +1 -3
- package/assets/templates/helpers/context-contract-sync.sh +1 -12
- package/assets/templates/helpers/contract-worktree.sh +26 -24
- package/assets/templates/helpers/ensure-task-workflow.sh +6 -8
- package/assets/templates/helpers/inspect-project-state.ts +1 -1
- package/assets/templates/helpers/migrate-project-template.sh +1 -1
- package/assets/templates/helpers/migrate-workflow-docs.ts +53 -239
- package/assets/templates/helpers/new-plan.sh +2 -2
- package/assets/templates/helpers/plan-to-todo.sh +3 -31
- package/assets/templates/helpers/sprint-backlog.sh +1 -1
- package/assets/templates/helpers/switch-plan.sh +1 -1
- package/assets/templates/helpers/workflow-contract.ts +1 -0
- package/assets/workflow-contract.v1.json +63 -171
- package/assets/workflows/xiaohongshu/xiaohongshu.collect-metrics.v1.draft.json +118 -0
- package/assets/workflows/xiaohongshu/xiaohongshu.publish-image-note.v1.draft.json +316 -0
- package/assets/workflows/xiaohongshu/xiaohongshu.publish-long-text.v1.draft.json +324 -0
- package/docs/architecture/CURRENT.md +148 -13
- package/docs/architecture/EVOLUTION.md +13 -0
- package/docs/architecture/decisions/20260801-controller-home-sqlite-state.md +2 -1
- package/docs/architecture/decisions/20260831-independent-windows-wsl-host-rescue.md +59 -0
- package/docs/architecture/decisions/20260904-ios-device-migration-baseline.md +120 -0
- package/docs/architecture/decisions/20260904-repository-runtime-data-boundary.md +42 -0
- package/docs/architecture/decisions/20260907-assistant-learning-loop.md +35 -0
- package/docs/architecture/decisions/20260907-controller-round-transition-policy.md +296 -0
- package/docs/architecture/decisions/20260907-maintenance-diagnosis-bounded-read.md +39 -0
- package/docs/architecture/domains/verification.md +7 -0
- package/docs/architecture/domains/workflow-engine.md +1 -1
- package/docs/architecture/index.md +3 -3
- package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +19 -8
- package/docs/architecture/requests/2026-09-04-computer-plugin-workflow-taxonomy.md +124 -0
- package/docs/forge-chatgpt-mcp-setup.md +5 -3
- package/docs/forge-plugin-management.md +20 -4
- package/docs/forge-runtime-self-healing-loop.md +2 -0
- package/docs/operations/benchmark-cross-version-freeze.md +38 -0
- package/docs/operations/controller-browser-plugin.md +3 -1
- package/docs/operations/homebrew.md +2 -2
- package/docs/operations/platform-support.md +16 -8
- package/docs/operations/platform-support.zh-CN.md +2 -2
- package/docs/operations/standalone-disaster-recovery.md +26 -1
- package/docs/operations/uu-remote-rescue.md +41 -0
- package/docs/operations/windows-wsl-host-rescue.md +133 -0
- package/docs/public-usage-guide.md +1 -1
- package/docs/public-usage-guide.zh-CN.md +1 -1
- package/docs/tutorials/01-install-and-start.md +1 -1
- package/docs/tutorials/01-install-and-start.zh-CN.md +1 -1
- package/docs/tutorials/02-connect-chatgpt.md +4 -4
- package/docs/tutorials/02-connect-chatgpt.zh-CN.md +4 -4
- package/docs/wiki/Implementation.md +1 -1
- package/package.json +23 -14
- package/packages/kernel/controller/api/index.ts +2 -0
- package/packages/kernel/controller/application/controller-round-service.ts +29 -0
- package/packages/kernel/controller/application/controller-service.ts +30 -0
- package/packages/kernel/controller/domain/controller-round-transition-policy.ts +308 -0
- package/packages/kernel/controller/domain/controller-round.ts +107 -0
- package/packages/kernel/controller/domain/execution-quality.ts +133 -0
- package/packages/kernel/controller/domain/types.ts +56 -0
- package/packages/kernel/controller/index.ts +1 -0
- package/packages/kernel/controller/infrastructure/controller-binding-store.ts +84 -0
- package/packages/kernel/controller/infrastructure/controller-round-store.ts +1384 -0
- package/packages/kernel/controller/infrastructure/controller-session-store.ts +578 -0
- package/packages/kernel/controller/ports/controller-host.ts +16 -0
- package/packages/kernel/goal/api/index.ts +1 -0
- package/packages/kernel/goal/domain/types.ts +40 -0
- package/packages/kernel/goal/index.ts +1 -0
- package/packages/kernel/identity/api/index.ts +5 -0
- package/packages/kernel/identity/application/identity-service.ts +67 -0
- package/packages/kernel/identity/domain/scope.ts +63 -0
- package/packages/kernel/identity/domain/semantic-sync.ts +81 -0
- package/packages/kernel/identity/domain/types.ts +44 -0
- package/packages/kernel/identity/domain/workspace.ts +56 -0
- package/packages/kernel/identity/index.ts +1 -0
- package/packages/kernel/identity/infrastructure/identity-store.ts +75 -0
- package/packages/kernel/memory/api/index.ts +5 -0
- package/packages/kernel/memory/application/experience-service.ts +118 -0
- package/packages/kernel/memory/application/outcome-service.ts +77 -0
- package/packages/kernel/memory/domain/experience.ts +103 -0
- package/packages/kernel/memory/domain/operational-prior.ts +92 -0
- package/packages/kernel/memory/index.ts +1 -0
- package/packages/kernel/progression/api/index.ts +2 -0
- package/packages/kernel/progression/application/projector.ts +211 -0
- package/packages/kernel/progression/domain/types.ts +116 -0
- package/packages/kernel/progression/index.ts +1 -0
- package/packages/kernel/scheduler/api/index.ts +6 -0
- package/packages/kernel/scheduler/application/continuation-service.ts +212 -0
- package/packages/kernel/scheduler/application/eligibility.ts +252 -0
- package/packages/kernel/scheduler/application/schedule-service.ts +24 -0
- package/packages/kernel/scheduler/application/settlement.ts +197 -0
- package/packages/kernel/scheduler/domain/continuation.ts +38 -0
- package/packages/kernel/scheduler/domain/schedule.ts +149 -0
- package/packages/kernel/scheduler/index.ts +1 -0
- package/packages/kernel/scheduler/infrastructure/continuation-dispatch-store.ts +38 -0
- package/packages/kernel/scheduler/infrastructure/schedule-store.ts +785 -0
- package/packages/kernel/work/api/index.ts +15 -0
- package/packages/kernel/work/application/work-service.ts +148 -0
- package/packages/kernel/work/domain/admission-policy.ts +34 -0
- package/packages/kernel/work/domain/check-receipt.ts +43 -0
- package/packages/kernel/work/domain/context-closure-receipt.ts +32 -0
- package/packages/kernel/work/domain/engineering-contracts.ts +157 -0
- package/packages/kernel/work/domain/engineering-design.ts +281 -0
- package/packages/kernel/work/domain/engineering-profile.ts +242 -0
- package/packages/kernel/work/domain/execution-concurrency.ts +254 -0
- package/packages/kernel/work/domain/execution-snapshot.ts +33 -0
- package/packages/kernel/work/domain/implementation-review.ts +510 -0
- package/packages/kernel/work/domain/project-engineering-contract.ts +202 -0
- package/packages/kernel/work/domain/repository-completion-receipt.ts +73 -0
- package/packages/kernel/work/domain/state-machine.ts +259 -0
- package/packages/kernel/work/domain/types.ts +528 -0
- package/packages/kernel/work/index.ts +1 -0
- package/packages/kernel/work/infrastructure/work-contract-store.ts +1876 -0
- package/packages/kernel/work/ports/work-contract-store.ts +18 -0
- package/packages/plugin-runtime/browser/index.ts +3 -0
- package/packages/plugin-runtime/browser/provider-registry.ts +110 -0
- package/packages/plugin-runtime/browser/runtime-contract.ts +150 -0
- package/packages/plugin-runtime/browser/session-authority.ts +61 -0
- package/packages/plugin-runtime/computer/index.ts +6 -0
- package/packages/plugin-runtime/computer/provider-error.ts +26 -0
- package/packages/plugin-runtime/computer/provider-registration.ts +52 -0
- package/packages/plugin-runtime/computer/provider-registry.ts +63 -0
- package/packages/plugin-runtime/computer/provider.ts +19 -0
- package/packages/plugin-runtime/computer/target-authority.ts +190 -0
- package/packages/plugin-runtime/computer/target-persistence.ts +54 -0
- package/packages/plugin-runtime/external/index.ts +1 -0
- package/packages/plugin-runtime/external/unix-jsonl-transport.ts +457 -0
- package/packages/plugin-runtime/index.ts +3 -0
- package/packages/protocols/browser/index.ts +1 -0
- package/packages/protocols/browser/session.ts +98 -0
- package/packages/protocols/computer/contract.ts +199 -0
- package/packages/protocols/computer/index.ts +1 -0
- package/packages/protocols/failure/contract.ts +158 -0
- package/packages/protocols/failure/index.ts +1 -0
- package/packages/protocols/handoff/index.ts +1 -0
- package/packages/protocols/handoff/status.ts +22 -0
- package/packages/protocols/mcp/execution-context.ts +11 -0
- package/packages/protocols/mcp/index.ts +2 -0
- package/packages/protocols/mcp/tool-contract.ts +42 -0
- package/packages/workflow-runtime/api/index.ts +2 -0
- package/packages/workflow-runtime/application/interpreter.ts +342 -0
- package/packages/workflow-runtime/domain/workflow-asset.ts +223 -0
- package/packages/workflow-runtime/index.ts +1 -0
- package/scripts/assemble-template.ts +1 -1
- package/scripts/benchmark-computer-hotpath.ts +255 -0
- package/scripts/benchmark-controller-round2.ts +54 -2
- package/scripts/benchmark-ios-agent-device.ts +3 -6
- package/scripts/benchmark-mcp-transport.ts +3 -4
- package/scripts/benchmark-operational-memory-activation.ts +235 -0
- package/scripts/benchmark-operational-shadow.ts +28 -0
- package/scripts/bootstrap-runtime-maintenance-recovery.sh +4 -1
- package/scripts/canary-chatgpt-provider-delivery.ts +97 -0
- package/scripts/canary-controller-semantic-wait.ts +195 -0
- package/scripts/canary-global-hook.sh +2 -2
- package/scripts/check-mcp-compatibility.ts +173 -7
- package/scripts/check-platform-support.mjs +28 -2
- package/scripts/check-public-docs.sh +1 -1
- package/scripts/check-release-readiness.sh +1 -1
- package/scripts/check-repository-hygiene.mjs +92 -0
- package/scripts/check-runtime-architecture.mjs +1206 -95
- package/scripts/check-source-duplication.mjs +9 -1
- package/scripts/check-tarball-install-smoke.sh +98 -15
- package/scripts/controller-ngrok-rotation.sh +5 -4
- package/scripts/create-project-dirs.sh +10 -74
- package/scripts/forge-local-recovery-helper.mjs +204 -0
- package/scripts/forge-uu-remote-rescue-helper.d.mts +31 -0
- package/scripts/forge-uu-remote-rescue-helper.mjs +409 -0
- package/scripts/forge-windows-host-recovery-helper.mjs +136 -0
- package/scripts/hook-shim.sh +2 -2
- package/scripts/init-project.sh +12 -70
- package/scripts/install-desktop-operator-registration.ts +4 -2
- package/scripts/install-independent-windows-wsl-recovery.ts +125 -0
- package/scripts/install-standalone-recovery.ts +122 -10
- package/scripts/install-uu-remote-rescue-registration.ts +74 -0
- package/scripts/install-windows-host-recovery-registration.ts +22 -0
- package/scripts/lib/controller-home-env.sh +6 -3
- package/scripts/lib/project-init-lib.sh +39 -582
- package/scripts/migrate-chatgpt-automation-schedules.ts +2 -2
- package/scripts/migrate-windows-wsl-controller-home.ts +306 -0
- package/scripts/open-source-audit-allowlist.txt +12 -1
- package/scripts/portability-bindings.mjs +70 -0
- package/scripts/run-bounded-command.mjs +55 -0
- package/scripts/run-bun-test-file.ts +22 -6
- package/scripts/run-governed-gate.ts +4 -1
- package/scripts/smoke-mcp-http-runtime.ts +1 -1
- package/scripts/smoke-runtime-recovery.ts +34 -1
- package/scripts/smoke-schedule-engine.ts +1 -1
- package/scripts/stage-runtime-release.ts +6 -1
- package/scripts/test-governance.ts +51 -2
- package/src/cli/agent-jobs/job-manager.ts +10 -1
- package/src/cli/chatgpt-browser/bind-server.ts +2 -1
- package/src/cli/chatgpt-browser/binding.ts +20 -10
- package/src/cli/chatgpt-browser/bridge-extension.ts +75 -0
- package/src/cli/chatgpt-browser/bridge-provider.ts +376 -11
- package/src/cli/chatgpt-browser/engine.ts +10 -11
- package/src/cli/chatgpt-browser/native-provider.ts +24 -83
- package/src/cli/chatgpt-browser/oracle-provider.ts +5 -3
- package/src/cli/chatgpt-browser/types.ts +1 -0
- package/src/cli/commands/bootstrap-control.ts +334 -0
- package/src/cli/commands/brain-assistant.ts +38 -0
- package/src/cli/commands/brain.ts +193 -40
- package/src/cli/commands/chatgpt.ts +127 -0
- package/src/cli/commands/computer.ts +437 -0
- package/src/cli/commands/doctor.ts +18 -1
- package/src/cli/commands/init-hook.ts +71 -61
- package/src/cli/commands/init.ts +3 -1
- package/src/cli/commands/mcp.ts +16 -10
- package/src/cli/commands/plugin.ts +38 -12
- package/src/cli/commands/recovery.ts +493 -80
- package/src/cli/commands/runtime.ts +9 -4
- package/src/cli/commands/setup-profile.ts +89 -39
- package/src/cli/commands/status.ts +2 -1
- package/src/cli/controller/check-runner.ts +487 -98
- package/src/cli/controller/context/types.ts +7 -1
- package/src/cli/controller/context-pack.ts +23 -3
- package/src/cli/controller/issue-store.ts +1 -1
- package/src/cli/controller/systemd-user.ts +110 -0
- package/src/cli/controller/types.ts +10 -70
- package/src/cli/controller/worklog.ts +67 -13
- package/src/cli/editing/edit-operation-contract.ts +92 -0
- package/src/cli/editing/edit-session.ts +289 -13
- package/src/cli/github/plugin.ts +17 -12
- package/src/cli/hook/route-registry.ts +3 -5
- package/src/cli/hook/runtime.ts +49 -3
- package/src/cli/index.ts +3 -0
- package/src/cli/local-bridge/connector-freshness.ts +2 -2
- package/src/cli/local-bridge/console-automations.ts +2 -2
- package/src/cli/local-bridge/facade-api.ts +41 -14
- package/src/cli/local-bridge/job-store.ts +1 -1
- package/src/cli/local-bridge/server.ts +24 -27
- package/src/cli/mcp/access-mode.ts +2 -132
- package/src/cli/mcp/access-tools.ts +2 -387
- package/src/cli/mcp/audit.ts +2 -32
- package/src/cli/mcp/auth.ts +2 -404
- package/src/cli/mcp/instructions.ts +2 -34
- package/src/cli/mcp/legacy-context.ts +2 -16
- package/src/cli/mcp/legacy-tool-service.ts +2 -5308
- package/src/cli/mcp/multi-repository.ts +2 -436
- package/src/cli/mcp/oauth.ts +2 -375
- package/src/cli/mcp/openai-secure-tunnel.ts +2 -0
- package/src/cli/mcp/paths.ts +2 -140
- package/src/cli/mcp/policy.ts +2 -232
- package/src/cli/mcp/post-finalize-work-attribution.ts +2 -150
- package/src/cli/mcp/proxy-env.ts +2 -62
- package/src/cli/mcp/readiness-projection.ts +2 -24
- package/src/cli/mcp/redaction.ts +2 -21
- package/src/cli/mcp/repo.ts +2 -24
- package/src/cli/mcp/repository-tools.ts +2 -1585
- package/src/cli/mcp/server.ts +2 -951
- package/src/cli/mcp/setup.ts +2 -737
- package/src/cli/mcp/tool-capability-cache.ts +2 -87
- package/src/cli/mcp/tool-contract.ts +2 -14
- package/src/cli/mcp/tools.ts +2 -9
- package/src/cli/mcp/toolset-names.ts +2 -70
- package/src/cli/mcp/toolset-selection.ts +2 -41
- package/src/cli/mcp/toolset.ts +2 -336
- package/src/cli/mcp/transports/AGENTS.md +1 -10
- package/src/cli/mcp/transports/CLAUDE.md +1 -10
- package/src/cli/mcp/transports/http.ts +2 -1414
- package/src/cli/mcp/transports/session-registry.ts +2 -490
- package/src/cli/mcp/transports/stdio.ts +2 -8
- package/src/cli/mcp/types.ts +2 -41
- package/src/cli/mcp/umbrella-tools.ts +2 -117
- package/src/cli/repositories/command-executor.ts +64 -18
- package/src/cli/repositories/command-scope.ts +117 -1
- package/src/cli/repositories/controller-home.ts +119 -1
- package/src/cli/repositories/identity.ts +13 -0
- package/src/cli/repositories/registry.ts +68 -49
- package/src/cli/repositories/repository-snapshot.ts +53 -7
- package/src/cli/repositories/runtime-storage.ts +27 -2
- package/src/cli/repositories/safe-patch.ts +34 -38
- package/src/cli/repositories/selected-path-actions.ts +22 -2
- package/src/cli/repositories/structured-git.ts +105 -25
- package/src/cli/runtime/helper-runner.ts +7 -0
- package/src/cli/runtime-invocation.ts +8 -1
- package/src/cli/tools/codegraph.ts +44 -25
- package/src/core/adoption/manifest-templates.ts +1 -1
- package/src/core/adoption/plan.ts +3 -26
- package/src/core/adoption/workflow-contract-plan.ts +17 -9
- package/src/effects/process-runner.ts +8 -14
- package/src/runtime/context/assistant-context.ts +154 -0
- package/src/runtime/context/assistant-work-context.ts +147 -0
- package/src/runtime/context/codegraph-cache-boundary.ts +255 -0
- package/src/runtime/context/codegraph-read-provider.ts +55 -22
- package/src/runtime/context/codegraph-sidecar.cjs +8 -1
- package/src/runtime/context/context-closure.ts +268 -0
- package/src/runtime/context/context-plane.ts +211 -0
- package/src/runtime/context/project-engineering-contract.ts +60 -0
- package/src/runtime/control-plane/bootstrap/capability-provider.ts +74 -0
- package/src/runtime/control-plane/bootstrap/control-api.ts +57 -0
- package/src/runtime/control-plane/bootstrap/index.ts +5 -0
- package/src/runtime/control-plane/bootstrap/state-machine.ts +141 -0
- package/src/runtime/control-plane/bootstrap/store.ts +21 -0
- package/src/runtime/control-plane/bootstrap/types.ts +87 -0
- package/src/runtime/control-plane/cleanup-artifact-retention.ts +328 -0
- package/src/runtime/control-plane/codegraph-cache-retention.ts +164 -0
- package/src/runtime/control-plane/concurrency/work-execution-concurrency.ts +320 -0
- package/src/runtime/control-plane/execution/content-equivalent-commit-authority.ts +169 -0
- package/src/runtime/control-plane/execution/controller-authority-recovery.ts +350 -0
- package/src/runtime/control-plane/execution/direct-canonical-work-reconciliation.ts +85 -0
- package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +417 -100
- package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +103 -15
- package/src/runtime/control-plane/execution/implementation-review-content.ts +95 -0
- package/src/runtime/control-plane/execution/repository-work-attribution.ts +2 -2
- package/src/runtime/control-plane/execution/retained-work-resume.ts +336 -6
- package/src/runtime/control-plane/execution/validation.ts +28 -3
- package/src/runtime/control-plane/execution/work-authority-reconciler.ts +116 -0
- package/src/runtime/control-plane/execution/work-completion-authority.ts +44 -2
- package/src/runtime/control-plane/execution/work-evidence-policy.ts +34 -3
- package/src/runtime/control-plane/execution/work-execution-support.ts +42 -22
- package/src/runtime/control-plane/execution/work-finalization-service.ts +1103 -166
- package/src/runtime/control-plane/execution/work-handle-authority.ts +267 -16
- package/src/runtime/control-plane/execution/work-handle-store.ts +77 -0
- package/src/runtime/control-plane/execution/work-head-settlement.ts +75 -0
- package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +145 -23
- package/src/runtime/control-plane/execution/work-operation-service.ts +49 -13
- package/src/runtime/control-plane/execution/work-preparation-service.ts +31 -15
- package/src/runtime/control-plane/execution/work-remote-delivery.ts +1 -1
- package/src/runtime/control-plane/execution/work-task-receipt.ts +20 -1
- package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +407 -76
- package/src/runtime/control-plane/execution/work-validation-reconciler.ts +88 -18
- package/src/runtime/control-plane/execution/work-verification-context.ts +1 -1
- package/src/runtime/control-plane/execution/work-verification-service.ts +258 -4
- package/src/runtime/control-plane/execution/work-verification-snapshot.ts +200 -15
- package/src/runtime/control-plane/facade/capability-registry.ts +21 -10
- package/src/runtime/control-plane/facade/controller-round-relay.ts +3 -1118
- package/src/runtime/control-plane/facade/controller-session-store.ts +2 -488
- package/src/runtime/control-plane/facade/goal-workloop-access.ts +6 -3
- package/src/runtime/control-plane/facade/goal-workloop.ts +972 -137
- package/src/runtime/control-plane/facade/handoff-inbox-application.ts +158 -0
- package/src/runtime/control-plane/facade/handoff-inbox-store.ts +10 -0
- package/src/runtime/control-plane/facade/index.ts +4 -2
- package/src/runtime/control-plane/facade/operation-digest.ts +22 -58
- package/src/runtime/control-plane/facade/plan-contract-store.ts +1004 -38
- package/src/runtime/control-plane/facade/repository-work-admission.ts +19 -1
- package/src/runtime/control-plane/facade/requirement-authority.ts +218 -0
- package/src/runtime/control-plane/facade/requirement-board.ts +6 -1
- package/src/runtime/control-plane/facade/rh-work-operation-contract.ts +48 -0
- package/src/runtime/control-plane/facade/semantic-admission.ts +8 -16
- package/src/runtime/control-plane/facade/suggested-actions.ts +4 -1
- package/src/runtime/control-plane/facade/types.ts +189 -532
- package/src/runtime/control-plane/facade/work-admission-policy.ts +84 -35
- package/src/runtime/control-plane/facade/work-contract-store.ts +2 -1043
- package/src/runtime/control-plane/facade/work-implementation-review.ts +2 -0
- package/src/runtime/control-plane/facade/work-state-machine.ts +2 -250
- package/src/runtime/control-plane/generated-cache-retention.ts +239 -18
- package/src/runtime/control-plane/global-scheduler/durable-admission.ts +12 -1
- package/src/runtime/control-plane/global-scheduler/maintenance.ts +76 -13
- package/src/runtime/control-plane/global-scheduler/periodic-cleanup-entry.ts +55 -0
- package/src/runtime/control-plane/global-scheduler/periodic-cleanup-process.ts +98 -0
- package/src/runtime/control-plane/global-scheduler/reconciliation.ts +1 -1
- package/src/runtime/control-plane/global-scheduler/scheduler-worker-entry.ts +10 -0
- package/src/runtime/control-plane/global-scheduler/scheduler.ts +173 -81
- package/src/runtime/control-plane/global-scheduler/worker-launch.ts +18 -6
- package/src/runtime/control-plane/launcher/chatgpt-round-continuation.ts +397 -0
- package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +2 -177
- package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +301 -560
- package/src/runtime/control-plane/launcher/provider-mcp-bootstrap.ts +20 -4
- package/src/runtime/control-plane/launcher/thin-launcher.ts +50 -4
- package/src/runtime/control-plane/lifecycle-retention-metrics.ts +60 -0
- package/src/runtime/control-plane/persistence/experience-store.ts +169 -0
- package/src/runtime/control-plane/persistence/operational-prior-store.ts +348 -0
- package/src/runtime/control-plane/persistence/requirement-store.ts +30 -22
- package/src/runtime/control-plane/persistence/sqlite-store.ts +545 -76
- package/src/runtime/control-plane/persistence/workflow-content-store.ts +98 -0
- package/src/runtime/control-plane/persistence/workflow-registry-store.ts +186 -0
- package/src/runtime/control-plane/persistence/workflow-run-store.ts +123 -0
- package/src/runtime/control-plane/release-retention.ts +56 -16
- package/src/runtime/control-plane/repository-namespace-retention.ts +208 -0
- package/src/runtime/control-plane/routing/route-policy.ts +19 -0
- package/src/runtime/control-plane/runtime-cleanup.ts +574 -37
- package/src/runtime/control-plane/runtime-generation.ts +6 -0
- package/src/runtime/control-plane/workspace/semantic-sync-service.ts +465 -0
- package/src/runtime/control-plane/workspace/workspace-store.ts +80 -0
- package/src/runtime/diagnostics/incident-repair.ts +406 -0
- package/src/runtime/diagnostics/mcp-timing.ts +125 -6
- package/src/runtime/diagnostics/performance.ts +6 -14
- package/src/runtime/diagnostics/read-only-tool.ts +1 -1
- package/src/runtime/evidence/artifact-store.ts +65 -2
- package/src/runtime/evidence/operational-shadow.ts +38 -0
- package/src/runtime/evidence/process-check-receipt.ts +5 -43
- package/src/runtime/evidence/sensitive-output.ts +4 -4
- package/src/runtime/execution/jobs/receipt-recovery.ts +1 -1
- package/src/runtime/execution/jobs/store.ts +121 -2
- package/src/runtime/execution/managed-workspace.ts +55 -5
- package/src/runtime/execution/process-runtime/canary.ts +19 -5
- package/src/runtime/execution/process-runtime/check-classification.ts +48 -0
- package/src/runtime/execution/process-runtime/check-facade.ts +23 -9
- package/src/runtime/execution/process-runtime/check-receipt.ts +1 -1
- package/src/runtime/execution/process-runtime/check-result-retention.ts +132 -0
- package/src/runtime/execution/process-runtime/check-result.ts +73 -2
- package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +42 -3
- package/src/runtime/execution/process-runtime/check-storage.ts +187 -0
- package/src/runtime/execution/process-runtime/command-facade.ts +38 -87
- package/src/runtime/execution/process-runtime/index.ts +1 -0
- package/src/runtime/execution/process-runtime/interactive-admission.ts +2 -1
- package/src/runtime/execution/process-runtime/lightweight-managed.ts +32 -1
- package/src/runtime/execution/process-runtime/persisted-check.ts +128 -21
- package/src/runtime/execution/process-runtime/resource-claims.ts +60 -3
- package/src/runtime/execution/process-runtime/runtime.ts +132 -15
- package/src/runtime/execution/process-runtime/types.ts +22 -0
- package/src/runtime/execution/workers/executor.ts +16 -13
- package/src/runtime/execution/workers/worker-entry.ts +1 -1
- package/src/runtime/gateway/mcp/execution-tools.ts +2 -207
- package/src/runtime/gateway/mcp/legacy-ios-tool-adapter.ts +2 -61
- package/src/runtime/gateway/mcp/persisted-check-process.ts +2 -3
- package/src/runtime/gateway/mcp/process-tools.ts +2 -289
- package/src/runtime/gateway/mcp/router.ts +2 -1125
- package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +2 -565
- package/src/runtime/gateway/mcp/runtime-tools.ts +2 -6651
- package/src/runtime/gateway/mcp/work-validation-reconciler.ts +2 -1
- package/src/runtime/maintenance/cleanup.ts +38 -24
- package/src/runtime/platform/browser-product-discovery.ts +174 -0
- package/src/runtime/platform/computer-platform.ts +14 -0
- package/src/runtime/platform/executable-discovery.ts +54 -0
- package/src/runtime/platform/fetch-http-server.ts +90 -0
- package/src/runtime/platform/service-manager.ts +138 -0
- package/src/runtime/platform/windows-host-discovery.ts +118 -0
- package/src/runtime/platform/windows-recovery-deployment.ts +67 -0
- package/src/runtime/plugins/action-application.ts +135 -0
- package/src/runtime/plugins/app-store-connect-adapter.ts +9 -21
- package/src/runtime/plugins/browser-adapter.ts +140 -100
- package/src/runtime/plugins/browser-automation-service.ts +15 -36
- package/src/runtime/plugins/browser-macos-bridge.ts +69 -21
- package/src/runtime/plugins/browser-manifest-surface.ts +30 -6
- package/src/runtime/plugins/browser-native-session-service.ts +1 -1
- package/src/runtime/plugins/browser-provider-registry.ts +2 -109
- package/src/runtime/plugins/browser-registration.ts +16 -0
- package/src/runtime/plugins/browser-runtime-contract.ts +2 -154
- package/src/runtime/plugins/browser-runtime.ts +41 -9
- package/src/runtime/plugins/browser-session-authority.ts +101 -291
- package/src/runtime/plugins/browser-session-legacy-migration.ts +203 -0
- package/src/runtime/plugins/browser-session-store.ts +229 -36
- package/src/runtime/plugins/browser-session-types.ts +2 -98
- package/src/runtime/plugins/capability-authorization-grants.ts +17 -0
- package/src/runtime/plugins/catalog-trust.ts +82 -0
- package/src/runtime/plugins/computer-registration.ts +839 -0
- package/src/runtime/plugins/config-store.ts +119 -0
- package/src/runtime/plugins/desktop-operator-external-policy.ts +932 -0
- package/src/runtime/plugins/desktop-operator-registration.ts +111 -8
- package/src/runtime/plugins/errors.ts +8 -1
- package/src/runtime/plugins/external-adapter.ts +45 -322
- package/src/runtime/plugins/external-provider-policy.ts +48 -0
- package/src/runtime/plugins/external-registration.ts +9 -1
- package/src/runtime/plugins/external-unix-socket-probe.cjs +9 -3
- package/src/runtime/plugins/external-unix-socket.ts +64 -193
- package/src/runtime/plugins/first-party-external-registration.ts +163 -0
- package/src/runtime/plugins/first-party-registry.ts +3 -1
- package/src/runtime/plugins/github-adapter.ts +1 -2
- package/src/runtime/plugins/gmail-adapter.ts +6 -6
- package/src/runtime/plugins/google-calendar-adapter.ts +6 -6
- package/src/runtime/plugins/google-shared.ts +31 -46
- package/src/runtime/plugins/google-tasks-adapter.ts +6 -6
- package/src/runtime/plugins/ios-adapter.ts +1 -2
- package/src/runtime/plugins/local-system-adapter.ts +180 -0
- package/src/runtime/plugins/macos-capability-broker.ts +15 -105
- package/src/runtime/plugins/resend-adapter.ts +15 -24
- package/src/runtime/plugins/store.ts +493 -51
- package/src/runtime/plugins/types.ts +8 -1
- package/src/runtime/plugins/uu-remote-rescue-registration.ts +155 -0
- package/src/runtime/plugins/windows-host-recovery-install.ts +68 -0
- package/src/runtime/plugins/windows-host-recovery-registration.ts +85 -0
- package/src/runtime/plugins/xiaohongshu-publish.ts +86 -430
- package/src/runtime/projections/controller-context.ts +12 -6
- package/src/runtime/projections/git-status-sampler.ts +73 -22
- package/src/runtime/projections/materialized-view.ts +14 -8
- package/src/runtime/recovery/capability-action-application.ts +228 -0
- package/src/runtime/recovery/index.ts +3 -0
- package/src/runtime/recovery/lifecycle-inventory.ts +338 -0
- package/src/runtime/recovery/local-jobs-repair.ts +14 -14
- package/src/runtime/recovery/maintenance-executor.ts +358 -76
- package/src/runtime/recovery/quarantine-retention.ts +256 -0
- package/src/runtime/release/identity.ts +6 -0
- package/src/runtime/resources/leases/store.ts +1 -1
- package/src/runtime/root/active-execution-power-assertion.ts +139 -0
- package/src/runtime/root/assistant-learning-loop.ts +45 -0
- package/src/runtime/root/browser-session-composition.ts +59 -0
- package/src/runtime/root/computer-composition.ts +191 -0
- package/src/runtime/root/computer-target-composition.ts +16 -0
- package/src/runtime/root/computer-target-persistence.ts +39 -0
- package/src/runtime/root/controller-round-composition.ts +107 -0
- package/src/runtime/root/controller-services.ts +2 -0
- package/src/runtime/root/entry.ts +4 -3
- package/src/runtime/root/gateway-adapter.ts +3 -3
- package/src/runtime/root/local-bridge.ts +5 -2
- package/src/runtime/root/mcp-transport.ts +5 -5
- package/src/runtime/root/package-connector-service.ts +108 -56
- package/src/runtime/root/package-runtime-release.ts +86 -18
- package/src/runtime/root/package-runtime-service.ts +63 -86
- package/src/runtime/root/release-execution-canary.ts +71 -0
- package/src/runtime/root/release-loader.ts +68 -0
- package/src/runtime/root/release-manifest.ts +106 -2
- package/src/runtime/root/release-materialize.ts +103 -4
- package/src/runtime/root/release-store.ts +31 -2
- package/src/runtime/root/runtime.ts +240 -25
- package/src/runtime/root/scheduled-controller-composition.ts +51 -0
- package/src/runtime/root/scheduler.ts +12 -4
- package/src/runtime/root/service-runner.ts +1 -1
- package/src/runtime/root/service.ts +41 -16
- package/src/runtime/root/status.ts +1 -0
- package/src/runtime/root/types.ts +24 -1
- package/src/runtime/shared/local-bridge-surface.ts +1 -1
- package/src/runtime/shared/process-environment.ts +19 -4
- package/src/runtime/shared/storage-capacity.ts +68 -0
- package/src/runtime/standalone-recovery/controller-home-migration.ts +1116 -0
- package/src/runtime/standalone-recovery/core.ts +984 -230
- package/src/runtime/standalone-recovery/entry.ts +182 -51
- package/src/runtime/standalone-recovery/independent-host-rescue.ts +172 -0
- package/src/runtime/standalone-recovery/installer.ts +212 -104
- package/src/runtime/standalone-recovery/mcp-server.ts +243 -0
- package/src/runtime/standalone-recovery/mutation-identity-contract.ts +21 -0
- package/src/runtime/standalone-recovery/operation-lock.ts +160 -0
- package/src/runtime/standalone-recovery/performance.ts +135 -0
- package/src/runtime/standalone-recovery/release.ts +5 -0
- package/src/runtime/watchdog/workflow-watchdog.ts +1 -1
- package/src/runtime/workflow/schedules/chatgpt-automation-migration.ts +1 -1
- package/src/runtime/workflow/schedules/engine.ts +191 -343
- package/src/runtime/workflow/schedules/pending-activation.ts +1 -1
- package/src/runtime/workflow/schedules/settlement.ts +2 -191
- package/src/runtime/workflow/schedules/store.ts +2 -607
- package/src/runtime/workflow/schedules/types.ts +2 -127
- package/src/runtime/workflow/schedules/work-continuation.ts +180 -15
- package/src/runtime/workflows/first-party/xiaohongshu.ts +122 -0
- package/src/runtime/workflows/runtime.ts +486 -0
- package/src/testing/test-governance.ts +90 -29
- package/assets/templates/helpers/archive-architecture-request.sh +0 -244
- package/docs/architecture/history/README.md +0 -7
- package/docs/architecture/history/chatgpt-handoff-facade.md +0 -397
- package/docs/architecture/history/global-hook-runtime.md +0 -166
- package/docs/architecture/history/ios-semantic-automation-provider-v2.md +0 -170
- package/docs/architecture/requests/archive/2026/20260528-154740-ai-hooks-ai-hooks-hook-input-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-154816-assets-hooks-assets-hooks-hook-input-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-161851-ai-harness-workflow-contract-json-ai-harness-workflow-contract-json.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-161853-scripts-check-task-workflow-sh-scripts-check-task-workflow-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-161911-assets-workflow-contract-v1-json-assets-workflow-contract-v1-json.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-222416-ai-hooks-ai-hooks-hook-input-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-222444-ai-hooks-ai-hooks-pre-edit-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-222457-ai-hooks-ai-hooks-pre-edit-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-222648-ai-hooks-ai-hooks-pre-edit-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-222705-ai-hooks-ai-hooks-worktree-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-222726-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-222727-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-224736-ai-hooks-ai-hooks-hook-input-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-233259-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-233335-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-233456-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-233457-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-233637-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-233638-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260529-000322-root-package-json.md +0 -59
- package/docs/architecture/requests/archive/2026/20260529-004238-ai-hooks-ai-hooks-lib-workflow-state-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260529-020654-ai-harness-policy-json-ai-harness-policy-json.md +0 -63
- package/docs/architecture/requests/archive/2026/20260529-094236-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -60
- package/docs/architecture/requests/archive/2026/20260529-094323-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -60
- package/docs/architecture/requests/archive/2026/20260529-094331-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -60
- package/docs/architecture/requests/archive/2026/20260529-094339-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -60
- package/docs/architecture/requests/archive/2026/20260529-094430-assets-hooks-assets-hooks-session-start-context-sh.md +0 -60
- package/docs/architecture/requests/archive/2026/20260529-094446-ai-hooks-ai-hooks-session-start-context-sh.md +0 -60
- package/docs/architecture/requests/archive/2026/20260613-002040-runtime-harness-hook-adapters.md +0 -68
- package/docs/architecture/requests/archive/2026/root.md +0 -68
- package/docs/architecture/requests/archive/2026/runtime-harness-hook-adapters.md +0 -76
- package/docs/architecture/requests/archive/2026/verification-evals-checks.md +0 -68
- package/docs/architecture/requests/archive/2026/workflow-engine-contract-assets.md +0 -69
- package/scripts/archive-architecture-request.sh +0 -4
- package/scripts/run-skill-evals.ts +0 -1022
|
@@ -0,0 +1,783 @@
|
|
|
1
|
+
import { randomUUID } from 'crypto';
|
|
2
|
+
import { AsyncLocalStorage } from 'node:async_hooks';
|
|
3
|
+
import type { ExecutionJobOrigin } from '../../src/runtime/execution/jobs/types';
|
|
4
|
+
import { browserActions } from '../../src/runtime/plugins/browser-manifest-surface';
|
|
5
|
+
import { controllerPluginRepository, executeControllerScopedPluginAction, getControllerPluginManifest, submitAssistantPluginAction } from '../../src/runtime/plugins/store';
|
|
6
|
+
import {
|
|
7
|
+
CHATGPT_AUTOMATION_SUBMISSION_OUTCOME_UNKNOWN,
|
|
8
|
+
ChatgptProviderDeliveryError,
|
|
9
|
+
DEFAULT_CHATGPT_AUTOMATION_MODEL,
|
|
10
|
+
type ChatgptAutomationReasoning,
|
|
11
|
+
type ChatgptAutomationTabCleanupStatus,
|
|
12
|
+
} from './provider-delivery';
|
|
13
|
+
|
|
14
|
+
const DEFAULT_CHATGPT_AUTOMATION_PLUGIN_MENTION = '@forge';
|
|
15
|
+
|
|
16
|
+
type ChatgptBrowserActionOrigin = Pick<ExecutionJobOrigin, 'surface' | 'actor'>;
|
|
17
|
+
interface ChatgptBrowserActionContext {
|
|
18
|
+
origin: ChatgptBrowserActionOrigin;
|
|
19
|
+
authorizationGrantRefs: Set<string>;
|
|
20
|
+
}
|
|
21
|
+
const chatgptBrowserActionOrigin = new AsyncLocalStorage<ChatgptBrowserActionContext>();
|
|
22
|
+
|
|
23
|
+
export function withChatgptBrowserActionOrigin<T>(
|
|
24
|
+
origin: ChatgptBrowserActionOrigin,
|
|
25
|
+
operation: () => Promise<T>,
|
|
26
|
+
authorizationGrantRefs: Set<string> = new Set(),
|
|
27
|
+
): Promise<T> {
|
|
28
|
+
return chatgptBrowserActionOrigin.run({ origin, authorizationGrantRefs }, operation);
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function withForgePluginMention(prompt: string): string {
|
|
32
|
+
const value = prompt.trim();
|
|
33
|
+
if (!value) throw new Error('CHATGPT_AUTOMATION_PROMPT_REQUIRED');
|
|
34
|
+
if (/^@forge(?:\s|$)/i.test(value)) return value;
|
|
35
|
+
return `${DEFAULT_CHATGPT_AUTOMATION_PLUGIN_MENTION} ${value}`;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const CHATGPT_PROMPT_SELECTOR = 'div#prompt-textarea[contenteditable="true"]';
|
|
39
|
+
const CHATGPT_SEND_SELECTOR = '[data-testid="send-button"], button[aria-label*="Send"], button[data-testid*="send"]';
|
|
40
|
+
const CHATGPT_USER_MESSAGE_SELECTOR = '[data-message-author-role="user"]';
|
|
41
|
+
const CHATGPT_INTELLIGENCE_CONTROL_SELECTORS = [
|
|
42
|
+
'main button, main [role="button"]',
|
|
43
|
+
'button, [role="button"]',
|
|
44
|
+
] as const;
|
|
45
|
+
const CHATGPT_CAPABILITY_SLIDER_SELECTOR = '[role="slider"]';
|
|
46
|
+
const CHATGPT_CAPABILITY_MENUITEM_SELECTOR = '[role="menuitem"][aria-keyshortcuts~="ArrowLeft"][aria-keyshortcuts~="ArrowRight"]';
|
|
47
|
+
|
|
48
|
+
function requestId(workId: string, actionId: string): string {
|
|
49
|
+
return `chatgpt-work:${workId}:${actionId}:${randomUUID()}`;
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
const CHATGPT_BROWSER_AUTHORIZATION_ACTIONS = new Set(
|
|
53
|
+
browserActions()
|
|
54
|
+
.filter((action) => !action.readOnly && action.confirmation === 'authorization')
|
|
55
|
+
.map((action) => action.actionId),
|
|
56
|
+
);
|
|
57
|
+
|
|
58
|
+
const CHATGPT_BROWSER_TRANSPORT_OVERRIDES = {
|
|
59
|
+
browser_mode: 'attach_preferred',
|
|
60
|
+
cdp_attach_fallback: 'fail_closed',
|
|
61
|
+
native_attach_mode: 'auto',
|
|
62
|
+
native_browser_candidates: ['chrome'],
|
|
63
|
+
} as const;
|
|
64
|
+
|
|
65
|
+
export function chatgptBrowserActionArgs(actionId: string, args: Record<string, unknown>): Record<string, unknown> {
|
|
66
|
+
if (actionId === 'configure' || actionId === 'list_sessions' || actionId === 'reconcile_sessions') return args;
|
|
67
|
+
return { ...args, ...CHATGPT_BROWSER_TRANSPORT_OVERRIDES };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
export function chatgptBrowserActionResult(envelope: Record<string, unknown>, actionId: string): Record<string, unknown> {
|
|
71
|
+
const result = envelope.result;
|
|
72
|
+
if (!result || typeof result !== 'object' || Array.isArray(result)) {
|
|
73
|
+
throw new Error(`CHATGPT_BROWSER_ACTION_RESULT_INVALID:${actionId}`);
|
|
74
|
+
}
|
|
75
|
+
return result as Record<string, unknown>;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
async function controllerBrowserAction(
|
|
79
|
+
controllerHome: string,
|
|
80
|
+
workId: string,
|
|
81
|
+
actionId: string,
|
|
82
|
+
args: Record<string, unknown>,
|
|
83
|
+
timeoutMs?: number,
|
|
84
|
+
): Promise<Record<string, unknown>> {
|
|
85
|
+
const context = chatgptBrowserActionOrigin.getStore();
|
|
86
|
+
const origin = context?.origin ?? { surface: 'schedule', actor: 'chatgpt-work-continuation' };
|
|
87
|
+
const actionRequest = {
|
|
88
|
+
pluginId: 'browser',
|
|
89
|
+
actionId,
|
|
90
|
+
requestId: requestId(workId, actionId),
|
|
91
|
+
args: chatgptBrowserActionArgs(actionId, args),
|
|
92
|
+
timeoutMs,
|
|
93
|
+
origin,
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
// Interactive ChatGPT delivery is the only place allowed to establish a
|
|
97
|
+
// reusable controller-scoped Browser grant. Scheduled delivery stays on the
|
|
98
|
+
// low-level executor and must present explicit refs already bound to this Work.
|
|
99
|
+
if (origin.surface === 'chatgpt-action' && CHATGPT_BROWSER_AUTHORIZATION_ACTIONS.has(actionId)) {
|
|
100
|
+
const submitted = await submitAssistantPluginAction(
|
|
101
|
+
controllerHome,
|
|
102
|
+
controllerPluginRepository(controllerHome),
|
|
103
|
+
actionRequest,
|
|
104
|
+
);
|
|
105
|
+
const grantId = submitted.authorization?.grantId?.trim();
|
|
106
|
+
if (grantId) context?.authorizationGrantRefs.add(grantId);
|
|
107
|
+
if (!submitted.result) throw new Error(`CHATGPT_BROWSER_ACTION_RESULT_INVALID:${actionId}`);
|
|
108
|
+
return chatgptBrowserActionResult(submitted.result, actionId);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
const envelope = await executeControllerScopedPluginAction({
|
|
112
|
+
controllerHome,
|
|
113
|
+
...actionRequest,
|
|
114
|
+
authorizationGrantRefs: [...(context?.authorizationGrantRefs ?? [])],
|
|
115
|
+
});
|
|
116
|
+
return chatgptBrowserActionResult(envelope, actionId);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export async function ensureControllerChatgptBrowser(controllerHome: string, workId: string): Promise<void> {
|
|
120
|
+
// Browser configure is not a read: it persists configuration and closes managed
|
|
121
|
+
// contexts. Scheduled continuations must not disturb an already-enabled provider
|
|
122
|
+
// merely to prove it is available. Read the existing controller-scoped manifest
|
|
123
|
+
// authority, and only enable it when that persisted authority is explicitly disabled;
|
|
124
|
+
// action-level transport overrides still fail closed later.
|
|
125
|
+
if (getControllerPluginManifest(controllerHome, 'browser').enabled) return;
|
|
126
|
+
await controllerBrowserAction(controllerHome, workId, 'configure', { enabled: true });
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
function stringField(value: unknown): string | undefined {
|
|
130
|
+
return typeof value === 'string' && value.trim() ? value.trim() : undefined;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
interface BrowserQueryMatch {
|
|
134
|
+
text?: string;
|
|
135
|
+
selectorHint?: string;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function queryMatches(result: Record<string, unknown> | undefined): BrowserQueryMatch[] {
|
|
139
|
+
if (!result || !Array.isArray(result.matches)) return [];
|
|
140
|
+
return result.matches.filter((value): value is BrowserQueryMatch => Boolean(value) && typeof value === 'object');
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function matchText(value: BrowserQueryMatch): string {
|
|
144
|
+
return typeof value.text === 'string' ? value.text.trim() : '';
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
function matchSelector(value: BrowserQueryMatch | undefined): string | undefined {
|
|
148
|
+
return typeof value?.selectorHint === 'string' && value.selectorHint.trim() ? value.selectorHint.trim() : undefined;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
function normalizeChatgptOutboundText(value: string): string {
|
|
152
|
+
return value.replace(/\s+/g, ' ').trim();
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
const CHATGPT_OUTBOUND_MESSAGE_UI_SUFFIXES = ['收起', 'Collapse', 'Show less'] as const;
|
|
156
|
+
const MAX_CHATGPT_OUTBOUND_VERIFICATION_CHARS = 100_000;
|
|
157
|
+
const MIN_TRUNCATED_CHATGPT_OUTBOUND_PREFIX_CHARS = 256;
|
|
158
|
+
|
|
159
|
+
export function chatgptOutboundMessageMatchesPrompt(
|
|
160
|
+
messageText: string,
|
|
161
|
+
prompt: string,
|
|
162
|
+
options: { truncated?: boolean } = {},
|
|
163
|
+
): boolean {
|
|
164
|
+
const message = normalizeChatgptOutboundText(messageText);
|
|
165
|
+
const normalizedPrompt = normalizeChatgptOutboundText(prompt);
|
|
166
|
+
if (!message || !normalizedPrompt) return false;
|
|
167
|
+
if (message === normalizedPrompt) return true;
|
|
168
|
+
if (CHATGPT_OUTBOUND_MESSAGE_UI_SUFFIXES.some((suffix) => message === `${normalizedPrompt} ${suffix}`)) return true;
|
|
169
|
+
// Browser text extraction is deliberately bounded. A large ControllerRound
|
|
170
|
+
// prompt can exceed that bound, so requiring exact equality makes successful
|
|
171
|
+
// submissions mechanically unverifiable. Accept only an explicitly reported
|
|
172
|
+
// truncation of a substantial exact prefix; ordinary partial/mismatched text
|
|
173
|
+
// remains insufficient evidence.
|
|
174
|
+
return options.truncated === true
|
|
175
|
+
&& message.length >= MIN_TRUNCATED_CHATGPT_OUTBOUND_PREFIX_CHARS
|
|
176
|
+
&& normalizedPrompt.startsWith(message);
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
async function latestChatgptUserMessage(
|
|
180
|
+
controllerHome: string,
|
|
181
|
+
workId: string,
|
|
182
|
+
browserSessionId: string,
|
|
183
|
+
timeoutMs?: number,
|
|
184
|
+
): Promise<{ selector?: string; preview: string; url?: string }> {
|
|
185
|
+
const result = await controllerBrowserAction(controllerHome, workId, 'query_all', {
|
|
186
|
+
session_id: browserSessionId,
|
|
187
|
+
selector: CHATGPT_USER_MESSAGE_SELECTOR,
|
|
188
|
+
limit: 1,
|
|
189
|
+
from_end: true,
|
|
190
|
+
timeout_ms: Math.min(timeoutMs ?? 3_000, 3_000),
|
|
191
|
+
}, timeoutMs);
|
|
192
|
+
const latest = queryMatches(result).at(-1);
|
|
193
|
+
return { selector: matchSelector(latest), preview: latest ? matchText(latest) : '', url: resultUrl(result) };
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
async function fullChatgptMessageText(
|
|
197
|
+
controllerHome: string,
|
|
198
|
+
workId: string,
|
|
199
|
+
browserSessionId: string,
|
|
200
|
+
message: { selector?: string; preview: string },
|
|
201
|
+
timeoutMs?: number,
|
|
202
|
+
): Promise<{ text: string; truncated: boolean }> {
|
|
203
|
+
if (!message.selector) return { text: message.preview, truncated: false };
|
|
204
|
+
const result = await controllerBrowserAction(controllerHome, workId, 'get_text', {
|
|
205
|
+
session_id: browserSessionId,
|
|
206
|
+
selector: message.selector,
|
|
207
|
+
max_chars: MAX_CHATGPT_OUTBOUND_VERIFICATION_CHARS,
|
|
208
|
+
timeout_ms: Math.min(timeoutMs ?? 3_000, 3_000),
|
|
209
|
+
}, timeoutMs).catch(() => undefined);
|
|
210
|
+
return {
|
|
211
|
+
text: stringField(result?.text) ?? message.preview,
|
|
212
|
+
truncated: result?.truncated === true,
|
|
213
|
+
};
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function chatgptSendControlUnavailable(error: unknown): boolean {
|
|
217
|
+
return error instanceof Error && (error.message.includes('Selector') && error.message.includes('not found'));
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
function normalizeExecutionControlLabel(label: string | undefined): string {
|
|
221
|
+
return (label ?? '').trim().toLowerCase().replace(/[\s_-]+/g, '');
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
export function chatgptAutomationReasoningLevelFromLabel(label: string | undefined): ChatgptAutomationReasoning | undefined {
|
|
225
|
+
const normalized = normalizeExecutionControlLabel(label);
|
|
226
|
+
if (!normalized) return undefined;
|
|
227
|
+
|
|
228
|
+
const exact = new Map<string, ChatgptAutomationReasoning>([
|
|
229
|
+
['medium', 'medium'], ['中', 'medium'], ['中等', 'medium'],
|
|
230
|
+
['high', 'high'], ['高', 'high'],
|
|
231
|
+
['xhigh', 'xhigh'], ['extrahigh', 'xhigh'], ['超高', 'xhigh'], ['极高', 'xhigh'],
|
|
232
|
+
]);
|
|
233
|
+
const exactLevel = exact.get(normalized);
|
|
234
|
+
if (exactLevel) return exactLevel;
|
|
235
|
+
|
|
236
|
+
const hasReasoningContext = ['reasoning', 'thinking', '推理', '思考']
|
|
237
|
+
.some((token) => normalized.includes(token));
|
|
238
|
+
if (!hasReasoningContext) return undefined;
|
|
239
|
+
if (['xhigh', 'extrahigh', '超高', '极高'].some((token) => normalized.includes(token))) return 'xhigh';
|
|
240
|
+
if (['medium', '中等'].some((token) => normalized.includes(token))) return 'medium';
|
|
241
|
+
if (['high', '高'].some((token) => normalized.includes(token))) return 'high';
|
|
242
|
+
return undefined;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
function modelLabelMatches(label: string | undefined, model: string): boolean {
|
|
246
|
+
if (model !== DEFAULT_CHATGPT_AUTOMATION_MODEL || !label) return false;
|
|
247
|
+
const normalized = normalizeExecutionControlLabel(label);
|
|
248
|
+
return normalized.includes('5.6sol') || normalized.includes('gpt5.6sol');
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
export function chatgptAutomationModelFamilyMenuTrigger(
|
|
252
|
+
label: string | undefined,
|
|
253
|
+
ariaHasPopup: string | undefined,
|
|
254
|
+
): boolean {
|
|
255
|
+
if (ariaHasPopup !== 'menu') return false;
|
|
256
|
+
const normalized = normalizeExecutionControlLabel(label);
|
|
257
|
+
// ChatGPT's composer can rotate model names independently of Forge releases.
|
|
258
|
+
// Locate the combined intelligence control by a bounded model-family prefix
|
|
259
|
+
// plus its menu-trigger role; do not treat arbitrary GPT prose/buttons as authority.
|
|
260
|
+
return /^gpt\d/.test(normalized);
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
function reasoningLabelMatches(label: string | undefined, reasoning: ChatgptAutomationReasoning): boolean {
|
|
264
|
+
return chatgptAutomationReasoningLevelFromLabel(label) === reasoning;
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
function isReasoningControlLabel(label: string | undefined): boolean {
|
|
268
|
+
return reasoningLabelMatches(label, 'medium')
|
|
269
|
+
|| reasoningLabelMatches(label, 'high')
|
|
270
|
+
|| reasoningLabelMatches(label, 'xhigh');
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
function reasoningSliderValue(reasoning: ChatgptAutomationReasoning): number {
|
|
274
|
+
if (reasoning === 'medium') return 2;
|
|
275
|
+
if (reasoning === 'high') return 3;
|
|
276
|
+
return 4;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
async function findChatgptIntelligenceControl(
|
|
280
|
+
controllerHome: string,
|
|
281
|
+
workId: string,
|
|
282
|
+
browserSessionId: string,
|
|
283
|
+
timeoutMs?: number,
|
|
284
|
+
): Promise<BrowserQueryMatch | undefined> {
|
|
285
|
+
for (const selector of CHATGPT_INTELLIGENCE_CONTROL_SELECTORS) {
|
|
286
|
+
const result = await controllerBrowserAction(controllerHome, workId, 'query_all', {
|
|
287
|
+
session_id: browserSessionId,
|
|
288
|
+
selector,
|
|
289
|
+
// Prefer the composer/main region so sidebar history cannot crowd the
|
|
290
|
+
// reasoning control out of a bounded query. Keep a larger global fallback
|
|
291
|
+
// for ChatGPT layouts that place the control outside <main>.
|
|
292
|
+
limit: chatgptAutomationControlQueryLimit(selector),
|
|
293
|
+
timeout_ms: timeoutMs ?? 60_000,
|
|
294
|
+
}, timeoutMs);
|
|
295
|
+
const candidates = queryMatches(result);
|
|
296
|
+
const exactMatch = candidates.find((candidate) => {
|
|
297
|
+
const label = matchText(candidate);
|
|
298
|
+
return modelLabelMatches(label, DEFAULT_CHATGPT_AUTOMATION_MODEL) || isReasoningControlLabel(label);
|
|
299
|
+
});
|
|
300
|
+
if (exactMatch) return exactMatch;
|
|
301
|
+
for (const candidate of candidates) {
|
|
302
|
+
const candidateSelector = matchSelector(candidate);
|
|
303
|
+
if (!candidateSelector || !/^gpt\d/.test(normalizeExecutionControlLabel(matchText(candidate)))) continue;
|
|
304
|
+
const popup = await controllerBrowserAction(controllerHome, workId, 'get_attribute', {
|
|
305
|
+
session_id: browserSessionId,
|
|
306
|
+
selector: candidateSelector,
|
|
307
|
+
attribute: 'aria-haspopup',
|
|
308
|
+
timeout_ms: timeoutMs ?? 60_000,
|
|
309
|
+
}, timeoutMs).catch(() => undefined);
|
|
310
|
+
if (chatgptAutomationModelFamilyMenuTrigger(matchText(candidate), stringField(popup?.value))) return candidate;
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
return undefined;
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
export function chatgptAutomationControlQueryLimit(selector: string): number {
|
|
317
|
+
// Long tool-heavy conversations can contribute well over 80 buttons before
|
|
318
|
+
// the composer controls. Keep the preferred <main> scan bounded but large
|
|
319
|
+
// enough that the current reasoning control is not crowded out by history.
|
|
320
|
+
return selector.startsWith('main ') ? 160 : 320;
|
|
321
|
+
}
|
|
322
|
+
|
|
323
|
+
export function chatgptAutomationControlWaitBudgets(timeoutMs?: number): { waitBudgetMs: number; probeTimeoutMs: number } {
|
|
324
|
+
const waitBudgetMs = Math.min(Math.max(timeoutMs ?? 30_000, 1_000), 30_000);
|
|
325
|
+
// Native Chrome attachment has a few seconds of Apple Events/DOM latency on
|
|
326
|
+
// long conversations. Keep each probe bounded while allowing a real query to
|
|
327
|
+
// complete; the total readiness window remains capped at 30 seconds.
|
|
328
|
+
return { waitBudgetMs, probeTimeoutMs: Math.min(waitBudgetMs, 5_000) };
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
async function waitForChatgptIntelligenceControl(
|
|
332
|
+
controllerHome: string,
|
|
333
|
+
workId: string,
|
|
334
|
+
browserSessionId: string,
|
|
335
|
+
timeoutMs?: number,
|
|
336
|
+
): Promise<BrowserQueryMatch | undefined> {
|
|
337
|
+
const { waitBudgetMs, probeTimeoutMs } = chatgptAutomationControlWaitBudgets(timeoutMs);
|
|
338
|
+
const deadline = Date.now() + waitBudgetMs;
|
|
339
|
+
do {
|
|
340
|
+
const control = await findChatgptIntelligenceControl(controllerHome, workId, browserSessionId, probeTimeoutMs);
|
|
341
|
+
if (control) return control;
|
|
342
|
+
if (Date.now() >= deadline) break;
|
|
343
|
+
await new Promise((resolveWait) => setTimeout(resolveWait, 500));
|
|
344
|
+
} while (Date.now() < deadline);
|
|
345
|
+
return undefined;
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
export function chatgptAutomationPageFailure(
|
|
349
|
+
bodyText: string | undefined,
|
|
350
|
+
composerAvailable: boolean,
|
|
351
|
+
): 'CHATGPT_AUTOMATION_LOGIN_REQUIRED' | 'CHATGPT_AUTOMATION_COMPOSER_UNAVAILABLE' | undefined {
|
|
352
|
+
if (composerAvailable) return undefined;
|
|
353
|
+
const normalized = (bodyText ?? '').toLowerCase();
|
|
354
|
+
const loginMarkers = [
|
|
355
|
+
'log in', 'sign up', 'continue with google', 'continue with apple',
|
|
356
|
+
'登录', '登陆', '注册', '使用 google 继续', '使用 apple 继续',
|
|
357
|
+
];
|
|
358
|
+
return loginMarkers.some((marker) => normalized.includes(marker))
|
|
359
|
+
? 'CHATGPT_AUTOMATION_LOGIN_REQUIRED'
|
|
360
|
+
: 'CHATGPT_AUTOMATION_COMPOSER_UNAVAILABLE';
|
|
361
|
+
}
|
|
362
|
+
|
|
363
|
+
async function assertChatgptAutomationComposerReady(
|
|
364
|
+
controllerHome: string,
|
|
365
|
+
workId: string,
|
|
366
|
+
browserSessionId: string,
|
|
367
|
+
timeoutMs?: number,
|
|
368
|
+
): Promise<void> {
|
|
369
|
+
const composerResult = await controllerBrowserAction(controllerHome, workId, 'query_all', {
|
|
370
|
+
session_id: browserSessionId,
|
|
371
|
+
selector: CHATGPT_PROMPT_SELECTOR,
|
|
372
|
+
limit: 1,
|
|
373
|
+
timeout_ms: Math.min(timeoutMs ?? 5_000, 5_000),
|
|
374
|
+
}, timeoutMs).catch(() => undefined);
|
|
375
|
+
const composerAvailable = queryMatches(composerResult).length > 0;
|
|
376
|
+
if (composerAvailable) return;
|
|
377
|
+
|
|
378
|
+
const bodyResult = await controllerBrowserAction(controllerHome, workId, 'get_text', {
|
|
379
|
+
session_id: browserSessionId,
|
|
380
|
+
selector: 'body',
|
|
381
|
+
max_chars: 4_000,
|
|
382
|
+
timeout_ms: Math.min(timeoutMs ?? 5_000, 5_000),
|
|
383
|
+
}, timeoutMs).catch(() => undefined);
|
|
384
|
+
const failure = chatgptAutomationPageFailure(stringField(bodyResult?.text), composerAvailable);
|
|
385
|
+
throw new Error(failure ?? 'CHATGPT_AUTOMATION_COMPOSER_UNAVAILABLE');
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
export async function ensureChatgptExecutionPreference(
|
|
389
|
+
controllerHome: string,
|
|
390
|
+
workId: string,
|
|
391
|
+
browserSessionId: string,
|
|
392
|
+
model: string,
|
|
393
|
+
reasoning: ChatgptAutomationReasoning,
|
|
394
|
+
timeoutMs?: number,
|
|
395
|
+
): Promise<boolean> {
|
|
396
|
+
if (model !== DEFAULT_CHATGPT_AUTOMATION_MODEL) throw new Error(`CHATGPT_AUTOMATION_MODEL_UNSUPPORTED:${model}`);
|
|
397
|
+
let control = await waitForChatgptIntelligenceControl(controllerHome, workId, browserSessionId, timeoutMs);
|
|
398
|
+
if (!control) {
|
|
399
|
+
await assertChatgptAutomationComposerReady(controllerHome, workId, browserSessionId, timeoutMs);
|
|
400
|
+
throw new Error('CHATGPT_AUTOMATION_INTELLIGENCE_CONTROL_UNAVAILABLE');
|
|
401
|
+
}
|
|
402
|
+
// Current ChatGPT UI exposes the reasoning level (for example `高`) on the
|
|
403
|
+
// composer control instead of the model label. The model remains fail-closed
|
|
404
|
+
// through normalizeModel above; only the user-adjustable reasoning control is
|
|
405
|
+
// required to be observable in the page before unattended dispatch proceeds.
|
|
406
|
+
if (reasoningLabelMatches(matchText(control), reasoning)) return true;
|
|
407
|
+
let controlSelector = matchSelector(control);
|
|
408
|
+
if (!controlSelector) throw new Error('CHATGPT_AUTOMATION_INTELLIGENCE_CONTROL_UNAVAILABLE');
|
|
409
|
+
|
|
410
|
+
const expanded = await controllerBrowserAction(controllerHome, workId, 'get_attribute', {
|
|
411
|
+
session_id: browserSessionId,
|
|
412
|
+
selector: controlSelector,
|
|
413
|
+
attribute: 'aria-expanded',
|
|
414
|
+
timeout_ms: timeoutMs ?? 60_000,
|
|
415
|
+
}, timeoutMs).catch(() => undefined);
|
|
416
|
+
if (stringField(expanded?.value) !== 'true') {
|
|
417
|
+
await controllerBrowserAction(controllerHome, workId, 'press', {
|
|
418
|
+
session_id: browserSessionId,
|
|
419
|
+
selector: controlSelector,
|
|
420
|
+
key: 'ArrowDown',
|
|
421
|
+
timeout_ms: timeoutMs ?? 60_000,
|
|
422
|
+
post_action_wait_ms: 150,
|
|
423
|
+
}, timeoutMs);
|
|
424
|
+
}
|
|
425
|
+
|
|
426
|
+
const currentValueResult = await controllerBrowserAction(controllerHome, workId, 'get_attribute', {
|
|
427
|
+
session_id: browserSessionId,
|
|
428
|
+
selector: CHATGPT_CAPABILITY_SLIDER_SELECTOR,
|
|
429
|
+
attribute: 'aria-valuenow',
|
|
430
|
+
timeout_ms: timeoutMs ?? 60_000,
|
|
431
|
+
}, timeoutMs).catch(() => undefined);
|
|
432
|
+
const currentValue = Number(stringField(currentValueResult?.value));
|
|
433
|
+
const targetValue = reasoningSliderValue(reasoning);
|
|
434
|
+
if (!Number.isInteger(currentValue) || currentValue < 0 || currentValue > 4) {
|
|
435
|
+
throw new Error(`CHATGPT_AUTOMATION_REASONING_STATE_UNAVAILABLE:${reasoning}`);
|
|
436
|
+
}
|
|
437
|
+
const direction = targetValue > currentValue ? 'ArrowRight' : 'ArrowLeft';
|
|
438
|
+
for (let index = 0; index < Math.abs(targetValue - currentValue); index += 1) {
|
|
439
|
+
await controllerBrowserAction(controllerHome, workId, 'press', {
|
|
440
|
+
session_id: browserSessionId,
|
|
441
|
+
selector: CHATGPT_CAPABILITY_MENUITEM_SELECTOR,
|
|
442
|
+
key: direction,
|
|
443
|
+
timeout_ms: timeoutMs ?? 60_000,
|
|
444
|
+
post_action_wait_ms: 120,
|
|
445
|
+
}, timeoutMs);
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
const verifiedValueResult = await controllerBrowserAction(controllerHome, workId, 'get_attribute', {
|
|
449
|
+
session_id: browserSessionId,
|
|
450
|
+
selector: CHATGPT_CAPABILITY_SLIDER_SELECTOR,
|
|
451
|
+
attribute: 'aria-valuenow',
|
|
452
|
+
timeout_ms: timeoutMs ?? 60_000,
|
|
453
|
+
}, timeoutMs).catch(() => undefined);
|
|
454
|
+
if (Number(stringField(verifiedValueResult?.value)) !== targetValue) {
|
|
455
|
+
throw new Error(`CHATGPT_AUTOMATION_REASONING_NOT_VERIFIED:${reasoning}`);
|
|
456
|
+
}
|
|
457
|
+
control = await findChatgptIntelligenceControl(controllerHome, workId, browserSessionId, timeoutMs) ?? control;
|
|
458
|
+
if (isReasoningControlLabel(matchText(control)) && !reasoningLabelMatches(matchText(control), reasoning)) {
|
|
459
|
+
throw new Error(`CHATGPT_AUTOMATION_REASONING_NOT_VERIFIED:${reasoning}`);
|
|
460
|
+
}
|
|
461
|
+
controlSelector = matchSelector(control) ?? controlSelector;
|
|
462
|
+
await controllerBrowserAction(controllerHome, workId, 'press', {
|
|
463
|
+
session_id: browserSessionId,
|
|
464
|
+
selector: controlSelector,
|
|
465
|
+
key: 'Escape',
|
|
466
|
+
timeout_ms: timeoutMs ?? 60_000,
|
|
467
|
+
post_action_wait_ms: 100,
|
|
468
|
+
}, timeoutMs).catch(() => undefined);
|
|
469
|
+
return true;
|
|
470
|
+
}
|
|
471
|
+
|
|
472
|
+
function resultUrl(result: Record<string, unknown>): string | undefined {
|
|
473
|
+
return stringField(result.url)
|
|
474
|
+
?? stringField((result.session as Record<string, unknown> | undefined)?.url);
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
function resultSessionId(result: Record<string, unknown>): string | undefined {
|
|
478
|
+
return stringField(result.session_id)
|
|
479
|
+
?? stringField((result.session as Record<string, unknown> | undefined)?.sessionId);
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
interface ChatgptBrowserSessionInventoryItem {
|
|
483
|
+
sessionId?: unknown;
|
|
484
|
+
url?: unknown;
|
|
485
|
+
liveness?: unknown;
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
function chatgptUrlMatchesContinuationTarget(observedUrl: string, targetUrl: string): boolean {
|
|
489
|
+
try {
|
|
490
|
+
const observed = new URL(observedUrl);
|
|
491
|
+
const target = new URL(targetUrl);
|
|
492
|
+
const allowedHosts = new Set(['chatgpt.com', 'www.chatgpt.com', 'chat.openai.com']);
|
|
493
|
+
if (observed.protocol !== 'https:' || target.protocol !== 'https:' || !allowedHosts.has(observed.hostname) || !allowedHosts.has(target.hostname)) return false;
|
|
494
|
+
const targetConversation = /\/c\/([^/?#]+)/.exec(target.pathname)?.[1];
|
|
495
|
+
if (targetConversation) {
|
|
496
|
+
const observedConversation = /\/c\/([^/?#]+)/.exec(observed.pathname)?.[1];
|
|
497
|
+
return observedConversation === targetConversation;
|
|
498
|
+
}
|
|
499
|
+
return observed.pathname === target.pathname;
|
|
500
|
+
} catch {
|
|
501
|
+
return false;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
function completeChatgptBrowserInventorySessions(
|
|
506
|
+
inventory: Record<string, unknown>,
|
|
507
|
+
): ChatgptBrowserSessionInventoryItem[] | undefined {
|
|
508
|
+
if (stringField(inventory.nextCursor)) return undefined;
|
|
509
|
+
return Array.isArray(inventory.sessions)
|
|
510
|
+
? inventory.sessions as ChatgptBrowserSessionInventoryItem[]
|
|
511
|
+
: [];
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
export function reconciledNewChatgptOpenPageSessionId(
|
|
515
|
+
beforeInventory: Record<string, unknown>,
|
|
516
|
+
afterInventory: Record<string, unknown>,
|
|
517
|
+
targetUrl: string,
|
|
518
|
+
): string | undefined {
|
|
519
|
+
const beforeSessions = completeChatgptBrowserInventorySessions(beforeInventory);
|
|
520
|
+
const afterSessions = completeChatgptBrowserInventorySessions(afterInventory);
|
|
521
|
+
if (!beforeSessions || !afterSessions) return undefined;
|
|
522
|
+
const beforeIds = new Set(beforeSessions
|
|
523
|
+
.map((entry) => stringField(entry.sessionId))
|
|
524
|
+
.filter((value): value is string => Boolean(value)));
|
|
525
|
+
const matches = afterSessions.flatMap((entry) => {
|
|
526
|
+
const sessionId = stringField(entry.sessionId);
|
|
527
|
+
const url = stringField(entry.url);
|
|
528
|
+
return sessionId
|
|
529
|
+
&& !beforeIds.has(sessionId)
|
|
530
|
+
&& entry.liveness === 'live'
|
|
531
|
+
&& url
|
|
532
|
+
&& chatgptUrlMatchesContinuationTarget(url, targetUrl)
|
|
533
|
+
? [sessionId]
|
|
534
|
+
: [];
|
|
535
|
+
});
|
|
536
|
+
return matches.length === 1 ? matches[0] : undefined;
|
|
537
|
+
}
|
|
538
|
+
|
|
539
|
+
function browserMutationOutcomeUnknown(error: unknown, actionId: string): boolean {
|
|
540
|
+
return error instanceof Error
|
|
541
|
+
&& error.message.includes('PLUGIN_BROWSER_MUTATION_OUTCOME_UNKNOWN')
|
|
542
|
+
&& error.message.includes(`Browser action ${actionId}`);
|
|
543
|
+
}
|
|
544
|
+
|
|
545
|
+
export function chatgptAutomationNavigationRequiresReplacement(error: unknown): boolean {
|
|
546
|
+
if (!(error instanceof Error)) return false;
|
|
547
|
+
return [
|
|
548
|
+
'BROWSER_AUTOMATION_BACKGROUND_NAVIGATION_REQUIRES_REPLACEMENT',
|
|
549
|
+
'PLUGIN_BROWSER_SESSION_STATE_LOST',
|
|
550
|
+
'PLUGIN_SESSION_NOT_FOUND',
|
|
551
|
+
'PLUGIN_BROWSER_NATIVE_TAB_IDENTITY_UNPROVEN',
|
|
552
|
+
].some((marker) => error.message.includes(marker));
|
|
553
|
+
}
|
|
554
|
+
|
|
555
|
+
export async function closeChatgptAutomationTabAfterDispatch(
|
|
556
|
+
controllerHome: string,
|
|
557
|
+
workId: string,
|
|
558
|
+
browserSessionId: string,
|
|
559
|
+
timeoutMs?: number,
|
|
560
|
+
): Promise<{ status: ChatgptAutomationTabCleanupStatus; error?: { code: string; message: string } }> {
|
|
561
|
+
try {
|
|
562
|
+
const closed = await controllerBrowserAction(controllerHome, workId, 'close_page', {
|
|
563
|
+
session_id: browserSessionId,
|
|
564
|
+
}, Math.min(timeoutMs ?? 15_000, 15_000));
|
|
565
|
+
if (closed.preservedUserOwnedTab === true) return { status: 'preserved_user_owned' };
|
|
566
|
+
if (closed.resourceClosed === true) return { status: 'closed' };
|
|
567
|
+
return { status: 'session_closed' };
|
|
568
|
+
} catch (error) {
|
|
569
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
570
|
+
return {
|
|
571
|
+
status: 'failed',
|
|
572
|
+
error: {
|
|
573
|
+
code: error instanceof Error && error.message.includes(':') ? error.message.split(':', 1)[0] : 'CHATGPT_AUTOMATION_TAB_CLEANUP_FAILED',
|
|
574
|
+
message,
|
|
575
|
+
},
|
|
576
|
+
};
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
/**
|
|
581
|
+
* Settles the browser resource for one completed Work-bound ChatGPT round.
|
|
582
|
+
* Conversation identity is durable in the Work binding and intentionally
|
|
583
|
+
* survives this ephemeral tab/session cleanup. Browser ownership policy keeps
|
|
584
|
+
* user-owned native tabs intact.
|
|
585
|
+
*/
|
|
586
|
+
export async function settleWorkChatgptAutomationTab(input: {
|
|
587
|
+
controllerHome: string;
|
|
588
|
+
workId: string;
|
|
589
|
+
browserSessionId: string;
|
|
590
|
+
timeoutMs?: number;
|
|
591
|
+
}): Promise<{ status: ChatgptAutomationTabCleanupStatus; error?: { code: string; message: string } }> {
|
|
592
|
+
if (input.browserSessionId.startsWith('forge-chatgpt-bridge-')) return { status: 'session_closed' };
|
|
593
|
+
return closeChatgptAutomationTabAfterDispatch(
|
|
594
|
+
input.controllerHome,
|
|
595
|
+
input.workId,
|
|
596
|
+
input.browserSessionId,
|
|
597
|
+
input.timeoutMs,
|
|
598
|
+
);
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
export function isChatgptConversationUrl(value: string): boolean {
|
|
602
|
+
try {
|
|
603
|
+
const url = new URL(value);
|
|
604
|
+
return url.protocol === 'https:'
|
|
605
|
+
&& ['chatgpt.com', 'www.chatgpt.com', 'chat.openai.com'].includes(url.hostname)
|
|
606
|
+
&& /\/c\/[^/?#]+/.test(url.pathname);
|
|
607
|
+
} catch {
|
|
608
|
+
return false;
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
export async function navigateWorkConversation(
|
|
613
|
+
controllerHome: string,
|
|
614
|
+
workId: string,
|
|
615
|
+
browserSessionId: string,
|
|
616
|
+
targetUrl: string,
|
|
617
|
+
timeoutMs?: number,
|
|
618
|
+
): Promise<{ submissionTargetUrl: string; recoveredFromStaleBinding: boolean; browserSessionId: string }> {
|
|
619
|
+
const navigate = async (sessionId: string, url: string) => controllerBrowserAction(controllerHome, workId, 'navigate', {
|
|
620
|
+
session_id: sessionId,
|
|
621
|
+
url,
|
|
622
|
+
wait_until: 'domcontentloaded',
|
|
623
|
+
timeout_ms: timeoutMs ?? 60_000,
|
|
624
|
+
retries: 1,
|
|
625
|
+
}, timeoutMs);
|
|
626
|
+
const openReplacement = async (sessionId: string, url: string): Promise<string> => {
|
|
627
|
+
// Fresh ControllerRound transport already owns an explicit Browser session
|
|
628
|
+
// identity. Use create_session with that exact id so native Browser never has
|
|
629
|
+
// to guess among multiple reusable Forge-owned tabs. The Browser adapter still
|
|
630
|
+
// owns tab identity/replacement and can recover a stale saved tab behind this id.
|
|
631
|
+
try {
|
|
632
|
+
const opened = await controllerBrowserAction(controllerHome, workId, 'create_session', {
|
|
633
|
+
session_id: sessionId,
|
|
634
|
+
url,
|
|
635
|
+
wait_until: 'domcontentloaded',
|
|
636
|
+
timeout_ms: timeoutMs ?? 60_000,
|
|
637
|
+
retries: 1,
|
|
638
|
+
}, timeoutMs);
|
|
639
|
+
const replacementSessionId = resultSessionId(opened);
|
|
640
|
+
if (replacementSessionId !== sessionId) throw new Error('CHATGPT_AUTOMATION_REPLACEMENT_SESSION_NOT_CONFIRMED');
|
|
641
|
+
return replacementSessionId;
|
|
642
|
+
} catch (error) {
|
|
643
|
+
if (!browserMutationOutcomeUnknown(error, 'create_session')) throw error;
|
|
644
|
+
try {
|
|
645
|
+
const verified = await controllerBrowserAction(controllerHome, workId, 'verify_state', {
|
|
646
|
+
session_id: sessionId,
|
|
647
|
+
expected_url: url,
|
|
648
|
+
}, timeoutMs);
|
|
649
|
+
if (verified.matched === true && stringField(verified.sessionId) === sessionId) return sessionId;
|
|
650
|
+
} catch {
|
|
651
|
+
// Preserve the original create_session mutation-unknown evidence.
|
|
652
|
+
}
|
|
653
|
+
throw error;
|
|
654
|
+
}
|
|
655
|
+
};
|
|
656
|
+
try {
|
|
657
|
+
// Prove the exact saved Browser resource is still attachable before dispatching
|
|
658
|
+
// a navigation mutation. A stale native tab must be replaced before any write;
|
|
659
|
+
// discovering staleness only after mutation creates an avoidable outcome-unknown window.
|
|
660
|
+
try {
|
|
661
|
+
await controllerBrowserAction(controllerHome, workId, 'verify_state', {
|
|
662
|
+
session_id: browserSessionId,
|
|
663
|
+
expected_url: targetUrl,
|
|
664
|
+
}, timeoutMs);
|
|
665
|
+
} catch (preflightError) {
|
|
666
|
+
if (!chatgptAutomationNavigationRequiresReplacement(preflightError)) throw preflightError;
|
|
667
|
+
const replacementSessionId = await openReplacement(browserSessionId, targetUrl);
|
|
668
|
+
return { submissionTargetUrl: targetUrl, recoveredFromStaleBinding: true, browserSessionId: replacementSessionId };
|
|
669
|
+
}
|
|
670
|
+
await navigate(browserSessionId, targetUrl);
|
|
671
|
+
return { submissionTargetUrl: targetUrl, recoveredFromStaleBinding: false, browserSessionId };
|
|
672
|
+
} catch (error) {
|
|
673
|
+
if (browserMutationOutcomeUnknown(error, 'navigate')) {
|
|
674
|
+
try {
|
|
675
|
+
const verified = await controllerBrowserAction(controllerHome, workId, 'verify_state', {
|
|
676
|
+
session_id: browserSessionId,
|
|
677
|
+
expected_url: targetUrl,
|
|
678
|
+
}, timeoutMs);
|
|
679
|
+
if (verified.matched === true && stringField(verified.sessionId) === browserSessionId) {
|
|
680
|
+
return { submissionTargetUrl: targetUrl, recoveredFromStaleBinding: false, browserSessionId };
|
|
681
|
+
}
|
|
682
|
+
} catch {
|
|
683
|
+
// Preserve the original mutation-unknown evidence. Verification failure is
|
|
684
|
+
// not authority to replay navigate or to invent a replacement session.
|
|
685
|
+
}
|
|
686
|
+
throw error;
|
|
687
|
+
}
|
|
688
|
+
if (chatgptAutomationNavigationRequiresReplacement(error)) {
|
|
689
|
+
const replacementSessionId = await openReplacement(browserSessionId, targetUrl);
|
|
690
|
+
return { submissionTargetUrl: targetUrl, recoveredFromStaleBinding: false, browserSessionId: replacementSessionId };
|
|
691
|
+
}
|
|
692
|
+
if (!isChatgptConversationUrl(targetUrl)) throw error;
|
|
693
|
+
try {
|
|
694
|
+
await navigate(browserSessionId, 'https://chatgpt.com/');
|
|
695
|
+
return { submissionTargetUrl: 'https://chatgpt.com/', recoveredFromStaleBinding: true, browserSessionId };
|
|
696
|
+
} catch (fallbackError) {
|
|
697
|
+
if (!chatgptAutomationNavigationRequiresReplacement(fallbackError)) throw fallbackError;
|
|
698
|
+
const replacementSessionId = await openReplacement(browserSessionId, 'https://chatgpt.com/');
|
|
699
|
+
return { submissionTargetUrl: 'https://chatgpt.com/', recoveredFromStaleBinding: true, browserSessionId: replacementSessionId };
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
|
|
704
|
+
export async function submitChatgptPrompt(
|
|
705
|
+
controllerHome: string,
|
|
706
|
+
workId: string,
|
|
707
|
+
browserSessionId: string,
|
|
708
|
+
prompt: string,
|
|
709
|
+
targetUrl: string,
|
|
710
|
+
timeoutMs?: number,
|
|
711
|
+
): Promise<string> {
|
|
712
|
+
const renderedPrompt = withForgePluginMention(prompt);
|
|
713
|
+
const before = await latestChatgptUserMessage(controllerHome, workId, browserSessionId, timeoutMs)
|
|
714
|
+
.catch((): { selector?: string; preview: string; url?: string } => ({ selector: undefined, preview: '', url: targetUrl }));
|
|
715
|
+
await controllerBrowserAction(controllerHome, workId, 'fill', {
|
|
716
|
+
session_id: browserSessionId,
|
|
717
|
+
selector: CHATGPT_PROMPT_SELECTOR,
|
|
718
|
+
text: renderedPrompt,
|
|
719
|
+
timeout_ms: timeoutMs ?? 60_000,
|
|
720
|
+
post_action_wait_ms: 100,
|
|
721
|
+
}, timeoutMs);
|
|
722
|
+
|
|
723
|
+
let observedUrl = targetUrl;
|
|
724
|
+
let submitOutcomeUnknown = false;
|
|
725
|
+
let observedNewOutbound = false;
|
|
726
|
+
try {
|
|
727
|
+
const sent = await controllerBrowserAction(controllerHome, workId, 'click', {
|
|
728
|
+
session_id: browserSessionId,
|
|
729
|
+
selector: CHATGPT_SEND_SELECTOR,
|
|
730
|
+
timeout_ms: timeoutMs ?? 60_000,
|
|
731
|
+
post_action_wait_ms: 250,
|
|
732
|
+
}, timeoutMs);
|
|
733
|
+
observedUrl = resultUrl(sent) ?? observedUrl;
|
|
734
|
+
} catch (error) {
|
|
735
|
+
if (browserMutationOutcomeUnknown(error, 'click')) {
|
|
736
|
+
// Never replay an outcome-unknown submit: semantic observation below decides
|
|
737
|
+
// whether the original click committed, preventing duplicate user messages.
|
|
738
|
+
submitOutcomeUnknown = true;
|
|
739
|
+
} else if (chatgptSendControlUnavailable(error)) {
|
|
740
|
+
const pressed = await controllerBrowserAction(controllerHome, workId, 'press', {
|
|
741
|
+
session_id: browserSessionId,
|
|
742
|
+
selector: CHATGPT_PROMPT_SELECTOR,
|
|
743
|
+
key: 'Enter',
|
|
744
|
+
timeout_ms: timeoutMs ?? 60_000,
|
|
745
|
+
post_action_wait_ms: 250,
|
|
746
|
+
}, timeoutMs);
|
|
747
|
+
observedUrl = resultUrl(pressed) ?? observedUrl;
|
|
748
|
+
} else {
|
|
749
|
+
throw error;
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
|
|
753
|
+
const deadline = Date.now() + Math.min(Math.max(timeoutMs ?? 10_000, 3_000), 10_000);
|
|
754
|
+
do {
|
|
755
|
+
const latest = await latestChatgptUserMessage(controllerHome, workId, browserSessionId, timeoutMs).catch(() => undefined);
|
|
756
|
+
if (latest) {
|
|
757
|
+
observedUrl = latest.url ?? observedUrl;
|
|
758
|
+
const isNewOutbound = Boolean(
|
|
759
|
+
(latest.selector && latest.selector !== before.selector)
|
|
760
|
+
|| (!before.preview && latest.preview)
|
|
761
|
+
|| latest.preview !== before.preview,
|
|
762
|
+
);
|
|
763
|
+
if (isNewOutbound) {
|
|
764
|
+
observedNewOutbound = true;
|
|
765
|
+
const fullText = await fullChatgptMessageText(controllerHome, workId, browserSessionId, latest, timeoutMs);
|
|
766
|
+
if (chatgptOutboundMessageMatchesPrompt(fullText.text, renderedPrompt, { truncated: fullText.truncated }) && /\/c\/[^/?#]+/.test(observedUrl)) {
|
|
767
|
+
return observedUrl;
|
|
768
|
+
}
|
|
769
|
+
}
|
|
770
|
+
}
|
|
771
|
+
await new Promise((resolveWait) => setTimeout(resolveWait, 150));
|
|
772
|
+
} while (Date.now() < deadline);
|
|
773
|
+
const hasConversationIdentity = /\/c\/[^/?#]+/.test(observedUrl);
|
|
774
|
+
const failureCode = submitOutcomeUnknown || (observedNewOutbound && hasConversationIdentity)
|
|
775
|
+
? CHATGPT_AUTOMATION_SUBMISSION_OUTCOME_UNKNOWN
|
|
776
|
+
: 'CHATGPT_AUTOMATION_SUBMISSION_NOT_CONFIRMED';
|
|
777
|
+
throw new ChatgptProviderDeliveryError(
|
|
778
|
+
failureCode,
|
|
779
|
+
`${failureCode}:${observedUrl}`,
|
|
780
|
+
{ conversationUrl: observedUrl },
|
|
781
|
+
);
|
|
782
|
+
}
|
|
783
|
+
|