@moretea-labs/forge 1.6.0 → 1.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +4 -0
- package/CHANGELOG.md +39 -0
- package/CLAUDE.md +4 -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/02-operating-mode.partial.md +2 -0
- package/assets/partials-agents/03-orchestration.partial.md +1 -1
- package/assets/plugin-registry.v1.json +3 -3
- 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-architecture-sync.sh +17 -0
- 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/inspect-project-state.ts +8 -6
- package/assets/templates/helpers/migrate-project-template.sh +22 -45
- package/assets/templates/helpers/migrate-workflow-docs.ts +9 -7
- 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/helpers/workflow-contract.ts +5 -1
- 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 +178 -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-plugin-management.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 +143 -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 +33 -11
- package/scripts/activate-source-baseline.ts +17 -13
- package/scripts/architecture-event.ts +2 -820
- package/scripts/architecture-queue.sh +2 -645
- package/scripts/archive-architecture-request.sh +2 -242
- package/scripts/archive-workflow.sh +1 -291
- 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/capability-config.ts +2 -392
- package/scripts/capability-resolver.ts +2 -503
- package/scripts/capture-plan.sh +1 -332
- package/scripts/check-agent-tooling.sh +2 -1558
- package/scripts/check-architecture-sync.sh +2 -371
- package/scripts/check-brain-manifest.sh +2 -253
- package/scripts/check-context-files.sh +2 -47
- package/scripts/check-deploy-sql-order.sh +2 -115
- package/scripts/check-mcp-compatibility.ts +14 -0
- package/scripts/check-runtime-architecture.mjs +349 -79
- package/scripts/check-skill-version.ts +3 -191
- package/scripts/check-source-duplication.mjs +61 -0
- package/scripts/check-stable-baseline.ts +92 -0
- package/scripts/check-task-sync.sh +2 -63
- package/scripts/check-task-workflow.sh +2 -1000
- package/scripts/codex-handoff-resume.sh +2 -267
- package/scripts/context-contract-sync.sh +2 -514
- package/scripts/contract-run.ts +2 -463
- package/scripts/contract-worktree.sh +1 -722
- package/scripts/ensure-task-workflow.sh +1 -1225
- package/scripts/host-temporary-cloud-mcp-e2e.sh +265 -0
- package/scripts/inspect-project-state.ts +4 -279
- package/scripts/install-standalone-recovery.ts +24 -0
- package/scripts/lib/project-init-lib.sh +22 -46
- package/scripts/lib/run-forge-helper.sh +19 -0
- package/scripts/lib/run-forge-helper.ts +26 -0
- package/scripts/migrate-project-template.sh +1 -1189
- package/scripts/migrate-workflow-docs.ts +4 -412
- package/scripts/new-plan.sh +1 -180
- package/scripts/new-spec.sh +1 -47
- package/scripts/new-sprint.sh +1 -46
- package/scripts/plan-to-todo.sh +1 -796
- package/scripts/prepare-codex-handoff.sh +2 -201
- package/scripts/prepare-handoff.sh +1 -78
- package/scripts/refresh-current-status.sh +2 -467
- package/scripts/run-governed-gate.ts +5 -1
- package/scripts/select-agent-context-blocks.sh +2 -64
- package/scripts/ship-worktrees.sh +1 -558
- 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/sprint-backlog.sh +1 -834
- package/scripts/stage-runtime-release.ts +2 -1
- package/scripts/summarize-failures.sh +2 -123
- package/scripts/switch-plan.sh +1 -150
- package/scripts/sync-brain-docs.sh +2 -379
- package/scripts/sync-generated-authority.mjs +175 -0
- package/scripts/verify-contract.sh +2 -703
- package/scripts/verify-forge-runtime.sh +0 -1
- package/scripts/verify-sprint.sh +1 -677
- package/scripts/workflow-contract.ts +4 -205
- package/scripts/workstream-sync.sh +2 -261
- package/src/cli/agent-jobs/executor-health.ts +1 -1
- package/src/cli/commands/init-hook.ts +3 -3
- package/src/cli/commands/mcp.ts +9 -5
- package/src/cli/commands/plugin.ts +12 -3
- 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 +113 -5
- 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 +828 -487
- 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/contracts.ts +16 -0
- package/src/cli/github/github.ts +135 -16
- package/src/cli/github/session.ts +2 -1
- 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 +37 -264
- 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 +2 -515
- 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 +10 -222
- package/src/cli/mcp/multi-repository.ts +16 -5
- package/src/cli/mcp/oauth.ts +175 -26
- package/src/cli/mcp/post-finalize-work-attribution.ts +150 -0
- package/src/cli/mcp/repository-tools.ts +282 -146
- package/src/cli/mcp/server.ts +586 -116
- package/src/cli/mcp/tool-capability-cache.ts +1 -1
- package/src/cli/mcp/tool-contract.ts +14 -0
- package/src/cli/mcp/toolset-selection.ts +1 -1
- package/src/cli/mcp/toolset.ts +44 -7
- package/src/cli/mcp/transports/http.ts +124 -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 +103 -21
- package/src/cli/repositories/command-executor.ts +78 -526
- 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 +124 -0
- package/src/cli/repositories/selected-path-actions.ts +17 -9
- package/src/cli/repositories/structured-git.ts +118 -6
- package/src/cli/repository/inspector.ts +563 -65
- 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-contract.ts +61 -0
- package/src/runtime/context/semantic-navigation.ts +223 -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 +48 -16
- package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +86 -24
- package/src/runtime/control-plane/execution/repository-work-attribution.ts +106 -0
- package/src/runtime/control-plane/execution/retained-work-resume.ts +203 -0
- 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-completion-authority.ts +58 -0
- package/src/runtime/control-plane/execution/work-evidence-policy.ts +200 -0
- package/src/runtime/control-plane/execution/work-execution-support.ts +386 -0
- package/src/runtime/control-plane/execution/work-finalization-service.ts +1913 -0
- package/src/runtime/control-plane/execution/work-handle-authority.ts +96 -0
- 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-operation-service.ts +468 -0
- package/src/runtime/control-plane/execution/work-path-scope.ts +37 -0
- package/src/runtime/control-plane/execution/work-preparation-service.ts +479 -0
- package/src/runtime/control-plane/execution/work-process-evidence.ts +129 -0
- package/src/runtime/control-plane/execution/work-remote-delivery.ts +215 -0
- package/src/runtime/control-plane/execution/work-task-receipt.ts +22 -18
- package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +156 -11
- package/src/runtime/control-plane/execution/work-validation-reconciler.ts +22 -14
- package/src/runtime/control-plane/execution/work-verification-context.ts +56 -0
- package/src/runtime/control-plane/execution/work-verification-service.ts +388 -0
- package/src/runtime/control-plane/execution/work-verification-snapshot.ts +90 -9
- package/src/runtime/control-plane/facade/capability-registry.ts +129 -25
- package/src/runtime/control-plane/facade/controller-round-relay.ts +1118 -0
- package/src/runtime/control-plane/facade/controller-session-store.ts +302 -12
- package/src/runtime/control-plane/facade/goal-workloop-access.ts +17 -1
- package/src/runtime/control-plane/facade/goal-workloop.ts +928 -190
- package/src/runtime/control-plane/facade/index.ts +3 -0
- package/src/runtime/control-plane/facade/plan-contract-store.ts +275 -42
- package/src/runtime/control-plane/facade/repository-work-admission.ts +147 -0
- package/src/runtime/control-plane/facade/requirement-authority.ts +83 -0
- 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 +90 -10
- package/src/runtime/control-plane/facade/work-contract-store.ts +157 -261
- package/src/runtime/control-plane/facade/work-state-machine.ts +250 -0
- 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 +152 -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 +278 -543
- 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 +63 -0
- package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +365 -117
- package/src/runtime/control-plane/launcher/launch-reservation-store.ts +89 -0
- package/src/runtime/control-plane/launcher/thin-launcher.ts +244 -11
- package/src/runtime/control-plane/persistence/controller-home-migration.ts +487 -0
- package/src/runtime/control-plane/persistence/requirement-store.ts +0 -12
- 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/repo-actor/registry.ts +4 -2
- package/src/runtime/control-plane/routing/route-policy.ts +24 -21
- package/src/runtime/control-plane/routing/workspace-admission.ts +70 -0
- 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/evidence/process-check-execution.ts +18 -0
- package/src/runtime/execution/jobs/child-reference.ts +6 -2
- 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 +88 -0
- package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +7 -1
- package/src/runtime/execution/process-runtime/check-scheduling.ts +114 -0
- package/src/runtime/execution/process-runtime/command-facade.ts +367 -40
- package/src/runtime/execution/process-runtime/gc.ts +38 -6
- package/src/runtime/execution/process-runtime/lightweight-managed.ts +1201 -0
- package/src/runtime/execution/process-runtime/persisted-check.ts +51 -5
- package/src/runtime/execution/process-runtime/resource-claims.ts +10 -6
- package/src/runtime/execution/process-runtime/runtime.ts +53 -3
- package/src/runtime/execution/process-runtime/store.ts +1 -0
- package/src/runtime/execution/process-runtime/types.ts +4 -12
- 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/work-activity.ts +21 -0
- package/src/runtime/execution/workers/executor.ts +18 -61
- package/src/runtime/gateway/mcp/execution-tools.ts +35 -1852
- package/src/runtime/gateway/mcp/legacy-ios-tool-adapter.ts +61 -0
- package/src/runtime/gateway/mcp/process-tools.ts +83 -37
- package/src/runtime/gateway/mcp/router.ts +186 -84
- package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +565 -0
- package/src/runtime/gateway/mcp/runtime-tools.ts +2378 -1947
- package/src/runtime/plugins/app-store-connect-adapter.ts +216 -47
- package/src/runtime/plugins/browser-adapter.ts +2662 -829
- package/src/runtime/plugins/browser-automation-service.ts +10 -0
- package/src/runtime/plugins/browser-handoff-host.ts +2 -25
- package/src/runtime/plugins/browser-handoff.ts +110 -5
- package/src/runtime/plugins/browser-macos-bridge.ts +701 -63
- package/src/runtime/plugins/browser-manifest-surface.ts +603 -0
- package/src/runtime/plugins/browser-native-session-service.ts +222 -0
- 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 +154 -0
- package/src/runtime/plugins/browser-runtime.ts +193 -0
- package/src/runtime/plugins/browser-session-authority.ts +332 -0
- package/src/runtime/plugins/browser-session-store.ts +121 -0
- package/src/runtime/plugins/browser-session-types.ts +98 -0
- package/src/runtime/plugins/capability-authorization-grants.ts +354 -0
- package/src/runtime/plugins/desktop-operator-registration.ts +10 -4
- package/src/runtime/plugins/external-adapter.ts +358 -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 +47 -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 +330 -33
- package/src/runtime/plugins/types.ts +38 -3
- 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 +426 -25
- package/src/runtime/root/mcp-transport.ts +180 -10
- package/src/runtime/root/package-connector-service.ts +230 -10
- package/src/runtime/root/package-runtime-release.ts +143 -17
- package/src/runtime/root/package-runtime-service.ts +461 -22
- package/src/runtime/root/release-manifest.ts +144 -18
- package/src/runtime/root/release-materialize.ts +125 -32
- 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 +12 -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 +1079 -240
- package/src/runtime/standalone-recovery/entry.ts +100 -24
- package/src/runtime/standalone-recovery/installer.ts +20 -4
- 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 +364 -202
- package/src/runtime/workflow/schedules/github-issue-watch.ts +158 -0
- package/src/runtime/workflow/schedules/settlement.ts +64 -17
- package/src/runtime/workflow/schedules/store.ts +209 -19
- 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,13 @@
|
|
|
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
|
+
hasChatgptConversationIdentity,
|
|
9
|
+
parseChatgptConversationIdentity,
|
|
10
|
+
rebindChatgptWorkConversation,
|
|
8
11
|
type ChatgptWorkConversationBinding,
|
|
9
12
|
} from './chatgpt-work-binding-store';
|
|
10
13
|
|
|
@@ -14,13 +17,17 @@ export const DEFAULT_CHATGPT_AUTOMATION_REASONING = 'high';
|
|
|
14
17
|
export const DEFAULT_CHATGPT_AUTOMATION_TAB_POLICY = 'auto';
|
|
15
18
|
export const DEFAULT_CHATGPT_AUTOMATION_PLUGIN_MENTION = '@forge';
|
|
16
19
|
const CHATGPT_PROMPT_SELECTOR = 'div#prompt-textarea[contenteditable="true"]';
|
|
17
|
-
const CHATGPT_SEND_SELECTOR = '[data-testid="send-button"]';
|
|
18
|
-
const
|
|
20
|
+
const CHATGPT_SEND_SELECTOR = '[data-testid="send-button"], button[aria-label*="Send"], button[data-testid*="send"]';
|
|
21
|
+
const CHATGPT_INTELLIGENCE_CONTROL_SELECTORS = [
|
|
22
|
+
'main button, main [role="button"]',
|
|
23
|
+
'button, [role="button"]',
|
|
24
|
+
] as const;
|
|
19
25
|
const CHATGPT_CAPABILITY_SLIDER_SELECTOR = '[role="slider"]';
|
|
20
26
|
const CHATGPT_CAPABILITY_MENUITEM_SELECTOR = '[role="menuitem"][aria-keyshortcuts~="ArrowLeft"][aria-keyshortcuts~="ArrowRight"]';
|
|
21
27
|
|
|
22
28
|
export type ChatgptAutomationReasoning = 'medium' | 'high' | 'xhigh';
|
|
23
29
|
export type ChatgptAutomationTabPolicy = 'auto' | 'reuse' | 'new';
|
|
30
|
+
export type ChatgptAutomationTabCleanupStatus = 'closed' | 'preserved_user_owned' | 'session_closed' | 'failed';
|
|
24
31
|
|
|
25
32
|
export interface WorkChatgptContinuationInput {
|
|
26
33
|
controllerHome: string;
|
|
@@ -37,33 +44,6 @@ export interface WorkChatgptContinuationInput {
|
|
|
37
44
|
timeoutMs?: number;
|
|
38
45
|
}
|
|
39
46
|
|
|
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
47
|
export interface WorkChatgptContinuationResult {
|
|
68
48
|
status: 'dispatched' | 'failed';
|
|
69
49
|
provider: 'controller-browser';
|
|
@@ -76,15 +56,42 @@ export interface WorkChatgptContinuationResult {
|
|
|
76
56
|
reasoning: ChatgptAutomationReasoning;
|
|
77
57
|
tabPolicy: ChatgptAutomationTabPolicy;
|
|
78
58
|
executionPreferenceVerified: boolean;
|
|
59
|
+
tabCleanupStatus?: ChatgptAutomationTabCleanupStatus;
|
|
60
|
+
tabCleanupError?: { code: string; message: string };
|
|
79
61
|
error?: { code: string; message: string };
|
|
80
62
|
}
|
|
81
63
|
|
|
64
|
+
export interface StandaloneChatgptPromptInput {
|
|
65
|
+
controllerHome: string;
|
|
66
|
+
repoId: string;
|
|
67
|
+
scopeId: string;
|
|
68
|
+
prompt: string;
|
|
69
|
+
browserSessionId?: string;
|
|
70
|
+
conversationUrl?: string;
|
|
71
|
+
model?: string;
|
|
72
|
+
reasoning?: ChatgptAutomationReasoning;
|
|
73
|
+
tabPolicy?: ChatgptAutomationTabPolicy;
|
|
74
|
+
timeoutMs?: number;
|
|
75
|
+
}
|
|
76
|
+
|
|
82
77
|
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
78
|
|
|
84
79
|
function requestId(workId: string, actionId: string): string {
|
|
85
80
|
return `chatgpt-work:${workId}:${actionId}:${randomUUID()}`;
|
|
86
81
|
}
|
|
87
82
|
|
|
83
|
+
const CHATGPT_BROWSER_TRANSPORT_OVERRIDES = {
|
|
84
|
+
browser_mode: 'attach_preferred',
|
|
85
|
+
cdp_attach_fallback: 'fail_closed',
|
|
86
|
+
native_attach_mode: 'auto',
|
|
87
|
+
native_browser_candidates: ['chrome'],
|
|
88
|
+
} as const;
|
|
89
|
+
|
|
90
|
+
export function chatgptBrowserActionArgs(actionId: string, args: Record<string, unknown>): Record<string, unknown> {
|
|
91
|
+
if (actionId === 'configure' || actionId === 'list_sessions' || actionId === 'reconcile_sessions') return args;
|
|
92
|
+
return { ...args, ...CHATGPT_BROWSER_TRANSPORT_OVERRIDES };
|
|
93
|
+
}
|
|
94
|
+
|
|
88
95
|
async function controllerBrowserAction(
|
|
89
96
|
controllerHome: string,
|
|
90
97
|
workId: string,
|
|
@@ -102,7 +109,7 @@ async function controllerBrowserAction(
|
|
|
102
109
|
pluginId: 'browser',
|
|
103
110
|
actionId,
|
|
104
111
|
requestId: requestId(workId, actionId),
|
|
105
|
-
args,
|
|
112
|
+
args: chatgptBrowserActionArgs(actionId, args),
|
|
106
113
|
timeoutMs,
|
|
107
114
|
origin: { surface: 'schedule', actor: 'chatgpt-work-continuation' },
|
|
108
115
|
});
|
|
@@ -112,18 +119,12 @@ async function ensureControllerChatgptBrowser(controllerHome: string, workId: st
|
|
|
112
119
|
const repository = controllerPluginRepository(controllerHome);
|
|
113
120
|
const repoRoot = repository.canonicalRoot ?? repository.localRoot;
|
|
114
121
|
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
|
-
});
|
|
122
|
+
// Browser configure is not a read: it persists configuration and closes managed
|
|
123
|
+
// contexts. Scheduled continuations must not disturb an already-enabled provider
|
|
124
|
+
// merely to prove it is available. Only enable it when the persisted authority is
|
|
125
|
+
// explicitly disabled; action-level transport overrides still fail closed later.
|
|
126
|
+
if (buildBrowserPluginManifest(0, undefined, repoRoot).enabled) return;
|
|
127
|
+
await controllerBrowserAction(controllerHome, workId, 'configure', { enabled: true });
|
|
127
128
|
}
|
|
128
129
|
|
|
129
130
|
function stringField(value: unknown): string | undefined {
|
|
@@ -149,9 +150,16 @@ export function stableChatgptWorkBrowserSessionId(repoId: string, workId: string
|
|
|
149
150
|
return `forge-chatgpt-work-${digest}`;
|
|
150
151
|
}
|
|
151
152
|
|
|
152
|
-
export function
|
|
153
|
-
const digest = createHash('sha256').update(`${repoId}\
|
|
154
|
-
return `forge-chatgpt-
|
|
153
|
+
export function stableStandaloneChatgptBrowserSessionId(repoId: string, scopeId: string): string {
|
|
154
|
+
const digest = createHash('sha256').update(`${repoId}\nstandalone\n${scopeId}`).digest('hex').slice(0, 20);
|
|
155
|
+
return `forge-chatgpt-standalone-${digest}`;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function resolveStandaloneChatgptBrowserSessionId(input: StandaloneChatgptPromptInput): string {
|
|
159
|
+
const policy = normalizeTabPolicy(input.tabPolicy);
|
|
160
|
+
const stable = stableStandaloneChatgptBrowserSessionId(input.repoId, input.scopeId);
|
|
161
|
+
if (policy === 'new') return `${stable}-${randomUUID().slice(0, 8)}`;
|
|
162
|
+
return input.browserSessionId?.trim() || stable;
|
|
155
163
|
}
|
|
156
164
|
|
|
157
165
|
export function resolveChatgptWorkBrowserSessionId(input: {
|
|
@@ -189,10 +197,45 @@ function matchSelector(value: BrowserQueryMatch | undefined): string | undefined
|
|
|
189
197
|
return typeof value?.selectorHint === 'string' && value.selectorHint.trim() ? value.selectorHint.trim() : undefined;
|
|
190
198
|
}
|
|
191
199
|
|
|
200
|
+
function normalizeExecutionControlLabel(label: string | undefined): string {
|
|
201
|
+
return (label ?? '').trim().toLowerCase().replace(/[\s_-]+/g, '');
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
export function chatgptAutomationReasoningLevelFromLabel(label: string | undefined): ChatgptAutomationReasoning | undefined {
|
|
205
|
+
const normalized = normalizeExecutionControlLabel(label);
|
|
206
|
+
if (!normalized) return undefined;
|
|
207
|
+
|
|
208
|
+
const exact = new Map<string, ChatgptAutomationReasoning>([
|
|
209
|
+
['medium', 'medium'], ['中', 'medium'], ['中等', 'medium'],
|
|
210
|
+
['high', 'high'], ['高', 'high'],
|
|
211
|
+
['xhigh', 'xhigh'], ['extrahigh', 'xhigh'], ['超高', 'xhigh'], ['极高', 'xhigh'],
|
|
212
|
+
]);
|
|
213
|
+
const exactLevel = exact.get(normalized);
|
|
214
|
+
if (exactLevel) return exactLevel;
|
|
215
|
+
|
|
216
|
+
const hasReasoningContext = ['reasoning', 'thinking', '推理', '思考']
|
|
217
|
+
.some((token) => normalized.includes(token));
|
|
218
|
+
if (!hasReasoningContext) return undefined;
|
|
219
|
+
if (['xhigh', 'extrahigh', '超高', '极高'].some((token) => normalized.includes(token))) return 'xhigh';
|
|
220
|
+
if (['medium', '中等'].some((token) => normalized.includes(token))) return 'medium';
|
|
221
|
+
if (['high', '高'].some((token) => normalized.includes(token))) return 'high';
|
|
222
|
+
return undefined;
|
|
223
|
+
}
|
|
224
|
+
|
|
192
225
|
function modelLabelMatches(label: string | undefined, model: string): boolean {
|
|
193
226
|
if (model !== DEFAULT_CHATGPT_AUTOMATION_MODEL || !label) return false;
|
|
194
|
-
const normalized = label
|
|
195
|
-
return normalized.includes('5.6sol') || normalized.includes('
|
|
227
|
+
const normalized = normalizeExecutionControlLabel(label);
|
|
228
|
+
return normalized.includes('5.6sol') || normalized.includes('gpt5.6sol');
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function reasoningLabelMatches(label: string | undefined, reasoning: ChatgptAutomationReasoning): boolean {
|
|
232
|
+
return chatgptAutomationReasoningLevelFromLabel(label) === reasoning;
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
function isReasoningControlLabel(label: string | undefined): boolean {
|
|
236
|
+
return reasoningLabelMatches(label, 'medium')
|
|
237
|
+
|| reasoningLabelMatches(label, 'high')
|
|
238
|
+
|| reasoningLabelMatches(label, 'xhigh');
|
|
196
239
|
}
|
|
197
240
|
|
|
198
241
|
function reasoningSliderValue(reasoning: ChatgptAutomationReasoning): number {
|
|
@@ -207,13 +250,38 @@ async function findChatgptIntelligenceControl(
|
|
|
207
250
|
browserSessionId: string,
|
|
208
251
|
timeoutMs?: number,
|
|
209
252
|
): Promise<BrowserQueryMatch | undefined> {
|
|
210
|
-
const
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
253
|
+
for (const selector of CHATGPT_INTELLIGENCE_CONTROL_SELECTORS) {
|
|
254
|
+
const result = await controllerBrowserAction(controllerHome, workId, 'query_all', {
|
|
255
|
+
session_id: browserSessionId,
|
|
256
|
+
selector,
|
|
257
|
+
// Prefer the composer/main region so sidebar history cannot crowd the
|
|
258
|
+
// reasoning control out of a bounded query. Keep a larger global fallback
|
|
259
|
+
// for ChatGPT layouts that place the control outside <main>.
|
|
260
|
+
limit: chatgptAutomationControlQueryLimit(selector),
|
|
261
|
+
timeout_ms: timeoutMs ?? 60_000,
|
|
262
|
+
}, timeoutMs);
|
|
263
|
+
const match = queryMatches(result).find((candidate) => {
|
|
264
|
+
const label = matchText(candidate);
|
|
265
|
+
return modelLabelMatches(label, DEFAULT_CHATGPT_AUTOMATION_MODEL) || isReasoningControlLabel(label);
|
|
266
|
+
});
|
|
267
|
+
if (match) return match;
|
|
268
|
+
}
|
|
269
|
+
return undefined;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
export function chatgptAutomationControlQueryLimit(selector: string): number {
|
|
273
|
+
// Long tool-heavy conversations can contribute well over 80 buttons before
|
|
274
|
+
// the composer controls. Keep the preferred <main> scan bounded but large
|
|
275
|
+
// enough that the current reasoning control is not crowded out by history.
|
|
276
|
+
return selector.startsWith('main ') ? 160 : 320;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
export function chatgptAutomationControlWaitBudgets(timeoutMs?: number): { waitBudgetMs: number; probeTimeoutMs: number } {
|
|
280
|
+
const waitBudgetMs = Math.min(Math.max(timeoutMs ?? 30_000, 1_000), 30_000);
|
|
281
|
+
// Native Chrome attachment has a few seconds of Apple Events/DOM latency on
|
|
282
|
+
// long conversations. Keep each probe bounded while allowing a real query to
|
|
283
|
+
// complete; the total readiness window remains capped at 30 seconds.
|
|
284
|
+
return { waitBudgetMs, probeTimeoutMs: Math.min(waitBudgetMs, 5_000) };
|
|
217
285
|
}
|
|
218
286
|
|
|
219
287
|
async function waitForChatgptIntelligenceControl(
|
|
@@ -222,10 +290,10 @@ async function waitForChatgptIntelligenceControl(
|
|
|
222
290
|
browserSessionId: string,
|
|
223
291
|
timeoutMs?: number,
|
|
224
292
|
): Promise<BrowserQueryMatch | undefined> {
|
|
225
|
-
const waitBudgetMs =
|
|
293
|
+
const { waitBudgetMs, probeTimeoutMs } = chatgptAutomationControlWaitBudgets(timeoutMs);
|
|
226
294
|
const deadline = Date.now() + waitBudgetMs;
|
|
227
295
|
do {
|
|
228
|
-
const control = await findChatgptIntelligenceControl(controllerHome, workId, browserSessionId,
|
|
296
|
+
const control = await findChatgptIntelligenceControl(controllerHome, workId, browserSessionId, probeTimeoutMs);
|
|
229
297
|
if (control) return control;
|
|
230
298
|
if (Date.now() >= deadline) break;
|
|
231
299
|
await new Promise((resolveWait) => setTimeout(resolveWait, 500));
|
|
@@ -233,6 +301,46 @@ async function waitForChatgptIntelligenceControl(
|
|
|
233
301
|
return undefined;
|
|
234
302
|
}
|
|
235
303
|
|
|
304
|
+
export function chatgptAutomationPageFailure(
|
|
305
|
+
bodyText: string | undefined,
|
|
306
|
+
composerAvailable: boolean,
|
|
307
|
+
): 'CHATGPT_AUTOMATION_LOGIN_REQUIRED' | 'CHATGPT_AUTOMATION_COMPOSER_UNAVAILABLE' | undefined {
|
|
308
|
+
if (composerAvailable) return undefined;
|
|
309
|
+
const normalized = (bodyText ?? '').toLowerCase();
|
|
310
|
+
const loginMarkers = [
|
|
311
|
+
'log in', 'sign up', 'continue with google', 'continue with apple',
|
|
312
|
+
'登录', '登陆', '注册', '使用 google 继续', '使用 apple 继续',
|
|
313
|
+
];
|
|
314
|
+
return loginMarkers.some((marker) => normalized.includes(marker))
|
|
315
|
+
? 'CHATGPT_AUTOMATION_LOGIN_REQUIRED'
|
|
316
|
+
: 'CHATGPT_AUTOMATION_COMPOSER_UNAVAILABLE';
|
|
317
|
+
}
|
|
318
|
+
|
|
319
|
+
async function assertChatgptAutomationComposerReady(
|
|
320
|
+
controllerHome: string,
|
|
321
|
+
workId: string,
|
|
322
|
+
browserSessionId: string,
|
|
323
|
+
timeoutMs?: number,
|
|
324
|
+
): Promise<void> {
|
|
325
|
+
const composerResult = await controllerBrowserAction(controllerHome, workId, 'query_all', {
|
|
326
|
+
session_id: browserSessionId,
|
|
327
|
+
selector: CHATGPT_PROMPT_SELECTOR,
|
|
328
|
+
limit: 1,
|
|
329
|
+
timeout_ms: Math.min(timeoutMs ?? 5_000, 5_000),
|
|
330
|
+
}, timeoutMs).catch(() => undefined);
|
|
331
|
+
const composerAvailable = queryMatches(composerResult).length > 0;
|
|
332
|
+
if (composerAvailable) return;
|
|
333
|
+
|
|
334
|
+
const bodyResult = await controllerBrowserAction(controllerHome, workId, 'get_text', {
|
|
335
|
+
session_id: browserSessionId,
|
|
336
|
+
selector: 'body',
|
|
337
|
+
max_chars: 4_000,
|
|
338
|
+
timeout_ms: Math.min(timeoutMs ?? 5_000, 5_000),
|
|
339
|
+
}, timeoutMs).catch(() => undefined);
|
|
340
|
+
const failure = chatgptAutomationPageFailure(stringField(bodyResult?.text), composerAvailable);
|
|
341
|
+
throw new Error(failure ?? 'CHATGPT_AUTOMATION_COMPOSER_UNAVAILABLE');
|
|
342
|
+
}
|
|
343
|
+
|
|
236
344
|
async function ensureChatgptExecutionPreference(
|
|
237
345
|
controllerHome: string,
|
|
238
346
|
workId: string,
|
|
@@ -243,8 +351,15 @@ async function ensureChatgptExecutionPreference(
|
|
|
243
351
|
): Promise<boolean> {
|
|
244
352
|
if (model !== DEFAULT_CHATGPT_AUTOMATION_MODEL) throw new Error(`CHATGPT_AUTOMATION_MODEL_UNSUPPORTED:${model}`);
|
|
245
353
|
let control = await waitForChatgptIntelligenceControl(controllerHome, workId, browserSessionId, timeoutMs);
|
|
246
|
-
if (!control)
|
|
247
|
-
|
|
354
|
+
if (!control) {
|
|
355
|
+
await assertChatgptAutomationComposerReady(controllerHome, workId, browserSessionId, timeoutMs);
|
|
356
|
+
throw new Error('CHATGPT_AUTOMATION_INTELLIGENCE_CONTROL_UNAVAILABLE');
|
|
357
|
+
}
|
|
358
|
+
// Current ChatGPT UI exposes the reasoning level (for example `高`) on the
|
|
359
|
+
// composer control instead of the model label. The model remains fail-closed
|
|
360
|
+
// through normalizeModel above; only the user-adjustable reasoning control is
|
|
361
|
+
// required to be observable in the page before unattended dispatch proceeds.
|
|
362
|
+
if (reasoningLabelMatches(matchText(control), reasoning)) return true;
|
|
248
363
|
let controlSelector = matchSelector(control);
|
|
249
364
|
if (!controlSelector) throw new Error('CHATGPT_AUTOMATION_INTELLIGENCE_CONTROL_UNAVAILABLE');
|
|
250
365
|
|
|
@@ -296,7 +411,9 @@ async function ensureChatgptExecutionPreference(
|
|
|
296
411
|
throw new Error(`CHATGPT_AUTOMATION_REASONING_NOT_VERIFIED:${reasoning}`);
|
|
297
412
|
}
|
|
298
413
|
control = await findChatgptIntelligenceControl(controllerHome, workId, browserSessionId, timeoutMs) ?? control;
|
|
299
|
-
if (!
|
|
414
|
+
if (isReasoningControlLabel(matchText(control)) && !reasoningLabelMatches(matchText(control), reasoning)) {
|
|
415
|
+
throw new Error(`CHATGPT_AUTOMATION_REASONING_NOT_VERIFIED:${reasoning}`);
|
|
416
|
+
}
|
|
300
417
|
controlSelector = matchSelector(control) ?? controlSelector;
|
|
301
418
|
await controllerBrowserAction(controllerHome, workId, 'press', {
|
|
302
419
|
session_id: browserSessionId,
|
|
@@ -313,6 +430,122 @@ function resultUrl(result: Record<string, unknown>): string | undefined {
|
|
|
313
430
|
?? stringField((result.session as Record<string, unknown> | undefined)?.url);
|
|
314
431
|
}
|
|
315
432
|
|
|
433
|
+
function resultSessionId(result: Record<string, unknown>): string | undefined {
|
|
434
|
+
return stringField(result.session_id)
|
|
435
|
+
?? stringField((result.session as Record<string, unknown> | undefined)?.sessionId);
|
|
436
|
+
}
|
|
437
|
+
|
|
438
|
+
export function chatgptAutomationNavigationRequiresReplacement(error: unknown): boolean {
|
|
439
|
+
if (!(error instanceof Error)) return false;
|
|
440
|
+
return [
|
|
441
|
+
'BROWSER_AUTOMATION_BACKGROUND_NAVIGATION_REQUIRES_REPLACEMENT',
|
|
442
|
+
'PLUGIN_BROWSER_SESSION_STATE_LOST',
|
|
443
|
+
'PLUGIN_SESSION_NOT_FOUND',
|
|
444
|
+
'PLUGIN_BROWSER_NATIVE_TAB_IDENTITY_UNPROVEN',
|
|
445
|
+
].some((marker) => error.message.includes(marker));
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
async function closeChatgptAutomationTabAfterDispatch(
|
|
449
|
+
controllerHome: string,
|
|
450
|
+
workId: string,
|
|
451
|
+
browserSessionId: string,
|
|
452
|
+
timeoutMs?: number,
|
|
453
|
+
): Promise<{ status: ChatgptAutomationTabCleanupStatus; error?: { code: string; message: string } }> {
|
|
454
|
+
try {
|
|
455
|
+
const closed = await controllerBrowserAction(controllerHome, workId, 'close_page', {
|
|
456
|
+
session_id: browserSessionId,
|
|
457
|
+
}, Math.min(timeoutMs ?? 15_000, 15_000));
|
|
458
|
+
if (closed.preservedUserOwnedTab === true) return { status: 'preserved_user_owned' };
|
|
459
|
+
if (closed.resourceClosed === true) return { status: 'closed' };
|
|
460
|
+
return { status: 'session_closed' };
|
|
461
|
+
} catch (error) {
|
|
462
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
463
|
+
return {
|
|
464
|
+
status: 'failed',
|
|
465
|
+
error: {
|
|
466
|
+
code: error instanceof Error && error.message.includes(':') ? error.message.split(':', 1)[0] : 'CHATGPT_AUTOMATION_TAB_CLEANUP_FAILED',
|
|
467
|
+
message,
|
|
468
|
+
},
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
/**
|
|
474
|
+
* Settles the browser resource for one completed Work-bound ChatGPT round.
|
|
475
|
+
* Conversation identity is durable in the Work binding and intentionally
|
|
476
|
+
* survives this ephemeral tab/session cleanup. Browser ownership policy keeps
|
|
477
|
+
* user-owned native tabs intact.
|
|
478
|
+
*/
|
|
479
|
+
export async function settleWorkChatgptAutomationTab(input: {
|
|
480
|
+
controllerHome: string;
|
|
481
|
+
workId: string;
|
|
482
|
+
browserSessionId: string;
|
|
483
|
+
timeoutMs?: number;
|
|
484
|
+
}): Promise<{ status: ChatgptAutomationTabCleanupStatus; error?: { code: string; message: string } }> {
|
|
485
|
+
return closeChatgptAutomationTabAfterDispatch(
|
|
486
|
+
input.controllerHome,
|
|
487
|
+
input.workId,
|
|
488
|
+
input.browserSessionId,
|
|
489
|
+
input.timeoutMs,
|
|
490
|
+
);
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
export function isChatgptConversationUrl(value: string): boolean {
|
|
494
|
+
try {
|
|
495
|
+
const url = new URL(value);
|
|
496
|
+
return url.protocol === 'https:'
|
|
497
|
+
&& ['chatgpt.com', 'www.chatgpt.com', 'chat.openai.com'].includes(url.hostname)
|
|
498
|
+
&& /\/c\/[^/?#]+/.test(url.pathname);
|
|
499
|
+
} catch {
|
|
500
|
+
return false;
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
async function navigateWorkConversation(
|
|
505
|
+
controllerHome: string,
|
|
506
|
+
workId: string,
|
|
507
|
+
browserSessionId: string,
|
|
508
|
+
targetUrl: string,
|
|
509
|
+
timeoutMs?: number,
|
|
510
|
+
): Promise<{ submissionTargetUrl: string; recoveredFromStaleBinding: boolean; browserSessionId: string }> {
|
|
511
|
+
const navigate = async (sessionId: string, url: string) => controllerBrowserAction(controllerHome, workId, 'navigate', {
|
|
512
|
+
session_id: sessionId,
|
|
513
|
+
url,
|
|
514
|
+
wait_until: 'domcontentloaded',
|
|
515
|
+
timeout_ms: timeoutMs ?? 60_000,
|
|
516
|
+
retries: 1,
|
|
517
|
+
}, timeoutMs);
|
|
518
|
+
const openReplacement = async (url: string): Promise<string> => {
|
|
519
|
+
const opened = await controllerBrowserAction(controllerHome, workId, 'open_page', {
|
|
520
|
+
url,
|
|
521
|
+
wait_until: 'domcontentloaded',
|
|
522
|
+
timeout_ms: timeoutMs ?? 60_000,
|
|
523
|
+
retries: 1,
|
|
524
|
+
}, timeoutMs);
|
|
525
|
+
const replacementSessionId = resultSessionId(opened);
|
|
526
|
+
if (!replacementSessionId) throw new Error('CHATGPT_AUTOMATION_REPLACEMENT_SESSION_NOT_CONFIRMED');
|
|
527
|
+
return replacementSessionId;
|
|
528
|
+
};
|
|
529
|
+
try {
|
|
530
|
+
await navigate(browserSessionId, targetUrl);
|
|
531
|
+
return { submissionTargetUrl: targetUrl, recoveredFromStaleBinding: false, browserSessionId };
|
|
532
|
+
} catch (error) {
|
|
533
|
+
if (chatgptAutomationNavigationRequiresReplacement(error)) {
|
|
534
|
+
const replacementSessionId = await openReplacement(targetUrl);
|
|
535
|
+
return { submissionTargetUrl: targetUrl, recoveredFromStaleBinding: false, browserSessionId: replacementSessionId };
|
|
536
|
+
}
|
|
537
|
+
if (!isChatgptConversationUrl(targetUrl)) throw error;
|
|
538
|
+
try {
|
|
539
|
+
await navigate(browserSessionId, 'https://chatgpt.com/');
|
|
540
|
+
return { submissionTargetUrl: 'https://chatgpt.com/', recoveredFromStaleBinding: true, browserSessionId };
|
|
541
|
+
} catch (fallbackError) {
|
|
542
|
+
if (!chatgptAutomationNavigationRequiresReplacement(fallbackError)) throw fallbackError;
|
|
543
|
+
const replacementSessionId = await openReplacement('https://chatgpt.com/');
|
|
544
|
+
return { submissionTargetUrl: 'https://chatgpt.com/', recoveredFromStaleBinding: true, browserSessionId: replacementSessionId };
|
|
545
|
+
}
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
|
|
316
549
|
async function submitChatgptPrompt(
|
|
317
550
|
controllerHome: string,
|
|
318
551
|
workId: string,
|
|
@@ -358,75 +591,77 @@ export function withForgePluginMention(prompt: string): string {
|
|
|
358
591
|
return `${DEFAULT_CHATGPT_AUTOMATION_PLUGIN_MENTION} ${value}`;
|
|
359
592
|
}
|
|
360
593
|
|
|
361
|
-
|
|
594
|
+
/**
|
|
595
|
+
* Dispatches a bounded standalone prompt through the same controller-owned
|
|
596
|
+
* ChatGPT browser path without creating or requiring a WorkContract. The scope
|
|
597
|
+
* id is only a stable browser correlation key (for example a Schedule id).
|
|
598
|
+
*/
|
|
599
|
+
export async function runStandaloneChatgptPrompt(input: StandaloneChatgptPromptInput): Promise<WorkChatgptContinuationResult> {
|
|
362
600
|
const model = normalizeModel(input.model);
|
|
363
601
|
const reasoning = normalizeReasoning(input.reasoning);
|
|
364
602
|
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;
|
|
603
|
+
const sessionId = resolveStandaloneChatgptBrowserSessionId(input);
|
|
604
|
+
const browserScopeId = `standalone:${input.scopeId}`;
|
|
605
|
+
const seedUrl = input.conversationUrl?.trim();
|
|
606
|
+
|
|
374
607
|
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(
|
|
608
|
+
await ensureControllerChatgptBrowser(input.controllerHome, browserScopeId);
|
|
609
|
+
const targetUrl = seedUrl ?? 'https://chatgpt.com/';
|
|
610
|
+
const navigation = await navigateWorkConversation(
|
|
399
611
|
input.controllerHome,
|
|
400
|
-
|
|
612
|
+
browserScopeId,
|
|
401
613
|
sessionId,
|
|
614
|
+
targetUrl,
|
|
615
|
+
input.timeoutMs,
|
|
616
|
+
);
|
|
617
|
+
const executionPreferenceVerified = await ensureChatgptExecutionPreference(
|
|
618
|
+
input.controllerHome,
|
|
619
|
+
browserScopeId,
|
|
620
|
+
navigation.browserSessionId,
|
|
402
621
|
model,
|
|
403
622
|
reasoning,
|
|
404
623
|
input.timeoutMs,
|
|
405
624
|
);
|
|
406
|
-
const observedUrl = await submitChatgptPrompt(
|
|
625
|
+
const observedUrl = await submitChatgptPrompt(
|
|
626
|
+
input.controllerHome,
|
|
627
|
+
browserScopeId,
|
|
628
|
+
navigation.browserSessionId,
|
|
629
|
+
input.prompt,
|
|
630
|
+
navigation.submissionTargetUrl,
|
|
631
|
+
input.timeoutMs,
|
|
632
|
+
);
|
|
633
|
+
const tabCleanup = await closeChatgptAutomationTabAfterDispatch(
|
|
634
|
+
input.controllerHome,
|
|
635
|
+
browserScopeId,
|
|
636
|
+
navigation.browserSessionId,
|
|
637
|
+
input.timeoutMs,
|
|
638
|
+
);
|
|
407
639
|
return {
|
|
408
640
|
status: 'dispatched',
|
|
409
641
|
provider: 'controller-browser',
|
|
410
|
-
browserSessionId:
|
|
642
|
+
browserSessionId: navigation.browserSessionId,
|
|
411
643
|
conversationUrl: observedUrl,
|
|
644
|
+
resumedFromBinding: false,
|
|
412
645
|
model,
|
|
413
646
|
reasoning,
|
|
414
647
|
tabPolicy,
|
|
415
648
|
executionPreferenceVerified,
|
|
416
|
-
|
|
649
|
+
tabCleanupStatus: tabCleanup.status,
|
|
650
|
+
...(tabCleanup.error ? { tabCleanupError: tabCleanup.error } : {}),
|
|
417
651
|
};
|
|
418
652
|
} catch (error) {
|
|
419
653
|
return {
|
|
420
654
|
status: 'failed',
|
|
421
655
|
provider: 'controller-browser',
|
|
422
656
|
browserSessionId: sessionId,
|
|
657
|
+
conversationUrl: seedUrl,
|
|
658
|
+
resumedFromBinding: false,
|
|
423
659
|
model,
|
|
424
660
|
reasoning,
|
|
425
661
|
tabPolicy,
|
|
426
662
|
executionPreferenceVerified: false,
|
|
427
|
-
reusedSession,
|
|
428
663
|
error: {
|
|
429
|
-
code: error instanceof Error && error.message.includes(':') ? error.message.split(':', 1)[0] : '
|
|
664
|
+
code: error instanceof Error && error.message.includes(':') ? error.message.split(':', 1)[0] : 'CHATGPT_CONTROLLER_BROWSER_FAILED',
|
|
430
665
|
message: error instanceof Error ? error.message : String(error),
|
|
431
666
|
},
|
|
432
667
|
};
|
|
@@ -459,7 +694,7 @@ export async function runWorkChatgptContinuation(input: WorkChatgptContinuationI
|
|
|
459
694
|
throw new Error(`CHATGPT_WORK_CONTRACT_NOT_FOUND: ${input.repoId}:${input.workId}`);
|
|
460
695
|
}
|
|
461
696
|
await ensureControllerChatgptBrowser(input.controllerHome, input.workId);
|
|
462
|
-
if (seedUrl && !binding) {
|
|
697
|
+
if (seedUrl && !binding && hasChatgptConversationIdentity(seedUrl)) {
|
|
463
698
|
binding = bindChatgptWorkConversation(store, {
|
|
464
699
|
workId: input.workId,
|
|
465
700
|
conversationUrl: seedUrl,
|
|
@@ -468,34 +703,47 @@ export async function runWorkChatgptContinuation(input: WorkChatgptContinuationI
|
|
|
468
703
|
});
|
|
469
704
|
}
|
|
470
705
|
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(
|
|
706
|
+
const navigation = await navigateWorkConversation(
|
|
479
707
|
input.controllerHome,
|
|
480
708
|
input.workId,
|
|
481
709
|
sessionId,
|
|
710
|
+
targetUrl,
|
|
711
|
+
input.timeoutMs,
|
|
712
|
+
);
|
|
713
|
+
const executionPreferenceVerified = await ensureChatgptExecutionPreference(
|
|
714
|
+
input.controllerHome,
|
|
715
|
+
input.workId,
|
|
716
|
+
navigation.browserSessionId,
|
|
482
717
|
model,
|
|
483
718
|
reasoning,
|
|
484
719
|
input.timeoutMs,
|
|
485
720
|
);
|
|
486
|
-
const observedUrl = await submitChatgptPrompt(input.controllerHome, input.workId,
|
|
721
|
+
const observedUrl = await submitChatgptPrompt(input.controllerHome, input.workId, navigation.browserSessionId, `${workflowToolAttributionInstruction(input.workId)}\n\n${input.prompt}`, navigation.submissionTargetUrl, input.timeoutMs);
|
|
487
722
|
if (/\/c\/[^/?#]+/.test(observedUrl)) {
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
723
|
+
const observedIdentity = parseChatgptConversationIdentity(observedUrl);
|
|
724
|
+
binding = binding && binding.conversationId !== observedIdentity.conversationId
|
|
725
|
+
? rebindChatgptWorkConversation(store, {
|
|
726
|
+
workId: input.workId,
|
|
727
|
+
previousConversationId: binding.conversationId,
|
|
728
|
+
conversationUrl: observedUrl,
|
|
729
|
+
latestBrowserSessionId: navigation.browserSessionId,
|
|
730
|
+
localAlias: binding.localAlias ?? input.title,
|
|
731
|
+
})
|
|
732
|
+
: bindChatgptWorkConversation(store, {
|
|
733
|
+
workId: input.workId,
|
|
734
|
+
conversationUrl: observedUrl,
|
|
735
|
+
latestBrowserSessionId: navigation.browserSessionId,
|
|
736
|
+
localAlias: binding?.localAlias ?? input.title,
|
|
737
|
+
});
|
|
494
738
|
}
|
|
739
|
+
// Work-bound dispatch keeps the Forge-owned automation tab/session alive for
|
|
740
|
+
// the launched semantic Controller round. The exact Controller release path
|
|
741
|
+
// settles it after an explicit disposition. Closing here would race the new
|
|
742
|
+
// ChatGPT round and make dispatch success look like round completion.
|
|
495
743
|
return {
|
|
496
744
|
status: 'dispatched',
|
|
497
745
|
provider: 'controller-browser',
|
|
498
|
-
browserSessionId:
|
|
746
|
+
browserSessionId: navigation.browserSessionId,
|
|
499
747
|
conversationUrl: binding?.conversationUrl ?? observedUrl,
|
|
500
748
|
conversationId: binding?.conversationId,
|
|
501
749
|
localAlias: binding?.localAlias,
|