@tangle-network/agent-app 0.44.9 → 0.44.11

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.
@@ -481,6 +481,14 @@ interface ChatTurnLifecycleComplete<TContext> extends ChatTurnLifecycleBase<TCon
481
481
  * retracted). The detached lane surfaces the same id as
482
482
  * `DetachedTurnResult.messageId`; one contract, two lanes. */
483
483
  assistantMessageId: string | null;
484
+ /** Set when `contextGate` answered this turn and the producer never ran.
485
+ *
486
+ * Present so telemetry can tell "the model produced nothing" apart from "the
487
+ * model was never asked" — they look identical here (`finalText: ''`,
488
+ * empty `usage`) and mean opposite things. A gated turn writes no assistant
489
+ * row, so `assistantMessageId` is `null`, and the billing/persistence
490
+ * `onTurnComplete` is deliberately NOT fired for it. */
491
+ gated?: true;
484
492
  }
485
493
  /** Represent an error occurring during a chat turn lifecycle with context and duration information */
486
494
  interface ChatTurnLifecycleError<TContext> extends ChatTurnLifecycleBase<TContext> {
@@ -743,6 +751,21 @@ declare function createChatTurnRoutes<TContext = void>(options: CreateChatTurnRo
743
751
  * missed failover; getting it wrong the other way duplicates a user's answer.
744
752
  */
745
753
  declare function isCommittingSandboxEvent(event: unknown): boolean;
754
+ /**
755
+ * Condense an abandoned attempt's raw failure text into something safe to show
756
+ * a customer in the transcript.
757
+ *
758
+ * Measured on the live router 2026-07-27 19:40 UTC: an edge 5xx arrives as
759
+ * Cloudflare's full HTML error PAGE, so the verbatim reason began
760
+ * `<!DOCTYPE html>\n<!--[if lt IE 7]> <html class="no-js ie6 oldie"…` and the
761
+ * fallback notice pasted 200 bytes of that markup into the answer the customer
762
+ * reads. The cause is worth stating; the markup is not.
763
+ *
764
+ * Only the DISPLAY string is condensed. The full text stays on
765
+ * `modelFailover.attempts[].reason` for the operator, so nothing is lost —
766
+ * this narrows what the customer sees, never what telemetry records.
767
+ */
768
+ declare function summarizeFailoverReason(reason: string): string;
746
769
  /** A terminal failure event, classified. `outage` decides failover vs surface. */
747
770
  interface TerminalFailure {
748
771
  outage: boolean;
@@ -1670,4 +1693,4 @@ interface PromoteAgentFilePartOptions {
1670
1693
  /** Promote a part of an agent file with optional byte limits and MIME type detection */
1671
1694
  declare function promoteAgentFilePart(options: PromoteAgentFilePartOptions): Promise<PromoteFilePartResult>;
1672
1695
 
1673
- export { type AssistantDraftSnapshot, type AssistantDraftStore, type AssistantDraftWriter, type AssistantDraftWriterOptions, type AssistantRowValues, type AttachmentPathArgs, type AttachmentPathCheck, type AttachmentReadResult, type AttachmentUploadAuthorization, type AttachmentWriteResult, type BuildDispatchPartsInput, ChatAttachmentKind, type ChatRouteDurableProjection, type ChatRouteDurableProjectionLogger, type ChatRouteEvent, type ChatTurnAuthorization, type ChatTurnAuthorizeArgs, type ChatTurnCompleteInput, ChatTurnFilePartInput, type ChatTurnGateResult, type ChatTurnHeartbeat, type ChatTurnInputPatch, type ChatTurnLifecycle, type ChatTurnLifecycleComplete, type ChatTurnLifecycleError, type ChatTurnLifecycleStart, type ChatTurnLock, type ChatTurnLockResult, type ChatTurnMessageStore, type ChatTurnModelFailover, ChatTurnPartInput, type ChatTurnProduceArgs, ChatTurnRequestPayload, type ChatTurnRouteProducer, type ChatTurnRoutes, type ChatTurnUsage, type CreateAttachmentUploadRouteOptions, type CreateChatTurnRoutesOptions, type CreateUploadRouteOptions, type DetachedTurnFinal, type DetachedTurnOptions, type DetachedTurnParts, type DetachedTurnResult, type DispatchPartsOutcome, type DraftPersistenceTuning, type DraftStoredMessage, type EmptyTurnRetryInfo, type FilePartPromotionOutcome, MAX_EMPTY_TURN_RETRIES, type ModelFailoverStreamHandle, type ModelFailoverStreamOptions, type ModelFallbackInfo, type OpenModelStream, PROMOTE_MAX_FILE_BYTES, type PromoteAgentFilePartOptions, type PromoteFilePartResult, PromptInputPart, type RawAgentFilePart, type ReadAttachmentFn, type ReadSandboxMentionFn, type ResolveChatAttachmentsOptions, type ResolveChatAttachmentsResult, type SandboxChatProducerOptions, type SandboxUploadSink, UPLOAD_INLINE_MAX_BYTES, UPLOAD_MAX_FILE_BYTES, type UploadAuthorization, type UploadedChatFile, type WriteAttachmentFn, assistantRowIdForTurn, buildDispatchParts, bytesToBase64, classifyTerminalFailure, createAssistantDraftWriter, createAttachmentUploadRoute, createChatTurnRoutes, createSandboxChatProducer, createUploadRoute, defaultValidateAttachmentPath, isCommittingSandboxEvent, isDraftContentEvent, promoteAgentFilePart, resolveChatAttachments, resolveEmptyTurnRetries, rowIdOf, runDetachedTurn, sanitizeUploadFilename, sniffMimeFromName, storeSupportsDraftPersistence, streamWithModelFailover, withDurableChatProjection };
1696
+ export { type AssistantDraftSnapshot, type AssistantDraftStore, type AssistantDraftWriter, type AssistantDraftWriterOptions, type AssistantRowValues, type AttachmentPathArgs, type AttachmentPathCheck, type AttachmentReadResult, type AttachmentUploadAuthorization, type AttachmentWriteResult, type BuildDispatchPartsInput, ChatAttachmentKind, type ChatRouteDurableProjection, type ChatRouteDurableProjectionLogger, type ChatRouteEvent, type ChatTurnAuthorization, type ChatTurnAuthorizeArgs, type ChatTurnCompleteInput, ChatTurnFilePartInput, type ChatTurnGateResult, type ChatTurnHeartbeat, type ChatTurnInputPatch, type ChatTurnLifecycle, type ChatTurnLifecycleComplete, type ChatTurnLifecycleError, type ChatTurnLifecycleStart, type ChatTurnLock, type ChatTurnLockResult, type ChatTurnMessageStore, type ChatTurnModelFailover, ChatTurnPartInput, type ChatTurnProduceArgs, ChatTurnRequestPayload, type ChatTurnRouteProducer, type ChatTurnRoutes, type ChatTurnUsage, type CreateAttachmentUploadRouteOptions, type CreateChatTurnRoutesOptions, type CreateUploadRouteOptions, type DetachedTurnFinal, type DetachedTurnOptions, type DetachedTurnParts, type DetachedTurnResult, type DispatchPartsOutcome, type DraftPersistenceTuning, type DraftStoredMessage, type EmptyTurnRetryInfo, type FilePartPromotionOutcome, MAX_EMPTY_TURN_RETRIES, type ModelFailoverStreamHandle, type ModelFailoverStreamOptions, type ModelFallbackInfo, type OpenModelStream, PROMOTE_MAX_FILE_BYTES, type PromoteAgentFilePartOptions, type PromoteFilePartResult, PromptInputPart, type RawAgentFilePart, type ReadAttachmentFn, type ReadSandboxMentionFn, type ResolveChatAttachmentsOptions, type ResolveChatAttachmentsResult, type SandboxChatProducerOptions, type SandboxUploadSink, UPLOAD_INLINE_MAX_BYTES, UPLOAD_MAX_FILE_BYTES, type UploadAuthorization, type UploadedChatFile, type WriteAttachmentFn, assistantRowIdForTurn, buildDispatchParts, bytesToBase64, classifyTerminalFailure, createAssistantDraftWriter, createAttachmentUploadRoute, createChatTurnRoutes, createSandboxChatProducer, createUploadRoute, defaultValidateAttachmentPath, isCommittingSandboxEvent, isDraftContentEvent, promoteAgentFilePart, resolveChatAttachments, resolveEmptyTurnRetries, rowIdOf, runDetachedTurn, sanitizeUploadFilename, sniffMimeFromName, storeSupportsDraftPersistence, streamWithModelFailover, summarizeFailoverReason, withDurableChatProjection };
@@ -86,6 +86,7 @@ import {
86
86
  import {
87
87
  buildModelChain,
88
88
  isUpstreamUnavailable,
89
+ readHttpStatusHint,
89
90
  runWithModelFailover
90
91
  } from "../chunk-DEXBRUZR.js";
91
92
  import {
@@ -474,6 +475,7 @@ function createChatTurnRoutes(options) {
474
475
  let turnStartedAtMs = 0;
475
476
  let turnStarted = false;
476
477
  let lifecycleSettled = false;
478
+ let gatedTurn = false;
477
479
  const fireTerminalLifecycle = async (failed, terminalError) => {
478
480
  if (lifecycleSettled) return;
479
481
  lifecycleSettled = true;
@@ -501,6 +503,7 @@ function createChatTurnRoutes(options) {
501
503
  finalText: producer?.finalText() ?? "",
502
504
  usage: producer?.usage?.() ?? {},
503
505
  assistantMessageId: assistantRowId(),
506
+ ...gatedTurn ? { gated: true } : {},
504
507
  ...producer?.model ? { model: producer.model } : {},
505
508
  ...failoverInfo ? { modelFailover: failoverInfo } : {}
506
509
  });
@@ -530,6 +533,25 @@ function createChatTurnRoutes(options) {
530
533
  const gate = await options.contextGate(produceArgs);
531
534
  if (!gate.proceed) {
532
535
  await releaseLock();
536
+ gatedTurn = true;
537
+ turnStartedAtMs = Date.now();
538
+ if (options.lifecycle?.onTurnStart) {
539
+ try {
540
+ await options.lifecycle.onTurnStart({
541
+ identity,
542
+ executionId,
543
+ turnStreamId,
544
+ context,
545
+ startedAt: turnStartedAtMs
546
+ });
547
+ } catch (err) {
548
+ log("[chat-routes] lifecycle.onTurnStart failed", {
549
+ turnId: turnStreamId,
550
+ error: err instanceof Error ? err.message : String(err)
551
+ });
552
+ }
553
+ }
554
+ await fireTerminalLifecycle(false, void 0);
533
555
  return gate.response;
534
556
  }
535
557
  }
@@ -831,6 +853,19 @@ function isCommittingSandboxEvent(event) {
831
853
  }
832
854
  return true;
833
855
  }
856
+ function summarizeFailoverReason(reason) {
857
+ const collapsed = reason.replace(/\s+/g, " ").trim();
858
+ if (!collapsed) return "upstream unavailable";
859
+ if (/<!doctype html|<html[\s>]/i.test(collapsed)) {
860
+ const status = readHttpStatusHint(collapsed);
861
+ return status ? `HTTP ${status}` : "upstream returned an error page";
862
+ }
863
+ const LIMIT = 160;
864
+ if (collapsed.length <= LIMIT) return collapsed;
865
+ const clipped = collapsed.slice(0, LIMIT);
866
+ const lastSpace = clipped.lastIndexOf(" ");
867
+ return `${(lastSpace > LIMIT - 30 ? clipped.slice(0, lastSpace) : clipped).trimEnd()}\u2026`;
868
+ }
834
869
  function classifyTerminalFailure(event) {
835
870
  const record = asRecord(event);
836
871
  if (!record) return null;
@@ -1103,8 +1138,12 @@ function createSandboxChatProducer(options) {
1103
1138
  pendingModelNotices.push({
1104
1139
  id: `model-fallback-${modelNoticeCount}`,
1105
1140
  // Named models on both sides: a quality regression after a downgrade
1106
- // must be attributable to the model that actually answered.
1107
- text: `${info.from} was unavailable (${info.reason}) \u2014 answered with ${info.to} instead.`
1141
+ // must be attributable to the model that actually answered. The
1142
+ // REASON is condensed a real edge 5xx arrives as Cloudflare's HTML
1143
+ // error page, and the raw text put `<!DOCTYPE html><!--[if lt IE 7]>…`
1144
+ // into the customer's transcript. The verbatim text is still on
1145
+ // `modelFailover.attempts[].reason` for the operator.
1146
+ text: `${info.from} was unavailable (${summarizeFailoverReason(info.reason)}) \u2014 answered with ${info.to} instead.`
1108
1147
  });
1109
1148
  options.onModelFallback?.(info);
1110
1149
  }
@@ -2355,6 +2394,7 @@ export {
2355
2394
  sniffMimeFromName,
2356
2395
  storeSupportsDraftPersistence,
2357
2396
  streamWithModelFailover,
2397
+ summarizeFailoverReason,
2358
2398
  validateSandboxMentionPath,
2359
2399
  withDurableChatProjection
2360
2400
  };