Skip to content

Xeo Forge v1.22.0 — Local-First Agent Workbench

Choose a tag to compare

@github-actions github-actions released this 27 Aug 21:43
· 44 commits to master since this release
3623d66

v1.22.0 — One Language

Summary

Two user-reported defects drove this release, and both turned out to be
honesty problems wearing UI clothes.

First: the chat surface could hang forever in "Thinking" with a locked
composer — the user watched a stuck background process that never finished
and never explained itself. Root cause: the client treated the SSE stream
as the only source of status truth. Any missed done event (a dead
EventSource, a provider crash without a terminal event, an Electron reload
orphaning the stream) left the status "running" permanently — and the
cancelled status was not even in the client's terminal check, so the
composer stayed locked after a stop. The DB row knew the run was over; the
UI never asked.

Second: the app read as two different products. The workspace and settings
pages spoke the black minimal design language, while leftover unicode
glyphs — ⌘K ×
••• — read as placeholders from another era, and
the live work session still carried a handful of them. Every glyph is now a
stroke icon from one inline library, sized and colored by the same tokens.

The icon system

components/icons.tsx grew from 7 to 29 icons (Lucide geometry, MIT, zero
dependencies — inline SVG with currentColor strokes so themes stay
correct for free). The set now covers direction (arrows, chevrons, panel
collapse/expand), objects (search, message, folder, settings gear, user,
plug, command), actions (play, plus, stop, x, check), states (circle,
help-circle), and appearance (sun, moon, monitor). Every consumer was
rewired: the workspace sidebar and topbar, session rows, capabilities nav,
settings back-link and section arrows, MCP server rows and approval mark,
provider rows and form dismiss buttons, theme segmented control, dialog
close, command palette, app shell, diff rename arrows, thinking block,
tool ledger disclosure, run-failure mark, login brand marks, and the admin
back link. The new glyph-eradication smoke scan proves the vocabulary is
gone from every .tsx — with two documented exemptions: the icon library's
own doc comments, and setMod('⌘'), which is a keyboard key label
(rendered as "Ctrl" on Windows), not an icon.

The run that never lies stuck

The fix is a contract change: the stream is an input; the task row is
the truth.

  • isTerminalTaskStatus (in runtime-state.ts) now encodes the server's
    full terminal vocabulary — completed / failed / cancelled /
    planned — so a stop or a promote can no longer strand the UI in a live
    state.
  • While a turn is live, both the chat and work surfaces re-read the task
    row every 4 seconds and adopt the server's terminal status. The missed-
    done family of hangs self-heals within one poll, regardless of cause.
  • If the server finished but nothing streamed to this client, the persisted
    answer is already in the database — the client refreshes and renders it
    verbatim rather than showing an empty completion.
  • A Stop control is rendered for the whole live turn (not only before
    the first token), wired to the real cancel route; the reconciliation poll
    adopts the resulting cancelled status even if the stream never
    delivers it.
  • If the live connection drops, the surface says so honestly ("Live
    connection interrupted — still reconciling with the saved task state")
    instead of silently pretending to think.
  • The chat done-handler now reads the streamed text through a ref mirror,
    fixing a stale-closure dedupe that could double-append a summary.

Verification

Local environment cannot install node_modules (better-sqlite3 native
build — same limitation as v1.21), so verification is the same honest
three-part shape: TSX syntax validated via esbuild across all 22 touched
files; a pure-module smoke (scripts/smoke_ui_unify.mjs) passing 12/12
under Node 24 type-stripping — terminal-vocabulary behavior, both
reconciliation contracts, glyph eradication, icon-library integrity; and
test/chat-hang-reconciliation.test.ts (+13 assertions in CI) pinning the
regression permanently: the terminal vocabulary, both surfaces'
reconciliation behavior, the Stop hatch, and the honest stream-loss state.