@moretea-labs/forge 1.5.1 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +2 -0
- package/CHANGELOG.md +46 -0
- package/CLAUDE.md +2 -0
- package/DESIGN.md +7 -2
- package/README.md +4 -4
- package/README.zh-CN.md +2 -2
- 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/pre-edit-guard.sh +8 -39
- package/assets/hooks/prompt-guard.sh +7 -83
- package/assets/hooks/session-start-context.sh +56 -1
- package/assets/partials/05-workflow.partial.md +1 -1
- package/assets/partials-agents/03-orchestration.partial.md +1 -1
- package/assets/plugin-registry.v1.json +54 -3
- package/assets/reference-configs/document-generation.md +1 -1
- package/assets/reference-configs/harness-overview.md +1 -1
- package/assets/reference-configs/hook-operations.md +1 -1
- package/assets/reference-configs/sprint-contracts.md +3 -3
- package/assets/skill-version.json +2 -2
- package/assets/templates/contract.template.md +2 -7
- package/assets/templates/helpers/capture-plan.sh +2 -2
- package/assets/templates/helpers/check-task-workflow.sh +4 -4
- package/assets/templates/helpers/contract-worktree.sh +1 -21
- package/assets/templates/helpers/ensure-task-workflow.sh +13 -38
- package/assets/templates/helpers/migrate-project-template.sh +22 -45
- package/assets/templates/helpers/new-plan.sh +1 -1
- package/assets/templates/helpers/plan-to-todo.sh +12 -37
- package/assets/templates/helpers/ship-worktrees.sh +2 -29
- package/assets/templates/helpers/sprint-backlog.sh +6 -6
- package/assets/templates/helpers/verify-contract.sh +1 -111
- package/assets/templates/helpers/verify-sprint.sh +3 -180
- package/assets/templates/plan.template.md +1 -1
- package/assets/templates/review.template.md +9 -34
- package/assets/workflow-contract.v1.json +10 -19
- package/docs/README.md +10 -10
- package/docs/architecture/CURRENT.md +170 -0
- package/docs/architecture/EVOLUTION.md +42 -0
- package/docs/architecture/current/README.md +4 -156
- package/docs/architecture/decisions/20260803-release-identity-binding-and-exit-policy.md +2 -2
- package/docs/architecture/decisions/20260823-immediate-controller-relay.md +53 -0
- package/docs/architecture/decisions/20260826-browser-runtime-v2.md +73 -0
- package/docs/architecture/history/README.md +7 -0
- package/docs/architecture/{chatgpt-handoff-facade.md → history/chatgpt-handoff-facade.md} +4 -0
- package/docs/architecture/{global-hook-runtime.md → history/global-hook-runtime.md} +7 -3
- package/docs/architecture/{ios-semantic-automation-provider-v2.md → history/ios-semantic-automation-provider-v2.md} +6 -2
- package/docs/architecture/history.md +3 -34
- package/docs/architecture/index.md +15 -98
- package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +1 -1
- package/docs/architecture/reviews/2026-08-22-semantic-harness-convergence-acceptance.md +39 -0
- package/docs/architecture/versions/1.6.md +38 -0
- package/docs/architecture/versions/1.7.md +41 -0
- package/docs/forge-chatgpt-browser-engine.md +2 -2
- package/docs/forge-chatgpt-mcp-setup.md +4 -28
- package/docs/forge-chatgpt-supervised-automation.md +3 -3
- package/docs/forge-github-issue-launcher.md +1 -1
- package/docs/forge-runtime-self-healing-loop.md +1 -1
- package/docs/operations/controller-browser-plugin.md +38 -22
- package/docs/operations/controller-performance-and-502.md +124 -0
- package/docs/operations/controller-reliability-runbook.md +1 -1
- package/docs/operations/features.md +3 -3
- 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/tutorials/01-install-and-start.md +1 -1
- package/docs/tutorials/01-install-and-start.zh-CN.md +1 -1
- package/docs/wiki/Architecture.md +6 -6
- package/docs/wiki/Implementation.md +2 -2
- package/docs/wiki/Installation.md +1 -1
- package/docs/wiki/Integrations.md +2 -2
- package/docs/wiki/Quick-Start.md +1 -1
- package/docs/wiki/Work-Lifecycle.md +1 -1
- package/package.json +29 -10
- package/scripts/activate-source-baseline.ts +17 -13
- package/scripts/benchmark-background-check-overlap.ts +228 -0
- package/scripts/benchmark-mcp-host.sh +117 -0
- package/scripts/benchmark-mcp-transport.ts +170 -0
- package/scripts/benchmark-quality-harness.ts +146 -0
- package/scripts/benchmark-route-session-concurrency.ts +207 -6
- package/scripts/benchmark-thin-harness-mcp-e2e.ts +7 -1
- package/scripts/benchmark-typescript-navigation.ts +186 -0
- package/scripts/benchmark-work-efficiency.ts +334 -0
- package/scripts/capture-plan.sh +3 -3
- package/scripts/check-agent-tooling.sh +8 -35
- package/scripts/check-architecture-sync.sh +17 -86
- package/scripts/check-mcp-compatibility.ts +14 -0
- package/scripts/check-public-docs.sh +14 -1
- package/scripts/check-runtime-architecture.mjs +107 -64
- package/scripts/check-stable-baseline.ts +92 -0
- package/scripts/check-task-workflow.sh +4 -4
- package/scripts/contract-worktree.sh +1 -21
- package/scripts/ensure-task-workflow.sh +22 -55
- package/scripts/host-temporary-cloud-mcp-e2e.sh +265 -0
- package/scripts/install-standalone-recovery.ts +24 -0
- package/scripts/lib/project-init-lib.sh +15 -44
- package/scripts/migrate-chatgpt-automation-schedules.ts +64 -0
- package/scripts/migrate-project-template.sh +23 -1179
- package/scripts/new-plan.sh +1 -1
- package/scripts/plan-to-todo.sh +11 -36
- package/scripts/run-governed-gate.ts +2 -1
- package/scripts/ship-worktrees.sh +2 -29
- package/scripts/smoke-ios-physical-device.ts +4 -2
- package/scripts/smoke-runtime-recovery.ts +1 -35
- package/scripts/smoke-schedule-engine.ts +1 -20
- package/scripts/sprint-backlog.sh +1 -1
- package/scripts/verify-contract.sh +1 -111
- package/scripts/verify-forge-runtime.sh +0 -1
- package/scripts/verify-sprint.sh +3 -180
- package/scripts/workflow-contract.ts +1 -5
- package/src/cli/commands/chatgpt.ts +31 -2
- package/src/cli/commands/init-hook.ts +3 -3
- package/src/cli/commands/mcp.ts +9 -5
- package/src/cli/commands/plugin.ts +37 -10
- package/src/cli/commands/recovery.ts +25 -3
- package/src/cli/commands/runtime.ts +55 -3
- package/src/cli/commands/security.ts +12 -1
- package/src/cli/commands/setup-profile.ts +31 -21
- package/src/cli/controller/check-runner.ts +106 -3
- package/src/cli/controller/composite-result.ts +1 -1
- package/src/cli/controller/context/focus.ts +51 -0
- package/src/cli/controller/context/instruction-context.ts +114 -0
- package/src/cli/controller/context/known-paths.ts +76 -0
- package/src/cli/controller/context/query-planning.ts +110 -0
- package/src/cli/controller/context/rule-resolution.ts +532 -0
- package/src/cli/controller/context/source-materializer.ts +278 -0
- package/src/cli/controller/context/structural.ts +16 -0
- package/src/cli/controller/context/types.ts +286 -0
- package/src/cli/controller/context-pack.ts +898 -430
- package/src/cli/controller/launch-agents.ts +77 -2
- package/src/cli/controller/operational-plan.ts +2 -2
- package/src/cli/controller/work-mode.ts +6 -4
- package/src/cli/editing/edit-session.ts +60 -7
- package/src/cli/github/github.ts +133 -0
- package/src/cli/hook/prompt-guard-decision.ts +17 -18
- package/src/cli/hook/prompt-intents.ts +1 -1
- package/src/cli/installer/managed-entries.ts +15 -1
- package/src/cli/local-bridge/console-automations.ts +225 -67
- package/src/cli/local-bridge/console-view-models.ts +3 -35
- package/src/cli/local-bridge/facade-api.ts +2 -217
- package/src/cli/local-bridge/frontend/src/App.tsx +3 -3
- package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +153 -3
- package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +37 -4
- package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +183 -3
- package/src/cli/local-bridge/frontend/src/features/repositories/RepositoriesView.tsx +1 -1
- package/src/cli/local-bridge/frontend/src/features/work/WorkView.tsx +4 -3
- package/src/cli/local-bridge/frontend/src/lib/api.ts +18 -11
- package/src/cli/local-bridge/frontend/src/lib/routes.ts +1 -1
- package/src/cli/local-bridge/frontend/src/shell/CommandBar.tsx +1 -1
- package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +4 -3
- package/src/cli/local-bridge/frontend/src/styles.css +27 -11
- package/src/cli/local-bridge/frontend/src/types.ts +8 -7
- package/src/cli/local-bridge/job-store.ts +1 -490
- package/src/cli/local-bridge/server.ts +29 -532
- package/src/cli/local-bridge/ui-dist/app.css +1 -1
- package/src/cli/local-bridge/ui-dist/app.js +5101 -4967
- package/src/cli/mcp/auth.ts +5 -5
- package/src/cli/mcp/instructions.ts +3 -2
- package/src/cli/mcp/legacy-tool-service.ts +0 -189
- package/src/cli/mcp/multi-repository.ts +16 -5
- package/src/cli/mcp/oauth.ts +175 -26
- package/src/cli/mcp/paths.ts +8 -2
- package/src/cli/mcp/repository-tools.ts +356 -101
- package/src/cli/mcp/server.ts +625 -54
- package/src/cli/mcp/tool-capability-cache.ts +1 -1
- package/src/cli/mcp/toolset-selection.ts +1 -1
- package/src/cli/mcp/toolset.ts +42 -5
- package/src/cli/mcp/transports/http.ts +125 -15
- package/src/cli/mcp/transports/session-registry.ts +67 -5
- package/src/cli/mcp/types.ts +3 -3
- package/src/cli/repositories/command-classifier.ts +209 -14
- package/src/cli/repositories/command-executor.ts +55 -521
- package/src/cli/repositories/command-process.ts +108 -0
- package/src/cli/repositories/command-scope.ts +99 -41
- package/src/cli/repositories/controller-home.ts +7 -8
- package/src/cli/repositories/git-command-executor.ts +2 -0
- package/src/cli/repositories/locks.ts +53 -16
- package/src/cli/repositories/repository-snapshot.ts +121 -0
- package/src/cli/repositories/safe-patch.ts +6 -4
- package/src/cli/repositories/selected-path-actions.ts +17 -9
- package/src/cli/repositories/structured-git.ts +110 -10
- package/src/cli/repository/inspector.ts +565 -62
- package/src/cli/repository/session-cache.ts +58 -17
- package/src/cli/v81-entry.ts +1 -4
- package/src/effects/fs-transaction.ts +85 -17
- package/src/runtime/context/codegraph-read-provider.ts +233 -2
- package/src/runtime/context/codegraph-sidecar.cjs +74 -40
- package/src/runtime/context/generic-lsp-provider.ts +355 -0
- package/src/runtime/context/lsp-client.ts +283 -0
- package/src/runtime/context/semantic-navigation.ts +275 -0
- package/src/runtime/context/swift-navigation.ts +565 -0
- package/src/runtime/context/typescript-navigation.ts +226 -0
- package/src/runtime/control-plane/dispatch-priority.ts +28 -0
- package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +196 -2
- package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +86 -24
- package/src/runtime/control-plane/execution/execution-identity.ts +65 -14
- package/src/runtime/control-plane/execution/session-store.ts +18 -0
- package/src/runtime/control-plane/execution/validation.ts +15 -2
- package/src/runtime/control-plane/execution/verification-evidence.ts +58 -3
- package/src/runtime/control-plane/execution/work-handle-store.ts +27 -2
- package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +258 -0
- package/src/runtime/control-plane/execution/work-remote-delivery.ts +215 -0
- package/src/runtime/control-plane/execution/work-task-receipt.ts +54 -11
- package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +175 -8
- package/src/runtime/control-plane/execution/work-validation-reconciler.ts +28 -21
- package/src/runtime/control-plane/execution/work-verification-snapshot.ts +87 -9
- package/src/runtime/control-plane/facade/capability-registry.ts +129 -25
- package/src/runtime/control-plane/facade/controller-round-relay.ts +991 -0
- package/src/runtime/control-plane/facade/controller-session-store.ts +144 -14
- package/src/runtime/control-plane/facade/goal-workloop-access.ts +9 -2
- package/src/runtime/control-plane/facade/goal-workloop.ts +885 -105
- package/src/runtime/control-plane/facade/index.ts +1 -0
- package/src/runtime/control-plane/facade/plan-contract-store.ts +211 -26
- package/src/runtime/control-plane/facade/requirement-board.ts +24 -8
- 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 +55 -11
- package/src/runtime/control-plane/facade/work-contract-store.ts +204 -73
- package/src/runtime/control-plane/generated-cache-retention.ts +271 -0
- package/src/runtime/control-plane/global-scheduler/config.ts +44 -0
- package/src/runtime/control-plane/global-scheduler/dispatch-capacity.ts +80 -0
- package/src/runtime/control-plane/global-scheduler/durable-admission.ts +46 -0
- package/src/runtime/control-plane/global-scheduler/maintenance.ts +145 -0
- package/src/runtime/control-plane/global-scheduler/projection-refresh.ts +84 -0
- package/src/runtime/control-plane/global-scheduler/reconciliation.ts +3 -3
- package/src/runtime/control-plane/global-scheduler/scheduler.ts +273 -542
- package/src/runtime/control-plane/global-scheduler/source-scan.ts +59 -0
- package/src/runtime/control-plane/global-scheduler/state.ts +88 -0
- package/src/runtime/control-plane/global-scheduler/worker-attachment.ts +57 -0
- package/src/runtime/control-plane/global-scheduler/worker-exit-decision.ts +38 -0
- package/src/runtime/control-plane/global-scheduler/worker-exit-reconciler.ts +175 -0
- package/src/runtime/control-plane/global-scheduler/worker-launch.ts +111 -0
- package/src/runtime/control-plane/global-scheduler/worker-lifecycle-store.ts +64 -0
- package/src/runtime/control-plane/global-scheduler/worker-lifecycle.ts +150 -0
- package/src/runtime/control-plane/global-scheduler/worker-process.ts +119 -0
- package/src/runtime/control-plane/global-scheduler/worker-stderr.ts +60 -0
- package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +50 -0
- package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +642 -51
- package/src/runtime/control-plane/launcher/launch-reservation-store.ts +40 -0
- package/src/runtime/control-plane/launcher/thin-launcher.ts +194 -10
- package/src/runtime/control-plane/persistence/controller-home-migration.ts +487 -0
- package/src/runtime/control-plane/persistence/requirement-store.ts +15 -11
- package/src/runtime/control-plane/persistence/sqlite-store.ts +33 -2
- package/src/runtime/control-plane/release-retention.ts +62 -2
- package/src/runtime/control-plane/routing/route-policy.ts +7 -16
- package/src/runtime/control-plane/runtime-status-client.ts +0 -2
- package/src/runtime/diagnostics/mcp-timing.ts +19 -0
- package/src/runtime/diagnostics/mcp-transport-benchmark.ts +129 -0
- package/src/runtime/diagnostics/performance.ts +24 -1
- package/src/runtime/evidence/event-ledger.ts +1 -1
- package/src/runtime/execution/jobs/store.ts +58 -4
- package/src/runtime/execution/jobs/types.ts +1 -1
- package/src/runtime/execution/managed-workspace.ts +203 -1
- package/src/runtime/execution/process-runtime/check-facade.ts +52 -14
- package/src/runtime/execution/process-runtime/check-result.ts +63 -2
- package/src/runtime/execution/process-runtime/check-scheduling.ts +114 -0
- package/src/runtime/execution/process-runtime/command-facade.ts +346 -40
- package/src/runtime/execution/process-runtime/gc.ts +98 -12
- package/src/runtime/execution/process-runtime/lightweight-managed.ts +1184 -0
- package/src/runtime/execution/process-runtime/persisted-check.ts +57 -6
- package/src/runtime/execution/process-runtime/resource-claims.ts +97 -8
- package/src/runtime/execution/process-runtime/runtime.ts +59 -4
- package/src/runtime/execution/process-runtime/store.ts +7 -0
- package/src/runtime/execution/process-runtime/types.ts +2 -0
- package/src/runtime/execution/thin-harness/async-process.ts +12 -2
- package/src/runtime/execution/thin-harness/search-worker.ts +54 -5
- package/src/runtime/execution/workers/executor.ts +0 -48
- package/src/runtime/gateway/mcp/execution-tools.ts +1219 -126
- package/src/runtime/gateway/mcp/process-tools.ts +83 -37
- package/src/runtime/gateway/mcp/router.ts +185 -83
- package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +546 -0
- package/src/runtime/gateway/mcp/runtime-tools.ts +2555 -1616
- package/src/runtime/model-clients/deepseek-adapter.ts +0 -32
- package/src/runtime/plugins/app-store-connect-adapter.ts +123 -19
- package/src/runtime/plugins/browser-adapter.ts +2925 -318
- package/src/runtime/plugins/browser-automation-service.ts +10 -0
- package/src/runtime/plugins/browser-handoff-host.ts +6 -31
- package/src/runtime/plugins/browser-handoff.ts +92 -5
- package/src/runtime/plugins/browser-macos-bridge.ts +586 -45
- package/src/runtime/plugins/browser-node-bridge.ts +6 -34
- package/src/runtime/plugins/browser-provider-registry.ts +109 -0
- package/src/runtime/plugins/browser-runtime-contract.ts +152 -0
- package/src/runtime/plugins/browser-runtime.ts +183 -0
- package/src/runtime/plugins/browser-session-authority.ts +332 -0
- package/src/runtime/plugins/capability-authorization-grants.ts +354 -0
- package/src/runtime/plugins/desktop-operator-registration.ts +15 -12
- package/src/runtime/plugins/external-adapter.ts +324 -3
- package/src/runtime/plugins/external-registration.ts +84 -5
- package/src/runtime/plugins/external-unix-socket.ts +7 -1
- package/src/runtime/plugins/first-party-registry.ts +4 -2
- package/src/runtime/plugins/github-adapter.ts +125 -0
- package/src/runtime/plugins/gmail-adapter.ts +28 -2
- 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 +125 -8
- package/src/runtime/plugins/macos-capability-broker.ts +55 -9
- package/src/runtime/plugins/plugin-action-sidecar.ts +59 -0
- package/src/runtime/plugins/plugin-management-adapter.ts +181 -0
- package/src/runtime/plugins/resend-adapter.ts +1 -1
- package/src/runtime/plugins/store.ts +366 -24
- package/src/runtime/plugins/types.ts +39 -2
- package/src/runtime/plugins/xiaohongshu-publish.ts +515 -0
- package/src/runtime/projections/git-status-sampler.ts +16 -16
- package/src/runtime/projections/materialized-view.ts +28 -10
- package/src/runtime/recovery/actions.ts +0 -10
- package/src/runtime/recovery/classifier.ts +0 -9
- package/src/runtime/recovery/maintenance-executor.ts +415 -18
- package/src/runtime/recovery/probe.ts +0 -1
- package/src/runtime/recovery/types.ts +0 -1
- package/src/runtime/root/mcp-transport.ts +180 -10
- package/src/runtime/root/package-connector-service.ts +186 -8
- package/src/runtime/root/package-runtime-release.ts +143 -17
- package/src/runtime/root/package-runtime-service.ts +430 -22
- package/src/runtime/root/release-manifest.ts +7 -2
- package/src/runtime/root/release-materialize.ts +162 -19
- package/src/runtime/root/release-store.ts +48 -11
- package/src/runtime/root/runtime.ts +23 -5
- package/src/runtime/root/service.ts +100 -27
- package/src/runtime/root/types.ts +2 -0
- package/src/runtime/safe-tooling/index.ts +0 -1
- package/src/runtime/safe-tooling/ios-development.ts +321 -63
- package/src/runtime/safe-tooling/types.ts +0 -30
- 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 +702 -94
- package/src/runtime/standalone-recovery/entry.ts +99 -23
- package/src/runtime/standalone-recovery/installer.ts +19 -3
- package/src/runtime/standalone-recovery/service-labels.ts +2 -0
- package/src/runtime/standalone-recovery/watchdog-heartbeat.ts +160 -0
- package/src/runtime/workflow/schedules/browser-probe.ts +258 -0
- package/src/runtime/workflow/schedules/chatgpt-automation-migration.ts +40 -0
- package/src/runtime/workflow/schedules/engine.ts +509 -80
- package/src/runtime/workflow/schedules/github-issue-watch.ts +158 -0
- package/src/runtime/workflow/schedules/settlement.ts +48 -4
- package/src/runtime/workflow/schedules/store.ts +131 -10
- package/src/runtime/workflow/schedules/types.ts +6 -3
- package/src/runtime/workflow/schedules/work-continuation.ts +120 -34
- 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/bin/forge-desktop-helper.mjs +0 -204
- 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 -453
- package/docs/architecture/current/codegraph-context-provider.md +0 -226
- 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 -354
- package/docs/architecture/current/entity-model.md +0 -592
- 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 -144
- 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 -311
- 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 -83
- package/docs/operations/controller-desktop-plugin.md +0 -112
- 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/plugins/desktop-adapter.ts +0 -374
- package/src/runtime/safe-tooling/browser-safe.ts +0 -133
- 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
|
@@ -68,7 +68,7 @@ function desktopOperatorActions(): AssistantPluginActionDescriptor[] {
|
|
|
68
68
|
{
|
|
69
69
|
actionId: 'desktop_session_open',
|
|
70
70
|
title: 'Open desktop session',
|
|
71
|
-
description: 'Launch or locate one application
|
|
71
|
+
description: 'Launch or locate one application and create a bounded interaction session; stays in the background by default, or explicitly activates the target when activate=true.',
|
|
72
72
|
readOnly: false,
|
|
73
73
|
risk: 'workspace_write',
|
|
74
74
|
confirmation: 'authorization',
|
|
@@ -83,7 +83,7 @@ function desktopOperatorActions(): AssistantPluginActionDescriptor[] {
|
|
|
83
83
|
bundle_id: { type: 'string' },
|
|
84
84
|
app_name: { type: 'string' },
|
|
85
85
|
launch: { type: 'boolean' },
|
|
86
|
-
activate: { type: 'boolean' },
|
|
86
|
+
activate: { type: 'boolean', description: 'Explicitly activate the target application after launch/lookup. Defaults to false.' },
|
|
87
87
|
},
|
|
88
88
|
anyOf: [{ required: ['bundle_id'] }, { required: ['app_name'] }],
|
|
89
89
|
additionalProperties: false,
|
|
@@ -119,7 +119,7 @@ function desktopOperatorActions(): AssistantPluginActionDescriptor[] {
|
|
|
119
119
|
{
|
|
120
120
|
actionId: 'desktop_press',
|
|
121
121
|
title: 'Press desktop element',
|
|
122
|
-
description: 'Press one Accessibility element in
|
|
122
|
+
description: 'Press one Accessibility element semantically in the background; pointer/coordinate fallback is intentionally unavailable.',
|
|
123
123
|
readOnly: false,
|
|
124
124
|
risk: 'workspace_write',
|
|
125
125
|
confirmation: 'authorization',
|
|
@@ -133,8 +133,11 @@ function desktopOperatorActions(): AssistantPluginActionDescriptor[] {
|
|
|
133
133
|
properties: {
|
|
134
134
|
interaction_id: { type: 'string' },
|
|
135
135
|
selector: SELECTOR_SCHEMA,
|
|
136
|
-
|
|
137
|
-
|
|
136
|
+
semantic_action: {
|
|
137
|
+
type: 'string',
|
|
138
|
+
enum: ['press', 'show_menu', 'pick', 'open', 'confirm'],
|
|
139
|
+
description: 'Bounded Accessibility action supported by the trusted Desktop Operator provider; defaults to press when omitted.',
|
|
140
|
+
},
|
|
138
141
|
},
|
|
139
142
|
required: ['interaction_id', 'selector'],
|
|
140
143
|
additionalProperties: false,
|
|
@@ -143,7 +146,7 @@ function desktopOperatorActions(): AssistantPluginActionDescriptor[] {
|
|
|
143
146
|
{
|
|
144
147
|
actionId: 'desktop_type_text',
|
|
145
148
|
title: 'Type desktop text',
|
|
146
|
-
description: '
|
|
149
|
+
description: 'Set text through Accessibility in the background without synthetic keyboard input or application activation.',
|
|
147
150
|
readOnly: false,
|
|
148
151
|
risk: 'workspace_write',
|
|
149
152
|
confirmation: 'authorization',
|
|
@@ -167,7 +170,7 @@ function desktopOperatorActions(): AssistantPluginActionDescriptor[] {
|
|
|
167
170
|
{
|
|
168
171
|
actionId: 'desktop_key',
|
|
169
172
|
title: 'Press desktop keys',
|
|
170
|
-
description: 'Send one bounded key chord
|
|
173
|
+
description: 'Send one bounded key chord only when the explicit target session is already foreground; the operator never activates it.',
|
|
171
174
|
readOnly: false,
|
|
172
175
|
risk: 'workspace_write',
|
|
173
176
|
confirmation: 'authorization',
|
|
@@ -182,7 +185,7 @@ function desktopOperatorActions(): AssistantPluginActionDescriptor[] {
|
|
|
182
185
|
interaction_id: { type: 'string' },
|
|
183
186
|
keys: { type: 'array', minItems: 1, maxItems: 8, items: { type: 'string' } },
|
|
184
187
|
},
|
|
185
|
-
required: ['keys'],
|
|
188
|
+
required: ['interaction_id', 'keys'],
|
|
186
189
|
additionalProperties: false,
|
|
187
190
|
},
|
|
188
191
|
},
|
|
@@ -222,7 +225,7 @@ function desktopOperatorActions(): AssistantPluginActionDescriptor[] {
|
|
|
222
225
|
{
|
|
223
226
|
actionId: 'desktop_copy',
|
|
224
227
|
title: 'Copy from desktop session',
|
|
225
|
-
description: '
|
|
228
|
+
description: 'Send Command+C only when the explicit target session is already foreground; the operator never activates it.',
|
|
226
229
|
readOnly: false,
|
|
227
230
|
risk: 'workspace_write',
|
|
228
231
|
confirmation: 'authorization',
|
|
@@ -241,7 +244,7 @@ function desktopOperatorActions(): AssistantPluginActionDescriptor[] {
|
|
|
241
244
|
{
|
|
242
245
|
actionId: 'desktop_paste',
|
|
243
246
|
title: 'Paste into desktop session',
|
|
244
|
-
description: '
|
|
247
|
+
description: 'Send Command+V only when the explicit target session is already foreground; the operator never activates it.',
|
|
245
248
|
readOnly: false,
|
|
246
249
|
risk: 'workspace_write',
|
|
247
250
|
confirmation: 'authorization',
|
|
@@ -260,7 +263,7 @@ function desktopOperatorActions(): AssistantPluginActionDescriptor[] {
|
|
|
260
263
|
{
|
|
261
264
|
actionId: 'desktop_open_url',
|
|
262
265
|
title: 'Open desktop URL',
|
|
263
|
-
description: 'Open one absolute URL through macOS
|
|
266
|
+
description: 'Open one absolute URL through macOS background routing without activating the destination application.',
|
|
264
267
|
readOnly: false,
|
|
265
268
|
risk: 'workspace_write',
|
|
266
269
|
confirmation: 'authorization',
|
|
@@ -407,7 +410,7 @@ export function createDesktopOperatorRegistrationInput(
|
|
|
407
410
|
{ capabilityId: 'desktop.permissions', title: 'Desktop permissions', description: 'Request the official macOS permissions required by Desktop Operator capabilities.', scopes: ['desktop.permissions'], actions: ['desktop_permissions_request'] },
|
|
408
411
|
{ capabilityId: 'desktop.session', title: 'Desktop sessions', description: 'Create and close application interaction sessions.', scopes: ['desktop.session'], actions: ['desktop_session_open', 'desktop_session_close'] },
|
|
409
412
|
{ capabilityId: 'desktop.observe', title: 'Desktop observation', description: 'Read bounded Accessibility and window state.', scopes: ['desktop.observe'], actions: ['desktop_observe'] },
|
|
410
|
-
{ capabilityId: 'desktop.interact', title: 'Desktop interaction', description: 'Press, type, send keys, and open URLs.', scopes: ['desktop.interact'], actions: ['desktop_press', 'desktop_type_text', 'desktop_key', 'desktop_open_url'] },
|
|
413
|
+
{ capabilityId: 'desktop.interact', title: 'Desktop interaction', description: 'Press controls, type text, send keys, and open URLs.', scopes: ['desktop.interact'], actions: ['desktop_press', 'desktop_type_text', 'desktop_key', 'desktop_open_url'] },
|
|
411
414
|
{ capabilityId: 'desktop.capture', title: 'Desktop capture', description: 'Capture authorized screenshots.', scopes: ['desktop.capture'], actions: ['desktop_screenshot'] },
|
|
412
415
|
{ capabilityId: 'desktop.clipboard', title: 'Desktop clipboard', description: 'Read or replace plain-text clipboard content and issue explicit copy/paste in a bound session.', scopes: ['desktop.clipboard'], actions: ['desktop_clipboard_read', 'desktop_clipboard_write', 'desktop_copy', 'desktop_paste'] },
|
|
413
416
|
{ capabilityId: 'desktop.batch', title: 'Desktop batch', description: 'Run bounded desktop action batches.', scopes: ['desktop.batch'], actions: ['desktop_batch'] },
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { createHash } from 'crypto';
|
|
1
2
|
import { AssistantPluginError } from './errors';
|
|
2
3
|
import { getExternalPluginRegistration, listExternalPluginRegistrations, type ExternalPluginRegistration } from './external-registration';
|
|
3
4
|
import { callExternalUnixSocket, probeExternalUnixSocketSync, type ExternalUnixSocketCallOptions } from './external-unix-socket';
|
|
4
5
|
import { executeManagedPluginProcess, executeManagedPluginProcessSync, type ManagedPluginProcessSpec } from './managed-process-adapter';
|
|
5
|
-
import { restartVerifiedUserLaunchAgent, startVerifiedUserLaunchAgent, stopVerifiedUserLaunchAgent } from './local-system-adapter';
|
|
6
|
-
import type { AssistantPluginActionDescriptor, AssistantPluginAdapter, AssistantPluginCapability, AssistantPluginHealth, AssistantPluginManifest, AssistantPluginPermissionScope } from './types';
|
|
6
|
+
import { activateAndVerifyFrontmostApplication, restartVerifiedUserLaunchAgent, startVerifiedUserLaunchAgent, stopVerifiedUserLaunchAgent } from './local-system-adapter';
|
|
7
|
+
import type { AssistantPluginActionDescriptor, AssistantPluginAdapter, AssistantPluginAuthorizationContext, AssistantPluginCapability, AssistantPluginHealth, AssistantPluginManifest, AssistantPluginPermissionScope } from './types';
|
|
7
8
|
|
|
8
9
|
interface ProviderManifest {
|
|
9
10
|
id: string;
|
|
@@ -31,6 +32,7 @@ export interface ExternalPluginAdapterDependencies {
|
|
|
31
32
|
startVerifiedUserLaunchAgent?: (label: unknown, expectedProgram: unknown) => Record<string, unknown>;
|
|
32
33
|
stopVerifiedUserLaunchAgent?: (label: unknown, expectedProgram: unknown) => Record<string, unknown>;
|
|
33
34
|
restartVerifiedUserLaunchAgent?: (label: unknown, expectedProgram: unknown) => Record<string, unknown>;
|
|
35
|
+
activateAndVerifyFrontmostApplication?: typeof activateAndVerifyFrontmostApplication;
|
|
34
36
|
now?: () => Date;
|
|
35
37
|
}
|
|
36
38
|
|
|
@@ -59,7 +61,10 @@ function parseProviderManifest(value: Record<string, unknown>, registration: Ext
|
|
|
59
61
|
if (manifest.protocolVersion !== registration.protocolVersion) throw providerError('EXTERNAL_PLUGIN_PROTOCOL_MISMATCH', `Expected provider protocol ${registration.protocolVersion}, received ${manifest.protocolVersion || 'unknown'}.`);
|
|
60
62
|
if (manifest.scope !== registration.scope) throw providerError('EXTERNAL_PLUGIN_SCOPE_MISMATCH', `Expected provider scope ${registration.scope}, received ${manifest.scope || 'unknown'}.`);
|
|
61
63
|
if (manifest.provider !== registration.provider) throw providerError('EXTERNAL_PLUGIN_PROVIDER_MISMATCH', `Expected provider ${registration.provider}, received ${manifest.provider || 'unknown'}.`);
|
|
62
|
-
const missingActions = registration.actions
|
|
64
|
+
const missingActions = registration.actions
|
|
65
|
+
.map((action) => action.actionId)
|
|
66
|
+
.filter((actionId) => !(registration.pluginId === 'desktop_operator' && ['desktop_pointer_click', 'desktop_foreground_pointer_click'].includes(actionId)))
|
|
67
|
+
.filter((actionId) => !manifest.actions.includes(actionId));
|
|
63
68
|
if (missingActions.length > 0) throw providerError('EXTERNAL_PLUGIN_ACTION_MISMATCH', `Provider is missing registered actions: ${missingActions.join(', ')}.`);
|
|
64
69
|
return manifest;
|
|
65
70
|
}
|
|
@@ -189,6 +194,225 @@ async function callProvider(
|
|
|
189
194
|
});
|
|
190
195
|
}
|
|
191
196
|
|
|
197
|
+
function externalProviderAuthorizationContext(registration: ExternalPluginRegistration): AssistantPluginAuthorizationContext {
|
|
198
|
+
return {
|
|
199
|
+
target: {
|
|
200
|
+
kind: 'external-provider',
|
|
201
|
+
id: registration.pluginId,
|
|
202
|
+
identityFingerprint: registration.registrationFingerprint,
|
|
203
|
+
},
|
|
204
|
+
expiresInMinutes: 30 * 24 * 60,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
function desktopApplicationAuthorizationContext(
|
|
209
|
+
registration: ExternalPluginRegistration,
|
|
210
|
+
stableApplicationId: string,
|
|
211
|
+
): AssistantPluginAuthorizationContext {
|
|
212
|
+
const normalized = stableApplicationId.trim();
|
|
213
|
+
return {
|
|
214
|
+
target: {
|
|
215
|
+
kind: 'desktop-application',
|
|
216
|
+
id: normalized,
|
|
217
|
+
identityFingerprint: createHash('sha256')
|
|
218
|
+
.update(`${registration.registrationFingerprint}\0${normalized}`)
|
|
219
|
+
.digest('hex'),
|
|
220
|
+
},
|
|
221
|
+
expiresInMinutes: 30 * 24 * 60,
|
|
222
|
+
};
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
function recordArray(value: unknown): Record<string, unknown>[] {
|
|
226
|
+
return Array.isArray(value)
|
|
227
|
+
? value.filter((entry): entry is Record<string, unknown> => Boolean(entry) && typeof entry === 'object' && !Array.isArray(entry))
|
|
228
|
+
: [];
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
function firstString(record: Record<string, unknown>, ...keys: string[]): string {
|
|
232
|
+
for (const key of keys) {
|
|
233
|
+
const value = record[key];
|
|
234
|
+
if (typeof value === 'string' && value.trim()) return value.trim();
|
|
235
|
+
}
|
|
236
|
+
return '';
|
|
237
|
+
}
|
|
238
|
+
|
|
239
|
+
function firstNumber(record: Record<string, unknown>, ...keys: string[]): number | undefined {
|
|
240
|
+
for (const key of keys) {
|
|
241
|
+
const value = record[key];
|
|
242
|
+
if (typeof value === 'number' && Number.isFinite(value)) return value;
|
|
243
|
+
}
|
|
244
|
+
return undefined;
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
function desktopSession(status: Record<string, unknown>, interactionId: string): Record<string, unknown> | undefined {
|
|
248
|
+
return recordArray(status.sessions).find((session) => firstString(session, 'interactionId', 'interaction_id') === interactionId);
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
function desktopApplicationIsActive(status: Record<string, unknown>, bundleId: string, appName: string): boolean {
|
|
252
|
+
return recordArray(status.applications).some((application) => {
|
|
253
|
+
if (application.active !== true || application.terminated === true) return false;
|
|
254
|
+
const candidateBundleId = firstString(application, 'bundle_id', 'bundleIdentifier');
|
|
255
|
+
const candidateName = firstString(application, 'name', 'appName');
|
|
256
|
+
return bundleId ? candidateBundleId === bundleId : Boolean(appName) && candidateName === appName;
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
|
|
260
|
+
async function openVerifiedDesktopSession(
|
|
261
|
+
registration: ExternalPluginRegistration,
|
|
262
|
+
requestId: string,
|
|
263
|
+
args: Record<string, unknown>,
|
|
264
|
+
timeoutMs: number | undefined,
|
|
265
|
+
signal: AbortSignal | undefined,
|
|
266
|
+
dependencies: ExternalPluginAdapterDependencies,
|
|
267
|
+
): Promise<Record<string, unknown>> {
|
|
268
|
+
const bundleId = typeof args.bundle_id === 'string' ? args.bundle_id.trim() : '';
|
|
269
|
+
const appName = typeof args.app_name === 'string' ? args.app_name.trim() : '';
|
|
270
|
+
if (!bundleId && !appName) {
|
|
271
|
+
throw providerError('DESKTOP_ACTIVATION_TARGET_UNAVAILABLE', 'Activated desktop sessions require an exact bundle id or application name.');
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
let result: Record<string, unknown>;
|
|
275
|
+
let fallbackVerified = false;
|
|
276
|
+
try {
|
|
277
|
+
result = await callProvider(
|
|
278
|
+
registration,
|
|
279
|
+
requestId,
|
|
280
|
+
'desktop_session_open',
|
|
281
|
+
args,
|
|
282
|
+
timeoutMs,
|
|
283
|
+
signal,
|
|
284
|
+
dependencies,
|
|
285
|
+
);
|
|
286
|
+
} catch (error) {
|
|
287
|
+
if (!(error instanceof AssistantPluginError) || error.code !== 'APP_ACTIVATION_FAILED') throw error;
|
|
288
|
+
|
|
289
|
+
result = await callProvider(
|
|
290
|
+
registration,
|
|
291
|
+
`${requestId}:fallback-session`,
|
|
292
|
+
'desktop_session_open',
|
|
293
|
+
{ ...(bundleId ? { bundle_id: bundleId } : { app_name: appName }), launch: false, activate: false },
|
|
294
|
+
timeoutMs,
|
|
295
|
+
signal,
|
|
296
|
+
dependencies,
|
|
297
|
+
);
|
|
298
|
+
|
|
299
|
+
const activateFallback = dependencies.activateAndVerifyFrontmostApplication ?? activateAndVerifyFrontmostApplication;
|
|
300
|
+
try {
|
|
301
|
+
const fallbackProof = await activateFallback({ bundleId: bundleId || undefined, appName: appName || undefined });
|
|
302
|
+
const proofBundleId = typeof fallbackProof.bundleId === 'string' ? fallbackProof.bundleId.trim() : '';
|
|
303
|
+
const proofAppName = typeof fallbackProof.appName === 'string' ? fallbackProof.appName.trim() : '';
|
|
304
|
+
const proofMatches = bundleId ? proofBundleId === bundleId : proofAppName === appName;
|
|
305
|
+
if (fallbackProof.activated !== true || fallbackProof.verified !== true || !proofMatches) {
|
|
306
|
+
throw new AssistantPluginError(
|
|
307
|
+
'DESKTOP_ACTIVATION_FALLBACK_IDENTITY_MISMATCH',
|
|
308
|
+
`Forge fallback did not prove the exact requested application ${bundleId || appName} as frontmost.`,
|
|
309
|
+
{ retryable: true, details: { target: { bundleId, appName }, observed: { bundleId: proofBundleId, appName: proofAppName } } },
|
|
310
|
+
);
|
|
311
|
+
}
|
|
312
|
+
fallbackVerified = true;
|
|
313
|
+
} catch (fallbackError) {
|
|
314
|
+
if (fallbackError instanceof AssistantPluginError) {
|
|
315
|
+
throw new AssistantPluginError(
|
|
316
|
+
'DESKTOP_ACTIVATION_FALLBACK_FAILED',
|
|
317
|
+
`Desktop Operator activation failed and Forge could not independently verify ${bundleId || appName} as frontmost.`,
|
|
318
|
+
{
|
|
319
|
+
retryable: fallbackError.retryable,
|
|
320
|
+
details: {
|
|
321
|
+
providerErrorCode: error.code,
|
|
322
|
+
fallbackErrorCode: fallbackError.code,
|
|
323
|
+
fallbackErrorMessage: fallbackError.message,
|
|
324
|
+
target: bundleId || appName,
|
|
325
|
+
},
|
|
326
|
+
},
|
|
327
|
+
);
|
|
328
|
+
}
|
|
329
|
+
throw new AssistantPluginError(
|
|
330
|
+
'DESKTOP_ACTIVATION_FALLBACK_FAILED',
|
|
331
|
+
`Desktop Operator activation failed and Forge fallback failed for ${bundleId || appName}.`,
|
|
332
|
+
{ retryable: true, details: { providerErrorCode: error.code, fallbackErrorMessage: String(fallbackError), target: bundleId || appName } },
|
|
333
|
+
);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
if (!fallbackVerified) {
|
|
338
|
+
const activeStatus = await callProvider(
|
|
339
|
+
registration,
|
|
340
|
+
`${requestId}:active-status`,
|
|
341
|
+
'desktop_status',
|
|
342
|
+
{ limit: 500 },
|
|
343
|
+
timeoutMs,
|
|
344
|
+
signal,
|
|
345
|
+
dependencies,
|
|
346
|
+
);
|
|
347
|
+
if (!desktopApplicationIsActive(activeStatus, bundleId, appName)) {
|
|
348
|
+
throw providerError('DESKTOP_ACTIVATION_NOT_CONFIRMED', `Desktop Operator did not confirm ${bundleId || appName} as the frontmost application after activation.`, true);
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
return result;
|
|
352
|
+
}
|
|
353
|
+
|
|
354
|
+
async function resolveExternalAuthorizationContext(
|
|
355
|
+
registration: ExternalPluginRegistration,
|
|
356
|
+
input: Parameters<NonNullable<AssistantPluginAdapter['resolveAuthorizationContext']>>[0],
|
|
357
|
+
dependencies: ExternalPluginAdapterDependencies,
|
|
358
|
+
): Promise<AssistantPluginAuthorizationContext | undefined> {
|
|
359
|
+
if (EXTERNAL_PROVIDER_LIFECYCLE_ACTIONS.has(input.actionId)) {
|
|
360
|
+
return externalProviderAuthorizationContext(registration);
|
|
361
|
+
}
|
|
362
|
+
if (registration.pluginId !== 'desktop_operator') return undefined;
|
|
363
|
+
if (input.actionId === 'desktop_permissions_request') {
|
|
364
|
+
return externalProviderAuthorizationContext(registration);
|
|
365
|
+
}
|
|
366
|
+
if (input.actionId === 'desktop_session_open') {
|
|
367
|
+
const bundleId = typeof input.args.bundle_id === 'string' ? input.args.bundle_id.trim() : '';
|
|
368
|
+
return bundleId ? desktopApplicationAuthorizationContext(registration, bundleId) : undefined;
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
const interactionId = typeof input.args.interaction_id === 'string' ? input.args.interaction_id.trim() : '';
|
|
372
|
+
if (!interactionId) return undefined;
|
|
373
|
+
|
|
374
|
+
const providerManifest = await callProvider(
|
|
375
|
+
registration,
|
|
376
|
+
`${input.requestId}:authorization-target:manifest`,
|
|
377
|
+
'manifest',
|
|
378
|
+
{},
|
|
379
|
+
undefined,
|
|
380
|
+
input.signal,
|
|
381
|
+
dependencies,
|
|
382
|
+
);
|
|
383
|
+
parseProviderManifest(providerManifest, registration);
|
|
384
|
+
const status = await callProvider(
|
|
385
|
+
registration,
|
|
386
|
+
`${input.requestId}:authorization-target:desktop-status`,
|
|
387
|
+
'desktop_status',
|
|
388
|
+
{ limit: 500 },
|
|
389
|
+
input.timeoutMs,
|
|
390
|
+
input.signal,
|
|
391
|
+
dependencies,
|
|
392
|
+
);
|
|
393
|
+
const sessions = Array.isArray(status.sessions) ? status.sessions : [];
|
|
394
|
+
const session = sessions.find((entry) => {
|
|
395
|
+
if (!entry || typeof entry !== 'object' || Array.isArray(entry)) return false;
|
|
396
|
+
const record = entry as Record<string, unknown>;
|
|
397
|
+
const id = typeof record.interactionId === 'string' ? record.interactionId : typeof record.interaction_id === 'string' ? record.interaction_id : '';
|
|
398
|
+
return id === interactionId;
|
|
399
|
+
}) as Record<string, unknown> | undefined;
|
|
400
|
+
if (!session) {
|
|
401
|
+
throw providerError('EXTERNAL_PLUGIN_AUTHORIZATION_TARGET_UNAVAILABLE', `Desktop session ${interactionId} was not found while resolving its stable authorization target.`, false);
|
|
402
|
+
}
|
|
403
|
+
const bundleId = typeof session.bundleIdentifier === 'string' ? session.bundleIdentifier.trim()
|
|
404
|
+
: typeof session.bundle_identifier === 'string' ? session.bundle_identifier.trim()
|
|
405
|
+
: '';
|
|
406
|
+
const appName = typeof session.appName === 'string' ? session.appName.trim()
|
|
407
|
+
: typeof session.app_name === 'string' ? session.app_name.trim()
|
|
408
|
+
: '';
|
|
409
|
+
const stableApplicationId = bundleId || (appName ? `name:${appName.toLowerCase()}` : '');
|
|
410
|
+
if (!stableApplicationId) {
|
|
411
|
+
throw providerError('EXTERNAL_PLUGIN_AUTHORIZATION_TARGET_UNAVAILABLE', `Desktop session ${interactionId} has no stable application identity.`, false);
|
|
412
|
+
}
|
|
413
|
+
return desktopApplicationAuthorizationContext(registration, stableApplicationId);
|
|
414
|
+
}
|
|
415
|
+
|
|
192
416
|
export function getExternalPluginAdapter(controllerHome: string, pluginId: string): AssistantPluginAdapter | undefined {
|
|
193
417
|
const registration = getExternalPluginRegistration(controllerHome, pluginId);
|
|
194
418
|
return registration ? createExternalPluginAdapter(registration) : undefined;
|
|
@@ -211,6 +435,7 @@ export function createExternalPluginAdapter(
|
|
|
211
435
|
return {
|
|
212
436
|
pluginId: registration.pluginId,
|
|
213
437
|
scope: registration.scope,
|
|
438
|
+
resolveAuthorizationContext: (input) => resolveExternalAuthorizationContext(registration, input, dependencies),
|
|
214
439
|
buildManifest(previousRevision = 0, previousUpdatedAt?: string): AssistantPluginManifest {
|
|
215
440
|
const checkedAt = now().toISOString();
|
|
216
441
|
if (!registration.enabled) {
|
|
@@ -305,6 +530,102 @@ export function createExternalPluginAdapter(
|
|
|
305
530
|
);
|
|
306
531
|
parseProviderManifest(providerManifest, registration);
|
|
307
532
|
}
|
|
533
|
+
if (registration.pluginId === 'desktop_operator' && input.actionId === 'desktop_foreground_pointer_click') {
|
|
534
|
+
const sourceInteractionId = typeof input.args.interaction_id === 'string' ? input.args.interaction_id.trim() : '';
|
|
535
|
+
const requestedWindowId = typeof input.args.window_id === 'number' && Number.isInteger(input.args.window_id) ? input.args.window_id : undefined;
|
|
536
|
+
const x = typeof input.args.x === 'number' && Number.isFinite(input.args.x) ? input.args.x : undefined;
|
|
537
|
+
const y = typeof input.args.y === 'number' && Number.isFinite(input.args.y) ? input.args.y : undefined;
|
|
538
|
+
if (!sourceInteractionId || !requestedWindowId || x === undefined || y === undefined) {
|
|
539
|
+
throw providerError('DESKTOP_FOREGROUND_POINTER_ARGUMENT_INVALID', 'desktop_foreground_pointer_click requires interaction_id, window_id, x, and y.');
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
const sourceStatus = await callProvider(
|
|
543
|
+
registration,
|
|
544
|
+
`${input.requestId}:source-status`,
|
|
545
|
+
'desktop_status',
|
|
546
|
+
{ limit: 500 },
|
|
547
|
+
input.timeoutMs,
|
|
548
|
+
input.signal,
|
|
549
|
+
dependencies,
|
|
550
|
+
);
|
|
551
|
+
const sourceSession = desktopSession(sourceStatus, sourceInteractionId);
|
|
552
|
+
if (!sourceSession) {
|
|
553
|
+
throw providerError('DESKTOP_FOREGROUND_POINTER_SESSION_NOT_FOUND', `Desktop session ${sourceInteractionId} is no longer available.`, true);
|
|
554
|
+
}
|
|
555
|
+
const bundleId = firstString(sourceSession, 'bundleIdentifier', 'bundle_id');
|
|
556
|
+
const appName = firstString(sourceSession, 'appName', 'app_name');
|
|
557
|
+
if (!bundleId && !appName) {
|
|
558
|
+
throw providerError('DESKTOP_FOREGROUND_POINTER_TARGET_UNAVAILABLE', `Desktop session ${sourceInteractionId} has no stable application identity.`);
|
|
559
|
+
}
|
|
560
|
+
|
|
561
|
+
const activation = await openVerifiedDesktopSession(
|
|
562
|
+
registration,
|
|
563
|
+
`${input.requestId}:activate`,
|
|
564
|
+
{ ...(bundleId ? { bundle_id: bundleId } : { app_name: appName }), launch: false, activate: true },
|
|
565
|
+
input.timeoutMs,
|
|
566
|
+
input.signal,
|
|
567
|
+
dependencies,
|
|
568
|
+
);
|
|
569
|
+
const activationInteractionId = firstString(activation, 'interactionId', 'interaction_id');
|
|
570
|
+
if (!activationInteractionId) {
|
|
571
|
+
throw providerError('DESKTOP_ACTIVATION_SESSION_MISSING', 'Desktop Operator activated the application without returning a bound interaction session.', true);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
const screenshot = await callProvider(
|
|
575
|
+
registration,
|
|
576
|
+
`${input.requestId}:screenshot`,
|
|
577
|
+
'desktop_screenshot',
|
|
578
|
+
{
|
|
579
|
+
interaction_id: activationInteractionId,
|
|
580
|
+
scope: 'window',
|
|
581
|
+
window_id: requestedWindowId,
|
|
582
|
+
...(typeof input.args.label === 'string' && input.args.label.trim() ? { label: input.args.label.trim() } : {}),
|
|
583
|
+
},
|
|
584
|
+
input.timeoutMs,
|
|
585
|
+
input.signal,
|
|
586
|
+
dependencies,
|
|
587
|
+
);
|
|
588
|
+
const visualRevision = firstNumber(screenshot, 'visual_revision', 'visualRevision');
|
|
589
|
+
const capturedWindowId = firstNumber(screenshot, 'windowId', 'window_id') ?? requestedWindowId;
|
|
590
|
+
if (!visualRevision || !capturedWindowId) {
|
|
591
|
+
throw providerError('DESKTOP_FOREGROUND_CAPTURE_INVALID', 'Desktop Operator did not return a fresh visual revision for the foreground window.', true);
|
|
592
|
+
}
|
|
593
|
+
|
|
594
|
+
const click = await callProvider(
|
|
595
|
+
registration,
|
|
596
|
+
`${input.requestId}:click`,
|
|
597
|
+
'desktop_pointer_click',
|
|
598
|
+
{
|
|
599
|
+
interaction_id: activationInteractionId,
|
|
600
|
+
window_id: capturedWindowId,
|
|
601
|
+
visual_revision: visualRevision,
|
|
602
|
+
x,
|
|
603
|
+
y,
|
|
604
|
+
},
|
|
605
|
+
input.timeoutMs,
|
|
606
|
+
input.signal,
|
|
607
|
+
dependencies,
|
|
608
|
+
);
|
|
609
|
+
return {
|
|
610
|
+
interactionId: activationInteractionId,
|
|
611
|
+
activationVerified: true,
|
|
612
|
+
windowId: capturedWindowId,
|
|
613
|
+
visualRevision,
|
|
614
|
+
screenshot,
|
|
615
|
+
click,
|
|
616
|
+
};
|
|
617
|
+
}
|
|
618
|
+
|
|
619
|
+
if (registration.pluginId === 'desktop_operator' && input.actionId === 'desktop_session_open' && input.args.activate === true) {
|
|
620
|
+
return await openVerifiedDesktopSession(
|
|
621
|
+
registration,
|
|
622
|
+
input.requestId,
|
|
623
|
+
input.args,
|
|
624
|
+
input.timeoutMs,
|
|
625
|
+
input.signal,
|
|
626
|
+
dependencies,
|
|
627
|
+
);
|
|
628
|
+
}
|
|
308
629
|
return await callProvider(
|
|
309
630
|
registration,
|
|
310
631
|
input.requestId,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { createHash } from 'crypto';
|
|
2
|
-
import { existsSync, mkdirSync, readdirSync } from 'fs';
|
|
2
|
+
import { existsSync, mkdirSync, readdirSync, rmSync } from 'fs';
|
|
3
3
|
import { isAbsolute, join } from 'path';
|
|
4
4
|
import { controllerSystemRoot, ensureControllerHome } from '../../cli/repositories/controller-home';
|
|
5
5
|
import { readJsonFile, sanitizeFileComponent, writeJsonAtomic } from '../shared/json-files';
|
|
@@ -284,6 +284,34 @@ export function listExternalPluginRegistrations(controllerHome: string): Externa
|
|
|
284
284
|
.sort((left, right) => left.pluginId.localeCompare(right.pluginId));
|
|
285
285
|
}
|
|
286
286
|
|
|
287
|
+
export interface ExternalPluginRegistrationPreview {
|
|
288
|
+
pluginId: string;
|
|
289
|
+
currentRevision: number;
|
|
290
|
+
nextRevision: number;
|
|
291
|
+
registrationFingerprint: string;
|
|
292
|
+
currentFingerprint?: string;
|
|
293
|
+
wouldChange: boolean;
|
|
294
|
+
registration: Omit<ExternalPluginRegistration, 'revision' | 'registrationFingerprint' | 'installedAt' | 'updatedAt'>;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
export function previewExternalPluginRegistration(
|
|
298
|
+
controllerHome: string,
|
|
299
|
+
input: ExternalPluginRegistrationInput,
|
|
300
|
+
): ExternalPluginRegistrationPreview {
|
|
301
|
+
const normalized = normalizeRegistrationInput(input);
|
|
302
|
+
const existing = getExternalPluginRegistration(controllerHome, normalized.pluginId);
|
|
303
|
+
const fingerprint = registrationFingerprint(normalized);
|
|
304
|
+
return {
|
|
305
|
+
pluginId: normalized.pluginId,
|
|
306
|
+
currentRevision: existing?.revision ?? 0,
|
|
307
|
+
nextRevision: existing && existing.registrationFingerprint === fingerprint ? existing.revision : (existing?.revision ?? 0) + 1,
|
|
308
|
+
registrationFingerprint: fingerprint,
|
|
309
|
+
currentFingerprint: existing?.registrationFingerprint,
|
|
310
|
+
wouldChange: existing?.registrationFingerprint !== fingerprint,
|
|
311
|
+
registration: normalized,
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
|
|
287
315
|
export function installExternalPluginRegistration(
|
|
288
316
|
controllerHome: string,
|
|
289
317
|
input: ExternalPluginRegistrationInput,
|
|
@@ -291,14 +319,19 @@ export function installExternalPluginRegistration(
|
|
|
291
319
|
): ExternalPluginRegistration {
|
|
292
320
|
const normalized = normalizeRegistrationInput(input);
|
|
293
321
|
const existing = getExternalPluginRegistration(controllerHome, normalized.pluginId);
|
|
294
|
-
|
|
295
|
-
|
|
322
|
+
const fingerprint = registrationFingerprint(normalized);
|
|
323
|
+
// Identical desired state is idempotent, including a replay carrying the old
|
|
324
|
+
// expected revision after the first write committed.
|
|
325
|
+
if (existing?.registrationFingerprint === fingerprint) return existing;
|
|
326
|
+
const currentRevision = existing?.revision ?? 0;
|
|
327
|
+
if (options.expectedRevision !== undefined && currentRevision !== options.expectedRevision) {
|
|
328
|
+
throw new Error(`EXTERNAL_PLUGIN_REGISTRATION_REVISION_CONFLICT: expected ${options.expectedRevision}, current ${currentRevision}`);
|
|
296
329
|
}
|
|
297
330
|
const at = (options.now ?? new Date()).toISOString();
|
|
298
331
|
const next: ExternalPluginRegistration = {
|
|
299
332
|
...normalized,
|
|
300
|
-
revision:
|
|
301
|
-
registrationFingerprint:
|
|
333
|
+
revision: currentRevision + 1,
|
|
334
|
+
registrationFingerprint: fingerprint,
|
|
302
335
|
installedAt: existing?.installedAt ?? at,
|
|
303
336
|
updatedAt: at,
|
|
304
337
|
};
|
|
@@ -306,3 +339,49 @@ export function installExternalPluginRegistration(
|
|
|
306
339
|
writeJsonAtomic(externalPluginRegistrationPath(controllerHome, next.pluginId), next);
|
|
307
340
|
return next;
|
|
308
341
|
}
|
|
342
|
+
|
|
343
|
+
function registrationInputFromStored(
|
|
344
|
+
registration: ExternalPluginRegistration,
|
|
345
|
+
patch: Partial<Pick<ExternalPluginRegistrationInput, 'enabled'>> = {},
|
|
346
|
+
): ExternalPluginRegistrationInput {
|
|
347
|
+
return {
|
|
348
|
+
pluginId: registration.pluginId,
|
|
349
|
+
providerPluginId: registration.providerPluginId,
|
|
350
|
+
displayName: registration.displayName,
|
|
351
|
+
provider: registration.provider,
|
|
352
|
+
pluginVersion: registration.pluginVersion,
|
|
353
|
+
protocolVersion: registration.protocolVersion,
|
|
354
|
+
scope: registration.scope,
|
|
355
|
+
enabled: patch.enabled ?? registration.enabled,
|
|
356
|
+
transport: structuredClone(registration.transport),
|
|
357
|
+
lifecycle: registration.lifecycle ? structuredClone(registration.lifecycle) : undefined,
|
|
358
|
+
permissions: structuredClone(registration.permissions),
|
|
359
|
+
capabilities: structuredClone(registration.capabilities),
|
|
360
|
+
actions: structuredClone(registration.actions),
|
|
361
|
+
legacyIdentities: [...(registration.legacyIdentities ?? [])],
|
|
362
|
+
};
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
export function disableExternalPluginRegistration(
|
|
366
|
+
controllerHome: string,
|
|
367
|
+
pluginId: string,
|
|
368
|
+
options: { expectedRevision?: number; now?: Date } = {},
|
|
369
|
+
): ExternalPluginRegistration {
|
|
370
|
+
const existing = getExternalPluginRegistration(controllerHome, pluginId);
|
|
371
|
+
if (!existing) throw new Error(`EXTERNAL_PLUGIN_REGISTRATION_NOT_FOUND: ${pluginId}`);
|
|
372
|
+
return installExternalPluginRegistration(controllerHome, registrationInputFromStored(existing, { enabled: false }), options);
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
export function removeExternalPluginRegistration(
|
|
376
|
+
controllerHome: string,
|
|
377
|
+
pluginId: string,
|
|
378
|
+
options: { expectedRevision?: number } = {},
|
|
379
|
+
): ExternalPluginRegistration {
|
|
380
|
+
const existing = getExternalPluginRegistration(controllerHome, pluginId);
|
|
381
|
+
if (!existing) throw new Error(`EXTERNAL_PLUGIN_REGISTRATION_NOT_FOUND: ${pluginId}`);
|
|
382
|
+
if (options.expectedRevision !== undefined && existing.revision !== options.expectedRevision) {
|
|
383
|
+
throw new Error(`EXTERNAL_PLUGIN_REGISTRATION_REVISION_CONFLICT: expected ${options.expectedRevision}, current ${existing.revision}`);
|
|
384
|
+
}
|
|
385
|
+
rmSync(externalPluginRegistrationPath(controllerHome, existing.pluginId));
|
|
386
|
+
return existing;
|
|
387
|
+
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { spawnSync } from 'child_process';
|
|
2
2
|
import { existsSync } from 'fs';
|
|
3
3
|
import { createConnection, type Socket } from 'net';
|
|
4
|
-
import { dirname, isAbsolute, join } from 'path';
|
|
4
|
+
import { dirname, isAbsolute, join, resolve } from 'path';
|
|
5
5
|
import { fileURLToPath } from 'url';
|
|
6
6
|
import { resolveBunExecutable } from '../shared/process-environment';
|
|
7
7
|
import { AssistantPluginError } from './errors';
|
|
@@ -178,7 +178,13 @@ export async function callExternalUnixSocket(
|
|
|
178
178
|
export function resolveExternalPluginProbeSidecarPath(
|
|
179
179
|
execPath: string = process.execPath,
|
|
180
180
|
moduleUrl: string = import.meta.url,
|
|
181
|
+
env: NodeJS.ProcessEnv = process.env,
|
|
181
182
|
): string {
|
|
183
|
+
const configuredReleaseRoot = env.FORGE_RELEASE_PATH?.trim();
|
|
184
|
+
if (configuredReleaseRoot) {
|
|
185
|
+
const releaseSidecar = join(resolve(configuredReleaseRoot), 'external-unix-socket-probe.cjs');
|
|
186
|
+
if (existsSync(releaseSidecar)) return releaseSidecar;
|
|
187
|
+
}
|
|
182
188
|
const releaseSibling = join(dirname(execPath), 'external-unix-socket-probe.cjs');
|
|
183
189
|
if (existsSync(releaseSibling)) return releaseSibling;
|
|
184
190
|
const moduleSibling = fileURLToPath(new URL('./external-unix-socket-probe.cjs', moduleUrl));
|
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { appStoreConnectPluginAdapter } from './app-store-connect-adapter';
|
|
2
2
|
import { browserPluginAdapter } from './browser-adapter';
|
|
3
|
-
import { desktopPluginAdapter } from './desktop-adapter';
|
|
4
3
|
import { githubPluginAdapter } from './github-adapter';
|
|
5
4
|
import { gmailPluginAdapter } from './gmail-adapter';
|
|
6
5
|
import { googleCalendarPluginAdapter } from './google-calendar-adapter';
|
|
7
6
|
import { googleTasksPluginAdapter } from './google-tasks-adapter';
|
|
8
7
|
import { iosPluginAdapter } from './ios-adapter';
|
|
9
8
|
import { localSystemPluginAdapter } from './local-system-adapter';
|
|
9
|
+
import { pluginManagementAdapter } from './plugin-management-adapter';
|
|
10
10
|
import { resendPluginAdapter } from './resend-adapter';
|
|
11
|
+
import { xiaohongshuPluginAdapter } from './xiaohongshu-publish';
|
|
11
12
|
import type { AssistantPluginAdapter } from './types';
|
|
12
13
|
|
|
13
14
|
const FIRST_PARTY_PLUGIN_ADAPTERS: readonly AssistantPluginAdapter[] = [
|
|
@@ -19,8 +20,9 @@ const FIRST_PARTY_PLUGIN_ADAPTERS: readonly AssistantPluginAdapter[] = [
|
|
|
19
20
|
googleCalendarPluginAdapter,
|
|
20
21
|
googleTasksPluginAdapter,
|
|
21
22
|
resendPluginAdapter,
|
|
23
|
+
xiaohongshuPluginAdapter,
|
|
22
24
|
localSystemPluginAdapter,
|
|
23
|
-
|
|
25
|
+
pluginManagementAdapter,
|
|
24
26
|
];
|
|
25
27
|
|
|
26
28
|
export function listFirstPartyPluginAdapters(): readonly AssistantPluginAdapter[] {
|