Skip to content

Release v0.0.284 — ACP images, #708/#711/#712/#714/#716 - #713

Merged
justrach merged 19 commits into
mainfrom
release/v0.0.284
Sep 1, 2026
Merged

Release v0.0.284 — ACP images, #708/#711/#712/#714/#716#713
justrach merged 19 commits into
mainfrom
release/v0.0.284

Conversation

@justrach

@justrach justrach commented Sep 1, 2026

Copy link
Copy Markdown
Owner

What changed

Cut of the next release: merges the ACP image-attachment fix and the advisory peer-wake change onto the release line, adds the v0.0.284 changelog entry and release notes, and raises the test-count baseline to 1882.

Why

Pasted images from ACP clients reached the model as path text instead of pixels, and the peer inbox wake read as a command that displaced trivial turns. Both fixes are merged here so they ship in the next release cycle. No tag until asked, per the release-notes convention.

#714 named-source UAF

named_work.remembered_task was process-global. A scout beginTurn left a pointer into the child's freed arena; the parent's later handle() crashed in hasNamedSource (Sol root → Terra scout → synthesis). The mention is now stored on the Agent (named_work_task / named_work_settled), so a sibling cannot poison the root. Test #714 covers handle reading the Agent copy, not a leftover global.

justrach and others added 9 commits September 1, 2026 09:41
… an inbox read (#708)

The one-line [peer] wake appended to a user turn ended in the imperative
'peer_message action=inbox', so a bare greeting made the agent spend the
turn reading the mailbox. The wake now says messages are parked, keeps
the how-to-read hint inline, and asks that the user's actual request be
answered first; an empty inbox no longer carries a read command at all.

Co-Authored-By: Codegraff <blackfloofie@codegraff.com>
The advisory framing added in the parent commit wrapped across several
dim rows in the pager. Keep the parked/read-when-relevant tone, drop the
second clause.

Co-Authored-By: Codegraff <blackfloofie@codegraff.com>
The ACP turn path appended the prompt as a plain text message, so an
image attached from a client rode to the model as literal @[path] text
and the pixels never did. mainloop and the REPL both stage and consume
the marker; ACP now does the same via a shared userMessage helper, with
a regression test covering both the image and non-image cases.

Co-Authored-By: Codegraff <blackfloofie@codegraff.com>
Co-Authored-By: Codegraff <blackfloofie@codegraff.com>
defaultProvider returned the first provider in spec order holding a key,
and the gateway sits at index 1 — above every subscription login. Merely
having a stored gateway key therefore seated a fresh launch (no --model,
no saved model) on the gateway while a valid Codex/xAI/Kimi credential
further down the array lost. Sessions the user believed were direct drew
on gateway credits instead, and a gateway outage broke launches that had
no business touching it.

providerFor already demotes the gateway to its phase 2 for exactly this
reason: #294 fixed the model-routing half and left the startup half.
Give defaultProvider the same demotion through one shared predicate —
direct providers first in spec order, the gateway only when nothing else
answers. A gateway-only user still boots on it.
provider_fallback carries to_provider, to_model and to_context, but the
TUI sink forwarded only the model name. The drain then set g_model_name
and left g_model_provider on the previous provider, so after an automatic
failover the status bar paired the new model with the OLD provider id — a
seat that serves nothing — and models.isCurrent, which compares the
provider precisely so two same-named seats stay apart, marked the wrong
picker row or none at all.

Widen the event to ModelChanged{model, provider}, forward both from the
sink, and move the ownership into Model.adoptFailover beside adoptModel,
which already documents the split: a provider id is a static spec literal,
while the model string dies with the drain that delivered it.

The sink test already built the event with to_provider set and never
asserted on it; it does now, on both sides of the boundary.
Parallel error bash results stay JSON strings on the next Codex body.
Bare /resume and /sessions list ~/.graff/sessions as well as cwd.
Peer-inbox wake is ambient so a greeting is not an inbox tool task.
@cursor cursor Bot changed the title Release v0.0.284 — ACP image attachments; advisory peer wake Release v0.0.284 — ACP images, #708 wake, #711 Codex JSON, #712 device resume Sep 1, 2026
The nudge walked all history, so a past SPEC.md re-armed every later
turn, and adopt/ACP re-injected the same user row. Settle a mention
once tools run or the nudge fires; skip duplicate user payloads and
fail closed after three copies (ADR 0060).
@cursor cursor Bot changed the title Release v0.0.284 — ACP images, #708 wake, #711 Codex JSON, #712 device resume Release v0.0.284 — ACP images, #708/#711/#712/#714 Sep 1, 2026
cursoragent and others added 5 commits September 1, 2026 08:14
Learn-auto (and a re-typed prompt) sends the same words after an
assistant reply. Treat that as a new ask. Dedup only when history
already ends on that exact user prompt.
…lobal

A scout's beginTurn stored remembered_task as a pointer into the child
arena. After the child exited, the parent's handle() walked that slice
and crashed in hasNamedSource (test-model-shape). Copy the text onto
the Agent so a sibling cannot poison the root.
Fullscreen /goal was published through the generic state callback, which
minted standing=true. attempt_completion then left the objective active,
and /resume steered the next unrelated prompt at finished work.

Route TUI /goal through the shared lifecycle (set/pause/unpause/clear).
status and bare /goal report; they do not mint an objective named "status".
An undefined Agent.sys_base skipped the empty-prefix guard and overflowed
setSystemPrompts. Tests now start from a blank prefix like every other
goal-flow fixture.
@cursor cursor Bot changed the title Release v0.0.284 — ACP images, #708/#711/#712/#714 Release v0.0.284 — ACP images, #708/#711/#712/#714/#716 Sep 1, 2026
justrach and others added 4 commits September 1, 2026 17:42
parsePorcelain handed back Entry slices pointing into the caller's buffer.
listEntries frees git's stdout the moment it returns, so every row's path
and branch referenced released memory: reused pages read back as NUL, which
printed blank rows for `workspace action=list` and made `action=use` match
neither a worktree's name nor its exact path, though git listed it fine.

Copy path, head and branch into the arena the rows themselves live in.

Both regression tests fail without the fix. The existing ones could not
catch it: every one parses a comptime literal, which is never freed. The
new pair zeroes an owned source buffer, and runs the real listEntries path
against git so a released buffer is actually exercised.
Same-session grok-4.6 table on this tip: all three harnesses 12/12.
OpenCode wins wall (235s vs graff 242s); graff still wins calls,
tokens, list$, and RSS. Last unique-frontier pin stays the 282 table.
Composite run-20260901-121759: graff 219.6s / 53 / $0.32 / 8.7M vs
OpenCode 234.8s / 77 / $0.68. Symlink stall (115s, 4 calls) retried at
67.8s. x_search stayed on; catalog unchanged.
@justrach
justrach merged commit a50041a into main Sep 1, 2026
13 checks passed
@justrach
justrach deleted the release/v0.0.284 branch September 1, 2026 14:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants