@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.
Files changed (34) hide show
  1. package/README.md +7 -13
  2. package/dist/compiler.d.ts +2 -0
  3. package/dist/compiler.js +27 -2
  4. package/dist/engine.d.ts +2 -0
  5. package/dist/engine.js +3 -2
  6. package/dist/extension.js +201 -16
  7. package/dist/store.js +1 -0
  8. package/dist/types.d.ts +3 -0
  9. package/dist/workflow-progress-health.d.ts +37 -0
  10. package/dist/workflow-progress-health.js +296 -0
  11. package/dist/workflow-runtime.d.ts +6 -0
  12. package/dist/workflow-runtime.js +33 -10
  13. package/dist/workflow-view.d.ts +2 -0
  14. package/dist/workflow-view.js +97 -18
  15. package/dist/workflow-web-source.js +32 -14
  16. package/docs/usage.md +1 -1
  17. package/package.json +6 -6
  18. package/src/compiler.ts +41 -2
  19. package/src/engine.ts +7 -16
  20. package/src/extension.ts +254 -22
  21. package/src/store.ts +1 -0
  22. package/src/types.ts +4 -0
  23. package/src/workflow-progress-health.ts +461 -0
  24. package/src/workflow-runtime.ts +50 -13
  25. package/src/workflow-view.ts +186 -41
  26. package/src/workflow-web-source.ts +192 -69
  27. package/workflows/deep-research/helpers/claim-evidence-gate.mjs +111 -37
  28. package/workflows/deep-research/helpers/final-audit-packet.mjs +191 -14
  29. package/workflows/deep-research/helpers/normalize-input-packet.mjs +159 -50
  30. package/workflows/deep-research/helpers/render-executive.mjs +671 -37
  31. package/workflows/deep-research/helpers/sanitize-verification-candidates.mjs +624 -0
  32. package/workflows/deep-research/schemas/deep-research-executive-render-control.schema.json +2 -0
  33. package/workflows/deep-research/schemas/deep-research-final-synthesis-control.schema.json +110 -0
  34. 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(([specId]) => specId === "research-questions" || specId.startsWith("research-questions."))
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) ? 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 ? { id } : { originLocator: `${sourceId}.claim-${String(index + 1).padStart(3, "0")}` }),
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 ? { id } : { originLocator: `${sourceId}.source-${String(index + 1).padStart(3, "0")}` }),
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 ? { id } : { originLocator: `${sourceId}.gap-${String(index + 1).padStart(3, "0")}` }),
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(claims.flatMap((claim) => claim.factSlotIds.map((slotId) => ({ slotId }))), (item) => item.slotId),
162
- evidenceGapsBySlot: countBy(evidenceGaps.flatMap((gap) => gap.factSlotIds.map((slotId) => ({ slotId }))), (item) => item.slotId),
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).length;
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))) issues.push("unknown_slot_id");
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 (includesAny(text, [/;/, /\b(?:and|plus|while|whereas|but)\b/i]) && factSlotIds.length > 1)
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 (includesAny(text, [/\b(?:should|must|best|ideal|recommended|recommendation|prefer|ought)\b/i]))
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 (includesAny(text, [/\b(?:all|always|never|any|every|guarantees?|proves?|only)\b/i]))
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 (looksQuantitative(text) && sourceRefs.length === 0 && sourceUrls.length === 0)
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 (mentionedEntities.length > 1 && includesAny(text, [/\b(?:better|cheaper|faster|slower|higher|lower|vs\.?|versus|than)\b/i]))
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")) return "preserve_or_gap_until_source_backed";
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 ? "verify_only_if_doc_scoped_or_replace_with_positive_source_claim" : "preserve_as_gap_not_claim";
262
- if (issues.includes("derived_recommendation")) return "split_source_atoms_keep_recommendation_caveated";
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 (issues.includes("normative_language") || issues.includes("overbroad_quantifier")) return "narrow_or_demote";
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.filter(isRequiredOrCriticalSlot).map((slot) => {
284
- const slotId = planSlotKey(slot);
285
- const facts = factsBySlot.get(slotId) ?? [];
286
- return {
287
- slotId,
288
- label: stringOf(slot.label),
289
- type: stringOf(slot.type),
290
- required: slot.required === true,
291
- sourcePriority: stringOf(slot.sourcePriority),
292
- verificationPriority: stringOf(slot.verificationPriority),
293
- observationCount: facts.length,
294
- representativeFactIds: compactStrings(facts.map((fact) => fact.id), 4),
295
- sourceRefs: compactStrings(facts.flatMap((fact) => fact.sourceRefs), 6),
296
- sourceUrls: compactStrings(facts.flatMap((fact) => fact.sourceUrls), 6),
297
- preservationNeed: facts.length > 0 ? "select_or_preserve_exact_slot_evidence" : "record_explicit_gap",
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.filter((slot) => slot.observationCount > 0).map((slot) => slot.slotId),
303
- missingRequiredOrCriticalSlots: requiredOrCriticalSlots.filter((slot) => slot.observationCount === 0).map((slot) => slot.slotId),
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(planSlots.map((slot) => [planSlotKey(slot), slot]).filter(([slotId]) => slotId));
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 = compactStrings(claim.sourceRefs, 1).length > 0 || compactStrings(claim.sourceUrls, 1).length > 0;
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).length,
326
- issueCounts: countBy(guardedClaims.flatMap((claim) => claim.issues.map((issue) => ({ issue }))), (item) => item.issue),
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) => compactExtractedFact(fact, sourceId, 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) => compactClaim(claim, sourceId, 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) => compactSource(item, sourceId, 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) => compactGap(item, sourceId, 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(compactVerificationPriority),
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({ extractedFacts, claims, evidenceGaps }),
514
+ overflowBySlot: overflowBySlot({
515
+ extractedFacts,
516
+ claims,
517
+ evidenceGaps,
518
+ }),
418
519
  slotFactCounts: countBy(extractedFacts, (fact) => fact.slotId),
419
- claimSlotCounts: countBy(claims.flatMap((claim) => claim.factSlotIds.map((slotId) => ({ slotId }))), (item) => item.slotId),
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((source) => typeof source.sourceRef === "string" && source.sourceRef).length,
529
+ sourcesWithSourceRefs: sourceCards.filter(
530
+ (source) =>
531
+ typeof source.sourceRef === "string" && source.sourceRef,
532
+ ).length,
424
533
  },
425
534
  },
426
535
  instructions: {