How Caveman Mode State is Managed Per Session in Claude Code

Caveman mode state is determined once per session at run start and exists only for the lifetime of that Claude agent run, with no persistence across invocations.

The Caveman SDK implements a stateless, per-run mode selection system for Claude Code integrations. Each time runClaudeAgent executes, it generates a fresh session identifier, evaluates gateway availability, and locks the mode—either "optimized" or "observe-only"—for that specific run. This design ensures predictable behavior without cross-session contamination, as implemented in JuliusBrussee/caveman.

Session ID Generation: The Foundation of Per-Session State

Every Claude agent run receives a unique session identifier constructed from three components: the agent definition ID, a cryptographically random UUID, and a claude- prefix.

In packages/agent/src/claude-runtime.ts, lines 138-140, the session ID is assembled as follows:

const runID = crypto.randomUUID();
const sessionID = `claude-${definition.id}-${runID}`;

This construction guarantees uniqueness across parallel executions while making the session traceable to its originating agent definition. The sessionID becomes the anchor for all subsequent mode decisions and header propagation.

Gateway Negotiation: How Mode Gets Determined

The actual mode selection happens through resolveCaveRoute, which evaluates two inputs: the caller-provided gatewayURL and the cave option ("auto" or "off").

From packages/agent/src/claude-runtime.ts, lines 35-36:

const { useGateway } = await resolveCaveRoute(gatewayURL, options, false);

The function returns a boolean flag that directly controls downstream behavior. No complex state machine or persistent configuration is consulted—just this single negotiation at run start.

Mode Assignment: Optimized vs. Observe-Only

With the gateway negotiation complete, the RunResult.mode field is set unconditionally in packages/agent/src/claude-runtime.ts, lines 83-84:

mode: useGateway ? "optimized" : "observe-only",
  • "optimized" — The local gateway is reachable; requests are compressed, cached, and routed through the Caveman gateway
  • "observe-only" — The gateway is unavailable or disabled (cave: "off"); the SDK connects directly to the LLM provider with no compression

This binary assignment is immutable for the run's duration. There is no mid-run mode switching or dynamic renegotiation.

Explicit Stateless Design: No Session Persistence

Critically, Caveman enforces statelessness by hardcoding persistSession: false in packages/agent/src/claude-runtime.ts, line 213:

persistSession: false,

This flag ensures that:

  • The sessionID is discarded after runClaudeAgent returns
  • Mode selection is re-evaluated fresh on every invocation
  • No local storage, cookies, or files retain session information

Each call to runClaudeAgent is therefore hermetic—previous runs cannot influence subsequent ones, and concurrent runs operate in complete isolation.

Header Propagation: Transient Context Passing

While no persistent state exists, the SDK does propagate session context through HTTP headers for downstream coordination. In packages/agent/src/claude-runtime.ts, lines 23-31:

const headers = {
  "x-cave-session": input.sessionID,
  "x-cave-cache-epoch":
    `${input.definition.id}:${input.sessionID}:${input.prefixSHA256.slice(0, 16)}`,
  // …
};

These x-cave-* headers allow the gateway, proxy layers, and Pi extension to associate requests with the correct session and cache epoch. However, this is one-way communication—downstream components read these headers but write no durable state back to the client.

Practical Implementation Examples

Running with Automatic Mode Detection

import { runClaudeAgent } from "caveman-agent";

const definition = /* your AgentDefinition */;
const input = "Explain quantum entanglement in simple terms.";

const result = await runClaudeAgent(definition, input);
console.log(result.mode);  // "optimized" or "observe-only"

The session ID is generated internally, gateway availability is tested, and the mode is locked without caller intervention.

Forcing Observe-Only Mode Explicitly

await runClaudeAgent(definition, input, {
  cave: "off",  // Bypass gateway regardless of availability
});

Even with a reachable gateway, this run will use "observe-only" mode.

Verifying Per-Run Session Uniqueness

// Two sequential calls = two distinct sessions
const run1 = await runClaudeAgent(definition, "Query 1");
const run2 = await runClaudeAgent(definition, "Query 2");

// run1.sessionID !== run2.sessionID (guaranteed)
// run1.mode may differ from run2.mode if gateway state changed

Key Source Files and Responsibilities

File Responsibility
packages/agent/src/claude-runtime.ts Generates sessionID, calls resolveCaveRoute, sets RunResult.mode, forces persistSession: false, and builds x-cave-* headers
packages/agent/src/runtime.ts Defines RunResult interface including the mode: "optimized" | "observe-only" type
packages/pi-extension/src/provider.ts Injects Caveman headers into provider requests for downstream consumption
packages/cli/src/native-hook-fast.ts Demonstrates CLI-to-runtime session ID forwarding pattern

Summary

  • Per-run session IDs: Fresh UUID generation on every runClaudeAgent call in claude-runtime.ts
  • Single negotiation point: resolveCaveRoute determines mode once at run start
  • Immutable mode assignment: "optimized" or "observe-only" locked in RunResult.mode
  • Explicit statelessness: persistSession: false prevents cross-run contamination
  • Transient header propagation: x-cave-session and x-cave-cache-epoch carry context without persistence

Frequently Asked Questions

Can Caveman mode change during an active Claude agent run?

No. Mode is determined when runClaudeAgent begins and remains fixed for that run's entire lifecycle. The useGateway boolean from resolveCaveRoute is evaluated exactly once, and the resulting mode string is baked into the RunResult object. There is no API to renegotiate or switch modes mid-execution.

Does Caveman store session state between CLI invocations?

No. The persistSession: false setting in claude-runtime.ts explicitly disables all persistence. Each CLI invocation, script execution, or programmatic call to runClaudeAgent starts with a blank slate—new sessionID, fresh gateway check, and independent mode decision.

What happens if the gateway becomes unreachable mid-run?

The mode has already been locked. If useGateway was true at run start, the run continues in "optimized" mode and will fail if gateway requests cannot complete. Conversely, if the mode was "observe-only" at start but the gateway later becomes available, that run will not benefit from optimization. The per-run isolation prevents partial state transitions.

How can I debug which mode was selected for a specific run?

Inspect the mode field on the returned RunResult object. For deeper tracing, enable logging in claude-runtime.ts to capture the generated sessionID and resolveCaveRoute decision. The x-cave-session header visible in network logs also connects individual HTTP requests back to their originating run.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →