@lssm/lib.support-bot 0.0.0-canary-20251217080011 → 0.0.0-canary-20251219202229

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Chaman Ventures, SASU
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
@@ -15,4 +15,5 @@ const ContractSpecCallOptionsSchema = z$1.object({
15
15
  metadata: z$1.record(z$1.string(), z$1.unknown()).optional()
16
16
  });
17
17
 
18
- //#endregion
18
+ //#endregion
19
+ //# sourceMappingURL=contract-spec-agent.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"contract-spec-agent.js","names":[],"sources":["../../../../../ai-agent/dist/agent/contract-spec-agent.js"],"sourcesContent":["import { agentKey } from \"../spec/spec.js\";\nimport { specToolsToAISDKTools } from \"../tools/tool-adapter.js\";\nimport { createKnowledgeQueryTool } from \"../tools/knowledge-tool.js\";\nimport { injectStaticKnowledge } from \"../knowledge/injector.js\";\nimport { generateSessionId } from \"../session/store.js\";\nimport { trackAgentStep } from \"../telemetry/adapter.js\";\nimport { Experimental_Agent, stepCountIs } from \"ai\";\nimport * as z$1 from \"zod\";\n\n//#region src/agent/contract-spec-agent.ts\n/**\n* Call options schema for AI SDK v6.\n* Maps ContractSpec's tenant/actor system to AI SDK callOptionsSchema.\n*/\nconst ContractSpecCallOptionsSchema = z$1.object({\n\ttenantId: z$1.string().optional(),\n\tactorId: z$1.string().optional(),\n\tsessionId: z$1.string().optional(),\n\tmetadata: z$1.record(z$1.string(), z$1.unknown()).optional()\n});\n/**\n* ContractSpec Agent implementation using AI SDK v6.\n*\n* Integrates ContractSpec's spec-first governance with AI SDK's\n* ToolLoopAgent, providing:\n* - Spec-defined tools with type-safe handlers\n* - Hybrid knowledge injection (static + dynamic RAG)\n* - Session persistence\n* - Telemetry for evolution\n* - MCP interoperability\n*/\nvar ContractSpecAgent = class ContractSpecAgent {\n\tversion = \"agent-v1\";\n\tid;\n\tspec;\n\ttools;\n\tinner;\n\tconfig;\n\tinstructions;\n\tconstructor(config, instructions, tools) {\n\t\tthis.config = config;\n\t\tthis.spec = config.spec;\n\t\tthis.id = agentKey(config.spec.meta);\n\t\tthis.tools = tools;\n\t\tthis.instructions = instructions;\n\t\tthis.inner = new Experimental_Agent({\n\t\t\tmodel: config.model,\n\t\t\tinstructions,\n\t\t\ttools,\n\t\t\tstopWhen: stepCountIs(config.spec.maxSteps ?? 10),\n\t\t\tcallOptionsSchema: ContractSpecCallOptionsSchema,\n\t\t\tonStepFinish: async (step) => {\n\t\t\t\tawait this.handleStepFinish(step);\n\t\t\t}\n\t\t});\n\t}\n\t/**\n\t* Create a ContractSpecAgent instance.\n\t* This is async because knowledge injection may need to fetch static content.\n\t*/\n\tstatic async create(config) {\n\t\tconst instructions = await injectStaticKnowledge(config.spec.instructions, config.spec.knowledge ?? [], config.knowledgeRetriever);\n\t\tconst specTools = specToolsToAISDKTools(config.spec.tools, config.toolHandlers, { agentId: agentKey(config.spec.meta) });\n\t\tconst knowledgeTool = config.knowledgeRetriever ? createKnowledgeQueryTool(config.knowledgeRetriever, config.spec.knowledge ?? []) : null;\n\t\treturn new ContractSpecAgent(config, instructions, {\n\t\t\t...specTools,\n\t\t\t...knowledgeTool ? { query_knowledge: knowledgeTool } : {},\n\t\t\t...config.additionalTools ?? {}\n\t\t});\n\t}\n\t/**\n\t* Generate a response (non-streaming).\n\t*/\n\tasync generate(params) {\n\t\tconst sessionId = params.options?.sessionId ?? generateSessionId();\n\t\tif (this.config.sessionStore) {\n\t\t\tif (!await this.config.sessionStore.get(sessionId)) await this.config.sessionStore.create({\n\t\t\t\tsessionId,\n\t\t\t\tagentId: this.id,\n\t\t\t\ttenantId: params.options?.tenantId,\n\t\t\t\tactorId: params.options?.actorId,\n\t\t\t\tstatus: \"running\",\n\t\t\t\tmessages: [],\n\t\t\t\tsteps: [],\n\t\t\t\tmetadata: params.options?.metadata\n\t\t\t});\n\t\t}\n\t\tconst prompt = params.systemOverride ? `${this.instructions}\\n\\n${params.systemOverride}\\n\\n${params.prompt}` : params.prompt;\n\t\tconst result = await this.inner.generate({\n\t\t\tprompt,\n\t\t\tabortSignal: params.signal,\n\t\t\toptions: {\n\t\t\t\ttenantId: params.options?.tenantId,\n\t\t\t\tactorId: params.options?.actorId,\n\t\t\t\tsessionId,\n\t\t\t\tmetadata: params.options?.metadata\n\t\t\t}\n\t\t});\n\t\tif (this.config.sessionStore) await this.config.sessionStore.update(sessionId, { status: \"completed\" });\n\t\treturn {\n\t\t\ttext: result.text,\n\t\t\tsteps: result.steps,\n\t\t\ttoolCalls: result.toolCalls.map((tc) => ({\n\t\t\t\ttype: \"tool-call\",\n\t\t\t\ttoolCallId: tc.toolCallId,\n\t\t\t\ttoolName: tc.toolName,\n\t\t\t\targs: \"args\" in tc ? tc.args : \"input\" in tc ? tc.input : void 0\n\t\t\t})),\n\t\t\ttoolResults: result.toolResults.map((tr) => ({\n\t\t\t\ttype: \"tool-result\",\n\t\t\t\ttoolCallId: tr.toolCallId,\n\t\t\t\ttoolName: tr.toolName,\n\t\t\t\toutput: tr.output\n\t\t\t})),\n\t\t\tfinishReason: result.finishReason,\n\t\t\tusage: result.usage\n\t\t};\n\t}\n\t/**\n\t* Stream a response with real-time updates.\n\t*/\n\tasync stream(params) {\n\t\tconst sessionId = params.options?.sessionId ?? generateSessionId();\n\t\tconst prompt = params.systemOverride ? `${this.instructions}\\n\\n${params.systemOverride}\\n\\n${params.prompt}` : params.prompt;\n\t\treturn this.inner.stream({\n\t\t\tprompt,\n\t\t\tabortSignal: params.signal,\n\t\t\toptions: {\n\t\t\t\ttenantId: params.options?.tenantId,\n\t\t\t\tactorId: params.options?.actorId,\n\t\t\t\tsessionId,\n\t\t\t\tmetadata: params.options?.metadata\n\t\t\t}\n\t\t});\n\t}\n\t/**\n\t* Handle step completion for persistence and telemetry.\n\t*/\n\tasync handleStepFinish(step) {\n\t\tconst sessionId = step.options?.sessionId;\n\t\tif (sessionId && this.config.sessionStore) await this.config.sessionStore.appendStep(sessionId, step);\n\t\tif (this.config.telemetryCollector) await trackAgentStep(this.config.telemetryCollector, this.id, step);\n\t}\n};\n\n//#endregion\nexport { ContractSpecAgent };\n//# sourceMappingURL=contract-spec-agent.js.map"],"mappings":";;;;;;;;;;AAcA,MAAM,gCAAgC,IAAI,OAAO;CAChD,UAAU,IAAI,QAAQ,CAAC,UAAU;CACjC,SAAS,IAAI,QAAQ,CAAC,UAAU;CAChC,WAAW,IAAI,QAAQ,CAAC,UAAU;CAClC,UAAU,IAAI,OAAO,IAAI,QAAQ,EAAE,IAAI,SAAS,CAAC,CAAC,UAAU;CAC5D,CAAC"}
@@ -20,4 +20,5 @@ function defineAgent(spec) {
20
20
  }
21
21
 
22
22
  //#endregion
23
- export { defineAgent };
23
+ export { defineAgent };
24
+ //# sourceMappingURL=spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spec.js","names":[],"sources":["../../../../../ai-agent/dist/spec/spec.js"],"sourcesContent":["//#region src/spec/spec.ts\n/**\n* Define and validate an agent specification.\n*\n* @param spec - The agent specification\n* @returns The frozen, validated specification\n* @throws Error if the specification is invalid\n*/\nfunction defineAgent(spec) {\n\tif (!spec.meta?.name) throw new Error(\"Agent name is required\");\n\tif (!Number.isFinite(spec.meta.version)) throw new Error(`Agent ${spec.meta.name} is missing a numeric version`);\n\tif (!spec.instructions?.trim()) throw new Error(`Agent ${spec.meta.name} requires instructions`);\n\tif (!spec.tools?.length) throw new Error(`Agent ${spec.meta.name} must expose at least one tool`);\n\tconst toolNames = /* @__PURE__ */ new Set();\n\tfor (const tool of spec.tools) {\n\t\tif (toolNames.has(tool.name)) throw new Error(`Agent ${spec.meta.name} has duplicate tool name: ${tool.name}`);\n\t\ttoolNames.add(tool.name);\n\t}\n\treturn Object.freeze(spec);\n}\n/**\n* Generate a unique key for an agent spec.\n*/\nfunction agentKey(meta) {\n\treturn `${meta.name}.v${meta.version}`;\n}\n\n//#endregion\nexport { agentKey, defineAgent };\n//# sourceMappingURL=spec.js.map"],"mappings":";;;;;;;;AAQA,SAAS,YAAY,MAAM;AAC1B,KAAI,CAAC,KAAK,MAAM,KAAM,OAAM,IAAI,MAAM,yBAAyB;AAC/D,KAAI,CAAC,OAAO,SAAS,KAAK,KAAK,QAAQ,CAAE,OAAM,IAAI,MAAM,SAAS,KAAK,KAAK,KAAK,+BAA+B;AAChH,KAAI,CAAC,KAAK,cAAc,MAAM,CAAE,OAAM,IAAI,MAAM,SAAS,KAAK,KAAK,KAAK,wBAAwB;AAChG,KAAI,CAAC,KAAK,OAAO,OAAQ,OAAM,IAAI,MAAM,SAAS,KAAK,KAAK,KAAK,gCAAgC;CACjG,MAAM,4BAA4B,IAAI,KAAK;AAC3C,MAAK,MAAM,QAAQ,KAAK,OAAO;AAC9B,MAAI,UAAU,IAAI,KAAK,KAAK,CAAE,OAAM,IAAI,MAAM,SAAS,KAAK,KAAK,KAAK,4BAA4B,KAAK,OAAO;AAC9G,YAAU,IAAI,KAAK,KAAK;;AAEzB,QAAO,OAAO,OAAO,KAAK"}
@@ -22,4 +22,5 @@ declare class AutoResponder {
22
22
  private renderCitations;
23
23
  }
24
24
  //#endregion
25
- export { AutoResponder, AutoResponderOptions };
25
+ export { AutoResponder, AutoResponderOptions };
26
+ //# sourceMappingURL=auto-responder.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-responder.d.ts","names":[],"sources":["../../src/bot/auto-responder.ts"],"sourcesContent":[],"mappings":";;;;UAQiB,oBAAA;QACT;EADS,KAAA,CAAA,EAAA,MAAA;EAOJ,IAAA,CAAA,EAAA,UAAa,GAAA,QAAA;EAMF,OAAA,CAAA,EAAA,MAAA;;AAaR,cAnBH,aAAA,CAmBG;EACI,iBAAA,GAAA;EACP,iBAAA,KAAA;EAAR,iBAAA,IAAA;EAAO,iBAAA,OAAA;wBAfY;gBAYZ,2BACI,mCACI,uBACf,QAAQ"}
@@ -80,4 +80,5 @@ ${this.closing}
80
80
  };
81
81
 
82
82
  //#endregion
83
- export { AutoResponder };
83
+ export { AutoResponder };
84
+ //# sourceMappingURL=auto-responder.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"auto-responder.js","names":[],"sources":["../../src/bot/auto-responder.ts"],"sourcesContent":["import type { LLMProvider } from '@lssm/lib.contracts/integrations/providers/llm';\nimport type {\n SupportResponseDraft,\n SupportResolution,\n SupportTicket,\n TicketClassification,\n} from '../types';\n\nexport interface AutoResponderOptions {\n llm?: LLMProvider;\n model?: string;\n tone?: 'friendly' | 'formal';\n closing?: string;\n}\n\nexport class AutoResponder {\n private readonly llm?: LLMProvider;\n private readonly model?: string;\n private readonly tone: 'friendly' | 'formal';\n private readonly closing: string;\n\n constructor(options?: AutoResponderOptions) {\n this.llm = options?.llm;\n this.model = options?.model;\n this.tone = options?.tone ?? 'friendly';\n this.closing =\n options?.closing ??\n (this.tone === 'friendly'\n ? 'We remain available if you need anything else.'\n : 'Please let us know if you require additional assistance.');\n }\n\n async draft(\n ticket: SupportTicket,\n resolution: SupportResolution,\n classification: TicketClassification\n ): Promise<SupportResponseDraft> {\n if (this.llm) {\n return this.generateWithLLM(ticket, resolution, classification);\n }\n return this.generateTemplate(ticket, resolution, classification);\n }\n\n private async generateWithLLM(\n ticket: SupportTicket,\n resolution: SupportResolution,\n classification: TicketClassification\n ): Promise<SupportResponseDraft> {\n const prompt = `You are a ${this.tone} support agent. Draft an email response.\nTicket Subject: ${ticket.subject}\nTicket Body: ${ticket.body}\nDetected Category: ${classification.category}\nDetected Priority: ${classification.priority}\nResolution:\n${resolution.answer}\nCitations: ${resolution.citations.map((c) => c.label).join(', ')}`;\n\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const response = await this.llm!.chat(\n [\n {\n role: 'system',\n content: [\n {\n type: 'text',\n text: 'Write empathetic, accurate support replies that cite sources when relevant.',\n },\n ],\n },\n {\n role: 'user',\n content: [{ type: 'text', text: prompt }],\n },\n ],\n { model: this.model }\n );\n\n const body = response.message.content\n .map((part) => ('text' in part ? part.text : ''))\n .join('')\n .trim();\n\n return this.buildDraft(ticket, resolution, classification, body);\n }\n\n private generateTemplate(\n ticket: SupportTicket,\n resolution: SupportResolution,\n classification: TicketClassification\n ): SupportResponseDraft {\n const greeting = ticket.customerName\n ? `Hi ${ticket.customerName},`\n : 'Hi there,';\n const body = `${greeting}\n\nThanks for contacting us about \"${ticket.subject}\". ${this.renderCategoryIntro(\n classification\n )}\n\n${resolution.answer}\n\n${this.renderCitations(resolution)}\n${this.closing}\n\n— ContractSpec Support`;\n\n return this.buildDraft(ticket, resolution, classification, body);\n }\n\n private buildDraft(\n ticket: SupportTicket,\n resolution: SupportResolution,\n classification: TicketClassification,\n body: string\n ): SupportResponseDraft {\n return {\n ticketId: ticket.id,\n subject: ticket.subject.startsWith('Re:')\n ? ticket.subject\n : `Re: ${ticket.subject}`,\n body,\n confidence: Math.min(resolution.confidence, classification.confidence),\n requiresEscalation:\n resolution.actions.some((action) => action.type === 'escalate') ||\n Boolean(classification.escalationRequired),\n citations: resolution.citations,\n };\n }\n\n private renderCategoryIntro(classification: TicketClassification) {\n switch (classification.category) {\n case 'billing':\n return 'I understand billing issues can be stressful, so let me clarify the situation.';\n case 'technical':\n return 'I see you encountered a technical issue. Here is what happened and how to fix it.';\n case 'product':\n return 'Thanks for sharing feedback about the product. Here are the next steps.';\n case 'account':\n return 'Account access is critical, so let me walk you through the resolution.';\n case 'compliance':\n return 'Compliance questions require precision. See the policy-aligned answer below.';\n default:\n return 'Here is what we found after reviewing your request.';\n }\n }\n\n private renderCitations(resolution: SupportResolution) {\n if (!resolution.citations.length) return '';\n const lines = resolution.citations.map((citation, index) => {\n const label = citation.label || `Source ${index + 1}`;\n const link = citation.url ? ` (${citation.url})` : '';\n return `- ${label}${link}`;\n });\n return `References:\\n${lines.join('\\n')}`;\n }\n}\n"],"mappings":";AAeA,IAAa,gBAAb,MAA2B;CACzB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CAEjB,YAAY,SAAgC;AAC1C,OAAK,MAAM,SAAS;AACpB,OAAK,QAAQ,SAAS;AACtB,OAAK,OAAO,SAAS,QAAQ;AAC7B,OAAK,UACH,SAAS,YACR,KAAK,SAAS,aACX,mDACA;;CAGR,MAAM,MACJ,QACA,YACA,gBAC+B;AAC/B,MAAI,KAAK,IACP,QAAO,KAAK,gBAAgB,QAAQ,YAAY,eAAe;AAEjE,SAAO,KAAK,iBAAiB,QAAQ,YAAY,eAAe;;CAGlE,MAAc,gBACZ,QACA,YACA,gBAC+B;EAC/B,MAAM,SAAS,aAAa,KAAK,KAAK;kBACxB,OAAO,QAAQ;eAClB,OAAO,KAAK;qBACN,eAAe,SAAS;qBACxB,eAAe,SAAS;;EAE3C,WAAW,OAAO;aACP,WAAW,UAAU,KAAK,MAAM,EAAE,MAAM,CAAC,KAAK,KAAK;EAsB5D,MAAM,QAnBW,MAAM,KAAK,IAAK,KAC/B,CACE;GACE,MAAM;GACN,SAAS,CACP;IACE,MAAM;IACN,MAAM;IACP,CACF;GACF,EACD;GACE,MAAM;GACN,SAAS,CAAC;IAAE,MAAM;IAAQ,MAAM;IAAQ,CAAC;GAC1C,CACF,EACD,EAAE,OAAO,KAAK,OAAO,CACtB,EAEqB,QAAQ,QAC3B,KAAK,SAAU,UAAU,OAAO,KAAK,OAAO,GAAI,CAChD,KAAK,GAAG,CACR,MAAM;AAET,SAAO,KAAK,WAAW,QAAQ,YAAY,gBAAgB,KAAK;;CAGlE,AAAQ,iBACN,QACA,YACA,gBACsB;EAItB,MAAM,OAAO,GAHI,OAAO,eACpB,MAAM,OAAO,aAAa,KAC1B,YACqB;;kCAEK,OAAO,QAAQ,KAAK,KAAK,oBACrD,eACD,CAAC;;EAEJ,WAAW,OAAO;;EAElB,KAAK,gBAAgB,WAAW,CAAC;EACjC,KAAK,QAAQ;;;AAIX,SAAO,KAAK,WAAW,QAAQ,YAAY,gBAAgB,KAAK;;CAGlE,AAAQ,WACN,QACA,YACA,gBACA,MACsB;AACtB,SAAO;GACL,UAAU,OAAO;GACjB,SAAS,OAAO,QAAQ,WAAW,MAAM,GACrC,OAAO,UACP,OAAO,OAAO;GAClB;GACA,YAAY,KAAK,IAAI,WAAW,YAAY,eAAe,WAAW;GACtE,oBACE,WAAW,QAAQ,MAAM,WAAW,OAAO,SAAS,WAAW,IAC/D,QAAQ,eAAe,mBAAmB;GAC5C,WAAW,WAAW;GACvB;;CAGH,AAAQ,oBAAoB,gBAAsC;AAChE,UAAQ,eAAe,UAAvB;GACE,KAAK,UACH,QAAO;GACT,KAAK,YACH,QAAO;GACT,KAAK,UACH,QAAO;GACT,KAAK,UACH,QAAO;GACT,KAAK,aACH,QAAO;GACT,QACE,QAAO;;;CAIb,AAAQ,gBAAgB,YAA+B;AACrD,MAAI,CAAC,WAAW,UAAU,OAAQ,QAAO;AAMzC,SAAO,gBALO,WAAW,UAAU,KAAK,UAAU,UAAU;AAG1D,UAAO,KAFO,SAAS,SAAS,UAAU,QAAQ,MACrC,SAAS,MAAM,KAAK,SAAS,IAAI,KAAK;IAEnD,CAC2B,KAAK,KAAK"}
@@ -16,4 +16,5 @@ declare class SupportFeedbackLoop {
16
16
  feedbackSummary(limit?: number): string;
17
17
  }
18
18
  //#endregion
19
- export { FeedbackMetrics, SupportFeedbackLoop };
19
+ export { FeedbackMetrics, SupportFeedbackLoop };
20
+ //# sourceMappingURL=feedback-loop.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feedback-loop.d.ts","names":[],"sources":["../../src/bot/feedback-loop.ts"],"sourcesContent":[],"mappings":";;;UAEiB,eAAA;;EAAA,YAAA,EAAA,MAAe;EAQnB,SAAA,EAAA,MAAA;;;;cAAA,mBAAA;;;4BAIe;aAOf"}
@@ -31,4 +31,5 @@ var SupportFeedbackLoop = class {
31
31
  };
32
32
 
33
33
  //#endregion
34
- export { SupportFeedbackLoop };
34
+ export { SupportFeedbackLoop };
35
+ //# sourceMappingURL=feedback-loop.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"feedback-loop.js","names":[],"sources":["../../src/bot/feedback-loop.ts"],"sourcesContent":["import type { ResolutionResultPayload } from '../types';\n\nexport interface FeedbackMetrics {\n totalTickets: number;\n autoResolved: number;\n escalated: number;\n avgConfidence: number;\n avgResponseTimeMs: number;\n}\n\nexport class SupportFeedbackLoop {\n private readonly history: ResolutionResultPayload[] = [];\n private readonly responseTimes = new Map<string, number>();\n\n recordResolution(payload: ResolutionResultPayload, responseTimeMs?: number) {\n this.history.push(payload);\n if (responseTimeMs != null) {\n this.responseTimes.set(payload.ticket.id, responseTimeMs);\n }\n }\n\n metrics(): FeedbackMetrics {\n const total = this.history.length;\n const autoResolved = this.history.filter(\n (entry) =>\n !entry.resolution.actions.some((action) => action.type === 'escalate')\n ).length;\n const escalated = total - autoResolved;\n const avgConfidence =\n total === 0\n ? 0\n : this.history.reduce(\n (sum, entry) => sum + entry.resolution.confidence,\n 0\n ) / total;\n const avgResponseTimeMs =\n this.responseTimes.size === 0\n ? 0\n : [...this.responseTimes.values()].reduce((a, b) => a + b, 0) /\n this.responseTimes.size;\n\n return {\n totalTickets: total,\n autoResolved,\n escalated,\n avgConfidence: Number(avgConfidence.toFixed(2)),\n avgResponseTimeMs: Math.round(avgResponseTimeMs),\n };\n }\n\n feedbackSummary(limit = 5): string {\n const recent = this.history.slice(-limit);\n if (!recent.length) return 'No feedback recorded yet.';\n return recent\n .map((entry) => {\n const status = entry.resolution.actions.some(\n (action) => action.type === 'escalate'\n )\n ? 'Escalated'\n : 'Auto-resolved';\n return `${entry.ticket.subject} – ${status} (confidence: ${entry.resolution.confidence})`;\n })\n .join('\\n');\n }\n}\n"],"mappings":";AAUA,IAAa,sBAAb,MAAiC;CAC/B,AAAiB,UAAqC,EAAE;CACxD,AAAiB,gCAAgB,IAAI,KAAqB;CAE1D,iBAAiB,SAAkC,gBAAyB;AAC1E,OAAK,QAAQ,KAAK,QAAQ;AAC1B,MAAI,kBAAkB,KACpB,MAAK,cAAc,IAAI,QAAQ,OAAO,IAAI,eAAe;;CAI7D,UAA2B;EACzB,MAAM,QAAQ,KAAK,QAAQ;EAC3B,MAAM,eAAe,KAAK,QAAQ,QAC/B,UACC,CAAC,MAAM,WAAW,QAAQ,MAAM,WAAW,OAAO,SAAS,WAAW,CACzE,CAAC;EACF,MAAM,YAAY,QAAQ;EAC1B,MAAM,gBACJ,UAAU,IACN,IACA,KAAK,QAAQ,QACV,KAAK,UAAU,MAAM,MAAM,WAAW,YACvC,EACD,GAAG;EACV,MAAM,oBACJ,KAAK,cAAc,SAAS,IACxB,IACA,CAAC,GAAG,KAAK,cAAc,QAAQ,CAAC,CAAC,QAAQ,GAAG,MAAM,IAAI,GAAG,EAAE,GAC3D,KAAK,cAAc;AAEzB,SAAO;GACL,cAAc;GACd;GACA;GACA,eAAe,OAAO,cAAc,QAAQ,EAAE,CAAC;GAC/C,mBAAmB,KAAK,MAAM,kBAAkB;GACjD;;CAGH,gBAAgB,QAAQ,GAAW;EACjC,MAAM,SAAS,KAAK,QAAQ,MAAM,CAAC,MAAM;AACzC,MAAI,CAAC,OAAO,OAAQ,QAAO;AAC3B,SAAO,OACJ,KAAK,UAAU;GACd,MAAM,SAAS,MAAM,WAAW,QAAQ,MACrC,WAAW,OAAO,SAAS,WAC7B,GACG,cACA;AACJ,UAAO,GAAG,MAAM,OAAO,QAAQ,KAAK,OAAO,gBAAgB,MAAM,WAAW,WAAW;IACvF,CACD,KAAK,KAAK"}
@@ -11,4 +11,5 @@ interface SupportToolsetOptions {
11
11
  }
12
12
  declare function createSupportTools(options: SupportToolsetOptions): Tool[];
13
13
  //#endregion
14
- export { SupportToolsetOptions, createSupportTools };
14
+ export { SupportToolsetOptions, createSupportTools };
15
+ //# sourceMappingURL=tools.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.d.ts","names":[],"sources":["../../src/bot/tools.ts"],"sourcesContent":[],"mappings":";;;;;;UAgGiB,qBAAA;YACL;EADK,UAAA,EAEH,gBAFwB;EAC1B,SAAA,EAEC,aAFD;;AAEC,iBAGG,kBAAA,CAHH,OAAA,EAG+B,qBAH/B,CAAA,EAGuD,IAHvD,EAAA"}
package/dist/bot/tools.js CHANGED
@@ -133,4 +133,5 @@ function createSupportTools(options) {
133
133
  }
134
134
 
135
135
  //#endregion
136
- export { createSupportTools };
136
+ export { createSupportTools };
137
+ //# sourceMappingURL=tools.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"tools.js","names":[],"sources":["../../src/bot/tools.ts"],"sourcesContent":["import type { TicketResolver } from '../rag/ticket-resolver';\nimport type { TicketClassifier } from '../tickets/classifier';\nimport type { AutoResponder } from './auto-responder';\nimport type {\n SupportAction,\n SupportCitation,\n SupportResolution,\n SupportTicket,\n TicketCategory,\n TicketClassification,\n TicketPriority,\n TicketSentiment,\n} from '../types';\nimport type { Tool } from '@ai-sdk/provider-utils';\nimport * as z from 'zod';\n\nconst ticketSchema = z.object({\n id: z.string(),\n subject: z.string(),\n body: z.string(),\n channel: z.enum(['email', 'chat', 'phone', 'portal']),\n customerName: z.string().optional(),\n customerEmail: z.string().optional(),\n metadata: z.object().optional(),\n}) satisfies z.ZodType<SupportTicket>;\nconst supportCitationSchema = z.object({\n label: z.string(),\n url: z.string().optional(),\n snippet: z.string().optional(),\n score: z.number().optional(),\n}) satisfies z.ZodType<SupportCitation>;\nconst supportActionSchema = z.object({\n type: z.enum(['respond', 'escalate', 'refund', 'manual']),\n label: z.string(),\n payload: z.record(z.string(), z.string()),\n}) satisfies z.ZodType<SupportAction>;\nconst supportResolutionSchema = z.object({\n ticketId: z.string(),\n answer: z.string(),\n confidence: z.number(),\n citations: supportCitationSchema.array(),\n actions: supportActionSchema.array(),\n escalationReason: z.string().optional(),\n knowledgeUpdates: z.array(z.string()).optional(),\n}) satisfies z.ZodType<SupportResolution>;\nconst ticketClassificationSchema = z.object({\n ticketId: z.string(),\n category: z.enum([\n 'billing',\n 'technical',\n 'product',\n 'account',\n 'compliance',\n 'other',\n ]) satisfies z.ZodType<TicketCategory>,\n priority: z.enum([\n 'urgent',\n 'high',\n 'medium',\n 'low',\n ]) satisfies z.ZodType<TicketPriority>,\n sentiment: z.enum([\n 'positive',\n 'neutral',\n 'negative',\n 'frustrated',\n ]) satisfies z.ZodType<TicketSentiment>,\n intents: z.array(z.string()),\n tags: z.array(z.string()),\n confidence: z.number(),\n escalationRequired: z.boolean().optional(),\n}) satisfies z.ZodType<TicketClassification>;\n\nfunction ensureTicket(input: unknown): SupportTicket {\n if (!input || typeof input !== 'object' || !('ticket' in input)) {\n throw new Error('Input must include ticket');\n }\n const ticket = (input as { ticket: SupportTicket }).ticket;\n if (!ticket?.id) throw new Error('Ticket is missing id');\n return ticket;\n}\n\nfunction extractResolution(input: unknown): SupportResolution | undefined {\n if (!input || typeof input !== 'object' || !('resolution' in input))\n return undefined;\n return (input as { resolution?: SupportResolution }).resolution;\n}\n\nfunction extractClassification(\n input: unknown\n): TicketClassification | undefined {\n if (!input || typeof input !== 'object' || !('classification' in input))\n return undefined;\n return (input as { classification?: TicketClassification }).classification;\n}\n\nexport interface SupportToolsetOptions {\n resolver: TicketResolver;\n classifier: TicketClassifier;\n responder: AutoResponder;\n}\n\nexport function createSupportTools(options: SupportToolsetOptions): Tool[] {\n const classifyTool: Tool = {\n title: 'support_classify_ticket',\n description: 'Classify a ticket for priority, sentiment, and category',\n inputSchema: z.object({ ticket: ticketSchema }),\n execute: async (input: unknown) => {\n const ticket = ensureTicket(input);\n const classification = await options.classifier.classify(ticket);\n return {\n content: JSON.stringify(classification),\n metadata: { ticketId: ticket.id },\n };\n },\n };\n\n const resolveTool: Tool = {\n title: 'support_resolve_ticket',\n description: 'Generate a knowledge-grounded resolution for a ticket',\n inputSchema: z.object({ ticket: ticketSchema }),\n execute: async (input: unknown) => {\n const ticket = ensureTicket(input);\n const resolution = await options.resolver.resolve(ticket);\n return {\n content: JSON.stringify(resolution),\n metadata: { ticketId: ticket.id },\n };\n },\n };\n\n const responderTool: Tool = {\n title: 'support_draft_response',\n description:\n 'Draft a user-facing reply based on resolution + classification',\n inputSchema: z.object({\n ticket: ticketSchema,\n resolution: supportResolutionSchema,\n classification: ticketClassificationSchema,\n }),\n execute: async (input: unknown) => {\n const ticket = ensureTicket(input);\n const resolution = extractResolution(input);\n const classification = extractClassification(input);\n if (!resolution || !classification) {\n throw new Error('resolution and classification are required');\n }\n const draft = await options.responder.draft(\n ticket,\n resolution,\n classification\n );\n return {\n content: JSON.stringify(draft),\n metadata: { ticketId: ticket.id },\n };\n },\n };\n\n return [classifyTool, resolveTool, responderTool];\n}\n"],"mappings":"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"}
@@ -21,4 +21,5 @@ declare class TicketResolver {
21
21
  private deriveConfidence;
22
22
  }
23
23
  //#endregion
24
- export { KnowledgeRetriever, TicketResolver, TicketResolverOptions };
24
+ export { KnowledgeRetriever, TicketResolver, TicketResolverOptions };
25
+ //# sourceMappingURL=ticket-resolver.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ticket-resolver.d.ts","names":[],"sources":["../../src/rag/ticket-resolver.ts"],"sourcesContent":[],"mappings":";;;;UAGiB,kBAAA;2BACU,QAAQ;AADnC;AAIiB,UAAA,qBAAA,CAAqB;EAMzB,SAAA,EALA,kBAKc;EAKJ,aAAA,CAAA,EAAA,MAAA;EAMC,aAAA,CAAA,EAAA,MAAA;;AAAgB,cAX3B,cAAA,CAW2B;EAAO,iBAAA,SAAA;;;uBANxB;kBAMC,gBAAgB,QAAQ"}
@@ -60,4 +60,5 @@ var TicketResolver = class {
60
60
  };
61
61
 
62
62
  //#endregion
63
- export { TicketResolver };
63
+ export { TicketResolver };
64
+ //# sourceMappingURL=ticket-resolver.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ticket-resolver.js","names":[],"sources":["../../src/rag/ticket-resolver.ts"],"sourcesContent":["import type { KnowledgeAnswer } from '@lssm/lib.knowledge/query/service';\nimport type { SupportResolution, SupportTicket } from '../types';\n\nexport interface KnowledgeRetriever {\n query(question: string): Promise<KnowledgeAnswer>;\n}\n\nexport interface TicketResolverOptions {\n knowledge: KnowledgeRetriever;\n minConfidence?: number;\n prependPrompt?: string;\n}\n\nexport class TicketResolver {\n private readonly knowledge: KnowledgeRetriever;\n private readonly minConfidence: number;\n private readonly prependPrompt?: string;\n\n constructor(options: TicketResolverOptions) {\n this.knowledge = options.knowledge;\n this.minConfidence = options.minConfidence ?? 0.65;\n this.prependPrompt = options.prependPrompt;\n }\n\n async resolve(ticket: SupportTicket): Promise<SupportResolution> {\n const question = this.buildQuestion(ticket);\n const answer = await this.knowledge.query(question);\n return this.toResolution(ticket, answer);\n }\n\n private buildQuestion(ticket: SupportTicket): string {\n const header = [`Subject: ${ticket.subject}`, `Channel: ${ticket.channel}`];\n if (ticket.customerName) header.push(`Customer: ${ticket.customerName}`);\n const sections = [\n this.prependPrompt,\n header.join('\\n'),\n '---',\n ticket.body,\n ].filter(Boolean);\n return sections.join('\\n');\n }\n\n private toResolution(\n ticket: SupportTicket,\n answer: KnowledgeAnswer\n ): SupportResolution {\n const citations = answer.references.map((ref) => {\n const label =\n typeof ref.payload?.title === 'string'\n ? ref.payload.title\n : typeof ref.payload?.documentId === 'string'\n ? ref.payload.documentId\n : ref.id;\n return {\n label,\n url: typeof ref.payload?.url === 'string' ? ref.payload.url : undefined,\n snippet:\n typeof ref.payload?.text === 'string'\n ? ref.payload.text.slice(0, 280)\n : undefined,\n score: ref.score,\n };\n });\n\n const confidence = this.deriveConfidence(answer);\n const escalate = confidence < this.minConfidence || citations.length === 0;\n\n return {\n ticketId: ticket.id,\n answer: answer.answer,\n confidence,\n citations,\n actions: [\n escalate\n ? { type: 'escalate', label: 'Escalate for human review' }\n : { type: 'respond', label: 'Send automated response' },\n ],\n escalationReason: escalate\n ? 'Insufficient confidence or missing knowledge references'\n : undefined,\n knowledgeUpdates: escalate ? [ticket.body.slice(0, 200)] : undefined,\n };\n }\n\n private deriveConfidence(answer: KnowledgeAnswer): number {\n if (!answer.references.length) return 0.3;\n const topScore = answer.references[0]?.score ?? 0.4;\n const normalized = Math.min(1, Math.max(0, topScore));\n const tokenPenalty = answer.usage?.completionTokens\n ? Math.min(answer.usage.completionTokens / 1000, 0.2)\n : 0;\n return Number((normalized - tokenPenalty).toFixed(2));\n }\n}\n"],"mappings":";AAaA,IAAa,iBAAb,MAA4B;CAC1B,AAAiB;CACjB,AAAiB;CACjB,AAAiB;CAEjB,YAAY,SAAgC;AAC1C,OAAK,YAAY,QAAQ;AACzB,OAAK,gBAAgB,QAAQ,iBAAiB;AAC9C,OAAK,gBAAgB,QAAQ;;CAG/B,MAAM,QAAQ,QAAmD;EAC/D,MAAM,WAAW,KAAK,cAAc,OAAO;EAC3C,MAAM,SAAS,MAAM,KAAK,UAAU,MAAM,SAAS;AACnD,SAAO,KAAK,aAAa,QAAQ,OAAO;;CAG1C,AAAQ,cAAc,QAA+B;EACnD,MAAM,SAAS,CAAC,YAAY,OAAO,WAAW,YAAY,OAAO,UAAU;AAC3E,MAAI,OAAO,aAAc,QAAO,KAAK,aAAa,OAAO,eAAe;AAOxE,SANiB;GACf,KAAK;GACL,OAAO,KAAK,KAAK;GACjB;GACA,OAAO;GACR,CAAC,OAAO,QAAQ,CACD,KAAK,KAAK;;CAG5B,AAAQ,aACN,QACA,QACmB;EACnB,MAAM,YAAY,OAAO,WAAW,KAAK,QAAQ;AAO/C,UAAO;IACL,OANA,OAAO,IAAI,SAAS,UAAU,WAC1B,IAAI,QAAQ,QACZ,OAAO,IAAI,SAAS,eAAe,WACjC,IAAI,QAAQ,aACZ,IAAI;IAGV,KAAK,OAAO,IAAI,SAAS,QAAQ,WAAW,IAAI,QAAQ,MAAM;IAC9D,SACE,OAAO,IAAI,SAAS,SAAS,WACzB,IAAI,QAAQ,KAAK,MAAM,GAAG,IAAI,GAC9B;IACN,OAAO,IAAI;IACZ;IACD;EAEF,MAAM,aAAa,KAAK,iBAAiB,OAAO;EAChD,MAAM,WAAW,aAAa,KAAK,iBAAiB,UAAU,WAAW;AAEzE,SAAO;GACL,UAAU,OAAO;GACjB,QAAQ,OAAO;GACf;GACA;GACA,SAAS,CACP,WACI;IAAE,MAAM;IAAY,OAAO;IAA6B,GACxD;IAAE,MAAM;IAAW,OAAO;IAA2B,CAC1D;GACD,kBAAkB,WACd,4DACA;GACJ,kBAAkB,WAAW,CAAC,OAAO,KAAK,MAAM,GAAG,IAAI,CAAC,GAAG;GAC5D;;CAGH,AAAQ,iBAAiB,QAAiC;AACxD,MAAI,CAAC,OAAO,WAAW,OAAQ,QAAO;EACtC,MAAM,WAAW,OAAO,WAAW,IAAI,SAAS;EAChD,MAAM,aAAa,KAAK,IAAI,GAAG,KAAK,IAAI,GAAG,SAAS,CAAC;EACrD,MAAM,eAAe,OAAO,OAAO,mBAC/B,KAAK,IAAI,OAAO,MAAM,mBAAmB,KAAM,GAAI,GACnD;AACJ,SAAO,QAAQ,aAAa,cAAc,QAAQ,EAAE,CAAC"}
package/dist/spec.d.ts CHANGED
@@ -9,4 +9,5 @@ interface SupportBotDefinition {
9
9
  }
10
10
  declare function defineSupportBot(definition: SupportBotDefinition): SupportBotSpec;
11
11
  //#endregion
12
- export { SupportBotDefinition, defineSupportBot };
12
+ export { SupportBotDefinition, defineSupportBot };
13
+ //# sourceMappingURL=spec.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spec.d.ts","names":[],"sources":["../src/spec.ts"],"sourcesContent":[],"mappings":";;;;UAIiB,oBAAA;QACT;EADS,KAAA,CAAA,EAEP,eAF2B,EAAA;EAMrB,qBAAgB,CAAA,EAAA,MAClB;;iBADE,gBAAA,aACF,uBACX"}
package/dist/spec.js CHANGED
@@ -32,4 +32,5 @@ function defineSupportBot(definition) {
32
32
  }
33
33
 
34
34
  //#endregion
35
- export { defineSupportBot };
35
+ export { defineSupportBot };
36
+ //# sourceMappingURL=spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"spec.js","names":[],"sources":["../src/spec.ts"],"sourcesContent":["import type { AgentSpec, AgentToolConfig } from '@lssm/lib.ai-agent';\nimport { defineAgent } from '@lssm/lib.ai-agent';\nimport type { SupportBotSpec } from './types';\n\nexport interface SupportBotDefinition {\n base: AgentSpec;\n tools?: AgentToolConfig[];\n autoEscalateThreshold?: number;\n}\n\nexport function defineSupportBot(\n definition: SupportBotDefinition\n): SupportBotSpec {\n const base = defineAgent({\n ...definition.base,\n policy: {\n ...definition.base.policy,\n confidence: {\n min: definition.base.policy?.confidence?.min ?? 0.7,\n default: definition.base.policy?.confidence?.default ?? 0.6,\n },\n escalation: {\n confidenceThreshold:\n definition.autoEscalateThreshold ??\n definition.base.policy?.escalation?.confidenceThreshold ??\n definition.base.policy?.confidence?.min ??\n 0.7,\n ...definition.base.policy?.escalation,\n },\n },\n memory: definition.base.memory ?? { maxEntries: 120, ttlMinutes: 120 },\n tools: definition.tools ?? definition.base.tools,\n instructions: `${definition.base.instructions}\\n\\nAlways cite support knowledge sources and flag compliance/billing issues for human review when unsure.`,\n });\n\n return {\n ...base,\n thresholds: {\n autoResolveMinConfidence: definition.autoEscalateThreshold ?? 0.75,\n maxIterations: 6,\n },\n };\n}\n"],"mappings":";;;;AAUA,SAAgB,iBACd,YACgB;AAuBhB,QAAO;EACL,GAvBW,YAAY;GACvB,GAAG,WAAW;GACd,QAAQ;IACN,GAAG,WAAW,KAAK;IACnB,YAAY;KACV,KAAK,WAAW,KAAK,QAAQ,YAAY,OAAO;KAChD,SAAS,WAAW,KAAK,QAAQ,YAAY,WAAW;KACzD;IACD,YAAY;KACV,qBACE,WAAW,yBACX,WAAW,KAAK,QAAQ,YAAY,uBACpC,WAAW,KAAK,QAAQ,YAAY,OACpC;KACF,GAAG,WAAW,KAAK,QAAQ;KAC5B;IACF;GACD,QAAQ,WAAW,KAAK,UAAU;IAAE,YAAY;IAAK,YAAY;IAAK;GACtE,OAAO,WAAW,SAAS,WAAW,KAAK;GAC3C,cAAc,GAAG,WAAW,KAAK,aAAa;GAC/C,CAAC;EAIA,YAAY;GACV,0BAA0B,WAAW,yBAAyB;GAC9D,eAAe;GAChB;EACF"}
@@ -21,4 +21,5 @@ declare class TicketClassifier {
21
21
  private estimateConfidence;
22
22
  }
23
23
  //#endregion
24
- export { TicketClassifier, TicketClassifierOptions };
24
+ export { TicketClassifier, TicketClassifierOptions };
25
+ //# sourceMappingURL=classifier.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classifier.d.ts","names":[],"sources":["../../src/tickets/classifier.ts"],"sourcesContent":[],"mappings":";;;;UAgCiB,uBAAA;aACJ,QAAQ,OAAO;EADX,GAAA,CAAA,EAET,WAFS;EACW,QAAA,CAAA,EAAA,MAAA;;AAAf,cAKA,gBAAA,CALA;EACL,iBAAA,QAAA;EAAW,iBAAA,GAAA;EAIN,iBAAA,QAAgB;EAKL,WAAA,CAAA,OAAA,CAAA,EAAA,uBAAA;EASC,QAAA,CAAA,MAAA,EAAA,aAAA,CAAA,EAAgB,OAAhB,CAAwB,oBAAxB,CAAA;EAAwB,QAAA,uBAAA;EAAR,QAAA,cAAA;EAAO,QAAA,cAAA"}
@@ -195,4 +195,5 @@ var TicketClassifier = class {
195
195
  };
196
196
 
197
197
  //#endregion
198
- export { TicketClassifier };
198
+ export { TicketClassifier };
199
+ //# sourceMappingURL=classifier.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"classifier.js","names":["CATEGORY_KEYWORDS: Record<TicketCategory, string[]>","PRIORITY_HINTS: Record<TicketPriority, string[]>","SENTIMENT_HINTS: Record<TicketSentiment, string[]>","intents: string[]"],"sources":["../../src/tickets/classifier.ts"],"sourcesContent":["import type { LLMProvider } from '@lssm/lib.contracts/integrations/providers/llm';\nimport type {\n SupportTicket,\n TicketCategory,\n TicketPriority,\n TicketSentiment,\n TicketClassification,\n} from '../types';\n\nconst CATEGORY_KEYWORDS: Record<TicketCategory, string[]> = {\n billing: ['invoice', 'payout', 'refund', 'charge', 'billing', 'payment'],\n technical: ['bug', 'error', 'crash', 'issue', 'failed', 'timeout'],\n product: ['feature', 'roadmap', 'idea', 'request', 'feedback'],\n account: ['login', 'password', '2fa', 'account', 'profile', 'email change'],\n compliance: ['kyc', 'aml', 'compliance', 'regulation', 'gdpr'],\n other: [],\n};\n\nconst PRIORITY_HINTS: Record<TicketPriority, string[]> = {\n urgent: ['urgent', 'asap', 'immediately', 'today', 'right away'],\n high: ['high priority', 'blocking', 'major', 'critical'],\n medium: ['soon', 'next few days'],\n low: ['nice to have', 'when possible', 'later'],\n};\n\nconst SENTIMENT_HINTS: Record<TicketSentiment, string[]> = {\n positive: ['love', 'great', 'awesome', 'thank you'],\n neutral: ['question', 'wonder', 'curious'],\n negative: ['unhappy', 'bad', 'terrible', 'awful', 'angry'],\n frustrated: ['furious', 'frustrated', 'fed up', 'ridiculous'],\n};\n\nexport interface TicketClassifierOptions {\n keywords?: Partial<Record<TicketCategory, string[]>>;\n llm?: LLMProvider;\n llmModel?: string;\n}\n\nexport class TicketClassifier {\n private readonly keywords: Record<TicketCategory, string[]>;\n private readonly llm?: LLMProvider;\n private readonly llmModel?: string;\n\n constructor(options?: TicketClassifierOptions) {\n this.keywords = {\n ...CATEGORY_KEYWORDS,\n ...(options?.keywords ?? {}),\n } as Record<TicketCategory, string[]>;\n this.llm = options?.llm;\n this.llmModel = options?.llmModel;\n }\n\n async classify(ticket: SupportTicket): Promise<TicketClassification> {\n const heuristics = this.heuristicClassification(ticket);\n if (!this.llm) return heuristics;\n\n try {\n const llmResult = await this.llm.chat(\n [\n {\n role: 'system',\n content: [{ type: 'text', text: 'Classify the support ticket.' }],\n },\n {\n role: 'user',\n content: [\n {\n type: 'text',\n text: JSON.stringify({\n subject: ticket.subject,\n body: ticket.body,\n channel: ticket.channel,\n }),\n },\n ],\n },\n ],\n {\n responseFormat: 'json',\n model: this.llmModel,\n }\n );\n const content = llmResult.message.content.find((part) => 'text' in part);\n if (content && 'text' in content) {\n const parsed = JSON.parse(\n content.text\n ) as Partial<TicketClassification>;\n return {\n ...heuristics,\n ...parsed,\n intents: parsed.intents ?? heuristics.intents,\n tags: parsed.tags ?? heuristics.tags,\n };\n }\n } catch {\n // fallback to heuristics\n }\n\n return heuristics;\n }\n\n private heuristicClassification(ticket: SupportTicket): TicketClassification {\n const text = `${ticket.subject}\\n${ticket.body}`.toLowerCase();\n const category = this.detectCategory(text);\n const priority = this.detectPriority(text);\n const sentiment = this.detectSentiment(text);\n const intents = this.extractIntents(text);\n const tags = intents.slice(0, 3);\n const confidence = this.estimateConfidence(category, priority, sentiment);\n\n return {\n ticketId: ticket.id,\n category,\n priority,\n sentiment,\n intents,\n tags,\n confidence,\n escalationRequired: priority === 'urgent' || category === 'compliance',\n };\n }\n\n private detectCategory(text: string): TicketCategory {\n for (const [category, keywords] of Object.entries(this.keywords) as [\n TicketCategory,\n string[],\n ][]) {\n if (keywords.some((keyword) => text.includes(keyword))) {\n return category;\n }\n }\n return 'other';\n }\n\n private detectPriority(text: string): TicketPriority {\n for (const priority of [\n 'urgent',\n 'high',\n 'medium',\n 'low',\n ] as TicketPriority[]) {\n if (PRIORITY_HINTS[priority].some((word) => text.includes(word))) {\n return priority;\n }\n }\n return 'medium';\n }\n\n private detectSentiment(text: string): TicketSentiment {\n for (const sentiment of [\n 'frustrated',\n 'negative',\n 'neutral',\n 'positive',\n ] as TicketSentiment[]) {\n if (SENTIMENT_HINTS[sentiment].some((word) => text.includes(word))) {\n return sentiment;\n }\n }\n return 'neutral';\n }\n\n private extractIntents(text: string): string[] {\n const intents: string[] = [];\n if (text.includes('refund') || text.includes('chargeback'))\n intents.push('refund');\n if (text.includes('payout')) intents.push('payout');\n if (text.includes('login')) intents.push('login-help');\n if (text.includes('feature')) intents.push('feature-request');\n if (text.includes('bug') || text.includes('error'))\n intents.push('bug-report');\n return intents.length ? intents : ['general'];\n }\n\n private estimateConfidence(\n category: TicketCategory,\n priority: TicketPriority,\n sentiment: TicketSentiment\n ): number {\n let base = 0.6;\n if (category !== 'other') base += 0.1;\n if (priority === 'urgent' || priority === 'low') base += 0.05;\n if (sentiment === 'frustrated') base -= 0.05;\n return Math.min(0.95, Math.max(0.4, Number(base.toFixed(2))));\n }\n}\n"],"mappings":"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"}
package/dist/types.d.ts CHANGED
@@ -72,4 +72,5 @@ interface ResolutionResultPayload extends ClassificationResultPayload {
72
72
  draft: SupportResponseDraft;
73
73
  }
74
74
  //#endregion
75
- export { ClassificationResultPayload, ResolutionResultPayload, SupportAction, SupportBotSpec, SupportCitation, SupportResolution, SupportResponseDraft, SupportTicket, TicketCategory, TicketChannel, TicketClassification, TicketPriority, TicketSentiment };
75
+ export { ClassificationResultPayload, ResolutionResultPayload, SupportAction, SupportBotSpec, SupportCitation, SupportResolution, SupportResponseDraft, SupportTicket, TicketCategory, TicketChannel, TicketClassification, TicketPriority, TicketSentiment };
76
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","names":[],"sources":["../src/types.ts"],"sourcesContent":[],"mappings":";;;KAEY,cAAA;KACA,cAAA;AADA,KAQA,aAAA,GARc,OAAA,GAAA,MAAA,GAAA,OAAA,GAAA,QAAA;AACd,KAQA,eAAA,GARc,UAAA,GAAA,SAAA,GAAA,UAAA,GAAA,YAAA;AAOd,UAOK,aAAA,CAPQ;EACb,EAAA,EAAA,MAAA;EAMK,OAAA,EAAA,MAAA;EAWA,IAAA,EAAA,MAAA;EAEL,OAAA,EATD,aASC;EACA,MAAA,CAAA,EAAA,MAAA;EACC,aAAA,CAAA,EAAA,MAAA;EAAe,YAAA,CAAA,EAAA,MAAA;EAOX,QAAA,CAAA,EAdJ,MAcI,CAAe,MAAA,EAAA,MAAA,CAAA;AAOhC;AAMiB,UAxBA,oBAAA,CA4BJ;EAMI,QAAA,EAAA,MAAA;EASA,QAAA,EAzCL,cAyCoB;EAWf,QAAA,EAnDL,cAmDK;EAKA,SAAA,EAvDJ,eAuD4B;EAC3B,OAAA,EAAA,MAAA,EAAA;EACL,IAAA,EAAA,MAAA,EAAA;EAFwC,UAAA,EAAA,MAAA;EAA2B,kBAAA,CAAA,EAAA,OAAA;;UAhD3D,eAAA;;;;;;UAOA,aAAA;;;YAGL;;UAGK,iBAAA;;;;aAIJ;WACF;;;;UAKM,oBAAA;;;;;;aAMJ;;UAGI,cAAA,SAAuB;;;;;;;;;;UAWvB,2BAAA;UACP;kBACQ;;UAGD,uBAAA,SAAgC;cACnC;SACL"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lssm/lib.support-bot",
3
- "version": "0.0.0-canary-20251217080011",
3
+ "version": "0.0.0-canary-20251219202229",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -23,15 +23,15 @@
23
23
  "test": "bun run"
24
24
  },
25
25
  "dependencies": {
26
- "@lssm/lib.ai-agent": "0.0.0-canary-20251217080011",
27
- "@lssm/lib.contracts": "0.0.0-canary-20251217080011",
28
- "@lssm/lib.knowledge": "0.0.0-canary-20251217080011",
29
- "@lssm/lib.schema": "0.0.0-canary-20251217080011"
26
+ "@lssm/lib.ai-agent": "0.0.0-canary-20251219202229",
27
+ "@lssm/lib.contracts": "0.0.0-canary-20251219202229",
28
+ "@lssm/lib.knowledge": "0.0.0-canary-20251219202229",
29
+ "@lssm/lib.schema": "0.0.0-canary-20251219202229"
30
30
  },
31
31
  "devDependencies": {
32
- "@lssm/tool.tsdown": "0.0.0-canary-20251217080011",
33
- "@lssm/tool.typescript": "0.0.0-canary-20251217080011",
34
- "tsdown": "^0.17.4",
32
+ "@lssm/tool.tsdown": "0.0.0-canary-20251219202229",
33
+ "@lssm/tool.typescript": "0.0.0-canary-20251219202229",
34
+ "tsdown": "^0.18.1",
35
35
  "typescript": "^5.9.3"
36
36
  },
37
37
  "exports": {
@@ -64,5 +64,6 @@
64
64
  "./types": "./dist/types.js",
65
65
  "./*": "./*"
66
66
  }
67
- }
67
+ },
68
+ "license": "MIT"
68
69
  }