@open-agent-toolkit/cli 0.2.51 → 0.2.53

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/assets/bundle-metadata.json +1 -1
  2. package/assets/docs/cli-utilities/bootstrap.md +6 -2
  3. package/assets/docs/cli-utilities/tool-packs.md +87 -33
  4. package/assets/docs/provider-sync/commands.md +30 -2
  5. package/assets/docs/provider-sync/config.md +19 -0
  6. package/assets/docs/provider-sync/manifest-and-drift.md +37 -0
  7. package/assets/docs/provider-sync/providers.md +69 -2
  8. package/assets/docs/provider-sync/scope-and-surface.md +44 -0
  9. package/assets/docs/reference/cli-reference.md +1 -0
  10. package/assets/docs/reference/oat-directory-structure.md +11 -4
  11. package/assets/docs/workflows/projects/evidence-layers.md +110 -1
  12. package/assets/docs/workflows/projects/implementation-execution.md +13 -0
  13. package/assets/docs/workflows/projects/orchestration-model.md +18 -0
  14. package/assets/public-package-versions.json +4 -4
  15. package/assets/skills/oat-dispatch-subagents/SKILL.md +57 -1
  16. package/assets/skills/oat-dispatch-subagents/references/record-schema.md +77 -1
  17. package/assets/skills/oat-project-autonomous/references/gate-inventory.md +1 -1
  18. package/assets/skills/oat-project-dispatch-subagents/SKILL.md +24 -1
  19. package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +1 -1
  20. package/assets/skills/oat-project-implement/references/dispatch-and-dry-run.md +11 -0
  21. package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +1 -1
  22. package/assets/skills/oat-project-plan-writing/SKILL.md +12 -1
  23. package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +1 -1
  24. package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +1 -1
  25. package/assets/skills/oat-project-review-provide/SKILL.md +12 -1
  26. package/assets/skills/oat-project-review-provide-remote/SKILL.md +12 -1
  27. package/dist/agents/canonical/index.d.ts +2 -0
  28. package/dist/agents/canonical/index.d.ts.map +1 -1
  29. package/dist/agents/canonical/index.js +1 -0
  30. package/dist/agents/canonical/resolve.d.ts +45 -0
  31. package/dist/agents/canonical/resolve.d.ts.map +1 -0
  32. package/dist/agents/canonical/resolve.js +175 -0
  33. package/dist/commands/decision/agents-guidance.d.ts +2 -2
  34. package/dist/commands/decision/agents-guidance.d.ts.map +1 -1
  35. package/dist/commands/decision/agents-guidance.js +1 -4
  36. package/dist/commands/decision/index.d.ts.map +1 -1
  37. package/dist/commands/decision/index.js +25 -6
  38. package/dist/commands/docs/init/index.d.ts +7 -1
  39. package/dist/commands/docs/init/index.d.ts.map +1 -1
  40. package/dist/commands/docs/init/index.js +53 -20
  41. package/dist/commands/doctor/index.d.ts +8 -2
  42. package/dist/commands/doctor/index.d.ts.map +1 -1
  43. package/dist/commands/doctor/index.js +157 -46
  44. package/dist/commands/init/index.d.ts +8 -3
  45. package/dist/commands/init/index.d.ts.map +1 -1
  46. package/dist/commands/init/index.js +66 -28
  47. package/dist/commands/init/tools/index.d.ts +12 -12
  48. package/dist/commands/init/tools/index.d.ts.map +1 -1
  49. package/dist/commands/init/tools/index.js +430 -99
  50. package/dist/commands/init/tools/install-state.d.ts +2 -0
  51. package/dist/commands/init/tools/install-state.d.ts.map +1 -1
  52. package/dist/commands/init/tools/install-state.js +11 -0
  53. package/dist/commands/init/tools/project-guidance.d.ts +49 -0
  54. package/dist/commands/init/tools/project-guidance.d.ts.map +1 -0
  55. package/dist/commands/init/tools/project-guidance.js +144 -0
  56. package/dist/commands/init/tools/workflows/index.d.ts +7 -0
  57. package/dist/commands/init/tools/workflows/index.d.ts.map +1 -1
  58. package/dist/commands/init/tools/workflows/index.js +160 -7
  59. package/dist/commands/pjm/index.d.ts.map +1 -1
  60. package/dist/commands/pjm/index.js +28 -3
  61. package/dist/commands/pjm/init.d.ts +5 -0
  62. package/dist/commands/pjm/init.d.ts.map +1 -1
  63. package/dist/commands/pjm/init.js +19 -3
  64. package/dist/commands/project/dispatch/index.d.ts +10 -0
  65. package/dist/commands/project/dispatch/index.d.ts.map +1 -0
  66. package/dist/commands/project/dispatch/index.js +137 -0
  67. package/dist/commands/project/dispatch/record.d.ts +91 -0
  68. package/dist/commands/project/dispatch/record.d.ts.map +1 -0
  69. package/dist/commands/project/dispatch/record.js +402 -0
  70. package/dist/commands/project/index.d.ts.map +1 -1
  71. package/dist/commands/project/index.js +2 -0
  72. package/dist/commands/providers/inspect/inspect.d.ts +8 -1
  73. package/dist/commands/providers/inspect/inspect.d.ts.map +1 -1
  74. package/dist/commands/providers/inspect/inspect.js +146 -26
  75. package/dist/commands/providers/list/list.d.ts +8 -1
  76. package/dist/commands/providers/list/list.d.ts.map +1 -1
  77. package/dist/commands/providers/list/list.js +139 -23
  78. package/dist/commands/providers/providers.types.d.ts +25 -1
  79. package/dist/commands/providers/providers.types.d.ts.map +1 -1
  80. package/dist/commands/providers/set/index.d.ts +9 -1
  81. package/dist/commands/providers/set/index.d.ts.map +1 -1
  82. package/dist/commands/providers/set/index.js +43 -33
  83. package/dist/commands/remove/skill/remove-skill.d.ts.map +1 -1
  84. package/dist/commands/remove/skill/remove-skill.js +11 -0
  85. package/dist/commands/shared/agents-md.d.ts +42 -14
  86. package/dist/commands/shared/agents-md.d.ts.map +1 -1
  87. package/dist/commands/shared/agents-md.js +364 -70
  88. package/dist/commands/status/index.d.ts +8 -3
  89. package/dist/commands/status/index.d.ts.map +1 -1
  90. package/dist/commands/status/index.js +149 -37
  91. package/dist/commands/sync/apply.d.ts.map +1 -1
  92. package/dist/commands/sync/apply.js +220 -29
  93. package/dist/commands/sync/dry-run.d.ts.map +1 -1
  94. package/dist/commands/sync/dry-run.js +30 -6
  95. package/dist/commands/sync/index.d.ts +17 -2
  96. package/dist/commands/sync/index.d.ts.map +1 -1
  97. package/dist/commands/sync/index.js +108 -43
  98. package/dist/commands/sync/sync.types.d.ts +39 -2
  99. package/dist/commands/sync/sync.types.d.ts.map +1 -1
  100. package/dist/commands/sync/sync.utils.d.ts +5 -1
  101. package/dist/commands/sync/sync.utils.d.ts.map +1 -1
  102. package/dist/commands/sync/sync.utils.js +73 -0
  103. package/dist/commands/tools/info/info-tool.d.ts +2 -0
  104. package/dist/commands/tools/info/info-tool.d.ts.map +1 -1
  105. package/dist/commands/tools/info/info-tool.js +34 -12
  106. package/dist/commands/tools/install/index.d.ts.map +1 -1
  107. package/dist/commands/tools/install/index.js +19 -3
  108. package/dist/commands/tools/list/list-tools.d.ts +2 -0
  109. package/dist/commands/tools/list/list-tools.d.ts.map +1 -1
  110. package/dist/commands/tools/list/list-tools.js +36 -7
  111. package/dist/commands/tools/remove/index.d.ts.map +1 -1
  112. package/dist/commands/tools/remove/index.js +150 -21
  113. package/dist/commands/tools/remove/remove-tools.d.ts +20 -0
  114. package/dist/commands/tools/remove/remove-tools.d.ts.map +1 -1
  115. package/dist/commands/tools/remove/remove-tools.js +122 -1
  116. package/dist/commands/tools/shared/format-pack-inventory.d.ts +19 -0
  117. package/dist/commands/tools/shared/format-pack-inventory.d.ts.map +1 -1
  118. package/dist/commands/tools/shared/format-pack-inventory.js +109 -0
  119. package/dist/commands/tools/shared/pack-evidence.d.ts +66 -0
  120. package/dist/commands/tools/shared/pack-evidence.d.ts.map +1 -0
  121. package/dist/commands/tools/shared/pack-evidence.js +132 -0
  122. package/dist/commands/tools/shared/pack-inventory.d.ts +1 -0
  123. package/dist/commands/tools/shared/pack-inventory.d.ts.map +1 -1
  124. package/dist/commands/tools/shared/pack-lifecycle-outcome.d.ts +44 -0
  125. package/dist/commands/tools/shared/pack-lifecycle-outcome.d.ts.map +1 -0
  126. package/dist/commands/tools/shared/pack-lifecycle-outcome.js +91 -0
  127. package/dist/commands/tools/update/index.d.ts.map +1 -1
  128. package/dist/commands/tools/update/index.js +74 -4
  129. package/dist/commands/tools/update/update-tools.d.ts +5 -0
  130. package/dist/commands/tools/update/update-tools.d.ts.map +1 -1
  131. package/dist/commands/tools/update/update-tools.js +64 -2
  132. package/dist/config/oat-config.d.ts +5 -0
  133. package/dist/config/oat-config.d.ts.map +1 -1
  134. package/dist/config/oat-config.js +17 -5
  135. package/dist/config/user-sync-config.d.ts +1 -0
  136. package/dist/config/user-sync-config.d.ts.map +1 -1
  137. package/dist/config/user-sync-config.js +8 -0
  138. package/dist/drift/detector.d.ts +2 -2
  139. package/dist/drift/detector.d.ts.map +1 -1
  140. package/dist/drift/detector.js +18 -0
  141. package/dist/drift/strays.d.ts.map +1 -1
  142. package/dist/drift/strays.js +5 -0
  143. package/dist/engine/collection-sync.d.ts +14 -0
  144. package/dist/engine/collection-sync.d.ts.map +1 -0
  145. package/dist/engine/collection-sync.js +282 -0
  146. package/dist/engine/compute-plan.d.ts +8 -1
  147. package/dist/engine/compute-plan.d.ts.map +1 -1
  148. package/dist/engine/compute-plan.js +303 -12
  149. package/dist/engine/engine.types.d.ts +61 -0
  150. package/dist/engine/engine.types.d.ts.map +1 -1
  151. package/dist/engine/execute-plan.d.ts +22 -2
  152. package/dist/engine/execute-plan.d.ts.map +1 -1
  153. package/dist/engine/execute-plan.js +459 -27
  154. package/dist/engine/index.d.ts +1 -1
  155. package/dist/engine/index.d.ts.map +1 -1
  156. package/dist/engine/index.js +1 -1
  157. package/dist/engine/provider-path-safety.d.ts +1 -0
  158. package/dist/engine/provider-path-safety.d.ts.map +1 -1
  159. package/dist/engine/provider-path-safety.js +15 -0
  160. package/dist/engine/scanner.d.ts +9 -2
  161. package/dist/engine/scanner.d.ts.map +1 -1
  162. package/dist/engine/scanner.js +58 -4
  163. package/dist/fs/io.d.ts +67 -0
  164. package/dist/fs/io.d.ts.map +1 -1
  165. package/dist/fs/io.js +536 -3
  166. package/dist/manifest/index.d.ts +2 -2
  167. package/dist/manifest/index.d.ts.map +1 -1
  168. package/dist/manifest/index.js +1 -1
  169. package/dist/manifest/manager.d.ts +2 -2
  170. package/dist/manifest/manager.d.ts.map +1 -1
  171. package/dist/manifest/manager.js +20 -3
  172. package/dist/manifest/manifest.types.d.ts +704 -13
  173. package/dist/manifest/manifest.types.d.ts.map +1 -1
  174. package/dist/manifest/manifest.types.js +131 -15
  175. package/dist/providers/codex/codec/sync-extension.d.ts +1 -1
  176. package/dist/providers/codex/codec/sync-extension.d.ts.map +1 -1
  177. package/dist/providers/codex/codec/sync-extension.js +48 -17
  178. package/dist/providers/cursor/codec/sync-extension.d.ts +1 -1
  179. package/dist/providers/cursor/codec/sync-extension.d.ts.map +1 -1
  180. package/dist/providers/cursor/codec/sync-extension.js +52 -23
  181. package/dist/providers/identity/absolute-paths.d.ts +68 -0
  182. package/dist/providers/identity/absolute-paths.d.ts.map +1 -0
  183. package/dist/providers/identity/absolute-paths.js +144 -0
  184. package/dist/providers/identity/claude-runtime-observation.d.ts +105 -0
  185. package/dist/providers/identity/claude-runtime-observation.d.ts.map +1 -0
  186. package/dist/providers/identity/claude-runtime-observation.fixtures.d.ts +40 -0
  187. package/dist/providers/identity/claude-runtime-observation.fixtures.d.ts.map +1 -0
  188. package/dist/providers/identity/claude-runtime-observation.fixtures.js +146 -0
  189. package/dist/providers/identity/claude-runtime-observation.js +229 -0
  190. package/dist/providers/identity/codex-runtime-observation.d.ts +75 -0
  191. package/dist/providers/identity/codex-runtime-observation.d.ts.map +1 -0
  192. package/dist/providers/identity/codex-runtime-observation.fixtures.d.ts +37 -0
  193. package/dist/providers/identity/codex-runtime-observation.fixtures.d.ts.map +1 -0
  194. package/dist/providers/identity/codex-runtime-observation.fixtures.js +539 -0
  195. package/dist/providers/identity/codex-runtime-observation.js +255 -0
  196. package/dist/providers/identity/generic-dispatch-record.d.ts +313 -0
  197. package/dist/providers/identity/generic-dispatch-record.d.ts.map +1 -0
  198. package/dist/providers/identity/generic-dispatch-record.js +539 -0
  199. package/dist/providers/identity/oat-dispatch-record.d.ts +1964 -0
  200. package/dist/providers/identity/oat-dispatch-record.d.ts.map +1 -0
  201. package/dist/providers/identity/oat-dispatch-record.js +691 -0
  202. package/dist/providers/identity/runtime-observation.d.ts +100 -0
  203. package/dist/providers/identity/runtime-observation.d.ts.map +1 -0
  204. package/dist/providers/identity/runtime-observation.js +169 -0
  205. package/dist/providers/shared/index.d.ts +2 -0
  206. package/dist/providers/shared/index.d.ts.map +1 -1
  207. package/dist/providers/shared/index.js +1 -0
  208. package/dist/providers/shared/materialization-extension.d.ts +12 -0
  209. package/dist/providers/shared/materialization-extension.d.ts.map +1 -1
  210. package/dist/providers/shared/registry.d.ts +69 -0
  211. package/dist/providers/shared/registry.d.ts.map +1 -0
  212. package/dist/providers/shared/registry.js +252 -0
  213. package/dist/providers/shared/restart-adviser.d.ts +26 -0
  214. package/dist/providers/shared/restart-adviser.d.ts.map +1 -0
  215. package/dist/providers/shared/restart-adviser.js +96 -0
  216. package/dist/shared/types.d.ts +4 -0
  217. package/dist/shared/types.d.ts.map +1 -1
  218. package/package.json +2 -2
@@ -1 +1 @@
1
- {"version":3,"file":"manifest.types.d.ts","sourceRoot":"","sources":["../../src/manifest/manifest.types.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAUxB,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA2B5B,CAAC;AAEL,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAsBvB,CAAC;AAEL,MAAM,MAAM,aAAa,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAChE,MAAM,MAAM,QAAQ,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,cAAc,CAAC,CAAC"}
1
+ {"version":3,"file":"manifest.types.d.ts","sourceRoot":"","sources":["../../src/manifest/manifest.types.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAoDxB,eAAO,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAED,CAAC;AAElC,eAAO,MAAM,mBAAmB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAmC9B,CAAC;AAEH,QAAA,MAAM,oCAAoC;;;;;;;;;;;;EAIxC,CAAC;AAEH,eAAO,MAAM,6BAA6B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAUxC,CAAC;AAiCH,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAOkD,CAAC;AAEhF,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAgEzB,CAAC;AAEL,eAAO,MAAM,cAAc;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;IAAgD,CAAC;AAE5E,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,qBAAqB,CAAC,CAAC;AACpE,MAAM,MAAM,eAAe,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,mBAAmB,CAAC,CAAC;AAClE,8EAA8E;AAC9E,MAAM,MAAM,aAAa,GAAG,eAAe,CAAC;AAC5C,MAAM,MAAM,uBAAuB,GAAG,CAAC,CAAC,KAAK,CAC3C,OAAO,6BAA6B,CACrC,CAAC;AACF,MAAM,MAAM,8BAA8B,GAAG,CAAC,CAAC,KAAK,CAClD,OAAO,oCAAoC,CAC5C,CAAC;AACF,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC1D,MAAM,MAAM,UAAU,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,gBAAgB,CAAC,CAAC;AAC1D,MAAM,MAAM,QAAQ,GAAG,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,GAAG;IACnD,OAAO,EAAE,aAAa,EAAE,CAAC;CAC1B,CAAC"}
@@ -4,18 +4,20 @@ const RelativePathSchema = z
4
4
  .string()
5
5
  .min(1)
6
6
  .refine((value) => !value.startsWith('/') && !value.startsWith('~'), 'Path must be relative');
7
- export const ManifestEntrySchema = z
8
- .object({
7
+ const CollectionRelativePathSchema = RelativePathSchema.refine((value) => !value.includes('\\') &&
8
+ value
9
+ .split('/')
10
+ .every((segment) => segment !== '' && segment !== '.' && segment !== '..'), 'Collection path must be a normalized relative POSIX path');
11
+ const ManifestEntryBaseSchema = z.object({
9
12
  canonicalPath: RelativePathSchema,
10
13
  providerPath: RelativePathSchema,
11
14
  provider: z.string().min(1),
12
15
  contentType: ContentTypeSchema,
13
- strategy: z.enum(['symlink', 'copy']),
14
16
  contentHash: z.string().nullable(),
15
17
  isFile: z.boolean().default(false),
16
18
  lastSynced: z.string().datetime(),
17
- })
18
- .superRefine((entry, ctx) => {
19
+ });
20
+ function validateEntryHash(entry, ctx) {
19
21
  if (entry.strategy === 'copy' && entry.contentHash === null) {
20
22
  ctx.addIssue({
21
23
  code: z.ZodIssueCode.custom,
@@ -23,34 +25,148 @@ export const ManifestEntrySchema = z
23
25
  path: ['contentHash'],
24
26
  });
25
27
  }
26
- if (entry.strategy === 'symlink' && entry.contentHash !== null) {
28
+ if (entry.strategy !== 'copy' && entry.contentHash !== null) {
27
29
  ctx.addIssue({
28
30
  code: z.ZodIssueCode.custom,
29
- message: 'symlink strategy requires null contentHash',
31
+ message: `${entry.strategy} strategy requires null contentHash`,
30
32
  path: ['contentHash'],
31
33
  });
32
34
  }
35
+ }
36
+ export const ManifestEntryV1Schema = ManifestEntryBaseSchema.extend({
37
+ strategy: z.enum(['symlink', 'copy']),
38
+ }).superRefine(validateEntryHash);
39
+ export const ManifestEntrySchema = ManifestEntryBaseSchema.extend({
40
+ strategy: z.enum(['symlink', 'copy', 'collection']),
41
+ collectionId: z.string().min(1).optional(),
42
+ }).superRefine((entry, ctx) => {
43
+ validateEntryHash(entry, ctx);
44
+ if (entry.strategy === 'collection' && entry.collectionId === undefined) {
45
+ ctx.addIssue({
46
+ code: z.ZodIssueCode.custom,
47
+ message: 'collection strategy requires collectionId',
48
+ path: ['collectionId'],
49
+ });
50
+ }
51
+ if (entry.strategy !== 'collection' && entry.collectionId !== undefined) {
52
+ ctx.addIssue({
53
+ code: z.ZodIssueCode.custom,
54
+ message: 'collectionId is only valid for collection strategy',
55
+ path: ['collectionId'],
56
+ });
57
+ }
58
+ if (entry.strategy === 'collection') {
59
+ for (const field of ['canonicalPath', 'providerPath']) {
60
+ const parsed = CollectionRelativePathSchema.safeParse(entry[field]);
61
+ if (!parsed.success) {
62
+ ctx.addIssue({
63
+ code: z.ZodIssueCode.custom,
64
+ message: 'Inherited collection paths must be normalized relative POSIX paths',
65
+ path: [field],
66
+ });
67
+ }
68
+ }
69
+ }
33
70
  });
34
- export const ManifestSchema = z
71
+ const ManifestCollectionLinkIdentitySchema = z.object({
72
+ device: z.string().min(1),
73
+ inode: z.string().min(1),
74
+ linkText: z.string().min(1),
75
+ });
76
+ export const ManifestCollectionEntrySchema = z.object({
77
+ id: z.string().min(1),
78
+ provider: z.string().min(1),
79
+ contentType: ContentTypeSchema,
80
+ canonicalDir: CollectionRelativePathSchema,
81
+ providerDir: CollectionRelativePathSchema,
82
+ linkTarget: CollectionRelativePathSchema,
83
+ ownership: z.enum(['oat-created', 'adopted-exact']),
84
+ createdLink: ManifestCollectionLinkIdentitySchema.optional(),
85
+ lastVerified: z.string().datetime(),
86
+ });
87
+ function isStrictPathDescendant(parent, candidate) {
88
+ const parentSegments = parent.split('/');
89
+ const candidateSegments = candidate.split('/');
90
+ return (candidateSegments.length > parentSegments.length &&
91
+ parentSegments.every((segment, index) => candidateSegments[index] === segment));
92
+ }
93
+ function validateUniqueEntries(entries, ctx) {
94
+ const seen = new Set();
95
+ for (const [index, entry] of entries.entries()) {
96
+ const key = `${entry.canonicalPath}::${entry.provider}`;
97
+ if (seen.has(key)) {
98
+ ctx.addIssue({
99
+ code: z.ZodIssueCode.custom,
100
+ message: 'duplicate (canonicalPath, provider) pair',
101
+ path: ['entries', index],
102
+ });
103
+ }
104
+ else {
105
+ seen.add(key);
106
+ }
107
+ }
108
+ }
109
+ export const ManifestV1Schema = z
35
110
  .object({
36
111
  version: z.literal(1),
37
112
  oatVersion: z.string().min(1),
113
+ entries: z.array(ManifestEntryV1Schema),
114
+ lastUpdated: z.string().datetime(),
115
+ })
116
+ .superRefine((manifest, ctx) => validateUniqueEntries(manifest.entries, ctx));
117
+ export const ManifestV2Schema = z
118
+ .object({
119
+ version: z.literal(2),
120
+ oatVersion: z.string().min(1),
38
121
  entries: z.array(ManifestEntrySchema),
122
+ collections: z.array(ManifestCollectionEntrySchema),
39
123
  lastUpdated: z.string().datetime(),
40
124
  })
41
125
  .superRefine((manifest, ctx) => {
42
- const seen = new Set();
43
- for (const [index, entry] of manifest.entries.entries()) {
44
- const key = `${entry.canonicalPath}::${entry.provider}`;
45
- if (seen.has(key)) {
126
+ validateUniqueEntries(manifest.entries, ctx);
127
+ const collections = new Map();
128
+ for (const [index, collection] of manifest.collections.entries()) {
129
+ if (collections.has(collection.id)) {
46
130
  ctx.addIssue({
47
131
  code: z.ZodIssueCode.custom,
48
- message: 'duplicate (canonicalPath, provider) pair',
49
- path: ['entries', index],
132
+ message: 'duplicate collection id',
133
+ path: ['collections', index, 'id'],
50
134
  });
51
135
  }
52
136
  else {
53
- seen.add(key);
137
+ collections.set(collection.id, collection);
138
+ }
139
+ if (collection.linkTarget !== collection.canonicalDir) {
140
+ ctx.addIssue({
141
+ code: z.ZodIssueCode.custom,
142
+ message: 'collection linkTarget must exactly reference canonicalDir',
143
+ path: ['collections', index, 'linkTarget'],
144
+ });
145
+ }
146
+ }
147
+ for (const [index, entry] of manifest.entries.entries()) {
148
+ if (entry.strategy !== 'collection') {
149
+ continue;
150
+ }
151
+ const collection = collections.get(entry.collectionId ?? '');
152
+ if (collection === undefined) {
153
+ ctx.addIssue({
154
+ code: z.ZodIssueCode.custom,
155
+ message: 'collection entry must reference exactly one collection',
156
+ path: ['entries', index, 'collectionId'],
157
+ });
158
+ continue;
159
+ }
160
+ if (entry.provider !== collection.provider ||
161
+ entry.contentType !== collection.contentType ||
162
+ !isStrictPathDescendant(collection.canonicalDir, entry.canonicalPath) ||
163
+ !isStrictPathDescendant(collection.providerDir, entry.providerPath)) {
164
+ ctx.addIssue({
165
+ code: z.ZodIssueCode.custom,
166
+ message: 'collection entry provider, content type, and ancestry must match its collection',
167
+ path: ['entries', index],
168
+ });
54
169
  }
55
170
  }
56
171
  });
172
+ export const ManifestSchema = z.union([ManifestV1Schema, ManifestV2Schema]);
@@ -1,4 +1,4 @@
1
- import type { CanonicalEntry } from '../../../engine/index.js';
1
+ import { type CanonicalEntry } from '../../../engine/index.js';
2
2
  import { type MaterializationAction, type MaterializationApplyResult, type MaterializationContext, type MaterializationExtension, type MaterializationPlan, type MaterializationWriteOperation } from '../../shared/index.js';
3
3
  export type CodexExtensionAction = MaterializationAction;
4
4
  export type CodexExtensionTarget = 'role' | 'config';
@@ -1 +1 @@
1
- {"version":3,"file":"sync-extension.d.ts","sourceRoot":"","sources":["../../../../src/providers/codex/codec/sync-extension.ts"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAKpD,OAAO,EAGL,KAAK,qBAAqB,EAC1B,KAAK,0BAA0B,EAC/B,KAAK,sBAAsB,EAC3B,KAAK,wBAAwB,EAC7B,KAAK,mBAAmB,EACxB,KAAK,6BAA6B,EACnC,MAAM,mBAAmB,CAAC;AAqB3B,MAAM,MAAM,oBAAoB,GAAG,qBAAqB,CAAC;AACzD,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,QAAQ,CAAC;AAErD,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,oBAAoB,CAAC;IAC7B,MAAM,EAAE,oBAAoB,CAAC;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,4BAA6B,SAAQ,6BAA6B,CACjF,OAAO,EACP,oBAAoB,CACrB;IACC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,kBAAmB,SAAQ,mBAAmB,CAC7D,OAAO,EACP,oBAAoB,EACpB,0BAA0B,CAC3B;IACC,UAAU,EAAE,4BAA4B,EAAE,CAAC;IAC3C,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,0BAA0B;IACzC,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,MAAM,yBAAyB,GAAG,0BAA0B,CAAC;AAgBnE,MAAM,WAAW,iCAAiC;IAChD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACzB;AAqiBD,wBAAsB,gCAAgC,CACpD,SAAS,EAAE,MAAM,EACjB,gBAAgB,EAAE,cAAc,EAAE,EAClC,qBAAqB,CAAC,EAAE,MAAM,EAAE,EAChC,OAAO,GAAE,iCAAsC,GAC9C,OAAO,CAAC,kBAAkB,CAAC,CAkL7B;AAED,wBAAsB,8BAA8B,CAClD,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,yBAAyB,CAAC,CA6BpC;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAE1E;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,kBAAkB,GAAG;IACjE,iBAAiB,EAAE,MAAM,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;CACjB,CAEA;AAED,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,kBAAkB,GACvB,uBAAuB,EAAE,CAQ3B;AAED,eAAO,MAAM,6BAA6B,EAAE,wBAAwB,CAClE,kBAAkB,EAClB,sBAAsB,CAAC,iCAAiC,CAAC,CAY1D,CAAC"}
1
+ {"version":3,"file":"sync-extension.d.ts","sourceRoot":"","sources":["../../../../src/providers/codex/codec/sync-extension.ts"],"names":[],"mappings":"AAkBA,OAAO,EAEL,KAAK,cAAc,EACpB,MAAM,eAAe,CAAC;AAKvB,OAAO,EAGL,KAAK,qBAAqB,EAC1B,KAAK,0BAA0B,EAC/B,KAAK,sBAAsB,EAC3B,KAAK,wBAAwB,EAC7B,KAAK,mBAAmB,EACxB,KAAK,6BAA6B,EACnC,MAAM,mBAAmB,CAAC;AAsB3B,MAAM,MAAM,oBAAoB,GAAG,qBAAqB,CAAC;AACzD,MAAM,MAAM,oBAAoB,GAAG,MAAM,GAAG,QAAQ,CAAC;AAErD,MAAM,WAAW,uBAAuB;IACtC,MAAM,EAAE,oBAAoB,CAAC;IAC7B,MAAM,EAAE,oBAAoB,CAAC;IAC7B,IAAI,EAAE,MAAM,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,4BAA6B,SAAQ,6BAA6B,CACjF,OAAO,EACP,oBAAoB,CACrB;IACC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,kBAAmB,SAAQ,mBAAmB,CAC7D,OAAO,EACP,oBAAoB,EACpB,0BAA0B,CAC3B;IACC,UAAU,EAAE,4BAA4B,EAAE,CAAC;IAC3C,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,WAAW,0BAA0B;IACzC,YAAY,EAAE,MAAM,EAAE,CAAC;IACvB,mBAAmB,EAAE,MAAM,CAAC;CAC7B;AAED,MAAM,MAAM,yBAAyB,GAAG,0BAA0B,CAAC;AAiBnE,MAAM,WAAW,iCAAiC;IAChD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;CACzB;AAoiBD,wBAAsB,gCAAgC,CACpD,SAAS,EAAE,MAAM,EACjB,gBAAgB,EAAE,cAAc,EAAE,EAClC,qBAAqB,CAAC,EAAE,MAAM,EAAE,EAChC,OAAO,GAAE,iCAAsC,GAC9C,OAAO,CAAC,kBAAkB,CAAC,CAkL7B;AAED,wBAAsB,8BAA8B,CAClD,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,kBAAkB,GACvB,OAAO,CAAC,yBAAyB,CAAC,CA+DpC;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,kBAAkB,GAAG,OAAO,CAE1E;AAED,wBAAgB,uBAAuB,CAAC,IAAI,EAAE,kBAAkB,GAAG;IACjE,iBAAiB,EAAE,MAAM,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;CACjB,CAEA;AAED,wBAAgB,0BAA0B,CACxC,IAAI,EAAE,kBAAkB,GACvB,uBAAuB,EAAE,CAQ3B;AAED,eAAO,MAAM,6BAA6B,EAAE,wBAAwB,CAClE,kBAAkB,EAClB,sBAAsB,CAAC,iCAAiC,CAAC,CAY1D,CAAC"}
@@ -4,6 +4,7 @@ import { basename, dirname, join, relative, resolve } from 'node:path';
4
4
  import { parseCanonicalAgentFile } from '../../../agents/canonical/index.js';
5
5
  import { isCodexMaterializedRouteTarget, isWorkflowDispatchCandidateLadder, isWorkflowDispatchFallbackRoute, normalizeProjectPath, resolveActiveProject, validateDispatchRouteTarget, } from '../../../config/oat-config.js';
6
6
  import { resolveEffectiveConfig } from '../../../config/resolve.js';
7
+ import { materializationCanonicalPathAllowed, } from '../../../engine/index.js';
7
8
  import { CliError } from '../../../errors/index.js';
8
9
  import { ensureDir, fileExists } from '../../../fs/io.js';
9
10
  import { validateRealPathWithinScope } from '../../../fs/paths.js';
@@ -26,12 +27,7 @@ function toRelativePath(scopeRoot, absolutePath) {
26
27
  return relative(scopeRoot, absolutePath).replaceAll('\\', '/');
27
28
  }
28
29
  function canonicalPathAllowed(scopeRoot, canonicalEntry, allowedCanonicalPaths) {
29
- if (!allowedCanonicalPaths?.length) {
30
- return true;
31
- }
32
- const allowedSet = new Set(allowedCanonicalPaths);
33
- const relativeCanonicalPath = toRelativePath(scopeRoot, canonicalEntry.canonicalPath);
34
- return allowedSet.has(relativeCanonicalPath);
30
+ return materializationCanonicalPathAllowed(scopeRoot, canonicalEntry, allowedCanonicalPaths);
35
31
  }
36
32
  async function readOptionalFile(path) {
37
33
  if (!(await fileExists(path))) {
@@ -62,6 +58,7 @@ async function desiredRolesFromCanonical(canonicalEntries, scopeRoot, materializ
62
58
  configFile: exported.configFile,
63
59
  rolePath: join(scopeRoot, '.codex', exported.configFile),
64
60
  content: exported.content,
61
+ sourcePath: entry.canonicalPath,
65
62
  });
66
63
  if (CODEX_MATERIALIZED_BASE_ROLES.has(exported.roleName)) {
67
64
  for (const target of materializationTargets) {
@@ -76,17 +73,20 @@ async function desiredRolesFromCanonical(canonicalEntries, scopeRoot, materializ
76
73
  configFile: materialized.configFile,
77
74
  rolePath: join(scopeRoot, '.codex', materialized.configFile),
78
75
  content: withOatManagedCodexRoleOwner(materialized.content, target.owner),
76
+ sourcePath: entry.canonicalPath,
79
77
  });
80
78
  }
81
79
  }
82
80
  }
83
81
  const roleContents = new Map();
82
+ const sourceByRoleName = new Map();
84
83
  for (const role of roles) {
85
84
  const existing = roleContents.get(role.roleName);
86
85
  if (existing !== undefined && existing !== role.content) {
87
- throw new CliError(`Distinct Codex targets produced the same role name ${role.roleName}. Refusing ambiguous role writes.`);
86
+ throw new CliError(`Duplicate Codex role name ${role.roleName} from ${sourceByRoleName.get(role.roleName) ?? 'an earlier canonical agent'} and ${role.sourcePath}. Refusing ambiguous role writes.`);
88
87
  }
89
88
  roleContents.set(role.roleName, role.content);
89
+ sourceByRoleName.set(role.roleName, role.sourcePath);
90
90
  }
91
91
  return roles.sort((left, right) => left.roleName.localeCompare(right.roleName));
92
92
  }
@@ -519,14 +519,17 @@ export async function computeCodexProjectExtensionPlan(scopeRoot, canonicalEntri
519
519
  };
520
520
  }
521
521
  export async function applyCodexProjectExtensionPlan(scopeRoot, plan) {
522
- const result = {
523
- applied: 0,
524
- failed: 0,
525
- skipped: 0,
526
- };
522
+ const operationResults = [];
527
523
  for (const operation of plan.operations) {
528
524
  if (operation.action === 'skip') {
529
- result.skipped += 1;
525
+ operationResults.push({
526
+ provider: operation.provider,
527
+ target: operation.target,
528
+ path: operation.path,
529
+ entryName: operation.entryName,
530
+ action: operation.action,
531
+ status: 'current',
532
+ });
530
533
  continue;
531
534
  }
532
535
  const absolutePath = resolve(scopeRoot, operation.path);
@@ -538,13 +541,41 @@ export async function applyCodexProjectExtensionPlan(scopeRoot, plan) {
538
541
  await ensureDir(dirname(absolutePath));
539
542
  await writeFile(absolutePath, operation.content ?? '', 'utf8');
540
543
  }
541
- result.applied += 1;
544
+ operationResults.push({
545
+ provider: operation.provider,
546
+ target: operation.target,
547
+ path: operation.path,
548
+ entryName: operation.entryName,
549
+ action: operation.action,
550
+ status: 'changed',
551
+ });
542
552
  }
543
- catch {
544
- result.failed += 1;
553
+ catch (error) {
554
+ const missing = typeof error === 'object' &&
555
+ error !== null &&
556
+ 'code' in error &&
557
+ error.code === 'ENOENT';
558
+ operationResults.push({
559
+ provider: operation.provider,
560
+ target: operation.target,
561
+ path: operation.path,
562
+ entryName: operation.entryName,
563
+ action: operation.action,
564
+ status: missing ? 'missing' : 'failed',
565
+ failure: missing
566
+ ? 'Materialization input was missing; restore it and retry sync.'
567
+ : 'Materialization failed; inspect local verbose diagnostics and retry sync.',
568
+ });
545
569
  }
546
570
  }
547
- return result;
571
+ return {
572
+ applied: operationResults.filter(({ status }) => status === 'changed')
573
+ .length,
574
+ failed: operationResults.filter(({ status }) => status === 'failed' || status === 'missing').length,
575
+ skipped: operationResults.filter(({ status }) => status === 'current')
576
+ .length,
577
+ operations: operationResults,
578
+ };
548
579
  }
549
580
  export function hasCodexExtensionChanges(plan) {
550
581
  return hasMaterializationChanges(plan);
@@ -1,4 +1,4 @@
1
- import type { CanonicalEntry } from '../../../engine/index.js';
1
+ import { type CanonicalEntry } from '../../../engine/index.js';
2
2
  import { type MaterializationApplyResult, type MaterializationContext, type MaterializationExtension, type MaterializationOperation, type MaterializationPlan, type MaterializationWriteOperation } from '../../shared/index.js';
3
3
  import { type CursorModelPinMapping } from './catalog.js';
4
4
  import { type CursorRoleOwner } from './shared.js';
@@ -1 +1 @@
1
- {"version":3,"file":"sync-extension.d.ts","sourceRoot":"","sources":["../../../../src/providers/cursor/codec/sync-extension.ts"],"names":[],"mappings":"AAqBA,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,eAAe,CAAC;AAIpD,OAAO,EAIL,KAAK,0BAA0B,EAC/B,KAAK,sBAAsB,EAC3B,KAAK,wBAAwB,EAC7B,KAAK,wBAAwB,EAC7B,KAAK,mBAAmB,EACxB,KAAK,6BAA6B,EACnC,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EAIL,KAAK,qBAAqB,EAC3B,MAAM,WAAW,CAAC;AAMnB,OAAO,EAIL,KAAK,eAAe,EACrB,MAAM,UAAU,CAAC;AAElB,MAAM,MAAM,qBAAqB,GAAG,MAAM,CAAC;AAE3C,MAAM,WAAW,wBAAyB,SAAQ,wBAAwB,CACxE,QAAQ,EACR,qBAAqB,CACtB;IACC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,6BAA8B,SAAQ,6BAA6B,CAClF,QAAQ,EACR,qBAAqB,CACtB;IACC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,2BAA2B;IAC1C,aAAa,EAAE,eAAe,EAAE,CAAC;IACjC,aAAa,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,mBAAoB,SAAQ,mBAAmB,CAC9D,QAAQ,EACR,qBAAqB,EACrB,2BAA2B,CAC5B;IACC,UAAU,EAAE,6BAA6B,EAAE,CAAC;CAC7C;AAED,MAAM,MAAM,0BAA0B,GAAG,0BAA0B,CAAC;AAQpE,MAAM,WAAW,kCAAkC;IACjD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,aAAa,CAAC,EAAE,SAAS,qBAAqB,EAAE,CAAC;IACjD,gBAAgB,CAAC,EAAE,SAAS,qBAAqB,EAAE,CAAC;CACrD;AAoWD,wBAAsB,iCAAiC,CACrD,SAAS,EAAE,MAAM,EACjB,gBAAgB,EAAE,cAAc,EAAE,EAClC,qBAAqB,CAAC,EAAE,MAAM,EAAE,EAChC,OAAO,GAAE,kCAAuC,GAC/C,OAAO,CAAC,mBAAmB,CAAC,CAmH9B;AA6DD,wBAAsB,+BAA+B,CACnD,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,mBAAmB,GACxB,OAAO,CAAC,0BAA0B,CAAC,CA0BrC;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAE5E;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,mBAAmB,GAAG;IACnE,iBAAiB,EAAE,MAAM,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;CACjB,CAEA;AAED,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,mBAAmB,GACxB,wBAAwB,EAAE,CAM5B;AAED,eAAO,MAAM,8BAA8B,EAAE,wBAAwB,CACnE,mBAAmB,EACnB,sBAAsB,CAAC,kCAAkC,CAAC,CAY3D,CAAC"}
1
+ {"version":3,"file":"sync-extension.d.ts","sourceRoot":"","sources":["../../../../src/providers/cursor/codec/sync-extension.ts"],"names":[],"mappings":"AAqBA,OAAO,EAEL,KAAK,cAAc,EACpB,MAAM,eAAe,CAAC;AAIvB,OAAO,EAIL,KAAK,0BAA0B,EAC/B,KAAK,sBAAsB,EAC3B,KAAK,wBAAwB,EAC7B,KAAK,wBAAwB,EAC7B,KAAK,mBAAmB,EACxB,KAAK,6BAA6B,EACnC,MAAM,mBAAmB,CAAC;AAI3B,OAAO,EAIL,KAAK,qBAAqB,EAC3B,MAAM,WAAW,CAAC;AAMnB,OAAO,EAIL,KAAK,eAAe,EACrB,MAAM,UAAU,CAAC;AAElB,MAAM,MAAM,qBAAqB,GAAG,MAAM,CAAC;AAE3C,MAAM,WAAW,wBAAyB,SAAQ,wBAAwB,CACxE,QAAQ,EACR,qBAAqB,CACtB;IACC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,6BAA8B,SAAQ,6BAA6B,CAClF,QAAQ,EACR,qBAAqB,CACtB;IACC,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,MAAM,WAAW,2BAA2B;IAC1C,aAAa,EAAE,eAAe,EAAE,CAAC;IACjC,aAAa,EAAE,OAAO,CAAC;CACxB;AAED,MAAM,WAAW,mBAAoB,SAAQ,mBAAmB,CAC9D,QAAQ,EACR,qBAAqB,EACrB,2BAA2B,CAC5B;IACC,UAAU,EAAE,6BAA6B,EAAE,CAAC;CAC7C;AAED,MAAM,MAAM,0BAA0B,GAAG,0BAA0B,CAAC;AAQpE,MAAM,WAAW,kCAAkC;IACjD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,WAAW,CAAC,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,GAAG,CAAC,EAAE,MAAM,CAAC,UAAU,CAAC;IACxB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,aAAa,CAAC,EAAE,SAAS,qBAAqB,EAAE,CAAC;IACjD,gBAAgB,CAAC,EAAE,SAAS,qBAAqB,EAAE,CAAC;CACrD;AAiWD,wBAAsB,iCAAiC,CACrD,SAAS,EAAE,MAAM,EACjB,gBAAgB,EAAE,cAAc,EAAE,EAClC,qBAAqB,CAAC,EAAE,MAAM,EAAE,EAChC,OAAO,GAAE,kCAAuC,GAC/C,OAAO,CAAC,mBAAmB,CAAC,CAmH9B;AA6DD,wBAAsB,+BAA+B,CACnD,SAAS,EAAE,MAAM,EACjB,IAAI,EAAE,mBAAmB,GACxB,OAAO,CAAC,0BAA0B,CAAC,CA4DrC;AAED,wBAAgB,yBAAyB,CAAC,IAAI,EAAE,mBAAmB,GAAG,OAAO,CAE5E;AAED,wBAAgB,wBAAwB,CAAC,IAAI,EAAE,mBAAmB,GAAG;IACnE,iBAAiB,EAAE,MAAM,CAAC;IAC1B,OAAO,EAAE,MAAM,CAAC;CACjB,CAEA;AAED,wBAAgB,2BAA2B,CACzC,IAAI,EAAE,mBAAmB,GACxB,wBAAwB,EAAE,CAM5B;AAED,eAAO,MAAM,8BAA8B,EAAE,wBAAwB,CACnE,mBAAmB,EACnB,sBAAsB,CAAC,kCAAkC,CAAC,CAY3D,CAAC"}
@@ -5,6 +5,7 @@ import { parseCanonicalAgentFile } from '../../../agents/canonical/index.js';
5
5
  import { normalizeDispatchMatrix, walkDispatchMatrix, } from '../../../config/dispatch-matrix.js';
6
6
  import { normalizeProjectPath, resolveActiveProject } from '../../../config/oat-config.js';
7
7
  import { resolveEffectiveConfig } from '../../../config/resolve.js';
8
+ import { materializationCanonicalPathAllowed, } from '../../../engine/index.js';
8
9
  import { CliError } from '../../../errors/index.js';
9
10
  import { ensureDir, fileExists } from '../../../fs/io.js';
10
11
  import { validateRealPathWithinScope } from '../../../fs/paths.js';
@@ -138,13 +139,11 @@ async function readCursorMaterializationTargets(scopeRoot, options) {
138
139
  return [...targets.values()].sort((left, right) => left.mapping.ladderModelId.localeCompare(right.mapping.ladderModelId));
139
140
  }
140
141
  function canonicalPathAllowed(scopeRoot, canonicalEntry, allowedCanonicalPaths) {
141
- if (!allowedCanonicalPaths?.length) {
142
- return true;
143
- }
144
- return new Set(allowedCanonicalPaths).has(toRelativePath(scopeRoot, canonicalEntry.canonicalPath));
142
+ return materializationCanonicalPathAllowed(scopeRoot, canonicalEntry, allowedCanonicalPaths);
145
143
  }
146
144
  async function desiredRolesFromCanonical(canonicalEntries, targets) {
147
145
  const desired = [];
146
+ const sourceByRoleName = new Map();
148
147
  for (const entry of canonicalEntries) {
149
148
  if (entry.type !== 'agent' ||
150
149
  !entry.isFile ||
@@ -156,19 +155,18 @@ async function desiredRolesFromCanonical(canonicalEntries, targets) {
156
155
  continue;
157
156
  }
158
157
  for (const target of targets) {
159
- desired.push(materializeCursorAgent({
158
+ const role = materializeCursorAgent({
160
159
  agent,
161
160
  mapping: target.mapping,
162
161
  owner: target.owner,
163
- }));
164
- }
165
- }
166
- const names = new Set();
167
- for (const role of desired) {
168
- if (names.has(role.roleName)) {
169
- throw new CliError(`Distinct Cursor targets produced the same Cursor role name ${role.roleName}. Refusing ambiguous role writes.`);
162
+ });
163
+ const existingSource = sourceByRoleName.get(role.roleName);
164
+ if (existingSource) {
165
+ throw new CliError(`Duplicate Cursor role name ${role.roleName} from ${existingSource} and ${entry.canonicalPath}. Refusing ambiguous role writes.`);
166
+ }
167
+ sourceByRoleName.set(role.roleName, entry.canonicalPath);
168
+ desired.push(role);
170
169
  }
171
- names.add(role.roleName);
172
170
  }
173
171
  return desired.sort((left, right) => left.roleName.localeCompare(right.roleName));
174
172
  }
@@ -343,14 +341,17 @@ async function assertSafeCursorMutationPath(scopeRoot, absolutePath) {
343
341
  }
344
342
  }
345
343
  export async function applyCursorProjectExtensionPlan(scopeRoot, plan) {
346
- const result = {
347
- applied: 0,
348
- failed: 0,
349
- skipped: 0,
350
- };
344
+ const operationResults = [];
351
345
  for (const operation of plan.operations) {
352
346
  if (operation.action === 'skip') {
353
- result.skipped += 1;
347
+ operationResults.push({
348
+ provider: operation.provider,
349
+ target: operation.target,
350
+ path: operation.path,
351
+ entryName: operation.entryName,
352
+ action: operation.action,
353
+ status: 'current',
354
+ });
354
355
  continue;
355
356
  }
356
357
  const absolutePath = resolve(scopeRoot, operation.path);
@@ -363,13 +364,41 @@ export async function applyCursorProjectExtensionPlan(scopeRoot, plan) {
363
364
  await ensureDir(dirname(absolutePath));
364
365
  await writeFile(absolutePath, operation.content ?? '', 'utf8');
365
366
  }
366
- result.applied += 1;
367
+ operationResults.push({
368
+ provider: operation.provider,
369
+ target: operation.target,
370
+ path: operation.path,
371
+ entryName: operation.entryName,
372
+ action: operation.action,
373
+ status: 'changed',
374
+ });
367
375
  }
368
- catch {
369
- result.failed += 1;
376
+ catch (error) {
377
+ const missing = typeof error === 'object' &&
378
+ error !== null &&
379
+ 'code' in error &&
380
+ error.code === 'ENOENT';
381
+ operationResults.push({
382
+ provider: operation.provider,
383
+ target: operation.target,
384
+ path: operation.path,
385
+ entryName: operation.entryName,
386
+ action: operation.action,
387
+ status: missing ? 'missing' : 'failed',
388
+ failure: missing
389
+ ? 'Materialization input was missing; restore it and retry sync.'
390
+ : 'Materialization failed; inspect local verbose diagnostics and retry sync.',
391
+ });
370
392
  }
371
393
  }
372
- return result;
394
+ return {
395
+ applied: operationResults.filter(({ status }) => status === 'changed')
396
+ .length,
397
+ failed: operationResults.filter(({ status }) => status === 'failed' || status === 'missing').length,
398
+ skipped: operationResults.filter(({ status }) => status === 'current')
399
+ .length,
400
+ operations: operationResults,
401
+ };
373
402
  }
374
403
  export function hasCursorExtensionChanges(plan) {
375
404
  return hasMaterializationChanges(plan);
@@ -0,0 +1,68 @@
1
+ /**
2
+ * Absolute-path detection for dispatch records and messages.
3
+ *
4
+ * Two patterns, deliberately different. `IDENTITY_PATH_PATTERN` is strict and
5
+ * used where a path is never a legitimate value, so ambiguity resolves to
6
+ * rejection. `PROSE_PATH_PATTERN` is conservative and used where a false
7
+ * positive would corrupt legitimate evidence, so it under-matches by design:
8
+ * colon-prefixed forms, path-shaped routes inside URLs, and trailing-slash
9
+ * candidates survive. Prose redaction is therefore best-effort, not a
10
+ * guarantee — see `spec.md` NFR1, amended 2026-09-03.
11
+ *
12
+ * Before this module there were two independent notions of
13
+ * "looks like a path": a regex inside `redactDispatchMessage`, which only
14
+ * scrubbed error text, and nothing at all on the persistence boundary. The
15
+ * durable journal is committed to a shared repository, so a username or local
16
+ * layout that reaches it is in git history permanently.
17
+ *
18
+ * Message redaction and record sanitization now share this detector, because
19
+ * the gap between them is precisely what allowed paths to be scrubbed from a
20
+ * message and written verbatim into the record in the same command.
21
+ */
22
+ /** The stable stand-in, matching the `<tier>/agents/<role>.md` precedent. */
23
+ export declare const REDACTED_PATH = "<redacted-path>";
24
+ /**
25
+ * Identity-field detector. Rejects on ambiguity by design.
26
+ */
27
+ export declare function containsAbsolutePath(value: string): boolean;
28
+ /**
29
+ * Best-effort prose redaction. NOT a guarantee.
30
+ *
31
+ * Known limits, stated so they are visible rather than assumed away:
32
+ * - A colon-prefixed path (`cwd:/Users/alice`) survives, because `:` cannot be
33
+ * treated as a path delimiter without mangling every URL.
34
+ * - A candidate inside an http(s) URL is left alone, so a path-shaped route
35
+ * such as `?next=/dashboard` survives.
36
+ * - A candidate ending in `/` is left alone, because `/foo/bar/` is far more
37
+ * often a regex literal than a directory in prose.
38
+ * - Any path shape the pattern does not match survives.
39
+ *
40
+ * The bias is deliberate. A path leaked into prose is a disclosure; a mangled
41
+ * URL or regex is corrupted evidence, and corrupted evidence is worse in a
42
+ * provenance record. Identity and control fields carry the enforceable half of
43
+ * the guarantee via {@link containsAbsolutePath}.
44
+ */
45
+ export declare function redactAbsolutePaths(value: string): string;
46
+ /**
47
+ * Recursively redact every string in a JSON value. Used for prose and nested
48
+ * evidence containers, where a path is plausible incidental content rather than
49
+ * a malformed identifier.
50
+ */
51
+ export declare function redactAbsolutePathsDeep<T>(value: T): T;
52
+ /**
53
+ * Recursively reject any absolute path. Used for identity and control fields,
54
+ * where a path is never a legitimate value and silently rewriting one would
55
+ * corrupt the identifier it claims to be.
56
+ */
57
+ export declare function assertNoAbsolutePath(value: unknown, path: string): void;
58
+ /**
59
+ * The publication postcondition, scoped to what is actually guaranteed.
60
+ *
61
+ * It hard-fails on a path in an identity or control field, which is the half of
62
+ * NFR1 that is enforceable. It deliberately does **not** claim that no absolute
63
+ * path remains anywhere in the revision: prose redaction is best-effort, and a
64
+ * postcondition that asserted more than the sanitizer can deliver would be a
65
+ * false guarantee rather than a check.
66
+ */
67
+ export declare function assertJournalIdentityHasNoAbsolutePath(identityFields: Readonly<Record<string, unknown>>): void;
68
+ //# sourceMappingURL=absolute-paths.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"absolute-paths.d.ts","sourceRoot":"","sources":["../../../src/providers/identity/absolute-paths.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AA6BH,6EAA6E;AAC7E,eAAO,MAAM,aAAa,oBAAoB,CAAC;AAM/C;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,MAAM,GAAG,OAAO,CAE3D;AAYD;;;;;;;;;;;;;;;;GAgBG;AACH,wBAAgB,mBAAmB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAOzD;AAED;;;;GAIG;AACH,wBAAgB,uBAAuB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,GAAG,CAAC,CActD;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,KAAK,EAAE,OAAO,EAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAmBvE;AAED;;;;;;;;GAQG;AACH,wBAAgB,sCAAsC,CACpD,cAAc,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC,GAChD,IAAI,CAIN"}