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

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 (218) 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 +165 -45
  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 +11 -19
  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 +7 -0
  47. package/dist/src/commands/rebuild-command.js +769 -279
  48. package/dist/src/commands/rebuild-command.js.map +1 -1
  49. package/dist/src/commands/rebuild-integration.d.ts +24 -47
  50. package/dist/src/commands/rebuild-integration.js +1074 -1011
  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 +216 -85
  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 +6 -6
  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 +2067 -839
  85. package/dist/src/tui/console-app-v4.js.map +1 -1
  86. package/dist/src/tui/controller-runtime.js +585 -33
  87. package/dist/src/tui/controller-runtime.js.map +1 -1
  88. package/dist/src/tui/controller.d.ts +152 -6
  89. package/dist/src/tui/controller.js +1168 -156
  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 +6 -1
  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 +306 -86
  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-renderer.d.ts +2 -1
  138. package/dist/src/utils/artifact-renderer.js +52 -52
  139. package/dist/src/utils/artifact-renderer.js.map +1 -1
  140. package/dist/src/utils/bundle-layout.js +4 -4
  141. package/dist/src/utils/bundle-layout.js.map +1 -1
  142. package/dist/src/utils/catalog-capability-projection.d.ts +8 -0
  143. package/dist/src/utils/catalog-capability-projection.js +14 -0
  144. package/dist/src/utils/catalog-capability-projection.js.map +1 -0
  145. package/dist/src/utils/catalog-generation-resolver.d.ts +14 -2
  146. package/dist/src/utils/catalog-generation-resolver.js +49 -27
  147. package/dist/src/utils/catalog-generation-resolver.js.map +1 -1
  148. package/dist/src/utils/catalog-generation-service.d.ts +8 -1
  149. package/dist/src/utils/catalog-generation-service.js +81 -47
  150. package/dist/src/utils/catalog-generation-service.js.map +1 -1
  151. package/dist/src/utils/catalog-generation-store.d.ts +1 -0
  152. package/dist/src/utils/catalog-generation-store.js +60 -5
  153. package/dist/src/utils/catalog-generation-store.js.map +1 -1
  154. package/dist/src/utils/catalog-operation-context.js +1 -0
  155. package/dist/src/utils/catalog-operation-context.js.map +1 -1
  156. package/dist/src/utils/catalog-public-error.js +4 -0
  157. package/dist/src/utils/catalog-public-error.js.map +1 -1
  158. package/dist/src/utils/catalog-source-impact.d.ts +3 -2
  159. package/dist/src/utils/catalog-source-impact.js +142 -20
  160. package/dist/src/utils/catalog-source-impact.js.map +1 -1
  161. package/dist/src/utils/catalog-source-references.js +95 -9
  162. package/dist/src/utils/catalog-source-references.js.map +1 -1
  163. package/dist/src/utils/catalog-sources.d.ts +1 -1
  164. package/dist/src/utils/catalog-sources.js +19 -3
  165. package/dist/src/utils/catalog-sources.js.map +1 -1
  166. package/dist/src/utils/codex-manager.js +8 -0
  167. package/dist/src/utils/codex-manager.js.map +1 -1
  168. package/dist/src/utils/codex-native-observation.d.ts +34 -1
  169. package/dist/src/utils/codex-native-observation.js +132 -0
  170. package/dist/src/utils/codex-native-observation.js.map +1 -1
  171. package/dist/src/utils/codex-reconciler.d.ts +0 -2
  172. package/dist/src/utils/codex-reconciler.js.map +1 -1
  173. package/dist/src/utils/codex-shared-native.d.ts +2 -26
  174. package/dist/src/utils/codex-shared-native.js +8 -82
  175. package/dist/src/utils/codex-shared-native.js.map +1 -1
  176. package/dist/src/utils/git-catalog-provider.d.ts +3 -0
  177. package/dist/src/utils/git-catalog-provider.js +40 -1
  178. package/dist/src/utils/git-catalog-provider.js.map +1 -1
  179. package/dist/src/utils/local-marketplace.d.ts +1 -0
  180. package/dist/src/utils/local-marketplace.js +69 -73
  181. package/dist/src/utils/local-marketplace.js.map +1 -1
  182. package/dist/src/utils/multi-source-catalog.js +1 -0
  183. package/dist/src/utils/multi-source-catalog.js.map +1 -1
  184. package/dist/src/utils/native-cli.d.ts +9 -4
  185. package/dist/src/utils/native-cli.js +280 -34
  186. package/dist/src/utils/native-cli.js.map +1 -1
  187. package/dist/src/utils/path-catalog-provider.js +2 -0
  188. package/dist/src/utils/path-catalog-provider.js.map +1 -1
  189. package/dist/src/utils/plugin-config-write.d.ts +15 -13
  190. package/dist/src/utils/plugin-config-write.js +418 -198
  191. package/dist/src/utils/plugin-config-write.js.map +1 -1
  192. package/dist/src/utils/plugin-config.d.ts +48 -4
  193. package/dist/src/utils/plugin-config.js +289 -38
  194. package/dist/src/utils/plugin-config.js.map +1 -1
  195. package/dist/src/utils/plugin-scripts.d.ts +20 -12
  196. package/dist/src/utils/plugin-scripts.js +376 -109
  197. package/dist/src/utils/plugin-scripts.js.map +1 -1
  198. package/dist/src/utils/process-tree.d.ts +2 -2
  199. package/dist/src/utils/process-tree.js +1 -1
  200. package/dist/src/utils/process-tree.js.map +1 -1
  201. package/dist/src/utils/project-boundary.d.ts +11 -1
  202. package/dist/src/utils/project-boundary.js +17 -8
  203. package/dist/src/utils/project-boundary.js.map +1 -1
  204. package/dist/src/utils/safe-operation-diagnostic.d.ts +15 -0
  205. package/dist/src/utils/safe-operation-diagnostic.js +33 -0
  206. package/dist/src/utils/safe-operation-diagnostic.js.map +1 -0
  207. package/dist/src/utils/sensitive-content.d.ts +1 -0
  208. package/dist/src/utils/sensitive-content.js +15 -0
  209. package/dist/src/utils/sensitive-content.js.map +1 -0
  210. package/dist/src/utils/windows-claude-shim.d.ts +9 -0
  211. package/dist/src/utils/windows-claude-shim.js +141 -0
  212. package/dist/src/utils/windows-claude-shim.js.map +1 -0
  213. package/dist/vitest.config.js +91 -10
  214. package/dist/vitest.config.js.map +1 -1
  215. package/package.json +9 -9
  216. package/dist/authoring/templates/aw-config-runtime.d.mts +0 -2
  217. package/dist/authoring/templates/aw-config-runtime.mjs +0 -407
  218. 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,7 @@ 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,
368
372
  };
369
373
  const env = runtime.env;
370
374
  const selected = selectedPlatforms(options.commandOptions.platform);
@@ -392,6 +396,7 @@ async function runSingleSourceRebuildCommand(options) {
392
396
  for (const plugin of catalogPlugins) {
393
397
  plugin.sourceId = options.runtime.namespacedSource.sourceId;
394
398
  plugin.sourceIdentity = options.runtime.namespacedSource.sourceIdentity;
399
+ plugin.catalogLogicalIdentity = options.runtime.namespacedSource.logicalIdentity;
395
400
  plugin.pluginId = formatPluginId({
396
401
  sourceId: options.runtime.namespacedSource.sourceId,
397
402
  pluginName: plugin.manifest.name,
@@ -411,7 +416,7 @@ async function runSingleSourceRebuildCommand(options) {
411
416
  }
412
417
  }
413
418
  const platforms = selected.filter((platform) => (requestedByPlatform.get(platform)?.length ?? 0) > 0);
414
- const integration = createRebuildIntegration({
419
+ const integration = options.execution?.integration ?? createRebuildIntegration({
415
420
  context: runtime,
416
421
  scriptTimeoutMs: options.commandOptions.scriptTimeoutMs,
417
422
  ...(options.execution?.prepared
@@ -421,11 +426,6 @@ async function runSingleSourceRebuildCommand(options) {
421
426
  const targets = [];
422
427
  const results = [];
423
428
  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
429
  for (const platform of platforms) {
430
430
  for (const pluginName of requestedByPlatform.get(platform) ?? []) {
431
431
  const currentPlugin = catalogByName.get(pluginName);
@@ -497,72 +497,15 @@ async function runSingleSourceRebuildCommand(options) {
497
497
  ...options.runtime.resolvedReviewEvidenceByPlugin?.[pluginName],
498
498
  });
499
499
  }
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
- },
545
- }
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
- };
500
+ const driver = integration.driver;
558
501
  result = await executeRebuildOperation({
559
502
  command: options.command,
560
503
  target,
561
504
  driver,
562
- onActionEvent: (event) => options.execution?.deferredPostApplyTasks
563
- && event.data.name === 'postApply'
564
- ? undefined
565
- : emitRebuildActionEvent(runtime, event),
505
+ ...(options.execution?.operationPreparation
506
+ ? { preparation: options.execution.operationPreparation }
507
+ : {}),
508
+ onActionEvent: (event) => emitRebuildActionEvent(runtime, event),
566
509
  });
567
510
  }
568
511
  catch (error) {
@@ -631,7 +574,7 @@ function mergeRebuildCommandResults(results) {
631
574
  : {}),
632
575
  };
633
576
  }
634
- function reconcileOverallState(targetResults, plan) {
577
+ function reconcileOverallState(targetResults, plan, rebuildResults = []) {
635
578
  const failed = targetResults.some((result) => result.state === 'failed' || result.action === 'skipped');
636
579
  if (!failed)
637
580
  return 'succeeded';
@@ -642,7 +585,15 @@ function reconcileOverallState(targetResults, plan) {
642
585
  && result.action !== 'skipped'
643
586
  && result.target.pluginId !== undefined
644
587
  && requested.has(`${result.target.pluginId}\0${result.target.platform}`));
645
- return completedRequested ? 'partial' : 'failed';
588
+ const completedMutation = targetResults.some((result) => result.state === 'completed'
589
+ && result.action !== 'keep'
590
+ && result.action !== 'skipped');
591
+ const observedMutation = rebuildResults.some((result) => result.actions.some((action) => action.status === 'succeeded'
592
+ && (action.name === 'stale.cleanup'
593
+ || action.name === 'native.write'
594
+ || action.name === 'artifacts.write'
595
+ || action.name === 'cleanup.owned')));
596
+ return completedRequested || completedMutation || observedMutation ? 'partial' : 'failed';
646
597
  }
647
598
  function createRebuildOperationPlan(options) {
648
599
  const selectedRoots = [...new Map(options.dependencyPlan.targets.flatMap((target) => target.relation === 'requested'
@@ -931,8 +882,8 @@ async function cleanupOfflineOwnedComponents(options) {
931
882
  try {
932
883
  await (options.removeObsoleteInstallationContracts ?? fs.remove)(obsoleteContracts);
933
884
  }
934
- catch (error) {
935
- warnings.push(`obsoleteContractsCleanupFailed: ${error instanceof Error ? error.message : String(error)}`);
885
+ catch {
886
+ warnings.push(OBSOLETE_CONTRACTS_CLEANUP_WARNING);
936
887
  }
937
888
  return leaseSafeRebuildResult({
938
889
  platforms: [...new Set(results.map((result) => result.target.platform))],
@@ -1034,6 +985,7 @@ async function buildForceExclusiveCleanupPlan(options) {
1034
985
  const pluginId = formatPluginId({ sourceId, pluginName: parsed.pluginName });
1035
986
  plugin.sourceId = sourceId;
1036
987
  plugin.sourceIdentity = active.sourceIdentity;
988
+ plugin.catalogLogicalIdentity = active.logicalIdentity;
1037
989
  plugin.pluginId = pluginId;
1038
990
  plugin.marketplaceName = sourceId === options.primarySourceId ? 'aw' : `aw-${sourceId}`;
1039
991
  const context = {
@@ -1179,7 +1131,7 @@ async function buildLiveForceExclusiveCleanupPlan(options) {
1179
1131
  writeNativeRequired: false,
1180
1132
  artifacts: [],
1181
1133
  operationActions: [],
1182
- postApplySource: plugin,
1134
+ preApplySource: plugin,
1183
1135
  forceExclusiveCleanup: true,
1184
1136
  });
1185
1137
  evidence.push(await forceOfflineCleanupEvidence({
@@ -1228,7 +1180,7 @@ async function mergeForceCleanupPlans(...plans) {
1228
1180
  }))),
1229
1181
  };
1230
1182
  }
1231
- export async function runRebuildCommand(options) {
1183
+ async function runRebuildCommandInSession(options) {
1232
1184
  const runtimeEnv = options.runtime?.env ?? process.env;
1233
1185
  const readInventory = options.runtime?.inventoryCatalogSourceReferences
1234
1186
  ?? inventoryCatalogSourceReferences;
@@ -1338,7 +1290,7 @@ export async function runRebuildCommand(options) {
1338
1290
  }
1339
1291
  return mergeRebuildCommandResults([]);
1340
1292
  }
1341
- if (options.command === 'cleanup') {
1293
+ if (options.command === 'cleanup' && !options.all) {
1342
1294
  if (cleanupForce) {
1343
1295
  const requestedPlatforms = selectedPlatforms(options.commandOptions.platform);
1344
1296
  const cleanupSourceSnapshot = await readCatalogSourcesConfigSnapshot(runtimeEnv).catch(() => undefined);
@@ -1512,8 +1464,8 @@ export async function runRebuildCommand(options) {
1512
1464
  try {
1513
1465
  await (options.runtime?.removeObsoleteInstallationContracts ?? fs.remove)(path.join(resolveAwHome(runtimeEnv), 'installation-contracts'));
1514
1466
  }
1515
- catch (error) {
1516
- result.warnings.push(`obsoleteContractsCleanupFailed: ${error instanceof Error ? error.message : String(error)}`);
1467
+ catch {
1468
+ result.warnings.push(OBSOLETE_CONTRACTS_CLEANUP_WARNING);
1517
1469
  }
1518
1470
  }
1519
1471
  return result;
@@ -1597,13 +1549,6 @@ export async function runRebuildCommand(options) {
1597
1549
  pluginId: formatPluginId({ sourceId: source.sourceId, pluginName: entry.name }),
1598
1550
  }));
1599
1551
  });
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
1552
  const selectedEntries = selectors.map((selector, index) => {
1608
1553
  const matches = availableEntries.filter((candidate) => candidate.entry.name === selector.pluginName
1609
1554
  && (selector.sourceId === undefined || candidate.source.sourceId === selector.sourceId));
@@ -1615,6 +1560,24 @@ export async function runRebuildCommand(options) {
1615
1560
  }
1616
1561
  return matches[0];
1617
1562
  });
1563
+ if (options.command === 'uninstall' || (options.command === 'cleanup' && options.all)) {
1564
+ await measureCommandPerformanceStage(options.runtime, 'catalog.validation', async () => {
1565
+ const selectedSources = new Map(selectedEntries.map((candidate) => [
1566
+ candidate.source.sourceId,
1567
+ candidate.source,
1568
+ ]));
1569
+ for (const source of selectedSources.values()) {
1570
+ await validateCatalogSnapshotComplete(source.rootDir);
1571
+ }
1572
+ });
1573
+ }
1574
+ const intentCatalogEntries = availableEntries.map((candidate) => ({
1575
+ pluginId: candidate.pluginId,
1576
+ dependencyPluginIds: candidate.entry.dependencies.map((dependency) => formatPluginId({
1577
+ sourceId: candidate.source.sourceId,
1578
+ pluginName: dependency,
1579
+ })),
1580
+ }));
1618
1581
  const dependencyEnabled = options.command === 'install'
1619
1582
  || options.command === 'sync'
1620
1583
  || options.command === 'reinstall';
@@ -1682,6 +1645,7 @@ export async function runRebuildCommand(options) {
1682
1645
  const plugin = await readPluginManifest(path.join(candidate.source.rootDir, 'plugins', candidate.entry.name), { mode: 'bundle' });
1683
1646
  plugin.sourceId = candidate.source.sourceId;
1684
1647
  plugin.sourceIdentity = candidate.source.sourceIdentity;
1648
+ plugin.catalogLogicalIdentity = candidate.source.logicalIdentity;
1685
1649
  plugin.pluginId = candidate.pluginId;
1686
1650
  return { source: candidate.source, plugin, pluginId: candidate.pluginId };
1687
1651
  }));
@@ -1905,12 +1869,40 @@ export async function runRebuildCommand(options) {
1905
1869
  }
1906
1870
  return marketplaceName;
1907
1871
  };
1872
+ const codexObservationScopeByMarketplace = new Map();
1873
+ const projectIntentPluginIds = new Set();
1874
+ for (const root of intentBeforeByScope.project.roots.filter((candidate) => candidate.platforms.includes('codex'))) {
1875
+ const rootEntry = availableEntries.find((candidate) => candidate.pluginId === root.pluginId);
1876
+ if (!rootEntry)
1877
+ continue;
1878
+ for (const candidate of entryClosure([rootEntry])) {
1879
+ projectIntentPluginIds.add(candidate.pluginId);
1880
+ }
1881
+ }
1882
+ const projectCodexConfig = await readCodexConfigFile(resolveCodexPaths(options.commandOptions.cwd, 'project', runtimeEnv).configTomlPath);
1883
+ for (const { entry, platform } of executionSelections) {
1884
+ if (platform !== 'codex')
1885
+ continue;
1886
+ const marketplaceName = marketplaceNameFor(entry, platform, options.commandOptions.scope);
1887
+ const codexPluginId = `${entry.plugin.manifest.name}@${marketplaceName}`;
1888
+ const requiredScope = options.commandOptions.scope === 'project'
1889
+ || projectIntentPluginIds.has(entry.pluginId)
1890
+ || Object.hasOwn(projectCodexConfig.snapshot.plugins, codexPluginId)
1891
+ ? 'both'
1892
+ : 'user';
1893
+ if (requiredScope === 'both'
1894
+ || !codexObservationScopeByMarketplace.has(marketplaceName)) {
1895
+ codexObservationScopeByMarketplace.set(marketplaceName, requiredScope);
1896
+ }
1897
+ }
1908
1898
  const executionContext = {
1909
1899
  env: runtimeEnv,
1910
1900
  nativeCliRunner: options.runtime?.nativeCliRunner ?? getDefaultNativeCliRunner(),
1911
1901
  reporter: options.runtime?.reporter ?? createNoopReporter(),
1912
1902
  debugLogger: options.runtime?.debugLogger ?? createNoopDebugLogger(),
1913
1903
  expectedCatalogConfigDigest: catalog.configDigest,
1904
+ codexObservationSession: options.runtime?.codexObservationSession,
1905
+ codexObservationScopeByMarketplace,
1914
1906
  };
1915
1907
  const inspection = createRebuildIntegration({
1916
1908
  context: executionContext,
@@ -1925,6 +1917,43 @@ export async function runRebuildCommand(options) {
1925
1917
  : []),
1926
1918
  }]]
1927
1919
  : [])).values()];
1920
+ const intentPlanForPreparedRoots = (plan, preparedKeys) => {
1921
+ const retainedRootKeys = new Set(plan.targets.flatMap((target) => target.relation === 'requested'
1922
+ && preparedKeys.has(`${target.pluginId}\0${target.platform}`)
1923
+ ? [`${target.pluginId}\0${target.platform}`]
1924
+ : []));
1925
+ const closureKeys = new Set(retainedRootKeys);
1926
+ const pending = [...closureKeys];
1927
+ while (pending.length > 0) {
1928
+ const consumerKey = pending.pop();
1929
+ for (const edge of plan.dependencyEdges) {
1930
+ if (`${edge.from}\0${edge.platform}` !== consumerKey)
1931
+ continue;
1932
+ const dependencyKey = `${edge.to}\0${edge.platform}`;
1933
+ if (closureKeys.has(dependencyKey))
1934
+ continue;
1935
+ closureKeys.add(dependencyKey);
1936
+ pending.push(dependencyKey);
1937
+ }
1938
+ }
1939
+ return {
1940
+ plan: {
1941
+ ...plan,
1942
+ requestedPluginIds: plan.requestedPluginIds.filter((pluginId) => plan.targets.some((target) => target.pluginId === pluginId
1943
+ && retainedRootKeys.has(`${target.pluginId}\0${target.platform}`))),
1944
+ resolvedPluginIds: plan.resolvedPluginIds.filter((pluginId) => plan.targets.some((target) => target.pluginId === pluginId
1945
+ && closureKeys.has(`${target.pluginId}\0${target.platform}`))),
1946
+ dependencyEdges: plan.dependencyEdges.filter((edge) => closureKeys.has(`${edge.from}\0${edge.platform}`)
1947
+ && closureKeys.has(`${edge.to}\0${edge.platform}`)),
1948
+ scopeOverrides: plan.scopeOverrides.filter((override) => closureKeys.has(`${override.pluginId}\0${override.platform}`)),
1949
+ targets: plan.targets.filter((target) => closureKeys.has(`${target.pluginId}\0${target.platform}`)),
1950
+ },
1951
+ selectedRoots: selectedRootsForPlan(plan).flatMap((root) => {
1952
+ const platforms = root.platforms.filter((platform) => retainedRootKeys.has(`${root.pluginId}\0${platform}`));
1953
+ return platforms.length > 0 ? [{ ...root, platforms }] : [];
1954
+ }),
1955
+ };
1956
+ };
1928
1957
  let plannedCodexRegistrations = new Map();
1929
1958
  let plannedOwnedTextTargets = new Map();
1930
1959
  let plannedSkillMarkerTargets = new Map();
@@ -1965,6 +1994,15 @@ export async function runRebuildCommand(options) {
1965
1994
  const placementNodes = [];
1966
1995
  const blockingTargets = new Map();
1967
1996
  const actualStates = new Map();
1997
+ const loadPlacementCodexConfigs = async () => {
1998
+ const [user, project] = await Promise.all(['user', 'project'].map((scope) => readCodexConfigFile(resolveCodexPaths(options.commandOptions.cwd, scope, runtimeEnv).configTomlPath)));
1999
+ return { user, project };
2000
+ };
2001
+ let placementCodexConfigs;
2002
+ const readPlacementCodexConfigs = () => {
2003
+ placementCodexConfigs ??= loadPlacementCodexConfigs();
2004
+ return placementCodexConfigs;
2005
+ };
1968
2006
  for (const [order, { entry, platform }] of executionSelections.entries()) {
1969
2007
  const relation = selectedIds.has(entry.pluginId) ? 'requested' : 'dependency';
1970
2008
  let resolvedScope = options.commandOptions.scope;
@@ -2050,12 +2088,17 @@ export async function runRebuildCommand(options) {
2050
2088
  cwd: options.commandOptions.cwd,
2051
2089
  env: runtimeEnv,
2052
2090
  nativeCliRunner: executionContext.nativeCliRunner,
2091
+ scope: codexObservationScopeByMarketplace.get(marketplaceNameFor(entry, platform, resolvedScope)) ?? 'both',
2092
+ observationSession: executionContext.codexObservationSession,
2053
2093
  ...(codexManager ? { manager: codexManager } : {}),
2054
2094
  })
2055
2095
  : undefined;
2056
2096
  if (codexRegistration) {
2057
2097
  plannedCodexRegistrations.set(codexPluginId, codexRegistration);
2058
2098
  }
2099
+ const placementActivationConfigs = codexRegistration
2100
+ ? await readPlacementCodexConfigs()
2101
+ : undefined;
2059
2102
  const placementState = (inspected, scope) => {
2060
2103
  if (platform !== 'codex'
2061
2104
  || surface?.classification !== 'native'
@@ -2070,8 +2113,12 @@ export async function runRebuildCommand(options) {
2070
2113
  return 'partial';
2071
2114
  if (codexRegistration.observation.value === 'absent')
2072
2115
  return 'absent';
2073
- return codexRegistration.observations[scope]
2074
- && codexRegistration.activations[scope] === true
2116
+ const configEnabled = placementActivationConfigs.user
2117
+ .snapshot.plugins[codexPluginId]?.enabled === true
2118
+ || (scope === 'project'
2119
+ && placementActivationConfigs.project
2120
+ .snapshot.plugins[codexPluginId]?.enabled === true);
2121
+ return configEnabled
2075
2122
  ? inspected.state
2076
2123
  : 'partial';
2077
2124
  };
@@ -2211,6 +2258,7 @@ export async function runRebuildCommand(options) {
2211
2258
  ? await resolveArtifactRenderConfig({
2212
2259
  sourceId: entry.source.sourceId,
2213
2260
  sourceIdentity: entry.source.sourceIdentity,
2261
+ logicalIdentity: entry.source.logicalIdentity,
2214
2262
  pluginName: entry.plugin.manifest.name,
2215
2263
  manifest: entry.plugin.manifest,
2216
2264
  scope: 'project',
@@ -2220,6 +2268,7 @@ export async function runRebuildCommand(options) {
2220
2268
  : await resolveEffectivePluginConfig({
2221
2269
  sourceId: entry.source.sourceId,
2222
2270
  sourceIdentity: entry.source.sourceIdentity,
2271
+ logicalIdentity: entry.source.logicalIdentity,
2223
2272
  pluginName: entry.plugin.manifest.name,
2224
2273
  manifest: entry.plugin.manifest,
2225
2274
  targetScope: 'user',
@@ -2418,15 +2467,29 @@ export async function runRebuildCommand(options) {
2418
2467
  return [...scopes].sort();
2419
2468
  };
2420
2469
  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
2470
  const [userConfig, projectConfig] = await Promise.all([
2425
- readActivationConfig('user'),
2426
- readActivationConfig('project'),
2471
+ readCodexConfigFile(resolveCodexPaths(options.commandOptions.cwd, 'user', runtimeEnv).configTomlPath),
2472
+ readCodexConfigFile(resolveCodexPaths(options.commandOptions.cwd, 'project', runtimeEnv).configTomlPath),
2427
2473
  ]);
2428
2474
  const catalogClosure = [];
2429
2475
  const liveEvidence = [];
2476
+ const desiredScopesByPluginId = new Map();
2477
+ const intentScopesByPluginId = new Map();
2478
+ for (const planned of reconciledCodexTargets) {
2479
+ const entry = available.find((candidate) => candidate.pluginId === planned.pluginId);
2480
+ const marketplaceName = marketplaceNameFor(entry, 'codex', planned.resolvedScope);
2481
+ const codexPluginId = `${entry.plugin.manifest.name}@${marketplaceName}`;
2482
+ const desiredScopes = desiredScopesForEntry(entry);
2483
+ const intentScopes = intentScopesForEntry(entry);
2484
+ desiredScopesByPluginId.set(entry.pluginId, desiredScopes);
2485
+ intentScopesByPluginId.set(entry.pluginId, intentScopes);
2486
+ if (planned.resolvedScope === 'project'
2487
+ || desiredScopes.includes('project')
2488
+ || intentScopes.includes('project')
2489
+ || Object.hasOwn(projectConfig.snapshot.plugins, codexPluginId)) {
2490
+ codexObservationScopeByMarketplace.set(marketplaceName, 'both');
2491
+ }
2492
+ }
2430
2493
  for (const planned of reconciledCodexTargets) {
2431
2494
  const entry = available.find((candidate) => candidate.pluginId === planned.pluginId);
2432
2495
  const surface = analyzeCodexSurface(entry.plugin.manifest);
@@ -2438,7 +2501,11 @@ export async function runRebuildCommand(options) {
2438
2501
  const staticContentDigest = hasNativeRenderTargets
2439
2502
  ? undefined
2440
2503
  : await calculateCodexSharedNativePayloadDigest(entry.plugin.rootDir);
2441
- const desiredScopes = desiredScopesForEntry(entry);
2504
+ const desiredScopes = desiredScopesByPluginId.get(entry.pluginId);
2505
+ const intentScopes = intentScopesByPluginId.get(entry.pluginId);
2506
+ const observationScope = codexObservationScopeByMarketplace.get(marketplaceName)
2507
+ ?? 'user';
2508
+ const plannedRegistration = plannedCodexRegistrations.get(codexPluginId);
2442
2509
  const sharedInspection = surface.nativeKinds.length > 0
2443
2510
  ? await inspectCodexSharedNativeLive({
2444
2511
  pluginName: entry.plugin.manifest.name,
@@ -2450,9 +2517,12 @@ export async function runRebuildCommand(options) {
2450
2517
  marketplaceName,
2451
2518
  marketplaceRoot: path.join(resolveAwHome(runtimeEnv), 'marketplaces', entry.source.sourceId),
2452
2519
  nativeCliRunner: executionContext.nativeCliRunner,
2520
+ observationScope,
2521
+ observationSession: executionContext.codexObservationSession,
2453
2522
  ...(codexManager ? { manager: codexManager } : {}),
2454
- ...(plannedCodexRegistrations.get(codexPluginId)
2455
- ? { pluginRegistration: plannedCodexRegistrations.get(codexPluginId) }
2523
+ ...(plannedRegistration
2524
+ && (observationScope === 'user' || plannedRegistration.observations.project)
2525
+ ? { pluginRegistration: plannedRegistration }
2456
2526
  : {}),
2457
2527
  ...(hasNativeRenderTargets ? { nativeRenderPlugin: entry.plugin } : {}),
2458
2528
  })
@@ -2469,7 +2539,7 @@ export async function runRebuildCommand(options) {
2469
2539
  relation: planned.relation,
2470
2540
  resolvedScope: planned.resolvedScope,
2471
2541
  desiredScopes,
2472
- intentScopes: intentScopesForEntry(entry),
2542
+ intentScopes,
2473
2543
  surface,
2474
2544
  });
2475
2545
  const sharedNative = sharedInspection?.sharedNative
@@ -2544,15 +2614,8 @@ export async function runRebuildCommand(options) {
2544
2614
  liveEvidence.push({
2545
2615
  codexPluginId,
2546
2616
  sharedNative,
2547
- ...(sharedInspection?.actualPayloadDigest
2548
- ? { sharedNativePayloadDigest: sharedInspection.actualPayloadDigest }
2549
- : {}),
2550
- ...(sharedInspection?.payloadPath
2551
- ? { sharedNativePayloadPath: sharedInspection.payloadPath }
2552
- : {}),
2553
2617
  activations: {
2554
- user: sharedInspection?.activations.user
2555
- ?? userConfig?.snapshot.plugins[codexPluginId]?.enabled,
2618
+ user: userConfig.snapshot.plugins[codexPluginId]?.enabled,
2556
2619
  project: projectConfig?.snapshot.plugins[codexPluginId]?.enabled,
2557
2620
  },
2558
2621
  extensions,
@@ -2664,6 +2727,7 @@ export async function runRebuildCommand(options) {
2664
2727
  const integration = createRebuildIntegration({
2665
2728
  context: executionContext,
2666
2729
  scriptTimeoutMs: options.commandOptions.scriptTimeoutMs,
2730
+ operationScopedNativePhases: true,
2667
2731
  });
2668
2732
  const preparedTargets = new Map();
2669
2733
  const preparedTargetErrors = new Map();
@@ -2689,8 +2753,10 @@ export async function runRebuildCommand(options) {
2689
2753
  const plannedNative = platform === 'codex'
2690
2754
  ? codexPlans.flatMap((codexPlan) => codexPlan.sharedNativeActions).find((action) => action.pluginId === entry.pluginId)
2691
2755
  : undefined;
2756
+ const codexNativeStillDesired = platform === 'codex'
2757
+ && codexPlans.some((codexPlan) => codexPlan.desiredTargets.some((target) => target.pluginId === entry.pluginId && target.scopes.length > 0));
2692
2758
  const preserveSharedNative = plannedNative?.action === 'reuse'
2693
- || plannedNative?.action === 'none';
2759
+ || (plannedNative?.action === 'none' && codexNativeStillDesired);
2694
2760
  const marketplaceName = marketplaceNameFor(entry, platform, planned.resolvedScope);
2695
2761
  try {
2696
2762
  const target = await measureCommandPerformanceStage(options.runtime, 'target.build', () => integration.buildTarget({
@@ -2731,10 +2797,20 @@ export async function runRebuildCommand(options) {
2731
2797
  ...codexPlan.extensionRemovals,
2732
2798
  ].some((action) => action.pluginId === entry.pluginId
2733
2799
  && action.scope === planned.resolvedScope));
2800
+ const hasActivationMutation = codexPlans.some((codexPlan) => [
2801
+ ...codexPlan.activationActions.user,
2802
+ ...codexPlan.activationActions.project,
2803
+ ].some((action) => action.pluginId === entry.pluginId
2804
+ && action.action !== 'none'));
2734
2805
  target.codexNativeObservationExpectation = preserveSharedNative && hasExtensionMutation
2735
2806
  ? (plannedNative.action === 'reuse' ? 'installed' : 'observed')
2736
2807
  : undefined;
2737
- if (!writesSharedNative) {
2808
+ const removesSharedNative = !writesSharedNative
2809
+ && !preserveSharedNative
2810
+ && (buildCommand === 'cleanup' || buildCommand === 'uninstall');
2811
+ target.codexNativeSensitivePreflightRequired = preserveSharedNative
2812
+ && hasActivationMutation;
2813
+ if (!writesSharedNative && !removesSharedNative) {
2738
2814
  const isSharedNativeComponent = (component) => component.kind === 'native-plugin'
2739
2815
  || (component.kind === 'shared-config'
2740
2816
  && component.identity.artifactKey.startsWith('marketplace-'));
@@ -2744,6 +2820,9 @@ export async function runRebuildCommand(options) {
2744
2820
  target.optionalOwnedComponents = target.optionalOwnedComponents?.filter((component) => !isSharedNativeComponent(component));
2745
2821
  target.operationActions = target.operationActions?.filter((action) => action.kind !== 'ensureNativeRegistration');
2746
2822
  }
2823
+ if (target.codexNativeSensitivePreflightRequired && !hasExtensionMutation) {
2824
+ await integration.driver.preflightMutation?.(target);
2825
+ }
2747
2826
  }
2748
2827
  preparedTargets.set(`${entry.pluginId}\0${platform}`, {
2749
2828
  target,
@@ -2793,6 +2872,7 @@ export async function runRebuildCommand(options) {
2793
2872
  const effective = await resolveEffectivePluginConfig({
2794
2873
  sourceId: entry.source.sourceId,
2795
2874
  sourceIdentity: entry.source.sourceIdentity,
2875
+ logicalIdentity: entry.source.logicalIdentity,
2796
2876
  pluginName: entry.plugin.manifest.name,
2797
2877
  manifest: entry.plugin.manifest,
2798
2878
  targetScope: scope,
@@ -2806,6 +2886,7 @@ export async function runRebuildCommand(options) {
2806
2886
  });
2807
2887
  }));
2808
2888
  return {
2889
+ integration,
2809
2890
  preparedTargets,
2810
2891
  preparedTargetErrors,
2811
2892
  targetActions: [...preparedTargets.values()].flatMap((prepared) => prepared.target.operationActions ?? []),
@@ -2882,6 +2963,7 @@ export async function runRebuildCommand(options) {
2882
2963
  : {}),
2883
2964
  sourceId: entry.source.sourceId,
2884
2965
  sourceIdentity: entry.source.sourceIdentity,
2966
+ logicalIdentity: entry.source.logicalIdentity,
2885
2967
  resolvedCommit: entry.source.resolvedCommit,
2886
2968
  manifestDigest: entry.source.manifestDigest,
2887
2969
  snapshotDigest: entry.source.snapshotDigest,
@@ -2894,60 +2976,9 @@ export async function runRebuildCommand(options) {
2894
2976
  const codexManagerRequired = provisionalLockedCodexPlans.some((plan) => plan.sharedNativeActions.some((action) => action.action === 'install'
2895
2977
  || action.action === 'replace'
2896
2978
  || 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({
2979
+ let lockedCodexPlans = provisionalLockedCodexPlans;
2980
+ let lockedPreparation = await prepareRenderedTargets(lockedPlan, lockedCodexPlans, true);
2981
+ let lockedOperationPlan = createRebuildOperationPlan({
2951
2982
  command: options.command,
2952
2983
  scope: options.commandOptions.scope,
2953
2984
  dependencyPlan: lockedPlan,
@@ -2972,23 +3003,303 @@ export async function runRebuildCommand(options) {
2972
3003
  || options.command === 'sync'
2973
3004
  || options.command === 'reinstall')) {
2974
3005
  const sharedNativeImpact = lockedCodexPlans.some((plan) => plan.review.requiresConfirmation);
2975
- if (sharedNativeImpact) {
2976
- throw namespacedPreflightError('sharedNativeImpactAuthorizationRequired: Replacing or force-reinstalling shared Codex native state requires --yes.', preflightTargets);
3006
+ const preApplyRequired = executionSelections.some(({ entry }) => entry.plugin.manifest.scripts?.preApply !== undefined);
3007
+ if (preApplyRequired || sharedNativeImpact) {
3008
+ throw namespacedPreflightError(preApplyRequired
3009
+ ? 'preApplyAuthorizationRequired: Running plugin preApply requires --yes.'
3010
+ : 'sharedNativeImpactAuthorizationRequired: Replacing or force-reinstalling shared Codex native state requires --yes.', preflightTargets);
2977
3011
  }
2978
3012
  }
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,
3013
+ const operationPreparations = new Map();
3014
+ const operationPreparationFailures = new Map();
3015
+ const lifecycleCommand = options.command === 'install'
3016
+ || options.command === 'sync'
3017
+ || options.command === 'reinstall';
3018
+ const executionCommandFor = (entry, platform) => {
3019
+ const planned = lockedPlan.targets.find((target) => target.pluginId === entry.pluginId && target.platform === platform);
3020
+ const desiredInCurrentScope = platform === 'codex'
3021
+ && lockedCodexPlans.some((codexPlan) => codexPlan.desiredTargets.some((target) => target.pluginId === entry.pluginId && target.scopes.includes(planned.resolvedScope)));
3022
+ if (platform === 'codex' && options.command === 'sync' && !desiredInCurrentScope) {
3023
+ return 'cleanup';
3024
+ }
3025
+ if (platform === 'codex' && options.command === 'cleanup' && desiredInCurrentScope) {
3026
+ return 'install';
3027
+ }
3028
+ if (planned.action === 'move') {
3029
+ return planned.state === 'partial' ? 'reinstall' : 'install';
3030
+ }
3031
+ if (planned.relation === 'dependency'
3032
+ && (planned.action === 'install' || planned.action === 'reinstall')) {
3033
+ return planned.action;
3034
+ }
3035
+ return options.command;
3036
+ };
3037
+ const prepareLockedTargets = async (preparationOptions) => {
3038
+ const preparations = new Map();
3039
+ const failures = new Map();
3040
+ const states = new Map();
3041
+ const integration = createRebuildIntegration({
3042
+ context: executionContext,
3043
+ scriptTimeoutMs: options.commandOptions.scriptTimeoutMs,
3044
+ preparedTargets: [...lockedPreparation.preparedTargets.values()].map((entry) => entry.target),
2991
3045
  });
3046
+ for (const { entry, platform } of executionSelections) {
3047
+ const key = `${entry.pluginId}\0${platform}`;
3048
+ if (preparationOptions.eligibleKeys
3049
+ && !preparationOptions.eligibleKeys.has(key)) {
3050
+ states.set(key, 'skipped');
3051
+ continue;
3052
+ }
3053
+ const directDependencyFailed = (entry.plugin.manifest.dependencies ?? []).some((pluginName) => {
3054
+ const dependencyPluginId = formatPluginId({
3055
+ sourceId: entry.source.sourceId,
3056
+ pluginName,
3057
+ });
3058
+ const state = states.get(`${dependencyPluginId}\0${platform}`);
3059
+ return state === 'failed' || state === 'skipped';
3060
+ });
3061
+ const planned = lockedPlan.targets.find((target) => target.pluginId === entry.pluginId && target.platform === platform);
3062
+ if (directDependencyFailed || planned.action === 'block') {
3063
+ states.set(key, 'skipped');
3064
+ continue;
3065
+ }
3066
+ const preparationError = lockedPreparation.preparedTargetErrors.get(key);
3067
+ const prepared = lockedPreparation.preparedTargets.get(key);
3068
+ if (preparationError || !prepared) {
3069
+ const message = preparationError instanceof Error
3070
+ ? preparationError.message
3071
+ : String(preparationError ?? 'Target preparation did not produce a target.');
3072
+ failures.set(key, failedResult(options.command, {
3073
+ sourceId: entry.source.sourceId,
3074
+ sourceIdentity: entry.source.sourceIdentity,
3075
+ pluginId: entry.pluginId,
3076
+ pluginName: entry.plugin.manifest.name,
3077
+ pluginVersion: entry.plugin.manifest.version,
3078
+ platform,
3079
+ scope: planned.resolvedScope,
3080
+ cwd: options.commandOptions.cwd,
3081
+ }, message));
3082
+ states.set(key, 'failed');
3083
+ continue;
3084
+ }
3085
+ const executionCommand = executionCommandFor(entry, platform);
3086
+ if (executionCommand === 'cleanup' || executionCommand === 'uninstall') {
3087
+ states.set(key, 'ready');
3088
+ continue;
3089
+ }
3090
+ const preparedOperation = await prepareRebuildOperation({
3091
+ command: executionCommand,
3092
+ target: prepared.target,
3093
+ driver: integration.driver,
3094
+ ...(preparationOptions.skipPreApply ? { skipPreApply: true } : {}),
3095
+ ...(preparationOptions.deferManagedNativeInspection
3096
+ && platform === 'codex'
3097
+ && executionContext.codexManager === undefined
3098
+ && (prepared.target.writeNativeRequired ?? prepared.target.requiresNative)
3099
+ ? { deferNativeInspection: true }
3100
+ : {}),
3101
+ onActionEvent: (event) => emitRebuildActionEvent(executionContext, event),
3102
+ });
3103
+ if (preparedOperation.status === 'failed') {
3104
+ failures.set(key, preparedOperation.result);
3105
+ states.set(key, 'failed');
3106
+ continue;
3107
+ }
3108
+ preparations.set(key, preparedOperation.preparation);
3109
+ states.set(key, 'ready');
3110
+ }
3111
+ return { preparations, failures };
3112
+ };
3113
+ if (lifecycleCommand) {
3114
+ const initialPreparation = await prepareLockedTargets({
3115
+ deferManagedNativeInspection: codexManagerRequired,
3116
+ });
3117
+ for (const [key, preparation] of initialPreparation.preparations) {
3118
+ operationPreparations.set(key, preparation);
3119
+ }
3120
+ for (const [key, failure] of initialPreparation.failures) {
3121
+ operationPreparationFailures.set(key, failure);
3122
+ }
3123
+ const nativePreparationKeys = new Set([...operationPreparations].flatMap(([key, preparation]) => preparation.target.identity.platform === 'codex'
3124
+ && (preparation.target.writeNativeRequired ?? preparation.target.requiresNative)
3125
+ ? [key]
3126
+ : []));
3127
+ const activationPluginIds = new Set(provisionalLockedCodexPlans.flatMap((plan) => plan.postcheck.plugins.map((plugin) => plugin.pluginId)));
3128
+ const activationPreparationKeys = new Set([...operationPreparations].flatMap(([key, preparation]) => preparation.target.identity.platform === 'codex'
3129
+ && preparation.target.identity.pluginId
3130
+ && activationPluginIds.has(preparation.target.identity.pluginId)
3131
+ ? [key]
3132
+ : []));
3133
+ const codexRequirementScopeFor = (keys) => {
3134
+ const pluginIds = new Set([...keys].flatMap((key) => {
3135
+ const pluginId = operationPreparations.get(key)?.target.identity.pluginId;
3136
+ return pluginId ? [pluginId] : [];
3137
+ }));
3138
+ return provisionalLockedCodexPlans.some((plan) => plan.desiredTargets.some((target) => pluginIds.has(target.pluginId) && target.scopes.includes('project')))
3139
+ ? 'project'
3140
+ : 'user';
3141
+ };
3142
+ let refreshManagedNativePlan = false;
3143
+ const failPreparedTargets = (keys, error) => {
3144
+ const message = error instanceof Error ? error.message : String(error);
3145
+ for (const key of keys) {
3146
+ const target = operationPreparations.get(key)?.target;
3147
+ if (!target)
3148
+ continue;
3149
+ operationPreparations.delete(key);
3150
+ operationPreparationFailures.set(key, failedResult(options.command, target.identity, message));
3151
+ }
3152
+ };
3153
+ if (executionContext.codexManager === undefined
3154
+ && nativePreparationKeys.size > 0) {
3155
+ try {
3156
+ if (options.runtime?.codexManagerResolver) {
3157
+ executionContext.codexManager = await options.runtime.codexManagerResolver({
3158
+ cwd: options.commandOptions.cwd,
3159
+ env: runtimeEnv,
3160
+ runner: executionContext.nativeCliRunner,
3161
+ authorizer: options.runtime?.managedCodexAuthorizer,
3162
+ operationRequirement: getCodexOperationRequirement(codexRequirementScopeFor(nativePreparationKeys)),
3163
+ });
3164
+ }
3165
+ else {
3166
+ const consumer = await (options.runtime?.codexOperationConsumerResolver ?? resolveCodexOperationConsumer)({
3167
+ cwd: options.commandOptions.cwd,
3168
+ env: runtimeEnv,
3169
+ runner: executionContext.nativeCliRunner,
3170
+ authorizer: options.runtime?.managedCodexAuthorizer,
3171
+ operationRequirement: getCodexOperationRequirement(codexRequirementScopeFor(nativePreparationKeys)),
3172
+ requiresNativeMaterialization: true,
3173
+ });
3174
+ if (consumer.support !== 'observed') {
3175
+ throw new Error('Codex native materialization requires an observed CLI consumer.');
3176
+ }
3177
+ executionContext.codexManager = consumer.manager;
3178
+ }
3179
+ refreshManagedNativePlan = true;
3180
+ }
3181
+ catch (error) {
3182
+ failPreparedTargets(nativePreparationKeys, error);
3183
+ }
3184
+ }
3185
+ else if (executionContext.codexManager === undefined
3186
+ && activationPreparationKeys.size > 0) {
3187
+ try {
3188
+ const consumer = await (options.runtime?.codexOperationConsumerResolver ?? resolveCodexOperationConsumer)({
3189
+ cwd: options.commandOptions.cwd,
3190
+ env: runtimeEnv,
3191
+ runner: executionContext.nativeCliRunner,
3192
+ operationRequirement: getCodexOperationRequirement(codexRequirementScopeFor(activationPreparationKeys)),
3193
+ requiresNativeMaterialization: false,
3194
+ });
3195
+ if (consumer.support === 'observed') {
3196
+ executionContext.codexManager = consumer.manager;
3197
+ }
3198
+ else {
3199
+ lockedCodexPlans = lockedCodexPlans.map((plan) => ({
3200
+ ...plan,
3201
+ warnings: [...new Set([
3202
+ ...plan.warnings,
3203
+ 'codexProjectActivationAssumed',
3204
+ ])],
3205
+ postcheck: { ...plan.postcheck, support: 'assumed' },
3206
+ }));
3207
+ }
3208
+ }
3209
+ catch (error) {
3210
+ failPreparedTargets(activationPreparationKeys, error);
3211
+ }
3212
+ }
3213
+ if (executionContext.codexManager) {
3214
+ executionContext.codexObservationSession?.setManager(executionContext.codexManager);
3215
+ }
3216
+ if (refreshManagedNativePlan) {
3217
+ if (executionContext.codexManager) {
3218
+ lockedPlan = await createPlan(stateInventory, executionContext.codexManager);
3219
+ assertWriteInventoryComplete(stateInventory, exactOwnedTextTargets(lockedPlan), exactSkillMarkerTargets(lockedPlan));
3220
+ }
3221
+ lockedCodexPlans = await createCodexPlans(lockedPlan, 'observed', executionContext.codexManager);
3222
+ lockedPreparation = await prepareRenderedTargets(lockedPlan, lockedCodexPlans, false);
3223
+ const eligibleKeys = new Set(operationPreparations.keys());
3224
+ const initialOperationPreparations = new Map(operationPreparations);
3225
+ const finalPreparation = await prepareLockedTargets({
3226
+ eligibleKeys,
3227
+ skipPreApply: true,
3228
+ });
3229
+ operationPreparations.clear();
3230
+ for (const [key, preparation] of finalPreparation.preparations) {
3231
+ operationPreparations.set(key, {
3232
+ ...preparation,
3233
+ actions: [
3234
+ ...(initialOperationPreparations.get(key)?.actions ?? []),
3235
+ ...preparation.actions,
3236
+ ],
3237
+ });
3238
+ }
3239
+ for (const [key, failure] of finalPreparation.failures) {
3240
+ operationPreparationFailures.set(key, failure);
3241
+ }
3242
+ }
3243
+ const eligibleTargets = [...operationPreparations.keys()].flatMap((key) => {
3244
+ const prepared = lockedPreparation.preparedTargets.get(key);
3245
+ return prepared ? [prepared.target] : [];
3246
+ });
3247
+ lockedPreparation.integration.setNativeMarketplaceEligibleTargets(eligibleTargets);
3248
+ }
3249
+ lockedOperationPlan = createRebuildOperationPlan({
3250
+ command: options.command,
3251
+ scope: options.commandOptions.scope,
3252
+ dependencyPlan: lockedPlan,
3253
+ codexPlans: lockedCodexPlans,
3254
+ targetActions: lockedPreparation.targetActions,
3255
+ catalogSources: catalog.sources.flatMap((source) => source.resolution ? [source.resolution] : []),
3256
+ intentBeforeByScope,
3257
+ configDigests: lockedPreparation.configDigests,
3258
+ catalogEntries: intentCatalogEntries,
3259
+ });
3260
+ if (options.runtime?.expectedPlanFingerprint) {
3261
+ assertOperationPlanFingerprint(options.runtime.expectedPlanFingerprint, lockedOperationPlan);
3262
+ }
3263
+ if (options.runtime?.directCli
3264
+ && options.commandOptions.yes !== true
3265
+ && lifecycleCommand
3266
+ && lockedCodexPlans.some((plan) => plan.review.requiresConfirmation)) {
3267
+ throw namespacedPreflightError('sharedNativeImpactAuthorizationRequired: Replacing or force-reinstalling shared Codex native state requires --yes.', preflightTargets);
3268
+ }
3269
+ if (options.runtime?.awHomeMutationToken) {
3270
+ if (lifecycleCommand) {
3271
+ const preparedKeys = new Set(operationPreparations.keys());
3272
+ const intentSelection = intentPlanForPreparedRoots(lockedPlan, preparedKeys);
3273
+ const intentPlan = intentSelection.plan;
3274
+ const selectedRoots = intentSelection.selectedRoots;
3275
+ if (selectedRoots.length > 0) {
3276
+ await writeDesiredIntentSuperset({
3277
+ command: options.command,
3278
+ cwd: options.commandOptions.cwd,
3279
+ scope: options.commandOptions.scope,
3280
+ selectedRoots,
3281
+ plan: intentPlan,
3282
+ codexPlan: lockedCodexPlans[0],
3283
+ preserveOtherCodexScopeRootIds,
3284
+ catalogEntries: intentCatalogEntries,
3285
+ env: runtimeEnv,
3286
+ });
3287
+ }
3288
+ }
3289
+ else {
3290
+ const selectedRoots = selectedRootsForPlan(lockedPlan);
3291
+ await writeDesiredIntentSuperset({
3292
+ command: options.command,
3293
+ cwd: options.commandOptions.cwd,
3294
+ scope: options.commandOptions.scope,
3295
+ selectedRoots,
3296
+ plan: lockedPlan,
3297
+ codexPlan: lockedCodexPlans[0],
3298
+ preserveOtherCodexScopeRootIds,
3299
+ catalogEntries: intentCatalogEntries,
3300
+ env: runtimeEnv,
3301
+ });
3302
+ }
2992
3303
  }
2993
3304
  for (const pluginName of new Set(lockedPlan.targets.flatMap((target) => target.platform === 'claude'
2994
3305
  && (target.action === 'install'
@@ -3142,7 +3453,6 @@ export async function runRebuildCommand(options) {
3142
3453
  }
3143
3454
  }
3144
3455
  const results = [];
3145
- const deferredPostApplyTasks = [];
3146
3456
  const dependencyStates = new Map();
3147
3457
  for (const { entry, platform } of executionSelections) {
3148
3458
  const planned = lockedPlan.targets.find((target) => target.pluginId === entry.pluginId && target.platform === platform);
@@ -3230,6 +3540,27 @@ export async function runRebuildCommand(options) {
3230
3540
  dependencyStates.set(`${entry.pluginId}\0${platform}`, failedDependency || failedConsumer ? 'skipped' : 'failed');
3231
3541
  continue;
3232
3542
  }
3543
+ const operationPreparationFailure = operationPreparationFailures.get(`${entry.pluginId}\0${platform}`);
3544
+ if (operationPreparationFailure) {
3545
+ const targetResult = toTargetResult(operationPreparationFailure);
3546
+ results.push({
3547
+ platforms: [platform],
3548
+ plugins: [],
3549
+ warnings: [],
3550
+ rebuildResults: [operationPreparationFailure],
3551
+ targetResults: [{ ...targetResult, action: planned.action }],
3552
+ overallState: 'failed',
3553
+ receiptTargets: [{
3554
+ pluginName: identity.pluginName,
3555
+ pluginId: identity.pluginId,
3556
+ sourceId: identity.sourceId,
3557
+ platform,
3558
+ scope: planned.resolvedScope,
3559
+ }],
3560
+ });
3561
+ dependencyStates.set(`${entry.pluginId}\0${platform}`, 'failed');
3562
+ continue;
3563
+ }
3233
3564
  if (reconcileActivationOnly) {
3234
3565
  const preparedError = preparedTargetErrors.get(`${entry.pluginId}\0${platform}`);
3235
3566
  if (preparedError) {
@@ -3255,43 +3586,55 @@ export async function runRebuildCommand(options) {
3255
3586
  dependencyStates.set(`${entry.pluginId}\0${platform}`, 'failed');
3256
3587
  continue;
3257
3588
  }
3258
- const warnings = [];
3259
- const result = {
3589
+ const actions = operationPreparations.get(`${entry.pluginId}\0${platform}`)?.actions
3590
+ ?? [];
3591
+ const preparedTarget = preparedTargets.get(`${entry.pluginId}\0${platform}`)?.target;
3592
+ let result;
3593
+ if (preparedTarget) {
3594
+ const nativeSkillKeys = new Set((entry.plugin.manifest.artifacts?.targets ?? [])
3595
+ .filter((target) => target.platform === 'codex'
3596
+ && target.target === 'codex.skill'
3597
+ && target.delivery?.mode === 'codex-native')
3598
+ .map((target) => target.skillKey));
3599
+ const marketplaceRoot = preparedTarget.identity.marketplaceRoot;
3600
+ const legacyNativeSkills = preparedTarget.ownedComponents.filter((component) => component.kind === 'skill-directory'
3601
+ && nativeSkillKeys.has(component.identity.artifactKey)
3602
+ && (!marketplaceRoot || path.relative(marketplaceRoot, component.path).startsWith('..')));
3603
+ if (legacyNativeSkills.length > 0) {
3604
+ result = await executeRebuildComponentCleanup({
3605
+ command: options.command,
3606
+ target: preparedTarget,
3607
+ driver: lockedPreparation.integration.driver,
3608
+ components: legacyNativeSkills,
3609
+ actions,
3610
+ onActionEvent: (event) => emitRebuildActionEvent(executionContext, event),
3611
+ ...(rebuildCommandTestHooks?.beforeActivationLegacyCleanupMutation ? {
3612
+ beforeMutation: rebuildCommandTestHooks.beforeActivationLegacyCleanupMutation,
3613
+ } : {}),
3614
+ ...(rebuildCommandTestHooks?.afterActivationLegacyCleanupQuarantine ? {
3615
+ afterQuarantine: rebuildCommandTestHooks.afterActivationLegacyCleanupQuarantine,
3616
+ } : {}),
3617
+ });
3618
+ }
3619
+ }
3620
+ result ??= {
3260
3621
  status: 'succeeded', command: options.command, target: identity,
3261
- actions: [{ name: 'postApply', status: 'succeeded' }],
3622
+ actions,
3262
3623
  cleanupAttempted: false, removed: [], absent: [], residual: [], quarantined: [], failures: [],
3263
3624
  };
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
3625
  results.push({
3286
- platforms: [platform], plugins: [identity.pluginName], warnings, rebuildResults: [result],
3287
- targetResults: [{ target: identity, action: planned.action, state: 'completed' }],
3288
- overallState: 'succeeded',
3626
+ platforms: [platform],
3627
+ plugins: result.status === 'succeeded' ? [identity.pluginName] : [],
3628
+ warnings: result.failures.map((failure) => failure.message),
3629
+ rebuildResults: [result],
3630
+ targetResults: [{ ...toTargetResult(result), action: planned.action }],
3631
+ overallState: result.status === 'succeeded' ? 'succeeded' : 'failed',
3289
3632
  receiptTargets: [{
3290
3633
  pluginName: identity.pluginName, pluginId: identity.pluginId,
3291
3634
  sourceId: identity.sourceId, platform, scope: planned.resolvedScope,
3292
3635
  }],
3293
3636
  });
3294
- dependencyStates.set(`${entry.pluginId}\0${platform}`, 'completed');
3637
+ dependencyStates.set(`${entry.pluginId}\0${platform}`, result.status === 'succeeded' ? 'completed' : 'failed');
3295
3638
  continue;
3296
3639
  }
3297
3640
  if (platform === 'codex'
@@ -3318,33 +3661,39 @@ export async function runRebuildCommand(options) {
3318
3661
  && !reconcileObsolete
3319
3662
  && options.command !== 'cleanup'
3320
3663
  && 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',
3664
+ const preparedError = preparedTargetErrors.get(`${entry.pluginId}\0${platform}`);
3665
+ if (preparedError) {
3666
+ const message = preparedError instanceof Error
3667
+ ? preparedError.message
3668
+ : String(preparedError);
3669
+ const result = failedResult(options.command, identity, message);
3670
+ results.push({
3671
+ platforms: [platform], plugins: [], warnings: [], rebuildResults: [result],
3672
+ targetResults: [{
3673
+ target: identity,
3674
+ action: planned.action,
3675
+ state: 'failed',
3676
+ errorCode: message.split(':', 1)[0] ?? 'targetPreparationFailed',
3677
+ message,
3678
+ }],
3679
+ overallState: 'failed',
3680
+ receiptTargets: [{
3681
+ pluginName: identity.pluginName, pluginId: identity.pluginId,
3682
+ sourceId: identity.sourceId, platform, scope: planned.resolvedScope,
3334
3683
  }],
3335
- scriptTimeoutMs: options.commandOptions.scriptTimeoutMs,
3336
3684
  });
3685
+ dependencyStates.set(`${entry.pluginId}\0${platform}`, 'failed');
3686
+ continue;
3337
3687
  }
3338
- catch (error) {
3339
- warnings.push(error instanceof Error ? error.message : String(error));
3340
- }
3688
+ const actions = operationPreparations.get(`${entry.pluginId}\0${platform}`)?.actions
3689
+ ?? [];
3341
3690
  const result = {
3342
3691
  status: 'succeeded', command: options.command, target: identity,
3343
- actions: [{ name: 'postApply', status: warnings.length > 0 ? 'failed' : 'succeeded' }],
3692
+ actions,
3344
3693
  cleanupAttempted: false, removed: [], absent: [], residual: [], quarantined: [], failures: [],
3345
3694
  };
3346
3695
  results.push({
3347
- platforms: [platform], plugins: [identity.pluginName], warnings, rebuildResults: [result],
3696
+ platforms: [platform], plugins: [identity.pluginName], warnings: [], rebuildResults: [result],
3348
3697
  targetResults: [{ target: identity, action: 'keep', state: 'completed' }],
3349
3698
  overallState: 'succeeded',
3350
3699
  receiptTargets: [{
@@ -3359,6 +3708,7 @@ export async function runRebuildCommand(options) {
3359
3708
  const namespacedSource = {
3360
3709
  sourceId: entry.source.sourceId,
3361
3710
  sourceIdentity: entry.source.sourceIdentity,
3711
+ logicalIdentity: entry.source.logicalIdentity,
3362
3712
  marketplaceName,
3363
3713
  marketplaceRoot: path.join(resolveAwHome(runtimeEnv), 'marketplaces', entry.source.sourceId),
3364
3714
  };
@@ -3400,6 +3750,7 @@ export async function runRebuildCommand(options) {
3400
3750
  : {}),
3401
3751
  sourceId: entry.source.sourceId,
3402
3752
  sourceIdentity: entry.source.sourceIdentity,
3753
+ logicalIdentity: entry.source.logicalIdentity,
3403
3754
  resolvedCommit: entry.source.resolvedCommit,
3404
3755
  manifestDigest: entry.source.manifestDigest,
3405
3756
  snapshotDigest: entry.source.snapshotDigest,
@@ -3411,10 +3762,9 @@ export async function runRebuildCommand(options) {
3411
3762
  reviewCompleted: true,
3412
3763
  targetHookCompleted: true,
3413
3764
  prepared: preparedTargets.get(`${entry.pluginId}\0${platform}`),
3765
+ operationPreparation: operationPreparations.get(`${entry.pluginId}\0${platform}`),
3766
+ integration: lockedPreparation.integration,
3414
3767
  preparationError: preparedTargetErrors.get(`${entry.pluginId}\0${platform}`),
3415
- ...(platform === 'codex' && lockedCodexPlans.length > 0
3416
- ? { deferredPostApplyTasks }
3417
- : {}),
3418
3768
  },
3419
3769
  });
3420
3770
  if (reconcileDesiredCleanup) {
@@ -3426,14 +3776,84 @@ export async function runRebuildCommand(options) {
3426
3776
  results.push(executionResult);
3427
3777
  dependencyStates.set(`${entry.pluginId}\0${platform}`, executionResult.overallState === 'succeeded' ? 'completed' : 'failed');
3428
3778
  }
3779
+ const failCompletedCodexTarget = (options_) => {
3780
+ const resultIndex = results.findIndex((candidate) => candidate.targetResults.some((target) => target.target.pluginId === options_.identity.pluginId
3781
+ && target.target.platform === 'codex'
3782
+ && target.target.scope === options_.identity.scope));
3783
+ if (resultIndex === -1)
3784
+ return false;
3785
+ const existing = results[resultIndex];
3786
+ const phase = options_.phase ?? 'postcheck.inspect';
3787
+ const rebuildResults = existing.rebuildResults.map((rebuild) => {
3788
+ if (rebuild.target.pluginId !== options_.identity.pluginId
3789
+ || rebuild.target.platform !== 'codex'
3790
+ || rebuild.target.scope !== options_.identity.scope)
3791
+ return rebuild;
3792
+ if (!rebuild.failures.some((failure) => failure.phase === phase && failure.message === options_.message)) {
3793
+ rebuild.failures.unshift({ phase, message: options_.message });
3794
+ }
3795
+ return {
3796
+ ...rebuild,
3797
+ status: 'failed',
3798
+ actions: rebuild.actions,
3799
+ error: { phase, message: options_.message },
3800
+ };
3801
+ });
3802
+ results[resultIndex] = {
3803
+ ...existing,
3804
+ plugins: existing.plugins.filter((pluginName) => pluginName !== options_.identity.pluginName),
3805
+ rebuildResults,
3806
+ targetResults: existing.targetResults.map((target) => target.target.pluginId === options_.identity.pluginId
3807
+ && target.target.platform === 'codex'
3808
+ && target.target.scope === options_.identity.scope
3809
+ ? {
3810
+ ...target,
3811
+ target: options_.identity,
3812
+ action: options_.action ?? target.action,
3813
+ state: 'failed',
3814
+ errorCode: options_.message.split(':', 1)[0] ?? 'codexActivationPostcheckFailed',
3815
+ message: options_.message,
3816
+ ...(options_.recoveryActions
3817
+ ? { recoveryActions: options_.recoveryActions }
3818
+ : {}),
3819
+ }
3820
+ : target),
3821
+ overallState: 'failed',
3822
+ };
3823
+ dependencyStates.set(`${options_.identity.pluginId}\0codex`, 'failed');
3824
+ return true;
3825
+ };
3826
+ const activationMutationPluginIds = new Set(lockedCodexPlans.flatMap((plan) => [
3827
+ ...plan.activationActions.user,
3828
+ ...plan.activationActions.project,
3829
+ ].flatMap((action) => action.action === 'none' ? [] : [action.pluginId])));
3830
+ for (const { entry, platform } of executionSelections) {
3831
+ if (platform !== 'codex'
3832
+ || !activationMutationPluginIds.has(entry.pluginId)
3833
+ || (dependencyStates.get(`${entry.pluginId}\0${platform}`) !== 'completed'
3834
+ && dependencyStates.get(`${entry.pluginId}\0${platform}`) !== 'kept'))
3835
+ continue;
3836
+ const prepared = preparedTargets.get(`${entry.pluginId}\0${platform}`)?.target;
3837
+ if (!prepared)
3838
+ continue;
3839
+ try {
3840
+ await lockedPreparation.integration.prepareNativeMarketplaceGroup(prepared);
3841
+ }
3842
+ catch (error) {
3843
+ const message = error instanceof Error ? error.message : String(error);
3844
+ failCompletedCodexTarget({ identity: prepared.identity, message, phase: 'native.write' });
3845
+ }
3846
+ }
3429
3847
  const successfulCodexPluginIds = new Set(executionSelections.flatMap(({ entry, platform }) => platform === 'codex'
3430
3848
  && (dependencyStates.get(`${entry.pluginId}\0${platform}`) === 'completed'
3431
3849
  || dependencyStates.get(`${entry.pluginId}\0${platform}`) === 'kept')
3432
3850
  ? [entry.pluginId]
3433
3851
  : []));
3434
3852
  const activationPlans = selectCodexActivationPlans(lockedCodexPlans, successfulCodexPluginIds);
3435
- if (activationPlans.length > 0 || deferredPostApplyTasks.length > 0) {
3436
- let failedActivationPluginIds = new Set();
3853
+ if (activationPlans.length > 0
3854
+ || lockedPreparation.integration.driver.completePostchecks) {
3855
+ let activationError;
3856
+ const activationMutationMarketplaces = new Set();
3437
3857
  try {
3438
3858
  if (activationPlans.length > 0) {
3439
3859
  await executeCodexReconcileActivations({
@@ -3442,23 +3862,67 @@ export async function runRebuildCommand(options) {
3442
3862
  env: runtimeEnv,
3443
3863
  manager: executionContext.codexManager,
3444
3864
  runner: executionContext.nativeCliRunner,
3865
+ observationSession: executionContext.codexObservationSession,
3866
+ preflightCompleted: true,
3867
+ mutationMarketplaces: activationMutationMarketplaces,
3868
+ observationScopeByMarketplace: codexObservationScopeByMarketplace,
3445
3869
  });
3446
3870
  }
3447
3871
  }
3448
3872
  catch (error) {
3449
- failedActivationPluginIds = failedCodexActivationPluginIds({
3450
- error,
3873
+ activationError = error;
3874
+ }
3875
+ try {
3876
+ await lockedPreparation.integration.captureCodexPostMutationSnapshots([...activationMutationMarketplaces]);
3877
+ }
3878
+ catch (error) {
3879
+ activationError ??= error;
3880
+ }
3881
+ const failedCodexPluginIds = new Set(executionSelections.flatMap(({ entry, platform }) => platform === 'codex'
3882
+ && (dependencyStates.get(`${entry.pluginId}\0${platform}`) === 'failed'
3883
+ || dependencyStates.get(`${entry.pluginId}\0${platform}`) === 'skipped')
3884
+ ? [entry.pluginId]
3885
+ : []));
3886
+ for (const failure of await lockedPreparation.integration.driver.completePostchecks?.() ?? []) {
3887
+ const message = failure.error instanceof Error
3888
+ ? failure.error.message
3889
+ : String(failure.error);
3890
+ if (failCompletedCodexTarget({ identity: failure.target.identity, message })) {
3891
+ failedCodexPluginIds.add(failure.target.identity.pluginId ?? failure.target.identity.pluginName);
3892
+ }
3893
+ }
3894
+ if (!activationError && activationPlans.length > 0) {
3895
+ const postcheckPluginIds = new Set([...successfulCodexPluginIds]
3896
+ .filter((pluginId) => !failedCodexPluginIds.has(pluginId)));
3897
+ const postcheckPlans = selectCodexActivationPlans(activationPlans, postcheckPluginIds);
3898
+ try {
3899
+ await postcheckCodexReconcileActivations({
3900
+ plans: postcheckPlans,
3901
+ cwd: options.commandOptions.cwd,
3902
+ env: runtimeEnv,
3903
+ manager: executionContext.codexManager,
3904
+ runner: executionContext.nativeCliRunner,
3905
+ observationScopeByMarketplace: codexObservationScopeByMarketplace,
3906
+ observationSession: executionContext.codexObservationSession,
3907
+ });
3908
+ }
3909
+ catch (error) {
3910
+ activationError = error;
3911
+ }
3912
+ }
3913
+ if (activationError) {
3914
+ const failedActivationPluginIds = failedCodexActivationPluginIds({
3915
+ error: activationError,
3451
3916
  plans: activationPlans,
3452
3917
  successfulPluginIds: successfulCodexPluginIds,
3453
3918
  });
3454
- for (const pluginId of failedActivationPluginIds) {
3455
- dependencyStates.set(`${pluginId}\0codex`, 'failed');
3456
- }
3457
- const message = error instanceof Error ? error.message : String(error);
3919
+ const message = activationError instanceof Error
3920
+ ? activationError.message
3921
+ : String(activationError);
3458
3922
  const failedSelections = executionSelections.filter((candidate) => candidate.platform === 'codex'
3459
3923
  && failedActivationPluginIds.has(candidate.entry.pluginId));
3460
3924
  if (failedSelections.length === 0)
3461
- throw error;
3925
+ throw activationError;
3462
3926
  for (const selection of failedSelections) {
3463
3927
  const planned = lockedPlan.targets.find((target) => target.pluginId === selection.entry.pluginId && target.platform === 'codex');
3464
3928
  const identity = {
@@ -3471,44 +3935,48 @@ export async function runRebuildCommand(options) {
3471
3935
  scope: planned.resolvedScope,
3472
3936
  cwd: options.commandOptions.cwd,
3473
3937
  };
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
- };
3938
+ if (!failCompletedCodexTarget({ identity, message }))
3939
+ throw activationError;
3940
+ failedCodexPluginIds.add(selection.entry.pluginId);
3502
3941
  }
3503
3942
  }
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);
3943
+ let propagated = true;
3944
+ while (propagated) {
3945
+ propagated = false;
3946
+ for (const selection of executionSelections.filter((candidate) => candidate.platform === 'codex')) {
3947
+ if (failedCodexPluginIds.has(selection.entry.pluginId))
3948
+ continue;
3949
+ const failedDependency = (selection.entry.plugin.manifest.dependencies ?? [])
3950
+ .map((pluginName) => formatPluginId({
3951
+ sourceId: selection.entry.source.sourceId,
3952
+ pluginName,
3953
+ }))
3954
+ .find((pluginId) => failedCodexPluginIds.has(pluginId));
3955
+ if (!failedDependency)
3956
+ continue;
3957
+ const planned = lockedPlan.targets.find((target) => target.pluginId === selection.entry.pluginId && target.platform === 'codex');
3958
+ const identity = {
3959
+ sourceId: selection.entry.source.sourceId,
3960
+ sourceIdentity: selection.entry.source.sourceIdentity,
3961
+ pluginId: selection.entry.pluginId,
3962
+ pluginName: selection.entry.plugin.manifest.name,
3963
+ pluginVersion: selection.entry.plugin.manifest.version,
3964
+ platform: 'codex',
3965
+ scope: planned.resolvedScope,
3966
+ cwd: options.commandOptions.cwd,
3967
+ };
3968
+ const message = `dependencyInstallFailed: ${selection.entry.pluginId} requires failed dependency ${failedDependency}.`;
3969
+ if (failCompletedCodexTarget({
3970
+ identity,
3971
+ message,
3972
+ recoveryActions: [
3973
+ `run aw sync --all --platform codex --scope ${planned.resolvedScope}`,
3974
+ ],
3975
+ })) {
3976
+ failedCodexPluginIds.add(selection.entry.pluginId);
3977
+ dependencyStates.set(`${selection.entry.pluginId}\0codex`, 'failed');
3978
+ propagated = true;
3979
+ }
3512
3980
  }
3513
3981
  }
3514
3982
  }
@@ -3561,6 +4029,7 @@ export async function runRebuildCommand(options) {
3561
4029
  namespacedSource: {
3562
4030
  sourceId: entry.source.sourceId,
3563
4031
  sourceIdentity: entry.source.sourceIdentity,
4032
+ logicalIdentity: entry.source.logicalIdentity,
3564
4033
  marketplaceName,
3565
4034
  marketplaceRoot: path.join(resolveAwHome(runtimeEnv), 'marketplaces', entry.source.sourceId),
3566
4035
  },
@@ -3585,7 +4054,7 @@ export async function runRebuildCommand(options) {
3585
4054
  merged.warnings.push(MALFORMED_CONFIG_PRESERVED_WARNING);
3586
4055
  }
3587
4056
  merged.dependencyPlan = lockedPlan;
3588
- merged.overallState = reconcileOverallState(merged.targetResults, lockedPlan);
4057
+ merged.overallState = reconcileOverallState(merged.targetResults, lockedPlan, merged.rebuildResults);
3589
4058
  if (lockedCodexPlans.length > 0)
3590
4059
  merged.codexReconcilePlans = lockedCodexPlans;
3591
4060
  merged.operationPlan = lockedOperationPlan;
@@ -3596,8 +4065,8 @@ export async function runRebuildCommand(options) {
3596
4065
  try {
3597
4066
  await (options.runtime?.removeObsoleteInstallationContracts ?? fs.remove)(path.join(resolveAwHome(runtimeEnv), 'installation-contracts'));
3598
4067
  }
3599
- catch (error) {
3600
- resolvedResult.warnings.push(`obsoleteContractsCleanupFailed: ${error instanceof Error ? error.message : String(error)}`);
4068
+ catch {
4069
+ resolvedResult.warnings.push(OBSOLETE_CONTRACTS_CLEANUP_WARNING);
3601
4070
  }
3602
4071
  }
3603
4072
  return resolvedResult;
@@ -3623,4 +4092,25 @@ export async function runRebuildCommand(options) {
3623
4092
  throw error;
3624
4093
  }
3625
4094
  }
4095
+ export async function runRebuildCommand(options) {
4096
+ if (options.runtime?.codexObservationSession) {
4097
+ return runRebuildCommandInSession(options);
4098
+ }
4099
+ const env = options.runtime?.env ?? process.env;
4100
+ const session = await createCodexNativeObservationSession({
4101
+ exactCwd: options.commandOptions.cwd,
4102
+ env,
4103
+ runner: options.runtime?.nativeCliRunner ?? getDefaultNativeCliRunner(),
4104
+ manager: options.runtime?.codexManager,
4105
+ });
4106
+ try {
4107
+ return await runRebuildCommandInSession({
4108
+ ...options,
4109
+ runtime: { ...options.runtime, codexObservationSession: session },
4110
+ });
4111
+ }
4112
+ finally {
4113
+ await session.dispose();
4114
+ }
4115
+ }
3626
4116
  //# sourceMappingURL=rebuild-command.js.map