@open-agent-toolkit/cli 0.2.73 → 0.2.75

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 (149) hide show
  1. package/assets/bundle-metadata.json +1 -1
  2. package/assets/docs/cli-utilities/configuration.md +3 -56
  3. package/assets/docs/cli-utilities/tool-packs.md +4 -4
  4. package/assets/docs/contributing/code.md +2 -2
  5. package/assets/docs/contributing/index.md +0 -1
  6. package/assets/docs/reference/cli-reference.md +1 -1
  7. package/assets/docs/reference/troubleshooting.md +17 -40
  8. package/assets/docs/workflows/projects/artifacts.md +16 -34
  9. package/assets/docs/workflows/projects/lifecycle.md +20 -19
  10. package/assets/docs/workflows/skills/explainer-kit.md +180 -440
  11. package/assets/docs/workflows/skills/index.md +1 -2
  12. package/assets/public-package-versions.json +4 -4
  13. package/assets/skills/explainer-kit/SKILL.md +96 -171
  14. package/assets/skills/explainer-kit/recipes/engineer-tour.json +1 -22
  15. package/assets/skills/explainer-kit/recipes/program-recap.json +1 -29
  16. package/assets/skills/explainer-kit/recipes/project-explainer.json +1 -22
  17. package/assets/skills/explainer-kit/recipes/project-recap.v2.json +1 -43
  18. package/assets/skills/explainer-kit/references/fact-base-contract.md +76 -101
  19. package/assets/skills/explainer-kit/references/recap-authoring.md +99 -0
  20. package/assets/skills/explainer-kit/schemas/manifest.schema.json +13 -135
  21. package/assets/skills/explainer-kit/scripts/bundle.mjs +712 -0
  22. package/assets/skills/explainer-kit/scripts/lib/claim-subject.mjs +40 -0
  23. package/assets/skills/explainer-kit/scripts/lib/contracts.mjs +113 -1504
  24. package/assets/skills/explainer-kit/scripts/lib/failure.mjs +13 -0
  25. package/assets/skills/explainer-kit/scripts/lib/package-coverage.mjs +57 -403
  26. package/assets/skills/explainer-kit/scripts/lib/qa-result.mjs +168 -0
  27. package/assets/skills/explainer-kit/scripts/lib/qa.mjs +90 -488
  28. package/assets/skills/explainer-kit/scripts/lib/recipes.mjs +28 -545
  29. package/assets/skills/explainer-kit/scripts/lib/run-package.mjs +120 -0
  30. package/assets/skills/explainer-kit/scripts/lib/sanitize.mjs +42 -0
  31. package/assets/skills/explainer-kit/scripts/record.mjs +335 -0
  32. package/assets/skills/explainer-kit/scripts/verify.mjs +648 -0
  33. package/assets/skills/oat-explainer-kit/SKILL.md +57 -117
  34. package/assets/skills/oat-explainer-kit/references/config-contract.md +20 -64
  35. package/assets/skills/oat-explainer-kit/references/lifecycle-contract.md +85 -237
  36. package/assets/skills/oat-explainer-kit/scripts/bind-project-sources.mjs +18 -10
  37. package/assets/skills/oat-explainer-kit/scripts/check-core.mjs +1 -0
  38. package/assets/skills/oat-explainer-kit/scripts/check-terminal-outcome.mjs +256 -21
  39. package/assets/skills/oat-explainer-kit/scripts/persist-intent.mjs +82 -3
  40. package/assets/skills/oat-explainer-kit/scripts/resolve-config.mjs +0 -162
  41. package/assets/skills/oat-explainer-kit/scripts/resolve-intent.mjs +57 -123
  42. package/assets/skills/oat-project-autonomous/SKILL.md +14 -13
  43. package/assets/skills/oat-project-autonomous/references/gate-inventory.md +165 -165
  44. package/assets/skills/oat-project-complete/SKILL.md +168 -349
  45. package/assets/skills/oat-project-complete/scripts/consume-persisted-recap-intent.mjs +125 -0
  46. package/assets/skills/oat-project-complete/scripts/execute-synced-archive-entry.mjs +0 -30
  47. package/assets/skills/oat-project-complete/scripts/parse-completion-retry-fields.mjs +4 -37
  48. package/assets/skills/oat-project-complete/scripts/parse-synced-archive-resume-fields.mjs +17 -16
  49. package/assets/skills/oat-project-complete/scripts/recover-completion-receipts.mjs +9 -174
  50. package/assets/skills/oat-project-complete/scripts/resolve-completion-retry.mjs +1 -2
  51. package/assets/skills/oat-project-document/SKILL.md +1 -1
  52. package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +165 -165
  53. package/assets/skills/oat-project-implement/SKILL.md +1 -1
  54. package/assets/skills/oat-project-implement/references/completion-and-closeout.md +44 -56
  55. package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +165 -165
  56. package/assets/skills/oat-project-lite/SKILL.md +1 -1
  57. package/assets/skills/oat-project-lite/references/docs/autonomy-contract.md +165 -165
  58. package/assets/skills/oat-project-plan/SKILL.md +5 -6
  59. package/assets/skills/oat-project-pr-final/SKILL.md +1 -1
  60. package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +165 -165
  61. package/assets/skills/oat-project-quick-start/SKILL.md +1 -1
  62. package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +165 -165
  63. package/assets/skills/oat-project-summary/SKILL.md +35 -25
  64. package/assets/skills/oat-wave-execute/SKILL.md +11 -36
  65. package/assets/skills/oat-wave-program/SKILL.md +13 -37
  66. package/dist/commands/config/index.d.ts.map +1 -1
  67. package/dist/commands/config/index.js +9 -113
  68. package/dist/commands/project/archive/archive-utils.d.ts.map +1 -1
  69. package/dist/commands/project/archive/archive-utils.js +32 -184
  70. package/dist/commands/project/archive/explainer-package-coverage.d.ts +3 -8
  71. package/dist/commands/project/archive/explainer-package-coverage.d.ts.map +1 -1
  72. package/dist/commands/project/archive/explainer-package-coverage.js +1 -1
  73. package/dist/config/oat-config.d.ts +0 -11
  74. package/dist/config/oat-config.d.ts.map +1 -1
  75. package/dist/config/oat-config.js +0 -34
  76. package/dist/config/resolve.d.ts.map +1 -1
  77. package/dist/config/resolve.js +0 -8
  78. package/dist/release/public-package-contract.d.ts.map +1 -1
  79. package/dist/release/public-package-contract.js +2 -10
  80. package/dist/validation/project-state.d.ts.map +1 -1
  81. package/dist/validation/project-state.js +42 -3
  82. package/package.json +2 -2
  83. package/assets/docs/contributing/explainer-kit-verification.md +0 -133
  84. package/assets/docs/workflows/skills/explainer-kit-providers.md +0 -155
  85. package/assets/skills/explainer-kit/briefs/deep-dive.md +0 -35
  86. package/assets/skills/explainer-kit/briefs/project-page.md +0 -38
  87. package/assets/skills/explainer-kit/briefs/supporting-diagram.md +0 -28
  88. package/assets/skills/explainer-kit/briefs/walkthrough-deck.md +0 -35
  89. package/assets/skills/explainer-kit/examples/project-explainer/content.md +0 -31
  90. package/assets/skills/explainer-kit/examples/project-explainer/fact-base.md +0 -22
  91. package/assets/skills/explainer-kit/examples/project-recap/content.md +0 -77
  92. package/assets/skills/explainer-kit/examples/project-recap/fact-base.json +0 -104
  93. package/assets/skills/explainer-kit/examples/project-recap/fact-base.md +0 -44
  94. package/assets/skills/explainer-kit/examples/theme-bundle.json +0 -87
  95. package/assets/skills/explainer-kit/recipes/project-recap.v1.json +0 -93
  96. package/assets/skills/explainer-kit/references/contracts.md +0 -324
  97. package/assets/skills/explainer-kit/references/destination-contract.md +0 -210
  98. package/assets/skills/explainer-kit/references/extension-contract.md +0 -80
  99. package/assets/skills/explainer-kit/references/golden-conformance.md +0 -80
  100. package/assets/skills/explainer-kit/references/visual-review.md +0 -71
  101. package/assets/skills/explainer-kit/schemas/author-request.v2.schema.json +0 -212
  102. package/assets/skills/explainer-kit/schemas/author-request.v3.schema.json +0 -241
  103. package/assets/skills/explainer-kit/schemas/author-result.v2.schema.json +0 -52
  104. package/assets/skills/explainer-kit/schemas/build-record.schema.json +0 -98
  105. package/assets/skills/explainer-kit/schemas/durability-evidence.schema.json +0 -53
  106. package/assets/skills/explainer-kit/schemas/publish-receipt.v1.schema.json +0 -97
  107. package/assets/skills/explainer-kit/schemas/publish-receipt.v2.schema.json +0 -215
  108. package/assets/skills/explainer-kit/schemas/publish-request.v1.schema.json +0 -32
  109. package/assets/skills/explainer-kit/schemas/publish-request.v2.schema.json +0 -34
  110. package/assets/skills/explainer-kit/schemas/run-request.schema.json +0 -147
  111. package/assets/skills/explainer-kit/schemas/set-plan.v1.schema.json +0 -149
  112. package/assets/skills/explainer-kit/schemas/terminal-evidence.v1.schema.json +0 -81
  113. package/assets/skills/explainer-kit/schemas/visual-review-evidence.v1.schema.json +0 -66
  114. package/assets/skills/explainer-kit/schemas/visual-review-request.v1.schema.json +0 -117
  115. package/assets/skills/explainer-kit/schemas/visual-review-result.v1.schema.json +0 -80
  116. package/assets/skills/explainer-kit/scripts/lib/catalog.mjs +0 -349
  117. package/assets/skills/explainer-kit/scripts/lib/content-approval.mjs +0 -418
  118. package/assets/skills/explainer-kit/scripts/lib/diagram.mjs +0 -514
  119. package/assets/skills/explainer-kit/scripts/lib/durability.mjs +0 -643
  120. package/assets/skills/explainer-kit/scripts/lib/fact-base.mjs +0 -664
  121. package/assets/skills/explainer-kit/scripts/lib/internal-references.mjs +0 -538
  122. package/assets/skills/explainer-kit/scripts/lib/markdown.mjs +0 -414
  123. package/assets/skills/explainer-kit/scripts/lib/publication-policy.mjs +0 -54
  124. package/assets/skills/explainer-kit/scripts/lib/records.mjs +0 -954
  125. package/assets/skills/explainer-kit/scripts/lib/render.mjs +0 -703
  126. package/assets/skills/explainer-kit/scripts/lib/s3-roots.mjs +0 -353
  127. package/assets/skills/explainer-kit/scripts/lib/s3-static.mjs +0 -759
  128. package/assets/skills/explainer-kit/scripts/lib/set-plan.mjs +0 -209
  129. package/assets/skills/explainer-kit/scripts/lib/source-backlinks.mjs +0 -218
  130. package/assets/skills/explainer-kit/scripts/lib/terminal-evidence.mjs +0 -157
  131. package/assets/skills/explainer-kit/scripts/lib/visual-review.mjs +0 -400
  132. package/assets/skills/explainer-kit/scripts/publish.mjs +0 -76
  133. package/assets/skills/explainer-kit/scripts/record-durability.mjs +0 -64
  134. package/assets/skills/explainer-kit/scripts/render-qa.mjs +0 -461
  135. package/assets/skills/explainer-kit/scripts/run.mjs +0 -3038
  136. package/assets/skills/explainer-kit/scripts/validate.mjs +0 -59
  137. package/assets/skills/oat-explainer-kit/references/author-callback.md +0 -53
  138. package/assets/skills/oat-explainer-kit/references/migration.md +0 -144
  139. package/assets/skills/oat-explainer-kit/references/visual-review-callback.md +0 -83
  140. package/assets/skills/oat-explainer-kit/scripts/derive-destination.mjs +0 -91
  141. package/assets/skills/oat-explainer-kit/scripts/finalize-tracked-run.mjs +0 -531
  142. package/assets/skills/oat-explainer-kit/scripts/probe-recap-seams.mjs +0 -371
  143. package/assets/skills/oat-explainer-kit/scripts/run.mjs +0 -905
  144. package/dist/commands/project/archive/explainer-source-backlinks.d.ts +0 -18
  145. package/dist/commands/project/archive/explainer-source-backlinks.d.ts.map +0 -1
  146. package/dist/commands/project/archive/explainer-source-backlinks.js +0 -27
  147. package/dist/commands/project/archive/explainer-terminal-evidence.d.ts +0 -29
  148. package/dist/commands/project/archive/explainer-terminal-evidence.d.ts.map +0 -1
  149. package/dist/commands/project/archive/explainer-terminal-evidence.js +0 -37
@@ -1,905 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { readFile } from 'node:fs/promises';
4
- import { basename, dirname, join, resolve } from 'node:path';
5
- import { fileURLToPath, pathToFileURL } from 'node:url';
6
-
7
- import {
8
- bindProjectSources,
9
- bindRepositorySources,
10
- } from './bind-project-sources.mjs';
11
- import { checkCoreCompatibility } from './check-core.mjs';
12
- import { deriveExplainerDestination } from './derive-destination.mjs';
13
- import {
14
- resolveExplainerConfig,
15
- toExplainerRunRequest,
16
- } from './resolve-config.mjs';
17
- import { resolveExplainerOutputRoot } from './resolve-paths.mjs';
18
-
19
- export const MINIMUM_CORE_VERSION = '2.1.0';
20
- const ADAPTER_ROOT = resolve(dirname(fileURLToPath(import.meta.url)), '..');
21
-
22
- export async function runOatExplainer({
23
- adapterRoot = ADAPTER_ROOT,
24
- userSkillsRoot,
25
- repoRoot,
26
- invocation,
27
- activeProject,
28
- recipe,
29
- slug,
30
- suppliedFactBasePath,
31
- runtimeOverrides = {},
32
- getConfig,
33
- mode = 'unattended',
34
- durabilityStrategy = 'none',
35
- artDirection,
36
- defaultMode,
37
- renderStrategy,
38
- retainRawArtDirection = false,
39
- planSet,
40
- planSetModulePath,
41
- author,
42
- authorModulePath,
43
- critic,
44
- criticModulePath,
45
- browserSession,
46
- browserSessionModulePath,
47
- browserProbe,
48
- browserProbeModulePath,
49
- visualCritic,
50
- visualCriticModulePath,
51
- coreOptions = {},
52
- }) {
53
- const compatibility = await checkCoreCompatibility({
54
- adapterRoot,
55
- ...(userSkillsRoot && { userSkillsRoot }),
56
- minimumVersion: MINIMUM_CORE_VERSION,
57
- });
58
- if (!compatibility.ok) {
59
- const error = new Error(
60
- `${compatibility.message} Run \`${compatibility.guidance}\`.`,
61
- );
62
- error.code =
63
- compatibility.code === 'missing'
64
- ? 'E_CORE_MISSING'
65
- : 'E_CORE_INCOMPATIBLE';
66
- error.compatibility = compatibility;
67
- throw error;
68
- }
69
- if (!supportsAdaptiveSetPlanning(compatibility.installedVersion)) {
70
- const error = new Error(
71
- `Installed explainer-kit ${compatibility.installedVersion} does not provide adaptive set planning. Run \`oat tools update --pack utility --scope user\`.`,
72
- );
73
- error.code = 'E_CORE_INCOMPATIBLE';
74
- error.compatibility = compatibility;
75
- throw error;
76
- }
77
-
78
- const resolvedConfig = await resolveExplainerConfig({
79
- repoRoot,
80
- runtimeOverrides,
81
- ...(getConfig && { getConfig }),
82
- });
83
- const outputRoot = await resolveExplainerOutputRoot({
84
- repoRoot,
85
- invocation,
86
- activeProject,
87
- slug,
88
- });
89
- const destination = resolvedConfig.publish
90
- ? deriveExplainerDestination({
91
- invocation,
92
- ...(invocation === 'project' && {
93
- projectSlug: basename(activeProject),
94
- }),
95
- s3Uri: resolvedConfig.publish.s3Uri,
96
- publicBaseUrl: resolvedConfig.publish.publicBaseUrl,
97
- })
98
- : null;
99
- let bound;
100
- if (invocation === 'project') {
101
- if (!activeProject) {
102
- throw new Error(
103
- 'Project artifact binding requires an activeProject input.',
104
- );
105
- }
106
- bound = await bindProjectSources({
107
- projectRoot: resolve(repoRoot, activeProject),
108
- repoRoot,
109
- recipe,
110
- suppliedFactBasePath,
111
- });
112
- } else if (invocation === 'repo') {
113
- bound = await bindRepositorySources({
114
- repoRoot,
115
- suppliedFactBasePath,
116
- });
117
- } else {
118
- throw new Error(
119
- 'The OAT adapter accepts project or repository invocations; direct callers invoke the core with an explicit output root.',
120
- );
121
- }
122
- const requestConfig = destination
123
- ? {
124
- ...resolvedConfig,
125
- publish: {
126
- ...resolvedConfig.publish,
127
- ...destination,
128
- },
129
- }
130
- : resolvedConfig;
131
- const request = toExplainerRunRequest({
132
- resolvedConfig: requestConfig,
133
- recipe,
134
- slug,
135
- outputRoot,
136
- factBase: bound.factBase,
137
- mode,
138
- durabilityStrategy,
139
- artDirection,
140
- defaultMode,
141
- renderStrategy,
142
- retainRawArtDirection,
143
- });
144
-
145
- const coreModulePath = join(compatibility.coreRoot, 'scripts', 'run.mjs');
146
- let core;
147
- try {
148
- core = await import(pathToFileURL(coreModulePath).href);
149
- } catch (cause) {
150
- const error = new Error(
151
- `Compatible explainer-kit is missing its run entry point. Run \`${compatibility.guidance ?? 'oat tools update --pack utility --scope user'}\`.`,
152
- { cause },
153
- );
154
- error.code = 'E_CORE_INCOMPATIBLE';
155
- throw error;
156
- }
157
- if (typeof core.runExplainer !== 'function') {
158
- const error = new Error(
159
- 'Compatible explainer-kit does not export runExplainer(request, options).',
160
- );
161
- error.code = 'E_CORE_INCOMPATIBLE';
162
- throw error;
163
- }
164
-
165
- const lifecycleAuthor = await resolveLifecycleAuthor({
166
- author,
167
- authorModulePath,
168
- coreOptions,
169
- });
170
- const lifecycleSetPlanner = await resolveLifecycleSetPlanner({
171
- planSet,
172
- planSetModulePath,
173
- coreOptions,
174
- required: recipe === 'project-recap' && mode === 'unattended',
175
- });
176
- const lifecycleCritic = await resolveLifecycleCritic({
177
- critic,
178
- criticModulePath,
179
- coreOptions,
180
- });
181
- const reviewProvidersRequired =
182
- recipe === 'project-recap' && mode === 'unattended';
183
- const lifecycleBrowserSession = await resolveLifecycleBrowserSession({
184
- core,
185
- browserSession,
186
- browserSessionModulePath,
187
- browserProbe,
188
- browserProbeModulePath,
189
- coreOptions,
190
- required: reviewProvidersRequired,
191
- });
192
- const lifecycleVisualCritic = await resolveLifecycleVisualCritic({
193
- visualCritic,
194
- visualCriticModulePath,
195
- coreOptions,
196
- required: reviewProvidersRequired,
197
- });
198
- assertDistinctProviderRoles({
199
- author: lifecycleAuthor,
200
- factCritic: lifecycleCritic,
201
- browserProbe: lifecycleBrowserSession?.probe,
202
- visualCritic: lifecycleVisualCritic,
203
- });
204
- const result = await core.runExplainer(request, {
205
- ...coreOptions,
206
- ...(lifecycleSetPlanner && { planSet: lifecycleSetPlanner }),
207
- ...(lifecycleAuthor && { author: lifecycleAuthor }),
208
- ...(lifecycleCritic && { critic: lifecycleCritic }),
209
- ...(lifecycleBrowserSession && {
210
- browserSession: lifecycleBrowserSession,
211
- }),
212
- ...(lifecycleVisualCritic && { visualCritic: lifecycleVisualCritic }),
213
- ...(bound.sourceLoader && { sourceLoader: bound.sourceLoader }),
214
- ...(bound.sourceProvenance && {
215
- sourceProvenance: bound.sourceProvenance,
216
- }),
217
- reviewedSource: bound.reviewedSource,
218
- });
219
- const manifest = await readManifest(result, request);
220
- return {
221
- compatibility,
222
- request,
223
- manifest,
224
- result,
225
- marking: result.marking ?? null,
226
- publication: result.publication ?? null,
227
- outputRoot,
228
- destination,
229
- };
230
- }
231
-
232
- export function supportsAdaptiveSetPlanning(version) {
233
- if (typeof version !== 'string') return false;
234
- const match = version.match(/^(\d+)\.(\d+)\.(\d+)(?:-|$)/);
235
- if (!match) return false;
236
- const [major, minor, patch] = match.slice(1).map(Number);
237
- const [minimumMajor, minimumMinor, minimumPatch] =
238
- MINIMUM_CORE_VERSION.split('.').map(Number);
239
- return (
240
- major === minimumMajor &&
241
- (minor > minimumMinor || (minor === minimumMinor && patch >= minimumPatch))
242
- );
243
- }
244
-
245
- async function resolveLifecycleSetPlanner({
246
- planSet,
247
- planSetModulePath,
248
- coreOptions,
249
- required,
250
- }) {
251
- if (coreOptions?.planSet !== undefined) {
252
- throw new TypeError(
253
- 'coreOptions.planSet is not supported at the OAT adapter boundary; supply planSet directly.',
254
- );
255
- }
256
- if (planSet !== undefined && typeof planSet !== 'function') {
257
- throw new TypeError('planSet must be a function when supplied.');
258
- }
259
- if (planSet !== undefined && planSetModulePath !== undefined) {
260
- throw new Error(
261
- 'Supply only one provider-neutral set planner callback or set planner module entry point.',
262
- );
263
- }
264
- if (planSet === undefined && planSetModulePath === undefined) {
265
- if (!required) return null;
266
- const error = new Error(
267
- 'Unattended OAT project recaps require exactly one provider-neutral set planner callback or set planner module entry point.',
268
- );
269
- error.code = 'E_SET_PLANNER_REQUIRED';
270
- throw error;
271
- }
272
- if (planSetModulePath === undefined) {
273
- return planSet;
274
- }
275
- if (
276
- typeof planSetModulePath !== 'string' ||
277
- planSetModulePath.trim().length === 0
278
- ) {
279
- throw new TypeError('planSetModulePath must be a non-empty path.');
280
- }
281
-
282
- let plannerModule;
283
- try {
284
- plannerModule = await import(
285
- pathToFileURL(resolve(planSetModulePath.trim())).href
286
- );
287
- } catch (cause) {
288
- throw new Error(
289
- `Unable to load provider-neutral set planner module at ${planSetModulePath}.`,
290
- { cause },
291
- );
292
- }
293
- if (typeof plannerModule.planSet !== 'function') {
294
- throw new TypeError(
295
- 'Provider-neutral set planner module must export a planSet function.',
296
- );
297
- }
298
- return plannerModule.planSet;
299
- }
300
-
301
- async function resolveLifecycleAuthor({
302
- author,
303
- authorModulePath,
304
- coreOptions,
305
- }) {
306
- if (coreOptions?.author !== undefined) {
307
- throw new TypeError(
308
- 'coreOptions.author is not supported at the OAT adapter boundary; supply author directly.',
309
- );
310
- }
311
- if (author !== undefined && typeof author !== 'function') {
312
- throw new TypeError('author must be a function when supplied.');
313
- }
314
- if (author !== undefined && authorModulePath !== undefined) {
315
- throw new Error(
316
- 'Supply only one provider-neutral author callback or author module entry point.',
317
- );
318
- }
319
-
320
- if (author === undefined && authorModulePath === undefined) {
321
- const error = new Error(
322
- 'Unattended and interactive OAT explainer runs require exactly one provider-neutral author callback or author module entry point.',
323
- );
324
- error.code = 'E_AUTHOR_REQUIRED';
325
- throw error;
326
- }
327
- if (authorModulePath === undefined) {
328
- return author;
329
- }
330
- if (
331
- typeof authorModulePath !== 'string' ||
332
- authorModulePath.trim().length === 0
333
- ) {
334
- throw new TypeError('authorModulePath must be a non-empty path.');
335
- }
336
-
337
- let authorModule;
338
- try {
339
- authorModule = await import(
340
- pathToFileURL(resolve(authorModulePath.trim())).href
341
- );
342
- } catch (cause) {
343
- throw new Error(
344
- `Unable to load provider-neutral author module at ${authorModulePath}.`,
345
- { cause },
346
- );
347
- }
348
- if (typeof authorModule.author !== 'function') {
349
- throw new TypeError(
350
- 'Provider-neutral author module must export an author function.',
351
- );
352
- }
353
- return authorModule.author;
354
- }
355
-
356
- async function resolveLifecycleCritic({
357
- critic,
358
- criticModulePath,
359
- coreOptions,
360
- }) {
361
- const candidates = [
362
- typeof critic === 'function' ? critic : null,
363
- typeof coreOptions?.critic === 'function' ? coreOptions.critic : null,
364
- criticModulePath ? criticModulePath : null,
365
- ].filter(Boolean);
366
- if (candidates.length > 1) {
367
- throw new Error(
368
- 'Supply only one provider-neutral critic callback or critic module entry point.',
369
- );
370
- }
371
- if (critic !== undefined && typeof critic !== 'function') {
372
- throw new TypeError('critic must be a function when supplied.');
373
- }
374
- if (
375
- coreOptions?.critic !== undefined &&
376
- typeof coreOptions.critic !== 'function'
377
- ) {
378
- throw new TypeError('coreOptions.critic must be a function when supplied.');
379
- }
380
-
381
- let callback = critic ?? coreOptions?.critic;
382
- if (criticModulePath !== undefined) {
383
- if (
384
- typeof criticModulePath !== 'string' ||
385
- criticModulePath.trim().length === 0
386
- ) {
387
- throw new TypeError('criticModulePath must be a non-empty path.');
388
- }
389
- let criticModule;
390
- try {
391
- criticModule = await import(
392
- pathToFileURL(resolve(criticModulePath)).href
393
- );
394
- } catch (cause) {
395
- throw new Error(
396
- `Unable to load provider-neutral critic module at ${criticModulePath}.`,
397
- { cause },
398
- );
399
- }
400
- if (typeof criticModule.critic !== 'function') {
401
- throw new TypeError(
402
- 'Provider-neutral critic module must export a critic function.',
403
- );
404
- }
405
- callback = criticModule.critic;
406
- }
407
-
408
- if (!callback) {
409
- return null;
410
- }
411
- const wrapped = async (request) => {
412
- const result = await callback(request);
413
- if (
414
- !result ||
415
- typeof result !== 'object' ||
416
- typeof result.criticId !== 'string' ||
417
- !/^[a-z0-9][a-z0-9._-]*$/.test(result.criticId) ||
418
- !Array.isArray(result.findings) ||
419
- (result.executedAt !== undefined &&
420
- (typeof result.executedAt !== 'string' ||
421
- Number.isNaN(Date.parse(result.executedAt))))
422
- ) {
423
- throw new Error(
424
- 'Provider-neutral critic result does not match the critic result contract.',
425
- );
426
- }
427
- return result;
428
- };
429
- return markProviderIdentity(wrapped, callback);
430
- }
431
-
432
- async function resolveLifecycleBrowserSession({
433
- core,
434
- browserSession,
435
- browserSessionModulePath,
436
- browserProbe,
437
- browserProbeModulePath,
438
- coreOptions,
439
- required,
440
- }) {
441
- if (coreOptions?.browserProbe !== undefined) {
442
- throw new TypeError(
443
- 'coreOptions.browserProbe is not supported at the OAT adapter boundary; supply browserSession directly.',
444
- );
445
- }
446
- if (browserProbe !== undefined || browserProbeModulePath !== undefined) {
447
- throw new TypeError(
448
- 'Bare browserProbe callbacks are not supported at the OAT adapter boundary; supply a branded browserSession created by createBrowserProbeSession().',
449
- );
450
- }
451
- if (coreOptions?.browserSession !== undefined) {
452
- throw new TypeError(
453
- 'coreOptions.browserSession is not supported at the OAT adapter boundary; supply browserSession directly.',
454
- );
455
- }
456
- if (browserSession !== undefined && browserSessionModulePath !== undefined) {
457
- throw new Error(
458
- 'Supply only one browser session descriptor or browser session module entry point.',
459
- );
460
- }
461
- let resolvedSession = browserSession;
462
- if (browserSessionModulePath !== undefined) {
463
- if (
464
- typeof browserSessionModulePath !== 'string' ||
465
- browserSessionModulePath.trim().length === 0
466
- ) {
467
- throw new TypeError('browserSessionModulePath must be a non-empty path.');
468
- }
469
- let sessionModule;
470
- try {
471
- sessionModule = await import(
472
- pathToFileURL(resolve(browserSessionModulePath.trim())).href
473
- );
474
- } catch (cause) {
475
- throw new Error(
476
- `Unable to load browser session module at ${browserSessionModulePath}.`,
477
- { cause },
478
- );
479
- }
480
- resolvedSession = sessionModule.browserSession;
481
- }
482
- if (!resolvedSession) {
483
- if (!required) return null;
484
- const error = new Error(
485
- 'Unattended OAT project recaps require exactly one branded launched-Chromium browser session descriptor or browser session module entry point.',
486
- );
487
- error.code = 'E_BROWSER_PROBE_REQUIRED';
488
- throw error;
489
- }
490
- if (typeof core.assertBrowserProbeSession !== 'function') {
491
- const error = new Error(
492
- 'Compatible explainer-kit does not export browser session validation.',
493
- );
494
- error.code = 'E_CORE_INCOMPATIBLE';
495
- throw error;
496
- }
497
- try {
498
- return core.assertBrowserProbeSession(resolvedSession, {
499
- allowFixture: !required,
500
- });
501
- } catch {
502
- const error = new Error('Browser session validation failed.');
503
- error.code = 'E_BROWSER_PROBE_REQUIRED';
504
- throw error;
505
- }
506
- }
507
-
508
- async function resolveLifecycleVisualCritic({
509
- visualCritic,
510
- visualCriticModulePath,
511
- coreOptions,
512
- required,
513
- }) {
514
- if (coreOptions?.visualCritic !== undefined) {
515
- throw new TypeError(
516
- 'coreOptions.visualCritic is not supported at the OAT adapter boundary; supply visualCritic directly.',
517
- );
518
- }
519
- const callback = await resolveProviderCallback({
520
- callback: visualCritic,
521
- modulePath: visualCriticModulePath,
522
- exportName: 'visualCritic',
523
- label: 'visual critic',
524
- });
525
- if (!callback) {
526
- if (!required) return null;
527
- const error = new Error(
528
- 'Unattended OAT project recaps require exactly one provider-neutral visual critic callback or visual critic module entry point.',
529
- );
530
- error.code = 'E_VISUAL_CRITIC_REQUIRED';
531
- throw error;
532
- }
533
- const wrapped = async (request, evidenceInput) => {
534
- const result = await callback(request, evidenceInput);
535
- if (
536
- !result ||
537
- typeof result !== 'object' ||
538
- result.schemaVersion !== 'explainer-kit.visual-review-result/v1' ||
539
- typeof result.reviewId !== 'string' ||
540
- !/^[a-z0-9][a-z0-9._-]*$/.test(result.reviewId) ||
541
- result.requestId !== request?.requestId ||
542
- result.requestHash !== request?.requestHash ||
543
- typeof result.reviewedAt !== 'string' ||
544
- Number.isNaN(Date.parse(result.reviewedAt)) ||
545
- !['pass', 'correct', 'fail'].includes(result.disposition) ||
546
- !Array.isArray(result.artifactIds) ||
547
- result.artifactIds.length !== request?.renderedArtifacts?.length ||
548
- result.artifactIds.some(
549
- (artifactId, index) =>
550
- artifactId !== request.renderedArtifacts[index]?.artifactId,
551
- ) ||
552
- !Array.isArray(result.findings) ||
553
- result.findings.some((finding) => !finding || typeof finding !== 'object')
554
- ) {
555
- throw new Error(
556
- 'Provider-neutral visual critic result does not match the visual review result contract.',
557
- );
558
- }
559
- return result;
560
- };
561
- return markProviderIdentity(wrapped, callback);
562
- }
563
-
564
- async function resolveProviderCallback({
565
- callback,
566
- modulePath,
567
- exportName,
568
- label,
569
- }) {
570
- if (callback !== undefined && typeof callback !== 'function') {
571
- throw new TypeError(`${exportName} must be a function when supplied.`);
572
- }
573
- if (callback !== undefined && modulePath !== undefined) {
574
- throw new Error(
575
- `Supply only one provider-neutral ${label} callback or ${label} module entry point.`,
576
- );
577
- }
578
- if (modulePath === undefined) return callback ?? null;
579
- if (typeof modulePath !== 'string' || modulePath.trim().length === 0) {
580
- throw new TypeError(`${exportName}ModulePath must be a non-empty path.`);
581
- }
582
- let providerModule;
583
- try {
584
- providerModule = await import(
585
- pathToFileURL(resolve(modulePath.trim())).href
586
- );
587
- } catch (cause) {
588
- throw new Error(
589
- `Unable to load provider-neutral ${label} module at ${modulePath}.`,
590
- { cause },
591
- );
592
- }
593
- if (typeof providerModule[exportName] !== 'function') {
594
- throw new TypeError(
595
- `Provider-neutral ${label} module must export a ${exportName} function.`,
596
- );
597
- }
598
- return providerModule[exportName];
599
- }
600
-
601
- const PROVIDER_IDENTITY = Symbol('oat-explainer-provider-identity');
602
-
603
- function markProviderIdentity(wrapper, callback) {
604
- Object.defineProperty(wrapper, PROVIDER_IDENTITY, { value: callback });
605
- return wrapper;
606
- }
607
-
608
- function providerIdentity(callback) {
609
- return callback?.[PROVIDER_IDENTITY] ?? callback;
610
- }
611
-
612
- function assertDistinctProviderRoles(callbacks) {
613
- const entries = Object.entries(callbacks).filter(([, callback]) => callback);
614
- for (let left = 0; left < entries.length; left += 1) {
615
- for (let right = left + 1; right < entries.length; right += 1) {
616
- if (
617
- providerIdentity(entries[left][1]) ===
618
- providerIdentity(entries[right][1])
619
- ) {
620
- throw new Error(
621
- `Provider roles ${entries[left][0]} and ${entries[right][0]} must use distinct callback identities.`,
622
- );
623
- }
624
- }
625
- }
626
- }
627
-
628
- async function readManifest(result, request) {
629
- if (
630
- !result ||
631
- typeof result !== 'object' ||
632
- typeof result.manifestPath !== 'string'
633
- ) {
634
- throw new Error('explainer-kit returned no manifestPath.');
635
- }
636
- let manifest;
637
- try {
638
- manifest = JSON.parse(await readFile(result.manifestPath, 'utf8'));
639
- } catch (error) {
640
- if (
641
- error?.code === 'ENOENT' &&
642
- (result.outcome === 'failed' || result.outcome === 'incomplete')
643
- ) {
644
- return null;
645
- }
646
- throw error;
647
- }
648
- if (manifest.schemaVersion !== 'explainer-kit.manifest/v1') {
649
- throw new Error(
650
- `Unsupported core manifest version: ${manifest.schemaVersion ?? 'missing'}.`,
651
- );
652
- }
653
- if (
654
- manifest.recipe?.id !== request.recipe.id ||
655
- manifest.recipe?.version !== request.recipe.version
656
- ) {
657
- throw new Error(
658
- 'Core manifest recipe does not match the normalized request.',
659
- );
660
- }
661
- return manifest;
662
- }
663
-
664
- export async function runOatExplainerCli(
665
- argv = process.argv.slice(2),
666
- io = console,
667
- run = runOatExplainer,
668
- ) {
669
- try {
670
- if (argv.length !== 2 || argv[0] !== '--context') {
671
- throw new Error('Usage: run.mjs --context <adapter-context.json>');
672
- }
673
- const context = JSON.parse(await readFile(argv[1], 'utf8'));
674
- const result = await run(context);
675
- io.log(JSON.stringify(projectAdapterCliResult(result), null, 2));
676
- return result.result.outcome === 'failed' ? 1 : 0;
677
- } catch {
678
- io.error(
679
- JSON.stringify(
680
- {
681
- outcome: 'failed',
682
- reasons: [
683
- {
684
- stage: 'finalization',
685
- kind: 'pipeline-failure',
686
- count: 1,
687
- },
688
- ],
689
- },
690
- null,
691
- 2,
692
- ),
693
- );
694
- return 1;
695
- }
696
- }
697
-
698
- function projectAdapterCliResult(value) {
699
- if (!isObject(value) || !isObject(value.result)) {
700
- return {
701
- result: {
702
- outcome: 'failed',
703
- reasons: [
704
- {
705
- stage: 'finalization',
706
- kind: 'pipeline-failure',
707
- count: 1,
708
- },
709
- ],
710
- },
711
- };
712
- }
713
- const projected = {};
714
- for (const key of ['compatibility', 'request', 'manifest', 'destination']) {
715
- if (isObject(value[key])) projected[key] = structuredClone(value[key]);
716
- }
717
- for (const key of ['marking', 'outputRoot']) {
718
- if (typeof value[key] === 'string' || value[key] === null) {
719
- projected[key] = value[key];
720
- }
721
- }
722
- projected.result = projectAdapterRunResult(value.result);
723
- const publication = projectAdapterPublication(value.publication);
724
- if (publication) projected.publication = publication;
725
- return projected;
726
- }
727
-
728
- function projectAdapterRunResult(value) {
729
- const projected = {};
730
- for (const key of [
731
- 'runId',
732
- 'runRoot',
733
- 'manifestPath',
734
- 'buildRecordPath',
735
- 'outcome',
736
- 'marking',
737
- ]) {
738
- if (typeof value[key] === 'string') projected[key] = value[key];
739
- }
740
- if (Array.isArray(value.warnings)) {
741
- projected.warnings = [
742
- ...new Set(value.warnings.filter(isAdapterWarningCode)),
743
- ];
744
- }
745
- for (const key of ['discovery', 'approval']) {
746
- if (isObject(value[key])) projected[key] = structuredClone(value[key]);
747
- }
748
- const reasons = projectAdapterReasons(value.reasons);
749
- if (reasons.length > 0) projected.reasons = reasons;
750
- const visualReview = projectAdapterVisualReview(value.visualReview);
751
- if (visualReview) projected.visualReview = visualReview;
752
- const publication = projectAdapterPublication(value.publication);
753
- if (publication) projected.publication = publication;
754
- return projected;
755
- }
756
-
757
- function projectAdapterVisualReview(value) {
758
- if (!isObject(value)) return null;
759
- const reasons = projectAdapterReasons(value.reasons);
760
- if (
761
- value.schemaVersion !== 'explainer-kit.visual-review-evidence/v1' ||
762
- typeof value.requestHash !== 'string' ||
763
- ![1, 2].includes(value.attempt) ||
764
- !['pass', 'correct', 'failed'].includes(value.disposition) ||
765
- !Array.isArray(value.reasons) ||
766
- reasons.length !== value.reasons.length
767
- ) {
768
- return null;
769
- }
770
- return {
771
- schemaVersion: value.schemaVersion,
772
- requestHash: value.requestHash,
773
- attempt: value.attempt,
774
- disposition: value.disposition,
775
- reasons,
776
- };
777
- }
778
-
779
- function projectAdapterPublication(value) {
780
- if (!isObject(value)) return null;
781
- if (
782
- value.schemaVersion !== 'explainer-kit.publish-summary/v1' &&
783
- value.schemaVersion !== 'explainer-kit.publish-summary/v2'
784
- ) {
785
- return null;
786
- }
787
- return {
788
- schemaVersion: value.schemaVersion,
789
- ...(typeof value.receiptSchemaVersion === 'string' && {
790
- receiptSchemaVersion: value.receiptSchemaVersion,
791
- }),
792
- ...(typeof value.publicAccess === 'string' && {
793
- publicAccess: value.publicAccess,
794
- }),
795
- ...(Array.isArray(value.artifacts) && {
796
- artifacts: value.artifacts.map((artifact) =>
797
- isObject(artifact)
798
- ? {
799
- ...pickAdapterStringFields(artifact, [
800
- 'relativePath',
801
- 'publicUrl',
802
- 's3Uri',
803
- 'hash',
804
- 'contentType',
805
- ]),
806
- ...(isObject(artifact.source) && {
807
- source: pickAdapterStringFields(artifact.source, [
808
- 'kind',
809
- 'artifactId',
810
- 'name',
811
- ]),
812
- }),
813
- ...(isObject(artifact.objectVerification) && {
814
- objectVerification: projectAdapterVerification(
815
- artifact.objectVerification,
816
- ),
817
- }),
818
- ...(isObject(artifact.publicVerification) && {
819
- publicVerification: projectAdapterVerification(
820
- artifact.publicVerification,
821
- ),
822
- }),
823
- }
824
- : {},
825
- ),
826
- }),
827
- };
828
- }
829
-
830
- function projectAdapterVerification(value) {
831
- return {
832
- ...pickAdapterStringFields(value, ['status', 'method', 'hash']),
833
- ...(Number.isInteger(value.httpStatus) && {
834
- httpStatus: value.httpStatus,
835
- }),
836
- };
837
- }
838
-
839
- function projectAdapterReasons(value) {
840
- if (!Array.isArray(value)) return [];
841
- return value.flatMap((reason) => {
842
- if (
843
- !isObject(reason) ||
844
- ![
845
- 'planning',
846
- 'authoring',
847
- 'rendering',
848
- 'link-validation',
849
- 'browser-review',
850
- 'visual-review',
851
- 'durability',
852
- 'finalization',
853
- ].includes(reason.stage) ||
854
- ![
855
- 'finding',
856
- 'provider-failure',
857
- 'pipeline-failure',
858
- 'superseded',
859
- ].includes(reason.kind) ||
860
- !Number.isInteger(reason.count) ||
861
- reason.count < 1 ||
862
- reason.count > 50
863
- ) {
864
- return [];
865
- }
866
- return [
867
- {
868
- stage: reason.stage,
869
- kind: reason.kind,
870
- ...(typeof reason.artifactId === 'string' && {
871
- artifactId: reason.artifactId,
872
- }),
873
- count: reason.count,
874
- },
875
- ];
876
- });
877
- }
878
-
879
- function pickAdapterStringFields(value, keys) {
880
- return Object.fromEntries(
881
- keys.flatMap((key) =>
882
- typeof value[key] === 'string' ? [[key, value[key]]] : [],
883
- ),
884
- );
885
- }
886
-
887
- function isAdapterWarningCode(value) {
888
- return (
889
- typeof value === 'string' &&
890
- /^(?:fact-base-freshness-warning|theme-selection-normalized|durability-evidence-required|publish-receipt-evidence-required|(?:expansion|guideline|render|qa)-[a-z0-9-]+|visual-review-required:[a-z0-9-]+|stage-reopened:[a-z0-9-]+:[0-9TZ:.-]+|missing-(?:theme-token|required-anchor):[a-z0-9-]+)$/.test(
891
- value,
892
- )
893
- );
894
- }
895
-
896
- function isObject(value) {
897
- return value !== null && typeof value === 'object' && !Array.isArray(value);
898
- }
899
-
900
- if (
901
- process.argv[1] &&
902
- pathToFileURL(process.argv[1]).href === import.meta.url
903
- ) {
904
- process.exitCode = await runOatExplainerCli();
905
- }