@opperai/agents 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js ADDED
@@ -0,0 +1,2616 @@
1
+ import { randomUUID } from 'crypto';
2
+ import { z } from 'zod';
3
+ import { Opper } from 'opperai';
4
+ import { zodToJsonSchema } from 'zod-to-json-schema';
5
+ import { Client } from '@modelcontextprotocol/sdk/client/index.js';
6
+ import { SSEClientTransport } from '@modelcontextprotocol/sdk/client/sse.js';
7
+ import { StdioClientTransport } from '@modelcontextprotocol/sdk/client/stdio.js';
8
+ import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/streamableHttp.js';
9
+
10
+ var __defProp = Object.defineProperty;
11
+ var __getOwnPropNames = Object.getOwnPropertyNames;
12
+ var __esm = (fn, res) => function __init() {
13
+ return fn && (res = (0, fn[__getOwnPropNames(fn)[0]])(fn = 0)), res;
14
+ };
15
+ var __export = (target, all) => {
16
+ for (var name in all)
17
+ __defProp(target, name, { get: all[name], enumerable: true });
18
+ };
19
+
20
+ // src/base/result.ts
21
+ var Result, ok, err;
22
+ var init_result = __esm({
23
+ "src/base/result.ts"() {
24
+ Result = {
25
+ ok(value) {
26
+ return { ok: true, value };
27
+ },
28
+ err(error) {
29
+ return { ok: false, error };
30
+ },
31
+ isOk(result) {
32
+ return result.ok;
33
+ },
34
+ isErr(result) {
35
+ return !result.ok;
36
+ },
37
+ map(result, mapper) {
38
+ if (result.ok) {
39
+ return Result.ok(mapper(result.value));
40
+ }
41
+ return result;
42
+ },
43
+ mapError(result, mapper) {
44
+ if (!result.ok) {
45
+ return Result.err(mapper(result.error));
46
+ }
47
+ return result;
48
+ },
49
+ unwrapOr(result, fallback) {
50
+ return result.ok ? result.value : fallback;
51
+ }
52
+ };
53
+ ok = Result.ok;
54
+ err = Result.err;
55
+ }
56
+ });
57
+ var ToolMetadataSchema, ToolCallRecordSchema, toolResultBaseSchema, ToolResultSuccessSchema, ToolResultFailureSchema, ToolResultSchema, ToolResultFactory, createToolCallRecord, validateToolInput, coerceToolDefinition, isToolProvider, normalizeToolEntries;
58
+ var init_tool = __esm({
59
+ "src/base/tool.ts"() {
60
+ init_result();
61
+ ToolMetadataSchema = z.record(z.string(), z.unknown());
62
+ ToolCallRecordSchema = z.object({
63
+ id: z.string().optional().default(() => randomUUID()),
64
+ toolName: z.string(),
65
+ input: z.unknown(),
66
+ output: z.unknown().optional(),
67
+ success: z.boolean().optional(),
68
+ error: z.union([z.string(), z.object({ message: z.string() }).passthrough()]).optional(),
69
+ startedAt: z.number().default(() => Date.now()),
70
+ finishedAt: z.number().optional(),
71
+ metadata: ToolMetadataSchema.default({})
72
+ });
73
+ toolResultBaseSchema = z.object({
74
+ toolName: z.string(),
75
+ metadata: ToolMetadataSchema.default({}),
76
+ startedAt: z.number().optional(),
77
+ finishedAt: z.number().optional()
78
+ });
79
+ ToolResultSuccessSchema = toolResultBaseSchema.extend({
80
+ success: z.literal(true),
81
+ output: z.any()
82
+ }).refine((data) => data.output !== void 0, {
83
+ message: "output is required"
84
+ });
85
+ ToolResultFailureSchema = toolResultBaseSchema.extend({
86
+ success: z.literal(false),
87
+ error: z.union([z.string(), z.instanceof(Error)])
88
+ });
89
+ ToolResultSchema = z.union([
90
+ ToolResultSuccessSchema,
91
+ ToolResultFailureSchema
92
+ ]);
93
+ ToolResultFactory = {
94
+ success(toolName, output, init = {}) {
95
+ const result = {
96
+ success: true,
97
+ toolName,
98
+ output,
99
+ ...init.startedAt !== void 0 && { startedAt: init.startedAt },
100
+ finishedAt: init.finishedAt ?? Date.now(),
101
+ metadata: init.metadata ?? {}
102
+ };
103
+ ToolResultSuccessSchema.parse(result);
104
+ return result;
105
+ },
106
+ failure(toolName, error, init = {}) {
107
+ const result = {
108
+ success: false,
109
+ toolName,
110
+ error,
111
+ ...init.startedAt !== void 0 && { startedAt: init.startedAt },
112
+ finishedAt: init.finishedAt ?? Date.now(),
113
+ metadata: init.metadata ?? {}
114
+ };
115
+ ToolResultFailureSchema.parse(result);
116
+ return result;
117
+ },
118
+ isSuccess(result) {
119
+ return result.success;
120
+ },
121
+ isFailure(result) {
122
+ return !result.success;
123
+ }
124
+ };
125
+ createToolCallRecord = (input) => ToolCallRecordSchema.parse(input);
126
+ validateToolInput = (schema, payload) => {
127
+ const parsed = schema.safeParse(payload);
128
+ if (!parsed.success) {
129
+ return err(parsed.error);
130
+ }
131
+ return ok(parsed.data);
132
+ };
133
+ coerceToolDefinition = (definition) => {
134
+ if (!definition.name || definition.name.trim().length === 0) {
135
+ throw new Error("Tool definition requires a non-empty name");
136
+ }
137
+ if (!definition.metadata) {
138
+ definition.metadata = {};
139
+ }
140
+ return definition;
141
+ };
142
+ isToolProvider = (value) => {
143
+ if (typeof value !== "object" || value === null) {
144
+ return false;
145
+ }
146
+ const candidate = value;
147
+ return typeof candidate.setup === "function" && typeof candidate.teardown === "function";
148
+ };
149
+ normalizeToolEntries = (entries) => {
150
+ const tools = [];
151
+ const providers = [];
152
+ for (const entry of entries) {
153
+ if (isToolProvider(entry)) {
154
+ providers.push(entry);
155
+ } else {
156
+ tools.push(entry);
157
+ }
158
+ }
159
+ return { tools, providers };
160
+ };
161
+ }
162
+ });
163
+
164
+ // src/base/context.ts
165
+ var context_exports = {};
166
+ __export(context_exports, {
167
+ AgentContext: () => AgentContext,
168
+ ExecutionCycleSchema: () => ExecutionCycleSchema,
169
+ UsageSchema: () => UsageSchema
170
+ });
171
+ var UsageSchema, ExecutionCycleSchema, AgentContext;
172
+ var init_context = __esm({
173
+ "src/base/context.ts"() {
174
+ init_tool();
175
+ UsageSchema = z.object({
176
+ requests: z.number().int().nonnegative().default(0),
177
+ inputTokens: z.number().int().nonnegative().default(0),
178
+ outputTokens: z.number().int().nonnegative().default(0),
179
+ totalTokens: z.number().int().nonnegative().default(0),
180
+ cost: z.object({
181
+ generation: z.number().nonnegative().default(0),
182
+ platform: z.number().nonnegative().default(0),
183
+ total: z.number().nonnegative().default(0)
184
+ }).default({ generation: 0, platform: 0, total: 0 })
185
+ });
186
+ ExecutionCycleSchema = z.object({
187
+ iteration: z.number().int().nonnegative(),
188
+ thought: z.unknown().nullable().optional(),
189
+ toolCalls: z.array(ToolCallRecordSchema).default([]),
190
+ results: z.array(z.unknown()).default([]),
191
+ timestamp: z.number().default(() => Date.now())
192
+ });
193
+ AgentContext = class {
194
+ agentName;
195
+ sessionId;
196
+ parentSpanId;
197
+ iteration = 0;
198
+ goal;
199
+ executionHistory = [];
200
+ toolCalls = [];
201
+ usage;
202
+ metadata;
203
+ startedAt;
204
+ updatedAt;
205
+ constructor(options) {
206
+ const now = Date.now();
207
+ this.agentName = options.agentName;
208
+ this.sessionId = options.sessionId ?? randomUUID();
209
+ this.parentSpanId = options.parentSpanId ?? null;
210
+ this.goal = options.goal;
211
+ this.metadata = { ...options.metadata ?? {} };
212
+ this.startedAt = now;
213
+ this.updatedAt = now;
214
+ this.usage = UsageSchema.parse({});
215
+ }
216
+ updateUsage(delta) {
217
+ const next = UsageSchema.parse(delta);
218
+ this.usage = {
219
+ requests: this.usage.requests + next.requests,
220
+ inputTokens: this.usage.inputTokens + next.inputTokens,
221
+ outputTokens: this.usage.outputTokens + next.outputTokens,
222
+ totalTokens: this.usage.totalTokens + next.totalTokens,
223
+ cost: {
224
+ generation: this.usage.cost.generation + next.cost.generation,
225
+ platform: this.usage.cost.platform + next.cost.platform,
226
+ total: this.usage.cost.total + next.cost.total
227
+ }
228
+ };
229
+ this.touch();
230
+ }
231
+ addCycle(cycle) {
232
+ const parsed = ExecutionCycleSchema.parse(cycle);
233
+ this.executionHistory.push(parsed);
234
+ this.touch();
235
+ return parsed;
236
+ }
237
+ recordToolCall(call) {
238
+ const parsed = ToolCallRecordSchema.parse(call);
239
+ this.toolCalls.push(parsed);
240
+ this.touch();
241
+ return parsed;
242
+ }
243
+ getContextSize() {
244
+ return this.usage.totalTokens;
245
+ }
246
+ getLastNCycles(count = 3) {
247
+ if (count <= 0) {
248
+ return [];
249
+ }
250
+ return this.executionHistory.slice(-count);
251
+ }
252
+ getLastIterationsSummary(count = 2) {
253
+ return this.getLastNCycles(count).map((cycle) => {
254
+ const thought = typeof cycle.thought === "object" && cycle.thought !== null ? { ...cycle.thought } : { text: cycle.thought };
255
+ return {
256
+ iteration: cycle.iteration,
257
+ thought,
258
+ toolCalls: cycle.toolCalls.map((call) => ({
259
+ toolName: call.toolName,
260
+ ...call.success !== void 0 && { success: call.success }
261
+ })),
262
+ results: [...cycle.results]
263
+ };
264
+ });
265
+ }
266
+ setMetadata(key, value) {
267
+ this.metadata[key] = value;
268
+ this.touch();
269
+ }
270
+ clearHistory() {
271
+ this.executionHistory.length = 0;
272
+ this.toolCalls.length = 0;
273
+ this.touch();
274
+ }
275
+ snapshot() {
276
+ return {
277
+ agentName: this.agentName,
278
+ sessionId: this.sessionId,
279
+ parentSpanId: this.parentSpanId,
280
+ iteration: this.iteration,
281
+ goal: this.goal,
282
+ executionHistory: [...this.executionHistory],
283
+ usage: { ...this.usage },
284
+ toolCalls: [...this.toolCalls],
285
+ metadata: { ...this.metadata },
286
+ startedAt: this.startedAt,
287
+ updatedAt: this.updatedAt
288
+ };
289
+ }
290
+ touch() {
291
+ this.updatedAt = Date.now();
292
+ }
293
+ };
294
+ }
295
+ });
296
+
297
+ // src/utils/logger.ts
298
+ var LogLevel = /* @__PURE__ */ ((LogLevel2) => {
299
+ LogLevel2[LogLevel2["DEBUG"] = 0] = "DEBUG";
300
+ LogLevel2[LogLevel2["INFO"] = 1] = "INFO";
301
+ LogLevel2[LogLevel2["WARN"] = 2] = "WARN";
302
+ LogLevel2[LogLevel2["ERROR"] = 3] = "ERROR";
303
+ LogLevel2[LogLevel2["SILENT"] = 4] = "SILENT";
304
+ return LogLevel2;
305
+ })(LogLevel || {});
306
+ var formatMeta = (meta) => {
307
+ if (!meta) {
308
+ return "";
309
+ }
310
+ try {
311
+ return ` ${JSON.stringify(meta)}`;
312
+ } catch {
313
+ return " [unserializable meta]";
314
+ }
315
+ };
316
+ var writeToStdout = (level, message, meta) => {
317
+ const suffix = formatMeta(meta);
318
+ process.stdout.write(`[${level}] ${message}${suffix}
319
+ `);
320
+ };
321
+ var ConsoleLogger = class {
322
+ level;
323
+ constructor(level = 1 /* INFO */) {
324
+ this.level = level;
325
+ }
326
+ debug(message, meta) {
327
+ if (this.level <= 0 /* DEBUG */) {
328
+ writeToStdout("DEBUG", message, meta);
329
+ }
330
+ }
331
+ info(message, meta) {
332
+ if (this.level <= 1 /* INFO */) {
333
+ writeToStdout("INFO", message, meta);
334
+ }
335
+ }
336
+ warn(message, meta) {
337
+ if (this.level <= 2 /* WARN */) {
338
+ console.warn(`[WARN] ${message}`, meta ? meta : "");
339
+ }
340
+ }
341
+ error(message, error, meta) {
342
+ if (this.level <= 3 /* ERROR */) {
343
+ if (error) {
344
+ console.error(`[ERROR] ${message}`, error, meta ? meta : "");
345
+ } else {
346
+ console.error(`[ERROR] ${message}`, meta ? meta : "");
347
+ }
348
+ }
349
+ }
350
+ getLevel() {
351
+ return this.level;
352
+ }
353
+ setLevel(level) {
354
+ this.level = level;
355
+ }
356
+ };
357
+ var SilentLogger = class {
358
+ debug(message, meta) {
359
+ }
360
+ info(message, meta) {
361
+ }
362
+ warn(message, meta) {
363
+ }
364
+ error(message, error, meta) {
365
+ }
366
+ getLevel() {
367
+ return 4 /* SILENT */;
368
+ }
369
+ setLevel(level) {
370
+ }
371
+ };
372
+ var defaultLogger = new ConsoleLogger(2 /* WARN */);
373
+ function getDefaultLogger() {
374
+ return defaultLogger;
375
+ }
376
+ function setDefaultLogger(logger) {
377
+ defaultLogger = logger;
378
+ }
379
+
380
+ // src/base/hooks.ts
381
+ var HookEvents = {
382
+ AgentStart: "agent:start",
383
+ AgentEnd: "agent:end",
384
+ LoopStart: "loop:start",
385
+ LoopEnd: "loop:end",
386
+ LlmCall: "llm:call",
387
+ LlmResponse: "llm:response",
388
+ ThinkEnd: "think:end",
389
+ BeforeTool: "tool:before",
390
+ AfterTool: "tool:after",
391
+ ToolError: "tool:error",
392
+ MemoryRead: "memory:read",
393
+ MemoryWrite: "memory:write",
394
+ MemoryError: "memory:error"
395
+ };
396
+ var HookManager = class {
397
+ registry = /* @__PURE__ */ new Map();
398
+ logger;
399
+ constructor(logger) {
400
+ this.logger = logger ?? getDefaultLogger();
401
+ }
402
+ on(event, handler) {
403
+ const existing = this.registry.get(event) ?? /* @__PURE__ */ new Set();
404
+ existing.add(handler);
405
+ this.registry.set(event, existing);
406
+ return () => existing.delete(handler);
407
+ }
408
+ once(event, handler) {
409
+ const disposable = async (payload) => {
410
+ await handler(payload);
411
+ this.off(event, disposable);
412
+ };
413
+ return this.on(event, disposable);
414
+ }
415
+ off(event, handler) {
416
+ const listeners = this.registry.get(event);
417
+ if (!listeners) {
418
+ return;
419
+ }
420
+ listeners.delete(handler);
421
+ if (listeners.size === 0) {
422
+ this.registry.delete(event);
423
+ }
424
+ }
425
+ clear() {
426
+ this.registry.clear();
427
+ }
428
+ listenerCount(event) {
429
+ if (event) {
430
+ return this.registry.get(event)?.size ?? 0;
431
+ }
432
+ let total = 0;
433
+ for (const listeners of this.registry.values()) {
434
+ total += listeners.size;
435
+ }
436
+ return total;
437
+ }
438
+ async emit(event, payload) {
439
+ const listeners = Array.from(
440
+ this.registry.get(event) ?? /* @__PURE__ */ new Set()
441
+ );
442
+ if (listeners.length === 0) {
443
+ return;
444
+ }
445
+ for (const [index, listener] of listeners.entries()) {
446
+ try {
447
+ await Promise.resolve(listener(payload));
448
+ } catch (error) {
449
+ this.logger.warn(`Hook handler failed for event "${event}"`, {
450
+ event,
451
+ handlerIndex: index,
452
+ error: error instanceof Error ? error.message : String(error)
453
+ });
454
+ }
455
+ }
456
+ }
457
+ registerMany(registrations) {
458
+ const cleanups = registrations.map(
459
+ ({ event, handler }) => this.on(event, handler)
460
+ );
461
+ return () => cleanups.forEach((dispose) => dispose());
462
+ }
463
+ };
464
+ var createHookManager = (logger) => new HookManager(logger);
465
+
466
+ // src/base/agent.ts
467
+ init_tool();
468
+ var MemoryEntryMetadataSchema = z.object({
469
+ createdAt: z.number(),
470
+ updatedAt: z.number(),
471
+ accessCount: z.number().default(0)
472
+ });
473
+ var MemoryEntrySchema = z.object({
474
+ key: z.string(),
475
+ description: z.string(),
476
+ value: z.unknown(),
477
+ metadata: MemoryEntryMetadataSchema
478
+ });
479
+ var InMemoryStore = class {
480
+ store = /* @__PURE__ */ new Map();
481
+ /**
482
+ * Check if memory has any entries
483
+ */
484
+ async hasEntries() {
485
+ return this.store.size > 0;
486
+ }
487
+ /**
488
+ * List memory catalog (summaries without values) for LLM consumption
489
+ */
490
+ async listEntries() {
491
+ const catalog = [];
492
+ for (const entry of this.store.values()) {
493
+ catalog.push({
494
+ key: entry.key,
495
+ description: entry.description,
496
+ metadata: { ...entry.metadata }
497
+ });
498
+ }
499
+ return catalog;
500
+ }
501
+ /**
502
+ * Read values from memory by keys
503
+ */
504
+ async read(keys) {
505
+ const keysToRead = keys ?? Array.from(this.store.keys());
506
+ const snapshot = {};
507
+ for (const key of keysToRead) {
508
+ const entry = this.store.get(key);
509
+ if (entry) {
510
+ entry.metadata.accessCount += 1;
511
+ entry.metadata.updatedAt = Date.now();
512
+ snapshot[key] = entry.value;
513
+ }
514
+ }
515
+ return snapshot;
516
+ }
517
+ /**
518
+ * Write a value to memory with description
519
+ */
520
+ async write(key, value, description, metadata) {
521
+ const now = Date.now();
522
+ const existing = this.store.get(key);
523
+ const entry = {
524
+ key,
525
+ description: description ?? existing?.description ?? key,
526
+ value,
527
+ metadata: existing ? {
528
+ createdAt: existing.metadata.createdAt,
529
+ updatedAt: now,
530
+ accessCount: existing.metadata.accessCount,
531
+ ...metadata ?? {}
532
+ } : {
533
+ createdAt: now,
534
+ updatedAt: now,
535
+ accessCount: 0,
536
+ ...metadata ?? {}
537
+ }
538
+ };
539
+ this.store.set(key, entry);
540
+ return { ...entry, metadata: { ...entry.metadata } };
541
+ }
542
+ /**
543
+ * Delete a memory entry by key
544
+ */
545
+ async delete(key) {
546
+ return this.store.delete(key);
547
+ }
548
+ /**
549
+ * Clear all memory entries
550
+ */
551
+ async clear() {
552
+ const count = this.store.size;
553
+ this.store.clear();
554
+ return count;
555
+ }
556
+ /**
557
+ * Get the current size of the memory store
558
+ *
559
+ * @returns Number of entries in memory
560
+ */
561
+ get size() {
562
+ return this.store.size;
563
+ }
564
+ };
565
+ var createInMemoryStore = () => new InMemoryStore();
566
+
567
+ // src/base/agent.ts
568
+ var DEFAULT_MODEL = "gcp/gemini-flash-latest";
569
+ var BaseAgent = class {
570
+ /**
571
+ * Agent name
572
+ */
573
+ name;
574
+ /**
575
+ * Agent description
576
+ */
577
+ description;
578
+ /**
579
+ * Agent instructions
580
+ */
581
+ instructions;
582
+ /**
583
+ * Maximum iterations for the agent loop
584
+ */
585
+ maxIterations;
586
+ /**
587
+ * Model identifier
588
+ */
589
+ model;
590
+ /**
591
+ * Input validation schema
592
+ */
593
+ inputSchema;
594
+ /**
595
+ * Output validation schema
596
+ */
597
+ outputSchema;
598
+ /**
599
+ * Whether memory is enabled
600
+ */
601
+ enableMemory;
602
+ /**
603
+ * Memory instance for persistent storage (null if disabled or initialization failed)
604
+ */
605
+ memory;
606
+ /**
607
+ * Agent metadata
608
+ */
609
+ metadata;
610
+ /**
611
+ * Hook manager for lifecycle events
612
+ */
613
+ hooks;
614
+ /**
615
+ * Registry of available tools
616
+ */
617
+ tools;
618
+ /**
619
+ * Baseline tools registered directly on the agent
620
+ */
621
+ baseTools;
622
+ /**
623
+ * Registered tool providers that can expand into tools at runtime
624
+ */
625
+ toolProviders;
626
+ /**
627
+ * Active tools supplied by providers for the current execution
628
+ */
629
+ providerToolRegistry;
630
+ /**
631
+ * Opper client configuration
632
+ */
633
+ opperConfig;
634
+ constructor(config) {
635
+ this.name = config.name;
636
+ this.description = config.description;
637
+ this.instructions = config.instructions;
638
+ this.maxIterations = config.maxIterations ?? 25;
639
+ this.model = config.model ?? DEFAULT_MODEL;
640
+ this.inputSchema = config.inputSchema;
641
+ this.outputSchema = config.outputSchema;
642
+ this.enableMemory = config.enableMemory ?? false;
643
+ this.metadata = { ...config.metadata ?? {} };
644
+ this.hooks = new HookManager();
645
+ this.tools = /* @__PURE__ */ new Map();
646
+ this.baseTools = /* @__PURE__ */ new Map();
647
+ this.toolProviders = /* @__PURE__ */ new Set();
648
+ this.providerToolRegistry = /* @__PURE__ */ new Map();
649
+ this.opperConfig = {
650
+ apiKey: config.opperConfig?.apiKey ?? process.env["OPPER_API_KEY"],
651
+ baseUrl: config.opperConfig?.baseUrl,
652
+ ...config.opperConfig
653
+ };
654
+ this.memory = this.initializeMemory(config);
655
+ if (config.tools) {
656
+ this.registerTools(config.tools);
657
+ }
658
+ }
659
+ /**
660
+ * Initialize memory subsystem with graceful degradation
661
+ *
662
+ * @param config - Agent configuration
663
+ * @returns Memory instance or null
664
+ */
665
+ initializeMemory(config) {
666
+ if (!this.enableMemory && !config.memory) {
667
+ return null;
668
+ }
669
+ try {
670
+ return config.memory ?? new InMemoryStore();
671
+ } catch (error) {
672
+ console.warn(
673
+ `[${this.name}] Memory initialization failed, continuing without memory:`,
674
+ error
675
+ );
676
+ return null;
677
+ }
678
+ }
679
+ /**
680
+ * Main entry point for agent execution.
681
+ * Orchestrates lifecycle: context initialization, hook triggering, loop execution, teardown.
682
+ *
683
+ * @param input - Input to process
684
+ * @param parentSpanId - Optional parent span ID for tracing
685
+ * @returns Processed output
686
+ */
687
+ async process(input, parentSpanId) {
688
+ const validatedInput = this.inputSchema ? this.inputSchema.parse(input) : input;
689
+ const context = await this.initializeContext(validatedInput, parentSpanId);
690
+ try {
691
+ await this.activateToolProviders();
692
+ await this.triggerHook(HookEvents.AgentStart, { context });
693
+ const result = await this.runLoop(validatedInput, context);
694
+ const validatedOutput = this.outputSchema ? this.outputSchema.parse(result) : result;
695
+ await this.triggerHook(HookEvents.AgentEnd, {
696
+ context,
697
+ result: validatedOutput
698
+ });
699
+ return validatedOutput;
700
+ } catch (error) {
701
+ await this.triggerHook(HookEvents.AgentEnd, {
702
+ context,
703
+ error
704
+ });
705
+ throw error;
706
+ } finally {
707
+ await this.deactivateToolProviders();
708
+ await this.teardownContext(context);
709
+ }
710
+ }
711
+ /**
712
+ * Initialize agent context before execution.
713
+ * Can be overridden for custom initialization logic.
714
+ *
715
+ * @param input - Agent input
716
+ * @param parentSpanId - Optional parent span ID
717
+ * @returns Initialized context
718
+ */
719
+ async initializeContext(input, parentSpanId) {
720
+ const context = new (await Promise.resolve().then(() => (init_context(), context_exports))).AgentContext({
721
+ agentName: this.name,
722
+ parentSpanId: parentSpanId ?? null,
723
+ goal: input,
724
+ metadata: { ...this.metadata }
725
+ });
726
+ return context;
727
+ }
728
+ /**
729
+ * Teardown agent context after execution.
730
+ * Can be overridden for custom cleanup logic.
731
+ *
732
+ * @param context - Agent context to teardown
733
+ */
734
+ async teardownContext(context) {
735
+ }
736
+ /**
737
+ * Add a tool to the agent's tool registry.
738
+ *
739
+ * @param tool - Tool to add
740
+ */
741
+ addTool(tool2) {
742
+ const coerced = coerceToolDefinition(tool2);
743
+ if (this.tools.has(coerced.name)) {
744
+ console.warn(
745
+ `[${this.name}] Tool "${coerced.name}" already registered. Overwriting existing definition.`
746
+ );
747
+ }
748
+ const typedTool = coerced;
749
+ this.baseTools.set(coerced.name, typedTool);
750
+ this.tools.set(coerced.name, typedTool);
751
+ }
752
+ /**
753
+ * Remove a tool from the agent's tool registry.
754
+ *
755
+ * @param toolName - Name of tool to remove
756
+ * @returns True if tool was removed, false if not found
757
+ */
758
+ removeTool(toolName) {
759
+ this.baseTools.delete(toolName);
760
+ return this.tools.delete(toolName);
761
+ }
762
+ /**
763
+ * Get a tool by name.
764
+ *
765
+ * @param toolName - Name of tool to retrieve
766
+ * @returns Tool instance or undefined
767
+ */
768
+ getTool(toolName) {
769
+ return this.tools.get(toolName);
770
+ }
771
+ /**
772
+ * Get all registered tools.
773
+ *
774
+ * @returns Array of all tools
775
+ */
776
+ getTools() {
777
+ return Array.from(this.tools.values());
778
+ }
779
+ /**
780
+ * Register a tool provider that expands into tools at runtime.
781
+ *
782
+ * @param provider - Tool provider to add
783
+ */
784
+ addToolProvider(provider) {
785
+ this.toolProviders.add(provider);
786
+ }
787
+ /**
788
+ * Convert this agent into a tool that can be used by other agents.
789
+ *
790
+ * @param toolName - Optional custom name for the tool (defaults to agent name)
791
+ * @param toolDescription - Optional custom description (defaults to agent description)
792
+ * @returns Tool wrapping this agent
793
+ */
794
+ asTool(toolName, toolDescription) {
795
+ const tool2 = {
796
+ name: toolName ?? this.name,
797
+ description: toolDescription ?? this.description ?? `Agent: ${this.name}`,
798
+ ...this.inputSchema && { schema: this.inputSchema },
799
+ metadata: {
800
+ isAgent: true,
801
+ agentName: this.name
802
+ },
803
+ execute: async (input, executionContext) => {
804
+ try {
805
+ const parentSpanId = executionContext.spanId ?? executionContext.agentContext.parentSpanId ?? void 0;
806
+ const result = await this.process(input, parentSpanId);
807
+ return ToolResultFactory.success(tool2.name, result);
808
+ } catch (error) {
809
+ return ToolResultFactory.failure(
810
+ tool2.name,
811
+ error instanceof Error ? error : new Error(String(error))
812
+ );
813
+ }
814
+ }
815
+ };
816
+ return tool2;
817
+ }
818
+ /**
819
+ * Register a hook handler for a specific event.
820
+ *
821
+ * @param event - Hook event name
822
+ * @param handler - Hook handler function
823
+ * @returns Cleanup function to unregister the hook
824
+ */
825
+ registerHook(event, handler) {
826
+ return this.hooks.on(event, handler);
827
+ }
828
+ /**
829
+ * Trigger a hook event with a payload.
830
+ * Swallows errors to prevent hook failures from breaking agent execution.
831
+ *
832
+ * @param event - Hook event name
833
+ * @param payload - Event payload
834
+ */
835
+ async triggerHook(event, payload) {
836
+ try {
837
+ await this.hooks.emit(event, payload);
838
+ } catch (error) {
839
+ console.warn(`Hook error for event ${event}:`, error);
840
+ }
841
+ }
842
+ /**
843
+ * Execute a tool with proper context, hooks, and error handling.
844
+ *
845
+ * @param toolName - Name of tool to execute
846
+ * @param input - Tool input
847
+ * @param context - Agent context
848
+ * @param options - Optional execution options (signal, span ID)
849
+ * @returns Tool execution result
850
+ */
851
+ async executeTool(toolName, input, context, options) {
852
+ const tool2 = this.tools.get(toolName);
853
+ if (!tool2) {
854
+ const failure = ToolResultFactory.failure(
855
+ toolName,
856
+ new Error(`Tool "${toolName}" not found in agent registry`)
857
+ );
858
+ const timestamp = Date.now();
859
+ context.recordToolCall({
860
+ toolName,
861
+ input,
862
+ success: false,
863
+ error: failure.error instanceof Error ? failure.error.message : String(failure.error),
864
+ startedAt: timestamp,
865
+ finishedAt: timestamp,
866
+ metadata: {}
867
+ });
868
+ await this.triggerHook(HookEvents.ToolError, {
869
+ context,
870
+ toolName,
871
+ error: failure.error
872
+ });
873
+ return failure;
874
+ }
875
+ const executionContext = {
876
+ agentContext: context,
877
+ ...options?.signal && { signal: options.signal },
878
+ ...options?.spanId && { spanId: options.spanId },
879
+ metadata: {}
880
+ };
881
+ const startedAt = Date.now();
882
+ try {
883
+ await this.triggerHook(HookEvents.BeforeTool, {
884
+ context,
885
+ tool: tool2,
886
+ input
887
+ });
888
+ const result = await tool2.execute(input, executionContext);
889
+ const finishedAt = Date.now();
890
+ const record = context.recordToolCall({
891
+ toolName: tool2.name,
892
+ input,
893
+ ...result.success && { output: result.output },
894
+ success: result.success,
895
+ ...!result.success && {
896
+ error: result.error instanceof Error ? result.error.message : String(result.error)
897
+ },
898
+ startedAt,
899
+ finishedAt,
900
+ metadata: {}
901
+ });
902
+ await this.triggerHook(HookEvents.AfterTool, {
903
+ context,
904
+ tool: tool2,
905
+ result,
906
+ record
907
+ });
908
+ return result;
909
+ } catch (error) {
910
+ const failure = ToolResultFactory.failure(
911
+ toolName,
912
+ error instanceof Error ? error : new Error(String(error)),
913
+ {
914
+ startedAt,
915
+ finishedAt: Date.now()
916
+ }
917
+ );
918
+ const record = context.recordToolCall({
919
+ toolName: tool2.name,
920
+ input,
921
+ success: false,
922
+ error: failure.error instanceof Error ? failure.error.message : String(failure.error),
923
+ startedAt,
924
+ finishedAt: failure.finishedAt,
925
+ metadata: {}
926
+ });
927
+ await this.triggerHook(HookEvents.ToolError, {
928
+ context,
929
+ tool: tool2,
930
+ toolName: tool2.name,
931
+ error
932
+ });
933
+ await this.triggerHook(HookEvents.AfterTool, {
934
+ context,
935
+ tool: tool2,
936
+ result: failure,
937
+ record
938
+ });
939
+ return failure;
940
+ }
941
+ }
942
+ registerTools(entries) {
943
+ const { tools, providers } = normalizeToolEntries(entries);
944
+ for (const tool2 of tools) {
945
+ this.addTool(tool2);
946
+ }
947
+ for (const provider of providers) {
948
+ this.toolProviders.add(provider);
949
+ }
950
+ }
951
+ async activateToolProviders() {
952
+ const activationPromises = Array.from(this.toolProviders).filter((provider) => !this.providerToolRegistry.has(provider)).map(async (provider) => {
953
+ try {
954
+ const baseAgent = this;
955
+ const providedTools = await provider.setup(baseAgent) ?? [];
956
+ const normalizedTools = providedTools.map(
957
+ (tool2) => coerceToolDefinition(tool2)
958
+ );
959
+ this.providerToolRegistry.set(provider, normalizedTools);
960
+ for (const tool2 of normalizedTools) {
961
+ if (this.tools.has(tool2.name)) {
962
+ console.warn(
963
+ `[${this.name}] Tool "${tool2.name}" from provider overwrites existing tool.`
964
+ );
965
+ }
966
+ this.tools.set(tool2.name, tool2);
967
+ }
968
+ } catch (error) {
969
+ console.warn(
970
+ `[${this.name}] Failed to activate tool provider:`,
971
+ error
972
+ );
973
+ }
974
+ });
975
+ await Promise.allSettled(activationPromises);
976
+ }
977
+ async deactivateToolProviders() {
978
+ const teardownEntries = Array.from(this.providerToolRegistry.entries());
979
+ this.providerToolRegistry.clear();
980
+ const teardownPromises = teardownEntries.map(async ([provider, tools]) => {
981
+ for (const tool2 of tools) {
982
+ this.tools.delete(tool2.name);
983
+ const baseTool = this.baseTools.get(tool2.name);
984
+ if (baseTool) {
985
+ this.tools.set(tool2.name, baseTool);
986
+ }
987
+ }
988
+ try {
989
+ await provider.teardown();
990
+ } catch (error) {
991
+ console.warn(
992
+ `[${this.name}] Error while tearing down tool provider:`,
993
+ error
994
+ );
995
+ }
996
+ });
997
+ await Promise.allSettled(teardownPromises);
998
+ }
999
+ };
1000
+
1001
+ // src/index.ts
1002
+ init_context();
1003
+ init_result();
1004
+ init_tool();
1005
+ var ThoughtSchema = z.object({
1006
+ /**
1007
+ * The reasoning or internal monologue of the agent
1008
+ */
1009
+ reasoning: z.string(),
1010
+ /**
1011
+ * Planned next action(s)
1012
+ */
1013
+ plan: z.string().optional(),
1014
+ /**
1015
+ * Confidence level (0-1)
1016
+ */
1017
+ confidence: z.number().min(0).max(1).optional(),
1018
+ /**
1019
+ * Additional metadata
1020
+ */
1021
+ metadata: z.record(z.string(), z.unknown()).optional()
1022
+ });
1023
+ var ToolCallSchema = z.object({
1024
+ /**
1025
+ * Unique identifier for this tool call
1026
+ */
1027
+ id: z.string(),
1028
+ /**
1029
+ * Name of the tool to invoke
1030
+ */
1031
+ toolName: z.string(),
1032
+ /**
1033
+ * Arguments to pass to the tool
1034
+ */
1035
+ arguments: z.unknown()
1036
+ });
1037
+ var MemoryUpdateSchema = z.object({
1038
+ /**
1039
+ * Value to store for this key
1040
+ */
1041
+ value: z.unknown(),
1042
+ /**
1043
+ * Optional description of the memory entry
1044
+ */
1045
+ description: z.string().optional(),
1046
+ /**
1047
+ * Optional metadata for the memory entry
1048
+ */
1049
+ metadata: z.record(z.string(), z.unknown()).optional()
1050
+ });
1051
+ var AgentDecisionSchema = z.object({
1052
+ /**
1053
+ * Agent's internal reasoning
1054
+ */
1055
+ reasoning: z.string(),
1056
+ /**
1057
+ * Tool calls to execute (if any)
1058
+ * Empty array signals task completion
1059
+ */
1060
+ toolCalls: z.array(ToolCallSchema).default([]),
1061
+ /**
1062
+ * Memory keys to read during this iteration
1063
+ */
1064
+ memoryReads: z.array(z.string()).default([]),
1065
+ /**
1066
+ * Memory entries to write/update (key -> payload)
1067
+ */
1068
+ memoryUpdates: z.record(MemoryUpdateSchema).default({})
1069
+ });
1070
+ var ToolExecutionSummarySchema = z.object({
1071
+ /**
1072
+ * Tool name
1073
+ */
1074
+ toolName: z.string(),
1075
+ /**
1076
+ * Whether execution succeeded
1077
+ */
1078
+ success: z.boolean(),
1079
+ /**
1080
+ * Output if successful
1081
+ */
1082
+ output: z.unknown().optional(),
1083
+ /**
1084
+ * Error message if failed
1085
+ */
1086
+ error: z.string().optional()
1087
+ });
1088
+ var isPlainRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1089
+ var isZodSchema = (value) => typeof value === "object" && value !== null && "_def" in value && typeof value.parse === "function";
1090
+ var readTokenCount = (usage, key) => {
1091
+ if (!usage) {
1092
+ return 0;
1093
+ }
1094
+ const value = usage[key];
1095
+ return typeof value === "number" && Number.isFinite(value) ? value : 0;
1096
+ };
1097
+ var extractUsage = (response) => {
1098
+ const usageRecord = isPlainRecord(response.usage) ? response.usage : void 0;
1099
+ return {
1100
+ inputTokens: readTokenCount(usageRecord, "input_tokens"),
1101
+ outputTokens: readTokenCount(usageRecord, "output_tokens")
1102
+ };
1103
+ };
1104
+ var extractCost = (response) => {
1105
+ const costRecord = isPlainRecord(response.cost) ? response.cost : void 0;
1106
+ return {
1107
+ generation: readTokenCount(costRecord, "generation"),
1108
+ platform: readTokenCount(costRecord, "platform"),
1109
+ total: readTokenCount(costRecord, "total")
1110
+ };
1111
+ };
1112
+ var DEFAULT_RETRY_CONFIG = {
1113
+ maxRetries: 3,
1114
+ initialDelayMs: 1e3,
1115
+ backoffMultiplier: 2,
1116
+ maxDelayMs: 1e4
1117
+ };
1118
+ var OpperClient = class {
1119
+ client;
1120
+ logger;
1121
+ retryConfig;
1122
+ constructor(apiKey, options = {}) {
1123
+ this.client = new Opper({
1124
+ httpBearer: apiKey ?? process.env["OPPER_HTTP_BEARER"] ?? ""
1125
+ });
1126
+ this.logger = options.logger ?? getDefaultLogger();
1127
+ this.retryConfig = {
1128
+ ...DEFAULT_RETRY_CONFIG,
1129
+ ...options.retryConfig ?? {}
1130
+ };
1131
+ }
1132
+ /**
1133
+ * Make a call to Opper with retry logic
1134
+ */
1135
+ async call(options) {
1136
+ return this.withRetry(async () => {
1137
+ const inputSchema = this.toJsonSchema(options.inputSchema);
1138
+ const outputSchema = this.toJsonSchema(options.outputSchema);
1139
+ const response = await this.client.call({
1140
+ name: options.name,
1141
+ instructions: options.instructions,
1142
+ input: options.input,
1143
+ ...inputSchema && { inputSchema },
1144
+ ...outputSchema && { outputSchema },
1145
+ ...options.model && { model: options.model },
1146
+ ...options.parentSpanId && { parentSpanId: options.parentSpanId }
1147
+ });
1148
+ const usagePayload = extractUsage(response);
1149
+ const costPayload = extractCost(response);
1150
+ const usage = {
1151
+ inputTokens: usagePayload.inputTokens,
1152
+ outputTokens: usagePayload.outputTokens,
1153
+ totalTokens: usagePayload.inputTokens + usagePayload.outputTokens,
1154
+ cost: costPayload
1155
+ };
1156
+ const result = {
1157
+ ...response.jsonPayload !== void 0 && response.jsonPayload !== null ? { jsonPayload: response.jsonPayload } : {},
1158
+ ...response.message !== void 0 && response.message !== null ? { message: response.message } : {},
1159
+ spanId: response.spanId,
1160
+ usage
1161
+ };
1162
+ return result;
1163
+ }, options.name);
1164
+ }
1165
+ /**
1166
+ * Create a span for tracing
1167
+ */
1168
+ async createSpan(options) {
1169
+ return this.withRetry(async () => {
1170
+ const span = await this.client.spans.create({
1171
+ name: options.name,
1172
+ ...options.input !== void 0 && { input: options.input },
1173
+ ...options.parentSpanId && { parentId: options.parentSpanId }
1174
+ });
1175
+ return {
1176
+ id: span.id,
1177
+ name: options.name,
1178
+ input: options.input
1179
+ };
1180
+ }, `create-span:${options.name}`);
1181
+ }
1182
+ /**
1183
+ * Update a span with output or error
1184
+ */
1185
+ async updateSpan(spanId, output, options) {
1186
+ return this.withRetry(async () => {
1187
+ const serializedOutput = output !== void 0 && output !== null ? typeof output === "object" ? JSON.stringify(output) : String(output) : void 0;
1188
+ await this.client.spans.update(spanId, {
1189
+ ...serializedOutput !== void 0 && { output: serializedOutput },
1190
+ ...options?.error && { error: options.error }
1191
+ });
1192
+ }, `update-span:${spanId}`);
1193
+ }
1194
+ /**
1195
+ * Get the underlying Opper client
1196
+ */
1197
+ getClient() {
1198
+ return this.client;
1199
+ }
1200
+ /**
1201
+ * Execute a function with retry logic and exponential backoff
1202
+ */
1203
+ async withRetry(fn, operationName) {
1204
+ let lastError;
1205
+ let delay = this.retryConfig.initialDelayMs;
1206
+ for (let attempt = 0; attempt <= this.retryConfig.maxRetries; attempt++) {
1207
+ try {
1208
+ return await fn();
1209
+ } catch (error) {
1210
+ lastError = error instanceof Error ? error : new Error(String(error));
1211
+ if (attempt === this.retryConfig.maxRetries) {
1212
+ break;
1213
+ }
1214
+ if (!this.isRetryableError(error)) {
1215
+ throw lastError;
1216
+ }
1217
+ this.logger.warn(
1218
+ `Opper operation "${operationName}" failed (attempt ${attempt + 1}/${this.retryConfig.maxRetries + 1}), retrying in ${delay}ms`,
1219
+ {
1220
+ error: lastError.message,
1221
+ attempt: attempt + 1,
1222
+ delayMs: delay
1223
+ }
1224
+ );
1225
+ await this.sleep(delay);
1226
+ delay = Math.min(
1227
+ delay * this.retryConfig.backoffMultiplier,
1228
+ this.retryConfig.maxDelayMs
1229
+ );
1230
+ }
1231
+ }
1232
+ this.logger.error(
1233
+ `Opper operation "${operationName}" failed after ${this.retryConfig.maxRetries + 1} attempts`,
1234
+ lastError
1235
+ );
1236
+ throw lastError;
1237
+ }
1238
+ /**
1239
+ * Check if an error is retryable
1240
+ */
1241
+ isRetryableError(error) {
1242
+ if (!(error instanceof Error)) {
1243
+ return false;
1244
+ }
1245
+ const errorMessage = error.message.toLowerCase();
1246
+ const retryablePatterns = [
1247
+ "network",
1248
+ "timeout",
1249
+ "econnreset",
1250
+ "enotfound",
1251
+ "econnrefused",
1252
+ "etimedout",
1253
+ "rate limit",
1254
+ "429",
1255
+ "500",
1256
+ "502",
1257
+ "503",
1258
+ "504"
1259
+ ];
1260
+ return retryablePatterns.some((pattern) => errorMessage.includes(pattern));
1261
+ }
1262
+ /**
1263
+ * Convert Zod schema to JSON Schema
1264
+ */
1265
+ toJsonSchema(schema) {
1266
+ if (!schema) {
1267
+ return void 0;
1268
+ }
1269
+ if (isZodSchema(schema)) {
1270
+ try {
1271
+ return zodToJsonSchema(schema);
1272
+ } catch (error) {
1273
+ this.logger.warn("Failed to convert Zod schema to JSON Schema", {
1274
+ error: error instanceof Error ? error.message : String(error)
1275
+ });
1276
+ return void 0;
1277
+ }
1278
+ }
1279
+ if (isPlainRecord(schema)) {
1280
+ return schema;
1281
+ }
1282
+ this.logger.warn("Unsupported schema type provided to OpperClient", {
1283
+ schemaType: typeof schema
1284
+ });
1285
+ return void 0;
1286
+ }
1287
+ /**
1288
+ * Sleep for specified milliseconds
1289
+ */
1290
+ sleep(ms) {
1291
+ return new Promise((resolve) => setTimeout(resolve, ms));
1292
+ }
1293
+ };
1294
+ function createOpperClient(apiKey, options) {
1295
+ return new OpperClient(apiKey, options);
1296
+ }
1297
+ var SchemaValidationError = class extends Error {
1298
+ issues;
1299
+ constructor(message, issues) {
1300
+ super(message);
1301
+ this.name = "SchemaValidationError";
1302
+ this.issues = issues;
1303
+ }
1304
+ };
1305
+ var validateSchema = (schema, value, options = {}) => {
1306
+ const result = schema.safeParse(value);
1307
+ if (!result.success) {
1308
+ throw new SchemaValidationError(
1309
+ options.message ?? "Schema validation failed",
1310
+ result.error.issues
1311
+ );
1312
+ }
1313
+ return result.data;
1314
+ };
1315
+ var isSchemaValid = (schema, value) => {
1316
+ return schema.safeParse(value).success;
1317
+ };
1318
+ var schemaToJson = (schema, options = {}) => {
1319
+ return zodToJsonSchema(schema, options);
1320
+ };
1321
+ var getSchemaDefault = (schema) => {
1322
+ const result = schema.safeParse(void 0);
1323
+ return result.success ? result.data : void 0;
1324
+ };
1325
+ var mergeSchemaDefaults = (schema, value) => {
1326
+ const defaults = getSchemaDefault(schema);
1327
+ if (defaults && typeof defaults === "object") {
1328
+ return validateSchema(schema, { ...defaults, ...value });
1329
+ }
1330
+ return validateSchema(schema, value);
1331
+ };
1332
+
1333
+ // src/core/agent.ts
1334
+ var isToolSuccessResult = (value) => {
1335
+ if (typeof value !== "object" || value === null) {
1336
+ return false;
1337
+ }
1338
+ const candidate = value;
1339
+ return candidate.success === true && typeof candidate.toolName === "string" && "output" in candidate;
1340
+ };
1341
+ var Agent = class extends BaseAgent {
1342
+ opperClient;
1343
+ logger;
1344
+ verbose;
1345
+ constructor(config) {
1346
+ super(config);
1347
+ this.logger = config.logger ?? getDefaultLogger();
1348
+ this.verbose = config.verbose ?? false;
1349
+ if (this.verbose) {
1350
+ try {
1351
+ this.logger.setLevel?.(1 /* INFO */);
1352
+ } catch {
1353
+ }
1354
+ }
1355
+ this.opperClient = config.opperClient ?? new OpperClient(this.opperConfig.apiKey, {
1356
+ logger: this.logger
1357
+ });
1358
+ }
1359
+ /**
1360
+ * Serialize input for passing to LLM or spans
1361
+ */
1362
+ serializeInput(input) {
1363
+ if (typeof input === "string") {
1364
+ return input;
1365
+ }
1366
+ if (typeof input === "object" && input !== null) {
1367
+ return JSON.stringify(input);
1368
+ }
1369
+ return String(input);
1370
+ }
1371
+ /**
1372
+ * Main agent loop: think → tool execution → memory handling → repeat until complete
1373
+ */
1374
+ async runLoop(input, context) {
1375
+ this.log("Starting agent loop", {
1376
+ goal: input,
1377
+ maxIterations: this.maxIterations,
1378
+ tools: Array.from(this.tools.keys())
1379
+ });
1380
+ const parentSpan = await this.opperClient.createSpan({
1381
+ name: `${this.name}_execution`,
1382
+ input: this.serializeInput(input),
1383
+ ...context.parentSpanId ? { parentSpanId: context.parentSpanId } : {}
1384
+ });
1385
+ context.parentSpanId = parentSpan.id;
1386
+ try {
1387
+ while (context.iteration < this.maxIterations) {
1388
+ const currentIteration = context.iteration + 1;
1389
+ this.log(`Iteration ${currentIteration}/${this.maxIterations}`, {
1390
+ toolsAvailable: this.tools.size
1391
+ });
1392
+ await this.triggerHook(HookEvents.LoopStart, { context });
1393
+ let loopComplete = false;
1394
+ try {
1395
+ const { decision, spanId: thinkSpanId } = await this.think(
1396
+ input,
1397
+ context
1398
+ );
1399
+ const memoryResults = await this.handleMemoryActions(
1400
+ decision,
1401
+ context,
1402
+ thinkSpanId
1403
+ );
1404
+ const toolCallStartIndex = context.toolCalls.length;
1405
+ const toolResults = await this.executeToolCalls(
1406
+ decision,
1407
+ context,
1408
+ thinkSpanId
1409
+ );
1410
+ const combinedResults = [...memoryResults, ...toolResults];
1411
+ const newToolCalls = context.toolCalls.slice(toolCallStartIndex);
1412
+ context.addCycle({
1413
+ iteration: currentIteration,
1414
+ thought: {
1415
+ reasoning: decision.reasoning,
1416
+ memoryReads: decision.memoryReads,
1417
+ memoryUpdates: decision.memoryUpdates
1418
+ },
1419
+ toolCalls: newToolCalls,
1420
+ results: combinedResults,
1421
+ timestamp: Date.now()
1422
+ });
1423
+ context.iteration = currentIteration;
1424
+ const hasToolCalls = decision.toolCalls.length > 0;
1425
+ const hasMemoryReads = this.enableMemory && (decision.memoryReads?.length ?? 0) > 0;
1426
+ loopComplete = !hasToolCalls && !hasMemoryReads;
1427
+ } finally {
1428
+ await this.triggerHook(HookEvents.LoopEnd, { context });
1429
+ }
1430
+ if (loopComplete) {
1431
+ this.log("Loop complete, generating final result", {
1432
+ iteration: context.iteration + 1
1433
+ });
1434
+ break;
1435
+ }
1436
+ }
1437
+ if (context.iteration >= this.maxIterations) {
1438
+ throw new Error(
1439
+ `Agent exceeded maximum iterations (${this.maxIterations}) without completing the task`
1440
+ );
1441
+ }
1442
+ const result = await this.generateFinalResult(input, context);
1443
+ await this.opperClient.updateSpan(parentSpan.id, result);
1444
+ return result;
1445
+ } catch (error) {
1446
+ await this.opperClient.updateSpan(parentSpan.id, void 0, {
1447
+ error: error instanceof Error ? error.message : String(error)
1448
+ });
1449
+ throw error;
1450
+ }
1451
+ }
1452
+ /**
1453
+ * Think step: Call LLM to decide next action
1454
+ */
1455
+ async think(input, context) {
1456
+ const spanName = "think";
1457
+ this.log("Think step", { iteration: context.iteration });
1458
+ await this.triggerHook(HookEvents.LlmCall, { context, callType: "think" });
1459
+ try {
1460
+ const instructions = this.buildThinkInstructions();
1461
+ const thinkContext = await this.buildThinkContext(input, context);
1462
+ const response = await this.opperClient.call({
1463
+ name: spanName,
1464
+ instructions,
1465
+ input: thinkContext,
1466
+ outputSchema: AgentDecisionSchema,
1467
+ model: this.model,
1468
+ ...context.parentSpanId && { parentSpanId: context.parentSpanId }
1469
+ });
1470
+ context.updateUsage({
1471
+ requests: 1,
1472
+ inputTokens: response.usage.inputTokens,
1473
+ outputTokens: response.usage.outputTokens,
1474
+ totalTokens: response.usage.totalTokens,
1475
+ cost: response.usage.cost
1476
+ });
1477
+ const decision = AgentDecisionSchema.parse(response.jsonPayload);
1478
+ await this.triggerHook(HookEvents.LlmResponse, {
1479
+ context,
1480
+ callType: "think",
1481
+ response
1482
+ });
1483
+ await this.triggerHook(HookEvents.ThinkEnd, {
1484
+ context,
1485
+ thought: { reasoning: decision.reasoning }
1486
+ });
1487
+ this.log("Think result", {
1488
+ reasoning: decision.reasoning,
1489
+ toolCalls: decision.toolCalls.length,
1490
+ memoryReads: decision.memoryReads?.length ?? 0,
1491
+ memoryWrites: Object.keys(decision.memoryUpdates ?? {}).length
1492
+ });
1493
+ return { decision, spanId: response.spanId };
1494
+ } catch (error) {
1495
+ this.logger.error("Think step failed", error);
1496
+ throw new Error(
1497
+ `Think step failed: ${error instanceof Error ? error.message : String(error)}`
1498
+ );
1499
+ }
1500
+ }
1501
+ /**
1502
+ * Build static instructions for the think step
1503
+ */
1504
+ buildThinkInstructions() {
1505
+ let instructions = `You are in a Think-Act reasoning loop.
1506
+
1507
+ YOUR TASK:
1508
+ 1. Analyze the current situation
1509
+ 2. Decide if the goal is complete or more actions are needed
1510
+ 3. If more actions needed: specify tools to call
1511
+ 4. If goal complete: return empty tool_calls list
1512
+
1513
+ IMPORTANT:
1514
+ - Return empty toolCalls array when task is COMPLETE
1515
+ - Only use available tools
1516
+ - Provide clear reasoning for each decision`;
1517
+ if (this.enableMemory) {
1518
+ instructions += `
1519
+
1520
+ MEMORY SYSTEM:
1521
+ You have access to a persistent memory system that works across iterations.
1522
+
1523
+ Memory Operations:
1524
+ 1. READ: Add keys to memoryReads when you need to load existing entries
1525
+ 2. WRITE: Populate memoryUpdates with key-value pairs (with optional description/metadata)
1526
+ Example: memoryUpdates = { "favorite_color": { "value": "blue", "description": "User likes blue" } }
1527
+
1528
+ When to use memory:
1529
+ - Save important calculations, decisions, or user preferences
1530
+ - Load memory when you need information from earlier in the conversation
1531
+ - Use descriptive keys like "budget_total", "user_favorite_city", etc.
1532
+ - When a key appears in memory_catalog and you need its value, add it to memoryReads before continuing
1533
+
1534
+ The memory you write persists across all process() calls on this agent.`;
1535
+ }
1536
+ return instructions;
1537
+ }
1538
+ /**
1539
+ * Build dynamic context for the think step
1540
+ */
1541
+ async buildThinkContext(input, context) {
1542
+ const availableTools = Array.from(this.tools.values()).map((tool2) => ({
1543
+ name: tool2.name,
1544
+ description: tool2.description || "",
1545
+ // Convert Zod schema to JSON Schema for LLM consumption
1546
+ parameters: tool2.schema ? schemaToJson(tool2.schema) : {}
1547
+ }));
1548
+ const executionHistory = context.getLastNCycles(3).map((cycle) => {
1549
+ const thought = typeof cycle.thought === "object" && cycle.thought !== null ? cycle.thought["reasoning"] || "" : String(cycle.thought || "");
1550
+ const results = Array.isArray(cycle.results) ? cycle.results.map((r) => {
1551
+ const result = r;
1552
+ let actualOutput = result.output;
1553
+ if (result.success && isToolSuccessResult(actualOutput)) {
1554
+ actualOutput = actualOutput.output;
1555
+ }
1556
+ return {
1557
+ tool: result.toolName,
1558
+ success: result.success,
1559
+ // Serialize result properly - use JSON for objects, String for primitives
1560
+ result: result.success ? typeof actualOutput === "object" ? JSON.stringify(actualOutput) : String(actualOutput) : void 0,
1561
+ error: !result.success ? result.error : void 0
1562
+ };
1563
+ }) : [];
1564
+ return {
1565
+ iteration: cycle.iteration,
1566
+ thought,
1567
+ results
1568
+ };
1569
+ });
1570
+ let memoryCatalog = null;
1571
+ if (this.enableMemory && this.memory && await this.memory.hasEntries()) {
1572
+ memoryCatalog = await this.memory.listEntries();
1573
+ }
1574
+ const loadedMemory = context.metadata["current_memory"] ?? null;
1575
+ return {
1576
+ goal: this.serializeInput(input),
1577
+ agent_description: this.description || "",
1578
+ instructions: this.instructions || "No specific instructions.",
1579
+ available_tools: availableTools,
1580
+ execution_history: executionHistory,
1581
+ current_iteration: context.iteration + 1,
1582
+ max_iterations: this.maxIterations,
1583
+ memory_catalog: memoryCatalog,
1584
+ loaded_memory: loadedMemory
1585
+ };
1586
+ }
1587
+ /**
1588
+ * Execute all tool calls from a decision
1589
+ */
1590
+ async executeToolCalls(decision, context, parentSpanId) {
1591
+ if (decision.toolCalls.length === 0) {
1592
+ return [];
1593
+ }
1594
+ this.log(`Executing ${decision.toolCalls.length} tool call(s)`);
1595
+ const results = [];
1596
+ for (const toolCall of decision.toolCalls) {
1597
+ this.log(`Action: ${toolCall.toolName}`, {
1598
+ parameters: toolCall.arguments
1599
+ });
1600
+ const toolSpan = await this.opperClient.createSpan({
1601
+ name: `tool_${toolCall.toolName}`,
1602
+ input: toolCall.arguments,
1603
+ ...parentSpanId ? { parentSpanId } : context.parentSpanId ? { parentSpanId: context.parentSpanId } : {}
1604
+ });
1605
+ try {
1606
+ const result = await this.executeTool(
1607
+ toolCall.toolName,
1608
+ toolCall.arguments,
1609
+ context,
1610
+ { spanId: toolSpan.id }
1611
+ );
1612
+ if (result.success) {
1613
+ await this.opperClient.updateSpan(toolSpan.id, result.output);
1614
+ } else {
1615
+ await this.opperClient.updateSpan(toolSpan.id, void 0, {
1616
+ error: result.error instanceof Error ? result.error.message : String(result.error)
1617
+ });
1618
+ }
1619
+ const summary = {
1620
+ toolName: toolCall.toolName,
1621
+ success: result.success,
1622
+ ...result.success && { output: result.output },
1623
+ ...!result.success && {
1624
+ error: result.error instanceof Error ? result.error.message : String(result.error)
1625
+ }
1626
+ };
1627
+ results.push(ToolExecutionSummarySchema.parse(summary));
1628
+ this.log(
1629
+ `Tool ${toolCall.toolName} ${result.success ? "succeeded" : "failed"}`,
1630
+ {
1631
+ success: result.success
1632
+ }
1633
+ );
1634
+ } catch (error) {
1635
+ await this.opperClient.updateSpan(toolSpan.id, void 0, {
1636
+ error: error instanceof Error ? error.message : String(error)
1637
+ });
1638
+ const summary = {
1639
+ toolName: toolCall.toolName,
1640
+ success: false,
1641
+ error: error instanceof Error ? error.message : String(error)
1642
+ };
1643
+ results.push(ToolExecutionSummarySchema.parse(summary));
1644
+ this.logger.warn(`Tool ${toolCall.toolName} threw error`, {
1645
+ error: error instanceof Error ? error.message : String(error)
1646
+ });
1647
+ }
1648
+ }
1649
+ return results;
1650
+ }
1651
+ /**
1652
+ * Handle memory operations from a decision
1653
+ * Supports read and write operations with graceful degradation
1654
+ */
1655
+ async handleMemoryActions(decision, context, parentSpanId) {
1656
+ if (!this.enableMemory || !this.memory) {
1657
+ return [];
1658
+ }
1659
+ const hasReads = Array.isArray(decision.memoryReads) && decision.memoryReads.length > 0;
1660
+ const updateEntries = Object.entries(decision.memoryUpdates ?? {}).filter(
1661
+ ([key, update]) => typeof key === "string" && key.length > 0 && typeof update === "object" && update !== null && "value" in update
1662
+ );
1663
+ const hasWrites = updateEntries.length > 0;
1664
+ if (!hasReads && !hasWrites) {
1665
+ return [];
1666
+ }
1667
+ this.log("Handling memory operations", {
1668
+ reads: decision.memoryReads?.length ?? 0,
1669
+ writes: updateEntries.length
1670
+ });
1671
+ const spanParentId = parentSpanId ?? context.parentSpanId ?? void 0;
1672
+ const summaries = [];
1673
+ if (hasReads) {
1674
+ try {
1675
+ const keySet = new Set(
1676
+ decision.memoryReads.filter(
1677
+ (key) => typeof key === "string" && key.length > 0
1678
+ )
1679
+ );
1680
+ const keys = Array.from(keySet);
1681
+ if (keys.length > 0) {
1682
+ const memoryReadSpan = await this.opperClient.createSpan({
1683
+ name: "memory_read",
1684
+ input: keys,
1685
+ ...spanParentId && { parentSpanId: spanParentId }
1686
+ });
1687
+ const memoryData = await this.memory.read(keys);
1688
+ await this.opperClient.updateSpan(memoryReadSpan.id, memoryData);
1689
+ context.setMetadata("current_memory", memoryData);
1690
+ this.log(`Loaded ${Object.keys(memoryData).length} memory entries`, {
1691
+ keys
1692
+ });
1693
+ summaries.push(
1694
+ ToolExecutionSummarySchema.parse({
1695
+ toolName: "memory_read",
1696
+ success: true,
1697
+ output: { keys, data: memoryData }
1698
+ })
1699
+ );
1700
+ for (const key of keys) {
1701
+ await this.triggerHook(HookEvents.MemoryRead, {
1702
+ context,
1703
+ key,
1704
+ value: memoryData[key]
1705
+ });
1706
+ }
1707
+ }
1708
+ } catch (error) {
1709
+ await this.triggerHook(HookEvents.MemoryError, {
1710
+ context,
1711
+ operation: "read",
1712
+ error
1713
+ });
1714
+ this.logger.warn("Memory read failed", {
1715
+ error: error instanceof Error ? error.message : String(error)
1716
+ });
1717
+ summaries.push(
1718
+ ToolExecutionSummarySchema.parse({
1719
+ toolName: "memory_read",
1720
+ success: false,
1721
+ error: error instanceof Error ? error.message : String(error)
1722
+ })
1723
+ );
1724
+ }
1725
+ }
1726
+ if (hasWrites) {
1727
+ try {
1728
+ const memoryWriteSpan = await this.opperClient.createSpan({
1729
+ name: "memory_write",
1730
+ input: updateEntries.map(([key]) => key),
1731
+ ...spanParentId && { parentSpanId: spanParentId }
1732
+ });
1733
+ for (const [key, update] of updateEntries) {
1734
+ const castUpdate = update;
1735
+ await this.memory.write(
1736
+ key,
1737
+ castUpdate.value,
1738
+ castUpdate.description ?? key,
1739
+ castUpdate.metadata
1740
+ );
1741
+ await this.triggerHook(HookEvents.MemoryWrite, {
1742
+ context,
1743
+ key,
1744
+ value: castUpdate.value
1745
+ });
1746
+ }
1747
+ await this.opperClient.updateSpan(
1748
+ memoryWriteSpan.id,
1749
+ `Successfully wrote ${updateEntries.length} keys`
1750
+ );
1751
+ this.log(`Wrote ${updateEntries.length} memory entries`);
1752
+ summaries.push(
1753
+ ToolExecutionSummarySchema.parse({
1754
+ toolName: "memory_write",
1755
+ success: true,
1756
+ output: {
1757
+ keys: updateEntries.map(([key]) => key)
1758
+ }
1759
+ })
1760
+ );
1761
+ } catch (error) {
1762
+ await this.triggerHook(HookEvents.MemoryError, {
1763
+ context,
1764
+ operation: "write",
1765
+ error
1766
+ });
1767
+ this.logger.warn("Memory write failed", {
1768
+ error: error instanceof Error ? error.message : String(error)
1769
+ });
1770
+ summaries.push(
1771
+ ToolExecutionSummarySchema.parse({
1772
+ toolName: "memory_write",
1773
+ success: false,
1774
+ error: error instanceof Error ? error.message : String(error)
1775
+ })
1776
+ );
1777
+ }
1778
+ }
1779
+ return summaries;
1780
+ }
1781
+ /**
1782
+ * Generate final result based on execution history
1783
+ */
1784
+ async generateFinalResult(input, context) {
1785
+ this.log("Generating final result", { totalIterations: context.iteration });
1786
+ const finalContext = {
1787
+ goal: this.serializeInput(input),
1788
+ instructions: this.instructions || "No specific instructions.",
1789
+ execution_history: context.executionHistory.map((cycle) => {
1790
+ const results = Array.isArray(cycle.results) ? cycle.results : [];
1791
+ return {
1792
+ iteration: cycle.iteration,
1793
+ actions_taken: results.map(
1794
+ (r) => r.toolName
1795
+ ),
1796
+ results: results.filter((r) => r.success).map((r) => {
1797
+ const result = r;
1798
+ let actualOutput = result.output;
1799
+ if (isToolSuccessResult(actualOutput)) {
1800
+ actualOutput = actualOutput.output;
1801
+ }
1802
+ return {
1803
+ tool: result.toolName,
1804
+ // Serialize result properly - use JSON for objects, String for primitives
1805
+ result: typeof actualOutput === "object" ? JSON.stringify(actualOutput) : String(actualOutput)
1806
+ };
1807
+ })
1808
+ };
1809
+ }),
1810
+ total_iterations: context.iteration
1811
+ };
1812
+ const instructions = `Generate the final result based on the execution history.
1813
+ Follow any instructions provided for formatting and style.`;
1814
+ try {
1815
+ const callOptions = {
1816
+ name: "generate_final_result",
1817
+ instructions,
1818
+ input: finalContext,
1819
+ model: this.model
1820
+ };
1821
+ if (context.parentSpanId) {
1822
+ callOptions.parentSpanId = context.parentSpanId;
1823
+ }
1824
+ if (this.outputSchema) {
1825
+ callOptions.outputSchema = this.outputSchema;
1826
+ }
1827
+ const response = await this.opperClient.call(callOptions);
1828
+ context.updateUsage({
1829
+ requests: 1,
1830
+ inputTokens: response.usage.inputTokens,
1831
+ outputTokens: response.usage.outputTokens,
1832
+ totalTokens: response.usage.totalTokens,
1833
+ cost: response.usage.cost
1834
+ });
1835
+ if (this.outputSchema) {
1836
+ const parsed = this.outputSchema.parse(response.jsonPayload);
1837
+ this.log("Final result generated (schema-validated)");
1838
+ return parsed;
1839
+ }
1840
+ this.log("Final result generated");
1841
+ return response.message;
1842
+ } catch (error) {
1843
+ this.logger.error("Failed to generate final result", error);
1844
+ throw new Error(
1845
+ `Failed to generate final result: ${error instanceof Error ? error.message : String(error)}`
1846
+ );
1847
+ }
1848
+ }
1849
+ /**
1850
+ * Log helper
1851
+ */
1852
+ log(message, data) {
1853
+ if (this.verbose) {
1854
+ this.logger.info(`[${this.name}] ${message}`, data);
1855
+ }
1856
+ }
1857
+ };
1858
+ var DEFAULT_CLIENT_INFO = {
1859
+ name: "opper-agent-ts-mcp-client",
1860
+ version: "0.0.0"
1861
+ };
1862
+ var MCPClient = class _MCPClient {
1863
+ config;
1864
+ client;
1865
+ transport;
1866
+ connected;
1867
+ toolCache;
1868
+ constructor(config, options = {}) {
1869
+ this.config = config;
1870
+ this.client = new Client(options.clientInfo ?? DEFAULT_CLIENT_INFO, {
1871
+ enforceStrictCapabilities: false
1872
+ });
1873
+ this.transport = null;
1874
+ this.connected = false;
1875
+ this.toolCache = null;
1876
+ }
1877
+ static fromConfig(config, options) {
1878
+ return new _MCPClient(config, options);
1879
+ }
1880
+ async connect() {
1881
+ if (this.connected) {
1882
+ return;
1883
+ }
1884
+ const transport = this.createTransport();
1885
+ this.transport = transport;
1886
+ try {
1887
+ await this.client.connect(transport);
1888
+ this.connected = true;
1889
+ } catch (error) {
1890
+ await transport.close().catch(() => {
1891
+ });
1892
+ this.transport = null;
1893
+ throw error;
1894
+ }
1895
+ }
1896
+ async disconnect() {
1897
+ if (!this.connected) {
1898
+ return;
1899
+ }
1900
+ await this.client.close().catch(() => {
1901
+ });
1902
+ await this.transport?.close().catch(() => {
1903
+ });
1904
+ this.transport = null;
1905
+ this.connected = false;
1906
+ this.toolCache = null;
1907
+ }
1908
+ async listTools() {
1909
+ const session = this.ensureConnected();
1910
+ if (this.toolCache) {
1911
+ return this.toolCache;
1912
+ }
1913
+ const response = await session.listTools({});
1914
+ const tools = response.tools?.map((tool2) => {
1915
+ const parameters = tool2.inputSchema ?? {};
1916
+ const outputSchema = tool2["outputSchema"];
1917
+ const normalized = {
1918
+ name: tool2.name,
1919
+ description: tool2.description ?? "",
1920
+ parameters
1921
+ };
1922
+ if (outputSchema) {
1923
+ normalized.outputSchema = outputSchema;
1924
+ }
1925
+ return normalized;
1926
+ }) ?? [];
1927
+ this.toolCache = tools;
1928
+ return tools;
1929
+ }
1930
+ async callTool(toolName, args) {
1931
+ const session = this.ensureConnected();
1932
+ const response = await session.callTool({
1933
+ name: toolName,
1934
+ arguments: args
1935
+ });
1936
+ return response;
1937
+ }
1938
+ get isConnected() {
1939
+ return this.connected;
1940
+ }
1941
+ ensureConnected() {
1942
+ if (!this.connected) {
1943
+ throw new Error(`MCP server "${this.config.name}" is not connected`);
1944
+ }
1945
+ return this.client;
1946
+ }
1947
+ createTransport() {
1948
+ if (this.config.transport === "stdio") {
1949
+ const stdioOptions = {
1950
+ command: this.config.command
1951
+ };
1952
+ if (this.config.args.length > 0) {
1953
+ stdioOptions.args = this.config.args;
1954
+ }
1955
+ if (Object.keys(this.config.env).length > 0) {
1956
+ stdioOptions.env = this.config.env;
1957
+ }
1958
+ if (this.config.cwd) {
1959
+ stdioOptions.cwd = this.config.cwd;
1960
+ }
1961
+ if (this.config.stderr) {
1962
+ stdioOptions.stderr = this.config.stderr;
1963
+ }
1964
+ return new StdioClientTransport(stdioOptions);
1965
+ }
1966
+ if (this.config.transport === "streamable-http") {
1967
+ const options2 = {};
1968
+ const headers2 = this.config.headers;
1969
+ if (headers2 && Object.keys(headers2).length > 0) {
1970
+ options2.requestInit = { headers: headers2 };
1971
+ }
1972
+ if (this.config.sessionId) {
1973
+ options2.sessionId = this.config.sessionId;
1974
+ }
1975
+ return new StreamableHTTPClientTransport(
1976
+ new URL(this.config.url),
1977
+ options2
1978
+ );
1979
+ }
1980
+ const headers = this.config.headers ?? {};
1981
+ const headerEntries = Object.entries(headers);
1982
+ const options = {};
1983
+ if (headerEntries.length > 0) {
1984
+ options.eventSourceInit = {
1985
+ fetch: async (url, init) => {
1986
+ const mergedHeaders = new Headers(init?.headers ?? {});
1987
+ for (const [key, value] of headerEntries) {
1988
+ mergedHeaders.set(key, value);
1989
+ }
1990
+ if (!mergedHeaders.has("Accept")) {
1991
+ mergedHeaders.set("Accept", "text/event-stream");
1992
+ }
1993
+ return fetch(url, {
1994
+ ...init,
1995
+ headers: mergedHeaders
1996
+ });
1997
+ }
1998
+ };
1999
+ }
2000
+ if (headerEntries.length > 0 || this.config.method === "POST") {
2001
+ options.requestInit = {
2002
+ ...this.config.method ? { method: this.config.method } : {},
2003
+ ...headerEntries.length > 0 ? { headers } : {}
2004
+ };
2005
+ }
2006
+ return new SSEClientTransport(new URL(this.config.url), options);
2007
+ }
2008
+ };
2009
+ var MCPTransportSchema = z.enum(["stdio", "http-sse", "streamable-http"]);
2010
+ var MCPBaseConfigSchema = z.object({
2011
+ name: z.string().min(1, "name is required"),
2012
+ transport: MCPTransportSchema,
2013
+ timeout: z.number().positive("timeout must be positive").default(30),
2014
+ metadata: z.record(z.string(), z.unknown()).default({})
2015
+ });
2016
+ var MCPStdIoConfigSchema = MCPBaseConfigSchema.extend({
2017
+ transport: z.literal("stdio"),
2018
+ command: z.string().min(1, "command is required for stdio transport"),
2019
+ args: z.array(z.string()).default([]),
2020
+ env: z.record(z.string(), z.string()).default({}),
2021
+ cwd: z.string().optional(),
2022
+ stderr: z.union([z.literal("inherit"), z.literal("pipe"), z.literal("ignore")]).optional()
2023
+ });
2024
+ var MCPHttpSseConfigSchema = MCPBaseConfigSchema.extend({
2025
+ transport: z.literal("http-sse"),
2026
+ url: z.string().url("url must be a valid HTTP(S) URL"),
2027
+ headers: z.record(z.string(), z.string()).default({}),
2028
+ method: z.enum(["GET", "POST"]).default("GET")
2029
+ });
2030
+ var MCPStreamableHttpConfigSchema = MCPBaseConfigSchema.extend({
2031
+ transport: z.literal("streamable-http"),
2032
+ url: z.string().url("url must be a valid HTTP(S) URL"),
2033
+ headers: z.record(z.string(), z.string()).default({}),
2034
+ sessionId: z.string().optional()
2035
+ });
2036
+ var MCPConfigVariants = z.discriminatedUnion("transport", [
2037
+ MCPStdIoConfigSchema,
2038
+ MCPHttpSseConfigSchema,
2039
+ MCPStreamableHttpConfigSchema
2040
+ ]);
2041
+ var MCPServerConfigSchema = MCPConfigVariants.superRefine(
2042
+ (value, ctx) => {
2043
+ if (value.transport === "http-sse" || value.transport === "streamable-http") {
2044
+ if (!value.url) {
2045
+ ctx.addIssue({
2046
+ code: z.ZodIssueCode.custom,
2047
+ message: `url is required for ${value.transport} transport`,
2048
+ path: ["url"]
2049
+ });
2050
+ } else if (!value.url.startsWith("http://") && !value.url.startsWith("https://")) {
2051
+ ctx.addIssue({
2052
+ code: z.ZodIssueCode.custom,
2053
+ message: "url must start with http:// or https://",
2054
+ path: ["url"]
2055
+ });
2056
+ }
2057
+ }
2058
+ }
2059
+ );
2060
+ var MCPconfig = (config) => MCPServerConfigSchema.parse(config);
2061
+ var createMCPServerConfig = MCPconfig;
2062
+
2063
+ // src/mcp/provider.ts
2064
+ init_tool();
2065
+ var isRecord = (value) => typeof value === "object" && value !== null;
2066
+ var MCPToolProvider = class {
2067
+ configs;
2068
+ namePrefix;
2069
+ clientOptions;
2070
+ clientFactory;
2071
+ logger;
2072
+ clients;
2073
+ constructor(configs, options = {}) {
2074
+ if (configs.length === 0) {
2075
+ throw new Error(
2076
+ "MCPToolProvider requires at least one server configuration"
2077
+ );
2078
+ }
2079
+ this.configs = configs;
2080
+ this.namePrefix = options.namePrefix ?? void 0;
2081
+ this.clientOptions = options.clientOptions ?? void 0;
2082
+ this.clientFactory = options.clientFactory ?? ((config, factoryOptions) => MCPClient.fromConfig(config, factoryOptions));
2083
+ this.logger = options.logger ?? {
2084
+ warn: (message, context) => console.warn(`[MCPToolProvider] ${message}`, context),
2085
+ error: (message, context) => console.error(`[MCPToolProvider] ${message}`, context),
2086
+ debug: () => {
2087
+ }
2088
+ };
2089
+ this.clients = /* @__PURE__ */ new Map();
2090
+ }
2091
+ async setup(agent) {
2092
+ const tools = [];
2093
+ for (const config of this.configs) {
2094
+ let client = null;
2095
+ try {
2096
+ client = this.clientFactory(config, this.clientOptions);
2097
+ await client.connect();
2098
+ this.clients.set(config.name, client);
2099
+ const mcpTools = await client.listTools();
2100
+ for (const mcpTool of mcpTools) {
2101
+ tools.push(this.wrapTool(config, client, mcpTool));
2102
+ }
2103
+ this.logger?.debug?.("Registered MCP server tools", {
2104
+ server: config.name,
2105
+ toolCount: mcpTools.length
2106
+ });
2107
+ } catch (error) {
2108
+ this.logger?.warn?.("Failed to initialize MCP server", {
2109
+ server: config.name,
2110
+ error: error instanceof Error ? error.message : String(error)
2111
+ });
2112
+ if (client) {
2113
+ await client.disconnect().catch(() => {
2114
+ });
2115
+ this.clients.delete(config.name);
2116
+ }
2117
+ }
2118
+ }
2119
+ return tools;
2120
+ }
2121
+ async teardown() {
2122
+ const disconnects = Array.from(this.clients.entries()).map(
2123
+ async ([serverName, client]) => {
2124
+ try {
2125
+ await client.disconnect();
2126
+ this.logger?.debug?.("Disconnected MCP server", {
2127
+ server: serverName
2128
+ });
2129
+ } catch (error) {
2130
+ this.logger?.warn?.("Error disconnecting MCP server", {
2131
+ server: serverName,
2132
+ error: error instanceof Error ? error.message : String(error)
2133
+ });
2134
+ } finally {
2135
+ this.clients.delete(serverName);
2136
+ }
2137
+ }
2138
+ );
2139
+ await Promise.allSettled(disconnects);
2140
+ }
2141
+ wrapTool(config, client, mcpTool) {
2142
+ const prefix = this.namePrefix ?? config.name;
2143
+ const toolName = `${prefix}:${mcpTool.name}`;
2144
+ return {
2145
+ name: toolName,
2146
+ description: mcpTool.description,
2147
+ metadata: {
2148
+ provider: "mcp",
2149
+ server: config.name,
2150
+ originalToolName: mcpTool.name,
2151
+ parameters: mcpTool.parameters,
2152
+ outputSchema: mcpTool.outputSchema
2153
+ },
2154
+ execute: async (input, context) => {
2155
+ const startedAt = Date.now();
2156
+ const args = isRecord(input) ? input : input === void 0 ? {} : { value: input };
2157
+ try {
2158
+ const result = await client.callTool(mcpTool.name, args);
2159
+ return ToolResultFactory.success(toolName, result, {
2160
+ metadata: {
2161
+ provider: "mcp",
2162
+ server: config.name,
2163
+ tool: mcpTool.name
2164
+ },
2165
+ startedAt,
2166
+ finishedAt: Date.now()
2167
+ });
2168
+ } catch (error) {
2169
+ const failure = error instanceof Error ? error : new Error(String(error));
2170
+ return ToolResultFactory.failure(toolName, failure, {
2171
+ metadata: {
2172
+ provider: "mcp",
2173
+ server: config.name,
2174
+ tool: mcpTool.name
2175
+ },
2176
+ startedAt,
2177
+ finishedAt: Date.now()
2178
+ });
2179
+ }
2180
+ }
2181
+ };
2182
+ }
2183
+ };
2184
+ var mcp = (...configs) => {
2185
+ if (configs.length === 0) {
2186
+ throw new Error("At least one MCP server configuration is required");
2187
+ }
2188
+ const parsedConfigs = configs.map(
2189
+ (config) => MCPServerConfigSchema.parse(config)
2190
+ );
2191
+ return new MCPToolProvider(parsedConfigs);
2192
+ };
2193
+
2194
+ // src/utils/tool-decorators.ts
2195
+ init_tool();
2196
+ var reflectWithMetadata = Reflect;
2197
+ var ReflectMetadata = {
2198
+ define: (metadataKey, metadataValue, target, propertyKey) => {
2199
+ if (typeof reflectWithMetadata.defineMetadata === "function") {
2200
+ reflectWithMetadata.defineMetadata(
2201
+ metadataKey,
2202
+ metadataValue,
2203
+ target,
2204
+ propertyKey
2205
+ );
2206
+ }
2207
+ },
2208
+ get: (metadataKey, target, propertyKey) => {
2209
+ if (typeof reflectWithMetadata.getMetadata === "function") {
2210
+ return reflectWithMetadata.getMetadata(metadataKey, target, propertyKey);
2211
+ }
2212
+ return void 0;
2213
+ }
2214
+ };
2215
+ var TOOL_METADATA_KEY = "opper:tool";
2216
+ var toolMetadataStore = /* @__PURE__ */ new WeakMap();
2217
+ function setToolMetadata(target, propertyKey, metadata) {
2218
+ ReflectMetadata.define(TOOL_METADATA_KEY, metadata, target, propertyKey);
2219
+ let metadataForTarget = toolMetadataStore.get(target);
2220
+ if (!metadataForTarget) {
2221
+ metadataForTarget = /* @__PURE__ */ new Map();
2222
+ toolMetadataStore.set(target, metadataForTarget);
2223
+ }
2224
+ metadataForTarget.set(
2225
+ propertyKey,
2226
+ metadata
2227
+ );
2228
+ }
2229
+ function getToolMetadata(target, propertyKey) {
2230
+ const metadata = ReflectMetadata.get(
2231
+ TOOL_METADATA_KEY,
2232
+ target,
2233
+ propertyKey
2234
+ );
2235
+ if (metadata) {
2236
+ return metadata;
2237
+ }
2238
+ const metadataForTarget = toolMetadataStore.get(target);
2239
+ return metadataForTarget?.get(propertyKey);
2240
+ }
2241
+ function isStage3DecoratorContext(value) {
2242
+ return typeof value === "object" && value !== null && "kind" in value;
2243
+ }
2244
+ function createExecuteWrapper(toolName, method, options, callTargetRef) {
2245
+ return async (input, context) => {
2246
+ const startedAt = Date.now();
2247
+ try {
2248
+ if (options.schema) {
2249
+ options.schema.parse(input);
2250
+ }
2251
+ const invocationTarget = callTargetRef.current;
2252
+ const result = await Promise.resolve(
2253
+ method.call(invocationTarget, input, context)
2254
+ );
2255
+ return ToolResultFactory.success(toolName, result, {
2256
+ startedAt,
2257
+ finishedAt: Date.now(),
2258
+ ...options.metadata && { metadata: options.metadata }
2259
+ });
2260
+ } catch (error) {
2261
+ return ToolResultFactory.failure(
2262
+ toolName,
2263
+ error instanceof Error ? error : new Error(String(error)),
2264
+ {
2265
+ startedAt,
2266
+ finishedAt: Date.now(),
2267
+ ...options.metadata && { metadata: options.metadata }
2268
+ }
2269
+ );
2270
+ }
2271
+ };
2272
+ }
2273
+ function normalizePropertyKey(propertyKey) {
2274
+ return typeof propertyKey === "symbol" ? propertyKey.description ?? propertyKey.toString() : propertyKey;
2275
+ }
2276
+ function createToolDefinition(options, methodName, propertyKey, method, callTargetRef) {
2277
+ const name = options.name ?? methodName;
2278
+ const description = options.description ?? extractJSDocDescription(method) ?? `Tool: ${normalizePropertyKey(propertyKey)}`;
2279
+ return {
2280
+ name,
2281
+ description,
2282
+ ...options.schema && { schema: options.schema },
2283
+ ...options.timeoutMs !== void 0 && { timeoutMs: options.timeoutMs },
2284
+ metadata: {
2285
+ ...options.metadata,
2286
+ isDecorated: true,
2287
+ propertyKey: normalizePropertyKey(propertyKey)
2288
+ },
2289
+ execute: createExecuteWrapper(name, method, options, callTargetRef)
2290
+ };
2291
+ }
2292
+ function createFunctionTool(fn, options = {}) {
2293
+ const name = options.name ?? (fn.name || "anonymous_tool");
2294
+ const description = options.description ?? extractJSDocDescription(fn) ?? `Tool: ${name}`;
2295
+ const tool2 = {
2296
+ name,
2297
+ description,
2298
+ ...options.schema && { schema: options.schema },
2299
+ ...options.timeoutMs !== void 0 && { timeoutMs: options.timeoutMs },
2300
+ metadata: {
2301
+ ...options.metadata,
2302
+ isFunction: true,
2303
+ functionName: fn.name
2304
+ },
2305
+ execute: async (input, context) => {
2306
+ const startedAt = Date.now();
2307
+ try {
2308
+ if (options.schema) {
2309
+ options.schema.parse(input);
2310
+ }
2311
+ let result;
2312
+ if (options.timeoutMs !== void 0) {
2313
+ result = await executeWithTimeout(
2314
+ fn(input, context),
2315
+ options.timeoutMs,
2316
+ name
2317
+ );
2318
+ } else {
2319
+ result = await Promise.resolve(fn(input, context));
2320
+ }
2321
+ return ToolResultFactory.success(name, result, {
2322
+ startedAt,
2323
+ finishedAt: Date.now(),
2324
+ ...options.metadata && { metadata: options.metadata }
2325
+ });
2326
+ } catch (error) {
2327
+ return ToolResultFactory.failure(
2328
+ name,
2329
+ error instanceof Error ? error : new Error(String(error)),
2330
+ {
2331
+ startedAt,
2332
+ finishedAt: Date.now(),
2333
+ ...options.metadata && { metadata: options.metadata }
2334
+ }
2335
+ );
2336
+ }
2337
+ }
2338
+ };
2339
+ return tool2;
2340
+ }
2341
+ function tool(options) {
2342
+ const decoratorOptions = options ?? {};
2343
+ function decorator(...decoratorArgs) {
2344
+ if (decoratorArgs.length === 2 && isStage3DecoratorContext(decoratorArgs[1])) {
2345
+ const [method, context] = decoratorArgs;
2346
+ if (context.kind !== "method") {
2347
+ throw new Error("@tool can only be applied to methods.");
2348
+ }
2349
+ if (context.name === void 0) {
2350
+ throw new Error("@tool requires a named method to attach metadata.");
2351
+ }
2352
+ const propertyKey2 = context.name;
2353
+ const inferredName = typeof propertyKey2 === "string" && propertyKey2.length > 0 ? propertyKey2 : method.name || "anonymous_tool";
2354
+ const callTargetRef2 = {};
2355
+ const toolMetadata2 = createToolDefinition(
2356
+ decoratorOptions,
2357
+ inferredName,
2358
+ propertyKey2,
2359
+ method,
2360
+ callTargetRef2
2361
+ );
2362
+ const registerMetadata = (target2) => {
2363
+ callTargetRef2.current = target2;
2364
+ setToolMetadata(target2, propertyKey2, toolMetadata2);
2365
+ };
2366
+ if (typeof context.addInitializer === "function") {
2367
+ context.addInitializer(function() {
2368
+ const target2 = context.static ? this : Object.getPrototypeOf(this);
2369
+ if (target2) {
2370
+ registerMetadata(target2);
2371
+ }
2372
+ });
2373
+ }
2374
+ return;
2375
+ }
2376
+ const [target, propertyKey, descriptor] = decoratorArgs;
2377
+ const resolvedDescriptor = descriptor ?? Object.getOwnPropertyDescriptor(target, propertyKey) ?? (() => {
2378
+ const value = target[propertyKey];
2379
+ if (typeof value === "function") {
2380
+ return { value };
2381
+ }
2382
+ return void 0;
2383
+ })();
2384
+ if (!resolvedDescriptor || typeof resolvedDescriptor.value !== "function") {
2385
+ throw new Error(
2386
+ `@tool can only be applied to methods, not ${typeof resolvedDescriptor?.value}`
2387
+ );
2388
+ }
2389
+ const originalMethod = resolvedDescriptor.value;
2390
+ const callTargetRef = { current: target };
2391
+ const toolMetadata = createToolDefinition(
2392
+ decoratorOptions,
2393
+ normalizePropertyKey(propertyKey),
2394
+ propertyKey,
2395
+ originalMethod,
2396
+ callTargetRef
2397
+ );
2398
+ setToolMetadata(target, propertyKey, toolMetadata);
2399
+ return resolvedDescriptor;
2400
+ }
2401
+ return decorator;
2402
+ }
2403
+ function extractTools(instance) {
2404
+ const tools = [];
2405
+ const prototype = Object.getPrototypeOf(instance);
2406
+ const propertyKeys = [
2407
+ ...Object.getOwnPropertyNames(prototype),
2408
+ ...Object.getOwnPropertySymbols(prototype)
2409
+ ];
2410
+ for (const propertyKey of propertyKeys) {
2411
+ if (propertyKey === "constructor") continue;
2412
+ const toolMetadata = getToolMetadata(prototype, propertyKey);
2413
+ if (toolMetadata) {
2414
+ const instanceMethod = instance[propertyKey];
2415
+ if (typeof instanceMethod !== "function") {
2416
+ continue;
2417
+ }
2418
+ const tool2 = {
2419
+ ...toolMetadata,
2420
+ execute: async (input, context) => {
2421
+ const startedAt = Date.now();
2422
+ try {
2423
+ if (toolMetadata.schema) {
2424
+ toolMetadata.schema.parse(input);
2425
+ }
2426
+ const result = await Promise.resolve(
2427
+ instanceMethod.call(instance, input, context)
2428
+ );
2429
+ return ToolResultFactory.success(toolMetadata.name, result, {
2430
+ startedAt,
2431
+ finishedAt: Date.now(),
2432
+ ...toolMetadata.metadata && { metadata: toolMetadata.metadata }
2433
+ });
2434
+ } catch (error) {
2435
+ return ToolResultFactory.failure(
2436
+ toolMetadata.name,
2437
+ error instanceof Error ? error : new Error(String(error)),
2438
+ {
2439
+ startedAt,
2440
+ finishedAt: Date.now(),
2441
+ ...toolMetadata.metadata && {
2442
+ metadata: toolMetadata.metadata
2443
+ }
2444
+ }
2445
+ );
2446
+ }
2447
+ }
2448
+ };
2449
+ tools.push(tool2);
2450
+ }
2451
+ }
2452
+ return tools;
2453
+ }
2454
+ async function executeWithTimeout(promise, timeoutMs, toolName) {
2455
+ return Promise.race([
2456
+ Promise.resolve(promise),
2457
+ new Promise((_, reject) => {
2458
+ setTimeout(() => {
2459
+ reject(new Error(`Tool "${toolName}" timed out after ${timeoutMs}ms`));
2460
+ }, timeoutMs);
2461
+ })
2462
+ ]);
2463
+ }
2464
+ function extractJSDocDescription(fn) {
2465
+ const source = fn.toString();
2466
+ const match = /\/\*\*\s*\n\s*\*\s*(.+?)\s*\n/.exec(source);
2467
+ return match?.[1];
2468
+ }
2469
+
2470
+ // src/utils/tool-runner.ts
2471
+ init_tool();
2472
+ var ToolRunner = class {
2473
+ /**
2474
+ * Execute a tool with the given input and context
2475
+ *
2476
+ * @param tool - Tool to execute
2477
+ * @param input - Input data
2478
+ * @param context - Agent execution context
2479
+ * @param options - Execution options
2480
+ * @returns Tool execution result
2481
+ */
2482
+ static async execute(tool2, input, context, options = {}) {
2483
+ if (options.signal?.aborted) {
2484
+ return ToolResultFactory.failure(
2485
+ tool2.name,
2486
+ new Error(`Tool "${tool2.name}" execution was aborted`)
2487
+ );
2488
+ }
2489
+ if (tool2.schema) {
2490
+ const validation = tool2.schema.safeParse(input);
2491
+ if (!validation.success) {
2492
+ return ToolResultFactory.failure(
2493
+ tool2.name,
2494
+ new Error(
2495
+ `Invalid input for tool "${tool2.name}": ${validation.error.message}`
2496
+ )
2497
+ );
2498
+ }
2499
+ }
2500
+ const executionContext = {
2501
+ agentContext: context,
2502
+ ...options.signal && { signal: options.signal },
2503
+ metadata: options.metadata ?? {}
2504
+ };
2505
+ try {
2506
+ const timeoutMs = options.timeoutMs ?? tool2.timeoutMs;
2507
+ let result;
2508
+ if (timeoutMs !== void 0) {
2509
+ result = await this.executeWithTimeout(
2510
+ Promise.resolve(tool2.execute(input, executionContext)),
2511
+ timeoutMs,
2512
+ tool2.name
2513
+ );
2514
+ } else {
2515
+ result = await Promise.resolve(tool2.execute(input, executionContext));
2516
+ }
2517
+ return result;
2518
+ } catch (error) {
2519
+ return ToolResultFactory.failure(
2520
+ tool2.name,
2521
+ error instanceof Error ? error : new Error(String(error))
2522
+ );
2523
+ }
2524
+ }
2525
+ /**
2526
+ * Execute multiple tools in parallel
2527
+ *
2528
+ * @param executions - Array of tool execution tuples [tool, input, context, options?]
2529
+ * @returns Array of results in same order as input
2530
+ */
2531
+ static async executeParallel(executions) {
2532
+ return Promise.all(
2533
+ executions.map(
2534
+ ([tool2, input, context, options]) => this.execute(tool2, input, context, options)
2535
+ )
2536
+ );
2537
+ }
2538
+ /**
2539
+ * Execute multiple tools sequentially, stopping on first failure
2540
+ *
2541
+ * @param executions - Array of tool execution tuples
2542
+ * @returns Array of results up to first failure (inclusive)
2543
+ */
2544
+ static async executeSequential(executions) {
2545
+ const results = [];
2546
+ for (const [tool2, input, context, options] of executions) {
2547
+ const result = await this.execute(tool2, input, context, options);
2548
+ results.push(result);
2549
+ if (!result.success) {
2550
+ break;
2551
+ }
2552
+ }
2553
+ return results;
2554
+ }
2555
+ /**
2556
+ * Execute a tool with timeout
2557
+ *
2558
+ * @param promise - Tool execution promise
2559
+ * @param timeoutMs - Timeout in milliseconds
2560
+ * @param toolName - Tool name for error messages
2561
+ * @returns Result or rejects with timeout error
2562
+ */
2563
+ static async executeWithTimeout(promise, timeoutMs, toolName) {
2564
+ return Promise.race([
2565
+ promise,
2566
+ new Promise((_, reject) => {
2567
+ setTimeout(() => {
2568
+ reject(
2569
+ new Error(`Tool "${toolName}" timed out after ${timeoutMs}ms`)
2570
+ );
2571
+ }, timeoutMs);
2572
+ })
2573
+ ]);
2574
+ }
2575
+ /**
2576
+ * Validate tool input without executing
2577
+ *
2578
+ * @param tool - Tool to validate input for
2579
+ * @param input - Input to validate
2580
+ * @returns true if valid, Error if invalid
2581
+ */
2582
+ static validate(tool2, input) {
2583
+ if (!tool2.schema) {
2584
+ return true;
2585
+ }
2586
+ const validation = tool2.schema.safeParse(input);
2587
+ if (!validation.success) {
2588
+ return new Error(
2589
+ `Invalid input for tool "${tool2.name}": ${validation.error.message}`
2590
+ );
2591
+ }
2592
+ return true;
2593
+ }
2594
+ /**
2595
+ * Check if a result indicates success
2596
+ *
2597
+ * @param result - Tool result to check
2598
+ * @returns true if success, false if failure
2599
+ */
2600
+ static isSuccess(result) {
2601
+ return result.success === true;
2602
+ }
2603
+ /**
2604
+ * Check if a result indicates failure
2605
+ *
2606
+ * @param result - Tool result to check
2607
+ * @returns true if failure, false if success
2608
+ */
2609
+ static isFailure(result) {
2610
+ return result.success === false;
2611
+ }
2612
+ };
2613
+
2614
+ export { Agent, AgentContext, AgentDecisionSchema, BaseAgent, ConsoleLogger, DEFAULT_MODEL, DEFAULT_RETRY_CONFIG, ExecutionCycleSchema, HookEvents, HookManager, InMemoryStore, LogLevel, MCPClient, MCPServerConfigSchema, MCPToolProvider, MCPconfig, MemoryEntryMetadataSchema, MemoryEntrySchema, MemoryUpdateSchema, OpperClient, Result, SchemaValidationError, SilentLogger, ThoughtSchema, ToolCallRecordSchema, ToolCallSchema, ToolExecutionSummarySchema, ToolMetadataSchema, ToolResultFactory, ToolResultFailureSchema, ToolResultSchema, ToolResultSuccessSchema, ToolRunner, UsageSchema, coerceToolDefinition, createFunctionTool, createHookManager, createInMemoryStore, createMCPServerConfig, createOpperClient, createToolCallRecord, err, extractTools, getDefaultLogger, getSchemaDefault, isSchemaValid, isToolProvider, mcp, mergeSchemaDefaults, normalizeToolEntries, ok, schemaToJson, setDefaultLogger, tool, validateSchema, validateToolInput };
2615
+ //# sourceMappingURL=index.js.map
2616
+ //# sourceMappingURL=index.js.map