@moretea-labs/forge 1.8.0 → 1.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +73 -7
- package/CHANGELOG.md +34 -0
- package/CLAUDE.md +17 -2
- package/CONTRIBUTING.md +3 -2
- package/README.en.md +24 -3
- package/README.md +10 -6
- package/README.zh-CN.md +5 -3
- package/THIRD_PARTY_NOTICES.md +1 -2
- package/adapters/chatgpt/browser-delivery-host.ts +7 -1
- package/adapters/chatgpt/browser-delivery-runtime.ts +531 -56
- package/adapters/chatgpt/controller-binding-store.ts +101 -22
- package/adapters/chatgpt/controller-round-host.ts +15 -25
- package/adapters/chatgpt/controller-round-settlement-store.ts +58 -4
- package/adapters/chatgpt/provider-delivery.ts +229 -2
- package/adapters/chatgpt/work-conversation-binding-store.ts +99 -54
- package/adapters/computer/chatgpt-extension-target.ts +708 -0
- package/adapters/computer/chrome-extension/background.js +541 -0
- package/adapters/computer/chrome-extension/content.js +402 -0
- package/adapters/computer/chrome-extension/core.js +24 -0
- package/adapters/computer/chrome-extension/manifest.json +32 -0
- package/adapters/computer/desktop-operator-discovery.ts +11 -31
- package/adapters/computer/desktop-operator-provider.ts +18 -6
- package/adapters/computer/interaction-target-authority.ts +178 -24
- package/adapters/mcp/access-mode.ts +18 -39
- package/adapters/mcp/auth.ts +2 -7
- package/adapters/mcp/instructions.ts +6 -5
- package/adapters/mcp/multi-repository.ts +10 -47
- package/adapters/mcp/oauth.ts +73 -0
- package/adapters/mcp/paths.ts +4 -1
- package/adapters/mcp/runtime-gateway/bounded-result-runtime.ts +62 -0
- package/adapters/mcp/runtime-gateway/cognition-settlement.ts +201 -0
- package/adapters/mcp/runtime-gateway/context-adapter.ts +490 -113
- package/adapters/mcp/runtime-gateway/context-pack-sidecar.ts +83 -0
- package/adapters/mcp/runtime-gateway/controller-authority-adapter.ts +20 -5
- package/adapters/mcp/runtime-gateway/core-capability-adapter.ts +258 -0
- package/adapters/mcp/runtime-gateway/engineering-preconditions.ts +10 -27
- package/adapters/mcp/runtime-gateway/engineering-tool-contract.ts +15 -0
- package/adapters/mcp/runtime-gateway/execution-tools.ts +14 -54
- package/adapters/mcp/runtime-gateway/gateway-execution-adapter.ts +2 -1
- package/adapters/mcp/runtime-gateway/plugin-adapter.ts +132 -47
- package/adapters/mcp/runtime-gateway/process-tools.ts +173 -23
- package/adapters/mcp/runtime-gateway/protected-computer-adapter.ts +16 -4
- package/adapters/mcp/runtime-gateway/recovery-adapter.ts +69 -52
- package/adapters/mcp/runtime-gateway/repository-compat-adapter.ts +4 -35
- package/adapters/mcp/runtime-gateway/result-adapter.ts +10 -4
- package/adapters/mcp/runtime-gateway/router.ts +19 -507
- package/adapters/mcp/runtime-gateway/routing-policy.ts +506 -0
- package/adapters/mcp/runtime-gateway/runtime-observation-adapter.ts +15 -66
- package/adapters/mcp/runtime-gateway/runtime-readiness-observation.ts +218 -0
- package/adapters/mcp/runtime-gateway/runtime-tool-definitions.ts +123 -178
- package/adapters/mcp/runtime-gateway/runtime-tools.ts +11 -5
- package/adapters/mcp/runtime-gateway/scheduler-adapter.ts +37 -1
- package/adapters/mcp/runtime-gateway/shared-adapter.ts +20 -0
- package/adapters/mcp/runtime-gateway/status-inbox-adapter.ts +416 -270
- package/adapters/mcp/runtime-gateway/typescript-navigation-sidecar.ts +140 -0
- package/adapters/mcp/runtime-gateway/work-adapter.ts +262 -3123
- package/adapters/mcp/runtime-gateway/work-controller-operations.ts +257 -0
- package/adapters/mcp/runtime-gateway/work-detail-projection.ts +60 -0
- package/adapters/mcp/runtime-gateway/work-input-wire-migration.ts +229 -0
- package/adapters/mcp/runtime-gateway/work-learning-operations.ts +145 -0
- package/adapters/mcp/runtime-gateway/work-list-projection.ts +38 -0
- package/adapters/mcp/runtime-gateway/work-plan-operations.ts +382 -0
- package/adapters/mcp/runtime-gateway/work-repair-adapter.ts +308 -0
- package/adapters/mcp/runtime-gateway/work-requirement-operations.ts +148 -0
- package/adapters/mcp/runtime-gateway/work-semantic-operations.ts +320 -0
- package/adapters/mcp/runtime-gateway/work-workflow-operations.ts +113 -0
- package/adapters/mcp/runtime-gateway/workflow-supervisor-adapter.ts +180 -0
- package/adapters/mcp/server.ts +91 -26
- package/adapters/mcp/setup.ts +5 -13
- package/adapters/mcp/tool-mapping/access-tools.ts +0 -6
- package/adapters/mcp/tool-mapping/legacy-tool-service.ts +703 -3092
- package/adapters/mcp/tool-mapping/post-finalize-work-attribution.ts +5 -10
- package/adapters/mcp/tool-mapping/repository-tools.ts +170 -213
- package/adapters/mcp/toolset-names.ts +11 -37
- package/adapters/mcp/toolset.ts +19 -92
- package/{src/cli → adapters}/mcp/transports/AGENTS.md +5 -5
- package/{src/cli → adapters}/mcp/transports/CLAUDE.md +5 -5
- package/adapters/mcp/transports/http-observation.ts +377 -0
- package/adapters/mcp/transports/http-post-lifecycle.ts +19 -0
- package/adapters/mcp/transports/http.ts +200 -683
- package/adapters/mcp/transports/oauth-http.ts +322 -0
- package/adapters/mcp/transports/session-registry.ts +6 -0
- package/adapters/mcp/tunnels/openai-secure-tunnel.ts +89 -5
- package/adapters/mcp/types.ts +0 -6
- package/assets/hooks/lib/session-state.sh +0 -38
- package/assets/hooks/post-bash.sh +1 -1
- package/assets/hooks/post-tool-observer.sh +0 -5
- package/assets/hooks/prompt-guard.sh +0 -55
- package/assets/partials/04-project-structure.partial.md +1 -1
- package/assets/partials/07-footer.partial.md +1 -1
- package/assets/partials-agents/02-operating-mode.partial.md +1 -1
- package/assets/partials-agents/08-deep-docs.partial.md +1 -1
- package/assets/plugin-registry.v1.json +6 -4
- package/assets/reference-configs/agentic-development-flow.md +26 -1
- package/assets/reference-configs/external-tooling.md +0 -76
- package/assets/reference-configs/global-working-rules.md +19 -0
- package/assets/reference-configs/release-deploy.md +1 -1
- package/assets/templates/helpers/architecture-queue.sh +1 -1
- package/assets/templates/helpers/check-agent-tooling.sh +1 -327
- package/assets/templates/helpers/check-task-workflow.sh +1 -1
- package/assets/templates/helpers/ensure-task-workflow.sh +0 -14
- package/assets/templates/helpers/migrate-project-template.sh +2 -2
- package/assets/workflow-contract.v1.json +0 -2
- package/docs/README.md +5 -2
- package/docs/architecture/CURRENT.md +205 -51
- package/docs/architecture/decisions/20260803-release-identity-binding-and-exit-policy.md +14 -5
- package/docs/architecture/decisions/20260823-immediate-controller-relay.md +6 -3
- package/docs/architecture/decisions/20260904-ios-device-migration-baseline.md +42 -0
- package/docs/architecture/decisions/20260907-assistant-learning-loop.md +2 -0
- package/docs/architecture/decisions/20260907-controller-round-transition-policy.md +19 -4
- package/docs/architecture/decisions/20260916-workflow-supervisor-authority.md +249 -0
- package/docs/architecture/decisions/20260917-cognitive-memory-foundation.md +82 -0
- package/docs/architecture/decisions/20260918-stable-candidate-release-lifecycle.md +234 -0
- package/docs/architecture/decisions/20260919-autonomous-work-liveness.md +62 -0
- package/docs/architecture/decisions/20260924-thin-semantic-working-context.md +74 -0
- package/docs/architecture/decisions/20261001-supervisor-turn-authority.md +342 -0
- package/docs/architecture/decisions/20261005-forge-v3-desktop-client-boundary.md +77 -0
- package/docs/architecture/decisions/20261009-exact-code-context-discovery.md +19 -0
- package/docs/architecture/decisions/20261009-generated-xcode-cache-retention.md +37 -0
- package/docs/architecture/decisions/20261011-autonomous-continuation-convergence.md +597 -0
- package/docs/architecture/decisions/20261017-edit-session-finalization-reconciliation.md +17 -0
- package/docs/architecture/decisions/20261019-retire-host-skills.md +16 -0
- package/docs/architecture/diagrams/agentic-dev-plugin-architecture.html +1 -1
- package/docs/architecture/domains/public-surface.md +3 -5
- package/docs/architecture/domains/verification.md +6 -5
- package/docs/architecture/domains/workflow-engine.md +6 -0
- package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +3 -3
- package/docs/architecture/requests/2026-09-04-computer-plugin-workflow-taxonomy.md +4 -4
- package/docs/architecture/reviews/20261015-code-search-runtime-acceptance.md +75 -0
- package/docs/forge-plugin-management.md +16 -4
- package/docs/operations/controller-browser-plugin.md +23 -0
- package/docs/operations/controller-performance-and-502.md +17 -0
- package/docs/operations/controller-reliability-runbook.md +2 -2
- package/docs/operations/features.md +6 -7
- package/docs/operations/features.zh-CN.md +1 -1
- package/docs/operations/github-repository.md +4 -4
- package/docs/operations/github-semantic-release-projection.md +20 -0
- package/docs/operations/homebrew.md +2 -2
- package/docs/operations/mcp-tool-exposure.md +13 -52
- package/docs/operations/releasing.md +2 -0
- package/docs/operations/runtime-performance-diagnostics.md +8 -0
- package/docs/operations/standalone-disaster-recovery.md +15 -7
- package/docs/tutorials/01-install-and-start.zh-CN.md +1 -1
- package/docs/tutorials/03-first-repository-task.md +2 -0
- package/docs/wiki/Computer-and-Plugins.md +78 -0
- package/docs/wiki/Computer-and-Plugins.zh-CN.md +88 -0
- package/docs/wiki/Home.md +16 -2
- package/docs/wiki/Implementation.md +25 -13
- package/docs/wiki/Installation.md +28 -32
- package/docs/wiki/Integrations.md +4 -0
- package/docs/wiki/Quick-Start.md +35 -22
- package/docs/wiki/Releases-and-Upgrades.md +15 -25
- package/docs/wiki/_Sidebar.md +5 -1
- package/package.json +25 -25
- package/packages/kernel/cognition/api/index.ts +6 -0
- package/packages/kernel/cognition/application/activation-service.ts +451 -0
- package/packages/kernel/cognition/application/consolidation-service.ts +185 -0
- package/packages/kernel/cognition/application/legacy-experience-adapter.ts +38 -0
- package/packages/kernel/cognition/application/memory-service.ts +64 -0
- package/packages/kernel/cognition/domain/learning.ts +106 -0
- package/packages/kernel/cognition/domain/memory.ts +254 -0
- package/packages/kernel/controller/api/index.ts +1 -0
- package/packages/kernel/controller/application/continuation-service.ts +281 -0
- package/packages/kernel/controller/application/controller-round-service.ts +12 -0
- package/packages/kernel/controller/domain/controller-round-transition-policy.ts +242 -21
- package/packages/kernel/controller/domain/controller-round.ts +4 -1
- package/packages/kernel/controller/domain/execution-quality.ts +3 -1
- package/packages/kernel/controller/infrastructure/controller-round-store.ts +689 -103
- package/packages/kernel/controller/infrastructure/controller-session-store.ts +348 -97
- package/packages/kernel/controller/ports/controller-host.ts +2 -0
- package/packages/kernel/identity/api/index.ts +10 -0
- package/packages/kernel/identity/domain/activity-projection.ts +43 -0
- package/packages/kernel/identity/domain/capability-broker.ts +60 -0
- package/packages/kernel/identity/domain/grant.ts +72 -0
- package/packages/kernel/identity/domain/owned-resource.ts +116 -0
- package/packages/kernel/identity/domain/user-request.ts +93 -0
- package/packages/kernel/identity/infrastructure/activity-projection-store.ts +87 -0
- package/packages/kernel/identity/infrastructure/capability-broker.ts +81 -0
- package/packages/kernel/identity/infrastructure/grant-store.ts +284 -0
- package/packages/kernel/identity/infrastructure/owned-resource-store.ts +259 -0
- package/packages/kernel/identity/infrastructure/user-request-store.ts +182 -0
- package/packages/kernel/scheduler/api/index.ts +0 -2
- package/packages/kernel/scheduler/application/eligibility.ts +4 -1
- package/packages/kernel/scheduler/application/schedule-service.ts +3 -0
- package/packages/kernel/scheduler/application/settlement.ts +0 -15
- package/packages/kernel/scheduler/domain/schedule.ts +87 -0
- package/packages/kernel/scheduler/infrastructure/schedule-store.ts +166 -143
- package/packages/kernel/work/api/index.ts +3 -2
- package/packages/kernel/work/application/work-service.ts +18 -42
- package/packages/kernel/work/domain/current-task-lineage.ts +104 -0
- package/packages/kernel/work/domain/engineering-design.ts +10 -5
- package/packages/kernel/work/domain/engineering-profile.ts +8 -12
- package/packages/kernel/work/domain/evaluation-promotion-receipt.ts +186 -0
- package/packages/kernel/work/domain/execution-concurrency.ts +19 -26
- package/packages/kernel/work/domain/execution-snapshot.ts +0 -31
- package/packages/kernel/work/domain/implementation-review.ts +14 -331
- package/packages/kernel/work/domain/objective-graph.ts +119 -0
- package/packages/kernel/work/domain/project-engineering-contract.ts +23 -1
- package/packages/kernel/work/domain/repository-completion-receipt.ts +1 -1
- package/packages/kernel/work/domain/state-machine.ts +34 -65
- package/packages/kernel/work/domain/types.ts +57 -51
- package/packages/kernel/work/infrastructure/work-contract-store.ts +847 -729
- package/packages/kernel/work/ports/work-contract-store.ts +5 -2
- package/packages/plugin-runtime/browser/provider-registry.ts +12 -36
- package/packages/plugin-runtime/browser/session-authority.ts +2 -1
- package/packages/plugin-runtime/computer/chatgpt-identity.ts +24 -0
- package/packages/plugin-runtime/computer/conversation-target.ts +153 -0
- package/packages/plugin-runtime/computer/index.ts +2 -0
- package/packages/plugin-runtime/computer/target-authority.ts +26 -1
- package/packages/plugin-runtime/external/unix-jsonl-transport.ts +142 -18
- package/packages/protocols/computer/contract.ts +27 -1
- package/scripts/activate-source-baseline.ts +7 -9
- package/scripts/assemble-template.ts +11 -72
- package/scripts/canary-controller-semantic-wait.ts +12 -24
- package/scripts/check-mcp-compatibility.ts +94 -201
- package/scripts/check-package-identity.mjs +0 -5
- package/scripts/check-runtime-architecture.mjs +921 -239
- package/scripts/forge-local-recovery-helper.mjs +93 -15
- package/scripts/init-project.sh +0 -46
- package/scripts/install-ios-hid-toolchain.sh +60 -0
- package/scripts/install-standalone-recovery.ts +1 -1
- package/scripts/lib/project-init-lib.sh +6 -3
- package/scripts/migrate-windows-wsl-controller-home.ts +2 -0
- package/scripts/open-source-audit-allowlist.txt +1 -3
- package/scripts/run-bounded-command.mjs +5 -0
- package/scripts/run-governed-gate.ts +2 -1
- package/scripts/run-mobile-monitor-bridge.ts +0 -1
- package/scripts/smoke-control-plane-failure-domain.ts +94 -0
- package/scripts/smoke-ios-physical-device.ts +38 -10
- package/scripts/smoke-mcp-http-runtime.ts +12 -8
- package/scripts/smoke-mcp-tool-surface.ts +27 -41
- package/scripts/smoke-runtime-recovery.ts +12 -43
- package/scripts/smoke-schedule-engine.ts +23 -15
- package/scripts/smoke-unattended-transport-recovery.ts +223 -0
- package/scripts/smoke-user-request-continuation-restart.ts +232 -0
- package/scripts/smoke-workflow-supervisor-chrome-adapter.ts +95 -0
- package/scripts/smoke-workflow-supervisor-process-restart.ts +276 -0
- package/scripts/smoke-workflow-supervisor-restart-recovery.ts +150 -0
- package/scripts/smoke-workflow-supervisor.ts +103 -0
- package/scripts/sync-generated-authority.mjs +0 -1
- package/scripts/verify-forge-runtime.sh +2 -1
- package/src/cli/agent-jobs/executor-health.ts +12 -35
- package/src/cli/agent-jobs/integration.ts +20 -51
- package/src/cli/agent-jobs/job-manager.ts +7 -3
- package/src/cli/chatgpt-browser/file-policy.ts +2 -2
- package/src/cli/commands/brain-assistant.ts +27 -13
- package/src/cli/commands/brain.ts +13 -15
- package/src/cli/commands/chatgpt.ts +1 -163
- package/src/cli/commands/doctor.ts +1 -143
- package/src/cli/commands/global-runtime.ts +3 -196
- package/src/cli/commands/init-hook.ts +6 -23
- package/src/cli/commands/init.ts +3 -252
- package/src/cli/commands/mcp.ts +0 -5
- package/src/cli/commands/prompt-guard-decision.ts +0 -2
- package/src/cli/commands/recovery.ts +137 -135
- package/src/cli/commands/repository.ts +17 -1
- package/src/cli/commands/runtime.ts +3 -3
- package/src/cli/commands/setup-execution.ts +41 -0
- package/src/cli/commands/setup-profile.ts +8 -0
- package/src/cli/controller/check-runner.ts +13 -2
- package/src/cli/controller/completion-backlog.ts +3 -3
- package/src/cli/controller/completion-orchestrator.ts +5 -44
- package/src/cli/controller/completion-target.ts +3 -72
- package/src/cli/controller/composite-operations.ts +1 -1
- package/src/cli/controller/context/instruction-context.ts +1 -1
- package/src/cli/controller/context/known-paths.ts +2 -2
- package/src/cli/controller/context/query-planning.ts +11 -4
- package/src/cli/controller/context/rule-resolution.ts +2 -2
- package/src/cli/controller/context/source-materializer.ts +83 -102
- package/src/cli/controller/context/structural-provider.ts +84 -0
- package/src/cli/controller/context/structural.ts +6 -6
- package/src/cli/controller/context/types.ts +8 -9
- package/src/cli/controller/context-pack.ts +107 -84
- package/src/cli/controller/execution-completion.ts +4 -7
- package/src/cli/controller/execution-policy.ts +36 -153
- package/src/cli/controller/governance.ts +2 -3
- package/src/cli/controller/issue-store.ts +4 -99
- package/src/cli/controller/launch-agents.ts +59 -18
- package/src/cli/controller/operational-plan.ts +25 -52
- package/src/cli/controller/runtime-config.ts +5 -1
- package/src/cli/controller/stuck-state-migration.ts +2 -3
- package/src/cli/controller/systemd-user.ts +42 -10
- package/src/cli/editing/edit-session.ts +18 -12
- package/src/cli/github/github.ts +4 -4
- package/src/cli/github/semantic-projection.ts +174 -0
- package/src/cli/hook/prompt-intents.ts +0 -9
- package/src/cli/index.ts +1 -43
- package/src/cli/local-bridge/connector-freshness.ts +1 -12
- package/src/cli/local-bridge/console-automations.ts +19 -14
- package/src/cli/local-bridge/console-view-models.ts +4 -13
- package/src/cli/local-bridge/facade-api.ts +64 -193
- package/src/cli/local-bridge/server.ts +24 -147
- package/src/cli/local-bridge/types.ts +0 -1
- package/src/cli/mcp/oauth-client-retirement.ts +74 -0
- package/src/cli/repositories/command-classifier.ts +0 -9
- package/src/cli/repositories/command-executor.ts +29 -23
- package/src/cli/repositories/command-scope.ts +37 -18
- package/src/cli/repositories/controller-home.ts +48 -0
- package/src/cli/repositories/ephemeral-workspace.ts +20 -30
- package/src/cli/repositories/git-command-executor.ts +2 -2
- package/src/cli/repositories/locks.ts +20 -0
- package/src/cli/repositories/registry.ts +79 -12
- package/src/cli/repositories/safe-patch.ts +2 -12
- package/src/cli/repositories/structured-git.ts +101 -5
- package/src/cli/repository/inspector.ts +185 -343
- package/src/cli/repository/session-cache.ts +92 -6
- package/src/effects/process-runner.ts +17 -1
- package/src/runtime/context/assistant-context.ts +20 -7
- package/src/runtime/context/assistant-work-context.ts +89 -27
- package/src/runtime/context/automatic-learning.ts +565 -0
- package/src/runtime/context/cognitive-attention.ts +173 -0
- package/src/runtime/context/cognitive-skill-canary.ts +158 -0
- package/src/runtime/context/context-closure.ts +46 -9
- package/src/runtime/context/context-pack-process.ts +103 -0
- package/src/runtime/context/context-plane.ts +1 -9
- package/src/runtime/context/direct-controller-learning.ts +298 -0
- package/src/runtime/context/generic-lsp-provider.ts +1 -1
- package/src/runtime/context/semantic-navigation-contract.ts +16 -0
- package/src/runtime/context/semantic-navigation.ts +7 -35
- package/src/runtime/context/typescript-navigation-process.ts +145 -0
- package/src/runtime/context/typescript-navigation.ts +89 -3
- package/src/runtime/control-plane/cleanup-artifact-retention.ts +215 -12
- package/src/runtime/control-plane/concurrency/work-execution-concurrency.ts +9 -47
- package/src/runtime/control-plane/execution/controller-authority-recovery.ts +34 -4
- package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +29 -20
- package/src/runtime/control-plane/execution/execution-identity.ts +23 -3
- package/src/runtime/control-plane/execution/repository-work-attribution.ts +7 -30
- package/src/runtime/control-plane/execution/retained-work-resume.ts +206 -199
- package/src/runtime/control-plane/execution/session-store.ts +31 -1
- package/src/runtime/control-plane/execution/validation.ts +6 -8
- package/src/runtime/control-plane/execution/work-authority-reconciler.ts +13 -42
- package/src/runtime/control-plane/execution/work-check-evidence.ts +137 -0
- package/src/runtime/control-plane/execution/work-completion-authority.ts +30 -85
- package/src/runtime/control-plane/execution/work-delivery-service.ts +616 -0
- package/src/runtime/control-plane/execution/work-evidence-policy.ts +12 -50
- package/src/runtime/control-plane/execution/work-execution-support.ts +50 -149
- package/src/runtime/control-plane/execution/work-handle-authority.ts +197 -110
- package/src/runtime/control-plane/execution/work-handle-store.ts +72 -44
- package/src/runtime/control-plane/execution/work-head-settlement.ts +53 -20
- package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +52 -29
- package/src/runtime/control-plane/execution/work-operation-service.ts +60 -34
- package/src/runtime/control-plane/execution/work-preparation-service.ts +231 -267
- package/src/runtime/control-plane/execution/work-process-evidence.ts +2 -2
- package/src/runtime/control-plane/execution/work-remote-delivery.ts +2 -3
- package/src/runtime/control-plane/execution/work-revision-diff.ts +39 -0
- package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +201 -199
- package/src/runtime/control-plane/execution/work-validation-reconciler.ts +33 -85
- package/src/runtime/control-plane/execution/work-verification-service.ts +406 -50
- package/src/runtime/control-plane/execution/work-verification-snapshot.ts +6 -2
- package/src/runtime/control-plane/facade/capability-registry.ts +364 -37
- package/src/runtime/control-plane/facade/check-normalization.ts +3 -9
- package/src/runtime/control-plane/facade/codex-delegation.ts +10 -11
- package/src/runtime/control-plane/facade/controller-connection.ts +135 -0
- package/src/runtime/control-plane/facade/goal-workloop-access.ts +1 -20
- package/src/runtime/control-plane/facade/goal-workloop.ts +281 -1753
- package/src/runtime/control-plane/facade/handoff-inbox-application.ts +12 -25
- package/src/runtime/control-plane/facade/handoff-inbox-store.ts +256 -52
- package/src/runtime/control-plane/facade/index.ts +0 -1
- package/src/runtime/control-plane/facade/operation-digest.ts +12 -15
- package/src/runtime/control-plane/facade/plan-contract-store.ts +451 -991
- package/src/runtime/control-plane/facade/policy-gate.ts +0 -23
- package/src/runtime/control-plane/facade/repository-work-admission.ts +12 -22
- package/src/runtime/control-plane/facade/requirement-authority.ts +102 -199
- package/src/runtime/control-plane/facade/requirement-board.ts +3 -3
- package/src/runtime/control-plane/facade/rh-work-operation-contract.ts +51 -14
- package/src/runtime/control-plane/facade/self-healing-loop.ts +18 -53
- package/src/runtime/control-plane/facade/suggested-actions.ts +15 -18
- package/src/runtime/control-plane/facade/types.ts +49 -108
- package/src/runtime/control-plane/facade/work-continuation.ts +9 -9
- package/src/runtime/control-plane/generated-cache-retention.ts +206 -59
- package/src/runtime/control-plane/global-scheduler/autonomous-continuation.ts +248 -0
- package/src/runtime/control-plane/global-scheduler/durable-admission.ts +1 -1
- package/src/runtime/control-plane/global-scheduler/maintenance.ts +256 -117
- package/src/runtime/control-plane/global-scheduler/reconciliation.ts +3 -12
- package/src/runtime/control-plane/global-scheduler/scheduler.ts +66 -40
- package/src/runtime/control-plane/global-scheduler/wake-signal.ts +42 -2
- package/src/runtime/control-plane/governance/access-policy.ts +19 -2
- package/src/runtime/control-plane/governance/authorization.ts +20 -3
- package/src/runtime/control-plane/governance/external-effects.ts +1 -1
- package/src/runtime/control-plane/launcher/chatgpt-round-continuation.ts +63 -300
- package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +29 -300
- package/src/runtime/control-plane/launcher/local-conversation-controller.ts +238 -0
- package/src/runtime/control-plane/launcher/provider-mcp-bootstrap.ts +5 -2
- package/src/runtime/control-plane/launcher/thin-launcher.ts +96 -240
- package/src/runtime/control-plane/persistence/cognition-store.ts +820 -0
- package/src/runtime/control-plane/persistence/experience-store.ts +162 -29
- package/src/runtime/control-plane/persistence/requirement-store.ts +216 -74
- package/src/runtime/control-plane/persistence/sqlite-store.ts +113 -6
- package/src/runtime/control-plane/release-retention.ts +135 -47
- package/src/runtime/control-plane/repository-namespace-retention.ts +2 -2
- package/src/runtime/control-plane/runtime-cleanup.ts +295 -107
- package/src/runtime/control-plane/workspace/project-onboarding.ts +80 -0
- package/src/runtime/control-plane/workspace/semantic-sync-service.ts +19 -5
- package/src/runtime/control-plane/workspace/workspace-store.ts +169 -0
- package/src/runtime/diagnostics/incident-repair.ts +313 -146
- package/src/runtime/diagnostics/mcp-timing.ts +114 -0
- package/src/runtime/evidence/artifact-store.ts +158 -40
- package/src/runtime/evidence/evidence-store.ts +112 -8
- package/src/runtime/evidence/output-handle-index.ts +225 -0
- package/src/runtime/evidence/result-store.ts +158 -44
- package/src/runtime/evidence/sensitive-output.ts +22 -2
- package/src/runtime/execution/jobs/child-reference.ts +11 -0
- package/src/runtime/execution/jobs/receipt-recovery.ts +1 -11
- package/src/runtime/execution/jobs/store.ts +3 -4
- package/src/runtime/execution/managed-workspace.ts +57 -0
- package/src/runtime/execution/process-runtime/check-facade.ts +1 -1
- package/src/runtime/execution/process-runtime/check-result-retention.ts +2 -2
- package/src/runtime/execution/process-runtime/check-result.ts +105 -1
- package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +18 -8
- package/src/runtime/execution/process-runtime/command-facade.ts +82 -30
- package/src/runtime/execution/process-runtime/gc.ts +15 -2
- package/src/runtime/execution/process-runtime/handle-index.ts +176 -0
- package/src/runtime/execution/process-runtime/host-command-lane.ts +139 -0
- package/src/runtime/execution/process-runtime/index.ts +3 -0
- package/src/runtime/execution/process-runtime/lightweight-managed.ts +251 -72
- package/src/runtime/execution/process-runtime/persisted-check.ts +3 -3
- package/src/runtime/execution/process-runtime/process-runner-entry.ts +47 -5
- package/src/runtime/execution/process-runtime/process-scope.ts +160 -0
- package/src/runtime/execution/process-runtime/resource-claims.ts +65 -61
- package/src/runtime/execution/process-runtime/runtime.ts +55 -18
- package/src/runtime/execution/process-runtime/store.ts +30 -2
- package/src/runtime/execution/process-runtime/types.ts +6 -1
- package/src/runtime/execution/thin-harness/execution-router.ts +16 -16
- package/src/runtime/execution/thin-harness/fast-executor.ts +2 -2
- package/src/runtime/execution/thin-harness/search-worker.ts +2 -2
- package/src/runtime/execution/workers/executor.ts +3 -8
- package/src/runtime/maintenance/cleanup.ts +41 -5
- package/src/runtime/platform/service-inventory.ts +130 -0
- package/src/runtime/platform/service-manager.ts +8 -0
- package/src/runtime/plugins/action-application.ts +97 -29
- package/src/runtime/plugins/app-store-connect-adapter.ts +290 -18
- package/src/runtime/plugins/browser-adapter.ts +1124 -333
- package/src/runtime/plugins/browser-automation-service.ts +23 -3
- package/src/runtime/plugins/browser-macos-bridge.ts +431 -137
- package/src/runtime/plugins/browser-manifest-surface.ts +47 -12
- package/src/runtime/plugins/browser-native-session-service.ts +63 -13
- package/src/runtime/plugins/browser-registration.ts +18 -0
- package/src/runtime/plugins/browser-runtime.ts +40 -36
- package/src/runtime/plugins/browser-session-authority.ts +2 -2
- package/src/runtime/plugins/browser-session-store.ts +46 -76
- package/src/runtime/plugins/capability-authorization-grants.ts +258 -62
- package/src/runtime/plugins/computer-chatgpt-browser-adapter-installer.ts +228 -0
- package/src/runtime/plugins/computer-registration.ts +298 -20
- package/src/runtime/plugins/desktop-operator-external-policy.ts +36 -4
- package/src/runtime/plugins/desktop-operator-registration.ts +12 -4
- package/src/runtime/plugins/external-adapter.ts +42 -6
- package/src/runtime/plugins/external-registration.ts +21 -1
- package/src/runtime/plugins/github-adapter.ts +30 -3
- package/src/runtime/plugins/gmail-adapter.ts +189 -3
- package/src/runtime/plugins/google-calendar-adapter.ts +2 -2
- package/src/runtime/plugins/google-shared.ts +24 -12
- package/src/runtime/plugins/google-tasks-adapter.ts +2 -2
- package/src/runtime/plugins/ios/remote-xpc-hid.ts +70 -30
- package/src/runtime/plugins/ios-adapter.ts +46 -41
- package/src/runtime/plugins/ios-agent-device.ts +54 -4
- package/src/runtime/plugins/ios-device-ownership.ts +103 -0
- package/src/runtime/plugins/ios-physical-device.ts +497 -79
- package/src/runtime/plugins/lightweight-action.ts +208 -2
- package/src/runtime/plugins/local-system-adapter.ts +12 -100
- package/src/runtime/plugins/managed-process-adapter.ts +12 -5
- package/src/runtime/plugins/plugin-action-sidecar.ts +17 -13
- package/src/runtime/plugins/plugin-management-adapter.ts +1 -1
- package/src/runtime/plugins/store.ts +682 -435
- package/src/runtime/plugins/types.ts +24 -0
- package/src/runtime/plugins/xiaohongshu-publish.ts +16 -0
- package/src/runtime/projections/git-status-sampler.ts +2 -19
- package/src/runtime/projections/invalidation.ts +7 -3
- package/src/runtime/projections/materialized-view.ts +80 -36
- package/src/runtime/recovery/actions.ts +4 -4
- package/src/runtime/recovery/capability-action-application.ts +46 -45
- package/src/runtime/recovery/lifecycle-inventory.ts +30 -40
- package/src/runtime/recovery/maintenance-executor.ts +374 -123
- package/src/runtime/recovery/probe.ts +2 -2
- package/src/runtime/recovery/store.ts +29 -11
- package/src/runtime/release/release-coordinator.ts +312 -0
- package/src/runtime/release/release-session.ts +716 -0
- package/src/runtime/release/release-state-migration.ts +25 -0
- package/src/runtime/resources/leases/store.ts +56 -25
- package/src/runtime/root/computer-composition.ts +191 -6
- package/src/runtime/root/controller-progression-composition.ts +254 -0
- package/src/runtime/root/controller-round-composition.ts +20 -1
- package/src/runtime/root/deployment-topology.ts +106 -0
- package/src/runtime/root/entry.ts +4 -3
- package/src/runtime/root/known-good-recovery.ts +184 -0
- package/src/runtime/root/local-bridge.ts +0 -1
- package/src/runtime/root/mcp-transport.ts +111 -18
- package/src/runtime/root/ownership.ts +261 -9
- package/src/runtime/root/package-connector-service.ts +227 -29
- package/src/runtime/root/package-runtime-release.ts +117 -30
- package/src/runtime/root/package-runtime-service.ts +24 -24
- package/src/runtime/root/release-execution-canary.ts +57 -12
- package/src/runtime/root/release-loader.ts +90 -8
- package/src/runtime/root/release-manifest.ts +80 -40
- package/src/runtime/root/release-materialize.ts +372 -106
- package/src/runtime/root/release-store.ts +395 -36
- package/src/runtime/root/runtime-lane.ts +191 -0
- package/src/runtime/root/runtime.ts +160 -29
- package/src/runtime/root/scheduled-controller-composition.ts +29 -3
- package/src/runtime/root/service-runner.ts +2 -2
- package/src/runtime/root/service.ts +126 -44
- package/src/runtime/root/status.ts +48 -0
- package/src/runtime/root/types.ts +32 -9
- package/src/runtime/root/workflow-supervisor-composition.ts +681 -0
- package/src/runtime/root/workflow-supervisor-runtime.ts +564 -0
- package/src/runtime/root/write-fence.ts +50 -2
- package/src/runtime/safe-tooling/google-credential-store.ts +67 -22
- package/src/runtime/safe-tooling/google-oauth-broker.ts +25 -7
- package/src/runtime/safe-tooling/ios-development.ts +22 -8
- package/src/runtime/safe-tooling/job-summary.ts +1 -1
- package/src/runtime/safe-tooling/workspace-auth.ts +5 -2
- package/src/runtime/shared/atomic-text-file.ts +21 -0
- package/src/runtime/shared/bounded-child-supervisor.ts +9 -1
- package/src/runtime/shared/json-files.ts +8 -3
- package/src/runtime/shared/process-environment.ts +20 -9
- package/src/runtime/shared/response-budget.ts +1 -3
- package/src/runtime/standalone-recovery/controller-home-migration.ts +16 -24
- package/src/runtime/standalone-recovery/core.ts +3792 -417
- package/src/runtime/standalone-recovery/entry.ts +507 -45
- package/src/runtime/standalone-recovery/installer.ts +262 -148
- package/src/runtime/standalone-recovery/mcp-server.ts +29 -193
- package/src/runtime/standalone-recovery/operation-lock.ts +12 -2
- package/src/runtime/standalone-recovery/performance.ts +54 -16
- package/src/runtime/standalone-recovery/release.ts +53 -5
- package/src/runtime/standalone-recovery/service-labels.ts +5 -2
- package/src/runtime/standalone-recovery/watchdog-heartbeat.ts +5 -2
- package/src/runtime/workflow/schedules/browser-probe.ts +17 -6
- package/src/runtime/workflow/schedules/chatgpt-automation-migration.ts +11 -3
- package/src/runtime/workflow/schedules/engine.ts +166 -178
- package/src/runtime/workflow/schedules/pending-activation.ts +11 -4
- package/src/runtime/workflow/schedules/work-continuation.ts +95 -11
- package/src/runtime/workflows/runtime.ts +15 -19
- package/src/testing/test-governance.ts +5 -1
- package/supervisor/chatgpt-conversation.ts +23 -0
- package/supervisor/client.ts +218 -0
- package/supervisor/control-plane.ts +963 -0
- package/supervisor/entry.ts +15 -0
- package/supervisor/forge-validators.ts +147 -0
- package/supervisor/index.ts +5 -0
- package/supervisor/native-browser-adapter.ts +1151 -0
- package/supervisor/native-messaging/host.ts +58 -0
- package/supervisor/native-messaging/manifest.ts +26 -0
- package/supervisor/native-messaging/protocol.ts +27 -0
- package/supervisor/paths.ts +18 -0
- package/supervisor/protocol.ts +250 -0
- package/supervisor/server.ts +412 -0
- package/supervisor/store.ts +1870 -0
- package/supervisor/tsconfig.json +8 -0
- package/supervisor/types.ts +261 -0
- package/SKILL.md +0 -427
- package/adapters/chatgpt/controller-host.ts +0 -119
- package/adapters/mcp/controller-round-compatibility.ts +0 -184
- package/adapters/mcp/frozen-client-semantic-compatibility.ts +0 -292
- package/adapters/mcp/runtime-gateway/work-compat-adapter.ts +0 -502
- package/adapters/mcp/runtime-gateway/work-validation-reconciler.ts +0 -1
- package/adapters/mcp/toolset-selection.ts +0 -41
- package/agents/openai.yaml +0 -4
- package/assets/skill-hooks.json +0 -41
- package/assets/skill-version.json +0 -189
- package/assets/skills/claude-review/SKILL.md +0 -231
- package/assets/skills/codex-review/SKILL.md +0 -103
- package/assets/templates/helpers/check-skill-version.ts +0 -193
- package/packages/kernel/progression/api/index.ts +0 -2
- package/packages/kernel/progression/application/projector.ts +0 -211
- package/packages/kernel/progression/domain/types.ts +0 -116
- package/packages/kernel/progression/index.ts +0 -1
- package/packages/kernel/scheduler/application/continuation-service.ts +0 -212
- package/packages/kernel/scheduler/domain/continuation.ts +0 -38
- package/packages/kernel/scheduler/infrastructure/continuation-dispatch-store.ts +0 -38
- package/packages/kernel/work/domain/admission-policy.ts +0 -34
- package/scripts/benchmark-background-check-overlap.ts +0 -228
- package/scripts/benchmark-computer-hotpath.ts +0 -255
- package/scripts/benchmark-controller-hotpath.ts +0 -212
- package/scripts/benchmark-controller-round2.ts +0 -191
- package/scripts/benchmark-ios-agent-device.ts +0 -511
- package/scripts/benchmark-mcp-host.sh +0 -117
- package/scripts/benchmark-mcp-transport.ts +0 -169
- package/scripts/benchmark-operational-memory-activation.ts +0 -235
- package/scripts/benchmark-operational-shadow.ts +0 -28
- package/scripts/benchmark-quality-harness.ts +0 -146
- package/scripts/benchmark-route-session-concurrency.ts +0 -915
- package/scripts/benchmark-thin-harness-gateway-ab.ts +0 -632
- package/scripts/benchmark-thin-harness-mcp-e2e.ts +0 -498
- package/scripts/benchmark-thin-harness.ts +0 -251
- package/scripts/benchmark-typescript-navigation.ts +0 -186
- package/scripts/benchmark-work-efficiency.ts +0 -334
- package/scripts/build-controller-ui.ts +0 -28
- package/scripts/check-npm-release.sh +0 -32
- package/scripts/check-public-release-surface.sh +0 -19
- package/scripts/check-release-published.sh +0 -57
- package/scripts/check-release-readiness.sh +0 -37
- package/scripts/check-release-version.mjs +0 -74
- package/scripts/check-skill-version.ts +0 -5
- package/scripts/check-tarball-install-smoke.sh +0 -150
- package/scripts/ensure-codegraph.sh +0 -16
- package/scripts/package-source-archive.sh +0 -147
- package/scripts/public-release-files.txt +0 -39
- package/scripts/public-release.ts +0 -174
- package/scripts/publish-release-tarball.sh +0 -27
- package/scripts/route-nl-vs-ts-eval.ts +0 -679
- package/scripts/run-skill-hook.ts +0 -244
- package/scripts/stage-runtime-release.ts +0 -44
- package/scripts/sync-codex-installed-copies.sh +0 -182
- package/src/cli/commands/tools.ts +0 -167
- package/src/cli/controller/progress.ts +0 -656
- package/src/cli/controller/work-mode.ts +0 -181
- package/src/cli/local-bridge/console-assets.ts +0 -17
- package/src/cli/local-bridge/dashboard.ts +0 -17
- package/src/cli/local-bridge/frontend/src/App.tsx +0 -10
- package/src/cli/local-bridge/frontend/src/components/Button.tsx +0 -1
- package/src/cli/local-bridge/frontend/src/components/DetailPane.tsx +0 -3
- package/src/cli/local-bridge/frontend/src/components/SearchField.tsx +0 -1
- package/src/cli/local-bridge/frontend/src/components/SectionHeader.tsx +0 -1
- package/src/cli/local-bridge/frontend/src/components/Segmented.tsx +0 -1
- package/src/cli/local-bridge/frontend/src/components/StatusText.tsx +0 -3
- package/src/cli/local-bridge/frontend/src/design/icons.tsx +0 -11
- package/src/cli/local-bridge/frontend/src/design/tokens.css +0 -8
- package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +0 -153
- package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +0 -37
- package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +0 -183
- package/src/cli/local-bridge/frontend/src/features/repositories/RepositoriesView.tsx +0 -2
- package/src/cli/local-bridge/frontend/src/features/system/SystemView.tsx +0 -2
- package/src/cli/local-bridge/frontend/src/features/work/WorkView.tsx +0 -4
- package/src/cli/local-bridge/frontend/src/lib/api.ts +0 -30
- package/src/cli/local-bridge/frontend/src/lib/format.ts +0 -4
- package/src/cli/local-bridge/frontend/src/lib/routes.ts +0 -7
- package/src/cli/local-bridge/frontend/src/main.tsx +0 -1
- package/src/cli/local-bridge/frontend/src/shell/AppShell.tsx +0 -1
- package/src/cli/local-bridge/frontend/src/shell/CommandBar.tsx +0 -2
- package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +0 -4
- package/src/cli/local-bridge/frontend/src/styles.css +0 -28
- package/src/cli/local-bridge/frontend/src/types.ts +0 -15
- package/src/cli/local-bridge/frontend/src/vite-env.d.ts +0 -1
- package/src/cli/local-bridge/frontend/vite.config.ts +0 -27
- package/src/cli/local-bridge/ui-dist/app.css +0 -1
- package/src/cli/local-bridge/ui-dist/app.js +0 -11228
- package/src/cli/mcp/access-mode.ts +0 -2
- package/src/cli/mcp/access-tools.ts +0 -2
- package/src/cli/mcp/audit.ts +0 -2
- package/src/cli/mcp/auth.ts +0 -2
- package/src/cli/mcp/instructions.ts +0 -2
- package/src/cli/mcp/legacy-context.ts +0 -2
- package/src/cli/mcp/legacy-tool-service.ts +0 -2
- package/src/cli/mcp/multi-repository.ts +0 -2
- package/src/cli/mcp/oauth.ts +0 -2
- package/src/cli/mcp/openai-secure-tunnel.ts +0 -2
- package/src/cli/mcp/paths.ts +0 -2
- package/src/cli/mcp/policy.ts +0 -2
- package/src/cli/mcp/post-finalize-work-attribution.ts +0 -2
- package/src/cli/mcp/proxy-env.ts +0 -2
- package/src/cli/mcp/readiness-projection.ts +0 -2
- package/src/cli/mcp/redaction.ts +0 -2
- package/src/cli/mcp/repo.ts +0 -2
- package/src/cli/mcp/repository-tools.ts +0 -2
- package/src/cli/mcp/server.ts +0 -2
- package/src/cli/mcp/setup.ts +0 -2
- package/src/cli/mcp/tool-capability-cache.ts +0 -2
- package/src/cli/mcp/tool-contract.ts +0 -2
- package/src/cli/mcp/tools.ts +0 -2
- package/src/cli/mcp/toolset-names.ts +0 -2
- package/src/cli/mcp/toolset-selection.ts +0 -2
- package/src/cli/mcp/toolset.ts +0 -2
- package/src/cli/mcp/transports/http.ts +0 -2
- package/src/cli/mcp/transports/session-registry.ts +0 -2
- package/src/cli/mcp/transports/stdio.ts +0 -2
- package/src/cli/mcp/types.ts +0 -2
- package/src/cli/mcp/umbrella-tools.ts +0 -2
- package/src/cli/tools/codegraph.ts +0 -695
- package/src/core/adoption/helper-wrapper-plan.ts +0 -159
- package/src/runtime/context/codegraph-cache-boundary.ts +0 -255
- package/src/runtime/context/codegraph-read-provider.ts +0 -611
- package/src/runtime/context/codegraph-sidecar.cjs +0 -248
- package/src/runtime/control-plane/codegraph-cache-retention.ts +0 -164
- package/src/runtime/control-plane/execution/content-equivalent-commit-authority.ts +0 -169
- package/src/runtime/control-plane/execution/direct-canonical-work-reconciliation.ts +0 -85
- package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +0 -655
- package/src/runtime/control-plane/execution/work-finalization-service.ts +0 -2850
- package/src/runtime/control-plane/execution/work-path-scope.ts +0 -37
- package/src/runtime/control-plane/execution/work-task-receipt.ts +0 -468
- package/src/runtime/control-plane/facade/controller-round-relay.ts +0 -3
- package/src/runtime/control-plane/facade/work-admission-policy.ts +0 -142
- package/src/runtime/control-plane/facade/work-implementation-review.ts +0 -2
- package/src/runtime/control-plane/facade/work-state-machine.ts +0 -2
- package/src/runtime/control-plane/facade/work-task-projection.ts +0 -66
- package/src/runtime/control-plane/routing/index.ts +0 -1
- package/src/runtime/control-plane/routing/route-policy.ts +0 -401
- package/src/runtime/control-plane/routing/workspace-admission.ts +0 -70
- package/src/runtime/gateway/mcp/execution-tools.ts +0 -2
- package/src/runtime/gateway/mcp/legacy-ios-tool-adapter.ts +0 -2
- package/src/runtime/gateway/mcp/persisted-check-process.ts +0 -2
- package/src/runtime/gateway/mcp/process-tools.ts +0 -2
- package/src/runtime/gateway/mcp/router.ts +0 -2
- package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +0 -2
- package/src/runtime/gateway/mcp/runtime-tools.ts +0 -2
- package/src/runtime/gateway/mcp/work-validation-reconciler.ts +0 -2
- package/src/runtime/plugins/browser-provider-registry.ts +0 -2
|
@@ -1,12 +1,14 @@
|
|
|
1
1
|
import { execFile } from 'child_process';
|
|
2
|
-
import { existsSync, readFileSync, writeFileSync } from 'fs';
|
|
2
|
+
import { existsSync, readFileSync, statSync, writeFileSync } from 'fs';
|
|
3
3
|
import { homedir } from 'os';
|
|
4
4
|
import { basename, extname } from 'path';
|
|
5
5
|
import {
|
|
6
6
|
callBrowserAutomationBroker,
|
|
7
7
|
captureBrowserAutomationRegion,
|
|
8
8
|
type BrowserAutomationBrokerAction,
|
|
9
|
+
type BrowserAutomationSemanticTargetProof,
|
|
9
10
|
type BrowserAutomationTrustedInput,
|
|
11
|
+
type BrowserAutomationTrustedInputRequest,
|
|
10
12
|
} from './browser-automation-service';
|
|
11
13
|
import { AssistantPluginError } from './errors';
|
|
12
14
|
import type { BrowserNativeAttachAttempt, BrowserNativeProduct } from '../../../packages/protocols/browser/index';
|
|
@@ -14,6 +16,7 @@ import type { BrowserNativeAttachAttempt, BrowserNativeProduct } from '../../../
|
|
|
14
16
|
const RECORD_SEPARATOR = String.fromCharCode(30);
|
|
15
17
|
const DEFAULT_NATIVE_TIMEOUT_MS = 5_000;
|
|
16
18
|
const MAX_NATIVE_OUTPUT_BYTES = 1_048_576;
|
|
19
|
+
const MAX_NATIVE_BROWSER_FILE_ATTACH_BYTES = 16 * 1024 * 1024;
|
|
17
20
|
|
|
18
21
|
export type MacOsBrowserProduct = BrowserNativeProduct;
|
|
19
22
|
|
|
@@ -64,6 +67,8 @@ export interface MacOsBrowserTabInventoryEntry {
|
|
|
64
67
|
url: string;
|
|
65
68
|
title: string;
|
|
66
69
|
active: boolean;
|
|
70
|
+
/** True when this tab is active in the frontmost browser window. */
|
|
71
|
+
frontmost?: boolean;
|
|
67
72
|
}
|
|
68
73
|
|
|
69
74
|
export interface MacOsBrowserTabInventory {
|
|
@@ -119,7 +124,7 @@ function execFileText(file: string, args: string[], timeoutMs: number): Promise<
|
|
|
119
124
|
}, (error, stdout, stderr) => {
|
|
120
125
|
if (error) {
|
|
121
126
|
const diagnostic = String(stderr || error.message).trim().slice(-2_000);
|
|
122
|
-
reject(new Error(diagnostic || error.message));
|
|
127
|
+
reject(new Error(diagnostic || error.message, { cause: error }));
|
|
123
128
|
return;
|
|
124
129
|
}
|
|
125
130
|
resolve(String(stdout).trim());
|
|
@@ -132,10 +137,16 @@ const defaultRuntimeHooks: MacOsBrowserRuntimeHooks = {
|
|
|
132
137
|
appExists: (path) => existsSync(path),
|
|
133
138
|
processRunning: async (processName, timeoutMs) => {
|
|
134
139
|
try {
|
|
140
|
+
// Process existence only: the canonical Computer provider owns exact
|
|
141
|
+
// GUI PID selection and rejects ambiguous or unverified targets.
|
|
135
142
|
await execFileText('/usr/bin/pgrep', ['-x', processName], timeoutMs);
|
|
136
143
|
return true;
|
|
137
|
-
} catch {
|
|
138
|
-
|
|
144
|
+
} catch (error) {
|
|
145
|
+
// pgrep's no-match exit is absence; timeout/permission/transport failure
|
|
146
|
+
// cannot prove that an application's tabs are absent.
|
|
147
|
+
const cause = error instanceof Error ? error.cause : undefined;
|
|
148
|
+
if (cause && typeof cause === 'object' && 'code' in cause && cause.code === 1) return false;
|
|
149
|
+
throw error;
|
|
139
150
|
}
|
|
140
151
|
},
|
|
141
152
|
};
|
|
@@ -214,6 +225,7 @@ async function runCreateTabAutomation(
|
|
|
214
225
|
testArgs: string[],
|
|
215
226
|
timeoutMs: number,
|
|
216
227
|
): Promise<MacOsBrowserCreateTabEvidence> {
|
|
228
|
+
await assertMacOsBrowserApplicationRunning(request.product, timeoutMs);
|
|
217
229
|
if (runtimeHooks.runAppleScript) {
|
|
218
230
|
const value = await runtimeHooks.runAppleScript(testScript, testArgs, timeoutMs);
|
|
219
231
|
return parseMacOsBrowserCreateTabBrokerResult({ value });
|
|
@@ -227,6 +239,12 @@ async function runBrowserAutomationText(
|
|
|
227
239
|
testArgs: string[],
|
|
228
240
|
timeoutMs: number,
|
|
229
241
|
): Promise<string> {
|
|
242
|
+
// AppleScript `tell application` starts an application that is not already
|
|
243
|
+
// running while it compiles the script. Every browser entry point therefore
|
|
244
|
+
// fails closed unless the exact browser process is already live, so routine
|
|
245
|
+
// inventory/reconcile work can never launch the user's browser or hand Forge
|
|
246
|
+
// an unauthenticated profile.
|
|
247
|
+
if ('product' in request) await assertMacOsBrowserApplicationRunning(request.product, timeoutMs);
|
|
230
248
|
if (runtimeHooks.runAppleScript) return await runtimeHooks.runAppleScript(testScript, testArgs, timeoutMs);
|
|
231
249
|
const result = await callBrowserAutomationBroker(request, timeoutMs);
|
|
232
250
|
if (typeof result.value !== 'string') {
|
|
@@ -239,6 +257,76 @@ async function runBrowserAutomationText(
|
|
|
239
257
|
return result.value;
|
|
240
258
|
}
|
|
241
259
|
|
|
260
|
+
/**
|
|
261
|
+
* Non-launching precondition for every Apple Events browser operation.
|
|
262
|
+
* Path existence is checked without scripting the application, and process
|
|
263
|
+
* liveness comes from the process table, so neither probe can start a browser.
|
|
264
|
+
*/
|
|
265
|
+
export async function assertMacOsBrowserApplicationRunning(
|
|
266
|
+
product: MacOsBrowserProduct,
|
|
267
|
+
timeoutMs = DEFAULT_NATIVE_TIMEOUT_MS,
|
|
268
|
+
): Promise<void> {
|
|
269
|
+
const browser = browserDefinition(product);
|
|
270
|
+
const details = { browserProduct: product, bundleId: browser.bundleId, appName: browser.appName };
|
|
271
|
+
if (runtimeHooks.platform !== 'darwin') {
|
|
272
|
+
throw new AssistantPluginError(
|
|
273
|
+
'PLUGIN_BROWSER_NATIVE_APP_UNSUPPORTED_PLATFORM',
|
|
274
|
+
`Native ${browser.appName} Apple Events are unavailable on ${runtimeHooks.platform}.`,
|
|
275
|
+
{ retryable: false, details },
|
|
276
|
+
);
|
|
277
|
+
}
|
|
278
|
+
if (!browser.appPaths.some((path) => runtimeHooks.appExists(path))) {
|
|
279
|
+
throw new AssistantPluginError(
|
|
280
|
+
'PLUGIN_BROWSER_NATIVE_APP_NOT_INSTALLED',
|
|
281
|
+
`${browser.appName} is not installed; Forge never installs or substitutes a browser application.`,
|
|
282
|
+
{ retryable: false, details },
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
if (!(await runtimeHooks.processRunning(browser.processName, timeoutMs))) {
|
|
286
|
+
throw new AssistantPluginError(
|
|
287
|
+
'PLUGIN_BROWSER_NATIVE_APP_NOT_RUNNING',
|
|
288
|
+
`${browser.appName} is not running; Forge never launches a browser application implicitly.`,
|
|
289
|
+
{ retryable: true, details },
|
|
290
|
+
);
|
|
291
|
+
}
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Start only the browser family already chosen by the Computer target. This
|
|
296
|
+
* is deliberately narrower than a generic browser launch: it never guesses a
|
|
297
|
+
* product, profile, or authentication identity. The caller must have a
|
|
298
|
+
* durable preferred product and must still verify the resulting ChatGPT page
|
|
299
|
+
* before any provider mutation.
|
|
300
|
+
*/
|
|
301
|
+
export async function ensureMacOsBrowserApplicationRunning(
|
|
302
|
+
product: MacOsBrowserProduct,
|
|
303
|
+
timeoutMs = DEFAULT_NATIVE_TIMEOUT_MS,
|
|
304
|
+
): Promise<void> {
|
|
305
|
+
const browser = browserDefinition(product);
|
|
306
|
+
if (runtimeHooks.platform !== 'darwin') throw new AssistantPluginError(
|
|
307
|
+
'PLUGIN_BROWSER_NATIVE_APP_UNSUPPORTED_PLATFORM',
|
|
308
|
+
`Native ${browser.appName} Apple Events are unavailable on ${runtimeHooks.platform}.`,
|
|
309
|
+
{ retryable: false, details: { browserProduct: product } },
|
|
310
|
+
);
|
|
311
|
+
if (!browser.appPaths.some((path) => runtimeHooks.appExists(path))) throw new AssistantPluginError(
|
|
312
|
+
'PLUGIN_BROWSER_NATIVE_APP_NOT_INSTALLED',
|
|
313
|
+
`${browser.appName} is not installed.`,
|
|
314
|
+
{ retryable: false, details: { browserProduct: product } },
|
|
315
|
+
);
|
|
316
|
+
if (await runtimeHooks.processRunning(browser.processName, timeoutMs)) return;
|
|
317
|
+
await execFileText('/usr/bin/open', ['-a', browser.appName], timeoutMs);
|
|
318
|
+
const deadline = Date.now() + Math.max(1_000, timeoutMs);
|
|
319
|
+
while (Date.now() < deadline) {
|
|
320
|
+
if (await runtimeHooks.processRunning(browser.processName, Math.min(1_000, timeoutMs))) return;
|
|
321
|
+
await new Promise((resolve) => setTimeout(resolve, 100));
|
|
322
|
+
}
|
|
323
|
+
throw new AssistantPluginError(
|
|
324
|
+
'PLUGIN_BROWSER_NATIVE_APP_START_TIMEOUT',
|
|
325
|
+
`${browser.appName} did not become available after Forge started the remembered browser family.`,
|
|
326
|
+
{ retryable: true, details: { browserProduct: product } },
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
|
|
242
330
|
async function captureBrowserAutomation(
|
|
243
331
|
region: { x: number; y: number; width: number; height: number },
|
|
244
332
|
path: string,
|
|
@@ -251,9 +339,10 @@ async function captureBrowserAutomation(
|
|
|
251
339
|
}
|
|
252
340
|
|
|
253
341
|
async function sendBrowserTrustedInput(
|
|
254
|
-
request:
|
|
342
|
+
request: BrowserAutomationTrustedInputRequest,
|
|
255
343
|
timeoutMs: number,
|
|
256
344
|
): Promise<void> {
|
|
345
|
+
await assertMacOsBrowserApplicationRunning(request.product, timeoutMs);
|
|
257
346
|
if (runtimeHooks.trustedInput) {
|
|
258
347
|
await runtimeHooks.trustedInput(request, timeoutMs);
|
|
259
348
|
return;
|
|
@@ -405,6 +494,8 @@ set maxTabs to 256
|
|
|
405
494
|
set returnedCount to 0
|
|
406
495
|
set truncatedInventory to false
|
|
407
496
|
set outputText to "false"
|
|
497
|
+
set frontWindowId to ""
|
|
498
|
+
if (count of windows) is greater than 0 then set frontWindowId to ((id of front window) as text)
|
|
408
499
|
repeat with candidateWindow in windows
|
|
409
500
|
set activeTabId to ""
|
|
410
501
|
try
|
|
@@ -417,10 +508,11 @@ repeat with candidateWindow in windows
|
|
|
417
508
|
end if
|
|
418
509
|
set candidateWindowId to ((id of candidateWindow) as text)
|
|
419
510
|
set candidateTabId to ((id of candidateTab) as text)
|
|
511
|
+
set candidateFrontmost to (candidateWindowId is frontWindowId)
|
|
420
512
|
set candidateURL to my cleanField((URL of candidateTab as text), recordSeparator, fieldSeparator)
|
|
421
513
|
set candidateTitle to my cleanField((title of candidateTab as text), recordSeparator, fieldSeparator)
|
|
422
514
|
set candidateActive to (candidateTabId is activeTabId)
|
|
423
|
-
set outputText to outputText & recordSeparator & candidateWindowId & fieldSeparator & candidateTabId & fieldSeparator & (candidateActive as text) & fieldSeparator & candidateURL & fieldSeparator & candidateTitle
|
|
515
|
+
set outputText to outputText & recordSeparator & candidateWindowId & fieldSeparator & candidateTabId & fieldSeparator & (candidateActive as text) & fieldSeparator & (candidateFrontmost as text) & fieldSeparator & candidateURL & fieldSeparator & candidateTitle
|
|
424
516
|
set returnedCount to returnedCount + 1
|
|
425
517
|
end repeat
|
|
426
518
|
if truncatedInventory then exit repeat
|
|
@@ -458,14 +550,23 @@ function parseTabInventory(product: MacOsBrowserProduct, raw: string): MacOsBrow
|
|
|
458
550
|
const windowId = (fields[0] ?? '').trim();
|
|
459
551
|
const tabId = (fields[1] ?? '').trim();
|
|
460
552
|
const activeText = (fields[2] ?? '').trim().toLowerCase();
|
|
461
|
-
|
|
553
|
+
const hasFrontmostField = fields.length >= 6 && ['true', 'false'].includes((fields[3] ?? '').trim().toLowerCase());
|
|
554
|
+
const frontmostText = hasFrontmostField ? (fields[3] ?? '').trim().toLowerCase() : undefined;
|
|
555
|
+
if (!windowId || !tabId || (activeText !== 'true' && activeText !== 'false') || (fields.length >= 6 && !hasFrontmostField)) {
|
|
462
556
|
throw new AssistantPluginError(
|
|
463
557
|
'PLUGIN_MACOS_CAPABILITY_BROKER_PROTOCOL_ERROR',
|
|
464
558
|
'Stable Forge macOS capability broker returned invalid browser tab inventory metadata.',
|
|
465
559
|
{ retryable: true },
|
|
466
560
|
);
|
|
467
561
|
}
|
|
468
|
-
tabs.push({
|
|
562
|
+
tabs.push({
|
|
563
|
+
windowId,
|
|
564
|
+
tabId,
|
|
565
|
+
active: activeText === 'true',
|
|
566
|
+
...(frontmostText ? { frontmost: frontmostText === 'true' } : {}),
|
|
567
|
+
url: fields[hasFrontmostField ? 4 : 3] ?? '',
|
|
568
|
+
title: fields.slice(hasFrontmostField ? 5 : 4).join(fieldSeparator),
|
|
569
|
+
});
|
|
469
570
|
if (tabs.length > 256) {
|
|
470
571
|
throw new AssistantPluginError(
|
|
471
572
|
'PLUGIN_MACOS_CAPABILITY_BROKER_PROTOCOL_ERROR',
|
|
@@ -507,30 +608,78 @@ return (frontmost as text) & separator & (URL of targetTab as text) & separator
|
|
|
507
608
|
function activateTargetTabScript(browser: MacOsBrowserDefinition, ref: MacOsBrowserTabRef): string {
|
|
508
609
|
return browserTellScript(browser, `
|
|
509
610
|
${targetTabPreamble(ref)}
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
611
|
+
-- Activating by position is only safe inside the same enumeration that produced
|
|
612
|
+
-- the index. A tab created or closed by the provider between those two steps
|
|
613
|
+
-- makes the positional write fail with -1719 ("Can't get item N of every tab"),
|
|
614
|
+
-- so each attempt re-reads the tab list, bounds the index and verifies the exact
|
|
615
|
+
-- tab became active before it is reported as done.
|
|
616
|
+
set targetTabIdText to ((id of targetTab) as text)
|
|
617
|
+
set activationConfirmed to false
|
|
618
|
+
repeat 3 times
|
|
619
|
+
set targetTabIndex to 0
|
|
620
|
+
set candidateIndex to 0
|
|
621
|
+
repeat with candidateTab in tabs of targetWindow
|
|
622
|
+
set candidateIndex to candidateIndex + 1
|
|
623
|
+
if ((id of candidateTab) as text) is targetTabIdText then set targetTabIndex to candidateIndex
|
|
624
|
+
end repeat
|
|
625
|
+
if targetTabIndex is greater than 0 then
|
|
626
|
+
try
|
|
627
|
+
if targetTabIndex is less than or equal to (count of tabs of targetWindow) then
|
|
628
|
+
set active tab index of targetWindow to targetTabIndex
|
|
629
|
+
if ((id of active tab of targetWindow) as text) is targetTabIdText then set activationConfirmed to true
|
|
630
|
+
end if
|
|
631
|
+
end try
|
|
632
|
+
end if
|
|
633
|
+
if activationConfirmed then exit repeat
|
|
634
|
+
delay 0.05
|
|
514
635
|
end repeat
|
|
515
|
-
set active tab index of targetWindow to targetTabIndex
|
|
516
636
|
set index of targetWindow to 1
|
|
517
637
|
activate
|
|
518
638
|
`);
|
|
519
639
|
}
|
|
520
640
|
|
|
521
|
-
function createOwnedTabScript(browser: MacOsBrowserDefinition): string {
|
|
641
|
+
function createOwnedTabScript(browser: MacOsBrowserDefinition, preferredWindowId?: string): string {
|
|
522
642
|
return `on run argv
|
|
523
643
|
set targetUrl to item 1 of argv
|
|
524
644
|
${browserTellScript(browser, `
|
|
525
|
-
|
|
645
|
+
set preferredWindowId to "${preferredWindowId ?? ''}"
|
|
646
|
+
set targetWindow to missing value
|
|
647
|
+
if preferredWindowId is not "" then
|
|
648
|
+
repeat with candidateWindow in windows
|
|
649
|
+
if ((id of candidateWindow) as text) is preferredWindowId then
|
|
650
|
+
set targetWindow to candidateWindow
|
|
651
|
+
exit repeat
|
|
652
|
+
end if
|
|
653
|
+
end repeat
|
|
654
|
+
end if
|
|
655
|
+
if preferredWindowId is not "" and targetWindow is missing value then error "FORGE_BROWSER_PREFERRED_WINDOW_MISSING:" & preferredWindowId
|
|
656
|
+
if targetWindow is missing value and (count of windows) is greater than 0 then set targetWindow to front window
|
|
657
|
+
if targetWindow is missing value then
|
|
526
658
|
set targetWindow to make new window
|
|
527
659
|
set targetTab to active tab of targetWindow
|
|
528
660
|
set URL of targetTab to targetUrl
|
|
529
661
|
else
|
|
530
|
-
|
|
531
|
-
|
|
662
|
+
-- Preserve the selected background window; front-window reassignment here
|
|
663
|
+
-- previously discarded the caller's stable target and opened the wrong tab.
|
|
664
|
+
set originalActiveTabId to ((id of active tab of targetWindow) as text)
|
|
532
665
|
set targetTab to make new tab at end of tabs of targetWindow with properties {URL:targetUrl}
|
|
533
|
-
|
|
666
|
+
-- Restore the previously active tab by identity, never by a stale position:
|
|
667
|
+
-- the new tab can shift Chrome's tab order and make a pre-create index invalid.
|
|
668
|
+
repeat 3 times
|
|
669
|
+
if ((id of active tab of targetWindow) as text) is originalActiveTabId then exit repeat
|
|
670
|
+
set restoreIndex to 0
|
|
671
|
+
set candidateIndex to 0
|
|
672
|
+
repeat with candidateTab in tabs of targetWindow
|
|
673
|
+
set candidateIndex to candidateIndex + 1
|
|
674
|
+
if ((id of candidateTab) as text) is originalActiveTabId then set restoreIndex to candidateIndex
|
|
675
|
+
end repeat
|
|
676
|
+
if restoreIndex is 0 then exit repeat
|
|
677
|
+
try
|
|
678
|
+
if restoreIndex is less than or equal to (count of tabs of targetWindow) then set active tab index of targetWindow to restoreIndex
|
|
679
|
+
end try
|
|
680
|
+
if ((id of active tab of targetWindow) as text) is originalActiveTabId then exit repeat
|
|
681
|
+
delay 0.05
|
|
682
|
+
end repeat
|
|
534
683
|
end if
|
|
535
684
|
set targetTabId to id of targetTab
|
|
536
685
|
set separator to ASCII character 30
|
|
@@ -804,8 +953,25 @@ export class MacOsAppleEventsPage {
|
|
|
804
953
|
return await runBrowserAutomationText(effectiveRequest, effectiveScript, args, timeoutMs);
|
|
805
954
|
} catch (error) {
|
|
806
955
|
const message = error instanceof Error ? error.message : String(error);
|
|
807
|
-
|
|
808
|
-
|
|
956
|
+
// Preserve the provider's *typed* process-identity refusal. In particular,
|
|
957
|
+
// do not turn ambiguous Chrome app roots into a retryable generic error:
|
|
958
|
+
// retries must never guess another user's running browser instance.
|
|
959
|
+
const nativeCode = error instanceof AssistantPluginError ? error.code : undefined;
|
|
960
|
+
const nativeFailure = nativeCode === 'BROWSER_AUTOMATION_PROCESS_TARGET_AMBIGUOUS'
|
|
961
|
+
|| nativeCode === 'BROWSER_AUTOMATION_PROCESS_IDENTITY_LOST'
|
|
962
|
+
|| nativeCode === 'BROWSER_AUTOMATION_PROCESS_PROBE_UNAVAILABLE'
|
|
963
|
+
|| nativeCode === 'BROWSER_AUTOMATION_NATIVE_EVENT_FAILED'
|
|
964
|
+
|| nativeCode === 'BROWSER_AUTOMATION_MUTATION_OUTCOME_UNKNOWN'
|
|
965
|
+
? nativeCode : undefined;
|
|
966
|
+
const nonRetryable = nativeFailure && nativeFailure !== 'BROWSER_AUTOMATION_PROCESS_PROBE_UNAVAILABLE';
|
|
967
|
+
// An Apple Event can mutate the page even if its callback fails.
|
|
968
|
+
// Preserve uncertainty and never retry such a mutation blindly.
|
|
969
|
+
const outcomeUnknown = nativeFailure === 'BROWSER_AUTOMATION_MUTATION_OUTCOME_UNKNOWN'
|
|
970
|
+
|| (error instanceof AssistantPluginError && error.effectOutcome === 'outcome_unknown');
|
|
971
|
+
const brokerFailure = nativeFailure ?? /\b(BROWSER_AUTOMATION_TIMEOUT|BROWSER_AUTOMATION_SERIALIZATION_BUSY)\b/.exec(message)?.[1];
|
|
972
|
+
throw new AssistantPluginError(brokerFailure ?? 'PLUGIN_BROWSER_NATIVE_OPERATION_FAILED', `${this.browser.appName} Apple Events operation failed: ${message}`, {
|
|
973
|
+
retryable: !nonRetryable && !outcomeUnknown,
|
|
974
|
+
effectOutcome: outcomeUnknown ? 'outcome_unknown' : 'failed',
|
|
809
975
|
details: { browserProduct: this.browser.product, bundleId: this.browser.bundleId },
|
|
810
976
|
});
|
|
811
977
|
}
|
|
@@ -834,8 +1000,12 @@ export class MacOsAppleEventsPage {
|
|
|
834
1000
|
if (macOsBrowserJavaScriptAutomationDisabled(error)) {
|
|
835
1001
|
throw new AssistantPluginError(
|
|
836
1002
|
'PLUGIN_BROWSER_JAVASCRIPT_PERMISSION_REQUIRED',
|
|
837
|
-
`${this.browser.appName}
|
|
838
|
-
{ retryable: false, details: {
|
|
1003
|
+
`${this.browser.appName} rejected JavaScript from Apple Events for the browser instance addressed by macOS automation. This does not prove that the user's visible Chrome instance has this setting disabled. Verify the exact Chrome process/profile and View > Developer > Allow JavaScript from Apple Events before changing settings.`,
|
|
1004
|
+
{ retryable: false, details: {
|
|
1005
|
+
browserProduct: this.browser.product, bundleId: this.browser.bundleId,
|
|
1006
|
+
processIdentity: 'unverified',
|
|
1007
|
+
...(this.targetRef ? { windowId: this.targetRef.windowId, tabId: this.targetRef.tabId } : {}),
|
|
1008
|
+
} },
|
|
839
1009
|
);
|
|
840
1010
|
}
|
|
841
1011
|
throw error;
|
|
@@ -973,15 +1143,16 @@ export class MacOsAppleEventsPage {
|
|
|
973
1143
|
if (filePaths.length === 0 || filePaths.length > 32) {
|
|
974
1144
|
throw new AssistantPluginError('PLUGIN_ACTION_ARGUMENT_INVALID', 'Native browser file input accepts 1-32 local files.', { retryable: false });
|
|
975
1145
|
}
|
|
976
|
-
|
|
1146
|
+
|
|
1147
|
+
// Validate the entire batch before sending bytes or changing input.files.
|
|
1148
|
+
const attachments = filePaths.map((filePath) => {
|
|
977
1149
|
if (!filePath || !existsSync(filePath)) {
|
|
978
1150
|
throw new AssistantPluginError('PLUGIN_ACTION_ARGUMENT_INVALID', 'Local file does not exist.', { retryable: false });
|
|
979
1151
|
}
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
const bytes = readFileSync(filePath);
|
|
1152
|
+
const metadata = statSync(filePath);
|
|
1153
|
+
if (!metadata.isFile()) {
|
|
1154
|
+
throw new AssistantPluginError('PLUGIN_ACTION_ARGUMENT_INVALID', 'Native browser can attach only regular files.', { retryable: false });
|
|
1155
|
+
}
|
|
985
1156
|
const fileName = basename(filePath);
|
|
986
1157
|
const extension = extname(fileName).toLowerCase();
|
|
987
1158
|
const mimeType = extension === '.pdf' ? 'application/pdf'
|
|
@@ -989,123 +1160,98 @@ export class MacOsAppleEventsPage {
|
|
|
989
1160
|
: extension === '.png' ? 'image/png'
|
|
990
1161
|
: extension === '.jpg' || extension === '.jpeg' ? 'image/jpeg'
|
|
991
1162
|
: 'application/octet-stream';
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
1163
|
+
return { filePath, fileName, size: metadata.size, mimeType };
|
|
1164
|
+
});
|
|
1165
|
+
const totalBytes = attachments.reduce((sum, attachment) => sum + attachment.size, 0);
|
|
1166
|
+
if (totalBytes > MAX_NATIVE_BROWSER_FILE_ATTACH_BYTES) {
|
|
1167
|
+
throw new AssistantPluginError('PLUGIN_BROWSER_NATIVE_FILE_TOO_LARGE',
|
|
1168
|
+
'Native browser upload is bounded to 16 MiB per batch; select a smaller batch instead of queuing unbounded Apple Events.',
|
|
1169
|
+
{ retryable: false, details: { totalBytes, limitBytes: MAX_NATIVE_BROWSER_FILE_ATTACH_BYTES } });
|
|
1170
|
+
}
|
|
1171
|
+
|
|
1172
|
+
// A read-only DOM preflight must pass before staging starts. In particular,
|
|
1173
|
+
// multi-file uploads cannot reset a previous file selection on preflight failure.
|
|
1174
|
+
await this.evaluate(`(() => { // __forgeFileInputPreflight
|
|
1175
|
+
const matches = document.querySelectorAll(${JSON.stringify(selector)});
|
|
1176
|
+
if (matches.length !== 1) throw new Error('File input selector must resolve to exactly one element.');
|
|
1177
|
+
const element = matches[0];
|
|
1178
|
+
if (!(element instanceof HTMLInputElement) || element.type !== 'file') throw new Error('Selector must resolve to an input[type=file].');
|
|
1179
|
+
if (element.disabled) throw new Error('Target file input is disabled.');
|
|
1180
|
+
if (${attachments.length > 1} && !element.multiple) throw new Error('Target input does not allow multiple files.');
|
|
1181
|
+
return true;
|
|
1182
|
+
})()`);
|
|
1183
|
+
|
|
1184
|
+
const batchKey = `__forgeStagedFiles_${Date.now()}_${Math.random().toString(16).slice(2)}`;
|
|
1185
|
+
const bufferKey = `__forgeEncodedFile_${Date.now()}_${Math.random().toString(16).slice(2)}`;
|
|
1186
|
+
const expected = attachments.map(({ fileName, size }) => ({ name: fileName, size }));
|
|
1187
|
+
const chunkChars = 192 * 1024;
|
|
1188
|
+
try {
|
|
1189
|
+
for (const attachment of attachments) {
|
|
1190
|
+
// Stage one file at a time. No DOM selection or change event occurs
|
|
1191
|
+
// until the complete batch has arrived and its sizes have been checked.
|
|
1192
|
+
const encoded = readFileSync(attachment.filePath).toString('base64');
|
|
996
1193
|
await this.evaluate(`(() => { globalThis[${JSON.stringify(bufferKey)}] = ''; return true; })()`);
|
|
997
1194
|
for (let offset = 0; offset < encoded.length; offset += chunkChars) {
|
|
998
1195
|
const chunk = encoded.slice(offset, offset + chunkChars);
|
|
999
1196
|
await this.evaluate(`(() => { globalThis[${JSON.stringify(bufferKey)}] += ${JSON.stringify(chunk)}; return true; })()`);
|
|
1000
1197
|
}
|
|
1001
|
-
const
|
|
1002
|
-
const element = document.querySelector(${JSON.stringify(selector)});
|
|
1003
|
-
if (!(element instanceof HTMLInputElement) || element.type !== 'file') throw new Error('Selector must resolve to an input[type=file].');
|
|
1004
|
-
const expectedName = ${JSON.stringify(fileName)};
|
|
1005
|
-
const expectedSize = ${bytes.length};
|
|
1198
|
+
const staged = await this.evaluate<{ name?: string; size?: number }>(`(() => { // __forgeFileInputStaged
|
|
1006
1199
|
const encoded = String(globalThis[${JSON.stringify(bufferKey)}] || '');
|
|
1007
1200
|
const binary = atob(encoded);
|
|
1008
1201
|
const data = new Uint8Array(binary.length);
|
|
1009
1202
|
for (let i = 0; i < binary.length; i += 1) data[i] = binary.charCodeAt(i);
|
|
1010
|
-
const file = new File([data],
|
|
1011
|
-
const
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
const selected = element.files && element.files[0];
|
|
1017
|
-
return selected ? { name: selected.name, size: selected.size, expectedName, expectedSize } : null;
|
|
1203
|
+
const file = new File([data], ${JSON.stringify(attachment.fileName)}, { type: ${JSON.stringify(attachment.mimeType)} });
|
|
1204
|
+
const staged = globalThis[${JSON.stringify(batchKey)}] || [];
|
|
1205
|
+
staged.push(file);
|
|
1206
|
+
globalThis[${JSON.stringify(batchKey)}] = staged;
|
|
1207
|
+
delete globalThis[${JSON.stringify(bufferKey)}];
|
|
1208
|
+
return { name: file.name, size: file.size };
|
|
1018
1209
|
})()`);
|
|
1019
|
-
if (
|
|
1020
|
-
throw new AssistantPluginError('PLUGIN_BROWSER_NATIVE_FILE_ATTACH_FAILED',
|
|
1021
|
-
retryable:
|
|
1022
|
-
details: { expectedName: fileName, expectedSize: bytes.length, actualName: attached?.name, actualSize: attached?.size },
|
|
1023
|
-
});
|
|
1210
|
+
if (staged?.name !== attachment.fileName || staged?.size !== attachment.size) {
|
|
1211
|
+
throw new AssistantPluginError('PLUGIN_BROWSER_NATIVE_FILE_ATTACH_FAILED',
|
|
1212
|
+
'Browser did not stage the expected local file.', { retryable: false });
|
|
1024
1213
|
}
|
|
1025
|
-
} finally {
|
|
1026
|
-
await this.evaluate(`(() => { try { delete globalThis[${JSON.stringify(bufferKey)}]; } catch {} return true; })()`).catch(() => undefined);
|
|
1027
1214
|
}
|
|
1028
|
-
return;
|
|
1029
|
-
}
|
|
1030
1215
|
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
const
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
if (attachments.length > 1) {
|
|
1049
|
-
await this.evaluate(`(() => {
|
|
1050
|
-
const element = document.querySelector(${JSON.stringify(selector)});
|
|
1051
|
-
if (!(element instanceof HTMLInputElement) || element.type !== 'file') throw new Error('Selector must resolve to an input[type=file].');
|
|
1052
|
-
if (!element.multiple) throw new Error('Target input does not allow multiple files.');
|
|
1216
|
+
// One commit to input.files and one event pair, after all File objects
|
|
1217
|
+
// are staged and verified. Failed transfer never partially selects files.
|
|
1218
|
+
const selected = await this.evaluate<Array<{ name?: string; size?: number }>>(`(() => { // __forgeFileInputCommit
|
|
1219
|
+
const matches = document.querySelectorAll(${JSON.stringify(selector)});
|
|
1220
|
+
if (matches.length !== 1) throw new Error('File input selector changed during transfer.');
|
|
1221
|
+
const element = matches[0];
|
|
1222
|
+
if (!(element instanceof HTMLInputElement) || element.type !== 'file' || element.disabled) {
|
|
1223
|
+
throw new Error('File input became unavailable during transfer.');
|
|
1224
|
+
}
|
|
1225
|
+
if (${attachments.length > 1} && !element.multiple) throw new Error('File input no longer supports multiple files.');
|
|
1226
|
+
const staged = globalThis[${JSON.stringify(batchKey)}];
|
|
1227
|
+
const expected = ${JSON.stringify(expected)};
|
|
1228
|
+
if (!Array.isArray(staged) || staged.length !== expected.length ||
|
|
1229
|
+
!staged.every((file, i) => file instanceof File && file.name === expected[i].name && file.size === expected[i].size)) {
|
|
1230
|
+
throw new Error('Staged file batch does not match the requested selection.');
|
|
1231
|
+
}
|
|
1053
1232
|
const transfer = new DataTransfer();
|
|
1233
|
+
staged.forEach((file) => transfer.items.add(file));
|
|
1054
1234
|
element.files = transfer.files;
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
const chunkChars = 192 * 1024;
|
|
1060
|
-
try {
|
|
1061
|
-
for (let index = 0; index < attachments.length; index += 1) {
|
|
1062
|
-
const attachment = attachments[index];
|
|
1063
|
-
const encoded = attachment.bytes.toString('base64');
|
|
1064
|
-
await this.evaluate(`(() => { globalThis[${JSON.stringify(attachment.bufferKey)}] = ''; return true; })()`);
|
|
1065
|
-
for (let offset = 0; offset < encoded.length; offset += chunkChars) {
|
|
1066
|
-
const chunk = encoded.slice(offset, offset + chunkChars);
|
|
1067
|
-
await this.evaluate(`(() => { globalThis[${JSON.stringify(attachment.bufferKey)}] += ${JSON.stringify(chunk)}; return true; })()`);
|
|
1235
|
+
const selected = Array.from(element.files || []).map((file) => ({ name: file.name, size: file.size }));
|
|
1236
|
+
if (selected.length !== expected.length ||
|
|
1237
|
+
!selected.every((file, i) => file.name === expected[i].name && file.size === expected[i].size)) {
|
|
1238
|
+
throw new Error('Browser did not retain the requested file selection.');
|
|
1068
1239
|
}
|
|
1069
|
-
const append = attachments.length > 1 && index > 0;
|
|
1070
|
-
await this.evaluate(`(() => {
|
|
1071
|
-
const element = document.querySelector(${JSON.stringify(selector)});
|
|
1072
|
-
if (!(element instanceof HTMLInputElement) || element.type !== 'file') throw new Error('Selector must resolve to an input[type=file].');
|
|
1073
|
-
const expectedName = ${JSON.stringify(attachment.fileName)};
|
|
1074
|
-
const expectedSize = ${attachment.bytes.length};
|
|
1075
|
-
const encoded = String(globalThis[${JSON.stringify(attachment.bufferKey)}] || '');
|
|
1076
|
-
const binary = atob(encoded);
|
|
1077
|
-
const data = new Uint8Array(binary.length);
|
|
1078
|
-
for (let i = 0; i < binary.length; i += 1) data[i] = binary.charCodeAt(i);
|
|
1079
|
-
const file = new File([data], expectedName, { type: ${JSON.stringify(attachment.mimeType)} });
|
|
1080
|
-
const transfer = new DataTransfer();
|
|
1081
|
-
if (${append ? 'true' : 'false'}) {
|
|
1082
|
-
for (const existing of Array.from(element.files || [])) transfer.items.add(existing);
|
|
1083
|
-
}
|
|
1084
|
-
transfer.items.add(file);
|
|
1085
|
-
element.files = transfer.files;
|
|
1086
|
-
return Array.from(element.files || []).map((selected) => ({ name: selected.name, size: selected.size }));
|
|
1087
|
-
})()`);
|
|
1088
|
-
}
|
|
1089
|
-
|
|
1090
|
-
const selected = await this.evaluate<Array<{ name?: string; size?: number }>>(`(() => {
|
|
1091
|
-
const element = document.querySelector(${JSON.stringify(selector)});
|
|
1092
|
-
if (!(element instanceof HTMLInputElement) || element.type !== 'file') throw new Error('Selector must resolve to an input[type=file].');
|
|
1093
1240
|
element.dispatchEvent(new Event('input', { bubbles: true }));
|
|
1094
1241
|
element.dispatchEvent(new Event('change', { bubbles: true }));
|
|
1095
|
-
return
|
|
1242
|
+
return selected;
|
|
1096
1243
|
})()`);
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
retryable: true,
|
|
1102
|
-
details: { expected, actual: selected },
|
|
1103
|
-
});
|
|
1244
|
+
if (selected.length !== expected.length ||
|
|
1245
|
+
!expected.every((item, index) => selected[index]?.name === item.name && selected[index]?.size === item.size)) {
|
|
1246
|
+
throw new AssistantPluginError('PLUGIN_BROWSER_NATIVE_FILE_ATTACH_FAILED',
|
|
1247
|
+
'Native browser did not retain the requested local files on the target input.', { retryable: false });
|
|
1104
1248
|
}
|
|
1105
1249
|
} finally {
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1250
|
+
// Unknown effect outcomes must be reconciled by observation, never by replay.
|
|
1251
|
+
await this.evaluate(`(() => {
|
|
1252
|
+
try { delete globalThis[${JSON.stringify(bufferKey)}]; delete globalThis[${JSON.stringify(batchKey)}]; } catch {}
|
|
1253
|
+
return true;
|
|
1254
|
+
})()`).catch(() => undefined);
|
|
1109
1255
|
}
|
|
1110
1256
|
}
|
|
1111
1257
|
|
|
@@ -1123,6 +1269,57 @@ export class MacOsAppleEventsPage {
|
|
|
1123
1269
|
await this.refreshMetadata();
|
|
1124
1270
|
}
|
|
1125
1271
|
|
|
1272
|
+
/**
|
|
1273
|
+
* Supervisor-only semantic text boundary. Browser owns exact tab + DOM focus;
|
|
1274
|
+
* Computer receives only a one-shot semantic target proof and remains the OS
|
|
1275
|
+
* mutation transport. Generic Browser trusted text keeps its compatibility
|
|
1276
|
+
* semantics and does not implicitly become ChatGPT-specific.
|
|
1277
|
+
*/
|
|
1278
|
+
async trustedSemanticTextInput(text: string): Promise<void> {
|
|
1279
|
+
if (!this.targetRef) {
|
|
1280
|
+
throw new AssistantPluginError('PLUGIN_BROWSER_TRUSTED_INPUT_UNAVAILABLE', 'Semantic trusted text requires an exact saved browser tab.', { retryable: false });
|
|
1281
|
+
}
|
|
1282
|
+
this.targetRef = await resolveCurrentMacOsBrowserTabRef(this.browser.product, this.targetRef, this.timeoutMs);
|
|
1283
|
+
const metadata = await readResolvedMacOsBrowserOwnedTabMetadata(this.browser.product, this.targetRef, this.timeoutMs);
|
|
1284
|
+
if (!metadata.frontmost || metadata.active !== true) {
|
|
1285
|
+
throw new AssistantPluginError(
|
|
1286
|
+
'PLUGIN_BROWSER_FOREGROUND_REQUIRED',
|
|
1287
|
+
'Semantic trusted text requires the exact saved browser tab to be frontmost and active.',
|
|
1288
|
+
{ retryable: true, details: { browserProduct: this.browser.product, windowId: this.targetRef.windowId, tabId: this.targetRef.tabId } },
|
|
1289
|
+
);
|
|
1290
|
+
}
|
|
1291
|
+
const semanticTarget = await this.evaluate<BrowserAutomationSemanticTargetProof & { valid?: boolean }>(`(() => {
|
|
1292
|
+
const element = document.activeElement;
|
|
1293
|
+
if (!(element instanceof HTMLElement)) return { valid: false };
|
|
1294
|
+
const tag = element.tagName.toLowerCase();
|
|
1295
|
+
const inputType = element instanceof HTMLInputElement ? String(element.type || 'text').toLowerCase() : '';
|
|
1296
|
+
const textInput = element instanceof HTMLTextAreaElement
|
|
1297
|
+
|| element.isContentEditable
|
|
1298
|
+
|| (element instanceof HTMLInputElement && !['button','checkbox','color','file','hidden','image','radio','range','reset','submit'].includes(inputType));
|
|
1299
|
+
const accessibleName = String(element.getAttribute('aria-label') || element.getAttribute('placeholder') || element.getAttribute('title') || element.getAttribute('name') || '').trim();
|
|
1300
|
+
const domRole = String(element.getAttribute('role') || (textInput ? 'textbox' : '')).trim();
|
|
1301
|
+
const multiline = element instanceof HTMLTextAreaElement || element.isContentEditable || element.getAttribute('aria-multiline') === 'true';
|
|
1302
|
+
return { valid: textInput && document.activeElement === element && Boolean(domRole) && Boolean(accessibleName), domRole, accessibleName, editable: true, focused: true, multiline };
|
|
1303
|
+
})()`);
|
|
1304
|
+
if (semanticTarget.valid !== true || semanticTarget.editable !== true || semanticTarget.focused !== true
|
|
1305
|
+
|| !semanticTarget.domRole || !semanticTarget.accessibleName) {
|
|
1306
|
+
throw new AssistantPluginError(
|
|
1307
|
+
'PLUGIN_BROWSER_TRUSTED_INPUT_SEMANTIC_TARGET_UNPROVEN',
|
|
1308
|
+
'Browser could not prove one focused editable DOM target with an accessible identity before trusted text mutation.',
|
|
1309
|
+
{ retryable: true, details: { browserProduct: this.browser.product, windowId: this.targetRef.windowId, tabId: this.targetRef.tabId } },
|
|
1310
|
+
);
|
|
1311
|
+
}
|
|
1312
|
+
const { valid: _valid, ...proof } = semanticTarget;
|
|
1313
|
+
await sendBrowserTrustedInput({
|
|
1314
|
+
action: 'trusted_input',
|
|
1315
|
+
product: this.browser.product,
|
|
1316
|
+
ref: this.targetRef,
|
|
1317
|
+
input: { kind: 'text', text },
|
|
1318
|
+
semanticTarget: proof,
|
|
1319
|
+
}, this.timeoutMs);
|
|
1320
|
+
await this.refreshMetadata();
|
|
1321
|
+
}
|
|
1322
|
+
|
|
1126
1323
|
async screenshot(options: Record<string, unknown>): Promise<Buffer> {
|
|
1127
1324
|
const path = typeof options.path === 'string' ? options.path : undefined;
|
|
1128
1325
|
if (!path) throw new Error('Native browser screenshot requires an output path.');
|
|
@@ -1191,8 +1388,15 @@ export class MacOsAppleEventsPage {
|
|
|
1191
1388
|
const prototype = element instanceof HTMLTextAreaElement ? HTMLTextAreaElement.prototype : HTMLInputElement.prototype;
|
|
1192
1389
|
const setter = Object.getOwnPropertyDescriptor(prototype, 'value')?.set;
|
|
1193
1390
|
if (setter) setter.call(element, ${JSON.stringify(value)}); else element.value = ${JSON.stringify(value)};
|
|
1194
|
-
} else if (element.isContentEditable)
|
|
1195
|
-
|
|
1391
|
+
} else if (element.isContentEditable) {
|
|
1392
|
+
element.focus();
|
|
1393
|
+
const selection = window.getSelection();
|
|
1394
|
+
if (!selection) throw new Error('Contenteditable selection unavailable.');
|
|
1395
|
+
const range = document.createRange();
|
|
1396
|
+
range.selectNodeContents(element); selection.removeAllRanges(); selection.addRange(range);
|
|
1397
|
+
if (!document.execCommand('insertText', false, ${JSON.stringify(value)})) throw new Error('Contenteditable insertion rejected.');
|
|
1398
|
+
return true;
|
|
1399
|
+
} else throw new Error('Element does not accept text input.');
|
|
1196
1400
|
element.dispatchEvent(new Event('input', { bubbles: true }));
|
|
1197
1401
|
element.dispatchEvent(new Event('change', { bubbles: true }));
|
|
1198
1402
|
return true;
|
|
@@ -1200,8 +1404,26 @@ export class MacOsAppleEventsPage {
|
|
|
1200
1404
|
}
|
|
1201
1405
|
|
|
1202
1406
|
async type(selector: string, text: string): Promise<void> {
|
|
1203
|
-
|
|
1204
|
-
|
|
1407
|
+
await this.evaluate(selectorSource(selector, `
|
|
1408
|
+
if ('value' in element) {
|
|
1409
|
+
element.focus();
|
|
1410
|
+
const value = String(element.value || '');
|
|
1411
|
+
const prototype = element instanceof HTMLTextAreaElement ? HTMLTextAreaElement.prototype : HTMLInputElement.prototype;
|
|
1412
|
+
const setter = Object.getOwnPropertyDescriptor(prototype, 'value')?.set;
|
|
1413
|
+
if (setter) setter.call(element, value + ${JSON.stringify(text)}); else element.value = value + ${JSON.stringify(text)};
|
|
1414
|
+
element.dispatchEvent(new Event('input', { bubbles: true }));
|
|
1415
|
+
return true;
|
|
1416
|
+
}
|
|
1417
|
+
if (!element.isContentEditable) throw new Error('Element does not accept text input.');
|
|
1418
|
+
element.focus();
|
|
1419
|
+
const selection = window.getSelection();
|
|
1420
|
+
if (!selection) throw new Error('Contenteditable selection unavailable.');
|
|
1421
|
+
const range = document.createRange();
|
|
1422
|
+
range.selectNodeContents(element); range.collapse(false);
|
|
1423
|
+
selection.removeAllRanges(); selection.addRange(range);
|
|
1424
|
+
if (!document.execCommand('insertText', false, ${JSON.stringify(text)})) throw new Error('Contenteditable insertion rejected.');
|
|
1425
|
+
return true;
|
|
1426
|
+
`));
|
|
1205
1427
|
}
|
|
1206
1428
|
|
|
1207
1429
|
async press(selector: string, key: string): Promise<void> {
|
|
@@ -1359,7 +1581,9 @@ async function resolveCurrentMacOsBrowserTabRef(
|
|
|
1359
1581
|
? await runtimeHooks.tabInventory(product, timeoutMs)
|
|
1360
1582
|
: runtimeHooks.runAppleScript
|
|
1361
1583
|
? undefined
|
|
1362
|
-
|
|
1584
|
+
// Stable identity needs the complete inventory, not UI-current flags.
|
|
1585
|
+
// Input/activation checks read their exact target's foreground state.
|
|
1586
|
+
: await listMacOsBrowserTabs(product, timeoutMs, { correctFrontmost: false });
|
|
1363
1587
|
if (!inventory) return ref;
|
|
1364
1588
|
|
|
1365
1589
|
const matches = inventory.tabs.filter((candidate) => candidate.tabId === ref.tabId);
|
|
@@ -1388,18 +1612,58 @@ async function resolveCurrentMacOsBrowserTabRef(
|
|
|
1388
1612
|
return { windowId: match.windowId, tabId: match.tabId };
|
|
1389
1613
|
}
|
|
1390
1614
|
|
|
1615
|
+
export interface MacOsBrowserTabInventoryOptions {
|
|
1616
|
+
/**
|
|
1617
|
+
* Correct stale active/frontmost bits through one extra foreground metadata
|
|
1618
|
+
* read. Exact-target callers that only need stable windowId/tabId/url identity
|
|
1619
|
+
* can disable this optional probe so a blocked foreground Apple Events read
|
|
1620
|
+
* cannot starve the inventory itself.
|
|
1621
|
+
*/
|
|
1622
|
+
correctFrontmost?: boolean;
|
|
1623
|
+
}
|
|
1624
|
+
|
|
1391
1625
|
export async function listMacOsBrowserTabs(
|
|
1392
1626
|
product: MacOsBrowserProduct,
|
|
1393
1627
|
timeoutMs = DEFAULT_NATIVE_TIMEOUT_MS,
|
|
1628
|
+
options: MacOsBrowserTabInventoryOptions = {},
|
|
1394
1629
|
): Promise<MacOsBrowserTabInventory> {
|
|
1395
1630
|
const browser = browserDefinition(product);
|
|
1631
|
+
// Capture the frontmost tab before reading the stable inventory when callers
|
|
1632
|
+
// need corrected UI-current metadata. Exact-target identity does not depend on
|
|
1633
|
+
// this optional foreground probe.
|
|
1634
|
+
const inspected = options.correctFrontmost === false
|
|
1635
|
+
? undefined
|
|
1636
|
+
: await inspectBrowser(product, timeoutMs);
|
|
1396
1637
|
const raw = await runBrowserAutomationText(
|
|
1397
1638
|
{ action: 'list_tabs', product },
|
|
1398
1639
|
listTabsScript(browser),
|
|
1399
1640
|
[],
|
|
1400
1641
|
timeoutMs,
|
|
1401
1642
|
);
|
|
1402
|
-
|
|
1643
|
+
const inventory = parseTabInventory(product, raw);
|
|
1644
|
+
if (!inspected) return inventory;
|
|
1645
|
+
const metadata = inspected.metadata;
|
|
1646
|
+
const current = metadata?.frontmost === true && metadata.active !== false
|
|
1647
|
+
? { windowId: metadata.windowId, tabId: metadata.tabId, url: metadata.url.trim() }
|
|
1648
|
+
: undefined;
|
|
1649
|
+
if (!current) return inventory;
|
|
1650
|
+
const hasIdentity = Boolean(current.windowId && current.tabId);
|
|
1651
|
+
const currentMatches = inventory.tabs.filter(tab => (
|
|
1652
|
+
hasIdentity
|
|
1653
|
+
? tab.windowId === current.windowId && tab.tabId === current.tabId
|
|
1654
|
+
: Boolean(current.url) && tab.url.trim() === current.url
|
|
1655
|
+
));
|
|
1656
|
+
const currentMatch = currentMatches.length === 1 ? currentMatches[0] : undefined;
|
|
1657
|
+
if (!currentMatch) return inventory;
|
|
1658
|
+
return {
|
|
1659
|
+
...inventory,
|
|
1660
|
+
tabs: inventory.tabs.map(tab => ({
|
|
1661
|
+
...tab,
|
|
1662
|
+
...(currentMatch.windowId === tab.windowId && currentMatch.tabId === tab.tabId
|
|
1663
|
+
? { active: true, frontmost: true }
|
|
1664
|
+
: { frontmost: false }),
|
|
1665
|
+
})),
|
|
1666
|
+
};
|
|
1403
1667
|
}
|
|
1404
1668
|
|
|
1405
1669
|
export async function readMacOsBrowserOwnedTabMetadata(
|
|
@@ -1407,11 +1671,24 @@ export async function readMacOsBrowserOwnedTabMetadata(
|
|
|
1407
1671
|
ref: MacOsBrowserTabRef,
|
|
1408
1672
|
timeoutMs = DEFAULT_NATIVE_TIMEOUT_MS,
|
|
1409
1673
|
): Promise<MacOsBrowserMetadata> {
|
|
1410
|
-
const browser = browserDefinition(product);
|
|
1411
1674
|
const resolvedRef = await resolveCurrentMacOsBrowserTabRef(product, ref, timeoutMs);
|
|
1675
|
+
return await readResolvedMacOsBrowserOwnedTabMetadata(product, resolvedRef, timeoutMs);
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1678
|
+
/**
|
|
1679
|
+
* Read one exact native tab whose stable identity is already authoritative for
|
|
1680
|
+
* the caller. The targeted AppleScript still fails closed when that tab id is
|
|
1681
|
+
* gone, but does not perform another full browser inventory.
|
|
1682
|
+
*/
|
|
1683
|
+
export async function readResolvedMacOsBrowserOwnedTabMetadata(
|
|
1684
|
+
product: MacOsBrowserProduct,
|
|
1685
|
+
ref: MacOsBrowserTabRef,
|
|
1686
|
+
timeoutMs = DEFAULT_NATIVE_TIMEOUT_MS,
|
|
1687
|
+
): Promise<MacOsBrowserMetadata> {
|
|
1688
|
+
const browser = browserDefinition(product);
|
|
1412
1689
|
return parseMetadata(product, await runBrowserAutomationText(
|
|
1413
|
-
{ action: 'metadata', product, ref
|
|
1414
|
-
targetMetadataScript(browser,
|
|
1690
|
+
{ action: 'metadata', product, ref },
|
|
1691
|
+
targetMetadataScript(browser, ref),
|
|
1415
1692
|
[],
|
|
1416
1693
|
timeoutMs,
|
|
1417
1694
|
));
|
|
@@ -1481,12 +1758,13 @@ export async function createMacOsBrowserOwnedPageForProduct(
|
|
|
1481
1758
|
product: MacOsBrowserProduct,
|
|
1482
1759
|
url: string,
|
|
1483
1760
|
attempts: MacOsBrowserAttachAttempt[],
|
|
1761
|
+
preferredWindowId?: string,
|
|
1484
1762
|
timeoutMs = DEFAULT_NATIVE_TIMEOUT_MS,
|
|
1485
1763
|
): Promise<{ page: MacOsAppleEventsPage; attachment: MacOsBrowserAttachment }> {
|
|
1486
1764
|
const browser = browserDefinition(product);
|
|
1487
1765
|
const creationEvidence = await runCreateTabAutomation(
|
|
1488
1766
|
{ action: 'create_tab', product, url },
|
|
1489
|
-
createOwnedTabScript(browser),
|
|
1767
|
+
createOwnedTabScript(browser, preferredWindowId),
|
|
1490
1768
|
[url],
|
|
1491
1769
|
timeoutMs,
|
|
1492
1770
|
);
|
|
@@ -1544,12 +1822,28 @@ export async function closeMacOsBrowserOwnedTab(
|
|
|
1544
1822
|
ref: MacOsBrowserTabRef,
|
|
1545
1823
|
timeoutMs = DEFAULT_NATIVE_TIMEOUT_MS,
|
|
1546
1824
|
): Promise<void> {
|
|
1547
|
-
const browser = browserDefinition(product);
|
|
1548
1825
|
try {
|
|
1549
1826
|
const resolvedRef = await resolveCurrentMacOsBrowserTabRef(product, ref, timeoutMs);
|
|
1827
|
+
await closeResolvedMacOsBrowserOwnedTab(product, resolvedRef, timeoutMs);
|
|
1828
|
+
} finally {
|
|
1829
|
+
invalidateMacOsBrowserPageHandle(product, ref);
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1832
|
+
|
|
1833
|
+
/**
|
|
1834
|
+
* Close one exact native tab whose stable identity was already proven by the
|
|
1835
|
+
* caller's live inventory snapshot. This deliberately does not inventory again.
|
|
1836
|
+
*/
|
|
1837
|
+
export async function closeResolvedMacOsBrowserOwnedTab(
|
|
1838
|
+
product: MacOsBrowserProduct,
|
|
1839
|
+
ref: MacOsBrowserTabRef,
|
|
1840
|
+
timeoutMs = DEFAULT_NATIVE_TIMEOUT_MS,
|
|
1841
|
+
): Promise<void> {
|
|
1842
|
+
const browser = browserDefinition(product);
|
|
1843
|
+
try {
|
|
1550
1844
|
await runBrowserAutomationText(
|
|
1551
|
-
{ action: 'close_tab', product, ref
|
|
1552
|
-
closeOwnedTabScript(browser,
|
|
1845
|
+
{ action: 'close_tab', product, ref },
|
|
1846
|
+
closeOwnedTabScript(browser, ref),
|
|
1553
1847
|
[],
|
|
1554
1848
|
timeoutMs,
|
|
1555
1849
|
);
|