@claritylabs/cl-sdk 1.3.8 → 2.0.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/dist/index.js CHANGED
@@ -103,6 +103,11 @@ __export(index_exports, {
103
103
  DeductibleTypeSchema: () => DeductibleTypeSchema,
104
104
  DefenseCostTreatmentSchema: () => DefenseCostTreatmentSchema,
105
105
  DefinitionSchema: () => DefinitionSchema,
106
+ DocumentAgentGuidanceSchema: () => DocumentAgentGuidanceSchema,
107
+ DocumentMetadataSchema: () => DocumentMetadataSchema,
108
+ DocumentNodeSchema: () => DocumentNodeSchema,
109
+ DocumentPageMapEntrySchema: () => DocumentPageMapEntrySchema,
110
+ DocumentTableOfContentsEntrySchema: () => DocumentTableOfContentsEntrySchema,
106
111
  DocumentTypeSchema: () => DocumentTypeSchema,
107
112
  DriverRecordSchema: () => DriverRecordSchema,
108
113
  DwellingDetailsSchema: () => DwellingDetailsSchema,
@@ -905,14 +910,20 @@ var FormReferenceSchema = import_zod3.z.object({
905
910
  title: import_zod3.z.string().optional(),
906
911
  formType: import_zod3.z.enum(["coverage", "endorsement", "declarations", "application", "notice", "other"]),
907
912
  pageStart: import_zod3.z.number().optional(),
908
- pageEnd: import_zod3.z.number().optional()
913
+ pageEnd: import_zod3.z.number().optional(),
914
+ documentNodeId: import_zod3.z.string().optional(),
915
+ sourceSpanIds: import_zod3.z.array(import_zod3.z.string()).optional(),
916
+ sourceTextHash: import_zod3.z.string().optional()
909
917
  });
910
918
  var TaxFeeItemSchema = import_zod3.z.object({
911
919
  name: import_zod3.z.string(),
912
920
  amount: import_zod3.z.string(),
913
921
  amountValue: import_zod3.z.number().optional(),
914
922
  type: import_zod3.z.enum(["tax", "fee", "surcharge", "assessment"]).optional(),
915
- description: import_zod3.z.string().optional()
923
+ description: import_zod3.z.string().optional(),
924
+ documentNodeId: import_zod3.z.string().optional(),
925
+ sourceSpanIds: import_zod3.z.array(import_zod3.z.string()).optional(),
926
+ sourceTextHash: import_zod3.z.string().optional()
916
927
  });
917
928
  var RatingBasisSchema = import_zod3.z.object({
918
929
  type: RatingBasisTypeSchema,
@@ -968,6 +979,7 @@ var CoverageSchema = import_zod4.z.object({
968
979
  sectionRef: import_zod4.z.string().optional(),
969
980
  originalContent: import_zod4.z.string().optional(),
970
981
  recordId: import_zod4.z.string().optional(),
982
+ documentNodeId: import_zod4.z.string().optional(),
971
983
  sourceSpanIds: import_zod4.z.array(import_zod4.z.string()).optional(),
972
984
  sourceTextHash: import_zod4.z.string().optional()
973
985
  });
@@ -997,6 +1009,7 @@ var EnrichedCoverageSchema = import_zod4.z.object({
997
1009
  sectionRef: import_zod4.z.string().optional(),
998
1010
  originalContent: import_zod4.z.string().optional(),
999
1011
  recordId: import_zod4.z.string().optional(),
1012
+ documentNodeId: import_zod4.z.string().optional(),
1000
1013
  sourceSpanIds: import_zod4.z.array(import_zod4.z.string()).optional(),
1001
1014
  sourceTextHash: import_zod4.z.string().optional()
1002
1015
  });
@@ -1025,6 +1038,7 @@ var EndorsementSchema = import_zod5.z.object({
1025
1038
  pageStart: import_zod5.z.number(),
1026
1039
  pageEnd: import_zod5.z.number().optional(),
1027
1040
  recordId: import_zod5.z.string().optional(),
1041
+ documentNodeId: import_zod5.z.string().optional(),
1028
1042
  sourceSpanIds: import_zod5.z.array(import_zod5.z.string()).optional(),
1029
1043
  sourceTextHash: import_zod5.z.string().optional()
1030
1044
  });
@@ -1043,6 +1057,7 @@ var ExclusionSchema = import_zod6.z.object({
1043
1057
  content: import_zod6.z.string(),
1044
1058
  pageNumber: import_zod6.z.number().optional(),
1045
1059
  recordId: import_zod6.z.string().optional(),
1060
+ documentNodeId: import_zod6.z.string().optional(),
1046
1061
  sourceSpanIds: import_zod6.z.array(import_zod6.z.string()).optional(),
1047
1062
  sourceTextHash: import_zod6.z.string().optional()
1048
1063
  });
@@ -1060,6 +1075,7 @@ var PolicyConditionSchema = import_zod7.z.object({
1060
1075
  keyValues: import_zod7.z.array(ConditionKeyValueSchema).optional(),
1061
1076
  pageNumber: import_zod7.z.number().optional(),
1062
1077
  recordId: import_zod7.z.string().optional(),
1078
+ documentNodeId: import_zod7.z.string().optional(),
1063
1079
  sourceSpanIds: import_zod7.z.array(import_zod7.z.string()).optional(),
1064
1080
  sourceTextHash: import_zod7.z.string().optional()
1065
1081
  });
@@ -1636,6 +1652,7 @@ var SubsectionSchema = import_zod16.z.object({
1636
1652
  pageNumber: import_zod16.z.number().optional(),
1637
1653
  excerpt: import_zod16.z.string().optional(),
1638
1654
  content: import_zod16.z.string().optional(),
1655
+ documentNodeId: import_zod16.z.string().optional(),
1639
1656
  sourceSpanIds: import_zod16.z.array(import_zod16.z.string()).optional(),
1640
1657
  sourceTextHash: import_zod16.z.string().optional()
1641
1658
  });
@@ -1650,6 +1667,7 @@ var SectionSchema = import_zod16.z.object({
1650
1667
  content: import_zod16.z.string().optional(),
1651
1668
  subsections: import_zod16.z.array(SubsectionSchema).optional(),
1652
1669
  recordId: import_zod16.z.string().optional(),
1670
+ documentNodeId: import_zod16.z.string().optional(),
1653
1671
  sourceSpanIds: import_zod16.z.array(import_zod16.z.string()).optional(),
1654
1672
  sourceTextHash: import_zod16.z.string().optional()
1655
1673
  });
@@ -1663,13 +1681,19 @@ var UnderwritingConditionSchema = import_zod16.z.object({
1663
1681
  var PremiumLineSchema = import_zod16.z.object({
1664
1682
  line: import_zod16.z.string(),
1665
1683
  amount: import_zod16.z.string(),
1666
- amountValue: import_zod16.z.number().optional()
1684
+ amountValue: import_zod16.z.number().optional(),
1685
+ documentNodeId: import_zod16.z.string().optional(),
1686
+ sourceSpanIds: import_zod16.z.array(import_zod16.z.string()).optional(),
1687
+ sourceTextHash: import_zod16.z.string().optional()
1667
1688
  });
1668
1689
  var AuxiliaryFactSchema = import_zod16.z.object({
1669
1690
  key: import_zod16.z.string(),
1670
1691
  value: import_zod16.z.string(),
1671
1692
  subject: import_zod16.z.string().optional(),
1672
- context: import_zod16.z.string().optional()
1693
+ context: import_zod16.z.string().optional(),
1694
+ documentNodeId: import_zod16.z.string().optional(),
1695
+ sourceSpanIds: import_zod16.z.array(import_zod16.z.string()).optional(),
1696
+ sourceTextHash: import_zod16.z.string().optional()
1673
1697
  });
1674
1698
  var DefinitionSchema = import_zod16.z.object({
1675
1699
  term: import_zod16.z.string(),
@@ -1680,6 +1704,7 @@ var DefinitionSchema = import_zod16.z.object({
1680
1704
  sectionRef: import_zod16.z.string().optional(),
1681
1705
  originalContent: import_zod16.z.string().optional(),
1682
1706
  recordId: import_zod16.z.string().optional(),
1707
+ documentNodeId: import_zod16.z.string().optional(),
1683
1708
  sourceSpanIds: import_zod16.z.array(import_zod16.z.string()).optional(),
1684
1709
  sourceTextHash: import_zod16.z.string().optional()
1685
1710
  });
@@ -1697,9 +1722,60 @@ var CoveredReasonSchema = import_zod16.z.object({
1697
1722
  sectionRef: import_zod16.z.string().optional(),
1698
1723
  originalContent: import_zod16.z.string().optional(),
1699
1724
  recordId: import_zod16.z.string().optional(),
1725
+ documentNodeId: import_zod16.z.string().optional(),
1700
1726
  sourceSpanIds: import_zod16.z.array(import_zod16.z.string()).optional(),
1701
1727
  sourceTextHash: import_zod16.z.string().optional()
1702
1728
  });
1729
+ var DocumentTableOfContentsEntrySchema = import_zod16.z.object({
1730
+ title: import_zod16.z.string(),
1731
+ level: import_zod16.z.number().int().positive().optional(),
1732
+ pageStart: import_zod16.z.number().optional(),
1733
+ pageEnd: import_zod16.z.number().optional(),
1734
+ documentNodeId: import_zod16.z.string().optional(),
1735
+ sourceSpanIds: import_zod16.z.array(import_zod16.z.string()).optional()
1736
+ });
1737
+ var DocumentPageMapEntrySchema = import_zod16.z.object({
1738
+ page: import_zod16.z.number().int().positive(),
1739
+ label: import_zod16.z.string().optional(),
1740
+ formNumber: import_zod16.z.string().optional(),
1741
+ formTitle: import_zod16.z.string().optional(),
1742
+ sectionTitle: import_zod16.z.string().optional(),
1743
+ extractorNames: import_zod16.z.array(import_zod16.z.string()).optional(),
1744
+ sourceSpanIds: import_zod16.z.array(import_zod16.z.string()).optional()
1745
+ });
1746
+ var DocumentAgentGuidanceSchema = import_zod16.z.object({
1747
+ kind: import_zod16.z.string(),
1748
+ title: import_zod16.z.string(),
1749
+ detail: import_zod16.z.string(),
1750
+ sourceSpanIds: import_zod16.z.array(import_zod16.z.string()).optional()
1751
+ });
1752
+ var DocumentMetadataSchema = import_zod16.z.object({
1753
+ formInventory: import_zod16.z.array(FormReferenceSchema).optional(),
1754
+ tableOfContents: import_zod16.z.array(DocumentTableOfContentsEntrySchema).optional(),
1755
+ pageMap: import_zod16.z.array(DocumentPageMapEntrySchema).optional(),
1756
+ agentGuidance: import_zod16.z.array(DocumentAgentGuidanceSchema).optional()
1757
+ });
1758
+ var DocumentNodeSchema = import_zod16.z.lazy(
1759
+ () => import_zod16.z.object({
1760
+ id: import_zod16.z.string(),
1761
+ title: import_zod16.z.string(),
1762
+ originalTitle: import_zod16.z.string().optional(),
1763
+ type: import_zod16.z.string().optional(),
1764
+ label: import_zod16.z.string().optional(),
1765
+ level: import_zod16.z.number().int().positive().optional(),
1766
+ sectionNumber: import_zod16.z.string().optional(),
1767
+ pageStart: import_zod16.z.number().optional(),
1768
+ pageEnd: import_zod16.z.number().optional(),
1769
+ formNumber: import_zod16.z.string().optional(),
1770
+ formTitle: import_zod16.z.string().optional(),
1771
+ excerpt: import_zod16.z.string().optional(),
1772
+ content: import_zod16.z.string().optional(),
1773
+ interpretationLabels: import_zod16.z.array(import_zod16.z.string()).optional(),
1774
+ sourceSpanIds: import_zod16.z.array(import_zod16.z.string()).optional(),
1775
+ sourceTextHash: import_zod16.z.string().optional(),
1776
+ children: import_zod16.z.array(DocumentNodeSchema).optional()
1777
+ })
1778
+ );
1703
1779
  var BaseDocumentFields = {
1704
1780
  id: import_zod16.z.string(),
1705
1781
  carrier: import_zod16.z.string(),
@@ -1710,6 +1786,8 @@ var BaseDocumentFields = {
1710
1786
  summary: import_zod16.z.string().optional(),
1711
1787
  policyTypes: import_zod16.z.array(import_zod16.z.string()).optional(),
1712
1788
  coverages: import_zod16.z.array(CoverageSchema),
1789
+ documentMetadata: DocumentMetadataSchema,
1790
+ documentOutline: import_zod16.z.array(DocumentNodeSchema),
1713
1791
  sections: import_zod16.z.array(SectionSchema).optional(),
1714
1792
  definitions: import_zod16.z.array(DefinitionSchema).optional(),
1715
1793
  coveredReasons: import_zod16.z.array(CoveredReasonSchema).optional(),
@@ -3609,6 +3687,8 @@ function assembleDocument(documentId, documentType, memory) {
3609
3687
  carrier: readRecordValue(carrier, "carrierName") ?? "Unknown",
3610
3688
  insuredName: readRecordValue(insured, "insuredName") ?? "Unknown",
3611
3689
  coverages: coverageRecords,
3690
+ documentMetadata: {},
3691
+ documentOutline: [],
3612
3692
  policyTypes: readRecordValue(classify, "policyTypes"),
3613
3693
  ...sanitizeNulls(carrier ?? {}),
3614
3694
  ...sanitizeNulls(insured ?? {}),
@@ -3660,6 +3740,247 @@ function assembleDocument(documentId, documentType, memory) {
3660
3740
  return doc;
3661
3741
  }
3662
3742
 
3743
+ // src/extraction/document-structure.ts
3744
+ var FACT_ARRAY_PATHS = [
3745
+ "coverages",
3746
+ "enrichedCoverages",
3747
+ "endorsements",
3748
+ "exclusions",
3749
+ "conditions",
3750
+ "definitions",
3751
+ "coveredReasons",
3752
+ "taxesAndFees",
3753
+ "premiumBreakdown",
3754
+ "supplementaryFacts"
3755
+ ];
3756
+ function slugPart(value) {
3757
+ const text = String(value ?? "").toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "");
3758
+ return text || "node";
3759
+ }
3760
+ function numberValue(record, ...keys) {
3761
+ for (const key of keys) {
3762
+ const value = record[key];
3763
+ if (typeof value === "number" && Number.isFinite(value)) return value;
3764
+ }
3765
+ return void 0;
3766
+ }
3767
+ function stringValue2(record, ...keys) {
3768
+ for (const key of keys) {
3769
+ const value = record[key];
3770
+ if (typeof value === "string" && value.trim()) return value.trim();
3771
+ }
3772
+ return void 0;
3773
+ }
3774
+ function stringArray(value) {
3775
+ return Array.isArray(value) ? value.filter((item) => typeof item === "string" && item.length > 0) : [];
3776
+ }
3777
+ function sourceUnit3(span) {
3778
+ return span.sourceUnit ?? span.metadata?.sourceUnit;
3779
+ }
3780
+ function spanPage(span) {
3781
+ return span.pageStart ?? span.location?.page ?? span.location?.startPage;
3782
+ }
3783
+ function spanPageEnd(span) {
3784
+ return span.pageEnd ?? span.location?.endPage ?? spanPage(span);
3785
+ }
3786
+ function sourceSpansForPage(sourceSpans, page) {
3787
+ return sourceSpans.filter((span) => spanPage(span) === page && sourceUnit3(span) === "page").map((span) => span.id);
3788
+ }
3789
+ function nodePageOverlaps(node, record) {
3790
+ const recordStart = numberValue(record, "pageNumber", "pageStart", "resolvedFromPage");
3791
+ const recordEnd = numberValue(record, "pageNumber", "pageEnd", "resolvedFromPage") ?? recordStart;
3792
+ if (!recordStart) return false;
3793
+ const nodeStart = node.pageStart ?? node.pageEnd;
3794
+ const nodeEnd = node.pageEnd ?? node.pageStart;
3795
+ if (!nodeStart) return false;
3796
+ return recordStart <= (nodeEnd ?? nodeStart) && (recordEnd ?? recordStart) >= nodeStart;
3797
+ }
3798
+ function nodeFormMatches(node, record) {
3799
+ const formNumber = stringValue2(record, "formNumber");
3800
+ return Boolean(formNumber && node.formNumber && formNumber.toLowerCase() === node.formNumber.toLowerCase());
3801
+ }
3802
+ function nodeSourceOverlaps(node, record) {
3803
+ const nodeSourceIds = new Set(node.sourceSpanIds ?? []);
3804
+ if (nodeSourceIds.size === 0) return false;
3805
+ return stringArray(record.sourceSpanIds).some((id) => nodeSourceIds.has(id));
3806
+ }
3807
+ function findBestNode(nodes, record) {
3808
+ let best;
3809
+ const visit = (node) => {
3810
+ let score = 0;
3811
+ if (nodeSourceOverlaps(node, record)) score += 8;
3812
+ if (nodeFormMatches(node, record)) score += 4;
3813
+ if (nodePageOverlaps(node, record)) score += 3;
3814
+ if (score > 0 && (!best || score > best.score)) {
3815
+ best = { node, score };
3816
+ }
3817
+ for (const child of node.children ?? []) visit(child);
3818
+ };
3819
+ for (const node of nodes) visit(node);
3820
+ return best?.node;
3821
+ }
3822
+ function attachDocumentNodeIds(document, nodes) {
3823
+ const doc = document;
3824
+ for (const path of FACT_ARRAY_PATHS) {
3825
+ const value = doc[path];
3826
+ if (!Array.isArray(value)) continue;
3827
+ doc[path] = value.map((item) => {
3828
+ if (!item || typeof item !== "object" || Array.isArray(item)) return item;
3829
+ const record = item;
3830
+ if (typeof record.documentNodeId === "string" && record.documentNodeId.length > 0) return record;
3831
+ const node = findBestNode(nodes, record);
3832
+ return node ? { ...record, documentNodeId: node.id } : record;
3833
+ });
3834
+ }
3835
+ }
3836
+ function buildNodeFromSection(section, index) {
3837
+ const title = stringValue2(section, "title", "name", "sectionRef") ?? `Section ${index + 1}`;
3838
+ const id = stringValue2(section, "documentNodeId", "recordId") ?? `section:${index}:${slugPart(title)}`;
3839
+ const pageStart = numberValue(section, "pageStart", "pageNumber");
3840
+ const pageEnd = numberValue(section, "pageEnd", "pageNumber") ?? pageStart;
3841
+ const type = stringValue2(section, "type");
3842
+ const coverageType = stringValue2(section, "coverageType");
3843
+ const sourceSpanIds = stringArray(section.sourceSpanIds);
3844
+ const children = Array.isArray(section.subsections) ? section.subsections.filter((item) => Boolean(item) && typeof item === "object" && !Array.isArray(item)).map((subsection, childIndex) => {
3845
+ const childTitle = stringValue2(subsection, "title", "name") ?? `${title} ${childIndex + 1}`;
3846
+ const childId = stringValue2(subsection, "documentNodeId", "recordId") ?? `${id}:subsection:${childIndex}:${slugPart(childTitle)}`;
3847
+ return {
3848
+ id: childId,
3849
+ title: childTitle,
3850
+ originalTitle: childTitle,
3851
+ type,
3852
+ label: type,
3853
+ level: 2,
3854
+ sectionNumber: stringValue2(subsection, "sectionNumber"),
3855
+ pageStart: numberValue(subsection, "pageNumber") ?? pageStart,
3856
+ pageEnd: numberValue(subsection, "pageNumber") ?? pageStart,
3857
+ excerpt: stringValue2(subsection, "excerpt"),
3858
+ content: stringValue2(subsection, "content"),
3859
+ sourceSpanIds: stringArray(subsection.sourceSpanIds),
3860
+ sourceTextHash: stringValue2(subsection, "sourceTextHash")
3861
+ };
3862
+ }) : void 0;
3863
+ return {
3864
+ id,
3865
+ title,
3866
+ originalTitle: title,
3867
+ type,
3868
+ label: type,
3869
+ level: 1,
3870
+ sectionNumber: stringValue2(section, "sectionNumber"),
3871
+ pageStart,
3872
+ pageEnd,
3873
+ formNumber: stringValue2(section, "formNumber"),
3874
+ formTitle: stringValue2(section, "formTitle"),
3875
+ excerpt: stringValue2(section, "excerpt"),
3876
+ content: stringValue2(section, "content"),
3877
+ interpretationLabels: [type, coverageType].filter((value) => Boolean(value)),
3878
+ sourceSpanIds: sourceSpanIds.length > 0 ? sourceSpanIds : void 0,
3879
+ sourceTextHash: stringValue2(section, "sourceTextHash"),
3880
+ children: children && children.length > 0 ? children : void 0
3881
+ };
3882
+ }
3883
+ function buildNodesFromSourceSpans(sourceSpans) {
3884
+ const candidates = sourceSpans.filter((span) => {
3885
+ const unit = sourceUnit3(span);
3886
+ return unit === "section" || unit === "section_candidate" || unit === "page";
3887
+ });
3888
+ return candidates.sort((left, right) => (spanPage(left) ?? 0) - (spanPage(right) ?? 0) || left.id.localeCompare(right.id)).map((span, index) => {
3889
+ const title = span.sectionId ?? span.formNumber ?? (sourceUnit3(span) === "page" && spanPage(span) ? `Page ${spanPage(span)}` : `Source unit ${index + 1}`);
3890
+ return {
3891
+ id: `source:${index}:${slugPart(span.id)}`,
3892
+ title,
3893
+ originalTitle: title,
3894
+ type: sourceUnit3(span),
3895
+ label: sourceUnit3(span),
3896
+ level: 1,
3897
+ pageStart: spanPage(span),
3898
+ pageEnd: spanPageEnd(span),
3899
+ formNumber: span.formNumber,
3900
+ excerpt: span.text.slice(0, 500),
3901
+ sourceSpanIds: [span.id],
3902
+ sourceTextHash: span.textHash ?? span.hash
3903
+ };
3904
+ });
3905
+ }
3906
+ function flattenToc(nodes) {
3907
+ const entries = [];
3908
+ const visit = (node) => {
3909
+ entries.push({
3910
+ title: node.title,
3911
+ level: node.level,
3912
+ pageStart: node.pageStart,
3913
+ pageEnd: node.pageEnd,
3914
+ documentNodeId: node.id,
3915
+ sourceSpanIds: node.sourceSpanIds
3916
+ });
3917
+ for (const child of node.children ?? []) visit(child);
3918
+ };
3919
+ for (const node of nodes) visit(node);
3920
+ return entries;
3921
+ }
3922
+ function buildPageMap(pageAssignments, sourceSpans, document) {
3923
+ const forms = document.formInventory ?? [];
3924
+ return pageAssignments.sort((left, right) => left.localPageNumber - right.localPageNumber).map((assignment) => {
3925
+ const form = forms.find((item) => {
3926
+ const start = item.pageStart;
3927
+ const end = item.pageEnd ?? start;
3928
+ return start != null && end != null && assignment.localPageNumber >= start && assignment.localPageNumber <= end;
3929
+ });
3930
+ return {
3931
+ page: assignment.localPageNumber,
3932
+ label: assignment.notes,
3933
+ formNumber: form?.formNumber,
3934
+ formTitle: form?.title,
3935
+ extractorNames: assignment.extractorNames,
3936
+ sourceSpanIds: sourceSpansForPage(sourceSpans, assignment.localPageNumber)
3937
+ };
3938
+ });
3939
+ }
3940
+ function buildAgentGuidance(document) {
3941
+ const guidance = [
3942
+ {
3943
+ kind: "source_structure",
3944
+ title: "Use the source outline as navigation",
3945
+ detail: "The documentOutline preserves source order and page ranges. Treat interpretation labels as hints, not as a replacement for the source document structure."
3946
+ }
3947
+ ];
3948
+ if (document.declarations) {
3949
+ guidance.push({
3950
+ kind: "declarations",
3951
+ title: "Declarations establish policy facts",
3952
+ detail: "Declarations and schedules generally establish named insured, policy period, limits, deductibles, premium, and rating facts unless modified elsewhere."
3953
+ });
3954
+ }
3955
+ if (document.endorsements?.length || document.formInventory?.some((form) => form.formType === "endorsement")) {
3956
+ guidance.push({
3957
+ kind: "endorsement_override",
3958
+ title: "Endorsements may override base terms",
3959
+ detail: "Endorsements and change forms can add, remove, or override base policy wording. When terms conflict, inspect the effective endorsement language and source page."
3960
+ });
3961
+ }
3962
+ return guidance;
3963
+ }
3964
+ function attachDocumentStructure(params) {
3965
+ const docRecord = params.document;
3966
+ const sections = Array.isArray(params.document.sections) ? params.document.sections : [];
3967
+ const existingOutline = Array.isArray(docRecord.documentOutline) ? docRecord.documentOutline : [];
3968
+ const documentOutline = existingOutline.length > 0 ? existingOutline : sections.length > 0 ? sections.map((section, index) => buildNodeFromSection(section, index)) : buildNodesFromSourceSpans(params.sourceSpans);
3969
+ if (documentOutline.length > 0) {
3970
+ attachDocumentNodeIds(params.document, documentOutline);
3971
+ }
3972
+ const metadata = {
3973
+ ...docRecord.documentMetadata && typeof docRecord.documentMetadata === "object" ? docRecord.documentMetadata : {},
3974
+ ...params.document.formInventory?.length ? { formInventory: params.document.formInventory } : {},
3975
+ ...documentOutline.length > 0 ? { tableOfContents: flattenToc(documentOutline) } : {},
3976
+ ...params.pageAssignments.length > 0 ? { pageMap: buildPageMap(params.pageAssignments, params.sourceSpans, params.document) } : {},
3977
+ agentGuidance: buildAgentGuidance(params.document)
3978
+ };
3979
+ docRecord.documentMetadata = metadata;
3980
+ docRecord.documentOutline = documentOutline;
3981
+ return params.document;
3982
+ }
3983
+
3663
3984
  // src/prompts/coordinator/format.ts
3664
3985
  function buildFormatPrompt(entries) {
3665
3986
  const block = entries.map((e) => `===ENTRY ${e.id}===
@@ -4259,6 +4580,8 @@ function chunkDocument(doc) {
4259
4580
  {
4260
4581
  formNumber: form.formNumber,
4261
4582
  formType: form.formType,
4583
+ documentNodeId: form.documentNodeId,
4584
+ sourceSpanIds: form.sourceSpanIds?.join(","),
4262
4585
  documentType: doc.type
4263
4586
  }
4264
4587
  );
@@ -4285,6 +4608,7 @@ function chunkDocument(doc) {
4285
4608
  formNumber: end.formNumber,
4286
4609
  pageStart: end.pageStart,
4287
4610
  pageEnd: end.pageEnd,
4611
+ documentNodeId: end.documentNodeId,
4288
4612
  sourceSpanIds: end.sourceSpanIds?.join(","),
4289
4613
  sourceTextHash: end.sourceTextHash,
4290
4614
  documentType: doc.type
@@ -4296,6 +4620,8 @@ function chunkDocument(doc) {
4296
4620
  ${exc.content}`.trim(), {
4297
4621
  formNumber: exc.formNumber,
4298
4622
  pageNumber: exc.pageNumber,
4623
+ documentNodeId: exc.documentNodeId,
4624
+ sourceSpanIds: exc.sourceSpanIds?.join(","),
4299
4625
  documentType: doc.type
4300
4626
  });
4301
4627
  });
@@ -4313,6 +4639,8 @@ ${exc.content}`.trim(), {
4313
4639
  conditionName: cond.name,
4314
4640
  conditionType: cond.conditionType,
4315
4641
  pageNumber: cond.pageNumber,
4642
+ documentNodeId: cond.documentNodeId,
4643
+ sourceSpanIds: cond.sourceSpanIds?.join(","),
4316
4644
  documentType: doc.type
4317
4645
  }
4318
4646
  );
@@ -4334,6 +4662,9 @@ ${exc.content}`.trim(), {
4334
4662
  formTitle: firstString2(definition, ["formTitle"]),
4335
4663
  pageNumber: typeof definition.pageNumber === "number" ? definition.pageNumber : void 0,
4336
4664
  sectionRef: firstString2(definition, ["sectionRef", "sectionTitle"]),
4665
+ documentNodeId: firstString2(definition, ["documentNodeId"]),
4666
+ sourceSpanIds: Array.isArray(definition.sourceSpanIds) ? definition.sourceSpanIds.join(",") : void 0,
4667
+ sourceTextHash: firstString2(definition, ["sourceTextHash"]),
4337
4668
  documentType: doc.type
4338
4669
  }
4339
4670
  );
@@ -4362,6 +4693,9 @@ ${exc.content}`.trim(), {
4362
4693
  formTitle: firstString2(coveredReason, ["formTitle"]),
4363
4694
  pageNumber: typeof coveredReason.pageNumber === "number" ? coveredReason.pageNumber : void 0,
4364
4695
  sectionRef: firstString2(coveredReason, ["sectionRef", "sectionTitle"]),
4696
+ documentNodeId: firstString2(coveredReason, ["documentNodeId"]),
4697
+ sourceSpanIds: Array.isArray(coveredReason.sourceSpanIds) ? coveredReason.sourceSpanIds.join(",") : void 0,
4698
+ sourceTextHash: firstString2(coveredReason, ["sourceTextHash"]),
4365
4699
  documentType: doc.type
4366
4700
  }
4367
4701
  );
@@ -4423,6 +4757,7 @@ ${declLines.join("\n")}`, declMeta);
4423
4757
  evidenceKind: "navigation",
4424
4758
  sectionType: sec.type,
4425
4759
  sectionNumber: sec.sectionNumber,
4760
+ documentNodeId: sec.documentNodeId,
4426
4761
  pageStart: sec.pageStart,
4427
4762
  pageEnd: sec.pageEnd,
4428
4763
  sourceSpanIds: sec.sourceSpanIds?.join(","),
@@ -4446,6 +4781,7 @@ ${declLines.join("\n")}`, declMeta);
4446
4781
  sectionType: sec.type,
4447
4782
  parentSection: sec.title,
4448
4783
  sectionNumber: sub.sectionNumber,
4784
+ documentNodeId: sub.documentNodeId,
4449
4785
  pageNumber: sub.pageNumber,
4450
4786
  sourceSpanIds: sub.sourceSpanIds?.join(","),
4451
4787
  sourceTextHash: sub.sourceTextHash,
@@ -4454,6 +4790,31 @@ ${declLines.join("\n")}`, declMeta);
4454
4790
  );
4455
4791
  });
4456
4792
  });
4793
+ asRecordArray(extendedDoc.documentOutline).forEach((node, i) => {
4794
+ const title = firstString2(node, ["title", "originalTitle"]) ?? `Document Node ${i + 1}`;
4795
+ const children = asRecordArray(node.children);
4796
+ pushChunk(
4797
+ `section:outline:${i}`,
4798
+ "section",
4799
+ lines([
4800
+ `Document Outline: ${title}`,
4801
+ firstString2(node, ["label", "type"]) ? `Label: ${firstString2(node, ["label", "type"])}` : null,
4802
+ typeof node.pageStart === "number" ? `Pages: ${node.pageStart}${typeof node.pageEnd === "number" ? `-${node.pageEnd}` : ""}` : null,
4803
+ firstString2(node, ["excerpt", "content"]),
4804
+ children.length > 0 ? `Children: ${children.map((child, childIndex) => firstString2(child, ["title", "originalTitle"]) ?? `Child ${childIndex + 1}`).join(", ")}` : null
4805
+ ]),
4806
+ {
4807
+ evidenceKind: "navigation",
4808
+ documentNodeId: firstString2(node, ["id"]),
4809
+ sectionType: firstString2(node, ["type", "label"]),
4810
+ pageStart: typeof node.pageStart === "number" ? node.pageStart : void 0,
4811
+ pageEnd: typeof node.pageEnd === "number" ? node.pageEnd : void 0,
4812
+ sourceSpanIds: Array.isArray(node.sourceSpanIds) ? node.sourceSpanIds.join(",") : void 0,
4813
+ sourceTextHash: firstString2(node, ["sourceTextHash"]),
4814
+ documentType: doc.type
4815
+ }
4816
+ );
4817
+ });
4457
4818
  doc.locations?.forEach((loc, i) => {
4458
4819
  chunks.push({
4459
4820
  id: `${docId}:location:${i}`,
@@ -7277,13 +7638,13 @@ async function resolveReferentialCoverages(params) {
7277
7638
  function textValue(value) {
7278
7639
  return typeof value === "string" && value.trim() ? value.trim() : void 0;
7279
7640
  }
7280
- function numberValue(value) {
7641
+ function numberValue2(value) {
7281
7642
  return typeof value === "number" && Number.isFinite(value) ? value : void 0;
7282
7643
  }
7283
7644
  function normalize(value) {
7284
7645
  return value.toLowerCase().replace(/&/g, "and").replace(/[^a-z0-9]+/g, " ").trim();
7285
7646
  }
7286
- function sourceUnit3(span) {
7647
+ function sourceUnit4(span) {
7287
7648
  return span.sourceUnit ?? span.metadata?.sourceUnit;
7288
7649
  }
7289
7650
  function pageNumber(span) {
@@ -7353,7 +7714,7 @@ function firstField(fields, patterns) {
7353
7714
  }
7354
7715
  function coverageFromRow(span) {
7355
7716
  const rowText = span.text.trim();
7356
- if (!rowText || sourceUnit3(span) !== "table_row") return void 0;
7717
+ if (!rowText || sourceUnit4(span) !== "table_row") return void 0;
7357
7718
  if (span.table?.isHeader || span.metadata?.isHeader === "true") return void 0;
7358
7719
  const fields = splitRowFields(rowText);
7359
7720
  let name;
@@ -7415,7 +7776,7 @@ function coverageKey(coverage) {
7415
7776
  textValue(coverage.name) ?? "",
7416
7777
  textValue(coverage.limit) ?? "",
7417
7778
  textValue(coverage.limitType) ?? "",
7418
- numberValue(coverage.pageNumber) ?? ""
7779
+ numberValue2(coverage.pageNumber) ?? ""
7419
7780
  ].map((part) => normalize(String(part))).join("|");
7420
7781
  }
7421
7782
  function rowMatchesExisting(row, existing) {
@@ -7433,7 +7794,7 @@ function recoverCoverageScheduleRows(params) {
7433
7794
  const payload = params.memory.get("coverage_limits");
7434
7795
  const existing = Array.isArray(payload?.coverages) ? payload.coverages : [];
7435
7796
  const pages = coveragePages(params.pageAssignments);
7436
- const candidates = params.sourceSpans.filter((span) => sourceUnit3(span) === "table_row").filter((span) => {
7797
+ const candidates = params.sourceSpans.filter((span) => sourceUnit4(span) === "table_row").filter((span) => {
7437
7798
  const page = pageNumber(span);
7438
7799
  return page !== void 0 && pages.has(page);
7439
7800
  }).map(coverageFromRow).filter((coverage) => Boolean(coverage));
@@ -8108,7 +8469,10 @@ var ARRAY_PATHS = [
8108
8469
  { memoryKey: "sections", arrayKeys: ["sections"] },
8109
8470
  { memoryKey: "definitions", arrayKeys: ["definitions"] },
8110
8471
  { memoryKey: "covered_reasons", arrayKeys: ["coveredReasons", "covered_reasons"] },
8111
- { memoryKey: "declarations", arrayKeys: ["fields"] }
8472
+ { memoryKey: "declarations", arrayKeys: ["fields"] },
8473
+ { memoryKey: "premium_breakdown", arrayKeys: ["premiumBreakdown", "taxesAndFees"] },
8474
+ { memoryKey: "supplementary", arrayKeys: ["auxiliaryFacts", "supplementaryFacts"] },
8475
+ { memoryKey: "form_inventory", arrayKeys: ["forms"] }
8112
8476
  ];
8113
8477
  function normalize2(value) {
8114
8478
  return value.replace(/\s+/g, " ").trim().toLowerCase();
@@ -8120,7 +8484,7 @@ function textValue2(record, ...keys) {
8120
8484
  }
8121
8485
  return void 0;
8122
8486
  }
8123
- function numberValue2(record, ...keys) {
8487
+ function numberValue3(record, ...keys) {
8124
8488
  for (const key of keys) {
8125
8489
  const value = record[key];
8126
8490
  if (typeof value === "number" && Number.isFinite(value)) return value;
@@ -8153,11 +8517,11 @@ function textMatches(record, span) {
8153
8517
  function sourceHashFor(spans) {
8154
8518
  return spans.map((span) => span.textHash ?? span.hash).filter(Boolean).join(":") || void 0;
8155
8519
  }
8156
- function sourceUnit4(span) {
8520
+ function sourceUnit5(span) {
8157
8521
  return span.sourceUnit ?? span.metadata?.sourceUnit;
8158
8522
  }
8159
8523
  function hierarchyScore(span) {
8160
- switch (sourceUnit4(span)) {
8524
+ switch (sourceUnit5(span)) {
8161
8525
  case "table_row":
8162
8526
  return 5;
8163
8527
  case "table":
@@ -8180,8 +8544,8 @@ function preferParentRows(matches, sourceSpans) {
8180
8544
  const expanded = [];
8181
8545
  const seen = /* @__PURE__ */ new Set();
8182
8546
  for (const match of matches) {
8183
- const parent = sourceUnit4(match) === "table_cell" ? byId.get(parentRowId(match) ?? "") : void 0;
8184
- const preferred = parent && sourceUnit4(parent) === "table_row" ? parent : match;
8547
+ const parent = sourceUnit5(match) === "table_cell" ? byId.get(parentRowId(match) ?? "") : void 0;
8548
+ const preferred = parent && sourceUnit5(parent) === "table_row" ? parent : match;
8185
8549
  if (seen.has(preferred.id)) continue;
8186
8550
  seen.add(preferred.id);
8187
8551
  expanded.push(preferred);
@@ -8190,8 +8554,8 @@ function preferParentRows(matches, sourceSpans) {
8190
8554
  }
8191
8555
  function findSourceSpansForRecord(record, sourceSpans) {
8192
8556
  if (sourceSpans.length === 0) return [];
8193
- const pageStart = numberValue2(record, "pageNumber", "pageStart");
8194
- const pageEnd = numberValue2(record, "pageNumber", "pageEnd");
8557
+ const pageStart = numberValue3(record, "pageNumber", "pageStart");
8558
+ const pageEnd = numberValue3(record, "pageNumber", "pageEnd");
8195
8559
  const scored = sourceSpans.map((span) => {
8196
8560
  let score = 0;
8197
8561
  if (pageOverlaps(pageStart, pageEnd, span)) score += 4;
@@ -9134,6 +9498,7 @@ ${pageText}`;
9134
9498
  }));
9135
9499
  onProgress?.("Assembling document...");
9136
9500
  const document = assembleDocument(id, documentType, memory);
9501
+ attachDocumentStructure({ document, pageAssignments, sourceSpans });
9137
9502
  await pipelineCtx.save("assemble", {
9138
9503
  id,
9139
9504
  pageCount,
@@ -12932,6 +13297,11 @@ var AGENT_TOOLS = [
12932
13297
  DeductibleTypeSchema,
12933
13298
  DefenseCostTreatmentSchema,
12934
13299
  DefinitionSchema,
13300
+ DocumentAgentGuidanceSchema,
13301
+ DocumentMetadataSchema,
13302
+ DocumentNodeSchema,
13303
+ DocumentPageMapEntrySchema,
13304
+ DocumentTableOfContentsEntrySchema,
12935
13305
  DocumentTypeSchema,
12936
13306
  DriverRecordSchema,
12937
13307
  DwellingDetailsSchema,