Skip to content

Session-resume ## Data References inlines every captured tool-output into SessionStart context (~10K tok on long sessions) #840

@devotox

Description

@devotox

Summary

hooks/session-directive.mjs renders the session-resume directive injected at SessionStart. Its ## Data References section inlines every captured tool-output (grouped.data) into the context block — at both render sites. On a long agentic session (hundreds of tool calls) this adds ~10K tokens to every session start / resume.

This contradicts context-mode's own headline principle, stated in the same SessionStart block it ships (<context_window_protection>):

Every byte a tool returns enters your conversation memory and costs reasoning capacity… the context-mode tools let you do the work in a sandbox and surface only the derived answer — the raw bytes stay out.

The captured outputs are already in the sandbox and queryable via ctx_search — inlining all of them verbatim into the resume directive is exactly the raw-bytes-in-context cost the plugin exists to avoid.

Evidence

hooks/session-directive.mjs, two sites:

// site 1 (lines/array builder) — uncapped, full blob per entry
if (grouped.data?.length > 0) {
  lines.push("## Data References");
  lines.push("");
  for (const ev of grouped.data) lines.push(`- ${ev.data}`);
  lines.push("");
}

// site 2 (block builder) — 150-char-capped per entry, but still ALL entries
if (grouped.data?.length > 0) {
  block += `\n## Data References`;
  for (const ev of grouped.data) {
    const text = ev.data.length > 150 ? ev.data.substring(0, 147) + "..." : ev.data;
    block += `\n- ${text}`;
  }
  block += `\n`;
}

Observed: a single long session rendered a ## Data References section of hundreds of full tool-result JSON blobs ({"stdout":"...","stderr":"","interrupted":false,...}), ~10K tokens, injected on every SessionStart.

Proposed fix

Cap the inline rendering to the most recent N captures (e.g. 8) plus a one-line pointer; the rest stay queryable via ctx_search:

if (grouped.data?.length > 0) {
  lines.push("## Data References");
  lines.push("");
  const recent = grouped.data.slice(-8);
  for (const ev of recent) lines.push(`- ${ev.data}`);
  if (grouped.data.length > recent.length) {
    lines.push(`- … ${grouped.data.length - recent.length} older captures — query with ctx_search (kept in the sandbox).`);
  }
  lines.push("");
}

This keeps recent resume context, preserves full searchability, and aligns the resume directive with the plugin's own raw-bytes-stay-out principle.

Related: the ## Git section can also grow large on long sessions and may warrant a similar cap.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions