Skip to content

fix(desktop): restore file.attach client upload path (Phase 2b companion to #142)#144

Merged
OmarB97 merged 1 commit into
mainfrom
fix/restore-file-attach-client
Jun 10, 2026
Merged

fix(desktop): restore file.attach client upload path (Phase 2b companion to #142)#144
OmarB97 merged 1 commit into
mainfrom
fix/restore-file-attach-client

Conversation

@OmarB97

@OmarB97 OmarB97 commented Jun 10, 2026

Copy link
Copy Markdown
Owner

Why

Companion to #142 (backend file.attach handler + contract 2). The client half of dbbd1d4d0 was reverted by the same blocked-upstream-sync split-brain: on main the attachment-sync path only handled images, so a non-image @file: ref sent to a remote gateway resolved to a client-disk path the gateway can't see ("path outside the allowed workspace"). This restores the client upload path so remote sessions (the Phase 2b cross-device feature) can actually attach files.

What changed

  • apps/desktop/src/app/session/hooks/use-prompt-actions.ts:
    • syncImageAttachmentsForSubmitsyncAttachmentsForSubmit: images keep image.attach / image.attach_bytes; non-image files now use the file.attach RPC (upload bytes when remote, pass the path locally). Restores readFileDataUrlForAttach + the image-bytes helpers that were also clobbered.
    • rewriteOptimistic refreshes the optimistic message after refs resolve.
  • Merged against current main, not a blind take-theirs: kept the Phase 2b sender_device on prompt.submit; the remote check is $connection.get()?.mode === 'remote' || activeBackendIsRemote() so it fires for both whole-app remote mode AND a Phase 2b dialed-endpoint session; rewriteOptimistic carries the same interrupted: false fresh-turn invariant as seedOptimistic.

How to review

  1. syncAttachmentsForSubmit — the image branch is behavior-identical to before; the new file branch is the restored feature.
  2. The combined remote condition + the rewriteOptimistic interrupt reset (the two main-divergence merge points).

Evidence

  • uploads file bytes via file.attach on a remote gateway and passes the path directly via file.attach in local mode now pass; clears a leftover interrupted flag on a fresh submit passes.

Verification

  • tsc -b 0 errors. vitest use-prompt-actions.test.tsx — 15/16 pass. Full vitest src — 469 passed / 7 failed; all 7 are pre-existing on clean origin/main (the 6 in untouched files + the 1 below).

Risks / gaps

  • One pre-existing failure remains in this file: sleep/wake "session not found" resume-retry. It fails identically on clean origin/main, is unrelated to attachments (the resume-retry escape hatch returns ok:false), and is filed separately as a pre-existing-failure investigation (possibly another clobbered half of the upstream-sync split-brain).
  • 2 use-gateway-boot test failures persist on main (remote reconnect escape hatch) — pre-existing, flagged for a separate look (possible fallout from feat(desktop): attach to sessions on other devices — Live section + remote dial (Phase 2b/2) #139's prune keep-set); not touched here.
  • End-to-end remote file upload needs a two-device setup to exercise live; tracked with the rest of the channel canary on F-003-multi-participant-channels.

Collaborators

  • @OmarB97 (operator)
  • Claude Fable 5 (Claude Code)

Companion to PR #142 (backend handler + contract 2). The client half of
dbbd1d4 was reverted by the same blocked-upstream-sync split-brain: on main
the attachment-sync path only handled images (image.attach), so non-image
@file: refs to a remote gateway resolved to a client-disk path the gateway
couldn't see ("path outside the allowed workspace").

Restores the generalized attachment sync (use-prompt-actions.ts):
- syncImageAttachmentsForSubmit -> syncAttachmentsForSubmit: images still use
  image.attach / image.attach_bytes; non-image files now use the file.attach
  RPC (upload bytes on remote, pass path locally). Adds readFileDataUrlForAttach
  + the image-bytes helpers that were also clobbered.
- rewriteOptimistic refreshes the optimistic message after refs resolve.

Merged against current main (not a blind take-theirs): kept the Phase 2b
sender_device on prompt.submit; the "remote" test is `$connection.mode ===
'remote' || activeBackendIsRemote()` so it fires for BOTH whole-app remote
mode AND a Phase 2b dialed-endpoint session; rewriteOptimistic carries the
same interrupted:false fresh-turn invariant as seedOptimistic.

Fixes the 2 file.attach client tests (remote upload, local passthrough) and
the interrupted-flag test. One pre-existing failure remains in this file
(sleep/wake "session not found" resume-retry) — it fails identically on clean
origin/main, is unrelated to attachments, and is filed separately.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@OmarB97 OmarB97 merged commit 8a77fda into main Jun 10, 2026
13 of 19 checks passed
@github-actions

Copy link
Copy Markdown

🔎 Lint report: fix/restore-file-attach-client vs origin/main

ruff

Total: 0 on HEAD, 0 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 0 pre-existing issues carried over.

ty (type checker)

Total: 10583 on HEAD, 10583 on base (➖ 0)

🆕 New issues: none

✅ Fixed issues: none

Unchanged: 5544 pre-existing issues carried over.

Diagnostics are surfaced as warnings — this check never fails the build.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant