Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 16 additions & 7 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,16 +1,23 @@
# Changelog

## 0.6.0-beta.0

## 0.6.0

- Tool approval support: `needsApproval` in `createTool`, `agent.approveToolCall()`, `agent.denyToolCall()`
- Tool approval support: `needsApproval` in `createTool`,
`agent.approveToolCall()`, `agent.denyToolCall()`
- Auto-deny unresolved tool approvals when a new generation starts
- Fix unhandled rejection in DeltaStreamer on transaction teardown

**Note on versioning:** This release jumps from 0.3.2 to 0.6.0, skipping versions 0.4.0 and 0.5.0. This is intentional and aligns the `@convex-dev/agent` package version with the AI SDK v6 major version for clearer compatibility signaling. Going forward, the minor version of this package will track the AI SDK major version to make it easier for developers to identify which version of the AI SDK is supported.
**Note on versioning:** This release jumps from 0.3.2 to 0.6.0, skipping
versions 0.4.0 and 0.5.0. This is intentional and aligns the `@convex-dev/agent`
package version with the AI SDK v6 major version for clearer compatibility
signaling. Going forward, the minor version of this package will track the AI
SDK major version to make it easier for developers to identify which version of
the AI SDK is supported.

- Breaking: Requires AI SDK v6 and drops support for AI SDK v5. Projects pinned
to v5 must upgrade their AI SDK dependencies before updating to this
version.
to v5 must upgrade their AI SDK dependencies before updating to this version.
- Aligns this package's message and tool invocation types with the AI SDK v6
APIs to reduce casting/adapter code when integrating with the core SDK.
- Updates internal helpers to use the AI SDK v6 request/response shapes and
Expand All @@ -20,9 +27,11 @@
dependencies.
- Rebuild and run your type checker to surface any call sites that depend on
the old AI SDK v5 types or message shapes.
- Review any custom integrations that relied on deprecated v5-only helpers
and update them to the new AI SDK v6-compatible APIs.
- See Vercel's [v6 migration guide](https://ai-sdk.dev/docs/migration-guides/migration-guide-6-0) for details on AI SDK changes.
- Review any custom integrations that relied on deprecated v5-only helpers and
update them to the new AI SDK v6-compatible APIs.
- See Vercel's
[v6 migration guide](https://ai-sdk.dev/docs/migration-guides/migration-guide-6-0)
for details on AI SDK changes.

## 0.3.2

Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"email": "support@convex.dev",
"url": "https://github.com/get-convex/agent/issues"
},
"version": "0.6.0",
"version": "0.6.0-beta.0",
"license": "Apache-2.0",
"keywords": [
"convex",
Expand Down
26 changes: 8 additions & 18 deletions src/client/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1123,24 +1123,15 @@ export class Agent<
// The "already handled" check (tool-approval-response) relies on seeing
// responses before their corresponding requests. If the pagination order
// changes, this logic will need to be updated.
let cursor: string | null = null;
let existingResponseMessage: MessageDoc | undefined;
// Limit the search to the most recent messages. Approvals should always
// be near the end of the thread — scanning further is wasteful.
const MAX_MESSAGES_TO_SCAN = 200;
let scanned = 0;
do {
const page = await this.listMessages(ctx, {
threadId: args.threadId,
paginationOpts: { cursor, numItems: 100 },
});
// be near the end of the thread.
const page = await this.listMessages(ctx, {
threadId: args.threadId,
paginationOpts: { cursor: null, numItems: 100 },
});
{
for (const message of page.page) {
scanned++;
if (scanned > MAX_MESSAGES_TO_SCAN) {
throw new Error(
`Approval ${args.approvalId} not found in the last ${MAX_MESSAGES_TO_SCAN} messages`,
);
}
const content = message.message?.content;
if (!Array.isArray(content)) continue;
// Check if this assistant message starts a different approval step.
Expand Down Expand Up @@ -1187,11 +1178,10 @@ export class Agent<
}
}
}
cursor = page.isDone ? null : page.continueCursor;
} while (cursor !== null);
}

throw new Error(
`Approval request ${args.approvalId} was not found in thread ${args.threadId}`,
`Approval request ${args.approvalId} was not found in the last 100 messages of thread ${args.threadId}`,
);
}

Expand Down
14 changes: 7 additions & 7 deletions src/client/streaming.ts
Original file line number Diff line number Diff line change
Expand Up @@ -260,8 +260,9 @@ export class DeltaStreamer<T> {
reason: "abortSignal",
});
}
} catch (e) {
console.error("Error during stream abort cleanup:", e);
} catch {
// Best-effort cleanup — the stream will be garbage-collected
// by the 10-minute timeout if this fails.
}
});
}
Expand Down Expand Up @@ -301,7 +302,9 @@ export class DeltaStreamer<T> {
await this.addParts([chunk]);
}
// Skip finish if it will be handled externally (atomically with message save)
// or if the stream was aborted (e.g., due to a failed delta write)
// or if the stream was aborted (e.g., due to a failed delta write).
// Aborted streams are cleaned up via streams.abort (called by the abort
// signal handler), so we don't need to call finish() for them.
if (!this.#finishedExternally && !this.abortController.signal.aborted) {
await this.finish();
}
Expand Down Expand Up @@ -384,10 +387,7 @@ export class DeltaStreamer<T> {
return;
}
await this.#ongoingWrite;
if (this.abortController.signal.aborted) {
return;
}
await this.#sendDelta();
await this.#sendDelta(); // #sendDelta checks aborted internally
if (this.abortController.signal.aborted) {
return;
}
Expand Down
Loading