@moretea-labs/forge 1.7.2 → 1.8.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +58 -62
- package/CHANGELOG.md +26 -0
- package/CLAUDE.md +42 -62
- package/README.md +2 -2
- package/SKILL.md +0 -1
- package/THIRD_PARTY_NOTICES.md +5 -2
- package/adapters/browser/index.ts +2 -0
- package/adapters/chatgpt/browser-delivery-host.ts +71 -0
- package/adapters/chatgpt/browser-delivery-runtime.ts +783 -0
- package/adapters/chatgpt/controller-binding-store.ts +55 -0
- package/adapters/chatgpt/controller-host.ts +119 -0
- package/adapters/chatgpt/controller-round-host.ts +78 -0
- package/adapters/chatgpt/controller-round-settlement-store.ts +45 -0
- package/adapters/chatgpt/provider-delivery.ts +113 -0
- package/adapters/chatgpt/work-conversation-binding-store.ts +192 -0
- package/adapters/chatgpt/wsl-bridge-delivery-host.ts +63 -0
- package/adapters/computer/desktop-operator-contract.ts +7 -0
- package/adapters/computer/desktop-operator-discovery.ts +201 -0
- package/adapters/computer/desktop-operator-negotiation.ts +174 -0
- package/adapters/computer/desktop-operator-provider.ts +312 -0
- package/adapters/computer/index.ts +1 -0
- package/adapters/computer/interaction-target-authority.ts +944 -0
- package/adapters/controller-process/binding-store.ts +34 -0
- package/adapters/controller-process/controller-host.ts +26 -0
- package/adapters/mcp/access-mode.ts +132 -0
- package/adapters/mcp/audit.ts +32 -0
- package/adapters/mcp/auth.ts +407 -0
- package/adapters/mcp/controller-round-compatibility.ts +184 -0
- package/adapters/mcp/frozen-client-semantic-compatibility.ts +292 -0
- package/adapters/mcp/instructions.ts +36 -0
- package/adapters/mcp/legacy-context.ts +18 -0
- package/adapters/mcp/multi-repository.ts +437 -0
- package/adapters/mcp/oauth.ts +391 -0
- package/adapters/mcp/paths.ts +140 -0
- package/adapters/mcp/policy.ts +232 -0
- package/adapters/mcp/proxy-env.ts +62 -0
- package/adapters/mcp/readiness-projection.ts +24 -0
- package/adapters/mcp/redaction.ts +21 -0
- package/adapters/mcp/repo.ts +24 -0
- package/adapters/mcp/runtime-gateway/artifact-adapter.ts +52 -0
- package/adapters/mcp/runtime-gateway/context-adapter.ts +829 -0
- package/adapters/mcp/runtime-gateway/controller-authority-adapter.ts +191 -0
- package/adapters/mcp/runtime-gateway/engineering-preconditions.ts +147 -0
- package/adapters/mcp/runtime-gateway/execution-control-adapter.ts +28 -0
- package/adapters/mcp/runtime-gateway/execution-tools.ts +207 -0
- package/adapters/mcp/runtime-gateway/filesystem-adapter.ts +38 -0
- package/adapters/mcp/runtime-gateway/gateway-execution-adapter.ts +411 -0
- package/adapters/mcp/runtime-gateway/legacy-ios-tool-adapter.ts +105 -0
- package/adapters/mcp/runtime-gateway/maintenance-adapter.ts +44 -0
- package/adapters/mcp/runtime-gateway/model-adapter.ts +63 -0
- package/adapters/mcp/runtime-gateway/persisted-check-process.ts +3 -0
- package/adapters/mcp/runtime-gateway/plugin-adapter.ts +308 -0
- package/adapters/mcp/runtime-gateway/process-tools.ts +289 -0
- package/adapters/mcp/runtime-gateway/protected-computer-adapter.ts +76 -0
- package/adapters/mcp/runtime-gateway/recovery-adapter.ts +268 -0
- package/adapters/mcp/runtime-gateway/recovery-client-adapter.ts +70 -0
- package/adapters/mcp/runtime-gateway/recovery-tool-contract.ts +99 -0
- package/adapters/mcp/runtime-gateway/repository-compat-adapter.ts +110 -0
- package/adapters/mcp/runtime-gateway/result-adapter.ts +108 -0
- package/adapters/mcp/runtime-gateway/router.ts +728 -0
- package/adapters/mcp/runtime-gateway/runtime-observation-adapter.ts +1251 -0
- package/adapters/mcp/runtime-gateway/runtime-tool-definitions.ts +634 -0
- package/adapters/mcp/runtime-gateway/runtime-tool-shared.ts +27 -0
- package/adapters/mcp/runtime-gateway/runtime-tools.ts +68 -0
- package/adapters/mcp/runtime-gateway/scheduler-adapter.ts +186 -0
- package/adapters/mcp/runtime-gateway/shared-adapter.ts +25 -0
- package/adapters/mcp/runtime-gateway/status-inbox-adapter.ts +870 -0
- package/adapters/mcp/runtime-gateway/work-adapter.ts +3186 -0
- package/adapters/mcp/runtime-gateway/work-compat-adapter.ts +502 -0
- package/adapters/mcp/runtime-gateway/work-validation-reconciler.ts +1 -0
- package/adapters/mcp/server.ts +1048 -0
- package/adapters/mcp/setup.ts +791 -0
- package/adapters/mcp/tool-capability-cache.ts +87 -0
- package/adapters/mcp/tool-mapping/access-tools.ts +387 -0
- package/adapters/mcp/tool-mapping/legacy-tool-service.ts +5265 -0
- package/adapters/mcp/tool-mapping/post-finalize-work-attribution.ts +150 -0
- package/adapters/mcp/tool-mapping/repository-tools.ts +1691 -0
- package/adapters/mcp/tool-mapping/tools.ts +9 -0
- package/adapters/mcp/tool-mapping/umbrella-tools.ts +117 -0
- package/adapters/mcp/toolset-names.ts +74 -0
- package/adapters/mcp/toolset-selection.ts +41 -0
- package/adapters/mcp/toolset.ts +336 -0
- package/adapters/mcp/transports/http.ts +1456 -0
- package/adapters/mcp/transports/session-registry.ts +513 -0
- package/adapters/mcp/transports/stdio.ts +8 -0
- package/adapters/mcp/tunnels/openai-secure-tunnel.ts +139 -0
- package/adapters/mcp/types.ts +41 -0
- package/assets/AGENTS.md +2 -16
- package/assets/CLAUDE.md +2 -16
- package/assets/hooks/AGENTS.md +3 -14
- package/assets/hooks/CLAUDE.md +3 -14
- package/assets/hooks/hook-input.sh +9 -2
- package/assets/hooks/lib/workflow-state.sh +28 -10
- package/assets/hooks/prompt-guard.sh +63 -37
- package/assets/hooks/security-sentinel.sh +2 -1
- package/assets/hooks/session-start-context.sh +7 -5
- package/assets/hooks/stop-orchestrator.sh +13 -3
- package/assets/initializer-question-pack.v4.json +11 -7
- package/assets/partials/05-workflow.partial.md +18 -73
- package/assets/partials/07-footer.partial.md +1 -1
- package/assets/partials-agents/02-operating-mode.partial.md +1 -2
- package/assets/partials-agents/04-task-protocol.partial.md +14 -59
- package/assets/partials-agents/06-quality-safety.partial.md +11 -16
- package/assets/partials-agents/08-deep-docs.partial.md +1 -2
- package/assets/plan-map.json +11 -11
- package/assets/plugin-registry.v1.json +9 -3
- package/assets/plugin-trust-policy.v1.json +6 -0
- package/assets/recovery/ForgeRecovery.ps1 +100 -0
- package/assets/recovery/forge-wsl-rescue +271 -0
- package/assets/reference-configs/development-protocol.md +11 -15
- package/assets/reference-configs/document-generation.md +1 -1
- package/assets/reference-configs/global-working-rules.md +9 -1
- package/assets/reference-configs/harness-overview.md +22 -112
- package/assets/reference-configs/hook-operations.md +1 -1
- package/assets/reference-configs/sprint-contracts.md +2 -2
- package/assets/skill-version.json +2 -2
- package/assets/templates/helpers/architecture-event.ts +3 -34
- package/assets/templates/helpers/architecture-queue.sh +61 -10
- package/assets/templates/helpers/archive-workflow.sh +90 -180
- package/assets/templates/helpers/capability-config.ts +8 -11
- package/assets/templates/helpers/capability-resolver.ts +37 -19
- package/assets/templates/helpers/capture-plan.sh +2 -2
- package/assets/templates/helpers/check-architecture-sync.sh +1 -9
- package/assets/templates/helpers/check-task-sync.sh +82 -11
- package/assets/templates/helpers/check-task-workflow.sh +1 -3
- package/assets/templates/helpers/context-contract-sync.sh +1 -12
- package/assets/templates/helpers/contract-worktree.sh +26 -24
- package/assets/templates/helpers/ensure-task-workflow.sh +6 -8
- package/assets/templates/helpers/inspect-project-state.ts +1 -1
- package/assets/templates/helpers/migrate-project-template.sh +1 -1
- package/assets/templates/helpers/migrate-workflow-docs.ts +53 -239
- package/assets/templates/helpers/new-plan.sh +2 -2
- package/assets/templates/helpers/plan-to-todo.sh +3 -31
- package/assets/templates/helpers/sprint-backlog.sh +1 -1
- package/assets/templates/helpers/switch-plan.sh +1 -1
- package/assets/templates/helpers/workflow-contract.ts +1 -0
- package/assets/workflow-contract.v1.json +63 -171
- package/assets/workflows/xiaohongshu/xiaohongshu.collect-metrics.v1.draft.json +118 -0
- package/assets/workflows/xiaohongshu/xiaohongshu.publish-image-note.v1.draft.json +316 -0
- package/assets/workflows/xiaohongshu/xiaohongshu.publish-long-text.v1.draft.json +324 -0
- package/docs/architecture/CURRENT.md +148 -13
- package/docs/architecture/EVOLUTION.md +13 -0
- package/docs/architecture/decisions/20260801-controller-home-sqlite-state.md +2 -1
- package/docs/architecture/decisions/20260831-independent-windows-wsl-host-rescue.md +59 -0
- package/docs/architecture/decisions/20260904-ios-device-migration-baseline.md +120 -0
- package/docs/architecture/decisions/20260904-repository-runtime-data-boundary.md +42 -0
- package/docs/architecture/decisions/20260907-assistant-learning-loop.md +35 -0
- package/docs/architecture/decisions/20260907-controller-round-transition-policy.md +296 -0
- package/docs/architecture/decisions/20260907-maintenance-diagnosis-bounded-read.md +39 -0
- package/docs/architecture/domains/verification.md +7 -0
- package/docs/architecture/domains/workflow-engine.md +1 -1
- package/docs/architecture/index.md +3 -3
- package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +19 -8
- package/docs/architecture/requests/2026-09-04-computer-plugin-workflow-taxonomy.md +124 -0
- package/docs/forge-chatgpt-mcp-setup.md +5 -3
- package/docs/forge-plugin-management.md +20 -4
- package/docs/forge-runtime-self-healing-loop.md +2 -0
- package/docs/operations/benchmark-cross-version-freeze.md +38 -0
- package/docs/operations/controller-browser-plugin.md +3 -1
- package/docs/operations/homebrew.md +2 -2
- package/docs/operations/platform-support.md +16 -8
- package/docs/operations/platform-support.zh-CN.md +2 -2
- package/docs/operations/standalone-disaster-recovery.md +26 -1
- package/docs/operations/uu-remote-rescue.md +41 -0
- package/docs/operations/windows-wsl-host-rescue.md +133 -0
- package/docs/public-usage-guide.md +1 -1
- package/docs/public-usage-guide.zh-CN.md +1 -1
- package/docs/tutorials/01-install-and-start.md +1 -1
- package/docs/tutorials/01-install-and-start.zh-CN.md +1 -1
- package/docs/tutorials/02-connect-chatgpt.md +4 -4
- package/docs/tutorials/02-connect-chatgpt.zh-CN.md +4 -4
- package/docs/wiki/Implementation.md +1 -1
- package/package.json +23 -14
- package/packages/kernel/controller/api/index.ts +2 -0
- package/packages/kernel/controller/application/controller-round-service.ts +29 -0
- package/packages/kernel/controller/application/controller-service.ts +30 -0
- package/packages/kernel/controller/domain/controller-round-transition-policy.ts +308 -0
- package/packages/kernel/controller/domain/controller-round.ts +107 -0
- package/packages/kernel/controller/domain/execution-quality.ts +133 -0
- package/packages/kernel/controller/domain/types.ts +56 -0
- package/packages/kernel/controller/index.ts +1 -0
- package/packages/kernel/controller/infrastructure/controller-binding-store.ts +84 -0
- package/packages/kernel/controller/infrastructure/controller-round-store.ts +1384 -0
- package/packages/kernel/controller/infrastructure/controller-session-store.ts +578 -0
- package/packages/kernel/controller/ports/controller-host.ts +16 -0
- package/packages/kernel/goal/api/index.ts +1 -0
- package/packages/kernel/goal/domain/types.ts +40 -0
- package/packages/kernel/goal/index.ts +1 -0
- package/packages/kernel/identity/api/index.ts +5 -0
- package/packages/kernel/identity/application/identity-service.ts +67 -0
- package/packages/kernel/identity/domain/scope.ts +63 -0
- package/packages/kernel/identity/domain/semantic-sync.ts +81 -0
- package/packages/kernel/identity/domain/types.ts +44 -0
- package/packages/kernel/identity/domain/workspace.ts +56 -0
- package/packages/kernel/identity/index.ts +1 -0
- package/packages/kernel/identity/infrastructure/identity-store.ts +75 -0
- package/packages/kernel/memory/api/index.ts +5 -0
- package/packages/kernel/memory/application/experience-service.ts +118 -0
- package/packages/kernel/memory/application/outcome-service.ts +77 -0
- package/packages/kernel/memory/domain/experience.ts +103 -0
- package/packages/kernel/memory/domain/operational-prior.ts +92 -0
- package/packages/kernel/memory/index.ts +1 -0
- package/packages/kernel/progression/api/index.ts +2 -0
- package/packages/kernel/progression/application/projector.ts +211 -0
- package/packages/kernel/progression/domain/types.ts +116 -0
- package/packages/kernel/progression/index.ts +1 -0
- package/packages/kernel/scheduler/api/index.ts +6 -0
- package/packages/kernel/scheduler/application/continuation-service.ts +212 -0
- package/packages/kernel/scheduler/application/eligibility.ts +252 -0
- package/packages/kernel/scheduler/application/schedule-service.ts +24 -0
- package/packages/kernel/scheduler/application/settlement.ts +197 -0
- package/packages/kernel/scheduler/domain/continuation.ts +38 -0
- package/packages/kernel/scheduler/domain/schedule.ts +149 -0
- package/packages/kernel/scheduler/index.ts +1 -0
- package/packages/kernel/scheduler/infrastructure/continuation-dispatch-store.ts +38 -0
- package/packages/kernel/scheduler/infrastructure/schedule-store.ts +785 -0
- package/packages/kernel/work/api/index.ts +15 -0
- package/packages/kernel/work/application/work-service.ts +148 -0
- package/packages/kernel/work/domain/admission-policy.ts +34 -0
- package/packages/kernel/work/domain/check-receipt.ts +43 -0
- package/packages/kernel/work/domain/context-closure-receipt.ts +32 -0
- package/packages/kernel/work/domain/engineering-contracts.ts +157 -0
- package/packages/kernel/work/domain/engineering-design.ts +281 -0
- package/packages/kernel/work/domain/engineering-profile.ts +242 -0
- package/packages/kernel/work/domain/execution-concurrency.ts +254 -0
- package/packages/kernel/work/domain/execution-snapshot.ts +33 -0
- package/packages/kernel/work/domain/implementation-review.ts +510 -0
- package/packages/kernel/work/domain/project-engineering-contract.ts +202 -0
- package/packages/kernel/work/domain/repository-completion-receipt.ts +73 -0
- package/packages/kernel/work/domain/state-machine.ts +259 -0
- package/packages/kernel/work/domain/types.ts +528 -0
- package/packages/kernel/work/index.ts +1 -0
- package/packages/kernel/work/infrastructure/work-contract-store.ts +1876 -0
- package/packages/kernel/work/ports/work-contract-store.ts +18 -0
- package/packages/plugin-runtime/browser/index.ts +3 -0
- package/packages/plugin-runtime/browser/provider-registry.ts +110 -0
- package/packages/plugin-runtime/browser/runtime-contract.ts +150 -0
- package/packages/plugin-runtime/browser/session-authority.ts +61 -0
- package/packages/plugin-runtime/computer/index.ts +6 -0
- package/packages/plugin-runtime/computer/provider-error.ts +26 -0
- package/packages/plugin-runtime/computer/provider-registration.ts +52 -0
- package/packages/plugin-runtime/computer/provider-registry.ts +63 -0
- package/packages/plugin-runtime/computer/provider.ts +19 -0
- package/packages/plugin-runtime/computer/target-authority.ts +190 -0
- package/packages/plugin-runtime/computer/target-persistence.ts +54 -0
- package/packages/plugin-runtime/external/index.ts +1 -0
- package/packages/plugin-runtime/external/unix-jsonl-transport.ts +457 -0
- package/packages/plugin-runtime/index.ts +3 -0
- package/packages/protocols/browser/index.ts +1 -0
- package/packages/protocols/browser/session.ts +98 -0
- package/packages/protocols/computer/contract.ts +199 -0
- package/packages/protocols/computer/index.ts +1 -0
- package/packages/protocols/failure/contract.ts +158 -0
- package/packages/protocols/failure/index.ts +1 -0
- package/packages/protocols/handoff/index.ts +1 -0
- package/packages/protocols/handoff/status.ts +22 -0
- package/packages/protocols/mcp/execution-context.ts +11 -0
- package/packages/protocols/mcp/index.ts +2 -0
- package/packages/protocols/mcp/tool-contract.ts +42 -0
- package/packages/workflow-runtime/api/index.ts +2 -0
- package/packages/workflow-runtime/application/interpreter.ts +342 -0
- package/packages/workflow-runtime/domain/workflow-asset.ts +223 -0
- package/packages/workflow-runtime/index.ts +1 -0
- package/scripts/assemble-template.ts +1 -1
- package/scripts/benchmark-computer-hotpath.ts +255 -0
- package/scripts/benchmark-controller-round2.ts +54 -2
- package/scripts/benchmark-ios-agent-device.ts +3 -6
- package/scripts/benchmark-mcp-transport.ts +3 -4
- package/scripts/benchmark-operational-memory-activation.ts +235 -0
- package/scripts/benchmark-operational-shadow.ts +28 -0
- package/scripts/bootstrap-runtime-maintenance-recovery.sh +4 -1
- package/scripts/canary-chatgpt-provider-delivery.ts +97 -0
- package/scripts/canary-controller-semantic-wait.ts +195 -0
- package/scripts/canary-global-hook.sh +2 -2
- package/scripts/check-mcp-compatibility.ts +173 -7
- package/scripts/check-platform-support.mjs +28 -2
- package/scripts/check-public-docs.sh +1 -1
- package/scripts/check-release-readiness.sh +1 -1
- package/scripts/check-repository-hygiene.mjs +92 -0
- package/scripts/check-runtime-architecture.mjs +1206 -95
- package/scripts/check-source-duplication.mjs +9 -1
- package/scripts/check-tarball-install-smoke.sh +98 -15
- package/scripts/controller-ngrok-rotation.sh +5 -4
- package/scripts/create-project-dirs.sh +10 -74
- package/scripts/forge-local-recovery-helper.mjs +204 -0
- package/scripts/forge-uu-remote-rescue-helper.d.mts +31 -0
- package/scripts/forge-uu-remote-rescue-helper.mjs +409 -0
- package/scripts/forge-windows-host-recovery-helper.mjs +136 -0
- package/scripts/hook-shim.sh +2 -2
- package/scripts/init-project.sh +12 -70
- package/scripts/install-desktop-operator-registration.ts +4 -2
- package/scripts/install-independent-windows-wsl-recovery.ts +125 -0
- package/scripts/install-standalone-recovery.ts +122 -10
- package/scripts/install-uu-remote-rescue-registration.ts +74 -0
- package/scripts/install-windows-host-recovery-registration.ts +22 -0
- package/scripts/lib/controller-home-env.sh +6 -3
- package/scripts/lib/project-init-lib.sh +39 -582
- package/scripts/migrate-chatgpt-automation-schedules.ts +2 -2
- package/scripts/migrate-windows-wsl-controller-home.ts +306 -0
- package/scripts/open-source-audit-allowlist.txt +12 -1
- package/scripts/portability-bindings.mjs +70 -0
- package/scripts/run-bounded-command.mjs +55 -0
- package/scripts/run-bun-test-file.ts +22 -6
- package/scripts/run-governed-gate.ts +4 -1
- package/scripts/smoke-mcp-http-runtime.ts +1 -1
- package/scripts/smoke-runtime-recovery.ts +34 -1
- package/scripts/smoke-schedule-engine.ts +1 -1
- package/scripts/stage-runtime-release.ts +6 -1
- package/scripts/test-governance.ts +51 -2
- package/src/cli/agent-jobs/job-manager.ts +10 -1
- package/src/cli/chatgpt-browser/bind-server.ts +2 -1
- package/src/cli/chatgpt-browser/binding.ts +20 -10
- package/src/cli/chatgpt-browser/bridge-extension.ts +75 -0
- package/src/cli/chatgpt-browser/bridge-provider.ts +376 -11
- package/src/cli/chatgpt-browser/engine.ts +10 -11
- package/src/cli/chatgpt-browser/native-provider.ts +24 -83
- package/src/cli/chatgpt-browser/oracle-provider.ts +5 -3
- package/src/cli/chatgpt-browser/types.ts +1 -0
- package/src/cli/commands/bootstrap-control.ts +334 -0
- package/src/cli/commands/brain-assistant.ts +38 -0
- package/src/cli/commands/brain.ts +193 -40
- package/src/cli/commands/chatgpt.ts +127 -0
- package/src/cli/commands/computer.ts +437 -0
- package/src/cli/commands/doctor.ts +18 -1
- package/src/cli/commands/init-hook.ts +71 -61
- package/src/cli/commands/init.ts +3 -1
- package/src/cli/commands/mcp.ts +16 -10
- package/src/cli/commands/plugin.ts +38 -12
- package/src/cli/commands/recovery.ts +493 -80
- package/src/cli/commands/runtime.ts +9 -4
- package/src/cli/commands/setup-profile.ts +89 -39
- package/src/cli/commands/status.ts +2 -1
- package/src/cli/controller/check-runner.ts +487 -98
- package/src/cli/controller/context/types.ts +7 -1
- package/src/cli/controller/context-pack.ts +23 -3
- package/src/cli/controller/issue-store.ts +1 -1
- package/src/cli/controller/systemd-user.ts +110 -0
- package/src/cli/controller/types.ts +10 -70
- package/src/cli/controller/worklog.ts +67 -13
- package/src/cli/editing/edit-operation-contract.ts +92 -0
- package/src/cli/editing/edit-session.ts +289 -13
- package/src/cli/github/plugin.ts +17 -12
- package/src/cli/hook/route-registry.ts +3 -5
- package/src/cli/hook/runtime.ts +49 -3
- package/src/cli/index.ts +3 -0
- package/src/cli/local-bridge/connector-freshness.ts +2 -2
- package/src/cli/local-bridge/console-automations.ts +2 -2
- package/src/cli/local-bridge/facade-api.ts +41 -14
- package/src/cli/local-bridge/job-store.ts +1 -1
- package/src/cli/local-bridge/server.ts +24 -27
- package/src/cli/mcp/access-mode.ts +2 -132
- package/src/cli/mcp/access-tools.ts +2 -387
- package/src/cli/mcp/audit.ts +2 -32
- package/src/cli/mcp/auth.ts +2 -404
- package/src/cli/mcp/instructions.ts +2 -34
- package/src/cli/mcp/legacy-context.ts +2 -16
- package/src/cli/mcp/legacy-tool-service.ts +2 -5308
- package/src/cli/mcp/multi-repository.ts +2 -436
- package/src/cli/mcp/oauth.ts +2 -375
- package/src/cli/mcp/openai-secure-tunnel.ts +2 -0
- package/src/cli/mcp/paths.ts +2 -140
- package/src/cli/mcp/policy.ts +2 -232
- package/src/cli/mcp/post-finalize-work-attribution.ts +2 -150
- package/src/cli/mcp/proxy-env.ts +2 -62
- package/src/cli/mcp/readiness-projection.ts +2 -24
- package/src/cli/mcp/redaction.ts +2 -21
- package/src/cli/mcp/repo.ts +2 -24
- package/src/cli/mcp/repository-tools.ts +2 -1613
- package/src/cli/mcp/server.ts +2 -951
- package/src/cli/mcp/setup.ts +2 -737
- package/src/cli/mcp/tool-capability-cache.ts +2 -87
- package/src/cli/mcp/tool-contract.ts +2 -14
- package/src/cli/mcp/tools.ts +2 -9
- package/src/cli/mcp/toolset-names.ts +2 -70
- package/src/cli/mcp/toolset-selection.ts +2 -41
- package/src/cli/mcp/toolset.ts +2 -336
- package/src/cli/mcp/transports/AGENTS.md +1 -10
- package/src/cli/mcp/transports/CLAUDE.md +1 -10
- package/src/cli/mcp/transports/http.ts +2 -1414
- package/src/cli/mcp/transports/session-registry.ts +2 -490
- package/src/cli/mcp/transports/stdio.ts +2 -8
- package/src/cli/mcp/types.ts +2 -41
- package/src/cli/mcp/umbrella-tools.ts +2 -117
- package/src/cli/repositories/command-executor.ts +64 -18
- package/src/cli/repositories/command-scope.ts +117 -1
- package/src/cli/repositories/controller-home.ts +119 -1
- package/src/cli/repositories/identity.ts +13 -0
- package/src/cli/repositories/registry.ts +68 -49
- package/src/cli/repositories/repository-snapshot.ts +53 -7
- package/src/cli/repositories/runtime-storage.ts +27 -2
- package/src/cli/repositories/safe-patch.ts +34 -38
- package/src/cli/repositories/selected-path-actions.ts +22 -2
- package/src/cli/repositories/structured-git.ts +105 -25
- package/src/cli/runtime/helper-runner.ts +7 -0
- package/src/cli/runtime-invocation.ts +8 -1
- package/src/cli/tools/codegraph.ts +44 -25
- package/src/core/adoption/manifest-templates.ts +1 -1
- package/src/core/adoption/plan.ts +3 -26
- package/src/core/adoption/workflow-contract-plan.ts +17 -9
- package/src/effects/process-runner.ts +8 -14
- package/src/runtime/context/assistant-context.ts +154 -0
- package/src/runtime/context/assistant-work-context.ts +147 -0
- package/src/runtime/context/codegraph-cache-boundary.ts +255 -0
- package/src/runtime/context/codegraph-read-provider.ts +55 -22
- package/src/runtime/context/codegraph-sidecar.cjs +8 -1
- package/src/runtime/context/context-closure.ts +268 -0
- package/src/runtime/context/context-plane.ts +211 -0
- package/src/runtime/context/project-engineering-contract.ts +60 -0
- package/src/runtime/control-plane/bootstrap/capability-provider.ts +74 -0
- package/src/runtime/control-plane/bootstrap/control-api.ts +57 -0
- package/src/runtime/control-plane/bootstrap/index.ts +5 -0
- package/src/runtime/control-plane/bootstrap/state-machine.ts +141 -0
- package/src/runtime/control-plane/bootstrap/store.ts +21 -0
- package/src/runtime/control-plane/bootstrap/types.ts +87 -0
- package/src/runtime/control-plane/cleanup-artifact-retention.ts +328 -0
- package/src/runtime/control-plane/codegraph-cache-retention.ts +164 -0
- package/src/runtime/control-plane/concurrency/work-execution-concurrency.ts +320 -0
- package/src/runtime/control-plane/execution/content-equivalent-commit-authority.ts +169 -0
- package/src/runtime/control-plane/execution/controller-authority-recovery.ts +350 -0
- package/src/runtime/control-plane/execution/direct-canonical-work-reconciliation.ts +85 -0
- package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +369 -99
- package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +103 -15
- package/src/runtime/control-plane/execution/implementation-review-content.ts +95 -0
- package/src/runtime/control-plane/execution/repository-work-attribution.ts +2 -2
- package/src/runtime/control-plane/execution/retained-work-resume.ts +336 -6
- package/src/runtime/control-plane/execution/validation.ts +28 -3
- package/src/runtime/control-plane/execution/work-authority-reconciler.ts +116 -0
- package/src/runtime/control-plane/execution/work-completion-authority.ts +44 -2
- package/src/runtime/control-plane/execution/work-evidence-policy.ts +34 -3
- package/src/runtime/control-plane/execution/work-execution-support.ts +42 -22
- package/src/runtime/control-plane/execution/work-finalization-service.ts +1103 -166
- package/src/runtime/control-plane/execution/work-handle-authority.ts +219 -22
- package/src/runtime/control-plane/execution/work-handle-store.ts +77 -0
- package/src/runtime/control-plane/execution/work-head-settlement.ts +75 -0
- package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +145 -23
- package/src/runtime/control-plane/execution/work-operation-service.ts +49 -13
- package/src/runtime/control-plane/execution/work-preparation-service.ts +31 -15
- package/src/runtime/control-plane/execution/work-remote-delivery.ts +1 -1
- package/src/runtime/control-plane/execution/work-task-receipt.ts +20 -1
- package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +407 -76
- package/src/runtime/control-plane/execution/work-validation-reconciler.ts +88 -18
- package/src/runtime/control-plane/execution/work-verification-context.ts +1 -1
- package/src/runtime/control-plane/execution/work-verification-service.ts +258 -4
- package/src/runtime/control-plane/execution/work-verification-snapshot.ts +200 -15
- package/src/runtime/control-plane/facade/capability-registry.ts +21 -10
- package/src/runtime/control-plane/facade/controller-round-relay.ts +3 -1118
- package/src/runtime/control-plane/facade/controller-session-store.ts +2 -488
- package/src/runtime/control-plane/facade/goal-workloop-access.ts +6 -3
- package/src/runtime/control-plane/facade/goal-workloop.ts +972 -137
- package/src/runtime/control-plane/facade/handoff-inbox-application.ts +158 -0
- package/src/runtime/control-plane/facade/handoff-inbox-store.ts +10 -0
- package/src/runtime/control-plane/facade/index.ts +4 -2
- package/src/runtime/control-plane/facade/operation-digest.ts +22 -58
- package/src/runtime/control-plane/facade/plan-contract-store.ts +1004 -38
- package/src/runtime/control-plane/facade/repository-work-admission.ts +19 -1
- package/src/runtime/control-plane/facade/requirement-authority.ts +218 -0
- package/src/runtime/control-plane/facade/requirement-board.ts +6 -1
- package/src/runtime/control-plane/facade/rh-work-operation-contract.ts +48 -0
- package/src/runtime/control-plane/facade/semantic-admission.ts +8 -16
- package/src/runtime/control-plane/facade/suggested-actions.ts +4 -1
- package/src/runtime/control-plane/facade/types.ts +189 -532
- package/src/runtime/control-plane/facade/work-admission-policy.ts +84 -35
- package/src/runtime/control-plane/facade/work-contract-store.ts +2 -1043
- package/src/runtime/control-plane/facade/work-implementation-review.ts +2 -0
- package/src/runtime/control-plane/facade/work-state-machine.ts +2 -250
- package/src/runtime/control-plane/generated-cache-retention.ts +239 -18
- package/src/runtime/control-plane/global-scheduler/durable-admission.ts +12 -1
- package/src/runtime/control-plane/global-scheduler/maintenance.ts +76 -13
- package/src/runtime/control-plane/global-scheduler/periodic-cleanup-entry.ts +55 -0
- package/src/runtime/control-plane/global-scheduler/periodic-cleanup-process.ts +98 -0
- package/src/runtime/control-plane/global-scheduler/reconciliation.ts +1 -1
- package/src/runtime/control-plane/global-scheduler/scheduler-worker-entry.ts +10 -0
- package/src/runtime/control-plane/global-scheduler/scheduler.ts +173 -81
- package/src/runtime/control-plane/global-scheduler/worker-launch.ts +18 -6
- package/src/runtime/control-plane/launcher/chatgpt-round-continuation.ts +397 -0
- package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +2 -177
- package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +301 -560
- package/src/runtime/control-plane/launcher/provider-mcp-bootstrap.ts +20 -4
- package/src/runtime/control-plane/launcher/thin-launcher.ts +50 -4
- package/src/runtime/control-plane/lifecycle-retention-metrics.ts +60 -0
- package/src/runtime/control-plane/persistence/experience-store.ts +169 -0
- package/src/runtime/control-plane/persistence/operational-prior-store.ts +348 -0
- package/src/runtime/control-plane/persistence/requirement-store.ts +30 -22
- package/src/runtime/control-plane/persistence/sqlite-store.ts +545 -76
- package/src/runtime/control-plane/persistence/workflow-content-store.ts +98 -0
- package/src/runtime/control-plane/persistence/workflow-registry-store.ts +186 -0
- package/src/runtime/control-plane/persistence/workflow-run-store.ts +123 -0
- package/src/runtime/control-plane/release-retention.ts +56 -16
- package/src/runtime/control-plane/repository-namespace-retention.ts +208 -0
- package/src/runtime/control-plane/routing/route-policy.ts +19 -0
- package/src/runtime/control-plane/runtime-cleanup.ts +574 -37
- package/src/runtime/control-plane/runtime-generation.ts +6 -0
- package/src/runtime/control-plane/workspace/semantic-sync-service.ts +465 -0
- package/src/runtime/control-plane/workspace/workspace-store.ts +80 -0
- package/src/runtime/diagnostics/incident-repair.ts +406 -0
- package/src/runtime/diagnostics/mcp-timing.ts +125 -6
- package/src/runtime/diagnostics/performance.ts +6 -14
- package/src/runtime/diagnostics/read-only-tool.ts +1 -1
- package/src/runtime/evidence/artifact-store.ts +65 -2
- package/src/runtime/evidence/operational-shadow.ts +38 -0
- package/src/runtime/evidence/process-check-receipt.ts +5 -43
- package/src/runtime/evidence/sensitive-output.ts +4 -4
- package/src/runtime/execution/jobs/receipt-recovery.ts +1 -1
- package/src/runtime/execution/jobs/store.ts +121 -2
- package/src/runtime/execution/managed-workspace.ts +55 -5
- package/src/runtime/execution/process-runtime/canary.ts +19 -5
- package/src/runtime/execution/process-runtime/check-classification.ts +48 -0
- package/src/runtime/execution/process-runtime/check-facade.ts +23 -9
- package/src/runtime/execution/process-runtime/check-receipt.ts +1 -1
- package/src/runtime/execution/process-runtime/check-result-retention.ts +132 -0
- package/src/runtime/execution/process-runtime/check-result.ts +73 -2
- package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +42 -3
- package/src/runtime/execution/process-runtime/check-storage.ts +187 -0
- package/src/runtime/execution/process-runtime/command-facade.ts +38 -87
- package/src/runtime/execution/process-runtime/index.ts +1 -0
- package/src/runtime/execution/process-runtime/interactive-admission.ts +2 -1
- package/src/runtime/execution/process-runtime/lightweight-managed.ts +32 -1
- package/src/runtime/execution/process-runtime/persisted-check.ts +128 -21
- package/src/runtime/execution/process-runtime/resource-claims.ts +60 -3
- package/src/runtime/execution/process-runtime/runtime.ts +132 -15
- package/src/runtime/execution/process-runtime/types.ts +22 -0
- package/src/runtime/execution/workers/executor.ts +16 -13
- package/src/runtime/execution/workers/worker-entry.ts +1 -1
- package/src/runtime/gateway/mcp/execution-tools.ts +2 -207
- package/src/runtime/gateway/mcp/legacy-ios-tool-adapter.ts +2 -61
- package/src/runtime/gateway/mcp/persisted-check-process.ts +2 -3
- package/src/runtime/gateway/mcp/process-tools.ts +2 -289
- package/src/runtime/gateway/mcp/router.ts +2 -1125
- package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +2 -565
- package/src/runtime/gateway/mcp/runtime-tools.ts +2 -6655
- package/src/runtime/gateway/mcp/work-validation-reconciler.ts +2 -1
- package/src/runtime/maintenance/cleanup.ts +38 -24
- package/src/runtime/platform/browser-product-discovery.ts +174 -0
- package/src/runtime/platform/computer-platform.ts +14 -0
- package/src/runtime/platform/executable-discovery.ts +54 -0
- package/src/runtime/platform/fetch-http-server.ts +90 -0
- package/src/runtime/platform/service-manager.ts +138 -0
- package/src/runtime/platform/windows-host-discovery.ts +118 -0
- package/src/runtime/platform/windows-recovery-deployment.ts +67 -0
- package/src/runtime/plugins/action-application.ts +135 -0
- package/src/runtime/plugins/app-store-connect-adapter.ts +9 -21
- package/src/runtime/plugins/browser-adapter.ts +140 -100
- package/src/runtime/plugins/browser-automation-service.ts +15 -36
- package/src/runtime/plugins/browser-macos-bridge.ts +69 -21
- package/src/runtime/plugins/browser-manifest-surface.ts +30 -6
- package/src/runtime/plugins/browser-native-session-service.ts +1 -1
- package/src/runtime/plugins/browser-provider-registry.ts +2 -109
- package/src/runtime/plugins/browser-registration.ts +16 -0
- package/src/runtime/plugins/browser-runtime-contract.ts +2 -154
- package/src/runtime/plugins/browser-runtime.ts +41 -9
- package/src/runtime/plugins/browser-session-authority.ts +101 -291
- package/src/runtime/plugins/browser-session-legacy-migration.ts +203 -0
- package/src/runtime/plugins/browser-session-store.ts +229 -36
- package/src/runtime/plugins/browser-session-types.ts +2 -98
- package/src/runtime/plugins/capability-authorization-grants.ts +17 -0
- package/src/runtime/plugins/catalog-trust.ts +82 -0
- package/src/runtime/plugins/computer-registration.ts +839 -0
- package/src/runtime/plugins/config-store.ts +119 -0
- package/src/runtime/plugins/desktop-operator-external-policy.ts +932 -0
- package/src/runtime/plugins/desktop-operator-registration.ts +111 -8
- package/src/runtime/plugins/errors.ts +8 -1
- package/src/runtime/plugins/external-adapter.ts +45 -322
- package/src/runtime/plugins/external-provider-policy.ts +48 -0
- package/src/runtime/plugins/external-registration.ts +9 -1
- package/src/runtime/plugins/external-unix-socket-probe.cjs +9 -3
- package/src/runtime/plugins/external-unix-socket.ts +64 -193
- package/src/runtime/plugins/first-party-external-registration.ts +163 -0
- package/src/runtime/plugins/first-party-registry.ts +3 -1
- package/src/runtime/plugins/github-adapter.ts +1 -2
- package/src/runtime/plugins/gmail-adapter.ts +6 -6
- package/src/runtime/plugins/google-calendar-adapter.ts +6 -6
- package/src/runtime/plugins/google-shared.ts +31 -46
- package/src/runtime/plugins/google-tasks-adapter.ts +6 -6
- package/src/runtime/plugins/ios-adapter.ts +1 -2
- package/src/runtime/plugins/local-system-adapter.ts +180 -0
- package/src/runtime/plugins/macos-capability-broker.ts +15 -105
- package/src/runtime/plugins/resend-adapter.ts +15 -24
- package/src/runtime/plugins/store.ts +493 -51
- package/src/runtime/plugins/types.ts +8 -1
- package/src/runtime/plugins/uu-remote-rescue-registration.ts +155 -0
- package/src/runtime/plugins/windows-host-recovery-install.ts +68 -0
- package/src/runtime/plugins/windows-host-recovery-registration.ts +85 -0
- package/src/runtime/plugins/xiaohongshu-publish.ts +86 -430
- package/src/runtime/projections/controller-context.ts +12 -6
- package/src/runtime/projections/git-status-sampler.ts +73 -22
- package/src/runtime/projections/materialized-view.ts +14 -8
- package/src/runtime/recovery/capability-action-application.ts +228 -0
- package/src/runtime/recovery/index.ts +3 -0
- package/src/runtime/recovery/lifecycle-inventory.ts +338 -0
- package/src/runtime/recovery/local-jobs-repair.ts +14 -14
- package/src/runtime/recovery/maintenance-executor.ts +358 -76
- package/src/runtime/recovery/quarantine-retention.ts +256 -0
- package/src/runtime/release/identity.ts +6 -0
- package/src/runtime/resources/leases/store.ts +1 -1
- package/src/runtime/root/active-execution-power-assertion.ts +139 -0
- package/src/runtime/root/assistant-learning-loop.ts +45 -0
- package/src/runtime/root/browser-session-composition.ts +59 -0
- package/src/runtime/root/computer-composition.ts +191 -0
- package/src/runtime/root/computer-target-composition.ts +16 -0
- package/src/runtime/root/computer-target-persistence.ts +39 -0
- package/src/runtime/root/controller-round-composition.ts +107 -0
- package/src/runtime/root/controller-services.ts +2 -0
- package/src/runtime/root/entry.ts +4 -3
- package/src/runtime/root/gateway-adapter.ts +3 -3
- package/src/runtime/root/local-bridge.ts +5 -2
- package/src/runtime/root/mcp-transport.ts +5 -5
- package/src/runtime/root/package-connector-service.ts +108 -56
- package/src/runtime/root/package-runtime-release.ts +85 -18
- package/src/runtime/root/package-runtime-service.ts +60 -97
- package/src/runtime/root/release-execution-canary.ts +71 -0
- package/src/runtime/root/release-loader.ts +68 -0
- package/src/runtime/root/release-manifest.ts +106 -2
- package/src/runtime/root/release-materialize.ts +103 -4
- package/src/runtime/root/release-store.ts +31 -2
- package/src/runtime/root/runtime.ts +240 -25
- package/src/runtime/root/scheduled-controller-composition.ts +51 -0
- package/src/runtime/root/scheduler.ts +12 -4
- package/src/runtime/root/service-runner.ts +1 -1
- package/src/runtime/root/service.ts +41 -16
- package/src/runtime/root/status.ts +1 -0
- package/src/runtime/root/types.ts +24 -1
- package/src/runtime/shared/local-bridge-surface.ts +1 -1
- package/src/runtime/shared/process-environment.ts +19 -4
- package/src/runtime/shared/storage-capacity.ts +68 -0
- package/src/runtime/standalone-recovery/controller-home-migration.ts +1116 -0
- package/src/runtime/standalone-recovery/core.ts +984 -230
- package/src/runtime/standalone-recovery/entry.ts +182 -51
- package/src/runtime/standalone-recovery/independent-host-rescue.ts +172 -0
- package/src/runtime/standalone-recovery/installer.ts +212 -104
- package/src/runtime/standalone-recovery/mcp-server.ts +243 -0
- package/src/runtime/standalone-recovery/mutation-identity-contract.ts +21 -0
- package/src/runtime/standalone-recovery/operation-lock.ts +160 -0
- package/src/runtime/standalone-recovery/performance.ts +135 -0
- package/src/runtime/standalone-recovery/release.ts +5 -0
- package/src/runtime/watchdog/workflow-watchdog.ts +1 -1
- package/src/runtime/workflow/schedules/chatgpt-automation-migration.ts +1 -1
- package/src/runtime/workflow/schedules/engine.ts +191 -343
- package/src/runtime/workflow/schedules/pending-activation.ts +1 -1
- package/src/runtime/workflow/schedules/settlement.ts +2 -191
- package/src/runtime/workflow/schedules/store.ts +2 -607
- package/src/runtime/workflow/schedules/types.ts +2 -127
- package/src/runtime/workflow/schedules/work-continuation.ts +180 -15
- package/src/runtime/workflows/first-party/xiaohongshu.ts +122 -0
- package/src/runtime/workflows/runtime.ts +486 -0
- package/src/testing/test-governance.ts +90 -29
- package/assets/templates/helpers/archive-architecture-request.sh +0 -244
- package/docs/architecture/history/README.md +0 -7
- package/docs/architecture/history/chatgpt-handoff-facade.md +0 -397
- package/docs/architecture/history/global-hook-runtime.md +0 -166
- package/docs/architecture/history/ios-semantic-automation-provider-v2.md +0 -170
- package/docs/architecture/requests/archive/2026/20260528-154740-ai-hooks-ai-hooks-hook-input-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-154816-assets-hooks-assets-hooks-hook-input-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-161851-ai-harness-workflow-contract-json-ai-harness-workflow-contract-json.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-161853-scripts-check-task-workflow-sh-scripts-check-task-workflow-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-161911-assets-workflow-contract-v1-json-assets-workflow-contract-v1-json.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-222416-ai-hooks-ai-hooks-hook-input-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-222444-ai-hooks-ai-hooks-pre-edit-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-222457-ai-hooks-ai-hooks-pre-edit-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-222648-ai-hooks-ai-hooks-pre-edit-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-222705-ai-hooks-ai-hooks-worktree-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-222726-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-222727-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-224736-ai-hooks-ai-hooks-hook-input-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-233259-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-233335-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-233456-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-233457-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-233637-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260528-233638-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260529-000322-root-package-json.md +0 -59
- package/docs/architecture/requests/archive/2026/20260529-004238-ai-hooks-ai-hooks-lib-workflow-state-sh.md +0 -59
- package/docs/architecture/requests/archive/2026/20260529-020654-ai-harness-policy-json-ai-harness-policy-json.md +0 -63
- package/docs/architecture/requests/archive/2026/20260529-094236-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -60
- package/docs/architecture/requests/archive/2026/20260529-094323-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -60
- package/docs/architecture/requests/archive/2026/20260529-094331-ai-hooks-ai-hooks-prompt-guard-sh.md +0 -60
- package/docs/architecture/requests/archive/2026/20260529-094339-assets-hooks-assets-hooks-prompt-guard-sh.md +0 -60
- package/docs/architecture/requests/archive/2026/20260529-094430-assets-hooks-assets-hooks-session-start-context-sh.md +0 -60
- package/docs/architecture/requests/archive/2026/20260529-094446-ai-hooks-ai-hooks-session-start-context-sh.md +0 -60
- package/docs/architecture/requests/archive/2026/20260613-002040-runtime-harness-hook-adapters.md +0 -68
- package/docs/architecture/requests/archive/2026/root.md +0 -68
- package/docs/architecture/requests/archive/2026/runtime-harness-hook-adapters.md +0 -76
- package/docs/architecture/requests/archive/2026/verification-evals-checks.md +0 -68
- package/docs/architecture/requests/archive/2026/workflow-engine-contract-assets.md +0 -69
- package/scripts/archive-architecture-request.sh +0 -4
- package/scripts/run-skill-evals.ts +0 -1022
|
@@ -0,0 +1,932 @@
|
|
|
1
|
+
import { createHash } from 'crypto';
|
|
2
|
+
import { AssistantPluginError } from './errors';
|
|
3
|
+
import type { ExternalPluginRegistration } from './external-registration';
|
|
4
|
+
import type { DesktopApplicationIdentity, VerifiedDesktopApplicationActivation } from './local-system-adapter';
|
|
5
|
+
import type { AssistantPluginActionExecutionInput, AssistantPluginAuthorizationContext } from './types';
|
|
6
|
+
|
|
7
|
+
export interface DesktopOperatorExternalPolicyContext {
|
|
8
|
+
callProvider(
|
|
9
|
+
requestId: string,
|
|
10
|
+
actionId: string,
|
|
11
|
+
args: Record<string, unknown>,
|
|
12
|
+
timeoutMs?: number,
|
|
13
|
+
signal?: AbortSignal,
|
|
14
|
+
): Promise<Record<string, unknown>>;
|
|
15
|
+
verifyProviderIdentity(requestId: string, signal?: AbortSignal): Promise<void>;
|
|
16
|
+
activateAndVerifyFrontmostApplication(identity: DesktopApplicationIdentity): Promise<VerifiedDesktopApplicationActivation>;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface DesktopOperatorPolicyExecutionResult {
|
|
20
|
+
handled: boolean;
|
|
21
|
+
result?: Record<string, unknown>;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
function providerError(code: string, message: string, retryable = false): AssistantPluginError {
|
|
25
|
+
return new AssistantPluginError(code, message, { retryable });
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export function desktopOperatorAllowsMissingProviderAction(actionId: string): boolean {
|
|
29
|
+
// Only the retired Forge-composed foreground action may be absent from the provider.
|
|
30
|
+
// The stable public desktop_pointer_click surface requires explicit provider support.
|
|
31
|
+
return actionId === 'desktop_foreground_pointer_click';
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function externalProviderAuthorizationContext(registration: ExternalPluginRegistration): AssistantPluginAuthorizationContext {
|
|
35
|
+
return {
|
|
36
|
+
target: {
|
|
37
|
+
kind: 'external-provider',
|
|
38
|
+
id: registration.pluginId,
|
|
39
|
+
identityFingerprint: registration.registrationFingerprint,
|
|
40
|
+
},
|
|
41
|
+
expiresInMinutes: 30 * 24 * 60,
|
|
42
|
+
};
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function desktopApplicationAuthorizationContext(
|
|
46
|
+
registration: ExternalPluginRegistration,
|
|
47
|
+
stableApplicationId: string,
|
|
48
|
+
): AssistantPluginAuthorizationContext {
|
|
49
|
+
const normalized = stableApplicationId.trim();
|
|
50
|
+
return {
|
|
51
|
+
target: {
|
|
52
|
+
kind: 'desktop-application',
|
|
53
|
+
id: normalized,
|
|
54
|
+
identityFingerprint: createHash('sha256')
|
|
55
|
+
.update(`${registration.registrationFingerprint}\0${normalized}`)
|
|
56
|
+
.digest('hex'),
|
|
57
|
+
},
|
|
58
|
+
expiresInMinutes: 30 * 24 * 60,
|
|
59
|
+
};
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
function recordArray(value: unknown): Record<string, unknown>[] {
|
|
63
|
+
return Array.isArray(value)
|
|
64
|
+
? value.filter((entry): entry is Record<string, unknown> => Boolean(entry) && typeof entry === 'object' && !Array.isArray(entry))
|
|
65
|
+
: [];
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
function firstString(record: Record<string, unknown>, ...keys: string[]): string {
|
|
69
|
+
for (const key of keys) {
|
|
70
|
+
const value = record[key];
|
|
71
|
+
if (typeof value === 'string' && value.trim()) return value.trim();
|
|
72
|
+
}
|
|
73
|
+
return '';
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function firstNumber(record: Record<string, unknown>, ...keys: string[]): number | undefined {
|
|
77
|
+
for (const key of keys) {
|
|
78
|
+
const value = record[key];
|
|
79
|
+
if (typeof value === 'number' && Number.isFinite(value)) return value;
|
|
80
|
+
}
|
|
81
|
+
return undefined;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
function desktopSession(status: Record<string, unknown>, interactionId: string): Record<string, unknown> | undefined {
|
|
85
|
+
return recordArray(status.sessions).find((session) => firstString(session, 'interactionId', 'interaction_id') === interactionId);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
function recordValue(value: unknown): Record<string, unknown> | undefined {
|
|
89
|
+
return Boolean(value) && typeof value === 'object' && !Array.isArray(value)
|
|
90
|
+
? value as Record<string, unknown>
|
|
91
|
+
: undefined;
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
type DesktopFrame = { x: number; y: number; width: number; height: number };
|
|
95
|
+
|
|
96
|
+
function desktopObservedRoot(observation: Record<string, unknown>): Record<string, unknown> | undefined {
|
|
97
|
+
return recordValue(recordValue(observation.snapshot)?.root);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function desktopFrame(value: unknown): DesktopFrame | undefined {
|
|
101
|
+
const frame = recordValue(value);
|
|
102
|
+
if (!frame) return undefined;
|
|
103
|
+
const x = firstNumber(frame, 'x');
|
|
104
|
+
const y = firstNumber(frame, 'y');
|
|
105
|
+
const width = firstNumber(frame, 'width');
|
|
106
|
+
const height = firstNumber(frame, 'height');
|
|
107
|
+
if (x === undefined || y === undefined || width === undefined || height === undefined || width <= 0 || height <= 0) return undefined;
|
|
108
|
+
return { x, y, width, height };
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
function desktopStableSelector(node: Record<string, unknown>): Record<string, string> | undefined {
|
|
112
|
+
const identifier = firstString(node, 'identifier');
|
|
113
|
+
if (identifier) return { identifier };
|
|
114
|
+
const role = firstString(node, 'role');
|
|
115
|
+
const title = firstString(node, 'title');
|
|
116
|
+
return role && title ? { role, title } : undefined;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
function desktopWindowById(observation: Record<string, unknown>, windowId: number): Record<string, unknown> | undefined {
|
|
120
|
+
return recordArray(observation.windows).find((window) => firstNumber(window, 'windowId', 'window_id') === windowId);
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function frameContainsPoint(frame: DesktopFrame, x: number, y: number): boolean {
|
|
124
|
+
return x >= frame.x && x <= frame.x + frame.width && y >= frame.y && y <= frame.y + frame.height;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
function desktopFrameCenter(frame: DesktopFrame): { x: number; y: number } {
|
|
128
|
+
return { x: frame.x + frame.width / 2, y: frame.y + frame.height / 2 };
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
type DesktopObservedPointerTarget = {
|
|
132
|
+
frame: DesktopFrame;
|
|
133
|
+
point: { x: number; y: number };
|
|
134
|
+
reboundSelector?: Record<string, string>;
|
|
135
|
+
};
|
|
136
|
+
|
|
137
|
+
async function observeDesktopPointerTarget(
|
|
138
|
+
requestId: string,
|
|
139
|
+
interactionId: string,
|
|
140
|
+
selector: Record<string, string>,
|
|
141
|
+
referenceLabel: string,
|
|
142
|
+
requestedWindowId: number,
|
|
143
|
+
staleCode: 'DESKTOP_POINTER_REF_STALE' | 'DESKTOP_POINTER_TARGET_STALE',
|
|
144
|
+
timeoutMs: number | undefined,
|
|
145
|
+
signal: AbortSignal | undefined,
|
|
146
|
+
context: DesktopOperatorExternalPolicyContext,
|
|
147
|
+
): Promise<DesktopObservedPointerTarget> {
|
|
148
|
+
const observation = await context.callProvider(
|
|
149
|
+
requestId,
|
|
150
|
+
'desktop_observe',
|
|
151
|
+
{
|
|
152
|
+
interaction_id: interactionId,
|
|
153
|
+
root_selector: selector,
|
|
154
|
+
max_depth: 1,
|
|
155
|
+
max_nodes: 4,
|
|
156
|
+
include_values: false,
|
|
157
|
+
include_actions: false,
|
|
158
|
+
include_windows: true,
|
|
159
|
+
},
|
|
160
|
+
timeoutMs,
|
|
161
|
+
signal,
|
|
162
|
+
);
|
|
163
|
+
const root = desktopObservedRoot(observation);
|
|
164
|
+
const frame = desktopFrame(root?.frame);
|
|
165
|
+
if (!root || !frame || root.enabled === false) {
|
|
166
|
+
throw providerError(staleCode, `Desktop ref ${referenceLabel} no longer resolves to an enabled element with bounded geometry.`, true);
|
|
167
|
+
}
|
|
168
|
+
const window = desktopWindowById(observation, requestedWindowId);
|
|
169
|
+
const windowFrame = desktopFrame(window?.frame);
|
|
170
|
+
if (!window || window.onScreen === false || !windowFrame) {
|
|
171
|
+
throw providerError('DESKTOP_POINTER_WINDOW_STALE', `Desktop window ${requestedWindowId} is not an on-screen window in session ${interactionId}.`, true);
|
|
172
|
+
}
|
|
173
|
+
const point = desktopFrameCenter(frame);
|
|
174
|
+
if (!frameContainsPoint(windowFrame, point.x, point.y)) {
|
|
175
|
+
throw providerError('DESKTOP_POINTER_WINDOW_MISMATCH', `Desktop ref ${referenceLabel} does not belong to requested window ${requestedWindowId}.`, true);
|
|
176
|
+
}
|
|
177
|
+
const reboundSelector = desktopStableSelector(root);
|
|
178
|
+
return { frame, point, ...(reboundSelector ? { reboundSelector } : {}) };
|
|
179
|
+
}
|
|
180
|
+
|
|
181
|
+
function exactFocusedDesktopWindowId(observation: Record<string, unknown>): number | undefined {
|
|
182
|
+
const snapshot = recordValue(observation.snapshot);
|
|
183
|
+
const root = recordValue(snapshot?.root);
|
|
184
|
+
const focusedAxWindows = recordArray(root?.children).filter((window) =>
|
|
185
|
+
firstString(window, 'role') === 'AXWindow' && window.focused === true);
|
|
186
|
+
if (focusedAxWindows.length !== 1) return undefined;
|
|
187
|
+
|
|
188
|
+
const focusedTitle = firstString(focusedAxWindows[0]!, 'title');
|
|
189
|
+
if (!focusedTitle) return undefined;
|
|
190
|
+
const matches = recordArray(observation.windows).filter((window) => {
|
|
191
|
+
const windowId = firstNumber(window, 'windowId', 'window_id');
|
|
192
|
+
return Boolean(windowId)
|
|
193
|
+
&& Number.isInteger(windowId)
|
|
194
|
+
&& window.onScreen === true
|
|
195
|
+
&& firstString(window, 'title') === focusedTitle;
|
|
196
|
+
});
|
|
197
|
+
if (matches.length !== 1) return undefined;
|
|
198
|
+
return firstNumber(matches[0]!, 'windowId', 'window_id');
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
function desktopSessionApplicationIsActive(status: Record<string, unknown>, session: Record<string, unknown>): boolean {
|
|
202
|
+
const pid = firstNumber(session, 'pid');
|
|
203
|
+
const bundleId = firstString(session, 'bundleIdentifier', 'bundle_id');
|
|
204
|
+
const appName = firstString(session, 'appName', 'app_name');
|
|
205
|
+
return recordArray(status.applications).some((application) => {
|
|
206
|
+
if (application.active !== true || application.terminated === true) return false;
|
|
207
|
+
if (pid !== undefined) return firstNumber(application, 'pid') === pid;
|
|
208
|
+
if (bundleId) return firstString(application, 'bundle_id', 'bundleIdentifier') === bundleId;
|
|
209
|
+
return Boolean(appName) && firstString(application, 'name', 'appName') === appName;
|
|
210
|
+
});
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
async function observeExactDesktopWindow(
|
|
214
|
+
requestId: string,
|
|
215
|
+
interactionId: string,
|
|
216
|
+
timeoutMs: number | undefined,
|
|
217
|
+
signal: AbortSignal | undefined,
|
|
218
|
+
context: DesktopOperatorExternalPolicyContext,
|
|
219
|
+
): Promise<number> {
|
|
220
|
+
const observation = await context.callProvider(
|
|
221
|
+
requestId,
|
|
222
|
+
'desktop_observe',
|
|
223
|
+
{
|
|
224
|
+
interaction_id: interactionId,
|
|
225
|
+
max_depth: 3,
|
|
226
|
+
max_nodes: 500,
|
|
227
|
+
include_values: false,
|
|
228
|
+
include_actions: false,
|
|
229
|
+
include_windows: true,
|
|
230
|
+
},
|
|
231
|
+
timeoutMs,
|
|
232
|
+
signal,
|
|
233
|
+
);
|
|
234
|
+
const windowId = exactFocusedDesktopWindowId(observation);
|
|
235
|
+
if (!windowId) {
|
|
236
|
+
throw providerError(
|
|
237
|
+
'DESKTOP_EXACT_WINDOW_UNAVAILABLE',
|
|
238
|
+
`Desktop session ${interactionId} does not expose one uniquely focused AX window that maps to one on-screen CGWindow.`,
|
|
239
|
+
true,
|
|
240
|
+
);
|
|
241
|
+
}
|
|
242
|
+
return windowId;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
async function verifyExactForegroundDesktopSession(
|
|
246
|
+
requestId: string,
|
|
247
|
+
interactionId: string,
|
|
248
|
+
timeoutMs: number | undefined,
|
|
249
|
+
signal: AbortSignal | undefined,
|
|
250
|
+
context: DesktopOperatorExternalPolicyContext,
|
|
251
|
+
): Promise<number> {
|
|
252
|
+
const sourceStatus = await context.callProvider(
|
|
253
|
+
`${requestId}:source-status`,
|
|
254
|
+
'desktop_status',
|
|
255
|
+
{ limit: 500 },
|
|
256
|
+
timeoutMs,
|
|
257
|
+
signal,
|
|
258
|
+
);
|
|
259
|
+
const sourceSession = desktopSession(sourceStatus, interactionId);
|
|
260
|
+
if (!sourceSession) {
|
|
261
|
+
throw providerError('DESKTOP_EXACT_WINDOW_SESSION_NOT_FOUND', `Desktop session ${interactionId} is no longer available.`, true);
|
|
262
|
+
}
|
|
263
|
+
const expectedWindowId = await observeExactDesktopWindow(
|
|
264
|
+
`${requestId}:source-window`,
|
|
265
|
+
interactionId,
|
|
266
|
+
timeoutMs,
|
|
267
|
+
signal,
|
|
268
|
+
context,
|
|
269
|
+
);
|
|
270
|
+
const foregroundStatus = await context.callProvider(
|
|
271
|
+
`${requestId}:foreground-status`,
|
|
272
|
+
'desktop_status',
|
|
273
|
+
{ limit: 500 },
|
|
274
|
+
timeoutMs,
|
|
275
|
+
signal,
|
|
276
|
+
);
|
|
277
|
+
const foregroundSession = desktopSession(foregroundStatus, interactionId);
|
|
278
|
+
if (!foregroundSession || !desktopSessionApplicationIsActive(foregroundStatus, foregroundSession)) {
|
|
279
|
+
throw providerError(
|
|
280
|
+
'DESKTOP_EXACT_WINDOW_FOREGROUND_REQUIRED',
|
|
281
|
+
`Desktop session ${interactionId} is not the system-frontmost application; Forge will not reactivate the bundle because that can steal focus from another window.`,
|
|
282
|
+
true,
|
|
283
|
+
);
|
|
284
|
+
}
|
|
285
|
+
let confirmedWindowId: number;
|
|
286
|
+
try {
|
|
287
|
+
confirmedWindowId = await observeExactDesktopWindow(
|
|
288
|
+
`${requestId}:confirmed-window`,
|
|
289
|
+
interactionId,
|
|
290
|
+
timeoutMs,
|
|
291
|
+
signal,
|
|
292
|
+
context,
|
|
293
|
+
);
|
|
294
|
+
} catch (error) {
|
|
295
|
+
if (error instanceof AssistantPluginError && error.code === 'DESKTOP_EXACT_WINDOW_UNAVAILABLE') {
|
|
296
|
+
throw new AssistantPluginError(
|
|
297
|
+
'DESKTOP_EXACT_WINDOW_FOCUS_CHANGED',
|
|
298
|
+
`Desktop session ${interactionId} no longer exposes the previously verified exact focused window.`,
|
|
299
|
+
{ retryable: true, details: { expectedWindowId } },
|
|
300
|
+
);
|
|
301
|
+
}
|
|
302
|
+
throw error;
|
|
303
|
+
}
|
|
304
|
+
if (confirmedWindowId !== expectedWindowId) {
|
|
305
|
+
throw new AssistantPluginError(
|
|
306
|
+
'DESKTOP_EXACT_WINDOW_FOCUS_CHANGED',
|
|
307
|
+
`Desktop session ${interactionId} changed focused windows while Forge was verifying exact foreground authority.`,
|
|
308
|
+
{ retryable: true, details: { expectedWindowId, observedWindowId: confirmedWindowId } },
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
return expectedWindowId;
|
|
312
|
+
}
|
|
313
|
+
|
|
314
|
+
function normalizeDesktopKeys(keys: unknown[]): unknown[] {
|
|
315
|
+
return keys.map((key) => typeof key === 'string' && ['enter', 'return'].includes(key.trim().toLowerCase()) ? 'return' : key);
|
|
316
|
+
}
|
|
317
|
+
|
|
318
|
+
function normalizeDesktopBatchArguments(args: Record<string, unknown>): { args: Record<string, unknown>; foregroundInteractionId?: string } {
|
|
319
|
+
const rawSteps = Array.isArray(args.steps) ? args.steps : [];
|
|
320
|
+
const foregroundInteractionIds = new Set<string>();
|
|
321
|
+
const stepActions: string[] = [];
|
|
322
|
+
const keyStepIndexes: number[] = [];
|
|
323
|
+
let hasExplicitActivation = false;
|
|
324
|
+
const steps = rawSteps.map((value, index) => {
|
|
325
|
+
const step = recordValue(value);
|
|
326
|
+
if (!step) return value;
|
|
327
|
+
const action = firstString(step, 'action');
|
|
328
|
+
stepActions.push(action);
|
|
329
|
+
const argumentsValue = recordValue(step.arguments) ?? {};
|
|
330
|
+
if (action === 'desktop_session_open' && argumentsValue.activate === true) hasExplicitActivation = true;
|
|
331
|
+
if (['desktop_key', 'desktop_paste', 'desktop_copy'].includes(action)) {
|
|
332
|
+
const interactionId = firstString(argumentsValue, 'interaction_id');
|
|
333
|
+
if (interactionId) foregroundInteractionIds.add(interactionId);
|
|
334
|
+
}
|
|
335
|
+
if (action === 'desktop_key') {
|
|
336
|
+
keyStepIndexes.push(index);
|
|
337
|
+
if (Array.isArray(argumentsValue.keys)) {
|
|
338
|
+
return { ...step, arguments: { ...argumentsValue, keys: normalizeDesktopKeys(argumentsValue.keys) } };
|
|
339
|
+
}
|
|
340
|
+
}
|
|
341
|
+
return value;
|
|
342
|
+
});
|
|
343
|
+
if (foregroundInteractionIds.size > 1) {
|
|
344
|
+
throw providerError(
|
|
345
|
+
'DESKTOP_BATCH_EXACT_WINDOW_AMBIGUOUS',
|
|
346
|
+
'desktop_batch cannot safely target multiple foreground desktop sessions because one exact window can own keyboard focus at a time.',
|
|
347
|
+
);
|
|
348
|
+
}
|
|
349
|
+
if (foregroundInteractionIds.size === 1 && hasExplicitActivation) {
|
|
350
|
+
throw providerError(
|
|
351
|
+
'DESKTOP_BATCH_EXACT_WINDOW_ACTIVATION_CONFLICT',
|
|
352
|
+
'desktop_batch cannot mix explicit application activation with key/copy/paste input because activation can move focus to another window in the same bundle.',
|
|
353
|
+
);
|
|
354
|
+
}
|
|
355
|
+
if (foregroundInteractionIds.size === 1) {
|
|
356
|
+
const exactWindowSafeActions = new Set([
|
|
357
|
+
'desktop_clipboard_read', 'desktop_clipboard_write', 'desktop_copy', 'desktop_paste', 'desktop_key', 'desktop_observe',
|
|
358
|
+
]);
|
|
359
|
+
const unsafeAction = stepActions.find((action) => !exactWindowSafeActions.has(action));
|
|
360
|
+
if (unsafeAction) {
|
|
361
|
+
throw providerError(
|
|
362
|
+
'DESKTOP_BATCH_EXACT_WINDOW_FOCUS_MUTATION_CONFLICT',
|
|
363
|
+
`desktop_batch cannot mix ${unsafeAction} with exact-window key/copy/paste input because that step can invalidate foreground window authority after verification.`,
|
|
364
|
+
);
|
|
365
|
+
}
|
|
366
|
+
if (keyStepIndexes.length > 1) {
|
|
367
|
+
throw providerError(
|
|
368
|
+
'DESKTOP_BATCH_EXACT_WINDOW_MULTIPLE_KEY_STEPS',
|
|
369
|
+
'desktop_batch allows at most one desktop_key step when exact-window authority is required; a synthetic key can itself change focused windows.',
|
|
370
|
+
);
|
|
371
|
+
}
|
|
372
|
+
const keyIndex = keyStepIndexes[0];
|
|
373
|
+
if (keyIndex !== undefined) {
|
|
374
|
+
const laterForegroundInput = rawSteps.slice(keyIndex + 1).some((value) => {
|
|
375
|
+
const action = firstString(recordValue(value) ?? {}, 'action');
|
|
376
|
+
return ['desktop_key', 'desktop_paste', 'desktop_copy'].includes(action);
|
|
377
|
+
});
|
|
378
|
+
if (laterForegroundInput) {
|
|
379
|
+
throw providerError(
|
|
380
|
+
'DESKTOP_BATCH_EXACT_WINDOW_INPUT_AFTER_KEY',
|
|
381
|
+
'desktop_batch cannot send key/copy/paste input after desktop_key because the key may have changed the focused window.',
|
|
382
|
+
);
|
|
383
|
+
}
|
|
384
|
+
}
|
|
385
|
+
}
|
|
386
|
+
return { args: { ...args, steps }, foregroundInteractionId: [...foregroundInteractionIds][0] };
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
async function independentlyEnsureExactFrontmostDesktopApplication(
|
|
390
|
+
bundleId: string,
|
|
391
|
+
appName: string,
|
|
392
|
+
context: DesktopOperatorExternalPolicyContext,
|
|
393
|
+
): Promise<void> {
|
|
394
|
+
const proof = await context.activateAndVerifyFrontmostApplication({ bundleId: bundleId || undefined, appName: appName || undefined });
|
|
395
|
+
const proofBundleId = typeof proof.bundleId === 'string' ? proof.bundleId.trim() : '';
|
|
396
|
+
const proofAppName = typeof proof.appName === 'string' ? proof.appName.trim() : '';
|
|
397
|
+
const proofMatches = bundleId ? proofBundleId === bundleId : proofAppName === appName;
|
|
398
|
+
if (proof.activated !== true || proof.verified !== true || !proofMatches) {
|
|
399
|
+
throw new AssistantPluginError(
|
|
400
|
+
'DESKTOP_ACTIVATION_FALLBACK_IDENTITY_MISMATCH',
|
|
401
|
+
`Forge did not prove the exact requested application ${bundleId || appName} as frontmost.`,
|
|
402
|
+
{ retryable: true, details: { target: { bundleId, appName }, observed: { bundleId: proofBundleId, appName: proofAppName } } },
|
|
403
|
+
);
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
async function openVerifiedDesktopSession(
|
|
408
|
+
requestId: string,
|
|
409
|
+
args: Record<string, unknown>,
|
|
410
|
+
timeoutMs: number | undefined,
|
|
411
|
+
signal: AbortSignal | undefined,
|
|
412
|
+
context: DesktopOperatorExternalPolicyContext,
|
|
413
|
+
): Promise<Record<string, unknown>> {
|
|
414
|
+
const bundleId = typeof args.bundle_id === 'string' ? args.bundle_id.trim() : '';
|
|
415
|
+
const appName = typeof args.app_name === 'string' ? args.app_name.trim() : '';
|
|
416
|
+
if (!bundleId && !appName) {
|
|
417
|
+
throw providerError('DESKTOP_ACTIVATION_TARGET_UNAVAILABLE', 'Activated desktop sessions require an exact bundle id or application name.');
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
let result: Record<string, unknown>;
|
|
421
|
+
let providerActivationError: AssistantPluginError | undefined;
|
|
422
|
+
try {
|
|
423
|
+
result = await context.callProvider(requestId, 'desktop_session_open', args, timeoutMs, signal);
|
|
424
|
+
} catch (error) {
|
|
425
|
+
if (!(error instanceof AssistantPluginError) || error.code !== 'APP_ACTIVATION_FAILED') throw error;
|
|
426
|
+
providerActivationError = error;
|
|
427
|
+
result = await context.callProvider(
|
|
428
|
+
`${requestId}:fallback-session`,
|
|
429
|
+
'desktop_session_open',
|
|
430
|
+
{ ...(bundleId ? { bundle_id: bundleId } : { app_name: appName }), launch: false, activate: false },
|
|
431
|
+
timeoutMs,
|
|
432
|
+
signal,
|
|
433
|
+
);
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
try {
|
|
437
|
+
await independentlyEnsureExactFrontmostDesktopApplication(bundleId, appName, context);
|
|
438
|
+
} catch (verificationError) {
|
|
439
|
+
const wrappedCode = providerActivationError ? 'DESKTOP_ACTIVATION_FALLBACK_FAILED' : 'DESKTOP_ACTIVATION_NOT_CONFIRMED';
|
|
440
|
+
const wrappedMessage = providerActivationError
|
|
441
|
+
? `Desktop Operator activation failed and Forge could not independently verify ${bundleId || appName} as frontmost.`
|
|
442
|
+
: `Desktop Operator returned an activated session, but Forge could not independently verify ${bundleId || appName} as frontmost.`;
|
|
443
|
+
if (verificationError instanceof AssistantPluginError) {
|
|
444
|
+
throw new AssistantPluginError(wrappedCode, wrappedMessage, {
|
|
445
|
+
retryable: verificationError.retryable,
|
|
446
|
+
details: {
|
|
447
|
+
...(providerActivationError ? { providerErrorCode: providerActivationError.code } : {}),
|
|
448
|
+
verificationErrorCode: verificationError.code,
|
|
449
|
+
verificationErrorMessage: verificationError.message,
|
|
450
|
+
target: bundleId || appName,
|
|
451
|
+
},
|
|
452
|
+
});
|
|
453
|
+
}
|
|
454
|
+
throw new AssistantPluginError(wrappedCode, wrappedMessage, {
|
|
455
|
+
retryable: true,
|
|
456
|
+
details: {
|
|
457
|
+
...(providerActivationError ? { providerErrorCode: providerActivationError.code } : {}),
|
|
458
|
+
verificationErrorMessage: String(verificationError),
|
|
459
|
+
target: bundleId || appName,
|
|
460
|
+
},
|
|
461
|
+
});
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
return result;
|
|
465
|
+
}
|
|
466
|
+
|
|
467
|
+
export async function resolveDesktopOperatorAuthorizationContext(
|
|
468
|
+
registration: ExternalPluginRegistration,
|
|
469
|
+
input: AssistantPluginActionExecutionInput,
|
|
470
|
+
context: DesktopOperatorExternalPolicyContext,
|
|
471
|
+
): Promise<AssistantPluginAuthorizationContext | undefined> {
|
|
472
|
+
if (input.actionId === 'desktop_permissions_request') {
|
|
473
|
+
return externalProviderAuthorizationContext(registration);
|
|
474
|
+
}
|
|
475
|
+
if (input.actionId === 'desktop_session_open') {
|
|
476
|
+
const bundleId = typeof input.args.bundle_id === 'string' ? input.args.bundle_id.trim() : '';
|
|
477
|
+
return bundleId ? desktopApplicationAuthorizationContext(registration, bundleId) : undefined;
|
|
478
|
+
}
|
|
479
|
+
|
|
480
|
+
const interactionId = typeof input.args.interaction_id === 'string' ? input.args.interaction_id.trim() : '';
|
|
481
|
+
if (!interactionId) return undefined;
|
|
482
|
+
|
|
483
|
+
await context.verifyProviderIdentity(`${input.requestId}:authorization-target:manifest`, input.signal);
|
|
484
|
+
const status = await context.callProvider(
|
|
485
|
+
`${input.requestId}:authorization-target:desktop-status`,
|
|
486
|
+
'desktop_status',
|
|
487
|
+
{ limit: 500 },
|
|
488
|
+
input.timeoutMs,
|
|
489
|
+
input.signal,
|
|
490
|
+
);
|
|
491
|
+
const session = desktopSession(status, interactionId);
|
|
492
|
+
if (!session) {
|
|
493
|
+
throw providerError('EXTERNAL_PLUGIN_AUTHORIZATION_TARGET_UNAVAILABLE', `Desktop session ${interactionId} was not found while resolving its stable authorization target.`, false);
|
|
494
|
+
}
|
|
495
|
+
const bundleId = firstString(session, 'bundleIdentifier', 'bundle_identifier');
|
|
496
|
+
const appName = firstString(session, 'appName', 'app_name');
|
|
497
|
+
const stableApplicationId = bundleId || (appName ? `name:${appName.toLowerCase()}` : '');
|
|
498
|
+
if (!stableApplicationId) {
|
|
499
|
+
throw providerError('EXTERNAL_PLUGIN_AUTHORIZATION_TARGET_UNAVAILABLE', `Desktop session ${interactionId} has no stable application identity.`, false);
|
|
500
|
+
}
|
|
501
|
+
return desktopApplicationAuthorizationContext(registration, stableApplicationId);
|
|
502
|
+
}
|
|
503
|
+
|
|
504
|
+
export async function executeDesktopOperatorPolicyAction(
|
|
505
|
+
input: AssistantPluginActionExecutionInput,
|
|
506
|
+
context: DesktopOperatorExternalPolicyContext,
|
|
507
|
+
): Promise<DesktopOperatorPolicyExecutionResult> {
|
|
508
|
+
if (input.actionId === 'desktop_pointer_click') {
|
|
509
|
+
const sourceInteractionId = typeof input.args.interaction_id === 'string' ? input.args.interaction_id.trim() : '';
|
|
510
|
+
const requestedWindowId = typeof input.args.window_id === 'number' && Number.isInteger(input.args.window_id) && input.args.window_id > 0
|
|
511
|
+
? input.args.window_id
|
|
512
|
+
: undefined;
|
|
513
|
+
const selector = recordValue(input.args.selector);
|
|
514
|
+
const sourceRef = selector ? firstString(selector, 'ref') : '';
|
|
515
|
+
if (!sourceInteractionId || !requestedWindowId || !sourceRef) {
|
|
516
|
+
throw providerError('DESKTOP_POINTER_ARGUMENT_INVALID', 'desktop_pointer_click requires interaction_id, selector.ref, and a positive window_id.');
|
|
517
|
+
}
|
|
518
|
+
|
|
519
|
+
const sourceStatus = await context.callProvider(
|
|
520
|
+
`${input.requestId}:source-status`,
|
|
521
|
+
'desktop_status',
|
|
522
|
+
{ limit: 500 },
|
|
523
|
+
input.timeoutMs,
|
|
524
|
+
input.signal,
|
|
525
|
+
);
|
|
526
|
+
const sourceSession = desktopSession(sourceStatus, sourceInteractionId);
|
|
527
|
+
if (!sourceSession) {
|
|
528
|
+
throw providerError('DESKTOP_POINTER_SESSION_NOT_FOUND', `Desktop session ${sourceInteractionId} is no longer available.`, true);
|
|
529
|
+
}
|
|
530
|
+
const bundleId = firstString(sourceSession, 'bundleIdentifier', 'bundle_id');
|
|
531
|
+
const appName = firstString(sourceSession, 'appName', 'app_name');
|
|
532
|
+
if (!bundleId && !appName) {
|
|
533
|
+
throw providerError('DESKTOP_POINTER_TARGET_UNAVAILABLE', `Desktop session ${sourceInteractionId} has no stable application identity.`);
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
const sourceObservation = await context.callProvider(
|
|
537
|
+
`${input.requestId}:source-observe`,
|
|
538
|
+
'desktop_observe',
|
|
539
|
+
{
|
|
540
|
+
interaction_id: sourceInteractionId,
|
|
541
|
+
root_selector: { ref: sourceRef },
|
|
542
|
+
max_depth: 1,
|
|
543
|
+
max_nodes: 4,
|
|
544
|
+
include_values: false,
|
|
545
|
+
include_actions: false,
|
|
546
|
+
include_windows: true,
|
|
547
|
+
},
|
|
548
|
+
input.timeoutMs,
|
|
549
|
+
input.signal,
|
|
550
|
+
);
|
|
551
|
+
const sourceRoot = desktopObservedRoot(sourceObservation);
|
|
552
|
+
if (!sourceRoot) {
|
|
553
|
+
throw providerError('DESKTOP_POINTER_REF_STALE', `Desktop ref ${sourceRef} no longer resolves in session ${sourceInteractionId}.`, true);
|
|
554
|
+
}
|
|
555
|
+
const sourceElementFrame = desktopFrame(sourceRoot.frame);
|
|
556
|
+
if (!sourceElementFrame || sourceRoot.enabled === false) {
|
|
557
|
+
throw providerError('DESKTOP_POINTER_REF_STALE', `Desktop ref ${sourceRef} no longer resolves to an enabled element with bounded geometry.`, true);
|
|
558
|
+
}
|
|
559
|
+
const sourceWindow = desktopWindowById(sourceObservation, requestedWindowId);
|
|
560
|
+
const sourceWindowFrame = desktopFrame(sourceWindow?.frame);
|
|
561
|
+
if (!sourceWindow || sourceWindow.onScreen === false || !sourceWindowFrame) {
|
|
562
|
+
throw providerError('DESKTOP_POINTER_WINDOW_STALE', `Desktop window ${requestedWindowId} is not an on-screen window in source session ${sourceInteractionId}.`, true);
|
|
563
|
+
}
|
|
564
|
+
const sourceCenter = desktopFrameCenter(sourceElementFrame);
|
|
565
|
+
if (!frameContainsPoint(sourceWindowFrame, sourceCenter.x, sourceCenter.y)) {
|
|
566
|
+
throw providerError('DESKTOP_POINTER_WINDOW_MISMATCH', `Desktop ref ${sourceRef} does not belong to requested source window ${requestedWindowId}.`, true);
|
|
567
|
+
}
|
|
568
|
+
const reboundSelector = desktopStableSelector(sourceRoot);
|
|
569
|
+
let clickInteractionId = sourceInteractionId;
|
|
570
|
+
let clickFrame = sourceElementFrame;
|
|
571
|
+
let point = sourceCenter;
|
|
572
|
+
let activationVerified = false;
|
|
573
|
+
|
|
574
|
+
if (reboundSelector) {
|
|
575
|
+
const activation = await openVerifiedDesktopSession(
|
|
576
|
+
`${input.requestId}:activate`,
|
|
577
|
+
{ ...(bundleId ? { bundle_id: bundleId } : { app_name: appName }), launch: false, activate: true },
|
|
578
|
+
input.timeoutMs,
|
|
579
|
+
input.signal,
|
|
580
|
+
context,
|
|
581
|
+
);
|
|
582
|
+
const activationInteractionId = firstString(activation, 'interactionId', 'interaction_id');
|
|
583
|
+
if (!activationInteractionId) {
|
|
584
|
+
throw providerError('DESKTOP_ACTIVATION_SESSION_MISSING', 'Desktop Operator activated the application without returning a bound interaction session.', true);
|
|
585
|
+
}
|
|
586
|
+
|
|
587
|
+
const freshObservation = await context.callProvider(
|
|
588
|
+
`${input.requestId}:fresh-observe`,
|
|
589
|
+
'desktop_observe',
|
|
590
|
+
{
|
|
591
|
+
interaction_id: activationInteractionId,
|
|
592
|
+
root_selector: reboundSelector,
|
|
593
|
+
max_depth: 1,
|
|
594
|
+
max_nodes: 4,
|
|
595
|
+
include_values: false,
|
|
596
|
+
include_actions: false,
|
|
597
|
+
include_windows: true,
|
|
598
|
+
},
|
|
599
|
+
input.timeoutMs,
|
|
600
|
+
input.signal,
|
|
601
|
+
);
|
|
602
|
+
const freshRoot = desktopObservedRoot(freshObservation);
|
|
603
|
+
const freshElementFrame = desktopFrame(freshRoot?.frame);
|
|
604
|
+
if (!freshRoot || !freshElementFrame || freshRoot.enabled === false) {
|
|
605
|
+
throw providerError('DESKTOP_POINTER_TARGET_STALE', 'The previously observed desktop element no longer resolves to an enabled element with bounded geometry after activation.', true);
|
|
606
|
+
}
|
|
607
|
+
const freshWindow = desktopWindowById(freshObservation, requestedWindowId);
|
|
608
|
+
const freshWindowFrame = desktopFrame(freshWindow?.frame);
|
|
609
|
+
if (!freshWindow || freshWindow.onScreen === false || !freshWindowFrame) {
|
|
610
|
+
throw providerError('DESKTOP_POINTER_WINDOW_STALE', `Desktop window ${requestedWindowId} is no longer on-screen with valid geometry after activation.`, true);
|
|
611
|
+
}
|
|
612
|
+
const freshPoint = desktopFrameCenter(freshElementFrame);
|
|
613
|
+
if (!frameContainsPoint(freshWindowFrame, freshPoint.x, freshPoint.y)) {
|
|
614
|
+
throw providerError('DESKTOP_POINTER_WINDOW_MISMATCH', `Rebound desktop element ${sourceRef} is no longer inside desktop window ${requestedWindowId}.`, true);
|
|
615
|
+
}
|
|
616
|
+
clickInteractionId = activationInteractionId;
|
|
617
|
+
clickFrame = freshElementFrame;
|
|
618
|
+
point = freshPoint;
|
|
619
|
+
activationVerified = true;
|
|
620
|
+
}
|
|
621
|
+
|
|
622
|
+
const screenshot = await context.callProvider(
|
|
623
|
+
`${input.requestId}:screenshot`,
|
|
624
|
+
'desktop_screenshot',
|
|
625
|
+
{
|
|
626
|
+
interaction_id: clickInteractionId,
|
|
627
|
+
scope: 'window',
|
|
628
|
+
window_id: requestedWindowId,
|
|
629
|
+
...(typeof input.args.label === 'string' && input.args.label.trim() ? { label: input.args.label.trim() } : {}),
|
|
630
|
+
},
|
|
631
|
+
input.timeoutMs,
|
|
632
|
+
input.signal,
|
|
633
|
+
);
|
|
634
|
+
const visualRevision = firstNumber(screenshot, 'visual_revision', 'visualRevision');
|
|
635
|
+
const capturedWindowId = firstNumber(screenshot, 'windowId', 'window_id') ?? requestedWindowId;
|
|
636
|
+
if (!visualRevision || capturedWindowId !== requestedWindowId) {
|
|
637
|
+
throw providerError('DESKTOP_POINTER_CAPTURE_STALE', `Desktop window ${requestedWindowId} did not produce a matching fresh visual revision.`, true);
|
|
638
|
+
}
|
|
639
|
+
|
|
640
|
+
const click = await context.callProvider(
|
|
641
|
+
`${input.requestId}:click`,
|
|
642
|
+
'desktop_pointer_click',
|
|
643
|
+
{
|
|
644
|
+
interaction_id: clickInteractionId,
|
|
645
|
+
window_id: requestedWindowId,
|
|
646
|
+
visual_revision: visualRevision,
|
|
647
|
+
x: point.x,
|
|
648
|
+
y: point.y,
|
|
649
|
+
},
|
|
650
|
+
input.timeoutMs,
|
|
651
|
+
input.signal,
|
|
652
|
+
);
|
|
653
|
+
return {
|
|
654
|
+
handled: true,
|
|
655
|
+
result: {
|
|
656
|
+
interactionId: clickInteractionId,
|
|
657
|
+
activationVerified,
|
|
658
|
+
sourceRef,
|
|
659
|
+
...(reboundSelector ? { reboundSelector } : { foregroundSourceSession: true }),
|
|
660
|
+
windowId: requestedWindowId,
|
|
661
|
+
visualRevision,
|
|
662
|
+
frame: clickFrame,
|
|
663
|
+
screenshot,
|
|
664
|
+
click,
|
|
665
|
+
},
|
|
666
|
+
};
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
if (input.actionId === 'desktop_pointer_drag') {
|
|
670
|
+
const sourceInteractionId = typeof input.args.interaction_id === 'string' ? input.args.interaction_id.trim() : '';
|
|
671
|
+
const requestedWindowId = typeof input.args.window_id === 'number' && Number.isInteger(input.args.window_id) && input.args.window_id > 0
|
|
672
|
+
? input.args.window_id
|
|
673
|
+
: undefined;
|
|
674
|
+
const sourceSelector = recordValue(input.args.source_selector);
|
|
675
|
+
const targetSelector = recordValue(input.args.target_selector);
|
|
676
|
+
const sourceRef = sourceSelector ? firstString(sourceSelector, 'ref') : '';
|
|
677
|
+
const targetRef = targetSelector ? firstString(targetSelector, 'ref') : '';
|
|
678
|
+
if (!sourceInteractionId || !requestedWindowId || !sourceRef || !targetRef || sourceRef === targetRef) {
|
|
679
|
+
throw providerError('DESKTOP_POINTER_ARGUMENT_INVALID', 'desktop_pointer_drag requires interaction_id, distinct source_selector.ref and target_selector.ref values, and a positive window_id.');
|
|
680
|
+
}
|
|
681
|
+
|
|
682
|
+
const sourceStatus = await context.callProvider(
|
|
683
|
+
`${input.requestId}:source-status`,
|
|
684
|
+
'desktop_status',
|
|
685
|
+
{ limit: 500 },
|
|
686
|
+
input.timeoutMs,
|
|
687
|
+
input.signal,
|
|
688
|
+
);
|
|
689
|
+
const sourceSession = desktopSession(sourceStatus, sourceInteractionId);
|
|
690
|
+
if (!sourceSession) {
|
|
691
|
+
throw providerError('DESKTOP_POINTER_SESSION_NOT_FOUND', `Desktop session ${sourceInteractionId} is no longer available.`, true);
|
|
692
|
+
}
|
|
693
|
+
const bundleId = firstString(sourceSession, 'bundleIdentifier', 'bundle_id');
|
|
694
|
+
const appName = firstString(sourceSession, 'appName', 'app_name');
|
|
695
|
+
if (!bundleId && !appName) {
|
|
696
|
+
throw providerError('DESKTOP_POINTER_TARGET_UNAVAILABLE', `Desktop session ${sourceInteractionId} has no stable application identity.`);
|
|
697
|
+
}
|
|
698
|
+
|
|
699
|
+
let dragInteractionId = sourceInteractionId;
|
|
700
|
+
let sourceTarget = await observeDesktopPointerTarget(
|
|
701
|
+
`${input.requestId}:source-observe`,
|
|
702
|
+
sourceInteractionId,
|
|
703
|
+
{ ref: sourceRef },
|
|
704
|
+
sourceRef,
|
|
705
|
+
requestedWindowId,
|
|
706
|
+
'DESKTOP_POINTER_REF_STALE',
|
|
707
|
+
input.timeoutMs,
|
|
708
|
+
input.signal,
|
|
709
|
+
context,
|
|
710
|
+
);
|
|
711
|
+
let destinationTarget = await observeDesktopPointerTarget(
|
|
712
|
+
`${input.requestId}:target-observe`,
|
|
713
|
+
sourceInteractionId,
|
|
714
|
+
{ ref: targetRef },
|
|
715
|
+
targetRef,
|
|
716
|
+
requestedWindowId,
|
|
717
|
+
'DESKTOP_POINTER_REF_STALE',
|
|
718
|
+
input.timeoutMs,
|
|
719
|
+
input.signal,
|
|
720
|
+
context,
|
|
721
|
+
);
|
|
722
|
+
let activationVerified = false;
|
|
723
|
+
|
|
724
|
+
if (sourceTarget.reboundSelector && destinationTarget.reboundSelector) {
|
|
725
|
+
const activation = await openVerifiedDesktopSession(
|
|
726
|
+
`${input.requestId}:activate`,
|
|
727
|
+
{ ...(bundleId ? { bundle_id: bundleId } : { app_name: appName }), launch: false, activate: true },
|
|
728
|
+
input.timeoutMs,
|
|
729
|
+
input.signal,
|
|
730
|
+
context,
|
|
731
|
+
);
|
|
732
|
+
const activationInteractionId = firstString(activation, 'interactionId', 'interaction_id');
|
|
733
|
+
if (!activationInteractionId) {
|
|
734
|
+
throw providerError('DESKTOP_ACTIVATION_SESSION_MISSING', 'Desktop Operator activated the application without returning a bound interaction session.', true);
|
|
735
|
+
}
|
|
736
|
+
dragInteractionId = activationInteractionId;
|
|
737
|
+
sourceTarget = await observeDesktopPointerTarget(
|
|
738
|
+
`${input.requestId}:fresh-source-observe`,
|
|
739
|
+
activationInteractionId,
|
|
740
|
+
sourceTarget.reboundSelector,
|
|
741
|
+
sourceRef,
|
|
742
|
+
requestedWindowId,
|
|
743
|
+
'DESKTOP_POINTER_TARGET_STALE',
|
|
744
|
+
input.timeoutMs,
|
|
745
|
+
input.signal,
|
|
746
|
+
context,
|
|
747
|
+
);
|
|
748
|
+
destinationTarget = await observeDesktopPointerTarget(
|
|
749
|
+
`${input.requestId}:fresh-target-observe`,
|
|
750
|
+
activationInteractionId,
|
|
751
|
+
destinationTarget.reboundSelector,
|
|
752
|
+
targetRef,
|
|
753
|
+
requestedWindowId,
|
|
754
|
+
'DESKTOP_POINTER_TARGET_STALE',
|
|
755
|
+
input.timeoutMs,
|
|
756
|
+
input.signal,
|
|
757
|
+
context,
|
|
758
|
+
);
|
|
759
|
+
activationVerified = true;
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
const screenshot = await context.callProvider(
|
|
763
|
+
`${input.requestId}:screenshot`,
|
|
764
|
+
'desktop_screenshot',
|
|
765
|
+
{
|
|
766
|
+
interaction_id: dragInteractionId,
|
|
767
|
+
scope: 'window',
|
|
768
|
+
window_id: requestedWindowId,
|
|
769
|
+
...(typeof input.args.label === 'string' && input.args.label.trim() ? { label: input.args.label.trim() } : {}),
|
|
770
|
+
},
|
|
771
|
+
input.timeoutMs,
|
|
772
|
+
input.signal,
|
|
773
|
+
);
|
|
774
|
+
const visualRevision = firstNumber(screenshot, 'visual_revision', 'visualRevision');
|
|
775
|
+
const capturedWindowId = firstNumber(screenshot, 'windowId', 'window_id') ?? requestedWindowId;
|
|
776
|
+
if (!visualRevision || capturedWindowId !== requestedWindowId) {
|
|
777
|
+
throw providerError('DESKTOP_POINTER_CAPTURE_STALE', `Desktop window ${requestedWindowId} did not produce a matching fresh visual revision.`, true);
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
const drag = await context.callProvider(
|
|
781
|
+
`${input.requestId}:drag`,
|
|
782
|
+
'desktop_pointer_drag',
|
|
783
|
+
{
|
|
784
|
+
interaction_id: dragInteractionId,
|
|
785
|
+
window_id: requestedWindowId,
|
|
786
|
+
visual_revision: visualRevision,
|
|
787
|
+
from_x: sourceTarget.point.x,
|
|
788
|
+
from_y: sourceTarget.point.y,
|
|
789
|
+
to_x: destinationTarget.point.x,
|
|
790
|
+
to_y: destinationTarget.point.y,
|
|
791
|
+
},
|
|
792
|
+
input.timeoutMs,
|
|
793
|
+
input.signal,
|
|
794
|
+
);
|
|
795
|
+
return {
|
|
796
|
+
handled: true,
|
|
797
|
+
result: {
|
|
798
|
+
interactionId: dragInteractionId,
|
|
799
|
+
activationVerified,
|
|
800
|
+
sourceRef,
|
|
801
|
+
targetRef,
|
|
802
|
+
windowId: requestedWindowId,
|
|
803
|
+
visualRevision,
|
|
804
|
+
sourceFrame: sourceTarget.frame,
|
|
805
|
+
targetFrame: destinationTarget.frame,
|
|
806
|
+
screenshot,
|
|
807
|
+
drag,
|
|
808
|
+
},
|
|
809
|
+
};
|
|
810
|
+
}
|
|
811
|
+
|
|
812
|
+
if (input.actionId === 'desktop_foreground_pointer_click') {
|
|
813
|
+
const sourceInteractionId = typeof input.args.interaction_id === 'string' ? input.args.interaction_id.trim() : '';
|
|
814
|
+
const requestedWindowId = typeof input.args.window_id === 'number' && Number.isInteger(input.args.window_id) ? input.args.window_id : undefined;
|
|
815
|
+
const x = typeof input.args.x === 'number' && Number.isFinite(input.args.x) ? input.args.x : undefined;
|
|
816
|
+
const y = typeof input.args.y === 'number' && Number.isFinite(input.args.y) ? input.args.y : undefined;
|
|
817
|
+
if (!sourceInteractionId || !requestedWindowId || x === undefined || y === undefined) {
|
|
818
|
+
throw providerError('DESKTOP_FOREGROUND_POINTER_ARGUMENT_INVALID', 'desktop_foreground_pointer_click requires interaction_id, window_id, x, and y.');
|
|
819
|
+
}
|
|
820
|
+
|
|
821
|
+
const sourceStatus = await context.callProvider(`${input.requestId}:source-status`, 'desktop_status', { limit: 500 }, input.timeoutMs, input.signal);
|
|
822
|
+
const sourceSession = desktopSession(sourceStatus, sourceInteractionId);
|
|
823
|
+
if (!sourceSession) {
|
|
824
|
+
throw providerError('DESKTOP_FOREGROUND_POINTER_SESSION_NOT_FOUND', `Desktop session ${sourceInteractionId} is no longer available.`, true);
|
|
825
|
+
}
|
|
826
|
+
const bundleId = firstString(sourceSession, 'bundleIdentifier', 'bundle_id');
|
|
827
|
+
const appName = firstString(sourceSession, 'appName', 'app_name');
|
|
828
|
+
if (!bundleId && !appName) {
|
|
829
|
+
throw providerError('DESKTOP_FOREGROUND_POINTER_TARGET_UNAVAILABLE', `Desktop session ${sourceInteractionId} has no stable application identity.`);
|
|
830
|
+
}
|
|
831
|
+
|
|
832
|
+
const activation = await openVerifiedDesktopSession(
|
|
833
|
+
`${input.requestId}:activate`,
|
|
834
|
+
{ ...(bundleId ? { bundle_id: bundleId } : { app_name: appName }), launch: false, activate: true },
|
|
835
|
+
input.timeoutMs,
|
|
836
|
+
input.signal,
|
|
837
|
+
context,
|
|
838
|
+
);
|
|
839
|
+
const activationInteractionId = firstString(activation, 'interactionId', 'interaction_id');
|
|
840
|
+
if (!activationInteractionId) {
|
|
841
|
+
throw providerError('DESKTOP_ACTIVATION_SESSION_MISSING', 'Desktop Operator activated the application without returning a bound interaction session.', true);
|
|
842
|
+
}
|
|
843
|
+
|
|
844
|
+
const screenshot = await context.callProvider(
|
|
845
|
+
`${input.requestId}:screenshot`,
|
|
846
|
+
'desktop_screenshot',
|
|
847
|
+
{
|
|
848
|
+
interaction_id: activationInteractionId,
|
|
849
|
+
scope: 'window',
|
|
850
|
+
window_id: requestedWindowId,
|
|
851
|
+
...(typeof input.args.label === 'string' && input.args.label.trim() ? { label: input.args.label.trim() } : {}),
|
|
852
|
+
},
|
|
853
|
+
input.timeoutMs,
|
|
854
|
+
input.signal,
|
|
855
|
+
);
|
|
856
|
+
const visualRevision = firstNumber(screenshot, 'visual_revision', 'visualRevision');
|
|
857
|
+
const capturedWindowId = firstNumber(screenshot, 'windowId', 'window_id') ?? requestedWindowId;
|
|
858
|
+
if (!visualRevision || !capturedWindowId) {
|
|
859
|
+
throw providerError('DESKTOP_FOREGROUND_CAPTURE_INVALID', 'Desktop Operator did not return a fresh visual revision for the foreground window.', true);
|
|
860
|
+
}
|
|
861
|
+
|
|
862
|
+
const click = await context.callProvider(
|
|
863
|
+
`${input.requestId}:click`,
|
|
864
|
+
'desktop_pointer_click',
|
|
865
|
+
{
|
|
866
|
+
interaction_id: activationInteractionId,
|
|
867
|
+
window_id: capturedWindowId,
|
|
868
|
+
visual_revision: visualRevision,
|
|
869
|
+
x,
|
|
870
|
+
y,
|
|
871
|
+
},
|
|
872
|
+
input.timeoutMs,
|
|
873
|
+
input.signal,
|
|
874
|
+
);
|
|
875
|
+
return {
|
|
876
|
+
handled: true,
|
|
877
|
+
result: {
|
|
878
|
+
interactionId: activationInteractionId,
|
|
879
|
+
activationVerified: true,
|
|
880
|
+
windowId: capturedWindowId,
|
|
881
|
+
visualRevision,
|
|
882
|
+
screenshot,
|
|
883
|
+
click,
|
|
884
|
+
},
|
|
885
|
+
};
|
|
886
|
+
}
|
|
887
|
+
|
|
888
|
+
if (input.actionId === 'desktop_key') {
|
|
889
|
+
const sourceInteractionId = typeof input.args.interaction_id === 'string' ? input.args.interaction_id.trim() : '';
|
|
890
|
+
const keys = Array.isArray(input.args.keys) ? input.args.keys : [];
|
|
891
|
+
if (!sourceInteractionId || keys.length === 0) {
|
|
892
|
+
throw providerError('DESKTOP_FOREGROUND_KEY_ARGUMENT_INVALID', 'desktop_key requires interaction_id and at least one key.');
|
|
893
|
+
}
|
|
894
|
+
await verifyExactForegroundDesktopSession(input.requestId, sourceInteractionId, input.timeoutMs, input.signal, context);
|
|
895
|
+
return {
|
|
896
|
+
handled: true,
|
|
897
|
+
result: await context.callProvider(`${input.requestId}:key`, 'desktop_key', { interaction_id: sourceInteractionId, keys: normalizeDesktopKeys(keys) }, input.timeoutMs, input.signal),
|
|
898
|
+
};
|
|
899
|
+
}
|
|
900
|
+
|
|
901
|
+
if (input.actionId === 'desktop_paste') {
|
|
902
|
+
const sourceInteractionId = typeof input.args.interaction_id === 'string' ? input.args.interaction_id.trim() : '';
|
|
903
|
+
if (!sourceInteractionId) {
|
|
904
|
+
throw providerError('DESKTOP_FOREGROUND_PASTE_ARGUMENT_INVALID', 'desktop_paste requires interaction_id.');
|
|
905
|
+
}
|
|
906
|
+
await verifyExactForegroundDesktopSession(input.requestId, sourceInteractionId, input.timeoutMs, input.signal, context);
|
|
907
|
+
return {
|
|
908
|
+
handled: true,
|
|
909
|
+
result: await context.callProvider(`${input.requestId}:paste`, 'desktop_paste', { interaction_id: sourceInteractionId }, input.timeoutMs, input.signal),
|
|
910
|
+
};
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
if (input.actionId === 'desktop_batch') {
|
|
914
|
+
const normalized = normalizeDesktopBatchArguments(input.args);
|
|
915
|
+
if (normalized.foregroundInteractionId) {
|
|
916
|
+
await verifyExactForegroundDesktopSession(input.requestId, normalized.foregroundInteractionId, input.timeoutMs, input.signal, context);
|
|
917
|
+
}
|
|
918
|
+
return {
|
|
919
|
+
handled: true,
|
|
920
|
+
result: await context.callProvider(`${input.requestId}:batch`, 'desktop_batch', normalized.args, input.timeoutMs, input.signal),
|
|
921
|
+
};
|
|
922
|
+
}
|
|
923
|
+
|
|
924
|
+
if (input.actionId === 'desktop_session_open' && input.args.activate === true) {
|
|
925
|
+
return {
|
|
926
|
+
handled: true,
|
|
927
|
+
result: await openVerifiedDesktopSession(input.requestId, input.args, input.timeoutMs, input.signal, context),
|
|
928
|
+
};
|
|
929
|
+
}
|
|
930
|
+
|
|
931
|
+
return { handled: false };
|
|
932
|
+
}
|