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

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 (60) hide show
  1. package/dist/authoring/scripts/validate-plugin.mjs +33 -42
  2. package/dist/authoring/scripts/validate-plugin.mjs.map +1 -1
  3. package/dist/src/commands/operation-planner.js +11 -1
  4. package/dist/src/commands/operation-planner.js.map +1 -1
  5. package/dist/src/commands/rebuild-command.d.ts +12 -0
  6. package/dist/src/commands/rebuild-command.js +72 -17
  7. package/dist/src/commands/rebuild-command.js.map +1 -1
  8. package/dist/src/commands/rebuild-integration.d.ts +1 -0
  9. package/dist/src/commands/rebuild-integration.js +323 -105
  10. package/dist/src/commands/rebuild-integration.js.map +1 -1
  11. package/dist/src/commands/status-v6.js +8 -8
  12. package/dist/src/commands/status-v6.js.map +1 -1
  13. package/dist/src/index.js +52 -14
  14. package/dist/src/index.js.map +1 -1
  15. package/dist/src/portable/materializer.js +5 -3
  16. package/dist/src/portable/materializer.js.map +1 -1
  17. package/dist/src/tui/console-app-v4.js +1 -21
  18. package/dist/src/tui/console-app-v4.js.map +1 -1
  19. package/dist/src/tui/controller-runtime.js +165 -48
  20. package/dist/src/tui/controller-runtime.js.map +1 -1
  21. package/dist/src/tui/controller.d.ts +4 -4
  22. package/dist/src/tui/controller.js +19 -7
  23. package/dist/src/tui/controller.js.map +1 -1
  24. package/dist/src/tui/diagnostics.js +6 -6
  25. package/dist/src/tui/diagnostics.js.map +1 -1
  26. package/dist/src/tui/model.js +15 -10
  27. package/dist/src/tui/model.js.map +1 -1
  28. package/dist/src/tui/operation-review-planner.js +66 -19
  29. package/dist/src/tui/operation-review-planner.js.map +1 -1
  30. package/dist/src/utils/artifact-plan.d.ts +2 -2
  31. package/dist/src/utils/artifact-plan.js +8 -6
  32. package/dist/src/utils/artifact-plan.js.map +1 -1
  33. package/dist/src/utils/artifact-renderer.js +96 -77
  34. package/dist/src/utils/artifact-renderer.js.map +1 -1
  35. package/dist/src/utils/artifact-request.d.ts +5 -0
  36. package/dist/src/utils/artifact-request.js +40 -0
  37. package/dist/src/utils/artifact-request.js.map +1 -1
  38. package/dist/src/utils/codex-shared-native.d.ts +4 -0
  39. package/dist/src/utils/codex-shared-native.js +13 -0
  40. package/dist/src/utils/codex-shared-native.js.map +1 -1
  41. package/dist/src/utils/local-marketplace.d.ts +14 -1
  42. package/dist/src/utils/local-marketplace.js +242 -14
  43. package/dist/src/utils/local-marketplace.js.map +1 -1
  44. package/dist/src/utils/plugin-config-impact.d.ts +33 -0
  45. package/dist/src/utils/plugin-config-impact.js +298 -0
  46. package/dist/src/utils/plugin-config-impact.js.map +1 -0
  47. package/dist/src/utils/plugin-config-write.d.ts +22 -2
  48. package/dist/src/utils/plugin-config-write.js +368 -104
  49. package/dist/src/utils/plugin-config-write.js.map +1 -1
  50. package/dist/src/utils/plugin-config.d.ts +10 -0
  51. package/dist/src/utils/plugin-config.js +39 -22
  52. package/dist/src/utils/plugin-config.js.map +1 -1
  53. package/dist/src/utils/plugin-scripts.js +47 -39
  54. package/dist/src/utils/plugin-scripts.js.map +1 -1
  55. package/dist/src/utils/project-boundary.js +22 -9
  56. package/dist/src/utils/project-boundary.js.map +1 -1
  57. package/dist/src/utils/sensitive-content.d.ts +3 -0
  58. package/dist/src/utils/sensitive-content.js +28 -7
  59. package/dist/src/utils/sensitive-content.js.map +1 -1
  60. package/package.json +3 -3
@@ -6,23 +6,23 @@ import { generateInstructionArtifacts, renderCodexManagedInstructionBlock, } fro
6
6
  import { generateCodexPermissionArtifacts, mergeClaudePermissions, } from '../generators/permissions.js';
7
7
  import { buildGeneratedName } from '../generators/shared.js';
8
8
  import { applyConditionalCodexConfigTransactionFile, applyCodexConfigTransactionFile, composeManagedDeveloperInstructionsBlock, readCodexConfigFile, } from '../utils/codex-config.js';
9
- import { canonicalizePathThroughExistingAncestor } from '../utils/state-paths.js';
9
+ import { canonicalizePathThroughExistingAncestor, resolveClaudeProjectKey, resolveCodexProjectKey, } from '../utils/state-paths.js';
10
10
  import { createCodexSkillExpectedTree, createCodexSkillStagingTreeContentDigest, } from '../utils/codex-skill-staging.js';
11
- import { createDynamicSkillExpectedTree, createDynamicSkillTreeContentDigest, readDynamicSkillTree, validateSkillDirectory, writeDynamicSkillTree, } from '../utils/dynamic-skill-tree.js';
11
+ import { AW_PLUGIN_REQUIREMENTS_RECEIPT_PATH, createDynamicSkillExpectedTree, createDynamicSkillTreeContentDigest, readDynamicSkillTree, validateSkillDirectory, writeDynamicSkillTree, } from '../utils/dynamic-skill-tree.js';
12
12
  import { parseCodexSharedHooksFile, serializeTrackedHookGroupIdentity, } from '../utils/hooks-contract.js';
13
13
  import { buildCodexMarketplaceAddInvocation, buildCodexMarketplaceRemoveInvocation, buildCodexPluginAddInvocation, buildCodexPluginRemoveInvocation, CodexProjectConfigNotEffectiveError, runNativeCliChecked, SYSTEM_CODEX_MANAGER, } from '../utils/native-cli.js';
14
14
  import { OWNED_TEXT_HEADER_PREFIX, createSharedOwnerToken, createSkillOwnerMarkerContent, parseSharedOwnerToken, parseOwnedTextHeader, readSkillOwnerMarker, renderOwnedTextFile, inspectOwnedComponents, writeOwnedTextFile, } from '../utils/ownership.js';
15
15
  import { resolveArtifactRenderConfig, resolveArtifactRendererTarget, runArtifactRenderer, } from '../utils/artifact-renderer.js';
16
16
  import { createArtifactConfigDigest, validateArtifactPlan, } from '../utils/artifact-plan.js';
17
- import { resolveAwDeliveredArtifactIds } from '../utils/artifact-request.js';
18
- import { createAwPluginRequirementsReceipt, } from '../utils/manifest.js';
17
+ import { resolveAwDeliveredArtifactRequestBatches } from '../utils/artifact-request.js';
18
+ import { createAwPluginRequirementsReceipt, parseAwPluginRequirementsReceipt, tryParseStrictSemVer, } from '../utils/manifest.js';
19
19
  import { resolvePlatformEntries } from '../utils/platform-entries.js';
20
20
  import { resolveAwHome, resolveClaudePaths, resolveCodexPaths, resolveRepoRoot, getCodexHome, } from '../utils/paths.js';
21
21
  import { assertNoSensitiveConfigInFiles, collectSensitivePluginConfigValues, runPreApplyScript, } from '../utils/plugin-scripts.js';
22
22
  import { findSensitiveTokenPath } from '../utils/sensitive-content.js';
23
23
  import { atomicRemoveConfigFile, atomicReplaceConfigFile, withConfigFileMutation, } from '../utils/plugin-config.js';
24
24
  import { readCatalogSourcesConfigSnapshot } from '../utils/catalog-sources.js';
25
- import { getNamespacedMarketplaceName, projectMarketplaceManifestForPlatform, readStrictNamespacedMarketplaceRoot, replayNamespacedCodexSkillOverlays, } from '../utils/local-marketplace.js';
25
+ import { getNamespacedMarketplaceName, MarketplaceRootValidationError, projectMarketplaceManifestForPlatform, readStrictNamespacedMarketplaceRoot, replayNamespacedCodexNativeRenderProjections, replayNamespacedCodexSkillOverlays, } from '../utils/local-marketplace.js';
26
26
  import { commitMarketplaceGenerationBackup } from '../utils/orphan-marketplace-cleanup.js';
27
27
  import { readClaudeSettings, writeClaudeSettings } from '../adapters/helpers.js';
28
28
  import { analyzeCodexSurface } from '../utils/codex-surface-analysis.js';
@@ -39,6 +39,26 @@ function requirementsReceipt(plugin) {
39
39
  const receipt = createAwPluginRequirementsReceipt({ name: plugin.manifest.name, version: plugin.manifest.version }, plugin.manifest.requires?.aw.capabilities ?? []);
40
40
  return Buffer.from(`${JSON.stringify(receipt, null, 2)}\n`, 'utf8');
41
41
  }
42
+ function withoutVerifiedRequirementsReceipt(plugin, files) {
43
+ const receipt = files[AW_PLUGIN_REQUIREMENTS_RECEIPT_PATH];
44
+ if (!receipt)
45
+ return { ...files };
46
+ let actual;
47
+ let expected;
48
+ try {
49
+ actual = parseAwPluginRequirementsReceipt(JSON.parse(receipt.toString('utf8')));
50
+ expected = parseAwPluginRequirementsReceipt(JSON.parse(requirementsReceipt(plugin).toString('utf8')));
51
+ }
52
+ catch {
53
+ return { ...files };
54
+ }
55
+ if (JSON.stringify(actual) !== JSON.stringify(expected)) {
56
+ return { ...files };
57
+ }
58
+ const output = { ...files };
59
+ delete output[AW_PLUGIN_REQUIREMENTS_RECEIPT_PATH];
60
+ return output;
61
+ }
42
62
  export class CodexActivationPostcheckError extends Error {
43
63
  code = 'codexActivationPostcheckFailed';
44
64
  codexPluginIds;
@@ -924,25 +944,29 @@ function renderCodexRole(options) {
924
944
  ].join('\n');
925
945
  }
926
946
  async function resolveDynamicArtifacts(options) {
927
- const requestedArtifactIds = resolveAwDeliveredArtifactIds({
947
+ const requestBatches = resolveAwDeliveredArtifactRequestBatches({
928
948
  manifest: options.plugin.manifest,
929
949
  platform: options.platform,
930
950
  scope: options.scope,
931
951
  });
932
- if (requestedArtifactIds.length === 0) {
952
+ if (requestBatches.length === 0) {
933
953
  return { artifacts: [], sourceDirs: () => [], sensitiveValues: [] };
934
954
  }
935
- const render = await runArtifactRenderer({
936
- event: options.event,
937
- plugin: options.plugin,
938
- platform: options.platform,
939
- scope: options.scope,
940
- cwd: options.cwd,
941
- commandEnv: options.env,
942
- previousArtifacts: [...options.previousArtifacts],
943
- requestedArtifactIds,
944
- scriptTimeoutMs: options.scriptTimeoutMs,
945
- });
955
+ const previousArtifacts = [...options.previousArtifacts];
956
+ const renders = [];
957
+ for (const requestedArtifactIds of requestBatches) {
958
+ renders.push(await runArtifactRenderer({
959
+ event: options.event,
960
+ plugin: options.plugin,
961
+ platform: options.platform,
962
+ scope: options.scope,
963
+ cwd: options.cwd,
964
+ commandEnv: options.env,
965
+ previousArtifacts,
966
+ requestedArtifactIds,
967
+ scriptTimeoutMs: options.scriptTimeoutMs,
968
+ }));
969
+ }
946
970
  const config = await resolveArtifactRenderConfig({
947
971
  sourceId: options.plugin.sourceId,
948
972
  sourceIdentity: options.plugin.sourceIdentity,
@@ -953,19 +977,26 @@ async function resolveDynamicArtifacts(options) {
953
977
  cwd: options.cwd,
954
978
  env: options.env,
955
979
  });
956
- const artifacts = validateArtifactPlan({
957
- manifest: options.plugin.manifest,
958
- plan: render.plan,
959
- platform: options.platform,
960
- scope: options.scope,
961
- requestedArtifactIds: render.context.requestedArtifactIds,
962
- renderConfig: config,
963
- rendererDigest: render.target.rendererDigest,
964
- manifestDigest: render.target.manifestDigest,
965
- }).artifacts;
980
+ const artifacts = [];
981
+ const sourceDirsByArtifact = new Map();
982
+ for (const render of renders) {
983
+ artifacts.push(...validateArtifactPlan({
984
+ manifest: options.plugin.manifest,
985
+ plan: render.plan,
986
+ platform: options.platform,
987
+ scope: options.scope,
988
+ requestedArtifactIds: render.context.requestedArtifactIds,
989
+ renderConfig: config,
990
+ rendererDigest: render.target.rendererDigest,
991
+ manifestDigest: render.target.manifestDigest,
992
+ }).artifacts);
993
+ for (const source of render.context.artifactSources) {
994
+ sourceDirsByArtifact.set(`${source.id}\0${source.target}`, source.sourceDirs);
995
+ }
996
+ }
966
997
  return {
967
998
  artifacts,
968
- sourceDirs: (artifact) => render.context.artifactSources.find((entry) => entry.id === artifact.id && entry.target === artifact.target)?.sourceDirs ?? [],
999
+ sourceDirs: (artifact) => sourceDirsByArtifact.get(`${artifact.id}\0${artifact.target}`) ?? [],
969
1000
  sensitiveValues: collectSensitivePluginConfigValues(options.plugin.manifest, config.values),
970
1001
  };
971
1002
  }
@@ -1140,7 +1171,7 @@ async function buildClaudeWrites(options) {
1140
1171
  requirementsReceipt: requirementsReceipt(options.plugin),
1141
1172
  sourceRequirementsReceiptPolicy: 'allow',
1142
1173
  });
1143
- assertNoSensitiveConfigInFiles(tree.files, dynamic.sensitiveValues, 'artifactRendererSensitiveConfigExposed: Final dynamic Skill contains sensitive config.');
1174
+ assertNoSensitiveConfigInFiles(withoutVerifiedRequirementsReceipt(options.plugin, tree.files), dynamic.sensitiveValues, 'artifactRendererSensitiveConfigExposed: Final dynamic Skill contains sensitive config.');
1144
1175
  writes.push(dynamicSkillWrite({
1145
1176
  ...options,
1146
1177
  platform: 'claude',
@@ -1217,7 +1248,7 @@ async function buildCodexWrites(options) {
1217
1248
  requirementsReceipt: requirementsReceipt(options.plugin),
1218
1249
  sourceRequirementsReceiptPolicy: 'allow',
1219
1250
  });
1220
- assertNoSensitiveConfigInFiles(tree.files, dynamic.sensitiveValues, 'artifactRendererSensitiveConfigExposed: Final dynamic Skill contains sensitive config.');
1251
+ assertNoSensitiveConfigInFiles(withoutVerifiedRequirementsReceipt(options.plugin, tree.files), dynamic.sensitiveValues, 'artifactRendererSensitiveConfigExposed: Final dynamic Skill contains sensitive config.');
1221
1252
  writes.push(dynamicSkillWrite({
1222
1253
  ...options,
1223
1254
  platform: 'codex',
@@ -1782,6 +1813,8 @@ async function codexPayloadPathSafe(payloadRoot, allowedRoot = payloadRoot) {
1782
1813
  }
1783
1814
  return true;
1784
1815
  }
1816
+ class CodexNativeProjectionUnsafeError extends Error {
1817
+ }
1785
1818
  async function resolveCodexSkillsRoot(payloadRoot, rawSkillsRoot) {
1786
1819
  const normalizedSeparators = rawSkillsRoot.replace(/\\/gu, '/');
1787
1820
  const relativeRoot = path.posix.normalize(normalizedSeparators.replace(/^\.\//u, ''));
@@ -1791,11 +1824,11 @@ async function resolveCodexSkillsRoot(payloadRoot, rawSkillsRoot) {
1791
1824
  || relativeRoot.startsWith('../')
1792
1825
  || path.posix.isAbsolute(normalizedSeparators)
1793
1826
  || path.win32.isAbsolute(rawSkillsRoot)) {
1794
- throw new Error('codexNativeSkillsRootInvalid: Codex native Skills root is invalid.');
1827
+ throw new CodexNativeProjectionUnsafeError('codexNativeSkillsRootInvalid: Codex native Skills root is invalid.');
1795
1828
  }
1796
1829
  const root = path.join(payloadRoot, ...relativeRoot.split('/'));
1797
1830
  if (!await codexReadPathSafe(payloadRoot, root)) {
1798
- throw new Error('codexNativeSkillsRootInvalid: Codex native Skills root is unsafe.');
1831
+ throw new CodexNativeProjectionUnsafeError('codexNativeSkillsRootInvalid: Codex native Skills root is unsafe.');
1799
1832
  }
1800
1833
  return { relativeRoot, root };
1801
1834
  }
@@ -1810,16 +1843,45 @@ async function readCodexNativeRenderReceiptEvidence(payloadRoot) {
1810
1843
  if (!stats)
1811
1844
  return { state: 'absent' };
1812
1845
  if (!stats.isFile() || stats.isSymbolicLink())
1846
+ return { state: 'unsafe' };
1847
+ let content;
1848
+ try {
1849
+ content = await fs.readFile(receiptPath, 'utf8');
1850
+ }
1851
+ catch {
1813
1852
  return { state: 'unreadable' };
1853
+ }
1814
1854
  try {
1815
- return {
1816
- state: 'present',
1817
- receipt: parseCodexNativeRenderReceipt(await fs.readFile(receiptPath, 'utf8')),
1818
- };
1855
+ return { state: 'present', receipt: parseCodexNativeRenderReceipt(content) };
1856
+ }
1857
+ catch {
1858
+ return { state: 'invalid' };
1859
+ }
1860
+ }
1861
+ function codexNativeRenderEvidenceIssue(reason, diagnostic) {
1862
+ return { state: 'issue', reason, diagnostic };
1863
+ }
1864
+ async function readCodexNativeProjectionManifest(manifestPath) {
1865
+ let stats;
1866
+ try {
1867
+ stats = await lstatIfPresent(manifestPath);
1819
1868
  }
1820
1869
  catch {
1821
1870
  return { state: 'unreadable' };
1822
1871
  }
1872
+ if (!stats)
1873
+ return { state: 'missing' };
1874
+ if (!stats.isFile() || stats.isSymbolicLink())
1875
+ return { state: 'unsafe' };
1876
+ try {
1877
+ const value = await fs.readJson(manifestPath);
1878
+ return typeof value === 'object' && value !== null && !Array.isArray(value)
1879
+ ? { state: 'present', value: value }
1880
+ : { state: 'invalid' };
1881
+ }
1882
+ catch (error) {
1883
+ return { state: error instanceof SyntaxError ? 'invalid' : 'unreadable' };
1884
+ }
1823
1885
  }
1824
1886
  async function resolveCodexNativeRenderExpectedEvidence(options) {
1825
1887
  const logicalPluginId = options.plugin.pluginId
@@ -1830,54 +1892,101 @@ async function resolveCodexNativeRenderExpectedEvidence(options) {
1830
1892
  const marketplace = await readStrictNamespacedMarketplaceRoot({
1831
1893
  trustedMarketplaceRoot: path.join(resolveAwHome(options.env), 'marketplaces'),
1832
1894
  rootDir: options.marketplaceRoot,
1833
- sourceId: options.plugin.sourceId,
1834
- marketplaceName: options.marketplaceName,
1835
1895
  });
1836
1896
  if (!marketplace) {
1837
- throw new Error('Codex native render canonical marketplace generation is missing.');
1897
+ return codexNativeRenderEvidenceIssue('damaged', 'projectionReceiptMissing');
1838
1898
  }
1839
- if (marketplace.config.sourceIdentity !== options.plugin.sourceIdentity) {
1840
- return { state: 'foreign' };
1899
+ if (marketplace.config.sourceId !== options.plugin.sourceId
1900
+ || marketplace.config.sourceIdentity !== options.plugin.sourceIdentity
1901
+ || marketplace.config.marketplaceName !== options.marketplaceName) {
1902
+ return codexNativeRenderEvidenceIssue('foreign', 'projectionReceiptIdentityMismatch');
1841
1903
  }
1842
1904
  const payloadRoot = path.join(options.marketplaceRoot, 'plugins', options.plugin.manifest.name);
1843
- if (!await codexPayloadPathSafe(payloadRoot, options.marketplaceRoot)) {
1844
- throw new Error('Codex native render canonical marketplace payload path is unsafe.');
1845
- }
1846
- const receiptEvidence = await readCodexNativeRenderReceiptEvidence(payloadRoot);
1847
- if (receiptEvidence.state !== 'present') {
1848
- throw new Error('Codex native render canonical marketplace receipt is missing or unreadable.');
1905
+ const payloadStats = await lstatIfPresent(payloadRoot);
1906
+ if (!payloadStats) {
1907
+ return codexNativeRenderEvidenceIssue('damaged', 'projectionReceiptMissing');
1849
1908
  }
1850
- const receipt = receiptEvidence.receipt;
1851
- if (receipt.logicalPluginId !== logicalPluginId
1852
- || receipt.strategy !== 'shared-user'
1853
- || receipt.sourceIdentity !== marketplace.config.sourceIdentity) {
1854
- throw new Error('Codex native render canonical marketplace receipt identity is invalid.');
1909
+ if (!payloadStats.isDirectory() || payloadStats.isSymbolicLink()) {
1910
+ return codexNativeRenderEvidenceIssue('foreign', 'payloadPathUnsafe');
1855
1911
  }
1856
- const payloadDigest = await calculateCodexNativeRenderPayloadDigest(payloadRoot);
1857
- if (payloadDigest !== receipt.payloadDigest) {
1858
- throw new Error('Codex native render canonical marketplace payload does not match its receipt.');
1859
- }
1860
- const sourceManifestPath = path.join(payloadRoot, '.agents-plugin', 'plugin.json');
1861
- const sourceManifestStats = await lstatIfPresent(sourceManifestPath);
1862
- const sourceManifest = sourceManifestStats?.isFile() && !sourceManifestStats.isSymbolicLink()
1863
- ? await fs.readJson(sourceManifestPath).catch(() => null)
1864
- : null;
1865
- if (sourceManifest?.name !== options.plugin.manifest.name
1866
- || sourceManifest.version !== receipt.pluginVersion) {
1867
- throw new Error('Codex native render canonical marketplace plugin identity is invalid.');
1912
+ if (!await codexPayloadPathSafe(payloadRoot, options.marketplaceRoot)) {
1913
+ return codexNativeRenderEvidenceIssue('foreign', 'payloadPathUnsafe');
1868
1914
  }
1869
1915
  const codexManifestPath = path.join(payloadRoot, '.codex-plugin', 'plugin.json');
1870
- const codexManifestStats = await lstatIfPresent(codexManifestPath);
1871
- const codexManifest = codexManifestStats?.isFile() && !codexManifestStats.isSymbolicLink()
1872
- ? await fs.readJson(codexManifestPath).catch(() => null)
1873
- : null;
1874
- if (!codexManifest) {
1875
- throw new Error('Codex native render canonical marketplace manifest is invalid.');
1916
+ const [sourceManifestEvidence, codexManifestEvidence] = await Promise.all([
1917
+ readCodexNativeProjectionManifest(path.join(payloadRoot, '.agents-plugin', 'plugin.json')),
1918
+ readCodexNativeProjectionManifest(codexManifestPath),
1919
+ ]);
1920
+ if (sourceManifestEvidence.state === 'unsafe'
1921
+ || codexManifestEvidence.state === 'unsafe') {
1922
+ return codexNativeRenderEvidenceIssue('foreign', 'payloadPathUnsafe');
1923
+ }
1924
+ if ((sourceManifestEvidence.state === 'present'
1925
+ && sourceManifestEvidence.value.name !== options.plugin.manifest.name)
1926
+ || (codexManifestEvidence.state === 'present'
1927
+ && codexManifestEvidence.value.name !== options.plugin.manifest.name)
1928
+ || (sourceManifestEvidence.state === 'present'
1929
+ && codexManifestEvidence.state === 'present'
1930
+ && sourceManifestEvidence.value.version !== codexManifestEvidence.value.version)) {
1931
+ return codexNativeRenderEvidenceIssue('foreign', 'manifestIdentityMismatch');
1932
+ }
1933
+ if (sourceManifestEvidence.state === 'unreadable'
1934
+ || codexManifestEvidence.state === 'unreadable') {
1935
+ return codexNativeRenderEvidenceIssue('unreadable', 'manifestUnreadable');
1936
+ }
1937
+ if (sourceManifestEvidence.state === 'missing'
1938
+ || codexManifestEvidence.state === 'missing') {
1939
+ return codexNativeRenderEvidenceIssue('damaged', 'manifestMissing');
1940
+ }
1941
+ if (sourceManifestEvidence.state === 'invalid'
1942
+ || codexManifestEvidence.state === 'invalid') {
1943
+ return codexNativeRenderEvidenceIssue('damaged', 'manifestUnreadable');
1944
+ }
1945
+ if (sourceManifestEvidence.state !== 'present'
1946
+ || codexManifestEvidence.state !== 'present') {
1947
+ return codexNativeRenderEvidenceIssue('damaged', 'manifestUnreadable');
1948
+ }
1949
+ const sourceManifest = sourceManifestEvidence.value;
1950
+ const codexManifest = codexManifestEvidence.value;
1951
+ if (typeof sourceManifest.version !== 'string'
1952
+ || !tryParseStrictSemVer(sourceManifest.version)) {
1953
+ return codexNativeRenderEvidenceIssue('damaged', 'manifestVersionInvalid');
1876
1954
  }
1877
1955
  const rawSkillsRoot = typeof codexManifest.skills === 'string' && codexManifest.skills.trim() !== ''
1878
1956
  ? codexManifest.skills
1879
1957
  : './codex/skills';
1880
1958
  const { relativeRoot: skillsRelativeRoot } = await resolveCodexSkillsRoot(payloadRoot, rawSkillsRoot);
1959
+ const skillsRoot = path.join(payloadRoot, ...skillsRelativeRoot.split('/'));
1960
+ const skillsRootStats = await lstatIfPresent(skillsRoot);
1961
+ if (skillsRootStats && (!skillsRootStats.isDirectory() || skillsRootStats.isSymbolicLink())) {
1962
+ return codexNativeRenderEvidenceIssue('foreign', 'payloadPathUnsafe');
1963
+ }
1964
+ const targets = codexNativeRenderTargets(options.plugin);
1965
+ for (const target of targets) {
1966
+ if (await lstatIfPresent(path.join(skillsRoot, target.skillKey, '.agent-workflows-owner.json'))) {
1967
+ return codexNativeRenderEvidenceIssue('foreign', 'payloadPathUnsafe');
1968
+ }
1969
+ }
1970
+ const receiptEvidence = await readCodexNativeRenderReceiptEvidence(payloadRoot);
1971
+ if (receiptEvidence.state === 'absent') {
1972
+ return codexNativeRenderEvidenceIssue('damaged', 'projectionReceiptMissing');
1973
+ }
1974
+ if (receiptEvidence.state === 'unsafe') {
1975
+ return codexNativeRenderEvidenceIssue('foreign', 'payloadPathUnsafe');
1976
+ }
1977
+ if (receiptEvidence.state === 'invalid') {
1978
+ return codexNativeRenderEvidenceIssue('damaged', 'projectionReceiptUnreadable');
1979
+ }
1980
+ if (receiptEvidence.state === 'unreadable') {
1981
+ return codexNativeRenderEvidenceIssue('unreadable', 'projectionReceiptUnreadable');
1982
+ }
1983
+ const receipt = receiptEvidence.receipt;
1984
+ if (receipt.logicalPluginId !== logicalPluginId
1985
+ || receipt.strategy !== 'shared-user'
1986
+ || receipt.sourceIdentity !== marketplace.config.sourceIdentity
1987
+ || receipt.pluginVersion !== sourceManifest.version) {
1988
+ return codexNativeRenderEvidenceIssue('foreign', 'projectionReceiptIdentityMismatch');
1989
+ }
1881
1990
  const emptyArtifactDigest = createCodexSkillStagingTreeContentDigest({
1882
1991
  skillKey: 'empty',
1883
1992
  files: {},
@@ -1885,22 +1994,37 @@ async function resolveCodexNativeRenderExpectedEvidence(options) {
1885
1994
  for (const receiptTarget of receipt.targets) {
1886
1995
  const skillRoot = path.join(payloadRoot, ...skillsRelativeRoot.split('/'), receiptTarget.skillKey);
1887
1996
  if (await lstatIfPresent(path.join(skillRoot, '.agent-workflows-owner.json'))) {
1888
- throw new Error('Codex native render canonical marketplace Skill has an AW owner marker.');
1997
+ return codexNativeRenderEvidenceIssue('foreign', 'payloadPathUnsafe');
1998
+ }
1999
+ const skillStats = await lstatIfPresent(skillRoot);
2000
+ if (skillStats && (!skillStats.isDirectory() || skillStats.isSymbolicLink())) {
2001
+ return codexNativeRenderEvidenceIssue('foreign', 'payloadPathUnsafe');
2002
+ }
2003
+ let tree;
2004
+ try {
2005
+ tree = skillStats ? await readDynamicSkillTree(skillRoot, 'codex.skill') : null;
2006
+ }
2007
+ catch (error) {
2008
+ return codexNativeRenderEvidenceIssue(error instanceof Error && typeof error.code !== 'string'
2009
+ ? 'damaged'
2010
+ : 'unreadable', 'projectionPayloadDigestMismatch');
1889
2011
  }
1890
- const tree = await readDynamicSkillTree(skillRoot, 'codex.skill');
1891
2012
  const artifactDigest = tree
1892
2013
  ? createCodexSkillStagingTreeContentDigest(tree)
1893
2014
  : emptyArtifactDigest;
1894
2015
  if (artifactDigest !== receiptTarget.artifactDigest) {
1895
- throw new Error('Codex native render canonical marketplace Skill does not match its receipt.');
2016
+ return codexNativeRenderEvidenceIssue('damaged', 'projectionPayloadDigestMismatch');
1896
2017
  }
1897
2018
  }
1898
- const targets = codexNativeRenderTargets(options.plugin);
2019
+ const payloadDigest = await calculateCodexNativeRenderPayloadDigest(payloadRoot);
2020
+ if (payloadDigest !== receipt.payloadDigest) {
2021
+ return codexNativeRenderEvidenceIssue('damaged', 'projectionPayloadDigestMismatch');
2022
+ }
1899
2023
  const receiptTargets = new Map(receipt.targets.map((target) => [target.id, target]));
1900
2024
  if (targets.length === 0
1901
2025
  || targets.length !== receipt.targets.length
1902
2026
  || targets.some((target) => receiptTargets.get(target.id)?.skillKey !== target.skillKey)) {
1903
- return { state: 'present', payloadDigest, sourceProjection: 'drifted' };
2027
+ return { state: 'present', payloadDigest, receipt, sourceProjection: 'drifted' };
1904
2028
  }
1905
2029
  const renderer = await resolveArtifactRendererTarget(options.plugin);
1906
2030
  if (renderer.noOp || !renderer.rendererDigest) {
@@ -1934,7 +2058,7 @@ async function resolveCodexNativeRenderExpectedEvidence(options) {
1934
2058
  target: 'codex.skill',
1935
2059
  refreshKeys: target.refreshKeys,
1936
2060
  },
1937
- configValues: config.values,
2061
+ renderConfig: config,
1938
2062
  digestSalt: renderer.rendererDigest,
1939
2063
  }),
1940
2064
  artifactDigest: receiptTargets.get(target.id).artifactDigest,
@@ -1942,6 +2066,7 @@ async function resolveCodexNativeRenderExpectedEvidence(options) {
1942
2066
  });
1943
2067
  return {
1944
2068
  state: 'present',
2069
+ receipt,
1945
2070
  projection,
1946
2071
  payloadDigest,
1947
2072
  sourceProjection: codexNativeRenderProjectionsEqual(receipt, projection)
@@ -1966,11 +2091,31 @@ async function assertNoSensitiveValuesInCanonicalCodexNativePayload(options) {
1966
2091
  throw new Error('Codex native sensitive scan canonical marketplace path is unsafe.');
1967
2092
  }
1968
2093
  const payloadRoot = path.join(options.marketplaceRoot, 'plugins', options.plugin.manifest.name);
2094
+ if (!await codexPayloadPathSafe(payloadRoot, options.marketplaceRoot)) {
2095
+ throw new Error('Codex native sensitive scan payload path is unsafe.');
2096
+ }
2097
+ const receiptEvidence = await readCodexNativeRenderReceiptEvidence(payloadRoot);
2098
+ if (receiptEvidence.state === 'unsafe') {
2099
+ throw new Error('codexNativeRenderReceiptInvalid: Codex native render receipt path is unsafe.');
2100
+ }
2101
+ let verifiedRenderReceipt;
2102
+ if (receiptEvidence.state === 'present') {
2103
+ const expected = await resolveCodexNativeRenderExpectedEvidence({
2104
+ plugin: options.plugin,
2105
+ marketplaceRoot: options.marketplaceRoot,
2106
+ marketplaceName: options.marketplaceName,
2107
+ env: options.env,
2108
+ }).catch(() => undefined);
2109
+ if (expected?.state === 'present' && expected.sourceProjection === 'current') {
2110
+ verifiedRenderReceipt = expected.receipt;
2111
+ }
2112
+ }
1969
2113
  await assertNoSensitiveValuesInCodexNativePayload({
1970
2114
  plugin: options.plugin,
1971
2115
  payloadRoot,
1972
2116
  allowedRoot: options.marketplaceRoot,
1973
2117
  sensitiveValues: options.sensitiveValues,
2118
+ verifiedRenderReceipt,
1974
2119
  });
1975
2120
  }
1976
2121
  function observedCodexNativeState(observation, operation) {
@@ -2053,7 +2198,7 @@ export async function inspectCodexSharedNativeLive(options) {
2053
2198
  });
2054
2199
  let expectedPayloadDigest = dynamicProjectionExpected ? undefined : options.expectedPayloadDigest;
2055
2200
  let sourceProjection;
2056
- let expectedMarketplaceForeign = false;
2201
+ let sourceProjectionIssue;
2057
2202
  const installed = pluginList.observation.state === 'observed'
2058
2203
  && pluginList.observation.value === 'installed';
2059
2204
  if (installed && dynamicProjectionExpected && options.nativeRenderPlugin) {
@@ -2064,32 +2209,48 @@ export async function inspectCodexSharedNativeLive(options) {
2064
2209
  marketplaceName,
2065
2210
  env: options.env,
2066
2211
  });
2067
- if (expected.state === 'foreign') {
2068
- expectedMarketplaceForeign = true;
2212
+ if (expected.state === 'issue') {
2213
+ sourceProjectionIssue = expected;
2069
2214
  }
2070
2215
  else {
2071
2216
  expectedPayloadDigest = expected.payloadDigest;
2072
2217
  sourceProjection = expected.sourceProjection;
2073
2218
  }
2074
2219
  }
2075
- catch {
2076
- sourceProjection = 'drifted';
2220
+ catch (error) {
2221
+ if (error instanceof MarketplaceRootValidationError) {
2222
+ sourceProjectionIssue = {
2223
+ reason: error.reason === 'unsafe'
2224
+ ? 'foreign'
2225
+ : error.reason === 'invalid' ? 'damaged' : 'unreadable',
2226
+ diagnostic: error.reason === 'unsafe'
2227
+ ? 'payloadPathUnsafe'
2228
+ : 'projectionReceiptUnreadable',
2229
+ };
2230
+ }
2231
+ else {
2232
+ sourceProjectionIssue = {
2233
+ reason: error instanceof CodexNativeProjectionUnsafeError ? 'foreign' : 'unreadable',
2234
+ diagnostic: error instanceof CodexNativeProjectionUnsafeError
2235
+ ? 'payloadPathUnsafe'
2236
+ : 'projectionReceiptUnreadable',
2237
+ };
2238
+ }
2077
2239
  }
2078
2240
  }
2079
- const sharedNative = expectedMarketplaceForeign
2080
- ? { state: 'blocked', reason: 'foreign', diagnostic: 'marketplaceRegistrationForeign' }
2081
- : inspectCodexSharedNative({
2082
- pluginName: options.pluginName,
2083
- pluginId,
2084
- marketplaceName,
2085
- canonicalMarketplaceRoot: marketplaceRoot,
2086
- desiredVersion: options.desiredVersion,
2087
- expectedPayloadDigest,
2088
- marketplaceRegistration: config.snapshot.marketplaces[marketplaceName],
2089
- marketplacePathSafe,
2090
- observation: pluginList.observation,
2091
- ...(sourceProjection ? { sourceProjection } : {}),
2092
- });
2241
+ const sharedNative = inspectCodexSharedNative({
2242
+ pluginName: options.pluginName,
2243
+ pluginId,
2244
+ marketplaceName,
2245
+ canonicalMarketplaceRoot: marketplaceRoot,
2246
+ desiredVersion: options.desiredVersion,
2247
+ expectedPayloadDigest,
2248
+ marketplaceRegistration: config.snapshot.marketplaces[marketplaceName],
2249
+ marketplacePathSafe,
2250
+ observation: pluginList.observation,
2251
+ ...(sourceProjection ? { sourceProjection } : {}),
2252
+ ...(sourceProjectionIssue ? { sourceProjectionIssue } : {}),
2253
+ });
2093
2254
  return {
2094
2255
  sharedNative,
2095
2256
  ...(expectedPayloadDigest ? { expectedPayloadDigest } : {}),
@@ -2522,7 +2683,7 @@ async function assertNoSensitiveValuesInCodexNativePayload(options) {
2522
2683
  const tree = await readDynamicSkillTree(skillRoot, 'codex.skill');
2523
2684
  if (!tree)
2524
2685
  continue;
2525
- const leakedPath = findSensitiveTokenPath(tree.files, options.sensitiveValues);
2686
+ const leakedPath = findSensitiveTokenPath(withoutVerifiedRequirementsReceipt(options.plugin, tree.files), options.sensitiveValues);
2526
2687
  if (leakedPath) {
2527
2688
  throw new Error(`codexNativeRenderSensitiveValue: Codex native Skill "${entry.name}" exposes ` +
2528
2689
  `sensitive config in "${leakedPath}".`);
@@ -2535,10 +2696,22 @@ async function assertNoSensitiveValuesInCodexNativePayload(options) {
2535
2696
  if (!receiptStats.isFile()) {
2536
2697
  throw new Error('Codex native sensitive scan render receipt path is unsafe.');
2537
2698
  }
2538
- const receiptLeak = findSensitiveTokenPath({ [CODEX_NATIVE_RENDER_RECEIPT_PATH]: await fs.readFile(receiptPath) }, options.sensitiveValues);
2539
- if (receiptLeak) {
2540
- throw new Error(`codexNativeRenderSensitiveValue: Codex native render receipt exposes sensitive config in ` +
2541
- `"${receiptLeak}".`);
2699
+ const receiptContent = await fs.readFile(receiptPath);
2700
+ let actualReceipt;
2701
+ try {
2702
+ actualReceipt = parseCodexNativeRenderReceipt(receiptContent.toString('utf8'));
2703
+ }
2704
+ catch {
2705
+ actualReceipt = undefined;
2706
+ }
2707
+ const verified = options.verifiedRenderReceipt
2708
+ && JSON.stringify(actualReceipt) === JSON.stringify(options.verifiedRenderReceipt);
2709
+ if (!verified) {
2710
+ const receiptLeak = findSensitiveTokenPath({ [CODEX_NATIVE_RENDER_RECEIPT_PATH]: receiptContent }, options.sensitiveValues);
2711
+ if (receiptLeak) {
2712
+ throw new Error(`codexNativeRenderSensitiveValue: Codex native render receipt exposes sensitive config in ` +
2713
+ `"${receiptLeak}".`);
2714
+ }
2542
2715
  }
2543
2716
  }
2544
2717
  }
@@ -2596,7 +2769,7 @@ async function prepareCodexNativeRender(options) {
2596
2769
  requirementsReceipt: requirementsReceipt(options.plugin),
2597
2770
  sourceRequirementsReceiptPolicy: 'allow',
2598
2771
  });
2599
- assertNoSensitiveConfigInFiles(tree.files, collectSensitivePluginConfigValues(options.plugin.manifest, rendered.context.config.values), 'artifactRendererSensitiveConfigExposed: Final dynamic Skill contains sensitive config.');
2772
+ assertNoSensitiveConfigInFiles(withoutVerifiedRequirementsReceipt(options.plugin, tree.files), collectSensitivePluginConfigValues(options.plugin.manifest, rendered.context.config.values), 'artifactRendererSensitiveConfigExposed: Final dynamic Skill contains sensitive config.');
2600
2773
  const skillKey = artifact.skillKey;
2601
2774
  renderedArtifacts.push({
2602
2775
  artifact,
@@ -2693,11 +2866,12 @@ async function materializeCodexNativeRender(options) {
2693
2866
  return {
2694
2867
  payloadDigest,
2695
2868
  projection: options.prepared.projection,
2869
+ receipt,
2696
2870
  presentSkillKeys: options.prepared.presentSkillKeys,
2697
2871
  targetSkillKeys: options.prepared.targetSkillKeys,
2698
2872
  };
2699
2873
  }
2700
- async function mirrorCatalog(catalog, env, target, logicalIdentity, prepareStaging) {
2874
+ async function mirrorCatalog(catalog, env, target, logicalIdentity, prepareStaging, rematerializedCodexNativePluginIds = new Set()) {
2701
2875
  if (!target?.sourceId || !target.marketplaceRoot) {
2702
2876
  const rootDir = resolveAwHome(env);
2703
2877
  await fs.ensureDir(rootDir);
@@ -2761,6 +2935,14 @@ async function mirrorCatalog(catalog, env, target, logicalIdentity, prepareStagi
2761
2935
  sourceIdentity: target.sourceIdentity,
2762
2936
  marketplaceName,
2763
2937
  });
2938
+ await replayNamespacedCodexNativeRenderProjections({
2939
+ sourceRoot: destination,
2940
+ stagingRoot: staging,
2941
+ sourceId: target.sourceId,
2942
+ sourceIdentity: target.sourceIdentity,
2943
+ marketplaceName,
2944
+ rematerializedPluginIds: rematerializedCodexNativePluginIds,
2945
+ });
2764
2946
  }
2765
2947
  await prepareStaging?.(staging);
2766
2948
  const backup = `${destination}.backup-${crypto.randomUUID()}`;
@@ -3085,10 +3267,15 @@ function createNativeDriver(options) {
3085
3267
  payloadRoot: path.join(stagingRoot, 'plugins', candidate.identity.pluginName),
3086
3268
  allowedRoot: stagingRoot,
3087
3269
  sensitiveValues: await resolveSensitiveUserConfigValues(candidate.plugin, options.context.env),
3270
+ ...(nativeRender ? { verifiedRenderReceipt: nativeRender.receipt } : {}),
3088
3271
  });
3089
3272
  }
3090
3273
  }
3091
- } : undefined);
3274
+ } : undefined, new Set(first.identity.platform === 'codex'
3275
+ ? materializationTargets.flatMap((candidate) => pluginHasCodexNativeSkillSurface(candidate.plugin)
3276
+ ? [candidate.identity.pluginId ?? `${candidate.identity.sourceId}/${candidate.identity.pluginName}`]
3277
+ : [])
3278
+ : []));
3092
3279
  try {
3093
3280
  await transaction.commit();
3094
3281
  }
@@ -3478,6 +3665,7 @@ function createNativeDriver(options) {
3478
3665
  await assertNoSensitiveValuesInCanonicalCodexNativePayload({
3479
3666
  plugin: target.plugin,
3480
3667
  marketplaceRoot: target.identity.marketplaceRoot ?? resolveAwHome(options.context.env),
3668
+ marketplaceName: target.identity.marketplaceName ?? MARKETPLACE_NAME,
3481
3669
  env: options.context.env,
3482
3670
  sensitiveValues,
3483
3671
  });
@@ -3803,7 +3991,7 @@ function createNativeDriver(options) {
3803
3991
  return target.artifacts;
3804
3992
  },
3805
3993
  runPreApply: async (invocation) => {
3806
- await runPreApplyScript({
3994
+ const run = () => runPreApplyScript({
3807
3995
  plugin: invocation.plugin,
3808
3996
  platform: invocation.target.identity.platform,
3809
3997
  scope: invocation.target.identity.scope,
@@ -3811,6 +3999,36 @@ function createNativeDriver(options) {
3811
3999
  context: options.context,
3812
4000
  scriptTimeoutMs: options.scriptTimeoutMs,
3813
4001
  });
4002
+ const cache = options.context.preApplyExecutionCache;
4003
+ if (!cache) {
4004
+ await run();
4005
+ return;
4006
+ }
4007
+ const { identity } = invocation.target;
4008
+ const sourceIdentity = invocation.plugin.sourceIdentity ?? identity.sourceIdentity;
4009
+ const pluginId = invocation.plugin.pluginId ?? identity.pluginId;
4010
+ if (!sourceIdentity || !pluginId) {
4011
+ await run();
4012
+ return;
4013
+ }
4014
+ const projectKey = identity.scope === 'project'
4015
+ ? identity.platform === 'codex'
4016
+ ? await resolveCodexProjectKey(identity.cwd)
4017
+ : await resolveClaudeProjectKey(identity.cwd)
4018
+ : '';
4019
+ const key = [
4020
+ sourceIdentity,
4021
+ pluginId,
4022
+ identity.platform,
4023
+ identity.scope,
4024
+ projectKey,
4025
+ ].join('\0');
4026
+ let execution = cache.get(key);
4027
+ if (!execution) {
4028
+ execution = run();
4029
+ cache.set(key, execution);
4030
+ }
4031
+ await execution;
3814
4032
  },
3815
4033
  };
3816
4034
  }