whale-code 6.5.8 → 6.5.10

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.
Files changed (101) hide show
  1. package/dist/cli/services/agent-loop.js +26 -2
  2. package/dist/cli/services/agent-loop.js.map +1 -1
  3. package/dist/cli/services/config-store.js +2 -3
  4. package/dist/cli/services/config-store.js.map +1 -1
  5. package/dist/cli/services/hooks.js +2 -1
  6. package/dist/cli/services/hooks.js.map +1 -1
  7. package/dist/cli/services/telemetry-spans.js +1 -0
  8. package/dist/cli/services/telemetry-spans.js.map +1 -1
  9. package/dist/cli/services/telemetry.d.ts +23 -0
  10. package/dist/cli/services/telemetry.js +45 -1
  11. package/dist/cli/services/telemetry.js.map +1 -1
  12. package/dist/server/handlers/__test-utils__/test-db.d.ts +17 -3
  13. package/dist/server/handlers/__test-utils__/test-db.js +113 -14
  14. package/dist/server/handlers/__test-utils__/test-db.js.map +1 -1
  15. package/dist/server/handlers/affiliates.d.ts +9 -0
  16. package/dist/server/handlers/affiliates.js +197 -0
  17. package/dist/server/handlers/affiliates.js.map +1 -0
  18. package/dist/server/handlers/api-docs.d.ts +4 -2
  19. package/dist/server/handlers/api-docs.js +204 -1681
  20. package/dist/server/handlers/api-docs.js.map +1 -1
  21. package/dist/server/handlers/campaigns.d.ts +9 -0
  22. package/dist/server/handlers/campaigns.js +237 -0
  23. package/dist/server/handlers/campaigns.js.map +1 -0
  24. package/dist/server/handlers/catalog-schemas.js +9 -9
  25. package/dist/server/handlers/catalog-schemas.js.map +1 -1
  26. package/dist/server/handlers/catalog.js +1 -1
  27. package/dist/server/handlers/catalog.js.map +1 -1
  28. package/dist/server/handlers/comms-documents.js +28 -2
  29. package/dist/server/handlers/comms-documents.js.map +1 -1
  30. package/dist/server/handlers/comms-pdf-generation.js +25 -3
  31. package/dist/server/handlers/comms-pdf-generation.js.map +1 -1
  32. package/dist/server/handlers/comms-pdf-helpers.js +4 -4
  33. package/dist/server/handlers/comms-pdf-helpers.js.map +1 -1
  34. package/dist/server/handlers/comms.d.ts +100 -0
  35. package/dist/server/handlers/comms.js +146 -12
  36. package/dist/server/handlers/comms.js.map +1 -1
  37. package/dist/server/handlers/coupons.d.ts +9 -0
  38. package/dist/server/handlers/coupons.js +220 -0
  39. package/dist/server/handlers/coupons.js.map +1 -0
  40. package/dist/server/handlers/embeddings.js +1 -1
  41. package/dist/server/handlers/embeddings.js.map +1 -1
  42. package/dist/server/handlers/enrichment.js +2 -622
  43. package/dist/server/handlers/enrichment.js.map +1 -1
  44. package/dist/server/handlers/fulfillment.d.ts +9 -0
  45. package/dist/server/handlers/fulfillment.js +209 -0
  46. package/dist/server/handlers/fulfillment.js.map +1 -0
  47. package/dist/server/handlers/google-ads.d.ts +24 -0
  48. package/dist/server/handlers/google-ads.js +2199 -0
  49. package/dist/server/handlers/google-ads.js.map +1 -0
  50. package/dist/server/handlers/invoices.d.ts +9 -0
  51. package/dist/server/handlers/invoices.js +252 -0
  52. package/dist/server/handlers/invoices.js.map +1 -0
  53. package/dist/server/handlers/loyalty.d.ts +9 -0
  54. package/dist/server/handlers/loyalty.js +197 -0
  55. package/dist/server/handlers/loyalty.js.map +1 -0
  56. package/dist/server/handlers/meta-ads-graph-api.js +18 -3
  57. package/dist/server/handlers/meta-ads-graph-api.js.map +1 -1
  58. package/dist/server/handlers/phone.d.ts +9 -0
  59. package/dist/server/handlers/phone.js +197 -0
  60. package/dist/server/handlers/phone.js.map +1 -0
  61. package/dist/server/handlers/pipeline.d.ts +9 -0
  62. package/dist/server/handlers/pipeline.js +277 -0
  63. package/dist/server/handlers/pipeline.js.map +1 -0
  64. package/dist/server/handlers/qr-codes.d.ts +9 -0
  65. package/dist/server/handlers/qr-codes.js +198 -0
  66. package/dist/server/handlers/qr-codes.js.map +1 -0
  67. package/dist/server/handlers/reviews.d.ts +9 -0
  68. package/dist/server/handlers/reviews.js +171 -0
  69. package/dist/server/handlers/reviews.js.map +1 -0
  70. package/dist/server/handlers/segments.d.ts +9 -0
  71. package/dist/server/handlers/segments.js +229 -0
  72. package/dist/server/handlers/segments.js.map +1 -0
  73. package/dist/server/handlers/social.d.ts +9 -0
  74. package/dist/server/handlers/social.js +81 -0
  75. package/dist/server/handlers/social.js.map +1 -0
  76. package/dist/server/handlers/tax.d.ts +9 -0
  77. package/dist/server/handlers/tax.js +182 -0
  78. package/dist/server/handlers/tax.js.map +1 -0
  79. package/dist/server/handlers/wallet.d.ts +9 -0
  80. package/dist/server/handlers/wallet.js +203 -0
  81. package/dist/server/handlers/wallet.js.map +1 -0
  82. package/dist/server/handlers/webhooks-mgmt.d.ts +9 -0
  83. package/dist/server/handlers/webhooks-mgmt.js +181 -0
  84. package/dist/server/handlers/webhooks-mgmt.js.map +1 -0
  85. package/dist/server/handlers/wholesale.d.ts +9 -0
  86. package/dist/server/handlers/wholesale.js +219 -0
  87. package/dist/server/handlers/wholesale.js.map +1 -0
  88. package/dist/server/index.js +20 -9
  89. package/dist/server/index.js.map +1 -1
  90. package/dist/server/lib/clickhouse-buffer.js +1 -0
  91. package/dist/server/lib/clickhouse-buffer.js.map +1 -1
  92. package/dist/server/lib/coa-renderer.d.ts +1 -1
  93. package/dist/server/lib/coa-renderer.js +32 -10
  94. package/dist/server/lib/coa-renderer.js.map +1 -1
  95. package/dist/server/server-worker.d.ts +1 -0
  96. package/dist/server/server-worker.js +464 -3
  97. package/dist/server/server-worker.js.map +1 -1
  98. package/dist/server/tool-router.js +118 -4
  99. package/dist/server/tool-router.js.map +1 -1
  100. package/package.json +26 -3
  101. package/vendor/ink/package.json +0 -2
@@ -123,6 +123,7 @@ export function auditRowToSpan(row) {
123
123
  return {
124
124
  span_id: row.span_id || randomHexId(),
125
125
  trace_id: row.trace_id || row.request_id || randomUUID(),
126
+ parent_span_id: row.parent_span_id || details.parent_span_id || undefined,
126
127
  store_id: row.store_id || undefined,
127
128
  service_name: row.service_name || "agent-server",
128
129
  operation_name: row.action || undefined,
@@ -1 +1 @@
1
- {"version":3,"file":"clickhouse-buffer.js","names":["randomUUID","getClickHouseClient","spanBuffer","FLUSH_INTERVAL","FLUSH_MAX","flushTimer","queueSpan","span","push","length","flushSpans","setTimeout","clearTimeout","batch","splice","client","insert","auditRowToSpan","row","details","now","Date","toISOString","input_tokens","detailInputTokens","output_tokens","detailOutputTokens","total_cost","detailCost","model","detailModel","genAiModel","genAiInput","genAiOutput","genAiCost","genAiCacheRead","genAiCacheCreation","cache_read_tokens","detailCacheRead","cache_creation_tokens","detailCacheCreation","stop_reason","detailStopReason","turn_number","detailTurnNumber","turn_count","detailTurnCount","parent_conversation_id","detailParentConvId","input_bytes","detailInputBytes","output_bytes","detailOutputBytes","error_type","detailErrorType","retryable","detailRetryable","remainingDetails","toolName","resource_type","resource_id","undefined","tool_name","promptTokens","completionTokens","tokenCostUsd","modelName","span_id","randomHexId","trace_id","request_id","store_id","service_name","operation_name","action","span_kind","source","started_at","start_time","ended_at","end_time","duration_ms","status_code","severity","model_name","prompt_tokens","completion_tokens","total_tokens","token_cost_usd","agent_id","conversation_id","error_message","attributes","Object","keys","JSON","stringify","user_id","environment","user_email","classifyErrorType","errorMessage","lower","toLowerCase","includes","bytes","Uint8Array","crypto","getRandomValues","Array","from","map","b","toString","padStart","join"],"sources":["../../../src/server/lib/clickhouse-buffer.ts"],"sourcesContent":["/**\n * ClickHouse Span Buffer — batches spans for bulk insert to ClickHouse ai_spans.\n *\n * Same batching semantics (500ms / 100 records).\n * Also provides auditRowToSpan() to convert legacy row shapes to ClickHouseSpan.\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport { type ClickHouseSpan, getClickHouseClient } from \"./clickhouse-client.js\";\n\n// ============================================================================\n// Buffer config\n// ============================================================================\n\nconst spanBuffer: ClickHouseSpan[] = [];\nconst FLUSH_INTERVAL = 500; // ms\nconst FLUSH_MAX = 100; // max records before force flush\nlet flushTimer: ReturnType<typeof setTimeout> | null = null;\n\n// ============================================================================\n// Public API\n// ============================================================================\n\n/**\n * Queue a span for batch insert to ClickHouse.\n */\nexport function queueSpan(span: ClickHouseSpan): void {\n spanBuffer.push(span);\n\n if (spanBuffer.length >= FLUSH_MAX) {\n flushSpans();\n } else if (!flushTimer) {\n flushTimer = setTimeout(() => {\n flushTimer = null;\n flushSpans();\n }, FLUSH_INTERVAL);\n }\n}\n\n/**\n * Flush all buffered spans to ClickHouse.\n * Call this on server shutdown or at end of request processing.\n */\nexport async function flushSpans(): Promise<void> {\n if (flushTimer) {\n clearTimeout(flushTimer);\n flushTimer = null;\n }\n\n if (spanBuffer.length > 0) {\n const batch = spanBuffer.splice(0, spanBuffer.length);\n const client = getClickHouseClient();\n await client.insert(\"ai_spans\", batch as unknown as Record<string, unknown>[]);\n }\n}\n\n// ============================================================================\n// Mapper: audit_log row → ClickHouseSpan\n// ============================================================================\n\n/**\n * Convert an existing audit_log row to a ClickHouseSpan.\n *\n * Field mapping:\n * ai_spans.action → operation_name\n * ai_spans.source → source\n * ai_spans.severity → severity\n * ai_spans.store_id → store_id\n * ai_spans.duration_ms → duration_ms\n * ai_spans.error_message → error_message\n * ai_spans.trace_id → trace_id\n * ai_spans.span_id → span_id\n * ai_spans.span_kind → span_kind\n * ai_spans.service_name → service_name\n * ai_spans.status_code → status_code\n * ai_spans.start_time → started_at\n * ai_spans.end_time → ended_at\n * ai_spans.conversation_id → conversation_id\n * ai_spans.user_id → user_id\n * ai_spans.input_tokens → prompt_tokens\n * ai_spans.output_tokens → completion_tokens\n * ai_spans.total_cost → token_cost_usd\n * ai_spans.model → model_name\n * ai_spans.resource_id (mcp_tool) → tool_name\n * ai_spans.details (remaining) → attributes (JSON string)\n */\nexport function auditRowToSpan(row: Record<string, unknown>): ClickHouseSpan {\n const details = (row.details || {}) as Record<string, unknown>;\n const now = new Date().toISOString();\n\n // Extract known fields from details that map to top-level ClickHouse columns\n const {\n input_tokens: detailInputTokens,\n output_tokens: detailOutputTokens,\n total_cost: detailCost,\n model: detailModel,\n \"gen_ai.request.model\": genAiModel,\n \"gen_ai.usage.input_tokens\": genAiInput,\n \"gen_ai.usage.output_tokens\": genAiOutput,\n \"gen_ai.usage.cost\": genAiCost,\n \"gen_ai.usage.cache_read_tokens\": genAiCacheRead,\n \"gen_ai.usage.cache_creation_tokens\": genAiCacheCreation,\n cache_read_tokens: detailCacheRead,\n cache_creation_tokens: detailCacheCreation,\n stop_reason: detailStopReason,\n turn_number: detailTurnNumber,\n turn_count: detailTurnCount,\n parent_conversation_id: detailParentConvId,\n input_bytes: detailInputBytes,\n output_bytes: detailOutputBytes,\n error_type: detailErrorType,\n retryable: detailRetryable,\n ...remainingDetails\n } = details;\n\n // Determine tool_name from resource_id when it's an mcp_tool\n const toolName = row.resource_type === \"mcp_tool\"\n ? (row.resource_id as string) || undefined\n : (details.tool_name as string) || undefined;\n\n // Compute token fields — prefer top-level, fall back to details.\n // Use ?? (not ||) for numeric fields so that 0 is not treated as falsy.\n const promptTokens = (row.input_tokens as number)\n ?? (genAiInput as number)\n ?? (detailInputTokens as number)\n ?? undefined;\n const completionTokens = (row.output_tokens as number)\n ?? (genAiOutput as number)\n ?? (detailOutputTokens as number)\n ?? undefined;\n const tokenCostUsd = (row.total_cost as number)\n ?? (genAiCost as number)\n ?? (detailCost as number)\n ?? undefined;\n const modelName = (row.model as string)\n || (genAiModel as string)\n || (detailModel as string)\n || undefined;\n\n return {\n span_id: (row.span_id as string) || randomHexId(),\n trace_id: (row.trace_id as string) || (row.request_id as string) || randomUUID(),\n store_id: (row.store_id as string) || undefined,\n service_name: (row.service_name as string) || \"agent-server\",\n operation_name: (row.action as string) || undefined,\n span_kind: (row.span_kind as string) || \"INTERNAL\",\n source: (row.source as string) || undefined,\n started_at: (row.start_time as string) || now,\n ended_at: (row.end_time as string) || now,\n duration_ms: (row.duration_ms as number) ?? 0,\n status_code: (row.status_code as string) || \"OK\",\n severity: (row.severity as string) || \"info\",\n model_name: modelName,\n prompt_tokens: promptTokens,\n completion_tokens: completionTokens,\n total_tokens: promptTokens && completionTokens ? promptTokens + completionTokens : undefined,\n token_cost_usd: tokenCostUsd,\n agent_id: (row.resource_id as string) || (details.agent_id as string) || undefined,\n conversation_id: (row.conversation_id as string) || undefined,\n tool_name: toolName,\n error_message: (row.error_message as string) || undefined,\n attributes: Object.keys(remainingDetails).length > 0 ? JSON.stringify(remainingDetails) : undefined,\n request_id: (row.request_id as string) || undefined,\n user_id: (row.user_id as string) || undefined,\n environment: \"production\",\n // Enriched columns (003_enrich_spans)\n user_email: (row.user_email as string) || undefined,\n error_type: (row.error_type as string) || (detailErrorType as string) || undefined,\n retryable: (detailRetryable as boolean) ? 1 : (row.severity === \"error\" ? 0 : undefined),\n resource_type: (row.resource_type as string) || undefined,\n cache_read_tokens: (genAiCacheRead as number) ?? (detailCacheRead as number) ?? undefined,\n cache_creation_tokens: (genAiCacheCreation as number) ?? (detailCacheCreation as number) ?? undefined,\n input_bytes: (row.input_bytes as number) ?? (detailInputBytes as number) ?? undefined,\n output_bytes: (row.output_bytes as number) ?? (detailOutputBytes as number) ?? undefined,\n stop_reason: (row.stop_reason as string) || (detailStopReason as string) || undefined,\n turn_number: (row.turn_number as number) ?? (detailTurnNumber as number) ?? (detailTurnCount as number) ?? undefined,\n parent_conversation_id: (row.parent_conversation_id as string) || (detailParentConvId as string) || undefined,\n };\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/**\n * Classify an error message into a standard error_type category.\n * Used by auditRowToSpan and callers to populate the error_type column.\n */\nexport function classifyErrorType(errorMessage: string | null | undefined): string | undefined {\n if (!errorMessage) return undefined;\n const lower = errorMessage.toLowerCase();\n if (lower.includes(\"rate limit\") || lower.includes(\"429\")) return \"rate_limit\";\n if (lower.includes(\"auth\") || lower.includes(\"401\") || lower.includes(\"403\")) return \"auth\";\n if (lower.includes(\"timeout\") || lower.includes(\"timed out\")) return \"timeout\";\n if (lower.includes(\"validation\") || lower.includes(\"invalid\")) return \"validation\";\n if (lower.includes(\"not found\") || lower.includes(\"404\")) return \"not_found\";\n if (lower.includes(\"overloaded\") || lower.includes(\"529\")) return \"overloaded\";\n if (lower.includes(\"circuit breaker\")) return \"circuit_breaker\";\n return undefined;\n}\n\nfunction randomHexId(): string {\n const bytes = new Uint8Array(8);\n crypto.getRandomValues(bytes);\n return Array.from(bytes).map(b => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\n"],"mappings":"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","ignoreList":[]}
1
+ {"version":3,"file":"clickhouse-buffer.js","names":["randomUUID","getClickHouseClient","spanBuffer","FLUSH_INTERVAL","FLUSH_MAX","flushTimer","queueSpan","span","push","length","flushSpans","setTimeout","clearTimeout","batch","splice","client","insert","auditRowToSpan","row","details","now","Date","toISOString","input_tokens","detailInputTokens","output_tokens","detailOutputTokens","total_cost","detailCost","model","detailModel","genAiModel","genAiInput","genAiOutput","genAiCost","genAiCacheRead","genAiCacheCreation","cache_read_tokens","detailCacheRead","cache_creation_tokens","detailCacheCreation","stop_reason","detailStopReason","turn_number","detailTurnNumber","turn_count","detailTurnCount","parent_conversation_id","detailParentConvId","input_bytes","detailInputBytes","output_bytes","detailOutputBytes","error_type","detailErrorType","retryable","detailRetryable","remainingDetails","toolName","resource_type","resource_id","undefined","tool_name","promptTokens","completionTokens","tokenCostUsd","modelName","span_id","randomHexId","trace_id","request_id","parent_span_id","store_id","service_name","operation_name","action","span_kind","source","started_at","start_time","ended_at","end_time","duration_ms","status_code","severity","model_name","prompt_tokens","completion_tokens","total_tokens","token_cost_usd","agent_id","conversation_id","error_message","attributes","Object","keys","JSON","stringify","user_id","environment","user_email","classifyErrorType","errorMessage","lower","toLowerCase","includes","bytes","Uint8Array","crypto","getRandomValues","Array","from","map","b","toString","padStart","join"],"sources":["../../../src/server/lib/clickhouse-buffer.ts"],"sourcesContent":["/**\n * ClickHouse Span Buffer — batches spans for bulk insert to ClickHouse ai_spans.\n *\n * Same batching semantics (500ms / 100 records).\n * Also provides auditRowToSpan() to convert legacy row shapes to ClickHouseSpan.\n */\n\nimport { randomUUID } from \"node:crypto\";\nimport { type ClickHouseSpan, getClickHouseClient } from \"./clickhouse-client.js\";\n\n// ============================================================================\n// Buffer config\n// ============================================================================\n\nconst spanBuffer: ClickHouseSpan[] = [];\nconst FLUSH_INTERVAL = 500; // ms\nconst FLUSH_MAX = 100; // max records before force flush\nlet flushTimer: ReturnType<typeof setTimeout> | null = null;\n\n// ============================================================================\n// Public API\n// ============================================================================\n\n/**\n * Queue a span for batch insert to ClickHouse.\n */\nexport function queueSpan(span: ClickHouseSpan): void {\n spanBuffer.push(span);\n\n if (spanBuffer.length >= FLUSH_MAX) {\n flushSpans();\n } else if (!flushTimer) {\n flushTimer = setTimeout(() => {\n flushTimer = null;\n flushSpans();\n }, FLUSH_INTERVAL);\n }\n}\n\n/**\n * Flush all buffered spans to ClickHouse.\n * Call this on server shutdown or at end of request processing.\n */\nexport async function flushSpans(): Promise<void> {\n if (flushTimer) {\n clearTimeout(flushTimer);\n flushTimer = null;\n }\n\n if (spanBuffer.length > 0) {\n const batch = spanBuffer.splice(0, spanBuffer.length);\n const client = getClickHouseClient();\n await client.insert(\"ai_spans\", batch as unknown as Record<string, unknown>[]);\n }\n}\n\n// ============================================================================\n// Mapper: audit_log row → ClickHouseSpan\n// ============================================================================\n\n/**\n * Convert an existing audit_log row to a ClickHouseSpan.\n *\n * Field mapping:\n * ai_spans.action → operation_name\n * ai_spans.source → source\n * ai_spans.severity → severity\n * ai_spans.store_id → store_id\n * ai_spans.duration_ms → duration_ms\n * ai_spans.error_message → error_message\n * ai_spans.trace_id → trace_id\n * ai_spans.span_id → span_id\n * ai_spans.span_kind → span_kind\n * ai_spans.service_name → service_name\n * ai_spans.status_code → status_code\n * ai_spans.start_time → started_at\n * ai_spans.end_time → ended_at\n * ai_spans.conversation_id → conversation_id\n * ai_spans.user_id → user_id\n * ai_spans.input_tokens → prompt_tokens\n * ai_spans.output_tokens → completion_tokens\n * ai_spans.total_cost → token_cost_usd\n * ai_spans.model → model_name\n * ai_spans.resource_id (mcp_tool) → tool_name\n * ai_spans.details (remaining) → attributes (JSON string)\n */\nexport function auditRowToSpan(row: Record<string, unknown>): ClickHouseSpan {\n const details = (row.details || {}) as Record<string, unknown>;\n const now = new Date().toISOString();\n\n // Extract known fields from details that map to top-level ClickHouse columns\n const {\n input_tokens: detailInputTokens,\n output_tokens: detailOutputTokens,\n total_cost: detailCost,\n model: detailModel,\n \"gen_ai.request.model\": genAiModel,\n \"gen_ai.usage.input_tokens\": genAiInput,\n \"gen_ai.usage.output_tokens\": genAiOutput,\n \"gen_ai.usage.cost\": genAiCost,\n \"gen_ai.usage.cache_read_tokens\": genAiCacheRead,\n \"gen_ai.usage.cache_creation_tokens\": genAiCacheCreation,\n cache_read_tokens: detailCacheRead,\n cache_creation_tokens: detailCacheCreation,\n stop_reason: detailStopReason,\n turn_number: detailTurnNumber,\n turn_count: detailTurnCount,\n parent_conversation_id: detailParentConvId,\n input_bytes: detailInputBytes,\n output_bytes: detailOutputBytes,\n error_type: detailErrorType,\n retryable: detailRetryable,\n ...remainingDetails\n } = details;\n\n // Determine tool_name from resource_id when it's an mcp_tool\n const toolName = row.resource_type === \"mcp_tool\"\n ? (row.resource_id as string) || undefined\n : (details.tool_name as string) || undefined;\n\n // Compute token fields — prefer top-level, fall back to details.\n // Use ?? (not ||) for numeric fields so that 0 is not treated as falsy.\n const promptTokens = (row.input_tokens as number)\n ?? (genAiInput as number)\n ?? (detailInputTokens as number)\n ?? undefined;\n const completionTokens = (row.output_tokens as number)\n ?? (genAiOutput as number)\n ?? (detailOutputTokens as number)\n ?? undefined;\n const tokenCostUsd = (row.total_cost as number)\n ?? (genAiCost as number)\n ?? (detailCost as number)\n ?? undefined;\n const modelName = (row.model as string)\n || (genAiModel as string)\n || (detailModel as string)\n || undefined;\n\n return {\n span_id: (row.span_id as string) || randomHexId(),\n trace_id: (row.trace_id as string) || (row.request_id as string) || randomUUID(),\n parent_span_id: (row.parent_span_id as string) || (details.parent_span_id as string) || undefined,\n store_id: (row.store_id as string) || undefined,\n service_name: (row.service_name as string) || \"agent-server\",\n operation_name: (row.action as string) || undefined,\n span_kind: (row.span_kind as string) || \"INTERNAL\",\n source: (row.source as string) || undefined,\n started_at: (row.start_time as string) || now,\n ended_at: (row.end_time as string) || now,\n duration_ms: (row.duration_ms as number) ?? 0,\n status_code: (row.status_code as string) || \"OK\",\n severity: (row.severity as string) || \"info\",\n model_name: modelName,\n prompt_tokens: promptTokens,\n completion_tokens: completionTokens,\n total_tokens: promptTokens && completionTokens ? promptTokens + completionTokens : undefined,\n token_cost_usd: tokenCostUsd,\n agent_id: (row.resource_id as string) || (details.agent_id as string) || undefined,\n conversation_id: (row.conversation_id as string) || undefined,\n tool_name: toolName,\n error_message: (row.error_message as string) || undefined,\n attributes: Object.keys(remainingDetails).length > 0 ? JSON.stringify(remainingDetails) : undefined,\n request_id: (row.request_id as string) || undefined,\n user_id: (row.user_id as string) || undefined,\n environment: \"production\",\n // Enriched columns (003_enrich_spans)\n user_email: (row.user_email as string) || undefined,\n error_type: (row.error_type as string) || (detailErrorType as string) || undefined,\n retryable: (detailRetryable as boolean) ? 1 : (row.severity === \"error\" ? 0 : undefined),\n resource_type: (row.resource_type as string) || undefined,\n cache_read_tokens: (genAiCacheRead as number) ?? (detailCacheRead as number) ?? undefined,\n cache_creation_tokens: (genAiCacheCreation as number) ?? (detailCacheCreation as number) ?? undefined,\n input_bytes: (row.input_bytes as number) ?? (detailInputBytes as number) ?? undefined,\n output_bytes: (row.output_bytes as number) ?? (detailOutputBytes as number) ?? undefined,\n stop_reason: (row.stop_reason as string) || (detailStopReason as string) || undefined,\n turn_number: (row.turn_number as number) ?? (detailTurnNumber as number) ?? (detailTurnCount as number) ?? undefined,\n parent_conversation_id: (row.parent_conversation_id as string) || (detailParentConvId as string) || undefined,\n };\n}\n\n// ============================================================================\n// Helpers\n// ============================================================================\n\n/**\n * Classify an error message into a standard error_type category.\n * Used by auditRowToSpan and callers to populate the error_type column.\n */\nexport function classifyErrorType(errorMessage: string | null | undefined): string | undefined {\n if (!errorMessage) return undefined;\n const lower = errorMessage.toLowerCase();\n if (lower.includes(\"rate limit\") || lower.includes(\"429\")) return \"rate_limit\";\n if (lower.includes(\"auth\") || lower.includes(\"401\") || lower.includes(\"403\")) return \"auth\";\n if (lower.includes(\"timeout\") || lower.includes(\"timed out\")) return \"timeout\";\n if (lower.includes(\"validation\") || lower.includes(\"invalid\")) return \"validation\";\n if (lower.includes(\"not found\") || lower.includes(\"404\")) return \"not_found\";\n if (lower.includes(\"overloaded\") || lower.includes(\"529\")) return \"overloaded\";\n if (lower.includes(\"circuit breaker\")) return \"circuit_breaker\";\n return undefined;\n}\n\nfunction randomHexId(): string {\n const bytes = new Uint8Array(8);\n crypto.getRandomValues(bytes);\n return Array.from(bytes).map(b => b.toString(16).padStart(2, \"0\")).join(\"\");\n}\n"],"mappings":"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","ignoreList":[]}
@@ -1,12 +1,12 @@
1
1
  export interface COAData {
2
2
  labName?: string;
3
+ labAddress?: string;
3
4
  labContact?: string;
4
5
  logoUrl?: string;
5
6
  labWebsite?: string;
6
7
  labDirector?: string;
7
8
  directorTitle?: string;
8
9
  signatureUrl?: string;
9
- approvalDate?: string;
10
10
  sampleName: string;
11
11
  sampleId: string;
12
12
  strain?: string;
@@ -750,9 +750,15 @@ const PanelPageHeader = ({
750
750
  style: styles.companySubname
751
751
  }, (data.labName || "").split(" ").slice(1).join(" ") || "")), e(View, {
752
752
  style: styles.divider
753
- }), e(Text, {
753
+ }), e(View, null, e(Text, {
754
754
  style: styles.labContact
755
- }, data.labContact || "")), e(Text, {
755
+ }, data.labContact || ""), data.labAddress ? e(Text, {
756
+ style: {
757
+ fontSize: 7,
758
+ color: colors.gray600,
759
+ lineHeight: 1.3
760
+ }
761
+ }, data.labAddress) : null)), e(Text, {
756
762
  style: {
757
763
  ...styles.docTitle,
758
764
  fontSize: 10,
@@ -917,7 +923,7 @@ const PanelPageFooter = ({
917
923
  style: styles.signatureTitle
918
924
  }, data.directorTitle || "Laboratory Director"), e(Text, {
919
925
  style: styles.signatureTitle
920
- }, data.approvalDate || data.dateTested || "\u2014"), e(Text, {
926
+ }, data.dateTested || "\u2014"), e(Text, {
921
927
  style: {
922
928
  fontSize: 7,
923
929
  color: colors.gray500,
@@ -1427,9 +1433,15 @@ const CannabisCOADocument = ({
1427
1433
  style: styles.companySubname
1428
1434
  }, (data.labName || "").split(" ").slice(1).join(" ") || "")), e(View, {
1429
1435
  style: styles.divider
1430
- }), e(Text, {
1436
+ }), e(View, null, e(Text, {
1431
1437
  style: styles.labContact
1432
- }, data.labContact || "")), e(Text, {
1438
+ }, data.labContact || ""), data.labAddress ? e(Text, {
1439
+ style: {
1440
+ fontSize: 7,
1441
+ color: colors.gray600,
1442
+ lineHeight: 1.3
1443
+ }
1444
+ }, data.labAddress) : null)), e(Text, {
1433
1445
  style: styles.docTitle
1434
1446
  }, "Certificate of Analysis")), e(View, {
1435
1447
  style: styles.sampleSection
@@ -1778,14 +1790,24 @@ const CannabisCOADocument = ({
1778
1790
  style: styles.signatureTitle
1779
1791
  }, data.directorTitle || "Laboratory Director"), e(Text, {
1780
1792
  style: styles.signatureTitle
1781
- }, data.approvalDate || data.dateTested || "\u2014"), data.fullPanel ? e(Text, {
1793
+ }, data.dateTested || "\u2014")))), data.fullPanel ? e(View, {
1794
+ style: {
1795
+ position: "absolute",
1796
+ bottom: 20,
1797
+ left: 20,
1798
+ right: 20,
1799
+ flexDirection: "row",
1800
+ justifyContent: "flex-end",
1801
+ borderTopWidth: 1,
1802
+ borderTopColor: colors.gray200,
1803
+ paddingTop: 8
1804
+ }
1805
+ }, e(Text, {
1782
1806
  style: {
1783
1807
  fontSize: 7,
1784
- color: colors.gray500,
1785
- marginTop: 8,
1786
- textAlign: "right"
1808
+ color: colors.gray600
1787
1809
  }
1788
- }, `Page 1 of ${totalPages}`) : null)))));
1810
+ }, `Page 1 of ${totalPages}`)) : null));
1789
1811
  if (data.fullPanel) {
1790
1812
  pages.push(e(SafetyTestingPage, {
1791
1813
  key: "page2",