avantgate 1.1.0 → 1.1.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,1700 @@
1
+ import {
2
+ sanitizePII
3
+ } from "../chunk-DVCF4CSV.mjs";
4
+
5
+ // src/agent/errors.ts
6
+ var StepSuspendedError = class extends Error {
7
+ stepId;
8
+ workflowId;
9
+ metadata;
10
+ constructor(stepId, workflowId, metadata) {
11
+ super(`Step execution suspended for human approval: [${workflowId}::${stepId}]`);
12
+ this.name = "StepSuspendedError";
13
+ this.stepId = stepId;
14
+ this.workflowId = workflowId;
15
+ this.metadata = metadata;
16
+ }
17
+ };
18
+ var PiiLeakError = class extends Error {
19
+ toolName;
20
+ maskedCount;
21
+ constructor(toolName, maskedCount) {
22
+ super(`PII leak detected in tool output for "${toolName}" (${maskedCount} occurrences detected).`);
23
+ this.name = "PiiLeakError";
24
+ this.toolName = toolName;
25
+ this.maskedCount = maskedCount;
26
+ }
27
+ };
28
+ var StepExecutionError = class extends Error {
29
+ stepId;
30
+ workflowId;
31
+ originalError;
32
+ constructor(stepId, workflowId, message, originalError) {
33
+ super(`Execution failed at step [${workflowId}::${stepId}]: ${message}`);
34
+ this.name = "StepExecutionError";
35
+ this.stepId = stepId;
36
+ this.workflowId = workflowId;
37
+ this.originalError = originalError;
38
+ }
39
+ };
40
+ var ToolAccessDeniedError = class extends Error {
41
+ toolName;
42
+ requiredRole;
43
+ constructor(toolName, requiredRole) {
44
+ const roleMsg = requiredRole ? ` (requires role "${requiredRole}")` : "";
45
+ super(`Access denied for tool "${toolName}"${roleMsg}.`);
46
+ this.name = "ToolAccessDeniedError";
47
+ this.toolName = toolName;
48
+ this.requiredRole = requiredRole;
49
+ }
50
+ };
51
+ var CircularToolCallError = class extends Error {
52
+ cycle;
53
+ constructor(cycle) {
54
+ super(`Circular tool call detected: ${cycle.join(" -> ")}`);
55
+ this.name = "CircularToolCallError";
56
+ this.cycle = cycle;
57
+ }
58
+ };
59
+ var ToolCallDepthExceededError = class extends Error {
60
+ depth;
61
+ maxDepth;
62
+ constructor(depth, maxDepth) {
63
+ super(`Tool call depth limit exceeded: depth ${depth} exceeds max allowed depth of ${maxDepth}.`);
64
+ this.name = "ToolCallDepthExceededError";
65
+ this.depth = depth;
66
+ this.maxDepth = maxDepth;
67
+ }
68
+ };
69
+ var ToolSubCallQuotaError = class extends Error {
70
+ totalCalls;
71
+ maxCalls;
72
+ constructor(totalCalls, maxCalls) {
73
+ super(`Tool sub-call quota exceeded: ${totalCalls} calls exceeds session quota of ${maxCalls}.`);
74
+ this.name = "ToolSubCallQuotaError";
75
+ this.totalCalls = totalCalls;
76
+ this.maxCalls = maxCalls;
77
+ }
78
+ };
79
+ var ToolNotFoundError = class extends Error {
80
+ toolId;
81
+ constructor(toolId) {
82
+ super(`Tool not found in registry: "${toolId}".`);
83
+ this.name = "ToolNotFoundError";
84
+ this.toolId = toolId;
85
+ }
86
+ };
87
+ var DtoValidationError = class extends Error {
88
+ toolName;
89
+ issues;
90
+ constructor(toolName, message, issues = []) {
91
+ super(`LLM DTO validation failed for tool "${toolName}": ${message}`);
92
+ this.name = "DtoValidationError";
93
+ this.toolName = toolName;
94
+ this.issues = issues;
95
+ }
96
+ };
97
+
98
+ // src/agent/guardrails.ts
99
+ function sanitizeString(value) {
100
+ const result = sanitizePII(value);
101
+ return { text: result.text, count: result.maskedCount };
102
+ }
103
+ function sanitizeArray(items, options, visited) {
104
+ let totalCount = 0;
105
+ const sanitized = items.map((item) => {
106
+ const res = recursivelySanitize(item, options, visited);
107
+ totalCount += res.count;
108
+ return res.sanitized;
109
+ });
110
+ return { sanitized, count: totalCount };
111
+ }
112
+ function sanitizeObject(target, options, visited) {
113
+ let totalCount = 0;
114
+ const copy = {};
115
+ for (const [key, val] of Object.entries(target)) {
116
+ const res = recursivelySanitize(val, options, visited);
117
+ copy[key] = res.sanitized;
118
+ totalCount += res.count;
119
+ }
120
+ return { sanitized: copy, count: totalCount };
121
+ }
122
+ function recursivelySanitize(data, options, visited = /* @__PURE__ */ new WeakSet()) {
123
+ if (typeof data === "string") {
124
+ const res = sanitizeString(data);
125
+ return { sanitized: res.text, count: res.count };
126
+ }
127
+ if (data !== null && typeof data === "object") {
128
+ if (visited.has(data)) {
129
+ return { sanitized: "[CIRCULAR_REFERENCE]", count: 0 };
130
+ }
131
+ visited.add(data);
132
+ if (Array.isArray(data)) {
133
+ return sanitizeArray(data, options, visited);
134
+ }
135
+ return sanitizeObject(data, options, visited);
136
+ }
137
+ return { sanitized: data, count: 0 };
138
+ }
139
+ function auditToolResult(data, options) {
140
+ const { sanitized, count } = recursivelySanitize(data, options);
141
+ if (options.throwOnPii && count > 0) {
142
+ throw new PiiLeakError(options.toolName, count);
143
+ }
144
+ return {
145
+ sanitizedData: sanitized,
146
+ maskedCount: count
147
+ };
148
+ }
149
+
150
+ // src/agent/isolated-tool.ts
151
+ async function dispatchClientData(rawResult, callback) {
152
+ if (!callback) {
153
+ return;
154
+ }
155
+ await callback(rawResult);
156
+ }
157
+ async function produceLlmPayload(rawResult, args, context, transformer) {
158
+ if (transformer) {
159
+ return await transformer(rawResult, args, context);
160
+ }
161
+ return rawResult;
162
+ }
163
+ function validateLlmDto(payload, schema, toolIdentifier) {
164
+ const parseResult = schema.safeParse(payload);
165
+ if (!parseResult.success) {
166
+ const issues = parseResult.error.issues ?? [];
167
+ const errorMessages = issues.map((issue) => `${issue.path.join(".") || "root"}: ${issue.message}`).join(", ");
168
+ throw new DtoValidationError(
169
+ toolIdentifier,
170
+ errorMessages,
171
+ issues
172
+ );
173
+ }
174
+ return parseResult.data;
175
+ }
176
+ function protectLlmPayload(payload, toolIdentifier, sanitizePii = true, throwOnPii = false) {
177
+ if (!sanitizePii) {
178
+ return { sanitized: payload, count: 0 };
179
+ }
180
+ const { sanitizedData, maskedCount } = auditToolResult(payload, {
181
+ toolName: toolIdentifier,
182
+ throwOnPii
183
+ });
184
+ return { sanitized: sanitizedData, count: maskedCount };
185
+ }
186
+ async function processLlmPayload(rawResult, args, context, config, toolIdentifier) {
187
+ let payload = await produceLlmPayload(rawResult, args, context, config.llmDto);
188
+ if (config.llmDtoSchema) {
189
+ payload = validateLlmDto(payload, config.llmDtoSchema, toolIdentifier);
190
+ }
191
+ return protectLlmPayload(
192
+ payload,
193
+ toolIdentifier,
194
+ config.sanitizePii !== false,
195
+ config.throwOnPii === true
196
+ );
197
+ }
198
+ function createIsolatedTool(config) {
199
+ const toolId = config.id || config.name;
200
+ const toolAlias = config.alias;
201
+ const clientCallback = config.clientDto;
202
+ const tool = {
203
+ description: config.description,
204
+ parameters: config.parameters,
205
+ _toolId: toolId,
206
+ _toolName: config.name,
207
+ _toolAlias: toolAlias,
208
+ _isIsolated: true,
209
+ _cacheTTL: config.cacheTTL,
210
+ _lastPiiFilteredCount: 0,
211
+ async execute(args, context) {
212
+ const updatedContext = {
213
+ ...context,
214
+ callChain: context?.callChain ?? Object.freeze([toolId])
215
+ };
216
+ const rawResult = await config.execute(args, updatedContext);
217
+ await dispatchClientData(rawResult, clientCallback);
218
+ const protection = await processLlmPayload(
219
+ rawResult,
220
+ args,
221
+ updatedContext,
222
+ config,
223
+ toolAlias || config.name
224
+ );
225
+ tool._lastPiiFilteredCount = protection.count;
226
+ return protection.sanitized;
227
+ }
228
+ };
229
+ return tool;
230
+ }
231
+
232
+ // src/agent/dto.ts
233
+ var dto = {
234
+ /**
235
+ * Generates a boolean acknowledgment for mutations ({ success: true | false }).
236
+ * If rawResult contains a boolean `success` property, it is preserved; otherwise defaults to true.
237
+ */
238
+ boolean: () => (data) => ({
239
+ success: typeof data?.success === "boolean" ? data.success : true
240
+ }),
241
+ /**
242
+ * Generates a boolean acknowledgment preserving an opaque technical identifier for tool chaining.
243
+ */
244
+ booleanWithId: (idKey = "id") => (data) => ({
245
+ success: typeof data?.success === "boolean" ? data.success : true,
246
+ [idKey]: data?.[idKey]
247
+ }),
248
+ /**
249
+ * Extracts a numeric count from a list or nested array property without exposing items to the LLM.
250
+ */
251
+ count: (arrayKey) => (data) => {
252
+ const list = arrayKey ? data?.[arrayKey] : data;
253
+ return {
254
+ success: true,
255
+ count: Array.isArray(list) ? list.length : 0
256
+ };
257
+ },
258
+ /**
259
+ * Filters raw output by extracting only a strict whitelist of allowed fields.
260
+ */
261
+ pick: (keys) => (data) => {
262
+ const result = {};
263
+ if (!data || typeof data !== "object") {
264
+ return result;
265
+ }
266
+ for (const key of keys) {
267
+ if (key in data) {
268
+ result[key] = data[key];
269
+ }
270
+ }
271
+ return result;
272
+ }
273
+ };
274
+
275
+ // src/agent/factory.ts
276
+ var AgentToolFactory = class _AgentToolFactory {
277
+ context;
278
+ constructor(defaultContext = {}) {
279
+ this.context = Object.freeze({ ...defaultContext });
280
+ }
281
+ /**
282
+ * Returns a new factory instance with merged context.
283
+ */
284
+ withContext(extraContext) {
285
+ return new _AgentToolFactory({
286
+ ...this.context,
287
+ ...extraContext
288
+ });
289
+ }
290
+ /**
291
+ * Returns current factory context.
292
+ */
293
+ getContext() {
294
+ return this.context;
295
+ }
296
+ /**
297
+ * Instantiates an isolated tool by injecting factory context into the builder function.
298
+ */
299
+ createTool(builder) {
300
+ const config = builder(this.context);
301
+ return createIsolatedTool(config);
302
+ }
303
+ };
304
+
305
+ // src/agent/registry.ts
306
+ var ToolRegistry = class _ToolRegistry {
307
+ toolsById = /* @__PURE__ */ new Map();
308
+ toolsByPublicName = /* @__PURE__ */ new Map();
309
+ /**
310
+ * Registers a new tool in the registry.
311
+ */
312
+ register(toolDef) {
313
+ const id = toolDef.id || toolDef.name;
314
+ const publicName = toolDef.alias || toolDef.name;
315
+ const normalizedDef = {
316
+ ...toolDef,
317
+ id
318
+ };
319
+ this.toolsById.set(id, normalizedDef);
320
+ this.toolsByPublicName.set(publicName, normalizedDef);
321
+ return this;
322
+ }
323
+ /**
324
+ * Retrieves a tool by its ID or public name/alias.
325
+ */
326
+ get(idOrName) {
327
+ return this.toolsById.get(idOrName) ?? this.toolsByPublicName.get(idOrName);
328
+ }
329
+ /**
330
+ * Retrieves a tool strictly by its immutable technical ID (O(1)).
331
+ */
332
+ getById(id) {
333
+ return this.toolsById.get(id);
334
+ }
335
+ /**
336
+ * Retrieves a tool by its public alias seen by the LLM (O(1)).
337
+ */
338
+ getByPublicName(publicName) {
339
+ return this.toolsByPublicName.get(publicName);
340
+ }
341
+ /**
342
+ * Checks if a tool is registered by ID or public name.
343
+ */
344
+ has(idOrName) {
345
+ return this.toolsById.has(idOrName) || this.toolsByPublicName.has(idOrName);
346
+ }
347
+ /**
348
+ * Returns all registered tools without duplicates.
349
+ */
350
+ getAll() {
351
+ return Array.from(this.toolsById.values());
352
+ }
353
+ /**
354
+ * Filters tools matching a specific workflow phase.
355
+ */
356
+ getByPhase(phase) {
357
+ return this.getAll().filter((item) => {
358
+ if (!item.phases || item.phases.length === 0) {
359
+ return true;
360
+ }
361
+ return item.phases.includes(phase);
362
+ });
363
+ }
364
+ /**
365
+ * Filters tools matching the user's roles (RBAC).
366
+ */
367
+ filterByRoles(userRoles) {
368
+ const roleSet = new Set(userRoles);
369
+ return this.getAll().filter((item) => {
370
+ if (!item.requiredRoles || item.requiredRoles.length === 0) {
371
+ return true;
372
+ }
373
+ return item.requiredRoles.some((role) => roleSet.has(role));
374
+ });
375
+ }
376
+ /**
377
+ * Converts instance registered tools to the record map format required by Vercel AI SDK.
378
+ */
379
+ toRecord(options = {}) {
380
+ return _ToolRegistry.toRecord(this.getAll(), options);
381
+ }
382
+ /**
383
+ * Converts a list of registered tools to the record map format required by Vercel AI SDK.
384
+ * If anonymize is true, uses alias (if defined) as dictionary key instead of technical name.
385
+ */
386
+ static toRecord(tools, options = {}) {
387
+ const record = {};
388
+ for (const item of tools) {
389
+ const key = options.anonymize && item.alias ? item.alias : item.name;
390
+ record[key] = item.tool;
391
+ }
392
+ return record;
393
+ }
394
+ };
395
+
396
+ // src/agent/strategy.ts
397
+ var PhaseBasedToolStrategy = class {
398
+ selectTools(tools, context) {
399
+ if (!context.phase) {
400
+ return tools;
401
+ }
402
+ const currentPhase = context.phase;
403
+ return tools.filter((item) => {
404
+ if (!item.phases || item.phases.length === 0) {
405
+ return true;
406
+ }
407
+ return item.phases.includes(currentPhase);
408
+ });
409
+ }
410
+ };
411
+ var RoleBasedToolStrategy = class {
412
+ selectTools(tools, context) {
413
+ if (!context.role) {
414
+ return tools.filter((t) => !t.requiredRoles || t.requiredRoles.length === 0);
415
+ }
416
+ const currentRole = context.role;
417
+ return tools.filter((item) => {
418
+ if (!item.requiredRoles || item.requiredRoles.length === 0) {
419
+ return true;
420
+ }
421
+ return item.requiredRoles.includes(currentRole);
422
+ });
423
+ }
424
+ };
425
+ var CompositeToolStrategy = class {
426
+ strategies;
427
+ constructor(strategies) {
428
+ this.strategies = strategies;
429
+ }
430
+ async selectTools(tools, context) {
431
+ let current = tools;
432
+ for (const strategy of this.strategies) {
433
+ current = await strategy.selectTools(current, context);
434
+ }
435
+ return current;
436
+ }
437
+ };
438
+ async function applyToolStrategy(tools, strategy, context) {
439
+ const selected = await strategy.selectTools(tools, context);
440
+ return ToolRegistry.toRecord(selected);
441
+ }
442
+
443
+ // src/agent/adapters/memory-adapter.ts
444
+ var MemoryStorageAdapter = class {
445
+ storage = /* @__PURE__ */ new Map();
446
+ toolExecutions = [];
447
+ cacheStorage = /* @__PURE__ */ new Map();
448
+ stateStorage = /* @__PURE__ */ new Map();
449
+ ttlMs;
450
+ constructor(options = {}) {
451
+ this.ttlMs = options.ttlMs;
452
+ }
453
+ buildKey(workflowId, stepId) {
454
+ return `${workflowId}::${stepId}`;
455
+ }
456
+ isExpired(entry) {
457
+ if (!entry.expiresAt) {
458
+ return false;
459
+ }
460
+ return Date.now() > entry.expiresAt;
461
+ }
462
+ async getStep(workflowId, stepId) {
463
+ const key = this.buildKey(workflowId, stepId);
464
+ const entry = this.storage.get(key);
465
+ if (!entry) {
466
+ return null;
467
+ }
468
+ if (this.isExpired(entry)) {
469
+ this.storage.delete(key);
470
+ return null;
471
+ }
472
+ return entry.record;
473
+ }
474
+ async saveStep(step) {
475
+ const key = this.buildKey(step.workflowId, step.stepId);
476
+ const expiresAt = this.ttlMs ? Date.now() + this.ttlMs : void 0;
477
+ this.storage.set(key, {
478
+ record: step,
479
+ expiresAt
480
+ });
481
+ }
482
+ async updateStepStatus(workflowId, stepId, status, patch) {
483
+ const existing = await this.getStep(workflowId, stepId);
484
+ const now = (/* @__PURE__ */ new Date()).toISOString();
485
+ const updated = {
486
+ ...existing ?? {
487
+ workflowId,
488
+ stepId,
489
+ createdAt: now
490
+ },
491
+ ...patch,
492
+ status,
493
+ updatedAt: now
494
+ };
495
+ await this.saveStep(updated);
496
+ }
497
+ async listSteps(workflowId) {
498
+ const results = [];
499
+ for (const [key, entry] of this.storage.entries()) {
500
+ if (this.isExpired(entry)) {
501
+ this.storage.delete(key);
502
+ continue;
503
+ }
504
+ if (entry.record.workflowId === workflowId) {
505
+ results.push(entry.record);
506
+ }
507
+ }
508
+ return results;
509
+ }
510
+ async saveToolExecution(record) {
511
+ this.toolExecutions.push(record);
512
+ }
513
+ async listToolExecutions(workflowId, stepId) {
514
+ return this.toolExecutions.filter((item) => {
515
+ if (workflowId && item.workflowId !== workflowId) return false;
516
+ if (stepId && item.stepId !== stepId) return false;
517
+ return true;
518
+ });
519
+ }
520
+ async getCachedToolResult(cacheKey) {
521
+ const entry = this.cacheStorage.get(cacheKey);
522
+ if (!entry) {
523
+ return null;
524
+ }
525
+ if (Date.now() > entry.expiresAt) {
526
+ this.cacheStorage.delete(cacheKey);
527
+ return null;
528
+ }
529
+ return entry.result;
530
+ }
531
+ async setCachedToolResult(cacheKey, result, ttlSeconds) {
532
+ const expiresAt = Date.now() + ttlSeconds * 1e3;
533
+ this.cacheStorage.set(cacheKey, { result, expiresAt });
534
+ }
535
+ async getStateValue(key) {
536
+ const entry = this.stateStorage.get(key);
537
+ if (!entry) {
538
+ return null;
539
+ }
540
+ if (entry.expiresAt && Date.now() > entry.expiresAt) {
541
+ this.stateStorage.delete(key);
542
+ return null;
543
+ }
544
+ return entry.value;
545
+ }
546
+ async setStateValue(key, value, ttlSeconds) {
547
+ const expiresAt = ttlSeconds ? Date.now() + ttlSeconds * 1e3 : void 0;
548
+ this.stateStorage.set(key, { value, expiresAt });
549
+ }
550
+ async deleteStateValue(key) {
551
+ this.stateStorage.delete(key);
552
+ }
553
+ /**
554
+ * Resets all internal in-memory maps (useful for test isolation).
555
+ */
556
+ clear() {
557
+ this.storage.clear();
558
+ this.toolExecutions.length = 0;
559
+ this.cacheStorage.clear();
560
+ this.stateStorage.clear();
561
+ }
562
+ };
563
+
564
+ // src/agent/step-runner.ts
565
+ async function fetchCachedResult(storage, workflowId, stepId) {
566
+ const existing = await storage.getStep(workflowId, stepId);
567
+ if (existing && existing.status === "COMPLETED") {
568
+ return { isCompleted: true, result: existing.result };
569
+ }
570
+ return { isCompleted: false };
571
+ }
572
+ async function markStepRunning(storage, workflowId, stepId, runId) {
573
+ const now = (/* @__PURE__ */ new Date()).toISOString();
574
+ await storage.saveStep({
575
+ workflowId,
576
+ stepId,
577
+ runId,
578
+ status: "RUNNING",
579
+ createdAt: now,
580
+ updatedAt: now
581
+ });
582
+ }
583
+ async function handleStepFailure(storage, workflowId, stepId, error) {
584
+ if (error instanceof StepSuspendedError) {
585
+ throw error;
586
+ }
587
+ const errorMsg = error instanceof Error ? error.message : String(error);
588
+ await storage.updateStepStatus(workflowId, stepId, "FAILED", {
589
+ error: errorMsg
590
+ });
591
+ throw new StepExecutionError(stepId, workflowId, errorMsg, error);
592
+ }
593
+ async function executeWithPersistence(storage, workflowId, stepId, runId, executeFn) {
594
+ const cache = await fetchCachedResult(storage, workflowId, stepId);
595
+ if (cache.isCompleted) {
596
+ return cache.result;
597
+ }
598
+ await markStepRunning(storage, workflowId, stepId, runId);
599
+ try {
600
+ const result = await executeFn();
601
+ await storage.updateStepStatus(workflowId, stepId, "COMPLETED", { result });
602
+ return result;
603
+ } catch (error) {
604
+ return handleStepFailure(storage, workflowId, stepId, error);
605
+ }
606
+ }
607
+ async function handleWaitForApproval(storage, workflowId, stepId, runId, options) {
608
+ const existing = await storage.getStep(workflowId, stepId);
609
+ if (existing && existing.status === "COMPLETED") {
610
+ return existing.result ?? options?.defaultResult;
611
+ }
612
+ const now = (/* @__PURE__ */ new Date()).toISOString();
613
+ const stepRecord = {
614
+ workflowId,
615
+ stepId,
616
+ runId,
617
+ status: "WAITING_APPROVAL",
618
+ metadata: options?.metadata,
619
+ result: options?.defaultResult,
620
+ createdAt: existing?.createdAt ?? now,
621
+ updatedAt: now
622
+ };
623
+ await storage.saveStep(stepRecord);
624
+ throw new StepSuspendedError(stepId, workflowId, options?.metadata);
625
+ }
626
+ function createStepRunner(config) {
627
+ const { workflowId, runId = config.runId ?? config.workflowId, storage = new MemoryStorageAdapter() } = config;
628
+ return {
629
+ workflowId,
630
+ storage,
631
+ async run(stepId, executeFn) {
632
+ return executeWithPersistence(storage, workflowId, stepId, runId, executeFn);
633
+ },
634
+ async waitForApproval(stepId, options) {
635
+ return handleWaitForApproval(storage, workflowId, stepId, runId, options);
636
+ },
637
+ async approveStep(stepId, approvalData) {
638
+ await storage.updateStepStatus(workflowId, stepId, "COMPLETED", {
639
+ result: approvalData
640
+ });
641
+ },
642
+ async rejectStep(stepId, reason) {
643
+ await storage.updateStepStatus(workflowId, stepId, "FAILED", {
644
+ error: reason ?? "Rejected by human operator"
645
+ });
646
+ }
647
+ };
648
+ }
649
+
650
+ // src/agent/tool-state.ts
651
+ function createToolSharedState(storage) {
652
+ const volatileMap = /* @__PURE__ */ new Map();
653
+ return {
654
+ async get(key) {
655
+ if (storage?.getStateValue) {
656
+ return storage.getStateValue(key);
657
+ }
658
+ const entry = volatileMap.get(key);
659
+ if (!entry) {
660
+ return null;
661
+ }
662
+ if (entry.expiresAt && Date.now() > entry.expiresAt) {
663
+ volatileMap.delete(key);
664
+ return null;
665
+ }
666
+ return entry.value;
667
+ },
668
+ async set(key, value, ttlSeconds) {
669
+ if (storage?.setStateValue) {
670
+ await storage.setStateValue(key, value, ttlSeconds);
671
+ return;
672
+ }
673
+ const expiresAt = ttlSeconds ? Date.now() + ttlSeconds * 1e3 : void 0;
674
+ volatileMap.set(key, { value, expiresAt });
675
+ },
676
+ async delete(key) {
677
+ if (storage?.deleteStateValue) {
678
+ await storage.deleteStateValue(key);
679
+ return;
680
+ }
681
+ volatileMap.delete(key);
682
+ },
683
+ async clear() {
684
+ if (storage?.clearStateValues) {
685
+ await storage.clearStateValues();
686
+ return;
687
+ }
688
+ volatileMap.clear();
689
+ }
690
+ };
691
+ }
692
+
693
+ // src/agent/tool-invoker.ts
694
+ function buildCacheKey(toolId, args) {
695
+ try {
696
+ return `avantgate:cache:${toolId}:${JSON.stringify(args)}`;
697
+ } catch {
698
+ return `avantgate:cache:${toolId}:${String(args)}`;
699
+ }
700
+ }
701
+ function validateCallLimits(registeredId, parentChain, maxDepth, totalCalls, maxTotalCalls) {
702
+ if (totalCalls > maxTotalCalls) {
703
+ throw new ToolSubCallQuotaError(totalCalls, maxTotalCalls);
704
+ }
705
+ if (parentChain.includes(registeredId)) {
706
+ throw new CircularToolCallError([...parentChain, registeredId]);
707
+ }
708
+ if (parentChain.length >= maxDepth) {
709
+ throw new ToolCallDepthExceededError(parentChain.length + 1, maxDepth);
710
+ }
711
+ }
712
+ async function recordExecution(storage, record) {
713
+ if (!storage?.saveToolExecution) {
714
+ return;
715
+ }
716
+ try {
717
+ await storage.saveToolExecution(record);
718
+ } catch {
719
+ }
720
+ }
721
+ function createToolInvoker(registry, storage, options = {}) {
722
+ const {
723
+ maxDepth = 5,
724
+ maxTotalSubCalls = 20,
725
+ workflowId = options.workflowId,
726
+ stepId = options.stepId
727
+ } = options;
728
+ let totalCallsCount = 0;
729
+ const sharedState = createToolSharedState(storage);
730
+ const invoker = {
731
+ async invokeTool(toolId, args, context) {
732
+ totalCallsCount++;
733
+ const registered = registry.get(toolId);
734
+ if (!registered) {
735
+ throw new ToolNotFoundError(toolId);
736
+ }
737
+ const parentChain = context?.callChain ?? [];
738
+ validateCallLimits(
739
+ registered.id,
740
+ parentChain,
741
+ maxDepth,
742
+ totalCallsCount,
743
+ maxTotalSubCalls
744
+ );
745
+ const cacheKey = buildCacheKey(registered.id, args);
746
+ const cacheTTL = registered.tool._cacheTTL;
747
+ if (cacheTTL && storage?.getCachedToolResult) {
748
+ const cached = await storage.getCachedToolResult(cacheKey);
749
+ if (cached !== null && cached !== void 0) {
750
+ return cached;
751
+ }
752
+ }
753
+ const activeChain = Object.freeze([...parentChain, registered.id]);
754
+ const childContext = {
755
+ ...context,
756
+ workflowId: context?.workflowId ?? workflowId,
757
+ stepId: context?.stepId ?? stepId,
758
+ callChain: activeChain,
759
+ state: sharedState,
760
+ storage,
761
+ callTool: (subId, subArgs) => invoker.invokeTool(subId, subArgs, childContext)
762
+ };
763
+ const startTime = Date.now();
764
+ const parentId = parentChain[parentChain.length - 1];
765
+ const runId = context?.runId ?? childContext.workflowId;
766
+ try {
767
+ const result = await registered.tool.execute(args, childContext);
768
+ const durationMs = Date.now() - startTime;
769
+ if (cacheTTL && storage?.setCachedToolResult) {
770
+ await storage.setCachedToolResult(cacheKey, result, cacheTTL);
771
+ }
772
+ const piiFilteredCount = registered.tool._lastPiiFilteredCount ?? 0;
773
+ await recordExecution(storage, {
774
+ executionId: `exec-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`,
775
+ workflowId: childContext.workflowId,
776
+ stepId: childContext.stepId,
777
+ runId,
778
+ toolId: registered.id,
779
+ parentToolId: parentId,
780
+ aliasUsed: registered.alias,
781
+ depth: activeChain.length,
782
+ inputArgs: args,
783
+ outputSummary: result,
784
+ durationMs,
785
+ status: "SUCCESS",
786
+ piiFilteredCount,
787
+ tokens: childContext.tokens,
788
+ costUsd: childContext.costUsd,
789
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
790
+ });
791
+ return result;
792
+ } catch (error) {
793
+ const durationMs = Date.now() - startTime;
794
+ const errorMsg = error instanceof Error ? error.message : String(error);
795
+ await recordExecution(storage, {
796
+ executionId: `exec-${Date.now()}-${Math.random().toString(36).slice(2, 7)}`,
797
+ workflowId: childContext.workflowId,
798
+ stepId: childContext.stepId,
799
+ runId,
800
+ toolId: registered.id,
801
+ parentToolId: parentId,
802
+ aliasUsed: registered.alias,
803
+ depth: activeChain.length,
804
+ inputArgs: args,
805
+ durationMs,
806
+ status: "FAILED",
807
+ error: errorMsg,
808
+ tokens: childContext.tokens,
809
+ costUsd: childContext.costUsd,
810
+ createdAt: (/* @__PURE__ */ new Date()).toISOString()
811
+ });
812
+ throw error;
813
+ }
814
+ }
815
+ };
816
+ return invoker;
817
+ }
818
+
819
+ // src/agent/adapters/custom-adapter.ts
820
+ function createCustomStorageAdapter(handlers) {
821
+ return {
822
+ getStep: handlers.getStep,
823
+ saveStep: handlers.saveStep,
824
+ updateStepStatus: handlers.updateStepStatus,
825
+ listSteps: handlers.listSteps,
826
+ saveToolExecution: handlers.saveToolExecution,
827
+ listToolExecutions: handlers.listToolExecutions,
828
+ getCachedToolResult: handlers.getCachedToolResult,
829
+ setCachedToolResult: handlers.setCachedToolResult,
830
+ getStateValue: handlers.getStateValue,
831
+ setStateValue: handlers.setStateValue,
832
+ deleteStateValue: handlers.deleteStateValue
833
+ };
834
+ }
835
+ var KeyValueStorageAdapter = class {
836
+ client;
837
+ prefix;
838
+ ttlSeconds;
839
+ constructor(client, options = {}) {
840
+ this.client = client;
841
+ this.prefix = options.prefix ?? "avantgate:step:";
842
+ this.ttlSeconds = options.ttlSeconds;
843
+ }
844
+ buildKey(workflowId, stepId) {
845
+ return `${this.prefix}${workflowId}:${stepId}`;
846
+ }
847
+ async getStep(workflowId, stepId) {
848
+ const raw = await this.client.get(this.buildKey(workflowId, stepId));
849
+ if (!raw) {
850
+ return null;
851
+ }
852
+ try {
853
+ return JSON.parse(raw);
854
+ } catch {
855
+ return null;
856
+ }
857
+ }
858
+ async saveStep(step) {
859
+ const key = this.buildKey(step.workflowId, step.stepId);
860
+ const serialized = JSON.stringify(step);
861
+ await this.client.set(key, serialized, this.ttlSeconds);
862
+ }
863
+ async updateStepStatus(workflowId, stepId, status, patch) {
864
+ const existing = await this.getStep(workflowId, stepId);
865
+ const now = (/* @__PURE__ */ new Date()).toISOString();
866
+ const updated = {
867
+ ...existing ?? {
868
+ workflowId,
869
+ stepId,
870
+ createdAt: now
871
+ },
872
+ ...patch,
873
+ status,
874
+ updatedAt: now
875
+ };
876
+ await this.saveStep(updated);
877
+ }
878
+ async listSteps(workflowId) {
879
+ if (!this.client.keys) {
880
+ return [];
881
+ }
882
+ const pattern = `${this.prefix}${workflowId}:*`;
883
+ const matchedKeys = await this.client.keys(pattern);
884
+ const records = [];
885
+ for (const key of matchedKeys) {
886
+ const raw = await this.client.get(key);
887
+ if (raw) {
888
+ try {
889
+ records.push(JSON.parse(raw));
890
+ } catch {
891
+ }
892
+ }
893
+ }
894
+ return records;
895
+ }
896
+ async getCachedToolResult(cacheKey) {
897
+ const raw = await this.client.get(`cache:${cacheKey}`);
898
+ if (!raw) return null;
899
+ try {
900
+ return JSON.parse(raw);
901
+ } catch {
902
+ return null;
903
+ }
904
+ }
905
+ async setCachedToolResult(cacheKey, result, ttlSeconds) {
906
+ await this.client.set(`cache:${cacheKey}`, JSON.stringify(result), ttlSeconds);
907
+ }
908
+ async getStateValue(key) {
909
+ const raw = await this.client.get(`state:${key}`);
910
+ if (!raw) return null;
911
+ try {
912
+ return JSON.parse(raw);
913
+ } catch {
914
+ return null;
915
+ }
916
+ }
917
+ async setStateValue(key, value, ttlSeconds) {
918
+ await this.client.set(`state:${key}`, JSON.stringify(value), ttlSeconds);
919
+ }
920
+ async deleteStateValue(key) {
921
+ if (this.client.del) {
922
+ await this.client.del(`state:${key}`);
923
+ return;
924
+ }
925
+ await this.client.set(`state:${key}`, "", 0);
926
+ }
927
+ };
928
+
929
+ // src/agent/adapters/prisma-adapter.ts
930
+ function toStepRecord(raw) {
931
+ const result = typeof raw.result === "string" ? tryParseJson(raw.result) : raw.result;
932
+ const metadata = typeof raw.metadata === "string" ? tryParseJson(raw.metadata) : raw.metadata;
933
+ return {
934
+ workflowId: raw.workflowId,
935
+ stepId: raw.stepId,
936
+ status: raw.status,
937
+ result,
938
+ error: raw.error ?? void 0,
939
+ metadata,
940
+ createdAt: raw.createdAt instanceof Date ? raw.createdAt.toISOString() : String(raw.createdAt),
941
+ updatedAt: raw.updatedAt instanceof Date ? raw.updatedAt.toISOString() : String(raw.updatedAt)
942
+ };
943
+ }
944
+ function tryParseJson(value) {
945
+ if (typeof value !== "string") {
946
+ return value;
947
+ }
948
+ try {
949
+ return JSON.parse(value);
950
+ } catch {
951
+ return value;
952
+ }
953
+ }
954
+ var PrismaStorageAdapter = class {
955
+ model;
956
+ toolDelegates;
957
+ volatileCache = /* @__PURE__ */ new Map();
958
+ volatileState = /* @__PURE__ */ new Map();
959
+ volatileExecutions = [];
960
+ constructor(model, toolDelegates) {
961
+ this.model = model;
962
+ this.toolDelegates = toolDelegates;
963
+ }
964
+ async getStep(workflowId, stepId) {
965
+ const raw = await this.model.findUnique({
966
+ where: {
967
+ workflowId_stepId: { workflowId, stepId }
968
+ }
969
+ });
970
+ if (!raw) {
971
+ return null;
972
+ }
973
+ return toStepRecord(raw);
974
+ }
975
+ async saveStep(step) {
976
+ const payload = {
977
+ workflowId: step.workflowId,
978
+ stepId: step.stepId,
979
+ status: step.status,
980
+ result: step.result !== void 0 ? JSON.stringify(step.result) : null,
981
+ error: step.error ?? null,
982
+ metadata: step.metadata ? JSON.stringify(step.metadata) : null,
983
+ updatedAt: new Date(step.updatedAt)
984
+ };
985
+ await this.model.upsert({
986
+ where: {
987
+ workflowId_stepId: {
988
+ workflowId: step.workflowId,
989
+ stepId: step.stepId
990
+ }
991
+ },
992
+ create: {
993
+ ...payload,
994
+ createdAt: new Date(step.createdAt)
995
+ },
996
+ update: payload
997
+ });
998
+ }
999
+ async updateStepStatus(workflowId, stepId, status, patch) {
1000
+ const existing = await this.getStep(workflowId, stepId);
1001
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1002
+ const updated = {
1003
+ ...existing ?? {
1004
+ workflowId,
1005
+ stepId,
1006
+ createdAt: now
1007
+ },
1008
+ ...patch,
1009
+ status,
1010
+ updatedAt: now
1011
+ };
1012
+ await this.saveStep(updated);
1013
+ }
1014
+ async listSteps(workflowId) {
1015
+ const list = await this.model.findMany({
1016
+ where: { workflowId },
1017
+ orderBy: { createdAt: "asc" }
1018
+ });
1019
+ return list.map(toStepRecord);
1020
+ }
1021
+ async saveToolExecution(record) {
1022
+ if (this.toolDelegates?.toolExecution) {
1023
+ await this.toolDelegates.toolExecution.create({
1024
+ data: {
1025
+ executionId: record.executionId,
1026
+ workflowId: record.workflowId,
1027
+ stepId: record.stepId,
1028
+ toolId: record.toolId,
1029
+ parentToolId: record.parentToolId,
1030
+ aliasUsed: record.aliasUsed,
1031
+ depth: record.depth,
1032
+ inputArgs: record.inputArgs ? JSON.stringify(record.inputArgs) : null,
1033
+ outputSummary: record.outputSummary ? JSON.stringify(record.outputSummary) : null,
1034
+ durationMs: record.durationMs,
1035
+ status: record.status,
1036
+ error: record.error,
1037
+ createdAt: new Date(record.createdAt)
1038
+ }
1039
+ });
1040
+ return;
1041
+ }
1042
+ this.volatileExecutions.push(record);
1043
+ }
1044
+ async listToolExecutions(workflowId, stepId) {
1045
+ if (this.toolDelegates?.toolExecution) {
1046
+ const rows = await this.toolDelegates.toolExecution.findMany({
1047
+ where: {
1048
+ ...workflowId ? { workflowId } : {},
1049
+ ...stepId ? { stepId } : {}
1050
+ },
1051
+ orderBy: { createdAt: "asc" }
1052
+ });
1053
+ return rows.map((r) => ({
1054
+ ...r,
1055
+ inputArgs: tryParseJson(r.inputArgs),
1056
+ outputSummary: tryParseJson(r.outputSummary),
1057
+ createdAt: r.createdAt instanceof Date ? r.createdAt.toISOString() : String(r.createdAt)
1058
+ }));
1059
+ }
1060
+ return this.volatileExecutions.filter((item) => {
1061
+ if (workflowId && item.workflowId !== workflowId) return false;
1062
+ if (stepId && item.stepId !== stepId) return false;
1063
+ return true;
1064
+ });
1065
+ }
1066
+ async getCachedToolResult(cacheKey) {
1067
+ if (this.toolDelegates?.toolCache) {
1068
+ const row = await this.toolDelegates.toolCache.findUnique({
1069
+ where: { cacheKey }
1070
+ });
1071
+ if (!row || Date.now() > Number(row.expiresAt)) return null;
1072
+ return tryParseJson(row.result);
1073
+ }
1074
+ const entry = this.volatileCache.get(cacheKey);
1075
+ if (!entry || Date.now() > entry.expiresAt) return null;
1076
+ return entry.result;
1077
+ }
1078
+ async setCachedToolResult(cacheKey, result, ttlSeconds) {
1079
+ const expiresAt = Date.now() + ttlSeconds * 1e3;
1080
+ if (this.toolDelegates?.toolCache) {
1081
+ await this.toolDelegates.toolCache.upsert({
1082
+ where: { cacheKey },
1083
+ create: { cacheKey, result: JSON.stringify(result), expiresAt },
1084
+ update: { result: JSON.stringify(result), expiresAt }
1085
+ });
1086
+ return;
1087
+ }
1088
+ this.volatileCache.set(cacheKey, { result, expiresAt });
1089
+ }
1090
+ async getStateValue(key) {
1091
+ if (this.toolDelegates?.sharedState) {
1092
+ const row = await this.toolDelegates.sharedState.findUnique({
1093
+ where: { stateKey: key }
1094
+ });
1095
+ if (!row || row.expiresAt && Date.now() > Number(row.expiresAt)) return null;
1096
+ return tryParseJson(row.value);
1097
+ }
1098
+ const entry = this.volatileState.get(key);
1099
+ if (!entry || entry.expiresAt && Date.now() > entry.expiresAt) return null;
1100
+ return entry.value;
1101
+ }
1102
+ async setStateValue(key, value, ttlSeconds) {
1103
+ const expiresAt = ttlSeconds ? Date.now() + ttlSeconds * 1e3 : null;
1104
+ if (this.toolDelegates?.sharedState) {
1105
+ await this.toolDelegates.sharedState.upsert({
1106
+ where: { stateKey: key },
1107
+ create: { stateKey: key, value: JSON.stringify(value), expiresAt },
1108
+ update: { value: JSON.stringify(value), expiresAt }
1109
+ });
1110
+ return;
1111
+ }
1112
+ this.volatileState.set(key, { value, expiresAt: expiresAt ?? void 0 });
1113
+ }
1114
+ async deleteStateValue(key) {
1115
+ if (this.toolDelegates?.sharedState) {
1116
+ await this.toolDelegates.sharedState.delete({
1117
+ where: { stateKey: key }
1118
+ });
1119
+ return;
1120
+ }
1121
+ this.volatileState.delete(key);
1122
+ }
1123
+ };
1124
+
1125
+ // src/agent/adapters/sqlite-adapter.ts
1126
+ function parseSqliteRow(row) {
1127
+ let result = void 0;
1128
+ let metadata = void 0;
1129
+ try {
1130
+ if (row.result) {
1131
+ result = JSON.parse(row.result);
1132
+ }
1133
+ } catch {
1134
+ }
1135
+ try {
1136
+ if (row.metadata) {
1137
+ metadata = JSON.parse(row.metadata);
1138
+ }
1139
+ } catch {
1140
+ }
1141
+ return {
1142
+ workflowId: row.workflow_id,
1143
+ stepId: row.step_id,
1144
+ runId: metadata?.runId ?? row.workflow_id,
1145
+ status: row.status,
1146
+ result,
1147
+ error: row.error ?? void 0,
1148
+ piiDetectedCount: metadata?.piiDetectedCount,
1149
+ tokens: metadata?.tokens,
1150
+ costUsd: metadata?.costUsd,
1151
+ metadata,
1152
+ createdAt: row.created_at,
1153
+ updatedAt: row.updated_at
1154
+ };
1155
+ }
1156
+ function parseExecutionRow(row) {
1157
+ let inputArgs = row.input_args;
1158
+ let outputSummary = row.output_summary;
1159
+ try {
1160
+ if (typeof row.input_args === "string") inputArgs = JSON.parse(row.input_args);
1161
+ } catch {
1162
+ }
1163
+ try {
1164
+ if (typeof row.output_summary === "string") outputSummary = JSON.parse(row.output_summary);
1165
+ } catch {
1166
+ }
1167
+ return {
1168
+ executionId: row.execution_id,
1169
+ workflowId: row.workflow_id ?? void 0,
1170
+ stepId: row.step_id ?? void 0,
1171
+ runId: row.workflow_id ?? void 0,
1172
+ toolId: row.tool_id,
1173
+ parentToolId: row.parent_tool_id ?? void 0,
1174
+ aliasUsed: row.alias_used ?? void 0,
1175
+ depth: Number(row.depth),
1176
+ inputArgs,
1177
+ outputSummary,
1178
+ durationMs: Number(row.duration_ms),
1179
+ status: row.status,
1180
+ error: row.error ?? void 0,
1181
+ piiFilteredCount: row.pii_filtered_count !== null && row.pii_filtered_count !== void 0 ? Number(row.pii_filtered_count) : void 0,
1182
+ costUsd: row.cost_usd !== null && row.cost_usd !== void 0 ? Number(row.cost_usd) : void 0,
1183
+ createdAt: row.created_at
1184
+ };
1185
+ }
1186
+ var SQLiteStorageAdapter = class {
1187
+ db;
1188
+ constructor(db) {
1189
+ this.db = db;
1190
+ this.initTables();
1191
+ }
1192
+ initTables() {
1193
+ this.db.exec(`
1194
+ CREATE TABLE IF NOT EXISTS avantgate_steps (
1195
+ workflow_id TEXT NOT NULL,
1196
+ step_id TEXT NOT NULL,
1197
+ status TEXT NOT NULL,
1198
+ result TEXT,
1199
+ error TEXT,
1200
+ metadata TEXT,
1201
+ created_at TEXT NOT NULL,
1202
+ updated_at TEXT NOT NULL,
1203
+ PRIMARY KEY (workflow_id, step_id)
1204
+ );
1205
+
1206
+ CREATE TABLE IF NOT EXISTS avantgate_tool_executions (
1207
+ execution_id TEXT PRIMARY KEY,
1208
+ workflow_id TEXT,
1209
+ step_id TEXT,
1210
+ tool_id TEXT NOT NULL,
1211
+ parent_tool_id TEXT,
1212
+ alias_used TEXT,
1213
+ depth INTEGER NOT NULL,
1214
+ input_args TEXT,
1215
+ output_summary TEXT,
1216
+ duration_ms INTEGER NOT NULL,
1217
+ status TEXT NOT NULL,
1218
+ error TEXT,
1219
+ pii_filtered_count INTEGER,
1220
+ cost_usd REAL,
1221
+ created_at TEXT NOT NULL
1222
+ );
1223
+
1224
+ CREATE TABLE IF NOT EXISTS avantgate_tool_cache (
1225
+ cache_key TEXT PRIMARY KEY,
1226
+ result TEXT NOT NULL,
1227
+ expires_at INTEGER NOT NULL
1228
+ );
1229
+
1230
+ CREATE TABLE IF NOT EXISTS avantgate_shared_state (
1231
+ state_key TEXT PRIMARY KEY,
1232
+ value TEXT NOT NULL,
1233
+ expires_at INTEGER
1234
+ );
1235
+ `);
1236
+ }
1237
+ async getStep(workflowId, stepId) {
1238
+ const stmt = this.db.prepare(
1239
+ "SELECT * FROM avantgate_steps WHERE workflow_id = ? AND step_id = ?"
1240
+ );
1241
+ const row = stmt.get(workflowId, stepId);
1242
+ if (!row) {
1243
+ return null;
1244
+ }
1245
+ return parseSqliteRow(row);
1246
+ }
1247
+ async saveStep(step) {
1248
+ const stmt = this.db.prepare(`
1249
+ INSERT INTO avantgate_steps (
1250
+ workflow_id, step_id, status, result, error, metadata, created_at, updated_at
1251
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?)
1252
+ ON CONFLICT(workflow_id, step_id) DO UPDATE SET
1253
+ status = excluded.status,
1254
+ result = excluded.result,
1255
+ error = excluded.error,
1256
+ metadata = excluded.metadata,
1257
+ updated_at = excluded.updated_at;
1258
+ `);
1259
+ const mergedMetadata = {
1260
+ ...step.metadata ?? {},
1261
+ ...step.runId ? { runId: step.runId } : {},
1262
+ ...step.piiDetectedCount !== void 0 ? { piiDetectedCount: step.piiDetectedCount } : {},
1263
+ ...step.tokens ? { tokens: step.tokens } : {},
1264
+ ...step.costUsd !== void 0 ? { costUsd: step.costUsd } : {}
1265
+ };
1266
+ const resultStr = step.result !== void 0 ? JSON.stringify(step.result) : null;
1267
+ const metadataStr = Object.keys(mergedMetadata).length > 0 ? JSON.stringify(mergedMetadata) : null;
1268
+ stmt.run(
1269
+ step.workflowId,
1270
+ step.stepId,
1271
+ step.status,
1272
+ resultStr,
1273
+ step.error ?? null,
1274
+ metadataStr,
1275
+ step.createdAt,
1276
+ step.updatedAt
1277
+ );
1278
+ }
1279
+ async updateStepStatus(workflowId, stepId, status, patch) {
1280
+ const existing = await this.getStep(workflowId, stepId);
1281
+ const now = (/* @__PURE__ */ new Date()).toISOString();
1282
+ const updated = {
1283
+ workflowId,
1284
+ stepId,
1285
+ status,
1286
+ result: patch?.result !== void 0 ? patch.result : existing?.result,
1287
+ error: patch?.error !== void 0 ? patch.error : existing?.error,
1288
+ metadata: patch?.metadata !== void 0 ? patch.metadata : existing?.metadata,
1289
+ createdAt: existing?.createdAt ?? now,
1290
+ updatedAt: now
1291
+ };
1292
+ await this.saveStep(updated);
1293
+ }
1294
+ async listSteps(workflowId) {
1295
+ const stmt = this.db.prepare(
1296
+ "SELECT * FROM avantgate_steps WHERE workflow_id = ? ORDER BY created_at ASC"
1297
+ );
1298
+ const rows = stmt.all(workflowId);
1299
+ return rows.map(parseSqliteRow);
1300
+ }
1301
+ async saveToolExecution(record) {
1302
+ const stmt = this.db.prepare(`
1303
+ INSERT INTO avantgate_tool_executions (
1304
+ execution_id, workflow_id, step_id, tool_id, parent_tool_id, alias_used,
1305
+ depth, input_args, output_summary, duration_ms, status, error,
1306
+ pii_filtered_count, cost_usd, created_at
1307
+ ) VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
1308
+ `);
1309
+ stmt.run(
1310
+ record.executionId,
1311
+ record.workflowId ?? null,
1312
+ record.stepId ?? null,
1313
+ record.toolId,
1314
+ record.parentToolId ?? null,
1315
+ record.aliasUsed ?? null,
1316
+ record.depth,
1317
+ record.inputArgs !== void 0 ? JSON.stringify(record.inputArgs) : null,
1318
+ record.outputSummary !== void 0 ? JSON.stringify(record.outputSummary) : null,
1319
+ record.durationMs,
1320
+ record.status,
1321
+ record.error ?? null,
1322
+ record.piiFilteredCount ?? null,
1323
+ record.costUsd ?? null,
1324
+ record.createdAt
1325
+ );
1326
+ }
1327
+ async listToolExecutions(workflowId, stepId) {
1328
+ let query = "SELECT * FROM avantgate_tool_executions";
1329
+ const params = [];
1330
+ if (workflowId && stepId) {
1331
+ query += " WHERE workflow_id = ? AND step_id = ?";
1332
+ params.push(workflowId, stepId);
1333
+ } else if (workflowId) {
1334
+ query += " WHERE workflow_id = ?";
1335
+ params.push(workflowId);
1336
+ }
1337
+ query += " ORDER BY created_at ASC";
1338
+ const stmt = this.db.prepare(query);
1339
+ const rows = stmt.all(...params);
1340
+ return rows.map(parseExecutionRow);
1341
+ }
1342
+ async getCachedToolResult(cacheKey) {
1343
+ const stmt = this.db.prepare(
1344
+ "SELECT result, expires_at FROM avantgate_tool_cache WHERE cache_key = ?"
1345
+ );
1346
+ const row = stmt.get(cacheKey);
1347
+ if (!row) {
1348
+ return null;
1349
+ }
1350
+ if (Date.now() > Number(row.expires_at)) {
1351
+ this.db.prepare("DELETE FROM avantgate_tool_cache WHERE cache_key = ?").run(cacheKey);
1352
+ return null;
1353
+ }
1354
+ try {
1355
+ return JSON.parse(row.result);
1356
+ } catch {
1357
+ return null;
1358
+ }
1359
+ }
1360
+ async setCachedToolResult(cacheKey, result, ttlSeconds) {
1361
+ const stmt = this.db.prepare(`
1362
+ INSERT INTO avantgate_tool_cache (cache_key, result, expires_at)
1363
+ VALUES (?, ?, ?)
1364
+ ON CONFLICT(cache_key) DO UPDATE SET
1365
+ result = excluded.result,
1366
+ expires_at = excluded.expires_at;
1367
+ `);
1368
+ const expiresAt = Date.now() + ttlSeconds * 1e3;
1369
+ stmt.run(cacheKey, JSON.stringify(result), expiresAt);
1370
+ }
1371
+ async getStateValue(key) {
1372
+ const stmt = this.db.prepare(
1373
+ "SELECT value, expires_at FROM avantgate_shared_state WHERE state_key = ?"
1374
+ );
1375
+ const row = stmt.get(key);
1376
+ if (!row) {
1377
+ return null;
1378
+ }
1379
+ if (row.expires_at && Date.now() > Number(row.expires_at)) {
1380
+ this.db.prepare("DELETE FROM avantgate_shared_state WHERE state_key = ?").run(key);
1381
+ return null;
1382
+ }
1383
+ try {
1384
+ return JSON.parse(row.value);
1385
+ } catch {
1386
+ return null;
1387
+ }
1388
+ }
1389
+ async setStateValue(key, value, ttlSeconds) {
1390
+ const stmt = this.db.prepare(`
1391
+ INSERT INTO avantgate_shared_state (state_key, value, expires_at)
1392
+ VALUES (?, ?, ?)
1393
+ ON CONFLICT(state_key) DO UPDATE SET
1394
+ value = excluded.value,
1395
+ expires_at = excluded.expires_at;
1396
+ `);
1397
+ const expiresAt = ttlSeconds ? Date.now() + ttlSeconds * 1e3 : null;
1398
+ stmt.run(key, JSON.stringify(value), expiresAt);
1399
+ }
1400
+ async deleteStateValue(key) {
1401
+ this.db.prepare("DELETE FROM avantgate_shared_state WHERE state_key = ?").run(key);
1402
+ }
1403
+ };
1404
+
1405
+ // src/agent/adapters/platform-adapter.ts
1406
+ function mapStepToEvent(record) {
1407
+ const now = record.updatedAt || record.createdAt || (/* @__PURE__ */ new Date()).toISOString();
1408
+ if (record.status === "RUNNING") {
1409
+ const startEvent = {
1410
+ type: "STEP_START",
1411
+ timestamp: now,
1412
+ stepName: record.stepId,
1413
+ metadata: record.metadata
1414
+ };
1415
+ return startEvent;
1416
+ }
1417
+ if (record.status === "COMPLETED") {
1418
+ const completedEvent = {
1419
+ type: "STEP_COMPLETED",
1420
+ timestamp: now,
1421
+ stepName: record.stepId,
1422
+ piiDetectedCount: record.piiDetectedCount,
1423
+ resultSummary: record.result,
1424
+ metadata: record.metadata
1425
+ };
1426
+ return completedEvent;
1427
+ }
1428
+ if (record.status === "FAILED") {
1429
+ const failedEvent = {
1430
+ type: "STEP_FAILED",
1431
+ timestamp: now,
1432
+ stepName: record.stepId,
1433
+ error: record.error ?? "Step execution failed"
1434
+ };
1435
+ return failedEvent;
1436
+ }
1437
+ if (record.status === "WAITING_APPROVAL") {
1438
+ const approvalEvent = {
1439
+ type: "STEP_APPROVAL_REQUEST",
1440
+ timestamp: now,
1441
+ stepName: record.stepId,
1442
+ actionType: record.metadata?.actionType ?? "MANUAL_APPROVAL",
1443
+ payloadSummary: record.result ?? record.metadata
1444
+ };
1445
+ return approvalEvent;
1446
+ }
1447
+ return null;
1448
+ }
1449
+ function mapToolExecutionToEvent(record) {
1450
+ return {
1451
+ type: "TOOL_EXECUTION",
1452
+ timestamp: record.createdAt,
1453
+ toolId: record.toolId,
1454
+ toolName: record.toolId,
1455
+ aliasUsed: record.aliasUsed,
1456
+ durationMs: record.durationMs,
1457
+ success: record.status === "SUCCESS",
1458
+ parentToolId: record.parentToolId,
1459
+ depth: record.depth,
1460
+ llmSummary: record.outputSummary,
1461
+ piiFilteredCount: record.piiFilteredCount,
1462
+ tokens: record.tokens,
1463
+ costUsd: record.costUsd,
1464
+ cached: record.cached,
1465
+ error: record.error
1466
+ };
1467
+ }
1468
+ var PlatformStorageAdapter = class {
1469
+ exporter;
1470
+ primary;
1471
+ constructor(config) {
1472
+ this.exporter = config.exporter;
1473
+ this.primary = config.primaryStorage ?? new MemoryStorageAdapter();
1474
+ }
1475
+ async getStep(workflowId, stepId) {
1476
+ return this.primary.getStep(workflowId, stepId);
1477
+ }
1478
+ async saveStep(record) {
1479
+ await this.primary.saveStep(record);
1480
+ const event = mapStepToEvent(record);
1481
+ if (!event) return;
1482
+ const runId = record.runId ?? record.workflowId;
1483
+ this.exporter.enqueue(runId, event, record.tokens ? { ...record.tokens, costUsd: record.costUsd } : void 0);
1484
+ }
1485
+ async updateStepStatus(workflowId, stepId, status, updates) {
1486
+ await this.primary.updateStepStatus(workflowId, stepId, status, updates);
1487
+ const existing = await this.primary.getStep(workflowId, stepId);
1488
+ if (!existing) return;
1489
+ const event = mapStepToEvent(existing);
1490
+ if (!event) return;
1491
+ const runId = existing.runId ?? workflowId;
1492
+ this.exporter.enqueue(runId, event);
1493
+ }
1494
+ async listSteps(workflowId) {
1495
+ return this.primary.listSteps(workflowId);
1496
+ }
1497
+ async saveToolExecution(record) {
1498
+ if (this.primary.saveToolExecution) {
1499
+ await this.primary.saveToolExecution(record);
1500
+ }
1501
+ const event = mapToolExecutionToEvent(record);
1502
+ const runId = record.runId ?? record.workflowId ?? "default-run";
1503
+ this.exporter.enqueue(runId, event, record.tokens ? { ...record.tokens, costUsd: record.costUsd } : void 0);
1504
+ }
1505
+ async listToolExecutions(workflowId, stepId) {
1506
+ if (this.primary.listToolExecutions) {
1507
+ return this.primary.listToolExecutions(workflowId, stepId);
1508
+ }
1509
+ return [];
1510
+ }
1511
+ async getCachedToolResult(cacheKey) {
1512
+ if (this.primary.getCachedToolResult) {
1513
+ return this.primary.getCachedToolResult(cacheKey);
1514
+ }
1515
+ return null;
1516
+ }
1517
+ async setCachedToolResult(cacheKey, result, ttlSeconds) {
1518
+ if (this.primary.setCachedToolResult) {
1519
+ await this.primary.setCachedToolResult(cacheKey, result, ttlSeconds);
1520
+ }
1521
+ }
1522
+ async getStateValue(key) {
1523
+ if (this.primary.getStateValue) {
1524
+ return this.primary.getStateValue(key);
1525
+ }
1526
+ return null;
1527
+ }
1528
+ async setStateValue(key, value, ttlSeconds) {
1529
+ if (this.primary.setStateValue) {
1530
+ await this.primary.setStateValue(key, value, ttlSeconds);
1531
+ }
1532
+ }
1533
+ async deleteStateValue(key) {
1534
+ if (this.primary.deleteStateValue) {
1535
+ await this.primary.deleteStateValue(key);
1536
+ }
1537
+ }
1538
+ async clearStateValues() {
1539
+ if (this.primary.clearStateValues) {
1540
+ await this.primary.clearStateValues();
1541
+ }
1542
+ }
1543
+ };
1544
+
1545
+ // src/agent/telemetry/http-exporter.ts
1546
+ var DEFAULT_ENDPOINT = "https://api.avantgate.cloud/api/v1/ingest/events";
1547
+ var DEFAULT_INTERVAL_MS = 5e3;
1548
+ var DEFAULT_MAX_BATCH = 50;
1549
+ var DEFAULT_MAX_QUEUE = 1e3;
1550
+ function aggregateUsage(items) {
1551
+ let prompt = 0;
1552
+ let completion = 0;
1553
+ let total = 0;
1554
+ let cost = 0;
1555
+ let hasUsage = false;
1556
+ for (const item of items) {
1557
+ if (!item.usage) continue;
1558
+ hasUsage = true;
1559
+ prompt += item.usage.promptTokens ?? 0;
1560
+ completion += item.usage.completionTokens ?? 0;
1561
+ total += item.usage.totalTokens ?? 0;
1562
+ cost += item.usage.costUsd ?? 0;
1563
+ }
1564
+ if (!hasUsage) return void 0;
1565
+ return { promptTokens: prompt, completionTokens: completion, totalTokens: total, costUsd: cost };
1566
+ }
1567
+ function buildPayloads(items, agentName) {
1568
+ const grouped = /* @__PURE__ */ new Map();
1569
+ for (const item of items) {
1570
+ const list = grouped.get(item.runId) ?? [];
1571
+ list.push(item);
1572
+ grouped.set(item.runId, list);
1573
+ }
1574
+ const payloads = [];
1575
+ for (const [runId, runItems] of grouped.entries()) {
1576
+ payloads.push({
1577
+ runId,
1578
+ agentName,
1579
+ timestamp: (/* @__PURE__ */ new Date()).toISOString(),
1580
+ events: runItems.map((ri) => ri.event),
1581
+ usage: aggregateUsage(runItems)
1582
+ });
1583
+ }
1584
+ return payloads;
1585
+ }
1586
+ async function sendPayload(payload, endpoint, apiKey, fetchFn) {
1587
+ const response = await fetchFn(endpoint, {
1588
+ method: "POST",
1589
+ headers: {
1590
+ "Content-Type": "application/json",
1591
+ Authorization: `Bearer ${apiKey}`
1592
+ },
1593
+ body: JSON.stringify(payload)
1594
+ });
1595
+ if (!response.ok) {
1596
+ throw new Error(`Telemetry ingestion failed: ${response.status} ${response.statusText}`);
1597
+ }
1598
+ }
1599
+ var HttpTelemetryExporter = class {
1600
+ apiKey;
1601
+ endpoint;
1602
+ agentName;
1603
+ batchIntervalMs;
1604
+ maxBatchSize;
1605
+ maxQueueSize;
1606
+ fetchFn;
1607
+ onError;
1608
+ queue = [];
1609
+ timer = null;
1610
+ isFlushing = false;
1611
+ constructor(options) {
1612
+ this.apiKey = options.apiKey;
1613
+ this.endpoint = options.endpoint ?? DEFAULT_ENDPOINT;
1614
+ this.agentName = options.agentName;
1615
+ this.batchIntervalMs = options.batchIntervalMs ?? DEFAULT_INTERVAL_MS;
1616
+ this.maxBatchSize = options.maxBatchSize ?? DEFAULT_MAX_BATCH;
1617
+ this.maxQueueSize = options.maxQueueSize ?? DEFAULT_MAX_QUEUE;
1618
+ this.fetchFn = options.fetchFn ?? globalThis.fetch;
1619
+ this.onError = options.onError;
1620
+ this.startTimer();
1621
+ }
1622
+ startTimer() {
1623
+ if (this.batchIntervalMs <= 0) return;
1624
+ this.timer = setInterval(() => {
1625
+ void this.flush();
1626
+ }, this.batchIntervalMs);
1627
+ if (this.timer && typeof this.timer === "object" && "unref" in this.timer) {
1628
+ this.timer.unref();
1629
+ }
1630
+ }
1631
+ enqueue(runId, event, usage) {
1632
+ if (this.queue.length >= this.maxQueueSize) {
1633
+ this.queue.shift();
1634
+ }
1635
+ this.queue.push({ runId, event, usage });
1636
+ if (this.queue.length >= this.maxBatchSize) {
1637
+ void this.flush();
1638
+ }
1639
+ }
1640
+ async flush() {
1641
+ if (this.isFlushing || this.queue.length === 0) return;
1642
+ this.isFlushing = true;
1643
+ const toProcess = this.queue.splice(0, this.maxBatchSize);
1644
+ const payloads = buildPayloads(toProcess, this.agentName);
1645
+ let hadError = false;
1646
+ try {
1647
+ await Promise.all(
1648
+ payloads.map(
1649
+ (p) => sendPayload(p, this.endpoint, this.apiKey, this.fetchFn)
1650
+ )
1651
+ );
1652
+ } catch (err) {
1653
+ hadError = true;
1654
+ const error = err instanceof Error ? err : new Error(String(err));
1655
+ this.onError?.(error);
1656
+ } finally {
1657
+ this.isFlushing = false;
1658
+ if (!hadError && this.queue.length > 0) {
1659
+ void this.flush();
1660
+ }
1661
+ }
1662
+ }
1663
+ async shutdown() {
1664
+ if (this.timer) {
1665
+ clearInterval(this.timer);
1666
+ this.timer = null;
1667
+ }
1668
+ await this.flush();
1669
+ }
1670
+ };
1671
+ export {
1672
+ AgentToolFactory,
1673
+ CircularToolCallError,
1674
+ CompositeToolStrategy,
1675
+ DtoValidationError,
1676
+ HttpTelemetryExporter,
1677
+ KeyValueStorageAdapter,
1678
+ MemoryStorageAdapter,
1679
+ PhaseBasedToolStrategy,
1680
+ PiiLeakError,
1681
+ PlatformStorageAdapter,
1682
+ PrismaStorageAdapter,
1683
+ RoleBasedToolStrategy,
1684
+ SQLiteStorageAdapter,
1685
+ StepExecutionError,
1686
+ StepSuspendedError,
1687
+ ToolAccessDeniedError,
1688
+ ToolCallDepthExceededError,
1689
+ ToolNotFoundError,
1690
+ ToolRegistry,
1691
+ ToolSubCallQuotaError,
1692
+ applyToolStrategy,
1693
+ auditToolResult,
1694
+ createCustomStorageAdapter,
1695
+ createIsolatedTool,
1696
+ createStepRunner,
1697
+ createToolInvoker,
1698
+ createToolSharedState,
1699
+ dto
1700
+ };