@tangle-network/agent-app 0.47.11 → 0.47.13

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/README.md CHANGED
@@ -36,7 +36,7 @@ pnpm add @tangle-network/agent-eval @tangle-network/agent-integrations
36
36
  | Peer | Required by | Range |
37
37
  |---|---|---|
38
38
  | `@tangle-network/agent-eval` | `/eval`, `/eval-campaign`, `/profile`, `/knowledge` | `>=0.179.0 <0.180.0` |
39
- | `@tangle-network/agent-runtime` | `/runtime`, `/chat-routes` | `>=0.204.1 <0.205.0` |
39
+ | `@tangle-network/agent-runtime` | `/runtime`, `/chat-routes` | `>=0.207.0 <0.208.0` |
40
40
  | `@tangle-network/agent-integrations` | `/integrations` | `>=0.53.55 <0.54.0` |
41
41
  | `@tangle-network/agent-interface` | `/interactions`, `/chat-store`, `/harness` | `^2.6.0` |
42
42
  | `@tangle-network/sandbox` | `/sandbox` | `>=0.38.2 <0.39.0` |
@@ -160,6 +160,10 @@ function summarizeFailoverReason(reason) {
160
160
  const lastSpace = clipped.lastIndexOf(" ");
161
161
  return `${(lastSpace > LIMIT - 30 ? clipped.slice(0, lastSpace) : clipped).trimEnd()}\u2026`;
162
162
  }
163
+ function isLiveLifecycleEvent(event) {
164
+ const type = asString(asRecord(event)?.type) ?? "";
165
+ return type === "start" || type === "execution.started" || type === "status" || type === "model-processing" || type === "model.processing" || type === "session.created" || type === "session.updated" || type === "session.idle";
166
+ }
163
167
  function classifyTerminalFailure(event) {
164
168
  const record = asRecord(event);
165
169
  if (!record) return null;
@@ -247,6 +251,8 @@ function streamWithModelFailover(options) {
247
251
  DEFAULT_MODEL_FIRST_RESPONSE_TIMEOUT_MS,
248
252
  "firstResponseTimeoutMs"
249
253
  );
254
+ const liveLifecycle = options.liveLifecycleEvents === true;
255
+ const progress = createProgressQueue();
250
256
  let serving;
251
257
  let trail = [];
252
258
  let fellBack = false;
@@ -368,7 +374,11 @@ function streamWithModelFailover(options) {
368
374
  empty: false
369
375
  };
370
376
  }
371
- buffered.push(event);
377
+ if (liveLifecycle && !failure && !commits && isLiveLifecycleEvent(event)) {
378
+ progress.push(event);
379
+ } else {
380
+ buffered.push(event);
381
+ }
372
382
  if (failure) {
373
383
  firstResponseDeadline.clear();
374
384
  return { outcome: { committed: true, buffered, iterator, abort }, empty: false };
@@ -437,24 +447,31 @@ function streamWithModelFailover(options) {
437
447
  };
438
448
  const events = (async function* () {
439
449
  let handle;
450
+ const settled = runWithModelFailover({
451
+ models: options.models,
452
+ run: probe,
453
+ // The probe has already classified the raw payload with
454
+ // `isUpstreamUnavailable`; this reads its verdict rather than
455
+ // re-classifying a wrapper object, so the two can never disagree.
456
+ isUnavailableResult: (result) => result.committed === false,
457
+ onFallback: (attempt, nextModel) => {
458
+ const info = {
459
+ from: attempt.model,
460
+ to: nextModel,
461
+ reason: attempt.reason ?? "upstream unavailable"
462
+ };
463
+ options.log?.("[chat-routes] model upstream unavailable; falling over", { ...info });
464
+ options.onFallback?.(info);
465
+ }
466
+ }).finally(() => progress.close());
467
+ settled.catch(() => void 0);
468
+ for (; ; ) {
469
+ const item = await progress.pull();
470
+ if (item === PROGRESS_CLOSED) break;
471
+ yield item;
472
+ }
440
473
  try {
441
- const outcome = await runWithModelFailover({
442
- models: options.models,
443
- run: probe,
444
- // The probe has already classified the raw payload with
445
- // `isUpstreamUnavailable`; this reads its verdict rather than
446
- // re-classifying a wrapper object, so the two can never disagree.
447
- isUnavailableResult: (result) => result.committed === false,
448
- onFallback: (attempt, nextModel) => {
449
- const info = {
450
- from: attempt.model,
451
- to: nextModel,
452
- reason: attempt.reason ?? "upstream unavailable"
453
- };
454
- options.log?.("[chat-routes] model upstream unavailable; falling over", { ...info });
455
- options.onFallback?.(info);
456
- }
457
- });
474
+ const outcome = await settled;
458
475
  serving = outcome.model;
459
476
  trail = outcome.attempts;
460
477
  fellBack = outcome.usedFallback;
@@ -494,6 +511,33 @@ function streamWithModelFailover(options) {
494
511
  usedFallback: () => fellBack
495
512
  };
496
513
  }
514
+ var PROGRESS_CLOSED = /* @__PURE__ */ Symbol("progress-closed");
515
+ function createProgressQueue() {
516
+ const items = [];
517
+ const waiters = [];
518
+ let closed = false;
519
+ const deliver = (value) => {
520
+ const waiter = waiters.shift();
521
+ if (waiter) waiter(value);
522
+ else items.push(value);
523
+ };
524
+ return {
525
+ push(event) {
526
+ if (closed) return;
527
+ deliver(event);
528
+ },
529
+ close() {
530
+ if (closed) return;
531
+ closed = true;
532
+ deliver(PROGRESS_CLOSED);
533
+ },
534
+ pull() {
535
+ if (items.length > 0) return Promise.resolve(items.shift());
536
+ if (closed) return Promise.resolve(PROGRESS_CLOSED);
537
+ return new Promise((resolve) => waiters.push(resolve));
538
+ }
539
+ };
540
+ }
497
541
 
498
542
  // src/chat-routes/sandbox-turn-usage.ts
499
543
  function addStepFinishUsage(part, usage) {
@@ -573,6 +617,17 @@ function extractReportedTurnUsage(data) {
573
617
  if (costUsd !== null) reported.costUsd = costUsd;
574
618
  return reported;
575
619
  }
620
+ function extractUsageSnapshot(value) {
621
+ const inputTokens = toFiniteNumber(value?.promptTokens);
622
+ const outputTokens = toFiniteNumber(value?.completionTokens);
623
+ if (inputTokens === null || outputTokens === null || inputTokens < 0 || outputTokens < 0) return null;
624
+ const reported = { inputTokens, outputTokens };
625
+ const reasoningTokens = toFiniteNumber(value?.reasoningTokens);
626
+ if (reasoningTokens !== null && reasoningTokens >= 0) reported.reasoningTokens = reasoningTokens;
627
+ const costUsd = toFiniteNumber(value?.providerCostUsd);
628
+ if (costUsd !== null && costUsd >= 0) reported.costUsd = costUsd;
629
+ return reported;
630
+ }
576
631
  function applyTerminalUsage(reported, usage) {
577
632
  usage.inputTokens = reported.inputTokens;
578
633
  usage.outputTokens = reported.outputTokens;
@@ -645,6 +700,7 @@ function createSandboxChatProducer(options) {
645
700
  ...options.firstResponseTimeoutMs !== void 0 ? { firstResponseTimeoutMs: options.firstResponseTimeoutMs } : {},
646
701
  ...options.emptyTurnRetries !== void 0 ? { emptyTurnRetries: options.emptyTurnRetries } : {},
647
702
  ...options.onEmptyTurnRetry ? { onEmptyTurnRetry: options.onEmptyTurnRetry } : {},
703
+ ...options.liveLifecycleEvents !== void 0 ? { liveLifecycleEvents: options.liveLifecycleEvents } : {},
648
704
  onFallback: (info) => {
649
705
  modelNoticeCount += 1;
650
706
  pendingModelNotices.push({
@@ -931,6 +987,12 @@ function createSandboxChatProducer(options) {
931
987
  yield toProducerWireEvent(record);
932
988
  continue;
933
989
  }
990
+ if (event.type === "usage") {
991
+ const reported = extractUsageSnapshot(asRecord(record.usage));
992
+ if (reported) applyTerminalUsage(reported, usage);
993
+ yield toProducerWireEvent(record);
994
+ continue;
995
+ }
934
996
  if (event.type === "result") {
935
997
  const finalText = asString(event.data?.finalText);
936
998
  if (finalText) fullText = finalText;
@@ -2787,6 +2849,7 @@ export {
2787
2849
  immutableAttachmentPath,
2788
2850
  isCommittingSandboxEvent,
2789
2851
  isDraftContentEvent,
2852
+ isLiveLifecycleEvent,
2790
2853
  mediaTypeForMentionPath,
2791
2854
  mentionKindForPath,
2792
2855
  normalizeChatPromptForSandbox,