Releases: vstaln/gray
Release list
v0.1.9
Fixed
- The REPL composer stays on the last rows of the screen. Once the transcript overflows the
viewport, a latchedbottom_anchoredkeeps the input box and the footer pinned to the screen's
last rows instead of parking them above cleared rows; a shrink (status dock, live cards clearing at
a tool result, end of turn) slides the viewport down and repaints what it vacated with the surface
colour. Band budgeting makes the text area and the footer un-trimmable and sheds the most transient
band first, so a busy screen loses the status dock before the transcript.
Changelog
v0.1.8
Fixed
-
Heredocs survive the cwd-report suffix.
bashappended
; __gray_rc=$?; printf ...to the command text, so a command whose last
line was a heredoc terminator readEOF; __gray_rc=$?and the terminator
never matched: the whole suffix landed inside the heredoc body — a shell file
written with a garbage trailer, or aSyntaxErrorfor an interpreter
heredoc, silently (rc=0). Each piece of the suffix now sits on its own
line, which also stops a trailing#comment from eating it and makes
cmd &legal. 33 of 47 DeepSWE runs in the 2026-09-29 retro reported this;
it cost each a wasted turn at best. -
- The
windows-runtimeCI gate stops hanging on the search-index bench.
Everycirun since the search-as-command merge (#145) died at
index_vs_spawn_tax— "running for over 60 seconds", then silence until
the job's 60-minute budget was spent. The hang had no reachable timeout:
the bench's deadline assert lived inside its own poll loop, but the call it
polled (SearchPool::warm_picker) never returned — it took the pool's
residentmutex with a blockinglock(), and index construction held that
same mutex across everything fff does, which on Windows stalled inside
unbounded dependency waits (LMDB writer lock, git status, watcher init).
The probe is now non-blocking (try_lockwith a ~50ms budget, then "not
warm" → fd/rg answers), construction runs with no pool lock held and is
deduped by root, and every bench phase carries a hard timeout that fails
naming the phase. A wedged build now costs one search its fallback lane —
and costs CI a two-minute failure with a name on it — instead of a 60-minute
silent hang.windows-focusedtakes atest-pathinput, so a native
single-test iteration no longer means editing the workflow.
- The
v0.1.7
Added
-
The composer keeps its place, and its footer stops flickering. A latched
viewport floor (latched_viewport_floor) grows the frame by exactly what
the pre-computed dock estimate short-cuts mid-stream, and the footer gauge
paints on the frame's last row (footer_paint_row) instead of being pushed
past the bottom and skipped for that frame —Ω 223.8k/300kand the pad band
under it now ride out a mid-turn text wrap. Shrinking the viewport keeps its
top edge fixed, so no blank scrollback is painted behind the box. -
Bare Enter continues. While the last turn is resumable, the empty input box
shows a dimPlease continue…ghost hint and submitting empty text continues
the conversation; a box with text (or an attachment) still submits it. -
Streaming rows hold orphan punctuation. A lone
./,/?arriving as its own
delta is held until the next chunk says whether it belongs to it, then
released glued to what follows — no more a frozen lone-.transcript row at
an interrupt.
Fixed
-
~/.gray/provider_models.jsonno longer accepts loopback base URLs. A local
server's port changes on every start (and a unit test's is random) and
nothing evicted old keys, so every one was a permanent dead entry — 43 had
piled up. Remote providers still persist, so/modelstill paints its cached
list instantly. -
The provider model-list fetch no longer rides the ambient Tokio runtime: a
background refresh that outlives REPL shutdown used to kill its thread with
Tokio 1.x context ... being shutdown. It runs in its own short-lived
current-thread runtime. -
Binary detection in the read guard no longer magic-sniffs the first bytes —
the NUL check decides — and a regular-file-to-FIFO swap between the guard and
the open is now caught by anO_NONBLOCKopen instead of hanging the turn.
Changed
-
Self-update hashing uses the
sha2crate that is already a dependency
instead of shelling out tosha256sum/shasumand writing the tarball to a
temp file. -
gray-markdown drops the incremental open-block cache (its measured cost was
maintenance, not reads) and the LaTeX-to-unicode stack — raw TeX shows as
typed. Unicode repair uses a minimal Latin accent table instead of the
unicode-normalizationcrate, so the dep count holds. -
The shell lane is one type carrying the live child, its process-group guard,
the output pump and the liveness clock, instead of four parallel ones.
Chore
- The internal working journal (superpowers plans, study notes, session
records) is untracked from the public repo; reference and product docs stay
tracked.
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).
v0.1.5
Changed
-
The search index is a command, not a lane inside the
findandgrep
tools. 0.1.4 left those two tools answering differently depending on the
directory, the pattern and a decade of glob semantics — and a missed rule
there is a wrong answer, not a slow one.findandgreparefd/rg
again, unconditionally, andgray find PATTERN [PATH]/gray grep PATTERN [PATH]own the index: the same answer, served warm, with the fallback lane
being the very tool the command stands in for. Both are claimed by bash like
gray view, so a model reaches them withoutgrayon its PATH.The policy is cost-first, which the tool placement had hidden: the index
lives in process memory, so a fresh process holding none paid a full scan —
1.4s againstfd's 20ms on a 20k-file repo, a "speedup" 70x slower than the
shell it replaced. Now the first search in a process goes tofd/rgand
starts the index in the background; every search after that is index-served.
Coldgray findon that repo: 21ms. What the index is actually worth is
grep on repeat searches (2.9x the tool lane);findis a wash. -
catis no longer a second way to see a picture. It returned a
full-resolution vision block whilegray viewcapped at the shared 2000px,
so the model had two paths with two answers and picked by habit.gray view
is now the only one:cat <media>says so in one line instead of streaming
binary into the context. Same text, same tool, one fewer thing to learn.
Fixed
- A search cancelled before it starts now answers
cancelled by user
instead of racing the spawnedfd/rgchild's first line, which could
return a finished result for a call the caller had already given up on.
v0.1.4
Added
-
findandgrepanswer from a resident file index instead of spawning
fd/rgper call. A watcher-backedfff-searchindex is built lazily per
search root, ranks hits by frecency, and serves the next search out of warm
memory: measured on this repo,find9.7ms againstfd's 17.8ms andgrep
17ms againstrg's 95ms, with identical result sets. The tool surface does
not change — anything the index cannot answer exactly (a non-git root,
ignoreCase, a negated or depth-anchored glob, an invalid pattern, a file
target) falls straight through to the old lanes, and a cancel still stops at
once instead of waiting out a cold scan. Design and decline rules:
docs/fff-search-index.md. -
gray view PATH...shows an image file as an image (downscaled to the 2000px
cap, the one shared with thereadtool and pasted attachments). Theview
tool that 9ae15d3 deleted comes back as a command: bash's one vision path
now claimsgray view <path>...before the shell runs, alongside
cat <path>(full resolution) — so an agent checking a rendered chart,
screenshot or diagram gets an image instead of pixel soup, with no new tool
in its toolset. Multi-path by design; a leading~is expanded, since
nothing else would do it before the shell runs. -
gray view movie.mp4shows a contact sheet of sampled frames, so an agent
can see a recording without shipping the whole file into the context;
--frames Nsets how many. Gemini and Gemma models get the native video
instead of the sheet, and an oversized file says so rather than silently
downscaling past the cap. -
/gatewayis the connections picker: every installed app, its own status,
and Enter on a needs-setup row runs that app's setup — the channel picker
covers DMs, setup is token-first, the configs an app writes land in the
user's home rather than gray's, andgray gateway setup <app>does the same
headlessly for scripts. -
gray --json progressnarrates a turn:tool_started/tool_ran/
tool_finishedrows carrying disclosed, bounded, redacted output plus the
internal call id, andthinkingphase rows. A front end can render live
tool activity and a separate persistent tool card without parsing prose. -
Memory carries a turn: a one-sentence profile summary is injected with it,
the prompt asks the model to show a memory's KEY before leaning on it, and
the daily ingest is mechanical and bounded — append-only edits, daily caps,
and no verbatim duplicates under a new key. -
Provider plugins join the plugin protocol at 1.2: host-owned credential
refs, sidecar RPCs, cache/runtime roles,/connectplugin login, and an
OpenAI dynamic provider profile. Codex/ChatGPT OAuth ships as a first-party
optional plugin (plugins/codex-auth), so/connectdiscovers auth
providers only when one is actually installed and enabled.
Fixed
-
An interrupted sidecar or provider stream no longer turns a usable partial
answer into an error. A provider stream that ends without its completion
marker is finished with a capped, nonfatal interruption notice rather than a
CoreError, because the visible delta is already committed to history and
replaying it would duplicate text the user read. On Windows a blocked pipe
write is bounded by a worker task instead of stalling the runtime thread, so
cleanup can actually terminate the child; child termination after a write
timeout is bounded, and an already-exited child id no longer wedges cleanup. -
The default prompt points the agent at
gray view, notcat, for images —
catstill returns bytes, but the agent is told which one to reach for. -
Prompt caching drops two pieces of pi-parity over-engineering
(cache_controlmasking andx-session-id), and/resumepreviews the
latest message of a session rather than its opener. -
A 23-finding source audit landed with its sweep: drive-named archive entries
refused on every platform, symlink-cycle and atomic-pid-claim guards,
serialized config read-modify-write, a log-rotation guard that acquires
before it opens, and the remaining secret-redaction gaps in tool output.
Dispositions:docs/audit-fixes-2026-09-22.md.
v0.1.3
Added
-
Onboarding points at one place: a run with no model configured, and
/model
with nothing set, both sayrun /connect to set up your provider & key(with
/model provider/idand/helpas the alternates) instead of naming
/provider, which/helpnever listed. -
Official plugins now ship in this monorepo under
plugins/rather than a
separategraypluginsrepo, sogray plugin install <name>can never point
at a repo that does not exist. Each directory is one sidecar — a single
plugin.shspeaking protocol v1 NDJSON over stdio, withecho/as the
reference implementation — andplugins/index.jsonis the catalog
gray plugin installreads. Push tagplugins-v<version>(which must equal
every manifest'sversion) to runplugins-release.yml: it syntax-checks
each sidecar, tarballs the directories, and publishes aplugins-v<version>
release withSHA256SUMS-plugins.discord/,background/and
permissions/are scaffolds until the real bridge/runner/gate logic is
ported in — each file's TODO says where.
Fixed
-
Compaction pinned the stable anchor by value: the retained tail was
filtered withretain(|m| m != &anchor), which deleted every message
equal to the original intent — including a later turn that legitimately
repeated the prompt — and left the request ending on an assistant message
instead of a user turn. The walk now starts pastcandidate[0], the
anchor's own position, so only that one copy is excluded. As a side effect
the anchor's tokens are no longer charged to both the pinned segment and
the tail (arXiv:2512.22087). -
The context-overflow path compacted the unscrubbed history: a salvaged
partial that the CCRM scrub (arXiv:2605.08563) had flagged rode the
compaction trigger in full, so the failed trajectory was baked into the
summary and reached every later request — exactly what the scrub exists to
prevent. The scrub is now one view (Agent::scrubbed_messages) shared by
the outbound request and the compaction input, so both carry the one-line
marker. The persisted transcript still keeps the full text the user saw.
v0.1.2
Added
-
/gateway(alias/gw) opens a connections panel: one toggleable row per
installed app (the merged plugin registry, so a transport appears the day it
is installed), a rule, then one-line pointers at daemon, cron and memory —
gray gateway status,/cronand/memoryalready print everything about
those, so the panel names the command instead of restating its output. An app
row carries what it still needs (needs setupwhen its default config file
is absent — existence only, never the file, which holds the token) and the
commands its own manifest declares; nothing is invented on its behalf.
spaceflips an app's enabled flag through the same registry path
/pluginuses,/gateway on|offstill flips the persisted gateway master
switch, and piped stdin prints the rows as text./gatewayand/gware
real commands again (they previously answered "the TUI gateway is gone") -
gray login,gray whoami,gray logout(and/login,/whoami,
/logoutin the REPL): enroll this machine with gray.alignment.id. The
site's account page mints a one-time 5-minute code from a Supabase session;
gray loginexchanges it for a long-livedgray_...registry token stored
in~/.gray/registry-token.json(mode 0600, same atomic writer as
auth.json). Baregray loginprints the walkthrough and prompts for the
code;gray login <code>is the non-interactive form the site's copy
command emits.gray logoutrevokes the token server-side before dropping
it, and logging in again revokes the token it replaces so a re-login never
leaves a working credential the machine has forgotten. All three run before
provider configuration, so a fresh machine can enroll before it can run a
turn.GRAY_REGISTRY_URLpoints at a local registry
(pnpm backend:dev); cleartext http is accepted only on loopback, since
every call carries the token. Nothing in gray is gated on an account — the token
only names the caller on registry calls — and the onboarding banner now says
so instead of implying a login exists -
/cronand/memoryare interactive on a TTY, riding the same picker loop
as/pluginand/skills:/cronlists every job (name, id, schedule, next
run, last status) plus the ticker's liveness row, andspacepauses/resumes
in place throughCronStore::set_paused; adding and removing stay on the
gray cronCLI./memorylists every curated entry (key, scope, first
line) read-only — forgetting staysgray memory remove <key>, because a
picker must not make deletion a keystroke. Headless output is unchanged for
both. The shared manager loop gained the axes these panels needed:
per-rowread_only(separators and pointers carry no switch), a
supports_removeflag (listing panels leave removal to their command), and
anerrors_tabflag (package-install errors are noise on a cron listing)
Changed
- Memory entries carry their latent reasoning. The policy now asks every saved
entry to record the failure or correction that prompted it (quoted), whether
it has recurred since, what was already tried and falsified, and the verbatim
text of any entry it replaces — the keep/delete rule from arXiv 2608.11095,
whose finding is that an instruction nobody can justify is an instruction
nobody can safely delete, which is why prompt files only ever grow. A why
without its outcome is worse than none. The rule itself: if an entry's failure
has not recurred since the entry was added it is probably preventing that
failure, so keep it; delete only when the failure kept recurring anyway or the
entry duplicates another's target, and carry the removed entry's falsified
attempts into its replacement gray memory auditreports which entries lack a why, which duplicate
another's target, and which record a falsified outcome, with the rule above
printed beside them. It deletes nothing — the paper's own warning is that
automating the deletion emptied one prompt in eight and lost satisfaction on
exactly those — so the decision stays with a human- Repeated net growth with no removal (three consecutive saves) now prints a
one-line warning pointing at the audit: unbounded growth is the disease, and
it is visible in the entry count long before it is visible in behavior AGENTS.md/CLAUDE.mdmay carry# r<n>: ...rationale comments for a
rule. They stay in the file for whoever edits it and are stripped before the
rules reach the model, so rationale never costs the executor tokens — and
the served block says so, so an editor preserves them. Files without such
comments render byte-identically to before- Windows installs natively by default.
dist/install.ps1no longer routes a
bare invocation into WSL: with no arguments it installsgray.exeinto
%LOCALAPPDATA%\Programs\gray\bin, verifies the archive checksum, extracts
onlygray.exe/LICENSE/THIRD_PARTY_NOTICES.mdfrom the ZIP, and
updates the user PATH.-Wslremains an explicit compatibility route that
pipesinstall.shinto a distro, and a native failure never falls back to
it. The "experimental" and "acceptance pending" wording is gone from the
installers and docs, and the README platform table lists Windows as native
x86_64. Still documented as unsupported, unchanged by this: gateway and cron
execution, self-update, Unix-shebang plugins, and ACL hardening of
credential files
v0.1.1
- Windows builds ship with the release:
gray-<channel>-x86_64-windows.zip
alongside the four tarballs, checksummed into the sameSHA256SUMSfile.
install-native.ps1installs it without elevation, probes the binary with a
bounded 10s timeout, and replaces a runninggray.exethrough a staged
copy-plus-backup rather than an in-place write. It is exercised in CI on
windows-2025 under both PowerShell 7 and Windows PowerShell 5.1. The public
install.ps1still defaults to installing inside WSL; native is opt-in via
-Native
Added
-
Prompt-cache warmth timer + cache-miss warning in the composer. The
footer carries a◷ 4mcountdown next to the cache-hit percentage —
how long the last request's prompt cache stays warm before an idle gap
re-bills the whole prompt (Anthropic'scache_controland OpenAI's
automatic prefix caching both expire after ~5 idle minutes), fading in
the last minute and hidden entirely when the provider never reports
cache activity. When a request re-bills tokens the previous one should
have served from cache — an idle gap past the TTL, a model switch, or a
provider-side eviction — the transcript gets a warning row
(⚠ Cache miss after 6m idle: 100k tokens re-billed (~$0.35)) once the
miss crosses 20k tokens or $0.10 over a full hit, so routine breakpoint
noise stays silent. Detection is a port of pi's
packages/coding-agent/src/core/cache-stats.ts(reference checkout
underreference/pi-mono) onto gray's per-roundStepUsagereports;
compaction and/newreset the baseline, since a fresh summary is new
content rather than re-billed content -
Remove a provider from the connect modal:
shift+enteron a highlighted
provider (the modal footer advertises it only for a row that holds a stored
credential) opens a confirmation naming the provider and its endpoint, and
enterdeletes the credential —auth.jsonentry plus the active
provider's second copy inconfig.json, so a removal cannot resurrect on
the next start./providerreloads the agent and reports the removal
instead of announcing a connection -
Gateway daemon (
gray gateway ...): the always-on host that fires cron with
no REPL open.runis the foreground daemon (60s cron ticker with the same
HeadlessRunner/SaveLocalDeliverastick/serve, plus a control socket
at$GRAY_HOME/gateway.sockansweringidentify/status— one JSON line
in, one out, hermes wire shape);installwrites a user service (runit
run+log/runon Void, systemd--userunit elsewhere,--printpreviews,
--no-startdefers),start/stop/restartdrive it,uninstallremoves
it,statusreports daemon + service + ticker health (exit 1 when down).
Process shape follows the hermes gateway: O_EXCL pid claim with
/procstart-time against PID reuse,gateway.state.jsonrecording why the
last run stopped, socket-first liveness with pid-file fallback, 0600 socket,
supervisor detected from the real init (never inferred outside-in), and a
bounded 65s drain of in-flight fires on SIGTERM/SIGINT.install/start
wait up to 10s forrunsvdirto pick up a fresh service dir (it rescans
every ~5s) instead of racing it, andstoptells runit-stopped vs
tick-draining apart (was a⚠ still runningeither way). -
Cron in-chat firing: background REPL tick,
/crondashboard, delivery seam. -
Cron workstream B:
gray cron tick|serve|pause|resume|run, job skills +
pre-run scripts, local-file delivery (cron/output/<id>/<ts>.md). -
Cron ticker liveness: every tick pass writes a heartbeat
($GRAY_HOME/cron/.last_tick), andgray cron list,gray cron add, and the
/crondashboard report it — a store nobody is ticking now says so instead of
printing anext=that will never arrive. -
Plugin system:
gray-plugincrate with Plugin trait, builtin tools as profile-ordered plugins,gray.ymlprofile loader + sidecar entries, sidecar hook protocol over stdio with timeout/crash degradation -
Gateway daemon:
gray-gatewaycrate (Telegram/Discord/Slack), real Discord adapter with slash commands, OAuth2 invite URL, full/gatewayREPL suite, delegation durability -
Cron jobs: schedule/store/CLI + REPL, local wall-clock daily schedules, AI self-scheduling via
schedule_task -
Dynamic context window via models.dev + disk cache (LiteLLM table, proportional reserve/keep),
/contextvisual modal with suffix completion and thousand-separator parsing -
Usage/cost tracking: session cost from LiteLLM rates, footer +
/usage, persisted session totals -
Skills:
skilltool loading SKILL.md bodies,/skillscommand, discovery across opencode plugins/agents/claude skills -
Anthropic prompt caching always-on with cache hit-rate display; reasoning summaries on Responses API
-
request_user_inputtool (question overlay) so the agent can ask the user mid-turn -
Codex-style session resume (
--last/--all, picker, transcript replay) -
/effortthinking-level selector and/thinkingtoggle -
Auto-compact on threshold/overflow; exploration-stall guard
-
Noir space marketing site (landing/docs/pricing, favicon/OG/robots/sitemap) deployed to gray.alignment.id
-
Multi-platform releases (darwin x86_64/aarch64, linux aarch64) with release channels (
RELEASING.md) -
Startup update check +
gray updatesubcommand -
Bulk hermes→Rust 1:1 port slices across core/cli/tui/tools/plugins/gateway/provider/sandbox/state/cron/acp
-
Effort picker filtering: online models.dev capability filter with offline static ARM fallback
-
Wire is_error flag, DeepSeek provider path, resume-replay, retention policy, and chat-shard routing
-
Windows cfg gates for platform-specific code paths
-
Resume messages for restored sessions
-
Shift+Enter newline handling in composer
-
Footer/badge/panel TUI chrome updates
-
Attach guards for file attachment paths
-
Glob tool for file pattern matching
-
Session-ID threading across turns
-
prompt_cache_key passthrough for chat requests
-
gray sessions prune --older-than-days Nfor session-store GC;persist_redacted: truegateway option to scrub secrets from persisted gateway transcripts -
Verified installs: SHA256SUMS published per release, checked by install.sh (S1)
-
GRAY_NO_UPDATE_CHECK=1and 24h update-check cache (L4) -
Gateway autostart defaults off; corrupt gateway.yaml warns instead of silently resetting (S2, S3)
-
Safety / Subcommands / Platform / gateway docs in README (D2, S4)
-
The connect modal's footer and the install manager's per-tab footers share
extracted same-file helpers instead of repeating the render scaffolding
three times each (-188 net lines across the two files). Behavior is
unchanged, including the connect modal's conditionalshift+enterhint,
which only appears for a row that actually holds a stored credential
Changed
-
gray plugin install discordcompiles the plugin from its pinned commit
(cargo build --release --locked) instead of pip-installing a Python
package, so installing a first-party plugin needs no interpreter. The
catalog is Rust-only; user-written plugins stay language-agnostic
(GRAY_PLUGIN_PATH,plugin.sh). A source pin must be a full commit ID,
and the built binary has to answerplugin/manifestwith the expected
name before it is registered -
Clipboard/image paste is core again:
arboard+imageare always compiled in, no--features clipboardneeded (kept as a no-op alias) -
Removed the native messaging gateway: deleted
crates/gray-gateway(adapters, daemon, pairing, delivery, systemd), theplugins/gatewaysidecar,gray gateway .../gray send, and thetelegram/discord/slack/all-platformsfeatures. Chat returns as a plugin;gray cron --delivertargets are stored opaquely until a delivery backend exists. Dropped the--all-featuresCI checks. -
Multi-line input is no longer clipped by the inline viewport. The viewport
cap was pinned near 14 rows regardless of terminal height, so a pasted
paragraph that wrapped to 12 content rows lost its last row and anything
longer was cut hard. The cap is the terminal height now (minus the shell
prompt row); the idle 14-row transcript is unchanged -
The default tool surface stays bash-only, and
bashnow carries image
vision:cat <image>returns the file as a vision block at full
resolution (decode, EXIF orientation, re-encode at native size — no
downscale, no halving) instead of the binary garbage a shell would
stream. The claim is deliberately narrow — exactlycatplus one bare
path — so flags, pipes, redirects, globs, and multi-file cats run
normally, and missing or mislabeled files fall through to the shell's
own error. A separateviewtool was tried and removed the same day,
on one principle: a capability the existing surface can carry does not
need a new tool.tools-minimalis pinned bash-only by a test -
Project rules arrive without being asked for: the nearest
AGENTS.md/
CLAUDE.mdabove the working directory is served every turn as a
self-describing<project_context>block (nearest ancestor wins, the
gray-home file is skipped since it is the stored system prompt, 32K-char
cap), so repo rules stay in the permanent prompt instead of arriving as
prunable tool observations that can fall out of context mid-session -
An explicit
/skills <name>invocation now pastes a binding directive
ahead of the skill body — the instructions are binding for the current
task, drop any conflicting plan — because a bare body pasted mid-task
reads as background material and the model resumed its previous plan.
The per-turn<available_skills>block gained the matching mid-task
clause: stop and read a matching skill before continuing -
The default system prompt is 31 lines, down from 39: the three prose
sections folded into workflow steps and two guideline bullets (both
verify-contract sentences preserved verbatim), and the project-rules /
skills n...
v0.1.0
Full Changelog: https://github.com/vstaln/gray/commits/v0.1.0