@lssm/lib.support-bot 0.0.0-canary-20251206160926

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 (39) hide show
  1. package/README.md +46 -0
  2. package/dist/ai-agent/dist/agent/agent-factory.js +1 -0
  3. package/dist/ai-agent/dist/agent/contract-spec-agent.js +1 -0
  4. package/dist/ai-agent/dist/agent/index.js +1 -0
  5. package/dist/ai-agent/dist/approval/index.js +1 -0
  6. package/dist/ai-agent/dist/approval/workflow.js +1 -0
  7. package/dist/ai-agent/dist/index.js +1 -0
  8. package/dist/ai-agent/dist/schema/index.js +1 -0
  9. package/dist/ai-agent/dist/schema/json-schema-to-zod.js +1 -0
  10. package/dist/ai-agent/dist/schema/schema-output.js +1 -0
  11. package/dist/ai-agent/dist/spec/spec.js +1 -0
  12. package/dist/ai-agent/dist/tools/index.js +1 -0
  13. package/dist/ai-agent/dist/tools/knowledge-tool.js +1 -0
  14. package/dist/ai-agent/dist/tools/mcp-client.js +1 -0
  15. package/dist/ai-agent/dist/tools/mcp-server.js +1 -0
  16. package/dist/ai-agent/dist/tools/tool-adapter.js +1 -0
  17. package/dist/bot/auto-responder.d.ts +25 -0
  18. package/dist/bot/auto-responder.js +18 -0
  19. package/dist/bot/feedback-loop.d.ts +19 -0
  20. package/dist/bot/feedback-loop.js +2 -0
  21. package/dist/bot/index.d.ts +4 -0
  22. package/dist/bot/index.js +1 -0
  23. package/dist/bot/tools.d.ts +14 -0
  24. package/dist/bot/tools.js +1 -0
  25. package/dist/index.d.ts +8 -0
  26. package/dist/index.js +1 -0
  27. package/dist/rag/index.d.ts +2 -0
  28. package/dist/rag/index.js +1 -0
  29. package/dist/rag/ticket-resolver.d.ts +24 -0
  30. package/dist/rag/ticket-resolver.js +3 -0
  31. package/dist/spec.d.ts +12 -0
  32. package/dist/spec.js +1 -0
  33. package/dist/tickets/classifier.d.ts +24 -0
  34. package/dist/tickets/classifier.js +1 -0
  35. package/dist/tickets/index.d.ts +2 -0
  36. package/dist/tickets/index.js +1 -0
  37. package/dist/types.d.ts +75 -0
  38. package/dist/types.js +0 -0
  39. package/package.json +51 -0
package/README.md ADDED
@@ -0,0 +1,46 @@
1
+ # @lssm/lib.support-bot
2
+
3
+ Production-ready building blocks for AI-first support desks powered by ContractSpec. The library wires knowledge-grounded responses, ticket classification, auto-resolutions, and performance telemetry into `@lssm/lib.ai-agent`.
4
+
5
+ ## Highlights
6
+
7
+ - **TicketClassifier** – fast heuristics + optional LLM validation for category, priority, urgency, and sentiment.
8
+ - **TicketResolver** – multi-space RAG pipeline that cites references and surfaces confidence scores.
9
+ - **AutoResponder** – generates tone-aware replies, adds policy disclaimers, and escalates when risky.
10
+ - **FeedbackLoop** – records outcomes, tunes confidence thresholds, and syncs new knowledge back.
11
+ - **Agent Tools** – helpers to expose the above capabilities as `AgentRunner` tools in one line.
12
+
13
+ ## Quickstart
14
+
15
+ ```ts
16
+ import { defineSupportBot, TicketResolver, TicketClassifier, createSupportTools } from '@lssm/lib.support-bot';
17
+ import { AgentRegistry, AgentRunner, ToolExecutor } from '@lssm/lib.ai-agent';
18
+
19
+ const SupportBot = defineSupportBot({
20
+ base: {
21
+ meta: { name: 'support.bot', version: 1, owners: ['team-support'], domain: 'operations' },
22
+ instructions: 'Resolve support tickets with empathy. Escalate if billing or compliance risk.',
23
+ tools: [{ name: 'resolve_ticket' }, { name: 'escalate_ticket' }],
24
+ },
25
+ escalation: { confidenceThreshold: 0.75 },
26
+ });
27
+
28
+ const resolver = new TicketResolver({ knowledge: knowledgeQueryService });
29
+ const classifier = new TicketClassifier();
30
+ const tools = createSupportTools({ resolver, classifier });
31
+
32
+ const registry = new AgentRegistry().register(SupportBot);
33
+ const runner = new AgentRunner({
34
+ registry,
35
+ llm: mistralProvider,
36
+ toolExecutor: new ToolExecutor({ tools }),
37
+ });
38
+
39
+ const outcome = await runner.run({
40
+ agent: 'support.bot',
41
+ input: 'My payout failed yesterday and it is urgent',
42
+ tenantId: 'acme',
43
+ });
44
+ ```
45
+
46
+ See `/packages/contractspec/examples/ai-support-bot` for an end-to-end integration with ticket ingestion, review queues, and analytics.
@@ -0,0 +1 @@
1
+ import"./contract-spec-agent.js";
@@ -0,0 +1 @@
1
+ import"../tools/tool-adapter.js";import"../tools/knowledge-tool.js";import{z as e}from"zod";import"ai";e.object({tenantId:e.string().optional(),actorId:e.string().optional(),sessionId:e.string().optional(),metadata:e.record(e.string(),e.unknown()).optional()});
@@ -0,0 +1 @@
1
+ import"./contract-spec-agent.js";import"./agent-factory.js";
@@ -0,0 +1 @@
1
+ import"./workflow.js";
@@ -0,0 +1 @@
1
+ import"node:crypto";
@@ -0,0 +1 @@
1
+ import"./schema/json-schema-to-zod.js";import"./tools/tool-adapter.js";import"./tools/knowledge-tool.js";import{e}from"./spec/spec.js";import"./agent/contract-spec-agent.js";import"./agent/agent-factory.js";import"./agent/index.js";import"./tools/mcp-client.js";import"./tools/mcp-server.js";import"./tools/index.js";import"./schema/schema-output.js";import"./schema/index.js";import"./approval/workflow.js";import"./approval/index.js";import"ai";
@@ -0,0 +1 @@
1
+ import"./json-schema-to-zod.js";import"./schema-output.js";
@@ -0,0 +1 @@
1
+ import{z as e}from"zod";
@@ -0,0 +1 @@
1
+ import"./json-schema-to-zod.js";import{z as e}from"zod";import"ai";
@@ -0,0 +1 @@
1
+ function e(e){if(!e.meta?.name)throw Error(`Agent name is required`);if(!Number.isFinite(e.meta.version))throw Error(`Agent ${e.meta.name} is missing a numeric version`);if(!e.instructions?.trim())throw Error(`Agent ${e.meta.name} requires instructions`);if(!e.tools?.length)throw Error(`Agent ${e.meta.name} must expose at least one tool`);let t=new Set;for(let n of e.tools){if(t.has(n.name))throw Error(`Agent ${e.meta.name} has duplicate tool name: ${n.name}`);t.add(n.name)}return Object.freeze(e)}export{e};
@@ -0,0 +1 @@
1
+ import"./tool-adapter.js";import"./knowledge-tool.js";import"./mcp-client.js";import"./mcp-server.js";
@@ -0,0 +1 @@
1
+ import{z as e}from"zod";import"ai";
@@ -0,0 +1 @@
1
+ import"@ai-sdk/mcp";import"@ai-sdk/mcp/mcp-stdio";
@@ -0,0 +1 @@
1
+ import"../schema/json-schema-to-zod.js";import{z as e}from"zod";import"@modelcontextprotocol/sdk/server/mcp.js";
@@ -0,0 +1 @@
1
+ import"../schema/json-schema-to-zod.js";import"ai";
@@ -0,0 +1,25 @@
1
+ import { SupportResolution, SupportResponseDraft, SupportTicket, TicketClassification } from "../types.js";
2
+ import { LLMProvider } from "@lssm/lib.contracts/integrations/providers/llm";
3
+
4
+ //#region src/bot/auto-responder.d.ts
5
+ interface AutoResponderOptions {
6
+ llm?: LLMProvider;
7
+ model?: string;
8
+ tone?: 'friendly' | 'formal';
9
+ closing?: string;
10
+ }
11
+ declare class AutoResponder {
12
+ private readonly llm?;
13
+ private readonly model?;
14
+ private readonly tone;
15
+ private readonly closing;
16
+ constructor(options?: AutoResponderOptions);
17
+ draft(ticket: SupportTicket, resolution: SupportResolution, classification: TicketClassification): Promise<SupportResponseDraft>;
18
+ private generateWithLLM;
19
+ private generateTemplate;
20
+ private buildDraft;
21
+ private renderCategoryIntro;
22
+ private renderCitations;
23
+ }
24
+ //#endregion
25
+ export { AutoResponder, AutoResponderOptions };
@@ -0,0 +1,18 @@
1
+ var e=class{llm;model;tone;closing;constructor(e){this.llm=e?.llm,this.model=e?.model,this.tone=e?.tone??`friendly`,this.closing=e?.closing??(this.tone===`friendly`?`We remain available if you need anything else.`:`Please let us know if you require additional assistance.`)}async draft(e,t,n){return this.llm?this.generateWithLLM(e,t,n):this.generateTemplate(e,t,n)}async generateWithLLM(e,t,n){let r=`You are a ${this.tone} support agent. Draft an email response.
2
+ Ticket Subject: ${e.subject}
3
+ Ticket Body: ${e.body}
4
+ Detected Category: ${n.category}
5
+ Detected Priority: ${n.priority}
6
+ Resolution:
7
+ ${t.answer}
8
+ Citations: ${t.citations.map(e=>e.label).join(`, `)}`,i=(await this.llm.chat([{role:`system`,content:[{type:`text`,text:`Write empathetic, accurate support replies that cite sources when relevant.`}]},{role:`user`,content:[{type:`text`,text:r}]}],{model:this.model})).message.content.map(e=>`text`in e?e.text:``).join(``).trim();return this.buildDraft(e,t,n,i)}generateTemplate(e,t,n){let r=`${e.customerName?`Hi ${e.customerName},`:`Hi there,`}
9
+
10
+ Thanks for contacting us about "${e.subject}". ${this.renderCategoryIntro(n)}
11
+
12
+ ${t.answer}
13
+
14
+ ${this.renderCitations(t)}
15
+ ${this.closing}
16
+
17
+ — ContractSpec Support`;return this.buildDraft(e,t,n,r)}buildDraft(e,t,n,r){return{ticketId:e.id,subject:e.subject.startsWith(`Re:`)?e.subject:`Re: ${e.subject}`,body:r,confidence:Math.min(t.confidence,n.confidence),requiresEscalation:t.actions.some(e=>e.type===`escalate`)||!!n.escalationRequired,citations:t.citations}}renderCategoryIntro(e){switch(e.category){case`billing`:return`I understand billing issues can be stressful, so let me clarify the situation.`;case`technical`:return`I see you encountered a technical issue. Here is what happened and how to fix it.`;case`product`:return`Thanks for sharing feedback about the product. Here are the next steps.`;case`account`:return`Account access is critical, so let me walk you through the resolution.`;case`compliance`:return`Compliance questions require precision. See the policy-aligned answer below.`;default:return`Here is what we found after reviewing your request.`}}renderCitations(e){return e.citations.length?`References:\n${e.citations.map((e,t)=>`- ${e.label||`Source ${t+1}`}${e.url?` (${e.url})`:``}`).join(`
18
+ `)}`:``}};export{e as AutoResponder};
@@ -0,0 +1,19 @@
1
+ import { ResolutionResultPayload } from "../types.js";
2
+
3
+ //#region src/bot/feedback-loop.d.ts
4
+ interface FeedbackMetrics {
5
+ totalTickets: number;
6
+ autoResolved: number;
7
+ escalated: number;
8
+ avgConfidence: number;
9
+ avgResponseTimeMs: number;
10
+ }
11
+ declare class SupportFeedbackLoop {
12
+ private readonly history;
13
+ private readonly responseTimes;
14
+ recordResolution(payload: ResolutionResultPayload, responseTimeMs?: number): void;
15
+ metrics(): FeedbackMetrics;
16
+ feedbackSummary(limit?: number): string;
17
+ }
18
+ //#endregion
19
+ export { FeedbackMetrics, SupportFeedbackLoop };
@@ -0,0 +1,2 @@
1
+ var e=class{history=[];responseTimes=new Map;recordResolution(e,t){this.history.push(e),t!=null&&this.responseTimes.set(e.ticket.id,t)}metrics(){let e=this.history.length,t=this.history.filter(e=>!e.resolution.actions.some(e=>e.type===`escalate`)).length,n=e-t,r=e===0?0:this.history.reduce((e,t)=>e+t.resolution.confidence,0)/e,i=this.responseTimes.size===0?0:[...this.responseTimes.values()].reduce((e,t)=>e+t,0)/this.responseTimes.size;return{totalTickets:e,autoResolved:t,escalated:n,avgConfidence:Number(r.toFixed(2)),avgResponseTimeMs:Math.round(i)}}feedbackSummary(e=5){let t=this.history.slice(-e);return t.length?t.map(e=>{let t=e.resolution.actions.some(e=>e.type===`escalate`)?`Escalated`:`Auto-resolved`;return`${e.ticket.subject} – ${t} (confidence: ${e.resolution.confidence})`}).join(`
2
+ `):`No feedback recorded yet.`}};export{e as SupportFeedbackLoop};
@@ -0,0 +1,4 @@
1
+ import { AutoResponder, AutoResponderOptions } from "./auto-responder.js";
2
+ import { FeedbackMetrics, SupportFeedbackLoop } from "./feedback-loop.js";
3
+ import { SupportToolsetOptions, createSupportTools } from "./tools.js";
4
+ export { AutoResponder, AutoResponderOptions, FeedbackMetrics, SupportFeedbackLoop, SupportToolsetOptions, createSupportTools };
@@ -0,0 +1 @@
1
+ import{AutoResponder as e}from"./auto-responder.js";import{SupportFeedbackLoop as t}from"./feedback-loop.js";import{createSupportTools as n}from"./tools.js";export{e as AutoResponder,t as SupportFeedbackLoop,n as createSupportTools};
@@ -0,0 +1,14 @@
1
+ import { AutoResponder } from "./auto-responder.js";
2
+ import { TicketResolver } from "../rag/ticket-resolver.js";
3
+ import { TicketClassifier } from "../tickets/classifier.js";
4
+ import { Tool } from "@ai-sdk/provider-utils";
5
+
6
+ //#region src/bot/tools.d.ts
7
+ interface SupportToolsetOptions {
8
+ resolver: TicketResolver;
9
+ classifier: TicketClassifier;
10
+ responder: AutoResponder;
11
+ }
12
+ declare function createSupportTools(options: SupportToolsetOptions): Tool[];
13
+ //#endregion
14
+ export { SupportToolsetOptions, createSupportTools };
@@ -0,0 +1 @@
1
+ import{z as e}from"zod";const t=e.object({id:e.string(),subject:e.string(),body:e.string(),channel:e.enum([`email`,`chat`,`phone`,`portal`]),customerName:e.string().optional(),customerEmail:e.string().optional(),metadata:e.object().optional()}),n=e.object({label:e.string(),url:e.string().optional(),snippet:e.string().optional(),score:e.number().optional()}),r=e.object({type:e.enum([`respond`,`escalate`,`refund`,`manual`]),label:e.string(),payload:e.record(e.string(),e.string())}),i=e.object({ticketId:e.string(),answer:e.string(),confidence:e.number(),citations:n.array(),actions:r.array(),escalationReason:e.string().optional(),knowledgeUpdates:e.array(e.string()).optional()}),a=e.object({ticketId:e.string(),category:e.enum([`billing`,`technical`,`product`,`account`,`compliance`,`other`]),priority:e.enum([`urgent`,`high`,`medium`,`low`]),sentiment:e.enum([`positive`,`neutral`,`negative`,`frustrated`]),intents:e.array(e.string()),tags:e.array(e.string()),confidence:e.number(),escalationRequired:e.boolean().optional()});function o(e){if(!e||typeof e!=`object`||!(`ticket`in e))throw Error(`Input must include ticket`);let t=e.ticket;if(!t?.id)throw Error(`Ticket is missing id`);return t}function s(e){if(!(!e||typeof e!=`object`||!(`resolution`in e)))return e.resolution}function c(e){if(!(!e||typeof e!=`object`||!(`classification`in e)))return e.classification}function l(n){return[{title:`support_classify_ticket`,description:`Classify a ticket for priority, sentiment, and category`,inputSchema:e.object({ticket:t}),execute:async e=>{let t=o(e),r=await n.classifier.classify(t);return{content:JSON.stringify(r),metadata:{ticketId:t.id}}}},{title:`support_resolve_ticket`,description:`Generate a knowledge-grounded resolution for a ticket`,inputSchema:e.object({ticket:t}),execute:async e=>{let t=o(e),r=await n.resolver.resolve(t);return{content:JSON.stringify(r),metadata:{ticketId:t.id}}}},{title:`support_draft_response`,description:`Draft a user-facing reply based on resolution + classification`,inputSchema:e.object({ticket:t,resolution:i,classification:a}),execute:async e=>{let t=o(e),r=s(e),i=c(e);if(!r||!i)throw Error(`resolution and classification are required`);let a=await n.responder.draft(t,r,i);return{content:JSON.stringify(a),metadata:{ticketId:t.id}}}}]}export{l as createSupportTools};
@@ -0,0 +1,8 @@
1
+ import { ClassificationResultPayload, ResolutionResultPayload, SupportAction, SupportBotSpec, SupportCitation, SupportResolution, SupportResponseDraft, SupportTicket, TicketCategory, TicketChannel, TicketClassification, TicketPriority, TicketSentiment } from "./types.js";
2
+ import { AutoResponder, AutoResponderOptions } from "./bot/auto-responder.js";
3
+ import { FeedbackMetrics, SupportFeedbackLoop } from "./bot/feedback-loop.js";
4
+ import { KnowledgeRetriever, TicketResolver, TicketResolverOptions } from "./rag/ticket-resolver.js";
5
+ import { TicketClassifier, TicketClassifierOptions } from "./tickets/classifier.js";
6
+ import { SupportToolsetOptions, createSupportTools } from "./bot/tools.js";
7
+ import { SupportBotDefinition, defineSupportBot } from "./spec.js";
8
+ export { AutoResponder, AutoResponderOptions, ClassificationResultPayload, FeedbackMetrics, KnowledgeRetriever, ResolutionResultPayload, SupportAction, SupportBotDefinition, SupportBotSpec, SupportCitation, SupportFeedbackLoop, SupportResolution, SupportResponseDraft, SupportTicket, SupportToolsetOptions, TicketCategory, TicketChannel, TicketClassification, TicketClassifier, TicketClassifierOptions, TicketPriority, TicketResolver, TicketResolverOptions, TicketSentiment, createSupportTools, defineSupportBot };
package/dist/index.js ADDED
@@ -0,0 +1 @@
1
+ import{defineSupportBot as e}from"./spec.js";import{TicketResolver as t}from"./rag/ticket-resolver.js";import{TicketClassifier as n}from"./tickets/classifier.js";import{AutoResponder as r}from"./bot/auto-responder.js";import{SupportFeedbackLoop as i}from"./bot/feedback-loop.js";import{createSupportTools as a}from"./bot/tools.js";import"./bot/index.js";export{r as AutoResponder,i as SupportFeedbackLoop,n as TicketClassifier,t as TicketResolver,a as createSupportTools,e as defineSupportBot};
@@ -0,0 +1,2 @@
1
+ import { KnowledgeRetriever, TicketResolver, TicketResolverOptions } from "./ticket-resolver.js";
2
+ export { KnowledgeRetriever, TicketResolver, TicketResolverOptions };
@@ -0,0 +1 @@
1
+ import{TicketResolver as e}from"./ticket-resolver.js";export{e as TicketResolver};
@@ -0,0 +1,24 @@
1
+ import { SupportResolution, SupportTicket } from "../types.js";
2
+ import { KnowledgeAnswer } from "@lssm/lib.contracts/knowledge/query/service";
3
+
4
+ //#region src/rag/ticket-resolver.d.ts
5
+ interface KnowledgeRetriever {
6
+ query(question: string): Promise<KnowledgeAnswer>;
7
+ }
8
+ interface TicketResolverOptions {
9
+ knowledge: KnowledgeRetriever;
10
+ minConfidence?: number;
11
+ prependPrompt?: string;
12
+ }
13
+ declare class TicketResolver {
14
+ private readonly knowledge;
15
+ private readonly minConfidence;
16
+ private readonly prependPrompt?;
17
+ constructor(options: TicketResolverOptions);
18
+ resolve(ticket: SupportTicket): Promise<SupportResolution>;
19
+ private buildQuestion;
20
+ private toResolution;
21
+ private deriveConfidence;
22
+ }
23
+ //#endregion
24
+ export { KnowledgeRetriever, TicketResolver, TicketResolverOptions };
@@ -0,0 +1,3 @@
1
+ var e=class{knowledge;minConfidence;prependPrompt;constructor(e){this.knowledge=e.knowledge,this.minConfidence=e.minConfidence??.65,this.prependPrompt=e.prependPrompt}async resolve(e){let t=this.buildQuestion(e),n=await this.knowledge.query(t);return this.toResolution(e,n)}buildQuestion(e){let t=[`Subject: ${e.subject}`,`Channel: ${e.channel}`];return e.customerName&&t.push(`Customer: ${e.customerName}`),[this.prependPrompt,t.join(`
2
+ `),`---`,e.body].filter(Boolean).join(`
3
+ `)}toResolution(e,t){let n=t.references.map(e=>({label:typeof e.payload?.title==`string`?e.payload.title:typeof e.payload?.documentId==`string`?e.payload.documentId:e.id,url:typeof e.payload?.url==`string`?e.payload.url:void 0,snippet:typeof e.payload?.text==`string`?e.payload.text.slice(0,280):void 0,score:e.score})),r=this.deriveConfidence(t),i=r<this.minConfidence||n.length===0;return{ticketId:e.id,answer:t.answer,confidence:r,citations:n,actions:[i?{type:`escalate`,label:`Escalate for human review`}:{type:`respond`,label:`Send automated response`}],escalationReason:i?`Insufficient confidence or missing knowledge references`:void 0,knowledgeUpdates:i?[e.body.slice(0,200)]:void 0}}deriveConfidence(e){if(!e.references.length)return .3;let t=e.references[0]?.score??.4,n=Math.min(1,Math.max(0,t)),r=e.usage?.completionTokens?Math.min(e.usage.completionTokens/1e3,.2):0;return Number((n-r).toFixed(2))}};export{e as TicketResolver};
package/dist/spec.d.ts ADDED
@@ -0,0 +1,12 @@
1
+ import { SupportBotSpec } from "./types.js";
2
+ import { AgentSpec, AgentToolConfig } from "@lssm/lib.ai-agent";
3
+
4
+ //#region src/spec.d.ts
5
+ interface SupportBotDefinition {
6
+ base: AgentSpec;
7
+ tools?: AgentToolConfig[];
8
+ autoEscalateThreshold?: number;
9
+ }
10
+ declare function defineSupportBot(definition: SupportBotDefinition): SupportBotSpec;
11
+ //#endregion
12
+ export { SupportBotDefinition, defineSupportBot };
package/dist/spec.js ADDED
@@ -0,0 +1 @@
1
+ import{e}from"./ai-agent/dist/spec/spec.js";import"./ai-agent/dist/index.js";function t(t){return{...e({...t.base,policy:{...t.base.policy,confidence:{min:t.base.policy?.confidence?.min??.7,default:t.base.policy?.confidence?.default??.6},escalation:{confidenceThreshold:t.autoEscalateThreshold??t.base.policy?.escalation?.confidenceThreshold??t.base.policy?.confidence?.min??.7,...t.base.policy?.escalation}},memory:t.base.memory??{maxEntries:120,ttlMinutes:120},tools:t.tools??t.base.tools,instructions:`${t.base.instructions}\n\nAlways cite support knowledge sources and flag compliance/billing issues for human review when unsure.`}),thresholds:{autoResolveMinConfidence:t.autoEscalateThreshold??.75,maxIterations:6}}}export{t as defineSupportBot};
@@ -0,0 +1,24 @@
1
+ import { SupportTicket, TicketCategory, TicketClassification } from "../types.js";
2
+ import { LLMProvider } from "@lssm/lib.contracts/integrations/providers/llm";
3
+
4
+ //#region src/tickets/classifier.d.ts
5
+ interface TicketClassifierOptions {
6
+ keywords?: Partial<Record<TicketCategory, string[]>>;
7
+ llm?: LLMProvider;
8
+ llmModel?: string;
9
+ }
10
+ declare class TicketClassifier {
11
+ private readonly keywords;
12
+ private readonly llm?;
13
+ private readonly llmModel?;
14
+ constructor(options?: TicketClassifierOptions);
15
+ classify(ticket: SupportTicket): Promise<TicketClassification>;
16
+ private heuristicClassification;
17
+ private detectCategory;
18
+ private detectPriority;
19
+ private detectSentiment;
20
+ private extractIntents;
21
+ private estimateConfidence;
22
+ }
23
+ //#endregion
24
+ export { TicketClassifier, TicketClassifierOptions };
@@ -0,0 +1 @@
1
+ const e={billing:[`invoice`,`payout`,`refund`,`charge`,`billing`,`payment`],technical:[`bug`,`error`,`crash`,`issue`,`failed`,`timeout`],product:[`feature`,`roadmap`,`idea`,`request`,`feedback`],account:[`login`,`password`,`2fa`,`account`,`profile`,`email change`],compliance:[`kyc`,`aml`,`compliance`,`regulation`,`gdpr`],other:[]},t={urgent:[`urgent`,`asap`,`immediately`,`today`,`right away`],high:[`high priority`,`blocking`,`major`,`critical`],medium:[`soon`,`next few days`],low:[`nice to have`,`when possible`,`later`]},n={positive:[`love`,`great`,`awesome`,`thank you`],neutral:[`question`,`wonder`,`curious`],negative:[`unhappy`,`bad`,`terrible`,`awful`,`angry`],frustrated:[`furious`,`frustrated`,`fed up`,`ridiculous`]};var r=class{keywords;llm;llmModel;constructor(t){this.keywords={...e,...t?.keywords??{}},this.llm=t?.llm,this.llmModel=t?.llmModel}async classify(e){let t=this.heuristicClassification(e);if(!this.llm)return t;try{let n=(await this.llm.chat([{role:`system`,content:[{type:`text`,text:`Classify the support ticket.`}]},{role:`user`,content:[{type:`text`,text:JSON.stringify({subject:e.subject,body:e.body,channel:e.channel})}]}],{responseFormat:`json`,model:this.llmModel})).message.content.find(e=>`text`in e);if(n&&`text`in n){let e=JSON.parse(n.text);return{...t,...e,intents:e.intents??t.intents,tags:e.tags??t.tags}}}catch{}return t}heuristicClassification(e){let t=`${e.subject}\n${e.body}`.toLowerCase(),n=this.detectCategory(t),r=this.detectPriority(t),i=this.detectSentiment(t),a=this.extractIntents(t),o=a.slice(0,3),s=this.estimateConfidence(n,r,i);return{ticketId:e.id,category:n,priority:r,sentiment:i,intents:a,tags:o,confidence:s,escalationRequired:r===`urgent`||n===`compliance`}}detectCategory(e){for(let[t,n]of Object.entries(this.keywords))if(n.some(t=>e.includes(t)))return t;return`other`}detectPriority(e){for(let n of[`urgent`,`high`,`medium`,`low`])if(t[n].some(t=>e.includes(t)))return n;return`medium`}detectSentiment(e){for(let t of[`frustrated`,`negative`,`neutral`,`positive`])if(n[t].some(t=>e.includes(t)))return t;return`neutral`}extractIntents(e){let t=[];return(e.includes(`refund`)||e.includes(`chargeback`))&&t.push(`refund`),e.includes(`payout`)&&t.push(`payout`),e.includes(`login`)&&t.push(`login-help`),e.includes(`feature`)&&t.push(`feature-request`),(e.includes(`bug`)||e.includes(`error`))&&t.push(`bug-report`),t.length?t:[`general`]}estimateConfidence(e,t,n){let r=.6;return e!==`other`&&(r+=.1),(t===`urgent`||t===`low`)&&(r+=.05),n===`frustrated`&&(r-=.05),Math.min(.95,Math.max(.4,Number(r.toFixed(2))))}};export{r as TicketClassifier};
@@ -0,0 +1,2 @@
1
+ import { TicketClassifier, TicketClassifierOptions } from "./classifier.js";
2
+ export { TicketClassifier, TicketClassifierOptions };
@@ -0,0 +1 @@
1
+ import{TicketClassifier as e}from"./classifier.js";export{e as TicketClassifier};
@@ -0,0 +1,75 @@
1
+ import { AgentSpec } from "@lssm/lib.ai-agent";
2
+
3
+ //#region src/types.d.ts
4
+ type TicketPriority = 'low' | 'medium' | 'high' | 'urgent';
5
+ type TicketCategory = 'billing' | 'technical' | 'product' | 'account' | 'compliance' | 'other';
6
+ type TicketChannel = 'email' | 'chat' | 'phone' | 'portal';
7
+ type TicketSentiment = 'positive' | 'neutral' | 'negative' | 'frustrated';
8
+ interface SupportTicket {
9
+ id: string;
10
+ subject: string;
11
+ body: string;
12
+ channel: TicketChannel;
13
+ locale?: string;
14
+ customerEmail?: string;
15
+ customerName?: string;
16
+ metadata?: Record<string, string>;
17
+ }
18
+ interface TicketClassification {
19
+ ticketId: string;
20
+ category: TicketCategory;
21
+ priority: TicketPriority;
22
+ sentiment: TicketSentiment;
23
+ intents: string[];
24
+ tags: string[];
25
+ confidence: number;
26
+ escalationRequired?: boolean;
27
+ }
28
+ interface SupportCitation {
29
+ label: string;
30
+ url?: string;
31
+ snippet?: string;
32
+ score?: number;
33
+ }
34
+ interface SupportAction {
35
+ type: 'respond' | 'escalate' | 'refund' | 'manual';
36
+ label: string;
37
+ payload?: Record<string, string>;
38
+ }
39
+ interface SupportResolution {
40
+ ticketId: string;
41
+ answer: string;
42
+ confidence: number;
43
+ citations: SupportCitation[];
44
+ actions: SupportAction[];
45
+ escalationReason?: string;
46
+ knowledgeUpdates?: string[];
47
+ }
48
+ interface SupportResponseDraft {
49
+ ticketId: string;
50
+ subject: string;
51
+ body: string;
52
+ confidence: number;
53
+ requiresEscalation: boolean;
54
+ citations: SupportCitation[];
55
+ }
56
+ interface SupportBotSpec extends AgentSpec {
57
+ thresholds?: {
58
+ autoResolveMinConfidence?: number;
59
+ maxIterations?: number;
60
+ };
61
+ review?: {
62
+ queueName?: string;
63
+ approvalWorkflow?: string;
64
+ };
65
+ }
66
+ interface ClassificationResultPayload {
67
+ ticket: SupportTicket;
68
+ classification: TicketClassification;
69
+ }
70
+ interface ResolutionResultPayload extends ClassificationResultPayload {
71
+ resolution: SupportResolution;
72
+ draft: SupportResponseDraft;
73
+ }
74
+ //#endregion
75
+ export { ClassificationResultPayload, ResolutionResultPayload, SupportAction, SupportBotSpec, SupportCitation, SupportResolution, SupportResponseDraft, SupportTicket, TicketCategory, TicketChannel, TicketClassification, TicketPriority, TicketSentiment };
package/dist/types.js ADDED
File without changes
package/package.json ADDED
@@ -0,0 +1,51 @@
1
+ {
2
+ "name": "@lssm/lib.support-bot",
3
+ "version": "0.0.0-canary-20251206160926",
4
+ "type": "module",
5
+ "main": "./dist/index.js",
6
+ "module": "./dist/index.js",
7
+ "types": "./dist/index.d.ts",
8
+ "files": [
9
+ "dist",
10
+ "README.md"
11
+ ],
12
+ "scripts": {
13
+ "publish:pkg": "bun publish --tolerate-republish --ignore-scripts --verbose",
14
+ "build": "bun build:bundle && bun build:types",
15
+ "build:bundle": "tsdown",
16
+ "build:types": "tsc --noEmit",
17
+ "dev": "bun build:bundle --watch",
18
+ "clean": "rimraf dist .turbo",
19
+ "lint": "bun lint:fix",
20
+ "lint:fix": "eslint src --fix",
21
+ "lint:check": "eslint src",
22
+ "test": "bun run"
23
+ },
24
+ "dependencies": {
25
+ "@lssm/lib.ai-agent": "workspace:*",
26
+ "@lssm/lib.contracts": "workspace:*"
27
+ },
28
+ "devDependencies": {
29
+ "@lssm/tool.tsdown": "workspace:*",
30
+ "@lssm/tool.typescript": "workspace:*",
31
+ "tsdown": "^0.17.0",
32
+ "typescript": "^5.9.3"
33
+ },
34
+ "exports": {
35
+ ".": "./dist/index.js",
36
+ "./bot": "./dist/bot/index.js",
37
+ "./bot/auto-responder": "./dist/bot/auto-responder.js",
38
+ "./bot/feedback-loop": "./dist/bot/feedback-loop.js",
39
+ "./bot/tools": "./dist/bot/tools.js",
40
+ "./rag": "./dist/rag/index.js",
41
+ "./rag/ticket-resolver": "./dist/rag/ticket-resolver.js",
42
+ "./spec": "./dist/spec.js",
43
+ "./tickets": "./dist/tickets/index.js",
44
+ "./tickets/classifier": "./dist/tickets/classifier.js",
45
+ "./types": "./dist/types.js",
46
+ "./*": "./*"
47
+ },
48
+ "publishConfig": {
49
+ "access": "public"
50
+ }
51
+ }