@gendive/chatllm 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.mjs ADDED
@@ -0,0 +1,2058 @@
1
+ // src/types.ts
2
+ var DEFAULT_PERSONALIZATION = {
3
+ responseStyle: {
4
+ warmth: "medium",
5
+ enthusiasm: "medium",
6
+ emojiUsage: "low",
7
+ formatting: "default",
8
+ verbosity: "balanced"
9
+ },
10
+ userProfile: {},
11
+ useMemory: true,
12
+ language: "auto"
13
+ };
14
+
15
+ // src/providers/base.ts
16
+ var BaseProvider = class {
17
+ constructor(config) {
18
+ this.config = config;
19
+ }
20
+ /**
21
+ * Generate a unique message ID
22
+ */
23
+ generateMessageId() {
24
+ return `msg_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
25
+ }
26
+ };
27
+ var ProviderRegistry = class {
28
+ providers = /* @__PURE__ */ new Map();
29
+ register(provider) {
30
+ this.providers.set(provider.name, provider);
31
+ }
32
+ get(name) {
33
+ return this.providers.get(name);
34
+ }
35
+ has(name) {
36
+ return this.providers.has(name);
37
+ }
38
+ getAll() {
39
+ return this.providers;
40
+ }
41
+ };
42
+
43
+ // src/config.ts
44
+ import { join } from "path";
45
+ import { homedir } from "os";
46
+ var DEFAULT_STORAGE_PATH = join(homedir(), ".devdive-chat");
47
+ var DEFAULT_OLLAMA_BASE_URL = "http://localhost:11434";
48
+ var DEFAULT_MODELS = {
49
+ openai: "gpt-4",
50
+ anthropic: "claude-3-opus-20240229",
51
+ google: "gemini-pro",
52
+ naver: "HCX-003",
53
+ ollama: "llama2"
54
+ };
55
+ function validateProviderConfig(provider, config) {
56
+ const providerConfig = config.providers[provider];
57
+ if (!providerConfig) {
58
+ throw new Error(`Provider "${provider}" is not configured`);
59
+ }
60
+ switch (provider) {
61
+ case "openai":
62
+ if (!("apiKey" in providerConfig) || !providerConfig.apiKey) {
63
+ throw new Error("OpenAI provider requires apiKey");
64
+ }
65
+ break;
66
+ case "anthropic":
67
+ if (!("apiKey" in providerConfig) || !providerConfig.apiKey) {
68
+ throw new Error("Anthropic provider requires apiKey");
69
+ }
70
+ break;
71
+ case "google":
72
+ if (!("apiKey" in providerConfig) || !providerConfig.apiKey) {
73
+ throw new Error("Google provider requires apiKey");
74
+ }
75
+ break;
76
+ case "naver":
77
+ if (!("apiKey" in providerConfig) || !providerConfig.apiKey || !("apiGatewayKey" in providerConfig) || !providerConfig.apiGatewayKey) {
78
+ throw new Error("Naver provider requires apiKey and apiGatewayKey");
79
+ }
80
+ break;
81
+ case "ollama":
82
+ break;
83
+ }
84
+ }
85
+ function getProviderBaseUrl(provider, config) {
86
+ const providerConfig = config.providers[provider];
87
+ switch (provider) {
88
+ case "openai":
89
+ return providerConfig?.baseUrl ?? "https://api.openai.com/v1";
90
+ case "anthropic":
91
+ return providerConfig?.baseUrl ?? "https://api.anthropic.com";
92
+ case "google":
93
+ return "https://generativelanguage.googleapis.com";
94
+ case "naver":
95
+ return providerConfig?.baseUrl ?? "https://clovastudio.apigw.ntruss.com";
96
+ case "ollama":
97
+ return providerConfig?.baseUrl ?? DEFAULT_OLLAMA_BASE_URL;
98
+ }
99
+ }
100
+ function getDefaultModel(provider, config) {
101
+ return config.defaultModel ?? DEFAULT_MODELS[provider];
102
+ }
103
+ function mergeConfig(userConfig) {
104
+ return {
105
+ ...userConfig,
106
+ storagePath: userConfig.storagePath ?? DEFAULT_STORAGE_PATH
107
+ };
108
+ }
109
+
110
+ // src/utils/errors.ts
111
+ var ChatLLMError = class extends Error {
112
+ constructor(message) {
113
+ super(message);
114
+ this.name = "ChatLLMError";
115
+ }
116
+ };
117
+ var ProviderError = class extends ChatLLMError {
118
+ constructor(provider, message, statusCode, originalError) {
119
+ super(`[${provider}] ${message}`);
120
+ this.provider = provider;
121
+ this.statusCode = statusCode;
122
+ this.originalError = originalError;
123
+ this.name = "ProviderError";
124
+ }
125
+ };
126
+ var ConfigurationError = class extends ChatLLMError {
127
+ constructor(message) {
128
+ super(message);
129
+ this.name = "ConfigurationError";
130
+ }
131
+ };
132
+ var ToolExecutionError = class extends ChatLLMError {
133
+ constructor(toolName, message, originalError) {
134
+ super(`Tool "${toolName}" execution failed: ${message}`);
135
+ this.toolName = toolName;
136
+ this.originalError = originalError;
137
+ this.name = "ToolExecutionError";
138
+ }
139
+ };
140
+ var StorageError = class extends ChatLLMError {
141
+ constructor(operation, message, originalError) {
142
+ super(`Storage ${operation} failed: ${message}`);
143
+ this.operation = operation;
144
+ this.originalError = originalError;
145
+ this.name = "StorageError";
146
+ }
147
+ };
148
+ var StreamError = class extends ChatLLMError {
149
+ constructor(message, originalError) {
150
+ super(message);
151
+ this.originalError = originalError;
152
+ this.name = "StreamError";
153
+ }
154
+ };
155
+ var ValidationError = class extends ChatLLMError {
156
+ constructor(field, message) {
157
+ super(`Validation error for "${field}": ${message}`);
158
+ this.field = field;
159
+ this.name = "ValidationError";
160
+ }
161
+ };
162
+ function wrapProviderError(provider, error, statusCode) {
163
+ if (error instanceof ProviderError) {
164
+ return error;
165
+ }
166
+ const message = error instanceof Error ? error.message : String(error);
167
+ const originalError = error instanceof Error ? error : void 0;
168
+ return new ProviderError(provider, message, statusCode, originalError);
169
+ }
170
+ function isProviderError(error) {
171
+ return error instanceof ProviderError;
172
+ }
173
+ function isToolExecutionError(error) {
174
+ return error instanceof ToolExecutionError;
175
+ }
176
+ function isStorageError(error) {
177
+ return error instanceof StorageError;
178
+ }
179
+
180
+ // src/utils/stream.ts
181
+ async function* parseSSEStream(response) {
182
+ const reader = response.body?.getReader();
183
+ if (!reader) {
184
+ throw new StreamError("Response body is not readable");
185
+ }
186
+ const decoder = new TextDecoder();
187
+ let buffer = "";
188
+ try {
189
+ while (true) {
190
+ const { done, value } = await reader.read();
191
+ if (done) break;
192
+ buffer += decoder.decode(value, { stream: true });
193
+ const lines = buffer.split("\n");
194
+ buffer = lines.pop() || "";
195
+ for (const line of lines) {
196
+ const trimmed = line.trim();
197
+ if (trimmed.startsWith("data: ")) {
198
+ const data = trimmed.slice(6);
199
+ if (data !== "[DONE]") {
200
+ yield data;
201
+ }
202
+ }
203
+ }
204
+ }
205
+ if (buffer.trim()) {
206
+ const trimmed = buffer.trim();
207
+ if (trimmed.startsWith("data: ")) {
208
+ const data = trimmed.slice(6);
209
+ if (data !== "[DONE]") {
210
+ yield data;
211
+ }
212
+ }
213
+ }
214
+ } finally {
215
+ reader.releaseLock();
216
+ }
217
+ }
218
+ async function* parseNDJSONStream(response) {
219
+ const reader = response.body?.getReader();
220
+ if (!reader) {
221
+ throw new StreamError("Response body is not readable");
222
+ }
223
+ const decoder = new TextDecoder();
224
+ let buffer = "";
225
+ try {
226
+ while (true) {
227
+ const { done, value } = await reader.read();
228
+ if (done) break;
229
+ buffer += decoder.decode(value, { stream: true });
230
+ const lines = buffer.split("\n");
231
+ buffer = lines.pop() || "";
232
+ for (const line of lines) {
233
+ const trimmed = line.trim();
234
+ if (trimmed) {
235
+ try {
236
+ yield JSON.parse(trimmed);
237
+ } catch {
238
+ }
239
+ }
240
+ }
241
+ }
242
+ if (buffer.trim()) {
243
+ try {
244
+ yield JSON.parse(buffer.trim());
245
+ } catch {
246
+ }
247
+ }
248
+ } finally {
249
+ reader.releaseLock();
250
+ }
251
+ }
252
+ function createStreamEvent(type, data) {
253
+ return {
254
+ type,
255
+ ...data
256
+ };
257
+ }
258
+ async function collectStreamContent(stream) {
259
+ let content = "";
260
+ for await (const event of stream) {
261
+ if (event.type === "text" && event.content) {
262
+ content += event.content;
263
+ }
264
+ }
265
+ return content;
266
+ }
267
+
268
+ // src/providers/ollama.ts
269
+ var OllamaProvider = class extends BaseProvider {
270
+ name = "ollama";
271
+ baseUrl;
272
+ constructor(config) {
273
+ super(config);
274
+ this.baseUrl = getProviderBaseUrl("ollama", config);
275
+ }
276
+ /**
277
+ * Send a message and get a complete response
278
+ */
279
+ async sendMessage(messages, options) {
280
+ const formattedMessages = this.formatMessages(messages);
281
+ const tools = options.tools ? this.formatTools(options.tools) : void 0;
282
+ const body = this.buildRequestBody(formattedMessages, options, tools);
283
+ try {
284
+ const response = await fetch(this.getEndpoint(options.model || "llama2"), {
285
+ method: "POST",
286
+ headers: this.getHeaders(),
287
+ body: JSON.stringify(body)
288
+ });
289
+ if (!response.ok) {
290
+ const errorText = await response.text();
291
+ throw wrapProviderError("ollama", errorText, response.status);
292
+ }
293
+ const data = await response.json();
294
+ return this.parseResponse(data);
295
+ } catch (error) {
296
+ throw wrapProviderError("ollama", error);
297
+ }
298
+ }
299
+ /**
300
+ * Send a message and stream the response
301
+ */
302
+ async *sendMessageStream(messages, options) {
303
+ const formattedMessages = this.formatMessages(messages);
304
+ const tools = options.tools ? this.formatTools(options.tools) : void 0;
305
+ const body = this.buildRequestBody(formattedMessages, options, tools);
306
+ body.stream = true;
307
+ try {
308
+ const response = await fetch(this.getEndpoint(options.model || "llama2"), {
309
+ method: "POST",
310
+ headers: this.getHeaders(),
311
+ body: JSON.stringify(body)
312
+ });
313
+ if (!response.ok) {
314
+ const errorText = await response.text();
315
+ yield createStreamEvent("error", {
316
+ error: wrapProviderError("ollama", errorText, response.status)
317
+ });
318
+ return;
319
+ }
320
+ let fullContent = "";
321
+ const toolCalls = [];
322
+ for await (const chunk of parseNDJSONStream(response)) {
323
+ const data = chunk;
324
+ if (data.message?.content) {
325
+ fullContent += data.message.content;
326
+ yield createStreamEvent("text", { content: data.message.content });
327
+ }
328
+ if (data.message?.tool_calls) {
329
+ for (const tc of data.message.tool_calls) {
330
+ const toolCall = {
331
+ id: `call_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`,
332
+ name: tc.function.name,
333
+ arguments: tc.function.arguments
334
+ };
335
+ toolCalls.push(toolCall);
336
+ yield createStreamEvent("tool_call", { toolCall });
337
+ }
338
+ }
339
+ if (data.done) {
340
+ const message = {
341
+ id: this.generateMessageId(),
342
+ role: "assistant",
343
+ content: fullContent,
344
+ provider: "ollama",
345
+ model: data.model,
346
+ toolCalls: toolCalls.length > 0 ? toolCalls : void 0,
347
+ timestamp: Date.now()
348
+ };
349
+ yield createStreamEvent("done", { message });
350
+ return;
351
+ }
352
+ }
353
+ } catch (error) {
354
+ yield createStreamEvent("error", {
355
+ error: wrapProviderError("ollama", error)
356
+ });
357
+ }
358
+ }
359
+ /**
360
+ * Convert internal message format to Ollama format
361
+ */
362
+ formatMessages(messages) {
363
+ return messages.map((msg) => {
364
+ const formatted = {
365
+ role: msg.role,
366
+ content: msg.content
367
+ };
368
+ if (msg.toolCalls && msg.toolCalls.length > 0) {
369
+ formatted.tool_calls = msg.toolCalls.map((tc) => ({
370
+ function: {
371
+ name: tc.name,
372
+ arguments: tc.arguments
373
+ }
374
+ }));
375
+ }
376
+ return formatted;
377
+ });
378
+ }
379
+ /**
380
+ * Convert tool definitions to Ollama format
381
+ */
382
+ formatTools(tools) {
383
+ return tools.map((tool) => ({
384
+ type: "function",
385
+ function: {
386
+ name: tool.name,
387
+ description: tool.description,
388
+ parameters: {
389
+ type: "object",
390
+ properties: tool.parameters.properties,
391
+ required: tool.parameters.required
392
+ }
393
+ }
394
+ }));
395
+ }
396
+ /**
397
+ * Parse Ollama response into internal message format
398
+ */
399
+ parseResponse(response) {
400
+ const toolCalls = [];
401
+ if (response.message.tool_calls) {
402
+ for (const tc of response.message.tool_calls) {
403
+ toolCalls.push({
404
+ id: `call_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`,
405
+ name: tc.function.name,
406
+ arguments: tc.function.arguments
407
+ });
408
+ }
409
+ }
410
+ return {
411
+ id: this.generateMessageId(),
412
+ role: "assistant",
413
+ content: response.message.content,
414
+ provider: "ollama",
415
+ model: response.model,
416
+ toolCalls: toolCalls.length > 0 ? toolCalls : void 0,
417
+ timestamp: Date.now()
418
+ };
419
+ }
420
+ /**
421
+ * Get the Ollama API endpoint
422
+ */
423
+ getEndpoint(_model) {
424
+ return `${this.baseUrl}/api/chat`;
425
+ }
426
+ /**
427
+ * Get headers for Ollama API requests
428
+ */
429
+ getHeaders() {
430
+ return {
431
+ "Content-Type": "application/json"
432
+ };
433
+ }
434
+ /**
435
+ * Build request body for Ollama API
436
+ */
437
+ buildRequestBody(messages, options, tools) {
438
+ const body = {
439
+ model: options.model || "llama2",
440
+ messages,
441
+ stream: false
442
+ };
443
+ if (options.temperature !== void 0 || options.maxTokens !== void 0) {
444
+ body.options = {};
445
+ if (options.temperature !== void 0) {
446
+ body.options.temperature = options.temperature;
447
+ }
448
+ if (options.maxTokens !== void 0) {
449
+ body.options.num_predict = options.maxTokens;
450
+ }
451
+ }
452
+ if (tools && tools.length > 0) {
453
+ body.tools = tools;
454
+ }
455
+ return body;
456
+ }
457
+ /**
458
+ * List available models from Ollama
459
+ */
460
+ async listModels() {
461
+ try {
462
+ const response = await fetch(`${this.baseUrl}/api/tags`, {
463
+ method: "GET",
464
+ headers: this.getHeaders()
465
+ });
466
+ if (!response.ok) {
467
+ throw wrapProviderError("ollama", "Failed to list models", response.status);
468
+ }
469
+ const data = await response.json();
470
+ return data.models.map((m) => m.name);
471
+ } catch (error) {
472
+ throw wrapProviderError("ollama", error);
473
+ }
474
+ }
475
+ /**
476
+ * Check if Ollama server is available
477
+ */
478
+ async isAvailable() {
479
+ try {
480
+ const response = await fetch(`${this.baseUrl}/api/tags`, {
481
+ method: "GET",
482
+ headers: this.getHeaders()
483
+ });
484
+ return response.ok;
485
+ } catch {
486
+ return false;
487
+ }
488
+ }
489
+ };
490
+
491
+ // src/core/message.ts
492
+ function generateMessageId() {
493
+ return `msg_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
494
+ }
495
+ function createMessage(role, content, options) {
496
+ return {
497
+ id: generateMessageId(),
498
+ role,
499
+ content,
500
+ provider: options?.provider,
501
+ model: options?.model,
502
+ toolCalls: options?.toolCalls,
503
+ toolCallId: options?.toolCallId,
504
+ timestamp: Date.now(),
505
+ metadata: options?.metadata
506
+ };
507
+ }
508
+ function createUserMessage(content) {
509
+ return createMessage("user", content);
510
+ }
511
+ function createAssistantMessage(content, provider, model, toolCalls) {
512
+ return createMessage("assistant", content, { provider, model, toolCalls });
513
+ }
514
+ function createSystemMessage(content) {
515
+ return createMessage("system", content);
516
+ }
517
+ function createToolMessage(content, toolCallId) {
518
+ return createMessage("tool", content, { toolCallId });
519
+ }
520
+ function cloneMessage(message, overrides) {
521
+ return {
522
+ ...message,
523
+ ...overrides,
524
+ id: overrides?.id ?? generateMessageId(),
525
+ timestamp: overrides?.timestamp ?? Date.now()
526
+ };
527
+ }
528
+ function filterByRole(messages, role) {
529
+ return messages.filter((m) => m.role === role);
530
+ }
531
+ function filterByProvider(messages, provider) {
532
+ return messages.filter((m) => m.provider === provider);
533
+ }
534
+ function getLastMessageByRole(messages, role) {
535
+ for (let i = messages.length - 1; i >= 0; i--) {
536
+ if (messages[i].role === role) {
537
+ return messages[i];
538
+ }
539
+ }
540
+ return void 0;
541
+ }
542
+ function getTotalContentLength(messages) {
543
+ return messages.reduce((sum, msg) => sum + msg.content.length, 0);
544
+ }
545
+ function truncateMessages(messages, maxTokens, preserveSystem = true) {
546
+ const maxChars = maxTokens * 4;
547
+ const result = [];
548
+ let currentLength = 0;
549
+ if (preserveSystem) {
550
+ for (const msg of messages) {
551
+ if (msg.role === "system") {
552
+ result.push(msg);
553
+ currentLength += msg.content.length;
554
+ }
555
+ }
556
+ }
557
+ const nonSystemMessages = messages.filter((m) => m.role !== "system");
558
+ for (let i = nonSystemMessages.length - 1; i >= 0; i--) {
559
+ const msg = nonSystemMessages[i];
560
+ if (currentLength + msg.content.length <= maxChars) {
561
+ result.unshift(msg);
562
+ currentLength += msg.content.length;
563
+ } else {
564
+ break;
565
+ }
566
+ }
567
+ return result.sort((a, b) => {
568
+ if (a.role === "system" && b.role !== "system") return -1;
569
+ if (a.role !== "system" && b.role === "system") return 1;
570
+ return a.timestamp - b.timestamp;
571
+ });
572
+ }
573
+ function formatMessagesForDisplay(messages) {
574
+ return messages.map((msg) => {
575
+ const provider = msg.provider ? ` [${msg.provider}/${msg.model}]` : "";
576
+ const prefix = `[${msg.role.toUpperCase()}]${provider}`;
577
+ return `${prefix}: ${msg.content.substring(0, 100)}${msg.content.length > 100 ? "..." : ""}`;
578
+ }).join("\n");
579
+ }
580
+
581
+ // src/core/session.ts
582
+ function generateSessionId() {
583
+ return `session_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
584
+ }
585
+ function createSession(options) {
586
+ const now = Date.now();
587
+ return {
588
+ id: options?.id ?? generateSessionId(),
589
+ messages: [],
590
+ systemPrompt: options?.systemPrompt,
591
+ createdAt: now,
592
+ updatedAt: now,
593
+ metadata: options?.metadata ?? {}
594
+ };
595
+ }
596
+ function addMessage(session, message) {
597
+ return {
598
+ ...session,
599
+ messages: [...session.messages, message],
600
+ updatedAt: Date.now()
601
+ };
602
+ }
603
+ function addMessages(session, messages) {
604
+ return {
605
+ ...session,
606
+ messages: [...session.messages, ...messages],
607
+ updatedAt: Date.now()
608
+ };
609
+ }
610
+ function getMessagesForProvider(session, _provider) {
611
+ const messages = [];
612
+ if (session.systemPrompt) {
613
+ messages.push(createSystemMessage(session.systemPrompt));
614
+ }
615
+ messages.push(...session.messages);
616
+ return messages;
617
+ }
618
+ function setSystemPrompt(session, prompt) {
619
+ return {
620
+ ...session,
621
+ systemPrompt: prompt,
622
+ updatedAt: Date.now()
623
+ };
624
+ }
625
+ function clearMessages(session) {
626
+ return {
627
+ ...session,
628
+ messages: [],
629
+ updatedAt: Date.now()
630
+ };
631
+ }
632
+ function getLastMessages(session, count) {
633
+ return session.messages.slice(-count);
634
+ }
635
+ function getMessagesAfter(session, timestamp) {
636
+ return session.messages.filter((m) => m.timestamp > timestamp);
637
+ }
638
+ function getSessionSummary(session) {
639
+ const providers = /* @__PURE__ */ new Set();
640
+ let firstMessageAt = null;
641
+ let lastMessageAt = null;
642
+ for (const msg of session.messages) {
643
+ if (msg.provider) {
644
+ providers.add(msg.provider);
645
+ }
646
+ if (firstMessageAt === null || msg.timestamp < firstMessageAt) {
647
+ firstMessageAt = msg.timestamp;
648
+ }
649
+ if (lastMessageAt === null || msg.timestamp > lastMessageAt) {
650
+ lastMessageAt = msg.timestamp;
651
+ }
652
+ }
653
+ return {
654
+ id: session.id,
655
+ messageCount: session.messages.length,
656
+ providers: Array.from(providers),
657
+ firstMessageAt,
658
+ lastMessageAt
659
+ };
660
+ }
661
+ function setSessionMetadata(session, key, value) {
662
+ return {
663
+ ...session,
664
+ metadata: {
665
+ ...session.metadata,
666
+ [key]: value
667
+ },
668
+ updatedAt: Date.now()
669
+ };
670
+ }
671
+ function serializeSession(session) {
672
+ return {
673
+ id: session.id,
674
+ messages: session.messages,
675
+ systemPrompt: session.systemPrompt,
676
+ createdAt: session.createdAt,
677
+ updatedAt: session.updatedAt,
678
+ metadata: session.metadata
679
+ };
680
+ }
681
+ function deserializeSession(data) {
682
+ return {
683
+ id: data.id,
684
+ messages: data.messages,
685
+ systemPrompt: data.systemPrompt,
686
+ createdAt: data.createdAt,
687
+ updatedAt: data.updatedAt,
688
+ metadata: data.metadata ?? {}
689
+ };
690
+ }
691
+
692
+ // src/core/chat.ts
693
+ function generateChatId() {
694
+ return `chat_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
695
+ }
696
+ var chatStates = /* @__PURE__ */ new Map();
697
+ function createChat(config) {
698
+ const mergedConfig = mergeConfig(config);
699
+ const chatId = config.id ?? generateChatId();
700
+ const providerRegistry = new ProviderRegistry();
701
+ if (config.providers.ollama !== void 0 || config.defaultProvider === "ollama") {
702
+ providerRegistry.register(new OllamaProvider(mergedConfig));
703
+ }
704
+ if (!providerRegistry.has(config.defaultProvider)) {
705
+ if (config.defaultProvider === "ollama") {
706
+ providerRegistry.register(new OllamaProvider(mergedConfig));
707
+ } else {
708
+ throw new ConfigurationError(
709
+ `Default provider "${config.defaultProvider}" is not configured`
710
+ );
711
+ }
712
+ }
713
+ const now = Date.now();
714
+ const chat = {
715
+ id: chatId,
716
+ config: mergedConfig,
717
+ messages: [],
718
+ tools: /* @__PURE__ */ new Map(),
719
+ memory: /* @__PURE__ */ new Map(),
720
+ createdAt: now,
721
+ updatedAt: now
722
+ };
723
+ const session = createSession({
724
+ systemPrompt: config.systemPrompt
725
+ });
726
+ chatStates.set(chatId, {
727
+ chat,
728
+ session,
729
+ providerRegistry
730
+ });
731
+ return chat;
732
+ }
733
+ function getChatState(chat) {
734
+ const state = chatStates.get(chat.id);
735
+ if (!state) {
736
+ throw new ConfigurationError(`Chat "${chat.id}" not found`);
737
+ }
738
+ return state;
739
+ }
740
+ function getProvider(chat, providerType) {
741
+ const state = getChatState(chat);
742
+ const type = providerType ?? chat.config.defaultProvider;
743
+ const provider = state.providerRegistry.get(type);
744
+ if (!provider) {
745
+ throw new ConfigurationError(`Provider "${type}" is not available`);
746
+ }
747
+ return provider;
748
+ }
749
+ async function sendMessage(chat, content, options) {
750
+ const state = getChatState(chat);
751
+ const provider = getProvider(chat, options?.provider);
752
+ const model = options?.model ?? getDefaultModel(provider.name, chat.config);
753
+ const userMessage = createUserMessage(content);
754
+ state.session = addMessage(state.session, userMessage);
755
+ chat.messages.push(userMessage);
756
+ const messages = getMessagesForProvider(state.session, provider.name);
757
+ const tools = options?.tools ?? Array.from(chat.tools.values());
758
+ const sendOptions = {
759
+ ...options,
760
+ model,
761
+ tools: tools.length > 0 ? tools : void 0
762
+ };
763
+ const response = await provider.sendMessage(messages, sendOptions);
764
+ state.session = addMessage(state.session, response);
765
+ chat.messages.push(response);
766
+ chat.updatedAt = Date.now();
767
+ if (response.toolCalls && response.toolCalls.length > 0) {
768
+ return await handleToolCalls(chat, response, sendOptions);
769
+ }
770
+ return response;
771
+ }
772
+ async function* sendMessageStream(chat, content, options) {
773
+ const state = getChatState(chat);
774
+ const provider = getProvider(chat, options?.provider);
775
+ const model = options?.model ?? getDefaultModel(provider.name, chat.config);
776
+ const userMessage = createUserMessage(content);
777
+ state.session = addMessage(state.session, userMessage);
778
+ chat.messages.push(userMessage);
779
+ const messages = getMessagesForProvider(state.session, provider.name);
780
+ const tools = options?.tools ?? Array.from(chat.tools.values());
781
+ const sendOptions = {
782
+ ...options,
783
+ model,
784
+ tools: tools.length > 0 ? tools : void 0
785
+ };
786
+ for await (const event of provider.sendMessageStream(messages, sendOptions)) {
787
+ yield event;
788
+ if (event.type === "done" && event.message) {
789
+ state.session = addMessage(state.session, event.message);
790
+ chat.messages.push(event.message);
791
+ chat.updatedAt = Date.now();
792
+ }
793
+ }
794
+ }
795
+ async function handleToolCalls(chat, response, options) {
796
+ const state = getChatState(chat);
797
+ const provider = getProvider(chat, options.provider);
798
+ if (!response.toolCalls) {
799
+ return response;
800
+ }
801
+ for (const toolCall of response.toolCalls) {
802
+ const tool = chat.tools.get(toolCall.name);
803
+ if (!tool) {
804
+ const errorMessage = createAssistantMessage(
805
+ `Tool "${toolCall.name}" not found`,
806
+ provider.name,
807
+ options.model
808
+ );
809
+ state.session = addMessage(state.session, errorMessage);
810
+ continue;
811
+ }
812
+ try {
813
+ const result = await tool.handler(toolCall.arguments);
814
+ const toolResultMessage = {
815
+ id: `msg_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`,
816
+ role: "tool",
817
+ content: JSON.stringify(result),
818
+ toolCallId: toolCall.id,
819
+ timestamp: Date.now()
820
+ };
821
+ state.session = addMessage(state.session, toolResultMessage);
822
+ chat.messages.push(toolResultMessage);
823
+ } catch (error) {
824
+ const errorContent = error instanceof Error ? error.message : String(error);
825
+ const toolErrorMessage = {
826
+ id: `msg_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`,
827
+ role: "tool",
828
+ content: JSON.stringify({ error: errorContent }),
829
+ toolCallId: toolCall.id,
830
+ timestamp: Date.now()
831
+ };
832
+ state.session = addMessage(state.session, toolErrorMessage);
833
+ chat.messages.push(toolErrorMessage);
834
+ }
835
+ }
836
+ const messages = getMessagesForProvider(state.session, provider.name);
837
+ const finalResponse = await provider.sendMessage(messages, options);
838
+ state.session = addMessage(state.session, finalResponse);
839
+ chat.messages.push(finalResponse);
840
+ chat.updatedAt = Date.now();
841
+ if (finalResponse.toolCalls && finalResponse.toolCalls.length > 0) {
842
+ return handleToolCalls(chat, finalResponse, options);
843
+ }
844
+ return finalResponse;
845
+ }
846
+ function registerTool(chat, tool) {
847
+ chat.tools.set(tool.name, tool);
848
+ chat.updatedAt = Date.now();
849
+ }
850
+ function unregisterTool(chat, toolName) {
851
+ const deleted = chat.tools.delete(toolName);
852
+ if (deleted) {
853
+ chat.updatedAt = Date.now();
854
+ }
855
+ return deleted;
856
+ }
857
+ function getTools(chat) {
858
+ return Array.from(chat.tools.values());
859
+ }
860
+ function setSystemPrompt2(chat, prompt) {
861
+ const state = getChatState(chat);
862
+ state.session.systemPrompt = prompt;
863
+ chat.config.systemPrompt = prompt;
864
+ chat.updatedAt = Date.now();
865
+ }
866
+ function getMessages(chat) {
867
+ return [...chat.messages];
868
+ }
869
+ function clearMessages2(chat) {
870
+ const state = getChatState(chat);
871
+ chat.messages = [];
872
+ state.session.messages = [];
873
+ chat.updatedAt = Date.now();
874
+ }
875
+ function resetChat(chat) {
876
+ const state = getChatState(chat);
877
+ chat.messages = [];
878
+ state.session.messages = [];
879
+ chat.tools.clear();
880
+ chat.memory.clear();
881
+ const now = Date.now();
882
+ chat.updatedAt = now;
883
+ state.session.updatedAt = now;
884
+ }
885
+ function getSession(chat) {
886
+ const state = getChatState(chat);
887
+ return state.session;
888
+ }
889
+ function destroyChat(chat) {
890
+ chatStates.delete(chat.id);
891
+ }
892
+
893
+ // src/memory/storage.ts
894
+ import { existsSync, mkdirSync, readFileSync, writeFileSync, unlinkSync } from "fs";
895
+ import { join as join2, dirname } from "path";
896
+ var FileStorage = class {
897
+ basePath;
898
+ constructor(basePath) {
899
+ this.basePath = basePath;
900
+ this.ensureDirectory(basePath);
901
+ }
902
+ /**
903
+ * Ensure a directory exists
904
+ */
905
+ ensureDirectory(path) {
906
+ if (!existsSync(path)) {
907
+ try {
908
+ mkdirSync(path, { recursive: true });
909
+ } catch (error) {
910
+ throw new StorageError(
911
+ "write",
912
+ `Failed to create directory: ${path}`,
913
+ error instanceof Error ? error : void 0
914
+ );
915
+ }
916
+ }
917
+ }
918
+ /**
919
+ * Get the full path for a key
920
+ */
921
+ getFilePath(key) {
922
+ const sanitizedKey = key.replace(/[^a-zA-Z0-9_-]/g, "_");
923
+ return join2(this.basePath, `${sanitizedKey}.json`);
924
+ }
925
+ /**
926
+ * Read data from storage
927
+ */
928
+ read(key) {
929
+ const filePath = this.getFilePath(key);
930
+ if (!existsSync(filePath)) {
931
+ return null;
932
+ }
933
+ try {
934
+ const content = readFileSync(filePath, "utf-8");
935
+ return JSON.parse(content);
936
+ } catch (error) {
937
+ throw new StorageError(
938
+ "read",
939
+ `Failed to read key: ${key}`,
940
+ error instanceof Error ? error : void 0
941
+ );
942
+ }
943
+ }
944
+ /**
945
+ * Write data to storage
946
+ */
947
+ write(key, data) {
948
+ const filePath = this.getFilePath(key);
949
+ try {
950
+ this.ensureDirectory(dirname(filePath));
951
+ writeFileSync(filePath, JSON.stringify(data, null, 2), "utf-8");
952
+ } catch (error) {
953
+ throw new StorageError(
954
+ "write",
955
+ `Failed to write key: ${key}`,
956
+ error instanceof Error ? error : void 0
957
+ );
958
+ }
959
+ }
960
+ /**
961
+ * Delete data from storage
962
+ */
963
+ delete(key) {
964
+ const filePath = this.getFilePath(key);
965
+ if (!existsSync(filePath)) {
966
+ return false;
967
+ }
968
+ try {
969
+ unlinkSync(filePath);
970
+ return true;
971
+ } catch (error) {
972
+ throw new StorageError(
973
+ "delete",
974
+ `Failed to delete key: ${key}`,
975
+ error instanceof Error ? error : void 0
976
+ );
977
+ }
978
+ }
979
+ /**
980
+ * Check if a key exists
981
+ */
982
+ exists(key) {
983
+ return existsSync(this.getFilePath(key));
984
+ }
985
+ /**
986
+ * Get the base path
987
+ */
988
+ getBasePath() {
989
+ return this.basePath;
990
+ }
991
+ };
992
+ var MemoryStorage = class {
993
+ data = /* @__PURE__ */ new Map();
994
+ read(key) {
995
+ const value = this.data.get(key);
996
+ return value !== void 0 ? value : null;
997
+ }
998
+ write(key, data) {
999
+ this.data.set(key, data);
1000
+ }
1001
+ delete(key) {
1002
+ return this.data.delete(key);
1003
+ }
1004
+ exists(key) {
1005
+ return this.data.has(key);
1006
+ }
1007
+ clear() {
1008
+ this.data.clear();
1009
+ }
1010
+ getAll() {
1011
+ return new Map(this.data);
1012
+ }
1013
+ };
1014
+
1015
+ // src/memory/global.ts
1016
+ import { join as join3 } from "path";
1017
+ var GlobalMemoryStore = class {
1018
+ entries = /* @__PURE__ */ new Map();
1019
+ storage;
1020
+ storageKey = "global_memory";
1021
+ autoSave;
1022
+ constructor(storagePath, autoSave = true) {
1023
+ this.autoSave = autoSave;
1024
+ if (storagePath) {
1025
+ this.storage = new FileStorage(join3(storagePath, "memory"));
1026
+ this.load();
1027
+ } else {
1028
+ this.storage = new MemoryStorage();
1029
+ }
1030
+ }
1031
+ /**
1032
+ * Load entries from storage
1033
+ */
1034
+ load() {
1035
+ const data = this.storage.read(this.storageKey);
1036
+ if (data) {
1037
+ this.entries = new Map(Object.entries(data));
1038
+ }
1039
+ }
1040
+ /**
1041
+ * Save entries to storage
1042
+ */
1043
+ save() {
1044
+ if (!this.autoSave) return;
1045
+ const data = {};
1046
+ for (const [key, value] of this.entries) {
1047
+ data[key] = value;
1048
+ }
1049
+ this.storage.write(this.storageKey, data);
1050
+ }
1051
+ /**
1052
+ * Set a memory entry
1053
+ */
1054
+ set(key, value) {
1055
+ const now = Date.now();
1056
+ const existing = this.entries.get(key);
1057
+ this.entries.set(key, {
1058
+ key,
1059
+ value,
1060
+ createdAt: existing?.createdAt ?? now,
1061
+ updatedAt: now
1062
+ });
1063
+ this.save();
1064
+ }
1065
+ /**
1066
+ * Get a memory entry value
1067
+ */
1068
+ get(key) {
1069
+ const entry = this.entries.get(key);
1070
+ return entry?.value;
1071
+ }
1072
+ /**
1073
+ * Get a memory entry with metadata
1074
+ */
1075
+ getEntry(key) {
1076
+ return this.entries.get(key);
1077
+ }
1078
+ /**
1079
+ * Check if a key exists
1080
+ */
1081
+ has(key) {
1082
+ return this.entries.has(key);
1083
+ }
1084
+ /**
1085
+ * Delete a memory entry
1086
+ */
1087
+ delete(key) {
1088
+ const deleted = this.entries.delete(key);
1089
+ if (deleted) {
1090
+ this.save();
1091
+ }
1092
+ return deleted;
1093
+ }
1094
+ /**
1095
+ * Get all memory entries
1096
+ */
1097
+ getAll() {
1098
+ return new Map(this.entries);
1099
+ }
1100
+ /**
1101
+ * Get all keys
1102
+ */
1103
+ keys() {
1104
+ return Array.from(this.entries.keys());
1105
+ }
1106
+ /**
1107
+ * Clear all entries
1108
+ */
1109
+ clear() {
1110
+ this.entries.clear();
1111
+ this.save();
1112
+ }
1113
+ /**
1114
+ * Get entries count
1115
+ */
1116
+ size() {
1117
+ return this.entries.size;
1118
+ }
1119
+ /**
1120
+ * Force save to storage
1121
+ */
1122
+ forceSave() {
1123
+ const originalAutoSave = this.autoSave;
1124
+ this.autoSave = true;
1125
+ this.save();
1126
+ this.autoSave = originalAutoSave;
1127
+ }
1128
+ /**
1129
+ * Convert entries to a format suitable for system prompt injection
1130
+ */
1131
+ toPromptContext() {
1132
+ if (this.entries.size === 0) {
1133
+ return "";
1134
+ }
1135
+ const lines = ["[Global Memory]"];
1136
+ for (const [key, entry] of this.entries) {
1137
+ const valueStr = typeof entry.value === "object" ? JSON.stringify(entry.value) : String(entry.value);
1138
+ lines.push(`- ${key}: ${valueStr}`);
1139
+ }
1140
+ return lines.join("\n");
1141
+ }
1142
+ };
1143
+ var globalMemoryInstance = null;
1144
+ function initGlobalMemory(storagePath, autoSave = true) {
1145
+ globalMemoryInstance = new GlobalMemoryStore(storagePath ?? DEFAULT_STORAGE_PATH, autoSave);
1146
+ }
1147
+ function getGlobalMemoryStore() {
1148
+ if (!globalMemoryInstance) {
1149
+ globalMemoryInstance = new GlobalMemoryStore();
1150
+ }
1151
+ return globalMemoryInstance;
1152
+ }
1153
+ function setGlobalMemory(key, value) {
1154
+ getGlobalMemoryStore().set(key, value);
1155
+ }
1156
+ function getGlobalMemory(key) {
1157
+ return getGlobalMemoryStore().get(key);
1158
+ }
1159
+ function deleteGlobalMemory(key) {
1160
+ return getGlobalMemoryStore().delete(key);
1161
+ }
1162
+ function hasGlobalMemory(key) {
1163
+ return getGlobalMemoryStore().has(key);
1164
+ }
1165
+ function getGlobalMemoryKeys() {
1166
+ return getGlobalMemoryStore().keys();
1167
+ }
1168
+ function clearGlobalMemory() {
1169
+ getGlobalMemoryStore().clear();
1170
+ }
1171
+ function getGlobalMemoryContext() {
1172
+ return getGlobalMemoryStore().toPromptContext();
1173
+ }
1174
+
1175
+ // src/memory/chat.ts
1176
+ import { join as join4 } from "path";
1177
+ var ChatMemoryStore = class {
1178
+ storage;
1179
+ autoSave;
1180
+ constructor(storagePath, autoSave = true) {
1181
+ this.autoSave = autoSave;
1182
+ if (storagePath) {
1183
+ this.storage = new FileStorage(join4(storagePath, "chat_memory"));
1184
+ } else {
1185
+ this.storage = new MemoryStorage();
1186
+ }
1187
+ }
1188
+ /**
1189
+ * Get storage key for a chat
1190
+ */
1191
+ getChatKey(chatId) {
1192
+ return `chat_${chatId}`;
1193
+ }
1194
+ /**
1195
+ * Load chat memory from storage
1196
+ */
1197
+ loadChatMemory(chatId) {
1198
+ const data = this.storage.read(this.getChatKey(chatId));
1199
+ if (data) {
1200
+ return new Map(Object.entries(data));
1201
+ }
1202
+ return /* @__PURE__ */ new Map();
1203
+ }
1204
+ /**
1205
+ * Save chat memory to storage
1206
+ */
1207
+ saveChatMemory(chatId, entries) {
1208
+ if (!this.autoSave) return;
1209
+ const data = {};
1210
+ for (const [key, value] of entries) {
1211
+ data[key] = value;
1212
+ }
1213
+ this.storage.write(this.getChatKey(chatId), data);
1214
+ }
1215
+ /**
1216
+ * Set a memory entry for a chat
1217
+ */
1218
+ set(chatId, key, value) {
1219
+ const entries = this.loadChatMemory(chatId);
1220
+ const now = Date.now();
1221
+ const existing = entries.get(key);
1222
+ entries.set(key, {
1223
+ key,
1224
+ value,
1225
+ createdAt: existing?.createdAt ?? now,
1226
+ updatedAt: now
1227
+ });
1228
+ this.saveChatMemory(chatId, entries);
1229
+ }
1230
+ /**
1231
+ * Get a memory entry value for a chat
1232
+ */
1233
+ get(chatId, key) {
1234
+ const entries = this.loadChatMemory(chatId);
1235
+ const entry = entries.get(key);
1236
+ return entry?.value;
1237
+ }
1238
+ /**
1239
+ * Get a memory entry with metadata
1240
+ */
1241
+ getEntry(chatId, key) {
1242
+ const entries = this.loadChatMemory(chatId);
1243
+ return entries.get(key);
1244
+ }
1245
+ /**
1246
+ * Check if a key exists for a chat
1247
+ */
1248
+ has(chatId, key) {
1249
+ const entries = this.loadChatMemory(chatId);
1250
+ return entries.has(key);
1251
+ }
1252
+ /**
1253
+ * Delete a memory entry for a chat
1254
+ */
1255
+ delete(chatId, key) {
1256
+ const entries = this.loadChatMemory(chatId);
1257
+ const deleted = entries.delete(key);
1258
+ if (deleted) {
1259
+ this.saveChatMemory(chatId, entries);
1260
+ }
1261
+ return deleted;
1262
+ }
1263
+ /**
1264
+ * Get all memory entries for a chat
1265
+ */
1266
+ getAll(chatId) {
1267
+ return this.loadChatMemory(chatId);
1268
+ }
1269
+ /**
1270
+ * Get all keys for a chat
1271
+ */
1272
+ keys(chatId) {
1273
+ const entries = this.loadChatMemory(chatId);
1274
+ return Array.from(entries.keys());
1275
+ }
1276
+ /**
1277
+ * Clear all entries for a chat
1278
+ */
1279
+ clear(chatId) {
1280
+ this.storage.delete(this.getChatKey(chatId));
1281
+ }
1282
+ /**
1283
+ * Get entries count for a chat
1284
+ */
1285
+ size(chatId) {
1286
+ const entries = this.loadChatMemory(chatId);
1287
+ return entries.size;
1288
+ }
1289
+ /**
1290
+ * Convert chat memory to a format suitable for system prompt injection
1291
+ */
1292
+ toPromptContext(chatId) {
1293
+ const entries = this.loadChatMemory(chatId);
1294
+ if (entries.size === 0) {
1295
+ return "";
1296
+ }
1297
+ const lines = ["[Chat Memory]"];
1298
+ for (const [key, entry] of entries) {
1299
+ const valueStr = typeof entry.value === "object" ? JSON.stringify(entry.value) : String(entry.value);
1300
+ lines.push(`- ${key}: ${valueStr}`);
1301
+ }
1302
+ return lines.join("\n");
1303
+ }
1304
+ };
1305
+ var chatMemoryInstance = null;
1306
+ function initChatMemory(storagePath, autoSave = true) {
1307
+ chatMemoryInstance = new ChatMemoryStore(storagePath, autoSave);
1308
+ }
1309
+ function getChatMemoryStore() {
1310
+ if (!chatMemoryInstance) {
1311
+ chatMemoryInstance = new ChatMemoryStore();
1312
+ }
1313
+ return chatMemoryInstance;
1314
+ }
1315
+ function setChatMemory(chat, key, value) {
1316
+ getChatMemoryStore().set(chat.id, key, value);
1317
+ const now = Date.now();
1318
+ const existing = chat.memory.get(key);
1319
+ chat.memory.set(key, {
1320
+ key,
1321
+ value,
1322
+ createdAt: existing?.createdAt ?? now,
1323
+ updatedAt: now
1324
+ });
1325
+ }
1326
+ function getChatMemory(chat, key) {
1327
+ const internalEntry = chat.memory.get(key);
1328
+ if (internalEntry) {
1329
+ return internalEntry.value;
1330
+ }
1331
+ return getChatMemoryStore().get(chat.id, key);
1332
+ }
1333
+ function deleteChatMemory(chat, key) {
1334
+ chat.memory.delete(key);
1335
+ return getChatMemoryStore().delete(chat.id, key);
1336
+ }
1337
+ function hasChatMemory(chat, key) {
1338
+ return chat.memory.has(key) || getChatMemoryStore().has(chat.id, key);
1339
+ }
1340
+ function getChatMemoryKeys(chat) {
1341
+ const internalKeys = Array.from(chat.memory.keys());
1342
+ const storedKeys = getChatMemoryStore().keys(chat.id);
1343
+ return [.../* @__PURE__ */ new Set([...internalKeys, ...storedKeys])];
1344
+ }
1345
+ function clearChatMemory(chat) {
1346
+ chat.memory.clear();
1347
+ getChatMemoryStore().clear(chat.id);
1348
+ }
1349
+ function getChatMemoryContext(chat) {
1350
+ return getChatMemoryStore().toPromptContext(chat.id);
1351
+ }
1352
+ function syncChatMemory(chat) {
1353
+ for (const [key, entry] of chat.memory) {
1354
+ getChatMemoryStore().set(chat.id, key, entry.value);
1355
+ }
1356
+ }
1357
+
1358
+ // src/memory/manager.ts
1359
+ function initMemory(config = {}) {
1360
+ initGlobalMemory(config.storagePath, config.autoSave);
1361
+ initChatMemory(config.storagePath, config.autoSave);
1362
+ }
1363
+ function getMemoryContext(chat) {
1364
+ const globalContext = getGlobalMemoryContext();
1365
+ const chatContext = getChatMemoryContext(chat);
1366
+ const parts = [];
1367
+ if (globalContext) {
1368
+ parts.push(globalContext);
1369
+ }
1370
+ if (chatContext) {
1371
+ parts.push(chatContext);
1372
+ }
1373
+ return parts.join("\n\n");
1374
+ }
1375
+ function buildSystemPromptWithMemory(basePrompt, chat) {
1376
+ const memoryContext = getMemoryContext(chat);
1377
+ if (!memoryContext) {
1378
+ return basePrompt;
1379
+ }
1380
+ return `${basePrompt}
1381
+
1382
+ ---
1383
+ ${memoryContext}
1384
+ ---`;
1385
+ }
1386
+
1387
+ // src/acontext/observer.ts
1388
+ var observerStates = /* @__PURE__ */ new Map();
1389
+ var observerCallbacks = /* @__PURE__ */ new Map();
1390
+ var DEFAULT_OBSERVER_CONFIG = {
1391
+ extractTasks: true,
1392
+ extractPreferences: true,
1393
+ autoLearn: false
1394
+ };
1395
+ function generateId(prefix) {
1396
+ return `${prefix}_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
1397
+ }
1398
+ function getState(chatId) {
1399
+ let state = observerStates.get(chatId);
1400
+ if (!state) {
1401
+ state = {
1402
+ enabled: false,
1403
+ config: { ...DEFAULT_OBSERVER_CONFIG },
1404
+ tasks: [],
1405
+ preferences: []
1406
+ };
1407
+ observerStates.set(chatId, state);
1408
+ }
1409
+ return state;
1410
+ }
1411
+ function emitEvent(chatId, event) {
1412
+ const callbacks = observerCallbacks.get(chatId) || [];
1413
+ for (const callback of callbacks) {
1414
+ try {
1415
+ callback(event);
1416
+ } catch (error) {
1417
+ console.error("Observer callback error:", error);
1418
+ }
1419
+ }
1420
+ }
1421
+ function enableObserver(chat, config) {
1422
+ const state = getState(chat.id);
1423
+ state.enabled = true;
1424
+ state.config = { ...DEFAULT_OBSERVER_CONFIG, ...config };
1425
+ observerStates.set(chat.id, state);
1426
+ }
1427
+ function disableObserver(chat) {
1428
+ const state = getState(chat.id);
1429
+ state.enabled = false;
1430
+ observerStates.set(chat.id, state);
1431
+ }
1432
+ function isObserverEnabled(chat) {
1433
+ return getState(chat.id).enabled;
1434
+ }
1435
+ function getObserverConfig(chat) {
1436
+ return { ...getState(chat.id).config };
1437
+ }
1438
+ function onObserverEvent(chat, callback) {
1439
+ const callbacks = observerCallbacks.get(chat.id) || [];
1440
+ callbacks.push(callback);
1441
+ observerCallbacks.set(chat.id, callbacks);
1442
+ return () => {
1443
+ const currentCallbacks = observerCallbacks.get(chat.id) || [];
1444
+ const index = currentCallbacks.indexOf(callback);
1445
+ if (index > -1) {
1446
+ currentCallbacks.splice(index, 1);
1447
+ observerCallbacks.set(chat.id, currentCallbacks);
1448
+ }
1449
+ };
1450
+ }
1451
+ function analyzeMessages(chat, messages) {
1452
+ const state = getState(chat.id);
1453
+ if (!state.enabled) {
1454
+ return { tasks: [], preferences: [] };
1455
+ }
1456
+ const messagesToAnalyze = messages || chat.messages;
1457
+ const tasks = [];
1458
+ const preferences = [];
1459
+ for (const msg of messagesToAnalyze) {
1460
+ if (msg.role === "user") {
1461
+ const taskPatterns = [
1462
+ /(?:해줘|해주세요|만들어|작성해|구현해|추가해|수정해|삭제해|찾아)/,
1463
+ /(?:please|create|make|write|implement|add|update|delete|find)/i
1464
+ ];
1465
+ for (const pattern of taskPatterns) {
1466
+ if (pattern.test(msg.content)) {
1467
+ const description = msg.content.substring(0, 100).trim();
1468
+ tasks.push({
1469
+ description,
1470
+ status: "pending",
1471
+ progress: 0
1472
+ });
1473
+ break;
1474
+ }
1475
+ }
1476
+ const preferencePatterns = [
1477
+ { pattern: /한국어|korean/i, key: "language", value: "ko" },
1478
+ { pattern: /영어|english/i, key: "language", value: "en" },
1479
+ { pattern: /간단히|짧게|briefly/i, key: "response_length", value: "short" },
1480
+ { pattern: /자세히|상세히|in detail/i, key: "response_length", value: "detailed" },
1481
+ { pattern: /코드로|with code/i, key: "include_code", value: true }
1482
+ ];
1483
+ for (const { pattern, key, value } of preferencePatterns) {
1484
+ if (pattern.test(msg.content)) {
1485
+ preferences.push({
1486
+ key,
1487
+ value,
1488
+ confidence: 0.7
1489
+ });
1490
+ }
1491
+ }
1492
+ }
1493
+ if (msg.role === "assistant") {
1494
+ const completionPatterns = [
1495
+ /완료|완성|done|finished|completed/i
1496
+ ];
1497
+ for (const pattern of completionPatterns) {
1498
+ if (pattern.test(msg.content) && tasks.length > 0) {
1499
+ for (let i = tasks.length - 1; i >= 0; i--) {
1500
+ if (tasks[i].status === "pending") {
1501
+ tasks[i].status = "completed";
1502
+ tasks[i].progress = 100;
1503
+ break;
1504
+ }
1505
+ }
1506
+ }
1507
+ }
1508
+ }
1509
+ }
1510
+ return { tasks, preferences };
1511
+ }
1512
+ function processMessages(chat, newMessages) {
1513
+ const state = getState(chat.id);
1514
+ if (!state.enabled) return;
1515
+ const extraction = analyzeMessages(chat, newMessages);
1516
+ const now = Date.now();
1517
+ if (state.config.extractTasks) {
1518
+ for (const extractedTask of extraction.tasks) {
1519
+ const task = {
1520
+ id: generateId("task"),
1521
+ description: extractedTask.description,
1522
+ status: extractedTask.status,
1523
+ progress: extractedTask.progress,
1524
+ createdAt: now,
1525
+ updatedAt: now
1526
+ };
1527
+ state.tasks.push(task);
1528
+ emitEvent(chat.id, {
1529
+ type: "task_detected",
1530
+ timestamp: now,
1531
+ data: task
1532
+ });
1533
+ }
1534
+ }
1535
+ if (state.config.extractPreferences) {
1536
+ for (const extractedPref of extraction.preferences) {
1537
+ const existingIndex = state.preferences.findIndex((p) => p.key === extractedPref.key);
1538
+ const preference = {
1539
+ key: extractedPref.key,
1540
+ value: extractedPref.value,
1541
+ confidence: extractedPref.confidence,
1542
+ source: `message_${newMessages[0]?.id || "unknown"}`
1543
+ };
1544
+ if (existingIndex >= 0) {
1545
+ state.preferences[existingIndex] = preference;
1546
+ } else {
1547
+ state.preferences.push(preference);
1548
+ }
1549
+ emitEvent(chat.id, {
1550
+ type: "preference_detected",
1551
+ timestamp: now,
1552
+ data: preference
1553
+ });
1554
+ }
1555
+ }
1556
+ observerStates.set(chat.id, state);
1557
+ }
1558
+ function getObservedTasks(chat) {
1559
+ return [...getState(chat.id).tasks];
1560
+ }
1561
+ function getObservedPreferences(chat) {
1562
+ return [...getState(chat.id).preferences];
1563
+ }
1564
+ function updateTaskStatus(chat, taskId, status, progress) {
1565
+ const state = getState(chat.id);
1566
+ const task = state.tasks.find((t) => t.id === taskId);
1567
+ if (!task) return false;
1568
+ task.status = status;
1569
+ if (progress !== void 0) {
1570
+ task.progress = progress;
1571
+ }
1572
+ task.updatedAt = Date.now();
1573
+ emitEvent(chat.id, {
1574
+ type: "task_updated",
1575
+ timestamp: Date.now(),
1576
+ data: task
1577
+ });
1578
+ observerStates.set(chat.id, state);
1579
+ return true;
1580
+ }
1581
+ function clearObserverState(chat) {
1582
+ observerStates.delete(chat.id);
1583
+ observerCallbacks.delete(chat.id);
1584
+ }
1585
+ function getObserverState(chat) {
1586
+ return { ...getState(chat.id) };
1587
+ }
1588
+
1589
+ // src/acontext/learner.ts
1590
+ import { join as join5 } from "path";
1591
+ var SkillLearner = class {
1592
+ storage;
1593
+ skills = /* @__PURE__ */ new Map();
1594
+ storageKey = "learned_skills";
1595
+ constructor(storagePath) {
1596
+ if (storagePath) {
1597
+ this.storage = new FileStorage(join5(storagePath, "acontext"));
1598
+ this.load();
1599
+ } else {
1600
+ this.storage = new MemoryStorage();
1601
+ }
1602
+ }
1603
+ /**
1604
+ * Load skills from storage
1605
+ */
1606
+ load() {
1607
+ const data = this.storage.read(this.storageKey);
1608
+ if (data) {
1609
+ this.skills = new Map(Object.entries(data));
1610
+ }
1611
+ }
1612
+ /**
1613
+ * Save skills to storage
1614
+ */
1615
+ save() {
1616
+ const data = {};
1617
+ for (const [key, value] of this.skills) {
1618
+ data[key] = value;
1619
+ }
1620
+ this.storage.write(this.storageKey, data);
1621
+ }
1622
+ /**
1623
+ * Generate a unique skill ID
1624
+ */
1625
+ generateId() {
1626
+ return `skill_${Date.now()}_${Math.random().toString(36).substring(2, 9)}`;
1627
+ }
1628
+ /**
1629
+ * Extract category from task description
1630
+ */
1631
+ extractCategory(description) {
1632
+ const categoryPatterns = [
1633
+ [/여행|travel|trip/i, "travel"],
1634
+ [/코드|code|programming|개발/i, "coding"],
1635
+ [/글|write|작성|문서/i, "writing"],
1636
+ [/분석|analyze|analysis/i, "analysis"],
1637
+ [/검색|search|find|찾/i, "search"],
1638
+ [/번역|translate/i, "translation"],
1639
+ [/요약|summarize|summary/i, "summarization"]
1640
+ ];
1641
+ for (const [pattern, category] of categoryPatterns) {
1642
+ if (pattern.test(description)) {
1643
+ return category;
1644
+ }
1645
+ }
1646
+ return "general";
1647
+ }
1648
+ /**
1649
+ * Learn from a completed conversation
1650
+ */
1651
+ learn(input) {
1652
+ const now = Date.now();
1653
+ const skillId = this.generateId();
1654
+ const category = this.extractCategory(input.taskDescription);
1655
+ const steps = input.steps.map((step, index) => ({
1656
+ order: index + 1,
1657
+ action: this.summarizeAction(step),
1658
+ expectedInput: this.extractInputPattern(step.userInput),
1659
+ expectedOutput: this.extractOutputPattern(step.assistantOutput)
1660
+ }));
1661
+ const skill = {
1662
+ id: skillId,
1663
+ name: this.generateSkillName(input.taskDescription),
1664
+ description: input.taskDescription,
1665
+ category,
1666
+ steps,
1667
+ successRate: input.outcome === "success" ? 1 : input.outcome === "partial" ? 0.5 : 0,
1668
+ usageCount: 0,
1669
+ createdAt: now,
1670
+ updatedAt: now
1671
+ };
1672
+ this.skills.set(skillId, skill);
1673
+ this.save();
1674
+ return skill;
1675
+ }
1676
+ /**
1677
+ * Summarize an action from a learning step
1678
+ */
1679
+ summarizeAction(step) {
1680
+ const summary = step.assistantOutput.substring(0, 100).trim();
1681
+ if (step.toolsUsed && step.toolsUsed.length > 0) {
1682
+ return `${summary} [Tools: ${step.toolsUsed.join(", ")}]`;
1683
+ }
1684
+ return summary;
1685
+ }
1686
+ /**
1687
+ * Extract input pattern from user input
1688
+ */
1689
+ extractInputPattern(input) {
1690
+ return input.substring(0, 50).trim();
1691
+ }
1692
+ /**
1693
+ * Extract output pattern from assistant output
1694
+ */
1695
+ extractOutputPattern(output) {
1696
+ return output.substring(0, 50).trim();
1697
+ }
1698
+ /**
1699
+ * Generate a skill name from task description
1700
+ */
1701
+ generateSkillName(description) {
1702
+ const name = description.substring(0, 30).trim();
1703
+ return name.replace(/[^a-zA-Z0-9가-힣\s]/g, "").trim() || "Unnamed Skill";
1704
+ }
1705
+ /**
1706
+ * Get a skill by ID
1707
+ */
1708
+ getSkill(skillId) {
1709
+ return this.skills.get(skillId);
1710
+ }
1711
+ /**
1712
+ * Get all skills
1713
+ */
1714
+ getAllSkills() {
1715
+ return Array.from(this.skills.values());
1716
+ }
1717
+ /**
1718
+ * Search skills by category
1719
+ */
1720
+ getSkillsByCategory(category) {
1721
+ return this.getAllSkills().filter((s) => s.category === category);
1722
+ }
1723
+ /**
1724
+ * Search skills by keyword
1725
+ */
1726
+ searchSkills(keyword) {
1727
+ const lowerKeyword = keyword.toLowerCase();
1728
+ return this.getAllSkills().filter(
1729
+ (s) => s.name.toLowerCase().includes(lowerKeyword) || s.description.toLowerCase().includes(lowerKeyword) || s.category.toLowerCase().includes(lowerKeyword)
1730
+ );
1731
+ }
1732
+ /**
1733
+ * Update skill success rate
1734
+ */
1735
+ updateSuccessRate(skillId, success) {
1736
+ const skill = this.skills.get(skillId);
1737
+ if (!skill) return;
1738
+ skill.usageCount += 1;
1739
+ skill.successRate = (skill.successRate * (skill.usageCount - 1) + (success ? 1 : 0)) / skill.usageCount;
1740
+ skill.updatedAt = Date.now();
1741
+ this.skills.set(skillId, skill);
1742
+ this.save();
1743
+ }
1744
+ /**
1745
+ * Delete a skill
1746
+ */
1747
+ deleteSkill(skillId) {
1748
+ const deleted = this.skills.delete(skillId);
1749
+ if (deleted) {
1750
+ this.save();
1751
+ }
1752
+ return deleted;
1753
+ }
1754
+ /**
1755
+ * Clear all skills
1756
+ */
1757
+ clearSkills() {
1758
+ this.skills.clear();
1759
+ this.save();
1760
+ }
1761
+ };
1762
+ var learnerInstance = null;
1763
+ function initLearner(storagePath) {
1764
+ learnerInstance = new SkillLearner(storagePath ?? DEFAULT_STORAGE_PATH);
1765
+ }
1766
+ function getLearner() {
1767
+ if (!learnerInstance) {
1768
+ learnerInstance = new SkillLearner();
1769
+ }
1770
+ return learnerInstance;
1771
+ }
1772
+ function learnFromConversation(chat, taskDescription, outcome = "success") {
1773
+ const steps = [];
1774
+ let currentUserInput = "";
1775
+ for (const msg of chat.messages) {
1776
+ if (msg.role === "user") {
1777
+ currentUserInput = msg.content;
1778
+ } else if (msg.role === "assistant" && currentUserInput) {
1779
+ steps.push({
1780
+ userInput: currentUserInput,
1781
+ assistantOutput: msg.content,
1782
+ toolsUsed: msg.toolCalls?.map((tc) => tc.name)
1783
+ });
1784
+ currentUserInput = "";
1785
+ }
1786
+ }
1787
+ return getLearner().learn({
1788
+ taskDescription,
1789
+ conversationSummary: `Conversation with ${chat.messages.length} messages`,
1790
+ outcome,
1791
+ steps
1792
+ });
1793
+ }
1794
+ function getLearnedSkills(category) {
1795
+ if (category) {
1796
+ return getLearner().getSkillsByCategory(category);
1797
+ }
1798
+ return getLearner().getAllSkills();
1799
+ }
1800
+ function searchLearnedSkills(keyword) {
1801
+ return getLearner().searchSkills(keyword);
1802
+ }
1803
+ function getLearnedSkill(skillId) {
1804
+ return getLearner().getSkill(skillId);
1805
+ }
1806
+ function recordSkillUsage(skillId, success) {
1807
+ getLearner().updateSuccessRate(skillId, success);
1808
+ }
1809
+ function deleteLearnedSkill(skillId) {
1810
+ return getLearner().deleteSkill(skillId);
1811
+ }
1812
+ function clearLearnedSkills() {
1813
+ getLearner().clearSkills();
1814
+ }
1815
+
1816
+ // src/acontext/skills.ts
1817
+ function findRelevantSkills(query, options = {}) {
1818
+ const { category, minSuccessRate = 0, limit = 10 } = options;
1819
+ let skills = getLearnedSkills(category);
1820
+ if (minSuccessRate > 0) {
1821
+ skills = skills.filter((s) => s.successRate >= minSuccessRate);
1822
+ }
1823
+ const scoredSkills = skills.map((skill) => ({
1824
+ skill,
1825
+ score: calculateRelevanceScore(query, skill)
1826
+ }));
1827
+ scoredSkills.sort((a, b) => b.score - a.score);
1828
+ return scoredSkills.filter((s) => s.score > 0).slice(0, limit).map((s) => s.skill);
1829
+ }
1830
+ function calculateRelevanceScore(query, skill) {
1831
+ const lowerQuery = query.toLowerCase();
1832
+ const words = lowerQuery.split(/\s+/).filter((w) => w.length > 1);
1833
+ let score = 0;
1834
+ const lowerName = skill.name.toLowerCase();
1835
+ for (const word of words) {
1836
+ if (lowerName.includes(word)) {
1837
+ score += 3;
1838
+ }
1839
+ }
1840
+ const lowerDesc = skill.description.toLowerCase();
1841
+ for (const word of words) {
1842
+ if (lowerDesc.includes(word)) {
1843
+ score += 2;
1844
+ }
1845
+ }
1846
+ if (lowerQuery.includes(skill.category)) {
1847
+ score += 2;
1848
+ }
1849
+ score *= 0.5 + skill.successRate * 0.5;
1850
+ score += Math.min(skill.usageCount * 0.1, 1);
1851
+ return score;
1852
+ }
1853
+ function applySkill(_chat, skill) {
1854
+ if (!skill || !skill.steps || skill.steps.length === 0) {
1855
+ return {
1856
+ applied: false,
1857
+ skillId: skill?.id || "unknown",
1858
+ error: "Invalid skill or no steps available"
1859
+ };
1860
+ }
1861
+ try {
1862
+ const suggestedPrompt = generateSkillPrompt(skill);
1863
+ return {
1864
+ applied: true,
1865
+ skillId: skill.id,
1866
+ suggestedPrompt
1867
+ };
1868
+ } catch (error) {
1869
+ return {
1870
+ applied: false,
1871
+ skillId: skill.id,
1872
+ error: error instanceof Error ? error.message : "Unknown error"
1873
+ };
1874
+ }
1875
+ }
1876
+ function generateSkillPrompt(skill) {
1877
+ const lines = [
1878
+ `[Skill: ${skill.name}]`,
1879
+ "",
1880
+ "Follow these steps:"
1881
+ ];
1882
+ for (const step of skill.steps) {
1883
+ lines.push(`${step.order}. ${step.action}`);
1884
+ if (step.expectedInput) {
1885
+ lines.push(` Input pattern: ${step.expectedInput}`);
1886
+ }
1887
+ }
1888
+ return lines.join("\n");
1889
+ }
1890
+ function applySkillToSystemPrompt(chat, skill, baseSystemPrompt) {
1891
+ const result = applySkill(chat, skill);
1892
+ if (!result.applied || !result.suggestedPrompt) {
1893
+ return baseSystemPrompt;
1894
+ }
1895
+ return `${baseSystemPrompt}
1896
+
1897
+ ---
1898
+ ${result.suggestedPrompt}
1899
+ ---`;
1900
+ }
1901
+ function getBestSkill(query, options = {}) {
1902
+ const skills = findRelevantSkills(query, { ...options, limit: 1 });
1903
+ return skills[0];
1904
+ }
1905
+ function suggestSkills(chat, limit = 3) {
1906
+ const recentMessages = chat.messages.slice(-5);
1907
+ const context = recentMessages.map((m) => m.content).join(" ");
1908
+ return findRelevantSkills(context, { limit, minSuccessRate: 0.3 });
1909
+ }
1910
+ function recordSkillApplication(skillId, success) {
1911
+ recordSkillUsage(skillId, success);
1912
+ }
1913
+ var skillApplicationHistory = /* @__PURE__ */ new Map();
1914
+ function trackSkillApplication(chatId, skillId) {
1915
+ const history = skillApplicationHistory.get(chatId) || [];
1916
+ history.push({
1917
+ skillId,
1918
+ appliedAt: Date.now()
1919
+ });
1920
+ skillApplicationHistory.set(chatId, history);
1921
+ }
1922
+ function updateSkillApplicationSuccess(chatId, skillId, success) {
1923
+ const history = skillApplicationHistory.get(chatId) || [];
1924
+ let application;
1925
+ for (let i = history.length - 1; i >= 0; i--) {
1926
+ if (history[i].skillId === skillId) {
1927
+ application = history[i];
1928
+ break;
1929
+ }
1930
+ }
1931
+ if (application) {
1932
+ application.success = success;
1933
+ recordSkillApplication(skillId, success);
1934
+ }
1935
+ }
1936
+ function getSkillApplicationHistory(chatId) {
1937
+ return [...skillApplicationHistory.get(chatId) || []];
1938
+ }
1939
+ function clearSkillApplicationHistory(chatId) {
1940
+ skillApplicationHistory.delete(chatId);
1941
+ }
1942
+ export {
1943
+ BaseProvider,
1944
+ ChatLLMError,
1945
+ ConfigurationError,
1946
+ DEFAULT_MODELS,
1947
+ DEFAULT_OLLAMA_BASE_URL,
1948
+ DEFAULT_PERSONALIZATION,
1949
+ DEFAULT_STORAGE_PATH,
1950
+ FileStorage,
1951
+ MemoryStorage,
1952
+ OllamaProvider,
1953
+ ProviderError,
1954
+ ProviderRegistry,
1955
+ StorageError,
1956
+ StreamError,
1957
+ ToolExecutionError,
1958
+ ValidationError,
1959
+ addMessage as addSessionMessage,
1960
+ addMessages as addSessionMessages,
1961
+ analyzeMessages,
1962
+ applySkill,
1963
+ applySkillToSystemPrompt,
1964
+ buildSystemPromptWithMemory,
1965
+ clearChatMemory,
1966
+ clearGlobalMemory,
1967
+ clearLearnedSkills,
1968
+ clearMessages2 as clearMessages,
1969
+ clearObserverState,
1970
+ clearMessages as clearSessionMessages,
1971
+ clearSkillApplicationHistory,
1972
+ cloneMessage,
1973
+ collectStreamContent,
1974
+ createAssistantMessage,
1975
+ createChat,
1976
+ createMessage,
1977
+ createSession,
1978
+ createStreamEvent,
1979
+ createSystemMessage,
1980
+ createToolMessage,
1981
+ createUserMessage,
1982
+ deleteChatMemory,
1983
+ deleteGlobalMemory,
1984
+ deleteLearnedSkill,
1985
+ deserializeSession,
1986
+ destroyChat,
1987
+ disableObserver,
1988
+ enableObserver,
1989
+ filterByProvider,
1990
+ filterByRole,
1991
+ findRelevantSkills,
1992
+ formatMessagesForDisplay,
1993
+ getBestSkill,
1994
+ getChatMemory,
1995
+ getChatMemoryContext,
1996
+ getChatMemoryKeys,
1997
+ getDefaultModel,
1998
+ getGlobalMemory,
1999
+ getGlobalMemoryContext,
2000
+ getGlobalMemoryKeys,
2001
+ getGlobalMemoryStore,
2002
+ getLastMessageByRole,
2003
+ getLastMessages,
2004
+ getLearnedSkill,
2005
+ getLearnedSkills,
2006
+ getMemoryContext,
2007
+ getMessages,
2008
+ getMessagesAfter,
2009
+ getMessagesForProvider,
2010
+ getObservedPreferences,
2011
+ getObservedTasks,
2012
+ getObserverConfig,
2013
+ getObserverState,
2014
+ getProviderBaseUrl,
2015
+ getSession,
2016
+ getSessionSummary,
2017
+ getSkillApplicationHistory,
2018
+ getTools,
2019
+ getTotalContentLength,
2020
+ hasChatMemory,
2021
+ hasGlobalMemory,
2022
+ initChatMemory,
2023
+ initGlobalMemory,
2024
+ initLearner,
2025
+ initMemory,
2026
+ isObserverEnabled,
2027
+ isProviderError,
2028
+ isStorageError,
2029
+ isToolExecutionError,
2030
+ learnFromConversation,
2031
+ mergeConfig,
2032
+ onObserverEvent,
2033
+ parseNDJSONStream,
2034
+ parseSSEStream,
2035
+ processMessages,
2036
+ recordSkillApplication,
2037
+ recordSkillUsage,
2038
+ registerTool,
2039
+ resetChat,
2040
+ searchLearnedSkills,
2041
+ sendMessage,
2042
+ sendMessageStream,
2043
+ serializeSession,
2044
+ setChatMemory,
2045
+ setGlobalMemory,
2046
+ setSessionMetadata,
2047
+ setSystemPrompt as setSessionSystemPrompt,
2048
+ setSystemPrompt2 as setSystemPrompt,
2049
+ suggestSkills,
2050
+ syncChatMemory,
2051
+ trackSkillApplication,
2052
+ truncateMessages,
2053
+ unregisterTool,
2054
+ updateSkillApplicationSuccess,
2055
+ updateTaskStatus,
2056
+ validateProviderConfig,
2057
+ wrapProviderError
2058
+ };