## Problem An imported issue whose bug report *is* a screenshot arrived at the agent as an unfetchable link. Everything needed to show an agent an image already existed — the executor builds a `## Attachments` section pointing at `.fusion/tasks/<id>/attachments/` (`executor.ts:18887`), triage inlines image attachments as base64 vision blocks, and `agent-prompts.ts:171` explicitly permits reading that directory. But the import routes only ever called `createTask()` with the issue body as text and never called `addAttachment()`, so **that directory was always empty for imported issues**. The images can't be fetched later by the agent: GitHub `user-attachments` assets redirect to a signed CDN URL and 404 on private repos without credentials, and GitLab `/uploads/...` needs the instance token. Import time is the only point where those credentials are known to be present. ## Change New `packages/dashboard/src/issue-image-attachments.ts` extracts images from an issue's **body and comments**, downloads them, and stores them via `addAttachment` — which already bridges images into the artifact registry, so they also surface in the UI gallery. Wired into every import surface: - `POST /github/issues/import` - `POST /github/issues/batch-import` - All four GitLab routes, via the shared `importItem` chokepoint Provider differences sit behind an `ImageImportPolicy` rather than one shared host list, because the forges disagree on what matters: | | GitHub | GitLab | |---|---|---| | URL form | absolute | usually relative `/uploads/<sha>/f.png` | | Resolution | n/a | **project**-rooted, not instance-rooted | | Trust boundary | fixed host allowlist | the configured instance origin (self-managed = any host) | | Auth | `Bearer` (gh CLI token) | `PRIVATE-TOKEN` | Notable decisions: - **Extraction runs on the original body, not the translated one.** The translation model can rewrite or drop URLs — the same reason the existing code appends `Source:` *after* translating. - **`resolve()` returning null is the SSRF guard.** It's the single place deciding a URL is ours to fetch, so `` in an issue body is never requested. - **Best-effort.** A failed download or comment fetch never fails an import that already produced the task. - **Batch stays cheap.** The REST `comments` count (free on the payload) skips the comment fetch for issues with none, so a 50-issue batch doesn't pay 50 round trips to discover empty threads. - Capped at 10 images / 5MB each (matching `MAX_ATTACHMENT_SIZE`) / 15s timeout. - `GitLabClient.listNotes` is new and **read-only** — the client's existing "no comment side effects" rule governs writes. ## Verification - **30 new/updated tests pass** (23 helper + route-level wiring on both forges). Route tests drive the real Express routes through to `addAttachment`; the helper tests alone wouldn't prove the wiring. - Typecheck clean (exit 0), lint clean, `check:changesets` passes. - **Pre-existing failures confirmed against the untouched baseline, not caused here:** 5 in `routes-github.test.ts` (`engine-unavailable`, conflict-reclaim) and the `test:gate` `chat.test.ts` mock-completeness failure both reproduce identically on `main` with this branch stashed. One incidental test fix: `routes-gitlab.test.ts` used `mockResolvedValue(jsonResponse(...))`, handing the **same** `Response` instance to every call. A `Response` body is single-use, so the added notes fetch got a consumed body. Switched to `mockImplementation` to build a fresh one per call, matching the neighbouring test. ## Notes for the reviewer Images are attached but the body's markdown links are left as-is — the agent reads the files, and rewriting URLs in operator-visible text seemed worse than leaving them. 🤖 Generated with [Claude Code](https://claude.com/claude-code) <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * GitHub and GitLab issue (and merge request) imports now convert embedded screenshots in issue descriptions and comments/notes into real task attachments. * Works across single-issue and batch import workflows, including project/group import flows. * **Bug Fixes** * Attachment extraction/import is resilient: per-image failures, comment/notes fetch issues, and problematic/unsafe/oversized links won’t break the overall import. * **Tests** * Added comprehensive coverage for URL extraction, provider policies, attachment downloading/limits, redirects, and route integration for both GitHub and GitLab. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1010 B
@runfusion/fusion
| @runfusion/fusion |
|---|
| minor |
summary: Imported GitHub and GitLab issues now carry their screenshots as task attachments, so agents can see them.
category: feature
dev: importIssueImageAttachments (packages/dashboard/src/issue-image-attachments.ts) downloads images embedded in an issue's body and comments and stores them via addAttachment, wired into POST /github/issues/import, POST /github/issues/batch-import, and every GitLab import route via importItem. Provider differences sit behind an ImageImportPolicy: GitHub images are absolute URLs on a fixed host allowlist; GitLab /uploads/... are project-relative and restricted to the configured instance origin. GitLab note bodies come from the new read-only GitLabClient.listNotes. Extraction runs on the original (untranslated) body; downloads are capped at 10 images / 5MB each with a 15s timeout, authenticated per provider (gh CLI token / PRIVATE-TOKEN), and best-effort so a failed image or comment fetch never fails the import.