@moretea-labs/forge 1.6.0 → 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 +26 -0
- package/CLAUDE.md +2 -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/03-orchestration.partial.md +1 -1
- package/assets/plugin-registry.v1.json +1 -1
- 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-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/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/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 +26 -6
- package/docs/operations/controller-performance-and-502.md +124 -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 +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-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-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/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/init-hook.ts +3 -3
- package/src/cli/commands/mcp.ts +9 -5
- package/src/cli/commands/plugin.ts +1 -1
- 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 +785 -469
- 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/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 +0 -217
- 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 +0 -513
- 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 +0 -189
- package/src/cli/mcp/multi-repository.ts +16 -5
- package/src/cli/mcp/oauth.ts +175 -26
- package/src/cli/mcp/repository-tools.ts +247 -107
- package/src/cli/mcp/server.ts +537 -111
- 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 +101 -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 +101 -19
- 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/selected-path-actions.ts +17 -9
- package/src/cli/repositories/structured-git.ts +61 -3
- package/src/cli/repository/inspector.ts +553 -63
- 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 +47 -8
- package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +86 -24
- 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-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 +143 -8
- package/src/runtime/control-plane/execution/work-validation-reconciler.ts +22 -14
- package/src/runtime/control-plane/execution/work-verification-snapshot.ts +85 -8
- 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 +132 -1
- package/src/runtime/control-plane/facade/goal-workloop-access.ts +2 -0
- package/src/runtime/control-plane/facade/goal-workloop.ts +686 -129
- package/src/runtime/control-plane/facade/index.ts +1 -0
- package/src/runtime/control-plane/facade/plan-contract-store.ts +185 -20
- 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 +22 -5
- package/src/runtime/control-plane/facade/work-contract-store.ts +175 -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 +355 -116
- 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/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 +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/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 +53 -0
- package/src/runtime/execution/process-runtime/check-scheduling.ts +114 -0
- package/src/runtime/execution/process-runtime/command-facade.ts +345 -40
- package/src/runtime/execution/process-runtime/gc.ts +38 -6
- package/src/runtime/execution/process-runtime/lightweight-managed.ts +1184 -0
- package/src/runtime/execution/process-runtime/persisted-check.ts +47 -4
- package/src/runtime/execution/process-runtime/resource-claims.ts +10 -6
- package/src/runtime/execution/process-runtime/runtime.ts +43 -3
- package/src/runtime/execution/process-runtime/store.ts +1 -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 +1192 -114
- package/src/runtime/gateway/mcp/process-tools.ts +83 -37
- package/src/runtime/gateway/mcp/router.ts +184 -82
- package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +546 -0
- package/src/runtime/gateway/mcp/runtime-tools.ts +2224 -1589
- package/src/runtime/plugins/app-store-connect-adapter.ts +123 -19
- package/src/runtime/plugins/browser-adapter.ts +2901 -241
- package/src/runtime/plugins/browser-automation-service.ts +10 -0
- package/src/runtime/plugins/browser-handoff.ts +92 -2
- package/src/runtime/plugins/browser-macos-bridge.ts +582 -49
- 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 +8 -2
- package/src/runtime/plugins/external-adapter.ts +324 -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 +55 -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 +269 -29
- package/src/runtime/plugins/types.ts +35 -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/maintenance-executor.ts +393 -28
- 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 +19 -0
- 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 +2 -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 +700 -92
- 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 +81 -3
- package/src/runtime/workflow/schedules/engine.ts +287 -151
- 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 +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
|
@@ -8,14 +8,16 @@ import {
|
|
|
8
8
|
writeFileSync,
|
|
9
9
|
} from 'fs';
|
|
10
10
|
import { dirname, join, resolve } from 'path';
|
|
11
|
-
import { repositoryControllerRoot } from '../../../cli/repositories/controller-home';
|
|
11
|
+
import { repoLocalNoIndexControllerHome, repositoryControllerRoot } from '../../../cli/repositories/controller-home';
|
|
12
12
|
import {
|
|
13
13
|
getRepository,
|
|
14
14
|
listRepositories,
|
|
15
15
|
selectRepositoryCheckout,
|
|
16
16
|
setRepositoryCheckoutLifecycle,
|
|
17
17
|
} from '../../../cli/repositories/registry';
|
|
18
|
+
import { managedPathInside, managedWorktreeStorageRoot } from '../../../cli/repositories/worktree-storage';
|
|
18
19
|
import { markRepositoryProjectionDirty } from '../../projections/invalidation';
|
|
20
|
+
import { listControlPlaneRecords } from '../persistence/sqlite-store';
|
|
19
21
|
import { getWorkContract } from '../facade/work-contract-store';
|
|
20
22
|
import { getControllerSession, releaseControllerSession } from '../facade/controller-session-store';
|
|
21
23
|
import { isRepositoryCompletionReceipt, isTerminalWorkContractStatus, type WorkContract } from '../facade/types';
|
|
@@ -30,6 +32,7 @@ import {
|
|
|
30
32
|
import { getProcessRecord, listProcessRecords } from '../../execution/process-runtime/store';
|
|
31
33
|
import {
|
|
32
34
|
listWorkHandles,
|
|
35
|
+
readWorkHandle,
|
|
33
36
|
transitionWorkHandle,
|
|
34
37
|
writeWorkHandle,
|
|
35
38
|
type WorkCleanupReceipt,
|
|
@@ -391,6 +394,42 @@ function selectTerminalCleanupTarget(repository: ReturnType<typeof getRepository
|
|
|
391
394
|
return repository;
|
|
392
395
|
}
|
|
393
396
|
|
|
397
|
+
function isCurrentControllerManagedWorktree(
|
|
398
|
+
controllerHome: string,
|
|
399
|
+
worktreePath: string,
|
|
400
|
+
): boolean {
|
|
401
|
+
try {
|
|
402
|
+
const storageRoot = managedWorktreeStorageRoot(
|
|
403
|
+
controllerHome,
|
|
404
|
+
listRepositories(controllerHome, { includeRemoved: true }),
|
|
405
|
+
);
|
|
406
|
+
if (managedPathInside(storageRoot, worktreePath)) return true;
|
|
407
|
+
// Older Controller releases used a namespaced sibling of controller/.
|
|
408
|
+
// Accept only namespaces whose source Home appears in this target's
|
|
409
|
+
// controlled migration receipt; then let the caller's Git common-dir,
|
|
410
|
+
// branch, cleanliness, and preservation checks prove the exact Work before
|
|
411
|
+
// any removal.
|
|
412
|
+
const sourceHomes = new Set<string>([resolve(controllerHome)]);
|
|
413
|
+
for (const record of listControlPlaneRecords<{ sourceHome?: unknown; status?: unknown }>(controllerHome, {
|
|
414
|
+
namespace: 'controller_home_migration',
|
|
415
|
+
limit: 1_000,
|
|
416
|
+
})) {
|
|
417
|
+
if (record.value.status !== 'applied' || typeof record.value.sourceHome !== 'string') continue;
|
|
418
|
+
const sourceHome = resolve(record.value.sourceHome);
|
|
419
|
+
sourceHomes.add(sourceHome);
|
|
420
|
+
const physical = repoLocalNoIndexControllerHome(sourceHome);
|
|
421
|
+
if (physical) sourceHomes.add(physical);
|
|
422
|
+
}
|
|
423
|
+
return [...sourceHomes].some((sourceHome) => {
|
|
424
|
+
const namespace = createHash('sha256').update(sourceHome).digest('hex').slice(0, 16);
|
|
425
|
+
const legacyRoot = join(dirname(resolve(controllerHome)), 'managed-worktrees', namespace);
|
|
426
|
+
return managedPathInside(legacyRoot, worktreePath);
|
|
427
|
+
});
|
|
428
|
+
} catch {
|
|
429
|
+
return false;
|
|
430
|
+
}
|
|
431
|
+
}
|
|
432
|
+
|
|
394
433
|
export interface TerminalWorkCleanupReconcileOptions {
|
|
395
434
|
nowMs?: number;
|
|
396
435
|
minAgeMs?: number;
|
|
@@ -417,11 +456,81 @@ function terminalOutcomeForContract(contract: WorkContract): WorkTerminalOutcome
|
|
|
417
456
|
return 'failed';
|
|
418
457
|
}
|
|
419
458
|
|
|
420
|
-
function
|
|
459
|
+
export function recoverTerminalWorkHandle(
|
|
460
|
+
controllerHome: string,
|
|
461
|
+
repositoryId: string,
|
|
462
|
+
workId: string,
|
|
463
|
+
): WorkHandleState | undefined {
|
|
464
|
+
const existing = readWorkHandle(controllerHome, repositoryId, workId);
|
|
465
|
+
if (existing) return existing;
|
|
466
|
+
const contract = getWorkContract({ controllerHome, repoId: repositoryId }, workId);
|
|
467
|
+
if (!contract || !isTerminalWorkContractStatus(contract.status)) return undefined;
|
|
468
|
+
const worktreePath = contract.worktreeRef?.trim();
|
|
469
|
+
const checkoutId = contract.checkoutId?.trim();
|
|
470
|
+
if (!worktreePath || !checkoutId || !existsSync(worktreePath)) return undefined;
|
|
471
|
+
if (!isCurrentControllerManagedWorktree(controllerHome, worktreePath)) return undefined;
|
|
472
|
+
|
|
473
|
+
const repository = getRepository(repositoryId, controllerHome, { includeRemoved: true });
|
|
474
|
+
// Reconstruct physical cleanup authority from the recorded checkout entry itself.
|
|
475
|
+
// selectRepositoryCheckout returns a RepositoryRecord view rooted at a checkout,
|
|
476
|
+
// not the checkout entry (and terminal cleanup must also tolerate an already
|
|
477
|
+
// archived/removed registry lifecycle while the physical worktree still exists).
|
|
478
|
+
const checkout = repository.checkouts.find((candidate) => candidate.checkoutId === checkoutId);
|
|
479
|
+
if (!checkout || checkout.worktree !== true || resolve(checkout.canonicalRoot) !== resolve(worktreePath)) return undefined;
|
|
480
|
+
const branch = checkout.branch?.trim();
|
|
481
|
+
if (!branch) return undefined;
|
|
482
|
+
|
|
483
|
+
const head = git(worktreePath, ['rev-parse', 'HEAD']);
|
|
484
|
+
if (!head.ok || !head.stdout) return undefined;
|
|
485
|
+
const targetBranch = repository.defaultBranch || 'main';
|
|
486
|
+
const targetExists = branchExists(repository.canonicalRoot, targetBranch);
|
|
487
|
+
const contained = targetExists
|
|
488
|
+
? git(repository.canonicalRoot, ['merge-base', '--is-ancestor', head.stdout, `refs/heads/${targetBranch}`]).ok
|
|
489
|
+
: false;
|
|
490
|
+
const delivered = contract.status === 'completed' && contained;
|
|
491
|
+
const session = getControllerSession({ controllerHome, repoId: repositoryId }, workId);
|
|
492
|
+
const recordedAt = nowIso();
|
|
493
|
+
return writeWorkHandle(controllerHome, {
|
|
494
|
+
schemaVersion: 1,
|
|
495
|
+
workId,
|
|
496
|
+
workContractId: workId,
|
|
497
|
+
sessionId: session?.sessionId ?? `terminal-recovery:${workId}`,
|
|
498
|
+
principalId: contract.principalId?.trim() || session?.principalId || 'terminal-recovery',
|
|
499
|
+
repositoryId,
|
|
500
|
+
checkoutId,
|
|
501
|
+
sourceCheckoutId: repository.activeCheckoutId,
|
|
502
|
+
worktreePath,
|
|
503
|
+
branch,
|
|
504
|
+
managedWorktree: true,
|
|
505
|
+
baseCommit: contract.baseRevision,
|
|
506
|
+
expectedHead: head.stdout,
|
|
507
|
+
permissionSnapshotVersion: 1,
|
|
508
|
+
state: delivered ? 'merged' : 'failed_terminal_cleanup',
|
|
509
|
+
failureReason: delivered ? undefined : 'Recovered terminal Work ownership for cleanup; target-branch containment was not proven.',
|
|
510
|
+
createdAt: contract.createdAt || recordedAt,
|
|
511
|
+
updatedAt: recordedAt,
|
|
512
|
+
cleanupResponsibility: { owner: 'work_finalizer', registeredAt: recordedAt },
|
|
513
|
+
finalization: {
|
|
514
|
+
validation: contract.status === 'failed' ? 'failed' : 'done',
|
|
515
|
+
commit: 'skipped',
|
|
516
|
+
merge: delivered ? 'done' : 'skipped',
|
|
517
|
+
branchCleanup: 'pending',
|
|
518
|
+
worktreeCleanup: 'pending',
|
|
519
|
+
...(contract.status === 'failed' ? { lastError: 'Recovered failed terminal Work for cleanup.' } : {}),
|
|
520
|
+
},
|
|
521
|
+
});
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
function cleanupRetainedByRequest(contract: WorkContract, handle: WorkHandleState): boolean {
|
|
421
525
|
// `skipped` in old WorkHandle finalization records was also used for legacy
|
|
422
526
|
// no-op/error paths, so it is not sufficient proof of an explicit retention
|
|
423
|
-
// request.
|
|
424
|
-
//
|
|
527
|
+
// request. New cancelled/terminal Work records persist an explicit physical
|
|
528
|
+
// resource disposition on the WorkHandle. Completed Work keeps the existing
|
|
529
|
+
// completion-receipt warning as the delivery authority.
|
|
530
|
+
if (
|
|
531
|
+
handle.terminalResourceDisposition?.mode === 'retained_by_request'
|
|
532
|
+
&& handle.terminalResourceDisposition.retainWorktree === true
|
|
533
|
+
) return true;
|
|
425
534
|
const receipt = contract.completionReceipt;
|
|
426
535
|
if (!receipt || !isRepositoryCompletionReceipt(receipt)) return false;
|
|
427
536
|
return receipt.cleanup.warnings.some((warning) => warning.code === 'cleanup_retained_by_request');
|
|
@@ -588,7 +697,10 @@ export async function cleanupTerminalWork(input: TerminalWorkCleanupInput): Prom
|
|
|
588
697
|
current = persist(input.controllerHome, current, receipt);
|
|
589
698
|
return { handle: current, receipt };
|
|
590
699
|
}
|
|
591
|
-
|
|
700
|
+
const migratedManagedWorktree = !registeredPath
|
|
701
|
+
&& existsSync(current.worktreePath)
|
|
702
|
+
&& isCurrentControllerManagedWorktree(input.controllerHome, current.worktreePath);
|
|
703
|
+
if (!registeredPath && existsSync(current.worktreePath) && !migratedManagedWorktree) {
|
|
592
704
|
addBlocker(receipt, 'CHECKOUT_NOT_CONTROLLER_REGISTERED');
|
|
593
705
|
current = persist(input.controllerHome, current, receipt);
|
|
594
706
|
return { handle: current, receipt };
|
|
@@ -686,6 +798,9 @@ export async function cleanupTerminalWork(input: TerminalWorkCleanupInput): Prom
|
|
|
686
798
|
addBlocker(receipt, `CHECKOUT_REGISTRY_REMOVE_FAILED: ${receipt.checkoutRegistry.reason}`);
|
|
687
799
|
}
|
|
688
800
|
}
|
|
801
|
+
if (!registeredPath && migratedManagedWorktree) {
|
|
802
|
+
receipt.checkoutRegistry.reason = 'Checkout metadata was absent after Controller Home migration; canonical managed-worktree and Git identity checks passed before cleanup.';
|
|
803
|
+
}
|
|
689
804
|
|
|
690
805
|
if (!deleteBranch) {
|
|
691
806
|
receipt.branchCleanup.status = 'retained';
|
|
@@ -789,8 +904,24 @@ export async function reconcileTerminalWorkCleanups(
|
|
|
789
904
|
const repositories = listRepositories(controllerHome).filter((repository) => repository.enabled && !repository.removedAt);
|
|
790
905
|
outer: for (const repository of repositories) {
|
|
791
906
|
const handles = listWorkHandles(controllerHome, repository.repoId, 10_000)
|
|
792
|
-
.filter((handle) => handle.managedWorktree)
|
|
793
|
-
|
|
907
|
+
.filter((handle) => handle.managedWorktree);
|
|
908
|
+
const knownWorkIds = new Set(handles.map((handle) => handle.workId));
|
|
909
|
+
const recoveredWorkIds = new Set<string>();
|
|
910
|
+
for (const record of listControlPlaneRecords<WorkContract>(controllerHome, {
|
|
911
|
+
namespace: 'work_contract',
|
|
912
|
+
scope: repository.repoId,
|
|
913
|
+
limit: 10_000,
|
|
914
|
+
})) {
|
|
915
|
+
const contract = record.value;
|
|
916
|
+
if (knownWorkIds.has(contract.workId) || !isTerminalWorkContractStatus(contract.status)) continue;
|
|
917
|
+
if (!contract.worktreeRef?.trim() || !existsSync(contract.worktreeRef)) continue;
|
|
918
|
+
const recovered = recoverTerminalWorkHandle(controllerHome, repository.repoId, contract.workId);
|
|
919
|
+
if (!recovered) continue;
|
|
920
|
+
handles.push(recovered);
|
|
921
|
+
knownWorkIds.add(recovered.workId);
|
|
922
|
+
recoveredWorkIds.add(recovered.workId);
|
|
923
|
+
}
|
|
924
|
+
handles.sort((left, right) => left.updatedAt.localeCompare(right.updatedAt));
|
|
794
925
|
for (const originalHandle of handles) {
|
|
795
926
|
report.scanned += 1;
|
|
796
927
|
const contract = getWorkContract({ controllerHome, repoId: repository.repoId }, originalHandle.workContractId ?? originalHandle.workId);
|
|
@@ -803,7 +934,11 @@ export async function reconcileTerminalWorkCleanups(
|
|
|
803
934
|
report.skippedRetained.push(originalHandle.workId);
|
|
804
935
|
continue;
|
|
805
936
|
}
|
|
806
|
-
|
|
937
|
+
// Reconstructing a missing handle is metadata recovery, not new Work activity.
|
|
938
|
+
// Do not let that write reset the terminal-age grace period indefinitely.
|
|
939
|
+
const terminalAt = recoveredWorkIds.has(originalHandle.workId)
|
|
940
|
+
? (Date.parse(contract.updatedAt) || 0)
|
|
941
|
+
: Math.max(Date.parse(contract.updatedAt) || 0, Date.parse(originalHandle.updatedAt) || 0);
|
|
807
942
|
if (terminalAt > 0 && nowMs - terminalAt < minAgeMs) {
|
|
808
943
|
report.skippedRecent.push(originalHandle.workId);
|
|
809
944
|
continue;
|
|
@@ -166,27 +166,35 @@ export function reconcileWorkValidation(
|
|
|
166
166
|
}
|
|
167
167
|
|
|
168
168
|
try {
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
169
|
+
if (binding.checkExecution && binding.checkExecution.checkId !== checkId) {
|
|
170
|
+
throw new Error(
|
|
171
|
+
`PROCESS_CHECK_RECEIPT_IDENTITY_MISMATCH: bound check ${binding.checkExecution.checkId} != ${checkId}`,
|
|
172
|
+
);
|
|
173
|
+
}
|
|
174
|
+
const expectedCheckExecution = binding.checkExecution
|
|
175
|
+
?? (record.checkExecution
|
|
176
|
+
? controllerCheckExecutionIdentity(handle.worktreePath, checkId, record.checkExecution.timeoutMs)
|
|
177
|
+
: undefined);
|
|
172
178
|
const receipt = processCheckCompletionReceipt(record, {
|
|
173
179
|
repoId: handle.repositoryId,
|
|
174
180
|
checkoutId: handle.checkoutId,
|
|
175
181
|
workId: handle.workId,
|
|
176
182
|
checkId,
|
|
177
183
|
processId: binding.processId,
|
|
178
|
-
...(
|
|
184
|
+
...(expectedCheckExecution ? {
|
|
179
185
|
checkExecution: {
|
|
180
|
-
cacheKey:
|
|
181
|
-
revision:
|
|
182
|
-
definitionDigest:
|
|
183
|
-
environmentFingerprint:
|
|
184
|
-
timeoutMs:
|
|
185
|
-
scopeKey:
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
186
|
+
cacheKey: expectedCheckExecution.cacheKey,
|
|
187
|
+
revision: expectedCheckExecution.revision,
|
|
188
|
+
definitionDigest: expectedCheckExecution.definitionDigest,
|
|
189
|
+
environmentFingerprint: expectedCheckExecution.environmentFingerprint,
|
|
190
|
+
timeoutMs: expectedCheckExecution.timeoutMs,
|
|
191
|
+
scopeKey: binding.checkExecution
|
|
192
|
+
? binding.checkExecution.scopeKey
|
|
193
|
+
: processCheckSemanticScopeKey({
|
|
194
|
+
checkoutId: handle.checkoutId,
|
|
195
|
+
workId: handle.workId,
|
|
196
|
+
verificationBinding: { executionSessionId: handle.sessionId },
|
|
197
|
+
}, expectedCheckExecution.reuseScope),
|
|
190
198
|
},
|
|
191
199
|
} : {}),
|
|
192
200
|
});
|
|
@@ -5,7 +5,9 @@ import {
|
|
|
5
5
|
lstatSync,
|
|
6
6
|
mkdirSync,
|
|
7
7
|
mkdtempSync,
|
|
8
|
+
readFileSync,
|
|
8
9
|
readdirSync,
|
|
10
|
+
rmdirSync,
|
|
9
11
|
rmSync,
|
|
10
12
|
statSync,
|
|
11
13
|
symlinkSync,
|
|
@@ -91,11 +93,28 @@ function cloneHead(sourceRoot: string, targetRoot: string, head: string): void {
|
|
|
91
93
|
git(targetRoot, ['checkout', '--detach', '--quiet', head]);
|
|
92
94
|
}
|
|
93
95
|
|
|
96
|
+
function pruneEmptySnapshotParents(targetRoot: string, targetPath: string): void {
|
|
97
|
+
const boundary = resolve(targetRoot);
|
|
98
|
+
let current = dirname(resolve(targetPath));
|
|
99
|
+
while (current !== boundary) {
|
|
100
|
+
try {
|
|
101
|
+
if (readdirSync(current).length > 0) return;
|
|
102
|
+
rmdirSync(current);
|
|
103
|
+
} catch {
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
current = dirname(current);
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
|
|
94
110
|
function overlayPath(sourceRoot: string, targetRoot: string, relativePath: string): void {
|
|
95
111
|
const source = resolve(sourceRoot, relativePath);
|
|
96
112
|
const target = resolve(targetRoot, relativePath);
|
|
97
113
|
rmSync(target, { recursive: true, force: true });
|
|
98
|
-
if (!existsSync(source))
|
|
114
|
+
if (!existsSync(source)) {
|
|
115
|
+
pruneEmptySnapshotParents(targetRoot, target);
|
|
116
|
+
return;
|
|
117
|
+
}
|
|
99
118
|
mkdirSync(dirname(target), { recursive: true });
|
|
100
119
|
const stat = lstatSync(source);
|
|
101
120
|
if (stat.isSymbolicLink()) {
|
|
@@ -105,11 +124,69 @@ function overlayPath(sourceRoot: string, targetRoot: string, relativePath: strin
|
|
|
105
124
|
cpSync(source, target, { recursive: stat.isDirectory(), dereference: false, force: true, preserveTimestamps: true });
|
|
106
125
|
}
|
|
107
126
|
|
|
108
|
-
|
|
109
|
-
|
|
127
|
+
const DEPENDENCY_LOCK_FILES = ['bun.lock', 'bun.lockb', 'pnpm-lock.yaml', 'package-lock.json', 'yarn.lock'] as const;
|
|
128
|
+
const DEPENDENCY_CONFIG_FILES = ['pnpm-workspace.yaml', 'bunfig.toml', '.npmrc', '.yarnrc.yml'] as const;
|
|
129
|
+
const DEPENDENCY_METADATA_NAMES = ['package.json', ...DEPENDENCY_LOCK_FILES, ...DEPENDENCY_CONFIG_FILES] as const;
|
|
130
|
+
|
|
131
|
+
function isDependencyMetadataPath(path: string): boolean {
|
|
132
|
+
const name = path.split('/').pop() ?? path;
|
|
133
|
+
return DEPENDENCY_METADATA_NAMES.some((candidate) => candidate === name);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function hasUntrackedDependencyMetadata(root: string): boolean {
|
|
137
|
+
return nulPaths(git(root, ['ls-files', '--others', '--exclude-standard', '-z'])).some(isDependencyMetadataPath);
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
function dependencyMetadataDigest(root: string): string | undefined {
|
|
141
|
+
if (!existsSync(join(root, 'package.json'))) return undefined;
|
|
142
|
+
const rootLock = DEPENDENCY_LOCK_FILES.find((name) => existsSync(join(root, name)));
|
|
143
|
+
if (!rootLock) return undefined;
|
|
144
|
+
const pathspecs = DEPENDENCY_METADATA_NAMES.flatMap((name) => [name, `:(glob)**/${name}`]);
|
|
145
|
+
const tracked = nulPaths(git(root, ['ls-files', '-z', '--', ...pathspecs]));
|
|
146
|
+
const files = [...new Set(tracked)].sort();
|
|
147
|
+
if (!files.includes('package.json') || !files.includes(rootLock)) return undefined;
|
|
148
|
+
const hash = createHash('sha256');
|
|
149
|
+
for (const path of files) {
|
|
150
|
+
hash.update(path);
|
|
151
|
+
hash.update('\0');
|
|
152
|
+
hash.update(readFileSync(join(root, path)));
|
|
153
|
+
hash.update('\0');
|
|
154
|
+
}
|
|
155
|
+
return hash.digest('hex');
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
function primaryWorktreeRoot(sourceRoot: string): string | undefined {
|
|
159
|
+
const commonDirValue = git(sourceRoot, ['rev-parse', '--git-common-dir']).toString('utf8').trim();
|
|
160
|
+
if (!commonDirValue) return undefined;
|
|
161
|
+
const commonDir = resolve(sourceRoot, commonDirValue);
|
|
162
|
+
const candidate = dirname(commonDir);
|
|
163
|
+
if (resolve(candidate) === resolve(sourceRoot)) return undefined;
|
|
164
|
+
if (!existsSync(join(candidate, '.git'))) return undefined;
|
|
165
|
+
return candidate;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
function linkIgnoredNodeModules(sourceRoot: string, targetRoot: string, dirtyPaths: readonly string[]): void {
|
|
169
|
+
let source = join(sourceRoot, 'node_modules');
|
|
110
170
|
const target = join(targetRoot, 'node_modules');
|
|
111
|
-
if (
|
|
112
|
-
|
|
171
|
+
if (existsSync(target)) return;
|
|
172
|
+
if (!existsSync(source)) {
|
|
173
|
+
if (dirtyPaths.some(isDependencyMetadataPath)) return;
|
|
174
|
+
const primaryRoot = primaryWorktreeRoot(sourceRoot);
|
|
175
|
+
if (!primaryRoot) return;
|
|
176
|
+
const primaryNodeModules = join(primaryRoot, 'node_modules');
|
|
177
|
+
if (!existsSync(primaryNodeModules) || hasUntrackedDependencyMetadata(primaryRoot)) return;
|
|
178
|
+
const sourceDigest = dependencyMetadataDigest(sourceRoot);
|
|
179
|
+
const primaryDigest = dependencyMetadataDigest(primaryRoot);
|
|
180
|
+
if (!sourceDigest || sourceDigest !== primaryDigest) return;
|
|
181
|
+
source = primaryNodeModules;
|
|
182
|
+
}
|
|
183
|
+
// An existing managed-worktree node_modules may itself be a symlink. Git
|
|
184
|
+
// rejects child pathspecs through symlinks, while an absent node_modules
|
|
185
|
+
// needs a virtual child path so directory-only ignore rules still match.
|
|
186
|
+
const ignoreProbe = existsSync(join(sourceRoot, 'node_modules'))
|
|
187
|
+
? 'node_modules'
|
|
188
|
+
: 'node_modules/.forge-verification-probe';
|
|
189
|
+
const ignored = spawnSync('git', ['-C', sourceRoot, 'check-ignore', '--quiet', '--', ignoreProbe], { stdio: 'ignore', timeout: 10_000 });
|
|
113
190
|
if (ignored.status !== 0) return;
|
|
114
191
|
try {
|
|
115
192
|
symlinkSync(source, target, process.platform === 'win32' ? 'junction' : 'dir');
|
|
@@ -136,18 +213,18 @@ export function materializeWorkVerificationSnapshot(input: {
|
|
|
136
213
|
excludedPaths.push(path);
|
|
137
214
|
continue;
|
|
138
215
|
}
|
|
139
|
-
if (input.scope.allowedPaths.length
|
|
216
|
+
if (input.scope.allowedPaths.length === 0 || matchesAny(input.scope.allowedPaths, path)) {
|
|
140
217
|
includedPaths.push(path);
|
|
141
218
|
continue;
|
|
142
219
|
}
|
|
143
|
-
throw new Error(`WORK_VERIFICATION_PATH_OWNERSHIP_AMBIGUOUS: ${path} is dirty but is
|
|
220
|
+
throw new Error(`WORK_VERIFICATION_PATH_OWNERSHIP_AMBIGUOUS: ${path} is dirty but is outside the allowed path fence for ${input.scope.workId}`);
|
|
144
221
|
}
|
|
145
222
|
|
|
146
223
|
const root = snapshotRoot(input.controllerHome, input.repoId, input.scope.workId);
|
|
147
224
|
try {
|
|
148
225
|
cloneHead(input.sourceRoot, root, sourceHead);
|
|
149
226
|
for (const path of includedPaths) overlayPath(input.sourceRoot, root, path);
|
|
150
|
-
linkIgnoredNodeModules(input.sourceRoot, root);
|
|
227
|
+
linkIgnoredNodeModules(input.sourceRoot, root, dirtyPaths);
|
|
151
228
|
const ownershipDigest = createHash('sha256').update(JSON.stringify({
|
|
152
229
|
sourceHead,
|
|
153
230
|
workId: input.scope.workId,
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { AssistantPluginManifest } from '../../plugins/types';
|
|
2
|
-
import type { CapabilityDescriptor, CapabilityDomain, CapabilityGroupSummary, CapabilityOperationClass, CapabilityRisk, FacadeTool } from './types';
|
|
2
|
+
import type { CapabilityDescriptor, CapabilityDomain, CapabilityExecutionSurface, CapabilityGroupSummary, CapabilityOperationClass, CapabilityRisk, FacadeTool } from './types';
|
|
3
3
|
|
|
4
4
|
const CORE_CAPABILITIES: CapabilityDescriptor[] = [
|
|
5
5
|
{
|
|
@@ -83,24 +83,14 @@ const CORE_CAPABILITIES: CapabilityDescriptor[] = [
|
|
|
83
83
|
summary: 'Diagnose and dry-run repair stuck jobs, stale projections, invalid check pollution, and worker unavailability without treating infrastructure failure as acceptance failure.',
|
|
84
84
|
},
|
|
85
85
|
{
|
|
86
|
-
capabilityId: 'controller.
|
|
86
|
+
capabilityId: 'controller.external_controller',
|
|
87
87
|
domain: 'controller',
|
|
88
88
|
group: 'controller',
|
|
89
89
|
operationClass: 'execute',
|
|
90
90
|
risk: 'workspace_write',
|
|
91
91
|
exposedVia: 'rh_work',
|
|
92
92
|
schemaExposure: 'stable_static',
|
|
93
|
-
summary: '
|
|
94
|
-
},
|
|
95
|
-
{
|
|
96
|
-
capabilityId: 'controller.autonomous_goal_loop',
|
|
97
|
-
domain: 'controller',
|
|
98
|
-
group: 'controller',
|
|
99
|
-
operationClass: 'execute',
|
|
100
|
-
risk: 'workspace_write',
|
|
101
|
-
exposedVia: 'rh_work',
|
|
102
|
-
schemaExposure: 'stable_static',
|
|
103
|
-
summary: 'Advance daemon-owned goal contracts through deterministic policy, executor routing, verification, repair, and handoff.',
|
|
93
|
+
summary: 'Claim Work ownership and start an external Codex, Claude, or ChatGPT controller through controller_claim and launcher_start, with handoff decisions coordinated through rh_inbox; deprecated delegate is not an execution path.',
|
|
104
94
|
},
|
|
105
95
|
{
|
|
106
96
|
capabilityId: 'controller.work_contract',
|
|
@@ -148,9 +138,9 @@ const CORE_CAPABILITIES: CapabilityDescriptor[] = [
|
|
|
148
138
|
group: 'browser',
|
|
149
139
|
operationClass: 'execute',
|
|
150
140
|
risk: 'unknown',
|
|
151
|
-
exposedVia: '
|
|
141
|
+
exposedVia: 'plugin_action_execute',
|
|
152
142
|
schemaExposure: 'stable_static',
|
|
153
|
-
summary: 'Use typed HTTP(S) browser navigation, snapshot, and interaction actions when browser capability is configured.',
|
|
143
|
+
summary: 'Use typed HTTP(S) browser navigation, snapshot, and interaction actions through plugin_action_execute when browser capability is configured.',
|
|
154
144
|
},
|
|
155
145
|
{
|
|
156
146
|
capabilityId: 'platform.ios',
|
|
@@ -158,9 +148,9 @@ const CORE_CAPABILITIES: CapabilityDescriptor[] = [
|
|
|
158
148
|
group: 'ios',
|
|
159
149
|
operationClass: 'execute',
|
|
160
150
|
risk: 'workspace_write',
|
|
161
|
-
exposedVia: '
|
|
151
|
+
exposedVia: 'plugin_action_execute',
|
|
162
152
|
schemaExposure: 'stable_static',
|
|
163
|
-
summary: 'Use typed Xcode, simulator
|
|
153
|
+
summary: 'Use typed Xcode, simulator, screenshot, log, and smoke-review actions through plugin_action_execute when the local iOS toolchain is ready.',
|
|
164
154
|
},
|
|
165
155
|
];
|
|
166
156
|
|
|
@@ -185,13 +175,16 @@ function domainFromPlugin(pluginId: string): CapabilityDomain {
|
|
|
185
175
|
return 'plugin';
|
|
186
176
|
}
|
|
187
177
|
|
|
188
|
-
function exposedViaFromPluginAction(
|
|
189
|
-
|
|
190
|
-
return '
|
|
178
|
+
function exposedViaFromPluginAction(_readOnly: boolean, _risk: string): CapabilityExecutionSurface {
|
|
179
|
+
// rh_context exposes schema/policy. plugin_action_execute is the sole action executor.
|
|
180
|
+
return 'plugin_action_execute';
|
|
191
181
|
}
|
|
192
182
|
|
|
193
|
-
|
|
194
|
-
|
|
183
|
+
function pluginCapabilityDescriptor(
|
|
184
|
+
manifest: AssistantPluginManifest,
|
|
185
|
+
action: AssistantPluginManifest['actions'][number],
|
|
186
|
+
): CapabilityDescriptor {
|
|
187
|
+
return {
|
|
195
188
|
capabilityId: `plugin.${manifest.pluginId}.${action.actionId}`,
|
|
196
189
|
domain: domainFromPlugin(manifest.pluginId),
|
|
197
190
|
group: manifest.pluginId === 'browser' ? 'browser' : 'plugin',
|
|
@@ -200,7 +193,11 @@ export function pluginCapabilities(manifests: readonly AssistantPluginManifest[]
|
|
|
200
193
|
exposedVia: exposedViaFromPluginAction(action.readOnly, action.risk),
|
|
201
194
|
schemaExposure: 'plugin_manifest',
|
|
202
195
|
summary: `${manifest.displayName}: ${action.title}. ${action.description}`,
|
|
203
|
-
} satisfies CapabilityDescriptor
|
|
196
|
+
} satisfies CapabilityDescriptor;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
export function pluginCapabilities(manifests: readonly AssistantPluginManifest[] = []): CapabilityDescriptor[] {
|
|
200
|
+
return manifests.flatMap((manifest) => manifest.actions.map((action) => pluginCapabilityDescriptor(manifest, action)));
|
|
204
201
|
}
|
|
205
202
|
|
|
206
203
|
export function listCapabilityDescriptors(manifests: readonly AssistantPluginManifest[] = []): CapabilityDescriptor[] {
|
|
@@ -209,6 +206,97 @@ export function listCapabilityDescriptors(manifests: readonly AssistantPluginMan
|
|
|
209
206
|
return [...byId.values()].sort((a, b) => a.capabilityId.localeCompare(b.capabilityId));
|
|
210
207
|
}
|
|
211
208
|
|
|
209
|
+
export interface CapabilitySearchMatch {
|
|
210
|
+
capabilityId: string;
|
|
211
|
+
score: number;
|
|
212
|
+
matchedTerms: string[];
|
|
213
|
+
descriptor: CapabilityDescriptor;
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
function normalizedCapabilityText(value: string): string {
|
|
217
|
+
return value
|
|
218
|
+
.toLowerCase()
|
|
219
|
+
.replace(/[_./:-]+/g, ' ')
|
|
220
|
+
.replace(/[^a-z0-9]+/g, ' ')
|
|
221
|
+
.trim();
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function capabilityIntentTokens(query: string): string[] {
|
|
225
|
+
return [...new Set(normalizedCapabilityText(query).split(' ').filter((token) => token.length >= 2))];
|
|
226
|
+
}
|
|
227
|
+
|
|
228
|
+
function intentBoosts(query: string, capabilityId: string): Array<{ label: string; score: number }> {
|
|
229
|
+
const normalized = normalizedCapabilityText(query);
|
|
230
|
+
const boosts: Array<{ label: string; score: number }> = [];
|
|
231
|
+
const appleDevelopmentIntent = /\b(xcode|provision|provisioning|signing|developer account|apple developer)\b/.test(normalized);
|
|
232
|
+
if (appleDevelopmentIntent) {
|
|
233
|
+
if (capabilityId === 'platform.ios') boosts.push({ label: 'apple-development', score: 90 });
|
|
234
|
+
if (capabilityId.startsWith('plugin.ios.')) boosts.push({ label: 'apple-development', score: 72 });
|
|
235
|
+
if (capabilityId === 'plugin.app_store_connect.auth_status') boosts.push({ label: 'apple-development', score: 88 });
|
|
236
|
+
else if (capabilityId === 'plugin.app_store_connect.configure') boosts.push({ label: 'apple-development', score: 82 });
|
|
237
|
+
else if (capabilityId.startsWith('plugin.app_store_connect.')) boosts.push({ label: 'apple-development', score: 28 });
|
|
238
|
+
if (capabilityId === 'plugin.desktop_operator.desktop_session_open') boosts.push({ label: 'xcode-desktop-fallback', score: 86 });
|
|
239
|
+
else if (capabilityId === 'plugin.desktop_operator.desktop_observe') boosts.push({ label: 'xcode-desktop-fallback', score: 78 });
|
|
240
|
+
else if (capabilityId === 'plugin.desktop_operator.desktop_press') boosts.push({ label: 'xcode-desktop-fallback', score: 74 });
|
|
241
|
+
else if (capabilityId.startsWith('plugin.desktop_operator.')) boosts.push({ label: 'xcode-desktop-fallback', score: 22 });
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
const browserLoginIntent = normalized.includes('browser')
|
|
245
|
+
&& /\b(login|log in|signin|sign in|auth|authentication|session)\b/.test(normalized);
|
|
246
|
+
if (browserLoginIntent) {
|
|
247
|
+
if (capabilityId === 'plugin.browser') boosts.push({ label: 'browser-auth', score: 90 });
|
|
248
|
+
else if (capabilityId.startsWith('plugin.browser.')) boosts.push({ label: 'browser-auth', score: 62 });
|
|
249
|
+
if (capabilityId === 'plugin.desktop_operator.desktop_session_open') boosts.push({ label: 'browser-desktop-fallback', score: 70 });
|
|
250
|
+
else if (capabilityId === 'plugin.desktop_operator.desktop_observe') boosts.push({ label: 'browser-desktop-fallback', score: 62 });
|
|
251
|
+
else if (capabilityId.startsWith('plugin.desktop_operator.')) boosts.push({ label: 'browser-desktop-fallback', score: 18 });
|
|
252
|
+
}
|
|
253
|
+
return boosts;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
export function searchCapabilityDescriptors(
|
|
257
|
+
query: string,
|
|
258
|
+
manifests: readonly AssistantPluginManifest[] = [],
|
|
259
|
+
limit = 12,
|
|
260
|
+
): CapabilitySearchMatch[] {
|
|
261
|
+
const normalizedQuery = normalizedCapabilityText(query);
|
|
262
|
+
if (!normalizedQuery) return [];
|
|
263
|
+
const tokens = capabilityIntentTokens(query);
|
|
264
|
+
const boundedLimit = Math.max(1, Math.min(24, Math.floor(limit)));
|
|
265
|
+
|
|
266
|
+
return listCapabilityDescriptors(manifests)
|
|
267
|
+
.map((descriptor): CapabilitySearchMatch | undefined => {
|
|
268
|
+
const haystack = normalizedCapabilityText([
|
|
269
|
+
descriptor.capabilityId,
|
|
270
|
+
descriptor.summary,
|
|
271
|
+
descriptor.group,
|
|
272
|
+
descriptor.domain,
|
|
273
|
+
descriptor.operationClass,
|
|
274
|
+
descriptor.exposedVia,
|
|
275
|
+
].join(' '));
|
|
276
|
+
let score = haystack.includes(normalizedQuery) ? 48 : 0;
|
|
277
|
+
const matchedTerms = new Set<string>();
|
|
278
|
+
for (const token of tokens) {
|
|
279
|
+
if (!haystack.includes(token)) continue;
|
|
280
|
+
matchedTerms.add(token);
|
|
281
|
+
score += descriptor.capabilityId.toLowerCase().includes(token) ? 12 : 5;
|
|
282
|
+
}
|
|
283
|
+
for (const boost of intentBoosts(query, descriptor.capabilityId)) {
|
|
284
|
+
score += boost.score;
|
|
285
|
+
matchedTerms.add(boost.label);
|
|
286
|
+
}
|
|
287
|
+
if (score <= 0) return undefined;
|
|
288
|
+
return {
|
|
289
|
+
capabilityId: descriptor.capabilityId,
|
|
290
|
+
score,
|
|
291
|
+
matchedTerms: [...matchedTerms].sort(),
|
|
292
|
+
descriptor,
|
|
293
|
+
};
|
|
294
|
+
})
|
|
295
|
+
.filter((entry): entry is CapabilitySearchMatch => Boolean(entry))
|
|
296
|
+
.sort((a, b) => b.score - a.score || a.capabilityId.localeCompare(b.capabilityId))
|
|
297
|
+
.slice(0, boundedLimit);
|
|
298
|
+
}
|
|
299
|
+
|
|
212
300
|
export function summarizeCapabilityGroups(manifests: readonly AssistantPluginManifest[] = []): CapabilityGroupSummary[] {
|
|
213
301
|
const grouped = new Map<CapabilityDescriptor['group'], CapabilityDescriptor[]>();
|
|
214
302
|
for (const descriptor of listCapabilityDescriptors(manifests)) {
|
|
@@ -222,7 +310,8 @@ export function summarizeCapabilityGroups(manifests: readonly AssistantPluginMan
|
|
|
222
310
|
group,
|
|
223
311
|
capabilityCount: entries.length,
|
|
224
312
|
domains: [...new Set(entries.map((entry) => entry.domain))].sort(),
|
|
225
|
-
|
|
313
|
+
executionSurfaces: [...new Set(entries.map((entry) => entry.exposedVia))].sort(),
|
|
314
|
+
facadeTools: [...new Set(entries.map((entry) => entry.exposedVia).filter((surface): surface is FacadeTool => surface.startsWith('rh_')))].sort(),
|
|
226
315
|
operationClasses: [...new Set(entries.map((entry) => entry.operationClass))].sort(),
|
|
227
316
|
risks: [...new Set(entries.map((entry) => entry.risk))].sort(),
|
|
228
317
|
schemaExposures: [...new Set(entries.map((entry) => entry.schemaExposure))].sort(),
|
|
@@ -257,6 +346,12 @@ export function getPluginActionCapabilitySchema(
|
|
|
257
346
|
scopes: [...action.scopes],
|
|
258
347
|
resourceClaims: structuredClone(action.resourceClaims),
|
|
259
348
|
argumentsSchema: structuredClone(action.argumentsSchema),
|
|
349
|
+
authorizationReuse: action.confirmation === 'authorization'
|
|
350
|
+
? {
|
|
351
|
+
mode: 'exact_target_persistent_when_adapter_supported',
|
|
352
|
+
hostPermissionBoundary: 'Tool invocation permission remains host-managed; reusable Forge grants never bypass strong_confirmation or destructive gates.',
|
|
353
|
+
}
|
|
354
|
+
: { mode: 'not_reusable' },
|
|
260
355
|
executeWith: 'plugin_action_execute',
|
|
261
356
|
};
|
|
262
357
|
}
|
|
@@ -265,5 +360,14 @@ export function getPluginActionCapabilitySchema(
|
|
|
265
360
|
}
|
|
266
361
|
|
|
267
362
|
export function getCapabilityDescriptor(capabilityId: string, manifests: readonly AssistantPluginManifest[] = []): CapabilityDescriptor | undefined {
|
|
268
|
-
|
|
363
|
+
const core = CORE_CAPABILITIES.find((descriptor) => descriptor.capabilityId === capabilityId);
|
|
364
|
+
if (core) return core;
|
|
365
|
+
for (const manifest of manifests) {
|
|
366
|
+
for (const action of manifest.actions) {
|
|
367
|
+
if (`plugin.${manifest.pluginId}.${action.actionId}` === capabilityId) {
|
|
368
|
+
return pluginCapabilityDescriptor(manifest, action);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
return undefined;
|
|
269
373
|
}
|