summaryrefslogtreecommitdiffhomepage
path: root/.dispatch
diff options
context:
space:
mode:
authorAdam Malczewski <[email protected]>2026-06-27 20:20:02 +0900
committerAdam Malczewski <[email protected]>2026-06-27 20:20:02 +0900
commitf5dc22f7c14d6c0dd4bcedee5a85b21ecd294aed (patch)
tree8ba78efaefc25148dfb550b4332204822f5acf5d /.dispatch
parentfa0bd9c0e433b1abddc814b48a358c94954c7d36 (diff)
downloaddispatch-web-f5dc22f7c14d6c0dd4bcedee5a85b21ecd294aed.tar.gz
dispatch-web-f5dc22f7c14d6c0dd4bcedee5a85b21ecd294aed.zip
feat(vision): resolve persisted image URLs against the API base
Images are now stored on disk under tmp (not SQLite) and served via GET /images/:conversationId/:imageId. Persisted ImageChunk.url is a compact relative HTTP path (/images/<conv>/<uuid>.png) instead of a base64 data URL. No wire/transport-contract type change (behavior only) — re-mirrored the delta notes. - New pure resolveImageUrl(url, apiBase) helper (core/chunks/image-url.ts, +8 tests): data/absolute URLs pass through; relative paths are prepended with the API base (no double slash; empty base -> root-relative). Exported from core/chunks + re-exported from features/chat. - ChatView: new apiBaseUrl prop; <img src> uses resolveImageUrl. The optimistic echo's data URL passes through; persisted relative paths resolve against the base. +3 tests. - AppStore exposes httpBase (getter); App.svelte passes apiBaseUrl into ChatView and the heartbeat RunModal (also renders image chunks). Verification: svelte-check 0/0; vitest 959/959 (run twice, +11); biome clean; vite build OK. See backend-handoff.md §2j-update-2. Not merged or pushed.
Diffstat (limited to '.dispatch')
-rw-r--r--.dispatch/transport-contract.reference.md8
-rw-r--r--.dispatch/wire.reference.md28
2 files changed, 29 insertions, 7 deletions
diff --git a/.dispatch/transport-contract.reference.md b/.dispatch/transport-contract.reference.md
index 047f050..182279a 100644
--- a/.dispatch/transport-contract.reference.md
+++ b/.dispatch/transport-contract.reference.md
@@ -27,6 +27,14 @@
> `PUT /settings/vision` ← `SetVisionSettingsRequest` (partial: `imageLimit?` non-negative int, 0 = disable
> compaction; `compactionModel?` `<key>/<model>` or null = auto). See `backend-handoff.md` §2j.
>
+> **2026-06-26 update (image storage — NO type change, behavior only):** persisted `ImageChunk.url`s are now
+> compact relative HTTP paths (`/images/<conversationId>/<uuid>.png`) served by the new
+> `GET /images/:conversationId/:imageId` endpoint (raw image bytes + correct Content-Type) — NOT base64 data
+> URLs (images are stored on disk under tmp, not in the SQLite store). `ChatRequest.images` (`ImageInput.url`)
+> is UNCHANGED — clients still send data URLs; the backend saves them to tmp and returns compact paths in
+> the persisted chunks. A client resolves a relative `url` against its API base (`resolveImageUrl`); the
+> optimistic echo's data URL and any absolute URL pass through. See `backend-handoff.md` §2j.
+>
> **2026-06-25 delta (SSH handoff #2 — ADDITIVE to `[email protected]`, NO version bump):** adds the
> computer HTTP API types: `ComputerListResponse` (`GET /computers`), `ComputerResponse` (`GET /computers/:alias`),
> `ComputerStatusResponse` (`GET /computers/:alias/status`), `TestComputerResponse` (`POST /computers/:alias/test`),
diff --git a/.dispatch/wire.reference.md b/.dispatch/wire.reference.md
index c40fe81..75adac5 100644
--- a/.dispatch/wire.reference.md
+++ b/.dispatch/wire.reference.md
@@ -14,6 +14,15 @@
> — the orchestrator's vision handoff transcribes each to a text description (persisted as a separate
> `text` chunk in the SAME user message). See `backend-handoff.md` §2j.
>
+> **2026-06-26 update (image storage — NO type change, behavior only):** `ImageChunk.url` for PERSISTED
+> chunks is now a compact relative HTTP path (`/images/<conversationId>/<uuid>.png`) served by the backend's
+> new `GET /images/:conversationId/:imageId` endpoint (raw bytes + correct Content-Type), NOT a base64 data
+> URL — images are stored on disk under tmp, not in the SQLite conversation store (keeps payloads small).
+> `ImageInput.url` (what a client SENDS on `ChatRequest.images`) is UNCHANGED — still a data URL or
+> `http(s)://` URL; the backend saves it to tmp and returns the compact path in the persisted chunk. A client
+> resolves a relative `url` against its API base (`resolveImageUrl`); a data URL (the optimistic echo) or an
+> absolute URL passes through unchanged. See `backend-handoff.md` §2j.
+>
> **2026-06-23 delta (workspaces handoff — package bumped `0.11.0` → `0.12.0`, ADDITIVE):** adds
> `Workspace` + `WorkspaceEntry` (a list entry with a conversation count) and a required
> `workspaceId: string` on `ConversationMeta` (`"default"` for legacy/unspecified conversations). A
@@ -156,12 +165,17 @@ export interface SystemChunk {
/**
* An image attached to a message (e.g. a user-pasted screenshot or pasted
* photo). Carries a `url` that is EITHER a base64 data URL
- * (`data:image/png;base64,…`) OR an `http(s)://` URL. Vision-capable models
- * receive it natively (the provider serializes it to its image-content
- * format); non-vision models never see it directly — the orchestrator's
- * **vision handoff** transcribes it to a text description (via a
- * vision-capable model) and feeds that text instead, so a text-only model can
- * still reason about the image's contents.
+ * (`data:image/png;base64,…`) OR an `http(s)://` URL OR — for PERSISTED chunks
+ * (history/replay) — a compact relative HTTP path (`/images/<conversationId>/
+ * <uuid>.png`) served by the backend's `GET /images/:conversationId/:imageId`
+ * endpoint (images are stored on disk under tmp, NOT in the conversation store,
+ * to keep SQLite payloads small). A client resolves a relative path against its
+ * API base URL; a data URL (the optimistic echo / a pasted image) or an
+ * absolute URL is rendered as-is. Vision-capable models receive it natively
+ * (the provider serializes it to its image-content format); non-vision models
+ * never see it directly — the orchestrator's **vision handoff** transcribes it
+ * to a text description (via a vision-capable model) and feeds that text
+ * instead, so a text-only model can still reason about the image's contents.
*
* When a transcription was performed, it is persisted as a separate `text`
* chunk alongside the `image` chunk in the SAME user message, so the
@@ -170,7 +184,7 @@ export interface SystemChunk {
*/
export interface ImageChunk {
readonly type: "image";
- /** Image source: a base64 data URL (`data:image/…;base64,…`) or an `http(s)://` URL. */
+ /** Image source: a base64 data URL (`data:image/…;base64,…`), an `http(s)://` URL, or a compact relative path (`/images/<conv>/<uuid>.png`) for persisted chunks. */
readonly url: string;
/**
* Optional MIME type of the image (e.g. `"image/png"`). Inferred from the