@lesliechan721/aw 0.1.0-beta.8 → 0.1.0-beta.9

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 (275) hide show
  1. package/dist/authoring/scripts/add-skill.mjs +4 -4
  2. package/dist/authoring/scripts/add-skill.mjs.map +1 -1
  3. package/dist/authoring/scripts/init-plugin.mjs +4 -4
  4. package/dist/authoring/scripts/init-plugin.mjs.map +1 -1
  5. package/dist/authoring/scripts/lib/plugin-version-check.mjs +1 -1
  6. package/dist/authoring/scripts/lib/skill-scaffold.d.mts +77 -90
  7. package/dist/authoring/scripts/lib/skill-scaffold.mjs +99 -120
  8. package/dist/authoring/scripts/lib/skill-scaffold.mjs.map +1 -1
  9. package/dist/authoring/scripts/remove-plugin.mjs +1 -1
  10. package/dist/authoring/scripts/remove-plugin.mjs.map +1 -1
  11. package/dist/authoring/scripts/validate-plugin.mjs +34 -778
  12. package/dist/authoring/scripts/validate-plugin.mjs.map +1 -1
  13. package/dist/scripts/aw-skill-eval-materializer.d.ts +24 -0
  14. package/dist/scripts/{plugin-eval-materializer.js → aw-skill-eval-materializer.js} +34 -145
  15. package/dist/scripts/aw-skill-eval-materializer.js.map +1 -0
  16. package/dist/scripts/bundle-plugins.d.ts +1 -0
  17. package/dist/scripts/bundle-plugins.js +2 -1
  18. package/dist/scripts/bundle-plugins.js.map +1 -1
  19. package/dist/scripts/plugin-staging.d.ts +5 -6
  20. package/dist/scripts/plugin-staging.js +12 -13
  21. package/dist/scripts/plugin-staging.js.map +1 -1
  22. package/dist/src/authoring/workspace-commands.d.ts +2 -1
  23. package/dist/src/authoring/workspace-commands.js.map +1 -1
  24. package/dist/src/commands/aw-capabilities.d.ts +2 -2
  25. package/dist/src/commands/aw-capabilities.js +6 -11
  26. package/dist/src/commands/aw-capabilities.js.map +1 -1
  27. package/dist/src/commands/catalog-dev-source.d.ts +18 -12
  28. package/dist/src/commands/catalog-dev-source.js +241 -305
  29. package/dist/src/commands/catalog-dev-source.js.map +1 -1
  30. package/dist/src/commands/catalog-generation.d.ts +6 -19
  31. package/dist/src/commands/catalog-generation.js +10 -11
  32. package/dist/src/commands/catalog-generation.js.map +1 -1
  33. package/dist/src/commands/catalog-publication-inspection.d.ts +28 -27
  34. package/dist/src/commands/catalog-publication-inspection.js +139 -50
  35. package/dist/src/commands/catalog-publication-inspection.js.map +1 -1
  36. package/dist/src/commands/catalog-publication-journal.d.ts +49 -0
  37. package/dist/src/commands/catalog-publication-journal.js +147 -0
  38. package/dist/src/commands/catalog-publication-journal.js.map +1 -0
  39. package/dist/src/commands/catalog-publication-target.d.ts +2 -1
  40. package/dist/src/commands/catalog-publication-target.js +37 -2
  41. package/dist/src/commands/catalog-publication-target.js.map +1 -1
  42. package/dist/src/commands/catalog-publish.d.ts +51 -15
  43. package/dist/src/commands/catalog-publish.js +576 -219
  44. package/dist/src/commands/catalog-publish.js.map +1 -1
  45. package/dist/src/commands/catalog-source.d.ts +3 -28
  46. package/dist/src/commands/catalog-source.js +29 -140
  47. package/dist/src/commands/catalog-source.js.map +1 -1
  48. package/dist/src/commands/catalog-status.d.ts +6 -26
  49. package/dist/src/commands/catalog-status.js +8 -146
  50. package/dist/src/commands/catalog-status.js.map +1 -1
  51. package/dist/src/commands/catalog-workspace-resolution.d.ts +13 -0
  52. package/dist/src/commands/catalog-workspace-resolution.js +42 -0
  53. package/dist/src/commands/catalog-workspace-resolution.js.map +1 -0
  54. package/dist/src/commands/dependency-operation-plan.d.ts +88 -9
  55. package/dist/src/commands/dependency-operation-plan.js +331 -6
  56. package/dist/src/commands/dependency-operation-plan.js.map +1 -1
  57. package/dist/src/commands/init.d.ts +5 -11
  58. package/dist/src/commands/init.js +30 -51
  59. package/dist/src/commands/init.js.map +1 -1
  60. package/dist/src/commands/offline-cleanup-evidence.d.ts +20 -0
  61. package/dist/src/commands/offline-cleanup-evidence.js +87 -9
  62. package/dist/src/commands/offline-cleanup-evidence.js.map +1 -1
  63. package/dist/src/commands/options.d.ts +6 -1
  64. package/dist/src/commands/options.js +64 -6
  65. package/dist/src/commands/options.js.map +1 -1
  66. package/dist/src/commands/plugin-capabilities.js +4 -0
  67. package/dist/src/commands/plugin-capabilities.js.map +1 -1
  68. package/dist/src/commands/rebuild-command.d.ts +10 -8
  69. package/dist/src/commands/rebuild-command.js +1217 -426
  70. package/dist/src/commands/rebuild-command.js.map +1 -1
  71. package/dist/src/commands/rebuild-integration.d.ts +54 -6
  72. package/dist/src/commands/rebuild-integration.js +691 -364
  73. package/dist/src/commands/rebuild-integration.js.map +1 -1
  74. package/dist/src/commands/rebuild-operations.d.ts +2 -0
  75. package/dist/src/commands/rebuild-operations.js +31 -17
  76. package/dist/src/commands/rebuild-operations.js.map +1 -1
  77. package/dist/src/commands/skill-eval.d.ts +33 -0
  78. package/dist/src/commands/skill-eval.js +363 -0
  79. package/dist/src/commands/skill-eval.js.map +1 -0
  80. package/dist/src/commands/{status-v5.d.ts → status-v6.d.ts} +59 -26
  81. package/dist/src/commands/{status-v5.js → status-v6.js} +882 -79
  82. package/dist/src/commands/status-v6.js.map +1 -0
  83. package/dist/src/commands/sync-command.d.ts +21 -0
  84. package/dist/src/commands/sync-command.js +206 -0
  85. package/dist/src/commands/sync-command.js.map +1 -0
  86. package/dist/src/commands/wizard.d.ts +2 -2
  87. package/dist/src/commands/write-commands.js +1 -43
  88. package/dist/src/commands/write-commands.js.map +1 -1
  89. package/dist/src/constants.d.ts +5 -4
  90. package/dist/src/constants.js +5 -4
  91. package/dist/src/constants.js.map +1 -1
  92. package/dist/src/eval/manifest-v1.d.ts +66 -0
  93. package/dist/src/eval/manifest-v1.js +326 -0
  94. package/dist/src/eval/manifest-v1.js.map +1 -0
  95. package/dist/src/generators/local-plugin.d.ts +3 -0
  96. package/dist/src/generators/local-plugin.js +123 -55
  97. package/dist/src/generators/local-plugin.js.map +1 -1
  98. package/dist/src/index.d.ts +2 -0
  99. package/dist/src/index.js +412 -152
  100. package/dist/src/index.js.map +1 -1
  101. package/dist/src/output/command-receipt.d.ts +58 -8
  102. package/dist/src/output/command-receipt.js +141 -11
  103. package/dist/src/output/command-receipt.js.map +1 -1
  104. package/dist/src/output/model.d.ts +3 -11
  105. package/dist/src/portable/commands.d.ts +2 -2
  106. package/dist/src/portable/commands.js +2 -8
  107. package/dist/src/portable/commands.js.map +1 -1
  108. package/dist/src/portable/materializer.d.ts +1 -4
  109. package/dist/src/portable/materializer.js +0 -2
  110. package/dist/src/portable/materializer.js.map +1 -1
  111. package/dist/src/tui/catalog-generation.d.ts +20 -41
  112. package/dist/src/tui/catalog-generation.js +65 -224
  113. package/dist/src/tui/catalog-generation.js.map +1 -1
  114. package/dist/src/tui/components.js +1 -2
  115. package/dist/src/tui/components.js.map +1 -1
  116. package/dist/src/tui/console-app-v4.js +461 -330
  117. package/dist/src/tui/console-app-v4.js.map +1 -1
  118. package/dist/src/tui/controller-runtime.d.ts +3 -5
  119. package/dist/src/tui/controller-runtime.js +182 -187
  120. package/dist/src/tui/controller-runtime.js.map +1 -1
  121. package/dist/src/tui/controller.d.ts +42 -45
  122. package/dist/src/tui/controller.js +454 -407
  123. package/dist/src/tui/controller.js.map +1 -1
  124. package/dist/src/tui/diagnostics.d.ts +1 -2
  125. package/dist/src/tui/diagnostics.js.map +1 -1
  126. package/dist/src/tui/index.d.ts +1 -1
  127. package/dist/src/tui/index.js +1 -1
  128. package/dist/src/tui/index.js.map +1 -1
  129. package/dist/src/tui/model.d.ts +81 -13
  130. package/dist/src/tui/model.js +378 -95
  131. package/dist/src/tui/model.js.map +1 -1
  132. package/dist/src/tui/router.d.ts +9 -0
  133. package/dist/src/tui/router.js.map +1 -1
  134. package/dist/src/types.d.ts +54 -47
  135. package/dist/src/types.js.map +1 -1
  136. package/dist/src/utils/agent-version.d.ts +4 -1
  137. package/dist/src/utils/agent-version.js +28 -3
  138. package/dist/src/utils/agent-version.js.map +1 -1
  139. package/dist/src/utils/catalog-digests.d.ts +7 -0
  140. package/dist/src/utils/catalog-digests.js +27 -0
  141. package/dist/src/utils/catalog-digests.js.map +1 -1
  142. package/dist/src/utils/catalog-generation-control-snapshot.d.ts +11 -0
  143. package/dist/src/utils/catalog-generation-control-snapshot.js +45 -0
  144. package/dist/src/utils/catalog-generation-control-snapshot.js.map +1 -0
  145. package/dist/src/utils/catalog-generation-error.d.ts +2 -1
  146. package/dist/src/utils/catalog-generation-error.js +12 -0
  147. package/dist/src/utils/catalog-generation-error.js.map +1 -1
  148. package/dist/src/utils/catalog-generation-resolver.d.ts +10 -3
  149. package/dist/src/utils/catalog-generation-resolver.js +90 -76
  150. package/dist/src/utils/catalog-generation-resolver.js.map +1 -1
  151. package/dist/src/utils/catalog-generation-service.d.ts +24 -33
  152. package/dist/src/utils/catalog-generation-service.js +253 -567
  153. package/dist/src/utils/catalog-generation-service.js.map +1 -1
  154. package/dist/src/utils/catalog-generation-state.d.ts +4 -0
  155. package/dist/src/utils/catalog-generation-state.js +73 -106
  156. package/dist/src/utils/catalog-generation-state.js.map +1 -1
  157. package/dist/src/utils/catalog-generation-store.d.ts +16 -26
  158. package/dist/src/utils/catalog-generation-store.js +154 -290
  159. package/dist/src/utils/catalog-generation-store.js.map +1 -1
  160. package/dist/src/utils/catalog-manifest-v6.d.ts +5 -0
  161. package/dist/src/utils/{catalog-manifest-v5.js → catalog-manifest-v6.js} +23 -35
  162. package/dist/src/utils/catalog-manifest-v6.js.map +1 -0
  163. package/dist/src/utils/catalog-operation-context.d.ts +6 -14
  164. package/dist/src/utils/catalog-operation-context.js +68 -169
  165. package/dist/src/utils/catalog-operation-context.js.map +1 -1
  166. package/dist/src/utils/catalog-public-error.js +1 -4
  167. package/dist/src/utils/catalog-public-error.js.map +1 -1
  168. package/dist/src/utils/catalog-runtime-projection.d.ts +3 -0
  169. package/dist/src/utils/catalog-runtime-projection.js +5 -40
  170. package/dist/src/utils/catalog-runtime-projection.js.map +1 -1
  171. package/dist/src/utils/catalog-snapshot.d.ts +2 -2
  172. package/dist/src/utils/catalog-source-references.d.ts +7 -0
  173. package/dist/src/utils/catalog-source-references.js +44 -55
  174. package/dist/src/utils/catalog-source-references.js.map +1 -1
  175. package/dist/src/utils/catalog-sources.js +25 -9
  176. package/dist/src/utils/catalog-sources.js.map +1 -1
  177. package/dist/src/utils/codex-config.d.ts +2 -0
  178. package/dist/src/utils/codex-config.js +22 -1
  179. package/dist/src/utils/codex-config.js.map +1 -1
  180. package/dist/src/utils/codex-manager.d.ts +50 -2
  181. package/dist/src/utils/codex-manager.js +160 -6
  182. package/dist/src/utils/codex-manager.js.map +1 -1
  183. package/dist/src/utils/codex-reconciler.d.ts +131 -0
  184. package/dist/src/utils/codex-reconciler.js +327 -0
  185. package/dist/src/utils/codex-reconciler.js.map +1 -0
  186. package/dist/src/utils/codex-shared-native.d.ts +56 -0
  187. package/dist/src/utils/codex-shared-native.js +87 -0
  188. package/dist/src/utils/codex-shared-native.js.map +1 -0
  189. package/dist/src/utils/codex-surface-analysis.d.ts +44 -0
  190. package/dist/src/utils/codex-surface-analysis.js +247 -0
  191. package/dist/src/utils/codex-surface-analysis.js.map +1 -0
  192. package/dist/src/utils/git-catalog-provider.js +56 -115
  193. package/dist/src/utils/git-catalog-provider.js.map +1 -1
  194. package/dist/src/utils/manifest.d.ts +1 -3
  195. package/dist/src/utils/manifest.js +1 -3
  196. package/dist/src/utils/manifest.js.map +1 -1
  197. package/dist/src/utils/multi-source-catalog.d.ts +1 -10
  198. package/dist/src/utils/multi-source-catalog.js +8 -175
  199. package/dist/src/utils/multi-source-catalog.js.map +1 -1
  200. package/dist/src/utils/native-cli.d.ts +34 -0
  201. package/dist/src/utils/native-cli.js +91 -0
  202. package/dist/src/utils/native-cli.js.map +1 -1
  203. package/dist/src/utils/orphan-marketplace-cleanup.js +1 -0
  204. package/dist/src/utils/orphan-marketplace-cleanup.js.map +1 -1
  205. package/dist/src/utils/ownership.d.ts +0 -10
  206. package/dist/src/utils/ownership.js +3 -88
  207. package/dist/src/utils/ownership.js.map +1 -1
  208. package/dist/src/utils/path-catalog-provider.d.ts +1 -1
  209. package/dist/src/utils/path-catalog-provider.js +45 -62
  210. package/dist/src/utils/path-catalog-provider.js.map +1 -1
  211. package/dist/src/utils/paths.d.ts +0 -1
  212. package/dist/src/utils/paths.js +0 -1
  213. package/dist/src/utils/paths.js.map +1 -1
  214. package/dist/src/utils/plugin-config-write.js +0 -5
  215. package/dist/src/utils/plugin-config-write.js.map +1 -1
  216. package/dist/src/utils/plugin-intent-state.d.ts +39 -0
  217. package/dist/src/utils/plugin-intent-state.js +325 -0
  218. package/dist/src/utils/plugin-intent-state.js.map +1 -0
  219. package/dist/src/utils/target-eligibility.d.ts +3 -2
  220. package/dist/src/utils/target-eligibility.js +4 -3
  221. package/dist/src/utils/target-eligibility.js.map +1 -1
  222. package/package.json +5 -4
  223. package/dist/scripts/aw-skill-eval-engine.d.ts +0 -13
  224. package/dist/scripts/aw-skill-eval-engine.js +0 -31
  225. package/dist/scripts/aw-skill-eval-engine.js.map +0 -1
  226. package/dist/scripts/codex-eval-app-server.d.ts +0 -91
  227. package/dist/scripts/codex-eval-app-server.js +0 -1334
  228. package/dist/scripts/codex-eval-app-server.js.map +0 -1
  229. package/dist/scripts/codex-eval-isolation.d.ts +0 -61
  230. package/dist/scripts/codex-eval-isolation.js +0 -318
  231. package/dist/scripts/codex-eval-isolation.js.map +0 -1
  232. package/dist/scripts/codex-eval-process-management.d.ts +0 -8
  233. package/dist/scripts/codex-eval-process-management.js +0 -69
  234. package/dist/scripts/codex-eval-process-management.js.map +0 -1
  235. package/dist/scripts/codex-eval-process-tree.d.ts +0 -38
  236. package/dist/scripts/codex-eval-process-tree.js +0 -803
  237. package/dist/scripts/codex-eval-process-tree.js.map +0 -1
  238. package/dist/scripts/codex-eval-runtime-config.d.ts +0 -62
  239. package/dist/scripts/codex-eval-runtime-config.js +0 -843
  240. package/dist/scripts/codex-eval-runtime-config.js.map +0 -1
  241. package/dist/scripts/codex-trigger-eval.d.ts +0 -84
  242. package/dist/scripts/codex-trigger-eval.js +0 -968
  243. package/dist/scripts/codex-trigger-eval.js.map +0 -1
  244. package/dist/scripts/eval-plugin-skills-audited.d.ts +0 -6
  245. package/dist/scripts/eval-plugin-skills-audited.js +0 -261
  246. package/dist/scripts/eval-plugin-skills-audited.js.map +0 -1
  247. package/dist/scripts/eval-plugin-skills.d.ts +0 -45
  248. package/dist/scripts/eval-plugin-skills.js +0 -1589
  249. package/dist/scripts/eval-plugin-skills.js.map +0 -1
  250. package/dist/scripts/eval-run-layout.d.ts +0 -17
  251. package/dist/scripts/eval-run-layout.js +0 -26
  252. package/dist/scripts/eval-run-layout.js.map +0 -1
  253. package/dist/scripts/plugin-eval-contract.d.ts +0 -189
  254. package/dist/scripts/plugin-eval-contract.js +0 -745
  255. package/dist/scripts/plugin-eval-contract.js.map +0 -1
  256. package/dist/scripts/plugin-eval-materializer.d.ts +0 -13
  257. package/dist/scripts/plugin-eval-materializer.js.map +0 -1
  258. package/dist/scripts/skill-reporting.d.ts +0 -212
  259. package/dist/scripts/skill-reporting.js +0 -922
  260. package/dist/scripts/skill-reporting.js.map +0 -1
  261. package/dist/src/commands/catalog-context.d.ts +0 -52
  262. package/dist/src/commands/catalog-context.js +0 -156
  263. package/dist/src/commands/catalog-context.js.map +0 -1
  264. package/dist/src/commands/status-v5.js.map +0 -1
  265. package/dist/src/utils/catalog-manifest-v5.d.ts +0 -5
  266. package/dist/src/utils/catalog-manifest-v5.js.map +0 -1
  267. package/dist/src/utils/catalog-source-cache.d.ts +0 -62
  268. package/dist/src/utils/catalog-source-cache.js +0 -321
  269. package/dist/src/utils/catalog-source-cache.js.map +0 -1
  270. package/dist/src/utils/codex-project-surface.d.ts +0 -3
  271. package/dist/src/utils/codex-project-surface.js +0 -49
  272. package/dist/src/utils/codex-project-surface.js.map +0 -1
  273. package/dist/src/utils/plugin-release.d.ts +0 -2
  274. package/dist/src/utils/plugin-release.js +0 -2
  275. package/dist/src/utils/plugin-release.js.map +0 -1
@@ -1,10 +1,9 @@
1
1
  import crypto from 'node:crypto';
2
2
  import path from 'node:path';
3
- import { statusCommandV5, } from '../commands/status-v5.js';
3
+ import { statusCommandV6, } from '../commands/status-v6.js';
4
4
  import { addCatalogSourceCommand, listCatalogSources, preflightCatalogSourceMutation, removeCatalogSourceCommand, setPrimaryCatalogSourceCommand, updateCatalogSourceCommand, } from '../commands/catalog-source.js';
5
5
  import { cleanupWriteCommand, installWriteCommand, reinstallWriteCommand, uninstallWriteCommand, upgradeWriteCommand, } from '../commands/write-commands.js';
6
6
  import { readCliPackageInfo } from '../utils/local-marketplace.js';
7
- import { CatalogGenerationError } from '../utils/catalog-generation-error.js';
8
7
  import { readCatalogSourcesConfigSnapshot, } from '../utils/catalog-sources.js';
9
8
  import { withResolvedCatalogPlugin, withResolvedMultiSourceCatalog, formatPluginId, parsePluginId, } from '../utils/multi-source-catalog.js';
10
9
  import { coercePluginConfigUpdates, readNamespacedPluginConfigStoreIdentity, readPluginConfigStore, resolveConfigRead, } from '../utils/plugin-config.js';
@@ -12,7 +11,7 @@ import { applyPluginConfigWrite, resetPluginConfigWithHelper, } from '../utils/p
12
11
  import { offlineCleanupEvidenceFromInventory, } from '../commands/offline-cleanup-evidence.js';
13
12
  import { inventoryCatalogSourceReferences } from '../utils/catalog-source-references.js';
14
13
  import { operationEvidenceFingerprint, TuiController, TuiOperationBlockedError, } from './controller.js';
15
- import { buildConfigGroups, buildConfigPage, projectPlatformPlacement, } from './model.js';
14
+ import { buildConfigGroups, buildConfigPage, isPureNativeCodexTarget, projectPlatformPlacement, } from './model.js';
16
15
  import { projectCatalogSourceLocation } from '../utils/catalog-source-presentation.js';
17
16
  import { targetCanBeUninstalled, targetHasArtifactCollision, } from '../utils/target-eligibility.js';
18
17
  import { calculateCatalogSourceIdentity } from '../utils/catalog-source-identity.js';
@@ -20,20 +19,18 @@ import { checkAwCapabilitySupport, readPluginManifest } from '../utils/manifest.
20
19
  import { withAwHomeMutationLock } from '../utils/aw-home-mutation-lock.js';
21
20
  import { IMPLEMENTED_AW_CAPABILITIES } from '../commands/aw-capabilities.js';
22
21
  import { resolveCatalogOperationContext, resolvePartialCatalogOperationContext, selectCatalogOperationContext, } from '../utils/catalog-operation-context.js';
23
- import { activateCatalogSourceCandidate, refreshCatalogSourceGeneration, setCatalogSourceRefreshPolicy, } from '../utils/catalog-generation-service.js';
24
22
  import { loadCatalogGenerationViewModels } from './catalog-generation.js';
25
23
  import { installPortableSkills, preparePortableInstall, PortableCommandError, } from '../portable/commands.js';
26
24
  import { MANAGED_CODEX_PACKAGE_NAME, MANAGED_CODEX_VERSION, } from '../utils/codex-manager.js';
25
+ import { calculatePluginIntentDigest, canonicalizePluginIntentState, readPluginIntentState, writePluginIntentState, } from '../utils/plugin-intent-state.js';
27
26
  async function loadPortablePluginIds(options) {
28
27
  return withResolvedMultiSourceCatalog({
29
28
  cliVersion: options.cliVersion,
30
- freshnessPolicy: 'cacheFirst',
31
29
  env: options.context.env,
32
30
  signal: options.signal,
33
31
  ...(options.catalogOperationContext
34
32
  ? { operationContext: options.catalogOperationContext }
35
33
  : {}),
36
- ...(options.bootstrapMissingActive === false ? { bootstrapMissingActive: false } : {}),
37
34
  consume: async (catalog) => (await Promise.all(catalog.sources.flatMap((item) => item.resolution
38
35
  ? item.resolution.manifest.entries.flatMap((entry) => {
39
36
  const support = checkAwCapabilitySupport(entry.requires.aw.capabilities ?? [], IMPLEMENTED_AW_CAPABILITIES);
@@ -119,8 +116,6 @@ function throwIfWriteReviewStale(result) {
119
116
  }
120
117
  }
121
118
  const SOURCE_PROGRESS_PHASES = {
122
- refreshingCatalog: '刷新 Catalog 缓存',
123
- catalogReady: 'Catalog 已读取,索引已刷新',
124
119
  savingConfig: '保存来源配置',
125
120
  };
126
121
  function pluginIntent(intent) {
@@ -131,23 +126,48 @@ function assertIntentIsLegal(intent, targets, revisions = []) {
131
126
  ? ['codex', 'claude']
132
127
  : [intent.platform];
133
128
  for (const platform of platforms) {
134
- const selected = targets.find((target) => target.pluginId === intent.pluginId
135
- && target.platform === platform && target.scope === intent.scope);
129
+ const platformTargets = targets.filter((target) => target.pluginId === intent.pluginId
130
+ && target.platform === platform);
131
+ const selected = platformTargets.find((target) => target.scope === intent.scope)
132
+ ?? (platform === 'codex' ? platformTargets[0] : undefined);
136
133
  if (!selected || selected.errors.some((error) => error.code === 'unsupportedTarget')) {
137
134
  throw new TuiOperationBlockedError('unsupportedTarget', '所选插件不支持这个平台和使用位置。');
138
135
  }
139
- const otherScopeInstalled = targets.some((target) => target.platform === platform
140
- && target.scope !== intent.scope
141
- && (target.state === 'present' || target.state === 'partial'));
142
- if (intent.action === 'install' && otherScopeInstalled) {
136
+ if (intent.action === 'sync' && platform !== 'codex') {
137
+ throw new TuiOperationBlockedError('syncPlatformUnsupported', 'sync 只支持 Codex。');
138
+ }
139
+ if (intent.dependencyPlacementOnly === 'desired')
140
+ continue;
141
+ const otherScope = intent.scope === 'user' ? 'project' : 'user';
142
+ const otherScopeInstalled = platform === 'codex'
143
+ ? platformTargets.some((target) => target.desired.scopes.includes(otherScope)
144
+ || target.activation?.[otherScope] === true)
145
+ : targets.some((target) => target.platform === platform
146
+ && target.scope !== intent.scope
147
+ && (target.state === 'present' || target.state === 'partial'));
148
+ const pureNativeCodex = isPureNativeCodexTarget(selected);
149
+ if (intent.action === 'install' && otherScopeInstalled && !pureNativeCodex
150
+ && !intent.dependencyPlacementAdjustment) {
143
151
  throw new TuiOperationBlockedError('scopeConflict', '这个平台已安装在另一个使用位置。请先移除原位置。');
144
152
  }
145
- if (intent.action === 'install' && targetHasArtifactCollision(selected)) {
153
+ if ((intent.action === 'install' || intent.action === 'upgrade' || intent.action === 'reinstall')
154
+ && targetHasArtifactCollision(selected)) {
146
155
  throw new TuiOperationBlockedError('artifactCollision', '所选位置存在输出冲突。请先移除或迁移冲突内容。');
147
156
  }
148
- if (intent.action === 'install' && selected.state !== 'absent') {
157
+ const selectedScopeInstalled = platform === 'codex'
158
+ ? selected.desired.scopes.includes(intent.scope)
159
+ || selected.activation?.[intent.scope] === true
160
+ : selected.state !== 'absent';
161
+ if (intent.action === 'install' && selectedScopeInstalled
162
+ && !intent.dependencyPlacementAdjustment) {
149
163
  throw new TuiOperationBlockedError('targetAlreadyInstalled', '所选位置已经安装。');
150
164
  }
165
+ if (intent.action === 'sync'
166
+ && selected.actual.state !== 'needsSync'
167
+ && selected.actual.state !== 'drifted'
168
+ && selected.actual.state !== 'conflict') {
169
+ throw new TuiOperationBlockedError('targetStateChanged', '当前安装状态不需要同步。');
170
+ }
151
171
  const revision = revisions.find((entry) => entry.pluginId === intent.pluginId
152
172
  && entry.platform === platform && entry.scope === intent.scope)?.revision;
153
173
  if (intent.action === 'upgrade'
@@ -170,7 +190,9 @@ async function assertTypedIssueOrigin(intent, targets) {
170
190
  };
171
191
  const selected = targets.find((target) => target.pluginId === intent.pluginId
172
192
  && target.platform === intent.platform
173
- && target.scope === intent.scope);
193
+ && target.scope === intent.scope)
194
+ ?? targets.find((target) => target.pluginId === intent.pluginId
195
+ && target.platform === 'codex');
174
196
  if (intent.originIssue.category === 'dependencyMissing') {
175
197
  if (intent.originIssue.actionId !== 'installDependency'
176
198
  || intent.action !== 'install' || !selected || selected.state !== 'absent') {
@@ -207,9 +229,17 @@ async function assertTypedIssueOrigin(intent, targets) {
207
229
  return;
208
230
  }
209
231
  else if (intent.originIssue.category === 'scopeConflict') {
210
- const installedScopes = new Set(targets.filter((target) => target.pluginId === intent.pluginId
211
- && target.platform === intent.platform
212
- && (target.state === 'present' || target.state === 'partial')).map((target) => target.scope));
232
+ const installedScopes = new Set();
233
+ for (const target of targets.filter((candidate) => candidate.pluginId === intent.pluginId && candidate.platform === intent.platform)) {
234
+ if (target.state === 'present' || target.state === 'partial')
235
+ installedScopes.add(target.scope);
236
+ for (const scope of target.desired.scopes)
237
+ installedScopes.add(scope);
238
+ if (target.activation?.user === true)
239
+ installedScopes.add('user');
240
+ if (target.activation?.project === true)
241
+ installedScopes.add('project');
242
+ }
213
243
  const correctBranch = intent.action === 'uninstall'
214
244
  && ((intent.originIssue.actionId === 'keepUserScope' && intent.scope === 'project')
215
245
  || (intent.originIssue.actionId === 'keepProjectScope' && intent.scope === 'user'));
@@ -239,13 +269,11 @@ async function assertTypedIssueOrigin(intent, targets) {
239
269
  async function loadDependencyOverviews(options) {
240
270
  const graph = await withResolvedMultiSourceCatalog({
241
271
  cliVersion: options.cliVersion,
242
- freshnessPolicy: 'cacheFirst',
243
272
  env: options.context.env,
244
273
  signal: options.signal,
245
274
  ...(options.catalogOperationContext
246
275
  ? { operationContext: options.catalogOperationContext }
247
276
  : {}),
248
- ...(options.bootstrapMissingActive === false ? { bootstrapMissingActive: false } : {}),
249
277
  consume: async (catalog) => ({
250
278
  nodes: catalog.sources.flatMap((item) => item.resolution
251
279
  ? item.resolution.manifest.entries.map((entry) => ({
@@ -332,7 +360,8 @@ async function loadDependencyOverviews(options) {
332
360
  return 'notInstalled';
333
361
  }
334
362
  return placements.some((placement) => placement.state === 'scopeConflict'
335
- || (placement.state === 'installed' && placement.health !== 'ready'))
363
+ || (placement.state === 'installed'
364
+ && (placement.health !== 'ready' || placement.blockingReason !== undefined)))
336
365
  ? 'needsAttention'
337
366
  : 'ready';
338
367
  };
@@ -479,6 +508,30 @@ function createReviewStageReporter(options) {
479
508
  export function createTuiControllerDependencies(context, progressFeed, options = {}) {
480
509
  const readInventory = options.inventoryCatalogSourceReferences
481
510
  ?? inventoryCatalogSourceReferences;
511
+ const validatedStoreEntries = new Map();
512
+ const validatingStoreEntries = new Map();
513
+ const loadCatalogGenerations = async () => {
514
+ const generations = await loadCatalogGenerationViewModels({
515
+ env: context.env,
516
+ validatedStoreEntries,
517
+ validatingStoreEntries,
518
+ onStage: (event) => context.debugLogger.log({
519
+ type: 'performance:stage',
520
+ level: 'debug',
521
+ target: 'debugLog',
522
+ message: 'Catalog generation load stage completed.',
523
+ data: event,
524
+ }),
525
+ });
526
+ const retained = new Set(generations.flatMap((source) => [
527
+ source.active?.storeKey,
528
+ ].filter((value) => value !== undefined)));
529
+ for (const storeKey of validatedStoreEntries.keys()) {
530
+ if (!retained.has(storeKey))
531
+ validatedStoreEntries.delete(storeKey);
532
+ }
533
+ return generations;
534
+ };
482
535
  const createOperationInventoryReader = () => {
483
536
  let ordinaryInventory;
484
537
  return (inventoryOptions) => {
@@ -492,7 +545,6 @@ export function createTuiControllerDependencies(context, progressFeed, options =
492
545
  const catalogOperationByEvidence = new WeakMap();
493
546
  let sessionCatalogOperationContext;
494
547
  let sessionStatusResult;
495
- let bootstrapNextCatalogSession = true;
496
548
  const sessionContextForPlugin = (pluginId) => {
497
549
  const sourceId = parsePluginId(pluginId).sourceId;
498
550
  if (!sourceId || !sessionCatalogOperationContext)
@@ -504,23 +556,20 @@ export function createTuiControllerDependencies(context, progressFeed, options =
504
556
  const packageInfo = await readCliPackageInfo();
505
557
  const catalogOperationContext = sessionCatalogOperationContext
506
558
  ?? await resolvePartialCatalogOperationContext({
507
- cliVersion: packageInfo.version,
508
559
  env: context.env,
509
560
  signal,
510
- ...(bootstrapNextCatalogSession ? {} : { bootstrapMissingActive: false }),
511
- ...(options.catalogGenerationRuntime ? { runtime: options.catalogGenerationRuntime } : {}),
561
+ validatedStoreEntries,
562
+ validatingStoreEntries,
512
563
  }).catch(() => undefined);
513
564
  if (catalogOperationContext)
514
565
  sessionCatalogOperationContext = catalogOperationContext;
515
566
  for (let attempt = 0; attempt < 3; attempt += 1) {
516
- const result = await statusCommandV5({
567
+ const result = await statusCommandV6({
517
568
  cwd: context.cwd,
518
569
  env: context.env,
519
570
  cliVersion: packageInfo.version,
520
571
  verbose: context.diagnostics.verbose,
521
572
  ...(catalogOperationContext ? { catalogOperationContext } : {}),
522
- ...(!catalogOperationContext ? { bootstrapMissingActive: false } : {}),
523
- ...(options.catalogGenerationRuntime ? { catalogGenerationRuntime: options.catalogGenerationRuntime } : {}),
524
573
  inventoryCatalogSourceReferences: readInventory,
525
574
  });
526
575
  signal.throwIfAborted();
@@ -535,17 +584,15 @@ export function createTuiControllerDependencies(context, progressFeed, options =
535
584
  status: result,
536
585
  signal,
537
586
  ...(catalogOperationContext ? { catalogOperationContext } : {}),
538
- ...(!catalogOperationContext ? { bootstrapMissingActive: false } : {}),
539
587
  });
540
588
  const portablePluginIds = await loadPortablePluginIds({
541
589
  context,
542
590
  cliVersion: packageInfo.version,
543
591
  signal,
544
592
  ...(catalogOperationContext ? { catalogOperationContext } : {}),
545
- ...(!catalogOperationContext ? { bootstrapMissingActive: false } : {}),
546
593
  });
547
594
  signal.throwIfAborted();
548
- const catalogGenerations = await loadCatalogGenerationViewModels({ env: context.env });
595
+ const catalogGenerations = await loadCatalogGenerations();
549
596
  signal.throwIfAborted();
550
597
  const statusById = new Map(result.payload.catalog.sources.map((source) => [source.id, source]));
551
598
  return {
@@ -556,6 +603,8 @@ export function createTuiControllerDependencies(context, progressFeed, options =
556
603
  dependencyOverviews,
557
604
  portablePluginIds,
558
605
  catalogGenerations,
606
+ sessionGenerationKeys: Object.fromEntries((catalogOperationContext?.generationSet.sources ?? [])
607
+ .map((source) => [source.sourceId, source.storeKey])),
559
608
  sourceDetails: configured.sources.map((source) => {
560
609
  const status = statusById.get(source.id);
561
610
  return {
@@ -583,38 +632,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
583
632
  }
584
633
  throw new TuiOperationBlockedError('operationReviewStale', '状态读取期间来源配置持续变化。请重试。');
585
634
  };
586
- const captureEvidence = async (intent, signal, preparation, bootstrapMissingActive = true, operationInventory = createOperationInventoryReader()) => {
587
- if (intent.kind === 'catalogActivate') {
588
- signal?.throwIfAborted();
589
- const source = (await loadCatalogGenerationViewModels({ env: context.env }))
590
- .find((entry) => entry.sourceId === intent.sourceId);
591
- if (!source?.active || !source.candidate) {
592
- throw operationReviewStale('Catalog Candidate 不再可用。');
593
- }
594
- if (source.candidate.generationKey !== intent.candidateGenerationKey) {
595
- throw operationReviewStale('Catalog Candidate 已变化。');
596
- }
597
- const sessionCatalog = sessionCatalogOperationContext?.catalog;
598
- return {
599
- catalog: {
600
- configDigest: sessionCatalog?.configDigest ?? 'unavailable',
601
- compositeDigest: sessionCatalog?.compositeDigest ?? 'unavailable',
602
- ...(sessionCatalogOperationContext?.generationSetDigest
603
- ? { generationSetDigest: sessionCatalogOperationContext.generationSetDigest }
604
- : {}),
605
- },
606
- catalogCandidate: {
607
- sourceId: source.sourceId,
608
- activeVersion: source.active.catalogVersion,
609
- candidateVersion: source.candidate.catalogVersion,
610
- channel: source.candidate.channel,
611
- requestedRef: source.candidate.requestedRef,
612
- candidateGenerationKey: source.candidate.generationKey,
613
- digestShort: source.candidate.digestShort,
614
- pluginChanges: source.candidate.pluginChanges,
615
- },
616
- };
617
- }
635
+ const captureEvidence = async (intent, signal, preparation, operationInventory = createOperationInventoryReader(), freshCatalog = false) => {
618
636
  if (intent.kind === 'sourceMutation') {
619
637
  signal?.throwIfAborted();
620
638
  if (intent.action === 'add' && !intent.source
@@ -636,10 +654,10 @@ export function createTuiControllerDependencies(context, progressFeed, options =
636
654
  await preparation?.reporter.emit('evidence');
637
655
  signal?.throwIfAborted();
638
656
  const evidence = {
639
- catalog: { configDigest: preview.configDigest, compositeDigest: preview.candidateDigest },
657
+ catalog: { configDigest: preview.configDigest, compositeDigest: preview.proposedConfigDigest },
640
658
  sourceWrite: {
641
659
  configDigest: preview.configDigest,
642
- candidateDigest: preview.candidateDigest,
660
+ proposedConfigDigest: preview.proposedConfigDigest,
643
661
  ...(preview.sourceIdentity ? { sourceIdentity: preview.sourceIdentity } : {}),
644
662
  inventoryDigest: digest({ references: preview.references.references, errors: preview.references.errors }),
645
663
  referenceCount: preview.references.references.length,
@@ -647,7 +665,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
647
665
  },
648
666
  };
649
667
  if (preparation?.validateIntent
650
- && evidence.sourceWrite?.configDigest === evidence.sourceWrite?.candidateDigest) {
668
+ && evidence.sourceWrite?.configDigest === evidence.sourceWrite?.proposedConfigDigest) {
651
669
  throw new TuiOperationBlockedError('sourceNoChanges', '来源没有变化。');
652
670
  }
653
671
  return evidence;
@@ -666,19 +684,16 @@ export function createTuiControllerDependencies(context, progressFeed, options =
666
684
  : undefined;
667
685
  let catalogOperation;
668
686
  let catalogOperationError;
669
- if (!offlineCleanupReason && intent.kind !== 'portableInstall'
670
- && sessionCatalogOperationContext) {
687
+ if (!offlineCleanupReason && intent.kind !== 'portableInstall') {
671
688
  try {
672
689
  const sourceIds = [parsePluginId(intent.pluginId).sourceId];
673
- catalogOperation = bootstrapMissingActive
690
+ catalogOperation = !freshCatalog && sessionCatalogOperationContext
674
691
  ? selectCatalogOperationContext(sessionCatalogOperationContext, sourceIds)
675
692
  : await resolveCatalogOperationContext({
676
- cliVersion: packageInfo.version,
677
693
  sourceIds,
678
694
  env: context.env,
679
- ...(options.catalogGenerationRuntime ? { runtime: options.catalogGenerationRuntime } : {}),
695
+ ...(!freshCatalog ? { validatedStoreEntries, validatingStoreEntries } : {}),
680
696
  ...(signal ? { signal } : {}),
681
- ...(!bootstrapMissingActive ? { bootstrapMissingActive: false } : {}),
682
697
  });
683
698
  }
684
699
  catch (error) {
@@ -734,7 +749,6 @@ export function createTuiControllerDependencies(context, progressFeed, options =
734
749
  const selected = await withResolvedCatalogPlugin({
735
750
  selector: intent.pluginId,
736
751
  cliVersion: packageInfo.version,
737
- freshnessPolicy: 'cacheFirst',
738
752
  configSnapshot,
739
753
  env: context.env,
740
754
  operationContext: catalogOperation,
@@ -798,8 +812,8 @@ export function createTuiControllerDependencies(context, progressFeed, options =
798
812
  await preparation?.reporter.emit('status');
799
813
  const statusCatalogOperation = offlineCleanupReason
800
814
  ? undefined
801
- : catalogOperation ?? (bootstrapMissingActive ? sessionCatalogOperationContext : undefined);
802
- const status = await statusCommandV5({
815
+ : catalogOperation ?? (!freshCatalog ? sessionCatalogOperationContext : undefined);
816
+ const status = await statusCommandV6({
803
817
  plugins: [...new Set([intent.pluginId,
804
818
  ...(intent.kind === 'pluginWrite' && intent.originIssue?.consumerPluginId
805
819
  ? [intent.originIssue.consumerPluginId] : [])])],
@@ -811,10 +825,6 @@ export function createTuiControllerDependencies(context, progressFeed, options =
811
825
  env: context.env,
812
826
  cliVersion: packageInfo.version,
813
827
  ...(statusCatalogOperation ? { catalogOperationContext: statusCatalogOperation } : {}),
814
- ...(offlineCleanupReason || !bootstrapMissingActive
815
- || (!statusCatalogOperation && sessionCatalogOperationContext)
816
- ? { bootstrapMissingActive: false }
817
- : {}),
818
828
  inventoryCatalogSourceReferences: operationInventory,
819
829
  });
820
830
  signal?.throwIfAborted();
@@ -833,16 +843,15 @@ export function createTuiControllerDependencies(context, progressFeed, options =
833
843
  }
834
844
  if (!offlineCleanupReason && !catalogOperation) {
835
845
  catalogOperation = await resolveCatalogOperationContext({
836
- cliVersion: packageInfo.version,
837
846
  sourceIds: [parsePluginId(intent.pluginId).sourceId],
838
847
  configSnapshot,
839
848
  env: context.env,
840
- ...(options.catalogGenerationRuntime ? { runtime: options.catalogGenerationRuntime } : {}),
849
+ ...(!freshCatalog ? { validatedStoreEntries, validatingStoreEntries } : {}),
841
850
  ...(signal ? { signal } : {}),
842
- ...(!bootstrapMissingActive ? { bootstrapMissingActive: false } : {}),
843
851
  });
844
852
  }
845
853
  let dependencyPlan;
854
+ let codexReconcilePlans;
846
855
  await preparation?.reporter.emit('catalog');
847
856
  const offlineCleanupTarget = offlineCleanupReason && intent.kind === 'pluginWrite'
848
857
  ? status.payload.targets.find((target) => target.pluginId === intent.pluginId
@@ -888,7 +897,6 @@ export function createTuiControllerDependencies(context, progressFeed, options =
888
897
  : await withResolvedCatalogPlugin({
889
898
  selector: intent.pluginId,
890
899
  cliVersion: packageInfo.version,
891
- freshnessPolicy: 'cacheFirst',
892
900
  configSnapshot,
893
901
  env: context.env,
894
902
  ...(catalogOperation ? { operationContext: catalogOperation } : {}),
@@ -925,11 +933,15 @@ export function createTuiControllerDependencies(context, progressFeed, options =
925
933
  reinstall: reinstallWriteCommand,
926
934
  uninstall: uninstallWriteCommand,
927
935
  cleanup: cleanupWriteCommand,
936
+ sync: installWriteCommand,
928
937
  }[intent.action];
929
938
  const preview = await command([intent.pluginId], {
930
939
  cwd: context.cwd,
931
940
  platform: intent.platform,
932
941
  scope: intent.scope,
942
+ ...(intent.action === 'install' && intent.dependencyScopeOverrides
943
+ ? { dependencyScopeOverrides: intent.dependencyScopeOverrides }
944
+ : {}),
933
945
  ...(intent.action === 'cleanup' ? { force: intent.force === true } : {}),
934
946
  }, {
935
947
  ...context,
@@ -940,15 +952,20 @@ export function createTuiControllerDependencies(context, progressFeed, options =
940
952
  expectedGenerationSetDigest: catalogOperation.generationSetDigest,
941
953
  } : {}),
942
954
  previewDependencyPlanOnly: true,
955
+ ...(intent.action === 'sync' ? { reconcileIntentOnly: true } : {}),
943
956
  ...(preparation ? {
944
957
  onPreviewStage: (stage) => stage === 'catalog' ? undefined : preparation.reporter.emit(stage),
945
958
  } : {}),
946
959
  });
947
960
  signal?.throwIfAborted();
961
+ codexReconcilePlans = intent.dependencyPlacementOnly === 'desired'
962
+ ? undefined
963
+ : preview?.codexReconcilePlans;
948
964
  if (hasDeclaredDependencies
949
965
  && (intent.action === 'install'
950
966
  || intent.action === 'upgrade'
951
- || intent.action === 'reinstall')) {
967
+ || intent.action === 'reinstall'
968
+ || intent.action === 'sync')) {
952
969
  dependencyPlan = preview.dependencyPlan;
953
970
  if (!dependencyPlan) {
954
971
  throw new TuiOperationBlockedError('dependencyGraphInvalid', '无法建立可信的依赖安装计划。');
@@ -959,13 +976,14 @@ export function createTuiControllerDependencies(context, progressFeed, options =
959
976
  }
960
977
  }
961
978
  const managerPreview = preview?.codexManagerPreview;
962
- if (managerPreview?.state === 'blocked') {
979
+ if (managerPreview?.state === 'blocked' && intent.dependencyPlacementOnly !== 'desired') {
963
980
  const message = managerPreview.code === 'codexSystemCliUnsupported'
964
981
  ? '系统 Codex CLI 版本不受支持。请升级官方 Codex CLI 后重试。'
965
982
  : 'AW-managed Codex runtime 当前不可用。请重试、升级 AW,或安装官方 Codex CLI。';
966
983
  throw new TuiOperationBlockedError(managerPreview.code, `${managerPreview.code}: ${message}`);
967
984
  }
968
- if (managerPreview) {
985
+ if (managerPreview && managerPreview.state !== 'blocked'
986
+ && intent.dependencyPlacementOnly !== 'desired') {
969
987
  codexManager = {
970
988
  state: managerPreview.state,
971
989
  packageName: MANAGED_CODEX_PACKAGE_NAME,
@@ -977,6 +995,18 @@ export function createTuiControllerDependencies(context, progressFeed, options =
977
995
  }
978
996
  await preparation?.reporter.emit('evidence');
979
997
  signal?.throwIfAborted();
998
+ let projectIntentDigest;
999
+ if (intent.kind === 'pluginWrite' && intent.dependencyPlacementOnly === 'desired') {
1000
+ if (intent.scope !== 'project') {
1001
+ throw new TuiOperationBlockedError('dependencyPlacementScopeInvalid', '缩小依赖位置只能更新当前项目 desired intent。');
1002
+ }
1003
+ projectIntentDigest = calculatePluginIntentDigest(await readPluginIntentState({
1004
+ scope: 'project',
1005
+ cwd: context.cwd,
1006
+ env: context.env,
1007
+ }));
1008
+ signal?.throwIfAborted();
1009
+ }
980
1010
  const evidenceTargets = intent.kind === 'configSave' || intent.kind === 'configReset'
981
1011
  ? configAffectedStatusTargets(status.payload.targets, intent.pluginId, intent.scope)
982
1012
  : status.payload.targets;
@@ -996,6 +1026,8 @@ export function createTuiControllerDependencies(context, progressFeed, options =
996
1026
  source: selected.source,
997
1027
  ...(offlineCleanup ? { offlineCleanup } : {}),
998
1028
  ...(dependencyPlan ? { dependencyPlan } : {}),
1029
+ ...(projectIntentDigest ? { projectIntentDigest } : {}),
1030
+ ...(codexReconcilePlans ? { codexReconcilePlans } : {}),
999
1031
  ...(codexManager ? { codexManager } : {}),
1000
1032
  targetFingerprint: statusTargetFingerprint(evidenceTargets, evidenceRevisions),
1001
1033
  };
@@ -1025,7 +1057,10 @@ export function createTuiControllerDependencies(context, progressFeed, options =
1025
1057
  const dependencies = {
1026
1058
  beginCatalogSession(sessionOptions) {
1027
1059
  sessionCatalogOperationContext = undefined;
1028
- bootstrapNextCatalogSession = sessionOptions?.bootstrapMissingActive !== false;
1060
+ if (sessionOptions?.reuseValidatedGenerations !== true) {
1061
+ validatedStoreEntries.clear();
1062
+ validatingStoreEntries.clear();
1063
+ }
1029
1064
  },
1030
1065
  loadStatus,
1031
1066
  async inspectSourceRemoval(_sourceId, signal) {
@@ -1056,14 +1091,14 @@ export function createTuiControllerDependencies(context, progressFeed, options =
1056
1091
  await reporter.finish();
1057
1092
  }
1058
1093
  },
1094
+ async previewDependencyPlacement(intent, signal) {
1095
+ const evidence = await captureEvidence(intent, signal);
1096
+ return evidence.dependencyPlan;
1097
+ },
1059
1098
  async validateIntent(intent, signal) {
1060
- if (intent.kind === 'catalogActivate') {
1061
- await captureEvidence(intent, signal);
1062
- return;
1063
- }
1064
1099
  if (intent.kind === 'sourceMutation') {
1065
1100
  const evidence = await captureEvidence(intent, signal);
1066
- if (evidence.sourceWrite?.configDigest === evidence.sourceWrite?.candidateDigest) {
1101
+ if (evidence.sourceWrite?.configDigest === evidence.sourceWrite?.proposedConfigDigest) {
1067
1102
  throw new TuiOperationBlockedError('sourceNoChanges', '来源没有变化。');
1068
1103
  }
1069
1104
  return;
@@ -1073,7 +1108,6 @@ export function createTuiControllerDependencies(context, progressFeed, options =
1073
1108
  await withResolvedCatalogPlugin({
1074
1109
  selector: intent.pluginId,
1075
1110
  cliVersion: packageInfo.version,
1076
- freshnessPolicy: 'cacheFirst',
1077
1111
  env: context.env,
1078
1112
  signal,
1079
1113
  ...(sessionContextForPlugin(intent.pluginId)
@@ -1108,89 +1142,45 @@ export function createTuiControllerDependencies(context, progressFeed, options =
1108
1142
  && (intent.originIssue.category === 'sourceMissing'
1109
1143
  || intent.originIssue.category === 'sourceUnavailable');
1110
1144
  const sourceIds = pluginIds.map((pluginId) => parsePluginId(pluginId).sourceId);
1111
- const catalogOperation = offlineCleanup
1145
+ let catalogOperation = offlineCleanup
1112
1146
  ? undefined
1113
1147
  : sessionCatalogOperationContext
1114
1148
  ? selectCatalogOperationContext(sessionCatalogOperationContext, sourceIds)
1115
1149
  : undefined;
1116
- const status = await statusCommandV5({
1150
+ const statusOptions = {
1117
1151
  plugins: pluginIds,
1118
1152
  platform: intent.platform,
1119
1153
  cwd: context.cwd,
1120
1154
  env: context.env,
1121
1155
  cliVersion: packageInfo.version,
1122
- ...(catalogOperation ? { catalogOperationContext: catalogOperation } : {}),
1123
- ...(offlineCleanup || sessionCatalogOperationContext
1124
- ? { bootstrapMissingActive: false }
1125
- : {}),
1126
1156
  inventoryCatalogSourceReferences: readInventory,
1157
+ };
1158
+ let status = await statusCommandV6({
1159
+ ...statusOptions,
1160
+ ...(catalogOperation ? { catalogOperationContext: catalogOperation } : {}),
1127
1161
  });
1128
- signal.throwIfAborted();
1129
- await assertTypedIssueOrigin(intent, status.payload.targets);
1130
- assertIntentIsLegal(intent, status.payload.targets, status.targetRevisions);
1131
- },
1132
- async checkSource(sourceId, signal) {
1133
- const packageInfo = await readCliPackageInfo();
1134
- const configSnapshot = await readCatalogSourcesConfigSnapshot(context.env);
1135
- return withResolvedMultiSourceCatalog({
1136
- cliVersion: packageInfo.version,
1137
- freshnessPolicy: 'forceRemote',
1138
- sourceIds: [sourceId],
1139
- configSnapshot,
1140
- env: context.env,
1141
- signal,
1142
- consume: (catalog) => {
1143
- const item = catalog.sources[0];
1144
- if (item.error)
1145
- throw item.error;
1146
- return {
1147
- sourceIdentity: item.resolution.sourceIdentity,
1148
- requestedRef: item.resolution.requestedRef,
1149
- };
1150
- },
1151
- });
1152
- },
1153
- async refreshCatalogSource(sourceId, signal, onState) {
1154
- let refreshError;
1155
- let refreshCompleted = false;
1156
- try {
1157
- await refreshCatalogSourceGeneration({
1158
- sourceId,
1159
- cliVersion: (await readCliPackageInfo()).version,
1162
+ const platforms = intent.platform === 'both'
1163
+ ? ['codex', 'claude']
1164
+ : [intent.platform];
1165
+ const missingSelectedTarget = platforms.some((platform) => !status.payload.targets.some((target) => target.pluginId === intent.pluginId
1166
+ && target.platform === platform
1167
+ && (platform === 'codex' || target.scope === intent.scope)));
1168
+ if (!offlineCleanup && !catalogOperation && missingSelectedTarget) {
1169
+ catalogOperation = await resolveCatalogOperationContext({
1170
+ sourceIds,
1160
1171
  env: context.env,
1172
+ validatedStoreEntries,
1173
+ validatingStoreEntries,
1161
1174
  signal,
1162
- runtime: {
1163
- ...options.catalogGenerationRuntime,
1164
- onProgress: (event) => onState(event.state),
1165
- },
1166
1175
  });
1167
- refreshCompleted = true;
1168
- }
1169
- catch (error) {
1170
- refreshError = error;
1176
+ status = await statusCommandV6({
1177
+ ...statusOptions,
1178
+ catalogOperationContext: catalogOperation,
1179
+ });
1171
1180
  }
1172
1181
  signal.throwIfAborted();
1173
- const source = (await loadCatalogGenerationViewModels({ env: context.env }))
1174
- .find((entry) => entry.sourceId === sourceId);
1175
- if (!source)
1176
- throw refreshError ?? new TuiOperationBlockedError('catalogGenerationSourceMissing', 'Catalog 来源已不存在。');
1177
- if (refreshError && source.refreshState !== 'failed'
1178
- && source.refreshState !== 'immutableViolation')
1179
- throw refreshError;
1180
- return {
1181
- source: refreshCompleted && source.refreshState === 'idle'
1182
- ? { ...source, refreshState: 'upToDate' }
1183
- : source,
1184
- };
1185
- },
1186
- async setCatalogRefreshPolicy(sourceId, refreshPolicy) {
1187
- await setCatalogSourceRefreshPolicy({
1188
- sourceId,
1189
- refreshPolicy,
1190
- env: context.env,
1191
- });
1192
- return (await loadCatalogGenerationViewModels({ env: context.env }))
1193
- .find((source) => source.sourceId === sourceId);
1182
+ await assertTypedIssueOrigin(intent, status.payload.targets);
1183
+ assertIntentIsLegal(intent, status.payload.targets, status.targetRevisions);
1194
1184
  },
1195
1185
  captureEvidence,
1196
1186
  logError(_error, code, logContext) {
@@ -1209,7 +1199,6 @@ export function createTuiControllerDependencies(context, progressFeed, options =
1209
1199
  return withResolvedCatalogPlugin({
1210
1200
  selector: pluginId,
1211
1201
  cliVersion: packageInfo.version,
1212
- freshnessPolicy: 'cacheFirst',
1213
1202
  env: context.env,
1214
1203
  signal,
1215
1204
  ...(sessionContextForPlugin(pluginId)
@@ -1244,7 +1233,6 @@ export function createTuiControllerDependencies(context, progressFeed, options =
1244
1233
  return withResolvedCatalogPlugin({
1245
1234
  selector: pluginId,
1246
1235
  cliVersion: packageInfo.version,
1247
- freshnessPolicy: 'cacheFirst',
1248
1236
  env: context.env,
1249
1237
  signal,
1250
1238
  ...(sessionContextForPlugin(pluginId)
@@ -1377,27 +1365,6 @@ export function createTuiControllerDependencies(context, progressFeed, options =
1377
1365
  throw error;
1378
1366
  }
1379
1367
  }
1380
- if (intent.kind === 'catalogActivate') {
1381
- const currentEvidence = await captureEvidence(intent, undefined, undefined, false);
1382
- if (operationEvidenceFingerprint(currentEvidence) !== operationEvidenceFingerprint(expectedEvidence)) {
1383
- throw operationReviewStale('确认后 Catalog Candidate 发生变化。');
1384
- }
1385
- try {
1386
- return await activateCatalogSourceCandidate({
1387
- sourceId: intent.sourceId,
1388
- expectedCandidateGenerationKey: intent.candidateGenerationKey,
1389
- env: context.env,
1390
- });
1391
- }
1392
- catch (error) {
1393
- if (error instanceof CatalogGenerationError
1394
- && (error.code === 'catalogGenerationCandidateChanged'
1395
- || error.code === 'catalogGenerationCandidateUnavailable')) {
1396
- throw operationReviewStale('确认后 Catalog Candidate 发生变化。');
1397
- }
1398
- throw error;
1399
- }
1400
- }
1401
1368
  if (intent.kind === 'sourceMutation') {
1402
1369
  const currentEvidence = await captureEvidence(intent);
1403
1370
  if (operationEvidenceFingerprint(currentEvidence) !== operationEvidenceFingerprint(expectedEvidence)) {
@@ -1437,7 +1404,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
1437
1404
  action: async (awHomeMutationToken) => {
1438
1405
  await tuiControllerRuntimeTestHooks?.beforeMutationEvidenceCapture?.();
1439
1406
  const operationInventory = createOperationInventoryReader();
1440
- const currentEvidence = await captureEvidence(intent, undefined, undefined, false, operationInventory);
1407
+ const currentEvidence = await captureEvidence(intent, undefined, undefined, operationInventory, true);
1441
1408
  if (operationEvidenceFingerprint(currentEvidence) !== operationEvidenceFingerprint(expectedEvidence)) {
1442
1409
  throw new TuiOperationBlockedError('operationReviewStale', '确认后状态发生变化。');
1443
1410
  }
@@ -1448,10 +1415,34 @@ export function createTuiControllerDependencies(context, progressFeed, options =
1448
1415
  }
1449
1416
  await tuiControllerRuntimeTestHooks?.afterMutationEvidenceCapture?.();
1450
1417
  if (intent.kind === 'pluginWrite') {
1418
+ if (intent.dependencyPlacementOnly === 'desired') {
1419
+ const plan = currentEvidence.dependencyPlan;
1420
+ if (!plan || !expectedEvidence.projectIntentDigest) {
1421
+ throw operationReviewStale('确认信息缺少依赖位置或 project intent 证据。');
1422
+ }
1423
+ const location = { scope: 'project', cwd: context.cwd, env: context.env };
1424
+ const currentIntent = await readPluginIntentState(location);
1425
+ if (calculatePluginIntentDigest(currentIntent) !== expectedEvidence.projectIntentDigest) {
1426
+ throw operationReviewStale('确认后 project desired intent 发生变化。');
1427
+ }
1428
+ const plannedKeys = new Set(plan.targets.map((target) => `${target.pluginId}\0${target.platform}`));
1429
+ const nextIntent = canonicalizePluginIntentState({
1430
+ ...currentIntent,
1431
+ dependencyScopeOverrides: [
1432
+ ...currentIntent.dependencyScopeOverrides.filter((override) => !plannedKeys.has(`${override.pluginId}\0${override.platform}`)),
1433
+ ...plan.scopeOverrides,
1434
+ ],
1435
+ });
1436
+ await writePluginIntentState(location, nextIntent);
1437
+ return { dependencyPlan: plan, desiredPlacementUpdated: true };
1438
+ }
1451
1439
  const options = {
1452
1440
  cwd: context.cwd,
1453
1441
  platform: intent.platform,
1454
1442
  scope: intent.scope,
1443
+ ...(intent.action === 'install' && intent.dependencyScopeOverrides
1444
+ ? { dependencyScopeOverrides: intent.dependencyScopeOverrides }
1445
+ : {}),
1455
1446
  ...(expectedEvidence.codexManager?.state === 'requires-install'
1456
1447
  ? { allowManagedCodexInstall: true }
1457
1448
  : {}),
@@ -1463,6 +1454,7 @@ export function createTuiControllerDependencies(context, progressFeed, options =
1463
1454
  reinstall: reinstallWriteCommand,
1464
1455
  uninstall: uninstallWriteCommand,
1465
1456
  cleanup: cleanupWriteCommand,
1457
+ sync: installWriteCommand,
1466
1458
  }[intent.action];
1467
1459
  const currentSource = currentEvidence.source;
1468
1460
  const offlineCleanup = currentEvidence.offlineCleanup;
@@ -1476,6 +1468,10 @@ export function createTuiControllerDependencies(context, progressFeed, options =
1476
1468
  ...(currentEvidence.dependencyPlan
1477
1469
  ? { expectedOperationDependencyPlan: currentEvidence.dependencyPlan }
1478
1470
  : {}),
1471
+ ...(currentEvidence.codexReconcilePlans
1472
+ ? { expectedCodexReconcilePlans: currentEvidence.codexReconcilePlans }
1473
+ : {}),
1474
+ ...(intent.action === 'sync' ? { reconcileIntentOnly: true } : {}),
1479
1475
  expectedCatalogConfigDigest: expectedEvidence.catalog.configDigest,
1480
1476
  ...(catalogOperation ? {
1481
1477
  catalogOperationContext: catalogOperation,
@@ -1500,7 +1496,6 @@ export function createTuiControllerDependencies(context, progressFeed, options =
1500
1496
  return withResolvedCatalogPlugin({
1501
1497
  selector: intent.pluginId,
1502
1498
  cliVersion: packageInfo.version,
1503
- freshnessPolicy: 'cacheFirst',
1504
1499
  env: context.env,
1505
1500
  ...(catalogOperation ? { operationContext: catalogOperation } : {}),
1506
1501
  consume: async ({ plugin, source }) => {