vercel / vercel/ai-elements

Confirmation's approval type is a hand copy of the SDK's, and it has now drifted

Open Beginner friendly
#484 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
2.4k
Forks
283
Avg merge
32m
Merged PRs (30d)
1

Description

packages/elements/src/confirmation.tsx imports ToolUIPart from ai and then declares its own approval shape underneath it rather than deriving it:

type ToolUIPartApproval =
  | { id: string; approved?: never; reason?: never }
  | { id: string; approved: boolean; reason?: string }
  | { id: string; approved: true;    reason?: string }
  | { id: string; approved: false;   reason?: string }
  | undefined;

Those four members line up with the states the SDK defines an approval on: approval-requested, approval-responded, output-available / output-error, and output-denied. In ai@7.0.84 each of those carries three more fields than the copy does. Taking approval-requested as the example:

approval: {
  id: string;
  approved?: never;
  requestReason?: string;   // missing from the copy
  reason?: never;
  isAutomatic?: boolean;    // missing from the copy
  signature?: string;       // missing from the copy
}

requestReason is the one that shows the cost of the copy. It was added in vercel/ai#19658, shipped in ai@7.0.82, and it exists specifically so the explanation of why a call was routed to a person reaches that person. Anyone typing their approval object with ToolUIPartApproval cannot read it without a cast, so the component's own type hides the field that was added for the component's own job.

isAutomatic has the same problem and predates it. The tool approval docs branch on part.state === 'approval-requested' && !part.approval.isAutomatic, so that a person is not asked to approve something the SDK already approved on its own. The type here discourages the check the documentation asks for.

Flattening the per-state shapes into one union also made two members unreachable. { approved: boolean; reason?: string } already covers both { approved: true; reason?: string } and { approved: false; reason?: string }, so those two branches can never be discriminated on and add nothing.

Deriving it would fix all of this at once and keep the two in step:

type ToolUIPartApproval = NonNullable<ToolUIPart['approval']>;

Worth flagging that the shape is still moving. vercel/ai#18967 is open and adds context?: JSONValue to the user-approval status. Because the type here is a copy rather than a reference, nothing in this file will fail to compile when that lands either. It will just disagree quietly again, the same way it is disagreeing now.

For context, vercel/ai#19658 came out of an issue I filed. Happy to open a PR if deriving the type is the direction you want.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in packages/elements/src/confirmation.tsx, where ToolUIPart is imported and the local ToolUIPartApproval union is declared. Compare that alias with ToolUIPart['approval'] and confirm the component's approval usage still type-checks. Done means the confirmation type exposes the SDK's current fields and no longer duplicates its per-state shape.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
frontend
Issue type
Refactor
Difficulty
1/5
Estimated time
Under an hour
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
88/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.