reassess-roadmap: flip default from true → false. Most reassess units
conclude "roadmap is fine" burning a session for no change; the
plan-slice prompt now carries a JIT preamble at zero cost. (#4778)
tool-execution: always prefer toolDefinition.label when non-empty,
even when label === name — allows tools to display their canonical
name explicitly. (#4758)
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- model-registry: export PROXY_FAMILY_PRIORITY and GLOBAL_PROVIDER_FALLBACK
constants; add getModelsForProxy() returning candidates ordered by family
priority then global fallback (opencode → opencode-go → openrouter →
ollama-cloud); add getModel() convenience wrapper
- proxy-server: add priorityOverrides option; handleChat iterates all
candidates in priority order and falls through to the next on 429
- settings-manager: add ProxySettings type with providerPriority override
map; add getProxyProviderPriority() / setProxyFamilyProvider() accessors
- settings-selector: add ProxyPrioritySubmenu — a two-level TUI submenu
(family → provider) that dynamically generates entries from
PROXY_FAMILY_PRIORITY; wired in interactive-mode with full callback
Family defaults: MiniMax→minimax, GLM→zai, Kimi→kimi-coding,
MiMo→global-fallback, Gemini/Gemma→google-gemini-cli, Claude→anthropic,
GPT/o-series→openai
https://claude.ai/code/session_013BwmqG3NuwwZY3vsUb4Y9Y
Co-authored-by: Claude <noreply@anthropic.com>
Updates channel prefixes, log messages, comments, and configuration values
across daemon, mcp-server, and related packages to complete the rebrand from
gsd to sf-run naming.
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When message_end does not fire before agent_end (e.g. abort path), the
agent_end case was calling chatContainer.removeChild(streamingComponent),
which silently erased the last assistant message from the TUI chat history.
Fix: follow the message_end finalization pattern — call setShowMetadata(true)
and updateContent() before clearing the reference. Never call removeChild on
a component that was added to the persistent chat history.
Closes#4197
- DynamicBorder: verify lastExternalRender tracking suppresses redundant
renders during streaming, and standalone renders fire when idle
- TUI clearOnShrink: verify debounce flag lifecycle — deferred shrink
preserves maxLinesRendered, flag resets when content grows back
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
rebuildChatFromMessages() called populatePinnedFromMessages() which
re-populated the pinned zone with text already present in the chat
history, causing visible duplication during session state changes.
Additionally, the spinner interval at 80ms generated ~12.5 renders/s
for a purely cosmetic animation, and clearOnShrink triggered
unnecessary full redraws during pinned-zone transitions.
- Remove populatePinnedFromMessages() from rebuildChatFromMessages()
and add pinnedMessageContainer.clear() instead — the streaming
lifecycle in chat-controller manages pinned content during active work
- Reduce spinner interval 80ms→200ms with render-batching that skips
redundant renders when streaming already triggers requestRender()
- Debounce clearOnShrink: defer full redraw by one render tick so
pinned-clear→new-streaming transitions avoid a wasted full redraw
- Increase notification widget safety-net timer 5s→30s since the
store subscription already handles push-based updates
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Commit c8c416802 (#4144) introduced module-level renderedSegments state
to track interleaved text/tool components per assistant turn, but never
reset it when an adapter shrinks streamingMessage.content[] back to 0/1
at a provider sub-turn boundary within one assistant lifecycle (the
claude-code adapter does this). Consequence chain: the segment walker
finds the stale text-run entry at startIndex=0, calls updateContent on
it with the new (shrunk) message, and the in-place edit destroys the
prior sub-turn's visible text. New tool blocks at contentIndex=1 then
collide with stale registrations, causing visual ordering corruption.
hasToolsInTurn stays sticky-true and lastPinnedText never clears, so
the pinned "Working - Latest Output" mirror freezes on the pre-shrink
snapshot.
Track lastContentLength explicitly. On shrink, clear renderedSegments,
reset lastPinnedText, and reset lastProcessedContentIndex so the
walker treats the new sub-turn as fresh segments that append after
prior sub-turn children. Prior history stays rendered as frozen
components; pendingTools and the spinner border are untouched.
Adds two regression tests in chat-controller-ordering.test.ts: one
verifies prior sub-turn components are not overwritten and new tools
append in content[] order after a shrink, the other verifies the
pinned markdown updates from the first sub-turn's text to the second
sub-turn's text across a shrink boundary.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
InteractiveMode.renderWidgets() called Container.clear() on the
widgetContainerAbove/Below render mounts, which disposed every mounted
extension widget and then re-added the now-dead components. In AUTO mode
updateProgressWidget re-registers gsd-progress on every unit dispatch,
so gsd-notifications and gsd-health had their refresh timers and store
subscriptions killed after the first dispatch. Renders kept returning
the widgets' frozen cachedLines, making them look alive but never update
(/gsd notifications clear appeared to do nothing, belowEditor last-commit
went stale while the top-of-screen dashboard stayed correct).
Split detach from dispose: add Container.detachChildren() and use it from
the two widget-mount call sites. clear() still disposes for every other
caller (chat, editor, status, pinned-message containers). The
extensionWidgets* maps remain the single owner of widget disposal via
removeExisting() and clearExtensionWidgets().
While in AUTO, gsd-progress duplicates gsd-health on last commit, cost/
budget, and the health signal. Make gsd-progress the single source of
truth: hide gsd-health from auto-start and re-register it from every
exit point in auto.ts (lock-lost stop, cleanupAfterLoopExit !paused
guard, stopAuto, pauseAuto). gsd-notifications stays visible — it is
independent state and, with the detach fix, its subscription + 5s
refresh actually work again.
Tests: Container.detachChildren()/clear() contract guards added to
packages/pi-tui/src/__tests__/tui.test.ts. health-widget,
notification-{store,widget,overlay}, notifications-handler, notifications,
and auto-paused-ui-cleanup suites all pass.
Previously the chat-controller created one AssistantMessageComponent per
assistant message and removed/re-appended it to the chat container's tail
on every tool block, forcing all narration after every tool execution
regardless of stream order. Users had to scroll up to read text that was
written before each tool call.
Replace the reorder hack with a stream-order segment walker that walks
content[] left-to-right, collapses contiguous text/thinking blocks into
text-run segments, emits one segment per tool block, and append-only adds
new segments to chatContainer. AssistantMessageComponent gains a
ContentRange API so a single message can spawn multiple text-run
components, plus a separate showMetadata flag so timestamp/error footers
render only on the trailing segment without duplicating earlier text.
Adds a regression test that streams [text, tool, text, tool, text] and
asserts both interleaved order and per-segment rendered text content.
Closes#4144
Extract the post-tool text-block selection logic into a small pure
helper (`findLatestPinnableText`) so the regression scenario can be
covered without standing up the full interactive controller harness.
The new test pins the bug from #4120: when content blocks are
`[text1, tool1, text2_streaming]`, the helper must return `text1`
(not `text2`), because `text2` is still streaming live into the chat
container and mirroring it would render the same tokens twice.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The pinned `Working · Latest Output` border above the editor mirrors
the assistant's latest text block while tools run, so prose stays
visible after a tool's output scrolls it off-screen. The mirror walked
content blocks from the end and picked the last text block — but when
the assistant streams a *new* text block after a tool call (sequence
`[text1, tool1, text2_streaming]`), it picked `text2`, which was also
being streamed live into the chat container. Result: identical tokens
rendered in two places at once.
Restrict the search to text blocks whose index is strictly less than
the index of the most recent tool call. Text after the last tool call
stays in the chat container only; earlier prose (e.g. `text1`) remains
mirrored the entire time the new text streams, so context isn't lost
and the loading-animation handoff is undisturbed.
Fixes#4120
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Strip the `mcp__<server>__` prefix from tool_use blocks emitted by the
Claude Agent SDK so registered GSD extension renderers (gsd_plan_milestone,
gsd_task_complete, etc.) match instead of falling through to the generic
JSON-dump fallback. The original server name is preserved on the toolCall
block under `mcpServer` for downstream rendering.
Tighten the generic ToolExecutionComponent fallback for any remaining
prefixed names (third-party MCP servers): show a muted `server·tool`
title, render primitive args as compact `key=value` pairs, and truncate
output to 10 lines when collapsed.
Filter models whose provider has no working API key or OAuth out of
every user-facing selection path. Previously, stale defaults and scoped
sets could leak unconfigured models into /model, /gsd model, and auto
run — the user could "pick" a model that immediately threw on use.
- model-selector: filter scopedModels via isProviderRequestReady;
default to "all" scope when no scoped model is ready.
- model-controller: same filter for getModelCandidates, so exact-match
resolution from /model <term> can't return an unauth'd scoped model.
- model-resolver: gate findInitialModel step 3 on provider readiness so
a stale saved default falls through to the available-models path.
- startup-model-validation: check configuredExists against getAvailable
instead of getAll, so a configured-but-unauth default triggers the
fallback picker and thinking-level reset.
- auto-start: validate resolveDefaultSessionModel against the live
registry + auth before snapshotting, and warn when PREFERENCES.md
names an unconfigured model.
https://claude.ai/code/session_015q6b23ap9Pyqdogzz2FXGh
The pinned "Latest Output" zone was only cleared at agent_end, but during
flows with form elicitation (e.g. discuss-phase), there is a gap between
message_end and agent_end where the agent waits for user input. During this
gap, the same content was visible in both the chat history and the pinned
zone. Clear the pinned zone at message_end when the assistant message is
finalized in the chat container.
Restores the pinned assistant output zone that shows the latest narration
during tool execution. Adds markdown rendering, animated spinner, height
capping to prevent render flashing, and session rebuild support.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Stream tool results in real-time during Claude Code SDK sessions
instead of deferring until session end. Tool calls (read, bash, write,
etc.) now show their output as they complete, not collapsed as "..."
- Stop suppressing toolcall_start/delta/end events from stream adapter
so the TUI can render tool call progress during streaming
- On SDK turn boundary (user message with tool results), push synthetic
toolcall_end events with externalResult attached for immediate rendering
- Chat controller checks for externalResult on toolcall_end message
updates and calls updateResult on pending ToolExecutionComponents
- Fix case-sensitive tool name matching (Read vs read, Bash vs bash)
in TUI ToolExecutionComponent rendering
- Auto-discover and pass GSD_WORKFLOW_EXECUTORS_MODULE and
GSD_WORKFLOW_WRITE_GATE_MODULE env vars in MCP server launch config
- Add /gsd mcp init command and auto-bootstrap .mcp.json for Claude
Code provider during auto-start
- Add tool_execution_update event type for web UI streaming updates
- Add setStderrLoggingEnabled toggle for workflow logger