# Chat Tool Calls

## Usage

ChatToolCalls displays tool or function call invocations from an LLM response. Pass an array of calls and the component handles the rest: a single call renders inline, while multiple calls collapse into a summary with the latest call visible at the surface. Use it anywhere an AI agent shows what actions it took.

An empty array renders nothing. Several calls collapse into one header row that shows the latest call; the header toggles between that summary and the full list. `v-model:is-expanded` controls the group, and `default-is-expanded` sets the state used while the model is unbound. `label` replaces the localized call count once the group is open. A call with `resultDetail` (or its `detail` alias), or the `detail` slot, expands on its own.

```vue
<script setup lang="ts">
import { ChatMessage, ChatToolCalls } from '@astryx-vue/core/Chat'

const calls = [
  { name: 'bash', status: 'complete' as const, target: 'git status', duration: '1.2s' },
]
</script>

<template>
  <ChatMessage sender="assistant">
    <ChatToolCalls :calls="calls" />
  </ChatMessage>
</template>
```

### Anatomy

- **Status icon**: A themed semantic success/error icon, or a spinner while the call is pending or running.
- **Tool name**: The function or tool name displayed in monospace: bash, edit, read, web_search, etc.
- **Node badge**: A neutral pill badge showing which sandbox or environment ran the tool, like cli:remote-server or workspace.
- **Target label**: The target of the action (a file path, command, or search query) shown after the tool name.
- **Diff stats**: Green additions and red deletions counts for edit operations, displayed inline after the target.
- **Duration**: Execution time shown on the trailing edge for completed calls.
- **Group header**: A wrench icon with a call count, shown when multiple calls are present. Clicking toggles between the summary and the full list.

## Best practices

| Guidance | Practices |
| --- | --- |
| Do | Include a target string on every call so the user can see what the tool acted on: a file path, a shell command, or a search query. |
| Do | Show a duration on completed calls so users can judge which tools are slow and understand why a response took time. |
| Do | Provide a group label when the automatic count does not explain what the calls represent. |
| Do | Provide the `detail` slot with a code block for calls that produce output (diffs for edits, terminal output for shell commands) so users can inspect results inline. |
| Do | Set a unique `key` on each call item when streaming so Vue can animate additions without re-mounting completed rows. |
| Don't | Don't omit the status field. Without it the call defaults to complete, which is misleading for calls that are still running or have failed. |
| Don't | Don't display tool calls outside a chat message context; they are designed to sit inside an assistant message, not as standalone UI. |
| Don't | Don't use custom wrappers around individual calls; the component handles single vs. grouped layout automatically based on the array length. |

## Examples

Common configurations, variations, and states.

### Expandable

Tool calls with expandable result details showing diffs and command output in code blocks. Click a row to reveal its result.

```vue
<script setup lang="ts">
import type { ChatToolCallItem } from '@astryx-vue/core/Chat'
import { ChatMessage, ChatMessageList, ChatToolCalls } from '@astryx-vue/core/Chat'
import { computed } from 'vue'

const calls = computed<ChatToolCallItem[]>(() => [
  {
    name: 'edit',
    status: 'complete',
    node: 'cli:remote-server',
    target: 'src/utils/formatDate.ts',
    additions: 6,
    deletions: 3,
    duration: '85ms',
    resultDetail: 'diff',
  },
  {
    name: 'bash',
    status: 'complete',
    node: 'cli:remote-server',
    target: 'yarn test',
    duration: '1.8s',
    resultDetail: 'ok',
  },
  {
    name: 'web_search',
    status: 'complete',
    target: 'Intl.DateTimeFormat locale options',
    duration: '1.2s',
    resultDetail: 'ok',
  },
])

const diff = `function formatDate(value) {
-  return value.toLocaleDateString();
+  return new Intl.DateTimeFormat(locale).format(value);
}`
</script>

<template>
  <ChatMessageList>
    <ChatMessage sender="assistant">
      <ChatToolCalls :calls="calls" default-is-expanded>
        <template #detail="{ call }">
          <pre class="chat-preview__code">{{ call.name === 'edit' ? diff : 'passed' }}</pre>
        </template>
      </ChatToolCalls>
    </ChatMessage>
  </ChatMessageList>
</template>
```

### Simple

A single inline tool call above a collapsible multi-call group with diff stats. Shows both layouts side by side.

```vue
<script setup lang="ts">
import type { ChatToolCallItem } from '@astryx-vue/core/Chat'
import { ChatMessage, ChatMessageList, ChatToolCalls } from '@astryx-vue/core/Chat'
import { computed } from 'vue'

const single = computed<ChatToolCallItem[]>(() => [
  { name: 'bash', status: 'complete', target: 'git status', duration: '1.2s' },
])

const group = computed<ChatToolCallItem[]>(() => [
  { name: 'read', status: 'complete', target: 'src/components/DataGrid.tsx', duration: '30ms' },
  { name: 'edit', status: 'complete', target: 'src/components/DataGrid.tsx', additions: 24, deletions: 8, duration: '85ms' },
  { name: 'edit', status: 'complete', target: 'src/components/DataGrid.test.tsx', additions: 45, duration: '60ms' },
])
</script>

<template>
  <ChatMessageList>
    <ChatMessage sender="assistant">
      <ChatToolCalls :calls="single" />
      <ChatToolCalls :calls="group" default-is-expanded />
    </ChatMessage>
  </ChatMessageList>
</template>
```

### Statuses

All four status states (pending, running, complete, and error) shown together in a single group.

```vue
<script setup lang="ts">
import type { ChatToolCallItem } from '@astryx-vue/core/Chat'
import { ChatMessage, ChatMessageList, ChatToolCalls } from '@astryx-vue/core/Chat'
import { computed } from 'vue'

const calls = computed<ChatToolCallItem[]>(() => [
  { name: 'bash', status: 'pending', target: 'yarn build' },
  { name: 'bash', status: 'running', target: 'yarn test' },
  { name: 'edit', status: 'complete', target: 'src/App.tsx', additions: 8, deletions: 2, duration: '120ms' },
  {
    name: 'bash',
    status: 'error',
    target: 'yarn lint',
    errorMessage: '3 lint errors found',
  },
])
</script>

<template>
  <ChatMessageList>
    <ChatMessage sender="assistant">
      <ChatToolCalls :calls="calls" default-is-expanded />
    </ChatMessage>
  </ChatMessageList>
</template>
```

## Props

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `calls` | `ChatToolCallItem[]` | — | Tool calls from one assistant turn. An empty array renders nothing. Required. Each item takes `name`, `status`, `target`, `duration`, `node`, `additions`, `deletions`, `stats`, `errorMessage`, `key`, `resultDetail`, and `data`. |
| `label` | `string` | localized call count | Summary shown while a group is expanded. |
| `isExpanded` | `boolean` | — | Controlled expanded state of the group, bound with `v-model:is-expanded`. |
| `defaultIsExpanded` | `boolean` | `false` | Expanded state used while `isExpanded` is unbound. |

## Events

- `expandedChange(isExpanded: boolean)`: fires with the state the header is asking for. `v-model:is-expanded` also emits `update:isExpanded`.

## Slots

- `detail`: rich detail for one call, receiving `{ call }`. Falls back to `call.resultDetail` when omitted.

## Exposed

- `element`: the root element.

## Types

`ChatToolCallItem`:

| Field | Type | Description |
| --- | --- | --- |
| `name` | `string` | Tool or function name, shown in monospace. Required. |
| `status` | `'pending' \| 'running' \| 'complete' \| 'error'` | Execution status. Omit it and the call is treated as complete. |
| `target` | `string` | What the tool acted on, such as a file path, a command, or a query. |
| `duration` | `string` | Execution time shown on the trailing edge, such as `1.2s`. |
| `node` | `string` | Sandbox or worker name, shown as a neutral badge. |
| `additions` | `number` | Added lines or characters, rendered as `+12`. |
| `deletions` | `number` | Removed lines or characters, rendered as `-3`. |
| `stats` | `string` | Extra plain text after the label. |
| `errorMessage` | `string` | Error detail when `status` is `error`. Spoken to assistive tech and used as the status icon's tooltip. |
| `key` | `string` | Stable identity. Derived from the other fields when omitted. |
| `resultDetail` | `string` | Inline detail revealed when the row is expanded, such as a diff or command output. Its presence makes the row expandable. Use the `detail` slot instead for rich markup. |
| `detail` | `string` | Alias of `resultDetail`. `resultDetail` wins when both are set. |
| `data` | `unknown` | Arbitrary caller data kept on the item. Never rendered. |
