@tangle-network/agent-app 0.43.71 → 0.43.73
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/chat-routes/index.d.ts +127 -34
- package/dist/chat-routes/index.js +30 -14
- package/dist/chat-routes/index.js.map +1 -1
- package/dist/{chunk-YN7QR7MJ.js → chunk-RK2MKLBT.js} +4 -2
- package/dist/chunk-RK2MKLBT.js.map +1 -0
- package/dist/index.js +1 -1
- package/dist/stream/index.d.ts +7 -0
- package/dist/stream/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-YN7QR7MJ.js.map +0 -1
|
@@ -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. */
|
|
@@ -213,24 +222,31 @@ declare function assistantRowIdForTurn(turnKey: string): string;
|
|
|
213
222
|
* no router import. Auth/access is one injected `authorize` seam, composable
|
|
214
223
|
* with `/app-auth` guards but not coupled to them.
|
|
215
224
|
*
|
|
216
|
-
*
|
|
217
|
-
*
|
|
218
|
-
*
|
|
225
|
+
* Optional product seams let a complex turn-orchestrator compose the vertical
|
|
226
|
+
* instead of hand-rolling a generator — each omittable to the exact behavior
|
|
227
|
+
* above: `turnLock` (single-flight acquire/release around the turn),
|
|
219
228
|
* `contextGate` (pre-producer domain-readiness short-circuit), `beforeTurn`
|
|
220
229
|
* (observe + augment the producer input), `lifecycle` (deterministic
|
|
221
230
|
* start/complete/error telemetry), `heartbeat` (keepalive during silent
|
|
222
|
-
* producer waits),
|
|
223
|
-
* `
|
|
224
|
-
*
|
|
231
|
+
* producer waits), and `onRawEvent` (the raw producer events, for telemetry) —
|
|
232
|
+
* plus the `authorize` result's `insertUserMessage` flag (suppress the user row
|
|
233
|
+
* for a product-dispatched turn). `handleChatTurn` stays the engine — the seams
|
|
234
|
+
* only wrap its input, its producer stream, and its settle.
|
|
235
|
+
*
|
|
236
|
+
* Seam stability: all of the above are STABLE and safe to depend on. They
|
|
237
|
+
* graduated in #227 against the bar this package holds itself to — a seam is
|
|
238
|
+
* provisional until two INDEPENDENT consumers exercise it, because one
|
|
239
|
+
* consumer's shape is indistinguishable from that consumer's assumptions.
|
|
240
|
+
* `turnLock` cleared it with `/turn-stream`'s shared DO adapter (#221);
|
|
241
|
+
* `contextGate`, `beforeTurn`, `onRawEvent` and `insertUserMessage` each
|
|
242
|
+
* cleared it with two product verticals reading them differently — and the
|
|
243
|
+
* review found no leaked assumptions to fix, only `ChatRouteEvent` to export.
|
|
225
244
|
*
|
|
226
|
-
*
|
|
227
|
-
*
|
|
228
|
-
*
|
|
229
|
-
*
|
|
230
|
-
*
|
|
231
|
-
* grouped under a `hooks` object): that grouping would break the shipped
|
|
232
|
-
* consumer's call for no mechanism gain, and this package's exports are
|
|
233
|
-
* additive-only.
|
|
245
|
+
* They stay FLAT top-level options (not grouped under a `hooks` object): that
|
|
246
|
+
* grouping would break every shipped consumer's call for no mechanism gain, and
|
|
247
|
+
* this package's exports are additive-only. For the same reason `onRawEvent`
|
|
248
|
+
* keeps its two-argument `(event, context)` signature rather than being
|
|
249
|
+
* normalized to the single-args shape the other seams take.
|
|
234
250
|
*/
|
|
235
251
|
|
|
236
252
|
/** Usage receipt persisted onto the assistant message (the flattened
|
|
@@ -329,7 +345,14 @@ type ChatTurnAuthorization<TContext> = {
|
|
|
329
345
|
* never overrides — the engine's retry-dedup: `authorize` runs before
|
|
330
346
|
* turn identity is resolved, so it cannot tell a retry from a fresh turn;
|
|
331
347
|
* a turn already deduped stays deduped. Omit / `true` → today's behavior.
|
|
332
|
-
*
|
|
348
|
+
*
|
|
349
|
+
* Settled shape (#227). The validated case in both consumers is the same:
|
|
350
|
+
* a durable plan-approval follow-up re-entering an execution the decision
|
|
351
|
+
* route already enqueued — a decision, not a typed message, so it must
|
|
352
|
+
* not surface a user bubble. Suppressing the insert also propagates:
|
|
353
|
+
* `ChatTurnProduceArgs.userMessageId` is `null` for the rest of the turn
|
|
354
|
+
* when there is no row to reuse, so a seam anchoring to the user row must
|
|
355
|
+
* handle that arm rather than assume a string. */
|
|
333
356
|
insertUserMessage?: boolean;
|
|
334
357
|
} | {
|
|
335
358
|
ok: false;
|
|
@@ -360,9 +383,28 @@ interface ChatTurnProduceArgs<TContext> {
|
|
|
360
383
|
/** The turn-buffer id announced to the client for replay. */
|
|
361
384
|
turnStreamId: string;
|
|
362
385
|
priorMessages: PersistedChatMessageForTurn[];
|
|
386
|
+
/** The durable `role:'user'` row this turn is anchored to — the row the
|
|
387
|
+
* factory just inserted, or the one retry-dedup REUSED. Products anchor
|
|
388
|
+
* optimistic-bubble swaps, retry targeting, and stop-polling to it, and it
|
|
389
|
+
* is the only way to name a REUSED row (which `priorMessages` excludes).
|
|
390
|
+
*
|
|
391
|
+
* Three states, all meaningful:
|
|
392
|
+
* - `undefined` — not resolved yet. `turnLock.acquire` is the one seam that
|
|
393
|
+
* sees this: it runs before any side effect by contract, so the row does
|
|
394
|
+
* not exist when it reads the args.
|
|
395
|
+
* - `null` — resolved, no row: `authorize` returned `insertUserMessage:
|
|
396
|
+
* false` on a turn with nothing to reuse, or the store's `appendMessage`
|
|
397
|
+
* resolved without a usable id.
|
|
398
|
+
* - a string — the row id, for `contextGate`, `beforeTurn`, and `produce`. */
|
|
399
|
+
userMessageId?: string | null;
|
|
363
400
|
}
|
|
364
401
|
/** One event as it crosses the route: the producer's own vocabulary, or an
|
|
365
|
-
* injected keepalive. Same shape the engine forwards verbatim.
|
|
402
|
+
* injected keepalive. Same shape the engine forwards verbatim.
|
|
403
|
+
*
|
|
404
|
+
* Public because it IS the vocabulary of two seams — `onRawEvent`'s parameter
|
|
405
|
+
* and `heartbeat.event`'s return. Exported so a product can declare a
|
|
406
|
+
* standalone handler (`function onRawEvent(e: ChatRouteEvent, ctx: T)`) rather
|
|
407
|
+
* than depending on contextual typing from an inline object literal. */
|
|
366
408
|
type ChatRouteEvent = {
|
|
367
409
|
type: string;
|
|
368
410
|
data?: Record<string, unknown>;
|
|
@@ -434,6 +476,11 @@ interface ChatTurnLifecycleComplete<TContext> extends ChatTurnLifecycleBase<TCon
|
|
|
434
476
|
/** Attribution for a downgrade: which models were tried and why each failed.
|
|
435
477
|
* `undefined` when the producer reports no failover support. */
|
|
436
478
|
modelFailover?: ChatTurnModelFailover;
|
|
479
|
+
/** The durable `role:'assistant'` row this turn wrote, or `null` when it
|
|
480
|
+
* wrote none (an empty turn leaves no row — a draft started mid-stream is
|
|
481
|
+
* retracted). The detached lane surfaces the same id as
|
|
482
|
+
* `DetachedTurnResult.messageId`; one contract, two lanes. */
|
|
483
|
+
assistantMessageId: string | null;
|
|
437
484
|
}
|
|
438
485
|
/** Represent an error occurring during a chat turn lifecycle with context and duration information */
|
|
439
486
|
interface ChatTurnLifecycleError<TContext> extends ChatTurnLifecycleBase<TContext> {
|
|
@@ -451,6 +498,31 @@ interface ChatTurnLifecycle<TContext> {
|
|
|
451
498
|
onTurnComplete?(info: ChatTurnLifecycleComplete<TContext>): void | Promise<void>;
|
|
452
499
|
onTurnError?(info: ChatTurnLifecycleError<TContext>): void | Promise<void>;
|
|
453
500
|
}
|
|
501
|
+
/** What a settled turn reports to `onTurnComplete` — the product's
|
|
502
|
+
* post-processing seam (billing, titles, audit). */
|
|
503
|
+
interface ChatTurnCompleteInput<TContext> {
|
|
504
|
+
identity: ChatTurnIdentity;
|
|
505
|
+
finalText: string;
|
|
506
|
+
context: TContext;
|
|
507
|
+
failed: boolean;
|
|
508
|
+
failureReason?: string;
|
|
509
|
+
/** The model that SERVED this turn. With failover wired it may differ from
|
|
510
|
+
* the requested one, so a product that bills or scores per model MUST read
|
|
511
|
+
* it here rather than assuming the model it asked for. */
|
|
512
|
+
model?: string;
|
|
513
|
+
/** Present when the producer supports failover: the full attempt trail, and
|
|
514
|
+
* `usedFallback` — the flag that makes a silent downgrade impossible. */
|
|
515
|
+
modelFailover?: ChatTurnModelFailover;
|
|
516
|
+
/** The durable `role:'assistant'` row this turn wrote, or `null` when it
|
|
517
|
+
* wrote none (an empty turn leaves no row).
|
|
518
|
+
*
|
|
519
|
+
* Populated even when `failed` is true — a terminal error event still
|
|
520
|
+
* persists whatever partial answer arrived, and that pairing is exactly
|
|
521
|
+
* what lets a product render an error row against a REAL message instead of
|
|
522
|
+
* hunting for the newest row in the thread. The detached lane surfaces the
|
|
523
|
+
* same id as `DetachedTurnResult.messageId`. */
|
|
524
|
+
assistantMessageId: string | null;
|
|
525
|
+
}
|
|
454
526
|
/** Define options to configure chat turn routes including authorization, storage, and event buffering */
|
|
455
527
|
interface CreateChatTurnRoutesOptions<TContext = void> {
|
|
456
528
|
/** Names the product in `deriveExecutionId` so retries land on the same
|
|
@@ -479,11 +551,34 @@ interface CreateChatTurnRoutesOptions<TContext = void> {
|
|
|
479
551
|
/** Pre-turn readiness gate that can short-circuit with a product `Response`
|
|
480
552
|
* before the producer runs (the user row is already persisted). Runs after
|
|
481
553
|
* `turnLock.acquire`, before `beforeTurn`. Omit → always proceed.
|
|
482
|
-
*
|
|
554
|
+
*
|
|
555
|
+
* Settled shape (#227). What a consumer may depend on:
|
|
556
|
+
* - `args.userMessageId` is RESOLVED here — a string, or `null` when the
|
|
557
|
+
* insert was suppressed with nothing to reuse. (`turnLock.acquire` is the
|
|
558
|
+
* only seam that sees it `undefined`.)
|
|
559
|
+
* - `{proceed:false}` releases the lock and returns the product's `Response`
|
|
560
|
+
* verbatim: `beforeTurn` and `produce` never run, no assistant row is
|
|
561
|
+
* written, and the user row already inserted is KEPT — a real user turn
|
|
562
|
+
* whose assistant side is the gate's own response.
|
|
563
|
+
* - Always returning `{proceed:true}` is supported, not a misuse: the seam
|
|
564
|
+
* doubles as the one place that runs after the user row exists and before
|
|
565
|
+
* the producer, which is where per-turn analytics and readiness
|
|
566
|
+
* precomputation belong. A gate that never gates is a valid consumer. */
|
|
483
567
|
contextGate?(args: ChatTurnProduceArgs<TContext>): ChatTurnGateResult | Promise<ChatTurnGateResult>;
|
|
484
568
|
/** Observe the assembled producer input and optionally augment it (rewrite
|
|
485
569
|
* the prompt / prior messages) before the producer runs. Omit → no change.
|
|
486
|
-
*
|
|
570
|
+
*
|
|
571
|
+
* Settled shape (#227). BOTH return arms are contract:
|
|
572
|
+
* - a `ChatTurnInputPatch` shallow-merges over the route-assembled args, so
|
|
573
|
+
* an omitted field keeps the route's value;
|
|
574
|
+
* - `void` means "no patch" — and mutating `args.context` in place is the
|
|
575
|
+
* supported way to thread request-scoped state forward to `produce` /
|
|
576
|
+
* `lifecycle` / `onTurnComplete`, which all receive the same object.
|
|
577
|
+
*
|
|
578
|
+
* A throw propagates (the turn fails with the lock released). It runs BEFORE
|
|
579
|
+
* `lifecycle.onTurnStart`, so a throw here fires no terminal lifecycle hook —
|
|
580
|
+
* the span never opened. Telemetry for a failure in this seam belongs in the
|
|
581
|
+
* seam, not in `lifecycle`. */
|
|
487
582
|
beforeTurn?(args: ChatTurnProduceArgs<TContext>): ChatTurnInputPatch | void | Promise<ChatTurnInputPatch | void>;
|
|
488
583
|
/** Deterministic run telemetry (start / complete / error) with identity and
|
|
489
584
|
* timing. Omit → no telemetry. */
|
|
@@ -494,7 +589,18 @@ interface CreateChatTurnRoutesOptions<TContext = void> {
|
|
|
494
589
|
* before any heartbeat injection (the raw sidecar-producer events, for
|
|
495
590
|
* telemetry). Never alters the stream; errors are swallowed. Distinct from
|
|
496
591
|
* `onEvent`, which sees the engine-framed stream incl. lifecycle envelopes.
|
|
497
|
-
*
|
|
592
|
+
*
|
|
593
|
+
* Settled shape (#227). What a consumer may depend on:
|
|
594
|
+
* - it sees EXACTLY the producer's own events — no engine lifecycle
|
|
595
|
+
* envelopes, and no injected keepalives (`heartbeat` wraps the stream
|
|
596
|
+
* downstream of this tap, so a synthetic event never reaches a trace);
|
|
597
|
+
* - a throw is caught and logged, never surfaced: a broken telemetry sink
|
|
598
|
+
* cannot fail a turn or truncate the client's stream;
|
|
599
|
+
* - it is an OBSERVER — the return value is ignored, and the event object
|
|
600
|
+
* continues downstream. Mutating it mutates the stream; don't.
|
|
601
|
+
*
|
|
602
|
+
* Takes `(event, context)` rather than one args object, matching both
|
|
603
|
+
* shipped consumers; see the module header on why it stays that way. */
|
|
498
604
|
onRawEvent?(event: ChatRouteEvent, context: TContext): void | Promise<void>;
|
|
499
605
|
/** Pre-persist transform of the final text (e.g. `/redact`'s `redactPII`).
|
|
500
606
|
* Live stream is never altered. */
|
|
@@ -534,20 +640,7 @@ interface CreateChatTurnRoutesOptions<TContext = void> {
|
|
|
534
640
|
* than billing an empty turn and marking it done. A turn that THROWS never
|
|
535
641
|
* reaches this hook (the engine skips it on a producer throw). Errors are
|
|
536
642
|
* 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>;
|
|
643
|
+
onTurnComplete?(input: ChatTurnCompleteInput<TContext>): Promise<void>;
|
|
551
644
|
/** Per-event side channel (product broadcast). The turn-buffer tap is
|
|
552
645
|
* already wired; this runs in addition. */
|
|
553
646
|
onEvent?(event: {
|
|
@@ -1517,4 +1610,4 @@ interface PromoteAgentFilePartOptions {
|
|
|
1517
1610
|
/** Promote a part of an agent file with optional byte limits and MIME type detection */
|
|
1518
1611
|
declare function promoteAgentFilePart(options: PromoteAgentFilePartOptions): Promise<PromoteFilePartResult>;
|
|
1519
1612
|
|
|
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 };
|
|
1613
|
+
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 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-
|
|
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
|
-
|
|
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
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
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
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
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,
|