@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
|
@@ -49,6 +49,7 @@ export interface SessionCacheSnapshot {
|
|
|
49
49
|
metrics: SessionCacheMetrics;
|
|
50
50
|
fileCount: number;
|
|
51
51
|
searchCount: number;
|
|
52
|
+
structuralCount: number;
|
|
52
53
|
}
|
|
53
54
|
|
|
54
55
|
function shaText(value: string): string {
|
|
@@ -101,9 +102,9 @@ function rangeKey(path: string, start: number, end: number): string {
|
|
|
101
102
|
export class RepositorySessionCache {
|
|
102
103
|
private identity: SessionIdentity;
|
|
103
104
|
private readonly repoRoot: string;
|
|
104
|
-
private readonly fileSha = new Map<string, string>();
|
|
105
105
|
private readonly ranges = new Map<string, FileRangeCacheEntry>();
|
|
106
106
|
private readonly searches = new Map<string, SearchCacheEntry>();
|
|
107
|
+
private readonly structural = new Map<string, { at: number; value: unknown }>();
|
|
107
108
|
private readonly gitSnapshots = new Map<string, { at: number; value: unknown }>();
|
|
108
109
|
private readonly checks = new Map<string, unknown>();
|
|
109
110
|
private runtimeGeneration?: string;
|
|
@@ -141,6 +142,7 @@ export class RepositorySessionCache {
|
|
|
141
142
|
metrics: this.getMetrics(),
|
|
142
143
|
fileCount: this.ranges.size,
|
|
143
144
|
searchCount: this.searches.size,
|
|
145
|
+
structuralCount: this.structural.size,
|
|
144
146
|
};
|
|
145
147
|
}
|
|
146
148
|
|
|
@@ -148,6 +150,10 @@ export class RepositorySessionCache {
|
|
|
148
150
|
return { ...this.identity };
|
|
149
151
|
}
|
|
150
152
|
|
|
153
|
+
repositoryRootPath(): string {
|
|
154
|
+
return this.repoRoot;
|
|
155
|
+
}
|
|
156
|
+
|
|
151
157
|
refreshIdentity(next: SessionIdentity): void {
|
|
152
158
|
const prev = this.identity;
|
|
153
159
|
this.identity = next;
|
|
@@ -178,16 +184,12 @@ export class RepositorySessionCache {
|
|
|
178
184
|
}
|
|
179
185
|
|
|
180
186
|
getFileSha(relativePath: string): string | null {
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
const sha = fileShaOf(absolute);
|
|
188
|
-
if (sha) this.fileSha.set(relativePath, sha);
|
|
189
|
-
this.metrics.cacheMiss += 1;
|
|
190
|
-
return sha;
|
|
187
|
+
// Range reuse is correctness-sensitive: Git identity is intentionally sampled
|
|
188
|
+
// with a short TTL, so it can lag an edit by a few seconds. Hash the current
|
|
189
|
+
// file bytes before serving a cached range instead of trusting an older
|
|
190
|
+
// session-level SHA. Warm filesystem hashing is sub-millisecond for normal
|
|
191
|
+
// source files and still preserves the expensive range/materialization reuse.
|
|
192
|
+
return fileShaOf(join(this.repoRoot, relativePath));
|
|
191
193
|
}
|
|
192
194
|
|
|
193
195
|
/**
|
|
@@ -213,15 +215,16 @@ export class RepositorySessionCache {
|
|
|
213
215
|
entry.path === relativePath
|
|
214
216
|
&& entry.fileSha === sha
|
|
215
217
|
&& entry.startLine <= startLine
|
|
216
|
-
&& entry.endLine >= endLine
|
|
218
|
+
&& (entry.endLine >= endLine || entry.endLine >= entry.totalLines)
|
|
217
219
|
) {
|
|
218
220
|
this.metrics.cacheHit += 1;
|
|
219
221
|
this.metrics.bytesAvoided += Math.max(0, entry.bytes);
|
|
222
|
+
const resolvedEndLine = Math.min(endLine, entry.totalLines);
|
|
220
223
|
return {
|
|
221
224
|
...entry,
|
|
222
225
|
startLine,
|
|
223
|
-
endLine,
|
|
224
|
-
content: sliceNumberedContent(entry.content, entry.startLine, startLine,
|
|
226
|
+
endLine: resolvedEndLine,
|
|
227
|
+
content: sliceNumberedContent(entry.content, entry.startLine, startLine, resolvedEndLine),
|
|
225
228
|
};
|
|
226
229
|
}
|
|
227
230
|
}
|
|
@@ -230,7 +233,6 @@ export class RepositorySessionCache {
|
|
|
230
233
|
}
|
|
231
234
|
|
|
232
235
|
putRange(entry: FileRangeCacheEntry): void {
|
|
233
|
-
this.fileSha.set(entry.path, entry.fileSha);
|
|
234
236
|
this.ranges.set(rangeKey(entry.path, entry.startLine, entry.endLine), entry);
|
|
235
237
|
}
|
|
236
238
|
|
|
@@ -251,6 +253,33 @@ export class RepositorySessionCache {
|
|
|
251
253
|
this.searches.set(key, entry);
|
|
252
254
|
}
|
|
253
255
|
|
|
256
|
+
getStructural(key: string, ttlMs = 30_000): unknown | null {
|
|
257
|
+
const scopedKey = `${this.identity.head}|${this.identity.workingTreeFingerprint}|${key}`;
|
|
258
|
+
const hit = this.structural.get(scopedKey);
|
|
259
|
+
if (hit && Date.now() - hit.at <= ttlMs) {
|
|
260
|
+
this.metrics.cacheHit += 1;
|
|
261
|
+
this.metrics.scanAvoided += 1;
|
|
262
|
+
return hit.value;
|
|
263
|
+
}
|
|
264
|
+
if (hit) this.structural.delete(scopedKey);
|
|
265
|
+
this.metrics.cacheMiss += 1;
|
|
266
|
+
return null;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
/** Inspect structural reuse eligibility without changing request metrics. */
|
|
270
|
+
peekStructural(key: string, ttlMs = 30_000): unknown | null {
|
|
271
|
+
const scopedKey = `${this.identity.head}|${this.identity.workingTreeFingerprint}|${key}`;
|
|
272
|
+
const hit = this.structural.get(scopedKey);
|
|
273
|
+
if (hit && Date.now() - hit.at <= ttlMs) return hit.value;
|
|
274
|
+
if (hit) this.structural.delete(scopedKey);
|
|
275
|
+
return null;
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
putStructural(key: string, value: unknown): void {
|
|
279
|
+
const scopedKey = `${this.identity.head}|${this.identity.workingTreeFingerprint}|${key}`;
|
|
280
|
+
this.structural.set(scopedKey, { at: Date.now(), value });
|
|
281
|
+
}
|
|
282
|
+
|
|
254
283
|
getGitSnapshot(): unknown | null {
|
|
255
284
|
const key = `${this.identity.head}|${this.identity.workingTreeFingerprint}`;
|
|
256
285
|
const hit = this.gitSnapshots.get(key);
|
|
@@ -284,7 +313,6 @@ export class RepositorySessionCache {
|
|
|
284
313
|
|
|
285
314
|
/** Precise invalidation when one file changes. */
|
|
286
315
|
invalidateFile(relativePath: string): void {
|
|
287
|
-
this.fileSha.delete(relativePath);
|
|
288
316
|
for (const key of [...this.ranges.keys()]) {
|
|
289
317
|
if (key.startsWith(`${relativePath}:`)) this.ranges.delete(key);
|
|
290
318
|
}
|
|
@@ -294,9 +322,9 @@ export class RepositorySessionCache {
|
|
|
294
322
|
}
|
|
295
323
|
|
|
296
324
|
invalidateCheckout(): void {
|
|
297
|
-
this.fileSha.clear();
|
|
298
325
|
this.ranges.clear();
|
|
299
326
|
this.searches.clear();
|
|
327
|
+
this.structural.clear();
|
|
300
328
|
this.gitSnapshots.clear();
|
|
301
329
|
this.checks.clear();
|
|
302
330
|
this.metrics.invalidations += 1;
|
|
@@ -304,6 +332,7 @@ export class RepositorySessionCache {
|
|
|
304
332
|
|
|
305
333
|
invalidateHeadRelated(): void {
|
|
306
334
|
this.searches.clear();
|
|
335
|
+
this.structural.clear();
|
|
307
336
|
this.gitSnapshots.clear();
|
|
308
337
|
this.checks.clear();
|
|
309
338
|
this.metrics.invalidations += 1;
|
|
@@ -311,6 +340,7 @@ export class RepositorySessionCache {
|
|
|
311
340
|
|
|
312
341
|
invalidateWorkingTree(): void {
|
|
313
342
|
this.searches.clear();
|
|
343
|
+
this.structural.clear();
|
|
314
344
|
this.gitSnapshots.clear();
|
|
315
345
|
this.checks.clear();
|
|
316
346
|
this.metrics.invalidations += 1;
|
|
@@ -406,6 +436,17 @@ export function pruneSessionCaches(nowMs = Date.now()): number {
|
|
|
406
436
|
}
|
|
407
437
|
|
|
408
438
|
/** Called by MCP transport close/eviction so read caches do not outlive a session. */
|
|
439
|
+
export function invalidateSessionCachesForRepository(repoRoot: string): number {
|
|
440
|
+
const root = resolve(repoRoot);
|
|
441
|
+
let invalidated = 0;
|
|
442
|
+
for (const cache of globalSessions.values()) {
|
|
443
|
+
if (cache.repositoryRootPath() !== root) continue;
|
|
444
|
+
cache.invalidateWorkingTree();
|
|
445
|
+
invalidated += 1;
|
|
446
|
+
}
|
|
447
|
+
return invalidated;
|
|
448
|
+
}
|
|
449
|
+
|
|
409
450
|
export function clearSessionCachesForSession(sessionId: string): number {
|
|
410
451
|
let removed = 0;
|
|
411
452
|
for (const [key, cache] of globalSessions) {
|
package/src/cli/v81-entry.ts
CHANGED
|
@@ -1,11 +1,8 @@
|
|
|
1
1
|
import { buildProgram } from './index';
|
|
2
|
-
import { buildRepositoryCommand } from './commands/repository';
|
|
3
2
|
import { CLI_VERSION } from './commands/status';
|
|
4
3
|
|
|
5
4
|
export function buildV81Program() {
|
|
6
|
-
|
|
7
|
-
program.addCommand(buildRepositoryCommand());
|
|
8
|
-
return program;
|
|
5
|
+
return buildProgram();
|
|
9
6
|
}
|
|
10
7
|
|
|
11
8
|
export async function runV81Cli(argv: string[] = process.argv): Promise<void> {
|
|
@@ -234,6 +234,26 @@ export function atomicWriteFile(
|
|
|
234
234
|
});
|
|
235
235
|
}
|
|
236
236
|
|
|
237
|
+
/** Durable replacement for transaction metadata and rollback restores. Unlike
|
|
238
|
+
* atomicWriteFile, this intentionally does not create another recovery backup. */
|
|
239
|
+
function replaceFileDurably(repoRoot: string, path: string, content: string, mode?: number): void {
|
|
240
|
+
const target = resolveInsideRepo(repoRoot, path);
|
|
241
|
+
if (!target.ok || !target.path) throw new Error(target.error ?? "invalid path");
|
|
242
|
+
const parentError = ensureParent(repoRoot, path);
|
|
243
|
+
if (parentError) throw new Error(parentError);
|
|
244
|
+
const targetPath = target.path;
|
|
245
|
+
withTargetLock(targetPath, () => {
|
|
246
|
+
const tempPath = resolve(dirname(targetPath), `.${basename(targetPath)}.${process.pid}.${Date.now()}.tmp`);
|
|
247
|
+
try {
|
|
248
|
+
writeFileDurably(tempPath, content, mode);
|
|
249
|
+
renameSync(tempPath, targetPath);
|
|
250
|
+
fsyncDirectory(dirname(targetPath));
|
|
251
|
+
} finally {
|
|
252
|
+
rmSync(tempPath, { force: true });
|
|
253
|
+
}
|
|
254
|
+
});
|
|
255
|
+
}
|
|
256
|
+
|
|
237
257
|
export function applyMkdirOperation(repoRoot: string, operation: MkdirOperation, dryRun = false): ApplyOperationResult {
|
|
238
258
|
const target = resolveInsideRepo(repoRoot, operation.path);
|
|
239
259
|
if (!target.ok || !target.path) return failure(operation, target.error ?? "invalid path");
|
|
@@ -311,12 +331,17 @@ export function applyAppendManagedBlockOperation(
|
|
|
311
331
|
}
|
|
312
332
|
}
|
|
313
333
|
|
|
314
|
-
function writeTransactionManifest(
|
|
334
|
+
function writeTransactionManifest(
|
|
335
|
+
plan: AdoptionPlan,
|
|
336
|
+
transactionDir: string,
|
|
337
|
+
createdAt: string,
|
|
338
|
+
results: readonly ApplyOperationResult[],
|
|
339
|
+
): string {
|
|
315
340
|
const manifestPath = `${transactionDir}/manifest.json`;
|
|
316
341
|
const manifest: FsTransactionManifest = {
|
|
317
342
|
protocol: 1,
|
|
318
343
|
command: "adopt",
|
|
319
|
-
createdAt
|
|
344
|
+
createdAt,
|
|
320
345
|
repoRoot: plan.repoRoot,
|
|
321
346
|
mode: plan.mode,
|
|
322
347
|
operations: results.map((result) => {
|
|
@@ -336,7 +361,7 @@ function writeTransactionManifest(plan: AdoptionPlan, transactionDir: string, re
|
|
|
336
361
|
command: `forge adopt rollback --transaction ${manifestPath}`,
|
|
337
362
|
},
|
|
338
363
|
};
|
|
339
|
-
|
|
364
|
+
replaceFileDurably(plan.repoRoot, manifestPath, `${JSON.stringify(manifest, null, 2)}\n`, 0o600);
|
|
340
365
|
return manifestPath;
|
|
341
366
|
}
|
|
342
367
|
|
|
@@ -350,21 +375,64 @@ export function applyAdoptionPlan(plan: AdoptionPlan, dryRun = false): ApplyAdop
|
|
|
350
375
|
};
|
|
351
376
|
}
|
|
352
377
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
return
|
|
358
|
-
|
|
359
|
-
return
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
378
|
+
if (dryRun) {
|
|
379
|
+
const results = plan.operations.map((operation) => {
|
|
380
|
+
switch (operation.kind) {
|
|
381
|
+
case "mkdir": return applyMkdirOperation(plan.repoRoot, operation, true);
|
|
382
|
+
case "writeFile": return applyWriteFileOperation(plan.repoRoot, operation, true);
|
|
383
|
+
case "appendManagedBlock": return applyAppendManagedBlockOperation(plan.repoRoot, operation, true);
|
|
384
|
+
default: return failure(operation, `unsupported operation kind: ${operation.kind}`);
|
|
385
|
+
}
|
|
386
|
+
});
|
|
387
|
+
return { ok: results.every((result) => result.status !== "failed"), dryRun: true, results };
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
const transactionDir = transactionDirFor();
|
|
391
|
+
const createdAt = new Date().toISOString();
|
|
392
|
+
const results: ApplyOperationResult[] = [];
|
|
393
|
+
let transactionManifestPath: string;
|
|
394
|
+
try {
|
|
395
|
+
// Establish recovery evidence before the first adoption operation mutates
|
|
396
|
+
// repository state. Each completed/failed operation is checkpointed below.
|
|
397
|
+
transactionManifestPath = writeTransactionManifest(plan, transactionDir, createdAt, results);
|
|
398
|
+
} catch (error) {
|
|
399
|
+
return {
|
|
400
|
+
ok: false,
|
|
401
|
+
dryRun: false,
|
|
402
|
+
results: [{
|
|
403
|
+
id: "transaction-manifest",
|
|
404
|
+
kind: "runCheck",
|
|
405
|
+
status: "failed",
|
|
406
|
+
error: `failed to initialize transaction manifest: ${errorMessage(error)}`,
|
|
407
|
+
}],
|
|
408
|
+
};
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
for (const operation of plan.operations) {
|
|
412
|
+
const result = (() => {
|
|
413
|
+
switch (operation.kind) {
|
|
414
|
+
case "mkdir": return applyMkdirOperation(plan.repoRoot, operation, false);
|
|
415
|
+
case "writeFile": return applyWriteFileOperation(plan.repoRoot, operation, false, transactionDir);
|
|
416
|
+
case "appendManagedBlock": return applyAppendManagedBlockOperation(plan.repoRoot, operation, false, transactionDir);
|
|
417
|
+
default: return failure(operation, `unsupported operation kind: ${operation.kind}`);
|
|
418
|
+
}
|
|
419
|
+
})();
|
|
420
|
+
results.push(result);
|
|
421
|
+
try {
|
|
422
|
+
writeTransactionManifest(plan, transactionDir, createdAt, results);
|
|
423
|
+
} catch (error) {
|
|
424
|
+
results.push({
|
|
425
|
+
id: "transaction-manifest",
|
|
426
|
+
kind: "runCheck",
|
|
427
|
+
status: "failed",
|
|
428
|
+
error: `failed to checkpoint transaction manifest: ${errorMessage(error)}`,
|
|
429
|
+
});
|
|
430
|
+
break;
|
|
364
431
|
}
|
|
365
|
-
|
|
432
|
+
if (result.status === "failed") break;
|
|
433
|
+
}
|
|
434
|
+
|
|
366
435
|
const ok = results.every((result) => result.status !== "failed");
|
|
367
|
-
const transactionManifestPath = !dryRun && ok && transactionDir ? writeTransactionManifest(plan, transactionDir, results) : undefined;
|
|
368
436
|
|
|
369
437
|
return {
|
|
370
438
|
ok,
|
|
@@ -413,7 +481,7 @@ function rollbackFileOperation(repoRoot: string, operation: FsTransactionManifes
|
|
|
413
481
|
return rollbackFailed(operation, "restore_backup", "current file hash differs from transaction content hash");
|
|
414
482
|
}
|
|
415
483
|
}
|
|
416
|
-
|
|
484
|
+
replaceFileDurably(repoRoot, operation.path, readFileSync(backup.path, "utf-8"));
|
|
417
485
|
return { id: operation.id, kind: operation.kind, path: operation.path, status: "rolled_back", action: "restore_backup" };
|
|
418
486
|
} catch (error) {
|
|
419
487
|
return rollbackFailed(operation, "restore_backup", errorMessage(error));
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
-
import { spawnSync } from 'child_process';
|
|
1
|
+
import { spawn, spawnSync, type ChildProcessWithoutNullStreams } from 'child_process';
|
|
2
2
|
import { accessSync, constants, existsSync } from 'fs';
|
|
3
3
|
import { createRequire } from 'module';
|
|
4
4
|
import { dirname, join, resolve } from 'path';
|
|
5
|
+
import { createInterface, type Interface as ReadlineInterface } from 'readline';
|
|
5
6
|
import { fileURLToPath } from 'url';
|
|
6
7
|
|
|
7
8
|
const require = createRequire(import.meta.url);
|
|
@@ -10,6 +11,7 @@ const MAX_REQUEST_BYTES = 64 * 1024;
|
|
|
10
11
|
const MAX_RESPONSE_BYTES = 1024 * 1024;
|
|
11
12
|
const MAX_STDERR_CHARS = 8_000;
|
|
12
13
|
const DEFAULT_TIMEOUT_MS = 15_000;
|
|
14
|
+
const PERSISTENT_SIDECAR_IDLE_MS = 30_000;
|
|
13
15
|
const ALLOWED_OPERATIONS = new Set<CodeGraphReadOperation>(['status', 'search', 'context', 'impact', 'file_dependencies']);
|
|
14
16
|
|
|
15
17
|
export type CodeGraphReadOperation = 'status' | 'search' | 'context' | 'impact' | 'file_dependencies';
|
|
@@ -132,7 +134,8 @@ export function resolveCodeGraphBundledRuntime(options: { runtimeRoot?: string }
|
|
|
132
134
|
} {
|
|
133
135
|
const target = `${process.platform}-${process.arch}`;
|
|
134
136
|
const platformPackage = `@colbymchenry/codegraph-${target}`;
|
|
135
|
-
const
|
|
137
|
+
const configuredReleaseRoot = process.env.FORGE_RELEASE_PATH?.trim();
|
|
138
|
+
const runtimeRoot = options.runtimeRoot ?? (configuredReleaseRoot ? resolve(configuredReleaseRoot) : dirname(process.execPath));
|
|
136
139
|
const releaseNode = join(runtimeRoot, 'codegraph-node');
|
|
137
140
|
const releaseSidecar = join(runtimeRoot, 'codegraph-sidecar.cjs');
|
|
138
141
|
const releaseLibrary = join(runtimeRoot, 'codegraph-lib', 'dist', 'index.js');
|
|
@@ -345,3 +348,231 @@ export function queryCodeGraphReadProvider(
|
|
|
345
348
|
},
|
|
346
349
|
};
|
|
347
350
|
}
|
|
351
|
+
|
|
352
|
+
interface PersistentCodeGraphSession {
|
|
353
|
+
repoRoot: string;
|
|
354
|
+
runtimeKey: string;
|
|
355
|
+
child: ChildProcessWithoutNullStreams;
|
|
356
|
+
reader: ReadlineInterface;
|
|
357
|
+
lines: AsyncIterator<string>;
|
|
358
|
+
stderr: string;
|
|
359
|
+
closed: boolean;
|
|
360
|
+
idleTimer?: NodeJS.Timeout;
|
|
361
|
+
queue: Promise<void>;
|
|
362
|
+
}
|
|
363
|
+
|
|
364
|
+
const persistentSessions = new Map<string, PersistentCodeGraphSession>();
|
|
365
|
+
let persistentSessionSpawns = 0;
|
|
366
|
+
let persistentSessionReuses = 0;
|
|
367
|
+
let persistentSessionRequests = 0;
|
|
368
|
+
|
|
369
|
+
function persistentRuntimeKey(runtime: ReturnType<typeof resolveCodeGraphBundledRuntime>): string {
|
|
370
|
+
return `${runtime.nodeExecutable}\u0000${runtime.sidecarPath}\u0000${runtime.libraryPath}`;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
function closePersistentSessionNow(session: PersistentCodeGraphSession): void {
|
|
374
|
+
if (session.closed) return;
|
|
375
|
+
session.closed = true;
|
|
376
|
+
if (session.idleTimer) clearTimeout(session.idleTimer);
|
|
377
|
+
if (persistentSessions.get(session.repoRoot) === session) persistentSessions.delete(session.repoRoot);
|
|
378
|
+
try { session.reader.close(); } catch {}
|
|
379
|
+
try { session.child.stdin.end(); } catch {}
|
|
380
|
+
try { session.child.kill(); } catch {}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function schedulePersistentSessionIdle(session: PersistentCodeGraphSession): void {
|
|
384
|
+
if (session.idleTimer) clearTimeout(session.idleTimer);
|
|
385
|
+
const timer = setTimeout(() => closePersistentSessionNow(session), PERSISTENT_SIDECAR_IDLE_MS);
|
|
386
|
+
timer.unref();
|
|
387
|
+
session.idleTimer = timer;
|
|
388
|
+
}
|
|
389
|
+
|
|
390
|
+
function createPersistentSession(
|
|
391
|
+
repoRoot: string,
|
|
392
|
+
runtime: ReturnType<typeof resolveCodeGraphBundledRuntime>,
|
|
393
|
+
): PersistentCodeGraphSession {
|
|
394
|
+
const child = spawn(runtime.nodeExecutable, [runtime.sidecarPath], {
|
|
395
|
+
cwd: repoRoot,
|
|
396
|
+
env: { ...minimalEnvironment(runtime), FORGE_CODEGRAPH_PERSISTENT: '1' },
|
|
397
|
+
stdio: ['pipe', 'pipe', 'pipe'],
|
|
398
|
+
windowsHide: true,
|
|
399
|
+
shell: false,
|
|
400
|
+
});
|
|
401
|
+
child.unref();
|
|
402
|
+
const reader = createInterface({ input: child.stdout, crlfDelay: Infinity });
|
|
403
|
+
const session: PersistentCodeGraphSession = {
|
|
404
|
+
repoRoot,
|
|
405
|
+
runtimeKey: persistentRuntimeKey(runtime),
|
|
406
|
+
child,
|
|
407
|
+
reader,
|
|
408
|
+
lines: reader[Symbol.asyncIterator](),
|
|
409
|
+
stderr: '',
|
|
410
|
+
closed: false,
|
|
411
|
+
queue: Promise.resolve(),
|
|
412
|
+
};
|
|
413
|
+
child.stderr.on('data', (chunk: Buffer) => {
|
|
414
|
+
if (session.stderr.length >= MAX_STDERR_CHARS) return;
|
|
415
|
+
session.stderr += chunk.toString('utf8').slice(0, MAX_STDERR_CHARS - session.stderr.length);
|
|
416
|
+
});
|
|
417
|
+
child.on('error', () => closePersistentSessionNow(session));
|
|
418
|
+
child.on('close', () => {
|
|
419
|
+
session.closed = true;
|
|
420
|
+
if (session.idleTimer) clearTimeout(session.idleTimer);
|
|
421
|
+
if (persistentSessions.get(repoRoot) === session) persistentSessions.delete(repoRoot);
|
|
422
|
+
});
|
|
423
|
+
persistentSessions.set(repoRoot, session);
|
|
424
|
+
persistentSessionSpawns += 1;
|
|
425
|
+
return session;
|
|
426
|
+
}
|
|
427
|
+
|
|
428
|
+
function getPersistentSession(
|
|
429
|
+
repoRoot: string,
|
|
430
|
+
runtime: ReturnType<typeof resolveCodeGraphBundledRuntime>,
|
|
431
|
+
): PersistentCodeGraphSession {
|
|
432
|
+
const key = persistentRuntimeKey(runtime);
|
|
433
|
+
const current = persistentSessions.get(repoRoot);
|
|
434
|
+
if (current && !current.closed && current.runtimeKey === key) {
|
|
435
|
+
if (current.idleTimer) clearTimeout(current.idleTimer);
|
|
436
|
+
current.idleTimer = undefined;
|
|
437
|
+
persistentSessionReuses += 1;
|
|
438
|
+
return current;
|
|
439
|
+
}
|
|
440
|
+
if (current) closePersistentSessionNow(current);
|
|
441
|
+
return createPersistentSession(repoRoot, runtime);
|
|
442
|
+
}
|
|
443
|
+
|
|
444
|
+
async function serializedPersistentRequest<T>(session: PersistentCodeGraphSession, operation: () => Promise<T>): Promise<T> {
|
|
445
|
+
const previous = session.queue;
|
|
446
|
+
let release!: () => void;
|
|
447
|
+
session.queue = new Promise<void>((resolveQueue) => { release = resolveQueue; });
|
|
448
|
+
await previous.catch(() => undefined);
|
|
449
|
+
try {
|
|
450
|
+
return await operation();
|
|
451
|
+
} finally {
|
|
452
|
+
release();
|
|
453
|
+
}
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
async function nextPersistentLine(session: PersistentCodeGraphSession, timeoutMs: number): Promise<string> {
|
|
457
|
+
let timer: NodeJS.Timeout | undefined;
|
|
458
|
+
try {
|
|
459
|
+
const timeout = new Promise<never>((_, reject) => {
|
|
460
|
+
timer = setTimeout(() => reject(new Error('CODEGRAPH_TIMEOUT')), timeoutMs);
|
|
461
|
+
timer.unref();
|
|
462
|
+
});
|
|
463
|
+
const next = await Promise.race([session.lines.next(), timeout]);
|
|
464
|
+
if (next.done || typeof next.value !== 'string') {
|
|
465
|
+
throw new Error(session.stderr || 'CODEGRAPH_SIDECAR_CLOSED');
|
|
466
|
+
}
|
|
467
|
+
return next.value;
|
|
468
|
+
} finally {
|
|
469
|
+
if (timer) clearTimeout(timer);
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
export async function closeCodeGraphReadProviderSessions(): Promise<void> {
|
|
474
|
+
const sessions = [...persistentSessions.values()];
|
|
475
|
+
persistentSessions.clear();
|
|
476
|
+
for (const session of sessions) closePersistentSessionNow(session);
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
export async function clearCodeGraphReadProviderSessionsForTest(): Promise<void> {
|
|
480
|
+
await closeCodeGraphReadProviderSessions();
|
|
481
|
+
persistentSessionSpawns = 0;
|
|
482
|
+
persistentSessionReuses = 0;
|
|
483
|
+
persistentSessionRequests = 0;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
export function codeGraphReadProviderSessionSnapshotForTest(): {
|
|
487
|
+
active: number;
|
|
488
|
+
spawns: number;
|
|
489
|
+
reuses: number;
|
|
490
|
+
requests: number;
|
|
491
|
+
} {
|
|
492
|
+
return {
|
|
493
|
+
active: persistentSessions.size,
|
|
494
|
+
spawns: persistentSessionSpawns,
|
|
495
|
+
reuses: persistentSessionReuses,
|
|
496
|
+
requests: persistentSessionRequests,
|
|
497
|
+
};
|
|
498
|
+
}
|
|
499
|
+
|
|
500
|
+
/** Async sidecar path used by the first broad Context Plane fan-in. */
|
|
501
|
+
export async function queryCodeGraphReadProviderAsync(
|
|
502
|
+
repoRoot: string,
|
|
503
|
+
request: CodeGraphReadRequest,
|
|
504
|
+
options: { timeoutMs?: number } = {},
|
|
505
|
+
): Promise<CodeGraphReadProviderResponse> {
|
|
506
|
+
const startedAt = performance.now();
|
|
507
|
+
if (!ALLOWED_OPERATIONS.has(request.operation)) {
|
|
508
|
+
return errorResponse(request.operation, 'CODEGRAPH_OPERATION_NOT_ALLOWED', `Unsupported read operation: ${String(request.operation)}`, performance.now() - startedAt);
|
|
509
|
+
}
|
|
510
|
+
let runtime: ReturnType<typeof resolveCodeGraphBundledRuntime>;
|
|
511
|
+
try { runtime = resolveCodeGraphBundledRuntime(); }
|
|
512
|
+
catch (error) {
|
|
513
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
514
|
+
return errorResponse(request.operation, message.split(':', 1)[0] || 'CODEGRAPH_RUNTIME_UNAVAILABLE', message, performance.now() - startedAt);
|
|
515
|
+
}
|
|
516
|
+
const resolvedRepoRoot = resolve(repoRoot);
|
|
517
|
+
const input = JSON.stringify({
|
|
518
|
+
schemaVersion: SCHEMA_VERSION,
|
|
519
|
+
operation: request.operation,
|
|
520
|
+
projectRoot: resolvedRepoRoot,
|
|
521
|
+
...(typeof request.query === 'string' ? { query: request.query.slice(0, 2_000) } : {}),
|
|
522
|
+
...(typeof request.nodeId === 'string' ? { nodeId: request.nodeId.slice(0, 1_000) } : {}),
|
|
523
|
+
...(typeof request.filePath === 'string' ? { filePath: request.filePath.slice(0, 2_000) } : {}),
|
|
524
|
+
limit: boundedInteger(request.limit, 12, 1, 40),
|
|
525
|
+
maxNodes: boundedInteger(request.maxNodes, 40, 1, 80),
|
|
526
|
+
maxDepth: boundedInteger(request.maxDepth, 2, 1, 5),
|
|
527
|
+
});
|
|
528
|
+
if (Buffer.byteLength(input) > MAX_REQUEST_BYTES) {
|
|
529
|
+
return errorResponse(request.operation, 'CODEGRAPH_REQUEST_TOO_LARGE', 'CodeGraph request exceeded the bounded input limit.', performance.now() - startedAt);
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
let session = getPersistentSession(resolvedRepoRoot, runtime);
|
|
533
|
+
return await serializedPersistentRequest(session, async () => {
|
|
534
|
+
if (session.closed) session = getPersistentSession(resolvedRepoRoot, runtime);
|
|
535
|
+
const processStartedAt = performance.now();
|
|
536
|
+
persistentSessionRequests += 1;
|
|
537
|
+
try {
|
|
538
|
+
await new Promise<void>((resolveWrite, rejectWrite) => {
|
|
539
|
+
session.child.stdin.write(`${input}\n`, (error) => error ? rejectWrite(error) : resolveWrite());
|
|
540
|
+
});
|
|
541
|
+
const raw = await nextPersistentLine(
|
|
542
|
+
session,
|
|
543
|
+
boundedInteger(options.timeoutMs, DEFAULT_TIMEOUT_MS, 1_000, 60_000),
|
|
544
|
+
);
|
|
545
|
+
if (Buffer.byteLength(raw) > MAX_RESPONSE_BYTES) {
|
|
546
|
+
closePersistentSessionNow(session);
|
|
547
|
+
return errorResponse(request.operation, 'CODEGRAPH_RESPONSE_TOO_LARGE', 'CodeGraph response exceeded the bounded output limit.', performance.now() - startedAt);
|
|
548
|
+
}
|
|
549
|
+
const parsed = parseSidecarResponse(raw);
|
|
550
|
+
if (!parsed.ok) {
|
|
551
|
+
schedulePersistentSessionIdle(session);
|
|
552
|
+
return errorResponse(request.operation, parsed.error.code, parsed.error.message, performance.now() - startedAt);
|
|
553
|
+
}
|
|
554
|
+
const metadata = filterGitIgnoredCodeGraphChanges(resolvedRepoRoot, parsed.metadata);
|
|
555
|
+
schedulePersistentSessionIdle(session);
|
|
556
|
+
return {
|
|
557
|
+
schemaVersion: 1,
|
|
558
|
+
provider: 'codegraph',
|
|
559
|
+
operation: request.operation,
|
|
560
|
+
ok: metadata.initialized,
|
|
561
|
+
status: providerStatus(metadata),
|
|
562
|
+
metadata,
|
|
563
|
+
result: parsed.result,
|
|
564
|
+
timingsMs: {
|
|
565
|
+
total: Math.round((performance.now() - startedAt) * 100) / 100,
|
|
566
|
+
process: Math.round((performance.now() - processStartedAt) * 100) / 100,
|
|
567
|
+
...(typeof parsed.timingsMs?.open === 'number' ? { open: parsed.timingsMs.open } : {}),
|
|
568
|
+
...(typeof parsed.timingsMs?.query === 'number' ? { query: parsed.timingsMs.query } : {}),
|
|
569
|
+
},
|
|
570
|
+
};
|
|
571
|
+
} catch (error) {
|
|
572
|
+
closePersistentSessionNow(session);
|
|
573
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
574
|
+
const code = message === 'CODEGRAPH_TIMEOUT' ? 'CODEGRAPH_TIMEOUT' : 'CODEGRAPH_SIDECAR_FAILED';
|
|
575
|
+
return errorResponse(request.operation, code, message, performance.now() - startedAt);
|
|
576
|
+
}
|
|
577
|
+
});
|
|
578
|
+
}
|