@agwab/pi-workflow 0.1.2 → 0.2.1
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/README.md +9 -13
- package/dist/compiler.d.ts +5 -5
- package/dist/compiler.js +82 -24
- package/dist/dynamic-generated-task-runtime.d.ts +2 -0
- package/dist/dynamic-generated-task-runtime.js +21 -8
- package/dist/engine.d.ts +6 -5
- package/dist/engine.js +39 -54
- package/dist/extension.js +211 -24
- package/dist/store.d.ts +3 -1
- package/dist/store.js +135 -38
- package/dist/subagent-backend.d.ts +4 -0
- package/dist/subagent-backend.js +128 -4
- package/dist/types.d.ts +5 -0
- package/dist/workflow-progress-health.d.ts +37 -0
- package/dist/workflow-progress-health.js +296 -0
- package/dist/workflow-runtime.d.ts +8 -0
- package/dist/workflow-runtime.js +63 -10
- package/dist/workflow-view.d.ts +2 -0
- package/dist/workflow-view.js +97 -18
- package/dist/workflow-web-source.js +32 -14
- package/docs/usage.md +12 -1
- package/package.json +6 -6
- package/src/compiler.ts +136 -41
- package/src/dynamic-generated-task-runtime.ts +47 -12
- package/src/engine.ts +55 -100
- package/src/extension.ts +270 -34
- package/src/store.ts +180 -44
- package/src/subagent-backend.ts +170 -6
- package/src/types.ts +10 -0
- package/src/workflow-progress-health.ts +461 -0
- package/src/workflow-runtime.ts +85 -13
- package/src/workflow-view.ts +186 -41
- package/src/workflow-web-source.ts +192 -69
- package/workflows/deep-research/helpers/claim-evidence-gate.mjs +111 -37
- package/workflows/deep-research/helpers/final-audit-packet.mjs +191 -14
- package/workflows/deep-research/helpers/normalize-input-packet.mjs +159 -50
- package/workflows/deep-research/helpers/render-executive.mjs +671 -37
- package/workflows/deep-research/helpers/sanitize-verification-candidates.mjs +624 -0
- package/workflows/deep-research/schemas/deep-research-executive-render-control.schema.json +2 -0
- package/workflows/deep-research/schemas/deep-research-final-synthesis-control.schema.json +110 -0
- package/workflows/deep-research/spec.json +41 -11
|
@@ -50,8 +50,11 @@ function collectUrls(value, urls = new Set()) {
|
|
|
50
50
|
}
|
|
51
51
|
|
|
52
52
|
function looksLikeLocalSourceRef(value) {
|
|
53
|
-
const text = String(value ?? "")
|
|
54
|
-
|
|
53
|
+
const text = String(value ?? "")
|
|
54
|
+
.trim()
|
|
55
|
+
.replace(/^(?:file|repo):/i, "")
|
|
56
|
+
.replace(/#L\d+(?:-L?\d+)?$/i, "");
|
|
57
|
+
return /^(?:\.?[\w.-]+\/)?[\w./-]+\.(?:md|json|ya?ml|ts|tsx|js|mjs|cjs|py|go|rs|zig|txt|sol|java|kt|swift|rb|php|c|cc|cpp|h|hpp)$/i.test(
|
|
55
58
|
text,
|
|
56
59
|
);
|
|
57
60
|
}
|
|
@@ -60,9 +63,19 @@ function collectEvidenceRefs(claim) {
|
|
|
60
63
|
const refs = new Set([...collectUrls(claim)]);
|
|
61
64
|
for (const row of Array.isArray(claim?.evidence) ? claim.evidence : []) {
|
|
62
65
|
if (!row || typeof row !== "object") continue;
|
|
63
|
-
for (const value of [
|
|
66
|
+
for (const value of [
|
|
67
|
+
row.url,
|
|
68
|
+
row.source,
|
|
69
|
+
row.file,
|
|
70
|
+
row.path,
|
|
71
|
+
row.sourceRef,
|
|
72
|
+
]) {
|
|
64
73
|
if (typeof value !== "string") continue;
|
|
65
|
-
if (
|
|
74
|
+
if (
|
|
75
|
+
/^https?:\/\//i.test(value) ||
|
|
76
|
+
isWorkflowSourceRef(value) ||
|
|
77
|
+
looksLikeLocalSourceRef(value)
|
|
78
|
+
)
|
|
66
79
|
refs.add(value.trim());
|
|
67
80
|
}
|
|
68
81
|
}
|
|
@@ -71,7 +84,8 @@ function collectEvidenceRefs(claim) {
|
|
|
71
84
|
|
|
72
85
|
function collectWorkflowSourceRefs(value, refs = new Set()) {
|
|
73
86
|
if (typeof value === "string") {
|
|
74
|
-
for (const match of value.matchAll(/\bwsrc_[a-f0-9]{32}\b/g))
|
|
87
|
+
for (const match of value.matchAll(/\bwsrc_[a-f0-9]{32}\b/g))
|
|
88
|
+
refs.add(match[0]);
|
|
75
89
|
return refs;
|
|
76
90
|
}
|
|
77
91
|
if (Array.isArray(value)) {
|
|
@@ -79,7 +93,8 @@ function collectWorkflowSourceRefs(value, refs = new Set()) {
|
|
|
79
93
|
return refs;
|
|
80
94
|
}
|
|
81
95
|
if (value && typeof value === "object") {
|
|
82
|
-
for (const item of Object.values(value))
|
|
96
|
+
for (const item of Object.values(value))
|
|
97
|
+
collectWorkflowSourceRefs(item, refs);
|
|
83
98
|
}
|
|
84
99
|
return refs;
|
|
85
100
|
}
|
|
@@ -90,7 +105,9 @@ function isWorkflowSourceRef(value) {
|
|
|
90
105
|
|
|
91
106
|
function sourceUrlArray(value) {
|
|
92
107
|
if (!Array.isArray(value)) return [];
|
|
93
|
-
return value
|
|
108
|
+
return value
|
|
109
|
+
.filter((item) => typeof item === "string" && item.trim())
|
|
110
|
+
.map((item) => item.trim());
|
|
94
111
|
}
|
|
95
112
|
|
|
96
113
|
function stripCitationUrlPunctuation(value) {
|
|
@@ -135,7 +152,9 @@ function buildUrlSourceRefLookup(normalizeInputPacket) {
|
|
|
135
152
|
if (!source || typeof source !== "object") continue;
|
|
136
153
|
addUrlSourceRef(urlToSourceRef, source.url, source.sourceRef);
|
|
137
154
|
}
|
|
138
|
-
const sourceRefIndex = asArray(
|
|
155
|
+
const sourceRefIndex = asArray(
|
|
156
|
+
normalizeInputPacket?.packet?.research?.sourceRefIndex,
|
|
157
|
+
);
|
|
139
158
|
for (const source of sourceRefIndex) {
|
|
140
159
|
if (!source || typeof source !== "object") continue;
|
|
141
160
|
addUrlSourceRef(urlToSourceRef, source.url, source.sourceRef);
|
|
@@ -162,7 +181,9 @@ function sourceRefsForUrls(urls, urlToSourceRef) {
|
|
|
162
181
|
// a keyword scan over the serialized claim, this cannot be satisfied by merely
|
|
163
182
|
// mentioning a URL/path in prose.
|
|
164
183
|
function hasFetchedEvidence(claim) {
|
|
165
|
-
return
|
|
184
|
+
return (
|
|
185
|
+
Array.isArray(claim?.evidence) && claim.evidence.some(hasStrongEvidenceRow)
|
|
186
|
+
);
|
|
166
187
|
}
|
|
167
188
|
|
|
168
189
|
function hasStrongEvidenceRow(row) {
|
|
@@ -170,26 +191,51 @@ function hasStrongEvidenceRow(row) {
|
|
|
170
191
|
const refs = [row.url, row.source, row.file, row.path, row.sourceRef].filter(
|
|
171
192
|
(value) => typeof value === "string",
|
|
172
193
|
);
|
|
173
|
-
const
|
|
174
|
-
(value) =>
|
|
175
|
-
/^https?:\/\//i.test(value) ||
|
|
176
|
-
isWorkflowSourceRef(value) ||
|
|
177
|
-
looksLikeLocalSourceRef(value),
|
|
194
|
+
const hasExternalRef = refs.some(
|
|
195
|
+
(value) => /^https?:\/\//i.test(value) || isWorkflowSourceRef(value),
|
|
178
196
|
);
|
|
197
|
+
const hasLocalRef = refs.some((value) => looksLikeLocalSourceRef(value));
|
|
198
|
+
const hasLocatedLocalRef =
|
|
199
|
+
hasLocalRef &&
|
|
200
|
+
(refs.some(hasLineFragment) || hasLocalEvidenceLocation(row));
|
|
201
|
+
const sourceRef = hasExternalRef || hasLocatedLocalRef;
|
|
179
202
|
const quote = typeof row.quote === "string" && row.quote.trim().length > 0;
|
|
180
203
|
if (!sourceRef || !quote) return false;
|
|
181
204
|
if (isCandidateEvidenceRow(row)) return false;
|
|
182
205
|
return true;
|
|
183
206
|
}
|
|
184
207
|
|
|
208
|
+
function hasLineFragment(value) {
|
|
209
|
+
return /#L\d+(?:-L?\d+)?$/i.test(String(value ?? "").trim());
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
function hasLocalEvidenceLocation(row) {
|
|
213
|
+
return [
|
|
214
|
+
row.line,
|
|
215
|
+
row.lineStart,
|
|
216
|
+
row.lineEnd,
|
|
217
|
+
row.lines,
|
|
218
|
+
row.excerptLocation,
|
|
219
|
+
].some(
|
|
220
|
+
(value) =>
|
|
221
|
+
typeof value === "number" ||
|
|
222
|
+
(typeof value === "string" && value.trim().length > 0),
|
|
223
|
+
);
|
|
224
|
+
}
|
|
225
|
+
|
|
185
226
|
function isCandidateEvidenceRow(row) {
|
|
186
|
-
return
|
|
227
|
+
return (
|
|
228
|
+
row?.candidateOnly === true ||
|
|
229
|
+
row?.matchType === "terms" ||
|
|
230
|
+
row?.sourceRead?.matchType === "terms"
|
|
231
|
+
);
|
|
187
232
|
}
|
|
188
233
|
|
|
189
234
|
function strongEvidenceIssue(claim) {
|
|
190
235
|
const rows = Array.isArray(claim?.evidence) ? claim.evidence : [];
|
|
191
236
|
if (rows.length === 0) return "missing_structured_evidence_rows";
|
|
192
|
-
if (rows.some(isCandidateEvidenceRow))
|
|
237
|
+
if (rows.some(isCandidateEvidenceRow))
|
|
238
|
+
return "candidate_only_evidence_not_strong";
|
|
193
239
|
return "evidence_rows_missing_source_or_quote";
|
|
194
240
|
}
|
|
195
241
|
|
|
@@ -275,7 +321,10 @@ function conservativeVerifierStatus(statuses) {
|
|
|
275
321
|
if (normalized.includes(status)) return status;
|
|
276
322
|
}
|
|
277
323
|
if (normalized.every((status) => status === "verified")) return "verified";
|
|
278
|
-
return
|
|
324
|
+
return (
|
|
325
|
+
normalized.find((status) => typeof status === "string" && status) ??
|
|
326
|
+
"unverified"
|
|
327
|
+
);
|
|
279
328
|
}
|
|
280
329
|
|
|
281
330
|
function issueForVerifierRow({ sourceId, claim, reason, claimId, index }) {
|
|
@@ -303,20 +352,24 @@ function gapForVerifierIssue(issue) {
|
|
|
303
352
|
|
|
304
353
|
function mergeVerifierRows(rows) {
|
|
305
354
|
const first = rows[0];
|
|
306
|
-
if (rows.length === 1)
|
|
355
|
+
if (rows.length === 1)
|
|
356
|
+
return { sourceId: first.sourceId, claim: first.claim, duplicate: null };
|
|
307
357
|
const sourceIds = rows.map((row) => row.sourceId);
|
|
308
358
|
const statusInputs = rows.map((row) => verdictOf(row.claim));
|
|
309
359
|
const selectedStatus = conservativeVerifierStatus(statusInputs);
|
|
310
360
|
const selectedRow =
|
|
311
|
-
rows.find(
|
|
312
|
-
|
|
361
|
+
rows.find(
|
|
362
|
+
(row) => canonicalVerifierStatus(verdictOf(row.claim)) === selectedStatus,
|
|
363
|
+
) ?? first;
|
|
313
364
|
const merged = { ...selectedRow.claim };
|
|
314
365
|
const evidence = rows.flatMap((row) =>
|
|
315
366
|
Array.isArray(row.claim?.evidence) ? row.claim.evidence : [],
|
|
316
367
|
);
|
|
317
368
|
if (evidence.length > 0) merged.evidence = evidence;
|
|
318
369
|
for (const field of ["sourceRefs", "sourceUrls", "factSlotIds"]) {
|
|
319
|
-
const values = compactStrings(
|
|
370
|
+
const values = compactStrings(
|
|
371
|
+
rows.flatMap((row) => row.claim?.[field] ?? []),
|
|
372
|
+
);
|
|
320
373
|
if (values.length > 0) merged[field] = values;
|
|
321
374
|
}
|
|
322
375
|
merged.status = selectedStatus;
|
|
@@ -362,7 +415,11 @@ function findSource(sources, stageId) {
|
|
|
362
415
|
|
|
363
416
|
export default async function claimEvidenceGate({ sources, options = {} }) {
|
|
364
417
|
const plan = findSource(sources, "plan");
|
|
365
|
-
const
|
|
418
|
+
const normalizeClaims = findSource(sources, "normalize-claims");
|
|
419
|
+
const sanitizedCandidates =
|
|
420
|
+
findSource(sources, "sanitize-claims") ??
|
|
421
|
+
findSource(sources, "sanitize-verification-candidates");
|
|
422
|
+
const normalized = sanitizedCandidates ?? normalizeClaims;
|
|
366
423
|
const normalizeInputPacket = findSource(sources, "normalize-input-packet");
|
|
367
424
|
const urlToSourceRef = buildUrlSourceRefLookup(normalizeInputPacket);
|
|
368
425
|
const candidateRecords = [];
|
|
@@ -406,7 +463,8 @@ export default async function claimEvidenceGate({ sources, options = {} }) {
|
|
|
406
463
|
);
|
|
407
464
|
// Legacy layout: when no verify-claims.* source ids exist (for example a
|
|
408
465
|
// single from: string dependency), fall back to every non-plan/non-normalize
|
|
409
|
-
// source.
|
|
466
|
+
// source. Exclude sanitizer sources because they are canonicalizer inputs, not
|
|
467
|
+
// verifier verdict rows.
|
|
410
468
|
const verifierClaims =
|
|
411
469
|
claims.length > 0
|
|
412
470
|
? claims
|
|
@@ -415,7 +473,9 @@ export default async function claimEvidenceGate({ sources, options = {} }) {
|
|
|
415
473
|
([specId]) =>
|
|
416
474
|
!specId.startsWith("plan") &&
|
|
417
475
|
!specId.startsWith("normalize-claims") &&
|
|
418
|
-
!specId.startsWith("normalize-input-packet")
|
|
476
|
+
!specId.startsWith("normalize-input-packet") &&
|
|
477
|
+
!specId.startsWith("sanitize-claims") &&
|
|
478
|
+
!specId.startsWith("sanitize-verification-candidates"),
|
|
419
479
|
)
|
|
420
480
|
.flatMap(([sourceId, source]) =>
|
|
421
481
|
asArray(source).map((claim, index) => ({ sourceId, claim, index })),
|
|
@@ -488,7 +548,13 @@ export default async function claimEvidenceGate({ sources, options = {} }) {
|
|
|
488
548
|
}
|
|
489
549
|
}
|
|
490
550
|
|
|
491
|
-
function auditClaim({
|
|
551
|
+
function auditClaim({
|
|
552
|
+
sourceId,
|
|
553
|
+
claim,
|
|
554
|
+
candidate,
|
|
555
|
+
claimId,
|
|
556
|
+
missingVerifierResult = false,
|
|
557
|
+
}) {
|
|
492
558
|
if (!claim || typeof claim !== "object") return;
|
|
493
559
|
gateSummary.total += 1;
|
|
494
560
|
const evidenceRefs = [...collectEvidenceRefs(claim)];
|
|
@@ -549,22 +615,24 @@ export default async function claimEvidenceGate({ sources, options = {} }) {
|
|
|
549
615
|
workflowSourceRefs.size - beforeUrlBackfillSourceRefCount;
|
|
550
616
|
}
|
|
551
617
|
if (workflowSourceRefs.size > 0) next.sourceRefs = [...workflowSourceRefs];
|
|
618
|
+
const httpSourceUrls = [
|
|
619
|
+
...new Set([
|
|
620
|
+
...sourceUrlArray(candidate?.sourceUrls).filter((ref) =>
|
|
621
|
+
/^https?:\/\//i.test(ref),
|
|
622
|
+
),
|
|
623
|
+
...evidenceRefs.filter((ref) => /^https?:\/\//i.test(ref)),
|
|
624
|
+
]),
|
|
625
|
+
];
|
|
552
626
|
if (
|
|
553
627
|
claimId &&
|
|
554
628
|
candidate &&
|
|
555
629
|
workflowSourceRefs.size === 0 &&
|
|
556
|
-
|
|
557
|
-
evidenceRefs.some((ref) => /^https?:\/\//i.test(ref)))
|
|
630
|
+
httpSourceUrls.length > 0
|
|
558
631
|
) {
|
|
559
632
|
const failure = {
|
|
560
633
|
claimId,
|
|
561
634
|
evidenceState: "source_ref_not_available",
|
|
562
|
-
sourceUrls:
|
|
563
|
-
...new Set([
|
|
564
|
-
...sourceUrlArray(candidate?.sourceUrls),
|
|
565
|
-
...evidenceRefs.filter((ref) => /^https?:\/\//i.test(ref)),
|
|
566
|
-
]),
|
|
567
|
-
],
|
|
635
|
+
sourceUrls: httpSourceUrls,
|
|
568
636
|
nextStep:
|
|
569
637
|
"Preserve sourceRefs from workflow_web_fetch_source through research and normalization when available.",
|
|
570
638
|
};
|
|
@@ -573,7 +641,8 @@ export default async function claimEvidenceGate({ sources, options = {} }) {
|
|
|
573
641
|
}
|
|
574
642
|
|
|
575
643
|
const verdict = verdictOf(next);
|
|
576
|
-
const exactQuantitativeForGate =
|
|
644
|
+
const exactQuantitativeForGate =
|
|
645
|
+
exactQuantitative || hasExactQuantitativeClaim(next);
|
|
577
646
|
if (
|
|
578
647
|
verdict === "verified" &&
|
|
579
648
|
options.requireFetchedEvidenceForVerified !== false &&
|
|
@@ -610,7 +679,8 @@ export default async function claimEvidenceGate({ sources, options = {} }) {
|
|
|
610
679
|
gateSummary.downgraded += 1;
|
|
611
680
|
remainingGaps.push({
|
|
612
681
|
claimId: next.id ?? next.claimId,
|
|
613
|
-
evidenceState:
|
|
682
|
+
evidenceState:
|
|
683
|
+
next.evidenceGate?.reasonCode ?? "insufficient_for_verified",
|
|
614
684
|
reason: next.evidenceGate?.reason,
|
|
615
685
|
sourceUrls: evidenceRefs,
|
|
616
686
|
nextStep:
|
|
@@ -653,7 +723,10 @@ export default async function claimEvidenceGate({ sources, options = {} }) {
|
|
|
653
723
|
status === "partiallySupported" ? "partially_supported" : status,
|
|
654
724
|
);
|
|
655
725
|
const hasStatusConflict = new Set(statuses).size > 1;
|
|
656
|
-
const duplicate = {
|
|
726
|
+
const duplicate = {
|
|
727
|
+
...merged.duplicate,
|
|
728
|
+
statusConflict: hasStatusConflict,
|
|
729
|
+
};
|
|
657
730
|
duplicateVerifierRows.push(duplicate);
|
|
658
731
|
gateSummary.duplicateVerifierClaims += 1;
|
|
659
732
|
gateSummary.duplicateVerifierRows += rows.length - 1;
|
|
@@ -764,6 +837,7 @@ export default async function claimEvidenceGate({ sources, options = {} }) {
|
|
|
764
837
|
droppedSlotIds,
|
|
765
838
|
},
|
|
766
839
|
identityJoinNotes,
|
|
767
|
-
precisionGuardDiagnostics:
|
|
840
|
+
precisionGuardDiagnostics:
|
|
841
|
+
normalizeInputPacket?.packet?.precisionGuard?.summary,
|
|
768
842
|
};
|
|
769
843
|
}
|
|
@@ -20,7 +20,9 @@ function asArray(value) {
|
|
|
20
20
|
}
|
|
21
21
|
|
|
22
22
|
function asObject(value) {
|
|
23
|
-
return value && typeof value === "object" && !Array.isArray(value)
|
|
23
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
24
|
+
? value
|
|
25
|
+
: {};
|
|
24
26
|
}
|
|
25
27
|
|
|
26
28
|
function stringOf(value) {
|
|
@@ -46,6 +48,14 @@ function compactStrings(values, limit = 5) {
|
|
|
46
48
|
return out;
|
|
47
49
|
}
|
|
48
50
|
|
|
51
|
+
function truncateText(value, limit = 240) {
|
|
52
|
+
const text = stringOf(value);
|
|
53
|
+
if (!text) return undefined;
|
|
54
|
+
const normalized = text.replace(/\s+/g, " ").trim();
|
|
55
|
+
if (normalized.length <= limit) return normalized;
|
|
56
|
+
return `${normalized.slice(0, Math.max(0, limit - 1)).trimEnd()}…`;
|
|
57
|
+
}
|
|
58
|
+
|
|
49
59
|
function compactClaimDigest(claim) {
|
|
50
60
|
const digest = asObject(claim);
|
|
51
61
|
return {
|
|
@@ -56,7 +66,11 @@ function compactClaimDigest(claim) {
|
|
|
56
66
|
factSlotIds: compactStrings(digest.factSlotIds, 12),
|
|
57
67
|
sourceRefs: compactStrings(digest.sourceRefs, 8),
|
|
58
68
|
sourceUrls: compactStrings(digest.sourceUrls, 8),
|
|
59
|
-
support: stringOf(
|
|
69
|
+
support: stringOf(
|
|
70
|
+
digest.verdictDigest?.support ??
|
|
71
|
+
digest.verdictDigest?.summary ??
|
|
72
|
+
digest.verdictDigest,
|
|
73
|
+
),
|
|
60
74
|
caveat: stringOf(digest.verdictDigest?.caveat ?? digest.caveat),
|
|
61
75
|
correctionOrCounterclaim: stringOf(digest.correctionOrCounterclaim),
|
|
62
76
|
...(digest.evidenceGate ? { evidenceGate: digest.evidenceGate } : {}),
|
|
@@ -81,6 +95,7 @@ function compactSlot(slot) {
|
|
|
81
95
|
function compactGap(gap) {
|
|
82
96
|
const item = asObject(gap);
|
|
83
97
|
return {
|
|
98
|
+
id: stringOf(item.id ?? item.gapId),
|
|
84
99
|
claimId: stringOf(item.claimId),
|
|
85
100
|
slotId: stringOf(item.slotId),
|
|
86
101
|
evidenceState: stringOf(item.evidenceState),
|
|
@@ -108,7 +123,9 @@ function compactDuplicateVerifierRow(row) {
|
|
|
108
123
|
const item = asObject(row);
|
|
109
124
|
return {
|
|
110
125
|
claimId: stringOf(item.claimId),
|
|
111
|
-
rowCount: Number.isFinite(Number(item.rowCount))
|
|
126
|
+
rowCount: Number.isFinite(Number(item.rowCount))
|
|
127
|
+
? Number(item.rowCount)
|
|
128
|
+
: undefined,
|
|
112
129
|
sourceIds: compactStrings(item.sourceIds, 8),
|
|
113
130
|
statusInputs: compactStrings(item.statusInputs, 8),
|
|
114
131
|
selectedStatus: stringOf(item.selectedStatus),
|
|
@@ -126,9 +143,124 @@ function countByStatus(slots) {
|
|
|
126
143
|
return counts;
|
|
127
144
|
}
|
|
128
145
|
|
|
146
|
+
function withGeneratedIds(items, prefix) {
|
|
147
|
+
return items.map((item, index) => ({
|
|
148
|
+
...item,
|
|
149
|
+
id: stringOf(item.id) ?? `${prefix}-${String(index + 1).padStart(3, "0")}`,
|
|
150
|
+
}));
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
function synthesisClaimDigest(claim) {
|
|
154
|
+
const item = compactClaimDigest(claim);
|
|
155
|
+
return {
|
|
156
|
+
id: item.id,
|
|
157
|
+
claim: truncateText(item.claim, 260),
|
|
158
|
+
status: item.status,
|
|
159
|
+
confidence: item.confidence,
|
|
160
|
+
factSlotIds: compactStrings(item.factSlotIds, 8),
|
|
161
|
+
support: truncateText(item.support, 240),
|
|
162
|
+
caveat: truncateText(item.caveat, 180),
|
|
163
|
+
correctionOrCounterclaim: truncateText(item.correctionOrCounterclaim, 180),
|
|
164
|
+
hasSourceUrls: compactStrings(item.sourceUrls, 1).length > 0,
|
|
165
|
+
hasSourceRefs: compactStrings(item.sourceRefs, 1).length > 0,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
|
|
169
|
+
function synthesisFactSlot(slot) {
|
|
170
|
+
const item = asObject(slot);
|
|
171
|
+
return {
|
|
172
|
+
slotId: stringOf(item.slotId),
|
|
173
|
+
label: truncateText(item.label, 120),
|
|
174
|
+
status: stringOf(item.status),
|
|
175
|
+
gapReason: truncateText(item.gapReason, 120),
|
|
176
|
+
parentImpact: truncateText(item.parentImpact, 120),
|
|
177
|
+
};
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
function synthesisGap(gap) {
|
|
181
|
+
const item = asObject(gap);
|
|
182
|
+
return {
|
|
183
|
+
id: stringOf(item.id),
|
|
184
|
+
kind: stringOf(item.kind),
|
|
185
|
+
claimId: stringOf(item.claimId),
|
|
186
|
+
slotId: stringOf(item.slotId),
|
|
187
|
+
evidenceState: stringOf(item.evidenceState),
|
|
188
|
+
reason: truncateText(item.reason, 220),
|
|
189
|
+
nextStep: truncateText(item.nextStep, 180),
|
|
190
|
+
scopeItem: truncateText(item.scopeItem, 160),
|
|
191
|
+
whyItMatters: truncateText(item.whyItMatters, 180),
|
|
192
|
+
};
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
function synthesisScopeCoverage(row) {
|
|
196
|
+
const item = asObject(row);
|
|
197
|
+
return {
|
|
198
|
+
scopeItem: truncateText(item.scopeItem ?? item.item ?? item.topic, 160),
|
|
199
|
+
status: stringOf(item.status ?? item.coverageStatus),
|
|
200
|
+
evidenceState: stringOf(item.evidenceState),
|
|
201
|
+
summary: truncateText(item.summary ?? item.reason, 220),
|
|
202
|
+
whyItMatters: truncateText(item.whyItMatters, 180),
|
|
203
|
+
};
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
function buildSynthesisInput({
|
|
207
|
+
plan,
|
|
208
|
+
factSlotCoverage,
|
|
209
|
+
claimDigests,
|
|
210
|
+
preservedClaims,
|
|
211
|
+
coverageGaps,
|
|
212
|
+
remainingGaps,
|
|
213
|
+
sourceRefJoinFailures,
|
|
214
|
+
researchScopeCoverage,
|
|
215
|
+
integritySummary,
|
|
216
|
+
audit,
|
|
217
|
+
}) {
|
|
218
|
+
return {
|
|
219
|
+
researchMetadata: {
|
|
220
|
+
depth: stringOf(plan.depth),
|
|
221
|
+
taskType: stringOf(plan.taskType),
|
|
222
|
+
expectedFinalShape: stringOf(plan.expectedFinalShape),
|
|
223
|
+
researchQuestions: asArray(plan.researchQuestions).length,
|
|
224
|
+
plannedFactSlots: asArray(plan.factSlots).length,
|
|
225
|
+
},
|
|
226
|
+
verdictCounts: asObject(audit.verdictCounts),
|
|
227
|
+
factSlotStatusCounts: countByStatus(factSlotCoverage),
|
|
228
|
+
integritySummary,
|
|
229
|
+
researchScopeCoverage: asArray(researchScopeCoverage)
|
|
230
|
+
.slice(0, 24)
|
|
231
|
+
.map(synthesisScopeCoverage),
|
|
232
|
+
factSlots: factSlotCoverage.map(synthesisFactSlot),
|
|
233
|
+
claims: claimDigests.map(synthesisClaimDigest),
|
|
234
|
+
preservedClaims: preservedClaims.slice(0, 12).map((claim) => ({
|
|
235
|
+
id: idOf(claim),
|
|
236
|
+
claim: truncateText(claim.claim, 240),
|
|
237
|
+
factSlotIds: compactStrings(claim.factSlotIds, 8),
|
|
238
|
+
whyItMatters: truncateText(claim.whyItMatters ?? claim.reason, 180),
|
|
239
|
+
})),
|
|
240
|
+
gaps: [
|
|
241
|
+
...remainingGaps.map((gap) =>
|
|
242
|
+
synthesisGap({ ...gap, kind: "remaining" }),
|
|
243
|
+
),
|
|
244
|
+
...coverageGaps.map((gap) => synthesisGap({ ...gap, kind: "coverage" })),
|
|
245
|
+
...sourceRefJoinFailures.map((gap) =>
|
|
246
|
+
synthesisGap({ ...gap, kind: "sourceRefJoinFailure" }),
|
|
247
|
+
),
|
|
248
|
+
],
|
|
249
|
+
};
|
|
250
|
+
}
|
|
251
|
+
|
|
129
252
|
export default async function finalAuditPacket({ sources }) {
|
|
130
253
|
const plan = asObject(findSource(sources, "plan"));
|
|
131
|
-
const
|
|
254
|
+
const normalizeClaims = asObject(findSource(sources, "normalize-claims"));
|
|
255
|
+
const sanitizedCandidates = asObject(
|
|
256
|
+
findSource(sources, "sanitize-claims") ??
|
|
257
|
+
findSource(sources, "sanitize-verification-candidates"),
|
|
258
|
+
);
|
|
259
|
+
const normalized =
|
|
260
|
+
Object.keys(sanitizedCandidates).length > 0
|
|
261
|
+
? sanitizedCandidates
|
|
262
|
+
: normalizeClaims;
|
|
263
|
+
const sanitizerDiagnostics = asObject(normalized.sanitizerDiagnostics);
|
|
132
264
|
const audit = asObject(findSource(sources, "audit-claims"));
|
|
133
265
|
const claimInventory = asObject(normalized.claimInventory);
|
|
134
266
|
const verificationCandidates = asArray(claimInventory.verificationCandidates);
|
|
@@ -137,12 +269,27 @@ export default async function finalAuditPacket({ sources }) {
|
|
|
137
269
|
const auditedIds = new Set(claimDigests.map(idOf).filter(Boolean));
|
|
138
270
|
const candidateIds = verificationCandidates.map(idOf).filter(Boolean);
|
|
139
271
|
const omittedCandidateIds = candidateIds.filter((id) => !auditedIds.has(id));
|
|
140
|
-
const factSlotCoverage = asArray(normalized.factSlotCoverage).map(
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
|
|
272
|
+
const factSlotCoverage = asArray(normalized.factSlotCoverage).map(
|
|
273
|
+
compactSlot,
|
|
274
|
+
);
|
|
275
|
+
const coverageGaps = withGeneratedIds(
|
|
276
|
+
asArray(normalized.coverageGaps).map(compactGap),
|
|
277
|
+
"gap-coverage",
|
|
278
|
+
);
|
|
279
|
+
const remainingGaps = withGeneratedIds(
|
|
280
|
+
asArray(audit.remainingGaps).map(compactGap),
|
|
281
|
+
"gap-remaining",
|
|
282
|
+
);
|
|
283
|
+
const sourceRefJoinFailures = withGeneratedIds(
|
|
284
|
+
asArray(audit.sourceRefJoinFailures).map(compactGap),
|
|
285
|
+
"gap-source-ref",
|
|
286
|
+
);
|
|
287
|
+
const invalidVerifierRows = asArray(audit.invalidVerifierRows).map(
|
|
288
|
+
compactVerifierIssue,
|
|
289
|
+
);
|
|
290
|
+
const duplicateVerifierRows = asArray(audit.duplicateVerifierRows).map(
|
|
291
|
+
compactDuplicateVerifierRow,
|
|
292
|
+
);
|
|
146
293
|
const gateSummary = asObject(audit.gateSummary);
|
|
147
294
|
const precisionGuardDiagnostics = asObject(audit.precisionGuardDiagnostics);
|
|
148
295
|
const sourceRefCoverage = {
|
|
@@ -154,10 +301,31 @@ export default async function finalAuditPacket({ sources }) {
|
|
|
154
301
|
).length,
|
|
155
302
|
sourceRefJoinFailures: sourceRefJoinFailures.length,
|
|
156
303
|
};
|
|
304
|
+
const integritySummary = {
|
|
305
|
+
omittedVerificationCandidateCount: omittedCandidateIds.length,
|
|
306
|
+
sourceRefJoinFailures: sourceRefJoinFailures.length,
|
|
307
|
+
invalidVerifierRows: invalidVerifierRows.length,
|
|
308
|
+
duplicateVerifierRows: duplicateVerifierRows.length,
|
|
309
|
+
missingVerifierResults: Number(gateSummary.missingVerifierResults ?? 0),
|
|
310
|
+
sourceRefCoverage,
|
|
311
|
+
};
|
|
312
|
+
const synthesisInput = buildSynthesisInput({
|
|
313
|
+
plan,
|
|
314
|
+
factSlotCoverage,
|
|
315
|
+
claimDigests,
|
|
316
|
+
preservedClaims,
|
|
317
|
+
coverageGaps,
|
|
318
|
+
remainingGaps,
|
|
319
|
+
sourceRefJoinFailures,
|
|
320
|
+
researchScopeCoverage: normalized.researchScopeCoverage,
|
|
321
|
+
integritySummary,
|
|
322
|
+
audit,
|
|
323
|
+
});
|
|
157
324
|
|
|
158
325
|
return {
|
|
159
326
|
schema: SCHEMA,
|
|
160
327
|
packet: {
|
|
328
|
+
synthesisInput,
|
|
161
329
|
researchMetadataSeed: {
|
|
162
330
|
depth: stringOf(plan.depth),
|
|
163
331
|
taskType: stringOf(plan.taskType),
|
|
@@ -165,9 +333,15 @@ export default async function finalAuditPacket({ sources }) {
|
|
|
165
333
|
researchQuestions: asArray(plan.researchQuestions).length,
|
|
166
334
|
sourcePolicy: asObject(plan.sourcePolicy),
|
|
167
335
|
plannedFactSlots: asArray(plan.factSlots).length,
|
|
168
|
-
filledFactSlots: factSlotCoverage.filter(
|
|
169
|
-
|
|
170
|
-
|
|
336
|
+
filledFactSlots: factSlotCoverage.filter(
|
|
337
|
+
(slot) => slot.status === "filled",
|
|
338
|
+
).length,
|
|
339
|
+
partialFactSlots: factSlotCoverage.filter(
|
|
340
|
+
(slot) => slot.status === "partial",
|
|
341
|
+
).length,
|
|
342
|
+
missingFactSlots: factSlotCoverage.filter(
|
|
343
|
+
(slot) => slot.status === "missing",
|
|
344
|
+
).length,
|
|
171
345
|
},
|
|
172
346
|
verdictCounts: asObject(audit.verdictCounts),
|
|
173
347
|
statusPartitions: asObject(audit.statusPartitions),
|
|
@@ -184,6 +358,7 @@ export default async function finalAuditPacket({ sources }) {
|
|
|
184
358
|
},
|
|
185
359
|
normalizerDiagnostics: {
|
|
186
360
|
precisionGuard: precisionGuardDiagnostics,
|
|
361
|
+
sanitizer: sanitizerDiagnostics,
|
|
187
362
|
},
|
|
188
363
|
preservedClaims: preservedClaims.map((claim) => ({
|
|
189
364
|
id: idOf(claim),
|
|
@@ -203,7 +378,9 @@ export default async function finalAuditPacket({ sources }) {
|
|
|
203
378
|
verifierIntegrity: {
|
|
204
379
|
invalidVerifierRows: invalidVerifierRows.length,
|
|
205
380
|
duplicateVerifierRows: duplicateVerifierRows.length,
|
|
206
|
-
missingVerifierResults: Number(
|
|
381
|
+
missingVerifierResults: Number(
|
|
382
|
+
gateSummary.missingVerifierResults ?? 0,
|
|
383
|
+
),
|
|
207
384
|
},
|
|
208
385
|
},
|
|
209
386
|
overflowLedger: {
|