Skip to content

Vercel AI SDK middleware silently drops tool-call and tool-result messages when saving conversations #1206

Description

@kbpr21

Summary

In packages/tools/src/vercel/middleware.ts, convertToConversationMessages
(lines 18–76) only maps text and file content parts. Any other part type,
including tool-call (assistant) and tool-result (tool role), hits return null
and is removed by .filter((part) => part !== null).

Because a pure tool-call assistant turn has only tool-call parts, all its parts
become null, contentParts.length is 0, and the entire message is skipped.
The same happens to tool-role messages carrying tool-result.

Result: conversations that use tool calling via the Vercel middleware are saved to
Supermemory with all tool interactions stripped. The tool that was called, its
arguments, and its result are lost from the stored conversation.

Evidence

  • convertToConversationMessages handles only c.type === "text" and
    c.type === "file"; everything else returns null (map at ~line 35–50).
  • Searching the vercel folder for tool-call / tool-result / toolCallId
    returns nothing, there is no tool handling at all.
  • The OpenAI middleware (openai/middleware.ts) does preserve tool metadata, so
    Vercel-based memories are inconsistent with OpenAI-based ones.
  • ConversationMessage (conversations-client.ts) already supports
    tool_calls?: ToolCall[] and tool_call_id?: string, and the
    /v4/conversations endpoint doc states it "supports tool calls and responses",
    the receiving side is ready; the Vercel adapter just never sends them.

SDK version

packages/tools/package.json pins ai: ^5.0.29 (AI SDK v5), where tool parts are:

  • assistant: { type: "tool-call", toolCallId, toolName, input }
  • tool: { type: "tool-result", toolCallId, toolName, output }

Proposed fix

Extend the content-part mapping in convertToConversationMessages to handle v5
tool parts:

  • tool-call → append to the message's tool_calls as
    { id: toolCallId, type: "function", function: { name: toolName, arguments: JSON.stringify(input) } }
  • tool-result → emit a tool-role message with tool_call_id: toolCallId and
    the serialized output as content

Happy to submit a PR with unit tests covering a tool-call + tool-result round trip.
Want me to proceed?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions