@claritylabs/cl-sdk 0.10.3 → 0.12.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/dist/index.mjs CHANGED
@@ -1575,6 +1575,358 @@ async function runExtractor(params) {
1575
1575
  };
1576
1576
  }
1577
1577
 
1578
+ // src/extraction/promote.ts
1579
+ function getDeclarationFields(doc) {
1580
+ const decl = doc.declarations;
1581
+ return Array.isArray(decl?.fields) ? decl.fields : [];
1582
+ }
1583
+ function fieldMatches(fieldName, patterns) {
1584
+ const lower = fieldName.toLowerCase().replace(/[\s_-]/g, "");
1585
+ return patterns.some((p) => lower === p.toLowerCase().replace(/[\s_-]/g, ""));
1586
+ }
1587
+ function findFieldValue(fields, patterns) {
1588
+ const match = fields.find((f) => fieldMatches(f.field, patterns));
1589
+ return match?.value;
1590
+ }
1591
+ function promoteCarrierFields(doc) {
1592
+ const raw = doc;
1593
+ if (!raw.carrierNaicNumber && raw.naicNumber) {
1594
+ raw.carrierNaicNumber = raw.naicNumber;
1595
+ }
1596
+ if (!raw.carrierAmBestRating && raw.amBestRating) {
1597
+ raw.carrierAmBestRating = raw.amBestRating;
1598
+ }
1599
+ if (!raw.carrierAdmittedStatus && raw.admittedStatus) {
1600
+ raw.carrierAdmittedStatus = raw.admittedStatus;
1601
+ }
1602
+ delete raw.naicNumber;
1603
+ delete raw.amBestRating;
1604
+ delete raw.admittedStatus;
1605
+ if (!raw.insurer && raw.carrierLegalName) {
1606
+ raw.insurer = {
1607
+ legalName: raw.carrierLegalName,
1608
+ ...raw.carrierNaicNumber ? { naicNumber: raw.carrierNaicNumber } : {},
1609
+ ...raw.carrierAmBestRating ? { amBestRating: raw.carrierAmBestRating } : {},
1610
+ ...raw.carrierAdmittedStatus ? { admittedStatus: raw.carrierAdmittedStatus } : {}
1611
+ };
1612
+ }
1613
+ }
1614
+ var BROKER_NAME_PATTERNS = [
1615
+ "brokerName",
1616
+ "broker",
1617
+ "agentName",
1618
+ "agent",
1619
+ "producerName",
1620
+ "producerAgency",
1621
+ "agencyName",
1622
+ "brokerAgency"
1623
+ ];
1624
+ var BROKER_CONTACT_PATTERNS = [
1625
+ "brokerContactName",
1626
+ "brokerContact",
1627
+ "agentContactName",
1628
+ "producerContactName",
1629
+ "producerContact"
1630
+ ];
1631
+ var BROKER_LICENSE_PATTERNS = [
1632
+ "brokerLicenseNumber",
1633
+ "brokerNumber",
1634
+ "agentLicenseNumber",
1635
+ "producerLicenseNumber",
1636
+ "producerNumber",
1637
+ "agentNumber"
1638
+ ];
1639
+ var BROKER_PHONE_PATTERNS = ["brokerPhone", "agentPhone", "producerPhone"];
1640
+ var BROKER_EMAIL_PATTERNS = ["brokerEmail", "agentEmail", "producerEmail"];
1641
+ var BROKER_ADDRESS_PATTERNS = ["brokerAddress", "agentAddress", "producerAddress"];
1642
+ function promoteBroker(doc) {
1643
+ const raw = doc;
1644
+ const fields = getDeclarationFields(doc);
1645
+ const brokerAgency = raw.brokerAgency || findFieldValue(fields, BROKER_NAME_PATTERNS);
1646
+ const brokerContact = raw.brokerContactName || findFieldValue(fields, BROKER_CONTACT_PATTERNS);
1647
+ const brokerLicense = raw.brokerLicenseNumber || findFieldValue(fields, BROKER_LICENSE_PATTERNS);
1648
+ const brokerPhone = findFieldValue(fields, BROKER_PHONE_PATTERNS);
1649
+ const brokerEmail = findFieldValue(fields, BROKER_EMAIL_PATTERNS);
1650
+ const brokerAddress = findFieldValue(fields, BROKER_ADDRESS_PATTERNS);
1651
+ if (brokerAgency) raw.brokerAgency = brokerAgency;
1652
+ if (brokerContact) raw.brokerContactName = brokerContact;
1653
+ if (brokerLicense) raw.brokerLicenseNumber = brokerLicense;
1654
+ if (!raw.producer && brokerAgency) {
1655
+ raw.producer = {
1656
+ agencyName: brokerAgency,
1657
+ ...brokerContact ? { contactName: brokerContact } : {},
1658
+ ...brokerLicense ? { licenseNumber: brokerLicense } : {},
1659
+ ...brokerPhone ? { phone: brokerPhone } : {},
1660
+ ...brokerEmail ? { email: brokerEmail } : {},
1661
+ ...brokerAddress ? { address: { street1: brokerAddress } } : {}
1662
+ };
1663
+ }
1664
+ }
1665
+ var LOSS_PAYEE_NAME_PATTERNS = [
1666
+ "lossPayeeName",
1667
+ "lossPayee",
1668
+ "lossPayeeHolder"
1669
+ ];
1670
+ var LOSS_PAYEE_ADDRESS_PATTERNS = ["lossPayeeAddress"];
1671
+ var MORTGAGE_HOLDER_NAME_PATTERNS = [
1672
+ "mortgagee",
1673
+ "mortgageHolder",
1674
+ "mortgageHolderName",
1675
+ "mortgageeName",
1676
+ "lienholder",
1677
+ "lienholderName"
1678
+ ];
1679
+ var MORTGAGE_HOLDER_ADDRESS_PATTERNS = [
1680
+ "mortgageeAddress",
1681
+ "mortgageHolderAddress",
1682
+ "lienholderAddress"
1683
+ ];
1684
+ function promoteLossPayees(doc) {
1685
+ const raw = doc;
1686
+ const fields = getDeclarationFields(doc);
1687
+ if (!raw.lossPayees || Array.isArray(raw.lossPayees) && raw.lossPayees.length === 0) {
1688
+ const name = findFieldValue(fields, LOSS_PAYEE_NAME_PATTERNS);
1689
+ if (name) {
1690
+ const address = findFieldValue(fields, LOSS_PAYEE_ADDRESS_PATTERNS);
1691
+ raw.lossPayees = [{
1692
+ name,
1693
+ role: "loss_payee",
1694
+ ...address ? { address: { street1: address } } : {}
1695
+ }];
1696
+ }
1697
+ }
1698
+ if (!raw.mortgageHolders || Array.isArray(raw.mortgageHolders) && raw.mortgageHolders.length === 0) {
1699
+ const name = findFieldValue(fields, MORTGAGE_HOLDER_NAME_PATTERNS);
1700
+ if (name) {
1701
+ const address = findFieldValue(fields, MORTGAGE_HOLDER_ADDRESS_PATTERNS);
1702
+ raw.mortgageHolders = [{
1703
+ name,
1704
+ role: "mortgage_holder",
1705
+ ...address ? { address: { street1: address } } : {}
1706
+ }];
1707
+ }
1708
+ }
1709
+ }
1710
+ function promoteLocations(doc) {
1711
+ const raw = doc;
1712
+ if (Array.isArray(raw.locations) && raw.locations.length > 0) return;
1713
+ const fields = getDeclarationFields(doc);
1714
+ if (fields.length === 0) return;
1715
+ const locationGroups = /* @__PURE__ */ new Map();
1716
+ for (const f of fields) {
1717
+ const lower = f.field.toLowerCase().replace(/[\s_-]/g, "");
1718
+ if (lower.includes("locationnumber") || lower.includes("locnumber") || lower.includes("locno")) {
1719
+ const key = f.value;
1720
+ if (!locationGroups.has(key)) locationGroups.set(key, /* @__PURE__ */ new Map());
1721
+ locationGroups.get(key).set("number", f.value);
1722
+ continue;
1723
+ }
1724
+ if (lower.includes("buildingnumber") || lower.includes("bldgnumber") || lower.includes("bldgno")) {
1725
+ const lastKey2 = [...locationGroups.keys()].pop();
1726
+ if (lastKey2) locationGroups.get(lastKey2).set("buildingNumber", f.value);
1727
+ continue;
1728
+ }
1729
+ const section = (f.section ?? "").toLowerCase();
1730
+ const isLocationField = section.includes("location") || section.includes("building") || section.includes("premises") || section.includes("schedule of locations");
1731
+ if (!isLocationField) continue;
1732
+ if (locationGroups.size === 0) {
1733
+ locationGroups.set("1", /* @__PURE__ */ new Map([["number", "1"]]));
1734
+ }
1735
+ const lastKey = [...locationGroups.keys()].pop();
1736
+ const group = locationGroups.get(lastKey);
1737
+ if (lower.includes("construction") || lower.includes("constructiontype")) {
1738
+ group.set("constructionType", f.value);
1739
+ } else if (lower.includes("occupancy") || lower.includes("occupancytype")) {
1740
+ group.set("occupancy", f.value);
1741
+ } else if (lower.includes("yearbuilt")) {
1742
+ group.set("yearBuilt", f.value);
1743
+ } else if (lower.includes("squarefootage") || lower.includes("sqft") || lower.includes("area")) {
1744
+ group.set("squareFootage", f.value);
1745
+ } else if (lower.includes("protectionclass") || lower.includes("fireprotection")) {
1746
+ group.set("protectionClass", f.value);
1747
+ } else if (lower.includes("sprinkler")) {
1748
+ group.set("sprinklered", f.value);
1749
+ } else if (lower.includes("buildingvalue") || lower.includes("buildingamt") || lower.includes("buildingcoverage")) {
1750
+ group.set("buildingValue", f.value);
1751
+ } else if (lower.includes("contentsvalue") || lower.includes("contentsamt") || lower.includes("contentscoverage")) {
1752
+ group.set("contentsValue", f.value);
1753
+ } else if (lower.includes("businessincome") || lower.includes("bivalue") || lower.includes("businessincomevalue")) {
1754
+ group.set("businessIncomeValue", f.value);
1755
+ } else if (lower.includes("description") || lower.includes("buildingdescription") || lower.includes("locationdescription")) {
1756
+ group.set("description", f.value);
1757
+ } else if (lower.includes("address") || lower.includes("industryaddress") || lower.includes("locationaddress") || lower.includes("premisesaddress")) {
1758
+ group.set("address", f.value);
1759
+ } else if (lower.includes("alarm") || lower.includes("alarmtype")) {
1760
+ group.set("alarmType", f.value);
1761
+ }
1762
+ }
1763
+ if (locationGroups.size === 0) return;
1764
+ const locations = [];
1765
+ for (const [, group] of locationGroups) {
1766
+ const num = parseInt(group.get("number") ?? "0", 10) || locations.length + 1;
1767
+ const addressStr = group.get("address");
1768
+ locations.push({
1769
+ number: num,
1770
+ address: addressStr ? { street1: addressStr } : { street1: "See declarations" },
1771
+ ...group.get("description") ? { description: group.get("description") } : {},
1772
+ ...group.get("constructionType") ? { constructionType: group.get("constructionType") } : {},
1773
+ ...group.get("occupancy") ? { occupancy: group.get("occupancy") } : {},
1774
+ ...group.get("yearBuilt") ? { yearBuilt: parseInt(group.get("yearBuilt"), 10) || void 0 } : {},
1775
+ ...group.get("squareFootage") ? { squareFootage: parseInt(group.get("squareFootage").replace(/[^0-9]/g, ""), 10) || void 0 } : {},
1776
+ ...group.get("protectionClass") ? { protectionClass: group.get("protectionClass") } : {},
1777
+ ...group.get("sprinklered") ? { sprinklered: /yes|true/i.test(group.get("sprinklered")) } : {},
1778
+ ...group.get("buildingValue") ? { buildingValue: group.get("buildingValue") } : {},
1779
+ ...group.get("contentsValue") ? { contentsValue: group.get("contentsValue") } : {},
1780
+ ...group.get("businessIncomeValue") ? { businessIncomeValue: group.get("businessIncomeValue") } : {},
1781
+ ...group.get("alarmType") ? { alarmType: group.get("alarmType") } : {}
1782
+ });
1783
+ }
1784
+ if (locations.length > 0) {
1785
+ raw.locations = locations;
1786
+ }
1787
+ }
1788
+ function normalizeName(name) {
1789
+ return name.toLowerCase().replace(/[^a-z0-9]/g, "");
1790
+ }
1791
+ var LIMIT_COVERAGE_MAP = [
1792
+ // GL standard
1793
+ [["eachoccurrence", "peroccurrence", "occurrencecombined"], "perOccurrence"],
1794
+ [["generalaggregate"], "generalAggregate"],
1795
+ [["productscompletedoperationsaggregate", "productscompletedopsaggregate", "prodcompopsagg"], "productsCompletedOpsAggregate"],
1796
+ [["personaladvertisinginjury", "personaladvinjury", "pai"], "personalAdvertisingInjury"],
1797
+ [["firedamage", "firedamagelegalliability", "damagetorentedpremises", "damagetopremisesrentedtoyou"], "fireDamage"],
1798
+ [["medicalexpense", "medexp", "medicalexpenseanypersonanyperson", "medicalexpenseanyone"], "medicalExpense"],
1799
+ // Auto
1800
+ [["combinedsingle", "combinedsinglelimit", "csl"], "combinedSingleLimit"],
1801
+ [["bodilyinjuryperperson", "biperperson"], "bodilyInjuryPerPerson"],
1802
+ [["bodilyinjuryperaccident", "biperaccident"], "bodilyInjuryPerAccident"],
1803
+ [["propertydamage", "pdperaccident"], "propertyDamage"],
1804
+ // Umbrella/Excess
1805
+ [["umbrellaoccurrence", "eachoccurrenceumbrella", "excessoccurrence", "excesseachoccurrence"], "eachOccurrenceUmbrella"],
1806
+ [["umbrellaaggregate", "excessaggregate"], "umbrellaAggregate"],
1807
+ [["umbrella retention", "selfinsuredretention", "sir", "excessretention"], "umbrellaRetention"]
1808
+ ];
1809
+ function synthesizeLimits(doc) {
1810
+ const raw = doc;
1811
+ if (raw.limits && typeof raw.limits === "object" && Object.keys(raw.limits).length > 0) return;
1812
+ const coverages = doc.coverages;
1813
+ if (!coverages || coverages.length === 0) return;
1814
+ const limits = {};
1815
+ for (const cov of coverages) {
1816
+ if (!cov.name || !cov.limit) continue;
1817
+ const normalized = normalizeName(cov.name);
1818
+ for (const [patterns, fieldName] of LIMIT_COVERAGE_MAP) {
1819
+ if (patterns.some((p) => normalized.includes(p) || p.includes(normalized))) {
1820
+ if (fieldName.includes("Aggregate") || fieldName.includes("aggregate")) {
1821
+ if (!cov.limitType || cov.limitType === "aggregate") {
1822
+ limits[fieldName] = cov.limit;
1823
+ }
1824
+ } else {
1825
+ if (!limits[fieldName]) {
1826
+ limits[fieldName] = cov.limit;
1827
+ }
1828
+ }
1829
+ break;
1830
+ }
1831
+ }
1832
+ }
1833
+ const hasStatutory = coverages.some(
1834
+ (c) => c.limitType === "statutory" || normalizeName(c.name ?? "").includes("statutory")
1835
+ );
1836
+ if (hasStatutory) {
1837
+ limits.statutory = "true";
1838
+ }
1839
+ const elCoverages = coverages.filter(
1840
+ (c) => normalizeName(c.name ?? "").includes("employersliability")
1841
+ );
1842
+ if (elCoverages.length > 0) {
1843
+ const el = {};
1844
+ for (const c of elCoverages) {
1845
+ if (!c.limit) continue;
1846
+ const n = normalizeName(c.name ?? "");
1847
+ if (n.includes("accident") || n.includes("eachaccident")) el.eachAccident = c.limit;
1848
+ else if (n.includes("diseasepolicy") || n.includes("diseasepolicylimit")) el.diseasePolicyLimit = c.limit;
1849
+ else if (n.includes("diseaseemployee") || n.includes("diseaseeachemployee")) el.diseaseEachEmployee = c.limit;
1850
+ else if (!el.eachAccident) el.eachAccident = c.limit;
1851
+ }
1852
+ if (Object.keys(el).length > 0) {
1853
+ limits.employersLiability = el;
1854
+ }
1855
+ }
1856
+ if (Object.keys(limits).length > 0) {
1857
+ const result = { ...limits };
1858
+ if (result.statutory === "true") result.statutory = true;
1859
+ raw.limits = result;
1860
+ }
1861
+ }
1862
+ function synthesizeDeductibles(doc) {
1863
+ const raw = doc;
1864
+ if (raw.deductibles && typeof raw.deductibles === "object" && Object.keys(raw.deductibles).length > 0) return;
1865
+ const coverages = doc.coverages;
1866
+ if (!coverages || coverages.length === 0) return;
1867
+ const deductibleValues = coverages.filter((c) => c.deductible && c.deductible.trim() !== "" && c.deductible !== "N/A" && c.deductible !== "None").map((c) => c.deductible);
1868
+ if (deductibleValues.length === 0) return;
1869
+ const freq = /* @__PURE__ */ new Map();
1870
+ for (const d of deductibleValues) {
1871
+ freq.set(d, (freq.get(d) ?? 0) + 1);
1872
+ }
1873
+ let mostCommon = deductibleValues[0];
1874
+ let maxFreq = 0;
1875
+ for (const [val, count] of freq) {
1876
+ if (count > maxFreq) {
1877
+ mostCommon = val;
1878
+ maxFreq = count;
1879
+ }
1880
+ }
1881
+ const deductibles = {};
1882
+ const hasPerClaim = coverages.some(
1883
+ (c) => c.deductible && normalizeName(c.name ?? "").includes("perclaim")
1884
+ );
1885
+ if (hasPerClaim) {
1886
+ deductibles.perClaim = mostCommon;
1887
+ } else {
1888
+ deductibles.perOccurrence = mostCommon;
1889
+ }
1890
+ const sirCoverage = coverages.find(
1891
+ (c) => c.deductible && (normalizeName(c.name ?? "").includes("selfinsuredretention") || normalizeName(c.name ?? "").includes("sir"))
1892
+ );
1893
+ if (sirCoverage?.deductible) {
1894
+ deductibles.selfInsuredRetention = sirCoverage.deductible;
1895
+ }
1896
+ const aggDed = coverages.find(
1897
+ (c) => c.deductible && normalizeName(c.name ?? "").includes("aggregatedeductible")
1898
+ );
1899
+ if (aggDed?.deductible) {
1900
+ deductibles.aggregateDeductible = aggDed.deductible;
1901
+ }
1902
+ if (Object.keys(deductibles).length > 0) {
1903
+ raw.deductibles = deductibles;
1904
+ }
1905
+ }
1906
+ var PREMIUM_PATTERNS = ["premium", "totalPremium", "annualPremium", "policyPremium", "basePremium"];
1907
+ var TOTAL_COST_PATTERNS = ["totalCost", "totalDue", "totalAmount", "totalPolicyPremium"];
1908
+ function promotePremium(doc) {
1909
+ const raw = doc;
1910
+ const fields = getDeclarationFields(doc);
1911
+ if (!raw.premium) {
1912
+ const premium = findFieldValue(fields, PREMIUM_PATTERNS);
1913
+ if (premium) raw.premium = premium;
1914
+ }
1915
+ if (!raw.totalCost) {
1916
+ const totalCost = findFieldValue(fields, TOTAL_COST_PATTERNS);
1917
+ if (totalCost) raw.totalCost = totalCost;
1918
+ }
1919
+ }
1920
+ function promoteExtractedFields(doc) {
1921
+ promoteCarrierFields(doc);
1922
+ promoteBroker(doc);
1923
+ promoteLossPayees(doc);
1924
+ promoteLocations(doc);
1925
+ synthesizeLimits(doc);
1926
+ synthesizeDeductibles(doc);
1927
+ promotePremium(doc);
1928
+ }
1929
+
1578
1930
  // src/extraction/assembler.ts
1579
1931
  function assembleDocument(documentId, documentType, memory) {
1580
1932
  const carrier = memory.get("carrier_info");
@@ -1598,6 +1950,9 @@ function assembleDocument(documentId, documentType, memory) {
1598
1950
  policyTypes: classify?.policyTypes,
1599
1951
  ...sanitizeNulls(carrier ?? {}),
1600
1952
  ...sanitizeNulls(insured ?? {}),
1953
+ // Map named_insured extractor's loss payees/mortgage holders to EndorsementParty shape
1954
+ ...Array.isArray(insured?.lossPayees) && insured.lossPayees.length > 0 ? { lossPayees: insured.lossPayees.map((lp) => ({ ...lp, role: "loss_payee" })) } : {},
1955
+ ...Array.isArray(insured?.mortgageHolders) && insured.mortgageHolders.length > 0 ? { mortgageHolders: insured.mortgageHolders.map((mh) => ({ ...mh, role: "mortgage_holder" })) } : {},
1601
1956
  ...sanitizeNulls(coverages ?? {}),
1602
1957
  ...sanitizeNulls(premium ?? {}),
1603
1958
  ...sanitizeNulls(supplementary ?? {}),
@@ -1609,8 +1964,9 @@ function assembleDocument(documentId, documentType, memory) {
1609
1964
  declarations: declarations ? sanitizeNulls(declarations) : void 0,
1610
1965
  ...sanitizeNulls(lossHistory ?? {})
1611
1966
  };
1967
+ let doc;
1612
1968
  if (documentType === "policy") {
1613
- return {
1969
+ doc = {
1614
1970
  ...base,
1615
1971
  type: "policy",
1616
1972
  policyNumber: carrier?.policyNumber ?? insured?.policyNumber ?? "Unknown",
@@ -1618,17 +1974,20 @@ function assembleDocument(documentId, documentType, memory) {
1618
1974
  expirationDate: carrier?.expirationDate,
1619
1975
  policyTermType: carrier?.policyTermType
1620
1976
  };
1977
+ } else {
1978
+ doc = {
1979
+ ...base,
1980
+ type: "quote",
1981
+ quoteNumber: carrier?.quoteNumber ?? "Unknown",
1982
+ proposedEffectiveDate: carrier?.proposedEffectiveDate,
1983
+ proposedExpirationDate: carrier?.proposedExpirationDate,
1984
+ subjectivities: coverages?.subjectivities,
1985
+ underwritingConditions: coverages?.underwritingConditions,
1986
+ premiumBreakdown: premium?.premiumBreakdown
1987
+ };
1621
1988
  }
1622
- return {
1623
- ...base,
1624
- type: "quote",
1625
- quoteNumber: carrier?.quoteNumber ?? "Unknown",
1626
- proposedEffectiveDate: carrier?.proposedEffectiveDate,
1627
- proposedExpirationDate: carrier?.proposedExpirationDate,
1628
- subjectivities: coverages?.subjectivities,
1629
- underwritingConditions: coverages?.underwritingConditions,
1630
- premiumBreakdown: premium?.premiumBreakdown
1631
- };
1989
+ promoteExtractedFields(doc);
1990
+ return doc;
1632
1991
  }
1633
1992
 
1634
1993
  // src/prompts/coordinator/format.ts
@@ -3062,21 +3421,69 @@ Use the page map to target follow-up extraction pages precisely. Prefer narrow,
3062
3421
  Respond with JSON only.`;
3063
3422
  }
3064
3423
 
3065
- // src/prompts/extractors/carrier-info.ts
3424
+ // src/prompts/coordinator/summarize.ts
3066
3425
  import { z as z22 } from "zod";
3067
- var CarrierInfoSchema = z22.object({
3068
- carrierName: z22.string().describe("Primary insurance company name for display"),
3069
- carrierLegalName: z22.string().optional().describe("Legal entity name of insurer"),
3070
- naicNumber: z22.string().optional().describe("NAIC company code"),
3071
- amBestRating: z22.string().optional().describe("AM Best rating, e.g. 'A+ XV'"),
3072
- admittedStatus: z22.enum(["admitted", "non_admitted", "surplus_lines"]).optional().describe("Admitted status of the carrier"),
3073
- mga: z22.string().optional().describe("Managing General Agent or Program Administrator name"),
3074
- underwriter: z22.string().optional().describe("Named individual underwriter"),
3075
- policyNumber: z22.string().optional().describe("Policy or quote reference number"),
3076
- effectiveDate: z22.string().optional().describe("Policy effective date (MM/DD/YYYY)"),
3077
- expirationDate: z22.string().optional().describe("Policy expiration date (MM/DD/YYYY)"),
3078
- quoteNumber: z22.string().optional().describe("Quote or proposal reference number"),
3079
- proposedEffectiveDate: z22.string().optional().describe("Proposed effective date for quotes (MM/DD/YYYY)")
3426
+ var SummaryResultSchema = z22.object({
3427
+ summary: z22.string().describe("A 1-3 sentence overview of this insurance document")
3428
+ });
3429
+ function buildSummaryPrompt(doc) {
3430
+ const snapshot = {
3431
+ type: doc.type,
3432
+ carrier: doc.carrier,
3433
+ insuredName: doc.insuredName,
3434
+ policyTypes: doc.policyTypes,
3435
+ premium: doc.premium,
3436
+ coverageCount: doc.coverages?.length ?? 0
3437
+ };
3438
+ if (doc.type === "policy") {
3439
+ snapshot.policyNumber = doc.policyNumber;
3440
+ snapshot.effectiveDate = doc.effectiveDate;
3441
+ snapshot.expirationDate = doc.expirationDate;
3442
+ } else {
3443
+ snapshot.quoteNumber = doc.quoteNumber;
3444
+ snapshot.proposedEffectiveDate = doc.proposedEffectiveDate;
3445
+ }
3446
+ const raw = doc;
3447
+ if (raw.limits) snapshot.limits = raw.limits;
3448
+ if (raw.deductibles) snapshot.deductibles = raw.deductibles;
3449
+ if (raw.brokerAgency) snapshot.brokerAgency = raw.brokerAgency;
3450
+ if (doc.endorsements?.length) snapshot.endorsementCount = doc.endorsements.length;
3451
+ if (doc.exclusions?.length) snapshot.exclusionCount = doc.exclusions.length;
3452
+ if (doc.coverages?.length) {
3453
+ snapshot.topCoverages = doc.coverages.slice(0, 5).map((c) => c.name);
3454
+ }
3455
+ return `You are an expert insurance document analyst. Generate a brief summary of this insurance document.
3456
+
3457
+ Write 1-3 sentences that capture the essential facts a broker or underwriter would want at a glance:
3458
+ - Who is insured and by whom
3459
+ - What type of policy/quote and the key coverages
3460
+ - Policy period and premium if available
3461
+ - Any notable features (high limits, unusual exclusions, etc.)
3462
+
3463
+ Document data:
3464
+ ${JSON.stringify(snapshot, null, 2)}
3465
+
3466
+ Return JSON only with a "summary" field.`;
3467
+ }
3468
+
3469
+ // src/prompts/extractors/carrier-info.ts
3470
+ import { z as z23 } from "zod";
3471
+ var CarrierInfoSchema = z23.object({
3472
+ carrierName: z23.string().describe("Primary insurance company name for display"),
3473
+ carrierLegalName: z23.string().optional().describe("Legal entity name of insurer"),
3474
+ naicNumber: z23.string().optional().describe("NAIC company code"),
3475
+ amBestRating: z23.string().optional().describe("AM Best rating, e.g. 'A+ XV'"),
3476
+ admittedStatus: z23.enum(["admitted", "non_admitted", "surplus_lines"]).optional().describe("Admitted status of the carrier"),
3477
+ mga: z23.string().optional().describe("Managing General Agent or Program Administrator name"),
3478
+ underwriter: z23.string().optional().describe("Named individual underwriter"),
3479
+ brokerAgency: z23.string().optional().describe("Broker or producer agency name"),
3480
+ brokerContactName: z23.string().optional().describe("Broker or producer contact person name"),
3481
+ brokerLicenseNumber: z23.string().optional().describe("Broker or producer license number"),
3482
+ policyNumber: z23.string().optional().describe("Policy or quote reference number"),
3483
+ effectiveDate: z23.string().optional().describe("Policy effective date (MM/DD/YYYY)"),
3484
+ expirationDate: z23.string().optional().describe("Policy expiration date (MM/DD/YYYY)"),
3485
+ quoteNumber: z23.string().optional().describe("Quote or proposal reference number"),
3486
+ proposedEffectiveDate: z23.string().optional().describe("Proposed effective date for quotes (MM/DD/YYYY)")
3080
3487
  });
3081
3488
  function buildCarrierInfoPrompt() {
3082
3489
  return `You are an expert insurance document analyst. Extract carrier and policy identification information from this document.
@@ -3087,27 +3494,30 @@ Focus on:
3087
3494
  - Whether the carrier is admitted, non-admitted, or surplus lines
3088
3495
  - Managing General Agent (MGA) or Program Administrator if applicable
3089
3496
  - Named individual underwriter if listed
3497
+ - Broker/producer/agent: agency name, contact person name, and license number
3090
3498
  - Policy number and effective/expiration dates
3091
3499
  - For quotes: quote number and proposed effective date
3092
3500
 
3093
3501
  For carrier vs. security distinction: "carrier" is the primary company name; the legal entity on risk (e.g. "Lloyd's Underwriters") may differ from the display name.
3094
3502
 
3503
+ Look for broker/producer/agent information near the carrier or on the declarations page. This may be labeled "Producer", "Agent", "Broker", or similar.
3504
+
3095
3505
  Return JSON only.`;
3096
3506
  }
3097
3507
 
3098
3508
  // src/prompts/extractors/named-insured.ts
3099
- import { z as z23 } from "zod";
3100
- var AddressSchema2 = z23.object({
3101
- street1: z23.string(),
3102
- city: z23.string(),
3103
- state: z23.string(),
3104
- zip: z23.string()
3509
+ import { z as z24 } from "zod";
3510
+ var AddressSchema2 = z24.object({
3511
+ street1: z24.string(),
3512
+ city: z24.string(),
3513
+ state: z24.string(),
3514
+ zip: z24.string()
3105
3515
  });
3106
- var NamedInsuredSchema2 = z23.object({
3107
- insuredName: z23.string().describe("Name of primary named insured"),
3108
- insuredDba: z23.string().optional().describe("Doing-business-as name"),
3516
+ var NamedInsuredSchema2 = z24.object({
3517
+ insuredName: z24.string().describe("Name of primary named insured"),
3518
+ insuredDba: z24.string().optional().describe("Doing-business-as name"),
3109
3519
  insuredAddress: AddressSchema2.optional().describe("Primary insured mailing address"),
3110
- insuredEntityType: z23.enum([
3520
+ insuredEntityType: z24.enum([
3111
3521
  "corporation",
3112
3522
  "llc",
3113
3523
  "partnership",
@@ -3120,16 +3530,28 @@ var NamedInsuredSchema2 = z23.object({
3120
3530
  "married_couple",
3121
3531
  "other"
3122
3532
  ]).optional().describe("Legal entity type of the insured"),
3123
- insuredFein: z23.string().optional().describe("Federal Employer Identification Number"),
3124
- insuredSicCode: z23.string().optional().describe("SIC code"),
3125
- insuredNaicsCode: z23.string().optional().describe("NAICS code"),
3126
- additionalNamedInsureds: z23.array(
3127
- z23.object({
3128
- name: z23.string(),
3129
- relationship: z23.string().optional().describe("e.g. subsidiary, affiliate"),
3533
+ insuredFein: z24.string().optional().describe("Federal Employer Identification Number"),
3534
+ insuredSicCode: z24.string().optional().describe("SIC code"),
3535
+ insuredNaicsCode: z24.string().optional().describe("NAICS code"),
3536
+ additionalNamedInsureds: z24.array(
3537
+ z24.object({
3538
+ name: z24.string(),
3539
+ relationship: z24.string().optional().describe("e.g. subsidiary, affiliate"),
3540
+ address: AddressSchema2.optional()
3541
+ })
3542
+ ).optional().describe("Additional named insureds listed on the policy"),
3543
+ lossPayees: z24.array(
3544
+ z24.object({
3545
+ name: z24.string(),
3546
+ address: AddressSchema2.optional()
3547
+ })
3548
+ ).optional().describe("Loss payees listed on the policy"),
3549
+ mortgageHolders: z24.array(
3550
+ z24.object({
3551
+ name: z24.string(),
3130
3552
  address: AddressSchema2.optional()
3131
3553
  })
3132
- ).optional().describe("Additional named insureds listed on the policy")
3554
+ ).optional().describe("Mortgage holders / lienholders listed on the policy")
3133
3555
  });
3134
3556
  function buildNamedInsuredPrompt() {
3135
3557
  return `You are an expert insurance document analyst. Extract all named insured information from this document.
@@ -3140,21 +3562,23 @@ Focus on:
3140
3562
  - FEIN (Federal Employer Identification Number) if listed
3141
3563
  - SIC code and NAICS code if listed
3142
3564
  - ALL additional named insureds with their relationship (subsidiary, affiliate, etc.) and address if provided
3565
+ - ALL loss payees with name and address (e.g. "Loss Payee: BMO Bank of Montreal")
3566
+ - ALL mortgage holders / lienholders / mortgagees with name and address
3143
3567
 
3144
- Look on the declarations page, named insured schedule, and any endorsements that add or modify named insureds.
3568
+ Look on the declarations page, named insured schedule, loss payee schedule, mortgagee schedule, and any endorsements that add or modify named insureds, loss payees, or mortgage holders.
3145
3569
 
3146
3570
  Return JSON only.`;
3147
3571
  }
3148
3572
 
3149
3573
  // src/prompts/extractors/coverage-limits.ts
3150
- import { z as z24 } from "zod";
3574
+ import { z as z25 } from "zod";
3151
3575
  var ExtractorCoverageSchema = CoverageSchema.extend({
3152
- coverageCode: z24.string().optional().describe("Coverage code or class code")
3576
+ coverageCode: z25.string().optional().describe("Coverage code or class code")
3153
3577
  });
3154
- var CoverageLimitsSchema = z24.object({
3155
- coverages: z24.array(ExtractorCoverageSchema).describe("All coverages with their limits"),
3156
- coverageForm: z24.enum(["occurrence", "claims_made", "accident"]).optional().describe("Primary coverage trigger type"),
3157
- retroactiveDate: z24.string().optional().describe("Retroactive date for claims-made policies (MM/DD/YYYY)")
3578
+ var CoverageLimitsSchema = z25.object({
3579
+ coverages: z25.array(ExtractorCoverageSchema).describe("All coverages with their limits"),
3580
+ coverageForm: z25.enum(["occurrence", "claims_made", "accident"]).optional().describe("Primary coverage trigger type"),
3581
+ retroactiveDate: z25.string().optional().describe("Retroactive date for claims-made policies (MM/DD/YYYY)")
3158
3582
  });
3159
3583
  function buildCoverageLimitsPrompt() {
3160
3584
  return `You are an expert insurance document analyst. Extract all coverage limits and deductibles from this document.
@@ -3193,14 +3617,14 @@ Return JSON only.`;
3193
3617
  }
3194
3618
 
3195
3619
  // src/prompts/extractors/endorsements.ts
3196
- import { z as z25 } from "zod";
3197
- var EndorsementsSchema = z25.object({
3198
- endorsements: z25.array(
3199
- z25.object({
3200
- formNumber: z25.string().describe("Form number, e.g. 'CG 21 47'"),
3201
- editionDate: z25.string().optional().describe("Edition date, e.g. '12 07'"),
3202
- title: z25.string().describe("Endorsement title"),
3203
- endorsementType: z25.enum([
3620
+ import { z as z26 } from "zod";
3621
+ var EndorsementsSchema = z26.object({
3622
+ endorsements: z26.array(
3623
+ z26.object({
3624
+ formNumber: z26.string().describe("Form number, e.g. 'CG 21 47'"),
3625
+ editionDate: z26.string().optional().describe("Edition date, e.g. '12 07'"),
3626
+ title: z26.string().describe("Endorsement title"),
3627
+ endorsementType: z26.enum([
3204
3628
  "additional_insured",
3205
3629
  "waiver_of_subrogation",
3206
3630
  "primary_noncontributory",
@@ -3220,12 +3644,12 @@ var EndorsementsSchema = z25.object({
3220
3644
  "territorial_extension",
3221
3645
  "other"
3222
3646
  ]).describe("Endorsement type classification"),
3223
- effectiveDate: z25.string().optional().describe("Endorsement effective date"),
3224
- affectedCoverageParts: z25.array(z25.string()).optional().describe("Coverage parts affected by this endorsement"),
3225
- namedParties: z25.array(
3226
- z25.object({
3227
- name: z25.string().describe("Party name"),
3228
- role: z25.enum([
3647
+ effectiveDate: z26.string().optional().describe("Endorsement effective date"),
3648
+ affectedCoverageParts: z26.array(z26.string()).optional().describe("Coverage parts affected by this endorsement"),
3649
+ namedParties: z26.array(
3650
+ z26.object({
3651
+ name: z26.string().describe("Party name"),
3652
+ role: z26.enum([
3229
3653
  "additional_insured",
3230
3654
  "loss_payee",
3231
3655
  "mortgage_holder",
@@ -3234,15 +3658,15 @@ var EndorsementsSchema = z25.object({
3234
3658
  "designated_person",
3235
3659
  "other"
3236
3660
  ]).describe("Party role"),
3237
- relationship: z25.string().optional().describe("Relationship to insured"),
3238
- scope: z25.string().optional().describe("Scope of coverage for this party")
3661
+ relationship: z26.string().optional().describe("Relationship to insured"),
3662
+ scope: z26.string().optional().describe("Scope of coverage for this party")
3239
3663
  })
3240
3664
  ).optional().describe("Named parties (additional insureds, loss payees, etc.)"),
3241
- keyTerms: z25.array(z25.string()).optional().describe("Key terms or notable provisions in the endorsement"),
3242
- premiumImpact: z25.string().optional().describe("Additional premium or credit"),
3243
- content: z25.string().describe("Full verbatim text of the endorsement"),
3244
- pageStart: z25.number().describe("Starting page number of this endorsement"),
3245
- pageEnd: z25.number().optional().describe("Ending page number of this endorsement")
3665
+ keyTerms: z26.array(z26.string()).optional().describe("Key terms or notable provisions in the endorsement"),
3666
+ premiumImpact: z26.string().optional().describe("Additional premium or credit"),
3667
+ content: z26.string().describe("Full verbatim text of the endorsement"),
3668
+ pageStart: z26.number().describe("Starting page number of this endorsement"),
3669
+ pageEnd: z26.number().optional().describe("Ending page number of this endorsement")
3246
3670
  })
3247
3671
  ).describe("All endorsements found in the document")
3248
3672
  });
@@ -3273,20 +3697,20 @@ Return JSON only.`;
3273
3697
  }
3274
3698
 
3275
3699
  // src/prompts/extractors/exclusions.ts
3276
- import { z as z26 } from "zod";
3277
- var ExclusionsSchema = z26.object({
3278
- exclusions: z26.array(
3279
- z26.object({
3280
- name: z26.string().describe("Exclusion title or short description"),
3281
- formNumber: z26.string().optional().describe("Form number if part of a named endorsement"),
3282
- excludedPerils: z26.array(z26.string()).optional().describe("Specific perils excluded"),
3283
- isAbsolute: z26.boolean().optional().describe("Whether the exclusion is absolute (no exceptions)"),
3284
- exceptions: z26.array(z26.string()).optional().describe("Exceptions to the exclusion, if any"),
3285
- buybackAvailable: z26.boolean().optional().describe("Whether coverage can be bought back via endorsement"),
3286
- buybackEndorsement: z26.string().optional().describe("Form number of the buyback endorsement if available"),
3287
- appliesTo: z26.array(z26.string()).optional().describe("Coverage types this exclusion applies to"),
3288
- content: z26.string().describe("Full verbatim exclusion text"),
3289
- pageNumber: z26.number().optional().describe("Page number where exclusion appears")
3700
+ import { z as z27 } from "zod";
3701
+ var ExclusionsSchema = z27.object({
3702
+ exclusions: z27.array(
3703
+ z27.object({
3704
+ name: z27.string().describe("Exclusion title or short description"),
3705
+ formNumber: z27.string().optional().describe("Form number if part of a named endorsement"),
3706
+ excludedPerils: z27.array(z27.string()).optional().describe("Specific perils excluded"),
3707
+ isAbsolute: z27.boolean().optional().describe("Whether the exclusion is absolute (no exceptions)"),
3708
+ exceptions: z27.array(z27.string()).optional().describe("Exceptions to the exclusion, if any"),
3709
+ buybackAvailable: z27.boolean().optional().describe("Whether coverage can be bought back via endorsement"),
3710
+ buybackEndorsement: z27.string().optional().describe("Form number of the buyback endorsement if available"),
3711
+ appliesTo: z27.array(z27.string()).optional().describe("Coverage types this exclusion applies to"),
3712
+ content: z27.string().describe("Full verbatim exclusion text"),
3713
+ pageNumber: z27.number().optional().describe("Page number where exclusion appears")
3290
3714
  })
3291
3715
  ).describe("All exclusions found in the document")
3292
3716
  });
@@ -3322,12 +3746,12 @@ Return JSON only.`;
3322
3746
  }
3323
3747
 
3324
3748
  // src/prompts/extractors/conditions.ts
3325
- import { z as z27 } from "zod";
3326
- var ConditionsSchema = z27.object({
3327
- conditions: z27.array(
3328
- z27.object({
3329
- name: z27.string().describe("Condition title"),
3330
- conditionType: z27.enum([
3749
+ import { z as z28 } from "zod";
3750
+ var ConditionsSchema = z28.object({
3751
+ conditions: z28.array(
3752
+ z28.object({
3753
+ name: z28.string().describe("Condition title"),
3754
+ conditionType: z28.enum([
3331
3755
  "duties_after_loss",
3332
3756
  "notice_requirements",
3333
3757
  "other_insurance",
@@ -3346,14 +3770,14 @@ var ConditionsSchema = z27.object({
3346
3770
  "separation_of_insureds",
3347
3771
  "other"
3348
3772
  ]).describe("Condition category"),
3349
- content: z27.string().describe("Full verbatim condition text"),
3350
- keyValues: z27.array(
3351
- z27.object({
3352
- key: z27.string().describe("Key name (e.g. 'noticePeriod', 'suitDeadline')"),
3353
- value: z27.string().describe("Value (e.g. '30 days', '2 years')")
3773
+ content: z28.string().describe("Full verbatim condition text"),
3774
+ keyValues: z28.array(
3775
+ z28.object({
3776
+ key: z28.string().describe("Key name (e.g. 'noticePeriod', 'suitDeadline')"),
3777
+ value: z28.string().describe("Value (e.g. '30 days', '2 years')")
3354
3778
  })
3355
3779
  ).optional().describe("Key values extracted from the condition (notice periods, deadlines, etc.)"),
3356
- pageNumber: z27.number().optional().describe("Page number where condition appears")
3780
+ pageNumber: z28.number().optional().describe("Page number where condition appears")
3357
3781
  })
3358
3782
  ).describe("All policy conditions found in the document")
3359
3783
  });
@@ -3391,28 +3815,28 @@ Return JSON only.`;
3391
3815
  }
3392
3816
 
3393
3817
  // src/prompts/extractors/premium-breakdown.ts
3394
- import { z as z28 } from "zod";
3395
- var PremiumBreakdownSchema = z28.object({
3396
- premium: z28.string().optional().describe("Total premium amount, e.g. '$5,000'"),
3397
- totalCost: z28.string().optional().describe("Total cost including taxes and fees, e.g. '$5,250'"),
3398
- premiumBreakdown: z28.array(
3399
- z28.object({
3400
- line: z28.string().describe("Coverage line name"),
3401
- amount: z28.string().describe("Premium amount for this line")
3818
+ import { z as z29 } from "zod";
3819
+ var PremiumBreakdownSchema = z29.object({
3820
+ premium: z29.string().optional().describe("Total premium amount, e.g. '$5,000'"),
3821
+ totalCost: z29.string().optional().describe("Total cost including taxes and fees, e.g. '$5,250'"),
3822
+ premiumBreakdown: z29.array(
3823
+ z29.object({
3824
+ line: z29.string().describe("Coverage line name"),
3825
+ amount: z29.string().describe("Premium amount for this line")
3402
3826
  })
3403
3827
  ).optional().describe("Per-coverage-line premium breakdown"),
3404
- taxesAndFees: z28.array(
3405
- z28.object({
3406
- name: z28.string().describe("Fee or tax name"),
3407
- amount: z28.string().describe("Dollar amount"),
3408
- type: z28.enum(["tax", "fee", "surcharge", "assessment"]).optional().describe("Fee category")
3828
+ taxesAndFees: z29.array(
3829
+ z29.object({
3830
+ name: z29.string().describe("Fee or tax name"),
3831
+ amount: z29.string().describe("Dollar amount"),
3832
+ type: z29.enum(["tax", "fee", "surcharge", "assessment"]).optional().describe("Fee category")
3409
3833
  })
3410
3834
  ).optional().describe("Taxes, fees, surcharges, and assessments"),
3411
- minimumPremium: z28.string().optional().describe("Minimum premium if stated"),
3412
- depositPremium: z28.string().optional().describe("Deposit premium if stated"),
3413
- paymentPlan: z28.string().optional().describe("Payment plan description"),
3414
- auditType: z28.enum(["annual", "semi_annual", "quarterly", "monthly", "final", "self"]).optional().describe("Premium audit type"),
3415
- ratingBasis: z28.string().optional().describe("Rating basis, e.g. payroll, revenue, area, units")
3835
+ minimumPremium: z29.string().optional().describe("Minimum premium if stated"),
3836
+ depositPremium: z29.string().optional().describe("Deposit premium if stated"),
3837
+ paymentPlan: z29.string().optional().describe("Payment plan description"),
3838
+ auditType: z29.enum(["annual", "semi_annual", "quarterly", "monthly", "final", "self"]).optional().describe("Premium audit type"),
3839
+ ratingBasis: z29.string().optional().describe("Rating basis, e.g. payroll, revenue, area, units")
3416
3840
  });
3417
3841
  function buildPremiumBreakdownPrompt() {
3418
3842
  return `You are an expert insurance document analyst. Extract all premium and cost information from this document.
@@ -3432,14 +3856,14 @@ Return JSON only.`;
3432
3856
  }
3433
3857
 
3434
3858
  // src/prompts/extractors/declarations.ts
3435
- import { z as z29 } from "zod";
3436
- var DeclarationsFieldSchema = z29.object({
3437
- field: z29.string().describe("Descriptive field name (e.g. 'policyNumber', 'effectiveDate', 'coverageALimit')"),
3438
- value: z29.string().describe("Extracted value exactly as it appears in the document"),
3439
- section: z29.string().optional().describe("Section or grouping this field belongs to (e.g. 'Coverage Limits', 'Vehicle Schedule')")
3859
+ import { z as z30 } from "zod";
3860
+ var DeclarationsFieldSchema = z30.object({
3861
+ field: z30.string().describe("Descriptive field name (e.g. 'policyNumber', 'effectiveDate', 'coverageALimit')"),
3862
+ value: z30.string().describe("Extracted value exactly as it appears in the document"),
3863
+ section: z30.string().optional().describe("Section or grouping this field belongs to (e.g. 'Coverage Limits', 'Vehicle Schedule')")
3440
3864
  });
3441
- var DeclarationsExtractSchema = z29.object({
3442
- fields: z29.array(DeclarationsFieldSchema).describe("All declarations page fields extracted as key-value pairs. Structure varies by line of business.")
3865
+ var DeclarationsExtractSchema = z30.object({
3866
+ fields: z30.array(DeclarationsFieldSchema).describe("All declarations page fields extracted as key-value pairs. Structure varies by line of business.")
3443
3867
  });
3444
3868
  function buildDeclarationsPrompt() {
3445
3869
  return `You are an expert insurance document analyst. Extract all declarations page data from this document into a flexible key-value structure.
@@ -3479,21 +3903,21 @@ Preserve original values exactly as they appear. Return JSON only.`;
3479
3903
  }
3480
3904
 
3481
3905
  // src/prompts/extractors/loss-history.ts
3482
- import { z as z30 } from "zod";
3483
- var LossHistorySchema = z30.object({
3484
- lossSummary: z30.string().optional().describe("Summary of loss history, e.g. '3 claims in past 5 years totaling $125,000'"),
3485
- individualClaims: z30.array(
3486
- z30.object({
3487
- date: z30.string().optional().describe("Date of loss or claim"),
3488
- type: z30.string().optional().describe("Type of claim, e.g. 'property damage', 'bodily injury'"),
3489
- description: z30.string().optional().describe("Brief description of the claim"),
3490
- amountPaid: z30.string().optional().describe("Amount paid"),
3491
- amountReserved: z30.string().optional().describe("Amount reserved"),
3492
- status: z30.enum(["open", "closed", "reopened"]).optional().describe("Claim status"),
3493
- claimNumber: z30.string().optional().describe("Claim reference number")
3906
+ import { z as z31 } from "zod";
3907
+ var LossHistorySchema = z31.object({
3908
+ lossSummary: z31.string().optional().describe("Summary of loss history, e.g. '3 claims in past 5 years totaling $125,000'"),
3909
+ individualClaims: z31.array(
3910
+ z31.object({
3911
+ date: z31.string().optional().describe("Date of loss or claim"),
3912
+ type: z31.string().optional().describe("Type of claim, e.g. 'property damage', 'bodily injury'"),
3913
+ description: z31.string().optional().describe("Brief description of the claim"),
3914
+ amountPaid: z31.string().optional().describe("Amount paid"),
3915
+ amountReserved: z31.string().optional().describe("Amount reserved"),
3916
+ status: z31.enum(["open", "closed", "reopened"]).optional().describe("Claim status"),
3917
+ claimNumber: z31.string().optional().describe("Claim reference number")
3494
3918
  })
3495
3919
  ).optional().describe("Individual claim records"),
3496
- experienceMod: z30.string().optional().describe("Experience modification factor for workers comp, e.g. '0.85'")
3920
+ experienceMod: z31.string().optional().describe("Experience modification factor for workers comp, e.g. '0.85'")
3497
3921
  });
3498
3922
  function buildLossHistoryPrompt() {
3499
3923
  return `You are an expert insurance document analyst. Extract all loss history and claims information from this document.
@@ -3510,18 +3934,18 @@ Return JSON only.`;
3510
3934
  }
3511
3935
 
3512
3936
  // src/prompts/extractors/sections.ts
3513
- import { z as z31 } from "zod";
3514
- var SubsectionSchema2 = z31.object({
3515
- title: z31.string().describe("Subsection title"),
3516
- sectionNumber: z31.string().optional().describe("Subsection number"),
3517
- pageNumber: z31.number().optional().describe("Page number"),
3518
- content: z31.string().describe("Full verbatim text")
3937
+ import { z as z32 } from "zod";
3938
+ var SubsectionSchema2 = z32.object({
3939
+ title: z32.string().describe("Subsection title"),
3940
+ sectionNumber: z32.string().optional().describe("Subsection number"),
3941
+ pageNumber: z32.number().optional().describe("Page number"),
3942
+ content: z32.string().describe("Full verbatim text")
3519
3943
  });
3520
- var SectionsSchema = z31.object({
3521
- sections: z31.array(
3522
- z31.object({
3523
- title: z31.string().describe("Section title"),
3524
- type: z31.enum([
3944
+ var SectionsSchema = z32.object({
3945
+ sections: z32.array(
3946
+ z32.object({
3947
+ title: z32.string().describe("Section title"),
3948
+ type: z32.enum([
3525
3949
  "declarations",
3526
3950
  "insuring_agreement",
3527
3951
  "policy_form",
@@ -3535,10 +3959,10 @@ var SectionsSchema = z31.object({
3535
3959
  "regulatory",
3536
3960
  "other"
3537
3961
  ]).describe("Section type classification"),
3538
- content: z31.string().describe("Full verbatim text of the section"),
3539
- pageStart: z31.number().describe("Starting page number"),
3540
- pageEnd: z31.number().optional().describe("Ending page number"),
3541
- subsections: z31.array(SubsectionSchema2).optional().describe("Subsections within this section")
3962
+ content: z32.string().describe("Full verbatim text of the section"),
3963
+ pageStart: z32.number().describe("Starting page number"),
3964
+ pageEnd: z32.number().optional().describe("Ending page number"),
3965
+ subsections: z32.array(SubsectionSchema2).optional().describe("Subsections within this section")
3542
3966
  })
3543
3967
  ).describe("All document sections")
3544
3968
  });
@@ -3568,20 +3992,20 @@ Return JSON only.`;
3568
3992
  }
3569
3993
 
3570
3994
  // src/prompts/extractors/supplementary.ts
3571
- import { z as z32 } from "zod";
3572
- var ContactSchema2 = z32.object({
3573
- name: z32.string().optional().describe("Organization or person name"),
3574
- phone: z32.string().optional().describe("Phone number"),
3575
- email: z32.string().optional().describe("Email address"),
3576
- address: z32.string().optional().describe("Mailing address"),
3577
- type: z32.string().optional().describe("Contact type, e.g. 'State Department of Insurance'")
3995
+ import { z as z33 } from "zod";
3996
+ var ContactSchema2 = z33.object({
3997
+ name: z33.string().optional().describe("Organization or person name"),
3998
+ phone: z33.string().optional().describe("Phone number"),
3999
+ email: z33.string().optional().describe("Email address"),
4000
+ address: z33.string().optional().describe("Mailing address"),
4001
+ type: z33.string().optional().describe("Contact type, e.g. 'State Department of Insurance'")
3578
4002
  });
3579
- var SupplementarySchema = z32.object({
3580
- regulatoryContacts: z32.array(ContactSchema2).optional().describe("Regulatory body contacts (state department of insurance, ombudsman)"),
3581
- claimsContacts: z32.array(ContactSchema2).optional().describe("Claims reporting contacts and instructions"),
3582
- thirdPartyAdministrators: z32.array(ContactSchema2).optional().describe("Third-party administrators for claims handling"),
3583
- cancellationNoticeDays: z32.number().optional().describe("Required notice period for cancellation in days"),
3584
- nonrenewalNoticeDays: z32.number().optional().describe("Required notice period for nonrenewal in days")
4003
+ var SupplementarySchema = z33.object({
4004
+ regulatoryContacts: z33.array(ContactSchema2).optional().describe("Regulatory body contacts (state department of insurance, ombudsman)"),
4005
+ claimsContacts: z33.array(ContactSchema2).optional().describe("Claims reporting contacts and instructions"),
4006
+ thirdPartyAdministrators: z33.array(ContactSchema2).optional().describe("Third-party administrators for claims handling"),
4007
+ cancellationNoticeDays: z33.number().optional().describe("Required notice period for cancellation in days"),
4008
+ nonrenewalNoticeDays: z33.number().optional().describe("Required notice period for nonrenewal in days")
3585
4009
  });
3586
4010
  function buildSupplementaryPrompt() {
3587
4011
  return `You are an expert insurance document analyst. Extract supplementary and regulatory information from this document.
@@ -4440,6 +4864,31 @@ function createExtractor(config) {
4440
4864
  memory: Object.fromEntries(memory),
4441
4865
  document
4442
4866
  });
4867
+ if (!document.summary) {
4868
+ onProgress?.("Generating document summary...");
4869
+ try {
4870
+ const summaryResponse = await safeGenerateObject(
4871
+ generateObject,
4872
+ {
4873
+ prompt: buildSummaryPrompt(document),
4874
+ schema: SummaryResultSchema,
4875
+ maxTokens: 512,
4876
+ providerOptions
4877
+ },
4878
+ {
4879
+ fallback: { summary: "" },
4880
+ log,
4881
+ onError: (err, attempt) => log?.(`Summary attempt ${attempt + 1} failed: ${err instanceof Error ? err.message : String(err)}`)
4882
+ }
4883
+ );
4884
+ trackUsage(summaryResponse.usage);
4885
+ if (summaryResponse.object.summary) {
4886
+ document.summary = summaryResponse.object.summary;
4887
+ }
4888
+ } catch (error) {
4889
+ await log?.(`Summary generation failed, skipping: ${error instanceof Error ? error.message : String(error)}`);
4890
+ }
4891
+ }
4443
4892
  onProgress?.("Formatting extracted content...");
4444
4893
  const formatResult = await formatDocumentContent(document, generateText, {
4445
4894
  providerOptions,
@@ -4683,8 +5132,8 @@ Respond with JSON only:
4683
5132
  }`;
4684
5133
 
4685
5134
  // src/schemas/application.ts
4686
- import { z as z33 } from "zod";
4687
- var FieldTypeSchema = z33.enum([
5135
+ import { z as z34 } from "zod";
5136
+ var FieldTypeSchema = z34.enum([
4688
5137
  "text",
4689
5138
  "numeric",
4690
5139
  "currency",
@@ -4693,131 +5142,131 @@ var FieldTypeSchema = z33.enum([
4693
5142
  "table",
4694
5143
  "declaration"
4695
5144
  ]);
4696
- var ApplicationFieldSchema = z33.object({
4697
- id: z33.string(),
4698
- label: z33.string(),
4699
- section: z33.string(),
5145
+ var ApplicationFieldSchema = z34.object({
5146
+ id: z34.string(),
5147
+ label: z34.string(),
5148
+ section: z34.string(),
4700
5149
  fieldType: FieldTypeSchema,
4701
- required: z33.boolean(),
4702
- options: z33.array(z33.string()).optional(),
4703
- columns: z33.array(z33.string()).optional(),
4704
- requiresExplanationIfYes: z33.boolean().optional(),
4705
- condition: z33.object({
4706
- dependsOn: z33.string(),
4707
- whenValue: z33.string()
5150
+ required: z34.boolean(),
5151
+ options: z34.array(z34.string()).optional(),
5152
+ columns: z34.array(z34.string()).optional(),
5153
+ requiresExplanationIfYes: z34.boolean().optional(),
5154
+ condition: z34.object({
5155
+ dependsOn: z34.string(),
5156
+ whenValue: z34.string()
4708
5157
  }).optional(),
4709
- value: z33.string().optional(),
4710
- source: z33.string().optional().describe("Where the value came from: auto-fill, user, lookup"),
4711
- confidence: z33.enum(["confirmed", "high", "medium", "low"]).optional()
5158
+ value: z34.string().optional(),
5159
+ source: z34.string().optional().describe("Where the value came from: auto-fill, user, lookup"),
5160
+ confidence: z34.enum(["confirmed", "high", "medium", "low"]).optional()
4712
5161
  });
4713
- var ApplicationClassifyResultSchema = z33.object({
4714
- isApplication: z33.boolean(),
4715
- confidence: z33.number().min(0).max(1),
4716
- applicationType: z33.string().nullable()
5162
+ var ApplicationClassifyResultSchema = z34.object({
5163
+ isApplication: z34.boolean(),
5164
+ confidence: z34.number().min(0).max(1),
5165
+ applicationType: z34.string().nullable()
4717
5166
  });
4718
- var FieldExtractionResultSchema = z33.object({
4719
- fields: z33.array(ApplicationFieldSchema)
5167
+ var FieldExtractionResultSchema = z34.object({
5168
+ fields: z34.array(ApplicationFieldSchema)
4720
5169
  });
4721
- var AutoFillMatchSchema = z33.object({
4722
- fieldId: z33.string(),
4723
- value: z33.string(),
4724
- confidence: z33.enum(["confirmed"]),
4725
- contextKey: z33.string()
5170
+ var AutoFillMatchSchema = z34.object({
5171
+ fieldId: z34.string(),
5172
+ value: z34.string(),
5173
+ confidence: z34.enum(["confirmed"]),
5174
+ contextKey: z34.string()
4726
5175
  });
4727
- var AutoFillResultSchema = z33.object({
4728
- matches: z33.array(AutoFillMatchSchema)
5176
+ var AutoFillResultSchema = z34.object({
5177
+ matches: z34.array(AutoFillMatchSchema)
4729
5178
  });
4730
- var QuestionBatchResultSchema = z33.object({
4731
- batches: z33.array(z33.array(z33.string()).describe("Array of field IDs in this batch"))
5179
+ var QuestionBatchResultSchema = z34.object({
5180
+ batches: z34.array(z34.array(z34.string()).describe("Array of field IDs in this batch"))
4732
5181
  });
4733
- var LookupRequestSchema = z33.object({
4734
- type: z33.string().describe("Type of lookup: 'records', 'website', 'policy'"),
4735
- description: z33.string(),
4736
- url: z33.string().optional(),
4737
- targetFieldIds: z33.array(z33.string())
5182
+ var LookupRequestSchema = z34.object({
5183
+ type: z34.string().describe("Type of lookup: 'records', 'website', 'policy'"),
5184
+ description: z34.string(),
5185
+ url: z34.string().optional(),
5186
+ targetFieldIds: z34.array(z34.string())
4738
5187
  });
4739
- var ReplyIntentSchema = z33.object({
4740
- primaryIntent: z33.enum(["answers_only", "question", "lookup_request", "mixed"]),
4741
- hasAnswers: z33.boolean(),
4742
- questionText: z33.string().optional(),
4743
- questionFieldIds: z33.array(z33.string()).optional(),
4744
- lookupRequests: z33.array(LookupRequestSchema).optional()
5188
+ var ReplyIntentSchema = z34.object({
5189
+ primaryIntent: z34.enum(["answers_only", "question", "lookup_request", "mixed"]),
5190
+ hasAnswers: z34.boolean(),
5191
+ questionText: z34.string().optional(),
5192
+ questionFieldIds: z34.array(z34.string()).optional(),
5193
+ lookupRequests: z34.array(LookupRequestSchema).optional()
4745
5194
  });
4746
- var ParsedAnswerSchema = z33.object({
4747
- fieldId: z33.string(),
4748
- value: z33.string(),
4749
- explanation: z33.string().optional()
5195
+ var ParsedAnswerSchema = z34.object({
5196
+ fieldId: z34.string(),
5197
+ value: z34.string(),
5198
+ explanation: z34.string().optional()
4750
5199
  });
4751
- var AnswerParsingResultSchema = z33.object({
4752
- answers: z33.array(ParsedAnswerSchema),
4753
- unanswered: z33.array(z33.string()).describe("Field IDs that were not answered")
5200
+ var AnswerParsingResultSchema = z34.object({
5201
+ answers: z34.array(ParsedAnswerSchema),
5202
+ unanswered: z34.array(z34.string()).describe("Field IDs that were not answered")
4754
5203
  });
4755
- var LookupFillSchema = z33.object({
4756
- fieldId: z33.string(),
4757
- value: z33.string(),
4758
- source: z33.string().describe("Specific citable reference, e.g. 'GL Policy #POL-12345 (Hartford)'")
5204
+ var LookupFillSchema = z34.object({
5205
+ fieldId: z34.string(),
5206
+ value: z34.string(),
5207
+ source: z34.string().describe("Specific citable reference, e.g. 'GL Policy #POL-12345 (Hartford)'")
4759
5208
  });
4760
- var LookupFillResultSchema = z33.object({
4761
- fills: z33.array(LookupFillSchema),
4762
- unfillable: z33.array(z33.string()),
4763
- explanation: z33.string().optional()
5209
+ var LookupFillResultSchema = z34.object({
5210
+ fills: z34.array(LookupFillSchema),
5211
+ unfillable: z34.array(z34.string()),
5212
+ explanation: z34.string().optional()
4764
5213
  });
4765
- var FlatPdfPlacementSchema = z33.object({
4766
- fieldId: z33.string(),
4767
- page: z33.number(),
4768
- x: z33.number().describe("Percentage from left edge (0-100)"),
4769
- y: z33.number().describe("Percentage from top edge (0-100)"),
4770
- text: z33.string(),
4771
- fontSize: z33.number().optional(),
4772
- isCheckmark: z33.boolean().optional()
5214
+ var FlatPdfPlacementSchema = z34.object({
5215
+ fieldId: z34.string(),
5216
+ page: z34.number(),
5217
+ x: z34.number().describe("Percentage from left edge (0-100)"),
5218
+ y: z34.number().describe("Percentage from top edge (0-100)"),
5219
+ text: z34.string(),
5220
+ fontSize: z34.number().optional(),
5221
+ isCheckmark: z34.boolean().optional()
4773
5222
  });
4774
- var AcroFormMappingSchema = z33.object({
4775
- fieldId: z33.string(),
4776
- acroFormName: z33.string(),
4777
- value: z33.string()
5223
+ var AcroFormMappingSchema = z34.object({
5224
+ fieldId: z34.string(),
5225
+ acroFormName: z34.string(),
5226
+ value: z34.string()
4778
5227
  });
4779
- var QualityGateStatusSchema = z33.enum(["passed", "warning", "failed"]);
4780
- var QualitySeveritySchema = z33.enum(["info", "warning", "blocking"]);
4781
- var ApplicationQualityIssueSchema = z33.object({
4782
- code: z33.string(),
5228
+ var QualityGateStatusSchema = z34.enum(["passed", "warning", "failed"]);
5229
+ var QualitySeveritySchema = z34.enum(["info", "warning", "blocking"]);
5230
+ var ApplicationQualityIssueSchema = z34.object({
5231
+ code: z34.string(),
4783
5232
  severity: QualitySeveritySchema,
4784
- message: z33.string(),
4785
- fieldId: z33.string().optional()
5233
+ message: z34.string(),
5234
+ fieldId: z34.string().optional()
4786
5235
  });
4787
- var ApplicationQualityRoundSchema = z33.object({
4788
- round: z33.number(),
4789
- kind: z33.string(),
5236
+ var ApplicationQualityRoundSchema = z34.object({
5237
+ round: z34.number(),
5238
+ kind: z34.string(),
4790
5239
  status: QualityGateStatusSchema,
4791
- summary: z33.string().optional()
5240
+ summary: z34.string().optional()
4792
5241
  });
4793
- var ApplicationQualityArtifactSchema = z33.object({
4794
- kind: z33.string(),
4795
- label: z33.string().optional(),
4796
- itemCount: z33.number().optional()
5242
+ var ApplicationQualityArtifactSchema = z34.object({
5243
+ kind: z34.string(),
5244
+ label: z34.string().optional(),
5245
+ itemCount: z34.number().optional()
4797
5246
  });
4798
- var ApplicationEmailReviewSchema = z33.object({
4799
- issues: z33.array(ApplicationQualityIssueSchema),
5247
+ var ApplicationEmailReviewSchema = z34.object({
5248
+ issues: z34.array(ApplicationQualityIssueSchema),
4800
5249
  qualityGateStatus: QualityGateStatusSchema
4801
5250
  });
4802
- var ApplicationQualityReportSchema = z33.object({
4803
- issues: z33.array(ApplicationQualityIssueSchema),
4804
- rounds: z33.array(ApplicationQualityRoundSchema).optional(),
4805
- artifacts: z33.array(ApplicationQualityArtifactSchema).optional(),
5251
+ var ApplicationQualityReportSchema = z34.object({
5252
+ issues: z34.array(ApplicationQualityIssueSchema),
5253
+ rounds: z34.array(ApplicationQualityRoundSchema).optional(),
5254
+ artifacts: z34.array(ApplicationQualityArtifactSchema).optional(),
4806
5255
  emailReview: ApplicationEmailReviewSchema.optional(),
4807
5256
  qualityGateStatus: QualityGateStatusSchema
4808
5257
  });
4809
- var ApplicationStateSchema = z33.object({
4810
- id: z33.string(),
4811
- pdfBase64: z33.string().optional().describe("Original PDF, omitted after extraction"),
4812
- title: z33.string().optional(),
4813
- applicationType: z33.string().nullable().optional(),
4814
- fields: z33.array(ApplicationFieldSchema),
4815
- batches: z33.array(z33.array(z33.string())).optional(),
4816
- currentBatchIndex: z33.number().default(0),
5258
+ var ApplicationStateSchema = z34.object({
5259
+ id: z34.string(),
5260
+ pdfBase64: z34.string().optional().describe("Original PDF, omitted after extraction"),
5261
+ title: z34.string().optional(),
5262
+ applicationType: z34.string().nullable().optional(),
5263
+ fields: z34.array(ApplicationFieldSchema),
5264
+ batches: z34.array(z34.array(z34.string())).optional(),
5265
+ currentBatchIndex: z34.number().default(0),
4817
5266
  qualityReport: ApplicationQualityReportSchema.optional(),
4818
- status: z33.enum(["classifying", "extracting", "auto_filling", "batching", "collecting", "confirming", "mapping", "complete"]),
4819
- createdAt: z33.number(),
4820
- updatedAt: z33.number()
5267
+ status: z34.enum(["classifying", "extracting", "auto_filling", "batching", "collecting", "confirming", "mapping", "complete"]),
5268
+ createdAt: z34.number(),
5269
+ updatedAt: z34.number()
4821
5270
  });
4822
5271
 
4823
5272
  // src/application/agents/classifier.ts
@@ -5887,7 +6336,7 @@ Respond with the explanation text only \u2014 no JSON, no field ID, no extra for
5887
6336
  }
5888
6337
 
5889
6338
  // src/prompts/query/classify.ts
5890
- function buildQueryClassifyPrompt(question, conversationContext) {
6339
+ function buildQueryClassifyPrompt(question, conversationContext, attachmentContext) {
5891
6340
  return `You are a query classifier for an insurance document intelligence system.
5892
6341
 
5893
6342
  Analyze the user's question and produce a structured classification.
@@ -5897,6 +6346,9 @@ ${question}
5897
6346
  ${conversationContext ? `
5898
6347
  CONVERSATION CONTEXT:
5899
6348
  ${conversationContext}` : ""}
6349
+ ${attachmentContext ? `
6350
+ ATTACHMENT CONTEXT:
6351
+ ${attachmentContext}` : ""}
5900
6352
 
5901
6353
  INSTRUCTIONS:
5902
6354
 
@@ -5917,6 +6369,7 @@ INSTRUCTIONS:
5917
6369
  - requiresDocumentLookup: true if a specific document needs to be fetched by ID/number/carrier
5918
6370
  - requiresChunkSearch: true if semantic search over document chunks is needed
5919
6371
  - requiresConversationHistory: true if the question references prior conversation
6372
+ - If the user's attachment already contains critical facts, still request chunk/document lookup when policy or quote details should be cross-checked against stored records
5920
6373
 
5921
6374
  CHUNK TYPES (for chunkTypes filter):
5922
6375
  carrier_info, named_insured, coverage, endorsement, exclusion, condition, section, declaration, loss_history, premium, supplementary
@@ -5950,73 +6403,90 @@ Respond with the final answer, deduplicated citations array, overall confidence
5950
6403
  }
5951
6404
 
5952
6405
  // src/schemas/query.ts
5953
- import { z as z34 } from "zod";
5954
- var QueryIntentSchema = z34.enum([
6406
+ import { z as z35 } from "zod";
6407
+ var QueryIntentSchema = z35.enum([
5955
6408
  "policy_question",
5956
6409
  "coverage_comparison",
5957
6410
  "document_search",
5958
6411
  "claims_inquiry",
5959
6412
  "general_knowledge"
5960
6413
  ]);
5961
- var SubQuestionSchema = z34.object({
5962
- question: z34.string().describe("Atomic sub-question to retrieve and answer independently"),
6414
+ var QueryAttachmentKindSchema = z35.enum(["image", "pdf", "text"]);
6415
+ var QueryAttachmentSchema = z35.object({
6416
+ id: z35.string().optional().describe("Optional stable attachment ID from the caller"),
6417
+ kind: QueryAttachmentKindSchema,
6418
+ name: z35.string().optional().describe("Original filename or user-facing label"),
6419
+ mimeType: z35.string().optional().describe("MIME type such as image/jpeg or application/pdf"),
6420
+ base64: z35.string().optional().describe("Base64-encoded file content for image/pdf attachments"),
6421
+ text: z35.string().optional().describe("Plain-text attachment content when available"),
6422
+ description: z35.string().optional().describe("Caller-provided description of the attachment")
6423
+ });
6424
+ var SubQuestionSchema = z35.object({
6425
+ question: z35.string().describe("Atomic sub-question to retrieve and answer independently"),
5963
6426
  intent: QueryIntentSchema,
5964
- chunkTypes: z34.array(z34.string()).optional().describe("Chunk types to filter retrieval (e.g. coverage, endorsement, declaration)"),
5965
- documentFilters: z34.object({
5966
- type: z34.enum(["policy", "quote"]).optional(),
5967
- carrier: z34.string().optional(),
5968
- insuredName: z34.string().optional(),
5969
- policyNumber: z34.string().optional(),
5970
- quoteNumber: z34.string().optional()
6427
+ chunkTypes: z35.array(z35.string()).optional().describe("Chunk types to filter retrieval (e.g. coverage, endorsement, declaration)"),
6428
+ documentFilters: z35.object({
6429
+ type: z35.enum(["policy", "quote"]).optional(),
6430
+ carrier: z35.string().optional(),
6431
+ insuredName: z35.string().optional(),
6432
+ policyNumber: z35.string().optional(),
6433
+ quoteNumber: z35.string().optional()
5971
6434
  }).optional().describe("Structured filters to narrow document lookup")
5972
6435
  });
5973
- var QueryClassifyResultSchema = z34.object({
6436
+ var QueryClassifyResultSchema = z35.object({
5974
6437
  intent: QueryIntentSchema,
5975
- subQuestions: z34.array(SubQuestionSchema).min(1).describe("Decomposed atomic sub-questions"),
5976
- requiresDocumentLookup: z34.boolean().describe("Whether structured document lookup is needed"),
5977
- requiresChunkSearch: z34.boolean().describe("Whether semantic chunk search is needed"),
5978
- requiresConversationHistory: z34.boolean().describe("Whether conversation history is relevant")
6438
+ subQuestions: z35.array(SubQuestionSchema).min(1).describe("Decomposed atomic sub-questions"),
6439
+ requiresDocumentLookup: z35.boolean().describe("Whether structured document lookup is needed"),
6440
+ requiresChunkSearch: z35.boolean().describe("Whether semantic chunk search is needed"),
6441
+ requiresConversationHistory: z35.boolean().describe("Whether conversation history is relevant")
5979
6442
  });
5980
- var EvidenceItemSchema = z34.object({
5981
- source: z34.enum(["chunk", "document", "conversation"]),
5982
- chunkId: z34.string().optional(),
5983
- documentId: z34.string().optional(),
5984
- turnId: z34.string().optional(),
5985
- text: z34.string().describe("Text excerpt from the source"),
5986
- relevance: z34.number().min(0).max(1),
5987
- metadata: z34.array(z34.object({ key: z34.string(), value: z34.string() })).optional()
6443
+ var EvidenceItemSchema = z35.object({
6444
+ source: z35.enum(["chunk", "document", "conversation", "attachment"]),
6445
+ chunkId: z35.string().optional(),
6446
+ documentId: z35.string().optional(),
6447
+ turnId: z35.string().optional(),
6448
+ attachmentId: z35.string().optional(),
6449
+ text: z35.string().describe("Text excerpt from the source"),
6450
+ relevance: z35.number().min(0).max(1),
6451
+ metadata: z35.array(z35.object({ key: z35.string(), value: z35.string() })).optional()
5988
6452
  });
5989
- var RetrievalResultSchema = z34.object({
5990
- subQuestion: z34.string(),
5991
- evidence: z34.array(EvidenceItemSchema)
6453
+ var AttachmentInterpretationSchema = z35.object({
6454
+ summary: z35.string().describe("Concise summary of what the attachment shows or contains"),
6455
+ extractedFacts: z35.array(z35.string()).describe("Specific observable or document facts grounded in the attachment"),
6456
+ recommendedFocus: z35.array(z35.string()).describe("Important details to incorporate when answering follow-up questions"),
6457
+ confidence: z35.number().min(0).max(1)
5992
6458
  });
5993
- var CitationSchema = z34.object({
5994
- index: z34.number().describe("Citation number [1], [2], etc."),
5995
- chunkId: z34.string().describe("Source chunk ID, e.g. doc-123:coverage:2"),
5996
- documentId: z34.string(),
5997
- documentType: z34.enum(["policy", "quote"]).optional(),
5998
- field: z34.string().optional().describe("Specific field path, e.g. coverages[0].deductible"),
5999
- quote: z34.string().describe("Exact text from source that supports the claim"),
6000
- relevance: z34.number().min(0).max(1)
6459
+ var RetrievalResultSchema = z35.object({
6460
+ subQuestion: z35.string(),
6461
+ evidence: z35.array(EvidenceItemSchema)
6001
6462
  });
6002
- var SubAnswerSchema = z34.object({
6003
- subQuestion: z34.string(),
6004
- answer: z34.string(),
6005
- citations: z34.array(CitationSchema),
6006
- confidence: z34.number().min(0).max(1),
6007
- needsMoreContext: z34.boolean().describe("True if evidence was insufficient to answer fully")
6463
+ var CitationSchema = z35.object({
6464
+ index: z35.number().describe("Citation number [1], [2], etc."),
6465
+ chunkId: z35.string().describe("Source chunk ID, e.g. doc-123:coverage:2"),
6466
+ documentId: z35.string(),
6467
+ documentType: z35.enum(["policy", "quote"]).optional(),
6468
+ field: z35.string().optional().describe("Specific field path, e.g. coverages[0].deductible"),
6469
+ quote: z35.string().describe("Exact text from source that supports the claim"),
6470
+ relevance: z35.number().min(0).max(1)
6008
6471
  });
6009
- var VerifyResultSchema = z34.object({
6010
- approved: z34.boolean().describe("Whether all sub-answers are adequately grounded"),
6011
- issues: z34.array(z34.string()).describe("Specific grounding or consistency issues found"),
6012
- retrySubQuestions: z34.array(z34.string()).optional().describe("Sub-questions that need additional retrieval or re-reasoning")
6472
+ var SubAnswerSchema = z35.object({
6473
+ subQuestion: z35.string(),
6474
+ answer: z35.string(),
6475
+ citations: z35.array(CitationSchema),
6476
+ confidence: z35.number().min(0).max(1),
6477
+ needsMoreContext: z35.boolean().describe("True if evidence was insufficient to answer fully")
6013
6478
  });
6014
- var QueryResultSchema = z34.object({
6015
- answer: z34.string(),
6016
- citations: z34.array(CitationSchema),
6479
+ var VerifyResultSchema = z35.object({
6480
+ approved: z35.boolean().describe("Whether all sub-answers are adequately grounded"),
6481
+ issues: z35.array(z35.string()).describe("Specific grounding or consistency issues found"),
6482
+ retrySubQuestions: z35.array(z35.string()).optional().describe("Sub-questions that need additional retrieval or re-reasoning")
6483
+ });
6484
+ var QueryResultSchema = z35.object({
6485
+ answer: z35.string(),
6486
+ citations: z35.array(CitationSchema),
6017
6487
  intent: QueryIntentSchema,
6018
- confidence: z34.number().min(0).max(1),
6019
- followUp: z34.string().optional().describe("Suggested follow-up question if applicable")
6488
+ confidence: z35.number().min(0).max(1),
6489
+ followUp: z35.string().optional().describe("Suggested follow-up question if applicable")
6020
6490
  });
6021
6491
 
6022
6492
  // src/query/retriever.ts
@@ -6306,7 +6776,7 @@ async function verify(originalQuestion, subAnswers, allEvidence, config) {
6306
6776
 
6307
6777
  // src/query/quality.ts
6308
6778
  function sourceIdForEvidence(evidence) {
6309
- return evidence.chunkId ?? evidence.documentId ?? evidence.turnId;
6779
+ return evidence.chunkId ?? evidence.documentId ?? evidence.turnId ?? evidence.attachmentId;
6310
6780
  }
6311
6781
  function citationSourceId(citation) {
6312
6782
  return citation.chunkId || citation.documentId;
@@ -6410,6 +6880,152 @@ function buildQueryReviewReport(params) {
6410
6880
  };
6411
6881
  }
6412
6882
 
6883
+ // src/prompts/query/interpret-attachment.ts
6884
+ function buildInterpretAttachmentPrompt(question, attachment) {
6885
+ const attachmentLabel = attachment.name ?? attachment.id ?? "attachment";
6886
+ const descriptor = [
6887
+ `Attachment: ${attachmentLabel}`,
6888
+ `Kind: ${attachment.kind}`,
6889
+ attachment.mimeType ? `MIME type: ${attachment.mimeType}` : null,
6890
+ attachment.description ? `Caller description: ${attachment.description}` : null
6891
+ ].filter(Boolean).join("\n");
6892
+ return `You are interpreting a user-supplied attachment for an insurance-support question.
6893
+
6894
+ USER QUESTION:
6895
+ ${question}
6896
+
6897
+ ATTACHMENT METADATA:
6898
+ ${descriptor}
6899
+
6900
+ ${attachment.kind === "text" && attachment.text ? `ATTACHMENT TEXT:
6901
+ ${attachment.text}
6902
+ ` : "The attachment content is provided separately as a file or image input.\n"}
6903
+ INSTRUCTIONS:
6904
+ 1. Describe what the attachment appears to show or contain in a concise summary.
6905
+ 2. Extract concrete facts that may matter when answering the user's question.
6906
+ 3. Note the most important details to carry forward into follow-up questions.
6907
+ 4. If the attachment is a document, identify the key business or insurance details visible.
6908
+ 5. If the attachment is a photo of damage or a real-world issue, describe the observable issue without guessing beyond what is visible.
6909
+ 6. Do not invent unreadable text. If something is unclear, say so in the summary or extracted facts.
6910
+
6911
+ Respond with the structured interpretation.`;
6912
+ }
6913
+
6914
+ // src/query/multimodal.ts
6915
+ function attachmentSourceId(attachment, index) {
6916
+ return attachment.id ?? `attachment-${index + 1}`;
6917
+ }
6918
+ function buildAttachmentProviderOptions(attachment, providerOptions) {
6919
+ const merged = {
6920
+ ...providerOptions,
6921
+ attachments: [
6922
+ {
6923
+ kind: attachment.kind,
6924
+ name: attachment.name,
6925
+ mimeType: attachment.mimeType,
6926
+ base64: attachment.base64,
6927
+ text: attachment.text,
6928
+ description: attachment.description
6929
+ }
6930
+ ]
6931
+ };
6932
+ if (attachment.kind === "pdf" && attachment.base64) {
6933
+ merged.pdfBase64 = attachment.base64;
6934
+ }
6935
+ if (attachment.kind === "image" && attachment.base64) {
6936
+ merged.images = [
6937
+ {
6938
+ imageBase64: attachment.base64,
6939
+ mimeType: attachment.mimeType ?? "image/jpeg"
6940
+ }
6941
+ ];
6942
+ }
6943
+ return merged;
6944
+ }
6945
+ function buildAttachmentEvidenceText(attachment, interpretation) {
6946
+ const lines = [
6947
+ `Attachment kind: ${attachment.kind}`,
6948
+ attachment.name ? `Attachment name: ${attachment.name}` : null,
6949
+ attachment.mimeType ? `MIME type: ${attachment.mimeType}` : null,
6950
+ attachment.description ? `Caller description: ${attachment.description}` : null,
6951
+ `Summary: ${interpretation.summary}`,
6952
+ interpretation.extractedFacts.length > 0 ? `Extracted facts:
6953
+ ${interpretation.extractedFacts.map((fact) => `- ${fact}`).join("\n")}` : null,
6954
+ interpretation.recommendedFocus.length > 0 ? `Important follow-up details:
6955
+ ${interpretation.recommendedFocus.map((item) => `- ${item}`).join("\n")}` : null,
6956
+ attachment.kind === "text" && attachment.text ? `Original text:
6957
+ ${attachment.text}` : null
6958
+ ];
6959
+ return lines.filter(Boolean).join("\n");
6960
+ }
6961
+ async function interpretAttachments(params) {
6962
+ const { attachments = [], question, generateObject, providerOptions, log, onUsage } = params;
6963
+ if (attachments.length === 0) {
6964
+ return { evidence: [] };
6965
+ }
6966
+ const evidence = [];
6967
+ for (const [index, attachment] of attachments.entries()) {
6968
+ const id = attachmentSourceId(attachment, index);
6969
+ if (attachment.kind === "text" && attachment.text) {
6970
+ const textEvidence = buildAttachmentEvidenceText(attachment, {
6971
+ summary: attachment.description ?? "User supplied text context.",
6972
+ extractedFacts: [attachment.text],
6973
+ recommendedFocus: [],
6974
+ confidence: 1
6975
+ });
6976
+ evidence.push({
6977
+ source: "attachment",
6978
+ attachmentId: id,
6979
+ chunkId: id,
6980
+ documentId: id,
6981
+ text: textEvidence,
6982
+ relevance: 0.95,
6983
+ metadata: [
6984
+ { key: "kind", value: attachment.kind },
6985
+ ...attachment.name ? [{ key: "name", value: attachment.name }] : []
6986
+ ]
6987
+ });
6988
+ continue;
6989
+ }
6990
+ const prompt = buildInterpretAttachmentPrompt(question, attachment);
6991
+ const { object, usage } = await safeGenerateObject(
6992
+ generateObject,
6993
+ {
6994
+ prompt,
6995
+ schema: AttachmentInterpretationSchema,
6996
+ maxTokens: 2048,
6997
+ providerOptions: buildAttachmentProviderOptions(attachment, providerOptions)
6998
+ },
6999
+ {
7000
+ fallback: {
7001
+ summary: attachment.description ?? `User supplied ${attachment.kind} attachment.`,
7002
+ extractedFacts: [],
7003
+ recommendedFocus: [],
7004
+ confidence: 0.2
7005
+ },
7006
+ log,
7007
+ onError: (error, attempt) => log?.(`Attachment interpretation attempt ${attempt + 1} failed for "${attachment.name ?? id}": ${error}`)
7008
+ }
7009
+ );
7010
+ onUsage?.(usage);
7011
+ evidence.push({
7012
+ source: "attachment",
7013
+ attachmentId: id,
7014
+ chunkId: id,
7015
+ documentId: id,
7016
+ text: buildAttachmentEvidenceText(attachment, object),
7017
+ relevance: Math.max(0.7, object.confidence),
7018
+ metadata: [
7019
+ { key: "kind", value: attachment.kind },
7020
+ ...attachment.name ? [{ key: "name", value: attachment.name }] : []
7021
+ ]
7022
+ });
7023
+ }
7024
+ const contextSummary = evidence.map((item, index) => `Attachment ${index + 1}:
7025
+ ${item.text}`).join("\n\n");
7026
+ return { evidence, contextSummary };
7027
+ }
7028
+
6413
7029
  // src/query/coordinator.ts
6414
7030
  function createQueryAgent(config) {
6415
7031
  const {
@@ -6437,13 +7053,23 @@ function createQueryAgent(config) {
6437
7053
  }
6438
7054
  async function query(input) {
6439
7055
  totalUsage = { inputTokens: 0, outputTokens: 0 };
6440
- const { question, conversationId, context } = input;
7056
+ const { question, conversationId, context, attachments } = input;
6441
7057
  const pipelineCtx = createPipelineContext({
6442
7058
  id: `query-${Date.now()}`
6443
7059
  });
7060
+ onProgress?.("Interpreting attachments...");
7061
+ const { evidence: attachmentEvidence, contextSummary: attachmentContext } = await interpretAttachments({
7062
+ attachments,
7063
+ question,
7064
+ generateObject,
7065
+ providerOptions,
7066
+ log,
7067
+ onUsage: trackUsage
7068
+ });
7069
+ await pipelineCtx.save("attachments", { attachmentEvidence });
6444
7070
  onProgress?.("Classifying query...");
6445
- const classification = await classify(question, conversationId);
6446
- await pipelineCtx.save("classify", { classification });
7071
+ const classification = await classify(question, conversationId, attachmentContext);
7072
+ await pipelineCtx.save("classify", { classification, attachmentEvidence });
6447
7073
  onProgress?.(`Retrieving evidence for ${classification.subQuestions.length} sub-question(s)...`);
6448
7074
  const retrieverConfig = {
6449
7075
  documentStore,
@@ -6456,8 +7082,8 @@ function createQueryAgent(config) {
6456
7082
  (sq) => limit(() => retrieve(sq, conversationId, retrieverConfig))
6457
7083
  )
6458
7084
  );
6459
- const allEvidence = retrievalResults.flatMap((r) => r.evidence);
6460
- await pipelineCtx.save("retrieve", { classification, evidence: allEvidence });
7085
+ const allEvidence = [...attachmentEvidence, ...retrievalResults.flatMap((r) => r.evidence)];
7086
+ await pipelineCtx.save("retrieve", { classification, attachmentEvidence, evidence: allEvidence });
6461
7087
  onProgress?.("Reasoning over evidence...");
6462
7088
  const reasonerConfig = { generateObject, providerOptions };
6463
7089
  const reasonResults = await Promise.allSettled(
@@ -6466,7 +7092,7 @@ function createQueryAgent(config) {
6466
7092
  const { subAnswer, usage } = await reason(
6467
7093
  sq.question,
6468
7094
  sq.intent,
6469
- retrievalResults[i].evidence,
7095
+ [...attachmentEvidence, ...retrievalResults[i].evidence],
6470
7096
  reasonerConfig
6471
7097
  );
6472
7098
  trackUsage(usage);
@@ -6490,7 +7116,7 @@ function createQueryAgent(config) {
6490
7116
  });
6491
7117
  }
6492
7118
  }
6493
- await pipelineCtx.save("reason", { classification, evidence: allEvidence, subAnswers });
7119
+ await pipelineCtx.save("reason", { classification, attachmentEvidence, evidence: allEvidence, subAnswers });
6494
7120
  onProgress?.("Verifying answer grounding...");
6495
7121
  const verifierConfig = { generateObject, providerOptions };
6496
7122
  const verifyRounds = [];
@@ -6538,7 +7164,7 @@ function createQueryAgent(config) {
6538
7164
  const { subAnswer, usage: u } = await reason(
6539
7165
  sq.question,
6540
7166
  sq.intent,
6541
- retryRetrievals[i].evidence,
7167
+ [...attachmentEvidence, ...retryRetrievals[i].evidence],
6542
7168
  reasonerConfig
6543
7169
  );
6544
7170
  trackUsage(u);
@@ -6573,6 +7199,7 @@ function createQueryAgent(config) {
6573
7199
  });
6574
7200
  await pipelineCtx.save("review", {
6575
7201
  classification,
7202
+ attachmentEvidence,
6576
7203
  evidence: allEvidence,
6577
7204
  subAnswers,
6578
7205
  reviewReport
@@ -6605,7 +7232,7 @@ function createQueryAgent(config) {
6605
7232
  }
6606
7233
  return { ...queryResult, tokenUsage: totalUsage, reviewReport };
6607
7234
  }
6608
- async function classify(question, conversationId) {
7235
+ async function classify(question, conversationId, attachmentContext) {
6609
7236
  let conversationContext;
6610
7237
  if (conversationId) {
6611
7238
  try {
@@ -6616,7 +7243,7 @@ function createQueryAgent(config) {
6616
7243
  } catch {
6617
7244
  }
6618
7245
  }
6619
- const prompt = buildQueryClassifyPrompt(question, conversationContext);
7246
+ const prompt = buildQueryClassifyPrompt(question, conversationContext, attachmentContext);
6620
7247
  const { object, usage } = await safeGenerateObject(
6621
7248
  generateObject,
6622
7249
  {
@@ -6831,6 +7458,7 @@ export {
6831
7458
  ApplicationQualityReportSchema,
6832
7459
  ApplicationQualityRoundSchema,
6833
7460
  ApplicationStateSchema,
7461
+ AttachmentInterpretationSchema,
6834
7462
  AuditTypeSchema,
6835
7463
  AutoFillMatchSchema,
6836
7464
  AutoFillResultSchema,
@@ -6952,6 +7580,8 @@ export {
6952
7580
  ProducerInfoSchema,
6953
7581
  ProfessionalLiabilityDeclarationsSchema,
6954
7582
  QUOTE_SECTION_TYPES,
7583
+ QueryAttachmentKindSchema,
7584
+ QueryAttachmentSchema,
6955
7585
  QueryClassifyResultSchema,
6956
7586
  QueryIntentSchema,
6957
7587
  QueryResultSchema,
@@ -7010,6 +7640,7 @@ export {
7010
7640
  buildFormattingPrompt,
7011
7641
  buildIdentityPrompt,
7012
7642
  buildIntentPrompt,
7643
+ buildInterpretAttachmentPrompt,
7013
7644
  buildLookupFillPrompt,
7014
7645
  buildQueryClassifyPrompt,
7015
7646
  buildQuestionBatchPrompt,