The version check compared Version strings, but during development both
client and server report "devel" — so stale servers were never restarted.
BuildID is now derived from the executable's modification time, which
changes on every recompilation (including go run). The server exposes it
via /v1/version and the client checks both Version and BuildID match.
Assisted-by: Crush:AWS Claude Opus 4.6
Remove the auto-publish from DiscoverWithStates so that
discoverSkills in the coordinator is the only code path that
emits skill discovery events. This eliminates the double-publish
where DiscoverWithStates would emit user-only states, followed
by the coordinator emitting the merged builtin+user states.
Update the Discover tests to call DiscoverWithStates directly
and assert on the returned states slice, rather than subscribing
to the pubsub broker. This also lets those tests run in parallel.
Resolve race condition by implementing a package-level cache for skill discovery states. Add ~/.agents/skills/ and ~/.claude/skills/ as global skill scan paths.
Each frame the chat list is turned into a string with ANSI color codes
that we then have to parse back into cells before drawing. When the
chat list output is the same as the previous frame, we now reuse the
cells we already produced and skip the parsing step.
Co-Authored-By: Charm Crush <crush@charm.land>
Bedrock routes through Fantasy's Anthropic implementation with a
different display name, so provider options must still be filed under
anthropic.Name for the language model to pick them up. Previously the
bedrock provider type was missing from getProviderOptions entirely, so
reasoning_effort and think were silently dropped.
Co-authored-by: JJ Bot <john.jansen+bot@me.com>
While a model is streaming a reply, we used to re-render the entire
message text every time a new chunk arrived. Now we keep the rendered
output of the parts that are clearly finished and only render the
trailing piece on each update. The check for what counts as a finished
part is conservative on purpose, so anything risky (open code blocks,
lists, tables, html blocks, link references) still falls back to a
full render. The visible output is the same.
Co-Authored-By: Charm Crush <crush@charm.land>
Previously the chat list pulled the entire rendered output of every
on-screen item into a buffer and then trimmed it down to the viewport
height at the end. For very tall items, like a long reasoning block,
that meant building a buffer with thousands of lines just to throw
most of them away every frame. The list now stops collecting lines
as soon as the viewport is full, so per-frame work is bounded by the
visible window. Output is unchanged.
Co-Authored-By: Charm Crush <crush@charm.land>
The chat list now remembers what each item rendered last frame and
reuses it when nothing about that item has changed. Once a message
turn is fully done, its rendered output is reused verbatim on every
subsequent frame for as long as it stays unchanged. Resizing the
window, focusing or selecting an item, dragging a selection over it,
toggling a thinking block, and any other state change still triggers
a fresh render. The visible output is identical to before; the work
to produce it is much less.
Co-Authored-By: Charm Crush <crush@charm.land>
There still seems to be a TUI-sync issue on upstream/main where
the assistant cuts off mid-sentence on screen even though the
DB has the full response. Two recent PRs probably get most of
the way to fixing it: #2840 reordered the agent's
TypeAgentFinished notification past the activeRequests cleanup,
and #2836 made channelBufferSize actually honored. In
power-user cases the default (64) is still too small, though.
A streaming turn calls messages.Update on every OnTextDelta /
OnReasoningDelta callback, and the TUI's glamour re-render
periodically stalls the Update loop for tens to hundreds of
milliseconds at a time. 64 slots fill before drain catches up
and the non-blocking publish in broker.go silently drops the
rest.
This diff adopts a pretty aggressive fix -- 64 -> 4096 covers a
long turn at typical SSE rates even under pathological View()
stalls. Cost is a few MB of ring-buffer allocation across all
brokers, comfortably inside any modern dev machine's budget;
users on tighter memory targets can still tune via
NewBrokerWithOptions.
Drive-by cleanup: maxEvents (struct field + NewBrokerWithOptions
param) is set to 1000 by default but never read anywhere in
tree -- it's bufferSize, not maxEvents, that actually bounds the
queue. Drop maxEvents on the theory that misleading dead code
is worse than no code.
💘 Generated with Crush
Assisted-by: Claude Opus 4.7 via Crush <crush@charm.land>