@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
|
@@ -2,6 +2,10 @@ import { bindRepositoryEntities } from '../repositories/entity-migration';
|
|
|
2
2
|
import { bootstrapLocalProject, diagnoseLatestLocalProjectSource } from '../repositories/local-project-onboarding';
|
|
3
3
|
import { resolveEphemeralWorkspaceTarget } from '../repositories/ephemeral-workspace';
|
|
4
4
|
import type { ResolvedExecutionIdentity } from '../../runtime/control-plane/execution/execution-identity';
|
|
5
|
+
import { readExecutionSession } from '../../runtime/control-plane/execution/session-store';
|
|
6
|
+
import { getWorkContract } from '../../runtime/control-plane/facade/work-contract-store';
|
|
7
|
+
import { getControllerSession, listControllerSessions } from '../../runtime/control-plane/facade/controller-session-store';
|
|
8
|
+
import { isTerminalWorkContractStatus } from '../../runtime/control-plane/facade/types';
|
|
5
9
|
import { executeRepositoryCommand, previewRepositoryCommandExecution } from '../repositories/command-executor';
|
|
6
10
|
import { withControllerLock } from '../repositories/locks';
|
|
7
11
|
import {
|
|
@@ -9,6 +13,7 @@ import {
|
|
|
9
13
|
findIdenticalRepositoryRegistration,
|
|
10
14
|
getRepository,
|
|
11
15
|
listRepositories,
|
|
16
|
+
reconcileRepositoryCheckouts,
|
|
12
17
|
refreshRepository,
|
|
13
18
|
registerRepository,
|
|
14
19
|
removeRepository,
|
|
@@ -19,9 +24,8 @@ import {
|
|
|
19
24
|
} from '../repositories/registry';
|
|
20
25
|
import { buildControllerWorkbench } from '../repositories/workbench';
|
|
21
26
|
import { applySafePatch, buildSafePatchPlan } from '../repositories/safe-patch';
|
|
22
|
-
import { getEditSessionDiff } from '../editing/edit-session';
|
|
27
|
+
import { getEditSession, getEditSessionDiff, type EditSessionBinding } from '../editing/edit-session';
|
|
23
28
|
import { buildSyncOperationDigest, classifyUserFacingError } from '../../runtime/control-plane/facade/operation-digest';
|
|
24
|
-
import { diagnoseRepositoryStuckState, listRepositoryGoalRuns, readRepositoryGoalRegistry, runRepositoryGoal, upsertRepositoryGoal } from '../repositories/goal-registry';
|
|
25
29
|
import {
|
|
26
30
|
repositoryGitCommit,
|
|
27
31
|
repositoryGitCreateBranch,
|
|
@@ -49,6 +53,10 @@ import {
|
|
|
49
53
|
executeRepositoryCommandViaProcessRuntime,
|
|
50
54
|
} from '../../runtime/execution/process-runtime/command-facade';
|
|
51
55
|
import { assessWorkMode, parseExplicitTaskMode } from '../controller/work-mode';
|
|
56
|
+
import { normalizeRepositoryCommand } from '../repositories/command-normalization';
|
|
57
|
+
import { readRepositoryRange } from '../repository/inspector';
|
|
58
|
+
import { getMcpPolicy } from './policy';
|
|
59
|
+
import { redactMcpText } from './redaction';
|
|
52
60
|
import type { CallToolResult, McpToolDefinition } from './tools';
|
|
53
61
|
import {
|
|
54
62
|
boundUtf8,
|
|
@@ -111,6 +119,13 @@ export const repositoryToolDefinitions: McpToolDefinition[] = [
|
|
|
111
119
|
include_removed: { type: 'boolean' },
|
|
112
120
|
detail_level: { type: 'string', enum: ['summary', 'detail'], description: 'Defaults to summary; detail returns the complete checkout history.' },
|
|
113
121
|
}, ['repo_id'], true),
|
|
122
|
+
definition('read_repository_file', 'Read a line range from one policy-readable repository file.', {
|
|
123
|
+
repo_id: repoId,
|
|
124
|
+
checkout_id: { type: 'string' },
|
|
125
|
+
path: { type: 'string' },
|
|
126
|
+
start_line: { type: 'number' },
|
|
127
|
+
end_line: { type: 'number' },
|
|
128
|
+
}, ['path'], true),
|
|
114
129
|
definition('repository_validate', 'Validate repository identity and migrate legacy ownership.', {
|
|
115
130
|
repo_id: repoId,
|
|
116
131
|
detail_level: { type: 'string', enum: ['summary', 'detail'], description: 'Defaults to summary; detail returns full migration evidence.' },
|
|
@@ -130,7 +145,7 @@ export const repositoryToolDefinitions: McpToolDefinition[] = [
|
|
|
130
145
|
}, ['repo_id']),
|
|
131
146
|
definition('repository_remove', 'Soft-remove a repository while retaining audit history.', {
|
|
132
147
|
repo_id: repoId,
|
|
133
|
-
}, ['repo_id']
|
|
148
|
+
}, ['repo_id']),
|
|
134
149
|
definition('repository_workbench', 'Return Workbench state or invoke one bounded Thin Harness operation without adding top-level MCP tools. Prefer batch_execute for multi-step status→search→read→diff or read→patch→check flows so ChatGPT pays one routing/receipt ownership cycle.', {
|
|
135
150
|
repo_id: repoId,
|
|
136
151
|
checkout_id: { type: 'string', description: 'Optional checkout identity for repository-scoped operations.' },
|
|
@@ -146,38 +161,6 @@ export const repositoryToolDefinitions: McpToolDefinition[] = [
|
|
|
146
161
|
additionalProperties: true,
|
|
147
162
|
},
|
|
148
163
|
}),
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
definition('repository_goal_list', 'List durable repository goals stored inside the checkout.', {
|
|
153
|
-
repo_id: repoId,
|
|
154
|
-
checkout_id: { type: 'string', description: 'Optional checkout identity for repositories with multiple local clones.' },
|
|
155
|
-
}, [], true),
|
|
156
|
-
definition('repository_goal_upsert', 'Create or update one durable repository goal for repeated assistant workflows.', {
|
|
157
|
-
repo_id: repoId,
|
|
158
|
-
checkout_id: { type: 'string', description: 'Optional checkout identity for repositories with multiple local clones.' },
|
|
159
|
-
id: { type: 'string' },
|
|
160
|
-
title: { type: 'string' },
|
|
161
|
-
status: { type: 'string', enum: ['active', 'paused', 'done'] },
|
|
162
|
-
checks: { type: 'array', items: { type: 'string' } },
|
|
163
|
-
notes: { type: 'string' },
|
|
164
|
-
}, []),
|
|
165
|
-
definition('repository_stuck_diagnose', 'Diagnose likely repository workflow blockers from Git state and registered goals.', {
|
|
166
|
-
repo_id: repoId,
|
|
167
|
-
checkout_id: { type: 'string', description: 'Optional checkout identity for repositories with multiple local clones.' },
|
|
168
|
-
}, [], true),
|
|
169
|
-
definition('repository_goal_run', 'Run one durable repository goal iteration, optionally executing its configured checks and recording a goal-run artifact.', {
|
|
170
|
-
repo_id: repoId,
|
|
171
|
-
checkout_id: { type: 'string', description: 'Optional checkout identity for repositories with multiple local clones.' },
|
|
172
|
-
goal_id: { type: 'string', description: 'Goal id. Defaults to the first active goal.' },
|
|
173
|
-
run_checks: { type: 'boolean', description: 'When true, execute configured checks. Defaults to diagnosis-only.' },
|
|
174
|
-
}, []),
|
|
175
|
-
definition('repository_goal_runs', 'List recent repository goal-run artifacts.', {
|
|
176
|
-
repo_id: repoId,
|
|
177
|
-
checkout_id: { type: 'string', description: 'Optional checkout identity for repositories with multiple local clones.' },
|
|
178
|
-
limit: { type: 'number' },
|
|
179
|
-
}, [], true),
|
|
180
|
-
|
|
181
164
|
definition('repository_git_status', 'Return a structured Git status snapshot for the selected repository checkout.', {
|
|
182
165
|
repo_id: repoId,
|
|
183
166
|
checkout_id: { type: 'string', description: 'Optional checkout identity for repositories with multiple local clones.' },
|
|
@@ -240,6 +223,7 @@ export const repositoryToolDefinitions: McpToolDefinition[] = [
|
|
|
240
223
|
definition('repository_safe_patch_apply', 'Apply one coherent deterministic edit batch and return bounded review evidence. Checks are opt-in: pass check_ids only when the batch is stable. Long checks return managed Process handles instead of blocking the MCP call; use validation_only with the returned session/request ids to join later without replaying the patch.', {
|
|
241
224
|
repo_id: repoId,
|
|
242
225
|
checkout_id: { type: 'string', description: 'Optional checkout identity for repositories with multiple local clones.' },
|
|
226
|
+
work_id: { type: 'string', description: 'Optional durable Work identity. Workflow controllers should pass the exact claimed Work id so attribution survives transient MCP transport sessions.' },
|
|
243
227
|
session_id: { type: 'string', description: 'Existing edit session id. Required for validation_only; otherwise omit to create one.' },
|
|
244
228
|
purpose: { type: 'string', description: 'Purpose for a newly created edit session.' },
|
|
245
229
|
operations: { type: 'array', items: { type: 'object' }, description: 'Edit operations using the same shape as apply_patch. Required unless validation_only=true.' },
|
|
@@ -263,9 +247,10 @@ export const repositoryToolDefinitions: McpToolDefinition[] = [
|
|
|
263
247
|
cwd: { type: 'string', description: 'Optional root-relative working directory.' },
|
|
264
248
|
workspace_root: { type: 'string', description: 'Absolute existing local directory used as an ephemeral execution root. Mutually exclusive with repo_id/checkout_id; does not register or initialize the directory.' },
|
|
265
249
|
}, ['command'], true),
|
|
266
|
-
definition('repository_command_execute', 'Execute one repository-scoped
|
|
250
|
+
definition('repository_command_execute', 'Execute one repository-scoped command through the thinnest eligible lane: ephemeral for ordinary local work, an in-memory lightweight handle when it outlives the interaction budget, or explicit Durable handling for Work/external/release effects. Lightweight handles have no SQLite, Lease, recovery, or replay membership. Use rh_context for routine code discovery/reading; shell exploration is fallback-only.', {
|
|
267
251
|
repo_id: repoId,
|
|
268
252
|
checkout_id: { type: 'string', description: 'Optional checkout identity for repositories with multiple local clones.' },
|
|
253
|
+
work_id: { type: 'string', description: 'Optional durable Work identity. Workflow controllers should pass the exact claimed Work id so attribution survives transient MCP transport sessions.' },
|
|
269
254
|
command: { anyOf: [{ type: 'string' }, { type: 'array', items: { type: 'string' }, minItems: 1 }], description: 'Legacy shell string or typed argv array. Typed argv executes without a shell.' },
|
|
270
255
|
cwd: { type: 'string', description: 'Optional root-relative working directory.' },
|
|
271
256
|
workspace_root: { type: 'string', description: 'Absolute existing local directory used as an ephemeral execution root. Mutually exclusive with repo_id/checkout_id; does not register or initialize the directory.' },
|
|
@@ -302,10 +287,169 @@ function withResponseMeta(payload: Record<string, unknown>, startedAt: number):
|
|
|
302
287
|
return response;
|
|
303
288
|
}
|
|
304
289
|
|
|
290
|
+
export interface RepositoryToolCallerContext {
|
|
291
|
+
sessionId?: string;
|
|
292
|
+
principalId?: string;
|
|
293
|
+
controllerInstanceId?: string;
|
|
294
|
+
}
|
|
295
|
+
|
|
296
|
+
function claimedExplicitWork(
|
|
297
|
+
controllerHome: string,
|
|
298
|
+
repoId: string,
|
|
299
|
+
caller: RepositoryToolCallerContext | undefined,
|
|
300
|
+
requestedWorkId: string,
|
|
301
|
+
) {
|
|
302
|
+
if (!caller?.principalId?.trim()) return undefined;
|
|
303
|
+
const work = getWorkContract({ controllerHome, repoId }, requestedWorkId);
|
|
304
|
+
if (!work || isTerminalWorkContractStatus(work.status)) throw new Error(`WORK_ATTRIBUTION_INVALID: ${requestedWorkId}`);
|
|
305
|
+
const owner = getControllerSession({ controllerHome, repoId }, requestedWorkId);
|
|
306
|
+
if (!owner) throw new Error(`WORK_CONTROLLER_CLAIM_REQUIRED: ${requestedWorkId}`);
|
|
307
|
+
if ((owner.principalId?.trim() || owner.controllerId) !== caller.principalId.trim()) throw new Error(`WORK_CONTROLLER_OWNERSHIP_MISMATCH: ${requestedWorkId}`);
|
|
308
|
+
return work;
|
|
309
|
+
}
|
|
310
|
+
|
|
311
|
+
function assertNoBoundExecutionSessionMutation(
|
|
312
|
+
controllerHome: string,
|
|
313
|
+
repository: ReturnType<typeof resolveRepositorySelection>,
|
|
314
|
+
caller?: RepositoryToolCallerContext,
|
|
315
|
+
): void {
|
|
316
|
+
if (!caller?.principalId?.trim() || !caller.sessionId?.trim()) return;
|
|
317
|
+
const executionSession = readExecutionSession(controllerHome, {
|
|
318
|
+
sessionId: caller.sessionId,
|
|
319
|
+
principalId: caller.principalId,
|
|
320
|
+
controllerInstanceId: caller.controllerInstanceId,
|
|
321
|
+
});
|
|
322
|
+
const workId = executionSession?.activeWorkId?.trim();
|
|
323
|
+
if (!workId || executionSession?.activeRepositoryId !== repository.repoId) return;
|
|
324
|
+
const work = getWorkContract({ controllerHome, repoId: repository.repoId }, workId);
|
|
325
|
+
if (!work) throw new Error(`WORK_ATTRIBUTION_INVALID: ${workId}`);
|
|
326
|
+
if (isTerminalWorkContractStatus(work.status)) {
|
|
327
|
+
throw new Error(`WORK_ATTRIBUTION_TERMINAL: ${work.workId}:${work.status}`);
|
|
328
|
+
}
|
|
329
|
+
throw new Error(`WORK_ATTRIBUTION_REQUIRED: ${work.workId}; active execution session mutations must pass work_id explicitly`);
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
function claimedSessionWorkId(
|
|
333
|
+
controllerHome: string,
|
|
334
|
+
repository: ReturnType<typeof resolveRepositorySelection>,
|
|
335
|
+
caller?: RepositoryToolCallerContext,
|
|
336
|
+
explicitWorkId?: unknown,
|
|
337
|
+
): string | undefined {
|
|
338
|
+
const requestedWorkId = typeof explicitWorkId === 'string' ? explicitWorkId.trim() : '';
|
|
339
|
+
if (requestedWorkId) {
|
|
340
|
+
const work = claimedExplicitWork(controllerHome, repository.repoId, caller, requestedWorkId);
|
|
341
|
+
if (!work) return undefined;
|
|
342
|
+
if (work.checkoutId && work.checkoutId !== repository.activeCheckoutId) throw new Error(`WORK_CHECKOUT_MISMATCH: ${requestedWorkId}:${repository.activeCheckoutId}`);
|
|
343
|
+
return requestedWorkId;
|
|
344
|
+
}
|
|
345
|
+
if (!caller?.principalId?.trim()) return undefined;
|
|
346
|
+
if (caller.sessionId?.trim()) {
|
|
347
|
+
const executionSession = readExecutionSession(controllerHome, {
|
|
348
|
+
sessionId: caller.sessionId,
|
|
349
|
+
principalId: caller.principalId,
|
|
350
|
+
controllerInstanceId: caller.controllerInstanceId,
|
|
351
|
+
});
|
|
352
|
+
const workId = executionSession?.activeWorkId?.trim();
|
|
353
|
+
if (workId && executionSession?.activeRepositoryId === repository.repoId && (!executionSession.activeCheckoutId || executionSession.activeCheckoutId === repository.activeCheckoutId)) {
|
|
354
|
+
const work = getWorkContract({ controllerHome, repoId: repository.repoId }, workId);
|
|
355
|
+
if (work && isTerminalWorkContractStatus(work.status)) {
|
|
356
|
+
throw new Error(`WORK_ATTRIBUTION_TERMINAL: ${work.workId}:${work.status}`);
|
|
357
|
+
}
|
|
358
|
+
const owner = getControllerSession({ controllerHome, repoId: repository.repoId }, workId);
|
|
359
|
+
if (work && owner?.sessionId === caller.sessionId && (owner.principalId?.trim() || owner.controllerId) === caller.principalId.trim()) return workId;
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
const principal = caller.principalId.trim();
|
|
363
|
+
const candidates = listControllerSessions({ controllerHome, repoId: repository.repoId })
|
|
364
|
+
.filter((owner) => (owner.principalId?.trim() || owner.controllerId) === principal)
|
|
365
|
+
.map((owner) => ({ owner, work: getWorkContract({ controllerHome, repoId: repository.repoId }, owner.workId) }))
|
|
366
|
+
.filter((entry): entry is { owner: ReturnType<typeof listControllerSessions>[number]; work: NonNullable<ReturnType<typeof getWorkContract>> } => Boolean(entry.work && !isTerminalWorkContractStatus(entry.work.status) && (!entry.work.checkoutId || entry.work.checkoutId === repository.activeCheckoutId)));
|
|
367
|
+
if (candidates.length === 1) return candidates[0].work.workId;
|
|
368
|
+
if (candidates.length > 1) throw new Error(`WORK_ATTRIBUTION_AMBIGUOUS: principal ${principal} owns ${candidates.length} active Works on checkout ${repository.activeCheckoutId}`);
|
|
369
|
+
return undefined;
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
export function claimedSessionEditBinding(
|
|
373
|
+
controllerHome: string,
|
|
374
|
+
repository: ReturnType<typeof resolveRepositorySelection>,
|
|
375
|
+
caller?: RepositoryToolCallerContext,
|
|
376
|
+
explicitWorkId?: unknown,
|
|
377
|
+
): EditSessionBinding | undefined {
|
|
378
|
+
const workId = claimedSessionWorkId(controllerHome, repository, caller, explicitWorkId);
|
|
379
|
+
if (!workId || !caller?.principalId?.trim()) return undefined;
|
|
380
|
+
const work = getWorkContract({ controllerHome, repoId: repository.repoId }, workId);
|
|
381
|
+
if (!work) return undefined;
|
|
382
|
+
return {
|
|
383
|
+
workId,
|
|
384
|
+
repoId: repository.repoId,
|
|
385
|
+
checkoutId: repository.activeCheckoutId,
|
|
386
|
+
principalId: caller.principalId.trim(),
|
|
387
|
+
controllerInstanceId: caller.controllerInstanceId,
|
|
388
|
+
routeDecisionFingerprint: work.routeDecisionFingerprint,
|
|
389
|
+
};
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
function safePatchEditBinding(
|
|
393
|
+
controllerHome: string,
|
|
394
|
+
repository: ReturnType<typeof resolveRepositorySelection>,
|
|
395
|
+
caller: RepositoryToolCallerContext | undefined,
|
|
396
|
+
args: Record<string, unknown>,
|
|
397
|
+
): EditSessionBinding | undefined {
|
|
398
|
+
const explicitWorkId = typeof args.work_id === 'string' ? args.work_id.trim() : '';
|
|
399
|
+
if (explicitWorkId) return claimedSessionEditBinding(controllerHome, repository, caller, explicitWorkId);
|
|
400
|
+
const sessionId = typeof args.session_id === 'string' ? args.session_id.trim() : '';
|
|
401
|
+
if (sessionId) {
|
|
402
|
+
const existingWorkId = getEditSession(repository.canonicalRoot, sessionId).workId?.trim();
|
|
403
|
+
return existingWorkId
|
|
404
|
+
? claimedSessionEditBinding(controllerHome, repository, caller, existingWorkId)
|
|
405
|
+
: undefined;
|
|
406
|
+
}
|
|
407
|
+
assertNoBoundExecutionSessionMutation(controllerHome, repository, caller);
|
|
408
|
+
return undefined;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
function resolveRepositorySelectionForClaimedWork(
|
|
412
|
+
controllerHome: string,
|
|
413
|
+
args: Record<string, unknown>,
|
|
414
|
+
repoIdValue: string,
|
|
415
|
+
caller?: RepositoryToolCallerContext,
|
|
416
|
+
): ReturnType<typeof resolveRepositorySelection> {
|
|
417
|
+
const checkoutId = typeof args.checkout_id === 'string' ? args.checkout_id.trim() : '';
|
|
418
|
+
const explicitWorkId = typeof args.work_id === 'string' ? args.work_id.trim() : '';
|
|
419
|
+
const repository = resolveRepositorySelection({
|
|
420
|
+
repoId: repoIdValue || undefined,
|
|
421
|
+
checkoutId: checkoutId || undefined,
|
|
422
|
+
controllerHome,
|
|
423
|
+
allowSoleRepository: true,
|
|
424
|
+
});
|
|
425
|
+
if (!explicitWorkId || checkoutId) return repository;
|
|
426
|
+
const work = claimedExplicitWork(controllerHome, repository.repoId, caller, explicitWorkId);
|
|
427
|
+
if (!work?.checkoutId || work.checkoutId === repository.activeCheckoutId) return repository;
|
|
428
|
+
return resolveRepositorySelection({
|
|
429
|
+
repoId: repository.repoId,
|
|
430
|
+
checkoutId: work.checkoutId,
|
|
431
|
+
controllerHome,
|
|
432
|
+
allowSoleRepository: true,
|
|
433
|
+
});
|
|
434
|
+
}
|
|
435
|
+
|
|
436
|
+
function rawDefaultBranchMergeCommand(repository: ReturnType<typeof resolveRepositorySelection>, command: unknown): boolean {
|
|
437
|
+
if (!repository.defaultBranch) return false;
|
|
438
|
+
const checkout = repository.checkouts.find((entry) => entry.checkoutId === repository.activeCheckoutId);
|
|
439
|
+
if (checkout?.branch !== repository.defaultBranch) return false;
|
|
440
|
+
const normalized = normalizeRepositoryCommand(command);
|
|
441
|
+
if (normalized.kind === 'argv') {
|
|
442
|
+
const executable = normalized.executable?.split('/').pop();
|
|
443
|
+
return executable === 'git' && normalized.args?.[0] === 'merge';
|
|
444
|
+
}
|
|
445
|
+
return /(?:^|[;&|]\s*)git\s+merge\b/.test(normalized.shellCommand ?? '');
|
|
446
|
+
}
|
|
447
|
+
|
|
305
448
|
function resolveRepositoryCommandTarget(
|
|
306
449
|
controllerHome: string,
|
|
307
450
|
args: Record<string, unknown>,
|
|
308
451
|
repoIdValue: string,
|
|
452
|
+
caller?: RepositoryToolCallerContext,
|
|
309
453
|
): {
|
|
310
454
|
repository: ReturnType<typeof resolveRepositorySelection>;
|
|
311
455
|
executionIdentity: ResolvedExecutionIdentity;
|
|
@@ -330,12 +474,11 @@ function resolveRepositoryCommandTarget(
|
|
|
330
474
|
workspace: { workspaceId: target.workspaceId, root: target.canonicalRoot, registered: false },
|
|
331
475
|
};
|
|
332
476
|
}
|
|
333
|
-
const repository =
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
controllerHome,
|
|
337
|
-
|
|
338
|
-
});
|
|
477
|
+
const repository = resolveRepositorySelectionForClaimedWork(controllerHome, args, repoIdValue, caller);
|
|
478
|
+
const explicitWorkId = typeof args.work_id === 'string' ? args.work_id.trim() : '';
|
|
479
|
+
const workId = explicitWorkId
|
|
480
|
+
? claimedSessionWorkId(controllerHome, repository, caller, explicitWorkId)
|
|
481
|
+
: undefined;
|
|
339
482
|
return {
|
|
340
483
|
repository,
|
|
341
484
|
executionIdentity: {
|
|
@@ -344,6 +487,7 @@ function resolveRepositoryCommandTarget(
|
|
|
344
487
|
repositoryId: repository.repoId,
|
|
345
488
|
checkoutId: repository.activeCheckoutId,
|
|
346
489
|
canonicalRoot: repository.canonicalRoot,
|
|
490
|
+
...(workId ? { workId } : {}),
|
|
347
491
|
},
|
|
348
492
|
};
|
|
349
493
|
}
|
|
@@ -434,6 +578,33 @@ function failure(error: unknown): RepositoryToolResult {
|
|
|
434
578
|
return { ...result({ error: { code, message, ...(compactDetails !== undefined ? { details: compactDetails } : {}) } }), isError: true };
|
|
435
579
|
}
|
|
436
580
|
|
|
581
|
+
interface RepositoryExplorationGuidance {
|
|
582
|
+
code: 'FRAGMENTED_REPOSITORY_EXPLORATION';
|
|
583
|
+
recommendedTool: 'rh_context';
|
|
584
|
+
recommendedOperation: 'search';
|
|
585
|
+
message: string;
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
function fragmentedRepositoryExplorationGuidance(command: unknown): RepositoryExplorationGuidance | undefined {
|
|
589
|
+
if (typeof command !== 'string') return undefined;
|
|
590
|
+
const segments = command.split(/(?:&&|\|\||[;|])/g);
|
|
591
|
+
const readPrograms = new Set<string>();
|
|
592
|
+
let readStepCount = 0;
|
|
593
|
+
for (const segment of segments) {
|
|
594
|
+
const match = segment.trim().match(/^(?:(?:command|env)\s+)?(?:[A-Za-z_][A-Za-z0-9_]*=[^\s]+\s+)*(rg|grep|sed|cat|head|tail)\b/i);
|
|
595
|
+
if (!match) continue;
|
|
596
|
+
readStepCount += 1;
|
|
597
|
+
readPrograms.add(match[1]!.toLowerCase());
|
|
598
|
+
}
|
|
599
|
+
if (readStepCount < 2) return undefined;
|
|
600
|
+
return {
|
|
601
|
+
code: 'FRAGMENTED_REPOSITORY_EXPLORATION',
|
|
602
|
+
recommendedTool: 'rh_context',
|
|
603
|
+
recommendedOperation: 'search',
|
|
604
|
+
message: `Detected ${readStepCount} chained repository read steps (${[...readPrograms].join(', ')}). Prefer a broad rh_context request first, then repeat rh_context with selected paths or relationships when more evidence can improve correctness; keep shell search as fallback when the Context Plane cannot supply the needed evidence.`,
|
|
605
|
+
};
|
|
606
|
+
}
|
|
607
|
+
|
|
437
608
|
function compactProcessCommandPayload(input: {
|
|
438
609
|
accepted?: boolean;
|
|
439
610
|
mode: string;
|
|
@@ -456,6 +627,7 @@ function compactProcessCommandPayload(input: {
|
|
|
456
627
|
/** summary (default) omits nested process / routing dumps; detail restores diagnostics. */
|
|
457
628
|
detailLevel?: 'summary' | 'detail';
|
|
458
629
|
includeFullProcess?: boolean;
|
|
630
|
+
guidance?: RepositoryExplorationGuidance;
|
|
459
631
|
}): Record<string, unknown> {
|
|
460
632
|
const output = compactCommandOutput(input.stdout, input.stderr, { ok: input.ok === true });
|
|
461
633
|
const detail = input.detailLevel === 'detail' || input.includeFullProcess === true;
|
|
@@ -483,10 +655,14 @@ function compactProcessCommandPayload(input: {
|
|
|
483
655
|
...(input.workspace ? { workspace: input.workspace } : {}),
|
|
484
656
|
...(input.processId ? { processId: input.processId } : {}),
|
|
485
657
|
...(completed ? { exitCode: input.exitCode ?? (ok ? 0 : 1) } : {}),
|
|
658
|
+
// A running default response otherwise loses its only dependency-bound
|
|
659
|
+
// continuation advice when the detailed Process record is omitted.
|
|
660
|
+
...(!completed ? { next: input.next } : {}),
|
|
486
661
|
stdout: output.stdout ?? '',
|
|
487
662
|
stderr: output.stderr ?? '',
|
|
488
663
|
...(output.stdoutTruncated ? { stdoutTruncated: true, stdoutBytes: output.stdoutBytes } : {}),
|
|
489
664
|
...(output.stderrTruncated ? { stderrTruncated: true, stderrBytes: output.stderrBytes } : {}),
|
|
665
|
+
...(input.guidance ? { guidance: input.guidance, suggestedOperation: 'rh_context' } : {}),
|
|
490
666
|
};
|
|
491
667
|
if (completed && !ok) {
|
|
492
668
|
payload.error = {
|
|
@@ -534,6 +710,7 @@ function compactProcessCommandPayload(input: {
|
|
|
534
710
|
}
|
|
535
711
|
: {}),
|
|
536
712
|
durableSideEffects: effects,
|
|
713
|
+
...(input.guidance ? { guidance: input.guidance, suggestedOperation: 'rh_context' } : {}),
|
|
537
714
|
next: input.next,
|
|
538
715
|
detailLevel: 'detail',
|
|
539
716
|
};
|
|
@@ -551,6 +728,18 @@ function emptyRepositoryMigration(repository: ReturnType<typeof registerReposito
|
|
|
551
728
|
};
|
|
552
729
|
}
|
|
553
730
|
|
|
731
|
+
function registrationOnlyMetadataRefresh(
|
|
732
|
+
match: ReturnType<typeof findIdenticalRepositoryRegistration>,
|
|
733
|
+
): boolean {
|
|
734
|
+
if (!match || match.identical || match.reasons.length === 0) return false;
|
|
735
|
+
const metadataReasons = new Set([
|
|
736
|
+
'remote_identity_changed',
|
|
737
|
+
'default_branch_changed',
|
|
738
|
+
'display_name_changed',
|
|
739
|
+
]);
|
|
740
|
+
return match.reasons.every((reason) => metadataReasons.has(reason));
|
|
741
|
+
}
|
|
742
|
+
|
|
554
743
|
function registrationOnlyAttachedExistingWorktree(repository: ReturnType<typeof registerRepository>): boolean {
|
|
555
744
|
const selected = repository.checkouts.find((checkout) => checkout.checkoutId === repository.activeCheckoutId);
|
|
556
745
|
return selected?.worktree === true
|
|
@@ -561,11 +750,35 @@ export async function callRepositoryTool(
|
|
|
561
750
|
controllerHome: string,
|
|
562
751
|
name: string,
|
|
563
752
|
args: Record<string, unknown>,
|
|
753
|
+
caller?: RepositoryToolCallerContext,
|
|
564
754
|
): Promise<RepositoryToolResult | undefined> {
|
|
565
|
-
if (!name.startsWith('repository_')) return undefined;
|
|
755
|
+
if (!name.startsWith('repository_') && name !== 'read_repository_file') return undefined;
|
|
566
756
|
try {
|
|
567
757
|
const repoIdValue = typeof args.repo_id === 'string' ? args.repo_id.trim() : '';
|
|
568
758
|
switch (name) {
|
|
759
|
+
case 'read_repository_file': {
|
|
760
|
+
const repository = resolveRepositorySelection({
|
|
761
|
+
repoId: typeof args.repo_id === 'string' ? args.repo_id : undefined,
|
|
762
|
+
checkoutId: typeof args.checkout_id === 'string' ? args.checkout_id : undefined,
|
|
763
|
+
controllerHome,
|
|
764
|
+
allowSoleRepository: true,
|
|
765
|
+
});
|
|
766
|
+
const path = String(args.path ?? '').trim();
|
|
767
|
+
if (!path) throw new Error('REPOSITORY_PATH_REQUIRED: path is required');
|
|
768
|
+
const session = caller?.sessionId
|
|
769
|
+
? { sessionId: caller.sessionId, repoId: repository.repoId, checkoutId: repository.activeCheckoutId }
|
|
770
|
+
: undefined;
|
|
771
|
+
const range = readRepositoryRange(
|
|
772
|
+
repository.canonicalRoot,
|
|
773
|
+
getMcpPolicy('controller', { repoRoot: repository.canonicalRoot }),
|
|
774
|
+
path,
|
|
775
|
+
typeof args.start_line === 'number' ? args.start_line : 1,
|
|
776
|
+
typeof args.end_line === 'number' ? args.end_line : 200,
|
|
777
|
+
session,
|
|
778
|
+
);
|
|
779
|
+
const redacted = redactMcpText(range.content);
|
|
780
|
+
return result({ ...range, content: redacted.text, redactions: redacted.redactions });
|
|
781
|
+
}
|
|
569
782
|
case 'repository_register': {
|
|
570
783
|
const startedAt = performance.now();
|
|
571
784
|
const registerInput = {
|
|
@@ -587,16 +800,17 @@ export async function callRepositoryTool(
|
|
|
587
800
|
startedAt,
|
|
588
801
|
));
|
|
589
802
|
}
|
|
803
|
+
const metadataRefresh = registrationOnlyMetadataRefresh(identical);
|
|
590
804
|
const repository = registerRepository(registerInput);
|
|
591
|
-
// Adding a worktree checkout
|
|
592
|
-
// checkout
|
|
593
|
-
// bound to
|
|
594
|
-
//
|
|
595
|
-
const migration = registrationOnlyAttachedExistingWorktree(repository)
|
|
805
|
+
// Adding a worktree checkout or changing only display/remote/default-branch
|
|
806
|
+
// metadata preserves repository + checkout authority. Existing entities
|
|
807
|
+
// are already bound to the stable repoId, so a historical migration scan
|
|
808
|
+
// would only add latency and stale unresolved diagnostics.
|
|
809
|
+
const migration = registrationOnlyAttachedExistingWorktree(repository) || metadataRefresh
|
|
596
810
|
? emptyRepositoryMigration(repository)
|
|
597
811
|
: bindRepositoryEntities(repository);
|
|
598
812
|
return result(withResponseMeta(
|
|
599
|
-
summarizeRepositoryRegistration(repository, migration, args.detail_level === 'detail'),
|
|
813
|
+
summarizeRepositoryRegistration(repository, migration, args.detail_level === 'detail', metadataRefresh),
|
|
600
814
|
startedAt,
|
|
601
815
|
));
|
|
602
816
|
}
|
|
@@ -623,6 +837,11 @@ export async function callRepositoryTool(
|
|
|
623
837
|
case 'repository_list':
|
|
624
838
|
return result({ repositories: listRepositories(controllerHome, { includeRemoved: args.include_removed === true }).map(repositorySummary) });
|
|
625
839
|
case 'repository_get': {
|
|
840
|
+
// Repository inspection is also the bounded reconciliation point for
|
|
841
|
+
// checkout lifecycle drift caused by worktrees being removed outside
|
|
842
|
+
// Forge. Reuse the existing registry reconciler so stale worktrees are
|
|
843
|
+
// not exposed as lifecycle=active to automation callers.
|
|
844
|
+
reconcileRepositoryCheckouts(repoIdValue, controllerHome);
|
|
626
845
|
const repository = getRepository(repoIdValue, controllerHome, { includeRemoved: args.include_removed === true });
|
|
627
846
|
return result(summarizeRepositoryInspection(repository, args.detail_level === 'detail'));
|
|
628
847
|
}
|
|
@@ -716,38 +935,6 @@ export async function callRepositoryTool(
|
|
|
716
935
|
checkout_id: typeof args.checkout_id === 'string' ? args.checkout_id : payload.checkout_id,
|
|
717
936
|
});
|
|
718
937
|
}
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
case 'repository_goal_list': {
|
|
723
|
-
const repository = resolveRepositorySelection({ repoId: repoIdValue || undefined, checkoutId: typeof args.checkout_id === 'string' ? args.checkout_id : undefined, controllerHome, allowSoleRepository: true });
|
|
724
|
-
return result({ repoId: repository.repoId, checkoutId: repository.activeCheckoutId, registry: readRepositoryGoalRegistry(repository) });
|
|
725
|
-
}
|
|
726
|
-
case 'repository_goal_upsert': {
|
|
727
|
-
const repository = resolveRepositorySelection({ repoId: repoIdValue || undefined, checkoutId: typeof args.checkout_id === 'string' ? args.checkout_id : undefined, controllerHome, allowSoleRepository: true });
|
|
728
|
-
return result({ repoId: repository.repoId, checkoutId: repository.activeCheckoutId, ...upsertRepositoryGoal(repository, { id: args.id, title: args.title, status: args.status, checks: args.checks, notes: args.notes }) as unknown as Record<string, unknown> });
|
|
729
|
-
}
|
|
730
|
-
case 'repository_stuck_diagnose': {
|
|
731
|
-
const repository = resolveRepositorySelection({ repoId: repoIdValue || undefined, checkoutId: typeof args.checkout_id === 'string' ? args.checkout_id : undefined, controllerHome, allowSoleRepository: true });
|
|
732
|
-
return result({ diagnosis: diagnoseRepositoryStuckState(repository) });
|
|
733
|
-
}
|
|
734
|
-
|
|
735
|
-
case 'repository_goal_run': {
|
|
736
|
-
const repository = resolveRepositorySelection({ repoId: repoIdValue || undefined, checkoutId: typeof args.checkout_id === 'string' ? args.checkout_id : undefined, controllerHome, allowSoleRepository: true });
|
|
737
|
-
const ran = withControllerLock(
|
|
738
|
-
controllerHome,
|
|
739
|
-
{ scope: 'repository', repoId: repository.repoId },
|
|
740
|
-
'mcp:repository_goal_run',
|
|
741
|
-
() => runRepositoryGoal(repository, { goalId: args.goal_id, runChecks: args.run_checks }),
|
|
742
|
-
60_000,
|
|
743
|
-
);
|
|
744
|
-
return result({ repoId: repository.repoId, checkoutId: repository.activeCheckoutId, ...ran as unknown as Record<string, unknown> });
|
|
745
|
-
}
|
|
746
|
-
case 'repository_goal_runs': {
|
|
747
|
-
const repository = resolveRepositorySelection({ repoId: repoIdValue || undefined, checkoutId: typeof args.checkout_id === 'string' ? args.checkout_id : undefined, controllerHome, allowSoleRepository: true });
|
|
748
|
-
const limit = typeof args.limit === 'number' ? args.limit : typeof args.limit === 'string' ? Number(args.limit) : undefined;
|
|
749
|
-
return result({ repoId: repository.repoId, checkoutId: repository.activeCheckoutId, runs: listRepositoryGoalRuns(repository, limit) });
|
|
750
|
-
}
|
|
751
938
|
case 'repository_git_status': {
|
|
752
939
|
const repository = resolveRepositorySelection({ repoId: repoIdValue || undefined, checkoutId: typeof args.checkout_id === 'string' ? args.checkout_id : undefined, controllerHome, allowSoleRepository: true });
|
|
753
940
|
const status = args.refresh === true
|
|
@@ -809,12 +996,8 @@ export async function callRepositoryTool(
|
|
|
809
996
|
});
|
|
810
997
|
}
|
|
811
998
|
case 'repository_safe_patch_apply': {
|
|
812
|
-
const repository =
|
|
813
|
-
|
|
814
|
-
checkoutId: typeof args.checkout_id === 'string' ? args.checkout_id : undefined,
|
|
815
|
-
controllerHome,
|
|
816
|
-
allowSoleRepository: true,
|
|
817
|
-
});
|
|
999
|
+
const repository = resolveRepositorySelectionForClaimedWork(controllerHome, args, repoIdValue, caller);
|
|
1000
|
+
const binding = safePatchEditBinding(controllerHome, repository, caller, args);
|
|
818
1001
|
const applied = withControllerLock(
|
|
819
1002
|
controllerHome,
|
|
820
1003
|
{ scope: 'repository', repoId: repository.repoId },
|
|
@@ -829,6 +1012,7 @@ export async function callRepositoryTool(
|
|
|
829
1012
|
continueOnError: args.continue_on_error,
|
|
830
1013
|
refreshFingerprints: args.refresh_fingerprints,
|
|
831
1014
|
recoverStaleSession: args.recover_stale_session,
|
|
1015
|
+
binding,
|
|
832
1016
|
}),
|
|
833
1017
|
60_000,
|
|
834
1018
|
);
|
|
@@ -892,7 +1076,7 @@ export async function callRepositoryTool(
|
|
|
892
1076
|
return ok ? result(payload) : { ...result(payload), isError: true };
|
|
893
1077
|
}
|
|
894
1078
|
case 'repository_command_preview': {
|
|
895
|
-
const target = resolveRepositoryCommandTarget(controllerHome, args, repoIdValue);
|
|
1079
|
+
const target = resolveRepositoryCommandTarget(controllerHome, args, repoIdValue, caller);
|
|
896
1080
|
const { repository } = target;
|
|
897
1081
|
const execution = withControllerLock(
|
|
898
1082
|
controllerHome,
|
|
@@ -912,8 +1096,17 @@ export async function callRepositoryTool(
|
|
|
912
1096
|
});
|
|
913
1097
|
}
|
|
914
1098
|
case 'repository_command_execute': {
|
|
915
|
-
const
|
|
1099
|
+
const explorationGuidance = fragmentedRepositoryExplorationGuidance(args.command);
|
|
1100
|
+
const target = resolveRepositoryCommandTarget(controllerHome, args, repoIdValue, caller);
|
|
916
1101
|
const { repository, executionIdentity } = target;
|
|
1102
|
+
const explicitWorkId = typeof args.work_id === 'string' ? args.work_id.trim() : '';
|
|
1103
|
+
if (!explicitWorkId) assertNoBoundExecutionSessionMutation(controllerHome, repository, caller);
|
|
1104
|
+
const deliveryWorkId = rawDefaultBranchMergeCommand(repository, args.command)
|
|
1105
|
+
? executionIdentity.workId
|
|
1106
|
+
: undefined;
|
|
1107
|
+
if (deliveryWorkId) {
|
|
1108
|
+
throw new Error(`WORK_DELIVERY_REQUIRES_FINALIZE: ${deliveryWorkId} must pass Work verification and use rh_work finalize before default-branch integration`);
|
|
1109
|
+
}
|
|
917
1110
|
const timeoutMs = typeof args.timeout_ms === 'number'
|
|
918
1111
|
? args.timeout_ms
|
|
919
1112
|
: typeof args.timeout_ms === 'string'
|
|
@@ -939,16 +1132,33 @@ export async function callRepositoryTool(
|
|
|
939
1132
|
const forceDurable = fromDurableWorker
|
|
940
1133
|
|| args.mode === 'durable'
|
|
941
1134
|
|| args.force_durable === true;
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
1135
|
+
// repository_command_execute owns its command execution architecture.
|
|
1136
|
+
// Do not send ordinary command text through Thin Harness semantic risk
|
|
1137
|
+
// routing: that creates duplicate regex/classifier policy and turns
|
|
1138
|
+
// unknown CLI shapes into false durable/reject decisions. Only an
|
|
1139
|
+
// explicitly requested durable mode uses the generic durable router.
|
|
1140
|
+
const routingDecision = forceDurable
|
|
1141
|
+
? routeExecution({
|
|
1142
|
+
operation: 'repository_command_execute',
|
|
1143
|
+
mode: 'durable',
|
|
1144
|
+
timeoutMs,
|
|
1145
|
+
background: args.background === true || args.async === true,
|
|
1146
|
+
})
|
|
1147
|
+
: {
|
|
1148
|
+
mode: 'fast' as const,
|
|
1149
|
+
reasons: ['repository_command_process_runtime_authoritative'],
|
|
1150
|
+
risk: 'unknown' as const,
|
|
1151
|
+
estimatedClass: 'unknown' as const,
|
|
1152
|
+
requiresIsolation: false,
|
|
1153
|
+
requiresRecovery: false,
|
|
1154
|
+
suggestedOperation: undefined,
|
|
1155
|
+
effects: {
|
|
1156
|
+
readsWorkspace: true,
|
|
1157
|
+
mutatesWorkspace: true,
|
|
1158
|
+
mutatesGitRefs: false,
|
|
1159
|
+
remoteWrite: false,
|
|
1160
|
+
},
|
|
1161
|
+
};
|
|
952
1162
|
// Unified Process Runtime for local commands (Direct/Managed) when not forced durable.
|
|
953
1163
|
// Ephemeral workspaces cannot persist a recoverable async Process handle,
|
|
954
1164
|
// so their async requests remain promotion-required below.
|
|
@@ -959,7 +1169,43 @@ export async function callRepositoryTool(
|
|
|
959
1169
|
defaultBranch: repository.defaultBranch,
|
|
960
1170
|
timeoutMs,
|
|
961
1171
|
});
|
|
962
|
-
if (routeClass.route === '
|
|
1172
|
+
if (routeClass.route === 'reject') {
|
|
1173
|
+
return result({
|
|
1174
|
+
accepted: false,
|
|
1175
|
+
mode: 'reject',
|
|
1176
|
+
path: routeClass.reason,
|
|
1177
|
+
route: 'reject',
|
|
1178
|
+
status: 'rejected',
|
|
1179
|
+
repoId: repository.repoId,
|
|
1180
|
+
checkoutId: repository.activeCheckoutId,
|
|
1181
|
+
workspace: target.workspace,
|
|
1182
|
+
message: routeClass.reason === 'standalone_recovery_lifecycle_required'
|
|
1183
|
+
? 'Canonical Runtime and connector lifecycle operations must be submitted to the standalone Forge Recovery owner, not executed as a child of repository Process Runtime.'
|
|
1184
|
+
: 'Repository command execution was rejected by Process Runtime routing policy.',
|
|
1185
|
+
suggestedOperation: routeClass.reason === 'standalone_recovery_lifecycle_required'
|
|
1186
|
+
? 'Use the standalone Forge Recovery connector.'
|
|
1187
|
+
: routingDecision.suggestedOperation,
|
|
1188
|
+
durableSideEffects: {
|
|
1189
|
+
executionJobCount: 0,
|
|
1190
|
+
localJobCount: 0,
|
|
1191
|
+
workerSpawnCount: 0,
|
|
1192
|
+
projectionUpdateCount: 0,
|
|
1193
|
+
},
|
|
1194
|
+
});
|
|
1195
|
+
}
|
|
1196
|
+
if (routeClass.route === 'durable' && target.workspace) {
|
|
1197
|
+
return result({
|
|
1198
|
+
accepted: false,
|
|
1199
|
+
mode: 'durable',
|
|
1200
|
+
path: 'ephemeral_workspace_promotion_required',
|
|
1201
|
+
repoId: repository.repoId,
|
|
1202
|
+
checkoutId: repository.activeCheckoutId,
|
|
1203
|
+
workspace: target.workspace,
|
|
1204
|
+
message: 'Ephemeral workspaces must be registered before durable remote effects so Process identity and reconciliation have a stable repository authority.',
|
|
1205
|
+
suggestedOperation: 'repository_register',
|
|
1206
|
+
});
|
|
1207
|
+
}
|
|
1208
|
+
if (routeClass.route === 'process_direct' || routeClass.route === 'process_managed' || routeClass.route === 'durable') {
|
|
963
1209
|
const processResult = await executeRepositoryCommandViaProcessRuntime({
|
|
964
1210
|
controllerHome,
|
|
965
1211
|
repository,
|
|
@@ -972,6 +1218,7 @@ export async function callRepositoryTool(
|
|
|
972
1218
|
maxOutputBytes,
|
|
973
1219
|
returnHandleImmediately,
|
|
974
1220
|
requestId: typeof args.request_id === 'string' ? args.request_id : undefined,
|
|
1221
|
+
workId: executionIdentity.workId,
|
|
975
1222
|
executionIdentity,
|
|
976
1223
|
allowNonGitWorkspace: target.workspace !== undefined,
|
|
977
1224
|
});
|
|
@@ -1033,13 +1280,14 @@ export async function callRepositoryTool(
|
|
|
1033
1280
|
stdout: processResult.stdout,
|
|
1034
1281
|
stderr: processResult.stderr,
|
|
1035
1282
|
durableSideEffects: processResult.durableSideEffects,
|
|
1283
|
+
guidance: explorationGuidance,
|
|
1036
1284
|
next: directWithoutHandle
|
|
1037
1285
|
? processResult.reason === 'readonly_fast_path'
|
|
1038
1286
|
? 'Bounded readonly execution completed without Process record / Lease / Local Job / ExecutionJob / Worker.'
|
|
1039
1287
|
: 'Bounded ephemeral workspace execution completed without Process record / Lease / Local Job / ExecutionJob / Worker.'
|
|
1040
1288
|
: processResult.route === 'process_direct' || completed
|
|
1041
1289
|
? 'Process Runtime completed without Local Job / ExecutionJob / Worker.'
|
|
1042
|
-
: `Process Runtime is managing ${handle?.processId};
|
|
1290
|
+
: `Process Runtime is managing ${handle?.processId}. Continue independent work; use process_get only if an observation can change the next decision, then join with process_wait when this result becomes a dependency. Do not re-run or periodically poll.`,
|
|
1043
1291
|
detailLevel,
|
|
1044
1292
|
});
|
|
1045
1293
|
payload.status = status;
|
|
@@ -1111,6 +1359,7 @@ export async function callRepositoryTool(
|
|
|
1111
1359
|
timeoutMs,
|
|
1112
1360
|
maxOutputBytes,
|
|
1113
1361
|
requestId: typeof args.request_id === 'string' ? args.request_id : undefined,
|
|
1362
|
+
workId: executionIdentity.workId,
|
|
1114
1363
|
executionIdentity,
|
|
1115
1364
|
});
|
|
1116
1365
|
if (processResult.route === 'process_direct') {
|
|
@@ -1155,6 +1404,12 @@ export async function callRepositoryTool(
|
|
|
1155
1404
|
routing: compactRoutingSummary({ path: 'durable', mode: 'durable', reasons: [routingDecision.reasons.join(','), 'local_bridge_execution_retired'] }),
|
|
1156
1405
|
message: 'This command requires explicit external Controller handling; Local Bridge Jobs are retired for repository commands.',
|
|
1157
1406
|
suggestedOperation: 'Create or claim WorkContract, then use rh_work.launcher_start or a Process Runtime-compatible command.',
|
|
1407
|
+
externalEffect: {
|
|
1408
|
+
outcome: 'not_started',
|
|
1409
|
+
ambiguousFailureOutcome: 'outcome_unknown',
|
|
1410
|
+
replayPolicy: 'never_auto_retry',
|
|
1411
|
+
reconciliation: 'After an ambiguous dispatch failure, inspect the remote ref/registry/release state before deciding whether to retry.',
|
|
1412
|
+
},
|
|
1158
1413
|
});
|
|
1159
1414
|
}
|
|
1160
1415
|
case 'repository_batch_execute': {
|