Skip to content

v2.2.0 - feat:thinking/reasoning stream for chat + agent - #60

Merged
ddutchie merged 8 commits into
mainfrom
ddutchie/uxtweakspass2
Jun 23, 2026
Merged

ddutchie merged 8 commits into
mainfrom
ddutchie/uxtweakspass2

Conversation

@ddutchie

@ddutchie ddutchie commented Jun 23, 2026 •

Copy link
Copy Markdown
Owner

What does this PR do?

Adds thinking/reasoning stream support to AI chat and the Cairn Agent — model reasoning text (Claude thinking_delta, OpenAI delta.reasoning) now streams through a separate IPC channel and renders in a collapsible Thinking panel above the assistant's reply, auto-collapsing when content starts streaming. Reasoning is persisted to DB so past messages retain their thinking panel across restarts. Also ships GitHub-style raw HTML in markdown, a chat thread selection fix, and the v2.2.0 changelog.

Type of change

  • Bug fix
  • New feature
  • Refactor / code quality
  • Docs / changelog
  • Tests

Screenshots / recording

Checklist

  • npm run type-check:all passes
  • npm run lint passes
  • npm test passes (runs npm run compile first so electron/bundle-guard.test.ts actually executes — npm run test:bundle to run just that)
  • npm run test:e2e passes (run before merging UI changes or cutting a release)
  • No hardcoded colours — CSS variables only (var(--accent), var(--text-primary), etc.)
  • No text-[Npx] pixel font classes — rem equivalents only (text-[0.714rem], text-xs, etc.)
  • New IPC handlers wrapped in handle() and return IpcResult<T>
  • New DB migrations appended (not edited) in schema.ts
  • New SQL goes in electron/db/queries.ts — single source of truth (imported by both Electron main process and MCP server); never construct a Database instance outside db/client.ts (Electron) or mcp-server.ts (MCP runtime)
  • New dependencies or devDependencies added to ROLE_MAP in scripts/generate-licenses.js and licenses.json regenerated
  • New MCP tools registered in electron/mcp/tools/index.ts dispatch + electron/lib/tool-schemas.ts Zod schema
  • If you added/removed a --external:<pkg> flag in the compile script: updated the appropriate allowlist group (RUNTIME_PROVIDED / OPTIONAL_TRANSITIVE / SUBPROCESS_ONLY / SHIPPED_NATIVE) in electron/bundle-guard.test.ts and (if SHIPPED_NATIVE) shipped the package via electron-builder.yml

Notes for reviewer

  • Reasoning is intentionally stripped from compaction summaries and LLM history replay — only role+content is sent to summarisation. Token counts (reasoningTokens) remain tracked for metrics. See the guidance doc in PR discussion for the full rationale.
  • The chat:thought / pi-agent:thought IPC channels mirror the existing chat:token / pi-agent:token pattern — same payload shape, same listener cleanup pattern.
  • No max_tokens changes — existing 4096 is sufficient for most reasoning models. Gemini may need ≥256; flagged in changelog.
  • The rehype-raw dependency was added in an earlier branch commit (not in this PR's diff) but is covered in the changelog for completeness.

Summary by CodeRabbit

  • New Features
    • Added a collapsible Thinking panel for chat and agent responses, including live reasoning/thought streaming.
    • Chat/agent usage now reports reasoning token counts alongside existing token metrics.
  • Bug Fixes
    • Fixed chat thread switching when creating new threads and when switching projects/workspaces.
  • Improvements
    • Persisted reasoning text for chat and agent messages, and display token breakdown updates (including reasoning/answer).
    • Enabled raw HTML rendering in markdown across chat and notes.
  • Chores
    • Improved lint tooling by adding a missing ESLint dependency.

ddutchie added 3 commits June 22, 2026 17:39
Fixes CodeRabbit ESLint install failure — the eslint.config.mjs imports
from 'eslint/config' (@eslint/core) which was only available transitively
through eslint@9 and not resolvable in clean installs.
Markdown:
- Install rehype-raw and add as first rehype plugin in all three
  pipelines (NoteMarkdownPreview, note-editor, MarkdownContent).
  This preserves GitHub-style raw HTML like <p align="center">,
  <img>, <details>, <summary> instead of stripping them.

Chat sessions:
- SessionPane: handleNewChatThread now calls setActiveChatThreadId
  with the newly created thread's id, so the chat panel immediately
  switches to the new session instead of keeping the old one.
- ChatPanel: the init effect now detects when activeProjectId changes
  and switches to a thread scoped to the new project (via
  getOrCreateThread). Previously the effect short-circuited whenever
  activeChatThreadId was non-null, so switching projects kept showing
  the old project's chat thread.
Thinking / reasoning support for AI chat and Cairn Agent:
- Parse delta.reasoning from OpenAI-compatible SSE on both chat
  (electron/ipc/chat.ts) and pi-agent (electron/lib/pi-agent-loop.ts)
  streaming paths; emit chat:thought / pi-agent:thought IPC channels
- New shared ThinkingPanel component: expanded while reasoning streams,
  auto-collapses on first content token, re-expandable via chevron,
  persisted state collapsed by default
- Persist reasoning text to chat_messages.reasoning and
  pi_agent_messages.reasoning (DB migration v20)
- Parse completion_tokens_details.reasoning_tokens from usage; surface
  Answer / Thinking / Total breakdown in ContextRing popover
- New preload listeners: chat.onThought, piAgent.onThought
- Reasoning stripped from compaction summaries and LLM history replay

Also includes:
- GitHub-style raw HTML in markdown (rehype-raw) [from earlier branch commit]
- Chat thread selection + project switching fix [from earlier branch commit]
- @eslint/core explicit devDependency [from earlier branch commit]
- v2.2.0 changelog
- Version bump to 2.2.0
@coderabbitai

coderabbitai Bot commented Jun 23, 2026 •

Copy link
Copy Markdown

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: bf5b4095-86ba-48ba-9079-98750670bbf1

📥 Commits

Reviewing files that changed from the base of the PR and between b406215 and 8f2b529.

📒 Files selected for processing (7)
  • electron/ipc/chat.ts
  • electron/lib/pi-agent-loop.ts
  • electron/preload.ts
  • src/components/agent/AgentChatPane.tsx
  • src/components/chat/chat-panel/ThinkingPanel.tsx
  • src/components/chat/chat-panel/index.tsx
  • src/store/slices/terminal-sessions.ts
🚧 Files skipped from review as they are similar to previous changes (6)
  • src/components/chat/chat-panel/ThinkingPanel.tsx
  • src/components/chat/chat-panel/index.tsx
  • electron/preload.ts
  • electron/lib/pi-agent-loop.ts
  • src/components/agent/AgentChatPane.tsx
  • electron/ipc/chat.ts

📝 Walkthrough

Walkthrough

Adds end-to-end "thinking/reasoning stream" support: a DB migration adds reasoning columns to chat_messages and pi_agent_messages; backend SSE parsing accumulates delta.reasoning and emits new IPC events (chat:thought, pi-agent:thought); frontend store slices, hooks, and types are extended to carry reasoningTokens and reasoning text; a new collapsible ThinkingPanel component is integrated into chat and agent message bubbles. Also fixes chat thread selection on project changes, enables rehype-raw across all markdown renderers, tightens tool parameter validation, and adds @eslint/core / rehype-raw devDependencies.

Changes

Reasoning/Thinking Stream Feature

Layer / File(s) Summary
Core type contracts for reasoning and token usage
src/types/index.ts, electron/lib/llm.ts, electron/lib/pi-agent-loop.ts
Adds reasoning?: string to ChatMessage, PiAgentMessage, and AgentAssistantMsg; introduces CompletionUsage interface; expands lastUsage objects across threads and sessions with optional reasoningTokens; extends AgentLoopCallbacks with onThought and updated onUsage signature; adds thought_signature to OpenAIMessage for Gemini 3.x tool-call round-tripping.
DB migration and persistence queries
electron/db/schema.ts, electron/db/queries.ts, electron/shared/db-mappers.ts
Migration v20 conditionally adds reasoning columns to chat_messages and pi_agent_messages; addChatMessage, upsertPiMessage, savePiMessages, and toChatMessage are updated to read/write reasoning.
PI agent-loop reasoning accumulation and SSE handling
electron/lib/pi-agent-loop.ts
Accumulates delta.reasoning into a reasoningBuffer, emits via onThought callback, extracts reasoning_tokens from usage, strips prior reasoning before context to prevent sending reasoning back to model, and persists accumulated reasoning on assistant messages; preserves optional thought_signature from tool-call deltas for round-tripping.
Chat tool-loop reasoning handling and IPC wiring
electron/ipc/chat.ts
Extends runToolLoop to accumulate reasoning across local and remote execution, emit via onThought callback, handle completion_tokens_details.reasoning_tokens, strip reasoning from follow-up messages, and return accumulated reasoning on all exit paths; IPC wiring emits chat:thought and chat:usage events and updates chat:done payloads to include reasoning.
PI agent IPC event emission and preload API surface
electron/ipc/pi-agent.ts, electron/preload.ts
PI-agent IPC wiring emits pi-agent:thought events and includes reasoningTokens in pi-agent:usage. Preload exposes chat.onThought and piAgent.onThought subscriptions; expands onDone, onUsage payload types to carry reasoning and optional reasoningTokens and breakdown.
Frontend store slices and useChatStream hook wiring
src/store/slices/terminal-sessions.ts, src/store/slices/chat.ts, src/hooks/useChatStream.ts
Terminal-sessions store adds appendPiThought and extends updatePiUsage/updatePiSubagentUsage with required reasoningTokens; finalisePiMessage preserves reasoning-only messages. Chat slice carries reasoning through addMessage and extends setThreadUsage with optional reasoningTokens and breakdown. useChatStream subscribes to chat.onThought, accumulates streamingThought, and persists e.reasoning on done.
ThinkingPanel component with streaming state machine
src/components/chat/chat-panel/ThinkingPanel.tsx
New memoized ThinkingPanel with collapsible behavior: auto-expands on streaming start, auto-collapses when companion content arrives (unless manually overridden), supports manual toggle, animates pulse during streaming, renders truncated preview when collapsed and full markdown when expanded.
ThinkingPanel integration into message bubbles and token display
src/components/chat/chat-panel/ToolCallIndicator.tsx, src/components/chat/chat-panel/ChatMessageBubble.tsx, src/components/agent/AgentMessageBubble.tsx, src/components/agent/ContextRing.tsx
Integrates ThinkingPanel into ChatMessageBubble, ToolCallIndicator, AgentMessageBubble, and SubagentMessageRow; extends ContextRing to display Answer/Thinking/Total token breakdown; refines spinner logic to suppress when reasoning exists.
Chat and agent pane subscription and persistence
src/components/agent/AgentChatPane.tsx, src/components/chat/chat-panel/index.tsx, src/app/page.tsx
AgentChatPane subscribes to parent and subagent piAgent.onThought events, appends thought deltas via store actions, persists reasoning on message completion/error, and passes reasoningTokens to ContextRing. ChatPanel extracts streamingThought and passes it to ToolCallIndicator. Page hydration restores persisted reasoning from IPC session messages.
Chat thread switching fix, rehype-raw markdown rendering, and supporting changes
src/components/agent/SessionPane.tsx, src/components/chat/chat-panel/index.tsx, src/components/chat/chat-panel/MarkdownContent.tsx, src/components/notes/NoteMarkdownPreview.tsx, src/components/notes/note-editor.tsx, electron/lib/tool-schemas.ts, package.json, changelogs/v2.2.0.md
SessionPane sets active thread ID before switching to chat. ChatPanel replaces thread-init effect with project-change-aware logic using prevProjectIdRef/prevWorkspaceIdRef to preserve threads across project boundaries. rehypeRaw added to all markdown pipelines. search_notes_semantic tool's k parameter tightened to min(1). @eslint/core and rehype-raw added as devDependencies. v2.2.0 changelog documents all changes.

Sequence Diagram(s)

sequenceDiagram
  participant Model
  participant AgentLoop as Agent/Tool Loop
  participant EventEmitter as IPC Emitter
  participant Preload
  participant RendererStore
  participant UI
  Model->>AgentLoop: stream delta.reasoning
  AgentLoop->>AgentLoop: accumulate in reasoningBuffer
  AgentLoop->>EventEmitter: emit thought event
  EventEmitter->>Preload: forward thought event
  Preload->>RendererStore: onThought callback
  RendererStore->>UI: update streamingThought
  Model->>AgentLoop: stream usage with reasoning_tokens
  AgentLoop->>EventEmitter: emit usage event
  EventEmitter->>Preload: forward usage event
  Preload->>RendererStore: onUsage callback
  RendererStore->>UI: update reasoningTokens
  AgentLoop->>AgentLoop: finalize turn with accumulated reasoning
  EventEmitter->>Preload: emit done event
  Preload->>RendererStore: onDone callback
  RendererStore->>UI: render ThinkingPanel with persisted reasoning
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~65 minutes

Possibly related PRs

  • ddutchie/cairn#59: Both PRs modify electron/ipc/chat.ts and electron/lib/pi-agent-loop.ts SSE streaming—this PR extends the same streaming pipelines to emit and accumulate delta.reasoning across chat and agent message handling alongside the retrieved PR's focus on chunk-boundary tool-call argument assembly and error handling.

Poem

🐇 A rabbit thinks deep, then hops into view,
The Thinking Panel blooms when reasoning comes through,
SSE tokens dance from delta to store,
rehype-raw opens each raw HTML door,
Threads switch with grace when projects shift their face,
And reasoning tokens find their rightful place! ✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 23.08% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and specifically summarizes the primary change: adding thinking/reasoning stream support for both chat and agent features in v2.2.0.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch ddutchie/uxtweakspass2

Warning

There were issues while running some tools. Please review the errors and either fix the tool's configuration or disable the tool if it's a critical failure.

🔧 ESLint

If the error stems from missing dependencies, add them to the package.json file. For unrecoverable errors (e.g., due to private dependencies), disable the tool in the CodeRabbit configuration.

ESLint install failed: one or more packages not found in the registry.


Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@electron/ipc/chat.ts`:
- Around line 146-152: In the catch block handling the Local LLM Engine error on
the return statement, change the reasoning field from an empty string to
accumulatedReasoning to maintain consistency with abort path returns (found at
lines 97, 174, 243). This ensures that any reasoning accumulated across multiple
rounds is preserved in the chat:done payload instead of being discarded when an
error occurs in a later round.
- Around line 255-268: The assistantMsg object includes a reasoning field that
will cause API validation errors when sent to OpenAI or Anthropic. Either remove
the reasoning field from the assistantMsg object before it is pushed to the
messages array, or implement provider-specific handling that removes the
reasoning field for OpenAI messages while preserving the original signed
thinking blocks for Anthropic. The reasoning field should not be included in the
messages array that gets sent to the API endpoint since both providers have
strict schema validation and handle reasoning differently internally.

In `@electron/lib/pi-agent-loop.ts`:
- Line 63: The AgentAssistantMsg interface on Line 63 includes an optional
reasoning field that should be preserved in session.messages for UI and database
persistence, but must not be sent to the LLM. To fix this, create a utility
function that strips the reasoning field from assistant messages when they are
prepared for outbound LLM requests. Apply this stripping function at the points
where contextMessages are populated (around Line 462) and where messages are
sent to the LLM (around Line 484), as well as in any other locations mentioned
in the PR comments (630-647). Keep the reasoning field in the interface
definition and in session.messages itself, but ensure it is removed only when
creating the messages array for LLM communication.

In `@src/components/chat/chat-panel/ThinkingPanel.tsx`:
- Around line 41-45: The useEffect at lines 41-45 includes `open` in its
dependency array, which causes the effect to re-run whenever `open` changes.
Since this effect sets `open` to false, it creates a circular dependency that
can conflict with the second effect at lines 48-52 which opens the panel. Remove
`open` from the dependency array of the first useEffect to prevent it from being
triggered by its own state changes, ensuring the closing logic in the first
effect does not create a ping-pong with the opening logic in the second effect.
- Line 91: In the ThinkingPanel component, the border styling on line 91
currently uses the Tailwind alpha variant syntax with border-[var(--border)]/50,
which does not follow the repo's styling guidelines for alpha colors. Replace
the border-[var(--border)]/50 class with the proper color-mix syntax that uses
color-mix(in srgb, var(--border) 50%, transparent) to apply the 50% alpha
transparency to the border color variable, ensuring compliance with the
repository's CSS variable and color-mix requirements.

In `@src/store/slices/terminal-sessions.ts`:
- Around line 182-214: The appendPiThought function creates streaming assistant
messages that can have empty content but non-empty reasoning. The blank-message
cleanup logic (referenced at Line 224) removes messages with empty content
without checking if reasoning exists. Update the cleanup condition that filters
out blank messages to also preserve messages when they have non-empty reasoning.
Specifically, modify the condition that determines whether to drop a piMessage
to check if either content or reasoning is non-empty, so reasoning-only
streaming turns are not lost during finalization.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 63451782-e0f8-409d-8e63-82a8ad386c96

📥 Commits

Reviewing files that changed from the base of the PR and between f13a028 and 6491f6b.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (25)
  • changelogs/v2.2.0.md
  • electron/db/queries.ts
  • electron/db/schema.ts
  • electron/ipc/chat.ts
  • electron/ipc/pi-agent.ts
  • electron/lib/pi-agent-loop.ts
  • electron/preload.ts
  • electron/shared/db-mappers.ts
  • package.json
  • src/app/page.tsx
  • src/components/agent/AgentChatPane.tsx
  • src/components/agent/AgentMessageBubble.tsx
  • src/components/agent/ContextRing.tsx
  • src/components/agent/SessionPane.tsx
  • src/components/chat/chat-panel/ChatMessageBubble.tsx
  • src/components/chat/chat-panel/MarkdownContent.tsx
  • src/components/chat/chat-panel/ThinkingPanel.tsx
  • src/components/chat/chat-panel/ToolCallIndicator.tsx
  • src/components/chat/chat-panel/index.tsx
  • src/components/notes/NoteMarkdownPreview.tsx
  • src/components/notes/note-editor.tsx
  • src/hooks/useChatStream.ts
  • src/store/slices/chat.ts
  • src/store/slices/terminal-sessions.ts
  • src/types/index.ts

Comment thread electron/ipc/chat.ts Outdated
Comment thread electron/ipc/chat.ts
Comment thread electron/lib/pi-agent-loop.ts
Comment thread src/components/chat/chat-panel/ThinkingPanel.tsx Outdated
Comment thread src/components/chat/chat-panel/ThinkingPanel.tsx Outdated
Comment thread src/store/slices/terminal-sessions.ts
ddutchie added 4 commits June 23, 2026 10:14
- Preserve accumulatedReasoning on local LLM error return
- Strip reasoning field from assistant messages before sending to API
  (chat.ts streaming + localllm paths, pi-agent-loop contextMessages)
- Remove circular useEffect dep in ThinkingPanel auto-collapse
- Use color-mix for border alpha per styling guidelines
- Preserve reasoning-only pi messages during finalisation
…ition

Use a ref to track previous text presence so the re-expand effect
doesn't reopen the panel after auto-collapse while text remains
non-empty (ping-pong between the two effects).
…chema

Zod's .positive() generates exclusiveMinimum: 0 which many
OpenAI-compatible proxies reject. Using .min(1) produces
minimum: 1 instead — semantically equivalent and universally
accepted.
Capture thought_signature from SSE delta.tool_calls[] and persist
on assistant tool_calls[] in both runToolLoop and runAgentLoop so
Gemini 3.x with thinking can resume its reasoning state across
multi-turn tool calls.

Extended OpenAIMessage.tool_calls[] and ToolCallSpec with an
optional thought_signature field — opaque to Cairn, only meaningful
when round-tripped back to the proxy/Vertex. Other providers
ignore it.

The reasoning-stripper in pi-agent contextMessages only destructures
reasoning — thought_signature passes through untouched.
@coderabbitai

coderabbitai Bot commented Jun 23, 2026

Copy link
Copy Markdown

Caution

Failed to replace (edit) comment. This is likely due to insufficient permissions or the comment being deleted.

Error details
{}

@ddutchie

Copy link
Copy Markdown
Owner Author

@coderabbitai please review we had an error with your last comment

@coderabbitai

coderabbitai Bot commented Jun 23, 2026 •

Copy link
Copy Markdown
🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
gh pr view 60 --repo ddutchie/cairn --json comments --jq '.comments[] | {author: .author.login, body: .body[0:500], createdAt: .createdAt}'

Length of output: 1464



@ddutchie I can see the issue — the previous review failed when attempting to edit/replace a comment due to a permissions error. I'll kick off a full review of this PR now.

✅ Action performed

Full review finished.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 8

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@electron/ipc/chat.ts`:
- Around line 440-442: When abortCtrl.signal.aborted is true, the chat:done
message being sent hardcodes reasoning to an empty string, which discards the
partial reasoning that was accumulated by runToolLoop(...) before the abort
occurred. Replace the hardcoded `reasoning: ""` in the send call with the actual
accumulated reasoning variable that contains the partial reasoning from the
aborted runToolLoop(...) execution.

In `@electron/lib/pi-agent-loop.ts`:
- Around line 469-475: The reasoning field is being stripped from assistant
messages after the pruner function is called, but transformContext may process
messages while they still contain reasoning data, potentially leaking thinking
text into summaries or LLM history. Move the reasoning stripping to occur before
invoking the pruner by creating a copy of session.messages, removing the
reasoning field from any assistant messages in that copy, then passing the
cleaned copy to the pruner function instead of the raw messages. Optionally keep
the existing post-pruner map strip as a safety net to ensure custom context
transforms cannot access reasoning data.

In `@electron/preload.ts`:
- Around line 141-143: The onUsage callback parameter type in the preload is
missing the breakdown field that the main process emits in its IPC payload.
Locate the onUsage function handler definition around lines 141-143 and add the
missing breakdown property to the callback parameter object type to match the
actual IPC payload structure being emitted from the main process. Also apply the
same type fix to the other onUsage callback location mentioned in the comment.

In `@src/components/agent/AgentChatPane.tsx`:
- Around line 181-184: The onThought listener currently only handles parent
sessionIds and ignores subagent session IDs with the pattern ${sessionId}:sub:*,
while the token handling on lines 301-304 includes dedicated subagent
subscription logic. Add a matching subagent thought subscription and cleanup
alongside the parent thought subscription in the electron.piAgent.onThought
listener block, ensuring that subagent thought events matching the
${sessionId}:sub:* pattern are properly routed to appendPiThought with their
respective subagent sessionIds, just as token subscriptions are handled
separately for subagents.
- Around line 186-192: The usage breakdown information from the event object is
not being passed to the update functions, which causes the breakdown data to be
lost. Add the `e.breakdown` parameter to both the `updatePiUsage` call (for the
parent step branch) and the `updatePiSubagentUsage` call (for the subagent step
branch), placing it after the `reasoningTokens ?? 0` parameter in each function
call to preserve the usage breakdown.

In `@src/components/chat/chat-panel/index.tsx`:
- Around line 283-303: The useEffect in the chat-panel component tracks project
changes via prevProjectIdRef but does not track workspace changes, even though
activeWorkspaceId is a dependency. When the workspace changes while
activeProjectId remains the same (particularly when undefined), the old
workspace's active thread may be retained. Create a ref to track the previous
workspace ID similar to prevProjectIdRef, update prevWorkspaceIdRef at the start
of the effect, and add workspace change detection logic that invalidates the
active thread when activeWorkspaceId changes, ensuring threads are not retained
across workspace boundaries.

In `@src/components/chat/chat-panel/MarkdownContent.tsx`:
- Line 7: Add HTML sanitization to prevent injection attacks when parsing raw
HTML in markdown. First, add rehype-sanitize to the project dependencies, then
import it alongside the existing rehypeRaw import in MarkdownContent.tsx (line
7). In the rehype plugins array (around line 101 in MarkdownContent.tsx), ensure
rehypeSanitize is applied immediately after rehypeRaw to sanitize the parsed
HTML before rendering. Apply the same pattern to NoteMarkdownPreview.tsx (line
48) and note-editor.tsx (line 1007) by adding the import and placing the
sanitization plugin after rehypeRaw in their respective rehype plugin
configurations.

In `@src/components/chat/chat-panel/ThinkingPanel.tsx`:
- Around line 49-56: The useEffect hook that handles re-expanding the thinking
panel on new reasoning tokens needs to also check whether companion content (the
answer) has already started streaming. Currently, the condition only checks for
`streaming`, `text`, `hadText`, and `userOverride`, but doesn't account for late
reasoning deltas arriving after answer streaming has commenced. Add a check to
verify that `companionContent` is empty before allowing the panel to reopen when
the first reasoning token arrives, ensuring the panel won't reopen if an answer
is already being streamed and was previously auto-collapsed.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: afb5c177-9ace-4a5a-b2e7-ffd73d07cd1a

📥 Commits

Reviewing files that changed from the base of the PR and between f13a028 and b406215.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (27)
  • changelogs/v2.2.0.md
  • electron/db/queries.ts
  • electron/db/schema.ts
  • electron/ipc/chat.ts
  • electron/ipc/pi-agent.ts
  • electron/lib/llm.ts
  • electron/lib/pi-agent-loop.ts
  • electron/lib/tool-schemas.ts
  • electron/preload.ts
  • electron/shared/db-mappers.ts
  • package.json
  • src/app/page.tsx
  • src/components/agent/AgentChatPane.tsx
  • src/components/agent/AgentMessageBubble.tsx
  • src/components/agent/ContextRing.tsx
  • src/components/agent/SessionPane.tsx
  • src/components/chat/chat-panel/ChatMessageBubble.tsx
  • src/components/chat/chat-panel/MarkdownContent.tsx
  • src/components/chat/chat-panel/ThinkingPanel.tsx
  • src/components/chat/chat-panel/ToolCallIndicator.tsx
  • src/components/chat/chat-panel/index.tsx
  • src/components/notes/NoteMarkdownPreview.tsx
  • src/components/notes/note-editor.tsx
  • src/hooks/useChatStream.ts
  • src/store/slices/chat.ts
  • src/store/slices/terminal-sessions.ts
  • src/types/index.ts

Comment thread electron/ipc/chat.ts
Comment thread electron/lib/pi-agent-loop.ts Outdated
Comment thread electron/preload.ts Outdated
Comment thread src/components/agent/AgentChatPane.tsx
Comment thread src/components/agent/AgentChatPane.tsx Outdated
Comment thread src/components/chat/chat-panel/index.tsx
Comment thread src/components/chat/chat-panel/MarkdownContent.tsx
Comment thread src/components/chat/chat-panel/ThinkingPanel.tsx Outdated
…threads

- Preserve loopResult.reasoning on abort in chat:done payload
- Strip reasoning before pruner in pi-agent contextMessages to
  prevent leaking thinking text into summaries or transforms;
  keep post-pruner map as safety net
- Add breakdown field to onUsage callback types in preload
- Pass e.breakdown to updatePiUsage/updatePiSubagentUsage
- Add appendPiSubagentThought store action + subagent thought
  subscription/cleanup in AgentChatPane
- Track workspace changes via prevWorkspaceIdRef in chat-panel
  to invalidate active thread across workspace boundaries
- ThinkingPanel re-expand checks companionContent so panel won't
  reopen if answer streaming has already started
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