@genkit-ai/vercel-ai 0.2.0-rc.0

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.
@@ -0,0 +1,162 @@
1
+ import { UIMessage } from 'ai';
2
+ import { MessageData, Part } from 'genkit';
3
+
4
+ /**
5
+ * Copyright 2026 Google LLC
6
+ *
7
+ * Licensed under the Apache License, Version 2.0 (the "License");
8
+ * you may not use this file except in compliance with the License.
9
+ * You may obtain a copy of the License at
10
+ *
11
+ * http://www.apache.org/licenses/LICENSE-2.0
12
+ *
13
+ * Unless required by applicable law or agreed to in writing, software
14
+ * distributed under the License is distributed on an "AS IS" BASIS,
15
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16
+ * See the License for the specific language governing permissions and
17
+ * limitations under the License.
18
+ */
19
+
20
+ /**
21
+ * Maps a single Vercel AI SDK v6 UIMessagePart to zero or more Genkit Parts.
22
+ *
23
+ * Handles `text`, `file`, and tool parts (both `ToolUIPart` with
24
+ * `type: 'tool-<name>'` and `DynamicToolUIPart` with `type: 'dynamic-tool'`).
25
+ *
26
+ * A tool part with state `output-available` produces both a `toolRequest`
27
+ * and a `toolResponse` part (matching Genkit's conversation model).
28
+ */
29
+ declare function mapUIPartToGenkit(part: UIMessage['parts'][number]): Part[];
30
+ /**
31
+ * Maps a Vercel AI SDK UIMessage to a Genkit MessageData.
32
+ */
33
+ declare function mapUIMessageToGenkit(msg: UIMessage): MessageData;
34
+ /**
35
+ * Maps a single Genkit {@link MessageData} to an AI SDK {@link UIMessage}.
36
+ *
37
+ * Pass `responsesByRef` (built across the whole conversation) so each
38
+ * `toolRequest` can be paired with the `toolResponse` that resolved it, even
39
+ * though they live in different messages. When omitted, all tool parts are
40
+ * emitted in the `input-available` state.
41
+ */
42
+ declare function mapGenkitMessageToUI(msg: MessageData, responsesByRef?: Map<string, unknown>, id?: string): UIMessage;
43
+ /**
44
+ * Converts the `messages` of a Genkit `SessionSnapshot` (an array of
45
+ * {@link MessageData}) into AI SDK {@link UIMessage}s suitable for seeding
46
+ * `useChat` (via its `messages` option or `setMessages`) to rehydrate a
47
+ * previous conversation after a reload.
48
+ *
49
+ * Tool requests and their responses (which Genkit stores in separate
50
+ * `model`/`tool` messages) are merged into single AI SDK tool parts, so the
51
+ * UI renders each tool call as one element with both its input and output.
52
+ * `tool`-role messages are therefore omitted from the result. Unresolved tool
53
+ * requests (e.g. a pending interrupt) are emitted in the `input-available`
54
+ * state.
55
+ *
56
+ * Conversation state is server-managed by `sessionId` (the `useChat` `id`), so
57
+ * to resume the *next* turn you only need to reuse the same chat `id` — there
58
+ * is no snapshot to restore on the client.
59
+ *
60
+ * @example
61
+ * ```ts
62
+ * const snapshot = await runFlow({ url: '/api/chat/weather/state', input: sessionId });
63
+ * const messages = messagesFromSnapshot(snapshot.state.messages);
64
+ * // pass `messages` to useChat({ id: sessionId, messages })
65
+ * ```
66
+ */
67
+ declare function messagesFromSnapshot(messages: MessageData[]): UIMessage[];
68
+ /**
69
+ * Marker placed on a tool output to signal that the user chose to *restart*
70
+ * (re-run) an interrupted tool rather than supply a final response.
71
+ *
72
+ * `Symbol.for` is intentionally avoided because tool outputs are serialized to
73
+ * JSON before reaching the transport; a string marker survives that round-trip.
74
+ */
75
+ declare const RESTART_MARKER = "__genkitVercelAiRestart__";
76
+ /**
77
+ * The serialized shape produced by {@link restartInterrupt}.
78
+ */
79
+ interface RestartInterruptOutput {
80
+ [RESTART_MARKER]: true;
81
+ /**
82
+ * Optional metadata surfaced to the tool as its `resumed` argument when it
83
+ * re-runs server-side. Defaults to `true` when omitted.
84
+ */
85
+ metadata?: unknown;
86
+ }
87
+ /**
88
+ * Builds a tool output that instructs the Genkit agent to **restart** (re-run)
89
+ * an interrupted tool instead of accepting a user-supplied response.
90
+ *
91
+ * Pass the result to the AI SDK's `addToolResult` as the tool `output`:
92
+ *
93
+ * ```tsx
94
+ * addToolResult({
95
+ * tool: 'userApproval',
96
+ * toolCallId,
97
+ * output: restartInterrupt({ note: 'user revised the request' }),
98
+ * });
99
+ * ```
100
+ *
101
+ * The transport detects this marker and emits a `resume.restart` entry (with
102
+ * the original tool input, which the server requires to match exactly) instead
103
+ * of a `resume.respond` entry. Any `metadata` is passed through to the tool as
104
+ * its `resumed` value.
105
+ */
106
+ declare function restartInterrupt(metadata?: unknown): RestartInterruptOutput;
107
+ /**
108
+ * Returns the restart payload if `output` was produced by
109
+ * {@link restartInterrupt}, otherwise `undefined`.
110
+ */
111
+ declare function asRestartInterrupt(output: unknown): RestartInterruptOutput | undefined;
112
+ /**
113
+ * A resolved tool invocation extracted from the frontend message history.
114
+ */
115
+ interface ResolvedToolResult {
116
+ toolCallId: string;
117
+ toolName: string;
118
+ /** The original tool input (needed to build a `resume.restart`). */
119
+ input: unknown;
120
+ /** The user-supplied output (or a {@link restartInterrupt} marker). */
121
+ result: unknown;
122
+ }
123
+ /**
124
+ * Extracts resolved tool invocations from a UIMessage array.
125
+ *
126
+ * Supports the v6 per-tool format (`type === 'tool-<toolName>'`) as well as
127
+ * dynamic tools (`type === 'dynamic-tool'`, with an explicit `toolName`),
128
+ * matching `state === 'output-available'`.
129
+ *
130
+ * Scans **all** messages (most-recent first) and returns every resolved tool
131
+ * result, de-duplicated by `toolCallId` (keeping the most recent).
132
+ *
133
+ * Prefer {@link currentTurnResolvedTools} for interrupt-resume detection — it
134
+ * scopes the scan to the most recent turn so prior turns' (already-completed)
135
+ * tool calls are not re-sent as a resume payload.
136
+ */
137
+ declare function extractResolvedToolResults(messages: UIMessage[]): ResolvedToolResult[];
138
+ /**
139
+ * Returns the resolved tool invocations that constitute an interrupt resume
140
+ * for the *current* turn, or an empty array if this is a fresh user turn.
141
+ *
142
+ * An interrupt resume is identified by the last assistant message carrying
143
+ * resolved tool outputs (e.g. supplied via `addToolResult`) with no *non-empty*
144
+ * user message after it. A real user message following the assistant turn means
145
+ * the user started a new turn, so no resume is performed.
146
+ *
147
+ * This deliberately scopes detection to the most recent turn: tool calls from
148
+ * earlier turns are already part of the server-side session state and must not
149
+ * be replayed as a resume payload.
150
+ */
151
+ declare function currentTurnResolvedTools(messages: UIMessage[]): ResolvedToolResult[];
152
+ /**
153
+ * Finds the last user message in a UIMessage array.
154
+ */
155
+ declare function findLastUserMessage(messages: UIMessage[]): UIMessage | undefined;
156
+ /**
157
+ * Finds the last *non-empty* user message in a UIMessage array, skipping
158
+ * phantom empty messages produced by `sendMessage({ text: '' })`.
159
+ */
160
+ declare function findLastNonEmptyUserMessage(messages: UIMessage[]): UIMessage | undefined;
161
+
162
+ export { type ResolvedToolResult, type RestartInterruptOutput, asRestartInterrupt, currentTurnResolvedTools, extractResolvedToolResults, findLastNonEmptyUserMessage, findLastUserMessage, mapGenkitMessageToUI, mapUIMessageToGenkit, mapUIPartToGenkit, messagesFromSnapshot, restartInterrupt };
package/lib/mapping.js ADDED
@@ -0,0 +1,280 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+ var mapping_exports = {};
20
+ __export(mapping_exports, {
21
+ asRestartInterrupt: () => asRestartInterrupt,
22
+ currentTurnResolvedTools: () => currentTurnResolvedTools,
23
+ extractResolvedToolResults: () => extractResolvedToolResults,
24
+ findLastNonEmptyUserMessage: () => findLastNonEmptyUserMessage,
25
+ findLastUserMessage: () => findLastUserMessage,
26
+ mapGenkitMessageToUI: () => mapGenkitMessageToUI,
27
+ mapUIMessageToGenkit: () => mapUIMessageToGenkit,
28
+ mapUIPartToGenkit: () => mapUIPartToGenkit,
29
+ messagesFromSnapshot: () => messagesFromSnapshot,
30
+ restartInterrupt: () => restartInterrupt
31
+ });
32
+ module.exports = __toCommonJS(mapping_exports);
33
+ function mapRole(role) {
34
+ switch (role) {
35
+ case "assistant":
36
+ return "model";
37
+ case "user":
38
+ return "user";
39
+ case "system":
40
+ return "system";
41
+ default:
42
+ return "user";
43
+ }
44
+ }
45
+ function mapGenkitRole(role) {
46
+ switch (role) {
47
+ case "model":
48
+ return "assistant";
49
+ case "system":
50
+ return "system";
51
+ case "user":
52
+ default:
53
+ return "user";
54
+ }
55
+ }
56
+ function extractPartMetadata(part) {
57
+ const md = part?.metadata;
58
+ if (md && typeof md === "object" && !Array.isArray(md)) {
59
+ return md;
60
+ }
61
+ return void 0;
62
+ }
63
+ function mapUIPartToGenkit(part) {
64
+ const metadata = extractPartMetadata(part);
65
+ const withMetadata = (p) => metadata ? { ...p, metadata } : p;
66
+ if (part.type === "text") {
67
+ return [withMetadata({ text: part.text })];
68
+ }
69
+ if (part.type === "reasoning") {
70
+ const reasoning = part.text;
71
+ return typeof reasoning === "string" && reasoning !== "" ? [withMetadata({ reasoning })] : [];
72
+ }
73
+ if (part.type === "file") {
74
+ return [
75
+ withMetadata({
76
+ media: {
77
+ url: part.url,
78
+ contentType: part.mediaType
79
+ }
80
+ })
81
+ ];
82
+ }
83
+ if (part.type === "dynamic-tool" || part.type.startsWith("tool-")) {
84
+ const p = part;
85
+ const toolCallId = typeof p.toolCallId === "string" ? p.toolCallId : crypto.randomUUID();
86
+ const toolName = typeof p.toolName === "string" && p.toolName ? p.toolName : part.type.startsWith("tool-") ? part.type.slice("tool-".length) : "unknown";
87
+ const parts = [
88
+ withMetadata({
89
+ toolRequest: {
90
+ ref: toolCallId,
91
+ name: toolName,
92
+ input: p.input
93
+ }
94
+ })
95
+ ];
96
+ if (p.state === "output-available" && p.output !== void 0) {
97
+ parts.push(
98
+ withMetadata({
99
+ toolResponse: {
100
+ ref: toolCallId,
101
+ name: toolName,
102
+ output: p.output
103
+ }
104
+ })
105
+ );
106
+ }
107
+ return parts;
108
+ }
109
+ return [];
110
+ }
111
+ function mapUIMessageToGenkit(msg) {
112
+ const role = mapRole(msg.role);
113
+ const genkitParts = msg.parts.flatMap(mapUIPartToGenkit);
114
+ return { role, content: genkitParts };
115
+ }
116
+ function mapGenkitPartToUI(part, responsesByRef) {
117
+ const metadata = part.metadata && typeof part.metadata === "object" ? part.metadata : void 0;
118
+ const withMetadata = (p) => metadata ? { ...p, metadata } : p;
119
+ if (typeof part.text === "string") {
120
+ return [withMetadata({ type: "text", text: part.text })];
121
+ }
122
+ if (typeof part.reasoning === "string") {
123
+ return part.reasoning !== "" ? [withMetadata({ type: "reasoning", text: part.reasoning })] : [];
124
+ }
125
+ if (part.media) {
126
+ return [
127
+ withMetadata({
128
+ type: "file",
129
+ url: part.media.url,
130
+ mediaType: part.media.contentType
131
+ })
132
+ ];
133
+ }
134
+ if (part.toolRequest) {
135
+ const tr = part.toolRequest;
136
+ const toolCallId = tr.ref ?? crypto.randomUUID();
137
+ const base = {
138
+ type: `tool-${tr.name}`,
139
+ toolCallId,
140
+ input: tr.input
141
+ };
142
+ if (responsesByRef?.has(toolCallId)) {
143
+ return [
144
+ withMetadata({
145
+ ...base,
146
+ state: "output-available",
147
+ output: responsesByRef.get(toolCallId)
148
+ })
149
+ ];
150
+ }
151
+ return [withMetadata({ ...base, state: "input-available" })];
152
+ }
153
+ return [];
154
+ }
155
+ function mapGenkitMessageToUI(msg, responsesByRef, id) {
156
+ const parts = (msg.content ?? []).flatMap(
157
+ (part) => mapGenkitPartToUI(part, responsesByRef)
158
+ );
159
+ return {
160
+ id: id ?? `restored-${crypto.randomUUID()}`,
161
+ role: mapGenkitRole(msg.role),
162
+ parts
163
+ };
164
+ }
165
+ function messagesFromSnapshot(messages) {
166
+ const responsesByRef = /* @__PURE__ */ new Map();
167
+ for (const msg of messages) {
168
+ for (const part of msg.content ?? []) {
169
+ if (part.toolResponse?.ref) {
170
+ responsesByRef.set(part.toolResponse.ref, part.toolResponse.output);
171
+ }
172
+ }
173
+ }
174
+ const result = [];
175
+ let index = 0;
176
+ for (const msg of messages) {
177
+ if (msg.role === "tool") continue;
178
+ const ui = mapGenkitMessageToUI(msg, responsesByRef, `restored-${index}`);
179
+ index++;
180
+ if (ui.parts.length > 0) result.push(ui);
181
+ }
182
+ return result;
183
+ }
184
+ const RESTART_MARKER = "__genkitVercelAiRestart__";
185
+ function restartInterrupt(metadata) {
186
+ return metadata === void 0 ? { [RESTART_MARKER]: true } : { [RESTART_MARKER]: true, metadata };
187
+ }
188
+ function asRestartInterrupt(output) {
189
+ if (output && typeof output === "object" && output[RESTART_MARKER] === true) {
190
+ return output;
191
+ }
192
+ return void 0;
193
+ }
194
+ function isEmptyUserMessage(msg) {
195
+ return !msg.parts.some((part) => {
196
+ if (part.type === "text") {
197
+ return typeof part.text === "string" && part.text.trim() !== "";
198
+ }
199
+ if (part.type === "file") {
200
+ return true;
201
+ }
202
+ return false;
203
+ });
204
+ }
205
+ function resolvedToolsFromMessage(msg) {
206
+ const seen = /* @__PURE__ */ new Set();
207
+ const results = [];
208
+ for (const part of msg.parts) {
209
+ const p = part;
210
+ const isTool = part.type === "dynamic-tool" || part.type.startsWith("tool-");
211
+ if (isTool && typeof p.toolCallId === "string" && p.toolCallId && !seen.has(p.toolCallId) && p.state === "output-available" && p.output !== void 0) {
212
+ const toolName = typeof p.toolName === "string" && p.toolName ? p.toolName : part.type.startsWith("tool-") ? part.type.slice("tool-".length) : "unknown";
213
+ seen.add(p.toolCallId);
214
+ results.push({
215
+ toolCallId: p.toolCallId,
216
+ toolName,
217
+ input: p.input,
218
+ result: p.output
219
+ });
220
+ }
221
+ }
222
+ return results;
223
+ }
224
+ function extractResolvedToolResults(messages) {
225
+ const seen = /* @__PURE__ */ new Set();
226
+ const results = [];
227
+ for (let i = messages.length - 1; i >= 0; i--) {
228
+ const msg = messages[i];
229
+ if (msg.role !== "assistant") continue;
230
+ for (const tr of resolvedToolsFromMessage(msg)) {
231
+ if (seen.has(tr.toolCallId)) continue;
232
+ seen.add(tr.toolCallId);
233
+ results.push(tr);
234
+ }
235
+ }
236
+ return results;
237
+ }
238
+ function currentTurnResolvedTools(messages) {
239
+ let lastAssistantIdx = -1;
240
+ for (let i = messages.length - 1; i >= 0; i--) {
241
+ if (messages[i].role === "assistant") {
242
+ lastAssistantIdx = i;
243
+ break;
244
+ }
245
+ }
246
+ if (lastAssistantIdx === -1) return [];
247
+ for (let i = lastAssistantIdx + 1; i < messages.length; i++) {
248
+ if (messages[i].role === "user" && !isEmptyUserMessage(messages[i])) {
249
+ return [];
250
+ }
251
+ }
252
+ return resolvedToolsFromMessage(messages[lastAssistantIdx]);
253
+ }
254
+ function findLastUserMessage(messages) {
255
+ for (let i = messages.length - 1; i >= 0; i--) {
256
+ if (messages[i].role === "user") return messages[i];
257
+ }
258
+ return void 0;
259
+ }
260
+ function findLastNonEmptyUserMessage(messages) {
261
+ for (let i = messages.length - 1; i >= 0; i--) {
262
+ const msg = messages[i];
263
+ if (msg.role === "user" && !isEmptyUserMessage(msg)) return msg;
264
+ }
265
+ return void 0;
266
+ }
267
+ // Annotate the CommonJS export names for ESM import in node:
268
+ 0 && (module.exports = {
269
+ asRestartInterrupt,
270
+ currentTurnResolvedTools,
271
+ extractResolvedToolResults,
272
+ findLastNonEmptyUserMessage,
273
+ findLastUserMessage,
274
+ mapGenkitMessageToUI,
275
+ mapUIMessageToGenkit,
276
+ mapUIPartToGenkit,
277
+ messagesFromSnapshot,
278
+ restartInterrupt
279
+ });
280
+ //# sourceMappingURL=mapping.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/mapping.ts"],"sourcesContent":["/**\n * Copyright 2026 Google LLC\n *\n * Licensed under the Apache License, Version 2.0 (the \"License\");\n * you may not use this file except in compliance with the License.\n * You may obtain a copy of the License at\n *\n * http://www.apache.org/licenses/LICENSE-2.0\n *\n * Unless required by applicable law or agreed to in writing, software\n * distributed under the License is distributed on an \"AS IS\" BASIS,\n * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.\n * See the License for the specific language governing permissions and\n * limitations under the License.\n */\n\nimport type { UIMessage } from 'ai';\nimport type { MessageData, Part } from 'genkit';\n\n// ---------------------------------------------------------------------------\n// UIMessage → Genkit\n// ---------------------------------------------------------------------------\n\n/**\n * Maps a Vercel AI SDK role to a Genkit role.\n */\nfunction mapRole(role: UIMessage['role']): MessageData['role'] {\n switch (role) {\n case 'assistant':\n return 'model';\n case 'user':\n return 'user';\n case 'system':\n return 'system';\n default:\n // Unknown roles fall back to 'user' so content is not silently dropped.\n return 'user';\n }\n}\n\n/**\n * Maps a Genkit role to a Vercel AI SDK role.\n */\nfunction mapGenkitRole(role: MessageData['role']): UIMessage['role'] {\n switch (role) {\n case 'model':\n return 'assistant';\n case 'system':\n return 'system';\n case 'user':\n default:\n return 'user';\n }\n}\n\n/**\n * Extracts a plain-object `metadata` value from an AI SDK UI part, if present.\n *\n * AI SDK part types don't all formally declare a `metadata` field, but the\n * transport (and middleware-aware UIs) may attach one. We read it defensively\n * so Genkit part metadata survives a UI → Genkit round-trip.\n */\nfunction extractPartMetadata(\n part: unknown\n): Record<string, unknown> | undefined {\n const md = (part as { metadata?: unknown } | null)?.metadata;\n if (md && typeof md === 'object' && !Array.isArray(md)) {\n return md as Record<string, unknown>;\n }\n return undefined;\n}\n\n/**\n * Maps a single Vercel AI SDK v6 UIMessagePart to zero or more Genkit Parts.\n *\n * Handles `text`, `file`, and tool parts (both `ToolUIPart` with\n * `type: 'tool-<name>'` and `DynamicToolUIPart` with `type: 'dynamic-tool'`).\n *\n * A tool part with state `output-available` produces both a `toolRequest`\n * and a `toolResponse` part (matching Genkit's conversation model).\n */\nexport function mapUIPartToGenkit(part: UIMessage['parts'][number]): Part[] {\n // Genkit part metadata that may have been attached to the UI part (e.g. by\n // a middleware-aware UI or a previous Genkit → UI mapping). Preserved so it\n // survives a UI → Genkit round-trip.\n const metadata = extractPartMetadata(part);\n const withMetadata = <T extends Part>(p: T): T =>\n metadata ? ({ ...p, metadata } as T) : p;\n\n if (part.type === 'text') {\n return [withMetadata({ text: part.text })];\n }\n\n if (part.type === 'reasoning') {\n // Map AI SDK reasoning parts to Genkit reasoning parts so the model's\n // thinking is preserved across turns (e.g. when replaying history).\n // Empty reasoning is dropped to avoid emitting stray, contentless\n // reasoning blocks (mirrors the streaming side in client.ts, which also\n // skips `reasoning === ''`).\n const reasoning = (part as { text?: unknown }).text;\n return typeof reasoning === 'string' && reasoning !== ''\n ? [withMetadata({ reasoning })]\n : [];\n }\n\n if (part.type === 'file') {\n return [\n withMetadata({\n media: {\n url: part.url,\n contentType: part.mediaType,\n },\n }),\n ];\n }\n\n // Handle v6 tool parts:\n // ToolUIPart: type: 'tool-<name>', toolCallId, state, input?, output?\n // DynamicToolUIPart: type: 'dynamic-tool', toolName, toolCallId, state, input?, output?\n //\n // Use runtime property checks via Record cast since TypeScript can't\n // narrow on prefix match for template literal types.\n if (part.type === 'dynamic-tool' || part.type.startsWith('tool-')) {\n const p = part as unknown as Record<string, unknown>;\n const toolCallId =\n typeof p.toolCallId === 'string' ? p.toolCallId : crypto.randomUUID();\n const toolName =\n typeof p.toolName === 'string' && p.toolName\n ? p.toolName\n : part.type.startsWith('tool-')\n ? part.type.slice('tool-'.length)\n : 'unknown';\n\n const parts: Part[] = [\n withMetadata({\n toolRequest: {\n ref: toolCallId,\n name: toolName,\n input: p.input,\n },\n }),\n ];\n\n if (p.state === 'output-available' && p.output !== undefined) {\n parts.push(\n withMetadata({\n toolResponse: {\n ref: toolCallId,\n name: toolName,\n output: p.output,\n },\n })\n );\n }\n\n return parts;\n }\n\n // Unsupported part types (source-*, step-start, data-*) are silently\n // skipped — they don't have a direct Genkit equivalent.\n return [];\n}\n\n/**\n * Maps a Vercel AI SDK UIMessage to a Genkit MessageData.\n */\nexport function mapUIMessageToGenkit(msg: UIMessage): MessageData {\n const role = mapRole(msg.role);\n const genkitParts: Part[] = msg.parts.flatMap(mapUIPartToGenkit);\n return { role, content: genkitParts };\n}\n\n// ---------------------------------------------------------------------------\n// Genkit → UIMessage (session restore)\n// ---------------------------------------------------------------------------\n\n/**\n * Maps a single Genkit {@link Part} to zero or more AI SDK UI parts.\n *\n * `toolResponse` outputs live in separate (`tool`-role) Genkit messages, so a\n * `toolRequest` is paired with its response via `responsesByRef` to produce a\n * single AI SDK tool part: `output-available` when the response is known, or\n * `input-available` when it is still pending (e.g. an unresolved interrupt).\n *\n * Standalone `toolResponse` parts are skipped here — they're merged into the\n * matching request's tool part instead of producing a separate one.\n */\nfunction mapGenkitPartToUI(\n part: Part,\n responsesByRef?: Map<string, unknown>\n): UIMessage['parts'] {\n // Preserve Genkit part metadata on the UI part so it survives a\n // Genkit → UI → Genkit round-trip (read back by extractPartMetadata).\n const metadata =\n part.metadata && typeof part.metadata === 'object'\n ? (part.metadata as Record<string, unknown>)\n : undefined;\n const withMetadata = (\n p: Record<string, unknown>\n ): UIMessage['parts'][number] =>\n (metadata ? { ...p, metadata } : p) as UIMessage['parts'][number];\n\n if (typeof part.text === 'string') {\n return [withMetadata({ type: 'text', text: part.text })];\n }\n\n if (typeof part.reasoning === 'string') {\n return part.reasoning !== ''\n ? [withMetadata({ type: 'reasoning', text: part.reasoning })]\n : [];\n }\n\n if (part.media) {\n return [\n withMetadata({\n type: 'file',\n url: part.media.url,\n mediaType: part.media.contentType,\n }),\n ];\n }\n\n if (part.toolRequest) {\n const tr = part.toolRequest;\n const toolCallId = tr.ref ?? crypto.randomUUID();\n const base: Record<string, unknown> = {\n type: `tool-${tr.name}`,\n toolCallId,\n input: tr.input,\n };\n if (responsesByRef?.has(toolCallId)) {\n return [\n withMetadata({\n ...base,\n state: 'output-available',\n output: responsesByRef.get(toolCallId),\n }),\n ];\n }\n return [withMetadata({ ...base, state: 'input-available' })];\n }\n\n // Standalone toolResponse (paired with a request above) or any other part\n // type without a direct UI equivalent is skipped.\n return [];\n}\n\n/**\n * Maps a single Genkit {@link MessageData} to an AI SDK {@link UIMessage}.\n *\n * Pass `responsesByRef` (built across the whole conversation) so each\n * `toolRequest` can be paired with the `toolResponse` that resolved it, even\n * though they live in different messages. When omitted, all tool parts are\n * emitted in the `input-available` state.\n */\nexport function mapGenkitMessageToUI(\n msg: MessageData,\n responsesByRef?: Map<string, unknown>,\n id?: string\n): UIMessage {\n const parts = (msg.content ?? []).flatMap((part) =>\n mapGenkitPartToUI(part, responsesByRef)\n );\n return {\n id: id ?? `restored-${crypto.randomUUID()}`,\n role: mapGenkitRole(msg.role),\n parts,\n };\n}\n\n/**\n * Converts the `messages` of a Genkit `SessionSnapshot` (an array of\n * {@link MessageData}) into AI SDK {@link UIMessage}s suitable for seeding\n * `useChat` (via its `messages` option or `setMessages`) to rehydrate a\n * previous conversation after a reload.\n *\n * Tool requests and their responses (which Genkit stores in separate\n * `model`/`tool` messages) are merged into single AI SDK tool parts, so the\n * UI renders each tool call as one element with both its input and output.\n * `tool`-role messages are therefore omitted from the result. Unresolved tool\n * requests (e.g. a pending interrupt) are emitted in the `input-available`\n * state.\n *\n * Conversation state is server-managed by `sessionId` (the `useChat` `id`), so\n * to resume the *next* turn you only need to reuse the same chat `id` — there\n * is no snapshot to restore on the client.\n *\n * @example\n * ```ts\n * const snapshot = await runFlow({ url: '/api/chat/weather/state', input: sessionId });\n * const messages = messagesFromSnapshot(snapshot.state.messages);\n * // pass `messages` to useChat({ id: sessionId, messages })\n * ```\n */\nexport function messagesFromSnapshot(messages: MessageData[]): UIMessage[] {\n // Collect every tool response by ref so a request in one message can be\n // paired with its response in another (Genkit stores them separately).\n const responsesByRef = new Map<string, unknown>();\n for (const msg of messages) {\n for (const part of msg.content ?? []) {\n if (part.toolResponse?.ref) {\n responsesByRef.set(part.toolResponse.ref, part.toolResponse.output);\n }\n }\n }\n\n const result: UIMessage[] = [];\n let index = 0;\n for (const msg of messages) {\n // tool-role messages only carry responses, which are merged into the\n // matching request's tool part — skip them.\n if (msg.role === 'tool') continue;\n const ui = mapGenkitMessageToUI(msg, responsesByRef, `restored-${index}`);\n index++;\n // Drop messages that produced no renderable parts.\n if (ui.parts.length > 0) result.push(ui);\n }\n return result;\n}\n\n// ---------------------------------------------------------------------------\n// Interrupt restart support\n// ---------------------------------------------------------------------------\n\n/**\n * Marker placed on a tool output to signal that the user chose to *restart*\n * (re-run) an interrupted tool rather than supply a final response.\n *\n * `Symbol.for` is intentionally avoided because tool outputs are serialized to\n * JSON before reaching the transport; a string marker survives that round-trip.\n */\nconst RESTART_MARKER = '__genkitVercelAiRestart__';\n\n/**\n * The serialized shape produced by {@link restartInterrupt}.\n */\nexport interface RestartInterruptOutput {\n [RESTART_MARKER]: true;\n /**\n * Optional metadata surfaced to the tool as its `resumed` argument when it\n * re-runs server-side. Defaults to `true` when omitted.\n */\n metadata?: unknown;\n}\n\n/**\n * Builds a tool output that instructs the Genkit agent to **restart** (re-run)\n * an interrupted tool instead of accepting a user-supplied response.\n *\n * Pass the result to the AI SDK's `addToolResult` as the tool `output`:\n *\n * ```tsx\n * addToolResult({\n * tool: 'userApproval',\n * toolCallId,\n * output: restartInterrupt({ note: 'user revised the request' }),\n * });\n * ```\n *\n * The transport detects this marker and emits a `resume.restart` entry (with\n * the original tool input, which the server requires to match exactly) instead\n * of a `resume.respond` entry. Any `metadata` is passed through to the tool as\n * its `resumed` value.\n */\nexport function restartInterrupt(metadata?: unknown): RestartInterruptOutput {\n return metadata === undefined\n ? { [RESTART_MARKER]: true }\n : { [RESTART_MARKER]: true, metadata };\n}\n\n/**\n * Returns the restart payload if `output` was produced by\n * {@link restartInterrupt}, otherwise `undefined`.\n */\nexport function asRestartInterrupt(\n output: unknown\n): RestartInterruptOutput | undefined {\n if (\n output &&\n typeof output === 'object' &&\n (output as Record<string, unknown>)[RESTART_MARKER] === true\n ) {\n return output as RestartInterruptOutput;\n }\n return undefined;\n}\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\n/**\n * A resolved tool invocation extracted from the frontend message history.\n */\nexport interface ResolvedToolResult {\n toolCallId: string;\n toolName: string;\n /** The original tool input (needed to build a `resume.restart`). */\n input: unknown;\n /** The user-supplied output (or a {@link restartInterrupt} marker). */\n result: unknown;\n}\n\n/**\n * Returns true if a user message carries no meaningful content — i.e. it has\n * no non-empty text and no file parts. The AI SDK's `sendMessage({ text: '' })`\n * (used to nudge a resume) produces such a \"phantom\" user message, which must\n * not be treated as a real user turn.\n */\nfunction isEmptyUserMessage(msg: UIMessage): boolean {\n return !msg.parts.some((part) => {\n if (part.type === 'text') {\n return typeof part.text === 'string' && part.text.trim() !== '';\n }\n if (part.type === 'file') {\n return true;\n }\n return false;\n });\n}\n\n/**\n * Extracts the resolved tool invocations carried by a single UIMessage.\n *\n * Supports the v6 per-tool format (`type === 'tool-<toolName>'`) as well as\n * dynamic tools (`type === 'dynamic-tool'`, with an explicit `toolName`),\n * matching `state === 'output-available'`. De-duplicates by `toolCallId`.\n */\nfunction resolvedToolsFromMessage(msg: UIMessage): ResolvedToolResult[] {\n const seen = new Set<string>();\n const results: ResolvedToolResult[] = [];\n for (const part of msg.parts) {\n const p = part as unknown as Record<string, unknown>;\n const isTool =\n part.type === 'dynamic-tool' || part.type.startsWith('tool-');\n if (\n isTool &&\n typeof p.toolCallId === 'string' &&\n p.toolCallId &&\n !seen.has(p.toolCallId) &&\n p.state === 'output-available' &&\n p.output !== undefined\n ) {\n const toolName =\n typeof p.toolName === 'string' && p.toolName\n ? p.toolName\n : part.type.startsWith('tool-')\n ? part.type.slice('tool-'.length)\n : 'unknown';\n seen.add(p.toolCallId);\n results.push({\n toolCallId: p.toolCallId,\n toolName,\n input: p.input,\n result: p.output,\n });\n }\n }\n return results;\n}\n\n/**\n * Extracts resolved tool invocations from a UIMessage array.\n *\n * Supports the v6 per-tool format (`type === 'tool-<toolName>'`) as well as\n * dynamic tools (`type === 'dynamic-tool'`, with an explicit `toolName`),\n * matching `state === 'output-available'`.\n *\n * Scans **all** messages (most-recent first) and returns every resolved tool\n * result, de-duplicated by `toolCallId` (keeping the most recent).\n *\n * Prefer {@link currentTurnResolvedTools} for interrupt-resume detection — it\n * scopes the scan to the most recent turn so prior turns' (already-completed)\n * tool calls are not re-sent as a resume payload.\n */\nexport function extractResolvedToolResults(\n messages: UIMessage[]\n): ResolvedToolResult[] {\n const seen = new Set<string>();\n const results: ResolvedToolResult[] = [];\n\n // Walk backwards so the most recent resolution for a given toolCallId wins.\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i];\n if (msg.role !== 'assistant') continue;\n for (const tr of resolvedToolsFromMessage(msg)) {\n if (seen.has(tr.toolCallId)) continue;\n seen.add(tr.toolCallId);\n results.push(tr);\n }\n }\n\n return results;\n}\n\n/**\n * Returns the resolved tool invocations that constitute an interrupt resume\n * for the *current* turn, or an empty array if this is a fresh user turn.\n *\n * An interrupt resume is identified by the last assistant message carrying\n * resolved tool outputs (e.g. supplied via `addToolResult`) with no *non-empty*\n * user message after it. A real user message following the assistant turn means\n * the user started a new turn, so no resume is performed.\n *\n * This deliberately scopes detection to the most recent turn: tool calls from\n * earlier turns are already part of the server-side session state and must not\n * be replayed as a resume payload.\n */\nexport function currentTurnResolvedTools(\n messages: UIMessage[]\n): ResolvedToolResult[] {\n // Locate the most recent assistant message.\n let lastAssistantIdx = -1;\n for (let i = messages.length - 1; i >= 0; i--) {\n if (messages[i].role === 'assistant') {\n lastAssistantIdx = i;\n break;\n }\n }\n if (lastAssistantIdx === -1) return [];\n\n // A non-empty user message after the assistant turn means a fresh user turn.\n for (let i = lastAssistantIdx + 1; i < messages.length; i++) {\n if (messages[i].role === 'user' && !isEmptyUserMessage(messages[i])) {\n return [];\n }\n }\n\n return resolvedToolsFromMessage(messages[lastAssistantIdx]);\n}\n\n/**\n * Finds the last user message in a UIMessage array.\n */\nexport function findLastUserMessage(\n messages: UIMessage[]\n): UIMessage | undefined {\n for (let i = messages.length - 1; i >= 0; i--) {\n if (messages[i].role === 'user') return messages[i];\n }\n return undefined;\n}\n\n/**\n * Finds the last *non-empty* user message in a UIMessage array, skipping\n * phantom empty messages produced by `sendMessage({ text: '' })`.\n */\nexport function findLastNonEmptyUserMessage(\n messages: UIMessage[]\n): UIMessage | undefined {\n for (let i = messages.length - 1; i >= 0; i--) {\n const msg = messages[i];\n if (msg.role === 'user' && !isEmptyUserMessage(msg)) return msg;\n }\n return undefined;\n}\n"],"mappings":"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","names":[]}