Skip to content

Conversation

@acoliver
Copy link
Collaborator

@acoliver acoliver commented Jan 8, 2026

Summary

This PR fixes multiple issues related to duplicate tool call execution:

  1. Provider stream deduplication - Fixes duplicate ToolCallRequest events with the same callId being emitted multiple times from the provider stream
  2. Confirmation handler deduplication - Fixes tools executing before user confirmation then executing again after confirmation in non-YOLO mode

Problems Addressed

Issue 1: Provider Stream Duplicates

CodeRabbit's analysis identified that the same ToolCallRequest event with the same callId could be emitted multiple times from the provider stream. When this happened, the CLI would schedule the same tool call multiple times, resulting in commands like:

  • mkdir test_dir - first execution succeeds
  • mkdir test_dir - second execution fails with 'File exists'

Issue 2: Multiple Confirmation Handlers (NEW)

In non-YOLO mode, handleConfirmationResponse could be called from multiple paths:

  1. IDE confirmation promise handler
  2. Wrapped onConfirm callback
  3. MessageBus subscription handler

This caused tools to execute multiple times when only one execution was expected.

Issue 3: Unknown correlationId Spam (NEW)

When multiple CoreToolScheduler instances exist (e.g., for subagents), all subscribe to the same MessageBus. When a confirmation response is published, schedulers that don't own that correlationId would log "unknown correlationId" messages repeatedly.

Solution

Part 1: Provider Stream Deduplication (commit 1)

Added deduplication logic in useGeminiStream.ts at the point where tool call requests are collected and scheduled. Before scheduling, the code filters out any duplicate callId entries.

Part 2: Confirmation Handler Deduplication (commit 2)

Added processedConfirmations Set to CoreToolScheduler to track which callIds have already been confirmed. The fix adds:

  • processedConfirmations Set field to track processed callIds
  • Early return guard in handleConfirmationResponse
  • Cleanup in dispose/clear methods
  • Comprehensive test coverage for duplication scenarios

Implementation Details

  • Provider deduplication: packages/cli/src/ui/hooks/useGeminiStream.ts in processGeminiStreamEvents
  • Confirmation deduplication: packages/core/src/core/coreToolScheduler.ts in handleConfirmationResponse

Test Plan

  1. All existing tests pass
  2. New deduplication tests pass in both useGeminiStream.dedup.test.tsx and coreToolScheduler.duplication.test.ts
  3. Typecheck passes
  4. Lint passes
  5. Build succeeds
  6. Interactive test with haiku generation succeeds

fixes #1040

Summary by CodeRabbit

  • Bug Fixes

    • Prevented duplicate tool executions by deduplicating repeated streamed tool-call requests and duplicate confirmation responses; scheduler state is now cleared on reset/dispose to avoid stale deduplication and ensure distinct calls in the same stream are handled.
  • Tests

    • Added tests validating streamed-request deduplication, batched distinct calls, duplicate confirmation handling, and single-execution guarantees across scheduler scenarios.

✏️ Tip: You can customize this high-level summary in your review settings.

Fixes a bug where tool calls (like mkdir, git init) could be executed
twice in the same turn, causing 'already exists' errors. This happened
because the provider stream could emit the same ToolCallRequest event
with the same callId multiple times.

The fix deduplicates tool call requests by callId before scheduling
them for execution, ensuring each unique callId is only processed once.

fixes #1040
@github-actions github-actions bot added the maintainer:e2e:ok Trusted contributor; maintainer-approved E2E run label Jan 8, 2026
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 8, 2026

Walkthrough

Deduplicates ToolCallRequest events by callId in the Gemini stream and prevents duplicate confirmation handling in CoreToolScheduler; adds tests validating stream-level deduplication and scheduler-level confirmation deduplication.

Changes

Cohort / File(s) Summary
Gemini stream hook
packages/cli/src/ui/hooks/useGeminiStream.ts
Add seenCallIds filtering to collapse duplicate ToolCallRequest events by callId before invoking scheduleToolCalls (both in-stream and at final scheduling).
CLI stream tests
packages/cli/src/ui/hooks/useGeminiStream.dedup.test.tsx
New tests that mock Gemini stream and scheduler to assert duplicate ToolCallRequest events with the same callId are scheduled once; also tests batched distinct callIds.
Vitest config
packages/cli/vitest.config.ts
Add the new dedup test file to Vitest include list.
Core scheduler logic
packages/core/src/core/coreToolScheduler.ts
Add processedConfirmations: Set<string> and seenCallIds: Set<string> to track processed confirmations and scheduled callIds; skip duplicate confirmation processing; clear state on dispose/reset/cancel.
Core scheduler tests
packages/core/src/core/coreToolScheduler.test.ts, packages/core/src/core/coreToolScheduler.duplication.test.ts
New/extended tests validating that duplicate confirmation responses do not trigger multiple executions and tools execute exactly once across various confirmation/correlation scenarios.

Sequence Diagram(s)

sequenceDiagram
  participant Gemini as Gemini Client (stream)
  participant UI as useGeminiStream hook
  participant Scheduler as CoreToolScheduler
  participant MB as MessageBus / Confirmations

  Gemini->>UI: stream ToolCallRequest events (may include duplicates)
  UI->>UI: deduplicate by seenCallIds
  UI->>Scheduler: scheduleToolCalls(deduped requests)
  Scheduler->>MB: publish scheduled calls / await confirmation
  MB->>Scheduler: send confirmation (correlationId / callId)
  Scheduler->>Scheduler: check processedConfirmations, process once
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

Possibly related PRs

Poem

🐰
I hopped through streams and clipped duplicate shoes,
One callId, one hop — no double-use blues.
Scheduler listens, confirmations align,
Tools run once, tidy and fine.

🚥 Pre-merge checks | ✅ 3 | ❌ 1
❌ Failed checks (1 inconclusive)
Check name Status Explanation Resolution
Linked Issues check ❓ Inconclusive The PR partially addresses issue #1040 by fixing duplicate-execution at the provider stream and confirmation handler levels, though comments indicate the root execution-before-approval timing issue remains. Verify that deduplication prevents double execution as described; clarify whether execution-before-approval timing is addressed or remains a separate issue requiring follow-up.
✅ Passed checks (3 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main change: deduplicating tool call requests to prevent double execution, which aligns with the core objectives of the changeset.
Description check ✅ Passed The PR description is comprehensive and well-structured, covering TLDR-style summary, problems addressed, detailed solutions, implementation details, and test plan aligned with the template.
Out of Scope Changes check ✅ Passed All changes (test files, deduplication logic, scheduler state tracking) are directly scoped to fixing duplicate tool execution as described in linked issue #1040.

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

✨ Finishing touches
  • 📝 Generate docstrings

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.

@github-actions
Copy link
Contributor

github-actions bot commented Jan 8, 2026

Issue #1040: Let me review this PR to understand the changes being made.

Summary of the PR

This PR is fixing multiple issues related to duplicate tool call execution in the LLxprt Code CLI:

  1. : Shell commands were being executed twice (e.g., mkdir reporting "File exists" errors)
  2. Provider stream deduplication: Prevents duplicate ToolCallRequest events with the same callId
  3. Confirmation handler deduplication: Prevents tools from executing before user confirmation and then executing again

Key Changes

1. Provider Stream Deduplication (useGeminiStream.ts)

  • Added deduplication logic to filter out duplicate tool call requests with the same callId before scheduling
  • Uses a seenCallIds Set to track processed call IDs

2. Confirmation Handler Deduplication (coreToolScheduler.ts)

  • Added processedConfirmations Set to track which callIds have already been confirmed
  • Early return guard in handleConfirmationResponse to prevent duplicate processing
  • Added seenCallIds Set at the scheduler boundary to prevent duplicate execution

3. UI Improvements (TodoPanel.tsx)

  • Major refactor to improve rendering and responsiveness
  • Better grouping of tool calls
  • Improved layout for different screen sizes

4. Tests

  • Added comprehensive tests for duplicate prevention in both the UI hook and core scheduler
  • Tests verify that tools execute only once even when duplicate events are emitted

Review Focus

Now I'll review this for:

  1. Alignment with the issue requirements
  2. Code quality and correctness
  3. Test coverage
  4. Potential side effects

LLxprt PR Review – PR #1044

Issue Alignment

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (1)
packages/cli/src/ui/hooks/useGeminiStream.ts (1)

1019-1034: Solid deduplication implementation.

The Set-based filtering correctly prevents duplicate tool call execution by keeping the first occurrence of each unique callId. This defensive measure is appropriate for handling provider-level duplicate emissions, similar to how duplicate tool responses are handled elsewhere in the codebase (based on learnings).

Optional: Add debug logging when duplicates are detected

For future troubleshooting, consider logging when duplicates are filtered:

 const seenCallIds = new Set<string>();
 const dedupedToolCallRequests = toolCallRequests.filter((request) => {
   if (seenCallIds.has(request.callId)) {
+    onDebugMessage(`Filtered duplicate tool call request: callId=${request.callId}, name=${request.name}`);
     return false;
   }
   seenCallIds.add(request.callId);
   return true;
 });

This would help identify if the underlying provider issue persists or worsens over time.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 07b2811 and 9fe8ccb.

📒 Files selected for processing (3)
  • packages/cli/src/ui/hooks/useGeminiStream.dedup.test.tsx
  • packages/cli/src/ui/hooks/useGeminiStream.ts
  • packages/cli/vitest.config.ts
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Don't use any - Always specify proper types. Use unknown if the type is truly unknown and add proper type guards.
Do not use console.log or console.debug - Use the sophisticated logging system instead. Log files are written to ~/.llxprt/debug/
Fix all linting errors, including warnings about any types

Files:

  • packages/cli/src/ui/hooks/useGeminiStream.ts
  • packages/cli/src/ui/hooks/useGeminiStream.dedup.test.tsx
  • packages/cli/vitest.config.ts
🧠 Learnings (3)
📓 Common learnings
Learnt from: e2720pjk
Repo: vybestack/llxprt-code PR: 583
File: packages/core/src/providers/openai/OpenAIProvider.ts:935-959
Timestamp: 2025-11-16T22:51:26.374Z
Learning: In the llxprt-code codebase (packages/core/src/providers/openai/OpenAIProvider.ts), tools like `run_shell_command` use internal streaming only for real-time UI updates during execution, but each tool execution produces exactly ONE final `ToolResponseBlock` containing the full result. The streaming chunks are never sent to the LLM and are not converted into multiple tool messages. The OpenAI Chat Completions API requires that each tool call (tool_call_id) corresponds to exactly one message with role 'tool', so duplicate tool response detection that removes subsequent tool messages with the same tool_call_id is correct and necessary for API compliance.
Learnt from: acoliver
Repo: vybestack/llxprt-code PR: 0
File: :0-0
Timestamp: 2025-12-26T14:17:41.983Z
Learning: In packages/core/src/core/coreToolScheduler.ts, the `publishBufferedResults()` method (line 1355) is called concurrently by multiple tool completion handlers (line 1510 in each tool's `.then()` callback) when parallel tools finish simultaneously. This causes a race condition where `nextPublishIndex` can be corrupted and tools can hang in 'executing' state forever, blocking the scheduler and causing subsequent tool calls to queue indefinitely. The method must be made reentrant-safe using a mutex/lock pattern to serialize concurrent calls.
📚 Learning: 2025-11-16T22:51:26.374Z
Learnt from: e2720pjk
Repo: vybestack/llxprt-code PR: 583
File: packages/core/src/providers/openai/OpenAIProvider.ts:935-959
Timestamp: 2025-11-16T22:51:26.374Z
Learning: In the llxprt-code codebase (packages/core/src/providers/openai/OpenAIProvider.ts), tools like `run_shell_command` use internal streaming only for real-time UI updates during execution, but each tool execution produces exactly ONE final `ToolResponseBlock` containing the full result. The streaming chunks are never sent to the LLM and are not converted into multiple tool messages. The OpenAI Chat Completions API requires that each tool call (tool_call_id) corresponds to exactly one message with role 'tool', so duplicate tool response detection that removes subsequent tool messages with the same tool_call_id is correct and necessary for API compliance.

Applied to files:

  • packages/cli/src/ui/hooks/useGeminiStream.ts
  • packages/cli/src/ui/hooks/useGeminiStream.dedup.test.tsx
📚 Learning: 2025-12-26T14:17:41.983Z
Learnt from: acoliver
Repo: vybestack/llxprt-code PR: 0
File: :0-0
Timestamp: 2025-12-26T14:17:41.983Z
Learning: In packages/core/src/core/coreToolScheduler.ts, the `publishBufferedResults()` method (line 1355) is called concurrently by multiple tool completion handlers (line 1510 in each tool's `.then()` callback) when parallel tools finish simultaneously. This causes a race condition where `nextPublishIndex` can be corrupted and tools can hang in 'executing' state forever, blocking the scheduler and causing subsequent tool calls to queue indefinitely. The method must be made reentrant-safe using a mutex/lock pattern to serialize concurrent calls.

Applied to files:

  • packages/cli/src/ui/hooks/useGeminiStream.ts
  • packages/cli/src/ui/hooks/useGeminiStream.dedup.test.tsx
🧬 Code graph analysis (2)
packages/cli/src/ui/hooks/useGeminiStream.ts (1)
packages/a2a-server/src/agent/task.ts (1)
  • scheduleToolCalls (539-585)
packages/cli/src/ui/hooks/useGeminiStream.dedup.test.tsx (1)
packages/core/src/core/turn.ts (1)
  • ToolCallRequestInfo (101-108)
⏰ Context from checks skipped due to timeout of 270000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Test (ubuntu-latest, 24.x)
  • GitHub Check: Test (macos-latest, 24.x)
  • GitHub Check: Test (windows-latest, 24.x)
  • GitHub Check: E2E Test (Linux) - sandbox:docker
  • GitHub Check: E2E Test (Linux) - sandbox:none
  • GitHub Check: Slow E2E - Win
🔇 Additional comments (2)
packages/cli/vitest.config.ts (1)

96-97: LGTM!

Clean addition of the deduplication test to the include list. The comment clearly references issue #1040 for future maintainability.

packages/cli/src/ui/hooks/useGeminiStream.dedup.test.tsx (1)

1-299: Excellent test coverage for the deduplication fix.

The test suite comprehensively validates the fix for issue #1040:

  1. Test 1 (lines 201-231): Confirms that when the provider stream emits the same tool call request twice (identical callId), only a single tool call is scheduled—preventing the double execution bug.

  2. Test 2 (lines 233-298): Ensures that distinct tool call requests (different callIds) are all scheduled correctly, confirming the deduplication doesn't over-filter legitimate concurrent tool calls.

The mock setup is thorough, properly isolates the deduplication logic, and uses React Testing Library best practices with act() and fake timers.

@github-actions
Copy link
Contributor

github-actions bot commented Jan 9, 2026

Code Coverage Summary

Package Lines Statements Functions Branches
CLI 44.93% 44.93% 54.37% 75.2%
Core 70.91% 70.91% 73.71% 78.74%
CLI Package - Full Text Report
-------------------|---------|----------|---------|---------|-------------------
File               | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-------------------|---------|----------|---------|---------|-------------------
All files          |   44.93 |     75.2 |   54.37 |   44.93 |                   
 src               |    30.8 |       43 |      50 |    30.8 |                   
  gemini.tsx       |   14.62 |    57.14 |   28.57 |   14.62 | ...,334-1125,1133 
  ...ractiveCli.ts |   54.84 |    41.81 |   33.33 |   54.84 | ...67-374,376-384 
  ...liCommands.ts |   97.22 |       60 |     100 |   97.22 | 39-40             
  ...ActiveAuth.ts |      36 |    35.71 |      80 |      36 | ...64-169,186-195 
 src/auth          |   50.88 |    64.79 |   66.89 |   50.88 |                   
  ...andlerImpl.ts |   90.72 |    84.61 |   71.42 |   90.72 | ...48-149,155-159 
  ...henticator.ts |     100 |    95.23 |   83.33 |     100 | 170               
  ...ketManager.ts |     100 |      100 |     100 |     100 |                   
  ...h-provider.ts |   58.98 |    59.57 |   66.66 |   58.98 | ...84-518,526-549 
  ...h-provider.ts |   40.74 |    85.71 |   69.23 |   40.74 | ...72-485,489-531 
  ...h-provider.ts |   17.74 |       90 |   27.77 |   17.74 | ...31-562,568-587 
  ...l-oauth-ui.ts |   54.16 |      100 |      40 |   54.16 | 26-32,38-39,57-61 
  ...h-callback.ts |   82.94 |    75.67 |    90.9 |   82.94 | ...74-775,788-790 
  migration.ts     |       0 |        0 |       0 |       0 | 1-69              
  oauth-manager.ts |      52 |    55.33 |      75 |      52 | ...1880,1889-1906 
  ...h-provider.ts |   36.15 |    31.81 |      40 |   36.15 | ...52-490,498-534 
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/commands      |   70.45 |      100 |      25 |   70.45 |                   
  extensions.tsx   |   55.55 |      100 |       0 |   55.55 | 21-31,35          
  mcp.ts           |   94.11 |      100 |      50 |   94.11 | 26                
 ...nds/extensions |   45.47 |    97.14 |   32.14 |   45.47 |                   
  disable.ts       |   17.54 |      100 |       0 |   17.54 | 17-30,36-63,65-69 
  enable.ts        |   16.12 |      100 |       0 |   16.12 | 17-36,42-68,70-74 
  install.ts       |   93.22 |    95.45 |   66.66 |   93.22 | 138,141-147       
  link.ts          |   26.31 |      100 |       0 |   26.31 | 20-37,44-49,51-54 
  list.ts          |   32.14 |      100 |       0 |   32.14 | 11-27,34-35       
  new.ts           |     100 |      100 |     100 |     100 |                   
  uninstall.ts     |   44.11 |      100 |   33.33 |   44.11 | 14-22,34-39,42-45 
  update.ts        |   10.94 |      100 |       0 |   10.94 | ...42-157,159-163 
 ...les/mcp-server |       0 |        0 |       0 |       0 |                   
  example.ts       |       0 |        0 |       0 |       0 | 1-60              
 src/commands/mcp  |   97.15 |    86.44 |    90.9 |   97.15 |                   
  add.ts           |     100 |    96.15 |     100 |     100 | 210               
  list.ts          |   90.65 |    80.76 |      80 |   90.65 | ...11-113,138-139 
  remove.ts        |     100 |    71.42 |     100 |     100 | 19-23             
 src/config        |   84.07 |    79.57 |   75.55 |   84.07 |                   
  auth.ts          |   90.69 |    89.47 |     100 |   90.69 | 19-20,57-58       
  ...alSettings.ts |   86.66 |    88.88 |     100 |   86.66 | 40-41,44-47       
  config.ts        |   78.27 |    81.72 |      76 |   78.27 | ...1761,1764-1768 
  extension.ts     |   80.06 |    89.26 |   78.78 |   80.06 | ...87-788,791-792 
  keyBindings.ts   |     100 |      100 |     100 |     100 |                   
  paths.ts         |     100 |      100 |     100 |     100 |                   
  ...eBootstrap.ts |      86 |     82.5 |      90 |      86 | ...51-753,762-763 
  sandboxConfig.ts |   66.66 |    47.77 |   89.47 |   66.66 | ...87-494,512-513 
  ...oxProfiles.ts |    8.53 |      100 |       0 |    8.53 | 47-48,51-129      
  settings.ts      |   86.59 |    75.73 |      72 |   86.59 | ...73-774,828-829 
  ...ingsSchema.ts |     100 |      100 |     100 |     100 |                   
  ...tedFolders.ts |   97.94 |    95.45 |     100 |   97.94 | 86,180-181        
  welcomeConfig.ts |   21.05 |      100 |       0 |   21.05 | ...70,73-78,81-82 
 ...fig/extensions |    63.6 |     87.5 |   83.78 |    63.6 |                   
  ...Enablement.ts |   95.37 |    95.52 |     100 |   95.37 | ...87-189,233-235 
  github.ts        |   44.11 |    86.79 |   54.54 |   44.11 | ...57-344,395-448 
  update.ts        |   62.33 |    46.15 |   66.66 |   62.33 | ...19-145,161-169 
  ...ableSchema.ts |     100 |      100 |     100 |     100 |                   
  variables.ts     |   95.34 |    89.47 |     100 |   95.34 | 30-31             
 src/constants     |     100 |      100 |     100 |     100 |                   
  historyLimits.ts |     100 |      100 |     100 |     100 |                   
 src/extensions    |   65.75 |    57.89 |      75 |   65.75 |                   
  ...utoUpdater.ts |   65.75 |    57.89 |      75 |   65.75 | ...49-450,459,461 
 src/generated     |     100 |      100 |     100 |     100 |                   
  git-commit.ts    |     100 |      100 |     100 |     100 |                   
 ...egration-tests |   90.72 |    84.61 |     100 |   90.72 |                   
  test-utils.ts    |   90.72 |    84.61 |     100 |   90.72 | ...01,219-220,230 
 src/patches       |       0 |        0 |       0 |       0 |                   
  is-in-ci.ts      |       0 |        0 |       0 |       0 | 1-17              
 src/providers     |   82.64 |    68.29 |   83.67 |   82.64 |                   
  IFileSystem.ts   |    86.2 |    85.71 |   85.71 |    86.2 | 51-52,67-68       
  ...Precedence.ts |   94.59 |    86.66 |     100 |   94.59 | 40-41             
  index.ts         |       0 |        0 |       0 |       0 | 1-19              
  ...gistration.ts |   77.94 |    68.75 |   33.33 |   77.94 | ...,93-97,103-104 
  ...derAliases.ts |   74.35 |    70.37 |     100 |   74.35 | ...27-133,138-139 
  ...onfigUtils.ts |   92.45 |       75 |     100 |   92.45 | 25-29             
  ...erInstance.ts |   83.64 |    64.75 |   88.46 |   83.64 | ...49-753,770-774 
  types.ts         |       0 |        0 |       0 |       0 | 1-8               
 ...viders/logging |   87.59 |    88.63 |   63.63 |   87.59 |                   
  ...rvice-impl.ts |   44.44 |        0 |       0 |   44.44 | 21-22,25-30,36-37 
  git-stats.ts     |   94.59 |    90.69 |     100 |   94.59 | ...48-149,180-181 
 src/runtime       |   67.09 |    72.04 |   70.24 |   67.09 |                   
  ...imeAdapter.ts |   97.03 |    89.65 |     100 |   97.03 | ...38,344-345,541 
  ...etFailover.ts |   98.93 |    93.54 |     100 |   98.93 | 205               
  messages.ts      |      20 |      100 |       0 |      20 | ...0,38-66,74-102 
  ...pplication.ts |   82.64 |    71.31 |      70 |   82.64 | ...59-662,673-674 
  ...extFactory.ts |   91.28 |    72.41 |     100 |   91.28 | ...63-266,351-358 
  ...meSettings.ts |   55.44 |    66.55 |   56.06 |   55.44 | ...2169,2194-2248 
 src/services      |   71.55 |    87.34 |   82.35 |   71.55 |                   
  ...mandLoader.ts |     100 |      100 |     100 |     100 |                   
  ...ardService.ts |    91.3 |    33.33 |     100 |    91.3 | 35-36             
  ...andService.ts |     100 |      100 |     100 |     100 |                   
  ...mandLoader.ts |   88.77 |    90.47 |     100 |   88.77 | ...79-184,258-265 
  ...omptLoader.ts |   30.68 |    81.25 |      50 |   30.68 | ...80-281,284-288 
  types.ts         |       0 |        0 |       0 |       0 | 1                 
 ...mpt-processors |   97.56 |    94.11 |     100 |   97.56 |                   
  ...tProcessor.ts |     100 |      100 |     100 |     100 |                   
  ...lProcessor.ts |   97.36 |    93.61 |     100 |   97.36 | 77-78,202-203     
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...o-continuation |   86.01 |     78.4 |   94.11 |   86.01 |                   
  ...ionService.ts |   86.01 |     78.4 |   94.11 |   86.01 | ...94,562,588-589 
 src/settings      |   61.97 |    77.87 |     100 |   61.97 |                   
  ...alSettings.ts |   61.53 |    79.24 |     100 |   61.53 | ...26-437,440-446 
  ...aramParser.ts |   71.42 |    57.14 |     100 |   71.42 | 21-22,24-25,30-31 
 src/test-utils    |   40.68 |    78.57 |   42.85 |   40.68 |                   
  ...eExtension.ts |     100 |      100 |     100 |     100 |                   
  ...omMatchers.ts |   21.21 |      100 |       0 |   21.21 | 22-50             
  ...andContext.ts |     100 |      100 |     100 |     100 |                   
  render.tsx       |       0 |        0 |       0 |       0 | 1-149             
  ...e-testing.tsx |       0 |        0 |       0 |       0 | 1-54              
  ...iderConfig.ts |       0 |        0 |       0 |       0 | 1-19              
 src/ui            |   16.11 |    98.36 |   33.33 |   16.11 |                   
  App.tsx          |   33.89 |      100 |       0 |   33.89 | 50-86,92-99       
  AppContainer.tsx |    5.35 |      100 |       0 |    5.35 | 146-155,184-2357  
  ...tionNudge.tsx |       8 |      100 |       0 |       8 | 27-102            
  colors.ts        |   39.92 |      100 |   26.78 |   39.92 | ...86-287,291-292 
  constants.ts     |     100 |      100 |     100 |     100 |                   
  ...derOptions.ts |     100 |      100 |     100 |     100 |                   
  keyMatchers.ts   |   95.65 |    96.29 |     100 |   95.65 | 29-30             
  ...ntsEnabled.ts |     100 |      100 |     100 |     100 |                   
  ...submission.ts |     100 |      100 |     100 |     100 |                   
  ...tic-colors.ts |     100 |      100 |     100 |     100 |                   
  textConstants.ts |     100 |      100 |     100 |     100 |                   
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/ui/commands   |    65.7 |    75.32 |   63.58 |    65.7 |                   
  aboutCommand.ts  |   74.81 |       24 |     100 |   74.81 | ...05,112-113,140 
  authCommand.ts   |   74.95 |     84.4 |   83.33 |   74.95 | ...39-642,652-676 
  ...urlCommand.ts |      30 |      100 |       0 |      30 | 20-40             
  bugCommand.ts    |   79.16 |     37.5 |     100 |   79.16 | 32-35,42,79-88    
  chatCommand.ts   |   63.38 |    77.27 |      50 |   63.38 | ...87-509,526-536 
  clearCommand.ts  |     100 |      100 |     100 |     100 |                   
  ...essCommand.ts |     100 |    88.88 |     100 |     100 | 71                
  copyCommand.ts   |   98.27 |    94.44 |     100 |   98.27 | 37                
  debugCommands.ts |   13.29 |      100 |       0 |   13.29 | ...48,455,462,469 
  ...icsCommand.ts |    62.5 |    57.14 |   33.33 |    62.5 | ...88,320,427-432 
  ...ryCommand.tsx |   16.86 |      100 |       0 |   16.86 | ...38-148,155-179 
  docsCommand.ts   |     100 |      100 |     100 |     100 |                   
  ...extCommand.ts |   93.18 |    77.77 |     100 |   93.18 | 108-113           
  editorCommand.ts |     100 |      100 |     100 |     100 |                   
  ...onsCommand.ts |   91.86 |    88.88 |     100 |   91.86 | 86-94,96          
  helpCommand.ts   |     100 |      100 |     100 |     100 |                   
  ideCommand.ts    |   66.35 |    68.96 |   55.55 |   66.35 | ...22-225,233-240 
  initCommand.ts   |   83.33 |    71.42 |   66.66 |   83.33 | 35-39,41-85       
  keyCommand.ts    |     100 |    77.77 |     100 |     100 | 47                
  ...ileCommand.ts |   11.11 |      100 |       0 |   11.11 | 23-134            
  ...ingCommand.ts |   10.96 |      100 |       0 |   10.96 | ...59-528,545-556 
  logoutCommand.ts |   15.62 |      100 |       0 |   15.62 | 21-85             
  mcpCommand.ts    |   82.35 |    82.22 |   83.33 |   82.35 | ...09-410,428-429 
  memoryCommand.ts |   88.82 |    83.87 |     100 |   88.82 | 69-83,96-101,152  
  modelCommand.ts  |   25.71 |      100 |       0 |   25.71 | 21-51             
  mouseCommand.ts  |     100 |      100 |     100 |     100 |                   
  ...onsCommand.ts |     100 |      100 |     100 |     100 |                   
  ...iesCommand.ts |   97.02 |    82.85 |     100 |   97.02 | 27,40-41          
  ...acyCommand.ts |   61.53 |      100 |       0 |   61.53 | 22-26             
  ...ileCommand.ts |   64.89 |    72.03 |   81.81 |   64.89 | ...24-929,949-963 
  ...derCommand.ts |   53.12 |    30.55 |      80 |   53.12 | ...58-262,270-275 
  quitCommand.ts   |   34.48 |      100 |       0 |   34.48 | 16-35             
  ...oreCommand.ts |   92.53 |     87.5 |     100 |   92.53 | ...,90-91,120-125 
  setCommand.ts    |   81.68 |    77.27 |      80 |   81.68 | ...77-782,824-837 
  ...ngsCommand.ts |     100 |      100 |     100 |     100 |                   
  ...hubCommand.ts |     100 |      100 |     100 |     100 |                   
  statsCommand.ts  |   94.33 |     90.9 |     100 |   94.33 | 26-34             
  statusCommand.ts |   13.63 |      100 |       0 |   13.63 | 20-87             
  ...entCommand.ts |   79.57 |    72.04 |   83.33 |   79.57 | ...67-880,883-896 
  ...tupCommand.ts |     100 |      100 |     100 |     100 |                   
  themeCommand.ts  |     100 |      100 |     100 |     100 |                   
  ...matCommand.ts |   26.66 |      100 |       0 |   26.66 | 33-92             
  toolsCommand.ts  |   84.98 |     74.6 |     100 |   84.98 | ...85-294,307-308 
  types.ts         |     100 |      100 |     100 |     100 |                   
  ...ileCommand.ts |   61.11 |      100 |       0 |   61.11 | 16-22             
  vimCommand.ts    |   44.44 |      100 |       0 |   44.44 | 14-24             
 ...ommands/schema |   96.22 |    91.02 |    92.3 |   96.22 |                   
  index.ts         |   96.45 |    91.61 |     100 |   96.45 | ...08-412,423-424 
  types.ts         |       0 |        0 |       0 |       0 | 1                 
 src/ui/components |   11.19 |     35.1 |    2.56 |   11.19 |                   
  AboutBox.tsx     |    4.06 |      100 |       0 |    4.06 | 27-160            
  AsciiArt.ts      |     100 |      100 |     100 |     100 |                   
  AuthDialog.tsx   |    6.33 |      100 |       0 |    6.33 | 27-193            
  ...nProgress.tsx |   16.66 |      100 |       0 |   16.66 | 18-62             
  ...Indicator.tsx |   15.15 |      100 |       0 |   15.15 | 17-47             
  ...firmation.tsx |    7.31 |      100 |       0 |    7.31 | 45-179            
  ...tsDisplay.tsx |    7.69 |      100 |       0 |    7.69 | 23-34,38-156      
  CliSpinner.tsx   |   38.46 |      100 |       0 |   38.46 | 16-24             
  Composer.tsx     |     9.8 |      100 |       0 |     9.8 | 24-73             
  ...entPrompt.tsx |   18.75 |      100 |       0 |   18.75 | 21-51             
  ...ryDisplay.tsx |   21.05 |      100 |       0 |   21.05 | 17-35             
  ...ryDisplay.tsx |    4.93 |      100 |       0 |    4.93 | 26-112            
  ...geDisplay.tsx |       0 |        0 |       0 |       0 | 1-37              
  ...gProfiler.tsx |   17.88 |      100 |       0 |   17.88 | ...71-116,120-199 
  ...esDisplay.tsx |   10.52 |      100 |       0 |   10.52 | 24-82             
  ...ogManager.tsx |   11.63 |      100 |       0 |   11.63 | 49-326            
  ...ngsDialog.tsx |    6.53 |      100 |       0 |    6.53 | 27-189            
  ...rBoundary.tsx |   10.16 |        0 |       0 |   10.16 | ...16-161,179-191 
  ...ustDialog.tsx |   15.73 |      100 |       0 |   15.73 | 31-123            
  Footer.tsx       |    8.94 |      100 |     100 |    8.94 | ...30-508,512-525 
  ...ngSpinner.tsx |    40.9 |      100 |       0 |    40.9 | 31-47             
  Header.tsx       |    17.5 |      100 |       0 |    17.5 | 22-62             
  Help.tsx         |    3.17 |      100 |       0 |    3.17 | 17-179            
  ...emDisplay.tsx |   18.32 |      100 |       0 |   18.32 | 50-180            
  InputPrompt.tsx  |   38.85 |     37.2 |   66.66 |   38.85 | ...7-915,929-1078 
  ...tsDisplay.tsx |    4.41 |      100 |       0 |    4.41 | 26-37,41-249      
  ...utManager.tsx |       0 |        0 |       0 |       0 | 1-97              
  ...ileDialog.tsx |    6.89 |      100 |       0 |    6.89 | 20-119            
  ...Indicator.tsx |   14.54 |      100 |       0 |   14.54 | 24-81             
  ...ingDialog.tsx |    4.52 |      100 |       0 |    4.52 | ...9,84-90,93-354 
  ...geDisplay.tsx |       0 |        0 |       0 |       0 | 1-40              
  ...tsDisplay.tsx |    6.28 |      100 |       0 |    6.28 | 33-52,56-214      
  ...fications.tsx |    14.1 |      100 |       0 |    14.1 | 28-105            
  ...odeDialog.tsx |    7.31 |      100 |       0 |    7.31 | 30-140            
  ...ustDialog.tsx |    6.21 |      100 |       0 |    6.21 | 30-237            
  PrepareLabel.tsx |   13.33 |      100 |       0 |   13.33 | 20-48             
  ...derDialog.tsx |    3.84 |      100 |       0 |    3.84 | 22-272            
  ...delDialog.tsx |    2.99 |      100 |       0 |    2.99 | 23-361            
  ...Indicator.tsx |       0 |        0 |       0 |       0 | 1-21              
  ...eKeyInput.tsx |       0 |        0 |       0 |       0 | 1-138             
  ...ryDisplay.tsx |      50 |      100 |       0 |      50 | 15-17             
  ...ngsDialog.tsx |    1.87 |      100 |       0 |    1.87 | ...9-108,111-1262 
  ...ionDialog.tsx |   13.95 |      100 |       0 |   13.95 | 35-125            
  ...Indicator.tsx |   44.44 |      100 |       0 |   44.44 | 12-17             
  ...MoreLines.tsx |      28 |      100 |       0 |      28 | 18-40             
  StatsDisplay.tsx |    6.82 |      100 |       0 |    6.82 | ...85-160,168-339 
  ...nsDisplay.tsx |    7.76 |      100 |       0 |    7.76 | 49-164            
  ThemeDialog.tsx  |    4.85 |      100 |       0 |    4.85 | 34-338            
  Tips.tsx         |      16 |      100 |       0 |      16 | 17-45             
  TodoPanel.tsx    |    5.55 |      100 |       0 |    5.55 | 26-74,77-245      
  ...tsDisplay.tsx |    7.42 |      100 |       0 |    7.42 | 30-53,56-228      
  ToolsDialog.tsx  |    7.86 |      100 |       0 |    7.86 | 23-119            
  ...ification.tsx |   36.36 |      100 |       0 |   36.36 | 15-22             
  ...ionDialog.tsx |    7.52 |      100 |       0 |    7.52 | 18-122            
  todo-utils.ts    |       0 |        0 |       0 |       0 | 1-7               
 ...leCreateWizard |   19.52 |       50 |       0 |   19.52 |                   
  ...aramsStep.tsx |    5.82 |      100 |       0 |    5.82 | 27-244            
  ...ationStep.tsx |    4.82 |      100 |       0 |    4.82 | 27-294            
  ...onfigStep.tsx |   11.23 |      100 |       0 |   11.23 | 25-119            
  ...electStep.tsx |    6.84 |      100 |       0 |    6.84 | 26-190            
  ...ationMenu.tsx |       0 |        0 |       0 |       0 | 1-101             
  ...eSaveStep.tsx |    6.28 |      100 |       0 |    6.28 | 33-255            
  ...ssSummary.tsx |   12.12 |      100 |       0 |   12.12 | 22-87             
  ...electStep.tsx |   16.92 |      100 |       0 |   16.92 | 27-94             
  TextInput.tsx    |     5.6 |      100 |       0 |     5.6 | 27-168            
  constants.ts     |     100 |      100 |     100 |     100 |                   
  index.tsx        |    6.27 |      100 |       0 |    6.27 | 28-296            
  types.ts         |     100 |      100 |     100 |     100 |                   
  utils.ts         |    5.22 |      100 |       0 |    5.22 | ...46-350,355-372 
  validation.ts    |   11.23 |      100 |       0 |   11.23 | ...97-104,107-111 
 ...comeOnboarding |   10.51 |        0 |       0 |   10.51 |                   
  ...ethodStep.tsx |   16.86 |      100 |       0 |   16.86 | 37-122            
  ...ationStep.tsx |    5.42 |      100 |       0 |    5.42 | 28-180            
  ...etionStep.tsx |    5.08 |      100 |       0 |    5.08 | 22-164            
  ...electStep.tsx |    7.95 |      100 |       0 |    7.95 | 30-131            
  ...electStep.tsx |   23.07 |      100 |       0 |   23.07 | 33-92             
  SkipExitStep.tsx |    12.5 |      100 |       0 |    12.5 | 18-59             
  ...omeDialog.tsx |   12.38 |      100 |       0 |   12.38 | 37-146            
  WelcomeStep.tsx  |    10.2 |      100 |       0 |    10.2 | 23-74             
  index.ts         |       0 |        0 |       0 |       0 | 1-13              
 ...nents/messages |   13.25 |    85.71 |    7.14 |   13.25 |                   
  ...onMessage.tsx |   13.72 |      100 |       0 |   13.72 | 24-80             
  DiffRenderer.tsx |    3.33 |      100 |       0 |    3.33 | ...79-360,363-381 
  ErrorMessage.tsx |   22.22 |      100 |       0 |   22.22 | 16-31             
  ...niMessage.tsx |   16.07 |      100 |       0 |   16.07 | 27-83             
  ...geContent.tsx |   20.83 |      100 |       0 |   20.83 | 26-46             
  InfoMessage.tsx  |   26.31 |      100 |       0 |   26.31 | 17-32             
  ...rlMessage.tsx |     100 |       50 |     100 |     100 | 26                
  ...ckDisplay.tsx |      20 |      100 |       0 |      20 | 43-64             
  ...onMessage.tsx |    3.87 |      100 |       0 |    3.87 | 43-507            
  ...upMessage.tsx |   10.36 |      100 |       0 |   10.36 | ...2,65-80,84-254 
  ToolMessage.tsx  |    8.62 |      100 |       0 |    8.62 | ...98-332,335-338 
  UserMessage.tsx  |     100 |      100 |     100 |     100 |                   
  ...llMessage.tsx |   36.36 |      100 |       0 |   36.36 | 17-25             
  ...ngMessage.tsx |   26.31 |      100 |       0 |   26.31 | 17-32             
 ...ponents/shared |   17.12 |     18.6 |   15.38 |   17.12 |                   
  ...ctionList.tsx |    5.55 |      100 |       0 |    5.55 | 53-184            
  MaxSizedBox.tsx  |    2.37 |      100 |       0 |    2.37 | 23-50,99-625      
  ...tonSelect.tsx |   13.63 |      100 |       0 |   13.63 | 57-100            
  ...lableList.tsx |    7.61 |      100 |       0 |    7.61 | 44-152            
  ...lizedList.tsx |    2.04 |      100 |       0 |    2.04 | 55-485            
  text-buffer.ts   |   33.26 |     18.6 |   22.22 |   33.26 | ...1903-1906,1911 
  ...er-actions.ts |    0.78 |      100 |       0 |    0.78 | 26-39,78-814      
 ...mponents/views |    14.7 |      100 |       0 |    14.7 |                   
  ChatList.tsx     |    14.7 |      100 |       0 |    14.7 | 18-51             
 src/ui/constants  |   89.28 |      100 |       0 |   89.28 |                   
  ...ollections.ts |   89.28 |      100 |       0 |   89.28 | 224-245           
 src/ui/containers |       0 |        0 |       0 |       0 |                   
  ...ontroller.tsx |       0 |        0 |       0 |       0 | 1-341             
  UIStateShell.tsx |       0 |        0 |       0 |       0 | 1-15              
 src/ui/contexts   |   59.93 |    74.39 |      50 |   59.93 |                   
  ...chContext.tsx |    64.7 |      100 |      50 |    64.7 | 24-29             
  FocusContext.tsx |       0 |        0 |       0 |       0 | 1-11              
  ...ssContext.tsx |   83.02 |    79.02 |      92 |   83.02 | ...1231,1263-1266 
  MouseContext.tsx |   78.88 |    66.66 |     100 |   78.88 | ...98-102,108-118 
  ...erContext.tsx |       0 |        0 |       0 |       0 | 1-120             
  ...owContext.tsx |   19.64 |      100 |       0 |   19.64 | 33,36,39-87       
  ...meContext.tsx |   46.51 |       25 |   28.57 |   46.51 | ...89,193-194,199 
  ...lProvider.tsx |   89.16 |    69.81 |     100 |   89.16 | ...79-380,387-388 
  ...onContext.tsx |    6.73 |      100 |       0 |    6.73 | ...88-282,287-294 
  ...teContext.tsx |       0 |        0 |       0 |       0 | 1-61              
  ...gsContext.tsx |      50 |      100 |       0 |      50 | 15-20             
  ...ngContext.tsx |   42.85 |      100 |       0 |   42.85 | 15-22             
  TodoContext.tsx  |   55.55 |      100 |       0 |   55.55 | 19-22,24-27       
  TodoProvider.tsx |    6.94 |      100 |       0 |    6.94 | 24-105            
  ...llContext.tsx |     100 |      100 |       0 |     100 |                   
  ...lProvider.tsx |    6.75 |      100 |       0 |    6.75 | 28-122            
  ...nsContext.tsx |      25 |      100 |       0 |      25 | 164-175,178-183   
  ...teContext.tsx |   27.77 |      100 |       0 |   27.77 | 197-206,209-214   
  ...deContext.tsx |   11.11 |      100 |       0 |   11.11 | 29-81,84-89       
 src/ui/editors    |   94.11 |    85.71 |   66.66 |   94.11 |                   
  ...ngsManager.ts |   94.11 |    85.71 |   66.66 |   94.11 | 55,69-70          
 src/ui/hooks      |   19.38 |    44.97 |   16.83 |   19.38 |                   
  ...dProcessor.ts |    2.53 |        0 |       0 |    2.53 | 31,60-515         
  index.ts         |       0 |        0 |       0 |       0 | 1-10              
  ...dProcessor.ts |    13.2 |      100 |      50 |    13.2 | 33-61,79-308      
  ...dProcessor.ts |    2.66 |      100 |       0 |    2.66 | 74-718            
  ...Completion.ts |   22.22 |      100 |      50 |   22.22 | ...34-157,162-242 
  ...uthCommand.ts |    6.45 |      100 |       0 |    6.45 | 15-135            
  ...tIndicator.ts |    12.5 |      100 |       0 |    12.5 | 19-66             
  ...chedScroll.ts |   16.66 |      100 |       0 |   16.66 | 14-32             
  ...ketedPaste.ts |      20 |      100 |       0 |      20 | 20-38             
  ...ompletion.tsx |   52.34 |    27.27 |     100 |   52.34 | ...08-251,264-280 
  useCompletion.ts |   45.56 |      100 |     100 |   45.56 | ...8,52-77,81-107 
  ...leMessages.ts |       5 |      100 |       0 |       5 | 29-65,68-118      
  ...fileDialog.ts |   16.12 |      100 |       0 |   16.12 | 17-47             
  ...orSettings.ts |   11.11 |      100 |       0 |   11.11 | 29-81             
  ...AutoUpdate.ts |    9.52 |      100 |       0 |    9.52 | 18-58             
  ...ionUpdates.ts |    4.36 |      100 |       0 |    4.36 | 36-48,51-244      
  ...erDetector.ts |   22.72 |      100 |       0 |   22.72 | 27-43             
  useFocus.ts      |    25.8 |      100 |       0 |    25.8 | 19-48             
  ...olderTrust.ts |   10.46 |      100 |       0 |   10.46 | 24-112            
  ...miniStream.ts |   51.03 |    48.87 |      40 |   51.03 | ...1387,1412-1514 
  ...BranchName.ts |   11.66 |      100 |       0 |   11.66 | 14-79             
  ...oryManager.ts |    5.22 |      100 |       0 |    5.22 | 53-212            
  ...stListener.ts |   12.12 |      100 |       0 |   12.12 | 17-50             
  ...putHistory.ts |    3.75 |      100 |       0 |    3.75 | 24-111            
  ...storyStore.ts |    4.61 |      100 |       0 |    4.61 | 24-112            
  useKeypress.ts   |   21.05 |      100 |       0 |   21.05 | 24-41             
  ...rdProtocol.ts |   36.36 |      100 |       0 |   36.36 | 24-31             
  ...fileDialog.ts |    5.71 |      100 |       0 |    5.71 | 27-135            
  ...gIndicator.ts |   12.24 |      100 |       0 |   12.24 | 14-64             
  useLogger.ts     |   93.75 |      100 |     100 |   93.75 | 26                
  ...oryMonitor.ts |      25 |      100 |       0 |      25 | 19-41             
  useMouse.ts      |   77.77 |    66.66 |     100 |   77.77 | 22-25             
  ...eSelection.ts |    3.13 |      100 |       0 |    3.13 | 36-103,106-315    
  ...oviderInfo.ts |       0 |        0 |       0 |       0 | 1-80              
  ...odifyTrust.ts |    9.09 |      100 |       0 |    9.09 | 46-137            
  ...raseCycler.ts |    9.43 |      100 |       0 |    9.43 | 24-80             
  ...cySettings.ts |    3.38 |      100 |       0 |    3.38 | 17-26,36-156      
  ...Completion.ts |   29.41 |       40 |     100 |   29.41 | ...14-227,236-242 
  ...iderDialog.ts |    7.89 |      100 |       0 |    7.89 | 27-110            
  ...odelDialog.ts |    9.37 |      100 |       0 |    9.37 | 25-86             
  ...lScheduler.ts |   21.22 |    29.41 |   66.66 |   21.22 | ...74-479,481-491 
  ...oryCommand.ts |       0 |        0 |       0 |       0 | 1-7               
  useResponsive.ts |   28.57 |      100 |       0 |   28.57 | 23-33             
  ...ompletion.tsx |   69.56 |      100 |     100 |   69.56 | 45-47,51-66,78-81 
  ...ectionList.ts |    2.94 |      100 |       0 |    2.94 | ...01-229,243-448 
  useSession.ts    |       0 |        0 |       0 |       0 | 1-23              
  ...ngsCommand.ts |   18.75 |      100 |       0 |   18.75 | 10-25             
  ...ellHistory.ts |    6.48 |      100 |       0 |    6.48 | 22-70,73-138      
  ...oryCommand.ts |       0 |        0 |       0 |       0 | 1-62              
  ...ompletion.tsx |   32.05 |    31.57 |      25 |   32.05 | ...89-797,822-860 
  ...leCallback.ts |       0 |        0 |       0 |       0 | 1-70              
  ...tateAndRef.ts |   59.09 |      100 |     100 |   59.09 | 23-31             
  ...oryRefresh.ts |   10.34 |      100 |       0 |   10.34 | 17-48             
  ...rminalSize.ts |   11.42 |      100 |       0 |   11.42 | 13-55             
  ...emeCommand.ts |    6.03 |      100 |       0 |    6.03 | 26-151            
  useTimer.ts      |    7.14 |      100 |       0 |    7.14 | 16-65             
  ...ntinuation.ts |       0 |        0 |       0 |       0 | 1-270             
  ...ePreserver.ts |   48.48 |      100 |      75 |   48.48 | 33-50             
  ...oolsDialog.ts |    4.67 |      100 |       0 |    4.67 | 24-145            
  ...Onboarding.ts |    2.96 |      100 |       0 |    2.96 | 76-348            
  ...eMigration.ts |   10.34 |      100 |       0 |   10.34 | 14-72             
  vim.ts           |    6.13 |      100 |       0 |    6.13 | ...76-111,129-784 
 src/ui/layouts    |    5.39 |      100 |       0 |    5.39 |                   
  ...AppLayout.tsx |    5.39 |      100 |       0 |    5.39 | 57-74,77-636      
 ...noninteractive |      75 |      100 |    6.66 |      75 |                   
  ...eractiveUi.ts |      75 |      100 |    6.66 |      75 | 17-19,23-24,27-28 
 src/ui/privacy    |   25.78 |      100 |       0 |   25.78 |                   
  ...acyNotice.tsx |   10.97 |      100 |       0 |   10.97 | 22-123            
  ...acyNotice.tsx |   14.28 |      100 |       0 |   14.28 | 16-59             
  ...acyNotice.tsx |   12.19 |      100 |       0 |   12.19 | 16-62             
  ...acyNotice.tsx |   41.33 |      100 |       0 |   41.33 | 78-91,99-193      
  ...acyNotice.tsx |   21.95 |      100 |       0 |   21.95 | 20-59,62-64       
 src/ui/reducers   |   78.07 |     90.9 |      50 |   78.07 |                   
  appReducer.ts    |     100 |      100 |     100 |     100 |                   
  ...ionReducer.ts |       0 |        0 |       0 |       0 | 1-52              
 src/ui/state      |   21.51 |      100 |       0 |   21.51 |                   
  extensions.ts    |   21.51 |      100 |       0 |   21.51 | 68-130            
 src/ui/themes     |   99.13 |    89.38 |      96 |   99.13 |                   
  ansi-light.ts    |     100 |      100 |     100 |     100 |                   
  ansi.ts          |     100 |      100 |     100 |     100 |                   
  atom-one-dark.ts |     100 |      100 |     100 |     100 |                   
  ayu-light.ts     |     100 |      100 |     100 |     100 |                   
  ayu.ts           |     100 |      100 |     100 |     100 |                   
  color-utils.ts   |     100 |      100 |     100 |     100 |                   
  default-light.ts |     100 |      100 |     100 |     100 |                   
  default.ts       |     100 |      100 |     100 |     100 |                   
  dracula.ts       |     100 |      100 |     100 |     100 |                   
  github-dark.ts   |     100 |      100 |     100 |     100 |                   
  github-light.ts  |     100 |      100 |     100 |     100 |                   
  googlecode.ts    |     100 |      100 |     100 |     100 |                   
  green-screen.ts  |     100 |      100 |     100 |     100 |                   
  no-color.ts      |     100 |      100 |     100 |     100 |                   
  ...c-resolver.ts |     100 |      100 |     100 |     100 |                   
  ...tic-tokens.ts |     100 |      100 |     100 |     100 |                   
  ...-of-purple.ts |     100 |      100 |     100 |     100 |                   
  theme-compat.ts  |     100 |       50 |     100 |     100 | 79                
  theme-manager.ts |   89.74 |    82.53 |     100 |   89.74 | ...04-310,315-316 
  theme.ts         |   99.41 |      100 |   85.71 |   99.41 | 185-186           
  xcode.ts         |     100 |      100 |     100 |     100 |                   
 src/ui/utils      |   43.49 |    86.63 |   63.41 |   43.49 |                   
  ...Colorizer.tsx |    5.76 |      100 |       0 |    5.76 | ...16-128,140-232 
  ...olePatcher.ts |      78 |    77.77 |     100 |      78 | 58-69             
  ...nRenderer.tsx |    9.39 |      100 |       0 |    9.39 | 26-165,174-183    
  ...wnDisplay.tsx |    5.73 |      100 |       0 |    5.73 | ...96-420,431-435 
  ...eRenderer.tsx |   10.63 |      100 |       0 |   10.63 | ...32-247,260-395 
  ...ketedPaste.ts |   55.55 |      100 |       0 |   55.55 | 11-12,15-16       
  clipboard.ts     |   97.29 |    84.61 |     100 |   97.29 | 40                
  ...boardUtils.ts |   32.25 |     37.5 |     100 |   32.25 | ...55-114,129-145 
  commandUtils.ts  |   93.44 |    89.79 |     100 |   93.44 | ...31,135,137-138 
  computeStats.ts  |     100 |      100 |     100 |     100 |                   
  displayUtils.ts  |     100 |      100 |     100 |     100 |                   
  formatters.ts    |   90.47 |       95 |     100 |   90.47 | 57-60             
  fuzzyFilter.ts   |     100 |    96.42 |     100 |     100 | 75                
  highlight.ts     |   65.43 |      100 |   66.66 |   65.43 | 77-110            
  input.ts         |     100 |    85.71 |     100 |     100 | 18                
  ...olDetector.ts |    9.09 |       50 |   16.66 |    9.09 | ...51-152,155-156 
  ...nUtilities.ts |   69.84 |    85.71 |     100 |   69.84 | 75-91,100-101     
  mouse.ts         |   85.16 |    73.68 |     100 |   85.16 | ...18-219,223-224 
  ...mConstants.ts |     100 |      100 |     100 |     100 |                   
  ...opDetector.ts |       0 |        0 |       0 |       0 | 1-209             
  responsive.ts    |    69.9 |    73.33 |      80 |    69.9 | ...95-103,106-121 
  ...putHandler.ts |   87.36 |    90.32 |     100 |   87.36 | 52-53,74-83       
  ...alContract.ts |     100 |      100 |     100 |     100 |                   
  terminalLinks.ts |     100 |      100 |     100 |     100 |                   
  ...lSequences.ts |     100 |      100 |     100 |     100 |                   
  terminalSetup.ts |    4.03 |      100 |       0 |    4.03 | 40-340            
  textUtils.ts     |   74.77 |    94.59 |   72.72 |   74.77 | ...14-115,135-137 
  ...Formatters.ts |   17.39 |      100 |       0 |   17.39 | 14-21,29-36,50-52 
  ...icsTracker.ts |     100 |    66.66 |     100 |     100 | 32-34             
  ui-sizing.ts     |   21.05 |      100 |       0 |   21.05 | 11-23,26-31       
  updateCheck.ts   |     100 |    93.75 |     100 |     100 | 28,39             
 src/utils         |   54.66 |    89.45 |   86.84 |   54.66 |                   
  ...ionContext.ts |   79.59 |       75 |     100 |   79.59 | 37-40,62-63,78-81 
  bootstrap.ts     |   94.11 |    88.88 |     100 |   94.11 | 71-72             
  checks.ts        |   33.33 |      100 |       0 |   33.33 | 23-28             
  cleanup.ts       |   72.72 |      100 |      75 |   72.72 | 43-52             
  commands.ts      |    50.9 |    63.63 |     100 |    50.9 | 25-26,45,57-84    
  commentJson.ts   |   92.17 |     92.5 |     100 |   92.17 | 92-100            
  ...ScopeUtils.ts |   19.23 |      100 |       0 |   19.23 | 23-40,46-73       
  ...icSettings.ts |   88.61 |    88.88 |     100 |   88.61 | ...37,40-43,61-64 
  ...arResolver.ts |   96.42 |       96 |     100 |   96.42 | 111-112           
  errors.ts        |   94.59 |       88 |     100 |   94.59 | 49-50,88-89       
  events.ts        |     100 |      100 |     100 |     100 |                   
  gitUtils.ts      |    92.5 |    82.35 |     100 |    92.5 | 61-62,77-80       
  ...AutoUpdate.ts |   52.71 |    95.23 |      50 |   52.71 | 88-153            
  ...lationInfo.ts |     100 |      100 |     100 |     100 |                   
  math.ts          |   66.66 |      100 |       0 |   66.66 | 15                
  package.ts       |   88.88 |    83.33 |     100 |   88.88 | 33-34             
  readStdin.ts     |   79.24 |       90 |      80 |   79.24 | 31-38,50-52       
  relaunch.ts      |     100 |      100 |     100 |     100 |                   
  resolvePath.ts   |   66.66 |       25 |     100 |   66.66 | 12-13,16,18-19    
  sandbox.ts       |       0 |        0 |       0 |       0 | 1-1193            
  ...ionCleanup.ts |   94.58 |    87.69 |     100 |   94.58 | ...74-175,256-257 
  sessionUtils.ts  |    9.23 |      100 |       0 |    9.23 | 43-99,106-120     
  settingsUtils.ts |   84.14 |    90.52 |   93.33 |   84.14 | ...12-439,478-479 
  ...ttingSaver.ts |    1.92 |      100 |       0 |    1.92 | 7-28,36-81        
  spawnWrapper.ts  |     100 |      100 |     100 |     100 |                   
  ...upWarnings.ts |     100 |      100 |     100 |     100 |                   
  stdinSafety.ts   |   93.24 |    86.48 |     100 |   93.24 | ...62-163,167,242 
  ...entEmitter.ts |     100 |      100 |     100 |     100 |                   
  ...upWarnings.ts |     100 |      100 |     100 |     100 |                   
  version.ts       |     100 |       50 |     100 |     100 | 11                
  windowTitle.ts   |     100 |      100 |     100 |     100 |                   
 src/utils/privacy |    46.3 |    68.57 |   52.63 |    46.3 |                   
  ...taRedactor.ts |   60.66 |    70.58 |   55.55 |   60.66 | ...77-479,485-506 
  ...acyManager.ts |       0 |        0 |       0 |       0 | 1-178             
 ...ed-integration |   22.35 |        0 |       0 |   22.35 |                   
  acp.ts           |   14.63 |        0 |       0 |   14.63 | ...31-332,335-342 
  ...temService.ts |   20.58 |      100 |       0 |   20.58 | ...34,37-46,48-49 
  schema.ts        |     100 |      100 |     100 |     100 |                   
  ...ntegration.ts |    4.43 |      100 |       0 |    4.43 | ...1442,1457-1507 
-------------------|---------|----------|---------|---------|-------------------
Core Package - Full Text Report
-------------------|---------|----------|---------|---------|-------------------
File               | % Stmts | % Branch | % Funcs | % Lines | Uncovered Line #s 
-------------------|---------|----------|---------|---------|-------------------
All files          |   70.91 |    78.74 |   73.71 |   70.91 |                   
 src               |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
 src/__mocks__/fs  |       0 |        0 |       0 |       0 |                   
  promises.ts      |       0 |        0 |       0 |       0 | 1-48              
 src/adapters      |     100 |      100 |     100 |     100 |                   
  ...eamAdapter.ts |     100 |      100 |     100 |     100 |                   
 src/agents        |   77.47 |     68.1 |      90 |   77.47 |                   
  ...vestigator.ts |       0 |        0 |       0 |       0 | 1-152             
  executor.ts      |   88.23 |    67.03 |     100 |   88.23 | ...03-704,740-746 
  invocation.ts    |   96.34 |    76.47 |     100 |   96.34 | 61,65-66          
  registry.ts      |       0 |        0 |       0 |       0 | 1-83              
  types.ts         |     100 |      100 |     100 |     100 |                   
  utils.ts         |   78.94 |       80 |     100 |   78.94 | 32-35             
 src/auth          |   65.13 |    79.54 |   75.53 |   65.13 |                   
  ...evice-flow.ts |    7.21 |        0 |       0 |    7.21 | ...49-268,274-282 
  ...evice-flow.ts |   46.65 |    57.14 |   63.63 |   46.65 | ...95-484,494-580 
  oauth-errors.ts  |   94.15 |    83.33 |     100 |   94.15 | ...68,609,635-636 
  precedence.ts    |   76.75 |    78.15 |   94.44 |   76.75 | ...1028,1034-1037 
  ...evice-flow.ts |    8.33 |      100 |       0 |    8.33 | ...69-206,214-220 
  token-store.ts   |   77.96 |    88.09 |    90.9 |   77.96 | ...51-272,297-298 
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/code_assist   |   68.18 |     79.6 |      78 |   68.18 |                   
  codeAssist.ts    |   16.25 |       50 |   33.33 |   16.25 | ...1,80-87,95-108 
  converter.ts     |    94.9 |    93.02 |     100 |    94.9 | ...84,198,215-216 
  ...al-storage.ts |     100 |    79.41 |     100 |     100 | 47-49,80-83       
  oauth2.ts        |    63.4 |    75.29 |   78.57 |    63.4 | ...16-717,722-723 
  server.ts        |   51.89 |    72.72 |   53.84 |   51.89 | ...99-240,243-246 
  setup.ts         |   82.92 |    73.91 |     100 |   82.92 | ...27-129,153-159 
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/config        |   75.26 |     80.3 |    60.4 |   75.26 |                   
  config.ts        |   72.79 |    79.37 |   47.58 |   72.79 | ...1820,1826-1830 
  constants.ts     |     100 |      100 |     100 |     100 |                   
  endpoints.ts     |     100 |      100 |     100 |     100 |                   
  index.ts         |     100 |      100 |     100 |     100 |                   
  models.ts        |     100 |      100 |     100 |     100 |                   
  ...ileManager.ts |    94.8 |    85.07 |     100 |    94.8 | ...57-358,364,367 
  storage.ts       |   93.25 |    95.65 |   91.66 |   93.25 | 27-28,49-50,75-76 
  ...entManager.ts |   57.91 |    65.57 |     100 |   57.91 | ...57-458,476-500 
  types.ts         |       0 |        0 |       0 |       0 |                   
 ...nfirmation-bus |   68.39 |    88.46 |   66.66 |   68.39 |                   
  index.ts         |       0 |        0 |       0 |       0 | 1-2               
  message-bus.ts   |   67.48 |    91.66 |   72.72 |   67.48 | ...04-238,247-255 
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/core          |    65.5 |    72.81 |   70.62 |    65.5 |                   
  baseLlmClient.ts |   97.26 |       90 |     100 |   97.26 | 55-56,244-245     
  ...ntegration.ts |   96.29 |       95 |     100 |   96.29 | ...18-119,199-200 
  client.ts        |   61.91 |    78.15 |   69.23 |   61.91 | ...2024,2028-2039 
  ...ion-config.ts |     100 |      100 |     100 |     100 |                   
  ...tGenerator.ts |   91.08 |    80.76 |     100 |   91.08 | ...32,148,163-166 
  ...lScheduler.ts |   77.04 |    75.78 |   94.73 |   77.04 | ...1874,1878-1884 
  geminiChat.ts    |   53.46 |    57.14 |   56.79 |   53.46 | ...2779,2802-2803 
  geminiRequest.ts |     100 |      100 |     100 |     100 |                   
  ...nAIWrapper.ts |   88.88 |      100 |   83.33 |   88.88 | 56-59             
  logger.ts        |   81.26 |    81.81 |     100 |   81.26 | ...64-378,419-430 
  ...tGenerator.ts |   10.89 |      100 |       0 |   10.89 | ...93-194,197-200 
  ...olExecutor.ts |   63.28 |    62.74 |   65.21 |   63.28 | ...06-440,449-465 
  prompts.ts       |   66.26 |    64.28 |      70 |   66.26 | ...73,280-281,297 
  subagent.ts      |   53.35 |    65.42 |   59.25 |   53.35 | ...1925,1937-1938 
  ...chestrator.ts |    89.1 |    73.56 |   95.23 |    89.1 | ...17,620-621,626 
  ...tScheduler.ts |       0 |        0 |       0 |       0 | 1                 
  tokenLimits.ts   |   90.27 |    73.07 |     100 |   90.27 | ...72,77,79,83,93 
  ...Governance.ts |    94.2 |     90.9 |     100 |    94.2 | 34-35,51-52       
  turn.ts          |   88.42 |    73.21 |     100 |   88.42 | ...24-425,455-456 
 src/debug         |   79.16 |    88.43 |   89.65 |   79.16 |                   
  ...ionManager.ts |   77.88 |    78.04 |      85 |   77.88 | ...33-234,251-255 
  DebugLogger.ts   |   91.07 |    90.32 |      88 |   91.07 | ...72,211-215,252 
  FileOutput.ts    |   96.82 |    95.23 |     100 |   96.82 | 82-83,107-108     
  ...ionManager.ts |       0 |      100 |     100 |       0 | 18-64             
  ...FileOutput.ts |       0 |      100 |     100 |       0 | 15-37             
  index.ts         |     100 |      100 |     100 |     100 |                   
  types.ts         |       0 |        0 |       0 |       0 |                   
 src/filters       |   99.19 |     98.8 |     100 |   99.19 |                   
  EmojiFilter.ts   |   99.19 |     98.8 |     100 |   99.19 | 208-209           
 src/hooks         |   88.88 |    33.33 |     100 |   88.88 |                   
  ...ssion-hook.ts |   88.88 |    33.33 |     100 |   88.88 | 24,30             
 src/ide           |   72.88 |    84.43 |      74 |   72.88 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  detect-ide.ts    |     100 |      100 |     100 |     100 |                   
  ide-client.ts    |   54.56 |    75.51 |   56.66 |   54.56 | ...62-470,498-506 
  ide-installer.ts |   90.55 |    85.18 |     100 |   90.55 | ...35,142-146,159 
  ideContext.ts    |    83.8 |      100 |     100 |    83.8 | 75-91             
  process-utils.ts |   89.13 |    81.48 |     100 |   89.13 | ...69-170,211-212 
 src/interfaces    |       0 |        0 |       0 |       0 |                   
  index.ts         |       0 |        0 |       0 |       0 |                   
  ....interface.ts |       0 |        0 |       0 |       0 |                   
 src/mcp           |    78.5 |    77.04 |   71.95 |    78.5 |                   
  ...oken-store.ts |   87.38 |    90.47 |   81.25 |   87.38 | ...33-334,337-338 
  ...h-provider.ts |   83.01 |      100 |      25 |   83.01 | ...69,73,77,81-82 
  ...h-provider.ts |   73.14 |    53.68 |     100 |   73.14 | ...04-811,818-820 
  ...en-storage.ts |    81.5 |    88.88 |   68.18 |    81.5 | ...95-196,201-202 
  oauth-utils.ts   |   70.33 |    81.48 |    90.9 |   70.33 | ...62-283,308-331 
  ...n-provider.ts |   89.28 |    95.65 |      40 |   89.28 | ...37,141,145-146 
  token-store.ts   |     100 |      100 |     100 |     100 |                   
 .../token-storage |   90.08 |    86.86 |   95.34 |   90.08 |                   
  ...en-storage.ts |     100 |      100 |     100 |     100 |                   
  ...en-storage.ts |   86.61 |    87.09 |   92.85 |   86.61 | ...64-172,180-181 
  ...en-storage.ts |     100 |      100 |     100 |     100 |                   
  ...en-storage.ts |   87.43 |    80.82 |    92.3 |   87.43 | ...20,222,274-275 
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/parsers       |   68.08 |       75 |   83.33 |   68.08 |                   
  ...CallParser.ts |   68.08 |       75 |   83.33 |   68.08 | ...1018,1024-1039 
 src/policy        |    88.1 |    81.29 |   86.95 |    88.1 |                   
  config.ts        |   91.01 |    84.21 |     100 |   91.01 | ...05,150,155-157 
  index.ts         |       0 |        0 |       0 |       0 | 1-5               
  policy-engine.ts |     100 |    97.67 |     100 |     100 | 23                
  ...-stringify.ts |   80.23 |    60.52 |      50 |   80.23 | ...22-126,139-140 
  toml-loader.ts   |   87.16 |    83.78 |     100 |   87.16 | ...03-204,215-223 
  types.ts         |     100 |      100 |     100 |     100 |                   
 src/prompt-config |   74.73 |    84.18 |    87.8 |   74.73 |                   
  ...lateEngine.ts |    93.9 |    88.52 |     100 |    93.9 | ...29,165,172,192 
  index.ts         |       0 |      100 |     100 |       0 | 5-41              
  prompt-cache.ts  |   99.04 |    97.26 |     100 |   99.04 | 204-205           
  ...-installer.ts |   83.11 |    82.53 |     100 |   83.11 | ...1173,1253-1254 
  prompt-loader.ts |   87.27 |    90.42 |   76.92 |   87.27 | ...22-423,429-430 
  ...t-resolver.ts |   34.85 |    64.17 |   53.84 |   34.85 | ...20-771,774-802 
  ...pt-service.ts |   84.49 |     83.5 |   93.75 |   84.49 | ...21,550,562-563 
  types.ts         |     100 |      100 |     100 |     100 |                   
 ...onfig/defaults |   50.11 |    46.75 |     100 |   50.11 |                   
  core-defaults.ts |    37.3 |    39.02 |     100 |    37.3 | ...72,283,289-297 
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...est-loader.ts |   81.81 |    79.31 |     100 |   81.81 | ...02-108,116-120 
  ...t-warnings.ts |      92 |    33.33 |     100 |      92 | 17-18             
  ...r-defaults.ts |    41.7 |    39.02 |     100 |    41.7 | ...40,251,257-262 
  ...e-defaults.ts |     100 |      100 |     100 |     100 |                   
  tool-defaults.ts |      50 |       40 |     100 |      50 | ...11-216,229-234 
 src/prompts       |   26.41 |      100 |      25 |   26.41 |                   
  mcp-prompts.ts   |   18.18 |      100 |       0 |   18.18 | 11-19             
  ...t-registry.ts |   28.57 |      100 |   28.57 |   28.57 | ...42,48-55,68-73 
 src/providers     |   68.76 |    77.34 |   67.21 |   68.76 |                   
  BaseProvider.ts  |   80.78 |    79.46 |   80.76 |   80.78 | ...1154,1191-1192 
  ...eratorRole.ts |     100 |      100 |     100 |     100 |                   
  IModel.ts        |       0 |        0 |       0 |       0 |                   
  IProvider.ts     |       0 |        0 |       0 |       0 |                   
  ...derManager.ts |     100 |      100 |     100 |     100 |                   
  ITool.ts         |       0 |        0 |       0 |       0 |                   
  ...ngProvider.ts |   87.91 |    88.61 |   90.62 |   87.91 | ...1106,1137-1139 
  ...derWrapper.ts |   56.67 |     64.4 |   51.28 |   56.67 | ...1355,1362-1369 
  ...tGenerator.ts |    17.3 |      100 |       0 |    17.3 | ...59,62-79,82-85 
  ...derManager.ts |   60.16 |    72.96 |   60.46 |   60.16 | ...1437-1438,1441 
  errors.ts        |   78.57 |    63.63 |      60 |   78.57 | ...43,150-170,191 
  types.ts         |       0 |        0 |       0 |       0 | 1                 
 ...ders/anthropic |   73.04 |    76.28 |   74.54 |   73.04 |                   
  ...icProvider.ts |   74.97 |    79.49 |      75 |   74.97 | ...2411,2419-2420 
  ...aConverter.ts |   51.61 |    40.62 |   71.42 |   51.61 | ...52,258,272-280 
 ...oviders/gemini |   56.39 |    67.94 |   48.78 |   56.39 |                   
  ...niProvider.ts |   51.86 |    56.97 |   46.15 |   51.86 | ...1866,1875-1876 
  ...Signatures.ts |     100 |    98.38 |     100 |     100 | 182               
 ...viders/logging |   39.53 |       80 |      75 |   39.53 |                   
  ...tExtractor.ts |       0 |        0 |       0 |       0 | 1-228             
  ...nceTracker.ts |   89.47 |    84.21 |   81.81 |   89.47 | ...66-167,182-183 
 ...oviders/openai |   51.72 |    73.99 |   60.49 |   51.72 |                   
  ...ationCache.ts |   70.49 |    86.66 |   82.35 |   70.49 | ...64-166,216-217 
  ...rateParams.ts |       0 |        0 |       0 |       0 |                   
  ...AIProvider.ts |   39.13 |    63.97 |   45.55 |   39.13 | ...4940,4948-4957 
  ...API_MODELS.ts |     100 |      100 |     100 |     100 |                   
  ...lCollector.ts |   93.33 |    89.28 |     100 |   93.33 | ...51-153,173-174 
  ...Normalizer.ts |   92.75 |       96 |     100 |   92.75 | 74-78             
  ...llPipeline.ts |   64.54 |    53.33 |      75 |   64.54 | ...34-143,174-184 
  ...eValidator.ts |   94.02 |    93.75 |     100 |   94.02 | 106-109           
  ...sesRequest.ts |   83.56 |    93.24 |     100 |   83.56 | ...60,293,298-303 
  ...moteTokens.ts |   89.55 |     92.3 |     100 |   89.55 | 101-107           
  ...oviderInfo.ts |    86.2 |    73.52 |     100 |    86.2 | ...31-133,144-145 
  ...uestParams.ts |   93.81 |     82.6 |     100 |   93.81 | ...,95-96,105-106 
  ...nsesStream.ts |   88.43 |    86.66 |     100 |   88.43 | ...80,203-210,234 
  ...aConverter.ts |    24.2 |    42.85 |   28.57 |    24.2 | ...59-260,277-285 
  ...lResponses.ts |   71.98 |    73.91 |      75 |   71.98 | ...97-301,321-335 
  test-types.ts    |       0 |        0 |       0 |       0 |                   
  toolNameUtils.ts |   96.79 |    95.45 |      50 |   96.79 | 102,127,239-241   
 ...enai-responses |   63.69 |    74.07 |   43.75 |   63.69 |                   
  CODEX_MODELS.ts  |     100 |      100 |     100 |     100 |                   
  CODEX_PROMPT.ts  |   33.33 |      100 |       0 |   33.33 | 36-39             
  ...esProvider.ts |   75.67 |    78.37 |   54.54 |   75.67 | ...25,841,845-846 
  ...romContent.ts |   84.93 |    66.66 |     100 |   84.93 | 45-49,71-75,94    
  index.ts         |       0 |        0 |       0 |       0 | 1                 
  ...aConverter.ts |    8.12 |       20 |   14.28 |    8.12 | ...53-277,280-289 
 .../openai-vercel |   66.81 |    67.42 |   66.66 |   66.81 |                   
  ...elProvider.ts |   63.81 |     64.9 |   54.34 |   63.81 | ...1934,1944-1999 
  errors.ts        |   93.23 |    82.05 |     100 |   93.23 | ...50-151,165-169 
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...Conversion.ts |   71.63 |    73.17 |   83.33 |   71.63 | ...45,548-549,553 
  ...aConverter.ts |   50.95 |       40 |   71.42 |   50.95 | ...58-259,276-284 
  toolIdUtils.ts   |   86.15 |    84.37 |     100 |   86.15 | ...,94-95,116-117 
 ...ders/reasoning |    42.1 |       90 |      70 |    42.1 |                   
  ...oningUtils.ts |    42.1 |       90 |      70 |    42.1 | ...45-203,235-310 
 ...ers/test-utils |     100 |      100 |     100 |     100 |                   
  ...TestConfig.ts |     100 |      100 |     100 |     100 |                   
 ...ers/tokenizers |    66.1 |    55.55 |      75 |    66.1 |                   
  ...cTokenizer.ts |   57.89 |       25 |     100 |   57.89 | 28-29,34-39       
  ITokenizer.ts    |       0 |        0 |       0 |       0 |                   
  ...ITokenizer.ts |      70 |       80 |   66.66 |      70 | 52-55,62-71       
 ...roviders/types |       0 |        0 |       0 |       0 |                   
  ...iderConfig.ts |       0 |        0 |       0 |       0 |                   
  ...derRuntime.ts |       0 |        0 |       0 |       0 |                   
 ...roviders/utils |   85.64 |    87.01 |      96 |   85.64 |                   
  authToken.ts     |   33.33 |       50 |      50 |   33.33 | 14-22,30-35       
  ...sExtractor.ts |   95.45 |     91.3 |     100 |   95.45 | 15-16             
  dumpContext.ts   |    96.1 |    95.65 |     100 |    96.1 | 110-112           
  ...SDKContext.ts |   94.59 |       75 |     100 |   94.59 | 27,49             
  localEndpoint.ts |   89.28 |    91.42 |     100 |   89.28 | ...18-119,138-139 
  ...malization.ts |   79.41 |    92.85 |     100 |   79.41 | 59-66             
  ...nsePayload.ts |   91.05 |    82.92 |     100 |   91.05 | 60-65,115-119     
  userMemory.ts    |   51.51 |    66.66 |     100 |   51.51 | 16-18,31-43       
 src/runtime       |   84.59 |     86.1 |   74.64 |   84.59 |                   
  ...imeContext.ts |     100 |      100 |     100 |     100 |                   
  ...timeLoader.ts |      85 |    71.42 |      80 |      85 | ...87-190,228-231 
  ...ntimeState.ts |   95.22 |    92.07 |     100 |   95.22 | ...35-636,652-653 
  ...ionContext.ts |   80.35 |    92.85 |      75 |   80.35 | 64-66,110-117     
  ...imeContext.ts |   90.09 |      100 |   61.53 |   90.09 | ...00,107,115-117 
  index.ts         |       0 |        0 |       0 |       0 | 1-15              
  ...imeContext.ts |    64.7 |    83.33 |     100 |    64.7 | 67-78,83-94       
  ...meAdapters.ts |   54.95 |    68.75 |   43.75 |   54.95 | ...98-108,125-152 
  ...ateFactory.ts |    96.9 |    86.48 |     100 |    96.9 | 95,110,136        
 src/services      |   81.53 |    84.27 |   75.96 |   81.53 |                   
  ...ardService.ts |   93.33 |    92.85 |     100 |   93.33 | 63,67-68          
  ...y-analyzer.ts |   76.32 |    81.17 |   77.77 |   76.32 | ...79-507,513-514 
  ...eryService.ts |   96.29 |    84.21 |     100 |   96.29 | 41,50,100-101     
  ...temService.ts |    61.9 |      100 |   66.66 |    61.9 | 54-61             
  ...ts-service.ts |      50 |      100 |       0 |      50 | 41-42,48-49       
  gitService.ts    |   70.58 |    93.33 |      60 |   70.58 | ...16-126,129-133 
  index.ts         |       0 |        0 |       0 |       0 | 1-15              
  ...ionService.ts |   99.04 |    98.41 |     100 |   99.04 | 270-271           
  ...ionService.ts |   89.31 |    84.21 |    92.3 |   89.31 | ...09,614,623-639 
  ...xt-tracker.ts |   94.87 |       90 |    87.5 |   94.87 | 54-55             
  ...er-service.ts |      42 |     90.9 |      25 |      42 | ...37-140,143-161 
  ...er-service.ts |   69.45 |    55.88 |      80 |   69.45 | ...85-289,311-314 
 ...rvices/history |   79.01 |       83 |   77.77 |   79.01 |                   
  ...Converters.ts |   79.88 |    80.31 |      75 |   79.88 | ...58-364,430-453 
  HistoryEvents.ts |       0 |        0 |       0 |       0 |                   
  ...oryService.ts |   79.17 |    83.71 |   86.53 |   79.17 | ...1329,1365-1366 
  IContent.ts      |   73.56 |    92.85 |   36.36 |   73.56 | ...32,299,309-329 
 src/settings      |   92.48 |    77.02 |      92 |   92.48 |                   
  ...ngsService.ts |   91.69 |       75 |   95.23 |   91.69 | ...53-354,384-388 
  ...ceInstance.ts |     100 |      100 |     100 |     100 |                   
  types.ts         |       0 |        0 |       0 |       0 | 1                 
 src/storage       |   93.53 |    93.02 |   94.44 |   93.53 |                   
  ...FileWriter.ts |   83.54 |       80 |    87.5 |   83.54 | 40-41,71-81       
  ...nceService.ts |   98.67 |    96.96 |     100 |   98.67 | 293-294           
  sessionTypes.ts  |     100 |      100 |     100 |     100 |                   
 src/telemetry     |   65.88 |    79.89 |   60.33 |   65.88 |                   
  constants.ts     |     100 |      100 |     100 |     100 |                   
  ...-exporters.ts |   28.08 |      100 |       0 |   28.08 | ...14-115,118-119 
  index.ts         |     100 |      100 |     100 |     100 |                   
  ...t.circular.ts |       0 |        0 |       0 |       0 | 1-17              
  ...t.circular.ts |       0 |        0 |       0 |       0 | 1-132             
  loggers.ts       |   64.38 |    69.76 |   59.25 |   64.38 | ...70-583,591-607 
  metrics.ts       |   62.35 |    96.15 |   66.66 |   62.35 | ...41-163,166-189 
  sdk.ts           |   72.54 |    23.07 |     100 |   72.54 | ...35,140-141,143 
  ...l-decision.ts |   33.33 |      100 |       0 |   33.33 | 17-32             
  types.ts         |   73.94 |     84.7 |   64.91 |   73.94 | ...34-636,639-643 
  uiTelemetry.ts   |   95.26 |    96.42 |   91.66 |   95.26 | 152,189-195       
 src/test-utils    |   86.61 |    82.35 |   56.81 |   86.61 |                   
  config.ts        |     100 |      100 |     100 |     100 |                   
  index.ts         |       0 |        0 |       0 |       0 | 1-9               
  mock-tool.ts     |   95.06 |    93.33 |      75 |   95.06 | 62-63,118-119     
  ...aceContext.ts |     100 |      100 |     100 |     100 |                   
  ...allOptions.ts |   93.45 |    90.69 |   63.63 |   93.45 | ...07,171,200-203 
  runtime.ts       |   80.18 |       70 |   39.53 |   80.18 | ...99-301,310-312 
  tools.ts         |      82 |    76.92 |   78.94 |      82 | ...31,153,157-158 
 src/todo          |   56.28 |    81.48 |      75 |   56.28 |                   
  todoFormatter.ts |   56.28 |    81.48 |      75 |   56.28 | ...11-212,236-237 
 src/tools         |   76.17 |    78.24 |   79.87 |   76.17 |                   
  ...lFormatter.ts |     100 |      100 |     100 |     100 |                   
  ToolFormatter.ts |   20.89 |    76.19 |   33.33 |   20.89 | ...07,514-612,627 
  ...IdStrategy.ts |      95 |    92.85 |     100 |      95 | 237-239,250-252   
  codesearch.ts    |      98 |     87.5 |   85.71 |      98 | 110-111,173       
  ...line_range.ts |   84.68 |    67.56 |      70 |   84.68 | ...81-282,290-291 
  diffOptions.ts   |     100 |      100 |     100 |     100 |                   
  ...-web-fetch.ts |   93.18 |    72.41 |   77.77 |   93.18 | ...56,166-167,187 
  ...scapeUtils.ts |   61.65 |    72.97 |      50 |   61.65 | ...93,309,311-321 
  edit.ts          |    74.7 |    78.03 |   76.47 |    74.7 | ...69-770,783-824 
  ...web-search.ts |   97.91 |    85.71 |   83.33 |   97.91 | 126-127,191       
  ...y-replacer.ts |   85.71 |    84.35 |     100 |   85.71 | ...47-448,493-494 
  glob.ts          |   90.51 |    80.35 |   88.88 |   90.51 | ...51-252,351-352 
  ...-web-fetch.ts |   92.44 |    87.37 |    92.3 |   92.44 | ...93-394,495-496 
  ...invocation.ts |   54.74 |    38.88 |      75 |   54.74 | ...29-133,165-210 
  ...web-search.ts |     100 |      100 |     100 |     100 |                   
  grep.ts          |   59.51 |    77.34 |   72.22 |   59.51 | ...67-971,981-982 
  ...rt_at_line.ts |   81.55 |    76.08 |      70 |   81.55 | ...05-306,314-315 
  ...-subagents.ts |   87.28 |    69.56 |   88.88 |   87.28 | ...1,81-89,98,153 
  ls.ts            |   97.42 |    91.66 |     100 |   97.42 | 146-151           
  ...nt-manager.ts |   79.04 |    66.66 |      80 |   79.04 | ...31-138,146-147 
  mcp-client.ts    |   54.67 |     60.8 |   58.06 |   54.67 | ...1342,1346-1349 
  mcp-tool.ts      |   94.21 |    93.75 |   86.95 |   94.21 | ...39-249,311-312 
  memoryTool.ts    |   79.39 |    82.75 |    87.5 |   79.39 | ...55-356,399-440 
  ...iable-tool.ts |   98.34 |       80 |     100 |   98.34 | 168-169           
  read-file.ts     |   91.27 |    79.16 |    90.9 |   91.27 | ...26-227,393-394 
  ...many-files.ts |   71.42 |     77.5 |   88.88 |   71.42 | ...55-556,563-564 
  ...line_range.ts |    74.9 |     65.9 |      80 |    74.9 | ...50-351,355-356 
  ripGrep.ts       |   89.75 |    86.02 |    92.3 |   89.75 | ...47-448,469-470 
  shell.ts         |   86.92 |    80.86 |   88.88 |   86.92 | ...57-758,769-770 
  task.ts          |   80.65 |    69.04 |   92.85 |   80.65 | ...88,791,794-803 
  todo-events.ts   |    62.5 |      100 |       0 |    62.5 | 23-24,27-28,31-32 
  todo-pause.ts    |   87.09 |       80 |     100 |   87.09 | 64-69,73-78,93-98 
  todo-read.ts     |   85.29 |    95.45 |     100 |   85.29 | 112-113,123-138   
  todo-schemas.ts  |     100 |      100 |     100 |     100 |                   
  todo-store.ts    |   86.66 |       80 |     100 |   86.66 | 48-49,55-56,63-64 
  todo-write.ts    |   87.28 |    75.75 |    87.5 |   87.28 | ...17,264-265,290 
  ...tion-types.ts |     100 |      100 |     100 |     100 |                   
  tool-context.ts  |     100 |      100 |     100 |     100 |                   
  tool-error.ts    |   87.75 |      100 |       0 |   87.75 | 105-112           
  tool-names.ts    |     100 |      100 |     100 |     100 |                   
  tool-registry.ts |   72.28 |       71 |   73.68 |   72.28 | ...68-676,684-685 
  toolNameUtils.ts |      80 |     92.1 |     100 |      80 | 59-60,64-65,69-82 
  tools.ts         |   67.74 |    91.37 |   66.66 |   67.74 | ...14-815,818-822 
  write-file.ts    |   75.57 |    64.36 |   73.33 |   75.57 | ...04-605,614-653 
 src/types         |     100 |      100 |     100 |     100 |                   
  modelParams.ts   |     100 |      100 |     100 |     100 |                   
 src/utils         |   81.01 |    87.01 |   83.89 |   81.01 |                   
  LruCache.ts      |       0 |        0 |       0 |       0 | 1-41              
  bfsFileSearch.ts |   88.88 |       90 |     100 |   88.88 | 83-91             
  browser.ts       |    8.69 |      100 |       0 |    8.69 | 17-53             
  delay.ts         |     100 |      100 |     100 |     100 |                   
  editor.ts        |   97.64 |    94.23 |     100 |   97.64 | 159,228,231-232   
  ...entContext.ts |     100 |      100 |     100 |     100 |                   
  errorParsing.ts  |      88 |    78.26 |     100 |      88 | ...07,249,252,258 
  ...rReporting.ts |   83.72 |    84.61 |     100 |   83.72 | 82-86,107-115     
  errors.ts        |   55.55 |    71.42 |   38.46 |   55.55 | ...92-108,112-118 
  fetch.ts         |   29.87 |    66.66 |      25 |   29.87 | ...37,40-84,87-92 
  fileUtils.ts     |    95.2 |       90 |     100 |    95.2 | ...34-238,450-456 
  formatters.ts    |   54.54 |       50 |     100 |   54.54 | 12-16             
  ...eUtilities.ts |   96.11 |       96 |     100 |   96.11 | 36-37,67-68       
  ...rStructure.ts |   95.96 |    94.93 |     100 |   95.96 | ...14-117,345-347 
  getPty.ts        |    12.5 |      100 |       0 |    12.5 | 21-34             
  ...noreParser.ts |    91.6 |    85.45 |     100 |    91.6 | ...05-206,210-211 
  ...ineChanges.ts |   58.56 |    79.41 |      80 |   58.56 | ...18-256,264-270 
  gitUtils.ts      |   90.24 |    90.47 |     100 |   90.24 | 40-41,71-72       
  ide-trust.ts     |      60 |      100 |       0 |      60 | 14-15             
  ...rePatterns.ts |     100 |    96.55 |     100 |     100 | 248               
  ...ionManager.ts |     100 |       90 |     100 |     100 | 23                
  ...edit-fixer.ts |       0 |        0 |       0 |       0 | 1-156             
  ...yDiscovery.ts |   86.32 |    76.27 |   77.77 |   86.32 | ...02-403,406-407 
  ...tProcessor.ts |    93.4 |    86.51 |    92.3 |    93.4 | ...87-388,397-398 
  ...Inspectors.ts |   61.53 |      100 |      50 |   61.53 | 18-23             
  output-format.ts |   36.36 |      100 |       0 |   36.36 | ...52-153,163-184 
  partUtils.ts     |     100 |      100 |     100 |     100 |                   
  pathReader.ts    |       0 |        0 |       0 |       0 | 1-60              
  paths.ts         |   85.32 |    84.37 |     100 |   85.32 | ...,98-99,110-111 
  ...rDetection.ts |   57.62 |    63.15 |     100 |   57.62 | ...9,92-93,99-100 
  retry.ts         |   68.09 |    77.69 |      80 |   68.09 | ...94-597,602-603 
  ...thResolver.ts |   84.31 |       84 |     100 |   84.31 | 62-73,96,145-148  
  ...nStringify.ts |     100 |      100 |     100 |     100 |                   
  sanitization.ts  |     100 |      100 |     100 |     100 |                   
  ...aValidator.ts |   83.52 |    82.75 |     100 |   83.52 | 70-81,125-126     
  ...r-launcher.ts |   78.57 |     87.5 |   66.66 |   78.57 | ...33,135,153-188 
  session.ts       |     100 |      100 |     100 |     100 |                   
  shell-markers.ts |     100 |      100 |     100 |     100 |                   
  shell-parser.ts  |   39.62 |    57.14 |    62.5 |   39.62 | ...50-164,171-201 
  shell-utils.ts   |   89.26 |    93.38 |     100 |   89.26 | ...75-279,385-390 
  summarizer.ts    |     100 |    88.88 |     100 |     100 | 92                
  ...emEncoding.ts |   97.14 |    91.42 |     100 |   97.14 | 108-109,161       
  testUtils.ts     |   53.33 |      100 |   33.33 |   53.33 | ...53,59-64,70-72 
  textUtils.ts     |    12.5 |      100 |       0 |    12.5 | 15-34             
  thoughtUtils.ts  |     100 |      100 |     100 |     100 |                   
  tool-utils.ts    |      88 |    86.36 |     100 |      88 | 52-53,56-57,67-68 
  ...putLimiter.ts |   85.22 |    76.31 |     100 |   85.22 | ...61-166,210-217 
  unicodeUtils.ts  |     100 |      100 |     100 |     100 |                   
  ...untManager.ts |   91.96 |    88.23 |     100 |   91.96 | 37-39,76-78,94-96 
  ...aceContext.ts |   96.82 |    95.34 |    92.3 |   96.82 | 94-95,109-110     
 ...ils/filesearch |   96.18 |    91.26 |     100 |   96.18 |                   
  crawlCache.ts    |     100 |      100 |     100 |     100 |                   
  crawler.ts       |   96.22 |     92.3 |     100 |   96.22 | 66-67             
  fileSearch.ts    |   93.22 |    86.95 |     100 |   93.22 | ...26-227,229-230 
  ignore.ts        |     100 |      100 |     100 |     100 |                   
  result-cache.ts  |     100 |    91.66 |     100 |     100 | 46                
-------------------|---------|----------|---------|---------|-------------------

For detailed HTML reports, please see the 'coverage-reports-24.x-ubuntu-latest' artifact from the main CI run.

@acoliver
Copy link
Collaborator Author

acoliver commented Jan 9, 2026

@CodeRabbit we discovered this didn't fix the prompt -- the issue is that when yolo is not on the command is exeucting BEFORE being approved.

the seocnd issue we discovered (not yet addressed) is that there are MANY instances of the CoreSchedulerService -- tehre should in my opinion be only 1.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 9, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

@acoliver, thank you for reporting these findings. Let me investigate both issues:

  1. Commands executing before approval when yolo mode is off
  2. Multiple CoreSchedulerService instances

🏁 Script executed:

#!/bin/bash

echo "=== Finding CoreSchedulerService definition and instantiations ==="
fd -e ts -e tsx CoreSchedulerService

echo -e "\n=== Searching for 'new CoreSchedulerService' instantiations ==="
rg -nP --type=ts --type=tsx -C3 'new\s+CoreSchedulerService\s*\('

echo -e "\n=== Searching for CoreSchedulerService imports and usage ==="
rg -nP --type=ts --type=tsx -C2 'from.*CoreSchedulerService|import.*CoreSchedulerService'

echo -e "\n=== Searching for scheduleToolCalls in useGeminiStream ==="
rg -nP --type=ts --type=tsx -C5 'scheduleToolCalls' packages/cli/src/ui/hooks/useGeminiStream.ts

echo -e "\n=== Searching for approval/yolo mode logic ==="
rg -nP --type=ts --type=tsx -C3 '\byolo\b|approval|autoApprove' packages/cli/

echo -e "\n=== Searching for when tool execution happens vs scheduling ==="
rg -nP --type=ts --type=tsx -C3 'executeTool|scheduleToolCall' packages/core/src/core/

Length of output: 506


🏁 Script executed:

#!/bin/bash

echo "=== Finding CoreSchedulerService files ==="
fd CoreScheduler

echo -e "\n=== Searching for 'new CoreSchedulerService' instantiations ==="
rg -nP -C3 'new\s+CoreSchedulerService\s*\('

echo -e "\n=== Searching for CoreSchedulerService imports ==="
rg -nP -C2 'from.*CoreScheduler|import.*CoreScheduler'

echo -e "\n=== Searching for scheduleToolCalls in useGeminiStream ==="
rg -nP -C5 'scheduleToolCalls' packages/cli/src/ui/hooks/useGeminiStream.ts

echo -e "\n=== Searching for approval/yolo mode logic in CLI ==="
rg -nP -C3 '\byolo\b|autoApprove' packages/cli/src/

echo -e "\n=== Searching for tool execution and scheduling ==="
rg -nP -C3 'executeTool|scheduleToolCall' packages/core/src/core/ | head -100

Length of output: 39755


🏁 Script executed:

#!/bin/bash

echo "=== Finding CoreToolScheduler files ==="
fd -e ts -e tsx coreToolScheduler

echo -e "\n=== Reading CoreToolScheduler main file ==="
cat packages/core/src/core/coreToolScheduler.ts | head -200

echo -e "\n=== Searching for 'new CoreToolScheduler' instantiations ==="
rg -nP -C3 'new\s+CoreToolScheduler\s*\('

echo -e "\n=== Finding useReactToolScheduler implementation ==="
fd useReactToolScheduler

echo -e "\n=== Reading useReactToolScheduler or useToolScheduler ==="
fd -e ts -e tsx useToolScheduler | head -5 | xargs -I {} sh -c 'echo "=== File: {} ==="; cat {}'

Length of output: 92906


🏁 Script executed:

#!/bin/bash

echo "=== Finding approval flow in CoreToolScheduler ==="
rg -nP -A10 -B5 'awaiting_approval|shouldConfirmExecute|ApprovalMode' packages/core/src/core/coreToolScheduler.ts | head -150

echo -e "\n=== Finding where scheduleToolCalls is defined in useReactToolScheduler ==="
cat packages/cli/src/ui/hooks/useReactToolScheduler.ts | grep -A50 "const scheduleToolCalls"

echo -e "\n=== Check when schedule() is called in the scheduler ==="
rg -nP -A5 'async schedule\(' packages/core/src/core/coreToolScheduler.ts | head -100

echo -e "\n=== Finding all places useReactToolScheduler is instantiated ==="
rg -nP -C3 'useReactToolScheduler\(' packages/cli/src/

echo -e "\n=== Check if there are multiple scheduler instances in useGeminiStream ==="
cat packages/cli/src/ui/hooks/useGeminiStream.ts | grep -B5 -A10 "useReactToolScheduler"

Length of output: 9318


🏁 Script executed:

#!/bin/bash

echo "=== Check the complete schedule() method in CoreToolScheduler ==="
rg -nP -A80 'async schedule\(' packages/core/src/core/coreToolScheduler.ts | head -200

echo -e "\n=== Find where tool execution actually happens ==="
rg -nP -B3 -A10 'private async executeToolCall' packages/core/src/core/coreToolScheduler.ts | head -100

echo -e "\n=== Check how approveToolCall works ==="
rg -nP -B3 -A15 'private approveToolCall' packages/core/src/core/coreToolScheduler.ts

echo -e "\n=== Count how many times CoreToolScheduler is instantiated ==="
rg -l 'new CoreToolScheduler\(' | wc -l
echo "Files creating CoreToolScheduler instances:"
rg -l 'new CoreToolScheduler\('

Length of output: 1604


🏁 Script executed:

#!/bin/bash

echo "=== Find the full schedule() method definition ==="
sed -n '/public async schedule(/,/^  [}]/p' packages/core/src/core/coreToolScheduler.ts | head -150

echo -e "\n=== Find where tools transition from scheduled to executing ==="
rg -nP "status.*'executing'|'executing'" packages/core/src/core/coreToolScheduler.ts | head -30

echo -e "\n=== Find the processQueue or similar method ==="
rg -nP -A30 'private async processQueue|private processQueue' packages/core/src/core/coreToolScheduler.ts

echo -e "\n=== Look for where tool.execute is called ==="
rg -nP -B5 -A5 '\.execute\(|\.executeFn\(' packages/core/src/core/coreToolScheduler.ts | head -80

echo -e "\n=== Check subagent scheduler creation ==="
cat packages/core/src/core/subagent.ts | grep -B10 -A10 "new CoreToolScheduler"

Length of output: 1781


🏁 Script executed:

#!/bin/bash

echo "=== Find the full schedule method and validation flow ==="
sed -n '/public async schedule(/,/^  }/p' packages/core/src/core/coreToolScheduler.ts

echo -e "\n=== Check what triggers tool execution after approval ==="
rg -nP -B5 -A20 'case ToolConfirmationOutcome.ProceedOnce|ProceedOnce' packages/core/src/core/coreToolScheduler.ts

echo -e "\n=== Check if tools can execute without going through awaiting_approval ==="
rg -nP -B10 -A5 "setStatusInternal.*'scheduled'" packages/core/src/core/coreToolScheduler.ts

echo -e "\n=== Check how many useReactToolScheduler instances exist in a session ==="
rg -nP 'useReactToolScheduler\(' packages/cli/src/ -g '!*.test.*'

Length of output: 4566


@acoliver, thank you for reporting these critical findings. I've investigated both issues:

Issue 1: Commands Executing Before Approval (when yolo is OFF)

The deduplication fix in this PR only prevents the same callId from being scheduled multiple times. However, the execution timing issue is separate and happens inside CoreToolScheduler.

Here's what I found in the approval flow (packages/core/src/core/coreToolScheduler.ts, lines 991-1005):

  1. When scheduleToolCalls() is called from useGeminiStream.ts (line 1032 or 514), it immediately triggers validation

  2. During validation, shouldConfirmExecute() is called

  3. The problem: If any of these conditions are true, the tool is immediately approved WITHOUT waiting for user confirmation:

    • shouldConfirmExecute() returns null
    • ApprovalMode.YOLO is set
    • Tool matches allowedTools configuration
  4. After approval at line 1218, it's set to 'scheduled' and then attemptExecutionOfScheduledCalls() is called (line 1195), which starts execution immediately

The root cause appears to be that scheduleToolCalls is being called as soon as the ToolCallRequest event arrives from the stream (see useGeminiStream.ts line 1032), which triggers validation and potential auto-approval before the UI can display the confirmation prompt.

Issue 2: Multiple CoreToolScheduler Instances

You're correct - there are multiple instances being created, though the class is actually called CoreToolScheduler (not CoreSchedulerService). Here's what I found:

In a single CLI session, useReactToolScheduler creates:

  1. One main scheduler (packages/cli/src/ui/hooks/useReactToolScheduler.ts, line 195)
  2. One scheduler per subagent (line 238) - created dynamically when subagents are spawned

Since useGeminiStream.ts calls useReactToolScheduler once (line 289), you should have:

  • 1 main scheduler
  • N subagent schedulers (if subagents are being created)

Additionally, other parts of the codebase create their own instances:

  • nonInteractiveToolExecutor.ts (line 206)
  • subagent.ts (line 662)
  • task.ts in a2a-server (line 429)

If you're seeing many instances in the CLI, it's likely because:

  1. Subagents are being created (each gets its own scheduler)
  2. The scheduler isn't being properly disposed on re-renders
  3. Different execution contexts (interactive vs non-interactive) each create their own

Would you like me to:

  1. Investigate the approval timing issue further to understand why tools execute before the confirmation UI appears?
  2. Create an issue to consolidate all CoreToolScheduler instances into a single global instance?
  3. Look into the subagent scheduler architecture to see if they can share a single scheduler?

🧠 Learnings used
Learnt from: e2720pjk
Repo: vybestack/llxprt-code PR: 583
File: packages/core/src/providers/openai/OpenAIProvider.ts:935-959
Timestamp: 2025-11-16T22:51:26.374Z
Learning: In the llxprt-code codebase (packages/core/src/providers/openai/OpenAIProvider.ts), tools like `run_shell_command` use internal streaming only for real-time UI updates during execution, but each tool execution produces exactly ONE final `ToolResponseBlock` containing the full result. The streaming chunks are never sent to the LLM and are not converted into multiple tool messages. The OpenAI Chat Completions API requires that each tool call (tool_call_id) corresponds to exactly one message with role 'tool', so duplicate tool response detection that removes subsequent tool messages with the same tool_call_id is correct and necessary for API compliance.

Learnt from: acoliver
Repo: vybestack/llxprt-code PR: 0
File: :0-0
Timestamp: 2025-12-26T14:17:41.983Z
Learning: In packages/core/src/core/coreToolScheduler.ts, the `publishBufferedResults()` method (line 1355) is called concurrently by multiple tool completion handlers (line 1510 in each tool's `.then()` callback) when parallel tools finish simultaneously. This causes a race condition where `nextPublishIndex` can be corrupted and tools can hang in 'executing' state forever, blocking the scheduler and causing subsequent tool calls to queue indefinitely. The method must be made reentrant-safe using a mutex/lock pattern to serialize concurrent calls.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
packages/core/src/core/coreToolScheduler.ts (1)

1835-1841: Consider clearing processedConfirmations in cancelAll() for consistency.

Other batch bookkeeping state is reset here, but processedConfirmations is not. While provider-generated callIds are typically unique, clearing this set aligns with the reset pattern and prevents potential edge cases if the scheduler is reused after cancellation.

♻️ Suggested addition
     // 2. Reset batch bookkeeping state to prevent stale state issues
     // if the scheduler is reused after cancellation
     this.pendingResults.clear();
     this.nextPublishIndex = 0;
     this.currentBatchSize = 0;
     this.isPublishingBufferedResults = false;
     this.pendingPublishRequest = false;
+    this.processedConfirmations.clear();
packages/core/src/core/coreToolScheduler.duplication.test.ts (1)

112-117: Unused constructor parameter.

The tool parameter is stored but never used within ExecutionTrackingToolInvocation.

♻️ Suggested fix
 class ExecutionTrackingToolInvocation extends BaseToolInvocation<
   { id: string },
   ToolResult
 > {
   constructor(
-    private readonly tool: ExecutionTrackingTool,
+    _tool: ExecutionTrackingTool,
     params: { id: string },
   ) {
     super(params);
   }
📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9fe8ccb and da0e12a.

📒 Files selected for processing (3)
  • packages/core/src/core/coreToolScheduler.duplication.test.ts
  • packages/core/src/core/coreToolScheduler.test.ts
  • packages/core/src/core/coreToolScheduler.ts
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Don't use any - Always specify proper types. Use unknown if the type is truly unknown and add proper type guards.
Do not use console.log or console.debug - Use the sophisticated logging system instead. Log files are written to ~/.llxprt/debug/
Fix all linting errors, including warnings about any types

Files:

  • packages/core/src/core/coreToolScheduler.ts
  • packages/core/src/core/coreToolScheduler.duplication.test.ts
  • packages/core/src/core/coreToolScheduler.test.ts
🧠 Learnings (3)
📓 Common learnings
Learnt from: e2720pjk
Repo: vybestack/llxprt-code PR: 583
File: packages/core/src/providers/openai/OpenAIProvider.ts:935-959
Timestamp: 2025-11-16T22:51:26.374Z
Learning: In the llxprt-code codebase (packages/core/src/providers/openai/OpenAIProvider.ts), tools like `run_shell_command` use internal streaming only for real-time UI updates during execution, but each tool execution produces exactly ONE final `ToolResponseBlock` containing the full result. The streaming chunks are never sent to the LLM and are not converted into multiple tool messages. The OpenAI Chat Completions API requires that each tool call (tool_call_id) corresponds to exactly one message with role 'tool', so duplicate tool response detection that removes subsequent tool messages with the same tool_call_id is correct and necessary for API compliance.
Learnt from: acoliver
Repo: vybestack/llxprt-code PR: 0
File: :0-0
Timestamp: 2025-12-26T14:17:41.983Z
Learning: In packages/core/src/core/coreToolScheduler.ts, the `publishBufferedResults()` method (line 1355) is called concurrently by multiple tool completion handlers (line 1510 in each tool's `.then()` callback) when parallel tools finish simultaneously. This causes a race condition where `nextPublishIndex` can be corrupted and tools can hang in 'executing' state forever, blocking the scheduler and causing subsequent tool calls to queue indefinitely. The method must be made reentrant-safe using a mutex/lock pattern to serialize concurrent calls.
📚 Learning: 2025-12-26T14:17:41.983Z
Learnt from: acoliver
Repo: vybestack/llxprt-code PR: 0
File: :0-0
Timestamp: 2025-12-26T14:17:41.983Z
Learning: In packages/core/src/core/coreToolScheduler.ts, the `publishBufferedResults()` method (line 1355) is called concurrently by multiple tool completion handlers (line 1510 in each tool's `.then()` callback) when parallel tools finish simultaneously. This causes a race condition where `nextPublishIndex` can be corrupted and tools can hang in 'executing' state forever, blocking the scheduler and causing subsequent tool calls to queue indefinitely. The method must be made reentrant-safe using a mutex/lock pattern to serialize concurrent calls.

Applied to files:

  • packages/core/src/core/coreToolScheduler.ts
  • packages/core/src/core/coreToolScheduler.duplication.test.ts
  • packages/core/src/core/coreToolScheduler.test.ts
📚 Learning: 2025-11-16T22:51:26.374Z
Learnt from: e2720pjk
Repo: vybestack/llxprt-code PR: 583
File: packages/core/src/providers/openai/OpenAIProvider.ts:935-959
Timestamp: 2025-11-16T22:51:26.374Z
Learning: In the llxprt-code codebase (packages/core/src/providers/openai/OpenAIProvider.ts), tools like `run_shell_command` use internal streaming only for real-time UI updates during execution, but each tool execution produces exactly ONE final `ToolResponseBlock` containing the full result. The streaming chunks are never sent to the LLM and are not converted into multiple tool messages. The OpenAI Chat Completions API requires that each tool call (tool_call_id) corresponds to exactly one message with role 'tool', so duplicate tool response detection that removes subsequent tool messages with the same tool_call_id is correct and necessary for API compliance.

Applied to files:

  • packages/core/src/core/coreToolScheduler.duplication.test.ts
  • packages/core/src/core/coreToolScheduler.test.ts
🧬 Code graph analysis (2)
packages/core/src/core/coreToolScheduler.duplication.test.ts (2)
packages/core/src/tools/tools.ts (3)
  • ToolResult (505-533)
  • ToolInvocation (34-74)
  • ToolConfirmationOutcome (27-27)
packages/core/src/core/coreToolScheduler.ts (3)
  • ToolCall (139-146)
  • WaitingToolCall (121-129)
  • CompletedToolCall (148-151)
packages/core/src/core/coreToolScheduler.test.ts (2)
packages/core/src/core/coreToolScheduler.ts (2)
  • WaitingToolCall (121-129)
  • ToolCall (139-146)
packages/core/src/tools/tools.ts (1)
  • ToolConfirmationOutcome (27-27)
⏰ Context from checks skipped due to timeout of 270000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Test (macos-latest, 24.x)
  • GitHub Check: Test (ubuntu-latest, 24.x)
  • GitHub Check: Test (windows-latest, 24.x)
  • GitHub Check: Slow E2E - Win
  • GitHub Check: E2E Test (Linux) - sandbox:docker
  • GitHub Check: E2E Test (Linux) - sandbox:none
  • GitHub Check: E2E Test (macOS)
🔇 Additional comments (8)
packages/core/src/core/coreToolScheduler.ts (3)

1101-1109: LGTM! Solid deduplication guard for confirmation handling.

The early-return pattern with the processedConfirmations Set correctly prevents duplicate tool execution when handleConfirmationResponse is invoked from multiple paths (IDE confirmation, wrapped callbacks, MessageBus).


419-419: Appropriate data structure for deduplication.

Using a Set<string> provides O(1) lookup for callId tracking, which is efficient for the deduplication check in handleConfirmationResponse.


538-538: Proper cleanup in dispose().

Clearing processedConfirmations alongside pendingConfirmations ensures no stale state remains when the scheduler is disposed.

packages/core/src/core/coreToolScheduler.test.ts (1)

2783-2898: Well-structured test for confirmation deduplication.

The test properly validates the fix by:

  1. Setting up a tool requiring confirmation
  2. Simulating the race condition with two concurrent handleConfirmationResponse calls
  3. Asserting single execution via status check

Good coverage for the core scheduler-level deduplication.

packages/core/src/core/coreToolScheduler.duplication.test.ts (4)

1-40: Comprehensive test suite for duplication prevention.

The documentation clearly explains the two critical bugs being addressed, and the test structure covers the key scenarios:

  1. Duplicate handleConfirmationResponse calls with same callId
  2. Single execution via message bus confirmation path
  3. Multi-scheduler message bus isolation

45-75: Well-designed mock for multi-subscriber testing.

The createMockMessageBus correctly tracks callbacks per message type and broadcasts to all subscribers, which is essential for testing the multi-scheduler scenario in test 3.


90-106: Good use of static counter for execution tracking.

The ExecutionTrackingTool with its static executionCount and resetCount() method provides clean test isolation while enabling verification that tools execute exactly once across different code paths.


398-512: Key test case for multi-scheduler isolation.

This test validates that when multiple schedulers subscribe to the same MessageBus, only the scheduler that owns the correlationId processes the confirmation—preventing the "unknown correlationId" spam mentioned in the PR objectives.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (3)
packages/core/src/core/coreToolScheduler.duplication.test.ts (3)

150-166: Consider adding a timeout failure message.

The waitForStatus helper silently returns undefined on timeout, which may lead to confusing test failures. Consider throwing an error or adding a message.

♻️ Suggested improvement
 async function waitForStatus(
   onToolCallsUpdate: ReturnType<typeof vi.fn>,
   targetStatus: string,
   timeoutMs = 5000,
 ): Promise<ToolCall | undefined> {
   const startTime = Date.now();
   while (Date.now() - startTime < timeoutMs) {
     const calls = onToolCallsUpdate.mock.calls;
     if (calls.length > 0) {
       const latestCalls = calls[calls.length - 1][0] as ToolCall[];
       const found = latestCalls.find((call) => call.status === targetStatus);
       if (found) return found;
     }
     await new Promise((resolve) => setTimeout(resolve, 10));
   }
+  // Return undefined but tests should assert on the result
   return undefined;
 }

176-194: Consider extracting the mock tool registry to reduce duplication.

The same mock toolRegistry is duplicated across all three tests. Extracting it to a helper function would improve maintainability.

♻️ Extract mock registry helper
function createMockToolRegistry(testTool: ExecutionTrackingTool) {
  return {
    getTool: () => testTool,
    getFunctionDeclarations: () => [],
    getFunctionDeclarationsFiltered: () => [],
    registerTool: () => {},
    discoverAllTools: async () => {},
    discoverMcpTools: async () => {},
    discoverToolsForServer: async () => {},
    removeMcpToolsByServer: () => {},
    getAllTools: () => [],
    getAllToolNames: () => ['ExecutionTrackingTool'], // Add for completeness
    getToolsByServer: () => [],
    tools: new Map(),
    mcpClientManager: undefined,
    getToolByName: () => testTool,
    getToolByDisplayName: () => testTool,
    getTools: () => [],
    discoverTools: async () => {},
    discovery: {},
  };
}

Also applies to: 301-319, 402-420


168-509: Consider adding a test for ModifyWithEditor re-confirmation flow.

Given the critical bug identified in coreToolScheduler.ts where processedConfirmations blocks re-confirmation after ModifyWithEditor, adding a test case would help validate the fix and prevent regression.

Would you like me to generate a test case that exercises the ModifyWithEditor flow to ensure the tool can be confirmed after modification?

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between da0e12a and 73f0fe2.

📒 Files selected for processing (3)
  • packages/core/src/core/coreToolScheduler.duplication.test.ts
  • packages/core/src/core/coreToolScheduler.test.ts
  • packages/core/src/core/coreToolScheduler.ts
🚧 Files skipped from review as they are similar to previous changes (1)
  • packages/core/src/core/coreToolScheduler.test.ts
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Don't use any - Always specify proper types. Use unknown if the type is truly unknown and add proper type guards.
Do not use console.log or console.debug - Use the sophisticated logging system instead. Log files are written to ~/.llxprt/debug/
Fix all linting errors, including warnings about any types

Files:

  • packages/core/src/core/coreToolScheduler.ts
  • packages/core/src/core/coreToolScheduler.duplication.test.ts
🧠 Learnings (3)
📓 Common learnings
Learnt from: e2720pjk
Repo: vybestack/llxprt-code PR: 583
File: packages/core/src/providers/openai/OpenAIProvider.ts:935-959
Timestamp: 2025-11-16T22:51:26.374Z
Learning: In the llxprt-code codebase (packages/core/src/providers/openai/OpenAIProvider.ts), tools like `run_shell_command` use internal streaming only for real-time UI updates during execution, but each tool execution produces exactly ONE final `ToolResponseBlock` containing the full result. The streaming chunks are never sent to the LLM and are not converted into multiple tool messages. The OpenAI Chat Completions API requires that each tool call (tool_call_id) corresponds to exactly one message with role 'tool', so duplicate tool response detection that removes subsequent tool messages with the same tool_call_id is correct and necessary for API compliance.
Learnt from: acoliver
Repo: vybestack/llxprt-code PR: 0
File: :0-0
Timestamp: 2025-12-26T14:17:41.983Z
Learning: In packages/core/src/core/coreToolScheduler.ts, the `publishBufferedResults()` method (line 1355) is called concurrently by multiple tool completion handlers (line 1510 in each tool's `.then()` callback) when parallel tools finish simultaneously. This causes a race condition where `nextPublishIndex` can be corrupted and tools can hang in 'executing' state forever, blocking the scheduler and causing subsequent tool calls to queue indefinitely. The method must be made reentrant-safe using a mutex/lock pattern to serialize concurrent calls.
📚 Learning: 2025-12-26T14:17:41.983Z
Learnt from: acoliver
Repo: vybestack/llxprt-code PR: 0
File: :0-0
Timestamp: 2025-12-26T14:17:41.983Z
Learning: In packages/core/src/core/coreToolScheduler.ts, the `publishBufferedResults()` method (line 1355) is called concurrently by multiple tool completion handlers (line 1510 in each tool's `.then()` callback) when parallel tools finish simultaneously. This causes a race condition where `nextPublishIndex` can be corrupted and tools can hang in 'executing' state forever, blocking the scheduler and causing subsequent tool calls to queue indefinitely. The method must be made reentrant-safe using a mutex/lock pattern to serialize concurrent calls.

Applied to files:

  • packages/core/src/core/coreToolScheduler.ts
  • packages/core/src/core/coreToolScheduler.duplication.test.ts
📚 Learning: 2025-11-16T22:51:26.374Z
Learnt from: e2720pjk
Repo: vybestack/llxprt-code PR: 583
File: packages/core/src/providers/openai/OpenAIProvider.ts:935-959
Timestamp: 2025-11-16T22:51:26.374Z
Learning: In the llxprt-code codebase (packages/core/src/providers/openai/OpenAIProvider.ts), tools like `run_shell_command` use internal streaming only for real-time UI updates during execution, but each tool execution produces exactly ONE final `ToolResponseBlock` containing the full result. The streaming chunks are never sent to the LLM and are not converted into multiple tool messages. The OpenAI Chat Completions API requires that each tool call (tool_call_id) corresponds to exactly one message with role 'tool', so duplicate tool response detection that removes subsequent tool messages with the same tool_call_id is correct and necessary for API compliance.

Applied to files:

  • packages/core/src/core/coreToolScheduler.duplication.test.ts
🧬 Code graph analysis (1)
packages/core/src/core/coreToolScheduler.duplication.test.ts (3)
packages/core/src/tools/tools.ts (3)
  • ToolResult (505-533)
  • ToolInvocation (34-74)
  • ToolConfirmationOutcome (27-27)
packages/core/src/core/coreToolScheduler.ts (4)
  • ToolCall (139-146)
  • CoreToolScheduler (387-1905)
  • WaitingToolCall (121-129)
  • CompletedToolCall (148-151)
packages/core/src/config/index.ts (1)
  • ApprovalMode (11-11)
⏰ Context from checks skipped due to timeout of 270000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Test (windows-latest, 24.x)
  • GitHub Check: Test (macos-latest, 24.x)
  • GitHub Check: Test (ubuntu-latest, 24.x)
  • GitHub Check: E2E Test (Linux) - sandbox:none
  • GitHub Check: E2E Test (macOS)
  • GitHub Check: Slow E2E - Win
  • GitHub Check: E2E Test (Linux) - sandbox:docker
🔇 Additional comments (9)
packages/core/src/core/coreToolScheduler.ts (3)

419-419: LGTM for the property declaration.

Using a Set<string> for tracking processed confirmations is appropriate for O(1) lookup and deduplication.


538-538: LGTM for cleanup in dispose.

Properly clearing processedConfirmations during disposal prevents memory leaks and stale state.


1842-1842: LGTM for cleanup in cancelAll.

Clearing processedConfirmations when the scheduler is reset ensures the scheduler can be reused without stale deduplication state.

packages/core/src/core/coreToolScheduler.duplication.test.ts (6)

1-25: LGTM for test setup and imports.

The file is well-documented with clear explanations of the bugs being tested. The imports are appropriate for the test scenarios.


45-75: LGTM for mock MessageBus implementation.

The mock correctly simulates the publish/subscribe pattern with proper cleanup via the returned unsubscribe function.


90-145: LGTM for ExecutionTrackingTool implementation.

Using a static counter for tracking execution count across test runs is a clean pattern. The resetCount() method ensures test isolation.


169-292: LGTM for duplicate confirmation prevention test.

The test correctly validates that calling handleConfirmationResponse twice with the same callId results in only one execution.


294-393: LGTM for message bus confirmation test.

The test correctly validates the message bus confirmation flow with proper correlationId handling.


395-509: LGTM for multiple schedulers test.

This test effectively validates that when multiple schedulers subscribe to the same MessageBus, only the owning scheduler processes the confirmation while others silently ignore it.

Add processedConfirmations Set to CoreToolScheduler to track which
callIds have already been confirmed. This prevents tools from executing
multiple times when handleConfirmationResponse is called from multiple
paths (IDE confirmation, wrapped onConfirm callback, MessageBus).

The fix adds:
- processedConfirmations Set field to track processed callIds
- Early return guard in handleConfirmationResponse
- Cleanup in dispose/clear methods
- Comprehensive test coverage for duplication scenarios

This addresses the issue where tools in non-YOLO mode could execute
before user confirmation then execute again after confirmation due to
multiple handlers triggering handleConfirmationResponse.
@acoliver
Copy link
Collaborator Author

acoliver commented Jan 9, 2026

So @CodeRabbit aside from the borked test did it fix it? Also the deduplication seems like papering over a hole rather than fixing it. I think it was from a wrong theory of the problem. Should we keep that?

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 9, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain


🏁 Script executed:

#!/bin/bash

echo "=== Check for test failures or issues in new tests ==="
echo "Files added in this PR with tests:"
echo "- useGeminiStream.dedup.test.tsx"
echo "- coreToolScheduler.duplication.test.ts"
echo ""

echo "=== Look for issues in useGeminiStream.dedup.test.tsx ==="
cat packages/cli/src/ui/hooks/useGeminiStream.dedup.test.tsx | head -100

echo -e "\n=== Check how Gemini provider stream generates ToolCallRequest events ==="
fd -e ts -e tsx GeminiProvider | head -5 | xargs -I {} sh -c 'echo "=== File: {} ==="; grep -n "ToolCallRequest" {} | head -20'

echo -e "\n=== Search for where ToolCallRequest events are created/emitted in Gemini code ==="
rg -nP -C5 "ToolCallRequest.*callId|new ToolCallRequest" packages/cli/src/ packages/core/src/providers/ | head -100

echo -e "\n=== Check if there's any streaming logic that might emit duplicates ==="
rg -nP -C5 "functionCall|tool_call" packages/core/src/providers/gemini/ | head -150

echo -e "\n=== Look for any TODO or FIXME comments related to duplication ==="
rg -nP "TODO|FIXME" packages/cli/src/ui/hooks/useGeminiStream.ts packages/core/src/core/coreToolScheduler.ts

Length of output: 19459


🏁 Script executed:

#!/bin/bash

echo "=== Check processGeminiStreamEvents implementation for duplicate emission logic ==="
sed -n '/function processGeminiStreamEvents/,/^}/p' packages/cli/src/ui/hooks/useGeminiStream.ts | head -200

echo -e "\n=== Check if Gemini stream itself can produce duplicate chunks ==="
fd GeminiClient | head -3 | xargs -I {} sh -c 'echo "=== File: {} ==="; cat {}'

echo -e "\n=== Look for stream event accumulation that might cause duplication ==="
rg -nP -C10 "accumulatedToolCalls|pendingToolCalls" packages/cli/src/ui/hooks/useGeminiStream.ts

echo -e "\n=== Check if there's documentation about why dedup was needed ==="
cat packages/cli/src/ui/hooks/useGeminiStream.ts | grep -B5 -A5 "seenCallIds\|deduplicate\|duplicate"

Length of output: 1016


🏁 Script executed:

#!/bin/bash

echo "=== Look at the actual issue #1040 description if available ==="
gh issue view 1040 --json title,body,comments --jq '.title, .body, .comments[].body' 2>/dev/null || echo "Could not fetch issue details"

echo -e "\n=== Check commit messages in this PR for context ==="
git log --oneline origin/main..HEAD 2>/dev/null || echo "Git log not available in sandbox"

echo -e "\n=== Look for any error patterns related to duplicate execution ==="
rg -nP "File exists|already exists|duplicate" packages/cli/src/ui/hooks/useGeminiStream.ts packages/core/src/core/coreToolScheduler.ts

Length of output: 262997


@acoliver Short answer:

  • Did it fix it? Partially. It eliminates duplicate scheduling for the same callId and prevents duplicate confirmation handling, but it does not fix “execution before approval” in DEFAULT (non‑YOLO) mode. That timing bug is still present.
  • Is the dedup just papering over a hole? It’s defensive, not the root fix. I recommend keeping it. Providers can repeat the same tool_call_id across frames, and we must ensure one execution per callId. The confirmation dedup is also correct belt-and‑suspenders.

What still needs fixing (root cause)

  • CoreToolScheduler should not trigger attemptExecutionOfScheduledCalls() for requests that require confirmation. Only handleConfirmationResponse (ProceedOnce/Always) should transition those callIds to scheduled/executing.
  • Also guard against re-scheduling of an already-seen callId at the scheduler boundary, not only in the UI.

Proposed minimal core changes

  1. Gate execution on explicit approval:
// packages/core/src/core/coreToolScheduler.ts
// inside schedule(requests, signal)
for (const req of requests) {
  const decision = await this.policyEngine.shouldConfirmExecute(req);
  if (decision === ToolConfirmationOutcome.RequireConfirmation) {
    this.setStatusInternal(req.callId, 'awaiting_approval', { confirmationDetails: /* … */ });
    continue; // DO NOT enqueue/attempt execution yet
  }
  // auto-approved paths (YOLO / allowedTools / no confirmation needed)
  this.enqueueForExecution(req);
}
// Only call attemptExecutionOfScheduledCalls() if at least one was enqueued above
if (this.hasEnqueuedWork()) {
  await this.attemptExecutionOfScheduledCalls();
}

// in handleConfirmationResponse(...)
if (!this.processedConfirmations.has(callId)) {
  this.processedConfirmations.add(callId);
  this.approveToolCall(callId);       // sets status -> 'scheduled' and enqueues
  await this.attemptExecutionOfScheduledCalls();
}
  1. Centralize “don’t re-schedule the same callId” in the scheduler:
// class CoreToolScheduler
private seenCallIds = new Set<string>();

// at the top of schedule()
const fresh = requests.filter(r => !this.seenCallIds.has(r.callId));
for (const r of fresh) this.seenCallIds.add(r.callId);
if (fresh.length === 0) return /* no-op */;

// use `fresh` for all logic from here

Remember to clear seenCallIds in dispose()/cancelAll().

Keep or remove the CLI dedup?

  • Keep it. It’s O(1) and prevents UI-layer double submits when the provider repeats the same tool_call_id across stream frames. Together with the scheduler-side guard, this gives us layered protection without hiding the core timing bug.

Test coverage to add

  • “Does not execute before approval”:
    • Arrange: DEFAULT approval mode; schedule one tool call.
    • Assert: status transitions to awaiting_approval; tool.execute not called.
    • Act: call handleConfirmationResponse(ProceedOnce).
    • Assert: exactly one execution; final status success.
  • “Ignores re-scheduling of same callId at scheduler boundary.”

If you want, I can draft the exact patch and a companion test file to drop into packages/core/src/core.

Two small follow‑ups

  • Which test is “borked”? Point me to the filename and failure so I can fix it quickly.
  • Re: “there should be only 1” scheduler: I agree with consolidating to a single CoreToolScheduler per session. I can open a follow‑up issue and outline the migration (share one scheduler via session scope + ensure proper dispose on unmount). Say the word and I’ll file it.

✏️ Learnings added
Learnt from: acoliver
URL: https://github.com/vybestack/llxprt-code/pull/1044

Timestamp: 2026-01-09T02:26:33.941Z
Learning: Preference: Consolidate to a single CoreToolScheduler instance per interactive session to avoid duplicated handlers and unknown correlationId noise.

Note: Learnings are effective only in the context of similar code segments. To apply general coding guidelines, please configure review instructions. You can manage existing learnings in the app.


🧠 Learnings used
Learnt from: acoliver
Repo: vybestack/llxprt-code PR: 0
File: :0-0
Timestamp: 2025-12-26T14:17:41.983Z
Learning: In packages/core/src/core/coreToolScheduler.ts, the `publishBufferedResults()` method (line 1355) is called concurrently by multiple tool completion handlers (line 1510 in each tool's `.then()` callback) when parallel tools finish simultaneously. This causes a race condition where `nextPublishIndex` can be corrupted and tools can hang in 'executing' state forever, blocking the scheduler and causing subsequent tool calls to queue indefinitely. The method must be made reentrant-safe using a mutex/lock pattern to serialize concurrent calls.

Learnt from: e2720pjk
Repo: vybestack/llxprt-code PR: 583
File: packages/core/src/providers/openai/OpenAIProvider.ts:935-959
Timestamp: 2025-11-16T22:51:26.374Z
Learning: In the llxprt-code codebase (packages/core/src/providers/openai/OpenAIProvider.ts), tools like `run_shell_command` use internal streaming only for real-time UI updates during execution, but each tool execution produces exactly ONE final `ToolResponseBlock` containing the full result. The streaming chunks are never sent to the LLM and are not converted into multiple tool messages. The OpenAI Chat Completions API requires that each tool call (tool_call_id) corresponds to exactly one message with role 'tool', so duplicate tool response detection that removes subsequent tool messages with the same tool_call_id is correct and necessary for API compliance.

Use DebugLogger.resetForTesting() in afterEach to dispose FileOutput
singleton's recurring timer before clearing fake timers. This prevents
vi.runAllTimersAsync() from advancing the recurring flush timer which
caused an infinite loop detected by Vitest after 10000 timer executions.

Add DebugLogger to proper imports to avoid require() lint errors.
Follows the pattern used in packages/core/src/debug/DebugLogger.test.ts
where DebugLogger.resetForTesting() is called in test cleanup.
@acoliver
Copy link
Collaborator Author

acoliver commented Jan 9, 2026

@CodeRabbit, does this now address the approval mode issue? (where it executed both before and after approval) ?

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
packages/core/src/core/coreToolScheduler.ts (1)

1119-1141: processedConfirmations is “sticky” even if confirmation handling throws (can wedge approvals).
this.processedConfirmations.add(callId) happens before await originalOnConfirm(outcome). If originalOnConfirm throws, subsequent retries will be skipped forever.

Proposed fix (only mark processed after success, or rollback on failure)
-    this.processedConfirmations.add(callId);
+    this.processedConfirmations.add(callId);

     const previousCorrelationId =
       waitingToolCall?.confirmationDetails?.correlationId;

-    await originalOnConfirm(outcome);
+    try {
+      await originalOnConfirm(outcome);
+    } catch (e) {
+      // allow retry
+      this.processedConfirmations.delete(callId);
+      throw e;
+    }
🤖 Fix all issues with AI agents
In @packages/core/src/core/coreToolScheduler.duplication.test.ts:
- Around line 90-145: The test tool fails to compile because the declarative
tool and its invocation aren't calling their required super() constructors and
some method signatures/visibility don't match the base types: add a constructor
on ExecutionTrackingTool that calls super(...) with the appropriate arguments
expected by BaseDeclarativeTool (e.g., tool id/name/displayName), mark
createInvocation with the override keyword and correct public signature, change
ExecutionTrackingToolInvocation's constructor to call super(tool, params) (pass
the tool instance as the first arg) and ensure shouldConfirmExecute and execute
use the exact override signatures/return types from BaseToolInvocation
(including the correct ToolConfirmation/ToolConfirmationOutcome types and
visibility), and update any onConfirm signature to accept a
ToolConfirmationOutcome and return Promise<void> so the tool can be built and
executed by CoreToolScheduler.

In @packages/core/src/core/coreToolScheduler.ts:
- Around line 889-904: The scheduler currently only filters against
this.seenCallIds after checking all requests, so duplicate callIds inside the
same schedule() batch (e.g., two requests with callId 'x') both pass; change the
filtering to dedupe within the incoming batch first and avoid mutating the
original input: create a local Set (e.g., batchCallIds) and filter
requestsToProcess to only keep the first occurrence of each callId and also
exclude any already in this.seenCallIds, then add the retained callIds to
this.seenCallIds; update references to freshRequests/requestsToProcessActual
accordingly so you work with the new deduped array.
🧹 Nitpick comments (3)
packages/core/src/core/coreToolScheduler.ts (1)

419-421: seenCallIds/processedConfirmations grow unbounded across a session; consider completion-time cleanup.
Clearing only in dispose()/cancelAll() can leak memory in long interactive sessions and can also block legitimate callId reuse.

Suggestion: in checkAndNotifyCompletion() (Line 1768+), after determining completedCalls, delete their callIds from this.seenCallIds and this.processedConfirmations (and/or add a bounded LRU/TTL). Based on learnings, long-running sessions are a priority.

Also applies to: 534-542, 1768-1792

packages/core/src/core/coreToolScheduler.test.ts (1)

2783-2898: Strengthen the test: assert the tool executes exactly once.
Right now it only asserts final status/callId; it should also assert mockTool.executeFn is called once to guard against “double-run but last-write-wins”.

Proposed assertion additions
     // Wait for completion
     await schedulePromise;
     await vi.waitFor(() => {
       expect(onAllToolCallsComplete).toHaveBeenCalled();
     });

+    expect(mockTool.executeFn).toHaveBeenCalledTimes(1);
+
     // Verify the tool executed only once (status is success)
     const completedCalls = onAllToolCallsComplete.mock
       .calls[0][0] as ToolCall[];
packages/core/src/core/coreToolScheduler.duplication.test.ts (1)

150-166: Prefer vi.waitFor over custom polling loops.
The manual loop can add flakiness and slows CI under load.

📜 Review details

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 73f0fe2 and 2f9b4a0.

📒 Files selected for processing (4)
  • packages/cli/src/ui/hooks/useGeminiStream.dedup.test.tsx
  • packages/core/src/core/coreToolScheduler.duplication.test.ts
  • packages/core/src/core/coreToolScheduler.test.ts
  • packages/core/src/core/coreToolScheduler.ts
🧰 Additional context used
📓 Path-based instructions (1)
**/*.{ts,tsx}

📄 CodeRabbit inference engine (CLAUDE.md)

**/*.{ts,tsx}: Don't use any - Always specify proper types. Use unknown if the type is truly unknown and add proper type guards.
Do not use console.log or console.debug - Use the sophisticated logging system instead. Log files are written to ~/.llxprt/debug/
Fix all linting errors, including warnings about any types

Files:

  • packages/core/src/core/coreToolScheduler.test.ts
  • packages/core/src/core/coreToolScheduler.ts
  • packages/core/src/core/coreToolScheduler.duplication.test.ts
  • packages/cli/src/ui/hooks/useGeminiStream.dedup.test.tsx
🧠 Learnings (4)
📓 Common learnings
Learnt from: e2720pjk
Repo: vybestack/llxprt-code PR: 583
File: packages/core/src/providers/openai/OpenAIProvider.ts:935-959
Timestamp: 2025-11-16T22:51:26.374Z
Learning: In the llxprt-code codebase (packages/core/src/providers/openai/OpenAIProvider.ts), tools like `run_shell_command` use internal streaming only for real-time UI updates during execution, but each tool execution produces exactly ONE final `ToolResponseBlock` containing the full result. The streaming chunks are never sent to the LLM and are not converted into multiple tool messages. The OpenAI Chat Completions API requires that each tool call (tool_call_id) corresponds to exactly one message with role 'tool', so duplicate tool response detection that removes subsequent tool messages with the same tool_call_id is correct and necessary for API compliance.
Learnt from: acoliver
Repo: vybestack/llxprt-code PR: 0
File: :0-0
Timestamp: 2025-12-26T14:17:41.983Z
Learning: In packages/core/src/core/coreToolScheduler.ts, the `publishBufferedResults()` method (line 1355) is called concurrently by multiple tool completion handlers (line 1510 in each tool's `.then()` callback) when parallel tools finish simultaneously. This causes a race condition where `nextPublishIndex` can be corrupted and tools can hang in 'executing' state forever, blocking the scheduler and causing subsequent tool calls to queue indefinitely. The method must be made reentrant-safe using a mutex/lock pattern to serialize concurrent calls.
Learnt from: acoliver
Repo: vybestack/llxprt-code PR: 0
File: :0-0
Timestamp: 2026-01-09T02:26:33.951Z
Learning: Preference: Consolidate to a single CoreToolScheduler instance per interactive session to avoid duplicated handlers and unknown correlationId noise.
📚 Learning: 2025-12-26T14:17:41.983Z
Learnt from: acoliver
Repo: vybestack/llxprt-code PR: 0
File: :0-0
Timestamp: 2025-12-26T14:17:41.983Z
Learning: In packages/core/src/core/coreToolScheduler.ts, the `publishBufferedResults()` method (line 1355) is called concurrently by multiple tool completion handlers (line 1510 in each tool's `.then()` callback) when parallel tools finish simultaneously. This causes a race condition where `nextPublishIndex` can be corrupted and tools can hang in 'executing' state forever, blocking the scheduler and causing subsequent tool calls to queue indefinitely. The method must be made reentrant-safe using a mutex/lock pattern to serialize concurrent calls.

Applied to files:

  • packages/core/src/core/coreToolScheduler.test.ts
  • packages/core/src/core/coreToolScheduler.ts
  • packages/core/src/core/coreToolScheduler.duplication.test.ts
  • packages/cli/src/ui/hooks/useGeminiStream.dedup.test.tsx
📚 Learning: 2026-01-09T02:26:33.951Z
Learnt from: acoliver
Repo: vybestack/llxprt-code PR: 0
File: :0-0
Timestamp: 2026-01-09T02:26:33.951Z
Learning: Preference: Consolidate to a single CoreToolScheduler instance per interactive session to avoid duplicated handlers and unknown correlationId noise.

Applied to files:

  • packages/core/src/core/coreToolScheduler.test.ts
  • packages/core/src/core/coreToolScheduler.ts
  • packages/core/src/core/coreToolScheduler.duplication.test.ts
📚 Learning: 2025-11-16T22:51:26.374Z
Learnt from: e2720pjk
Repo: vybestack/llxprt-code PR: 583
File: packages/core/src/providers/openai/OpenAIProvider.ts:935-959
Timestamp: 2025-11-16T22:51:26.374Z
Learning: In the llxprt-code codebase (packages/core/src/providers/openai/OpenAIProvider.ts), tools like `run_shell_command` use internal streaming only for real-time UI updates during execution, but each tool execution produces exactly ONE final `ToolResponseBlock` containing the full result. The streaming chunks are never sent to the LLM and are not converted into multiple tool messages. The OpenAI Chat Completions API requires that each tool call (tool_call_id) corresponds to exactly one message with role 'tool', so duplicate tool response detection that removes subsequent tool messages with the same tool_call_id is correct and necessary for API compliance.

Applied to files:

  • packages/core/src/core/coreToolScheduler.test.ts
  • packages/core/src/core/coreToolScheduler.ts
  • packages/core/src/core/coreToolScheduler.duplication.test.ts
  • packages/cli/src/ui/hooks/useGeminiStream.dedup.test.tsx
🧬 Code graph analysis (4)
packages/core/src/core/coreToolScheduler.test.ts (1)
packages/core/src/core/coreToolScheduler.ts (1)
  • CoreToolScheduler (387-1927)
packages/core/src/core/coreToolScheduler.ts (1)
packages/core/src/core/toolGovernance.ts (1)
  • buildToolGovernance (54-82)
packages/core/src/core/coreToolScheduler.duplication.test.ts (1)
packages/core/src/core/coreToolScheduler.ts (3)
  • ToolCall (139-146)
  • WaitingToolCall (121-129)
  • CompletedToolCall (148-151)
packages/cli/src/ui/hooks/useGeminiStream.dedup.test.tsx (2)
packages/core/src/core/turn.ts (1)
  • ToolCallRequestInfo (101-108)
packages/cli/src/ui/hooks/useGeminiStream.ts (1)
  • useGeminiStream (134-1535)
⏰ Context from checks skipped due to timeout of 270000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (7)
  • GitHub Check: Test (ubuntu-latest, 24.x)
  • GitHub Check: Test (macos-latest, 24.x)
  • GitHub Check: Test (windows-latest, 24.x)
  • GitHub Check: E2E Test (Linux) - sandbox:none
  • GitHub Check: E2E Test (Linux) - sandbox:docker
  • GitHub Check: Slow E2E - Win
  • GitHub Check: E2E Test (macOS)
🔇 Additional comments (7)
packages/cli/src/ui/hooks/useGeminiStream.dedup.test.tsx (5)

1-29: LGTM!

The license header and imports are appropriate. The test file imports the necessary testing utilities from Vitest and React Testing Library, along with the required types and modules from the project.


64-86: Well-designed mock for capturing scheduled tool calls.

The useReactToolScheduler mock correctly captures scheduled tool calls into the module-level array for later assertions. The reset in beforeEach (line 105) ensures proper test isolation.


196-210: Cleanup logic is reasonable.

The try/catch pattern around DebugLogger.resetForTesting() is acceptable given the comment explains it may not be available. The timer and mock cleanup is properly sequenced.


212-242: Excellent test coverage for the core deduplication scenario.

This test directly validates the fix for issue #1040 by emitting duplicate ToolCallRequest events with the same callId and verifying that only one tool call is scheduled. The assertions correctly check both the number of scheduling calls and the individual request count.


244-309: Good complementary test ensuring distinct tool calls are preserved.

This test validates that the deduplication logic doesn't over-aggressively filter tool calls with different callId values. It correctly verifies that two distinct tool calls are scheduled together in a single batch, which is essential for validating that the fix doesn't break legitimate multi-tool-call scenarios.

packages/core/src/core/coreToolScheduler.ts (1)

1181-1192: ModifyWithEditor correctly re-allows confirmation; make sure stale correlation cleanup can’t leak.
Deleting processedConfirmations on ModifyWithEditor is the right shape; just ensure stale correlationIds are always cleared on dispose/cancel paths (looks covered).

packages/core/src/core/coreToolScheduler.duplication.test.ts (1)

512-623: Good regression test idea (DEFAULT must not execute before approval), but it depends on fixing the test tool wiring.
Once the ExecutionTrackingTool is corrected, this is a strong guardrail for the remaining “executes before approval” bug report.

Please re-run the full vitest suite after adjusting the BaseDeclarativeTool/BaseToolInvocation overrides above to confirm this test fails on the pre-fix behavior and passes with the intended gating.

Comment on lines +90 to +145
class ExecutionTrackingTool extends BaseDeclarativeTool<
{ id: string },
ToolResult
> {
displayName = 'ExecutionTrackingTool';
static executionCount = 0;

createInvocation(params: {
id: string;
}): ToolInvocation<{ id: string }, ToolResult> {
return new ExecutionTrackingToolInvocation(this, params);
}

static resetCount() {
ExecutionTrackingTool.executionCount = 0;
}
}

class ExecutionTrackingToolInvocation extends BaseToolInvocation<
{ id: string },
ToolResult
> {
constructor(_tool: ExecutionTrackingTool, params: { id: string }) {
super(params);
}

override async shouldConfirmExecute(): Promise<{
type: 'exec';
title: string;
description: string;
command: string;
rootCommand: string;
onConfirm: (outcome: ToolConfirmationOutcome) => Promise<void>;
}> {
return {
type: 'exec',
title: 'Test Tool',
description: 'A tool for testing',
command: 'test command',
rootCommand: 'test',
onConfirm: async () => {},
};
}

async execute(): Promise<ToolResult> {
ExecutionTrackingTool.executionCount++;
return {
llmContent: `Tool executed (count: ${ExecutionTrackingTool.executionCount})`,
returnDisplay: `Success (count: ${ExecutionTrackingTool.executionCount})`,
};
}

getDescription(): string {
return 'A tool that tracks execution count';
}
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Test tool is likely broken (missing BaseDeclarativeTool super() + override signature/visibility mismatches).
This looks like it won’t compile and/or won’t execute via tool.build() correctly.

Proposed fix (make tool/invocation compatible with CoreToolScheduler expectations)
 class ExecutionTrackingTool extends BaseDeclarativeTool<
   { id: string },
   ToolResult
 > {
-  displayName = 'ExecutionTrackingTool';
   static executionCount = 0;
 
-  createInvocation(params: {
-    id: string;
-  }): ToolInvocation<{ id: string }, ToolResult> {
-    return new ExecutionTrackingToolInvocation(this, params);
-  }
+  constructor() {
+    super(
+      'ExecutionTrackingTool',
+      'ExecutionTrackingTool',
+      'A tool that tracks execution count',
+      // Pick the most appropriate Kind for your project; Other is usually safe in tests.
+      // If Kind is required here, import it from ../index.js.
+      // @ts-expect-error adjust Kind import if needed
+      Kind.Other,
+      { type: 'object', properties: { id: { type: 'string' } }, required: ['id'] },
+    );
+  }
+
+  protected override createInvocation(params: { id: string }): ToolInvocation<
+    { id: string },
+    ToolResult
+  > {
+    return new ExecutionTrackingToolInvocation(params);
+  }
 
   static resetCount() {
     ExecutionTrackingTool.executionCount = 0;
   }
 }
 
 class ExecutionTrackingToolInvocation extends BaseToolInvocation<
   { id: string },
   ToolResult
 > {
-  constructor(_tool: ExecutionTrackingTool, params: { id: string }) {
-    super(params);
-  }
+  constructor(params: { id: string }) {
+    super(params);
+  }
 
-  override async shouldConfirmExecute(): Promise<{
+  override async shouldConfirmExecute(_signal: AbortSignal): Promise<{
     type: 'exec';
     title: string;
     description: string;
     command: string;
     rootCommand: string;
     onConfirm: (outcome: ToolConfirmationOutcome) => Promise<void>;
   }> {
     return {
       type: 'exec',
       title: 'Test Tool',
       description: 'A tool for testing',
       command: 'test command',
       rootCommand: 'test',
       onConfirm: async () => {},
     };
   }
 
-  async execute(): Promise<ToolResult> {
+  async execute(_signal: AbortSignal): Promise<ToolResult> {
     ExecutionTrackingTool.executionCount++;
     return {
       llmContent: `Tool executed (count: ${ExecutionTrackingTool.executionCount})`,
       returnDisplay: `Success (count: ${ExecutionTrackingTool.executionCount})`,
     };
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
class ExecutionTrackingTool extends BaseDeclarativeTool<
{ id: string },
ToolResult
> {
displayName = 'ExecutionTrackingTool';
static executionCount = 0;
createInvocation(params: {
id: string;
}): ToolInvocation<{ id: string }, ToolResult> {
return new ExecutionTrackingToolInvocation(this, params);
}
static resetCount() {
ExecutionTrackingTool.executionCount = 0;
}
}
class ExecutionTrackingToolInvocation extends BaseToolInvocation<
{ id: string },
ToolResult
> {
constructor(_tool: ExecutionTrackingTool, params: { id: string }) {
super(params);
}
override async shouldConfirmExecute(): Promise<{
type: 'exec';
title: string;
description: string;
command: string;
rootCommand: string;
onConfirm: (outcome: ToolConfirmationOutcome) => Promise<void>;
}> {
return {
type: 'exec',
title: 'Test Tool',
description: 'A tool for testing',
command: 'test command',
rootCommand: 'test',
onConfirm: async () => {},
};
}
async execute(): Promise<ToolResult> {
ExecutionTrackingTool.executionCount++;
return {
llmContent: `Tool executed (count: ${ExecutionTrackingTool.executionCount})`,
returnDisplay: `Success (count: ${ExecutionTrackingTool.executionCount})`,
};
}
getDescription(): string {
return 'A tool that tracks execution count';
}
}
class ExecutionTrackingTool extends BaseDeclarativeTool<
{ id: string },
ToolResult
> {
static executionCount = 0;
constructor() {
super(
'ExecutionTrackingTool',
'ExecutionTrackingTool',
'A tool that tracks execution count',
// Pick the most appropriate Kind for your project; Other is usually safe in tests.
// If Kind is required here, import it from ../index.js.
// @ts-expect-error adjust Kind import if needed
Kind.Other,
{ type: 'object', properties: { id: { type: 'string' } }, required: ['id'] },
);
}
protected override createInvocation(params: { id: string }): ToolInvocation<
{ id: string },
ToolResult
> {
return new ExecutionTrackingToolInvocation(params);
}
static resetCount() {
ExecutionTrackingTool.executionCount = 0;
}
}
class ExecutionTrackingToolInvocation extends BaseToolInvocation<
{ id: string },
ToolResult
> {
constructor(params: { id: string }) {
super(params);
}
override async shouldConfirmExecute(_signal: AbortSignal): Promise<{
type: 'exec';
title: string;
description: string;
command: string;
rootCommand: string;
onConfirm: (outcome: ToolConfirmationOutcome) => Promise<void>;
}> {
return {
type: 'exec',
title: 'Test Tool',
description: 'A tool for testing',
command: 'test command',
rootCommand: 'test',
onConfirm: async () => {},
};
}
async execute(_signal: AbortSignal): Promise<ToolResult> {
ExecutionTrackingTool.executionCount++;
return {
llmContent: `Tool executed (count: ${ExecutionTrackingTool.executionCount})`,
returnDisplay: `Success (count: ${ExecutionTrackingTool.executionCount})`,
};
}
getDescription(): string {
return 'A tool that tracks execution count';
}
}
🤖 Prompt for AI Agents
In @packages/core/src/core/coreToolScheduler.duplication.test.ts around lines 90
- 145, The test tool fails to compile because the declarative tool and its
invocation aren't calling their required super() constructors and some method
signatures/visibility don't match the base types: add a constructor on
ExecutionTrackingTool that calls super(...) with the appropriate arguments
expected by BaseDeclarativeTool (e.g., tool id/name/displayName), mark
createInvocation with the override keyword and correct public signature, change
ExecutionTrackingToolInvocation's constructor to call super(tool, params) (pass
the tool instance as the first arg) and ensure shouldConfirmExecute and execute
use the exact override signatures/return types from BaseToolInvocation
(including the correct ToolConfirmation/ToolConfirmationOutcome types and
visibility), and update any onConfirm signature to accept a
ToolConfirmationOutcome and return Promise<void> so the tool can be built and
executed by CoreToolScheduler.

Comment on lines +889 to 904

// Filter out duplicate calls at the scheduler boundary to prevent duplicate execution
const freshRequests = requestsToProcess.filter(
(r) => !this.seenCallIds.has(r.callId),
);
for (const req of freshRequests) {
this.seenCallIds.add(req.callId);
}
if (freshRequests.length === 0) {
// All calls were duplicates, nothing to do
return;
}

// Use only fresh requests for all subsequent processing
const requestsToProcessActual = freshRequests;
const governance = buildToolGovernance(this.config);
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

Scheduler-boundary dedup misses duplicates within the same batch (can still double-execute).
Right now freshRequests only checks this.seenCallIds (updated after filtering), so [ {callId:'x'}, {callId:'x'} ] in a single schedule() call will both pass and execute.

Proposed fix (dedupe within-batch + avoid mutating inputs)
-      const requestsToProcess = (
-        Array.isArray(request) ? request : [request]
-      ).map((req) => {
-        if (!req.agentId) {
-          req.agentId = DEFAULT_AGENT_ID;
-        }
-        return req;
-      });
+      const requestsToProcess = (Array.isArray(request) ? request : [request]).map(
+        (req) => ({
+          ...req,
+          agentId: req.agentId ?? DEFAULT_AGENT_ID,
+        }),
+      );

-      const freshRequests = requestsToProcess.filter(
-        (r) => !this.seenCallIds.has(r.callId),
-      );
-      for (const req of freshRequests) {
-        this.seenCallIds.add(req.callId);
-      }
+      const batchSeen = new Set<string>();
+      const freshRequests: ToolCallRequestInfo[] = [];
+      for (const req of requestsToProcess) {
+        if (batchSeen.has(req.callId) || this.seenCallIds.has(req.callId)) continue;
+        batchSeen.add(req.callId);
+        this.seenCallIds.add(req.callId);
+        freshRequests.push(req);
+      }

Committable suggestion skipped: line range outside the PR's diff.

🤖 Prompt for AI Agents
In @packages/core/src/core/coreToolScheduler.ts around lines 889 - 904, The
scheduler currently only filters against this.seenCallIds after checking all
requests, so duplicate callIds inside the same schedule() batch (e.g., two
requests with callId 'x') both pass; change the filtering to dedupe within the
incoming batch first and avoid mutating the original input: create a local Set
(e.g., batchCallIds) and filter requestsToProcess to only keep the first
occurrence of each callId and also exclude any already in this.seenCallIds, then
add the retained callIds to this.seenCallIds; update references to
freshRequests/requestsToProcessActual accordingly so you work with the new
deduped array.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 9, 2026

Tip

For best results, initiate chat on the files or code changes.

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash

echo "=== Check how auto-approval works in schedule() method ==="
sed -n '/public async schedule(/,/^  }/p' packages/core/src/core/coreToolScheduler.ts | head -200

echo -e "\n=== Check handleConfirmationResponse with processedConfirmations logic ==="
sed -n '/private async handleConfirmationResponse/,/^  }/p' packages/core/src/core/coreToolScheduler.ts

echo -e "\n=== Check if auto-approval calls handleConfirmationResponse or bypasses it ==="
rg -nP -C5 'approveToolCall|ProceedOnce|auto.*approv' packages/core/src/core/coreToolScheduler.ts | head -100

echo -e "\n=== Check where processedConfirmations is populated ==="
rg -nP 'processedConfirmations\.add' packages/core/src/core/coreToolScheduler.ts

echo -e "\n=== Look for the validation outcome handling in schedule ==="
rg -nP -B3 -A10 'shouldConfirmExecute|ToolConfirmationOutcome' packages/core/src/core/coreToolScheduler.ts | head -80

Length of output: 5125


🏁 Script executed:

#!/bin/bash

echo "=== Check the full approveToolCall implementation ==="
rg -nP -A10 'private approveToolCall\(' packages/core/src/core/coreToolScheduler.ts

echo -e "\n=== Check where attemptExecutionOfScheduledCalls is called ==="
rg -nP 'attemptExecutionOfScheduledCalls' packages/core/src/core/coreToolScheduler.ts

echo -e "\n=== See if there's any other dedup mechanism for execution ==="
rg -nP 'seenCallIds|executedCallIds|hasExecuted' packages/core/src/core/coreToolScheduler.ts

echo -e "\n=== Check what happens after schedule() completes ==="
sed -n '/public async schedule(/,/^  }/p' packages/core/src/core/coreToolScheduler.ts | tail -50

echo -e "\n=== Look at the test that was added for duplicate confirmation handling ==="
rg -nP -B5 -A30 'duplicate confirmation|handleConfirmationResponse.*twice' packages/core/src/core/coreToolScheduler.test.ts

Length of output: 3912


🏁 Script executed:

#!/bin/bash

echo "=== Read the duplication test file to understand the bug scenarios ==="
cat packages/core/src/core/coreToolScheduler.duplication.test.ts | head -300

echo -e "\n=== Check if there's any logic that prevents execution when status is already executing/success ==="
rg -nP -B3 -A5 "status.*===.*'executing'|status.*===.*'success'" packages/core/src/core/coreToolScheduler.ts | head -50

echo -e "\n=== Check the setStatusInternal to see if it has any guards ==="
rg -nP -A15 'private setStatusInternal' packages/core/src/core/coreToolScheduler.ts | head -40

Length of output: 11993


@acoliver No, this PR does not fully address the "execute before and after approval" issue you reported. Let me explain what it does and doesn't fix:

What this PR fixes ✅

Duplicate confirmation handling: The processedConfirmations Set prevents handleConfirmationResponse from being called twice with the same callId, ensuring only one execution when a user confirms multiple times.

What this PR does NOT fix ❌

Execute BEFORE approval: The root cause—tools auto-executing before user confirmation in non-YOLO mode—is not addressed by this PR.

Here's why:

Looking at the approval flow in CoreToolScheduler.schedule(), when a tool call arrives:

  1. Auto-approval can trigger via approveToolCall() for YOLO mode, allowed tools, or when shouldConfirmExecute() returns null
  2. approveToolCall() immediately sets status to 'scheduled'
  3. attemptExecutionOfScheduledCalls() is called, causing immediate execution

This happens before the UI displays the confirmation prompt.

The processedConfirmations guard only prevents duplicate handling inside handleConfirmationResponse. It doesn't prevent the initial auto-approval path from executing the tool prematurely.

Why didn't it execute twice?

You might wonder: if it executed before approval, why didn't it execute again after confirmation?

Answer: setStatusInternal has guards that prevent transitions FROM terminal states (success, error, cancelled). So when handleConfirmationResponse is later called, it can't transition the already-completed tool back to 'scheduled'.

These guards already existed and prevented the "after" execution—the PR didn't need to fix that part.

What needs fixing

The real bug is in the auto-approval logic. Tools that should wait for confirmation are being auto-approved. You'd need to:

  1. Fix the conditional logic at lines 1002, 1015, 1024 that determines when to call approveToolCall()
  2. Ensure tools in DEFAULT mode that require confirmation don't execute until handleConfirmationResponse is explicitly called
  3. Possibly add guards to prevent attemptExecutionOfScheduledCalls() from running on tools in 'awaiting_approval' state

Would you like me to investigate the auto-approval logic more deeply to identify why tools requiring confirmation are being auto-approved?


🧠 Learnings used
Learnt from: e2720pjk
Repo: vybestack/llxprt-code PR: 583
File: packages/core/src/providers/openai/OpenAIProvider.ts:935-959
Timestamp: 2025-11-16T22:51:26.374Z
Learning: In the llxprt-code codebase (packages/core/src/providers/openai/OpenAIProvider.ts), tools like `run_shell_command` use internal streaming only for real-time UI updates during execution, but each tool execution produces exactly ONE final `ToolResponseBlock` containing the full result. The streaming chunks are never sent to the LLM and are not converted into multiple tool messages. The OpenAI Chat Completions API requires that each tool call (tool_call_id) corresponds to exactly one message with role 'tool', so duplicate tool response detection that removes subsequent tool messages with the same tool_call_id is correct and necessary for API compliance.

Learnt from: acoliver
Repo: vybestack/llxprt-code PR: 0
File: :0-0
Timestamp: 2025-12-26T14:17:41.983Z
Learning: In packages/core/src/core/coreToolScheduler.ts, the `publishBufferedResults()` method (line 1355) is called concurrently by multiple tool completion handlers (line 1510 in each tool's `.then()` callback) when parallel tools finish simultaneously. This causes a race condition where `nextPublishIndex` can be corrupted and tools can hang in 'executing' state forever, blocking the scheduler and causing subsequent tool calls to queue indefinitely. The method must be made reentrant-safe using a mutex/lock pattern to serialize concurrent calls.

Learnt from: acoliver
Repo: vybestack/llxprt-code PR: 0
File: :0-0
Timestamp: 2026-01-09T02:26:33.951Z
Learning: Preference: Consolidate to a single CoreToolScheduler instance per interactive session to avoid duplicated handlers and unknown correlationId noise.

@acoliver
Copy link
Collaborator Author

acoliver commented Jan 9, 2026

Response to CodeRabbit Comment on 'Execute BEFORE Approval'

@vybestack I want to clarify that this PR DOES fix the original bug reported in #1040, but there appears to be a misunderstanding of what the actual bug was.

The Actual Bug (Issue #1040)

The bug wasn't about tools auto-executing before approval. The actual bug was:

  • The same tool call (same callId) was being scheduled MULTIPLE TIMES from different sources
  • This caused the tool to execute BEFORE the user even saw the confirmation
  • When the user tried to confirm, the system failed with "file already exists" / "already done" errors

The user's experience was the ERROR messages, not seeing the tool execute twice.

How This PR Fixes It

  1. CLI-level dedup in useGeminiStream.ts - Filters duplicate callId requests from the provider stream before they reach the scheduler
  2. Confirmation dedup with processedConfirmations - Prevents handleConfirmationResponse from processing the same callId multiple times
  3. Scheduler-level dedup with seenCallIds - Filters duplicates at the scheduler boundary

Why CodeRabbit's Concern is About a Different Problem

CodeRabbit's analysis focuses on the auto-approvalgating logic (lines 1002, 1015, 1024 in schedule()). But those conditions are working correctly:

  • Line 1002: Auto-approve when policy ALLOWs - correct behavior
  • Line 1015: Auto-approve when tool says "no confirmation needed" - correct behavior
  • Line 1024: Auto-approve when YOLO mode OR tool matches allowedTools - correct behavior
  • Line 1027: Otherwise, go to awaiting_approval - correct behavior

The awaiting_approval path (lines 1027-1082) properly sets the status and waits for user confirmation. The tool does NOT execute until handleConfirmationResponse is called (line 1226 sets status to 'scheduled', then line 1228 executes).

The BUG Test

The test "should NOT execute tool until user confirms in DEFAULT mode" correctly passes because:

  • It only schedules the call ONCE
  • The tool goes to awaiting_approval propery
  • It doesn't execute until confirmed

The reason this test doesn't "catch the original bug" is that the bug required DUPLICATE schedules (same callId from multiple sources). Our dedup fixes now prevent that scenario from happening, so the test passes.

Conclusion

The original issue was duplicate scheduling of the same callId, not "tools executing before approval due to bad gating logic". Our dedup fixes address exactly that problem. The auto-approval and confirmation gating are working correctly and don't need changes.

@acoliver
Copy link
Collaborator Author

acoliver commented Jan 9, 2026

Response to CodeRabbit Review #3644838170

Thank you for the detailed review! I've addressed your concerns about the "execute before approval" bug in the comment above: #1044 (comment)

To summarize: The PR DOES fix the original bug (#1040) which was about duplicate scheduling of the same callId from multiple sources (provider stream + message bus + wrapped callbacks), not about tools auto-executing before approval due to bad gating logic. The auto-approval and confirmation gating are working correctly.

Regarding the actionable comments:

I've marked the following as dismissed because they're not critical for this PR:

  • Memory cleanup for long-running sessions (nice to have, but not blocking)
  • Using vi.waitFor instead of custom polling (preference, not critical)
  • Strengthening assertions in coreToolScheduler.test.ts (test improvement, not blocking)

Critical issues that were already fixed:

The test tool compilation issues mentioned were resolved. The ExecutionTrackingTool is working correctly and the tests pass (316 passed).

The PR is functionally complete and addresses the actual bug. The auto-approval logic is working as intended - tools requiring confirmation go to awaiting_approval status and don't execute until the user confirms.

@acoliver acoliver merged commit a051029 into main Jan 9, 2026
20 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

maintainer:e2e:ok Trusted contributor; maintainer-approved E2E run

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Shell issue

2 participants