@moretea-labs/forge 1.6.0 → 1.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +4 -0
- package/CHANGELOG.md +39 -0
- package/CLAUDE.md +4 -0
- package/README.md +2 -2
- package/README.zh-CN.md +1 -1
- package/SKILL.md +2 -2
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/agents/openai.yaml +1 -1
- package/assets/hooks/hook-input.sh +11 -8
- package/assets/hooks/lib/session-state.sh +10 -7
- package/assets/hooks/lib/workflow-state.sh +13 -177
- package/assets/hooks/post-tool-observer.sh +8 -2
- package/assets/hooks/prompt-guard.sh +5 -81
- package/assets/hooks/session-start-context.sh +56 -1
- package/assets/partials/05-workflow.partial.md +1 -1
- package/assets/partials-agents/02-operating-mode.partial.md +2 -0
- package/assets/partials-agents/03-orchestration.partial.md +1 -1
- package/assets/plugin-registry.v1.json +3 -3
- package/assets/reference-configs/document-generation.md +1 -1
- package/assets/reference-configs/harness-overview.md +1 -1
- package/assets/reference-configs/sprint-contracts.md +3 -3
- package/assets/skill-version.json +2 -2
- package/assets/templates/contract.template.md +2 -7
- package/assets/templates/helpers/capture-plan.sh +2 -2
- package/assets/templates/helpers/check-architecture-sync.sh +17 -0
- package/assets/templates/helpers/check-task-workflow.sh +4 -4
- package/assets/templates/helpers/contract-worktree.sh +1 -21
- package/assets/templates/helpers/ensure-task-workflow.sh +11 -36
- package/assets/templates/helpers/inspect-project-state.ts +8 -6
- package/assets/templates/helpers/migrate-project-template.sh +22 -45
- package/assets/templates/helpers/migrate-workflow-docs.ts +9 -7
- package/assets/templates/helpers/new-plan.sh +1 -1
- package/assets/templates/helpers/plan-to-todo.sh +12 -37
- package/assets/templates/helpers/ship-worktrees.sh +2 -29
- package/assets/templates/helpers/verify-contract.sh +1 -111
- package/assets/templates/helpers/verify-sprint.sh +3 -180
- package/assets/templates/helpers/workflow-contract.ts +5 -1
- package/assets/templates/plan.template.md +1 -1
- package/assets/templates/review.template.md +9 -34
- package/assets/workflow-contract.v1.json +10 -19
- package/docs/README.md +10 -10
- package/docs/architecture/CURRENT.md +178 -0
- package/docs/architecture/EVOLUTION.md +42 -0
- package/docs/architecture/current/README.md +4 -156
- package/docs/architecture/decisions/20260803-release-identity-binding-and-exit-policy.md +2 -2
- package/docs/architecture/decisions/20260823-immediate-controller-relay.md +53 -0
- package/docs/architecture/decisions/20260826-browser-runtime-v2.md +73 -0
- package/docs/architecture/history/README.md +7 -0
- package/docs/architecture/{chatgpt-handoff-facade.md → history/chatgpt-handoff-facade.md} +4 -0
- package/docs/architecture/{global-hook-runtime.md → history/global-hook-runtime.md} +7 -3
- package/docs/architecture/{ios-semantic-automation-provider-v2.md → history/ios-semantic-automation-provider-v2.md} +6 -2
- package/docs/architecture/history.md +3 -34
- package/docs/architecture/index.md +15 -98
- package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +1 -1
- package/docs/architecture/reviews/2026-08-22-semantic-harness-convergence-acceptance.md +39 -0
- package/docs/architecture/versions/1.6.md +38 -0
- package/docs/architecture/versions/1.7.md +41 -0
- package/docs/forge-chatgpt-browser-engine.md +2 -2
- package/docs/forge-chatgpt-mcp-setup.md +4 -28
- package/docs/forge-chatgpt-supervised-automation.md +3 -3
- package/docs/forge-github-issue-launcher.md +1 -1
- package/docs/forge-plugin-management.md +1 -1
- package/docs/forge-runtime-self-healing-loop.md +1 -1
- package/docs/operations/controller-browser-plugin.md +26 -6
- package/docs/operations/controller-performance-and-502.md +143 -0
- package/docs/operations/controller-reliability-runbook.md +1 -1
- package/docs/operations/features.md +2 -2
- package/docs/operations/github-repository.md +1 -1
- package/docs/operations/homebrew.md +2 -2
- package/docs/operations/mcp-tool-exposure.md +5 -3
- package/docs/wiki/Architecture.md +6 -6
- package/docs/wiki/Implementation.md +2 -2
- package/docs/wiki/Integrations.md +2 -2
- package/docs/wiki/Work-Lifecycle.md +1 -1
- package/package.json +33 -11
- package/scripts/activate-source-baseline.ts +17 -13
- package/scripts/architecture-event.ts +2 -820
- package/scripts/architecture-queue.sh +2 -645
- package/scripts/archive-architecture-request.sh +2 -242
- package/scripts/archive-workflow.sh +1 -291
- package/scripts/benchmark-background-check-overlap.ts +228 -0
- package/scripts/benchmark-mcp-host.sh +117 -0
- package/scripts/benchmark-mcp-transport.ts +170 -0
- package/scripts/benchmark-quality-harness.ts +146 -0
- package/scripts/benchmark-route-session-concurrency.ts +207 -6
- package/scripts/benchmark-thin-harness-mcp-e2e.ts +7 -1
- package/scripts/benchmark-typescript-navigation.ts +186 -0
- package/scripts/benchmark-work-efficiency.ts +334 -0
- package/scripts/capability-config.ts +2 -392
- package/scripts/capability-resolver.ts +2 -503
- package/scripts/capture-plan.sh +1 -332
- package/scripts/check-agent-tooling.sh +2 -1558
- package/scripts/check-architecture-sync.sh +2 -371
- package/scripts/check-brain-manifest.sh +2 -253
- package/scripts/check-context-files.sh +2 -47
- package/scripts/check-deploy-sql-order.sh +2 -115
- package/scripts/check-mcp-compatibility.ts +14 -0
- package/scripts/check-runtime-architecture.mjs +349 -79
- package/scripts/check-skill-version.ts +3 -191
- package/scripts/check-source-duplication.mjs +61 -0
- package/scripts/check-stable-baseline.ts +92 -0
- package/scripts/check-task-sync.sh +2 -63
- package/scripts/check-task-workflow.sh +2 -1000
- package/scripts/codex-handoff-resume.sh +2 -267
- package/scripts/context-contract-sync.sh +2 -514
- package/scripts/contract-run.ts +2 -463
- package/scripts/contract-worktree.sh +1 -722
- package/scripts/ensure-task-workflow.sh +1 -1225
- package/scripts/host-temporary-cloud-mcp-e2e.sh +265 -0
- package/scripts/inspect-project-state.ts +4 -279
- package/scripts/install-standalone-recovery.ts +24 -0
- package/scripts/lib/project-init-lib.sh +22 -46
- package/scripts/lib/run-forge-helper.sh +19 -0
- package/scripts/lib/run-forge-helper.ts +26 -0
- package/scripts/migrate-project-template.sh +1 -1189
- package/scripts/migrate-workflow-docs.ts +4 -412
- package/scripts/new-plan.sh +1 -180
- package/scripts/new-spec.sh +1 -47
- package/scripts/new-sprint.sh +1 -46
- package/scripts/plan-to-todo.sh +1 -796
- package/scripts/prepare-codex-handoff.sh +2 -201
- package/scripts/prepare-handoff.sh +1 -78
- package/scripts/refresh-current-status.sh +2 -467
- package/scripts/run-governed-gate.ts +5 -1
- package/scripts/select-agent-context-blocks.sh +2 -64
- package/scripts/ship-worktrees.sh +1 -558
- package/scripts/smoke-ios-physical-device.ts +4 -2
- package/scripts/smoke-runtime-recovery.ts +1 -35
- package/scripts/smoke-schedule-engine.ts +1 -20
- package/scripts/sprint-backlog.sh +1 -834
- package/scripts/stage-runtime-release.ts +2 -1
- package/scripts/summarize-failures.sh +2 -123
- package/scripts/switch-plan.sh +1 -150
- package/scripts/sync-brain-docs.sh +2 -379
- package/scripts/sync-generated-authority.mjs +175 -0
- package/scripts/verify-contract.sh +2 -703
- package/scripts/verify-forge-runtime.sh +0 -1
- package/scripts/verify-sprint.sh +1 -677
- package/scripts/workflow-contract.ts +4 -205
- package/scripts/workstream-sync.sh +2 -261
- package/src/cli/agent-jobs/executor-health.ts +1 -1
- package/src/cli/commands/init-hook.ts +3 -3
- package/src/cli/commands/mcp.ts +9 -5
- package/src/cli/commands/plugin.ts +12 -3
- package/src/cli/commands/recovery.ts +25 -3
- package/src/cli/commands/runtime.ts +55 -3
- package/src/cli/commands/security.ts +12 -1
- package/src/cli/commands/setup-profile.ts +31 -21
- package/src/cli/controller/check-runner.ts +113 -5
- package/src/cli/controller/composite-result.ts +1 -1
- package/src/cli/controller/context/focus.ts +51 -0
- package/src/cli/controller/context/instruction-context.ts +114 -0
- package/src/cli/controller/context/known-paths.ts +76 -0
- package/src/cli/controller/context/query-planning.ts +110 -0
- package/src/cli/controller/context/rule-resolution.ts +532 -0
- package/src/cli/controller/context/source-materializer.ts +278 -0
- package/src/cli/controller/context/structural.ts +16 -0
- package/src/cli/controller/context/types.ts +286 -0
- package/src/cli/controller/context-pack.ts +828 -487
- package/src/cli/controller/launch-agents.ts +77 -2
- package/src/cli/controller/operational-plan.ts +2 -2
- package/src/cli/controller/work-mode.ts +6 -4
- package/src/cli/editing/edit-session.ts +60 -7
- package/src/cli/github/contracts.ts +16 -0
- package/src/cli/github/github.ts +135 -16
- package/src/cli/github/session.ts +2 -1
- package/src/cli/installer/managed-entries.ts +15 -1
- package/src/cli/local-bridge/console-automations.ts +54 -78
- package/src/cli/local-bridge/console-view-models.ts +1 -35
- package/src/cli/local-bridge/facade-api.ts +37 -264
- package/src/cli/local-bridge/frontend/src/App.tsx +3 -3
- package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +23 -13
- package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +37 -4
- package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +6 -4
- package/src/cli/local-bridge/frontend/src/lib/api.ts +1 -6
- package/src/cli/local-bridge/frontend/src/lib/routes.ts +1 -1
- package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +2 -2
- package/src/cli/local-bridge/frontend/src/styles.css +1 -1
- package/src/cli/local-bridge/frontend/src/types.ts +5 -8
- package/src/cli/local-bridge/job-store.ts +1 -490
- package/src/cli/local-bridge/server.ts +2 -515
- package/src/cli/local-bridge/ui-dist/app.css +1 -1
- package/src/cli/local-bridge/ui-dist/app.js +3703 -3757
- package/src/cli/mcp/auth.ts +5 -5
- package/src/cli/mcp/instructions.ts +3 -2
- package/src/cli/mcp/legacy-tool-service.ts +10 -222
- package/src/cli/mcp/multi-repository.ts +16 -5
- package/src/cli/mcp/oauth.ts +175 -26
- package/src/cli/mcp/post-finalize-work-attribution.ts +150 -0
- package/src/cli/mcp/repository-tools.ts +282 -146
- package/src/cli/mcp/server.ts +586 -116
- package/src/cli/mcp/tool-capability-cache.ts +1 -1
- package/src/cli/mcp/tool-contract.ts +14 -0
- package/src/cli/mcp/toolset-selection.ts +1 -1
- package/src/cli/mcp/toolset.ts +44 -7
- package/src/cli/mcp/transports/http.ts +124 -14
- package/src/cli/mcp/transports/session-registry.ts +43 -5
- package/src/cli/mcp/types.ts +3 -3
- package/src/cli/repositories/command-classifier.ts +103 -21
- package/src/cli/repositories/command-executor.ts +78 -526
- package/src/cli/repositories/command-process.ts +108 -0
- package/src/cli/repositories/command-scope.ts +99 -41
- package/src/cli/repositories/controller-home.ts +7 -8
- package/src/cli/repositories/git-command-executor.ts +2 -0
- package/src/cli/repositories/locks.ts +53 -16
- package/src/cli/repositories/repository-snapshot.ts +124 -0
- package/src/cli/repositories/selected-path-actions.ts +17 -9
- package/src/cli/repositories/structured-git.ts +118 -6
- package/src/cli/repository/inspector.ts +563 -65
- package/src/cli/repository/session-cache.ts +58 -17
- package/src/cli/v81-entry.ts +1 -4
- package/src/effects/fs-transaction.ts +85 -17
- package/src/runtime/context/codegraph-read-provider.ts +233 -2
- package/src/runtime/context/codegraph-sidecar.cjs +74 -40
- package/src/runtime/context/generic-lsp-provider.ts +355 -0
- package/src/runtime/context/lsp-client.ts +283 -0
- package/src/runtime/context/semantic-navigation-contract.ts +61 -0
- package/src/runtime/context/semantic-navigation.ts +223 -0
- package/src/runtime/context/swift-navigation.ts +565 -0
- package/src/runtime/context/typescript-navigation.ts +226 -0
- package/src/runtime/control-plane/dispatch-priority.ts +28 -0
- package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +48 -16
- package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +86 -24
- package/src/runtime/control-plane/execution/repository-work-attribution.ts +106 -0
- package/src/runtime/control-plane/execution/retained-work-resume.ts +203 -0
- package/src/runtime/control-plane/execution/session-store.ts +18 -0
- package/src/runtime/control-plane/execution/verification-evidence.ts +58 -3
- package/src/runtime/control-plane/execution/work-completion-authority.ts +58 -0
- package/src/runtime/control-plane/execution/work-evidence-policy.ts +200 -0
- package/src/runtime/control-plane/execution/work-execution-support.ts +386 -0
- package/src/runtime/control-plane/execution/work-finalization-service.ts +1913 -0
- package/src/runtime/control-plane/execution/work-handle-authority.ts +96 -0
- package/src/runtime/control-plane/execution/work-handle-store.ts +27 -2
- package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +258 -0
- package/src/runtime/control-plane/execution/work-operation-service.ts +468 -0
- package/src/runtime/control-plane/execution/work-path-scope.ts +37 -0
- package/src/runtime/control-plane/execution/work-preparation-service.ts +479 -0
- package/src/runtime/control-plane/execution/work-process-evidence.ts +129 -0
- package/src/runtime/control-plane/execution/work-remote-delivery.ts +215 -0
- package/src/runtime/control-plane/execution/work-task-receipt.ts +22 -18
- package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +156 -11
- package/src/runtime/control-plane/execution/work-validation-reconciler.ts +22 -14
- package/src/runtime/control-plane/execution/work-verification-context.ts +56 -0
- package/src/runtime/control-plane/execution/work-verification-service.ts +388 -0
- package/src/runtime/control-plane/execution/work-verification-snapshot.ts +90 -9
- package/src/runtime/control-plane/facade/capability-registry.ts +129 -25
- package/src/runtime/control-plane/facade/controller-round-relay.ts +1118 -0
- package/src/runtime/control-plane/facade/controller-session-store.ts +302 -12
- package/src/runtime/control-plane/facade/goal-workloop-access.ts +17 -1
- package/src/runtime/control-plane/facade/goal-workloop.ts +928 -190
- package/src/runtime/control-plane/facade/index.ts +3 -0
- package/src/runtime/control-plane/facade/plan-contract-store.ts +275 -42
- package/src/runtime/control-plane/facade/repository-work-admission.ts +147 -0
- package/src/runtime/control-plane/facade/requirement-authority.ts +83 -0
- package/src/runtime/control-plane/facade/semantic-admission.ts +205 -0
- package/src/runtime/control-plane/facade/suggested-actions.ts +5 -1
- package/src/runtime/control-plane/facade/types.ts +90 -10
- package/src/runtime/control-plane/facade/work-contract-store.ts +157 -261
- package/src/runtime/control-plane/facade/work-state-machine.ts +250 -0
- package/src/runtime/control-plane/generated-cache-retention.ts +271 -0
- package/src/runtime/control-plane/global-scheduler/config.ts +44 -0
- package/src/runtime/control-plane/global-scheduler/dispatch-capacity.ts +80 -0
- package/src/runtime/control-plane/global-scheduler/durable-admission.ts +46 -0
- package/src/runtime/control-plane/global-scheduler/maintenance.ts +152 -0
- package/src/runtime/control-plane/global-scheduler/projection-refresh.ts +84 -0
- package/src/runtime/control-plane/global-scheduler/reconciliation.ts +3 -3
- package/src/runtime/control-plane/global-scheduler/scheduler.ts +278 -543
- package/src/runtime/control-plane/global-scheduler/source-scan.ts +59 -0
- package/src/runtime/control-plane/global-scheduler/state.ts +88 -0
- package/src/runtime/control-plane/global-scheduler/worker-attachment.ts +57 -0
- package/src/runtime/control-plane/global-scheduler/worker-exit-decision.ts +38 -0
- package/src/runtime/control-plane/global-scheduler/worker-exit-reconciler.ts +175 -0
- package/src/runtime/control-plane/global-scheduler/worker-launch.ts +111 -0
- package/src/runtime/control-plane/global-scheduler/worker-lifecycle-store.ts +64 -0
- package/src/runtime/control-plane/global-scheduler/worker-lifecycle.ts +150 -0
- package/src/runtime/control-plane/global-scheduler/worker-process.ts +119 -0
- package/src/runtime/control-plane/global-scheduler/worker-stderr.ts +60 -0
- package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +63 -0
- package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +365 -117
- package/src/runtime/control-plane/launcher/launch-reservation-store.ts +89 -0
- package/src/runtime/control-plane/launcher/thin-launcher.ts +244 -11
- package/src/runtime/control-plane/persistence/controller-home-migration.ts +487 -0
- package/src/runtime/control-plane/persistence/requirement-store.ts +0 -12
- package/src/runtime/control-plane/persistence/sqlite-store.ts +33 -2
- package/src/runtime/control-plane/release-retention.ts +62 -2
- package/src/runtime/control-plane/repo-actor/registry.ts +4 -2
- package/src/runtime/control-plane/routing/route-policy.ts +24 -21
- package/src/runtime/control-plane/routing/workspace-admission.ts +70 -0
- package/src/runtime/control-plane/runtime-status-client.ts +0 -2
- package/src/runtime/diagnostics/mcp-timing.ts +13 -0
- package/src/runtime/diagnostics/mcp-transport-benchmark.ts +129 -0
- package/src/runtime/diagnostics/performance.ts +24 -1
- package/src/runtime/evidence/event-ledger.ts +1 -1
- package/src/runtime/evidence/process-check-execution.ts +18 -0
- package/src/runtime/execution/jobs/child-reference.ts +6 -2
- package/src/runtime/execution/jobs/store.ts +58 -4
- package/src/runtime/execution/jobs/types.ts +1 -1
- package/src/runtime/execution/managed-workspace.ts +203 -1
- package/src/runtime/execution/process-runtime/check-facade.ts +51 -4
- package/src/runtime/execution/process-runtime/check-result.ts +88 -0
- package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +7 -1
- package/src/runtime/execution/process-runtime/check-scheduling.ts +114 -0
- package/src/runtime/execution/process-runtime/command-facade.ts +367 -40
- package/src/runtime/execution/process-runtime/gc.ts +38 -6
- package/src/runtime/execution/process-runtime/lightweight-managed.ts +1201 -0
- package/src/runtime/execution/process-runtime/persisted-check.ts +51 -5
- package/src/runtime/execution/process-runtime/resource-claims.ts +10 -6
- package/src/runtime/execution/process-runtime/runtime.ts +53 -3
- package/src/runtime/execution/process-runtime/store.ts +1 -0
- package/src/runtime/execution/process-runtime/types.ts +4 -12
- package/src/runtime/execution/thin-harness/async-process.ts +12 -2
- package/src/runtime/execution/thin-harness/search-worker.ts +54 -5
- package/src/runtime/execution/work-activity.ts +21 -0
- package/src/runtime/execution/workers/executor.ts +18 -61
- package/src/runtime/gateway/mcp/execution-tools.ts +35 -1852
- package/src/runtime/gateway/mcp/legacy-ios-tool-adapter.ts +61 -0
- package/src/runtime/gateway/mcp/process-tools.ts +83 -37
- package/src/runtime/gateway/mcp/router.ts +186 -84
- package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +565 -0
- package/src/runtime/gateway/mcp/runtime-tools.ts +2378 -1947
- package/src/runtime/plugins/app-store-connect-adapter.ts +216 -47
- package/src/runtime/plugins/browser-adapter.ts +2662 -829
- package/src/runtime/plugins/browser-automation-service.ts +10 -0
- package/src/runtime/plugins/browser-handoff-host.ts +2 -25
- package/src/runtime/plugins/browser-handoff.ts +110 -5
- package/src/runtime/plugins/browser-macos-bridge.ts +701 -63
- package/src/runtime/plugins/browser-manifest-surface.ts +603 -0
- package/src/runtime/plugins/browser-native-session-service.ts +222 -0
- package/src/runtime/plugins/browser-node-bridge.ts +6 -34
- package/src/runtime/plugins/browser-provider-registry.ts +109 -0
- package/src/runtime/plugins/browser-runtime-contract.ts +154 -0
- package/src/runtime/plugins/browser-runtime.ts +193 -0
- package/src/runtime/plugins/browser-session-authority.ts +332 -0
- package/src/runtime/plugins/browser-session-store.ts +121 -0
- package/src/runtime/plugins/browser-session-types.ts +98 -0
- package/src/runtime/plugins/capability-authorization-grants.ts +354 -0
- package/src/runtime/plugins/desktop-operator-registration.ts +10 -4
- package/src/runtime/plugins/external-adapter.ts +358 -3
- package/src/runtime/plugins/external-unix-socket.ts +7 -1
- package/src/runtime/plugins/first-party-registry.ts +2 -0
- package/src/runtime/plugins/github-adapter.ts +125 -0
- package/src/runtime/plugins/ios/agent-device-provider.ts +1 -1
- package/src/runtime/plugins/ios/agent-device-typed-provider.ts +52 -9
- package/src/runtime/plugins/ios/remote-xpc-hid.ts +1171 -0
- package/src/runtime/plugins/ios-adapter.ts +106 -16
- package/src/runtime/plugins/ios-agent-device.ts +234 -64
- package/src/runtime/plugins/ios-physical-device.ts +1143 -392
- package/src/runtime/plugins/lightweight-action.ts +153 -0
- package/src/runtime/plugins/local-system-adapter.ts +123 -6
- package/src/runtime/plugins/macos-capability-broker.ts +47 -9
- package/src/runtime/plugins/plugin-action-sidecar.ts +59 -0
- package/src/runtime/plugins/plugin-management-adapter.ts +49 -8
- package/src/runtime/plugins/resend-adapter.ts +1 -1
- package/src/runtime/plugins/store.ts +330 -33
- package/src/runtime/plugins/types.ts +38 -3
- package/src/runtime/plugins/xiaohongshu-publish.ts +515 -0
- package/src/runtime/projections/git-status-sampler.ts +16 -16
- package/src/runtime/projections/materialized-view.ts +28 -10
- package/src/runtime/recovery/maintenance-executor.ts +426 -25
- package/src/runtime/root/mcp-transport.ts +180 -10
- package/src/runtime/root/package-connector-service.ts +230 -10
- package/src/runtime/root/package-runtime-release.ts +143 -17
- package/src/runtime/root/package-runtime-service.ts +461 -22
- package/src/runtime/root/release-manifest.ts +144 -18
- package/src/runtime/root/release-materialize.ts +125 -32
- package/src/runtime/root/release-store.ts +48 -11
- package/src/runtime/root/runtime.ts +23 -5
- package/src/runtime/root/service.ts +100 -29
- package/src/runtime/root/types.ts +12 -0
- package/src/runtime/safe-tooling/ios-development.ts +321 -63
- package/src/runtime/shared/trusted-executable.ts +62 -0
- package/src/runtime/shared/trusted-node-executable.ts +48 -0
- package/src/runtime/standalone-recovery/core.ts +1079 -240
- package/src/runtime/standalone-recovery/entry.ts +100 -24
- package/src/runtime/standalone-recovery/installer.ts +20 -4
- package/src/runtime/standalone-recovery/service-labels.ts +2 -0
- package/src/runtime/standalone-recovery/watchdog-heartbeat.ts +160 -0
- package/src/runtime/workflow/schedules/browser-probe.ts +81 -3
- package/src/runtime/workflow/schedules/engine.ts +364 -202
- package/src/runtime/workflow/schedules/github-issue-watch.ts +158 -0
- package/src/runtime/workflow/schedules/settlement.ts +64 -17
- package/src/runtime/workflow/schedules/store.ts +209 -19
- package/src/runtime/workflow/schedules/types.ts +2 -4
- package/src/runtime/workflow/schedules/work-continuation.ts +26 -13
- package/src/testing/test-governance.ts +8 -40
- package/assets/reference-configs/heartbeat-triage.md +0 -57
- package/assets/templates/helpers/harness-trace-grade.sh +0 -150
- package/assets/templates/helpers/heartbeat-triage.sh +0 -361
- package/assets/templates/helpers/maintenance-triage.sh +0 -167
- package/docs/architecture/current/approved-target-architecture.zh-CN.md +0 -1501
- package/docs/architecture/current/architecture-invariants.md +0 -331
- package/docs/architecture/current/automation-and-schedule-engine.md +0 -459
- package/docs/architecture/current/codegraph-context-provider.md +0 -238
- package/docs/architecture/current/control-plane-authority-inventory.md +0 -94
- package/docs/architecture/current/controller-repository-decoupling.md +0 -148
- package/docs/architecture/current/dispatch-and-agent-strategy.md +0 -373
- package/docs/architecture/current/entity-model.md +0 -612
- package/docs/architecture/current/external-plugin-governance.md +0 -396
- package/docs/architecture/current/failure-recovery.md +0 -447
- package/docs/architecture/current/governance.md +0 -213
- package/docs/architecture/current/human-interaction-plane.md +0 -87
- package/docs/architecture/current/implementation-status.md +0 -128
- package/docs/architecture/current/job-and-run-lifecycle.md +0 -413
- package/docs/architecture/current/legacy-governance.md +0 -58
- package/docs/architecture/current/local-workspace-target-grants.md +0 -70
- package/docs/architecture/current/migration-roadmap.md +0 -105
- package/docs/architecture/current/multi-repository-execution.md +0 -371
- package/docs/architecture/current/operations-runbook.md +0 -45
- package/docs/architecture/current/peer-supercontroller-runtime.md +0 -32
- package/docs/architecture/current/personal-assistant-plugin-baseline.md +0 -206
- package/docs/architecture/current/repository-access-modes.md +0 -102
- package/docs/architecture/current/route-policy.md +0 -15
- package/docs/architecture/current/route-session-concurrency.md +0 -85
- package/docs/architecture/current/runtime-architecture-simplification.md +0 -214
- package/docs/architecture/current/runtime-directory-map.md +0 -49
- package/docs/architecture/current/runtime-health-and-resource-lifecycle.md +0 -85
- package/docs/architecture/current/scheduler-and-resource-claims.md +0 -422
- package/docs/architecture/current/session-aware-execution-and-authorization.md +0 -77
- package/docs/architecture/current/system-overview.md +0 -169
- package/docs/architecture/current/target-requirements-traceability.md +0 -202
- package/docs/architecture/current/thin-harness-governance-policy.md +0 -32
- package/docs/architecture/current/thin-harness-v1.md +0 -321
- package/docs/architecture/current/verification-and-release-gates.md +0 -391
- package/docs/forge-autonomous-goal-loop.md +0 -9
- package/docs/forge-provider-configuration.md +0 -7
- package/docs/operations/chatgpt-personal-assistant-runtime.md +0 -93
- package/docs/operations/gmail-assistant-routines.md +0 -186
- package/docs/operations/personal-assistant-operator-guide.md +0 -61
- package/docs/operations/provider-configuration.md +0 -31
- package/scripts/harness-trace-grade.sh +0 -150
- package/scripts/heartbeat-triage.sh +0 -361
- package/scripts/maintenance-triage.sh +0 -167
- package/src/cli/local-bridge/frontend/src/features/settings/SettingsView.tsx +0 -2
- package/src/cli/repositories/goal-registry.ts +0 -239
- package/src/runtime/assistant/action-proposals.ts +0 -279
- package/src/runtime/assistant/intent.ts +0 -395
- package/src/runtime/assistant/model-provider.ts +0 -460
- package/src/runtime/assistant/openapi.ts +0 -223
- package/src/runtime/assistant/policy.ts +0 -89
- package/src/runtime/assistant/readiness.ts +0 -258
- package/src/runtime/assistant/routine-runtime.ts +0 -477
- package/src/runtime/assistant/schedule-binding.ts +0 -171
- package/src/runtime/assistant/standing-grants.ts +0 -375
- package/src/runtime/assistant/store.ts +0 -243
- package/src/runtime/assistant/types.ts +0 -135
- package/src/runtime/control-plane/goal-loop/config-facade.ts +0 -700
- package/src/runtime/control-plane/goal-loop/config-store.ts +0 -570
- package/src/runtime/control-plane/goal-loop/config-types.ts +0 -213
- package/src/runtime/control-plane/goal-loop/executor-router.ts +0 -109
- package/src/runtime/control-plane/goal-loop/failure-taxonomy.ts +0 -149
- package/src/runtime/control-plane/goal-loop/goal-contract-store.ts +0 -304
- package/src/runtime/control-plane/goal-loop/goal-loop-engine.ts +0 -1001
- package/src/runtime/control-plane/goal-loop/handoff-packet.ts +0 -160
- package/src/runtime/control-plane/goal-loop/index.ts +0 -13
- package/src/runtime/control-plane/goal-loop/local-tools.ts +0 -290
- package/src/runtime/control-plane/goal-loop/policy-gates.ts +0 -203
- package/src/runtime/control-plane/goal-loop/provider-dispatch.ts +0 -93
- package/src/runtime/control-plane/goal-loop/provider-registry.ts +0 -545
- package/src/runtime/control-plane/goal-loop/types.ts +0 -378
- package/src/runtime/control-plane/startup-recovery.ts +0 -198
- package/src/runtime/gateway/mcp/resource-policy.ts +0 -186
- package/src/runtime/personal-assistant/gmail-triage-manager.ts +0 -231
- package/src/runtime/personal-assistant/reporting-runtime.ts +0 -344
- package/src/runtime/personal-assistant/triage-runtime.ts +0 -748
- package/src/runtime/workflow/findings/store.ts +0 -204
- package/src/runtime/workflow/findings/types.ts +0 -58
- package/src/runtime/workflow/portfolio/engine.ts +0 -20
- package/src/runtime/workflow/portfolio/store.ts +0 -185
- package/src/runtime/workflow/portfolio/types.ts +0 -29
|
@@ -0,0 +1,222 @@
|
|
|
1
|
+
import { AssistantPluginError } from './errors';
|
|
2
|
+
import {
|
|
3
|
+
closeMacOsBrowserOwnedTab,
|
|
4
|
+
listMacOsBrowserTabs,
|
|
5
|
+
macOsBrowserPageHandleStale,
|
|
6
|
+
readMacOsBrowserOwnedTabMetadata,
|
|
7
|
+
type MacOsBrowserProduct,
|
|
8
|
+
} from './browser-macos-bridge';
|
|
9
|
+
import { listSavedBrowserSessions, removeBrowserSession } from './browser-session-store';
|
|
10
|
+
import type { BrowserSessionInventoryItem, BrowserSessionState } from './browser-session-types';
|
|
11
|
+
|
|
12
|
+
export interface NativeOwnedSessionInspection {
|
|
13
|
+
items: Map<string, BrowserSessionInventoryItem>;
|
|
14
|
+
liveCount: number;
|
|
15
|
+
deadCount: number;
|
|
16
|
+
unverifiedCount: number;
|
|
17
|
+
prunedCount: number;
|
|
18
|
+
closedInvalidCount: number;
|
|
19
|
+
failedCleanupCount: number;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export function nativeTabInventoryUnsupported(error: unknown): boolean {
|
|
23
|
+
if (error instanceof AssistantPluginError && error.code === 'BROWSER_AUTOMATION_ACTION_UNSUPPORTED') return true;
|
|
24
|
+
const message = error instanceof Error ? error.message : String(error ?? '');
|
|
25
|
+
return /\bBROWSER_AUTOMATION_ACTION_UNSUPPORTED\b/.test(message);
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function isDiscardableNativeOwnedTabUrl(url: string): boolean {
|
|
29
|
+
const normalized = url.trim().toLowerCase();
|
|
30
|
+
return normalized === ''
|
|
31
|
+
|| normalized === 'about:blank'
|
|
32
|
+
|| normalized === 'chrome://newtab/'
|
|
33
|
+
|| normalized === 'chrome://new-tab-page/'
|
|
34
|
+
|| normalized === 'vivaldi://newtab/'
|
|
35
|
+
|| normalized === 'vivaldi://startpage/';
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Native tab liveness/cleanup belongs to one service so the Browser adapter does
|
|
40
|
+
* not reinterpret stable native identity or prune policy action-by-action.
|
|
41
|
+
*/
|
|
42
|
+
export async function inspectNativeOwnedSessions(input: {
|
|
43
|
+
repoRoot: string;
|
|
44
|
+
savedSessions: BrowserSessionState[];
|
|
45
|
+
timeoutMs: number;
|
|
46
|
+
pruneDead?: boolean;
|
|
47
|
+
}): Promise<NativeOwnedSessionInspection> {
|
|
48
|
+
const { repoRoot, savedSessions, timeoutMs } = input;
|
|
49
|
+
const items = new Map<string, BrowserSessionInventoryItem>();
|
|
50
|
+
const inventories = new Map<MacOsBrowserProduct, Awaited<ReturnType<typeof listMacOsBrowserTabs>> | Error>();
|
|
51
|
+
const groups = new Map<string, BrowserSessionState[]>();
|
|
52
|
+
let liveCount = 0;
|
|
53
|
+
let deadCount = 0;
|
|
54
|
+
let unverifiedCount = 0;
|
|
55
|
+
let prunedCount = 0;
|
|
56
|
+
let closedInvalidCount = 0;
|
|
57
|
+
let failedCleanupCount = 0;
|
|
58
|
+
|
|
59
|
+
for (const session of savedSessions) {
|
|
60
|
+
const browser = session.browser;
|
|
61
|
+
const tab = browser?.tab;
|
|
62
|
+
const product = browser?.browserProduct;
|
|
63
|
+
if (browser?.provider !== 'macos-apple-events' || tab?.ownership !== 'plugin_owned' || !product || !tab.windowId || !tab.tabId) continue;
|
|
64
|
+
const key = `${product}:${tab.windowId}:${tab.tabId}`;
|
|
65
|
+
const group = groups.get(key) ?? [];
|
|
66
|
+
group.push(session);
|
|
67
|
+
groups.set(key, group);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
for (const group of groups.values()) {
|
|
71
|
+
const canonical = group.reduce((latest, session) => session.updatedAt > latest.updatedAt ? session : latest);
|
|
72
|
+
const browser = canonical.browser!;
|
|
73
|
+
const tab = browser.tab!;
|
|
74
|
+
const product = browser.browserProduct!;
|
|
75
|
+
let inventory = inventories.get(product);
|
|
76
|
+
if (!inventory) {
|
|
77
|
+
try {
|
|
78
|
+
inventory = await listMacOsBrowserTabs(product, timeoutMs);
|
|
79
|
+
} catch (error) {
|
|
80
|
+
inventory = error instanceof Error ? error : new Error(String(error));
|
|
81
|
+
}
|
|
82
|
+
inventories.set(product, inventory);
|
|
83
|
+
}
|
|
84
|
+
if (inventory instanceof Error) {
|
|
85
|
+
if (!nativeTabInventoryUnsupported(inventory)) {
|
|
86
|
+
for (const session of group) items.set(session.sessionId, { session, liveness: 'unverified', evidence: 'native_inventory_unavailable', cleanupError: inventory.message });
|
|
87
|
+
unverifiedCount += group.length;
|
|
88
|
+
continue;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
const ref = { windowId: tab.windowId!, tabId: tab.tabId! };
|
|
92
|
+
let metadata: Awaited<ReturnType<typeof readMacOsBrowserOwnedTabMetadata>>;
|
|
93
|
+
try {
|
|
94
|
+
metadata = await readMacOsBrowserOwnedTabMetadata(product, ref, timeoutMs);
|
|
95
|
+
} catch (metadataError) {
|
|
96
|
+
if (macOsBrowserPageHandleStale(metadataError)) {
|
|
97
|
+
const pruned = input.pruneDead === true;
|
|
98
|
+
if (pruned) {
|
|
99
|
+
for (const session of group) removeBrowserSession(repoRoot, session.sessionId);
|
|
100
|
+
prunedCount += group.length;
|
|
101
|
+
}
|
|
102
|
+
for (const session of group) items.set(session.sessionId, { session, liveness: 'dead', evidence: 'native_tab_missing', pruned });
|
|
103
|
+
deadCount += group.length;
|
|
104
|
+
} else {
|
|
105
|
+
const cleanupError = metadataError instanceof Error ? metadataError.message : String(metadataError);
|
|
106
|
+
for (const session of group) items.set(session.sessionId, { session, liveness: 'unverified', evidence: 'native_inventory_unavailable', cleanupError });
|
|
107
|
+
unverifiedCount += group.length;
|
|
108
|
+
}
|
|
109
|
+
continue;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
if (isDiscardableNativeOwnedTabUrl(metadata.url)) {
|
|
113
|
+
deadCount += group.length;
|
|
114
|
+
if (input.pruneDead === true) {
|
|
115
|
+
try {
|
|
116
|
+
await closeMacOsBrowserOwnedTab(product, ref, timeoutMs);
|
|
117
|
+
for (const session of group) removeBrowserSession(repoRoot, session.sessionId);
|
|
118
|
+
prunedCount += group.length;
|
|
119
|
+
closedInvalidCount += 1;
|
|
120
|
+
for (const session of group) items.set(session.sessionId, { session, liveness: 'dead', evidence: 'native_tab_invalid_closed', pruned: true });
|
|
121
|
+
} catch (error) {
|
|
122
|
+
failedCleanupCount += 1;
|
|
123
|
+
const cleanupError = error instanceof Error ? error.message : String(error);
|
|
124
|
+
for (const session of group) items.set(session.sessionId, { session, liveness: 'dead', evidence: 'native_tab_invalid_close_failed', cleanupError });
|
|
125
|
+
}
|
|
126
|
+
} else {
|
|
127
|
+
for (const session of group) items.set(session.sessionId, { session, liveness: 'dead', evidence: 'native_tab_invalid' });
|
|
128
|
+
}
|
|
129
|
+
} else {
|
|
130
|
+
for (const session of group) items.set(session.sessionId, { session, liveness: 'live', evidence: 'native_tab_live' });
|
|
131
|
+
liveCount += group.length;
|
|
132
|
+
}
|
|
133
|
+
continue;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
const live = inventory.tabs.find((entry) => entry.windowId === tab.windowId && entry.tabId === tab.tabId);
|
|
137
|
+
if (!live) {
|
|
138
|
+
const pruned = input.pruneDead === true;
|
|
139
|
+
if (pruned) {
|
|
140
|
+
for (const session of group) removeBrowserSession(repoRoot, session.sessionId);
|
|
141
|
+
prunedCount += group.length;
|
|
142
|
+
}
|
|
143
|
+
for (const session of group) items.set(session.sessionId, { session, liveness: 'dead', evidence: 'native_tab_missing', pruned });
|
|
144
|
+
deadCount += group.length;
|
|
145
|
+
continue;
|
|
146
|
+
}
|
|
147
|
+
|
|
148
|
+
if (isDiscardableNativeOwnedTabUrl(live.url)) {
|
|
149
|
+
deadCount += group.length;
|
|
150
|
+
if (input.pruneDead === true) {
|
|
151
|
+
try {
|
|
152
|
+
await closeMacOsBrowserOwnedTab(product, { windowId: tab.windowId!, tabId: tab.tabId! }, timeoutMs);
|
|
153
|
+
for (const session of group) removeBrowserSession(repoRoot, session.sessionId);
|
|
154
|
+
prunedCount += group.length;
|
|
155
|
+
closedInvalidCount += 1;
|
|
156
|
+
for (const session of group) items.set(session.sessionId, { session, liveness: 'dead', evidence: 'native_tab_invalid_closed', pruned: true });
|
|
157
|
+
} catch (error) {
|
|
158
|
+
failedCleanupCount += 1;
|
|
159
|
+
const cleanupError = error instanceof Error ? error.message : String(error);
|
|
160
|
+
for (const session of group) items.set(session.sessionId, { session, liveness: 'dead', evidence: 'native_tab_invalid_close_failed', cleanupError });
|
|
161
|
+
}
|
|
162
|
+
} else {
|
|
163
|
+
for (const session of group) items.set(session.sessionId, { session, liveness: 'dead', evidence: 'native_tab_invalid' });
|
|
164
|
+
}
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
for (const session of group) items.set(session.sessionId, { session, liveness: 'live', evidence: 'native_tab_live' });
|
|
169
|
+
liveCount += group.length;
|
|
170
|
+
}
|
|
171
|
+
|
|
172
|
+
return { items, liveCount, deadCount, unverifiedCount, prunedCount, closedInvalidCount, failedCleanupCount };
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export async function closeTrackedNativeOwnedSession(
|
|
176
|
+
session: BrowserSessionState,
|
|
177
|
+
timeoutMs: number,
|
|
178
|
+
): Promise<{ resourceClosed: boolean; resourceAlreadyMissing: boolean }> {
|
|
179
|
+
const browser = session.browser;
|
|
180
|
+
const tab = browser?.tab;
|
|
181
|
+
if (browser?.provider !== 'macos-apple-events' || tab?.ownership !== 'plugin_owned') {
|
|
182
|
+
return { resourceClosed: false, resourceAlreadyMissing: false };
|
|
183
|
+
}
|
|
184
|
+
if (!browser.browserProduct || !tab.windowId || !tab.tabId) {
|
|
185
|
+
throw new AssistantPluginError('PLUGIN_BROWSER_NATIVE_OWNERSHIP_MISSING', 'Tracked plugin-owned native tab is missing stable browser identity; retaining session metadata instead of orphaning the tab.', {
|
|
186
|
+
retryable: false, details: { sessionId: session.sessionId, browserProduct: browser.browserProduct, windowId: tab.windowId, tabId: tab.tabId },
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
const ref = { windowId: tab.windowId, tabId: tab.tabId };
|
|
190
|
+
let inventory: Awaited<ReturnType<typeof listMacOsBrowserTabs>> | undefined;
|
|
191
|
+
try {
|
|
192
|
+
inventory = await listMacOsBrowserTabs(browser.browserProduct, timeoutMs);
|
|
193
|
+
} catch (error) {
|
|
194
|
+
if (!nativeTabInventoryUnsupported(error)) throw error;
|
|
195
|
+
try {
|
|
196
|
+
await readMacOsBrowserOwnedTabMetadata(browser.browserProduct, ref, timeoutMs);
|
|
197
|
+
} catch (metadataError) {
|
|
198
|
+
if (macOsBrowserPageHandleStale(metadataError)) return { resourceClosed: false, resourceAlreadyMissing: true };
|
|
199
|
+
throw metadataError;
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
if (inventory && !inventory.tabs.some((entry) => entry.windowId === tab.windowId && entry.tabId === tab.tabId)) {
|
|
203
|
+
return { resourceClosed: false, resourceAlreadyMissing: true };
|
|
204
|
+
}
|
|
205
|
+
await closeMacOsBrowserOwnedTab(browser.browserProduct, ref, timeoutMs);
|
|
206
|
+
return { resourceClosed: true, resourceAlreadyMissing: false };
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
export function nativeOwnedAliasSessionIds(repoRoot: string, session: BrowserSessionState): string[] {
|
|
210
|
+
const browser = session.browser;
|
|
211
|
+
const tab = browser?.tab;
|
|
212
|
+
if (browser?.provider !== 'macos-apple-events' || tab?.ownership !== 'plugin_owned' || !browser.browserProduct || !tab.windowId || !tab.tabId) {
|
|
213
|
+
return [session.sessionId];
|
|
214
|
+
}
|
|
215
|
+
return listSavedBrowserSessions(repoRoot)
|
|
216
|
+
.filter((candidate) => candidate.browser?.provider === 'macos-apple-events'
|
|
217
|
+
&& candidate.browser.browserProduct === browser.browserProduct
|
|
218
|
+
&& candidate.browser.tab?.ownership === 'plugin_owned'
|
|
219
|
+
&& candidate.browser.tab.windowId === tab.windowId
|
|
220
|
+
&& candidate.browser.tab.tabId === tab.tabId)
|
|
221
|
+
.map((candidate) => candidate.sessionId);
|
|
222
|
+
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { spawn } from 'child_process';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { existsSync } from 'fs';
|
|
3
|
+
import { dirname, join } from 'path';
|
|
4
4
|
import { fileURLToPath } from 'url';
|
|
5
5
|
import type { AssistantPluginActionExecutionInput } from './types';
|
|
6
6
|
import { AssistantPluginError } from './errors';
|
|
7
|
+
import { resolveTrustedNodeExecutable } from '../shared/trusted-node-executable';
|
|
7
8
|
|
|
8
9
|
const BRIDGE_SCHEMA_VERSION = 1;
|
|
9
10
|
const MAX_REQUEST_BYTES = 1_048_576;
|
|
@@ -43,43 +44,14 @@ interface BridgeFailure {
|
|
|
43
44
|
|
|
44
45
|
type BridgeResponse = BridgeSuccess | BridgeFailure;
|
|
45
46
|
|
|
46
|
-
function isExecutable(path: string): boolean {
|
|
47
|
-
try {
|
|
48
|
-
accessSync(path, constants.X_OK);
|
|
49
|
-
return existsSync(path);
|
|
50
|
-
} catch {
|
|
51
|
-
return false;
|
|
52
|
-
}
|
|
53
|
-
}
|
|
54
|
-
|
|
55
|
-
function pathCandidates(env: NodeJS.ProcessEnv): string[] {
|
|
56
|
-
const candidates = [
|
|
57
|
-
env.FORGE_NODE_EXECUTABLE,
|
|
58
|
-
env.VOLTA_HOME ? join(env.VOLTA_HOME, 'bin', 'node') : undefined,
|
|
59
|
-
'/opt/homebrew/bin/node',
|
|
60
|
-
'/usr/local/bin/node',
|
|
61
|
-
'/usr/bin/node',
|
|
62
|
-
];
|
|
63
|
-
for (const entry of (env.PATH ?? '').split(delimiter)) {
|
|
64
|
-
if (entry.trim()) candidates.push(join(entry, process.platform === 'win32' ? 'node.exe' : 'node'));
|
|
65
|
-
}
|
|
66
|
-
return candidates.filter((value): value is string => Boolean(value));
|
|
67
|
-
}
|
|
68
|
-
|
|
69
47
|
export function resolveBrowserBridgeNodeExecutable(env: NodeJS.ProcessEnv = process.env): string {
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
: resolve(env.FORGE_NODE_EXECUTABLE);
|
|
74
|
-
if (isExecutable(configured)) return configured;
|
|
48
|
+
const resolution = resolveTrustedNodeExecutable(env);
|
|
49
|
+
if (resolution.executable) return resolution.executable;
|
|
50
|
+
if (resolution.configuredInvalid) {
|
|
75
51
|
throw new AssistantPluginError('PLUGIN_BROWSER_NODE_UNAVAILABLE', 'The configured Browser bridge Node executable is not executable.', {
|
|
76
52
|
retryable: false,
|
|
77
53
|
});
|
|
78
54
|
}
|
|
79
|
-
for (const candidate of pathCandidates(env)) {
|
|
80
|
-
const absolute = isAbsolute(candidate) ? candidate : resolve(candidate);
|
|
81
|
-
if (isExecutable(absolute)) return absolute;
|
|
82
|
-
}
|
|
83
55
|
throw new AssistantPluginError('PLUGIN_BROWSER_NODE_UNAVAILABLE', 'Browser attach operations require a trusted Node executable, but none was found.', {
|
|
84
56
|
retryable: false,
|
|
85
57
|
});
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import { AssistantPluginError } from './errors';
|
|
2
|
+
import {
|
|
3
|
+
providerSupports,
|
|
4
|
+
type BrowserForegroundRequirement,
|
|
5
|
+
type BrowserProviderCapabilities,
|
|
6
|
+
type BrowserProviderCapability,
|
|
7
|
+
} from './browser-runtime-contract';
|
|
8
|
+
|
|
9
|
+
export interface BrowserRuntimeProvider<TInput, TResult> extends BrowserProviderCapabilities {
|
|
10
|
+
/** Lower values are preferred when multiple providers satisfy the same capability contract. */
|
|
11
|
+
priority: number;
|
|
12
|
+
/** Availability is decided before execution; ordinary provider failures are never fallback signals. */
|
|
13
|
+
supportsInput(input: TInput): boolean;
|
|
14
|
+
execute(input: TInput): Promise<TResult>;
|
|
15
|
+
/** Runs only after an explicit stale/restart/configuration boundary. */
|
|
16
|
+
revalidate?: () => Promise<void> | void;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
interface BrowserProviderState<TInput, TResult> {
|
|
20
|
+
provider: BrowserRuntimeProvider<TInput, TResult>;
|
|
21
|
+
generation: number;
|
|
22
|
+
invalidatedReason?: string;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export interface BrowserProviderSelection<TInput, TResult> {
|
|
26
|
+
provider: BrowserRuntimeProvider<TInput, TResult>;
|
|
27
|
+
generation: number;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* A typed pre-action rejection. Runtime may select another provider because this proves the action
|
|
32
|
+
* was not attempted. Arbitrary provider errors are intentionally not mapped to this signal.
|
|
33
|
+
*/
|
|
34
|
+
export class BrowserProviderUnavailableBeforeActionError extends Error {
|
|
35
|
+
readonly providerId: string;
|
|
36
|
+
readonly reason: string;
|
|
37
|
+
|
|
38
|
+
constructor(providerId: string, reason: string) {
|
|
39
|
+
super(`${providerId} unavailable before action: ${reason}`);
|
|
40
|
+
this.name = 'BrowserProviderUnavailableBeforeActionError';
|
|
41
|
+
this.providerId = providerId;
|
|
42
|
+
this.reason = reason;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
function foregroundCompatible(
|
|
47
|
+
provider: BrowserProviderCapabilities,
|
|
48
|
+
requested: BrowserForegroundRequirement,
|
|
49
|
+
): boolean {
|
|
50
|
+
// Background-safe providers may execute explicit foreground transactions; foreground-only
|
|
51
|
+
// providers may not satisfy a background-safe transaction.
|
|
52
|
+
return requested === 'explicit_required' || provider.foreground === 'none';
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export class BrowserProviderRegistry<TInput, TResult> {
|
|
56
|
+
private readonly providers = new Map<string, BrowserProviderState<TInput, TResult>>();
|
|
57
|
+
|
|
58
|
+
register(provider: BrowserRuntimeProvider<TInput, TResult>): void {
|
|
59
|
+
const current = this.providers.get(provider.providerId);
|
|
60
|
+
if (current) {
|
|
61
|
+
// Refresh call-local closures without discarding the warm provider generation.
|
|
62
|
+
current.provider = provider;
|
|
63
|
+
return;
|
|
64
|
+
}
|
|
65
|
+
this.providers.set(provider.providerId, { provider, generation: 1 });
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
invalidate(providerId: string, reason: string): void {
|
|
69
|
+
const state = this.providers.get(providerId);
|
|
70
|
+
if (state) state.invalidatedReason = reason;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
invalidateAll(reason: string): void {
|
|
74
|
+
for (const state of this.providers.values()) state.invalidatedReason = reason;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
private async prepare(state: BrowserProviderState<TInput, TResult>): Promise<void> {
|
|
78
|
+
if (!state.invalidatedReason) return;
|
|
79
|
+
await state.provider.revalidate?.();
|
|
80
|
+
state.invalidatedReason = undefined;
|
|
81
|
+
state.generation += 1;
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
async select(options: {
|
|
85
|
+
input: TInput;
|
|
86
|
+
requiredCapabilities: readonly BrowserProviderCapability[];
|
|
87
|
+
foreground: BrowserForegroundRequirement;
|
|
88
|
+
excludedProviderIds?: ReadonlySet<string>;
|
|
89
|
+
}): Promise<BrowserProviderSelection<TInput, TResult>> {
|
|
90
|
+
const candidates = [...this.providers.values()]
|
|
91
|
+
.filter(({ provider }) => !options.excludedProviderIds?.has(provider.providerId))
|
|
92
|
+
.filter(({ provider }) => provider.supportsInput(options.input))
|
|
93
|
+
.filter(({ provider }) => providerSupports(provider, options.requiredCapabilities))
|
|
94
|
+
.filter(({ provider }) => foregroundCompatible(provider, options.foreground))
|
|
95
|
+
.sort((left, right) => left.provider.priority - right.provider.priority
|
|
96
|
+
|| left.provider.providerId.localeCompare(right.provider.providerId));
|
|
97
|
+
|
|
98
|
+
const selected = candidates[0];
|
|
99
|
+
if (!selected) {
|
|
100
|
+
throw new AssistantPluginError(
|
|
101
|
+
'PLUGIN_BROWSER_PROVIDER_UNAVAILABLE',
|
|
102
|
+
`No Browser provider satisfies required capabilities: ${options.requiredCapabilities.join(', ') || 'browser.transaction'}.`,
|
|
103
|
+
{ retryable: true },
|
|
104
|
+
);
|
|
105
|
+
}
|
|
106
|
+
await this.prepare(selected);
|
|
107
|
+
return { provider: selected.provider, generation: selected.generation };
|
|
108
|
+
}
|
|
109
|
+
}
|
|
@@ -0,0 +1,154 @@
|
|
|
1
|
+
export const MAX_BROWSER_CDP_ENDPOINT_CANDIDATES = 5;
|
|
2
|
+
|
|
3
|
+
export type BrowserProviderCapability =
|
|
4
|
+
| 'dom.read'
|
|
5
|
+
| 'dom.interact'
|
|
6
|
+
| 'browser.internal_resources'
|
|
7
|
+
| 'browser.screenshot'
|
|
8
|
+
| 'browser.trusted_input'
|
|
9
|
+
| 'browser.foreground'
|
|
10
|
+
| 'browser.transaction'
|
|
11
|
+
| 'browser.persistent_handle';
|
|
12
|
+
|
|
13
|
+
export type BrowserTargetOwnership = 'plugin_owned' | 'user_owned' | 'provider_owned';
|
|
14
|
+
export type BrowserTargetKind = 'page' | 'tab' | 'browser_internal_resource';
|
|
15
|
+
export type BrowserForegroundRequirement = 'none' | 'explicit_required';
|
|
16
|
+
export type BrowserActionReplaySafety = 'read_only' | 'idempotent' | 'non_idempotent';
|
|
17
|
+
|
|
18
|
+
/** Stable resource identity. URL/title are deliberately excluded because they are mutable observations. */
|
|
19
|
+
export interface BrowserTargetIdentity {
|
|
20
|
+
providerId: string;
|
|
21
|
+
resourceKind: BrowserTargetKind;
|
|
22
|
+
resourceId: string;
|
|
23
|
+
browserProduct?: string;
|
|
24
|
+
ownership: BrowserTargetOwnership;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface BrowserObservedState {
|
|
28
|
+
url: string;
|
|
29
|
+
title?: string;
|
|
30
|
+
observedAt: string;
|
|
31
|
+
providerRevision?: string;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export interface BrowserProviderCapabilities {
|
|
35
|
+
providerId: string;
|
|
36
|
+
capabilities: readonly BrowserProviderCapability[];
|
|
37
|
+
foreground: BrowserForegroundRequirement;
|
|
38
|
+
/** True only when the provider can verify postconditions in the same provider call that performs the action. */
|
|
39
|
+
verifiesPostconditions: boolean;
|
|
40
|
+
/** True only when an already-bound provider handle can be reused across bounded consecutive actions. */
|
|
41
|
+
persistentHandle: boolean;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
export type BrowserCondition =
|
|
45
|
+
| { kind: 'target_identity'; target: BrowserTargetIdentity }
|
|
46
|
+
| { kind: 'url_equals'; url: string }
|
|
47
|
+
| { kind: 'url_matches'; pattern: string }
|
|
48
|
+
| { kind: 'selector_exists'; selector: string }
|
|
49
|
+
| { kind: 'selector_visible'; selector: string }
|
|
50
|
+
| { kind: 'text_contains'; text: string; selector?: string }
|
|
51
|
+
| { kind: 'attribute_equals'; selector: string; attribute: string; value: string }
|
|
52
|
+
| { kind: 'provider_assertion'; assertion: string; expected: string | number | boolean };
|
|
53
|
+
|
|
54
|
+
export type BrowserTransactionAction =
|
|
55
|
+
| { kind: 'read'; operation: string; arguments?: Record<string, unknown> }
|
|
56
|
+
| { kind: 'dom'; operation: string; arguments?: Record<string, unknown> }
|
|
57
|
+
| { kind: 'internal_resource'; operation: string; arguments?: Record<string, unknown> }
|
|
58
|
+
| { kind: 'trusted_input'; operation: string; arguments?: Record<string, unknown> };
|
|
59
|
+
|
|
60
|
+
export interface BrowserTransaction {
|
|
61
|
+
transactionId: string;
|
|
62
|
+
target: BrowserTargetIdentity;
|
|
63
|
+
requiredCapabilities: readonly BrowserProviderCapability[];
|
|
64
|
+
foreground: BrowserForegroundRequirement;
|
|
65
|
+
replaySafety: BrowserActionReplaySafety;
|
|
66
|
+
preconditions?: readonly BrowserCondition[];
|
|
67
|
+
action: BrowserTransactionAction;
|
|
68
|
+
/** Writes/interactions require at least one postcondition unless the action itself returns a typed read result. */
|
|
69
|
+
postconditions?: readonly BrowserCondition[];
|
|
70
|
+
timeoutMs: number;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export interface BrowserConditionEvidence {
|
|
74
|
+
condition: BrowserCondition;
|
|
75
|
+
satisfied: boolean;
|
|
76
|
+
observed?: unknown;
|
|
77
|
+
reason?: string;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export interface BrowserTransactionTiming {
|
|
81
|
+
providerCalls: number;
|
|
82
|
+
providerDurationMs: number;
|
|
83
|
+
totalDurationMs: number;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface BrowserTransactionResult<T = unknown> {
|
|
87
|
+
transactionId: string;
|
|
88
|
+
target: BrowserTargetIdentity;
|
|
89
|
+
transportSucceeded: boolean;
|
|
90
|
+
actionPerformed: boolean;
|
|
91
|
+
/** Authoritative success bit. AX/transport success alone must never set this true. */
|
|
92
|
+
verified: boolean;
|
|
93
|
+
result?: T;
|
|
94
|
+
observedState?: BrowserObservedState;
|
|
95
|
+
preconditionEvidence: readonly BrowserConditionEvidence[];
|
|
96
|
+
postconditionEvidence: readonly BrowserConditionEvidence[];
|
|
97
|
+
timing: BrowserTransactionTiming;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
export interface BrowserRuntimeBudget {
|
|
101
|
+
p95Ms: number;
|
|
102
|
+
maxProviderCallsWarm: number;
|
|
103
|
+
maxProviderCallsCold: number;
|
|
104
|
+
foregroundAllowed: boolean;
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Architecture targets, not per-host promises. Live V2 gates report p50/p95 and fail when a
|
|
109
|
+
* regression materially exceeds these budgets without attributable provider evidence.
|
|
110
|
+
*/
|
|
111
|
+
export const BROWSER_RUNTIME_V2_BUDGETS = {
|
|
112
|
+
read: { p95Ms: 350, maxProviderCallsWarm: 1, maxProviderCallsCold: 2, foregroundAllowed: false },
|
|
113
|
+
domInteraction: { p95Ms: 600, maxProviderCallsWarm: 1, maxProviderCallsCold: 2, foregroundAllowed: false },
|
|
114
|
+
internalResource: { p95Ms: 700, maxProviderCallsWarm: 1, maxProviderCallsCold: 2, foregroundAllowed: false },
|
|
115
|
+
physicalInput: { p95Ms: 1_500, maxProviderCallsWarm: 1, maxProviderCallsCold: 2, foregroundAllowed: true },
|
|
116
|
+
} as const satisfies Record<string, BrowserRuntimeBudget>;
|
|
117
|
+
|
|
118
|
+
export function browserTargetKey(target: BrowserTargetIdentity): string {
|
|
119
|
+
return [target.providerId, target.browserProduct ?? '', target.resourceKind, target.resourceId, target.ownership].join(':');
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
export function providerSupports(
|
|
123
|
+
provider: BrowserProviderCapabilities,
|
|
124
|
+
required: readonly BrowserProviderCapability[],
|
|
125
|
+
): boolean {
|
|
126
|
+
const available = new Set(provider.capabilities);
|
|
127
|
+
return required.every((capability) => available.has(capability));
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
export function browserTransactionVerified(result: BrowserTransactionResult): boolean {
|
|
131
|
+
return result.transportSucceeded
|
|
132
|
+
&& result.actionPerformed
|
|
133
|
+
&& result.verified
|
|
134
|
+
&& result.preconditionEvidence.every((entry) => entry.satisfied)
|
|
135
|
+
&& result.postconditionEvidence.every((entry) => entry.satisfied);
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
export function browserTransactionRequiresPostcondition(transaction: BrowserTransaction): boolean {
|
|
139
|
+
return transaction.replaySafety !== 'read_only';
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
export function validateBrowserTransaction(transaction: BrowserTransaction): string[] {
|
|
143
|
+
const errors: string[] = [];
|
|
144
|
+
if (!transaction.transactionId.trim()) errors.push('transactionId is required');
|
|
145
|
+
if (!transaction.target.providerId.trim() || !transaction.target.resourceId.trim()) errors.push('stable target identity is incomplete');
|
|
146
|
+
if (!Number.isFinite(transaction.timeoutMs) || transaction.timeoutMs <= 0) errors.push('timeoutMs must be positive');
|
|
147
|
+
if (transaction.foreground === 'none' && transaction.requiredCapabilities.includes('browser.foreground')) {
|
|
148
|
+
errors.push('foreground capability cannot be required by a foreground-free transaction');
|
|
149
|
+
}
|
|
150
|
+
if (browserTransactionRequiresPostcondition(transaction) && (transaction.postconditions?.length ?? 0) === 0) {
|
|
151
|
+
errors.push('interactive transactions require at least one postcondition');
|
|
152
|
+
}
|
|
153
|
+
return errors;
|
|
154
|
+
}
|