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