@claritylabs/cl-sdk 0.3.1 → 0.5.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
@@ -3185,6 +3185,129 @@ Respond with JSON only:
3185
3185
  "applicationType": string | null // e.g. "General Liability", "Professional Liability", "Commercial Property", "Workers Compensation", "ACORD 125", etc.
3186
3186
  }`;
3187
3187
 
3188
+ // src/schemas/application.ts
3189
+ import { z as z31 } from "zod";
3190
+ var FieldTypeSchema = z31.enum([
3191
+ "text",
3192
+ "numeric",
3193
+ "currency",
3194
+ "date",
3195
+ "yes_no",
3196
+ "table",
3197
+ "declaration"
3198
+ ]);
3199
+ var ApplicationFieldSchema = z31.object({
3200
+ id: z31.string(),
3201
+ label: z31.string(),
3202
+ section: z31.string(),
3203
+ fieldType: FieldTypeSchema,
3204
+ required: z31.boolean(),
3205
+ options: z31.array(z31.string()).optional(),
3206
+ columns: z31.array(z31.string()).optional(),
3207
+ requiresExplanationIfYes: z31.boolean().optional(),
3208
+ condition: z31.object({
3209
+ dependsOn: z31.string(),
3210
+ whenValue: z31.string()
3211
+ }).optional(),
3212
+ value: z31.string().optional(),
3213
+ source: z31.string().optional().describe("Where the value came from: auto-fill, user, lookup"),
3214
+ confidence: z31.enum(["confirmed", "high", "medium", "low"]).optional()
3215
+ });
3216
+ var ApplicationClassifyResultSchema = z31.object({
3217
+ isApplication: z31.boolean(),
3218
+ confidence: z31.number().min(0).max(1),
3219
+ applicationType: z31.string().nullable()
3220
+ });
3221
+ var FieldExtractionResultSchema = z31.object({
3222
+ fields: z31.array(ApplicationFieldSchema)
3223
+ });
3224
+ var AutoFillMatchSchema = z31.object({
3225
+ fieldId: z31.string(),
3226
+ value: z31.string(),
3227
+ confidence: z31.enum(["confirmed"]),
3228
+ contextKey: z31.string()
3229
+ });
3230
+ var AutoFillResultSchema = z31.object({
3231
+ matches: z31.array(AutoFillMatchSchema)
3232
+ });
3233
+ var QuestionBatchResultSchema = z31.object({
3234
+ batches: z31.array(z31.array(z31.string()).describe("Array of field IDs in this batch"))
3235
+ });
3236
+ var LookupRequestSchema = z31.object({
3237
+ type: z31.string().describe("Type of lookup: 'records', 'website', 'policy'"),
3238
+ description: z31.string(),
3239
+ url: z31.string().optional(),
3240
+ targetFieldIds: z31.array(z31.string())
3241
+ });
3242
+ var ReplyIntentSchema = z31.object({
3243
+ primaryIntent: z31.enum(["answers_only", "question", "lookup_request", "mixed"]),
3244
+ hasAnswers: z31.boolean(),
3245
+ questionText: z31.string().optional(),
3246
+ questionFieldIds: z31.array(z31.string()).optional(),
3247
+ lookupRequests: z31.array(LookupRequestSchema).optional()
3248
+ });
3249
+ var ParsedAnswerSchema = z31.object({
3250
+ fieldId: z31.string(),
3251
+ value: z31.string(),
3252
+ explanation: z31.string().optional()
3253
+ });
3254
+ var AnswerParsingResultSchema = z31.object({
3255
+ answers: z31.array(ParsedAnswerSchema),
3256
+ unanswered: z31.array(z31.string()).describe("Field IDs that were not answered")
3257
+ });
3258
+ var LookupFillSchema = z31.object({
3259
+ fieldId: z31.string(),
3260
+ value: z31.string(),
3261
+ source: z31.string().describe("Specific citable reference, e.g. 'GL Policy #POL-12345 (Hartford)'")
3262
+ });
3263
+ var LookupFillResultSchema = z31.object({
3264
+ fills: z31.array(LookupFillSchema),
3265
+ unfillable: z31.array(z31.string()),
3266
+ explanation: z31.string().optional()
3267
+ });
3268
+ var FlatPdfPlacementSchema = z31.object({
3269
+ fieldId: z31.string(),
3270
+ page: z31.number(),
3271
+ x: z31.number().describe("Percentage from left edge (0-100)"),
3272
+ y: z31.number().describe("Percentage from top edge (0-100)"),
3273
+ text: z31.string(),
3274
+ fontSize: z31.number().optional(),
3275
+ isCheckmark: z31.boolean().optional()
3276
+ });
3277
+ var AcroFormMappingSchema = z31.object({
3278
+ fieldId: z31.string(),
3279
+ acroFormName: z31.string(),
3280
+ value: z31.string()
3281
+ });
3282
+ var ApplicationStateSchema = z31.object({
3283
+ id: z31.string(),
3284
+ pdfBase64: z31.string().optional().describe("Original PDF, omitted after extraction"),
3285
+ title: z31.string().optional(),
3286
+ applicationType: z31.string().nullable().optional(),
3287
+ fields: z31.array(ApplicationFieldSchema),
3288
+ batches: z31.array(z31.array(z31.string())).optional(),
3289
+ currentBatchIndex: z31.number().default(0),
3290
+ status: z31.enum(["classifying", "extracting", "auto_filling", "batching", "collecting", "confirming", "mapping", "complete"]),
3291
+ createdAt: z31.number(),
3292
+ updatedAt: z31.number()
3293
+ });
3294
+
3295
+ // src/application/agents/classifier.ts
3296
+ async function classifyApplication(pdfContent, generateObject, providerOptions) {
3297
+ const { object, usage } = await withRetry(
3298
+ () => generateObject({
3299
+ prompt: `${APPLICATION_CLASSIFY_PROMPT}
3300
+
3301
+ Analyze the following document content:
3302
+ ${pdfContent}`,
3303
+ schema: ApplicationClassifyResultSchema,
3304
+ maxTokens: 512,
3305
+ providerOptions
3306
+ })
3307
+ );
3308
+ return { result: object, usage };
3309
+ }
3310
+
3188
3311
  // src/prompts/application/field-extraction.ts
3189
3312
  function buildFieldExtractionPrompt() {
3190
3313
  return `Extract all fillable fields from this insurance application PDF as a JSON array. Be concise \u2014 use short IDs and minimal keys.
@@ -3217,6 +3340,24 @@ Example:
3217
3340
  Extract ALL fields. Respond with ONLY the JSON array, no other text.`;
3218
3341
  }
3219
3342
 
3343
+ // src/application/agents/field-extractor.ts
3344
+ async function extractFields(pdfContent, generateObject, providerOptions) {
3345
+ const prompt = `${buildFieldExtractionPrompt()}
3346
+
3347
+ Extract fields from this application:
3348
+ ${pdfContent}`;
3349
+ const { object, usage } = await withRetry(
3350
+ () => generateObject({
3351
+ prompt,
3352
+ schema: FieldExtractionResultSchema,
3353
+ maxTokens: 8192,
3354
+ providerOptions
3355
+ })
3356
+ );
3357
+ const result = object;
3358
+ return { fields: result.fields, usage };
3359
+ }
3360
+
3220
3361
  // src/prompts/application/auto-fill.ts
3221
3362
  function buildAutoFillPrompt(fields, orgContext) {
3222
3363
  const fieldList = fields.map((f) => `- ${f.id}: "${f.label}" (${f.fieldType}, section: ${f.section})`).join("\n");
@@ -3246,6 +3387,39 @@ Respond with JSON only:
3246
3387
  Only include fields you can confidently fill. Do not guess or fabricate values.`;
3247
3388
  }
3248
3389
 
3390
+ // src/application/agents/auto-filler.ts
3391
+ async function autoFillFromContext(fields, orgContext, generateObject, providerOptions) {
3392
+ const fieldSummaries = fields.map((f) => ({
3393
+ id: f.id,
3394
+ label: f.label,
3395
+ fieldType: f.fieldType,
3396
+ section: f.section
3397
+ }));
3398
+ const prompt = buildAutoFillPrompt(fieldSummaries, orgContext);
3399
+ const { object, usage } = await withRetry(
3400
+ () => generateObject({
3401
+ prompt,
3402
+ schema: AutoFillResultSchema,
3403
+ maxTokens: 4096,
3404
+ providerOptions
3405
+ })
3406
+ );
3407
+ return { result: object, usage };
3408
+ }
3409
+ async function backfillFromPriorAnswers(fields, backfillProvider) {
3410
+ const unfilled = fields.filter((f) => !f.value);
3411
+ if (unfilled.length === 0) return [];
3412
+ return backfillProvider.searchPriorAnswers(
3413
+ unfilled.map((f) => ({
3414
+ id: f.id,
3415
+ label: f.label,
3416
+ section: f.section,
3417
+ fieldType: f.fieldType
3418
+ })),
3419
+ { limit: unfilled.length * 2 }
3420
+ );
3421
+ }
3422
+
3249
3423
  // src/prompts/application/question-batch.ts
3250
3424
  function buildQuestionBatchPrompt(unfilledFields) {
3251
3425
  const fieldList = unfilledFields.map(
@@ -3280,120 +3454,27 @@ Respond with JSON only:
3280
3454
  }`;
3281
3455
  }
3282
3456
 
3283
- // src/prompts/application/answer-parsing.ts
3284
- function buildAnswerParsingPrompt(questions, emailBody) {
3285
- const questionList = questions.map(
3286
- (q, i) => `${i + 1}. ${q.id}: "${q.label ?? q.text}" (type: ${q.fieldType})`
3287
- ).join("\n");
3288
- return `You are parsing a user's email reply to extract answers for specific insurance application questions.
3289
-
3290
- QUESTIONS ASKED:
3291
- ${questionList}
3292
-
3293
- USER'S EMAIL REPLY:
3294
- ${emailBody}
3295
-
3296
- Extract answers for each question. Handle:
3297
- - Direct numbered answers (1. answer, 2. answer)
3298
- - Inline answers referencing the question
3299
- - Table data provided as lists or comma-separated values
3300
- - Yes/no answers with optional explanations
3301
- - Partial responses (some questions answered, others skipped)
3302
-
3303
- Respond with JSON only:
3304
- {
3305
- "answers": [
3306
- {
3307
- "fieldId": "company_name",
3308
- "value": "Acme Corp"
3309
- },
3310
- {
3311
- "fieldId": "prior_claims_decl",
3312
- "value": "yes",
3313
- "explanation": "One claim in 2024 for water damage, $15,000 paid"
3314
- }
3315
- ],
3316
- "unanswered": ["field_id_that_was_not_answered"]
3317
- }
3318
-
3319
- Only include answers you are confident about. If a response is ambiguous, include the field in "unanswered".`;
3320
- }
3321
-
3322
- // src/prompts/application/confirmation.ts
3323
- function buildConfirmationSummaryPrompt(fields, applicationTitle) {
3324
- const fieldList = fields.map((f) => {
3325
- const label = f.label ?? f.text ?? f.id;
3326
- const value = f.value ?? "(not provided)";
3327
- return `[${f.section}] ${label}: ${value}`;
3328
- }).join("\n");
3329
- return `Format the following insurance application answers into a clean, readable summary grouped by section. This will be sent as an email for the user to review and confirm.
3330
-
3331
- APPLICATION: ${applicationTitle}
3332
-
3333
- FIELD VALUES:
3334
- ${fieldList}
3335
-
3336
- Format as a readable summary:
3337
- - Group by section with section headers
3338
- - Show each field as "Label: Value"
3339
- - For declarations, show the question and the yes/no answer plus any explanation
3340
- - Skip fields with no value unless they are required
3341
- - End with a note asking the user to reply "Looks good" to confirm, or describe any changes needed
3342
-
3343
- Respond with the formatted summary text only (no JSON wrapper). Use markdown formatting (bold headers, bullet points).`;
3344
- }
3345
-
3346
- // src/prompts/application/batch-email.ts
3347
- function buildBatchEmailGenerationPrompt(batchFields, batchIndex, totalBatches, appTitle, totalFieldCount, filledFieldCount, previousBatchSummary, companyName) {
3348
- const nonConditionalFields = batchFields.filter((f) => !f.condition);
3349
- const conditionalFields = batchFields.filter((f) => f.condition);
3350
- const fieldList = nonConditionalFields.map((f, i) => {
3351
- let line = `${i + 1}. id="${f.id}" label="${f.label}" type=${f.fieldType}`;
3352
- if (f.options) line += ` options=[${f.options.join(", ")}]`;
3353
- return line;
3354
- }).join("\n");
3355
- const conditionalNote = conditionalFields.length > 0 ? `
3356
-
3357
- CONDITIONAL FIELDS (DO NOT include in this email \u2014 they will be asked as follow-ups in a separate email after the parent is answered):
3358
- ${conditionalFields.map((f) => `- id="${f.id}" label="${f.label}" depends on ${f.condition.dependsOn} = "${f.condition.whenValue}"`).join("\n")}` : "";
3359
- const company = companyName ?? "the company";
3360
- const remainingFields = totalFieldCount - filledFieldCount;
3361
- const estMinutes = Math.max(1, Math.round(remainingFields * 0.5));
3362
- return `You are an internal risk management assistant helping your colleague fill out an insurance application for ${company}. You work FOR ${company} \u2014 you are NOT the insurer, broker, or any external party.
3363
-
3364
- APPLICATION: ${appTitle ?? "Insurance Application"}
3365
- COMPANY: ${company}
3366
- PROGRESS: ${filledFieldCount} of ${totalFieldCount} fields done, ~${remainingFields} remaining (~${estMinutes} min of questions left)
3367
- ${previousBatchSummary ? `
3368
- PREVIOUS ANSWERS RECEIVED:
3369
- ${previousBatchSummary}
3370
- ` : ""}
3371
- FIELDS TO ASK ABOUT:
3372
- ${fieldList}${conditionalNote}
3373
-
3374
- Rules:
3375
- - ${previousBatchSummary ? 'Start by acknowledging previous answers or auto-filled data. If fields were auto-filled, list each field with its value AND cite the specific source (e.g. "from your GL Policy #ABC123", "from vercel.com", "from your business context"). If a web lookup was done, name the URL that was checked. Ask them to reply with corrections if anything is wrong.' : "Start with a one-line intro."}
3376
- - Mention progress once using estimated time remaining. Don't mention section/batch numbers or field counts.
3377
- - Use "${company}" by name when referring to the company. Also fine: "we" or "our". Never "our company" or "the company".
3378
- - Ask questions plainly. No em-dashes for dramatic effect, no filler phrases like "need to nail down" or "let's dive into". Just ask.
3379
- - For yes/no questions, ask naturally in one sentence. Don't list "Yes / No" as options. Mention what you'll need if the answer triggers a follow-up (e.g. "If not, I'll need a brief explanation.").
3380
- - For fields with 2-3 options, mention them inline. 4+ options can be a short list.
3381
- - Group related fields (address, coverage limits) into single compound questions.
3382
- - Do NOT include conditional/follow-up fields. They will be sent separately.
3383
- - Number each question.
3384
- - Note expected format where relevant: dollar amounts for currency, MM/DD/YYYY for dates, column descriptions for tables.
3385
- - End with a short closing.
3386
- - Tone: professional, brief, matter-of-fact. Write like a busy coworker, not a chatbot. No flourishes, no em-dashes between clauses, no editorializing about the questions.
3387
-
3388
- NEVER:
3389
- - Sound like a salesperson or customer service agent
3390
- - Use em-dashes for emphasis or dramatic pacing
3391
- - Editorialize ("these two should wrap up this section", "just a couple more")
3392
- - List "Yes / No / N/A" as bullet options
3393
- - Include conditional follow-up questions
3394
- - Mention section numbers, batch numbers, or field counts
3395
-
3396
- Output the email body text ONLY. No subject line, no JSON. Use markdown for numbered lists.`;
3457
+ // src/application/agents/batcher.ts
3458
+ async function batchQuestions(unfilledFields, generateObject, providerOptions) {
3459
+ const fieldSummaries = unfilledFields.map((f) => ({
3460
+ id: f.id,
3461
+ label: f.label,
3462
+ text: f.label,
3463
+ fieldType: f.fieldType,
3464
+ section: f.section,
3465
+ required: f.required,
3466
+ condition: f.condition
3467
+ }));
3468
+ const prompt = buildQuestionBatchPrompt(fieldSummaries);
3469
+ const { object, usage } = await withRetry(
3470
+ () => generateObject({
3471
+ prompt,
3472
+ schema: QuestionBatchResultSchema,
3473
+ maxTokens: 2048,
3474
+ providerOptions
3475
+ })
3476
+ );
3477
+ return { result: object, usage };
3397
3478
  }
3398
3479
 
3399
3480
  // src/prompts/application/reply-intent.ts
@@ -3432,23 +3513,78 @@ Respond with JSON only:
3432
3513
  }`;
3433
3514
  }
3434
3515
 
3435
- // src/prompts/application/field-explanation.ts
3436
- function buildFieldExplanationPrompt(field, question, policyContext) {
3437
- return `You are an internal risk management assistant helping a colleague fill out an insurance application for your company. They asked a question about a field on the form.
3516
+ // src/application/agents/reply-router.ts
3517
+ async function classifyReplyIntent(fields, replyText, generateObject, providerOptions) {
3518
+ const fieldSummaries = fields.map((f) => ({ id: f.id, label: f.label }));
3519
+ const prompt = buildReplyIntentClassificationPrompt(fieldSummaries, replyText);
3520
+ const { object, usage } = await withRetry(
3521
+ () => generateObject({
3522
+ prompt,
3523
+ schema: ReplyIntentSchema,
3524
+ maxTokens: 1024,
3525
+ providerOptions
3526
+ })
3527
+ );
3528
+ return { intent: object, usage };
3529
+ }
3438
3530
 
3439
- FIELD: "${field.label}" (type: ${field.fieldType}${field.options ? `, options: ${field.options.join(", ")}` : ""})
3531
+ // src/prompts/application/answer-parsing.ts
3532
+ function buildAnswerParsingPrompt(questions, emailBody) {
3533
+ const questionList = questions.map(
3534
+ (q, i) => `${i + 1}. ${q.id}: "${q.label ?? q.text}" (type: ${q.fieldType})`
3535
+ ).join("\n");
3536
+ return `You are parsing a user's email reply to extract answers for specific insurance application questions.
3440
3537
 
3441
- THEIR QUESTION: "${question}"
3538
+ QUESTIONS ASKED:
3539
+ ${questionList}
3442
3540
 
3443
- ${policyContext ? `RELEVANT POLICY/CONTEXT INFO:
3444
- ${policyContext}
3445
- ` : ""}
3541
+ USER'S EMAIL REPLY:
3542
+ ${emailBody}
3446
3543
 
3447
- Provide a short, helpful explanation (2-3 sentences) as a coworker would. If the field has options, briefly explain what each means if relevant. If there's policy context that helps, cite the specific source (e.g. "According to our GL Policy #ABC123 with Hartford, our current aggregate limit is $2M").
3544
+ Extract answers for each question. Handle:
3545
+ - Direct numbered answers (1. answer, 2. answer)
3546
+ - Inline answers referencing the question
3547
+ - Table data provided as lists or comma-separated values
3548
+ - Yes/no answers with optional explanations
3549
+ - Partial responses (some questions answered, others skipped)
3448
3550
 
3449
- End with: "Just reply with the answer when you're ready and I'll fill it in."
3551
+ Respond with JSON only:
3552
+ {
3553
+ "answers": [
3554
+ {
3555
+ "fieldId": "company_name",
3556
+ "value": "Acme Corp"
3557
+ },
3558
+ {
3559
+ "fieldId": "prior_claims_decl",
3560
+ "value": "yes",
3561
+ "explanation": "One claim in 2024 for water damage, $15,000 paid"
3562
+ }
3563
+ ],
3564
+ "unanswered": ["field_id_that_was_not_answered"]
3565
+ }
3450
3566
 
3451
- Respond with the explanation text only \u2014 no JSON, no field ID, no extra formatting.`;
3567
+ Only include answers you are confident about. If a response is ambiguous, include the field in "unanswered".`;
3568
+ }
3569
+
3570
+ // src/application/agents/answer-parser.ts
3571
+ async function parseAnswers(fields, replyText, generateObject, providerOptions) {
3572
+ const questions = fields.map((f) => ({
3573
+ id: f.id,
3574
+ label: f.label,
3575
+ text: f.label,
3576
+ fieldType: f.fieldType
3577
+ }));
3578
+ const prompt = buildAnswerParsingPrompt(questions, replyText);
3579
+ const { object, usage } = await withRetry(
3580
+ () => generateObject({
3581
+ prompt,
3582
+ schema: AnswerParsingResultSchema,
3583
+ maxTokens: 4096,
3584
+ providerOptions
3585
+ })
3586
+ );
3587
+ return { result: object, usage };
3452
3588
  }
3453
3589
 
3454
3590
  // src/prompts/application/pdf-mapping.ts
@@ -3555,6 +3691,1122 @@ Respond with JSON only:
3555
3691
  }`;
3556
3692
  }
3557
3693
 
3694
+ // src/application/agents/lookup-filler.ts
3695
+ async function fillFromLookup(requests, targetFields, availableData, generateObject, providerOptions) {
3696
+ const requestSummaries = requests.map((r) => ({
3697
+ type: r.type,
3698
+ description: r.description,
3699
+ targetFieldIds: r.targetFieldIds
3700
+ }));
3701
+ const fieldSummaries = targetFields.map((f) => ({
3702
+ id: f.id,
3703
+ label: f.label,
3704
+ fieldType: f.fieldType
3705
+ }));
3706
+ const prompt = buildLookupFillPrompt(requestSummaries, fieldSummaries, availableData);
3707
+ const { object, usage } = await withRetry(
3708
+ () => generateObject({
3709
+ prompt,
3710
+ schema: LookupFillResultSchema,
3711
+ maxTokens: 4096,
3712
+ providerOptions
3713
+ })
3714
+ );
3715
+ return { result: object, usage };
3716
+ }
3717
+
3718
+ // src/prompts/application/batch-email.ts
3719
+ function buildBatchEmailGenerationPrompt(batchFields, batchIndex, totalBatches, appTitle, totalFieldCount, filledFieldCount, previousBatchSummary, companyName) {
3720
+ const nonConditionalFields = batchFields.filter((f) => !f.condition);
3721
+ const conditionalFields = batchFields.filter((f) => f.condition);
3722
+ const fieldList = nonConditionalFields.map((f, i) => {
3723
+ let line = `${i + 1}. id="${f.id}" label="${f.label}" type=${f.fieldType}`;
3724
+ if (f.options) line += ` options=[${f.options.join(", ")}]`;
3725
+ return line;
3726
+ }).join("\n");
3727
+ const conditionalNote = conditionalFields.length > 0 ? `
3728
+
3729
+ CONDITIONAL FIELDS (DO NOT include in this email \u2014 they will be asked as follow-ups in a separate email after the parent is answered):
3730
+ ${conditionalFields.map((f) => `- id="${f.id}" label="${f.label}" depends on ${f.condition.dependsOn} = "${f.condition.whenValue}"`).join("\n")}` : "";
3731
+ const company = companyName ?? "the company";
3732
+ const remainingFields = totalFieldCount - filledFieldCount;
3733
+ const estMinutes = Math.max(1, Math.round(remainingFields * 0.5));
3734
+ return `You are an internal risk management assistant helping your colleague fill out an insurance application for ${company}. You work FOR ${company} \u2014 you are NOT the insurer, broker, or any external party.
3735
+
3736
+ APPLICATION: ${appTitle ?? "Insurance Application"}
3737
+ COMPANY: ${company}
3738
+ PROGRESS: ${filledFieldCount} of ${totalFieldCount} fields done, ~${remainingFields} remaining (~${estMinutes} min of questions left)
3739
+ ${previousBatchSummary ? `
3740
+ PREVIOUS ANSWERS RECEIVED:
3741
+ ${previousBatchSummary}
3742
+ ` : ""}
3743
+ FIELDS TO ASK ABOUT:
3744
+ ${fieldList}${conditionalNote}
3745
+
3746
+ Rules:
3747
+ - ${previousBatchSummary ? 'Start by acknowledging previous answers or auto-filled data. If fields were auto-filled, list each field with its value AND cite the specific source (e.g. "from your GL Policy #ABC123", "from vercel.com", "from your business context"). If a web lookup was done, name the URL that was checked. Ask them to reply with corrections if anything is wrong.' : "Start with a one-line intro."}
3748
+ - Mention progress once using estimated time remaining. Don't mention section/batch numbers or field counts.
3749
+ - Use "${company}" by name when referring to the company. Also fine: "we" or "our". Never "our company" or "the company".
3750
+ - Ask questions plainly. No em-dashes for dramatic effect, no filler phrases like "need to nail down" or "let's dive into". Just ask.
3751
+ - For yes/no questions, ask naturally in one sentence. Don't list "Yes / No" as options. Mention what you'll need if the answer triggers a follow-up (e.g. "If not, I'll need a brief explanation.").
3752
+ - For fields with 2-3 options, mention them inline. 4+ options can be a short list.
3753
+ - Group related fields (address, coverage limits) into single compound questions.
3754
+ - Do NOT include conditional/follow-up fields. They will be sent separately.
3755
+ - Number each question.
3756
+ - Note expected format where relevant: dollar amounts for currency, MM/DD/YYYY for dates, column descriptions for tables.
3757
+ - End with a short closing.
3758
+ - Tone: professional, brief, matter-of-fact. Write like a busy coworker, not a chatbot. No flourishes, no em-dashes between clauses, no editorializing about the questions.
3759
+
3760
+ NEVER:
3761
+ - Sound like a salesperson or customer service agent
3762
+ - Use em-dashes for emphasis or dramatic pacing
3763
+ - Editorialize ("these two should wrap up this section", "just a couple more")
3764
+ - List "Yes / No / N/A" as bullet options
3765
+ - Include conditional follow-up questions
3766
+ - Mention section numbers, batch numbers, or field counts
3767
+
3768
+ Output the email body text ONLY. No subject line, no JSON. Use markdown for numbered lists.`;
3769
+ }
3770
+
3771
+ // src/application/agents/email-generator.ts
3772
+ async function generateBatchEmail(batchFields, batchIndex, totalBatches, opts, generateText, providerOptions) {
3773
+ const fieldSummaries = batchFields.map((f) => ({
3774
+ id: f.id,
3775
+ label: f.label,
3776
+ fieldType: f.fieldType,
3777
+ options: f.options,
3778
+ condition: f.condition
3779
+ }));
3780
+ const prompt = buildBatchEmailGenerationPrompt(
3781
+ fieldSummaries,
3782
+ batchIndex,
3783
+ totalBatches,
3784
+ opts.appTitle,
3785
+ opts.totalFieldCount,
3786
+ opts.filledFieldCount,
3787
+ opts.previousBatchSummary,
3788
+ opts.companyName
3789
+ );
3790
+ const { text, usage } = await withRetry(
3791
+ () => generateText({
3792
+ prompt,
3793
+ maxTokens: 2048,
3794
+ providerOptions
3795
+ })
3796
+ );
3797
+ return { text, usage };
3798
+ }
3799
+
3800
+ // src/application/coordinator.ts
3801
+ function createApplicationPipeline(config) {
3802
+ const {
3803
+ generateText,
3804
+ generateObject,
3805
+ applicationStore,
3806
+ documentStore,
3807
+ memoryStore,
3808
+ backfillProvider,
3809
+ orgContext = [],
3810
+ concurrency = 4,
3811
+ onTokenUsage,
3812
+ onProgress,
3813
+ log,
3814
+ providerOptions
3815
+ } = config;
3816
+ const limit = pLimit(concurrency);
3817
+ let totalUsage = { inputTokens: 0, outputTokens: 0 };
3818
+ function trackUsage(usage) {
3819
+ if (usage) {
3820
+ totalUsage.inputTokens += usage.inputTokens;
3821
+ totalUsage.outputTokens += usage.outputTokens;
3822
+ onTokenUsage?.(usage);
3823
+ }
3824
+ }
3825
+ async function processApplication(input) {
3826
+ totalUsage = { inputTokens: 0, outputTokens: 0 };
3827
+ const { pdfBase64, context } = input;
3828
+ const id = input.applicationId ?? `app-${Date.now()}`;
3829
+ const now = Date.now();
3830
+ let state = {
3831
+ id,
3832
+ pdfBase64: void 0,
3833
+ // Don't persist the full PDF in state
3834
+ title: void 0,
3835
+ applicationType: null,
3836
+ fields: [],
3837
+ batches: void 0,
3838
+ currentBatchIndex: 0,
3839
+ status: "classifying",
3840
+ createdAt: now,
3841
+ updatedAt: now
3842
+ };
3843
+ onProgress?.("Classifying document...");
3844
+ const { result: classifyResult, usage: classifyUsage } = await classifyApplication(
3845
+ pdfBase64.slice(0, 2e3),
3846
+ // Send truncated content for classification
3847
+ generateObject,
3848
+ providerOptions
3849
+ );
3850
+ trackUsage(classifyUsage);
3851
+ if (!classifyResult.isApplication) {
3852
+ state.status = "complete";
3853
+ state.updatedAt = Date.now();
3854
+ await applicationStore?.save(state);
3855
+ return { state, tokenUsage: totalUsage };
3856
+ }
3857
+ state.applicationType = classifyResult.applicationType;
3858
+ state.status = "extracting";
3859
+ state.updatedAt = Date.now();
3860
+ onProgress?.("Extracting form fields...");
3861
+ const { fields, usage: extractUsage } = await extractFields(
3862
+ pdfBase64,
3863
+ generateObject,
3864
+ providerOptions
3865
+ );
3866
+ trackUsage(extractUsage);
3867
+ state.fields = fields;
3868
+ state.title = classifyResult.applicationType ?? void 0;
3869
+ state.status = "auto_filling";
3870
+ state.updatedAt = Date.now();
3871
+ await applicationStore?.save(state);
3872
+ onProgress?.(`Auto-filling ${fields.length} fields...`);
3873
+ const fillTasks = [];
3874
+ if (backfillProvider) {
3875
+ fillTasks.push(
3876
+ (async () => {
3877
+ try {
3878
+ const priorAnswers = await backfillFromPriorAnswers(fields, backfillProvider);
3879
+ for (const pa of priorAnswers) {
3880
+ const field = state.fields.find((f) => f.id === pa.fieldId);
3881
+ if (field && !field.value && pa.relevance > 0.8) {
3882
+ field.value = pa.value;
3883
+ field.source = `backfill: ${pa.source}`;
3884
+ field.confidence = "high";
3885
+ }
3886
+ }
3887
+ } catch (e) {
3888
+ await log?.(`Backfill failed: ${e}`);
3889
+ }
3890
+ })()
3891
+ );
3892
+ }
3893
+ if (orgContext.length > 0) {
3894
+ fillTasks.push(
3895
+ limit(async () => {
3896
+ const unfilledFields2 = state.fields.filter((f) => !f.value);
3897
+ if (unfilledFields2.length === 0) return;
3898
+ const { result: autoFillResult, usage: afUsage } = await autoFillFromContext(
3899
+ unfilledFields2,
3900
+ orgContext,
3901
+ generateObject,
3902
+ providerOptions
3903
+ );
3904
+ trackUsage(afUsage);
3905
+ for (const match of autoFillResult.matches) {
3906
+ const field = state.fields.find((f) => f.id === match.fieldId);
3907
+ if (field && !field.value) {
3908
+ field.value = match.value;
3909
+ field.source = `auto-fill: ${match.contextKey}`;
3910
+ field.confidence = match.confidence;
3911
+ }
3912
+ }
3913
+ })
3914
+ );
3915
+ }
3916
+ if (documentStore && memoryStore) {
3917
+ fillTasks.push(
3918
+ (async () => {
3919
+ try {
3920
+ const unfilledFields2 = state.fields.filter((f) => !f.value);
3921
+ const searchPromises = unfilledFields2.slice(0, 10).map(
3922
+ (f) => limit(async () => {
3923
+ const chunks = await memoryStore.search(f.label, { limit: 3 });
3924
+ for (const chunk of chunks) {
3925
+ if (!state.fields.find((sf) => sf.id === f.id)?.value) {
3926
+ }
3927
+ }
3928
+ })
3929
+ );
3930
+ await Promise.all(searchPromises);
3931
+ } catch (e) {
3932
+ await log?.(`Document backfill search failed: ${e}`);
3933
+ }
3934
+ })()
3935
+ );
3936
+ }
3937
+ await Promise.all(fillTasks);
3938
+ state.updatedAt = Date.now();
3939
+ await applicationStore?.save(state);
3940
+ const unfilledFields = state.fields.filter((f) => !f.value);
3941
+ if (unfilledFields.length > 0) {
3942
+ onProgress?.(`Batching ${unfilledFields.length} remaining questions...`);
3943
+ state.status = "batching";
3944
+ const { result: batchResult, usage: batchUsage } = await batchQuestions(
3945
+ unfilledFields,
3946
+ generateObject,
3947
+ providerOptions
3948
+ );
3949
+ trackUsage(batchUsage);
3950
+ state.batches = batchResult.batches;
3951
+ state.currentBatchIndex = 0;
3952
+ state.status = "collecting";
3953
+ } else {
3954
+ state.status = "confirming";
3955
+ }
3956
+ state.updatedAt = Date.now();
3957
+ await applicationStore?.save(state);
3958
+ const filledCount = state.fields.filter((f) => f.value).length;
3959
+ onProgress?.(`Application processed: ${filledCount}/${state.fields.length} fields filled, ${state.batches?.length ?? 0} batches to collect.`);
3960
+ return { state, tokenUsage: totalUsage };
3961
+ }
3962
+ async function processReply(input) {
3963
+ totalUsage = { inputTokens: 0, outputTokens: 0 };
3964
+ const { applicationId, replyText, context } = input;
3965
+ let state = null;
3966
+ if (applicationStore) {
3967
+ state = await applicationStore.get(applicationId);
3968
+ }
3969
+ if (!state) {
3970
+ throw new Error(`Application ${applicationId} not found`);
3971
+ }
3972
+ const currentBatchFieldIds = state.batches?.[state.currentBatchIndex] ?? [];
3973
+ const currentBatchFields = state.fields.filter(
3974
+ (f) => currentBatchFieldIds.includes(f.id)
3975
+ );
3976
+ onProgress?.("Classifying reply...");
3977
+ const { intent, usage: intentUsage } = await classifyReplyIntent(
3978
+ currentBatchFields,
3979
+ replyText,
3980
+ generateObject,
3981
+ providerOptions
3982
+ );
3983
+ trackUsage(intentUsage);
3984
+ let fieldsFilled = 0;
3985
+ let responseText;
3986
+ if (intent.hasAnswers) {
3987
+ onProgress?.("Parsing answers...");
3988
+ const { result: parseResult, usage: parseUsage } = await parseAnswers(
3989
+ currentBatchFields,
3990
+ replyText,
3991
+ generateObject,
3992
+ providerOptions
3993
+ );
3994
+ trackUsage(parseUsage);
3995
+ for (const answer of parseResult.answers) {
3996
+ const field = state.fields.find((f) => f.id === answer.fieldId);
3997
+ if (field) {
3998
+ field.value = answer.value;
3999
+ field.source = "user";
4000
+ field.confidence = "confirmed";
4001
+ fieldsFilled++;
4002
+ }
4003
+ }
4004
+ }
4005
+ if (intent.lookupRequests?.length) {
4006
+ onProgress?.("Processing lookup requests...");
4007
+ let availableData = "";
4008
+ if (documentStore) {
4009
+ try {
4010
+ const docs = await documentStore.query({});
4011
+ availableData = docs.map((d) => {
4012
+ const doc = d;
4013
+ return `Document ${doc.id}: ${doc.type} - ${doc.carrier ?? "unknown carrier"} - ${doc.insuredName ?? ""}`;
4014
+ }).join("\n");
4015
+ } catch (e) {
4016
+ await log?.(`Document query for lookup failed: ${e}`);
4017
+ }
4018
+ }
4019
+ if (availableData) {
4020
+ const targetFields = state.fields.filter(
4021
+ (f) => intent.lookupRequests.some((lr) => lr.targetFieldIds.includes(f.id))
4022
+ );
4023
+ const { result: lookupResult, usage: lookupUsage } = await fillFromLookup(
4024
+ intent.lookupRequests,
4025
+ targetFields,
4026
+ availableData,
4027
+ generateObject,
4028
+ providerOptions
4029
+ );
4030
+ trackUsage(lookupUsage);
4031
+ for (const fill of lookupResult.fills) {
4032
+ const field = state.fields.find((f) => f.id === fill.fieldId);
4033
+ if (field) {
4034
+ field.value = fill.value;
4035
+ field.source = `lookup: ${fill.source}`;
4036
+ field.confidence = "high";
4037
+ fieldsFilled++;
4038
+ }
4039
+ }
4040
+ }
4041
+ }
4042
+ if (intent.primaryIntent === "question" || intent.primaryIntent === "mixed") {
4043
+ if (intent.questionText) {
4044
+ const { text, usage } = await generateText({
4045
+ prompt: `The user is filling out an insurance application and asked: "${intent.questionText}"
4046
+
4047
+ Provide a brief, helpful explanation (2-3 sentences). End with "Just reply with the answer when you're ready and I'll fill it in."`,
4048
+ maxTokens: 512,
4049
+ providerOptions
4050
+ });
4051
+ trackUsage(usage);
4052
+ responseText = text;
4053
+ }
4054
+ }
4055
+ const currentBatchComplete = currentBatchFieldIds.every(
4056
+ (fid) => state.fields.find((f) => f.id === fid)?.value
4057
+ );
4058
+ if (currentBatchComplete && state.batches) {
4059
+ if (state.currentBatchIndex < state.batches.length - 1) {
4060
+ state.currentBatchIndex++;
4061
+ const nextBatchFieldIds = state.batches[state.currentBatchIndex];
4062
+ const nextBatchFields = state.fields.filter(
4063
+ (f) => nextBatchFieldIds.includes(f.id)
4064
+ );
4065
+ const filledCount = state.fields.filter((f) => f.value).length;
4066
+ const { text: emailText, usage: emailUsage } = await generateBatchEmail(
4067
+ nextBatchFields,
4068
+ state.currentBatchIndex,
4069
+ state.batches.length,
4070
+ {
4071
+ appTitle: state.title,
4072
+ totalFieldCount: state.fields.length,
4073
+ filledFieldCount: filledCount,
4074
+ companyName: context?.companyName
4075
+ },
4076
+ generateText,
4077
+ providerOptions
4078
+ );
4079
+ trackUsage(emailUsage);
4080
+ if (!responseText) {
4081
+ responseText = emailText;
4082
+ } else {
4083
+ responseText += `
4084
+
4085
+ ${emailText}`;
4086
+ }
4087
+ } else {
4088
+ state.status = "confirming";
4089
+ }
4090
+ }
4091
+ state.updatedAt = Date.now();
4092
+ await applicationStore?.save(state);
4093
+ return {
4094
+ state,
4095
+ intent: intent.primaryIntent,
4096
+ fieldsFilled,
4097
+ responseText,
4098
+ tokenUsage: totalUsage
4099
+ };
4100
+ }
4101
+ async function generateCurrentBatchEmail(applicationId, opts) {
4102
+ totalUsage = { inputTokens: 0, outputTokens: 0 };
4103
+ const state = await applicationStore?.get(applicationId);
4104
+ if (!state) throw new Error(`Application ${applicationId} not found`);
4105
+ if (!state.batches?.length) throw new Error("No batches available");
4106
+ const batchFieldIds = state.batches[state.currentBatchIndex];
4107
+ const batchFields = state.fields.filter((f) => batchFieldIds.includes(f.id));
4108
+ const filledCount = state.fields.filter((f) => f.value).length;
4109
+ const { text, usage } = await generateBatchEmail(
4110
+ batchFields,
4111
+ state.currentBatchIndex,
4112
+ state.batches.length,
4113
+ {
4114
+ appTitle: state.title,
4115
+ totalFieldCount: state.fields.length,
4116
+ filledFieldCount: filledCount,
4117
+ companyName: opts?.companyName,
4118
+ previousBatchSummary: opts?.previousBatchSummary
4119
+ },
4120
+ generateText,
4121
+ providerOptions
4122
+ );
4123
+ trackUsage(usage);
4124
+ return { text, tokenUsage: totalUsage };
4125
+ }
4126
+ async function getConfirmationSummary(applicationId) {
4127
+ totalUsage = { inputTokens: 0, outputTokens: 0 };
4128
+ const state = await applicationStore?.get(applicationId);
4129
+ if (!state) throw new Error(`Application ${applicationId} not found`);
4130
+ const filledFields = state.fields.filter((f) => f.value);
4131
+ const fieldSummary = filledFields.map((f) => `${f.section} > ${f.label}: ${f.value} (source: ${f.source ?? "unknown"})`).join("\n");
4132
+ const { text, usage } = await generateText({
4133
+ prompt: `Format these filled insurance application fields as a clean confirmation summary for the user to review. Group by section, show each field as "Label: Value". End with a note asking them to confirm or request changes.
4134
+
4135
+ Application: ${state.title ?? "Insurance Application"}
4136
+
4137
+ Fields:
4138
+ ${fieldSummary}`,
4139
+ maxTokens: 4096,
4140
+ providerOptions
4141
+ });
4142
+ trackUsage(usage);
4143
+ return { text, tokenUsage: totalUsage };
4144
+ }
4145
+ return {
4146
+ processApplication,
4147
+ processReply,
4148
+ generateCurrentBatchEmail,
4149
+ getConfirmationSummary
4150
+ };
4151
+ }
4152
+
4153
+ // src/prompts/application/confirmation.ts
4154
+ function buildConfirmationSummaryPrompt(fields, applicationTitle) {
4155
+ const fieldList = fields.map((f) => {
4156
+ const label = f.label ?? f.text ?? f.id;
4157
+ const value = f.value ?? "(not provided)";
4158
+ return `[${f.section}] ${label}: ${value}`;
4159
+ }).join("\n");
4160
+ return `Format the following insurance application answers into a clean, readable summary grouped by section. This will be sent as an email for the user to review and confirm.
4161
+
4162
+ APPLICATION: ${applicationTitle}
4163
+
4164
+ FIELD VALUES:
4165
+ ${fieldList}
4166
+
4167
+ Format as a readable summary:
4168
+ - Group by section with section headers
4169
+ - Show each field as "Label: Value"
4170
+ - For declarations, show the question and the yes/no answer plus any explanation
4171
+ - Skip fields with no value unless they are required
4172
+ - End with a note asking the user to reply "Looks good" to confirm, or describe any changes needed
4173
+
4174
+ Respond with the formatted summary text only (no JSON wrapper). Use markdown formatting (bold headers, bullet points).`;
4175
+ }
4176
+
4177
+ // src/prompts/application/field-explanation.ts
4178
+ function buildFieldExplanationPrompt(field, question, policyContext) {
4179
+ return `You are an internal risk management assistant helping a colleague fill out an insurance application for your company. They asked a question about a field on the form.
4180
+
4181
+ FIELD: "${field.label}" (type: ${field.fieldType}${field.options ? `, options: ${field.options.join(", ")}` : ""})
4182
+
4183
+ THEIR QUESTION: "${question}"
4184
+
4185
+ ${policyContext ? `RELEVANT POLICY/CONTEXT INFO:
4186
+ ${policyContext}
4187
+ ` : ""}
4188
+
4189
+ Provide a short, helpful explanation (2-3 sentences) as a coworker would. If the field has options, briefly explain what each means if relevant. If there's policy context that helps, cite the specific source (e.g. "According to our GL Policy #ABC123 with Hartford, our current aggregate limit is $2M").
4190
+
4191
+ End with: "Just reply with the answer when you're ready and I'll fill it in."
4192
+
4193
+ Respond with the explanation text only \u2014 no JSON, no field ID, no extra formatting.`;
4194
+ }
4195
+
4196
+ // src/prompts/query/classify.ts
4197
+ function buildQueryClassifyPrompt(question, conversationContext) {
4198
+ return `You are a query classifier for an insurance document intelligence system.
4199
+
4200
+ Analyze the user's question and produce a structured classification.
4201
+
4202
+ USER QUESTION:
4203
+ ${question}
4204
+ ${conversationContext ? `
4205
+ CONVERSATION CONTEXT:
4206
+ ${conversationContext}` : ""}
4207
+
4208
+ INSTRUCTIONS:
4209
+
4210
+ 1. Determine the primary intent:
4211
+ - "policy_question": questions about specific coverage, limits, deductibles, endorsements, conditions
4212
+ - "coverage_comparison": comparing coverages across multiple documents or policies
4213
+ - "document_search": looking for a specific document by carrier, policy number, insured name
4214
+ - "claims_inquiry": questions about claims history, loss runs, experience modification
4215
+ - "general_knowledge": insurance concepts not tied to a specific document
4216
+
4217
+ 2. Decompose into atomic sub-questions:
4218
+ - Each sub-question should be answerable from a single retrieval pass
4219
+ - Simple questions produce exactly one sub-question (the question itself)
4220
+ - Complex questions (comparisons, multi-policy, multi-field) decompose into 2-5 sub-questions
4221
+ - Each sub-question should specify which chunk types are most relevant
4222
+
4223
+ 3. Determine which storage backends are needed:
4224
+ - requiresDocumentLookup: true if a specific document needs to be fetched by ID/number/carrier
4225
+ - requiresChunkSearch: true if semantic search over document chunks is needed
4226
+ - requiresConversationHistory: true if the question references prior conversation
4227
+
4228
+ CHUNK TYPES (for chunkTypes filter):
4229
+ carrier_info, named_insured, coverage, endorsement, exclusion, condition, section, declaration, loss_history, premium, supplementary
4230
+
4231
+ Respond with the structured classification.`;
4232
+ }
4233
+
4234
+ // src/prompts/query/respond.ts
4235
+ function buildRespondPrompt(originalQuestion, subAnswersJson, platform) {
4236
+ const formatGuidance = platform === "email" ? "Format as a professional email response. Use plain text, no markdown." : platform === "sms" ? "Keep the response concise and conversational. No markdown." : "Format as clear, well-structured text. Use markdown for lists and emphasis where helpful.";
4237
+ return `You are composing a final answer to an insurance question. You have verified sub-answers with citations that you need to merge into a single, natural response.
4238
+
4239
+ ORIGINAL QUESTION:
4240
+ ${originalQuestion}
4241
+
4242
+ VERIFIED SUB-ANSWERS:
4243
+ ${subAnswersJson}
4244
+
4245
+ FORMATTING:
4246
+ ${formatGuidance}
4247
+
4248
+ INSTRUCTIONS:
4249
+ 1. Write a natural, direct answer to the original question.
4250
+ 2. Embed inline citation numbers [1], [2], etc. after each factual claim. These reference the citation objects from the sub-answers \u2014 preserve the original citation index numbers.
4251
+ 3. If any sub-answer had low confidence or noted missing context, mention what information was unavailable rather than omitting silently.
4252
+ 4. If the answer naturally leads to a follow-up question the user might want to ask, suggest it in the followUp field.
4253
+ 5. Merge overlapping citations \u2014 if two sub-answers cite the same chunk, use one citation number.
4254
+ 6. Keep the tone helpful and professional.
4255
+
4256
+ Respond with the final answer, deduplicated citations array, overall confidence (weighted average of sub-answer confidences), and an optional follow-up suggestion.`;
4257
+ }
4258
+
4259
+ // src/schemas/query.ts
4260
+ import { z as z32 } from "zod";
4261
+ var QueryIntentSchema = z32.enum([
4262
+ "policy_question",
4263
+ "coverage_comparison",
4264
+ "document_search",
4265
+ "claims_inquiry",
4266
+ "general_knowledge"
4267
+ ]);
4268
+ var SubQuestionSchema = z32.object({
4269
+ question: z32.string().describe("Atomic sub-question to retrieve and answer independently"),
4270
+ intent: QueryIntentSchema,
4271
+ chunkTypes: z32.array(z32.string()).optional().describe("Chunk types to filter retrieval (e.g. coverage, endorsement, declaration)"),
4272
+ documentFilters: z32.object({
4273
+ type: z32.enum(["policy", "quote"]).optional(),
4274
+ carrier: z32.string().optional(),
4275
+ insuredName: z32.string().optional(),
4276
+ policyNumber: z32.string().optional(),
4277
+ quoteNumber: z32.string().optional()
4278
+ }).optional().describe("Structured filters to narrow document lookup")
4279
+ });
4280
+ var QueryClassifyResultSchema = z32.object({
4281
+ intent: QueryIntentSchema,
4282
+ subQuestions: z32.array(SubQuestionSchema).min(1).describe("Decomposed atomic sub-questions"),
4283
+ requiresDocumentLookup: z32.boolean().describe("Whether structured document lookup is needed"),
4284
+ requiresChunkSearch: z32.boolean().describe("Whether semantic chunk search is needed"),
4285
+ requiresConversationHistory: z32.boolean().describe("Whether conversation history is relevant")
4286
+ });
4287
+ var EvidenceItemSchema = z32.object({
4288
+ source: z32.enum(["chunk", "document", "conversation"]),
4289
+ chunkId: z32.string().optional(),
4290
+ documentId: z32.string().optional(),
4291
+ turnId: z32.string().optional(),
4292
+ text: z32.string().describe("Text excerpt from the source"),
4293
+ relevance: z32.number().min(0).max(1),
4294
+ metadata: z32.record(z32.string(), z32.string()).optional()
4295
+ });
4296
+ var RetrievalResultSchema = z32.object({
4297
+ subQuestion: z32.string(),
4298
+ evidence: z32.array(EvidenceItemSchema)
4299
+ });
4300
+ var CitationSchema = z32.object({
4301
+ index: z32.number().describe("Citation number [1], [2], etc."),
4302
+ chunkId: z32.string().describe("Source chunk ID, e.g. doc-123:coverage:2"),
4303
+ documentId: z32.string(),
4304
+ documentType: z32.enum(["policy", "quote"]).optional(),
4305
+ field: z32.string().optional().describe("Specific field path, e.g. coverages[0].deductible"),
4306
+ quote: z32.string().describe("Exact text from source that supports the claim"),
4307
+ relevance: z32.number().min(0).max(1)
4308
+ });
4309
+ var SubAnswerSchema = z32.object({
4310
+ subQuestion: z32.string(),
4311
+ answer: z32.string(),
4312
+ citations: z32.array(CitationSchema),
4313
+ confidence: z32.number().min(0).max(1),
4314
+ needsMoreContext: z32.boolean().describe("True if evidence was insufficient to answer fully")
4315
+ });
4316
+ var VerifyResultSchema = z32.object({
4317
+ approved: z32.boolean().describe("Whether all sub-answers are adequately grounded"),
4318
+ issues: z32.array(z32.string()).describe("Specific grounding or consistency issues found"),
4319
+ retrySubQuestions: z32.array(z32.string()).optional().describe("Sub-questions that need additional retrieval or re-reasoning")
4320
+ });
4321
+ var QueryResultSchema = z32.object({
4322
+ answer: z32.string(),
4323
+ citations: z32.array(CitationSchema),
4324
+ intent: QueryIntentSchema,
4325
+ confidence: z32.number().min(0).max(1),
4326
+ followUp: z32.string().optional().describe("Suggested follow-up question if applicable")
4327
+ });
4328
+
4329
+ // src/query/retriever.ts
4330
+ async function retrieve(subQuestion, conversationId, config) {
4331
+ const { documentStore, memoryStore, retrievalLimit, log } = config;
4332
+ const evidence = [];
4333
+ const tasks = [];
4334
+ tasks.push(
4335
+ (async () => {
4336
+ try {
4337
+ const filter = {};
4338
+ if (subQuestion.chunkTypes?.length) {
4339
+ const chunkResults = await Promise.all(
4340
+ subQuestion.chunkTypes.map(
4341
+ (type) => memoryStore.search(subQuestion.question, {
4342
+ limit: Math.ceil(retrievalLimit / subQuestion.chunkTypes.length),
4343
+ filter: { ...filter, type }
4344
+ })
4345
+ )
4346
+ );
4347
+ for (const chunks of chunkResults) {
4348
+ for (const chunk of chunks) {
4349
+ evidence.push({
4350
+ source: "chunk",
4351
+ chunkId: chunk.id,
4352
+ documentId: chunk.documentId,
4353
+ text: chunk.text,
4354
+ relevance: 0.8,
4355
+ // Default — store doesn't expose scores directly
4356
+ metadata: chunk.metadata
4357
+ });
4358
+ }
4359
+ }
4360
+ } else {
4361
+ const chunks = await memoryStore.search(subQuestion.question, {
4362
+ limit: retrievalLimit
4363
+ });
4364
+ for (const chunk of chunks) {
4365
+ evidence.push({
4366
+ source: "chunk",
4367
+ chunkId: chunk.id,
4368
+ documentId: chunk.documentId,
4369
+ text: chunk.text,
4370
+ relevance: 0.8,
4371
+ metadata: chunk.metadata
4372
+ });
4373
+ }
4374
+ }
4375
+ } catch (e) {
4376
+ await log?.(`Chunk search failed for "${subQuestion.question}": ${e}`);
4377
+ }
4378
+ })()
4379
+ );
4380
+ if (subQuestion.documentFilters) {
4381
+ tasks.push(
4382
+ (async () => {
4383
+ try {
4384
+ const filters = {};
4385
+ if (subQuestion.documentFilters?.type) filters.type = subQuestion.documentFilters.type;
4386
+ if (subQuestion.documentFilters?.carrier) filters.carrier = subQuestion.documentFilters.carrier;
4387
+ if (subQuestion.documentFilters?.insuredName) filters.insuredName = subQuestion.documentFilters.insuredName;
4388
+ if (subQuestion.documentFilters?.policyNumber) filters.policyNumber = subQuestion.documentFilters.policyNumber;
4389
+ if (subQuestion.documentFilters?.quoteNumber) filters.quoteNumber = subQuestion.documentFilters.quoteNumber;
4390
+ const docs = await documentStore.query(filters);
4391
+ for (const doc of docs) {
4392
+ const summary = buildDocumentSummary(doc);
4393
+ evidence.push({
4394
+ source: "document",
4395
+ documentId: doc.id,
4396
+ text: summary,
4397
+ relevance: 0.9,
4398
+ // Direct lookup is high relevance
4399
+ metadata: {
4400
+ type: doc.type,
4401
+ carrier: doc.carrier ?? "",
4402
+ insuredName: doc.insuredName ?? ""
4403
+ }
4404
+ });
4405
+ }
4406
+ } catch (e) {
4407
+ await log?.(`Document lookup failed: ${e}`);
4408
+ }
4409
+ })()
4410
+ );
4411
+ }
4412
+ if (conversationId) {
4413
+ tasks.push(
4414
+ (async () => {
4415
+ try {
4416
+ const turns = await memoryStore.searchHistory(
4417
+ subQuestion.question,
4418
+ conversationId
4419
+ );
4420
+ for (const turn of turns.slice(0, 5)) {
4421
+ evidence.push({
4422
+ source: "conversation",
4423
+ turnId: turn.id,
4424
+ text: `[${turn.role}]: ${turn.content}`,
4425
+ relevance: 0.6
4426
+ // Conversation context is lower relevance than documents
4427
+ });
4428
+ }
4429
+ } catch (e) {
4430
+ await log?.(`Conversation history search failed: ${e}`);
4431
+ }
4432
+ })()
4433
+ );
4434
+ }
4435
+ await Promise.all(tasks);
4436
+ evidence.sort((a, b) => b.relevance - a.relevance);
4437
+ return {
4438
+ subQuestion: subQuestion.question,
4439
+ evidence: evidence.slice(0, retrievalLimit)
4440
+ };
4441
+ }
4442
+ function buildDocumentSummary(doc) {
4443
+ const parts = [];
4444
+ const type = doc.type;
4445
+ parts.push(`Document type: ${type}`);
4446
+ if (doc.carrier) parts.push(`Carrier: ${doc.carrier}`);
4447
+ if (doc.insuredName) parts.push(`Insured: ${doc.insuredName}`);
4448
+ if (type === "policy") {
4449
+ if (doc.policyNumber) parts.push(`Policy #: ${doc.policyNumber}`);
4450
+ if (doc.effectiveDate) parts.push(`Effective: ${doc.effectiveDate}`);
4451
+ if (doc.expirationDate) parts.push(`Expiration: ${doc.expirationDate}`);
4452
+ } else if (type === "quote") {
4453
+ if (doc.quoteNumber) parts.push(`Quote #: ${doc.quoteNumber}`);
4454
+ if (doc.proposedEffectiveDate) parts.push(`Proposed effective: ${doc.proposedEffectiveDate}`);
4455
+ }
4456
+ if (doc.premium) parts.push(`Premium: ${doc.premium}`);
4457
+ const coverages = doc.coverages;
4458
+ if (coverages?.length) {
4459
+ parts.push(`Coverages (${coverages.length}):`);
4460
+ for (const cov of coverages.slice(0, 10)) {
4461
+ const line = [cov.name, cov.limit ? `Limit: ${cov.limit}` : null, cov.deductible ? `Ded: ${cov.deductible}` : null].filter(Boolean).join(" | ");
4462
+ parts.push(` - ${line}`);
4463
+ }
4464
+ }
4465
+ return parts.join("\n");
4466
+ }
4467
+
4468
+ // src/prompts/query/reason.ts
4469
+ var INTENT_INSTRUCTIONS = {
4470
+ policy_question: `You are answering a question about a specific insurance policy or quote.
4471
+
4472
+ RULES:
4473
+ - Answer ONLY from the evidence provided. Do not use general knowledge.
4474
+ - When citing limits, deductibles, or amounts, use the exact values from the source.
4475
+ - If the evidence mentions an endorsement that modifies coverage, include that context.
4476
+ - If the evidence is insufficient, say what is missing rather than guessing.
4477
+ - Reference specific coverage names, form numbers, and endorsement titles when available.`,
4478
+ coverage_comparison: `You are comparing coverages across insurance documents.
4479
+
4480
+ RULES:
4481
+ - Answer ONLY from the evidence provided.
4482
+ - Structure your comparison around specific coverage attributes: limits, deductibles, forms, triggers.
4483
+ - Note differences clearly: "Policy A has X, while Policy B has Y."
4484
+ - Flag where one document has coverage the other lacks entirely.
4485
+ - If evidence for one side of the comparison is missing, state that explicitly.`,
4486
+ document_search: `You are helping locate a specific insurance document.
4487
+
4488
+ RULES:
4489
+ - Answer ONLY from the evidence provided.
4490
+ - Identify the document by carrier, policy/quote number, insured name, and effective dates.
4491
+ - If multiple documents match, list them with distinguishing details.
4492
+ - If no documents match, say so clearly.`,
4493
+ claims_inquiry: `You are answering a question about claims history or loss experience.
4494
+
4495
+ RULES:
4496
+ - Answer ONLY from the evidence provided.
4497
+ - Reference specific claim dates, amounts, descriptions, and statuses.
4498
+ - Include experience modification factors if available.
4499
+ - Be precise with dollar amounts and dates \u2014 do not approximate.
4500
+ - If the evidence shows no claims, state that explicitly.`,
4501
+ general_knowledge: `You are answering a general insurance question using available document context.
4502
+
4503
+ RULES:
4504
+ - You may use general insurance knowledge to frame your answer.
4505
+ - If the question can be answered from the evidence, prefer that over general knowledge.
4506
+ - When mixing general knowledge with document-specific data, make the distinction clear.
4507
+ - Still cite evidence when referencing specific documents.`
4508
+ };
4509
+ function buildReasonPrompt(subQuestion, intent, evidence) {
4510
+ return `${INTENT_INSTRUCTIONS[intent]}
4511
+
4512
+ SUB-QUESTION:
4513
+ ${subQuestion}
4514
+
4515
+ EVIDENCE:
4516
+ ${evidence}
4517
+
4518
+ Answer the sub-question based on the evidence above. For every factual claim, include a citation referencing the source evidence item by its chunkId or documentId. Rate your confidence from 0 to 1 based on how well the evidence supports your answer. Set needsMoreContext to true if the evidence was insufficient.`;
4519
+ }
4520
+
4521
+ // src/query/reasoner.ts
4522
+ async function reason(subQuestion, intent, evidence, config) {
4523
+ const { generateObject, providerOptions } = config;
4524
+ const evidenceText = evidence.map((e, i) => {
4525
+ const sourceLabel = e.source === "chunk" ? `[chunk:${e.chunkId}]` : e.source === "document" ? `[doc:${e.documentId}]` : `[turn:${e.turnId}]`;
4526
+ return `Evidence ${i + 1} ${sourceLabel} (relevance: ${e.relevance.toFixed(2)}):
4527
+ ${e.text}`;
4528
+ }).join("\n\n");
4529
+ const prompt = buildReasonPrompt(subQuestion, intent, evidenceText);
4530
+ const { object, usage } = await withRetry(
4531
+ () => generateObject({
4532
+ prompt,
4533
+ schema: SubAnswerSchema,
4534
+ maxTokens: 4096,
4535
+ providerOptions
4536
+ })
4537
+ );
4538
+ return { subAnswer: object, usage };
4539
+ }
4540
+
4541
+ // src/prompts/query/verify.ts
4542
+ function buildVerifyPrompt(originalQuestion, subAnswersJson, evidenceJson) {
4543
+ return `You are a verification agent for an insurance document intelligence system. Your job is to check that answers are accurate, grounded, and complete.
4544
+
4545
+ ORIGINAL QUESTION:
4546
+ ${originalQuestion}
4547
+
4548
+ SUB-ANSWERS:
4549
+ ${subAnswersJson}
4550
+
4551
+ AVAILABLE EVIDENCE:
4552
+ ${evidenceJson}
4553
+
4554
+ CHECK EACH SUB-ANSWER FOR:
4555
+
4556
+ 1. GROUNDING: Every factual claim must be supported by a citation that references actual evidence. Flag any claim that:
4557
+ - Has no citation
4558
+ - Cites a source that doesn't actually contain the claimed information
4559
+ - Extrapolates beyond what the evidence states
4560
+
4561
+ 2. CONSISTENCY: Sub-answers should not contradict each other. Flag any contradictions, noting which sub-answers conflict and what the discrepancy is.
4562
+
4563
+ 3. COMPLETENESS: Did each sub-question get an adequate answer? Flag any sub-question where:
4564
+ - The answer is vague or hedged when the evidence supports a specific answer
4565
+ - Important details from the evidence were omitted
4566
+ - The confidence rating seems miscalibrated (high confidence with weak evidence, or low confidence with strong evidence)
4567
+
4568
+ RESPOND WITH:
4569
+ - approved: true only if ALL sub-answers pass all three checks
4570
+ - issues: list every specific issue found (empty array if approved)
4571
+ - retrySubQuestions: sub-questions that need re-retrieval or re-reasoning (only if not approved)`;
4572
+ }
4573
+
4574
+ // src/query/verifier.ts
4575
+ async function verify(originalQuestion, subAnswers, allEvidence, config) {
4576
+ const { generateObject, providerOptions } = config;
4577
+ const subAnswersJson = JSON.stringify(
4578
+ subAnswers.map((sa) => ({
4579
+ subQuestion: sa.subQuestion,
4580
+ answer: sa.answer,
4581
+ citations: sa.citations,
4582
+ confidence: sa.confidence,
4583
+ needsMoreContext: sa.needsMoreContext
4584
+ })),
4585
+ null,
4586
+ 2
4587
+ );
4588
+ const evidenceJson = JSON.stringify(
4589
+ allEvidence.map((e) => ({
4590
+ source: e.source,
4591
+ id: e.chunkId ?? e.documentId ?? e.turnId,
4592
+ text: e.text.slice(0, 500),
4593
+ // Truncate for context efficiency
4594
+ relevance: e.relevance
4595
+ })),
4596
+ null,
4597
+ 2
4598
+ );
4599
+ const prompt = buildVerifyPrompt(originalQuestion, subAnswersJson, evidenceJson);
4600
+ const { object, usage } = await withRetry(
4601
+ () => generateObject({
4602
+ prompt,
4603
+ schema: VerifyResultSchema,
4604
+ maxTokens: 2048,
4605
+ providerOptions
4606
+ })
4607
+ );
4608
+ return { result: object, usage };
4609
+ }
4610
+
4611
+ // src/query/coordinator.ts
4612
+ function createQueryAgent(config) {
4613
+ const {
4614
+ generateText,
4615
+ generateObject,
4616
+ documentStore,
4617
+ memoryStore,
4618
+ concurrency = 3,
4619
+ maxVerifyRounds = 1,
4620
+ retrievalLimit = 10,
4621
+ onTokenUsage,
4622
+ onProgress,
4623
+ log,
4624
+ providerOptions
4625
+ } = config;
4626
+ const limit = pLimit(concurrency);
4627
+ let totalUsage = { inputTokens: 0, outputTokens: 0 };
4628
+ function trackUsage(usage) {
4629
+ if (usage) {
4630
+ totalUsage.inputTokens += usage.inputTokens;
4631
+ totalUsage.outputTokens += usage.outputTokens;
4632
+ onTokenUsage?.(usage);
4633
+ }
4634
+ }
4635
+ async function query(input) {
4636
+ totalUsage = { inputTokens: 0, outputTokens: 0 };
4637
+ const { question, conversationId, context } = input;
4638
+ onProgress?.("Classifying query...");
4639
+ const classification = await classify(question, conversationId);
4640
+ onProgress?.(`Retrieving evidence for ${classification.subQuestions.length} sub-question(s)...`);
4641
+ const retrieverConfig = {
4642
+ documentStore,
4643
+ memoryStore,
4644
+ retrievalLimit,
4645
+ log
4646
+ };
4647
+ const retrievalResults = await Promise.all(
4648
+ classification.subQuestions.map(
4649
+ (sq) => limit(() => retrieve(sq, conversationId, retrieverConfig))
4650
+ )
4651
+ );
4652
+ const allEvidence = retrievalResults.flatMap((r) => r.evidence);
4653
+ onProgress?.("Reasoning over evidence...");
4654
+ const reasonerConfig = { generateObject, providerOptions };
4655
+ let subAnswers = await Promise.all(
4656
+ classification.subQuestions.map(
4657
+ (sq, i) => limit(async () => {
4658
+ const { subAnswer, usage } = await reason(
4659
+ sq.question,
4660
+ sq.intent,
4661
+ retrievalResults[i].evidence,
4662
+ reasonerConfig
4663
+ );
4664
+ trackUsage(usage);
4665
+ return subAnswer;
4666
+ })
4667
+ )
4668
+ );
4669
+ onProgress?.("Verifying answer grounding...");
4670
+ const verifierConfig = { generateObject, providerOptions };
4671
+ for (let round = 0; round < maxVerifyRounds; round++) {
4672
+ const { result: verifyResult, usage } = await verify(
4673
+ question,
4674
+ subAnswers,
4675
+ allEvidence,
4676
+ verifierConfig
4677
+ );
4678
+ trackUsage(usage);
4679
+ if (verifyResult.approved) {
4680
+ onProgress?.("Verification passed.");
4681
+ break;
4682
+ }
4683
+ onProgress?.(`Verification found ${verifyResult.issues.length} issue(s), round ${round + 1}/${maxVerifyRounds}`);
4684
+ await log?.(`Verify issues: ${verifyResult.issues.join("; ")}`);
4685
+ if (verifyResult.retrySubQuestions?.length) {
4686
+ const retryQuestions = classification.subQuestions.filter(
4687
+ (sq) => verifyResult.retrySubQuestions.includes(sq.question)
4688
+ );
4689
+ if (retryQuestions.length > 0) {
4690
+ const retryRetrievals = await Promise.all(
4691
+ retryQuestions.map(
4692
+ (sq) => limit(
4693
+ () => retrieve(sq, conversationId, {
4694
+ ...retrieverConfig,
4695
+ retrievalLimit: retrievalLimit * 2
4696
+ // Broader retrieval on retry
4697
+ })
4698
+ )
4699
+ )
4700
+ );
4701
+ for (const r of retryRetrievals) {
4702
+ allEvidence.push(...r.evidence);
4703
+ }
4704
+ const retrySubAnswers = await Promise.all(
4705
+ retryQuestions.map(
4706
+ (sq, i) => limit(async () => {
4707
+ const { subAnswer, usage: u } = await reason(
4708
+ sq.question,
4709
+ sq.intent,
4710
+ retryRetrievals[i].evidence,
4711
+ reasonerConfig
4712
+ );
4713
+ trackUsage(u);
4714
+ return subAnswer;
4715
+ })
4716
+ )
4717
+ );
4718
+ const retryQSet = new Set(retryQuestions.map((sq) => sq.question));
4719
+ subAnswers = subAnswers.map((sa) => {
4720
+ if (retryQSet.has(sa.subQuestion)) {
4721
+ const replacement = retrySubAnswers.find((r) => r.subQuestion === sa.subQuestion);
4722
+ return replacement ?? sa;
4723
+ }
4724
+ return sa;
4725
+ });
4726
+ }
4727
+ }
4728
+ }
4729
+ onProgress?.("Composing final answer...");
4730
+ const queryResult = await respond(
4731
+ question,
4732
+ subAnswers,
4733
+ classification,
4734
+ context?.platform
4735
+ );
4736
+ if (conversationId) {
4737
+ try {
4738
+ await memoryStore.addTurn({
4739
+ id: `turn-${Date.now()}-q`,
4740
+ conversationId,
4741
+ role: "user",
4742
+ content: question,
4743
+ timestamp: Date.now()
4744
+ });
4745
+ await memoryStore.addTurn({
4746
+ id: `turn-${Date.now()}-a`,
4747
+ conversationId,
4748
+ role: "assistant",
4749
+ content: queryResult.answer,
4750
+ timestamp: Date.now()
4751
+ });
4752
+ } catch (e) {
4753
+ await log?.(`Failed to store conversation turn: ${e}`);
4754
+ }
4755
+ }
4756
+ return { ...queryResult, tokenUsage: totalUsage };
4757
+ }
4758
+ async function classify(question, conversationId) {
4759
+ let conversationContext;
4760
+ if (conversationId) {
4761
+ try {
4762
+ const history = await memoryStore.getHistory(conversationId, { limit: 5 });
4763
+ if (history.length > 0) {
4764
+ conversationContext = history.map((t) => `[${t.role}]: ${t.content}`).join("\n");
4765
+ }
4766
+ } catch {
4767
+ }
4768
+ }
4769
+ const prompt = buildQueryClassifyPrompt(question, conversationContext);
4770
+ const { object, usage } = await withRetry(
4771
+ () => generateObject({
4772
+ prompt,
4773
+ schema: QueryClassifyResultSchema,
4774
+ maxTokens: 2048,
4775
+ providerOptions
4776
+ })
4777
+ );
4778
+ trackUsage(usage);
4779
+ return object;
4780
+ }
4781
+ async function respond(originalQuestion, subAnswers, classification, platform) {
4782
+ const subAnswersJson = JSON.stringify(
4783
+ subAnswers.map((sa) => ({
4784
+ subQuestion: sa.subQuestion,
4785
+ answer: sa.answer,
4786
+ citations: sa.citations,
4787
+ confidence: sa.confidence,
4788
+ needsMoreContext: sa.needsMoreContext
4789
+ })),
4790
+ null,
4791
+ 2
4792
+ );
4793
+ const prompt = buildRespondPrompt(originalQuestion, subAnswersJson, platform);
4794
+ const { object, usage } = await withRetry(
4795
+ () => generateObject({
4796
+ prompt,
4797
+ schema: QueryResultSchema,
4798
+ maxTokens: 4096,
4799
+ providerOptions
4800
+ })
4801
+ );
4802
+ trackUsage(usage);
4803
+ const result = object;
4804
+ result.intent = classification.intent;
4805
+ return result;
4806
+ }
4807
+ return { query };
4808
+ }
4809
+
3558
4810
  // src/prompts/intent.ts
3559
4811
  function buildClassifyMessagePrompt(platform) {
3560
4812
  const platformFields = {
@@ -3680,9 +4932,16 @@ export {
3680
4932
  AGENT_TOOLS,
3681
4933
  APPLICATION_CLASSIFY_PROMPT,
3682
4934
  AUDIT_TYPES,
4935
+ AcroFormMappingSchema,
3683
4936
  AddressSchema,
3684
4937
  AdmittedStatusSchema,
4938
+ AnswerParsingResultSchema,
4939
+ ApplicationClassifyResultSchema,
4940
+ ApplicationFieldSchema,
4941
+ ApplicationStateSchema,
3685
4942
  AuditTypeSchema,
4943
+ AutoFillMatchSchema,
4944
+ AutoFillResultSchema,
3686
4945
  BOAT_TYPES,
3687
4946
  BindingAuthoritySchema,
3688
4947
  BoatTypeSchema,
@@ -3696,6 +4955,7 @@ export {
3696
4955
  COVERAGE_FORMS,
3697
4956
  COVERAGE_TRIGGERS,
3698
4957
  ChunkTypeSchema,
4958
+ CitationSchema,
3699
4959
  ClaimRecordSchema,
3700
4960
  ClaimStatusSchema,
3701
4961
  ClassificationCodeSchema,
@@ -3738,12 +4998,16 @@ export {
3738
4998
  EnrichedSubjectivitySchema,
3739
4999
  EnrichedUnderwritingConditionSchema,
3740
5000
  EntityTypeSchema,
5001
+ EvidenceItemSchema,
3741
5002
  ExclusionSchema,
3742
5003
  ExperienceModSchema,
3743
5004
  ExtendedReportingPeriodSchema,
3744
5005
  FLOOD_ZONES,
3745
5006
  FOUNDATION_TYPES,
3746
5007
  FarmRanchDeclarationsSchema,
5008
+ FieldExtractionResultSchema,
5009
+ FieldTypeSchema,
5010
+ FlatPdfPlacementSchema,
3747
5011
  FloodDeclarationsSchema,
3748
5012
  FloodZoneSchema,
3749
5013
  FormReferenceSchema,
@@ -3762,6 +5026,9 @@ export {
3762
5026
  LimitScheduleSchema,
3763
5027
  LimitTypeSchema,
3764
5028
  LocationPremiumSchema,
5029
+ LookupFillResultSchema,
5030
+ LookupFillSchema,
5031
+ LookupRequestSchema,
3765
5032
  LossSettlementSchema,
3766
5033
  LossSummarySchema,
3767
5034
  NamedInsuredSchema,
@@ -3771,6 +5038,7 @@ export {
3771
5038
  POLICY_SECTION_TYPES,
3772
5039
  POLICY_TERM_TYPES,
3773
5040
  POLICY_TYPES,
5041
+ ParsedAnswerSchema,
3774
5042
  PaymentInstallmentSchema,
3775
5043
  PaymentPlanSchema,
3776
5044
  PersonalArticlesDeclarationsSchema,
@@ -3790,6 +5058,10 @@ export {
3790
5058
  ProducerInfoSchema,
3791
5059
  ProfessionalLiabilityDeclarationsSchema,
3792
5060
  QUOTE_SECTION_TYPES,
5061
+ QueryClassifyResultSchema,
5062
+ QueryIntentSchema,
5063
+ QueryResultSchema,
5064
+ QuestionBatchResultSchema,
3793
5065
  QuoteDocumentSchema,
3794
5066
  QuoteSectionTypeSchema,
3795
5067
  RATING_BASIS_TYPES,
@@ -3799,12 +5071,16 @@ export {
3799
5071
  RatingBasisSchema,
3800
5072
  RatingBasisTypeSchema,
3801
5073
  RecreationalVehicleDeclarationsSchema,
5074
+ ReplyIntentSchema,
5075
+ RetrievalResultSchema,
3802
5076
  RoofTypeSchema,
3803
5077
  SCHEDULED_ITEM_CATEGORIES,
3804
5078
  SUBJECTIVITY_CATEGORIES,
3805
5079
  ScheduledItemCategorySchema,
3806
5080
  SectionSchema,
3807
5081
  SharedLimitSchema,
5082
+ SubAnswerSchema,
5083
+ SubQuestionSchema,
3808
5084
  SubjectivityCategorySchema,
3809
5085
  SubjectivitySchema,
3810
5086
  SublimitSchema,
@@ -3821,6 +5097,7 @@ export {
3821
5097
  ValuationMethodSchema,
3822
5098
  VehicleCoverageSchema,
3823
5099
  VehicleCoverageTypeSchema,
5100
+ VerifyResultSchema,
3824
5101
  WatercraftDeclarationsSchema,
3825
5102
  WorkersCompDeclarationsSchema,
3826
5103
  buildAcroFormMappingPrompt,
@@ -3840,12 +5117,18 @@ export {
3840
5117
  buildIdentityPrompt,
3841
5118
  buildIntentPrompt,
3842
5119
  buildLookupFillPrompt,
5120
+ buildQueryClassifyPrompt,
3843
5121
  buildQuestionBatchPrompt,
3844
5122
  buildQuotesPoliciesPrompt,
5123
+ buildReasonPrompt,
3845
5124
  buildReplyIntentClassificationPrompt,
5125
+ buildRespondPrompt,
3846
5126
  buildSafetyPrompt,
5127
+ buildVerifyPrompt,
3847
5128
  chunkDocument,
5129
+ createApplicationPipeline,
3848
5130
  createExtractor,
5131
+ createQueryAgent,
3849
5132
  extractPageRange,
3850
5133
  fillAcroForm,
3851
5134
  getAcroFormFields,