Skip to content

Conversation

@zerob13
Copy link
Collaborator

@zerob13 zerob13 commented Jan 15, 2026

Summary by CodeRabbit

  • New Features

    • Directory-tree depth control (0–3, default 1) and read_file pagination (offset/limit).
    • Tool output offloading for large outputs (>3000 chars) with inline stubs and preview.
    • Permission request/grant/deny/continue flows surfaced in streams and UI.
    • Tool-system prompt for offload awareness; reasoning_time included in streaming events.
  • Bug Fixes

    • Improved propagation and display of raw error text.
    • Some validation errors adjusted to allow retries.
  • Documentation

    • Plan, spec, and task docs for tool output guardrails.
  • Tests

    • Unit/integration tests for depth limits and offload behavior.
  • Chores

    • Session-scoped file read restrictions and offload cleanup on session delete.

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

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 15, 2026

📝 Walkthrough

Walkthrough

Implements tool-output guardrails: directory_tree depth limits, per-conversation session-scoped file access, offloading long tool outputs (>3000 chars) to per-session files with model-facing stubs, permission_request propagation with new permission-related tool_call variants, and improved error text propagation.

Changes

Cohort / File(s) Summary
Documentation Specs
docs/specs/tool-output-guardrails/spec.md, docs/specs/tool-output-guardrails/plan.md, docs/specs/tool-output-guardrails/tasks.md
New spec/plan/tasks describing directory_tree depth, offload behavior (>3000 chars) to ~/.deepchat/sessions/<conversationId>/tool_<toolCallId>.offload, stub format (total chars, preview, path), permission flows, tests, and cleanup considerations.
Agent filesystem handler
src/main/presenter/agentPresenter/acp/agentFileSystemHandler.ts
Added conversationId/session-root state; DirectoryTreeArgsSchema gains depth; introduced accessType in PathValidationOptions; enforce session-scoped read checks and validate read paths for read operations; added read paging/truncation fields.
Agent tool manager & tool defs
src/main/presenter/agentPresenter/acp/agentToolManager.ts
Passes { conversationId } to AgentFileSystemHandler; adds depth param to directory_tree schema and updates tool descriptions and read_file pagination (offset/limit).
Tool call processing (offload)
src/main/presenter/agentPresenter/loop/toolCallProcessor.ts, src/main/presenter/sessionPresenter/sessionPaths.ts, src/main/presenter/sessionPresenter/index.ts
Offloads long tool outputs to per-session files; writes raw output to resolved offload file and replaces model-facing content with a stub (total chars, 1024-char preview, file path); adds resolve/delete helpers and onToolCallFinished notifications; session offload deletion on session removal.
Tool presenter / system prompt
src/main/presenter/toolPresenter/index.ts, src/main/presenter/agentPresenter/tool/toolCallCenter.ts, src/shared/types/presenters/tool.presenter.d.ts
Adds buildToolSystemPrompt({ conversationId }) API to include offload-path guidance in system prompt; ToolCallCenter exposes delegating method; presenter interface updated.
Error & retry handling
src/main/presenter/agentPresenter/index.ts, src/main/presenter/agentPresenter/loop/errorClassification.ts
Include textual error messages in STREAM_EVENTS.ERROR payloads; adjusted classification for certain schema/validation errors to be retryable.
Streaming / permission propagation
src/shared/types/core/agent-events.ts, src/main/presenter/agentPresenter/streaming/llmEventHandler.ts, src/main/presenter/agentPresenter/streaming/streamUpdateScheduler.ts
Extended tool_call union with permission-granted, permission-denied, continue; added reasoning_time and propagated permission_request through deltas to renderer and final flush.
Message building / tool prompt mode
src/main/presenter/agentPresenter/message/messageBuilder.ts
Added isToolPromptMode check; append tool system prompt when applicable; reuse ToolCallCenter instance for prompt construction.
Renderer chat store / permission flows
src/renderer/src/stores/chat.ts
Extended stream message typing for permission events; handling for permission-required, permission-granted, permission-denied, continue; added reasoning_time propagation and active-thread-aware edit/refresh logic.
Message component formatting
src/renderer/src/components/message/MessageBlockToolCall.vue
Replaced code-block rendering of params with plain inline text rendering (visual/formatting change).
Tests
test/main/presenter/agentPresenter/acp/agentFileSystemHandler.test.ts, test/main/presenter/agentPresenter/loop/toolCallProcessor.test.ts, test/main/presenter/sessionPresenter/permissionHandler.test.ts
Added directory_tree depth tests; tool output offload test validating stub, offload file creation, and message insertion; updated mocks to include buildToolSystemPrompt.
Session paths & cleanup
src/main/presenter/sessionPresenter/sessionPaths.ts, src/main/presenter/sessionPresenter/index.ts
Added session path helpers (getSessionsRoot, resolveSessionDir, resolveToolOffloadPath, resolveToolOffloadTemplatePath) and session offload deletion during session removal.

Sequence Diagram(s)

sequenceDiagram
    participant Agent as Agent Presenter
    participant Processor as ToolCallProcessor
    participant FS as Session FS
    participant LLM as LLM / Model

    Agent->>Processor: Tool completes (toolCallId, conversationId, output)
    Processor->>Processor: Measure output length (>3000?)
    alt Exceeds threshold
        Processor->>FS: Write full output to ~/.deepchat/sessions/<conversationId>/tool_<toolCallId>.offload
        Processor->>Processor: Build stub (total chars, preview, file path)
        Processor->>Agent: Insert stub into tool_call_response (tool_call_response_raw unchanged)
        Agent->>LLM: Model receives stub with offload path
    else Within threshold
        Processor->>Agent: Insert full content into tool_call_response
        Agent->>LLM: Model receives full content
    end
    Note right of LLM: Model may request file via session-scoped file tool if permitted
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~75 minutes

Possibly related PRs

Suggested labels

enhancement

Poem

🐰
I stash long outputs in a burrow snug and deep,
A stub peeks out while the full file sleeps.
Trees stay shallow, permissions mind the gate,
Paths are tidy, previews keep the state.
Hooray — the rabbit guards the data heap! 🥕

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title 'feat: large tool call offload' directly summarizes the main change: implementing offloading of large tool call outputs to external files when they exceed a threshold.

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

✨ Finishing touches
  • 📝 Generate docstrings


📜 Recent review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 2594b1f and 052554a.

📒 Files selected for processing (5)
  • src/main/presenter/agentPresenter/streaming/llmEventHandler.ts
  • src/main/presenter/agentPresenter/streaming/streamUpdateScheduler.ts
  • src/renderer/src/components/message/MessageBlockThink.vue
  • src/renderer/src/stores/chat.ts
  • src/shared/types/core/agent-events.ts
🧰 Additional context used
📓 Path-based instructions (19)
**/*.{js,ts,tsx,jsx,vue,mjs,cjs}

📄 CodeRabbit inference engine (.cursor/rules/development-setup.mdc)

All logs and comments must be in English

Files:

  • src/renderer/src/components/message/MessageBlockThink.vue
  • src/main/presenter/agentPresenter/streaming/llmEventHandler.ts
  • src/renderer/src/stores/chat.ts
  • src/main/presenter/agentPresenter/streaming/streamUpdateScheduler.ts
  • src/shared/types/core/agent-events.ts
**/*.{js,ts,tsx,jsx,vue,json,mjs,cjs}

📄 CodeRabbit inference engine (.cursor/rules/development-setup.mdc)

Use Prettier as the code formatter

Files:

  • src/renderer/src/components/message/MessageBlockThink.vue
  • src/main/presenter/agentPresenter/streaming/llmEventHandler.ts
  • src/renderer/src/stores/chat.ts
  • src/main/presenter/agentPresenter/streaming/streamUpdateScheduler.ts
  • src/shared/types/core/agent-events.ts
src/renderer/src/**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/i18n.mdc)

src/renderer/src/**/*.{vue,ts,tsx}: Use vue-i18n framework for internationalization located at src/renderer/src/i18n/
All user-facing strings must use i18n keys, not hardcoded text

src/renderer/src/**/*.{vue,ts,tsx}: Use ref for primitives and references, reactive for objects in Vue 3 Composition API
Prefer computed properties over methods for derived state in Vue components
Import Shadcn Vue components from @/shadcn/components/ui/ path alias
Use the cn() utility function combining clsx and tailwind-merge for dynamic Tailwind classes
Use defineAsyncComponent() for lazy loading heavy Vue components
Use TypeScript for all Vue components and composables with explicit type annotations
Define TypeScript interfaces for Vue component props and data structures
Use usePresenter composable for main process communication instead of direct IPC calls

Files:

  • src/renderer/src/components/message/MessageBlockThink.vue
  • src/renderer/src/stores/chat.ts
src/renderer/src/**/*.vue

📄 CodeRabbit inference engine (.cursor/rules/i18n.mdc)

Import useI18n from vue-i18n in Vue components to access translation functions t and locale

src/renderer/src/**/*.vue: Use <script setup> syntax for concise Vue 3 component definitions with Composition API
Define props and emits explicitly in Vue components using defineProps and defineEmits with TypeScript interfaces
Use provide/inject for dependency injection in Vue components instead of prop drilling
Use Tailwind CSS for all styling instead of writing scoped CSS files
Use mobile-first responsive design approach with Tailwind breakpoints
Use Iconify Vue with lucide icons as primary choice, following pattern lucide:{icon-name}
Use v-memo directive for memoizing expensive computations in templates
Use v-once directive for rendering static content without reactivity updates
Use virtual scrolling with RecycleScroller component for rendering long lists
Subscribe to events using rendererEvents.on() and unsubscribe in onUnmounted lifecycle hook

Files:

  • src/renderer/src/components/message/MessageBlockThink.vue
src/renderer/src/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (.cursor/rules/i18n.mdc)

Ensure all code comments are in English and all log messages are in English, with no non-English text in code comments or console statements

Use VueUse composables for common utilities like useLocalStorage, useClipboard, useDebounceFn

Vue 3 renderer app code should be organized in src/renderer/src with subdirectories for components/, stores/, views/, i18n/, and lib/

Files:

  • src/renderer/src/components/message/MessageBlockThink.vue
  • src/renderer/src/stores/chat.ts
src/renderer/src/components/**/*.vue

📄 CodeRabbit inference engine (.cursor/rules/vue-stack-guide.mdc)

Name Vue components using PascalCase (e.g., ChatInput.vue, MessageItemUser.vue)

Files:

  • src/renderer/src/components/message/MessageBlockThink.vue
**/*.vue

📄 CodeRabbit inference engine (AGENTS.md)

Vue components must be named in PascalCase (e.g., ChatInput.vue) and use Vue 3 Composition API with Pinia for state management and Tailwind for styling

**/*.vue: Use Vue 3 Composition API with <script setup> syntax
Use Pinia stores for state management
Style Vue components using Tailwind CSS (v4) with shadcn/ui (reka-ui)

Files:

  • src/renderer/src/components/message/MessageBlockThink.vue
**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,vue}: Use camelCase for variable and function names; use PascalCase for types and classes; use SCREAMING_SNAKE_CASE for constants
Configure Prettier with single quotes, no semicolons, and line width of 100 characters. Run pnpm run format after completing features

Files:

  • src/renderer/src/components/message/MessageBlockThink.vue
  • src/main/presenter/agentPresenter/streaming/llmEventHandler.ts
  • src/renderer/src/stores/chat.ts
  • src/main/presenter/agentPresenter/streaming/streamUpdateScheduler.ts
  • src/shared/types/core/agent-events.ts
**/*.{ts,tsx,js,jsx,vue}

📄 CodeRabbit inference engine (CLAUDE.md)

Use English for all logs and comments

Files:

  • src/renderer/src/components/message/MessageBlockThink.vue
  • src/main/presenter/agentPresenter/streaming/llmEventHandler.ts
  • src/renderer/src/stores/chat.ts
  • src/main/presenter/agentPresenter/streaming/streamUpdateScheduler.ts
  • src/shared/types/core/agent-events.ts
src/renderer/**/*.{ts,tsx,js,jsx,vue}

📄 CodeRabbit inference engine (CLAUDE.md)

All user-facing strings must use i18n keys (supports 12 languages)

Files:

  • src/renderer/src/components/message/MessageBlockThink.vue
  • src/renderer/src/stores/chat.ts
**/*.{js,ts,tsx,jsx,mjs,cjs}

📄 CodeRabbit inference engine (.cursor/rules/development-setup.mdc)

Use OxLint as the linter

Files:

  • src/main/presenter/agentPresenter/streaming/llmEventHandler.ts
  • src/renderer/src/stores/chat.ts
  • src/main/presenter/agentPresenter/streaming/streamUpdateScheduler.ts
  • src/shared/types/core/agent-events.ts
src/main/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Electron main process code should reside in src/main/, with presenters organized in presenter/ subdirectory (Window, Tab, Thread, Mcp, Config, LLMProvider), and app events managed via eventbus.ts

Files:

  • src/main/presenter/agentPresenter/streaming/llmEventHandler.ts
  • src/main/presenter/agentPresenter/streaming/streamUpdateScheduler.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use OxLint for linting JavaScript and TypeScript files; ensure lint-staged hooks and typecheck pass before commits

Enable strict TypeScript type checking

Files:

  • src/main/presenter/agentPresenter/streaming/llmEventHandler.ts
  • src/renderer/src/stores/chat.ts
  • src/main/presenter/agentPresenter/streaming/streamUpdateScheduler.ts
  • src/shared/types/core/agent-events.ts
src/main/presenter/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Implement all system capabilities as main-process presenters following the Presenter Pattern

Files:

  • src/main/presenter/agentPresenter/streaming/llmEventHandler.ts
  • src/main/presenter/agentPresenter/streaming/streamUpdateScheduler.ts
{src/main,src/renderer,test}/**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (CLAUDE.md)

Use IPC communication: Renderer calls main process via usePresenter composable, Main sends to Renderer via EventBus

Files:

  • src/main/presenter/agentPresenter/streaming/llmEventHandler.ts
  • src/renderer/src/stores/chat.ts
  • src/main/presenter/agentPresenter/streaming/streamUpdateScheduler.ts
src/renderer/src/stores/**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/vue-stack-guide.mdc)

src/renderer/src/stores/**/*.ts: Use Setup Store syntax with defineStore function pattern in Pinia stores
Use getters (computed properties) for derived state in Pinia stores
Keep Pinia store actions focused on state mutations and async operations

Files:

  • src/renderer/src/stores/chat.ts
src/renderer/src/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/vue-stack-guide.mdc)

Use class-variance-authority (CVA) for defining component variants with Tailwind classes

Files:

  • src/renderer/src/stores/chat.ts
src/renderer/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/vue-stack-guide.mdc)

src/renderer/src/**/*.{ts,tsx}: Use shallowRef and shallowReactive for optimizing reactivity with large objects
Prefer type over interface in TypeScript unless using inheritance with extends

Files:

  • src/renderer/src/stores/chat.ts
src/shared/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Shared TypeScript types and utilities should be placed in src/shared/

Files:

  • src/shared/types/core/agent-events.ts
🧠 Learnings (2)
📚 Learning: 2026-01-13T09:23:07.415Z
Learnt from: CR
Repo: ThinkInAIXYZ/deepchat PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-13T09:23:07.415Z
Learning: Applies to src/main/presenter/llmProviderPresenter/providers/*.ts : Implement `coreStream` method following standardized event interface for LLM providers

Applied to files:

  • src/main/presenter/agentPresenter/streaming/llmEventHandler.ts
📚 Learning: 2025-06-21T15:49:17.044Z
Learnt from: neoragex2002
Repo: ThinkInAIXYZ/deepchat PR: 550
File: src/renderer/src/stores/chat.ts:1011-1035
Timestamp: 2025-06-21T15:49:17.044Z
Learning: In src/renderer/src/stores/chat.ts, the user prefers to keep both `text` and `content` properties in the `handleMeetingInstruction` function's `sendMessage` call, even though they are redundant, rather than removing the `content` property.

Applied to files:

  • src/renderer/src/stores/chat.ts
🧬 Code graph analysis (2)
src/renderer/src/stores/chat.ts (2)
src/shared/chat/messageBlocks.ts (1)
  • finalizeAssistantMessageBlocks (3-25)
src/shared/chat.d.ts (2)
  • AssistantMessage (39-42)
  • UserMessage (34-37)
src/main/presenter/agentPresenter/streaming/streamUpdateScheduler.ts (1)
src/shared/types/core/agent-events.ts (1)
  • LLMAgentEventData (4-61)
🔇 Additional comments (17)
src/renderer/src/components/message/MessageBlockThink.vue (1)

135-142: LGTM!

The watch handler now unconditionally updates displayedSeconds when reasoningDuration changes, ensuring real-time UI updates during streaming. This correctly aligns with the new reasoning_time propagation from the streaming pipeline.

src/shared/types/core/agent-events.ts (2)

13-13: LGTM!

The reasoning_time field addition is correctly typed and aligns with the streaming pipeline changes that propagate this data from the LLM event handler through to the renderer.


23-32: LGTM!

The expanded tool_call union with 'permission-granted', 'permission-denied', and 'continue' variants correctly supports the new permission flow handling throughout the streaming pipeline.

src/main/presenter/agentPresenter/streaming/streamUpdateScheduler.ts (5)

8-26: LGTM!

The PendingDelta interface is correctly extended with reasoning_time and permission_request fields, aligning with the LLMAgentEventData type.


145-148: LGTM!

The comment correctly explains the intent: always using the latest reasoning_time since it contains the updated end time during streaming.


176-178: LGTM!

Permission request propagation through the enqueue path is correctly implemented.


253-263: LGTM!

Both reasoning_time and permission_request are correctly included in the flushRender delta payload.


381-391: LGTM!

Both reasoning_time and permission_request are correctly included in flushAll, addressing the previously flagged issue about missing permission_request.

src/main/presenter/agentPresenter/streaming/llmEventHandler.ts (3)

250-257: LGTM!

The delta now correctly extracts reasoning_time from the last reasoning_content block, ensuring real-time propagation to the renderer during streaming.


271-273: LGTM!

Permission request is correctly propagated in the delta when a tool_call is present.


297-314: Good restructuring of error handling flow.

Flushing stream buffers before persisting the error prevents stale snapshot overwrites. The fallback path (lines 308-313) correctly handles edge cases where the error arrives for a message without an active generating state.

src/renderer/src/stores/chat.ts (6)

883-929: LGTM!

The stream message type is correctly extended with reasoning_time and permission_request, matching the LLMAgentEventData type from agent-events.ts.


1035-1091: LGTM!

The permission-required handling correctly finalizes existing blocks and creates a permission action block with comprehensive metadata. The use of JSON.stringify for complex objects in the extra field is appropriate given its Record<string, string | boolean> type constraint.


1088-1125: LGTM!

The permission state transitions (permission-granted, permission-denied, continue) correctly locate the matching permission block and update its status. The reverse search ensures the most recent matching block is found.


1259-1260: Good addition.

Finalizing previous blocks before adding a new content block ensures proper state transitions, especially when switching from reasoning_content to content blocks.


1271-1287: LGTM!

The reasoning_time handling correctly updates existing blocks from stream data and initializes new blocks with proper timestamps.


1700-1737: Good refactoring for cross-thread safety.

The early returns when no active thread or when the message belongs to a different conversation prevent cross-thread contamination. The variant message handling correctly updates the main message in the active thread context.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.


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

❤️ Share

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

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: 4

Caution

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

⚠️ Outside diff range comments (2)
src/main/presenter/agentPresenter/loop/errorClassification.ts (1)

10-14: Docstring is inconsistent with the new behavior.

The function docstring still lists "Invalid input format (invalid URL, malformed JSON, etc.)" and "Schema validation failures" as non-retryable errors, but the implementation now returns false (retryable) for JSON and schema validation errors at lines 44 and 58. Update the documentation to reflect the actual behavior.

📝 Suggested docstring update
 /**
  * Checks if an error is non-retryable (should stop the agent loop).
  *
  * Non-retryable errors are those that won't be resolved by retrying:
- * - Invalid input format (invalid URL, malformed JSON, etc.)
+ * - Invalid URL format
  * - Explicit permission denied
- * - Schema validation failures
  * - Authentication errors that can't be resolved by retry
  * - Malformed requests that won't work on retry
+ * - Tool definition not found
+ *
+ * Retryable errors (LLM may correct on retry):
+ * - Invalid/malformed JSON (LLM can fix argument formatting)
+ * - Schema validation failures (LLM can correct parameters)
  *
src/main/presenter/agentPresenter/index.ts (1)

235-242: Error messages should be in English per coding guidelines.

Lines 236 and 241 contain Chinese error messages. As per coding guidelines, all logs and comments (including error messages) must be in English for TypeScript files.

📝 Suggested fix
     if (message.role !== 'assistant') {
-      throw new Error('只能重试助手消息')
+      throw new Error('Can only retry assistant messages')
     }

     const userMessage = await this.messageManager.getMessage(message.parentId || '')
     if (!userMessage) {
-      throw new Error('找不到对应的用户消息')
+      throw new Error('Could not find corresponding user message')
     }
🤖 Fix all issues with AI agents
In `@docs/specs/tool-output-guardrails/plan.md`:
- Line 29: The document has an internal inconsistency: the file name is listed
as `tool_<toolCallId>.offload` on line 29 but as a `.txt` file on line 69;
standardize the extension to match spec.md by choosing one extension (either
`.offload` or `.txt`), update every occurrence of `tool_<toolCallId>.*` in this
document to that extension, and ensure the chosen extension aligns with the
filename and examples used in spec.md.

In `@docs/specs/tool-output-guardrails/spec.md`:
- Around line 51-52: Standardize the offload file extension between the two
docs: update the example in spec.md that currently shows `tool_<toolCallId>.txt`
to use the same extension used in plan.md (change to `tool_<toolCallId>.offload`
or vice versa), and ensure both spec.md and plan.md consistently reference that
single extension everywhere.

In `@src/main/presenter/agentPresenter/streaming/streamUpdateScheduler.ts`:
- Line 257: The flushAll method is missing the permission_request field so
pending permission-request deltas get dropped; update the eventData assembly in
flushAll (mirror how flushRender constructs eventData) to include
permission_request: delta.permission_request (or the same source used in
flushRender), ensuring the permission_request from the current delta is passed
through when emitting or sending the event.
🧹 Nitpick comments (3)
test/main/presenter/agentPresenter/loop/toolCallProcessor.test.ts (1)

1-95: Well-structured test for tool output offloading.

The test properly validates the offload behavior:

  • Creates a temporary directory and mocks app.getPath
  • Uses 3001 characters to exceed the 3000-char threshold
  • Verifies stub content, file path, raw data preservation, and conversation message updates
  • Cleans up resources in afterEach

One minor suggestion: consider using a typed array instead of any[] on line 57 for better type safety.

🔧 Optional type improvement
-    const events: any[] = []
+    const events: Array<{ type: string; data?: Record<string, unknown> }> = []
test/main/presenter/agentPresenter/acp/agentFileSystemHandler.test.ts (1)

134-136: Validates max depth enforcement.

The test correctly verifies that requesting depth beyond the maximum (3) results in rejection. No shared constant currently exists for the max depth value in agentFileSystemHandler—it's hardcoded in the DirectoryTreeArgsSchema. Extracting this to a named constant would improve maintainability and allow the test to reference it directly instead of hardcoding depth: 4.

src/main/presenter/agentPresenter/loop/toolCallProcessor.ts (1)

375-399: Solid implementation with graceful error handling.

The offload logic is well-structured with proper fallback behavior. One minor consideration:

The toolCallId sanitization (line 386) only removes path separators (/, \). While tool call IDs are typically safe UUIDs, for defense-in-depth, you could use a more comprehensive sanitization to handle any unexpected characters that might be problematic on different file systems.

🔧 Optional: More robust filename sanitization
-    const safeToolCallId = toolCallId.replace(/[\\/]/g, '_')
+    const safeToolCallId = toolCallId.replace(/[<>:"/\\|?*\x00-\x1f]/g, '_')
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between e8aec9a and 54b9dab.

📒 Files selected for processing (19)
  • docs/specs/tool-output-guardrails/plan.md
  • docs/specs/tool-output-guardrails/spec.md
  • docs/specs/tool-output-guardrails/tasks.md
  • src/main/presenter/agentPresenter/acp/agentFileSystemHandler.ts
  • src/main/presenter/agentPresenter/acp/agentToolManager.ts
  • src/main/presenter/agentPresenter/index.ts
  • src/main/presenter/agentPresenter/loop/errorClassification.ts
  • src/main/presenter/agentPresenter/loop/toolCallProcessor.ts
  • src/main/presenter/agentPresenter/message/messageBuilder.ts
  • src/main/presenter/agentPresenter/streaming/llmEventHandler.ts
  • src/main/presenter/agentPresenter/streaming/streamUpdateScheduler.ts
  • src/main/presenter/agentPresenter/tool/toolCallCenter.ts
  • src/main/presenter/toolPresenter/index.ts
  • src/renderer/src/stores/chat.ts
  • src/shared/types/core/agent-events.ts
  • src/shared/types/presenters/tool.presenter.d.ts
  • test/main/presenter/agentPresenter/acp/agentFileSystemHandler.test.ts
  • test/main/presenter/agentPresenter/loop/toolCallProcessor.test.ts
  • test/main/presenter/sessionPresenter/permissionHandler.test.ts
🧰 Additional context used
📓 Path-based instructions (18)
**/*.{js,ts,tsx,jsx,vue,mjs,cjs}

📄 CodeRabbit inference engine (.cursor/rules/development-setup.mdc)

All logs and comments must be in English

Files:

  • src/shared/types/presenters/tool.presenter.d.ts
  • test/main/presenter/agentPresenter/loop/toolCallProcessor.test.ts
  • src/main/presenter/agentPresenter/streaming/llmEventHandler.ts
  • src/shared/types/core/agent-events.ts
  • src/main/presenter/toolPresenter/index.ts
  • src/main/presenter/agentPresenter/index.ts
  • test/main/presenter/sessionPresenter/permissionHandler.test.ts
  • src/main/presenter/agentPresenter/streaming/streamUpdateScheduler.ts
  • src/main/presenter/agentPresenter/tool/toolCallCenter.ts
  • test/main/presenter/agentPresenter/acp/agentFileSystemHandler.test.ts
  • src/main/presenter/agentPresenter/loop/toolCallProcessor.ts
  • src/main/presenter/agentPresenter/loop/errorClassification.ts
  • src/main/presenter/agentPresenter/message/messageBuilder.ts
  • src/main/presenter/agentPresenter/acp/agentToolManager.ts
  • src/main/presenter/agentPresenter/acp/agentFileSystemHandler.ts
  • src/renderer/src/stores/chat.ts
**/*.{js,ts,tsx,jsx,mjs,cjs}

📄 CodeRabbit inference engine (.cursor/rules/development-setup.mdc)

Use OxLint as the linter

Files:

  • src/shared/types/presenters/tool.presenter.d.ts
  • test/main/presenter/agentPresenter/loop/toolCallProcessor.test.ts
  • src/main/presenter/agentPresenter/streaming/llmEventHandler.ts
  • src/shared/types/core/agent-events.ts
  • src/main/presenter/toolPresenter/index.ts
  • src/main/presenter/agentPresenter/index.ts
  • test/main/presenter/sessionPresenter/permissionHandler.test.ts
  • src/main/presenter/agentPresenter/streaming/streamUpdateScheduler.ts
  • src/main/presenter/agentPresenter/tool/toolCallCenter.ts
  • test/main/presenter/agentPresenter/acp/agentFileSystemHandler.test.ts
  • src/main/presenter/agentPresenter/loop/toolCallProcessor.ts
  • src/main/presenter/agentPresenter/loop/errorClassification.ts
  • src/main/presenter/agentPresenter/message/messageBuilder.ts
  • src/main/presenter/agentPresenter/acp/agentToolManager.ts
  • src/main/presenter/agentPresenter/acp/agentFileSystemHandler.ts
  • src/renderer/src/stores/chat.ts
**/*.{js,ts,tsx,jsx,vue,json,mjs,cjs}

📄 CodeRabbit inference engine (.cursor/rules/development-setup.mdc)

Use Prettier as the code formatter

Files:

  • src/shared/types/presenters/tool.presenter.d.ts
  • test/main/presenter/agentPresenter/loop/toolCallProcessor.test.ts
  • src/main/presenter/agentPresenter/streaming/llmEventHandler.ts
  • src/shared/types/core/agent-events.ts
  • src/main/presenter/toolPresenter/index.ts
  • src/main/presenter/agentPresenter/index.ts
  • test/main/presenter/sessionPresenter/permissionHandler.test.ts
  • src/main/presenter/agentPresenter/streaming/streamUpdateScheduler.ts
  • src/main/presenter/agentPresenter/tool/toolCallCenter.ts
  • test/main/presenter/agentPresenter/acp/agentFileSystemHandler.test.ts
  • src/main/presenter/agentPresenter/loop/toolCallProcessor.ts
  • src/main/presenter/agentPresenter/loop/errorClassification.ts
  • src/main/presenter/agentPresenter/message/messageBuilder.ts
  • src/main/presenter/agentPresenter/acp/agentToolManager.ts
  • src/main/presenter/agentPresenter/acp/agentFileSystemHandler.ts
  • src/renderer/src/stores/chat.ts
src/shared/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Shared TypeScript types and utilities should be placed in src/shared/

Files:

  • src/shared/types/presenters/tool.presenter.d.ts
  • src/shared/types/core/agent-events.ts
**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,vue}: Use camelCase for variable and function names; use PascalCase for types and classes; use SCREAMING_SNAKE_CASE for constants
Configure Prettier with single quotes, no semicolons, and line width of 100 characters. Run pnpm run format after completing features

Files:

  • src/shared/types/presenters/tool.presenter.d.ts
  • test/main/presenter/agentPresenter/loop/toolCallProcessor.test.ts
  • src/main/presenter/agentPresenter/streaming/llmEventHandler.ts
  • src/shared/types/core/agent-events.ts
  • src/main/presenter/toolPresenter/index.ts
  • src/main/presenter/agentPresenter/index.ts
  • test/main/presenter/sessionPresenter/permissionHandler.test.ts
  • src/main/presenter/agentPresenter/streaming/streamUpdateScheduler.ts
  • src/main/presenter/agentPresenter/tool/toolCallCenter.ts
  • test/main/presenter/agentPresenter/acp/agentFileSystemHandler.test.ts
  • src/main/presenter/agentPresenter/loop/toolCallProcessor.ts
  • src/main/presenter/agentPresenter/loop/errorClassification.ts
  • src/main/presenter/agentPresenter/message/messageBuilder.ts
  • src/main/presenter/agentPresenter/acp/agentToolManager.ts
  • src/main/presenter/agentPresenter/acp/agentFileSystemHandler.ts
  • src/renderer/src/stores/chat.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use OxLint for linting JavaScript and TypeScript files; ensure lint-staged hooks and typecheck pass before commits

Enable strict TypeScript type checking

Files:

  • src/shared/types/presenters/tool.presenter.d.ts
  • test/main/presenter/agentPresenter/loop/toolCallProcessor.test.ts
  • src/main/presenter/agentPresenter/streaming/llmEventHandler.ts
  • src/shared/types/core/agent-events.ts
  • src/main/presenter/toolPresenter/index.ts
  • src/main/presenter/agentPresenter/index.ts
  • test/main/presenter/sessionPresenter/permissionHandler.test.ts
  • src/main/presenter/agentPresenter/streaming/streamUpdateScheduler.ts
  • src/main/presenter/agentPresenter/tool/toolCallCenter.ts
  • test/main/presenter/agentPresenter/acp/agentFileSystemHandler.test.ts
  • src/main/presenter/agentPresenter/loop/toolCallProcessor.ts
  • src/main/presenter/agentPresenter/loop/errorClassification.ts
  • src/main/presenter/agentPresenter/message/messageBuilder.ts
  • src/main/presenter/agentPresenter/acp/agentToolManager.ts
  • src/main/presenter/agentPresenter/acp/agentFileSystemHandler.ts
  • src/renderer/src/stores/chat.ts
**/*.{ts,tsx,js,jsx,vue}

📄 CodeRabbit inference engine (CLAUDE.md)

Use English for all logs and comments

Files:

  • src/shared/types/presenters/tool.presenter.d.ts
  • test/main/presenter/agentPresenter/loop/toolCallProcessor.test.ts
  • src/main/presenter/agentPresenter/streaming/llmEventHandler.ts
  • src/shared/types/core/agent-events.ts
  • src/main/presenter/toolPresenter/index.ts
  • src/main/presenter/agentPresenter/index.ts
  • test/main/presenter/sessionPresenter/permissionHandler.test.ts
  • src/main/presenter/agentPresenter/streaming/streamUpdateScheduler.ts
  • src/main/presenter/agentPresenter/tool/toolCallCenter.ts
  • test/main/presenter/agentPresenter/acp/agentFileSystemHandler.test.ts
  • src/main/presenter/agentPresenter/loop/toolCallProcessor.ts
  • src/main/presenter/agentPresenter/loop/errorClassification.ts
  • src/main/presenter/agentPresenter/message/messageBuilder.ts
  • src/main/presenter/agentPresenter/acp/agentToolManager.ts
  • src/main/presenter/agentPresenter/acp/agentFileSystemHandler.ts
  • src/renderer/src/stores/chat.ts
test/**/*.test.ts

📄 CodeRabbit inference engine (AGENTS.md)

Vitest test suites should be organized in test/main/** and test/renderer/** mirroring source structure, with file names following *.test.ts or *.spec.ts pattern

Files:

  • test/main/presenter/agentPresenter/loop/toolCallProcessor.test.ts
  • test/main/presenter/sessionPresenter/permissionHandler.test.ts
  • test/main/presenter/agentPresenter/acp/agentFileSystemHandler.test.ts
{src/main,src/renderer,test}/**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (CLAUDE.md)

Use IPC communication: Renderer calls main process via usePresenter composable, Main sends to Renderer via EventBus

Files:

  • test/main/presenter/agentPresenter/loop/toolCallProcessor.test.ts
  • src/main/presenter/agentPresenter/streaming/llmEventHandler.ts
  • src/main/presenter/toolPresenter/index.ts
  • src/main/presenter/agentPresenter/index.ts
  • test/main/presenter/sessionPresenter/permissionHandler.test.ts
  • src/main/presenter/agentPresenter/streaming/streamUpdateScheduler.ts
  • src/main/presenter/agentPresenter/tool/toolCallCenter.ts
  • test/main/presenter/agentPresenter/acp/agentFileSystemHandler.test.ts
  • src/main/presenter/agentPresenter/loop/toolCallProcessor.ts
  • src/main/presenter/agentPresenter/loop/errorClassification.ts
  • src/main/presenter/agentPresenter/message/messageBuilder.ts
  • src/main/presenter/agentPresenter/acp/agentToolManager.ts
  • src/main/presenter/agentPresenter/acp/agentFileSystemHandler.ts
  • src/renderer/src/stores/chat.ts
**/*.test.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Write tests using Vitest with separate test suites for main and renderer processes

Files:

  • test/main/presenter/agentPresenter/loop/toolCallProcessor.test.ts
  • test/main/presenter/sessionPresenter/permissionHandler.test.ts
  • test/main/presenter/agentPresenter/acp/agentFileSystemHandler.test.ts
src/main/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Electron main process code should reside in src/main/, with presenters organized in presenter/ subdirectory (Window, Tab, Thread, Mcp, Config, LLMProvider), and app events managed via eventbus.ts

Files:

  • src/main/presenter/agentPresenter/streaming/llmEventHandler.ts
  • src/main/presenter/toolPresenter/index.ts
  • src/main/presenter/agentPresenter/index.ts
  • src/main/presenter/agentPresenter/streaming/streamUpdateScheduler.ts
  • src/main/presenter/agentPresenter/tool/toolCallCenter.ts
  • src/main/presenter/agentPresenter/loop/toolCallProcessor.ts
  • src/main/presenter/agentPresenter/loop/errorClassification.ts
  • src/main/presenter/agentPresenter/message/messageBuilder.ts
  • src/main/presenter/agentPresenter/acp/agentToolManager.ts
  • src/main/presenter/agentPresenter/acp/agentFileSystemHandler.ts
src/main/presenter/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Implement all system capabilities as main-process presenters following the Presenter Pattern

Files:

  • src/main/presenter/agentPresenter/streaming/llmEventHandler.ts
  • src/main/presenter/toolPresenter/index.ts
  • src/main/presenter/agentPresenter/index.ts
  • src/main/presenter/agentPresenter/streaming/streamUpdateScheduler.ts
  • src/main/presenter/agentPresenter/tool/toolCallCenter.ts
  • src/main/presenter/agentPresenter/loop/toolCallProcessor.ts
  • src/main/presenter/agentPresenter/loop/errorClassification.ts
  • src/main/presenter/agentPresenter/message/messageBuilder.ts
  • src/main/presenter/agentPresenter/acp/agentToolManager.ts
  • src/main/presenter/agentPresenter/acp/agentFileSystemHandler.ts
src/renderer/src/**/*.{vue,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/i18n.mdc)

src/renderer/src/**/*.{vue,ts,tsx}: Use vue-i18n framework for internationalization located at src/renderer/src/i18n/
All user-facing strings must use i18n keys, not hardcoded text

src/renderer/src/**/*.{vue,ts,tsx}: Use ref for primitives and references, reactive for objects in Vue 3 Composition API
Prefer computed properties over methods for derived state in Vue components
Import Shadcn Vue components from @/shadcn/components/ui/ path alias
Use the cn() utility function combining clsx and tailwind-merge for dynamic Tailwind classes
Use defineAsyncComponent() for lazy loading heavy Vue components
Use TypeScript for all Vue components and composables with explicit type annotations
Define TypeScript interfaces for Vue component props and data structures
Use usePresenter composable for main process communication instead of direct IPC calls

Files:

  • src/renderer/src/stores/chat.ts
src/renderer/src/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (.cursor/rules/i18n.mdc)

Ensure all code comments are in English and all log messages are in English, with no non-English text in code comments or console statements

Use VueUse composables for common utilities like useLocalStorage, useClipboard, useDebounceFn

Vue 3 renderer app code should be organized in src/renderer/src with subdirectories for components/, stores/, views/, i18n/, and lib/

Files:

  • src/renderer/src/stores/chat.ts
src/renderer/src/stores/**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/vue-stack-guide.mdc)

src/renderer/src/stores/**/*.ts: Use Setup Store syntax with defineStore function pattern in Pinia stores
Use getters (computed properties) for derived state in Pinia stores
Keep Pinia store actions focused on state mutations and async operations

Files:

  • src/renderer/src/stores/chat.ts
src/renderer/src/**/*.{ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/vue-stack-guide.mdc)

Use class-variance-authority (CVA) for defining component variants with Tailwind classes

Files:

  • src/renderer/src/stores/chat.ts
src/renderer/src/**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/vue-stack-guide.mdc)

src/renderer/src/**/*.{ts,tsx}: Use shallowRef and shallowReactive for optimizing reactivity with large objects
Prefer type over interface in TypeScript unless using inheritance with extends

Files:

  • src/renderer/src/stores/chat.ts
src/renderer/**/*.{ts,tsx,js,jsx,vue}

📄 CodeRabbit inference engine (CLAUDE.md)

All user-facing strings must use i18n keys (supports 12 languages)

Files:

  • src/renderer/src/stores/chat.ts
🧠 Learnings (6)
📚 Learning: 2026-01-13T09:23:07.415Z
Learnt from: CR
Repo: ThinkInAIXYZ/deepchat PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-13T09:23:07.415Z
Learning: Applies to src/main/presenter/**/*.ts : Implement all system capabilities as main-process presenters following the Presenter Pattern

Applied to files:

  • src/shared/types/presenters/tool.presenter.d.ts
  • src/main/presenter/toolPresenter/index.ts
  • test/main/presenter/sessionPresenter/permissionHandler.test.ts
  • src/main/presenter/agentPresenter/tool/toolCallCenter.ts
  • src/main/presenter/agentPresenter/message/messageBuilder.ts
📚 Learning: 2026-01-13T09:23:07.415Z
Learnt from: CR
Repo: ThinkInAIXYZ/deepchat PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-13T09:23:07.415Z
Learning: Applies to src/main/presenter/mcpPresenter/inMemoryServers/**/*.ts : Implement new MCP tools in the inMemoryServers directory and register in mcpPresenter/index.ts

Applied to files:

  • src/shared/types/presenters/tool.presenter.d.ts
  • test/main/presenter/agentPresenter/loop/toolCallProcessor.test.ts
  • src/main/presenter/toolPresenter/index.ts
  • test/main/presenter/sessionPresenter/permissionHandler.test.ts
  • src/main/presenter/agentPresenter/tool/toolCallCenter.ts
  • src/main/presenter/agentPresenter/loop/toolCallProcessor.ts
  • src/main/presenter/agentPresenter/message/messageBuilder.ts
  • src/main/presenter/agentPresenter/acp/agentToolManager.ts
📚 Learning: 2026-01-13T09:23:07.415Z
Learnt from: CR
Repo: ThinkInAIXYZ/deepchat PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-13T09:23:07.415Z
Learning: Applies to **/*.test.ts : Write tests using Vitest with separate test suites for main and renderer processes

Applied to files:

  • test/main/presenter/agentPresenter/loop/toolCallProcessor.test.ts
📚 Learning: 2026-01-13T09:23:07.415Z
Learnt from: CR
Repo: ThinkInAIXYZ/deepchat PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-13T09:23:07.415Z
Learning: Applies to src/main/presenter/llmProviderPresenter/providers/*.ts : Implement `coreStream` method following standardized event interface for LLM providers

Applied to files:

  • src/main/presenter/agentPresenter/streaming/llmEventHandler.ts
  • src/main/presenter/agentPresenter/index.ts
  • test/main/presenter/sessionPresenter/permissionHandler.test.ts
📚 Learning: 2026-01-05T02:41:45.219Z
Learnt from: CR
Repo: ThinkInAIXYZ/deepchat PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-05T02:41:45.219Z
Learning: Project uses Node.js ≥ 20.19 and pnpm ≥ 10.11 (pnpm only); Windows developers must enable Developer Mode for symlinks

Applied to files:

  • src/main/presenter/agentPresenter/acp/agentFileSystemHandler.ts
📚 Learning: 2025-06-21T15:49:17.044Z
Learnt from: neoragex2002
Repo: ThinkInAIXYZ/deepchat PR: 550
File: src/renderer/src/stores/chat.ts:1011-1035
Timestamp: 2025-06-21T15:49:17.044Z
Learning: In src/renderer/src/stores/chat.ts, the user prefers to keep both `text` and `content` properties in the `handleMeetingInstruction` function's `sendMessage` call, even though they are redundant, rather than removing the `content` property.

Applied to files:

  • src/renderer/src/stores/chat.ts
🧬 Code graph analysis (8)
test/main/presenter/agentPresenter/loop/toolCallProcessor.test.ts (1)
src/main/presenter/agentPresenter/loop/toolCallProcessor.ts (1)
  • ToolCallProcessor (46-502)
src/main/presenter/toolPresenter/index.ts (1)
test/mocks/electron.ts (1)
  • app (2-10)
src/main/presenter/agentPresenter/index.ts (1)
src/main/eventbus.ts (1)
  • eventBus (151-151)
src/main/presenter/agentPresenter/streaming/streamUpdateScheduler.ts (1)
src/shared/types/core/agent-events.ts (1)
  • LLMAgentEventData (4-60)
src/main/presenter/agentPresenter/message/messageBuilder.ts (2)
src/main/presenter/agentPresenter/tool/toolCallCenter.ts (1)
  • ToolCallCenter (15-29)
src/main/presenter/index.ts (1)
  • presenter (334-334)
src/main/presenter/agentPresenter/acp/agentToolManager.ts (1)
src/main/presenter/agentPresenter/acp/agentFileSystemHandler.ts (1)
  • AgentFileSystemHandler (162-1199)
src/main/presenter/agentPresenter/acp/agentFileSystemHandler.ts (1)
test/mocks/electron.ts (1)
  • app (2-10)
src/renderer/src/stores/chat.ts (2)
src/shared/chat/messageBlocks.ts (1)
  • finalizeAssistantMessageBlocks (3-25)
src/shared/chat.d.ts (2)
  • AssistantMessage (39-42)
  • UserMessage (34-37)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build-check (x64)
🔇 Additional comments (39)
src/main/presenter/agentPresenter/loop/errorClassification.ts (1)

38-59: LGTM! Making JSON and schema errors retryable is a reasonable design choice.

Allowing the LLM to retry when it produces malformed JSON or incorrect parameters gives it an opportunity to self-correct. This aligns well with the agent loop's error recovery strategy.

docs/specs/tool-output-guardrails/tasks.md (1)

1-12: LGTM!

The task breakdown is well-structured and aligns with the spec and plan documents.

docs/specs/tool-output-guardrails/spec.md (1)

1-59: LGTM!

The spec clearly defines acceptance criteria for error presentation, directory_tree depth control, and tool output offload. The depth semantics (root=0) and offload threshold (>3000 chars) are well documented.

docs/specs/tool-output-guardrails/plan.md (1)

81-87: Good risk mitigation coverage.

The fallback behavior for missing conversationId (truncate without offload) is a sensible degradation strategy. Consider documenting the truncation length for this fallback case.

src/main/presenter/agentPresenter/index.ts (3)

173-182: LGTM! Consistent error propagation pattern.

The error extraction and propagation to the renderer is well implemented. The pattern error instanceof Error ? error.message : String(error) handles both Error instances and string errors gracefully.


203-212: LGTM!

Consistent error handling pattern with sendMessage.


253-262: LGTM!

Consistent error handling pattern with sendMessage and continueLoop.

src/main/presenter/agentPresenter/streaming/llmEventHandler.ts (1)

264-266: Correctly propagates permission_request to the stream delta.

The condition msg.permission_request !== undefined properly handles cases where permission_request might be a falsy but defined value (e.g., empty object), ensuring it's included in the delta for downstream handling.

test/main/presenter/sessionPresenter/permissionHandler.test.ts (2)

112-114: Mock correctly updated to include new interface method.

The buildToolSystemPrompt mock is properly added to align with the updated IToolPresenter interface.


228-230: Consistent mock update.

Same mock addition as above, maintaining consistency across test helpers.

src/shared/types/presenters/tool.presenter.d.ts (1)

29-33: Clean interface extension for tool system prompt construction.

The new method follows the established pattern in the interface with proper JSDoc documentation. The optional conversationId in the context object provides flexibility for different use cases.

test/main/presenter/agentPresenter/acp/agentFileSystemHandler.test.ts (1)

94-132: Thorough depth-limiting test coverage.

The test comprehensively validates depth behavior at levels 0, 1, and 2, correctly asserting:

  • At depth 0: directories exist but have no children populated
  • At depth 1: immediate children are populated, nested directories have no children
  • At depth 2: two levels of children are populated

The nested directory structure setup and assertions are well-organized.

src/main/presenter/agentPresenter/tool/toolCallCenter.ts (1)

25-28: LGTM!

The new method follows the established delegation pattern in this class, cleanly forwarding to toolPresenter.buildToolSystemPrompt. Consistent with the existing architecture.

src/main/presenter/agentPresenter/streaming/streamUpdateScheduler.ts (2)

20-20: LGTM!

Type reference to LLMAgentEventData['permission_request'] ensures consistency with the shared type definition.


171-173: LGTM!

Follows the established pattern for handling delta fields.

src/main/presenter/agentPresenter/message/messageBuilder.ts (3)

109-109: LGTM!

The isToolPromptMode variable correctly captures both 'agent' and 'acp agent' modes for determining when tool system prompts should be included.


121-121: LGTM!

ToolCallCenter instantiation is appropriate here as it's reused for both getAllToolDefinitions and the new buildToolSystemPrompt call.


162-167: LGTM!

The guard condition correctly ensures the tool system prompt is only added when tools are available and the mode supports tool usage. The prompt is properly appended to the system prompt.

src/shared/types/core/agent-events.ts (1)

22-31: LGTM!

The expanded tool_call union type correctly adds the permission flow states ('permission-granted', 'permission-denied') and 'continue' state. This is an additive, backward-compatible change that enables the new permission handling workflow.

src/main/presenter/toolPresenter/index.ts (2)

9-10: LGTM!

Necessary imports for path construction and accessing the home directory.


24-24: LGTM!

Interface properly declares the new method signature.

src/main/presenter/agentPresenter/acp/agentToolManager.ts (3)

168-178: LGTM!

The depth parameter schema is well-defined with appropriate constraints (integer, 0-3 range, default 1) to prevent excessive recursion while allowing useful directory tree exploration.


475-488: LGTM!

The updated description correctly documents the new depth parameter capability, keeping user-facing documentation in sync with the schema changes.


620-623: LGTM! Session-scoped handler correctly instantiated.

The conversationId is properly passed to enable per-conversation session file access restrictions during tool execution.

Note: The instance-level this.fileSystemHandler created in getAllToolDefinitions (line 243) doesn't receive conversationId, but this appears intentional since that handler is only used for workspace path management, not actual file operations with session-scoped access requirements.

src/main/presenter/agentPresenter/loop/toolCallProcessor.ts (5)

9-11: LGTM!

The new imports are appropriate for the file system offload functionality.


43-44: LGTM!

The threshold (3000 chars) and preview length (1024 chars) are reasonable defaults for the offload mechanism.


401-412: LGTM! Path traversal protection is correctly implemented.

The validation logic properly handles various traversal attempts:

  • .. paths are correctly rejected since the resolved path won't start with sessionsRoot/
  • . is rejected by the equality check
  • Embedded ../ in paths like foo/../bar resolve safely within the sessions directory

Good defensive coding.


414-422: LGTM!

The stub format is clear, informative, and provides sufficient context (total size, file path, preview) for both the LLM and users to understand the offloaded content.


186-233: LGTM!

The offload integration is well-implemented:

  • Both native and legacy function call paths consistently use toolContentForModel
  • The original toolResponse.rawData is preserved in tool_call_response_raw for debugging/UI purposes
  • The flow correctly handles the async offload operation before proceeding
src/main/presenter/agentPresenter/acp/agentFileSystemHandler.ts (7)

92-95: LGTM!

The DirectoryTreeArgsSchema correctly mirrors the schema defined in agentToolManager.ts, ensuring consistency between validation and tool definition.


162-178: LGTM!

The constructor properly initializes session-scoped access control with:

  • Optional conversationId for per-conversation restrictions
  • Computed sessionsRoot path for session boundary enforcement

216-263: LGTM! Thorough security validation.

The validatePath method correctly performs session read checks both:

  1. On the initially resolved path (line 224)
  2. After symlink resolution (line 238)

This double-check prevents symlink-based bypass attacks where a link could point outside the allowed session directory.


265-286: LGTM! Session isolation is correctly enforced.

The session read access control logic:

  1. Allows unrestricted access outside the sessions directory
  2. Requires an active conversation for any session file access
  3. Restricts access to only the current conversation's session directory

The path comparison correctly handles both exact directory matches and subdirectory access.


1046-1082: LGTM! Depth-controlled recursion correctly implemented.

The depth parameter works as documented:

  • depth=0: Root level only (no directory recursion)
  • depth=1: Root + one level of subdirectories
  • depth=3 (max): Up to 3 levels deep

The accessType: 'read' validation ensures session-scoped restrictions apply to directory tree operations.


548-551: LGTM!

Search operations correctly apply accessType: 'read' validation to enforce session-scoped access restrictions.


831-852: LGTM!

Read operations (readFile, listDirectory, grepSearch, getFileInfo, globSearch) consistently apply accessType: 'read' validation, ensuring session-scoped restrictions are enforced across all file reading paths.

src/renderer/src/stores/chat.ts (3)

892-928: LGTM!

The extended tool_call union and permission_request type definition comprehensively support the new permission workflow. The structure accommodates various permission scenarios (read/write/command) with appropriate metadata fields.


1033-1125: LGTM! Permission flow handling is well-structured.

The implementation correctly:

  1. Creates permission request blocks with comprehensive metadata
  2. Uses reverse search to find the most recent matching permission block (important for nested tool calls)
  3. Updates block status appropriately for granted/denied/continue states
  4. Clears needsUserAction flag when permission decisions are made

The JSON.stringify for nested objects in permissionExtra (lines 1042-1044) ensures serialization compatibility with the message block system.


1688-1729: LGTM! Thread-aware message editing prevents cross-conversation pollution.

The updated handleMessageEdited correctly:

  1. Guards against missing context (lines 1691-1692, 1704)
  2. Filters out edits from other conversations (lines 1707-1709)
  3. Properly handles variant messages by updating the parent main message (lines 1712-1723)
  4. Updates view cache only for the active thread's messages

This prevents stale or incorrect message updates when multiple conversations are open.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

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

Caution

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

⚠️ Outside diff range comments (1)
src/main/presenter/agentPresenter/loop/toolCallProcessor.ts (1)

287-327: Double stringification bug in appendNativeFunctionCallMessages.

The method receives toolResponse: { content: unknown } where content is already a string (from toolContentForModel which comes from stringifyToolContent). However, line 321 calls stringifyToolContent(toolResponse.content) again. This will double-stringify content that's already a string, which is redundant but harmless for strings. However, if the intent is to use the offloaded stub directly, this re-stringification is unnecessary.

🐛 Suggested fix

Since toolResponse.content is already stringified when passed from lines 194-196, you can use it directly:

-    const toolContent = this.stringifyToolContent(toolResponse.content)
     conversationMessages.push({
       role: 'tool',
-      content: toolContent,
+      content: toolResponse.content as string,
       tool_call_id: toolCall.id
     })

Alternatively, rename the parameter to clarify that it's already a string:

   private appendNativeFunctionCallMessages(
     conversationMessages: ChatMessage[],
     toolCall: { id: string; name: string; arguments: string },
-    toolResponse: { content: unknown }
+    toolResponse: { content: string }
   ): void {
🤖 Fix all issues with AI agents
In `@docs/specs/tool-output-guardrails/spec.md`:
- Around line 3-4: Replace the incorrect date string "2025-03-08" in the spec
header with the correct date "2026-01-15" so the document status header shows
the accurate creation/update date (update the line containing 日期: 2025-03-08 to
日期: 2026-01-15).
♻️ Duplicate comments (1)
src/main/presenter/toolPresenter/index.ts (1)

164-175: Consider platform-agnostic fallback path.

The fallback path '~/.deepchat/sessions/<conversationId>/tool_<toolCallId>.offload' uses Unix-style ~ which may not be accurate on Windows. However, since conversationId is always provided by the caller (per messageBuilder.ts), this fallback is defensive and unlikely to be used in practice.

The existing past review comment about prompt clarity is still valid - the LLM receives the actual path in the stub message itself, so the system prompt could be more explicit about using that concrete path.

♻️ Optional: Platform-agnostic fallback
   buildToolSystemPrompt(context: { conversationId?: string }): string {
     const conversationId = context.conversationId || '<conversationId>'
     const offloadPath =
       resolveToolOffloadTemplatePath(conversationId) ??
-      '~/.deepchat/sessions/<conversationId>/tool_<toolCallId>.offload'
+      '<session_directory>/tool_<toolCallId>.offload'
 
     return [
       'Tool outputs may be offloaded when large.',
-      `When you see an offload stub, read the full output from: ${offloadPath}`,
+      `When you see "[Tool output offloaded]" with "Full output saved to: <path>", use that exact path to read the full output.`,
       'Use file tools to read that path. Access is limited to the current conversation session.'
     ].join('\n')
   }
🧹 Nitpick comments (4)
test/main/presenter/agentPresenter/loop/toolCallProcessor.test.ts (1)

45-95: LGTM - comprehensive offload test!

The test thoroughly validates the offload behavior:

  • Uses 3001 characters (just above the 3000 threshold)
  • Verifies the stub contains the offload indicator, character count, and file path
  • Confirms tool_call_response_raw preserves the original data
  • Validates the offloaded content is correctly written to disk
  • Checks that the tool message in conversation history contains the offload indicator

This aligns with the spec requirements and tests the key integration points.

Consider adding edge case test.

An additional test for exactly 3000 characters (which should NOT trigger offload) would strengthen the boundary condition coverage.

♻️ Optional: Boundary condition test
it('does not offload tool responses at exactly 3000 characters', async () => {
  const exactOutput = 'x'.repeat(3000)
  const rawData = { content: exactOutput } as MCPToolResponse
  const processor = new ToolCallProcessor({
    getAllToolDefinitions: async () => [toolDefinition],
    callTool: async () => ({ content: exactOutput, rawData })
  })

  const conversationMessages: ChatMessage[] = [{ role: 'assistant', content: 'hello' }]
  const conversationId = 'conv-456'
  const modelConfig = { functionCall: true } as ModelConfig

  const events: any[] = []
  for await (const event of processor.process({
    eventId: 'event-2',
    toolCalls: [{ id: 'tool-2', name: 'mock_tool', arguments: '{}' }],
    enabledMcpTools: [],
    conversationMessages,
    modelConfig,
    abortSignal: new AbortController().signal,
    currentToolCallCount: 0,
    maxToolCalls: 5,
    conversationId
  })) {
    events.push(event)
  }

  const endEvent = events.find(
    (event) => event.type === 'response' && event.data?.tool_call === 'end'
  )
  expect(endEvent).toBeDefined()
  expect(endEvent.data.tool_call_response).not.toContain('[Tool output offloaded]')
})
src/main/presenter/agentPresenter/acp/agentFileSystemHandler.ts (2)

221-223: Consider validating the parent path for read access in the catch block.

When the target path doesn't exist and we fall back to validating the parent directory (lines 245-259), the session read check is only performed on the original normalizedRequested path. If the parent directory resolution succeeds, we return normalizedRequested without a session read check on the resolved parent.

This is likely fine since we're creating new files (write operations), but for consistency with read operations, you may want to ensure the catch block doesn't apply session restrictions for paths that don't exist yet.

Also applies to: 235-237


1134-1144: Unused filtering logic - consider simplification.

The validMatches mapping currently just returns the filePath unchanged without any filtering. This appears to be leftover from removed allowlist filtering logic. Consider simplifying:

♻️ Suggested simplification
-      // Preserve matches without allowlist filtering for read operations.
-      const validMatches = await Promise.all(
-        matches.map(async (filePath) => {
-          return filePath
-        })
-      )
-
-      const filteredMatches = validMatches.filter((match): match is string => match !== null)
+      const filteredMatches = matches
src/main/presenter/agentPresenter/loop/toolCallProcessor.ts (1)

329-373: Same pattern issue in appendLegacyFunctionCallMessages.

The parameter toolResponse: { content: unknown; rawData?: unknown } receives already-stringified content but the type suggests it could be anything. Consider updating the type to { content: string } for clarity, since callers (lines 214-216) always pass stringified content.

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 54b9dab and 71eba1d.

📒 Files selected for processing (9)
  • docs/specs/tool-output-guardrails/plan.md
  • docs/specs/tool-output-guardrails/spec.md
  • docs/specs/tool-output-guardrails/tasks.md
  • src/main/presenter/agentPresenter/acp/agentFileSystemHandler.ts
  • src/main/presenter/agentPresenter/loop/toolCallProcessor.ts
  • src/main/presenter/sessionPresenter/index.ts
  • src/main/presenter/sessionPresenter/sessionPaths.ts
  • src/main/presenter/toolPresenter/index.ts
  • test/main/presenter/agentPresenter/loop/toolCallProcessor.test.ts
✅ Files skipped from review due to trivial changes (1)
  • docs/specs/tool-output-guardrails/tasks.md
🧰 Additional context used
📓 Path-based instructions (11)
**/*.{js,ts,tsx,jsx,vue,mjs,cjs}

📄 CodeRabbit inference engine (.cursor/rules/development-setup.mdc)

All logs and comments must be in English

Files:

  • src/main/presenter/toolPresenter/index.ts
  • src/main/presenter/sessionPresenter/sessionPaths.ts
  • src/main/presenter/agentPresenter/acp/agentFileSystemHandler.ts
  • test/main/presenter/agentPresenter/loop/toolCallProcessor.test.ts
  • src/main/presenter/sessionPresenter/index.ts
  • src/main/presenter/agentPresenter/loop/toolCallProcessor.ts
**/*.{js,ts,tsx,jsx,mjs,cjs}

📄 CodeRabbit inference engine (.cursor/rules/development-setup.mdc)

Use OxLint as the linter

Files:

  • src/main/presenter/toolPresenter/index.ts
  • src/main/presenter/sessionPresenter/sessionPaths.ts
  • src/main/presenter/agentPresenter/acp/agentFileSystemHandler.ts
  • test/main/presenter/agentPresenter/loop/toolCallProcessor.test.ts
  • src/main/presenter/sessionPresenter/index.ts
  • src/main/presenter/agentPresenter/loop/toolCallProcessor.ts
**/*.{js,ts,tsx,jsx,vue,json,mjs,cjs}

📄 CodeRabbit inference engine (.cursor/rules/development-setup.mdc)

Use Prettier as the code formatter

Files:

  • src/main/presenter/toolPresenter/index.ts
  • src/main/presenter/sessionPresenter/sessionPaths.ts
  • src/main/presenter/agentPresenter/acp/agentFileSystemHandler.ts
  • test/main/presenter/agentPresenter/loop/toolCallProcessor.test.ts
  • src/main/presenter/sessionPresenter/index.ts
  • src/main/presenter/agentPresenter/loop/toolCallProcessor.ts
src/main/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Electron main process code should reside in src/main/, with presenters organized in presenter/ subdirectory (Window, Tab, Thread, Mcp, Config, LLMProvider), and app events managed via eventbus.ts

Files:

  • src/main/presenter/toolPresenter/index.ts
  • src/main/presenter/sessionPresenter/sessionPaths.ts
  • src/main/presenter/agentPresenter/acp/agentFileSystemHandler.ts
  • src/main/presenter/sessionPresenter/index.ts
  • src/main/presenter/agentPresenter/loop/toolCallProcessor.ts
**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,vue}: Use camelCase for variable and function names; use PascalCase for types and classes; use SCREAMING_SNAKE_CASE for constants
Configure Prettier with single quotes, no semicolons, and line width of 100 characters. Run pnpm run format after completing features

Files:

  • src/main/presenter/toolPresenter/index.ts
  • src/main/presenter/sessionPresenter/sessionPaths.ts
  • src/main/presenter/agentPresenter/acp/agentFileSystemHandler.ts
  • test/main/presenter/agentPresenter/loop/toolCallProcessor.test.ts
  • src/main/presenter/sessionPresenter/index.ts
  • src/main/presenter/agentPresenter/loop/toolCallProcessor.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (AGENTS.md)

Use OxLint for linting JavaScript and TypeScript files; ensure lint-staged hooks and typecheck pass before commits

Enable strict TypeScript type checking

Files:

  • src/main/presenter/toolPresenter/index.ts
  • src/main/presenter/sessionPresenter/sessionPaths.ts
  • src/main/presenter/agentPresenter/acp/agentFileSystemHandler.ts
  • test/main/presenter/agentPresenter/loop/toolCallProcessor.test.ts
  • src/main/presenter/sessionPresenter/index.ts
  • src/main/presenter/agentPresenter/loop/toolCallProcessor.ts
**/*.{ts,tsx,js,jsx,vue}

📄 CodeRabbit inference engine (CLAUDE.md)

Use English for all logs and comments

Files:

  • src/main/presenter/toolPresenter/index.ts
  • src/main/presenter/sessionPresenter/sessionPaths.ts
  • src/main/presenter/agentPresenter/acp/agentFileSystemHandler.ts
  • test/main/presenter/agentPresenter/loop/toolCallProcessor.test.ts
  • src/main/presenter/sessionPresenter/index.ts
  • src/main/presenter/agentPresenter/loop/toolCallProcessor.ts
src/main/presenter/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Implement all system capabilities as main-process presenters following the Presenter Pattern

Files:

  • src/main/presenter/toolPresenter/index.ts
  • src/main/presenter/sessionPresenter/sessionPaths.ts
  • src/main/presenter/agentPresenter/acp/agentFileSystemHandler.ts
  • src/main/presenter/sessionPresenter/index.ts
  • src/main/presenter/agentPresenter/loop/toolCallProcessor.ts
{src/main,src/renderer,test}/**/*.{ts,tsx,js}

📄 CodeRabbit inference engine (CLAUDE.md)

Use IPC communication: Renderer calls main process via usePresenter composable, Main sends to Renderer via EventBus

Files:

  • src/main/presenter/toolPresenter/index.ts
  • src/main/presenter/sessionPresenter/sessionPaths.ts
  • src/main/presenter/agentPresenter/acp/agentFileSystemHandler.ts
  • test/main/presenter/agentPresenter/loop/toolCallProcessor.test.ts
  • src/main/presenter/sessionPresenter/index.ts
  • src/main/presenter/agentPresenter/loop/toolCallProcessor.ts
test/**/*.test.ts

📄 CodeRabbit inference engine (AGENTS.md)

Vitest test suites should be organized in test/main/** and test/renderer/** mirroring source structure, with file names following *.test.ts or *.spec.ts pattern

Files:

  • test/main/presenter/agentPresenter/loop/toolCallProcessor.test.ts
**/*.test.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Write tests using Vitest with separate test suites for main and renderer processes

Files:

  • test/main/presenter/agentPresenter/loop/toolCallProcessor.test.ts
🧠 Learnings (6)
📚 Learning: 2026-01-13T09:23:07.415Z
Learnt from: CR
Repo: ThinkInAIXYZ/deepchat PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-13T09:23:07.415Z
Learning: Applies to src/main/presenter/**/*.ts : Implement all system capabilities as main-process presenters following the Presenter Pattern

Applied to files:

  • src/main/presenter/toolPresenter/index.ts
  • src/main/presenter/sessionPresenter/sessionPaths.ts
  • src/main/presenter/sessionPresenter/index.ts
📚 Learning: 2026-01-13T09:23:07.415Z
Learnt from: CR
Repo: ThinkInAIXYZ/deepchat PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-13T09:23:07.415Z
Learning: Applies to src/main/presenter/mcpPresenter/inMemoryServers/**/*.ts : Implement new MCP tools in the inMemoryServers directory and register in mcpPresenter/index.ts

Applied to files:

  • src/main/presenter/toolPresenter/index.ts
  • src/main/presenter/sessionPresenter/sessionPaths.ts
  • test/main/presenter/agentPresenter/loop/toolCallProcessor.test.ts
  • src/main/presenter/sessionPresenter/index.ts
  • src/main/presenter/agentPresenter/loop/toolCallProcessor.ts
📚 Learning: 2026-01-05T02:41:45.219Z
Learnt from: CR
Repo: ThinkInAIXYZ/deepchat PR: 0
File: AGENTS.md:0-0
Timestamp: 2026-01-05T02:41:45.219Z
Learning: Applies to src/main/**/*.ts : Electron main process code should reside in `src/main/`, with presenters organized in `presenter/` subdirectory (Window, Tab, Thread, Mcp, Config, LLMProvider), and app events managed via `eventbus.ts`

Applied to files:

  • src/main/presenter/sessionPresenter/sessionPaths.ts
  • src/main/presenter/sessionPresenter/index.ts
📚 Learning: 2026-01-13T09:23:07.415Z
Learnt from: CR
Repo: ThinkInAIXYZ/deepchat PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-13T09:23:07.415Z
Learning: Applies to **/*.test.ts : Write tests using Vitest with separate test suites for main and renderer processes

Applied to files:

  • test/main/presenter/agentPresenter/loop/toolCallProcessor.test.ts
📚 Learning: 2026-01-13T09:23:07.415Z
Learnt from: CR
Repo: ThinkInAIXYZ/deepchat PR: 0
File: CLAUDE.md:0-0
Timestamp: 2026-01-13T09:23:07.415Z
Learning: Applies to {src/main,src/renderer,test}/**/*.{ts,tsx,js} : Use IPC communication: Renderer calls main process via `usePresenter` composable, Main sends to Renderer via EventBus

Applied to files:

  • src/main/presenter/sessionPresenter/index.ts
📚 Learning: 2026-01-05T02:41:31.661Z
Learnt from: CR
Repo: ThinkInAIXYZ/deepchat PR: 0
File: .cursor/rules/vue-stack-guide.mdc:0-0
Timestamp: 2026-01-05T02:41:31.661Z
Learning: Applies to src/renderer/src/**/*.{vue,ts,tsx} : Use `usePresenter` composable for main process communication instead of direct IPC calls

Applied to files:

  • src/main/presenter/sessionPresenter/index.ts
🧬 Code graph analysis (6)
src/main/presenter/toolPresenter/index.ts (1)
src/main/presenter/sessionPresenter/sessionPaths.ts (1)
  • resolveToolOffloadTemplatePath (28-32)
src/main/presenter/sessionPresenter/sessionPaths.ts (1)
test/mocks/electron.ts (1)
  • app (2-10)
src/main/presenter/agentPresenter/acp/agentFileSystemHandler.ts (1)
src/main/presenter/sessionPresenter/sessionPaths.ts (1)
  • getSessionsRoot (4-6)
test/main/presenter/agentPresenter/loop/toolCallProcessor.test.ts (3)
test/mocks/electron.ts (1)
  • app (2-10)
src/main/presenter/agentPresenter/loop/toolCallProcessor.ts (1)
  • ToolCallProcessor (46-487)
src/shared/types/presenters/legacy.presenters.d.ts (1)
  • ModelConfig (148-168)
src/main/presenter/sessionPresenter/index.ts (1)
src/main/presenter/sessionPresenter/sessionPaths.ts (1)
  • resolveSessionDir (8-19)
src/main/presenter/agentPresenter/loop/toolCallProcessor.ts (1)
src/main/presenter/sessionPresenter/sessionPaths.ts (1)
  • resolveToolOffloadPath (21-26)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build-check (x64)
🔇 Additional comments (21)
src/main/presenter/sessionPresenter/index.ts (3)

18-28: LGTM!

The imports are correctly added for the new offload file cleanup functionality. The fs promises API and resolveSessionDir utility are appropriately imported to support the session cleanup logic.


434-439: LGTM!

The deletion order is correct: clear permissions first, then delete offload files, and finally delete the conversation from the database. This ensures proper cleanup of all session-related artifacts.


496-508: LGTM!

The deleteSessionOffloadFiles method correctly:

  1. Returns early if resolveSessionDir returns null (invalid/malicious conversationId)
  2. Uses fs.rm with recursive: true, force: true for safe directory removal
  3. Swallows errors with a warning log, which is appropriate since offload file cleanup failure shouldn't block conversation deletion

Note: The AI summary mentioned a "duplicate insertion" of this method, but I only see one definition here, which appears to be a summary inaccuracy.

src/main/presenter/toolPresenter/index.ts (1)

9-9: LGTM!

The import is correctly added to support the new buildToolSystemPrompt method.

docs/specs/tool-output-guardrails/spec.md (1)

39-59: LGTM!

The specification is well-structured and clearly documents:

  • directory_tree depth semantics (root=0, max=3, default=1)
  • Offload threshold (3000 characters)
  • File extension (.offload) - now consistent with the implementation
  • Session-scoped file access restrictions

The acceptance criteria align with the code implementation.

test/main/presenter/agentPresenter/loop/toolCallProcessor.test.ts (1)

35-43: LGTM!

The test setup correctly:

  1. Creates an isolated temp directory for each test
  2. Mocks app.getPath to return the temp directory
  3. Cleans up the temp directory after each test

This ensures test isolation and prevents file system pollution.

src/main/presenter/sessionPresenter/sessionPaths.ts (4)

4-6: LGTM!

The getSessionsRoot function correctly uses app.getPath('home') for cross-platform home directory resolution, combined with path.resolve for proper path construction.


8-19: LGTM - robust path traversal protection!

The resolveSessionDir function correctly implements security measures:

  1. Rejects empty/whitespace-only conversation IDs
  2. Uses path.resolve to normalize the path and collapse .. sequences
  3. Validates the resolved path stays within the sessions root
  4. Handles the edge case where resolvedSessionDir === sessionsRoot (e.g., when conversationId is ".")

This effectively prevents directory traversal attacks.


21-26: LGTM!

The resolveToolOffloadPath function correctly:

  1. Delegates base path validation to resolveSessionDir
  2. Sanitizes toolCallId by replacing both forward and back slashes with underscores, preventing path injection

The regex [\\/] handles both Unix (/) and Windows (\) path separators.


28-32: LGTM!

The template path function correctly returns a placeholder pattern for use in system prompts, leveraging the same secure resolveSessionDir validation.

src/main/presenter/agentPresenter/acp/agentFileSystemHandler.ts (5)

92-95: LGTM! Depth parameter properly constrained.

The schema correctly limits depth to integers between 0 and 3 with a sensible default of 1. This aligns with the plan to prevent unbounded recursion in directory tree operations.


157-160: LGTM! PathValidationOptions properly extended.

The optional accessType field cleanly extends the existing interface for read/write differentiation without breaking existing callers.


162-176: LGTM! Constructor and session scoping look correct.

The constructor properly initializes the conversation context and caches the normalized sessions root path. Good defensive handling with optional conversationId.


263-284: Session read access control is well implemented.

The logic correctly:

  1. Allows access outside sessions root without restriction
  2. Requires an active conversation for any session file access
  3. Restricts reads to only the current conversation's directory

One minor observation: the error messages are clear and will help with debugging access issues.


1044-1079: LGTM! Directory tree depth control correctly implemented.

The depth limiting logic is sound:

  • currentDepth starts at 0 for the root
  • Recursion only proceeds when currentDepth < depth
  • This means depth: 1 allows one level of children (root + 1), which matches the plan

This effectively prevents unbounded recursion that could cause the 10MB failure mentioned in the plan.

src/main/presenter/agentPresenter/loop/toolCallProcessor.ts (4)

43-44: LGTM! Offload thresholds align with the plan.

The threshold (3000) and preview length (1024) match the specification in plan.md. These are reasonable values for balancing context window usage while preserving enough preview for the model.


375-397: LGTM! Offload logic is robust with good fallback behavior.

The implementation correctly:

  1. Returns original content if under threshold or no conversationId
  2. Gracefully falls back to original content on file I/O errors
  3. Creates the session directory recursively before writing

This matches the plan's risk mitigation for missing conversationId scenarios.


399-407: LGTM! Stub format is clear and informative.

The stub includes all necessary information for the model to understand the offload and retrieve the full content via file tools.


110-123: LGTM! Tool call finished notification is well-implemented.

Good defensive programming with the try-catch wrapper to prevent callback errors from breaking the main flow.

docs/specs/tool-output-guardrails/plan.md (2)

1-87: Plan document is well-structured and aligns with implementation.

The plan accurately describes the implemented features:

  • Directory tree depth (0-3, default 1) ✓
  • Offload threshold (3000 chars) ✓
  • Preview length (1024 chars) ✓
  • File path format (.offload extension) ✓
  • Session-scoped read access ✓

The extension inconsistency noted in past review comments has been resolved - both lines 29 and 69 now correctly use .offload.


85-87: Good call-out on conversationId fallback behavior.

The plan correctly notes the need to define fallback behavior when conversationId is missing. The implementation in toolCallProcessor.ts handles this by simply returning the original content without offloading (line 381), which matches the suggested "truncate only, don't offload" approach mentioned here.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

- Raise offload threshold from 3000 to 5000 characters
- Add whitelist for tools that require offload (execute_command, directory_tree, etc.)
- Add offset/limit params to read_file for pagination support
- Auto-truncate large files at 4500 chars with metadata hint
- Prevents infinite offload loop when reading offloaded files
@zerob13 zerob13 merged commit 5421dce into dev Jan 15, 2026
2 checks passed
zhangmo8 pushed a commit that referenced this pull request Jan 16, 2026
* docs(spec): add tool output guardrails

* fix(agent): tool output guardrails

* fix(agent): standardize tool offload extension

* feat: extract path to session

* fix: review issue

* fix: error response on renderer

* feat: add read_file pagination and whitelist-based tool offload

- Raise offload threshold from 3000 to 5000 characters
- Add whitelist for tools that require offload (execute_command, directory_tree, etc.)
- Add offset/limit params to read_file for pagination support
- Auto-truncate large files at 4500 chars with metadata hint
- Prevents infinite offload loop when reading offloaded files

* fix: independent reasoning time for each thinking block in agent loop
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants