Bind spilled diff artifacts to their tool calls and validate their size and structure before recovery promotion.
Preserve exact recovered history while canonicalizing checkpoint-only storage projections.
Keep durable diff snapshots handle-backed across imports, native seeds, and recovered session copies.
Reject conflicting presentation sources and authenticate copied artifacts by family and content.
The reclip fixture wrote a legacy schema-2 session file, but the legacy
loader never reads origin_workspace_root: it sets origin equal to the
current workspace, so the diverging-roots condition the test describes
never reached the resume path. A mutation restoring the old root
blanking passed every assertion.
Rewrite the fixture in the modern durable format: schema-4 session
metadata with distinct origin and current roots, content-addressed
result artifacts, and events.jsonl conversation frames. The observe-row
assertions now exercise the moved-workspace path for real; the same
mutation fails with the raw session id back on screen.
The moved-workspace fixture's run and observe results carry session
status JSON, so their byte counts match the payload length. The new
resume projection test proves the sink resolves labels seeded from the
live root; its title now says that instead of overstating coverage of
the seeding decision, which the end-to-end moved-workspace test owns.
Resume seeded historical session labels with a blanked workspace root
whenever the session's origin and current workspaces differed. The
two-condition guard in the run-command formatter then withheld every
absolute-path label, so shell interact and stop rows fell back to the raw
session id (Observed shell-42) even though the launch command was
recorded and the Ran rows beside them rendered it in full.
Seed the labels map with the app's live workspace root like the sibling
replay paths already do. Commands under the live root abbreviate the
same way as Ran rows; commands elsewhere show raw instead of dropping
to the raw session id, matching the reclip behavior the moved-workspace
resume already ships.
A host-defined tool named `write_file` or `edit_file` no longer fails at call
time with "unsupported file mutation tool". The kernel routes by the
registered executor kind instead of the public name: a host tool under either
name runs the host's `execute()` through the ordinary registered-tool
permission path, while the builtin file mutations keep their typed contract.
`tool_start` events now include the tool's `input` object, so a host UI can
show what the agent is doing while it does it. Inputs over 64 KiB of JSON
arrive as an `inputPreview` string prefix with `inputTruncated: true`; the
tool still receives the complete arguments.
`agent.prompt()` accepts `{ type: "image", data, mimeType }` blocks (the ACP
content shape) on both the native and WebAssembly backends. Images are sent
only to models that advertise image input; any other model fails the turn
with "Image prompts are unavailable for the selected model" and no image
bytes leave the process.
The SDK rejects oversized prompts with typed errors before any request: over
5 MiB of base64 per image, more than 8 images, over 8 MiB of image data, or a
total prompt frame over 8 MiB (text-only prompts included, which previously
hung the WebAssembly backend).
Prompt images are retained in `checkpoint()` bytes within the existing 4 MiB
checkpoint bound, so a restored agent can refer to earlier images on either
backend.
Accept both the shipped nine-key committed_file_presentation shape
and the extended handle-carrying shape when parsing, and omit
content_handle from written records when no snapshot was spilled so
older builds can still read new checkpoints. Resume degrades to the
inline diff preview when a spilled artifact is missing or corrupt
instead of failing.
createFxAgent() accepts a boolean fast option matching --fast/--no-fast.
Enabling fast is validated against the selected model's advertised fast
path during agent creation and rejects with a LIBFX_UNSUPPORTED_FAST
error naming the model. fast: false or omitting the option keeps current
behavior.
createFxAgent() accepts an effort option using the same vocabulary as
--effort. A named level is validated against the selected model's
advertised effort levels during agent creation and rejects with a
LIBFX_UNSUPPORTED_EFFORT error naming the supported set. ACP turn
usage now surfaces provider reasoning tokens.
Large before/after file snapshots no longer inline into the
conversation event log or the recovery checkpoint. Edits past the
inline budget persist one content-addressed artifact under
tool-results and the durable records carry its handle. Resume
reloads the snapshots for the full diff view and falls back to the
compact preview when an artifact is missing or corrupt. Older
session logs remain readable.
Asking a server for resources or prompts it never advertised failed the
mcp_features call with tool_execution_failed and an internal error name the
model could not act on. A missing capability is not an execution failure:
render an explicit note naming the server and the absent capability,
keeping the untrusted-external envelope, so the model can fall back to the
server's tools or another server.
McpRefreshRejected now fires only when the token endpoint answers 400 with
invalid_grant; 429 and 5xx map to a transient McpRefreshUnavailable that
fails the attempt without flipping the server to needs_auth. The mark
itself is generation-guarded so a concurrent successful refresh is never
overwritten by a stale rejection, and installing usable credentials clears
the challenge flag so a recovered server stops asking for re-auth.
A server whose stored OAuth credentials fail refresh was left claiming
auth=authenticated while the connection failed, so every surface reported
it as a generic failure with no path forward. When the token endpoint
rejects the refresh (or credentials expire with no refresh token), flip
the challenge and credential flags so the shared classifier reports
needs_auth everywhere: the list view and menu offer /mcp auth, capability
search emits its authentication guidance, the startup notice names the
server, and the model catalog stops calling it ready. The rejected
credentials stay installed until a successful re-auth replaces them.
Extend the expired-refresh e2e to assert the re-authentication
classification in fx mcp list output.