# Thinking Control Flow

> Modify thinking behavior in vscode-shim (auto-expand, enable by default, toggle, conditional display). Use when thinking appears non-streaming or needs UI/default changes.

- Skill: `tankygranny05/thinking-control-flow` (Agent Skill)
- Install (CLI): `npx skillmds@latest add tankygranny05/thinking-control-flow`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tankygranny05/thinking-control-flow/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: tankygranny05 (https://skillmd.com/u/tankygranny05)
- Updated: 2026-09-10
- Page: https://skillmd.com/skills/tankygranny05/thinking-control-flow

---


# Thinking Control Flow
*[Created by Codex: 019bd742-f29e-70a1-ae7a-70c54d7277f4]*
*[Edited by Codex: 019bd742-f29e-70a1-ae7a-70c54d7277f4]*

Last verified: claude-code-2.1.11, 2026-01-20

## 5-minute Fix TL;DR

If thinking “doesn’t stream,” it’s almost always collapsed UI. Apply this one-liner:

```javascript
<details open={isCurrentlyThinking || isExpanded}>
```

## Table of Contents

1. Key Insight
2. Quick Fixes (Solution First)
3. Persistence Options
4. Data Flow Diagram
5. Validation Checklist
6. Per-block vs Global Expansion
7. Thinking Token Budget Mapping
8. Debug Commands + Expected Output
9. Diagnostics Table
10. Warnings + Rollback
11. Thinking Code Line Map
12. Re-verify Line Numbers

## Key Insight

**Thinking already streams; it looks “non‑streaming” because `<details>` is collapsed by default.** Fixing the `open` logic reveals live deltas.

## Quick Fixes (Solution First)

### 1) Auto‑expand while streaming (shows live deltas)

**File:** `src/public/webview/index.js`  
**Function:** `xZe`  
**Line:** ~238487  

```javascript
// BEFORE
<details open={isExpanded}>

// AFTER (streaming visible, user toggle still respected)
<details open={isCurrentlyThinking || isExpanded}>
```

Line anchors from investigation:
- `xZe` render block at `src/public/webview/index.js:238487`.
- Streaming deltas append at `src/public/webview/index.js:171450`.
- `content_block_delta` triggers `updated()` at `src/public/webview/index.js:171377`.
- Default expand state initialized at `src/public/webview/index.js:240540`.

### 2) Enable thinking by default for new sessions

**File:** `src/server.py`  
**Where:** init response and launch fallback  

```python
# init_response (search for "init_response")
"thinkingLevel": "default_on"

# launch (search for "launch" or "thinking_level")
thinking_level = options.get("thinkingLevel") or "default_on"
```

Important: If you keep seeing `thinkingLevel: "off"` in logs, you are likely hitting an older process. Start with `--kill-port` or verify the PID bound to 8787.

### 3) Always expanded (never collapse)

```javascript
<details open={true}>
```

### 4) Hide thinking until complete (no streaming view)

```javascript
if (isCurrentlyThinking) return null;
```

## Persistence Options (keep user preference across reloads)

### A) Use `acquireVsCodeApi` state (recommended)

**File:** `src/public/webview/index.js`  
**Where:** initialize `thinkingLevelOverride`

```javascript
// NOTE: This code must run inside the session class (Vk) where
// `this.thinkingLevelOverride` is defined. Do not paste at module scope.

// Read on boot (inside session constructor or init)
const api = acquireVsCodeApi();
const stored = api.getState()?.thinkingLevelOverride;
if (stored && !this.thinkingLevelOverride.value) {
  this.thinkingLevelOverride.value = stored;
}

// Write on toggle (inside setThinkingLevel method)
api.setState({ ...api.getState(), thinkingLevelOverride: nextLevel });
```

### B) Use localStorage (shim‑only)

**Exact code to persist thinking preference using localStorage**

**File:** `src/public/webview/index.js`  
**Where:** near the toggle handler and session init

```javascript
// NOTE: Same constraint—must be inside session class (Vk) for `this` access.

// Read on boot (inside session constructor or init)
const storedThinkingLevel = localStorage.getItem("thinkingLevelOverride");
if (storedThinkingLevel && !this.thinkingLevelOverride.value) {
  this.thinkingLevelOverride.value = storedThinkingLevel;
}

// Write on toggle (inside setThinkingLevel method)
localStorage.setItem("thinkingLevelOverride", nextLevel);
```

Note: `src/public/vscode-shim.js` already stores `acquireVsCodeApi().setState` in
localStorage under `claude-webview-state`, so either approach works.

## Data Flow Diagram (ASCII)

```
CLI (stream-json)        Shim (server.py)          Webview (index.js)
  | stream_event            | from-extension          | window.postMessage
  | content_block_start ----> io_message ------------> assembler.processStreamEvent
  | type: thinking                                   | xZe renders <details>
  | content_block_delta ---> io_message ------------> yet(): thinking_delta
  | thinking_delta                                     updated() bumps key
```

## Sequence Diagrams
*[Edited by Opus: 933941fe-4c97-4da0-a807-96b2b63e9f03]*

### 1. Session Init (how thinking level is set on startup)

```
┌─────────┐          ┌─────────────┐          ┌─────────────┐
│ Browser │          │ server.py   │          │ index.js    │
└────┬────┘          └──────┬──────┘          └──────┬──────┘
     │                      │                        │
     │  GET /               │                        │
     │─────────────────────>│                        │
     │                      │                        │
     │  index.html + JS     │                        │
     │<─────────────────────│                        │
     │                      │                        │
     │  EventSource /events │                        │
     │─────────────────────>│                        │
     │                      │                        │
     │  SSE: init_response  │                        │
     │  {thinkingLevel:     │                        │
     │   "default_on"}      │                        │
     │<─────────────────────│                        │
     │                      │                        │
     │                      │   postMessage          │
     │                      │   {type:"response",    │
     │                      │    response:{type:     │
     │                      │     "init_response"}}  │
     │                      │ ─────────────────────> │
     │                      │                        │
     │                      │                        │ store in
     │                      │                        │ state.thinkingLevel
     │                      │                        ▼
```

**Key file:line refs:**
- `server.py:1091` — init_response thinkingLevel
- `index.js:242940` — message bridge receives postMessage
- `index.js:171553` — computed thinkingLevel stored

---

### 2. Thinking Stream (deltas flowing from CLI to UI)

```
┌─────────┐       ┌─────────────┐       ┌─────────────┐       ┌─────────┐
│  CLI    │       │ server.py   │       │ vscode-shim │       │ index.js│
│(claude) │       │  (SSE)      │       │    .js      │       │  (xZe)  │
└────┬────┘       └──────┬──────┘       └──────┬──────┘       └────┬────┘
     │                   │                     │                   │
     │ stream_event      │                     │                   │
     │ content_block_start                     │                   │
     │ {type:"thinking"} │                     │                   │
     │──────────────────>│                     │                   │
     │                   │                     │                   │
     │                   │ SSE: io_message     │                   │
     │                   │ wrapped in          │                   │
     │                   │ {type:"from-extension"}                 │
     │                   │────────────────────>│                   │
     │                   │                     │                   │
     │                   │                     │ postMessage       │
     │                   │                     │──────────────────>│
     │                   │                     │                   │
     │                   │                     │                   │ assembler
     │                   │                     │                   │ creates block
     │                   │                     │                   ▼
     │ stream_event      │                     │                   │
     │ content_block_delta                     │                   │
     │ {thinking_delta:  │                     │                   │
     │  "Let me..."}     │                     │                   │
     │──────────────────>│                     │                   │
     │                   │                     │                   │
     │                   │ SSE: io_message     │                   │
     │                   │────────────────────>│                   │
     │                   │                     │ postMessage       │
     │                   │                     │──────────────────>│
     │                   │                     │                   │
     │                   │                     │                   │ yet() appends
     │                   │                     │                   │ i.thinking +=
     │                   │                     │                   │ updated()
     │                   │                     │                   ▼
     │ (repeat deltas)   │                     │                   │
     │ ═══════════════════════════════════════════════════════════>│
     │                   │                     │                   │
     │ stream_event      │                     │                   │
     │ content_block_stop│                     │                   │
     │──────────────────>│                     │                   │
     │                   │────────────────────>│──────────────────>│
     │                   │                     │                   │
     │                   │                     │                   │ isCurrently
     │                   │                     │                   │ Thinking=false
     │                   │                     │                   ▼
```

**Key file:line refs:**
- `index.js:171377` — content_block_delta triggers `updated()`
- `index.js:171450` — `thinking_delta` appended to `i.thinking`
- `index.js:238487` — `xZe` renders `<details open={isCurrentlyThinking || isExpanded}>`

---

### 3. Toggle Thinking Level (user clicks toggle)

```
┌─────────┐       ┌─────────────┐       ┌─────────────┐       ┌─────────┐
│  User   │       │ index.js    │       │ server.py   │       │  CLI    │
│ (click) │       │ (webview)   │       │  (shim)     │       │(claude) │
└────┬────┘       └──────┬──────┘       └──────┬──────┘       └────┬────┘
     │                   │                     │                   │
     │ click toggle      │                     │                   │
     │──────────────────>│                     │                   │
     │                   │                     │                   │
     │                   │ postMessage         │                   │
     │                   │ {type:"request",    │                   │
     │                   │  request:{type:     │                   │
     │                   │   "set_thinking_level",                 │
     │                   │   thinkingLevel:"off"}}                 │
     │                   │────────────────────>│                   │
     │                   │                     │                   │
     │                   │                     │ control_request   │
     │                   │                     │ set_max_thinking  │
     │                   │                     │ _tokens: 0        │
     │                   │                     │──────────────────>│
     │                   │                     │                   │
     │                   │                     │                   │ (thinking
     │                   │                     │                   │  disabled)
     │                   │                     │                   ▼
     │                   │                     │                   │
     │                   │ SSE: response       │                   │
     │                   │ {type:"response",   │                   │
     │                   │  response:{type:    │                   │
     │                   │   "set_thinking_level_response"}}       │
     │                   │<────────────────────│                   │
     │                   │                     │                   │
     │                   │ update              │                   │
     │                   │ thinkingLevelOverride                   │
     │                   ▼                     │                   │
```

**Key file:line refs:**
- `index.js:171849` — `setThinkingLevel` updates override
- `server.py:1276` — `set_thinking_level` handler
- `server.py:499` — `max_thinking_tokens` mapping (off=0)

---

### 4. Launch New Session (with thinking level)

```
┌─────────┐       ┌─────────────┐       ┌─────────────┐       ┌─────────┐
│  User   │       │ index.js    │       │ server.py   │       │  CLI    │
│ (send)  │       │ (webview)   │       │  (shim)     │       │(spawned)│
└────┬────┘       └──────┬──────┘       └──────┬──────┘       └────┬────┘
     │                   │                     │                   │
     │ submit prompt     │                     │                   │
     │──────────────────>│                     │                   │
     │                   │                     │                   │
     │                   │ postMessage         │                   │
     │                   │ {type:"launch_claude",                  │
     │                   │  prompt:"...",      │                   │
     │                   │  thinkingLevel:     │                   │
     │                   │   "default_on"}     │                   │
     │                   │────────────────────>│                   │
     │                   │                     │                   │
     │                   │                     │ spawn CLI         │
     │                   │                     │ --max-thinking-   │
     │                   │                     │ tokens 31999      │
     │                   │                     │──────────────────>│
     │                   │                     │                   │
     │                   │                     │      (CLI starts) │
     │                   │                     │                   ▼
     │                   │                     │                   │
     │                   │                     │ stream begins     │
     │                   │                     │<══════════════════│
     │                   │<════════════════════│                   │
     │<══════════════════│                     │                   │
```

**Key file:line refs:**
- `server.py:699` — launch fallback `thinking_level = options.get("thinkingLevel") or "default_on"`
- CLI spawn includes `--max-thinking-tokens` based on level

---

### 5. UI Render Decision (xZe component)

```
                    ┌─────────────────────────────────┐
                    │         xZe(props)              │
                    │   isCurrentlyThinking: bool     │
                    │   isExpanded: bool (global)     │
                    │   thinking: string              │
                    └───────────────┬─────────────────┘
                                    │
                    ┌───────────────▼───────────────┐
                    │  thinking.trim() === "" ?     │
                    └───────────────┬───────────────┘
                           yes │          │ no
                               ▼          ▼
                    ┌──────────────┐  ┌──────────────────────────┐
                    │ return null  │  │ <details                 │
                    │ (no render)  │  │   open={isCurrently      │
                    └──────────────┘  │         Thinking ||      │
                                      │         isExpanded}>     │
                                      │   <summary>Thinking      │
                                      │   </summary>             │
                                      │   <div>{thinking}</div>  │
                                      │ </details>               │
                                      └──────────────────────────┘
                                                   │
                          ┌────────────────────────┼────────────────────────┐
                          │                        │                        │
                          ▼                        ▼                        ▼
                 ┌─────────────────┐    ┌─────────────────┐    ┌─────────────────┐
                 │ isCurrently     │    │ !isCurrently    │    │ !isCurrently    │
                 │ Thinking=true   │    │ Thinking &&     │    │ Thinking &&     │
                 │                 │    │ isExpanded=true │    │ isExpanded=false│
                 │ OPEN (streaming │    │ OPEN (user      │    │ CLOSED (user    │
                 │ visible)        │    │ expanded)       │    │ collapsed)      │
                 └─────────────────┘    └─────────────────┘    └─────────────────┘
```

**Key insight:** The `||` in `open={isCurrentlyThinking || isExpanded}` ensures streaming is always visible, while still respecting user toggle after completion.

## Validation Checklist

- [ ] `thinking_delta` appears in `cli_to_extension.jsonl`
- [ ] `init_response` includes `thinkingLevel: "default_on"`
- [ ] `launch_claude` includes `thinkingLevel: "default_on"`
- [ ] CLI spawn args include `--max-thinking-tokens`
- [ ] `<details open=...>` renders streaming thinking as expected

## Per-block vs Global Expansion

**Current behavior:** global state `areThinkingBlocksExpanded` lives in the session view
and is passed to all thinking blocks.  

**Per-block approach (untested/theoretical):** track expanded state per block key.

**Key changes (conceptual, untested—not a complete patch):**

1. Change state from boolean to Map:
```javascript
// BEFORE
let [S, E] = (0, qe.useState)(!1)

// AFTER
let [S, E] = (0, qe.useState)(new Map())
```

2. Change toggle function to use blockKey:
```javascript
function xc(blockKey) {
  E((prev) => {
    let next = new Map(prev);
    next.set(blockKey, !(next.get(blockKey) ?? false));
    return next;
  });
}
```

3. Wire blockKey into xZe (additional work required):
```javascript
// In xZe props:
isExpanded={areThinkingBlocksExpanded.get(blockKey)}
onToggle={() => setAreThinkingBlocksExpanded(blockKey)}

// where blockKey = content.key or message_index + block_index
```

**Note:** This requires threading `blockKey` through the component tree—not shown here.

## Thinking Token Budget Mapping

Current `src/server.py:max_thinking_tokens()` implementation:

| thinkingLevel | max_thinking_tokens |
|---------------|---------------------|
| off           | 0                   |
| *(any other)* | 31999               |

**Note:** The current code treats ALL non-off values as 31999. If you want granular levels (low/medium/high), you must modify `max_thinking_tokens()` to handle them:

```python
def max_thinking_tokens(level: str) -> int:
    return {
        "off": 0,
        "low": 10000,
        "medium": 20000,
        "default_on": 31999,
        "high": 31999,
    }.get(level, 31999)
```

## Debug Commands (use these first)

```bash
# Check init_response thinkingLevel
rg -n "init_response|thinkingLevel" \
  ~/centralized-logs/claude-vscode-shim/extension_to_webview.jsonl | tail -n 1

# Check launch_claude thinkingLevel
rg -n "launch_claude|thinkingLevel" \
  ~/centralized-logs/claude-vscode-shim/webview_to_extension.jsonl | tail -n 1

# Confirm deltas exist
rg -n "thinking_delta|content_block_start\".*\"thinking\"" \
  ~/centralized-logs/claude-vscode-shim/cli_to_extension.jsonl | head -n 5

# Verify server PID bound to 8787
lsof -nP -iTCP:8787 -sTCP:LISTEN
```

## Debug Commands: Expected Output (Examples)

- init_response:
  - Expected: `"thinkingLevel":"default_on"`
- launch_claude:
  - Expected: `"thinkingLevel":"default_on"`
- thinking_delta:
  - Expected: lines containing `"thinking_delta"` and `content_block_start` type `"thinking"`
- lsof:
  - Expected: one `python` process listening on `127.0.0.1:8787`

## Diagnostics Table

| Symptom | Likely Cause | Fix |
|--------|--------------|-----|
| No thinking blocks at all | init default is "off" | Set `thinkingLevel` in init_response and launch fallback |
| Thinking exists but hidden | `<details>` collapsed | Use `open={isCurrentlyThinking || isExpanded}` |
| Toggle appears on but no deltas | CLI not getting tokens | Verify `--max-thinking-tokens` arg and control_request |
| Changes don’t show up | Old server process | Use `--kill-port` or check PID with `lsof` |

## Common Mistakes

1) Editing `src/server.py` but testing an old server process on 8787.  
2) Assuming streaming is broken when the UI is simply collapsed.  
3) Setting init defaults but forgetting the launch fallback.  
4) Editing the webview bundle in production extension (updates will overwrite changes).  
5) Assuming line numbers are stable across extension versions.  

## Warnings + Rollback

Warnings:
- Do not edit the minified extension bundle directly in production; updates overwrite changes.
- Line numbers shift between extension releases; re-verify after upgrades.

Rollback:
- Revert the `<details open=...>` change to restore default collapsed behavior.
- Set `thinkingLevel` back to `"off"` in `init_response` and launch fallback to disable defaults.

## Thinking Code Line Map (file:line pairs)

- `src/public/webview/index.js:171377` content_block_delta -> `updated()`
- `src/public/webview/index.js:171450` `thinking_delta` append
- `src/public/webview/index.js:170934` `lh` content block class (key, updated)
- `src/public/webview/index.js:238487` `xZe` thinking block render
- `src/public/webview/index.js:238562` `Zy` memo render for content blocks
- `src/public/webview/index.js:240540` session view state init for expand toggle
- `src/public/webview/index.js:171553` computed `thinkingLevel`
- `src/public/webview/index.js:171849` `setThinkingLevel` updates override
- `src/public/webview/index.js:242940` message bridge from `window.postMessage`
- `src/public/index.css:8766` thinking summary styles (class `.A`)
- `src/public/index.css:8780` thinking toggle styles (class `.Kt`)
- `src/public/index.css:8785` thinking content styles (class `.Jt`)
- `src/server.py:1091` `init_response` thinkingLevel
- `src/server.py:699` launch fallback `thinking_level`
- `src/server.py:499` `max_thinking_tokens`
- `src/server.py:1276` `set_thinking_level` handler

## Re-verify Line Numbers

Run after upgrading the extension bundle:

```bash
rg -n "xZe|thinking_delta|content_block_delta|thinkingLevelOverride" src/public/webview/index.js
rg -n "thinkingLevel|max_thinking_tokens|set_thinking_level" src/server.py
rg -n "\\.A\\b|\\.Kt\\b|\\.Jt\\b" src/public/index.css
```

