diff --git a/CHANGELOG.md b/CHANGELOG.md index 7a5002a1..dadcfcd6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -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 @@ -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 diff --git a/package-lock.json b/package-lock.json index cab349f1..cc4c0300 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@convex-dev/agent", - "version": "0.6.0", + "version": "0.6.0-beta.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@convex-dev/agent", - "version": "0.6.0", + "version": "0.6.0-beta.0", "license": "Apache-2.0", "devDependencies": { "@ai-sdk/anthropic": "^3.0.13", diff --git a/package.json b/package.json index 38423de3..14d56230 100644 --- a/package.json +++ b/package.json @@ -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", diff --git a/src/client/index.ts b/src/client/index.ts index 994c9a0f..874b5648 100644 --- a/src/client/index.ts +++ b/src/client/index.ts @@ -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. @@ -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}`, ); } diff --git a/src/client/streaming.ts b/src/client/streaming.ts index d274cb3c..c3146b43 100644 --- a/src/client/streaming.ts +++ b/src/client/streaming.ts @@ -260,8 +260,9 @@ export class DeltaStreamer { 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. } }); } @@ -301,7 +302,9 @@ export class DeltaStreamer { 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(); } @@ -384,10 +387,7 @@ export class DeltaStreamer { 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; }