Conversation
Driver: an error listener on the child before any await, a bounded ACP handshake, a PATH pre-flight naming the missing binary, fallback to a new session when the stored one cannot resume, an expected flag on exit events so a requested stop is not reported as a crash, and a permission fallback that cancels rather than throws. Supervisor: one turn at a time per agent with held semantics for prompts that land mid-turn, a denylist child environment that keeps the login shell's SSH/proxy/token vars and adds the TLS CA and Dispatch vars, stopAll at shutdown, restoreRunning at boot, and overlay removal on stop. Recorder: per-row size bounds with a truncated flag, coalesced chunk writes, and tool paths relative to the agent's cwd. Prompt dispatch moves into AgentManager.getPromptTarget/promptDsh, launch guidance inputs share one helper, extractAppendedSystemPrompt replaces the codex-branch trick, payload types live with the store, the chat envelope lookup folds into delivery, dispatchMcpUrl takes only what it reads, and the unused StreamStore.latest is gone. dsh is opt-in (DEFAULT_ENABLED_AGENT_TYPES), documented in the runbook, with an update-migrations manifest for the release. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Activity rows no longer swallow the agent's header: layoutFeed leaves lastPost untouched for them, and the first row of a run renders inside a Post with the agent's avatar. Streaming rows version by text length so the pane keeps following as a reply grows, without flickering the new-messages pill. The diff is a line-aligned LCS with coloured +/- rows and treats a null old text as an empty file. Activity rows are buttons only when expandable, announce title and state, pulse while in progress, and mark a failure with an icon and colour; paths show relative and keep the title readable. The writing indicator is a labelled status row that respects reduced motion. Truncated rows say so. Also: the turn chain survives a rejected turn, a spawn failure reports the child's exit reason over the aborted handshake, and per-row writes are chained so a timer flush cannot land after close. The unread gap for stream entries is recorded in the spec. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
entryVersion keys the enter animation and must not change per chunk; the pane's follow logic keys on a separate entryGrowthKey that adds text length and status. DiffBlock memoizes its alignment across feed refetches. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Activity rows group under the previous agent row (post or activity); the assistant post after a run still opens its own group. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…e default model restoreRunning ran inside initializeApp, before app.listen, so a resumed harness could not attach Dispatch's MCP endpoint and every restore failed. It now runs from start() after listen. A fresh dsh session receives the agent's launch-context post as its first turn (dsh takes no launch argument). With no model chosen, the overlay picks a model whose provider key the service has instead of the profile's DeepSeek default. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The create dialog offers the model picker for dsh; the Agent pane opens on Chat for a dsh agent whether or not the global chat-surface flag is on, quick prompts route through Chat for it, its launch post is durable, and its launch guidance assumes Chat. The type icon is the fish, dsh's own mark, instead of a text badge. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The launch guidance for dsh drops the pane-era chat rule (which sent every reply through dispatch_chat_post, so the first live turn answered twice) and states that replies stream into Chat, with dispatch_chat_post only for a question with options. dsh's ACP tool calls carry no kind, so the recorder infers one from the tool name. dsh reports no token counts on the wire; recorded in the spec with the harvester fix. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…Dispatch tokens Contracts, pure reducer, formatting helpers and the braille stream ticker come over from @mytraai/promptkit trimmed to what dsh emits. The six primitives (PromptLine, StepRow, StepDetail, ActivityBlock, ResultTurn, TurnStream) are rewritten on shadcn and Dispatch's status tokens, with a module registry in place of the React context. Tailwind gains the three harness keyframes. Also rebrands the agent type: 'Dispatch' in menus, 'Dispatch Harness' in settings, and the Dispatch brand mark in place of the fish icon. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…pane The turn stream (PromptKit port) becomes the default view for a Dispatch Harness agent, with Chat and Console behind a three-segment toggle. Turns come from the harness turns route, refresh on chat.changed, and prompts go through the chat composer so cross-agent messaging is unchanged. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…on it The stored pane view now starts as null so a Dispatch Harness agent opens on the Harness view instead of the atom's old "chat" default. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ersion Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ches Turn settles reach the web as agent events, not chat.changed, so the Harness view sat on a stale trace until a reload. Invalidating alongside the feed covers agent upserts, reconnects, reviews and messages. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…r dsh skills Steps keep the tool call's raw input (dsh sends it), so an execute step reads as its command, a read step as its file, and an MCP tool as its name with a digest of its arguments; steps with nothing underneath are no longer expandable. Locations fall back to the input or read wrapper. Dispatch Harness agents lose the redundant Chat segment: the toggle is Harness | Console and a stored Chat preference lands on Harness. Typing / in the Harness composer opens a picker over the skills dsh can load (project .agents/skills, .dsh/skills, and the harness home), served by GET /api/v1/agents/:id/harness/skills; the persona tells the agent a leading /<name> means load that skill. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…g, keyed providers only dsh advertises session config options (a provider-grouped model select and a reasoning-effort select) over the Agent Client Protocol. The driver keeps them per session and can set one; the supervisor persists a model switch on the agent and drops provider groups whose API key the service lacks. The create dialog's dsh list now comes from a cached probe session, so every model dsh serves (gpt-5.6-sol, terra, …) is offered; dsh model ids validate by shape. The Harness pane shows a model · effort chip that opens the picker; /model in the composer opens it too. Also stops the turn stream snapping to the bottom on refetch: it follows only while the reader is at the end. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…ugh the picker Radix Select refuses an item whose value is ""; dsh's reasoning-effort list opens with one. The picker holds values under a stand-in and decodes them at the edges. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A persona launch (and an MCP launch) hands the kickoff as initialPrompt, which CLI agents type into the pane. A dsh agent's first turn is read from the Chat launch post, which only carried the create dialog's context prompt, so persona reviews on Dispatch Harness agents started with no brief and sat idle. The launch post now falls back to initialPrompt for dsh agents. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…6-only catalog Step details now format what they show: read output becomes a highlighted code block with a line-number gutter, path lists render as paths, JSON tool output and arguments pretty-print with highlighting, and shell output stays plain. Prompts Dispatch injects (review thread updates, persona kickoffs, the rename nudge) render as a one-line notice with the full block on demand instead of as something the user typed. The harness agent's view segment reads "Chat". The dsh model catalog carries the provider as a group for section headers, and OpenAI offers only the gpt-5.6 generation (plus whatever a session is already on). Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The release notes, the runbook and one line of Settings copy described a Harness view and an Agent-types opt-in, neither of which ships: a dispatch agent reads in the Chat feed, behind the new Dispatch Harness setting. Four more references outrun the plan's list. Three name a view that is deleted rather than moved, in the release notes' motion bullet and in two doc comments; the fourth, on the publishHarness hook, still promised that this plan would drop the coarse event, which it narrowed instead. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The turn payload's prompt path was spelled twice, once in TypeScript off TurnPayload and once as a SQL literal in the feed's anti-join. Only one of them is compiler-checked, and a rename would have left every chat prompt rendering twice, so the path now has one owner beside its TypeScript reader. HarnessQuestion had no reader outside the server's assembler and no wire message carried it, so it moves next to AssembledTurn as AssembledQuestion rather than sitting in the client/server contract package inviting a client to import it. ResultTurn's showTime and isStreaming were both unreachable: its one caller pins showTime false and never passes isStreaming, and the streaming placeholder went with the turn stream. useHarnessQueued and useHarnessQueue differed by one character in the same module and were called on adjacent lines; the query is useQueuedPrompts now. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Three reviewers found the anti-join shipping without an index. Nothing covered kind or the prompt path, so Postgres answered it with a sequential scan that was not even restricted to the agent: one agent's feed page slowed down as a different agent accumulated rows. Measured on a synthetic 60k-row agent, 64.6ms and 8576 buffers become 2.5ms and 413. Two reviewers found the publish racing itself. Each recorder flush composes the whole open turn, so two composes overlap on a slow disk and the older one can publish last, leaving a turn short and still marked streaming. Nothing corrected it, because that turn's own event no longer refetches the feed and the flush that settled it was the last one. There is now one compose per agent at a time with a single trailing re-run, and the client ignores a turn whose updatedAt is older than the row it already holds. A prerelease install also lost its harness opt-in on update, silently: the type was turned on by adding dispatch to enabled_agent_types, which is now stripped on read, and a running dispatch parent's persona launch would start throwing mid-turn. A migration carries the old choice onto the new flag. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
A turn is anchored where it started, so the status event an agent emits mid-turn becomes the tail entry while the turn keeps growing. Following read growth off the tail alone, so a reader pinned to the bottom drifted off it the moment the agent reported its first phase, which CLAUDE.md has every agent do. ArrowUp took the newest queued prompt whatever its source, so a child agent's message queued behind a running turn could be deleted undelivered and land in the user's draft to be re-sent as their own. It takes the newest chat-sourced one now, the same rule the history walk beside it already used. Template launches and job runs read an agent type straight from their request body and reached createAgent without consulting the offered list, so dispatch agents were creatable with the harness flag off. The gate moves to createAgent, which every creation path goes through, before the media directory is made. Two E2E defects: both harness specs left a server-wide flag on, which the settings assertion only survived by file ordering, and the turn seeder's seq arithmetic double-counted, writing 1, 3, 6, 10 where the recorder writes 1, 2, 3, 4. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Folding the harness into the chat pane handed a dispatch agent the unread badge, the child-agent filter and the copy action. None of the three could see a turn, which is the only thing that type produces. Unread counted chat rows, and a harness answer is never one, so the badge could fire only for a question the agent asked. Turns carry no per-row read state, so agents gain a chat_read_at watermark: a full mark-read moves it, and both the pane count and the sidebar summary now add the turns that settled after it. Existing rows are stamped at migration time so a backlog does not light up every badge at once. The child filter matched agent_message entries only, so hiding a child conversation hid the outbound half and left the turn the child's message drove. ChatTurnPrompt carries senderAgentId now, which the server had already parsed and thrown away. A cross-agent message rendered twice: once as its own row, once as the turn's prompt, in two cards that disagreed, and minutes apart whenever the prompt had queued. The turn stops drawing its own copy; the row that carries the sender's badge and id is the single rendering. Also: the turn's answer gets the copy button its prompt always had, the empty state stops telling a dispatch user to look in a Console its agent never writes to, the status line stops repeating the sentence the composer prints under the field, and the queue hint says which message ArrowUp takes. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Three fixes the reviewers found in the batch before this one. The read watermark never moved. It was gated on an unbounded read, but the pane always sends a bound (the newest agent message it holds), so the gate never fired and a settled turn stayed unread forever. Every read moves it now, and it moves to now rather than to the bound: on a harness agent that bound can be far older than the turns under it, so honouring it would leave them unread just as permanently. A route test covers the path the pane actually takes; the store test did not. A settled turn also invalidated nothing, so the badge waited for a refocus to appear. The chat.entry handler counts one now. Filtering child agents hid a child-started turn, which was an overreach once the turn stopped rendering the child's words: what is left is the parent's own rail and answer, no other agent type's filter hides the agent's own output, and an agent driven entirely by children would show an empty pane. Reverted; senderAgentId stays on the wire. Composing a turn entry reads the whole open turn and the recorder asks about ten times a second, so a server with no browser attached was paying for an announcement nobody received. It skips when no client is connected; a reconnecting one refetches from the rows anyway. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…ess-provider-auth-ux # Conflicts: # apps/browser-extension/package.json # apps/browser-extension/public/manifest.json # apps/server/package.json # apps/server/src/chat/service.ts # apps/server/src/chat/store.ts # apps/server/test/chat-service.test.ts # apps/web/package.json # apps/web/src/components/app/chat/chat-feed.test.tsx # package.json # packages/shared/package.json # release-notes/current.md
An install that ran the v0.38.14 release has 0051_agent-chat-reactions in pgmigrations where this branch ships 0051_agent-stream-events. node-pg-migrate compares the stored names against the shipped files position by position, so the server threw before the first migration ran and every start after the update crash-looped. The record is dead bookkeeping: this branch ships the same table as 0057_agent-chat-reactions, and that file is guarded with IF NOT EXISTS, so re-applying it on top of the existing table is a no-op. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
…resence Every dispatch_event landed twice: as a row in the feed and, in the same words, in the presence line above the composer. The row was the duplicate, and each one moved the reader's tail and fired the follow logic for content that said nothing new. Status entries now lay out no row at all; the server still emits them and the presence line still shows the latest one. The presence line also moves above the harness chrome, so what the agent is doing now reads before the model, auth and usage chips, which are controls rather than news. Non-harness agents see no change: their chrome is null. Removed with it: collapseFeed, StatusLine and the status cluster in ChatFeed, which had no other callers. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
The feed followed the bottom with an instant scrollTo after each render, so every change in the live turn's size was a snap: a step row landing, the thinking row appearing after its 500ms delay and vanishing on the next tool call, a streamed line, and the largest one, the rail folding from its full height to a one-line summary on settle. Depending on the size of the answer the whole feed jumped by that much at once. The turn's rail and answer now sit in one AutoHeight body, a wrapper that measures its content with a ResizeObserver and animates its height between sizes on the harness's standard curve (instant under reduced motion). The pane pins the bottom through those frames with a ResizeObserver on the feed content while the reader is following, so older rows glide upward instead of the reader chasing the bottom. Measured on a live turn at 50ms: the distance from the bottom stayed at 0px throughout, and the settle collapsed 180 to 74px across four samples rather than one. Without a ResizeObserver the wrapper stays auto and the content sizes itself as before. Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
A pass over the whole branch for code and comments that carry no weight. Server: unused types and a struct field that was never read (StatusPayload, StreamPayloadByKind, Live.stderrTail), exports nothing imports (TERMINAL_OUTPUT_MAX_BYTES, AUTONOMOUS_IDLE_MS, modelOptionOf, isUsageEngineId, HARNESS_MODEL_HALF, the feed limit re-exports, SOURCE_RANK, locationsFromInput), the three unreachable 503 branches on the harness usage routes (registerSystemRoutes has one caller and it always wires them), and the chatSurface flag buildHarnessPersona took and never read. Web: the PromptKit-parity fields the server never emits (retry and skipped step statuses, clarification results, attempts, context chips, error hints, prompt attachments) and the PromptLine branch that rendered a typed prompt, which the turn entry never reaches since typed prompts render as the user's own post. The ungrouped AgentModelSelect path no longer carries the group rendering the grouped early return already handles. Two dead exports in chat-composer. Tests and fixtures: an unused stderr handle on the fake ACP agent, a duplicated constant, stderr traces in the E2E fake, and comments that named review rounds or people instead of behavior. The assisted-update manifest now counts the migrations this release ships and lists the v0.38.14 reactions record the runner forgets at boot. Co-Authored-By: Claude Fable 5.1 <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.
Summary
Validation
pnpm run checkpasses.Product decision
Strict ACP completion selected: unmarked post-prompt goal rounds require an explicit adapter lifecycle signal.