confused-ai-core 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/FEATURES.md +169 -0
- package/package.json +119 -0
- package/src/agent.ts +187 -0
- package/src/agentic/index.ts +87 -0
- package/src/agentic/runner.ts +386 -0
- package/src/agentic/types.ts +91 -0
- package/src/artifacts/artifact.ts +417 -0
- package/src/artifacts/index.ts +42 -0
- package/src/artifacts/media.ts +304 -0
- package/src/cli/index.ts +122 -0
- package/src/core/base-agent.ts +151 -0
- package/src/core/context-builder.ts +106 -0
- package/src/core/index.ts +8 -0
- package/src/core/schemas.ts +17 -0
- package/src/core/types.ts +158 -0
- package/src/create-agent.ts +309 -0
- package/src/debug-logger.ts +188 -0
- package/src/dx/agent.ts +88 -0
- package/src/dx/define-agent.ts +183 -0
- package/src/dx/dev-logger.ts +57 -0
- package/src/dx/index.ts +11 -0
- package/src/errors.ts +175 -0
- package/src/execution/engine.ts +522 -0
- package/src/execution/graph-builder.ts +362 -0
- package/src/execution/index.ts +8 -0
- package/src/execution/types.ts +257 -0
- package/src/execution/worker-pool.ts +308 -0
- package/src/extensions/index.ts +123 -0
- package/src/guardrails/allowlist.ts +155 -0
- package/src/guardrails/index.ts +17 -0
- package/src/guardrails/types.ts +159 -0
- package/src/guardrails/validator.ts +265 -0
- package/src/index.ts +74 -0
- package/src/knowledge/index.ts +5 -0
- package/src/knowledge/types.ts +52 -0
- package/src/learning/in-memory-store.ts +72 -0
- package/src/learning/index.ts +6 -0
- package/src/learning/types.ts +42 -0
- package/src/llm/cache.ts +300 -0
- package/src/llm/index.ts +22 -0
- package/src/llm/model-resolver.ts +81 -0
- package/src/llm/openai-provider.ts +313 -0
- package/src/llm/openrouter-provider.ts +29 -0
- package/src/llm/types.ts +131 -0
- package/src/memory/in-memory-store.ts +255 -0
- package/src/memory/index.ts +7 -0
- package/src/memory/types.ts +193 -0
- package/src/memory/vector-store.ts +251 -0
- package/src/observability/console-logger.ts +123 -0
- package/src/observability/index.ts +12 -0
- package/src/observability/metrics.ts +85 -0
- package/src/observability/otlp-exporter.ts +417 -0
- package/src/observability/tracer.ts +105 -0
- package/src/observability/types.ts +341 -0
- package/src/orchestration/agent-adapter.ts +33 -0
- package/src/orchestration/index.ts +34 -0
- package/src/orchestration/load-balancer.ts +151 -0
- package/src/orchestration/mcp-types.ts +59 -0
- package/src/orchestration/message-bus.ts +192 -0
- package/src/orchestration/orchestrator.ts +349 -0
- package/src/orchestration/pipeline.ts +66 -0
- package/src/orchestration/supervisor.ts +107 -0
- package/src/orchestration/swarm.ts +1099 -0
- package/src/orchestration/toolkit.ts +47 -0
- package/src/orchestration/types.ts +339 -0
- package/src/planner/classical-planner.ts +383 -0
- package/src/planner/index.ts +8 -0
- package/src/planner/llm-planner.ts +353 -0
- package/src/planner/types.ts +227 -0
- package/src/planner/validator.ts +297 -0
- package/src/production/circuit-breaker.ts +290 -0
- package/src/production/graceful-shutdown.ts +251 -0
- package/src/production/health.ts +333 -0
- package/src/production/index.ts +57 -0
- package/src/production/latency-eval.ts +62 -0
- package/src/production/rate-limiter.ts +287 -0
- package/src/production/resumable-stream.ts +289 -0
- package/src/production/types.ts +81 -0
- package/src/sdk/index.ts +374 -0
- package/src/session/db-driver.ts +50 -0
- package/src/session/in-memory-store.ts +235 -0
- package/src/session/index.ts +12 -0
- package/src/session/sql-store.ts +315 -0
- package/src/session/sqlite-store.ts +61 -0
- package/src/session/types.ts +153 -0
- package/src/tools/base-tool.ts +223 -0
- package/src/tools/browser-tool.ts +123 -0
- package/src/tools/calculator-tool.ts +265 -0
- package/src/tools/file-tools.ts +394 -0
- package/src/tools/github-tool.ts +432 -0
- package/src/tools/hackernews-tool.ts +187 -0
- package/src/tools/http-tool.ts +118 -0
- package/src/tools/index.ts +99 -0
- package/src/tools/jira-tool.ts +373 -0
- package/src/tools/notion-tool.ts +322 -0
- package/src/tools/openai-tool.ts +236 -0
- package/src/tools/registry.ts +131 -0
- package/src/tools/serpapi-tool.ts +234 -0
- package/src/tools/shell-tool.ts +118 -0
- package/src/tools/slack-tool.ts +327 -0
- package/src/tools/telegram-tool.ts +127 -0
- package/src/tools/types.ts +229 -0
- package/src/tools/websearch-tool.ts +335 -0
- package/src/tools/wikipedia-tool.ts +177 -0
- package/src/tools/yfinance-tool.ts +33 -0
- package/src/voice/index.ts +17 -0
- package/src/voice/voice-provider.ts +228 -0
- package/tests/artifact.test.ts +241 -0
- package/tests/circuit-breaker.test.ts +171 -0
- package/tests/health.test.ts +192 -0
- package/tests/llm-cache.test.ts +186 -0
- package/tests/rate-limiter.test.ts +161 -0
- package/tsconfig.json +29 -0
- package/vitest.config.ts +47 -0
|
@@ -0,0 +1,315 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQL-backed session store. Works with any DB via SessionDbDriver.
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type {
|
|
6
|
+
SessionStore,
|
|
7
|
+
Session,
|
|
8
|
+
SessionRun,
|
|
9
|
+
SessionId,
|
|
10
|
+
SessionQuery,
|
|
11
|
+
SessionStoreConfig,
|
|
12
|
+
} from './types.js';
|
|
13
|
+
import { SessionState } from './types.js';
|
|
14
|
+
import type { SessionDbDriver, SessionRow, SessionRunRow } from './db-driver.js';
|
|
15
|
+
import type { Message } from '../llm/types.js';
|
|
16
|
+
|
|
17
|
+
const DEFAULT_CONFIG: Required<SessionStoreConfig> = {
|
|
18
|
+
defaultTtlMs: 24 * 60 * 60 * 1000,
|
|
19
|
+
maxSessionsPerAgent: 100,
|
|
20
|
+
maxMessagesPerSession: 1000,
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
function toSession(row: SessionRow): Session {
|
|
24
|
+
return {
|
|
25
|
+
id: row.id,
|
|
26
|
+
agentId: row.agent_id,
|
|
27
|
+
userId: row.user_id ?? undefined,
|
|
28
|
+
state: row.state as SessionState,
|
|
29
|
+
messages: JSON.parse(row.messages || '[]') as Message[],
|
|
30
|
+
metadata: (JSON.parse(row.metadata || '{}') as Session['metadata']) ?? {},
|
|
31
|
+
context: (JSON.parse(row.context || '{}') as Record<string, unknown>) ?? {},
|
|
32
|
+
createdAt: new Date(row.created_at),
|
|
33
|
+
updatedAt: new Date(row.updated_at),
|
|
34
|
+
expiresAt: row.expires_at ? new Date(row.expires_at) : undefined,
|
|
35
|
+
};
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
function toSessionRun(row: SessionRunRow): SessionRun {
|
|
39
|
+
return {
|
|
40
|
+
id: row.id,
|
|
41
|
+
sessionId: row.session_id,
|
|
42
|
+
agentId: row.agent_id,
|
|
43
|
+
startTime: new Date(row.start_time),
|
|
44
|
+
endTime: row.end_time ? new Date(row.end_time) : undefined,
|
|
45
|
+
status: row.status as SessionRun['status'],
|
|
46
|
+
steps: row.steps,
|
|
47
|
+
result: row.result != null ? JSON.parse(row.result) : undefined,
|
|
48
|
+
error: row.error ?? undefined,
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
function genId(prefix: string): string {
|
|
53
|
+
return `${prefix}-${Date.now()}-${Math.random().toString(36).slice(2, 11)}`;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* SQL session store. Use with SessionDbDriver (SQLite, PostgreSQL, etc.).
|
|
58
|
+
*/
|
|
59
|
+
export class SqlSessionStore implements SessionStore {
|
|
60
|
+
private driver: SessionDbDriver;
|
|
61
|
+
private config: Required<SessionStoreConfig>;
|
|
62
|
+
private tableSessions: string;
|
|
63
|
+
private tableRuns: string;
|
|
64
|
+
|
|
65
|
+
constructor(
|
|
66
|
+
driver: SessionDbDriver,
|
|
67
|
+
config: SessionStoreConfig & { tablePrefix?: string } = {}
|
|
68
|
+
) {
|
|
69
|
+
this.driver = driver;
|
|
70
|
+
this.config = { ...DEFAULT_CONFIG, ...config };
|
|
71
|
+
const prefix = config.tablePrefix ?? 'agent';
|
|
72
|
+
this.tableSessions = `${prefix}_sessions`;
|
|
73
|
+
this.tableRuns = `${prefix}_session_runs`;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/** Create tables if they don't exist. Call once at startup. */
|
|
77
|
+
async migrate(): Promise<void> {
|
|
78
|
+
const exec = this.driver.exec ?? ((sql: string) => this.driver.run(sql));
|
|
79
|
+
await exec(
|
|
80
|
+
`CREATE TABLE IF NOT EXISTS ${this.tableSessions} (
|
|
81
|
+
id TEXT PRIMARY KEY,
|
|
82
|
+
agent_id TEXT NOT NULL,
|
|
83
|
+
user_id TEXT,
|
|
84
|
+
state TEXT NOT NULL,
|
|
85
|
+
messages TEXT NOT NULL DEFAULT '[]',
|
|
86
|
+
metadata TEXT NOT NULL DEFAULT '{}',
|
|
87
|
+
context TEXT NOT NULL DEFAULT '{}',
|
|
88
|
+
created_at TEXT NOT NULL,
|
|
89
|
+
updated_at TEXT NOT NULL,
|
|
90
|
+
expires_at TEXT
|
|
91
|
+
)`
|
|
92
|
+
);
|
|
93
|
+
await exec(
|
|
94
|
+
`CREATE TABLE IF NOT EXISTS ${this.tableRuns} (
|
|
95
|
+
id TEXT PRIMARY KEY,
|
|
96
|
+
session_id TEXT NOT NULL,
|
|
97
|
+
agent_id TEXT NOT NULL,
|
|
98
|
+
start_time TEXT NOT NULL,
|
|
99
|
+
end_time TEXT,
|
|
100
|
+
status TEXT NOT NULL,
|
|
101
|
+
steps INTEGER NOT NULL DEFAULT 0,
|
|
102
|
+
result TEXT,
|
|
103
|
+
error TEXT
|
|
104
|
+
)`
|
|
105
|
+
);
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
async create(session: Omit<Session, 'id' | 'createdAt' | 'updatedAt'>): Promise<Session> {
|
|
109
|
+
const id = genId('session');
|
|
110
|
+
const now = new Date().toISOString();
|
|
111
|
+
const messages = JSON.stringify(session.messages ?? []);
|
|
112
|
+
const metadata = JSON.stringify(session.metadata ?? {});
|
|
113
|
+
const context = JSON.stringify(session.context ?? {});
|
|
114
|
+
const expiresAt = session.expiresAt?.toISOString() ?? null;
|
|
115
|
+
|
|
116
|
+
await this.driver.run(
|
|
117
|
+
`INSERT INTO ${this.tableSessions} (id, agent_id, user_id, state, messages, metadata, context, created_at, updated_at, expires_at)
|
|
118
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
119
|
+
[
|
|
120
|
+
id,
|
|
121
|
+
session.agentId,
|
|
122
|
+
session.userId ?? null,
|
|
123
|
+
session.state,
|
|
124
|
+
messages,
|
|
125
|
+
metadata,
|
|
126
|
+
context,
|
|
127
|
+
now,
|
|
128
|
+
now,
|
|
129
|
+
expiresAt,
|
|
130
|
+
]
|
|
131
|
+
);
|
|
132
|
+
|
|
133
|
+
await this.enforceMaxSessions(session.agentId);
|
|
134
|
+
|
|
135
|
+
return this.get(id) as Promise<Session>;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
async get(sessionId: SessionId): Promise<Session | null> {
|
|
139
|
+
const rows = await this.driver.query<SessionRow>(
|
|
140
|
+
`SELECT * FROM ${this.tableSessions} WHERE id = ?`,
|
|
141
|
+
[sessionId]
|
|
142
|
+
);
|
|
143
|
+
const row = rows[0];
|
|
144
|
+
if (!row) return null;
|
|
145
|
+
|
|
146
|
+
if (row.expires_at && new Date(row.expires_at) < new Date()) {
|
|
147
|
+
await this.delete(sessionId);
|
|
148
|
+
return null;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
return toSession(row);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
async update(sessionId: SessionId, updates: Partial<Omit<Session, 'id' | 'createdAt'>>): Promise<Session> {
|
|
155
|
+
const existing = await this.get(sessionId);
|
|
156
|
+
if (!existing) throw new Error(`Session not found: ${sessionId}`);
|
|
157
|
+
|
|
158
|
+
const updated: Session = { ...existing, ...updates, updatedAt: new Date() };
|
|
159
|
+
const messages = JSON.stringify(updated.messages);
|
|
160
|
+
const metadata = JSON.stringify(updated.metadata);
|
|
161
|
+
const context = JSON.stringify(updated.context);
|
|
162
|
+
const expiresAt = updated.expiresAt?.toISOString() ?? null;
|
|
163
|
+
|
|
164
|
+
await this.driver.run(
|
|
165
|
+
`UPDATE ${this.tableSessions} SET agent_id = ?, user_id = ?, state = ?, messages = ?, metadata = ?, context = ?, updated_at = ?, expires_at = ?
|
|
166
|
+
WHERE id = ?`,
|
|
167
|
+
[
|
|
168
|
+
updated.agentId,
|
|
169
|
+
updated.userId ?? null,
|
|
170
|
+
updated.state,
|
|
171
|
+
messages,
|
|
172
|
+
metadata,
|
|
173
|
+
context,
|
|
174
|
+
updated.updatedAt.toISOString(),
|
|
175
|
+
expiresAt,
|
|
176
|
+
sessionId,
|
|
177
|
+
]
|
|
178
|
+
);
|
|
179
|
+
|
|
180
|
+
return this.get(sessionId) as Promise<Session>;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
async delete(sessionId: SessionId): Promise<boolean> {
|
|
184
|
+
await this.driver.run(`DELETE FROM ${this.tableRuns} WHERE session_id = ?`, [sessionId]);
|
|
185
|
+
await this.driver.run(`DELETE FROM ${this.tableSessions} WHERE id = ?`, [sessionId]);
|
|
186
|
+
return true;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
async list(query?: SessionQuery): Promise<Session[]> {
|
|
190
|
+
let sql = `SELECT * FROM ${this.tableSessions} WHERE 1=1`;
|
|
191
|
+
const params: unknown[] = [];
|
|
192
|
+
|
|
193
|
+
if (query?.agentId) {
|
|
194
|
+
sql += ' AND agent_id = ?';
|
|
195
|
+
params.push(query.agentId);
|
|
196
|
+
}
|
|
197
|
+
if (query?.userId) {
|
|
198
|
+
sql += ' AND user_id = ?';
|
|
199
|
+
params.push(query.userId);
|
|
200
|
+
}
|
|
201
|
+
if (query?.state) {
|
|
202
|
+
sql += ' AND state = ?';
|
|
203
|
+
params.push(query.state);
|
|
204
|
+
}
|
|
205
|
+
if (query?.before) {
|
|
206
|
+
sql += ' AND created_at < ?';
|
|
207
|
+
params.push(query.before.toISOString());
|
|
208
|
+
}
|
|
209
|
+
if (query?.after) {
|
|
210
|
+
sql += ' AND created_at > ?';
|
|
211
|
+
params.push(query.after.toISOString());
|
|
212
|
+
}
|
|
213
|
+
|
|
214
|
+
sql += ' ORDER BY updated_at DESC';
|
|
215
|
+
if (query?.limit) {
|
|
216
|
+
sql += ' LIMIT ?';
|
|
217
|
+
params.push(query.limit);
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
const rows = await this.driver.query<SessionRow>(sql, params);
|
|
221
|
+
return rows.map(toSession);
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
async addMessage(sessionId: SessionId, message: Message): Promise<Session> {
|
|
225
|
+
const session = await this.get(sessionId);
|
|
226
|
+
if (!session) throw new Error(`Session not found: ${sessionId}`);
|
|
227
|
+
|
|
228
|
+
const messages = [...session.messages, message];
|
|
229
|
+
if (messages.length > this.config.maxMessagesPerSession) {
|
|
230
|
+
messages.shift();
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return this.update(sessionId, { messages, state: SessionState.ACTIVE });
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
async getMessages(sessionId: SessionId): Promise<Message[]> {
|
|
237
|
+
const session = await this.get(sessionId);
|
|
238
|
+
if (!session) throw new Error(`Session not found: ${sessionId}`);
|
|
239
|
+
return [...session.messages];
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
async clearMessages(sessionId: SessionId): Promise<Session> {
|
|
243
|
+
return this.update(sessionId, { messages: [] });
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
async setContext(sessionId: SessionId, key: string, value: unknown): Promise<Session> {
|
|
247
|
+
const session = await this.get(sessionId);
|
|
248
|
+
if (!session) throw new Error(`Session not found: ${sessionId}`);
|
|
249
|
+
return this.update(sessionId, { context: { ...session.context, [key]: value } });
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
async getContext(sessionId: SessionId, key: string): Promise<unknown> {
|
|
253
|
+
const session = await this.get(sessionId);
|
|
254
|
+
if (!session) throw new Error(`Session not found: ${sessionId}`);
|
|
255
|
+
return session.context[key];
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
async recordRun(run: Omit<SessionRun, 'id'>): Promise<SessionRun> {
|
|
259
|
+
const session = await this.get(run.sessionId);
|
|
260
|
+
if (!session) throw new Error(`Session not found: ${run.sessionId}`);
|
|
261
|
+
|
|
262
|
+
const id = genId('run');
|
|
263
|
+
await this.driver.run(
|
|
264
|
+
`INSERT INTO ${this.tableRuns} (id, session_id, agent_id, start_time, end_time, status, steps, result, error)
|
|
265
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`,
|
|
266
|
+
[
|
|
267
|
+
id,
|
|
268
|
+
run.sessionId,
|
|
269
|
+
run.agentId,
|
|
270
|
+
run.startTime.toISOString(),
|
|
271
|
+
run.endTime?.toISOString() ?? null,
|
|
272
|
+
run.status,
|
|
273
|
+
run.steps,
|
|
274
|
+
run.result != null ? JSON.stringify(run.result) : null,
|
|
275
|
+
run.error ?? null,
|
|
276
|
+
]
|
|
277
|
+
);
|
|
278
|
+
|
|
279
|
+
const rows = await this.driver.query<SessionRunRow>(`SELECT * FROM ${this.tableRuns} WHERE id = ?`, [id]);
|
|
280
|
+
return toSessionRun(rows[0]!);
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
async getRuns(sessionId: SessionId): Promise<SessionRun[]> {
|
|
284
|
+
const rows = await this.driver.query<SessionRunRow>(
|
|
285
|
+
`SELECT * FROM ${this.tableRuns} WHERE session_id = ? ORDER BY start_time DESC`,
|
|
286
|
+
[sessionId]
|
|
287
|
+
);
|
|
288
|
+
return rows.map(toSessionRun);
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
async cleanup(): Promise<number> {
|
|
292
|
+
const now = new Date().toISOString();
|
|
293
|
+
const rows = await this.driver.query<{ id: string }>(
|
|
294
|
+
`SELECT id FROM ${this.tableSessions} WHERE expires_at IS NOT NULL AND expires_at < ?`,
|
|
295
|
+
[now]
|
|
296
|
+
);
|
|
297
|
+
for (const r of rows) {
|
|
298
|
+
await this.delete(r.id);
|
|
299
|
+
}
|
|
300
|
+
return rows.length;
|
|
301
|
+
}
|
|
302
|
+
|
|
303
|
+
private async enforceMaxSessions(agentId: string): Promise<void> {
|
|
304
|
+
const rows = await this.driver.query<{ id: string; created_at: string }>(
|
|
305
|
+
`SELECT id, created_at FROM ${this.tableSessions} WHERE agent_id = ? ORDER BY created_at ASC`,
|
|
306
|
+
[agentId]
|
|
307
|
+
);
|
|
308
|
+
const excess = rows.length - this.config.maxSessionsPerAgent;
|
|
309
|
+
if (excess > 0) {
|
|
310
|
+
for (let i = 0; i < excess; i++) {
|
|
311
|
+
await this.delete(rows[i]!.id);
|
|
312
|
+
}
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* SQLite session store. Requires optional peer dependency: better-sqlite3.
|
|
3
|
+
*
|
|
4
|
+
* Install: pnpm add better-sqlite3
|
|
5
|
+
* Then: import { createSqliteSessionStore } from '@confused-ai/core/session';
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { SessionStore } from './types.js';
|
|
9
|
+
import type { SessionStoreConfig } from './types.js';
|
|
10
|
+
import type { SessionDbDriver } from './db-driver.js';
|
|
11
|
+
import { SqlSessionStore } from './sql-store.js';
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Create a SQLite-backed session store.
|
|
15
|
+
* Requires better-sqlite3: npm install better-sqlite3
|
|
16
|
+
*
|
|
17
|
+
* @param filePath - Path to SQLite database file (e.g. ./data/sessions.db)
|
|
18
|
+
* @param config - Optional store config (defaultTtlMs, maxSessionsPerAgent, maxMessagesPerSession, tablePrefix)
|
|
19
|
+
*/
|
|
20
|
+
export async function createSqliteSessionStore(
|
|
21
|
+
filePath: string,
|
|
22
|
+
config?: SessionStoreConfig & { tablePrefix?: string }
|
|
23
|
+
): Promise<SessionStore> {
|
|
24
|
+
let Database: (path: string) => {
|
|
25
|
+
exec: (sql: string) => void;
|
|
26
|
+
prepare: (sql: string) => {
|
|
27
|
+
run: (...params: unknown[]) => { lastInsertRowid: number };
|
|
28
|
+
all: (...params: unknown[]) => unknown[];
|
|
29
|
+
};
|
|
30
|
+
close: () => void;
|
|
31
|
+
};
|
|
32
|
+
try {
|
|
33
|
+
// eslint-disable-next-line @typescript-eslint/no-require-imports
|
|
34
|
+
Database = require('better-sqlite3') as typeof Database;
|
|
35
|
+
} catch {
|
|
36
|
+
throw new Error(
|
|
37
|
+
'createSqliteSessionStore requires better-sqlite3. Install it: npm install better-sqlite3'
|
|
38
|
+
);
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const db = Database(filePath);
|
|
42
|
+
|
|
43
|
+
const driver: SessionDbDriver = {
|
|
44
|
+
async query<T = Record<string, unknown>>(sql: string, params: unknown[] = []): Promise<T[]> {
|
|
45
|
+
const stmt = db.prepare(sql);
|
|
46
|
+
const rows = stmt.all(...params) as T[];
|
|
47
|
+
return rows;
|
|
48
|
+
},
|
|
49
|
+
async run(sql: string, params: unknown[] = []): Promise<void> {
|
|
50
|
+
const stmt = db.prepare(sql);
|
|
51
|
+
stmt.run(...params);
|
|
52
|
+
},
|
|
53
|
+
async exec(sql: string): Promise<void> {
|
|
54
|
+
db.exec(sql);
|
|
55
|
+
},
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const store = new SqlSessionStore(driver, config);
|
|
59
|
+
await store.migrate();
|
|
60
|
+
return store;
|
|
61
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session management types and interfaces
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
import type { EntityId } from '../core/types.js';
|
|
6
|
+
import type { Message } from '../llm/types.js';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Unique session identifier
|
|
10
|
+
*/
|
|
11
|
+
export type SessionId = string;
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Session state
|
|
15
|
+
*/
|
|
16
|
+
export enum SessionState {
|
|
17
|
+
ACTIVE = 'active',
|
|
18
|
+
PAUSED = 'paused',
|
|
19
|
+
COMPLETED = 'completed',
|
|
20
|
+
EXPIRED = 'expired',
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Session metadata
|
|
25
|
+
*/
|
|
26
|
+
export interface SessionMetadata {
|
|
27
|
+
readonly title?: string;
|
|
28
|
+
readonly description?: string;
|
|
29
|
+
readonly tags?: string[];
|
|
30
|
+
readonly custom?: Record<string, unknown>;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Session entry representing a conversation session
|
|
35
|
+
*/
|
|
36
|
+
export interface Session {
|
|
37
|
+
readonly id: SessionId;
|
|
38
|
+
readonly agentId: EntityId;
|
|
39
|
+
readonly userId?: string;
|
|
40
|
+
readonly state: SessionState;
|
|
41
|
+
readonly messages: Message[];
|
|
42
|
+
readonly metadata: SessionMetadata;
|
|
43
|
+
readonly context: Record<string, unknown>;
|
|
44
|
+
readonly createdAt: Date;
|
|
45
|
+
readonly updatedAt: Date;
|
|
46
|
+
readonly expiresAt?: Date;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
/**
|
|
50
|
+
* Run/execution entry within a session
|
|
51
|
+
*/
|
|
52
|
+
export interface SessionRun {
|
|
53
|
+
readonly id: string;
|
|
54
|
+
readonly sessionId: SessionId;
|
|
55
|
+
readonly agentId: EntityId;
|
|
56
|
+
readonly startTime: Date;
|
|
57
|
+
readonly endTime?: Date;
|
|
58
|
+
readonly status: 'running' | 'completed' | 'failed' | 'interrupted';
|
|
59
|
+
readonly steps: number;
|
|
60
|
+
readonly result?: unknown;
|
|
61
|
+
readonly error?: string;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
/**
|
|
65
|
+
* Configuration for session store
|
|
66
|
+
*/
|
|
67
|
+
export interface SessionStoreConfig {
|
|
68
|
+
readonly defaultTtlMs?: number;
|
|
69
|
+
readonly maxSessionsPerAgent?: number;
|
|
70
|
+
readonly maxMessagesPerSession?: number;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/**
|
|
74
|
+
* Query options for session retrieval
|
|
75
|
+
*/
|
|
76
|
+
export interface SessionQuery {
|
|
77
|
+
readonly agentId?: EntityId;
|
|
78
|
+
readonly userId?: string;
|
|
79
|
+
readonly state?: SessionState;
|
|
80
|
+
readonly limit?: number;
|
|
81
|
+
readonly before?: Date;
|
|
82
|
+
readonly after?: Date;
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Session store interface for persisting conversation and run state
|
|
87
|
+
*/
|
|
88
|
+
export interface SessionStore {
|
|
89
|
+
/**
|
|
90
|
+
* Create a new session
|
|
91
|
+
*/
|
|
92
|
+
create(session: Omit<Session, 'id' | 'createdAt' | 'updatedAt'>): Promise<Session>;
|
|
93
|
+
|
|
94
|
+
/**
|
|
95
|
+
* Get a session by ID
|
|
96
|
+
*/
|
|
97
|
+
get(sessionId: SessionId): Promise<Session | null>;
|
|
98
|
+
|
|
99
|
+
/**
|
|
100
|
+
* Update an existing session
|
|
101
|
+
*/
|
|
102
|
+
update(sessionId: SessionId, updates: Partial<Omit<Session, 'id' | 'createdAt'>>): Promise<Session>;
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Delete a session
|
|
106
|
+
*/
|
|
107
|
+
delete(sessionId: SessionId): Promise<boolean>;
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* List sessions matching query
|
|
111
|
+
*/
|
|
112
|
+
list(query?: SessionQuery): Promise<Session[]>;
|
|
113
|
+
|
|
114
|
+
/**
|
|
115
|
+
* Add a message to a session
|
|
116
|
+
*/
|
|
117
|
+
addMessage(sessionId: SessionId, message: Message): Promise<Session>;
|
|
118
|
+
|
|
119
|
+
/**
|
|
120
|
+
* Get messages from a session
|
|
121
|
+
*/
|
|
122
|
+
getMessages(sessionId: SessionId): Promise<Message[]>;
|
|
123
|
+
|
|
124
|
+
/**
|
|
125
|
+
* Clear messages from a session
|
|
126
|
+
*/
|
|
127
|
+
clearMessages(sessionId: SessionId): Promise<Session>;
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* Set session context data
|
|
131
|
+
*/
|
|
132
|
+
setContext(sessionId: SessionId, key: string, value: unknown): Promise<Session>;
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Get session context data
|
|
136
|
+
*/
|
|
137
|
+
getContext(sessionId: SessionId, key: string): Promise<unknown>;
|
|
138
|
+
|
|
139
|
+
/**
|
|
140
|
+
* Record a run within a session
|
|
141
|
+
*/
|
|
142
|
+
recordRun(run: Omit<SessionRun, 'id'>): Promise<SessionRun>;
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Get runs for a session
|
|
146
|
+
*/
|
|
147
|
+
getRuns(sessionId: SessionId): Promise<SessionRun[]>;
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* Clean up expired sessions
|
|
151
|
+
*/
|
|
152
|
+
cleanup(): Promise<number>;
|
|
153
|
+
}
|