@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.cjs CHANGED
@@ -394,7 +394,11 @@ var HookEvents = {
394
394
  ToolError: "tool:error",
395
395
  MemoryRead: "memory:read",
396
396
  MemoryWrite: "memory:write",
397
- MemoryError: "memory:error"
397
+ MemoryError: "memory:error",
398
+ StreamStart: "stream:start",
399
+ StreamChunk: "stream:chunk",
400
+ StreamEnd: "stream:end",
401
+ StreamError: "stream:error"
398
402
  };
399
403
  var HookManager = class {
400
404
  registry = /* @__PURE__ */ new Map();
@@ -466,6 +470,82 @@ var HookManager = class {
466
470
  };
467
471
  var createHookManager = (logger) => new HookManager(logger);
468
472
 
473
+ // src/base/events.ts
474
+ var AgentEvents = {
475
+ StreamStart: HookEvents.StreamStart,
476
+ StreamChunk: HookEvents.StreamChunk,
477
+ StreamEnd: HookEvents.StreamEnd,
478
+ StreamError: HookEvents.StreamError
479
+ };
480
+ var AgentEventEmitter = class {
481
+ registry = /* @__PURE__ */ new Map();
482
+ logger;
483
+ constructor(logger) {
484
+ this.logger = logger ?? getDefaultLogger();
485
+ }
486
+ on(event, listener) {
487
+ const listeners = this.registry.get(event) ?? /* @__PURE__ */ new Set();
488
+ listeners.add(listener);
489
+ this.registry.set(event, listeners);
490
+ return () => this.off(event, listener);
491
+ }
492
+ once(event, listener) {
493
+ const wrapper = (payload) => {
494
+ try {
495
+ listener(payload);
496
+ } finally {
497
+ this.off(event, wrapper);
498
+ }
499
+ };
500
+ return this.on(event, wrapper);
501
+ }
502
+ off(event, listener) {
503
+ const listeners = this.registry.get(event);
504
+ if (!listeners) {
505
+ return;
506
+ }
507
+ listeners.delete(listener);
508
+ if (listeners.size === 0) {
509
+ this.registry.delete(event);
510
+ }
511
+ }
512
+ emit(event, payload) {
513
+ const listeners = Array.from(
514
+ this.registry.get(event) ?? /* @__PURE__ */ new Set()
515
+ );
516
+ if (listeners.length === 0) {
517
+ return;
518
+ }
519
+ for (const listener of listeners) {
520
+ try {
521
+ listener(payload);
522
+ } catch (error) {
523
+ this.logger.warn(`Agent event listener failed for "${event}"`, {
524
+ event,
525
+ error: error instanceof Error ? error.message : String(error)
526
+ });
527
+ }
528
+ }
529
+ }
530
+ removeAllListeners(event) {
531
+ if (event) {
532
+ this.registry.delete(event);
533
+ return;
534
+ }
535
+ this.registry.clear();
536
+ }
537
+ listenerCount(event) {
538
+ if (event) {
539
+ return this.registry.get(event)?.size ?? 0;
540
+ }
541
+ let total = 0;
542
+ for (const listeners of this.registry.values()) {
543
+ total += listeners.size;
544
+ }
545
+ return total;
546
+ }
547
+ };
548
+
469
549
  // src/base/agent.ts
470
550
  init_tool();
471
551
  function sanitizeId(name) {
@@ -825,6 +905,10 @@ var BaseAgent = class {
825
905
  * Whether memory is enabled
826
906
  */
827
907
  enableMemory;
908
+ /**
909
+ * Whether streaming is enabled
910
+ */
911
+ enableStreaming;
828
912
  /**
829
913
  * Memory instance for persistent storage (null if disabled or initialization failed)
830
914
  */
@@ -837,6 +921,10 @@ var BaseAgent = class {
837
921
  * Hook manager for lifecycle events
838
922
  */
839
923
  hooks;
924
+ /**
925
+ * Event dispatcher for runtime events (notably streaming)
926
+ */
927
+ events;
840
928
  /**
841
929
  * Registry of available tools
842
930
  */
@@ -866,12 +954,26 @@ var BaseAgent = class {
866
954
  this.inputSchema = config.inputSchema;
867
955
  this.outputSchema = config.outputSchema;
868
956
  this.enableMemory = config.enableMemory ?? false;
957
+ this.enableStreaming = config.enableStreaming ?? false;
869
958
  this.metadata = { ...config.metadata ?? {} };
870
959
  this.hooks = new HookManager();
960
+ this.events = new AgentEventEmitter();
871
961
  this.tools = /* @__PURE__ */ new Map();
872
962
  this.baseTools = /* @__PURE__ */ new Map();
873
963
  this.toolProviders = /* @__PURE__ */ new Set();
874
964
  this.providerToolRegistry = /* @__PURE__ */ new Map();
965
+ if (config.onStreamStart) {
966
+ this.on(HookEvents.StreamStart, config.onStreamStart);
967
+ }
968
+ if (config.onStreamChunk) {
969
+ this.on(HookEvents.StreamChunk, config.onStreamChunk);
970
+ }
971
+ if (config.onStreamEnd) {
972
+ this.on(HookEvents.StreamEnd, config.onStreamEnd);
973
+ }
974
+ if (config.onStreamError) {
975
+ this.on(HookEvents.StreamError, config.onStreamError);
976
+ }
875
977
  this.opperConfig = {
876
978
  apiKey: config.opperConfig?.apiKey ?? process.env["OPPER_API_KEY"],
877
979
  baseUrl: config.opperConfig?.baseUrl,
@@ -1053,6 +1155,35 @@ var BaseAgent = class {
1053
1155
  registerHook(event, handler) {
1054
1156
  return this.hooks.on(event, handler);
1055
1157
  }
1158
+ /**
1159
+ * Register an event listener.
1160
+ *
1161
+ * @param event - Event name
1162
+ * @param listener - Listener callback
1163
+ * @returns Cleanup function to unregister the listener
1164
+ */
1165
+ on(event, listener) {
1166
+ return this.events.on(event, listener);
1167
+ }
1168
+ /**
1169
+ * Register a one-time event listener that removes itself after the first call.
1170
+ *
1171
+ * @param event - Event name
1172
+ * @param listener - Listener callback
1173
+ * @returns Cleanup function (no-op once listener fires)
1174
+ */
1175
+ once(event, listener) {
1176
+ return this.events.once(event, listener);
1177
+ }
1178
+ /**
1179
+ * Remove a previously registered event listener.
1180
+ *
1181
+ * @param event - Event name
1182
+ * @param listener - Listener callback to remove
1183
+ */
1184
+ off(event, listener) {
1185
+ this.events.off(event, listener);
1186
+ }
1056
1187
  /**
1057
1188
  * Trigger a hook event with a payload.
1058
1189
  * Swallows errors to prevent hook failures from breaking agent execution.
@@ -1067,6 +1198,15 @@ var BaseAgent = class {
1067
1198
  console.warn(`Hook error for event ${event}:`, error);
1068
1199
  }
1069
1200
  }
1201
+ /**
1202
+ * Emit a runtime event to listeners.
1203
+ *
1204
+ * @param event - Event name
1205
+ * @param payload - Event payload
1206
+ */
1207
+ emitAgentEvent(event, payload) {
1208
+ this.events.emit(event, payload);
1209
+ }
1070
1210
  /**
1071
1211
  * Execute a tool with proper context, hooks, and error handling.
1072
1212
  *
@@ -1340,7 +1480,7 @@ var ToolExecutionSummarySchema = zod.z.object({
1340
1480
 
1341
1481
  // package.json
1342
1482
  var package_default = {
1343
- version: "0.1.3"};
1483
+ version: "0.2.0"};
1344
1484
 
1345
1485
  // src/utils/version.ts
1346
1486
  var SDK_NAME = "@opperai/agents";
@@ -1403,7 +1543,7 @@ var OpperClient = class {
1403
1543
  return this.withRetry(async () => {
1404
1544
  const inputSchema = this.toJsonSchema(options.inputSchema);
1405
1545
  const outputSchema = this.toJsonSchema(options.outputSchema);
1406
- const response = await this.client.call({
1546
+ const callPayload = {
1407
1547
  name: options.name,
1408
1548
  instructions: options.instructions,
1409
1549
  input: options.input,
@@ -1411,7 +1551,8 @@ var OpperClient = class {
1411
1551
  ...outputSchema && { outputSchema },
1412
1552
  ...options.model && { model: options.model },
1413
1553
  ...options.parentSpanId && { parentSpanId: options.parentSpanId }
1414
- });
1554
+ };
1555
+ const response = options.signal ? await this.client.call(callPayload, { signal: options.signal }) : await this.client.call(callPayload);
1415
1556
  const usagePayload = extractUsage(response);
1416
1557
  const costPayload = extractCost(response);
1417
1558
  const usage = {
@@ -1429,6 +1570,36 @@ var OpperClient = class {
1429
1570
  return result;
1430
1571
  }, options.name);
1431
1572
  }
1573
+ /**
1574
+ * Stream a call to Opper with retry logic
1575
+ */
1576
+ async stream(options) {
1577
+ return this.withRetry(async () => {
1578
+ const inputSchema = this.toJsonSchema(options.inputSchema);
1579
+ const outputSchema = this.toJsonSchema(options.outputSchema);
1580
+ const streamPayload = {
1581
+ name: options.name,
1582
+ instructions: options.instructions,
1583
+ input: options.input,
1584
+ ...inputSchema && { inputSchema },
1585
+ ...outputSchema && { outputSchema },
1586
+ ...options.model && { model: options.model },
1587
+ ...options.parentSpanId && { parentSpanId: options.parentSpanId }
1588
+ };
1589
+ const response = options.signal ? await this.client.stream(streamPayload, { signal: options.signal }) : await this.client.stream(streamPayload);
1590
+ const iterable = {
1591
+ async *[Symbol.asyncIterator]() {
1592
+ for await (const event of response.result) {
1593
+ yield event;
1594
+ }
1595
+ }
1596
+ };
1597
+ return {
1598
+ headers: response.headers,
1599
+ result: iterable
1600
+ };
1601
+ }, `stream:${options.name}`);
1602
+ }
1432
1603
  /**
1433
1604
  * Create a span for tracing
1434
1605
  */
@@ -1597,6 +1768,189 @@ var mergeSchemaDefaults = (schema, value) => {
1597
1768
  return validateSchema(schema, value);
1598
1769
  };
1599
1770
 
1771
+ // src/utils/streaming.ts
1772
+ var STREAM_ROOT_PATH = "_root";
1773
+ var NUMERIC_TOKEN_PATTERN = /^\d+$/;
1774
+ var BRACKET_TOKEN_PATTERN = /\[(\d+)\]/g;
1775
+ var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1776
+ var coercePrimitive = (value) => {
1777
+ const trimmed = value.trim();
1778
+ if (trimmed.length === 0) {
1779
+ return value;
1780
+ }
1781
+ const lower = trimmed.toLowerCase();
1782
+ if (lower === "true") {
1783
+ return true;
1784
+ }
1785
+ if (lower === "false") {
1786
+ return false;
1787
+ }
1788
+ if (lower === "null") {
1789
+ return null;
1790
+ }
1791
+ if (/^-?\d+$/.test(trimmed)) {
1792
+ const intValue = Number.parseInt(trimmed, 10);
1793
+ return Number.isNaN(intValue) ? value : intValue;
1794
+ }
1795
+ if (/^-?\d+\.\d+$/.test(trimmed)) {
1796
+ const floatValue = Number.parseFloat(trimmed);
1797
+ return Number.isNaN(floatValue) ? value : floatValue;
1798
+ }
1799
+ return value;
1800
+ };
1801
+ var toDisplayString = (value) => {
1802
+ if (value === null || value === void 0) {
1803
+ return "";
1804
+ }
1805
+ if (typeof value === "string") {
1806
+ return value;
1807
+ }
1808
+ return String(value);
1809
+ };
1810
+ var parsePathSegments = (path) => {
1811
+ return path.replace(BRACKET_TOKEN_PATTERN, (_, index) => `.${index}`).split(".").map((segment) => segment.trim()).filter((segment) => segment.length > 0);
1812
+ };
1813
+ var setNestedValue = (target, path, value) => {
1814
+ const segments = parsePathSegments(path);
1815
+ if (segments.length === 0) {
1816
+ return;
1817
+ }
1818
+ let current = target;
1819
+ for (let i = 0; i < segments.length - 1; i++) {
1820
+ const segment = segments[i];
1821
+ if (segment === void 0) {
1822
+ return;
1823
+ }
1824
+ const existing = current[segment];
1825
+ if (!isRecord(existing)) {
1826
+ const nextLevel = {};
1827
+ current[segment] = nextLevel;
1828
+ current = nextLevel;
1829
+ continue;
1830
+ }
1831
+ current = existing;
1832
+ }
1833
+ const lastSegment = segments[segments.length - 1];
1834
+ if (lastSegment === void 0) {
1835
+ return;
1836
+ }
1837
+ current[lastSegment] = value;
1838
+ };
1839
+ var normalizeIndexed = (value) => {
1840
+ if (Array.isArray(value)) {
1841
+ return value.map((item) => normalizeIndexed(item));
1842
+ }
1843
+ if (!isRecord(value)) {
1844
+ return value;
1845
+ }
1846
+ const normalizedEntries = {};
1847
+ for (const [key, entryValue] of Object.entries(value)) {
1848
+ normalizedEntries[key] = normalizeIndexed(entryValue);
1849
+ }
1850
+ const keys = Object.keys(normalizedEntries);
1851
+ if (keys.length > 0 && keys.every((key) => NUMERIC_TOKEN_PATTERN.test(key))) {
1852
+ const parsedIndices = keys.map((key) => Number.parseInt(key, 10)).filter((index) => Number.isFinite(index) && index >= 0);
1853
+ if (parsedIndices.length === 0) {
1854
+ return normalizedEntries;
1855
+ }
1856
+ const maxIndex = Math.max(...parsedIndices);
1857
+ const result = new Array(maxIndex + 1).fill(void 0);
1858
+ for (const [key, entryValue] of Object.entries(normalizedEntries)) {
1859
+ const index = Number.parseInt(key, 10);
1860
+ if (!Number.isFinite(index) || index < 0) {
1861
+ continue;
1862
+ }
1863
+ result[index] = entryValue;
1864
+ }
1865
+ return result.map(
1866
+ (item) => item === void 0 ? null : item
1867
+ );
1868
+ }
1869
+ return normalizedEntries;
1870
+ };
1871
+ var resolveFieldValue = (path, displayBuffers, valueBuffers) => {
1872
+ const values = valueBuffers.get(path) ?? [];
1873
+ if (values.length === 0) {
1874
+ return displayBuffers.get(path) ?? "";
1875
+ }
1876
+ const last = values[values.length - 1];
1877
+ if (typeof last === "number" || typeof last === "boolean") {
1878
+ return last;
1879
+ }
1880
+ if (last === null) {
1881
+ return null;
1882
+ }
1883
+ const joined = displayBuffers.get(path) ?? values.map((value) => toDisplayString(value)).join("");
1884
+ return coercePrimitive(joined);
1885
+ };
1886
+ var StreamAssembler = class {
1887
+ displayBuffers = /* @__PURE__ */ new Map();
1888
+ valueBuffers = /* @__PURE__ */ new Map();
1889
+ schema;
1890
+ constructor(options = {}) {
1891
+ this.schema = options.schema;
1892
+ }
1893
+ feed(chunk) {
1894
+ const { delta } = chunk;
1895
+ if (delta === null || delta === void 0) {
1896
+ return null;
1897
+ }
1898
+ const path = chunk.jsonPath === null || chunk.jsonPath === void 0 ? STREAM_ROOT_PATH : chunk.jsonPath;
1899
+ const existingDisplay = this.displayBuffers.get(path) ?? "";
1900
+ const nextDisplay = `${existingDisplay}${toDisplayString(delta)}`;
1901
+ this.displayBuffers.set(path, nextDisplay);
1902
+ const existingValues = this.valueBuffers.get(path) ?? [];
1903
+ existingValues.push(delta);
1904
+ this.valueBuffers.set(path, existingValues);
1905
+ return {
1906
+ path,
1907
+ accumulated: nextDisplay,
1908
+ snapshot: this.snapshot()
1909
+ };
1910
+ }
1911
+ snapshot() {
1912
+ return Object.fromEntries(this.displayBuffers.entries());
1913
+ }
1914
+ hasStructuredFields() {
1915
+ const keys = Array.from(this.displayBuffers.keys());
1916
+ return keys.some((key) => key !== STREAM_ROOT_PATH);
1917
+ }
1918
+ finalize() {
1919
+ if (this.displayBuffers.size === 0) {
1920
+ return { type: "empty" };
1921
+ }
1922
+ if (!this.hasStructuredFields()) {
1923
+ const root = this.displayBuffers.get(STREAM_ROOT_PATH) ?? "";
1924
+ return { type: "root", rootText: root };
1925
+ }
1926
+ const structured = this.reconstructStructured();
1927
+ let coerced = structured;
1928
+ if (this.schema !== void 0) {
1929
+ try {
1930
+ coerced = this.schema.parse(structured);
1931
+ } catch {
1932
+ coerced = structured;
1933
+ }
1934
+ }
1935
+ return { type: "structured", structured: coerced };
1936
+ }
1937
+ getFieldBuffers() {
1938
+ return new Map(this.displayBuffers);
1939
+ }
1940
+ reconstructStructured() {
1941
+ const result = {};
1942
+ for (const path of this.valueBuffers.keys()) {
1943
+ if (path === STREAM_ROOT_PATH) {
1944
+ continue;
1945
+ }
1946
+ const value = resolveFieldValue(path, this.displayBuffers, this.valueBuffers);
1947
+ setNestedValue(result, path, value);
1948
+ }
1949
+ return normalizeIndexed(result);
1950
+ }
1951
+ };
1952
+ var createStreamAssembler = (options) => new StreamAssembler(options);
1953
+
1600
1954
  // src/core/agent.ts
1601
1955
  var isToolSuccessResult = (value) => {
1602
1956
  if (typeof value !== "object" || value === null) {
@@ -1723,6 +2077,9 @@ var Agent = class extends BaseAgent {
1723
2077
  const spanName = "think";
1724
2078
  this.log("Think step", { iteration: context.iteration });
1725
2079
  await this.triggerHook(HookEvents.LlmCall, { context, callType: "think" });
2080
+ if (this.enableStreaming) {
2081
+ return this.thinkStreaming(input, context);
2082
+ }
1726
2083
  try {
1727
2084
  const instructions = this.buildThinkInstructions();
1728
2085
  const thinkContext = await this.buildThinkContext(input, context);
@@ -1765,6 +2122,132 @@ var Agent = class extends BaseAgent {
1765
2122
  );
1766
2123
  }
1767
2124
  }
2125
+ async thinkStreaming(input, context) {
2126
+ const spanName = "think";
2127
+ const instructions = this.buildThinkInstructions();
2128
+ const thinkContext = await this.buildThinkContext(input, context);
2129
+ const assembler = createStreamAssembler({
2130
+ schema: AgentDecisionSchema
2131
+ });
2132
+ let streamSpanId;
2133
+ await this.triggerHook(HookEvents.StreamStart, {
2134
+ context,
2135
+ callType: "think"
2136
+ });
2137
+ this.emitAgentEvent(HookEvents.StreamStart, {
2138
+ context,
2139
+ callType: "think"
2140
+ });
2141
+ try {
2142
+ const streamResponse = await this.opperClient.stream({
2143
+ name: spanName,
2144
+ instructions,
2145
+ input: thinkContext,
2146
+ outputSchema: AgentDecisionSchema,
2147
+ model: this.model,
2148
+ ...context.parentSpanId && { parentSpanId: context.parentSpanId }
2149
+ });
2150
+ for await (const event of streamResponse.result) {
2151
+ const data = event?.data;
2152
+ if (!data) {
2153
+ continue;
2154
+ }
2155
+ if (!streamSpanId && typeof data.spanId === "string" && data.spanId) {
2156
+ streamSpanId = data.spanId;
2157
+ }
2158
+ const feedResult = assembler.feed({
2159
+ delta: data.delta,
2160
+ jsonPath: data.jsonPath
2161
+ });
2162
+ if (!feedResult) {
2163
+ continue;
2164
+ }
2165
+ const chunkPayload = {
2166
+ context,
2167
+ callType: "think",
2168
+ chunkData: {
2169
+ delta: data.delta,
2170
+ jsonPath: data.jsonPath ?? null,
2171
+ chunkType: data.chunkType ?? null
2172
+ },
2173
+ accumulated: feedResult.accumulated,
2174
+ fieldBuffers: feedResult.snapshot
2175
+ };
2176
+ await this.triggerHook(HookEvents.StreamChunk, chunkPayload);
2177
+ this.emitAgentEvent(HookEvents.StreamChunk, chunkPayload);
2178
+ }
2179
+ const fieldBuffers = assembler.snapshot();
2180
+ const endPayload = {
2181
+ context,
2182
+ callType: "think",
2183
+ fieldBuffers
2184
+ };
2185
+ await this.triggerHook(HookEvents.StreamEnd, endPayload);
2186
+ this.emitAgentEvent(HookEvents.StreamEnd, endPayload);
2187
+ const finalize = assembler.finalize();
2188
+ let decision;
2189
+ if (finalize.type === "structured" && finalize.structured) {
2190
+ decision = AgentDecisionSchema.parse(
2191
+ finalize.structured
2192
+ );
2193
+ } else {
2194
+ decision = AgentDecisionSchema.parse({
2195
+ reasoning: finalize.type === "root" ? finalize.rootText ?? "" : ""
2196
+ });
2197
+ }
2198
+ const usageTracked = await this.trackStreamingUsageBySpan(
2199
+ context,
2200
+ streamSpanId
2201
+ );
2202
+ if (!usageTracked) {
2203
+ context.updateUsage({
2204
+ requests: 1,
2205
+ inputTokens: 0,
2206
+ outputTokens: 0,
2207
+ totalTokens: 0,
2208
+ cost: { generation: 0, platform: 0, total: 0 }
2209
+ });
2210
+ }
2211
+ await this.triggerHook(HookEvents.LlmResponse, {
2212
+ context,
2213
+ callType: "think",
2214
+ response: streamResponse,
2215
+ parsed: decision
2216
+ });
2217
+ await this.triggerHook(HookEvents.ThinkEnd, {
2218
+ context,
2219
+ thought: { reasoning: decision.reasoning }
2220
+ });
2221
+ this.log("Think result", {
2222
+ reasoning: decision.reasoning,
2223
+ toolCalls: decision.toolCalls.length,
2224
+ memoryReads: decision.memoryReads?.length ?? 0,
2225
+ memoryWrites: Object.keys(decision.memoryUpdates ?? {}).length
2226
+ });
2227
+ const resultPayload = {
2228
+ decision
2229
+ };
2230
+ if (streamSpanId) {
2231
+ resultPayload.spanId = streamSpanId;
2232
+ }
2233
+ return resultPayload;
2234
+ } catch (error) {
2235
+ await this.triggerHook(HookEvents.StreamError, {
2236
+ context,
2237
+ callType: "think",
2238
+ error
2239
+ });
2240
+ this.emitAgentEvent(HookEvents.StreamError, {
2241
+ context,
2242
+ callType: "think",
2243
+ error
2244
+ });
2245
+ this.logger.error("Think step failed", error);
2246
+ throw new Error(
2247
+ `Think step failed: ${error instanceof Error ? error.message : String(error)}`
2248
+ );
2249
+ }
2250
+ }
1768
2251
  /**
1769
2252
  * Build static instructions for the think step
1770
2253
  */
@@ -2078,6 +2561,13 @@ The memory you write persists across all process() calls on this agent.`;
2078
2561
  };
2079
2562
  const instructions = `Generate the final result based on the execution history.
2080
2563
  Follow any instructions provided for formatting and style.`;
2564
+ if (this.enableStreaming) {
2565
+ return this.generateFinalResultStreaming(
2566
+ context,
2567
+ finalContext,
2568
+ instructions
2569
+ );
2570
+ }
2081
2571
  try {
2082
2572
  const callOptions = {
2083
2573
  name: "generate_final_result",
@@ -2113,6 +2603,170 @@ Follow any instructions provided for formatting and style.`;
2113
2603
  );
2114
2604
  }
2115
2605
  }
2606
+ async generateFinalResultStreaming(context, finalContext, instructions) {
2607
+ const assembler = createStreamAssembler(
2608
+ this.outputSchema ? { schema: this.outputSchema } : void 0
2609
+ );
2610
+ let streamSpanId;
2611
+ await this.triggerHook(HookEvents.StreamStart, {
2612
+ context,
2613
+ callType: "final_result"
2614
+ });
2615
+ this.emitAgentEvent(HookEvents.StreamStart, {
2616
+ context,
2617
+ callType: "final_result"
2618
+ });
2619
+ try {
2620
+ const streamResponse = await this.opperClient.stream({
2621
+ name: "generate_final_result",
2622
+ instructions,
2623
+ input: finalContext,
2624
+ model: this.model,
2625
+ ...context.parentSpanId && { parentSpanId: context.parentSpanId },
2626
+ ...this.outputSchema && { outputSchema: this.outputSchema }
2627
+ });
2628
+ for await (const event of streamResponse.result) {
2629
+ const data = event?.data;
2630
+ if (!data) {
2631
+ continue;
2632
+ }
2633
+ if (!streamSpanId && typeof data.spanId === "string" && data.spanId) {
2634
+ streamSpanId = data.spanId;
2635
+ }
2636
+ const feedResult = assembler.feed({
2637
+ delta: data.delta,
2638
+ jsonPath: data.jsonPath
2639
+ });
2640
+ if (!feedResult) {
2641
+ continue;
2642
+ }
2643
+ const chunkPayload = {
2644
+ context,
2645
+ callType: "final_result",
2646
+ chunkData: {
2647
+ delta: data.delta,
2648
+ jsonPath: data.jsonPath ?? null,
2649
+ chunkType: data.chunkType ?? null
2650
+ },
2651
+ accumulated: feedResult.accumulated,
2652
+ fieldBuffers: feedResult.snapshot
2653
+ };
2654
+ await this.triggerHook(HookEvents.StreamChunk, chunkPayload);
2655
+ this.emitAgentEvent(HookEvents.StreamChunk, chunkPayload);
2656
+ }
2657
+ const fieldBuffers = assembler.snapshot();
2658
+ const endPayload = {
2659
+ context,
2660
+ callType: "final_result",
2661
+ fieldBuffers
2662
+ };
2663
+ await this.triggerHook(HookEvents.StreamEnd, endPayload);
2664
+ this.emitAgentEvent(HookEvents.StreamEnd, endPayload);
2665
+ const finalize = assembler.finalize();
2666
+ let result;
2667
+ if (this.outputSchema) {
2668
+ if (finalize.type !== "structured" || finalize.structured === void 0) {
2669
+ throw new Error(
2670
+ "Streaming response did not provide structured data for the configured output schema."
2671
+ );
2672
+ }
2673
+ result = this.outputSchema.parse(
2674
+ finalize.structured
2675
+ );
2676
+ } else if (finalize.type === "root") {
2677
+ result = finalize.rootText ?? "";
2678
+ } else if (finalize.type === "structured" && finalize.structured) {
2679
+ result = JSON.stringify(finalize.structured);
2680
+ } else {
2681
+ result = "";
2682
+ }
2683
+ const usageTracked = await this.trackStreamingUsageBySpan(
2684
+ context,
2685
+ streamSpanId
2686
+ );
2687
+ if (!usageTracked) {
2688
+ context.updateUsage({
2689
+ requests: 1,
2690
+ inputTokens: 0,
2691
+ outputTokens: 0,
2692
+ totalTokens: 0,
2693
+ cost: { generation: 0, platform: 0, total: 0 }
2694
+ });
2695
+ }
2696
+ await this.triggerHook(HookEvents.LlmResponse, {
2697
+ context,
2698
+ callType: "final_result",
2699
+ response: streamResponse,
2700
+ parsed: result
2701
+ });
2702
+ this.log(
2703
+ this.outputSchema ? "Final result generated (streaming, schema-validated)" : "Final result generated (streaming)"
2704
+ );
2705
+ return result;
2706
+ } catch (error) {
2707
+ await this.triggerHook(HookEvents.StreamError, {
2708
+ context,
2709
+ callType: "final_result",
2710
+ error
2711
+ });
2712
+ this.emitAgentEvent(HookEvents.StreamError, {
2713
+ context,
2714
+ callType: "final_result",
2715
+ error
2716
+ });
2717
+ this.logger.error("Failed to generate final result", error);
2718
+ throw new Error(
2719
+ `Failed to generate final result: ${error instanceof Error ? error.message : String(error)}`
2720
+ );
2721
+ }
2722
+ }
2723
+ async trackStreamingUsageBySpan(context, spanId) {
2724
+ if (!spanId) {
2725
+ return false;
2726
+ }
2727
+ try {
2728
+ const span = await this.opperClient.getClient().spans.get(spanId);
2729
+ const traceId = span?.traceId ?? span?.trace_id;
2730
+ if (!traceId) {
2731
+ return false;
2732
+ }
2733
+ const trace = await this.opperClient.getClient().traces.get(traceId);
2734
+ const spans = trace?.spans;
2735
+ if (!Array.isArray(spans)) {
2736
+ return false;
2737
+ }
2738
+ for (const entry of spans) {
2739
+ const entryId = entry?.id ?? entry["id"];
2740
+ if (entryId !== spanId) {
2741
+ continue;
2742
+ }
2743
+ const data = entry?.data;
2744
+ if (!data) {
2745
+ continue;
2746
+ }
2747
+ const record = data;
2748
+ const primaryTotal = record["totalTokens"];
2749
+ const fallbackTotal = record["total_tokens"];
2750
+ const totalTokensRaw = typeof primaryTotal === "number" && Number.isFinite(primaryTotal) ? primaryTotal : typeof fallbackTotal === "number" && Number.isFinite(fallbackTotal) ? fallbackTotal : void 0;
2751
+ if (totalTokensRaw !== void 0) {
2752
+ context.updateUsage({
2753
+ requests: 1,
2754
+ inputTokens: 0,
2755
+ outputTokens: 0,
2756
+ totalTokens: totalTokensRaw,
2757
+ cost: { generation: 0, platform: 0, total: 0 }
2758
+ });
2759
+ return true;
2760
+ }
2761
+ }
2762
+ } catch (error) {
2763
+ this.logger.warn("Could not fetch streaming usage", {
2764
+ spanId,
2765
+ error: error instanceof Error ? error.message : String(error)
2766
+ });
2767
+ }
2768
+ return false;
2769
+ }
2116
2770
  /**
2117
2771
  * Log helper
2118
2772
  */
@@ -2329,7 +2983,7 @@ var createMCPServerConfig = MCPconfig;
2329
2983
 
2330
2984
  // src/mcp/provider.ts
2331
2985
  init_tool();
2332
- var isRecord = (value) => typeof value === "object" && value !== null;
2986
+ var isRecord2 = (value) => typeof value === "object" && value !== null;
2333
2987
  var MCPToolProvider = class {
2334
2988
  configs;
2335
2989
  namePrefix;
@@ -2420,7 +3074,7 @@ var MCPToolProvider = class {
2420
3074
  },
2421
3075
  execute: async (input, context) => {
2422
3076
  const startedAt = Date.now();
2423
- const args = isRecord(input) ? input : input === void 0 ? {} : { value: input };
3077
+ const args = isRecord2(input) ? input : input === void 0 ? {} : { value: input };
2424
3078
  try {
2425
3079
  const result = await client.callTool(mcpTool.name, args);
2426
3080
  return exports.ToolResultFactory.success(toolName, result, {
@@ -2880,6 +3534,8 @@ var ToolRunner = class {
2880
3534
 
2881
3535
  exports.Agent = Agent;
2882
3536
  exports.AgentDecisionSchema = AgentDecisionSchema;
3537
+ exports.AgentEventEmitter = AgentEventEmitter;
3538
+ exports.AgentEvents = AgentEvents;
2883
3539
  exports.BaseAgent = BaseAgent;
2884
3540
  exports.ConsoleLogger = ConsoleLogger;
2885
3541
  exports.DEFAULT_MODEL = DEFAULT_MODEL;
@@ -2896,8 +3552,10 @@ exports.MemoryEntryMetadataSchema = MemoryEntryMetadataSchema;
2896
3552
  exports.MemoryEntrySchema = MemoryEntrySchema;
2897
3553
  exports.MemoryUpdateSchema = MemoryUpdateSchema;
2898
3554
  exports.OpperClient = OpperClient;
3555
+ exports.STREAM_ROOT_PATH = STREAM_ROOT_PATH;
2899
3556
  exports.SchemaValidationError = SchemaValidationError;
2900
3557
  exports.SilentLogger = SilentLogger;
3558
+ exports.StreamAssembler = StreamAssembler;
2901
3559
  exports.ThoughtSchema = ThoughtSchema;
2902
3560
  exports.ToolCallSchema = ToolCallSchema;
2903
3561
  exports.ToolExecutionSummarySchema = ToolExecutionSummarySchema;
@@ -2907,6 +3565,7 @@ exports.createHookManager = createHookManager;
2907
3565
  exports.createInMemoryStore = createInMemoryStore;
2908
3566
  exports.createMCPServerConfig = createMCPServerConfig;
2909
3567
  exports.createOpperClient = createOpperClient;
3568
+ exports.createStreamAssembler = createStreamAssembler;
2910
3569
  exports.extractTools = extractTools;
2911
3570
  exports.generateAgentFlowDiagram = generateAgentFlowDiagram;
2912
3571
  exports.getDefaultLogger = getDefaultLogger;