@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,98 @@
|
|
|
1
|
+
export type BrowserMode = 'attach_preferred' | 'managed_persistent' | 'isolated';
|
|
2
|
+
export type BrowserProfileMode = 'repo_local' | 'custom';
|
|
3
|
+
export type BrowserCdpAttachFallback = 'managed_persistent' | 'fail_closed';
|
|
4
|
+
export type BrowserNativeProduct = 'chrome' | 'vivaldi';
|
|
5
|
+
export type BrowserTabMatchReason = 'owned_token' | 'recovered_tab' | 'saved_url_title' | 'saved_url' | 'exact_url' | 'blank' | 'new_page';
|
|
6
|
+
|
|
7
|
+
export interface CdpAttachAttempt {
|
|
8
|
+
endpoint: string;
|
|
9
|
+
discoveredEndpoint?: string;
|
|
10
|
+
probeUrl?: string;
|
|
11
|
+
browserVersion?: string;
|
|
12
|
+
error?: string;
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
export interface BrowserNativeAttachAttempt {
|
|
16
|
+
product: BrowserNativeProduct;
|
|
17
|
+
appName: string;
|
|
18
|
+
bundleId: string;
|
|
19
|
+
status: 'selected' | 'available' | 'not_installed' | 'not_running' | 'unavailable';
|
|
20
|
+
frontmost?: boolean;
|
|
21
|
+
error?: string;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export interface BrowserSessionResumeDiagnostic {
|
|
25
|
+
sessionId: string;
|
|
26
|
+
status: 'matched' | 'stale_tab' | 'no_saved_tab';
|
|
27
|
+
reason: string;
|
|
28
|
+
savedTab?: Pick<BrowserTabResumeState, 'key' | 'url' | 'title' | 'index'>;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export interface BrowserConnectionFallback {
|
|
32
|
+
policy: BrowserCdpAttachFallback;
|
|
33
|
+
from: 'attach_preferred';
|
|
34
|
+
to: 'managed_persistent';
|
|
35
|
+
reason: string;
|
|
36
|
+
attempts: CdpAttachAttempt[];
|
|
37
|
+
nativeAttempts?: BrowserNativeAttachAttempt[];
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export interface BrowserTabResumeState {
|
|
41
|
+
key: string;
|
|
42
|
+
index: number;
|
|
43
|
+
url: string;
|
|
44
|
+
title?: string;
|
|
45
|
+
matchedBy: BrowserTabMatchReason;
|
|
46
|
+
inventoryCount: number;
|
|
47
|
+
capturedAt: string;
|
|
48
|
+
ownership?: 'plugin_owned' | 'user_owned';
|
|
49
|
+
ownerToken?: string;
|
|
50
|
+
windowId?: string;
|
|
51
|
+
tabId?: string;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
export interface BrowserSessionConnectionState {
|
|
55
|
+
mode: BrowserMode;
|
|
56
|
+
activeMode: BrowserMode;
|
|
57
|
+
provider: 'playwright-cdp' | 'playwright-persistent-context' | 'macos-apple-events';
|
|
58
|
+
endpoint?: string;
|
|
59
|
+
browserVersion?: string;
|
|
60
|
+
browserProduct?: BrowserNativeProduct;
|
|
61
|
+
nativeBrowserCandidates?: BrowserNativeProduct[];
|
|
62
|
+
fallback?: BrowserConnectionFallback;
|
|
63
|
+
tab?: BrowserTabResumeState;
|
|
64
|
+
sessionResume?: BrowserSessionResumeDiagnostic;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export interface BrowserSessionState {
|
|
68
|
+
schemaVersion: 1;
|
|
69
|
+
sessionId: string;
|
|
70
|
+
url: string;
|
|
71
|
+
title?: string;
|
|
72
|
+
createdAt: string;
|
|
73
|
+
updatedAt: string;
|
|
74
|
+
browser?: BrowserSessionConnectionState;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
export type BrowserSessionLiveness = 'live' | 'unverified' | 'dead';
|
|
78
|
+
export type BrowserSessionLivenessEvidence =
|
|
79
|
+
| 'runtime_session_binding'
|
|
80
|
+
| 'runtime_owner_token'
|
|
81
|
+
| 'runtime_bound_page_missing'
|
|
82
|
+
| 'runtime_context_unavailable'
|
|
83
|
+
| 'runtime_owner_token_not_observed'
|
|
84
|
+
| 'provider_unverified'
|
|
85
|
+
| 'native_tab_live'
|
|
86
|
+
| 'native_tab_missing'
|
|
87
|
+
| 'native_tab_invalid'
|
|
88
|
+
| 'native_tab_invalid_closed'
|
|
89
|
+
| 'native_tab_invalid_close_failed'
|
|
90
|
+
| 'native_inventory_unavailable';
|
|
91
|
+
|
|
92
|
+
export interface BrowserSessionInventoryItem {
|
|
93
|
+
session: BrowserSessionState;
|
|
94
|
+
liveness: BrowserSessionLiveness;
|
|
95
|
+
evidence: BrowserSessionLivenessEvidence;
|
|
96
|
+
pruned?: boolean;
|
|
97
|
+
cleanupError?: string;
|
|
98
|
+
}
|
|
@@ -0,0 +1,354 @@
|
|
|
1
|
+
import { randomUUID } from 'crypto';
|
|
2
|
+
import { existsSync, mkdirSync } from 'fs';
|
|
3
|
+
import { join } from 'path';
|
|
4
|
+
import { controllerSystemRoot } from '../../cli/repositories/controller-home';
|
|
5
|
+
import { ControllerLockContentionError, withControllerLock } from '../../cli/repositories/locks';
|
|
6
|
+
import { readJsonFile, writeJsonAtomic } from '../shared/json-files';
|
|
7
|
+
import type { AssistantPluginActionRisk, AssistantPluginAuthorizationTarget } from './types';
|
|
8
|
+
|
|
9
|
+
export const DEFAULT_PLUGIN_CAPABILITY_GRANT_MINUTES = 30 * 24 * 60;
|
|
10
|
+
const MAX_PLUGIN_CAPABILITY_GRANT_MINUTES = 90 * 24 * 60;
|
|
11
|
+
|
|
12
|
+
export type PluginCapabilityAuthorizationGrantErrorCode =
|
|
13
|
+
| 'PLUGIN_CAPABILITY_GRANT_STORE_CORRUPT'
|
|
14
|
+
| 'PLUGIN_CAPABILITY_GRANT_STORE_BUSY'
|
|
15
|
+
| 'PLUGIN_CAPABILITY_GRANT_ARGUMENT_INVALID'
|
|
16
|
+
| 'PLUGIN_CAPABILITY_GRANT_DESTRUCTIVE_DENIED'
|
|
17
|
+
| 'PLUGIN_CAPABILITY_GRANT_NOT_FOUND'
|
|
18
|
+
| 'PLUGIN_CAPABILITY_GRANT_OWNER_MISMATCH';
|
|
19
|
+
|
|
20
|
+
export class PluginCapabilityAuthorizationGrantError extends Error {
|
|
21
|
+
constructor(
|
|
22
|
+
public readonly code: PluginCapabilityAuthorizationGrantErrorCode,
|
|
23
|
+
message: string,
|
|
24
|
+
) {
|
|
25
|
+
super(message);
|
|
26
|
+
this.name = 'PluginCapabilityAuthorizationGrantError';
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
export interface PluginCapabilityAuthorizationGrant {
|
|
31
|
+
schemaVersion: 1;
|
|
32
|
+
grantId: string;
|
|
33
|
+
ownerScope: string;
|
|
34
|
+
repoId: string;
|
|
35
|
+
pluginId: string;
|
|
36
|
+
capabilityId: string;
|
|
37
|
+
target: AssistantPluginAuthorizationTarget;
|
|
38
|
+
scopes: string[];
|
|
39
|
+
riskCeiling: Exclude<AssistantPluginActionRisk, 'destructive'>;
|
|
40
|
+
createdAt: string;
|
|
41
|
+
updatedAt: string;
|
|
42
|
+
expiresAt: string;
|
|
43
|
+
revokedAt?: string;
|
|
44
|
+
revokedReason?: string;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
interface PluginCapabilityAuthorizationGrantStore {
|
|
48
|
+
schemaVersion: 1;
|
|
49
|
+
grants: PluginCapabilityAuthorizationGrant[];
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
export interface PluginCapabilityAuthorizationQuery {
|
|
53
|
+
ownerScope: string;
|
|
54
|
+
repoId: string;
|
|
55
|
+
pluginId: string;
|
|
56
|
+
capabilityId: string;
|
|
57
|
+
target: AssistantPluginAuthorizationTarget;
|
|
58
|
+
scopes: readonly string[];
|
|
59
|
+
risk: AssistantPluginActionRisk;
|
|
60
|
+
at?: Date;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
export interface RecordPluginCapabilityAuthorizationInput extends Omit<PluginCapabilityAuthorizationQuery, 'risk' | 'at'> {
|
|
64
|
+
riskCeiling: AssistantPluginActionRisk;
|
|
65
|
+
expiresInMinutes?: number;
|
|
66
|
+
now?: Date;
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
export interface RevokePluginCapabilityAuthorizationInput {
|
|
70
|
+
grantId: string;
|
|
71
|
+
ownerScope: string;
|
|
72
|
+
reason: string;
|
|
73
|
+
now?: Date;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
const RISK_RANK: Record<AssistantPluginActionRisk, number> = {
|
|
77
|
+
readonly: 0,
|
|
78
|
+
workspace_write: 1,
|
|
79
|
+
remote_write: 2,
|
|
80
|
+
destructive: 3,
|
|
81
|
+
};
|
|
82
|
+
|
|
83
|
+
const GENERIC_PLUGIN_AUTHORIZATION_ACTORS = new Set(['', 'anonymous', 'plugin_action_execute']);
|
|
84
|
+
|
|
85
|
+
export function pluginCapabilityAuthorizationOwnerScope(origin: { surface: string; actor?: string }): string {
|
|
86
|
+
const actor = origin.actor?.trim() ?? '';
|
|
87
|
+
if (GENERIC_PLUGIN_AUTHORIZATION_ACTORS.has(actor)) return 'controller:shared';
|
|
88
|
+
return `${origin.surface}:${actor}`;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
function required(value: string, label: string): string {
|
|
92
|
+
const normalized = value.trim();
|
|
93
|
+
if (!normalized) {
|
|
94
|
+
throw new PluginCapabilityAuthorizationGrantError(
|
|
95
|
+
'PLUGIN_CAPABILITY_GRANT_ARGUMENT_INVALID',
|
|
96
|
+
`${label} is required.`,
|
|
97
|
+
);
|
|
98
|
+
}
|
|
99
|
+
return normalized;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function normalizeScopes(scopes: readonly string[]): string[] {
|
|
103
|
+
return [...new Set(scopes.map((scope) => scope.trim()).filter(Boolean))].sort();
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
function normalizeTarget(target: AssistantPluginAuthorizationTarget): AssistantPluginAuthorizationTarget {
|
|
107
|
+
const kind = required(target.kind, 'target.kind');
|
|
108
|
+
const id = required(target.id, 'target.id');
|
|
109
|
+
const identityFingerprint = target.identityFingerprint?.trim();
|
|
110
|
+
return {
|
|
111
|
+
kind,
|
|
112
|
+
id,
|
|
113
|
+
...(identityFingerprint ? { identityFingerprint } : {}),
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
function validateRisk(value: unknown, index: number): Exclude<AssistantPluginActionRisk, 'destructive'> {
|
|
118
|
+
if (value === 'readonly' || value === 'workspace_write' || value === 'remote_write') return value;
|
|
119
|
+
throw new Error(`grants[${index}].riskCeiling is invalid`);
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
function validatePersistedGrant(value: unknown, index: number): PluginCapabilityAuthorizationGrant {
|
|
123
|
+
if (!value || typeof value !== 'object' || Array.isArray(value)) throw new Error(`grants[${index}] must be an object`);
|
|
124
|
+
const record = value as Record<string, unknown>;
|
|
125
|
+
const string = (field: string) => {
|
|
126
|
+
const current = record[field];
|
|
127
|
+
if (typeof current !== 'string' || !current.trim()) throw new Error(`grants[${index}].${field} is invalid`);
|
|
128
|
+
return current.trim();
|
|
129
|
+
};
|
|
130
|
+
const targetValue = record.target;
|
|
131
|
+
if (!targetValue || typeof targetValue !== 'object' || Array.isArray(targetValue)) throw new Error(`grants[${index}].target is invalid`);
|
|
132
|
+
const targetRecord = targetValue as Record<string, unknown>;
|
|
133
|
+
if (typeof targetRecord.kind !== 'string' || !targetRecord.kind.trim() || typeof targetRecord.id !== 'string' || !targetRecord.id.trim()) {
|
|
134
|
+
throw new Error(`grants[${index}].target identity is invalid`);
|
|
135
|
+
}
|
|
136
|
+
if (targetRecord.identityFingerprint !== undefined && typeof targetRecord.identityFingerprint !== 'string') {
|
|
137
|
+
throw new Error(`grants[${index}].target.identityFingerprint is invalid`);
|
|
138
|
+
}
|
|
139
|
+
if (!Array.isArray(record.scopes) || record.scopes.some((scope) => typeof scope !== 'string')) {
|
|
140
|
+
throw new Error(`grants[${index}].scopes is invalid`);
|
|
141
|
+
}
|
|
142
|
+
const createdAt = string('createdAt');
|
|
143
|
+
const updatedAt = string('updatedAt');
|
|
144
|
+
const expiresAt = string('expiresAt');
|
|
145
|
+
if (![createdAt, updatedAt, expiresAt].every((timestamp) => Number.isFinite(Date.parse(timestamp)))) {
|
|
146
|
+
throw new Error(`grants[${index}] has invalid timestamps`);
|
|
147
|
+
}
|
|
148
|
+
if (Date.parse(expiresAt) < Date.parse(createdAt)) throw new Error(`grants[${index}] expires before creation`);
|
|
149
|
+
const revokedAt = record.revokedAt;
|
|
150
|
+
const revokedReason = record.revokedReason;
|
|
151
|
+
if (revokedAt !== undefined && (typeof revokedAt !== 'string' || !Number.isFinite(Date.parse(revokedAt)))) {
|
|
152
|
+
throw new Error(`grants[${index}].revokedAt is invalid`);
|
|
153
|
+
}
|
|
154
|
+
if (revokedReason !== undefined && typeof revokedReason !== 'string') throw new Error(`grants[${index}].revokedReason is invalid`);
|
|
155
|
+
return {
|
|
156
|
+
schemaVersion: 1,
|
|
157
|
+
grantId: string('grantId'),
|
|
158
|
+
ownerScope: string('ownerScope'),
|
|
159
|
+
repoId: string('repoId'),
|
|
160
|
+
pluginId: string('pluginId'),
|
|
161
|
+
capabilityId: string('capabilityId'),
|
|
162
|
+
target: normalizeTarget({
|
|
163
|
+
kind: targetRecord.kind,
|
|
164
|
+
id: targetRecord.id,
|
|
165
|
+
...(typeof targetRecord.identityFingerprint === 'string' && targetRecord.identityFingerprint.trim()
|
|
166
|
+
? { identityFingerprint: targetRecord.identityFingerprint.trim() }
|
|
167
|
+
: {}),
|
|
168
|
+
}),
|
|
169
|
+
scopes: normalizeScopes(record.scopes as string[]),
|
|
170
|
+
riskCeiling: validateRisk(record.riskCeiling, index),
|
|
171
|
+
createdAt,
|
|
172
|
+
updatedAt,
|
|
173
|
+
expiresAt,
|
|
174
|
+
...(typeof revokedAt === 'string' ? { revokedAt } : {}),
|
|
175
|
+
...(typeof revokedReason === 'string' ? { revokedReason } : {}),
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
export function pluginCapabilityAuthorizationGrantStorePath(controllerHome: string): string {
|
|
180
|
+
return join(controllerSystemRoot(controllerHome), 'plugin-capability-authorizations', 'grants.json');
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
function loadStore(controllerHome: string): PluginCapabilityAuthorizationGrantStore {
|
|
184
|
+
const path = pluginCapabilityAuthorizationGrantStorePath(controllerHome);
|
|
185
|
+
if (!existsSync(path)) return { schemaVersion: 1, grants: [] };
|
|
186
|
+
try {
|
|
187
|
+
const raw = readJsonFile<unknown>(path);
|
|
188
|
+
if (!raw || typeof raw !== 'object' || Array.isArray(raw)) throw new Error('store must be an object');
|
|
189
|
+
const record = raw as Record<string, unknown>;
|
|
190
|
+
if (record.schemaVersion !== 1 || !Array.isArray(record.grants)) throw new Error('schemaVersion/grants are invalid');
|
|
191
|
+
return { schemaVersion: 1, grants: record.grants.map(validatePersistedGrant) };
|
|
192
|
+
} catch (error) {
|
|
193
|
+
throw new PluginCapabilityAuthorizationGrantError(
|
|
194
|
+
'PLUGIN_CAPABILITY_GRANT_STORE_CORRUPT',
|
|
195
|
+
`Plugin capability authorization store is corrupt: ${error instanceof Error ? error.message : String(error)}`,
|
|
196
|
+
);
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
function saveStore(controllerHome: string, store: PluginCapabilityAuthorizationGrantStore): void {
|
|
201
|
+
const path = pluginCapabilityAuthorizationGrantStorePath(controllerHome);
|
|
202
|
+
mkdirSync(join(controllerSystemRoot(controllerHome), 'plugin-capability-authorizations'), { recursive: true });
|
|
203
|
+
writeJsonAtomic(path, store);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function targetMatches(left: AssistantPluginAuthorizationTarget, right: AssistantPluginAuthorizationTarget): boolean {
|
|
207
|
+
return left.kind === right.kind
|
|
208
|
+
&& left.id === right.id
|
|
209
|
+
&& (left.identityFingerprint ?? '') === (right.identityFingerprint ?? '');
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function scopesContain(granted: readonly string[], requested: readonly string[]): boolean {
|
|
213
|
+
const available = new Set(granted);
|
|
214
|
+
return requested.every((scope) => available.has(scope));
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
export function findActivePluginCapabilityAuthorization(
|
|
218
|
+
controllerHome: string,
|
|
219
|
+
query: PluginCapabilityAuthorizationQuery,
|
|
220
|
+
): PluginCapabilityAuthorizationGrant | undefined {
|
|
221
|
+
if (query.risk === 'destructive') return undefined;
|
|
222
|
+
const ownerScope = required(query.ownerScope, 'ownerScope');
|
|
223
|
+
const repoId = required(query.repoId, 'repoId');
|
|
224
|
+
const pluginId = required(query.pluginId, 'pluginId');
|
|
225
|
+
const capabilityId = required(query.capabilityId, 'capabilityId');
|
|
226
|
+
const target = normalizeTarget(query.target);
|
|
227
|
+
const scopes = normalizeScopes(query.scopes);
|
|
228
|
+
const atMs = (query.at ?? new Date()).getTime();
|
|
229
|
+
return loadStore(controllerHome).grants
|
|
230
|
+
.filter((grant) => !grant.revokedAt && Date.parse(grant.expiresAt) > atMs)
|
|
231
|
+
.filter((grant) => grant.ownerScope === ownerScope
|
|
232
|
+
&& grant.repoId === repoId
|
|
233
|
+
&& grant.pluginId === pluginId
|
|
234
|
+
&& grant.capabilityId === capabilityId
|
|
235
|
+
&& targetMatches(grant.target, target)
|
|
236
|
+
&& scopesContain(grant.scopes, scopes)
|
|
237
|
+
&& RISK_RANK[grant.riskCeiling] >= RISK_RANK[query.risk])
|
|
238
|
+
.sort((left, right) => Date.parse(right.updatedAt) - Date.parse(left.updatedAt))[0];
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
export function recordPluginCapabilityAuthorization(
|
|
242
|
+
controllerHome: string,
|
|
243
|
+
input: RecordPluginCapabilityAuthorizationInput,
|
|
244
|
+
): PluginCapabilityAuthorizationGrant {
|
|
245
|
+
if (input.riskCeiling === 'destructive') {
|
|
246
|
+
throw new PluginCapabilityAuthorizationGrantError(
|
|
247
|
+
'PLUGIN_CAPABILITY_GRANT_DESTRUCTIVE_DENIED',
|
|
248
|
+
'Destructive plugin actions can never establish reusable capability authorization.',
|
|
249
|
+
);
|
|
250
|
+
}
|
|
251
|
+
const now = input.now ?? new Date();
|
|
252
|
+
const ownerScope = required(input.ownerScope, 'ownerScope');
|
|
253
|
+
const repoId = required(input.repoId, 'repoId');
|
|
254
|
+
const pluginId = required(input.pluginId, 'pluginId');
|
|
255
|
+
const capabilityId = required(input.capabilityId, 'capabilityId');
|
|
256
|
+
const target = normalizeTarget(input.target);
|
|
257
|
+
const scopes = normalizeScopes(input.scopes);
|
|
258
|
+
const rawMinutes = input.expiresInMinutes ?? DEFAULT_PLUGIN_CAPABILITY_GRANT_MINUTES;
|
|
259
|
+
if (!Number.isFinite(rawMinutes) || rawMinutes <= 0) {
|
|
260
|
+
throw new PluginCapabilityAuthorizationGrantError('PLUGIN_CAPABILITY_GRANT_ARGUMENT_INVALID', 'expiresInMinutes must be positive.');
|
|
261
|
+
}
|
|
262
|
+
const expiresInMinutes = Math.min(Math.max(1, Math.trunc(rawMinutes)), MAX_PLUGIN_CAPABILITY_GRANT_MINUTES);
|
|
263
|
+
const timestamp = now.toISOString();
|
|
264
|
+
const grant: PluginCapabilityAuthorizationGrant = {
|
|
265
|
+
schemaVersion: 1,
|
|
266
|
+
grantId: `plugin-grant-${randomUUID()}`,
|
|
267
|
+
ownerScope,
|
|
268
|
+
repoId,
|
|
269
|
+
pluginId,
|
|
270
|
+
capabilityId,
|
|
271
|
+
target,
|
|
272
|
+
scopes,
|
|
273
|
+
riskCeiling: input.riskCeiling,
|
|
274
|
+
createdAt: timestamp,
|
|
275
|
+
updatedAt: timestamp,
|
|
276
|
+
expiresAt: new Date(now.getTime() + expiresInMinutes * 60_000).toISOString(),
|
|
277
|
+
};
|
|
278
|
+
try {
|
|
279
|
+
return withControllerLock(
|
|
280
|
+
controllerHome,
|
|
281
|
+
{ scope: 'global', resource: 'plugin-capability-authorization-grants' },
|
|
282
|
+
`plugin-capability-grant:${ownerScope}`,
|
|
283
|
+
() => {
|
|
284
|
+
const store = loadStore(controllerHome);
|
|
285
|
+
store.grants = store.grants.filter((entry) => !(entry.ownerScope === ownerScope
|
|
286
|
+
&& entry.repoId === repoId
|
|
287
|
+
&& entry.pluginId === pluginId
|
|
288
|
+
&& entry.capabilityId === capabilityId
|
|
289
|
+
&& entry.target.kind === target.kind
|
|
290
|
+
&& entry.target.id === target.id));
|
|
291
|
+
store.grants.push(grant);
|
|
292
|
+
saveStore(controllerHome, store);
|
|
293
|
+
return grant;
|
|
294
|
+
},
|
|
295
|
+
5_000,
|
|
296
|
+
);
|
|
297
|
+
} catch (error) {
|
|
298
|
+
if (error instanceof ControllerLockContentionError) {
|
|
299
|
+
throw new PluginCapabilityAuthorizationGrantError('PLUGIN_CAPABILITY_GRANT_STORE_BUSY', error.message);
|
|
300
|
+
}
|
|
301
|
+
throw error;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
export function revokePluginCapabilityAuthorization(
|
|
306
|
+
controllerHome: string,
|
|
307
|
+
input: RevokePluginCapabilityAuthorizationInput,
|
|
308
|
+
): PluginCapabilityAuthorizationGrant {
|
|
309
|
+
const grantId = required(input.grantId, 'grantId');
|
|
310
|
+
const ownerScope = required(input.ownerScope, 'ownerScope');
|
|
311
|
+
const reason = required(input.reason, 'reason');
|
|
312
|
+
const now = input.now ?? new Date();
|
|
313
|
+
try {
|
|
314
|
+
return withControllerLock(
|
|
315
|
+
controllerHome,
|
|
316
|
+
{ scope: 'global', resource: 'plugin-capability-authorization-grants' },
|
|
317
|
+
`plugin-capability-revoke:${ownerScope}`,
|
|
318
|
+
() => {
|
|
319
|
+
const store = loadStore(controllerHome);
|
|
320
|
+
const index = store.grants.findIndex((grant) => grant.grantId === grantId);
|
|
321
|
+
if (index < 0) throw new PluginCapabilityAuthorizationGrantError('PLUGIN_CAPABILITY_GRANT_NOT_FOUND', `Grant ${grantId} was not found.`);
|
|
322
|
+
const current = store.grants[index]!;
|
|
323
|
+
if (current.ownerScope !== ownerScope) {
|
|
324
|
+
throw new PluginCapabilityAuthorizationGrantError('PLUGIN_CAPABILITY_GRANT_OWNER_MISMATCH', `Grant ${grantId} belongs to another owner scope.`);
|
|
325
|
+
}
|
|
326
|
+
const revoked: PluginCapabilityAuthorizationGrant = {
|
|
327
|
+
...current,
|
|
328
|
+
updatedAt: now.toISOString(),
|
|
329
|
+
revokedAt: now.toISOString(),
|
|
330
|
+
revokedReason: reason,
|
|
331
|
+
};
|
|
332
|
+
store.grants[index] = revoked;
|
|
333
|
+
saveStore(controllerHome, store);
|
|
334
|
+
return revoked;
|
|
335
|
+
},
|
|
336
|
+
5_000,
|
|
337
|
+
);
|
|
338
|
+
} catch (error) {
|
|
339
|
+
if (error instanceof ControllerLockContentionError) {
|
|
340
|
+
throw new PluginCapabilityAuthorizationGrantError('PLUGIN_CAPABILITY_GRANT_STORE_BUSY', error.message);
|
|
341
|
+
}
|
|
342
|
+
throw error;
|
|
343
|
+
}
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
export function listPluginCapabilityAuthorizations(
|
|
347
|
+
controllerHome: string,
|
|
348
|
+
ownerScope?: string,
|
|
349
|
+
): PluginCapabilityAuthorizationGrant[] {
|
|
350
|
+
const normalizedOwner = ownerScope?.trim();
|
|
351
|
+
return loadStore(controllerHome).grants
|
|
352
|
+
.filter((grant) => !normalizedOwner || grant.ownerScope === normalizedOwner)
|
|
353
|
+
.map((grant) => structuredClone(grant));
|
|
354
|
+
}
|
|
@@ -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,6 +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', description: 'Explicitly activate the target application after launch/lookup. Defaults to false.' },
|
|
86
87
|
},
|
|
87
88
|
anyOf: [{ required: ['bundle_id'] }, { required: ['app_name'] }],
|
|
88
89
|
additionalProperties: false,
|
|
@@ -118,7 +119,7 @@ function desktopOperatorActions(): AssistantPluginActionDescriptor[] {
|
|
|
118
119
|
{
|
|
119
120
|
actionId: 'desktop_press',
|
|
120
121
|
title: 'Press desktop element',
|
|
121
|
-
description: '
|
|
122
|
+
description: 'Perform one bounded Accessibility semantic action in the background, including one-page list scrolling; pointer/coordinate and raw input fallback are intentionally unavailable.',
|
|
122
123
|
readOnly: false,
|
|
123
124
|
risk: 'workspace_write',
|
|
124
125
|
confirmation: 'authorization',
|
|
@@ -132,6 +133,11 @@ function desktopOperatorActions(): AssistantPluginActionDescriptor[] {
|
|
|
132
133
|
properties: {
|
|
133
134
|
interaction_id: { type: 'string' },
|
|
134
135
|
selector: SELECTOR_SCHEMA,
|
|
136
|
+
semantic_action: {
|
|
137
|
+
type: 'string',
|
|
138
|
+
enum: ['press', 'show_menu', 'pick', 'open', 'confirm', 'scroll_down_page', 'scroll_up_page'],
|
|
139
|
+
description: 'Bounded Accessibility action supported by the trusted Desktop Operator provider; scroll actions move exactly one page and default to press when omitted.',
|
|
140
|
+
},
|
|
135
141
|
},
|
|
136
142
|
required: ['interaction_id', 'selector'],
|
|
137
143
|
additionalProperties: false,
|
|
@@ -164,7 +170,7 @@ function desktopOperatorActions(): AssistantPluginActionDescriptor[] {
|
|
|
164
170
|
{
|
|
165
171
|
actionId: 'desktop_key',
|
|
166
172
|
title: 'Press desktop keys',
|
|
167
|
-
description: 'Send one bounded key chord
|
|
173
|
+
description: 'Send one bounded key chord to the exact target session after Forge independently re-establishes and verifies that application as system-frontmost at the action boundary.',
|
|
168
174
|
readOnly: false,
|
|
169
175
|
risk: 'workspace_write',
|
|
170
176
|
confirmation: 'authorization',
|
|
@@ -404,7 +410,7 @@ export function createDesktopOperatorRegistrationInput(
|
|
|
404
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'] },
|
|
405
411
|
{ capabilityId: 'desktop.session', title: 'Desktop sessions', description: 'Create and close application interaction sessions.', scopes: ['desktop.session'], actions: ['desktop_session_open', 'desktop_session_close'] },
|
|
406
412
|
{ capabilityId: 'desktop.observe', title: 'Desktop observation', description: 'Read bounded Accessibility and window state.', scopes: ['desktop.observe'], actions: ['desktop_observe'] },
|
|
407
|
-
{ 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'] },
|
|
408
414
|
{ capabilityId: 'desktop.capture', title: 'Desktop capture', description: 'Capture authorized screenshots.', scopes: ['desktop.capture'], actions: ['desktop_screenshot'] },
|
|
409
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'] },
|
|
410
416
|
{ capabilityId: 'desktop.batch', title: 'Desktop batch', description: 'Run bounded desktop action batches.', scopes: ['desktop.batch'], actions: ['desktop_batch'] },
|