@tangle-network/agent-app 0.44.16 → 0.44.17

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.
@@ -17,6 +17,124 @@ import {
17
17
  defineAppTool
18
18
  } from "../chunk-YEFFHORB.js";
19
19
 
20
+ // src/work-product/claim-support.ts
21
+ var CURRENCY = /[$€£¥₹]/gu;
22
+ var NUMBER_IN_TEXT = /[$€£¥₹]?\s*\d{1,3}(?:,\d{3})+(?:\.\d+)?|[$€£¥₹]?\s*\d+(?:\.\d+)?/gu;
23
+ var FIGURE_IN_PROSE = /[$€£¥₹]\s*[-+]?\d[\d,]*(?:\.\d+)?|[-+]?\d{1,3}(?:,\d{3})+(?:\.\d+)?|[-+]?\d+\.\d{2}(?!\d)/gu;
24
+ var WHOLE_VALUE = /^[$€£¥₹]?\s*[-+]?\s*(?:\d{1,3}(?:,\d{3})+|\d+)(?:\.\d+)?\s*%?$/u;
25
+ function canonicalizeValue(token) {
26
+ let text = token.trim();
27
+ if (text.length === 0) return null;
28
+ if (/^\(.*\)$/u.test(text)) text = text.slice(1, -1).trim();
29
+ text = text.replace(CURRENCY, "").trim();
30
+ text = text.replace(/^[-+]\s*/u, "").trim();
31
+ text = text.replace(/%$/u, "").trim();
32
+ if (!/^(?:\d{1,3}(?:,\d{3})+|\d+)(?:\.\d+)?$/u.test(text)) return null;
33
+ text = text.replace(/,/gu, "");
34
+ if (text.includes(".")) text = text.replace(/0+$/u, "").replace(/\.$/u, "");
35
+ text = text.replace(/^0+(?=\d)/u, "");
36
+ return text.length === 0 ? null : text;
37
+ }
38
+ function valuesInText(text) {
39
+ const seen = /* @__PURE__ */ new Set();
40
+ for (const match of text.matchAll(NUMBER_IN_TEXT)) {
41
+ const canonical = canonicalizeValue(match[0]);
42
+ if (canonical !== null) seen.add(canonical);
43
+ }
44
+ return [...seen];
45
+ }
46
+ function claimValues(claim) {
47
+ const trimmed = claim.trim();
48
+ if (WHOLE_VALUE.test(trimmed)) {
49
+ const whole = canonicalizeValue(trimmed);
50
+ if (whole !== null) return [whole];
51
+ }
52
+ const seen = /* @__PURE__ */ new Set();
53
+ for (const match of trimmed.matchAll(FIGURE_IN_PROSE)) {
54
+ const canonical = canonicalizeValue(match[0]);
55
+ if (canonical !== null) seen.add(canonical);
56
+ }
57
+ return [...seen];
58
+ }
59
+ function verifyClaimSupport(quote, claim) {
60
+ if (quote.trim().length === 0) return { status: "not_applicable" };
61
+ const claimed = claimValues(claim);
62
+ if (claimed.length === 0) return { status: "not_applicable" };
63
+ const present = valuesInText(quote);
64
+ const matched = claimed.find((value) => present.includes(value));
65
+ if (matched !== void 0) return { status: "supported", matched };
66
+ return { status: "unsupported", claimed, present };
67
+ }
68
+ function excerpt(quote, limit = 120) {
69
+ const flat = quote.replace(/\s+/gu, " ").trim();
70
+ return flat.length <= limit ? flat : `${flat.slice(0, limit)}\u2026`;
71
+ }
72
+ function claimSupportErrorDetail(failure, quote) {
73
+ const wanted = failure.claimed.length === 1 ? failure.claimed[0] : `any of ${failure.claimed.join(", ")}`;
74
+ const carries = failure.present.length === 0 ? "that line carries no figure at all" : `the only figures on it are ${failure.present.join(", ")}`;
75
+ return `the cited text does not contain ${wanted}. It reads "${excerpt(quote)}", and ${carries}. Cite locator.find with the value exactly as it appears in the document and the platform will locate the right line for you. If this figure was COMPUTED rather than read from the document, omit the locator entirely and state the computation in claim.`;
76
+ }
77
+ function foldLabel(value) {
78
+ return value.normalize("NFKC").toLowerCase().replace(/[^\p{L}\p{N}]+/gu, " ").trim();
79
+ }
80
+ function verifyTargetLabel(quote, target, groups) {
81
+ if (quote.trim().length === 0) return { status: "not_applicable" };
82
+ const group = groups.find((candidate) => Object.hasOwn(candidate.labels, target));
83
+ if (!group) return { status: "not_applicable" };
84
+ const line = ` ${foldLabel(quote)} `;
85
+ const own = group.labels[target] ?? [];
86
+ for (const label of own) {
87
+ const folded = foldLabel(label);
88
+ if (folded.length > 0 && line.includes(folded)) return { status: "identified", label };
89
+ }
90
+ for (const [rival, labels] of Object.entries(group.labels)) {
91
+ if (rival === target) continue;
92
+ for (const label of labels) {
93
+ const folded = foldLabel(label);
94
+ if (folded.length === 0 || !line.includes(folded)) continue;
95
+ return {
96
+ status: "crossed",
97
+ rival,
98
+ rivalLabel: label,
99
+ expected: own,
100
+ ...group.note === void 0 ? {} : { note: group.note }
101
+ };
102
+ }
103
+ }
104
+ return { status: "not_applicable" };
105
+ }
106
+ function targetLabelErrorDetail(failure, target, quote) {
107
+ const ownLooks = failure.expected.length === 0 ? "" : ` A citation for ${target} should land on the line naming ${failure.expected.map((label) => JSON.stringify(label)).join(" or ")}.`;
108
+ const note = failure.note === void 0 ? "" : ` (${failure.note})`;
109
+ return `the line it cites reads "${excerpt(quote)}", which is the line for ${failure.rival} \u2014 it names ${JSON.stringify(failure.rivalLabel)}${note}.${ownLooks} Cite the line that belongs to this target, or attach this citation to ${failure.rival} instead. The figure is real; it is on the wrong line.`;
110
+ }
111
+ function indexArtifactValues(fields, normalizeTarget) {
112
+ const index = /* @__PURE__ */ new Map();
113
+ for (const [key, raw] of Object.entries(fields ?? {})) {
114
+ const value = typeof raw === "number" && Number.isFinite(raw) ? canonicalizeValue(String(raw)) : typeof raw === "string" ? canonicalizeValue(raw) : null;
115
+ if (value === null) continue;
116
+ index.set(normalizeTarget ? normalizeTarget(key) : key, value);
117
+ }
118
+ return index;
119
+ }
120
+ function verifyArtifactAgreement(target, claim, fieldValues) {
121
+ const expected = fieldValues.get(target);
122
+ if (expected === void 0) return { status: "not_applicable" };
123
+ const claimed = claimValues(claim);
124
+ if (claimed.length === 0) return { status: "not_applicable" };
125
+ if (claimed.includes(expected)) return { status: "agrees", value: expected };
126
+ for (const value of claimed) {
127
+ for (const [other, otherValue] of fieldValues) {
128
+ if (other === target || otherValue !== value) continue;
129
+ return { status: "contradicts", claimed: value, expected, belongsTo: other };
130
+ }
131
+ }
132
+ return { status: "not_applicable" };
133
+ }
134
+ function artifactAgreementErrorDetail(failure, target) {
135
+ return `the artifact reports ${failure.expected} on ${target} and ${failure.claimed} on ${failure.belongsTo}, so a citation claiming ${failure.claimed} does not support ${target} \u2014 it supports ${failure.belongsTo}. Attach this citation to ${failure.belongsTo}, or correct the artifact if ${target} really is ${failure.claimed}. The package cannot state both.`;
136
+ }
137
+
20
138
  // src/work-product/service.ts
21
139
  var WORK_PRODUCT_TRANSITIONS = {
22
140
  draft: /* @__PURE__ */ new Set(["blocked", "ready"]),
@@ -52,6 +170,29 @@ function mergeById(existing, incoming) {
52
170
  }
53
171
  return merged;
54
172
  }
173
+ function evidenceIdentity(entry) {
174
+ const whole = canonicalizeValue(entry.claim);
175
+ const claimKey = whole ?? entry.claim.normalize("NFKC").toLowerCase().replace(/\s+/gu, " ").trim();
176
+ return `${entry.target}\0${entry.sourceRef}\0${claimKey}`;
177
+ }
178
+ function mergeEvidence(existing, incoming) {
179
+ const merged = existing.slice();
180
+ for (const entry of incoming) {
181
+ const identity = evidenceIdentity(entry);
182
+ const hits = [];
183
+ for (let index = 0; index < merged.length; index += 1) {
184
+ const candidate = merged[index];
185
+ if (candidate.id === entry.id || evidenceIdentity(candidate) === identity) hits.push(index);
186
+ }
187
+ if (hits.length === 0) {
188
+ merged.push(entry);
189
+ continue;
190
+ }
191
+ merged[hits[0]] = entry;
192
+ for (const index of hits.slice(1).reverse()) merged.splice(index, 1);
193
+ }
194
+ return merged;
195
+ }
55
196
  function createWorkProductService(options) {
56
197
  const { store } = options;
57
198
  const now = options.now ?? (() => Date.now());
@@ -158,7 +299,7 @@ function createWorkProductService(options) {
158
299
  const upsertEvidence = (id, entries) => guardedMerge(
159
300
  id,
160
301
  ["draft", "blocked"],
161
- (record) => ({ evidence: mergeById(record.evidence, entries) }),
302
+ (record) => ({ evidence: mergeEvidence(record.evidence, entries) }),
162
303
  {
163
304
  step: "wp.evidence",
164
305
  message: (record) => `Evidence upserted (${entries.length} entries, ${record.evidence.length} total)`,
@@ -387,64 +528,6 @@ function workProductTrustInputs(records, verdictsFor) {
387
528
  return items;
388
529
  }
389
530
 
390
- // src/work-product/claim-support.ts
391
- var CURRENCY = /[$€£¥₹]/gu;
392
- var NUMBER_IN_TEXT = /[$€£¥₹]?\s*\d{1,3}(?:,\d{3})+(?:\.\d+)?|[$€£¥₹]?\s*\d+(?:\.\d+)?/gu;
393
- var FIGURE_IN_PROSE = /[$€£¥₹]\s*[-+]?\d[\d,]*(?:\.\d+)?|[-+]?\d{1,3}(?:,\d{3})+(?:\.\d+)?|[-+]?\d+\.\d{2}(?!\d)/gu;
394
- var WHOLE_VALUE = /^[$€£¥₹]?\s*[-+]?\s*(?:\d{1,3}(?:,\d{3})+|\d+)(?:\.\d+)?\s*%?$/u;
395
- function canonicalizeValue(token) {
396
- let text = token.trim();
397
- if (text.length === 0) return null;
398
- if (/^\(.*\)$/u.test(text)) text = text.slice(1, -1).trim();
399
- text = text.replace(CURRENCY, "").trim();
400
- text = text.replace(/^[-+]\s*/u, "").trim();
401
- text = text.replace(/%$/u, "").trim();
402
- if (!/^(?:\d{1,3}(?:,\d{3})+|\d+)(?:\.\d+)?$/u.test(text)) return null;
403
- text = text.replace(/,/gu, "");
404
- if (text.includes(".")) text = text.replace(/0+$/u, "").replace(/\.$/u, "");
405
- text = text.replace(/^0+(?=\d)/u, "");
406
- return text.length === 0 ? null : text;
407
- }
408
- function valuesInText(text) {
409
- const seen = /* @__PURE__ */ new Set();
410
- for (const match of text.matchAll(NUMBER_IN_TEXT)) {
411
- const canonical = canonicalizeValue(match[0]);
412
- if (canonical !== null) seen.add(canonical);
413
- }
414
- return [...seen];
415
- }
416
- function claimValues(claim) {
417
- const trimmed = claim.trim();
418
- if (WHOLE_VALUE.test(trimmed)) {
419
- const whole = canonicalizeValue(trimmed);
420
- if (whole !== null) return [whole];
421
- }
422
- const seen = /* @__PURE__ */ new Set();
423
- for (const match of trimmed.matchAll(FIGURE_IN_PROSE)) {
424
- const canonical = canonicalizeValue(match[0]);
425
- if (canonical !== null) seen.add(canonical);
426
- }
427
- return [...seen];
428
- }
429
- function verifyClaimSupport(quote, claim) {
430
- if (quote.trim().length === 0) return { status: "not_applicable" };
431
- const claimed = claimValues(claim);
432
- if (claimed.length === 0) return { status: "not_applicable" };
433
- const present = valuesInText(quote);
434
- const matched = claimed.find((value) => present.includes(value));
435
- if (matched !== void 0) return { status: "supported", matched };
436
- return { status: "unsupported", claimed, present };
437
- }
438
- function excerpt(quote, limit = 120) {
439
- const flat = quote.replace(/\s+/gu, " ").trim();
440
- return flat.length <= limit ? flat : `${flat.slice(0, limit)}\u2026`;
441
- }
442
- function claimSupportErrorDetail(failure, quote) {
443
- const wanted = failure.claimed.length === 1 ? failure.claimed[0] : `any of ${failure.claimed.join(", ")}`;
444
- const carries = failure.present.length === 0 ? "that line carries no figure at all" : `the only figures on it are ${failure.present.join(", ")}`;
445
- return `the cited text does not contain ${wanted}. It reads "${excerpt(quote)}", and ${carries}. Cite locator.find with the value exactly as it appears in the document and the platform will locate the right line for you. If this figure was COMPUTED rather than read from the document, omit the locator entirely and state the computation in claim.`;
446
- }
447
-
448
531
  // src/work-product/quote.ts
449
532
  var WHITESPACE = /[\s\p{Zs}\u2028\u2029\u200b-\u200d\ufeff]+/gu;
450
533
  var DASHES = /[\u2010-\u2015\u2212\ufe58\ufe63\uff0d]/gu;
@@ -537,6 +620,8 @@ var MAX_WORK_PRODUCT_BATCH = 50;
537
620
  var EVIDENCE_COVERAGE_CHECK = "evidence_coverage";
538
621
  var QUOTE_VERIFICATION_CHECK = "quote_verification";
539
622
  var CLAIM_SUPPORT_CHECK = "claim_support";
623
+ var TARGET_CORRECTNESS_CHECK = "target_correctness";
624
+ var ARTIFACT_AGREEMENT_CHECK = "artifact_agreement";
540
625
  async function unwrap(run, code) {
541
626
  let outcome = await run();
542
627
  if (!outcome.succeeded && outcome.conflict) outcome = await run();
@@ -704,6 +789,35 @@ function assertClaimsSupported(config, entries) {
704
789
  );
705
790
  }
706
791
  }
792
+ function assertTargetsNotCrossed(config, entries) {
793
+ const groups = config.confusableTargets;
794
+ if (!groups || groups.length === 0) return;
795
+ for (let index = 0; index < entries.length; index += 1) {
796
+ const entry = entries[index];
797
+ const quote = entry.locator.quote;
798
+ if (quote === void 0) continue;
799
+ const verdict = verifyTargetLabel(quote, entry.target, groups);
800
+ if (verdict.status !== "crossed") continue;
801
+ throw new ToolInputError(
802
+ "target_crossed",
803
+ `entries[${index}] is attached to ${entry.target} but ${targetLabelErrorDetail(verdict, entry.target, quote)}`
804
+ );
805
+ }
806
+ }
807
+ function assertEvidenceAgreesWithArtifact(config, entries, artifact) {
808
+ if (config.verifyArtifactAgreement === false) return;
809
+ const fieldValues = indexArtifactValues(artifact?.fields, config.normalizeTarget);
810
+ if (fieldValues.size === 0) return;
811
+ for (let index = 0; index < entries.length; index += 1) {
812
+ const entry = entries[index];
813
+ const agreement = verifyArtifactAgreement(entry.target, entry.claim, fieldValues);
814
+ if (agreement.status !== "contradicts") continue;
815
+ throw new ToolInputError(
816
+ "contradicts_artifact",
817
+ `entries[${index}].claim ${JSON.stringify(entry.claim)} contradicts this work product's own artifact: ${artifactAgreementErrorDetail(agreement, entry.target)}`
818
+ );
819
+ }
820
+ }
707
821
  async function summarizeQuoteVerification(config, evidence, ctx) {
708
822
  const readSourceText = config.readSourceText;
709
823
  if (!readSourceText) return void 0;
@@ -753,6 +867,41 @@ function summarizeClaimSupport(evidence) {
753
867
  }
754
868
  return { supported, checkable, unsupported };
755
869
  }
870
+ function summarizeTargetCorrectness(evidence, groups, targetOf) {
871
+ let correct = 0;
872
+ let checkable = 0;
873
+ const crossed = [];
874
+ for (const entry of evidence) {
875
+ const quote = entry.locator.quote;
876
+ if (quote === void 0) continue;
877
+ const target = targetOf(entry);
878
+ const verdict = verifyTargetLabel(quote, target, groups);
879
+ if (verdict.status === "not_applicable") continue;
880
+ checkable += 1;
881
+ if (verdict.status === "identified") correct += 1;
882
+ else crossed.push({ id: entry.id, detail: `${entry.id} (${target} cites the ${verdict.rival} line)` });
883
+ }
884
+ return { correct, checkable, crossed };
885
+ }
886
+ function summarizeArtifactAgreement(evidence, fieldValues, targetOf) {
887
+ let agreeing = 0;
888
+ let checkable = 0;
889
+ const contradicting = [];
890
+ for (const entry of evidence) {
891
+ const target = targetOf(entry);
892
+ const agreement = verifyArtifactAgreement(target, entry.claim, fieldValues);
893
+ if (agreement.status === "not_applicable") continue;
894
+ checkable += 1;
895
+ if (agreement.status === "agrees") agreeing += 1;
896
+ else {
897
+ contradicting.push({
898
+ id: entry.id,
899
+ detail: `${entry.id} (${target} claims ${agreement.claimed}, which the artifact reports on ${agreement.belongsTo}; ${target} is ${agreement.expected})`
900
+ });
901
+ }
902
+ }
903
+ return { agreeing, checkable, contradicting };
904
+ }
756
905
  function buildWorkProductTools(config) {
757
906
  const service = createWorkProductService({
758
907
  store: config.store,
@@ -821,6 +970,7 @@ function buildWorkProductTools(config) {
821
970
  for (let index = 0; index < raw.length; index += 1) {
822
971
  const parsed = parseEvidenceInput(raw[index], `entries[${index}]`);
823
972
  if (!parsed.ok) throw new ToolInputError("invalid_evidence", `${parsed.field}: ${parsed.error}`);
973
+ if (config.normalizeTarget) parsed.value.target = config.normalizeTarget(parsed.value.target);
824
974
  entries.push(parsed.value);
825
975
  }
826
976
  for (let index = 0; index < entries.length; index += 1) {
@@ -834,7 +984,9 @@ function buildWorkProductTools(config) {
834
984
  }
835
985
  await resolveEvidenceQuotes(config, entries, ctx);
836
986
  assertClaimsSupported(config, entries);
987
+ assertTargetsNotCrossed(config, entries);
837
988
  const draft = await resolveDraft(service, config, scopeKey, ctx);
989
+ assertEvidenceAgreesWithArtifact(config, entries, draft.artifact);
838
990
  const record = await unwrap(() => service.upsertEvidence(draft.id, entries), "evidence_rejected");
839
991
  return {
840
992
  workProductId: record.id,
@@ -1015,15 +1167,55 @@ function buildWorkProductTools(config) {
1015
1167
  );
1016
1168
  }
1017
1169
  }
1170
+ const targetOf = (entry) => config.normalizeTarget ? config.normalizeTarget(entry.target) : entry.target;
1171
+ if (config.confusableTargets && config.confusableTargets.length > 0) {
1172
+ const crossing = summarizeTargetCorrectness(draft.evidence, config.confusableTargets, targetOf);
1173
+ checks.unshift({
1174
+ id: TARGET_CORRECTNESS_CHECK,
1175
+ name: TARGET_CORRECTNESS_CHECK,
1176
+ passed: crossing.crossed.length === 0,
1177
+ detail: crossing.crossed.length > 0 ? `Citations attached to the wrong target: ${crossing.crossed.map((item) => item.detail).join("; ")}` : crossing.checkable === 0 ? "No citation lands on a line this product can tell apart from a sibling target \u2014 nothing to check" : `${crossing.correct}/${crossing.checkable} citations land on a line belonging to their own target`,
1178
+ source: "platform"
1179
+ });
1180
+ if (crossing.crossed.length > 0) {
1181
+ await unwrap(() => service.recordChecks(draft.id, checks), "checks_rejected");
1182
+ throw new ToolInputError(
1183
+ "target_crossed",
1184
+ `Cannot submit: ${crossing.crossed.length} evidence entr${crossing.crossed.length === 1 ? "y cites" : "ies cite"} a line belonging to a different target \u2014 ${crossing.crossed.map((item) => item.detail).join("; ")}. Re-emit each against the target whose line it actually cites, or cite the line that belongs to the target it is attached to.`
1185
+ );
1186
+ }
1187
+ }
1188
+ const artifactValues = indexArtifactValues(artifact.fields, config.normalizeTarget);
1189
+ if (config.verifyArtifactAgreement !== false && artifactValues.size > 0) {
1190
+ const agreement = summarizeArtifactAgreement(draft.evidence, artifactValues, targetOf);
1191
+ checks.unshift({
1192
+ id: ARTIFACT_AGREEMENT_CHECK,
1193
+ name: ARTIFACT_AGREEMENT_CHECK,
1194
+ passed: agreement.contradicting.length === 0,
1195
+ detail: agreement.contradicting.length > 0 ? `Evidence contradicts the artifact on: ${agreement.contradicting.map((item) => item.detail).join("; ")}` : agreement.checkable === 0 ? "No evidence claim states a figure the artifact also states \u2014 nothing to check" : `${agreement.agreeing}/${agreement.checkable} evidence claims agree with the artifact field they support`,
1196
+ source: "platform"
1197
+ });
1198
+ if (agreement.contradicting.length > 0) {
1199
+ await unwrap(() => service.recordChecks(draft.id, checks), "checks_rejected");
1200
+ throw new ToolInputError(
1201
+ "contradicts_artifact",
1202
+ `Cannot submit: ${agreement.contradicting.length} evidence entr${agreement.contradicting.length === 1 ? "y contradicts" : "ies contradict"} the artifact they support \u2014 ${agreement.contradicting.map((item) => item.detail).join("; ")}. Move each citation to the target it actually supports, or correct the artifact. The package cannot state both.`
1203
+ );
1204
+ }
1205
+ }
1018
1206
  if (config.materialTargets) {
1019
- const targets = config.materialTargets(artifact);
1020
- const covered = new Set(draft.evidence.map((entry) => entry.target));
1207
+ const targets = [
1208
+ ...new Set(
1209
+ config.materialTargets(artifact).map((target) => config.normalizeTarget ? config.normalizeTarget(target) : target)
1210
+ )
1211
+ ];
1212
+ const covered = new Set(draft.evidence.map(targetOf));
1021
1213
  const missing = targets.filter((target) => !covered.has(target));
1022
1214
  const anchoredTargets = new Set(
1023
- draft.evidence.filter((entry) => entry.locator.quoteBasis !== void 0).map((entry) => entry.target)
1215
+ draft.evidence.filter((entry) => entry.locator.quoteBasis !== void 0).map(targetOf)
1024
1216
  );
1025
1217
  const spanTargets = new Set(
1026
- draft.evidence.filter((entry) => entry.locator.quoteBasis === "span").map((entry) => entry.target)
1218
+ draft.evidence.filter((entry) => entry.locator.quoteBasis === "span").map(targetOf)
1027
1219
  );
1028
1220
  const present = targets.filter((target) => covered.has(target));
1029
1221
  const unanchored = present.filter((target) => !anchoredTargets.has(target));
@@ -1161,10 +1353,13 @@ function createWorkProductRoutes(options) {
1161
1353
  return { list, detail, verdict };
1162
1354
  }
1163
1355
  export {
1356
+ ARTIFACT_AGREEMENT_CHECK,
1164
1357
  CLAIM_SUPPORT_CHECK,
1165
1358
  EVIDENCE_COVERAGE_CHECK,
1166
1359
  MAX_WORK_PRODUCT_BATCH,
1167
1360
  QUOTE_VERIFICATION_CHECK,
1361
+ TARGET_CORRECTNESS_CHECK,
1362
+ artifactAgreementErrorDetail,
1168
1363
  buildWorkProductTools,
1169
1364
  canTransitionWorkProduct,
1170
1365
  canonicalizeValue,
@@ -1175,6 +1370,7 @@ export {
1175
1370
  createWorkProductService,
1176
1371
  finalizeWorkProductProvenance,
1177
1372
  findSourceLine,
1373
+ indexArtifactValues,
1178
1374
  isWorkProductStatus,
1179
1375
  isWorkProductTerminal,
1180
1376
  normalizeQuoteText,
@@ -1188,10 +1384,13 @@ export {
1188
1384
  sliceSourceSpan,
1189
1385
  sourceContainsQuote,
1190
1386
  stampProvenance,
1387
+ targetLabelErrorDetail,
1191
1388
  unresolvedBlockingExceptions,
1192
1389
  validateWorkProductVerdictBody,
1193
1390
  valuesInText,
1391
+ verifyArtifactAgreement,
1194
1392
  verifyClaimSupport,
1393
+ verifyTargetLabel,
1195
1394
  workProductToPersistedPart,
1196
1395
  workProductTrustInputs
1197
1396
  };