Skip to content

feat(channels): add a Pi TUI client for the Web Channel - #2437

Merged
cjol merged 1 commit into
channels/08-ai-sdk-tuifrom
channels/09-pi-tui
Oct 2, 2026
Merged

cjol merged 1 commit into
channels/08-ai-sdk-tuifrom
channels/09-pi-tui

Conversation

@cjol

@cjol cjol commented Oct 1, 2026 •

Copy link
Copy Markdown
Member

This PR adds npx agents tui <url>, a terminal client for any conversation's Web Channel, built on @earendil-works/pi-tui.

Why

  • Developers need to talk to a Channels agent from a terminal without writing a client. The @ai-sdk/tui client in the example is tied to that example.
  • The client covers what the Web Channel offers: live transcript and turns from every surface, approvals, client tool results, cancellation, and moving between conversations.
  • Its dependencies are bundled into dist/cli.js, so installing agents adds none.

Public API Surface

Additive CLI:

Command or flag Notes
agents tui <url> Connects to a Web Channel URL
--header, -H Extra headers on the WebSocket upgrade
--as Sets the as query parameter, which demo agents read as the participant
CF_ACCESS_CLIENT_ID, CF_ACCESS_CLIENT_SECRET Sent as an Access service token
/new, /fork, /reset [note] Create, fork or reset a conversation, when the agent offers it
/conversations, /switch <id> List the agent's conversations and follow one; a unique id prefix is enough

Code Changes

  • channels/web/tui/ holds argument parsing, the socket, the view model, and rendering components with Markdown highlighting and collapsible reasoning and tool blocks.
  • The header and footer show the route and the current conversation id.
  • cli/index.ts dispatches tui. scripts/build.ts bundles the CLI.
  • The Channels example gains a tui2 [harness] [room] script and a README entry. Patch changeset for agents.

@changeset-bot

changeset-bot Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 1da3166

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 2 packages
Name Type
agents Patch
@cloudflare/agent-think Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@cjol
cjol added this pull request to stack #2440 October 1, 2026 13:56
@agent-think

agent-think Bot commented Oct 1, 2026 •

Copy link
Copy Markdown
Contributor

✅ agents import sizes: no significant changes (77d10cf5 → 1da3166c, workflow run)

@cjol
cjol force-pushed the channels/09-pi-tui branch from 5fa7763 to a668a3c Compare October 1, 2026 14:10
@cjol
cjol force-pushed the channels/09-pi-tui branch from a668a3c to 37df5b1 Compare October 1, 2026 14:27
@pkg-pr-new

pkg-pr-new Bot commented Oct 1, 2026 •

Copy link
Copy Markdown

Open in StackBlitz

agents

npm i https://pkg.pr.new/agents@2437

@cloudflare/ai-chat

npm i https://pkg.pr.new/@cloudflare/ai-chat@2437

@cloudflare/codemode

npm i https://pkg.pr.new/@cloudflare/codemode@2437

hono-agents

npm i https://pkg.pr.new/hono-agents@2437

@cloudflare/shell

npm i https://pkg.pr.new/@cloudflare/shell@2437

@cloudflare/think

npm i https://pkg.pr.new/@cloudflare/think@2437

@cloudflare/voice

npm i https://pkg.pr.new/@cloudflare/voice@2437

@cloudflare/worker-bundler

npm i https://pkg.pr.new/@cloudflare/worker-bundler@2437

commit: 1da3166

@cjol
cjol force-pushed the channels/09-pi-tui branch from 37df5b1 to 7b5674b Compare October 2, 2026 10:13
@cjol
cjol force-pushed the channels/09-pi-tui branch from 7b5674b to 719e05c Compare October 2, 2026 10:18
@cjol
cjol force-pushed the channels/09-pi-tui branch from 719e05c to d95fb21 Compare October 2, 2026 11:08
@cjol
cjol removed this pull request from stack #2440 October 2, 2026 17:37
@cjol
cjol force-pushed the channels/09-pi-tui branch from d95fb21 to 55fd5bd Compare October 2, 2026 17:37
@cjol
cjol marked this pull request as ready for review October 2, 2026 17:38
@cjol
cjol added this pull request to stack #2460 October 2, 2026 17:39

@devin-ai-integration devin-ai-integration Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Devin Review found 4 potential issues.

Devin Review

refresh();
return;
}
client.follow(matches[0]);

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔴 Conversation switches lose the agent route

With /channels/ai-sdk/lobby, /switch reconnects WebChannelClient to /channels/ai-sdk/<id>, making the conversation ID the room. The gateway connects to a different agent; /new and /fork also lose their conversation on reconnect.

Learn more

The TUI uses a Web Channel client whose default follow URL rewrites /channels/<route>/<conversation> with only one route segment. The example gateway instead uses /channels/<harness>/<room>/<conversation>, so a room URL is misread as a conversation URL. Following new-id from /channels/ai-sdk/lobby connects to the room new-id instead of the room lobby; from an explicit conversation URL, the follow URL does not change at all. The same URL rewriting applies when the server moves the connection after a create or fork, so later reconnects go to the wrong conversation. See defaultFollowUrl and the gateway resolver.

Example: Start at /channels/ai-sdk/lobby, create conversation c2, then lose the WebSocket. Reconnection uses /channels/ai-sdk/c2, which reaches the c2 room's default session instead of the lobby room's c2 conversation.

Recommended fix: Supply a followUrl function to WebChannelClient that preserves the entire agent route and replaces or appends only the conversation segment. Ensure the initial URL with or without a conversation ID and URLs with multiple route segments both reconnect to the intended agent and conversation.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +95 to +97
if (!protocol) throw new UsageError(`Unsupported protocol ${url.protocol}`);
url.protocol = protocol;
return url;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟡 Fragment URLs crash the terminal client

When the input URL contains #fragment, toSocketUrl passes it to WebChannelClient unchanged. WebSocket rejects fragment URLs, so the terminal client exits before connecting.

Learn more

parseArgs accepts both HTTP and WebSocket URLs and converts the HTTP schemes to WebSocket schemes. A URL fragment is legal in an HTTP URL but not in a WebSocket constructor URL. WebChannelClient passes the parsed URL straight to new WebSocket, which throws for fragments.

Example: agents tui 'https://example.com/channels/ai-sdk/lobby#chat' produces a wss: URL with #chat and fails before connecting instead of using the channel path.

Recommended fix: Reject nonempty url.hash with a UsageError, or remove the hash when converting HTTP URLs if fragments have no useful Web Channel meaning.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

Comment on lines +61 to +64
`pnpm tui2 [harness] [room]` runs the Agents SDK terminal client built on Pi
TUI (`npx agents tui <url>`) against either agent, for example
`pnpm tui2 ai-sdk lobby`. It shows turns started from other surfaces, lets you
provide client-tool results, and can start, fork, list and switch

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Example advertises an unregistered harness

The terminal instructions claim tui2 works with either harness, but the gateway resolver registers only ai-sdk. The example needs either the second harness or narrower instructions.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

local.runningSince.clear();
local.notices = [];
prompted.clear();
context.ui = { ...context.ui, selected: undefined };

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🔍 Block expansion state crosses conversations

Switching conversations clears selection but retains context.ui.toggled. Reused tool-call or message IDs inherit expansion choices from the previous conversation; consider clearing per-block toggles on follow.

Devin Review


Was this helpful? React with 👍 or 👎 to provide feedback.

@cjol
cjol force-pushed the channels/09-pi-tui branch from 55fd5bd to e463b13 Compare October 2, 2026 18:20
This PR adds `npx agents tui <url>`, a terminal client for any conversation's Web Channel, built on `@earendil-works/pi-tui`.

## Why

- Developers need to talk to a Channels agent from a terminal without writing a client. The `@ai-sdk/tui` client in the example is tied to that example.
- The client covers what the Web Channel offers: live transcript and turns from every surface, approvals, client tool results, cancellation, and moving between conversations.
- Its dependencies are bundled into `dist/cli.js`, so installing `agents` adds none.

## Public API Surface

Additive CLI:

| Command or flag | Notes |
| --- | --- |
| `agents tui <url>` | Connects to a Web Channel URL |
| `--header`, `-H` | Extra headers on the WebSocket upgrade |
| `--as` | Sets the `as` query parameter, which demo agents read as the participant |
| `CF_ACCESS_CLIENT_ID`, `CF_ACCESS_CLIENT_SECRET` | Sent as an Access service token |
| `/new`, `/fork`, `/reset [note]` | Create, fork or reset a conversation, when the agent offers it |
| `/conversations`, `/switch <id>` | List the agent's conversations and follow one; a unique id prefix is enough |

## Code Changes

- `channels/web/tui/` holds argument parsing, the socket, the view model, and rendering components with Markdown highlighting and collapsible reasoning and tool blocks.
- The header and footer show the route and the current conversation id.
- `cli/index.ts` dispatches `tui`. `scripts/build.ts` bundles the CLI.
- The Channels example gains a `tui2 [harness] [room]` script and a README entry. Patch changeset for `agents`.

# Conflicts:
#	packages/agents/package.json
#	pnpm-lock.yaml

# Please enter the commit message for your changes. Lines starting
# with '#' will be kept; you may remove them yourself if you want to.
# An empty message aborts the commit.
#
# interactive rebase in progress; onto 74570a1
# Last commands done (19 commands done):
#    update-ref 62d6f8f 
#    pick 55fd5bd # feat(channels): add a Pi TUI client for the Web Channel
# Next commands to do (8 remaining commands):
#    update-ref 55fd5bd 
#    pick 3d73aec # feat(examples): add the pi agent to the Channels example
# You are currently rebasing branch 'channels/13-harness-bridge' on '74570a19a'.
#
# Changes to be committed:
#	new file:   .changeset/channels-tui.md
#	modified:   examples/next/channels/README.md
#	modified:   examples/next/channels/package.json
#	modified:   packages/agents/package.json
#	modified:   packages/agents/scripts/build.ts
#	new file:   packages/agents/src/cli/index.ts
#	new file:   packages/agents/src/experimental/channels/__tests__/tui-args.test.ts
#	new file:   packages/agents/src/experimental/channels/__tests__/tui-render.test.ts
#	new file:   packages/agents/src/experimental/channels/__tests__/tui-view.test.ts
#	new file:   packages/agents/src/experimental/channels/web/tui/app.ts
#	new file:   packages/agents/src/experimental/channels/web/tui/args.ts
#	new file:   packages/agents/src/experimental/channels/web/tui/components/blocks.ts
#	new file:   packages/agents/src/experimental/channels/web/tui/components/footer.ts
#	new file:   packages/agents/src/experimental/channels/web/tui/components/modal.ts
#	new file:   packages/agents/src/experimental/channels/web/tui/highlight.ts
#	new file:   packages/agents/src/experimental/channels/web/tui/main.ts
#	new file:   packages/agents/src/experimental/channels/web/tui/socket.ts
#	new file:   packages/agents/src/experimental/channels/web/tui/theme.ts
#	new file:   packages/agents/src/experimental/channels/web/tui/view.ts
#	modified:   pnpm-lock.yaml
#
@cjol
cjol force-pushed the channels/09-pi-tui branch from e463b13 to 1da3166 Compare October 2, 2026 18:53
@cjol
cjol merged commit 5a7643e into main Oct 2, 2026
24 of 30 checks passed
@cjol
cjol deleted the channels/09-pi-tui branch October 2, 2026 19:20
@github-actions github-actions Bot mentioned this pull request Oct 2, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant