Skip to content

proxy: bump headless request_shape capture and header filtering - #55

Merged
Juliusolsson05 merged 2 commits into
mainfrom
fix/proxy-request-shape-capture
May 7, 2026
Merged

Juliusolsson05 merged 2 commits into
mainfrom
fix/proxy-request-shape-capture

Conversation

@Juliusolsson05

@Juliusolsson05 Juliusolsson05 commented May 7, 2026 •

Copy link
Copy Markdown
Owner

Summary

Bumps packages/claude-code-headless so the mitm addon emits request_shape alongside proxy request events. This repairs the body-shape sidecar predicate that was silently inert in production: modern Claude Code requests include large conversation bodies, so the old adapter usually had no body data to inspect and title-gen / branch-name-gen / hook-agent flows could leak into the cc-shell feed as orphan ghosts.

This bump also includes two proxy-capture behavior changes that should be reviewed with the same care as the request-shape field:

  • request headers written to proxy-events.jsonl are filtered through an allowlist, so sensitive headers such as Authorization and unreviewed custom headers are no longer persisted
  • the forensic body_b64 capture cap increases from 256 KiB to 2 MiB; request_shape is still the predicate path, while body_b64 remains for debugging captured small/medium requests

Forensic root cause (debug bundle 2026-05-07T08-26-35-212-5d948ab5)

signal observed
/v1/messages requests with body captured 1 of 23 (the 323-byte warmup quota check)
/v1/messages requests dropped past 256 KiB cap 22 of 23 (Content-Length 681862-1033291)
state.requestShape non-null when isSidecarFlow ran 0
flow_ignored events emitted 0
orphan ghosts visible at the bottom of the user feed 7

The c8c2623 predicate was correct; the addon never fed it data on real Claude Code traffic because every modern request includes the full conversation history.

Submodule change (packages/claude-code-headless)

fix/emit-request-shape now points at commit f978e15 and includes:

  • mitmAddon.py parses the buffered request body inside the addon and emits a small request_shape JSON object regardless of body size
  • ClaudeProxyAdapter.ts accepts optional request_shape, prefers it over parsing body_b64, and keeps body_b64 as the fallback for older addon binaries
  • request header capture now uses an allowlist to avoid persisting Authorization or future sensitive headers by default
  • _REQUEST_BODY_CAP is now 2 MiB for forensic body_b64 capture; request-shape extraction remains independent of that cap

Followups

  • The renderer-side suppression in fix(feed): suppress orphan ghosts with title-gen / predict-next-prompt shape #54 stays in place as belt-and-suspenders for sidecar variants whose system-prompt fingerprint we do not yet have. Once we capture and add the predict-next-prompt prefix to SIDECAR_SYSTEM_PROMPT_PREFIXES, the renderer filter can be removed.
  • A separate PR in the claude-code-headless repo should merge fix/emit-request-shape to its main so the submodule pointer here is reachable from a stable ref.

Verification

  • npm run build in packages/claude-code-headless
  • npm run build:app in cc-shell
  • npx tsx src/testing/verify-sidecar-haiku.ts — 13 passed, 0 failed
  • After merge: user restarts the app, generates a Claude turn, and we expect flow_ignored events for any sidecar shape that matches signal 2a (max_tokens <= 1024 AND messageCount <= 3) or signal 2b (system-prefix fingerprint).

Picks up packages/claude-code-headless 505377a (proxy: emit
request_shape from mitm addon, decouple sidecar predicate from body
size cap).

Why this matters for cc-shell:
  c8c2623's body-shape sidecar predicate has been silently no-op
  since shipping — production debug bundles (e.g.
  2026-05-07T08-26-35-212-5d948ab5) show ~99% of /v1/messages requests
  exceeding the addon's 256 KiB body capture cap, so state.requestShape
  was null and no flow_ignored events ever fired. Title-gen,
  branch-name-gen, and the new predict-next-prompt feature all leaked
  through as live semantic flows, ghost-orphaned, and parked at the
  bottom of the conversation feed.

  After this submodule bump, the addon parses the body in process and
  emits a small request_shape blob regardless of body size, so the
  existing predicate finally has data to work with. The renderer-side
  symptom suppression on fix/orphan-sidecar-ghosts remains useful as
  belt-and-suspenders for any sidecar variant we don't yet have a
  fingerprint for.
…ded request_shape)

Picks up packages/claude-code-headless f978e15 (proxy: filter
request headers + extend request_shape with forensic
discriminators).

Adds to the existing request_shape capture from 505377a:
  - Headers filtered to an allowlist (no Authorization, no
    user-set custom headers) so debug bundles are safe to share.
  - request_shape gains tools_count, system_blocks_count,
    system_total_chars, messages_total_chars,
    attribution_entrypoint — all the discriminators we identified
    by reading vendor/claude-code-src/.
  - body_b64 cap raised from 256 KiB to 2 MiB so forensic tooling
    can decode the actual prompt text from real turns. Previous
    cap silently dropped every body past the warmup quota check.

No cc-shell-side change required — the adapter type extension is
backward-compatible, the renderer is unaffected, and the new
fields are diagnostic-only until a follow-up wires them into
isSidecarFlow.
@Juliusolsson05

Copy link
Copy Markdown
Owner Author

Scope expansion — additional commit on this branch (and the submodule branch):

Building on the original `request_shape` capture, added:

  • Header allowlist (`mitmAddon.py`). `dict(request.headers)` was exfiltrating bearer tokens and ANTHROPIC_CUSTOM_HEADERS-injected secrets to disk. Now restricted to the headers we actually use for forensic correlation (`x-app`, `user-agent`, `x-claude-code-session-id`, `anthropic-beta`, etc.). Authorization is structurally excluded.

  • Extended `request_shape` with four diagnostic discriminators identified by reverse-engineering Claude Code's HTTP layer (`vendor/claude-code-src/full/services/api/client.ts`, `utils/sideQuery.ts`):

    • `tools_count` — real CC turns ALWAYS ship the tools array; sidecars routed through `sideQuery`/`queryHaiku` ship `tools: []` or omit. Strongest single signal we don't yet wire into `isSidecarFlow`.
    • `system_blocks_count`, `system_total_chars`, `messages_total_chars` — magnitude signals.
    • `attribution_entrypoint` — `cc_entrypoint` slug parsed out of the attribution header (`cli` / `sdk-cli` / `mcp` / etc.).
  • Body cap raised 256 KiB → 2 MiB so forensic tooling (debug bundles) can decode actual prompt text. The previous cap silently dropped the body for every conversation past the warmup quota check.

`verify-sidecar-haiku.ts`: 13/13 still passing — predicate logic untouched; all new fields are additive.

Submodule commits: 505377a (original), f978e15 (this expansion).
cc-shell commits: e034ad0 (pointer bump), ba55ae7 (pointer re-bump for f978e15).

@Juliusolsson05 Juliusolsson05 changed the title proxy: bump claude-code-headless to ship request_shape capture proxy: bump headless request_shape capture and header filtering May 7, 2026
@Juliusolsson05
Juliusolsson05 merged commit 6893322 into main May 7, 2026
@Juliusolsson05
Juliusolsson05 deleted the fix/proxy-request-shape-capture branch July 7, 2026 21:51
Juliusolsson05 added a commit that referenced this pull request Sep 27, 2026
Settled reads now also refuse inside an open synchronized update and after an
unredrawn resize, and count a chunk before pty-data listeners run. Interim
pointer; the final bump goes to #55's merge commit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
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