@lesliechan721/aw 0.1.0-beta.64 → 0.1.0-beta.65

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 (302) hide show
  1. package/dist/authoring/assets/aw-runtime-config.mjs +3 -3
  2. package/dist/authoring/assets/aw-runtime-config.mjs.map +1 -1
  3. package/dist/authoring/scripts/add-skill.mjs +3 -17
  4. package/dist/authoring/scripts/add-skill.mjs.map +1 -1
  5. package/dist/authoring/scripts/init-plugin.mjs +1 -0
  6. package/dist/authoring/scripts/init-plugin.mjs.map +1 -1
  7. package/dist/authoring/scripts/lib/plugin-version-check.mjs +1 -1
  8. package/dist/authoring/scripts/lib/skill-scaffold.d.mts +2 -0
  9. package/dist/authoring/scripts/lib/skill-scaffold.mjs +2 -0
  10. package/dist/authoring/scripts/lib/skill-scaffold.mjs.map +1 -1
  11. package/dist/authoring/scripts/validate-plugin.mjs +95 -698
  12. package/dist/authoring/scripts/validate-plugin.mjs.map +1 -1
  13. package/dist/scripts/aw-skill-eval-materializer.d.ts +2 -0
  14. package/dist/scripts/aw-skill-eval-materializer.js +74 -44
  15. package/dist/scripts/aw-skill-eval-materializer.js.map +1 -1
  16. package/dist/scripts/bundle-plugins.js +10 -2
  17. package/dist/scripts/bundle-plugins.js.map +1 -1
  18. package/dist/scripts/plugin-staging.d.ts +1 -1
  19. package/dist/scripts/plugin-staging.js +1 -1
  20. package/dist/scripts/plugin-staging.js.map +1 -1
  21. package/dist/scripts/test-plugin-scripts.js +31 -31
  22. package/dist/scripts/test-plugin-scripts.js.map +1 -1
  23. package/dist/src/commands/aw-capabilities.d.ts +1 -1
  24. package/dist/src/commands/aw-capabilities.js +2 -0
  25. package/dist/src/commands/aw-capabilities.js.map +1 -1
  26. package/dist/src/commands/console.js +4 -3
  27. package/dist/src/commands/console.js.map +1 -1
  28. package/dist/src/commands/intent-command.d.ts +30 -0
  29. package/dist/src/commands/intent-command.js +164 -0
  30. package/dist/src/commands/intent-command.js.map +1 -0
  31. package/dist/src/commands/operation-plan.d.ts +2 -0
  32. package/dist/src/commands/operation-plan.js +2 -1
  33. package/dist/src/commands/operation-plan.js.map +1 -1
  34. package/dist/src/commands/options.d.ts +3 -11
  35. package/dist/src/commands/options.js +11 -17
  36. package/dist/src/commands/options.js.map +1 -1
  37. package/dist/src/commands/plugin-capabilities.js +27 -33
  38. package/dist/src/commands/plugin-capabilities.js.map +1 -1
  39. package/dist/src/commands/rebuild-command.d.ts +108 -6
  40. package/dist/src/commands/rebuild-command.js +617 -1549
  41. package/dist/src/commands/rebuild-command.js.map +1 -1
  42. package/dist/src/commands/rebuild-integration.d.ts +19 -3
  43. package/dist/src/commands/rebuild-integration.js +500 -459
  44. package/dist/src/commands/rebuild-integration.js.map +1 -1
  45. package/dist/src/commands/rebuild-operations.js +1 -2
  46. package/dist/src/commands/rebuild-operations.js.map +1 -1
  47. package/dist/src/commands/review-inspection.d.ts +2 -2
  48. package/dist/src/commands/review-inspection.js +30 -86
  49. package/dist/src/commands/review-inspection.js.map +1 -1
  50. package/dist/src/commands/review-preparation.js +16 -7
  51. package/dist/src/commands/review-preparation.js.map +1 -1
  52. package/dist/src/commands/skill-eval.d.ts +2 -0
  53. package/dist/src/commands/skill-eval.js +15 -9
  54. package/dist/src/commands/skill-eval.js.map +1 -1
  55. package/dist/src/commands/status-v6.d.ts +38 -5
  56. package/dist/src/commands/status-v6.js +269 -102
  57. package/dist/src/commands/status-v6.js.map +1 -1
  58. package/dist/src/commands/targets-command.d.ts +27 -0
  59. package/dist/src/commands/targets-command.js +77 -0
  60. package/dist/src/commands/targets-command.js.map +1 -0
  61. package/dist/src/commands/write-commands.js +5 -5
  62. package/dist/src/commands/write-commands.js.map +1 -1
  63. package/dist/src/eval/manifest-v1.d.ts +2 -0
  64. package/dist/src/eval/manifest-v1.js +7 -2
  65. package/dist/src/eval/manifest-v1.js.map +1 -1
  66. package/dist/src/generators/local-plugin.d.ts +2 -1
  67. package/dist/src/generators/local-plugin.js +111 -108
  68. package/dist/src/generators/local-plugin.js.map +1 -1
  69. package/dist/src/index.d.ts +10 -17
  70. package/dist/src/index.js +585 -1349
  71. package/dist/src/index.js.map +1 -1
  72. package/dist/src/intent/deployment-preferences.d.ts +63 -0
  73. package/dist/src/intent/deployment-preferences.js +170 -0
  74. package/dist/src/intent/deployment-preferences.js.map +1 -0
  75. package/dist/src/intent/diagnostics.d.ts +11 -0
  76. package/dist/src/intent/diagnostics.js +14 -0
  77. package/dist/src/intent/diagnostics.js.map +1 -0
  78. package/dist/src/intent/intent-codec.d.ts +19 -0
  79. package/dist/src/intent/intent-codec.js +117 -0
  80. package/dist/src/intent/intent-codec.js.map +1 -0
  81. package/dist/src/intent/intent-store.d.ts +43 -0
  82. package/dist/src/intent/intent-store.js +118 -0
  83. package/dist/src/intent/intent-store.js.map +1 -0
  84. package/dist/src/intent/scope-context.d.ts +33 -0
  85. package/dist/src/intent/scope-context.js +145 -0
  86. package/dist/src/intent/scope-context.js.map +1 -0
  87. package/dist/src/intent/work-context.d.ts +13 -0
  88. package/dist/src/intent/work-context.js +22 -0
  89. package/dist/src/intent/work-context.js.map +1 -0
  90. package/dist/src/output/command-receipt.js +16 -9
  91. package/dist/src/output/command-receipt.js.map +1 -1
  92. package/dist/src/portable/commands.d.ts +18 -8
  93. package/dist/src/portable/commands.js +46 -562
  94. package/dist/src/portable/commands.js.map +1 -1
  95. package/dist/src/portable/historical-materialization.d.ts +22 -0
  96. package/dist/src/portable/historical-materialization.js +124 -0
  97. package/dist/src/portable/historical-materialization.js.map +1 -0
  98. package/dist/src/portable/materializer.d.ts +8 -0
  99. package/dist/src/portable/materializer.js +75 -44
  100. package/dist/src/portable/materializer.js.map +1 -1
  101. package/dist/src/portable/recovery-layout.d.ts +17 -0
  102. package/dist/src/portable/recovery-layout.js +55 -0
  103. package/dist/src/portable/recovery-layout.js.map +1 -0
  104. package/dist/src/portable/recovery.d.ts +143 -0
  105. package/dist/src/portable/recovery.js +339 -0
  106. package/dist/src/portable/recovery.js.map +1 -0
  107. package/dist/src/portable/writer.d.ts +64 -0
  108. package/dist/src/portable/writer.js +316 -0
  109. package/dist/src/portable/writer.js.map +1 -0
  110. package/dist/src/recovery/coordinator.d.ts +137 -0
  111. package/dist/src/recovery/coordinator.js +1198 -0
  112. package/dist/src/recovery/coordinator.js.map +1 -0
  113. package/dist/src/recovery/demand.d.ts +44 -0
  114. package/dist/src/recovery/demand.js +178 -0
  115. package/dist/src/recovery/demand.js.map +1 -0
  116. package/dist/src/recovery/joint-coordinator.d.ts +24 -0
  117. package/dist/src/recovery/joint-coordinator.js +770 -0
  118. package/dist/src/recovery/joint-coordinator.js.map +1 -0
  119. package/dist/src/recovery/joint-demand.d.ts +68 -0
  120. package/dist/src/recovery/joint-demand.js +166 -0
  121. package/dist/src/recovery/joint-demand.js.map +1 -0
  122. package/dist/src/recovery/receipt.d.ts +81 -0
  123. package/dist/src/recovery/receipt.js +2 -0
  124. package/dist/src/recovery/receipt.js.map +1 -0
  125. package/dist/src/recovery/retirement.d.ts +45 -0
  126. package/dist/src/recovery/retirement.js +366 -0
  127. package/dist/src/recovery/retirement.js.map +1 -0
  128. package/dist/src/recovery/status.d.ts +108 -0
  129. package/dist/src/recovery/status.js +417 -0
  130. package/dist/src/recovery/status.js.map +1 -0
  131. package/dist/src/recovery/types.d.ts +86 -0
  132. package/dist/src/recovery/types.js +2 -0
  133. package/dist/src/recovery/types.js.map +1 -0
  134. package/dist/src/review/review-profile.d.ts +2 -1
  135. package/dist/src/review/review-profile.js +15 -15
  136. package/dist/src/review/review-profile.js.map +1 -1
  137. package/dist/src/review/review-selection.d.ts +1 -2
  138. package/dist/src/review/review-selection.js +40 -114
  139. package/dist/src/review/review-selection.js.map +1 -1
  140. package/dist/src/review/review-types.d.ts +4 -4
  141. package/dist/src/review/review-types.js.map +1 -1
  142. package/dist/src/review/skill-materialization.js +15 -6
  143. package/dist/src/review/skill-materialization.js.map +1 -1
  144. package/dist/src/review/source-binding.d.ts +14 -4
  145. package/dist/src/review/source-binding.js +62 -117
  146. package/dist/src/review/source-binding.js.map +1 -1
  147. package/dist/src/tui/agent-management.d.ts +23 -0
  148. package/dist/src/tui/agent-management.js +217 -0
  149. package/dist/src/tui/agent-management.js.map +1 -0
  150. package/dist/src/tui/agent-settings.d.ts +44 -0
  151. package/dist/src/tui/agent-settings.js +98 -0
  152. package/dist/src/tui/agent-settings.js.map +1 -0
  153. package/dist/src/tui/components.d.ts +3 -0
  154. package/dist/src/tui/components.js +10 -1
  155. package/dist/src/tui/components.js.map +1 -1
  156. package/dist/src/tui/config-references.d.ts +6 -3
  157. package/dist/src/tui/config-references.js +17 -12
  158. package/dist/src/tui/config-references.js.map +1 -1
  159. package/dist/src/tui/console-app-v4.d.ts +1 -2
  160. package/dist/src/tui/console-app-v4.js +1533 -1047
  161. package/dist/src/tui/console-app-v4.js.map +1 -1
  162. package/dist/src/tui/controller-runtime.d.ts +2 -1
  163. package/dist/src/tui/controller-runtime.js +405 -1626
  164. package/dist/src/tui/controller-runtime.js.map +1 -1
  165. package/dist/src/tui/controller.d.ts +131 -81
  166. package/dist/src/tui/controller.js +307 -227
  167. package/dist/src/tui/controller.js.map +1 -1
  168. package/dist/src/tui/entity-cards.d.ts +2 -2
  169. package/dist/src/tui/entity-cards.js +98 -20
  170. package/dist/src/tui/entity-cards.js.map +1 -1
  171. package/dist/src/tui/glossary.d.ts +2 -0
  172. package/dist/src/tui/glossary.js +20 -0
  173. package/dist/src/tui/glossary.js.map +1 -1
  174. package/dist/src/tui/model.d.ts +28 -10
  175. package/dist/src/tui/model.js +41 -41
  176. package/dist/src/tui/model.js.map +1 -1
  177. package/dist/src/tui/operation-review-planner.d.ts +2 -0
  178. package/dist/src/tui/operation-review-planner.js +58 -122
  179. package/dist/src/tui/operation-review-planner.js.map +1 -1
  180. package/dist/src/tui/plugin-content.js +7 -4
  181. package/dist/src/tui/plugin-content.js.map +1 -1
  182. package/dist/src/tui/plugin-detail.d.ts +22 -3
  183. package/dist/src/tui/plugin-detail.js +113 -26
  184. package/dist/src/tui/plugin-detail.js.map +1 -1
  185. package/dist/src/tui/reader.d.ts +1 -0
  186. package/dist/src/tui/reader.js +3 -1
  187. package/dist/src/tui/reader.js.map +1 -1
  188. package/dist/src/tui/router.d.ts +23 -33
  189. package/dist/src/tui/router.js.map +1 -1
  190. package/dist/src/tui/scoped-flow.d.ts +45 -0
  191. package/dist/src/tui/scoped-flow.js +96 -0
  192. package/dist/src/tui/scoped-flow.js.map +1 -0
  193. package/dist/src/tui/scoped-inventory.d.ts +122 -0
  194. package/dist/src/tui/scoped-inventory.js +225 -0
  195. package/dist/src/tui/scoped-inventory.js.map +1 -0
  196. package/dist/src/tui/screen-registry.d.ts +25 -25
  197. package/dist/src/tui/screen-registry.js +3 -7
  198. package/dist/src/tui/screen-registry.js.map +1 -1
  199. package/dist/src/tui/session-console-app.js +1 -1
  200. package/dist/src/tui/session-console-app.js.map +1 -1
  201. package/dist/src/tui/session-host.js +12 -2
  202. package/dist/src/tui/session-host.js.map +1 -1
  203. package/dist/src/types.d.ts +15 -12
  204. package/dist/src/types.js.map +1 -1
  205. package/dist/src/utils/agent-version.js +2 -0
  206. package/dist/src/utils/agent-version.js.map +1 -1
  207. package/dist/src/utils/artifact-plan.js +13 -6
  208. package/dist/src/utils/artifact-plan.js.map +1 -1
  209. package/dist/src/utils/artifact-renderer.d.ts +6 -3
  210. package/dist/src/utils/artifact-renderer.js +27 -32
  211. package/dist/src/utils/artifact-renderer.js.map +1 -1
  212. package/dist/src/utils/artifact-request.d.ts +1 -0
  213. package/dist/src/utils/artifact-request.js +22 -12
  214. package/dist/src/utils/artifact-request.js.map +1 -1
  215. package/dist/src/utils/artifact-sources.d.ts +2 -2
  216. package/dist/src/utils/bundle-layout.js +18 -16
  217. package/dist/src/utils/bundle-layout.js.map +1 -1
  218. package/dist/src/utils/catalog-digests.js +2 -0
  219. package/dist/src/utils/catalog-digests.js.map +1 -1
  220. package/dist/src/utils/catalog-runtime-projection.d.ts +4 -3
  221. package/dist/src/utils/catalog-runtime-projection.js.map +1 -1
  222. package/dist/src/utils/catalog-source-references.d.ts +5 -0
  223. package/dist/src/utils/catalog-source-references.js +42 -57
  224. package/dist/src/utils/catalog-source-references.js.map +1 -1
  225. package/dist/src/utils/claude-native-render-receipt.d.ts +14 -0
  226. package/dist/src/utils/claude-native-render-receipt.js +26 -0
  227. package/dist/src/utils/claude-native-render-receipt.js.map +1 -0
  228. package/dist/src/utils/claude-skills-root.js +14 -3
  229. package/dist/src/utils/claude-skills-root.js.map +1 -1
  230. package/dist/src/utils/codex-manager.js +6 -12
  231. package/dist/src/utils/codex-manager.js.map +1 -1
  232. package/dist/src/utils/codex-native-observation.js +29 -13
  233. package/dist/src/utils/codex-native-observation.js.map +1 -1
  234. package/dist/src/utils/codex-reconciler.d.ts +2 -5
  235. package/dist/src/utils/codex-reconciler.js +25 -1
  236. package/dist/src/utils/codex-reconciler.js.map +1 -1
  237. package/dist/src/utils/codex-surface-analysis.js +5 -3
  238. package/dist/src/utils/codex-surface-analysis.js.map +1 -1
  239. package/dist/src/utils/local-marketplace.d.ts +14 -2
  240. package/dist/src/utils/local-marketplace.js +209 -42
  241. package/dist/src/utils/local-marketplace.js.map +1 -1
  242. package/dist/src/utils/managed-command-evidence.js +1 -1
  243. package/dist/src/utils/managed-command-evidence.js.map +1 -1
  244. package/dist/src/utils/managed-command-materialization.d.ts +4 -0
  245. package/dist/src/utils/managed-command-materialization.js +15 -6
  246. package/dist/src/utils/managed-command-materialization.js.map +1 -1
  247. package/dist/src/utils/managed-command-store.d.ts +15 -0
  248. package/dist/src/utils/managed-command-store.js +73 -8
  249. package/dist/src/utils/managed-command-store.js.map +1 -1
  250. package/dist/src/utils/native-cli.d.ts +2 -0
  251. package/dist/src/utils/native-cli.js +22 -0
  252. package/dist/src/utils/native-cli.js.map +1 -1
  253. package/dist/src/utils/native-demand.d.ts +10 -0
  254. package/dist/src/utils/native-demand.js +4 -0
  255. package/dist/src/utils/native-demand.js.map +1 -0
  256. package/dist/src/utils/ownership.js +2 -0
  257. package/dist/src/utils/ownership.js.map +1 -1
  258. package/dist/src/utils/paths.js +1 -1
  259. package/dist/src/utils/paths.js.map +1 -1
  260. package/dist/src/utils/platform-support.d.ts +1 -1
  261. package/dist/src/utils/platform-support.js +1 -1
  262. package/dist/src/utils/platform-support.js.map +1 -1
  263. package/dist/src/utils/plugin-config-impact.d.ts +3 -0
  264. package/dist/src/utils/plugin-config-impact.js +115 -273
  265. package/dist/src/utils/plugin-config-impact.js.map +1 -1
  266. package/dist/src/utils/plugin-config-references.d.ts +9 -2
  267. package/dist/src/utils/plugin-config-references.js +75 -85
  268. package/dist/src/utils/plugin-config-references.js.map +1 -1
  269. package/dist/src/utils/plugin-config-write.d.ts +58 -1
  270. package/dist/src/utils/plugin-config-write.js +756 -809
  271. package/dist/src/utils/plugin-config-write.js.map +1 -1
  272. package/dist/src/utils/plugin-config.d.ts +47 -46
  273. package/dist/src/utils/plugin-config.js +292 -833
  274. package/dist/src/utils/plugin-config.js.map +1 -1
  275. package/dist/src/utils/plugin-scripts.d.ts +11 -6
  276. package/dist/src/utils/plugin-scripts.js +52 -35
  277. package/dist/src/utils/plugin-scripts.js.map +1 -1
  278. package/dist/src/utils/runtime-config.js +5 -4
  279. package/dist/src/utils/runtime-config.js.map +1 -1
  280. package/dist/src/utils/runtime-skill-closure.d.ts +2 -2
  281. package/dist/src/utils/runtime-skill-closure.js.map +1 -1
  282. package/dist/src/utils/state-paths.d.ts +8 -3
  283. package/dist/src/utils/state-paths.js +53 -16
  284. package/dist/src/utils/state-paths.js.map +1 -1
  285. package/dist/src/utils/windows-npm-shim.d.ts +2 -0
  286. package/dist/src/utils/windows-npm-shim.js +5 -7
  287. package/dist/src/utils/windows-npm-shim.js.map +1 -1
  288. package/dist/vitest.config.js +1 -0
  289. package/dist/vitest.config.js.map +1 -1
  290. package/package.json +3 -3
  291. package/dist/src/commands/sync-command.d.ts +0 -40
  292. package/dist/src/commands/sync-command.js +0 -254
  293. package/dist/src/commands/sync-command.js.map +0 -1
  294. package/dist/src/portable/skills-cli.d.ts +0 -51
  295. package/dist/src/portable/skills-cli.js +0 -202
  296. package/dist/src/portable/skills-cli.js.map +0 -1
  297. package/dist/src/tui/project-inventory.d.ts +0 -68
  298. package/dist/src/tui/project-inventory.js +0 -203
  299. package/dist/src/tui/project-inventory.js.map +0 -1
  300. package/dist/src/utils/plugin-intent-state.d.ts +0 -49
  301. package/dist/src/utils/plugin-intent-state.js +0 -320
  302. package/dist/src/utils/plugin-intent-state.js.map +0 -1
@@ -0,0 +1,1198 @@
1
+ import { describeNativeToolClosure } from '../utils/native-tool-review.js';
2
+ import path from 'node:path';
3
+ import { getCommandPackageId } from '@lesliechan721/aw-plugin-core/manifest';
4
+ import { runRebuildCommand } from '../commands/rebuild-command.js';
5
+ import { buildRebuildReceiptJson } from '../output/command-receipt.js';
6
+ import { withAwHomeMutationLock } from '../utils/aw-home-mutation-lock.js';
7
+ import { createCodexNativeObservationSession } from '../utils/codex-native-observation.js';
8
+ import { bindNativeCliRunnerSignal, getDefaultNativeCliRunner, prepareNativeCliExecution } from '../utils/native-cli.js';
9
+ import { portableRecoveryLayout } from '../portable/recovery-layout.js';
10
+ import { canonicalizePathThroughExistingAncestor, resolveClaudeProjectKey, resolveCodexProjectKey } from '../utils/state-paths.js';
11
+ import { parsePluginId } from '../utils/multi-source-catalog.js';
12
+ import { activateCatalogSourceGeneration, probeCatalogSourceGeneration, } from '../utils/catalog-generation-service.js';
13
+ import { readCatalogGenerationState } from '../utils/catalog-generation-state.js';
14
+ import { readCatalogSourcesConfigSnapshot } from '../utils/catalog-sources.js';
15
+ import { calculateCatalogSourceImpactDigest, inspectCatalogSourceImpact, } from '../utils/catalog-source-impact.js';
16
+ import { preparePortableRecovery, portablePreparationDiagnostic, preparePortableCleanup, inspectPortableRecovery, inspectPortableResidue, executePortableRecovery, } from '../portable/recovery.js';
17
+ import { addRequirements } from '../commands/intent-command.js';
18
+ import { readDemand, demandNativeProjection, resolveDelivery } from './demand.js';
19
+ import { selectAgents, writePreferences } from '../intent/deployment-preferences.js';
20
+ import { calculateIntentDigest } from '../intent/intent-codec.js';
21
+ import { observeIntent } from '../intent/intent-store.js';
22
+ import { assertNeutralAncestorChain, createScopeContext } from '../intent/scope-context.js';
23
+ import { prepareJointRecovery, executeJointRecovery } from './joint-coordinator.js';
24
+ import { createEmptyNativeDemandView } from '../utils/native-demand.js';
25
+ import { resolvePluginMetaDependencyClosure } from '../utils/plugin-dependencies.js';
26
+ import { runPreApplyScript } from '../utils/plugin-scripts.js';
27
+ import { readSafeOperationError } from '../utils/safe-operation-diagnostic.js';
28
+ function isPortableAgent(agent) {
29
+ return agent !== 'codex' && agent !== 'claude';
30
+ }
31
+ function diagnostic(code) {
32
+ const messages = {
33
+ nativeToolAuthorizationRequired: '原生工具声明有变化,请使用 --yes 或确认当前 Review 后执行。',
34
+ localTargetsRequired: '已有需求,请先设置本机 Agent。',
35
+ deliveryUnsupported: '此插件没有适用于所选 Agent 的交付。',
36
+ operationReviewStale: '确认依据已改变,请重新检查并确认。',
37
+ syncPluginNotDesired: '所选插件尚未声明,请先运行 aw plugin intent add。',
38
+ syncSelectionRequired: '请指定插件或使用 --all。',
39
+ invalidExactTargets: '精确重试目标不属于当前请求,请重新检查并选择。',
40
+ sourceRefreshFailed: '来源刷新失败,此来源的插件未恢复。',
41
+ sourceRefreshConfirmationRequired: '来源更新必须先单独确认,再重新准备插件恢复。',
42
+ artifactCollision: '多个交付占用相同输出位置,请先处理冲突。',
43
+ deliverySwitchCleanupRequired: '此目标仍有旧 Native 输出;请先清理旧交付,再恢复 Portable。',
44
+ deliverySwitchObservationFailed: '无法确认此目标是否仍有旧 Native 输出;请先恢复 Native 观察能力或人工核验。',
45
+ deliverySwitchPortableCleanupRequired: '此目标仍有旧 Portable 输出;请先核验并移开旧交付,再恢复 Native。',
46
+ deliverySwitchPortableObservationFailed: '无法确认此目标是否仍有旧 Portable 输出;请先核验 Portable 现场。',
47
+ 'backend-action-unsupported': '此后端尚不支持安全移除,请检查精确的 Portable 输出。',
48
+ };
49
+ return { code, message: messages[code] ?? `操作受阻:${code}。` };
50
+ }
51
+ function errorCode(error, fallback) {
52
+ const code = error?.code;
53
+ if (typeof code === 'string' && /^[A-Za-z][A-Za-z0-9-]*$/u.test(code))
54
+ return code;
55
+ const prefix = error instanceof Error ? error.message.split(':')[0] : '';
56
+ return /^[A-Za-z][A-Za-z0-9-]*$/u.test(prefix ?? '') ? prefix : fallback;
57
+ }
58
+ function intentEvidence(intent) {
59
+ return 'rawDigest' in intent ? `${intent.state}:${intent.rawDigest}` : intent.state;
60
+ }
61
+ function preferenceEvidence(demand) {
62
+ return 'rawDigest' in demand.preferences
63
+ ? `${demand.preferences.state}:${demand.preferences.rawDigest}` : demand.preferences.state;
64
+ }
65
+ function agentForPlatform(platform) {
66
+ return platform;
67
+ }
68
+ function pair(plan, pluginId, agent) {
69
+ return plan.receipt.targetResults.find((target) => target.pluginId === pluginId && target.agent === agent);
70
+ }
71
+ function block(target, code, detail = diagnostic(code)) {
72
+ if (!target.blockers.some((entry) => entry.code === code))
73
+ target.blockers.push(detail);
74
+ }
75
+ function completeReceipt(receipt) {
76
+ receipt.requestedTargets = receipt.targetResults.map(({ pluginId, agent, scope, delivery }) => ({ pluginId, agent, scope, delivery }));
77
+ receipt.remainingTargets = receipt.targetResults.filter((target) => target.blockers.length > 0
78
+ || !['applied', 'no-op'].includes(target.execution))
79
+ .map(({ pluginId, agent, scope, delivery }) => ({ pluginId, agent, scope, delivery }));
80
+ receipt.ok = receipt.blockers.length === 0 && receipt.remainingTargets.length === 0
81
+ && (receipt.commandResults ?? []).every((result) => result.state === 'completed');
82
+ receipt.errors = [...receipt.blockers];
83
+ receipt.recoveryActions = [...new Set([
84
+ ...(receipt.commandResults ?? []).flatMap((result) => result.recoveryActions),
85
+ ...receipt.blockers.map((entry) => entry.code === 'localTargetsRequired'
86
+ ? `aw plugin targets set <target...>` : `aw plugin status --context ${receipt.scope} --json`),
87
+ ...receipt.remainingTargets.flatMap((target) => {
88
+ const result = receipt.targetResults.find((entry) => entry.pluginId === target.pluginId && entry.agent === target.agent);
89
+ if (result?.recoveryActions?.length)
90
+ return result.recoveryActions;
91
+ return result?.blockers.some((entry) => entry.code === 'backend-action-unsupported' || entry.code === 'portableObservationFailed')
92
+ ? `检查 ${target.pluginId} 的 Portable 输出及 provider 关联;AW 不支持安全自动删除。`
93
+ : `aw plugin sync ${target.pluginId} --context ${receipt.scope} --target ${target.agent}`;
94
+ }),
95
+ ])];
96
+ return receipt;
97
+ }
98
+ function displayPortablePath(plan, physicalPath) {
99
+ if (plan.options.scope === 'user' || !plan.context.projectRoot)
100
+ return physicalPath;
101
+ const relative = path.relative(plan.context.projectRoot, physicalPath);
102
+ return relative && relative !== '..' && !relative.startsWith(`..${path.sep}`) && !path.isAbsolute(relative)
103
+ ? `项目根目录/${relative.split(path.sep).join('/')}` : physicalPath;
104
+ }
105
+ function portableRecoveryActions(plan, pluginId, targets) {
106
+ return [...new Set(targets.map((target) => {
107
+ const directory = displayPortablePath(plan, target.physicalPath);
108
+ const shared = target.agent === 'generic' || target.agent === 'antigravity'
109
+ ? '该目录可能同时被 Codex 和 Cursor 读取;移开前请确认对两者的影响。' : `该目录供 ${target.agent} 使用。`;
110
+ return `${pluginId}:请检查并备份目录 ${directory};确认归属后可自行移开。${shared}`
111
+ + (plan.command === 'cleanup' || plan.command === 'uninstall'
112
+ ? '无法证实该目录的安全删除边界;处理后重新检查状态。'
113
+ : `然后重试 aw plugin sync ${pluginId} --context ${plan.options.scope} --target ${target.agent}。`);
114
+ }))];
115
+ }
116
+ function selectRoots(demand, selectors, all) {
117
+ const roots = demand.roots?.map((root) => root.pluginId) ?? [];
118
+ if (all && selectors.length)
119
+ throw new Error('syncSelectorConflict');
120
+ if (all)
121
+ return roots;
122
+ if (!selectors.length)
123
+ return roots;
124
+ return [...new Set(selectors.map((selector) => {
125
+ const parsed = parsePluginId(selector);
126
+ const matches = roots.filter((id) => parsed.sourceId ? id === selector
127
+ : parsePluginId(id).pluginName === parsed.pluginName);
128
+ if (matches.length !== 1)
129
+ throw new Error('syncPluginNotDesired');
130
+ return matches[0];
131
+ }))];
132
+ }
133
+ function nativeGroupTargets(group) {
134
+ return group.targets ?? group.pluginIds.flatMap((pluginId) => (group.platform === 'both' ? ['codex', 'claude'] : [group.platform])
135
+ .map((platform) => ({ pluginId, platform })));
136
+ }
137
+ function nativeTargetSelection(targets) {
138
+ const platforms = new Set(targets.map((target) => target.platform));
139
+ return { pluginIds: [...new Set(targets.map((target) => target.pluginId))],
140
+ platform: platforms.size === 1 ? targets[0].platform : 'both', targets: [...targets] };
141
+ }
142
+ async function nativeOperation(plan, pluginIds, platform, preview, runtime = plan.runtime, command = plan.command, selectedTargets) {
143
+ if (!runtime.awHomeMutationToken) {
144
+ await prepareNativeCliExecution({ env: plan.context.env, runner: runtime.nativeCliRunner });
145
+ }
146
+ const rebuildCommand = command === 'install' ? 'sync' : command;
147
+ const result = await runRebuildCommand({
148
+ command: rebuildCommand,
149
+ pluginNames: pluginIds,
150
+ commandOptions: {
151
+ cwd: plan.context.executionCwd, scope: plan.options.scope, platform,
152
+ yes: plan.options.yes,
153
+ allowManagedCodexInstall: plan.options.allowManagedCodexInstall,
154
+ scriptTimeoutMs: plan.options.scriptTimeoutMs,
155
+ ...(plan.options.dependencyScopeOverrides ? { dependencyScopeOverrides: plan.options.dependencyScopeOverrides } : {}),
156
+ ...(command === 'cleanup' ? { force: plan.options.force } : {}),
157
+ },
158
+ runtime: {
159
+ ...runtime,
160
+ preserveLegacyNativeSkills: plan.options.existingOnly === true,
161
+ nativeActivationOnlyTargets: plan.options.nativeActivationOnlyTargets,
162
+ nativePlannedRetirements: plan.options.nativePlannedRetirements,
163
+ ...(preview ? { nativeCliRunner: bindNativeCliRunnerSignal(runtime.nativeCliRunner ?? getDefaultNativeCliRunner(), runtime.signal) } : {}),
164
+ env: plan.options.env,
165
+ catalogOperationContext: plan.demand.catalogOperationContext,
166
+ previewOperationPlan: preview,
167
+ executionInput: {
168
+ context: plan.context,
169
+ requirements: command === 'cleanup' && (plan.demand.intent.state !== 'valid'
170
+ || plan.demand.preferences.state === 'invalid' || plan.demand.preferences.state === 'unreadable')
171
+ ? createEmptyNativeDemandView() : demandNativeProjection(plan.demand),
172
+ selectedTargets: (selectedTargets ?? nativeGroupTargets({ pluginIds, platform }))
173
+ .map((target) => ({ ...target, scope: plan.options.scope })),
174
+ preserveOtherObservedTargets: true,
175
+ },
176
+ },
177
+ });
178
+ if (preview)
179
+ runtime.signal?.throwIfAborted();
180
+ return result;
181
+ }
182
+ function nativeCleanupEvidence(result, pluginId, platform, scope) {
183
+ const actions = result.operationPlan?.actions.filter((action) => action.pluginId === pluginId && action.platform === platform && action.scope === scope) ?? [];
184
+ return [...new Set(actions.map((action) => action.kind))];
185
+ }
186
+ function nativeResults(plan, group, result, executed) {
187
+ if (executed && result.commandResults?.length) {
188
+ plan.receipt.commandResults = [...new Map([
189
+ ...(plan.receipt.commandResults ?? []), ...result.commandResults,
190
+ ].map((entry) => [getCommandPackageId(entry.producer), entry])).values()];
191
+ }
192
+ for (const { pluginId, platform } of nativeGroupTargets(group)) {
193
+ const agent = agentForPlatform(platform);
194
+ const target = pair(plan, pluginId, agent);
195
+ if (!target)
196
+ continue;
197
+ const backend = buildRebuildReceiptJson(group.command === 'install' ? 'sync' : group.command, result);
198
+ target.backend = backend;
199
+ target.recoveryActions = backend.recoveryActions;
200
+ const backendTargets = result.targetResults.filter((entry) => entry.target.pluginId === pluginId
201
+ && entry.target.platform === platform && entry.target.scope === plan.options.scope);
202
+ const dependencies = result.dependencyPlan?.targets.filter((entry) => entry.platform === platform && (entry.pluginId === pluginId || entry.requiredBy?.includes(pluginId))) ?? [];
203
+ const producers = (result.commandResults ?? []).filter((entry) => {
204
+ if (entry.producer.platform !== null && entry.producer.platform !== platform)
205
+ return false;
206
+ if (entry.producer.platform === null) {
207
+ // Package identity is shared by AW; its declaration defines the Native consumers.
208
+ const plugin = plan.demand.plugins.find((plugin) => plugin.sourceId === entry.producer.sourceId
209
+ && plugin.sourceIdentity === entry.producer.sourceIdentity
210
+ && plugin.manifest.name === entry.producer.pluginName);
211
+ const declaration = plugin?.manifest.managedCommands?.packages.find((declaration) => declaration.key === entry.producer.key && declaration.scope === entry.producer.scope);
212
+ if (!declaration?.platforms.includes(platform)
213
+ || entry.producer.scope === 'project' && entry.producer.projectRoot !== plan.context.projectRoot)
214
+ return false;
215
+ }
216
+ const producerPluginId = `${entry.producer.sourceId}/${entry.producer.pluginName}`;
217
+ return producerPluginId === pluginId
218
+ || dependencies.some((dependency) => dependency.pluginId === producerPluginId)
219
+ || result.targetResults.some((target) => target.target.pluginId === producerPluginId
220
+ && target.target.platform === platform && target.requiredBy?.includes(pluginId)
221
+ && target.target.scope === (result.dependencyPlan?.targets.find((dependency) => dependency.pluginId === producerPluginId && dependency.platform === platform)?.resolvedScope
222
+ ?? plan.options.scope));
223
+ });
224
+ for (const failure of backendTargets.filter((entry) => entry.state === 'failed')) {
225
+ block(target, failure.errorCode ?? 'nativeRecoveryFailed');
226
+ }
227
+ for (const dependency of dependencies.filter((entry) => entry.action === 'block')) {
228
+ block(target, dependency.errorCode ?? 'dependencyNotReady');
229
+ }
230
+ if (executed) {
231
+ const commandFailures = producers.filter((entry) => entry.state !== 'completed');
232
+ for (const producer of commandFailures) {
233
+ for (const error of producer.errors)
234
+ block(target, error.code);
235
+ if (producer.errors.length === 0)
236
+ block(target, 'managedCommandRecoveryFailed');
237
+ }
238
+ const succeeded = commandFailures.length === 0 && backendTargets.length > 0
239
+ && backendTargets.every((entry) => entry.state === 'completed');
240
+ target.execution = succeeded ? backendTargets.every((entry) => entry.action === 'skipped') ? 'no-op' : 'applied'
241
+ : backendTargets.some((entry) => entry.state === 'completed') ? 'partial' : 'failed';
242
+ if (succeeded && (plan.command === 'cleanup' || plan.command === 'uninstall')) {
243
+ const completed = result.rebuildResults.filter((entry) => entry.target.pluginId === pluginId
244
+ && entry.target.platform === platform && entry.target.scope === plan.options.scope);
245
+ const footprints = result.nativeFootprints?.filter((entry) => entry.pluginId === pluginId
246
+ && entry.platform === platform && entry.scope === plan.options.scope);
247
+ const absent = completed.length > 0 && footprints !== undefined && footprints.length > 0
248
+ && footprints.every((entry) => !entry.shared)
249
+ && completed.every((entry) => entry.status === 'succeeded' && entry.cleanupAttempted
250
+ && entry.removed.length + entry.absent.length > 0 && entry.residual.length === 0
251
+ && entry.quarantined.length === 0 && entry.failures.length === 0);
252
+ target.observation = absent ? 'missing' : 'unknown';
253
+ target.content = absent ? 'absent' : 'unknown';
254
+ }
255
+ else {
256
+ target.observation = succeeded ? 'verified' : 'unknown';
257
+ target.content = succeeded ? 'satisfied' : 'unknown';
258
+ }
259
+ if (!succeeded && !target.blockers.length)
260
+ block(target, 'nativeRecoveryFailed');
261
+ }
262
+ }
263
+ }
264
+ function portableRequiredBy(plan, prepared, target) {
265
+ return plan.portableGroups.filter((group) => group.logicalIdentity === prepared.logicalIdentity
266
+ && group.targets.some((entry) => entry.providerPluginId === target.providerPluginId
267
+ && entry.expectedDigest === target.expectedDigest && entry.physicalPath === target.physicalPath))
268
+ .map((group) => group.options.selector);
269
+ }
270
+ function portableResults(plan, prepared) {
271
+ for (const agent of prepared.options.agents) {
272
+ const target = pair(plan, prepared.options.selector, agent);
273
+ const observations = prepared.targets.filter((entry) => entry.agent === agent);
274
+ target.content = observations.some((entry) => entry.observation === 'conflict') ? 'conflict'
275
+ : observations.some((entry) => entry.observation === 'unknown') ? 'unknown'
276
+ : observations.some((entry) => entry.observation === 'drifted') ? 'drifted'
277
+ : observations.some((entry) => entry.observation === 'absent') ? 'absent'
278
+ : observations.length ? 'satisfied' : 'unknown';
279
+ target.observation = target.content === 'satisfied' ? 'verified'
280
+ : target.content === 'absent' ? 'missing' : 'unknown';
281
+ for (const code of [...prepared.globalBlockers, ...observations.flatMap((entry) => entry.blocker ? [entry.blocker] : [])]) {
282
+ block(target, code, portablePreparationDiagnostic(prepared, diagnostic(code), agent));
283
+ }
284
+ target.backend = observations.map((entry) => ({ skill: entry.skill, providerPluginId: entry.providerPluginId,
285
+ requiredBy: portableRequiredBy(plan, prepared, entry), observation: entry.observation,
286
+ ownership: entry.ownership, associatedAgents: entry.associatedAgents }));
287
+ target.recoveryActions = portableRecoveryActions(plan, target.pluginId, observations.filter((entry) => ['conflict', 'unknown'].includes(entry.observation)));
288
+ }
289
+ }
290
+ function portableOutcomes(plan, prepared, agent, outcomes, globalBlockers) {
291
+ const target = pair(plan, prepared.options.selector, agent);
292
+ const contentOutcomes = outcomes.filter((entry) => entry.disposition !== 'remove');
293
+ target.execution = prepared.mode === 'cleanup' && outcomes.length === 0 && !globalBlockers.length ? 'no-op' : outcomes.length > 0 && outcomes.every((entry) => entry.execution === 'no-op') ? 'no-op'
294
+ : outcomes.length > 0 && outcomes.every((entry) => ['applied', 'no-op'].includes(entry.execution)) ? 'applied'
295
+ : outcomes.some((entry) => ['applied', 'no-op'].includes(entry.execution)) ? 'partial'
296
+ : outcomes.some((entry) => entry.execution === 'failed') ? 'failed' : 'not-run';
297
+ target.observation = contentOutcomes.length > 0 && contentOutcomes.every((entry) => entry.observation === 'satisfied')
298
+ && globalBlockers.length === 0 ? 'verified'
299
+ : globalBlockers.length === 0 && contentOutcomes.some((entry) => entry.observation === 'absent'
300
+ || entry.observation === 'drifted') && contentOutcomes.every((entry) => entry.observation !== 'unknown'
301
+ && entry.observation !== 'conflict') ? 'missing' : 'unknown';
302
+ target.content = contentOutcomes.some((entry) => entry.observation === 'conflict') ? 'conflict'
303
+ : contentOutcomes.some((entry) => entry.observation === 'unknown') ? 'unknown'
304
+ : contentOutcomes.some((entry) => entry.observation === 'drifted') ? 'drifted'
305
+ : contentOutcomes.some((entry) => entry.observation === 'absent') ? 'absent'
306
+ : contentOutcomes.length ? 'satisfied' : 'unknown';
307
+ if (prepared.mode === 'cleanup' && globalBlockers.length === 0
308
+ && outcomes.every((entry) => entry.observation === 'absent' && entry.blockers.length === 0)) {
309
+ target.observation = 'missing';
310
+ target.content = 'absent';
311
+ }
312
+ target.backend = outcomes.map((entry) => ({ skill: entry.skill, providerPluginId: entry.providerPluginId,
313
+ requiredBy: portableRequiredBy(plan, prepared, entry), observation: entry.observation,
314
+ ownership: entry.ownership, associatedAgents: entry.associatedAgents,
315
+ execution: entry.execution, blockers: entry.blockers }));
316
+ for (const code of [...globalBlockers, ...outcomes.flatMap((entry) => entry.blockers)])
317
+ block(target, code);
318
+ target.recoveryActions = portableRecoveryActions(plan, target.pluginId, outcomes.filter((entry) => ['conflict', 'unknown'].includes(entry.observation)));
319
+ }
320
+ function portablePluginClosure(plan, prepared) {
321
+ const { sourceId, pluginName } = parsePluginId(prepared.options.selector);
322
+ return resolvePluginMetaDependencyClosure(plan.demand.plugins.filter((plugin) => plugin.sourceId === sourceId), [pluginName])
323
+ .filter((plugin) => !prepared.options.excludedPluginIds?.includes(plugin.pluginId));
324
+ }
325
+ function preparePortablePreApplyAuthorization(plan) {
326
+ if (plan.command === 'cleanup' || plan.command === 'uninstall')
327
+ return;
328
+ const authorization = new Map();
329
+ for (const prepared of plan.portableGroups) {
330
+ for (const plugin of portablePluginClosure(plan, prepared)) {
331
+ if (!plugin.manifest.scripts?.preApply)
332
+ continue;
333
+ for (const platform of prepared.options.agents) {
334
+ if (pair(plan, prepared.options.selector, platform)?.blockers.length)
335
+ continue;
336
+ const target = {
337
+ pluginId: plugin.pluginId, pluginName: plugin.manifest.name,
338
+ sourceId: plugin.sourceId, sourceIdentity: plugin.sourceIdentity,
339
+ platform, scope: plan.options.scope,
340
+ relation: plugin.pluginId === prepared.options.selector ? 'requested'
341
+ : plan.demand.plugins.find((entry) => entry.pluginId === prepared.options.selector)?.manifest.dependencies?.includes(plugin.manifest.name)
342
+ ? 'direct' : 'transitive',
343
+ };
344
+ authorization.set([target.pluginId, target.platform, target.scope].join('\0'), target);
345
+ }
346
+ }
347
+ }
348
+ plan.preApplyAuthorization = [...authorization.values()];
349
+ plan.changes.push(...plan.preApplyAuthorization.map((target) => `运行插件 preApply 脚本:${target.pluginId} / ${target.platform} / ${target.scope}。`));
350
+ }
351
+ async function executePortablePreApply(plan, prepared, agent, runtime) {
352
+ if (prepared.mode === 'cleanup' || plan.command === 'cleanup' || plan.command === 'uninstall')
353
+ return;
354
+ const plugins = portablePluginClosure(plan, prepared);
355
+ const required = plan.preApplyAuthorization?.filter((target) => target.platform === agent
356
+ && plugins.some((plugin) => plugin.pluginId === target.pluginId)) ?? [];
357
+ return executePreApplyAuthorization(plan, required, runtime);
358
+ }
359
+ async function executePreApplyAuthorization(plan, required, runtime) {
360
+ if (!required.length)
361
+ return;
362
+ if (plan.options.yes !== true && runtime.preApplyConfirmed !== true)
363
+ return 'preApplyAuthorizationRequired';
364
+ const cache = runtime.preApplyExecutionCache ??= new Map();
365
+ for (const target of required) {
366
+ const plugin = plan.demand.plugins.find((entry) => entry.pluginId === target.pluginId);
367
+ if (!plugin || plugin.sourceIdentity !== target.sourceIdentity)
368
+ return 'sourceUnavailable';
369
+ const context = target.scope === plan.options.scope ? plan.context
370
+ : await createScopeContext({ scope: target.scope, cwd: plan.context.callerCwd, env: plan.options.env });
371
+ try {
372
+ const projectKey = target.scope !== 'project' ? '' : target.platform === 'codex'
373
+ ? await resolveCodexProjectKey(context.executionCwd) : target.platform === 'claude'
374
+ ? await resolveClaudeProjectKey(context.executionCwd) : context.projectKey;
375
+ const key = [target.sourceIdentity, target.pluginId, target.platform, target.scope, projectKey].join('\0');
376
+ let execution = cache.get(key);
377
+ if (!execution) {
378
+ execution = runPreApplyScript({ plugin, platform: target.platform, scope: target.scope,
379
+ cwd: context.executionCwd, context: { env: context.env },
380
+ referencePlugins: plan.demand.plugins, scriptTimeoutMs: plan.options.scriptTimeoutMs });
381
+ cache.set(key, execution);
382
+ }
383
+ await execution;
384
+ }
385
+ catch (error) {
386
+ runtime.signal?.throwIfAborted();
387
+ if (error instanceof Error && error.name === 'AbortError')
388
+ throw error;
389
+ return readSafeOperationError(error)?.code ?? errorCode(error, 'preApplyFailed');
390
+ }
391
+ finally {
392
+ if (context !== plan.context)
393
+ await context.dispose();
394
+ }
395
+ }
396
+ }
397
+ /** Run the reviewed replacement's hooks before any dependent old-output cleanup. */
398
+ export async function executeRecoveryPreApply(plan, pluginId, agent, runtime) {
399
+ if (plan.command === 'cleanup' || plan.command === 'uninstall')
400
+ return;
401
+ if (plan.receipt.nativeToolChanges?.length && plan.options.yes !== true)
402
+ return 'nativeToolAuthorizationRequired';
403
+ const target = pair(plan, pluginId, agent);
404
+ if (!target)
405
+ return 'preApplyTargetUnavailable';
406
+ if (target.blockers.length)
407
+ return target.blockers[0].code;
408
+ if (target.delivery?.kind === 'native') {
409
+ const platform = target.delivery.platform;
410
+ try {
411
+ const authorization = await nativeOperation(plan, [pluginId], platform, true, { ...runtime, resolvePreApplyAuthorization: true }, plan.command, [{ pluginId, platform }]);
412
+ if (authorization.overallState !== 'succeeded' || !authorization.preApplyAuthorization) {
413
+ return authorization.targetResults.find((result) => result.errorCode)?.errorCode ?? 'preApplyAuthorizationUnavailable';
414
+ }
415
+ const blocked = authorization.dependencyPlan?.targets.find((entry) => entry.action === 'block'
416
+ && (entry.pluginId === pluginId || entry.requiredBy.includes(pluginId)));
417
+ if (blocked)
418
+ return blocked.errorCode ?? 'nativePreparationFailed';
419
+ return executePreApplyAuthorization(plan, authorization.preApplyAuthorization, runtime);
420
+ }
421
+ catch (error) {
422
+ runtime.signal?.throwIfAborted();
423
+ if (error instanceof Error && error.name === 'AbortError')
424
+ throw error;
425
+ return readSafeOperationError(error)?.code ?? errorCode(error, 'nativePreparationFailed');
426
+ }
427
+ }
428
+ if (isPortableAgent(agent)) {
429
+ const prepared = plan.portableGroups.find((group) => group.options.selector === pluginId
430
+ && group.options.agents.includes(agent));
431
+ if (prepared)
432
+ return executePortablePreApply(plan, prepared, agent, runtime);
433
+ }
434
+ }
435
+ async function checkFootprints(plan) {
436
+ const footprints = [];
437
+ for (const [nativeGroup, group] of plan.nativeGroups.entries()) {
438
+ const actions = group.preview.operationPlan?.actions.filter((action) => (action.kind === 'writeOwnedArtifact' || action.kind === 'removeOwnedArtifact')
439
+ && !group.preview.nativeFootprints?.some((footprint) => footprint.path === action.path
440
+ && footprint.pluginId === action.pluginId && footprint.platform === action.platform)) ?? [];
441
+ for (const action of [...(group.preview.nativeFootprints ?? []), ...actions]) {
442
+ if (!('path' in action))
443
+ continue;
444
+ const consumers = group.preview.dependencyPlan?.targets.filter((target) => target.pluginId === action.pluginId)
445
+ .flatMap((target) => target.requiredBy ?? []) ?? [];
446
+ footprints.push({ path: await canonicalizePathThroughExistingAncestor(action.path),
447
+ pluginIds: group.pluginIds.filter((id) => id === action.pluginId || consumers.includes(id)),
448
+ agent: agentForPlatform(action.platform), identity: `native:${action.platform}:${action.pluginId}`,
449
+ sharedNative: 'shared' in action && action.shared,
450
+ ...('sharedMarketplace' in action && action.sharedMarketplace
451
+ ? { sharedMarketplaceSource: parsePluginId(action.pluginId).sourceId } : {}),
452
+ nativeGroup });
453
+ }
454
+ }
455
+ for (const prepared of plan.portableGroups) {
456
+ for (const target of prepared.targets)
457
+ footprints.push({
458
+ path: await canonicalizePathThroughExistingAncestor(target.physicalPath),
459
+ pluginIds: [prepared.options.selector], agent: target.agent,
460
+ identity: `agent-skills:${prepared.logicalIdentity}:${target.providerPluginId}:${target.expectedDigest}`,
461
+ });
462
+ }
463
+ for (let index = 0; index < footprints.length; index += 1) {
464
+ const left = footprints[index];
465
+ for (const right of footprints.slice(index + 1)) {
466
+ if (left.path !== right.path || left.identity === right.identity
467
+ || (left.nativeGroup !== undefined && left.nativeGroup === right.nativeGroup)
468
+ || (left.sharedMarketplaceSource !== undefined
469
+ && left.sharedMarketplaceSource === right.sharedMarketplaceSource)
470
+ || (left.sharedNative && right.sharedNative && left.agent === right.agent))
471
+ continue;
472
+ for (const footprint of [left, right]) {
473
+ for (const pluginId of footprint.pluginIds) {
474
+ const target = pair(plan, pluginId, footprint.agent);
475
+ if (target)
476
+ block(target, 'artifactCollision');
477
+ }
478
+ }
479
+ }
480
+ }
481
+ for (const prepared of plan.portableGroups) {
482
+ const layout = portableRecoveryLayout(plan.context);
483
+ plan.changes.push(`${prepared.options.selector}:AW 写入所选 Agent 的 owned Skill 目录;准备目录 ${layout.generationRoot}。`);
484
+ }
485
+ }
486
+ export async function prepareScopedRecovery(command, selectors, options, runtime = {}) {
487
+ runtime = { ...runtime, preparedArtifactRenderResults: runtime.preparedArtifactRenderResults ?? new Map() };
488
+ options.signal?.throwIfAborted();
489
+ const context = await createScopeContext({ scope: options.scope, cwd: options.cwd, env: options.env });
490
+ try {
491
+ options.signal?.throwIfAborted();
492
+ options = { ...options, cwd: context.executionCwd, env: context.env };
493
+ let demand = options.demandOverride ?? await readDemand(options);
494
+ const refreshFailures = new Map();
495
+ const sourceRefreshes = [];
496
+ if (options.refresh && command === 'sync') {
497
+ let selected = [];
498
+ try {
499
+ selected = selectRoots(demand, selectors, options.all ?? false);
500
+ }
501
+ catch { /* Selection below reports the invalid request without refreshing any source. */ }
502
+ if (selected.length > 0) {
503
+ const config = await readCatalogSourcesConfigSnapshot(options.env);
504
+ const generationState = await readCatalogGenerationState(options.env);
505
+ for (const sourceId of new Set(selected.map((id) => parsePluginId(id).sourceId))) {
506
+ try {
507
+ const source = config.config.sources.find((entry) => entry.id === sourceId);
508
+ const probe = await probeCatalogSourceGeneration({ sourceId, env: options.env,
509
+ cliVersion: options.cliVersion, signal: options.signal, runtime: runtime.catalogGenerationRuntime });
510
+ const previousActive = generationState.sources[sourceId]?.active ?? null;
511
+ const candidateChanged = !previousActive
512
+ || previousActive.sourceConfigFingerprint !== probe.candidate.sourceConfigFingerprint
513
+ || previousActive.sourceIdentity !== probe.candidate.sourceIdentity
514
+ || previousActive.logicalIdentity !== probe.candidate.logicalIdentity
515
+ || previousActive.requestedRef !== probe.candidate.requestedRef
516
+ || previousActive.manifestDigest !== probe.candidate.manifestDigest
517
+ || previousActive.snapshotDigest !== probe.candidate.snapshotDigest
518
+ || previousActive.resolvedCommit !== probe.candidate.resolvedCommit;
519
+ if (!candidateChanged)
520
+ continue;
521
+ const inspectImpact = runtime.catalogGenerationRuntime?.inspectSourceImpact
522
+ ?? inspectCatalogSourceImpact;
523
+ const impact = await inspectImpact({ sourceId, cwd: options.cwd, env: options.env,
524
+ sourcePrimary: source?.primary === true, mode: 'generation', candidateManifest: probe.manifest });
525
+ sourceRefreshes.push({ sourceId, candidate: probe.candidate, previousActive,
526
+ configDigest: config.digest, impact, impactDigest: calculateCatalogSourceImpactDigest(impact) });
527
+ }
528
+ catch (error) {
529
+ options.signal?.throwIfAborted();
530
+ if (error instanceof Error && error.name === 'AbortError')
531
+ throw error;
532
+ const code = errorCode(error, 'catalogGenerationRefreshFailed');
533
+ refreshFailures.set(sourceId, code.startsWith('catalog') ? code : 'catalogGenerationRefreshFailed');
534
+ }
535
+ }
536
+ }
537
+ }
538
+ options.signal?.throwIfAborted();
539
+ const preparationRuntime = { ...runtime, signal: options.signal };
540
+ const originalIntent = intentEvidence(demand.intent);
541
+ const proposed = command === 'install'
542
+ ? await addRequirements({ ...options, selectors, dryRun: true, installSelectors: true }) : undefined;
543
+ if (proposed?.state && proposed.errors.length === 0) {
544
+ const rawBytes = Buffer.from(JSON.stringify(proposed.state));
545
+ demand = await readDemand({ ...options, intent: {
546
+ state: 'valid', value: proposed.state, rawBytes,
547
+ digest: calculateIntentDigest(proposed.state), rawDigest: originalIntent,
548
+ } });
549
+ }
550
+ const receipt = {
551
+ schemaVersion: 1, kind: 'aw-recovery', scope: options.scope, command, ok: false,
552
+ intent: { state: demand.intent.state, ...('digest' in demand.intent ? { digest: demand.intent.digest } : {}),
553
+ roots: demand.roots?.map((root) => root.pluginId) ?? null },
554
+ requestedRoots: [], requestedTargets: [], selection: { agents: [], origin: 'none', preferences: demand.preferences.state },
555
+ blockers: proposed?.errors ?? [], errors: [], targetResults: [], remainingTargets: [], recoveryActions: [],
556
+ declarationChanged: false, preferencesChanged: false, configChanged: false, outputsMayHaveChanged: false,
557
+ };
558
+ const { signal: _signal, ...executionOptions } = options;
559
+ const plan = { command, options: executionOptions, receipt, demand, context, originalIntent,
560
+ plannedImpact: { declaration: proposed?.changed === true, preferences: false, config: false, outputs: false },
561
+ preferencesEvidence: preferenceEvidence(demand), changes: [], warnings: [],
562
+ nativeGroups: [], historicalNativeTargets: new Set(), deliverySwitchChecks: [], portableGroups: [],
563
+ portableProtectionGroups: [], runtime,
564
+ portableResidues: [], sourceRefreshes,
565
+ sourceRefreshFailures: [...refreshFailures].map(([sourceId, code]) => ({ sourceId, code })),
566
+ blockExecution: false,
567
+ ...(proposed?.state && proposed.errors.length === 0 ? { declaration: proposed.roots } : {}),
568
+ };
569
+ if (proposed?.changed)
570
+ plan.changes.push(`保存 ${options.scope} 插件需求:${proposed.roots.join('、')}。`);
571
+ const preciseCleanup = (command === 'cleanup' || command === 'uninstall') && selectors.length > 0
572
+ && options.targets !== undefined;
573
+ try {
574
+ receipt.selection = selectAgents({ preferences: preciseCleanup ? { state: 'absent' } : demand.preferences,
575
+ targets: options.targets });
576
+ receipt.requestedRoots = preciseCleanup ? [...new Set(selectors.map((selector) => {
577
+ const parsed = parsePluginId(selector);
578
+ if (!parsed.sourceId)
579
+ throw new Error('cleanupPluginIdRequired');
580
+ return `${parsed.sourceId}/${parsed.pluginName}`;
581
+ }))] : selectRoots(demand, selectors, options.all ?? false);
582
+ }
583
+ catch (error) {
584
+ receipt.blockers.push(diagnostic(errorCode(error, 'projectSelectionInvalid')));
585
+ }
586
+ if (options.exactTargets && (options.exactTargets.length === 0 || options.exactTargets.some((target) => !receipt.requestedRoots.includes(target.pluginId) || !receipt.selection.agents.includes(target.agent)))) {
587
+ receipt.blockers.push(diagnostic('invalidExactTargets'));
588
+ }
589
+ if (!preciseCleanup) {
590
+ if (!['valid', 'absent'].includes(demand.intent.state))
591
+ receipt.blockers.push(diagnostic('pluginIntentStateInvalid'));
592
+ }
593
+ if (receipt.requestedRoots.length && !receipt.selection.agents.length)
594
+ receipt.blockers.push(diagnostic('localTargetsRequired'));
595
+ if (!preciseCleanup)
596
+ receipt.blockers.push(...demand.errors);
597
+ plan.blockExecution = Boolean(proposed?.errors.length)
598
+ || receipt.blockers.length > 0;
599
+ for (const [sourceId, code] of refreshFailures) {
600
+ receipt.blockers.push({ code, message: `${sourceId}:${diagnostic('sourceRefreshFailed').message}` });
601
+ }
602
+ for (const root of demand.roots ?? []) {
603
+ if (!receipt.requestedRoots.includes(root.pluginId) || preciseCleanup)
604
+ continue;
605
+ receipt.blockers.push(...root.blockers.map((code) => ({ ...diagnostic(code), message: `${root.pluginId}:${diagnostic(code).message}` })));
606
+ }
607
+ for (const pluginId of receipt.requestedRoots) {
608
+ const root = demand.roots?.find((entry) => entry.pluginId === pluginId);
609
+ const agents = receipt.selection.agents;
610
+ for (const agent of agents) {
611
+ if (options.exactTargets
612
+ && !options.exactTargets.some((target) => target.pluginId === pluginId && target.agent === agent))
613
+ continue;
614
+ const resolvedDelivery = root?.plugin ? resolveDelivery(root.plugin.manifest, agent, options.scope) ?? null : null;
615
+ const delivery = preciseCleanup ? isPortableAgent(agent)
616
+ ? { kind: 'agent-skills', provider: 'aw', agentId: agent }
617
+ : { kind: 'native', platform: agent } : resolvedDelivery;
618
+ receipt.targetResults.push({ pluginId, agent, scope: options.scope, delivery, execution: 'not-run', observation: 'unknown',
619
+ blockers: [...(preciseCleanup ? [] : root?.blockers ?? []).map(diagnostic),
620
+ ...(refreshFailures.has(parsePluginId(pluginId).sourceId)
621
+ ? [diagnostic(refreshFailures.get(parsePluginId(pluginId).sourceId))] : []),
622
+ ...(!delivery ? [diagnostic(preciseCleanup ? 'backend-action-unsupported' : 'deliveryUnsupported')] : [])],
623
+ requiredBy: [pluginId] });
624
+ }
625
+ }
626
+ if (plan.blockExecution)
627
+ return { ...plan, receipt: completeReceipt(receipt) };
628
+ if (sourceRefreshes.length > 0) {
629
+ for (const refresh of sourceRefreshes) {
630
+ plan.changes.push(`刷新 Catalog 来源 ${refresh.sourceId},确认后激活已检查的候选 generation。`);
631
+ if (!refresh.impact.inventoryComplete) {
632
+ plan.warnings.push(`${refresh.sourceId}:来源影响清单不完整,需要明确确认。`);
633
+ }
634
+ else if (refresh.impact.affectedReferences.length > 0) {
635
+ plan.warnings.push(`${refresh.sourceId}:来源更新会影响 ${refresh.impact.affectedReferences.length} 个已知引用,需要明确确认。`);
636
+ }
637
+ }
638
+ return { ...plan, receipt: completeReceipt(receipt) };
639
+ }
640
+ if (command === 'cleanup' || command === 'uninstall') {
641
+ for (const platform of ['codex', 'claude']) {
642
+ const candidates = receipt.targetResults.filter((target) => target.agent === agentForPlatform(platform));
643
+ if (candidates.length === 0)
644
+ continue;
645
+ try {
646
+ const preview = await nativeOperation(plan, candidates.map((target) => target.pluginId), platform, true, preparationRuntime, 'cleanup');
647
+ for (const target of candidates) {
648
+ if (target.delivery?.kind === 'native')
649
+ continue;
650
+ const cleanupEvidence = nativeCleanupEvidence(preview, target.pluginId, platform, options.scope);
651
+ if (cleanupEvidence.length === 0)
652
+ continue;
653
+ target.delivery = { kind: 'native', platform };
654
+ plan.historicalNativeTargets.add(`${target.pluginId}\0${platform}`);
655
+ target.blockers = target.blockers.filter((entry) => entry.code !== 'backend-action-unsupported'
656
+ && entry.code !== 'deliveryUnsupported');
657
+ }
658
+ }
659
+ catch (error) {
660
+ options.signal?.throwIfAborted();
661
+ if (error instanceof Error && error.name === 'AbortError')
662
+ throw error;
663
+ for (const target of candidates.filter((entry) => entry.delivery?.kind === 'native')) {
664
+ block(target, errorCode(error, 'nativePreparationFailed'));
665
+ }
666
+ }
667
+ }
668
+ }
669
+ if (command !== 'cleanup' && command !== 'uninstall') {
670
+ for (const platform of ['codex', 'claude']) {
671
+ const candidates = receipt.targetResults.filter((target) => target.blockers.length === 0
672
+ && target.delivery?.kind === 'agent-skills' && target.agent === agentForPlatform(platform));
673
+ if (candidates.length === 0)
674
+ continue;
675
+ try {
676
+ const pluginIds = candidates.map((target) => target.pluginId);
677
+ const preview = await nativeOperation(plan, pluginIds, platform, true, preparationRuntime, 'cleanup');
678
+ const cleanPluginIds = [];
679
+ for (const target of candidates) {
680
+ const evidence = nativeCleanupEvidence(preview, target.pluginId, platform, options.scope);
681
+ if (evidence.length === 0) {
682
+ if (preview.overallState !== 'succeeded' || preview.warnings.length > 0
683
+ || !preview.operationPlan) {
684
+ block(target, 'deliverySwitchObservationFailed');
685
+ target.recoveryActions = [`检查 ${target.pluginId}/${target.agent} 的 Native 现场后重试 aw plugin sync。`];
686
+ }
687
+ else {
688
+ cleanPluginIds.push(target.pluginId);
689
+ }
690
+ continue;
691
+ }
692
+ block(target, 'deliverySwitchCleanupRequired');
693
+ target.backend = { previousDelivery: { kind: 'native', platform }, cleanupEvidence: evidence };
694
+ target.recoveryActions = [
695
+ `aw plugin cleanup ${target.pluginId} --scope ${options.scope} --target ${target.agent}`,
696
+ `aw plugin sync ${target.pluginId} --context ${options.scope} --target ${target.agent}`,
697
+ ];
698
+ plan.changes.push(`先清理 ${target.pluginId}/${target.agent} 的旧 Native 输出,再恢复 Portable。`);
699
+ }
700
+ if (cleanPluginIds.length && preview.operationPlan)
701
+ plan.deliverySwitchChecks.push({
702
+ pluginIds, cleanPluginIds, platform, fingerprint: preview.operationPlan.planFingerprint,
703
+ });
704
+ }
705
+ catch (error) {
706
+ options.signal?.throwIfAborted();
707
+ if (error instanceof Error && error.name === 'AbortError')
708
+ throw error;
709
+ for (const target of candidates) {
710
+ block(target, 'deliverySwitchObservationFailed');
711
+ target.recoveryActions = [`检查 ${target.pluginId}/${target.agent} 的 Native 现场后重试 aw plugin sync。`];
712
+ plan.warnings.push(`${target.pluginId}/${target.agent}:${errorCode(error, 'deliverySwitchObservationFailed')}。`);
713
+ }
714
+ }
715
+ }
716
+ }
717
+ const groups = new Map();
718
+ for (const target of receipt.targetResults) {
719
+ if (target.blockers.length || target.delivery?.kind !== 'native')
720
+ continue;
721
+ const command = plan.historicalNativeTargets.has(`${target.pluginId}\0${target.delivery.platform}`)
722
+ ? 'cleanup' : plan.command;
723
+ const key = `${parsePluginId(target.pluginId).sourceId}\0${command}`;
724
+ const group = groups.get(key) ?? { command, targets: [] };
725
+ group.targets.push({ pluginId: target.pluginId, platform: target.delivery.platform });
726
+ groups.set(key, group);
727
+ }
728
+ for (const pending of groups.values()) {
729
+ const group = { ...nativeTargetSelection(pending.targets), command: pending.command };
730
+ if (group.targets.length === 0)
731
+ continue;
732
+ try {
733
+ const preview = await nativeOperation(plan, group.pluginIds, group.platform, true, preparationRuntime, group.command, group.targets);
734
+ options.signal?.throwIfAborted();
735
+ const nativeGroup = { ...group, preview };
736
+ plan.nativeGroups.push(nativeGroup);
737
+ nativeResults(plan, nativeGroup, preview, false);
738
+ plan.changes.push(...(preview.operationPlan?.changes ?? []));
739
+ const nativeToolChanges = preview.operationPlan?.actions.flatMap((action) => action.nativeToolChanges ?? []) ?? [];
740
+ if (nativeToolChanges.length)
741
+ receipt.nativeToolChanges = [...(receipt.nativeToolChanges ?? []), ...nativeToolChanges];
742
+ for (const action of preview.operationPlan?.actions ?? []) {
743
+ for (const change of action.nativeToolChanges ?? []) {
744
+ for (const [verb, closure] of [['撤销', change.before], ['使用', change.after]]) {
745
+ if (closure)
746
+ plan.changes.push(...describeNativeToolClosure(closure).map((description) => `${verb}原生工具 · ${change.producer.sourceId}/${change.producer.pluginName} · ${change.producer.key} · ${description}`));
747
+ }
748
+ }
749
+ }
750
+ for (const override of preview.dependencyPlan?.scopeOverrides ?? []) {
751
+ if (options.dependencyScopeOverrides?.some((requested) => requested.pluginId === override.pluginId
752
+ && (!requested.platform || requested.platform === override.platform))) {
753
+ plan.changes.push(`推导的依赖提供位置:${override.pluginId}#${override.platform}=${override.scope}。`);
754
+ }
755
+ }
756
+ plan.warnings.push(...preview.warnings);
757
+ }
758
+ catch (error) {
759
+ options.signal?.throwIfAborted();
760
+ if (error instanceof Error && error.name === 'AbortError')
761
+ throw error;
762
+ for (const { pluginId, platform } of group.targets) {
763
+ block(pair(plan, pluginId, agentForPlatform(platform)), errorCode(error, 'nativePreparationFailed'));
764
+ }
765
+ }
766
+ }
767
+ for (const pluginId of receipt.requestedRoots) {
768
+ const agents = receipt.targetResults.filter((target) => target.pluginId === pluginId
769
+ && !target.blockers.length && target.delivery?.kind === 'agent-skills').map((target) => target.agent).filter(isPortableAgent);
770
+ if (!agents.length)
771
+ continue;
772
+ try {
773
+ const prepared = command === 'cleanup' || command === 'uninstall'
774
+ ? await preparePortableCleanup({ scope: options.scope, context, selector: pluginId, agents, force: options.force, signal: options.signal })
775
+ : await preparePortableRecovery({ scope: options.scope, context, selector: pluginId, agents,
776
+ env: options.env, cliVersion: options.cliVersion, signal: options.signal,
777
+ catalogOperationContext: demand.catalogOperationContext, excludedPluginIds: options.excludedPluginIds });
778
+ plan.portableGroups.push(prepared);
779
+ }
780
+ catch (error) {
781
+ options.signal?.throwIfAborted();
782
+ if (error instanceof Error && error.name === 'AbortError')
783
+ throw error;
784
+ for (const agent of agents)
785
+ block(pair(plan, pluginId, agent), errorCode(error, 'portablePreparationFailed'));
786
+ }
787
+ }
788
+ for (const prepared of plan.portableGroups)
789
+ portableResults(plan, prepared);
790
+ preparePortablePreApplyAuthorization(plan);
791
+ if (command !== 'cleanup' && command !== 'uninstall') {
792
+ // Complete scope demand protects copies contributed by unselected roots and their dependencies.
793
+ const desiredAgents = new Set([...receipt.selection.agents,
794
+ ...(demand.preferences.state === 'valid' ? demand.preferences.value.targets : [])]);
795
+ const retainedPortableTargets = [];
796
+ for (const root of demand.roots ?? []) {
797
+ const portableAgents = [...desiredAgents].filter(isPortableAgent).filter((agent) => root.plugin
798
+ && resolveDelivery(root.plugin.manifest, agent, options.scope)?.kind === 'agent-skills');
799
+ if (!portableAgents.length)
800
+ continue;
801
+ try {
802
+ const prepared = plan.portableGroups.find((group) => group.options.selector === root.pluginId
803
+ && portableAgents.every((agent) => group.options.agents.some((item) => item === agent)))
804
+ ?? await preparePortableRecovery({ scope: options.scope, context, selector: root.pluginId,
805
+ env: options.env, agents: portableAgents, cliVersion: options.cliVersion, signal: options.signal,
806
+ catalogOperationContext: demand.catalogOperationContext, excludedPluginIds: options.excludedPluginIds });
807
+ if (!prepared.globalBlockers.length) {
808
+ retainedPortableTargets.push(...prepared.targets.filter((target) => !target.blocker && ['satisfied', 'drifted'].includes(target.observation)));
809
+ if (!plan.portableGroups.includes(prepared))
810
+ plan.portableProtectionGroups.push(prepared);
811
+ }
812
+ }
813
+ catch (error) {
814
+ options.signal?.throwIfAborted();
815
+ if (error instanceof Error && error.name === 'AbortError')
816
+ throw error;
817
+ // Missing current Portable evidence cannot protect historical residue.
818
+ }
819
+ }
820
+ }
821
+ options.signal?.throwIfAborted();
822
+ await checkFootprints(plan);
823
+ options.signal?.throwIfAborted();
824
+ for (const prepared of [...plan.portableGroups, ...plan.portableProtectionGroups]) {
825
+ delete prepared.options.signal;
826
+ }
827
+ plan.plannedImpact.outputs = !plan.blockExecution && (plan.nativeGroups.some((group) => (group.preview.operationPlan?.actions.length ?? 0) > 0
828
+ && nativeGroupTargets(group).some(({ pluginId, platform }) => pair(plan, pluginId, agentForPlatform(platform))?.blockers.length === 0))
829
+ || (plan.portableGroups.some((group) => group.targets.some((target) => pair(plan, group.options.selector, target.agent)?.blockers.length === 0
830
+ && (group.mode === 'cleanup' || target.observation === 'absent' || target.observation === 'drifted'
831
+ || (command === 'reinstall' && target.observation === 'satisfied' && target.ownership === 'aw'))))));
832
+ completeReceipt(receipt);
833
+ return plan;
834
+ }
835
+ catch (error) {
836
+ await context.dispose();
837
+ throw error;
838
+ }
839
+ }
840
+ export async function activateRecoverySourcesAndReprepare(plan, execution = {}) {
841
+ if (plan.sourceRefreshes.length === 0)
842
+ return plan;
843
+ const runtime = { ...plan.runtime, ...execution.runtime };
844
+ const failures = new Map(plan.sourceRefreshFailures.map(({ sourceId, code }) => [sourceId, code]));
845
+ try {
846
+ for (const refresh of plan.sourceRefreshes) {
847
+ try {
848
+ await activateCatalogSourceGeneration({
849
+ sourceId: refresh.sourceId,
850
+ cliVersion: plan.options.cliVersion,
851
+ cwd: plan.options.cwd,
852
+ env: plan.options.env,
853
+ impactConfirmed: true,
854
+ expectedImpactDigest: refresh.impactDigest,
855
+ expectedCandidate: refresh.candidate,
856
+ expectedActive: refresh.previousActive,
857
+ expectedConfigDigest: refresh.configDigest,
858
+ runtime: runtime.catalogGenerationRuntime,
859
+ });
860
+ }
861
+ catch (error) {
862
+ const code = errorCode(error, 'catalogGenerationRefreshFailed');
863
+ failures.set(refresh.sourceId, code.startsWith('catalog') ? code : 'catalogGenerationRefreshFailed');
864
+ }
865
+ }
866
+ const failedSourceIds = new Set(failures.keys());
867
+ const originalExactTargets = plan.options.exactTargets
868
+ ?? plan.receipt.targetResults.map(({ pluginId, agent }) => ({ pluginId, agent }));
869
+ const exactTargets = failures.size > 0
870
+ ? originalExactTargets.filter((target) => !failedSourceIds.has(parsePluginId(target.pluginId).sourceId))
871
+ : plan.options.exactTargets;
872
+ const next = await prepareRecovery(plan.command, plan.receipt.requestedRoots, {
873
+ ...plan.options,
874
+ all: false,
875
+ refresh: false,
876
+ ...(exactTargets ? { exactTargets } : {}),
877
+ }, runtime);
878
+ if (failures.size === 0)
879
+ return next;
880
+ if (exactTargets?.length === 0) {
881
+ next.receipt.blockers = next.receipt.blockers.filter((entry) => entry.code !== 'invalidExactTargets');
882
+ next.blockExecution = true;
883
+ }
884
+ for (const [sourceId, code] of failures) {
885
+ next.receipt.blockers.push({ code, message: `${sourceId}:${diagnostic('sourceRefreshFailed').message}` });
886
+ }
887
+ for (const target of plan.receipt.targetResults) {
888
+ const sourceId = parsePluginId(target.pluginId).sourceId;
889
+ const code = failures.get(sourceId);
890
+ if (!code || next.receipt.targetResults.some((entry) => entry.pluginId === target.pluginId && entry.agent === target.agent))
891
+ continue;
892
+ const failed = structuredClone(target);
893
+ block(failed, code);
894
+ next.receipt.targetResults.push(failed);
895
+ }
896
+ next.receipt = completeReceipt(next.receipt);
897
+ return next;
898
+ }
899
+ finally {
900
+ await plan.context.dispose();
901
+ }
902
+ }
903
+ export async function executeScopedRecovery(plan, execution = {}) {
904
+ if (plan.sourceRefreshes.length > 0) {
905
+ const receipt = structuredClone(plan.receipt);
906
+ receipt.blockers.push(diagnostic('sourceRefreshConfirmationRequired'));
907
+ await plan.context.dispose();
908
+ return completeReceipt(receipt);
909
+ }
910
+ const receipt = structuredClone(plan.receipt);
911
+ plan = { ...plan, receipt, options: { ...plan.options, yes: execution.yes ?? plan.options.yes },
912
+ runtime: { ...plan.runtime, ...execution.runtime } };
913
+ try {
914
+ if (receipt.nativeToolChanges?.length && plan.options.yes !== true) {
915
+ receipt.blockers.push(diagnostic('nativeToolAuthorizationRequired'));
916
+ return completeReceipt(receipt);
917
+ }
918
+ const freshContext = await createScopeContext({ scope: plan.options.scope,
919
+ cwd: plan.context.callerCwd, env: plan.options.env });
920
+ const environmentStable = freshContext.awHome === plan.context.awHome
921
+ && freshContext.generationRoot === plan.context.generationRoot
922
+ && freshContext.projectKey === plan.context.projectKey
923
+ && JSON.stringify(freshContext.targetHomes) === JSON.stringify(plan.context.targetHomes);
924
+ await freshContext.dispose();
925
+ const executionCwdStable = plan.options.scope !== 'user'
926
+ || await assertNeutralAncestorChain(plan.context.executionCwd)
927
+ .then((value) => value === plan.context.executionCwd, () => false);
928
+ if (!environmentStable || !executionCwdStable) {
929
+ receipt.blockers.push(diagnostic('operationReviewStale'));
930
+ return completeReceipt(receipt);
931
+ }
932
+ return await withAwHomeMutationLock({ env: plan.options.env, token: plan.runtime.awHomeMutationToken,
933
+ action: async (token) => {
934
+ const actualIntent = await observeIntent(plan.options);
935
+ const current = await readDemand({ ...plan.options, ...(plan.declaration || plan.options.demandOverride ? { intent: plan.demand.intent } : {}) });
936
+ if (intentEvidence(actualIntent) !== plan.originalIntent || preferenceEvidence(current) !== plan.preferencesEvidence
937
+ || current.catalogOperationContext?.generationSetDigest !== plan.demand.catalogOperationContext?.generationSetDigest) {
938
+ receipt.blockers.push(diagnostic('operationReviewStale'));
939
+ return completeReceipt(receipt);
940
+ }
941
+ const previewManager = plan.nativeGroups.map((group) => group.preview.codexManagerPreview)
942
+ .find((entry) => entry?.state === 'system' || entry?.state === 'cached');
943
+ const session = plan.runtime.codexObservationSession ?? (plan.nativeGroups.length ? await createCodexNativeObservationSession({
944
+ exactCwd: plan.context.executionCwd, env: plan.context.env,
945
+ runner: plan.runtime.nativeCliRunner ?? getDefaultNativeCliRunner(),
946
+ manager: plan.runtime.codexManager ?? (previewManager?.state === 'system' || previewManager?.state === 'cached'
947
+ ? previewManager.manager : undefined),
948
+ }) : undefined);
949
+ const nativeRuntime = { ...plan.runtime, awHomeMutationToken: token,
950
+ managedCommandProducerCache: plan.runtime.managedCommandProducerCache
951
+ ?? new Map(),
952
+ ...(session ? { codexObservationSession: session } : {}) };
953
+ try {
954
+ // Freeze every backend before the first declaration or output write.
955
+ for (const check of plan.deliverySwitchChecks) {
956
+ const checked = await nativeOperation(plan, check.pluginIds, check.platform, true, nativeRuntime, 'cleanup')
957
+ .catch(() => undefined);
958
+ if (!checked || checked.overallState !== 'succeeded' || checked.warnings.length > 0
959
+ || checked.operationPlan?.planFingerprint !== check.fingerprint
960
+ || check.cleanPluginIds.some((pluginId) => nativeCleanupEvidence(checked, pluginId, check.platform, plan.options.scope).length > 0)) {
961
+ receipt.blockers.push(diagnostic('operationReviewStale'));
962
+ return completeReceipt(receipt);
963
+ }
964
+ }
965
+ const executableNativeGroups = [];
966
+ for (const group of plan.nativeGroups) {
967
+ const targets = nativeGroupTargets(group);
968
+ const executable = targets.filter(({ pluginId, platform }) => !pair(plan, pluginId, agentForPlatform(platform)).blockers.length);
969
+ if (executable.length === 0)
970
+ continue;
971
+ const checked = await nativeOperation(plan, group.pluginIds, group.platform, true, nativeRuntime, group.command, targets);
972
+ if (checked.operationPlan?.planFingerprint !== group.preview.operationPlan?.planFingerprint) {
973
+ receipt.blockers.push(diagnostic('operationReviewStale'));
974
+ return completeReceipt(receipt);
975
+ }
976
+ const selection = nativeTargetSelection(executable);
977
+ const preview = executable.length === targets.length ? checked
978
+ : await nativeOperation(plan, selection.pluginIds, selection.platform, true, nativeRuntime, group.command, executable);
979
+ executableNativeGroups.push({ ...group, ...selection, preview });
980
+ }
981
+ for (const prepared of plan.portableGroups) {
982
+ if (prepared.options.agents.every((agent) => pair(plan, prepared.options.selector, agent).blockers.length))
983
+ continue;
984
+ const checked = await inspectPortableRecovery(prepared.options);
985
+ if (checked.evidenceDigest !== prepared.evidenceDigest) {
986
+ receipt.blockers.push(diagnostic('operationReviewStale'));
987
+ return completeReceipt(receipt);
988
+ }
989
+ }
990
+ for (const prepared of plan.portableProtectionGroups) {
991
+ const checked = await inspectPortableRecovery(prepared.options);
992
+ if (checked.evidenceDigest !== prepared.evidenceDigest) {
993
+ receipt.blockers.push(diagnostic('operationReviewStale'));
994
+ return completeReceipt(receipt);
995
+ }
996
+ }
997
+ for (const residue of plan.portableResidues) {
998
+ const checked = await inspectPortableResidue({ scope: plan.options.scope, context: plan.context,
999
+ selector: residue.pluginId, env: plan.options.env, agents: residue.agents.filter(isPortableAgent),
1000
+ retainedTargets: residue.retainedTargets }).catch(() => undefined);
1001
+ if (!checked || JSON.stringify(checked) !== JSON.stringify(residue.observation)) {
1002
+ receipt.blockers.push(diagnostic('operationReviewStale'));
1003
+ return completeReceipt(receipt);
1004
+ }
1005
+ }
1006
+ if (plan.declaration) {
1007
+ const declared = await addRequirements({ ...plan.options, selectors: plan.declaration, token });
1008
+ receipt.declarationChanged = declared.changed;
1009
+ receipt.blockers.push(...declared.errors);
1010
+ plan.blockExecution ||= declared.errors.length > 0;
1011
+ const intent = await observeIntent(plan.options);
1012
+ receipt.intent = { ...receipt.intent, state: intent.state, ...('digest' in intent ? { digest: intent.digest } : {}) };
1013
+ }
1014
+ if (plan.blockExecution)
1015
+ return completeReceipt(receipt);
1016
+ if (plan.preferences) {
1017
+ try {
1018
+ const saved = await writePreferences({ ...plan.options, token }, plan.preferences);
1019
+ receipt.preferencesChanged = saved.changed;
1020
+ }
1021
+ catch (error) {
1022
+ receipt.blockers.push(diagnostic(errorCode(error, 'deploymentPreferencesWriteFailed')));
1023
+ return completeReceipt(receipt);
1024
+ }
1025
+ }
1026
+ for (const group of executableNativeGroups) {
1027
+ try {
1028
+ const result = await nativeOperation(plan, group.pluginIds, group.platform, false, { ...nativeRuntime,
1029
+ ...(group.preview.operationPlan
1030
+ ? { expectedPlanFingerprint: group.preview.operationPlan.planFingerprint } : {}) }, group.command, nativeGroupTargets(group));
1031
+ receipt.outputsMayHaveChanged ||= result.rebuildResults.some((result) => result.actions.some((action) => action.name === 'stale.cleanup' || action.name === 'native.write' || action.name === 'artifacts.write'
1032
+ || action.name === 'recovery.cleanup' || action.name === 'cleanup.owned'))
1033
+ || ((result.commandResults?.length ?? 0) > 0
1034
+ && result.operationPlan?.actions.some((action) => action.kind === 'syncManagedCommandPackage') === true);
1035
+ nativeResults(plan, group, result, true);
1036
+ }
1037
+ catch (error) {
1038
+ const code = errorCode(error, 'nativeRecoveryFailed');
1039
+ receipt.outputsMayHaveChanged ||= code !== 'preApplyAuthorizationRequired';
1040
+ for (const { pluginId, platform } of nativeGroupTargets(group)) {
1041
+ const target = pair(plan, pluginId, agentForPlatform(platform));
1042
+ target.execution = 'failed';
1043
+ block(target, code);
1044
+ }
1045
+ }
1046
+ }
1047
+ const completedFootprints = [];
1048
+ const failedFootprints = [];
1049
+ for (const prepared of plan.portableGroups) {
1050
+ const agents = [];
1051
+ for (const agent of prepared.options.agents) {
1052
+ if (pair(plan, prepared.options.selector, agent).blockers.length)
1053
+ continue;
1054
+ const outcomes = prepared.targets.filter((entry) => entry.agent === agent).map((entry) => {
1055
+ const failed = failedFootprints.find((failure) => failure.logicalIdentity === prepared.logicalIdentity
1056
+ && failure.outcome.physicalPath === entry.physicalPath && failure.outcome.expectedDigest === entry.expectedDigest
1057
+ && failure.outcome.providerPluginId === entry.providerPluginId);
1058
+ return failed ? { ...failed.outcome, agent } : { ...entry, execution: 'not-run', blockers: [] };
1059
+ });
1060
+ if (outcomes.some((entry) => entry.execution === 'failed')) {
1061
+ portableOutcomes(plan, prepared, agent, outcomes, []);
1062
+ continue;
1063
+ }
1064
+ const preApplyFailure = await executePortablePreApply(plan, prepared, agent, nativeRuntime);
1065
+ if (preApplyFailure) {
1066
+ const target = pair(plan, prepared.options.selector, agent);
1067
+ target.execution = 'failed';
1068
+ block(target, preApplyFailure);
1069
+ target.recoveryActions = [`aw plugin sync ${target.pluginId} --context ${target.scope} --target ${agent} --yes`];
1070
+ continue;
1071
+ }
1072
+ agents.push(agent);
1073
+ }
1074
+ if (!agents.length)
1075
+ continue;
1076
+ const result = await executePortableRecovery({ prepared, token, authorizeUpdate: plan.options.yes,
1077
+ action: plan.command === 'install' ? 'sync' : plan.command, force: plan.options.force,
1078
+ targets: prepared.targets.filter((target) => agents.includes(target.agent)).map(({ skill, agent }) => ({ skill, agent })),
1079
+ completedFootprints });
1080
+ receipt.outputsMayHaveChanged ||= result.outputsMayHaveChanged;
1081
+ for (const agent of agents) {
1082
+ portableOutcomes(plan, prepared, agent, result.targets.filter((entry) => entry.agent === agent), result.globalBlockers);
1083
+ }
1084
+ failedFootprints.push(...result.targets.filter((target) => target.execution === 'failed')
1085
+ .map((outcome) => ({ logicalIdentity: prepared.logicalIdentity, outcome })));
1086
+ completedFootprints.push(...result.targets.filter((target) => target.observation === 'satisfied'
1087
+ && ['applied', 'no-op'].includes(target.execution) && target.blockers.length === 0)
1088
+ .map(({ physicalPath, expectedDigest, providerPluginId }) => ({ physicalPath, expectedDigest, providerPluginId,
1089
+ logicalIdentity: prepared.logicalIdentity })));
1090
+ }
1091
+ for (const { kind, pluginId, observation } of plan.portableResidues) {
1092
+ if (kind !== 'cleanup')
1093
+ continue;
1094
+ if (observation.state === 'absent')
1095
+ continue;
1096
+ const targets = receipt.targetResults.filter((target) => target.pluginId === pluginId
1097
+ && (observation.targets.some((entry) => entry.agent === target.agent)
1098
+ || (observation.unknownAgents?.some((item) => item === target.agent)
1099
+ ?? (observation.blockers.length > 0))));
1100
+ for (const target of targets) {
1101
+ block(target, isPortableAgent(target.agent) && observation.unknownAgents?.some((item) => item === target.agent)
1102
+ || !observation.targets.some((entry) => entry.agent === target.agent)
1103
+ ? 'portableObservationFailed' : 'backend-action-unsupported');
1104
+ if (target.execution === 'applied')
1105
+ target.execution = 'partial';
1106
+ target.backend = { ...(target.delivery?.kind === 'native' ? { native: target.backend } : { portable: target.backend }), portableRemaining: {
1107
+ state: observation.state, targets: observation.targets.map(({ skill, agent, physicalPath }) => ({ skill, agent, physicalPath: displayPortablePath(plan, physicalPath) })),
1108
+ } };
1109
+ target.recoveryActions = [...new Set([...(target.recoveryActions ?? []), ...portableRecoveryActions(plan, pluginId, observation.targets.filter((entry) => entry.agent === target.agent))])];
1110
+ }
1111
+ }
1112
+ if (receipt.declarationChanged) {
1113
+ const intent = await observeIntent(plan.options);
1114
+ receipt.intent = { state: intent.state, ...('digest' in intent ? { digest: intent.digest } : {}),
1115
+ roots: intent.state === 'valid' ? intent.value.roots.map((root) => root.pluginId) : null };
1116
+ }
1117
+ return completeReceipt(receipt);
1118
+ }
1119
+ finally {
1120
+ if (!plan.runtime.codexObservationSession)
1121
+ await session?.dispose();
1122
+ }
1123
+ } });
1124
+ }
1125
+ finally {
1126
+ await plan.context.dispose();
1127
+ }
1128
+ }
1129
+ export async function runRecovery(command, selectors, options, runtime = {}) {
1130
+ let plan = await prepareRecovery(command, selectors, options, runtime);
1131
+ if (plan.sourceRefreshes.length > 0) {
1132
+ if (options.yes !== true) {
1133
+ if (!options.interactive || !options.prompt?.confirm) {
1134
+ plan.receipt.blockers.push(diagnostic('sourceRefreshConfirmationRequired'));
1135
+ await plan.context.dispose();
1136
+ return completeReceipt(plan.receipt);
1137
+ }
1138
+ const response = await options.prompt.confirm({ defaultValue: false, message: [
1139
+ `先更新来源:${plan.sourceRefreshes.map((entry) => entry.sourceId).join('、')}。`,
1140
+ ...plan.changes,
1141
+ ...plan.warnings,
1142
+ '确认后只激活已检查的来源候选;随后会基于新的 Active 重新准备插件恢复,并再次确认。',
1143
+ ].join('\n') });
1144
+ if (response.cancelled || response.value !== true) {
1145
+ plan.receipt.blockers.push(diagnostic('operationCancelled'));
1146
+ await plan.context.dispose();
1147
+ return completeReceipt(plan.receipt);
1148
+ }
1149
+ }
1150
+ plan = await activateRecoverySourcesAndReprepare(plan, { runtime });
1151
+ }
1152
+ if (options.interactive && options.yes !== true && options.prompt?.confirm
1153
+ && (plan.declaration || plan.receipt.targetResults.some((target) => target.blockers.length === 0))) {
1154
+ const response = await options.prompt.confirm({ defaultValue: false, message: [
1155
+ `${options.scope} 请求:${plan.receipt.requestedRoots.join('、')};Agent:${plan.receipt.selection.agents.join('、') || '未选择'}。`,
1156
+ ...plan.receipt.targetResults.map((target) => `${target.pluginId} / ${target.agent}:${target.blockers.length
1157
+ ? `受阻(${target.blockers.map((entry) => entry.code).join('、')})` : '可执行'}`),
1158
+ ...plan.changes, ...plan.warnings, ...plan.receipt.blockers.map((entry) => entry.message),
1159
+ '是否执行以上可处理变更及计划中的 preApply 脚本?',
1160
+ ].join('\n') });
1161
+ if (response.cancelled || response.value !== true) {
1162
+ plan.receipt.blockers.push(diagnostic('operationCancelled'));
1163
+ await plan.context.dispose();
1164
+ return completeReceipt(plan.receipt);
1165
+ }
1166
+ return executeRecovery(plan, { yes: true, runtime: { preApplyConfirmed: true } });
1167
+ }
1168
+ return executeRecovery(plan);
1169
+ }
1170
+ export async function prepareRecovery(command, selectors, options, runtime = {}) {
1171
+ if (command === 'cleanup') {
1172
+ if (!options.scope)
1173
+ throw new Error('cleanupScopeRequired');
1174
+ return prepareScopedRecovery(command, selectors, { ...options, scope: options.scope }, runtime);
1175
+ }
1176
+ return prepareJointRecovery(command, selectors, options, runtime);
1177
+ }
1178
+ export async function executeRecovery(plan, execution = {}) {
1179
+ return plan.joint ? executeJointRecovery(plan, execution) : executeScopedRecovery(plan, execution);
1180
+ }
1181
+ /** Review evidence uses existing backend fingerprints and observation digests; it is not stored state. */
1182
+ export function describeRecoveryPlan(plan) {
1183
+ const describe = (child) => ({ scope: child.options.scope, targets: child.receipt.requestedTargets,
1184
+ native: child.nativeGroups.map((group) => group.preview.operationPlan?.planFingerprint),
1185
+ preApplyAuthorization: child.preApplyAuthorization,
1186
+ nativeActivationOnlyTargets: child.options.nativeActivationOnlyTargets,
1187
+ nativePlannedRetirements: child.options.nativePlannedRetirements,
1188
+ portable: child.portableGroups.map((group) => group.evidenceDigest),
1189
+ protectedPortable: child.portableProtectionGroups.map((group) => group.evidenceDigest),
1190
+ residue: child.portableResidues, blockers: child.receipt.blockers,
1191
+ targetBlockers: child.receipt.targetResults.map((target) => target.blockers),
1192
+ environment: { homes: child.context.targetHomes, projectRoot: child.context.projectRoot, awHome: child.context.awHome },
1193
+ });
1194
+ return { inputs: plan.joint?.evidence ?? [plan.originalIntent, plan.preferencesEvidence],
1195
+ command: plan.command, declaration: plan.joint?.declaration, packages: plan.runtime.configApplyPackages ? { targets: plan.runtime.configApplyPackages.targets, evidence: plan.runtime.configApplyPackages.evidence } : undefined, blockers: plan.receipt.blockers,
1196
+ children: (plan.joint?.children ?? [plan]).map(describe) };
1197
+ }
1198
+ //# sourceMappingURL=coordinator.js.map