@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
|
@@ -6,12 +6,6 @@ import {
|
|
|
6
6
|
type McpLocalConfig,
|
|
7
7
|
writeMcpServiceLocalConfig,
|
|
8
8
|
} from './auth';
|
|
9
|
-
import type { McpToolset } from './types';
|
|
10
|
-
import {
|
|
11
|
-
migrateControllerToolsetConfig,
|
|
12
|
-
normalizeConfiguredMcpToolset,
|
|
13
|
-
resolveControllerToolsetSelection,
|
|
14
|
-
} from './toolset-selection';
|
|
15
9
|
import {
|
|
16
10
|
isAccessMode,
|
|
17
11
|
type AccessMode,
|
|
@@ -20,28 +14,23 @@ import {
|
|
|
20
14
|
export type ControllerAccessConfigSource =
|
|
21
15
|
| 'controller_home.access_mode'
|
|
22
16
|
| 'repo_local_fallback.access_mode'
|
|
23
|
-
| 'controller_home.legacy_toolset'
|
|
24
|
-
| 'repo_local_fallback.legacy_toolset'
|
|
25
17
|
| 'default';
|
|
26
18
|
|
|
27
19
|
export interface ControllerAccessState {
|
|
28
20
|
configuredAccessMode: AccessMode;
|
|
29
21
|
effectiveAccessMode: AccessMode;
|
|
30
|
-
/**
|
|
31
|
-
effectiveToolset:
|
|
22
|
+
/** The complete MCP capability registry is always available. */
|
|
23
|
+
effectiveToolset: 'full';
|
|
32
24
|
source: ControllerAccessConfigSource;
|
|
33
25
|
lastAppliedAt?: string;
|
|
34
26
|
exposureRevision: number;
|
|
35
27
|
configPathSource: 'controller_home' | 'repo_local_fallback' | 'default';
|
|
36
|
-
legacyToolset?: McpToolset;
|
|
37
28
|
schemaStableAcrossAccessModes: true;
|
|
38
29
|
}
|
|
39
30
|
|
|
40
31
|
export interface ResolveControllerAccessStateInput {
|
|
41
32
|
controllerHome: string;
|
|
42
33
|
repoRoot?: string;
|
|
43
|
-
toolsetOverride?: McpToolset;
|
|
44
|
-
toolsetLocked?: boolean;
|
|
45
34
|
}
|
|
46
35
|
|
|
47
36
|
export interface PersistControllerAccessModeResult {
|
|
@@ -50,21 +39,22 @@ export interface PersistControllerAccessModeResult {
|
|
|
50
39
|
state: ControllerAccessState;
|
|
51
40
|
}
|
|
52
41
|
|
|
53
|
-
export function accessModeForLegacyToolset(toolset: McpToolset): AccessMode {
|
|
54
|
-
return toolset === 'core' ? 'request' : 'full_access';
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/** Retained for compatibility only; access mode no longer changes schema. */
|
|
58
|
-
export function legacyToolsetForAccessMode(_mode: AccessMode): McpToolset {
|
|
59
|
-
return 'core';
|
|
60
|
-
}
|
|
61
|
-
|
|
62
42
|
function configLocation(controllerHome: string, repoRoot?: string): 'controller_home' | 'repo_local_fallback' | 'default' {
|
|
63
43
|
if (existsSync(mcpControllerHomeLocalConfigPath(controllerHome))) return 'controller_home';
|
|
64
44
|
if (repoRoot && existsSync(mcpLocalConfigPath(repoRoot))) return 'repo_local_fallback';
|
|
65
45
|
return 'default';
|
|
66
46
|
}
|
|
67
47
|
|
|
48
|
+
/** One-way interpretation of old persisted permissions, not a toolset selector. */
|
|
49
|
+
export function configuredControllerAccessMode(config: McpLocalConfig | null | undefined): AccessMode {
|
|
50
|
+
if (isAccessMode(config?.accessMode)) return config.accessMode;
|
|
51
|
+
// Historically, core without an explicit accessMode meant Request, not Full Access.
|
|
52
|
+
// Preserve that permission until the next canonical config write retires toolset.
|
|
53
|
+
return (config as { toolset?: unknown } | null | undefined)?.toolset === 'core'
|
|
54
|
+
? 'request'
|
|
55
|
+
: 'full_access';
|
|
56
|
+
}
|
|
57
|
+
|
|
68
58
|
function normalizedRevision(value: unknown): number {
|
|
69
59
|
if (typeof value !== 'number' || !Number.isFinite(value)) return 0;
|
|
70
60
|
return Math.max(0, Math.trunc(value));
|
|
@@ -77,27 +67,18 @@ export function resolveControllerAccessState(input: ResolveControllerAccessState
|
|
|
77
67
|
? config.accessModeUpdatedAt
|
|
78
68
|
: undefined;
|
|
79
69
|
const exposureRevision = normalizedRevision(config?.accessModeRevision);
|
|
80
|
-
const
|
|
81
|
-
const selectedToolset = resolveControllerToolsetSelection(config, input.toolsetOverride);
|
|
82
|
-
const configuredAccessMode = isAccessMode(config?.accessMode)
|
|
83
|
-
? config.accessMode
|
|
84
|
-
: rawLegacyToolset
|
|
85
|
-
? accessModeForLegacyToolset(rawLegacyToolset)
|
|
86
|
-
: 'full_access';
|
|
70
|
+
const configuredAccessMode = configuredControllerAccessMode(config);
|
|
87
71
|
const source: ControllerAccessConfigSource = isAccessMode(config?.accessMode)
|
|
88
72
|
? (location === 'controller_home' ? 'controller_home.access_mode' : 'repo_local_fallback.access_mode')
|
|
89
|
-
:
|
|
90
|
-
? (location === 'controller_home' ? 'controller_home.legacy_toolset' : 'repo_local_fallback.legacy_toolset')
|
|
91
|
-
: 'default';
|
|
73
|
+
: 'default';
|
|
92
74
|
return {
|
|
93
75
|
configuredAccessMode,
|
|
94
76
|
effectiveAccessMode: configuredAccessMode,
|
|
95
|
-
effectiveToolset:
|
|
77
|
+
effectiveToolset: 'full',
|
|
96
78
|
source,
|
|
97
79
|
lastAppliedAt,
|
|
98
80
|
exposureRevision,
|
|
99
81
|
configPathSource: location,
|
|
100
|
-
legacyToolset: rawLegacyToolset,
|
|
101
82
|
schemaStableAcrossAccessModes: true,
|
|
102
83
|
};
|
|
103
84
|
}
|
|
@@ -110,7 +91,6 @@ export function persistControllerAccessMode(
|
|
|
110
91
|
const existing = loadMcpServiceLocalConfig(controllerHome, repoRoot) ?? {};
|
|
111
92
|
const accessModeUpdatedAt = new Date().toISOString();
|
|
112
93
|
const accessModeRevision = normalizedRevision(existing.accessModeRevision) + 1;
|
|
113
|
-
const migratedToolset = migrateControllerToolsetConfig(existing);
|
|
114
94
|
const config: McpLocalConfig = {
|
|
115
95
|
...existing,
|
|
116
96
|
version: Math.max(existing.version ?? 1, 2),
|
|
@@ -118,11 +98,10 @@ export function persistControllerAccessMode(
|
|
|
118
98
|
accessMode: mode,
|
|
119
99
|
accessModeUpdatedAt,
|
|
120
100
|
accessModeRevision,
|
|
121
|
-
// Access changes preserve an explicit profile and migrate the old implicit
|
|
122
|
-
// Advanced label to Core; they never expand or shrink schema themselves.
|
|
123
|
-
toolset: migratedToolset.toolset,
|
|
124
|
-
toolsetExplicit: migratedToolset.toolsetExplicit,
|
|
125
101
|
};
|
|
102
|
+
// Stored legacy profile values are not part of the one-surface contract.
|
|
103
|
+
delete (config as Record<string, unknown>).toolset;
|
|
104
|
+
delete (config as Record<string, unknown>).toolsetExplicit;
|
|
126
105
|
const configPath = writeMcpServiceLocalConfig(controllerHome, config);
|
|
127
106
|
return {
|
|
128
107
|
configPath,
|
package/adapters/mcp/auth.ts
CHANGED
|
@@ -3,7 +3,6 @@ import { existsSync, mkdirSync, readFileSync, writeFileSync } from 'fs';
|
|
|
3
3
|
import { dirname, join } from 'path';
|
|
4
4
|
import { resolveControllerHome } from '../../src/cli/repositories/controller-home';
|
|
5
5
|
import type { AccessMode } from '../../src/runtime/control-plane/governance/access-policy';
|
|
6
|
-
import type { McpToolset } from './types';
|
|
7
6
|
|
|
8
7
|
export interface McpLocalConfig {
|
|
9
8
|
version?: number;
|
|
@@ -29,9 +28,6 @@ export interface McpLocalConfig {
|
|
|
29
28
|
localEndpoint?: string;
|
|
30
29
|
};
|
|
31
30
|
profile?: string;
|
|
32
|
-
toolset?: 'facade' | 'core' | 'advanced' | 'full';
|
|
33
|
-
/** Compatibility marker retained for previously persisted toolset choices. */
|
|
34
|
-
toolsetExplicit?: boolean;
|
|
35
31
|
accessMode?: AccessMode;
|
|
36
32
|
accessModeUpdatedAt?: string;
|
|
37
33
|
accessModeRevision?: number;
|
|
@@ -90,10 +86,10 @@ export interface McpRuntimeState {
|
|
|
90
86
|
forgeVersion?: string;
|
|
91
87
|
toolSurfaceFingerprint?: string;
|
|
92
88
|
runtimeToolSurfaceFingerprint?: string;
|
|
93
|
-
toolset?: '
|
|
89
|
+
toolset?: 'full';
|
|
94
90
|
configuredAccessMode?: AccessMode;
|
|
95
91
|
effectiveAccessMode?: AccessMode;
|
|
96
|
-
effectiveToolset?:
|
|
92
|
+
effectiveToolset?: 'full';
|
|
97
93
|
accessModeSource?: string;
|
|
98
94
|
accessModeLastAppliedAt?: string;
|
|
99
95
|
exposureRevision?: number;
|
|
@@ -12,13 +12,13 @@ export function mcpServerInstructions(profile: McpProfileName): string {
|
|
|
12
12
|
'Use the shortest valid execution path. Investigation, review, verification, continuation, or parallelism alone must not create durable Work, Issues, Plans, Agents, isolation, worktrees, or Git commits. Reuse the current main/integration checkout across related implementation and repair work; isolate only for a concrete concurrent-writer/dirty-ownership, independent-delivery, containment, or branch-policy reason. Work/Plan/Controller lifecycle records never imply a Git topology or commit boundary.',
|
|
13
13
|
'Engineering receipts are optional structured context: never invent Project Contract, Context Closure, Product DoD, design, critique, semantic-tool, validation, journey, or review receipt ids from prose. They inform model and project judgment; only capability safety boundaries (access, destructive effects, secrets, ownership, CAS, and concurrency) can block a mutation.',
|
|
14
14
|
'Context Closure returned by rh_context.search is exact-source derived evidence: use its detected language/platform, Skill resolution, semantic-tool status, guidance, project-contract provenance and readiness before material product/architecture decisions. Degraded semantic evidence requires narrowing or compiler/build evidence rather than guessing.',
|
|
15
|
-
'For bounded implementation,
|
|
15
|
+
'For bounded implementation, the model selects rh_context semantic retrieval, execute_command terminal search, or exact file reads according to the evidence required; no mandatory retrieval router or tool sequence is imposed. If rh_context is used, follow with exact paths, symbols, tests, relationships, or impact domains only when that materially improves correctness. Its current raw snippets and file SHA identities are valid implementation evidence; ChatGPT alone decides semantic sufficiency. For cross-cutting state/time/schema/cache/API/event/concurrency changes, ChatGPT should select relevant impact_domains so Forge expands those evidence dimensions mechanically; Forge never infers that the selected domains are semantically complete. Use retrieval_mode=plan|debug|review when the task intentionally needs a broader evidence surface. ChatGPT chooses semantic source retrieval or direct terminal search according to what the task actually requires; Forge does not rank tools as a workflow policy. The low-level search_repository handler is an internal compatibility surface. Use execute_command for ordinary local Git and shell operations, repository_safe_patch_apply for bounded source changes, and rh_work only when the task genuinely requires durable orchestration. Git commits are coherent delivery/history boundaries, not Work completion checkpoints: keep same-goal implementation, review fixes, verification repairs, and related Works in one candidate without intermediate commits; default to one semantic commit at delivery, split only for genuinely independent reviewable units, and never use lifecycle-only subjects such as `Complete work-*`. Ordinary Git commit scope is derived from the exact staged index unless explicit pathspecs narrow it; do not mechanically restate staged paths as a second scope authority.',
|
|
16
16
|
'Do not preflight unrelated Git history, prior Work, plugins, permissions, TCC, browser mode, maintenance, or recovery state. Inspect them only when the requested operation depends on them or a real failure points there.',
|
|
17
17
|
'Self-learning is model-driven, not phrase-driven. Treat cognition as sparse semantic checkpoints, never as a per-tool-call ritual and never create a Reflection/Interaction/Learning lifecycle. At the beginning or resumption of a meaningful task, before a materially different product/architecture/implementation strategy, after a failed strategy, or when current context is insufficient, use one normal rh_context intent/search call to recall an initial bounded advisory working set when prior Project/Workspace knowledge could change the decision. Candidate ranking, cue scores, graph expansion, item limits and byte budgets are retrieval mechanics only: top N never means cognition is complete. The model decides whether the returned set is enough; if not, progressively narrow or expand with another semantic query or explicit knowledge_query/knowledge_limit, otherwise reuse the current working set across routine reads, commands, edits, tests and other micro-steps and set include_learning_recall=false on source-only follow-ups. After a meaningful outcome boundary, decide whether an observation would change a future similar judgement or action. Emit zero learning when it would not. For ordinary successful work, prefer attaching one optional cognition_settlement to the successful tool call that closes that meaningful outcome; it can carry model-authored learning_signals and exact-memory learning_feedback and Forge settles them mechanically after the primary tool result. Do not attach cognition_settlement to routine micro-steps merely because the field exists. Use rh_work learning_record/learning_feedback when there is no natural successful tool boundary, when an explicit standalone learning action is clearer, or when a selected learning batch exceeds one transport envelope. User corrections, discovered root causes, successful shorter paths, review reversals, stable architecture/product/UX patterns and execution outcomes may all become learning when the model judges them reusable. Distill observations into semantic deltas rather than copying source prose: prefer a Project-specific lesson about how to judge/do the work, and write Workspace guidance only when the model explicitly judges the abstraction genuinely cross-project. Forge never decides that from kind, keywords, failure count, repetition count, confidence threshold, or execution codes. learning_signals kind is descriptive open vocabulary; the per-call array bound is only a transport envelope, not an interaction quota, so submit further learning_record batches when the model selected more reusable knowledge than one envelope carries. Forge may associate/consolidate representations and usage mechanically but must not invent a higher-level lesson or cross-scope generalization. Work-bound Controller rounds keep assistant_context_usage as the exact used/rejected feedback channel. Outside Work, do not manufacture a usage lifecycle merely to count feedback. When recalled memory materially helps or is rejected, use learning_feedback with its exact memory address and model-authored irrelevant/stale/contradicted classification. A new correction/contradiction/other learning signal requires genuinely new reusable semantic knowledge, not merely that an old memory was used.',
|
|
18
18
|
'Minimize mechanically redundant MCP round trips. For an ongoing Forge-controlled task, prefer one rh_status(summary) as the initial controller snapshot: it includes bounded readiness, repository identity, top active Work, active Plans, and pending handoffs. Do not immediately follow it with rh_work(plan_list) or rh_context(list/get) unless the next decision needs details that the snapshot does not contain. Use rh_context(work_id=..., detail_level=summary) for one Work, and request detail/raw or capability hydration only when that extra evidence can change the decision.',
|
|
19
19
|
'Run independent reads, searches, diagnostics, checks, repositories, checkouts, and tasks concurrently by default. Serialize only on a real dependency or Resource Claim conflict, and reuse existing projections, process handles, and check evidence instead of repeating successful observations.',
|
|
20
20
|
'Validation must be proportional to the change: cheap patch-safety checks belong to every coherent edit batch, focused tests run after that batch is stable, and expensive full-suite checks belong at candidate/release or explicitly high-risk boundaries. When a Direct Edit batch is stable, repository_safe_patch_apply may receive explicit check_ids to start revision-bound validation; omit check_ids during intermediate edits. Long checks return managed Processes: continue independent read/review work. Do not call process_wait while useful independent work remains. Use process_get only when a non-blocking observation can change the next decision; when the exact check result becomes a dependency, attach with process_wait (or validation_only with the returned edit-session/validation ids). An attach is transport-bounded: if it returns a running handle, resume useful work and attach again only at a later real dependency boundary, never as periodic polling. Do not perform maintenance or deep runtime diagnosis before ordinary work unless execution is actually blocked.',
|
|
21
|
-
'repository_safe_patch_apply returns bounded edit-session diff evidence for semantic review. Review that evidence directly instead of issuing a mandatory follow-up diff/status call; expand it only when truncated or semantically ambiguous. Its typed operation contract is strict: use type=create only for a path that does not exist, and use write/replace/insert/prepend/append/delete only for existing targets; never reinterpret a missing write target as create. Whole-file source replacement is valid when it is an explicit scoped write against the exact expected file SHA/fingerprint; stale-source and dirty-ownership fences must remain authoritative. Do not bypass those fences with arbitrary shell redirection merely because a deterministic rewrite is convenient. After a bounded successful change, finish the requested delivery directly. Avoid additional investigation whose result cannot change implementation or acceptance, and do not invoke
|
|
21
|
+
'repository_safe_patch_apply returns bounded edit-session diff evidence for semantic review. Review that evidence directly instead of issuing a mandatory follow-up diff/status call; expand it only when truncated or semantically ambiguous. Its typed operation contract is strict: use type=create only for a path that does not exist, and use write/replace/insert/prepend/append/delete only for existing targets; never reinterpret a missing write target as create. Whole-file source replacement is valid when it is an explicit scoped write against the exact expected file SHA/fingerprint; stale-source and dirty-ownership fences must remain authoritative. Do not bypass those fences with arbitrary shell redirection merely because a deterministic rewrite is convenient. After a bounded successful change, finish the requested delivery directly. Avoid additional investigation whose result cannot change implementation or acceptance, and do not invoke redundant atomic compatibility tools merely because they remain registered.',
|
|
22
22
|
].join(' ');
|
|
23
23
|
}
|
|
24
24
|
if (profile === 'orchestrator') {
|
|
@@ -2,8 +2,7 @@ import { loadMcpLocalConfig, loadMcpServiceLocalConfig } from './auth';
|
|
|
2
2
|
import { getMcpPolicy, parseMcpProfile } from './policy';
|
|
3
3
|
import { buildMcpToolDefinitions, callMcpTool, type CallToolResult, type McpToolContext, type McpToolDefinition } from './tool-mapping/tools';
|
|
4
4
|
import { DEFAULT_AGENT_TIMEOUT_MS, MAX_AGENT_TIMEOUT_MS, normalizeAgentTimeoutMs } from '../../src/cli/controller/runtime-config';
|
|
5
|
-
import type { McpAgentRunnerName
|
|
6
|
-
import { configuredControllerToolset } from './toolset-selection';
|
|
5
|
+
import type { McpAgentRunnerName } from './types';
|
|
7
6
|
import { realpathSync } from 'fs';
|
|
8
7
|
import { resolve } from 'path';
|
|
9
8
|
import { ensureRepoPreferredControllerHome } from '../../src/cli/repositories/controller-home';
|
|
@@ -28,7 +27,6 @@ export interface McpServerOptions {
|
|
|
28
27
|
devRunnerAgents?: string;
|
|
29
28
|
devRunnerTimeoutMs?: number;
|
|
30
29
|
devRunnerMaxTimeoutMs?: number;
|
|
31
|
-
toolset?: McpToolset | string;
|
|
32
30
|
sessionId?: string;
|
|
33
31
|
principalId?: string;
|
|
34
32
|
controllerType?: McpToolContext['controllerType'];
|
|
@@ -42,8 +40,7 @@ export interface McpServerOptions {
|
|
|
42
40
|
export interface MultiRepositoryMcpToolContext extends McpToolContext {
|
|
43
41
|
controllerHome: string;
|
|
44
42
|
explicitRepository?: RepositoryRecord;
|
|
45
|
-
toolset:
|
|
46
|
-
toolsetLocked: boolean;
|
|
43
|
+
toolset: 'full';
|
|
47
44
|
runtimeSourceRoot?: string;
|
|
48
45
|
}
|
|
49
46
|
|
|
@@ -66,14 +63,6 @@ const REPOSITORY_LOCKED_TOOLS = new Set([
|
|
|
66
63
|
]);
|
|
67
64
|
|
|
68
65
|
|
|
69
|
-
export function parseMcpToolset(value: unknown, profile: string): McpToolset {
|
|
70
|
-
if (profile !== 'controller') return 'full';
|
|
71
|
-
const normalized = typeof value === 'string' ? value.trim().toLowerCase() : '';
|
|
72
|
-
if (!normalized) return 'advanced';
|
|
73
|
-
if (normalized === 'facade' || normalized === 'core' || normalized === 'advanced' || normalized === 'full') return normalized;
|
|
74
|
-
throw new Error(`invalid MCP toolset "${String(value)}" (expected: facade, core, advanced, or full)`);
|
|
75
|
-
}
|
|
76
|
-
|
|
77
66
|
function repositoryForExplicitPath(path: string, controllerHome: string): RepositoryRecord | undefined {
|
|
78
67
|
const selectedPath = (() => {
|
|
79
68
|
try { return realpathSync(path); } catch { return resolve(path); }
|
|
@@ -284,8 +273,7 @@ export function repositoryScopedToolArgs(
|
|
|
284
273
|
// checkout after validating the authenticated principal instead of inheriting
|
|
285
274
|
// the repository's currently active checkout.
|
|
286
275
|
const workOwnedCheckoutResolution = [
|
|
287
|
-
'
|
|
288
|
-
'repository_command_execute',
|
|
276
|
+
'execute_command',
|
|
289
277
|
'repository_safe_patch_apply',
|
|
290
278
|
].includes(name)
|
|
291
279
|
&& typeof input.work_id === 'string'
|
|
@@ -333,24 +321,14 @@ export function createMcpToolContext(opts: McpServerOptions): MultiRepositoryMcp
|
|
|
333
321
|
: undefined;
|
|
334
322
|
const policyRoot = explicitRepository?.canonicalRoot ?? controllerHome;
|
|
335
323
|
const policy = runtimePolicy(policyRoot, { ...opts, controllerHome });
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
: loadMcpLocalConfig(policyRoot);
|
|
339
|
-
const toolsetOverride = opts.toolset ?? process.env.FORGE_MCP_TOOLSET;
|
|
340
|
-
const configuredToolset = policy.profile === 'controller'
|
|
341
|
-
? configuredControllerToolset(config)
|
|
342
|
-
: config?.toolset;
|
|
343
|
-
const toolset = parseMcpToolset(
|
|
344
|
-
toolsetOverride ?? configuredToolset,
|
|
345
|
-
policy.profile,
|
|
346
|
-
);
|
|
324
|
+
// All Forge capabilities share one public MCP tool registry.
|
|
325
|
+
// Host authorization remains independent of tool discovery.
|
|
347
326
|
return {
|
|
348
327
|
controllerHome,
|
|
349
328
|
explicitRepository,
|
|
350
329
|
repoRoot: policyRoot,
|
|
351
330
|
policy,
|
|
352
|
-
toolset,
|
|
353
|
-
toolsetLocked: toolsetOverride !== undefined || configuredToolset !== undefined,
|
|
331
|
+
toolset: 'full',
|
|
354
332
|
enableChatgptBrowser: opts.enableChatgptBrowser === true,
|
|
355
333
|
sessionId: opts.sessionId,
|
|
356
334
|
principalId: opts.principalId,
|
|
@@ -402,6 +380,7 @@ export async function callMultiRepositoryTool(
|
|
|
402
380
|
controllerInstanceId: ctx.controllerInstanceId,
|
|
403
381
|
repoId: repository.repoId,
|
|
404
382
|
checkoutId: repository.activeCheckoutId,
|
|
383
|
+
publishedToolNames: ctx.publishedToolNames,
|
|
405
384
|
};
|
|
406
385
|
const invoke = async () => await callMcpTool(scopedContext, name, scopedArgs) as ToolResult;
|
|
407
386
|
const result = REPOSITORY_LOCKED_TOOLS.has(name)
|
|
@@ -1,20 +1,20 @@
|
|
|
1
1
|
import { createHash } from "crypto";
|
|
2
|
-
import { existsSync } from "fs";
|
|
3
|
-
import { join } from "path";
|
|
4
2
|
import { defaultSemanticProviderRegistry, type SemanticNavigationKind, type SemanticNavigationRequest } from "../../../src/runtime/context/semantic-navigation";
|
|
3
|
+
import { isIncidentalSemanticDependencyProbe } from "../../../src/runtime/context/semantic-navigation-contract";
|
|
5
4
|
import { buildContextClosureReceipt } from "../../../src/runtime/context/context-closure";
|
|
6
|
-
import {
|
|
5
|
+
import { readWorkHandle } from "../../../src/runtime/control-plane/execution/work-handle-store";
|
|
7
6
|
import type { MultiRepositoryMcpToolContext } from "../multi-repository";
|
|
8
7
|
import { result } from "./result-adapter";
|
|
9
8
|
import { boundedRuntimeResult } from "./bounded-result-runtime";
|
|
10
9
|
import { selected } from "./shared-adapter";
|
|
11
10
|
import { resolveMcpPath } from "../paths";
|
|
12
11
|
import { freshGitIdentity } from "../../../src/cli/repository/inspector";
|
|
13
|
-
import {
|
|
12
|
+
import { repositorySummary, resolveRepositorySelection } from "../../../src/cli/repositories/registry";
|
|
14
13
|
import { getExecutionJob, listExecutionJobs } from "../../../src/runtime/execution/jobs/store";
|
|
15
14
|
import { isManagedProcessActive, listProcessRecords, listRecoverableProcessRecords, processRuntimeResourceDiagnostics } from "../../../src/runtime/execution/process-runtime";
|
|
16
15
|
import { CONTROLLER_CONTEXT_IMPACT_DOMAINS, type ControllerContextImpactDomain } from "../../../src/cli/controller/context/types";
|
|
17
16
|
import { buildControllerContextPackInSidecar } from "../../../src/runtime/context/context-pack-process";
|
|
17
|
+
import { isCodeShapedTerm } from "../../../src/cli/controller/context/query-planning";
|
|
18
18
|
import { listControllerChecks } from "../../../src/cli/controller/check-runner";
|
|
19
19
|
import { getAssistantPluginManifest, getControllerPluginManifest, listAssistantPluginManifests, listControllerPluginManifests } from "../../../src/runtime/plugins/store";
|
|
20
20
|
import { allowedFacadeOperations, buildFacadeResult, listCapabilityDescriptors, getCapabilityDescriptor, getCoreCapabilityExecutionSchema, getPluginActionCapabilitySchema, searchCapabilityDescriptors, summarizeCapabilityGroups, listHandoffAttentionItems, listHandoffItems, normalizeCheckIds, summarizeHandoffItem, buildWorkContinuationSnapshot, getPlanContract, planSemanticView, FACADE_TOOLS } from "../../../src/runtime/control-plane/facade";
|
|
@@ -280,6 +280,7 @@ interface RhContextSemanticNavigationProjection {
|
|
|
280
280
|
providers: unknown[];
|
|
281
281
|
policyDeniedLocations: number;
|
|
282
282
|
policyDeniedReads: number;
|
|
283
|
+
materialPolicyDeniedReads: number;
|
|
283
284
|
policyDeniedReadSamples: string[];
|
|
284
285
|
requestTruncated: boolean;
|
|
285
286
|
freshness: 'not_requested' | 'changed_during_query' | 'current_at_query';
|
|
@@ -300,6 +301,7 @@ async function rhContextSemanticNavigation(
|
|
|
300
301
|
let anyLocationTruncated = false;
|
|
301
302
|
let policyDeniedLocations = 0;
|
|
302
303
|
let policyDeniedReads = 0;
|
|
304
|
+
let materialPolicyDeniedReads = 0;
|
|
303
305
|
const policyDeniedReadSamples = new Set<string>();
|
|
304
306
|
const semanticAccessScope = createHash('sha256')
|
|
305
307
|
.update(JSON.stringify({ profile: policy.profile, readGlobs: policy.readGlobs, denyGlobs: policy.denyGlobs }))
|
|
@@ -347,6 +349,7 @@ async function rhContextSemanticNavigation(
|
|
|
347
349
|
const decision = resolveMcpPath(repoRoot, relativePath, policy, 'read');
|
|
348
350
|
if (decision.ok) return true;
|
|
349
351
|
policyDeniedReads += 1;
|
|
352
|
+
if (!isIncidentalSemanticDependencyProbe(relativePath)) materialPolicyDeniedReads += 1;
|
|
350
353
|
if (policyDeniedReadSamples.size < 20) policyDeniedReadSamples.add(relativePath);
|
|
351
354
|
return false;
|
|
352
355
|
};
|
|
@@ -372,6 +375,7 @@ async function rhContextSemanticNavigation(
|
|
|
372
375
|
const semantic = outcome.result;
|
|
373
376
|
const sidecarDeniedReads = Math.max(0, Number(semantic.policyDeniedReads ?? 0));
|
|
374
377
|
policyDeniedReads += sidecarDeniedReads;
|
|
378
|
+
materialPolicyDeniedReads += Math.max(0, Number(semantic.materialPolicyDeniedReads ?? sidecarDeniedReads));
|
|
375
379
|
const sidecarDeniedSamples = semantic.details?.policyDeniedReadSamples;
|
|
376
380
|
if (Array.isArray(sidecarDeniedSamples)) {
|
|
377
381
|
for (const sample of sidecarDeniedSamples) {
|
|
@@ -385,6 +389,11 @@ async function rhContextSemanticNavigation(
|
|
|
385
389
|
policyDeniedLocations += 1;
|
|
386
390
|
return false;
|
|
387
391
|
});
|
|
392
|
+
// With no resolved requested relationship, even dependency-only denied reads
|
|
393
|
+
// may conceal material evidence. Preserve admission blocking for this case.
|
|
394
|
+
if (allowedLocations.length === 0 && sidecarDeniedReads > 0 && Number(semantic.materialPolicyDeniedReads ?? sidecarDeniedReads) === 0) {
|
|
395
|
+
materialPolicyDeniedReads += sidecarDeniedReads;
|
|
396
|
+
}
|
|
388
397
|
const truncated = allowedLocations.length > RH_CONTEXT_SEMANTIC_LOCATION_LIMIT;
|
|
389
398
|
anyLocationTruncated ||= truncated;
|
|
390
399
|
results.push({
|
|
@@ -398,6 +407,7 @@ async function rhContextSemanticNavigation(
|
|
|
398
407
|
returnedLocations: Math.min(allowedLocations.length, RH_CONTEXT_SEMANTIC_LOCATION_LIMIT),
|
|
399
408
|
truncated,
|
|
400
409
|
policyDeniedReads: semantic.policyDeniedReads ?? 0,
|
|
410
|
+
materialPolicyDeniedReads: semantic.materialPolicyDeniedReads ?? semantic.policyDeniedReads ?? 0,
|
|
401
411
|
...(semantic.details ?? {}),
|
|
402
412
|
});
|
|
403
413
|
}
|
|
@@ -426,7 +436,7 @@ async function rhContextSemanticNavigation(
|
|
|
426
436
|
}
|
|
427
437
|
|
|
428
438
|
const requestTruncated = raw.length > RH_CONTEXT_SEMANTIC_QUERY_LIMIT;
|
|
429
|
-
const incomplete = requestTruncated || anyLocationTruncated || policyDeniedLocations > 0 ||
|
|
439
|
+
const incomplete = requestTruncated || anyLocationTruncated || policyDeniedLocations > 0 || materialPolicyDeniedReads > 0 || errors.length > 0 || sourceChangedDuringQuery;
|
|
430
440
|
const languages = new Set(results.map((entry) => String(entry.language ?? '')).filter(Boolean));
|
|
431
441
|
const singleLanguage = languages.size === 1 ? [...languages][0] : undefined;
|
|
432
442
|
const scope = singleLanguage === 'typescript'
|
|
@@ -446,6 +456,7 @@ async function rhContextSemanticNavigation(
|
|
|
446
456
|
providers: defaultSemanticProviderRegistry.list(),
|
|
447
457
|
policyDeniedLocations,
|
|
448
458
|
policyDeniedReads,
|
|
459
|
+
materialPolicyDeniedReads,
|
|
449
460
|
policyDeniedReadSamples: Array.from(policyDeniedReadSamples),
|
|
450
461
|
requestTruncated,
|
|
451
462
|
freshness: raw.length === 0 ? 'not_requested' : sourceChangedDuringQuery ? 'changed_during_query' : 'current_at_query',
|
|
@@ -467,35 +478,59 @@ async function rhContextSemanticNavigation(
|
|
|
467
478
|
};
|
|
468
479
|
}
|
|
469
480
|
|
|
470
|
-
function codegraphIndexExists(controllerHome: string, root: string): boolean {
|
|
471
|
-
return existsSync(join(codegraphRepositoryCacheRoot(controllerHome, root), 'codegraph.db'))
|
|
472
|
-
|| existsSync(join(root, '.codegraph', 'codegraph.db'));
|
|
473
|
-
}
|
|
474
|
-
|
|
475
481
|
/**
|
|
476
|
-
*
|
|
477
|
-
*
|
|
478
|
-
*
|
|
479
|
-
*
|
|
482
|
+
* Work-specific source evidence uses the exact registered physical placement.
|
|
483
|
+
* Work semantic identity stays in Kernel; Repository Registry owns checkout
|
|
484
|
+
* resolution. This projection neither creates source state nor chooses a
|
|
485
|
+
* different checkout when the exact Work target has disappeared.
|
|
480
486
|
*/
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
const
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
const
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
487
|
+
function selectContextSourceRepository(
|
|
488
|
+
ctx: MultiRepositoryMcpToolContext,
|
|
489
|
+
args: Record<string, unknown>,
|
|
490
|
+
operation: string,
|
|
491
|
+
): ReturnType<typeof selected> {
|
|
492
|
+
const workId = operation === 'search' && typeof args.query === 'string' && args.query.trim()
|
|
493
|
+
&& typeof args.work_id === 'string' ? args.work_id.trim() : '';
|
|
494
|
+
if (!workId) return selected(ctx, args);
|
|
495
|
+
const repository = selected(ctx, { ...args, checkout_id: undefined });
|
|
496
|
+
const work = getWorkContract({ controllerHome: ctx.controllerHome, repoId: repository.repoId }, workId);
|
|
497
|
+
if (!work) throw new Error(`CONTEXT_WORK_NOT_FOUND: ${workId}`);
|
|
498
|
+
if (work.repoId && work.repoId !== repository.repoId) {
|
|
499
|
+
throw new Error(`CONTEXT_WORK_REPOSITORY_MISMATCH: ${workId}`);
|
|
500
|
+
}
|
|
501
|
+
const handle = readWorkHandle(ctx.controllerHome, repository.repoId, workId);
|
|
502
|
+
const workCheckoutId = handle?.checkoutId?.trim()
|
|
503
|
+
|| work.executionPlacement?.checkoutId?.trim()
|
|
504
|
+
|| work.checkoutId?.trim();
|
|
505
|
+
// Pure semantic Work has no physical source. Keep explicitly elected
|
|
506
|
+
// repository context for this case instead of inventing source placement.
|
|
507
|
+
if (!workCheckoutId) return selected(ctx, args);
|
|
508
|
+
const requestedCheckoutId = typeof args.checkout_id === 'string' ? args.checkout_id.trim() : '';
|
|
509
|
+
if (requestedCheckoutId && requestedCheckoutId !== workCheckoutId) {
|
|
510
|
+
throw new Error(`CONTEXT_WORK_CHECKOUT_MISMATCH: ${workId}`);
|
|
511
|
+
}
|
|
512
|
+
if (handle?.state === 'cleaned' || handle?.state === 'failed') {
|
|
513
|
+
throw new Error(`CONTEXT_WORK_CHECKOUT_RETIRED: ${workId}`);
|
|
514
|
+
}
|
|
515
|
+
let selectedCheckout: ReturnType<typeof selected>;
|
|
516
|
+
try {
|
|
517
|
+
selectedCheckout = selected(ctx, { ...args, repo_id: repository.repoId, checkout_id: workCheckoutId });
|
|
518
|
+
} catch {
|
|
519
|
+
throw new Error(`CONTEXT_WORK_CHECKOUT_UNAVAILABLE: ${workId}`);
|
|
520
|
+
}
|
|
521
|
+
if (selectedCheckout.activeCheckoutId !== workCheckoutId) {
|
|
522
|
+
throw new Error(`CONTEXT_WORK_CHECKOUT_MISMATCH: ${workId}`);
|
|
523
|
+
}
|
|
524
|
+
if (handle?.expectedHead && freshGitIdentity(selectedCheckout.canonicalRoot).head !== handle.expectedHead) {
|
|
525
|
+
throw new Error(`CONTEXT_WORK_SOURCE_REVISION_MISMATCH: ${workId}`);
|
|
526
|
+
}
|
|
527
|
+
return selectedCheckout;
|
|
493
528
|
}
|
|
494
529
|
|
|
495
530
|
export async function callContextAdapter(ctx: MultiRepositoryMcpToolContext, name: string, args: Record<string, unknown>): Promise<CallToolResult | undefined> {
|
|
496
531
|
if (name !== 'rh_context') return undefined;
|
|
497
|
-
const repository = selected(ctx, args);
|
|
498
532
|
const operation = String(args.operation ?? 'get');
|
|
533
|
+
const repository = selectContextSourceRepository(ctx, args, operation);
|
|
499
534
|
if (!allowedFacadeOperations('rh_context').includes(operation)) {
|
|
500
535
|
return invalidFacadeOperation('rh_context', operation);
|
|
501
536
|
}
|
|
@@ -543,11 +578,10 @@ export async function callContextAdapter(ctx: MultiRepositoryMcpToolContext, nam
|
|
|
543
578
|
: 'implementation';
|
|
544
579
|
const impactDomains = list(args.impact_domains)
|
|
545
580
|
.filter((domain): domain is ControllerContextImpactDomain => CONTROLLER_CONTEXT_IMPACT_DOMAINS.includes(domain as ControllerContextImpactDomain));
|
|
581
|
+
// CodeGraph is opt-in. Ordinary source location never starts a graph index.
|
|
546
582
|
const structuralContext = args.structural_context === 'off' || args.structural_context === 'auto' || args.structural_context === 'required'
|
|
547
583
|
? args.structural_context
|
|
548
|
-
:
|
|
549
|
-
? 'required'
|
|
550
|
-
: 'auto';
|
|
584
|
+
: 'off';
|
|
551
585
|
const requestedCheckIds = list(args.requested_check_ids);
|
|
552
586
|
// Source retrieval is the default Context responsibility. Check
|
|
553
587
|
// readiness is only meaningful when the controller has named the
|
|
@@ -562,23 +596,26 @@ export async function callContextAdapter(ctx: MultiRepositoryMcpToolContext, nam
|
|
|
562
596
|
checkoutId: repository.activeCheckoutId,
|
|
563
597
|
})
|
|
564
598
|
: undefined;
|
|
599
|
+
// GPT supplies repository-language code hints for natural-language intents.
|
|
600
|
+
// Forge only searches those literal terms; it does not translate or infer semantics.
|
|
601
|
+
const codeSearchTerms = list(args.search_terms).filter((term) => term.length <= 160).slice(0, 8);
|
|
602
|
+
const exactCodeQuery = !/\s/u.test(retrievalQuery) && isCodeShapedTerm(retrievalQuery);
|
|
565
603
|
const pack = await buildControllerContextPackInSidecar({
|
|
566
604
|
repoRoot: repository.canonicalRoot,
|
|
567
605
|
policy: ctx.policy,
|
|
568
606
|
options: {
|
|
569
607
|
description: retrievalQuery,
|
|
570
|
-
//
|
|
571
|
-
//
|
|
572
|
-
//
|
|
573
|
-
|
|
574
|
-
|
|
608
|
+
// Only an actual single code identifier is an implicit exact needle.
|
|
609
|
+
// Prose (including mixed-language queries) stays in description; the
|
|
610
|
+
// model supplies matching source-language hints through search_terms.
|
|
611
|
+
searchTerms: codeSearchTerms.length ? codeSearchTerms
|
|
612
|
+
: exactCodeQuery ? [retrievalQuery] : undefined,
|
|
575
613
|
knownPaths: list(args.known_paths),
|
|
576
614
|
includeGlobs: list(args.include_globs),
|
|
577
615
|
excludeGlobs: list(args.exclude_globs),
|
|
578
616
|
maxFiles: typeof args.max_files === 'number' ? args.max_files : undefined,
|
|
579
617
|
maxSnippets: typeof args.max_snippets === 'number' ? args.max_snippets : undefined,
|
|
580
618
|
structuralContext,
|
|
581
|
-
structuralIndexRoot: structuralContext === 'off' ? undefined : resolveStructuralIndexRoot(ctx.controllerHome, repository),
|
|
582
619
|
retrievalMode,
|
|
583
620
|
impactDomains,
|
|
584
621
|
session: rhContextReadSessionId(ctx)
|
|
@@ -602,7 +639,7 @@ export async function callContextAdapter(ctx: MultiRepositoryMcpToolContext, nam
|
|
|
602
639
|
const semanticReasonCodes = Array.from(new Set([
|
|
603
640
|
...semanticNavigation.errors.map((entry) => `semantic.${String(entry.code ?? 'provider_error').toLowerCase()}`),
|
|
604
641
|
...(semanticNavigation.requestTruncated ? ['semantic.request_truncated'] : []),
|
|
605
|
-
...(semanticNavigation.policyDeniedLocations > 0 || semanticNavigation.
|
|
642
|
+
...(semanticNavigation.policyDeniedLocations > 0 || semanticNavigation.materialPolicyDeniedReads > 0 ? ['semantic.policy_denied'] : []),
|
|
606
643
|
...(semanticNavigation.freshness === 'changed_during_query' ? ['semantic.source_changed_during_query'] : []),
|
|
607
644
|
...(semanticNavigation.requested > 0 && semanticNavigation.staticClosure.status !== 'complete_for_requested_symbols' ? ['semantic.static_closure_incomplete'] : []),
|
|
608
645
|
]));
|
|
@@ -680,7 +717,7 @@ export async function callContextAdapter(ctx: MultiRepositoryMcpToolContext, nam
|
|
|
680
717
|
} : {}),
|
|
681
718
|
retrievalPolicy: {
|
|
682
719
|
defaultBackend: 'bounded_lexical',
|
|
683
|
-
structuralBackend: '
|
|
720
|
+
structuralBackend: 'retired',
|
|
684
721
|
rawReadTool: 'read_repository_file',
|
|
685
722
|
shellSearchFallbackOnly: true,
|
|
686
723
|
executionReadiness: 'requested_check_ids_only',
|
|
@@ -801,11 +838,13 @@ export async function callContextAdapter(ctx: MultiRepositoryMcpToolContext, nam
|
|
|
801
838
|
const learningRecall = args.include_learning_recall === false
|
|
802
839
|
? undefined
|
|
803
840
|
: rhContextLearningRecall(ctx, repository, capabilityIntentQuery);
|
|
804
|
-
|
|
841
|
+
// Intent-ranked capability discovery must reflect the executable plugin contract.
|
|
842
|
+
// Persisted manifests are status projections and may outlive a source release that moved an action.
|
|
843
|
+
const manifestOptions = { preferStored: false };
|
|
805
844
|
const repositoryManifests = listAssistantPluginManifests(ctx.controllerHome, repository, manifestOptions);
|
|
806
845
|
const controllerManifests = listControllerPluginManifests(ctx.controllerHome, manifestOptions);
|
|
807
846
|
const manifests = [...new Map(
|
|
808
|
-
[...
|
|
847
|
+
[...controllerManifests, ...repositoryManifests].map((manifest) => [manifest.pluginId, manifest] as const),
|
|
809
848
|
).values()];
|
|
810
849
|
const matches = searchCapabilityDescriptors(capabilityIntentQuery, manifests, 12)
|
|
811
850
|
.map((match) => ({
|
|
@@ -5,7 +5,6 @@ import type { McpPolicy, McpProfileName } from '../types';
|
|
|
5
5
|
import { buildControllerContextPackAsync } from '../../../src/cli/controller/context-pack';
|
|
6
6
|
import type { ControllerContextPackOptions } from '../../../src/cli/controller/context/types';
|
|
7
7
|
import { PROCESS_RUNTIME_RELEASE_CANARY_ARG } from '../../../src/runtime/execution/process-runtime/canary';
|
|
8
|
-
import { closeCodeGraphReadProviderSessions } from '../../../src/runtime/context/codegraph-read-provider';
|
|
9
8
|
|
|
10
9
|
interface ContextPackEnvelope {
|
|
11
10
|
schemaVersion: 1;
|
|
@@ -65,20 +64,13 @@ export async function runContextPackSidecar(
|
|
|
65
64
|
if (envelope.schemaVersion !== 1 || typeof envelope.repoRoot !== 'string' || !envelope.repoRoot.trim()) {
|
|
66
65
|
throw new Error('CONTEXT_PACK_REQUEST_INVALID');
|
|
67
66
|
}
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
return 0;
|
|
76
|
-
} finally {
|
|
77
|
-
// The Context Pack async path may reuse a CodeGraph session for progressive
|
|
78
|
-
// waves. This process is intentionally disposable, so do not inherit the
|
|
79
|
-
// normal 30-second provider idle lifetime.
|
|
80
|
-
await closeCodeGraphReadProviderSessions();
|
|
81
|
-
}
|
|
67
|
+
const pack = await buildControllerContextPackAsync(
|
|
68
|
+
resolve(envelope.repoRoot),
|
|
69
|
+
policy(envelope.policy),
|
|
70
|
+
envelope.options && typeof envelope.options === 'object' ? envelope.options : {},
|
|
71
|
+
);
|
|
72
|
+
process.stdout.write(`${JSON.stringify({ schemaVersion: 1, pack })}\n`);
|
|
73
|
+
return 0;
|
|
82
74
|
}
|
|
83
75
|
|
|
84
76
|
if (import.meta.main) {
|