@lesliechan721/aw 0.1.0-beta.10 → 0.1.0-beta.12

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 (227) hide show
  1. package/dist/authoring/assets/aw-config.d.mts +1 -50
  2. package/dist/authoring/assets/aw-config.mjs +58 -928
  3. package/dist/authoring/assets/aw-config.mjs.map +1 -1
  4. package/dist/authoring/scripts/init-plugin.mjs +28 -33
  5. package/dist/authoring/scripts/init-plugin.mjs.map +1 -1
  6. package/dist/authoring/scripts/lib/plugin-version-check.d.mts +1 -0
  7. package/dist/authoring/scripts/lib/plugin-version-check.mjs +229 -8
  8. package/dist/authoring/scripts/lib/plugin-version-check.mjs.map +1 -1
  9. package/dist/authoring/scripts/validate-plugin.mjs +159 -48
  10. package/dist/authoring/scripts/validate-plugin.mjs.map +1 -1
  11. package/dist/scripts/bundle-plugins.js +18 -4
  12. package/dist/scripts/bundle-plugins.js.map +1 -1
  13. package/dist/scripts/test-plugin-scripts.d.ts +2 -0
  14. package/dist/scripts/test-plugin-scripts.js +21 -5
  15. package/dist/scripts/test-plugin-scripts.js.map +1 -1
  16. package/dist/src/commands/aw-capabilities.d.ts +1 -1
  17. package/dist/src/commands/aw-capabilities.js +1 -1
  18. package/dist/src/commands/aw-capabilities.js.map +1 -1
  19. package/dist/src/commands/catalog-dev-source.d.ts +6 -3
  20. package/dist/src/commands/catalog-dev-source.js +96 -35
  21. package/dist/src/commands/catalog-dev-source.js.map +1 -1
  22. package/dist/src/commands/catalog-publication-inspection.d.ts +1 -1
  23. package/dist/src/commands/catalog-publication-inspection.js +54 -8
  24. package/dist/src/commands/catalog-publication-inspection.js.map +1 -1
  25. package/dist/src/commands/catalog-publication-journal.js +1 -1
  26. package/dist/src/commands/catalog-publication-journal.js.map +1 -1
  27. package/dist/src/commands/catalog-source.d.ts +19 -4
  28. package/dist/src/commands/catalog-source.js +121 -12
  29. package/dist/src/commands/catalog-source.js.map +1 -1
  30. package/dist/src/commands/catalog-store.d.ts +26 -7
  31. package/dist/src/commands/catalog-store.js +361 -42
  32. package/dist/src/commands/catalog-store.js.map +1 -1
  33. package/dist/src/commands/console.js +28 -17
  34. package/dist/src/commands/console.js.map +1 -1
  35. package/dist/src/commands/operation-plan.d.ts +2 -1
  36. package/dist/src/commands/operation-plan.js +24 -0
  37. package/dist/src/commands/operation-plan.js.map +1 -1
  38. package/dist/src/commands/operation-planner.d.ts +2 -2
  39. package/dist/src/commands/operation-planner.js +22 -20
  40. package/dist/src/commands/operation-planner.js.map +1 -1
  41. package/dist/src/commands/options.d.ts +2 -2
  42. package/dist/src/commands/options.js +18 -4
  43. package/dist/src/commands/options.js.map +1 -1
  44. package/dist/src/commands/plugin-capabilities.js +2 -2
  45. package/dist/src/commands/plugin-capabilities.js.map +1 -1
  46. package/dist/src/commands/rebuild-command.d.ts +19 -0
  47. package/dist/src/commands/rebuild-command.js +828 -283
  48. package/dist/src/commands/rebuild-command.js.map +1 -1
  49. package/dist/src/commands/rebuild-integration.d.ts +25 -47
  50. package/dist/src/commands/rebuild-integration.js +1176 -1016
  51. package/dist/src/commands/rebuild-integration.js.map +1 -1
  52. package/dist/src/commands/rebuild-operations.d.ts +50 -12
  53. package/dist/src/commands/rebuild-operations.js +207 -101
  54. package/dist/src/commands/rebuild-operations.js.map +1 -1
  55. package/dist/src/commands/status-v6.d.ts +21 -2
  56. package/dist/src/commands/status-v6.js +163 -31
  57. package/dist/src/commands/status-v6.js.map +1 -1
  58. package/dist/src/commands/sync-command.d.ts +17 -0
  59. package/dist/src/commands/sync-command.js +45 -37
  60. package/dist/src/commands/sync-command.js.map +1 -1
  61. package/dist/src/commands/write-commands.d.ts +5 -0
  62. package/dist/src/commands/write-commands.js +50 -3
  63. package/dist/src/commands/write-commands.js.map +1 -1
  64. package/dist/src/generators/local-plugin.js +14 -4
  65. package/dist/src/generators/local-plugin.js.map +1 -1
  66. package/dist/src/index.js +260 -91
  67. package/dist/src/index.js.map +1 -1
  68. package/dist/src/output/command-receipt.js +1 -3
  69. package/dist/src/output/command-receipt.js.map +1 -1
  70. package/dist/src/output/rebuild-progress.js +1 -3
  71. package/dist/src/output/rebuild-progress.js.map +1 -1
  72. package/dist/src/portable/materializer.js +10 -8
  73. package/dist/src/portable/materializer.js.map +1 -1
  74. package/dist/src/tui/catalog-generation.js +41 -1
  75. package/dist/src/tui/catalog-generation.js.map +1 -1
  76. package/dist/src/tui/catalog-sources/session.d.ts +1 -0
  77. package/dist/src/tui/catalog-sources/session.js +16 -3
  78. package/dist/src/tui/catalog-sources/session.js.map +1 -1
  79. package/dist/src/tui/catalog-sources/types.d.ts +3 -2
  80. package/dist/src/tui/components.d.ts +4 -0
  81. package/dist/src/tui/components.js +36 -24
  82. package/dist/src/tui/components.js.map +1 -1
  83. package/dist/src/tui/console-app-v4.d.ts +13 -4
  84. package/dist/src/tui/console-app-v4.js +2065 -857
  85. package/dist/src/tui/console-app-v4.js.map +1 -1
  86. package/dist/src/tui/controller-runtime.js +731 -62
  87. package/dist/src/tui/controller-runtime.js.map +1 -1
  88. package/dist/src/tui/controller.d.ts +156 -10
  89. package/dist/src/tui/controller.js +1187 -163
  90. package/dist/src/tui/controller.js.map +1 -1
  91. package/dist/src/tui/diagnostic-projection.d.ts +22 -0
  92. package/dist/src/tui/diagnostic-projection.js +437 -0
  93. package/dist/src/tui/diagnostic-projection.js.map +1 -0
  94. package/dist/src/tui/diagnostics.d.ts +1 -1
  95. package/dist/src/tui/diagnostics.js +7 -2
  96. package/dist/src/tui/diagnostics.js.map +1 -1
  97. package/dist/src/tui/glossary.d.ts +23 -0
  98. package/dist/src/tui/glossary.js +42 -0
  99. package/dist/src/tui/glossary.js.map +1 -0
  100. package/dist/src/tui/index.d.ts +4 -1
  101. package/dist/src/tui/index.js +4 -1
  102. package/dist/src/tui/index.js.map +1 -1
  103. package/dist/src/tui/keys.d.ts +44 -0
  104. package/dist/src/tui/keys.js +43 -15
  105. package/dist/src/tui/keys.js.map +1 -1
  106. package/dist/src/tui/lifecycle-action-view.d.ts +6 -0
  107. package/dist/src/tui/lifecycle-action-view.js +34 -0
  108. package/dist/src/tui/lifecycle-action-view.js.map +1 -0
  109. package/dist/src/tui/model.d.ts +12 -2
  110. package/dist/src/tui/model.js +128 -52
  111. package/dist/src/tui/model.js.map +1 -1
  112. package/dist/src/tui/operation-diagnostic-screens.d.ts +21 -0
  113. package/dist/src/tui/operation-diagnostic-screens.js +101 -0
  114. package/dist/src/tui/operation-diagnostic-screens.js.map +1 -0
  115. package/dist/src/tui/operation-history.d.ts +80 -0
  116. package/dist/src/tui/operation-history.js +130 -0
  117. package/dist/src/tui/operation-history.js.map +1 -0
  118. package/dist/src/tui/operation-review-planner.js +366 -99
  119. package/dist/src/tui/operation-review-planner.js.map +1 -1
  120. package/dist/src/tui/router.d.ts +82 -17
  121. package/dist/src/tui/router.js +186 -38
  122. package/dist/src/tui/router.js.map +1 -1
  123. package/dist/src/tui/screen-registry.d.ts +546 -0
  124. package/dist/src/tui/screen-registry.js +179 -0
  125. package/dist/src/tui/screen-registry.js.map +1 -0
  126. package/dist/src/tui/session-console-app.d.ts +6 -0
  127. package/dist/src/tui/session-console-app.js +13 -0
  128. package/dist/src/tui/session-console-app.js.map +1 -0
  129. package/dist/src/tui/session-host.d.ts +44 -0
  130. package/dist/src/tui/session-host.js +197 -0
  131. package/dist/src/tui/session-host.js.map +1 -0
  132. package/dist/src/types.d.ts +64 -3
  133. package/dist/src/types.js +1 -1
  134. package/dist/src/types.js.map +1 -1
  135. package/dist/src/utils/agent-version.js +1 -1
  136. package/dist/src/utils/agent-version.js.map +1 -1
  137. package/dist/src/utils/artifact-plan.d.ts +2 -2
  138. package/dist/src/utils/artifact-plan.js +8 -6
  139. package/dist/src/utils/artifact-plan.js.map +1 -1
  140. package/dist/src/utils/artifact-renderer.d.ts +2 -1
  141. package/dist/src/utils/artifact-renderer.js +139 -120
  142. package/dist/src/utils/artifact-renderer.js.map +1 -1
  143. package/dist/src/utils/artifact-request.d.ts +5 -0
  144. package/dist/src/utils/artifact-request.js +40 -0
  145. package/dist/src/utils/artifact-request.js.map +1 -1
  146. package/dist/src/utils/bundle-layout.js +4 -4
  147. package/dist/src/utils/bundle-layout.js.map +1 -1
  148. package/dist/src/utils/catalog-capability-projection.d.ts +8 -0
  149. package/dist/src/utils/catalog-capability-projection.js +14 -0
  150. package/dist/src/utils/catalog-capability-projection.js.map +1 -0
  151. package/dist/src/utils/catalog-generation-resolver.d.ts +14 -2
  152. package/dist/src/utils/catalog-generation-resolver.js +49 -27
  153. package/dist/src/utils/catalog-generation-resolver.js.map +1 -1
  154. package/dist/src/utils/catalog-generation-service.d.ts +8 -1
  155. package/dist/src/utils/catalog-generation-service.js +81 -47
  156. package/dist/src/utils/catalog-generation-service.js.map +1 -1
  157. package/dist/src/utils/catalog-generation-store.d.ts +1 -0
  158. package/dist/src/utils/catalog-generation-store.js +60 -5
  159. package/dist/src/utils/catalog-generation-store.js.map +1 -1
  160. package/dist/src/utils/catalog-operation-context.js +1 -0
  161. package/dist/src/utils/catalog-operation-context.js.map +1 -1
  162. package/dist/src/utils/catalog-public-error.js +4 -0
  163. package/dist/src/utils/catalog-public-error.js.map +1 -1
  164. package/dist/src/utils/catalog-source-impact.d.ts +3 -2
  165. package/dist/src/utils/catalog-source-impact.js +142 -20
  166. package/dist/src/utils/catalog-source-impact.js.map +1 -1
  167. package/dist/src/utils/catalog-source-references.js +95 -9
  168. package/dist/src/utils/catalog-source-references.js.map +1 -1
  169. package/dist/src/utils/catalog-sources.d.ts +1 -1
  170. package/dist/src/utils/catalog-sources.js +19 -3
  171. package/dist/src/utils/catalog-sources.js.map +1 -1
  172. package/dist/src/utils/codex-manager.js +8 -0
  173. package/dist/src/utils/codex-manager.js.map +1 -1
  174. package/dist/src/utils/codex-native-observation.d.ts +34 -1
  175. package/dist/src/utils/codex-native-observation.js +132 -0
  176. package/dist/src/utils/codex-native-observation.js.map +1 -1
  177. package/dist/src/utils/codex-reconciler.d.ts +0 -2
  178. package/dist/src/utils/codex-reconciler.js.map +1 -1
  179. package/dist/src/utils/codex-shared-native.d.ts +2 -26
  180. package/dist/src/utils/codex-shared-native.js +8 -82
  181. package/dist/src/utils/codex-shared-native.js.map +1 -1
  182. package/dist/src/utils/git-catalog-provider.d.ts +3 -0
  183. package/dist/src/utils/git-catalog-provider.js +40 -1
  184. package/dist/src/utils/git-catalog-provider.js.map +1 -1
  185. package/dist/src/utils/local-marketplace.d.ts +1 -0
  186. package/dist/src/utils/local-marketplace.js +69 -73
  187. package/dist/src/utils/local-marketplace.js.map +1 -1
  188. package/dist/src/utils/multi-source-catalog.js +1 -0
  189. package/dist/src/utils/multi-source-catalog.js.map +1 -1
  190. package/dist/src/utils/native-cli.d.ts +9 -4
  191. package/dist/src/utils/native-cli.js +280 -34
  192. package/dist/src/utils/native-cli.js.map +1 -1
  193. package/dist/src/utils/path-catalog-provider.js +2 -0
  194. package/dist/src/utils/path-catalog-provider.js.map +1 -1
  195. package/dist/src/utils/plugin-config-impact.d.ts +33 -0
  196. package/dist/src/utils/plugin-config-impact.js +298 -0
  197. package/dist/src/utils/plugin-config-impact.js.map +1 -0
  198. package/dist/src/utils/plugin-config-write.d.ts +36 -14
  199. package/dist/src/utils/plugin-config-write.js +709 -225
  200. package/dist/src/utils/plugin-config-write.js.map +1 -1
  201. package/dist/src/utils/plugin-config.d.ts +58 -4
  202. package/dist/src/utils/plugin-config.js +328 -60
  203. package/dist/src/utils/plugin-config.js.map +1 -1
  204. package/dist/src/utils/plugin-scripts.d.ts +20 -12
  205. package/dist/src/utils/plugin-scripts.js +389 -114
  206. package/dist/src/utils/plugin-scripts.js.map +1 -1
  207. package/dist/src/utils/process-tree.d.ts +2 -2
  208. package/dist/src/utils/process-tree.js +1 -1
  209. package/dist/src/utils/process-tree.js.map +1 -1
  210. package/dist/src/utils/project-boundary.d.ts +11 -1
  211. package/dist/src/utils/project-boundary.js +33 -11
  212. package/dist/src/utils/project-boundary.js.map +1 -1
  213. package/dist/src/utils/safe-operation-diagnostic.d.ts +15 -0
  214. package/dist/src/utils/safe-operation-diagnostic.js +33 -0
  215. package/dist/src/utils/safe-operation-diagnostic.js.map +1 -0
  216. package/dist/src/utils/sensitive-content.d.ts +4 -0
  217. package/dist/src/utils/sensitive-content.js +36 -0
  218. package/dist/src/utils/sensitive-content.js.map +1 -0
  219. package/dist/src/utils/windows-claude-shim.d.ts +9 -0
  220. package/dist/src/utils/windows-claude-shim.js +141 -0
  221. package/dist/src/utils/windows-claude-shim.js.map +1 -0
  222. package/dist/vitest.config.js +91 -10
  223. package/dist/vitest.config.js.map +1 -1
  224. package/package.json +9 -9
  225. package/dist/authoring/templates/aw-config-runtime.d.mts +0 -2
  226. package/dist/authoring/templates/aw-config-runtime.mjs +0 -407
  227. package/dist/authoring/templates/aw-config-runtime.mjs.map +0 -1
@@ -5,6 +5,7 @@ import { previewCodexManager, resolveCodexOperationConsumer, } from '../utils/co
5
5
  import { createNoopDebugLogger } from '../output/debug-log.js';
6
6
  import { createNoopReporter } from '../output/reporter.js';
7
7
  import { getDefaultNativeCliRunner, } from '../utils/native-cli.js';
8
+ import { createCodexNativeObservationSession } from '../utils/codex-native-observation.js';
8
9
  import { readPluginManifest } from '../utils/manifest.js';
9
10
  import { resolveAwHome, resolveCodexPaths } from '../utils/paths.js';
10
11
  import { resolveCliPackageRoot } from '../utils/paths.js';
@@ -13,8 +14,8 @@ import { analyzeCodexSurface } from '../utils/codex-surface-analysis.js';
13
14
  import { buildGeneratedName } from '../generators/shared.js';
14
15
  import { createCodexReconcilePlan, } from '../utils/codex-reconciler.js';
15
16
  import { readCodexConfigFile } from '../utils/codex-config.js';
16
- import { executeRebuildOperation, } from './rebuild-operations.js';
17
- import { assertNoClaudeUnboundNativeNameCollision, CodexActivationPostcheckError, createRebuildIntegration, executeCodexReconcileActivations, inspectCodexPluginRegistrationLive, inspectCodexSharedNativeLive, inspectNativeMarketplaceRegistrations, } from './rebuild-integration.js';
17
+ import { executeRebuildComponentCleanup, executeRebuildOperation, prepareRebuildOperation, } from './rebuild-operations.js';
18
+ import { assertNoClaudeUnboundNativeNameCollision, CodexActivationPostcheckError, createRebuildIntegration, executeCodexReconcileActivations, inspectCodexPluginRegistrationLive, inspectCodexSharedNativeLive, inspectNativeMarketplaceRegistrations, postcheckCodexReconcileActivations, } from './rebuild-integration.js';
18
19
  import { getNamespacedMarketplaceName } from '../utils/local-marketplace.js';
19
20
  import { formatPluginId, parsePluginId, planPluginSelectorSourceIds, preflightDesiredArtifactCollisions, withResolvedMultiSourceCatalog, } from '../utils/multi-source-catalog.js';
20
21
  import { calculateCodexNativeRenderPayloadDigest, calculateCodexSharedNativePayloadDigest, } from '../utils/catalog-digests.js';
@@ -32,8 +33,7 @@ import { readCatalogSourcesConfigSnapshot } from '../utils/catalog-sources.js';
32
33
  import { catalogGenerationErrorCode, isOfflineCleanupCatalogGenerationError, } from '../utils/catalog-generation-error.js';
33
34
  import { canonicalizePluginIntentState, calculatePluginIntentDigest, createEmptyPluginIntentState, mergePluginIntentRootPlatforms, readPluginIntentState, removePluginIntentRootPlatforms, writePluginIntentState, } from '../utils/plugin-intent-state.js';
34
35
  import { assertOperationPlanFingerprint, createOperationPlanConfigDigest, createOperationPlan, } from './operation-plan.js';
35
- import { validateCatalogSnapshotPluginEntriesComplete } from '../utils/catalog-snapshot.js';
36
- import { runPostApplyScript } from '../utils/plugin-scripts.js';
36
+ import { validateCatalogSnapshotComplete, validateCatalogSnapshotPluginEntriesComplete, } from '../utils/catalog-snapshot.js';
37
37
  import { AwCapabilityUnsupportedError, IMPLEMENTED_AW_CAPABILITIES, } from './aw-capabilities.js';
38
38
  import { checkAwCapabilitySupport } from '../utils/manifest.js';
39
39
  import { createDependencyPlacementPlan, } from './dependency-operation-plan.js';
@@ -86,6 +86,7 @@ function assertRebuildCatalogSourceInventoryComplete(inventory, targetScope) {
86
86
  });
87
87
  }
88
88
  const MALFORMED_CONFIG_PRESERVED_WARNING = 'pluginConfigPreserved: 目标插件的无效配置文件已保留。可稍后运行 aw config reset 清理。';
89
+ const OBSOLETE_CONTRACTS_CLEANUP_WARNING = 'obsoleteContractsCleanupFailed: Obsolete installation contracts could not be removed.';
89
90
  function omitSelectedPluginConfigErrors(inventory, targets) {
90
91
  let omitted = false;
91
92
  const errors = inventory.errors.filter((error) => {
@@ -326,9 +327,11 @@ function toTargetResult(result) {
326
327
  state: result.status === 'succeeded' ? 'completed' : 'failed',
327
328
  ...(result.status === 'failed' ? {
328
329
  message: result.error.message,
329
- errorCode: errorPrefix && /^[A-Za-z][A-Za-z0-9]*$/u.test(errorPrefix)
330
- ? errorPrefix
331
- : 'writeFailed',
330
+ errorCode: result.error.phase === 'preApply'
331
+ ? 'preApplyFailed'
332
+ : errorPrefix && /^[A-Za-z][A-Za-z0-9]*$/u.test(errorPrefix)
333
+ ? errorPrefix
334
+ : 'writeFailed',
332
335
  } : {}),
333
336
  };
334
337
  }
@@ -365,6 +368,8 @@ async function runSingleSourceRebuildCommand(options) {
365
368
  ...(options.runtime?.codexManager ? { codexManager: options.runtime.codexManager } : {}),
366
369
  expectedCatalogConfigDigest: options.runtime?.expectedCatalogConfigDigest,
367
370
  codexUserActivationDesired: options.runtime?.codexUserActivationDesired,
371
+ codexObservationSession: options.runtime?.codexObservationSession,
372
+ preApplyExecutionCache: options.runtime?.preApplyExecutionCache,
368
373
  };
369
374
  const env = runtime.env;
370
375
  const selected = selectedPlatforms(options.commandOptions.platform);
@@ -392,6 +397,7 @@ async function runSingleSourceRebuildCommand(options) {
392
397
  for (const plugin of catalogPlugins) {
393
398
  plugin.sourceId = options.runtime.namespacedSource.sourceId;
394
399
  plugin.sourceIdentity = options.runtime.namespacedSource.sourceIdentity;
400
+ plugin.catalogLogicalIdentity = options.runtime.namespacedSource.logicalIdentity;
395
401
  plugin.pluginId = formatPluginId({
396
402
  sourceId: options.runtime.namespacedSource.sourceId,
397
403
  pluginName: plugin.manifest.name,
@@ -411,7 +417,7 @@ async function runSingleSourceRebuildCommand(options) {
411
417
  }
412
418
  }
413
419
  const platforms = selected.filter((platform) => (requestedByPlatform.get(platform)?.length ?? 0) > 0);
414
- const integration = createRebuildIntegration({
420
+ const integration = options.execution?.integration ?? createRebuildIntegration({
415
421
  context: runtime,
416
422
  scriptTimeoutMs: options.commandOptions.scriptTimeoutMs,
417
423
  ...(options.execution?.prepared
@@ -421,11 +427,6 @@ async function runSingleSourceRebuildCommand(options) {
421
427
  const targets = [];
422
428
  const results = [];
423
429
  const warnings = [];
424
- const recordWarning = (error) => {
425
- const message = error instanceof Error ? error.message : String(error);
426
- if (!warnings.includes(message))
427
- warnings.push(message);
428
- };
429
430
  for (const platform of platforms) {
430
431
  for (const pluginName of requestedByPlatform.get(platform) ?? []) {
431
432
  const currentPlugin = catalogByName.get(pluginName);
@@ -497,72 +498,34 @@ async function runSingleSourceRebuildCommand(options) {
497
498
  ...options.runtime.resolvedReviewEvidenceByPlugin?.[pluginName],
498
499
  });
499
500
  }
500
- const driver = options.execution?.deferredPostApplyTasks
501
- ? {
502
- ...integration.driver,
503
- runPostApply: async (invocation) => {
504
- options.execution.deferredPostApplyTasks.push({
505
- pluginId: identity.pluginId ?? identity.pluginName,
506
- run: async () => {
507
- const startedAt = Date.now();
508
- const emit = (data) => emitRebuildActionEvent(runtime, {
509
- type: 'rebuild:action',
510
- source: {
511
- plugin: invocation.target.identity.pluginName,
512
- platform: invocation.target.identity.platform,
513
- scope: invocation.target.identity.scope,
514
- action: options.command,
515
- },
516
- data,
517
- });
518
- await emit({ name: 'postApply', phase: 'start' });
519
- try {
520
- await integration.driver.runPostApply(invocation);
521
- }
522
- catch (error) {
523
- await emit({
524
- name: 'postApply',
525
- phase: 'failure',
526
- durationMs: Math.max(0, Date.now() - startedAt),
527
- message: '运行 postApply 失败。',
528
- });
529
- throw error;
530
- }
531
- await emit({
532
- name: 'postApply',
533
- phase: 'success',
534
- durationMs: Math.max(0, Date.now() - startedAt),
535
- });
536
- },
537
- onFailure: (error) => {
538
- recordWarning(error);
539
- const action = result.actions.find((entry) => entry.name === 'postApply');
540
- if (action)
541
- action.status = 'failed';
542
- },
543
- });
544
- },
501
+ const driver = integration.driver;
502
+ let operationPreparation = options.execution?.operationPreparation;
503
+ if (options.execution?.skipPreApply
504
+ && !operationPreparation
505
+ && (options.command === 'install'
506
+ || options.command === 'sync'
507
+ || options.command === 'reinstall')) {
508
+ const prepared = await prepareRebuildOperation({
509
+ command: options.command,
510
+ target,
511
+ driver,
512
+ skipPreApply: true,
513
+ onActionEvent: (event) => emitRebuildActionEvent(runtime, event),
514
+ });
515
+ if (prepared.status === 'failed') {
516
+ results.push(prepared.result);
517
+ continue;
545
518
  }
546
- : {
547
- ...integration.driver,
548
- runPostApply: async (invocation) => {
549
- try {
550
- await integration.driver.runPostApply(invocation);
551
- }
552
- catch (error) {
553
- recordWarning(error);
554
- throw error;
555
- }
556
- },
557
- };
519
+ operationPreparation = prepared.preparation;
520
+ }
558
521
  result = await executeRebuildOperation({
559
522
  command: options.command,
560
523
  target,
561
524
  driver,
562
- onActionEvent: (event) => options.execution?.deferredPostApplyTasks
563
- && event.data.name === 'postApply'
564
- ? undefined
565
- : emitRebuildActionEvent(runtime, event),
525
+ ...(operationPreparation
526
+ ? { preparation: operationPreparation }
527
+ : {}),
528
+ onActionEvent: (event) => emitRebuildActionEvent(runtime, event),
566
529
  });
567
530
  }
568
531
  catch (error) {
@@ -631,7 +594,7 @@ function mergeRebuildCommandResults(results) {
631
594
  : {}),
632
595
  };
633
596
  }
634
- function reconcileOverallState(targetResults, plan) {
597
+ function reconcileOverallState(targetResults, plan, rebuildResults = []) {
635
598
  const failed = targetResults.some((result) => result.state === 'failed' || result.action === 'skipped');
636
599
  if (!failed)
637
600
  return 'succeeded';
@@ -642,7 +605,15 @@ function reconcileOverallState(targetResults, plan) {
642
605
  && result.action !== 'skipped'
643
606
  && result.target.pluginId !== undefined
644
607
  && requested.has(`${result.target.pluginId}\0${result.target.platform}`));
645
- return completedRequested ? 'partial' : 'failed';
608
+ const completedMutation = targetResults.some((result) => result.state === 'completed'
609
+ && result.action !== 'keep'
610
+ && result.action !== 'skipped');
611
+ const observedMutation = rebuildResults.some((result) => result.actions.some((action) => action.status === 'succeeded'
612
+ && (action.name === 'stale.cleanup'
613
+ || action.name === 'native.write'
614
+ || action.name === 'artifacts.write'
615
+ || action.name === 'cleanup.owned')));
616
+ return completedRequested || completedMutation || observedMutation ? 'partial' : 'failed';
646
617
  }
647
618
  function createRebuildOperationPlan(options) {
648
619
  const selectedRoots = [...new Map(options.dependencyPlan.targets.flatMap((target) => target.relation === 'requested'
@@ -931,8 +902,8 @@ async function cleanupOfflineOwnedComponents(options) {
931
902
  try {
932
903
  await (options.removeObsoleteInstallationContracts ?? fs.remove)(obsoleteContracts);
933
904
  }
934
- catch (error) {
935
- warnings.push(`obsoleteContractsCleanupFailed: ${error instanceof Error ? error.message : String(error)}`);
905
+ catch {
906
+ warnings.push(OBSOLETE_CONTRACTS_CLEANUP_WARNING);
936
907
  }
937
908
  return leaseSafeRebuildResult({
938
909
  platforms: [...new Set(results.map((result) => result.target.platform))],
@@ -1034,6 +1005,7 @@ async function buildForceExclusiveCleanupPlan(options) {
1034
1005
  const pluginId = formatPluginId({ sourceId, pluginName: parsed.pluginName });
1035
1006
  plugin.sourceId = sourceId;
1036
1007
  plugin.sourceIdentity = active.sourceIdentity;
1008
+ plugin.catalogLogicalIdentity = active.logicalIdentity;
1037
1009
  plugin.pluginId = pluginId;
1038
1010
  plugin.marketplaceName = sourceId === options.primarySourceId ? 'aw' : `aw-${sourceId}`;
1039
1011
  const context = {
@@ -1179,7 +1151,7 @@ async function buildLiveForceExclusiveCleanupPlan(options) {
1179
1151
  writeNativeRequired: false,
1180
1152
  artifacts: [],
1181
1153
  operationActions: [],
1182
- postApplySource: plugin,
1154
+ preApplySource: plugin,
1183
1155
  forceExclusiveCleanup: true,
1184
1156
  });
1185
1157
  evidence.push(await forceOfflineCleanupEvidence({
@@ -1228,7 +1200,7 @@ async function mergeForceCleanupPlans(...plans) {
1228
1200
  }))),
1229
1201
  };
1230
1202
  }
1231
- export async function runRebuildCommand(options) {
1203
+ async function runRebuildCommandInSession(options) {
1232
1204
  const runtimeEnv = options.runtime?.env ?? process.env;
1233
1205
  const readInventory = options.runtime?.inventoryCatalogSourceReferences
1234
1206
  ?? inventoryCatalogSourceReferences;
@@ -1338,7 +1310,7 @@ export async function runRebuildCommand(options) {
1338
1310
  }
1339
1311
  return mergeRebuildCommandResults([]);
1340
1312
  }
1341
- if (options.command === 'cleanup') {
1313
+ if (options.command === 'cleanup' && !options.all) {
1342
1314
  if (cleanupForce) {
1343
1315
  const requestedPlatforms = selectedPlatforms(options.commandOptions.platform);
1344
1316
  const cleanupSourceSnapshot = await readCatalogSourcesConfigSnapshot(runtimeEnv).catch(() => undefined);
@@ -1512,8 +1484,8 @@ export async function runRebuildCommand(options) {
1512
1484
  try {
1513
1485
  await (options.runtime?.removeObsoleteInstallationContracts ?? fs.remove)(path.join(resolveAwHome(runtimeEnv), 'installation-contracts'));
1514
1486
  }
1515
- catch (error) {
1516
- result.warnings.push(`obsoleteContractsCleanupFailed: ${error instanceof Error ? error.message : String(error)}`);
1487
+ catch {
1488
+ result.warnings.push(OBSOLETE_CONTRACTS_CLEANUP_WARNING);
1517
1489
  }
1518
1490
  }
1519
1491
  return result;
@@ -1597,13 +1569,6 @@ export async function runRebuildCommand(options) {
1597
1569
  pluginId: formatPluginId({ sourceId: source.sourceId, pluginName: entry.name }),
1598
1570
  }));
1599
1571
  });
1600
- const intentCatalogEntries = availableEntries.map((candidate) => ({
1601
- pluginId: candidate.pluginId,
1602
- dependencyPluginIds: candidate.entry.dependencies.map((dependency) => formatPluginId({
1603
- sourceId: candidate.source.sourceId,
1604
- pluginName: dependency,
1605
- })),
1606
- }));
1607
1572
  const selectedEntries = selectors.map((selector, index) => {
1608
1573
  const matches = availableEntries.filter((candidate) => candidate.entry.name === selector.pluginName
1609
1574
  && (selector.sourceId === undefined || candidate.source.sourceId === selector.sourceId));
@@ -1615,6 +1580,24 @@ export async function runRebuildCommand(options) {
1615
1580
  }
1616
1581
  return matches[0];
1617
1582
  });
1583
+ if (options.command === 'uninstall' || (options.command === 'cleanup' && options.all)) {
1584
+ await measureCommandPerformanceStage(options.runtime, 'catalog.validation', async () => {
1585
+ const selectedSources = new Map(selectedEntries.map((candidate) => [
1586
+ candidate.source.sourceId,
1587
+ candidate.source,
1588
+ ]));
1589
+ for (const source of selectedSources.values()) {
1590
+ await validateCatalogSnapshotComplete(source.rootDir);
1591
+ }
1592
+ });
1593
+ }
1594
+ const intentCatalogEntries = availableEntries.map((candidate) => ({
1595
+ pluginId: candidate.pluginId,
1596
+ dependencyPluginIds: candidate.entry.dependencies.map((dependency) => formatPluginId({
1597
+ sourceId: candidate.source.sourceId,
1598
+ pluginName: dependency,
1599
+ })),
1600
+ }));
1618
1601
  const dependencyEnabled = options.command === 'install'
1619
1602
  || options.command === 'sync'
1620
1603
  || options.command === 'reinstall';
@@ -1682,6 +1665,7 @@ export async function runRebuildCommand(options) {
1682
1665
  const plugin = await readPluginManifest(path.join(candidate.source.rootDir, 'plugins', candidate.entry.name), { mode: 'bundle' });
1683
1666
  plugin.sourceId = candidate.source.sourceId;
1684
1667
  plugin.sourceIdentity = candidate.source.sourceIdentity;
1668
+ plugin.catalogLogicalIdentity = candidate.source.logicalIdentity;
1685
1669
  plugin.pluginId = candidate.pluginId;
1686
1670
  return { source: candidate.source, plugin, pluginId: candidate.pluginId };
1687
1671
  }));
@@ -1905,12 +1889,41 @@ export async function runRebuildCommand(options) {
1905
1889
  }
1906
1890
  return marketplaceName;
1907
1891
  };
1892
+ const codexObservationScopeByMarketplace = new Map();
1893
+ const projectIntentPluginIds = new Set();
1894
+ for (const root of intentBeforeByScope.project.roots.filter((candidate) => candidate.platforms.includes('codex'))) {
1895
+ const rootEntry = availableEntries.find((candidate) => candidate.pluginId === root.pluginId);
1896
+ if (!rootEntry)
1897
+ continue;
1898
+ for (const candidate of entryClosure([rootEntry])) {
1899
+ projectIntentPluginIds.add(candidate.pluginId);
1900
+ }
1901
+ }
1902
+ const projectCodexConfig = await readCodexConfigFile(resolveCodexPaths(options.commandOptions.cwd, 'project', runtimeEnv).configTomlPath);
1903
+ for (const { entry, platform } of executionSelections) {
1904
+ if (platform !== 'codex')
1905
+ continue;
1906
+ const marketplaceName = marketplaceNameFor(entry, platform, options.commandOptions.scope);
1907
+ const codexPluginId = `${entry.plugin.manifest.name}@${marketplaceName}`;
1908
+ const requiredScope = options.commandOptions.scope === 'project'
1909
+ || projectIntentPluginIds.has(entry.pluginId)
1910
+ || Object.hasOwn(projectCodexConfig.snapshot.plugins, codexPluginId)
1911
+ ? 'both'
1912
+ : 'user';
1913
+ if (requiredScope === 'both'
1914
+ || !codexObservationScopeByMarketplace.has(marketplaceName)) {
1915
+ codexObservationScopeByMarketplace.set(marketplaceName, requiredScope);
1916
+ }
1917
+ }
1908
1918
  const executionContext = {
1909
1919
  env: runtimeEnv,
1910
1920
  nativeCliRunner: options.runtime?.nativeCliRunner ?? getDefaultNativeCliRunner(),
1911
1921
  reporter: options.runtime?.reporter ?? createNoopReporter(),
1912
1922
  debugLogger: options.runtime?.debugLogger ?? createNoopDebugLogger(),
1913
1923
  expectedCatalogConfigDigest: catalog.configDigest,
1924
+ codexObservationSession: options.runtime?.codexObservationSession,
1925
+ codexObservationScopeByMarketplace,
1926
+ preApplyExecutionCache: options.runtime?.preApplyExecutionCache,
1914
1927
  };
1915
1928
  const inspection = createRebuildIntegration({
1916
1929
  context: executionContext,
@@ -1925,6 +1938,43 @@ export async function runRebuildCommand(options) {
1925
1938
  : []),
1926
1939
  }]]
1927
1940
  : [])).values()];
1941
+ const intentPlanForPreparedRoots = (plan, preparedKeys) => {
1942
+ const retainedRootKeys = new Set(plan.targets.flatMap((target) => target.relation === 'requested'
1943
+ && preparedKeys.has(`${target.pluginId}\0${target.platform}`)
1944
+ ? [`${target.pluginId}\0${target.platform}`]
1945
+ : []));
1946
+ const closureKeys = new Set(retainedRootKeys);
1947
+ const pending = [...closureKeys];
1948
+ while (pending.length > 0) {
1949
+ const consumerKey = pending.pop();
1950
+ for (const edge of plan.dependencyEdges) {
1951
+ if (`${edge.from}\0${edge.platform}` !== consumerKey)
1952
+ continue;
1953
+ const dependencyKey = `${edge.to}\0${edge.platform}`;
1954
+ if (closureKeys.has(dependencyKey))
1955
+ continue;
1956
+ closureKeys.add(dependencyKey);
1957
+ pending.push(dependencyKey);
1958
+ }
1959
+ }
1960
+ return {
1961
+ plan: {
1962
+ ...plan,
1963
+ requestedPluginIds: plan.requestedPluginIds.filter((pluginId) => plan.targets.some((target) => target.pluginId === pluginId
1964
+ && retainedRootKeys.has(`${target.pluginId}\0${target.platform}`))),
1965
+ resolvedPluginIds: plan.resolvedPluginIds.filter((pluginId) => plan.targets.some((target) => target.pluginId === pluginId
1966
+ && closureKeys.has(`${target.pluginId}\0${target.platform}`))),
1967
+ dependencyEdges: plan.dependencyEdges.filter((edge) => closureKeys.has(`${edge.from}\0${edge.platform}`)
1968
+ && closureKeys.has(`${edge.to}\0${edge.platform}`)),
1969
+ scopeOverrides: plan.scopeOverrides.filter((override) => closureKeys.has(`${override.pluginId}\0${override.platform}`)),
1970
+ targets: plan.targets.filter((target) => closureKeys.has(`${target.pluginId}\0${target.platform}`)),
1971
+ },
1972
+ selectedRoots: selectedRootsForPlan(plan).flatMap((root) => {
1973
+ const platforms = root.platforms.filter((platform) => retainedRootKeys.has(`${root.pluginId}\0${platform}`));
1974
+ return platforms.length > 0 ? [{ ...root, platforms }] : [];
1975
+ }),
1976
+ };
1977
+ };
1928
1978
  let plannedCodexRegistrations = new Map();
1929
1979
  let plannedOwnedTextTargets = new Map();
1930
1980
  let plannedSkillMarkerTargets = new Map();
@@ -1956,7 +2006,7 @@ export async function runRebuildCommand(options) {
1956
2006
  }
1957
2007
  return output;
1958
2008
  };
1959
- const createPlan = async (inventory, codexManager) => {
2009
+ const createPlan = async (inventory, codexManager, planOptions) => {
1960
2010
  plannedCodexRegistrations = new Map();
1961
2011
  plannedOwnedTextTargets = new Map();
1962
2012
  plannedSkillMarkerTargets = new Map();
@@ -1965,6 +2015,15 @@ export async function runRebuildCommand(options) {
1965
2015
  const placementNodes = [];
1966
2016
  const blockingTargets = new Map();
1967
2017
  const actualStates = new Map();
2018
+ const loadPlacementCodexConfigs = async () => {
2019
+ const [user, project] = await Promise.all(['user', 'project'].map((scope) => readCodexConfigFile(resolveCodexPaths(options.commandOptions.cwd, scope, runtimeEnv).configTomlPath)));
2020
+ return { user, project };
2021
+ };
2022
+ let placementCodexConfigs;
2023
+ const readPlacementCodexConfigs = () => {
2024
+ placementCodexConfigs ??= loadPlacementCodexConfigs();
2025
+ return placementCodexConfigs;
2026
+ };
1968
2027
  for (const [order, { entry, platform }] of executionSelections.entries()) {
1969
2028
  const relation = selectedIds.has(entry.pluginId) ? 'requested' : 'dependency';
1970
2029
  let resolvedScope = options.commandOptions.scope;
@@ -2050,12 +2109,17 @@ export async function runRebuildCommand(options) {
2050
2109
  cwd: options.commandOptions.cwd,
2051
2110
  env: runtimeEnv,
2052
2111
  nativeCliRunner: executionContext.nativeCliRunner,
2112
+ scope: codexObservationScopeByMarketplace.get(marketplaceNameFor(entry, platform, resolvedScope)) ?? 'both',
2113
+ observationSession: executionContext.codexObservationSession,
2053
2114
  ...(codexManager ? { manager: codexManager } : {}),
2054
2115
  })
2055
2116
  : undefined;
2056
2117
  if (codexRegistration) {
2057
2118
  plannedCodexRegistrations.set(codexPluginId, codexRegistration);
2058
2119
  }
2120
+ const placementActivationConfigs = codexRegistration
2121
+ ? await readPlacementCodexConfigs()
2122
+ : undefined;
2059
2123
  const placementState = (inspected, scope) => {
2060
2124
  if (platform !== 'codex'
2061
2125
  || surface?.classification !== 'native'
@@ -2070,8 +2134,12 @@ export async function runRebuildCommand(options) {
2070
2134
  return 'partial';
2071
2135
  if (codexRegistration.observation.value === 'absent')
2072
2136
  return 'absent';
2073
- return codexRegistration.observations[scope]
2074
- && codexRegistration.activations[scope] === true
2137
+ const configEnabled = placementActivationConfigs.user
2138
+ .snapshot.plugins[codexPluginId]?.enabled === true
2139
+ || (scope === 'project'
2140
+ && placementActivationConfigs.project
2141
+ .snapshot.plugins[codexPluginId]?.enabled === true);
2142
+ return configEnabled
2075
2143
  ? inspected.state
2076
2144
  : 'partial';
2077
2145
  };
@@ -2205,12 +2273,14 @@ export async function runRebuildCommand(options) {
2205
2273
  state = resolvedInspection.state;
2206
2274
  errorCode ??= 'dependencyNotReady';
2207
2275
  }
2208
- if (entry.plugin.manifest.config?.fields
2276
+ if (!planOptions?.skipConfigValidation
2277
+ && entry.plugin.manifest.config?.fields
2209
2278
  && (action === 'install' || action === 'reinstall')) {
2210
2279
  const effective = resolvedScope === 'project'
2211
2280
  ? await resolveArtifactRenderConfig({
2212
2281
  sourceId: entry.source.sourceId,
2213
2282
  sourceIdentity: entry.source.sourceIdentity,
2283
+ logicalIdentity: entry.source.logicalIdentity,
2214
2284
  pluginName: entry.plugin.manifest.name,
2215
2285
  manifest: entry.plugin.manifest,
2216
2286
  scope: 'project',
@@ -2220,6 +2290,7 @@ export async function runRebuildCommand(options) {
2220
2290
  : await resolveEffectivePluginConfig({
2221
2291
  sourceId: entry.source.sourceId,
2222
2292
  sourceIdentity: entry.source.sourceIdentity,
2293
+ logicalIdentity: entry.source.logicalIdentity,
2223
2294
  pluginName: entry.plugin.manifest.name,
2224
2295
  manifest: entry.plugin.manifest,
2225
2296
  targetScope: 'user',
@@ -2418,15 +2489,29 @@ export async function runRebuildCommand(options) {
2418
2489
  return [...scopes].sort();
2419
2490
  };
2420
2491
  const inspectBothScopes = options.command !== 'cleanup' && options.command !== 'uninstall';
2421
- const readActivationConfig = async (scope) => {
2422
- return readCodexConfigFile(resolveCodexPaths(options.commandOptions.cwd, scope, runtimeEnv).configTomlPath);
2423
- };
2424
2492
  const [userConfig, projectConfig] = await Promise.all([
2425
- readActivationConfig('user'),
2426
- readActivationConfig('project'),
2493
+ readCodexConfigFile(resolveCodexPaths(options.commandOptions.cwd, 'user', runtimeEnv).configTomlPath),
2494
+ readCodexConfigFile(resolveCodexPaths(options.commandOptions.cwd, 'project', runtimeEnv).configTomlPath),
2427
2495
  ]);
2428
2496
  const catalogClosure = [];
2429
2497
  const liveEvidence = [];
2498
+ const desiredScopesByPluginId = new Map();
2499
+ const intentScopesByPluginId = new Map();
2500
+ for (const planned of reconciledCodexTargets) {
2501
+ const entry = available.find((candidate) => candidate.pluginId === planned.pluginId);
2502
+ const marketplaceName = marketplaceNameFor(entry, 'codex', planned.resolvedScope);
2503
+ const codexPluginId = `${entry.plugin.manifest.name}@${marketplaceName}`;
2504
+ const desiredScopes = desiredScopesForEntry(entry);
2505
+ const intentScopes = intentScopesForEntry(entry);
2506
+ desiredScopesByPluginId.set(entry.pluginId, desiredScopes);
2507
+ intentScopesByPluginId.set(entry.pluginId, intentScopes);
2508
+ if (planned.resolvedScope === 'project'
2509
+ || desiredScopes.includes('project')
2510
+ || intentScopes.includes('project')
2511
+ || Object.hasOwn(projectConfig.snapshot.plugins, codexPluginId)) {
2512
+ codexObservationScopeByMarketplace.set(marketplaceName, 'both');
2513
+ }
2514
+ }
2430
2515
  for (const planned of reconciledCodexTargets) {
2431
2516
  const entry = available.find((candidate) => candidate.pluginId === planned.pluginId);
2432
2517
  const surface = analyzeCodexSurface(entry.plugin.manifest);
@@ -2438,7 +2523,11 @@ export async function runRebuildCommand(options) {
2438
2523
  const staticContentDigest = hasNativeRenderTargets
2439
2524
  ? undefined
2440
2525
  : await calculateCodexSharedNativePayloadDigest(entry.plugin.rootDir);
2441
- const desiredScopes = desiredScopesForEntry(entry);
2526
+ const desiredScopes = desiredScopesByPluginId.get(entry.pluginId);
2527
+ const intentScopes = intentScopesByPluginId.get(entry.pluginId);
2528
+ const observationScope = codexObservationScopeByMarketplace.get(marketplaceName)
2529
+ ?? 'user';
2530
+ const plannedRegistration = plannedCodexRegistrations.get(codexPluginId);
2442
2531
  const sharedInspection = surface.nativeKinds.length > 0
2443
2532
  ? await inspectCodexSharedNativeLive({
2444
2533
  pluginName: entry.plugin.manifest.name,
@@ -2450,9 +2539,12 @@ export async function runRebuildCommand(options) {
2450
2539
  marketplaceName,
2451
2540
  marketplaceRoot: path.join(resolveAwHome(runtimeEnv), 'marketplaces', entry.source.sourceId),
2452
2541
  nativeCliRunner: executionContext.nativeCliRunner,
2542
+ observationScope,
2543
+ observationSession: executionContext.codexObservationSession,
2453
2544
  ...(codexManager ? { manager: codexManager } : {}),
2454
- ...(plannedCodexRegistrations.get(codexPluginId)
2455
- ? { pluginRegistration: plannedCodexRegistrations.get(codexPluginId) }
2545
+ ...(plannedRegistration
2546
+ && (observationScope === 'user' || plannedRegistration.observations.project)
2547
+ ? { pluginRegistration: plannedRegistration }
2456
2548
  : {}),
2457
2549
  ...(hasNativeRenderTargets ? { nativeRenderPlugin: entry.plugin } : {}),
2458
2550
  })
@@ -2469,7 +2561,7 @@ export async function runRebuildCommand(options) {
2469
2561
  relation: planned.relation,
2470
2562
  resolvedScope: planned.resolvedScope,
2471
2563
  desiredScopes,
2472
- intentScopes: intentScopesForEntry(entry),
2564
+ intentScopes,
2473
2565
  surface,
2474
2566
  });
2475
2567
  const sharedNative = sharedInspection?.sharedNative
@@ -2544,15 +2636,8 @@ export async function runRebuildCommand(options) {
2544
2636
  liveEvidence.push({
2545
2637
  codexPluginId,
2546
2638
  sharedNative,
2547
- ...(sharedInspection?.actualPayloadDigest
2548
- ? { sharedNativePayloadDigest: sharedInspection.actualPayloadDigest }
2549
- : {}),
2550
- ...(sharedInspection?.payloadPath
2551
- ? { sharedNativePayloadPath: sharedInspection.payloadPath }
2552
- : {}),
2553
2639
  activations: {
2554
- user: sharedInspection?.activations.user
2555
- ?? userConfig?.snapshot.plugins[codexPluginId]?.enabled,
2640
+ user: userConfig.snapshot.plugins[codexPluginId]?.enabled,
2556
2641
  project: projectConfig?.snapshot.plugins[codexPluginId]?.enabled,
2557
2642
  },
2558
2643
  extensions,
@@ -2664,6 +2749,7 @@ export async function runRebuildCommand(options) {
2664
2749
  const integration = createRebuildIntegration({
2665
2750
  context: executionContext,
2666
2751
  scriptTimeoutMs: options.commandOptions.scriptTimeoutMs,
2752
+ operationScopedNativePhases: true,
2667
2753
  });
2668
2754
  const preparedTargets = new Map();
2669
2755
  const preparedTargetErrors = new Map();
@@ -2689,8 +2775,10 @@ export async function runRebuildCommand(options) {
2689
2775
  const plannedNative = platform === 'codex'
2690
2776
  ? codexPlans.flatMap((codexPlan) => codexPlan.sharedNativeActions).find((action) => action.pluginId === entry.pluginId)
2691
2777
  : undefined;
2778
+ const codexNativeStillDesired = platform === 'codex'
2779
+ && codexPlans.some((codexPlan) => codexPlan.desiredTargets.some((target) => target.pluginId === entry.pluginId && target.scopes.length > 0));
2692
2780
  const preserveSharedNative = plannedNative?.action === 'reuse'
2693
- || plannedNative?.action === 'none';
2781
+ || (plannedNative?.action === 'none' && codexNativeStillDesired);
2694
2782
  const marketplaceName = marketplaceNameFor(entry, platform, planned.resolvedScope);
2695
2783
  try {
2696
2784
  const target = await measureCommandPerformanceStage(options.runtime, 'target.build', () => integration.buildTarget({
@@ -2731,10 +2819,20 @@ export async function runRebuildCommand(options) {
2731
2819
  ...codexPlan.extensionRemovals,
2732
2820
  ].some((action) => action.pluginId === entry.pluginId
2733
2821
  && action.scope === planned.resolvedScope));
2822
+ const hasActivationMutation = codexPlans.some((codexPlan) => [
2823
+ ...codexPlan.activationActions.user,
2824
+ ...codexPlan.activationActions.project,
2825
+ ].some((action) => action.pluginId === entry.pluginId
2826
+ && action.action !== 'none'));
2734
2827
  target.codexNativeObservationExpectation = preserveSharedNative && hasExtensionMutation
2735
2828
  ? (plannedNative.action === 'reuse' ? 'installed' : 'observed')
2736
2829
  : undefined;
2737
- if (!writesSharedNative) {
2830
+ const removesSharedNative = !writesSharedNative
2831
+ && !preserveSharedNative
2832
+ && (buildCommand === 'cleanup' || buildCommand === 'uninstall');
2833
+ target.codexNativeSensitivePreflightRequired = preserveSharedNative
2834
+ && hasActivationMutation;
2835
+ if (!writesSharedNative && !removesSharedNative) {
2738
2836
  const isSharedNativeComponent = (component) => component.kind === 'native-plugin'
2739
2837
  || (component.kind === 'shared-config'
2740
2838
  && component.identity.artifactKey.startsWith('marketplace-'));
@@ -2744,6 +2842,9 @@ export async function runRebuildCommand(options) {
2744
2842
  target.optionalOwnedComponents = target.optionalOwnedComponents?.filter((component) => !isSharedNativeComponent(component));
2745
2843
  target.operationActions = target.operationActions?.filter((action) => action.kind !== 'ensureNativeRegistration');
2746
2844
  }
2845
+ if (target.codexNativeSensitivePreflightRequired && !hasExtensionMutation) {
2846
+ await integration.driver.preflightMutation?.(target);
2847
+ }
2747
2848
  }
2748
2849
  preparedTargets.set(`${entry.pluginId}\0${platform}`, {
2749
2850
  target,
@@ -2793,6 +2894,7 @@ export async function runRebuildCommand(options) {
2793
2894
  const effective = await resolveEffectivePluginConfig({
2794
2895
  sourceId: entry.source.sourceId,
2795
2896
  sourceIdentity: entry.source.sourceIdentity,
2897
+ logicalIdentity: entry.source.logicalIdentity,
2796
2898
  pluginName: entry.plugin.manifest.name,
2797
2899
  manifest: entry.plugin.manifest,
2798
2900
  targetScope: scope,
@@ -2806,12 +2908,40 @@ export async function runRebuildCommand(options) {
2806
2908
  });
2807
2909
  }));
2808
2910
  return {
2911
+ integration,
2809
2912
  preparedTargets,
2810
2913
  preparedTargetErrors,
2811
2914
  targetActions: [...preparedTargets.values()].flatMap((prepared) => prepared.target.operationActions ?? []),
2812
2915
  configDigests,
2813
2916
  };
2814
2917
  };
2918
+ if (options.runtime?.resolvePreApplyAuthorization) {
2919
+ const authorizationPlan = await measureCommandPerformanceStage(options.runtime, 'dependency.plan', () => createPlan(stateInventory, undefined, { skipConfigValidation: true }));
2920
+ assertWriteInventoryComplete(stateInventory, exactOwnedTextTargets(authorizationPlan), exactSkillMarkerTargets(authorizationPlan));
2921
+ const preview = mergeRebuildCommandResults([]);
2922
+ preview.dependencyPlan = authorizationPlan;
2923
+ preview.preApplyAuthorization = executionSelections.flatMap(({ entry, platform }) => {
2924
+ const planned = authorizationPlan.targets.find((target) => target.pluginId === entry.pluginId && target.platform === platform);
2925
+ if (!planned || planned.action === 'block' || !entry.plugin.manifest.scripts?.preApply) {
2926
+ return [];
2927
+ }
2928
+ const relation = planned.relation === 'requested'
2929
+ ? 'requested'
2930
+ : planned.requiredBy.some((pluginId) => selectedIds.has(pluginId))
2931
+ ? 'direct'
2932
+ : 'transitive';
2933
+ return [{
2934
+ pluginId: entry.pluginId,
2935
+ pluginName: entry.plugin.manifest.name,
2936
+ sourceId: entry.source.sourceId,
2937
+ sourceIdentity: entry.source.sourceIdentity,
2938
+ platform,
2939
+ scope: planned.resolvedScope,
2940
+ relation,
2941
+ }];
2942
+ });
2943
+ return preview;
2944
+ }
2815
2945
  if (options.runtime?.previewOperationPlan) {
2816
2946
  await options.runtime.onPreviewStage?.('dependencyPlan');
2817
2947
  const initialPlan = await measureCommandPerformanceStage(options.runtime, 'dependency.plan', () => createPlan(stateInventory));
@@ -2882,6 +3012,7 @@ export async function runRebuildCommand(options) {
2882
3012
  : {}),
2883
3013
  sourceId: entry.source.sourceId,
2884
3014
  sourceIdentity: entry.source.sourceIdentity,
3015
+ logicalIdentity: entry.source.logicalIdentity,
2885
3016
  resolvedCommit: entry.source.resolvedCommit,
2886
3017
  manifestDigest: entry.source.manifestDigest,
2887
3018
  snapshotDigest: entry.source.snapshotDigest,
@@ -2890,64 +3021,25 @@ export async function runRebuildCommand(options) {
2890
3021
  }
2891
3022
  let lockedPlan = await measureCommandPerformanceStage(options.runtime, 'dependency.plan', () => createPlan(stateInventory));
2892
3023
  assertWriteInventoryComplete(stateInventory, exactOwnedTextTargets(lockedPlan), exactSkillMarkerTargets(lockedPlan));
3024
+ const lifecycleCommand = options.command === 'install'
3025
+ || options.command === 'sync'
3026
+ || options.command === 'reinstall';
3027
+ const preApplyRequired = lifecycleCommand && executionSelections.some(({ entry, platform }) => entry.plugin.manifest.scripts?.preApply !== undefined
3028
+ && lockedPlan.targets.some((target) => target.pluginId === entry.pluginId
3029
+ && target.platform === platform
3030
+ && target.action !== 'block'));
3031
+ if (preApplyRequired
3032
+ && options.commandOptions.yes !== true
3033
+ && options.runtime?.preApplyConfirmed !== true) {
3034
+ throw namespacedPreflightError('preApplyAuthorizationRequired: Running plugin preApply requires --yes or an accepted Review.', preflightTargets);
3035
+ }
2893
3036
  const provisionalLockedCodexPlans = await createCodexPlans(lockedPlan);
2894
3037
  const codexManagerRequired = provisionalLockedCodexPlans.some((plan) => plan.sharedNativeActions.some((action) => action.action === 'install'
2895
3038
  || action.action === 'replace'
2896
3039
  || action.action === 'repair'));
2897
- const activationOnlyOperation = !codexManagerRequired
2898
- && provisionalLockedCodexPlans.some((plan) => plan.postcheck.support === 'observed'
2899
- && plan.postcheck.plugins.length > 0);
2900
- const activationScope = provisionalLockedCodexPlans.some((plan) => plan.requirement.requiresProjectPluginContract) ? 'project' : 'user';
2901
- const activationConsumer = activationOnlyOperation
2902
- ? await (options.runtime?.codexOperationConsumerResolver ?? resolveCodexOperationConsumer)({
2903
- cwd: options.commandOptions.cwd,
2904
- env: runtimeEnv,
2905
- runner: executionContext.nativeCliRunner,
2906
- operationRequirement: getCodexOperationRequirement(activationScope),
2907
- requiresNativeMaterialization: false,
2908
- })
2909
- : undefined;
2910
- if (activationConsumer?.support === 'observed') {
2911
- executionContext.codexManager = activationConsumer.manager;
2912
- }
2913
- if (codexManagerRequired) {
2914
- const codexRequirementScope = provisionalLockedCodexPlans.some((plan) => plan.requirement.requiresProjectPluginContract)
2915
- ? 'project'
2916
- : 'user';
2917
- if (options.runtime?.codexManagerResolver) {
2918
- executionContext.codexManager = await options.runtime.codexManagerResolver({
2919
- cwd: options.commandOptions.cwd,
2920
- env: runtimeEnv,
2921
- runner: executionContext.nativeCliRunner,
2922
- authorizer: options.runtime?.managedCodexAuthorizer,
2923
- operationRequirement: getCodexOperationRequirement(codexRequirementScope),
2924
- });
2925
- }
2926
- else {
2927
- const consumer = await (options.runtime?.codexOperationConsumerResolver ?? resolveCodexOperationConsumer)({
2928
- cwd: options.commandOptions.cwd,
2929
- env: runtimeEnv,
2930
- runner: executionContext.nativeCliRunner,
2931
- authorizer: options.runtime?.managedCodexAuthorizer,
2932
- operationRequirement: getCodexOperationRequirement(codexRequirementScope),
2933
- requiresNativeMaterialization: true,
2934
- });
2935
- if (consumer.support !== 'observed') {
2936
- throw new Error('Codex native materialization requires an observed CLI consumer.');
2937
- }
2938
- executionContext.codexManager = consumer.manager;
2939
- }
2940
- }
2941
- let lockedCodexPlans = activationConsumer?.support === 'assumed'
2942
- ? await createCodexPlans(lockedPlan, 'assumed')
2943
- : provisionalLockedCodexPlans;
2944
- if (executionContext.codexManager) {
2945
- lockedPlan = await createPlan(stateInventory, executionContext.codexManager);
2946
- assertWriteInventoryComplete(stateInventory, exactOwnedTextTargets(lockedPlan), exactSkillMarkerTargets(lockedPlan));
2947
- lockedCodexPlans = await createCodexPlans(lockedPlan, 'observed', executionContext.codexManager);
2948
- }
2949
- const lockedPreparation = await prepareRenderedTargets(lockedPlan, lockedCodexPlans, true);
2950
- const lockedOperationPlan = createRebuildOperationPlan({
3040
+ let lockedCodexPlans = provisionalLockedCodexPlans;
3041
+ let lockedPreparation = await prepareRenderedTargets(lockedPlan, lockedCodexPlans, true);
3042
+ let lockedOperationPlan = createRebuildOperationPlan({
2951
3043
  command: options.command,
2952
3044
  scope: options.commandOptions.scope,
2953
3045
  dependencyPlan: lockedPlan,
@@ -2968,27 +3060,299 @@ export async function runRebuildCommand(options) {
2968
3060
  }
2969
3061
  if (options.runtime?.directCli
2970
3062
  && options.commandOptions.yes !== true
2971
- && (options.command === 'install'
2972
- || options.command === 'sync'
2973
- || options.command === 'reinstall')) {
3063
+ && lifecycleCommand) {
2974
3064
  const sharedNativeImpact = lockedCodexPlans.some((plan) => plan.review.requiresConfirmation);
2975
3065
  if (sharedNativeImpact) {
2976
3066
  throw namespacedPreflightError('sharedNativeImpactAuthorizationRequired: Replacing or force-reinstalling shared Codex native state requires --yes.', preflightTargets);
2977
3067
  }
2978
3068
  }
2979
- if (options.runtime?.awHomeMutationToken) {
2980
- const selectedRoots = selectedRootsForPlan(lockedPlan);
2981
- await writeDesiredIntentSuperset({
2982
- command: options.command,
2983
- cwd: options.commandOptions.cwd,
2984
- scope: options.commandOptions.scope,
2985
- selectedRoots,
2986
- plan: lockedPlan,
2987
- codexPlan: lockedCodexPlans[0],
2988
- preserveOtherCodexScopeRootIds,
2989
- catalogEntries: intentCatalogEntries,
2990
- env: runtimeEnv,
3069
+ const operationPreparations = new Map();
3070
+ const operationPreparationFailures = new Map();
3071
+ const executionCommandFor = (entry, platform) => {
3072
+ const planned = lockedPlan.targets.find((target) => target.pluginId === entry.pluginId && target.platform === platform);
3073
+ const desiredInCurrentScope = platform === 'codex'
3074
+ && lockedCodexPlans.some((codexPlan) => codexPlan.desiredTargets.some((target) => target.pluginId === entry.pluginId && target.scopes.includes(planned.resolvedScope)));
3075
+ if (platform === 'codex' && options.command === 'sync' && !desiredInCurrentScope) {
3076
+ return 'cleanup';
3077
+ }
3078
+ if (platform === 'codex' && options.command === 'cleanup' && desiredInCurrentScope) {
3079
+ return 'install';
3080
+ }
3081
+ if (planned.action === 'move') {
3082
+ return planned.state === 'partial' ? 'reinstall' : 'install';
3083
+ }
3084
+ if (planned.relation === 'dependency'
3085
+ && (planned.action === 'install' || planned.action === 'reinstall')) {
3086
+ return planned.action;
3087
+ }
3088
+ return options.command;
3089
+ };
3090
+ const prepareLockedTargets = async (preparationOptions) => {
3091
+ const preparations = new Map();
3092
+ const failures = new Map();
3093
+ const states = new Map();
3094
+ const integration = createRebuildIntegration({
3095
+ context: executionContext,
3096
+ scriptTimeoutMs: options.commandOptions.scriptTimeoutMs,
3097
+ preparedTargets: [...lockedPreparation.preparedTargets.values()].map((entry) => entry.target),
2991
3098
  });
3099
+ for (const { entry, platform } of executionSelections) {
3100
+ const key = `${entry.pluginId}\0${platform}`;
3101
+ if (preparationOptions.eligibleKeys
3102
+ && !preparationOptions.eligibleKeys.has(key)) {
3103
+ states.set(key, 'skipped');
3104
+ continue;
3105
+ }
3106
+ const directDependencyFailed = (entry.plugin.manifest.dependencies ?? []).some((pluginName) => {
3107
+ const dependencyPluginId = formatPluginId({
3108
+ sourceId: entry.source.sourceId,
3109
+ pluginName,
3110
+ });
3111
+ const state = states.get(`${dependencyPluginId}\0${platform}`);
3112
+ return state === 'failed' || state === 'skipped';
3113
+ });
3114
+ const planned = lockedPlan.targets.find((target) => target.pluginId === entry.pluginId && target.platform === platform);
3115
+ if (directDependencyFailed || planned.action === 'block') {
3116
+ states.set(key, 'skipped');
3117
+ continue;
3118
+ }
3119
+ const preparationError = lockedPreparation.preparedTargetErrors.get(key);
3120
+ const prepared = lockedPreparation.preparedTargets.get(key);
3121
+ if (preparationError || !prepared) {
3122
+ const message = preparationError instanceof Error
3123
+ ? preparationError.message
3124
+ : String(preparationError ?? 'Target preparation did not produce a target.');
3125
+ failures.set(key, failedResult(options.command, {
3126
+ sourceId: entry.source.sourceId,
3127
+ sourceIdentity: entry.source.sourceIdentity,
3128
+ pluginId: entry.pluginId,
3129
+ pluginName: entry.plugin.manifest.name,
3130
+ pluginVersion: entry.plugin.manifest.version,
3131
+ platform,
3132
+ scope: planned.resolvedScope,
3133
+ cwd: options.commandOptions.cwd,
3134
+ }, message));
3135
+ states.set(key, 'failed');
3136
+ continue;
3137
+ }
3138
+ const executionCommand = executionCommandFor(entry, platform);
3139
+ if (executionCommand === 'cleanup' || executionCommand === 'uninstall') {
3140
+ states.set(key, 'ready');
3141
+ continue;
3142
+ }
3143
+ const preparedOperation = await prepareRebuildOperation({
3144
+ command: executionCommand,
3145
+ target: prepared.target,
3146
+ driver: integration.driver,
3147
+ ...(preparationOptions.skipPreApply ? { skipPreApply: true } : {}),
3148
+ ...(preparationOptions.deferManagedNativeInspection
3149
+ && platform === 'codex'
3150
+ && executionContext.codexManager === undefined
3151
+ && (prepared.target.writeNativeRequired ?? prepared.target.requiresNative)
3152
+ ? { deferNativeInspection: true }
3153
+ : {}),
3154
+ onActionEvent: (event) => emitRebuildActionEvent(executionContext, event),
3155
+ });
3156
+ if (preparedOperation.status === 'failed') {
3157
+ failures.set(key, preparedOperation.result);
3158
+ states.set(key, 'failed');
3159
+ continue;
3160
+ }
3161
+ preparations.set(key, preparedOperation.preparation);
3162
+ states.set(key, 'ready');
3163
+ }
3164
+ return { preparations, failures };
3165
+ };
3166
+ if (lifecycleCommand) {
3167
+ const initialPreparation = await prepareLockedTargets({
3168
+ deferManagedNativeInspection: codexManagerRequired,
3169
+ });
3170
+ for (const [key, preparation] of initialPreparation.preparations) {
3171
+ operationPreparations.set(key, preparation);
3172
+ }
3173
+ for (const [key, failure] of initialPreparation.failures) {
3174
+ operationPreparationFailures.set(key, failure);
3175
+ }
3176
+ const nativePreparationKeys = new Set([...operationPreparations].flatMap(([key, preparation]) => preparation.target.identity.platform === 'codex'
3177
+ && (preparation.target.writeNativeRequired ?? preparation.target.requiresNative)
3178
+ ? [key]
3179
+ : []));
3180
+ const activationPluginIds = new Set(provisionalLockedCodexPlans.flatMap((plan) => plan.postcheck.plugins.map((plugin) => plugin.pluginId)));
3181
+ const activationPreparationKeys = new Set([...operationPreparations].flatMap(([key, preparation]) => preparation.target.identity.platform === 'codex'
3182
+ && preparation.target.identity.pluginId
3183
+ && activationPluginIds.has(preparation.target.identity.pluginId)
3184
+ ? [key]
3185
+ : []));
3186
+ const codexRequirementScopeFor = (keys) => {
3187
+ const pluginIds = new Set([...keys].flatMap((key) => {
3188
+ const pluginId = operationPreparations.get(key)?.target.identity.pluginId;
3189
+ return pluginId ? [pluginId] : [];
3190
+ }));
3191
+ return provisionalLockedCodexPlans.some((plan) => plan.desiredTargets.some((target) => pluginIds.has(target.pluginId) && target.scopes.includes('project')))
3192
+ ? 'project'
3193
+ : 'user';
3194
+ };
3195
+ let refreshManagedNativePlan = false;
3196
+ const failPreparedTargets = (keys, error) => {
3197
+ const message = error instanceof Error ? error.message : String(error);
3198
+ for (const key of keys) {
3199
+ const target = operationPreparations.get(key)?.target;
3200
+ if (!target)
3201
+ continue;
3202
+ operationPreparations.delete(key);
3203
+ operationPreparationFailures.set(key, failedResult(options.command, target.identity, message));
3204
+ }
3205
+ };
3206
+ if (executionContext.codexManager === undefined
3207
+ && nativePreparationKeys.size > 0) {
3208
+ try {
3209
+ if (options.runtime?.codexManagerResolver) {
3210
+ executionContext.codexManager = await options.runtime.codexManagerResolver({
3211
+ cwd: options.commandOptions.cwd,
3212
+ env: runtimeEnv,
3213
+ runner: executionContext.nativeCliRunner,
3214
+ authorizer: options.runtime?.managedCodexAuthorizer,
3215
+ operationRequirement: getCodexOperationRequirement(codexRequirementScopeFor(nativePreparationKeys)),
3216
+ });
3217
+ }
3218
+ else {
3219
+ const consumer = await (options.runtime?.codexOperationConsumerResolver ?? resolveCodexOperationConsumer)({
3220
+ cwd: options.commandOptions.cwd,
3221
+ env: runtimeEnv,
3222
+ runner: executionContext.nativeCliRunner,
3223
+ authorizer: options.runtime?.managedCodexAuthorizer,
3224
+ operationRequirement: getCodexOperationRequirement(codexRequirementScopeFor(nativePreparationKeys)),
3225
+ requiresNativeMaterialization: true,
3226
+ });
3227
+ if (consumer.support !== 'observed') {
3228
+ throw new Error('Codex native materialization requires an observed CLI consumer.');
3229
+ }
3230
+ executionContext.codexManager = consumer.manager;
3231
+ }
3232
+ refreshManagedNativePlan = true;
3233
+ }
3234
+ catch (error) {
3235
+ failPreparedTargets(nativePreparationKeys, error);
3236
+ }
3237
+ }
3238
+ else if (executionContext.codexManager === undefined
3239
+ && activationPreparationKeys.size > 0) {
3240
+ try {
3241
+ const consumer = await (options.runtime?.codexOperationConsumerResolver ?? resolveCodexOperationConsumer)({
3242
+ cwd: options.commandOptions.cwd,
3243
+ env: runtimeEnv,
3244
+ runner: executionContext.nativeCliRunner,
3245
+ operationRequirement: getCodexOperationRequirement(codexRequirementScopeFor(activationPreparationKeys)),
3246
+ requiresNativeMaterialization: false,
3247
+ });
3248
+ if (consumer.support === 'observed') {
3249
+ executionContext.codexManager = consumer.manager;
3250
+ }
3251
+ else {
3252
+ lockedCodexPlans = lockedCodexPlans.map((plan) => ({
3253
+ ...plan,
3254
+ warnings: [...new Set([
3255
+ ...plan.warnings,
3256
+ 'codexProjectActivationAssumed',
3257
+ ])],
3258
+ postcheck: { ...plan.postcheck, support: 'assumed' },
3259
+ }));
3260
+ }
3261
+ }
3262
+ catch (error) {
3263
+ failPreparedTargets(activationPreparationKeys, error);
3264
+ }
3265
+ }
3266
+ if (executionContext.codexManager) {
3267
+ executionContext.codexObservationSession?.setManager(executionContext.codexManager);
3268
+ }
3269
+ if (refreshManagedNativePlan) {
3270
+ if (executionContext.codexManager) {
3271
+ lockedPlan = await createPlan(stateInventory, executionContext.codexManager);
3272
+ assertWriteInventoryComplete(stateInventory, exactOwnedTextTargets(lockedPlan), exactSkillMarkerTargets(lockedPlan));
3273
+ }
3274
+ lockedCodexPlans = await createCodexPlans(lockedPlan, 'observed', executionContext.codexManager);
3275
+ lockedPreparation = await prepareRenderedTargets(lockedPlan, lockedCodexPlans, false);
3276
+ const eligibleKeys = new Set(operationPreparations.keys());
3277
+ const initialOperationPreparations = new Map(operationPreparations);
3278
+ const finalPreparation = await prepareLockedTargets({
3279
+ eligibleKeys,
3280
+ skipPreApply: true,
3281
+ });
3282
+ operationPreparations.clear();
3283
+ for (const [key, preparation] of finalPreparation.preparations) {
3284
+ operationPreparations.set(key, {
3285
+ ...preparation,
3286
+ actions: [
3287
+ ...(initialOperationPreparations.get(key)?.actions ?? []),
3288
+ ...preparation.actions,
3289
+ ],
3290
+ });
3291
+ }
3292
+ for (const [key, failure] of finalPreparation.failures) {
3293
+ operationPreparationFailures.set(key, failure);
3294
+ }
3295
+ }
3296
+ const eligibleTargets = [...operationPreparations.keys()].flatMap((key) => {
3297
+ const prepared = lockedPreparation.preparedTargets.get(key);
3298
+ return prepared ? [prepared.target] : [];
3299
+ });
3300
+ lockedPreparation.integration.setNativeMarketplaceEligibleTargets(eligibleTargets);
3301
+ }
3302
+ lockedOperationPlan = createRebuildOperationPlan({
3303
+ command: options.command,
3304
+ scope: options.commandOptions.scope,
3305
+ dependencyPlan: lockedPlan,
3306
+ codexPlans: lockedCodexPlans,
3307
+ targetActions: lockedPreparation.targetActions,
3308
+ catalogSources: catalog.sources.flatMap((source) => source.resolution ? [source.resolution] : []),
3309
+ intentBeforeByScope,
3310
+ configDigests: lockedPreparation.configDigests,
3311
+ catalogEntries: intentCatalogEntries,
3312
+ });
3313
+ if (options.runtime?.expectedPlanFingerprint) {
3314
+ assertOperationPlanFingerprint(options.runtime.expectedPlanFingerprint, lockedOperationPlan);
3315
+ }
3316
+ if (options.runtime?.directCli
3317
+ && options.commandOptions.yes !== true
3318
+ && lifecycleCommand
3319
+ && lockedCodexPlans.some((plan) => plan.review.requiresConfirmation)) {
3320
+ throw namespacedPreflightError('sharedNativeImpactAuthorizationRequired: Replacing or force-reinstalling shared Codex native state requires --yes.', preflightTargets);
3321
+ }
3322
+ if (options.runtime?.awHomeMutationToken) {
3323
+ if (lifecycleCommand) {
3324
+ const preparedKeys = new Set(operationPreparations.keys());
3325
+ const intentSelection = intentPlanForPreparedRoots(lockedPlan, preparedKeys);
3326
+ const intentPlan = intentSelection.plan;
3327
+ const selectedRoots = intentSelection.selectedRoots;
3328
+ if (selectedRoots.length > 0) {
3329
+ await writeDesiredIntentSuperset({
3330
+ command: options.command,
3331
+ cwd: options.commandOptions.cwd,
3332
+ scope: options.commandOptions.scope,
3333
+ selectedRoots,
3334
+ plan: intentPlan,
3335
+ codexPlan: lockedCodexPlans[0],
3336
+ preserveOtherCodexScopeRootIds,
3337
+ catalogEntries: intentCatalogEntries,
3338
+ env: runtimeEnv,
3339
+ });
3340
+ }
3341
+ }
3342
+ else {
3343
+ const selectedRoots = selectedRootsForPlan(lockedPlan);
3344
+ await writeDesiredIntentSuperset({
3345
+ command: options.command,
3346
+ cwd: options.commandOptions.cwd,
3347
+ scope: options.commandOptions.scope,
3348
+ selectedRoots,
3349
+ plan: lockedPlan,
3350
+ codexPlan: lockedCodexPlans[0],
3351
+ preserveOtherCodexScopeRootIds,
3352
+ catalogEntries: intentCatalogEntries,
3353
+ env: runtimeEnv,
3354
+ });
3355
+ }
2992
3356
  }
2993
3357
  for (const pluginName of new Set(lockedPlan.targets.flatMap((target) => target.platform === 'claude'
2994
3358
  && (target.action === 'install'
@@ -3142,7 +3506,6 @@ export async function runRebuildCommand(options) {
3142
3506
  }
3143
3507
  }
3144
3508
  const results = [];
3145
- const deferredPostApplyTasks = [];
3146
3509
  const dependencyStates = new Map();
3147
3510
  for (const { entry, platform } of executionSelections) {
3148
3511
  const planned = lockedPlan.targets.find((target) => target.pluginId === entry.pluginId && target.platform === platform);
@@ -3230,6 +3593,27 @@ export async function runRebuildCommand(options) {
3230
3593
  dependencyStates.set(`${entry.pluginId}\0${platform}`, failedDependency || failedConsumer ? 'skipped' : 'failed');
3231
3594
  continue;
3232
3595
  }
3596
+ const operationPreparationFailure = operationPreparationFailures.get(`${entry.pluginId}\0${platform}`);
3597
+ if (operationPreparationFailure) {
3598
+ const targetResult = toTargetResult(operationPreparationFailure);
3599
+ results.push({
3600
+ platforms: [platform],
3601
+ plugins: [],
3602
+ warnings: [],
3603
+ rebuildResults: [operationPreparationFailure],
3604
+ targetResults: [{ ...targetResult, action: planned.action }],
3605
+ overallState: 'failed',
3606
+ receiptTargets: [{
3607
+ pluginName: identity.pluginName,
3608
+ pluginId: identity.pluginId,
3609
+ sourceId: identity.sourceId,
3610
+ platform,
3611
+ scope: planned.resolvedScope,
3612
+ }],
3613
+ });
3614
+ dependencyStates.set(`${entry.pluginId}\0${platform}`, 'failed');
3615
+ continue;
3616
+ }
3233
3617
  if (reconcileActivationOnly) {
3234
3618
  const preparedError = preparedTargetErrors.get(`${entry.pluginId}\0${platform}`);
3235
3619
  if (preparedError) {
@@ -3255,43 +3639,55 @@ export async function runRebuildCommand(options) {
3255
3639
  dependencyStates.set(`${entry.pluginId}\0${platform}`, 'failed');
3256
3640
  continue;
3257
3641
  }
3258
- const warnings = [];
3259
- const result = {
3642
+ const actions = operationPreparations.get(`${entry.pluginId}\0${platform}`)?.actions
3643
+ ?? [];
3644
+ const preparedTarget = preparedTargets.get(`${entry.pluginId}\0${platform}`)?.target;
3645
+ let result;
3646
+ if (preparedTarget) {
3647
+ const nativeSkillKeys = new Set((entry.plugin.manifest.artifacts?.targets ?? [])
3648
+ .filter((target) => target.platform === 'codex'
3649
+ && target.target === 'codex.skill'
3650
+ && target.delivery?.mode === 'codex-native')
3651
+ .map((target) => target.skillKey));
3652
+ const marketplaceRoot = preparedTarget.identity.marketplaceRoot;
3653
+ const legacyNativeSkills = preparedTarget.ownedComponents.filter((component) => component.kind === 'skill-directory'
3654
+ && nativeSkillKeys.has(component.identity.artifactKey)
3655
+ && (!marketplaceRoot || path.relative(marketplaceRoot, component.path).startsWith('..')));
3656
+ if (legacyNativeSkills.length > 0) {
3657
+ result = await executeRebuildComponentCleanup({
3658
+ command: options.command,
3659
+ target: preparedTarget,
3660
+ driver: lockedPreparation.integration.driver,
3661
+ components: legacyNativeSkills,
3662
+ actions,
3663
+ onActionEvent: (event) => emitRebuildActionEvent(executionContext, event),
3664
+ ...(rebuildCommandTestHooks?.beforeActivationLegacyCleanupMutation ? {
3665
+ beforeMutation: rebuildCommandTestHooks.beforeActivationLegacyCleanupMutation,
3666
+ } : {}),
3667
+ ...(rebuildCommandTestHooks?.afterActivationLegacyCleanupQuarantine ? {
3668
+ afterQuarantine: rebuildCommandTestHooks.afterActivationLegacyCleanupQuarantine,
3669
+ } : {}),
3670
+ });
3671
+ }
3672
+ }
3673
+ result ??= {
3260
3674
  status: 'succeeded', command: options.command, target: identity,
3261
- actions: [{ name: 'postApply', status: 'succeeded' }],
3675
+ actions,
3262
3676
  cleanupAttempted: false, removed: [], absent: [], residual: [], quarantined: [], failures: [],
3263
3677
  };
3264
- deferredPostApplyTasks.push({
3265
- pluginId: entry.pluginId,
3266
- run: () => runPostApplyScript({
3267
- plugin: entry.plugin,
3268
- platform,
3269
- scope: planned.resolvedScope,
3270
- cwd: options.commandOptions.cwd,
3271
- context: executionContext,
3272
- artifacts: [],
3273
- components: [{
3274
- kind: 'target',
3275
- key: `${platform}:${planned.resolvedScope}`,
3276
- outcome: 'unchanged',
3277
- }],
3278
- scriptTimeoutMs: options.commandOptions.scriptTimeoutMs,
3279
- }),
3280
- onFailure: (error) => {
3281
- warnings.push(error instanceof Error ? error.message : String(error));
3282
- result.actions[0].status = 'failed';
3283
- },
3284
- });
3285
3678
  results.push({
3286
- platforms: [platform], plugins: [identity.pluginName], warnings, rebuildResults: [result],
3287
- targetResults: [{ target: identity, action: planned.action, state: 'completed' }],
3288
- overallState: 'succeeded',
3679
+ platforms: [platform],
3680
+ plugins: result.status === 'succeeded' ? [identity.pluginName] : [],
3681
+ warnings: result.failures.map((failure) => failure.message),
3682
+ rebuildResults: [result],
3683
+ targetResults: [{ ...toTargetResult(result), action: planned.action }],
3684
+ overallState: result.status === 'succeeded' ? 'succeeded' : 'failed',
3289
3685
  receiptTargets: [{
3290
3686
  pluginName: identity.pluginName, pluginId: identity.pluginId,
3291
3687
  sourceId: identity.sourceId, platform, scope: planned.resolvedScope,
3292
3688
  }],
3293
3689
  });
3294
- dependencyStates.set(`${entry.pluginId}\0${platform}`, 'completed');
3690
+ dependencyStates.set(`${entry.pluginId}\0${platform}`, result.status === 'succeeded' ? 'completed' : 'failed');
3295
3691
  continue;
3296
3692
  }
3297
3693
  if (platform === 'codex'
@@ -3318,33 +3714,39 @@ export async function runRebuildCommand(options) {
3318
3714
  && !reconcileObsolete
3319
3715
  && options.command !== 'cleanup'
3320
3716
  && options.command !== 'uninstall') {
3321
- const warnings = [];
3322
- try {
3323
- await runPostApplyScript({
3324
- plugin: entry.plugin,
3325
- platform,
3326
- scope: planned.resolvedScope,
3327
- cwd: options.commandOptions.cwd,
3328
- context: executionContext,
3329
- artifacts: [],
3330
- components: [{
3331
- kind: 'target',
3332
- key: `${platform}:${planned.resolvedScope}`,
3333
- outcome: 'unchanged',
3717
+ const preparedError = preparedTargetErrors.get(`${entry.pluginId}\0${platform}`);
3718
+ if (preparedError) {
3719
+ const message = preparedError instanceof Error
3720
+ ? preparedError.message
3721
+ : String(preparedError);
3722
+ const result = failedResult(options.command, identity, message);
3723
+ results.push({
3724
+ platforms: [platform], plugins: [], warnings: [], rebuildResults: [result],
3725
+ targetResults: [{
3726
+ target: identity,
3727
+ action: planned.action,
3728
+ state: 'failed',
3729
+ errorCode: message.split(':', 1)[0] ?? 'targetPreparationFailed',
3730
+ message,
3731
+ }],
3732
+ overallState: 'failed',
3733
+ receiptTargets: [{
3734
+ pluginName: identity.pluginName, pluginId: identity.pluginId,
3735
+ sourceId: identity.sourceId, platform, scope: planned.resolvedScope,
3334
3736
  }],
3335
- scriptTimeoutMs: options.commandOptions.scriptTimeoutMs,
3336
3737
  });
3738
+ dependencyStates.set(`${entry.pluginId}\0${platform}`, 'failed');
3739
+ continue;
3337
3740
  }
3338
- catch (error) {
3339
- warnings.push(error instanceof Error ? error.message : String(error));
3340
- }
3741
+ const actions = operationPreparations.get(`${entry.pluginId}\0${platform}`)?.actions
3742
+ ?? [];
3341
3743
  const result = {
3342
3744
  status: 'succeeded', command: options.command, target: identity,
3343
- actions: [{ name: 'postApply', status: warnings.length > 0 ? 'failed' : 'succeeded' }],
3745
+ actions,
3344
3746
  cleanupAttempted: false, removed: [], absent: [], residual: [], quarantined: [], failures: [],
3345
3747
  };
3346
3748
  results.push({
3347
- platforms: [platform], plugins: [identity.pluginName], warnings, rebuildResults: [result],
3749
+ platforms: [platform], plugins: [identity.pluginName], warnings: [], rebuildResults: [result],
3348
3750
  targetResults: [{ target: identity, action: 'keep', state: 'completed' }],
3349
3751
  overallState: 'succeeded',
3350
3752
  receiptTargets: [{
@@ -3359,6 +3761,7 @@ export async function runRebuildCommand(options) {
3359
3761
  const namespacedSource = {
3360
3762
  sourceId: entry.source.sourceId,
3361
3763
  sourceIdentity: entry.source.sourceIdentity,
3764
+ logicalIdentity: entry.source.logicalIdentity,
3362
3765
  marketplaceName,
3363
3766
  marketplaceRoot: path.join(resolveAwHome(runtimeEnv), 'marketplaces', entry.source.sourceId),
3364
3767
  };
@@ -3400,6 +3803,7 @@ export async function runRebuildCommand(options) {
3400
3803
  : {}),
3401
3804
  sourceId: entry.source.sourceId,
3402
3805
  sourceIdentity: entry.source.sourceIdentity,
3806
+ logicalIdentity: entry.source.logicalIdentity,
3403
3807
  resolvedCommit: entry.source.resolvedCommit,
3404
3808
  manifestDigest: entry.source.manifestDigest,
3405
3809
  snapshotDigest: entry.source.snapshotDigest,
@@ -3411,10 +3815,10 @@ export async function runRebuildCommand(options) {
3411
3815
  reviewCompleted: true,
3412
3816
  targetHookCompleted: true,
3413
3817
  prepared: preparedTargets.get(`${entry.pluginId}\0${platform}`),
3818
+ operationPreparation: operationPreparations.get(`${entry.pluginId}\0${platform}`),
3819
+ integration: lockedPreparation.integration,
3414
3820
  preparationError: preparedTargetErrors.get(`${entry.pluginId}\0${platform}`),
3415
- ...(platform === 'codex' && lockedCodexPlans.length > 0
3416
- ? { deferredPostApplyTasks }
3417
- : {}),
3821
+ skipPreApply: reconcileDesiredCleanup,
3418
3822
  },
3419
3823
  });
3420
3824
  if (reconcileDesiredCleanup) {
@@ -3426,14 +3830,84 @@ export async function runRebuildCommand(options) {
3426
3830
  results.push(executionResult);
3427
3831
  dependencyStates.set(`${entry.pluginId}\0${platform}`, executionResult.overallState === 'succeeded' ? 'completed' : 'failed');
3428
3832
  }
3833
+ const failCompletedCodexTarget = (options_) => {
3834
+ const resultIndex = results.findIndex((candidate) => candidate.targetResults.some((target) => target.target.pluginId === options_.identity.pluginId
3835
+ && target.target.platform === 'codex'
3836
+ && target.target.scope === options_.identity.scope));
3837
+ if (resultIndex === -1)
3838
+ return false;
3839
+ const existing = results[resultIndex];
3840
+ const phase = options_.phase ?? 'postcheck.inspect';
3841
+ const rebuildResults = existing.rebuildResults.map((rebuild) => {
3842
+ if (rebuild.target.pluginId !== options_.identity.pluginId
3843
+ || rebuild.target.platform !== 'codex'
3844
+ || rebuild.target.scope !== options_.identity.scope)
3845
+ return rebuild;
3846
+ if (!rebuild.failures.some((failure) => failure.phase === phase && failure.message === options_.message)) {
3847
+ rebuild.failures.unshift({ phase, message: options_.message });
3848
+ }
3849
+ return {
3850
+ ...rebuild,
3851
+ status: 'failed',
3852
+ actions: rebuild.actions,
3853
+ error: { phase, message: options_.message },
3854
+ };
3855
+ });
3856
+ results[resultIndex] = {
3857
+ ...existing,
3858
+ plugins: existing.plugins.filter((pluginName) => pluginName !== options_.identity.pluginName),
3859
+ rebuildResults,
3860
+ targetResults: existing.targetResults.map((target) => target.target.pluginId === options_.identity.pluginId
3861
+ && target.target.platform === 'codex'
3862
+ && target.target.scope === options_.identity.scope
3863
+ ? {
3864
+ ...target,
3865
+ target: options_.identity,
3866
+ action: options_.action ?? target.action,
3867
+ state: 'failed',
3868
+ errorCode: options_.message.split(':', 1)[0] ?? 'codexActivationPostcheckFailed',
3869
+ message: options_.message,
3870
+ ...(options_.recoveryActions
3871
+ ? { recoveryActions: options_.recoveryActions }
3872
+ : {}),
3873
+ }
3874
+ : target),
3875
+ overallState: 'failed',
3876
+ };
3877
+ dependencyStates.set(`${options_.identity.pluginId}\0codex`, 'failed');
3878
+ return true;
3879
+ };
3880
+ const activationMutationPluginIds = new Set(lockedCodexPlans.flatMap((plan) => [
3881
+ ...plan.activationActions.user,
3882
+ ...plan.activationActions.project,
3883
+ ].flatMap((action) => action.action === 'none' ? [] : [action.pluginId])));
3884
+ for (const { entry, platform } of executionSelections) {
3885
+ if (platform !== 'codex'
3886
+ || !activationMutationPluginIds.has(entry.pluginId)
3887
+ || (dependencyStates.get(`${entry.pluginId}\0${platform}`) !== 'completed'
3888
+ && dependencyStates.get(`${entry.pluginId}\0${platform}`) !== 'kept'))
3889
+ continue;
3890
+ const prepared = preparedTargets.get(`${entry.pluginId}\0${platform}`)?.target;
3891
+ if (!prepared)
3892
+ continue;
3893
+ try {
3894
+ await lockedPreparation.integration.prepareNativeMarketplaceGroup(prepared);
3895
+ }
3896
+ catch (error) {
3897
+ const message = error instanceof Error ? error.message : String(error);
3898
+ failCompletedCodexTarget({ identity: prepared.identity, message, phase: 'native.write' });
3899
+ }
3900
+ }
3429
3901
  const successfulCodexPluginIds = new Set(executionSelections.flatMap(({ entry, platform }) => platform === 'codex'
3430
3902
  && (dependencyStates.get(`${entry.pluginId}\0${platform}`) === 'completed'
3431
3903
  || dependencyStates.get(`${entry.pluginId}\0${platform}`) === 'kept')
3432
3904
  ? [entry.pluginId]
3433
3905
  : []));
3434
3906
  const activationPlans = selectCodexActivationPlans(lockedCodexPlans, successfulCodexPluginIds);
3435
- if (activationPlans.length > 0 || deferredPostApplyTasks.length > 0) {
3436
- let failedActivationPluginIds = new Set();
3907
+ if (activationPlans.length > 0
3908
+ || lockedPreparation.integration.driver.completePostchecks) {
3909
+ let activationError;
3910
+ const activationMutationMarketplaces = new Set();
3437
3911
  try {
3438
3912
  if (activationPlans.length > 0) {
3439
3913
  await executeCodexReconcileActivations({
@@ -3442,23 +3916,67 @@ export async function runRebuildCommand(options) {
3442
3916
  env: runtimeEnv,
3443
3917
  manager: executionContext.codexManager,
3444
3918
  runner: executionContext.nativeCliRunner,
3919
+ observationSession: executionContext.codexObservationSession,
3920
+ preflightCompleted: true,
3921
+ mutationMarketplaces: activationMutationMarketplaces,
3922
+ observationScopeByMarketplace: codexObservationScopeByMarketplace,
3445
3923
  });
3446
3924
  }
3447
3925
  }
3448
3926
  catch (error) {
3449
- failedActivationPluginIds = failedCodexActivationPluginIds({
3450
- error,
3927
+ activationError = error;
3928
+ }
3929
+ try {
3930
+ await lockedPreparation.integration.captureCodexPostMutationSnapshots([...activationMutationMarketplaces]);
3931
+ }
3932
+ catch (error) {
3933
+ activationError ??= error;
3934
+ }
3935
+ const failedCodexPluginIds = new Set(executionSelections.flatMap(({ entry, platform }) => platform === 'codex'
3936
+ && (dependencyStates.get(`${entry.pluginId}\0${platform}`) === 'failed'
3937
+ || dependencyStates.get(`${entry.pluginId}\0${platform}`) === 'skipped')
3938
+ ? [entry.pluginId]
3939
+ : []));
3940
+ for (const failure of await lockedPreparation.integration.driver.completePostchecks?.() ?? []) {
3941
+ const message = failure.error instanceof Error
3942
+ ? failure.error.message
3943
+ : String(failure.error);
3944
+ if (failCompletedCodexTarget({ identity: failure.target.identity, message })) {
3945
+ failedCodexPluginIds.add(failure.target.identity.pluginId ?? failure.target.identity.pluginName);
3946
+ }
3947
+ }
3948
+ if (!activationError && activationPlans.length > 0) {
3949
+ const postcheckPluginIds = new Set([...successfulCodexPluginIds]
3950
+ .filter((pluginId) => !failedCodexPluginIds.has(pluginId)));
3951
+ const postcheckPlans = selectCodexActivationPlans(activationPlans, postcheckPluginIds);
3952
+ try {
3953
+ await postcheckCodexReconcileActivations({
3954
+ plans: postcheckPlans,
3955
+ cwd: options.commandOptions.cwd,
3956
+ env: runtimeEnv,
3957
+ manager: executionContext.codexManager,
3958
+ runner: executionContext.nativeCliRunner,
3959
+ observationScopeByMarketplace: codexObservationScopeByMarketplace,
3960
+ observationSession: executionContext.codexObservationSession,
3961
+ });
3962
+ }
3963
+ catch (error) {
3964
+ activationError = error;
3965
+ }
3966
+ }
3967
+ if (activationError) {
3968
+ const failedActivationPluginIds = failedCodexActivationPluginIds({
3969
+ error: activationError,
3451
3970
  plans: activationPlans,
3452
3971
  successfulPluginIds: successfulCodexPluginIds,
3453
3972
  });
3454
- for (const pluginId of failedActivationPluginIds) {
3455
- dependencyStates.set(`${pluginId}\0codex`, 'failed');
3456
- }
3457
- const message = error instanceof Error ? error.message : String(error);
3973
+ const message = activationError instanceof Error
3974
+ ? activationError.message
3975
+ : String(activationError);
3458
3976
  const failedSelections = executionSelections.filter((candidate) => candidate.platform === 'codex'
3459
3977
  && failedActivationPluginIds.has(candidate.entry.pluginId));
3460
3978
  if (failedSelections.length === 0)
3461
- throw error;
3979
+ throw activationError;
3462
3980
  for (const selection of failedSelections) {
3463
3981
  const planned = lockedPlan.targets.find((target) => target.pluginId === selection.entry.pluginId && target.platform === 'codex');
3464
3982
  const identity = {
@@ -3471,44 +3989,48 @@ export async function runRebuildCommand(options) {
3471
3989
  scope: planned.resolvedScope,
3472
3990
  cwd: options.commandOptions.cwd,
3473
3991
  };
3474
- const resultIndex = results.findIndex((candidate) => candidate.targetResults.some((target) => target.target.pluginId === identity.pluginId
3475
- && target.target.platform === 'codex'
3476
- && target.target.scope === identity.scope));
3477
- if (resultIndex === -1)
3478
- throw error;
3479
- const existing = results[resultIndex];
3480
- const failed = failedResult(options.command, identity, message, 'postcheck.inspect');
3481
- results[resultIndex] = {
3482
- ...existing,
3483
- plugins: existing.plugins.filter((pluginName) => pluginName !== identity.pluginName),
3484
- rebuildResults: existing.rebuildResults.map((rebuild) => rebuild.target.pluginId === identity.pluginId
3485
- && rebuild.target.platform === 'codex'
3486
- && rebuild.target.scope === identity.scope
3487
- ? failed
3488
- : rebuild),
3489
- targetResults: existing.targetResults.map((target) => target.target.pluginId === identity.pluginId
3490
- && target.target.platform === 'codex'
3491
- && target.target.scope === identity.scope
3492
- ? {
3493
- target: identity,
3494
- action: target.action,
3495
- state: 'failed',
3496
- errorCode: message.split(':', 1)[0] ?? 'codexActivationPostcheckFailed',
3497
- message,
3498
- }
3499
- : target),
3500
- overallState: 'failed',
3501
- };
3992
+ if (!failCompletedCodexTarget({ identity, message }))
3993
+ throw activationError;
3994
+ failedCodexPluginIds.add(selection.entry.pluginId);
3502
3995
  }
3503
3996
  }
3504
- for (const task of deferredPostApplyTasks) {
3505
- if (failedActivationPluginIds.has(task.pluginId))
3506
- continue;
3507
- try {
3508
- await task.run();
3509
- }
3510
- catch (error) {
3511
- task.onFailure(error);
3997
+ let propagated = true;
3998
+ while (propagated) {
3999
+ propagated = false;
4000
+ for (const selection of executionSelections.filter((candidate) => candidate.platform === 'codex')) {
4001
+ if (failedCodexPluginIds.has(selection.entry.pluginId))
4002
+ continue;
4003
+ const failedDependency = (selection.entry.plugin.manifest.dependencies ?? [])
4004
+ .map((pluginName) => formatPluginId({
4005
+ sourceId: selection.entry.source.sourceId,
4006
+ pluginName,
4007
+ }))
4008
+ .find((pluginId) => failedCodexPluginIds.has(pluginId));
4009
+ if (!failedDependency)
4010
+ continue;
4011
+ const planned = lockedPlan.targets.find((target) => target.pluginId === selection.entry.pluginId && target.platform === 'codex');
4012
+ const identity = {
4013
+ sourceId: selection.entry.source.sourceId,
4014
+ sourceIdentity: selection.entry.source.sourceIdentity,
4015
+ pluginId: selection.entry.pluginId,
4016
+ pluginName: selection.entry.plugin.manifest.name,
4017
+ pluginVersion: selection.entry.plugin.manifest.version,
4018
+ platform: 'codex',
4019
+ scope: planned.resolvedScope,
4020
+ cwd: options.commandOptions.cwd,
4021
+ };
4022
+ const message = `dependencyInstallFailed: ${selection.entry.pluginId} requires failed dependency ${failedDependency}.`;
4023
+ if (failCompletedCodexTarget({
4024
+ identity,
4025
+ message,
4026
+ recoveryActions: [
4027
+ `run aw sync --all --platform codex --scope ${planned.resolvedScope}`,
4028
+ ],
4029
+ })) {
4030
+ failedCodexPluginIds.add(selection.entry.pluginId);
4031
+ dependencyStates.set(`${selection.entry.pluginId}\0codex`, 'failed');
4032
+ propagated = true;
4033
+ }
3512
4034
  }
3513
4035
  }
3514
4036
  }
@@ -3524,8 +4046,9 @@ export async function runRebuildCommand(options) {
3524
4046
  return {
3525
4047
  ...target,
3526
4048
  recoveryActions: [
3527
- `run aw cleanup ${planned.pluginId} --platform ${planned.platform} --scope ${planned.preservedScope}`,
3528
- `then run aw sync --all --platform ${planned.platform} --scope ${planned.resolvedScope}`,
4049
+ `remove every direct and indirect dependent of ${planned.pluginId} in consumer-first order; if dependencyStillRequired is reported, handle the other reported dependents first`,
4050
+ `then run aw cleanup ${planned.pluginId} --platform ${planned.platform} --scope ${planned.preservedScope}`,
4051
+ `then reinstall every removed root at its original platform and scope, selecting --dependency-scope ${planned.pluginId}#${planned.platform}=${planned.resolvedScope} during each install`,
3529
4052
  ],
3530
4053
  };
3531
4054
  });
@@ -3561,6 +4084,7 @@ export async function runRebuildCommand(options) {
3561
4084
  namespacedSource: {
3562
4085
  sourceId: entry.source.sourceId,
3563
4086
  sourceIdentity: entry.source.sourceIdentity,
4087
+ logicalIdentity: entry.source.logicalIdentity,
3564
4088
  marketplaceName,
3565
4089
  marketplaceRoot: path.join(resolveAwHome(runtimeEnv), 'marketplaces', entry.source.sourceId),
3566
4090
  },
@@ -3585,7 +4109,7 @@ export async function runRebuildCommand(options) {
3585
4109
  merged.warnings.push(MALFORMED_CONFIG_PRESERVED_WARNING);
3586
4110
  }
3587
4111
  merged.dependencyPlan = lockedPlan;
3588
- merged.overallState = reconcileOverallState(merged.targetResults, lockedPlan);
4112
+ merged.overallState = reconcileOverallState(merged.targetResults, lockedPlan, merged.rebuildResults);
3589
4113
  if (lockedCodexPlans.length > 0)
3590
4114
  merged.codexReconcilePlans = lockedCodexPlans;
3591
4115
  merged.operationPlan = lockedOperationPlan;
@@ -3596,8 +4120,8 @@ export async function runRebuildCommand(options) {
3596
4120
  try {
3597
4121
  await (options.runtime?.removeObsoleteInstallationContracts ?? fs.remove)(path.join(resolveAwHome(runtimeEnv), 'installation-contracts'));
3598
4122
  }
3599
- catch (error) {
3600
- resolvedResult.warnings.push(`obsoleteContractsCleanupFailed: ${error instanceof Error ? error.message : String(error)}`);
4123
+ catch {
4124
+ resolvedResult.warnings.push(OBSOLETE_CONTRACTS_CLEANUP_WARNING);
3601
4125
  }
3602
4126
  }
3603
4127
  return resolvedResult;
@@ -3623,4 +4147,25 @@ export async function runRebuildCommand(options) {
3623
4147
  throw error;
3624
4148
  }
3625
4149
  }
4150
+ export async function runRebuildCommand(options) {
4151
+ if (options.runtime?.codexObservationSession) {
4152
+ return runRebuildCommandInSession(options);
4153
+ }
4154
+ const env = options.runtime?.env ?? process.env;
4155
+ const session = await createCodexNativeObservationSession({
4156
+ exactCwd: options.commandOptions.cwd,
4157
+ env,
4158
+ runner: options.runtime?.nativeCliRunner ?? getDefaultNativeCliRunner(),
4159
+ manager: options.runtime?.codexManager,
4160
+ });
4161
+ try {
4162
+ return await runRebuildCommandInSession({
4163
+ ...options,
4164
+ runtime: { ...options.runtime, codexObservationSession: session },
4165
+ });
4166
+ }
4167
+ finally {
4168
+ await session.dispose();
4169
+ }
4170
+ }
3626
4171
  //# sourceMappingURL=rebuild-command.js.map