@opperai/agents 0.1.3 → 0.2.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 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
  */
@@ -864,12 +952,26 @@ var BaseAgent = class {
864
952
  this.inputSchema = config.inputSchema;
865
953
  this.outputSchema = config.outputSchema;
866
954
  this.enableMemory = config.enableMemory ?? false;
955
+ this.enableStreaming = config.enableStreaming ?? false;
867
956
  this.metadata = { ...config.metadata ?? {} };
868
957
  this.hooks = new HookManager();
958
+ this.events = new AgentEventEmitter();
869
959
  this.tools = /* @__PURE__ */ new Map();
870
960
  this.baseTools = /* @__PURE__ */ new Map();
871
961
  this.toolProviders = /* @__PURE__ */ new Set();
872
962
  this.providerToolRegistry = /* @__PURE__ */ new Map();
963
+ if (config.onStreamStart) {
964
+ this.on(HookEvents.StreamStart, config.onStreamStart);
965
+ }
966
+ if (config.onStreamChunk) {
967
+ this.on(HookEvents.StreamChunk, config.onStreamChunk);
968
+ }
969
+ if (config.onStreamEnd) {
970
+ this.on(HookEvents.StreamEnd, config.onStreamEnd);
971
+ }
972
+ if (config.onStreamError) {
973
+ this.on(HookEvents.StreamError, config.onStreamError);
974
+ }
873
975
  this.opperConfig = {
874
976
  apiKey: config.opperConfig?.apiKey ?? process.env["OPPER_API_KEY"],
875
977
  baseUrl: config.opperConfig?.baseUrl,
@@ -1051,6 +1153,35 @@ var BaseAgent = class {
1051
1153
  registerHook(event, handler) {
1052
1154
  return this.hooks.on(event, handler);
1053
1155
  }
1156
+ /**
1157
+ * Register an event listener.
1158
+ *
1159
+ * @param event - Event name
1160
+ * @param listener - Listener callback
1161
+ * @returns Cleanup function to unregister the listener
1162
+ */
1163
+ on(event, listener) {
1164
+ return this.events.on(event, listener);
1165
+ }
1166
+ /**
1167
+ * Register a one-time event listener that removes itself after the first call.
1168
+ *
1169
+ * @param event - Event name
1170
+ * @param listener - Listener callback
1171
+ * @returns Cleanup function (no-op once listener fires)
1172
+ */
1173
+ once(event, listener) {
1174
+ return this.events.once(event, listener);
1175
+ }
1176
+ /**
1177
+ * Remove a previously registered event listener.
1178
+ *
1179
+ * @param event - Event name
1180
+ * @param listener - Listener callback to remove
1181
+ */
1182
+ off(event, listener) {
1183
+ this.events.off(event, listener);
1184
+ }
1054
1185
  /**
1055
1186
  * Trigger a hook event with a payload.
1056
1187
  * Swallows errors to prevent hook failures from breaking agent execution.
@@ -1065,6 +1196,15 @@ var BaseAgent = class {
1065
1196
  console.warn(`Hook error for event ${event}:`, error);
1066
1197
  }
1067
1198
  }
1199
+ /**
1200
+ * Emit a runtime event to listeners.
1201
+ *
1202
+ * @param event - Event name
1203
+ * @param payload - Event payload
1204
+ */
1205
+ emitAgentEvent(event, payload) {
1206
+ this.events.emit(event, payload);
1207
+ }
1068
1208
  /**
1069
1209
  * Execute a tool with proper context, hooks, and error handling.
1070
1210
  *
@@ -1338,7 +1478,7 @@ var ToolExecutionSummarySchema = z.object({
1338
1478
 
1339
1479
  // package.json
1340
1480
  var package_default = {
1341
- version: "0.1.3"};
1481
+ version: "0.2.0"};
1342
1482
 
1343
1483
  // src/utils/version.ts
1344
1484
  var SDK_NAME = "@opperai/agents";
@@ -1401,7 +1541,7 @@ var OpperClient = class {
1401
1541
  return this.withRetry(async () => {
1402
1542
  const inputSchema = this.toJsonSchema(options.inputSchema);
1403
1543
  const outputSchema = this.toJsonSchema(options.outputSchema);
1404
- const response = await this.client.call({
1544
+ const callPayload = {
1405
1545
  name: options.name,
1406
1546
  instructions: options.instructions,
1407
1547
  input: options.input,
@@ -1409,7 +1549,8 @@ var OpperClient = class {
1409
1549
  ...outputSchema && { outputSchema },
1410
1550
  ...options.model && { model: options.model },
1411
1551
  ...options.parentSpanId && { parentSpanId: options.parentSpanId }
1412
- });
1552
+ };
1553
+ const response = options.signal ? await this.client.call(callPayload, { signal: options.signal }) : await this.client.call(callPayload);
1413
1554
  const usagePayload = extractUsage(response);
1414
1555
  const costPayload = extractCost(response);
1415
1556
  const usage = {
@@ -1427,6 +1568,36 @@ var OpperClient = class {
1427
1568
  return result;
1428
1569
  }, options.name);
1429
1570
  }
1571
+ /**
1572
+ * Stream a call to Opper with retry logic
1573
+ */
1574
+ async stream(options) {
1575
+ return this.withRetry(async () => {
1576
+ const inputSchema = this.toJsonSchema(options.inputSchema);
1577
+ const outputSchema = this.toJsonSchema(options.outputSchema);
1578
+ const streamPayload = {
1579
+ name: options.name,
1580
+ instructions: options.instructions,
1581
+ input: options.input,
1582
+ ...inputSchema && { inputSchema },
1583
+ ...outputSchema && { outputSchema },
1584
+ ...options.model && { model: options.model },
1585
+ ...options.parentSpanId && { parentSpanId: options.parentSpanId }
1586
+ };
1587
+ const response = options.signal ? await this.client.stream(streamPayload, { signal: options.signal }) : await this.client.stream(streamPayload);
1588
+ const iterable = {
1589
+ async *[Symbol.asyncIterator]() {
1590
+ for await (const event of response.result) {
1591
+ yield event;
1592
+ }
1593
+ }
1594
+ };
1595
+ return {
1596
+ headers: response.headers,
1597
+ result: iterable
1598
+ };
1599
+ }, `stream:${options.name}`);
1600
+ }
1430
1601
  /**
1431
1602
  * Create a span for tracing
1432
1603
  */
@@ -1595,6 +1766,189 @@ var mergeSchemaDefaults = (schema, value) => {
1595
1766
  return validateSchema(schema, value);
1596
1767
  };
1597
1768
 
1769
+ // src/utils/streaming.ts
1770
+ var STREAM_ROOT_PATH = "_root";
1771
+ var NUMERIC_TOKEN_PATTERN = /^\d+$/;
1772
+ var BRACKET_TOKEN_PATTERN = /\[(\d+)\]/g;
1773
+ var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1774
+ var coercePrimitive = (value) => {
1775
+ const trimmed = value.trim();
1776
+ if (trimmed.length === 0) {
1777
+ return value;
1778
+ }
1779
+ const lower = trimmed.toLowerCase();
1780
+ if (lower === "true") {
1781
+ return true;
1782
+ }
1783
+ if (lower === "false") {
1784
+ return false;
1785
+ }
1786
+ if (lower === "null") {
1787
+ return null;
1788
+ }
1789
+ if (/^-?\d+$/.test(trimmed)) {
1790
+ const intValue = Number.parseInt(trimmed, 10);
1791
+ return Number.isNaN(intValue) ? value : intValue;
1792
+ }
1793
+ if (/^-?\d+\.\d+$/.test(trimmed)) {
1794
+ const floatValue = Number.parseFloat(trimmed);
1795
+ return Number.isNaN(floatValue) ? value : floatValue;
1796
+ }
1797
+ return value;
1798
+ };
1799
+ var toDisplayString = (value) => {
1800
+ if (value === null || value === void 0) {
1801
+ return "";
1802
+ }
1803
+ if (typeof value === "string") {
1804
+ return value;
1805
+ }
1806
+ return String(value);
1807
+ };
1808
+ var parsePathSegments = (path) => {
1809
+ return path.replace(BRACKET_TOKEN_PATTERN, (_, index) => `.${index}`).split(".").map((segment) => segment.trim()).filter((segment) => segment.length > 0);
1810
+ };
1811
+ var setNestedValue = (target, path, value) => {
1812
+ const segments = parsePathSegments(path);
1813
+ if (segments.length === 0) {
1814
+ return;
1815
+ }
1816
+ let current = target;
1817
+ for (let i = 0; i < segments.length - 1; i++) {
1818
+ const segment = segments[i];
1819
+ if (segment === void 0) {
1820
+ return;
1821
+ }
1822
+ const existing = current[segment];
1823
+ if (!isRecord(existing)) {
1824
+ const nextLevel = {};
1825
+ current[segment] = nextLevel;
1826
+ current = nextLevel;
1827
+ continue;
1828
+ }
1829
+ current = existing;
1830
+ }
1831
+ const lastSegment = segments[segments.length - 1];
1832
+ if (lastSegment === void 0) {
1833
+ return;
1834
+ }
1835
+ current[lastSegment] = value;
1836
+ };
1837
+ var normalizeIndexed = (value) => {
1838
+ if (Array.isArray(value)) {
1839
+ return value.map((item) => normalizeIndexed(item));
1840
+ }
1841
+ if (!isRecord(value)) {
1842
+ return value;
1843
+ }
1844
+ const normalizedEntries = {};
1845
+ for (const [key, entryValue] of Object.entries(value)) {
1846
+ normalizedEntries[key] = normalizeIndexed(entryValue);
1847
+ }
1848
+ const keys = Object.keys(normalizedEntries);
1849
+ if (keys.length > 0 && keys.every((key) => NUMERIC_TOKEN_PATTERN.test(key))) {
1850
+ const parsedIndices = keys.map((key) => Number.parseInt(key, 10)).filter((index) => Number.isFinite(index) && index >= 0);
1851
+ if (parsedIndices.length === 0) {
1852
+ return normalizedEntries;
1853
+ }
1854
+ const maxIndex = Math.max(...parsedIndices);
1855
+ const result = new Array(maxIndex + 1).fill(void 0);
1856
+ for (const [key, entryValue] of Object.entries(normalizedEntries)) {
1857
+ const index = Number.parseInt(key, 10);
1858
+ if (!Number.isFinite(index) || index < 0) {
1859
+ continue;
1860
+ }
1861
+ result[index] = entryValue;
1862
+ }
1863
+ return result.map(
1864
+ (item) => item === void 0 ? null : item
1865
+ );
1866
+ }
1867
+ return normalizedEntries;
1868
+ };
1869
+ var resolveFieldValue = (path, displayBuffers, valueBuffers) => {
1870
+ const values = valueBuffers.get(path) ?? [];
1871
+ if (values.length === 0) {
1872
+ return displayBuffers.get(path) ?? "";
1873
+ }
1874
+ const last = values[values.length - 1];
1875
+ if (typeof last === "number" || typeof last === "boolean") {
1876
+ return last;
1877
+ }
1878
+ if (last === null) {
1879
+ return null;
1880
+ }
1881
+ const joined = displayBuffers.get(path) ?? values.map((value) => toDisplayString(value)).join("");
1882
+ return coercePrimitive(joined);
1883
+ };
1884
+ var StreamAssembler = class {
1885
+ displayBuffers = /* @__PURE__ */ new Map();
1886
+ valueBuffers = /* @__PURE__ */ new Map();
1887
+ schema;
1888
+ constructor(options = {}) {
1889
+ this.schema = options.schema;
1890
+ }
1891
+ feed(chunk) {
1892
+ const { delta } = chunk;
1893
+ if (delta === null || delta === void 0) {
1894
+ return null;
1895
+ }
1896
+ const path = chunk.jsonPath === null || chunk.jsonPath === void 0 ? STREAM_ROOT_PATH : chunk.jsonPath;
1897
+ const existingDisplay = this.displayBuffers.get(path) ?? "";
1898
+ const nextDisplay = `${existingDisplay}${toDisplayString(delta)}`;
1899
+ this.displayBuffers.set(path, nextDisplay);
1900
+ const existingValues = this.valueBuffers.get(path) ?? [];
1901
+ existingValues.push(delta);
1902
+ this.valueBuffers.set(path, existingValues);
1903
+ return {
1904
+ path,
1905
+ accumulated: nextDisplay,
1906
+ snapshot: this.snapshot()
1907
+ };
1908
+ }
1909
+ snapshot() {
1910
+ return Object.fromEntries(this.displayBuffers.entries());
1911
+ }
1912
+ hasStructuredFields() {
1913
+ const keys = Array.from(this.displayBuffers.keys());
1914
+ return keys.some((key) => key !== STREAM_ROOT_PATH);
1915
+ }
1916
+ finalize() {
1917
+ if (this.displayBuffers.size === 0) {
1918
+ return { type: "empty" };
1919
+ }
1920
+ if (!this.hasStructuredFields()) {
1921
+ const root = this.displayBuffers.get(STREAM_ROOT_PATH) ?? "";
1922
+ return { type: "root", rootText: root };
1923
+ }
1924
+ const structured = this.reconstructStructured();
1925
+ let coerced = structured;
1926
+ if (this.schema !== void 0) {
1927
+ try {
1928
+ coerced = this.schema.parse(structured);
1929
+ } catch {
1930
+ coerced = structured;
1931
+ }
1932
+ }
1933
+ return { type: "structured", structured: coerced };
1934
+ }
1935
+ getFieldBuffers() {
1936
+ return new Map(this.displayBuffers);
1937
+ }
1938
+ reconstructStructured() {
1939
+ const result = {};
1940
+ for (const path of this.valueBuffers.keys()) {
1941
+ if (path === STREAM_ROOT_PATH) {
1942
+ continue;
1943
+ }
1944
+ const value = resolveFieldValue(path, this.displayBuffers, this.valueBuffers);
1945
+ setNestedValue(result, path, value);
1946
+ }
1947
+ return normalizeIndexed(result);
1948
+ }
1949
+ };
1950
+ var createStreamAssembler = (options) => new StreamAssembler(options);
1951
+
1598
1952
  // src/core/agent.ts
1599
1953
  var isToolSuccessResult = (value) => {
1600
1954
  if (typeof value !== "object" || value === null) {
@@ -1721,6 +2075,9 @@ var Agent = class extends BaseAgent {
1721
2075
  const spanName = "think";
1722
2076
  this.log("Think step", { iteration: context.iteration });
1723
2077
  await this.triggerHook(HookEvents.LlmCall, { context, callType: "think" });
2078
+ if (this.enableStreaming) {
2079
+ return this.thinkStreaming(input, context);
2080
+ }
1724
2081
  try {
1725
2082
  const instructions = this.buildThinkInstructions();
1726
2083
  const thinkContext = await this.buildThinkContext(input, context);
@@ -1763,6 +2120,132 @@ var Agent = class extends BaseAgent {
1763
2120
  );
1764
2121
  }
1765
2122
  }
2123
+ async thinkStreaming(input, context) {
2124
+ const spanName = "think";
2125
+ const instructions = this.buildThinkInstructions();
2126
+ const thinkContext = await this.buildThinkContext(input, context);
2127
+ const assembler = createStreamAssembler({
2128
+ schema: AgentDecisionSchema
2129
+ });
2130
+ let streamSpanId;
2131
+ await this.triggerHook(HookEvents.StreamStart, {
2132
+ context,
2133
+ callType: "think"
2134
+ });
2135
+ this.emitAgentEvent(HookEvents.StreamStart, {
2136
+ context,
2137
+ callType: "think"
2138
+ });
2139
+ try {
2140
+ const streamResponse = await this.opperClient.stream({
2141
+ name: spanName,
2142
+ instructions,
2143
+ input: thinkContext,
2144
+ outputSchema: AgentDecisionSchema,
2145
+ model: this.model,
2146
+ ...context.parentSpanId && { parentSpanId: context.parentSpanId }
2147
+ });
2148
+ for await (const event of streamResponse.result) {
2149
+ const data = event?.data;
2150
+ if (!data) {
2151
+ continue;
2152
+ }
2153
+ if (!streamSpanId && typeof data.spanId === "string" && data.spanId) {
2154
+ streamSpanId = data.spanId;
2155
+ }
2156
+ const feedResult = assembler.feed({
2157
+ delta: data.delta,
2158
+ jsonPath: data.jsonPath
2159
+ });
2160
+ if (!feedResult) {
2161
+ continue;
2162
+ }
2163
+ const chunkPayload = {
2164
+ context,
2165
+ callType: "think",
2166
+ chunkData: {
2167
+ delta: data.delta,
2168
+ jsonPath: data.jsonPath ?? null,
2169
+ chunkType: data.chunkType ?? null
2170
+ },
2171
+ accumulated: feedResult.accumulated,
2172
+ fieldBuffers: feedResult.snapshot
2173
+ };
2174
+ await this.triggerHook(HookEvents.StreamChunk, chunkPayload);
2175
+ this.emitAgentEvent(HookEvents.StreamChunk, chunkPayload);
2176
+ }
2177
+ const fieldBuffers = assembler.snapshot();
2178
+ const endPayload = {
2179
+ context,
2180
+ callType: "think",
2181
+ fieldBuffers
2182
+ };
2183
+ await this.triggerHook(HookEvents.StreamEnd, endPayload);
2184
+ this.emitAgentEvent(HookEvents.StreamEnd, endPayload);
2185
+ const finalize = assembler.finalize();
2186
+ let decision;
2187
+ if (finalize.type === "structured" && finalize.structured) {
2188
+ decision = AgentDecisionSchema.parse(
2189
+ finalize.structured
2190
+ );
2191
+ } else {
2192
+ decision = AgentDecisionSchema.parse({
2193
+ reasoning: finalize.type === "root" ? finalize.rootText ?? "" : ""
2194
+ });
2195
+ }
2196
+ const usageTracked = await this.trackStreamingUsageBySpan(
2197
+ context,
2198
+ streamSpanId
2199
+ );
2200
+ if (!usageTracked) {
2201
+ context.updateUsage({
2202
+ requests: 1,
2203
+ inputTokens: 0,
2204
+ outputTokens: 0,
2205
+ totalTokens: 0,
2206
+ cost: { generation: 0, platform: 0, total: 0 }
2207
+ });
2208
+ }
2209
+ await this.triggerHook(HookEvents.LlmResponse, {
2210
+ context,
2211
+ callType: "think",
2212
+ response: streamResponse,
2213
+ parsed: decision
2214
+ });
2215
+ await this.triggerHook(HookEvents.ThinkEnd, {
2216
+ context,
2217
+ thought: { reasoning: decision.reasoning }
2218
+ });
2219
+ this.log("Think result", {
2220
+ reasoning: decision.reasoning,
2221
+ toolCalls: decision.toolCalls.length,
2222
+ memoryReads: decision.memoryReads?.length ?? 0,
2223
+ memoryWrites: Object.keys(decision.memoryUpdates ?? {}).length
2224
+ });
2225
+ const resultPayload = {
2226
+ decision
2227
+ };
2228
+ if (streamSpanId) {
2229
+ resultPayload.spanId = streamSpanId;
2230
+ }
2231
+ return resultPayload;
2232
+ } catch (error) {
2233
+ await this.triggerHook(HookEvents.StreamError, {
2234
+ context,
2235
+ callType: "think",
2236
+ error
2237
+ });
2238
+ this.emitAgentEvent(HookEvents.StreamError, {
2239
+ context,
2240
+ callType: "think",
2241
+ error
2242
+ });
2243
+ this.logger.error("Think step failed", error);
2244
+ throw new Error(
2245
+ `Think step failed: ${error instanceof Error ? error.message : String(error)}`
2246
+ );
2247
+ }
2248
+ }
1766
2249
  /**
1767
2250
  * Build static instructions for the think step
1768
2251
  */
@@ -2076,6 +2559,13 @@ The memory you write persists across all process() calls on this agent.`;
2076
2559
  };
2077
2560
  const instructions = `Generate the final result based on the execution history.
2078
2561
  Follow any instructions provided for formatting and style.`;
2562
+ if (this.enableStreaming) {
2563
+ return this.generateFinalResultStreaming(
2564
+ context,
2565
+ finalContext,
2566
+ instructions
2567
+ );
2568
+ }
2079
2569
  try {
2080
2570
  const callOptions = {
2081
2571
  name: "generate_final_result",
@@ -2111,6 +2601,170 @@ Follow any instructions provided for formatting and style.`;
2111
2601
  );
2112
2602
  }
2113
2603
  }
2604
+ async generateFinalResultStreaming(context, finalContext, instructions) {
2605
+ const assembler = createStreamAssembler(
2606
+ this.outputSchema ? { schema: this.outputSchema } : void 0
2607
+ );
2608
+ let streamSpanId;
2609
+ await this.triggerHook(HookEvents.StreamStart, {
2610
+ context,
2611
+ callType: "final_result"
2612
+ });
2613
+ this.emitAgentEvent(HookEvents.StreamStart, {
2614
+ context,
2615
+ callType: "final_result"
2616
+ });
2617
+ try {
2618
+ const streamResponse = await this.opperClient.stream({
2619
+ name: "generate_final_result",
2620
+ instructions,
2621
+ input: finalContext,
2622
+ model: this.model,
2623
+ ...context.parentSpanId && { parentSpanId: context.parentSpanId },
2624
+ ...this.outputSchema && { outputSchema: this.outputSchema }
2625
+ });
2626
+ for await (const event of streamResponse.result) {
2627
+ const data = event?.data;
2628
+ if (!data) {
2629
+ continue;
2630
+ }
2631
+ if (!streamSpanId && typeof data.spanId === "string" && data.spanId) {
2632
+ streamSpanId = data.spanId;
2633
+ }
2634
+ const feedResult = assembler.feed({
2635
+ delta: data.delta,
2636
+ jsonPath: data.jsonPath
2637
+ });
2638
+ if (!feedResult) {
2639
+ continue;
2640
+ }
2641
+ const chunkPayload = {
2642
+ context,
2643
+ callType: "final_result",
2644
+ chunkData: {
2645
+ delta: data.delta,
2646
+ jsonPath: data.jsonPath ?? null,
2647
+ chunkType: data.chunkType ?? null
2648
+ },
2649
+ accumulated: feedResult.accumulated,
2650
+ fieldBuffers: feedResult.snapshot
2651
+ };
2652
+ await this.triggerHook(HookEvents.StreamChunk, chunkPayload);
2653
+ this.emitAgentEvent(HookEvents.StreamChunk, chunkPayload);
2654
+ }
2655
+ const fieldBuffers = assembler.snapshot();
2656
+ const endPayload = {
2657
+ context,
2658
+ callType: "final_result",
2659
+ fieldBuffers
2660
+ };
2661
+ await this.triggerHook(HookEvents.StreamEnd, endPayload);
2662
+ this.emitAgentEvent(HookEvents.StreamEnd, endPayload);
2663
+ const finalize = assembler.finalize();
2664
+ let result;
2665
+ if (this.outputSchema) {
2666
+ if (finalize.type !== "structured" || finalize.structured === void 0) {
2667
+ throw new Error(
2668
+ "Streaming response did not provide structured data for the configured output schema."
2669
+ );
2670
+ }
2671
+ result = this.outputSchema.parse(
2672
+ finalize.structured
2673
+ );
2674
+ } else if (finalize.type === "root") {
2675
+ result = finalize.rootText ?? "";
2676
+ } else if (finalize.type === "structured" && finalize.structured) {
2677
+ result = JSON.stringify(finalize.structured);
2678
+ } else {
2679
+ result = "";
2680
+ }
2681
+ const usageTracked = await this.trackStreamingUsageBySpan(
2682
+ context,
2683
+ streamSpanId
2684
+ );
2685
+ if (!usageTracked) {
2686
+ context.updateUsage({
2687
+ requests: 1,
2688
+ inputTokens: 0,
2689
+ outputTokens: 0,
2690
+ totalTokens: 0,
2691
+ cost: { generation: 0, platform: 0, total: 0 }
2692
+ });
2693
+ }
2694
+ await this.triggerHook(HookEvents.LlmResponse, {
2695
+ context,
2696
+ callType: "final_result",
2697
+ response: streamResponse,
2698
+ parsed: result
2699
+ });
2700
+ this.log(
2701
+ this.outputSchema ? "Final result generated (streaming, schema-validated)" : "Final result generated (streaming)"
2702
+ );
2703
+ return result;
2704
+ } catch (error) {
2705
+ await this.triggerHook(HookEvents.StreamError, {
2706
+ context,
2707
+ callType: "final_result",
2708
+ error
2709
+ });
2710
+ this.emitAgentEvent(HookEvents.StreamError, {
2711
+ context,
2712
+ callType: "final_result",
2713
+ error
2714
+ });
2715
+ this.logger.error("Failed to generate final result", error);
2716
+ throw new Error(
2717
+ `Failed to generate final result: ${error instanceof Error ? error.message : String(error)}`
2718
+ );
2719
+ }
2720
+ }
2721
+ async trackStreamingUsageBySpan(context, spanId) {
2722
+ if (!spanId) {
2723
+ return false;
2724
+ }
2725
+ try {
2726
+ const span = await this.opperClient.getClient().spans.get(spanId);
2727
+ const traceId = span?.traceId ?? span?.trace_id;
2728
+ if (!traceId) {
2729
+ return false;
2730
+ }
2731
+ const trace = await this.opperClient.getClient().traces.get(traceId);
2732
+ const spans = trace?.spans;
2733
+ if (!Array.isArray(spans)) {
2734
+ return false;
2735
+ }
2736
+ for (const entry of spans) {
2737
+ const entryId = entry?.id ?? entry["id"];
2738
+ if (entryId !== spanId) {
2739
+ continue;
2740
+ }
2741
+ const data = entry?.data;
2742
+ if (!data) {
2743
+ continue;
2744
+ }
2745
+ const record = data;
2746
+ const primaryTotal = record["totalTokens"];
2747
+ const fallbackTotal = record["total_tokens"];
2748
+ const totalTokensRaw = typeof primaryTotal === "number" && Number.isFinite(primaryTotal) ? primaryTotal : typeof fallbackTotal === "number" && Number.isFinite(fallbackTotal) ? fallbackTotal : void 0;
2749
+ if (totalTokensRaw !== void 0) {
2750
+ context.updateUsage({
2751
+ requests: 1,
2752
+ inputTokens: 0,
2753
+ outputTokens: 0,
2754
+ totalTokens: totalTokensRaw,
2755
+ cost: { generation: 0, platform: 0, total: 0 }
2756
+ });
2757
+ return true;
2758
+ }
2759
+ }
2760
+ } catch (error) {
2761
+ this.logger.warn("Could not fetch streaming usage", {
2762
+ spanId,
2763
+ error: error instanceof Error ? error.message : String(error)
2764
+ });
2765
+ }
2766
+ return false;
2767
+ }
2114
2768
  /**
2115
2769
  * Log helper
2116
2770
  */
@@ -2327,7 +2981,7 @@ var createMCPServerConfig = MCPconfig;
2327
2981
 
2328
2982
  // src/mcp/provider.ts
2329
2983
  init_tool();
2330
- var isRecord = (value) => typeof value === "object" && value !== null;
2984
+ var isRecord2 = (value) => typeof value === "object" && value !== null;
2331
2985
  var MCPToolProvider = class {
2332
2986
  configs;
2333
2987
  namePrefix;
@@ -2418,7 +3072,7 @@ var MCPToolProvider = class {
2418
3072
  },
2419
3073
  execute: async (input, context) => {
2420
3074
  const startedAt = Date.now();
2421
- const args = isRecord(input) ? input : input === void 0 ? {} : { value: input };
3075
+ const args = isRecord2(input) ? input : input === void 0 ? {} : { value: input };
2422
3076
  try {
2423
3077
  const result = await client.callTool(mcpTool.name, args);
2424
3078
  return ToolResultFactory.success(toolName, result, {
@@ -2876,6 +3530,6 @@ var ToolRunner = class {
2876
3530
  }
2877
3531
  };
2878
3532
 
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 };
3533
+ 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, createFunctionTool, createHookManager, createInMemoryStore, createMCPServerConfig, createOpperClient, createStreamAssembler, createToolCallRecord, err, extractTools, generateAgentFlowDiagram, getDefaultLogger, getSchemaDefault, isSchemaValid, isToolProvider, mcp, mergeSchemaDefaults, normalizeToolEntries, ok, schemaToJson, setDefaultLogger, tool, validateSchema, validateToolInput };
2880
3534
  //# sourceMappingURL=index.js.map
2881
3535
  //# sourceMappingURL=index.js.map