@opperai/agents 0.1.3 → 0.3.0-beta

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 CHANGED
@@ -392,7 +392,11 @@ var HookEvents = {
392
392
  ToolError: "tool:error",
393
393
  MemoryRead: "memory:read",
394
394
  MemoryWrite: "memory:write",
395
- MemoryError: "memory:error"
395
+ MemoryError: "memory:error",
396
+ StreamStart: "stream:start",
397
+ StreamChunk: "stream:chunk",
398
+ StreamEnd: "stream:end",
399
+ StreamError: "stream:error"
396
400
  };
397
401
  var HookManager = class {
398
402
  registry = /* @__PURE__ */ new Map();
@@ -464,6 +468,82 @@ var HookManager = class {
464
468
  };
465
469
  var createHookManager = (logger) => new HookManager(logger);
466
470
 
471
+ // src/base/events.ts
472
+ var AgentEvents = {
473
+ StreamStart: HookEvents.StreamStart,
474
+ StreamChunk: HookEvents.StreamChunk,
475
+ StreamEnd: HookEvents.StreamEnd,
476
+ StreamError: HookEvents.StreamError
477
+ };
478
+ var AgentEventEmitter = class {
479
+ registry = /* @__PURE__ */ new Map();
480
+ logger;
481
+ constructor(logger) {
482
+ this.logger = logger ?? getDefaultLogger();
483
+ }
484
+ on(event, listener) {
485
+ const listeners = this.registry.get(event) ?? /* @__PURE__ */ new Set();
486
+ listeners.add(listener);
487
+ this.registry.set(event, listeners);
488
+ return () => this.off(event, listener);
489
+ }
490
+ once(event, listener) {
491
+ const wrapper = (payload) => {
492
+ try {
493
+ listener(payload);
494
+ } finally {
495
+ this.off(event, wrapper);
496
+ }
497
+ };
498
+ return this.on(event, wrapper);
499
+ }
500
+ off(event, listener) {
501
+ const listeners = this.registry.get(event);
502
+ if (!listeners) {
503
+ return;
504
+ }
505
+ listeners.delete(listener);
506
+ if (listeners.size === 0) {
507
+ this.registry.delete(event);
508
+ }
509
+ }
510
+ emit(event, payload) {
511
+ const listeners = Array.from(
512
+ this.registry.get(event) ?? /* @__PURE__ */ new Set()
513
+ );
514
+ if (listeners.length === 0) {
515
+ return;
516
+ }
517
+ for (const listener of listeners) {
518
+ try {
519
+ listener(payload);
520
+ } catch (error) {
521
+ this.logger.warn(`Agent event listener failed for "${event}"`, {
522
+ event,
523
+ error: error instanceof Error ? error.message : String(error)
524
+ });
525
+ }
526
+ }
527
+ }
528
+ removeAllListeners(event) {
529
+ if (event) {
530
+ this.registry.delete(event);
531
+ return;
532
+ }
533
+ this.registry.clear();
534
+ }
535
+ listenerCount(event) {
536
+ if (event) {
537
+ return this.registry.get(event)?.size ?? 0;
538
+ }
539
+ let total = 0;
540
+ for (const listeners of this.registry.values()) {
541
+ total += listeners.size;
542
+ }
543
+ return total;
544
+ }
545
+ };
546
+
467
547
  // src/base/agent.ts
468
548
  init_tool();
469
549
  function sanitizeId(name) {
@@ -823,6 +903,10 @@ var BaseAgent = class {
823
903
  * Whether memory is enabled
824
904
  */
825
905
  enableMemory;
906
+ /**
907
+ * Whether streaming is enabled
908
+ */
909
+ enableStreaming;
826
910
  /**
827
911
  * Memory instance for persistent storage (null if disabled or initialization failed)
828
912
  */
@@ -835,6 +919,10 @@ var BaseAgent = class {
835
919
  * Hook manager for lifecycle events
836
920
  */
837
921
  hooks;
922
+ /**
923
+ * Event dispatcher for runtime events (notably streaming)
924
+ */
925
+ events;
838
926
  /**
839
927
  * Registry of available tools
840
928
  */
@@ -855,6 +943,28 @@ var BaseAgent = class {
855
943
  * Opper client configuration
856
944
  */
857
945
  opperConfig;
946
+ /**
947
+ * Creates a new BaseAgent instance
948
+ *
949
+ * @param config - Agent configuration object
950
+ * @param config.name - Unique name identifying this agent (required)
951
+ * @param config.description - Human-readable description of the agent's purpose
952
+ * @param config.instructions - System instructions guiding agent behavior
953
+ * @param config.tools - Array of tools or tool providers available to the agent
954
+ * @param config.maxIterations - Maximum iterations before terminating the agent loop (default: 25)
955
+ * @param config.model - Model identifier(s). Single model or array for fallback (default: "gcp/gemini-flash-latest")
956
+ * @param config.inputSchema - Zod schema for input validation
957
+ * @param config.outputSchema - Zod schema for output validation
958
+ * @param config.enableStreaming - Enable Opper streaming APIs for LLM calls (default: false)
959
+ * @param config.enableMemory - Enable memory subsystem (default: false)
960
+ * @param config.memory - Custom memory implementation (defaults to InMemoryStore if enableMemory is true)
961
+ * @param config.metadata - Additional metadata for the agent
962
+ * @param config.opperConfig - Opper API configuration containing apiKey and baseUrl
963
+ * @param config.onStreamStart - Handler invoked when a streaming call starts
964
+ * @param config.onStreamChunk - Handler invoked for each streaming chunk
965
+ * @param config.onStreamEnd - Handler invoked when a streaming call ends
966
+ * @param config.onStreamError - Handler invoked when streaming encounters an error
967
+ */
858
968
  constructor(config) {
859
969
  this.name = config.name;
860
970
  this.description = config.description;
@@ -864,12 +974,26 @@ var BaseAgent = class {
864
974
  this.inputSchema = config.inputSchema;
865
975
  this.outputSchema = config.outputSchema;
866
976
  this.enableMemory = config.enableMemory ?? false;
977
+ this.enableStreaming = config.enableStreaming ?? false;
867
978
  this.metadata = { ...config.metadata ?? {} };
868
979
  this.hooks = new HookManager();
980
+ this.events = new AgentEventEmitter();
869
981
  this.tools = /* @__PURE__ */ new Map();
870
982
  this.baseTools = /* @__PURE__ */ new Map();
871
983
  this.toolProviders = /* @__PURE__ */ new Set();
872
984
  this.providerToolRegistry = /* @__PURE__ */ new Map();
985
+ if (config.onStreamStart) {
986
+ this.on(HookEvents.StreamStart, config.onStreamStart);
987
+ }
988
+ if (config.onStreamChunk) {
989
+ this.on(HookEvents.StreamChunk, config.onStreamChunk);
990
+ }
991
+ if (config.onStreamEnd) {
992
+ this.on(HookEvents.StreamEnd, config.onStreamEnd);
993
+ }
994
+ if (config.onStreamError) {
995
+ this.on(HookEvents.StreamError, config.onStreamError);
996
+ }
873
997
  this.opperConfig = {
874
998
  apiKey: config.opperConfig?.apiKey ?? process.env["OPPER_API_KEY"],
875
999
  baseUrl: config.opperConfig?.baseUrl,
@@ -1051,6 +1175,35 @@ var BaseAgent = class {
1051
1175
  registerHook(event, handler) {
1052
1176
  return this.hooks.on(event, handler);
1053
1177
  }
1178
+ /**
1179
+ * Register an event listener.
1180
+ *
1181
+ * @param event - Event name
1182
+ * @param listener - Listener callback
1183
+ * @returns Cleanup function to unregister the listener
1184
+ */
1185
+ on(event, listener) {
1186
+ return this.events.on(event, listener);
1187
+ }
1188
+ /**
1189
+ * Register a one-time event listener that removes itself after the first call.
1190
+ *
1191
+ * @param event - Event name
1192
+ * @param listener - Listener callback
1193
+ * @returns Cleanup function (no-op once listener fires)
1194
+ */
1195
+ once(event, listener) {
1196
+ return this.events.once(event, listener);
1197
+ }
1198
+ /**
1199
+ * Remove a previously registered event listener.
1200
+ *
1201
+ * @param event - Event name
1202
+ * @param listener - Listener callback to remove
1203
+ */
1204
+ off(event, listener) {
1205
+ this.events.off(event, listener);
1206
+ }
1054
1207
  /**
1055
1208
  * Trigger a hook event with a payload.
1056
1209
  * Swallows errors to prevent hook failures from breaking agent execution.
@@ -1065,6 +1218,15 @@ var BaseAgent = class {
1065
1218
  console.warn(`Hook error for event ${event}:`, error);
1066
1219
  }
1067
1220
  }
1221
+ /**
1222
+ * Emit a runtime event to listeners.
1223
+ *
1224
+ * @param event - Event name
1225
+ * @param payload - Event payload
1226
+ */
1227
+ emitAgentEvent(event, payload) {
1228
+ this.events.emit(event, payload);
1229
+ }
1068
1230
  /**
1069
1231
  * Execute a tool with proper context, hooks, and error handling.
1070
1232
  *
@@ -1303,6 +1465,10 @@ var AgentDecisionSchema = z.object({
1303
1465
  * Agent's internal reasoning
1304
1466
  */
1305
1467
  reasoning: z.string(),
1468
+ /**
1469
+ * Status message for the user (e.g., "Searching for information...", "Processing results...")
1470
+ */
1471
+ userMessage: z.string().default("Working on it..."),
1306
1472
  /**
1307
1473
  * Tool calls to execute (if any)
1308
1474
  * Empty array signals task completion
@@ -1315,8 +1481,34 @@ var AgentDecisionSchema = z.object({
1315
1481
  /**
1316
1482
  * Memory entries to write/update (key -> payload)
1317
1483
  */
1318
- memoryUpdates: z.record(MemoryUpdateSchema).default({})
1484
+ memoryUpdates: z.record(MemoryUpdateSchema).default({}),
1485
+ /**
1486
+ * Whether the task is complete and finalResult is available
1487
+ * (single LLM call pattern)
1488
+ */
1489
+ isComplete: z.boolean().default(false),
1490
+ /**
1491
+ * The final result when isComplete=true
1492
+ * Should match outputSchema if specified
1493
+ */
1494
+ finalResult: z.unknown().optional()
1319
1495
  });
1496
+ function createAgentDecisionWithOutputSchema(outputSchema) {
1497
+ if (!outputSchema) {
1498
+ return AgentDecisionSchema;
1499
+ }
1500
+ const finalResultSchema = outputSchema.optional();
1501
+ const dynamicSchema = z.object({
1502
+ reasoning: z.string(),
1503
+ userMessage: z.string().default("Working on it..."),
1504
+ toolCalls: z.array(ToolCallSchema).default([]),
1505
+ memoryReads: z.array(z.string()).default([]),
1506
+ memoryUpdates: z.record(MemoryUpdateSchema).default({}),
1507
+ isComplete: z.boolean().default(false),
1508
+ finalResult: finalResultSchema
1509
+ });
1510
+ return dynamicSchema;
1511
+ }
1320
1512
  var ToolExecutionSummarySchema = z.object({
1321
1513
  /**
1322
1514
  * Tool name
@@ -1338,7 +1530,7 @@ var ToolExecutionSummarySchema = z.object({
1338
1530
 
1339
1531
  // package.json
1340
1532
  var package_default = {
1341
- version: "0.1.3"};
1533
+ version: "0.3.0-beta"};
1342
1534
 
1343
1535
  // src/utils/version.ts
1344
1536
  var SDK_NAME = "@opperai/agents";
@@ -1401,7 +1593,7 @@ var OpperClient = class {
1401
1593
  return this.withRetry(async () => {
1402
1594
  const inputSchema = this.toJsonSchema(options.inputSchema);
1403
1595
  const outputSchema = this.toJsonSchema(options.outputSchema);
1404
- const response = await this.client.call({
1596
+ const callPayload = {
1405
1597
  name: options.name,
1406
1598
  instructions: options.instructions,
1407
1599
  input: options.input,
@@ -1409,7 +1601,8 @@ var OpperClient = class {
1409
1601
  ...outputSchema && { outputSchema },
1410
1602
  ...options.model && { model: options.model },
1411
1603
  ...options.parentSpanId && { parentSpanId: options.parentSpanId }
1412
- });
1604
+ };
1605
+ const response = options.signal ? await this.client.call(callPayload, { signal: options.signal }) : await this.client.call(callPayload);
1413
1606
  const usagePayload = extractUsage(response);
1414
1607
  const costPayload = extractCost(response);
1415
1608
  const usage = {
@@ -1427,6 +1620,36 @@ var OpperClient = class {
1427
1620
  return result;
1428
1621
  }, options.name);
1429
1622
  }
1623
+ /**
1624
+ * Stream a call to Opper with retry logic
1625
+ */
1626
+ async stream(options) {
1627
+ return this.withRetry(async () => {
1628
+ const inputSchema = this.toJsonSchema(options.inputSchema);
1629
+ const outputSchema = this.toJsonSchema(options.outputSchema);
1630
+ const streamPayload = {
1631
+ name: options.name,
1632
+ instructions: options.instructions,
1633
+ input: options.input,
1634
+ ...inputSchema && { inputSchema },
1635
+ ...outputSchema && { outputSchema },
1636
+ ...options.model && { model: options.model },
1637
+ ...options.parentSpanId && { parentSpanId: options.parentSpanId }
1638
+ };
1639
+ const response = options.signal ? await this.client.stream(streamPayload, { signal: options.signal }) : await this.client.stream(streamPayload);
1640
+ const iterable = {
1641
+ async *[Symbol.asyncIterator]() {
1642
+ for await (const event of response.result) {
1643
+ yield event;
1644
+ }
1645
+ }
1646
+ };
1647
+ return {
1648
+ headers: response.headers,
1649
+ result: iterable
1650
+ };
1651
+ }, `stream:${options.name}`);
1652
+ }
1430
1653
  /**
1431
1654
  * Create a span for tracing
1432
1655
  */
@@ -1435,7 +1658,8 @@ var OpperClient = class {
1435
1658
  const span = await this.client.spans.create({
1436
1659
  name: options.name,
1437
1660
  ...options.input !== void 0 && { input: options.input },
1438
- ...options.parentSpanId && { parentId: options.parentSpanId }
1661
+ ...options.parentSpanId && { parentId: options.parentSpanId },
1662
+ ...options.type && { type: options.type }
1439
1663
  });
1440
1664
  return {
1441
1665
  id: span.id,
@@ -1452,7 +1676,11 @@ var OpperClient = class {
1452
1676
  const serializedOutput = output !== void 0 && output !== null ? typeof output === "object" ? JSON.stringify(output) : String(output) : void 0;
1453
1677
  await this.client.spans.update(spanId, {
1454
1678
  ...serializedOutput !== void 0 && { output: serializedOutput },
1455
- ...options?.error && { error: options.error }
1679
+ ...options?.error && { error: options.error },
1680
+ ...options?.startTime && { startTime: options.startTime },
1681
+ ...options?.endTime && { endTime: options.endTime },
1682
+ ...options?.meta && { meta: options.meta },
1683
+ ...options?.name && { name: options.name }
1456
1684
  });
1457
1685
  }, `update-span:${spanId}`);
1458
1686
  }
@@ -1595,6 +1823,193 @@ var mergeSchemaDefaults = (schema, value) => {
1595
1823
  return validateSchema(schema, value);
1596
1824
  };
1597
1825
 
1826
+ // src/utils/streaming.ts
1827
+ var STREAM_ROOT_PATH = "_root";
1828
+ var NUMERIC_TOKEN_PATTERN = /^\d+$/;
1829
+ var BRACKET_TOKEN_PATTERN = /\[(\d+)\]/g;
1830
+ var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1831
+ var coercePrimitive = (value) => {
1832
+ const trimmed = value.trim();
1833
+ if (trimmed.length === 0) {
1834
+ return value;
1835
+ }
1836
+ const lower = trimmed.toLowerCase();
1837
+ if (lower === "true") {
1838
+ return true;
1839
+ }
1840
+ if (lower === "false") {
1841
+ return false;
1842
+ }
1843
+ if (lower === "null") {
1844
+ return null;
1845
+ }
1846
+ if (/^-?\d+$/.test(trimmed)) {
1847
+ const intValue = Number.parseInt(trimmed, 10);
1848
+ return Number.isNaN(intValue) ? value : intValue;
1849
+ }
1850
+ if (/^-?\d+\.\d+$/.test(trimmed)) {
1851
+ const floatValue = Number.parseFloat(trimmed);
1852
+ return Number.isNaN(floatValue) ? value : floatValue;
1853
+ }
1854
+ return value;
1855
+ };
1856
+ var toDisplayString = (value) => {
1857
+ if (value === null || value === void 0) {
1858
+ return "";
1859
+ }
1860
+ if (typeof value === "string") {
1861
+ return value;
1862
+ }
1863
+ return String(value);
1864
+ };
1865
+ var parsePathSegments = (path) => {
1866
+ return path.replace(BRACKET_TOKEN_PATTERN, (_, index) => `.${index}`).split(".").map((segment) => segment.trim()).filter((segment) => segment.length > 0);
1867
+ };
1868
+ var setNestedValue = (target, path, value) => {
1869
+ const segments = parsePathSegments(path);
1870
+ if (segments.length === 0) {
1871
+ return;
1872
+ }
1873
+ let current = target;
1874
+ for (let i = 0; i < segments.length - 1; i++) {
1875
+ const segment = segments[i];
1876
+ if (segment === void 0) {
1877
+ return;
1878
+ }
1879
+ const existing = current[segment];
1880
+ if (!isRecord(existing)) {
1881
+ const nextLevel = {};
1882
+ current[segment] = nextLevel;
1883
+ current = nextLevel;
1884
+ continue;
1885
+ }
1886
+ current = existing;
1887
+ }
1888
+ const lastSegment = segments[segments.length - 1];
1889
+ if (lastSegment === void 0) {
1890
+ return;
1891
+ }
1892
+ current[lastSegment] = value;
1893
+ };
1894
+ var normalizeIndexed = (value) => {
1895
+ if (Array.isArray(value)) {
1896
+ return value.map((item) => normalizeIndexed(item));
1897
+ }
1898
+ if (!isRecord(value)) {
1899
+ return value;
1900
+ }
1901
+ const normalizedEntries = {};
1902
+ for (const [key, entryValue] of Object.entries(value)) {
1903
+ normalizedEntries[key] = normalizeIndexed(entryValue);
1904
+ }
1905
+ const keys = Object.keys(normalizedEntries);
1906
+ if (keys.length > 0 && keys.every((key) => NUMERIC_TOKEN_PATTERN.test(key))) {
1907
+ const parsedIndices = keys.map((key) => Number.parseInt(key, 10)).filter((index) => Number.isFinite(index) && index >= 0);
1908
+ if (parsedIndices.length === 0) {
1909
+ return normalizedEntries;
1910
+ }
1911
+ const maxIndex = Math.max(...parsedIndices);
1912
+ const result = new Array(maxIndex + 1).fill(void 0);
1913
+ for (const [key, entryValue] of Object.entries(normalizedEntries)) {
1914
+ const index = Number.parseInt(key, 10);
1915
+ if (!Number.isFinite(index) || index < 0) {
1916
+ continue;
1917
+ }
1918
+ result[index] = entryValue;
1919
+ }
1920
+ return result.map(
1921
+ (item) => item === void 0 ? null : item
1922
+ );
1923
+ }
1924
+ return normalizedEntries;
1925
+ };
1926
+ var resolveFieldValue = (path, displayBuffers, valueBuffers) => {
1927
+ const values = valueBuffers.get(path) ?? [];
1928
+ if (values.length === 0) {
1929
+ return displayBuffers.get(path) ?? "";
1930
+ }
1931
+ const last = values[values.length - 1];
1932
+ if (typeof last === "number" || typeof last === "boolean") {
1933
+ return last;
1934
+ }
1935
+ if (last === null) {
1936
+ return null;
1937
+ }
1938
+ const joined = displayBuffers.get(path) ?? values.map((value) => toDisplayString(value)).join("");
1939
+ return coercePrimitive(joined);
1940
+ };
1941
+ var StreamAssembler = class {
1942
+ displayBuffers = /* @__PURE__ */ new Map();
1943
+ valueBuffers = /* @__PURE__ */ new Map();
1944
+ schema;
1945
+ constructor(options = {}) {
1946
+ this.schema = options.schema;
1947
+ }
1948
+ feed(chunk) {
1949
+ const { delta } = chunk;
1950
+ if (delta === null || delta === void 0) {
1951
+ return null;
1952
+ }
1953
+ const path = chunk.jsonPath === null || chunk.jsonPath === void 0 ? STREAM_ROOT_PATH : chunk.jsonPath;
1954
+ const existingDisplay = this.displayBuffers.get(path) ?? "";
1955
+ const nextDisplay = `${existingDisplay}${toDisplayString(delta)}`;
1956
+ this.displayBuffers.set(path, nextDisplay);
1957
+ const existingValues = this.valueBuffers.get(path) ?? [];
1958
+ existingValues.push(delta);
1959
+ this.valueBuffers.set(path, existingValues);
1960
+ return {
1961
+ path,
1962
+ accumulated: nextDisplay,
1963
+ snapshot: this.snapshot()
1964
+ };
1965
+ }
1966
+ snapshot() {
1967
+ return Object.fromEntries(this.displayBuffers.entries());
1968
+ }
1969
+ hasStructuredFields() {
1970
+ const keys = Array.from(this.displayBuffers.keys());
1971
+ return keys.some((key) => key !== STREAM_ROOT_PATH);
1972
+ }
1973
+ finalize() {
1974
+ if (this.displayBuffers.size === 0) {
1975
+ return { type: "empty" };
1976
+ }
1977
+ if (!this.hasStructuredFields()) {
1978
+ const root = this.displayBuffers.get(STREAM_ROOT_PATH) ?? "";
1979
+ return { type: "root", rootText: root };
1980
+ }
1981
+ const structured = this.reconstructStructured();
1982
+ let coerced = structured;
1983
+ if (this.schema !== void 0) {
1984
+ try {
1985
+ coerced = this.schema.parse(structured);
1986
+ } catch {
1987
+ coerced = structured;
1988
+ }
1989
+ }
1990
+ return { type: "structured", structured: coerced };
1991
+ }
1992
+ getFieldBuffers() {
1993
+ return new Map(this.displayBuffers);
1994
+ }
1995
+ reconstructStructured() {
1996
+ const result = {};
1997
+ for (const path of this.valueBuffers.keys()) {
1998
+ if (path === STREAM_ROOT_PATH) {
1999
+ continue;
2000
+ }
2001
+ const value = resolveFieldValue(
2002
+ path,
2003
+ this.displayBuffers,
2004
+ this.valueBuffers
2005
+ );
2006
+ setNestedValue(result, path, value);
2007
+ }
2008
+ return normalizeIndexed(result);
2009
+ }
2010
+ };
2011
+ var createStreamAssembler = (options) => new StreamAssembler(options);
2012
+
1598
2013
  // src/core/agent.ts
1599
2014
  var isToolSuccessResult = (value) => {
1600
2015
  if (typeof value !== "object" || value === null) {
@@ -1607,6 +2022,31 @@ var Agent = class extends BaseAgent {
1607
2022
  opperClient;
1608
2023
  logger;
1609
2024
  verbose;
2025
+ /**
2026
+ * Creates a new Agent instance
2027
+ *
2028
+ * @param config - Agent configuration object
2029
+ * @param config.name - Unique name identifying this agent (required)
2030
+ * @param config.description - Human-readable description of the agent's purpose
2031
+ * @param config.instructions - System instructions guiding agent behavior
2032
+ * @param config.tools - Array of tools or tool providers available to the agent
2033
+ * @param config.maxIterations - Maximum iterations before terminating (default: 25)
2034
+ * @param config.model - Model identifier(s) as string or array for fallback (default: "gcp/gemini-flash-latest")
2035
+ * @param config.inputSchema - Zod schema for input validation
2036
+ * @param config.outputSchema - Zod schema for output validation
2037
+ * @param config.enableStreaming - Enable streaming for LLM calls (default: false)
2038
+ * @param config.enableMemory - Enable memory subsystem (default: false)
2039
+ * @param config.memory - Custom memory implementation (defaults to InMemoryStore if enableMemory is true)
2040
+ * @param config.metadata - Additional metadata for the agent
2041
+ * @param config.opperConfig - Opper API configuration (apiKey, baseUrl)
2042
+ * @param config.opperClient - Custom Opper client instance (for testing or custom configuration)
2043
+ * @param config.logger - Logger instance for debugging
2044
+ * @param config.verbose - Enable verbose logging (default: false)
2045
+ * @param config.onStreamStart - Handler invoked when streaming starts
2046
+ * @param config.onStreamChunk - Handler invoked for each streaming chunk
2047
+ * @param config.onStreamEnd - Handler invoked when streaming ends
2048
+ * @param config.onStreamError - Handler invoked on streaming errors
2049
+ */
1610
2050
  constructor(config) {
1611
2051
  super(config);
1612
2052
  this.logger = config.logger ?? getDefaultLogger();
@@ -1642,6 +2082,7 @@ var Agent = class extends BaseAgent {
1642
2082
  maxIterations: this.maxIterations,
1643
2083
  tools: Array.from(this.tools.keys())
1644
2084
  });
2085
+ const executionStartTime = /* @__PURE__ */ new Date();
1645
2086
  const parentSpan = await this.opperClient.createSpan({
1646
2087
  name: `${this.name}_execution`,
1647
2088
  input: this.serializeInput(input),
@@ -1661,6 +2102,47 @@ var Agent = class extends BaseAgent {
1661
2102
  input,
1662
2103
  context
1663
2104
  );
2105
+ if (decision.isComplete && decision.finalResult !== void 0) {
2106
+ this.log("Task completed with final result in single call", {
2107
+ iteration: currentIteration
2108
+ });
2109
+ let finalResult;
2110
+ if (this.outputSchema) {
2111
+ const parseResult = this.outputSchema.safeParse(
2112
+ decision.finalResult
2113
+ );
2114
+ if (parseResult.success) {
2115
+ finalResult = parseResult.data;
2116
+ } else {
2117
+ this.logger.warn(
2118
+ "Final result validation against output schema failed, falling back to generate_final_result",
2119
+ { error: parseResult.error.message }
2120
+ );
2121
+ break;
2122
+ }
2123
+ } else {
2124
+ const rawResult = decision.finalResult;
2125
+ if (typeof rawResult === "string") {
2126
+ finalResult = rawResult;
2127
+ } else if (rawResult === null || rawResult === void 0) {
2128
+ finalResult = "";
2129
+ } else if (typeof rawResult === "object") {
2130
+ finalResult = JSON.stringify(rawResult);
2131
+ } else {
2132
+ finalResult = String(rawResult);
2133
+ }
2134
+ }
2135
+ const executionEndTime2 = /* @__PURE__ */ new Date();
2136
+ await this.opperClient.updateSpan(parentSpan.id, finalResult, {
2137
+ startTime: executionStartTime,
2138
+ endTime: executionEndTime2,
2139
+ meta: {
2140
+ durationMs: executionEndTime2.getTime() - executionStartTime.getTime()
2141
+ }
2142
+ });
2143
+ await this.triggerHook(HookEvents.LoopEnd, { context });
2144
+ return finalResult;
2145
+ }
1664
2146
  const memoryResults = await this.handleMemoryActions(
1665
2147
  decision,
1666
2148
  context,
@@ -1670,7 +2152,7 @@ var Agent = class extends BaseAgent {
1670
2152
  const toolResults = await this.executeToolCalls(
1671
2153
  decision,
1672
2154
  context,
1673
- thinkSpanId
2155
+ context.parentSpanId ?? void 0
1674
2156
  );
1675
2157
  const combinedResults = [...memoryResults, ...toolResults];
1676
2158
  const newToolCalls = context.toolCalls.slice(toolCallStartIndex);
@@ -1705,11 +2187,24 @@ var Agent = class extends BaseAgent {
1705
2187
  );
1706
2188
  }
1707
2189
  const result = await this.generateFinalResult(input, context);
1708
- await this.opperClient.updateSpan(parentSpan.id, result);
2190
+ const executionEndTime = /* @__PURE__ */ new Date();
2191
+ await this.opperClient.updateSpan(parentSpan.id, result, {
2192
+ startTime: executionStartTime,
2193
+ endTime: executionEndTime,
2194
+ meta: {
2195
+ durationMs: executionEndTime.getTime() - executionStartTime.getTime()
2196
+ }
2197
+ });
1709
2198
  return result;
1710
2199
  } catch (error) {
2200
+ const executionEndTime = /* @__PURE__ */ new Date();
1711
2201
  await this.opperClient.updateSpan(parentSpan.id, void 0, {
1712
- error: error instanceof Error ? error.message : String(error)
2202
+ error: error instanceof Error ? error.message : String(error),
2203
+ startTime: executionStartTime,
2204
+ endTime: executionEndTime,
2205
+ meta: {
2206
+ durationMs: executionEndTime.getTime() - executionStartTime.getTime()
2207
+ }
1713
2208
  });
1714
2209
  throw error;
1715
2210
  }
@@ -1718,9 +2213,16 @@ var Agent = class extends BaseAgent {
1718
2213
  * Think step: Call LLM to decide next action
1719
2214
  */
1720
2215
  async think(input, context) {
1721
- const spanName = "think";
2216
+ const sanitizedName = this.name.toLowerCase().replace(/[\s-]/g, "_");
2217
+ const spanName = `think_${sanitizedName}`;
1722
2218
  this.log("Think step", { iteration: context.iteration });
1723
2219
  await this.triggerHook(HookEvents.LlmCall, { context, callType: "think" });
2220
+ const decisionSchema = createAgentDecisionWithOutputSchema(
2221
+ this.outputSchema
2222
+ );
2223
+ if (this.enableStreaming) {
2224
+ return this.thinkStreaming(input, context, decisionSchema, spanName);
2225
+ }
1724
2226
  try {
1725
2227
  const instructions = this.buildThinkInstructions();
1726
2228
  const thinkContext = await this.buildThinkContext(input, context);
@@ -1728,7 +2230,7 @@ var Agent = class extends BaseAgent {
1728
2230
  name: spanName,
1729
2231
  instructions,
1730
2232
  input: thinkContext,
1731
- outputSchema: AgentDecisionSchema,
2233
+ outputSchema: decisionSchema,
1732
2234
  model: this.model,
1733
2235
  ...context.parentSpanId && { parentSpanId: context.parentSpanId }
1734
2236
  });
@@ -1739,15 +2241,25 @@ var Agent = class extends BaseAgent {
1739
2241
  totalTokens: response.usage.totalTokens,
1740
2242
  cost: response.usage.cost
1741
2243
  });
1742
- const decision = AgentDecisionSchema.parse(response.jsonPayload);
2244
+ const decision = decisionSchema.parse(
2245
+ response.jsonPayload
2246
+ );
1743
2247
  await this.triggerHook(HookEvents.LlmResponse, {
1744
2248
  context,
1745
2249
  callType: "think",
1746
2250
  response
1747
2251
  });
2252
+ if (response.spanId) {
2253
+ await this.opperClient.updateSpan(response.spanId, void 0, {
2254
+ name: "think"
2255
+ });
2256
+ }
1748
2257
  await this.triggerHook(HookEvents.ThinkEnd, {
1749
2258
  context,
1750
- thought: { reasoning: decision.reasoning }
2259
+ thought: {
2260
+ reasoning: decision.reasoning,
2261
+ userMessage: decision.userMessage
2262
+ }
1751
2263
  });
1752
2264
  this.log("Think result", {
1753
2265
  reasoning: decision.reasoning,
@@ -1763,6 +2275,139 @@ var Agent = class extends BaseAgent {
1763
2275
  );
1764
2276
  }
1765
2277
  }
2278
+ async thinkStreaming(input, context, decisionSchema, spanName) {
2279
+ const instructions = this.buildThinkInstructions();
2280
+ const thinkContext = await this.buildThinkContext(input, context);
2281
+ const assembler = createStreamAssembler({
2282
+ schema: decisionSchema
2283
+ });
2284
+ let streamSpanId;
2285
+ await this.triggerHook(HookEvents.StreamStart, {
2286
+ context,
2287
+ callType: "think"
2288
+ });
2289
+ this.emitAgentEvent(HookEvents.StreamStart, {
2290
+ context,
2291
+ callType: "think"
2292
+ });
2293
+ try {
2294
+ const streamResponse = await this.opperClient.stream({
2295
+ name: spanName,
2296
+ instructions,
2297
+ input: thinkContext,
2298
+ outputSchema: decisionSchema,
2299
+ model: this.model,
2300
+ ...context.parentSpanId && { parentSpanId: context.parentSpanId }
2301
+ });
2302
+ for await (const event of streamResponse.result) {
2303
+ const data = event?.data;
2304
+ if (!data) {
2305
+ continue;
2306
+ }
2307
+ if (!streamSpanId && typeof data.spanId === "string" && data.spanId) {
2308
+ streamSpanId = data.spanId;
2309
+ }
2310
+ const feedResult = assembler.feed({
2311
+ delta: data.delta,
2312
+ jsonPath: data.jsonPath
2313
+ });
2314
+ if (!feedResult) {
2315
+ continue;
2316
+ }
2317
+ const chunkPayload = {
2318
+ context,
2319
+ callType: "think",
2320
+ chunkData: {
2321
+ delta: data.delta,
2322
+ jsonPath: data.jsonPath ?? null,
2323
+ chunkType: data.chunkType ?? null
2324
+ },
2325
+ accumulated: feedResult.accumulated,
2326
+ fieldBuffers: feedResult.snapshot
2327
+ };
2328
+ await this.triggerHook(HookEvents.StreamChunk, chunkPayload);
2329
+ this.emitAgentEvent(HookEvents.StreamChunk, chunkPayload);
2330
+ }
2331
+ const fieldBuffers = assembler.snapshot();
2332
+ const endPayload = {
2333
+ context,
2334
+ callType: "think",
2335
+ fieldBuffers
2336
+ };
2337
+ await this.triggerHook(HookEvents.StreamEnd, endPayload);
2338
+ this.emitAgentEvent(HookEvents.StreamEnd, endPayload);
2339
+ const finalize = assembler.finalize();
2340
+ let decision;
2341
+ if (finalize.type === "structured" && finalize.structured) {
2342
+ decision = decisionSchema.parse(
2343
+ finalize.structured
2344
+ );
2345
+ } else {
2346
+ decision = decisionSchema.parse({
2347
+ reasoning: finalize.type === "root" ? finalize.rootText ?? "" : ""
2348
+ });
2349
+ }
2350
+ const usageTracked = await this.trackStreamingUsageBySpan(
2351
+ context,
2352
+ streamSpanId
2353
+ );
2354
+ if (!usageTracked) {
2355
+ context.updateUsage({
2356
+ requests: 1,
2357
+ inputTokens: 0,
2358
+ outputTokens: 0,
2359
+ totalTokens: 0,
2360
+ cost: { generation: 0, platform: 0, total: 0 }
2361
+ });
2362
+ }
2363
+ await this.triggerHook(HookEvents.LlmResponse, {
2364
+ context,
2365
+ callType: "think",
2366
+ response: streamResponse,
2367
+ parsed: decision
2368
+ });
2369
+ if (streamSpanId) {
2370
+ await this.opperClient.updateSpan(streamSpanId, void 0, {
2371
+ name: "think"
2372
+ });
2373
+ }
2374
+ await this.triggerHook(HookEvents.ThinkEnd, {
2375
+ context,
2376
+ thought: {
2377
+ reasoning: decision.reasoning,
2378
+ userMessage: decision.userMessage
2379
+ }
2380
+ });
2381
+ this.log("Think result", {
2382
+ reasoning: decision.reasoning,
2383
+ toolCalls: decision.toolCalls.length,
2384
+ memoryReads: decision.memoryReads?.length ?? 0,
2385
+ memoryWrites: Object.keys(decision.memoryUpdates ?? {}).length
2386
+ });
2387
+ const resultPayload = {
2388
+ decision
2389
+ };
2390
+ if (streamSpanId) {
2391
+ resultPayload.spanId = streamSpanId;
2392
+ }
2393
+ return resultPayload;
2394
+ } catch (error) {
2395
+ await this.triggerHook(HookEvents.StreamError, {
2396
+ context,
2397
+ callType: "think",
2398
+ error
2399
+ });
2400
+ this.emitAgentEvent(HookEvents.StreamError, {
2401
+ context,
2402
+ callType: "think",
2403
+ error
2404
+ });
2405
+ this.logger.error("Think step failed", error);
2406
+ throw new Error(
2407
+ `Think step failed: ${error instanceof Error ? error.message : String(error)}`
2408
+ );
2409
+ }
2410
+ }
1766
2411
  /**
1767
2412
  * Build static instructions for the think step
1768
2413
  */
@@ -1773,12 +2418,22 @@ YOUR TASK:
1773
2418
  1. Analyze the current situation
1774
2419
  2. Decide if the goal is complete or more actions are needed
1775
2420
  3. If more actions needed: specify tools to call
1776
- 4. If goal complete: return empty tool_calls list
2421
+ 4. If goal complete:
2422
+ - Set isComplete=true
2423
+ - Provide the complete answer/output in finalResult
2424
+ - Leave toolCalls empty
1777
2425
 
1778
2426
  IMPORTANT:
1779
- - Return empty toolCalls array when task is COMPLETE
2427
+ - When task is COMPLETE, you MUST set isComplete=true AND provide finalResult
2428
+ - The finalResult should be a complete, well-structured answer based on all work done
1780
2429
  - Only use available tools
1781
- - Provide clear reasoning for each decision`;
2430
+ - Provide clear reasoning for each decision
2431
+ - If an outputSchema was specified, ensure finalResult matches that schema
2432
+
2433
+ USER MESSAGE:
2434
+ - Always provide a brief, user-friendly status in userMessage
2435
+ - This message is shown to users to indicate progress (e.g., "Searching for weather data...", "Calculating results...", "Done!")
2436
+ - Keep it concise and informative`;
1782
2437
  if (this.enableMemory) {
1783
2438
  instructions += `
1784
2439
 
@@ -1862,9 +2517,14 @@ The memory you write persists across all process() calls on this agent.`;
1862
2517
  this.log(`Action: ${toolCall.toolName}`, {
1863
2518
  parameters: toolCall.arguments
1864
2519
  });
2520
+ const startTime = /* @__PURE__ */ new Date();
2521
+ const tool2 = this.tools.get(toolCall.toolName);
2522
+ const isAgentTool = tool2?.metadata?.["isAgent"] === true;
2523
+ const spanType = isAgentTool ? "\u{1F916} agent" : "\u{1F527} tool";
1865
2524
  const toolSpan = await this.opperClient.createSpan({
1866
2525
  name: `tool_${toolCall.toolName}`,
1867
2526
  input: toolCall.arguments,
2527
+ type: spanType,
1868
2528
  ...parentSpanId ? { parentSpanId } : context.parentSpanId ? { parentSpanId: context.parentSpanId } : {}
1869
2529
  });
1870
2530
  try {
@@ -1874,11 +2534,20 @@ The memory you write persists across all process() calls on this agent.`;
1874
2534
  context,
1875
2535
  { spanId: toolSpan.id }
1876
2536
  );
2537
+ const endTime = /* @__PURE__ */ new Date();
2538
+ const durationMs = endTime.getTime() - startTime.getTime();
1877
2539
  if (result.success) {
1878
- await this.opperClient.updateSpan(toolSpan.id, result.output);
2540
+ await this.opperClient.updateSpan(toolSpan.id, result.output, {
2541
+ startTime,
2542
+ endTime,
2543
+ meta: { durationMs }
2544
+ });
1879
2545
  } else {
1880
2546
  await this.opperClient.updateSpan(toolSpan.id, void 0, {
1881
- error: result.error instanceof Error ? result.error.message : String(result.error)
2547
+ error: result.error instanceof Error ? result.error.message : String(result.error),
2548
+ startTime,
2549
+ endTime,
2550
+ meta: { durationMs }
1882
2551
  });
1883
2552
  }
1884
2553
  const summary = {
@@ -1893,12 +2562,18 @@ The memory you write persists across all process() calls on this agent.`;
1893
2562
  this.log(
1894
2563
  `Tool ${toolCall.toolName} ${result.success ? "succeeded" : "failed"}`,
1895
2564
  {
1896
- success: result.success
2565
+ success: result.success,
2566
+ durationMs
1897
2567
  }
1898
2568
  );
1899
2569
  } catch (error) {
2570
+ const endTime = /* @__PURE__ */ new Date();
2571
+ const durationMs = endTime.getTime() - startTime.getTime();
1900
2572
  await this.opperClient.updateSpan(toolSpan.id, void 0, {
1901
- error: error instanceof Error ? error.message : String(error)
2573
+ error: error instanceof Error ? error.message : String(error),
2574
+ startTime,
2575
+ endTime,
2576
+ meta: { durationMs }
1902
2577
  });
1903
2578
  const summary = {
1904
2579
  toolName: toolCall.toolName,
@@ -1907,7 +2582,8 @@ The memory you write persists across all process() calls on this agent.`;
1907
2582
  };
1908
2583
  results.push(ToolExecutionSummarySchema.parse(summary));
1909
2584
  this.logger.warn(`Tool ${toolCall.toolName} threw error`, {
1910
- error: error instanceof Error ? error.message : String(error)
2585
+ error: error instanceof Error ? error.message : String(error),
2586
+ durationMs
1911
2587
  });
1912
2588
  }
1913
2589
  }
@@ -1936,6 +2612,7 @@ The memory you write persists across all process() calls on this agent.`;
1936
2612
  const spanParentId = parentSpanId ?? context.parentSpanId ?? void 0;
1937
2613
  const summaries = [];
1938
2614
  if (hasReads) {
2615
+ const startTime = /* @__PURE__ */ new Date();
1939
2616
  try {
1940
2617
  const keySet = new Set(
1941
2618
  decision.memoryReads.filter(
@@ -1947,10 +2624,17 @@ The memory you write persists across all process() calls on this agent.`;
1947
2624
  const memoryReadSpan = await this.opperClient.createSpan({
1948
2625
  name: "memory_read",
1949
2626
  input: keys,
2627
+ type: "\u{1F9E0} memory",
1950
2628
  ...spanParentId && { parentSpanId: spanParentId }
1951
2629
  });
1952
2630
  const memoryData = await this.memory.read(keys);
1953
- await this.opperClient.updateSpan(memoryReadSpan.id, memoryData);
2631
+ const endTime = /* @__PURE__ */ new Date();
2632
+ const durationMs = endTime.getTime() - startTime.getTime();
2633
+ await this.opperClient.updateSpan(memoryReadSpan.id, memoryData, {
2634
+ startTime,
2635
+ endTime,
2636
+ meta: { durationMs }
2637
+ });
1954
2638
  context.setMetadata("current_memory", memoryData);
1955
2639
  this.log(`Loaded ${Object.keys(memoryData).length} memory entries`, {
1956
2640
  keys
@@ -1989,10 +2673,12 @@ The memory you write persists across all process() calls on this agent.`;
1989
2673
  }
1990
2674
  }
1991
2675
  if (hasWrites) {
2676
+ const startTime = /* @__PURE__ */ new Date();
1992
2677
  try {
1993
2678
  const memoryWriteSpan = await this.opperClient.createSpan({
1994
2679
  name: "memory_write",
1995
2680
  input: updateEntries.map(([key]) => key),
2681
+ type: "\u{1F9E0} memory",
1996
2682
  ...spanParentId && { parentSpanId: spanParentId }
1997
2683
  });
1998
2684
  for (const [key, update] of updateEntries) {
@@ -2009,9 +2695,16 @@ The memory you write persists across all process() calls on this agent.`;
2009
2695
  value: castUpdate.value
2010
2696
  });
2011
2697
  }
2698
+ const endTime = /* @__PURE__ */ new Date();
2699
+ const durationMs = endTime.getTime() - startTime.getTime();
2012
2700
  await this.opperClient.updateSpan(
2013
2701
  memoryWriteSpan.id,
2014
- `Successfully wrote ${updateEntries.length} keys`
2702
+ `Successfully wrote ${updateEntries.length} keys`,
2703
+ {
2704
+ startTime,
2705
+ endTime,
2706
+ meta: { durationMs }
2707
+ }
2015
2708
  );
2016
2709
  this.log(`Wrote ${updateEntries.length} memory entries`);
2017
2710
  summaries.push(
@@ -2076,9 +2769,18 @@ The memory you write persists across all process() calls on this agent.`;
2076
2769
  };
2077
2770
  const instructions = `Generate the final result based on the execution history.
2078
2771
  Follow any instructions provided for formatting and style.`;
2772
+ if (this.enableStreaming) {
2773
+ return this.generateFinalResultStreaming(
2774
+ context,
2775
+ finalContext,
2776
+ instructions
2777
+ );
2778
+ }
2079
2779
  try {
2780
+ const sanitizedName = this.name.toLowerCase().replace(/[\s-]/g, "_");
2781
+ const functionName = `generate_final_result_${sanitizedName}`;
2080
2782
  const callOptions = {
2081
- name: "generate_final_result",
2783
+ name: functionName,
2082
2784
  instructions,
2083
2785
  input: finalContext,
2084
2786
  model: this.model
@@ -2111,6 +2813,172 @@ Follow any instructions provided for formatting and style.`;
2111
2813
  );
2112
2814
  }
2113
2815
  }
2816
+ async generateFinalResultStreaming(context, finalContext, instructions) {
2817
+ const assembler = createStreamAssembler(
2818
+ this.outputSchema ? { schema: this.outputSchema } : void 0
2819
+ );
2820
+ let streamSpanId;
2821
+ await this.triggerHook(HookEvents.StreamStart, {
2822
+ context,
2823
+ callType: "final_result"
2824
+ });
2825
+ this.emitAgentEvent(HookEvents.StreamStart, {
2826
+ context,
2827
+ callType: "final_result"
2828
+ });
2829
+ try {
2830
+ const sanitizedName = this.name.toLowerCase().replace(/[\s-]/g, "_");
2831
+ const functionName = `generate_final_result_${sanitizedName}`;
2832
+ const streamResponse = await this.opperClient.stream({
2833
+ name: functionName,
2834
+ instructions,
2835
+ input: finalContext,
2836
+ model: this.model,
2837
+ ...context.parentSpanId && { parentSpanId: context.parentSpanId },
2838
+ ...this.outputSchema && { outputSchema: this.outputSchema }
2839
+ });
2840
+ for await (const event of streamResponse.result) {
2841
+ const data = event?.data;
2842
+ if (!data) {
2843
+ continue;
2844
+ }
2845
+ if (!streamSpanId && typeof data.spanId === "string" && data.spanId) {
2846
+ streamSpanId = data.spanId;
2847
+ }
2848
+ const feedResult = assembler.feed({
2849
+ delta: data.delta,
2850
+ jsonPath: data.jsonPath
2851
+ });
2852
+ if (!feedResult) {
2853
+ continue;
2854
+ }
2855
+ const chunkPayload = {
2856
+ context,
2857
+ callType: "final_result",
2858
+ chunkData: {
2859
+ delta: data.delta,
2860
+ jsonPath: data.jsonPath ?? null,
2861
+ chunkType: data.chunkType ?? null
2862
+ },
2863
+ accumulated: feedResult.accumulated,
2864
+ fieldBuffers: feedResult.snapshot
2865
+ };
2866
+ await this.triggerHook(HookEvents.StreamChunk, chunkPayload);
2867
+ this.emitAgentEvent(HookEvents.StreamChunk, chunkPayload);
2868
+ }
2869
+ const fieldBuffers = assembler.snapshot();
2870
+ const endPayload = {
2871
+ context,
2872
+ callType: "final_result",
2873
+ fieldBuffers
2874
+ };
2875
+ await this.triggerHook(HookEvents.StreamEnd, endPayload);
2876
+ this.emitAgentEvent(HookEvents.StreamEnd, endPayload);
2877
+ const finalize = assembler.finalize();
2878
+ let result;
2879
+ if (this.outputSchema) {
2880
+ if (finalize.type !== "structured" || finalize.structured === void 0) {
2881
+ throw new Error(
2882
+ "Streaming response did not provide structured data for the configured output schema."
2883
+ );
2884
+ }
2885
+ result = this.outputSchema.parse(
2886
+ finalize.structured
2887
+ );
2888
+ } else if (finalize.type === "root") {
2889
+ result = finalize.rootText ?? "";
2890
+ } else if (finalize.type === "structured" && finalize.structured) {
2891
+ result = JSON.stringify(finalize.structured);
2892
+ } else {
2893
+ result = "";
2894
+ }
2895
+ const usageTracked = await this.trackStreamingUsageBySpan(
2896
+ context,
2897
+ streamSpanId
2898
+ );
2899
+ if (!usageTracked) {
2900
+ context.updateUsage({
2901
+ requests: 1,
2902
+ inputTokens: 0,
2903
+ outputTokens: 0,
2904
+ totalTokens: 0,
2905
+ cost: { generation: 0, platform: 0, total: 0 }
2906
+ });
2907
+ }
2908
+ await this.triggerHook(HookEvents.LlmResponse, {
2909
+ context,
2910
+ callType: "final_result",
2911
+ response: streamResponse,
2912
+ parsed: result
2913
+ });
2914
+ this.log(
2915
+ this.outputSchema ? "Final result generated (streaming, schema-validated)" : "Final result generated (streaming)"
2916
+ );
2917
+ return result;
2918
+ } catch (error) {
2919
+ await this.triggerHook(HookEvents.StreamError, {
2920
+ context,
2921
+ callType: "final_result",
2922
+ error
2923
+ });
2924
+ this.emitAgentEvent(HookEvents.StreamError, {
2925
+ context,
2926
+ callType: "final_result",
2927
+ error
2928
+ });
2929
+ this.logger.error("Failed to generate final result", error);
2930
+ throw new Error(
2931
+ `Failed to generate final result: ${error instanceof Error ? error.message : String(error)}`
2932
+ );
2933
+ }
2934
+ }
2935
+ async trackStreamingUsageBySpan(context, spanId) {
2936
+ if (!spanId) {
2937
+ return false;
2938
+ }
2939
+ try {
2940
+ const span = await this.opperClient.getClient().spans.get(spanId);
2941
+ const traceId = span?.traceId ?? span?.trace_id;
2942
+ if (!traceId) {
2943
+ return false;
2944
+ }
2945
+ const trace = await this.opperClient.getClient().traces.get(traceId);
2946
+ const spans = trace?.spans;
2947
+ if (!Array.isArray(spans)) {
2948
+ return false;
2949
+ }
2950
+ for (const entry of spans) {
2951
+ const entryId = entry?.id ?? entry["id"];
2952
+ if (entryId !== spanId) {
2953
+ continue;
2954
+ }
2955
+ const data = entry?.data;
2956
+ if (!data) {
2957
+ continue;
2958
+ }
2959
+ const record = data;
2960
+ const primaryTotal = record["totalTokens"];
2961
+ const fallbackTotal = record["total_tokens"];
2962
+ const totalTokensRaw = typeof primaryTotal === "number" && Number.isFinite(primaryTotal) ? primaryTotal : typeof fallbackTotal === "number" && Number.isFinite(fallbackTotal) ? fallbackTotal : void 0;
2963
+ if (totalTokensRaw !== void 0) {
2964
+ context.updateUsage({
2965
+ requests: 1,
2966
+ inputTokens: 0,
2967
+ outputTokens: 0,
2968
+ totalTokens: totalTokensRaw,
2969
+ cost: { generation: 0, platform: 0, total: 0 }
2970
+ });
2971
+ return true;
2972
+ }
2973
+ }
2974
+ } catch (error) {
2975
+ this.logger.warn("Could not fetch streaming usage", {
2976
+ spanId,
2977
+ error: error instanceof Error ? error.message : String(error)
2978
+ });
2979
+ }
2980
+ return false;
2981
+ }
2114
2982
  /**
2115
2983
  * Log helper
2116
2984
  */
@@ -2327,7 +3195,7 @@ var createMCPServerConfig = MCPconfig;
2327
3195
 
2328
3196
  // src/mcp/provider.ts
2329
3197
  init_tool();
2330
- var isRecord = (value) => typeof value === "object" && value !== null;
3198
+ var isRecord2 = (value) => typeof value === "object" && value !== null;
2331
3199
  var MCPToolProvider = class {
2332
3200
  configs;
2333
3201
  namePrefix;
@@ -2418,7 +3286,7 @@ var MCPToolProvider = class {
2418
3286
  },
2419
3287
  execute: async (input, context) => {
2420
3288
  const startedAt = Date.now();
2421
- const args = isRecord(input) ? input : input === void 0 ? {} : { value: input };
3289
+ const args = isRecord2(input) ? input : input === void 0 ? {} : { value: input };
2422
3290
  try {
2423
3291
  const result = await client.callTool(mcpTool.name, args);
2424
3292
  return ToolResultFactory.success(toolName, result, {
@@ -2876,6 +3744,6 @@ var ToolRunner = class {
2876
3744
  }
2877
3745
  };
2878
3746
 
2879
- 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, generateAgentFlowDiagram, getDefaultLogger, getSchemaDefault, isSchemaValid, isToolProvider, mcp, mergeSchemaDefaults, normalizeToolEntries, ok, schemaToJson, setDefaultLogger, tool, validateSchema, validateToolInput };
3747
+ export { Agent, AgentContext, AgentDecisionSchema, AgentEventEmitter, AgentEvents, BaseAgent, ConsoleLogger, DEFAULT_MODEL, DEFAULT_RETRY_CONFIG, ExecutionCycleSchema, HookEvents, HookManager, InMemoryStore, LogLevel, MCPClient, MCPServerConfigSchema, MCPToolProvider, MCPconfig, MemoryEntryMetadataSchema, MemoryEntrySchema, MemoryUpdateSchema, OpperClient, Result, STREAM_ROOT_PATH, SchemaValidationError, SilentLogger, StreamAssembler, ThoughtSchema, ToolCallRecordSchema, ToolCallSchema, ToolExecutionSummarySchema, ToolMetadataSchema, ToolResultFactory, ToolResultFailureSchema, ToolResultSchema, ToolResultSuccessSchema, ToolRunner, UsageSchema, coerceToolDefinition, createAgentDecisionWithOutputSchema, createFunctionTool, createHookManager, createInMemoryStore, createMCPServerConfig, createOpperClient, createStreamAssembler, createToolCallRecord, err, extractTools, generateAgentFlowDiagram, getDefaultLogger, getSchemaDefault, isSchemaValid, isToolProvider, mcp, mergeSchemaDefaults, normalizeToolEntries, ok, schemaToJson, setDefaultLogger, tool, validateSchema, validateToolInput };
2880
3748
  //# sourceMappingURL=index.js.map
2881
3749
  //# sourceMappingURL=index.js.map