[FEATURE] Mods: pass images and hidden context rows to session.compact so a hook does not drop them

Status Open
Reported on v2.1.289
Maintainer reply None cached
Activity 0 comments · opened Oct 8, 2026

Preflight Checklist

  • [x] I have searched existing requests and this feature hasn't been requested yet. The closest, #16592, asks for raw pasted image data in UserPromptSubmit hooks. This request is about what a session.compact hook receives and can keep.
  • [x] This is a single feature request (not multiple features)

Problem Statement

A session.compact function hook (Claude Code 2.1.289, macOS arm64, CLAUDE_CODE_ENABLE_FUNCTION_HOOKS=1, mod loaded from a plugin) receives messages: SessionMessage[] with text and tool blocks only. Part of what the model saw is not in that list, so any hook that returns { messages } removes it from context:

| Lost when a hook compacts | In the transcript | Lost in 66 real compactions |
|---|---|---|
| Pasted images | image blocks on user rows | 76 of 76 |
| Images inside tool results | image blocks in tool_result | 424 of 424 |
| Prompts typed while a turn runs | attachment rows, queued_command | 144 of 150 |
| Subagent hand-backs, other sessions | isMeta user rows, origin.kind: peer | 441 of 441 |
| Scheduled prompts | isMeta user rows | 76 of 76 |

These are 66 compactions by one session.compact mod, 28-09 to 07-10-2026, counted from the transcripts before and after each compact_boundary.

After the compaction the model works without the screenshot the user just pasted, or the instruction they typed mid-turn, and nothing tells it that these are gone.

Proposed Solution

Let session.compact see and keep everything the model saw, with additive, optional fields, so existing hooks behave exactly as today:

type SessionMessage = {
  role: Role; text: string; toolUses: ToolUseSummary[];
  toolResults?: ToolResultSummary[]; handle?: string;
  // new, optional
  content?: ContentBlock[];   // image and document blocks in order (base64, or a durable file ref)
  meta?: { kind: 'attachment' | 'meta'; origin?: PromptOrigin; attachmentType?: string };
};
  1. event.messages carries non-text blocks in content, and includes hidden rows (attachments, isMeta rows) in transcript order with meta set.
  2. A message a hook returns with content is written back with those blocks. A hidden row returned with its meta keeps its kind and origin framing.

Acceptance:

  • Paste an image, then compact with a hook that returns event.messages with old tool output trimmed. After --resume, the model still sees the image.
  • Type a prompt while a long Bash call runs, then compact the same way. The prompt is still in context, with its origin.
  • A subagent hand-back from before the compaction is still in context after it.
  • Hooks that ignore the new fields behave as today.

Alternative Solutions

What a mod can do today, and where it falls short:

  • Record deliveries in prompt.submit and session.receive, then write them back as plain text at compaction. Images and the engine's origin framing are lost, and the mod has to dedupe, anchor and retire its own records.
  • Keep the engine handle on plain user prompts that carried an image. A handle on a tool_result row undoes the compaction on --resume (#95328, #96485), so images inside tool results can only become a text pointer to a file, and pasted-image files under /private/tmp are gone after a reboot.

Priority

High - Significant impact on productivity

Feature Category

Developer tools/SDK

Use Case Example

  1. I paste a screenshot of a broken layout and ask Claude to fix it.
  2. Context passes 60%. My session.compact mod compacts early: it trims old tool output and keeps every message word for word.
  3. On the next turn the screenshot is gone. Claude asks for it again, or guesses.

The same happens to "stop, use the other config" typed during a long build, and to a background agent's report that arrived just before the compaction.

Additional Context

  • Measured on Claude Code 2.1.274 to 2.1.289, macOS arm64, in the desktop app and in claude -p.
  • Live probe: keeping handle on the image prompt keeps the image through two rounds of compact plus --resume. Keeping it on a tool_result row brings back the uncompacted history (the same bug as #95328).
  • Related, not duplicates: #16592 (raw pasted image data for UserPromptSubmit hooks; a different event and need), #95328 and #96485 (resume bugs that force hooks to drop handles).

View original on GitHub ↗