Context
The refactor in 2bbcb1a unified issue attachment uploads on a shared persistIssueAttachment service backed by POST /issues/:id/attachments (multipart) and inline attachments[] (base64) on issue create across REST + MCP.
As part of that cleanup the legacy Strapi-era POST /api/upload endpoint was removed from @forge/core. One caller still lives outside the issue domain and now fails loud:
packages/dev/src/pages/project/agent-chat/useAgentChatHandlers.ts:58 — paste/drop a file in the desktop agent-chat panel ⇒ uploadFile() throws legacy /api/upload no longer exists.
The function is intentionally a fail-loud stub (not silently dropped) so the gap surfaces in QA instead of regressing into ghost files.
Goal
Restore chat attachments end-to-end without re-introducing a generic upload endpoint. Attachments should live next to the agent-session message they were posted with.
Proposed shape
- Schema — add
agent_session_attachments (or fold into existing agent_messages payload) with the same FK/storage pattern as issue_attachments (uploader, mime, size, path, key prefix agent-sessions/<id>/<ts>-<name>).
- Service — extract a generic
persistAttachmentBytes({ scope: 'issue' | 'agent-session', parentId, bytes, ... }) from packages/core/src/issues/attachment-service.ts so it can back both surfaces. ALLOWED_MIMES + size caps stay in one place.
- Routes —
POST /agent-sessions/:id/attachments (multipart) for desktop paste-flow; inline base64 array on forge_agent_messages MCP create if/when it's needed.
- Desktop client — replace
uploadFile() callsite with uploadAgentSessionAttachment(sessionId, file) mirroring uploadIssueAttachment. Pre-create staging not needed because the message row exists before the upload.
- Drop the stub — once wired, delete
uploadFile() and its index re-export entirely.
Out of scope
- Chat attachments on the web widget / mobile (no current callers).
- Streaming uploads >10 MB (cap stays at
UPLOADS_MAX_BYTES).
Links
- Service:
packages/core/src/issues/attachment-service.ts
- Stub callsite:
packages/dev/src/lib/api/misc.ts (uploadFile)
- Failing chat callsite:
packages/dev/src/pages/project/agent-chat/useAgentChatHandlers.ts:58
- Refactor commit: 2bbcb1a
Context
The refactor in 2bbcb1a unified issue attachment uploads on a shared
persistIssueAttachmentservice backed byPOST /issues/:id/attachments(multipart) and inlineattachments[](base64) on issue create across REST + MCP.As part of that cleanup the legacy Strapi-era
POST /api/uploadendpoint was removed from@forge/core. One caller still lives outside the issue domain and now fails loud:packages/dev/src/pages/project/agent-chat/useAgentChatHandlers.ts:58— paste/drop a file in the desktop agent-chat panel ⇒uploadFile()throwslegacy /api/upload no longer exists.The function is intentionally a fail-loud stub (not silently dropped) so the gap surfaces in QA instead of regressing into ghost files.
Goal
Restore chat attachments end-to-end without re-introducing a generic upload endpoint. Attachments should live next to the agent-session message they were posted with.
Proposed shape
agent_session_attachments(or fold into existingagent_messagespayload) with the same FK/storage pattern asissue_attachments(uploader, mime, size, path, key prefixagent-sessions/<id>/<ts>-<name>).persistAttachmentBytes({ scope: 'issue' | 'agent-session', parentId, bytes, ... })frompackages/core/src/issues/attachment-service.tsso it can back both surfaces. ALLOWED_MIMES + size caps stay in one place.POST /agent-sessions/:id/attachments(multipart) for desktop paste-flow; inline base64 array onforge_agent_messagesMCP create if/when it's needed.uploadFile()callsite withuploadAgentSessionAttachment(sessionId, file)mirroringuploadIssueAttachment. Pre-create staging not needed because the message row exists before the upload.uploadFile()and its index re-export entirely.Out of scope
UPLOADS_MAX_BYTES).Links
packages/core/src/issues/attachment-service.tspackages/dev/src/lib/api/misc.ts(uploadFile)packages/dev/src/pages/project/agent-chat/useAgentChatHandlers.ts:58