Repository navigation
feat(server): add experimental GET /api/experimental/media/:hash and opt-in experimental-media-uri capability - #58657
Merged
Merged
Conversation
kitlangton
force-pushed
the
media-storage
branch
from
October 24, 2026 16:08
54b9209 to
3449c4e
Compare
kitlangton
force-pushed
the
media-wire
branch
from
October 24, 2026 16:08
f3d4f57 to
452ae37
Compare
This was referenced Oct 24, 2026
kitlangton
force-pushed
the
media-wire
branch
from
October 24, 2026 21:27
452ae37 to
dd4ee3b
Compare
kitlangton
force-pushed
the
media-backfill
branch
from
October 24, 2026 21:27
d6c1ed7 to
089383c
Compare
kitlangton
force-pushed
the
media-backfill
branch
from
October 25, 2026 20:19
089383c to
13a0ed5
Compare
kitlangton
force-pushed
the
media-wire
branch
from
October 25, 2026 20:19
dd4ee3b to
c0ffb14
Compare
kitlangton
force-pushed
the
media-backfill
branch
from
October 25, 2026 23:01
13a0ed5 to
af9d7d0
Compare
kitlangton
force-pushed
the
media-wire
branch
from
October 25, 2026 23:02
c0ffb14 to
9f57289
Compare
kitlangton
force-pushed
the
media-backfill
branch
from
October 25, 2026 23:26
af9d7d0 to
d541fcb
Compare
kitlangton
force-pushed
the
media-wire
branch
from
October 25, 2026 23:38
9f57289 to
8aea935
Compare
kitlangton
force-pushed
the
media-backfill
branch
from
October 25, 2026 23:38
d541fcb to
c1697d1
Compare
Contributor
Author
|
Superseded by #59103 (GitHub auto-marked this merged into the stack base during a reorder; nothing reached dev). |
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.
What it does
Lets a client skip inline base64 when reading a session and fetch attachment bytes only when it needs them. It's experimental and opt-in at both ends: the client has to ask for refs, and the session's location needs
experimental.media_blobon, which is off by default. Every other client and session gets inline base64 exactly as today.Stack, built on the merged #58656: write (#58741) → backfill (#58742) → wire (this PR) → app (#58743).
Why
With media stored once in
blob(#58741), message reads still turn every ref back into base64, so each load of a long session with screenshots re-sends every image, a third larger than the raw bytes. With refs, a read carries a shortmedia:<hash>per file, and a client fetches each image once and caches it. Before → after bytes per read have not been measured yet.How it works
X-OpenCode-Capabilities: experimental-media-uri. Onlysession.messages,session.messageandsession.inbox.listhave ref variants. They return stored files asmedia:<hash>refs withbytes,widthandheight. No route path is sent or stored.GET /api/experimental/media/:hash(generated client:experimental.media.get, OpenAPI tagexperimental) serves a blob only while a message or inbox item still references it. A blob that's been released or is waiting to be swept returns 404. Responses carry immutableCache-Control, anETagwith 304 support,nosniffand a sandbox CSP. Anything other than PNG, JPEG, GIF or WebP downloads asapplication/octet-stream.[media unavailable: <name> (<mime>)]placeholder instead of a dead link.media:<hash>ref back as an attachment. The server re-admits the stored bytes unchanged, without normalizing them again, and rejects a ref nothing references anymore.Risk
Server, protocol and the generated client. The new route and response shapes are experimental, and clients that don't opt in see no change. These still send media inline on purpose:
session.context, thesession.promptresponse, session export and import, and the runner's model requests. Legacy/api/media/URLs are not supported, since refs are the only form sent or accepted.Proof
bun test test/media.test.ts test/message.test.ts(packages/server): 4 pass. They cover the ref and inline shape switch; headers, ETag and 304; the missing-blob placeholder; following the flag from the session's project config when the server's own directory has it off; and a 404 plus a rejected prompt for an unreferenced blob.bun testfor the core prompt, owned, projector, store and backfill tests: 95 pass.bun test test/promise.test.ts(packages/client): 40 pass.bun run check:generated(protocol, client): clean.bun run typecheck(core, server, protocol): clean.oxlinton changed files: 0 errors.