# Swap a card

> Replace one of the package's cards, the confirm, choices, links, table, spec, comparison or sources card, with a component of your own through cards on AssistedProvider.

A card is how a tool's call shows in the conversation. The package brings seven, and `cards` on `AssistedProvider` replaces any of them for the whole app. A card you leave out stays the package's.

## Replace a card

Write a component that takes the tool call's props, and pass it under the card's key. Define it at module scope, so it keeps its identity across renders:

```tsx
import { type AssistedCards, AssistedProvider } from "@super-assist/react";
import { Fragment } from "react";

const Spec: NonNullable<AssistedCards["spec"]> = ({ args, result }) => {
  if (typeof result !== "string") return null;
  return (
    <dl className="my-2 grid grid-cols-2 gap-1 rounded border p-2 text-sm">
      {args.rows.map(({ label, value }) => (
        <Fragment key={label}>
          <dt className="text-muted-foreground">{label}</dt>
          <dd>{value}</dd>
        </Fragment>
      ))}
    </dl>
  );
};

const cards: AssistedCards = { spec: Spec };

<AssistedProvider runtime={runtime} cards={cards}>
  {children}
</AssistedProvider>;
```

`args` holds the model's arguments, typed per card. `result` is the call's answer once it has one. The package's show cards wait for it, so a call the browser has not run yet shows nothing.

## Answer a waiting call

The confirm and choices cards pause the call until the user answers. Their components get `interrupt`, set while the call waits, and `resume`, which answers it:

```tsx
const Confirm: NonNullable<AssistedCards["confirm"]> = ({ interrupt, resume, result }) => {
  const ask = interrupt?.payload as { label: string } | undefined;
  if (!ask) return result === undefined ? null : <p className="text-xs">Answered.</p>;
  return (
    <div className="my-2 flex items-center gap-2 rounded border p-2 text-sm">
      <span className="flex-1">{ask.label}</span>
      <button onClick={() => resume({ approved: false })}>Deny</button>
      <button onClick={() => resume({ approved: true })}>Approve</button>
    </div>
  );
};
```

| Key       | `interrupt.payload`     | Answer with                                                   |
| --------- | ----------------------- | ------------------------------------------------------------- |
| `confirm` | `{ name, label, id? }`  | `resume({ approved: true })` or `resume({ approved: false })` |
| `choices` | `{ question, options }` | `resume(option)`, one of the options                          |

The `confirm` card serves every consequential action and tool. A tool's own `render` on `useAssistedTool` replaces it for that tool only.

## The keys

| Key          | Tool                                | `args`                                     |
| ------------ | ----------------------------------- | ------------------------------------------ |
| `confirm`    | Every consequential action and tool | The action's or tool's arguments           |
| `choices`    | `offer_choices`                     | `{ question, options }`                    |
| `links`      | `show_links`                        | `{ items: { to, id?, title, gist? }[] }`   |
| `table`      | `show_table`                        | `{ title?, columns, rows }`                |
| `spec`       | `show_spec`                         | `{ title, rows }`                          |
| `comparison` | `show_comparison`                   | `{ traits, options, recommended, reason }` |
| `sources`    | `search_knowledge`, on the server   | `{ query, knowledgeBase? }`                |

What each tool sends and answers is on its card's page under [Cards](https://superassist.app/components.md#cards). The model still calls the same tools: a card changes how a call looks, never what the model sees.
