Summary
When claw runs in interactive REPL mode against an Anthropic-compatible proxy that re-emits streaming responses (in my case claude-code-router v2.0.0 with the built-in deepseek transformer in front of DeepSeek), the assistant's text response is hidden behind the Thinking (0 chars hidden) ✔ Done indicator. The text does arrive — it is written into the session JSONL as a normal {"type":"text"} block — so the receiver/persistence layer is correct, but the live REPL renderer is misclassifying the streamed deltas.
The same proxy and model work correctly in non-interactive claw prompt mode, which suggests the bug is in the REPL streaming-event parser, not in the network/auth/transport layers.
Environment
claw v0.1.0, git SHA 8e45f18 (HEAD of main, 2026-05-01)
- Built with
cargo build --release --workspace
- rustc 1.94.1, Ubuntu 24.04.4 LTS, x86_64
- Proxy:
@musistudio/claude-code-router@2.0.0 running on http://127.0.0.1:3456
- Upstream provider: DeepSeek (
https://api.deepseek.com/chat/completions, model deepseek-v4-pro) via CCR's deepseek transformer
- Env vars used by
claw:
ANTHROPIC_BASE_URL=http://127.0.0.1:3456
ANTHROPIC_AUTH_TOKEN=<ccr-bearer>
Reproduction
- Stand up CCR with a config whose
default route maps to deepseek,deepseek-v4-pro and uses the built-in deepseek transformer.
-
ANTHROPIC_BASE_URL=http://127.0.0.1:3456 \
ANTHROPIC_AUTH_TOKEN=<token> \
./target/release/claw
- At the prompt:
> ping!
Observed
REPL output:
> ping!
⠋ 🦀 Thinking...
▶ Thinking (0 chars hidden)
✔ ✨ Done
>
But the corresponding entry in ~/.claw/sessions/<wsid>/<session>.jsonl contains the actual text:
{
"message": {
"blocks": [{"text": "pong!", "type": "text"}],
"role": "assistant",
"usage": {
"cache_creation_input_tokens": 0,
"cache_read_input_tokens": 4864,
"input_tokens": 26,
"output_tokens": 32
}
},
"type": "message"
}
So the assistant returned a normal text block, but the REPL streaming UI counted 0 visible chars and stuffed everything into the (hidden) thinking section.
CCR side confirms the upstream call succeeded (HTTP 200 in ~1.9 s, model deepseek-v4-pro, stream: true, with tools present in the request). Both --model sonnet and the default claude-opus-4-6 reproduce the issue in the REPL.
Expected
The assistant text (pong! here) should render in the REPL the same way it gets persisted to the session JSONL.
Works around the bug
claw prompt 'ping!' (non-interactive) — prints pong! correctly.
- Reading the session JSONL after a REPL turn — text is there.
So this is reproducible only on the live streaming render path of the REPL.
Hypothesis
CCR's deepseek transformer likely emits SSE events with names/shapes that the REPL parser maps to a thinking block instead of a text block (perhaps content_block_delta with a delta.type other than text_delta, or a thinking_delta event substituted because the upstream emits a reasoning_content field). The receiver path that writes the JSONL appears to look at the final assembled message blocks and is unaffected; the live REPL parser is the divergence point.
If a maintainer can point me at the streaming event handler in the REPL (likely under crates/rusty-claude-cli or wherever the SSE consumer lives), I can capture the raw upstream SSE stream from CCR and attach it to this issue to nail down which event the parser is misclassifying.
Why this matters
Anthropic-compatible proxies (CCR, LiteLLM, OpenRouter's Anthropic shim, etc.) are a primary use case for using claw with non-Anthropic providers. The REPL is unusable against them today even though the underlying model is responding fine.
Summary
When
clawruns in interactive REPL mode against an Anthropic-compatible proxy that re-emits streaming responses (in my caseclaude-code-routerv2.0.0 with the built-indeepseektransformer in front of DeepSeek), the assistant's text response is hidden behind theThinking (0 chars hidden) ✔ Doneindicator. The text does arrive — it is written into the session JSONL as a normal{"type":"text"}block — so the receiver/persistence layer is correct, but the live REPL renderer is misclassifying the streamed deltas.The same proxy and model work correctly in non-interactive
claw promptmode, which suggests the bug is in the REPL streaming-event parser, not in the network/auth/transport layers.Environment
clawv0.1.0, git SHA8e45f18(HEAD ofmain, 2026-05-01)cargo build --release --workspace@musistudio/claude-code-router@2.0.0running onhttp://127.0.0.1:3456https://api.deepseek.com/chat/completions, modeldeepseek-v4-pro) via CCR'sdeepseektransformerclaw:ANTHROPIC_BASE_URL=http://127.0.0.1:3456ANTHROPIC_AUTH_TOKEN=<ccr-bearer>Reproduction
defaultroute maps todeepseek,deepseek-v4-proand uses the built-indeepseektransformer.> ping!Observed
REPL output:
But the corresponding entry in
~/.claw/sessions/<wsid>/<session>.jsonlcontains the actual text:{ "message": { "blocks": [{"text": "pong!", "type": "text"}], "role": "assistant", "usage": { "cache_creation_input_tokens": 0, "cache_read_input_tokens": 4864, "input_tokens": 26, "output_tokens": 32 } }, "type": "message" }So the assistant returned a normal
textblock, but the REPL streaming UI counted 0 visible chars and stuffed everything into the (hidden) thinking section.CCR side confirms the upstream call succeeded (HTTP 200 in ~1.9 s, model
deepseek-v4-pro,stream: true, withtoolspresent in the request). Both--model sonnetand the defaultclaude-opus-4-6reproduce the issue in the REPL.Expected
The assistant text (
pong!here) should render in the REPL the same way it gets persisted to the session JSONL.Works around the bug
claw prompt 'ping!'(non-interactive) — printspong!correctly.So this is reproducible only on the live streaming render path of the REPL.
Hypothesis
CCR's deepseek transformer likely emits SSE events with names/shapes that the REPL parser maps to a thinking block instead of a text block (perhaps
content_block_deltawith adelta.typeother thantext_delta, or athinking_deltaevent substituted because the upstream emits areasoning_contentfield). The receiver path that writes the JSONL appears to look at the final assembled message blocks and is unaffected; the live REPL parser is the divergence point.If a maintainer can point me at the streaming event handler in the REPL (likely under
crates/rusty-claude-clior wherever the SSE consumer lives), I can capture the raw upstream SSE stream from CCR and attach it to this issue to nail down which event the parser is misclassifying.Why this matters
Anthropic-compatible proxies (CCR, LiteLLM, OpenRouter's Anthropic shim, etc.) are a primary use case for using
clawwith non-Anthropic providers. The REPL is unusable against them today even though the underlying model is responding fine.