@tangle-network/agent-app 0.43.71 → 0.43.72

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.
@@ -186,6 +186,15 @@ declare function createAssistantDraftWriter(options: AssistantDraftWriterOptions
186
186
  * `updateMessage` a draft row could never be patched, so the caller keeps
187
187
  * today's exact single-write behavior. */
188
188
  declare function storeSupportsDraftPersistence(store: AssistantDraftStore): boolean;
189
+ /** The row id an `appendMessage` actually returned, or `null` when the store
190
+ * returned nothing usable. `ChatTurnMessageStore.appendMessage` is typed
191
+ * `Promise<unknown>` so a product adapter is free to resolve `void`; every
192
+ * caller that wants to NAME the row it just wrote has to read defensively.
193
+ *
194
+ * Deliberately no fallback to a caller-assigned id — see `writeOnce`, which
195
+ * adds its own. A caller that let the store mint the id has nothing to fall
196
+ * back TO, and guessing one would report a row that may not exist. */
197
+ declare function rowIdOf(inserted: unknown): string | null;
189
198
  /** The default deterministic assistant-row id for a turn. Readable on purpose
190
199
  * (an operator grepping a transcript row id finds the run), and stable across
191
200
  * re-entries because every input already is. */
@@ -360,6 +369,20 @@ interface ChatTurnProduceArgs<TContext> {
360
369
  /** The turn-buffer id announced to the client for replay. */
361
370
  turnStreamId: string;
362
371
  priorMessages: PersistedChatMessageForTurn[];
372
+ /** The durable `role:'user'` row this turn is anchored to — the row the
373
+ * factory just inserted, or the one retry-dedup REUSED. Products anchor
374
+ * optimistic-bubble swaps, retry targeting, and stop-polling to it, and it
375
+ * is the only way to name a REUSED row (which `priorMessages` excludes).
376
+ *
377
+ * Three states, all meaningful:
378
+ * - `undefined` — not resolved yet. `turnLock.acquire` is the one seam that
379
+ * sees this: it runs before any side effect by contract, so the row does
380
+ * not exist when it reads the args.
381
+ * - `null` — resolved, no row: `authorize` returned `insertUserMessage:
382
+ * false` on a turn with nothing to reuse, or the store's `appendMessage`
383
+ * resolved without a usable id.
384
+ * - a string — the row id, for `contextGate`, `beforeTurn`, and `produce`. */
385
+ userMessageId?: string | null;
363
386
  }
364
387
  /** One event as it crosses the route: the producer's own vocabulary, or an
365
388
  * injected keepalive. Same shape the engine forwards verbatim. */
@@ -434,6 +457,11 @@ interface ChatTurnLifecycleComplete<TContext> extends ChatTurnLifecycleBase<TCon
434
457
  /** Attribution for a downgrade: which models were tried and why each failed.
435
458
  * `undefined` when the producer reports no failover support. */
436
459
  modelFailover?: ChatTurnModelFailover;
460
+ /** The durable `role:'assistant'` row this turn wrote, or `null` when it
461
+ * wrote none (an empty turn leaves no row — a draft started mid-stream is
462
+ * retracted). The detached lane surfaces the same id as
463
+ * `DetachedTurnResult.messageId`; one contract, two lanes. */
464
+ assistantMessageId: string | null;
437
465
  }
438
466
  /** Represent an error occurring during a chat turn lifecycle with context and duration information */
439
467
  interface ChatTurnLifecycleError<TContext> extends ChatTurnLifecycleBase<TContext> {
@@ -451,6 +479,31 @@ interface ChatTurnLifecycle<TContext> {
451
479
  onTurnComplete?(info: ChatTurnLifecycleComplete<TContext>): void | Promise<void>;
452
480
  onTurnError?(info: ChatTurnLifecycleError<TContext>): void | Promise<void>;
453
481
  }
482
+ /** What a settled turn reports to `onTurnComplete` — the product's
483
+ * post-processing seam (billing, titles, audit). */
484
+ interface ChatTurnCompleteInput<TContext> {
485
+ identity: ChatTurnIdentity;
486
+ finalText: string;
487
+ context: TContext;
488
+ failed: boolean;
489
+ failureReason?: string;
490
+ /** The model that SERVED this turn. With failover wired it may differ from
491
+ * the requested one, so a product that bills or scores per model MUST read
492
+ * it here rather than assuming the model it asked for. */
493
+ model?: string;
494
+ /** Present when the producer supports failover: the full attempt trail, and
495
+ * `usedFallback` — the flag that makes a silent downgrade impossible. */
496
+ modelFailover?: ChatTurnModelFailover;
497
+ /** The durable `role:'assistant'` row this turn wrote, or `null` when it
498
+ * wrote none (an empty turn leaves no row).
499
+ *
500
+ * Populated even when `failed` is true — a terminal error event still
501
+ * persists whatever partial answer arrived, and that pairing is exactly
502
+ * what lets a product render an error row against a REAL message instead of
503
+ * hunting for the newest row in the thread. The detached lane surfaces the
504
+ * same id as `DetachedTurnResult.messageId`. */
505
+ assistantMessageId: string | null;
506
+ }
454
507
  /** Define options to configure chat turn routes including authorization, storage, and event buffering */
455
508
  interface CreateChatTurnRoutesOptions<TContext = void> {
456
509
  /** Names the product in `deriveExecutionId` so retries land on the same
@@ -534,20 +587,7 @@ interface CreateChatTurnRoutesOptions<TContext = void> {
534
587
  * than billing an empty turn and marking it done. A turn that THROWS never
535
588
  * reaches this hook (the engine skips it on a producer throw). Errors are
536
589
  * swallowed by the engine — they never fail a streamed turn. */
537
- onTurnComplete?(input: {
538
- identity: ChatTurnIdentity;
539
- finalText: string;
540
- context: TContext;
541
- failed: boolean;
542
- failureReason?: string;
543
- /** The model that SERVED this turn. With failover wired it may differ from
544
- * the requested one, so a product that bills or scores per model MUST read
545
- * it here rather than assuming the model it asked for. */
546
- model?: string;
547
- /** Present when the producer supports failover: the full attempt trail, and
548
- * `usedFallback` — the flag that makes a silent downgrade impossible. */
549
- modelFailover?: ChatTurnModelFailover;
550
- }): Promise<void>;
590
+ onTurnComplete?(input: ChatTurnCompleteInput<TContext>): Promise<void>;
551
591
  /** Per-event side channel (product broadcast). The turn-buffer tap is
552
592
  * already wired; this runs in addition. */
553
593
  onEvent?(event: {
@@ -1517,4 +1557,4 @@ interface PromoteAgentFilePartOptions {
1517
1557
  /** Promote a part of an agent file with optional byte limits and MIME type detection */
1518
1558
  declare function promoteAgentFilePart(options: PromoteAgentFilePartOptions): Promise<PromoteFilePartResult>;
1519
1559
 
1520
- 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 ChatTurnAuthorization, type ChatTurnAuthorizeArgs, 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 FilePartPromotionOutcome, 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, runDetachedTurn, sanitizeUploadFilename, sniffMimeFromName, storeSupportsDraftPersistence, streamWithModelFailover, withDurableChatProjection };
1560
+ 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 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 FilePartPromotionOutcome, 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, rowIdOf, runDetachedTurn, sanitizeUploadFilename, sniffMimeFromName, storeSupportsDraftPersistence, streamWithModelFailover, withDurableChatProjection };
@@ -31,7 +31,7 @@ import {
31
31
  normalizeClientTurnId,
32
32
  replayTurnEvents,
33
33
  resolveChatTurn
34
- } from "../chunk-YN7QR7MJ.js";
34
+ } from "../chunk-RK2MKLBT.js";
35
35
  import {
36
36
  createInteractionAnswerRoute
37
37
  } from "../chunk-3JUTOVUH.js";
@@ -216,8 +216,7 @@ function createAssistantDraftWriter(options) {
216
216
  role: "assistant",
217
217
  ...values
218
218
  });
219
- const insertedId = inserted?.id;
220
- rowId = typeof insertedId === "string" && insertedId ? insertedId : options.messageId;
219
+ rowId = rowIdOf(inserted) ?? options.messageId;
221
220
  writes += 1;
222
221
  }
223
222
  function trigger() {
@@ -284,6 +283,10 @@ function createAssistantDraftWriter(options) {
284
283
  function storeSupportsDraftPersistence(store) {
285
284
  return typeof store.updateMessage === "function";
286
285
  }
286
+ function rowIdOf(inserted) {
287
+ const id = inserted?.id;
288
+ return typeof id === "string" && id ? id : null;
289
+ }
287
290
  function assistantRowIdForTurn(turnKey) {
288
291
  return `assistant:${turnKey}`;
289
292
  }
@@ -464,6 +467,8 @@ function createChatTurnRoutes(options) {
464
467
  }
465
468
  let producer;
466
469
  let draft;
470
+ let assistantMessageId;
471
+ const assistantRowId = () => assistantMessageId ?? draft?.rowId() ?? null;
467
472
  let runFailed = false;
468
473
  let lastFailureData;
469
474
  let turnStartedAtMs = 0;
@@ -495,6 +500,7 @@ function createChatTurnRoutes(options) {
495
500
  durationMs,
496
501
  finalText: producer?.finalText() ?? "",
497
502
  usage: producer?.usage?.() ?? {},
503
+ assistantMessageId: assistantRowId(),
498
504
  ...producer?.model ? { model: producer.model } : {},
499
505
  ...failoverInfo ? { modelFailover: failoverInfo } : {}
500
506
  });
@@ -508,14 +514,18 @@ function createChatTurnRoutes(options) {
508
514
  };
509
515
  try {
510
516
  const insertUserMessage = chatTurn.shouldInsertUserMessage && (auth.insertUserMessage ?? true);
517
+ let userMessageId = chatTurn.reusedUserMessageId ?? null;
511
518
  if (insertUserMessage) {
512
- await options.store.appendMessage({
513
- threadId: payload.threadId,
514
- role: "user",
515
- content,
516
- parts: userPartsWithFiles(chatTurn.userParts, fileParts, mentions)
517
- });
519
+ userMessageId = rowIdOf(
520
+ await options.store.appendMessage({
521
+ threadId: payload.threadId,
522
+ role: "user",
523
+ content,
524
+ parts: userPartsWithFiles(chatTurn.userParts, fileParts, mentions)
525
+ })
526
+ );
518
527
  }
528
+ produceArgs = { ...produceArgs, userMessageId };
519
529
  if (options.contextGate) {
520
530
  const gate = await options.contextGate(produceArgs);
521
531
  if (!gate.proceed) {
@@ -613,6 +623,7 @@ function createChatTurnRoutes(options) {
613
623
  const parts = projected ? toChatMessageParts(projected) : void 0;
614
624
  if (!finalText.trim() && (!parts || parts.length === 0)) {
615
625
  await draft?.discard();
626
+ assistantMessageId = draft?.rowId() ?? null;
616
627
  return;
617
628
  }
618
629
  const usage = producer?.usage?.() ?? {};
@@ -629,13 +640,16 @@ function createChatTurnRoutes(options) {
629
640
  };
630
641
  if (draft) {
631
642
  await draft.finalize(values);
643
+ assistantMessageId = draft.rowId() ?? null;
632
644
  return;
633
645
  }
634
- await options.store.appendMessage({
635
- threadId: payload.threadId,
636
- role: "assistant",
637
- ...values
638
- });
646
+ assistantMessageId = rowIdOf(
647
+ await options.store.appendMessage({
648
+ threadId: payload.threadId,
649
+ role: "assistant",
650
+ ...values
651
+ })
652
+ );
639
653
  },
640
654
  ...options.onTurnComplete ? {
641
655
  // Wired into the engine's completion hook, which fires only when
@@ -650,6 +664,7 @@ function createChatTurnRoutes(options) {
650
664
  finalText,
651
665
  context,
652
666
  failed: runFailed,
667
+ assistantMessageId: assistantRowId(),
653
668
  ...runFailed ? { failureReason: failureReasonOf(lastFailureData) } : {},
654
669
  ...producer?.model ? { model: producer.model } : {},
655
670
  ...failoverInfo ? { modelFailover: failoverInfo } : {}
@@ -2288,6 +2303,7 @@ export {
2288
2303
  promptPartsByteSize,
2289
2304
  reconcileStaleTurnLock,
2290
2305
  resolveChatAttachments,
2306
+ rowIdOf,
2291
2307
  runDetachedTurn,
2292
2308
  sanitizeAttachmentFileName,
2293
2309
  sanitizeUploadFilename,