@agwab/pi-workflow 0.1.2 → 0.2.0
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 +7 -13
- package/dist/compiler.d.ts +2 -0
- package/dist/compiler.js +27 -2
- package/dist/engine.d.ts +2 -0
- package/dist/engine.js +3 -2
- package/dist/extension.js +201 -16
- package/dist/store.js +1 -0
- package/dist/types.d.ts +3 -0
- package/dist/workflow-progress-health.d.ts +37 -0
- package/dist/workflow-progress-health.js +296 -0
- package/dist/workflow-runtime.d.ts +6 -0
- package/dist/workflow-runtime.js +33 -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 +1 -1
- package/package.json +6 -6
- package/src/compiler.ts +41 -2
- package/src/engine.ts +7 -16
- package/src/extension.ts +254 -22
- package/src/store.ts +1 -0
- package/src/types.ts +4 -0
- package/src/workflow-progress-health.ts +461 -0
- package/src/workflow-runtime.ts +50 -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
|
@@ -16,7 +16,11 @@ function findSource(sources, stageId) {
|
|
|
16
16
|
|
|
17
17
|
function researchSources(sources) {
|
|
18
18
|
return Object.entries(sources ?? {})
|
|
19
|
-
.filter(
|
|
19
|
+
.filter(
|
|
20
|
+
([specId]) =>
|
|
21
|
+
specId === "research-questions" ||
|
|
22
|
+
specId.startsWith("research-questions."),
|
|
23
|
+
)
|
|
20
24
|
.map(([sourceId, source]) => ({ sourceId, source: asObject(source) }));
|
|
21
25
|
}
|
|
22
26
|
|
|
@@ -25,7 +29,9 @@ function asArray(value) {
|
|
|
25
29
|
}
|
|
26
30
|
|
|
27
31
|
function asObject(value) {
|
|
28
|
-
return value && typeof value === "object" && !Array.isArray(value)
|
|
32
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
33
|
+
? value
|
|
34
|
+
: {};
|
|
29
35
|
}
|
|
30
36
|
|
|
31
37
|
function stringOf(value) {
|
|
@@ -97,7 +103,11 @@ function compactClaim(claim, sourceId, index) {
|
|
|
97
103
|
const item = asObject(claim);
|
|
98
104
|
const id = stringOf(item.id);
|
|
99
105
|
return {
|
|
100
|
-
...(id
|
|
106
|
+
...(id
|
|
107
|
+
? { id }
|
|
108
|
+
: {
|
|
109
|
+
originLocator: `${sourceId}.claim-${String(index + 1).padStart(3, "0")}`,
|
|
110
|
+
}),
|
|
101
111
|
sourceId,
|
|
102
112
|
claim: stringOf(item.claim)?.slice(0, 600),
|
|
103
113
|
sourceUrls: compactStrings(item.sourceUrls, 6),
|
|
@@ -114,7 +124,11 @@ function compactSource(source, sourceId, index) {
|
|
|
114
124
|
const item = asObject(source);
|
|
115
125
|
const id = stringOf(item.id);
|
|
116
126
|
return {
|
|
117
|
-
...(id
|
|
127
|
+
...(id
|
|
128
|
+
? { id }
|
|
129
|
+
: {
|
|
130
|
+
originLocator: `${sourceId}.source-${String(index + 1).padStart(3, "0")}`,
|
|
131
|
+
}),
|
|
118
132
|
sourceId,
|
|
119
133
|
url: stringOf(item.url),
|
|
120
134
|
sourceRef: stringOf(item.sourceRef),
|
|
@@ -129,7 +143,11 @@ function compactGap(gap, sourceId, index) {
|
|
|
129
143
|
const item = asObject(gap);
|
|
130
144
|
const id = stringOf(item.id);
|
|
131
145
|
return {
|
|
132
|
-
...(id
|
|
146
|
+
...(id
|
|
147
|
+
? { id }
|
|
148
|
+
: {
|
|
149
|
+
originLocator: `${sourceId}.gap-${String(index + 1).padStart(3, "0")}`,
|
|
150
|
+
}),
|
|
133
151
|
sourceId,
|
|
134
152
|
lead: stringOf(item.lead ?? item.claim ?? item.note)?.slice(0, 500),
|
|
135
153
|
sourceUrls: compactStrings(item.sourceUrls, 6),
|
|
@@ -158,13 +176,24 @@ function countBy(values, keyFn) {
|
|
|
158
176
|
function overflowBySlot({ extractedFacts, claims, evidenceGaps }) {
|
|
159
177
|
return {
|
|
160
178
|
factsBySlot: countBy(extractedFacts, (fact) => fact.slotId),
|
|
161
|
-
claimsBySlot: countBy(
|
|
162
|
-
|
|
179
|
+
claimsBySlot: countBy(
|
|
180
|
+
claims.flatMap((claim) =>
|
|
181
|
+
claim.factSlotIds.map((slotId) => ({ slotId })),
|
|
182
|
+
),
|
|
183
|
+
(item) => item.slotId,
|
|
184
|
+
),
|
|
185
|
+
evidenceGapsBySlot: countBy(
|
|
186
|
+
evidenceGaps.flatMap((gap) =>
|
|
187
|
+
gap.factSlotIds.map((slotId) => ({ slotId })),
|
|
188
|
+
),
|
|
189
|
+
(item) => item.slotId,
|
|
190
|
+
),
|
|
163
191
|
};
|
|
164
192
|
}
|
|
165
193
|
|
|
166
194
|
function sourceRefCoverage(items) {
|
|
167
|
-
return items.filter((item) => compactStrings(item.sourceRefs, 1).length > 0)
|
|
195
|
+
return items.filter((item) => compactStrings(item.sourceRefs, 1).length > 0)
|
|
196
|
+
.length;
|
|
168
197
|
}
|
|
169
198
|
|
|
170
199
|
const CRITICAL_SLOT_TYPES = new Set([
|
|
@@ -219,7 +248,6 @@ function looksRetrievalGapInference(text) {
|
|
|
219
248
|
function looksDerivedRecommendation(text) {
|
|
220
249
|
return includesAny(text, [
|
|
221
250
|
/\b(?:feasible|minimum|practical|recommended|recommendation|checklist|tiering|implementation guidance|production-ready|turnkey)\b/i,
|
|
222
|
-
/\b(?:small[- ]?saas|api-only proxy logging|implementation tiers?)\b/i,
|
|
223
251
|
]);
|
|
224
252
|
}
|
|
225
253
|
|
|
@@ -230,13 +258,25 @@ function precisionIssuesForClaim(claim, slotMetaById) {
|
|
|
230
258
|
const sourceUrls = compactStrings(claim.sourceUrls, 1);
|
|
231
259
|
const issues = [];
|
|
232
260
|
if (factSlotIds.length === 0) issues.push("unslotted_claim");
|
|
233
|
-
if (factSlotIds.some((slotId) => !slotMetaById.has(slotId)))
|
|
261
|
+
if (factSlotIds.some((slotId) => !slotMetaById.has(slotId)))
|
|
262
|
+
issues.push("unknown_slot_id");
|
|
234
263
|
if (factSlotIds.length > 1) issues.push("bundled_slots");
|
|
235
|
-
if (
|
|
264
|
+
if (
|
|
265
|
+
includesAny(text, [/;/, /\b(?:and|plus|while|whereas|but)\b/i]) &&
|
|
266
|
+
factSlotIds.length > 1
|
|
267
|
+
)
|
|
236
268
|
issues.push("compound_or_bundled_text");
|
|
237
|
-
if (
|
|
269
|
+
if (
|
|
270
|
+
includesAny(text, [
|
|
271
|
+
/\b(?:should|must|best|ideal|recommended|recommendation|prefer|ought)\b/i,
|
|
272
|
+
])
|
|
273
|
+
)
|
|
238
274
|
issues.push("normative_language");
|
|
239
|
-
if (
|
|
275
|
+
if (
|
|
276
|
+
includesAny(text, [
|
|
277
|
+
/\b(?:all|always|never|any|every|guarantees?|proves?|only)\b/i,
|
|
278
|
+
])
|
|
279
|
+
)
|
|
240
280
|
issues.push("overbroad_quantifier");
|
|
241
281
|
if (
|
|
242
282
|
includesAny(text, [
|
|
@@ -245,21 +285,34 @@ function precisionIssuesForClaim(claim, slotMetaById) {
|
|
|
245
285
|
includesAny(text, [/;/, /\b(?:and|or|plus|with|while|whereas|but)\b/i])
|
|
246
286
|
)
|
|
247
287
|
issues.push("multi_obligation_claim");
|
|
248
|
-
if (
|
|
288
|
+
if (
|
|
289
|
+
looksQuantitative(text) &&
|
|
290
|
+
sourceRefs.length === 0 &&
|
|
291
|
+
sourceUrls.length === 0
|
|
292
|
+
)
|
|
249
293
|
issues.push("quantitative_without_visible_source");
|
|
250
294
|
if (looksRetrievalGapInference(text)) issues.push("retrieval_gap_inference");
|
|
251
295
|
if (looksDerivedRecommendation(text)) issues.push("derived_recommendation");
|
|
252
296
|
const mentionedEntities = entityMentions(text, [...slotMetaById.values()]);
|
|
253
|
-
if (
|
|
297
|
+
if (
|
|
298
|
+
mentionedEntities.length > 1 &&
|
|
299
|
+
includesAny(text, [
|
|
300
|
+
/\b(?:better|cheaper|faster|slower|higher|lower|vs\.?|versus|than)\b/i,
|
|
301
|
+
])
|
|
302
|
+
)
|
|
254
303
|
issues.push("entity_blend_risk");
|
|
255
304
|
return [...new Set(issues)];
|
|
256
305
|
}
|
|
257
306
|
|
|
258
307
|
function precisionAction(issues, { sourceBacked } = {}) {
|
|
259
|
-
if (issues.includes("quantitative_without_visible_source"))
|
|
308
|
+
if (issues.includes("quantitative_without_visible_source"))
|
|
309
|
+
return "preserve_or_gap_until_source_backed";
|
|
260
310
|
if (issues.includes("retrieval_gap_inference"))
|
|
261
|
-
return sourceBacked
|
|
262
|
-
|
|
311
|
+
return sourceBacked
|
|
312
|
+
? "verify_only_if_doc_scoped_or_replace_with_positive_source_claim"
|
|
313
|
+
: "preserve_as_gap_not_claim";
|
|
314
|
+
if (issues.includes("derived_recommendation"))
|
|
315
|
+
return "split_source_atoms_keep_recommendation_caveated";
|
|
263
316
|
if (
|
|
264
317
|
issues.includes("bundled_slots") ||
|
|
265
318
|
issues.includes("compound_or_bundled_text") ||
|
|
@@ -267,7 +320,11 @@ function precisionAction(issues, { sourceBacked } = {}) {
|
|
|
267
320
|
issues.includes("entity_blend_risk")
|
|
268
321
|
)
|
|
269
322
|
return "split_or_narrow_before_verification";
|
|
270
|
-
if (
|
|
323
|
+
if (
|
|
324
|
+
issues.includes("normative_language") ||
|
|
325
|
+
issues.includes("overbroad_quantifier")
|
|
326
|
+
)
|
|
327
|
+
return "narrow_or_demote";
|
|
271
328
|
return "eligible_if_slot_relevant";
|
|
272
329
|
}
|
|
273
330
|
|
|
@@ -280,35 +337,59 @@ function buildSlotPreservation({ planSlots, extractedFacts }) {
|
|
|
280
337
|
facts.push(fact);
|
|
281
338
|
factsBySlot.set(slotId, facts);
|
|
282
339
|
}
|
|
283
|
-
const requiredOrCriticalSlots = planSlots
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
slotId
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
340
|
+
const requiredOrCriticalSlots = planSlots
|
|
341
|
+
.filter(isRequiredOrCriticalSlot)
|
|
342
|
+
.map((slot) => {
|
|
343
|
+
const slotId = planSlotKey(slot);
|
|
344
|
+
const facts = factsBySlot.get(slotId) ?? [];
|
|
345
|
+
return {
|
|
346
|
+
slotId,
|
|
347
|
+
label: stringOf(slot.label),
|
|
348
|
+
type: stringOf(slot.type),
|
|
349
|
+
required: slot.required === true,
|
|
350
|
+
sourcePriority: stringOf(slot.sourcePriority),
|
|
351
|
+
verificationPriority: stringOf(slot.verificationPriority),
|
|
352
|
+
observationCount: facts.length,
|
|
353
|
+
representativeFactIds: compactStrings(
|
|
354
|
+
facts.map((fact) => fact.id),
|
|
355
|
+
4,
|
|
356
|
+
),
|
|
357
|
+
sourceRefs: compactStrings(
|
|
358
|
+
facts.flatMap((fact) => fact.sourceRefs),
|
|
359
|
+
6,
|
|
360
|
+
),
|
|
361
|
+
sourceUrls: compactStrings(
|
|
362
|
+
facts.flatMap((fact) => fact.sourceUrls),
|
|
363
|
+
6,
|
|
364
|
+
),
|
|
365
|
+
preservationNeed:
|
|
366
|
+
facts.length > 0
|
|
367
|
+
? "select_or_preserve_exact_slot_evidence"
|
|
368
|
+
: "record_explicit_gap",
|
|
369
|
+
};
|
|
370
|
+
});
|
|
300
371
|
return {
|
|
301
372
|
requiredOrCriticalSlots,
|
|
302
|
-
slotsWithEvidence: requiredOrCriticalSlots
|
|
303
|
-
|
|
373
|
+
slotsWithEvidence: requiredOrCriticalSlots
|
|
374
|
+
.filter((slot) => slot.observationCount > 0)
|
|
375
|
+
.map((slot) => slot.slotId),
|
|
376
|
+
missingRequiredOrCriticalSlots: requiredOrCriticalSlots
|
|
377
|
+
.filter((slot) => slot.observationCount === 0)
|
|
378
|
+
.map((slot) => slot.slotId),
|
|
304
379
|
};
|
|
305
380
|
}
|
|
306
381
|
|
|
307
382
|
function buildPrecisionGuard({ claims, planSlots }) {
|
|
308
|
-
const slotMetaById = new Map(
|
|
383
|
+
const slotMetaById = new Map(
|
|
384
|
+
planSlots
|
|
385
|
+
.map((slot) => [planSlotKey(slot), slot])
|
|
386
|
+
.filter(([slotId]) => slotId),
|
|
387
|
+
);
|
|
309
388
|
const guardedClaims = claims.map((claim) => {
|
|
310
389
|
const issues = precisionIssuesForClaim(claim, slotMetaById);
|
|
311
|
-
const sourceBacked =
|
|
390
|
+
const sourceBacked =
|
|
391
|
+
compactStrings(claim.sourceRefs, 1).length > 0 ||
|
|
392
|
+
compactStrings(claim.sourceUrls, 1).length > 0;
|
|
312
393
|
return {
|
|
313
394
|
id: stringOf(claim.id) ?? stringOf(claim.originLocator),
|
|
314
395
|
factSlotIds: compactStrings(claim.factSlotIds, 12),
|
|
@@ -322,8 +403,14 @@ function buildPrecisionGuard({ claims, planSlots }) {
|
|
|
322
403
|
schema: "deep-research-precision-guard-v1",
|
|
323
404
|
summary: {
|
|
324
405
|
totalClaims: guardedClaims.length,
|
|
325
|
-
flaggedClaims: guardedClaims.filter((claim) => claim.issues.length > 0)
|
|
326
|
-
|
|
406
|
+
flaggedClaims: guardedClaims.filter((claim) => claim.issues.length > 0)
|
|
407
|
+
.length,
|
|
408
|
+
issueCounts: countBy(
|
|
409
|
+
guardedClaims.flatMap((claim) =>
|
|
410
|
+
claim.issues.map((issue) => ({ issue })),
|
|
411
|
+
),
|
|
412
|
+
(item) => item.issue,
|
|
413
|
+
),
|
|
327
414
|
},
|
|
328
415
|
claims: guardedClaims.filter((claim) => claim.issues.length > 0),
|
|
329
416
|
instructions: {
|
|
@@ -360,28 +447,36 @@ export default async function normalizeInputPacket({ sources }) {
|
|
|
360
447
|
pushBounded(
|
|
361
448
|
extractedFacts,
|
|
362
449
|
overflow,
|
|
363
|
-
asArray(source.extractedFacts).map((fact, index) =>
|
|
450
|
+
asArray(source.extractedFacts).map((fact, index) =>
|
|
451
|
+
compactExtractedFact(fact, sourceId, index),
|
|
452
|
+
),
|
|
364
453
|
limits.extractedFacts,
|
|
365
454
|
"omittedExtractedFacts",
|
|
366
455
|
);
|
|
367
456
|
pushBounded(
|
|
368
457
|
claims,
|
|
369
458
|
overflow,
|
|
370
|
-
asArray(source.claims).map((claim, index) =>
|
|
459
|
+
asArray(source.claims).map((claim, index) =>
|
|
460
|
+
compactClaim(claim, sourceId, index),
|
|
461
|
+
),
|
|
371
462
|
limits.claims,
|
|
372
463
|
"omittedClaims",
|
|
373
464
|
);
|
|
374
465
|
pushBounded(
|
|
375
466
|
sourceCards,
|
|
376
467
|
overflow,
|
|
377
|
-
asArray(source.sources).map((item, index) =>
|
|
468
|
+
asArray(source.sources).map((item, index) =>
|
|
469
|
+
compactSource(item, sourceId, index),
|
|
470
|
+
),
|
|
378
471
|
limits.sources,
|
|
379
472
|
"omittedSources",
|
|
380
473
|
);
|
|
381
474
|
pushBounded(
|
|
382
475
|
evidenceGaps,
|
|
383
476
|
overflow,
|
|
384
|
-
asArray(source.additionalUnverifiedLeads).map((item, index) =>
|
|
477
|
+
asArray(source.additionalUnverifiedLeads).map((item, index) =>
|
|
478
|
+
compactGap(item, sourceId, index),
|
|
479
|
+
),
|
|
385
480
|
limits.evidenceGaps,
|
|
386
481
|
"omittedEvidenceGaps",
|
|
387
482
|
);
|
|
@@ -400,7 +495,9 @@ export default async function normalizeInputPacket({ sources }) {
|
|
|
400
495
|
expectedFinalShape: stringOf(plan.expectedFinalShape),
|
|
401
496
|
sourcePolicy: plan.sourcePolicy,
|
|
402
497
|
factSlots: planSlots,
|
|
403
|
-
verificationPriorities: asArray(plan.verificationPriorities).map(
|
|
498
|
+
verificationPriorities: asArray(plan.verificationPriorities).map(
|
|
499
|
+
compactVerificationPriority,
|
|
500
|
+
),
|
|
404
501
|
researchScopeCoverage: asArray(plan.researchScopeCoverage),
|
|
405
502
|
},
|
|
406
503
|
research: {
|
|
@@ -414,13 +511,25 @@ export default async function normalizeInputPacket({ sources }) {
|
|
|
414
511
|
precisionGuard,
|
|
415
512
|
ledgers: {
|
|
416
513
|
overflow,
|
|
417
|
-
overflowBySlot: overflowBySlot({
|
|
514
|
+
overflowBySlot: overflowBySlot({
|
|
515
|
+
extractedFacts,
|
|
516
|
+
claims,
|
|
517
|
+
evidenceGaps,
|
|
518
|
+
}),
|
|
418
519
|
slotFactCounts: countBy(extractedFacts, (fact) => fact.slotId),
|
|
419
|
-
claimSlotCounts: countBy(
|
|
520
|
+
claimSlotCounts: countBy(
|
|
521
|
+
claims.flatMap((claim) =>
|
|
522
|
+
claim.factSlotIds.map((slotId) => ({ slotId })),
|
|
523
|
+
),
|
|
524
|
+
(item) => item.slotId,
|
|
525
|
+
),
|
|
420
526
|
sourceRefCoverage: {
|
|
421
527
|
extractedFactsWithSourceRefs: sourceRefCoverage(extractedFacts),
|
|
422
528
|
claimsWithSourceRefs: sourceRefCoverage(claims),
|
|
423
|
-
sourcesWithSourceRefs: sourceCards.filter(
|
|
529
|
+
sourcesWithSourceRefs: sourceCards.filter(
|
|
530
|
+
(source) =>
|
|
531
|
+
typeof source.sourceRef === "string" && source.sourceRef,
|
|
532
|
+
).length,
|
|
424
533
|
},
|
|
425
534
|
},
|
|
426
535
|
instructions: {
|