agents 0.11.5 → 0.11.7
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/browser/ai.js +1 -1
- package/dist/browser/index.js +1 -1
- package/dist/browser/tanstack-ai.js +1 -1
- package/dist/browser/tanstack-ai.js.map +1 -1
- package/dist/chat/index.d.ts +162 -88
- package/dist/chat/index.js +232 -1
- package/dist/chat/index.js.map +1 -1
- package/dist/{classPrivateFieldGet2-DAZNVUKb.js → classPrivateFieldGet2-Bqby-AHD.js} +5 -5
- package/dist/{client-BYF13FDD.js → client-D1kFXo80.js} +7 -1
- package/dist/{client-BYF13FDD.js.map → client-D1kFXo80.js.map} +1 -1
- package/dist/client.d.ts +1 -1
- package/dist/compaction-helpers-C_cN3z55.js.map +1 -1
- package/dist/email.js.map +1 -1
- package/dist/experimental/memory/session/index.js.map +1 -1
- package/dist/{index-DabjI66m.d.ts → index-BM7Nk0QD.d.ts} +21 -9
- package/dist/index.d.ts +1 -1
- package/dist/index.js +46 -15
- package/dist/index.js.map +1 -1
- package/dist/mcp/client.d.ts +1 -1
- package/dist/mcp/client.js +1 -1
- package/dist/mcp/do-oauth-client-provider.js.map +1 -1
- package/dist/mcp/index.d.ts +1 -1
- package/dist/mcp/index.js +1 -1
- package/dist/mcp/index.js.map +1 -1
- package/dist/mcp/x402.js.map +1 -1
- package/dist/react.d.ts +1 -1
- package/dist/react.js.map +1 -1
- package/dist/{shared-BovR6hRc.js → shared-mfBbxjS1.js} +3 -3
- package/dist/{shared-BovR6hRc.js.map → shared-mfBbxjS1.js.map} +1 -1
- package/dist/sub-routing.d.ts +1 -1
- package/dist/sub-routing.js.map +1 -1
- package/dist/workflows.d.ts +1 -1
- package/dist/workflows.js.map +1 -1
- package/package.json +4 -4
package/dist/browser/ai.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as SEARCH_DESCRIPTION, r as createBrowserToolHandlers, t as EXECUTE_DESCRIPTION } from "../shared-
|
|
1
|
+
import { n as SEARCH_DESCRIPTION, r as createBrowserToolHandlers, t as EXECUTE_DESCRIPTION } from "../shared-mfBbxjS1.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { tool } from "ai";
|
|
4
4
|
//#region src/browser/ai.ts
|
package/dist/browser/index.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import { a as connectBrowser, i as CdpSession, n as SEARCH_DESCRIPTION, o as connectUrl, r as createBrowserToolHandlers, t as EXECUTE_DESCRIPTION } from "../shared-
|
|
1
|
+
import { a as connectBrowser, i as CdpSession, n as SEARCH_DESCRIPTION, o as connectUrl, r as createBrowserToolHandlers, t as EXECUTE_DESCRIPTION } from "../shared-mfBbxjS1.js";
|
|
2
2
|
export { CdpSession, EXECUTE_DESCRIPTION, SEARCH_DESCRIPTION, connectBrowser, connectUrl, createBrowserToolHandlers };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { n as SEARCH_DESCRIPTION, r as createBrowserToolHandlers, t as EXECUTE_DESCRIPTION } from "../shared-
|
|
1
|
+
import { n as SEARCH_DESCRIPTION, r as createBrowserToolHandlers, t as EXECUTE_DESCRIPTION } from "../shared-mfBbxjS1.js";
|
|
2
2
|
import { z } from "zod";
|
|
3
3
|
import { toolDefinition } from "@tanstack/ai";
|
|
4
4
|
//#region src/browser/tanstack-ai.ts
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"tanstack-ai.js","names":[],"sources":["../../src/browser/tanstack-ai.ts"],"sourcesContent":["import { toolDefinition } from \"@tanstack/ai\";\nimport type { ServerTool } from \"@tanstack/ai\";\nimport { z } from \"zod\";\nimport {\n createBrowserToolHandlers,\n SEARCH_DESCRIPTION,\n EXECUTE_DESCRIPTION,\n type BrowserToolsOptions\n} from \"./shared\";\n\nexport type { BrowserToolsOptions } from \"./shared\";\n\n/**\n * Create TanStack AI tools for browser automation via CDP code mode.\n *\n * Returns an array of `ServerTool`s: `browser_search` (query the CDP spec)\n * and `browser_execute` (run CDP commands against a live browser).\n *\n * @example\n * ```ts\n * import { createBrowserTools } from \"agents/browser/tanstack-ai\";\n * import { chat } from \"@tanstack/ai\";\n *\n * const browserTools = createBrowserTools({\n * browser: env.BROWSER,\n * loader: env.LOADER,\n * });\n *\n * const stream = chat({\n * adapter: openaiText(\"gpt-4o\"),\n * tools: [...browserTools, ...otherTools],\n * messages,\n * });\n * ```\n */\nexport function createBrowserTools(options: BrowserToolsOptions): ServerTool[] {\n const handlers = createBrowserToolHandlers(options);\n\n const search = toolDefinition({\n name: \"browser_search\" as const,\n description: SEARCH_DESCRIPTION,\n inputSchema: z.object({\n code: z.string().meta({\n description: \"JavaScript async arrow function that queries the CDP spec\"\n })\n })\n }).server(async ({ code }) => {\n const result = await handlers.search(code);\n if (result.isError) {\n throw new Error(result.text);\n }\n return { text: result.text };\n });\n\n const execute = toolDefinition({\n name: \"browser_execute\" as const,\n description: EXECUTE_DESCRIPTION,\n inputSchema: z.object({\n code: z.string().meta({\n description: \"JavaScript async arrow function that uses the cdp helper\"\n })\n })\n }).server(async ({ code }) => {\n const result = await handlers.execute(code);\n if (result.isError) {\n throw new Error(result.text);\n }\n return { text: result.text };\n });\n\n return [search, execute];\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,SAAgB,mBAAmB,SAA4C;CAC7E,MAAM,WAAW,0BAA0B,QAAQ;AAkCnD,QAAO,CAhCQ,eAAe;EAC5B,MAAM;EACN,aAAa;EACb,aAAa,EAAE,OAAO,EACpB,MAAM,EAAE,QAAQ,CAAC,KAAK,EACpB,aAAa,6DACd,CAAC,EACH,CAAC;EACH,CAAC,CAAC,OAAO,OAAO,EAAE,WAAW;EAC5B,MAAM,SAAS,MAAM,SAAS,OAAO,KAAK;AAC1C,MAAI,OAAO,QACT,OAAM,IAAI,MAAM,OAAO,KAAK;AAE9B,SAAO,EAAE,MAAM,OAAO,MAAM;
|
|
1
|
+
{"version":3,"file":"tanstack-ai.js","names":[],"sources":["../../src/browser/tanstack-ai.ts"],"sourcesContent":["import { toolDefinition } from \"@tanstack/ai\";\nimport type { ServerTool } from \"@tanstack/ai\";\nimport { z } from \"zod\";\nimport {\n createBrowserToolHandlers,\n SEARCH_DESCRIPTION,\n EXECUTE_DESCRIPTION,\n type BrowserToolsOptions\n} from \"./shared\";\n\nexport type { BrowserToolsOptions } from \"./shared\";\n\n/**\n * Create TanStack AI tools for browser automation via CDP code mode.\n *\n * Returns an array of `ServerTool`s: `browser_search` (query the CDP spec)\n * and `browser_execute` (run CDP commands against a live browser).\n *\n * @example\n * ```ts\n * import { createBrowserTools } from \"agents/browser/tanstack-ai\";\n * import { chat } from \"@tanstack/ai\";\n *\n * const browserTools = createBrowserTools({\n * browser: env.BROWSER,\n * loader: env.LOADER,\n * });\n *\n * const stream = chat({\n * adapter: openaiText(\"gpt-4o\"),\n * tools: [...browserTools, ...otherTools],\n * messages,\n * });\n * ```\n */\nexport function createBrowserTools(options: BrowserToolsOptions): ServerTool[] {\n const handlers = createBrowserToolHandlers(options);\n\n const search = toolDefinition({\n name: \"browser_search\" as const,\n description: SEARCH_DESCRIPTION,\n inputSchema: z.object({\n code: z.string().meta({\n description: \"JavaScript async arrow function that queries the CDP spec\"\n })\n })\n }).server(async ({ code }) => {\n const result = await handlers.search(code);\n if (result.isError) {\n throw new Error(result.text);\n }\n return { text: result.text };\n });\n\n const execute = toolDefinition({\n name: \"browser_execute\" as const,\n description: EXECUTE_DESCRIPTION,\n inputSchema: z.object({\n code: z.string().meta({\n description: \"JavaScript async arrow function that uses the cdp helper\"\n })\n })\n }).server(async ({ code }) => {\n const result = await handlers.execute(code);\n if (result.isError) {\n throw new Error(result.text);\n }\n return { text: result.text };\n });\n\n return [search, execute];\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;;AAmCA,SAAgB,mBAAmB,SAA4C;CAC7E,MAAM,WAAW,0BAA0B,QAAQ;AAkCnD,QAAO,CAhCQ,eAAe;EAC5B,MAAM;EACN,aAAa;EACb,aAAa,EAAE,OAAO,EACpB,MAAM,EAAE,QAAQ,CAAC,KAAK,EACpB,aAAa,6DACd,CAAC,EACH,CAAC;EACH,CAAC,CAAC,OAAO,OAAO,EAAE,WAAW;EAC5B,MAAM,SAAS,MAAM,SAAS,OAAO,KAAK;AAC1C,MAAI,OAAO,QACT,OAAM,IAAI,MAAM,OAAO,KAAK;AAE9B,SAAO,EAAE,MAAM,OAAO,MAAM;GAmBhB,EAhBE,eAAe;EAC7B,MAAM;EACN,aAAa;EACb,aAAa,EAAE,OAAO,EACpB,MAAM,EAAE,QAAQ,CAAC,KAAK,EACpB,aAAa,4DACd,CAAC,EACH,CAAC;EACH,CAAC,CAAC,OAAO,OAAO,EAAE,WAAW;EAC5B,MAAM,SAAS,MAAM,SAAS,QAAQ,KAAK;AAC3C,MAAI,OAAO,QACT,OAAM,IAAI,MAAM,OAAO,KAAK;AAE9B,SAAO,EAAE,MAAM,OAAO,MAAM;GAGP,CAAC"}
|
package/dist/chat/index.d.ts
CHANGED
|
@@ -188,6 +188,147 @@ declare class TurnQueue {
|
|
|
188
188
|
private _decrementCount;
|
|
189
189
|
}
|
|
190
190
|
//#endregion
|
|
191
|
+
//#region src/chat/client-tools.d.ts
|
|
192
|
+
/**
|
|
193
|
+
* Wire-format tool schema sent from the client.
|
|
194
|
+
* Uses `parameters` (JSONSchema7) rather than AI SDK's `inputSchema`
|
|
195
|
+
* because Zod schemas cannot be serialized over the wire.
|
|
196
|
+
*/
|
|
197
|
+
type ClientToolSchema = {
|
|
198
|
+
/** Unique name for the tool */name: string; /** Human-readable description of what the tool does */
|
|
199
|
+
description?: Tool["description"]; /** JSON Schema defining the tool's input parameters */
|
|
200
|
+
parameters?: JSONSchema7;
|
|
201
|
+
};
|
|
202
|
+
/**
|
|
203
|
+
* Converts client tool schemas to AI SDK tool format.
|
|
204
|
+
*
|
|
205
|
+
* These tools have no `execute` function — when the AI model calls them,
|
|
206
|
+
* the tool call is sent back to the client for execution.
|
|
207
|
+
*
|
|
208
|
+
* @param clientTools - Array of tool schemas from the client
|
|
209
|
+
* @returns Record of AI SDK tools that can be spread into your tools object
|
|
210
|
+
*/
|
|
211
|
+
declare function createToolsFromClientSchemas(clientTools?: ClientToolSchema[]): ToolSet;
|
|
212
|
+
//#endregion
|
|
213
|
+
//#region src/chat/lifecycle.d.ts
|
|
214
|
+
/**
|
|
215
|
+
* Result passed to the `onChatResponse` lifecycle hook after a chat
|
|
216
|
+
* turn completes.
|
|
217
|
+
*/
|
|
218
|
+
type ChatResponseResult = {
|
|
219
|
+
/** The finalized assistant message from this turn. */message: UIMessage; /** The request ID associated with this turn. */
|
|
220
|
+
requestId: string; /** Whether this turn was a continuation of a previous assistant turn. */
|
|
221
|
+
continuation: boolean; /** How the turn ended. */
|
|
222
|
+
status: "completed" | "error" | "aborted"; /** Error message when `status` is `"error"`. */
|
|
223
|
+
error?: string;
|
|
224
|
+
};
|
|
225
|
+
/**
|
|
226
|
+
* Result returned by programmatic entry points that may skip execution
|
|
227
|
+
* when the chat state has been invalidated mid-flight (e.g. by a
|
|
228
|
+
* `CHAT_CLEAR` protocol message).
|
|
229
|
+
*/
|
|
230
|
+
type SaveMessagesResult = {
|
|
231
|
+
/** Server-generated request ID for the chat turn. */requestId: string; /** Whether the turn ran or was skipped (e.g. because the chat was cleared). */
|
|
232
|
+
status: "completed" | "skipped";
|
|
233
|
+
};
|
|
234
|
+
/**
|
|
235
|
+
* Context passed to the `onChatRecovery` hook when an interrupted chat
|
|
236
|
+
* stream is detected after DO restart.
|
|
237
|
+
*/
|
|
238
|
+
type ChatRecoveryContext = {
|
|
239
|
+
/** Stream ID from the interrupted stream. */streamId: string; /** Request ID from the interrupted stream. */
|
|
240
|
+
requestId: string; /** Partial text extracted from stored chunks. */
|
|
241
|
+
partialText: string; /** Partial message parts reconstructed from chunks. */
|
|
242
|
+
partialParts: MessagePart[]; /** Checkpoint data from `this.stash()` during the interrupted stream. */
|
|
243
|
+
recoveryData: unknown | null; /** Current persisted messages. */
|
|
244
|
+
messages: UIMessage[]; /** Custom body from the last chat request. */
|
|
245
|
+
lastBody?: Record<string, unknown>; /** Client tool schemas from the last chat request. */
|
|
246
|
+
lastClientTools?: ClientToolSchema[];
|
|
247
|
+
/**
|
|
248
|
+
* Epoch milliseconds when the underlying fiber was started. Compare
|
|
249
|
+
* against `Date.now()` to suppress continuations for turns that have
|
|
250
|
+
* been orphaned too long to safely replay.
|
|
251
|
+
*/
|
|
252
|
+
createdAt: number;
|
|
253
|
+
};
|
|
254
|
+
/**
|
|
255
|
+
* Options returned from `onChatRecovery` to control recovery behavior.
|
|
256
|
+
*/
|
|
257
|
+
type ChatRecoveryOptions = {
|
|
258
|
+
/** Save the partial response from stored chunks. Default: true. */persist?: boolean; /** Schedule a continuation via `continueLastTurn()`. Default: true. */
|
|
259
|
+
continue?: boolean;
|
|
260
|
+
};
|
|
261
|
+
/**
|
|
262
|
+
* Controls how overlapping user submit requests behave while another
|
|
263
|
+
* chat turn is already active or queued.
|
|
264
|
+
*
|
|
265
|
+
* - `"queue"` (default) — queue every submit and process them in order.
|
|
266
|
+
* - `"latest"` — keep only the latest overlapping submit; superseded
|
|
267
|
+
* submits still persist their user messages, but do not start their
|
|
268
|
+
* own model turn.
|
|
269
|
+
* - `"merge"` — coalesce overlapping submits into one model turn while
|
|
270
|
+
* preserving the submitted user content. Exact persistence depends on
|
|
271
|
+
* the chat package's message model.
|
|
272
|
+
* - `"drop"` — ignore overlapping submits entirely (messages not
|
|
273
|
+
* persisted).
|
|
274
|
+
* - `{ strategy: "debounce", debounceMs? }` — trailing-edge latest with
|
|
275
|
+
* a quiet window.
|
|
276
|
+
*
|
|
277
|
+
* Only applies to `submit-message` requests. Regenerations, tool
|
|
278
|
+
* continuations, approvals, clears, programmatic `saveMessages`, and
|
|
279
|
+
* `continueLastTurn` keep their existing serialized behavior.
|
|
280
|
+
*/
|
|
281
|
+
type MessageConcurrency = "queue" | "latest" | "merge" | "drop" | {
|
|
282
|
+
strategy: "debounce";
|
|
283
|
+
debounceMs?: number;
|
|
284
|
+
};
|
|
285
|
+
//#endregion
|
|
286
|
+
//#region src/chat/submit-concurrency.d.ts
|
|
287
|
+
type NormalizedMessageConcurrency = "queue" | "latest" | "merge" | "drop" | {
|
|
288
|
+
strategy: "debounce";
|
|
289
|
+
debounceMs: number;
|
|
290
|
+
};
|
|
291
|
+
type SubmitConcurrencyDecision = {
|
|
292
|
+
action: "execute" | "drop";
|
|
293
|
+
strategy: NormalizedMessageConcurrency | null;
|
|
294
|
+
submitSequence: number | null;
|
|
295
|
+
debounceUntilMs: number | null;
|
|
296
|
+
};
|
|
297
|
+
declare class SubmitConcurrencyController {
|
|
298
|
+
private readonly options;
|
|
299
|
+
private _submitSequence;
|
|
300
|
+
private _latestOverlappingSubmitSequence;
|
|
301
|
+
private _pendingEnqueueCount;
|
|
302
|
+
private _resetEpoch;
|
|
303
|
+
private _activeDebounceTimers;
|
|
304
|
+
private _activeDebounceResolves;
|
|
305
|
+
constructor(options: {
|
|
306
|
+
defaultDebounceMs: number;
|
|
307
|
+
});
|
|
308
|
+
get pendingEnqueueCount(): number;
|
|
309
|
+
get overlappingSubmitCount(): number;
|
|
310
|
+
decide(options: {
|
|
311
|
+
concurrency: MessageConcurrency;
|
|
312
|
+
isSubmitMessage: boolean;
|
|
313
|
+
queuedTurns: number;
|
|
314
|
+
}): SubmitConcurrencyDecision;
|
|
315
|
+
/**
|
|
316
|
+
* Mark a submit as accepted and in-flight between admission and turn
|
|
317
|
+
* queue registration. Returns an idempotent `release()` function that
|
|
318
|
+
* must be called when the submit either reaches the turn queue or is
|
|
319
|
+
* abandoned. The returned function is bound to the controller's reset
|
|
320
|
+
* epoch — releases from before the most recent `reset()` are no-ops,
|
|
321
|
+
* so post-reset submits keep an accurate count.
|
|
322
|
+
*/
|
|
323
|
+
beginEnqueue(): () => void;
|
|
324
|
+
isSuperseded(submitSequence: number | null): boolean;
|
|
325
|
+
waitForTimestamp(timestampMs: number): Promise<void>;
|
|
326
|
+
cancelActiveDebounce(): void;
|
|
327
|
+
reset(): void;
|
|
328
|
+
waitForIdle(waitForQueueIdle: () => Promise<void>): Promise<void>;
|
|
329
|
+
private normalize;
|
|
330
|
+
}
|
|
331
|
+
//#endregion
|
|
191
332
|
//#region src/chat/broadcast-state.d.ts
|
|
192
333
|
type BroadcastStreamState = {
|
|
193
334
|
status: "idle";
|
|
@@ -341,28 +482,6 @@ declare class ResumableStream {
|
|
|
341
482
|
insertStaleStream(streamId: string, requestId: string, ageMs: number): void;
|
|
342
483
|
}
|
|
343
484
|
//#endregion
|
|
344
|
-
//#region src/chat/client-tools.d.ts
|
|
345
|
-
/**
|
|
346
|
-
* Wire-format tool schema sent from the client.
|
|
347
|
-
* Uses `parameters` (JSONSchema7) rather than AI SDK's `inputSchema`
|
|
348
|
-
* because Zod schemas cannot be serialized over the wire.
|
|
349
|
-
*/
|
|
350
|
-
type ClientToolSchema = {
|
|
351
|
-
/** Unique name for the tool */name: string; /** Human-readable description of what the tool does */
|
|
352
|
-
description?: Tool["description"]; /** JSON Schema defining the tool's input parameters */
|
|
353
|
-
parameters?: JSONSchema7;
|
|
354
|
-
};
|
|
355
|
-
/**
|
|
356
|
-
* Converts client tool schemas to AI SDK tool format.
|
|
357
|
-
*
|
|
358
|
-
* These tools have no `execute` function — when the AI model calls them,
|
|
359
|
-
* the tool call is sent back to the client for execution.
|
|
360
|
-
*
|
|
361
|
-
* @param clientTools - Array of tool schemas from the client
|
|
362
|
-
* @returns Record of AI SDK tools that can be spread into your tools object
|
|
363
|
-
*/
|
|
364
|
-
declare function createToolsFromClientSchemas(clientTools?: ClientToolSchema[]): ToolSet;
|
|
365
|
-
//#endregion
|
|
366
485
|
//#region src/chat/protocol.d.ts
|
|
367
486
|
/**
|
|
368
487
|
* Wire protocol message type constants for the cf_agent_chat_* protocol.
|
|
@@ -599,77 +718,32 @@ type ChatProtocolEvent = {
|
|
|
599
718
|
*/
|
|
600
719
|
declare function parseProtocolMessage(raw: string): ChatProtocolEvent | null;
|
|
601
720
|
//#endregion
|
|
602
|
-
//#region src/chat/
|
|
721
|
+
//#region src/chat/message-reconciler.d.ts
|
|
603
722
|
/**
|
|
604
|
-
*
|
|
605
|
-
*
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
* Result returned by programmatic entry points that may skip execution
|
|
616
|
-
* when the chat state has been invalidated mid-flight (e.g. by a
|
|
617
|
-
* `CHAT_CLEAR` protocol message).
|
|
618
|
-
*/
|
|
619
|
-
type SaveMessagesResult = {
|
|
620
|
-
/** Server-generated request ID for the chat turn. */requestId: string; /** Whether the turn ran or was skipped (e.g. because the chat was cleared). */
|
|
621
|
-
status: "completed" | "skipped";
|
|
622
|
-
};
|
|
623
|
-
/**
|
|
624
|
-
* Context passed to the `onChatRecovery` hook when an interrupted chat
|
|
625
|
-
* stream is detected after DO restart.
|
|
723
|
+
* Reconcile incoming client messages against server state.
|
|
724
|
+
*
|
|
725
|
+
* 1. Merges server-known tool outputs into incoming messages that still
|
|
726
|
+
* show stale states (input-available, approval-requested, approval-responded)
|
|
727
|
+
* 2. Reconciles assistant IDs: exact match → content-key match → toolCallId match
|
|
728
|
+
*
|
|
729
|
+
* @param incoming - Messages from the client
|
|
730
|
+
* @param serverMessages - Current server-side messages (source of truth)
|
|
731
|
+
* @param sanitizeForContentKey - Function to sanitize a message before computing
|
|
732
|
+
* its content key (typically strips ephemeral provider metadata)
|
|
733
|
+
* @returns Reconciled messages ready for persistence
|
|
626
734
|
*/
|
|
627
|
-
|
|
628
|
-
/** Stream ID from the interrupted stream. */streamId: string; /** Request ID from the interrupted stream. */
|
|
629
|
-
requestId: string; /** Partial text extracted from stored chunks. */
|
|
630
|
-
partialText: string; /** Partial message parts reconstructed from chunks. */
|
|
631
|
-
partialParts: MessagePart[]; /** Checkpoint data from `this.stash()` during the interrupted stream. */
|
|
632
|
-
recoveryData: unknown | null; /** Current persisted messages. */
|
|
633
|
-
messages: UIMessage[]; /** Custom body from the last chat request. */
|
|
634
|
-
lastBody?: Record<string, unknown>; /** Client tool schemas from the last chat request. */
|
|
635
|
-
lastClientTools?: ClientToolSchema[];
|
|
636
|
-
/**
|
|
637
|
-
* Epoch milliseconds when the underlying fiber was started. Compare
|
|
638
|
-
* against `Date.now()` to suppress continuations for turns that have
|
|
639
|
-
* been orphaned too long to safely replay.
|
|
640
|
-
*/
|
|
641
|
-
createdAt: number;
|
|
642
|
-
};
|
|
735
|
+
declare function reconcileMessages(incoming: UIMessage[], serverMessages: readonly UIMessage[], sanitizeForContentKey?: (message: UIMessage) => UIMessage): UIMessage[];
|
|
643
736
|
/**
|
|
644
|
-
*
|
|
737
|
+
* For a single message, resolve its ID by matching toolCallId against server state.
|
|
738
|
+
* Prevents duplicate DB rows when client IDs differ from server IDs.
|
|
739
|
+
* Tool call IDs are unique per conversation, so matching is safe regardless of state.
|
|
645
740
|
*/
|
|
646
|
-
|
|
647
|
-
/** Save the partial response from stored chunks. Default: true. */persist?: boolean; /** Schedule a continuation via `continueLastTurn()`. Default: true. */
|
|
648
|
-
continue?: boolean;
|
|
649
|
-
};
|
|
741
|
+
declare function resolveToolMergeId(message: UIMessage, serverMessages: readonly UIMessage[]): UIMessage;
|
|
650
742
|
/**
|
|
651
|
-
*
|
|
652
|
-
*
|
|
653
|
-
*
|
|
654
|
-
* - `"queue"` (default) — queue every submit and process them in order.
|
|
655
|
-
* - `"latest"` — keep only the latest overlapping submit; superseded
|
|
656
|
-
* submits still persist their user messages, but do not start their
|
|
657
|
-
* own model turn.
|
|
658
|
-
* - `"merge"` — like `latest`, but all overlapping user messages remain
|
|
659
|
-
* in the conversation history. The model sees them all in one turn.
|
|
660
|
-
* - `"drop"` — ignore overlapping submits entirely (messages not
|
|
661
|
-
* persisted).
|
|
662
|
-
* - `{ strategy: "debounce", debounceMs? }` — trailing-edge latest with
|
|
663
|
-
* a quiet window.
|
|
664
|
-
*
|
|
665
|
-
* Only applies to `submit-message` requests. Regenerations, tool
|
|
666
|
-
* continuations, approvals, clears, programmatic `saveMessages`, and
|
|
667
|
-
* `continueLastTurn` keep their existing serialized behavior.
|
|
743
|
+
* Content key for assistant messages used for dedup of identical short replies.
|
|
744
|
+
* Returns JSON of sanitized parts, or undefined for non-assistant messages.
|
|
668
745
|
*/
|
|
669
|
-
|
|
670
|
-
strategy: "debounce";
|
|
671
|
-
debounceMs?: number;
|
|
672
|
-
};
|
|
746
|
+
declare function assistantContentKey(message: UIMessage, sanitize?: (message: UIMessage) => UIMessage): string | undefined;
|
|
673
747
|
//#endregion
|
|
674
|
-
export { AbortRegistry, type BroadcastStreamEvent, type BroadcastStreamState, type TransitionResult as BroadcastTransitionResult, CHAT_MESSAGE_TYPES, type ChatProtocolEvent, type ChatRecoveryContext, type ChatRecoveryOptions, type ChatResponseResult, type ChunkAction, type ChunkResult, type ClientToolSchema, type ContinuationConnection, type ContinuationDeferred, type ContinuationPending, ContinuationState, type EnqueueOptions, type MessageConcurrency, type MessagePart, type MessageParts, ROW_MAX_BYTES, ResumableStream, type SaveMessagesResult, type SqlTaggedTemplate, StreamAccumulator, type StreamAccumulatorOptions, type StreamChunkData, type ToolPartUpdate, TurnQueue, type TurnResult, applyChunkToParts, applyToolUpdate, transition as broadcastTransition, byteLength, createToolsFromClientSchemas, enforceRowSizeLimit, parseProtocolMessage, sanitizeMessage, toolApprovalUpdate, toolResultUpdate };
|
|
748
|
+
export { AbortRegistry, type BroadcastStreamEvent, type BroadcastStreamState, type TransitionResult as BroadcastTransitionResult, CHAT_MESSAGE_TYPES, type ChatProtocolEvent, type ChatRecoveryContext, type ChatRecoveryOptions, type ChatResponseResult, type ChunkAction, type ChunkResult, type ClientToolSchema, type ContinuationConnection, type ContinuationDeferred, type ContinuationPending, ContinuationState, type EnqueueOptions, type MessageConcurrency, type MessagePart, type MessageParts, type NormalizedMessageConcurrency, ROW_MAX_BYTES, ResumableStream, type SaveMessagesResult, type SqlTaggedTemplate, StreamAccumulator, type StreamAccumulatorOptions, type StreamChunkData, SubmitConcurrencyController, type SubmitConcurrencyDecision, type ToolPartUpdate, TurnQueue, type TurnResult, applyChunkToParts, applyToolUpdate, assistantContentKey, transition as broadcastTransition, byteLength, createToolsFromClientSchemas, enforceRowSizeLimit, parseProtocolMessage, reconcileMessages, resolveToolMergeId, sanitizeMessage, toolApprovalUpdate, toolResultUpdate };
|
|
675
749
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/chat/index.js
CHANGED
|
@@ -574,6 +574,126 @@ var TurnQueue = class {
|
|
|
574
574
|
}
|
|
575
575
|
};
|
|
576
576
|
//#endregion
|
|
577
|
+
//#region src/chat/submit-concurrency.ts
|
|
578
|
+
var SubmitConcurrencyController = class {
|
|
579
|
+
constructor(options) {
|
|
580
|
+
this.options = options;
|
|
581
|
+
this._submitSequence = 0;
|
|
582
|
+
this._latestOverlappingSubmitSequence = 0;
|
|
583
|
+
this._pendingEnqueueCount = 0;
|
|
584
|
+
this._resetEpoch = 0;
|
|
585
|
+
this._activeDebounceTimers = /* @__PURE__ */ new Set();
|
|
586
|
+
this._activeDebounceResolves = /* @__PURE__ */ new Set();
|
|
587
|
+
}
|
|
588
|
+
get pendingEnqueueCount() {
|
|
589
|
+
return this._pendingEnqueueCount;
|
|
590
|
+
}
|
|
591
|
+
get overlappingSubmitCount() {
|
|
592
|
+
return this._latestOverlappingSubmitSequence;
|
|
593
|
+
}
|
|
594
|
+
decide(options) {
|
|
595
|
+
const queuedTurnsInCurrentEpoch = options.queuedTurns + this._pendingEnqueueCount;
|
|
596
|
+
if (!options.isSubmitMessage || queuedTurnsInCurrentEpoch === 0) return {
|
|
597
|
+
action: "execute",
|
|
598
|
+
strategy: null,
|
|
599
|
+
submitSequence: null,
|
|
600
|
+
debounceUntilMs: null
|
|
601
|
+
};
|
|
602
|
+
const concurrency = this.normalize(options.concurrency);
|
|
603
|
+
if (concurrency === "drop") return {
|
|
604
|
+
action: "drop",
|
|
605
|
+
strategy: concurrency,
|
|
606
|
+
submitSequence: null,
|
|
607
|
+
debounceUntilMs: null
|
|
608
|
+
};
|
|
609
|
+
if (concurrency === "queue") return {
|
|
610
|
+
action: "execute",
|
|
611
|
+
strategy: concurrency,
|
|
612
|
+
submitSequence: null,
|
|
613
|
+
debounceUntilMs: null
|
|
614
|
+
};
|
|
615
|
+
const submitSequence = ++this._submitSequence;
|
|
616
|
+
this._latestOverlappingSubmitSequence = submitSequence;
|
|
617
|
+
if (concurrency === "latest" || concurrency === "merge") return {
|
|
618
|
+
action: "execute",
|
|
619
|
+
strategy: concurrency,
|
|
620
|
+
submitSequence,
|
|
621
|
+
debounceUntilMs: null
|
|
622
|
+
};
|
|
623
|
+
return {
|
|
624
|
+
action: "execute",
|
|
625
|
+
strategy: concurrency,
|
|
626
|
+
submitSequence,
|
|
627
|
+
debounceUntilMs: Date.now() + concurrency.debounceMs
|
|
628
|
+
};
|
|
629
|
+
}
|
|
630
|
+
/**
|
|
631
|
+
* Mark a submit as accepted and in-flight between admission and turn
|
|
632
|
+
* queue registration. Returns an idempotent `release()` function that
|
|
633
|
+
* must be called when the submit either reaches the turn queue or is
|
|
634
|
+
* abandoned. The returned function is bound to the controller's reset
|
|
635
|
+
* epoch — releases from before the most recent `reset()` are no-ops,
|
|
636
|
+
* so post-reset submits keep an accurate count.
|
|
637
|
+
*/
|
|
638
|
+
beginEnqueue() {
|
|
639
|
+
this._pendingEnqueueCount++;
|
|
640
|
+
const epoch = this._resetEpoch;
|
|
641
|
+
let released = false;
|
|
642
|
+
return () => {
|
|
643
|
+
if (released) return;
|
|
644
|
+
released = true;
|
|
645
|
+
if (this._resetEpoch !== epoch) return;
|
|
646
|
+
this._pendingEnqueueCount = Math.max(0, this._pendingEnqueueCount - 1);
|
|
647
|
+
};
|
|
648
|
+
}
|
|
649
|
+
isSuperseded(submitSequence) {
|
|
650
|
+
return submitSequence !== null && submitSequence < this._latestOverlappingSubmitSequence;
|
|
651
|
+
}
|
|
652
|
+
async waitForTimestamp(timestampMs) {
|
|
653
|
+
const remainingMs = timestampMs - Date.now();
|
|
654
|
+
if (remainingMs <= 0) return;
|
|
655
|
+
await new Promise((resolve) => {
|
|
656
|
+
const wrappedResolve = () => {
|
|
657
|
+
this._activeDebounceResolves.delete(wrappedResolve);
|
|
658
|
+
resolve();
|
|
659
|
+
};
|
|
660
|
+
const timer = setTimeout(() => {
|
|
661
|
+
this._activeDebounceTimers.delete(timer);
|
|
662
|
+
wrappedResolve();
|
|
663
|
+
}, remainingMs);
|
|
664
|
+
this._activeDebounceTimers.add(timer);
|
|
665
|
+
this._activeDebounceResolves.add(wrappedResolve);
|
|
666
|
+
});
|
|
667
|
+
}
|
|
668
|
+
cancelActiveDebounce() {
|
|
669
|
+
for (const timer of this._activeDebounceTimers) clearTimeout(timer);
|
|
670
|
+
this._activeDebounceTimers.clear();
|
|
671
|
+
const resolves = [...this._activeDebounceResolves];
|
|
672
|
+
this._activeDebounceResolves.clear();
|
|
673
|
+
for (const resolve of resolves) resolve();
|
|
674
|
+
}
|
|
675
|
+
reset() {
|
|
676
|
+
this._resetEpoch++;
|
|
677
|
+
this._pendingEnqueueCount = 0;
|
|
678
|
+
this.cancelActiveDebounce();
|
|
679
|
+
}
|
|
680
|
+
async waitForIdle(waitForQueueIdle) {
|
|
681
|
+
while (true) {
|
|
682
|
+
await waitForQueueIdle();
|
|
683
|
+
if (this._pendingEnqueueCount === 0) return;
|
|
684
|
+
await new Promise((resolve) => setTimeout(resolve, 5));
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
normalize(concurrency) {
|
|
688
|
+
if (typeof concurrency === "string") return concurrency;
|
|
689
|
+
const debounceMs = concurrency.debounceMs;
|
|
690
|
+
return {
|
|
691
|
+
strategy: "debounce",
|
|
692
|
+
debounceMs: typeof debounceMs === "number" && Number.isFinite(debounceMs) && debounceMs >= 0 ? debounceMs : this.options.defaultDebounceMs
|
|
693
|
+
};
|
|
694
|
+
}
|
|
695
|
+
};
|
|
696
|
+
//#endregion
|
|
577
697
|
//#region src/chat/broadcast-state.ts
|
|
578
698
|
function transition(state, event) {
|
|
579
699
|
switch (event.type) {
|
|
@@ -1300,6 +1420,117 @@ function parseProtocolMessage(raw) {
|
|
|
1300
1420
|
}
|
|
1301
1421
|
}
|
|
1302
1422
|
//#endregion
|
|
1303
|
-
|
|
1423
|
+
//#region src/chat/message-reconciler.ts
|
|
1424
|
+
/**
|
|
1425
|
+
* Reconcile incoming client messages against server state.
|
|
1426
|
+
*
|
|
1427
|
+
* 1. Merges server-known tool outputs into incoming messages that still
|
|
1428
|
+
* show stale states (input-available, approval-requested, approval-responded)
|
|
1429
|
+
* 2. Reconciles assistant IDs: exact match → content-key match → toolCallId match
|
|
1430
|
+
*
|
|
1431
|
+
* @param incoming - Messages from the client
|
|
1432
|
+
* @param serverMessages - Current server-side messages (source of truth)
|
|
1433
|
+
* @param sanitizeForContentKey - Function to sanitize a message before computing
|
|
1434
|
+
* its content key (typically strips ephemeral provider metadata)
|
|
1435
|
+
* @returns Reconciled messages ready for persistence
|
|
1436
|
+
*/
|
|
1437
|
+
function reconcileMessages(incoming, serverMessages, sanitizeForContentKey) {
|
|
1438
|
+
return reconcileAssistantIds(mergeServerToolOutputs(incoming, serverMessages), serverMessages, sanitizeForContentKey);
|
|
1439
|
+
}
|
|
1440
|
+
/**
|
|
1441
|
+
* For a single message, resolve its ID by matching toolCallId against server state.
|
|
1442
|
+
* Prevents duplicate DB rows when client IDs differ from server IDs.
|
|
1443
|
+
* Tool call IDs are unique per conversation, so matching is safe regardless of state.
|
|
1444
|
+
*/
|
|
1445
|
+
function resolveToolMergeId(message, serverMessages) {
|
|
1446
|
+
if (message.role !== "assistant") return message;
|
|
1447
|
+
for (const part of message.parts) if ("toolCallId" in part && part.toolCallId) {
|
|
1448
|
+
const toolCallId = part.toolCallId;
|
|
1449
|
+
const existing = findMessageByToolCallId(serverMessages, toolCallId);
|
|
1450
|
+
if (existing && existing.id !== message.id) return {
|
|
1451
|
+
...message,
|
|
1452
|
+
id: existing.id
|
|
1453
|
+
};
|
|
1454
|
+
}
|
|
1455
|
+
return message;
|
|
1456
|
+
}
|
|
1457
|
+
/**
|
|
1458
|
+
* Content key for assistant messages used for dedup of identical short replies.
|
|
1459
|
+
* Returns JSON of sanitized parts, or undefined for non-assistant messages.
|
|
1460
|
+
*/
|
|
1461
|
+
function assistantContentKey(message, sanitize) {
|
|
1462
|
+
if (message.role !== "assistant") return;
|
|
1463
|
+
const sanitized = sanitize ? sanitize(message) : message;
|
|
1464
|
+
return JSON.stringify(sanitized.parts);
|
|
1465
|
+
}
|
|
1466
|
+
function mergeServerToolOutputs(incoming, serverMessages) {
|
|
1467
|
+
const serverToolOutputs = /* @__PURE__ */ new Map();
|
|
1468
|
+
for (const msg of serverMessages) {
|
|
1469
|
+
if (msg.role !== "assistant") continue;
|
|
1470
|
+
for (const part of msg.parts) if ("toolCallId" in part && "state" in part && part.state === "output-available" && "output" in part) serverToolOutputs.set(part.toolCallId, part.output);
|
|
1471
|
+
}
|
|
1472
|
+
if (serverToolOutputs.size === 0) return incoming;
|
|
1473
|
+
return incoming.map((msg) => {
|
|
1474
|
+
if (msg.role !== "assistant") return msg;
|
|
1475
|
+
let hasChanges = false;
|
|
1476
|
+
const updatedParts = msg.parts.map((part) => {
|
|
1477
|
+
if ("toolCallId" in part && "state" in part && (part.state === "input-available" || part.state === "approval-requested" || part.state === "approval-responded") && serverToolOutputs.has(part.toolCallId)) {
|
|
1478
|
+
hasChanges = true;
|
|
1479
|
+
return {
|
|
1480
|
+
...part,
|
|
1481
|
+
state: "output-available",
|
|
1482
|
+
output: serverToolOutputs.get(part.toolCallId)
|
|
1483
|
+
};
|
|
1484
|
+
}
|
|
1485
|
+
return part;
|
|
1486
|
+
});
|
|
1487
|
+
return hasChanges ? {
|
|
1488
|
+
...msg,
|
|
1489
|
+
parts: updatedParts
|
|
1490
|
+
} : msg;
|
|
1491
|
+
});
|
|
1492
|
+
}
|
|
1493
|
+
function reconcileAssistantIds(incoming, serverMessages, sanitize) {
|
|
1494
|
+
if (serverMessages.length === 0) return incoming;
|
|
1495
|
+
const claimedServerIndices = /* @__PURE__ */ new Set();
|
|
1496
|
+
const exactMatchMap = /* @__PURE__ */ new Map();
|
|
1497
|
+
for (let i = 0; i < incoming.length; i++) {
|
|
1498
|
+
const serverIdx = serverMessages.findIndex((sm, si) => !claimedServerIndices.has(si) && sm.id === incoming[i].id);
|
|
1499
|
+
if (serverIdx !== -1) {
|
|
1500
|
+
claimedServerIndices.add(serverIdx);
|
|
1501
|
+
exactMatchMap.set(i, serverIdx);
|
|
1502
|
+
}
|
|
1503
|
+
}
|
|
1504
|
+
return incoming.map((incomingMessage, incomingIdx) => {
|
|
1505
|
+
if (exactMatchMap.has(incomingIdx)) return incomingMessage;
|
|
1506
|
+
if (incomingMessage.role !== "assistant" || hasToolCallPart(incomingMessage)) return incomingMessage;
|
|
1507
|
+
const incomingKey = assistantContentKey(incomingMessage, sanitize);
|
|
1508
|
+
if (!incomingKey) return incomingMessage;
|
|
1509
|
+
for (let i = 0; i < serverMessages.length; i++) {
|
|
1510
|
+
if (claimedServerIndices.has(i)) continue;
|
|
1511
|
+
const serverMessage = serverMessages[i];
|
|
1512
|
+
if (serverMessage.role !== "assistant" || hasToolCallPart(serverMessage)) continue;
|
|
1513
|
+
if (assistantContentKey(serverMessage, sanitize) === incomingKey) {
|
|
1514
|
+
claimedServerIndices.add(i);
|
|
1515
|
+
return {
|
|
1516
|
+
...incomingMessage,
|
|
1517
|
+
id: serverMessage.id
|
|
1518
|
+
};
|
|
1519
|
+
}
|
|
1520
|
+
}
|
|
1521
|
+
return incomingMessage;
|
|
1522
|
+
});
|
|
1523
|
+
}
|
|
1524
|
+
function hasToolCallPart(message) {
|
|
1525
|
+
return message.parts.some((part) => "toolCallId" in part);
|
|
1526
|
+
}
|
|
1527
|
+
function findMessageByToolCallId(messages, toolCallId) {
|
|
1528
|
+
for (const msg of messages) {
|
|
1529
|
+
if (msg.role !== "assistant") continue;
|
|
1530
|
+
for (const part of msg.parts) if ("toolCallId" in part && part.toolCallId === toolCallId) return msg;
|
|
1531
|
+
}
|
|
1532
|
+
}
|
|
1533
|
+
//#endregion
|
|
1534
|
+
export { AbortRegistry, CHAT_MESSAGE_TYPES, ContinuationState, ROW_MAX_BYTES, ResumableStream, StreamAccumulator, SubmitConcurrencyController, TurnQueue, applyChunkToParts, applyToolUpdate, assistantContentKey, transition as broadcastTransition, byteLength, createToolsFromClientSchemas, enforceRowSizeLimit, parseProtocolMessage, reconcileMessages, resolveToolMergeId, sanitizeMessage, toolApprovalUpdate, toolResultUpdate };
|
|
1304
1535
|
|
|
1305
1536
|
//# sourceMappingURL=index.js.map
|