@moretea-labs/forge 1.6.0 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +2 -0
- package/CHANGELOG.md +26 -0
- package/CLAUDE.md +2 -0
- package/README.md +2 -2
- package/README.zh-CN.md +1 -1
- package/SKILL.md +2 -2
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/agents/openai.yaml +1 -1
- package/assets/hooks/hook-input.sh +11 -8
- package/assets/hooks/lib/session-state.sh +10 -7
- package/assets/hooks/lib/workflow-state.sh +13 -177
- package/assets/hooks/post-tool-observer.sh +8 -2
- package/assets/hooks/prompt-guard.sh +5 -81
- package/assets/hooks/session-start-context.sh +56 -1
- package/assets/partials/05-workflow.partial.md +1 -1
- package/assets/partials-agents/03-orchestration.partial.md +1 -1
- package/assets/plugin-registry.v1.json +1 -1
- package/assets/reference-configs/document-generation.md +1 -1
- package/assets/reference-configs/harness-overview.md +1 -1
- package/assets/reference-configs/sprint-contracts.md +3 -3
- package/assets/skill-version.json +2 -2
- package/assets/templates/contract.template.md +2 -7
- package/assets/templates/helpers/capture-plan.sh +2 -2
- package/assets/templates/helpers/check-task-workflow.sh +4 -4
- package/assets/templates/helpers/contract-worktree.sh +1 -21
- package/assets/templates/helpers/ensure-task-workflow.sh +11 -36
- package/assets/templates/helpers/migrate-project-template.sh +22 -45
- package/assets/templates/helpers/new-plan.sh +1 -1
- package/assets/templates/helpers/plan-to-todo.sh +12 -37
- package/assets/templates/helpers/ship-worktrees.sh +2 -29
- package/assets/templates/helpers/verify-contract.sh +1 -111
- package/assets/templates/helpers/verify-sprint.sh +3 -180
- package/assets/templates/plan.template.md +1 -1
- package/assets/templates/review.template.md +9 -34
- package/assets/workflow-contract.v1.json +10 -19
- package/docs/README.md +10 -10
- package/docs/architecture/CURRENT.md +170 -0
- package/docs/architecture/EVOLUTION.md +42 -0
- package/docs/architecture/current/README.md +4 -156
- package/docs/architecture/decisions/20260803-release-identity-binding-and-exit-policy.md +2 -2
- package/docs/architecture/decisions/20260823-immediate-controller-relay.md +53 -0
- package/docs/architecture/decisions/20260826-browser-runtime-v2.md +73 -0
- package/docs/architecture/history/README.md +7 -0
- package/docs/architecture/{chatgpt-handoff-facade.md → history/chatgpt-handoff-facade.md} +4 -0
- package/docs/architecture/{global-hook-runtime.md → history/global-hook-runtime.md} +7 -3
- package/docs/architecture/{ios-semantic-automation-provider-v2.md → history/ios-semantic-automation-provider-v2.md} +6 -2
- package/docs/architecture/history.md +3 -34
- package/docs/architecture/index.md +15 -98
- package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +1 -1
- package/docs/architecture/reviews/2026-08-22-semantic-harness-convergence-acceptance.md +39 -0
- package/docs/architecture/versions/1.6.md +38 -0
- package/docs/architecture/versions/1.7.md +41 -0
- package/docs/forge-chatgpt-browser-engine.md +2 -2
- package/docs/forge-chatgpt-mcp-setup.md +4 -28
- package/docs/forge-chatgpt-supervised-automation.md +3 -3
- package/docs/forge-github-issue-launcher.md +1 -1
- package/docs/forge-runtime-self-healing-loop.md +1 -1
- package/docs/operations/controller-browser-plugin.md +26 -6
- package/docs/operations/controller-performance-and-502.md +124 -0
- package/docs/operations/controller-reliability-runbook.md +1 -1
- package/docs/operations/features.md +2 -2
- package/docs/operations/github-repository.md +1 -1
- package/docs/operations/homebrew.md +2 -2
- package/docs/operations/mcp-tool-exposure.md +5 -3
- package/docs/wiki/Architecture.md +6 -6
- package/docs/wiki/Implementation.md +2 -2
- package/docs/wiki/Integrations.md +2 -2
- package/docs/wiki/Work-Lifecycle.md +1 -1
- package/package.json +29 -10
- package/scripts/activate-source-baseline.ts +17 -13
- package/scripts/benchmark-background-check-overlap.ts +228 -0
- package/scripts/benchmark-mcp-host.sh +117 -0
- package/scripts/benchmark-mcp-transport.ts +170 -0
- package/scripts/benchmark-quality-harness.ts +146 -0
- package/scripts/benchmark-route-session-concurrency.ts +207 -6
- package/scripts/benchmark-thin-harness-mcp-e2e.ts +7 -1
- package/scripts/benchmark-typescript-navigation.ts +186 -0
- package/scripts/benchmark-work-efficiency.ts +334 -0
- package/scripts/capture-plan.sh +3 -3
- package/scripts/check-agent-tooling.sh +8 -35
- package/scripts/check-architecture-sync.sh +17 -86
- package/scripts/check-mcp-compatibility.ts +14 -0
- package/scripts/check-runtime-architecture.mjs +107 -64
- package/scripts/check-stable-baseline.ts +92 -0
- package/scripts/check-task-workflow.sh +4 -4
- package/scripts/contract-worktree.sh +1 -21
- package/scripts/ensure-task-workflow.sh +22 -55
- package/scripts/host-temporary-cloud-mcp-e2e.sh +265 -0
- package/scripts/install-standalone-recovery.ts +24 -0
- package/scripts/lib/project-init-lib.sh +15 -44
- package/scripts/migrate-project-template.sh +23 -1179
- package/scripts/new-plan.sh +1 -1
- package/scripts/plan-to-todo.sh +11 -36
- package/scripts/run-governed-gate.ts +2 -1
- package/scripts/ship-worktrees.sh +2 -29
- package/scripts/smoke-ios-physical-device.ts +4 -2
- package/scripts/smoke-runtime-recovery.ts +1 -35
- package/scripts/smoke-schedule-engine.ts +1 -20
- package/scripts/verify-contract.sh +1 -111
- package/scripts/verify-forge-runtime.sh +0 -1
- package/scripts/verify-sprint.sh +3 -180
- package/scripts/workflow-contract.ts +1 -5
- package/src/cli/commands/init-hook.ts +3 -3
- package/src/cli/commands/mcp.ts +9 -5
- package/src/cli/commands/plugin.ts +1 -1
- package/src/cli/commands/recovery.ts +25 -3
- package/src/cli/commands/runtime.ts +55 -3
- package/src/cli/commands/security.ts +12 -1
- package/src/cli/commands/setup-profile.ts +31 -21
- package/src/cli/controller/check-runner.ts +106 -3
- package/src/cli/controller/composite-result.ts +1 -1
- package/src/cli/controller/context/focus.ts +51 -0
- package/src/cli/controller/context/instruction-context.ts +114 -0
- package/src/cli/controller/context/known-paths.ts +76 -0
- package/src/cli/controller/context/query-planning.ts +110 -0
- package/src/cli/controller/context/rule-resolution.ts +532 -0
- package/src/cli/controller/context/source-materializer.ts +278 -0
- package/src/cli/controller/context/structural.ts +16 -0
- package/src/cli/controller/context/types.ts +286 -0
- package/src/cli/controller/context-pack.ts +785 -469
- package/src/cli/controller/launch-agents.ts +77 -2
- package/src/cli/controller/operational-plan.ts +2 -2
- package/src/cli/controller/work-mode.ts +6 -4
- package/src/cli/editing/edit-session.ts +60 -7
- package/src/cli/github/github.ts +133 -0
- package/src/cli/installer/managed-entries.ts +15 -1
- package/src/cli/local-bridge/console-automations.ts +54 -78
- package/src/cli/local-bridge/console-view-models.ts +1 -35
- package/src/cli/local-bridge/facade-api.ts +0 -217
- package/src/cli/local-bridge/frontend/src/App.tsx +3 -3
- package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +23 -13
- package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +37 -4
- package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +6 -4
- package/src/cli/local-bridge/frontend/src/lib/api.ts +1 -6
- package/src/cli/local-bridge/frontend/src/lib/routes.ts +1 -1
- package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +2 -2
- package/src/cli/local-bridge/frontend/src/styles.css +1 -1
- package/src/cli/local-bridge/frontend/src/types.ts +5 -8
- package/src/cli/local-bridge/job-store.ts +1 -490
- package/src/cli/local-bridge/server.ts +0 -513
- package/src/cli/local-bridge/ui-dist/app.css +1 -1
- package/src/cli/local-bridge/ui-dist/app.js +3703 -3757
- package/src/cli/mcp/auth.ts +5 -5
- package/src/cli/mcp/instructions.ts +3 -2
- package/src/cli/mcp/legacy-tool-service.ts +0 -189
- package/src/cli/mcp/multi-repository.ts +16 -5
- package/src/cli/mcp/oauth.ts +175 -26
- package/src/cli/mcp/repository-tools.ts +247 -107
- package/src/cli/mcp/server.ts +537 -111
- package/src/cli/mcp/tool-capability-cache.ts +1 -1
- package/src/cli/mcp/toolset-selection.ts +1 -1
- package/src/cli/mcp/toolset.ts +42 -5
- package/src/cli/mcp/transports/http.ts +101 -14
- package/src/cli/mcp/transports/session-registry.ts +43 -5
- package/src/cli/mcp/types.ts +3 -3
- package/src/cli/repositories/command-classifier.ts +101 -19
- package/src/cli/repositories/command-executor.ts +55 -521
- package/src/cli/repositories/command-process.ts +108 -0
- package/src/cli/repositories/command-scope.ts +99 -41
- package/src/cli/repositories/controller-home.ts +7 -8
- package/src/cli/repositories/git-command-executor.ts +2 -0
- package/src/cli/repositories/locks.ts +53 -16
- package/src/cli/repositories/repository-snapshot.ts +121 -0
- package/src/cli/repositories/selected-path-actions.ts +17 -9
- package/src/cli/repositories/structured-git.ts +61 -3
- package/src/cli/repository/inspector.ts +553 -63
- package/src/cli/repository/session-cache.ts +58 -17
- package/src/cli/v81-entry.ts +1 -4
- package/src/effects/fs-transaction.ts +85 -17
- package/src/runtime/context/codegraph-read-provider.ts +233 -2
- package/src/runtime/context/codegraph-sidecar.cjs +74 -40
- package/src/runtime/context/generic-lsp-provider.ts +355 -0
- package/src/runtime/context/lsp-client.ts +283 -0
- package/src/runtime/context/semantic-navigation.ts +275 -0
- package/src/runtime/context/swift-navigation.ts +565 -0
- package/src/runtime/context/typescript-navigation.ts +226 -0
- package/src/runtime/control-plane/dispatch-priority.ts +28 -0
- package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +47 -8
- package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +86 -24
- package/src/runtime/control-plane/execution/session-store.ts +18 -0
- package/src/runtime/control-plane/execution/verification-evidence.ts +58 -3
- package/src/runtime/control-plane/execution/work-handle-store.ts +27 -2
- package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +258 -0
- package/src/runtime/control-plane/execution/work-remote-delivery.ts +215 -0
- package/src/runtime/control-plane/execution/work-task-receipt.ts +54 -11
- package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +143 -8
- package/src/runtime/control-plane/execution/work-validation-reconciler.ts +22 -14
- package/src/runtime/control-plane/execution/work-verification-snapshot.ts +85 -8
- package/src/runtime/control-plane/facade/capability-registry.ts +129 -25
- package/src/runtime/control-plane/facade/controller-round-relay.ts +991 -0
- package/src/runtime/control-plane/facade/controller-session-store.ts +132 -1
- package/src/runtime/control-plane/facade/goal-workloop-access.ts +2 -0
- package/src/runtime/control-plane/facade/goal-workloop.ts +686 -129
- package/src/runtime/control-plane/facade/index.ts +1 -0
- package/src/runtime/control-plane/facade/plan-contract-store.ts +185 -20
- package/src/runtime/control-plane/facade/semantic-admission.ts +205 -0
- package/src/runtime/control-plane/facade/suggested-actions.ts +5 -1
- package/src/runtime/control-plane/facade/types.ts +22 -5
- package/src/runtime/control-plane/facade/work-contract-store.ts +175 -73
- package/src/runtime/control-plane/generated-cache-retention.ts +271 -0
- package/src/runtime/control-plane/global-scheduler/config.ts +44 -0
- package/src/runtime/control-plane/global-scheduler/dispatch-capacity.ts +80 -0
- package/src/runtime/control-plane/global-scheduler/durable-admission.ts +46 -0
- package/src/runtime/control-plane/global-scheduler/maintenance.ts +145 -0
- package/src/runtime/control-plane/global-scheduler/projection-refresh.ts +84 -0
- package/src/runtime/control-plane/global-scheduler/reconciliation.ts +3 -3
- package/src/runtime/control-plane/global-scheduler/scheduler.ts +273 -542
- package/src/runtime/control-plane/global-scheduler/source-scan.ts +59 -0
- package/src/runtime/control-plane/global-scheduler/state.ts +88 -0
- package/src/runtime/control-plane/global-scheduler/worker-attachment.ts +57 -0
- package/src/runtime/control-plane/global-scheduler/worker-exit-decision.ts +38 -0
- package/src/runtime/control-plane/global-scheduler/worker-exit-reconciler.ts +175 -0
- package/src/runtime/control-plane/global-scheduler/worker-launch.ts +111 -0
- package/src/runtime/control-plane/global-scheduler/worker-lifecycle-store.ts +64 -0
- package/src/runtime/control-plane/global-scheduler/worker-lifecycle.ts +150 -0
- package/src/runtime/control-plane/global-scheduler/worker-process.ts +119 -0
- package/src/runtime/control-plane/global-scheduler/worker-stderr.ts +60 -0
- package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +50 -0
- package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +355 -116
- package/src/runtime/control-plane/launcher/launch-reservation-store.ts +40 -0
- package/src/runtime/control-plane/launcher/thin-launcher.ts +194 -10
- package/src/runtime/control-plane/persistence/controller-home-migration.ts +487 -0
- package/src/runtime/control-plane/persistence/sqlite-store.ts +33 -2
- package/src/runtime/control-plane/release-retention.ts +62 -2
- package/src/runtime/control-plane/routing/route-policy.ts +7 -16
- package/src/runtime/control-plane/runtime-status-client.ts +0 -2
- package/src/runtime/diagnostics/mcp-timing.ts +13 -0
- package/src/runtime/diagnostics/mcp-transport-benchmark.ts +129 -0
- package/src/runtime/diagnostics/performance.ts +24 -1
- package/src/runtime/evidence/event-ledger.ts +1 -1
- package/src/runtime/execution/jobs/store.ts +58 -4
- package/src/runtime/execution/jobs/types.ts +1 -1
- package/src/runtime/execution/managed-workspace.ts +203 -1
- package/src/runtime/execution/process-runtime/check-facade.ts +51 -4
- package/src/runtime/execution/process-runtime/check-result.ts +53 -0
- package/src/runtime/execution/process-runtime/check-scheduling.ts +114 -0
- package/src/runtime/execution/process-runtime/command-facade.ts +345 -40
- package/src/runtime/execution/process-runtime/gc.ts +38 -6
- package/src/runtime/execution/process-runtime/lightweight-managed.ts +1184 -0
- package/src/runtime/execution/process-runtime/persisted-check.ts +47 -4
- package/src/runtime/execution/process-runtime/resource-claims.ts +10 -6
- package/src/runtime/execution/process-runtime/runtime.ts +43 -3
- package/src/runtime/execution/process-runtime/store.ts +1 -0
- package/src/runtime/execution/process-runtime/types.ts +2 -0
- package/src/runtime/execution/thin-harness/async-process.ts +12 -2
- package/src/runtime/execution/thin-harness/search-worker.ts +54 -5
- package/src/runtime/execution/workers/executor.ts +0 -48
- package/src/runtime/gateway/mcp/execution-tools.ts +1192 -114
- package/src/runtime/gateway/mcp/process-tools.ts +83 -37
- package/src/runtime/gateway/mcp/router.ts +184 -82
- package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +546 -0
- package/src/runtime/gateway/mcp/runtime-tools.ts +2224 -1589
- package/src/runtime/plugins/app-store-connect-adapter.ts +123 -19
- package/src/runtime/plugins/browser-adapter.ts +2901 -241
- package/src/runtime/plugins/browser-automation-service.ts +10 -0
- package/src/runtime/plugins/browser-handoff.ts +92 -2
- package/src/runtime/plugins/browser-macos-bridge.ts +582 -49
- package/src/runtime/plugins/browser-node-bridge.ts +6 -34
- package/src/runtime/plugins/browser-provider-registry.ts +109 -0
- package/src/runtime/plugins/browser-runtime-contract.ts +152 -0
- package/src/runtime/plugins/browser-runtime.ts +183 -0
- package/src/runtime/plugins/browser-session-authority.ts +332 -0
- package/src/runtime/plugins/capability-authorization-grants.ts +354 -0
- package/src/runtime/plugins/desktop-operator-registration.ts +8 -2
- package/src/runtime/plugins/external-adapter.ts +324 -3
- package/src/runtime/plugins/external-unix-socket.ts +7 -1
- package/src/runtime/plugins/first-party-registry.ts +2 -0
- package/src/runtime/plugins/github-adapter.ts +125 -0
- package/src/runtime/plugins/ios/agent-device-provider.ts +1 -1
- package/src/runtime/plugins/ios/agent-device-typed-provider.ts +52 -9
- package/src/runtime/plugins/ios/remote-xpc-hid.ts +1171 -0
- package/src/runtime/plugins/ios-adapter.ts +106 -16
- package/src/runtime/plugins/ios-agent-device.ts +234 -64
- package/src/runtime/plugins/ios-physical-device.ts +1143 -392
- package/src/runtime/plugins/lightweight-action.ts +153 -0
- package/src/runtime/plugins/local-system-adapter.ts +123 -6
- package/src/runtime/plugins/macos-capability-broker.ts +55 -9
- package/src/runtime/plugins/plugin-action-sidecar.ts +59 -0
- package/src/runtime/plugins/plugin-management-adapter.ts +49 -8
- package/src/runtime/plugins/resend-adapter.ts +1 -1
- package/src/runtime/plugins/store.ts +269 -29
- package/src/runtime/plugins/types.ts +35 -2
- package/src/runtime/plugins/xiaohongshu-publish.ts +515 -0
- package/src/runtime/projections/git-status-sampler.ts +16 -16
- package/src/runtime/projections/materialized-view.ts +28 -10
- package/src/runtime/recovery/maintenance-executor.ts +393 -28
- package/src/runtime/root/mcp-transport.ts +180 -10
- package/src/runtime/root/package-connector-service.ts +186 -8
- package/src/runtime/root/package-runtime-release.ts +143 -17
- package/src/runtime/root/package-runtime-service.ts +430 -22
- package/src/runtime/root/release-manifest.ts +7 -2
- package/src/runtime/root/release-materialize.ts +19 -0
- package/src/runtime/root/release-store.ts +48 -11
- package/src/runtime/root/runtime.ts +23 -5
- package/src/runtime/root/service.ts +100 -29
- package/src/runtime/root/types.ts +2 -0
- package/src/runtime/safe-tooling/ios-development.ts +321 -63
- package/src/runtime/shared/trusted-executable.ts +62 -0
- package/src/runtime/shared/trusted-node-executable.ts +48 -0
- package/src/runtime/standalone-recovery/core.ts +700 -92
- package/src/runtime/standalone-recovery/entry.ts +99 -23
- package/src/runtime/standalone-recovery/installer.ts +19 -3
- package/src/runtime/standalone-recovery/service-labels.ts +2 -0
- package/src/runtime/standalone-recovery/watchdog-heartbeat.ts +160 -0
- package/src/runtime/workflow/schedules/browser-probe.ts +81 -3
- package/src/runtime/workflow/schedules/engine.ts +287 -151
- package/src/runtime/workflow/schedules/github-issue-watch.ts +158 -0
- package/src/runtime/workflow/schedules/settlement.ts +48 -4
- package/src/runtime/workflow/schedules/store.ts +131 -10
- package/src/runtime/workflow/schedules/types.ts +2 -4
- package/src/runtime/workflow/schedules/work-continuation.ts +26 -13
- package/src/testing/test-governance.ts +8 -40
- package/assets/reference-configs/heartbeat-triage.md +0 -57
- package/assets/templates/helpers/harness-trace-grade.sh +0 -150
- package/assets/templates/helpers/heartbeat-triage.sh +0 -361
- package/assets/templates/helpers/maintenance-triage.sh +0 -167
- package/docs/architecture/current/approved-target-architecture.zh-CN.md +0 -1501
- package/docs/architecture/current/architecture-invariants.md +0 -331
- package/docs/architecture/current/automation-and-schedule-engine.md +0 -459
- package/docs/architecture/current/codegraph-context-provider.md +0 -238
- package/docs/architecture/current/control-plane-authority-inventory.md +0 -94
- package/docs/architecture/current/controller-repository-decoupling.md +0 -148
- package/docs/architecture/current/dispatch-and-agent-strategy.md +0 -373
- package/docs/architecture/current/entity-model.md +0 -612
- package/docs/architecture/current/external-plugin-governance.md +0 -396
- package/docs/architecture/current/failure-recovery.md +0 -447
- package/docs/architecture/current/governance.md +0 -213
- package/docs/architecture/current/human-interaction-plane.md +0 -87
- package/docs/architecture/current/implementation-status.md +0 -128
- package/docs/architecture/current/job-and-run-lifecycle.md +0 -413
- package/docs/architecture/current/legacy-governance.md +0 -58
- package/docs/architecture/current/local-workspace-target-grants.md +0 -70
- package/docs/architecture/current/migration-roadmap.md +0 -105
- package/docs/architecture/current/multi-repository-execution.md +0 -371
- package/docs/architecture/current/operations-runbook.md +0 -45
- package/docs/architecture/current/peer-supercontroller-runtime.md +0 -32
- package/docs/architecture/current/personal-assistant-plugin-baseline.md +0 -206
- package/docs/architecture/current/repository-access-modes.md +0 -102
- package/docs/architecture/current/route-policy.md +0 -15
- package/docs/architecture/current/route-session-concurrency.md +0 -85
- package/docs/architecture/current/runtime-architecture-simplification.md +0 -214
- package/docs/architecture/current/runtime-directory-map.md +0 -49
- package/docs/architecture/current/runtime-health-and-resource-lifecycle.md +0 -85
- package/docs/architecture/current/scheduler-and-resource-claims.md +0 -422
- package/docs/architecture/current/session-aware-execution-and-authorization.md +0 -77
- package/docs/architecture/current/system-overview.md +0 -169
- package/docs/architecture/current/target-requirements-traceability.md +0 -202
- package/docs/architecture/current/thin-harness-governance-policy.md +0 -32
- package/docs/architecture/current/thin-harness-v1.md +0 -321
- package/docs/architecture/current/verification-and-release-gates.md +0 -391
- package/docs/forge-autonomous-goal-loop.md +0 -9
- package/docs/forge-provider-configuration.md +0 -7
- package/docs/operations/chatgpt-personal-assistant-runtime.md +0 -93
- package/docs/operations/gmail-assistant-routines.md +0 -186
- package/docs/operations/personal-assistant-operator-guide.md +0 -61
- package/docs/operations/provider-configuration.md +0 -31
- package/scripts/harness-trace-grade.sh +0 -150
- package/scripts/heartbeat-triage.sh +0 -361
- package/scripts/maintenance-triage.sh +0 -167
- package/src/cli/local-bridge/frontend/src/features/settings/SettingsView.tsx +0 -2
- package/src/cli/repositories/goal-registry.ts +0 -239
- package/src/runtime/assistant/action-proposals.ts +0 -279
- package/src/runtime/assistant/intent.ts +0 -395
- package/src/runtime/assistant/model-provider.ts +0 -460
- package/src/runtime/assistant/openapi.ts +0 -223
- package/src/runtime/assistant/policy.ts +0 -89
- package/src/runtime/assistant/readiness.ts +0 -258
- package/src/runtime/assistant/routine-runtime.ts +0 -477
- package/src/runtime/assistant/schedule-binding.ts +0 -171
- package/src/runtime/assistant/standing-grants.ts +0 -375
- package/src/runtime/assistant/store.ts +0 -243
- package/src/runtime/assistant/types.ts +0 -135
- package/src/runtime/control-plane/goal-loop/config-facade.ts +0 -700
- package/src/runtime/control-plane/goal-loop/config-store.ts +0 -570
- package/src/runtime/control-plane/goal-loop/config-types.ts +0 -213
- package/src/runtime/control-plane/goal-loop/executor-router.ts +0 -109
- package/src/runtime/control-plane/goal-loop/failure-taxonomy.ts +0 -149
- package/src/runtime/control-plane/goal-loop/goal-contract-store.ts +0 -304
- package/src/runtime/control-plane/goal-loop/goal-loop-engine.ts +0 -1001
- package/src/runtime/control-plane/goal-loop/handoff-packet.ts +0 -160
- package/src/runtime/control-plane/goal-loop/index.ts +0 -13
- package/src/runtime/control-plane/goal-loop/local-tools.ts +0 -290
- package/src/runtime/control-plane/goal-loop/policy-gates.ts +0 -203
- package/src/runtime/control-plane/goal-loop/provider-dispatch.ts +0 -93
- package/src/runtime/control-plane/goal-loop/provider-registry.ts +0 -545
- package/src/runtime/control-plane/goal-loop/types.ts +0 -378
- package/src/runtime/control-plane/startup-recovery.ts +0 -198
- package/src/runtime/gateway/mcp/resource-policy.ts +0 -186
- package/src/runtime/personal-assistant/gmail-triage-manager.ts +0 -231
- package/src/runtime/personal-assistant/reporting-runtime.ts +0 -344
- package/src/runtime/personal-assistant/triage-runtime.ts +0 -748
- package/src/runtime/workflow/findings/store.ts +0 -204
- package/src/runtime/workflow/findings/types.ts +0 -58
- package/src/runtime/workflow/portfolio/engine.ts +0 -20
- package/src/runtime/workflow/portfolio/store.ts +0 -185
- package/src/runtime/workflow/portfolio/types.ts +0 -29
|
@@ -1,10 +1,12 @@
|
|
|
1
1
|
import { createHash, randomUUID } from 'crypto';
|
|
2
|
-
import {
|
|
2
|
+
import { buildBrowserPluginManifest, executeBrowserPluginAction } from '../../plugins/browser-adapter';
|
|
3
3
|
import { controllerPluginRepository } from '../../plugins/store';
|
|
4
4
|
import { getWorkContract } from '../facade/work-contract-store';
|
|
5
5
|
import {
|
|
6
6
|
bindChatgptWorkConversation,
|
|
7
7
|
getChatgptWorkConversationBinding,
|
|
8
|
+
parseChatgptConversationIdentity,
|
|
9
|
+
rebindChatgptWorkConversation,
|
|
8
10
|
type ChatgptWorkConversationBinding,
|
|
9
11
|
} from './chatgpt-work-binding-store';
|
|
10
12
|
|
|
@@ -14,13 +16,17 @@ export const DEFAULT_CHATGPT_AUTOMATION_REASONING = 'high';
|
|
|
14
16
|
export const DEFAULT_CHATGPT_AUTOMATION_TAB_POLICY = 'auto';
|
|
15
17
|
export const DEFAULT_CHATGPT_AUTOMATION_PLUGIN_MENTION = '@forge';
|
|
16
18
|
const CHATGPT_PROMPT_SELECTOR = 'div#prompt-textarea[contenteditable="true"]';
|
|
17
|
-
const CHATGPT_SEND_SELECTOR = '[data-testid="send-button"]';
|
|
18
|
-
const
|
|
19
|
+
const CHATGPT_SEND_SELECTOR = '[data-testid="send-button"], button[aria-label*="Send"], button[data-testid*="send"]';
|
|
20
|
+
const CHATGPT_INTELLIGENCE_CONTROL_SELECTORS = [
|
|
21
|
+
'main button, main [role="button"]',
|
|
22
|
+
'button, [role="button"]',
|
|
23
|
+
] as const;
|
|
19
24
|
const CHATGPT_CAPABILITY_SLIDER_SELECTOR = '[role="slider"]';
|
|
20
25
|
const CHATGPT_CAPABILITY_MENUITEM_SELECTOR = '[role="menuitem"][aria-keyshortcuts~="ArrowLeft"][aria-keyshortcuts~="ArrowRight"]';
|
|
21
26
|
|
|
22
27
|
export type ChatgptAutomationReasoning = 'medium' | 'high' | 'xhigh';
|
|
23
28
|
export type ChatgptAutomationTabPolicy = 'auto' | 'reuse' | 'new';
|
|
29
|
+
export type ChatgptAutomationTabCleanupStatus = 'closed' | 'preserved_user_owned' | 'session_closed' | 'failed';
|
|
24
30
|
|
|
25
31
|
export interface WorkChatgptContinuationInput {
|
|
26
32
|
controllerHome: string;
|
|
@@ -37,33 +43,6 @@ export interface WorkChatgptContinuationInput {
|
|
|
37
43
|
timeoutMs?: number;
|
|
38
44
|
}
|
|
39
45
|
|
|
40
|
-
export interface ScheduledChatgptPromptInput {
|
|
41
|
-
controllerHome: string;
|
|
42
|
-
repoId: string;
|
|
43
|
-
automationId: string;
|
|
44
|
-
prompt: string;
|
|
45
|
-
title?: string;
|
|
46
|
-
browserSessionId?: string;
|
|
47
|
-
conversationUrl?: string;
|
|
48
|
-
model?: string;
|
|
49
|
-
reasoning?: ChatgptAutomationReasoning;
|
|
50
|
-
tabPolicy?: ChatgptAutomationTabPolicy;
|
|
51
|
-
timeoutMs?: number;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export interface ScheduledChatgptPromptResult {
|
|
55
|
-
status: 'dispatched' | 'failed';
|
|
56
|
-
provider: 'controller-browser';
|
|
57
|
-
browserSessionId: string;
|
|
58
|
-
conversationUrl?: string;
|
|
59
|
-
model: string;
|
|
60
|
-
reasoning: ChatgptAutomationReasoning;
|
|
61
|
-
tabPolicy: ChatgptAutomationTabPolicy;
|
|
62
|
-
executionPreferenceVerified: boolean;
|
|
63
|
-
reusedSession: boolean;
|
|
64
|
-
error?: { code: string; message: string };
|
|
65
|
-
}
|
|
66
|
-
|
|
67
46
|
export interface WorkChatgptContinuationResult {
|
|
68
47
|
status: 'dispatched' | 'failed';
|
|
69
48
|
provider: 'controller-browser';
|
|
@@ -76,15 +55,42 @@ export interface WorkChatgptContinuationResult {
|
|
|
76
55
|
reasoning: ChatgptAutomationReasoning;
|
|
77
56
|
tabPolicy: ChatgptAutomationTabPolicy;
|
|
78
57
|
executionPreferenceVerified: boolean;
|
|
58
|
+
tabCleanupStatus?: ChatgptAutomationTabCleanupStatus;
|
|
59
|
+
tabCleanupError?: { code: string; message: string };
|
|
79
60
|
error?: { code: string; message: string };
|
|
80
61
|
}
|
|
81
62
|
|
|
63
|
+
export interface StandaloneChatgptPromptInput {
|
|
64
|
+
controllerHome: string;
|
|
65
|
+
repoId: string;
|
|
66
|
+
scopeId: string;
|
|
67
|
+
prompt: string;
|
|
68
|
+
browserSessionId?: string;
|
|
69
|
+
conversationUrl?: string;
|
|
70
|
+
model?: string;
|
|
71
|
+
reasoning?: ChatgptAutomationReasoning;
|
|
72
|
+
tabPolicy?: ChatgptAutomationTabPolicy;
|
|
73
|
+
timeoutMs?: number;
|
|
74
|
+
}
|
|
75
|
+
|
|
82
76
|
const workflowToolAttributionInstruction = (workId: string): string => `Forge Workflow execution contract: first claim exact Work ${workId}. For every repository_command_execute and repository_safe_patch_apply call in this turn, pass work_id=${workId} explicitly. MCP transport sessions may change between tool calls; never omit this Work id.`;
|
|
83
77
|
|
|
84
78
|
function requestId(workId: string, actionId: string): string {
|
|
85
79
|
return `chatgpt-work:${workId}:${actionId}:${randomUUID()}`;
|
|
86
80
|
}
|
|
87
81
|
|
|
82
|
+
const CHATGPT_BROWSER_TRANSPORT_OVERRIDES = {
|
|
83
|
+
browser_mode: 'attach_preferred',
|
|
84
|
+
cdp_attach_fallback: 'fail_closed',
|
|
85
|
+
native_attach_mode: 'auto',
|
|
86
|
+
native_browser_candidates: ['chrome'],
|
|
87
|
+
} as const;
|
|
88
|
+
|
|
89
|
+
export function chatgptBrowserActionArgs(actionId: string, args: Record<string, unknown>): Record<string, unknown> {
|
|
90
|
+
if (actionId === 'configure' || actionId === 'list_sessions' || actionId === 'reconcile_sessions') return args;
|
|
91
|
+
return { ...args, ...CHATGPT_BROWSER_TRANSPORT_OVERRIDES };
|
|
92
|
+
}
|
|
93
|
+
|
|
88
94
|
async function controllerBrowserAction(
|
|
89
95
|
controllerHome: string,
|
|
90
96
|
workId: string,
|
|
@@ -102,7 +108,7 @@ async function controllerBrowserAction(
|
|
|
102
108
|
pluginId: 'browser',
|
|
103
109
|
actionId,
|
|
104
110
|
requestId: requestId(workId, actionId),
|
|
105
|
-
args,
|
|
111
|
+
args: chatgptBrowserActionArgs(actionId, args),
|
|
106
112
|
timeoutMs,
|
|
107
113
|
origin: { surface: 'schedule', actor: 'chatgpt-work-continuation' },
|
|
108
114
|
});
|
|
@@ -112,18 +118,12 @@ async function ensureControllerChatgptBrowser(controllerHome: string, workId: st
|
|
|
112
118
|
const repository = controllerPluginRepository(controllerHome);
|
|
113
119
|
const repoRoot = repository.canonicalRoot ?? repository.localRoot;
|
|
114
120
|
if (!repoRoot) throw new Error('CHATGPT_CONTROLLER_BROWSER_ROOT_UNAVAILABLE');
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
browser_channel: 'chromium',
|
|
122
|
-
cdp_attach_fallback: 'fail_closed',
|
|
123
|
-
native_attach_mode: 'auto',
|
|
124
|
-
native_browser_candidates: ['chrome'],
|
|
125
|
-
} : {}),
|
|
126
|
-
});
|
|
121
|
+
// Browser configure is not a read: it persists configuration and closes managed
|
|
122
|
+
// contexts. Scheduled continuations must not disturb an already-enabled provider
|
|
123
|
+
// merely to prove it is available. Only enable it when the persisted authority is
|
|
124
|
+
// explicitly disabled; action-level transport overrides still fail closed later.
|
|
125
|
+
if (buildBrowserPluginManifest(0, undefined, repoRoot).enabled) return;
|
|
126
|
+
await controllerBrowserAction(controllerHome, workId, 'configure', { enabled: true });
|
|
127
127
|
}
|
|
128
128
|
|
|
129
129
|
function stringField(value: unknown): string | undefined {
|
|
@@ -149,9 +149,16 @@ export function stableChatgptWorkBrowserSessionId(repoId: string, workId: string
|
|
|
149
149
|
return `forge-chatgpt-work-${digest}`;
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
-
export function
|
|
153
|
-
const digest = createHash('sha256').update(`${repoId}\
|
|
154
|
-
return `forge-chatgpt-
|
|
152
|
+
export function stableStandaloneChatgptBrowserSessionId(repoId: string, scopeId: string): string {
|
|
153
|
+
const digest = createHash('sha256').update(`${repoId}\nstandalone\n${scopeId}`).digest('hex').slice(0, 20);
|
|
154
|
+
return `forge-chatgpt-standalone-${digest}`;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function resolveStandaloneChatgptBrowserSessionId(input: StandaloneChatgptPromptInput): string {
|
|
158
|
+
const policy = normalizeTabPolicy(input.tabPolicy);
|
|
159
|
+
const stable = stableStandaloneChatgptBrowserSessionId(input.repoId, input.scopeId);
|
|
160
|
+
if (policy === 'new') return `${stable}-${randomUUID().slice(0, 8)}`;
|
|
161
|
+
return input.browserSessionId?.trim() || stable;
|
|
155
162
|
}
|
|
156
163
|
|
|
157
164
|
export function resolveChatgptWorkBrowserSessionId(input: {
|
|
@@ -189,10 +196,45 @@ function matchSelector(value: BrowserQueryMatch | undefined): string | undefined
|
|
|
189
196
|
return typeof value?.selectorHint === 'string' && value.selectorHint.trim() ? value.selectorHint.trim() : undefined;
|
|
190
197
|
}
|
|
191
198
|
|
|
199
|
+
function normalizeExecutionControlLabel(label: string | undefined): string {
|
|
200
|
+
return (label ?? '').trim().toLowerCase().replace(/[\s_-]+/g, '');
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
export function chatgptAutomationReasoningLevelFromLabel(label: string | undefined): ChatgptAutomationReasoning | undefined {
|
|
204
|
+
const normalized = normalizeExecutionControlLabel(label);
|
|
205
|
+
if (!normalized) return undefined;
|
|
206
|
+
|
|
207
|
+
const exact = new Map<string, ChatgptAutomationReasoning>([
|
|
208
|
+
['medium', 'medium'], ['中', 'medium'], ['中等', 'medium'],
|
|
209
|
+
['high', 'high'], ['高', 'high'],
|
|
210
|
+
['xhigh', 'xhigh'], ['extrahigh', 'xhigh'], ['超高', 'xhigh'], ['极高', 'xhigh'],
|
|
211
|
+
]);
|
|
212
|
+
const exactLevel = exact.get(normalized);
|
|
213
|
+
if (exactLevel) return exactLevel;
|
|
214
|
+
|
|
215
|
+
const hasReasoningContext = ['reasoning', 'thinking', '推理', '思考']
|
|
216
|
+
.some((token) => normalized.includes(token));
|
|
217
|
+
if (!hasReasoningContext) return undefined;
|
|
218
|
+
if (['xhigh', 'extrahigh', '超高', '极高'].some((token) => normalized.includes(token))) return 'xhigh';
|
|
219
|
+
if (['medium', '中等'].some((token) => normalized.includes(token))) return 'medium';
|
|
220
|
+
if (['high', '高'].some((token) => normalized.includes(token))) return 'high';
|
|
221
|
+
return undefined;
|
|
222
|
+
}
|
|
223
|
+
|
|
192
224
|
function modelLabelMatches(label: string | undefined, model: string): boolean {
|
|
193
225
|
if (model !== DEFAULT_CHATGPT_AUTOMATION_MODEL || !label) return false;
|
|
194
|
-
const normalized = label
|
|
195
|
-
return normalized.includes('5.6sol') || normalized.includes('
|
|
226
|
+
const normalized = normalizeExecutionControlLabel(label);
|
|
227
|
+
return normalized.includes('5.6sol') || normalized.includes('gpt5.6sol');
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function reasoningLabelMatches(label: string | undefined, reasoning: ChatgptAutomationReasoning): boolean {
|
|
231
|
+
return chatgptAutomationReasoningLevelFromLabel(label) === reasoning;
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function isReasoningControlLabel(label: string | undefined): boolean {
|
|
235
|
+
return reasoningLabelMatches(label, 'medium')
|
|
236
|
+
|| reasoningLabelMatches(label, 'high')
|
|
237
|
+
|| reasoningLabelMatches(label, 'xhigh');
|
|
196
238
|
}
|
|
197
239
|
|
|
198
240
|
function reasoningSliderValue(reasoning: ChatgptAutomationReasoning): number {
|
|
@@ -207,13 +249,31 @@ async function findChatgptIntelligenceControl(
|
|
|
207
249
|
browserSessionId: string,
|
|
208
250
|
timeoutMs?: number,
|
|
209
251
|
): Promise<BrowserQueryMatch | undefined> {
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
252
|
+
for (const selector of CHATGPT_INTELLIGENCE_CONTROL_SELECTORS) {
|
|
253
|
+
const result = await controllerBrowserAction(controllerHome, workId, 'query_all', {
|
|
254
|
+
session_id: browserSessionId,
|
|
255
|
+
selector,
|
|
256
|
+
// Prefer the composer/main region so sidebar history cannot crowd the
|
|
257
|
+
// reasoning control out of a bounded query. Keep a larger global fallback
|
|
258
|
+
// for ChatGPT layouts that place the control outside <main>.
|
|
259
|
+
limit: selector.startsWith('main ') ? 80 : 240,
|
|
260
|
+
timeout_ms: timeoutMs ?? 60_000,
|
|
261
|
+
}, timeoutMs);
|
|
262
|
+
const match = queryMatches(result).find((candidate) => {
|
|
263
|
+
const label = matchText(candidate);
|
|
264
|
+
return modelLabelMatches(label, DEFAULT_CHATGPT_AUTOMATION_MODEL) || isReasoningControlLabel(label);
|
|
265
|
+
});
|
|
266
|
+
if (match) return match;
|
|
267
|
+
}
|
|
268
|
+
return undefined;
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export function chatgptAutomationControlWaitBudgets(timeoutMs?: number): { waitBudgetMs: number; probeTimeoutMs: number } {
|
|
272
|
+
const waitBudgetMs = Math.min(Math.max(timeoutMs ?? 30_000, 1_000), 30_000);
|
|
273
|
+
// One probe checks both the preferred <main> scope and the global fallback.
|
|
274
|
+
// Keep each selector query short enough that one slow pre-hydration pass cannot
|
|
275
|
+
// consume the whole readiness window before React exposes the reasoning control.
|
|
276
|
+
return { waitBudgetMs, probeTimeoutMs: Math.min(waitBudgetMs, 2_500) };
|
|
217
277
|
}
|
|
218
278
|
|
|
219
279
|
async function waitForChatgptIntelligenceControl(
|
|
@@ -222,10 +282,10 @@ async function waitForChatgptIntelligenceControl(
|
|
|
222
282
|
browserSessionId: string,
|
|
223
283
|
timeoutMs?: number,
|
|
224
284
|
): Promise<BrowserQueryMatch | undefined> {
|
|
225
|
-
const waitBudgetMs =
|
|
285
|
+
const { waitBudgetMs, probeTimeoutMs } = chatgptAutomationControlWaitBudgets(timeoutMs);
|
|
226
286
|
const deadline = Date.now() + waitBudgetMs;
|
|
227
287
|
do {
|
|
228
|
-
const control = await findChatgptIntelligenceControl(controllerHome, workId, browserSessionId,
|
|
288
|
+
const control = await findChatgptIntelligenceControl(controllerHome, workId, browserSessionId, probeTimeoutMs);
|
|
229
289
|
if (control) return control;
|
|
230
290
|
if (Date.now() >= deadline) break;
|
|
231
291
|
await new Promise((resolveWait) => setTimeout(resolveWait, 500));
|
|
@@ -233,6 +293,46 @@ async function waitForChatgptIntelligenceControl(
|
|
|
233
293
|
return undefined;
|
|
234
294
|
}
|
|
235
295
|
|
|
296
|
+
export function chatgptAutomationPageFailure(
|
|
297
|
+
bodyText: string | undefined,
|
|
298
|
+
composerAvailable: boolean,
|
|
299
|
+
): 'CHATGPT_AUTOMATION_LOGIN_REQUIRED' | 'CHATGPT_AUTOMATION_COMPOSER_UNAVAILABLE' | undefined {
|
|
300
|
+
if (composerAvailable) return undefined;
|
|
301
|
+
const normalized = (bodyText ?? '').toLowerCase();
|
|
302
|
+
const loginMarkers = [
|
|
303
|
+
'log in', 'sign up', 'continue with google', 'continue with apple',
|
|
304
|
+
'登录', '登陆', '注册', '使用 google 继续', '使用 apple 继续',
|
|
305
|
+
];
|
|
306
|
+
return loginMarkers.some((marker) => normalized.includes(marker))
|
|
307
|
+
? 'CHATGPT_AUTOMATION_LOGIN_REQUIRED'
|
|
308
|
+
: 'CHATGPT_AUTOMATION_COMPOSER_UNAVAILABLE';
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
async function assertChatgptAutomationComposerReady(
|
|
312
|
+
controllerHome: string,
|
|
313
|
+
workId: string,
|
|
314
|
+
browserSessionId: string,
|
|
315
|
+
timeoutMs?: number,
|
|
316
|
+
): Promise<void> {
|
|
317
|
+
const composerResult = await controllerBrowserAction(controllerHome, workId, 'query_all', {
|
|
318
|
+
session_id: browserSessionId,
|
|
319
|
+
selector: CHATGPT_PROMPT_SELECTOR,
|
|
320
|
+
limit: 1,
|
|
321
|
+
timeout_ms: Math.min(timeoutMs ?? 5_000, 5_000),
|
|
322
|
+
}, timeoutMs).catch(() => undefined);
|
|
323
|
+
const composerAvailable = queryMatches(composerResult).length > 0;
|
|
324
|
+
if (composerAvailable) return;
|
|
325
|
+
|
|
326
|
+
const bodyResult = await controllerBrowserAction(controllerHome, workId, 'get_text', {
|
|
327
|
+
session_id: browserSessionId,
|
|
328
|
+
selector: 'body',
|
|
329
|
+
max_chars: 4_000,
|
|
330
|
+
timeout_ms: Math.min(timeoutMs ?? 5_000, 5_000),
|
|
331
|
+
}, timeoutMs).catch(() => undefined);
|
|
332
|
+
const failure = chatgptAutomationPageFailure(stringField(bodyResult?.text), composerAvailable);
|
|
333
|
+
throw new Error(failure ?? 'CHATGPT_AUTOMATION_COMPOSER_UNAVAILABLE');
|
|
334
|
+
}
|
|
335
|
+
|
|
236
336
|
async function ensureChatgptExecutionPreference(
|
|
237
337
|
controllerHome: string,
|
|
238
338
|
workId: string,
|
|
@@ -243,8 +343,15 @@ async function ensureChatgptExecutionPreference(
|
|
|
243
343
|
): Promise<boolean> {
|
|
244
344
|
if (model !== DEFAULT_CHATGPT_AUTOMATION_MODEL) throw new Error(`CHATGPT_AUTOMATION_MODEL_UNSUPPORTED:${model}`);
|
|
245
345
|
let control = await waitForChatgptIntelligenceControl(controllerHome, workId, browserSessionId, timeoutMs);
|
|
246
|
-
if (!control)
|
|
247
|
-
|
|
346
|
+
if (!control) {
|
|
347
|
+
await assertChatgptAutomationComposerReady(controllerHome, workId, browserSessionId, timeoutMs);
|
|
348
|
+
throw new Error('CHATGPT_AUTOMATION_INTELLIGENCE_CONTROL_UNAVAILABLE');
|
|
349
|
+
}
|
|
350
|
+
// Current ChatGPT UI exposes the reasoning level (for example `高`) on the
|
|
351
|
+
// composer control instead of the model label. The model remains fail-closed
|
|
352
|
+
// through normalizeModel above; only the user-adjustable reasoning control is
|
|
353
|
+
// required to be observable in the page before unattended dispatch proceeds.
|
|
354
|
+
if (reasoningLabelMatches(matchText(control), reasoning)) return true;
|
|
248
355
|
let controlSelector = matchSelector(control);
|
|
249
356
|
if (!controlSelector) throw new Error('CHATGPT_AUTOMATION_INTELLIGENCE_CONTROL_UNAVAILABLE');
|
|
250
357
|
|
|
@@ -296,7 +403,9 @@ async function ensureChatgptExecutionPreference(
|
|
|
296
403
|
throw new Error(`CHATGPT_AUTOMATION_REASONING_NOT_VERIFIED:${reasoning}`);
|
|
297
404
|
}
|
|
298
405
|
control = await findChatgptIntelligenceControl(controllerHome, workId, browserSessionId, timeoutMs) ?? control;
|
|
299
|
-
if (!
|
|
406
|
+
if (isReasoningControlLabel(matchText(control)) && !reasoningLabelMatches(matchText(control), reasoning)) {
|
|
407
|
+
throw new Error(`CHATGPT_AUTOMATION_REASONING_NOT_VERIFIED:${reasoning}`);
|
|
408
|
+
}
|
|
300
409
|
controlSelector = matchSelector(control) ?? controlSelector;
|
|
301
410
|
await controllerBrowserAction(controllerHome, workId, 'press', {
|
|
302
411
|
session_id: browserSessionId,
|
|
@@ -313,6 +422,121 @@ function resultUrl(result: Record<string, unknown>): string | undefined {
|
|
|
313
422
|
?? stringField((result.session as Record<string, unknown> | undefined)?.url);
|
|
314
423
|
}
|
|
315
424
|
|
|
425
|
+
function resultSessionId(result: Record<string, unknown>): string | undefined {
|
|
426
|
+
return stringField(result.session_id)
|
|
427
|
+
?? stringField((result.session as Record<string, unknown> | undefined)?.sessionId);
|
|
428
|
+
}
|
|
429
|
+
|
|
430
|
+
export function chatgptAutomationNavigationRequiresReplacement(error: unknown): boolean {
|
|
431
|
+
if (!(error instanceof Error)) return false;
|
|
432
|
+
return [
|
|
433
|
+
'BROWSER_AUTOMATION_BACKGROUND_NAVIGATION_REQUIRES_REPLACEMENT',
|
|
434
|
+
'PLUGIN_BROWSER_SESSION_STATE_LOST',
|
|
435
|
+
'PLUGIN_SESSION_NOT_FOUND',
|
|
436
|
+
].some((marker) => error.message.includes(marker));
|
|
437
|
+
}
|
|
438
|
+
|
|
439
|
+
async function closeChatgptAutomationTabAfterDispatch(
|
|
440
|
+
controllerHome: string,
|
|
441
|
+
workId: string,
|
|
442
|
+
browserSessionId: string,
|
|
443
|
+
timeoutMs?: number,
|
|
444
|
+
): Promise<{ status: ChatgptAutomationTabCleanupStatus; error?: { code: string; message: string } }> {
|
|
445
|
+
try {
|
|
446
|
+
const closed = await controllerBrowserAction(controllerHome, workId, 'close_page', {
|
|
447
|
+
session_id: browserSessionId,
|
|
448
|
+
}, Math.min(timeoutMs ?? 15_000, 15_000));
|
|
449
|
+
if (closed.preservedUserOwnedTab === true) return { status: 'preserved_user_owned' };
|
|
450
|
+
if (closed.resourceClosed === true) return { status: 'closed' };
|
|
451
|
+
return { status: 'session_closed' };
|
|
452
|
+
} catch (error) {
|
|
453
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
454
|
+
return {
|
|
455
|
+
status: 'failed',
|
|
456
|
+
error: {
|
|
457
|
+
code: error instanceof Error && error.message.includes(':') ? error.message.split(':', 1)[0] : 'CHATGPT_AUTOMATION_TAB_CLEANUP_FAILED',
|
|
458
|
+
message,
|
|
459
|
+
},
|
|
460
|
+
};
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
/**
|
|
465
|
+
* Settles the browser resource for one completed Work-bound ChatGPT round.
|
|
466
|
+
* Conversation identity is durable in the Work binding and intentionally
|
|
467
|
+
* survives this ephemeral tab/session cleanup. Browser ownership policy keeps
|
|
468
|
+
* user-owned native tabs intact.
|
|
469
|
+
*/
|
|
470
|
+
export async function settleWorkChatgptAutomationTab(input: {
|
|
471
|
+
controllerHome: string;
|
|
472
|
+
workId: string;
|
|
473
|
+
browserSessionId: string;
|
|
474
|
+
timeoutMs?: number;
|
|
475
|
+
}): Promise<{ status: ChatgptAutomationTabCleanupStatus; error?: { code: string; message: string } }> {
|
|
476
|
+
return closeChatgptAutomationTabAfterDispatch(
|
|
477
|
+
input.controllerHome,
|
|
478
|
+
input.workId,
|
|
479
|
+
input.browserSessionId,
|
|
480
|
+
input.timeoutMs,
|
|
481
|
+
);
|
|
482
|
+
}
|
|
483
|
+
|
|
484
|
+
export function isChatgptConversationUrl(value: string): boolean {
|
|
485
|
+
try {
|
|
486
|
+
const url = new URL(value);
|
|
487
|
+
return url.protocol === 'https:'
|
|
488
|
+
&& ['chatgpt.com', 'www.chatgpt.com', 'chat.openai.com'].includes(url.hostname)
|
|
489
|
+
&& /\/c\/[^/?#]+/.test(url.pathname);
|
|
490
|
+
} catch {
|
|
491
|
+
return false;
|
|
492
|
+
}
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
async function navigateWorkConversation(
|
|
496
|
+
controllerHome: string,
|
|
497
|
+
workId: string,
|
|
498
|
+
browserSessionId: string,
|
|
499
|
+
targetUrl: string,
|
|
500
|
+
timeoutMs?: number,
|
|
501
|
+
): Promise<{ submissionTargetUrl: string; recoveredFromStaleBinding: boolean; browserSessionId: string }> {
|
|
502
|
+
const navigate = async (sessionId: string, url: string) => controllerBrowserAction(controllerHome, workId, 'navigate', {
|
|
503
|
+
session_id: sessionId,
|
|
504
|
+
url,
|
|
505
|
+
wait_until: 'domcontentloaded',
|
|
506
|
+
timeout_ms: timeoutMs ?? 60_000,
|
|
507
|
+
retries: 1,
|
|
508
|
+
}, timeoutMs);
|
|
509
|
+
const openReplacement = async (url: string): Promise<string> => {
|
|
510
|
+
const opened = await controllerBrowserAction(controllerHome, workId, 'open_page', {
|
|
511
|
+
url,
|
|
512
|
+
wait_until: 'domcontentloaded',
|
|
513
|
+
timeout_ms: timeoutMs ?? 60_000,
|
|
514
|
+
retries: 1,
|
|
515
|
+
}, timeoutMs);
|
|
516
|
+
const replacementSessionId = resultSessionId(opened);
|
|
517
|
+
if (!replacementSessionId) throw new Error('CHATGPT_AUTOMATION_REPLACEMENT_SESSION_NOT_CONFIRMED');
|
|
518
|
+
return replacementSessionId;
|
|
519
|
+
};
|
|
520
|
+
try {
|
|
521
|
+
await navigate(browserSessionId, targetUrl);
|
|
522
|
+
return { submissionTargetUrl: targetUrl, recoveredFromStaleBinding: false, browserSessionId };
|
|
523
|
+
} catch (error) {
|
|
524
|
+
if (chatgptAutomationNavigationRequiresReplacement(error)) {
|
|
525
|
+
const replacementSessionId = await openReplacement(targetUrl);
|
|
526
|
+
return { submissionTargetUrl: targetUrl, recoveredFromStaleBinding: false, browserSessionId: replacementSessionId };
|
|
527
|
+
}
|
|
528
|
+
if (!isChatgptConversationUrl(targetUrl)) throw error;
|
|
529
|
+
try {
|
|
530
|
+
await navigate(browserSessionId, 'https://chatgpt.com/');
|
|
531
|
+
return { submissionTargetUrl: 'https://chatgpt.com/', recoveredFromStaleBinding: true, browserSessionId };
|
|
532
|
+
} catch (fallbackError) {
|
|
533
|
+
if (!chatgptAutomationNavigationRequiresReplacement(fallbackError)) throw fallbackError;
|
|
534
|
+
const replacementSessionId = await openReplacement('https://chatgpt.com/');
|
|
535
|
+
return { submissionTargetUrl: 'https://chatgpt.com/', recoveredFromStaleBinding: true, browserSessionId: replacementSessionId };
|
|
536
|
+
}
|
|
537
|
+
}
|
|
538
|
+
}
|
|
539
|
+
|
|
316
540
|
async function submitChatgptPrompt(
|
|
317
541
|
controllerHome: string,
|
|
318
542
|
workId: string,
|
|
@@ -358,75 +582,77 @@ export function withForgePluginMention(prompt: string): string {
|
|
|
358
582
|
return `${DEFAULT_CHATGPT_AUTOMATION_PLUGIN_MENTION} ${value}`;
|
|
359
583
|
}
|
|
360
584
|
|
|
361
|
-
|
|
585
|
+
/**
|
|
586
|
+
* Dispatches a bounded standalone prompt through the same controller-owned
|
|
587
|
+
* ChatGPT browser path without creating or requiring a WorkContract. The scope
|
|
588
|
+
* id is only a stable browser correlation key (for example a Schedule id).
|
|
589
|
+
*/
|
|
590
|
+
export async function runStandaloneChatgptPrompt(input: StandaloneChatgptPromptInput): Promise<WorkChatgptContinuationResult> {
|
|
362
591
|
const model = normalizeModel(input.model);
|
|
363
592
|
const reasoning = normalizeReasoning(input.reasoning);
|
|
364
593
|
const tabPolicy = normalizeTabPolicy(input.tabPolicy);
|
|
365
|
-
const
|
|
366
|
-
const
|
|
367
|
-
const
|
|
368
|
-
|
|
369
|
-
: explicitSessionId || stableSessionId;
|
|
370
|
-
const targetUrl = tabPolicy === 'new'
|
|
371
|
-
? 'https://chatgpt.com/'
|
|
372
|
-
: input.conversationUrl?.trim() || 'https://chatgpt.com/';
|
|
373
|
-
let reusedSession = false;
|
|
594
|
+
const sessionId = resolveStandaloneChatgptBrowserSessionId(input);
|
|
595
|
+
const browserScopeId = `standalone:${input.scopeId}`;
|
|
596
|
+
const seedUrl = input.conversationUrl?.trim();
|
|
597
|
+
|
|
374
598
|
try {
|
|
375
|
-
await ensureControllerChatgptBrowser(input.controllerHome,
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
await controllerBrowserAction(input.controllerHome, input.automationId, 'get_text', {
|
|
379
|
-
session_id: sessionId,
|
|
380
|
-
selector: 'body',
|
|
381
|
-
max_chars: 1,
|
|
382
|
-
timeout_ms: Math.min(input.timeoutMs ?? 60_000, 3_000),
|
|
383
|
-
}, input.timeoutMs);
|
|
384
|
-
reusedSession = true;
|
|
385
|
-
} catch {
|
|
386
|
-
reusedSession = false;
|
|
387
|
-
}
|
|
388
|
-
}
|
|
389
|
-
if (!reusedSession) {
|
|
390
|
-
await controllerBrowserAction(input.controllerHome, input.automationId, 'open_page', {
|
|
391
|
-
session_id: sessionId,
|
|
392
|
-
url: targetUrl,
|
|
393
|
-
wait_until: 'domcontentloaded',
|
|
394
|
-
timeout_ms: input.timeoutMs ?? 60_000,
|
|
395
|
-
retries: 1,
|
|
396
|
-
}, input.timeoutMs);
|
|
397
|
-
}
|
|
398
|
-
const executionPreferenceVerified = await ensureChatgptExecutionPreference(
|
|
599
|
+
await ensureControllerChatgptBrowser(input.controllerHome, browserScopeId);
|
|
600
|
+
const targetUrl = seedUrl ?? 'https://chatgpt.com/';
|
|
601
|
+
const navigation = await navigateWorkConversation(
|
|
399
602
|
input.controllerHome,
|
|
400
|
-
|
|
603
|
+
browserScopeId,
|
|
401
604
|
sessionId,
|
|
605
|
+
targetUrl,
|
|
606
|
+
input.timeoutMs,
|
|
607
|
+
);
|
|
608
|
+
const executionPreferenceVerified = await ensureChatgptExecutionPreference(
|
|
609
|
+
input.controllerHome,
|
|
610
|
+
browserScopeId,
|
|
611
|
+
navigation.browserSessionId,
|
|
402
612
|
model,
|
|
403
613
|
reasoning,
|
|
404
614
|
input.timeoutMs,
|
|
405
615
|
);
|
|
406
|
-
const observedUrl = await submitChatgptPrompt(
|
|
616
|
+
const observedUrl = await submitChatgptPrompt(
|
|
617
|
+
input.controllerHome,
|
|
618
|
+
browserScopeId,
|
|
619
|
+
navigation.browserSessionId,
|
|
620
|
+
input.prompt,
|
|
621
|
+
navigation.submissionTargetUrl,
|
|
622
|
+
input.timeoutMs,
|
|
623
|
+
);
|
|
624
|
+
const tabCleanup = await closeChatgptAutomationTabAfterDispatch(
|
|
625
|
+
input.controllerHome,
|
|
626
|
+
browserScopeId,
|
|
627
|
+
navigation.browserSessionId,
|
|
628
|
+
input.timeoutMs,
|
|
629
|
+
);
|
|
407
630
|
return {
|
|
408
631
|
status: 'dispatched',
|
|
409
632
|
provider: 'controller-browser',
|
|
410
|
-
browserSessionId:
|
|
633
|
+
browserSessionId: navigation.browserSessionId,
|
|
411
634
|
conversationUrl: observedUrl,
|
|
635
|
+
resumedFromBinding: false,
|
|
412
636
|
model,
|
|
413
637
|
reasoning,
|
|
414
638
|
tabPolicy,
|
|
415
639
|
executionPreferenceVerified,
|
|
416
|
-
|
|
640
|
+
tabCleanupStatus: tabCleanup.status,
|
|
641
|
+
...(tabCleanup.error ? { tabCleanupError: tabCleanup.error } : {}),
|
|
417
642
|
};
|
|
418
643
|
} catch (error) {
|
|
419
644
|
return {
|
|
420
645
|
status: 'failed',
|
|
421
646
|
provider: 'controller-browser',
|
|
422
647
|
browserSessionId: sessionId,
|
|
648
|
+
conversationUrl: seedUrl,
|
|
649
|
+
resumedFromBinding: false,
|
|
423
650
|
model,
|
|
424
651
|
reasoning,
|
|
425
652
|
tabPolicy,
|
|
426
653
|
executionPreferenceVerified: false,
|
|
427
|
-
reusedSession,
|
|
428
654
|
error: {
|
|
429
|
-
code: error instanceof Error && error.message.includes(':') ? error.message.split(':', 1)[0] : '
|
|
655
|
+
code: error instanceof Error && error.message.includes(':') ? error.message.split(':', 1)[0] : 'CHATGPT_CONTROLLER_BROWSER_FAILED',
|
|
430
656
|
message: error instanceof Error ? error.message : String(error),
|
|
431
657
|
},
|
|
432
658
|
};
|
|
@@ -468,34 +694,47 @@ export async function runWorkChatgptContinuation(input: WorkChatgptContinuationI
|
|
|
468
694
|
});
|
|
469
695
|
}
|
|
470
696
|
const targetUrl = binding?.conversationUrl ?? seedUrl ?? 'https://chatgpt.com/';
|
|
471
|
-
|
|
472
|
-
session_id: sessionId,
|
|
473
|
-
url: targetUrl,
|
|
474
|
-
wait_until: 'domcontentloaded',
|
|
475
|
-
timeout_ms: input.timeoutMs ?? 60_000,
|
|
476
|
-
retries: 1,
|
|
477
|
-
}, input.timeoutMs);
|
|
478
|
-
const executionPreferenceVerified = await ensureChatgptExecutionPreference(
|
|
697
|
+
const navigation = await navigateWorkConversation(
|
|
479
698
|
input.controllerHome,
|
|
480
699
|
input.workId,
|
|
481
700
|
sessionId,
|
|
701
|
+
targetUrl,
|
|
702
|
+
input.timeoutMs,
|
|
703
|
+
);
|
|
704
|
+
const executionPreferenceVerified = await ensureChatgptExecutionPreference(
|
|
705
|
+
input.controllerHome,
|
|
706
|
+
input.workId,
|
|
707
|
+
navigation.browserSessionId,
|
|
482
708
|
model,
|
|
483
709
|
reasoning,
|
|
484
710
|
input.timeoutMs,
|
|
485
711
|
);
|
|
486
|
-
const observedUrl = await submitChatgptPrompt(input.controllerHome, input.workId,
|
|
712
|
+
const observedUrl = await submitChatgptPrompt(input.controllerHome, input.workId, navigation.browserSessionId, `${workflowToolAttributionInstruction(input.workId)}\n\n${input.prompt}`, navigation.submissionTargetUrl, input.timeoutMs);
|
|
487
713
|
if (/\/c\/[^/?#]+/.test(observedUrl)) {
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
714
|
+
const observedIdentity = parseChatgptConversationIdentity(observedUrl);
|
|
715
|
+
binding = binding && binding.conversationId !== observedIdentity.conversationId
|
|
716
|
+
? rebindChatgptWorkConversation(store, {
|
|
717
|
+
workId: input.workId,
|
|
718
|
+
previousConversationId: binding.conversationId,
|
|
719
|
+
conversationUrl: observedUrl,
|
|
720
|
+
latestBrowserSessionId: navigation.browserSessionId,
|
|
721
|
+
localAlias: binding.localAlias ?? input.title,
|
|
722
|
+
})
|
|
723
|
+
: bindChatgptWorkConversation(store, {
|
|
724
|
+
workId: input.workId,
|
|
725
|
+
conversationUrl: observedUrl,
|
|
726
|
+
latestBrowserSessionId: navigation.browserSessionId,
|
|
727
|
+
localAlias: binding?.localAlias ?? input.title,
|
|
728
|
+
});
|
|
494
729
|
}
|
|
730
|
+
// Work-bound dispatch keeps the Forge-owned automation tab/session alive for
|
|
731
|
+
// the launched semantic Controller round. The exact Controller release path
|
|
732
|
+
// settles it after an explicit disposition. Closing here would race the new
|
|
733
|
+
// ChatGPT round and make dispatch success look like round completion.
|
|
495
734
|
return {
|
|
496
735
|
status: 'dispatched',
|
|
497
736
|
provider: 'controller-browser',
|
|
498
|
-
browserSessionId:
|
|
737
|
+
browserSessionId: navigation.browserSessionId,
|
|
499
738
|
conversationUrl: binding?.conversationUrl ?? observedUrl,
|
|
500
739
|
conversationId: binding?.conversationId,
|
|
501
740
|
localAlias: binding?.localAlias,
|