@avee1234/agent-kit 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.js ADDED
@@ -0,0 +1,619 @@
1
+ // src/model/mock.ts
2
+ var MockAdapter = class {
3
+ async chat(messages, tools) {
4
+ const lastUserMsg = [...messages].reverse().find((m) => m.role === "user");
5
+ const content = lastUserMsg?.content ?? "";
6
+ const systemMsg = messages.find((m) => m.role === "system");
7
+ if (systemMsg?.content.toLowerCase().includes("summarize")) {
8
+ const conversationContent = messages.filter((m) => m.role !== "system").map((m) => m.content).join(" ");
9
+ return { content: `[Mock Summary] ${conversationContent.slice(0, 200)}` };
10
+ }
11
+ if (tools?.length) {
12
+ for (const tool of tools) {
13
+ const toolNameWords = tool.name.replace(/_/g, " ").split(" ");
14
+ const matches = toolNameWords.some(
15
+ (word) => content.toLowerCase().includes(word.toLowerCase())
16
+ );
17
+ if (matches) {
18
+ return {
19
+ content: "",
20
+ toolCalls: [
21
+ {
22
+ id: `mock-tc-${Date.now()}`,
23
+ name: tool.name,
24
+ arguments: JSON.stringify(
25
+ Object.fromEntries(Object.keys(tool.parameters).map((key) => [key, content]))
26
+ )
27
+ }
28
+ ]
29
+ };
30
+ }
31
+ }
32
+ }
33
+ return {
34
+ content: `[Mock] Received: "${content}". Tools available: ${tools?.map((t) => t.name).join(", ") ?? "none"}.`
35
+ };
36
+ }
37
+ async *stream(messages, tools) {
38
+ const response = await this.chat(messages, tools);
39
+ const words = response.content.split(" ");
40
+ for (let i = 0; i < words.length; i++) {
41
+ yield {
42
+ text: (i > 0 ? " " : "") + words[i],
43
+ done: i === words.length - 1,
44
+ ...i === words.length - 1 && response.toolCalls ? { toolCalls: response.toolCalls } : {}
45
+ };
46
+ }
47
+ }
48
+ };
49
+
50
+ // src/model/openai-compatible.ts
51
+ var OpenAICompatibleAdapter = class {
52
+ config;
53
+ constructor(config) {
54
+ this.config = config;
55
+ }
56
+ async chat(messages, tools) {
57
+ const body = {
58
+ model: this.config.model,
59
+ messages: messages.map((m) => this.toOpenAIMessage(m))
60
+ };
61
+ if (tools?.length) {
62
+ body.tools = tools.map((t) => ({
63
+ type: "function",
64
+ function: {
65
+ name: t.name,
66
+ description: t.description,
67
+ parameters: { type: "object", properties: t.parameters }
68
+ }
69
+ }));
70
+ }
71
+ const headers = { "Content-Type": "application/json" };
72
+ if (this.config.apiKey) headers["Authorization"] = `Bearer ${this.config.apiKey}`;
73
+ const response = await fetch(`${this.config.baseURL}/chat/completions`, {
74
+ method: "POST",
75
+ headers,
76
+ body: JSON.stringify(body)
77
+ });
78
+ if (!response.ok) {
79
+ const errorText = await response.text();
80
+ throw new Error(`Model API error (${response.status}): ${errorText}`);
81
+ }
82
+ const data = await response.json();
83
+ const choice = data.choices[0].message;
84
+ const result = { content: choice.content ?? "" };
85
+ if (choice.tool_calls?.length) {
86
+ result.toolCalls = choice.tool_calls.map(
87
+ (tc) => ({
88
+ id: tc.id,
89
+ name: tc.function.name,
90
+ arguments: tc.function.arguments
91
+ })
92
+ );
93
+ }
94
+ if (data.usage) {
95
+ result.tokens = { input: data.usage.prompt_tokens, output: data.usage.completion_tokens };
96
+ }
97
+ return result;
98
+ }
99
+ async *stream(messages, tools) {
100
+ const headers = { "Content-Type": "application/json" };
101
+ if (this.config.apiKey) headers["Authorization"] = `Bearer ${this.config.apiKey}`;
102
+ const body = {
103
+ model: this.config.model,
104
+ messages: messages.map((m) => this.toOpenAIMessage(m)),
105
+ stream: true
106
+ };
107
+ if (tools?.length) {
108
+ body.tools = tools.map((t) => ({
109
+ type: "function",
110
+ function: {
111
+ name: t.name,
112
+ description: t.description,
113
+ parameters: { type: "object", properties: t.parameters }
114
+ }
115
+ }));
116
+ }
117
+ const response = await fetch(`${this.config.baseURL}/chat/completions`, {
118
+ method: "POST",
119
+ headers,
120
+ body: JSON.stringify(body)
121
+ });
122
+ if (!response.ok) {
123
+ const errorText = await response.text();
124
+ throw new Error(`Model API error (${response.status}): ${errorText}`);
125
+ }
126
+ const reader = response.body?.getReader();
127
+ if (!reader) throw new Error("No response body");
128
+ const decoder = new TextDecoder();
129
+ let buffer = "";
130
+ while (true) {
131
+ const { done, value } = await reader.read();
132
+ if (done) break;
133
+ buffer += decoder.decode(value, { stream: true });
134
+ const lines = buffer.split("\n");
135
+ buffer = lines.pop() ?? "";
136
+ for (const line of lines) {
137
+ if (!line.startsWith("data: ")) continue;
138
+ const data = line.slice(6).trim();
139
+ if (data === "[DONE]") {
140
+ yield { text: "", done: true };
141
+ return;
142
+ }
143
+ const parsed = JSON.parse(data);
144
+ const delta = parsed.choices?.[0]?.delta;
145
+ if (delta?.content) yield { text: delta.content, done: false };
146
+ }
147
+ }
148
+ }
149
+ toOpenAIMessage(msg) {
150
+ const result = { role: msg.role, content: msg.content };
151
+ if (msg.toolCalls) {
152
+ result.tool_calls = msg.toolCalls.map((tc) => ({
153
+ id: tc.id,
154
+ type: "function",
155
+ function: { name: tc.name, arguments: tc.arguments }
156
+ }));
157
+ }
158
+ if (msg.toolCallId) result.tool_call_id = msg.toolCallId;
159
+ return result;
160
+ }
161
+ };
162
+
163
+ // src/events.ts
164
+ var AgentEventEmitter = class {
165
+ listeners = /* @__PURE__ */ new Map();
166
+ on(type, handler) {
167
+ if (!this.listeners.has(type)) {
168
+ this.listeners.set(type, /* @__PURE__ */ new Set());
169
+ }
170
+ this.listeners.get(type).add(handler);
171
+ }
172
+ off(type, handler) {
173
+ this.listeners.get(type)?.delete(handler);
174
+ }
175
+ emit(event) {
176
+ this.listeners.get(event.type)?.forEach((handler) => handler(event));
177
+ if (event.type !== "*") {
178
+ this.listeners.get("*")?.forEach((handler) => handler(event));
179
+ }
180
+ }
181
+ };
182
+
183
+ // src/types.ts
184
+ function createMessage(opts) {
185
+ return {
186
+ ...opts,
187
+ timestamp: opts.timestamp ?? Date.now()
188
+ };
189
+ }
190
+ function isToolCallMessage(msg) {
191
+ return Array.isArray(msg.toolCalls) && msg.toolCalls.length > 0;
192
+ }
193
+ function createSummary(opts) {
194
+ return {
195
+ id: opts.id ?? crypto.randomUUID(),
196
+ content: opts.content,
197
+ timestamp: opts.timestamp ?? Date.now(),
198
+ messageRange: opts.messageRange ?? { from: 0, to: 0 }
199
+ };
200
+ }
201
+
202
+ // src/agent.ts
203
+ var Agent = class {
204
+ name;
205
+ model;
206
+ memory;
207
+ tools;
208
+ toolMap;
209
+ system;
210
+ maxToolRounds;
211
+ emitter;
212
+ constructor(config) {
213
+ this.name = config.name;
214
+ this.memory = config.memory;
215
+ this.tools = config.tools ?? [];
216
+ this.system = config.system;
217
+ this.maxToolRounds = config.maxToolRounds ?? 10;
218
+ this.emitter = new AgentEventEmitter();
219
+ this.toolMap = new Map(this.tools.map((t) => [t.name, t]));
220
+ if (!config.model) {
221
+ this.model = new MockAdapter();
222
+ } else if (this.isModelAdapter(config.model)) {
223
+ this.model = config.model;
224
+ } else {
225
+ const cfg = config.model;
226
+ const baseURL = cfg.provider === "ollama" ? cfg.baseURL ?? "http://localhost:11434/v1" : cfg.baseURL ?? "http://localhost:11434/v1";
227
+ this.model = new OpenAICompatibleAdapter({
228
+ baseURL,
229
+ model: cfg.model,
230
+ apiKey: cfg.apiKey
231
+ });
232
+ }
233
+ if (this.memory) {
234
+ this.memory.setModel(this.model);
235
+ }
236
+ }
237
+ isModelAdapter(obj) {
238
+ return typeof obj.chat === "function";
239
+ }
240
+ on(type, handler) {
241
+ this.emitter.on(type, handler);
242
+ }
243
+ off(type, handler) {
244
+ this.emitter.off(type, handler);
245
+ }
246
+ emit(type, data, extras) {
247
+ this.emitter.emit({
248
+ type,
249
+ timestamp: Date.now(),
250
+ agentId: this.name,
251
+ data,
252
+ ...extras
253
+ });
254
+ }
255
+ async chat(input) {
256
+ const toolDefs = this.tools.map((t) => t.definition);
257
+ const messages = [];
258
+ if (this.system) {
259
+ messages.push(createMessage({ role: "system", content: this.system }));
260
+ }
261
+ if (this.memory) {
262
+ const ctx = await this.memory.getContext(this.name, input);
263
+ this.emit("memory:retrieve", {
264
+ summaries: ctx.relevantSummaries.length,
265
+ recentMessages: ctx.recentMessages.length
266
+ });
267
+ for (const summary of ctx.relevantSummaries) {
268
+ messages.push(
269
+ createMessage({
270
+ role: "system",
271
+ content: `[Previous conversation summary]: ${summary.content}`,
272
+ timestamp: summary.timestamp
273
+ })
274
+ );
275
+ }
276
+ messages.push(...ctx.recentMessages);
277
+ }
278
+ const userMessage = createMessage({ role: "user", content: input });
279
+ messages.push(userMessage);
280
+ this.emit("message", { role: "user", content: input });
281
+ let response = { content: "" };
282
+ let toolRoundsUsed = 0;
283
+ response = await this.model.chat(messages, toolDefs.length ? toolDefs : void 0);
284
+ while (isToolCallMessage({ ...response, role: "assistant", timestamp: Date.now() }) && toolRoundsUsed < this.maxToolRounds) {
285
+ const assistantMsg = createMessage({
286
+ role: "assistant",
287
+ content: response.content,
288
+ toolCalls: response.toolCalls
289
+ });
290
+ messages.push(assistantMsg);
291
+ for (const toolCall of response.toolCalls ?? []) {
292
+ const tool = this.toolMap.get(toolCall.name);
293
+ const startTime = Date.now();
294
+ this.emit("tool:start", {
295
+ toolName: toolCall.name,
296
+ toolCallId: toolCall.id,
297
+ arguments: toolCall.arguments
298
+ });
299
+ let toolResult;
300
+ if (!tool) {
301
+ toolResult = `Error: tool "${toolCall.name}" not found`;
302
+ this.emit("error", {
303
+ message: `Tool not found: ${toolCall.name}`,
304
+ toolCallId: toolCall.id
305
+ });
306
+ } else {
307
+ try {
308
+ let parsedArgs = {};
309
+ try {
310
+ parsedArgs = JSON.parse(toolCall.arguments);
311
+ } catch {
312
+ parsedArgs = {};
313
+ }
314
+ const result = await tool.execute(parsedArgs);
315
+ toolResult = typeof result === "string" ? result : JSON.stringify(result);
316
+ } catch (err) {
317
+ const error = err;
318
+ toolResult = `Error: ${error.message}`;
319
+ this.emit("error", {
320
+ message: error.message,
321
+ toolCallId: toolCall.id,
322
+ toolName: toolCall.name
323
+ });
324
+ }
325
+ }
326
+ const latencyMs = Date.now() - startTime;
327
+ this.emit("tool:end", {
328
+ toolName: toolCall.name,
329
+ toolCallId: toolCall.id,
330
+ result: toolResult,
331
+ latencyMs
332
+ });
333
+ messages.push(
334
+ createMessage({
335
+ role: "tool",
336
+ content: toolResult,
337
+ toolCallId: toolCall.id
338
+ })
339
+ );
340
+ }
341
+ toolRoundsUsed++;
342
+ response = await this.model.chat(messages, toolDefs.length ? toolDefs : void 0);
343
+ }
344
+ this.emit("message", { role: "assistant", content: response.content });
345
+ if (this.memory) {
346
+ const exchangeMessages = [
347
+ userMessage,
348
+ createMessage({
349
+ role: "assistant",
350
+ content: response.content,
351
+ toolCalls: response.toolCalls
352
+ })
353
+ ];
354
+ await this.memory.saveExchange(this.name, exchangeMessages);
355
+ }
356
+ return response;
357
+ }
358
+ async *stream(input) {
359
+ const toolDefs = this.tools.map((t) => t.definition);
360
+ const messages = [];
361
+ if (this.system) {
362
+ messages.push(createMessage({ role: "system", content: this.system }));
363
+ }
364
+ if (this.memory) {
365
+ const ctx = await this.memory.getContext(this.name, input);
366
+ for (const summary of ctx.relevantSummaries) {
367
+ messages.push(
368
+ createMessage({
369
+ role: "system",
370
+ content: `[Previous conversation summary]: ${summary.content}`,
371
+ timestamp: summary.timestamp
372
+ })
373
+ );
374
+ }
375
+ messages.push(...ctx.recentMessages);
376
+ }
377
+ const userMessage = createMessage({ role: "user", content: input });
378
+ messages.push(userMessage);
379
+ this.emit("message", { role: "user", content: input });
380
+ let fullText = "";
381
+ for await (const chunk of this.model.stream(messages, toolDefs.length ? toolDefs : void 0)) {
382
+ fullText += chunk.text;
383
+ yield chunk;
384
+ }
385
+ this.emit("message", { role: "assistant", content: fullText });
386
+ if (this.memory) {
387
+ await this.memory.saveExchange(this.name, [
388
+ userMessage,
389
+ createMessage({ role: "assistant", content: fullText })
390
+ ]);
391
+ }
392
+ }
393
+ };
394
+
395
+ // src/tool.ts
396
+ var Tool = class _Tool {
397
+ name;
398
+ description;
399
+ definition;
400
+ executeFn;
401
+ constructor(config) {
402
+ this.name = config.name;
403
+ this.description = config.description;
404
+ this.definition = {
405
+ name: config.name,
406
+ description: config.description,
407
+ parameters: config.parameters
408
+ };
409
+ this.executeFn = config.execute;
410
+ }
411
+ static create(config) {
412
+ return new _Tool(config);
413
+ }
414
+ async execute(params) {
415
+ return this.executeFn(params);
416
+ }
417
+ };
418
+
419
+ // src/store/in-memory.ts
420
+ var InMemoryStore = class {
421
+ messages = /* @__PURE__ */ new Map();
422
+ summaries = /* @__PURE__ */ new Map();
423
+ async saveMessages(agentId, messages) {
424
+ const existing = this.messages.get(agentId) ?? [];
425
+ existing.push(...messages);
426
+ this.messages.set(agentId, existing);
427
+ }
428
+ async getRecentMessages(agentId, limit) {
429
+ const all = this.messages.get(agentId) ?? [];
430
+ return all.slice(-limit);
431
+ }
432
+ async saveSummary(agentId, summary) {
433
+ const existing = this.summaries.get(agentId) ?? [];
434
+ existing.push(summary);
435
+ this.summaries.set(agentId, existing);
436
+ }
437
+ async searchSummaries(agentId, query, limit) {
438
+ const all = this.summaries.get(agentId) ?? [];
439
+ const queryLower = query.toLowerCase();
440
+ const matches = all.filter((s) => s.content.toLowerCase().includes(queryLower)).sort((a, b) => b.timestamp - a.timestamp).slice(0, limit);
441
+ return matches;
442
+ }
443
+ };
444
+
445
+ // src/store/sqlite.ts
446
+ import { createRequire } from "module";
447
+ var require2 = createRequire(import.meta.url);
448
+ var SQLiteStore = class {
449
+ db;
450
+ constructor(path) {
451
+ const Database = require2("better-sqlite3");
452
+ this.db = new Database(path);
453
+ this.db.pragma("journal_mode = WAL");
454
+ this.init();
455
+ }
456
+ init() {
457
+ this.db.exec(`
458
+ CREATE TABLE IF NOT EXISTS messages (
459
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
460
+ agent_id TEXT NOT NULL,
461
+ role TEXT NOT NULL,
462
+ content TEXT NOT NULL,
463
+ timestamp INTEGER NOT NULL,
464
+ tool_calls TEXT,
465
+ tool_call_id TEXT
466
+ );
467
+ CREATE TABLE IF NOT EXISTS summaries (
468
+ id TEXT PRIMARY KEY,
469
+ agent_id TEXT NOT NULL,
470
+ content TEXT NOT NULL,
471
+ timestamp INTEGER NOT NULL,
472
+ message_range_from INTEGER NOT NULL,
473
+ message_range_to INTEGER NOT NULL
474
+ );
475
+ CREATE INDEX IF NOT EXISTS idx_messages_agent ON messages(agent_id);
476
+ CREATE INDEX IF NOT EXISTS idx_summaries_agent ON summaries(agent_id);
477
+ `);
478
+ }
479
+ async saveMessages(agentId, messages) {
480
+ const stmt = this.db.prepare(
481
+ "INSERT INTO messages (agent_id, role, content, timestamp, tool_calls, tool_call_id) VALUES (?, ?, ?, ?, ?, ?)"
482
+ );
483
+ const insertMany = this.db.transaction((msgs) => {
484
+ for (const msg of msgs) {
485
+ stmt.run(
486
+ agentId,
487
+ msg.role,
488
+ msg.content,
489
+ msg.timestamp,
490
+ msg.toolCalls ? JSON.stringify(msg.toolCalls) : null,
491
+ msg.toolCallId ?? null
492
+ );
493
+ }
494
+ });
495
+ insertMany(messages);
496
+ }
497
+ async getRecentMessages(agentId, limit) {
498
+ const rows = this.db.prepare(
499
+ `SELECT role, content, timestamp, tool_calls, tool_call_id
500
+ FROM messages WHERE agent_id = ?
501
+ ORDER BY id DESC LIMIT ?`
502
+ ).all(agentId, limit);
503
+ return rows.reverse().map((row) => ({
504
+ role: row.role,
505
+ content: row.content,
506
+ timestamp: row.timestamp,
507
+ ...row.tool_calls ? { toolCalls: JSON.parse(row.tool_calls) } : {},
508
+ ...row.tool_call_id ? { toolCallId: row.tool_call_id } : {}
509
+ }));
510
+ }
511
+ async saveSummary(agentId, summary) {
512
+ this.db.prepare(
513
+ "INSERT INTO summaries (id, agent_id, content, timestamp, message_range_from, message_range_to) VALUES (?, ?, ?, ?, ?, ?)"
514
+ ).run(
515
+ summary.id,
516
+ agentId,
517
+ summary.content,
518
+ summary.timestamp,
519
+ summary.messageRange.from,
520
+ summary.messageRange.to
521
+ );
522
+ }
523
+ async searchSummaries(agentId, query, limit) {
524
+ const rows = this.db.prepare(
525
+ `SELECT id, content, timestamp, message_range_from, message_range_to
526
+ FROM summaries WHERE agent_id = ? AND content LIKE ?
527
+ ORDER BY timestamp DESC LIMIT ?`
528
+ ).all(agentId, `%${query}%`, limit);
529
+ return rows.map((row) => ({
530
+ id: row.id,
531
+ content: row.content,
532
+ timestamp: row.timestamp,
533
+ messageRange: { from: row.message_range_from, to: row.message_range_to }
534
+ }));
535
+ }
536
+ close() {
537
+ this.db.close();
538
+ }
539
+ };
540
+
541
+ // src/memory.ts
542
+ var Memory = class {
543
+ store;
544
+ windowSize;
545
+ summarizeAfter;
546
+ model;
547
+ messageCount = /* @__PURE__ */ new Map();
548
+ constructor(config = {}) {
549
+ this.windowSize = config.windowSize ?? 20;
550
+ this.summarizeAfter = config.summarizeAfter ?? 20;
551
+ if (!config.store || config.store === "memory") {
552
+ this.store = new InMemoryStore();
553
+ } else if (config.store === "sqlite") {
554
+ this.store = new SQLiteStore(config.path ?? "./agent-memory.db");
555
+ } else {
556
+ this.store = config.store;
557
+ }
558
+ }
559
+ setModel(model) {
560
+ this.model = model;
561
+ }
562
+ getStore() {
563
+ return this.store;
564
+ }
565
+ async saveExchange(agentId, messages) {
566
+ await this.store.saveMessages(agentId, messages);
567
+ const count = (this.messageCount.get(agentId) ?? 0) + messages.length;
568
+ this.messageCount.set(agentId, count);
569
+ if (count >= this.summarizeAfter && this.model) {
570
+ await this.summarize(agentId);
571
+ this.messageCount.set(agentId, 0);
572
+ }
573
+ }
574
+ async getContext(agentId, query) {
575
+ const recentMessages = await this.store.getRecentMessages(agentId, this.windowSize);
576
+ const relevantSummaries = await this.store.searchSummaries(agentId, query, 3);
577
+ return { recentMessages, relevantSummaries };
578
+ }
579
+ async summarize(agentId) {
580
+ if (!this.model) return;
581
+ const allRecent = await this.store.getRecentMessages(agentId, this.summarizeAfter);
582
+ if (allRecent.length === 0) return;
583
+ const summaryResponse = await this.model.chat([
584
+ {
585
+ role: "system",
586
+ content: "Summarize the following conversation concisely. Focus on key topics, decisions, and facts discussed.",
587
+ timestamp: Date.now()
588
+ },
589
+ ...allRecent
590
+ ]);
591
+ const summary = createSummary({
592
+ content: summaryResponse.content,
593
+ messageRange: {
594
+ from: allRecent[0].timestamp,
595
+ to: allRecent[allRecent.length - 1].timestamp
596
+ }
597
+ });
598
+ await this.store.saveSummary(agentId, summary);
599
+ }
600
+ close() {
601
+ if ("close" in this.store && typeof this.store.close === "function") {
602
+ this.store.close();
603
+ }
604
+ }
605
+ };
606
+ export {
607
+ Agent,
608
+ AgentEventEmitter,
609
+ InMemoryStore,
610
+ Memory,
611
+ MockAdapter,
612
+ OpenAICompatibleAdapter,
613
+ SQLiteStore,
614
+ Tool,
615
+ createMessage,
616
+ createSummary,
617
+ isToolCallMessage
618
+ };
619
+ //# sourceMappingURL=index.js.map