@tangle-network/agent-app 0.43.61 → 0.43.62
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/assistant/index.d.ts +1 -1
- package/dist/assistant/index.js +1 -1
- package/dist/chat-routes/index.d.ts +262 -3
- package/dist/chat-routes/index.js +315 -13
- package/dist/chat-routes/index.js.map +1 -1
- package/dist/chat-store/index.d.ts +38 -2
- package/dist/chat-store/index.js +30 -1
- package/dist/chat-store/index.js.map +1 -1
- package/dist/{chunk-IG46XCZM.js → chunk-BG52UKNN.js} +5 -1
- package/dist/{chunk-IG46XCZM.js.map → chunk-BG52UKNN.js.map} +1 -1
- package/dist/{chunk-VCOD5AOR.js → chunk-YN7QR7MJ.js} +16 -9
- package/dist/chunk-YN7QR7MJ.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +4 -2
- package/dist/stream/index.d.ts +17 -2
- package/dist/stream/index.js +4 -2
- package/dist/{stream-normalizer-C3M--r6y.d.ts → stream-normalizer-BlCP_Cdd.d.ts} +18 -1
- package/dist/web-react/index.d.ts +1 -1
- package/dist/web-react/index.js +1 -1
- package/package.json +1 -1
- package/dist/chunk-VCOD5AOR.js.map +0 -1
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
normalizeClientTurnId,
|
|
27
27
|
replayTurnEvents,
|
|
28
28
|
resolveChatTurn
|
|
29
|
-
} from "../chunk-
|
|
29
|
+
} from "../chunk-YN7QR7MJ.js";
|
|
30
30
|
import {
|
|
31
31
|
createInteractionAnswerRoute
|
|
32
32
|
} from "../chunk-7YW6EBDX.js";
|
|
@@ -58,6 +58,7 @@ import {
|
|
|
58
58
|
import {
|
|
59
59
|
asRecord,
|
|
60
60
|
asString,
|
|
61
|
+
draftAssistantParts,
|
|
61
62
|
finalizeAssistantParts,
|
|
62
63
|
finalizePendingInteractionParts,
|
|
63
64
|
getPartKey,
|
|
@@ -65,7 +66,7 @@ import {
|
|
|
65
66
|
normalizePersistedPart,
|
|
66
67
|
normalizeToolEvent,
|
|
67
68
|
terminalizeDanglingAssistantToolUpdates
|
|
68
|
-
} from "../chunk-
|
|
69
|
+
} from "../chunk-BG52UKNN.js";
|
|
69
70
|
import {
|
|
70
71
|
cancelStatusFor,
|
|
71
72
|
interactionPartKey,
|
|
@@ -94,6 +95,192 @@ import "../chunk-7CTIUCQ4.js";
|
|
|
94
95
|
|
|
95
96
|
// src/chat-routes/turn-routes.ts
|
|
96
97
|
import { deriveExecutionId, handleChatTurn } from "@tangle-network/agent-runtime";
|
|
98
|
+
|
|
99
|
+
// src/chat-routes/draft-persistence.ts
|
|
100
|
+
var CONTENT_EVENT_TYPES = /* @__PURE__ */ new Set([
|
|
101
|
+
"text",
|
|
102
|
+
"reasoning",
|
|
103
|
+
"tool_call",
|
|
104
|
+
"tool_result",
|
|
105
|
+
"usage",
|
|
106
|
+
"notice",
|
|
107
|
+
"error",
|
|
108
|
+
"file",
|
|
109
|
+
"interaction",
|
|
110
|
+
"interaction.cancel",
|
|
111
|
+
"plan.submitted",
|
|
112
|
+
"message.part.updated"
|
|
113
|
+
]);
|
|
114
|
+
var DEFAULT_INTERVAL_MS = 2e3;
|
|
115
|
+
var DEFAULT_BACKOFF_BYTES = 262144;
|
|
116
|
+
var DEFAULT_MAX_DRAFT_TOOL_OUTPUT_BYTES = 32768;
|
|
117
|
+
function isDraftContentEvent(event) {
|
|
118
|
+
return typeof event?.type === "string" && CONTENT_EVENT_TYPES.has(event.type);
|
|
119
|
+
}
|
|
120
|
+
function capDraftToolOutput(part, maxBytes) {
|
|
121
|
+
if (maxBytes <= 0) return part;
|
|
122
|
+
if (String(part.type ?? "") !== "tool") return part;
|
|
123
|
+
const state = part.state;
|
|
124
|
+
if (!state || typeof state !== "object") return part;
|
|
125
|
+
const record = state;
|
|
126
|
+
const output = record.output;
|
|
127
|
+
if (output === void 0 || output === null) return part;
|
|
128
|
+
const serialized = typeof output === "string" ? output : safeStringify(output);
|
|
129
|
+
if (serialized.length <= maxBytes) return part;
|
|
130
|
+
const metadata = record.metadata && typeof record.metadata === "object" ? record.metadata : {};
|
|
131
|
+
return {
|
|
132
|
+
...part,
|
|
133
|
+
state: {
|
|
134
|
+
...record,
|
|
135
|
+
output: `${serialized.slice(0, maxBytes)}\u2026[draft-truncated ${serialized.length - maxBytes} chars]`,
|
|
136
|
+
metadata: { ...metadata, draftTruncated: true }
|
|
137
|
+
}
|
|
138
|
+
};
|
|
139
|
+
}
|
|
140
|
+
function safeStringify(value) {
|
|
141
|
+
try {
|
|
142
|
+
return JSON.stringify(value) ?? "";
|
|
143
|
+
} catch {
|
|
144
|
+
return String(value);
|
|
145
|
+
}
|
|
146
|
+
}
|
|
147
|
+
function createAssistantDraftWriter(options) {
|
|
148
|
+
const log = options.log ?? (() => {
|
|
149
|
+
});
|
|
150
|
+
const baseIntervalMs = options.intervalMs ?? DEFAULT_INTERVAL_MS;
|
|
151
|
+
const backoffBytes = options.backoffBytes ?? DEFAULT_BACKOFF_BYTES;
|
|
152
|
+
const maxToolOutput = options.maxDraftToolOutputBytes ?? DEFAULT_MAX_DRAFT_TOOL_OUTPUT_BYTES;
|
|
153
|
+
let dirty = false;
|
|
154
|
+
let closed = false;
|
|
155
|
+
let inFlight;
|
|
156
|
+
let lastWriteAt = 0;
|
|
157
|
+
let lastBlobBytes = 0;
|
|
158
|
+
let rowId;
|
|
159
|
+
let writes = 0;
|
|
160
|
+
function currentIntervalMs() {
|
|
161
|
+
if (lastBlobBytes >= backoffBytes * 10) return baseIntervalMs * 5;
|
|
162
|
+
if (lastBlobBytes >= backoffBytes) return Math.round(baseIntervalMs * 2.5);
|
|
163
|
+
return baseIntervalMs;
|
|
164
|
+
}
|
|
165
|
+
async function projectValues(snapshot) {
|
|
166
|
+
const transform = options.transformText;
|
|
167
|
+
const content = transform ? await transform(snapshot.content) : snapshot.content;
|
|
168
|
+
let parts;
|
|
169
|
+
if (snapshot.parts) {
|
|
170
|
+
const capped = snapshot.parts.map((part) => capDraftToolOutput(part, maxToolOutput));
|
|
171
|
+
const redacted = transform ? await Promise.all(
|
|
172
|
+
capped.map(
|
|
173
|
+
async (part) => String(part.type ?? "") === "text" ? { ...part, text: await transform(String(part.text ?? "")) } : part
|
|
174
|
+
)
|
|
175
|
+
) : capped;
|
|
176
|
+
parts = toChatMessageParts(redacted);
|
|
177
|
+
}
|
|
178
|
+
const usage = snapshot.usage ?? {};
|
|
179
|
+
return {
|
|
180
|
+
content,
|
|
181
|
+
...parts && parts.length > 0 ? { parts } : {},
|
|
182
|
+
...snapshot.model ? { model: snapshot.model } : {},
|
|
183
|
+
...usage.inputTokens !== void 0 ? { inputTokens: usage.inputTokens } : {},
|
|
184
|
+
...usage.outputTokens !== void 0 ? { outputTokens: usage.outputTokens } : {},
|
|
185
|
+
...usage.reasoningTokens !== void 0 ? { reasoningTokens: usage.reasoningTokens } : {},
|
|
186
|
+
...usage.cacheReadTokens !== void 0 ? { cacheReadTokens: usage.cacheReadTokens } : {},
|
|
187
|
+
...usage.cacheWriteTokens !== void 0 ? { cacheWriteTokens: usage.cacheWriteTokens } : {},
|
|
188
|
+
...usage.costUsd !== void 0 ? { costUsd: usage.costUsd } : {}
|
|
189
|
+
};
|
|
190
|
+
}
|
|
191
|
+
async function adoptRow() {
|
|
192
|
+
if (rowId) return rowId;
|
|
193
|
+
const existing = (await options.store.listMessages(options.threadId)).find(
|
|
194
|
+
(message) => message.id === options.messageId
|
|
195
|
+
);
|
|
196
|
+
if (existing) rowId = existing.id;
|
|
197
|
+
return rowId;
|
|
198
|
+
}
|
|
199
|
+
async function writeOnce(values) {
|
|
200
|
+
if (await adoptRow()) {
|
|
201
|
+
await options.store.updateMessage(rowId, values);
|
|
202
|
+
writes += 1;
|
|
203
|
+
return;
|
|
204
|
+
}
|
|
205
|
+
const inserted = await options.store.appendMessage({
|
|
206
|
+
id: options.messageId,
|
|
207
|
+
threadId: options.threadId,
|
|
208
|
+
role: "assistant",
|
|
209
|
+
...values
|
|
210
|
+
});
|
|
211
|
+
const insertedId = inserted?.id;
|
|
212
|
+
rowId = typeof insertedId === "string" && insertedId ? insertedId : options.messageId;
|
|
213
|
+
writes += 1;
|
|
214
|
+
}
|
|
215
|
+
function trigger() {
|
|
216
|
+
if (closed) return;
|
|
217
|
+
if (!dirty) return;
|
|
218
|
+
if (inFlight) return;
|
|
219
|
+
const now = Date.now();
|
|
220
|
+
if (lastWriteAt !== 0 && now - lastWriteAt < currentIntervalMs()) return;
|
|
221
|
+
const snapshot = options.snapshot();
|
|
222
|
+
if (!snapshot) return;
|
|
223
|
+
if (!snapshot.content && (!snapshot.parts || snapshot.parts.length === 0)) return;
|
|
224
|
+
dirty = false;
|
|
225
|
+
lastWriteAt = now;
|
|
226
|
+
inFlight = (async () => {
|
|
227
|
+
try {
|
|
228
|
+
const values = await projectValues(snapshot);
|
|
229
|
+
lastBlobBytes = values.parts ? safeStringify(values.parts).length : 0;
|
|
230
|
+
await writeOnce(values);
|
|
231
|
+
} catch (err) {
|
|
232
|
+
log("[chat-routes] incremental assistant persistence failed", {
|
|
233
|
+
messageId: options.messageId,
|
|
234
|
+
error: err instanceof Error ? err.message : String(err)
|
|
235
|
+
});
|
|
236
|
+
} finally {
|
|
237
|
+
inFlight = void 0;
|
|
238
|
+
if (dirty && !closed) trigger();
|
|
239
|
+
}
|
|
240
|
+
})();
|
|
241
|
+
}
|
|
242
|
+
return {
|
|
243
|
+
notify(event) {
|
|
244
|
+
if (closed) return;
|
|
245
|
+
if (isDraftContentEvent(event)) dirty = true;
|
|
246
|
+
trigger();
|
|
247
|
+
},
|
|
248
|
+
async close() {
|
|
249
|
+
closed = true;
|
|
250
|
+
if (inFlight) await inFlight;
|
|
251
|
+
},
|
|
252
|
+
async finalize(values) {
|
|
253
|
+
closed = true;
|
|
254
|
+
if (inFlight) await inFlight;
|
|
255
|
+
await writeOnce(values);
|
|
256
|
+
},
|
|
257
|
+
rowId: () => rowId,
|
|
258
|
+
async discard() {
|
|
259
|
+
closed = true;
|
|
260
|
+
if (inFlight) await inFlight;
|
|
261
|
+
if (!options.store.deleteMessage) return;
|
|
262
|
+
try {
|
|
263
|
+
if (!await adoptRow()) return;
|
|
264
|
+
await options.store.deleteMessage(rowId);
|
|
265
|
+
rowId = void 0;
|
|
266
|
+
} catch (err) {
|
|
267
|
+
log("[chat-routes] draft assistant row discard failed", {
|
|
268
|
+
messageId: options.messageId,
|
|
269
|
+
error: err instanceof Error ? err.message : String(err)
|
|
270
|
+
});
|
|
271
|
+
}
|
|
272
|
+
},
|
|
273
|
+
writeCount: () => writes
|
|
274
|
+
};
|
|
275
|
+
}
|
|
276
|
+
function storeSupportsDraftPersistence(store) {
|
|
277
|
+
return typeof store.updateMessage === "function";
|
|
278
|
+
}
|
|
279
|
+
function assistantRowIdForTurn(turnKey) {
|
|
280
|
+
return `assistant:${turnKey}`;
|
|
281
|
+
}
|
|
282
|
+
|
|
283
|
+
// src/chat-routes/turn-routes.ts
|
|
97
284
|
function failureReasonOf(data) {
|
|
98
285
|
if (!data) return void 0;
|
|
99
286
|
const message = data.message ?? data.error ?? data.reason;
|
|
@@ -184,6 +371,13 @@ async function* withStreamHeartbeat(source, intervalMs, makeEvent) {
|
|
|
184
371
|
}
|
|
185
372
|
function createChatTurnRoutes(options) {
|
|
186
373
|
const log = options.log ?? ((message, meta) => console.error(message, meta ?? ""));
|
|
374
|
+
const draftStore = options.store;
|
|
375
|
+
if (options.incrementalPersistence && !storeSupportsDraftPersistence(draftStore)) {
|
|
376
|
+
throw new Error(
|
|
377
|
+
"incrementalPersistence requires a store with updateMessage() \u2014 `/chat-store`'s createChatStore has it; a product store must implement it or omit the option"
|
|
378
|
+
);
|
|
379
|
+
}
|
|
380
|
+
const draftTuning = options.incrementalPersistence === false || !storeSupportsDraftPersistence(draftStore) ? null : options.incrementalPersistence ?? {};
|
|
187
381
|
async function turn(request, ctx) {
|
|
188
382
|
const [rawBody, badBody] = await parseJsonObjectBody(request);
|
|
189
383
|
if (badBody) return badBody;
|
|
@@ -204,7 +398,18 @@ function createChatTurnRoutes(options) {
|
|
|
204
398
|
content: m.content,
|
|
205
399
|
parts: m.parts ?? null
|
|
206
400
|
}));
|
|
207
|
-
|
|
401
|
+
let hasRunningTurn = false;
|
|
402
|
+
if (draftTuning && existingMessages.at(-1)?.role === "assistant") {
|
|
403
|
+
try {
|
|
404
|
+
hasRunningTurn = (await options.turnStore.listRunning?.(payload.threadId) ?? []).length > 0;
|
|
405
|
+
} catch (err) {
|
|
406
|
+
log("[chat-routes] listRunning probe failed; treating the thread as idle", {
|
|
407
|
+
threadId: payload.threadId,
|
|
408
|
+
error: err instanceof Error ? err.message : String(err)
|
|
409
|
+
});
|
|
410
|
+
}
|
|
411
|
+
}
|
|
412
|
+
const chatTurn = resolveChatTurn({ existingMessages, userContent: content, turnId, hasRunningTurn });
|
|
208
413
|
const identity = {
|
|
209
414
|
tenantId,
|
|
210
415
|
sessionId: payload.threadId,
|
|
@@ -250,6 +455,7 @@ function createChatTurnRoutes(options) {
|
|
|
250
455
|
lockHandle = acquired.handle;
|
|
251
456
|
}
|
|
252
457
|
let producer;
|
|
458
|
+
let draft;
|
|
253
459
|
let runFailed = false;
|
|
254
460
|
let lastFailureData;
|
|
255
461
|
let turnStartedAtMs = 0;
|
|
@@ -318,6 +524,25 @@ function createChatTurnRoutes(options) {
|
|
|
318
524
|
});
|
|
319
525
|
const turnMarker = { type: "turn", turnId: turnStreamId };
|
|
320
526
|
await tap.onEvent(turnMarker);
|
|
527
|
+
if (draftTuning) {
|
|
528
|
+
draft = createAssistantDraftWriter({
|
|
529
|
+
...draftTuning,
|
|
530
|
+
store: draftStore,
|
|
531
|
+
threadId: payload.threadId,
|
|
532
|
+
messageId: options.draftMessageId ? options.draftMessageId({ identity, executionId, threadId: payload.threadId }) : assistantRowIdForTurn(executionId),
|
|
533
|
+
snapshot: () => {
|
|
534
|
+
if (!producer) return null;
|
|
535
|
+
return {
|
|
536
|
+
content: producer.finalText(),
|
|
537
|
+
...producer.draftParts ? { parts: producer.draftParts() } : {},
|
|
538
|
+
...producer.usage ? { usage: producer.usage() } : {},
|
|
539
|
+
...producer.model ? { model: producer.model } : {}
|
|
540
|
+
};
|
|
541
|
+
},
|
|
542
|
+
...options.transformFinalText ? { transformText: options.transformFinalText } : {},
|
|
543
|
+
log
|
|
544
|
+
});
|
|
545
|
+
}
|
|
321
546
|
turnStartedAtMs = Date.now();
|
|
322
547
|
turnStarted = true;
|
|
323
548
|
if (options.lifecycle?.onTurnStart) {
|
|
@@ -363,6 +588,7 @@ function createChatTurnRoutes(options) {
|
|
|
363
588
|
lastFailureData = event.data;
|
|
364
589
|
}
|
|
365
590
|
await tap.onEvent(event);
|
|
591
|
+
draft?.notify(event);
|
|
366
592
|
if (options.onEvent) await options.onEvent(event, context);
|
|
367
593
|
},
|
|
368
594
|
...options.transformFinalText ? { transformFinalText: options.transformFinalText } : {},
|
|
@@ -374,11 +600,12 @@ function createChatTurnRoutes(options) {
|
|
|
374
600
|
)
|
|
375
601
|
) : rawParts;
|
|
376
602
|
const parts = projected ? toChatMessageParts(projected) : void 0;
|
|
377
|
-
if (!finalText.trim() && (!parts || parts.length === 0))
|
|
603
|
+
if (!finalText.trim() && (!parts || parts.length === 0)) {
|
|
604
|
+
await draft?.discard();
|
|
605
|
+
return;
|
|
606
|
+
}
|
|
378
607
|
const usage = producer?.usage?.() ?? {};
|
|
379
|
-
|
|
380
|
-
threadId: payload.threadId,
|
|
381
|
-
role: "assistant",
|
|
608
|
+
const values = {
|
|
382
609
|
content: finalText,
|
|
383
610
|
...parts && parts.length > 0 ? { parts } : {},
|
|
384
611
|
...producer?.model ? { model: producer.model } : {},
|
|
@@ -388,6 +615,15 @@ function createChatTurnRoutes(options) {
|
|
|
388
615
|
...usage.cacheReadTokens !== void 0 ? { cacheReadTokens: usage.cacheReadTokens } : {},
|
|
389
616
|
...usage.cacheWriteTokens !== void 0 ? { cacheWriteTokens: usage.cacheWriteTokens } : {},
|
|
390
617
|
...usage.costUsd !== void 0 ? { costUsd: usage.costUsd } : {}
|
|
618
|
+
};
|
|
619
|
+
if (draft) {
|
|
620
|
+
await draft.finalize(values);
|
|
621
|
+
return;
|
|
622
|
+
}
|
|
623
|
+
await options.store.appendMessage({
|
|
624
|
+
threadId: payload.threadId,
|
|
625
|
+
role: "assistant",
|
|
626
|
+
...values
|
|
391
627
|
});
|
|
392
628
|
},
|
|
393
629
|
...options.onTurnComplete ? {
|
|
@@ -424,6 +660,7 @@ function createChatTurnRoutes(options) {
|
|
|
424
660
|
});
|
|
425
661
|
}
|
|
426
662
|
const failed = runFailed || drainError !== void 0;
|
|
663
|
+
await draft?.close();
|
|
427
664
|
try {
|
|
428
665
|
await tap.done(failed ? "error" : "complete");
|
|
429
666
|
} catch (err) {
|
|
@@ -947,6 +1184,12 @@ ${diagnostic.userMessage}` : diagnostic.userMessage;
|
|
|
947
1184
|
finalizeAssistantParts(partOrder, partMap, fullText),
|
|
948
1185
|
interactionOutcome
|
|
949
1186
|
),
|
|
1187
|
+
// Mid-stream snapshot for incremental persistence: the same accumulators,
|
|
1188
|
+
// WITHOUT the two completion-time settlements (`terminalizeDanglingTool*`
|
|
1189
|
+
// and `finalizePendingInteractionParts`). A running tool and an unanswered
|
|
1190
|
+
// ask are the correct live state; settling them early would persist
|
|
1191
|
+
// phantom failures the final write then reverses.
|
|
1192
|
+
draftParts: () => draftAssistantParts(partOrder, partMap, fullText),
|
|
950
1193
|
usage: () => usage,
|
|
951
1194
|
...options.model ? { model: options.model } : {}
|
|
952
1195
|
};
|
|
@@ -973,6 +1216,58 @@ function cachedResultFrom(final) {
|
|
|
973
1216
|
}
|
|
974
1217
|
async function runDetachedTurn(opts) {
|
|
975
1218
|
const { store, turnId, scopeId } = opts;
|
|
1219
|
+
let producer;
|
|
1220
|
+
let draft;
|
|
1221
|
+
if (opts.persist) {
|
|
1222
|
+
const { store: persistStore, threadId, messageId, transformText, ...tuning } = opts.persist;
|
|
1223
|
+
if (!storeSupportsDraftPersistence(persistStore)) {
|
|
1224
|
+
throw new Error(
|
|
1225
|
+
"runDetachedTurn persist requires a store with updateMessage() \u2014 `/chat-store`'s createChatStore has it"
|
|
1226
|
+
);
|
|
1227
|
+
}
|
|
1228
|
+
draft = createAssistantDraftWriter({
|
|
1229
|
+
...tuning,
|
|
1230
|
+
store: persistStore,
|
|
1231
|
+
threadId,
|
|
1232
|
+
messageId: messageId ?? assistantRowIdForTurn(turnId),
|
|
1233
|
+
snapshot: () => producer ? {
|
|
1234
|
+
content: producer.finalText?.() ?? "",
|
|
1235
|
+
...producer.draftParts ? { parts: producer.draftParts() } : {},
|
|
1236
|
+
...producer.usage ? { usage: producer.usage() } : {},
|
|
1237
|
+
...opts.model ? { model: opts.model } : {}
|
|
1238
|
+
} : null,
|
|
1239
|
+
...transformText ? { transformText } : {},
|
|
1240
|
+
...opts.log ? { log: opts.log } : {}
|
|
1241
|
+
});
|
|
1242
|
+
}
|
|
1243
|
+
const settleRow = async (result) => {
|
|
1244
|
+
if (!draft) return result;
|
|
1245
|
+
const transform = opts.persist?.transformText;
|
|
1246
|
+
const content = transform ? await transform(result.text) : result.text;
|
|
1247
|
+
const rawParts = transform ? await Promise.all(
|
|
1248
|
+
result.parts.map(
|
|
1249
|
+
async (part) => String(part.type ?? "") === "text" ? { ...part, text: await transform(String(part.text ?? "")) } : part
|
|
1250
|
+
)
|
|
1251
|
+
) : result.parts;
|
|
1252
|
+
const parts2 = toChatMessageParts(rawParts);
|
|
1253
|
+
if (!content.trim() && parts2.length === 0) {
|
|
1254
|
+
await draft.discard();
|
|
1255
|
+
return { ...result, messageId: null };
|
|
1256
|
+
}
|
|
1257
|
+
const values = {
|
|
1258
|
+
content,
|
|
1259
|
+
...parts2.length > 0 ? { parts: parts2 } : {},
|
|
1260
|
+
...opts.model ? { model: opts.model } : {},
|
|
1261
|
+
...result.usage.inputTokens !== void 0 ? { inputTokens: result.usage.inputTokens } : {},
|
|
1262
|
+
...result.usage.outputTokens !== void 0 ? { outputTokens: result.usage.outputTokens } : {},
|
|
1263
|
+
...result.usage.reasoningTokens !== void 0 ? { reasoningTokens: result.usage.reasoningTokens } : {},
|
|
1264
|
+
...result.usage.cacheReadTokens !== void 0 ? { cacheReadTokens: result.usage.cacheReadTokens } : {},
|
|
1265
|
+
...result.usage.cacheWriteTokens !== void 0 ? { cacheWriteTokens: result.usage.cacheWriteTokens } : {},
|
|
1266
|
+
...result.usage.costUsd !== void 0 ? { costUsd: result.usage.costUsd } : {}
|
|
1267
|
+
};
|
|
1268
|
+
await draft.finalize(values);
|
|
1269
|
+
return { ...result, messageId: draft.rowId() ?? null };
|
|
1270
|
+
};
|
|
976
1271
|
const completed = async () => {
|
|
977
1272
|
if (!opts.completedResult) return null;
|
|
978
1273
|
try {
|
|
@@ -988,14 +1283,14 @@ async function runDetachedTurn(opts) {
|
|
|
988
1283
|
} catch (err) {
|
|
989
1284
|
opts.log?.("[chat-routes] runDetachedTurn getStatus failed; treating as no prior", { turnId, err: String(err) });
|
|
990
1285
|
}
|
|
991
|
-
if (prior === "complete") return cachedResultFrom(await completed());
|
|
1286
|
+
if (prior === "complete") return await settleRow(cachedResultFrom(await completed()));
|
|
992
1287
|
if (prior === "running") {
|
|
993
1288
|
const final = await completed();
|
|
994
1289
|
if (final) {
|
|
995
1290
|
await store.setStatus(turnId, "complete", scopeId).catch((err) => {
|
|
996
1291
|
opts.log?.("[chat-routes] runDetachedTurn failed to settle a completed running turn", { turnId, err: String(err) });
|
|
997
1292
|
});
|
|
998
|
-
return cachedResultFrom(final);
|
|
1293
|
+
return await settleRow(cachedResultFrom(final));
|
|
999
1294
|
}
|
|
1000
1295
|
if (opts.resetBuffer) {
|
|
1001
1296
|
await opts.resetBuffer(turnId).catch((err) => {
|
|
@@ -1012,7 +1307,7 @@ async function runDetachedTurn(opts) {
|
|
|
1012
1307
|
coalesce: opts.coalesce ?? coalesceDeltas
|
|
1013
1308
|
});
|
|
1014
1309
|
await tap.onEvent({ type: "turn", turnId });
|
|
1015
|
-
|
|
1310
|
+
producer = createSandboxChatProducer({
|
|
1016
1311
|
events: opts.events,
|
|
1017
1312
|
model: opts.model,
|
|
1018
1313
|
isRenderableInteraction: opts.isRenderableInteraction,
|
|
@@ -1026,11 +1321,14 @@ async function runDetachedTurn(opts) {
|
|
|
1026
1321
|
const type = ev.type;
|
|
1027
1322
|
if (typeof type === "string" && TERMINAL_ERROR_TYPES.has(type)) runError = errorMessageOf(ev);
|
|
1028
1323
|
await tap.onEvent(ev);
|
|
1324
|
+
draft?.notify(ev);
|
|
1029
1325
|
}
|
|
1030
1326
|
await tap.done(runError ? "error" : "complete");
|
|
1031
1327
|
} catch (err) {
|
|
1032
1328
|
await tap.done("error").catch(() => {
|
|
1033
1329
|
});
|
|
1330
|
+
await draft?.close().catch(() => {
|
|
1331
|
+
});
|
|
1034
1332
|
throw err;
|
|
1035
1333
|
}
|
|
1036
1334
|
const text = producer.finalText?.() ?? "";
|
|
@@ -1040,11 +1338,11 @@ async function runDetachedTurn(opts) {
|
|
|
1040
1338
|
const final = await completed();
|
|
1041
1339
|
if (final?.usage) usage = { ...usage, ...final.usage };
|
|
1042
1340
|
if (!text && final?.text) {
|
|
1043
|
-
return { state: "completed", text: final.text, parts: final.parts ?? parts, usage, cached: false };
|
|
1341
|
+
return await settleRow({ state: "completed", text: final.text, parts: final.parts ?? parts, usage, cached: false });
|
|
1044
1342
|
}
|
|
1045
1343
|
}
|
|
1046
|
-
if (runError) return { state: "failed", text, parts, usage, error: runError, cached: false };
|
|
1047
|
-
return { state: "completed", text, parts, usage, cached: false };
|
|
1344
|
+
if (runError) return await settleRow({ state: "failed", text, parts, usage, error: runError, cached: false });
|
|
1345
|
+
return await settleRow({ state: "completed", text, parts, usage, cached: false });
|
|
1048
1346
|
}
|
|
1049
1347
|
|
|
1050
1348
|
// src/chat-routes/durable-projection.ts
|
|
@@ -1755,6 +2053,7 @@ export {
|
|
|
1755
2053
|
UPLOAD_INLINE_MAX_BYTES,
|
|
1756
2054
|
UPLOAD_MAX_FILE_BYTES,
|
|
1757
2055
|
assertPromptPartsWithinCap,
|
|
2056
|
+
assistantRowIdForTurn,
|
|
1758
2057
|
attachmentSizeErrorMessage,
|
|
1759
2058
|
attachmentTotalSizeErrorMessage,
|
|
1760
2059
|
base64WireLen,
|
|
@@ -1763,6 +2062,7 @@ export {
|
|
|
1763
2062
|
bytesToBase64,
|
|
1764
2063
|
chatTurnRequestInit,
|
|
1765
2064
|
checkAttachmentType,
|
|
2065
|
+
createAssistantDraftWriter,
|
|
1766
2066
|
createAttachmentUploadRoute,
|
|
1767
2067
|
createChatTurnRoutes,
|
|
1768
2068
|
createSandboxChatProducer,
|
|
@@ -1771,6 +2071,7 @@ export {
|
|
|
1771
2071
|
defaultValidateAttachmentPath,
|
|
1772
2072
|
fileMentionsToParts,
|
|
1773
2073
|
formatBytes,
|
|
2074
|
+
isDraftContentEvent,
|
|
1774
2075
|
mediaTypeForMentionPath,
|
|
1775
2076
|
mentionKindForPath,
|
|
1776
2077
|
parseChatTurnParts,
|
|
@@ -1784,6 +2085,7 @@ export {
|
|
|
1784
2085
|
sanitizeUploadFilename,
|
|
1785
2086
|
sniffBinary,
|
|
1786
2087
|
sniffMimeFromName,
|
|
2088
|
+
storeSupportsDraftPersistence,
|
|
1787
2089
|
validateSandboxMentionPath,
|
|
1788
2090
|
withDurableChatProjection
|
|
1789
2091
|
};
|