v0.1.6
Added
-
/updateand/restartin the REPL, the two halves of landing a
self-update./updatechecks the channel, asks before installing, and says
what to do next;/restartputs the running gray on the binary that is on
disk — the gateway daemon first (it keeps running the build it started
with), then this session re-exec'd into the new one, resuming the
conversation withresume --last. Neither codex nor hermes has either: codex
shows an update popup at startup and hands you abrew upgradeline, and
hermes-rs'srestarttears down LSP clients. A CLI that installs its own
updates owes you a way to land on them.The gateway half distinguishes who owns the process. Installed under a
supervisor, the supervisor restarts it. Running with nobody supervising it
— a hand-launchedgray gateway run— it is stopped and started again on
the new build, because stopping without the relaunch would take a working
gateway down and call it a restart. -
Foreign plugin packages work with zero per-plugin code:
gray plugin install <git-url>now also takes a package'scommands/*.mdand
.opencode/command/*.mdprompt files, any installed package'sAGENTS.md
is served into every turn, and its command files become slash commands
that run as prompts (ponytail's ruleset + six commands included). An
optional packagegray.jsondeclares a state file so/x mode-style
switches persist. Package code is still never executed: hooks, MCP
servers, and lifecycle scripts stay out of scope.
Changed
-
The gray mark animates.
assets/logo-animated.svggrows out of its own
centre: the gem blooms first, then each of the 25 facet lines draws
outward from the vertex nearest the centre, staggered by distance so the
core lands before the outline. The source path — one path, nonzero fill
rule, facet lines as windings that cancel — is reused verbatim as the mask
the lines grow inside, so a line can only ever paint pixels the finished
logo already has as line: the last frame is the logo, verified pixel for
pixel. Pure CSS with no JS, it runs inside<img>, and it degrades to the
finished mark when animation is unsupported orprefers-reduced-motionis
set.scripts/animate_logo.pyregenerates it (--linesfor the
transparent variant on dark surfaces,--frames N dirfor previews);
assets/logo-grow.mp4is the 1.9s preview. -
The startup banner is the gray ASCII logo again. The graychan art is
/heheonly, and/heheis a toggle: press it again to drop the art and
get the logo back. -
The
/modelpicker no longer waits on the network every open: the last
fetched provider list is cached on disk and paints instantly, the live
fetch refreshes it in the background, and a─ recent ─divider separates
your recent models from the full list. -
bashtakeswait_msonaction:outputandaction:status, so a single
call can await a job instead of polling it once per turn. Capped at 600s,
and every wait comes back with a liveness verdict: the log either grew
while we waited (still producing) or stayed silent (possibly stuck — the
agent's cue to inspect or cancel it).
Fixed
- A command that goes silent no longer blocks its tool call forever. The
blockingbashlane waited onchild.wait()with no bound and reported
nothing while it waited, so an external process that wedged at shutdown —
a Playwrightbrowser.close()race, reproduced in
docs/shell-hang-postmortem-2026-09-28.md— held one call for 9m44s until
the user cancelled, and the evidence that would have explained it (the
job's log, finished at spawn time) was invisible until the call returned.
A command that sets notimeoutand emits no new output for 600s is now
handed to a background job and the call returns immediately:still running · job <id> · silent: no new output for 600s · log <path>, not
killed, with the agent's cue to inspect, await or cancel it. A chatty
command is never touched — every output chunk resets the bound — and the
reverted 30s/120s defaults stay reverted: this reports, it does not kill,
because a long build and a wedged process have to stay distinguishable. - The band between the last transcript row and the input box stops showing
a stripe of the terminal's default background. Plugin-widget rows, the
queued-follow-up preview and the ask modal were rendered as bare
Paragraphs — transparent — so on a terminal whose default background
differs from the theme they painted as a foreign block through the middle
of the composer whenever a turn streamed (worst with a widget installed:
its rows appear only while it has something to say). Those rows now carry
the composer's own background like the live tool cards and the input box. - The REPL composer holds its place. The inline viewport only re-anchored
when its rows overran the bottom of the screen, so every viewport resize
walked the input box and footer with it: a tool call grew them (status
dock + live tool card) and the tool result shrank them again, leaving the
footer parked above dead terminal rows while the turn kept streaming.
Once the transcript has filled the screen the composer's fixed rows now
stay on the screen's last rows, the dock/card rows above them scroll
instead, and a shrink repaints the rows it vacated with the composer's
own background. A transcript shorter than the screen still hugs the
conversation, as before. - The REPL footer's right segment stops walking across the bar mid-stream.
Whether the reasoning-effort badge paints depends on a reasoning flag that
background discovery keeps writing after startup (the provider's/models
atrepl/mod.rs, then models.dev), so on models whose two answers disagree
— StepFun's own gateway reportsstep-5-previewas non-reasoning while
models.dev marks it reasoning — the badge appeared and vanished a frame or
two into a turn, shiftingStep 5 Preview · xhigheight columns. The
visibility is now snapshotted once at the beginning of each turn, so the
footer holds its shape while anything streams; the provider's converged
answer still lands between turns. - A 503 burst outlasting the provider's own 5-attempt budget no longer kills
the turn the user is waiting on: the agent loop retries the whole request
(up to 2 more times, short ramp, nothing streamed yet — a visible delta
still ends the turn instead of replaying text you already read). 401responses whose body says the model isn't supported (OpenRouter's
ModelErrorshape) classify as a bad request, not an auth failure — no
more "check API key" advice for a model problem.- Empty tool-call padding deltas (gateways that append
{index}-only
entries to the final chunk) no longer materialize ghost fragments, so the
dropping tool call index N with empty namewarning spam is gone. gray viewinside a compound shell command no longer reads as success with
nothing attached: the bash tool appends a note that the image was NOT
attached and how to re-run it bare, and the CLI fallback line says the
terminal has no image protocol instead of a bareviewed …
(docs/bug-gray-view-compound-command.md).