@open-agent-toolkit/cli 0.2.72 → 0.2.74

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 (165) hide show
  1. package/assets/agents/oat-reviewer.md +4 -3
  2. package/assets/agents/recon-worker.md +42 -8
  3. package/assets/bundle-metadata.json +1 -1
  4. package/assets/docs/cli-utilities/configuration.md +3 -56
  5. package/assets/docs/cli-utilities/tool-packs.md +4 -4
  6. package/assets/docs/contributing/code.md +2 -2
  7. package/assets/docs/contributing/index.md +0 -1
  8. package/assets/docs/reference/cli-reference.md +1 -1
  9. package/assets/docs/reference/troubleshooting.md +17 -40
  10. package/assets/docs/workflows/projects/artifacts.md +16 -34
  11. package/assets/docs/workflows/projects/lifecycle.md +20 -19
  12. package/assets/docs/workflows/skills/explainer-kit.md +180 -440
  13. package/assets/docs/workflows/skills/index.md +1 -2
  14. package/assets/docs/workflows/skills/recon.md +114 -23
  15. package/assets/public-package-versions.json +4 -4
  16. package/assets/skills/explainer-kit/SKILL.md +96 -171
  17. package/assets/skills/explainer-kit/recipes/engineer-tour.json +1 -22
  18. package/assets/skills/explainer-kit/recipes/program-recap.json +1 -29
  19. package/assets/skills/explainer-kit/recipes/project-explainer.json +1 -22
  20. package/assets/skills/explainer-kit/recipes/project-recap.v2.json +1 -43
  21. package/assets/skills/explainer-kit/references/fact-base-contract.md +76 -101
  22. package/assets/skills/explainer-kit/references/recap-authoring.md +99 -0
  23. package/assets/skills/explainer-kit/schemas/manifest.schema.json +13 -135
  24. package/assets/skills/explainer-kit/scripts/bundle.mjs +712 -0
  25. package/assets/skills/explainer-kit/scripts/lib/claim-subject.mjs +40 -0
  26. package/assets/skills/explainer-kit/scripts/lib/contracts.mjs +113 -1504
  27. package/assets/skills/explainer-kit/scripts/lib/failure.mjs +13 -0
  28. package/assets/skills/explainer-kit/scripts/lib/package-coverage.mjs +57 -403
  29. package/assets/skills/explainer-kit/scripts/lib/qa-result.mjs +168 -0
  30. package/assets/skills/explainer-kit/scripts/lib/qa.mjs +90 -488
  31. package/assets/skills/explainer-kit/scripts/lib/recipes.mjs +28 -545
  32. package/assets/skills/explainer-kit/scripts/lib/run-package.mjs +120 -0
  33. package/assets/skills/explainer-kit/scripts/lib/sanitize.mjs +42 -0
  34. package/assets/skills/explainer-kit/scripts/record.mjs +335 -0
  35. package/assets/skills/explainer-kit/scripts/verify.mjs +648 -0
  36. package/assets/skills/oat-explainer-kit/SKILL.md +57 -117
  37. package/assets/skills/oat-explainer-kit/references/config-contract.md +20 -64
  38. package/assets/skills/oat-explainer-kit/references/lifecycle-contract.md +85 -237
  39. package/assets/skills/oat-explainer-kit/scripts/bind-project-sources.mjs +18 -10
  40. package/assets/skills/oat-explainer-kit/scripts/check-core.mjs +1 -0
  41. package/assets/skills/oat-explainer-kit/scripts/check-terminal-outcome.mjs +256 -21
  42. package/assets/skills/oat-explainer-kit/scripts/persist-intent.mjs +82 -3
  43. package/assets/skills/oat-explainer-kit/scripts/resolve-config.mjs +0 -162
  44. package/assets/skills/oat-explainer-kit/scripts/resolve-intent.mjs +57 -123
  45. package/assets/skills/oat-project-autonomous/SKILL.md +14 -13
  46. package/assets/skills/oat-project-autonomous/references/gate-inventory.md +165 -165
  47. package/assets/skills/oat-project-complete/SKILL.md +168 -349
  48. package/assets/skills/oat-project-complete/scripts/consume-persisted-recap-intent.mjs +125 -0
  49. package/assets/skills/oat-project-complete/scripts/execute-synced-archive-entry.mjs +0 -30
  50. package/assets/skills/oat-project-complete/scripts/parse-completion-retry-fields.mjs +4 -37
  51. package/assets/skills/oat-project-complete/scripts/parse-synced-archive-resume-fields.mjs +17 -16
  52. package/assets/skills/oat-project-complete/scripts/recover-completion-receipts.mjs +9 -174
  53. package/assets/skills/oat-project-complete/scripts/resolve-completion-retry.mjs +1 -2
  54. package/assets/skills/oat-project-document/SKILL.md +1 -1
  55. package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +165 -165
  56. package/assets/skills/oat-project-implement/SKILL.md +1 -1
  57. package/assets/skills/oat-project-implement/references/completion-and-closeout.md +44 -56
  58. package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +165 -165
  59. package/assets/skills/oat-project-lite/SKILL.md +1 -1
  60. package/assets/skills/oat-project-lite/references/docs/autonomy-contract.md +165 -165
  61. package/assets/skills/oat-project-plan/SKILL.md +5 -6
  62. package/assets/skills/oat-project-pr-final/SKILL.md +1 -1
  63. package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +165 -165
  64. package/assets/skills/oat-project-quick-start/SKILL.md +1 -1
  65. package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +165 -165
  66. package/assets/skills/oat-project-review-receive/SKILL.md +14 -1
  67. package/assets/skills/oat-project-summary/SKILL.md +35 -25
  68. package/assets/skills/oat-wave-execute/SKILL.md +11 -36
  69. package/assets/skills/oat-wave-program/SKILL.md +13 -37
  70. package/assets/skills/recon/SKILL.md +103 -40
  71. package/assets/skills/recon/references/packet-contract.md +150 -67
  72. package/assets/skills/recon/references/profiles.md +38 -16
  73. package/assets/skills/recon/references/worker-contract.md +39 -8
  74. package/assets/skills/recon/scripts/lib/contracts.mjs +703 -55
  75. package/assets/skills/recon/scripts/lib/routing.mjs +588 -0
  76. package/assets/skills/recon/scripts/lib/validated-run.mjs +2 -0
  77. package/assets/skills/recon/scripts/prepare-routing.mjs +88 -0
  78. package/assets/skills/recon/scripts/render-packet.mjs +69 -1
  79. package/assets/skills/recon/scripts/validate-packet.mjs +473 -56
  80. package/assets/skills/subagent-orchestration/SKILL.md +18 -9
  81. package/assets/skills/subagent-orchestration/references/model-selection-principles.md +17 -8
  82. package/dist/commands/config/index.d.ts.map +1 -1
  83. package/dist/commands/config/index.js +9 -113
  84. package/dist/commands/project/archive/archive-utils.d.ts.map +1 -1
  85. package/dist/commands/project/archive/archive-utils.js +32 -184
  86. package/dist/commands/project/archive/explainer-package-coverage.d.ts +3 -8
  87. package/dist/commands/project/archive/explainer-package-coverage.d.ts.map +1 -1
  88. package/dist/commands/project/archive/explainer-package-coverage.js +1 -1
  89. package/dist/config/oat-config.d.ts +0 -11
  90. package/dist/config/oat-config.d.ts.map +1 -1
  91. package/dist/config/oat-config.js +0 -34
  92. package/dist/config/resolve.d.ts.map +1 -1
  93. package/dist/config/resolve.js +0 -8
  94. package/dist/release/public-package-contract.d.ts.map +1 -1
  95. package/dist/release/public-package-contract.js +2 -10
  96. package/dist/validation/project-state.d.ts.map +1 -1
  97. package/dist/validation/project-state.js +42 -3
  98. package/package.json +2 -2
  99. package/assets/docs/contributing/explainer-kit-verification.md +0 -133
  100. package/assets/docs/workflows/skills/explainer-kit-providers.md +0 -155
  101. package/assets/skills/explainer-kit/briefs/deep-dive.md +0 -35
  102. package/assets/skills/explainer-kit/briefs/project-page.md +0 -38
  103. package/assets/skills/explainer-kit/briefs/supporting-diagram.md +0 -28
  104. package/assets/skills/explainer-kit/briefs/walkthrough-deck.md +0 -35
  105. package/assets/skills/explainer-kit/examples/project-explainer/content.md +0 -31
  106. package/assets/skills/explainer-kit/examples/project-explainer/fact-base.md +0 -22
  107. package/assets/skills/explainer-kit/examples/project-recap/content.md +0 -77
  108. package/assets/skills/explainer-kit/examples/project-recap/fact-base.json +0 -104
  109. package/assets/skills/explainer-kit/examples/project-recap/fact-base.md +0 -44
  110. package/assets/skills/explainer-kit/examples/theme-bundle.json +0 -87
  111. package/assets/skills/explainer-kit/recipes/project-recap.v1.json +0 -93
  112. package/assets/skills/explainer-kit/references/contracts.md +0 -324
  113. package/assets/skills/explainer-kit/references/destination-contract.md +0 -210
  114. package/assets/skills/explainer-kit/references/extension-contract.md +0 -80
  115. package/assets/skills/explainer-kit/references/golden-conformance.md +0 -80
  116. package/assets/skills/explainer-kit/references/visual-review.md +0 -71
  117. package/assets/skills/explainer-kit/schemas/author-request.v2.schema.json +0 -212
  118. package/assets/skills/explainer-kit/schemas/author-request.v3.schema.json +0 -241
  119. package/assets/skills/explainer-kit/schemas/author-result.v2.schema.json +0 -52
  120. package/assets/skills/explainer-kit/schemas/build-record.schema.json +0 -98
  121. package/assets/skills/explainer-kit/schemas/durability-evidence.schema.json +0 -53
  122. package/assets/skills/explainer-kit/schemas/publish-receipt.v1.schema.json +0 -97
  123. package/assets/skills/explainer-kit/schemas/publish-receipt.v2.schema.json +0 -215
  124. package/assets/skills/explainer-kit/schemas/publish-request.v1.schema.json +0 -32
  125. package/assets/skills/explainer-kit/schemas/publish-request.v2.schema.json +0 -34
  126. package/assets/skills/explainer-kit/schemas/run-request.schema.json +0 -147
  127. package/assets/skills/explainer-kit/schemas/set-plan.v1.schema.json +0 -149
  128. package/assets/skills/explainer-kit/schemas/terminal-evidence.v1.schema.json +0 -81
  129. package/assets/skills/explainer-kit/schemas/visual-review-evidence.v1.schema.json +0 -66
  130. package/assets/skills/explainer-kit/schemas/visual-review-request.v1.schema.json +0 -117
  131. package/assets/skills/explainer-kit/schemas/visual-review-result.v1.schema.json +0 -80
  132. package/assets/skills/explainer-kit/scripts/lib/catalog.mjs +0 -349
  133. package/assets/skills/explainer-kit/scripts/lib/content-approval.mjs +0 -418
  134. package/assets/skills/explainer-kit/scripts/lib/diagram.mjs +0 -514
  135. package/assets/skills/explainer-kit/scripts/lib/durability.mjs +0 -643
  136. package/assets/skills/explainer-kit/scripts/lib/fact-base.mjs +0 -664
  137. package/assets/skills/explainer-kit/scripts/lib/internal-references.mjs +0 -538
  138. package/assets/skills/explainer-kit/scripts/lib/markdown.mjs +0 -414
  139. package/assets/skills/explainer-kit/scripts/lib/publication-policy.mjs +0 -54
  140. package/assets/skills/explainer-kit/scripts/lib/records.mjs +0 -954
  141. package/assets/skills/explainer-kit/scripts/lib/render.mjs +0 -703
  142. package/assets/skills/explainer-kit/scripts/lib/s3-roots.mjs +0 -353
  143. package/assets/skills/explainer-kit/scripts/lib/s3-static.mjs +0 -759
  144. package/assets/skills/explainer-kit/scripts/lib/set-plan.mjs +0 -209
  145. package/assets/skills/explainer-kit/scripts/lib/source-backlinks.mjs +0 -218
  146. package/assets/skills/explainer-kit/scripts/lib/terminal-evidence.mjs +0 -157
  147. package/assets/skills/explainer-kit/scripts/lib/visual-review.mjs +0 -400
  148. package/assets/skills/explainer-kit/scripts/publish.mjs +0 -76
  149. package/assets/skills/explainer-kit/scripts/record-durability.mjs +0 -64
  150. package/assets/skills/explainer-kit/scripts/render-qa.mjs +0 -461
  151. package/assets/skills/explainer-kit/scripts/run.mjs +0 -3038
  152. package/assets/skills/explainer-kit/scripts/validate.mjs +0 -59
  153. package/assets/skills/oat-explainer-kit/references/author-callback.md +0 -53
  154. package/assets/skills/oat-explainer-kit/references/migration.md +0 -144
  155. package/assets/skills/oat-explainer-kit/references/visual-review-callback.md +0 -83
  156. package/assets/skills/oat-explainer-kit/scripts/derive-destination.mjs +0 -91
  157. package/assets/skills/oat-explainer-kit/scripts/finalize-tracked-run.mjs +0 -531
  158. package/assets/skills/oat-explainer-kit/scripts/probe-recap-seams.mjs +0 -371
  159. package/assets/skills/oat-explainer-kit/scripts/run.mjs +0 -905
  160. package/dist/commands/project/archive/explainer-source-backlinks.d.ts +0 -18
  161. package/dist/commands/project/archive/explainer-source-backlinks.d.ts.map +0 -1
  162. package/dist/commands/project/archive/explainer-source-backlinks.js +0 -27
  163. package/dist/commands/project/archive/explainer-terminal-evidence.d.ts +0 -29
  164. package/dist/commands/project/archive/explainer-terminal-evidence.d.ts.map +0 -1
  165. 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
- }