@shareai-lab/kode-sdk 2.7.1 → 2.7.2

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 (97) hide show
  1. package/dist/core/agent/breakpoint-manager.js +36 -0
  2. package/dist/core/agent/message-queue.js +57 -0
  3. package/dist/core/agent/permission-manager.js +32 -0
  4. package/dist/core/agent/todo-manager.js +91 -0
  5. package/dist/core/agent/tool-runner.js +45 -0
  6. package/dist/core/agent.js +2035 -0
  7. package/dist/core/config.js +2 -0
  8. package/dist/core/context-manager.js +241 -0
  9. package/dist/core/errors.js +49 -0
  10. package/dist/core/events.js +329 -0
  11. package/dist/core/file-pool.d.ts +2 -0
  12. package/dist/core/file-pool.js +125 -0
  13. package/dist/core/hooks.js +71 -0
  14. package/dist/core/permission-modes.js +61 -0
  15. package/dist/core/pool.js +301 -0
  16. package/dist/core/room.js +57 -0
  17. package/dist/core/scheduler.js +58 -0
  18. package/dist/core/skills/index.js +20 -0
  19. package/dist/core/skills/management-manager.js +557 -0
  20. package/dist/core/skills/manager.js +243 -0
  21. package/dist/core/skills/operation-queue.js +113 -0
  22. package/dist/core/skills/sandbox-file-manager.js +183 -0
  23. package/dist/core/skills/types.js +9 -0
  24. package/dist/core/skills/xml-generator.js +70 -0
  25. package/dist/core/template.js +35 -0
  26. package/dist/core/time-bridge.js +100 -0
  27. package/dist/core/todo.js +89 -0
  28. package/dist/core/types.js +3 -0
  29. package/dist/index.js +148 -60461
  30. package/dist/infra/db/postgres/postgres-store.js +1073 -0
  31. package/dist/infra/db/sqlite/sqlite-store.js +800 -0
  32. package/dist/infra/e2b/e2b-fs.js +128 -0
  33. package/dist/infra/e2b/e2b-sandbox.js +156 -0
  34. package/dist/infra/e2b/e2b-template.js +105 -0
  35. package/dist/infra/e2b/index.js +9 -0
  36. package/dist/infra/e2b/types.js +2 -0
  37. package/dist/infra/provider.js +67 -0
  38. package/dist/infra/providers/anthropic.js +308 -0
  39. package/dist/infra/providers/core/errors.js +353 -0
  40. package/dist/infra/providers/core/fork.js +418 -0
  41. package/dist/infra/providers/core/index.js +76 -0
  42. package/dist/infra/providers/core/logger.js +191 -0
  43. package/dist/infra/providers/core/retry.js +189 -0
  44. package/dist/infra/providers/core/usage.js +376 -0
  45. package/dist/infra/providers/gemini.js +493 -0
  46. package/dist/infra/providers/index.js +83 -0
  47. package/dist/infra/providers/openai.js +662 -0
  48. package/dist/infra/providers/types.js +20 -0
  49. package/dist/infra/providers/utils.js +400 -0
  50. package/dist/infra/sandbox-factory.js +30 -0
  51. package/dist/infra/sandbox.js +243 -0
  52. package/dist/infra/store/factory.js +80 -0
  53. package/dist/infra/store/index.js +26 -0
  54. package/dist/infra/store/json-store.js +606 -0
  55. package/dist/infra/store/types.js +2 -0
  56. package/dist/infra/store.js +29 -0
  57. package/dist/tools/bash_kill/index.js +35 -0
  58. package/dist/tools/bash_kill/prompt.js +14 -0
  59. package/dist/tools/bash_logs/index.js +40 -0
  60. package/dist/tools/bash_logs/prompt.js +14 -0
  61. package/dist/tools/bash_run/index.js +61 -0
  62. package/dist/tools/bash_run/prompt.js +18 -0
  63. package/dist/tools/builtin.js +26 -0
  64. package/dist/tools/define.js +214 -0
  65. package/dist/tools/fs_edit/index.js +62 -0
  66. package/dist/tools/fs_edit/prompt.js +15 -0
  67. package/dist/tools/fs_glob/index.js +40 -0
  68. package/dist/tools/fs_glob/prompt.js +15 -0
  69. package/dist/tools/fs_grep/index.js +66 -0
  70. package/dist/tools/fs_grep/prompt.js +16 -0
  71. package/dist/tools/fs_multi_edit/index.js +106 -0
  72. package/dist/tools/fs_multi_edit/prompt.js +16 -0
  73. package/dist/tools/fs_read/index.js +40 -0
  74. package/dist/tools/fs_read/prompt.js +16 -0
  75. package/dist/tools/fs_write/index.js +40 -0
  76. package/dist/tools/fs_write/prompt.js +15 -0
  77. package/dist/tools/index.js +61 -0
  78. package/dist/tools/mcp.js +185 -0
  79. package/dist/tools/registry.js +26 -0
  80. package/dist/tools/scripts.js +205 -0
  81. package/dist/tools/skills.js +115 -0
  82. package/dist/tools/task_run/index.js +58 -0
  83. package/dist/tools/task_run/prompt.js +25 -0
  84. package/dist/tools/todo_read/index.js +29 -0
  85. package/dist/tools/todo_read/prompt.js +18 -0
  86. package/dist/tools/todo_write/index.js +42 -0
  87. package/dist/tools/todo_write/prompt.js +23 -0
  88. package/dist/tools/tool.js +211 -0
  89. package/dist/tools/toolkit.js +98 -0
  90. package/dist/tools/type-inference.js +207 -0
  91. package/dist/utils/agent-id.js +28 -0
  92. package/dist/utils/logger.js +44 -0
  93. package/dist/utils/session-id.js +64 -0
  94. package/package.json +7 -38
  95. package/dist/index.js.map +0 -7
  96. package/dist/index.mjs +0 -60385
  97. package/dist/index.mjs.map +0 -7
@@ -0,0 +1,1073 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.PostgresStore = void 0;
37
+ const pg_1 = require("pg");
38
+ const store_1 = require("../../store");
39
+ /**
40
+ * PostgresStore 实现
41
+ *
42
+ * 混合存储策略:
43
+ * - 数据库:AgentInfo, Messages, ToolCallRecords, Snapshots(支持查询)
44
+ * - 文件系统:Events, Todos, History, MediaCache(高频写入)
45
+ *
46
+ * PostgreSQL 特性:
47
+ * - JSONB 类型 + GIN 索引
48
+ * - 连接池管理
49
+ * - 事务支持
50
+ */
51
+ class PostgresStore {
52
+ constructor(config, fileStoreBaseDir) {
53
+ // 指标追踪
54
+ this.metrics = {
55
+ saves: 0,
56
+ loads: 0,
57
+ queries: 0,
58
+ deletes: 0,
59
+ latencies: []
60
+ };
61
+ // 合并默认配置
62
+ const poolConfig = {
63
+ ...config,
64
+ port: config.port ?? 5432,
65
+ max: config.max ?? 10,
66
+ idleTimeoutMillis: config.idleTimeoutMillis ?? 30000,
67
+ connectionTimeoutMillis: config.connectionTimeoutMillis ?? 5000,
68
+ };
69
+ this.pool = new pg_1.Pool(poolConfig);
70
+ // 监听连接池错误,防止未处理的异常
71
+ this.pool.on('error', (err) => {
72
+ console.error('[PostgresStore] Unexpected pool error:', err.message);
73
+ });
74
+ this.fileStore = new store_1.JSONStore(fileStoreBaseDir);
75
+ this.initPromise = this.initialize();
76
+ }
77
+ // ========== 数据库初始化 ==========
78
+ /**
79
+ * 确保数据库已初始化
80
+ * 在所有公开的数据库操作方法开头调用
81
+ */
82
+ async ensureInitialized() {
83
+ await this.initPromise;
84
+ }
85
+ async initialize() {
86
+ await this.createTables();
87
+ await this.createIndexes();
88
+ }
89
+ async createTables() {
90
+ const client = await this.pool.connect();
91
+ try {
92
+ // 表 1: agents - Agent 元信息
93
+ await client.query(`
94
+ CREATE TABLE IF NOT EXISTS agents (
95
+ agent_id TEXT PRIMARY KEY,
96
+ template_id TEXT NOT NULL,
97
+ created_at TIMESTAMP NOT NULL,
98
+ config_version TEXT NOT NULL,
99
+ lineage JSONB NOT NULL,
100
+ message_count INTEGER NOT NULL DEFAULT 0,
101
+ last_sfp_index INTEGER NOT NULL DEFAULT -1,
102
+ last_bookmark JSONB,
103
+ breakpoint TEXT,
104
+ metadata JSONB NOT NULL
105
+ );
106
+ `);
107
+ // 表 2: messages - 对话消息
108
+ await client.query(`
109
+ CREATE TABLE IF NOT EXISTS messages (
110
+ id TEXT PRIMARY KEY,
111
+ agent_id TEXT NOT NULL,
112
+ role TEXT NOT NULL CHECK (role IN ('user', 'assistant', 'system')),
113
+ content JSONB NOT NULL,
114
+ seq INTEGER NOT NULL,
115
+ metadata JSONB,
116
+ created_at BIGINT NOT NULL,
117
+ FOREIGN KEY (agent_id) REFERENCES agents(agent_id) ON DELETE CASCADE
118
+ );
119
+ `);
120
+ // 表 3: tool_calls - 工具调用记录
121
+ await client.query(`
122
+ CREATE TABLE IF NOT EXISTS tool_calls (
123
+ id TEXT PRIMARY KEY,
124
+ agent_id TEXT NOT NULL,
125
+ name TEXT NOT NULL,
126
+ input JSONB NOT NULL,
127
+ state TEXT NOT NULL,
128
+ approval JSONB NOT NULL,
129
+ result JSONB,
130
+ error TEXT,
131
+ is_error BOOLEAN DEFAULT FALSE,
132
+ started_at BIGINT,
133
+ completed_at BIGINT,
134
+ duration_ms INTEGER,
135
+ created_at BIGINT NOT NULL,
136
+ updated_at BIGINT NOT NULL,
137
+ audit_trail JSONB NOT NULL,
138
+ FOREIGN KEY (agent_id) REFERENCES agents(agent_id) ON DELETE CASCADE
139
+ );
140
+ `);
141
+ // 表 4: snapshots - 快照
142
+ await client.query(`
143
+ CREATE TABLE IF NOT EXISTS snapshots (
144
+ agent_id TEXT NOT NULL,
145
+ snapshot_id TEXT NOT NULL,
146
+ messages JSONB NOT NULL,
147
+ last_sfp_index INTEGER NOT NULL,
148
+ last_bookmark JSONB NOT NULL,
149
+ created_at TIMESTAMP NOT NULL,
150
+ metadata JSONB,
151
+ PRIMARY KEY (agent_id, snapshot_id),
152
+ FOREIGN KEY (agent_id) REFERENCES agents(agent_id) ON DELETE CASCADE
153
+ );
154
+ `);
155
+ }
156
+ finally {
157
+ client.release();
158
+ }
159
+ }
160
+ async createIndexes() {
161
+ const client = await this.pool.connect();
162
+ try {
163
+ // agents 索引
164
+ await client.query(`
165
+ CREATE INDEX IF NOT EXISTS idx_agents_template_id ON agents(template_id);
166
+ CREATE INDEX IF NOT EXISTS idx_agents_created_at ON agents(created_at DESC);
167
+ CREATE INDEX IF NOT EXISTS idx_agents_lineage_gin ON agents USING GIN(lineage);
168
+ `);
169
+ // messages 索引
170
+ await client.query(`
171
+ CREATE INDEX IF NOT EXISTS idx_messages_agent_id ON messages(agent_id);
172
+ CREATE INDEX IF NOT EXISTS idx_messages_role ON messages(role);
173
+ CREATE INDEX IF NOT EXISTS idx_messages_created_at ON messages(created_at DESC);
174
+ CREATE UNIQUE INDEX IF NOT EXISTS idx_messages_agent_seq ON messages(agent_id, seq);
175
+ CREATE INDEX IF NOT EXISTS idx_messages_content_gin ON messages USING GIN(content);
176
+ `);
177
+ // tool_calls 索引
178
+ await client.query(`
179
+ CREATE INDEX IF NOT EXISTS idx_tool_calls_agent_id ON tool_calls(agent_id);
180
+ CREATE INDEX IF NOT EXISTS idx_tool_calls_name ON tool_calls(name);
181
+ CREATE INDEX IF NOT EXISTS idx_tool_calls_state ON tool_calls(state);
182
+ CREATE INDEX IF NOT EXISTS idx_tool_calls_created_at ON tool_calls(created_at DESC);
183
+ CREATE INDEX IF NOT EXISTS idx_tool_calls_input_gin ON tool_calls USING GIN(input);
184
+ `);
185
+ // snapshots 索引
186
+ await client.query(`
187
+ CREATE INDEX IF NOT EXISTS idx_snapshots_agent_id ON snapshots(agent_id);
188
+ CREATE INDEX IF NOT EXISTS idx_snapshots_created_at ON snapshots(created_at DESC);
189
+ `);
190
+ }
191
+ finally {
192
+ client.release();
193
+ }
194
+ }
195
+ // ========== 运行时状态管理(数据库) ==========
196
+ async saveMessages(agentId, messages) {
197
+ await this.ensureInitialized();
198
+ const client = await this.pool.connect();
199
+ try {
200
+ await client.query('BEGIN');
201
+ // 1. 删除旧消息
202
+ await client.query('DELETE FROM messages WHERE agent_id = $1', [agentId]);
203
+ // 2. 批量插入新消息
204
+ for (let index = 0; index < messages.length; index++) {
205
+ const msg = messages[index];
206
+ const id = this.generateMessageId();
207
+ await client.query(`INSERT INTO messages (
208
+ id, agent_id, role, content, seq, metadata, created_at
209
+ ) VALUES ($1, $2, $3, $4, $5, $6, $7)`, [
210
+ id,
211
+ agentId,
212
+ msg.role,
213
+ JSON.stringify(msg.content),
214
+ index,
215
+ msg.metadata ? JSON.stringify(msg.metadata) : null,
216
+ Date.now()
217
+ ]);
218
+ }
219
+ // 3. 更新 agents 表的 message_count
220
+ await client.query('UPDATE agents SET message_count = $1 WHERE agent_id = $2', [messages.length, agentId]);
221
+ await client.query('COMMIT');
222
+ }
223
+ catch (err) {
224
+ await client.query('ROLLBACK');
225
+ throw err;
226
+ }
227
+ finally {
228
+ client.release();
229
+ }
230
+ }
231
+ async loadMessages(agentId) {
232
+ await this.ensureInitialized();
233
+ const client = await this.pool.connect();
234
+ try {
235
+ const result = await client.query(`SELECT role, content, metadata
236
+ FROM messages
237
+ WHERE agent_id = $1
238
+ ORDER BY seq ASC`, [agentId]);
239
+ return result.rows.map(row => ({
240
+ role: row.role,
241
+ content: row.content,
242
+ metadata: row.metadata || undefined
243
+ }));
244
+ }
245
+ finally {
246
+ client.release();
247
+ }
248
+ }
249
+ generateMessageId() {
250
+ return `msg_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
251
+ }
252
+ async saveToolCallRecords(agentId, records) {
253
+ await this.ensureInitialized();
254
+ const client = await this.pool.connect();
255
+ try {
256
+ await client.query('BEGIN');
257
+ // 1. 删除旧记录
258
+ await client.query('DELETE FROM tool_calls WHERE agent_id = $1', [agentId]);
259
+ // 2. 批量插入新记录
260
+ for (const record of records) {
261
+ await client.query(`INSERT INTO tool_calls (
262
+ id, agent_id, name, input, state, approval,
263
+ result, error, is_error,
264
+ started_at, completed_at, duration_ms,
265
+ created_at, updated_at, audit_trail
266
+ ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)`, [
267
+ record.id,
268
+ agentId,
269
+ record.name,
270
+ JSON.stringify(record.input),
271
+ record.state,
272
+ JSON.stringify(record.approval),
273
+ record.result ? JSON.stringify(record.result) : null,
274
+ record.error || null,
275
+ record.isError,
276
+ record.startedAt || null,
277
+ record.completedAt || null,
278
+ record.durationMs || null,
279
+ record.createdAt,
280
+ record.updatedAt,
281
+ JSON.stringify(record.auditTrail)
282
+ ]);
283
+ }
284
+ await client.query('COMMIT');
285
+ }
286
+ catch (err) {
287
+ await client.query('ROLLBACK');
288
+ throw err;
289
+ }
290
+ finally {
291
+ client.release();
292
+ }
293
+ }
294
+ async loadToolCallRecords(agentId) {
295
+ await this.ensureInitialized();
296
+ const client = await this.pool.connect();
297
+ try {
298
+ const result = await client.query(`SELECT id, name, input, state, approval,
299
+ result, error, is_error,
300
+ started_at, completed_at, duration_ms,
301
+ created_at, updated_at, audit_trail
302
+ FROM tool_calls
303
+ WHERE agent_id = $1
304
+ ORDER BY created_at ASC`, [agentId]);
305
+ return result.rows.map(row => ({
306
+ id: row.id,
307
+ name: row.name,
308
+ input: row.input,
309
+ state: row.state,
310
+ approval: row.approval,
311
+ result: row.result || undefined,
312
+ error: row.error || undefined,
313
+ isError: row.is_error,
314
+ startedAt: row.started_at || undefined,
315
+ completedAt: row.completed_at || undefined,
316
+ durationMs: row.duration_ms || undefined,
317
+ createdAt: row.created_at,
318
+ updatedAt: row.updated_at,
319
+ auditTrail: row.audit_trail
320
+ }));
321
+ }
322
+ finally {
323
+ client.release();
324
+ }
325
+ }
326
+ // ========== 事件流管理(文件系统) ==========
327
+ async saveTodos(agentId, snapshot) {
328
+ return this.fileStore.saveTodos(agentId, snapshot);
329
+ }
330
+ async loadTodos(agentId) {
331
+ return this.fileStore.loadTodos(agentId);
332
+ }
333
+ async appendEvent(agentId, timeline) {
334
+ return this.fileStore.appendEvent(agentId, timeline);
335
+ }
336
+ async *readEvents(agentId, opts) {
337
+ yield* this.fileStore.readEvents(agentId, opts);
338
+ }
339
+ // ========== 历史与压缩管理(文件系统) ==========
340
+ async saveHistoryWindow(agentId, window) {
341
+ return this.fileStore.saveHistoryWindow(agentId, window);
342
+ }
343
+ async loadHistoryWindows(agentId) {
344
+ return this.fileStore.loadHistoryWindows(agentId);
345
+ }
346
+ async saveCompressionRecord(agentId, record) {
347
+ return this.fileStore.saveCompressionRecord(agentId, record);
348
+ }
349
+ async loadCompressionRecords(agentId) {
350
+ return this.fileStore.loadCompressionRecords(agentId);
351
+ }
352
+ async saveRecoveredFile(agentId, file) {
353
+ return this.fileStore.saveRecoveredFile(agentId, file);
354
+ }
355
+ async loadRecoveredFiles(agentId) {
356
+ return this.fileStore.loadRecoveredFiles(agentId);
357
+ }
358
+ // ========== 多模态缓存管理(文件系统) ==========
359
+ async saveMediaCache(agentId, records) {
360
+ return this.fileStore.saveMediaCache(agentId, records);
361
+ }
362
+ async loadMediaCache(agentId) {
363
+ return this.fileStore.loadMediaCache(agentId);
364
+ }
365
+ // ========== 快照管理(数据库) ==========
366
+ async saveSnapshot(agentId, snapshot) {
367
+ await this.ensureInitialized();
368
+ const client = await this.pool.connect();
369
+ try {
370
+ await client.query(`INSERT INTO snapshots (
371
+ agent_id, snapshot_id, messages, last_sfp_index,
372
+ last_bookmark, created_at, metadata
373
+ ) VALUES ($1, $2, $3, $4, $5, $6, $7)
374
+ ON CONFLICT (agent_id, snapshot_id)
375
+ DO UPDATE SET
376
+ messages = EXCLUDED.messages,
377
+ last_sfp_index = EXCLUDED.last_sfp_index,
378
+ last_bookmark = EXCLUDED.last_bookmark,
379
+ created_at = EXCLUDED.created_at,
380
+ metadata = EXCLUDED.metadata`, [
381
+ agentId,
382
+ snapshot.id,
383
+ JSON.stringify(snapshot.messages),
384
+ snapshot.lastSfpIndex,
385
+ JSON.stringify(snapshot.lastBookmark),
386
+ snapshot.createdAt,
387
+ snapshot.metadata ? JSON.stringify(snapshot.metadata) : null
388
+ ]);
389
+ }
390
+ finally {
391
+ client.release();
392
+ }
393
+ }
394
+ async loadSnapshot(agentId, snapshotId) {
395
+ await this.ensureInitialized();
396
+ const client = await this.pool.connect();
397
+ try {
398
+ const result = await client.query(`SELECT snapshot_id, messages, last_sfp_index,
399
+ last_bookmark, created_at, metadata
400
+ FROM snapshots
401
+ WHERE agent_id = $1 AND snapshot_id = $2`, [agentId, snapshotId]);
402
+ if (result.rows.length === 0) {
403
+ return undefined;
404
+ }
405
+ const row = result.rows[0];
406
+ return {
407
+ id: row.snapshot_id,
408
+ messages: row.messages,
409
+ lastSfpIndex: row.last_sfp_index,
410
+ lastBookmark: row.last_bookmark,
411
+ createdAt: row.created_at,
412
+ metadata: row.metadata || undefined
413
+ };
414
+ }
415
+ finally {
416
+ client.release();
417
+ }
418
+ }
419
+ async listSnapshots(agentId) {
420
+ await this.ensureInitialized();
421
+ const client = await this.pool.connect();
422
+ try {
423
+ const result = await client.query(`SELECT snapshot_id, messages, last_sfp_index,
424
+ last_bookmark, created_at, metadata
425
+ FROM snapshots
426
+ WHERE agent_id = $1
427
+ ORDER BY created_at DESC`, [agentId]);
428
+ return result.rows.map(row => ({
429
+ id: row.snapshot_id,
430
+ messages: row.messages,
431
+ lastSfpIndex: row.last_sfp_index,
432
+ lastBookmark: row.last_bookmark,
433
+ createdAt: row.created_at,
434
+ metadata: row.metadata || undefined
435
+ }));
436
+ }
437
+ finally {
438
+ client.release();
439
+ }
440
+ }
441
+ // ========== 元数据管理(数据库) ==========
442
+ async saveInfo(agentId, info) {
443
+ await this.ensureInitialized();
444
+ const client = await this.pool.connect();
445
+ try {
446
+ await client.query(`INSERT INTO agents (
447
+ agent_id, template_id, created_at, config_version,
448
+ lineage, message_count, last_sfp_index, last_bookmark,
449
+ breakpoint, metadata
450
+ ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)
451
+ ON CONFLICT (agent_id)
452
+ DO UPDATE SET
453
+ template_id = EXCLUDED.template_id,
454
+ created_at = EXCLUDED.created_at,
455
+ config_version = EXCLUDED.config_version,
456
+ lineage = EXCLUDED.lineage,
457
+ message_count = EXCLUDED.message_count,
458
+ last_sfp_index = EXCLUDED.last_sfp_index,
459
+ last_bookmark = EXCLUDED.last_bookmark,
460
+ breakpoint = EXCLUDED.breakpoint,
461
+ metadata = EXCLUDED.metadata`, [
462
+ info.agentId,
463
+ info.templateId,
464
+ info.createdAt,
465
+ info.configVersion,
466
+ JSON.stringify(info.lineage),
467
+ info.messageCount,
468
+ info.lastSfpIndex,
469
+ info.lastBookmark ? JSON.stringify(info.lastBookmark) : null,
470
+ info.breakpoint || null,
471
+ JSON.stringify(info.metadata)
472
+ ]);
473
+ }
474
+ finally {
475
+ client.release();
476
+ }
477
+ }
478
+ async loadInfo(agentId) {
479
+ await this.ensureInitialized();
480
+ const client = await this.pool.connect();
481
+ try {
482
+ const result = await client.query(`SELECT agent_id, template_id, created_at, config_version,
483
+ lineage, message_count, last_sfp_index, last_bookmark,
484
+ breakpoint, metadata
485
+ FROM agents
486
+ WHERE agent_id = $1`, [agentId]);
487
+ if (result.rows.length === 0) {
488
+ return undefined;
489
+ }
490
+ const row = result.rows[0];
491
+ const info = {
492
+ agentId: row.agent_id,
493
+ templateId: row.template_id,
494
+ createdAt: row.created_at,
495
+ configVersion: row.config_version,
496
+ lineage: row.lineage,
497
+ messageCount: row.message_count,
498
+ lastSfpIndex: row.last_sfp_index,
499
+ lastBookmark: row.last_bookmark || undefined,
500
+ metadata: row.metadata
501
+ };
502
+ // Restore breakpoint to AgentInfo if present
503
+ if (row.breakpoint) {
504
+ info.breakpoint = row.breakpoint;
505
+ }
506
+ return info;
507
+ }
508
+ finally {
509
+ client.release();
510
+ }
511
+ }
512
+ // ========== 生命周期管理 ==========
513
+ async exists(agentId) {
514
+ await this.ensureInitialized();
515
+ const client = await this.pool.connect();
516
+ try {
517
+ const result = await client.query('SELECT 1 FROM agents WHERE agent_id = $1', [agentId]);
518
+ return result.rows.length > 0;
519
+ }
520
+ finally {
521
+ client.release();
522
+ }
523
+ }
524
+ async delete(agentId) {
525
+ await this.ensureInitialized();
526
+ const client = await this.pool.connect();
527
+ try {
528
+ // 删除数据库记录(级联删除)
529
+ await client.query('DELETE FROM agents WHERE agent_id = $1', [agentId]);
530
+ // 删除文件系统数据
531
+ await this.fileStore.delete(agentId);
532
+ }
533
+ finally {
534
+ client.release();
535
+ }
536
+ }
537
+ async list(prefix) {
538
+ await this.ensureInitialized();
539
+ const client = await this.pool.connect();
540
+ try {
541
+ let query = 'SELECT agent_id FROM agents ORDER BY created_at DESC';
542
+ let params = [];
543
+ if (prefix) {
544
+ query = 'SELECT agent_id FROM agents WHERE agent_id LIKE $1 ORDER BY created_at DESC';
545
+ params = [`${prefix}%`];
546
+ }
547
+ const result = await client.query(query, params);
548
+ return result.rows.map(row => row.agent_id);
549
+ }
550
+ finally {
551
+ client.release();
552
+ }
553
+ }
554
+ // ========== QueryableStore 接口实现 ==========
555
+ async querySessions(filters) {
556
+ await this.ensureInitialized();
557
+ const client = await this.pool.connect();
558
+ try {
559
+ let sql = `
560
+ SELECT agent_id, template_id, created_at, message_count,
561
+ last_sfp_index, breakpoint
562
+ FROM agents
563
+ WHERE 1=1
564
+ `;
565
+ const params = [];
566
+ let paramIndex = 1;
567
+ if (filters.agentId) {
568
+ sql += ` AND agent_id = $${paramIndex++}`;
569
+ params.push(filters.agentId);
570
+ }
571
+ if (filters.templateId) {
572
+ sql += ` AND template_id = $${paramIndex++}`;
573
+ params.push(filters.templateId);
574
+ }
575
+ if (filters.startDate) {
576
+ sql += ` AND created_at >= $${paramIndex++}`;
577
+ params.push(new Date(filters.startDate).toISOString());
578
+ }
579
+ if (filters.endDate) {
580
+ sql += ` AND created_at <= $${paramIndex++}`;
581
+ params.push(new Date(filters.endDate).toISOString());
582
+ }
583
+ // Sorting
584
+ const sortBy = filters.sortBy || 'created_at';
585
+ const sortOrder = filters.sortOrder || 'desc';
586
+ sql += ` ORDER BY ${sortBy} ${sortOrder.toUpperCase()}`;
587
+ // Pagination
588
+ if (filters.limit) {
589
+ sql += ` LIMIT $${paramIndex++}`;
590
+ params.push(filters.limit);
591
+ if (filters.offset) {
592
+ sql += ` OFFSET $${paramIndex++}`;
593
+ params.push(filters.offset);
594
+ }
595
+ }
596
+ const result = await client.query(sql, params);
597
+ return result.rows.map(row => ({
598
+ agentId: row.agent_id,
599
+ templateId: row.template_id,
600
+ createdAt: row.created_at,
601
+ messageCount: row.message_count,
602
+ lastSfpIndex: row.last_sfp_index,
603
+ breakpoint: row.breakpoint
604
+ }));
605
+ }
606
+ finally {
607
+ client.release();
608
+ }
609
+ }
610
+ async queryMessages(filters) {
611
+ await this.ensureInitialized();
612
+ const client = await this.pool.connect();
613
+ try {
614
+ let sql = 'SELECT role, content, metadata FROM messages WHERE 1=1';
615
+ const params = [];
616
+ let paramIndex = 1;
617
+ if (filters.agentId) {
618
+ sql += ` AND agent_id = $${paramIndex++}`;
619
+ params.push(filters.agentId);
620
+ }
621
+ if (filters.role) {
622
+ sql += ` AND role = $${paramIndex++}`;
623
+ params.push(filters.role);
624
+ }
625
+ if (filters.startDate) {
626
+ sql += ` AND created_at >= $${paramIndex++}`;
627
+ params.push(filters.startDate);
628
+ }
629
+ if (filters.endDate) {
630
+ sql += ` AND created_at <= $${paramIndex++}`;
631
+ params.push(filters.endDate);
632
+ }
633
+ sql += ' ORDER BY created_at DESC';
634
+ if (filters.limit) {
635
+ sql += ` LIMIT $${paramIndex++}`;
636
+ params.push(filters.limit);
637
+ if (filters.offset) {
638
+ sql += ` OFFSET $${paramIndex++}`;
639
+ params.push(filters.offset);
640
+ }
641
+ }
642
+ const result = await client.query(sql, params);
643
+ return result.rows.map(row => ({
644
+ role: row.role,
645
+ content: row.content,
646
+ metadata: row.metadata || undefined
647
+ }));
648
+ }
649
+ finally {
650
+ client.release();
651
+ }
652
+ }
653
+ async queryToolCalls(filters) {
654
+ await this.ensureInitialized();
655
+ const client = await this.pool.connect();
656
+ try {
657
+ let sql = `
658
+ SELECT id, name, input, state, approval,
659
+ result, error, is_error,
660
+ started_at, completed_at, duration_ms,
661
+ created_at, updated_at, audit_trail
662
+ FROM tool_calls
663
+ WHERE 1=1
664
+ `;
665
+ const params = [];
666
+ let paramIndex = 1;
667
+ if (filters.agentId) {
668
+ sql += ` AND agent_id = $${paramIndex++}`;
669
+ params.push(filters.agentId);
670
+ }
671
+ if (filters.toolName) {
672
+ sql += ` AND name = $${paramIndex++}`;
673
+ params.push(filters.toolName);
674
+ }
675
+ if (filters.state) {
676
+ sql += ` AND state = $${paramIndex++}`;
677
+ params.push(filters.state);
678
+ }
679
+ if (filters.startDate) {
680
+ sql += ` AND created_at >= $${paramIndex++}`;
681
+ params.push(filters.startDate);
682
+ }
683
+ if (filters.endDate) {
684
+ sql += ` AND created_at <= $${paramIndex++}`;
685
+ params.push(filters.endDate);
686
+ }
687
+ sql += ' ORDER BY created_at DESC';
688
+ if (filters.limit) {
689
+ sql += ` LIMIT $${paramIndex++}`;
690
+ params.push(filters.limit);
691
+ if (filters.offset) {
692
+ sql += ` OFFSET $${paramIndex++}`;
693
+ params.push(filters.offset);
694
+ }
695
+ }
696
+ const result = await client.query(sql, params);
697
+ return result.rows.map(row => ({
698
+ id: row.id,
699
+ name: row.name,
700
+ input: row.input,
701
+ state: row.state,
702
+ approval: row.approval,
703
+ result: row.result || undefined,
704
+ error: row.error || undefined,
705
+ isError: row.is_error,
706
+ startedAt: row.started_at || undefined,
707
+ completedAt: row.completed_at || undefined,
708
+ durationMs: row.duration_ms || undefined,
709
+ createdAt: row.created_at,
710
+ updatedAt: row.updated_at,
711
+ auditTrail: row.audit_trail
712
+ }));
713
+ }
714
+ finally {
715
+ client.release();
716
+ }
717
+ }
718
+ async aggregateStats(agentId) {
719
+ await this.ensureInitialized();
720
+ const client = await this.pool.connect();
721
+ try {
722
+ // Total messages
723
+ const messageStats = await client.query('SELECT COUNT(*) as total FROM messages WHERE agent_id = $1', [agentId]);
724
+ // Total tool calls
725
+ const toolCallStats = await client.query('SELECT COUNT(*) as total FROM tool_calls WHERE agent_id = $1', [agentId]);
726
+ // Total snapshots
727
+ const snapshotStats = await client.query('SELECT COUNT(*) as total FROM snapshots WHERE agent_id = $1', [agentId]);
728
+ // Tool calls by name
729
+ const toolCallsByName = await client.query(`SELECT name, COUNT(*) as count
730
+ FROM tool_calls
731
+ WHERE agent_id = $1
732
+ GROUP BY name`, [agentId]);
733
+ // Tool calls by state
734
+ const toolCallsByState = await client.query(`SELECT state, COUNT(*) as count
735
+ FROM tool_calls
736
+ WHERE agent_id = $1
737
+ GROUP BY state`, [agentId]);
738
+ return {
739
+ totalMessages: parseInt(messageStats.rows[0].total),
740
+ totalToolCalls: parseInt(toolCallStats.rows[0].total),
741
+ totalSnapshots: parseInt(snapshotStats.rows[0].total),
742
+ avgMessagesPerSession: parseInt(messageStats.rows[0].total),
743
+ toolCallsByName: toolCallsByName.rows.reduce((acc, row) => {
744
+ acc[row.name] = parseInt(row.count);
745
+ return acc;
746
+ }, {}),
747
+ toolCallsByState: toolCallsByState.rows.reduce((acc, row) => {
748
+ acc[row.state] = parseInt(row.count);
749
+ return acc;
750
+ }, {})
751
+ };
752
+ }
753
+ finally {
754
+ client.release();
755
+ }
756
+ }
757
+ // ========== 连接管理 ==========
758
+ /**
759
+ * 关闭连接池
760
+ */
761
+ async close() {
762
+ await this.ensureInitialized();
763
+ await this.pool.end();
764
+ }
765
+ // ========== ExtendedStore 高级功能 ==========
766
+ /**
767
+ * 健康检查
768
+ */
769
+ async healthCheck() {
770
+ const checkedAt = Date.now();
771
+ let dbConnected = false;
772
+ let dbLatencyMs;
773
+ let fsWritable = false;
774
+ // 检查数据库连接
775
+ try {
776
+ const start = Date.now();
777
+ const client = await this.pool.connect();
778
+ try {
779
+ await client.query('SELECT 1');
780
+ dbConnected = true;
781
+ dbLatencyMs = Date.now() - start;
782
+ }
783
+ finally {
784
+ client.release();
785
+ }
786
+ }
787
+ catch (error) {
788
+ dbConnected = false;
789
+ }
790
+ // 检查文件系统
791
+ try {
792
+ const fs = await Promise.resolve().then(() => __importStar(require('fs')));
793
+ const path = await Promise.resolve().then(() => __importStar(require('path')));
794
+ const baseDir = this.fileStore.baseDir;
795
+ // 确保目录存在
796
+ if (!fs.existsSync(baseDir)) {
797
+ fs.mkdirSync(baseDir, { recursive: true });
798
+ }
799
+ const testFile = path.join(baseDir, '.health-check');
800
+ fs.writeFileSync(testFile, 'ok');
801
+ fs.unlinkSync(testFile);
802
+ fsWritable = true;
803
+ }
804
+ catch (error) {
805
+ fsWritable = false;
806
+ }
807
+ return {
808
+ healthy: dbConnected && fsWritable,
809
+ database: {
810
+ connected: dbConnected,
811
+ latencyMs: dbLatencyMs
812
+ },
813
+ fileSystem: {
814
+ writable: fsWritable
815
+ },
816
+ checkedAt
817
+ };
818
+ }
819
+ /**
820
+ * 一致性检查
821
+ * 检查数据库和文件系统之间的数据一致性
822
+ */
823
+ async checkConsistency(agentId) {
824
+ await this.ensureInitialized();
825
+ const issues = [];
826
+ const checkedAt = Date.now();
827
+ // 检查 Agent 是否存在于数据库
828
+ const dbExists = await this.exists(agentId);
829
+ if (!dbExists) {
830
+ issues.push(`Agent ${agentId} 不存在于数据库中`);
831
+ return { consistent: false, issues, checkedAt };
832
+ }
833
+ // 检查文件系统中的数据
834
+ const fs = await Promise.resolve().then(() => __importStar(require('fs'))).then(m => m.promises);
835
+ const path = await Promise.resolve().then(() => __importStar(require('path')));
836
+ const agentDir = path.join(this.fileStore.baseDir, agentId);
837
+ try {
838
+ await fs.access(agentDir);
839
+ }
840
+ catch {
841
+ // 文件系统目录不存在不一定是问题(可能还没有事件/todos等)
842
+ }
843
+ // 检查消息数量一致性
844
+ const info = await this.loadInfo(agentId);
845
+ const messages = await this.loadMessages(agentId);
846
+ if (info && info.messageCount !== messages.length) {
847
+ issues.push(`消息数量不一致: info.messageCount=${info.messageCount}, 实际消息数=${messages.length}`);
848
+ }
849
+ // 检查工具调用记录
850
+ const toolCalls = await this.loadToolCallRecords(agentId);
851
+ for (const call of toolCalls) {
852
+ if (!call.id || !call.name) {
853
+ issues.push(`工具调用记录缺少必要字段: ${JSON.stringify(call)}`);
854
+ }
855
+ }
856
+ return {
857
+ consistent: issues.length === 0,
858
+ issues,
859
+ checkedAt
860
+ };
861
+ }
862
+ /**
863
+ * 获取指标统计
864
+ */
865
+ async getMetrics() {
866
+ await this.ensureInitialized();
867
+ const client = await this.pool.connect();
868
+ try {
869
+ // 获取存储统计
870
+ const agentCount = await client.query('SELECT COUNT(*) as count FROM agents');
871
+ const messageCount = await client.query('SELECT COUNT(*) as count FROM messages');
872
+ const toolCallCount = await client.query('SELECT COUNT(*) as count FROM tool_calls');
873
+ // 尝试获取数据库大小(PostgreSQL 特有)
874
+ let dbSizeBytes;
875
+ try {
876
+ const sizeResult = await client.query("SELECT pg_database_size(current_database()) as size");
877
+ dbSizeBytes = parseInt(sizeResult.rows[0].size);
878
+ }
879
+ catch {
880
+ // 忽略,某些环境可能没有权限
881
+ }
882
+ // 计算性能指标
883
+ const latencies = this.metrics.latencies;
884
+ const avgLatencyMs = latencies.length > 0
885
+ ? latencies.reduce((a, b) => a + b, 0) / latencies.length
886
+ : 0;
887
+ const maxLatencyMs = latencies.length > 0 ? Math.max(...latencies) : 0;
888
+ const minLatencyMs = latencies.length > 0 ? Math.min(...latencies) : 0;
889
+ return {
890
+ operations: {
891
+ saves: this.metrics.saves,
892
+ loads: this.metrics.loads,
893
+ queries: this.metrics.queries,
894
+ deletes: this.metrics.deletes
895
+ },
896
+ performance: {
897
+ avgLatencyMs,
898
+ maxLatencyMs,
899
+ minLatencyMs
900
+ },
901
+ storage: {
902
+ totalAgents: parseInt(agentCount.rows[0].count),
903
+ totalMessages: parseInt(messageCount.rows[0].count),
904
+ totalToolCalls: parseInt(toolCallCount.rows[0].count),
905
+ dbSizeBytes
906
+ },
907
+ collectedAt: Date.now()
908
+ };
909
+ }
910
+ finally {
911
+ client.release();
912
+ }
913
+ }
914
+ /**
915
+ * 获取分布式锁
916
+ * 使用 PostgreSQL Advisory Lock
917
+ */
918
+ async acquireAgentLock(agentId, timeoutMs = 30000) {
919
+ await this.ensureInitialized();
920
+ // 将 agentId 转换为数字用于 advisory lock
921
+ const lockKey = this.hashStringToInt(agentId);
922
+ const client = await this.pool.connect();
923
+ try {
924
+ // 尝试获取锁(带超时)
925
+ const result = await client.query('SELECT pg_try_advisory_lock($1) as acquired', [lockKey]);
926
+ if (!result.rows[0].acquired) {
927
+ client.release();
928
+ throw new Error(`无法获取 Agent ${agentId} 的锁,可能被其他进程占用`);
929
+ }
930
+ // 设置超时自动释放
931
+ const timeoutId = setTimeout(async () => {
932
+ try {
933
+ await client.query('SELECT pg_advisory_unlock($1)', [lockKey]);
934
+ client.release();
935
+ }
936
+ catch {
937
+ // 忽略释放错误
938
+ }
939
+ }, timeoutMs);
940
+ // 返回释放函数
941
+ return async () => {
942
+ clearTimeout(timeoutId);
943
+ try {
944
+ await client.query('SELECT pg_advisory_unlock($1)', [lockKey]);
945
+ }
946
+ finally {
947
+ client.release();
948
+ }
949
+ };
950
+ }
951
+ catch (error) {
952
+ client.release();
953
+ throw error;
954
+ }
955
+ }
956
+ /**
957
+ * 批量 Fork Agent
958
+ */
959
+ async batchFork(agentId, count) {
960
+ await this.ensureInitialized();
961
+ // 加载源 Agent 数据
962
+ const sourceInfo = await this.loadInfo(agentId);
963
+ if (!sourceInfo) {
964
+ throw new Error(`源 Agent ${agentId} 不存在`);
965
+ }
966
+ const sourceMessages = await this.loadMessages(agentId);
967
+ const sourceToolCalls = await this.loadToolCallRecords(agentId);
968
+ const client = await this.pool.connect();
969
+ const newAgentIds = [];
970
+ try {
971
+ await client.query('BEGIN');
972
+ for (let i = 0; i < count; i++) {
973
+ // 生成新的 Agent ID
974
+ const newAgentId = this.generateAgentId();
975
+ newAgentIds.push(newAgentId);
976
+ // 创建新 Agent Info
977
+ const newInfo = {
978
+ ...sourceInfo,
979
+ agentId: newAgentId,
980
+ createdAt: new Date().toISOString(),
981
+ lineage: [...sourceInfo.lineage, agentId]
982
+ };
983
+ // 插入 Agent Info
984
+ await client.query(`INSERT INTO agents (
985
+ agent_id, template_id, created_at, config_version,
986
+ lineage, message_count, last_sfp_index, last_bookmark,
987
+ breakpoint, metadata
988
+ ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10)`, [
989
+ newInfo.agentId,
990
+ newInfo.templateId,
991
+ newInfo.createdAt,
992
+ newInfo.configVersion,
993
+ JSON.stringify(newInfo.lineage),
994
+ newInfo.messageCount,
995
+ newInfo.lastSfpIndex,
996
+ newInfo.lastBookmark ? JSON.stringify(newInfo.lastBookmark) : null,
997
+ newInfo.breakpoint || null,
998
+ JSON.stringify(newInfo.metadata)
999
+ ]);
1000
+ // 复制消息
1001
+ for (let index = 0; index < sourceMessages.length; index++) {
1002
+ const msg = sourceMessages[index];
1003
+ await client.query(`INSERT INTO messages (
1004
+ id, agent_id, role, content, seq, metadata, created_at
1005
+ ) VALUES ($1, $2, $3, $4, $5, $6, $7)`, [
1006
+ this.generateMessageId(),
1007
+ newAgentId,
1008
+ msg.role,
1009
+ JSON.stringify(msg.content),
1010
+ index,
1011
+ msg.metadata ? JSON.stringify(msg.metadata) : null,
1012
+ Date.now()
1013
+ ]);
1014
+ }
1015
+ // 复制工具调用记录
1016
+ for (const record of sourceToolCalls) {
1017
+ await client.query(`INSERT INTO tool_calls (
1018
+ id, agent_id, name, input, state, approval,
1019
+ result, error, is_error,
1020
+ started_at, completed_at, duration_ms,
1021
+ created_at, updated_at, audit_trail
1022
+ ) VALUES ($1, $2, $3, $4, $5, $6, $7, $8, $9, $10, $11, $12, $13, $14, $15)`, [
1023
+ `${record.id}_fork_${i}`,
1024
+ newAgentId,
1025
+ record.name,
1026
+ JSON.stringify(record.input),
1027
+ record.state,
1028
+ JSON.stringify(record.approval),
1029
+ record.result ? JSON.stringify(record.result) : null,
1030
+ record.error || null,
1031
+ record.isError,
1032
+ record.startedAt || null,
1033
+ record.completedAt || null,
1034
+ record.durationMs || null,
1035
+ record.createdAt,
1036
+ record.updatedAt,
1037
+ JSON.stringify(record.auditTrail)
1038
+ ]);
1039
+ }
1040
+ }
1041
+ await client.query('COMMIT');
1042
+ return newAgentIds;
1043
+ }
1044
+ catch (error) {
1045
+ await client.query('ROLLBACK');
1046
+ throw error;
1047
+ }
1048
+ finally {
1049
+ client.release();
1050
+ }
1051
+ }
1052
+ /**
1053
+ * 将字符串哈希为整数(用于 advisory lock)
1054
+ */
1055
+ hashStringToInt(str) {
1056
+ let hash = 0;
1057
+ for (let i = 0; i < str.length; i++) {
1058
+ const char = str.charCodeAt(i);
1059
+ hash = ((hash << 5) - hash) + char;
1060
+ hash = hash & hash; // Convert to 32bit integer
1061
+ }
1062
+ return Math.abs(hash);
1063
+ }
1064
+ /**
1065
+ * 生成 Agent ID
1066
+ */
1067
+ generateAgentId() {
1068
+ const timestamp = Date.now().toString(36);
1069
+ const random = Math.random().toString(36).substring(2, 18);
1070
+ return `agt-${timestamp}${random}`;
1071
+ }
1072
+ }
1073
+ exports.PostgresStore = PostgresStore;