The browser host snapshotted the request through its own RPC, then
prepare_http_send read the row again to build what was sent. An edit
committed between the two left the response pointing at a version it
never sent.
prepare_http_send now versions the stored row it has just read, in the
same call, and hands the id back with the prepared send; the response
takes it from there.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Since the plugin fix, every HTTP send versioned the stored row by
reading it again in resolve_send_inputs. On the desktop that read runs
after awaiting plugin startup, so an edit landing in between was
versioned while the earlier copy was sent, and the response named
content it never sent.
Callers now say where their request came from. One that loaded the
stored row and passes it on as is (the desktop send, the by-id sends,
MCP) gets that same copy versioned, with no second read. One that may
hold a rendered request (plugin sends) keeps the read-by-id fallback,
so rendered values still never reach history.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Clean merge with no conflicts or compile fixes. Regenerated bindings
differ from the merged files only in formatting, so none are included.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A workspace delete removes versions by workspace_id, but a request
moved to another workspace kept versions tagged with the old one, so
deleting the old workspace deleted versions that responses in the new
workspace still pointed at.
The workspace cascade has to stay: a workspace delete removes its
requests in bulk and never runs each request's own version cascade.
Instead, every request upsert retags that request's versions to its
current workspace, one indexed UPDATE that changes no rows unless the
request moved.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
The previous commit had connect re-read the request by id inside its
transaction, which runs after template rendering and auth. An edit made
during those awaits would have been versioned even though the
connection used the earlier copy. Connect already holds the stored row
it loaded, so it goes back to snapshotting that; only the HTTP send,
whose request may arrive rendered, reads the stored row by id, through
the new snapshot_stored_request_for_send.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
A plugin can send a request it has already rendered, such as the
response template function. resolve_send_inputs snapshotted whatever
request it was handed, so those versions held rendered values, secrets
included, where the user had templates, and a restore would have
written the literals back.
snapshot_request_for_send now takes a request id and reads the stored
row inside its transaction. A request with no id or no stored row gets
no version, as a failed snapshot already did. gRPC and WebSocket connect
go through the same method, so all three capture the same thing.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Restore told open editors to re-read the request before its change event
had reached the store, so header and parameter rows could keep showing
the old content. The restore RPC now returns the restored request, and
the frontend puts it in the store before calling wasUpdatedExternally,
the same way deleteModel applies its change locally once the RPC
resolves.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Conflicts were all at registration points; both sides are kept.
Main now routes every write through one connection (#642): ClientDb only
reads and writes go through `with_tx` on a WriteDb. Versioning follows:
snapshot, restore, prune and the cascade delete move to WriteDb, while the
lookups and the "has it changed" check stay on ClientDb. Callers take a
write transaction the way their neighbours do:
- The HTTP send snapshots in its own transaction from
`resolve_send_inputs`, and a failure there (including not getting the
writer) is still logged and swallowed, so the response just has no
version.
- gRPC and WebSocket connect snapshot inside the transaction that upserts
the connection.
- The snapshot and restore RPCs, on the desktop and in the browser host,
each run in a transaction.
- The request and workspace cascades live on main's WriteDb deletes.
The Request Changed dropdown moves into the response pane's
`statusLineRight` slot, since main moved the status line into the shared
pane view.
The plugin runtime's copy of the model bindings picks up `versionId`, and
`gen_rpc.ts` is regenerated.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
CVE-2026-111497 is an out-of-bounds write in the Brotli decoder. It
reaches Yaak through the bundled Node (`yaaknode`, used by plugins) and
through the pure-Rust `brotli-decompressor` that yaak-http uses to decode
`br` responses.
- Bump the plugin runtime Node from 24.11.1 to 24.22.2 and update the
vendoring script's SHA256 pins from the signed SHASUMS256.txt.
- Move yaak-http from `brotli = "8"` to `brotli = "9"`, locking brotli
9.1.1 and brotli-decompressor 6.2.0.
Tauri's pinned git rev still pulls brotli 8.0.1 and brotli-decompressor
5.0.0 for its embedded-asset compression; that needs a Tauri bump.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Rebuild a bare-path plain-HTTP request's URL from Host, answer 400
"no host in request" when there is none, and refuse with 508 a rebuilt
request that is routed back into the proxy, which would otherwise loop
without end.
- Drop Host when forwarding to an HTTP/2 upstream. Google's servers reset
every stream that carries Host alongside :authority.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>