feat(workspace): image paste + drag-drop attachments in foreman composer #578
No reviewers
Labels
No labels
area:agents
area:dashboard
area:database
area:design
area:design-review
area:flows
area:infra
area:meta
area:security
area:sessions
area:webhook
area:workdir
security
type:bug
type:chore
type:meta
type:user-story
No milestone
No project
No assignees
2 participants
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
charles/claude-hooks!578
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "boss/571"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Adds POST /foreman/uploads + GET /foreman/uploads/:id and routes paste / drag-drop attachments through the SDK as image content blocks (inline base64 ≤ 1 MiB, upload-store ref above).
Test plan
uploading…thenready, send still works/foreman/uploads/:idlink in its prompt/foreman/uploads/:id)Closes #571
52d43ea8c90ea308eaf3behavior:
resolveChatAttachmentsinapps/server/src/http/handlers/foreman.ts(around thesource === "ref"branch): the gateif (att.kind === "image")checks the client-suppliedkind, butForemanChatAttachmentwithsource: "ref"has nokindfield (packages/shared/src/foreman.ts), andbuildChatAttachmentsinapps/web/src/lib/foreman-attachments.tsnever sets one. Soatt.kindis alwaysundefined→ the image-block branch is never taken → every ref attachment (i.e. every large uploaded image sent viaPOST /foreman/uploads) falls into the non-image path and the foreman gets a text link instead of anImageBlockParam. The test plan item "drag-drop a > 1 MiB PNG" silently produces a file-link turn, not a multimodal image block. Fix: in thesource === "ref"branch, load the upload record first (loadUpload(id)) and branch onupload.record.kind(stored metadata) rather thanatt.kind(absent from the wire type).test-gap: No test in
foreman.test.tscovers the{ source: "ref" }image path. Add a test: callhandleForemanUploadwith a PNG, then callhandleForemanChatwith{ source: "ref", id }, and assert that the enqueued task hasimage_attachmentspopulated (not a file-footer entry intask).The wire shape `ForemanChatAttachment.ref` is `{ source, id }` — no `kind` field. Resolving a ref attachment by checking `att.kind` always falls into the non-image branch, so every ≥1 MiB image upload landed in the foreman as a plain `/foreman/uploads/<id>` link instead of an SDK `ImageBlockParam`. Read `record.kind` from the stored metadata instead. Test added covering the round-trip and the file-ref fallback.resolveChatAttachmentsnow reads the storedrecord.kind(vialoadUploadRecord) before deciding whether to emit anImageBlockParam—att.kindwas alwaysundefinedon the wire shape so every ref-image fell through to the file path. Loading the record first also keeps a non-image ref from pulling a 25 MiB blob off disk.foreman.test.ts—{ source: "ref", id }for an image PNG assertsimage_attachmentsis populated and the task body has no file-link footer; the file counterpart asserts the inverse.Pushed as
56a13c0.Both round-1 findings resolved, CI green.
resolveChatAttachmentsnow callsloadUploadRecord(id)first and branches onrecord.kind— the absent wire-levelatt.kindis no longer consulted. As a bonus the image path no longer double-fetches the record. Two new tests inforeman.test.tscover the ref-image round-trip and the file-ref fallback.