@moretea-labs/forge 1.8.1 → 1.8.2
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 +5 -2
- package/CHANGELOG.md +20 -0
- package/README.en.md +24 -3
- package/README.md +10 -6
- package/README.zh-CN.md +5 -3
- package/THIRD_PARTY_NOTICES.md +1 -2
- package/adapters/chatgpt/browser-delivery-runtime.ts +5 -3
- package/adapters/chatgpt/provider-delivery.ts +1 -1
- package/adapters/chatgpt/work-conversation-binding-store.ts +20 -1
- package/adapters/computer/chatgpt-extension-target.ts +295 -50
- package/adapters/computer/chrome-extension/background.js +227 -71
- package/adapters/computer/chrome-extension/content.js +181 -157
- package/adapters/computer/chrome-extension/manifest.json +3 -1
- package/adapters/computer/desktop-operator-provider.ts +18 -6
- package/adapters/computer/interaction-target-authority.ts +39 -9
- package/adapters/mcp/access-mode.ts +18 -39
- package/adapters/mcp/auth.ts +2 -6
- package/adapters/mcp/instructions.ts +2 -2
- package/adapters/mcp/multi-repository.ts +7 -28
- package/adapters/mcp/runtime-gateway/context-adapter.ts +79 -40
- package/adapters/mcp/runtime-gateway/context-pack-sidecar.ts +7 -15
- package/adapters/mcp/runtime-gateway/execution-tools.ts +7 -13
- package/adapters/mcp/runtime-gateway/plugin-adapter.ts +16 -3
- package/adapters/mcp/runtime-gateway/process-tools.ts +3 -21
- package/adapters/mcp/runtime-gateway/router.ts +3 -5
- package/adapters/mcp/runtime-gateway/routing-policy.ts +17 -10
- package/adapters/mcp/runtime-gateway/runtime-observation-adapter.ts +1 -1
- package/adapters/mcp/runtime-gateway/runtime-tool-definitions.ts +14 -9
- package/adapters/mcp/runtime-gateway/runtime-tools.ts +1 -5
- package/adapters/mcp/runtime-gateway/scheduler-adapter.ts +36 -1
- package/adapters/mcp/runtime-gateway/status-inbox-adapter.ts +58 -4
- package/adapters/mcp/runtime-gateway/typescript-navigation-sidecar.ts +7 -3
- package/adapters/mcp/runtime-gateway/work-adapter.ts +16 -16
- package/adapters/mcp/runtime-gateway/work-input-wire-migration.ts +0 -17
- package/adapters/mcp/runtime-gateway/work-semantic-operations.ts +69 -12
- package/adapters/mcp/runtime-gateway/workflow-supervisor-adapter.ts +26 -2
- package/adapters/mcp/server.ts +24 -3
- package/adapters/mcp/setup.ts +4 -10
- package/adapters/mcp/tool-mapping/access-tools.ts +0 -6
- package/adapters/mcp/tool-mapping/legacy-tool-service.ts +672 -2935
- package/adapters/mcp/tool-mapping/post-finalize-work-attribution.ts +1 -1
- package/adapters/mcp/tool-mapping/repository-tools.ts +52 -86
- package/adapters/mcp/toolset-names.ts +11 -42
- package/adapters/mcp/toolset.ts +16 -90
- package/{src/cli → adapters}/mcp/transports/AGENTS.md +5 -5
- package/{src/cli → adapters}/mcp/transports/CLAUDE.md +5 -5
- package/adapters/mcp/transports/http-observation.ts +18 -6
- package/adapters/mcp/transports/http-post-lifecycle.ts +19 -0
- package/adapters/mcp/transports/http.ts +54 -14
- package/adapters/mcp/types.ts +0 -6
- package/assets/hooks/lib/session-state.sh +0 -38
- package/assets/hooks/post-bash.sh +1 -1
- package/assets/hooks/post-tool-observer.sh +0 -5
- package/assets/hooks/prompt-guard.sh +0 -55
- package/assets/partials/04-project-structure.partial.md +1 -1
- package/assets/partials/07-footer.partial.md +1 -1
- package/assets/partials-agents/02-operating-mode.partial.md +1 -1
- package/assets/partials-agents/08-deep-docs.partial.md +1 -1
- package/assets/reference-configs/agentic-development-flow.md +1 -1
- package/assets/reference-configs/external-tooling.md +0 -76
- package/assets/reference-configs/release-deploy.md +1 -1
- package/assets/templates/helpers/architecture-queue.sh +1 -1
- package/assets/templates/helpers/check-agent-tooling.sh +1 -327
- package/assets/templates/helpers/check-task-workflow.sh +1 -1
- package/assets/templates/helpers/ensure-task-workflow.sh +0 -14
- package/assets/templates/helpers/migrate-project-template.sh +2 -2
- package/assets/workflow-contract.v1.json +0 -2
- package/docs/README.md +5 -2
- package/docs/architecture/CURRENT.md +114 -30
- package/docs/architecture/decisions/20260904-ios-device-migration-baseline.md +42 -0
- package/docs/architecture/decisions/20260918-stable-candidate-release-lifecycle.md +15 -2
- package/docs/architecture/decisions/20260924-thin-semantic-working-context.md +4 -2
- package/docs/architecture/decisions/20261001-supervisor-turn-authority.md +109 -4
- package/docs/architecture/decisions/20261009-exact-code-context-discovery.md +19 -0
- package/docs/architecture/decisions/20261009-generated-xcode-cache-retention.md +37 -0
- package/docs/architecture/decisions/20261011-autonomous-continuation-convergence.md +597 -0
- package/docs/architecture/decisions/20261017-edit-session-finalization-reconciliation.md +17 -0
- package/docs/architecture/decisions/20261019-retire-host-skills.md +16 -0
- package/docs/architecture/diagrams/agentic-dev-plugin-architecture.html +1 -1
- package/docs/architecture/domains/public-surface.md +3 -5
- package/docs/architecture/domains/verification.md +5 -5
- package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +2 -2
- package/docs/architecture/requests/2026-09-04-computer-plugin-workflow-taxonomy.md +4 -4
- package/docs/architecture/reviews/20261015-code-search-runtime-acceptance.md +75 -0
- package/docs/forge-plugin-management.md +14 -2
- package/docs/operations/controller-browser-plugin.md +23 -0
- package/docs/operations/features.md +6 -7
- package/docs/operations/features.zh-CN.md +1 -1
- package/docs/operations/github-repository.md +4 -4
- package/docs/operations/github-semantic-release-projection.md +20 -0
- package/docs/operations/homebrew.md +2 -2
- package/docs/operations/mcp-tool-exposure.md +13 -54
- package/docs/operations/runtime-performance-diagnostics.md +8 -0
- package/docs/operations/standalone-disaster-recovery.md +1 -1
- package/docs/tutorials/01-install-and-start.zh-CN.md +1 -1
- package/docs/tutorials/03-first-repository-task.md +2 -0
- package/docs/wiki/Computer-and-Plugins.md +78 -0
- package/docs/wiki/Computer-and-Plugins.zh-CN.md +88 -0
- package/docs/wiki/Home.md +16 -2
- package/docs/wiki/Implementation.md +24 -12
- package/docs/wiki/Installation.md +28 -32
- package/docs/wiki/Integrations.md +4 -0
- package/docs/wiki/Quick-Start.md +35 -22
- package/docs/wiki/Releases-and-Upgrades.md +15 -25
- package/docs/wiki/_Sidebar.md +5 -1
- package/package.json +1 -12
- package/packages/kernel/controller/infrastructure/controller-session-store.ts +63 -38
- package/packages/kernel/identity/infrastructure/activity-projection-store.ts +10 -0
- package/packages/kernel/scheduler/application/eligibility.ts +4 -1
- package/packages/kernel/scheduler/infrastructure/schedule-store.ts +13 -10
- package/packages/kernel/work/application/work-service.ts +1 -0
- package/packages/kernel/work/domain/repository-completion-receipt.ts +1 -1
- package/packages/kernel/work/domain/state-machine.ts +9 -2
- package/packages/kernel/work/infrastructure/work-contract-store.ts +28 -1
- package/packages/plugin-runtime/browser/provider-registry.ts +12 -36
- package/packages/plugin-runtime/computer/conversation-target.ts +17 -7
- package/scripts/assemble-template.ts +11 -72
- package/scripts/check-mcp-compatibility.ts +55 -74
- package/scripts/check-package-identity.mjs +0 -5
- package/scripts/check-runtime-architecture.mjs +305 -28
- package/scripts/init-project.sh +0 -46
- package/scripts/install-ios-hid-toolchain.sh +60 -0
- package/scripts/install-standalone-recovery.ts +1 -1
- package/scripts/lib/project-init-lib.sh +3 -3
- package/scripts/migrate-windows-wsl-controller-home.ts +2 -0
- package/scripts/open-source-audit-allowlist.txt +0 -1
- package/scripts/run-governed-gate.ts +2 -0
- package/scripts/smoke-ios-physical-device.ts +38 -10
- package/scripts/smoke-mcp-http-runtime.ts +7 -8
- package/scripts/smoke-mcp-tool-surface.ts +27 -41
- package/scripts/smoke-workflow-supervisor-chrome-adapter.ts +6 -3
- package/scripts/sync-generated-authority.mjs +0 -1
- package/src/cli/agent-jobs/executor-health.ts +1 -1
- package/src/cli/agent-jobs/integration.ts +2 -2
- package/src/cli/chatgpt-browser/file-policy.ts +2 -2
- package/src/cli/commands/doctor.ts +1 -143
- package/src/cli/commands/global-runtime.ts +3 -196
- package/src/cli/commands/init-hook.ts +6 -23
- package/src/cli/commands/init.ts +3 -252
- package/src/cli/commands/mcp.ts +0 -5
- package/src/cli/commands/prompt-guard-decision.ts +0 -2
- package/src/cli/commands/recovery.ts +2 -14
- package/src/cli/commands/repository.ts +17 -1
- package/src/cli/commands/setup-execution.ts +41 -0
- package/src/cli/controller/completion-orchestrator.ts +1 -1
- package/src/cli/controller/composite-operations.ts +1 -1
- package/src/cli/controller/context/instruction-context.ts +1 -1
- package/src/cli/controller/context/known-paths.ts +2 -2
- package/src/cli/controller/context/query-planning.ts +11 -4
- package/src/cli/controller/context/rule-resolution.ts +2 -2
- package/src/cli/controller/context/source-materializer.ts +2 -2
- package/src/cli/controller/context/structural-provider.ts +84 -0
- package/src/cli/controller/context/structural.ts +6 -6
- package/src/cli/controller/context/types.ts +8 -9
- package/src/cli/controller/context-pack.ts +97 -76
- package/src/cli/controller/operational-plan.ts +1 -1
- package/src/cli/controller/systemd-user.ts +4 -10
- package/src/cli/editing/edit-session.ts +17 -5
- package/src/cli/github/github.ts +4 -4
- package/src/cli/github/semantic-projection.ts +174 -0
- package/src/cli/hook/prompt-intents.ts +0 -9
- package/src/cli/index.ts +1 -43
- package/src/cli/local-bridge/connector-freshness.ts +1 -12
- package/src/cli/local-bridge/console-automations.ts +1 -1
- package/src/cli/local-bridge/console-view-models.ts +1 -1
- package/src/cli/local-bridge/facade-api.ts +0 -2
- package/src/cli/repositories/command-classifier.ts +0 -9
- package/src/cli/repositories/command-scope.ts +2 -2
- package/src/cli/repositories/locks.ts +20 -0
- package/src/cli/repositories/registry.ts +2 -1
- package/src/cli/repositories/safe-patch.ts +2 -12
- package/src/cli/repositories/structured-git.ts +39 -3
- package/src/cli/repository/inspector.ts +185 -343
- package/src/effects/process-runner.ts +17 -1
- package/src/runtime/context/generic-lsp-provider.ts +1 -1
- package/src/runtime/context/semantic-navigation-contract.ts +7 -0
- package/src/runtime/context/semantic-navigation.ts +1 -1
- package/src/runtime/control-plane/cleanup-artifact-retention.ts +4 -4
- package/src/runtime/control-plane/execution/work-delivery-service.ts +150 -31
- package/src/runtime/control-plane/execution/work-handle-authority.ts +12 -4
- package/src/runtime/control-plane/execution/work-handle-store.ts +53 -1
- package/src/runtime/control-plane/execution/work-operation-service.ts +20 -10
- package/src/runtime/control-plane/execution/work-preparation-service.ts +108 -4
- package/src/runtime/control-plane/execution/work-process-evidence.ts +2 -2
- package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +28 -13
- package/src/runtime/control-plane/execution/work-validation-reconciler.ts +5 -1
- package/src/runtime/control-plane/execution/work-verification-snapshot.ts +1 -1
- package/src/runtime/control-plane/facade/capability-registry.ts +2 -0
- package/src/runtime/control-plane/facade/work-continuation.ts +1 -1
- package/src/runtime/control-plane/generated-cache-retention.ts +114 -63
- package/src/runtime/control-plane/global-scheduler/maintenance.ts +20 -8
- package/src/runtime/control-plane/global-scheduler/reconciliation.ts +1 -11
- package/src/runtime/control-plane/governance/external-effects.ts +1 -1
- package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +1 -0
- package/src/runtime/control-plane/runtime-cleanup.ts +12 -93
- package/src/runtime/evidence/sensitive-output.ts +22 -2
- package/src/runtime/execution/jobs/child-reference.ts +11 -0
- package/src/runtime/execution/jobs/receipt-recovery.ts +1 -11
- package/src/runtime/execution/process-runtime/command-facade.ts +3 -3
- package/src/runtime/execution/process-runtime/runtime.ts +20 -16
- package/src/runtime/execution/thin-harness/execution-router.ts +16 -16
- package/src/runtime/execution/thin-harness/fast-executor.ts +2 -2
- package/src/runtime/execution/thin-harness/search-worker.ts +2 -2
- package/src/runtime/execution/workers/executor.ts +1 -2
- package/src/runtime/maintenance/cleanup.ts +41 -5
- package/src/runtime/plugins/action-application.ts +12 -1
- package/src/runtime/plugins/app-store-connect-adapter.ts +252 -4
- package/src/runtime/plugins/browser-adapter.ts +381 -313
- package/src/runtime/plugins/browser-macos-bridge.ts +114 -112
- package/src/runtime/plugins/browser-manifest-surface.ts +2 -1
- package/src/runtime/plugins/browser-registration.ts +2 -0
- package/src/runtime/plugins/browser-runtime.ts +26 -35
- package/src/runtime/plugins/computer-chatgpt-browser-adapter-installer.ts +11 -2
- package/src/runtime/plugins/computer-registration.ts +76 -9
- package/src/runtime/plugins/github-adapter.ts +30 -3
- package/src/runtime/plugins/gmail-adapter.ts +166 -1
- package/src/runtime/plugins/google-shared.ts +10 -5
- package/src/runtime/plugins/ios/remote-xpc-hid.ts +70 -30
- package/src/runtime/plugins/ios-adapter.ts +45 -40
- package/src/runtime/plugins/ios-agent-device.ts +54 -4
- package/src/runtime/plugins/ios-device-ownership.ts +103 -0
- package/src/runtime/plugins/ios-physical-device.ts +497 -79
- package/src/runtime/plugins/local-system-adapter.ts +1 -99
- package/src/runtime/plugins/store.ts +80 -7
- package/src/runtime/plugins/types.ts +4 -0
- package/src/runtime/projections/git-status-sampler.ts +2 -19
- package/src/runtime/recovery/actions.ts +1 -1
- package/src/runtime/recovery/lifecycle-inventory.ts +1 -11
- package/src/runtime/recovery/probe.ts +2 -2
- package/src/runtime/release/release-coordinator.ts +20 -4
- package/src/runtime/root/controller-progression-composition.ts +67 -5
- package/src/runtime/root/package-connector-service.ts +123 -11
- package/src/runtime/root/package-runtime-release.ts +6 -18
- package/src/runtime/root/package-runtime-service.ts +7 -13
- package/src/runtime/root/release-execution-canary.ts +18 -5
- package/src/runtime/root/release-manifest.ts +0 -34
- package/src/runtime/root/release-materialize.ts +0 -78
- package/src/runtime/root/runtime.ts +1 -2
- package/src/runtime/root/scheduled-controller-composition.ts +2 -1
- package/src/runtime/root/service.ts +90 -11
- package/src/runtime/root/types.ts +0 -6
- package/src/runtime/root/workflow-supervisor-composition.ts +207 -19
- package/src/runtime/root/workflow-supervisor-runtime.ts +365 -10
- package/src/runtime/safe-tooling/google-credential-store.ts +1 -11
- package/src/runtime/safe-tooling/ios-development.ts +22 -8
- package/src/runtime/safe-tooling/job-summary.ts +1 -1
- package/src/runtime/safe-tooling/workspace-auth.ts +4 -1
- package/src/runtime/shared/atomic-text-file.ts +21 -0
- package/src/runtime/shared/json-files.ts +8 -3
- package/src/runtime/standalone-recovery/core.ts +189 -174
- package/src/runtime/standalone-recovery/entry.ts +62 -30
- package/src/runtime/standalone-recovery/operation-lock.ts +11 -1
- package/src/runtime/workflow/schedules/chatgpt-automation-migration.ts +1 -0
- package/src/runtime/workflow/schedules/engine.ts +36 -14
- package/src/runtime/workflow/schedules/pending-activation.ts +11 -4
- package/src/runtime/workflow/schedules/work-continuation.ts +51 -6
- package/supervisor/client.ts +31 -14
- package/supervisor/control-plane.ts +210 -115
- package/supervisor/forge-validators.ts +88 -21
- package/supervisor/native-browser-adapter.ts +393 -28
- package/supervisor/native-messaging/host.ts +21 -2
- package/supervisor/protocol.ts +74 -26
- package/supervisor/server.ts +16 -8
- package/supervisor/store.ts +476 -57
- package/supervisor/types.ts +19 -13
- package/SKILL.md +0 -427
- package/adapters/computer/chatgpt-target-router.ts +0 -103
- package/adapters/mcp/runtime-gateway/automation-receipt-adapter.ts +0 -87
- package/adapters/mcp/runtime-gateway/work-validation-reconciler.ts +0 -1
- package/adapters/mcp/toolset-selection.ts +0 -41
- package/agents/openai.yaml +0 -4
- package/assets/skill-hooks.json +0 -41
- package/assets/skill-version.json +0 -189
- package/assets/skills/claude-review/SKILL.md +0 -231
- package/assets/skills/codex-review/SKILL.md +0 -103
- package/assets/templates/helpers/check-skill-version.ts +0 -193
- package/scripts/check-skill-version.ts +0 -5
- package/scripts/ensure-codegraph.sh +0 -16
- package/scripts/run-skill-hook.ts +0 -244
- package/scripts/sync-codex-installed-copies.sh +0 -182
- package/src/cli/commands/tools.ts +0 -167
- package/src/cli/mcp/access-mode.ts +0 -2
- package/src/cli/mcp/access-tools.ts +0 -2
- package/src/cli/mcp/audit.ts +0 -2
- package/src/cli/mcp/auth.ts +0 -2
- package/src/cli/mcp/instructions.ts +0 -2
- package/src/cli/mcp/legacy-context.ts +0 -2
- package/src/cli/mcp/legacy-tool-service.ts +0 -2
- package/src/cli/mcp/multi-repository.ts +0 -2
- package/src/cli/mcp/oauth.ts +0 -2
- package/src/cli/mcp/openai-secure-tunnel.ts +0 -2
- package/src/cli/mcp/paths.ts +0 -2
- package/src/cli/mcp/policy.ts +0 -2
- package/src/cli/mcp/post-finalize-work-attribution.ts +0 -2
- package/src/cli/mcp/proxy-env.ts +0 -2
- package/src/cli/mcp/readiness-projection.ts +0 -2
- package/src/cli/mcp/redaction.ts +0 -2
- package/src/cli/mcp/repo.ts +0 -2
- package/src/cli/mcp/repository-tools.ts +0 -2
- package/src/cli/mcp/server.ts +0 -2
- package/src/cli/mcp/setup.ts +0 -2
- package/src/cli/mcp/tool-capability-cache.ts +0 -2
- package/src/cli/mcp/tool-contract.ts +0 -2
- package/src/cli/mcp/tools.ts +0 -2
- package/src/cli/mcp/toolset-names.ts +0 -2
- package/src/cli/mcp/toolset-selection.ts +0 -2
- package/src/cli/mcp/toolset.ts +0 -2
- package/src/cli/mcp/transports/http.ts +0 -2
- package/src/cli/mcp/transports/session-registry.ts +0 -2
- package/src/cli/mcp/transports/stdio.ts +0 -2
- package/src/cli/mcp/types.ts +0 -2
- package/src/cli/mcp/umbrella-tools.ts +0 -2
- package/src/cli/tools/codegraph.ts +0 -695
- package/src/core/adoption/helper-wrapper-plan.ts +0 -159
- package/src/runtime/context/codegraph-cache-boundary.ts +0 -255
- package/src/runtime/context/codegraph-read-provider.ts +0 -611
- package/src/runtime/context/codegraph-sidecar.cjs +0 -248
- package/src/runtime/control-plane/codegraph-cache-retention.ts +0 -164
- package/src/runtime/gateway/mcp/execution-tools.ts +0 -2
- package/src/runtime/gateway/mcp/legacy-ios-tool-adapter.ts +0 -2
- package/src/runtime/gateway/mcp/persisted-check-process.ts +0 -2
- package/src/runtime/gateway/mcp/process-tools.ts +0 -2
- package/src/runtime/gateway/mcp/router.ts +0 -2
- package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +0 -2
- package/src/runtime/gateway/mcp/runtime-tools.ts +0 -2
- package/src/runtime/gateway/mcp/work-validation-reconciler.ts +0 -2
- package/src/runtime/plugins/browser-provider-registry.ts +0 -2
- package/src/runtime/plugins/computer-chatgpt-macos-target.ts +0 -891
|
@@ -5,19 +5,13 @@ import {
|
|
|
5
5
|
searchRepositoryManyAsync,
|
|
6
6
|
searchRepositoryManyCacheIdentity,
|
|
7
7
|
} from "../repository/inspector";
|
|
8
|
-
import { globMatches } from "
|
|
8
|
+
import { globMatches } from "../../../adapters/mcp/paths";
|
|
9
9
|
import { posix } from "path";
|
|
10
|
-
import type { McpPolicy } from "
|
|
10
|
+
import type { McpPolicy } from "../../../adapters/mcp/types";
|
|
11
11
|
import { materializeSource } from "./context/source-materializer";
|
|
12
12
|
import { projectBoard } from "./issue-store";
|
|
13
13
|
import { buildControllerTaskLedgerProjection } from "./task-ledger";
|
|
14
14
|
import { legacyIssueAuthorityRetired } from "./legacy-issue-cutover";
|
|
15
|
-
import {
|
|
16
|
-
queryCodeGraphReadProvider,
|
|
17
|
-
queryCodeGraphReadProviderAsync,
|
|
18
|
-
type CodeGraphNodeSummary,
|
|
19
|
-
type CodeGraphReadProviderResponse,
|
|
20
|
-
} from "../../runtime/context/codegraph-read-provider";
|
|
21
15
|
import {
|
|
22
16
|
CONTEXT_PACK_SCHEMA_VERSION,
|
|
23
17
|
CONTROLLER_CONTEXT_IMPACT_DOMAINS,
|
|
@@ -38,7 +32,7 @@ import {
|
|
|
38
32
|
clamp,
|
|
39
33
|
cleanList,
|
|
40
34
|
gitStatusChangedPaths,
|
|
41
|
-
|
|
35
|
+
explicitCodeQueries,
|
|
42
36
|
pathNoisePenalty,
|
|
43
37
|
structuralIntentQuery,
|
|
44
38
|
textTokens,
|
|
@@ -46,6 +40,11 @@ import {
|
|
|
46
40
|
import { issueTaskFocus, ledgerTask } from './context/focus';
|
|
47
41
|
import { addReason, coveredByGlob, expandKnownPath, looksLikeGlob, readableFile } from './context/known-paths';
|
|
48
42
|
import { structuralResult } from './context/structural';
|
|
43
|
+
import {
|
|
44
|
+
unavailableStructuralProvider,
|
|
45
|
+
unavailableStructuralProviderAsync,
|
|
46
|
+
type StructuralReadProviderResponse,
|
|
47
|
+
} from './context/structural-provider';
|
|
49
48
|
import { buildRepositoryInstructionContext } from './context/instruction-context';
|
|
50
49
|
import { resolveRepositoryGovernance } from './context/rule-resolution';
|
|
51
50
|
import { controllerCheckSelection, listControllerChecks } from './check-runner';
|
|
@@ -100,7 +99,7 @@ function resolveSourceReference(repoRoot: string, policy: McpPolicy, sourcePath:
|
|
|
100
99
|
function contextPathPriority(path: string, reasons: string[]): number {
|
|
101
100
|
const testBonus = /(^|\/)(__tests__|tests?|spec)(\/|$)|\.(test|spec)\.[^.]+$/i.test(path) ? 6 : 0;
|
|
102
101
|
const sourceBonus = reasons.some((reason) => reason.startsWith('source-reference:')) ? 5 : 0;
|
|
103
|
-
const structuralBonus = reasons.some((reason) => reason.startsWith('
|
|
102
|
+
const structuralBonus = reasons.some((reason) => reason.startsWith('structural:')) ? 3 : 0;
|
|
104
103
|
return testBonus + sourceBonus + structuralBonus;
|
|
105
104
|
}
|
|
106
105
|
|
|
@@ -148,7 +147,8 @@ export function buildControllerContextPack(
|
|
|
148
147
|
focus.task?.objective,
|
|
149
148
|
].filter((entry): entry is string => typeof entry === "string" && entry.trim().length > 0);
|
|
150
149
|
const goal = goalParts.join("\n").trim();
|
|
151
|
-
const
|
|
150
|
+
const requiredSearchQueries = explicitCodeQueries(options.searchTerms);
|
|
151
|
+
const terms = cleanList([...requiredSearchQueries, ...(options.searchTerms ?? []), ...(options.searchTerms ?? []).flatMap(textTokens), ...textTokens(goal)]).slice(0, 14);
|
|
152
152
|
const impactDomains = cleanList(options.impactDomains)
|
|
153
153
|
.filter((domain): domain is ControllerContextImpactDomain => CONTROLLER_CONTEXT_IMPACT_DOMAINS.includes(domain as ControllerContextImpactDomain))
|
|
154
154
|
.slice(0, 6);
|
|
@@ -168,9 +168,6 @@ export function buildControllerContextPack(
|
|
|
168
168
|
]).slice(0, 32);
|
|
169
169
|
// Preserve the guarantee for explicit code symbols while broad natural-
|
|
170
170
|
// language terms remain eligible for early discovery completion.
|
|
171
|
-
const requiredSearchQueries = cleanList(options.searchTerms)
|
|
172
|
-
.filter(isCodeShapedTerm)
|
|
173
|
-
.slice(0, 2);
|
|
174
171
|
const impactCandidatePaths = new Map<ControllerContextImpactDomain, Set<string>>(impactDomains.map((domain) => [domain, new Set<string>()]));
|
|
175
172
|
const candidates = new Map<string, { reasons: Set<string>; lines: Set<number> }>();
|
|
176
173
|
const deniedPaths: Array<{ path: string; reason: string }> = [];
|
|
@@ -192,7 +189,7 @@ export function buildControllerContextPack(
|
|
|
192
189
|
const baselineRevisionMatches = indexSource === "current_checkout" || gitSnapshot(structuralIndexRoot).head === git.head;
|
|
193
190
|
const graphImpactFiles = new Set<string>();
|
|
194
191
|
let structuralContext: ControllerContextPackProjection["structuralContext"] = {
|
|
195
|
-
provider: "
|
|
192
|
+
provider: "none",
|
|
196
193
|
requestedMode: structuralMode,
|
|
197
194
|
status: "disabled",
|
|
198
195
|
requiredSatisfied: structuralMode !== "required",
|
|
@@ -204,17 +201,17 @@ export function buildControllerContextPack(
|
|
|
204
201
|
truncated: false,
|
|
205
202
|
};
|
|
206
203
|
const repositorySessionCache = getRepositoryReadSessionCache(repoRoot, options.session);
|
|
207
|
-
const
|
|
208
|
-
const queryStructural = (queryRoot: string, request: Parameters<typeof
|
|
204
|
+
const structuralProvider = dependencies.queryStructural ?? unavailableStructuralProvider;
|
|
205
|
+
const queryStructural = (queryRoot: string, request: Parameters<typeof unavailableStructuralProvider>[1]): StructuralReadProviderResponse => {
|
|
209
206
|
const key = JSON.stringify({ queryRoot, request });
|
|
210
207
|
const cached = repositorySessionCache?.getStructural(key);
|
|
211
208
|
if (cached && typeof cached === "object") {
|
|
212
209
|
structuralCacheHits += 1;
|
|
213
|
-
return cached as
|
|
210
|
+
return cached as StructuralReadProviderResponse;
|
|
214
211
|
}
|
|
215
212
|
structuralCacheMisses += 1;
|
|
216
|
-
const response =
|
|
217
|
-
if (response.error?.code !==
|
|
213
|
+
const response = structuralProvider(queryRoot, request);
|
|
214
|
+
if (response.error?.code !== STRUCTURAL_PREFETCH_DEFERRED_CODE) {
|
|
218
215
|
repositorySessionCache?.putStructural(key, response);
|
|
219
216
|
}
|
|
220
217
|
return response;
|
|
@@ -262,7 +259,7 @@ export function buildControllerContextPack(
|
|
|
262
259
|
? "stale"
|
|
263
260
|
: structural.status;
|
|
264
261
|
structuralContext = {
|
|
265
|
-
provider: "
|
|
262
|
+
provider: "none",
|
|
266
263
|
requestedMode: structuralMode,
|
|
267
264
|
status: structuralStatus,
|
|
268
265
|
requiredSatisfied: structuralMode !== "required" || structuralEvidenceCurrent,
|
|
@@ -304,12 +301,12 @@ export function buildControllerContextPack(
|
|
|
304
301
|
// must never steer which raw source GPT sees first.
|
|
305
302
|
if (structuralEvidenceCurrent) {
|
|
306
303
|
for (const node of graph.nodes.slice(0, maxFiles * 6)) {
|
|
307
|
-
addStructuralPath(node.filePath, `
|
|
304
|
+
addStructuralPath(node.filePath, `structural:context:${node.kind}:${node.name}`, node.startLine);
|
|
308
305
|
}
|
|
309
306
|
for (const node of graph.entryPoints) {
|
|
310
|
-
addStructuralPath(node.filePath, `
|
|
307
|
+
addStructuralPath(node.filePath, `structural:entry:${node.name}`, node.startLine);
|
|
311
308
|
}
|
|
312
|
-
for (const path of graph.relatedFiles.slice(0, maxFiles * 6)) addStructuralPath(path, "
|
|
309
|
+
for (const path of graph.relatedFiles.slice(0, maxFiles * 6)) addStructuralPath(path, "structural:related");
|
|
313
310
|
// Context search identifies the structural entry points. For a bounded
|
|
314
311
|
// number of those files, enrich the same Context Plane call with direct
|
|
315
312
|
// file dependencies/dependents. This is impact evidence, not a second
|
|
@@ -326,18 +323,18 @@ export function buildControllerContextPack(
|
|
|
326
323
|
: [];
|
|
327
324
|
for (const path of dependencies.slice(0, 40)) {
|
|
328
325
|
graphImpactFiles.add(path);
|
|
329
|
-
addStructuralPath(path, `
|
|
326
|
+
addStructuralPath(path, `structural:dependency:${filePath}`);
|
|
330
327
|
}
|
|
331
328
|
for (const path of dependents.slice(0, 40)) {
|
|
332
329
|
graphImpactFiles.add(path);
|
|
333
|
-
addStructuralPath(path, `
|
|
330
|
+
addStructuralPath(path, `structural:dependent:${filePath}`);
|
|
334
331
|
}
|
|
335
332
|
}
|
|
336
333
|
}
|
|
337
|
-
for (const path of metadata?.changedFiles.removed ?? []) omitted.push({ path, reason: "
|
|
334
|
+
for (const path of metadata?.changedFiles.removed ?? []) omitted.push({ path, reason: "structural provider reports file removed since index" });
|
|
338
335
|
// A repository-baseline graph deliberately runs against another checkout.
|
|
339
336
|
// Current worktree changes are still authoritative raw-source evidence,
|
|
340
|
-
// but they come from Git rather than stale
|
|
337
|
+
// but they come from Git rather than stale structural metadata.
|
|
341
338
|
if (indexSource === "repository_baseline") {
|
|
342
339
|
for (const path of gitStatusChangedPaths(git.status)) addStructuralPath(path, "worktree:changed-file", 1);
|
|
343
340
|
}
|
|
@@ -349,16 +346,17 @@ export function buildControllerContextPack(
|
|
|
349
346
|
let exactKnownFileScope = explicitKnownPaths.length > 0;
|
|
350
347
|
for (const path of explicitKnownPaths) {
|
|
351
348
|
const expanded = expandKnownPath(repoRoot, policy, path, Math.max(maxFiles * 4, 40));
|
|
352
|
-
|
|
349
|
+
const allowedFiles = expanded.files.filter((file) => !excludeGlobs.some((glob) => globMatches(glob, file)));
|
|
350
|
+
for (const file of allowedFiles) {
|
|
353
351
|
addReason(candidates, file, expanded.directory ? `explicit-known-directory:${expanded.directory}` : "explicit-known-path");
|
|
354
352
|
}
|
|
355
|
-
if (!expanded.directory &&
|
|
356
|
-
exactKnownFiles.push(
|
|
353
|
+
if (!expanded.directory && allowedFiles.length === 1 && expanded.denied.length === 0 && !expanded.truncated) {
|
|
354
|
+
exactKnownFiles.push(allowedFiles[0]!);
|
|
357
355
|
} else {
|
|
358
356
|
exactKnownFileScope = false;
|
|
359
357
|
}
|
|
360
358
|
deniedPaths.push(...expanded.denied);
|
|
361
|
-
scannedFiles +=
|
|
359
|
+
scannedFiles += allowedFiles.length;
|
|
362
360
|
searchTruncated = searchTruncated || expanded.truncated;
|
|
363
361
|
}
|
|
364
362
|
// Exact caller-selected files own a reserved file slot. The inferred-file
|
|
@@ -407,6 +405,14 @@ export function buildControllerContextPack(
|
|
|
407
405
|
searchTruncated = searchTruncated || search.truncated;
|
|
408
406
|
for (const hit of search.results) {
|
|
409
407
|
if (!coveredByGlob(hit.path, searchIncludeGlobs) || excludeGlobs.some((glob) => globMatches(glob, hit.path))) continue;
|
|
408
|
+
if (candidates.size >= maxFiles * 3 && !candidates.has(hit.path) && !requiredSearchQueries.includes(hit.query)) continue;
|
|
409
|
+
if (requiredSearchQueries.includes(hit.query)) {
|
|
410
|
+
const source = hit.text.trimStart();
|
|
411
|
+
const declared = /^(?:(?:export|declare|default|async)\s+)*(?:function|class|interface|type|const|let|enum)\s+([\w$]+)/.exec(source)?.[1];
|
|
412
|
+
if (declared?.toLowerCase() === hit.query.toLowerCase()) {
|
|
413
|
+
addReason(candidates, hit.path, `${source.startsWith('export ') ? 'exported' : 'local'}-definition:${hit.query}`, hit.line);
|
|
414
|
+
}
|
|
415
|
+
}
|
|
410
416
|
if (terms.includes(hit.query)) {
|
|
411
417
|
addReason(candidates, hit.path, `search:${hit.query}`, hit.line);
|
|
412
418
|
} else {
|
|
@@ -417,12 +423,14 @@ export function buildControllerContextPack(
|
|
|
417
423
|
addReason(candidates, hit.path, `impact:${domain}:${hit.query}`, hit.line);
|
|
418
424
|
}
|
|
419
425
|
}
|
|
420
|
-
if (candidates.size >= maxFiles * 3) break;
|
|
421
426
|
}
|
|
422
427
|
}
|
|
423
428
|
|
|
424
429
|
timingsMs.lexical = Math.round((performance.now() - lexicalStartedAt) * 100) / 100;
|
|
425
430
|
const primarySearchReason = terms.length > 0 ? `search:${terms[0]}` : undefined;
|
|
431
|
+
const definitionPriority = (reasons: readonly string[]): number =>
|
|
432
|
+
reasons.some((reason) => reason.startsWith('exported-definition:')) ? 2
|
|
433
|
+
: reasons.some((reason) => reason.startsWith('local-definition:')) ? 1 : 0;
|
|
426
434
|
const rankedCandidates = Array.from(candidates.entries())
|
|
427
435
|
.map(([path, entry]) => ({ path, reasons: Array.from(entry.reasons), lines: Array.from(entry.lines) }))
|
|
428
436
|
.sort((left, right) => {
|
|
@@ -433,6 +441,8 @@ export function buildControllerContextPack(
|
|
|
433
441
|
? 2
|
|
434
442
|
: right.reasons.some((reason) => reason.startsWith("explicit-known-directory:")) ? 1 : 0;
|
|
435
443
|
if (leftExplicit !== rightExplicit) return rightExplicit - leftExplicit;
|
|
444
|
+
const definitionDelta = definitionPriority(right.reasons) - definitionPriority(left.reasons);
|
|
445
|
+
if (definitionDelta !== 0) return definitionDelta;
|
|
436
446
|
const leftPrimary = primarySearchReason && left.reasons.includes(primarySearchReason) ? 1 : 0;
|
|
437
447
|
const rightPrimary = primarySearchReason && right.reasons.includes(primarySearchReason) ? 1 : 0;
|
|
438
448
|
if (leftPrimary !== rightPrimary) return rightPrimary - leftPrimary;
|
|
@@ -453,12 +463,19 @@ export function buildControllerContextPack(
|
|
|
453
463
|
|| structuralMode !== "off"
|
|
454
464
|
|| impactDomains.length > 0;
|
|
455
465
|
timingsMs.expansionBudgetMax = multiWaveEligible ? Math.min(6, Math.max(2, Math.ceil(maxFiles / 2))) : 0;
|
|
456
|
-
|
|
466
|
+
// Reserve one best file per explicit symbol before broad hits can consume
|
|
467
|
+
// the result window. Caller-selected exact paths retain first priority.
|
|
468
|
+
const prioritizedCandidates = Array.from(new Map([
|
|
469
|
+
...rankedCandidates.filter(isExactKnownCandidate),
|
|
470
|
+
...requiredSearchQueries.flatMap((query) => rankedCandidates.filter((entry) => entry.reasons.includes(`search:${query}`)).slice(0, 1)),
|
|
471
|
+
...rankedCandidates,
|
|
472
|
+
].map((entry) => [entry.path, entry] as const)).values());
|
|
473
|
+
const rankedExactKnownCount = prioritizedCandidates.filter(isExactKnownCandidate).length;
|
|
457
474
|
const initialCandidateLimit = multiWaveEligible
|
|
458
475
|
? Math.min(maxFiles, Math.max(rankedExactKnownCount, Math.max(1, maxFiles - timingsMs.expansionBudgetMax)))
|
|
459
476
|
: maxFiles;
|
|
460
|
-
const selected =
|
|
461
|
-
const deferredSelected =
|
|
477
|
+
const selected = prioritizedCandidates.slice(0, initialCandidateLimit);
|
|
478
|
+
const deferredSelected = prioritizedCandidates.slice(initialCandidateLimit, maxFiles);
|
|
462
479
|
const selectedExactKnownCount = selected.filter(isExactKnownCandidate).length;
|
|
463
480
|
// Reserve at least one materialization for every selected exact known file.
|
|
464
481
|
// This may raise a caller's inferred snippet budget, but never beyond the
|
|
@@ -477,7 +494,7 @@ export function buildControllerContextPack(
|
|
|
477
494
|
status: selectedFiles.length > 0 ? "selected" as const : matchedFiles.length > 0 ? "matched_not_selected" as const : "no_evidence" as const,
|
|
478
495
|
};
|
|
479
496
|
});
|
|
480
|
-
for (const entry of
|
|
497
|
+
for (const entry of prioritizedCandidates.slice(maxFiles)) omitted.push({ path: entry.path, reason: "max_files" });
|
|
481
498
|
|
|
482
499
|
let remainingSnippets = maxSnippets;
|
|
483
500
|
const files: ControllerContextPackFile[] = [];
|
|
@@ -498,9 +515,12 @@ export function buildControllerContextPack(
|
|
|
498
515
|
const waveOnePerFileCap = multiWaveEligible
|
|
499
516
|
? Math.max(1, Math.ceil(maxSnippets / Math.max(1, initialCandidateLimit + timingsMs.expansionBudgetMax)))
|
|
500
517
|
: remainingSnippets;
|
|
518
|
+
// A caller-increased max_snippets budget must not let the first broad
|
|
519
|
+
// candidate consume every snippet and erase other selected source files.
|
|
520
|
+
const inferredFairShare = Math.max(1, Math.ceil(remainingSnippets / (selected.length - selectedIndex)));
|
|
501
521
|
const fileSnippetBudget = exactKnown
|
|
502
522
|
? Math.max(1, remainingSnippets - exactKnownAfter)
|
|
503
|
-
: Math.min(remainingSnippets, waveOnePerFileCap);
|
|
523
|
+
: Math.min(remainingSnippets, waveOnePerFileCap, inferredFairShare);
|
|
504
524
|
let snippets: ControllerContextPackSnippet[] = [];
|
|
505
525
|
try {
|
|
506
526
|
snippets = materializeSource({
|
|
@@ -509,8 +529,11 @@ export function buildControllerContextPack(
|
|
|
509
529
|
path: entry.path,
|
|
510
530
|
hitLines: entry.lines.length > 0 ? entry.lines : [1],
|
|
511
531
|
reasons: entry.reasons,
|
|
512
|
-
|
|
513
|
-
|
|
532
|
+
// Discovery should cover multiple entry points rather than dump one huge file.
|
|
533
|
+
// Explicit known files retain their full evidence budget; only inferred
|
|
534
|
+
// candidates receive the cheap two-snippet discovery default.
|
|
535
|
+
maxSnippets: Math.min(options.maxSnippets === undefined && !exactKnown ? 2 : fileSnippetBudget, remainingSnippets),
|
|
536
|
+
maxCharsPerSnippet: exactKnown && options.maxCharsPerSnippet === undefined ? 8000 : maxCharsPerSnippet,
|
|
514
537
|
session: options.session,
|
|
515
538
|
});
|
|
516
539
|
for (const snippet of snippets) {
|
|
@@ -534,7 +557,7 @@ export function buildControllerContextPack(
|
|
|
534
557
|
|
|
535
558
|
// Evidence-closure eligible requests perform bounded internal waves after
|
|
536
559
|
// Wave 1 raw source materialization. Each later wave derives concrete paths
|
|
537
|
-
// from the source that was just read plus current
|
|
560
|
+
// from the source that was just read plus current structural adjacency, then
|
|
538
561
|
// materializes those paths in the same context request.
|
|
539
562
|
const materializedPathSet = new Set(files.map((file) => file.path));
|
|
540
563
|
let frontier = files.slice();
|
|
@@ -572,11 +595,11 @@ export function buildControllerContextPack(
|
|
|
572
595
|
: [];
|
|
573
596
|
for (const path of dependencies.slice(0, 40)) {
|
|
574
597
|
graphImpactFiles.add(path);
|
|
575
|
-
addExpansionCandidate(path, `
|
|
598
|
+
addExpansionCandidate(path, `structural:dependency:${file.path}`);
|
|
576
599
|
}
|
|
577
600
|
for (const path of dependents.slice(0, 40)) {
|
|
578
601
|
graphImpactFiles.add(path);
|
|
579
|
-
addExpansionCandidate(path, `
|
|
602
|
+
addExpansionCandidate(path, `structural:dependent:${file.path}`);
|
|
580
603
|
}
|
|
581
604
|
}
|
|
582
605
|
|
|
@@ -752,7 +775,7 @@ export function buildControllerContextPack(
|
|
|
752
775
|
architectureContracts,
|
|
753
776
|
coverageGaps: Array.from(new Set(coverageGaps)).slice(0, 40),
|
|
754
777
|
relationSources: [
|
|
755
|
-
...(structuralMode !== "off" ? ["
|
|
778
|
+
...(structuralMode !== "off" ? ["structural" as const] : []),
|
|
756
779
|
...(files.length > 0 ? ["lexical" as const] : []),
|
|
757
780
|
...(taskChecks.length > 0 ? ["forge_checks" as const] : []),
|
|
758
781
|
],
|
|
@@ -985,7 +1008,7 @@ export function buildControllerContextPack(
|
|
|
985
1008
|
retrievalMode === "implementation"
|
|
986
1009
|
? "The pack contains policy-approved current raw source with file SHA identities, but returned snippets are bounded evidence rather than proof of semantic completeness. After reading this evidence, derive new exact paths, symbols, tests, or relationships from the source itself. A guessed lexical term with no result is not by itself a reason to keep scanning or repeat the same broad query."
|
|
987
1010
|
: `Investigation mode performed ${timingsMs.waveCount} bounded evidence wave(s) inside this request; expansion budget ${timingsMs.expansionBudgetUsed}/${timingsMs.expansionBudgetMax}.`,
|
|
988
|
-
"Search/
|
|
1011
|
+
"Search/structural ranking is discovery evidence, not a business-semantics authority. readiness and impactContext make the bounded evidence surface and unresolved gaps explicit; ChatGPT still decides semantic sufficiency.",
|
|
989
1012
|
instructionContext.contracts.length > 0
|
|
990
1013
|
? `Applicable repository guidance was resolved hierarchically for selected source paths: ${instructionContext.contracts.map((entry) => entry.path).join(", ")}. These AGENTS.md/CLAUDE.md files are guidance-only evidence and do not define semantic scope.`
|
|
991
1014
|
: "No applicable AGENTS.md/CLAUDE.md guidance file was found for the selected source paths.",
|
|
@@ -993,15 +1016,15 @@ export function buildControllerContextPack(
|
|
|
993
1016
|
governanceContext.status === 'none'
|
|
994
1017
|
? "No repository governance rule registry is present; existing guidance and explicit Task checks behave as before."
|
|
995
1018
|
: `Repository governance resolved ${governanceContext.activeRules.length} active rule(s), ${governanceContext.suppressedRules.length} suppressed rule(s), and ${governanceContext.recommendedCheckIds.length} recommended check(s) through bounded local matching. Recommendations are evidence only and never execute checks or create Work by themselves.`,
|
|
996
|
-
"
|
|
997
|
-
structuralContext.status === "stale" ? "
|
|
1019
|
+
"Structural evidence is discovery evidence. Raw source access still passes through Forge repository policy and current-file reads.",
|
|
1020
|
+
structuralContext.status === "stale" ? "Structural provider reports stale evidence. Treat relationships as hints and prefer the returned current raw source for changed files." : structuralContext.status === "unavailable" || structuralContext.status === "degraded" ? "Structural context was unavailable or degraded; bounded text search remains the fallback." : structuralContext.status === "ready" ? "Structural context was queried read-only." : "Structural context was not requested.",
|
|
998
1021
|
"Run focused validation after a coherent edit batch; expensive full-suite checks belong at candidate/release boundaries unless debugging specifically needs them earlier.",
|
|
999
1022
|
taskChecks.length > 0 ? `Task checks advertised by the board: ${taskChecks.join(", ")}.` : "No task-specific checks were found in the compact ledger.",
|
|
1000
1023
|
],
|
|
1001
1024
|
},
|
|
1002
1025
|
next: [
|
|
1003
1026
|
...(structuralMode === "required" && !structuralContext.requiredSatisfied
|
|
1004
|
-
? ["Structural context was required but is not current/ready.
|
|
1027
|
+
? ["Structural context was required but is not current/ready. Continue only with the degraded-plan warning visible or use current raw source / semantic navigation instead."]
|
|
1005
1028
|
: []),
|
|
1006
1029
|
files.length > 0
|
|
1007
1030
|
? retrievalMode === "implementation"
|
|
@@ -1016,26 +1039,26 @@ export function buildControllerContextPack(
|
|
|
1016
1039
|
|
|
1017
1040
|
/**
|
|
1018
1041
|
* Broad first-call fan-in for the canonical rh_context path. Lexical retrieval
|
|
1019
|
-
* remains required evidence. Auto
|
|
1042
|
+
* remains required evidence. Auto structural retrieval is opportunistic enrichment: it may
|
|
1020
1043
|
* finish inside the first-call latency budget, otherwise the request returns
|
|
1021
1044
|
* bounded lexical/raw evidence while the same source-bound structural request
|
|
1022
1045
|
* continues warming the session cache. Required structural mode still waits.
|
|
1023
1046
|
*/
|
|
1024
1047
|
export const AUTO_STRUCTURAL_PREFETCH_BUDGET_MS = 100;
|
|
1025
1048
|
export const AUTO_STRUCTURAL_PREFETCH_TIMEOUT_MS = 1_000;
|
|
1026
|
-
const
|
|
1027
|
-
const structuralPrefetchInflight = new Map<string, Promise<
|
|
1049
|
+
const STRUCTURAL_PREFETCH_DEFERRED_CODE = 'STRUCTURAL_PREFETCH_DEFERRED';
|
|
1050
|
+
const structuralPrefetchInflight = new Map<string, Promise<StructuralReadProviderResponse>>();
|
|
1028
1051
|
|
|
1029
|
-
function
|
|
1052
|
+
function structuralPrefetchDeferredResponse(elapsedMs: number): StructuralReadProviderResponse {
|
|
1030
1053
|
return {
|
|
1031
1054
|
schemaVersion: 1,
|
|
1032
|
-
provider: '
|
|
1055
|
+
provider: 'none',
|
|
1033
1056
|
operation: 'context',
|
|
1034
1057
|
ok: false,
|
|
1035
1058
|
status: 'degraded',
|
|
1036
1059
|
error: {
|
|
1037
|
-
code:
|
|
1038
|
-
message: `Optional
|
|
1060
|
+
code: STRUCTURAL_PREFETCH_DEFERRED_CODE,
|
|
1061
|
+
message: `Optional structural enrichment exceeded the ${AUTO_STRUCTURAL_PREFETCH_BUDGET_MS}ms first-call budget and continues warming the source-bound session cache.`,
|
|
1039
1062
|
},
|
|
1040
1063
|
timingsMs: { total: Math.round(elapsedMs * 100) / 100 },
|
|
1041
1064
|
};
|
|
@@ -1061,10 +1084,10 @@ function cacheDeferredStructuralResult(
|
|
|
1061
1084
|
sessionCache: ReturnType<typeof getRepositoryReadSessionCache>,
|
|
1062
1085
|
structuralCacheKey: string,
|
|
1063
1086
|
sourceIdentity: string | undefined,
|
|
1064
|
-
response:
|
|
1087
|
+
response: StructuralReadProviderResponse,
|
|
1065
1088
|
): void {
|
|
1066
1089
|
if (!sessionCache || !sourceIdentity) return;
|
|
1067
|
-
if (response.error?.code === '
|
|
1090
|
+
if (response.error?.code === 'STRUCTURAL_TIMEOUT' || response.error?.code === STRUCTURAL_PREFETCH_DEFERRED_CODE) return;
|
|
1068
1091
|
if (JSON.stringify(sessionCache.currentIdentity()) !== sourceIdentity) return;
|
|
1069
1092
|
sessionCache.putStructural(structuralCacheKey, response);
|
|
1070
1093
|
}
|
|
@@ -1076,7 +1099,7 @@ export async function buildControllerContextPackAsync(
|
|
|
1076
1099
|
dependencies: ControllerContextPackDependencies = {},
|
|
1077
1100
|
): Promise<ControllerContextPackProjection> {
|
|
1078
1101
|
const structuralMode = options.structuralContext ?? 'off';
|
|
1079
|
-
if ((dependencies.
|
|
1102
|
+
if ((dependencies.queryStructural && !dependencies.queryStructuralAsync) || !options.session || options.issueId || options.taskId) {
|
|
1080
1103
|
return buildControllerContextPack(repoRoot, policy, options, dependencies);
|
|
1081
1104
|
}
|
|
1082
1105
|
const prefetchStartedAt = performance.now();
|
|
@@ -1087,7 +1110,8 @@ export async function buildControllerContextPackAsync(
|
|
|
1087
1110
|
const explicitKnownPaths = knownPaths.filter((path) => !looksLikeGlob(path));
|
|
1088
1111
|
const includeGlobs = cleanList([...(options.includeGlobs ?? []), ...knownGlobs]);
|
|
1089
1112
|
const excludeGlobs = cleanList([...DEFAULT_SEARCH_EXCLUDE_GLOBS, ...(options.excludeGlobs ?? [])]);
|
|
1090
|
-
const
|
|
1113
|
+
const requiredSearchQueries = explicitCodeQueries(options.searchTerms);
|
|
1114
|
+
const terms = cleanList([...requiredSearchQueries, ...(options.searchTerms ?? []), ...(options.searchTerms ?? []).flatMap(textTokens), ...textTokens(options.description ?? '')]).slice(0, 14);
|
|
1091
1115
|
const impactDomains = cleanList(options.impactDomains)
|
|
1092
1116
|
.filter((domain): domain is ControllerContextImpactDomain => CONTROLLER_CONTEXT_IMPACT_DOMAINS.includes(domain as ControllerContextImpactDomain))
|
|
1093
1117
|
.slice(0, 6);
|
|
@@ -1095,8 +1119,9 @@ export async function buildControllerContextPackAsync(
|
|
|
1095
1119
|
let exactKnownFileScope = explicitKnownPaths.length > 0;
|
|
1096
1120
|
for (const path of explicitKnownPaths) {
|
|
1097
1121
|
const expanded = expandKnownPath(repoRoot, policy, path, Math.max(requestedMaxFiles * 4, 40));
|
|
1098
|
-
|
|
1099
|
-
|
|
1122
|
+
const allowedFiles = expanded.files.filter((file) => !excludeGlobs.some((glob) => globMatches(glob, file)));
|
|
1123
|
+
if (!expanded.directory && allowedFiles.length === 1 && expanded.denied.length === 0 && !expanded.truncated) {
|
|
1124
|
+
exactKnownFiles.push(allowedFiles[0]!);
|
|
1100
1125
|
} else {
|
|
1101
1126
|
exactKnownFileScope = false;
|
|
1102
1127
|
}
|
|
@@ -1112,9 +1137,6 @@ export async function buildControllerContextPackAsync(
|
|
|
1112
1137
|
...impactDomains.flatMap((domain) => IMPACT_DOMAIN_TERMS[domain]),
|
|
1113
1138
|
...terms.slice(1),
|
|
1114
1139
|
]).slice(0, 32);
|
|
1115
|
-
const requiredSearchQueries = cleanList(options.searchTerms)
|
|
1116
|
-
.filter(isCodeShapedTerm)
|
|
1117
|
-
.slice(0, 2);
|
|
1118
1140
|
const structuralQuery = structuralMode === 'off'
|
|
1119
1141
|
? ''
|
|
1120
1142
|
: structuralIntentQuery(options.description ?? '', terms, impactDomains);
|
|
@@ -1131,7 +1153,7 @@ export async function buildControllerContextPackAsync(
|
|
|
1131
1153
|
const sessionCache = getRepositoryReadSessionCache(repoRoot, options.session);
|
|
1132
1154
|
const structuralSourceIdentity = sessionCache ? JSON.stringify(sessionCache.currentIdentity()) : undefined;
|
|
1133
1155
|
const cachedStructural = structuralQuery
|
|
1134
|
-
? sessionCache?.peekStructural(structuralCacheKey) as
|
|
1156
|
+
? sessionCache?.peekStructural(structuralCacheKey) as StructuralReadProviderResponse | undefined
|
|
1135
1157
|
: undefined;
|
|
1136
1158
|
const lexicalOptions = searchQueries.length > 0
|
|
1137
1159
|
? (() => {
|
|
@@ -1168,18 +1190,18 @@ export async function buildControllerContextPackAsync(
|
|
|
1168
1190
|
return pack;
|
|
1169
1191
|
}
|
|
1170
1192
|
|
|
1171
|
-
const noStructuralResponse:
|
|
1193
|
+
const noStructuralResponse: StructuralReadProviderResponse = {
|
|
1172
1194
|
schemaVersion: 1,
|
|
1173
|
-
provider: '
|
|
1195
|
+
provider: 'none',
|
|
1174
1196
|
operation: 'context',
|
|
1175
1197
|
ok: false,
|
|
1176
1198
|
status: 'degraded',
|
|
1177
|
-
error: { code: '
|
|
1199
|
+
error: { code: 'STRUCTURAL_QUERY_REQUIRED', message: 'No context query was provided.' },
|
|
1178
1200
|
timingsMs: { total: 0 },
|
|
1179
1201
|
};
|
|
1180
1202
|
let structuralSettled = Boolean(cachedStructural) || !structuralQuery;
|
|
1181
|
-
let structuralSettledValue:
|
|
1182
|
-
const
|
|
1203
|
+
let structuralSettledValue: StructuralReadProviderResponse | undefined = cachedStructural || (!structuralQuery ? noStructuralResponse : undefined);
|
|
1204
|
+
const queryStructuralAsync = dependencies.queryStructuralAsync ?? unavailableStructuralProviderAsync;
|
|
1183
1205
|
const structuralInflightKey = structuralQuery && structuralSourceIdentity
|
|
1184
1206
|
? JSON.stringify({ structuralCacheKey, sourceIdentity: structuralSourceIdentity, structuralMode })
|
|
1185
1207
|
: undefined;
|
|
@@ -1188,10 +1210,9 @@ export async function buildControllerContextPackAsync(
|
|
|
1188
1210
|
const launchedStructuralPromise = cachedStructural
|
|
1189
1211
|
? Promise.resolve(cachedStructural)
|
|
1190
1212
|
: structuralQuery
|
|
1191
|
-
? existingStructuralInflight ??
|
|
1213
|
+
? existingStructuralInflight ?? queryStructuralAsync(
|
|
1192
1214
|
structuralIndexRoot,
|
|
1193
1215
|
structuralRequest,
|
|
1194
|
-
structuralMode === 'auto' ? { timeoutMs: AUTO_STRUCTURAL_PREFETCH_TIMEOUT_MS } : {},
|
|
1195
1216
|
)
|
|
1196
1217
|
: Promise.resolve(noStructuralResponse);
|
|
1197
1218
|
if (structuralInflightKey && structuralQuery && !cachedStructural && !existingStructuralInflight) {
|
|
@@ -1242,16 +1263,16 @@ export async function buildControllerContextPackAsync(
|
|
|
1242
1263
|
prefetched = outcome.value;
|
|
1243
1264
|
} else {
|
|
1244
1265
|
structuralPrefetchDeferred = true;
|
|
1245
|
-
prefetched =
|
|
1266
|
+
prefetched = structuralPrefetchDeferredResponse(performance.now() - prefetchStartedAt);
|
|
1246
1267
|
}
|
|
1247
1268
|
}
|
|
1248
1269
|
}
|
|
1249
1270
|
|
|
1250
|
-
const
|
|
1271
|
+
const fallbackQueryStructural = dependencies.queryStructural ?? unavailableStructuralProvider;
|
|
1251
1272
|
const pack = buildControllerContextPack(repoRoot, policy, options, {
|
|
1252
|
-
|
|
1273
|
+
queryStructural: (queryRoot, request) => request.operation === 'context' && queryRoot === structuralIndexRoot
|
|
1253
1274
|
? prefetched
|
|
1254
|
-
:
|
|
1275
|
+
: fallbackQueryStructural(queryRoot, request),
|
|
1255
1276
|
});
|
|
1256
1277
|
pack.timingsMs.parallelFirstPass = true;
|
|
1257
1278
|
pack.timingsMs.parallelPrefetch = Math.round((performance.now() - prefetchStartedAt) * 100) / 100;
|
|
@@ -169,7 +169,7 @@ export function buildControllerOperationalPlan(repoRoot: string, ledger: TaskLed
|
|
|
169
169
|
mcpToolSchemaConvergence: {
|
|
170
170
|
source: "mcp-tool-schema-convergence",
|
|
171
171
|
readModels: ["controller_context", "rh_context", "work_status_digest", "prepare_transfer_artifacts"],
|
|
172
|
-
commandModels: ["rh_work", "run_check", "
|
|
172
|
+
commandModels: ["rh_work", "run_check", "execute_command", "plugin_action_execute", "repository_safe_patch_apply"],
|
|
173
173
|
compatibilityRules: [
|
|
174
174
|
"compact reads are default",
|
|
175
175
|
"raw code and raw logs require explicit opt-in tools",
|
|
@@ -1,7 +1,8 @@
|
|
|
1
|
+
import { writeAtomicTextFile } from '../../runtime/shared/atomic-text-file';
|
|
1
2
|
import { spawnSync, type SpawnSyncReturns } from 'child_process';
|
|
2
|
-
import { existsSync,
|
|
3
|
+
import { existsSync, rmSync } from 'fs';
|
|
3
4
|
import { homedir } from 'os';
|
|
4
|
-
import {
|
|
5
|
+
import { join } from 'path';
|
|
5
6
|
|
|
6
7
|
export interface SystemdUserUnitInput {
|
|
7
8
|
description?: string;
|
|
@@ -12,13 +13,6 @@ export interface SystemdUserUnitInput {
|
|
|
12
13
|
restartSec?: number;
|
|
13
14
|
}
|
|
14
15
|
|
|
15
|
-
function atomicWrite(path: string, content: string, mode = 0o644): void {
|
|
16
|
-
mkdirSync(dirname(path), { recursive: true, mode: 0o700 });
|
|
17
|
-
const temporary = `${path}.${process.pid}.tmp`;
|
|
18
|
-
writeFileSync(temporary, content, { encoding: 'utf8', mode });
|
|
19
|
-
renameSync(temporary, path);
|
|
20
|
-
}
|
|
21
|
-
|
|
22
16
|
export function systemdEscape(value: string): string {
|
|
23
17
|
return `"${value.replaceAll('%', '%%').replaceAll('\\', '\\\\').replaceAll('"', '\\"')}"`;
|
|
24
18
|
}
|
|
@@ -79,7 +73,7 @@ export function writeSystemdUserUnit(
|
|
|
79
73
|
env: NodeJS.ProcessEnv = process.env,
|
|
80
74
|
): string {
|
|
81
75
|
const unitPath = systemdUserUnitPath(unitName, env);
|
|
82
|
-
|
|
76
|
+
writeAtomicTextFile(unitPath, renderSystemdUserUnit(input), 0o644);
|
|
83
77
|
return unitPath;
|
|
84
78
|
}
|
|
85
79
|
|
|
@@ -9,8 +9,8 @@ import {
|
|
|
9
9
|
type ControllerCheckResult,
|
|
10
10
|
} from '../controller/check-runner';
|
|
11
11
|
import { tryAppendControllerWorklogEvent } from '../controller/worklog';
|
|
12
|
-
import { globMatches, resolveMcpPath } from '
|
|
13
|
-
import type { McpPolicy } from '
|
|
12
|
+
import { globMatches, resolveMcpPath } from '../../../adapters/mcp/paths';
|
|
13
|
+
import type { McpPolicy } from '../../../adapters/mcp/types';
|
|
14
14
|
import type { ProcessCheckCompletionReceipt, ProcessCheckReceiptStatus } from '../../runtime/execution/process-runtime/check-receipt';
|
|
15
15
|
import { invalidateRepositoryReadCaches } from '../repository/inspector';
|
|
16
16
|
import type { EditOperation } from './edit-operation-contract';
|
|
@@ -312,6 +312,10 @@ function bindSessionControllerInstance(repoRoot: string, session: EditSession, b
|
|
|
312
312
|
assertEditSessionDurableBinding(session, binding);
|
|
313
313
|
const nextInstanceId = binding?.controllerInstanceId?.trim();
|
|
314
314
|
if (!nextInstanceId || session.controllerInstanceId === nextInstanceId) return session;
|
|
315
|
+
// A non-Work Safe Patch session has no Controller instance owner to rebind.
|
|
316
|
+
// Its persisted repository identity is still checked above; never promote
|
|
317
|
+
// a caller-supplied transient Runtime identity into durable session authority.
|
|
318
|
+
if (!session.workId && !session.controllerInstanceId) return session;
|
|
315
319
|
const durableIdentity = [
|
|
316
320
|
[session.workId, binding?.workId],
|
|
317
321
|
[session.repoId, binding?.repoId],
|
|
@@ -1144,7 +1148,7 @@ function sessionSummary(session: EditSession): EditSessionSummary {
|
|
|
1144
1148
|
};
|
|
1145
1149
|
}
|
|
1146
1150
|
|
|
1147
|
-
function findOccurrence(value: string, anchor: string, occurrence: number): number {
|
|
1151
|
+
export function findOccurrence(value: string, anchor: string, occurrence: number): number {
|
|
1148
1152
|
let from = 0;
|
|
1149
1153
|
for (let current = 1; current <= occurrence; current += 1) {
|
|
1150
1154
|
const index = value.indexOf(anchor, from);
|
|
@@ -1913,9 +1917,17 @@ export function finalizeEditSession(repoRoot: string, sessionId: string, input:
|
|
|
1913
1917
|
} = {}): EditSession {
|
|
1914
1918
|
let session = getEditSession(repoRoot, sessionId);
|
|
1915
1919
|
session = bindSessionControllerInstance(repoRoot, session, input.binding);
|
|
1916
|
-
|
|
1917
|
-
|
|
1920
|
+
const baseRevisionChanged = Boolean(session.baseRevision && gitRevision(repoRoot) !== session.baseRevision);
|
|
1921
|
+
const workspaceFingerprintChanged = Boolean(session.workspaceFingerprint && workspaceFingerprint(repoRoot) !== session.workspaceFingerprint);
|
|
1918
1922
|
if (session.assurance?.approvalRequired && input.approvalConfirmed !== true) throw new Error('EDIT_SESSION_APPROVAL_REQUIRED');
|
|
1923
|
+
if (baseRevisionChanged || workspaceFingerprintChanged) {
|
|
1924
|
+
// A commit changes HEAD and Git's workspace fingerprint even when the exact edit-session after-images are now integrated.
|
|
1925
|
+
// Reconcile through the canonical after-image + clean-tracked-path checks before rejecting the stale snapshot.
|
|
1926
|
+
const reconciled = reconcileEditSession(repoRoot, session.sessionId, input);
|
|
1927
|
+
if (reconciled.status === 'finalized' || reconciled.status === 'superseded') return reconciled;
|
|
1928
|
+
if (baseRevisionChanged) throw new Error('EDIT_SESSION_BASE_REVISION_CHANGED');
|
|
1929
|
+
throw new Error('EDIT_SESSION_WORKSPACE_FINGERPRINT_CHANGED');
|
|
1930
|
+
}
|
|
1919
1931
|
const emptyOpenSession = session.status === 'open' && session.operations.length === 0;
|
|
1920
1932
|
if (!emptyOpenSession && !['dirty', 'checked', 'check_failed'].includes(session.status)) {
|
|
1921
1933
|
throw new Error(`edit session cannot be finalized from ${session.status}`);
|
package/src/cli/github/github.ts
CHANGED
|
@@ -121,7 +121,7 @@ export function buildGitHubWorkflowDispatchArgs(repository: string, input: GitHu
|
|
|
121
121
|
return args;
|
|
122
122
|
}
|
|
123
123
|
|
|
124
|
-
function gh(repoRoot: string, args: string[], input?: string, maxOutputBytes = 512 * 1024) {
|
|
124
|
+
export function gh(repoRoot: string, args: string[], input?: string, maxOutputBytes = 512 * 1024) {
|
|
125
125
|
return runProcess('gh', args, {
|
|
126
126
|
cwd: repoRoot,
|
|
127
127
|
timeoutMs: 120_000,
|
|
@@ -284,7 +284,7 @@ function parseIssueUrl(output: string): { url: string; number: number } {
|
|
|
284
284
|
return { url, number };
|
|
285
285
|
}
|
|
286
286
|
|
|
287
|
-
function addProjectItem(repoRoot: string, link: GitHubIssueLink, projectOwner?: string, projectNumber?: number): GitHubIssueLink {
|
|
287
|
+
export function addProjectItem(repoRoot: string, link: GitHubIssueLink, projectOwner?: string, projectNumber?: number): GitHubIssueLink {
|
|
288
288
|
if (!projectOwner || !projectNumber) return link;
|
|
289
289
|
const result = gh(repoRoot, ['project', 'item-add', String(projectNumber), '--owner', projectOwner, '--url', link.url, '--format', 'json']);
|
|
290
290
|
if (!result.ok) throw new Error(`failed to add ${link.url} to GitHub Project: ${result.error || result.stderr}`);
|
|
@@ -302,7 +302,7 @@ function withoutPairedOption(args: string[], option: string): string[] {
|
|
|
302
302
|
return args.filter((entry, index, all) => entry !== option && all[index - 1] !== option);
|
|
303
303
|
}
|
|
304
304
|
|
|
305
|
-
function createGitHubIssue(repoRoot: string, repo: GitHubRepositoryInfo, title: string, bodyPath: string, labels: string[], parentNumber?: number): GitHubIssueLink {
|
|
305
|
+
export function createGitHubIssue(repoRoot: string, repo: GitHubRepositoryInfo, title: string, bodyPath: string, labels: string[], parentNumber?: number): GitHubIssueLink {
|
|
306
306
|
const args = ['issue', 'create', '--repo', repo.nameWithOwner, '--title', title, '--body-file', bodyPath];
|
|
307
307
|
for (const label of labels) args.push('--label', label);
|
|
308
308
|
if (parentNumber) args.push('--parent', String(parentNumber));
|
|
@@ -323,7 +323,7 @@ function createGitHubIssue(repoRoot: string, repo: GitHubRepositoryInfo, title:
|
|
|
323
323
|
return { owner: repo.owner, repo: repo.repo, number: parsed.number, url: parsed.url, syncedAt: new Date().toISOString() };
|
|
324
324
|
}
|
|
325
325
|
|
|
326
|
-
function updateGitHubIssue(repoRoot: string, repo: GitHubRepositoryInfo, link: GitHubIssueLink, title: string, bodyPath: string): GitHubIssueLink {
|
|
326
|
+
export function updateGitHubIssue(repoRoot: string, repo: GitHubRepositoryInfo, link: GitHubIssueLink, title: string, bodyPath: string): GitHubIssueLink {
|
|
327
327
|
const result = gh(repoRoot, ['issue', 'edit', String(link.number), '--repo', repo.nameWithOwner, '--title', title, '--body-file', bodyPath]);
|
|
328
328
|
if (!result.ok) throw new Error(`failed to update GitHub issue #${link.number}: ${result.error || result.stderr}`);
|
|
329
329
|
return { ...link, owner: repo.owner, repo: repo.repo, syncedAt: new Date().toISOString() };
|