@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.
Files changed (328) hide show
  1. package/AGENTS.md +5 -2
  2. package/CHANGELOG.md +20 -0
  3. package/README.en.md +24 -3
  4. package/README.md +10 -6
  5. package/README.zh-CN.md +5 -3
  6. package/THIRD_PARTY_NOTICES.md +1 -2
  7. package/adapters/chatgpt/browser-delivery-runtime.ts +5 -3
  8. package/adapters/chatgpt/provider-delivery.ts +1 -1
  9. package/adapters/chatgpt/work-conversation-binding-store.ts +20 -1
  10. package/adapters/computer/chatgpt-extension-target.ts +295 -50
  11. package/adapters/computer/chrome-extension/background.js +227 -71
  12. package/adapters/computer/chrome-extension/content.js +181 -157
  13. package/adapters/computer/chrome-extension/manifest.json +3 -1
  14. package/adapters/computer/desktop-operator-provider.ts +18 -6
  15. package/adapters/computer/interaction-target-authority.ts +39 -9
  16. package/adapters/mcp/access-mode.ts +18 -39
  17. package/adapters/mcp/auth.ts +2 -6
  18. package/adapters/mcp/instructions.ts +2 -2
  19. package/adapters/mcp/multi-repository.ts +7 -28
  20. package/adapters/mcp/runtime-gateway/context-adapter.ts +79 -40
  21. package/adapters/mcp/runtime-gateway/context-pack-sidecar.ts +7 -15
  22. package/adapters/mcp/runtime-gateway/execution-tools.ts +7 -13
  23. package/adapters/mcp/runtime-gateway/plugin-adapter.ts +16 -3
  24. package/adapters/mcp/runtime-gateway/process-tools.ts +3 -21
  25. package/adapters/mcp/runtime-gateway/router.ts +3 -5
  26. package/adapters/mcp/runtime-gateway/routing-policy.ts +17 -10
  27. package/adapters/mcp/runtime-gateway/runtime-observation-adapter.ts +1 -1
  28. package/adapters/mcp/runtime-gateway/runtime-tool-definitions.ts +14 -9
  29. package/adapters/mcp/runtime-gateway/runtime-tools.ts +1 -5
  30. package/adapters/mcp/runtime-gateway/scheduler-adapter.ts +36 -1
  31. package/adapters/mcp/runtime-gateway/status-inbox-adapter.ts +58 -4
  32. package/adapters/mcp/runtime-gateway/typescript-navigation-sidecar.ts +7 -3
  33. package/adapters/mcp/runtime-gateway/work-adapter.ts +16 -16
  34. package/adapters/mcp/runtime-gateway/work-input-wire-migration.ts +0 -17
  35. package/adapters/mcp/runtime-gateway/work-semantic-operations.ts +69 -12
  36. package/adapters/mcp/runtime-gateway/workflow-supervisor-adapter.ts +26 -2
  37. package/adapters/mcp/server.ts +24 -3
  38. package/adapters/mcp/setup.ts +4 -10
  39. package/adapters/mcp/tool-mapping/access-tools.ts +0 -6
  40. package/adapters/mcp/tool-mapping/legacy-tool-service.ts +672 -2935
  41. package/adapters/mcp/tool-mapping/post-finalize-work-attribution.ts +1 -1
  42. package/adapters/mcp/tool-mapping/repository-tools.ts +52 -86
  43. package/adapters/mcp/toolset-names.ts +11 -42
  44. package/adapters/mcp/toolset.ts +16 -90
  45. package/{src/cli → adapters}/mcp/transports/AGENTS.md +5 -5
  46. package/{src/cli → adapters}/mcp/transports/CLAUDE.md +5 -5
  47. package/adapters/mcp/transports/http-observation.ts +18 -6
  48. package/adapters/mcp/transports/http-post-lifecycle.ts +19 -0
  49. package/adapters/mcp/transports/http.ts +54 -14
  50. package/adapters/mcp/types.ts +0 -6
  51. package/assets/hooks/lib/session-state.sh +0 -38
  52. package/assets/hooks/post-bash.sh +1 -1
  53. package/assets/hooks/post-tool-observer.sh +0 -5
  54. package/assets/hooks/prompt-guard.sh +0 -55
  55. package/assets/partials/04-project-structure.partial.md +1 -1
  56. package/assets/partials/07-footer.partial.md +1 -1
  57. package/assets/partials-agents/02-operating-mode.partial.md +1 -1
  58. package/assets/partials-agents/08-deep-docs.partial.md +1 -1
  59. package/assets/reference-configs/agentic-development-flow.md +1 -1
  60. package/assets/reference-configs/external-tooling.md +0 -76
  61. package/assets/reference-configs/release-deploy.md +1 -1
  62. package/assets/templates/helpers/architecture-queue.sh +1 -1
  63. package/assets/templates/helpers/check-agent-tooling.sh +1 -327
  64. package/assets/templates/helpers/check-task-workflow.sh +1 -1
  65. package/assets/templates/helpers/ensure-task-workflow.sh +0 -14
  66. package/assets/templates/helpers/migrate-project-template.sh +2 -2
  67. package/assets/workflow-contract.v1.json +0 -2
  68. package/docs/README.md +5 -2
  69. package/docs/architecture/CURRENT.md +114 -30
  70. package/docs/architecture/decisions/20260904-ios-device-migration-baseline.md +42 -0
  71. package/docs/architecture/decisions/20260918-stable-candidate-release-lifecycle.md +15 -2
  72. package/docs/architecture/decisions/20260924-thin-semantic-working-context.md +4 -2
  73. package/docs/architecture/decisions/20261001-supervisor-turn-authority.md +109 -4
  74. package/docs/architecture/decisions/20261009-exact-code-context-discovery.md +19 -0
  75. package/docs/architecture/decisions/20261009-generated-xcode-cache-retention.md +37 -0
  76. package/docs/architecture/decisions/20261011-autonomous-continuation-convergence.md +597 -0
  77. package/docs/architecture/decisions/20261017-edit-session-finalization-reconciliation.md +17 -0
  78. package/docs/architecture/decisions/20261019-retire-host-skills.md +16 -0
  79. package/docs/architecture/diagrams/agentic-dev-plugin-architecture.html +1 -1
  80. package/docs/architecture/domains/public-surface.md +3 -5
  81. package/docs/architecture/domains/verification.md +5 -5
  82. package/docs/architecture/modules/controller-runtime/transport-lifecycle.md +2 -2
  83. package/docs/architecture/requests/2026-09-04-computer-plugin-workflow-taxonomy.md +4 -4
  84. package/docs/architecture/reviews/20261015-code-search-runtime-acceptance.md +75 -0
  85. package/docs/forge-plugin-management.md +14 -2
  86. package/docs/operations/controller-browser-plugin.md +23 -0
  87. package/docs/operations/features.md +6 -7
  88. package/docs/operations/features.zh-CN.md +1 -1
  89. package/docs/operations/github-repository.md +4 -4
  90. package/docs/operations/github-semantic-release-projection.md +20 -0
  91. package/docs/operations/homebrew.md +2 -2
  92. package/docs/operations/mcp-tool-exposure.md +13 -54
  93. package/docs/operations/runtime-performance-diagnostics.md +8 -0
  94. package/docs/operations/standalone-disaster-recovery.md +1 -1
  95. package/docs/tutorials/01-install-and-start.zh-CN.md +1 -1
  96. package/docs/tutorials/03-first-repository-task.md +2 -0
  97. package/docs/wiki/Computer-and-Plugins.md +78 -0
  98. package/docs/wiki/Computer-and-Plugins.zh-CN.md +88 -0
  99. package/docs/wiki/Home.md +16 -2
  100. package/docs/wiki/Implementation.md +24 -12
  101. package/docs/wiki/Installation.md +28 -32
  102. package/docs/wiki/Integrations.md +4 -0
  103. package/docs/wiki/Quick-Start.md +35 -22
  104. package/docs/wiki/Releases-and-Upgrades.md +15 -25
  105. package/docs/wiki/_Sidebar.md +5 -1
  106. package/package.json +1 -12
  107. package/packages/kernel/controller/infrastructure/controller-session-store.ts +63 -38
  108. package/packages/kernel/identity/infrastructure/activity-projection-store.ts +10 -0
  109. package/packages/kernel/scheduler/application/eligibility.ts +4 -1
  110. package/packages/kernel/scheduler/infrastructure/schedule-store.ts +13 -10
  111. package/packages/kernel/work/application/work-service.ts +1 -0
  112. package/packages/kernel/work/domain/repository-completion-receipt.ts +1 -1
  113. package/packages/kernel/work/domain/state-machine.ts +9 -2
  114. package/packages/kernel/work/infrastructure/work-contract-store.ts +28 -1
  115. package/packages/plugin-runtime/browser/provider-registry.ts +12 -36
  116. package/packages/plugin-runtime/computer/conversation-target.ts +17 -7
  117. package/scripts/assemble-template.ts +11 -72
  118. package/scripts/check-mcp-compatibility.ts +55 -74
  119. package/scripts/check-package-identity.mjs +0 -5
  120. package/scripts/check-runtime-architecture.mjs +305 -28
  121. package/scripts/init-project.sh +0 -46
  122. package/scripts/install-ios-hid-toolchain.sh +60 -0
  123. package/scripts/install-standalone-recovery.ts +1 -1
  124. package/scripts/lib/project-init-lib.sh +3 -3
  125. package/scripts/migrate-windows-wsl-controller-home.ts +2 -0
  126. package/scripts/open-source-audit-allowlist.txt +0 -1
  127. package/scripts/run-governed-gate.ts +2 -0
  128. package/scripts/smoke-ios-physical-device.ts +38 -10
  129. package/scripts/smoke-mcp-http-runtime.ts +7 -8
  130. package/scripts/smoke-mcp-tool-surface.ts +27 -41
  131. package/scripts/smoke-workflow-supervisor-chrome-adapter.ts +6 -3
  132. package/scripts/sync-generated-authority.mjs +0 -1
  133. package/src/cli/agent-jobs/executor-health.ts +1 -1
  134. package/src/cli/agent-jobs/integration.ts +2 -2
  135. package/src/cli/chatgpt-browser/file-policy.ts +2 -2
  136. package/src/cli/commands/doctor.ts +1 -143
  137. package/src/cli/commands/global-runtime.ts +3 -196
  138. package/src/cli/commands/init-hook.ts +6 -23
  139. package/src/cli/commands/init.ts +3 -252
  140. package/src/cli/commands/mcp.ts +0 -5
  141. package/src/cli/commands/prompt-guard-decision.ts +0 -2
  142. package/src/cli/commands/recovery.ts +2 -14
  143. package/src/cli/commands/repository.ts +17 -1
  144. package/src/cli/commands/setup-execution.ts +41 -0
  145. package/src/cli/controller/completion-orchestrator.ts +1 -1
  146. package/src/cli/controller/composite-operations.ts +1 -1
  147. package/src/cli/controller/context/instruction-context.ts +1 -1
  148. package/src/cli/controller/context/known-paths.ts +2 -2
  149. package/src/cli/controller/context/query-planning.ts +11 -4
  150. package/src/cli/controller/context/rule-resolution.ts +2 -2
  151. package/src/cli/controller/context/source-materializer.ts +2 -2
  152. package/src/cli/controller/context/structural-provider.ts +84 -0
  153. package/src/cli/controller/context/structural.ts +6 -6
  154. package/src/cli/controller/context/types.ts +8 -9
  155. package/src/cli/controller/context-pack.ts +97 -76
  156. package/src/cli/controller/operational-plan.ts +1 -1
  157. package/src/cli/controller/systemd-user.ts +4 -10
  158. package/src/cli/editing/edit-session.ts +17 -5
  159. package/src/cli/github/github.ts +4 -4
  160. package/src/cli/github/semantic-projection.ts +174 -0
  161. package/src/cli/hook/prompt-intents.ts +0 -9
  162. package/src/cli/index.ts +1 -43
  163. package/src/cli/local-bridge/connector-freshness.ts +1 -12
  164. package/src/cli/local-bridge/console-automations.ts +1 -1
  165. package/src/cli/local-bridge/console-view-models.ts +1 -1
  166. package/src/cli/local-bridge/facade-api.ts +0 -2
  167. package/src/cli/repositories/command-classifier.ts +0 -9
  168. package/src/cli/repositories/command-scope.ts +2 -2
  169. package/src/cli/repositories/locks.ts +20 -0
  170. package/src/cli/repositories/registry.ts +2 -1
  171. package/src/cli/repositories/safe-patch.ts +2 -12
  172. package/src/cli/repositories/structured-git.ts +39 -3
  173. package/src/cli/repository/inspector.ts +185 -343
  174. package/src/effects/process-runner.ts +17 -1
  175. package/src/runtime/context/generic-lsp-provider.ts +1 -1
  176. package/src/runtime/context/semantic-navigation-contract.ts +7 -0
  177. package/src/runtime/context/semantic-navigation.ts +1 -1
  178. package/src/runtime/control-plane/cleanup-artifact-retention.ts +4 -4
  179. package/src/runtime/control-plane/execution/work-delivery-service.ts +150 -31
  180. package/src/runtime/control-plane/execution/work-handle-authority.ts +12 -4
  181. package/src/runtime/control-plane/execution/work-handle-store.ts +53 -1
  182. package/src/runtime/control-plane/execution/work-operation-service.ts +20 -10
  183. package/src/runtime/control-plane/execution/work-preparation-service.ts +108 -4
  184. package/src/runtime/control-plane/execution/work-process-evidence.ts +2 -2
  185. package/src/runtime/control-plane/execution/work-terminal-cleanup.ts +28 -13
  186. package/src/runtime/control-plane/execution/work-validation-reconciler.ts +5 -1
  187. package/src/runtime/control-plane/execution/work-verification-snapshot.ts +1 -1
  188. package/src/runtime/control-plane/facade/capability-registry.ts +2 -0
  189. package/src/runtime/control-plane/facade/work-continuation.ts +1 -1
  190. package/src/runtime/control-plane/generated-cache-retention.ts +114 -63
  191. package/src/runtime/control-plane/global-scheduler/maintenance.ts +20 -8
  192. package/src/runtime/control-plane/global-scheduler/reconciliation.ts +1 -11
  193. package/src/runtime/control-plane/governance/external-effects.ts +1 -1
  194. package/src/runtime/control-plane/launcher/chatgpt-work-continuation.ts +1 -0
  195. package/src/runtime/control-plane/runtime-cleanup.ts +12 -93
  196. package/src/runtime/evidence/sensitive-output.ts +22 -2
  197. package/src/runtime/execution/jobs/child-reference.ts +11 -0
  198. package/src/runtime/execution/jobs/receipt-recovery.ts +1 -11
  199. package/src/runtime/execution/process-runtime/command-facade.ts +3 -3
  200. package/src/runtime/execution/process-runtime/runtime.ts +20 -16
  201. package/src/runtime/execution/thin-harness/execution-router.ts +16 -16
  202. package/src/runtime/execution/thin-harness/fast-executor.ts +2 -2
  203. package/src/runtime/execution/thin-harness/search-worker.ts +2 -2
  204. package/src/runtime/execution/workers/executor.ts +1 -2
  205. package/src/runtime/maintenance/cleanup.ts +41 -5
  206. package/src/runtime/plugins/action-application.ts +12 -1
  207. package/src/runtime/plugins/app-store-connect-adapter.ts +252 -4
  208. package/src/runtime/plugins/browser-adapter.ts +381 -313
  209. package/src/runtime/plugins/browser-macos-bridge.ts +114 -112
  210. package/src/runtime/plugins/browser-manifest-surface.ts +2 -1
  211. package/src/runtime/plugins/browser-registration.ts +2 -0
  212. package/src/runtime/plugins/browser-runtime.ts +26 -35
  213. package/src/runtime/plugins/computer-chatgpt-browser-adapter-installer.ts +11 -2
  214. package/src/runtime/plugins/computer-registration.ts +76 -9
  215. package/src/runtime/plugins/github-adapter.ts +30 -3
  216. package/src/runtime/plugins/gmail-adapter.ts +166 -1
  217. package/src/runtime/plugins/google-shared.ts +10 -5
  218. package/src/runtime/plugins/ios/remote-xpc-hid.ts +70 -30
  219. package/src/runtime/plugins/ios-adapter.ts +45 -40
  220. package/src/runtime/plugins/ios-agent-device.ts +54 -4
  221. package/src/runtime/plugins/ios-device-ownership.ts +103 -0
  222. package/src/runtime/plugins/ios-physical-device.ts +497 -79
  223. package/src/runtime/plugins/local-system-adapter.ts +1 -99
  224. package/src/runtime/plugins/store.ts +80 -7
  225. package/src/runtime/plugins/types.ts +4 -0
  226. package/src/runtime/projections/git-status-sampler.ts +2 -19
  227. package/src/runtime/recovery/actions.ts +1 -1
  228. package/src/runtime/recovery/lifecycle-inventory.ts +1 -11
  229. package/src/runtime/recovery/probe.ts +2 -2
  230. package/src/runtime/release/release-coordinator.ts +20 -4
  231. package/src/runtime/root/controller-progression-composition.ts +67 -5
  232. package/src/runtime/root/package-connector-service.ts +123 -11
  233. package/src/runtime/root/package-runtime-release.ts +6 -18
  234. package/src/runtime/root/package-runtime-service.ts +7 -13
  235. package/src/runtime/root/release-execution-canary.ts +18 -5
  236. package/src/runtime/root/release-manifest.ts +0 -34
  237. package/src/runtime/root/release-materialize.ts +0 -78
  238. package/src/runtime/root/runtime.ts +1 -2
  239. package/src/runtime/root/scheduled-controller-composition.ts +2 -1
  240. package/src/runtime/root/service.ts +90 -11
  241. package/src/runtime/root/types.ts +0 -6
  242. package/src/runtime/root/workflow-supervisor-composition.ts +207 -19
  243. package/src/runtime/root/workflow-supervisor-runtime.ts +365 -10
  244. package/src/runtime/safe-tooling/google-credential-store.ts +1 -11
  245. package/src/runtime/safe-tooling/ios-development.ts +22 -8
  246. package/src/runtime/safe-tooling/job-summary.ts +1 -1
  247. package/src/runtime/safe-tooling/workspace-auth.ts +4 -1
  248. package/src/runtime/shared/atomic-text-file.ts +21 -0
  249. package/src/runtime/shared/json-files.ts +8 -3
  250. package/src/runtime/standalone-recovery/core.ts +189 -174
  251. package/src/runtime/standalone-recovery/entry.ts +62 -30
  252. package/src/runtime/standalone-recovery/operation-lock.ts +11 -1
  253. package/src/runtime/workflow/schedules/chatgpt-automation-migration.ts +1 -0
  254. package/src/runtime/workflow/schedules/engine.ts +36 -14
  255. package/src/runtime/workflow/schedules/pending-activation.ts +11 -4
  256. package/src/runtime/workflow/schedules/work-continuation.ts +51 -6
  257. package/supervisor/client.ts +31 -14
  258. package/supervisor/control-plane.ts +210 -115
  259. package/supervisor/forge-validators.ts +88 -21
  260. package/supervisor/native-browser-adapter.ts +393 -28
  261. package/supervisor/native-messaging/host.ts +21 -2
  262. package/supervisor/protocol.ts +74 -26
  263. package/supervisor/server.ts +16 -8
  264. package/supervisor/store.ts +476 -57
  265. package/supervisor/types.ts +19 -13
  266. package/SKILL.md +0 -427
  267. package/adapters/computer/chatgpt-target-router.ts +0 -103
  268. package/adapters/mcp/runtime-gateway/automation-receipt-adapter.ts +0 -87
  269. package/adapters/mcp/runtime-gateway/work-validation-reconciler.ts +0 -1
  270. package/adapters/mcp/toolset-selection.ts +0 -41
  271. package/agents/openai.yaml +0 -4
  272. package/assets/skill-hooks.json +0 -41
  273. package/assets/skill-version.json +0 -189
  274. package/assets/skills/claude-review/SKILL.md +0 -231
  275. package/assets/skills/codex-review/SKILL.md +0 -103
  276. package/assets/templates/helpers/check-skill-version.ts +0 -193
  277. package/scripts/check-skill-version.ts +0 -5
  278. package/scripts/ensure-codegraph.sh +0 -16
  279. package/scripts/run-skill-hook.ts +0 -244
  280. package/scripts/sync-codex-installed-copies.sh +0 -182
  281. package/src/cli/commands/tools.ts +0 -167
  282. package/src/cli/mcp/access-mode.ts +0 -2
  283. package/src/cli/mcp/access-tools.ts +0 -2
  284. package/src/cli/mcp/audit.ts +0 -2
  285. package/src/cli/mcp/auth.ts +0 -2
  286. package/src/cli/mcp/instructions.ts +0 -2
  287. package/src/cli/mcp/legacy-context.ts +0 -2
  288. package/src/cli/mcp/legacy-tool-service.ts +0 -2
  289. package/src/cli/mcp/multi-repository.ts +0 -2
  290. package/src/cli/mcp/oauth.ts +0 -2
  291. package/src/cli/mcp/openai-secure-tunnel.ts +0 -2
  292. package/src/cli/mcp/paths.ts +0 -2
  293. package/src/cli/mcp/policy.ts +0 -2
  294. package/src/cli/mcp/post-finalize-work-attribution.ts +0 -2
  295. package/src/cli/mcp/proxy-env.ts +0 -2
  296. package/src/cli/mcp/readiness-projection.ts +0 -2
  297. package/src/cli/mcp/redaction.ts +0 -2
  298. package/src/cli/mcp/repo.ts +0 -2
  299. package/src/cli/mcp/repository-tools.ts +0 -2
  300. package/src/cli/mcp/server.ts +0 -2
  301. package/src/cli/mcp/setup.ts +0 -2
  302. package/src/cli/mcp/tool-capability-cache.ts +0 -2
  303. package/src/cli/mcp/tool-contract.ts +0 -2
  304. package/src/cli/mcp/tools.ts +0 -2
  305. package/src/cli/mcp/toolset-names.ts +0 -2
  306. package/src/cli/mcp/toolset-selection.ts +0 -2
  307. package/src/cli/mcp/toolset.ts +0 -2
  308. package/src/cli/mcp/transports/http.ts +0 -2
  309. package/src/cli/mcp/transports/session-registry.ts +0 -2
  310. package/src/cli/mcp/transports/stdio.ts +0 -2
  311. package/src/cli/mcp/types.ts +0 -2
  312. package/src/cli/mcp/umbrella-tools.ts +0 -2
  313. package/src/cli/tools/codegraph.ts +0 -695
  314. package/src/core/adoption/helper-wrapper-plan.ts +0 -159
  315. package/src/runtime/context/codegraph-cache-boundary.ts +0 -255
  316. package/src/runtime/context/codegraph-read-provider.ts +0 -611
  317. package/src/runtime/context/codegraph-sidecar.cjs +0 -248
  318. package/src/runtime/control-plane/codegraph-cache-retention.ts +0 -164
  319. package/src/runtime/gateway/mcp/execution-tools.ts +0 -2
  320. package/src/runtime/gateway/mcp/legacy-ios-tool-adapter.ts +0 -2
  321. package/src/runtime/gateway/mcp/persisted-check-process.ts +0 -2
  322. package/src/runtime/gateway/mcp/process-tools.ts +0 -2
  323. package/src/runtime/gateway/mcp/router.ts +0 -2
  324. package/src/runtime/gateway/mcp/runtime-tool-definitions.ts +0 -2
  325. package/src/runtime/gateway/mcp/runtime-tools.ts +0 -2
  326. package/src/runtime/gateway/mcp/work-validation-reconciler.ts +0 -2
  327. package/src/runtime/plugins/browser-provider-registry.ts +0 -2
  328. 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
- /** Selected schema profile; access mode never changes this value. */
31
- effectiveToolset: McpToolset;
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 rawLegacyToolset = normalizeConfiguredMcpToolset(config?.toolset);
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
- : rawLegacyToolset
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: selectedToolset.toolset,
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,
@@ -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?: 'facade' | 'core' | 'advanced' | 'full';
89
+ toolset?: 'full';
94
90
  configuredAccessMode?: AccessMode;
95
91
  effectiveAccessMode?: AccessMode;
96
- effectiveToolset?: McpToolset;
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, start with rh_context(operation=search, retrieval_mode=implementation) as the default code retrieval router when the exact path is unknown, then repeat it with exact paths, symbols, tests, relationships, or impact domains whenever more evidence can materially improve 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. Do not fall back to shell grep unless retrieval is unavailable or raw Git semantics are explicitly needed. The low-level search_repository handler is compatibility/internal surface, not a second default search path. Prefer repository_command_execute 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.',
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 hidden atomic compatibility tools merely because they exist in the full profile.',
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, McpToolset } from './types';
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: McpToolset;
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
- 'repository_command_preview',
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
- const config = policy.profile === 'controller'
337
- ? loadMcpServiceLocalConfig(controllerHome, explicitRepository?.canonicalRoot)
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 { codegraphRepositoryCacheRoot } from "../../../src/runtime/context/codegraph-cache-boundary";
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 { repositoryCheckoutLifecycle, repositorySummary, resolveRepositorySelection } from "../../../src/cli/repositories/registry";
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 || policyDeniedReads > 0 || errors.length > 0 || sourceChangedDuringQuery;
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
- * Managed worktrees borrow one repository baseline CodeGraph from a non-worktree
477
- * checkout. Dirty worktree bytes remain a raw/lexical overlay; they never create
478
- * a second structural-index authority merely because execution is isolated.
479
- * Repo-local `.codegraph` is retained only as a legacy-readable fallback.
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
- export function resolveStructuralIndexRoot(
482
- controllerHome: string,
483
- repository: ReturnType<typeof resolveRepositorySelection>,
484
- ): string | undefined {
485
- const active = repository.checkouts.find((checkout) => checkout.checkoutId === repository.activeCheckoutId);
486
- const nonWorktreeRoots = repository.checkouts
487
- .filter((checkout) => checkout.checkoutId !== repository.activeCheckoutId && checkout.worktree !== true && repositoryCheckoutLifecycle(checkout) === 'active')
488
- .map((checkout) => checkout.canonicalRoot);
489
- const candidates = active?.worktree === true
490
- ? [...nonWorktreeRoots, repository.canonicalRoot]
491
- : [repository.canonicalRoot, ...nonWorktreeRoots];
492
- return [...new Set(candidates)].find((root) => codegraphIndexExists(controllerHome, root));
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
- : retrievalMode === 'plan' || retrievalMode === 'debug'
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
- // Short code-like queries remain useful exact lexical needles. Long
571
- // semantic prompts are already tokenized from description; adding the
572
- // whole prompt as an exact needle prevents batch-search early exit and
573
- // forces a full bounded repository scan for a phrase that will not match.
574
- searchTerms: retrievalQuery.length <= 160 ? [retrievalQuery] : undefined,
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.policyDeniedReads > 0 ? ['semantic.policy_denied'] : []),
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: 'codegraph',
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
- const manifestOptions = { preferStored: true };
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
- [...repositoryManifests, ...controllerManifests].map((manifest) => [manifest.pluginId, manifest] as const),
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
- try {
69
- const pack = await buildControllerContextPackAsync(
70
- resolve(envelope.repoRoot),
71
- policy(envelope.policy),
72
- envelope.options && typeof envelope.options === 'object' ? envelope.options : {},
73
- );
74
- process.stdout.write(`${JSON.stringify({ schemaVersion: 1, pack })}\n`);
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) {