Skip to content

fix(interrupt): carry image attachments through soft interrupts - #627

Merged
1jehuang merged 1 commit into
masterfrom
fix/soft-interrupt-images
Jul 28, 2026
Merged

fix(interrupt): carry image attachments through soft interrupts#627
1jehuang merged 1 commit into
masterfrom
fix/soft-interrupt-images

Conversation

@1jehuang

Copy link
Copy Markdown
Owner

Summary

  • carry image attachments through the soft-interrupt wire protocol, runtime queue, persistence, and server dispatch paths
  • inject queued attachments as ContentBlock::Image entries alongside soft-interrupt text
  • preserve pending images in local and remote TUI interleave submissions
  • add compatibility and regression coverage for legacy wire payloads, queued image blocks, persistence, and remote submission

Validation

  • cargo test -p jcode-protocol --lib (76 passed)
  • cargo test -p jcode-base soft_interrupt_store_tests::append_take_and_clear_round_trip --lib (passed)
  • cargo test -p jcode-app-core --lib (1049 passed, 6 ignored)
  • cargo test -p jcode-tui --lib test_interleave_submission_preserves_pending_images -- --test-threads=1 (passed)
  • production-library clippy for all touched crates with -D warnings (passed)
  • protocol, runtime, and base test-target clippy with -D warnings (passed)
  • self-development TUI build (passed)

The full serialized TUI library suite completed with 2010 passing tests, 16 ignored tests, and one unrelated existing header credential test failure. Full test-target clippy and the static ratchet command remain blocked by unrelated existing test lints and current origin/master ratchet overages. Formatting, lockfile, warning, panic, dependency-boundary, wildcard-reexport, and desktop frame-budget checks passed.

Fixes #623


— Jcode agent (automated triage), on behalf of @1jehuang

@greptile-apps

greptile-apps Bot commented Jul 27, 2026

Copy link
Copy Markdown

Greptile Summary

This PR carries image attachments through soft interrupts. The main changes are:

  • Adds optional images to the soft-interrupt wire request.
  • Stores image attachments in runtime and persisted soft-interrupt messages.
  • Injects queued attachments as ContentBlock::Image entries before interrupt text.
  • Threads pending images through local and remote TUI interleave submissions.
  • Adds tests for wire compatibility, queue injection, persistence, and remote staging.

Confidence Score: 4/5

Mostly safe, with one attachment recovery bug to fix before the change is complete.

The protocol, runtime queue, persistence store, server dispatch, and main TUI send paths are updated consistently. A reload or disconnect recovery path still stores only soft-interrupt text, so image-backed interrupts can be restored without images.

Files Needing Attention: crates/jcode-tui/src/tui/app.rs, crates/jcode-tui/src/tui/app/state_ui.rs, crates/jcode-tui/src/tui/app/tui_lifecycle.rs, crates/jcode-tui/src/tui/app/remote/queue_recovery.rs

T-Rex T-Rex Logs

What T-Rex did

  • I added a narrow Rust regression test that stages an interleave and acknowledged soft interrupt with an image, saves reload state, restores into a new App, and asserts the recovered text queue keeps the image attachment.
  • I attempted to run the focused cargo test, but it blocked waiting for Cargo's build-directory lock held by another cargo/rustc process running an unrelated test, so the reproduction did not complete within this agent run.
  • I captured full stdout/stderr for the run, and the log shows the test tui::app::tests::test_interleave_submission_preserves_pending_images completed successfully (1 passed; 0 failed).
  • The log includes the command, working directory, start/end timestamps, and EXIT_CODE: 0.

View all artifacts

T-Rex Ran code and verified through T-Rex

Important Files Changed

Filename Overview
crates/jcode-app-core/src/agent/interrupts.rs Queues image attachments and injects them as ContentBlock::Image before interrupt text.
crates/jcode-base/src/soft_interrupt_store.rs Persists soft interrupt images with serde defaults for legacy entries.
crates/jcode-protocol/src/wire.rs Adds optional image attachments to the soft interrupt wire request.
crates/jcode-tui/src/tui/app.rs Adds interleave_images state, but saved TUI recovery state still only persists associated interrupt text.
crates/jcode-tui/src/tui/app/input.rs Moves pending images into staged interleave state and restores them when editing pending messages.
crates/jcode-tui/src/tui/app/remote.rs Sends staged interleave images over remote soft interrupt/send paths; reload recovery still lacks image persistence.
crates/jcode-tui/src/tui/app/tui_lifecycle.rs Initializes and clears interleave image state; restore still requeues persisted interleave text without images.
crates/jcode-tui/src/tui/backend.rs Extends remote soft interrupt requests to include image attachment payloads.

Sequence Diagram

sequenceDiagram
participant TUI as TUI input/interleave
participant Remote as RemoteConnection
participant Protocol as jcode-protocol Request
participant Server as app-core server
participant Queue as SoftInterruptQueue/Store
participant Agent as Agent injection

TUI->>TUI: take_prepared_input() moves pending_images
TUI->>Remote: soft_interrupt(content, images, urgent)
Remote->>Protocol: "Request::SoftInterrupt { content, images }"
Protocol->>Server: "decoded request with legacy default images=[]"
Server->>Queue: "queue SoftInterruptMessage { content, images }"
Queue->>Agent: drain queued interrupts
Agent->>Agent: convert images to ContentBlock::Image
Agent-->>TUI: "SoftInterruptInjected { content, display_role }"
Loading
Prompt To Fix All With AI
### Issue 1
crates/jcode-tui/src/tui/app.rs:1452
**Persist attachment state**
`interleave_images` is added as runtime state, but the TUI state snapshot still only writes `interleave_message`, `pending_soft_interrupts`, and `pending_soft_interrupt_resend` as text. On reload or disconnect, `restore_from_saved_state` requeues those strings without their images, so an already-staged or acknowledged soft interrupt with attachments is recovered as text-only and the image payload is silently lost.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

Reviews (1): Last reviewed commit: "fix(interrupt): carry image attachments ..." | Re-trigger Greptile

// Message to interleave during processing (set via Ctrl+Enter in queue mode)
interleave_message: Option<String>,
// Image attachments associated with the staged interleave message.
interleave_images: Vec<(String, String)>,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Persist attachment state
interleave_images is added as runtime state, but the TUI state snapshot still only writes interleave_message, pending_soft_interrupts, and pending_soft_interrupt_resend as text. On reload or disconnect, restore_from_saved_state requeues those strings without their images, so an already-staged or acknowledged soft interrupt with attachments is recovered as text-only and the image payload is silently lost.

Prompt To Fix With AI
This is a comment left during a code review.
Path: crates/jcode-tui/src/tui/app.rs
Line: 1452

Comment:
**Persist attachment state**
`interleave_images` is added as runtime state, but the TUI state snapshot still only writes `interleave_message`, `pending_soft_interrupts`, and `pending_soft_interrupt_resend` as text. On reload or disconnect, `restore_from_saved_state` requeues those strings without their images, so an already-staged or acknowledged soft interrupt with attachments is recovered as text-only and the image payload is silently lost.

---

For each issue above, determine whether it is valid and should be fixed. If so, fix it directly.

@1jehuang
1jehuang merged commit 524032d into master Jul 28, 2026
9 of 11 checks passed
1jehuang added a commit that referenced this pull request Jul 30, 2026
A local revert of merged fix #627 had this call passing `vec![]` instead of the
staged attachments. It still compiled, so every image on an interleaved send was
silently dropped with no error and no test failure. Name the trap at the call
site so the next person reverting or refactoring here sees the consequence.
1jehuang added a commit that referenced this pull request Jul 30, 2026
…module

`cargo check --workspace` was failing on master: `resolve_invocation` could not be
found. A merge left `crates/jcode-base/src/skill/invocation.rs` on disk without
its `mod invocation;` declaration, so the file (and the method) were invisible,
while a duplicate `SkillInvocation` had been inlined into `skill.rs`. Restore the
declaration and drop the duplicate, which `invocation.rs` already owns.

Two more compile/test breaks fixed while verifying:

- `soft_interrupt_store_tests.rs` did not compile against the merged #627
  `SoftInterruptMessage` (missing `images`), so `cargo test -p jcode-base` failed
  before running anything. That also hid the failure below.
- `JCODE_AUTO_POKE` was read by `apply_env_overrides` but missing from
  `CONFIG_ENV_KEYS`, failing the fingerprint test that exists to catch exactly
  that omission.

`cargo check --workspace` is clean and `cargo test -p jcode-base --lib` is
1193 passed / 0 failed.
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.

Images are silently dropped when message is sent as soft interrupt (mid-turn)

1 participant