@moretea-labs/forge 1.5.1 → 1.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +2 -0
- package/CHANGELOG.md +46 -0
- package/CLAUDE.md +2 -0
- package/DESIGN.md +7 -2
- package/README.md +4 -4
- package/README.zh-CN.md +2 -2
- package/SKILL.md +2 -2
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/agents/openai.yaml +1 -1
- package/assets/hooks/hook-input.sh +11 -8
- package/assets/hooks/lib/session-state.sh +10 -7
- package/assets/hooks/lib/workflow-state.sh +13 -177
- package/assets/hooks/post-tool-observer.sh +8 -2
- package/assets/hooks/pre-edit-guard.sh +8 -39
- package/assets/hooks/prompt-guard.sh +7 -83
- package/assets/hooks/session-start-context.sh +56 -1
- package/assets/partials/05-workflow.partial.md +1 -1
- package/assets/partials-agents/03-orchestration.partial.md +1 -1
- package/assets/plugin-registry.v1.json +54 -3
- package/assets/reference-configs/document-generation.md +1 -1
- package/assets/reference-configs/harness-overview.md +1 -1
- package/assets/reference-configs/hook-operations.md +1 -1
- package/assets/reference-configs/sprint-contracts.md +3 -3
- package/assets/skill-version.json +2 -2
- package/assets/templates/contract.template.md +2 -7
- package/assets/templates/helpers/capture-plan.sh +2 -2
- package/assets/templates/helpers/check-task-workflow.sh +4 -4
- package/assets/templates/helpers/contract-worktree.sh +1 -21
- package/assets/templates/helpers/ensure-task-workflow.sh +13 -38
- package/assets/templates/helpers/migrate-project-template.sh +22 -45
- package/assets/templates/helpers/new-plan.sh +1 -1
- package/assets/templates/helpers/plan-to-todo.sh +12 -37
- package/assets/templates/helpers/ship-worktrees.sh +2 -29
- package/assets/templates/helpers/sprint-backlog.sh +6 -6
- package/assets/templates/helpers/verify-contract.sh +1 -111
- package/assets/templates/helpers/verify-sprint.sh +3 -180
- package/assets/templates/plan.template.md +1 -1
- package/assets/templates/review.template.md +9 -34
- package/assets/workflow-contract.v1.json +10 -19
- package/docs/README.md +10 -10
- package/docs/architecture/CURRENT.md +170 -0
- package/docs/architecture/EVOLUTION.md +42 -0
- package/docs/architecture/current/README.md +4 -156
- package/docs/architecture/decisions/20260803-release-identity-binding-and-exit-policy.md +2 -2
- package/docs/architecture/decisions/20260823-immediate-controller-relay.md +53 -0
- package/docs/architecture/decisions/20260826-browser-runtime-v2.md +73 -0
- package/docs/architecture/history/README.md +7 -0
- package/docs/architecture/{chatgpt-handoff-facade.md → history/chatgpt-handoff-facade.md} +4 -0
- package/docs/architecture/{global-hook-runtime.md → history/global-hook-runtime.md} +7 -3
- package/docs/architecture/{ios-semantic-automation-provider-v2.md → history/ios-semantic-automation-provider-v2.md} +6 -2
- package/docs/architecture/history.md +3 -34
- package/docs/architecture/index.md +15 -98
- package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +1 -1
- package/docs/architecture/reviews/2026-08-22-semantic-harness-convergence-acceptance.md +39 -0
- package/docs/architecture/versions/1.6.md +38 -0
- package/docs/architecture/versions/1.7.md +41 -0
- package/docs/forge-chatgpt-browser-engine.md +2 -2
- package/docs/forge-chatgpt-mcp-setup.md +4 -28
- package/docs/forge-chatgpt-supervised-automation.md +3 -3
- package/docs/forge-github-issue-launcher.md +1 -1
- package/docs/forge-runtime-self-healing-loop.md +1 -1
- package/docs/operations/controller-browser-plugin.md +38 -22
- package/docs/operations/controller-performance-and-502.md +124 -0
- package/docs/operations/controller-reliability-runbook.md +1 -1
- package/docs/operations/features.md +3 -3
- package/docs/operations/github-repository.md +1 -1
- package/docs/operations/homebrew.md +2 -2
- package/docs/operations/mcp-tool-exposure.md +5 -3
- package/docs/tutorials/01-install-and-start.md +1 -1
- package/docs/tutorials/01-install-and-start.zh-CN.md +1 -1
- package/docs/wiki/Architecture.md +6 -6
- package/docs/wiki/Implementation.md +2 -2
- package/docs/wiki/Installation.md +1 -1
- package/docs/wiki/Integrations.md +2 -2
- package/docs/wiki/Quick-Start.md +1 -1
- package/docs/wiki/Work-Lifecycle.md +1 -1
- package/package.json +29 -10
- package/scripts/activate-source-baseline.ts +17 -13
- package/scripts/benchmark-background-check-overlap.ts +228 -0
- package/scripts/benchmark-mcp-host.sh +117 -0
- package/scripts/benchmark-mcp-transport.ts +170 -0
- package/scripts/benchmark-quality-harness.ts +146 -0
- package/scripts/benchmark-route-session-concurrency.ts +207 -6
- package/scripts/benchmark-thin-harness-mcp-e2e.ts +7 -1
- package/scripts/benchmark-typescript-navigation.ts +186 -0
- package/scripts/benchmark-work-efficiency.ts +334 -0
- package/scripts/capture-plan.sh +3 -3
- package/scripts/check-agent-tooling.sh +8 -35
- package/scripts/check-architecture-sync.sh +17 -86
- package/scripts/check-mcp-compatibility.ts +14 -0
- package/scripts/check-public-docs.sh +14 -1
- package/scripts/check-runtime-architecture.mjs +107 -64
- package/scripts/check-stable-baseline.ts +92 -0
- package/scripts/check-task-workflow.sh +4 -4
- package/scripts/contract-worktree.sh +1 -21
- package/scripts/ensure-task-workflow.sh +22 -55
- package/scripts/host-temporary-cloud-mcp-e2e.sh +265 -0
- package/scripts/install-standalone-recovery.ts +24 -0
- package/scripts/lib/project-init-lib.sh +15 -44
- package/scripts/migrate-chatgpt-automation-schedules.ts +64 -0
- package/scripts/migrate-project-template.sh +23 -1179
- package/scripts/new-plan.sh +1 -1
- package/scripts/plan-to-todo.sh +11 -36
- package/scripts/run-governed-gate.ts +2 -1
- package/scripts/ship-worktrees.sh +2 -29
- package/scripts/smoke-ios-physical-device.ts +4 -2
- package/scripts/smoke-runtime-recovery.ts +1 -35
- package/scripts/smoke-schedule-engine.ts +1 -20
- package/scripts/sprint-backlog.sh +1 -1
- package/scripts/verify-contract.sh +1 -111
- package/scripts/verify-forge-runtime.sh +0 -1
- package/scripts/verify-sprint.sh +3 -180
- package/scripts/workflow-contract.ts +1 -5
- package/src/cli/commands/chatgpt.ts +31 -2
- package/src/cli/commands/init-hook.ts +3 -3
- package/src/cli/commands/mcp.ts +9 -5
- package/src/cli/commands/plugin.ts +37 -10
- package/src/cli/commands/recovery.ts +25 -3
- package/src/cli/commands/runtime.ts +55 -3
- package/src/cli/commands/security.ts +12 -1
- package/src/cli/commands/setup-profile.ts +31 -21
- package/src/cli/controller/check-runner.ts +106 -3
- package/src/cli/controller/composite-result.ts +1 -1
- package/src/cli/controller/context/focus.ts +51 -0
- package/src/cli/controller/context/instruction-context.ts +114 -0
- package/src/cli/controller/context/known-paths.ts +76 -0
- package/src/cli/controller/context/query-planning.ts +110 -0
- package/src/cli/controller/context/rule-resolution.ts +532 -0
- package/src/cli/controller/context/source-materializer.ts +278 -0
- package/src/cli/controller/context/structural.ts +16 -0
- package/src/cli/controller/context/types.ts +286 -0
- package/src/cli/controller/context-pack.ts +898 -430
- package/src/cli/controller/launch-agents.ts +77 -2
- package/src/cli/controller/operational-plan.ts +2 -2
- package/src/cli/controller/work-mode.ts +6 -4
- package/src/cli/editing/edit-session.ts +60 -7
- package/src/cli/github/github.ts +133 -0
- package/src/cli/hook/prompt-guard-decision.ts +17 -18
- package/src/cli/hook/prompt-intents.ts +1 -1
- package/src/cli/installer/managed-entries.ts +15 -1
- package/src/cli/local-bridge/console-automations.ts +225 -67
- package/src/cli/local-bridge/console-view-models.ts +3 -35
- package/src/cli/local-bridge/facade-api.ts +2 -217
- package/src/cli/local-bridge/frontend/src/App.tsx +3 -3
- package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +153 -3
- package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +37 -4
- package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +183 -3
- package/src/cli/local-bridge/frontend/src/features/repositories/RepositoriesView.tsx +1 -1
- package/src/cli/local-bridge/frontend/src/features/work/WorkView.tsx +4 -3
- package/src/cli/local-bridge/frontend/src/lib/api.ts +18 -11
- package/src/cli/local-bridge/frontend/src/lib/routes.ts +1 -1
- package/src/cli/local-bridge/frontend/src/shell/CommandBar.tsx +1 -1
- package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +4 -3
- package/src/cli/local-bridge/frontend/src/styles.css +27 -11
- package/src/cli/local-bridge/frontend/src/types.ts +8 -7
- package/src/cli/local-bridge/job-store.ts +1 -490
- package/src/cli/local-bridge/server.ts +29 -532
- package/src/cli/local-bridge/ui-dist/app.css +1 -1
- package/src/cli/local-bridge/ui-dist/app.js +5101 -4967
- package/src/cli/mcp/auth.ts +5 -5
- package/src/cli/mcp/instructions.ts +3 -2
- package/src/cli/mcp/legacy-tool-service.ts +0 -189
- package/src/cli/mcp/multi-repository.ts +16 -5
- package/src/cli/mcp/oauth.ts +175 -26
- package/src/cli/mcp/paths.ts +8 -2
- package/src/cli/mcp/repository-tools.ts +356 -101
- package/src/cli/mcp/server.ts +625 -54
- package/src/cli/mcp/tool-capability-cache.ts +1 -1
- package/src/cli/mcp/toolset-selection.ts +1 -1
- package/src/cli/mcp/toolset.ts +42 -5
- package/src/cli/mcp/transports/http.ts +125 -15
- package/src/cli/mcp/transports/session-registry.ts +67 -5
- package/src/cli/mcp/types.ts +3 -3
- package/src/cli/repositories/command-classifier.ts +209 -14
- package/src/cli/repositories/command-executor.ts +55 -521
- package/src/cli/repositories/command-process.ts +108 -0
- package/src/cli/repositories/command-scope.ts +99 -41
- package/src/cli/repositories/controller-home.ts +7 -8
- package/src/cli/repositories/git-command-executor.ts +2 -0
- package/src/cli/repositories/locks.ts +53 -16
- package/src/cli/repositories/repository-snapshot.ts +121 -0
- package/src/cli/repositories/safe-patch.ts +6 -4
- package/src/cli/repositories/selected-path-actions.ts +17 -9
- package/src/cli/repositories/structured-git.ts +110 -10
- package/src/cli/repository/inspector.ts +565 -62
- package/src/cli/repository/session-cache.ts +58 -17
- package/src/cli/v81-entry.ts +1 -4
- package/src/effects/fs-transaction.ts +85 -17
- package/src/runtime/context/codegraph-read-provider.ts +233 -2
- package/src/runtime/context/codegraph-sidecar.cjs +74 -40
- package/src/runtime/context/generic-lsp-provider.ts +355 -0
- package/src/runtime/context/lsp-client.ts +283 -0
- package/src/runtime/context/semantic-navigation.ts +275 -0
- package/src/runtime/context/swift-navigation.ts +565 -0
- package/src/runtime/context/typescript-navigation.ts +226 -0
- package/src/runtime/control-plane/dispatch-priority.ts +28 -0
- package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +196 -2
- package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +86 -24
- package/src/runtime/control-plane/execution/execution-identity.ts +65 -14
- package/src/runtime/control-plane/execution/session-store.ts +18 -0
- package/src/runtime/control-plane/execution/validation.ts +15 -2
- package/src/runtime/control-plane/execution/verification-evidence.ts +58 -3
- package/src/runtime/control-plane/execution/work-handle-store.ts +27 -2
- package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +258 -0
- package/src/runtime/control-plane/execution/work-remote-delivery.ts +215 -0
- package/src/runtime/control-plane/execution/work-task-receipt.ts +54 -11
- package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +175 -8
- package/src/runtime/control-plane/execution/work-validation-reconciler.ts +28 -21
- package/src/runtime/control-plane/execution/work-verification-snapshot.ts +87 -9
- package/src/runtime/control-plane/facade/capability-registry.ts +129 -25
- package/src/runtime/control-plane/facade/controller-round-relay.ts +991 -0
- package/src/runtime/control-plane/facade/controller-session-store.ts +144 -14
- package/src/runtime/control-plane/facade/goal-workloop-access.ts +9 -2
- package/src/runtime/control-plane/facade/goal-workloop.ts +885 -105
- package/src/runtime/control-plane/facade/index.ts +1 -0
- package/src/runtime/control-plane/facade/plan-contract-store.ts +211 -26
- package/src/runtime/control-plane/facade/requirement-board.ts +24 -8
- package/src/runtime/control-plane/facade/semantic-admission.ts +205 -0
- package/src/runtime/control-plane/facade/suggested-actions.ts +5 -1
- package/src/runtime/control-plane/facade/types.ts +55 -11
- package/src/runtime/control-plane/facade/work-contract-store.ts +204 -73
- package/src/runtime/control-plane/generated-cache-retention.ts +271 -0
- package/src/runtime/control-plane/global-scheduler/config.ts +44 -0
- package/src/runtime/control-plane/global-scheduler/dispatch-capacity.ts +80 -0
- package/src/runtime/control-plane/global-scheduler/durable-admission.ts +46 -0
- package/src/runtime/control-plane/global-scheduler/maintenance.ts +145 -0
- package/src/runtime/control-plane/global-scheduler/projection-refresh.ts +84 -0
- package/src/runtime/control-plane/global-scheduler/reconciliation.ts +3 -3
- package/src/runtime/control-plane/global-scheduler/scheduler.ts +273 -542
- package/src/runtime/control-plane/global-scheduler/source-scan.ts +59 -0
- package/src/runtime/control-plane/global-scheduler/state.ts +88 -0
- package/src/runtime/control-plane/global-scheduler/worker-attachment.ts +57 -0
- package/src/runtime/control-plane/global-scheduler/worker-exit-decision.ts +38 -0
- package/src/runtime/control-plane/global-scheduler/worker-exit-reconciler.ts +175 -0
- package/src/runtime/control-plane/global-scheduler/worker-launch.ts +111 -0
- package/src/runtime/control-plane/global-scheduler/worker-lifecycle-store.ts +64 -0
- package/src/runtime/control-plane/global-scheduler/worker-lifecycle.ts +150 -0
- package/src/runtime/control-plane/global-scheduler/worker-process.ts +119 -0
- package/src/runtime/control-plane/global-scheduler/worker-stderr.ts +60 -0
- package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +50 -0
- package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +642 -51
- package/src/runtime/control-plane/launcher/launch-reservation-store.ts +40 -0
- package/src/runtime/control-plane/launcher/thin-launcher.ts +194 -10
- package/src/runtime/control-plane/persistence/controller-home-migration.ts +487 -0
- package/src/runtime/control-plane/persistence/requirement-store.ts +15 -11
- package/src/runtime/control-plane/persistence/sqlite-store.ts +33 -2
- package/src/runtime/control-plane/release-retention.ts +62 -2
- package/src/runtime/control-plane/routing/route-policy.ts +7 -16
- package/src/runtime/control-plane/runtime-status-client.ts +0 -2
- package/src/runtime/diagnostics/mcp-timing.ts +19 -0
- package/src/runtime/diagnostics/mcp-transport-benchmark.ts +129 -0
- package/src/runtime/diagnostics/performance.ts +24 -1
- package/src/runtime/evidence/event-ledger.ts +1 -1
- package/src/runtime/execution/jobs/store.ts +58 -4
- package/src/runtime/execution/jobs/types.ts +1 -1
- package/src/runtime/execution/managed-workspace.ts +203 -1
- package/src/runtime/execution/process-runtime/check-facade.ts +52 -14
- package/src/runtime/execution/process-runtime/check-result.ts +63 -2
- package/src/runtime/execution/process-runtime/check-scheduling.ts +114 -0
- package/src/runtime/execution/process-runtime/command-facade.ts +346 -40
- package/src/runtime/execution/process-runtime/gc.ts +98 -12
- package/src/runtime/execution/process-runtime/lightweight-managed.ts +1184 -0
- package/src/runtime/execution/process-runtime/persisted-check.ts +57 -6
- package/src/runtime/execution/process-runtime/resource-claims.ts +97 -8
- package/src/runtime/execution/process-runtime/runtime.ts +59 -4
- package/src/runtime/execution/process-runtime/store.ts +7 -0
- package/src/runtime/execution/process-runtime/types.ts +2 -0
- package/src/runtime/execution/thin-harness/async-process.ts +12 -2
- package/src/runtime/execution/thin-harness/search-worker.ts +54 -5
- package/src/runtime/execution/workers/executor.ts +0 -48
- package/src/runtime/gateway/mcp/execution-tools.ts +1219 -126
- package/src/runtime/gateway/mcp/process-tools.ts +83 -37
- package/src/runtime/gateway/mcp/router.ts +185 -83
- package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +546 -0
- package/src/runtime/gateway/mcp/runtime-tools.ts +2555 -1616
- package/src/runtime/model-clients/deepseek-adapter.ts +0 -32
- package/src/runtime/plugins/app-store-connect-adapter.ts +123 -19
- package/src/runtime/plugins/browser-adapter.ts +2925 -318
- package/src/runtime/plugins/browser-automation-service.ts +10 -0
- package/src/runtime/plugins/browser-handoff-host.ts +6 -31
- package/src/runtime/plugins/browser-handoff.ts +92 -5
- package/src/runtime/plugins/browser-macos-bridge.ts +586 -45
- package/src/runtime/plugins/browser-node-bridge.ts +6 -34
- package/src/runtime/plugins/browser-provider-registry.ts +109 -0
- package/src/runtime/plugins/browser-runtime-contract.ts +152 -0
- package/src/runtime/plugins/browser-runtime.ts +183 -0
- package/src/runtime/plugins/browser-session-authority.ts +332 -0
- package/src/runtime/plugins/capability-authorization-grants.ts +354 -0
- package/src/runtime/plugins/desktop-operator-registration.ts +15 -12
- package/src/runtime/plugins/external-adapter.ts +324 -3
- package/src/runtime/plugins/external-registration.ts +84 -5
- package/src/runtime/plugins/external-unix-socket.ts +7 -1
- package/src/runtime/plugins/first-party-registry.ts +4 -2
- package/src/runtime/plugins/github-adapter.ts +125 -0
- package/src/runtime/plugins/gmail-adapter.ts +28 -2
- package/src/runtime/plugins/ios/agent-device-provider.ts +1 -1
- package/src/runtime/plugins/ios/agent-device-typed-provider.ts +52 -9
- package/src/runtime/plugins/ios/remote-xpc-hid.ts +1171 -0
- package/src/runtime/plugins/ios-adapter.ts +106 -16
- package/src/runtime/plugins/ios-agent-device.ts +234 -64
- package/src/runtime/plugins/ios-physical-device.ts +1143 -392
- package/src/runtime/plugins/lightweight-action.ts +153 -0
- package/src/runtime/plugins/local-system-adapter.ts +125 -8
- package/src/runtime/plugins/macos-capability-broker.ts +55 -9
- package/src/runtime/plugins/plugin-action-sidecar.ts +59 -0
- package/src/runtime/plugins/plugin-management-adapter.ts +181 -0
- package/src/runtime/plugins/resend-adapter.ts +1 -1
- package/src/runtime/plugins/store.ts +366 -24
- package/src/runtime/plugins/types.ts +39 -2
- package/src/runtime/plugins/xiaohongshu-publish.ts +515 -0
- package/src/runtime/projections/git-status-sampler.ts +16 -16
- package/src/runtime/projections/materialized-view.ts +28 -10
- package/src/runtime/recovery/actions.ts +0 -10
- package/src/runtime/recovery/classifier.ts +0 -9
- package/src/runtime/recovery/maintenance-executor.ts +415 -18
- package/src/runtime/recovery/probe.ts +0 -1
- package/src/runtime/recovery/types.ts +0 -1
- package/src/runtime/root/mcp-transport.ts +180 -10
- package/src/runtime/root/package-connector-service.ts +186 -8
- package/src/runtime/root/package-runtime-release.ts +143 -17
- package/src/runtime/root/package-runtime-service.ts +430 -22
- package/src/runtime/root/release-manifest.ts +7 -2
- package/src/runtime/root/release-materialize.ts +162 -19
- package/src/runtime/root/release-store.ts +48 -11
- package/src/runtime/root/runtime.ts +23 -5
- package/src/runtime/root/service.ts +100 -27
- package/src/runtime/root/types.ts +2 -0
- package/src/runtime/safe-tooling/index.ts +0 -1
- package/src/runtime/safe-tooling/ios-development.ts +321 -63
- package/src/runtime/safe-tooling/types.ts +0 -30
- package/src/runtime/shared/trusted-executable.ts +62 -0
- package/src/runtime/shared/trusted-node-executable.ts +48 -0
- package/src/runtime/standalone-recovery/core.ts +702 -94
- package/src/runtime/standalone-recovery/entry.ts +99 -23
- package/src/runtime/standalone-recovery/installer.ts +19 -3
- package/src/runtime/standalone-recovery/service-labels.ts +2 -0
- package/src/runtime/standalone-recovery/watchdog-heartbeat.ts +160 -0
- package/src/runtime/workflow/schedules/browser-probe.ts +258 -0
- package/src/runtime/workflow/schedules/chatgpt-automation-migration.ts +40 -0
- package/src/runtime/workflow/schedules/engine.ts +509 -80
- package/src/runtime/workflow/schedules/github-issue-watch.ts +158 -0
- package/src/runtime/workflow/schedules/settlement.ts +48 -4
- package/src/runtime/workflow/schedules/store.ts +131 -10
- package/src/runtime/workflow/schedules/types.ts +6 -3
- package/src/runtime/workflow/schedules/work-continuation.ts +120 -34
- package/src/testing/test-governance.ts +8 -40
- package/assets/reference-configs/heartbeat-triage.md +0 -57
- package/assets/templates/helpers/harness-trace-grade.sh +0 -150
- package/assets/templates/helpers/heartbeat-triage.sh +0 -361
- package/assets/templates/helpers/maintenance-triage.sh +0 -167
- package/bin/forge-desktop-helper.mjs +0 -204
- package/docs/architecture/current/approved-target-architecture.zh-CN.md +0 -1501
- package/docs/architecture/current/architecture-invariants.md +0 -331
- package/docs/architecture/current/automation-and-schedule-engine.md +0 -453
- package/docs/architecture/current/codegraph-context-provider.md +0 -226
- package/docs/architecture/current/control-plane-authority-inventory.md +0 -94
- package/docs/architecture/current/controller-repository-decoupling.md +0 -148
- package/docs/architecture/current/dispatch-and-agent-strategy.md +0 -354
- package/docs/architecture/current/entity-model.md +0 -592
- package/docs/architecture/current/external-plugin-governance.md +0 -396
- package/docs/architecture/current/failure-recovery.md +0 -447
- package/docs/architecture/current/governance.md +0 -213
- package/docs/architecture/current/human-interaction-plane.md +0 -87
- package/docs/architecture/current/implementation-status.md +0 -128
- package/docs/architecture/current/job-and-run-lifecycle.md +0 -413
- package/docs/architecture/current/legacy-governance.md +0 -58
- package/docs/architecture/current/local-workspace-target-grants.md +0 -70
- package/docs/architecture/current/migration-roadmap.md +0 -105
- package/docs/architecture/current/multi-repository-execution.md +0 -371
- package/docs/architecture/current/operations-runbook.md +0 -45
- package/docs/architecture/current/peer-supercontroller-runtime.md +0 -32
- package/docs/architecture/current/personal-assistant-plugin-baseline.md +0 -206
- package/docs/architecture/current/repository-access-modes.md +0 -102
- package/docs/architecture/current/route-policy.md +0 -15
- package/docs/architecture/current/route-session-concurrency.md +0 -85
- package/docs/architecture/current/runtime-architecture-simplification.md +0 -214
- package/docs/architecture/current/runtime-directory-map.md +0 -49
- package/docs/architecture/current/runtime-health-and-resource-lifecycle.md +0 -85
- package/docs/architecture/current/scheduler-and-resource-claims.md +0 -422
- package/docs/architecture/current/session-aware-execution-and-authorization.md +0 -77
- package/docs/architecture/current/system-overview.md +0 -144
- package/docs/architecture/current/target-requirements-traceability.md +0 -202
- package/docs/architecture/current/thin-harness-governance-policy.md +0 -32
- package/docs/architecture/current/thin-harness-v1.md +0 -311
- package/docs/architecture/current/verification-and-release-gates.md +0 -391
- package/docs/forge-autonomous-goal-loop.md +0 -9
- package/docs/forge-provider-configuration.md +0 -7
- package/docs/operations/chatgpt-personal-assistant-runtime.md +0 -83
- package/docs/operations/controller-desktop-plugin.md +0 -112
- package/docs/operations/gmail-assistant-routines.md +0 -186
- package/docs/operations/personal-assistant-operator-guide.md +0 -61
- package/docs/operations/provider-configuration.md +0 -31
- package/scripts/harness-trace-grade.sh +0 -150
- package/scripts/heartbeat-triage.sh +0 -361
- package/scripts/maintenance-triage.sh +0 -167
- package/src/cli/local-bridge/frontend/src/features/settings/SettingsView.tsx +0 -2
- package/src/cli/repositories/goal-registry.ts +0 -239
- package/src/runtime/assistant/action-proposals.ts +0 -279
- package/src/runtime/assistant/intent.ts +0 -395
- package/src/runtime/assistant/model-provider.ts +0 -460
- package/src/runtime/assistant/openapi.ts +0 -223
- package/src/runtime/assistant/policy.ts +0 -89
- package/src/runtime/assistant/readiness.ts +0 -258
- package/src/runtime/assistant/routine-runtime.ts +0 -477
- package/src/runtime/assistant/schedule-binding.ts +0 -171
- package/src/runtime/assistant/standing-grants.ts +0 -375
- package/src/runtime/assistant/store.ts +0 -243
- package/src/runtime/assistant/types.ts +0 -135
- package/src/runtime/control-plane/goal-loop/config-facade.ts +0 -700
- package/src/runtime/control-plane/goal-loop/config-store.ts +0 -570
- package/src/runtime/control-plane/goal-loop/config-types.ts +0 -213
- package/src/runtime/control-plane/goal-loop/executor-router.ts +0 -109
- package/src/runtime/control-plane/goal-loop/failure-taxonomy.ts +0 -149
- package/src/runtime/control-plane/goal-loop/goal-contract-store.ts +0 -304
- package/src/runtime/control-plane/goal-loop/goal-loop-engine.ts +0 -1001
- package/src/runtime/control-plane/goal-loop/handoff-packet.ts +0 -160
- package/src/runtime/control-plane/goal-loop/index.ts +0 -13
- package/src/runtime/control-plane/goal-loop/local-tools.ts +0 -290
- package/src/runtime/control-plane/goal-loop/policy-gates.ts +0 -203
- package/src/runtime/control-plane/goal-loop/provider-dispatch.ts +0 -93
- package/src/runtime/control-plane/goal-loop/provider-registry.ts +0 -545
- package/src/runtime/control-plane/goal-loop/types.ts +0 -378
- package/src/runtime/control-plane/startup-recovery.ts +0 -198
- package/src/runtime/gateway/mcp/resource-policy.ts +0 -186
- package/src/runtime/personal-assistant/gmail-triage-manager.ts +0 -231
- package/src/runtime/personal-assistant/reporting-runtime.ts +0 -344
- package/src/runtime/personal-assistant/triage-runtime.ts +0 -748
- package/src/runtime/plugins/desktop-adapter.ts +0 -374
- package/src/runtime/safe-tooling/browser-safe.ts +0 -133
- package/src/runtime/workflow/findings/store.ts +0 -204
- package/src/runtime/workflow/findings/types.ts +0 -58
- package/src/runtime/workflow/portfolio/engine.ts +0 -20
- package/src/runtime/workflow/portfolio/store.ts +0 -185
- package/src/runtime/workflow/portfolio/types.ts +0 -29
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { createHash } from 'crypto';
|
|
2
2
|
import { existsSync } from 'fs';
|
|
3
3
|
import { dirname, resolve } from 'path';
|
|
4
|
+
import { fileURLToPath } from 'url';
|
|
4
5
|
import {
|
|
5
6
|
controllerCheckExecutionIdentity,
|
|
6
7
|
listControllerChecks,
|
|
@@ -23,9 +24,18 @@ import {
|
|
|
23
24
|
type RunCheckFacadeResult,
|
|
24
25
|
} from './check-facade';
|
|
25
26
|
import { claimsForCheck, toProcessClaims } from './resource-claims';
|
|
26
|
-
import { spawnManagedProcess } from './runtime';
|
|
27
|
+
import { getProcessHandle, spawnManagedProcess } from './runtime';
|
|
28
|
+
import { getProcessRecord, getProcessRequestBinding } from './store';
|
|
27
29
|
import { allocatePersistedCheckResultReceiptPath } from './check-result';
|
|
28
30
|
import { durationAwareInteractiveWaitMs } from './interactive-admission';
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Work verification may overlap another Work's repository-wide build cache.
|
|
34
|
+
* Wait through one ordinary sibling check, but never past Process Runtime's
|
|
35
|
+
* bounded lease-admission ceiling; explicit callers may still choose less.
|
|
36
|
+
*/
|
|
37
|
+
export const DEFAULT_WORK_CHECK_LEASE_WAIT_MS = 30_000;
|
|
38
|
+
|
|
29
39
|
const PERSISTED_CHECK_SOURCE_ENTRY = 'src/runtime/execution/process-runtime/check-runner-sidecar.ts';
|
|
30
40
|
|
|
31
41
|
export function resolvePersistedCheckCliInvocation(
|
|
@@ -37,7 +47,10 @@ export function resolvePersistedCheckCliInvocation(
|
|
|
37
47
|
}
|
|
38
48
|
|
|
39
49
|
export function resolveRuntimeCliTarget(controllerHome?: string): CliRuntimeTarget {
|
|
40
|
-
const
|
|
50
|
+
const moduleDir = typeof import.meta.dir === 'string' && import.meta.dir
|
|
51
|
+
? import.meta.dir
|
|
52
|
+
: dirname(fileURLToPath(import.meta.url));
|
|
53
|
+
const moduleSourceRoot = resolve(moduleDir, '..', '..', '..', '..');
|
|
41
54
|
const sourceEntry = resolve(moduleSourceRoot, 'src', 'cli', 'index.ts');
|
|
42
55
|
const source = resolveControllerRuntimeSourceRoot({
|
|
43
56
|
explicitRoot: existsSync(sourceEntry) ? moduleSourceRoot : undefined,
|
|
@@ -78,7 +91,7 @@ export function resolvePersistedCheckRuntimeExecutable(
|
|
|
78
91
|
entryExists: (path: string) => boolean = existsSync,
|
|
79
92
|
): string {
|
|
80
93
|
if (cliTarget.runtimeKind !== 'compiled_bun_release') return runtimeExecutable;
|
|
81
|
-
const checkRunner = resolve(
|
|
94
|
+
const checkRunner = resolve(cliTarget.cwd, 'forge-check-runner');
|
|
82
95
|
if (!entryExists(checkRunner)) {
|
|
83
96
|
throw new Error(`PERSISTED_CHECK_RUNNER_MISSING: ${checkRunner}`);
|
|
84
97
|
}
|
|
@@ -124,6 +137,38 @@ export async function runPersistedCheckViaProcessRuntime(
|
|
|
124
137
|
workerSpawnCount: 0,
|
|
125
138
|
projectionUpdateCount: 0,
|
|
126
139
|
};
|
|
140
|
+
const requestId = input.requestId?.trim();
|
|
141
|
+
const requestSemanticFingerprint = input.requestSemanticFingerprint?.trim();
|
|
142
|
+
if (requestId && requestSemanticFingerprint) {
|
|
143
|
+
const binding = getProcessRequestBinding(input.controllerHome, input.repoId, input.checkoutId, requestId);
|
|
144
|
+
if (binding) {
|
|
145
|
+
const record = getProcessRecord(input.controllerHome, input.repoId, binding.processId);
|
|
146
|
+
const exactIdentity = Boolean(
|
|
147
|
+
record
|
|
148
|
+
&& record.repoId === input.repoId
|
|
149
|
+
&& (record.checkoutId?.trim() || undefined) === (input.checkoutId?.trim() || undefined)
|
|
150
|
+
&& (record.workId?.trim() || undefined) === (input.workId?.trim() || undefined)
|
|
151
|
+
&& record.origin?.surface === 'check'
|
|
152
|
+
&& record.origin.requestId === requestId
|
|
153
|
+
&& record.origin.checkId === input.checkId
|
|
154
|
+
&& record.origin.requestSemanticFingerprint === requestSemanticFingerprint,
|
|
155
|
+
);
|
|
156
|
+
if (!exactIdentity) throw new Error(`PROCESS_REQUEST_ID_CONFLICT: ${requestId}`);
|
|
157
|
+
const existing = getProcessHandle(input.controllerHome, input.repoId, binding.processId);
|
|
158
|
+
if (!existing) {
|
|
159
|
+
throw new Error(`PROCESS_REQUEST_INCOMPLETE: request ${requestId} is bound to missing process ${binding.processId}; refusing re-execution`);
|
|
160
|
+
}
|
|
161
|
+
const check = listControllerChecks(input.repoRoot).find((entry) => entry.id === input.checkId);
|
|
162
|
+
return {
|
|
163
|
+
mode: existing.completed ? 'direct' : 'managed',
|
|
164
|
+
checkId: input.checkId,
|
|
165
|
+
check,
|
|
166
|
+
process: { ...existing, deduplicated: true, requestId },
|
|
167
|
+
ok: existing.completed ? existing.ok : undefined,
|
|
168
|
+
durableSideEffects: emptyEffects,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
}
|
|
127
172
|
const verificationSnapshot = input.verificationSnapshot
|
|
128
173
|
? materializeWorkVerificationSnapshot({
|
|
129
174
|
controllerHome: input.controllerHome,
|
|
@@ -149,7 +194,7 @@ export async function runPersistedCheckViaProcessRuntime(
|
|
|
149
194
|
durableSideEffects: emptyEffects,
|
|
150
195
|
};
|
|
151
196
|
}
|
|
152
|
-
if (input.forceDurable || checkRequiresDurableWorkflow(input.checkId, check)) {
|
|
197
|
+
if (input.forceDurable || (checkRequiresDurableWorkflow(input.checkId, check) && input.allowDurableCheckExecution !== true)) {
|
|
153
198
|
cleanupVerificationSnapshot();
|
|
154
199
|
return {
|
|
155
200
|
mode: 'durable',
|
|
@@ -164,7 +209,8 @@ export async function runPersistedCheckViaProcessRuntime(
|
|
|
164
209
|
}
|
|
165
210
|
|
|
166
211
|
const interactiveWaitMs = durationAwareInteractiveWaitMs(check.command, input.interactiveWaitMs);
|
|
167
|
-
const leaseWaitMs = input.leaseWaitMs
|
|
212
|
+
const leaseWaitMs = input.leaseWaitMs
|
|
213
|
+
?? (input.workId?.trim() ? DEFAULT_WORK_CHECK_LEASE_WAIT_MS : undefined);
|
|
168
214
|
const timeoutMs = Math.min(
|
|
169
215
|
check.timeoutMs,
|
|
170
216
|
typeof input.timeoutMs === 'number' && Number.isFinite(input.timeoutMs)
|
|
@@ -214,7 +260,11 @@ export async function runPersistedCheckViaProcessRuntime(
|
|
|
214
260
|
scopeKey: persistedCheckSemanticScopeKey(input, semanticCheck.reuseScope),
|
|
215
261
|
};
|
|
216
262
|
const cliTarget = resolveRuntimeCliTarget(input.controllerHome);
|
|
217
|
-
const checkResultReceiptPath = allocatePersistedCheckResultReceiptPath(
|
|
263
|
+
const checkResultReceiptPath = allocatePersistedCheckResultReceiptPath(
|
|
264
|
+
input.controllerHome,
|
|
265
|
+
input.repoId,
|
|
266
|
+
input.requestId,
|
|
267
|
+
);
|
|
218
268
|
const checkArgs = [
|
|
219
269
|
'--repo',
|
|
220
270
|
executionRoot,
|
|
@@ -254,6 +304,7 @@ export async function runPersistedCheckViaProcessRuntime(
|
|
|
254
304
|
toolName: 'run_check',
|
|
255
305
|
checkId: input.checkId,
|
|
256
306
|
requestId: input.requestId,
|
|
307
|
+
requestSemanticFingerprint: input.requestSemanticFingerprint,
|
|
257
308
|
correlationId: input.workId,
|
|
258
309
|
executionSessionId: input.verificationBinding?.executionSessionId,
|
|
259
310
|
editSessionId: input.verificationBinding?.editSessionId,
|
|
@@ -4,6 +4,8 @@
|
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
6
|
import type { ResourceClaimSpec } from '../jobs/types';
|
|
7
|
+
import { readFileSync } from 'fs';
|
|
8
|
+
import { join } from 'path';
|
|
7
9
|
import type { ControllerCheckEffects } from '../../../cli/controller/check-runner';
|
|
8
10
|
import { classifyRepositoryCommand, fixedShellWrapperCommand } from '../../../cli/repositories/command-classifier';
|
|
9
11
|
import { normalizeRepositoryCommand } from '../../../cli/repositories/command-normalization';
|
|
@@ -157,7 +159,89 @@ function iosSimulatorTestCommandClaims(
|
|
|
157
159
|
]);
|
|
158
160
|
}
|
|
159
161
|
|
|
160
|
-
function
|
|
162
|
+
function localHttpServerClaims(command: string | readonly string[]): ResourceClaimSpec[] | undefined {
|
|
163
|
+
const canonical = normalizeRepositoryCommand(command);
|
|
164
|
+
if (canonical.kind !== 'argv') return undefined;
|
|
165
|
+
const program = (canonical.executable ?? '').split(/[\\/]/).at(-1)?.toLowerCase();
|
|
166
|
+
if (program !== 'python' && program !== 'python3') return undefined;
|
|
167
|
+
const args = canonical.args ?? [];
|
|
168
|
+
if (args[0] !== '-m' || args[1] !== 'http.server' || args.includes('--cgi')) return undefined;
|
|
169
|
+
let bind = '0.0.0.0';
|
|
170
|
+
let port = '8000';
|
|
171
|
+
for (let index = 2; index < args.length; index += 1) {
|
|
172
|
+
const arg = args[index]!;
|
|
173
|
+
if (arg === '--bind') { bind = args[index + 1] ?? bind; index += 1; continue; }
|
|
174
|
+
if (arg === '--directory' || arg === '-d' || arg === '--protocol') { index += 1; continue; }
|
|
175
|
+
if (!arg.startsWith('-') && /^\d{1,5}$/.test(arg)) port = arg;
|
|
176
|
+
}
|
|
177
|
+
return [claimHostService(`tcp-listen:${bind}:${port}`, 'write')];
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function loopbackCurlClaims(command: string | readonly string[], repoId: string): ResourceClaimSpec[] | undefined {
|
|
181
|
+
const canonical = normalizeRepositoryCommand(command);
|
|
182
|
+
if (canonical.kind !== 'argv') return undefined;
|
|
183
|
+
const program = (canonical.executable ?? '').split(/[\\/]/).at(-1)?.toLowerCase();
|
|
184
|
+
if (program !== 'curl') return undefined;
|
|
185
|
+
const args = canonical.args ?? [];
|
|
186
|
+
if (args.some((arg) => /^(?:-X|--request|-d|--data|--data-raw|--data-binary|-F|--form|-T|--upload-file|-o|--output|-O|--remote-name)$/i.test(arg))) return undefined;
|
|
187
|
+
const explicitMethodIndex = args.findIndex((arg) => arg === '-X' || arg === '--request');
|
|
188
|
+
if (explicitMethodIndex >= 0 && !/^(?:GET|HEAD)$/i.test(args[explicitMethodIndex + 1] ?? '')) return undefined;
|
|
189
|
+
const urls = args.filter((arg) => /^https?:\/\//i.test(arg));
|
|
190
|
+
if (urls.length === 0 || urls.some((url) => {
|
|
191
|
+
try {
|
|
192
|
+
const host = new URL(url).hostname.toLowerCase();
|
|
193
|
+
return !['127.0.0.1', 'localhost', '::1'].includes(host);
|
|
194
|
+
} catch { return true; }
|
|
195
|
+
})) return undefined;
|
|
196
|
+
return [claimNetwork(repoId, 'read')];
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
function simpleViteScript(script: string): boolean {
|
|
200
|
+
const normalized = script.trim();
|
|
201
|
+
if (!normalized || /[;&|`$<>\n\r]/.test(normalized)) return false;
|
|
202
|
+
return /^(?:(?:\.\/)?node_modules\/.bin\/)?vite(?:\s|$)/.test(normalized);
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
function viteServiceClaims(
|
|
206
|
+
command: string | readonly string[],
|
|
207
|
+
repoId: string,
|
|
208
|
+
checkoutId?: string,
|
|
209
|
+
repoRoot?: string,
|
|
210
|
+
): ResourceClaimSpec[] | undefined {
|
|
211
|
+
const canonical = normalizeRepositoryCommand(command);
|
|
212
|
+
if (canonical.kind !== 'argv') return undefined;
|
|
213
|
+
const program = (canonical.executable ?? '').split(/[\\/]/).at(-1)?.toLowerCase();
|
|
214
|
+
const args = canonical.args ?? [];
|
|
215
|
+
let vite = program === 'vite';
|
|
216
|
+
if (!vite && (program === 'npx' || program === 'bunx') && args[0]?.toLowerCase() === 'vite') vite = true;
|
|
217
|
+
if (!vite && program === 'bun' && args[0]?.toLowerCase() === 'x' && args[1]?.toLowerCase() === 'vite') vite = true;
|
|
218
|
+
if (!vite && repoRoot && ['npm', 'pnpm', 'yarn', 'bun'].includes(program ?? '')) {
|
|
219
|
+
const runIndex = args[0]?.toLowerCase() === 'run' ? 1 : program === 'yarn' ? 0 : -1;
|
|
220
|
+
const scriptName = runIndex >= 0 ? args[runIndex]?.toLowerCase() : undefined;
|
|
221
|
+
if (scriptName === 'dev') {
|
|
222
|
+
try {
|
|
223
|
+
const pkg = JSON.parse(readFileSync(join(repoRoot, 'package.json'), 'utf8')) as { scripts?: Record<string, unknown> };
|
|
224
|
+
const script = typeof pkg.scripts?.dev === 'string' ? pkg.scripts.dev : '';
|
|
225
|
+
vite = simpleViteScript(script);
|
|
226
|
+
} catch {
|
|
227
|
+
vite = false;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
}
|
|
231
|
+
if (!vite) return undefined;
|
|
232
|
+
return normalizeClaims([
|
|
233
|
+
claimBuildCacheWrite(repoId),
|
|
234
|
+
claimHostService(`vite:${checkoutScope(checkoutId)}`, 'write'),
|
|
235
|
+
]);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
function hostOnlyCommandClaims(command: string | readonly string[], repoId: string, checkoutId?: string, repoRoot?: string): ResourceClaimSpec[] | undefined {
|
|
239
|
+
const localServer = localHttpServerClaims(command);
|
|
240
|
+
if (localServer) return localServer;
|
|
241
|
+
const loopbackCurl = loopbackCurlClaims(command, repoId);
|
|
242
|
+
if (loopbackCurl) return loopbackCurl;
|
|
243
|
+
const vite = viteServiceClaims(command, repoId, checkoutId, repoRoot);
|
|
244
|
+
if (vite) return vite;
|
|
161
245
|
const canonical = normalizeRepositoryCommand(command);
|
|
162
246
|
if (canonical.kind === 'shell') {
|
|
163
247
|
return browserAppleScriptHeredocClaims(canonical.shellCommand ?? '');
|
|
@@ -347,13 +431,14 @@ export function claimsForRepositoryCommand(
|
|
|
347
431
|
repoId: string,
|
|
348
432
|
checkoutId?: string,
|
|
349
433
|
defaultBranch?: string,
|
|
434
|
+
repoRoot?: string,
|
|
350
435
|
): ResourceClaimSpec[] {
|
|
351
436
|
const classification = classifyRepositoryCommand(command, defaultBranch);
|
|
352
437
|
const canonical = normalizeRepositoryCommand(command);
|
|
353
438
|
const focused = isFocusedCheckCommand(command);
|
|
354
439
|
const iosSimulatorClaims = iosSimulatorTestCommandClaims(command, repoId, checkoutId);
|
|
355
440
|
if (iosSimulatorClaims) return iosSimulatorClaims;
|
|
356
|
-
const hostClaims = hostOnlyCommandClaims(command);
|
|
441
|
+
const hostClaims = hostOnlyCommandClaims(command, repoId, checkoutId, repoRoot);
|
|
357
442
|
if (hostClaims) return hostClaims;
|
|
358
443
|
|
|
359
444
|
if (classification.risk === 'readonly') {
|
|
@@ -426,12 +511,16 @@ export function claimsForCheck(
|
|
|
426
511
|
): ResourceClaimSpec[] {
|
|
427
512
|
const heavy = /(?:^|:)(?:test(?::coverage)?|check:(?:ci|forge-runtime|public-export|release(?:-[a-z0-9-]+)?))$/.test(checkId)
|
|
428
513
|
|| /release|migration|integrate/i.test(checkId);
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
514
|
+
const baseClaims = effects
|
|
515
|
+
? claimsForDeclaredCheckEffects(checkId, effects, repoId, checkoutId)
|
|
516
|
+
: staticAnalysisCheckId(checkId)
|
|
517
|
+
? [claimWorkspaceRead(checkoutId), claimBuildCacheWrite(repoId)]
|
|
518
|
+
: command && command.length > 0
|
|
519
|
+
? claimsForRepositoryCommand(command, repoId, checkoutId)
|
|
520
|
+
: [claimWorkspaceWrite(checkoutId)];
|
|
521
|
+
// Heavy-check is an additional cross-check serialization fence, not a
|
|
522
|
+
// substitute for the resources the check actually reads or writes.
|
|
523
|
+
return heavy ? normalizeClaims([claimHeavyCheck(repoId), ...baseClaims]) : baseClaims;
|
|
435
524
|
}
|
|
436
525
|
|
|
437
526
|
export function scopeResourceClaims(
|
|
@@ -258,8 +258,23 @@ function captureIdentity(pid: number | undefined): {
|
|
|
258
258
|
} {
|
|
259
259
|
if (!pid || pid <= 0) return {};
|
|
260
260
|
const probe = defaultProcessIdentityProbe;
|
|
261
|
-
|
|
261
|
+
// The combined inspect probe already proves the PID exists on the normal
|
|
262
|
+
// POSIX path. Avoid an immediately preceding ps(stat) child process; only
|
|
263
|
+
// fall back to the separate alive/command/start-time probes when the combined
|
|
264
|
+
// observation is unavailable or incomplete.
|
|
262
265
|
const inspected = probe.inspect?.(pid);
|
|
266
|
+
if (inspected?.command && inspected.startTime) {
|
|
267
|
+
return {
|
|
268
|
+
identity: {
|
|
269
|
+
pid,
|
|
270
|
+
processStartTime: inspected.startTime,
|
|
271
|
+
executableFingerprint: executableFingerprint(inspected.command),
|
|
272
|
+
processGroupId: process.platform !== 'win32' ? pid : undefined,
|
|
273
|
+
},
|
|
274
|
+
identityUntrusted: false,
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
if (!probe.isAlive(pid)) return {};
|
|
263
278
|
const command = inspected?.command ?? probe.command(pid);
|
|
264
279
|
const startTime = inspected?.startTime ?? probe.startTime(pid);
|
|
265
280
|
if (!command || !startTime) {
|
|
@@ -291,8 +306,9 @@ function identityStillMatches(identity: ManagedProcessRecord['identity'] | undef
|
|
|
291
306
|
}
|
|
292
307
|
if (!isProcessAlive(identity.pid)) return false;
|
|
293
308
|
const probe = defaultProcessIdentityProbe;
|
|
294
|
-
const
|
|
295
|
-
const
|
|
309
|
+
const inspected = probe.inspect?.(identity.pid);
|
|
310
|
+
const startTime = inspected?.startTime ?? probe.startTime(identity.pid);
|
|
311
|
+
const command = inspected?.command ?? probe.command(identity.pid);
|
|
296
312
|
if (!startTime || !command) return false;
|
|
297
313
|
if (startTime !== identity.processStartTime) return false;
|
|
298
314
|
if (executableFingerprint(command) !== identity.executableFingerprint) return false;
|
|
@@ -526,7 +542,7 @@ export function completeProcessFromEvidence(
|
|
|
526
542
|
stdoutTail?: string;
|
|
527
543
|
stderrTail?: string;
|
|
528
544
|
},
|
|
529
|
-
options: { authority?: 'runtime_writer' | 'durable_exit_receipt' | 'durable_pre_spawn_abandonment' } = {},
|
|
545
|
+
options: { authority?: 'runtime_writer' | 'durable_exit_receipt' | 'durable_pre_spawn_abandonment' | 'durable_stale_active_reconciliation' } = {},
|
|
530
546
|
): ManagedProcessRecord | undefined {
|
|
531
547
|
// Validated independent evidence may only perform the monotonic,
|
|
532
548
|
// fence-token-bound terminal CAS below. It does not grant general writer
|
|
@@ -612,6 +628,45 @@ export function reconcileAbandonedPreSpawnProcess(
|
|
|
612
628
|
}, { authority: 'durable_pre_spawn_abandonment' });
|
|
613
629
|
}
|
|
614
630
|
|
|
631
|
+
/**
|
|
632
|
+
* Bounded maintenance reconciliation for stale active Process records that are
|
|
633
|
+
* no longer reachable through the v2 recovery index. This never deletes an
|
|
634
|
+
* active record and never re-executes a command: it first consumes any durable
|
|
635
|
+
* Runner receipt, preserves live monitors / matching process identities, and
|
|
636
|
+
* only then commits a monotonic terminal state for a provably dead identity.
|
|
637
|
+
*/
|
|
638
|
+
export function reconcileStaleManagedProcessForMaintenance(
|
|
639
|
+
controllerHome: string,
|
|
640
|
+
repoId: string,
|
|
641
|
+
processId: string,
|
|
642
|
+
options: { nowMs?: number; minAgeMs?: number } = {},
|
|
643
|
+
): ManagedProcessRecord | undefined {
|
|
644
|
+
let record = getProcessRecord(controllerHome, repoId, processId);
|
|
645
|
+
if (!record || !isManagedProcessActive(record)) return record;
|
|
646
|
+
|
|
647
|
+
const nowMs = options.nowMs ?? Date.now();
|
|
648
|
+
const minAgeMs = Math.max(1_000, options.minAgeMs ?? DEFAULT_PRE_SPAWN_ABANDONMENT_MS);
|
|
649
|
+
const updatedAtMs = Date.parse(record.updatedAt || record.startedAt);
|
|
650
|
+
if (!Number.isFinite(updatedAtMs) || nowMs - updatedAtMs < minAgeMs) return record;
|
|
651
|
+
|
|
652
|
+
const fromReceipt = applyReceiptIfPresent(controllerHome, repoId, processId, record);
|
|
653
|
+
if (fromReceipt) return fromReceipt;
|
|
654
|
+
|
|
655
|
+
if (record.status === 'starting' && !record.identity) {
|
|
656
|
+
return reconcileAbandonedPreSpawnProcess(controllerHome, repoId, processId, { nowMs, minAgeMs });
|
|
657
|
+
}
|
|
658
|
+
|
|
659
|
+
record = getProcessRecord(controllerHome, repoId, processId) ?? record;
|
|
660
|
+
if (!isManagedProcessActive(record) || liveMonitors.has(processId)) return record;
|
|
661
|
+
if (identityStillMatches(record.identity, record.identityUntrusted)) return record;
|
|
662
|
+
|
|
663
|
+
return completeProcessFromEvidence(controllerHome, repoId, processId, record.terminalFenceToken, {
|
|
664
|
+
status: 'completed_unknown',
|
|
665
|
+
finishedAt: new Date(nowMs).toISOString(),
|
|
666
|
+
errorMessage: 'PROCESS_STALE_ACTIVE_RECONCILED: stale active record has no live matching process identity or durable exit receipt',
|
|
667
|
+
}, { authority: 'durable_stale_active_reconciliation' });
|
|
668
|
+
}
|
|
669
|
+
|
|
615
670
|
async function killTree(child: ChildProcess): Promise<void> {
|
|
616
671
|
const pid = child.pid;
|
|
617
672
|
if (!pid) return;
|
|
@@ -299,6 +299,7 @@ function sanitizeProcessRecord(record: ManagedProcessRecord): { record: ManagedP
|
|
|
299
299
|
toolName: safeText(record.origin.toolName),
|
|
300
300
|
requestId: safeText(record.origin.requestId),
|
|
301
301
|
checkId: safeText(record.origin.checkId),
|
|
302
|
+
requestSemanticFingerprint: safeText(record.origin.requestSemanticFingerprint),
|
|
302
303
|
correlationId: safeText(record.origin.correlationId),
|
|
303
304
|
} : undefined,
|
|
304
305
|
};
|
|
@@ -308,6 +309,12 @@ function sanitizeProcessRecord(record: ManagedProcessRecord): { record: ManagedP
|
|
|
308
309
|
function readProcessRecord(path: string): ManagedProcessRecord | undefined {
|
|
309
310
|
const record = readJson<ManagedProcessRecord>(path);
|
|
310
311
|
if (!record || record.schemaVersion !== 1) return undefined;
|
|
312
|
+
// Legacy/partial Process files can predate the required command descriptor.
|
|
313
|
+
// They are not executable recovery authority and must never be repaired by
|
|
314
|
+
// inventing a command. Treat them as unavailable to normal Runtime readers;
|
|
315
|
+
// bounded GC has its own metadata-only parser so one such file cannot block
|
|
316
|
+
// cleanup of unrelated proven terminal records.
|
|
317
|
+
if (!record.command || typeof record.command !== 'object' || Array.isArray(record.command)) return undefined;
|
|
311
318
|
const sanitized = sanitizeProcessRecord(record);
|
|
312
319
|
if (sanitized.changed) atomicWrite(path, sanitized.record);
|
|
313
320
|
return sanitized.record;
|
|
@@ -239,6 +239,8 @@ export interface ManagedProcessRecord {
|
|
|
239
239
|
toolName?: string;
|
|
240
240
|
requestId?: string;
|
|
241
241
|
checkId?: string;
|
|
242
|
+
/** Stable semantic identity supplied by a facade for exact request reattachment. */
|
|
243
|
+
requestSemanticFingerprint?: string;
|
|
242
244
|
correlationId?: string;
|
|
243
245
|
/** Controller MCP session that initiated this check, when applicable. */
|
|
244
246
|
executionSessionId?: string;
|
|
@@ -10,6 +10,9 @@ export interface BoundedProcessOptions {
|
|
|
10
10
|
signal?: AbortSignal;
|
|
11
11
|
/** When true (default on non-Windows), create a new process group for tree kill. */
|
|
12
12
|
processGroup?: boolean;
|
|
13
|
+
onSpawn?: (pid: number) => void;
|
|
14
|
+
onStdout?: (chunk: string) => void;
|
|
15
|
+
onStderr?: (chunk: string) => void;
|
|
13
16
|
}
|
|
14
17
|
|
|
15
18
|
export interface BoundedProcessResult {
|
|
@@ -114,6 +117,7 @@ export async function runBoundedProcess(
|
|
|
114
117
|
stdio: ['ignore', 'pipe', 'pipe'],
|
|
115
118
|
detached: useProcessGroup,
|
|
116
119
|
});
|
|
120
|
+
if (child.pid) options.onSpawn?.(child.pid);
|
|
117
121
|
|
|
118
122
|
const finish = (exitCode: number) => {
|
|
119
123
|
if (settled) return;
|
|
@@ -150,8 +154,14 @@ export async function runBoundedProcess(
|
|
|
150
154
|
|
|
151
155
|
options.signal?.addEventListener('abort', onAbort, { once: true });
|
|
152
156
|
|
|
153
|
-
child.stdout?.on('data', (chunk: Buffer) =>
|
|
154
|
-
|
|
157
|
+
child.stdout?.on('data', (chunk: Buffer) => {
|
|
158
|
+
stdout.write(chunk);
|
|
159
|
+
options.onStdout?.(chunk.toString('utf8'));
|
|
160
|
+
});
|
|
161
|
+
child.stderr?.on('data', (chunk: Buffer) => {
|
|
162
|
+
stderr.write(chunk);
|
|
163
|
+
options.onStderr?.(chunk.toString('utf8'));
|
|
164
|
+
});
|
|
155
165
|
child.on('error', (error) => {
|
|
156
166
|
spawnError = error.message;
|
|
157
167
|
});
|
|
@@ -5,7 +5,12 @@
|
|
|
5
5
|
import { isMainThread, parentPort, workerData, Worker } from 'worker_threads';
|
|
6
6
|
import { fileURLToPath } from 'url';
|
|
7
7
|
import { getMcpPolicy } from '../../../cli/mcp/policy';
|
|
8
|
-
import {
|
|
8
|
+
import type { McpPolicy } from '../../../cli/mcp/types';
|
|
9
|
+
import {
|
|
10
|
+
searchRepository,
|
|
11
|
+
searchRepositoryMany,
|
|
12
|
+
type SearchRepositoryManyResult,
|
|
13
|
+
} from '../../../cli/repository/inspector';
|
|
9
14
|
|
|
10
15
|
export interface SearchWorkerInput {
|
|
11
16
|
repoRoot: string;
|
|
@@ -14,14 +19,44 @@ export interface SearchWorkerInput {
|
|
|
14
19
|
maxFiles: number;
|
|
15
20
|
}
|
|
16
21
|
|
|
22
|
+
export interface SearchManyWorkerInput {
|
|
23
|
+
repoRoot: string;
|
|
24
|
+
policy: McpPolicy;
|
|
25
|
+
queries: string[];
|
|
26
|
+
files?: string[];
|
|
27
|
+
includeGlobs?: string[];
|
|
28
|
+
excludeGlobs?: string[];
|
|
29
|
+
maxResultsPerQuery?: number;
|
|
30
|
+
maxFiles?: number;
|
|
31
|
+
caseSensitive?: boolean;
|
|
32
|
+
cacheKey?: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
17
35
|
export interface SearchWorkerResult {
|
|
18
36
|
ok: boolean;
|
|
19
37
|
payload?: Record<string, unknown>;
|
|
20
38
|
error?: string;
|
|
21
39
|
}
|
|
22
40
|
|
|
23
|
-
function
|
|
41
|
+
function isManySearch(input: SearchWorkerInput | SearchManyWorkerInput): input is SearchManyWorkerInput {
|
|
42
|
+
return Array.isArray((input as SearchManyWorkerInput).queries);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
function runSearchSync(input: SearchWorkerInput | SearchManyWorkerInput): SearchWorkerResult {
|
|
24
46
|
try {
|
|
47
|
+
if (isManySearch(input)) {
|
|
48
|
+
const result = searchRepositoryMany(input.repoRoot, input.policy, {
|
|
49
|
+
queries: input.queries,
|
|
50
|
+
files: input.files,
|
|
51
|
+
includeGlobs: input.includeGlobs,
|
|
52
|
+
excludeGlobs: input.excludeGlobs,
|
|
53
|
+
maxResultsPerQuery: input.maxResultsPerQuery,
|
|
54
|
+
maxFiles: input.maxFiles,
|
|
55
|
+
caseSensitive: input.caseSensitive,
|
|
56
|
+
cacheKey: input.cacheKey,
|
|
57
|
+
}) as unknown as Record<string, unknown>;
|
|
58
|
+
return { ok: true, payload: result };
|
|
59
|
+
}
|
|
25
60
|
const result = searchRepository(input.repoRoot, getMcpPolicy('controller', { repoRoot: input.repoRoot }), {
|
|
26
61
|
query: input.query,
|
|
27
62
|
maxResults: input.maxResults,
|
|
@@ -36,8 +71,8 @@ function runSearchSync(input: SearchWorkerInput): SearchWorkerResult {
|
|
|
36
71
|
}
|
|
37
72
|
}
|
|
38
73
|
|
|
39
|
-
|
|
40
|
-
input: SearchWorkerInput,
|
|
74
|
+
async function runSearchWorker(
|
|
75
|
+
input: SearchWorkerInput | SearchManyWorkerInput,
|
|
41
76
|
options: { timeoutMs?: number; signal?: AbortSignal } = {},
|
|
42
77
|
): Promise<Record<string, unknown>> {
|
|
43
78
|
if (options.signal?.aborted) throw new Error('CANCELLED: search aborted');
|
|
@@ -108,7 +143,21 @@ export async function runInspectorSearchInWorker(
|
|
|
108
143
|
});
|
|
109
144
|
}
|
|
110
145
|
|
|
146
|
+
export async function runInspectorSearchInWorker(
|
|
147
|
+
input: SearchWorkerInput,
|
|
148
|
+
options: { timeoutMs?: number; signal?: AbortSignal } = {},
|
|
149
|
+
): Promise<Record<string, unknown>> {
|
|
150
|
+
return await runSearchWorker(input, options);
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export async function runInspectorSearchManyInWorker(
|
|
154
|
+
input: SearchManyWorkerInput,
|
|
155
|
+
options: { timeoutMs?: number; signal?: AbortSignal } = {},
|
|
156
|
+
): Promise<SearchRepositoryManyResult> {
|
|
157
|
+
return await runSearchWorker(input, options) as unknown as SearchRepositoryManyResult;
|
|
158
|
+
}
|
|
159
|
+
|
|
111
160
|
if (!isMainThread && parentPort) {
|
|
112
|
-
const input = workerData as SearchWorkerInput;
|
|
161
|
+
const input = workerData as SearchWorkerInput | SearchManyWorkerInput;
|
|
113
162
|
parentPort.postMessage(runSearchSync(input));
|
|
114
163
|
}
|
|
@@ -21,7 +21,6 @@ import {
|
|
|
21
21
|
} from '../jobs/child-reference';
|
|
22
22
|
import { markOperationDelegated } from '../jobs/receipt-store';
|
|
23
23
|
import { assertAutomatedOperationAllowed } from '../../control-plane/governance/external-effects';
|
|
24
|
-
import { recordCandidateFinding, updateCandidateFinding } from '../../workflow/findings/store';
|
|
25
24
|
import { triggerWorkspaceAgent } from '../workspace-agent';
|
|
26
25
|
import { executeAssistantPluginAction } from '../../plugins/store';
|
|
27
26
|
import { CONTROLLER_SCOPE_REPO_ID, controllerSystemRoot, repositoryControllerRoot } from '../../../cli/repositories/controller-home';
|
|
@@ -29,7 +28,6 @@ import { writeExecutionArtifact } from '../../evidence/artifact-store';
|
|
|
29
28
|
import { existsSync } from 'fs';
|
|
30
29
|
import { isAbsolute, relative, resolve, sep } from 'path';
|
|
31
30
|
import { isAssistantPluginError } from '../../plugins/errors';
|
|
32
|
-
import { executeAssistantRoutineRuntime } from '../../assistant/routine-runtime';
|
|
33
31
|
|
|
34
32
|
|
|
35
33
|
function childReferenceFromLocalJob(
|
|
@@ -445,20 +443,6 @@ export async function executeExecutionJob(controllerHome: string, job: Execution
|
|
|
445
443
|
}
|
|
446
444
|
bindRepositoryEntities(repository);
|
|
447
445
|
|
|
448
|
-
if (job.payload.target === 'runtime' && job.payload.operation === 'assistant_routine_execute') {
|
|
449
|
-
const routineId = String(job.payload.arguments?.routineId ?? job.payload.arguments?.routine_id ?? '').trim();
|
|
450
|
-
if (!routineId) throw new Error('ASSISTANT_ROUTINE_ID_REQUIRED');
|
|
451
|
-
const routineResult = await executeAssistantRoutineRuntime({
|
|
452
|
-
controllerHome,
|
|
453
|
-
repository,
|
|
454
|
-
routineId,
|
|
455
|
-
requestId: job.requestId,
|
|
456
|
-
origin: job.origin,
|
|
457
|
-
occurrenceId: typeof job.payload.occurrenceId === 'string' ? job.payload.occurrenceId : undefined,
|
|
458
|
-
});
|
|
459
|
-
return { ok: true, result: { assistantRoutine: routineResult }, repoRoot };
|
|
460
|
-
}
|
|
461
|
-
|
|
462
446
|
if (job.payload.target === 'runtime' && job.payload.operation === 'legacy-local-job') {
|
|
463
447
|
const localJobId = String(job.payload.arguments?.localJobId ?? '').trim();
|
|
464
448
|
if (!localJobId) throw new Error('LEGACY_JOB_ID_REQUIRED');
|
|
@@ -511,26 +495,6 @@ export async function executeExecutionJob(controllerHome: string, job: Execution
|
|
|
511
495
|
: { ok: false, error: { code: 'RELEASE_GATE_BLOCKED', message: 'Release gate has blockers.', retryable: false, details: { gate } }, repoRoot };
|
|
512
496
|
}
|
|
513
497
|
|
|
514
|
-
if (job.payload.operation === 'record_candidate_finding') {
|
|
515
|
-
const args = job.payload.arguments ?? {};
|
|
516
|
-
const finding = recordCandidateFinding(controllerHome, {
|
|
517
|
-
repoId: job.repoId,
|
|
518
|
-
requestId: job.requestId,
|
|
519
|
-
semanticKey: String(args.semantic_key ?? args.semanticKey ?? '').trim(),
|
|
520
|
-
title: String(args.title ?? '').trim(),
|
|
521
|
-
summary: typeof args.summary === 'string' ? args.summary : undefined,
|
|
522
|
-
severity: ['low', 'medium', 'high', 'critical'].includes(String(args.severity))
|
|
523
|
-
? String(args.severity) as 'low' | 'medium' | 'high' | 'critical'
|
|
524
|
-
: 'medium',
|
|
525
|
-
evidence: {
|
|
526
|
-
source: job.origin.surface,
|
|
527
|
-
reference: typeof args.reference === 'string' ? args.reference : job.origin.correlationId,
|
|
528
|
-
details: args.evidence && typeof args.evidence === 'object' ? args.evidence as Record<string, unknown> : undefined,
|
|
529
|
-
},
|
|
530
|
-
});
|
|
531
|
-
return { ok: true, result: { finding }, repoRoot };
|
|
532
|
-
}
|
|
533
|
-
|
|
534
498
|
const allowedAgents = Array.isArray(job.payload.allowedAgents)
|
|
535
499
|
? job.payload.allowedAgents.filter((value): value is string => typeof value === 'string')
|
|
536
500
|
: [];
|
|
@@ -651,18 +615,6 @@ export async function executeExecutionJob(controllerHome: string, job: Execution
|
|
|
651
615
|
);
|
|
652
616
|
}
|
|
653
617
|
|
|
654
|
-
if (job.origin.actor === 'promote_candidate_finding' && job.origin.correlationId) {
|
|
655
|
-
try {
|
|
656
|
-
updateCandidateFinding(controllerHome, job.repoId, job.origin.correlationId, (current) => ({
|
|
657
|
-
...current,
|
|
658
|
-
status: result.isError ? 'candidate' : 'promoted',
|
|
659
|
-
promotedJobId: job.jobId,
|
|
660
|
-
}), job.requestId, result.isError ? 'candidate_promotion_failed' : 'candidate_finding_promoted');
|
|
661
|
-
} catch {
|
|
662
|
-
// The Issue result remains authoritative even if the candidate projection needs reconciliation.
|
|
663
|
-
}
|
|
664
|
-
}
|
|
665
|
-
|
|
666
618
|
if (result.isError) {
|
|
667
619
|
return { ok: false, error: { code: 'MCP_TOOL_FAILED', message: JSON.stringify(record), retryable: false }, repoRoot };
|
|
668
620
|
}
|