@convex-dev/agent 0.6.0-alpha.0 → 0.6.0-beta.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.
- package/MIGRATION.md +153 -0
- package/dist/UIMessages.d.ts.map +1 -1
- package/dist/UIMessages.js +88 -0
- package/dist/UIMessages.js.map +1 -1
- package/dist/client/createTool.d.ts +18 -21
- package/dist/client/createTool.d.ts.map +1 -1
- package/dist/client/createTool.js +3 -2
- package/dist/client/createTool.js.map +1 -1
- package/dist/client/definePlaygroundAPI.d.ts +31 -31
- package/dist/client/index.d.ts +70 -23
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +126 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/messages.d.ts +9 -9
- package/dist/client/mockModel.d.ts.map +1 -1
- package/dist/client/mockModel.js +9 -2
- package/dist/client/mockModel.js.map +1 -1
- package/dist/client/search.d.ts +9 -9
- package/dist/client/search.d.ts.map +1 -1
- package/dist/client/search.js +12 -2
- package/dist/client/search.js.map +1 -1
- package/dist/client/start.d.ts +1 -1
- package/dist/client/start.d.ts.map +1 -1
- package/dist/client/start.js +29 -15
- package/dist/client/start.js.map +1 -1
- package/dist/client/streamText.d.ts.map +1 -1
- package/dist/client/streamText.js +37 -3
- package/dist/client/streamText.js.map +1 -1
- package/dist/client/streaming.d.ts +78 -67
- package/dist/client/streaming.d.ts.map +1 -1
- package/dist/client/streaming.js +64 -28
- package/dist/client/streaming.js.map +1 -1
- package/dist/client/types.d.ts +13 -12
- package/dist/client/types.d.ts.map +1 -1
- package/dist/component/_generated/component.d.ts +1 -0
- package/dist/component/_generated/component.d.ts.map +1 -1
- package/dist/component/messages.d.ts +107 -106
- package/dist/component/messages.d.ts.map +1 -1
- package/dist/component/messages.js +13 -3
- package/dist/component/messages.js.map +1 -1
- package/dist/component/schema.d.ts +40 -40
- package/dist/component/streams.d.ts +4 -4
- package/dist/component/threads.d.ts +17 -17
- package/dist/component/users.d.ts +3 -3
- package/dist/component/vector/index.d.ts +1 -1
- package/dist/deltas.d.ts.map +1 -1
- package/dist/deltas.js +0 -1
- package/dist/deltas.js.map +1 -1
- package/dist/mapping.d.ts +22 -0
- package/dist/mapping.d.ts.map +1 -1
- package/dist/mapping.js +99 -5
- package/dist/mapping.js.map +1 -1
- package/dist/react/useDeltaStreams.d.ts.map +1 -1
- package/dist/react/useDeltaStreams.js +5 -0
- package/dist/react/useDeltaStreams.js.map +1 -1
- package/dist/validators.d.ts +13 -13
- package/package.json +4 -2
- package/src/UIMessages.ts +126 -0
- package/src/client/approval.test.ts +494 -0
- package/src/client/createTool.ts +50 -52
- package/src/client/index.ts +170 -1
- package/src/client/mockModel.ts +9 -2
- package/src/client/search.test.ts +4 -5
- package/src/client/search.ts +18 -2
- package/src/client/start.ts +42 -25
- package/src/client/streamText.ts +36 -3
- package/src/client/streaming.integration.test.ts +1206 -0
- package/src/client/streaming.ts +67 -31
- package/src/client/types.ts +14 -12
- package/src/component/_generated/component.ts +53 -64
- package/src/component/messages.ts +12 -2
- package/src/deltas.ts +0 -1
- package/src/mapping.test.ts +143 -1
- package/src/mapping.ts +131 -6
- package/src/react/useDeltaStreams.ts +6 -0
package/src/mapping.ts
CHANGED
|
@@ -139,6 +139,97 @@ export function docsToModelMessages(messages: MessageDoc[]): ModelMessage[] {
|
|
|
139
139
|
.map(toModelMessage);
|
|
140
140
|
}
|
|
141
141
|
|
|
142
|
+
/**
|
|
143
|
+
* Scan messages for unresolved `tool-approval-request` parts and inject
|
|
144
|
+
* synthetic `tool-approval-response` denials so that the AI SDK receives
|
|
145
|
+
* a complete history (every tool-call has a corresponding result or denial).
|
|
146
|
+
*
|
|
147
|
+
* This handles the case where a user sends a new message instead of
|
|
148
|
+
* resolving pending approvals — the old approvals are auto-denied rather
|
|
149
|
+
* than silently dropped.
|
|
150
|
+
*/
|
|
151
|
+
export function autoDenyUnresolvedApprovals(
|
|
152
|
+
messages: ModelMessage[],
|
|
153
|
+
): ModelMessage[] {
|
|
154
|
+
// Collect all approval requests: approvalId → { toolCallId, messageIndex }
|
|
155
|
+
const requests = new Map<
|
|
156
|
+
string,
|
|
157
|
+
{ toolCallId: string; messageIndex: number }
|
|
158
|
+
>();
|
|
159
|
+
// Collect all resolved approval IDs
|
|
160
|
+
const resolvedIds = new Set<string>();
|
|
161
|
+
|
|
162
|
+
for (let i = 0; i < messages.length; i++) {
|
|
163
|
+
const msg = messages[i];
|
|
164
|
+
if (!Array.isArray(msg.content)) continue;
|
|
165
|
+
for (const part of msg.content as any[]) {
|
|
166
|
+
if (part.type === "tool-approval-request") {
|
|
167
|
+
requests.set(part.approvalId, {
|
|
168
|
+
toolCallId: part.toolCallId,
|
|
169
|
+
messageIndex: i,
|
|
170
|
+
});
|
|
171
|
+
} else if (part.type === "tool-approval-response") {
|
|
172
|
+
resolvedIds.add(part.approvalId);
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
// Find unresolved approvals
|
|
178
|
+
const unresolved: Array<{
|
|
179
|
+
approvalId: string;
|
|
180
|
+
toolCallId: string;
|
|
181
|
+
messageIndex: number;
|
|
182
|
+
}> = [];
|
|
183
|
+
for (const [approvalId, info] of requests) {
|
|
184
|
+
if (!resolvedIds.has(approvalId)) {
|
|
185
|
+
unresolved.push({ approvalId, ...info });
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (unresolved.length === 0) {
|
|
190
|
+
return messages;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
// Group unresolved approvals by the assistant message index they came from
|
|
194
|
+
const byMessageIndex = new Map<
|
|
195
|
+
number,
|
|
196
|
+
Array<{ approvalId: string; toolCallId: string }>
|
|
197
|
+
>();
|
|
198
|
+
for (const entry of unresolved) {
|
|
199
|
+
console.warn(
|
|
200
|
+
`Auto-denying unresolved tool approval ${entry.approvalId} ` +
|
|
201
|
+
`(toolCallId: ${entry.toolCallId}): new generation started`,
|
|
202
|
+
);
|
|
203
|
+
let group = byMessageIndex.get(entry.messageIndex);
|
|
204
|
+
if (!group) {
|
|
205
|
+
group = [];
|
|
206
|
+
byMessageIndex.set(entry.messageIndex, group);
|
|
207
|
+
}
|
|
208
|
+
group.push(entry);
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
// Build result by inserting synthetic denial messages after each relevant
|
|
212
|
+
// assistant message
|
|
213
|
+
const result: ModelMessage[] = [];
|
|
214
|
+
for (let i = 0; i < messages.length; i++) {
|
|
215
|
+
result.push(messages[i]);
|
|
216
|
+
const group = byMessageIndex.get(i);
|
|
217
|
+
if (group) {
|
|
218
|
+
result.push({
|
|
219
|
+
role: "tool",
|
|
220
|
+
content: group.map((entry) => ({
|
|
221
|
+
type: "tool-approval-response" as const,
|
|
222
|
+
approvalId: entry.approvalId,
|
|
223
|
+
approved: false,
|
|
224
|
+
reason: "auto-denied: new generation started",
|
|
225
|
+
})),
|
|
226
|
+
});
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
return result;
|
|
231
|
+
}
|
|
232
|
+
|
|
142
233
|
export function serializeUsage(usage: LanguageModelUsage): Usage {
|
|
143
234
|
return {
|
|
144
235
|
promptTokens: usage.inputTokens ?? 0,
|
|
@@ -206,11 +297,49 @@ export function toModelMessageWarnings(
|
|
|
206
297
|
}) as any;
|
|
207
298
|
}
|
|
208
299
|
|
|
300
|
+
/**
|
|
301
|
+
* Serialize explicitly provided response messages for a step.
|
|
302
|
+
* Used by the streaming/generation loop where the caller tracks which
|
|
303
|
+
* messages are new via slicing.
|
|
304
|
+
*/
|
|
305
|
+
export async function serializeResponseMessages<TOOLS extends ToolSet>(
|
|
306
|
+
ctx: ActionCtx,
|
|
307
|
+
component: AgentComponent,
|
|
308
|
+
step: StepResult<TOOLS>,
|
|
309
|
+
model: ModelOrMetadata | undefined,
|
|
310
|
+
responseMessages: ModelMessage[],
|
|
311
|
+
): Promise<{ messages: MessageWithMetadata[] }> {
|
|
312
|
+
return serializeStepMessages(ctx, component, step, model, responseMessages);
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
/**
|
|
316
|
+
* Serialize the new messages from a step using a heuristic to determine
|
|
317
|
+
* which response messages are new (last 1-2 messages).
|
|
318
|
+
*/
|
|
209
319
|
export async function serializeNewMessagesInStep<TOOLS extends ToolSet>(
|
|
210
320
|
ctx: ActionCtx,
|
|
211
321
|
component: AgentComponent,
|
|
212
322
|
step: StepResult<TOOLS>,
|
|
213
323
|
model: ModelOrMetadata | undefined,
|
|
324
|
+
): Promise<{ messages: MessageWithMetadata[] }> {
|
|
325
|
+
const hasToolMessage = step.response.messages.at(-1)?.role === "tool";
|
|
326
|
+
let messagesToSerialize: ModelMessage[];
|
|
327
|
+
if (hasToolMessage) {
|
|
328
|
+
messagesToSerialize = step.response.messages.slice(-2);
|
|
329
|
+
} else if (step.content.length) {
|
|
330
|
+
messagesToSerialize = step.response.messages.slice(-1);
|
|
331
|
+
} else {
|
|
332
|
+
messagesToSerialize = [{ role: "assistant" as const, content: [] }];
|
|
333
|
+
}
|
|
334
|
+
return serializeStepMessages(ctx, component, step, model, messagesToSerialize);
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
async function serializeStepMessages<TOOLS extends ToolSet>(
|
|
338
|
+
ctx: ActionCtx,
|
|
339
|
+
component: AgentComponent,
|
|
340
|
+
step: StepResult<TOOLS>,
|
|
341
|
+
model: ModelOrMetadata | undefined,
|
|
342
|
+
messagesToSerialize: ModelMessage[],
|
|
214
343
|
): Promise<{ messages: MessageWithMetadata[] }> {
|
|
215
344
|
// If there are tool results, there's another message with the tool results
|
|
216
345
|
// ref: https://github.com/vercel/ai/blob/main/packages/ai/src/generate-text/to-response-messages.ts#L120
|
|
@@ -228,13 +357,9 @@ export async function serializeNewMessagesInStep<TOOLS extends ToolSet>(
|
|
|
228
357
|
sources: hasToolMessage ? undefined : step.sources,
|
|
229
358
|
} satisfies Omit<MessageWithMetadata, "message" | "text" | "fileIds">;
|
|
230
359
|
const toolFields = { sources: step.sources };
|
|
360
|
+
|
|
231
361
|
const messages: MessageWithMetadata[] = await Promise.all(
|
|
232
|
-
(
|
|
233
|
-
? step.response.messages.slice(-2)
|
|
234
|
-
: step.content.length
|
|
235
|
-
? step.response.messages.slice(-1)
|
|
236
|
-
: [{ role: "assistant" as const, content: [] }]
|
|
237
|
-
).map(async (msg): Promise<MessageWithMetadata> => {
|
|
362
|
+
messagesToSerialize.map(async (msg): Promise<MessageWithMetadata> => {
|
|
238
363
|
const { message, fileIds } = await serializeMessage(ctx, component, msg);
|
|
239
364
|
return parse(vMessageWithMetadata, {
|
|
240
365
|
message,
|
|
@@ -83,6 +83,12 @@ export function useDeltaStreams<
|
|
|
83
83
|
),
|
|
84
84
|
);
|
|
85
85
|
|
|
86
|
+
// When no active streams remain, clear the stale state so we stop
|
|
87
|
+
// returning old streaming UIMessages.
|
|
88
|
+
if (streamMessages !== undefined && streamMessages.length === 0) {
|
|
89
|
+
state.deltaStreams = undefined;
|
|
90
|
+
}
|
|
91
|
+
|
|
86
92
|
// Get the deltas for all the active streams, if any.
|
|
87
93
|
const cursorQuery = useQuery(
|
|
88
94
|
query,
|