Public
Remixed
Interactive chat UI for a Claude Managed Agent
Val Town is a collaborative website to build and scale JavaScript apps.
Deploy APIs, crons, & store data – all from the browser, and deployed in milliseconds.

Claude Managed Agent — Chat UI

An interactive chat UI for talking to a Claude Managed Agent. Each thread is one stateful agent session — persistent history plus a sandbox with bash, file editing, and web tools. Hono (backend) + React (frontend), with Val Town SQLite for storage.

Where claude-managed-agents is a one-shot script, this is a multi-turn, multi-thread UI.

How it works

  • A thread = one session. The agent + environment are created once (lazily on first use) and their ids are cached in a config row — sessions pin to an agent version, so recreating them per request would be wrong. Each thread makes one session and reuses it for follow-up turns.
  • On a new user message the server inserts the user row, ensures the thread has a session, then opens the event stream, sends the message, and writes each event to SQLite as it arrives (agent.message → text, agent.tool_use → tool name, session.error → error), returning when the session goes idle.
  • The frontend polls GET /api/threads/:id/messages?after=<lastId> about once a second and renders new rows in order. No SSE to the browser.
  • The built-in toolset runs in Anthropic's sandbox with permission_policy: always_allow, so tools just run — no approval prompts.
  • Files the agent creates (images, charts, PDFs) are surfaced after each turn. When the session goes idle, backend/files.ts lists the session's files via the Managed Agents Files API (files.list({ scope_id })), downloads any new ones (files.download(id)), stashes the bytes in std/blob (key file_<file_id>), and writes a messages row for each — type: "image" for image/*, else type: "file". The row's content is JSON { id, name, mime }. The frontend renders images inline and other files as a ⬇ name download link, both pointing at GET /api/files/:id, which streams the blob bytes with the right Content-Type.

Setup

Set ANTHROPIC_API_KEY (from https://platform.claude.com/settings/keys). Everything else is created on the first message. Tweak the model or system prompt in backend/agent.ts.

Files

  • index.ts — Hono server + API routes
  • backend/db.ts — SQLite schema + helpers (threads, messages, config)
  • backend/agent.ts — Managed Agents: create-once config, per-thread session, per-turn streaming
  • backend/files.ts — surface sandbox files: Files API → std/blob → image/file message rows
  • frontend/ — React chat UI (App.tsx, Chat.tsx, api.ts)

Backlog (not built yet)

  • Stream reconnection. The turn request stays open for the whole agent turn. If it dies before session.status_idle, reopen the stream and replay events for that session_id from the thread's last_cursor to resume writing to SQLite — session state is persisted server-side, so it's recoverable.
  • Long turns may exceed Val Town request limits → tied to the reconnection / background-execution work above.