feat(chat): launch prompt delivered as a Chat message; no Chat for terminal sessions - #1050
Merged
Conversation
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…sions Round 5 of the chat surface. With the flag on and a launch post recorded, the CLI's first user turn is that post wrapped in the same DISPATCH CHAT envelope a Chat message is injected with — its id, its attachment lines, and the trailer pointing the agent at dispatch_chat_post — so an agent started from the Chat tab answers there instead of in the terminal. The post id is minted before the command is built and handed to ChatService.prepareLaunchContext, which resolves the attachments and hands back both the envelope lines it will store and the write itself, so the pane and the feed describe the same attachments and the envelope names the row that was actually written. Resolving is bounded (it is on the launch's critical path): on timeout the agent launches unwrapped with no post. Unwrapped otherwise too — flag off, no context, job runs, terminal agents. Web: a terminal session has no CLI to chat with, so agentSupportsChat is ANDed with the flag once in agents-view and the narrowed value drives the tab label, the pane, the split-pane normaliser and the /chat redirect. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Four security-review findings plus one regression on the round-5 launch envelope: - Neutralize envelope markers in embedded text. buildChatEnvelope escapes any line of its body matching the marker grammar, so a prompt or an attachment cannot close Dispatch's block and open a forged one naming another message id. ChatService.post now rejects a replyTo that is not a message on the posting agent's own feed. - A durable post is the precondition for the envelope. createAgent awaits both the resolve and the write before building the wrapped turn, and drops the envelope on rejection, timeout or an id collision (ChatStore.insertIfAbsent is ON CONFLICT DO NOTHING). - The post says when it shows less than the turn: an explicit truncation note past CHAT_MESSAGE_MAX_CHARS, and a count of the attachments the 20-attachment cap left off. - Only a wrapped launch pays for the post. The chat-surface flag is read once, up front; a flag-off, job, terminal or inert launch keeps the round-4 detached path. - The enveloped turn lists every startup file, link and pin again, not just the first 20. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
…ate cleanly Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
selfcontained
marked this pull request as ready for review
September 4, 2026 05:59
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
Round 5 of the chat surface. Two changes, both from live use.
The launch prompt arrives as a Chat message
With the flag on and a launch-context post recorded, the CLI's first user turn is now the prompt wrapped in the same
--- DISPATCH CHAT (id: …) ---envelope a composer message uses: the attachments block (startup files, links, pins) and the trailer telling the agent the user reads Chat and to reply withdispatch_chat_post. So an agent started with context replies in Chat, reply-linked to its launch post, instead of only in the Console.ChatService.prepareLaunchContext, so the pane and the feed post share one id and one set of attachment lines.Terminal sessions never offer Chat
A terminal session has no CLI to talk to, so with the flag on it shows a plain Terminal tab, Console only: no toggle, no unread badge, split values fold to the terminal view, and
/agents/:id/chatredirects to the bare route.Deviation worth reading
Resolving the launch post is now on the launch's critical path, since the first turn needs its id (round 4 only had to keep the write off the path). It's bounded by a 5s resolve timeout: on timeout or error the agent launches unwrapped with no post, so the pane can never name a row that was never written. The write stays detached and bounded as before.
Reviews
Backend security and general code review filed five items; a verification pass found two more. All seven are fixed and verified:
dispatch_chat_postnow requiresreplyToto be a message on the posting agent's own feed.ON CONFLICT DO NOTHINGinsert, plain prompt with no post otherwise.failCreate, so a rejecting query can no longer strand an agent row increating.Checks
pnpm run check, prettier, fullpnpm run test(4948 passed),finalize:web, E2E (195 passed). Hands-on: a Claude agent launched with a prompt, a file and a link showed the envelope in its Console and replied in Chat withreplyToset to the launch post; a terminal session showed no Chat.🤖 Generated with Claude Code