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
63 changes: 63 additions & 0 deletions TYPE_FIX_SUMMARY.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
# AI SDK v6 Type Error Fix Summary

## Problem
The build fails with TypeScript errors after upgrading to AI SDK v6. The main issues are:
1. `ToolCallPart` type now requires `input` field (not optional), but stored data may only have deprecated `args` field
2. Tool-result output types missing newer types like `execution-denied` and extended content types
3. Generated component types out of sync with updated validators

## Changes Made

### 1. Fixed `tool-call` Part Handling (src/mapping.ts)
- Updated `toModelMessageContent()` to ensure `input` is always present by falling back to `args` or `{}`
- Updated `serializeContent()` and `fromModelMessageContent()` to handle both `input` and legacy `args` fields
- This fixes the core issue where AI SDK v6's `ToolCallPart` expects non-nullable `input`

### 2. Fixed Tool Approval Response Handling (src/client/search.ts)
- Updated `filterOutOrphanedToolMessages()` to handle tool-approval-response parts that don't have `toolCallId`
- Tool-approval-response only has `approvalId`, not `toolCallId`

### 3. Updated Generated Component Types (src/component/_generated/component.ts)
Made manual updates to sync with validators (normally done via `convex codegen`):
- Added `input: any` field to all tool-call type definitions
- Made `args` optional (`args?: any`) in tool-call types
- Added `execution-denied` output type to tool-result
- Added extended content types: `file-data`, `file-url`, `file-id`, `image-data`, `image-url`, `image-file-id`, `custom`
- Added `providerOptions` to text types in content values

## Remaining Issues (5 TypeScript errors)

The remaining errors are due to a structural mismatch in the generated component types:
- Generated types have BOTH `experimental_content` (deprecated) and `output` (new) fields on tool-result
- Our validators only define `output`, not `experimental_content`
- TypeScript is comparing our new output types against the old experimental_content types
- This cannot be fixed manually - requires proper component regeneration

### To Complete the Fix:
1. Run `convex codegen --component-dir ./src/component` with a valid Convex deployment
2. This will regenerate `src/component/_generated/component.ts` from the validators
3. The regenerated types will:
- Remove the deprecated `experimental_content` field
- Use only the `output` field with correct types
- Properly match the validator definitions

### Error Locations:
- `src/client/index.ts:1052` - addMessages call
- `src/client/index.ts:1103` - addMessages call
- `src/client/index.ts:1169` - updateMessage call
- `src/client/messages.ts:141` - addMessages call
- `src/client/start.ts:265` - addMessages call

All errors have the same root cause: content value types in tool-result output don't match experimental_content expectations.

## Testing Plan
Once component types are regenerated:
1. Run `npm run build` - should complete without errors
2. Run `npm test` - ensure no regressions
3. Test with actual AI SDK v6 workflow - verify tool-call handling works with both new `input` and legacy `args` fields

## Notes
- The mapping functions in `src/mapping.ts` correctly handle both old and new formats
- Data with only `args` will be converted to have `input` (with `args` as fallback)
- Data with `input` will work directly
- This provides backward compatibility while supporting AI SDK v6's requirements
Comment on lines +1 to +63
Copy link

Copilot AI Jan 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This documentation file provides excellent context about the migration work and remaining issues. However, it should not be committed to the main codebase as it's implementation-specific documentation. Consider moving this content to the PR description, a GitHub issue, or a separate documentation folder for migration notes.

Suggested change
# AI SDK v6 Type Error Fix Summary
## Problem
The build fails with TypeScript errors after upgrading to AI SDK v6. The main issues are:
1. `ToolCallPart` type now requires `input` field (not optional), but stored data may only have deprecated `args` field
2. Tool-result output types missing newer types like `execution-denied` and extended content types
3. Generated component types out of sync with updated validators
## Changes Made
### 1. Fixed `tool-call` Part Handling (src/mapping.ts)
- Updated `toModelMessageContent()` to ensure `input` is always present by falling back to `args` or `{}`
- Updated `serializeContent()` and `fromModelMessageContent()` to handle both `input` and legacy `args` fields
- This fixes the core issue where AI SDK v6's `ToolCallPart` expects non-nullable `input`
### 2. Fixed Tool Approval Response Handling (src/client/search.ts)
- Updated `filterOutOrphanedToolMessages()` to handle tool-approval-response parts that don't have `toolCallId`
- Tool-approval-response only has `approvalId`, not `toolCallId`
### 3. Updated Generated Component Types (src/component/_generated/component.ts)
Made manual updates to sync with validators (normally done via `convex codegen`):
- Added `input: any` field to all tool-call type definitions
- Made `args` optional (`args?: any`) in tool-call types
- Added `execution-denied` output type to tool-result
- Added extended content types: `file-data`, `file-url`, `file-id`, `image-data`, `image-url`, `image-file-id`, `custom`
- Added `providerOptions` to text types in content values
## Remaining Issues (5 TypeScript errors)
The remaining errors are due to a structural mismatch in the generated component types:
- Generated types have BOTH `experimental_content` (deprecated) and `output` (new) fields on tool-result
- Our validators only define `output`, not `experimental_content`
- TypeScript is comparing our new output types against the old experimental_content types
- This cannot be fixed manually - requires proper component regeneration
### To Complete the Fix:
1. Run `convex codegen --component-dir ./src/component` with a valid Convex deployment
2. This will regenerate `src/component/_generated/component.ts` from the validators
3. The regenerated types will:
- Remove the deprecated `experimental_content` field
- Use only the `output` field with correct types
- Properly match the validator definitions
### Error Locations:
- `src/client/index.ts:1052` - addMessages call
- `src/client/index.ts:1103` - addMessages call
- `src/client/index.ts:1169` - updateMessage call
- `src/client/messages.ts:141` - addMessages call
- `src/client/start.ts:265` - addMessages call
All errors have the same root cause: content value types in tool-result output don't match experimental_content expectations.
## Testing Plan
Once component types are regenerated:
1. Run `npm run build` - should complete without errors
2. Run `npm test` - ensure no regressions
3. Test with actual AI SDK v6 workflow - verify tool-call handling works with both new `input` and legacy `args` fields
## Notes
- The mapping functions in `src/mapping.ts` correctly handle both old and new formats
- Data with only `args` will be converted to have `input` (with `args` as fallback)
- Data with `input` will work directly
- This provides backward compatibility while supporting AI SDK v6's requirements
# Type Fix Summary
This file is intentionally left blank. Migration-specific implementation details have been moved to external developer documentation.

Copilot uses AI. Check for mistakes.
6 changes: 4 additions & 2 deletions src/client/files.ts
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ export async function storeFile(
storageId: newStorageId,
hash,
filename,
mimeType: blob.type,
mediaType: blob.type,
});
const url = (await ctx.storage.getUrl(storageId as Id<"_storage">))!;
if (storageId !== newStorageId) {
Expand Down Expand Up @@ -142,8 +142,10 @@ export async function getFile(
if (!url) {
throw new Error(`File not found in storage: ${file.storageId}`);
}
// Support both mediaType (preferred) and mimeType (deprecated)
const mediaType = file.mediaType ?? file.mimeType ?? "";
return {
...getParts(url, file.mimeType, file.filename),
...getParts(url, mediaType, file.filename),
file: {
fileId,
url,
Expand Down
1 change: 1 addition & 0 deletions src/client/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -193,6 +193,7 @@ describe("filterOutOrphanedToolMessages", () => {
type: "tool-call",
toolCallId: "1",
toolName: "tool1",
input: { test: "test" },
args: { test: "test" },
},
],
Expand Down
2 changes: 2 additions & 0 deletions src/client/search.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,7 @@ describe("search.ts", () => {
type: "tool-call",
toolCallId: "call_123",
toolName: "test",
input: {},
args: {},
},
],
Expand Down Expand Up @@ -202,6 +203,7 @@ describe("search.ts", () => {
type: "tool-call",
toolCallId: "call_orphaned",
toolName: "test",
input: {},
args: {},
},
],
Expand Down
11 changes: 8 additions & 3 deletions src/client/search.ts
Original file line number Diff line number Diff line change
Expand Up @@ -267,9 +267,14 @@ export function filterOutOrphanedToolMessages(docs: MessageDoc[]) {
});
}
} else if (doc.message?.role === "tool") {
const content = doc.message.content.filter((c) =>
toolCallIds.has(c.toolCallId),
);
const content = doc.message.content.filter((c) => {
// tool-result parts have toolCallId
if (c.type === "tool-result") {
return toolCallIds.has(c.toolCallId);
}
// tool-approval-response parts don't have toolCallId, so include them
Comment on lines +271 to +275
Copy link

Copilot AI Jan 18, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The filtering logic assumes that any part that isn't a tool-result should be included. However, tool-approval-request parts (which also exist in tool messages according to the validators) also have a toolCallId field. Consider explicitly checking for tool-approval-response type rather than relying on the absence of a toolCallId check.

Suggested change
// tool-result parts have toolCallId
if (c.type === "tool-result") {
return toolCallIds.has(c.toolCallId);
}
// tool-approval-response parts don't have toolCallId, so include them
// Filter parts that are tied to a specific tool call by toolCallId.
if (c.type === "tool-result" || c.type === "tool-approval-request") {
return toolCallIds.has(c.toolCallId);
}
// tool-approval-response and other non-call-bound parts are always included.

Copilot uses AI. Check for mistakes.
return true;
});
if (content.length) {
result.push({
...doc,
Expand Down
Loading
Loading