---
title: "Replies, tables, and status"
description: "How an Agent's answer arrives in Slack: streamed text, adaptive tables, task rows, the live status line, and the footer under every reply."
---

> Documentation Index
> Fetch the complete documentation index at: https://docs.chickpea.co/llms.txt
> Use this file to discover all available pages before exploring further.

# Replies, tables, and status

A **reply** is everything Chickpea posts back into a thread for one turn: the answer text, at most one table, the task rows above it, and a footer naming the Agent and its model. Where routing decides which Agent answers, presentation decides what that answer looks like in Slack. This page covers streamed text, the three ways data is shown, task rows, the live status line under the composer, and the footer.

## How a reply is delivered

Every reply is one Slack message posted under the Agent's own name and avatar. Chickpea opens it as a streaming message, appends task rows and answer text while the turn runs, then writes the finished answer and its footer into that same message. No separate progress post appears and is deleted later.

## Progressive streaming

Streamed text is a delivery choice the model makes once, before it writes any answer text, by calling an internal tool named `stream_answer`. When it does not, the whole answer arrives at once, which is the expected case and not an error.

The instruction is narrow. Declare streaming only for a direct answer whose stable early prose is worth reading before the response is complete, never for a short confirmation, a structured response, a correction, or a response likely to need another tool. Delivery is all it changes: the model may not alter the answer, claim that Slack is streaming, or mention the tool.

Only complete Markdown reaches the channel. Chickpea holds back a trailing fragment that later tokens could still rewrite: an unclosed `[link`, an odd number of `**` markers, a trailing backtick, an open `<`, and a table row that has no newline yet.

**Eligibility is frozen before the turn runs, and it fails closed.** A turn is offered streaming only when all of these hold.

- The deployment has not turned streaming off.
- The Agent's memory was not selected for this turn.
- The Agent's plan for this turn has no MCP connections, no API connections, and no repositories.
- The turn does not use the coding sandbox.
- Chickpea can prove which turn any streamed text belongs to.

If the model declares streaming and then does something else, the declaration is denied: another tool call, structured output, a repeated or late declaration, or a conversation reset all stop the relay, and the answer is delivered whole instead.

## Tables

Chickpea offers three shapes for structured data, chosen by size.

- **Prose or bullets** for a yes/no answer, sequential steps, one record, three or fewer simple facts, or rows that would hold long explanations.
- **A Markdown table written into the answer** for six or fewer data rows, kept to four short columns.
- **A native Slack table** when at least seven verified rows are a substantial part of the answer, at most once per answer.

A native table is `static` or `explore`. Static is a read-only snapshot with stable columns, wrapping, alignment, and typed numbers. Explore adds sorting, filtering, and pagination, and suits rankings, queues, inventories, and metric results. Chickpea promotes a static table to the explorable form on its own when it carries more than 100 lines or more than 10,000 characters of cell text.

| Limit | Value |
|---|---|
| Data rows | 7 to 200 |
| Columns | 1 to 20 |
| Cells per row | Exactly the column count |
| Column header | 80 characters |
| Text cell | 1,000 characters |
| All cells together | 20,000 characters |
| Caption | 200 characters |
| Page size, explorable tables | 1 to 100, default 10 or the row count, whichever is smaller |

Requests outside those bounds are refused rather than trimmed. A single record is refused, six or fewer rows are refused, and a column marked numeric is refused if any cell in it is not a number or a plain numeric string. Above the ceiling the model summarizes the important rows and delivers the rest as an artifact where available. A native table is never combined with streamed text.

Surfaces that cannot render the block get two fallbacks: a Markdown copy, trimmed with a `Showing 5 of 12 rows.` line when it does not fit, and a plain-text copy that labels every cell with its column.

## Task rows

When a turn is classified as work, meaning a task expected to take more than a few seconds, Chickpea asks for one to four short labels naming an artifact, a result, or a question. Generic activity labels such as working, investigating, thinking, or checking are rejected.

Two or more labels open as task rows on the reply message. Each row moves through pending, in progress, and complete as work reaches it. When execution fails, Chickpea marks the row that was active as failed and marks every later row not run, with the detail `Not run: work stopped after the prior milestone failed.` Success is never inferred from a generic model reply.

## The activity status line

While the turn runs, Slack's native under-composer status shows the phase. The copy is deterministic. A tool owner registers a closed descriptor, and Chickpea narrates it; the model is never asked to describe its own progress.

| What is happening | What the status says |
|---|---|
| Before model work starts | `Thinking…` |
| Reading through a managed connector | `Checking Gmail…`, then `Reviewing Gmail messages…` |
| Sending through a managed connector | `Sending with Gmail…` |
| Using a custom API or MCP connection | `Checking a connected service…` |
| Running a skill | `Using a skill…`, then `Reviewing skill results…` |
| Working in a repository | `Inspecting the repository…`, `Editing files…`, `Running tests…` |
| Reading or writing memory | `Checking memory…`, `Updating memory…` |
| Writing the answer | `Drafting the response…` |
| A step that failed or came back ambiguous | `Reassessing the request…` |
| Anything unregistered | `Working on the request…` |

Four rules bound that line. It is at most 50 characters, because Slack rejects a longer entry, and longer copy is cut to 49 characters and an ellipsis. It refreshes at about 90 seconds so it does not expire at Slack's two-minute boundary. It never carries reasoning, tool arguments, results, or customer-authored names: an Agent, skill, repository, or connection stays generic by family, and only a managed connector's catalog label is shown. If Slack rejects it, custom status stops for that turn, nothing is posted in its place, and the answer is unaffected.

## The reply footer

Every reply ends with a context line reading `Agent name | model | Configure`. The model segment is the model id the turn ran on. **Configure** links into Admin for that Agent.

The link needs a public origin: `SLACK_TAG_PUBLIC_URL` when the deployment sets it, otherwise the stored origin from the install. With neither, or with an origin that is not `http` or `https`, `Configure` renders as plain text.

## Deployment switches

| Variable | When you set it | Purpose |
|---|---|---|
| `SLACK_TAG_PROGRESSIVE_STREAMING` | To stop offering streamed answers | On unless set to `false`, `0`, `off`, or `no`. New turns then deliver whole answers. |
| `SLACK_TAG_SEMANTIC_ACTIVITY_STATUS` | To stop the status line | On unless set to the same values. Slack's generic processing state is then the only progress surface. |
| `SLACK_TAG_PUBLIC_URL` | On Node, or to pin the origin | The base URL behind the footer's Configure link. Unset leaves a plain label. |

## Why it works this way

Presentation decisions sit outside the answer on purpose. The model may ask for streamed delivery and hand over rows to render, but it cannot write the status line, invent a task row, or mark its own work complete. Those come from real execution events and a closed vocabulary, which keeps a channel-visible surface out of reach of anything a message, a web page, or a tool result put into the turn.

The row thresholds exist because a Slack table is heavier than small data deserves. Six rows read better inline, and one record reads better as a sentence.

## What is not covered

- **One native table per answer.** A second call is refused, and results past 200 rows or 20,000 cell characters belong in an artifact.
- **An Agent with connections or repositories does not stream.** Its answers arrive whole, however long the turn runs.
- **On Node the sandbox rule never applies.** The coding sandbox is Cloudflare only, so it never blocks streaming there.
- **The status line is not a record.** Activity telemetry is fixed-schema and content-free, and Admin has no conversation archive.

## Next steps

- [How Chickpea works](/start/how-it-works): what happens between a mention and a reply, and the rules that hold on every turn.
- [In Slack](/slack): conversations, handles and channel grants, and managing Chickpea by asking.
- [What an Agent is](/agents/agents): what an Agent is, and what it may reach while it answers.

Source: https://docs.chickpea.co/slack/replies/index.mdx
