@archships/dim-agent-sdk 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.
Files changed (78) hide show
  1. package/README.md +69 -0
  2. package/dist/agent-core/Agent.d.ts +29 -0
  3. package/dist/agent-core/LoopRunner.d.ts +30 -0
  4. package/dist/agent-core/MessageFactory.d.ts +20 -0
  5. package/dist/agent-core/ModelTurnCollector.d.ts +22 -0
  6. package/dist/agent-core/Session.d.ts +48 -0
  7. package/dist/agent-core/TerminationPolicy.d.ts +14 -0
  8. package/dist/agent-core/ToolExecutor.d.ts +27 -0
  9. package/dist/agent-core/agent-types.d.ts +26 -0
  10. package/dist/agent-core/createModel.d.ts +2 -0
  11. package/dist/agent-core/errors.d.ts +5 -0
  12. package/dist/agent-core/index.d.ts +13 -0
  13. package/dist/agent-core/session-state.d.ts +17 -0
  14. package/dist/agent-core/tool-call.d.ts +9 -0
  15. package/dist/context/AutoContextManager.d.ts +21 -0
  16. package/dist/context/index.d.ts +3 -0
  17. package/dist/context/types.d.ts +19 -0
  18. package/dist/contracts/common.d.ts +19 -0
  19. package/dist/contracts/content-normalize.d.ts +6 -0
  20. package/dist/contracts/content.d.ts +16 -0
  21. package/dist/contracts/event.d.ts +30 -0
  22. package/dist/contracts/index.d.ts +9 -0
  23. package/dist/contracts/message.d.ts +32 -0
  24. package/dist/contracts/model.d.ts +73 -0
  25. package/dist/contracts/state.d.ts +14 -0
  26. package/dist/contracts/tool-normalize.d.ts +3 -0
  27. package/dist/contracts/tool.d.ts +40 -0
  28. package/dist/index.d.ts +16 -0
  29. package/dist/index.js +2997 -0
  30. package/dist/persistence/FileStateStore.d.ts +14 -0
  31. package/dist/persistence/InMemoryStateStore.d.ts +9 -0
  32. package/dist/persistence/SnapshotCodec.d.ts +20 -0
  33. package/dist/persistence/index.d.ts +6 -0
  34. package/dist/persistence/store.d.ts +7 -0
  35. package/dist/plugin-host/HookPipeline.d.ts +7 -0
  36. package/dist/plugin-host/PluginHost.d.ts +36 -0
  37. package/dist/plugin-host/helpers.d.ts +3 -0
  38. package/dist/plugin-host/index.d.ts +4 -0
  39. package/dist/plugin-host/types.d.ts +1 -0
  40. package/dist/providers/anthropic/adapter.d.ts +13 -0
  41. package/dist/providers/anthropic/mapper.d.ts +28 -0
  42. package/dist/providers/gemini/adapter.d.ts +12 -0
  43. package/dist/providers/gemini/mapper.d.ts +30 -0
  44. package/dist/providers/index.d.ts +8 -0
  45. package/dist/providers/openai/adapter.d.ts +12 -0
  46. package/dist/providers/openai/mapper.d.ts +15 -0
  47. package/dist/providers/openai-responses/adapter.d.ts +12 -0
  48. package/dist/providers/openai-responses/mapper.d.ts +40 -0
  49. package/dist/providers/shared/http-error.d.ts +7 -0
  50. package/dist/providers/shared/provider-state.d.ts +4 -0
  51. package/dist/providers/shared/reasoning.d.ts +14 -0
  52. package/dist/providers/shared/tool-call.d.ts +5 -0
  53. package/dist/providers/shared/usage.d.ts +2 -0
  54. package/dist/services/ExecGateway.d.ts +14 -0
  55. package/dist/services/FileSystemGateway.d.ts +35 -0
  56. package/dist/services/GitGateway.d.ts +17 -0
  57. package/dist/services/ModelGateway.d.ts +7 -0
  58. package/dist/services/NetworkGateway.d.ts +6 -0
  59. package/dist/services/PermissionGateway.d.ts +11 -0
  60. package/dist/services/activity.d.ts +10 -0
  61. package/dist/services/index.d.ts +10 -0
  62. package/dist/services/permissions.d.ts +17 -0
  63. package/dist/services/types.d.ts +77 -0
  64. package/dist/tools/BaseTool.d.ts +6 -0
  65. package/dist/tools/ToolRegistry.d.ts +9 -0
  66. package/dist/tools/builtins/EditTool.d.ts +6 -0
  67. package/dist/tools/builtins/ExecTool.d.ts +6 -0
  68. package/dist/tools/builtins/ReadTool.d.ts +6 -0
  69. package/dist/tools/builtins/WriteTool.d.ts +6 -0
  70. package/dist/tools/builtins/index.d.ts +4 -0
  71. package/dist/tools/builtins/utils.d.ts +6 -0
  72. package/dist/tools/index.d.ts +4 -0
  73. package/dist/tools/result.d.ts +3 -0
  74. package/dist/utils/guards.d.ts +2 -0
  75. package/dist/utils/id.d.ts +1 -0
  76. package/dist/utils/json.d.ts +3 -0
  77. package/dist/utils/usage.d.ts +3 -0
  78. package/package.json +54 -0
package/dist/index.js ADDED
@@ -0,0 +1,2997 @@
1
+ // src/utils/guards.ts
2
+ function isRecord(value) {
3
+ return typeof value === "object" && value !== null && !Array.isArray(value);
4
+ }
5
+ function isStringArray(value) {
6
+ return Array.isArray(value) && value.every((item) => typeof item === "string");
7
+ }
8
+
9
+ // src/contracts/content-normalize.ts
10
+ function isTextContent(value) {
11
+ return isRecord(value) && value.type === "text" && typeof value.text === "string" && (value.annotations === undefined || isRecord(value.annotations)) && (value._meta === undefined || isRecord(value._meta));
12
+ }
13
+ function isImageContent(value) {
14
+ return isRecord(value) && value.type === "image" && typeof value.data === "string" && typeof value.mimeType === "string" && (value.annotations === undefined || isRecord(value.annotations)) && (value._meta === undefined || isRecord(value._meta));
15
+ }
16
+ function isContentBlock(value) {
17
+ return isTextContent(value) || isImageContent(value);
18
+ }
19
+ function normalizeContent(input) {
20
+ if (typeof input === "string") {
21
+ return [{ type: "text", text: input }];
22
+ }
23
+ if (Array.isArray(input)) {
24
+ const blocks = input.filter((item) => item != null);
25
+ if (!blocks.every(isContentBlock))
26
+ throw new TypeError("Content array must contain only text or image blocks");
27
+ return blocks.map((block) => ({ ...block }));
28
+ }
29
+ if (!isContentBlock(input))
30
+ throw new TypeError("Content must be a string, content block, or content block array");
31
+ return [{ ...input }];
32
+ }
33
+ function contentToText(content) {
34
+ return content.filter((block) => block.type === "text").map((block) => block.text).join("");
35
+ }
36
+ // src/context/AutoContextManager.ts
37
+ import path from "node:path";
38
+
39
+ // src/utils/id.ts
40
+ function createId(prefix) {
41
+ return `${prefix}_${crypto.randomUUID()}`;
42
+ }
43
+
44
+ // src/context/AutoContextManager.ts
45
+ class AutoContextManager {
46
+ services;
47
+ pluginHost;
48
+ maxItems;
49
+ maxChars;
50
+ constructor(options) {
51
+ this.services = options.services;
52
+ this.pluginHost = options.pluginHost;
53
+ this.maxItems = options.maxItems ?? 8;
54
+ this.maxChars = options.maxChars ?? 8000;
55
+ }
56
+ async resolve(options) {
57
+ const items = [];
58
+ if (options.cwd) {
59
+ items.push({
60
+ id: createId("ctx"),
61
+ type: "cwd",
62
+ title: "Working directory",
63
+ content: options.cwd,
64
+ priority: 10,
65
+ source: "agent"
66
+ });
67
+ }
68
+ items.push(...await this.resolveExplicitFiles(options.input, options.cwd));
69
+ items.push(...await this.resolveRecentFiles(options.cwd));
70
+ items.push(...await this.resolveGitState(options.cwd));
71
+ const contributed = await this.pluginHost?.collectContext({
72
+ sessionId: options.sessionId,
73
+ input: options.input,
74
+ messages: options.messages,
75
+ cwd: options.cwd
76
+ });
77
+ if (contributed)
78
+ items.push(...contributed);
79
+ return budgetContext(items, this.maxItems, this.maxChars);
80
+ }
81
+ format(items) {
82
+ if (items.length === 0)
83
+ return [];
84
+ const sections = items.map((item) => {
85
+ return [`[${item.title}]`, item.content].join(`
86
+ `);
87
+ });
88
+ return [`Additional context for this turn:
89
+
90
+ ${sections.join(`
91
+
92
+ `)}`];
93
+ }
94
+ async resolveExplicitFiles(input, cwd) {
95
+ const text = inputToText(input);
96
+ const candidates = extractFileCandidates(text);
97
+ const items = [];
98
+ for (const candidate of candidates) {
99
+ if (!cwd)
100
+ break;
101
+ if (!await this.services.fileSystem.exists(candidate, { cwd }))
102
+ continue;
103
+ const content = await safeReadText(this.services, candidate, cwd);
104
+ if (!content)
105
+ continue;
106
+ items.push({
107
+ id: createId("ctx"),
108
+ type: "file",
109
+ title: `File: ${candidate}`,
110
+ content,
111
+ priority: 100,
112
+ source: "explicit-file"
113
+ });
114
+ }
115
+ return items;
116
+ }
117
+ async resolveRecentFiles(cwd) {
118
+ if (!cwd || !this.pluginHost)
119
+ return [];
120
+ const items = [];
121
+ for (const filePath of this.pluginHost.recentFiles(3)) {
122
+ const relative = toRelative(cwd, filePath);
123
+ const content = await safeReadText(this.services, relative, cwd);
124
+ if (!content)
125
+ continue;
126
+ items.push({
127
+ id: createId("ctx"),
128
+ type: "file",
129
+ title: `Recent file: ${relative}`,
130
+ content,
131
+ priority: 70,
132
+ source: "recent-file"
133
+ });
134
+ }
135
+ return items;
136
+ }
137
+ async resolveGitState(cwd) {
138
+ if (!cwd)
139
+ return [];
140
+ const items = [];
141
+ const status = await this.services.git.status({ cwd });
142
+ if (status) {
143
+ items.push({
144
+ id: createId("ctx"),
145
+ type: "git_status",
146
+ title: "Git status",
147
+ content: status,
148
+ priority: 60,
149
+ source: "git"
150
+ });
151
+ }
152
+ const diff = await this.services.git.diff({ cwd });
153
+ if (diff) {
154
+ items.push({
155
+ id: createId("ctx"),
156
+ type: "git_diff",
157
+ title: "Git diff",
158
+ content: trimText(diff, 4000),
159
+ priority: 50,
160
+ source: "git"
161
+ });
162
+ }
163
+ return items;
164
+ }
165
+ }
166
+ function inputToText(input) {
167
+ return contentToText(normalizeContent(input));
168
+ }
169
+ function extractFileCandidates(text) {
170
+ const matches = text.match(/(?:[A-Za-z0-9._-]+\/)*[A-Za-z0-9._-]+\.[A-Za-z0-9_-]+/g) ?? [];
171
+ return [...new Set(matches)];
172
+ }
173
+ function trimText(text, maxChars) {
174
+ return text.length <= maxChars ? text : `${text.slice(0, maxChars)}
175
+ ...`;
176
+ }
177
+ async function safeReadText(services, filePath, cwd) {
178
+ try {
179
+ const text = await services.fileSystem.readText(filePath, { cwd });
180
+ return trimText(text, 2000);
181
+ } catch {
182
+ return null;
183
+ }
184
+ }
185
+ function budgetContext(items, maxItems, maxChars) {
186
+ const sorted = [...items].sort((left, right) => right.priority - left.priority);
187
+ const selected = [];
188
+ let totalChars = 0;
189
+ for (const item of sorted) {
190
+ if (selected.length >= maxItems)
191
+ break;
192
+ if (totalChars + item.content.length > maxChars && selected.length > 0)
193
+ continue;
194
+ selected.push(item);
195
+ totalChars += item.content.length;
196
+ }
197
+ return selected;
198
+ }
199
+ function toRelative(cwd, filePath) {
200
+ if (!path.isAbsolute(filePath))
201
+ return filePath;
202
+ return path.relative(cwd, filePath) || path.basename(filePath);
203
+ }
204
+ // src/plugin-host/helpers.ts
205
+ function isHookControlResult(value) {
206
+ if (!value || typeof value !== "object")
207
+ return false;
208
+ const candidate = value;
209
+ return (candidate.type === "continue" || candidate.type === "replace" || candidate.type === "stop") && "payload" in candidate;
210
+ }
211
+
212
+ // src/plugin-host/HookPipeline.ts
213
+ class HookPipeline {
214
+ registrations = [];
215
+ register(pluginId, priority, hooks) {
216
+ if (!hooks)
217
+ return;
218
+ if (hooks["model.event"]?.middleware && hooks["model.event"]?.middleware.length > 0)
219
+ throw new Error(`Plugin ${pluginId} cannot register middleware for model.event`);
220
+ this.registrations.push({ pluginId, priority, hooks });
221
+ this.registrations.sort((left, right) => right.priority - left.priority);
222
+ }
223
+ async runMiddleware(name, payload, context) {
224
+ let current = payload;
225
+ for (const registration of this.registrations) {
226
+ const middlewares = registration.hooks[name]?.middleware ?? [];
227
+ for (const middleware of middlewares) {
228
+ const result = await middleware({
229
+ payload: current,
230
+ context: { ...context, metadata: { ...context.metadata, pluginId: registration.pluginId } }
231
+ });
232
+ if (result === undefined)
233
+ continue;
234
+ if (isHookControlResult(result)) {
235
+ current = result.payload;
236
+ if (result.type === "stop")
237
+ return current;
238
+ continue;
239
+ }
240
+ current = result;
241
+ }
242
+ }
243
+ return current;
244
+ }
245
+ async runObservers(name, payload, context) {
246
+ for (const registration of this.registrations) {
247
+ const observers = registration.hooks[name]?.observers ?? [];
248
+ for (const observer of observers) {
249
+ await observer({
250
+ payload,
251
+ context: { ...context, metadata: { ...context.metadata, pluginId: registration.pluginId } }
252
+ });
253
+ }
254
+ }
255
+ }
256
+ }
257
+
258
+ // src/plugin-host/PluginHost.ts
259
+ class PluginHost {
260
+ pipeline = new HookPipeline;
261
+ contextContributors = [];
262
+ promptContributors = [];
263
+ tools = [];
264
+ services;
265
+ cwd;
266
+ agentId;
267
+ tracker;
268
+ constructor(options) {
269
+ this.agentId = options.agentId;
270
+ this.cwd = options.cwd;
271
+ this.services = options.services;
272
+ this.tracker = options.tracker;
273
+ for (const plugin of options.plugins ?? []) {
274
+ const priority = plugin.manifest.priority ?? 0;
275
+ const scopedServices = options.permissionGateway.scopeServices(this.services, plugin.manifest.id, plugin.manifest.permissions);
276
+ const setup = plugin.setup?.({
277
+ cwd: this.cwd,
278
+ manifest: plugin.manifest,
279
+ services: scopedServices
280
+ });
281
+ this.pipeline.register(plugin.manifest.id, priority, setup?.hooks);
282
+ for (const tool of setup?.tools ?? []) {
283
+ this.tools.push({
284
+ pluginId: plugin.manifest.id,
285
+ tool: wrapTool(plugin.manifest.id, tool, scopedServices)
286
+ });
287
+ }
288
+ for (const contributor of setup?.contextContributors ?? [])
289
+ this.contextContributors.push({ pluginId: plugin.manifest.id, contributor });
290
+ for (const contributor of setup?.promptContributors ?? [])
291
+ this.promptContributors.push({ pluginId: plugin.manifest.id, contributor });
292
+ }
293
+ }
294
+ pluginTools() {
295
+ return this.tools.map((entry) => entry.tool);
296
+ }
297
+ recentFiles(limit = 5) {
298
+ return this.tracker?.recentFiles(limit) ?? [];
299
+ }
300
+ async runMiddleware(name, payload, context = {}) {
301
+ return this.pipeline.runMiddleware(name, payload, this.createRuntimeContext(context));
302
+ }
303
+ async runObservers(name, payload, context = {}) {
304
+ await this.pipeline.runObservers(name, payload, this.createRuntimeContext(context));
305
+ }
306
+ async collectContext(input) {
307
+ const items = [];
308
+ for (const entry of this.contextContributors) {
309
+ const next = await entry.contributor(input);
310
+ if (next.length > 0)
311
+ items.push(...next);
312
+ }
313
+ return items;
314
+ }
315
+ async collectPromptSegments(input) {
316
+ const segments = [];
317
+ for (const entry of this.promptContributors) {
318
+ const next = await entry.contributor(input);
319
+ if (!next)
320
+ continue;
321
+ if (Array.isArray(next))
322
+ segments.push(...next.filter(Boolean));
323
+ else
324
+ segments.push(next);
325
+ }
326
+ return segments;
327
+ }
328
+ createRuntimeContext(context) {
329
+ return {
330
+ agentId: this.agentId,
331
+ cwd: context.cwd ?? this.cwd,
332
+ sessionId: context.sessionId,
333
+ requestId: context.requestId,
334
+ iteration: context.iteration,
335
+ metadata: context.metadata,
336
+ services: context.services ?? this.services
337
+ };
338
+ }
339
+ }
340
+ function wrapTool(pluginId, tool, services) {
341
+ return {
342
+ definition: tool.definition,
343
+ async execute(args, context) {
344
+ return tool.execute(args, {
345
+ ...context,
346
+ metadata: {
347
+ ...context.metadata,
348
+ pluginId
349
+ },
350
+ services
351
+ });
352
+ }
353
+ };
354
+ }
355
+
356
+ // src/services/activity.ts
357
+ class ActivityTracker {
358
+ records = [];
359
+ record(record) {
360
+ this.records.push({ ...record, timestamp: record.timestamp });
361
+ if (this.records.length > 200)
362
+ this.records.splice(0, this.records.length - 200);
363
+ }
364
+ recentFiles(limit = 5) {
365
+ const seen = new Set;
366
+ const files = [];
367
+ for (let index = this.records.length - 1;index >= 0; index -= 1) {
368
+ const record = this.records[index];
369
+ if (!record || record.type !== "file")
370
+ continue;
371
+ if (seen.has(record.label))
372
+ continue;
373
+ seen.add(record.label);
374
+ files.push(record.label);
375
+ if (files.length >= limit)
376
+ break;
377
+ }
378
+ return files;
379
+ }
380
+ }
381
+ // src/services/ExecGateway.ts
382
+ import { spawn } from "node:child_process";
383
+
384
+ class NodeExecGateway {
385
+ cwd;
386
+ env;
387
+ tracker;
388
+ constructor(options = {}) {
389
+ this.cwd = options.cwd;
390
+ this.env = options.env ?? process.env;
391
+ this.tracker = options.tracker;
392
+ }
393
+ async execute(command, options = {}) {
394
+ const cwd = options.cwd ?? this.cwd;
395
+ const env = {
396
+ ...this.env,
397
+ ...options.env
398
+ };
399
+ const result = await new Promise((resolve, reject) => {
400
+ const child = spawn(command, {
401
+ cwd,
402
+ env,
403
+ shell: true,
404
+ signal: options.signal
405
+ });
406
+ let stdout = "";
407
+ let stderr = "";
408
+ let settled = false;
409
+ let timedOut = false;
410
+ let timeout;
411
+ const finish = (value, isError) => {
412
+ if (settled)
413
+ return;
414
+ settled = true;
415
+ if (timeout)
416
+ clearTimeout(timeout);
417
+ if (isError)
418
+ reject(value);
419
+ else
420
+ resolve(value);
421
+ };
422
+ if (options.timeoutMs !== undefined) {
423
+ timeout = setTimeout(() => {
424
+ timedOut = true;
425
+ child.kill("SIGTERM");
426
+ }, options.timeoutMs);
427
+ }
428
+ child.stdout.on("data", (chunk) => {
429
+ stdout += String(chunk);
430
+ });
431
+ child.stderr.on("data", (chunk) => {
432
+ stderr += String(chunk);
433
+ });
434
+ child.on("error", (error) => finish(error, true));
435
+ child.on("close", (code, signal) => {
436
+ finish({
437
+ command,
438
+ cwd: cwd ?? null,
439
+ stdout,
440
+ stderr,
441
+ exitCode: code,
442
+ signal: signal ?? null,
443
+ timedOut
444
+ }, false);
445
+ });
446
+ });
447
+ this.tracker?.record({
448
+ type: "command",
449
+ label: command,
450
+ timestamp: Date.now()
451
+ });
452
+ return result;
453
+ }
454
+ }
455
+ // src/services/FileSystemGateway.ts
456
+ import { mkdir, readFile, readdir, stat, writeFile } from "node:fs/promises";
457
+ import path2 from "node:path";
458
+
459
+ class NodeFileSystemGateway {
460
+ cwd;
461
+ tracker;
462
+ constructor(options = {}) {
463
+ this.cwd = options.cwd;
464
+ this.tracker = options.tracker;
465
+ }
466
+ async readText(targetPath, options = {}) {
467
+ const resolved = resolvePath(this.cwd, targetPath, options.cwd);
468
+ const text = await readFile(resolved, "utf8");
469
+ this.recordFile(resolved);
470
+ return text;
471
+ }
472
+ async writeText(targetPath, content, options = {}) {
473
+ const resolved = resolvePath(this.cwd, targetPath, options.cwd);
474
+ await mkdir(path2.dirname(resolved), { recursive: true });
475
+ await writeFile(resolved, content, "utf8");
476
+ this.recordFile(resolved);
477
+ return {
478
+ path: resolved,
479
+ bytes: Buffer.byteLength(content, "utf8")
480
+ };
481
+ }
482
+ async editText(targetPath, options) {
483
+ const resolved = resolvePath(this.cwd, targetPath, options.cwd);
484
+ const source = await readFile(resolved, "utf8");
485
+ const matches = source.split(options.oldText).length - 1;
486
+ if (matches === 0)
487
+ throw new Error("oldText was not found in the target file");
488
+ if (!options.replaceAll && matches > 1)
489
+ throw new Error("oldText matched more than once; set replaceAll to true to replace all matches");
490
+ const updated = options.replaceAll ? source.split(options.oldText).join(options.newText) : source.replace(options.oldText, options.newText);
491
+ await writeFile(resolved, updated, "utf8");
492
+ this.recordFile(resolved);
493
+ return {
494
+ path: resolved,
495
+ replacements: options.replaceAll ? matches : 1
496
+ };
497
+ }
498
+ async exists(targetPath, options = {}) {
499
+ try {
500
+ const resolved = resolvePath(this.cwd, targetPath, options.cwd);
501
+ await stat(resolved);
502
+ return true;
503
+ } catch {
504
+ return false;
505
+ }
506
+ }
507
+ async glob(pattern, options = {}) {
508
+ const root = path2.resolve(options.cwd ?? this.cwd ?? process.cwd());
509
+ const matcher = compileGlob(pattern);
510
+ const limit = options.limit ?? 100;
511
+ const matches = [];
512
+ await walk(root, root, async (absolutePath, relativePath) => {
513
+ if (matches.length >= limit)
514
+ return false;
515
+ if (matcher.test(relativePath))
516
+ matches.push(absolutePath);
517
+ return true;
518
+ });
519
+ return matches;
520
+ }
521
+ async grep(query, options = {}) {
522
+ const pattern = options.pattern ?? "**/*";
523
+ const files = await this.glob(pattern, options);
524
+ const matches = [];
525
+ const limit = options.limit ?? 50;
526
+ for (const filePath of files) {
527
+ if (matches.length >= limit)
528
+ break;
529
+ let text;
530
+ try {
531
+ text = await readFile(filePath, "utf8");
532
+ } catch {
533
+ continue;
534
+ }
535
+ const lines = text.split(/\r?\n/g);
536
+ for (let index = 0;index < lines.length; index += 1) {
537
+ const line = lines[index];
538
+ if (!line || !line.includes(query))
539
+ continue;
540
+ matches.push({
541
+ path: filePath,
542
+ lineNumber: index + 1,
543
+ line
544
+ });
545
+ if (matches.length >= limit)
546
+ break;
547
+ }
548
+ }
549
+ return matches;
550
+ }
551
+ recordFile(filePath) {
552
+ this.tracker?.record({
553
+ type: "file",
554
+ label: filePath,
555
+ timestamp: Date.now()
556
+ });
557
+ }
558
+ }
559
+ function resolvePath(defaultCwd, targetPath, cwdOverride) {
560
+ if (!targetPath.trim())
561
+ throw new Error("path is required");
562
+ if (path2.isAbsolute(targetPath))
563
+ return path2.normalize(targetPath);
564
+ return path2.resolve(cwdOverride ?? defaultCwd ?? process.cwd(), targetPath);
565
+ }
566
+ function compileGlob(pattern) {
567
+ const normalized = pattern.replace(/\\/g, "/").replace(/^\.\//, "");
568
+ let expression = "^";
569
+ for (let index = 0;index < normalized.length; index += 1) {
570
+ const character = normalized[index];
571
+ const next = normalized[index + 1];
572
+ if (character === "*" && next === "*") {
573
+ expression += ".*";
574
+ index += 1;
575
+ continue;
576
+ }
577
+ if (character === "*") {
578
+ expression += "[^/]*";
579
+ continue;
580
+ }
581
+ expression += /[.+^${}()|[\]\\]/.test(character) ? `\\${character}` : character;
582
+ }
583
+ expression += "$";
584
+ return new RegExp(expression);
585
+ }
586
+ async function walk(root, current, visit) {
587
+ const entries = await readdir(current, { withFileTypes: true });
588
+ for (const entry of entries) {
589
+ if (entry.name === "node_modules" || entry.name === ".git")
590
+ continue;
591
+ const absolutePath = path2.join(current, entry.name);
592
+ const relativePath = path2.relative(root, absolutePath).replace(/\\/g, "/");
593
+ if (entry.isDirectory()) {
594
+ const keepGoing = await visit(absolutePath, relativePath);
595
+ if (keepGoing !== false)
596
+ await walk(root, absolutePath, visit);
597
+ continue;
598
+ }
599
+ await visit(absolutePath, relativePath);
600
+ }
601
+ }
602
+ // src/services/GitGateway.ts
603
+ class DefaultGitGateway {
604
+ exec;
605
+ cwd;
606
+ constructor(options) {
607
+ this.exec = options.exec;
608
+ this.cwd = options.cwd;
609
+ }
610
+ async status(options = {}) {
611
+ return this.runGitCommand("git status --short", options.cwd);
612
+ }
613
+ async diff(options = {}) {
614
+ return this.runGitCommand('git diff --stat && printf "\\n---\\n" && git diff -- .', options.cwd);
615
+ }
616
+ async runGitCommand(command, cwd) {
617
+ try {
618
+ const result = await this.exec.execute(command, { cwd: cwd ?? this.cwd, timeoutMs: 1e4 });
619
+ if (result.exitCode !== 0)
620
+ return null;
621
+ const text = [result.stdout, result.stderr].filter(Boolean).join(result.stdout && result.stderr ? `
622
+ ` : "");
623
+ return text.trim() || null;
624
+ } catch {
625
+ return null;
626
+ }
627
+ }
628
+ }
629
+ // src/services/ModelGateway.ts
630
+ class DefaultModelGateway {
631
+ model;
632
+ constructor(model) {
633
+ this.model = model;
634
+ }
635
+ stream(request) {
636
+ return this.model.stream(request);
637
+ }
638
+ }
639
+ // src/services/NetworkGateway.ts
640
+ class DefaultNetworkGateway {
641
+ fetchImpl;
642
+ constructor(fetchImpl = globalThis.fetch) {
643
+ this.fetchImpl = fetchImpl;
644
+ }
645
+ fetch(input, init) {
646
+ return this.fetchImpl(input, init);
647
+ }
648
+ }
649
+ // src/services/permissions.ts
650
+ var fullPermissions = {
651
+ fs: "full",
652
+ git: true,
653
+ network: true,
654
+ process: true,
655
+ model: true,
656
+ mcp: true
657
+ };
658
+
659
+ class PermissionDeniedError extends Error {
660
+ code = "permission_denied";
661
+ constructor(message) {
662
+ super(message);
663
+ this.name = "PermissionDeniedError";
664
+ }
665
+ }
666
+ function normalizePermissions(input) {
667
+ return {
668
+ ...fullPermissions,
669
+ ...input
670
+ };
671
+ }
672
+ function canReadFs(permissions) {
673
+ return permissions.fs === true || permissions.fs === "read" || permissions.fs === "full";
674
+ }
675
+ function canWriteFs(permissions) {
676
+ return permissions.fs === true || permissions.fs === "write" || permissions.fs === "full";
677
+ }
678
+ // src/services/PermissionGateway.ts
679
+ class PermissionGateway {
680
+ permissions;
681
+ constructor(options = {}) {
682
+ this.permissions = normalizePermissions(options.permissions);
683
+ }
684
+ get agentPermissions() {
685
+ return { ...this.permissions };
686
+ }
687
+ scopeServices(services, pluginId, requested) {
688
+ const effective = intersectPermissions(this.permissions, requested);
689
+ return {
690
+ fileSystem: {
691
+ readText: (path3, options) => {
692
+ if (!canReadFs(effective))
693
+ throw new PermissionDeniedError(`Plugin ${pluginId} is not allowed to read files`);
694
+ return services.fileSystem.readText(path3, options);
695
+ },
696
+ writeText: (path3, content, options) => {
697
+ if (!canWriteFs(effective))
698
+ throw new PermissionDeniedError(`Plugin ${pluginId} is not allowed to write files`);
699
+ return services.fileSystem.writeText(path3, content, options);
700
+ },
701
+ editText: (path3, options) => {
702
+ if (!canWriteFs(effective))
703
+ throw new PermissionDeniedError(`Plugin ${pluginId} is not allowed to edit files`);
704
+ return services.fileSystem.editText(path3, options);
705
+ },
706
+ exists: (path3, options) => {
707
+ if (!canReadFs(effective))
708
+ throw new PermissionDeniedError(`Plugin ${pluginId} is not allowed to inspect files`);
709
+ return services.fileSystem.exists(path3, options);
710
+ },
711
+ glob: (pattern, options) => {
712
+ if (!canReadFs(effective))
713
+ throw new PermissionDeniedError(`Plugin ${pluginId} is not allowed to glob files`);
714
+ return services.fileSystem.glob(pattern, options);
715
+ },
716
+ grep: (query, options) => {
717
+ if (!canReadFs(effective))
718
+ throw new PermissionDeniedError(`Plugin ${pluginId} is not allowed to grep files`);
719
+ return services.fileSystem.grep(query, options);
720
+ }
721
+ },
722
+ exec: {
723
+ execute: (command, options) => {
724
+ if (effective.process !== true)
725
+ throw new PermissionDeniedError(`Plugin ${pluginId} is not allowed to execute commands`);
726
+ return services.exec.execute(command, options);
727
+ }
728
+ },
729
+ git: {
730
+ status: (options) => {
731
+ if (effective.git !== true)
732
+ throw new PermissionDeniedError(`Plugin ${pluginId} is not allowed to inspect git state`);
733
+ return services.git.status(options);
734
+ },
735
+ diff: (options) => {
736
+ if (effective.git !== true)
737
+ throw new PermissionDeniedError(`Plugin ${pluginId} is not allowed to inspect git diff`);
738
+ return services.git.diff(options);
739
+ }
740
+ },
741
+ network: {
742
+ fetch: (input, init) => {
743
+ if (effective.network !== true)
744
+ throw new PermissionDeniedError(`Plugin ${pluginId} is not allowed to access the network`);
745
+ return services.network.fetch(input, init);
746
+ }
747
+ },
748
+ model: {
749
+ stream: (request) => {
750
+ if (effective.model !== true)
751
+ throw new PermissionDeniedError(`Plugin ${pluginId} is not allowed to call the model gateway`);
752
+ return services.model.stream(request);
753
+ }
754
+ }
755
+ };
756
+ }
757
+ }
758
+ function intersectPermissions(agent, requested) {
759
+ const normalized = requested ?? {};
760
+ return {
761
+ fs: intersectFs(agent.fs, normalized.fs ?? false),
762
+ git: agent.git === true && normalized.git === true,
763
+ network: agent.network === true && normalized.network === true,
764
+ process: agent.process === true && normalized.process === true,
765
+ model: agent.model === true && normalized.model === true,
766
+ mcp: agent.mcp === true && normalized.mcp === true
767
+ };
768
+ }
769
+ function intersectFs(left, right) {
770
+ const rank = new Map([
771
+ [false, 0],
772
+ ["read", 1],
773
+ ["write", 1],
774
+ [true, 2],
775
+ ["full", 2]
776
+ ]);
777
+ const leftValue = left ?? false;
778
+ const rightValue = right ?? false;
779
+ if (leftValue === "full" || leftValue === true)
780
+ return rightValue;
781
+ if (rightValue === "full" || rightValue === true)
782
+ return leftValue;
783
+ if (leftValue === rightValue)
784
+ return leftValue;
785
+ if (rank.get(leftValue) === 0 || rank.get(rightValue) === 0)
786
+ return false;
787
+ if (leftValue === "read" && rightValue === "write" || leftValue === "write" && rightValue === "read")
788
+ return false;
789
+ return leftValue;
790
+ }
791
+ // src/utils/json.ts
792
+ function isJsonSafeValue(value) {
793
+ if (value === null)
794
+ return true;
795
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean")
796
+ return true;
797
+ if (Array.isArray(value))
798
+ return value.every(isJsonSafeValue);
799
+ if (isRecord(value))
800
+ return Object.values(value).every(isJsonSafeValue);
801
+ return false;
802
+ }
803
+ function assertJsonSafeObject(value, label) {
804
+ if (!isRecord(value) || !isJsonSafeValue(value))
805
+ throw new TypeError(`${label} must be a JSON-safe object`);
806
+ }
807
+ function parseJsonObject(value, label) {
808
+ let parsed;
809
+ try {
810
+ parsed = JSON.parse(value);
811
+ } catch (error) {
812
+ const message = error instanceof Error ? error.message : String(error);
813
+ throw new Error(`${label} must be valid JSON: ${message}`);
814
+ }
815
+ if (!isRecord(parsed))
816
+ throw new Error(`${label} must decode to a JSON object`);
817
+ return parsed;
818
+ }
819
+
820
+ // src/contracts/tool-normalize.ts
821
+ function normalizeToolSchema(schema) {
822
+ if (!isRecord(schema) || schema.type !== "object")
823
+ throw new TypeError('Tool input schema must have type "object"');
824
+ const properties = schema.properties;
825
+ if (properties !== undefined && !isRecord(properties))
826
+ throw new TypeError("Tool input schema properties must be an object");
827
+ const required = schema.required;
828
+ if (required !== undefined && !isStringArray(required))
829
+ throw new TypeError("Tool input schema required must be a string array");
830
+ const normalized = {
831
+ ...schema,
832
+ type: "object",
833
+ properties: properties ? cloneSchemaRecord(properties) : {}
834
+ };
835
+ return normalized;
836
+ }
837
+ function cloneSchemaRecord(record) {
838
+ return Object.fromEntries(Object.entries(record).map(([key, value]) => {
839
+ if (!isRecord(value))
840
+ throw new TypeError(`Tool schema property ${key} must be an object`);
841
+ return [key, { ...value }];
842
+ }));
843
+ }
844
+ function normalizeToolResult(result) {
845
+ const normalized = {
846
+ content: normalizeContent(result.content),
847
+ isError: result.isError === true
848
+ };
849
+ if (result.structuredContent !== undefined) {
850
+ assertJsonSafeObject(result.structuredContent, "Tool structuredContent");
851
+ normalized.structuredContent = { ...result.structuredContent };
852
+ }
853
+ return normalized;
854
+ }
855
+
856
+ // src/tools/BaseTool.ts
857
+ class BaseTool {
858
+ definition;
859
+ constructor(definition) {
860
+ this.definition = {
861
+ ...definition,
862
+ inputSchema: normalizeToolSchema(definition.inputSchema)
863
+ };
864
+ }
865
+ }
866
+ // src/tools/result.ts
867
+ function textToolResult(text) {
868
+ return {
869
+ content: normalizeContent(text)
870
+ };
871
+ }
872
+ // src/tools/ToolRegistry.ts
873
+ class ToolRegistry {
874
+ tools = new Map;
875
+ register(tool) {
876
+ const name = tool.definition.name.trim();
877
+ if (!name)
878
+ throw new Error("Tool name is required");
879
+ if (this.tools.has(name))
880
+ throw new Error(`Tool already registered: ${name}`);
881
+ const normalizedDefinition = {
882
+ ...tool.definition,
883
+ name,
884
+ description: tool.definition.description.trim(),
885
+ inputSchema: normalizeToolSchema(tool.definition.inputSchema)
886
+ };
887
+ const normalizedTool = {
888
+ definition: normalizedDefinition,
889
+ execute(args, context) {
890
+ return tool.execute(args, context);
891
+ }
892
+ };
893
+ this.tools.set(name, normalizedTool);
894
+ return this;
895
+ }
896
+ unregister(name) {
897
+ return this.tools.delete(name);
898
+ }
899
+ get(name) {
900
+ return this.tools.get(name);
901
+ }
902
+ list() {
903
+ return [...this.tools.values()];
904
+ }
905
+ definitions() {
906
+ return this.list().map((tool) => ({
907
+ ...tool.definition,
908
+ inputSchema: normalizeToolSchema(tool.definition.inputSchema)
909
+ }));
910
+ }
911
+ }
912
+ // src/tools/builtins/utils.ts
913
+ function readStringArg(args, key, options = {}) {
914
+ const value = args[key];
915
+ if (typeof value !== "string")
916
+ throw new Error(`${key} must be a string`);
917
+ if (!options.allowEmpty && value.length === 0)
918
+ throw new Error(`${key} must be a non-empty string`);
919
+ return value;
920
+ }
921
+ function readOptionalBooleanArg(args, key) {
922
+ const value = args[key];
923
+ if (value === undefined)
924
+ return;
925
+ if (typeof value !== "boolean")
926
+ throw new Error(`${key} must be a boolean`);
927
+ return value;
928
+ }
929
+ function readOptionalNumberArg(args, key) {
930
+ const value = args[key];
931
+ if (value === undefined)
932
+ return;
933
+ if (typeof value !== "number" || !Number.isFinite(value))
934
+ throw new Error(`${key} must be a finite number`);
935
+ return value;
936
+ }
937
+
938
+ // src/tools/builtins/EditTool.ts
939
+ class EditTool extends BaseTool {
940
+ constructor() {
941
+ super({
942
+ name: "edit",
943
+ description: "Replace text in a UTF-8 file.",
944
+ inputSchema: {
945
+ type: "object",
946
+ properties: {
947
+ path: { type: "string", description: "File path to edit." },
948
+ oldText: { type: "string", description: "Text to replace." },
949
+ newText: { type: "string", description: "Replacement text." },
950
+ replaceAll: { type: "boolean", description: "Replace all matches when true." }
951
+ },
952
+ required: ["path", "oldText", "newText"]
953
+ }
954
+ });
955
+ }
956
+ async execute(args, context) {
957
+ const fileSystem = context.services?.fileSystem;
958
+ if (!fileSystem)
959
+ throw new Error("FileSystemGateway is not available in the tool execution context");
960
+ const targetPath = readStringArg(args, "path");
961
+ const oldText = readStringArg(args, "oldText");
962
+ const newText = readStringArg(args, "newText", { allowEmpty: true });
963
+ const replaceAll = readOptionalBooleanArg(args, "replaceAll") ?? false;
964
+ const result = await fileSystem.editText(targetPath, {
965
+ oldText,
966
+ newText,
967
+ replaceAll,
968
+ cwd: context.cwd
969
+ });
970
+ return normalizeToolResult({
971
+ content: [{ type: "text", text: `Edited ${result.path}` }],
972
+ structuredContent: result
973
+ });
974
+ }
975
+ }
976
+ // src/tools/builtins/ExecTool.ts
977
+ class ExecTool extends BaseTool {
978
+ constructor() {
979
+ super({
980
+ name: "exec",
981
+ description: "Execute a shell command.",
982
+ inputSchema: {
983
+ type: "object",
984
+ properties: {
985
+ command: { type: "string", description: "Shell command to execute." },
986
+ cwd: { type: "string", description: "Optional working directory override." },
987
+ timeoutMs: { type: "number", description: "Optional timeout in milliseconds." }
988
+ },
989
+ required: ["command"]
990
+ }
991
+ });
992
+ }
993
+ async execute(args, context) {
994
+ const exec = context.services?.exec;
995
+ if (!exec)
996
+ throw new Error("ExecGateway is not available in the tool execution context");
997
+ const command = readStringArg(args, "command");
998
+ const cwd = typeof args.cwd === "string" && args.cwd.length > 0 ? args.cwd : context.cwd;
999
+ const timeoutMs = readOptionalNumberArg(args, "timeoutMs");
1000
+ const result = await exec.execute(command, {
1001
+ cwd,
1002
+ timeoutMs,
1003
+ signal: context.signal
1004
+ });
1005
+ const text = [result.stdout, result.stderr].filter(Boolean).join(result.stdout && result.stderr ? `
1006
+ ` : "");
1007
+ return normalizeToolResult({
1008
+ content: [{ type: "text", text: text || `Command exited with code ${result.exitCode ?? -1}` }],
1009
+ structuredContent: { ...result },
1010
+ isError: result.exitCode !== 0
1011
+ });
1012
+ }
1013
+ }
1014
+ // src/tools/builtins/ReadTool.ts
1015
+ class ReadTool extends BaseTool {
1016
+ constructor() {
1017
+ super({
1018
+ name: "read",
1019
+ description: "Read a UTF-8 text file.",
1020
+ inputSchema: {
1021
+ type: "object",
1022
+ properties: {
1023
+ path: { type: "string", description: "File path to read." }
1024
+ },
1025
+ required: ["path"]
1026
+ }
1027
+ });
1028
+ }
1029
+ async execute(args, context) {
1030
+ const fileSystem = context.services?.fileSystem;
1031
+ if (!fileSystem)
1032
+ throw new Error("FileSystemGateway is not available in the tool execution context");
1033
+ const targetPath = readStringArg(args, "path");
1034
+ const text = await fileSystem.readText(targetPath, { cwd: context.cwd });
1035
+ return normalizeToolResult({
1036
+ content: [{ type: "text", text }],
1037
+ structuredContent: {
1038
+ path: targetPath,
1039
+ size: Buffer.byteLength(text, "utf8")
1040
+ }
1041
+ });
1042
+ }
1043
+ }
1044
+ // src/tools/builtins/WriteTool.ts
1045
+ class WriteTool extends BaseTool {
1046
+ constructor() {
1047
+ super({
1048
+ name: "write",
1049
+ description: "Write a UTF-8 text file.",
1050
+ inputSchema: {
1051
+ type: "object",
1052
+ properties: {
1053
+ path: { type: "string", description: "File path to write." },
1054
+ content: { type: "string", description: "Text content to write." }
1055
+ },
1056
+ required: ["path", "content"]
1057
+ }
1058
+ });
1059
+ }
1060
+ async execute(args, context) {
1061
+ const fileSystem = context.services?.fileSystem;
1062
+ if (!fileSystem)
1063
+ throw new Error("FileSystemGateway is not available in the tool execution context");
1064
+ const targetPath = readStringArg(args, "path");
1065
+ const content = readStringArg(args, "content", { allowEmpty: true });
1066
+ const result = await fileSystem.writeText(targetPath, content, { cwd: context.cwd });
1067
+ return normalizeToolResult({
1068
+ content: [{ type: "text", text: `Wrote ${result.path}` }],
1069
+ structuredContent: result
1070
+ });
1071
+ }
1072
+ }
1073
+ // src/agent-core/MessageFactory.ts
1074
+ class DefaultMessageFactory {
1075
+ createSystemMessage(input) {
1076
+ return {
1077
+ id: createId("msg"),
1078
+ role: "system",
1079
+ content: normalizeContent(input),
1080
+ createdAt: Date.now()
1081
+ };
1082
+ }
1083
+ createUserMessage(input) {
1084
+ return {
1085
+ id: createId("msg"),
1086
+ role: "user",
1087
+ content: normalizeContent(input),
1088
+ createdAt: Date.now()
1089
+ };
1090
+ }
1091
+ createAssistantMessage(input) {
1092
+ return {
1093
+ id: createId("msg"),
1094
+ role: "assistant",
1095
+ content: input.text.length > 0 ? normalizeContent(input.text) : [],
1096
+ createdAt: Date.now(),
1097
+ thinking: input.thinking,
1098
+ toolCalls: input.toolCalls.length > 0 ? input.toolCalls : undefined,
1099
+ stopReason: input.stopReason,
1100
+ metadata: input.metadata
1101
+ };
1102
+ }
1103
+ createToolMessage(toolCall, result) {
1104
+ return {
1105
+ id: createId("msg"),
1106
+ role: "tool",
1107
+ content: result.content,
1108
+ createdAt: Date.now(),
1109
+ toolCallId: toolCall.id,
1110
+ toolName: toolCall.function.name,
1111
+ structuredContent: result.structuredContent,
1112
+ isError: result.isError
1113
+ };
1114
+ }
1115
+ }
1116
+
1117
+ // src/persistence/SnapshotCodec.ts
1118
+ class JsonSnapshotCodec {
1119
+ encode(input) {
1120
+ return structuredClone({
1121
+ schemaVersion: 1,
1122
+ sessionId: input.sessionId,
1123
+ model: input.model,
1124
+ cwd: input.cwd,
1125
+ messages: input.messages,
1126
+ usage: input.usage,
1127
+ createdAt: input.createdAt,
1128
+ updatedAt: input.updatedAt,
1129
+ metadata: input.metadata
1130
+ });
1131
+ }
1132
+ decode(value) {
1133
+ if (!value || typeof value !== "object")
1134
+ throw new TypeError("Session snapshot must be an object");
1135
+ const snapshot = value;
1136
+ if (snapshot.schemaVersion !== 1)
1137
+ throw new TypeError("Unsupported session snapshot schemaVersion");
1138
+ if (typeof snapshot.sessionId !== "string")
1139
+ throw new TypeError("Session snapshot sessionId is required");
1140
+ if (!Array.isArray(snapshot.messages))
1141
+ throw new TypeError("Session snapshot messages must be an array");
1142
+ if (typeof snapshot.createdAt !== "number" || typeof snapshot.updatedAt !== "number")
1143
+ throw new TypeError("Session snapshot timestamps are required");
1144
+ return structuredClone(snapshot);
1145
+ }
1146
+ }
1147
+ var sessionSnapshotCodec = new JsonSnapshotCodec;
1148
+
1149
+ // src/utils/usage.ts
1150
+ function createEmptyUsage() {
1151
+ return {
1152
+ promptTokens: 0,
1153
+ completionTokens: 0,
1154
+ totalTokens: 0
1155
+ };
1156
+ }
1157
+ function addUsage(left, right) {
1158
+ if (!right)
1159
+ return { ...left };
1160
+ return {
1161
+ promptTokens: left.promptTokens + right.promptTokens,
1162
+ completionTokens: left.completionTokens + right.completionTokens,
1163
+ totalTokens: left.totalTokens + right.totalTokens
1164
+ };
1165
+ }
1166
+
1167
+ // src/agent-core/tool-call.ts
1168
+ function startToolCall(callId, toolName) {
1169
+ if (!callId.trim())
1170
+ throw new Error("Tool call id is required");
1171
+ if (!toolName.trim())
1172
+ throw new Error("Tool name is required");
1173
+ return {
1174
+ callId,
1175
+ toolName,
1176
+ argsText: ""
1177
+ };
1178
+ }
1179
+ function appendToolCallArgsDelta(draft, delta) {
1180
+ return {
1181
+ ...draft,
1182
+ argsText: `${draft.argsText}${delta}`
1183
+ };
1184
+ }
1185
+ function finalizeToolCall(draft) {
1186
+ const rawArgumentsText = draft.argsText.trim();
1187
+ const argumentsObject = rawArgumentsText.length === 0 ? {} : parseJsonObject(rawArgumentsText, `Tool call ${draft.callId} arguments`);
1188
+ return {
1189
+ id: draft.callId,
1190
+ type: "function",
1191
+ function: {
1192
+ name: draft.toolName,
1193
+ arguments: argumentsObject
1194
+ },
1195
+ rawArgumentsText
1196
+ };
1197
+ }
1198
+
1199
+ // src/agent-core/errors.ts
1200
+ class SessionExecutionError extends Error {
1201
+ payload;
1202
+ constructor(payload) {
1203
+ super(payload.message);
1204
+ this.name = "SessionExecutionError";
1205
+ this.payload = payload;
1206
+ }
1207
+ }
1208
+
1209
+ // src/agent-core/ModelTurnCollector.ts
1210
+ class DefaultModelTurnCollector {
1211
+ async* collect(options) {
1212
+ const assistantTextParts = [];
1213
+ const thinkingParts = [];
1214
+ const toolCalls = [];
1215
+ const drafts = new Map;
1216
+ let stopReason = "final";
1217
+ let responseUsage;
1218
+ let assistantMetadata;
1219
+ for await (const event of options.events) {
1220
+ await options.onEvent?.(event);
1221
+ if (event.type === "response_start")
1222
+ continue;
1223
+ if (event.type === "text_delta") {
1224
+ assistantTextParts.push(event.delta);
1225
+ yield { type: "text_delta", sessionId: options.sessionId, delta: event.delta };
1226
+ continue;
1227
+ }
1228
+ if (event.type === "thinking_delta") {
1229
+ thinkingParts.push(event.delta);
1230
+ yield { type: "thinking_delta", sessionId: options.sessionId, delta: event.delta };
1231
+ continue;
1232
+ }
1233
+ if (event.type === "tool_call_start") {
1234
+ drafts.set(event.callId, startToolCall(event.callId, event.toolName));
1235
+ continue;
1236
+ }
1237
+ if (event.type === "tool_call_args_delta") {
1238
+ const draft = drafts.get(event.callId);
1239
+ if (!draft) {
1240
+ const payload2 = {
1241
+ code: "tool_call_state_error",
1242
+ message: `Received tool_call_args_delta before tool_call_start for ${event.callId}`,
1243
+ requestId: options.requestId
1244
+ };
1245
+ yield { type: "error", sessionId: options.sessionId, error: payload2 };
1246
+ throw new SessionExecutionError(payload2);
1247
+ }
1248
+ drafts.set(event.callId, appendToolCallArgsDelta(draft, event.delta));
1249
+ continue;
1250
+ }
1251
+ if (event.type === "tool_call_end") {
1252
+ const draft = drafts.get(event.callId);
1253
+ if (!draft) {
1254
+ const payload2 = {
1255
+ code: "tool_call_state_error",
1256
+ message: `Received tool_call_end before tool_call_start for ${event.callId}`,
1257
+ requestId: options.requestId
1258
+ };
1259
+ yield { type: "error", sessionId: options.sessionId, error: payload2 };
1260
+ throw new SessionExecutionError(payload2);
1261
+ }
1262
+ try {
1263
+ toolCalls.push(finalizeToolCall(draft));
1264
+ } catch (error) {
1265
+ const payload2 = {
1266
+ code: "tool_call_parse_error",
1267
+ message: error instanceof Error ? error.message : String(error),
1268
+ requestId: options.requestId
1269
+ };
1270
+ yield { type: "error", sessionId: options.sessionId, error: payload2 };
1271
+ throw new SessionExecutionError(payload2);
1272
+ }
1273
+ continue;
1274
+ }
1275
+ if (event.type === "response_end") {
1276
+ stopReason = event.stopReason;
1277
+ responseUsage = event.usage;
1278
+ assistantMetadata = event.assistantMetadata;
1279
+ continue;
1280
+ }
1281
+ const payload = {
1282
+ ...event.error,
1283
+ requestId: options.requestId
1284
+ };
1285
+ yield { type: "error", sessionId: options.sessionId, error: payload };
1286
+ throw new SessionExecutionError(payload);
1287
+ }
1288
+ return {
1289
+ requestId: options.requestId,
1290
+ text: assistantTextParts.join(""),
1291
+ thinking: thinkingParts.join("") || undefined,
1292
+ toolCalls,
1293
+ stopReason,
1294
+ usage: responseUsage,
1295
+ assistantMetadata
1296
+ };
1297
+ }
1298
+ }
1299
+
1300
+ // src/agent-core/session-state.ts
1301
+ function touchRuntimeSessionState(state) {
1302
+ state.updatedAt = Date.now();
1303
+ }
1304
+
1305
+ // src/agent-core/LoopRunner.ts
1306
+ class LoopRunner {
1307
+ messageFactory;
1308
+ turnCollector;
1309
+ toolExecutor;
1310
+ terminationPolicy;
1311
+ contextManager;
1312
+ pluginHost;
1313
+ constructor(dependencies) {
1314
+ this.messageFactory = dependencies.messageFactory;
1315
+ this.turnCollector = dependencies.turnCollector;
1316
+ this.toolExecutor = dependencies.toolExecutor;
1317
+ this.terminationPolicy = dependencies.terminationPolicy;
1318
+ this.contextManager = dependencies.contextManager;
1319
+ this.pluginHost = dependencies.pluginHost;
1320
+ }
1321
+ async* run(state, input, options = {}) {
1322
+ const startPayload = await this.pluginHost?.runMiddleware("session.start", {
1323
+ sessionId: state.id,
1324
+ input
1325
+ }, {
1326
+ sessionId: state.id,
1327
+ cwd: state.cwd,
1328
+ metadata: state.metadata
1329
+ });
1330
+ const initialInput = startPayload?.input ?? input;
1331
+ const userMessage = this.messageFactory.createUserMessage(initialInput);
1332
+ state.messages.push(userMessage);
1333
+ touchRuntimeSessionState(state);
1334
+ let lastRequestId;
1335
+ try {
1336
+ for (let iteration = 0;iteration < state.maxIterations; iteration += 1) {
1337
+ const requestId = createId("req");
1338
+ lastRequestId = requestId;
1339
+ const turnStart = await this.pluginHost?.runMiddleware("turn.start", {
1340
+ requestId,
1341
+ iteration,
1342
+ messages: [...state.messages]
1343
+ }, {
1344
+ sessionId: state.id,
1345
+ requestId,
1346
+ iteration,
1347
+ cwd: state.cwd,
1348
+ metadata: state.metadata
1349
+ });
1350
+ const contextItems = await this.contextManager.resolve({
1351
+ sessionId: state.id,
1352
+ input: initialInput,
1353
+ messages: state.messages,
1354
+ cwd: state.cwd
1355
+ });
1356
+ const resolvedContext = await this.pluginHost?.runMiddleware("context.resolve", {
1357
+ contextItems,
1358
+ input: initialInput,
1359
+ messages: [...state.messages]
1360
+ }, {
1361
+ sessionId: state.id,
1362
+ requestId,
1363
+ iteration,
1364
+ cwd: state.cwd,
1365
+ metadata: state.metadata
1366
+ });
1367
+ const effectiveContext = resolvedContext?.contextItems ?? contextItems;
1368
+ const pluginPromptSegments = await this.pluginHost?.collectPromptSegments({
1369
+ sessionId: state.id,
1370
+ input: initialInput,
1371
+ messages: state.messages,
1372
+ contextItems: effectiveContext,
1373
+ cwd: state.cwd
1374
+ }) ?? [];
1375
+ const promptSegments = [
1376
+ ...this.contextManager.format(effectiveContext),
1377
+ ...pluginPromptSegments
1378
+ ];
1379
+ const resolvedPrompt = await this.pluginHost?.runMiddleware("prompt.resolve", {
1380
+ promptSegments,
1381
+ contextItems: effectiveContext,
1382
+ messages: [...state.messages]
1383
+ }, {
1384
+ sessionId: state.id,
1385
+ requestId,
1386
+ iteration,
1387
+ cwd: state.cwd,
1388
+ metadata: state.metadata
1389
+ });
1390
+ const effectivePromptSegments = resolvedPrompt?.promptSegments ?? promptSegments;
1391
+ const toolDefinitions = state.tools?.definitions() ?? [];
1392
+ const resolvedToolset = await this.pluginHost?.runMiddleware("toolset.resolve", {
1393
+ tools: toolDefinitions
1394
+ }, {
1395
+ sessionId: state.id,
1396
+ requestId,
1397
+ iteration,
1398
+ cwd: state.cwd,
1399
+ metadata: state.metadata
1400
+ });
1401
+ const effectiveTools = resolvedToolset?.tools ?? toolDefinitions;
1402
+ const requestMessages = [...turnStart?.messages ?? state.messages];
1403
+ if (effectivePromptSegments.length > 0)
1404
+ requestMessages.unshift(this.messageFactory.createSystemMessage(effectivePromptSegments.join(`
1405
+
1406
+ `)));
1407
+ const request = {
1408
+ requestId,
1409
+ model: state.modelRef,
1410
+ messages: requestMessages,
1411
+ tools: effectiveTools,
1412
+ reasoning: state.reasoning,
1413
+ signal: options.signal
1414
+ };
1415
+ const resolvedRequest = await this.pluginHost?.runMiddleware("model.request", {
1416
+ request
1417
+ }, {
1418
+ sessionId: state.id,
1419
+ requestId,
1420
+ iteration,
1421
+ cwd: state.cwd,
1422
+ metadata: state.metadata
1423
+ });
1424
+ const effectiveRequest = resolvedRequest?.request ?? request;
1425
+ const turn = yield* this.turnCollector.collect({
1426
+ sessionId: state.id,
1427
+ requestId,
1428
+ events: state.model.stream(effectiveRequest),
1429
+ onEvent: async (event) => {
1430
+ await this.pluginHost?.runObservers("model.event", { event }, {
1431
+ sessionId: state.id,
1432
+ requestId,
1433
+ iteration,
1434
+ cwd: state.cwd,
1435
+ metadata: state.metadata
1436
+ });
1437
+ }
1438
+ });
1439
+ const assistantDraft = this.messageFactory.createAssistantMessage({
1440
+ text: turn.text,
1441
+ thinking: turn.thinking,
1442
+ toolCalls: turn.toolCalls,
1443
+ stopReason: turn.stopReason,
1444
+ metadata: turn.assistantMetadata
1445
+ });
1446
+ const assistantPayload = await this.pluginHost?.runMiddleware("assistant.message", {
1447
+ message: assistantDraft
1448
+ }, {
1449
+ sessionId: state.id,
1450
+ requestId,
1451
+ iteration,
1452
+ cwd: state.cwd,
1453
+ metadata: state.metadata
1454
+ });
1455
+ const assistantMessage = assistantPayload?.message ?? assistantDraft;
1456
+ state.messages.push(assistantMessage);
1457
+ state.usage = addUsage(state.usage, turn.usage);
1458
+ touchRuntimeSessionState(state);
1459
+ const baseDecision = this.terminationPolicy.afterAssistantTurn(turn.toolCalls);
1460
+ const decisionPayload = await this.pluginHost?.runMiddleware("loop.decide", {
1461
+ toolCalls: turn.toolCalls,
1462
+ stopReason: turn.stopReason,
1463
+ decision: baseDecision
1464
+ }, {
1465
+ sessionId: state.id,
1466
+ requestId,
1467
+ iteration,
1468
+ cwd: state.cwd,
1469
+ metadata: state.metadata
1470
+ });
1471
+ const decision = decisionPayload?.decision ?? baseDecision;
1472
+ if (decision.type === "done") {
1473
+ yield {
1474
+ type: "done",
1475
+ sessionId: state.id,
1476
+ message: assistantMessage,
1477
+ usage: { ...state.usage }
1478
+ };
1479
+ await this.pluginHost?.runObservers("session.end", { message: assistantMessage }, {
1480
+ sessionId: state.id,
1481
+ requestId,
1482
+ iteration,
1483
+ cwd: state.cwd,
1484
+ metadata: state.metadata
1485
+ });
1486
+ await options.onDone?.(assistantMessage);
1487
+ return assistantMessage;
1488
+ }
1489
+ for (const toolCall of turn.toolCalls) {
1490
+ yield { type: "tool_call", sessionId: state.id, toolCall };
1491
+ const result = await this.toolExecutor.execute(toolCall, {
1492
+ signal: options.signal,
1493
+ cwd: state.cwd
1494
+ });
1495
+ const toolMessage = this.messageFactory.createToolMessage(toolCall, result);
1496
+ state.messages.push(toolMessage);
1497
+ touchRuntimeSessionState(state);
1498
+ yield { type: "tool_result", sessionId: state.id, toolCallId: toolCall.id, result };
1499
+ }
1500
+ }
1501
+ } catch (error) {
1502
+ if (error instanceof SessionExecutionError) {
1503
+ await this.pluginHost?.runObservers("session.error", { error: error.payload }, {
1504
+ sessionId: state.id,
1505
+ requestId: lastRequestId,
1506
+ cwd: state.cwd,
1507
+ metadata: state.metadata
1508
+ });
1509
+ }
1510
+ throw error;
1511
+ }
1512
+ const payload = this.terminationPolicy.onMaxIterationsExceeded(state.maxIterations, lastRequestId);
1513
+ yield { type: "error", sessionId: state.id, error: payload };
1514
+ await this.pluginHost?.runObservers("session.error", { error: payload }, {
1515
+ sessionId: state.id,
1516
+ requestId: lastRequestId,
1517
+ cwd: state.cwd,
1518
+ metadata: state.metadata
1519
+ });
1520
+ throw new SessionExecutionError(payload);
1521
+ }
1522
+ }
1523
+
1524
+ // src/agent-core/TerminationPolicy.ts
1525
+ class DefaultTerminationPolicy {
1526
+ afterAssistantTurn(toolCalls) {
1527
+ return toolCalls.length === 0 ? { type: "done" } : { type: "continue" };
1528
+ }
1529
+ onMaxIterationsExceeded(maxIterations, requestId) {
1530
+ return {
1531
+ code: "max_iterations_exceeded",
1532
+ message: `Session exceeded maxIterations (${maxIterations})`,
1533
+ requestId
1534
+ };
1535
+ }
1536
+ }
1537
+
1538
+ // src/agent-core/ToolExecutor.ts
1539
+ class DefaultToolExecutor {
1540
+ sessionId;
1541
+ tools;
1542
+ metadata;
1543
+ services;
1544
+ pluginHost;
1545
+ constructor(options) {
1546
+ this.sessionId = options.sessionId;
1547
+ this.tools = options.tools;
1548
+ this.metadata = options.metadata;
1549
+ this.services = options.services;
1550
+ this.pluginHost = options.pluginHost;
1551
+ }
1552
+ async execute(toolCall, options = {}) {
1553
+ const before = await this.pluginHost?.runMiddleware("tool.beforeExecute", {
1554
+ toolCall
1555
+ }, {
1556
+ sessionId: this.sessionId,
1557
+ cwd: options.cwd,
1558
+ services: this.services,
1559
+ metadata: this.metadata
1560
+ });
1561
+ const currentToolCall = before?.toolCall ?? toolCall;
1562
+ const tool = this.tools?.get(currentToolCall.function.name);
1563
+ if (!tool)
1564
+ return normalizeToolResult({ ...textToolResult(`Tool not found: ${currentToolCall.function.name}`), isError: true });
1565
+ let result;
1566
+ try {
1567
+ result = normalizeToolResult(await tool.execute(currentToolCall.function.arguments, {
1568
+ sessionId: this.sessionId,
1569
+ cwd: options.cwd,
1570
+ signal: options.signal,
1571
+ metadata: this.metadata,
1572
+ services: this.services
1573
+ }));
1574
+ } catch (error) {
1575
+ const message = error instanceof Error ? error.message : String(error);
1576
+ result = normalizeToolResult({
1577
+ ...textToolResult(message),
1578
+ isError: true
1579
+ });
1580
+ }
1581
+ const after = await this.pluginHost?.runMiddleware("tool.afterExecute", {
1582
+ toolCall: currentToolCall,
1583
+ result
1584
+ }, {
1585
+ sessionId: this.sessionId,
1586
+ cwd: options.cwd,
1587
+ services: this.services,
1588
+ metadata: this.metadata
1589
+ });
1590
+ return normalizeToolResult(after?.result ?? result);
1591
+ }
1592
+ }
1593
+
1594
+ // src/agent-core/Session.ts
1595
+ class Session {
1596
+ id;
1597
+ createdAt;
1598
+ stateStore;
1599
+ state;
1600
+ pluginHost;
1601
+ contextManager;
1602
+ services;
1603
+ constructor(options) {
1604
+ const now = Date.now();
1605
+ this.id = options.id ?? createId("session");
1606
+ this.createdAt = options.createdAt ?? now;
1607
+ this.stateStore = options.stateStore;
1608
+ this.pluginHost = options.pluginHost;
1609
+ this.contextManager = options.contextManager;
1610
+ this.services = options.services;
1611
+ this.state = {
1612
+ id: this.id,
1613
+ model: options.model,
1614
+ modelRef: options.modelRef ?? options.model.model,
1615
+ tools: options.tools,
1616
+ maxIterations: options.maxIterations ?? 8,
1617
+ cwd: options.cwd,
1618
+ metadata: options.metadata,
1619
+ reasoning: options.reasoning,
1620
+ messages: [...options.messages ?? []],
1621
+ usage: options.usage ? { ...options.usage } : createEmptyUsage(),
1622
+ createdAt: this.createdAt,
1623
+ updatedAt: options.updatedAt ?? now
1624
+ };
1625
+ }
1626
+ get messages() {
1627
+ return [...this.state.messages];
1628
+ }
1629
+ get usage() {
1630
+ return { ...this.state.usage };
1631
+ }
1632
+ get updatedAt() {
1633
+ return this.state.updatedAt;
1634
+ }
1635
+ getCwd() {
1636
+ return this.state.cwd;
1637
+ }
1638
+ setCwd(cwd) {
1639
+ this.state.cwd = cwd;
1640
+ touchRuntimeSessionState(this.state);
1641
+ }
1642
+ async save() {
1643
+ if (!this.stateStore)
1644
+ return;
1645
+ let snapshot = this.toSnapshot();
1646
+ if (this.pluginHost) {
1647
+ const payload = await this.pluginHost.runMiddleware("snapshot.encode", { snapshot }, {
1648
+ sessionId: this.id,
1649
+ cwd: this.state.cwd,
1650
+ metadata: this.state.metadata,
1651
+ services: this.services
1652
+ });
1653
+ snapshot = payload.snapshot;
1654
+ }
1655
+ await this.stateStore.save(snapshot);
1656
+ }
1657
+ toSnapshot() {
1658
+ return sessionSnapshotCodec.encode({
1659
+ sessionId: this.id,
1660
+ model: this.state.modelRef,
1661
+ cwd: this.state.cwd,
1662
+ messages: this.messages,
1663
+ usage: this.usage,
1664
+ createdAt: this.createdAt,
1665
+ updatedAt: this.updatedAt,
1666
+ metadata: this.state.metadata ? { ...this.state.metadata } : undefined
1667
+ });
1668
+ }
1669
+ async send(input, options) {
1670
+ const stream = this.stream(input, options);
1671
+ let doneMessage;
1672
+ while (true) {
1673
+ const next = await stream.next();
1674
+ if (next.done) {
1675
+ doneMessage = next.value;
1676
+ break;
1677
+ }
1678
+ }
1679
+ if (!doneMessage)
1680
+ throw new Error("Session completed without a final assistant message");
1681
+ return doneMessage;
1682
+ }
1683
+ async* stream(input, options) {
1684
+ const runner = this.createLoopRunner();
1685
+ return yield* runner.run(this.state, input, {
1686
+ signal: options?.signal,
1687
+ onDone: async () => {
1688
+ await this.save();
1689
+ }
1690
+ });
1691
+ }
1692
+ createLoopRunner() {
1693
+ return new LoopRunner({
1694
+ messageFactory: new DefaultMessageFactory,
1695
+ turnCollector: new DefaultModelTurnCollector,
1696
+ toolExecutor: new DefaultToolExecutor({
1697
+ sessionId: this.id,
1698
+ tools: this.state.tools,
1699
+ metadata: this.state.metadata,
1700
+ services: this.services,
1701
+ pluginHost: this.pluginHost
1702
+ }),
1703
+ terminationPolicy: new DefaultTerminationPolicy,
1704
+ contextManager: this.contextManager,
1705
+ pluginHost: this.pluginHost
1706
+ });
1707
+ }
1708
+ }
1709
+
1710
+ // src/agent-core/Agent.ts
1711
+ class Agent {
1712
+ id;
1713
+ pluginHost;
1714
+ services;
1715
+ tools;
1716
+ permissions;
1717
+ options;
1718
+ messageFactory = new DefaultMessageFactory;
1719
+ contextManager;
1720
+ tracker;
1721
+ permissionGateway;
1722
+ constructor(options) {
1723
+ this.id = createId("agent");
1724
+ this.options = options;
1725
+ this.permissions = options.permissions;
1726
+ this.tracker = new ActivityTracker;
1727
+ this.services = createServices(options, this.tracker);
1728
+ this.permissionGateway = new PermissionGateway({ permissions: options.permissions });
1729
+ this.pluginHost = new PluginHost({
1730
+ agentId: this.id,
1731
+ cwd: options.cwd,
1732
+ plugins: options.plugins,
1733
+ services: this.services,
1734
+ permissionGateway: this.permissionGateway,
1735
+ tracker: this.tracker
1736
+ });
1737
+ this.contextManager = options.contextManager ?? new AutoContextManager({
1738
+ services: this.services,
1739
+ pluginHost: this.pluginHost
1740
+ });
1741
+ this.tools = createToolRegistry({
1742
+ includeBuiltinTools: options.includeBuiltinTools !== false,
1743
+ tools: options.tools,
1744
+ pluginTools: this.pluginHost.pluginTools()
1745
+ });
1746
+ }
1747
+ async createSession(options = {}) {
1748
+ const messages = options.messages ? [...options.messages] : [];
1749
+ if (options.systemPrompt)
1750
+ messages.unshift(this.messageFactory.createSystemMessage(options.systemPrompt));
1751
+ return new Session({
1752
+ id: options.sessionId ?? createId("session"),
1753
+ model: this.options.model,
1754
+ modelRef: this.options.model.model,
1755
+ tools: this.tools,
1756
+ stateStore: this.options.stateStore,
1757
+ maxIterations: this.options.maxIterations,
1758
+ cwd: options.cwd ?? this.options.cwd,
1759
+ metadata: options.metadata ?? this.options.metadata,
1760
+ reasoning: this.options.reasoning,
1761
+ messages,
1762
+ pluginHost: this.pluginHost,
1763
+ contextManager: this.contextManager,
1764
+ services: this.services
1765
+ });
1766
+ }
1767
+ async restoreSession(sessionId) {
1768
+ if (!this.options.stateStore)
1769
+ return null;
1770
+ const snapshot = await this.options.stateStore.load(sessionId);
1771
+ if (!snapshot)
1772
+ return null;
1773
+ return this.sessionFromSnapshot(snapshot);
1774
+ }
1775
+ sessionFromSnapshot(snapshot) {
1776
+ return new Session({
1777
+ id: snapshot.sessionId,
1778
+ model: this.options.model,
1779
+ modelRef: snapshot.model ?? this.options.model.model,
1780
+ tools: this.tools,
1781
+ stateStore: this.options.stateStore,
1782
+ maxIterations: this.options.maxIterations,
1783
+ cwd: snapshot.cwd ?? this.options.cwd,
1784
+ metadata: snapshot.metadata ?? this.options.metadata,
1785
+ reasoning: this.options.reasoning,
1786
+ messages: snapshot.messages,
1787
+ usage: snapshot.usage,
1788
+ createdAt: snapshot.createdAt,
1789
+ updatedAt: snapshot.updatedAt,
1790
+ pluginHost: this.pluginHost,
1791
+ contextManager: this.contextManager,
1792
+ services: this.services
1793
+ });
1794
+ }
1795
+ }
1796
+ function createAgent(options) {
1797
+ return new Agent(options);
1798
+ }
1799
+ function createServices(options, tracker) {
1800
+ const exec = new NodeExecGateway({ cwd: options.cwd, tracker });
1801
+ return {
1802
+ fileSystem: new NodeFileSystemGateway({ cwd: options.cwd, tracker }),
1803
+ exec,
1804
+ git: new DefaultGitGateway({ exec, cwd: options.cwd }),
1805
+ network: new DefaultNetworkGateway,
1806
+ model: new DefaultModelGateway(options.model)
1807
+ };
1808
+ }
1809
+ function createToolRegistry(input) {
1810
+ const registry = input.tools instanceof ToolRegistry ? cloneRegistry(input.tools) : new ToolRegistry;
1811
+ if (input.includeBuiltinTools) {
1812
+ safeRegister(registry, new ReadTool);
1813
+ safeRegister(registry, new WriteTool);
1814
+ safeRegister(registry, new EditTool);
1815
+ safeRegister(registry, new ExecTool);
1816
+ }
1817
+ for (const tool of Array.isArray(input.tools) ? input.tools : [])
1818
+ safeRegister(registry, tool);
1819
+ for (const tool of input.pluginTools)
1820
+ safeRegister(registry, tool);
1821
+ return registry;
1822
+ }
1823
+ function cloneRegistry(source) {
1824
+ const registry = new ToolRegistry;
1825
+ for (const tool of source.list())
1826
+ registry.register(tool);
1827
+ return registry;
1828
+ }
1829
+ function safeRegister(registry, tool) {
1830
+ if (!registry.get(tool.definition.name))
1831
+ registry.register(tool);
1832
+ }
1833
+ // src/persistence/FileStateStore.ts
1834
+ import { mkdir as mkdir2, readFile as readFile2, readdir as readdir2, rm, writeFile as writeFile2 } from "node:fs/promises";
1835
+ import path3 from "node:path";
1836
+ class FileStateStore {
1837
+ directory;
1838
+ constructor(options) {
1839
+ this.directory = path3.resolve(options.directory);
1840
+ }
1841
+ async save(snapshot) {
1842
+ const validated = sessionSnapshotCodec.decode(snapshot);
1843
+ await mkdir2(this.directory, { recursive: true });
1844
+ await writeFile2(this.filePath(validated.sessionId), JSON.stringify(validated, null, 2), "utf8");
1845
+ }
1846
+ async load(sessionId) {
1847
+ try {
1848
+ const raw = await readFile2(this.filePath(sessionId), "utf8");
1849
+ return sessionSnapshotCodec.decode(JSON.parse(raw));
1850
+ } catch (error) {
1851
+ if (error.code === "ENOENT")
1852
+ return null;
1853
+ throw error;
1854
+ }
1855
+ }
1856
+ async delete(sessionId) {
1857
+ await rm(this.filePath(sessionId), { force: true });
1858
+ }
1859
+ async list() {
1860
+ await mkdir2(this.directory, { recursive: true });
1861
+ const entries = (await readdir2(this.directory)).sort();
1862
+ const snapshots = [];
1863
+ for (const entry of entries) {
1864
+ if (!entry.endsWith(".json"))
1865
+ continue;
1866
+ const raw = await readFile2(path3.join(this.directory, entry), "utf8");
1867
+ snapshots.push(sessionSnapshotCodec.decode(JSON.parse(raw)));
1868
+ }
1869
+ return snapshots;
1870
+ }
1871
+ filePath(sessionId) {
1872
+ return path3.join(this.directory, `${encodeURIComponent(sessionId)}.json`);
1873
+ }
1874
+ }
1875
+ // src/persistence/InMemoryStateStore.ts
1876
+ class InMemoryStateStore {
1877
+ snapshots = new Map;
1878
+ async save(snapshot) {
1879
+ const validated = sessionSnapshotCodec.decode(snapshot);
1880
+ this.snapshots.set(validated.sessionId, validated);
1881
+ }
1882
+ async load(sessionId) {
1883
+ const snapshot = this.snapshots.get(sessionId);
1884
+ return snapshot ? sessionSnapshotCodec.decode(snapshot) : null;
1885
+ }
1886
+ async delete(sessionId) {
1887
+ this.snapshots.delete(sessionId);
1888
+ }
1889
+ async list() {
1890
+ return [...this.snapshots.values()].map((snapshot) => sessionSnapshotCodec.decode(snapshot));
1891
+ }
1892
+ }
1893
+ // src/providers/shared/http-error.ts
1894
+ var MAX_TEXT_CHARS = 4000;
1895
+ var MAX_JSON_DEPTH = 4;
1896
+ var MAX_JSON_ARRAY_ITEMS = 20;
1897
+ var MAX_JSON_OBJECT_KEYS = 40;
1898
+ var TRUNCATED_SUFFIX = "...[truncated]";
1899
+ async function createHttpErrorPayload(input) {
1900
+ const responseBody = await readErrorResponseBody(input.response);
1901
+ return {
1902
+ code: input.code,
1903
+ message: `${input.provider} request failed with status ${input.response.status}`,
1904
+ status: input.response.status,
1905
+ details: {
1906
+ provider: input.provider,
1907
+ endpoint: input.endpoint,
1908
+ statusText: input.response.statusText,
1909
+ ...responseBody === undefined ? {} : { responseBody }
1910
+ }
1911
+ };
1912
+ }
1913
+ async function readErrorResponseBody(response) {
1914
+ let text = "";
1915
+ try {
1916
+ text = await response.text();
1917
+ } catch {
1918
+ return;
1919
+ }
1920
+ const normalized = text.trim();
1921
+ if (!normalized)
1922
+ return;
1923
+ try {
1924
+ return limitJsonValue(JSON.parse(normalized));
1925
+ } catch {
1926
+ return truncateText(normalized);
1927
+ }
1928
+ }
1929
+ function limitJsonValue(value, depth = 0) {
1930
+ if (value === null || typeof value === "boolean" || typeof value === "number")
1931
+ return value;
1932
+ if (typeof value === "string")
1933
+ return truncateText(value);
1934
+ if (Array.isArray(value))
1935
+ return value.slice(0, MAX_JSON_ARRAY_ITEMS).map((item) => limitJsonValue(item, depth + 1));
1936
+ if (typeof value === "object") {
1937
+ if (depth >= MAX_JSON_DEPTH)
1938
+ return truncateText(JSON.stringify(value));
1939
+ const limited = {};
1940
+ for (const [key, child] of Object.entries(value).slice(0, MAX_JSON_OBJECT_KEYS))
1941
+ limited[key] = limitJsonValue(child, depth + 1);
1942
+ return limited;
1943
+ }
1944
+ return truncateText(String(value));
1945
+ }
1946
+ function truncateText(value) {
1947
+ if (value.length <= MAX_TEXT_CHARS)
1948
+ return value;
1949
+ return `${value.slice(0, MAX_TEXT_CHARS)}${TRUNCATED_SUFFIX}`;
1950
+ }
1951
+
1952
+ // src/providers/shared/provider-state.ts
1953
+ var PROVIDER_STATE_METADATA_KEY = "_dimProviderState";
1954
+ function attachProviderState(metadata, provider, state) {
1955
+ if (!state || Object.keys(state).length === 0)
1956
+ return metadata;
1957
+ const nextMetadata = metadata ? structuredClone(metadata) : {};
1958
+ const currentBag = isRecord2(nextMetadata[PROVIDER_STATE_METADATA_KEY]) ? nextMetadata[PROVIDER_STATE_METADATA_KEY] : {};
1959
+ nextMetadata[PROVIDER_STATE_METADATA_KEY] = {
1960
+ ...currentBag,
1961
+ [provider]: structuredClone(state)
1962
+ };
1963
+ return nextMetadata;
1964
+ }
1965
+ function readProviderState(message, provider) {
1966
+ if (message.role !== "assistant" || !isRecord2(message.metadata))
1967
+ return;
1968
+ const bag = message.metadata[PROVIDER_STATE_METADATA_KEY];
1969
+ if (!isRecord2(bag))
1970
+ return;
1971
+ const state = bag[provider];
1972
+ return isRecord2(state) ? structuredClone(state) : undefined;
1973
+ }
1974
+ function isRecord2(value) {
1975
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
1976
+ }
1977
+
1978
+ // src/providers/shared/reasoning.ts
1979
+ function isRecord3(value) {
1980
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
1981
+ }
1982
+ function normalizeReasoningConfig(reasoning) {
1983
+ if (!isRecord3(reasoning))
1984
+ return;
1985
+ if (reasoning.enabled === false)
1986
+ return;
1987
+ return reasoning;
1988
+ }
1989
+ function resolveReasoningBudget(reasoning, fallback = 1024) {
1990
+ const normalized = normalizeReasoningConfig(reasoning);
1991
+ if (!normalized)
1992
+ return;
1993
+ for (const key of ["budgetTokens", "budget_tokens", "thinkingBudget"]) {
1994
+ const value = normalized[key];
1995
+ if (typeof value === "number" && Number.isFinite(value) && value > 0)
1996
+ return Math.floor(value);
1997
+ }
1998
+ return fallback;
1999
+ }
2000
+ function buildOpenAIResponsesReasoning(reasoning) {
2001
+ const normalized = normalizeReasoningConfig(reasoning);
2002
+ if (!normalized)
2003
+ return;
2004
+ const next = {};
2005
+ for (const [key, value] of Object.entries(normalized)) {
2006
+ if (key === "enabled" || key === "budgetTokens" || key === "budget_tokens" || key === "thinkingBudget" || key === "includeThoughts")
2007
+ continue;
2008
+ next[key] = value;
2009
+ }
2010
+ if (next.summary === undefined)
2011
+ next.summary = "auto";
2012
+ return next;
2013
+ }
2014
+ function buildAnthropicThinking(reasoning) {
2015
+ const budget = resolveReasoningBudget(reasoning);
2016
+ if (!budget)
2017
+ return { maxTokens: undefined, thinking: undefined };
2018
+ return {
2019
+ maxTokens: Math.max(budget + 1, budget * 2),
2020
+ thinking: {
2021
+ type: "enabled",
2022
+ budget_tokens: budget
2023
+ }
2024
+ };
2025
+ }
2026
+ function buildGeminiThinkingConfig(reasoning) {
2027
+ const normalized = normalizeReasoningConfig(reasoning);
2028
+ if (!normalized)
2029
+ return;
2030
+ return {
2031
+ thinkingBudget: resolveReasoningBudget(normalized) ?? 1024,
2032
+ includeThoughts: normalized.includeThoughts !== false
2033
+ };
2034
+ }
2035
+
2036
+ // src/providers/shared/usage.ts
2037
+ function normalizeUsage(raw) {
2038
+ if (!raw || typeof raw !== "object")
2039
+ return;
2040
+ const usage = raw;
2041
+ const promptTokens = numberOrZero(usage.prompt_tokens ?? usage.input_tokens ?? usage.promptTokenCount);
2042
+ const completionTokens = numberOrZero(usage.completion_tokens ?? usage.output_tokens ?? usage.candidatesTokenCount);
2043
+ const totalTokens = numberOrZero(usage.total_tokens ?? usage.totalTokenCount) || promptTokens + completionTokens;
2044
+ if (promptTokens === 0 && completionTokens === 0 && totalTokens === 0)
2045
+ return;
2046
+ return {
2047
+ promptTokens,
2048
+ completionTokens,
2049
+ totalTokens
2050
+ };
2051
+ }
2052
+ function numberOrZero(value) {
2053
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
2054
+ }
2055
+
2056
+ // src/providers/anthropic/mapper.ts
2057
+ var ANTHROPIC_STATE_KEY = "anthropic";
2058
+ function mapToolDefinitionsToAnthropic(tools) {
2059
+ if (!tools || tools.length === 0)
2060
+ return;
2061
+ return tools.map((tool) => ({
2062
+ name: tool.name,
2063
+ description: tool.description,
2064
+ input_schema: tool.inputSchema
2065
+ }));
2066
+ }
2067
+ function mapAnthropicStopReason(reason, toolCallCount) {
2068
+ if (toolCallCount > 0 || reason === "tool_use")
2069
+ return "tool_call";
2070
+ if (reason === "max_tokens")
2071
+ return "length";
2072
+ if (reason === "cancelled")
2073
+ return "cancelled";
2074
+ if (reason === "error")
2075
+ return "error";
2076
+ return "final";
2077
+ }
2078
+ function messagesToAnthropic(messages) {
2079
+ const systemChunks = [];
2080
+ const converted = [];
2081
+ for (const message of messages) {
2082
+ if (message.role === "system") {
2083
+ const text = contentToText(message.content);
2084
+ if (text)
2085
+ systemChunks.push(text);
2086
+ continue;
2087
+ }
2088
+ if (message.role === "tool") {
2089
+ converted.push({
2090
+ role: "user",
2091
+ content: [{
2092
+ type: "tool_result",
2093
+ tool_use_id: message.toolCallId,
2094
+ content: contentToText(message.content),
2095
+ is_error: message.isError === true
2096
+ }]
2097
+ });
2098
+ continue;
2099
+ }
2100
+ if (message.role === "assistant") {
2101
+ const blocks = [
2102
+ ...restoreAnthropicThinkingBlocks(message),
2103
+ ...message.content.map((block) => block.type === "text" ? { type: "text", text: block.text } : { type: "image", source: { type: "base64", media_type: block.mimeType, data: block.data } })
2104
+ ];
2105
+ for (const toolCall of message.toolCalls ?? []) {
2106
+ blocks.push({
2107
+ type: "tool_use",
2108
+ id: toolCall.id,
2109
+ name: toolCall.function.name,
2110
+ input: toolCall.function.arguments
2111
+ });
2112
+ }
2113
+ converted.push({ role: "assistant", content: blocks });
2114
+ continue;
2115
+ }
2116
+ converted.push({
2117
+ role: "user",
2118
+ content: message.content.map((block) => block.type === "text" ? { type: "text", text: block.text } : { type: "image", source: { type: "base64", media_type: block.mimeType, data: block.data } })
2119
+ });
2120
+ }
2121
+ return {
2122
+ system: systemChunks.length > 0 ? systemChunks.join(`
2123
+
2124
+ `) : undefined,
2125
+ messages: converted
2126
+ };
2127
+ }
2128
+ function normalizeAnthropicToolCalls(value) {
2129
+ if (!value)
2130
+ return [];
2131
+ const calls = [];
2132
+ for (const block of value) {
2133
+ if (block.type !== "tool_use" || typeof block.id !== "string" || typeof block.name !== "string")
2134
+ continue;
2135
+ calls.push({
2136
+ id: block.id,
2137
+ name: block.name,
2138
+ argsText: JSON.stringify(block.input ?? {})
2139
+ });
2140
+ }
2141
+ return calls;
2142
+ }
2143
+ function extractAnthropicThinking(value) {
2144
+ if (!value)
2145
+ return [];
2146
+ return value.filter((block) => block.type === "thinking").map((block) => block.thinking).filter((value2) => typeof value2 === "string" && value2.length > 0);
2147
+ }
2148
+ function createAnthropicThinkingState(value) {
2149
+ if (!value)
2150
+ return;
2151
+ const blocks = value.filter((block) => block.type === "thinking" || block.type === "redacted_thinking").map((block) => {
2152
+ if (block.type === "thinking")
2153
+ return { type: "thinking", thinking: block.thinking, signature: block.signature };
2154
+ return { type: "redacted_thinking", data: block.data };
2155
+ }).filter((block) => block.type === "thinking" && typeof block.signature === "string" || block.type === "redacted_thinking" && typeof block.data === "string");
2156
+ if (blocks.length === 0)
2157
+ return;
2158
+ return { blocks };
2159
+ }
2160
+ function restoreAnthropicThinkingBlocks(message) {
2161
+ const state = readProviderState(message, ANTHROPIC_STATE_KEY);
2162
+ const rawBlocks = state?.blocks;
2163
+ if (!Array.isArray(rawBlocks))
2164
+ return [];
2165
+ const blocks = [];
2166
+ for (const rawBlock of rawBlocks) {
2167
+ if (!isRecord4(rawBlock) || typeof rawBlock.type !== "string")
2168
+ continue;
2169
+ if (rawBlock.type === "thinking" && typeof rawBlock.signature === "string") {
2170
+ blocks.push({
2171
+ type: "thinking",
2172
+ thinking: typeof rawBlock.thinking === "string" ? rawBlock.thinking : undefined,
2173
+ signature: rawBlock.signature
2174
+ });
2175
+ continue;
2176
+ }
2177
+ if (rawBlock.type === "redacted_thinking" && typeof rawBlock.data === "string") {
2178
+ blocks.push({
2179
+ type: "redacted_thinking",
2180
+ data: rawBlock.data
2181
+ });
2182
+ }
2183
+ }
2184
+ return blocks;
2185
+ }
2186
+ function isRecord4(value) {
2187
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
2188
+ }
2189
+
2190
+ // src/providers/anthropic/adapter.ts
2191
+ function createAnthropicAdapter(options) {
2192
+ const fetchImpl = options.fetch ?? globalThis.fetch;
2193
+ const provider = options.provider ?? "anthropic";
2194
+ const defaultModel = { provider, modelId: options.defaultModel };
2195
+ const baseUrl = (options.baseUrl ?? "https://api.anthropic.com").replace(/\/$/, "");
2196
+ const anthropicVersion = options.anthropicVersion ?? "2023-06-01";
2197
+ return {
2198
+ provider,
2199
+ defaultModel,
2200
+ async* stream(request) {
2201
+ const requestId = request.requestId ?? crypto.randomUUID();
2202
+ yield { type: "response_start", requestId, model: request.model };
2203
+ const { system, messages } = messagesToAnthropic(request.messages);
2204
+ const thinkingConfig = buildAnthropicThinking(request.reasoning);
2205
+ const maxTokens = Math.max(request.maxOutputTokens ?? 1024, thinkingConfig.maxTokens ?? 0);
2206
+ let response;
2207
+ try {
2208
+ response = await fetchImpl(`${baseUrl}/v1/messages`, {
2209
+ method: "POST",
2210
+ headers: {
2211
+ "content-type": "application/json",
2212
+ "anthropic-version": anthropicVersion,
2213
+ ...options.apiKey ? { "x-api-key": options.apiKey } : {},
2214
+ ...options.headers
2215
+ },
2216
+ body: JSON.stringify({
2217
+ model: request.model.modelId,
2218
+ system,
2219
+ messages,
2220
+ tools: mapToolDefinitionsToAnthropic(request.tools),
2221
+ max_tokens: maxTokens,
2222
+ temperature: request.temperature,
2223
+ top_p: request.topP,
2224
+ thinking: thinkingConfig.thinking
2225
+ }),
2226
+ signal: request.signal
2227
+ });
2228
+ } catch (error) {
2229
+ yield { type: "error", requestId, error: toModelErrorPayload(error), terminal: true };
2230
+ return;
2231
+ }
2232
+ if (!response.ok) {
2233
+ yield {
2234
+ type: "error",
2235
+ requestId,
2236
+ error: await createHttpErrorPayload({
2237
+ code: "anthropic_http_error",
2238
+ provider: "Anthropic",
2239
+ endpoint: `${baseUrl}/v1/messages`,
2240
+ response
2241
+ }),
2242
+ terminal: true
2243
+ };
2244
+ return;
2245
+ }
2246
+ const data = await response.json();
2247
+ for (const delta of extractAnthropicThinking(data.content))
2248
+ yield { type: "thinking_delta", requestId, delta };
2249
+ for (const block of data.content ?? []) {
2250
+ if (block.type === "text" && block.text)
2251
+ yield { type: "text_delta", requestId, delta: block.text };
2252
+ }
2253
+ const toolCalls = normalizeAnthropicToolCalls(data.content);
2254
+ for (const toolCall of toolCalls) {
2255
+ yield { type: "tool_call_start", requestId, callId: toolCall.id, toolName: toolCall.name };
2256
+ yield { type: "tool_call_args_delta", requestId, callId: toolCall.id, delta: toolCall.argsText };
2257
+ yield { type: "tool_call_end", requestId, callId: toolCall.id };
2258
+ }
2259
+ yield {
2260
+ type: "response_end",
2261
+ requestId,
2262
+ stopReason: mapAnthropicStopReason(data.stop_reason, toolCalls.length),
2263
+ usage: normalizeUsage(data.usage),
2264
+ assistantMetadata: attachProviderState(undefined, "anthropic", createAnthropicThinkingState(data.content))
2265
+ };
2266
+ }
2267
+ };
2268
+ }
2269
+ function toModelErrorPayload(error) {
2270
+ if (error instanceof Error) {
2271
+ return {
2272
+ code: "anthropic_request_error",
2273
+ message: error.message
2274
+ };
2275
+ }
2276
+ return {
2277
+ code: "anthropic_request_error",
2278
+ message: String(error)
2279
+ };
2280
+ }
2281
+ // src/providers/gemini/mapper.ts
2282
+ var GEMINI_STATE_KEY = "gemini";
2283
+ function mapToolDefinitionsToGemini(tools) {
2284
+ if (!tools || tools.length === 0)
2285
+ return {};
2286
+ return {
2287
+ tools: [{
2288
+ functionDeclarations: tools.map((tool) => ({
2289
+ name: tool.name,
2290
+ description: tool.description,
2291
+ parametersJsonSchema: tool.inputSchema
2292
+ }))
2293
+ }],
2294
+ toolConfig: {
2295
+ functionCallingConfig: {
2296
+ mode: "AUTO"
2297
+ }
2298
+ }
2299
+ };
2300
+ }
2301
+ function mapGeminiStopReason(reason, toolCallCount) {
2302
+ if (toolCallCount > 0 || reason === "STOPFUNCTIONCALL" || reason === "function_call")
2303
+ return "tool_call";
2304
+ if (reason === "MAX_TOKENS")
2305
+ return "length";
2306
+ if (reason === "CANCELLED")
2307
+ return "cancelled";
2308
+ if (reason === "SAFETY" || reason === "RECITATION" || reason === "ERROR")
2309
+ return "error";
2310
+ return "final";
2311
+ }
2312
+ function messagesToGemini(messages) {
2313
+ const systemChunks = [];
2314
+ const contents = [];
2315
+ for (const message of messages) {
2316
+ if (message.role === "system") {
2317
+ const text = contentToText(message.content);
2318
+ if (text)
2319
+ systemChunks.push(text);
2320
+ continue;
2321
+ }
2322
+ if (message.role === "tool") {
2323
+ contents.push({
2324
+ role: "user",
2325
+ parts: [{
2326
+ functionResponse: {
2327
+ name: message.toolName,
2328
+ response: message.structuredContent ?? {
2329
+ content: contentToText(message.content),
2330
+ isError: message.isError === true
2331
+ }
2332
+ }
2333
+ }]
2334
+ });
2335
+ continue;
2336
+ }
2337
+ const parts = [];
2338
+ if (message.role === "assistant")
2339
+ parts.push(...restoreGeminiThoughtParts(message));
2340
+ for (const block of message.content) {
2341
+ if (block.type === "text")
2342
+ parts.push({ text: block.text });
2343
+ else
2344
+ parts.push({ inlineData: { mimeType: block.mimeType, data: block.data } });
2345
+ }
2346
+ if (message.role === "assistant") {
2347
+ for (const toolCall of message.toolCalls ?? []) {
2348
+ parts.push({
2349
+ functionCall: {
2350
+ name: toolCall.function.name,
2351
+ args: toolCall.function.arguments
2352
+ }
2353
+ });
2354
+ }
2355
+ }
2356
+ contents.push({
2357
+ role: message.role === "assistant" ? "model" : "user",
2358
+ parts
2359
+ });
2360
+ }
2361
+ return {
2362
+ contents,
2363
+ systemInstruction: systemChunks.length > 0 ? { parts: systemChunks.map((text) => ({ text })) } : undefined
2364
+ };
2365
+ }
2366
+ function normalizeGeminiToolCalls(value) {
2367
+ if (!value)
2368
+ return [];
2369
+ const calls = [];
2370
+ for (const part of value) {
2371
+ if (!("functionCall" in part) || typeof part.functionCall?.name !== "string")
2372
+ continue;
2373
+ calls.push({
2374
+ id: crypto.randomUUID(),
2375
+ name: String(part.functionCall.name),
2376
+ argsText: JSON.stringify(part.functionCall.args ?? {})
2377
+ });
2378
+ }
2379
+ return calls;
2380
+ }
2381
+ function extractGeminiThinking(value) {
2382
+ if (!value)
2383
+ return [];
2384
+ const deltas = [];
2385
+ for (const part of value) {
2386
+ if ("text" in part && part.thought === true && typeof part.text === "string" && part.text.length > 0)
2387
+ deltas.push(part.text);
2388
+ }
2389
+ return deltas;
2390
+ }
2391
+ function createGeminiThoughtState(value) {
2392
+ if (!value)
2393
+ return;
2394
+ const parts = value.filter((part) => part.thought === true || typeof part.thoughtSignature === "string").map((part) => {
2395
+ if ("functionCall" in part) {
2396
+ return {
2397
+ functionCall: part.functionCall,
2398
+ thought: part.thought === true,
2399
+ thoughtSignature: part.thoughtSignature
2400
+ };
2401
+ }
2402
+ return {
2403
+ text: "text" in part ? part.text : undefined,
2404
+ thought: part.thought === true,
2405
+ thoughtSignature: part.thoughtSignature
2406
+ };
2407
+ });
2408
+ if (parts.length === 0)
2409
+ return;
2410
+ return { parts };
2411
+ }
2412
+ function restoreGeminiThoughtParts(message) {
2413
+ const state = readProviderState(message, GEMINI_STATE_KEY);
2414
+ const rawParts = state?.parts;
2415
+ if (!Array.isArray(rawParts))
2416
+ return [];
2417
+ const parts = [];
2418
+ for (const rawPart of rawParts) {
2419
+ if (!isRecord5(rawPart))
2420
+ continue;
2421
+ const thought = rawPart.thought === true;
2422
+ const thoughtSignature = typeof rawPart.thoughtSignature === "string" ? rawPart.thoughtSignature : undefined;
2423
+ if (isRecord5(rawPart.functionCall) && typeof rawPart.functionCall.name === "string") {
2424
+ parts.push({
2425
+ functionCall: {
2426
+ name: rawPart.functionCall.name,
2427
+ args: isRecord5(rawPart.functionCall.args) ? rawPart.functionCall.args : {}
2428
+ },
2429
+ thought,
2430
+ thoughtSignature
2431
+ });
2432
+ continue;
2433
+ }
2434
+ if (typeof rawPart.text === "string" || thoughtSignature) {
2435
+ parts.push({
2436
+ text: typeof rawPart.text === "string" ? rawPart.text : undefined,
2437
+ thought,
2438
+ thoughtSignature
2439
+ });
2440
+ }
2441
+ }
2442
+ return parts;
2443
+ }
2444
+ function isRecord5(value) {
2445
+ return Boolean(value) && typeof value === "object" && !Array.isArray(value);
2446
+ }
2447
+
2448
+ // src/providers/gemini/adapter.ts
2449
+ function createGeminiAdapter(options) {
2450
+ const fetchImpl = options.fetch ?? globalThis.fetch;
2451
+ const provider = options.provider ?? "gemini";
2452
+ const defaultModel = { provider, modelId: options.defaultModel };
2453
+ const baseUrl = (options.baseUrl ?? "https://generativelanguage.googleapis.com").replace(/\/$/, "");
2454
+ return {
2455
+ provider,
2456
+ defaultModel,
2457
+ async* stream(request) {
2458
+ const requestId = request.requestId ?? crypto.randomUUID();
2459
+ yield { type: "response_start", requestId, model: request.model };
2460
+ const { contents, systemInstruction } = messagesToGemini(request.messages);
2461
+ const { tools, toolConfig } = mapToolDefinitionsToGemini(request.tools);
2462
+ const thinkingConfig = buildGeminiThinkingConfig(request.reasoning);
2463
+ const searchParams = new URLSearchParams;
2464
+ if (options.apiKey)
2465
+ searchParams.set("key", options.apiKey);
2466
+ const url = `${baseUrl}/v1beta/models/${encodeURIComponent(request.model.modelId)}:generateContent${searchParams.toString() ? `?${searchParams}` : ""}`;
2467
+ let response;
2468
+ try {
2469
+ response = await fetchImpl(url, {
2470
+ method: "POST",
2471
+ headers: {
2472
+ "content-type": "application/json",
2473
+ ...options.headers
2474
+ },
2475
+ body: JSON.stringify({
2476
+ contents,
2477
+ systemInstruction,
2478
+ tools,
2479
+ toolConfig,
2480
+ generationConfig: {
2481
+ temperature: request.temperature,
2482
+ topP: request.topP,
2483
+ maxOutputTokens: request.maxOutputTokens,
2484
+ stopSequences: request.stop,
2485
+ thinkingConfig
2486
+ }
2487
+ }),
2488
+ signal: request.signal
2489
+ });
2490
+ } catch (error) {
2491
+ yield { type: "error", requestId, error: toModelErrorPayload2(error), terminal: true };
2492
+ return;
2493
+ }
2494
+ if (!response.ok) {
2495
+ yield {
2496
+ type: "error",
2497
+ requestId,
2498
+ error: await createHttpErrorPayload({
2499
+ code: "gemini_http_error",
2500
+ provider: "Gemini",
2501
+ endpoint: url,
2502
+ response
2503
+ }),
2504
+ terminal: true
2505
+ };
2506
+ return;
2507
+ }
2508
+ const data = await response.json();
2509
+ const candidate = data.candidates?.[0];
2510
+ for (const delta of extractGeminiThinking(candidate?.content?.parts))
2511
+ yield { type: "thinking_delta", requestId, delta };
2512
+ for (const part of candidate?.content?.parts ?? []) {
2513
+ if ("text" in part && part.thought !== true && typeof part.text === "string" && part.text.length > 0)
2514
+ yield { type: "text_delta", requestId, delta: part.text };
2515
+ }
2516
+ const toolCalls = normalizeGeminiToolCalls(candidate?.content?.parts);
2517
+ for (const toolCall of toolCalls) {
2518
+ yield { type: "tool_call_start", requestId, callId: toolCall.id, toolName: toolCall.name };
2519
+ yield { type: "tool_call_args_delta", requestId, callId: toolCall.id, delta: toolCall.argsText };
2520
+ yield { type: "tool_call_end", requestId, callId: toolCall.id };
2521
+ }
2522
+ yield {
2523
+ type: "response_end",
2524
+ requestId,
2525
+ stopReason: mapGeminiStopReason(candidate?.finishReason, toolCalls.length),
2526
+ usage: normalizeUsage(data.usageMetadata),
2527
+ assistantMetadata: attachProviderState(undefined, "gemini", createGeminiThoughtState(candidate?.content?.parts))
2528
+ };
2529
+ }
2530
+ };
2531
+ }
2532
+ function toModelErrorPayload2(error) {
2533
+ if (error instanceof Error) {
2534
+ return {
2535
+ code: "gemini_request_error",
2536
+ message: error.message
2537
+ };
2538
+ }
2539
+ return {
2540
+ code: "gemini_request_error",
2541
+ message: String(error)
2542
+ };
2543
+ }
2544
+ // src/providers/openai/mapper.ts
2545
+ function mapToolDefinitionsToOpenAI(tools) {
2546
+ if (!tools || tools.length === 0)
2547
+ return;
2548
+ return tools.map((tool) => ({
2549
+ type: "function",
2550
+ function: {
2551
+ name: tool.name,
2552
+ description: tool.description,
2553
+ parameters: tool.inputSchema
2554
+ }
2555
+ }));
2556
+ }
2557
+ function mapOpenAIStopReason(reason, toolCallCount) {
2558
+ if (toolCallCount > 0 || reason === "tool_calls")
2559
+ return "tool_call";
2560
+ if (reason === "length")
2561
+ return "length";
2562
+ if (reason === "content_filter" || reason === "error")
2563
+ return "error";
2564
+ if (reason === "cancelled")
2565
+ return "cancelled";
2566
+ return "final";
2567
+ }
2568
+ function messageContentToOpenAI(content) {
2569
+ const hasImage = content.some((block) => block.type === "image");
2570
+ if (!hasImage)
2571
+ return contentToText(content);
2572
+ return content.map((block) => mapContentBlockToOpenAIPart(block));
2573
+ }
2574
+ function mapContentBlockToOpenAIPart(block) {
2575
+ if (block.type === "text")
2576
+ return { type: "text", text: block.text };
2577
+ return {
2578
+ type: "image_url",
2579
+ image_url: {
2580
+ url: imageDataUrl(block)
2581
+ }
2582
+ };
2583
+ }
2584
+ function imageDataUrl(block) {
2585
+ return `data:${block.mimeType};base64,${block.data}`;
2586
+ }
2587
+ function messagesToOpenAI(messages) {
2588
+ return messages.map((message) => {
2589
+ if (message.role === "tool") {
2590
+ return {
2591
+ role: "tool",
2592
+ tool_call_id: message.toolCallId,
2593
+ content: message.content.length > 0 ? contentToText(message.content) : JSON.stringify(message.structuredContent ?? {})
2594
+ };
2595
+ }
2596
+ if (message.role === "assistant") {
2597
+ return {
2598
+ role: "assistant",
2599
+ content: contentToText(message.content),
2600
+ tool_calls: message.toolCalls?.map((toolCall) => ({
2601
+ id: toolCall.id,
2602
+ type: "function",
2603
+ function: {
2604
+ name: toolCall.function.name,
2605
+ arguments: JSON.stringify(toolCall.function.arguments)
2606
+ }
2607
+ }))
2608
+ };
2609
+ }
2610
+ return {
2611
+ role: message.role,
2612
+ content: messageContentToOpenAI(message.content),
2613
+ ...message.role === "user" && message.name ? { name: message.name } : {}
2614
+ };
2615
+ });
2616
+ }
2617
+ function normalizeOpenAIToolCalls(value) {
2618
+ if (!value)
2619
+ return [];
2620
+ const calls = [];
2621
+ for (const call of value) {
2622
+ if (typeof call?.id !== "string" || typeof call.function?.name !== "string")
2623
+ continue;
2624
+ calls.push({
2625
+ id: call.id,
2626
+ name: call.function.name,
2627
+ argsText: typeof call.function.arguments === "string" ? call.function.arguments : "{}"
2628
+ });
2629
+ }
2630
+ return calls;
2631
+ }
2632
+
2633
+ // src/providers/openai/adapter.ts
2634
+ function createOpenAIAdapter(options) {
2635
+ const fetchImpl = options.fetch ?? globalThis.fetch;
2636
+ const provider = options.provider ?? "openai-compatible";
2637
+ const defaultModel = { provider, modelId: options.defaultModel };
2638
+ const baseUrl = (options.baseUrl ?? "https://api.openai.com/v1").replace(/\/$/, "");
2639
+ return {
2640
+ provider,
2641
+ defaultModel,
2642
+ async* stream(request) {
2643
+ const requestId = request.requestId ?? crypto.randomUUID();
2644
+ yield { type: "response_start", requestId, model: request.model };
2645
+ let response;
2646
+ try {
2647
+ response = await fetchImpl(`${baseUrl}/chat/completions`, {
2648
+ method: "POST",
2649
+ headers: {
2650
+ "content-type": "application/json",
2651
+ ...options.apiKey ? { authorization: `Bearer ${options.apiKey}` } : {},
2652
+ ...options.headers
2653
+ },
2654
+ body: JSON.stringify({
2655
+ model: request.model.modelId,
2656
+ messages: messagesToOpenAI(request.messages),
2657
+ tools: mapToolDefinitionsToOpenAI(request.tools),
2658
+ max_tokens: request.maxOutputTokens,
2659
+ temperature: request.temperature,
2660
+ top_p: request.topP,
2661
+ stop: request.stop,
2662
+ stream: false
2663
+ }),
2664
+ signal: request.signal
2665
+ });
2666
+ } catch (error) {
2667
+ yield { type: "error", requestId, error: toModelErrorPayload3(error), terminal: true };
2668
+ return;
2669
+ }
2670
+ if (!response.ok) {
2671
+ yield {
2672
+ type: "error",
2673
+ requestId,
2674
+ error: await createHttpErrorPayload({
2675
+ code: "openai_http_error",
2676
+ provider: "OpenAI-compatible",
2677
+ endpoint: `${baseUrl}/chat/completions`,
2678
+ response
2679
+ }),
2680
+ terminal: true
2681
+ };
2682
+ return;
2683
+ }
2684
+ const data = await response.json();
2685
+ const choice = data.choices?.[0];
2686
+ const text = choice?.message?.content ?? "";
2687
+ if (text)
2688
+ yield { type: "text_delta", requestId, delta: text };
2689
+ const toolCalls = normalizeOpenAIToolCalls(choice?.message?.tool_calls);
2690
+ for (const toolCall of toolCalls) {
2691
+ yield { type: "tool_call_start", requestId, callId: toolCall.id, toolName: toolCall.name };
2692
+ yield { type: "tool_call_args_delta", requestId, callId: toolCall.id, delta: toolCall.argsText };
2693
+ yield { type: "tool_call_end", requestId, callId: toolCall.id };
2694
+ }
2695
+ yield {
2696
+ type: "response_end",
2697
+ requestId,
2698
+ stopReason: mapOpenAIStopReason(choice?.finish_reason, toolCalls.length),
2699
+ usage: normalizeUsage(data.usage)
2700
+ };
2701
+ }
2702
+ };
2703
+ }
2704
+ function toModelErrorPayload3(error) {
2705
+ if (error instanceof Error) {
2706
+ return {
2707
+ code: "openai_request_error",
2708
+ message: error.message
2709
+ };
2710
+ }
2711
+ return {
2712
+ code: "openai_request_error",
2713
+ message: String(error)
2714
+ };
2715
+ }
2716
+ // src/providers/openai-responses/mapper.ts
2717
+ var OPENAI_RESPONSES_STATE_KEY = "openaiResponses";
2718
+ function mapToolDefinitionsToOpenAIResponses(tools) {
2719
+ if (!tools || tools.length === 0)
2720
+ return;
2721
+ return tools.map((tool) => ({
2722
+ type: "function",
2723
+ name: tool.name,
2724
+ description: tool.description,
2725
+ parameters: tool.inputSchema
2726
+ }));
2727
+ }
2728
+ function mapOpenAIResponsesStopReason(response, toolCallCount) {
2729
+ if (toolCallCount > 0)
2730
+ return "tool_call";
2731
+ if (response?.status === "cancelled")
2732
+ return "cancelled";
2733
+ if (response?.status === "failed")
2734
+ return "error";
2735
+ if (response?.incomplete_details?.reason === "max_output_tokens")
2736
+ return "length";
2737
+ return "final";
2738
+ }
2739
+ function messagesToOpenAIResponses(messages) {
2740
+ const instructions = messages.filter((message) => message.role === "system").map((message) => contentToText(message.content)).filter(Boolean).join(`
2741
+
2742
+ `) || undefined;
2743
+ let previousResponseId;
2744
+ let startIndex = 0;
2745
+ for (let index = messages.length - 1;index >= 0; index -= 1) {
2746
+ const state = readProviderState(messages[index], OPENAI_RESPONSES_STATE_KEY);
2747
+ if (typeof state?.responseId === "string") {
2748
+ previousResponseId = state.responseId;
2749
+ startIndex = index + 1;
2750
+ break;
2751
+ }
2752
+ }
2753
+ const input = [];
2754
+ for (const message of messages.slice(startIndex)) {
2755
+ if (message.role === "system")
2756
+ continue;
2757
+ if (message.role === "tool") {
2758
+ input.push({
2759
+ type: "function_call_output",
2760
+ call_id: message.toolCallId,
2761
+ output: JSON.stringify(message.structuredContent ?? {
2762
+ content: contentToText(message.content),
2763
+ isError: message.isError === true
2764
+ })
2765
+ });
2766
+ continue;
2767
+ }
2768
+ if (message.role === "assistant") {
2769
+ if (message.content.length > 0) {
2770
+ input.push({
2771
+ role: "assistant",
2772
+ content: messageContentToOpenAIResponses(message.content)
2773
+ });
2774
+ }
2775
+ for (const toolCall of message.toolCalls ?? []) {
2776
+ input.push({
2777
+ type: "function_call",
2778
+ call_id: toolCall.id,
2779
+ name: toolCall.function.name,
2780
+ arguments: JSON.stringify(toolCall.function.arguments)
2781
+ });
2782
+ }
2783
+ continue;
2784
+ }
2785
+ input.push({
2786
+ role: "user",
2787
+ content: messageContentToOpenAIResponses(message.content)
2788
+ });
2789
+ }
2790
+ return {
2791
+ instructions,
2792
+ input,
2793
+ previousResponseId
2794
+ };
2795
+ }
2796
+ function normalizeOpenAIResponsesToolCalls(output) {
2797
+ if (!output)
2798
+ return [];
2799
+ const calls = [];
2800
+ for (const item of output) {
2801
+ if (item.type !== "function_call" || typeof item.name !== "string")
2802
+ continue;
2803
+ const callId = typeof item.call_id === "string" && item.call_id.length > 0 ? item.call_id : typeof item.id === "string" && item.id.length > 0 ? item.id : crypto.randomUUID();
2804
+ calls.push({
2805
+ id: callId,
2806
+ name: item.name,
2807
+ argsText: typeof item.arguments === "string" ? item.arguments : JSON.stringify(item.arguments ?? {})
2808
+ });
2809
+ }
2810
+ return calls;
2811
+ }
2812
+ function extractOpenAIResponsesText(output) {
2813
+ if (!output)
2814
+ return [];
2815
+ const deltas = [];
2816
+ for (const item of output) {
2817
+ if (item.type !== "message")
2818
+ continue;
2819
+ for (const part of item.content ?? []) {
2820
+ if ((part.type === "output_text" || part.type === "text") && typeof part.text === "string" && part.text.length > 0)
2821
+ deltas.push(part.text);
2822
+ }
2823
+ }
2824
+ return deltas;
2825
+ }
2826
+ function extractOpenAIResponsesThinking(output) {
2827
+ if (!output)
2828
+ return [];
2829
+ const deltas = [];
2830
+ for (const item of output) {
2831
+ if (item.type !== "reasoning")
2832
+ continue;
2833
+ for (const part of item.summary ?? []) {
2834
+ if (typeof part.text === "string" && part.text.length > 0)
2835
+ deltas.push(part.text);
2836
+ }
2837
+ }
2838
+ return deltas;
2839
+ }
2840
+ function createOpenAIResponsesState(responseId) {
2841
+ if (!responseId)
2842
+ return;
2843
+ return { responseId };
2844
+ }
2845
+ function messageContentToOpenAIResponses(content) {
2846
+ const hasImage = content.some((block) => block.type === "image");
2847
+ if (!hasImage)
2848
+ return contentToText(content);
2849
+ return content.map((block) => {
2850
+ if (block.type === "text")
2851
+ return { type: "input_text", text: block.text };
2852
+ return {
2853
+ type: "input_image",
2854
+ image_url: imageDataUrl2(block)
2855
+ };
2856
+ });
2857
+ }
2858
+ function imageDataUrl2(block) {
2859
+ return `data:${block.mimeType};base64,${block.data}`;
2860
+ }
2861
+
2862
+ // src/providers/openai-responses/adapter.ts
2863
+ function createOpenAIResponsesAdapter(options) {
2864
+ const fetchImpl = options.fetch ?? globalThis.fetch;
2865
+ const provider = options.provider ?? "openai-responses";
2866
+ const defaultModel = { provider, modelId: options.defaultModel };
2867
+ const baseUrl = (options.baseUrl ?? "https://api.openai.com/v1").replace(/\/$/, "");
2868
+ return {
2869
+ provider,
2870
+ defaultModel,
2871
+ async* stream(request) {
2872
+ const requestId = request.requestId ?? crypto.randomUUID();
2873
+ yield { type: "response_start", requestId, model: request.model };
2874
+ const { instructions, input, previousResponseId } = messagesToOpenAIResponses(request.messages);
2875
+ let response;
2876
+ try {
2877
+ response = await fetchImpl(`${baseUrl}/responses`, {
2878
+ method: "POST",
2879
+ headers: {
2880
+ "content-type": "application/json",
2881
+ ...options.apiKey ? { authorization: `Bearer ${options.apiKey}` } : {},
2882
+ ...options.headers
2883
+ },
2884
+ body: JSON.stringify({
2885
+ model: request.model.modelId,
2886
+ instructions,
2887
+ input,
2888
+ previous_response_id: previousResponseId,
2889
+ tools: mapToolDefinitionsToOpenAIResponses(request.tools),
2890
+ max_output_tokens: request.maxOutputTokens,
2891
+ temperature: request.temperature,
2892
+ top_p: request.topP,
2893
+ reasoning: buildOpenAIResponsesReasoning(request.reasoning),
2894
+ stream: false
2895
+ }),
2896
+ signal: request.signal
2897
+ });
2898
+ } catch (error) {
2899
+ yield { type: "error", requestId, error: toModelErrorPayload4(error), terminal: true };
2900
+ return;
2901
+ }
2902
+ if (!response.ok) {
2903
+ yield {
2904
+ type: "error",
2905
+ requestId,
2906
+ error: await createHttpErrorPayload({
2907
+ code: "openai_responses_http_error",
2908
+ provider: "OpenAI Responses",
2909
+ endpoint: `${baseUrl}/responses`,
2910
+ response
2911
+ }),
2912
+ terminal: true
2913
+ };
2914
+ return;
2915
+ }
2916
+ const data = await response.json();
2917
+ for (const delta of extractOpenAIResponsesThinking(data.output))
2918
+ yield { type: "thinking_delta", requestId, delta };
2919
+ for (const delta of extractOpenAIResponsesText(data.output))
2920
+ yield { type: "text_delta", requestId, delta };
2921
+ const toolCalls = normalizeOpenAIResponsesToolCalls(data.output);
2922
+ for (const toolCall of toolCalls) {
2923
+ yield { type: "tool_call_start", requestId, callId: toolCall.id, toolName: toolCall.name };
2924
+ yield { type: "tool_call_args_delta", requestId, callId: toolCall.id, delta: toolCall.argsText };
2925
+ yield { type: "tool_call_end", requestId, callId: toolCall.id };
2926
+ }
2927
+ yield {
2928
+ type: "response_end",
2929
+ requestId,
2930
+ stopReason: mapOpenAIResponsesStopReason(data, toolCalls.length),
2931
+ usage: normalizeUsage(data.usage),
2932
+ assistantMetadata: attachProviderState(undefined, "openaiResponses", createOpenAIResponsesState(data.id))
2933
+ };
2934
+ }
2935
+ };
2936
+ }
2937
+ function toModelErrorPayload4(error) {
2938
+ if (error instanceof Error) {
2939
+ return {
2940
+ code: "openai_responses_request_error",
2941
+ message: error.message
2942
+ };
2943
+ }
2944
+ return {
2945
+ code: "openai_responses_request_error",
2946
+ message: String(error)
2947
+ };
2948
+ }
2949
+ // src/agent-core/createModel.ts
2950
+ function createModel(adapter) {
2951
+ return {
2952
+ adapter,
2953
+ model: adapter.defaultModel,
2954
+ stream(request) {
2955
+ return adapter.stream({
2956
+ ...request,
2957
+ model: request.model ?? adapter.defaultModel
2958
+ });
2959
+ }
2960
+ };
2961
+ }
2962
+ export {
2963
+ startToolCall,
2964
+ normalizeToolSchema,
2965
+ normalizeToolResult,
2966
+ normalizePermissions,
2967
+ normalizeContent,
2968
+ fullPermissions,
2969
+ finalizeToolCall,
2970
+ createOpenAIResponsesAdapter,
2971
+ createOpenAIAdapter,
2972
+ createModel,
2973
+ createGeminiAdapter,
2974
+ createAnthropicAdapter,
2975
+ createAgent,
2976
+ appendToolCallArgsDelta,
2977
+ WriteTool,
2978
+ ToolRegistry,
2979
+ SessionExecutionError,
2980
+ Session,
2981
+ ReadTool,
2982
+ PermissionGateway,
2983
+ PermissionDeniedError,
2984
+ NodeFileSystemGateway,
2985
+ NodeExecGateway,
2986
+ InMemoryStateStore,
2987
+ FileStateStore,
2988
+ ExecTool,
2989
+ EditTool,
2990
+ DefaultNetworkGateway,
2991
+ DefaultModelGateway,
2992
+ DefaultGitGateway,
2993
+ BaseTool,
2994
+ AutoContextManager,
2995
+ Agent,
2996
+ ActivityTracker
2997
+ };