@moretea-labs/forge 1.6.0 → 1.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/AGENTS.md +4 -0
- package/CHANGELOG.md +39 -0
- package/CLAUDE.md +4 -0
- package/README.md +2 -2
- package/README.zh-CN.md +1 -1
- package/SKILL.md +2 -2
- package/THIRD_PARTY_NOTICES.md +1 -1
- package/agents/openai.yaml +1 -1
- package/assets/hooks/hook-input.sh +11 -8
- package/assets/hooks/lib/session-state.sh +10 -7
- package/assets/hooks/lib/workflow-state.sh +13 -177
- package/assets/hooks/post-tool-observer.sh +8 -2
- package/assets/hooks/prompt-guard.sh +5 -81
- package/assets/hooks/session-start-context.sh +56 -1
- package/assets/partials/05-workflow.partial.md +1 -1
- package/assets/partials-agents/02-operating-mode.partial.md +2 -0
- package/assets/partials-agents/03-orchestration.partial.md +1 -1
- package/assets/plugin-registry.v1.json +3 -3
- package/assets/reference-configs/document-generation.md +1 -1
- package/assets/reference-configs/harness-overview.md +1 -1
- package/assets/reference-configs/sprint-contracts.md +3 -3
- package/assets/skill-version.json +2 -2
- package/assets/templates/contract.template.md +2 -7
- package/assets/templates/helpers/capture-plan.sh +2 -2
- package/assets/templates/helpers/check-architecture-sync.sh +17 -0
- package/assets/templates/helpers/check-task-workflow.sh +4 -4
- package/assets/templates/helpers/contract-worktree.sh +1 -21
- package/assets/templates/helpers/ensure-task-workflow.sh +11 -36
- package/assets/templates/helpers/inspect-project-state.ts +8 -6
- package/assets/templates/helpers/migrate-project-template.sh +22 -45
- package/assets/templates/helpers/migrate-workflow-docs.ts +9 -7
- package/assets/templates/helpers/new-plan.sh +1 -1
- package/assets/templates/helpers/plan-to-todo.sh +12 -37
- package/assets/templates/helpers/ship-worktrees.sh +2 -29
- package/assets/templates/helpers/verify-contract.sh +1 -111
- package/assets/templates/helpers/verify-sprint.sh +3 -180
- package/assets/templates/helpers/workflow-contract.ts +5 -1
- package/assets/templates/plan.template.md +1 -1
- package/assets/templates/review.template.md +9 -34
- package/assets/workflow-contract.v1.json +10 -19
- package/docs/README.md +10 -10
- package/docs/architecture/CURRENT.md +178 -0
- package/docs/architecture/EVOLUTION.md +42 -0
- package/docs/architecture/current/README.md +4 -156
- package/docs/architecture/decisions/20260803-release-identity-binding-and-exit-policy.md +2 -2
- package/docs/architecture/decisions/20260823-immediate-controller-relay.md +53 -0
- package/docs/architecture/decisions/20260826-browser-runtime-v2.md +73 -0
- package/docs/architecture/history/README.md +7 -0
- package/docs/architecture/{chatgpt-handoff-facade.md → history/chatgpt-handoff-facade.md} +4 -0
- package/docs/architecture/{global-hook-runtime.md → history/global-hook-runtime.md} +7 -3
- package/docs/architecture/{ios-semantic-automation-provider-v2.md → history/ios-semantic-automation-provider-v2.md} +6 -2
- package/docs/architecture/history.md +3 -34
- package/docs/architecture/index.md +15 -98
- package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +1 -1
- package/docs/architecture/reviews/2026-08-22-semantic-harness-convergence-acceptance.md +39 -0
- package/docs/architecture/versions/1.6.md +38 -0
- package/docs/architecture/versions/1.7.md +41 -0
- package/docs/forge-chatgpt-browser-engine.md +2 -2
- package/docs/forge-chatgpt-mcp-setup.md +4 -28
- package/docs/forge-chatgpt-supervised-automation.md +3 -3
- package/docs/forge-github-issue-launcher.md +1 -1
- package/docs/forge-plugin-management.md +1 -1
- package/docs/forge-runtime-self-healing-loop.md +1 -1
- package/docs/operations/controller-browser-plugin.md +26 -6
- package/docs/operations/controller-performance-and-502.md +143 -0
- package/docs/operations/controller-reliability-runbook.md +1 -1
- package/docs/operations/features.md +2 -2
- package/docs/operations/github-repository.md +1 -1
- package/docs/operations/homebrew.md +2 -2
- package/docs/operations/mcp-tool-exposure.md +5 -3
- package/docs/wiki/Architecture.md +6 -6
- package/docs/wiki/Implementation.md +2 -2
- package/docs/wiki/Integrations.md +2 -2
- package/docs/wiki/Work-Lifecycle.md +1 -1
- package/package.json +33 -11
- package/scripts/activate-source-baseline.ts +17 -13
- package/scripts/architecture-event.ts +2 -820
- package/scripts/architecture-queue.sh +2 -645
- package/scripts/archive-architecture-request.sh +2 -242
- package/scripts/archive-workflow.sh +1 -291
- package/scripts/benchmark-background-check-overlap.ts +228 -0
- package/scripts/benchmark-mcp-host.sh +117 -0
- package/scripts/benchmark-mcp-transport.ts +170 -0
- package/scripts/benchmark-quality-harness.ts +146 -0
- package/scripts/benchmark-route-session-concurrency.ts +207 -6
- package/scripts/benchmark-thin-harness-mcp-e2e.ts +7 -1
- package/scripts/benchmark-typescript-navigation.ts +186 -0
- package/scripts/benchmark-work-efficiency.ts +334 -0
- package/scripts/capability-config.ts +2 -392
- package/scripts/capability-resolver.ts +2 -503
- package/scripts/capture-plan.sh +1 -332
- package/scripts/check-agent-tooling.sh +2 -1558
- package/scripts/check-architecture-sync.sh +2 -371
- package/scripts/check-brain-manifest.sh +2 -253
- package/scripts/check-context-files.sh +2 -47
- package/scripts/check-deploy-sql-order.sh +2 -115
- package/scripts/check-mcp-compatibility.ts +14 -0
- package/scripts/check-runtime-architecture.mjs +349 -79
- package/scripts/check-skill-version.ts +3 -191
- package/scripts/check-source-duplication.mjs +61 -0
- package/scripts/check-stable-baseline.ts +92 -0
- package/scripts/check-task-sync.sh +2 -63
- package/scripts/check-task-workflow.sh +2 -1000
- package/scripts/codex-handoff-resume.sh +2 -267
- package/scripts/context-contract-sync.sh +2 -514
- package/scripts/contract-run.ts +2 -463
- package/scripts/contract-worktree.sh +1 -722
- package/scripts/ensure-task-workflow.sh +1 -1225
- package/scripts/host-temporary-cloud-mcp-e2e.sh +265 -0
- package/scripts/inspect-project-state.ts +4 -279
- package/scripts/install-standalone-recovery.ts +24 -0
- package/scripts/lib/project-init-lib.sh +22 -46
- package/scripts/lib/run-forge-helper.sh +19 -0
- package/scripts/lib/run-forge-helper.ts +26 -0
- package/scripts/migrate-project-template.sh +1 -1189
- package/scripts/migrate-workflow-docs.ts +4 -412
- package/scripts/new-plan.sh +1 -180
- package/scripts/new-spec.sh +1 -47
- package/scripts/new-sprint.sh +1 -46
- package/scripts/plan-to-todo.sh +1 -796
- package/scripts/prepare-codex-handoff.sh +2 -201
- package/scripts/prepare-handoff.sh +1 -78
- package/scripts/refresh-current-status.sh +2 -467
- package/scripts/run-governed-gate.ts +5 -1
- package/scripts/select-agent-context-blocks.sh +2 -64
- package/scripts/ship-worktrees.sh +1 -558
- package/scripts/smoke-ios-physical-device.ts +4 -2
- package/scripts/smoke-runtime-recovery.ts +1 -35
- package/scripts/smoke-schedule-engine.ts +1 -20
- package/scripts/sprint-backlog.sh +1 -834
- package/scripts/stage-runtime-release.ts +2 -1
- package/scripts/summarize-failures.sh +2 -123
- package/scripts/switch-plan.sh +1 -150
- package/scripts/sync-brain-docs.sh +2 -379
- package/scripts/sync-generated-authority.mjs +175 -0
- package/scripts/verify-contract.sh +2 -703
- package/scripts/verify-forge-runtime.sh +0 -1
- package/scripts/verify-sprint.sh +1 -677
- package/scripts/workflow-contract.ts +4 -205
- package/scripts/workstream-sync.sh +2 -261
- package/src/cli/agent-jobs/executor-health.ts +1 -1
- package/src/cli/commands/init-hook.ts +3 -3
- package/src/cli/commands/mcp.ts +9 -5
- package/src/cli/commands/plugin.ts +12 -3
- package/src/cli/commands/recovery.ts +25 -3
- package/src/cli/commands/runtime.ts +55 -3
- package/src/cli/commands/security.ts +12 -1
- package/src/cli/commands/setup-profile.ts +31 -21
- package/src/cli/controller/check-runner.ts +113 -5
- package/src/cli/controller/composite-result.ts +1 -1
- package/src/cli/controller/context/focus.ts +51 -0
- package/src/cli/controller/context/instruction-context.ts +114 -0
- package/src/cli/controller/context/known-paths.ts +76 -0
- package/src/cli/controller/context/query-planning.ts +110 -0
- package/src/cli/controller/context/rule-resolution.ts +532 -0
- package/src/cli/controller/context/source-materializer.ts +278 -0
- package/src/cli/controller/context/structural.ts +16 -0
- package/src/cli/controller/context/types.ts +286 -0
- package/src/cli/controller/context-pack.ts +828 -487
- package/src/cli/controller/launch-agents.ts +77 -2
- package/src/cli/controller/operational-plan.ts +2 -2
- package/src/cli/controller/work-mode.ts +6 -4
- package/src/cli/editing/edit-session.ts +60 -7
- package/src/cli/github/contracts.ts +16 -0
- package/src/cli/github/github.ts +135 -16
- package/src/cli/github/session.ts +2 -1
- package/src/cli/installer/managed-entries.ts +15 -1
- package/src/cli/local-bridge/console-automations.ts +54 -78
- package/src/cli/local-bridge/console-view-models.ts +1 -35
- package/src/cli/local-bridge/facade-api.ts +37 -264
- package/src/cli/local-bridge/frontend/src/App.tsx +3 -3
- package/src/cli/local-bridge/frontend/src/features/automations/AutomationsView.tsx +23 -13
- package/src/cli/local-bridge/frontend/src/features/capabilities/CapabilitiesView.tsx +37 -4
- package/src/cli/local-bridge/frontend/src/features/overview/OverviewView.tsx +6 -4
- package/src/cli/local-bridge/frontend/src/lib/api.ts +1 -6
- package/src/cli/local-bridge/frontend/src/lib/routes.ts +1 -1
- package/src/cli/local-bridge/frontend/src/shell/Sidebar.tsx +2 -2
- package/src/cli/local-bridge/frontend/src/styles.css +1 -1
- package/src/cli/local-bridge/frontend/src/types.ts +5 -8
- package/src/cli/local-bridge/job-store.ts +1 -490
- package/src/cli/local-bridge/server.ts +2 -515
- package/src/cli/local-bridge/ui-dist/app.css +1 -1
- package/src/cli/local-bridge/ui-dist/app.js +3703 -3757
- package/src/cli/mcp/auth.ts +5 -5
- package/src/cli/mcp/instructions.ts +3 -2
- package/src/cli/mcp/legacy-tool-service.ts +10 -222
- package/src/cli/mcp/multi-repository.ts +16 -5
- package/src/cli/mcp/oauth.ts +175 -26
- package/src/cli/mcp/post-finalize-work-attribution.ts +150 -0
- package/src/cli/mcp/repository-tools.ts +282 -146
- package/src/cli/mcp/server.ts +586 -116
- package/src/cli/mcp/tool-capability-cache.ts +1 -1
- package/src/cli/mcp/tool-contract.ts +14 -0
- package/src/cli/mcp/toolset-selection.ts +1 -1
- package/src/cli/mcp/toolset.ts +44 -7
- package/src/cli/mcp/transports/http.ts +124 -14
- package/src/cli/mcp/transports/session-registry.ts +43 -5
- package/src/cli/mcp/types.ts +3 -3
- package/src/cli/repositories/command-classifier.ts +103 -21
- package/src/cli/repositories/command-executor.ts +78 -526
- package/src/cli/repositories/command-process.ts +108 -0
- package/src/cli/repositories/command-scope.ts +99 -41
- package/src/cli/repositories/controller-home.ts +7 -8
- package/src/cli/repositories/git-command-executor.ts +2 -0
- package/src/cli/repositories/locks.ts +53 -16
- package/src/cli/repositories/repository-snapshot.ts +124 -0
- package/src/cli/repositories/selected-path-actions.ts +17 -9
- package/src/cli/repositories/structured-git.ts +118 -6
- package/src/cli/repository/inspector.ts +563 -65
- package/src/cli/repository/session-cache.ts +58 -17
- package/src/cli/v81-entry.ts +1 -4
- package/src/effects/fs-transaction.ts +85 -17
- package/src/runtime/context/codegraph-read-provider.ts +233 -2
- package/src/runtime/context/codegraph-sidecar.cjs +74 -40
- package/src/runtime/context/generic-lsp-provider.ts +355 -0
- package/src/runtime/context/lsp-client.ts +283 -0
- package/src/runtime/context/semantic-navigation-contract.ts +61 -0
- package/src/runtime/context/semantic-navigation.ts +223 -0
- package/src/runtime/context/swift-navigation.ts +565 -0
- package/src/runtime/context/typescript-navigation.ts +226 -0
- package/src/runtime/control-plane/dispatch-priority.ts +28 -0
- package/src/runtime/control-plane/execution/direct-edit-work-completion.ts +48 -16
- package/src/runtime/control-plane/execution/edit-validation-coordinator.ts +86 -24
- package/src/runtime/control-plane/execution/repository-work-attribution.ts +106 -0
- package/src/runtime/control-plane/execution/retained-work-resume.ts +203 -0
- package/src/runtime/control-plane/execution/session-store.ts +18 -0
- package/src/runtime/control-plane/execution/verification-evidence.ts +58 -3
- package/src/runtime/control-plane/execution/work-completion-authority.ts +58 -0
- package/src/runtime/control-plane/execution/work-evidence-policy.ts +200 -0
- package/src/runtime/control-plane/execution/work-execution-support.ts +386 -0
- package/src/runtime/control-plane/execution/work-finalization-service.ts +1913 -0
- package/src/runtime/control-plane/execution/work-handle-authority.ts +96 -0
- package/src/runtime/control-plane/execution/work-handle-store.ts +27 -2
- package/src/runtime/control-plane/execution/work-lifecycle-audit.ts +258 -0
- package/src/runtime/control-plane/execution/work-operation-service.ts +468 -0
- package/src/runtime/control-plane/execution/work-path-scope.ts +37 -0
- package/src/runtime/control-plane/execution/work-preparation-service.ts +479 -0
- package/src/runtime/control-plane/execution/work-process-evidence.ts +129 -0
- package/src/runtime/control-plane/execution/work-remote-delivery.ts +215 -0
- package/src/runtime/control-plane/execution/work-task-receipt.ts +22 -18
- package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +156 -11
- package/src/runtime/control-plane/execution/work-validation-reconciler.ts +22 -14
- package/src/runtime/control-plane/execution/work-verification-context.ts +56 -0
- package/src/runtime/control-plane/execution/work-verification-service.ts +388 -0
- package/src/runtime/control-plane/execution/work-verification-snapshot.ts +90 -9
- package/src/runtime/control-plane/facade/capability-registry.ts +129 -25
- package/src/runtime/control-plane/facade/controller-round-relay.ts +1118 -0
- package/src/runtime/control-plane/facade/controller-session-store.ts +302 -12
- package/src/runtime/control-plane/facade/goal-workloop-access.ts +17 -1
- package/src/runtime/control-plane/facade/goal-workloop.ts +928 -190
- package/src/runtime/control-plane/facade/index.ts +3 -0
- package/src/runtime/control-plane/facade/plan-contract-store.ts +275 -42
- package/src/runtime/control-plane/facade/repository-work-admission.ts +147 -0
- package/src/runtime/control-plane/facade/requirement-authority.ts +83 -0
- package/src/runtime/control-plane/facade/semantic-admission.ts +205 -0
- package/src/runtime/control-plane/facade/suggested-actions.ts +5 -1
- package/src/runtime/control-plane/facade/types.ts +90 -10
- package/src/runtime/control-plane/facade/work-contract-store.ts +157 -261
- package/src/runtime/control-plane/facade/work-state-machine.ts +250 -0
- package/src/runtime/control-plane/generated-cache-retention.ts +271 -0
- package/src/runtime/control-plane/global-scheduler/config.ts +44 -0
- package/src/runtime/control-plane/global-scheduler/dispatch-capacity.ts +80 -0
- package/src/runtime/control-plane/global-scheduler/durable-admission.ts +46 -0
- package/src/runtime/control-plane/global-scheduler/maintenance.ts +152 -0
- package/src/runtime/control-plane/global-scheduler/projection-refresh.ts +84 -0
- package/src/runtime/control-plane/global-scheduler/reconciliation.ts +3 -3
- package/src/runtime/control-plane/global-scheduler/scheduler.ts +278 -543
- package/src/runtime/control-plane/global-scheduler/source-scan.ts +59 -0
- package/src/runtime/control-plane/global-scheduler/state.ts +88 -0
- package/src/runtime/control-plane/global-scheduler/worker-attachment.ts +57 -0
- package/src/runtime/control-plane/global-scheduler/worker-exit-decision.ts +38 -0
- package/src/runtime/control-plane/global-scheduler/worker-exit-reconciler.ts +175 -0
- package/src/runtime/control-plane/global-scheduler/worker-launch.ts +111 -0
- package/src/runtime/control-plane/global-scheduler/worker-lifecycle-store.ts +64 -0
- package/src/runtime/control-plane/global-scheduler/worker-lifecycle.ts +150 -0
- package/src/runtime/control-plane/global-scheduler/worker-process.ts +119 -0
- package/src/runtime/control-plane/global-scheduler/worker-stderr.ts +60 -0
- package/src/runtime/control-plane/launcher/chatgpt-work-binding-store.ts +63 -0
- package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +365 -117
- package/src/runtime/control-plane/launcher/launch-reservation-store.ts +89 -0
- package/src/runtime/control-plane/launcher/thin-launcher.ts +244 -11
- package/src/runtime/control-plane/persistence/controller-home-migration.ts +487 -0
- package/src/runtime/control-plane/persistence/requirement-store.ts +0 -12
- package/src/runtime/control-plane/persistence/sqlite-store.ts +33 -2
- package/src/runtime/control-plane/release-retention.ts +62 -2
- package/src/runtime/control-plane/repo-actor/registry.ts +4 -2
- package/src/runtime/control-plane/routing/route-policy.ts +24 -21
- package/src/runtime/control-plane/routing/workspace-admission.ts +70 -0
- package/src/runtime/control-plane/runtime-status-client.ts +0 -2
- package/src/runtime/diagnostics/mcp-timing.ts +13 -0
- package/src/runtime/diagnostics/mcp-transport-benchmark.ts +129 -0
- package/src/runtime/diagnostics/performance.ts +24 -1
- package/src/runtime/evidence/event-ledger.ts +1 -1
- package/src/runtime/evidence/process-check-execution.ts +18 -0
- package/src/runtime/execution/jobs/child-reference.ts +6 -2
- package/src/runtime/execution/jobs/store.ts +58 -4
- package/src/runtime/execution/jobs/types.ts +1 -1
- package/src/runtime/execution/managed-workspace.ts +203 -1
- package/src/runtime/execution/process-runtime/check-facade.ts +51 -4
- package/src/runtime/execution/process-runtime/check-result.ts +88 -0
- package/src/runtime/execution/process-runtime/check-runner-sidecar.ts +7 -1
- package/src/runtime/execution/process-runtime/check-scheduling.ts +114 -0
- package/src/runtime/execution/process-runtime/command-facade.ts +367 -40
- package/src/runtime/execution/process-runtime/gc.ts +38 -6
- package/src/runtime/execution/process-runtime/lightweight-managed.ts +1201 -0
- package/src/runtime/execution/process-runtime/persisted-check.ts +51 -5
- package/src/runtime/execution/process-runtime/resource-claims.ts +10 -6
- package/src/runtime/execution/process-runtime/runtime.ts +53 -3
- package/src/runtime/execution/process-runtime/store.ts +1 -0
- package/src/runtime/execution/process-runtime/types.ts +4 -12
- package/src/runtime/execution/thin-harness/async-process.ts +12 -2
- package/src/runtime/execution/thin-harness/search-worker.ts +54 -5
- package/src/runtime/execution/work-activity.ts +21 -0
- package/src/runtime/execution/workers/executor.ts +18 -61
- package/src/runtime/gateway/mcp/execution-tools.ts +35 -1852
- package/src/runtime/gateway/mcp/legacy-ios-tool-adapter.ts +61 -0
- package/src/runtime/gateway/mcp/process-tools.ts +83 -37
- package/src/runtime/gateway/mcp/router.ts +186 -84
- package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +565 -0
- package/src/runtime/gateway/mcp/runtime-tools.ts +2378 -1947
- package/src/runtime/plugins/app-store-connect-adapter.ts +216 -47
- package/src/runtime/plugins/browser-adapter.ts +2662 -829
- package/src/runtime/plugins/browser-automation-service.ts +10 -0
- package/src/runtime/plugins/browser-handoff-host.ts +2 -25
- package/src/runtime/plugins/browser-handoff.ts +110 -5
- package/src/runtime/plugins/browser-macos-bridge.ts +701 -63
- package/src/runtime/plugins/browser-manifest-surface.ts +603 -0
- package/src/runtime/plugins/browser-native-session-service.ts +222 -0
- package/src/runtime/plugins/browser-node-bridge.ts +6 -34
- package/src/runtime/plugins/browser-provider-registry.ts +109 -0
- package/src/runtime/plugins/browser-runtime-contract.ts +154 -0
- package/src/runtime/plugins/browser-runtime.ts +193 -0
- package/src/runtime/plugins/browser-session-authority.ts +332 -0
- package/src/runtime/plugins/browser-session-store.ts +121 -0
- package/src/runtime/plugins/browser-session-types.ts +98 -0
- package/src/runtime/plugins/capability-authorization-grants.ts +354 -0
- package/src/runtime/plugins/desktop-operator-registration.ts +10 -4
- package/src/runtime/plugins/external-adapter.ts +358 -3
- package/src/runtime/plugins/external-unix-socket.ts +7 -1
- package/src/runtime/plugins/first-party-registry.ts +2 -0
- package/src/runtime/plugins/github-adapter.ts +125 -0
- package/src/runtime/plugins/ios/agent-device-provider.ts +1 -1
- package/src/runtime/plugins/ios/agent-device-typed-provider.ts +52 -9
- package/src/runtime/plugins/ios/remote-xpc-hid.ts +1171 -0
- package/src/runtime/plugins/ios-adapter.ts +106 -16
- package/src/runtime/plugins/ios-agent-device.ts +234 -64
- package/src/runtime/plugins/ios-physical-device.ts +1143 -392
- package/src/runtime/plugins/lightweight-action.ts +153 -0
- package/src/runtime/plugins/local-system-adapter.ts +123 -6
- package/src/runtime/plugins/macos-capability-broker.ts +47 -9
- package/src/runtime/plugins/plugin-action-sidecar.ts +59 -0
- package/src/runtime/plugins/plugin-management-adapter.ts +49 -8
- package/src/runtime/plugins/resend-adapter.ts +1 -1
- package/src/runtime/plugins/store.ts +330 -33
- package/src/runtime/plugins/types.ts +38 -3
- package/src/runtime/plugins/xiaohongshu-publish.ts +515 -0
- package/src/runtime/projections/git-status-sampler.ts +16 -16
- package/src/runtime/projections/materialized-view.ts +28 -10
- package/src/runtime/recovery/maintenance-executor.ts +426 -25
- package/src/runtime/root/mcp-transport.ts +180 -10
- package/src/runtime/root/package-connector-service.ts +230 -10
- package/src/runtime/root/package-runtime-release.ts +143 -17
- package/src/runtime/root/package-runtime-service.ts +461 -22
- package/src/runtime/root/release-manifest.ts +144 -18
- package/src/runtime/root/release-materialize.ts +125 -32
- package/src/runtime/root/release-store.ts +48 -11
- package/src/runtime/root/runtime.ts +23 -5
- package/src/runtime/root/service.ts +100 -29
- package/src/runtime/root/types.ts +12 -0
- package/src/runtime/safe-tooling/ios-development.ts +321 -63
- package/src/runtime/shared/trusted-executable.ts +62 -0
- package/src/runtime/shared/trusted-node-executable.ts +48 -0
- package/src/runtime/standalone-recovery/core.ts +1079 -240
- package/src/runtime/standalone-recovery/entry.ts +100 -24
- package/src/runtime/standalone-recovery/installer.ts +20 -4
- package/src/runtime/standalone-recovery/service-labels.ts +2 -0
- package/src/runtime/standalone-recovery/watchdog-heartbeat.ts +160 -0
- package/src/runtime/workflow/schedules/browser-probe.ts +81 -3
- package/src/runtime/workflow/schedules/engine.ts +364 -202
- package/src/runtime/workflow/schedules/github-issue-watch.ts +158 -0
- package/src/runtime/workflow/schedules/settlement.ts +64 -17
- package/src/runtime/workflow/schedules/store.ts +209 -19
- package/src/runtime/workflow/schedules/types.ts +2 -4
- package/src/runtime/workflow/schedules/work-continuation.ts +26 -13
- package/src/testing/test-governance.ts +8 -40
- package/assets/reference-configs/heartbeat-triage.md +0 -57
- package/assets/templates/helpers/harness-trace-grade.sh +0 -150
- package/assets/templates/helpers/heartbeat-triage.sh +0 -361
- package/assets/templates/helpers/maintenance-triage.sh +0 -167
- package/docs/architecture/current/approved-target-architecture.zh-CN.md +0 -1501
- package/docs/architecture/current/architecture-invariants.md +0 -331
- package/docs/architecture/current/automation-and-schedule-engine.md +0 -459
- package/docs/architecture/current/codegraph-context-provider.md +0 -238
- package/docs/architecture/current/control-plane-authority-inventory.md +0 -94
- package/docs/architecture/current/controller-repository-decoupling.md +0 -148
- package/docs/architecture/current/dispatch-and-agent-strategy.md +0 -373
- package/docs/architecture/current/entity-model.md +0 -612
- package/docs/architecture/current/external-plugin-governance.md +0 -396
- package/docs/architecture/current/failure-recovery.md +0 -447
- package/docs/architecture/current/governance.md +0 -213
- package/docs/architecture/current/human-interaction-plane.md +0 -87
- package/docs/architecture/current/implementation-status.md +0 -128
- package/docs/architecture/current/job-and-run-lifecycle.md +0 -413
- package/docs/architecture/current/legacy-governance.md +0 -58
- package/docs/architecture/current/local-workspace-target-grants.md +0 -70
- package/docs/architecture/current/migration-roadmap.md +0 -105
- package/docs/architecture/current/multi-repository-execution.md +0 -371
- package/docs/architecture/current/operations-runbook.md +0 -45
- package/docs/architecture/current/peer-supercontroller-runtime.md +0 -32
- package/docs/architecture/current/personal-assistant-plugin-baseline.md +0 -206
- package/docs/architecture/current/repository-access-modes.md +0 -102
- package/docs/architecture/current/route-policy.md +0 -15
- package/docs/architecture/current/route-session-concurrency.md +0 -85
- package/docs/architecture/current/runtime-architecture-simplification.md +0 -214
- package/docs/architecture/current/runtime-directory-map.md +0 -49
- package/docs/architecture/current/runtime-health-and-resource-lifecycle.md +0 -85
- package/docs/architecture/current/scheduler-and-resource-claims.md +0 -422
- package/docs/architecture/current/session-aware-execution-and-authorization.md +0 -77
- package/docs/architecture/current/system-overview.md +0 -169
- package/docs/architecture/current/target-requirements-traceability.md +0 -202
- package/docs/architecture/current/thin-harness-governance-policy.md +0 -32
- package/docs/architecture/current/thin-harness-v1.md +0 -321
- package/docs/architecture/current/verification-and-release-gates.md +0 -391
- package/docs/forge-autonomous-goal-loop.md +0 -9
- package/docs/forge-provider-configuration.md +0 -7
- package/docs/operations/chatgpt-personal-assistant-runtime.md +0 -93
- package/docs/operations/gmail-assistant-routines.md +0 -186
- package/docs/operations/personal-assistant-operator-guide.md +0 -61
- package/docs/operations/provider-configuration.md +0 -31
- package/scripts/harness-trace-grade.sh +0 -150
- package/scripts/heartbeat-triage.sh +0 -361
- package/scripts/maintenance-triage.sh +0 -167
- package/src/cli/local-bridge/frontend/src/features/settings/SettingsView.tsx +0 -2
- package/src/cli/repositories/goal-registry.ts +0 -239
- package/src/runtime/assistant/action-proposals.ts +0 -279
- package/src/runtime/assistant/intent.ts +0 -395
- package/src/runtime/assistant/model-provider.ts +0 -460
- package/src/runtime/assistant/openapi.ts +0 -223
- package/src/runtime/assistant/policy.ts +0 -89
- package/src/runtime/assistant/readiness.ts +0 -258
- package/src/runtime/assistant/routine-runtime.ts +0 -477
- package/src/runtime/assistant/schedule-binding.ts +0 -171
- package/src/runtime/assistant/standing-grants.ts +0 -375
- package/src/runtime/assistant/store.ts +0 -243
- package/src/runtime/assistant/types.ts +0 -135
- package/src/runtime/control-plane/goal-loop/config-facade.ts +0 -700
- package/src/runtime/control-plane/goal-loop/config-store.ts +0 -570
- package/src/runtime/control-plane/goal-loop/config-types.ts +0 -213
- package/src/runtime/control-plane/goal-loop/executor-router.ts +0 -109
- package/src/runtime/control-plane/goal-loop/failure-taxonomy.ts +0 -149
- package/src/runtime/control-plane/goal-loop/goal-contract-store.ts +0 -304
- package/src/runtime/control-plane/goal-loop/goal-loop-engine.ts +0 -1001
- package/src/runtime/control-plane/goal-loop/handoff-packet.ts +0 -160
- package/src/runtime/control-plane/goal-loop/index.ts +0 -13
- package/src/runtime/control-plane/goal-loop/local-tools.ts +0 -290
- package/src/runtime/control-plane/goal-loop/policy-gates.ts +0 -203
- package/src/runtime/control-plane/goal-loop/provider-dispatch.ts +0 -93
- package/src/runtime/control-plane/goal-loop/provider-registry.ts +0 -545
- package/src/runtime/control-plane/goal-loop/types.ts +0 -378
- package/src/runtime/control-plane/startup-recovery.ts +0 -198
- package/src/runtime/gateway/mcp/resource-policy.ts +0 -186
- package/src/runtime/personal-assistant/gmail-triage-manager.ts +0 -231
- package/src/runtime/personal-assistant/reporting-runtime.ts +0 -344
- package/src/runtime/personal-assistant/triage-runtime.ts +0 -748
- package/src/runtime/workflow/findings/store.ts +0 -204
- package/src/runtime/workflow/findings/types.ts +0 -58
- package/src/runtime/workflow/portfolio/engine.ts +0 -20
- package/src/runtime/workflow/portfolio/store.ts +0 -185
- package/src/runtime/workflow/portfolio/types.ts +0 -29
|
@@ -1,19 +1,26 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* repository_command_execute →
|
|
2
|
+
* repository_command_execute → three explicit execution lanes.
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
*
|
|
6
|
-
*
|
|
4
|
+
* - Ephemeral Direct: bounded ordinary reads and commands that finish inside
|
|
5
|
+
* the interactive budget.
|
|
6
|
+
* - Lightweight Managed: the same local command remains alive in memory and
|
|
7
|
+
* returns a bounded handle, without SQLite, Lease, or recovery membership.
|
|
8
|
+
* - Durable: explicit Work/external/release boundaries only.
|
|
7
9
|
*/
|
|
8
10
|
|
|
9
11
|
import type { RepositoryRecord } from '../../../cli/repositories/types';
|
|
10
|
-
import {
|
|
12
|
+
import { repositoryGitStatus } from '../../../cli/repositories/structured-git';
|
|
13
|
+
import { classifyRepositoryCommand, fixedShellWrapperCommand, shellSegments, shellWordsPreservingQuotes } from '../../../cli/repositories/command-classifier';
|
|
11
14
|
import {
|
|
12
15
|
executeRepositoryCommandAsync,
|
|
13
16
|
executeRepositoryReadOnlyCommandDirect,
|
|
14
17
|
type RepositoryCommandExecution,
|
|
15
18
|
} from '../../../cli/repositories/command-executor';
|
|
16
19
|
import { normalizeRepositoryCommand } from '../../../cli/repositories/command-normalization';
|
|
20
|
+
import {
|
|
21
|
+
assertRepositoryCommandNoPluginExecutionBypass,
|
|
22
|
+
assertRepositoryCommandStableHostIdentity,
|
|
23
|
+
} from '../../../cli/repositories/command-scope';
|
|
17
24
|
import { claimsForRepositoryCommand, scopeResourceClaims, toProcessClaims } from './resource-claims';
|
|
18
25
|
import {
|
|
19
26
|
spawnManagedProcess,
|
|
@@ -27,6 +34,15 @@ import { DEFAULT_INTERACTIVE_WAIT_MS } from './types';
|
|
|
27
34
|
import { durationAwareInteractiveWaitMs } from './interactive-admission';
|
|
28
35
|
import { isFocusedCheckCommand } from '../thin-harness/execution-router';
|
|
29
36
|
import { assertExecutionIdentity, type ResolvedExecutionIdentity } from '../../control-plane/execution/execution-identity';
|
|
37
|
+
import { readWorkHandle, transitionWorkHandle } from '../../control-plane/execution/work-handle-store';
|
|
38
|
+
import {
|
|
39
|
+
cancelLightweightProcess,
|
|
40
|
+
getLightweightProcessHandle,
|
|
41
|
+
isLightweightProcessId,
|
|
42
|
+
readLightweightProcessLogs,
|
|
43
|
+
startLightweightRepositoryCommand,
|
|
44
|
+
waitForLightweightProcess,
|
|
45
|
+
} from './lightweight-managed';
|
|
30
46
|
|
|
31
47
|
export type RepositoryCommandRoute =
|
|
32
48
|
| 'process_direct'
|
|
@@ -76,6 +92,19 @@ export interface RepositoryCommandProcessResult {
|
|
|
76
92
|
projectionUpdateCount: number;
|
|
77
93
|
};
|
|
78
94
|
suggestedOperation?: string;
|
|
95
|
+
externalEffect?: {
|
|
96
|
+
outcome: 'not_started' | 'outcome_unknown';
|
|
97
|
+
replayPolicy: 'never_auto_retry';
|
|
98
|
+
reconciliation: string;
|
|
99
|
+
};
|
|
100
|
+
executionMetrics?: {
|
|
101
|
+
lane: 'ephemeral_direct' | 'lightweight_managed' | 'durable_process' | 'durable_external';
|
|
102
|
+
preSpawnHarnessMs?: number;
|
|
103
|
+
childDurationMs?: number;
|
|
104
|
+
interactiveReturnMs?: number;
|
|
105
|
+
durableWrites: number;
|
|
106
|
+
leaseOperations: number;
|
|
107
|
+
};
|
|
79
108
|
}
|
|
80
109
|
|
|
81
110
|
const emptyEffects = {
|
|
@@ -85,6 +114,165 @@ const emptyEffects = {
|
|
|
85
114
|
projectionUpdateCount: 0,
|
|
86
115
|
};
|
|
87
116
|
|
|
117
|
+
export type WorkHeadSettlementReason =
|
|
118
|
+
| 'settled'
|
|
119
|
+
| 'no_work'
|
|
120
|
+
| 'command_not_successful'
|
|
121
|
+
| 'work_handle_missing'
|
|
122
|
+
| 'identity_mismatch'
|
|
123
|
+
| 'terminal_handle'
|
|
124
|
+
| 'branch_changed'
|
|
125
|
+
| 'head_unavailable'
|
|
126
|
+
| 'head_unchanged'
|
|
127
|
+
| 'concurrent_lifecycle_write';
|
|
128
|
+
|
|
129
|
+
export interface WorkHeadSettlementResult {
|
|
130
|
+
settled: boolean;
|
|
131
|
+
reason: WorkHeadSettlementReason;
|
|
132
|
+
previousHead?: string;
|
|
133
|
+
currentHead?: string;
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* Settle the one legitimate post-command Work HEAD transition without weakening
|
|
138
|
+
* the pre-command execution identity fence. The command has already been
|
|
139
|
+
* admitted only if repo/checkout/branch/expectedHead matched the WorkHandle.
|
|
140
|
+
* After success, adopt the exact current HEAD only while the same checkout and
|
|
141
|
+
* branch still own the Work. CAS failure is a bounded no-op: finalization keeps
|
|
142
|
+
* failing closed instead of accepting a concurrent lifecycle writer.
|
|
143
|
+
*/
|
|
144
|
+
export function settleWorkHandleExpectedHeadAfterRepositoryCommand(input: {
|
|
145
|
+
controllerHome: string;
|
|
146
|
+
repository: RepositoryRecord;
|
|
147
|
+
executionIdentity: ResolvedExecutionIdentity;
|
|
148
|
+
workId?: string;
|
|
149
|
+
ok?: boolean;
|
|
150
|
+
cancelled?: boolean;
|
|
151
|
+
timedOut?: boolean;
|
|
152
|
+
}): WorkHeadSettlementResult {
|
|
153
|
+
const workId = input.workId?.trim();
|
|
154
|
+
if (!workId) return { settled: false, reason: 'no_work' };
|
|
155
|
+
if (input.ok !== true || input.cancelled === true || input.timedOut === true) {
|
|
156
|
+
return { settled: false, reason: 'command_not_successful' };
|
|
157
|
+
}
|
|
158
|
+
const handle = readWorkHandle(input.controllerHome, input.repository.repoId, workId);
|
|
159
|
+
if (!handle) return { settled: false, reason: 'work_handle_missing' };
|
|
160
|
+
if (
|
|
161
|
+
handle.repositoryId !== input.executionIdentity.repositoryId
|
|
162
|
+
|| handle.checkoutId !== input.executionIdentity.checkoutId
|
|
163
|
+
|| handle.workId !== input.executionIdentity.workId
|
|
164
|
+
) return { settled: false, reason: 'identity_mismatch' };
|
|
165
|
+
if (handle.state === 'merged' || handle.state === 'cleaned' || handle.state === 'failed_terminal_cleanup') {
|
|
166
|
+
return { settled: false, reason: 'terminal_handle' };
|
|
167
|
+
}
|
|
168
|
+
const status = repositoryGitStatus(input.repository);
|
|
169
|
+
if (!status.branch || status.branch !== handle.branch) {
|
|
170
|
+
return { settled: false, reason: 'branch_changed', previousHead: handle.expectedHead, currentHead: status.head ?? undefined };
|
|
171
|
+
}
|
|
172
|
+
const currentHead = status.head?.trim();
|
|
173
|
+
if (!currentHead) return { settled: false, reason: 'head_unavailable', previousHead: handle.expectedHead };
|
|
174
|
+
if (currentHead === handle.expectedHead) {
|
|
175
|
+
return { settled: false, reason: 'head_unchanged', previousHead: handle.expectedHead, currentHead };
|
|
176
|
+
}
|
|
177
|
+
try {
|
|
178
|
+
transitionWorkHandle(input.controllerHome, handle, handle.state, { expectedHead: currentHead });
|
|
179
|
+
return { settled: true, reason: 'settled', previousHead: handle.expectedHead, currentHead };
|
|
180
|
+
} catch (error) {
|
|
181
|
+
if (error instanceof Error && error.message.includes('CONTROL_PLANE_REVISION_CONFLICT')) {
|
|
182
|
+
return { settled: false, reason: 'concurrent_lifecycle_write', previousHead: handle.expectedHead, currentHead };
|
|
183
|
+
}
|
|
184
|
+
throw error;
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const STANDALONE_RECOVERY_LIFECYCLE_COMMANDS = new Set([
|
|
189
|
+
'restart-runtime',
|
|
190
|
+
'bootstrap-cutover',
|
|
191
|
+
'restart-connector',
|
|
192
|
+
'stage-and-activate-runtime',
|
|
193
|
+
'recover',
|
|
194
|
+
'rollback',
|
|
195
|
+
'restart',
|
|
196
|
+
'activate-runtime',
|
|
197
|
+
'install',
|
|
198
|
+
]);
|
|
199
|
+
|
|
200
|
+
function commandBasename(value: string): string {
|
|
201
|
+
return value.split(/[\\/]/).pop()?.toLowerCase() ?? '';
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
function recoveryLifecycleFromArgv(executable: string, args: readonly string[]): string | undefined {
|
|
205
|
+
const executableName = commandBasename(executable);
|
|
206
|
+
let recoveryArgs: readonly string[];
|
|
207
|
+
if (executableName === 'forge' || executableName === 'forge.mjs') {
|
|
208
|
+
recoveryArgs = args;
|
|
209
|
+
} else if (['bun', 'node', 'nodejs'].includes(executableName)) {
|
|
210
|
+
const scriptIndex = args.findIndex((arg) => {
|
|
211
|
+
const name = commandBasename(arg);
|
|
212
|
+
return name === 'forge' || name === 'forge.mjs';
|
|
213
|
+
});
|
|
214
|
+
if (scriptIndex < 0) return undefined;
|
|
215
|
+
recoveryArgs = args.slice(scriptIndex + 1);
|
|
216
|
+
} else {
|
|
217
|
+
return undefined;
|
|
218
|
+
}
|
|
219
|
+
if (recoveryArgs[0] !== 'recovery') return undefined;
|
|
220
|
+
const subcommand = recoveryArgs[1]?.toLowerCase();
|
|
221
|
+
return subcommand && STANDALONE_RECOVERY_LIFECYCLE_COMMANDS.has(subcommand) ? subcommand : undefined;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
function recoveryLifecycleFromShell(shellCommand: string): string | undefined {
|
|
225
|
+
const match = /^\s*(?:exec\s+)?(?:(?:\S*[\\/])?(?:bun|node|nodejs)\s+)?(?:\S*[\\/])?forge(?:\.mjs)?\s+recovery\s+([a-z-]+)\b/i.exec(shellCommand);
|
|
226
|
+
const subcommand = match?.[1]?.toLowerCase();
|
|
227
|
+
return subcommand && STANDALONE_RECOVERY_LIFECYCLE_COMMANDS.has(subcommand) ? subcommand : undefined;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function repositoryCommandRecoveryLifecycle(command: string | readonly string[]): string | undefined {
|
|
231
|
+
const normalized = normalizeRepositoryCommand(command);
|
|
232
|
+
if (normalized.kind === 'shell') return recoveryLifecycleFromShell(normalized.shellCommand ?? '');
|
|
233
|
+
const direct = recoveryLifecycleFromArgv(normalized.executable ?? '', normalized.args ?? []);
|
|
234
|
+
if (direct) return direct;
|
|
235
|
+
const argv = normalized.value as string[];
|
|
236
|
+
const wrapped = fixedShellWrapperCommand(argv);
|
|
237
|
+
return wrapped ? recoveryLifecycleFromShell(wrapped) : undefined;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function gitCommitWordsRequireExplicitPathScope(words: readonly string[]): boolean | undefined {
|
|
241
|
+
const executable = commandBasename(words[0] ?? '');
|
|
242
|
+
if (executable !== 'git') return undefined;
|
|
243
|
+
const args = words.slice(1);
|
|
244
|
+
let index = 0;
|
|
245
|
+
while (index < args.length && args[index]?.startsWith('-')) {
|
|
246
|
+
const option = args[index]!;
|
|
247
|
+
if (['-c', '-C', '--git-dir', '--work-tree', '--namespace'].includes(option)) index += 2;
|
|
248
|
+
else if (['--git-dir=', '--work-tree=', '--namespace='].some((prefix) => option.startsWith(prefix))) index += 1;
|
|
249
|
+
else index += 1;
|
|
250
|
+
}
|
|
251
|
+
if (args[index]?.toLowerCase() !== 'commit') return undefined;
|
|
252
|
+
const commitArgs = args.slice(index + 1);
|
|
253
|
+
const pathSeparator = commitArgs.indexOf('--');
|
|
254
|
+
const paths = pathSeparator >= 0 ? commitArgs.slice(pathSeparator + 1).filter(Boolean) : [];
|
|
255
|
+
const widensScope = commitArgs.some((arg) => ['-a', '--all', '-i', '--include', '--interactive', '-p', '--patch'].includes(arg));
|
|
256
|
+
return paths.length === 0 || widensScope;
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
function rawGitCommitRequiresExplicitPathScope(command: string | readonly string[]): boolean {
|
|
260
|
+
const normalized = normalizeRepositoryCommand(command);
|
|
261
|
+
if (normalized.kind === 'argv') {
|
|
262
|
+
const direct = gitCommitWordsRequireExplicitPathScope([
|
|
263
|
+
normalized.executable ?? '',
|
|
264
|
+
...(normalized.args ?? []),
|
|
265
|
+
]);
|
|
266
|
+
if (direct !== undefined) return direct;
|
|
267
|
+
const wrapped = fixedShellWrapperCommand(normalized.value as string[]);
|
|
268
|
+
if (!wrapped) return false;
|
|
269
|
+
return shellSegments(wrapped)
|
|
270
|
+
.some((segment) => gitCommitWordsRequireExplicitPathScope(shellWordsPreservingQuotes(segment)) === true);
|
|
271
|
+
}
|
|
272
|
+
return shellSegments(normalized.shellCommand ?? '')
|
|
273
|
+
.some((segment) => gitCommitWordsRequireExplicitPathScope(shellWordsPreservingQuotes(segment)) === true);
|
|
274
|
+
}
|
|
275
|
+
|
|
88
276
|
function toProcessCommand(command: string | readonly string[], cwd: string): ProcessCommandSpec {
|
|
89
277
|
const normalized = normalizeRepositoryCommand(command);
|
|
90
278
|
if (normalized.kind === 'argv') {
|
|
@@ -109,41 +297,64 @@ export function classifyRepositoryCommandRoute(
|
|
|
109
297
|
command: string | readonly string[],
|
|
110
298
|
options: {
|
|
111
299
|
forceDurable?: boolean;
|
|
300
|
+
workId?: string;
|
|
112
301
|
defaultBranch?: string;
|
|
113
302
|
timeoutMs?: number;
|
|
114
303
|
} = {},
|
|
115
304
|
): { route: RepositoryCommandRoute; reason: string } {
|
|
305
|
+
if (repositoryCommandRecoveryLifecycle(command)) {
|
|
306
|
+
return { route: 'reject', reason: 'standalone_recovery_lifecycle_required' };
|
|
307
|
+
}
|
|
308
|
+
if (rawGitCommitRequiresExplicitPathScope(command)) {
|
|
309
|
+
return { route: 'reject', reason: 'git_commit_requires_explicit_path_scope' };
|
|
310
|
+
}
|
|
116
311
|
if (options.forceDurable) {
|
|
117
312
|
return { route: 'durable', reason: 'force_durable_or_async' };
|
|
118
313
|
}
|
|
119
314
|
const classification = classifyRepositoryCommand(command, options.defaultBranch);
|
|
120
|
-
//
|
|
121
|
-
//
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
reason: `risk_${classification.risk}_via_process_runtime`,
|
|
126
|
-
};
|
|
315
|
+
// Work-bound effects must survive Controller/Runtime rotation with one durable
|
|
316
|
+
// Process identity. Read-only observations can remain ephemeral because they
|
|
317
|
+
// have no ambiguous side effect to reconcile after a restart.
|
|
318
|
+
if (options.workId?.trim() && classification.risk !== 'readonly') {
|
|
319
|
+
return { route: 'process_managed', reason: 'work_attributed_effectful_durable' };
|
|
127
320
|
}
|
|
128
|
-
// release / rollback style commands
|
|
129
321
|
const text = Array.isArray(command) ? command.join(' ') : String(command);
|
|
130
|
-
|
|
131
|
-
|
|
322
|
+
// Classification chooses scheduling/receipt granularity only; it does not
|
|
323
|
+
// decide whether a scoped repository command is allowed to run. Known remote
|
|
324
|
+
// or broadly effectful commands use managed Process Runtime so existing
|
|
325
|
+
// resource claims and receipts can serialize/reconcile them. Unknown commands
|
|
326
|
+
// still execute instead of requiring a new allow rule.
|
|
327
|
+
if (classification.risk === 'remote_write' || classification.risk === 'destructive') {
|
|
328
|
+
return { route: 'process_managed', reason: 'effectful_command_managed' };
|
|
132
329
|
}
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
330
|
+
const argv = Array.isArray(command) ? command : [];
|
|
331
|
+
const executable = argv[0]?.split(/[\\/]/).pop()?.toLowerCase();
|
|
332
|
+
const shellWrapped = Boolean(executable
|
|
333
|
+
&& ['bash', 'sh', 'zsh', 'fish', 'dash', 'cmd', 'powershell', 'pwsh'].includes(executable)
|
|
334
|
+
&& argv.slice(1).some((arg) => ['-c', '-lc', '/c', '-Command'].includes(arg)));
|
|
335
|
+
const wrappedShellCommand = shellWrapped ? fixedShellWrapperCommand(argv) : undefined;
|
|
336
|
+
const wrapsInlineInterpreter = Boolean(wrappedShellCommand
|
|
337
|
+
&& /^\s*(?:exec\s+)?(?:node|nodejs|bun|deno|ruby|perl|python\d*)\b[^\n]*(?:\s-c\b|\s-e\b|\s--eval\b)/i.test(wrappedShellCommand));
|
|
338
|
+
const wrapsObviousExternalIo = Boolean(wrappedShellCommand
|
|
339
|
+
&& /\b(?:curl|wget|ssh|scp|sftp|ftp|telnet|nc|ncat)\b|https?:\/\//i.test(wrappedShellCommand));
|
|
340
|
+
if (shellWrapped && classification.risk !== 'readonly') {
|
|
341
|
+
if (wrapsObviousExternalIo && !wrapsInlineInterpreter) {
|
|
342
|
+
return { route: 'process_managed', reason: 'shell_wrapper_requires_managed_boundary' };
|
|
137
343
|
}
|
|
344
|
+
return { route: 'process_direct', reason: 'lightweight_local_shell_wrapper' };
|
|
345
|
+
}
|
|
346
|
+
if (executable && ['node', 'nodejs', 'bun', 'deno', 'ruby', 'perl', 'python', 'python3'].includes(executable)
|
|
347
|
+
&& argv.slice(1).some((arg) => ['-c', '-e', '--eval'].includes(arg))) {
|
|
348
|
+
return { route: 'process_direct', reason: 'lightweight_local_inline_interpreter' };
|
|
349
|
+
}
|
|
350
|
+
if (classification.risk === 'readonly') {
|
|
138
351
|
return { route: 'process_direct', reason: 'readonly_fast_path' };
|
|
139
352
|
}
|
|
140
353
|
// workspace_write / build-test
|
|
141
354
|
if (isFocusedCheckCommand(command) || /\b(?:test|typecheck|lint|build|check)\b/i.test(text)) {
|
|
142
|
-
return { route: '
|
|
355
|
+
return { route: 'process_direct', reason: 'ephemeral_local_build_or_test' };
|
|
143
356
|
}
|
|
144
|
-
|
|
145
|
-
// a process lifecycle budget and must never select a different architecture.
|
|
146
|
-
return { route: 'process_managed', reason: 'local_workspace_mutation' };
|
|
357
|
+
return { route: 'process_direct', reason: 'ephemeral_local_workspace_mutation' };
|
|
147
358
|
}
|
|
148
359
|
|
|
149
360
|
/**
|
|
@@ -153,18 +364,37 @@ export function classifyRepositoryCommandRoute(
|
|
|
153
364
|
export async function executeRepositoryCommandViaProcessRuntime(
|
|
154
365
|
input: RepositoryCommandProcessInput,
|
|
155
366
|
): Promise<RepositoryCommandProcessResult> {
|
|
367
|
+
// Process Runtime intentionally supports some command forms that the legacy
|
|
368
|
+
// repository executor rejects (for example bounded typed eval). Keep that
|
|
369
|
+
// surface, but fail closed before routing any macOS TCC-sensitive host tool:
|
|
370
|
+
// a release-specific forge-runtime executable must never own those grants.
|
|
371
|
+
assertRepositoryCommandStableHostIdentity(input.command);
|
|
372
|
+
assertRepositoryCommandNoPluginExecutionBypass(input.command);
|
|
156
373
|
const decision = classifyRepositoryCommandRoute(input.command, {
|
|
157
374
|
forceDurable: input.forceDurable,
|
|
375
|
+
workId: input.workId,
|
|
158
376
|
defaultBranch: input.repository.defaultBranch,
|
|
159
377
|
timeoutMs: input.timeoutMs,
|
|
160
378
|
});
|
|
161
|
-
|
|
162
|
-
|
|
379
|
+
// Only explicit lifecycle/delivery rejection or a caller-requested durable
|
|
380
|
+
// mode stops here. Ordinary command effects, including remote writes, execute
|
|
381
|
+
// through Process Runtime; classification does not become an authorization gate.
|
|
382
|
+
if (decision.route === 'reject' || decision.route === 'durable') {
|
|
163
383
|
return {
|
|
164
384
|
route: decision.route,
|
|
165
385
|
reason: decision.reason,
|
|
166
386
|
durableSideEffects: emptyEffects,
|
|
167
|
-
suggestedOperation:
|
|
387
|
+
suggestedOperation: decision.reason === 'standalone_recovery_lifecycle_required'
|
|
388
|
+
? 'Use the standalone Forge Recovery connector for canonical Runtime/connector lifecycle operations.'
|
|
389
|
+
: 'repository_command_execute via Durable Work / Local Job',
|
|
390
|
+
externalEffect: decision.route === 'durable'
|
|
391
|
+
? {
|
|
392
|
+
outcome: 'not_started',
|
|
393
|
+
replayPolicy: 'never_auto_retry',
|
|
394
|
+
reconciliation: 'If a separate external controller later loses the result after dispatch, report outcome_unknown and inspect remote state before any retry.',
|
|
395
|
+
}
|
|
396
|
+
: undefined,
|
|
397
|
+
executionMetrics: { lane: 'durable_external', durableWrites: 0, leaseOperations: 0 },
|
|
168
398
|
};
|
|
169
399
|
}
|
|
170
400
|
|
|
@@ -191,12 +421,12 @@ export async function executeRepositoryCommandViaProcessRuntime(
|
|
|
191
421
|
};
|
|
192
422
|
}
|
|
193
423
|
|
|
194
|
-
//
|
|
195
|
-
//
|
|
196
|
-
// non-persistent
|
|
197
|
-
//
|
|
424
|
+
// Ordinary local commands own no durable Process/Lease state. Validate the
|
|
425
|
+
// immutable repository identity first, then execute through the authorized
|
|
426
|
+
// non-persistent repository executor. Runtime/release durability is not a
|
|
427
|
+
// command-execution tax.
|
|
198
428
|
const canonicalCommand = normalizeRepositoryCommand(input.command);
|
|
199
|
-
if (decision.route === 'process_direct'
|
|
429
|
+
if (decision.route === 'process_direct') {
|
|
200
430
|
assertExecutionIdentity({
|
|
201
431
|
controllerHome: input.controllerHome,
|
|
202
432
|
identity: executionIdentity,
|
|
@@ -204,14 +434,73 @@ export async function executeRepositoryCommandViaProcessRuntime(
|
|
|
204
434
|
requestedRepoId: input.repository.repoId,
|
|
205
435
|
requestedCheckoutId: input.repository.activeCheckoutId,
|
|
206
436
|
});
|
|
207
|
-
const
|
|
437
|
+
const directInput = {
|
|
208
438
|
command: input.command,
|
|
209
439
|
cwd: input.cwd,
|
|
210
|
-
timeoutMs:
|
|
440
|
+
timeoutMs: input.timeoutMs,
|
|
211
441
|
maxOutputBytes: input.maxOutputBytes,
|
|
212
442
|
signal: input.signal,
|
|
213
443
|
allowNonGitWorkspace: executionIdentity.authority === 'ephemeral_workspace',
|
|
214
|
-
|
|
444
|
+
allowOpaqueLocalScript: decision.reason === 'lightweight_local_shell_wrapper'
|
|
445
|
+
|| decision.reason === 'lightweight_local_inline_interpreter',
|
|
446
|
+
};
|
|
447
|
+
const readonly = canonicalCommand.kind === 'argv'
|
|
448
|
+
&& classifyRepositoryCommand(input.command, input.repository.defaultBranch).risk === 'readonly';
|
|
449
|
+
if (!readonly) {
|
|
450
|
+
const deferLongPreparation = decision.reason === 'ephemeral_local_build_or_test';
|
|
451
|
+
const interactiveWaitMs = deferLongPreparation
|
|
452
|
+
? 0
|
|
453
|
+
: durationAwareInteractiveWaitMs(
|
|
454
|
+
input.command,
|
|
455
|
+
input.returnHandleImmediately ? 0 : input.interactiveWaitMs,
|
|
456
|
+
Math.min(DEFAULT_INTERACTIVE_WAIT_MS, 250),
|
|
457
|
+
);
|
|
458
|
+
const timeoutMs = Math.max(
|
|
459
|
+
interactiveWaitMs + 1,
|
|
460
|
+
Math.min(input.timeoutMs ?? 15 * 60_000, 24 * 60 * 60_000),
|
|
461
|
+
);
|
|
462
|
+
const lightweight = await startLightweightRepositoryCommand({
|
|
463
|
+
controllerHome: input.controllerHome,
|
|
464
|
+
repository: input.repository,
|
|
465
|
+
execution: directInput,
|
|
466
|
+
interactiveWaitMs,
|
|
467
|
+
timeoutMs,
|
|
468
|
+
maxOutputBytes: input.maxOutputBytes,
|
|
469
|
+
workId: input.workId,
|
|
470
|
+
commandId: input.commandId ?? input.requestId,
|
|
471
|
+
deferStart: deferLongPreparation,
|
|
472
|
+
reuseActiveEquivalent: deferLongPreparation,
|
|
473
|
+
onCompleted: (result) => {
|
|
474
|
+
// A terminal child exit is sufficient to close the Process, but not to
|
|
475
|
+
// advance Work mutation authority when post-execution repository
|
|
476
|
+
// evidence is unavailable. Finalization must keep failing closed until
|
|
477
|
+
// a later authoritative repository inspection proves the new HEAD.
|
|
478
|
+
if (result.evidenceError) return;
|
|
479
|
+
settleWorkHandleExpectedHeadAfterRepositoryCommand({
|
|
480
|
+
controllerHome: input.controllerHome,
|
|
481
|
+
repository: input.repository,
|
|
482
|
+
executionIdentity,
|
|
483
|
+
workId: input.workId,
|
|
484
|
+
ok: result.ok,
|
|
485
|
+
cancelled: result.cancelled,
|
|
486
|
+
timedOut: result.timedOut,
|
|
487
|
+
});
|
|
488
|
+
},
|
|
489
|
+
});
|
|
490
|
+
const handle = lightweight.handle;
|
|
491
|
+
return {
|
|
492
|
+
route: handle.completed ? 'process_direct' : 'process_managed',
|
|
493
|
+
reason: handle.completed ? decision.reason : 'interactive_budget_exceeded_lightweight_handle',
|
|
494
|
+
process: handle,
|
|
495
|
+
ok: handle.ok,
|
|
496
|
+
exitCode: handle.exitCode,
|
|
497
|
+
stdout: handle.stdout,
|
|
498
|
+
stderr: handle.stderr,
|
|
499
|
+
durableSideEffects: emptyEffects,
|
|
500
|
+
executionMetrics: lightweight.metrics,
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
const direct = await executeRepositoryReadOnlyCommandDirect(input.repository, directInput);
|
|
215
504
|
return {
|
|
216
505
|
route: 'process_direct',
|
|
217
506
|
reason: decision.reason,
|
|
@@ -224,6 +513,7 @@ export async function executeRepositoryCommandViaProcessRuntime(
|
|
|
224
513
|
authorizationDecision: direct.authorizationDecision,
|
|
225
514
|
approvalRequestId: direct.approvalRequestId,
|
|
226
515
|
durableSideEffects: emptyEffects,
|
|
516
|
+
executionMetrics: { lane: 'ephemeral_direct', durableWrites: 0, leaseOperations: 0 },
|
|
227
517
|
};
|
|
228
518
|
}
|
|
229
519
|
|
|
@@ -313,12 +603,49 @@ export async function executeRepositoryCommandViaProcessRuntime(
|
|
|
313
603
|
stdout: handle.stdout,
|
|
314
604
|
stderr: handle.stderr,
|
|
315
605
|
durableSideEffects: handle.durableSideEffects,
|
|
606
|
+
executionMetrics: {
|
|
607
|
+
lane: 'durable_process',
|
|
608
|
+
interactiveReturnMs: interactiveWaitMs,
|
|
609
|
+
durableWrites: 1,
|
|
610
|
+
leaseOperations: claims.length,
|
|
611
|
+
},
|
|
316
612
|
};
|
|
317
613
|
}
|
|
318
614
|
|
|
319
|
-
export {
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
615
|
+
export function getRepositoryCommandProcess(controllerHome: string, repoId: string, processId: string): ProcessHandle | undefined {
|
|
616
|
+
return isLightweightProcessId(processId)
|
|
617
|
+
? getLightweightProcessHandle(controllerHome, repoId, processId)
|
|
618
|
+
: getProcessHandle(controllerHome, repoId, processId);
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
export function waitRepositoryCommandProcess(
|
|
622
|
+
controllerHome: string,
|
|
623
|
+
repoId: string,
|
|
624
|
+
processId: string,
|
|
625
|
+
options?: Parameters<typeof waitForProcess>[3],
|
|
626
|
+
): Promise<ProcessHandle> {
|
|
627
|
+
return isLightweightProcessId(processId)
|
|
628
|
+
? waitForLightweightProcess(controllerHome, repoId, processId, options)
|
|
629
|
+
: waitForProcess(controllerHome, repoId, processId, options);
|
|
630
|
+
}
|
|
631
|
+
|
|
632
|
+
export function cancelRepositoryCommandProcess(
|
|
633
|
+
controllerHome: string,
|
|
634
|
+
repoId: string,
|
|
635
|
+
processId: string,
|
|
636
|
+
): Promise<ProcessHandle> {
|
|
637
|
+
return isLightweightProcessId(processId)
|
|
638
|
+
? cancelLightweightProcess(controllerHome, repoId, processId)
|
|
639
|
+
: cancelProcess(controllerHome, repoId, processId);
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
export function readRepositoryCommandProcessLogs(
|
|
643
|
+
controllerHome: string,
|
|
644
|
+
repoId: string,
|
|
645
|
+
processId: string,
|
|
646
|
+
maxBytes?: number,
|
|
647
|
+
) {
|
|
648
|
+
return isLightweightProcessId(processId)
|
|
649
|
+
? readLightweightProcessLogs(controllerHome, repoId, processId, maxBytes)
|
|
650
|
+
: readProcessLogs(controllerHome, repoId, processId, maxBytes);
|
|
651
|
+
}
|
|
@@ -9,8 +9,9 @@
|
|
|
9
9
|
import { existsSync, readFileSync, readdirSync, unlinkSync, statSync } from 'fs';
|
|
10
10
|
import { join } from 'path';
|
|
11
11
|
import { ensureRepositoryControllerLayout, repositoryControllerRoot } from '../../../cli/repositories/controller-home';
|
|
12
|
-
import { listActiveProcessIds } from './store';
|
|
13
|
-
import
|
|
12
|
+
import { getProcessRecord, listActiveProcessIds } from './store';
|
|
13
|
+
import { reconcileStaleManagedProcessForMaintenance } from './runtime';
|
|
14
|
+
import { isManagedProcessActive, type ProcessRuntimeStatus } from './types';
|
|
14
15
|
import { assertRuntimeMayWrite } from '../../root/write-fence';
|
|
15
16
|
import { isProcessAlive } from '../../shared/process-tree';
|
|
16
17
|
|
|
@@ -21,6 +22,7 @@ const TERMINAL: ReadonlySet<ProcessRuntimeStatus> = new Set([
|
|
|
21
22
|
'cancelled',
|
|
22
23
|
'orphaned',
|
|
23
24
|
'completed_unknown',
|
|
25
|
+
'unknown',
|
|
24
26
|
]);
|
|
25
27
|
|
|
26
28
|
export interface ProcessGcOptions {
|
|
@@ -32,6 +34,10 @@ export interface ProcessGcOptions {
|
|
|
32
34
|
maxTerminalRecords?: number;
|
|
33
35
|
/** Also delete associated log/receipt files. */
|
|
34
36
|
deleteLogs?: boolean;
|
|
37
|
+
/** Minimum age before an active record is eligible for evidence-based stale reconciliation (default 5m). */
|
|
38
|
+
staleActiveMinAgeMs?: number;
|
|
39
|
+
/** Max stale active records to reconcile in one selected-repository GC pass (default 100). */
|
|
40
|
+
maxStaleReconciliations?: number;
|
|
35
41
|
}
|
|
36
42
|
|
|
37
43
|
export interface ProcessGcResult {
|
|
@@ -39,6 +45,7 @@ export interface ProcessGcResult {
|
|
|
39
45
|
removedRecords: number;
|
|
40
46
|
removedLogs: number;
|
|
41
47
|
skippedActive: number;
|
|
48
|
+
reconciledStaleActive: number;
|
|
42
49
|
/** Legacy/corrupt records without enough terminal proof are preserved. */
|
|
43
50
|
skippedInvalid?: number;
|
|
44
51
|
error?: string;
|
|
@@ -125,28 +132,52 @@ export function gcTerminalProcesses(options: ProcessGcOptions): ProcessGcResult
|
|
|
125
132
|
removedRecords: 0,
|
|
126
133
|
removedLogs: 0,
|
|
127
134
|
skippedActive: 0,
|
|
135
|
+
reconciledStaleActive: 0,
|
|
128
136
|
error: `writer_fenced:${fence.reason ?? 'denied'}`,
|
|
129
137
|
};
|
|
130
138
|
}
|
|
131
139
|
|
|
132
140
|
const maxAgeMs = options.maxAgeMs ?? 7 * 24 * 60 * 60_000;
|
|
133
141
|
const maxTerminal = options.maxTerminalRecords ?? 500;
|
|
142
|
+
const staleActiveMinAgeMs = Math.max(1_000, options.staleActiveMinAgeMs ?? 5 * 60_000);
|
|
143
|
+
const maxStaleReconciliations = Math.max(0, options.maxStaleReconciliations ?? 100);
|
|
134
144
|
const root = processesDir(options.controllerHome, options.repoId);
|
|
135
145
|
if (!existsSync(root)) {
|
|
136
|
-
return { ok: true, removedRecords: 0, removedLogs: 0, skippedActive: 0 };
|
|
146
|
+
return { ok: true, removedRecords: 0, removedLogs: 0, skippedActive: 0, reconciledStaleActive: 0 };
|
|
137
147
|
}
|
|
138
148
|
|
|
139
149
|
const active = new Set(listActiveProcessIds(options.controllerHome, options.repoId));
|
|
140
150
|
const terminal: Array<{ processId: string; finishedAt: number; path: string; reusableCheckEvidence: boolean }> = [];
|
|
141
151
|
let skippedActive = 0;
|
|
142
152
|
let skippedInvalid = 0;
|
|
153
|
+
let reconciledStaleActive = 0;
|
|
154
|
+
const nowMs = Date.now();
|
|
143
155
|
|
|
144
156
|
for (const name of readdirSync(root)) {
|
|
145
157
|
if (!name.endsWith('.json') || name === 'active-index.json') continue;
|
|
146
158
|
const processId = name.slice(0, -'.json'.length);
|
|
147
159
|
if (active.has(processId)) {
|
|
148
|
-
|
|
149
|
-
|
|
160
|
+
let record = getProcessRecord(options.controllerHome, options.repoId, processId);
|
|
161
|
+
if (!record) {
|
|
162
|
+
skippedActive += 1;
|
|
163
|
+
continue;
|
|
164
|
+
}
|
|
165
|
+
if (isManagedProcessActive(record) && reconciledStaleActive < maxStaleReconciliations) {
|
|
166
|
+
const reconciled = reconcileStaleManagedProcessForMaintenance(
|
|
167
|
+
options.controllerHome,
|
|
168
|
+
options.repoId,
|
|
169
|
+
processId,
|
|
170
|
+
{ nowMs, minAgeMs: staleActiveMinAgeMs },
|
|
171
|
+
);
|
|
172
|
+
if (reconciled) {
|
|
173
|
+
if (isManagedProcessActive(record) && !isManagedProcessActive(reconciled)) reconciledStaleActive += 1;
|
|
174
|
+
record = reconciled;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
if (isManagedProcessActive(record)) {
|
|
178
|
+
skippedActive += 1;
|
|
179
|
+
continue;
|
|
180
|
+
}
|
|
150
181
|
}
|
|
151
182
|
const metadata = readProcessGcMetadata(join(root, name));
|
|
152
183
|
if (!metadata?.status) {
|
|
@@ -194,13 +225,14 @@ export function gcTerminalProcesses(options: ProcessGcOptions): ProcessGcResult
|
|
|
194
225
|
}
|
|
195
226
|
}
|
|
196
227
|
|
|
197
|
-
return { ok: true, removedRecords, removedLogs, skippedActive, skippedInvalid };
|
|
228
|
+
return { ok: true, removedRecords, removedLogs, skippedActive, reconciledStaleActive, skippedInvalid };
|
|
198
229
|
} catch (error) {
|
|
199
230
|
return {
|
|
200
231
|
ok: false,
|
|
201
232
|
removedRecords: 0,
|
|
202
233
|
removedLogs: 0,
|
|
203
234
|
skippedActive: 0,
|
|
235
|
+
reconciledStaleActive: 0,
|
|
204
236
|
error: error instanceof Error ? error.message : String(error),
|
|
205
237
|
};
|
|
206
238
|
}
|