Skip to content

feat(advisor): render /advisor calls as tool rows with token accounting#209

Open
WesleyMFrederick wants to merge 1 commit into
matt1398:mainfrom
WesleyMFrederick:feat/advisor-display-upstream
Open

feat(advisor): render /advisor calls as tool rows with token accounting#209
WesleyMFrederick wants to merge 1 commit into
matt1398:mainfrom
WesleyMFrederick:feat/advisor-display-upstream

Conversation

@WesleyMFrederick

@WesleyMFrederick WesleyMFrederick commented Jun 8, 2026

Copy link
Copy Markdown
Contributor

Summary

Renders /advisor tool calls as normal tool rows in the timeline, adds token accounting for advisor output, and an eval-report display.

What changed

  • Advisor rendering: /advisor calls now render as standard tool rows instead of being dropped ([BUG] Missing /advisor support #175).
  • Token accounting: Advisor output tokens are counted, empty input is labeled, and advisor usage surfaces in the context panel.
  • Eval report display: new renderer path for eval reports, plus tool-row improvements.
  • Tool-name string utils: shared helpers in stringUtils.ts for tool-name formatting, used by linking + summaries.
  • Types: jsonl.ts / messages.ts extended for advisor_tool_result / server_tool_use.
  • Hardening: guard block.content.text; harden a test literal.

Tests

  • SemanticStepExtractor.test.ts, displayItemBuilder.test.ts, renderHelpers.test.ts, stringUtils.test.ts, toolLinkingEngine.test.ts, toolTokens.test.ts
  • New fixture: test/mocks/advisorBlocks.fixture.ts

Scope

20 files changed (feature code + tests). Single squashed commit.

Summary by CodeRabbit

  • New Features

    • Added support for advisor tool functionality with proper semantic step tracking and result linking.
    • Added npm scripts (standalone:kill, standalone:restart) for managing standalone mode processes.
  • UI Improvements

    • Tool names now display with proper capitalization across all tool displays.
    • Empty tool parameters now show a "no parameters" placeholder instead of blank fields.
  • Improvements

    • Enhanced token counting logic for advisor tool results.

- Render /advisor tool calls as standard tool rows (matt1398#175)
- Count advisor output tokens; label empty input; surface in context panel
- Add eval-report display path and tool-row improvements
- Add tool-name string utilities (stringUtils) used by linking and summaries
- Extend jsonl/messages types for advisor_tool_result and server_tool_use
- Guard block.content.text; harden test literal

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
@coderabbitai coderabbitai Bot added the feature request New feature or request label Jun 8, 2026

@gemini-code-assist gemini-code-assist Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Code Review

This pull request introduces support for the advisor server tool, handling server_tool_use and advisor_tool_result content blocks across the backend parsing, semantic step extraction, and frontend rendering layers. It ensures that advisor tool calls (which have empty inputs) do not synthesize call tokens, carries over the advisorModel as the source model, and capitalizes tool names in the UI. It also adds helper scripts to package.json for managing the standalone server process, along with comprehensive unit tests. There are no review comments to address, so I have no feedback to provide.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

@coderabbitai

coderabbitai Bot commented Jun 8, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR adds end-to-end support for an "advisor" server tool flow: new JSONL content types encode advisor calls and results, messages carry advisorModel metadata, semantic steps are extracted for advisor interactions, display items are built and linked, tools are rendered with capitalized names and source-model labels, and token accounting skips estimation for advisor calls while counting result tokens. Additionally, standalone mode scripts are added for process lifecycle management.

Changes

Advisor Tool Flow Support

Layer / File(s) Summary
Type contracts for advisor blocks
src/main/types/jsonl.ts, src/main/types/messages.ts
New ServerToolUseContent and AdvisorToolResultContent interfaces, extended ContentBlock union, and optional advisorModel field in AssistantEntry and ParsedMessage to represent advisor tool invocations.
JSONL parsing with advisor metadata
src/main/utils/jsonl.ts
parseChatHistoryEntry extracts advisorModel from assistant entries and propagates it into the returned ParsedMessage.
Semantic step extraction for advisor
src/main/services/analysis/SemanticStepExtractor.ts
extractSemanticStepsFromAIChunk detects server_tool_use blocks (advisor) and emits tool_call steps with sourceModel; detects advisor_tool_result blocks and emits tool_result steps with token counts from advice text.
Advisor semantic extraction tests and fixtures
test/mocks/advisorBlocks.fixture.ts, test/main/services/analysis/SemanticStepExtractor.test.ts
Mock advisor call and result messages; test suite verifies tool_call/tool_result step emission and token accounting.
Display item construction from advisor messages
src/renderer/utils/displayItemBuilder.ts, test/renderer/utils/displayItemBuilder.test.ts
buildDisplayItemsFromMessages records advisor tool calls and results into indexed maps for later linking; subagent-input detection refined to exclude messages with tool results.
Tool name capitalization utility
src/renderer/utils/stringUtils.ts, test/renderer/utils/stringUtils.test.ts
New capitalize helper function and test coverage for name formatting.
Tool rendering with formatting and summaries
src/renderer/components/chat/items/linkedTool/renderHelpers.tsx, src/renderer/utils/toolRendering/toolSummaryHelpers.ts, src/renderer/components/chat/items/LinkedToolItem.tsx, src/renderer/components/chat/SessionContextPanel/items/ToolBreakdownItem.tsx, src/renderer/components/chat/SessionContextPanel/components/RankedInjectionList.tsx, test/renderer/utils/renderHelpers.test.ts
renderInput renders empty inputs as "no parameters" placeholder; getToolSummary accepts optional sourceModel and returns it for advisor tools; components use capitalize for display names and pass sourceModel through summary generation.
Token accounting for advisor tool calls
src/renderer/utils/toolLinkingEngine.ts, test/renderer/utils/toolLinkingEngine.test.ts, test/renderer/utils/toolRendering/toolTokens.test.ts
linkToolCallsToResults sets callTokens to undefined for advisor calls (skipping estimation) while counting result tokens.

Standalone Mode Process Management

Layer / File(s) Summary
Standalone lifecycle scripts
package.json
Added standalone:kill to terminate process on port 3456 and standalone:restart to chain kill → build → start commands.

Suggested labels

feature request

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
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.


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@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: 2

🧹 Nitpick comments (4)
src/main/services/analysis/SemanticStepExtractor.ts (1)

93-109: 💤 Low value

Structural inconsistency: advisor tool_call omits tokens field.

Regular tool_use blocks (lines 73-90) set a tokens field with { input: callTokens, output: 0 }, but the advisor tool_call step omits this field entirely. While test line 44 confirms this is intentional, the structural asymmetry could cause issues if downstream code expects all tool_call steps to have consistent shape.

Consider either:

  1. Explicitly setting tokens: undefined or tokens: { input: 0, output: 0 } for structural consistency
  2. Adding a comment explaining why advisor diverges from the standard tool pattern (e.g., "advisor calls bypass normal token estimation")
🤖 Prompt for 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.

In `@src/main/services/analysis/SemanticStepExtractor.ts` around lines 93 - 109,
The advisor branch in SemanticStepExtractor (the if checking block.type ===
'server_tool_use' && block.name === 'advisor') creates a tool_call step but
omits the tokens field, causing structural inconsistency with the other tool_use
handling; update that step object to include a tokens property (e.g., tokens: {
input: 0, output: 0 } or tokens: undefined explicitly) so all tool_call steps
share the same shape, or alternatively add a brief inline comment in the advisor
branch explaining why tokens are intentionally omitted (e.g., "advisor calls
bypass normal token estimation")—modify the step creation inside the advisor
conditional to implement this change.
test/mocks/advisorBlocks.fixture.ts (1)

1-68: ⚡ Quick win

Outdated comment and potentially unnecessary type cast.

Line 17 states "Cast needed until M2 adds advisorModel to ParsedMessage," but according to the ParsedMessage type definition (context snippet from messages.ts:66-106), advisorModel is already an optional field. The comment appears outdated.

Additionally, the as unknown as ParsedMessage double-cast on lines 39 and 67 suggests a type mismatch that might be resolvable. If the cast is genuinely needed due to content block type definitions, consider updating the comment to explain the actual reason.

🤖 Prompt for 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.

In `@test/mocks/advisorBlocks.fixture.ts` around lines 1 - 68, The comment
claiming "Cast needed until M2 adds advisorModel to ParsedMessage" is outdated
and the double-cast "as unknown as ParsedMessage" is likely unnecessary; update
or remove that comment and fix typing for advisorCallMessage and
advisorResultMessage by either (a) typing the objects directly as ParsedMessage
(remove the double-cast) if ParsedMessage already has advisorModel?: string, or
(b) narrow the content block types so the messages satisfy ParsedMessage without
casting (or replace the double-cast with a single explicit cast only if truly
unavoidable). Refer to the exported symbols advisorCallMessage,
advisorResultMessage, and the ParsedMessage/advisorModel field when making the
change.
package.json (1)

49-49: ⚡ Quick win

Consider graceful shutdown instead of forced termination.

Using kill -9 (SIGKILL) immediately terminates the process without allowing cleanup. This prevents graceful shutdown of HTTP connections, file handles, or other resources.

Best practice is to try kill -15 (SIGTERM) first, wait briefly, then escalate to kill -9 if the process doesn't exit.

🛠️ Suggested graceful kill script
-"standalone:kill": "PIDS=$(lsof -ti :3456); [ -n \"$PIDS\" ] && kill -9 $PIDS || true",
+"standalone:kill": "PIDS=$(lsof -ti :3456); if [ -n \"$PIDS\" ]; then kill -15 $PIDS; sleep 2; kill -9 $PIDS 2>/dev/null || true; fi",

Note: This still requires addressing the platform and port discovery issues.

🤖 Prompt for 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.

In `@package.json` at line 49, The NPM script "standalone:kill" currently uses a
forced SIGKILL (kill -9) which prevents cleanup; change it to first send SIGTERM
(kill -15) to PIDS=$(lsof -ti :3456), wait a short interval (e.g., a sleep loop
or timeout) to allow graceful shutdown, then if processes still exist escalate
to SIGKILL; update the script that defines PIDS and the kill invocation in
package.json accordingly so it attempts graceful termination before
force-killing.
src/renderer/utils/toolLinkingEngine.ts (1)

84-86: 💤 Low value

Consider clarifying the comment rationale.

The comment mentions advisor input is empty and "the fallback estimates ~0", but if token estimation ran on "advisor" + "{}", it would estimate ~10 tokens, not ~0. The actual intent appears to be that advisor call tokens should not be estimated/charged at all (separate from the fact that input happens to be empty).

Consider rephrasing to: "Advisor call tokens are not estimated since advisor invocations are internal; only result tokens are counted."

🤖 Prompt for 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.

In `@src/renderer/utils/toolLinkingEngine.ts` around lines 84 - 86, The inline
comment for callTokens is misleading about why advisor tokens aren't estimated;
update the comment near the callTokens assignment (referencing callTokens,
toolName, estimateTokens and the 'advisor' branch) to state that advisor call
tokens are intentionally not estimated/charged because advisor invocations are
internal and only result tokens are counted (e.g., "Advisor call tokens are not
estimated since advisor invocations are internal; only result tokens are
counted.").
🤖 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 `@package.json`:
- Line 49: The standalone:kill npm script is Unix-only and hardcodes port 3456;
update it to be cross-platform and to detect the actual running server port
(respecting PORT env and the server's fallback range 3456–3466 used in
HttpServer.ts). Change the script to (a) prefer reading a PID or port file
created by the server process if available (look for any PID/port-writing logic
in the standalone server startup), (b) otherwise read the PORT env var and try
that port first then iterate the 3456–3466 range to find a listening process,
and (c) use platform-appropriate commands: lsof (Unix) or
netstat/PowerShell/Get-Process (Windows) to map port→PID and then
kill/Stop-Process the PID. Ensure the npm script (standalone:kill) exposes a
PORT override via environment variable and falls back to the range so Windows
users and servers started on non-3456 ports are handled.

In `@src/main/types/jsonl.ts`:
- Around line 76-87: The AdvisorToolResultContent type currently requires a
content property but downstream files (displayItemBuilder.ts and
SemanticStepExtractor.ts) use optional chaining on block.content?.text, so
update the AdvisorToolResultContent interface (symbol: AdvisorToolResultContent)
to make content optional (content?) so the type matches runtime usage; ensure
the nested shape remains { type: 'advisor_result'; text: string } unless you
also want to allow text to be absent — if downstream expects text possibly
empty, consider making text optional as well.

---

Nitpick comments:
In `@package.json`:
- Line 49: The NPM script "standalone:kill" currently uses a forced SIGKILL
(kill -9) which prevents cleanup; change it to first send SIGTERM (kill -15) to
PIDS=$(lsof -ti :3456), wait a short interval (e.g., a sleep loop or timeout) to
allow graceful shutdown, then if processes still exist escalate to SIGKILL;
update the script that defines PIDS and the kill invocation in package.json
accordingly so it attempts graceful termination before force-killing.

In `@src/main/services/analysis/SemanticStepExtractor.ts`:
- Around line 93-109: The advisor branch in SemanticStepExtractor (the if
checking block.type === 'server_tool_use' && block.name === 'advisor') creates a
tool_call step but omits the tokens field, causing structural inconsistency with
the other tool_use handling; update that step object to include a tokens
property (e.g., tokens: { input: 0, output: 0 } or tokens: undefined explicitly)
so all tool_call steps share the same shape, or alternatively add a brief inline
comment in the advisor branch explaining why tokens are intentionally omitted
(e.g., "advisor calls bypass normal token estimation")—modify the step creation
inside the advisor conditional to implement this change.

In `@src/renderer/utils/toolLinkingEngine.ts`:
- Around line 84-86: The inline comment for callTokens is misleading about why
advisor tokens aren't estimated; update the comment near the callTokens
assignment (referencing callTokens, toolName, estimateTokens and the 'advisor'
branch) to state that advisor call tokens are intentionally not
estimated/charged because advisor invocations are internal and only result
tokens are counted (e.g., "Advisor call tokens are not estimated since advisor
invocations are internal; only result tokens are counted.").

In `@test/mocks/advisorBlocks.fixture.ts`:
- Around line 1-68: The comment claiming "Cast needed until M2 adds advisorModel
to ParsedMessage" is outdated and the double-cast "as unknown as ParsedMessage"
is likely unnecessary; update or remove that comment and fix typing for
advisorCallMessage and advisorResultMessage by either (a) typing the objects
directly as ParsedMessage (remove the double-cast) if ParsedMessage already has
advisorModel?: string, or (b) narrow the content block types so the messages
satisfy ParsedMessage without casting (or replace the double-cast with a single
explicit cast only if truly unavoidable). Refer to the exported symbols
advisorCallMessage, advisorResultMessage, and the ParsedMessage/advisorModel
field when making the change.
🪄 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: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 0651aa59-14af-4be1-9517-1b4cb604fef3

📥 Commits

Reviewing files that changed from the base of the PR and between 16cc3c8 and dfc4339.

📒 Files selected for processing (20)
  • package.json
  • src/main/services/analysis/SemanticStepExtractor.ts
  • src/main/types/jsonl.ts
  • src/main/types/messages.ts
  • src/main/utils/jsonl.ts
  • src/renderer/components/chat/SessionContextPanel/components/RankedInjectionList.tsx
  • src/renderer/components/chat/SessionContextPanel/items/ToolBreakdownItem.tsx
  • src/renderer/components/chat/items/LinkedToolItem.tsx
  • src/renderer/components/chat/items/linkedTool/renderHelpers.tsx
  • src/renderer/utils/displayItemBuilder.ts
  • src/renderer/utils/stringUtils.ts
  • src/renderer/utils/toolLinkingEngine.ts
  • src/renderer/utils/toolRendering/toolSummaryHelpers.ts
  • test/main/services/analysis/SemanticStepExtractor.test.ts
  • test/mocks/advisorBlocks.fixture.ts
  • test/renderer/utils/displayItemBuilder.test.ts
  • test/renderer/utils/renderHelpers.test.ts
  • test/renderer/utils/stringUtils.test.ts
  • test/renderer/utils/toolLinkingEngine.test.ts
  • test/renderer/utils/toolRendering/toolTokens.test.ts

Comment thread package.json
"standalone:build": "electron-vite build && vite build --config vite.standalone.config.ts",
"standalone:start": "node dist-standalone/index.cjs"
"standalone:start": "node dist-standalone/index.cjs",
"standalone:kill": "PIDS=$(lsof -ti :3456); [ -n \"$PIDS\" ] && kill -9 $PIDS || true",

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Platform incompatibility and port discovery issue.

The standalone:kill script has two major issues:

  1. Unix-only: Uses lsof, which is not available on Windows. Windows users cannot use this script.
  2. Hardcoded port: Targets port 3456, but the standalone server can bind to ports 3456 through 3466 if the preferred port is busy (per HttpServer.ts:124-133). Additionally, if a user sets PORT=3457 in the environment, the server will start on 3457 but this script will target 3456.

Consider:

  • Detecting the actual port from a PID file or server output
  • Providing a Windows-compatible alternative using netstat or PowerShell
  • Making the port configurable via environment variable
🔍 Script to verify port binding behavior
#!/bin/bash
# Description: Verify that HttpServer can bind to ports beyond 3456

rg -n -A5 -B5 'for.*attempt.*tryPort.*preferredPort' src/main/services/infrastructure/HttpServer.ts
🤖 Prompt for 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.

In `@package.json` at line 49, The standalone:kill npm script is Unix-only and
hardcodes port 3456; update it to be cross-platform and to detect the actual
running server port (respecting PORT env and the server's fallback range
3456–3466 used in HttpServer.ts). Change the script to (a) prefer reading a PID
or port file created by the server process if available (look for any
PID/port-writing logic in the standalone server startup), (b) otherwise read the
PORT env var and try that port first then iterate the 3456–3466 range to find a
listening process, and (c) use platform-appropriate commands: lsof (Unix) or
netstat/PowerShell/Get-Process (Windows) to map port→PID and then
kill/Stop-Process the PID. Ensure the npm script (standalone:kill) exposes a
PORT override via environment variable and falls back to the range so Windows
users and servers started on non-3456 ports are handled.

Comment thread src/main/types/jsonl.ts
Comment on lines +76 to +87
export interface ServerToolUseContent extends BaseContent {
type: 'server_tool_use';
id: string;
name: string;
input?: Record<string, unknown>;
}

export interface AdvisorToolResultContent extends BaseContent {
type: 'advisor_tool_result';
tool_use_id: string;
content: { type: 'advisor_result'; text: string };
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Consider making AdvisorToolResultContent.content optional.

Downstream code in displayItemBuilder.ts and SemanticStepExtractor.ts uses optional chaining (block.content?.text ?? ''), suggesting content may be undefined at runtime. The type currently requires it, creating a gap between the type contract and defensive runtime guards.

🛡️ Suggested type refinement
 export interface AdvisorToolResultContent extends BaseContent {
   type: 'advisor_tool_result';
   tool_use_id: string;
-  content: { type: 'advisor_result'; text: string };
+  content?: { type: 'advisor_result'; text: string };
 }
🤖 Prompt for 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.

In `@src/main/types/jsonl.ts` around lines 76 - 87, The AdvisorToolResultContent
type currently requires a content property but downstream files
(displayItemBuilder.ts and SemanticStepExtractor.ts) use optional chaining on
block.content?.text, so update the AdvisorToolResultContent interface (symbol:
AdvisorToolResultContent) to make content optional (content?) so the type
matches runtime usage; ensure the nested shape remains { type: 'advisor_result';
text: string } unless you also want to allow text to be absent — if downstream
expects text possibly empty, consider making text optional as well.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feature request New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant