@lssm/module.ai-chat 1.41.1 → 1.42.1

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 (102) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +3 -0
  3. package/dist/ai-chat.feature.d.ts +12 -0
  4. package/dist/ai-chat.feature.d.ts.map +1 -0
  5. package/dist/ai-chat.feature.js +95 -1
  6. package/dist/ai-chat.feature.js.map +1 -0
  7. package/dist/ai-chat.operations.d.ts +243 -0
  8. package/dist/ai-chat.operations.d.ts.map +1 -0
  9. package/dist/ai-chat.operations.js +174 -0
  10. package/dist/ai-chat.operations.js.map +1 -0
  11. package/dist/context/context-builder.d.ts +57 -0
  12. package/dist/context/context-builder.d.ts.map +1 -0
  13. package/dist/context/context-builder.js +148 -2
  14. package/dist/context/context-builder.js.map +1 -0
  15. package/dist/context/file-operations.d.ts +100 -0
  16. package/dist/context/file-operations.d.ts.map +1 -0
  17. package/dist/context/file-operations.js +175 -1
  18. package/dist/context/file-operations.js.map +1 -0
  19. package/dist/context/index.d.ts +4 -0
  20. package/dist/context/index.js +5 -1
  21. package/dist/context/workspace-context.d.ts +117 -0
  22. package/dist/context/workspace-context.d.ts.map +1 -0
  23. package/dist/context/workspace-context.js +124 -2
  24. package/dist/context/workspace-context.js.map +1 -0
  25. package/dist/core/chat-service.d.ts +73 -0
  26. package/dist/core/chat-service.d.ts.map +1 -0
  27. package/dist/core/chat-service.js +215 -2
  28. package/dist/core/chat-service.js.map +1 -0
  29. package/dist/core/conversation-store.d.ts +74 -0
  30. package/dist/core/conversation-store.d.ts.map +1 -0
  31. package/dist/core/conversation-store.js +109 -1
  32. package/dist/core/conversation-store.js.map +1 -0
  33. package/dist/core/index.d.ts +4 -0
  34. package/dist/core/index.js +4 -1
  35. package/dist/core/message-types.d.ts +150 -0
  36. package/dist/core/message-types.d.ts.map +1 -0
  37. package/dist/events.d.ts +115 -0
  38. package/dist/events.d.ts.map +1 -0
  39. package/dist/events.js +100 -0
  40. package/dist/events.js.map +1 -0
  41. package/dist/index.d.ts +21 -0
  42. package/dist/index.js +23 -1
  43. package/dist/libs/schema/dist/EnumType.js +2 -0
  44. package/dist/libs/schema/dist/FieldType.js +50 -0
  45. package/dist/libs/schema/dist/FieldType.js.map +1 -0
  46. package/dist/libs/schema/dist/ScalarTypeEnum.js +237 -0
  47. package/dist/libs/schema/dist/ScalarTypeEnum.js.map +1 -0
  48. package/dist/libs/schema/dist/SchemaModel.js +40 -0
  49. package/dist/libs/schema/dist/SchemaModel.js.map +1 -0
  50. package/dist/libs/schema/dist/entity/defineEntity.js +1 -0
  51. package/dist/libs/schema/dist/entity/index.js +2 -0
  52. package/dist/libs/schema/dist/entity/types.js +1 -0
  53. package/dist/libs/schema/dist/index.js +6 -0
  54. package/dist/presentation/components/ChatContainer.d.ts +21 -0
  55. package/dist/presentation/components/ChatContainer.d.ts.map +1 -0
  56. package/dist/presentation/components/ChatContainer.js +63 -1
  57. package/dist/presentation/components/ChatContainer.js.map +1 -0
  58. package/dist/presentation/components/ChatInput.d.ts +35 -0
  59. package/dist/presentation/components/ChatInput.d.ts.map +1 -0
  60. package/dist/presentation/components/ChatInput.js +149 -1
  61. package/dist/presentation/components/ChatInput.js.map +1 -0
  62. package/dist/presentation/components/ChatMessage.d.ts +24 -0
  63. package/dist/presentation/components/ChatMessage.d.ts.map +1 -0
  64. package/dist/presentation/components/ChatMessage.js +136 -1
  65. package/dist/presentation/components/ChatMessage.js.map +1 -0
  66. package/dist/presentation/components/CodePreview.d.ts +40 -0
  67. package/dist/presentation/components/CodePreview.d.ts.map +1 -0
  68. package/dist/presentation/components/CodePreview.js +127 -2
  69. package/dist/presentation/components/CodePreview.js.map +1 -0
  70. package/dist/presentation/components/ContextIndicator.d.ts +26 -0
  71. package/dist/presentation/components/ContextIndicator.d.ts.map +1 -0
  72. package/dist/presentation/components/ContextIndicator.js +97 -1
  73. package/dist/presentation/components/ContextIndicator.js.map +1 -0
  74. package/dist/presentation/components/ModelPicker.d.ts +39 -0
  75. package/dist/presentation/components/ModelPicker.d.ts.map +1 -0
  76. package/dist/presentation/components/ModelPicker.js +202 -1
  77. package/dist/presentation/components/ModelPicker.js.map +1 -0
  78. package/dist/presentation/components/index.d.ts +7 -0
  79. package/dist/presentation/components/index.js +8 -1
  80. package/dist/presentation/hooks/index.d.ts +3 -0
  81. package/dist/presentation/hooks/index.js +4 -1
  82. package/dist/presentation/hooks/useChat.d.ts +67 -0
  83. package/dist/presentation/hooks/useChat.d.ts.map +1 -0
  84. package/dist/presentation/hooks/useChat.js +172 -1
  85. package/dist/presentation/hooks/useChat.js.map +1 -0
  86. package/dist/presentation/hooks/useProviders.d.ts +38 -0
  87. package/dist/presentation/hooks/useProviders.d.ts.map +1 -0
  88. package/dist/presentation/hooks/useProviders.js +41 -1
  89. package/dist/presentation/hooks/useProviders.js.map +1 -0
  90. package/dist/presentation/index.d.ts +11 -0
  91. package/dist/presentation/index.js +12 -1
  92. package/dist/providers/chat-utilities.d.ts +15 -0
  93. package/dist/providers/chat-utilities.d.ts.map +1 -0
  94. package/dist/providers/chat-utilities.js +17 -1
  95. package/dist/providers/chat-utilities.js.map +1 -0
  96. package/dist/providers/index.d.ts +3 -0
  97. package/dist/providers/index.js +4 -1
  98. package/dist/schema.d.ts +222 -0
  99. package/dist/schema.d.ts.map +1 -0
  100. package/dist/schema.js +102 -0
  101. package/dist/schema.js.map +1 -0
  102. package/package.json +29 -22
@@ -1,2 +1,124 @@
1
- var e=class{workspacePath;allowWrites;specs=[];files=[];initialized=!1;constructor(e){this.workspacePath=e.workspacePath,this.allowWrites=e.allowWrites??!1}async initialize(){this.initialized||=!0}getSpecs(){return this.specs}getFiles(){return this.files}addSpecs(e){this.specs.push(...e)}addFiles(e){this.files.push(...e)}getSummary(){let e=this.specs.filter(e=>e.type===`command`).length,t=this.specs.filter(e=>e.type===`query`).length,n=this.specs.filter(e=>e.type===`event`).length,r=this.specs.filter(e=>e.type===`presentation`).length,i=this.files.filter(e=>e.extension===`.ts`).length,a=this.files.filter(e=>e.isSpec).length;return{name:this.workspacePath.split(`/`).pop()??`workspace`,path:this.workspacePath,specs:{total:this.specs.length,commands:e,queries:t,events:n,presentations:r},files:{total:this.files.length,typescript:i,specFiles:a}}}getContextSummary(){let e=this.getSummary(),t=[`Workspace: ${e.name}`,`Path: ${e.path}`,``,`### Specs`,`- Commands: ${e.specs.commands}`,`- Queries: ${e.specs.queries}`,`- Events: ${e.specs.events}`,`- Presentations: ${e.specs.presentations}`];if(this.specs.length>0){t.push(``,`### Available Specs`);for(let e of this.specs.slice(0,20))t.push(`- ${e.name} (${e.type})`);this.specs.length>20&&t.push(`- ... and ${this.specs.length-20} more`)}return t.join(`
2
- `)}findSpecs(e){let t=e.toLowerCase();return this.specs.filter(e=>e.name.toLowerCase().includes(t)||e.description?.toLowerCase().includes(t)||e.tags?.some(e=>e.toLowerCase().includes(t)))}findFiles(e){let t=e.toLowerCase();return this.files.filter(e=>e.path.toLowerCase().includes(t)||e.name.toLowerCase().includes(t))}};async function t(t,n){let r=new e({workspacePath:t,...n});return await r.initialize(),r}export{e as WorkspaceContext,t as createWorkspaceContext};
1
+ //#region src/context/workspace-context.ts
2
+ /**
3
+ * Workspace context for AI chat
4
+ */
5
+ var WorkspaceContext = class {
6
+ workspacePath;
7
+ allowWrites;
8
+ specs = [];
9
+ files = [];
10
+ initialized = false;
11
+ constructor(config) {
12
+ this.workspacePath = config.workspacePath;
13
+ this.allowWrites = config.allowWrites ?? false;
14
+ }
15
+ /**
16
+ * Initialize the workspace context by scanning files
17
+ */
18
+ async initialize() {
19
+ if (this.initialized) return;
20
+ this.initialized = true;
21
+ }
22
+ /**
23
+ * Get all discovered specs
24
+ */
25
+ getSpecs() {
26
+ return this.specs;
27
+ }
28
+ /**
29
+ * Get all discovered files
30
+ */
31
+ getFiles() {
32
+ return this.files;
33
+ }
34
+ /**
35
+ * Add specs to the context
36
+ */
37
+ addSpecs(specs) {
38
+ this.specs.push(...specs);
39
+ }
40
+ /**
41
+ * Add files to the context
42
+ */
43
+ addFiles(files) {
44
+ this.files.push(...files);
45
+ }
46
+ /**
47
+ * Get a summary of the workspace for context
48
+ */
49
+ getSummary() {
50
+ const commands = this.specs.filter((s) => s.type === "command").length;
51
+ const queries = this.specs.filter((s) => s.type === "query").length;
52
+ const events = this.specs.filter((s) => s.type === "event").length;
53
+ const presentations = this.specs.filter((s) => s.type === "presentation").length;
54
+ const tsFiles = this.files.filter((f) => f.extension === ".ts").length;
55
+ const specFiles = this.files.filter((f) => f.isSpec).length;
56
+ return {
57
+ name: this.workspacePath.split("/").pop() ?? "workspace",
58
+ path: this.workspacePath,
59
+ specs: {
60
+ total: this.specs.length,
61
+ commands,
62
+ queries,
63
+ events,
64
+ presentations
65
+ },
66
+ files: {
67
+ total: this.files.length,
68
+ typescript: tsFiles,
69
+ specFiles
70
+ }
71
+ };
72
+ }
73
+ /**
74
+ * Get a context summary for LLM prompts
75
+ */
76
+ getContextSummary() {
77
+ const summary = this.getSummary();
78
+ const parts = [
79
+ `Workspace: ${summary.name}`,
80
+ `Path: ${summary.path}`,
81
+ "",
82
+ "### Specs",
83
+ `- Commands: ${summary.specs.commands}`,
84
+ `- Queries: ${summary.specs.queries}`,
85
+ `- Events: ${summary.specs.events}`,
86
+ `- Presentations: ${summary.specs.presentations}`
87
+ ];
88
+ if (this.specs.length > 0) {
89
+ parts.push("", "### Available Specs");
90
+ for (const spec of this.specs.slice(0, 20)) parts.push(`- ${spec.name} (${spec.type})`);
91
+ if (this.specs.length > 20) parts.push(`- ... and ${this.specs.length - 20} more`);
92
+ }
93
+ return parts.join("\n");
94
+ }
95
+ /**
96
+ * Find specs matching a query
97
+ */
98
+ findSpecs(query) {
99
+ const lowerQuery = query.toLowerCase();
100
+ return this.specs.filter((s) => s.name.toLowerCase().includes(lowerQuery) || s.description?.toLowerCase().includes(lowerQuery) || s.tags?.some((t) => t.toLowerCase().includes(lowerQuery)));
101
+ }
102
+ /**
103
+ * Find files matching a query
104
+ */
105
+ findFiles(query) {
106
+ const lowerQuery = query.toLowerCase();
107
+ return this.files.filter((f) => f.path.toLowerCase().includes(lowerQuery) || f.name.toLowerCase().includes(lowerQuery));
108
+ }
109
+ };
110
+ /**
111
+ * Create a workspace context from a path
112
+ */
113
+ async function createWorkspaceContext(path, options) {
114
+ const context = new WorkspaceContext({
115
+ workspacePath: path,
116
+ ...options
117
+ });
118
+ await context.initialize();
119
+ return context;
120
+ }
121
+
122
+ //#endregion
123
+ export { WorkspaceContext, createWorkspaceContext };
124
+ //# sourceMappingURL=workspace-context.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"workspace-context.js","names":["parts: string[]"],"sources":["../../src/context/workspace-context.ts"],"sourcesContent":["/**\n * Workspace context management\n *\n * Provides access to specs, files, and codebase information\n * for context-aware AI chat assistance.\n */\n\n/**\n * Spec information for context\n */\nexport interface SpecInfo {\n name: string;\n version: number;\n type: 'command' | 'query' | 'event' | 'presentation';\n path: string;\n description?: string;\n tags?: string[];\n}\n\n/**\n * File information for context\n */\nexport interface FileInfo {\n path: string;\n relativePath: string;\n name: string;\n extension: string;\n size: number;\n isSpec: boolean;\n}\n\n/**\n * Workspace summary for context\n */\nexport interface WorkspaceSummary {\n name: string;\n path: string;\n specs: {\n total: number;\n commands: number;\n queries: number;\n events: number;\n presentations: number;\n };\n files: {\n total: number;\n typescript: number;\n specFiles: number;\n };\n}\n\n/**\n * Configuration for workspace context\n */\nexport interface WorkspaceContextConfig {\n /** Root path of the workspace */\n workspacePath: string;\n /** File patterns to include */\n includePatterns?: string[];\n /** File patterns to exclude */\n excludePatterns?: string[];\n /** Maximum file size to read (bytes) */\n maxFileSize?: number;\n /** Whether to enable file writes */\n allowWrites?: boolean;\n}\n\n/**\n * Workspace context for AI chat\n */\nexport class WorkspaceContext {\n readonly workspacePath: string;\n readonly allowWrites: boolean;\n\n private specs: SpecInfo[] = [];\n private files: FileInfo[] = [];\n private initialized = false;\n\n constructor(config: WorkspaceContextConfig) {\n this.workspacePath = config.workspacePath;\n this.allowWrites = config.allowWrites ?? false;\n }\n\n /**\n * Initialize the workspace context by scanning files\n */\n async initialize(): Promise<void> {\n if (this.initialized) return;\n\n // This would scan the workspace for specs and files\n // For now, we just mark as initialized\n this.initialized = true;\n }\n\n /**\n * Get all discovered specs\n */\n getSpecs(): SpecInfo[] {\n return this.specs;\n }\n\n /**\n * Get all discovered files\n */\n getFiles(): FileInfo[] {\n return this.files;\n }\n\n /**\n * Add specs to the context\n */\n addSpecs(specs: SpecInfo[]): void {\n this.specs.push(...specs);\n }\n\n /**\n * Add files to the context\n */\n addFiles(files: FileInfo[]): void {\n this.files.push(...files);\n }\n\n /**\n * Get a summary of the workspace for context\n */\n getSummary(): WorkspaceSummary {\n const commands = this.specs.filter((s) => s.type === 'command').length;\n const queries = this.specs.filter((s) => s.type === 'query').length;\n const events = this.specs.filter((s) => s.type === 'event').length;\n const presentations = this.specs.filter(\n (s) => s.type === 'presentation'\n ).length;\n\n const tsFiles = this.files.filter((f) => f.extension === '.ts').length;\n const specFiles = this.files.filter((f) => f.isSpec).length;\n\n return {\n name: this.workspacePath.split('/').pop() ?? 'workspace',\n path: this.workspacePath,\n specs: {\n total: this.specs.length,\n commands,\n queries,\n events,\n presentations,\n },\n files: {\n total: this.files.length,\n typescript: tsFiles,\n specFiles,\n },\n };\n }\n\n /**\n * Get a context summary for LLM prompts\n */\n getContextSummary(): string {\n const summary = this.getSummary();\n\n const parts: string[] = [\n `Workspace: ${summary.name}`,\n `Path: ${summary.path}`,\n '',\n '### Specs',\n `- Commands: ${summary.specs.commands}`,\n `- Queries: ${summary.specs.queries}`,\n `- Events: ${summary.specs.events}`,\n `- Presentations: ${summary.specs.presentations}`,\n ];\n\n if (this.specs.length > 0) {\n parts.push('', '### Available Specs');\n for (const spec of this.specs.slice(0, 20)) {\n parts.push(`- ${spec.name} (${spec.type})`);\n }\n if (this.specs.length > 20) {\n parts.push(`- ... and ${this.specs.length - 20} more`);\n }\n }\n\n return parts.join('\\n');\n }\n\n /**\n * Find specs matching a query\n */\n findSpecs(query: string): SpecInfo[] {\n const lowerQuery = query.toLowerCase();\n return this.specs.filter(\n (s) =>\n s.name.toLowerCase().includes(lowerQuery) ||\n s.description?.toLowerCase().includes(lowerQuery) ||\n s.tags?.some((t) => t.toLowerCase().includes(lowerQuery))\n );\n }\n\n /**\n * Find files matching a query\n */\n findFiles(query: string): FileInfo[] {\n const lowerQuery = query.toLowerCase();\n return this.files.filter(\n (f) =>\n f.path.toLowerCase().includes(lowerQuery) ||\n f.name.toLowerCase().includes(lowerQuery)\n );\n }\n}\n\n/**\n * Create a workspace context from a path\n */\nexport async function createWorkspaceContext(\n path: string,\n options?: Partial<WorkspaceContextConfig>\n): Promise<WorkspaceContext> {\n const context = new WorkspaceContext({\n workspacePath: path,\n ...options,\n });\n await context.initialize();\n return context;\n}\n"],"mappings":";;;;AAsEA,IAAa,mBAAb,MAA8B;CAC5B,AAAS;CACT,AAAS;CAET,AAAQ,QAAoB,EAAE;CAC9B,AAAQ,QAAoB,EAAE;CAC9B,AAAQ,cAAc;CAEtB,YAAY,QAAgC;AAC1C,OAAK,gBAAgB,OAAO;AAC5B,OAAK,cAAc,OAAO,eAAe;;;;;CAM3C,MAAM,aAA4B;AAChC,MAAI,KAAK,YAAa;AAItB,OAAK,cAAc;;;;;CAMrB,WAAuB;AACrB,SAAO,KAAK;;;;;CAMd,WAAuB;AACrB,SAAO,KAAK;;;;;CAMd,SAAS,OAAyB;AAChC,OAAK,MAAM,KAAK,GAAG,MAAM;;;;;CAM3B,SAAS,OAAyB;AAChC,OAAK,MAAM,KAAK,GAAG,MAAM;;;;;CAM3B,aAA+B;EAC7B,MAAM,WAAW,KAAK,MAAM,QAAQ,MAAM,EAAE,SAAS,UAAU,CAAC;EAChE,MAAM,UAAU,KAAK,MAAM,QAAQ,MAAM,EAAE,SAAS,QAAQ,CAAC;EAC7D,MAAM,SAAS,KAAK,MAAM,QAAQ,MAAM,EAAE,SAAS,QAAQ,CAAC;EAC5D,MAAM,gBAAgB,KAAK,MAAM,QAC9B,MAAM,EAAE,SAAS,eACnB,CAAC;EAEF,MAAM,UAAU,KAAK,MAAM,QAAQ,MAAM,EAAE,cAAc,MAAM,CAAC;EAChE,MAAM,YAAY,KAAK,MAAM,QAAQ,MAAM,EAAE,OAAO,CAAC;AAErD,SAAO;GACL,MAAM,KAAK,cAAc,MAAM,IAAI,CAAC,KAAK,IAAI;GAC7C,MAAM,KAAK;GACX,OAAO;IACL,OAAO,KAAK,MAAM;IAClB;IACA;IACA;IACA;IACD;GACD,OAAO;IACL,OAAO,KAAK,MAAM;IAClB,YAAY;IACZ;IACD;GACF;;;;;CAMH,oBAA4B;EAC1B,MAAM,UAAU,KAAK,YAAY;EAEjC,MAAMA,QAAkB;GACtB,cAAc,QAAQ;GACtB,SAAS,QAAQ;GACjB;GACA;GACA,eAAe,QAAQ,MAAM;GAC7B,cAAc,QAAQ,MAAM;GAC5B,aAAa,QAAQ,MAAM;GAC3B,oBAAoB,QAAQ,MAAM;GACnC;AAED,MAAI,KAAK,MAAM,SAAS,GAAG;AACzB,SAAM,KAAK,IAAI,sBAAsB;AACrC,QAAK,MAAM,QAAQ,KAAK,MAAM,MAAM,GAAG,GAAG,CACxC,OAAM,KAAK,KAAK,KAAK,KAAK,IAAI,KAAK,KAAK,GAAG;AAE7C,OAAI,KAAK,MAAM,SAAS,GACtB,OAAM,KAAK,aAAa,KAAK,MAAM,SAAS,GAAG,OAAO;;AAI1D,SAAO,MAAM,KAAK,KAAK;;;;;CAMzB,UAAU,OAA2B;EACnC,MAAM,aAAa,MAAM,aAAa;AACtC,SAAO,KAAK,MAAM,QACf,MACC,EAAE,KAAK,aAAa,CAAC,SAAS,WAAW,IACzC,EAAE,aAAa,aAAa,CAAC,SAAS,WAAW,IACjD,EAAE,MAAM,MAAM,MAAM,EAAE,aAAa,CAAC,SAAS,WAAW,CAAC,CAC5D;;;;;CAMH,UAAU,OAA2B;EACnC,MAAM,aAAa,MAAM,aAAa;AACtC,SAAO,KAAK,MAAM,QACf,MACC,EAAE,KAAK,aAAa,CAAC,SAAS,WAAW,IACzC,EAAE,KAAK,aAAa,CAAC,SAAS,WAAW,CAC5C;;;;;;AAOL,eAAsB,uBACpB,MACA,SAC2B;CAC3B,MAAM,UAAU,IAAI,iBAAiB;EACnC,eAAe;EACf,GAAG;EACJ,CAAC;AACF,OAAM,QAAQ,YAAY;AAC1B,QAAO"}
@@ -0,0 +1,73 @@
1
+ import { WorkspaceContext } from "../context/workspace-context.js";
2
+ import { ChatConversation, SendMessageOptions, SendMessageResult, StreamMessageResult } from "./message-types.js";
3
+ import { ConversationStore } from "./conversation-store.js";
4
+ import { Provider } from "@lssm/lib.ai-providers";
5
+
6
+ //#region src/core/chat-service.d.ts
7
+
8
+ /**
9
+ * Configuration for ChatService
10
+ */
11
+ interface ChatServiceConfig {
12
+ /** LLM provider to use */
13
+ provider: Provider;
14
+ /** Optional workspace context for code-aware chat */
15
+ context?: WorkspaceContext;
16
+ /** Optional conversation store (defaults to in-memory) */
17
+ store?: ConversationStore;
18
+ /** Default system prompt */
19
+ systemPrompt?: string;
20
+ /** Maximum conversation history to include */
21
+ maxHistoryMessages?: number;
22
+ /** Callback for usage tracking */
23
+ onUsage?: (usage: {
24
+ inputTokens: number;
25
+ outputTokens: number;
26
+ }) => void;
27
+ }
28
+ /**
29
+ * Main chat service for AI-powered conversations
30
+ */
31
+ declare class ChatService {
32
+ private readonly provider;
33
+ private readonly context?;
34
+ private readonly store;
35
+ private readonly systemPrompt;
36
+ private readonly maxHistoryMessages;
37
+ private readonly onUsage?;
38
+ constructor(config: ChatServiceConfig);
39
+ /**
40
+ * Send a message and get a complete response
41
+ */
42
+ send(options: SendMessageOptions): Promise<SendMessageResult>;
43
+ /**
44
+ * Send a message and get a streaming response
45
+ */
46
+ stream(options: SendMessageOptions): Promise<StreamMessageResult>;
47
+ /**
48
+ * Get a conversation by ID
49
+ */
50
+ getConversation(conversationId: string): Promise<ChatConversation | null>;
51
+ /**
52
+ * List conversations
53
+ */
54
+ listConversations(options?: {
55
+ limit?: number;
56
+ offset?: number;
57
+ }): Promise<ChatConversation[]>;
58
+ /**
59
+ * Delete a conversation
60
+ */
61
+ deleteConversation(conversationId: string): Promise<boolean>;
62
+ /**
63
+ * Build prompt string for LLM
64
+ */
65
+ private buildPrompt;
66
+ }
67
+ /**
68
+ * Create a chat service with the given configuration
69
+ */
70
+ declare function createChatService(config: ChatServiceConfig): ChatService;
71
+ //#endregion
72
+ export { ChatService, ChatServiceConfig, createChatService };
73
+ //# sourceMappingURL=chat-service.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-service.d.ts","names":[],"sources":["../../src/core/chat-service.ts"],"sourcesContent":[],"mappings":";;;;;;;;;AAmBA;AAEY,UAFK,iBAAA,CAEL;EAEA;EAEF,QAAA,EAJE,QAIF;EAAiB;EA8Bd,OAAA,CAAA,EAhCD,gBAgCY;EAWF;EAYA,KAAA,CAAA,EArDZ,iBAqDY;EAA6B;EAAR,YAAA,CAAA,EAAA,MAAA;EA6EnB;EAA6B,kBAAA,CAAA,EAAA,MAAA;EAAR;EAqGhC,OAAA,CAAA,EAAA,CAAA,KAAA,EAAA;IAAR,WAAA,EAAA,MAAA;IAUS,YAAA,EAAA,MAAA;EAAR,CAAA,EAAA,GAAA,IAAA;;;AA2DN;;cA9Qa,WAAA;;;;;;;sBAWS;;;;gBAYA,qBAAqB,QAAQ;;;;kBA6E3B,qBAAqB,QAAQ;;;;2CAqGhD,QAAQ;;;;;;;MAUP,QAAQ;;;;8CAUsC;;;;;;;;;iBAiDpC,iBAAA,SAA0B,oBAAoB"}
@@ -1,4 +1,14 @@
1
- import{InMemoryConversationStore as e}from"./conversation-store.js";import{generateText as t,streamText as n}from"ai";var r=class{provider;context;store;systemPrompt;maxHistoryMessages;onUsage;constructor(t){this.provider=t.provider,this.context=t.context,this.store=t.store??new e,this.systemPrompt=t.systemPrompt??`You are ContractSpec AI, an expert coding assistant specialized in ContractSpec development.
1
+ import { InMemoryConversationStore } from "./conversation-store.js";
2
+ import { generateText, streamText } from "ai";
3
+
4
+ //#region src/core/chat-service.ts
5
+ /**
6
+ * Main chat orchestration service
7
+ */
8
+ /**
9
+ * Default system prompt for ContractSpec vibe coding
10
+ */
11
+ const DEFAULT_SYSTEM_PROMPT = `You are ContractSpec AI, an expert coding assistant specialized in ContractSpec development.
2
12
 
3
13
  Your capabilities:
4
14
  - Help users create, modify, and understand ContractSpec specifications
@@ -11,4 +21,207 @@ Guidelines:
11
21
  - Provide code examples when helpful
12
22
  - Reference relevant ContractSpec concepts and patterns
13
23
  - Ask clarifying questions when the user's intent is unclear
14
- - When suggesting code changes, explain the rationale`,this.maxHistoryMessages=t.maxHistoryMessages??20,this.onUsage=t.onUsage}async send(e){let n;if(e.conversationId){let t=await this.store.get(e.conversationId);if(!t)throw Error(`Conversation ${e.conversationId} not found`);n=t}else n=await this.store.create({status:`active`,provider:this.provider.name,model:this.provider.model,messages:[],workspacePath:this.context?.workspacePath});await this.store.appendMessage(n.id,{role:`user`,content:e.content,status:`completed`,attachments:e.attachments});let r=this.buildPrompt(n,e),i=this.provider.getModel();try{let e=await t({model:i,prompt:r,system:this.systemPrompt}),a=await this.store.appendMessage(n.id,{role:`assistant`,content:e.text,status:`completed`}),o=await this.store.get(n.id);if(!o)throw Error(`Conversation lost after update`);return{message:a,conversation:o}}catch(e){throw await this.store.appendMessage(n.id,{role:`assistant`,content:``,status:`error`,error:{code:`generation_failed`,message:e instanceof Error?e.message:String(e)}}),e}}async stream(e){let t;if(e.conversationId){let n=await this.store.get(e.conversationId);if(!n)throw Error(`Conversation ${e.conversationId} not found`);t=n}else t=await this.store.create({status:`active`,provider:this.provider.name,model:this.provider.model,messages:[],workspacePath:this.context?.workspacePath});await this.store.appendMessage(t.id,{role:`user`,content:e.content,status:`completed`,attachments:e.attachments});let r=await this.store.appendMessage(t.id,{role:`assistant`,content:``,status:`streaming`}),i=this.buildPrompt(t,e),a=this.provider.getModel(),o=this;async function*s(){let e=``;try{let s=n({model:a,prompt:i,system:o.systemPrompt});for await(let t of s.textStream)e+=t,yield{type:`text`,content:t};await o.store.updateMessage(t.id,r.id,{content:e,status:`completed`}),yield{type:`done`}}catch(n){await o.store.updateMessage(t.id,r.id,{content:e,status:`error`,error:{code:`stream_failed`,message:n instanceof Error?n.message:String(n)}}),yield{type:`error`,error:{code:`stream_failed`,message:n instanceof Error?n.message:String(n)}}}}return{conversationId:t.id,messageId:r.id,stream:s()}}async getConversation(e){return this.store.get(e)}async listConversations(e){return this.store.list({status:`active`,...e})}async deleteConversation(e){return this.store.delete(e)}buildPrompt(e,t){let n=``,r=Math.max(0,e.messages.length-this.maxHistoryMessages);for(let t=r;t<e.messages.length;t++){let r=e.messages[t];r&&(r.role===`user`||r.role===`assistant`)&&(n+=`${r.role===`user`?`User`:`Assistant`}: ${r.content}\n\n`)}let i=t.content;if(t.attachments?.length){let e=t.attachments.map(e=>e.type===`file`||e.type===`code`?`\n\n### ${e.name}\n\`\`\`\n${e.content}\n\`\`\``:`\n\n[Attachment: ${e.name}]`).join(``);i+=e}return n+=`User: ${i}\n\nAssistant:`,n}};function i(e){return new r(e)}export{r as ChatService,i as createChatService};
24
+ - When suggesting code changes, explain the rationale`;
25
+ /**
26
+ * Main chat service for AI-powered conversations
27
+ */
28
+ var ChatService = class {
29
+ provider;
30
+ context;
31
+ store;
32
+ systemPrompt;
33
+ maxHistoryMessages;
34
+ onUsage;
35
+ constructor(config) {
36
+ this.provider = config.provider;
37
+ this.context = config.context;
38
+ this.store = config.store ?? new InMemoryConversationStore();
39
+ this.systemPrompt = config.systemPrompt ?? DEFAULT_SYSTEM_PROMPT;
40
+ this.maxHistoryMessages = config.maxHistoryMessages ?? 20;
41
+ this.onUsage = config.onUsage;
42
+ }
43
+ /**
44
+ * Send a message and get a complete response
45
+ */
46
+ async send(options) {
47
+ let conversation;
48
+ if (options.conversationId) {
49
+ const existing = await this.store.get(options.conversationId);
50
+ if (!existing) throw new Error(`Conversation ${options.conversationId} not found`);
51
+ conversation = existing;
52
+ } else conversation = await this.store.create({
53
+ status: "active",
54
+ provider: this.provider.name,
55
+ model: this.provider.model,
56
+ messages: [],
57
+ workspacePath: this.context?.workspacePath
58
+ });
59
+ await this.store.appendMessage(conversation.id, {
60
+ role: "user",
61
+ content: options.content,
62
+ status: "completed",
63
+ attachments: options.attachments
64
+ });
65
+ const prompt = this.buildPrompt(conversation, options);
66
+ const model = this.provider.getModel();
67
+ try {
68
+ const result = await generateText({
69
+ model,
70
+ prompt,
71
+ system: this.systemPrompt
72
+ });
73
+ const assistantMessage = await this.store.appendMessage(conversation.id, {
74
+ role: "assistant",
75
+ content: result.text,
76
+ status: "completed"
77
+ });
78
+ const updatedConversation = await this.store.get(conversation.id);
79
+ if (!updatedConversation) throw new Error("Conversation lost after update");
80
+ return {
81
+ message: assistantMessage,
82
+ conversation: updatedConversation
83
+ };
84
+ } catch (error) {
85
+ await this.store.appendMessage(conversation.id, {
86
+ role: "assistant",
87
+ content: "",
88
+ status: "error",
89
+ error: {
90
+ code: "generation_failed",
91
+ message: error instanceof Error ? error.message : String(error)
92
+ }
93
+ });
94
+ throw error;
95
+ }
96
+ }
97
+ /**
98
+ * Send a message and get a streaming response
99
+ */
100
+ async stream(options) {
101
+ let conversation;
102
+ if (options.conversationId) {
103
+ const existing = await this.store.get(options.conversationId);
104
+ if (!existing) throw new Error(`Conversation ${options.conversationId} not found`);
105
+ conversation = existing;
106
+ } else conversation = await this.store.create({
107
+ status: "active",
108
+ provider: this.provider.name,
109
+ model: this.provider.model,
110
+ messages: [],
111
+ workspacePath: this.context?.workspacePath
112
+ });
113
+ await this.store.appendMessage(conversation.id, {
114
+ role: "user",
115
+ content: options.content,
116
+ status: "completed",
117
+ attachments: options.attachments
118
+ });
119
+ const assistantMessage = await this.store.appendMessage(conversation.id, {
120
+ role: "assistant",
121
+ content: "",
122
+ status: "streaming"
123
+ });
124
+ const prompt = this.buildPrompt(conversation, options);
125
+ const model = this.provider.getModel();
126
+ const self = {
127
+ systemPrompt: this.systemPrompt,
128
+ store: this.store
129
+ };
130
+ async function* streamGenerator() {
131
+ let fullContent = "";
132
+ try {
133
+ const result = streamText({
134
+ model,
135
+ prompt,
136
+ system: self.systemPrompt
137
+ });
138
+ for await (const chunk of result.textStream) {
139
+ fullContent += chunk;
140
+ yield {
141
+ type: "text",
142
+ content: chunk
143
+ };
144
+ }
145
+ await self.store.updateMessage(conversation.id, assistantMessage.id, {
146
+ content: fullContent,
147
+ status: "completed"
148
+ });
149
+ yield { type: "done" };
150
+ } catch (error) {
151
+ await self.store.updateMessage(conversation.id, assistantMessage.id, {
152
+ content: fullContent,
153
+ status: "error",
154
+ error: {
155
+ code: "stream_failed",
156
+ message: error instanceof Error ? error.message : String(error)
157
+ }
158
+ });
159
+ yield {
160
+ type: "error",
161
+ error: {
162
+ code: "stream_failed",
163
+ message: error instanceof Error ? error.message : String(error)
164
+ }
165
+ };
166
+ }
167
+ }
168
+ return {
169
+ conversationId: conversation.id,
170
+ messageId: assistantMessage.id,
171
+ stream: streamGenerator()
172
+ };
173
+ }
174
+ /**
175
+ * Get a conversation by ID
176
+ */
177
+ async getConversation(conversationId) {
178
+ return this.store.get(conversationId);
179
+ }
180
+ /**
181
+ * List conversations
182
+ */
183
+ async listConversations(options) {
184
+ return this.store.list({
185
+ status: "active",
186
+ ...options
187
+ });
188
+ }
189
+ /**
190
+ * Delete a conversation
191
+ */
192
+ async deleteConversation(conversationId) {
193
+ return this.store.delete(conversationId);
194
+ }
195
+ /**
196
+ * Build prompt string for LLM
197
+ */
198
+ buildPrompt(conversation, options) {
199
+ let prompt = "";
200
+ const historyStart = Math.max(0, conversation.messages.length - this.maxHistoryMessages);
201
+ for (let i = historyStart; i < conversation.messages.length; i++) {
202
+ const msg = conversation.messages[i];
203
+ if (!msg) continue;
204
+ if (msg.role === "user" || msg.role === "assistant") prompt += `${msg.role === "user" ? "User" : "Assistant"}: ${msg.content}\n\n`;
205
+ }
206
+ let content = options.content;
207
+ if (options.attachments?.length) {
208
+ const attachmentInfo = options.attachments.map((a) => {
209
+ if (a.type === "file" || a.type === "code") return `\n\n### ${a.name}\n\`\`\`\n${a.content}\n\`\`\``;
210
+ return `\n\n[Attachment: ${a.name}]`;
211
+ }).join("");
212
+ content += attachmentInfo;
213
+ }
214
+ prompt += `User: ${content}\n\nAssistant:`;
215
+ return prompt;
216
+ }
217
+ };
218
+ /**
219
+ * Create a chat service with the given configuration
220
+ */
221
+ function createChatService(config) {
222
+ return new ChatService(config);
223
+ }
224
+
225
+ //#endregion
226
+ export { ChatService, createChatService };
227
+ //# sourceMappingURL=chat-service.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"chat-service.js","names":["conversation: ChatConversation"],"sources":["../../src/core/chat-service.ts"],"sourcesContent":["/**\n * Main chat orchestration service\n */\nimport { generateText, streamText } from 'ai';\nimport type { Provider as ChatProvider } from '@lssm/lib.ai-providers';\nimport type { WorkspaceContext } from '../context/workspace-context';\nimport type { ConversationStore } from './conversation-store';\nimport { InMemoryConversationStore } from './conversation-store';\nimport type {\n ChatConversation,\n ChatStreamChunk,\n SendMessageOptions,\n SendMessageResult,\n StreamMessageResult,\n} from './message-types';\n\n/**\n * Configuration for ChatService\n */\nexport interface ChatServiceConfig {\n /** LLM provider to use */\n provider: ChatProvider;\n /** Optional workspace context for code-aware chat */\n context?: WorkspaceContext;\n /** Optional conversation store (defaults to in-memory) */\n store?: ConversationStore;\n /** Default system prompt */\n systemPrompt?: string;\n /** Maximum conversation history to include */\n maxHistoryMessages?: number;\n /** Callback for usage tracking */\n onUsage?: (usage: { inputTokens: number; outputTokens: number }) => void;\n}\n\n/**\n * Default system prompt for ContractSpec vibe coding\n */\nconst DEFAULT_SYSTEM_PROMPT = `You are ContractSpec AI, an expert coding assistant specialized in ContractSpec development.\n\nYour capabilities:\n- Help users create, modify, and understand ContractSpec specifications\n- Generate code that follows ContractSpec patterns and best practices\n- Explain concepts from the ContractSpec documentation\n- Suggest improvements and identify issues in specs and implementations\n\nGuidelines:\n- Be concise but thorough\n- Provide code examples when helpful\n- Reference relevant ContractSpec concepts and patterns\n- Ask clarifying questions when the user's intent is unclear\n- When suggesting code changes, explain the rationale`;\n\n/**\n * Main chat service for AI-powered conversations\n */\nexport class ChatService {\n private readonly provider: ChatProvider;\n private readonly context?: WorkspaceContext;\n private readonly store: ConversationStore;\n private readonly systemPrompt: string;\n private readonly maxHistoryMessages: number;\n private readonly onUsage?: (usage: {\n inputTokens: number;\n outputTokens: number;\n }) => void;\n\n constructor(config: ChatServiceConfig) {\n this.provider = config.provider;\n this.context = config.context;\n this.store = config.store ?? new InMemoryConversationStore();\n this.systemPrompt = config.systemPrompt ?? DEFAULT_SYSTEM_PROMPT;\n this.maxHistoryMessages = config.maxHistoryMessages ?? 20;\n this.onUsage = config.onUsage;\n }\n\n /**\n * Send a message and get a complete response\n */\n async send(options: SendMessageOptions): Promise<SendMessageResult> {\n // Get or create conversation\n let conversation: ChatConversation;\n if (options.conversationId) {\n const existing = await this.store.get(options.conversationId);\n if (!existing) {\n throw new Error(`Conversation ${options.conversationId} not found`);\n }\n conversation = existing;\n } else {\n conversation = await this.store.create({\n status: 'active',\n provider: this.provider.name,\n model: this.provider.model,\n messages: [],\n workspacePath: this.context?.workspacePath,\n });\n }\n\n // Add user message\n await this.store.appendMessage(conversation.id, {\n role: 'user',\n content: options.content,\n status: 'completed',\n attachments: options.attachments,\n });\n\n // Build prompt from messages\n const prompt = this.buildPrompt(conversation, options);\n\n // Get the language model\n const model = this.provider.getModel();\n\n try {\n // Generate response\n const result = await generateText({\n model,\n prompt,\n system: this.systemPrompt,\n });\n\n // Save assistant message\n const assistantMessage = await this.store.appendMessage(conversation.id, {\n role: 'assistant',\n content: result.text,\n status: 'completed',\n });\n\n // Refresh conversation\n const updatedConversation = await this.store.get(conversation.id);\n if (!updatedConversation) {\n throw new Error('Conversation lost after update');\n }\n\n return {\n message: assistantMessage,\n conversation: updatedConversation,\n };\n } catch (error) {\n // Save error message\n await this.store.appendMessage(conversation.id, {\n role: 'assistant',\n content: '',\n status: 'error',\n error: {\n code: 'generation_failed',\n message: error instanceof Error ? error.message : String(error),\n },\n });\n\n throw error;\n }\n }\n\n /**\n * Send a message and get a streaming response\n */\n async stream(options: SendMessageOptions): Promise<StreamMessageResult> {\n // Get or create conversation\n let conversation: ChatConversation;\n if (options.conversationId) {\n const existing = await this.store.get(options.conversationId);\n if (!existing) {\n throw new Error(`Conversation ${options.conversationId} not found`);\n }\n conversation = existing;\n } else {\n conversation = await this.store.create({\n status: 'active',\n provider: this.provider.name,\n model: this.provider.model,\n messages: [],\n workspacePath: this.context?.workspacePath,\n });\n }\n\n // Add user message\n await this.store.appendMessage(conversation.id, {\n role: 'user',\n content: options.content,\n status: 'completed',\n attachments: options.attachments,\n });\n\n // Create placeholder for assistant message\n const assistantMessage = await this.store.appendMessage(conversation.id, {\n role: 'assistant',\n content: '',\n status: 'streaming',\n });\n\n // Build prompt\n const prompt = this.buildPrompt(conversation, options);\n\n // Get the language model\n const model = this.provider.getModel();\n\n // Create async generator for streaming\n const self = {\n systemPrompt: this.systemPrompt,\n store: this.store,\n };\n async function* streamGenerator(): AsyncIterable<ChatStreamChunk> {\n let fullContent = '';\n\n try {\n const result = streamText({\n model,\n prompt,\n system: self.systemPrompt,\n });\n\n for await (const chunk of result.textStream) {\n fullContent += chunk;\n yield { type: 'text', content: chunk };\n }\n\n // Update message with final content\n await self.store.updateMessage(conversation.id, assistantMessage.id, {\n content: fullContent,\n status: 'completed',\n });\n\n yield {\n type: 'done',\n };\n } catch (error) {\n await self.store.updateMessage(conversation.id, assistantMessage.id, {\n content: fullContent,\n status: 'error',\n error: {\n code: 'stream_failed',\n message: error instanceof Error ? error.message : String(error),\n },\n });\n\n yield {\n type: 'error',\n error: {\n code: 'stream_failed',\n message: error instanceof Error ? error.message : String(error),\n },\n };\n }\n }\n\n return {\n conversationId: conversation.id,\n messageId: assistantMessage.id,\n stream: streamGenerator(),\n };\n }\n\n /**\n * Get a conversation by ID\n */\n async getConversation(\n conversationId: string\n ): Promise<ChatConversation | null> {\n return this.store.get(conversationId);\n }\n\n /**\n * List conversations\n */\n async listConversations(options?: {\n limit?: number;\n offset?: number;\n }): Promise<ChatConversation[]> {\n return this.store.list({\n status: 'active',\n ...options,\n });\n }\n\n /**\n * Delete a conversation\n */\n async deleteConversation(conversationId: string): Promise<boolean> {\n return this.store.delete(conversationId);\n }\n\n /**\n * Build prompt string for LLM\n */\n private buildPrompt(\n conversation: ChatConversation,\n options: SendMessageOptions\n ): string {\n let prompt = '';\n\n // Add conversation history (limited)\n const historyStart = Math.max(\n 0,\n conversation.messages.length - this.maxHistoryMessages\n );\n for (let i = historyStart; i < conversation.messages.length; i++) {\n const msg = conversation.messages[i];\n if (!msg) continue;\n if (msg.role === 'user' || msg.role === 'assistant') {\n prompt += `${msg.role === 'user' ? 'User' : 'Assistant'}: ${msg.content}\\n\\n`;\n }\n }\n\n // Add current message with attachments\n let content = options.content;\n if (options.attachments?.length) {\n const attachmentInfo = options.attachments\n .map((a) => {\n if (a.type === 'file' || a.type === 'code') {\n return `\\n\\n### ${a.name}\\n\\`\\`\\`\\n${a.content}\\n\\`\\`\\``;\n }\n return `\\n\\n[Attachment: ${a.name}]`;\n })\n .join('');\n content += attachmentInfo;\n }\n\n prompt += `User: ${content}\\n\\nAssistant:`;\n\n return prompt;\n }\n}\n\n/**\n * Create a chat service with the given configuration\n */\nexport function createChatService(config: ChatServiceConfig): ChatService {\n return new ChatService(config);\n}\n"],"mappings":"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"}
@@ -0,0 +1,74 @@
1
+ import { ChatConversation, ChatMessage, ConversationStatus } from "./message-types.js";
2
+
3
+ //#region src/core/conversation-store.d.ts
4
+
5
+ /**
6
+ * Interface for conversation persistence
7
+ */
8
+ interface ConversationStore {
9
+ /**
10
+ * Get a conversation by ID
11
+ */
12
+ get(conversationId: string): Promise<ChatConversation | null>;
13
+ /**
14
+ * Create a new conversation
15
+ */
16
+ create(conversation: Omit<ChatConversation, 'id' | 'createdAt' | 'updatedAt'>): Promise<ChatConversation>;
17
+ /**
18
+ * Update conversation properties
19
+ */
20
+ update(conversationId: string, updates: Partial<Pick<ChatConversation, 'title' | 'status' | 'summary' | 'metadata'>>): Promise<ChatConversation | null>;
21
+ /**
22
+ * Append a message to a conversation
23
+ */
24
+ appendMessage(conversationId: string, message: Omit<ChatMessage, 'id' | 'conversationId' | 'createdAt' | 'updatedAt'>): Promise<ChatMessage>;
25
+ /**
26
+ * Update a message in a conversation
27
+ */
28
+ updateMessage(conversationId: string, messageId: string, updates: Partial<ChatMessage>): Promise<ChatMessage | null>;
29
+ /**
30
+ * Delete a conversation
31
+ */
32
+ delete(conversationId: string): Promise<boolean>;
33
+ /**
34
+ * List conversations with optional filters
35
+ */
36
+ list(options?: {
37
+ status?: ConversationStatus;
38
+ limit?: number;
39
+ offset?: number;
40
+ }): Promise<ChatConversation[]>;
41
+ /**
42
+ * Search conversations by content
43
+ */
44
+ search(query: string, limit?: number): Promise<ChatConversation[]>;
45
+ }
46
+ /**
47
+ * In-memory conversation store for development and testing
48
+ */
49
+ declare class InMemoryConversationStore implements ConversationStore {
50
+ private readonly conversations;
51
+ get(conversationId: string): Promise<ChatConversation | null>;
52
+ create(conversation: Omit<ChatConversation, 'id' | 'createdAt' | 'updatedAt'>): Promise<ChatConversation>;
53
+ update(conversationId: string, updates: Partial<Pick<ChatConversation, 'title' | 'status' | 'summary' | 'metadata'>>): Promise<ChatConversation | null>;
54
+ appendMessage(conversationId: string, message: Omit<ChatMessage, 'id' | 'conversationId' | 'createdAt' | 'updatedAt'>): Promise<ChatMessage>;
55
+ updateMessage(conversationId: string, messageId: string, updates: Partial<ChatMessage>): Promise<ChatMessage | null>;
56
+ delete(conversationId: string): Promise<boolean>;
57
+ list(options?: {
58
+ status?: ConversationStatus;
59
+ limit?: number;
60
+ offset?: number;
61
+ }): Promise<ChatConversation[]>;
62
+ search(query: string, limit?: number): Promise<ChatConversation[]>;
63
+ /**
64
+ * Clear all conversations (for testing)
65
+ */
66
+ clear(): void;
67
+ }
68
+ /**
69
+ * Create an in-memory conversation store
70
+ */
71
+ declare function createInMemoryConversationStore(): ConversationStore;
72
+ //#endregion
73
+ export { ConversationStore, InMemoryConversationStore, createInMemoryConversationStore };
74
+ //# sourceMappingURL=conversation-store.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"conversation-store.d.ts","names":[],"sources":["../../src/core/conversation-store.ts"],"sourcesContent":[],"mappings":";;;;;;;AAsBkB,UAVD,iBAAA,CAUC;EACL;;;EAQP,GAAA,CAAA,cAAA,EAAA,MAAA,CAAA,EAfyB,OAezB,CAfiC,gBAejC,GAAA,IAAA,CAAA;EADO;;;EAWP,MAAA,CAAA,YAAA,EAnBY,IAmBZ,CAnBiB,gBAmBjB,EAAA,IAAA,GAAA,WAAA,GAAA,WAAA,CAAA,CAAA,EAlBD,OAkBC,CAlBO,gBAkBP,CAAA;EADO;;;EAYQ,MAAA,CAAA,cAAA,EAAA,MAAA,EAAA,OAAA,EAtBR,OAsBQ,CArBf,IAqBe,CArBV,gBAqBU,EAAA,OAAA,GAAA,QAAA,GAAA,SAAA,GAAA,UAAA,CAAA,CAAA,CAAA,EAnBhB,OAmBgB,CAnBR,gBAmBQ,GAAA,IAAA,CAAA;EAAR;;;EAMqB,aAAA,CAAA,cAAA,EAAA,MAAA,EAAA,OAAA,EAlBrB,IAkBqB,CAjB5B,WAiB4B,EAAA,IAAA,GAAA,gBAAA,GAAA,WAAA,GAAA,WAAA,CAAA,CAAA,EAd7B,OAc6B,CAdrB,WAcqB,CAAA;EAMrB;;;EAQoC,aAAA,CAAA,cAAA,EAAA,MAAA,EAAA,SAAA,EAAA,MAAA,EAAA,OAAA,EApBpC,OAoBoC,CApB5B,WAoB4B,CAAA,CAAA,EAnB5C,OAmB4C,CAnBpC,WAmBoC,GAAA,IAAA,CAAA;EAAR;;AAazC;EAG6C,MAAA,CAAA,cAAA,EAAA,MAAA,CAAA,EA9BX,OA8BW,CAAA,OAAA,CAAA;EAAR;;;EAMxB,IAAA,CAAA,OAcA,CAdA,EAAA;IAAR,MAAA,CAAA,EA9BQ,kBA8BR;IAeM,KAAA,CAAA,EAAA,MAAA;IAAL,MAAA,CAAA,EAAA,MAAA;EADO,CAAA,CAAA,EAzCP,OAyCO,CAzCC,gBAyCD,EAAA,CAAA;EAGA;;;EAeA,MAAA,CAAA,KAAA,EAAA,MAAA,EAAA,KAAA,CAAA,EAAA,MAAA,CAAA,EAtD4B,OAsD5B,CAtDoC,gBAsDpC,EAAA,CAAA;;;;;AA4BA,cArEA,yBAAA,YAAqC,iBAqErC,CAAA;EAAR,iBAAA,aAAA;EAsBmC,GAAA,CAAA,cAAA,EAAA,MAAA,CAAA,EAxFH,OAwFG,CAxFK,gBAwFL,GAAA,IAAA,CAAA;EAK3B,MAAA,CAAA,YAAA,EAxFK,IAwFL,CAxFU,gBAwFV,EAAA,IAAA,GAAA,WAAA,GAAA,WAAA,CAAA,CAAA,EAvFR,OAuFQ,CAvFA,gBAuFA,CAAA;EAGC,MAAA,CAAA,cAAA,EAAA,MAAA,EAAA,OAAA,EA5ED,OA4EC,CA3ER,IA2EQ,CA3EH,gBA2EG,EAAA,OAAA,GAAA,QAAA,GAAA,SAAA,GAAA,UAAA,CAAA,CAAA,CAAA,EAzET,OAyES,CAzED,gBAyEC,GAAA,IAAA,CAAA;EAAR,aAAA,CAAA,cAAA,EAAA,MAAA,EAAA,OAAA,EA1DO,IA0DP,CAzDA,WAyDA,EAAA,IAAA,GAAA,gBAAA,GAAA,WAAA,GAAA,WAAA,CAAA,CAAA,EAtDD,OAsDC,CAtDO,WAsDP,CAAA;EAe6C,aAAA,CAAA,cAAA,EAAA,MAAA,EAAA,SAAA,EAAA,MAAA,EAAA,OAAA,EA9CtC,OA8CsC,CA9C9B,WA8C8B,CAAA,CAAA,EA7C9C,OA6C8C,CA7CtC,WA6CsC,GAAA,IAAA,CAAA;EAAR,MAAA,CAAA,cAAA,EAAA,MAAA,CAAA,EAvBH,OAuBG,CAAA,OAAA,CAAA;EAlHO,IAAA,CAAA,QAAA,EAAA;IAAiB,MAAA,CAAA,EAgGtD,kBAhGsD;IAsJnD,KAAA,CAAA,EAAA,MAAA;;MAnDV,QAAQ;yCAe6B,QAAQ;;;;;;;;;iBAoCnC,+BAAA,CAAA,GAAmC"}
@@ -1 +1,109 @@
1
- function e(e){return`${e}_${Date.now()}_${Math.random().toString(36).slice(2,11)}`}var t=class{conversations=new Map;async get(e){return this.conversations.get(e)??null}async create(t){let n=new Date,r={...t,id:e(`conv`),createdAt:n,updatedAt:n};return this.conversations.set(r.id,r),r}async update(e,t){let n=this.conversations.get(e);if(!n)return null;let r={...n,...t,updatedAt:new Date};return this.conversations.set(e,r),r}async appendMessage(t,n){let r=this.conversations.get(t);if(!r)throw Error(`Conversation ${t} not found`);let i=new Date,a={...n,id:e(`msg`),conversationId:t,createdAt:i,updatedAt:i};return r.messages.push(a),r.updatedAt=i,a}async updateMessage(e,t,n){let r=this.conversations.get(e);if(!r)return null;let i=r.messages.findIndex(e=>e.id===t);if(i===-1)return null;let a=r.messages[i];if(!a)return null;let o={...a,...n,updatedAt:new Date};return r.messages[i]=o,r.updatedAt=new Date,o}async delete(e){return this.conversations.delete(e)}async list(e){let t=Array.from(this.conversations.values());e?.status&&(t=t.filter(t=>t.status===e.status)),t.sort((e,t)=>t.updatedAt.getTime()-e.updatedAt.getTime());let n=e?.offset??0,r=e?.limit??100;return t.slice(n,n+r)}async search(e,t=20){let n=e.toLowerCase(),r=[];for(let e of this.conversations.values()){if(e.title?.toLowerCase().includes(n)){r.push(e);continue}if(e.messages.some(e=>e.content.toLowerCase().includes(n))&&r.push(e),r.length>=t)break}return r}clear(){this.conversations.clear()}};function n(){return new t}export{t as InMemoryConversationStore,n as createInMemoryConversationStore};
1
+ //#region src/core/conversation-store.ts
2
+ /**
3
+ * Generate a unique ID
4
+ */
5
+ function generateId(prefix) {
6
+ return `${prefix}_${Date.now()}_${Math.random().toString(36).slice(2, 11)}`;
7
+ }
8
+ /**
9
+ * In-memory conversation store for development and testing
10
+ */
11
+ var InMemoryConversationStore = class {
12
+ conversations = /* @__PURE__ */ new Map();
13
+ async get(conversationId) {
14
+ return this.conversations.get(conversationId) ?? null;
15
+ }
16
+ async create(conversation) {
17
+ const now = /* @__PURE__ */ new Date();
18
+ const fullConversation = {
19
+ ...conversation,
20
+ id: generateId("conv"),
21
+ createdAt: now,
22
+ updatedAt: now
23
+ };
24
+ this.conversations.set(fullConversation.id, fullConversation);
25
+ return fullConversation;
26
+ }
27
+ async update(conversationId, updates) {
28
+ const conversation = this.conversations.get(conversationId);
29
+ if (!conversation) return null;
30
+ const updated = {
31
+ ...conversation,
32
+ ...updates,
33
+ updatedAt: /* @__PURE__ */ new Date()
34
+ };
35
+ this.conversations.set(conversationId, updated);
36
+ return updated;
37
+ }
38
+ async appendMessage(conversationId, message) {
39
+ const conversation = this.conversations.get(conversationId);
40
+ if (!conversation) throw new Error(`Conversation ${conversationId} not found`);
41
+ const now = /* @__PURE__ */ new Date();
42
+ const fullMessage = {
43
+ ...message,
44
+ id: generateId("msg"),
45
+ conversationId,
46
+ createdAt: now,
47
+ updatedAt: now
48
+ };
49
+ conversation.messages.push(fullMessage);
50
+ conversation.updatedAt = now;
51
+ return fullMessage;
52
+ }
53
+ async updateMessage(conversationId, messageId, updates) {
54
+ const conversation = this.conversations.get(conversationId);
55
+ if (!conversation) return null;
56
+ const messageIndex = conversation.messages.findIndex((m) => m.id === messageId);
57
+ if (messageIndex === -1) return null;
58
+ const message = conversation.messages[messageIndex];
59
+ if (!message) return null;
60
+ const updated = {
61
+ ...message,
62
+ ...updates,
63
+ updatedAt: /* @__PURE__ */ new Date()
64
+ };
65
+ conversation.messages[messageIndex] = updated;
66
+ conversation.updatedAt = /* @__PURE__ */ new Date();
67
+ return updated;
68
+ }
69
+ async delete(conversationId) {
70
+ return this.conversations.delete(conversationId);
71
+ }
72
+ async list(options) {
73
+ let results = Array.from(this.conversations.values());
74
+ if (options?.status) results = results.filter((c) => c.status === options.status);
75
+ results.sort((a, b) => b.updatedAt.getTime() - a.updatedAt.getTime());
76
+ const offset = options?.offset ?? 0;
77
+ const limit = options?.limit ?? 100;
78
+ return results.slice(offset, offset + limit);
79
+ }
80
+ async search(query, limit = 20) {
81
+ const lowerQuery = query.toLowerCase();
82
+ const results = [];
83
+ for (const conversation of this.conversations.values()) {
84
+ if (conversation.title?.toLowerCase().includes(lowerQuery)) {
85
+ results.push(conversation);
86
+ continue;
87
+ }
88
+ if (conversation.messages.some((m) => m.content.toLowerCase().includes(lowerQuery))) results.push(conversation);
89
+ if (results.length >= limit) break;
90
+ }
91
+ return results;
92
+ }
93
+ /**
94
+ * Clear all conversations (for testing)
95
+ */
96
+ clear() {
97
+ this.conversations.clear();
98
+ }
99
+ };
100
+ /**
101
+ * Create an in-memory conversation store
102
+ */
103
+ function createInMemoryConversationStore() {
104
+ return new InMemoryConversationStore();
105
+ }
106
+
107
+ //#endregion
108
+ export { InMemoryConversationStore, createInMemoryConversationStore };
109
+ //# sourceMappingURL=conversation-store.js.map