@agwab/pi-workflow 0.1.1 → 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 +20 -15
- package/agents/researcher.md +17 -7
- package/dist/artifact-graph-runtime.js +1 -0
- package/dist/compiler.d.ts +2 -0
- package/dist/compiler.js +29 -4
- package/dist/dynamic-generated-task-runtime.js +4 -3
- package/dist/dynamic-runtime-bundle.js +3 -2
- package/dist/engine.d.ts +2 -0
- package/dist/engine.js +3 -2
- package/dist/extension.js +240 -16
- package/dist/store.js +1 -0
- package/dist/subagent-backend.js +82 -27
- package/dist/tool-metadata.d.ts +1 -0
- package/dist/tool-metadata.js +13 -1
- package/dist/types.d.ts +3 -0
- package/dist/workflow-artifact-extension.js +3 -2
- package/dist/workflow-artifact-tool.js +84 -4
- 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-extension.d.ts +43 -0
- package/dist/workflow-web-source-extension.js +1194 -0
- package/dist/workflow-web-source.d.ts +171 -0
- package/dist/workflow-web-source.js +915 -0
- package/docs/usage.md +32 -18
- package/node_modules/@agwab/pi-subagent/package.json +1 -1
- package/node_modules/@agwab/pi-subagent/src/api.ts +245 -132
- package/node_modules/@agwab/pi-subagent/src/artifacts/result.ts +243 -163
- package/node_modules/@agwab/pi-subagent/src/core/constants.ts +117 -90
- package/node_modules/@agwab/pi-subagent/src/core/validation.ts +728 -475
- package/node_modules/@agwab/pi-subagent/src/orchestrate/run.ts +305 -209
- package/node_modules/@agwab/pi-subagent/src/runners/headless-model.ts +750 -439
- package/node_modules/@agwab/pi-subagent/src/runners/tmux.ts +422 -268
- package/package.json +7 -7
- package/skills/workflow-guide/scaffolds/object-tool-fallback/schemas/fetch-control.schema.json +1 -1
- package/skills/workflow-guide/scaffolds/object-tool-fallback/spec.json +4 -3
- package/src/artifact-graph-runtime.ts +1 -0
- package/src/compiler.ts +43 -3
- package/src/dynamic-generated-task-runtime.ts +4 -2
- package/src/dynamic-runtime-bundle.ts +3 -2
- package/src/engine.ts +7 -16
- package/src/extension.ts +299 -22
- package/src/store.ts +1 -0
- package/src/subagent-backend.ts +121 -37
- package/src/tool-metadata.ts +22 -1
- package/src/types.ts +4 -0
- package/src/workflow-artifact-extension.ts +3 -2
- package/src/workflow-artifact-tool.ts +96 -4
- 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-extension.ts +1411 -0
- package/src/workflow-web-source.ts +1294 -0
- package/workflows/README.md +1 -1
- package/workflows/deep-research/helpers/claim-evidence-gate.mjs +552 -44
- package/workflows/deep-research/helpers/final-audit-packet.mjs +396 -0
- package/workflows/deep-research/helpers/normalize-input-packet.mjs +545 -0
- package/workflows/deep-research/helpers/render-executive.mjs +1199 -192
- package/workflows/deep-research/helpers/sanitize-verification-candidates.mjs +624 -0
- package/workflows/deep-research/schemas/deep-research-executive-render-control.schema.json +37 -8
- package/workflows/deep-research/schemas/deep-research-final-synthesis-control.schema.json +110 -0
- package/workflows/deep-research/schemas/deep-research-normalize-claims-control.schema.json +45 -4
- package/workflows/deep-research/schemas/deep-research-verify-claims-control.schema.json +0 -2
- package/workflows/deep-research/spec.json +71 -26
- package/workflows/deep-review/helpers/render-review-report.mjs +502 -0
- package/workflows/deep-review/schemas/deep-review-render-control.schema.json +50 -0
- package/workflows/deep-review/spec.json +22 -1
|
@@ -0,0 +1,545 @@
|
|
|
1
|
+
// Deterministic compact input packet for deep-research normalize-claims.
|
|
2
|
+
//
|
|
3
|
+
// This helper performs mechanical context hygiene before the normalizer LLM:
|
|
4
|
+
// it copies plan slots/priorities and compacts research-question observations
|
|
5
|
+
// into bounded arrays with overflow ledgers. It does not rank truth, select
|
|
6
|
+
// verification candidates, or discard evidence semantically.
|
|
7
|
+
|
|
8
|
+
const SCHEMA = "deep-research-normalize-input-packet-v2";
|
|
9
|
+
|
|
10
|
+
function findSource(sources, stageId) {
|
|
11
|
+
for (const [specId, source] of Object.entries(sources ?? {})) {
|
|
12
|
+
if (specId === stageId || specId.startsWith(`${stageId}.`)) return source;
|
|
13
|
+
}
|
|
14
|
+
return null;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function researchSources(sources) {
|
|
18
|
+
return Object.entries(sources ?? {})
|
|
19
|
+
.filter(
|
|
20
|
+
([specId]) =>
|
|
21
|
+
specId === "research-questions" ||
|
|
22
|
+
specId.startsWith("research-questions."),
|
|
23
|
+
)
|
|
24
|
+
.map(([sourceId, source]) => ({ sourceId, source: asObject(source) }));
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function asArray(value) {
|
|
28
|
+
return Array.isArray(value) ? value : [];
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function asObject(value) {
|
|
32
|
+
return value && typeof value === "object" && !Array.isArray(value)
|
|
33
|
+
? value
|
|
34
|
+
: {};
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
function stringOf(value) {
|
|
38
|
+
return typeof value === "string" ? value : undefined;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
function compactStrings(values, limit = 8) {
|
|
42
|
+
if (!Array.isArray(values)) return [];
|
|
43
|
+
const seen = new Set();
|
|
44
|
+
const out = [];
|
|
45
|
+
for (const value of values) {
|
|
46
|
+
if (typeof value !== "string") continue;
|
|
47
|
+
const text = value.trim();
|
|
48
|
+
if (!text || seen.has(text)) continue;
|
|
49
|
+
seen.add(text);
|
|
50
|
+
out.push(text);
|
|
51
|
+
if (out.length >= limit) break;
|
|
52
|
+
}
|
|
53
|
+
return out;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function compactPlanSlot(slot) {
|
|
57
|
+
const item = asObject(slot);
|
|
58
|
+
return {
|
|
59
|
+
id: stringOf(item.id),
|
|
60
|
+
label: stringOf(item.label),
|
|
61
|
+
type: stringOf(item.type),
|
|
62
|
+
required: item.required === true,
|
|
63
|
+
entities: compactStrings(item.entities, 8),
|
|
64
|
+
sourcePriority: stringOf(item.sourcePriority),
|
|
65
|
+
verificationPriority: stringOf(item.verificationPriority),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
function compactVerificationPriority(priority) {
|
|
70
|
+
const item = asObject(priority);
|
|
71
|
+
return {
|
|
72
|
+
id: stringOf(item.id),
|
|
73
|
+
targetSlots: compactStrings(item.targetSlots, 12),
|
|
74
|
+
claimFamily: stringOf(item.claimFamily),
|
|
75
|
+
priority: stringOf(item.priority),
|
|
76
|
+
reason: stringOf(item.reason),
|
|
77
|
+
evidenceRequirement: stringOf(item.evidenceRequirement),
|
|
78
|
+
};
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
function compactExtractedFact(fact, sourceId, index) {
|
|
82
|
+
const item = asObject(fact);
|
|
83
|
+
return {
|
|
84
|
+
id: `${sourceId}.fact-${String(index + 1).padStart(3, "0")}`,
|
|
85
|
+
sourceId,
|
|
86
|
+
slotId: stringOf(item.slotId),
|
|
87
|
+
slotLabel: stringOf(item.slotLabel),
|
|
88
|
+
entity: stringOf(item.entity),
|
|
89
|
+
value: item.value,
|
|
90
|
+
factType: stringOf(item.factType),
|
|
91
|
+
sourceUrls: compactStrings(item.sourceUrls, 6),
|
|
92
|
+
sourceRefs: compactStrings(item.sourceRefs, 6),
|
|
93
|
+
sourceTitleOrPublisher: stringOf(item.sourceTitleOrPublisher),
|
|
94
|
+
dateOrYear: stringOf(item.dateOrYear),
|
|
95
|
+
sourceQuality: stringOf(item.sourceQuality),
|
|
96
|
+
confidence: stringOf(item.confidence),
|
|
97
|
+
quote: stringOf(item.quote)?.slice(0, 500),
|
|
98
|
+
notes: stringOf(item.notes)?.slice(0, 300),
|
|
99
|
+
};
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function compactClaim(claim, sourceId, index) {
|
|
103
|
+
const item = asObject(claim);
|
|
104
|
+
const id = stringOf(item.id);
|
|
105
|
+
return {
|
|
106
|
+
...(id
|
|
107
|
+
? { id }
|
|
108
|
+
: {
|
|
109
|
+
originLocator: `${sourceId}.claim-${String(index + 1).padStart(3, "0")}`,
|
|
110
|
+
}),
|
|
111
|
+
sourceId,
|
|
112
|
+
claim: stringOf(item.claim)?.slice(0, 600),
|
|
113
|
+
sourceUrls: compactStrings(item.sourceUrls, 6),
|
|
114
|
+
sourceRefs: compactStrings(item.sourceRefs, 6),
|
|
115
|
+
sourceTitleOrPublisher: stringOf(item.sourceTitleOrPublisher),
|
|
116
|
+
dateOrYear: stringOf(item.dateOrYear),
|
|
117
|
+
sourceQuality: stringOf(item.sourceQuality),
|
|
118
|
+
scopeItems: compactStrings(item.scopeItems, 8),
|
|
119
|
+
factSlotIds: compactStrings(item.factSlotIds, 8),
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
function compactSource(source, sourceId, index) {
|
|
124
|
+
const item = asObject(source);
|
|
125
|
+
const id = stringOf(item.id);
|
|
126
|
+
return {
|
|
127
|
+
...(id
|
|
128
|
+
? { id }
|
|
129
|
+
: {
|
|
130
|
+
originLocator: `${sourceId}.source-${String(index + 1).padStart(3, "0")}`,
|
|
131
|
+
}),
|
|
132
|
+
sourceId,
|
|
133
|
+
url: stringOf(item.url),
|
|
134
|
+
sourceRef: stringOf(item.sourceRef),
|
|
135
|
+
title: stringOf(item.title ?? item.sourceTitleOrPublisher),
|
|
136
|
+
publisher: stringOf(item.publisher),
|
|
137
|
+
sourceQuality: stringOf(item.sourceQuality),
|
|
138
|
+
notes: stringOf(item.notes)?.slice(0, 300),
|
|
139
|
+
};
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
function compactGap(gap, sourceId, index) {
|
|
143
|
+
const item = asObject(gap);
|
|
144
|
+
const id = stringOf(item.id);
|
|
145
|
+
return {
|
|
146
|
+
...(id
|
|
147
|
+
? { id }
|
|
148
|
+
: {
|
|
149
|
+
originLocator: `${sourceId}.gap-${String(index + 1).padStart(3, "0")}`,
|
|
150
|
+
}),
|
|
151
|
+
sourceId,
|
|
152
|
+
lead: stringOf(item.lead ?? item.claim ?? item.note)?.slice(0, 500),
|
|
153
|
+
sourceUrls: compactStrings(item.sourceUrls, 6),
|
|
154
|
+
sourceRefs: compactStrings(item.sourceRefs, 6),
|
|
155
|
+
factSlotIds: compactStrings(item.factSlotIds, 8),
|
|
156
|
+
reason: stringOf(item.reason ?? item.gapReason),
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
function pushBounded(target, overflow, items, limit, overflowKind) {
|
|
161
|
+
for (const item of items) {
|
|
162
|
+
if (target.length < limit) target.push(item);
|
|
163
|
+
else overflow[overflowKind] = (overflow[overflowKind] ?? 0) + 1;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function countBy(values, keyFn) {
|
|
168
|
+
const counts = {};
|
|
169
|
+
for (const value of values) {
|
|
170
|
+
const key = keyFn(value) ?? "unknown";
|
|
171
|
+
counts[key] = (counts[key] ?? 0) + 1;
|
|
172
|
+
}
|
|
173
|
+
return counts;
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
function overflowBySlot({ extractedFacts, claims, evidenceGaps }) {
|
|
177
|
+
return {
|
|
178
|
+
factsBySlot: countBy(extractedFacts, (fact) => fact.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
|
+
),
|
|
191
|
+
};
|
|
192
|
+
}
|
|
193
|
+
|
|
194
|
+
function sourceRefCoverage(items) {
|
|
195
|
+
return items.filter((item) => compactStrings(item.sourceRefs, 1).length > 0)
|
|
196
|
+
.length;
|
|
197
|
+
}
|
|
198
|
+
|
|
199
|
+
const CRITICAL_SLOT_TYPES = new Set([
|
|
200
|
+
"numeric",
|
|
201
|
+
"pricing",
|
|
202
|
+
"ttl",
|
|
203
|
+
"limit",
|
|
204
|
+
"version",
|
|
205
|
+
"date",
|
|
206
|
+
"policy",
|
|
207
|
+
"security-impact",
|
|
208
|
+
]);
|
|
209
|
+
|
|
210
|
+
function planSlotKey(slot) {
|
|
211
|
+
return stringOf(slot?.id) ?? stringOf(slot?.slotId);
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
function isRequiredOrCriticalSlot(slot) {
|
|
215
|
+
return (
|
|
216
|
+
slot?.required === true ||
|
|
217
|
+
CRITICAL_SLOT_TYPES.has(String(slot?.type ?? "").toLowerCase()) ||
|
|
218
|
+
String(slot?.sourcePriority ?? "").toLowerCase() === "primary_required" ||
|
|
219
|
+
String(slot?.verificationPriority ?? "").toLowerCase() === "high"
|
|
220
|
+
);
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
function looksQuantitative(text) {
|
|
224
|
+
return /\b\d+(?:\.\d+)?\s*(?:%|percent|ms|s|sec|seconds|minutes|hours|x|×|usd|\$|k|m|b|tokens?|users?|samples?|n\s*=|gb|mb|tb|requests?|qps|rps|per\s+month|\/month)\b/i.test(
|
|
225
|
+
String(text ?? ""),
|
|
226
|
+
);
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
function includesAny(text, patterns) {
|
|
230
|
+
const value = String(text ?? "");
|
|
231
|
+
return patterns.some((pattern) => pattern.test(value));
|
|
232
|
+
}
|
|
233
|
+
|
|
234
|
+
function entityMentions(text, slots) {
|
|
235
|
+
const value = String(text ?? "").toLowerCase();
|
|
236
|
+
const entities = new Set(slots.flatMap((slot) => asArray(slot.entities)));
|
|
237
|
+
return [...entities].filter((entity) => value.includes(entity.toLowerCase()));
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
function looksRetrievalGapInference(text) {
|
|
241
|
+
return includesAny(text, [
|
|
242
|
+
/\b(?:not|never)\s+(?:established|retrieved|confirmed|found|available|documented)\b/i,
|
|
243
|
+
/\b(?:did not|does not|failed to)\s+(?:yield|show|establish|confirm|retrieve|find)\b/i,
|
|
244
|
+
/\b(?:unverified|no evidence|insufficient evidence|could not confirm|remains? unclear|not clear)\b/i,
|
|
245
|
+
]);
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
function looksDerivedRecommendation(text) {
|
|
249
|
+
return includesAny(text, [
|
|
250
|
+
/\b(?:feasible|minimum|practical|recommended|recommendation|checklist|tiering|implementation guidance|production-ready|turnkey)\b/i,
|
|
251
|
+
]);
|
|
252
|
+
}
|
|
253
|
+
|
|
254
|
+
function precisionIssuesForClaim(claim, slotMetaById) {
|
|
255
|
+
const text = stringOf(claim.claim) ?? "";
|
|
256
|
+
const factSlotIds = compactStrings(claim.factSlotIds, 12);
|
|
257
|
+
const sourceRefs = compactStrings(claim.sourceRefs, 1);
|
|
258
|
+
const sourceUrls = compactStrings(claim.sourceUrls, 1);
|
|
259
|
+
const issues = [];
|
|
260
|
+
if (factSlotIds.length === 0) issues.push("unslotted_claim");
|
|
261
|
+
if (factSlotIds.some((slotId) => !slotMetaById.has(slotId)))
|
|
262
|
+
issues.push("unknown_slot_id");
|
|
263
|
+
if (factSlotIds.length > 1) issues.push("bundled_slots");
|
|
264
|
+
if (
|
|
265
|
+
includesAny(text, [/;/, /\b(?:and|plus|while|whereas|but)\b/i]) &&
|
|
266
|
+
factSlotIds.length > 1
|
|
267
|
+
)
|
|
268
|
+
issues.push("compound_or_bundled_text");
|
|
269
|
+
if (
|
|
270
|
+
includesAny(text, [
|
|
271
|
+
/\b(?:should|must|best|ideal|recommended|recommendation|prefer|ought)\b/i,
|
|
272
|
+
])
|
|
273
|
+
)
|
|
274
|
+
issues.push("normative_language");
|
|
275
|
+
if (
|
|
276
|
+
includesAny(text, [
|
|
277
|
+
/\b(?:all|always|never|any|every|guarantees?|proves?|only)\b/i,
|
|
278
|
+
])
|
|
279
|
+
)
|
|
280
|
+
issues.push("overbroad_quantifier");
|
|
281
|
+
if (
|
|
282
|
+
includesAny(text, [
|
|
283
|
+
/\b(?:guidance|recommend(?:s|ed|ation)?|requires?|should|must|only|safe\s+only|intended\s+only)\b/i,
|
|
284
|
+
]) &&
|
|
285
|
+
includesAny(text, [/;/, /\b(?:and|or|plus|with|while|whereas|but)\b/i])
|
|
286
|
+
)
|
|
287
|
+
issues.push("multi_obligation_claim");
|
|
288
|
+
if (
|
|
289
|
+
looksQuantitative(text) &&
|
|
290
|
+
sourceRefs.length === 0 &&
|
|
291
|
+
sourceUrls.length === 0
|
|
292
|
+
)
|
|
293
|
+
issues.push("quantitative_without_visible_source");
|
|
294
|
+
if (looksRetrievalGapInference(text)) issues.push("retrieval_gap_inference");
|
|
295
|
+
if (looksDerivedRecommendation(text)) issues.push("derived_recommendation");
|
|
296
|
+
const mentionedEntities = entityMentions(text, [...slotMetaById.values()]);
|
|
297
|
+
if (
|
|
298
|
+
mentionedEntities.length > 1 &&
|
|
299
|
+
includesAny(text, [
|
|
300
|
+
/\b(?:better|cheaper|faster|slower|higher|lower|vs\.?|versus|than)\b/i,
|
|
301
|
+
])
|
|
302
|
+
)
|
|
303
|
+
issues.push("entity_blend_risk");
|
|
304
|
+
return [...new Set(issues)];
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
function precisionAction(issues, { sourceBacked } = {}) {
|
|
308
|
+
if (issues.includes("quantitative_without_visible_source"))
|
|
309
|
+
return "preserve_or_gap_until_source_backed";
|
|
310
|
+
if (issues.includes("retrieval_gap_inference"))
|
|
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";
|
|
316
|
+
if (
|
|
317
|
+
issues.includes("bundled_slots") ||
|
|
318
|
+
issues.includes("compound_or_bundled_text") ||
|
|
319
|
+
issues.includes("multi_obligation_claim") ||
|
|
320
|
+
issues.includes("entity_blend_risk")
|
|
321
|
+
)
|
|
322
|
+
return "split_or_narrow_before_verification";
|
|
323
|
+
if (
|
|
324
|
+
issues.includes("normative_language") ||
|
|
325
|
+
issues.includes("overbroad_quantifier")
|
|
326
|
+
)
|
|
327
|
+
return "narrow_or_demote";
|
|
328
|
+
return "eligible_if_slot_relevant";
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
function buildSlotPreservation({ planSlots, extractedFacts }) {
|
|
332
|
+
const factsBySlot = new Map();
|
|
333
|
+
for (const fact of extractedFacts) {
|
|
334
|
+
const slotId = stringOf(fact.slotId);
|
|
335
|
+
if (!slotId || slotId === "unslotted") continue;
|
|
336
|
+
const facts = factsBySlot.get(slotId) ?? [];
|
|
337
|
+
facts.push(fact);
|
|
338
|
+
factsBySlot.set(slotId, facts);
|
|
339
|
+
}
|
|
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
|
+
});
|
|
371
|
+
return {
|
|
372
|
+
requiredOrCriticalSlots,
|
|
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),
|
|
379
|
+
};
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
function buildPrecisionGuard({ claims, planSlots }) {
|
|
383
|
+
const slotMetaById = new Map(
|
|
384
|
+
planSlots
|
|
385
|
+
.map((slot) => [planSlotKey(slot), slot])
|
|
386
|
+
.filter(([slotId]) => slotId),
|
|
387
|
+
);
|
|
388
|
+
const guardedClaims = claims.map((claim) => {
|
|
389
|
+
const issues = precisionIssuesForClaim(claim, slotMetaById);
|
|
390
|
+
const sourceBacked =
|
|
391
|
+
compactStrings(claim.sourceRefs, 1).length > 0 ||
|
|
392
|
+
compactStrings(claim.sourceUrls, 1).length > 0;
|
|
393
|
+
return {
|
|
394
|
+
id: stringOf(claim.id) ?? stringOf(claim.originLocator),
|
|
395
|
+
factSlotIds: compactStrings(claim.factSlotIds, 12),
|
|
396
|
+
issues,
|
|
397
|
+
action: precisionAction(issues, { sourceBacked }),
|
|
398
|
+
sourceBacked,
|
|
399
|
+
claim: stringOf(claim.claim)?.slice(0, 220),
|
|
400
|
+
};
|
|
401
|
+
});
|
|
402
|
+
return {
|
|
403
|
+
schema: "deep-research-precision-guard-v1",
|
|
404
|
+
summary: {
|
|
405
|
+
totalClaims: guardedClaims.length,
|
|
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
|
+
),
|
|
414
|
+
},
|
|
415
|
+
claims: guardedClaims.filter((claim) => claim.issues.length > 0),
|
|
416
|
+
instructions: {
|
|
417
|
+
split:
|
|
418
|
+
"Claims flagged bundled_slots, compound_or_bundled_text, multi_obligation_claim, or entity_blend_risk should be split into atomic slot/entity-specific candidates before verification, while preserving source-backed measurement atoms needed for required slots.",
|
|
419
|
+
demote:
|
|
420
|
+
"Claims flagged normative_language or overbroad_quantifier should be narrowed to a source-backed factual statement or preserved as unverified context rather than promoted as a core recommendation.",
|
|
421
|
+
sourceGuard:
|
|
422
|
+
"Claims flagged quantitative_without_visible_source should not be promoted to core verification candidates until sourceUrls or sourceRefs are present; keep the slot as partial/missing or preserved evidence.",
|
|
423
|
+
retrievalGap:
|
|
424
|
+
"Claims flagged retrieval_gap_inference should be verification candidates only when framed as doc-scoped evidence about the exact retrieved sourceRefs; otherwise prefer a positive source-backed claim for the same slot or record a coverage gap.",
|
|
425
|
+
derivedRecommendation:
|
|
426
|
+
"Claims flagged derived_recommendation should split source-stated atoms into verificationCandidates and keep the product/design recommendation caveated in preservedClaims or final recommendations.",
|
|
427
|
+
},
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
export default async function normalizeInputPacket({ sources }) {
|
|
432
|
+
const plan = asObject(findSource(sources, "plan"));
|
|
433
|
+
const research = researchSources(sources);
|
|
434
|
+
const extractedFacts = [];
|
|
435
|
+
const claims = [];
|
|
436
|
+
const sourceCards = [];
|
|
437
|
+
const evidenceGaps = [];
|
|
438
|
+
const overflow = {};
|
|
439
|
+
const limits = {
|
|
440
|
+
extractedFacts: 240,
|
|
441
|
+
claims: 240,
|
|
442
|
+
sources: 160,
|
|
443
|
+
evidenceGaps: 120,
|
|
444
|
+
};
|
|
445
|
+
|
|
446
|
+
for (const { sourceId, source } of research) {
|
|
447
|
+
pushBounded(
|
|
448
|
+
extractedFacts,
|
|
449
|
+
overflow,
|
|
450
|
+
asArray(source.extractedFacts).map((fact, index) =>
|
|
451
|
+
compactExtractedFact(fact, sourceId, index),
|
|
452
|
+
),
|
|
453
|
+
limits.extractedFacts,
|
|
454
|
+
"omittedExtractedFacts",
|
|
455
|
+
);
|
|
456
|
+
pushBounded(
|
|
457
|
+
claims,
|
|
458
|
+
overflow,
|
|
459
|
+
asArray(source.claims).map((claim, index) =>
|
|
460
|
+
compactClaim(claim, sourceId, index),
|
|
461
|
+
),
|
|
462
|
+
limits.claims,
|
|
463
|
+
"omittedClaims",
|
|
464
|
+
);
|
|
465
|
+
pushBounded(
|
|
466
|
+
sourceCards,
|
|
467
|
+
overflow,
|
|
468
|
+
asArray(source.sources).map((item, index) =>
|
|
469
|
+
compactSource(item, sourceId, index),
|
|
470
|
+
),
|
|
471
|
+
limits.sources,
|
|
472
|
+
"omittedSources",
|
|
473
|
+
);
|
|
474
|
+
pushBounded(
|
|
475
|
+
evidenceGaps,
|
|
476
|
+
overflow,
|
|
477
|
+
asArray(source.additionalUnverifiedLeads).map((item, index) =>
|
|
478
|
+
compactGap(item, sourceId, index),
|
|
479
|
+
),
|
|
480
|
+
limits.evidenceGaps,
|
|
481
|
+
"omittedEvidenceGaps",
|
|
482
|
+
);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
const planSlots = asArray(plan.factSlots).map(compactPlanSlot);
|
|
486
|
+
const precisionGuard = buildPrecisionGuard({ claims, planSlots });
|
|
487
|
+
const slotPreservation = buildSlotPreservation({ planSlots, extractedFacts });
|
|
488
|
+
|
|
489
|
+
return {
|
|
490
|
+
schema: SCHEMA,
|
|
491
|
+
packet: {
|
|
492
|
+
plan: {
|
|
493
|
+
depth: stringOf(plan.depth),
|
|
494
|
+
taskType: stringOf(plan.taskType),
|
|
495
|
+
expectedFinalShape: stringOf(plan.expectedFinalShape),
|
|
496
|
+
sourcePolicy: plan.sourcePolicy,
|
|
497
|
+
factSlots: planSlots,
|
|
498
|
+
verificationPriorities: asArray(plan.verificationPriorities).map(
|
|
499
|
+
compactVerificationPriority,
|
|
500
|
+
),
|
|
501
|
+
researchScopeCoverage: asArray(plan.researchScopeCoverage),
|
|
502
|
+
},
|
|
503
|
+
research: {
|
|
504
|
+
sourceCount: research.length,
|
|
505
|
+
extractedFacts,
|
|
506
|
+
claims,
|
|
507
|
+
sources: sourceCards,
|
|
508
|
+
evidenceGaps,
|
|
509
|
+
},
|
|
510
|
+
slotPreservation,
|
|
511
|
+
precisionGuard,
|
|
512
|
+
ledgers: {
|
|
513
|
+
overflow,
|
|
514
|
+
overflowBySlot: overflowBySlot({
|
|
515
|
+
extractedFacts,
|
|
516
|
+
claims,
|
|
517
|
+
evidenceGaps,
|
|
518
|
+
}),
|
|
519
|
+
slotFactCounts: countBy(extractedFacts, (fact) => fact.slotId),
|
|
520
|
+
claimSlotCounts: countBy(
|
|
521
|
+
claims.flatMap((claim) =>
|
|
522
|
+
claim.factSlotIds.map((slotId) => ({ slotId })),
|
|
523
|
+
),
|
|
524
|
+
(item) => item.slotId,
|
|
525
|
+
),
|
|
526
|
+
sourceRefCoverage: {
|
|
527
|
+
extractedFactsWithSourceRefs: sourceRefCoverage(extractedFacts),
|
|
528
|
+
claimsWithSourceRefs: sourceRefCoverage(claims),
|
|
529
|
+
sourcesWithSourceRefs: sourceCards.filter(
|
|
530
|
+
(source) =>
|
|
531
|
+
typeof source.sourceRef === "string" && source.sourceRef,
|
|
532
|
+
).length,
|
|
533
|
+
},
|
|
534
|
+
},
|
|
535
|
+
instructions: {
|
|
536
|
+
selectionBoundary:
|
|
537
|
+
"Use this packet for mechanical lookup/coverage context only; semantic claim selection and verification priority remain the normalizer responsibility.",
|
|
538
|
+
noSilentLoss:
|
|
539
|
+
"If ledgers.overflow has non-zero counts, summarize omitted material in normalizationNotes instead of implying full coverage.",
|
|
540
|
+
precisionGuard:
|
|
541
|
+
"Use precisionGuard to split or demote broad, normative, bundled, source-weak, retrieval-gap, or derived-recommendation claims before verification; use slotPreservation so required/critical slot evidence is selected, preserved, or marked as an explicit gap.",
|
|
542
|
+
},
|
|
543
|
+
},
|
|
544
|
+
};
|
|
545
|
+
}
|