[release] v0.121.6 - #7232
Merged
Merged
[release] v0.121.6#7232
Conversation
A record body holding a NUL (U+0000) cannot be stored: Postgres rejects it
from both `text` and `jsonb` with `UntranslatableCharacterError` (SQLSTATE
22P05). The record is already off the runner by the time the INSERT runs, so
the batch could only be redelivered — and it was, every 30s for over a day,
from one `tool_result` carrying console output.
It never isolated because the rejection was not recognised as permanent.
SQLAlchemy's asyncpg dialect wraps the driver error in its own generic
`AsyncAdapt_asyncpg_dbapi.Error`, which it then cannot map to a typed
subclass, so it arrives as a bare `DBAPIError` and not `DataError`.
`_append_committed` therefore returned before its one-record isolation pass,
leaving the good records in the batch uncommitted too.
Fix both ends:
* strip NULs at the producer boundary that already bounds body size, so the
record is stored intact rather than quarantined. Record bodies carry
arbitrary tool output, so a NUL is reachable from ordinary use — a `grep`
over a binary file is enough.
* classify the rejection by unwrapping to the driver exception and reading
Postgres's SQLSTATE class 22 ("data exception"), which is always a property
of the row. Testing `DBAPIError` itself would be wrong: `OperationalError`
and `InterfaceError` are subclasses too, and treating an outage as permanent
is the silent record loss this worker exists to prevent.
Claude-Session: https://claude.ai/code/session_018YBQfPLZ8FVqcLevxcTmVU
A dropped connection (API restarting, laptop waking, offline) made every polled session read log console.error, which Next forwards to the dev terminal. The browser already reports these, so callFern now returns null without logging when the request never reached the server.
A bare TypeError thrown by a callFern callback is a bug and must still log.
The session list scrolls with no scrollbar and fades at an edge with more to scroll. Long session names fade at the row's edge instead of ending in an ellipsis. Agent headings stay pinned while their sessions scroll, and a reload scrolls the selected session into view.
The reveal ran only on a resize, so a selection that arrived after the rows rendered was never scrolled into view.
Integrations, Skills and Automations headers in the agent config panel now open a menu with 'Create with AI' beside the manual add. It writes a starter prompt into the chat composer so the agent runs the setup.
The connect dispatcher dropped the host's bare flag, so each connect row drew the app icon twice. A connect the runner deferred to next turn drew the failed glyph instead of the app's mark. Client-tool rows had no minimum height, so their spacing did not match the other steps.
The run is still in progress while it waits on the user, so the header keeps the live shimmer. The glyph drops the warning color and matches the step glyph size.
A closed text became the answer after a 1.2s hold. A model that took longer to start its next call showed its aside as the reply, then pulled it back into the fold. A closed trailing text now stays in the fold while the run is open.
A failed profile read (network, 5xx, an aborted request on reload) resolved to no user and wrote sessionAtom false. Every entity query then stayed off, so after a refresh the agent name fell back to "Agent" and the agent's revision never loaded. Only a real signed-out answer now clears the session, shared user and user id.
An observer that unmounted during the first records read cancelled the shared flight, and hydration took the cancel as an empty log: the session showed as having no stored history. The read now retries a cancel, and a read that truly failed shows a load error instead of claiming the history is gone.
A send can flip this tab's stream on and off and then run on the shared reader. The closed local stream read as a finished run, so the first closed text (an "Auto mode unavailable" notice) showed as the answer while the header still said Answering. The hold now uses the same live signal as the header.
A request_input call that errored (a bad schema, a runner refusal, a stop) showed as "The question could not be shown". Failed tool calls are already hidden from the fold; failed questions now follow the same rule and stay in the parts and the trace.
A long reply lands whole when its run ends, and following the bottom dropped the reader at its end. An answer that arrives while the reader follows is now anchored at its first line until their first scroll input. A reload or session open still lands on the latest message, and a message sent after the answer resumes following.
The composer cleared the request before the lazy input was ready, which cancelled its own retry and could drop the prompt. The request now stays set until the text lands, and only the matching request is cleared. Simplify: remove the composer-count atom and the plain-plus fallback. The config panel only renders beside a composer, and a pending request now waits for one.
The dock opened on a question whose input was still streaming or was the empty {} announce, so the card showed "This request couldn't be shown as a form" until the full schema landed. The dock now shows a question once its payload has arrived and parses; a bad payload still settles in the background, and a parked one still shows its Skip.
A failed header or records read was cached as an empty answer, so an unreachable server showed "Read-only - this session has no agent" and "no replayable history". The header read now throws on failure, and the session shows a "Couldn't load this session" state with Try again.
The deferred-connect helper import was out of order, and the records retry test still expected the old failed-read result without the failed flag.
A session that fails to load no longer borrows the last session's revision. A failed re-read of a tentative empty header counts as unreachable. The failed-history notice gets Try again, which re-runs hydration in place. A replay kept mounted across a session switch resets its follow and anchor state.
…ices - Blog index and authors index descriptions no longer describe Agenta as prompt management / evaluation / LLM observability; they use the current workspace-for-agents definition. - Every post published before the July 2026 relaunch now renders a dated update note (HTML page and markdown twin) linking to what Agenta is today. Publication dates and post content are untouched.
CodeRabbit review: {"type": "tool_result", "ty\x00pe": "other"} used to strip to
{"type": "other"}, letting a poisoned key displace the discriminator record
reconstruction depends on. The kept entry now wins on collision, and a clean
key always wins over a stripped one regardless of insertion order.
Also make the size-order test able to fail if truncation ran before
stripping: the body is oversized only because of the NULs, so the output
must survive whole with no truncation marker.
A message sent while a run was going flashed as a sent bubble in the chat, then jumped into the queue once the server parked it. When the queue released it, it vanished until its user row arrived. A busy-time send now shows as a queue row at once, matched to its server row by the send's idempotency key. A started row stays in the queue until its user row lands, for at most 2s. A send the server starts right away moves to the chat as before.
fix(api): stop a NUL in a record body from looping the records stream
…n-on-secret-change [fix] Keep the Advanced drawer open after removing a custom secret
The composer upload sent only session_id, so the API fell back to the callers default project and stored the attachment there. The runner, scoped to the sessions real project, then got 404 and never read the file. Send the sessions project_id on upload like every other session-scoped call, make it a required uploadAttachment param, and guard the pre-hydration null.
…project guard CodeRabbit: the transport test used a literal project, so a composer regression could pass. These hook tests assert the projectIdAtom value reaches uploadAttachment and that a missing project refuses the upload before any network call.
The rendered <img> read /sessions/attachments/<id>/content with only session_id, so the API fell back to the default project and a session in another project got a 404 (broken image). Send project_id from the same shared store the upload uses. The acceptance test (attach-send-render-reload) exercises this end to end; unit tests cover the URL builder.
…arker working Two small, independent robustness fixes in the session record persistence path, from investigating the 2026-09-28 EU runner OOM incident (production-session-investigation). 1. Bound each ingest POST with a per-request timeout (AbortSignal.timeout, env AGENTA_RECORDS_INGEST_TIMEOUT_MS, default 30s). Without it a single stalled request holds the per-session persist chain, and behind it the turn-end drain, open for as long as the socket stays open. A timeout now throws and is retried like any other transient error. 2. Stop flush() from consuming the per-session drop count. The turn-end finally in server.ts is the authoritative reader that marks a session incomplete when a record was dropped; flush() consumed and cleared the count first, so that reader always saw zero and the session was never marked. flush() now drains only and leaves the single read to the caller. Unit tests updated: the flush test now asserts the count survives for the caller. Typecheck clean; runner unit suite green apart from 4 pre-existing gateway-gating failures unrelated to this change. NOTE: the larger runaway-output memory bound (the actual OOM cause: unbounded events[] and streamed-text accumulators in tracing/otel.ts) is a separate follow-up, to be placed at the otel choke point and terminalized through the existing run-limits RUN_LIMIT_TRIPPED path, pending a recorded full-stack QA pass.
Use the durable record project_id when rendering attachment references. Navigation can change or clear the active project while records are loading. Covers both changed and cleared project scope, explicit overrides, and null overrides.
Following the bottom snapped the scroller to each new message. Pins after the first now glide with native smooth scrolling, and stay instant on first load, a session switch, a jump past two screens, or reduced motion. A glide's own scroll events do not end following; any reader input does.
* fix(mobile): scope permission checks to the requested project Without an explicit project_id query param, the auth middleware resolves the request scope to the workspace's default project. The access check then compares that default project against the requested scope_id and denies every permission on a non-default project, which disabled the custom-secret Attach button (and other edit_secret gates) there. * test(api): pin the permission-check scope contract; docs per review Codex review follow-ups: document on check_permissions that scope_id asserts the authenticated scope (it never selects it), add a regression test for the 403 on a scope mismatch, and shorten the client comment. --------- Co-authored-by: Mahmoud Mabrouk <mmabrouk@users.noreply.github.com>
fix(runner): bound runaway output and preserve crash recovery context
…-scope fix(frontend): scope session attachment uploads to the session project
[feat] Polish the sidebar session list and rail details
Stopping the mic without speaking showed "No speech was recognized". Track the recogniser's speechstart event and raise the notice only when the browser heard speech but returned no words.
[fix] Stop logging network failures of session reads as errors
…snapshot fix(runner): version Daytona sandbox snapshots
[feat] Create with AI in the agent config add menus
…efresh [fix] Polish the session timeline and survive failed reads on refresh
[fix] Show the no-speech notice only when speech was heard
ashrafchowdury
approved these changes
Sep 30, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
New version v0.121.6 in