@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.
- package/assets/agents/oat-reviewer.md +4 -3
- package/assets/agents/recon-worker.md +42 -8
- package/assets/bundle-metadata.json +1 -1
- package/assets/docs/cli-utilities/configuration.md +3 -56
- package/assets/docs/cli-utilities/tool-packs.md +4 -4
- package/assets/docs/contributing/code.md +2 -2
- package/assets/docs/contributing/index.md +0 -1
- package/assets/docs/reference/cli-reference.md +1 -1
- package/assets/docs/reference/troubleshooting.md +17 -40
- package/assets/docs/workflows/projects/artifacts.md +16 -34
- package/assets/docs/workflows/projects/lifecycle.md +20 -19
- package/assets/docs/workflows/skills/explainer-kit.md +180 -440
- package/assets/docs/workflows/skills/index.md +1 -2
- package/assets/docs/workflows/skills/recon.md +114 -23
- package/assets/public-package-versions.json +4 -4
- package/assets/skills/explainer-kit/SKILL.md +96 -171
- package/assets/skills/explainer-kit/recipes/engineer-tour.json +1 -22
- package/assets/skills/explainer-kit/recipes/program-recap.json +1 -29
- package/assets/skills/explainer-kit/recipes/project-explainer.json +1 -22
- package/assets/skills/explainer-kit/recipes/project-recap.v2.json +1 -43
- package/assets/skills/explainer-kit/references/fact-base-contract.md +76 -101
- package/assets/skills/explainer-kit/references/recap-authoring.md +99 -0
- package/assets/skills/explainer-kit/schemas/manifest.schema.json +13 -135
- package/assets/skills/explainer-kit/scripts/bundle.mjs +712 -0
- package/assets/skills/explainer-kit/scripts/lib/claim-subject.mjs +40 -0
- package/assets/skills/explainer-kit/scripts/lib/contracts.mjs +113 -1504
- package/assets/skills/explainer-kit/scripts/lib/failure.mjs +13 -0
- package/assets/skills/explainer-kit/scripts/lib/package-coverage.mjs +57 -403
- package/assets/skills/explainer-kit/scripts/lib/qa-result.mjs +168 -0
- package/assets/skills/explainer-kit/scripts/lib/qa.mjs +90 -488
- package/assets/skills/explainer-kit/scripts/lib/recipes.mjs +28 -545
- package/assets/skills/explainer-kit/scripts/lib/run-package.mjs +120 -0
- package/assets/skills/explainer-kit/scripts/lib/sanitize.mjs +42 -0
- package/assets/skills/explainer-kit/scripts/record.mjs +335 -0
- package/assets/skills/explainer-kit/scripts/verify.mjs +648 -0
- package/assets/skills/oat-explainer-kit/SKILL.md +57 -117
- package/assets/skills/oat-explainer-kit/references/config-contract.md +20 -64
- package/assets/skills/oat-explainer-kit/references/lifecycle-contract.md +85 -237
- package/assets/skills/oat-explainer-kit/scripts/bind-project-sources.mjs +18 -10
- package/assets/skills/oat-explainer-kit/scripts/check-core.mjs +1 -0
- package/assets/skills/oat-explainer-kit/scripts/check-terminal-outcome.mjs +256 -21
- package/assets/skills/oat-explainer-kit/scripts/persist-intent.mjs +82 -3
- package/assets/skills/oat-explainer-kit/scripts/resolve-config.mjs +0 -162
- package/assets/skills/oat-explainer-kit/scripts/resolve-intent.mjs +57 -123
- package/assets/skills/oat-project-autonomous/SKILL.md +14 -13
- package/assets/skills/oat-project-autonomous/references/gate-inventory.md +165 -165
- package/assets/skills/oat-project-complete/SKILL.md +168 -349
- package/assets/skills/oat-project-complete/scripts/consume-persisted-recap-intent.mjs +125 -0
- package/assets/skills/oat-project-complete/scripts/execute-synced-archive-entry.mjs +0 -30
- package/assets/skills/oat-project-complete/scripts/parse-completion-retry-fields.mjs +4 -37
- package/assets/skills/oat-project-complete/scripts/parse-synced-archive-resume-fields.mjs +17 -16
- package/assets/skills/oat-project-complete/scripts/recover-completion-receipts.mjs +9 -174
- package/assets/skills/oat-project-complete/scripts/resolve-completion-retry.mjs +1 -2
- package/assets/skills/oat-project-document/SKILL.md +1 -1
- package/assets/skills/oat-project-document/references/docs/autonomy-contract.md +165 -165
- package/assets/skills/oat-project-implement/SKILL.md +1 -1
- package/assets/skills/oat-project-implement/references/completion-and-closeout.md +44 -56
- package/assets/skills/oat-project-implement/references/docs/autonomy-contract.md +165 -165
- package/assets/skills/oat-project-lite/SKILL.md +1 -1
- package/assets/skills/oat-project-lite/references/docs/autonomy-contract.md +165 -165
- package/assets/skills/oat-project-plan/SKILL.md +5 -6
- package/assets/skills/oat-project-pr-final/SKILL.md +1 -1
- package/assets/skills/oat-project-pr-final/references/docs/autonomy-contract.md +165 -165
- package/assets/skills/oat-project-quick-start/SKILL.md +1 -1
- package/assets/skills/oat-project-quick-start/references/docs/autonomy-contract.md +165 -165
- package/assets/skills/oat-project-review-receive/SKILL.md +14 -1
- package/assets/skills/oat-project-summary/SKILL.md +35 -25
- package/assets/skills/oat-wave-execute/SKILL.md +11 -36
- package/assets/skills/oat-wave-program/SKILL.md +13 -37
- package/assets/skills/recon/SKILL.md +103 -40
- package/assets/skills/recon/references/packet-contract.md +150 -67
- package/assets/skills/recon/references/profiles.md +38 -16
- package/assets/skills/recon/references/worker-contract.md +39 -8
- package/assets/skills/recon/scripts/lib/contracts.mjs +703 -55
- package/assets/skills/recon/scripts/lib/routing.mjs +588 -0
- package/assets/skills/recon/scripts/lib/validated-run.mjs +2 -0
- package/assets/skills/recon/scripts/prepare-routing.mjs +88 -0
- package/assets/skills/recon/scripts/render-packet.mjs +69 -1
- package/assets/skills/recon/scripts/validate-packet.mjs +473 -56
- package/assets/skills/subagent-orchestration/SKILL.md +18 -9
- package/assets/skills/subagent-orchestration/references/model-selection-principles.md +17 -8
- package/dist/commands/config/index.d.ts.map +1 -1
- package/dist/commands/config/index.js +9 -113
- package/dist/commands/project/archive/archive-utils.d.ts.map +1 -1
- package/dist/commands/project/archive/archive-utils.js +32 -184
- package/dist/commands/project/archive/explainer-package-coverage.d.ts +3 -8
- package/dist/commands/project/archive/explainer-package-coverage.d.ts.map +1 -1
- package/dist/commands/project/archive/explainer-package-coverage.js +1 -1
- package/dist/config/oat-config.d.ts +0 -11
- package/dist/config/oat-config.d.ts.map +1 -1
- package/dist/config/oat-config.js +0 -34
- package/dist/config/resolve.d.ts.map +1 -1
- package/dist/config/resolve.js +0 -8
- package/dist/release/public-package-contract.d.ts.map +1 -1
- package/dist/release/public-package-contract.js +2 -10
- package/dist/validation/project-state.d.ts.map +1 -1
- package/dist/validation/project-state.js +42 -3
- package/package.json +2 -2
- package/assets/docs/contributing/explainer-kit-verification.md +0 -133
- package/assets/docs/workflows/skills/explainer-kit-providers.md +0 -155
- package/assets/skills/explainer-kit/briefs/deep-dive.md +0 -35
- package/assets/skills/explainer-kit/briefs/project-page.md +0 -38
- package/assets/skills/explainer-kit/briefs/supporting-diagram.md +0 -28
- package/assets/skills/explainer-kit/briefs/walkthrough-deck.md +0 -35
- package/assets/skills/explainer-kit/examples/project-explainer/content.md +0 -31
- package/assets/skills/explainer-kit/examples/project-explainer/fact-base.md +0 -22
- package/assets/skills/explainer-kit/examples/project-recap/content.md +0 -77
- package/assets/skills/explainer-kit/examples/project-recap/fact-base.json +0 -104
- package/assets/skills/explainer-kit/examples/project-recap/fact-base.md +0 -44
- package/assets/skills/explainer-kit/examples/theme-bundle.json +0 -87
- package/assets/skills/explainer-kit/recipes/project-recap.v1.json +0 -93
- package/assets/skills/explainer-kit/references/contracts.md +0 -324
- package/assets/skills/explainer-kit/references/destination-contract.md +0 -210
- package/assets/skills/explainer-kit/references/extension-contract.md +0 -80
- package/assets/skills/explainer-kit/references/golden-conformance.md +0 -80
- package/assets/skills/explainer-kit/references/visual-review.md +0 -71
- package/assets/skills/explainer-kit/schemas/author-request.v2.schema.json +0 -212
- package/assets/skills/explainer-kit/schemas/author-request.v3.schema.json +0 -241
- package/assets/skills/explainer-kit/schemas/author-result.v2.schema.json +0 -52
- package/assets/skills/explainer-kit/schemas/build-record.schema.json +0 -98
- package/assets/skills/explainer-kit/schemas/durability-evidence.schema.json +0 -53
- package/assets/skills/explainer-kit/schemas/publish-receipt.v1.schema.json +0 -97
- package/assets/skills/explainer-kit/schemas/publish-receipt.v2.schema.json +0 -215
- package/assets/skills/explainer-kit/schemas/publish-request.v1.schema.json +0 -32
- package/assets/skills/explainer-kit/schemas/publish-request.v2.schema.json +0 -34
- package/assets/skills/explainer-kit/schemas/run-request.schema.json +0 -147
- package/assets/skills/explainer-kit/schemas/set-plan.v1.schema.json +0 -149
- package/assets/skills/explainer-kit/schemas/terminal-evidence.v1.schema.json +0 -81
- package/assets/skills/explainer-kit/schemas/visual-review-evidence.v1.schema.json +0 -66
- package/assets/skills/explainer-kit/schemas/visual-review-request.v1.schema.json +0 -117
- package/assets/skills/explainer-kit/schemas/visual-review-result.v1.schema.json +0 -80
- package/assets/skills/explainer-kit/scripts/lib/catalog.mjs +0 -349
- package/assets/skills/explainer-kit/scripts/lib/content-approval.mjs +0 -418
- package/assets/skills/explainer-kit/scripts/lib/diagram.mjs +0 -514
- package/assets/skills/explainer-kit/scripts/lib/durability.mjs +0 -643
- package/assets/skills/explainer-kit/scripts/lib/fact-base.mjs +0 -664
- package/assets/skills/explainer-kit/scripts/lib/internal-references.mjs +0 -538
- package/assets/skills/explainer-kit/scripts/lib/markdown.mjs +0 -414
- package/assets/skills/explainer-kit/scripts/lib/publication-policy.mjs +0 -54
- package/assets/skills/explainer-kit/scripts/lib/records.mjs +0 -954
- package/assets/skills/explainer-kit/scripts/lib/render.mjs +0 -703
- package/assets/skills/explainer-kit/scripts/lib/s3-roots.mjs +0 -353
- package/assets/skills/explainer-kit/scripts/lib/s3-static.mjs +0 -759
- package/assets/skills/explainer-kit/scripts/lib/set-plan.mjs +0 -209
- package/assets/skills/explainer-kit/scripts/lib/source-backlinks.mjs +0 -218
- package/assets/skills/explainer-kit/scripts/lib/terminal-evidence.mjs +0 -157
- package/assets/skills/explainer-kit/scripts/lib/visual-review.mjs +0 -400
- package/assets/skills/explainer-kit/scripts/publish.mjs +0 -76
- package/assets/skills/explainer-kit/scripts/record-durability.mjs +0 -64
- package/assets/skills/explainer-kit/scripts/render-qa.mjs +0 -461
- package/assets/skills/explainer-kit/scripts/run.mjs +0 -3038
- package/assets/skills/explainer-kit/scripts/validate.mjs +0 -59
- package/assets/skills/oat-explainer-kit/references/author-callback.md +0 -53
- package/assets/skills/oat-explainer-kit/references/migration.md +0 -144
- package/assets/skills/oat-explainer-kit/references/visual-review-callback.md +0 -83
- package/assets/skills/oat-explainer-kit/scripts/derive-destination.mjs +0 -91
- package/assets/skills/oat-explainer-kit/scripts/finalize-tracked-run.mjs +0 -531
- package/assets/skills/oat-explainer-kit/scripts/probe-recap-seams.mjs +0 -371
- package/assets/skills/oat-explainer-kit/scripts/run.mjs +0 -905
- package/dist/commands/project/archive/explainer-source-backlinks.d.ts +0 -18
- package/dist/commands/project/archive/explainer-source-backlinks.d.ts.map +0 -1
- package/dist/commands/project/archive/explainer-source-backlinks.js +0 -27
- package/dist/commands/project/archive/explainer-terminal-evidence.d.ts +0 -29
- package/dist/commands/project/archive/explainer-terminal-evidence.d.ts.map +0 -1
- package/dist/commands/project/archive/explainer-terminal-evidence.js +0 -37
|
@@ -1,418 +0,0 @@
|
|
|
1
|
-
import { readFile } from 'node:fs/promises';
|
|
2
|
-
import { join } from 'node:path';
|
|
3
|
-
|
|
4
|
-
import { writeJsonAtomic } from './fs-safe.mjs';
|
|
5
|
-
import { loadRecipe, recipeFloor } from './recipes.mjs';
|
|
6
|
-
import { validateSafeRelativePath } from './safe-paths.mjs';
|
|
7
|
-
|
|
8
|
-
const APPROVAL_PATH = 'source/content-approval.json';
|
|
9
|
-
const APPROVAL_SCHEMA_V1 = 'explainer-kit.content-approval/v1';
|
|
10
|
-
const APPROVAL_SCHEMA_V2 = 'explainer-kit.content-approval/v2';
|
|
11
|
-
const MODES = new Set(['interactive', 'unattended']);
|
|
12
|
-
const STATUSES = new Set(['pending', 'approved', 'rejected']);
|
|
13
|
-
const ORIGINS = new Set(['floor', 'expansion']);
|
|
14
|
-
const AUTHORING_TYPES = new Set(['markdown', 'html']);
|
|
15
|
-
const CONTENT_EXTENSIONS = new Map([
|
|
16
|
-
['markdown', 'md'],
|
|
17
|
-
['html', 'html'],
|
|
18
|
-
]);
|
|
19
|
-
const SAFE_ID = /^[a-z0-9]+(?:-[a-z0-9]+)*$/;
|
|
20
|
-
const DECISIONS = new Map([
|
|
21
|
-
['approve', 'approved'],
|
|
22
|
-
['approved', 'approved'],
|
|
23
|
-
['reject', 'rejected'],
|
|
24
|
-
['rejected', 'rejected'],
|
|
25
|
-
]);
|
|
26
|
-
|
|
27
|
-
export async function resolveContentApproval(
|
|
28
|
-
run,
|
|
29
|
-
mode,
|
|
30
|
-
reviewedSource,
|
|
31
|
-
authorResultPaths,
|
|
32
|
-
artifacts,
|
|
33
|
-
) {
|
|
34
|
-
assertRun(run);
|
|
35
|
-
if (!MODES.has(mode)) {
|
|
36
|
-
throw new Error('Content approval mode must be interactive or unattended.');
|
|
37
|
-
}
|
|
38
|
-
if (artifacts !== undefined) {
|
|
39
|
-
assertArtifacts(artifacts);
|
|
40
|
-
}
|
|
41
|
-
if (authorResultPaths !== undefined) {
|
|
42
|
-
assertAuthorResultPaths(authorResultPaths);
|
|
43
|
-
}
|
|
44
|
-
|
|
45
|
-
const previous = await readContentApproval(run);
|
|
46
|
-
let record;
|
|
47
|
-
if (mode === 'unattended') {
|
|
48
|
-
assertAuthorResultPaths(authorResultPaths);
|
|
49
|
-
const provenance =
|
|
50
|
-
reviewedSource ?? approvedSourceProvenance(run.request.factBase);
|
|
51
|
-
assertProvenance(provenance);
|
|
52
|
-
record = {
|
|
53
|
-
schemaVersion: APPROVAL_SCHEMA_V2,
|
|
54
|
-
runId: run.runId,
|
|
55
|
-
mode,
|
|
56
|
-
status: 'approved',
|
|
57
|
-
marking: 'auto-drafted',
|
|
58
|
-
reviewedSource: structuredClone(provenance),
|
|
59
|
-
authorResultPaths: [...authorResultPaths],
|
|
60
|
-
attempts: previous?.attempts ?? [],
|
|
61
|
-
...optionalArtifacts(artifacts ?? previous?.artifacts),
|
|
62
|
-
};
|
|
63
|
-
} else if (reviewedSource === undefined) {
|
|
64
|
-
record = previous ?? {
|
|
65
|
-
schemaVersion: APPROVAL_SCHEMA_V2,
|
|
66
|
-
runId: run.runId,
|
|
67
|
-
mode,
|
|
68
|
-
status: 'pending',
|
|
69
|
-
attempts: [],
|
|
70
|
-
};
|
|
71
|
-
record = {
|
|
72
|
-
...record,
|
|
73
|
-
...optionalAuthorResultPaths(
|
|
74
|
-
authorResultPaths ?? previous?.authorResultPaths,
|
|
75
|
-
),
|
|
76
|
-
...optionalArtifacts(artifacts ?? previous?.artifacts),
|
|
77
|
-
};
|
|
78
|
-
} else {
|
|
79
|
-
const decision = normalizeDecision(reviewedSource.decision);
|
|
80
|
-
const attempt = interactiveAttempt(reviewedSource, decision);
|
|
81
|
-
record = {
|
|
82
|
-
schemaVersion: APPROVAL_SCHEMA_V2,
|
|
83
|
-
runId: run.runId,
|
|
84
|
-
mode,
|
|
85
|
-
status: decision,
|
|
86
|
-
attempts: [...(previous?.attempts ?? []), attempt],
|
|
87
|
-
...(decision === 'approved' && { marking: 'human-approved' }),
|
|
88
|
-
...(decision === 'approved' && {
|
|
89
|
-
reviewedSource: structuredClone(
|
|
90
|
-
reviewedSource.source ?? {
|
|
91
|
-
kind: 'human-review',
|
|
92
|
-
locator: 'source/content',
|
|
93
|
-
},
|
|
94
|
-
),
|
|
95
|
-
}),
|
|
96
|
-
...optionalAuthorResultPaths(
|
|
97
|
-
authorResultPaths ?? previous?.authorResultPaths,
|
|
98
|
-
),
|
|
99
|
-
...optionalArtifacts(artifacts ?? previous?.artifacts),
|
|
100
|
-
};
|
|
101
|
-
if (record.reviewedSource) {
|
|
102
|
-
assertProvenance(record.reviewedSource);
|
|
103
|
-
}
|
|
104
|
-
}
|
|
105
|
-
|
|
106
|
-
await writeJsonAtomic(run.runRoot, APPROVAL_PATH, record);
|
|
107
|
-
return {
|
|
108
|
-
status: record.status,
|
|
109
|
-
canResume: record.status === 'approved',
|
|
110
|
-
path: APPROVAL_PATH,
|
|
111
|
-
record,
|
|
112
|
-
};
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
export async function readContentApproval(run) {
|
|
116
|
-
assertRun(run);
|
|
117
|
-
try {
|
|
118
|
-
const previous = JSON.parse(
|
|
119
|
-
await readFile(join(run.runRoot, APPROVAL_PATH), 'utf8'),
|
|
120
|
-
);
|
|
121
|
-
if (previous.runId !== run.runId) {
|
|
122
|
-
throw new Error('Content approval record does not belong to this run.');
|
|
123
|
-
}
|
|
124
|
-
if (previous.schemaVersion === APPROVAL_SCHEMA_V1) {
|
|
125
|
-
return normalizeLegacyRecord(run, previous);
|
|
126
|
-
}
|
|
127
|
-
if (previous.schemaVersion !== APPROVAL_SCHEMA_V2) {
|
|
128
|
-
throw new Error('Content approval record has an unsupported version.');
|
|
129
|
-
}
|
|
130
|
-
assertApprovalRecord(previous);
|
|
131
|
-
return structuredClone(previous);
|
|
132
|
-
} catch (error) {
|
|
133
|
-
if (error?.code === 'ENOENT') return null;
|
|
134
|
-
throw error;
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
|
|
138
|
-
function normalizeLegacyRecord(run, previous) {
|
|
139
|
-
assertLegacyRecord(previous);
|
|
140
|
-
const marking =
|
|
141
|
-
previous.status === 'approved'
|
|
142
|
-
? previous.mode === 'unattended'
|
|
143
|
-
? 'auto-drafted'
|
|
144
|
-
: 'human-approved'
|
|
145
|
-
: undefined;
|
|
146
|
-
const record = {
|
|
147
|
-
schemaVersion: APPROVAL_SCHEMA_V2,
|
|
148
|
-
runId: previous.runId,
|
|
149
|
-
mode: previous.mode,
|
|
150
|
-
status: previous.status,
|
|
151
|
-
...(marking && { marking }),
|
|
152
|
-
...(previous.reviewedSource && {
|
|
153
|
-
reviewedSource: structuredClone(previous.reviewedSource),
|
|
154
|
-
}),
|
|
155
|
-
...(previous.authorResultPaths && {
|
|
156
|
-
authorResultPaths: [...previous.authorResultPaths],
|
|
157
|
-
}),
|
|
158
|
-
attempts: structuredClone(previous.attempts),
|
|
159
|
-
artifacts: legacyFloorArtifacts(run, previous.authorResultPaths),
|
|
160
|
-
};
|
|
161
|
-
assertApprovalRecord(record);
|
|
162
|
-
return record;
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
function legacyFloorArtifacts(run, authorResultPaths = []) {
|
|
166
|
-
const recipe = loadRecipe(run.request.recipe.id, run.request.recipe.version);
|
|
167
|
-
const resultPaths = new Set(authorResultPaths);
|
|
168
|
-
return recipeFloor(recipe).map((artifact) => {
|
|
169
|
-
const authorResultPath = `source/author/${artifact.id}.json`;
|
|
170
|
-
const authoring = artifact.authoring ?? 'markdown';
|
|
171
|
-
return {
|
|
172
|
-
artifactId: artifact.id,
|
|
173
|
-
origin: 'floor',
|
|
174
|
-
authoring,
|
|
175
|
-
contentPath: contentPathFor(artifact.id, authoring),
|
|
176
|
-
...(resultPaths.has(authorResultPath) && { authorResultPath }),
|
|
177
|
-
};
|
|
178
|
-
});
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
function contentPathFor(artifactId, authoring) {
|
|
182
|
-
const extension = CONTENT_EXTENSIONS.get(authoring);
|
|
183
|
-
if (!extension) {
|
|
184
|
-
throw new Error(`Unsupported content authoring mode: ${authoring}`);
|
|
185
|
-
}
|
|
186
|
-
return `source/content/${artifactId}.${extension}`;
|
|
187
|
-
}
|
|
188
|
-
|
|
189
|
-
function optionalArtifacts(artifacts) {
|
|
190
|
-
return artifacts === undefined
|
|
191
|
-
? {}
|
|
192
|
-
: { artifacts: structuredClone(artifacts) };
|
|
193
|
-
}
|
|
194
|
-
|
|
195
|
-
function optionalAuthorResultPaths(paths) {
|
|
196
|
-
return paths === undefined
|
|
197
|
-
? {}
|
|
198
|
-
: { authorResultPaths: structuredClone(paths) };
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
function assertLegacyRecord(record) {
|
|
202
|
-
if (
|
|
203
|
-
!isObject(record) ||
|
|
204
|
-
record.schemaVersion !== APPROVAL_SCHEMA_V1 ||
|
|
205
|
-
typeof record.runId !== 'string' ||
|
|
206
|
-
!MODES.has(record.mode) ||
|
|
207
|
-
!STATUSES.has(record.status) ||
|
|
208
|
-
!Array.isArray(record.attempts)
|
|
209
|
-
) {
|
|
210
|
-
throw new Error('Legacy content approval record is malformed.');
|
|
211
|
-
}
|
|
212
|
-
if (record.reviewedSource !== undefined) {
|
|
213
|
-
assertProvenance(record.reviewedSource);
|
|
214
|
-
}
|
|
215
|
-
if (record.authorResultPaths !== undefined) {
|
|
216
|
-
assertAuthorResultPaths(record.authorResultPaths);
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
function assertApprovalRecord(record) {
|
|
221
|
-
if (
|
|
222
|
-
!isObject(record) ||
|
|
223
|
-
record.schemaVersion !== APPROVAL_SCHEMA_V2 ||
|
|
224
|
-
typeof record.runId !== 'string' ||
|
|
225
|
-
!MODES.has(record.mode) ||
|
|
226
|
-
!STATUSES.has(record.status) ||
|
|
227
|
-
!Array.isArray(record.attempts)
|
|
228
|
-
) {
|
|
229
|
-
throw new Error('Content approval v2 record is malformed.');
|
|
230
|
-
}
|
|
231
|
-
const expectedMarking =
|
|
232
|
-
record.status !== 'approved'
|
|
233
|
-
? undefined
|
|
234
|
-
: record.mode === 'unattended'
|
|
235
|
-
? 'auto-drafted'
|
|
236
|
-
: 'human-approved';
|
|
237
|
-
if (record.marking !== expectedMarking) {
|
|
238
|
-
throw new Error('Content approval marking does not match mode and status.');
|
|
239
|
-
}
|
|
240
|
-
if (record.reviewedSource !== undefined) {
|
|
241
|
-
assertProvenance(record.reviewedSource);
|
|
242
|
-
}
|
|
243
|
-
if (record.authorResultPaths !== undefined) {
|
|
244
|
-
assertAuthorResultPaths(record.authorResultPaths);
|
|
245
|
-
}
|
|
246
|
-
if (record.artifacts !== undefined) {
|
|
247
|
-
assertArtifacts(record.artifacts);
|
|
248
|
-
}
|
|
249
|
-
}
|
|
250
|
-
|
|
251
|
-
function assertArtifacts(value) {
|
|
252
|
-
if (!Array.isArray(value) || value.length === 0) {
|
|
253
|
-
throw new Error('Content approval artifacts must be a non-empty array.');
|
|
254
|
-
}
|
|
255
|
-
const artifactIds = new Set();
|
|
256
|
-
const contentPaths = new Set();
|
|
257
|
-
const authorResultPaths = new Set();
|
|
258
|
-
for (const artifact of value) {
|
|
259
|
-
const keys = Object.keys(artifact ?? {}).sort();
|
|
260
|
-
const expectedKeys = [
|
|
261
|
-
'artifactId',
|
|
262
|
-
'authoring',
|
|
263
|
-
'contentPath',
|
|
264
|
-
'origin',
|
|
265
|
-
...(artifact?.profileId === undefined ? [] : ['profileId']),
|
|
266
|
-
...(artifact?.authorResultPath === undefined ? [] : ['authorResultPath']),
|
|
267
|
-
].sort();
|
|
268
|
-
if (
|
|
269
|
-
!isObject(artifact) ||
|
|
270
|
-
keys.length !== expectedKeys.length ||
|
|
271
|
-
keys.some((key, index) => key !== expectedKeys[index]) ||
|
|
272
|
-
!SAFE_ID.test(artifact.artifactId) ||
|
|
273
|
-
!ORIGINS.has(artifact.origin) ||
|
|
274
|
-
!AUTHORING_TYPES.has(artifact.authoring) ||
|
|
275
|
-
!validateSafeRelativePath(artifact.contentPath).valid
|
|
276
|
-
) {
|
|
277
|
-
throw new Error('Content approval artifact entry is malformed.');
|
|
278
|
-
}
|
|
279
|
-
if (
|
|
280
|
-
(artifact.origin === 'expansion') !==
|
|
281
|
-
(typeof artifact.profileId === 'string' &&
|
|
282
|
-
SAFE_ID.test(artifact.profileId))
|
|
283
|
-
) {
|
|
284
|
-
throw new Error(
|
|
285
|
-
'Expansion approval artifacts require a safe profileId, and floor artifacts forbid one.',
|
|
286
|
-
);
|
|
287
|
-
}
|
|
288
|
-
if (
|
|
289
|
-
artifact.authorResultPath !== undefined &&
|
|
290
|
-
(!validateSafeRelativePath(artifact.authorResultPath).valid ||
|
|
291
|
-
!/^source\/author\/[^/]+\.json$/.test(artifact.authorResultPath))
|
|
292
|
-
) {
|
|
293
|
-
throw new Error('Content approval authorResultPath is malformed.');
|
|
294
|
-
}
|
|
295
|
-
if (
|
|
296
|
-
artifactIds.has(artifact.artifactId) ||
|
|
297
|
-
contentPaths.has(artifact.contentPath) ||
|
|
298
|
-
(artifact.authorResultPath !== undefined &&
|
|
299
|
-
authorResultPaths.has(artifact.authorResultPath))
|
|
300
|
-
) {
|
|
301
|
-
throw new Error(
|
|
302
|
-
'Content approval artifact paths and ids must be unique.',
|
|
303
|
-
);
|
|
304
|
-
}
|
|
305
|
-
artifactIds.add(artifact.artifactId);
|
|
306
|
-
contentPaths.add(artifact.contentPath);
|
|
307
|
-
if (artifact.authorResultPath !== undefined) {
|
|
308
|
-
authorResultPaths.add(artifact.authorResultPath);
|
|
309
|
-
}
|
|
310
|
-
}
|
|
311
|
-
}
|
|
312
|
-
|
|
313
|
-
function interactiveAttempt(source, status) {
|
|
314
|
-
if (!isObject(source)) {
|
|
315
|
-
throw new TypeError('Interactive approval requires a review decision.');
|
|
316
|
-
}
|
|
317
|
-
if (
|
|
318
|
-
typeof source.reviewedAt !== 'string' ||
|
|
319
|
-
Number.isNaN(Date.parse(source.reviewedAt))
|
|
320
|
-
) {
|
|
321
|
-
throw new Error('Interactive approval requires a valid reviewedAt value.');
|
|
322
|
-
}
|
|
323
|
-
if (
|
|
324
|
-
source.reviewer !== undefined &&
|
|
325
|
-
(typeof source.reviewer !== 'string' || source.reviewer.length === 0)
|
|
326
|
-
) {
|
|
327
|
-
throw new Error(
|
|
328
|
-
'Interactive approval reviewer must be a non-empty string.',
|
|
329
|
-
);
|
|
330
|
-
}
|
|
331
|
-
const corrections = source.corrections ?? [];
|
|
332
|
-
if (
|
|
333
|
-
!Array.isArray(corrections) ||
|
|
334
|
-
corrections.some(
|
|
335
|
-
(correction) =>
|
|
336
|
-
typeof correction !== 'string' || correction.trim().length === 0,
|
|
337
|
-
)
|
|
338
|
-
) {
|
|
339
|
-
throw new Error('Approval corrections must be non-empty strings.');
|
|
340
|
-
}
|
|
341
|
-
if (status === 'rejected' && corrections.length === 0) {
|
|
342
|
-
throw new Error('Rejected content approval requires corrections.');
|
|
343
|
-
}
|
|
344
|
-
return {
|
|
345
|
-
decision: status === 'approved' ? 'approve' : 'reject',
|
|
346
|
-
reviewedAt: source.reviewedAt,
|
|
347
|
-
...(source.reviewer && { reviewer: source.reviewer }),
|
|
348
|
-
corrections: [...corrections],
|
|
349
|
-
};
|
|
350
|
-
}
|
|
351
|
-
|
|
352
|
-
function normalizeDecision(decision) {
|
|
353
|
-
const normalized = DECISIONS.get(decision);
|
|
354
|
-
if (!normalized) {
|
|
355
|
-
throw new Error('Review decision must be approve or reject.');
|
|
356
|
-
}
|
|
357
|
-
return normalized;
|
|
358
|
-
}
|
|
359
|
-
|
|
360
|
-
function approvedSourceProvenance(factBase) {
|
|
361
|
-
if (factBase?.mode === 'supplied') {
|
|
362
|
-
return {
|
|
363
|
-
kind: 'approved-fact-base',
|
|
364
|
-
locator: factBase.path,
|
|
365
|
-
};
|
|
366
|
-
}
|
|
367
|
-
return {
|
|
368
|
-
kind: 'approved-source-set',
|
|
369
|
-
locator: `federated:${factBase?.sources?.length ?? 0}`,
|
|
370
|
-
};
|
|
371
|
-
}
|
|
372
|
-
|
|
373
|
-
function assertProvenance(value) {
|
|
374
|
-
if (
|
|
375
|
-
!isObject(value) ||
|
|
376
|
-
typeof value.kind !== 'string' ||
|
|
377
|
-
value.kind.length === 0 ||
|
|
378
|
-
typeof value.locator !== 'string' ||
|
|
379
|
-
value.locator.length === 0
|
|
380
|
-
) {
|
|
381
|
-
throw new Error(
|
|
382
|
-
'Reviewed source provenance requires non-empty kind and locator values.',
|
|
383
|
-
);
|
|
384
|
-
}
|
|
385
|
-
}
|
|
386
|
-
|
|
387
|
-
function assertAuthorResultPaths(value) {
|
|
388
|
-
if (
|
|
389
|
-
!Array.isArray(value) ||
|
|
390
|
-
value.length === 0 ||
|
|
391
|
-
new Set(value).size !== value.length ||
|
|
392
|
-
value.some(
|
|
393
|
-
(path) =>
|
|
394
|
-
typeof path !== 'string' ||
|
|
395
|
-
!/^source\/author\/[^/]+\.json$/.test(path) ||
|
|
396
|
-
path.includes('..'),
|
|
397
|
-
)
|
|
398
|
-
) {
|
|
399
|
-
throw new Error(
|
|
400
|
-
'Unattended content approval requires unique source/author result paths.',
|
|
401
|
-
);
|
|
402
|
-
}
|
|
403
|
-
}
|
|
404
|
-
|
|
405
|
-
function assertRun(run) {
|
|
406
|
-
if (
|
|
407
|
-
!isObject(run) ||
|
|
408
|
-
typeof run.runId !== 'string' ||
|
|
409
|
-
typeof run.runRoot !== 'string' ||
|
|
410
|
-
!isObject(run.request)
|
|
411
|
-
) {
|
|
412
|
-
throw new TypeError('Run must be returned by initializeRun().');
|
|
413
|
-
}
|
|
414
|
-
}
|
|
415
|
-
|
|
416
|
-
function isObject(value) {
|
|
417
|
-
return typeof value === 'object' && value !== null && !Array.isArray(value);
|
|
418
|
-
}
|