@compilr-dev/agents 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1277 -0
- package/dist/agent.d.ts +1272 -0
- package/dist/agent.js +1912 -0
- package/dist/anchors/builtin.d.ts +24 -0
- package/dist/anchors/builtin.js +61 -0
- package/dist/anchors/index.d.ts +6 -0
- package/dist/anchors/index.js +5 -0
- package/dist/anchors/manager.d.ts +115 -0
- package/dist/anchors/manager.js +412 -0
- package/dist/anchors/types.d.ts +168 -0
- package/dist/anchors/types.js +10 -0
- package/dist/context/index.d.ts +12 -0
- package/dist/context/index.js +10 -0
- package/dist/context/manager.d.ts +224 -0
- package/dist/context/manager.js +770 -0
- package/dist/context/types.d.ts +377 -0
- package/dist/context/types.js +7 -0
- package/dist/costs/index.d.ts +8 -0
- package/dist/costs/index.js +7 -0
- package/dist/costs/tracker.d.ts +121 -0
- package/dist/costs/tracker.js +295 -0
- package/dist/costs/types.d.ts +157 -0
- package/dist/costs/types.js +8 -0
- package/dist/errors.d.ts +178 -0
- package/dist/errors.js +249 -0
- package/dist/guardrails/builtin.d.ts +27 -0
- package/dist/guardrails/builtin.js +223 -0
- package/dist/guardrails/index.d.ts +6 -0
- package/dist/guardrails/index.js +5 -0
- package/dist/guardrails/manager.d.ts +117 -0
- package/dist/guardrails/manager.js +288 -0
- package/dist/guardrails/types.d.ts +159 -0
- package/dist/guardrails/types.js +7 -0
- package/dist/hooks/index.d.ts +31 -0
- package/dist/hooks/index.js +29 -0
- package/dist/hooks/manager.d.ts +147 -0
- package/dist/hooks/manager.js +600 -0
- package/dist/hooks/types.d.ts +368 -0
- package/dist/hooks/types.js +12 -0
- package/dist/index.d.ts +45 -0
- package/dist/index.js +73 -0
- package/dist/mcp/client.d.ts +93 -0
- package/dist/mcp/client.js +287 -0
- package/dist/mcp/errors.d.ts +60 -0
- package/dist/mcp/errors.js +78 -0
- package/dist/mcp/index.d.ts +43 -0
- package/dist/mcp/index.js +45 -0
- package/dist/mcp/manager.d.ts +120 -0
- package/dist/mcp/manager.js +276 -0
- package/dist/mcp/tools.d.ts +54 -0
- package/dist/mcp/tools.js +99 -0
- package/dist/mcp/types.d.ts +150 -0
- package/dist/mcp/types.js +40 -0
- package/dist/memory/index.d.ts +8 -0
- package/dist/memory/index.js +7 -0
- package/dist/memory/loader.d.ts +114 -0
- package/dist/memory/loader.js +463 -0
- package/dist/memory/types.d.ts +182 -0
- package/dist/memory/types.js +8 -0
- package/dist/messages/index.d.ts +82 -0
- package/dist/messages/index.js +155 -0
- package/dist/permissions/index.d.ts +5 -0
- package/dist/permissions/index.js +4 -0
- package/dist/permissions/manager.d.ts +125 -0
- package/dist/permissions/manager.js +379 -0
- package/dist/permissions/types.d.ts +162 -0
- package/dist/permissions/types.js +7 -0
- package/dist/providers/claude.d.ts +90 -0
- package/dist/providers/claude.js +348 -0
- package/dist/providers/index.d.ts +8 -0
- package/dist/providers/index.js +11 -0
- package/dist/providers/mock.d.ts +133 -0
- package/dist/providers/mock.js +204 -0
- package/dist/providers/types.d.ts +168 -0
- package/dist/providers/types.js +4 -0
- package/dist/rate-limit/index.d.ts +45 -0
- package/dist/rate-limit/index.js +47 -0
- package/dist/rate-limit/limiter.d.ts +104 -0
- package/dist/rate-limit/limiter.js +326 -0
- package/dist/rate-limit/provider-wrapper.d.ts +112 -0
- package/dist/rate-limit/provider-wrapper.js +201 -0
- package/dist/rate-limit/retry.d.ts +108 -0
- package/dist/rate-limit/retry.js +287 -0
- package/dist/rate-limit/types.d.ts +181 -0
- package/dist/rate-limit/types.js +22 -0
- package/dist/rehearsal/file-analyzer.d.ts +22 -0
- package/dist/rehearsal/file-analyzer.js +351 -0
- package/dist/rehearsal/git-analyzer.d.ts +22 -0
- package/dist/rehearsal/git-analyzer.js +472 -0
- package/dist/rehearsal/index.d.ts +35 -0
- package/dist/rehearsal/index.js +36 -0
- package/dist/rehearsal/manager.d.ts +100 -0
- package/dist/rehearsal/manager.js +290 -0
- package/dist/rehearsal/types.d.ts +235 -0
- package/dist/rehearsal/types.js +8 -0
- package/dist/skills/index.d.ts +160 -0
- package/dist/skills/index.js +282 -0
- package/dist/state/agent-state.d.ts +41 -0
- package/dist/state/agent-state.js +88 -0
- package/dist/state/checkpointer.d.ts +110 -0
- package/dist/state/checkpointer.js +362 -0
- package/dist/state/errors.d.ts +66 -0
- package/dist/state/errors.js +88 -0
- package/dist/state/index.d.ts +35 -0
- package/dist/state/index.js +37 -0
- package/dist/state/serializer.d.ts +55 -0
- package/dist/state/serializer.js +172 -0
- package/dist/state/types.d.ts +312 -0
- package/dist/state/types.js +14 -0
- package/dist/tools/builtin/bash-output.d.ts +61 -0
- package/dist/tools/builtin/bash-output.js +90 -0
- package/dist/tools/builtin/bash.d.ts +150 -0
- package/dist/tools/builtin/bash.js +354 -0
- package/dist/tools/builtin/edit.d.ts +50 -0
- package/dist/tools/builtin/edit.js +215 -0
- package/dist/tools/builtin/glob.d.ts +62 -0
- package/dist/tools/builtin/glob.js +244 -0
- package/dist/tools/builtin/grep.d.ts +74 -0
- package/dist/tools/builtin/grep.js +363 -0
- package/dist/tools/builtin/index.d.ts +44 -0
- package/dist/tools/builtin/index.js +69 -0
- package/dist/tools/builtin/kill-shell.d.ts +44 -0
- package/dist/tools/builtin/kill-shell.js +80 -0
- package/dist/tools/builtin/read-file.d.ts +57 -0
- package/dist/tools/builtin/read-file.js +184 -0
- package/dist/tools/builtin/shell-manager.d.ts +176 -0
- package/dist/tools/builtin/shell-manager.js +337 -0
- package/dist/tools/builtin/task.d.ts +202 -0
- package/dist/tools/builtin/task.js +350 -0
- package/dist/tools/builtin/todo.d.ts +207 -0
- package/dist/tools/builtin/todo.js +453 -0
- package/dist/tools/builtin/utils.d.ts +27 -0
- package/dist/tools/builtin/utils.js +70 -0
- package/dist/tools/builtin/web-fetch.d.ts +96 -0
- package/dist/tools/builtin/web-fetch.js +290 -0
- package/dist/tools/builtin/write-file.d.ts +54 -0
- package/dist/tools/builtin/write-file.js +147 -0
- package/dist/tools/define.d.ts +60 -0
- package/dist/tools/define.js +65 -0
- package/dist/tools/index.d.ts +10 -0
- package/dist/tools/index.js +37 -0
- package/dist/tools/registry.d.ts +79 -0
- package/dist/tools/registry.js +151 -0
- package/dist/tools/types.d.ts +59 -0
- package/dist/tools/types.js +4 -0
- package/dist/tracing/hooks.d.ts +58 -0
- package/dist/tracing/hooks.js +377 -0
- package/dist/tracing/index.d.ts +51 -0
- package/dist/tracing/index.js +55 -0
- package/dist/tracing/logging.d.ts +78 -0
- package/dist/tracing/logging.js +310 -0
- package/dist/tracing/manager.d.ts +160 -0
- package/dist/tracing/manager.js +468 -0
- package/dist/tracing/otel.d.ts +102 -0
- package/dist/tracing/otel.js +246 -0
- package/dist/tracing/types.d.ts +346 -0
- package/dist/tracing/types.js +38 -0
- package/dist/utils/index.d.ts +23 -0
- package/dist/utils/index.js +44 -0
- package/package.json +79 -0
|
@@ -0,0 +1,362 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Checkpointer Implementations
|
|
3
|
+
*
|
|
4
|
+
* Built-in checkpointers for state persistence.
|
|
5
|
+
*/
|
|
6
|
+
import { promises as fs } from 'node:fs';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
import { JsonSerializer, defaultSerializer } from './serializer.js';
|
|
9
|
+
import { StateError } from './errors.js';
|
|
10
|
+
// ============================================================
|
|
11
|
+
// Helper Functions
|
|
12
|
+
// ============================================================
|
|
13
|
+
/**
|
|
14
|
+
* Extract preview text from a message
|
|
15
|
+
*/
|
|
16
|
+
function getMessagePreview(content, maxLength = 100) {
|
|
17
|
+
if (typeof content === 'string') {
|
|
18
|
+
return content.length > maxLength ? content.substring(0, maxLength) + '...' : content;
|
|
19
|
+
}
|
|
20
|
+
if (Array.isArray(content)) {
|
|
21
|
+
// Find first text block
|
|
22
|
+
for (const block of content) {
|
|
23
|
+
if (block && typeof block === 'object') {
|
|
24
|
+
const typedBlock = block;
|
|
25
|
+
if (typedBlock.type === 'text' && typeof typedBlock.text === 'string') {
|
|
26
|
+
const text = typedBlock.text;
|
|
27
|
+
return text.length > maxLength ? text.substring(0, maxLength) + '...' : text;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
return '';
|
|
33
|
+
}
|
|
34
|
+
/**
|
|
35
|
+
* Build session metadata from state
|
|
36
|
+
*/
|
|
37
|
+
function buildMetadata(state, overrides) {
|
|
38
|
+
// Find last user and assistant messages
|
|
39
|
+
let lastUserMessage;
|
|
40
|
+
let lastAssistantMessage;
|
|
41
|
+
for (let i = state.messages.length - 1; i >= 0; i--) {
|
|
42
|
+
const msg = state.messages[i];
|
|
43
|
+
if (!lastUserMessage && msg.role === 'user') {
|
|
44
|
+
lastUserMessage = getMessagePreview(msg.content);
|
|
45
|
+
}
|
|
46
|
+
if (!lastAssistantMessage && msg.role === 'assistant') {
|
|
47
|
+
lastAssistantMessage = getMessagePreview(msg.content);
|
|
48
|
+
}
|
|
49
|
+
if (lastUserMessage && lastAssistantMessage)
|
|
50
|
+
break;
|
|
51
|
+
}
|
|
52
|
+
return {
|
|
53
|
+
sessionId: state.sessionId,
|
|
54
|
+
createdAt: state.createdAt,
|
|
55
|
+
updatedAt: state.updatedAt,
|
|
56
|
+
messageCount: state.messages.length,
|
|
57
|
+
lastUserMessage,
|
|
58
|
+
lastAssistantMessage,
|
|
59
|
+
...overrides,
|
|
60
|
+
};
|
|
61
|
+
}
|
|
62
|
+
/**
|
|
63
|
+
* Convert metadata to session info
|
|
64
|
+
*/
|
|
65
|
+
function toSessionInfo(metadata) {
|
|
66
|
+
return {
|
|
67
|
+
sessionId: metadata.sessionId,
|
|
68
|
+
name: metadata.name,
|
|
69
|
+
createdAt: metadata.createdAt,
|
|
70
|
+
updatedAt: metadata.updatedAt,
|
|
71
|
+
messageCount: metadata.messageCount,
|
|
72
|
+
preview: metadata.lastAssistantMessage || metadata.lastUserMessage,
|
|
73
|
+
tags: metadata.tags,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Validate session ID format
|
|
78
|
+
*/
|
|
79
|
+
function validateSessionId(sessionId) {
|
|
80
|
+
if (!sessionId || typeof sessionId !== 'string') {
|
|
81
|
+
throw StateError.invalidSessionId(sessionId);
|
|
82
|
+
}
|
|
83
|
+
// Allow alphanumeric, hyphens, underscores
|
|
84
|
+
if (!/^[a-zA-Z0-9_-]+$/.test(sessionId)) {
|
|
85
|
+
throw StateError.invalidSessionId(sessionId);
|
|
86
|
+
}
|
|
87
|
+
// Max length check
|
|
88
|
+
if (sessionId.length > 128) {
|
|
89
|
+
throw StateError.invalidSessionId(`Session ID too long: ${sessionId.substring(0, 20)}...`);
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
// ============================================================
|
|
93
|
+
// Memory Checkpointer
|
|
94
|
+
// ============================================================
|
|
95
|
+
/**
|
|
96
|
+
* In-memory checkpointer for development and testing.
|
|
97
|
+
* State is lost when the process exits.
|
|
98
|
+
*/
|
|
99
|
+
export class MemoryCheckpointer {
|
|
100
|
+
sessions = new Map();
|
|
101
|
+
/**
|
|
102
|
+
* Save state to memory
|
|
103
|
+
*
|
|
104
|
+
* Pre-validates state before saving to prevent corrupted checkpoints.
|
|
105
|
+
* This catches issues like invalid data types that would fail on reload.
|
|
106
|
+
*/
|
|
107
|
+
save(sessionId, state, metadataOverrides) {
|
|
108
|
+
validateSessionId(sessionId);
|
|
109
|
+
// Pre-checkpoint validation: validate state BEFORE saving
|
|
110
|
+
// This prevents corrupted checkpoints that can't be loaded later
|
|
111
|
+
// (See LangGraph issue #6491)
|
|
112
|
+
defaultSerializer.validate(state);
|
|
113
|
+
const metadata = buildMetadata(state, metadataOverrides);
|
|
114
|
+
this.sessions.set(sessionId, { state: { ...state }, metadata });
|
|
115
|
+
return Promise.resolve();
|
|
116
|
+
}
|
|
117
|
+
/**
|
|
118
|
+
* Load state from memory
|
|
119
|
+
*/
|
|
120
|
+
load(sessionId) {
|
|
121
|
+
validateSessionId(sessionId);
|
|
122
|
+
const entry = this.sessions.get(sessionId);
|
|
123
|
+
return Promise.resolve(entry ? { ...entry.state } : null);
|
|
124
|
+
}
|
|
125
|
+
/**
|
|
126
|
+
* List all sessions
|
|
127
|
+
*/
|
|
128
|
+
list(options) {
|
|
129
|
+
let sessions = Array.from(this.sessions.values()).map((entry) => toSessionInfo(entry.metadata));
|
|
130
|
+
// Filter by tags
|
|
131
|
+
if (options?.tags?.length) {
|
|
132
|
+
const tags = options.tags;
|
|
133
|
+
sessions = sessions.filter((s) => tags.every((tag) => s.tags?.includes(tag)));
|
|
134
|
+
}
|
|
135
|
+
// Sort
|
|
136
|
+
const orderBy = options?.orderBy ?? 'updatedAt';
|
|
137
|
+
const order = options?.order ?? 'desc';
|
|
138
|
+
sessions.sort((a, b) => {
|
|
139
|
+
const aDate = new Date(a[orderBy]).getTime();
|
|
140
|
+
const bDate = new Date(b[orderBy]).getTime();
|
|
141
|
+
return order === 'desc' ? bDate - aDate : aDate - bDate;
|
|
142
|
+
});
|
|
143
|
+
// Pagination
|
|
144
|
+
const offset = options?.offset ?? 0;
|
|
145
|
+
const limit = options?.limit ?? sessions.length;
|
|
146
|
+
return Promise.resolve(sessions.slice(offset, offset + limit));
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* Delete a session
|
|
150
|
+
*/
|
|
151
|
+
delete(sessionId) {
|
|
152
|
+
validateSessionId(sessionId);
|
|
153
|
+
return Promise.resolve(this.sessions.delete(sessionId));
|
|
154
|
+
}
|
|
155
|
+
/**
|
|
156
|
+
* Check if session exists
|
|
157
|
+
*/
|
|
158
|
+
exists(sessionId) {
|
|
159
|
+
validateSessionId(sessionId);
|
|
160
|
+
return Promise.resolve(this.sessions.has(sessionId));
|
|
161
|
+
}
|
|
162
|
+
/**
|
|
163
|
+
* Get session metadata
|
|
164
|
+
*/
|
|
165
|
+
getMetadata(sessionId) {
|
|
166
|
+
validateSessionId(sessionId);
|
|
167
|
+
const entry = this.sessions.get(sessionId);
|
|
168
|
+
return Promise.resolve(entry ? { ...entry.metadata } : null);
|
|
169
|
+
}
|
|
170
|
+
/**
|
|
171
|
+
* Clear all sessions (useful for testing)
|
|
172
|
+
*/
|
|
173
|
+
clear() {
|
|
174
|
+
this.sessions.clear();
|
|
175
|
+
}
|
|
176
|
+
/**
|
|
177
|
+
* Get current session count
|
|
178
|
+
*/
|
|
179
|
+
get size() {
|
|
180
|
+
return this.sessions.size;
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
/**
|
|
184
|
+
* File-based checkpointer for simple persistence.
|
|
185
|
+
* Stores each session as a separate JSON file.
|
|
186
|
+
*/
|
|
187
|
+
export class FileCheckpointer {
|
|
188
|
+
baseDir;
|
|
189
|
+
serializer;
|
|
190
|
+
extension;
|
|
191
|
+
constructor(baseDir, options) {
|
|
192
|
+
this.baseDir = baseDir;
|
|
193
|
+
this.serializer = options?.serializer || new JsonSerializer();
|
|
194
|
+
this.extension = options?.extension || '.json';
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* Get file path for a session
|
|
198
|
+
*/
|
|
199
|
+
getStatePath(sessionId) {
|
|
200
|
+
return path.join(this.baseDir, `${sessionId}${this.extension}`);
|
|
201
|
+
}
|
|
202
|
+
/**
|
|
203
|
+
* Get metadata file path for a session
|
|
204
|
+
*/
|
|
205
|
+
getMetadataPath(sessionId) {
|
|
206
|
+
return path.join(this.baseDir, `${sessionId}.meta${this.extension}`);
|
|
207
|
+
}
|
|
208
|
+
/**
|
|
209
|
+
* Ensure base directory exists
|
|
210
|
+
*/
|
|
211
|
+
async ensureDir() {
|
|
212
|
+
try {
|
|
213
|
+
await fs.mkdir(this.baseDir, { recursive: true });
|
|
214
|
+
}
|
|
215
|
+
catch (error) {
|
|
216
|
+
throw StateError.storage(`Failed to create directory: ${this.baseDir}`, error instanceof Error ? error : undefined);
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* Save state to file
|
|
221
|
+
*
|
|
222
|
+
* Pre-validates state before saving to prevent corrupted checkpoints.
|
|
223
|
+
* This catches issues like invalid data types that would fail on reload.
|
|
224
|
+
*/
|
|
225
|
+
async save(sessionId, state, metadataOverrides) {
|
|
226
|
+
validateSessionId(sessionId);
|
|
227
|
+
// Pre-checkpoint validation: validate state BEFORE saving
|
|
228
|
+
// This prevents corrupted checkpoints that can't be loaded later
|
|
229
|
+
// (See LangGraph issue #6491)
|
|
230
|
+
this.serializer.validate(state);
|
|
231
|
+
await this.ensureDir();
|
|
232
|
+
const metadata = buildMetadata(state, metadataOverrides);
|
|
233
|
+
try {
|
|
234
|
+
// Save state
|
|
235
|
+
const stateData = this.serializer.serialize(state);
|
|
236
|
+
await fs.writeFile(this.getStatePath(sessionId), stateData, 'utf-8');
|
|
237
|
+
// Save metadata separately for fast listing
|
|
238
|
+
const metaData = JSON.stringify(metadata, null, 2);
|
|
239
|
+
await fs.writeFile(this.getMetadataPath(sessionId), metaData, 'utf-8');
|
|
240
|
+
}
|
|
241
|
+
catch (error) {
|
|
242
|
+
throw StateError.storage(`Failed to save session: ${sessionId}`, error instanceof Error ? error : undefined);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Load state from file
|
|
247
|
+
*/
|
|
248
|
+
async load(sessionId) {
|
|
249
|
+
validateSessionId(sessionId);
|
|
250
|
+
try {
|
|
251
|
+
const data = await fs.readFile(this.getStatePath(sessionId), 'utf-8');
|
|
252
|
+
return this.serializer.deserialize(data);
|
|
253
|
+
}
|
|
254
|
+
catch (error) {
|
|
255
|
+
if (error.code === 'ENOENT') {
|
|
256
|
+
return null;
|
|
257
|
+
}
|
|
258
|
+
throw StateError.storage(`Failed to load session: ${sessionId}`, error instanceof Error ? error : undefined);
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
/**
|
|
262
|
+
* List all sessions
|
|
263
|
+
*/
|
|
264
|
+
async list(options) {
|
|
265
|
+
try {
|
|
266
|
+
await this.ensureDir();
|
|
267
|
+
const files = await fs.readdir(this.baseDir);
|
|
268
|
+
// Find all metadata files
|
|
269
|
+
const metaFiles = files.filter((f) => f.endsWith(`.meta${this.extension}`));
|
|
270
|
+
let sessions = [];
|
|
271
|
+
for (const file of metaFiles) {
|
|
272
|
+
try {
|
|
273
|
+
const metaPath = path.join(this.baseDir, file);
|
|
274
|
+
const data = await fs.readFile(metaPath, 'utf-8');
|
|
275
|
+
const metadata = JSON.parse(data);
|
|
276
|
+
sessions.push(toSessionInfo(metadata));
|
|
277
|
+
}
|
|
278
|
+
catch {
|
|
279
|
+
// Skip invalid metadata files
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
// Filter by tags
|
|
283
|
+
if (options?.tags?.length) {
|
|
284
|
+
const tags = options.tags;
|
|
285
|
+
sessions = sessions.filter((s) => tags.every((tag) => s.tags?.includes(tag)));
|
|
286
|
+
}
|
|
287
|
+
// Sort
|
|
288
|
+
const orderBy = options?.orderBy ?? 'updatedAt';
|
|
289
|
+
const order = options?.order ?? 'desc';
|
|
290
|
+
sessions.sort((a, b) => {
|
|
291
|
+
const aDate = new Date(a[orderBy]).getTime();
|
|
292
|
+
const bDate = new Date(b[orderBy]).getTime();
|
|
293
|
+
return order === 'desc' ? bDate - aDate : aDate - bDate;
|
|
294
|
+
});
|
|
295
|
+
// Pagination
|
|
296
|
+
const offset = options?.offset || 0;
|
|
297
|
+
const limit = options?.limit || sessions.length;
|
|
298
|
+
return sessions.slice(offset, offset + limit);
|
|
299
|
+
}
|
|
300
|
+
catch (error) {
|
|
301
|
+
if (error.code === 'ENOENT') {
|
|
302
|
+
return [];
|
|
303
|
+
}
|
|
304
|
+
throw StateError.storage('Failed to list sessions', error instanceof Error ? error : undefined);
|
|
305
|
+
}
|
|
306
|
+
}
|
|
307
|
+
/**
|
|
308
|
+
* Delete a session
|
|
309
|
+
*/
|
|
310
|
+
async delete(sessionId) {
|
|
311
|
+
validateSessionId(sessionId);
|
|
312
|
+
let deleted = false;
|
|
313
|
+
try {
|
|
314
|
+
await fs.unlink(this.getStatePath(sessionId));
|
|
315
|
+
deleted = true;
|
|
316
|
+
}
|
|
317
|
+
catch (error) {
|
|
318
|
+
if (error.code !== 'ENOENT') {
|
|
319
|
+
throw StateError.storage(`Failed to delete session state: ${sessionId}`, error instanceof Error ? error : undefined);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
try {
|
|
323
|
+
await fs.unlink(this.getMetadataPath(sessionId));
|
|
324
|
+
deleted = true;
|
|
325
|
+
}
|
|
326
|
+
catch (error) {
|
|
327
|
+
if (error.code !== 'ENOENT') {
|
|
328
|
+
throw StateError.storage(`Failed to delete session metadata: ${sessionId}`, error instanceof Error ? error : undefined);
|
|
329
|
+
}
|
|
330
|
+
}
|
|
331
|
+
return deleted;
|
|
332
|
+
}
|
|
333
|
+
/**
|
|
334
|
+
* Check if session exists
|
|
335
|
+
*/
|
|
336
|
+
async exists(sessionId) {
|
|
337
|
+
validateSessionId(sessionId);
|
|
338
|
+
try {
|
|
339
|
+
await fs.access(this.getStatePath(sessionId));
|
|
340
|
+
return true;
|
|
341
|
+
}
|
|
342
|
+
catch {
|
|
343
|
+
return false;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
/**
|
|
347
|
+
* Get session metadata
|
|
348
|
+
*/
|
|
349
|
+
async getMetadata(sessionId) {
|
|
350
|
+
validateSessionId(sessionId);
|
|
351
|
+
try {
|
|
352
|
+
const data = await fs.readFile(this.getMetadataPath(sessionId), 'utf-8');
|
|
353
|
+
return JSON.parse(data);
|
|
354
|
+
}
|
|
355
|
+
catch (error) {
|
|
356
|
+
if (error.code === 'ENOENT') {
|
|
357
|
+
return null;
|
|
358
|
+
}
|
|
359
|
+
throw StateError.storage(`Failed to load session metadata: ${sessionId}`, error instanceof Error ? error : undefined);
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* State Management Errors
|
|
3
|
+
*
|
|
4
|
+
* Custom error types for state serialization and persistence.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Error codes for state management operations.
|
|
8
|
+
*/
|
|
9
|
+
export declare enum StateErrorCode {
|
|
10
|
+
/** Failed to serialize state to string */
|
|
11
|
+
SERIALIZATION_FAILED = "SERIALIZATION_FAILED",
|
|
12
|
+
/** Failed to deserialize string to state */
|
|
13
|
+
DESERIALIZATION_FAILED = "DESERIALIZATION_FAILED",
|
|
14
|
+
/** Session not found in storage */
|
|
15
|
+
SESSION_NOT_FOUND = "SESSION_NOT_FOUND",
|
|
16
|
+
/** Storage operation failed (I/O, permissions, etc.) */
|
|
17
|
+
STORAGE_ERROR = "STORAGE_ERROR",
|
|
18
|
+
/** State version mismatch (needs migration) */
|
|
19
|
+
VERSION_MISMATCH = "VERSION_MISMATCH",
|
|
20
|
+
/** Invalid state structure */
|
|
21
|
+
INVALID_STATE = "INVALID_STATE",
|
|
22
|
+
/** Session ID format is invalid */
|
|
23
|
+
INVALID_SESSION_ID = "INVALID_SESSION_ID"
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Base error class for state management operations.
|
|
27
|
+
*/
|
|
28
|
+
export declare class StateError extends Error {
|
|
29
|
+
/**
|
|
30
|
+
* Error code for programmatic handling
|
|
31
|
+
*/
|
|
32
|
+
readonly code: StateErrorCode;
|
|
33
|
+
/**
|
|
34
|
+
* Original error that caused this error (if any)
|
|
35
|
+
*/
|
|
36
|
+
readonly cause?: Error;
|
|
37
|
+
constructor(message: string, code: StateErrorCode, cause?: Error);
|
|
38
|
+
/**
|
|
39
|
+
* Create a serialization error
|
|
40
|
+
*/
|
|
41
|
+
static serialization(message: string, cause?: Error): StateError;
|
|
42
|
+
/**
|
|
43
|
+
* Create a deserialization error
|
|
44
|
+
*/
|
|
45
|
+
static deserialization(message: string, cause?: Error): StateError;
|
|
46
|
+
/**
|
|
47
|
+
* Create a session not found error
|
|
48
|
+
*/
|
|
49
|
+
static sessionNotFound(sessionId: string): StateError;
|
|
50
|
+
/**
|
|
51
|
+
* Create a storage error
|
|
52
|
+
*/
|
|
53
|
+
static storage(message: string, cause?: Error): StateError;
|
|
54
|
+
/**
|
|
55
|
+
* Create a version mismatch error
|
|
56
|
+
*/
|
|
57
|
+
static versionMismatch(expected: number, actual: number): StateError;
|
|
58
|
+
/**
|
|
59
|
+
* Create an invalid state error
|
|
60
|
+
*/
|
|
61
|
+
static invalidState(message: string): StateError;
|
|
62
|
+
/**
|
|
63
|
+
* Create an invalid session ID error
|
|
64
|
+
*/
|
|
65
|
+
static invalidSessionId(sessionId: string): StateError;
|
|
66
|
+
}
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* State Management Errors
|
|
3
|
+
*
|
|
4
|
+
* Custom error types for state serialization and persistence.
|
|
5
|
+
*/
|
|
6
|
+
/**
|
|
7
|
+
* Error codes for state management operations.
|
|
8
|
+
*/
|
|
9
|
+
export var StateErrorCode;
|
|
10
|
+
(function (StateErrorCode) {
|
|
11
|
+
/** Failed to serialize state to string */
|
|
12
|
+
StateErrorCode["SERIALIZATION_FAILED"] = "SERIALIZATION_FAILED";
|
|
13
|
+
/** Failed to deserialize string to state */
|
|
14
|
+
StateErrorCode["DESERIALIZATION_FAILED"] = "DESERIALIZATION_FAILED";
|
|
15
|
+
/** Session not found in storage */
|
|
16
|
+
StateErrorCode["SESSION_NOT_FOUND"] = "SESSION_NOT_FOUND";
|
|
17
|
+
/** Storage operation failed (I/O, permissions, etc.) */
|
|
18
|
+
StateErrorCode["STORAGE_ERROR"] = "STORAGE_ERROR";
|
|
19
|
+
/** State version mismatch (needs migration) */
|
|
20
|
+
StateErrorCode["VERSION_MISMATCH"] = "VERSION_MISMATCH";
|
|
21
|
+
/** Invalid state structure */
|
|
22
|
+
StateErrorCode["INVALID_STATE"] = "INVALID_STATE";
|
|
23
|
+
/** Session ID format is invalid */
|
|
24
|
+
StateErrorCode["INVALID_SESSION_ID"] = "INVALID_SESSION_ID";
|
|
25
|
+
})(StateErrorCode || (StateErrorCode = {}));
|
|
26
|
+
/**
|
|
27
|
+
* Base error class for state management operations.
|
|
28
|
+
*/
|
|
29
|
+
export class StateError extends Error {
|
|
30
|
+
/**
|
|
31
|
+
* Error code for programmatic handling
|
|
32
|
+
*/
|
|
33
|
+
code;
|
|
34
|
+
/**
|
|
35
|
+
* Original error that caused this error (if any)
|
|
36
|
+
*/
|
|
37
|
+
cause;
|
|
38
|
+
constructor(message, code, cause) {
|
|
39
|
+
super(message);
|
|
40
|
+
this.name = 'StateError';
|
|
41
|
+
this.code = code;
|
|
42
|
+
this.cause = cause;
|
|
43
|
+
// Maintain proper stack trace for where error was thrown (V8)
|
|
44
|
+
Error.captureStackTrace(this, StateError);
|
|
45
|
+
}
|
|
46
|
+
/**
|
|
47
|
+
* Create a serialization error
|
|
48
|
+
*/
|
|
49
|
+
static serialization(message, cause) {
|
|
50
|
+
return new StateError(`Serialization failed: ${message}`, StateErrorCode.SERIALIZATION_FAILED, cause);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Create a deserialization error
|
|
54
|
+
*/
|
|
55
|
+
static deserialization(message, cause) {
|
|
56
|
+
return new StateError(`Deserialization failed: ${message}`, StateErrorCode.DESERIALIZATION_FAILED, cause);
|
|
57
|
+
}
|
|
58
|
+
/**
|
|
59
|
+
* Create a session not found error
|
|
60
|
+
*/
|
|
61
|
+
static sessionNotFound(sessionId) {
|
|
62
|
+
return new StateError(`Session not found: ${sessionId}`, StateErrorCode.SESSION_NOT_FOUND);
|
|
63
|
+
}
|
|
64
|
+
/**
|
|
65
|
+
* Create a storage error
|
|
66
|
+
*/
|
|
67
|
+
static storage(message, cause) {
|
|
68
|
+
return new StateError(`Storage error: ${message}`, StateErrorCode.STORAGE_ERROR, cause);
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* Create a version mismatch error
|
|
72
|
+
*/
|
|
73
|
+
static versionMismatch(expected, actual) {
|
|
74
|
+
return new StateError(`Version mismatch: expected ${String(expected)}, got ${String(actual)}`, StateErrorCode.VERSION_MISMATCH);
|
|
75
|
+
}
|
|
76
|
+
/**
|
|
77
|
+
* Create an invalid state error
|
|
78
|
+
*/
|
|
79
|
+
static invalidState(message) {
|
|
80
|
+
return new StateError(`Invalid state: ${message}`, StateErrorCode.INVALID_STATE);
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Create an invalid session ID error
|
|
84
|
+
*/
|
|
85
|
+
static invalidSessionId(sessionId) {
|
|
86
|
+
return new StateError(`Invalid session ID: ${sessionId}`, StateErrorCode.INVALID_SESSION_ID);
|
|
87
|
+
}
|
|
88
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* State Management Module
|
|
3
|
+
*
|
|
4
|
+
* Provides conversation persistence and session management.
|
|
5
|
+
* Inspired by LangGraph's checkpointer pattern.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { FileCheckpointer, JsonSerializer } from '@compilr-dev/agents';
|
|
10
|
+
*
|
|
11
|
+
* // Create checkpointer
|
|
12
|
+
* const checkpointer = new FileCheckpointer('~/.myapp/sessions/');
|
|
13
|
+
*
|
|
14
|
+
* // Use with agent
|
|
15
|
+
* const agent = new Agent({
|
|
16
|
+
* provider,
|
|
17
|
+
* checkpointer,
|
|
18
|
+
* autoCheckpoint: true,
|
|
19
|
+
* });
|
|
20
|
+
*
|
|
21
|
+
* // Or manually checkpoint
|
|
22
|
+
* await agent.run('Hello!');
|
|
23
|
+
* const sessionId = await agent.checkpoint();
|
|
24
|
+
*
|
|
25
|
+
* // Resume later
|
|
26
|
+
* const resumed = await Agent.resume(sessionId, { provider, checkpointer });
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export type { AgentState, SessionMetadata, SessionInfo, StateSerializer, Checkpointer, CheckpointerWithPending, PendingWrite, ListSessionsOptions, ResumeOptions, FromStateOptions, } from './types.js';
|
|
30
|
+
export { CURRENT_STATE_VERSION } from './types.js';
|
|
31
|
+
export { StateError, StateErrorCode } from './errors.js';
|
|
32
|
+
export { JsonSerializer, CompactJsonSerializer, defaultSerializer } from './serializer.js';
|
|
33
|
+
export { MemoryCheckpointer, FileCheckpointer } from './checkpointer.js';
|
|
34
|
+
export type { FileCheckpointerOptions } from './checkpointer.js';
|
|
35
|
+
export { generateSessionId, createEmptyState, createAgentState, serializeTodos, deserializeTodos, touchState, } from './agent-state.js';
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* State Management Module
|
|
3
|
+
*
|
|
4
|
+
* Provides conversation persistence and session management.
|
|
5
|
+
* Inspired by LangGraph's checkpointer pattern.
|
|
6
|
+
*
|
|
7
|
+
* @example
|
|
8
|
+
* ```typescript
|
|
9
|
+
* import { FileCheckpointer, JsonSerializer } from '@compilr-dev/agents';
|
|
10
|
+
*
|
|
11
|
+
* // Create checkpointer
|
|
12
|
+
* const checkpointer = new FileCheckpointer('~/.myapp/sessions/');
|
|
13
|
+
*
|
|
14
|
+
* // Use with agent
|
|
15
|
+
* const agent = new Agent({
|
|
16
|
+
* provider,
|
|
17
|
+
* checkpointer,
|
|
18
|
+
* autoCheckpoint: true,
|
|
19
|
+
* });
|
|
20
|
+
*
|
|
21
|
+
* // Or manually checkpoint
|
|
22
|
+
* await agent.run('Hello!');
|
|
23
|
+
* const sessionId = await agent.checkpoint();
|
|
24
|
+
*
|
|
25
|
+
* // Resume later
|
|
26
|
+
* const resumed = await Agent.resume(sessionId, { provider, checkpointer });
|
|
27
|
+
* ```
|
|
28
|
+
*/
|
|
29
|
+
export { CURRENT_STATE_VERSION } from './types.js';
|
|
30
|
+
// Errors
|
|
31
|
+
export { StateError, StateErrorCode } from './errors.js';
|
|
32
|
+
// Serializers
|
|
33
|
+
export { JsonSerializer, CompactJsonSerializer, defaultSerializer } from './serializer.js';
|
|
34
|
+
// Checkpointers
|
|
35
|
+
export { MemoryCheckpointer, FileCheckpointer } from './checkpointer.js';
|
|
36
|
+
// Agent state utilities
|
|
37
|
+
export { generateSessionId, createEmptyState, createAgentState, serializeTodos, deserializeTodos, touchState, } from './agent-state.js';
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* State Serializers
|
|
3
|
+
*
|
|
4
|
+
* Implementations for serializing/deserializing AgentState.
|
|
5
|
+
*/
|
|
6
|
+
import type { AgentState, StateSerializer } from './types.js';
|
|
7
|
+
/**
|
|
8
|
+
* JSON-based state serializer.
|
|
9
|
+
* Handles standard JSON serialization with validation.
|
|
10
|
+
*/
|
|
11
|
+
export declare class JsonSerializer implements StateSerializer {
|
|
12
|
+
readonly version = "1.0";
|
|
13
|
+
/**
|
|
14
|
+
* Serialize agent state to JSON string
|
|
15
|
+
*/
|
|
16
|
+
serialize(state: AgentState): string;
|
|
17
|
+
/**
|
|
18
|
+
* Deserialize JSON string to agent state
|
|
19
|
+
*/
|
|
20
|
+
deserialize(data: string): AgentState;
|
|
21
|
+
/**
|
|
22
|
+
* Validate state before serialization (public interface method).
|
|
23
|
+
* This is called by checkpointers before saving to prevent corrupted checkpoints.
|
|
24
|
+
* @throws StateError if state is invalid
|
|
25
|
+
*/
|
|
26
|
+
validate(state: AgentState): void;
|
|
27
|
+
/**
|
|
28
|
+
* Validate that parsed data is a valid AgentState
|
|
29
|
+
*/
|
|
30
|
+
private validateParsed;
|
|
31
|
+
/**
|
|
32
|
+
* Migrate from older versions (currently no-op for v1)
|
|
33
|
+
*/
|
|
34
|
+
migrate(data: string, fromVersion: string): string;
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* Compact JSON serializer (no pretty printing).
|
|
38
|
+
* Useful for storage-constrained environments.
|
|
39
|
+
*/
|
|
40
|
+
export declare class CompactJsonSerializer implements StateSerializer {
|
|
41
|
+
readonly version = "1.0";
|
|
42
|
+
serialize(state: AgentState): string;
|
|
43
|
+
deserialize(data: string): AgentState;
|
|
44
|
+
/**
|
|
45
|
+
* Validate state before serialization (public interface method).
|
|
46
|
+
* This is called by checkpointers before saving to prevent corrupted checkpoints.
|
|
47
|
+
* @throws StateError if state is invalid
|
|
48
|
+
*/
|
|
49
|
+
validate(state: AgentState): void;
|
|
50
|
+
private validateParsed;
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* Default serializer instance
|
|
54
|
+
*/
|
|
55
|
+
export declare const defaultSerializer: JsonSerializer;
|