Skip to content

file attachment support

Kazushi Kamegawa edited this page Jul 19, 2026 · 1 revision

File Attachment Support

Date: 2026-07-19 Tracking: Issue #67, sub-issues #68, #69, #70, #71, and #72

Summary

Implement file attachments in the chat tool window: the paperclip button opens a multi-file picker, a # token in the composer opens an inline workspace-file suggestion list, and pending attachments render as removable chips above the composer. On send, attachments become turn/start input items — local image files as localImage items and other files as mention items, reusing the existing IDE-context mention serialization.

Drag & drop is explicitly deferred: Remote UI supports no code-behind or event handlers, so drop events cannot deliver file paths to the extension process. An upstream SDK feature request is open (VSExtensibility request 561). The deferral is recorded in doc/adr.md.

Design decisions

  • Chips are the single source of truth for attachments; the #filename text is a visual echo. Remote UI exposes no caret position, so token tracking is unreliable; deleting the text does not detach the file — the chip's remove button does.
  • User-picked files may live outside the workspace, but both the extension and the worker validate existence and the protected-directory policy (defense in depth). IDE-context mentions remain workspace-only.
  • Steering turns stay text-only; pending chips are consumed by the next turn/start. Chips clear only after a successful send.

Implementation

  1. Contracts and worker: add AttachmentInfo { Path, Kind } and StartTurnRequest.Attachments; emit localImage/mention items in BuildTurnInput behind a new IsAllowedAttachmentPath gate with deduplication against IDE-context mentions and a cap of 10.
  2. Services: IFilePickerService wrapping the shell multi-file dialog, and IWorkspaceFileSearchService backed by the workspace project query with a bounded disk-enumeration fallback, a short TTL cache, and testable filter/rank logic (top 20, filename prefix > substring > path substring).
  3. Chips UI: AttachmentChipViewModel data contract, PendingAttachments collection on the chat view model, a chip row in the composer grid, and a real attach-button handler (validate, dedupe, cap, announce).
  4. # trigger: a file-suggestion presentation view model parallel to the slash-command one, trailing-token detection with ## escape and mutual exclusion with slash suggestions, 150 ms debounce, and accept behavior that rewrites the token and adds a chip.
  5. Docs: create doc/adr.md (drag & drop deferral and the decisions above) and record the work in doc/task.md.

Validation

  • Unit tests for turn-input shapes, path-policy gating, deduplication, and the empty-attachment regression case.
  • View-model tests for chip add/remove/cap, send copy-and-clear, failure retention, and steering retention with a fake picker and search service.
  • Trigger tests: trailing-token-only activation, ## escape, slash/file mutual exclusion, accept rewrite, chip survival after token edits, and debounce cancellation.
  • Release build with warnings as errors; manual checks in the experimental instance for picker flow, chip rendering, # navigation keys, and caret stability while typing.

Constraints

  • No code-behind or event handlers in Remote UI XAML; all new view-model types carry DataContract/DataMember attributes.
  • localImage support depends on the installed Codex CLI version; an unsupported item type surfaces through the existing app-server error path.

Clone this wiki locally