# Call Capsule

> For agents: start with the [agent guide](https://pi-tui.ratstack.sh/llms.txt). Every page here has a Markdown twin, and every pattern has a JSON record.

Presentation / Tool and message output · `call-capsule` · [HTML](https://pi-tui.ratstack.sh/patterns/call-capsule/) · [JSON](https://pi-tui.ratstack.sh/patterns/call-capsule.json) · [all patterns](https://pi-tui.ratstack.sh/patterns.md)

Also known as Call-scoped renderer.

## Intent

Retain a display shell through call and result renders of one tool execution.

## Motivation

Dot314's renderer-call-state keeps a call display shell available during result rendering.

## Applicability

- Use this when streamed results should update their existing mounted presentation.

## Structure

```text
call context -> display shell
partial / final -> same call state
```

## Participants

- [`ToolRenderContext`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#tool-rendering): Shares per-call state, prior components and invalidation.
- [`ToolDefinition.renderCall`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#tool-rendering): Renders the call before or during execution.
- [`ToolDefinition.renderResult`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#tool-rendering): Renders partial or final tool output.
- `Display shell`: Belongs to one tool execution across call and result rendering.

Pi component APIs: [`ToolRenderContext`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#tool-rendering), [`ToolDefinition.renderCall`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#tool-rendering), [`ToolDefinition.renderResult`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#tool-rendering), `ToolExecutionComponent`

## Consequences

- One tool execution can update its retained presentation.
- Result rendering must tolerate an absent or hidden shell.

## Implementation

- Scope state to the tool call rather than the whole session.
- Handle an absent or hidden previous component.

## States

Each state was rendered at 40, 60, 80 and 120 columns in Pi's dark and light themes. Each frame links one WebP. An animated frame links its first image, then the animation.

### Call shell

`call`: Show a synthetic tool call before its result arrives.

Checks at every width and theme:

- width: ✓ pass
- style-leak: ✓ pass
- hard-coded-colour: ✓ pass
- height: ✓ pass

Frames:

- 40 columns: ![Call Capsule, Call shell, 40 columns, dark theme](https://pi-tui.ratstack.sh/frames/call-capsule/call-40-dark.a724559bc6a1.webp) ![Call Capsule, Call shell, 40 columns, light theme](https://pi-tui.ratstack.sh/frames/call-capsule/call-40-light.2ac2d29ed230.webp)
- 60 columns: ![Call Capsule, Call shell, 60 columns, dark theme](https://pi-tui.ratstack.sh/frames/call-capsule/call-60-dark.e8a42c77cfeb.webp) ![Call Capsule, Call shell, 60 columns, light theme](https://pi-tui.ratstack.sh/frames/call-capsule/call-60-light.09e37632b1c0.webp)
- 80 columns: ![Call Capsule, Call shell, 80 columns, dark theme](https://pi-tui.ratstack.sh/frames/call-capsule/call-80-dark.8dd30ca2cb9b.webp) ![Call Capsule, Call shell, 80 columns, light theme](https://pi-tui.ratstack.sh/frames/call-capsule/call-80-light.c4705f3ab311.webp)
- 120 columns: ![Call Capsule, Call shell, 120 columns, dark theme](https://pi-tui.ratstack.sh/frames/call-capsule/call-120-dark.d4791a8d23dc.webp) ![Call Capsule, Call shell, 120 columns, light theme](https://pi-tui.ratstack.sh/frames/call-capsule/call-120-light.9d79ef1277a0.webp)

### Shell updated

`partial`: Update the same presentation with partial data.

Checks at every width and theme:

- width: ✓ pass
- style-leak: ✓ pass
- hard-coded-colour: ✓ pass
- height: ✓ pass

Frames:

- 40 columns: ![Call Capsule, Shell updated, 40 columns, dark theme](https://pi-tui.ratstack.sh/frames/call-capsule/partial-40-dark.5a24b6a934fc.webp) ![Call Capsule, Shell updated, 40 columns, light theme](https://pi-tui.ratstack.sh/frames/call-capsule/partial-40-light.f4e5036524bb.webp)
- 60 columns: ![Call Capsule, Shell updated, 60 columns, dark theme](https://pi-tui.ratstack.sh/frames/call-capsule/partial-60-dark.a6dd7f8c5123.webp) ![Call Capsule, Shell updated, 60 columns, light theme](https://pi-tui.ratstack.sh/frames/call-capsule/partial-60-light.4d4f97c4bf3b.webp)
- 80 columns: ![Call Capsule, Shell updated, 80 columns, dark theme](https://pi-tui.ratstack.sh/frames/call-capsule/partial-80-dark.7282f3fb318c.webp) ![Call Capsule, Shell updated, 80 columns, light theme](https://pi-tui.ratstack.sh/frames/call-capsule/partial-80-light.1d010aac09ea.webp)
- 120 columns: ![Call Capsule, Shell updated, 120 columns, dark theme](https://pi-tui.ratstack.sh/frames/call-capsule/partial-120-dark.5a57af4069bf.webp) ![Call Capsule, Shell updated, 120 columns, light theme](https://pi-tui.ratstack.sh/frames/call-capsule/partial-120-light.1ae22a708e2b.webp)

### Missing shell

`no-shell`: Render a final result safely without an earlier mounted shell.

Checks at every width and theme:

- width: ✓ pass
- style-leak: ✓ pass
- hard-coded-colour: ✓ pass
- height: ✓ pass

Frames:

- 40 columns: ![Call Capsule, Missing shell, 40 columns, dark theme](https://pi-tui.ratstack.sh/frames/call-capsule/no-shell-40-dark.641ecdb5c4b5.webp) ![Call Capsule, Missing shell, 40 columns, light theme](https://pi-tui.ratstack.sh/frames/call-capsule/no-shell-40-light.62dcb0fa80a0.webp)
- 60 columns: ![Call Capsule, Missing shell, 60 columns, dark theme](https://pi-tui.ratstack.sh/frames/call-capsule/no-shell-60-dark.651c400eaf70.webp) ![Call Capsule, Missing shell, 60 columns, light theme](https://pi-tui.ratstack.sh/frames/call-capsule/no-shell-60-light.2b3e837fe5f7.webp)
- 80 columns: ![Call Capsule, Missing shell, 80 columns, dark theme](https://pi-tui.ratstack.sh/frames/call-capsule/no-shell-80-dark.e7fc335d2258.webp) ![Call Capsule, Missing shell, 80 columns, light theme](https://pi-tui.ratstack.sh/frames/call-capsule/no-shell-80-light.c5233b774ca8.webp)
- 120 columns: ![Call Capsule, Missing shell, 120 columns, dark theme](https://pi-tui.ratstack.sh/frames/call-capsule/no-shell-120-dark.fa2dd9b61a0f.webp) ![Call Capsule, Missing shell, 120 columns, light theme](https://pi-tui.ratstack.sh/frames/call-capsule/no-shell-120-light.b0eb444139a4.webp)

## Sample code

`stories/patterns/presentation/call-capsule.ts`, the story the frames above were rendered from.

```ts
// Call Capsule: retain one display shell per execution, with a safe missing-shell path.
import { Text, wrapTextWithAnsi } from "@earendil-works/pi-tui";
import { ToolExecutionComponent, type ToolRenderers } from "@earendil-works/pi-coding-agent";
import type { PatternStory } from "../../../src/pattern.ts";

export const story: PatternStory = {
  id: "call-capsule", title: "Call Capsule", kind: "component",
  apis: ["ToolExecutionComponent", "ToolRenderContext", "ToolDefinition.renderCall", "ToolDefinition.renderResult"],
  rows: 12,
  states: [
    { id: "call", label: "Call shell" },
    { id: "partial", label: "Shell updated", steps: [{ type: "action", name: "partial" }] },
    { id: "no-shell", label: "Missing shell", steps: [{ type: "action", name: "no-shell" }] },
  ],
  setup({ theme, tui, action }) {
    let omitShell = false;
    const renderers: ToolRenderers = {
      renderCall(_args, _theme, context) {
        if (omitShell) {
          delete context.state.shell;
          return new Text("", 0, 0);
        }
        if (!(context.state.shell instanceof Text)) {
          context.state.shell = new Text(theme.fg("toolTitle", "build_preview · waiting for output"), 0, 0);
        }
        return context.state.shell;
      },
      renderResult(_result, options, _theme, context) {
        const shell: unknown = context.state.shell;
        if (shell instanceof Text) {
          shell.setText(theme.fg("toolTitle", "build_preview · " + context.toolCallId));
        }
        const message = options.isPartial ? "Same shell · compiled 2 of 5 modules" :
          "No earlier shell · 5 modules compiled";
        return {
          invalidate() {},
          render(width) {
            return wrapTextWithAnsi(message, width).map(line =>
              theme.fg(options.isPartial ? "warning" : "success", line));
          },
        };
      },
    };
    const tool = new ToolExecutionComponent("build_preview", "example-build-1", {},
      undefined, renderers, tui, "/workspace/example");
    action("partial", () => {
      tool.markExecutionStarted();
      tool.updateResult({ content: [], isError: false }, true);
      tui.requestRender();
    });
    action("no-shell", () => {
      omitShell = true;
      tool.updateResult({ content: [], isError: false }, false);
      tui.requestRender();
    });
    return tool;
  },
};
```

## Known uses: seen in Nico's repos

- [**dot314**](https://github.com/nicobailon/dot314): Reuse call-scoped presentation across tool phases
  - [extensions/repoprompt-mcp/src/index.ts:2903-2932](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/repoprompt-mcp/src/index.ts#L2903-L2932) @17cce138
  - [extensions/repoprompt-cli/index.ts:3134-3164](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/repoprompt-cli/index.ts#L3134-L3164) @17cce138
  - [extensions/repoprompt-mcp/src/index.ts:2900-2935](https://github.com/nicobailon/dot314/blob/17cce138f3f687211d0485d204408a9650953c61/extensions/repoprompt-mcp/src/index.ts#L2900-L2935) @17cce138

## For agents: choose and check

Choose Call Capsule when your job matches its intent and applicability above. Its neighbours in Tool and message output are listed below. Read the one whose intent fits your job more closely before you commit.

- [Detail Fold](https://pi-tui.ratstack.sh/patterns/detail-fold.md): Show compact progress and summaries with bounded expanded tool detail.
- [Hinge Diff](https://pi-tui.ratstack.sh/patterns/hinge-diff.md): Choose compact, unified or split diff presentation from available width.
- [Word Spotlight](https://pi-tui.ratstack.sh/patterns/word-spotlight.md): Emphasize changed words inside parsed patch lines.
- [Error Digest](https://pi-tui.ratstack.sh/patterns/error-digest.md): Project structured errors into compact and expanded diagnostic output.
- [Renderer Chain](https://pi-tui.ratstack.sh/patterns/renderer-chain.md): Add tool rendering while preserving an existing renderer or fallback.
- [Message Fold](https://pi-tui.ratstack.sh/patterns/message-fold.md): Render custom message content as a preview with expanded detail.
- [Image Parachute](https://pi-tui.ratstack.sh/patterns/image-parachute.md): Render terminal images where supported and text placeholders otherwise.

Check your version:

- Render your version at 40, 60, 80 and 120 columns in the dark and light themes.
- Check width: no rendered line is wider than the terminal.
- Check style-leak: no line ends with colour, bold or a link still switched on.
- Check hard-coded-colour: every colour on screen comes from the active theme.
- Check height: the output fits in the rows the terminal has.
- Read the Pi 1.0.3 docs for [`ToolRenderContext`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#tool-rendering), [`ToolDefinition.renderCall`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#tool-rendering), [`ToolDefinition.renderResult`](https://github.com/earendil-works/pi/blob/v1.0.3/packages/coding-agent/docs/extensions.md#tool-rendering), `ToolExecutionComponent` before you use them.
- Every reference frame gets the verdict its state expects.

Next actions: `related({ id: "call-capsule" })` lists what to read next, and `states({ id: "call-capsule", width: 40 })` returns every frame and verdict at one width. The [build-pi-tui-component skill](https://pi-tui.ratstack.sh/skills/build-pi-tui-component.md) walks through the whole loop.

## Related

- [Renderer Chain](https://pi-tui.ratstack.sh/patterns/renderer-chain.md): selects or wraps the renderer

## Linked from

- [Detail Fold](https://pi-tui.ratstack.sh/patterns/detail-fold.md): retains a shell across result updates
- [Renderer Chain](https://pi-tui.ratstack.sh/patterns/renderer-chain.md): retains per-execution renderer state
