---
title: "Thread"
description: "The per-conversation handle — post and stream messages, run the agent, block on a human choice, and reach platform power through capability-gated methods."
---

## Overview

A `Thread` is the per-conversation handle passed to every handler, tool context, and interaction context. It posts UI (JSX from the [component vocabulary](/reference/bot/components/Message) or plain strings), drives the agent run loop, resolves human-in-the-loop choices, and exposes platform power through **capability-gated** methods that degrade gracefully on surfaces that don't support them.

```ts
interface Thread {
  readonly platform: string;
  post(ui: Renderable): Promise<MessageRef>;
  update(ref: MessageRef, ui: Renderable): Promise<MessageRef>;
  delete(ref: MessageRef): Promise<void>;
  stream(src: string | AsyncIterable<string>): Promise<MessageRef>;
  runAgent(input?: {
    context?: ContextEntry[];
    tools?: BotTool[];
    prompt?: string;
  }): Promise<MessageRef | undefined>;
  resume(value: unknown): Promise<MessageRef | undefined>;
  awaitChoice<T = unknown>(ui: Renderable): Promise<T>;
  getMessages(): Promise<ThreadMessage[]>;
  lookupUser(query: string): Promise<PlatformUser | undefined>;
  postFile(args: {
    bytes: Uint8Array;
    filename: string;
    title?: string;
    altText?: string;
  }): Promise<{ ok: boolean; fileId?: string; error?: string }>;
}
```

## Properties

<PropertyReference name="platform" type="string" required>
  The surface this conversation lives on, e.g. `"slack"`.
</PropertyReference>

## Methods

<PropertyReference name="post" type="(ui: Renderable) => Promise<MessageRef>">
  Render and post a message. JSX is rendered to the [BotNode IR](/reference/bot/types/BotNode), every event-prop handler in the tree is **bound** (minted a content-stable id, snapshotted, rewritten to the bare id), and the IR is handed to the adapter. Returns a `MessageRef` for later `update` / `delete`.
</PropertyReference>

<PropertyReference name="update" type="(ref: MessageRef, ui: Renderable) => Promise<MessageRef>">
  Re-render an existing message in place — e.g. flipping a confirmation card to its approved state from a button's `onClick`.
</PropertyReference>

<PropertyReference name="delete" type="(ref: MessageRef) => Promise<void>">
  Delete a posted message.
</PropertyReference>

<PropertyReference name="stream" type="(src: string | AsyncIterable<string>) => Promise<MessageRef>">
  Post a placeholder and edit it in place as the source yields text. On Slack this is throttled `chat.update` with multi-message chunking — see [slack()](/reference/bot/slack).
</PropertyReference>

<PropertyReference name="runAgent" type="(input?) => Promise<MessageRef | undefined>">
  Resolve the conversation's agent session, create the adapter's run renderer, and drive the run/tool/interrupt loop: streamed text is rendered into the thread, registered [tools](/reference/bot/functions/defineBotTool) are executed when the agent calls them, and captured interrupts are dispatched to `onInterrupt` handlers.

  <PropertyReference name="context" type="ContextEntry[]">
    Extra context entries merged on top of the bot-level `context` for this run only.
  </PropertyReference>

  <PropertyReference name="tools" type="BotTool[]">
    Extra tools merged on top of the bot-level `tools` for this run only.
  </PropertyReference>

  <PropertyReference name="prompt" type="string">
    A user message injected before running. Use when the input isn't already in the history the adapter reconstructs — e.g. a slash command's text, which is never posted to the channel.
  </PropertyReference>
</PropertyReference>

<PropertyReference name="resume" type="(value: unknown) => Promise<MessageRef | undefined>">
  Re-enter a paused interrupt run, forwarding `value` to the agent (sent as `forwardedProps.command = { resume: value }` — the LangGraph `Command` shape). Typically called from a picker button's `onClick` inside an `onInterrupt` handler.
</PropertyReference>

<PropertyReference name="awaitChoice" type="<T>(ui: Renderable) => Promise<T>">
  Post a picker and **block** until an interaction in this conversation resolves it — the human-in-the-loop primitive. Resolves to the clicked control's `value`; pass `T` to type it. See the [Button](/reference/bot/components/Button) page for a full confirm-card example.
</PropertyReference>

<PropertyReference name="getMessages" type="() => Promise<ThreadMessage[]>">
  Read the conversation's messages — each a `ThreadMessage` (`{ user?, text, ts?, isBot? }`). **Capability-gated**: returns `[]` when the adapter can't read history. On Slack, backed by `conversations.replies`.
</PropertyReference>

<PropertyReference name="lookupUser" type="(query: string) => Promise<PlatformUser | undefined>">
  Resolve a platform user from a free-form query (name, handle, email). **Capability-gated**: returns `undefined` when unsupported.
</PropertyReference>

<PropertyReference name="postFile" type="(args: { bytes: Uint8Array; filename: string; title?: string; altText?: string }) => Promise<{ ok: boolean; fileId?: string; error?: string }>">
  Upload a file into the conversation. **Capability-gated**: resolves `{ ok: false, error }` when the surface doesn't support uploads. On Slack, backed by `files.uploadV2`.
</PropertyReference>

## Usage

```tsx
bot.onMention(async ({ thread, message }) => {
  // Run the agent with extra per-run context:
  await thread.runAgent({
    context: [
      { description: "Requesting user", value: message.user.name ?? message.user.id },
    ],
  });
});
```

```tsx
// Inside a tool: read the thread, then block on approval.
async handler({ summary }, { thread }) {
  const choice = await thread.awaitChoice<{ confirmed: boolean }>(
    <ConfirmWrite action={summary} />,
  );
  return choice ?? { confirmed: false }; // serialized for the agent automatically
}
```

## Behavior

- **Capability gating keeps tools portable** — `getMessages` / `lookupUser` / `postFile` delegate to the adapter when supported and degrade gracefully (`[]` / `undefined` / `{ ok: false }`) when not, so the same tool runs on any surface.
- **Per-run merging** — `runAgent`'s `tools` and `context` apply to that run only, layered on top of the bot-level defaults.
- **History reconstruction** — on Slack, the conversation's `agent.messages` are rebuilt from Slack history each turn; the platform is the source of truth, so bot restarts don't lose conversations.

## Related

- [createBot](/reference/bot/functions/createBot) — where handlers receive a Thread
- [defineBotTool](/reference/bot/functions/defineBotTool) — tools receive the Thread in `ctx.thread`
- [Button](/reference/bot/components/Button) — interactive messages and `awaitChoice`
- [slack()](/reference/bot/slack) — how the Slack adapter backs these methods
