@axlsdk/axl 0.17.4 → 0.17.6

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
@@ -839,6 +839,7 @@ var OpenAIResponsesProvider = class {
839
839
  // src/providers/anthropic.ts
840
840
  var ANTHROPIC_API_VERSION = "2023-06-01";
841
841
  var ANTHROPIC_PRICING = {
842
+ "claude-opus-4-7": [5e-6, 25e-6],
842
843
  "claude-opus-4-6": [5e-6, 25e-6],
843
844
  "claude-sonnet-4-6": [3e-6, 15e-6],
844
845
  "claude-opus-4-5": [5e-6, 25e-6],
@@ -883,13 +884,21 @@ var THINKING_BUDGETS = {
883
884
  max: 3e4
884
885
  };
885
886
  function supportsAdaptiveThinking(model) {
886
- return model.startsWith("claude-opus-4-6") || model.startsWith("claude-sonnet-4-6");
887
+ return model.startsWith("claude-opus-4-7") || model.startsWith("claude-opus-4-6") || model.startsWith("claude-sonnet-4-6");
887
888
  }
888
889
  function supportsMaxEffort(model) {
889
- return model.startsWith("claude-opus-4-6");
890
+ return model.startsWith("claude-opus-4-7") || model.startsWith("claude-opus-4-6");
891
+ }
892
+ function supportsXhigh2(model) {
893
+ return model.startsWith("claude-opus-4-7");
890
894
  }
891
895
  function supportsEffort(model) {
892
- return model.startsWith("claude-opus-4-6") || model.startsWith("claude-sonnet-4-6") || model.startsWith("claude-opus-4-5");
896
+ return model.startsWith("claude-opus-4-7") || model.startsWith("claude-opus-4-6") || model.startsWith("claude-sonnet-4-6") || model.startsWith("claude-opus-4-5");
897
+ }
898
+ function clampAnthropicEffort(model, effort) {
899
+ if (effort === "max" && !supportsMaxEffort(model)) return "high";
900
+ if (effort === "xhigh" && !supportsXhigh2(model)) return "high";
901
+ return effort;
893
902
  }
894
903
  var AnthropicProvider = class {
895
904
  name = "anthropic";
@@ -990,10 +999,7 @@ var AnthropicProvider = class {
990
999
  body.tool_choice = this.mapToolChoice(options.toolChoice);
991
1000
  }
992
1001
  const { thinkingBudget, thinkingDisabled, activeEffort, hasBudgetOverride } = resolveThinkingOptions(options);
993
- let resolvedEffort = activeEffort;
994
- if (resolvedEffort === "max" && !supportsMaxEffort(options.model)) {
995
- resolvedEffort = "high";
996
- }
1002
+ const resolvedEffort = activeEffort ? clampAnthropicEffort(options.model, activeEffort) : void 0;
997
1003
  if (hasBudgetOverride) {
998
1004
  body.thinking = { type: "enabled", budget_tokens: thinkingBudget };
999
1005
  const currentMax = body.max_tokens;
@@ -1360,6 +1366,7 @@ var GEMINI_PRICING = {
1360
1366
  "gemini-3-pro-preview": [2e-6, 12e-6],
1361
1367
  "gemini-3-flash-preview": [5e-7, 3e-6],
1362
1368
  "gemini-3.1-pro-preview": [2e-6, 12e-6],
1369
+ "gemini-3.1-flash-lite": [25e-8, 15e-7],
1363
1370
  "gemini-3.1-flash-lite-preview": [25e-8, 15e-7]
1364
1371
  };
1365
1372
  var GEMINI_PRICING_KEYS_BY_LENGTH = Object.keys(GEMINI_PRICING).sort(
@@ -1385,12 +1392,16 @@ var THINKING_BUDGETS2 = {
1385
1392
  low: 1024,
1386
1393
  medium: 5e3,
1387
1394
  high: 1e4,
1395
+ xhigh: 16384,
1396
+ // between high (10000) and max (24576)
1388
1397
  max: 24576
1389
1398
  };
1390
1399
  var THINKING_LEVELS = {
1391
1400
  low: "low",
1392
1401
  medium: "medium",
1393
1402
  high: "high",
1403
+ xhigh: "high",
1404
+ // 3.x has no xhigh tier; clamp to 'high'
1394
1405
  max: "high"
1395
1406
  // 3.x caps at 'high'
1396
1407
  };
@@ -2337,6 +2348,314 @@ var PartialParser = class {
2337
2348
  }
2338
2349
  };
2339
2350
 
2351
+ // src/streaming-walker.ts
2352
+ var MAX_DEPTH2 = 256;
2353
+ function encodeSegment(s) {
2354
+ if (typeof s === "number") return String(s);
2355
+ return s.replace(/~/g, "~0").replace(/\//g, "~1");
2356
+ }
2357
+ function computePath(stack) {
2358
+ let path = "";
2359
+ for (const frame of stack) {
2360
+ if (frame.type === "object") {
2361
+ path += "/" + encodeSegment(frame.currentKey ?? "");
2362
+ } else {
2363
+ path += "/" + encodeSegment(frame.currentIndex);
2364
+ }
2365
+ }
2366
+ return path;
2367
+ }
2368
+ var StreamingWalker = class {
2369
+ constructor(callbacks) {
2370
+ this.callbacks = callbacks;
2371
+ }
2372
+ state = "pre-json";
2373
+ stack = [];
2374
+ // String parsing state — survives across chunks so escapes can split.
2375
+ escaped = false;
2376
+ /** When > 0, we're collecting `\\uXXXX` hex digits. 4→0 as digits arrive. */
2377
+ unicodePending = 0;
2378
+ /** Accumulator for `\\uXXXX` digits. Cleared when the escape resolves. */
2379
+ unicodeBuf = "";
2380
+ // Per-string accumulators.
2381
+ keyBuf = "";
2382
+ deltaBuf = "";
2383
+ /** Cached JSON Pointer to the value currently being read. Recomputed
2384
+ * on entry to `read-value-string` (the only state that needs it). */
2385
+ currentPath = "";
2386
+ /** Set true when a structural boundary lands. Caller reads via
2387
+ * `consumeBoundary()` after each chunk to decide on partial_object emit. */
2388
+ boundaryPending = false;
2389
+ /** Walk `text` char-by-char, advancing state and accumulating deltas.
2390
+ * Calls `onStringDelta` at end-of-chunk for any pending value text. */
2391
+ processChunk(text) {
2392
+ for (let i = 0; i < text.length; i++) {
2393
+ if (this.state === "done") return;
2394
+ this.processChar(text[i]);
2395
+ }
2396
+ if (this.deltaBuf.length > 0 && this.state === "read-value-string") {
2397
+ this.callbacks.onStringDelta?.(this.currentPath, this.deltaBuf);
2398
+ this.deltaBuf = "";
2399
+ }
2400
+ }
2401
+ /** Read-and-clear the structural-boundary flag. Caller checks this
2402
+ * after `processChunk` to decide whether to emit `partial_object`. */
2403
+ consumeBoundary() {
2404
+ const v = this.boundaryPending;
2405
+ this.boundaryPending = false;
2406
+ return v;
2407
+ }
2408
+ /** True when the walker has decided it's done with this stream
2409
+ * (root closed or fatal malformed input). Caller may use this to
2410
+ * short-circuit further work. */
2411
+ get isDone() {
2412
+ return this.state === "done";
2413
+ }
2414
+ // ── Per-char dispatch ───────────────────────────────────────────────────
2415
+ processChar(ch) {
2416
+ switch (this.state) {
2417
+ case "pre-json":
2418
+ if (ch === "{") {
2419
+ if (!this.pushFrame({ type: "object" })) return;
2420
+ this.state = "expect-key-or-close";
2421
+ }
2422
+ return;
2423
+ case "expect-key-or-close":
2424
+ if (isWhitespace(ch)) return;
2425
+ if (ch === "}") {
2426
+ this.popFrame();
2427
+ return;
2428
+ }
2429
+ if (ch === '"') {
2430
+ this.keyBuf = "";
2431
+ this.escaped = false;
2432
+ this.unicodePending = 0;
2433
+ this.unicodeBuf = "";
2434
+ this.state = "read-key";
2435
+ return;
2436
+ }
2437
+ this.state = "done";
2438
+ return;
2439
+ case "read-key":
2440
+ this.processStringChar(
2441
+ ch,
2442
+ /* isValue */
2443
+ false
2444
+ );
2445
+ return;
2446
+ case "expect-colon":
2447
+ if (isWhitespace(ch)) return;
2448
+ if (ch === ":") {
2449
+ this.state = "expect-value";
2450
+ return;
2451
+ }
2452
+ this.state = "done";
2453
+ return;
2454
+ case "expect-value":
2455
+ if (isWhitespace(ch)) return;
2456
+ if (ch === "{") {
2457
+ if (!this.pushFrame({ type: "object" })) return;
2458
+ this.state = "expect-key-or-close";
2459
+ return;
2460
+ }
2461
+ if (ch === "[") {
2462
+ if (!this.pushFrame({ type: "array", currentIndex: 0 })) return;
2463
+ this.state = "expect-value";
2464
+ return;
2465
+ }
2466
+ if (ch === "]") {
2467
+ this.popFrame();
2468
+ return;
2469
+ }
2470
+ if (ch === '"') {
2471
+ this.currentPath = computePath(this.stack);
2472
+ this.deltaBuf = "";
2473
+ this.escaped = false;
2474
+ this.unicodePending = 0;
2475
+ this.unicodeBuf = "";
2476
+ this.state = "read-value-string";
2477
+ return;
2478
+ }
2479
+ if (isValueStartChar(ch)) {
2480
+ this.state = "read-value-other";
2481
+ return;
2482
+ }
2483
+ this.state = "done";
2484
+ return;
2485
+ case "read-value-string":
2486
+ this.processStringChar(
2487
+ ch,
2488
+ /* isValue */
2489
+ true
2490
+ );
2491
+ return;
2492
+ case "read-value-other":
2493
+ if (ch === "," || ch === "}" || ch === "]" || isWhitespace(ch)) {
2494
+ this.state = "expect-comma-close";
2495
+ this.processChar(ch);
2496
+ return;
2497
+ }
2498
+ return;
2499
+ case "expect-comma-close":
2500
+ if (isWhitespace(ch)) return;
2501
+ if (ch === ",") {
2502
+ this.boundaryPending = true;
2503
+ const top = this.stack[this.stack.length - 1];
2504
+ if (!top) {
2505
+ this.state = "done";
2506
+ return;
2507
+ }
2508
+ if (top.type === "object") {
2509
+ top.currentKey = void 0;
2510
+ this.state = "expect-key-or-close";
2511
+ } else {
2512
+ top.currentIndex += 1;
2513
+ this.state = "expect-value";
2514
+ }
2515
+ return;
2516
+ }
2517
+ if (ch === "}" || ch === "]") {
2518
+ this.popFrame();
2519
+ return;
2520
+ }
2521
+ this.state = "done";
2522
+ return;
2523
+ case "done":
2524
+ return;
2525
+ }
2526
+ }
2527
+ // ── String state machine (shared between key and value reads) ───────────
2528
+ /**
2529
+ * One char inside a string. Translates JSON escapes, accumulates
2530
+ * `\\uXXXX` digits across chunks, and routes the resolved char to
2531
+ * either `keyBuf` (when reading a key) or `deltaBuf` (when reading a
2532
+ * value).
2533
+ */
2534
+ processStringChar(ch, isValue) {
2535
+ if (this.unicodePending > 0) {
2536
+ if (isHex(ch)) {
2537
+ this.unicodeBuf += ch;
2538
+ this.unicodePending -= 1;
2539
+ if (this.unicodePending === 0) {
2540
+ const code = parseInt(this.unicodeBuf, 16);
2541
+ this.unicodeBuf = "";
2542
+ this.appendChar(String.fromCharCode(code), isValue);
2543
+ }
2544
+ } else {
2545
+ this.state = "done";
2546
+ }
2547
+ return;
2548
+ }
2549
+ if (this.escaped) {
2550
+ this.escaped = false;
2551
+ switch (ch) {
2552
+ case '"':
2553
+ this.appendChar('"', isValue);
2554
+ return;
2555
+ case "\\":
2556
+ this.appendChar("\\", isValue);
2557
+ return;
2558
+ case "/":
2559
+ this.appendChar("/", isValue);
2560
+ return;
2561
+ case "b":
2562
+ this.appendChar("\b", isValue);
2563
+ return;
2564
+ case "f":
2565
+ this.appendChar("\f", isValue);
2566
+ return;
2567
+ case "n":
2568
+ this.appendChar("\n", isValue);
2569
+ return;
2570
+ case "r":
2571
+ this.appendChar("\r", isValue);
2572
+ return;
2573
+ case "t":
2574
+ this.appendChar(" ", isValue);
2575
+ return;
2576
+ case "u":
2577
+ this.unicodePending = 4;
2578
+ this.unicodeBuf = "";
2579
+ return;
2580
+ default:
2581
+ this.state = "done";
2582
+ return;
2583
+ }
2584
+ }
2585
+ if (ch === "\\") {
2586
+ this.escaped = true;
2587
+ return;
2588
+ }
2589
+ if (ch === '"') {
2590
+ if (isValue) {
2591
+ if (this.deltaBuf.length > 0) {
2592
+ this.callbacks.onStringDelta?.(this.currentPath, this.deltaBuf);
2593
+ this.deltaBuf = "";
2594
+ }
2595
+ this.state = "expect-comma-close";
2596
+ } else {
2597
+ const top = this.stack[this.stack.length - 1];
2598
+ if (!top || top.type !== "object") {
2599
+ this.state = "done";
2600
+ return;
2601
+ }
2602
+ top.currentKey = this.keyBuf;
2603
+ this.keyBuf = "";
2604
+ this.state = "expect-colon";
2605
+ }
2606
+ return;
2607
+ }
2608
+ this.appendChar(ch, isValue);
2609
+ }
2610
+ appendChar(c, isValue) {
2611
+ if (isValue) {
2612
+ this.deltaBuf += c;
2613
+ } else {
2614
+ this.keyBuf += c;
2615
+ }
2616
+ }
2617
+ // ── Frame stack ─────────────────────────────────────────────────────────
2618
+ /** Push a frame onto the stack. Returns false if the stack is at the
2619
+ * depth cap — in which case the walker is also moved to `done` so the
2620
+ * caller's subsequent state assignment must be guarded. */
2621
+ pushFrame(frame) {
2622
+ if (this.stack.length >= MAX_DEPTH2) {
2623
+ this.state = "done";
2624
+ return false;
2625
+ }
2626
+ this.stack.push(frame);
2627
+ return true;
2628
+ }
2629
+ /** Pop a frame on `}` or `]`, fire boundary, transition to next state.
2630
+ * Defensive on empty stack — the state machine never invokes this
2631
+ * without an enclosing container today (callers gate on state, and
2632
+ * state transitions out of `pre-json` / `done` only after pushing),
2633
+ * but a future state-machine bug could land us here with nothing to
2634
+ * pop. Fail closed (move to `done`, no spurious boundary fire). */
2635
+ popFrame() {
2636
+ if (this.stack.length === 0) {
2637
+ this.state = "done";
2638
+ return;
2639
+ }
2640
+ this.stack.pop();
2641
+ this.boundaryPending = true;
2642
+ if (this.stack.length === 0) {
2643
+ this.state = "done";
2644
+ } else {
2645
+ this.state = "expect-comma-close";
2646
+ }
2647
+ }
2648
+ };
2649
+ function isWhitespace(ch) {
2650
+ return ch === " " || ch === " " || ch === "\n" || ch === "\r";
2651
+ }
2652
+ function isHex(ch) {
2653
+ return ch >= "0" && ch <= "9" || ch >= "a" && ch <= "f" || ch >= "A" && ch <= "F";
2654
+ }
2655
+ function isValueStartChar(ch) {
2656
+ return ch === "-" || ch >= "0" && ch <= "9" || ch === "t" || ch === "f" || ch === "n";
2657
+ }
2658
+
2340
2659
  // src/event-utils.ts
2341
2660
  var COST_BEARING_LEAF_TYPES = [
2342
2661
  "agent_call_end",
@@ -2393,6 +2712,12 @@ var AXL_EVENT_TYPES = [
2393
2712
  "pipeline",
2394
2713
  // Progressive structured output — added in PR 2; reserved here.
2395
2714
  "partial_object",
2715
+ // Character-level deltas inside string values of progressive structured
2716
+ // output. Same gating as `partial_object` (schema set, no tools, root is
2717
+ // ZodObject); fires per text-delta chunk when the walker is inside a
2718
+ // string value at a known JSON Pointer path. Designed for chat-style
2719
+ // typewriter rendering of long string fields.
2720
+ "string_delta",
2396
2721
  // Verification
2397
2722
  "verify",
2398
2723
  // Observability
@@ -2490,6 +2815,27 @@ var AxlEventBus = class {
2490
2815
  * number of distinct askIds that emitted at least one partial.
2491
2816
  */
2492
2817
  latestPartialByAsk = /* @__PURE__ */ new Map();
2818
+ /**
2819
+ * Per-ask, per-path running accumulator for `string_delta` events,
2820
+ * updated on every `_push(string_delta)` regardless of consumer state.
2821
+ * Used to seed late `stringStream` subscribers with the current text-
2822
+ * so-far per (askId, path) pair, so a UI binding to `/summary` mid-
2823
+ * stream renders the field's current state on first event instead of
2824
+ * waiting for the next chunk.
2825
+ *
2826
+ * Cleared per-askId on `pipeline(failed)` (discarded attempt — old
2827
+ * deltas must not leak into the next attempt's view) and on `ask_end`
2828
+ * (frees memory; stream-event accumulators don't outlive the ask).
2829
+ *
2830
+ * Memory bound: O(active asks × distinct paths × total chars per
2831
+ * field). For typical chat workflows (1 ask, 1-3 streaming string
2832
+ * fields) this is a few KB. The clear-on-`ask_end` cap keeps a
2833
+ * long-running runtime from accumulating per-ask entries forever
2834
+ * (the way `latestPartialByAsk` does — partial snapshots are tiny by
2835
+ * comparison so that map's growth is acceptable; string accumulators
2836
+ * can be 4 KB+ each, hence the more aggressive cleanup here).
2837
+ */
2838
+ stringStreamByAsk = /* @__PURE__ */ new Map();
2493
2839
  /**
2494
2840
  * Hook fired when an iterator obtained from `[Symbol.asyncIterator]`
2495
2841
  * is `await using`-disposed. Set via the second constructor argument.
@@ -2793,6 +3139,141 @@ var AxlEventBus = class {
2793
3139
  }
2794
3140
  };
2795
3141
  }
3142
+ /**
3143
+ * Listener-based view over `string_delta` events with optional
3144
+ * filtering by `path` and/or `askId`. Yields `StringStreamEvent`s
3145
+ * carrying `delta` (the new chars) and `accumulated` (the full text-
3146
+ * so-far for that ask + path).
3147
+ *
3148
+ * Designed for chat-style typewriter rendering of long string fields:
3149
+ * a React component binding to `path: '/summary'` sets its text to
3150
+ * `event.accumulated` on every yield, getting smooth char-by-char
3151
+ * progression with no parser of its own.
3152
+ *
3153
+ * Late-subscriber seeding: at subscribe time, the bus's per-ask
3154
+ * accumulator is walked and one synthetic `StringStreamEvent` is
3155
+ * yielded per matching (askId, path) entry, with `delta ===
3156
+ * accumulated === <full text-so-far>`. After that, live deltas flow
3157
+ * normally. A consumer who attaches mid-ask sees the field's current
3158
+ * state on first iteration, then incremental updates.
3159
+ *
3160
+ * Like `partialObjects`, this view is listener-based — it does NOT
3161
+ * race the main async iterator (`for await (const e of bus)`).
3162
+ * Running both concurrently each receives every matching event.
3163
+ *
3164
+ * Schema-retry awareness: on `pipeline(status: 'failed')`, the
3165
+ * accumulator and any pending events for the affected ask are
3166
+ * dropped, so a consumer iterating during attempt-N never observes
3167
+ * stale text after attempt-N+1 begins. Mirrors `partialObjects`'
3168
+ * per-attempt reset.
3169
+ *
3170
+ * Termination: yields any pending events first (live + seeded), then
3171
+ * `done: true` once the bus is finished via `_finish()`.
3172
+ */
3173
+ stringStream(opts) {
3174
+ const self = this;
3175
+ const filterPath = opts?.path;
3176
+ const filterAskId = opts?.askId;
3177
+ if (filterPath !== void 0 && filterPath !== "" && !filterPath.startsWith("/")) {
3178
+ throw new Error(
3179
+ `AxlEventBus.stringStream({ path }) \u2014 path must start with '/' (RFC 6901 JSON Pointer); got ${JSON.stringify(filterPath)}. Examples: '/summary', '/sources/0/title'.`
3180
+ );
3181
+ }
3182
+ return {
3183
+ [Symbol.asyncIterator]() {
3184
+ const pending = [];
3185
+ let resolveWaiter = null;
3186
+ let finished = self.finished;
3187
+ let unsubscribed = false;
3188
+ const wakeWaiter = () => {
3189
+ const r = resolveWaiter;
3190
+ resolveWaiter = null;
3191
+ r?.();
3192
+ };
3193
+ const matchesFilter = (askId, path) => {
3194
+ if (filterAskId !== void 0 && askId !== filterAskId) return false;
3195
+ if (filterPath !== void 0 && path !== filterPath) return false;
3196
+ return true;
3197
+ };
3198
+ for (const [askId, paths] of self.stringStreamByAsk) {
3199
+ for (const [path, acc] of paths) {
3200
+ if (!matchesFilter(askId, path)) continue;
3201
+ pending.push({
3202
+ askId,
3203
+ agent: acc.agent,
3204
+ path,
3205
+ delta: acc.text,
3206
+ accumulated: acc.text,
3207
+ attempt: acc.attempt
3208
+ });
3209
+ }
3210
+ }
3211
+ const handler = (event) => {
3212
+ if (event.type !== "string_delta") return;
3213
+ const askId = event.askId ?? "";
3214
+ if (!matchesFilter(askId, event.data.path)) return;
3215
+ const paths = self.stringStreamByAsk.get(askId);
3216
+ const acc = paths?.get(event.data.path);
3217
+ const accumulated = acc?.text ?? event.data.delta;
3218
+ pending.push({
3219
+ askId,
3220
+ agent: event.agent,
3221
+ path: event.data.path,
3222
+ delta: event.data.delta,
3223
+ accumulated,
3224
+ attempt: event.attempt
3225
+ });
3226
+ wakeWaiter();
3227
+ };
3228
+ self.bus.on("string_delta", handler);
3229
+ const pipelineHandler = (event) => {
3230
+ if (event.type !== "pipeline") return;
3231
+ if (event.status !== "failed") return;
3232
+ const askId = event.askId ?? "";
3233
+ for (let i = pending.length - 1; i >= 0; i--) {
3234
+ if (pending[i].askId === askId) pending.splice(i, 1);
3235
+ }
3236
+ };
3237
+ self.bus.on("pipeline", pipelineHandler);
3238
+ const unsubscribe = () => {
3239
+ if (unsubscribed) return;
3240
+ unsubscribed = true;
3241
+ self.bus.off("string_delta", handler);
3242
+ self.bus.off("pipeline", pipelineHandler);
3243
+ unsubscribeFinish();
3244
+ };
3245
+ const unsubscribeFinish = self._onFinish(() => {
3246
+ finished = true;
3247
+ self.bus.off("string_delta", handler);
3248
+ self.bus.off("pipeline", pipelineHandler);
3249
+ wakeWaiter();
3250
+ });
3251
+ return {
3252
+ async next() {
3253
+ while (true) {
3254
+ if (pending.length > 0) {
3255
+ return { value: pending.shift(), done: false };
3256
+ }
3257
+ if (finished) {
3258
+ unsubscribe();
3259
+ return { value: void 0, done: true };
3260
+ }
3261
+ await new Promise((res) => {
3262
+ resolveWaiter = res;
3263
+ });
3264
+ }
3265
+ },
3266
+ async return() {
3267
+ unsubscribe();
3268
+ return { value: void 0, done: true };
3269
+ },
3270
+ [Symbol.asyncIterator]() {
3271
+ return this;
3272
+ }
3273
+ };
3274
+ }
3275
+ };
3276
+ }
2796
3277
  /** Iterator over structural lifecycle events only — skips per-token chatter
2797
3278
  * and progressive `partial_object` emissions. Useful for waterfall UIs
2798
3279
  * and any consumer that wants the "what happened" timeline. */
@@ -2829,14 +3310,6 @@ var AxlEventBus = class {
2829
3310
  */
2830
3311
  _push(event) {
2831
3312
  if (this.finished) return;
2832
- try {
2833
- this.bus.emit(event.type, event);
2834
- } catch (err) {
2835
- console.error(
2836
- `[axl] AxlEventBus listener for "${event.type}" threw; ignoring:`,
2837
- err instanceof Error ? err.message : String(err)
2838
- );
2839
- }
2840
3313
  if (event.type === "partial_object" && event.data?.object !== void 0) {
2841
3314
  const askId = event.askId ?? "";
2842
3315
  this.latestPartialByAsk.set(askId, {
@@ -2845,8 +3318,33 @@ var AxlEventBus = class {
2845
3318
  object: event.data.object,
2846
3319
  attempt: event.attempt
2847
3320
  });
3321
+ } else if (event.type === "string_delta") {
3322
+ const askId = event.askId ?? "";
3323
+ let paths = this.stringStreamByAsk.get(askId);
3324
+ if (!paths) {
3325
+ paths = /* @__PURE__ */ new Map();
3326
+ this.stringStreamByAsk.set(askId, paths);
3327
+ }
3328
+ const existing = paths.get(event.data.path);
3329
+ paths.set(event.data.path, {
3330
+ agent: event.agent,
3331
+ text: (existing?.text ?? "") + event.data.delta,
3332
+ attempt: event.attempt
3333
+ });
2848
3334
  } else if (event.type === "pipeline" && event.status === "failed") {
2849
- this.latestPartialByAsk.delete(event.askId ?? "");
3335
+ const askId = event.askId ?? "";
3336
+ this.latestPartialByAsk.delete(askId);
3337
+ this.stringStreamByAsk.delete(askId);
3338
+ } else if (event.type === "ask_end") {
3339
+ this.stringStreamByAsk.delete(event.askId ?? "");
3340
+ }
3341
+ try {
3342
+ this.bus.emit(event.type, event);
3343
+ } catch (err) {
3344
+ console.error(
3345
+ `[axl] AxlEventBus listener for "${event.type}" threw; ignoring:`,
3346
+ err instanceof Error ? err.message : String(err)
3347
+ );
2850
3348
  }
2851
3349
  const waiter = this.waiters.shift();
2852
3350
  if (waiter) {
@@ -3064,6 +3562,9 @@ var REDACTION_RULES = {
3064
3562
  handoff_return: passthrough,
3065
3563
  pipeline: (e) => e.status === "failed" ? { ...e, reason: REDACTED } : e,
3066
3564
  partial_object: (e) => ({ ...e, data: { ...e.data, object: REDACTED } }),
3565
+ // `path` is structural (schema-shape, no PII); `delta` is user content.
3566
+ // Scrub only the delta — preserving path keeps "which fields stream" telemetry.
3567
+ string_delta: (e) => ({ ...e, data: { ...e.data, delta: REDACTED } }),
3067
3568
  verify: (e) => e.data.lastError !== void 0 ? { ...e, data: { ...e.data, lastError: REDACTED } } : e,
3068
3569
  log: (e) => {
3069
3570
  if (!e.data || typeof e.data !== "object" || Array.isArray(e.data)) return e;
@@ -3907,36 +4408,26 @@ ${JSON.stringify(jsonSchema, null, 2)}`;
3907
4408
  const toolCallBuffers = /* @__PURE__ */ new Map();
3908
4409
  let streamProviderMetadata;
3909
4410
  let thinkingContent = "";
3910
- const partialObjectEnabled = !!options?.schema && toolDefs.length === 0 && options.schema instanceof z.ZodObject;
4411
+ const streamingObjectEnabled = !!options?.schema && toolDefs.length === 0 && options.schema instanceof z.ZodObject;
3911
4412
  const currentAttempt = schemaRetries + 1;
3912
- let inString = false;
3913
- let escaped = false;
3914
- let boundaryPending = false;
4413
+ const walker = streamingObjectEnabled ? new StreamingWalker({
4414
+ onStringDelta: (path, delta) => {
4415
+ this.emitEvent({
4416
+ type: "string_delta",
4417
+ agent: agent2._name,
4418
+ attempt: currentAttempt,
4419
+ data: { path, delta }
4420
+ });
4421
+ }
4422
+ }) : void 0;
3915
4423
  for await (const chunk of provider.stream(currentMessages, chatOptions)) {
3916
4424
  if (chunk.type === "text_delta") {
3917
4425
  content2 += chunk.content;
3918
4426
  this.emitEvent({ type: "token", data: chunk.content });
3919
4427
  this.onToken?.(chunk.content, callbackMeta);
3920
- if (partialObjectEnabled) {
3921
- for (const ch of chunk.content) {
3922
- if (escaped) {
3923
- escaped = false;
3924
- continue;
3925
- }
3926
- if (ch === "\\") {
3927
- escaped = true;
3928
- continue;
3929
- }
3930
- if (ch === '"') {
3931
- inString = !inString;
3932
- continue;
3933
- }
3934
- if (!inString && (ch === "," || ch === "}" || ch === "]")) {
3935
- boundaryPending = true;
3936
- }
3937
- }
3938
- if (boundaryPending) {
3939
- boundaryPending = false;
4428
+ if (walker) {
4429
+ walker.processChunk(chunk.content);
4430
+ if (walker.consumeBoundary()) {
3940
4431
  let parsed;
3941
4432
  try {
3942
4433
  parsed = parsePartialJson(extractJson(content2));
@@ -6745,6 +7236,20 @@ var AxlStream = class extends Readable {
6745
7236
  get partialObjects() {
6746
7237
  return this.events.partialObjects;
6747
7238
  }
7239
+ /** Listener-based view over `string_delta` events, with optional filter
7240
+ * on `path` and/or `askId`. Yields `StringStreamEvent`s carrying the new
7241
+ * `delta` and full text-so-far `accumulated`. Designed for chat-style
7242
+ * typewriter rendering of long string fields — bind a UI component to
7243
+ * one path and set its text to `event.accumulated` per yield.
7244
+ *
7245
+ * Late subscribers receive a synthetic event seeded from the bus's
7246
+ * per-ask accumulator (so the field's current state renders on the
7247
+ * first iteration, not after the next char arrives). Doesn't race the
7248
+ * main async iterator. See `AxlEventBus.stringStream` for full
7249
+ * semantics. */
7250
+ stringStream(opts) {
7251
+ return this.events.stringStream(opts);
7252
+ }
6748
7253
  pipe(destination, options) {
6749
7254
  const shouldEnd = options?.end !== false;
6750
7255
  this.events.on("token", (event) => {
@@ -7404,8 +7909,8 @@ async function createSpanManager(config) {
7404
7909
  if (!config?.enabled) {
7405
7910
  return new NoopSpanManager();
7406
7911
  }
7407
- const { OTelSpanManager: OTelSpanManager2 } = await import("./span-manager-3IKXXUTZ.js");
7408
- return OTelSpanManager2.create(config);
7912
+ const { OTelSpanManager } = await import("./span-manager-3IKXXUTZ.js");
7913
+ return OTelSpanManager.create(config);
7409
7914
  }
7410
7915
 
7411
7916
  // src/runtime.ts
@@ -8427,7 +8932,9 @@ var AxlRuntime = class extends EventEmitter2 {
8427
8932
  workflowCalls.set(event.workflow, (workflowCalls.get(event.workflow) ?? 0) + 1);
8428
8933
  }
8429
8934
  if (capturedTraces) {
8430
- if (event.type === "token" || event.type === "partial_object") return;
8935
+ if (event.type === "token" || event.type === "partial_object" || event.type === "string_delta") {
8936
+ return;
8937
+ }
8431
8938
  if (event.type === "agent_call_start" && event.data) {
8432
8939
  const d = event.data;
8433
8940
  if ("messages" in d) {
@@ -8751,6 +9258,49 @@ async function registerConditions(conditions) {
8751
9258
  }
8752
9259
  }
8753
9260
 
9261
+ // src/string-stream-from-events.ts
9262
+ async function* stringStreamFromEvents(source, opts) {
9263
+ const filterPath = opts?.path;
9264
+ const filterAskId = opts?.askId;
9265
+ if (filterPath !== void 0 && filterPath !== "" && !filterPath.startsWith("/")) {
9266
+ throw new Error(
9267
+ `stringStreamFromEvents({ path }) \u2014 path must start with '/' (RFC 6901 JSON Pointer); got ${JSON.stringify(filterPath)}. Examples: '/summary', '/sources/0/title'.`
9268
+ );
9269
+ }
9270
+ const acc = /* @__PURE__ */ new Map();
9271
+ for await (const event of source) {
9272
+ if (event.type === "string_delta") {
9273
+ const askId = event.askId ?? "";
9274
+ let paths = acc.get(askId);
9275
+ if (!paths) {
9276
+ paths = /* @__PURE__ */ new Map();
9277
+ acc.set(askId, paths);
9278
+ }
9279
+ const existing = paths.get(event.data.path);
9280
+ const accumulated = (existing?.text ?? "") + event.data.delta;
9281
+ paths.set(event.data.path, {
9282
+ agent: event.agent,
9283
+ text: accumulated,
9284
+ attempt: event.attempt
9285
+ });
9286
+ if (filterAskId !== void 0 && askId !== filterAskId) continue;
9287
+ if (filterPath !== void 0 && event.data.path !== filterPath) continue;
9288
+ yield {
9289
+ askId,
9290
+ agent: event.agent,
9291
+ path: event.data.path,
9292
+ delta: event.data.delta,
9293
+ accumulated,
9294
+ attempt: event.attempt
9295
+ };
9296
+ } else if (event.type === "pipeline" && event.status === "failed") {
9297
+ acc.delete(event.askId ?? "");
9298
+ } else if (event.type === "ask_end") {
9299
+ acc.delete(event.askId ?? "");
9300
+ }
9301
+ }
9302
+ }
9303
+
8754
9304
  // src/state/redis.ts
8755
9305
  var RedisStore = class _RedisStore {
8756
9306
  constructor(client) {
@@ -9155,6 +9705,7 @@ export {
9155
9705
  registerConditions,
9156
9706
  resolveRuntime,
9157
9707
  resolveThinkingOptions,
9708
+ stringStreamFromEvents,
9158
9709
  tool,
9159
9710
  workflow,
9160
9711
  zodToJsonSchema