Skip to content

feat: add question tools #1298

Merged
zerob13 merged 11 commits intodevfrom
feat/question-tool
Feb 5, 2026
Merged

feat: add question tools #1298
zerob13 merged 11 commits intodevfrom
feat/question-tool

Conversation

@zerob13
Copy link
Collaborator

@zerob13 zerob13 commented Feb 4, 2026

Summary by CodeRabbit

Release Notes

  • New Features

    • Agents can now ask questions with customizable options, allowing users to select answers
    • Users can accept or reject question responses
    • Added new session state for waiting on question responses
  • User Interface

    • New question dialog component with selectable options
    • Internationalization support added for question interactions across all supported languages

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 4, 2026

📝 Walkthrough

Walkthrough

This PR introduces a built-in "question" tool feature enabling the assistant to ask users for input and manage pending questions through resolution or rejection. The implementation spans tool definition, event handling, session state management, UI rendering, and internationalization across multiple languages.

Changes

Cohort / File(s) Summary
Question Tool Definition & Validation
src/main/presenter/agentPresenter/acp/agentToolManager.ts, src/main/presenter/agentPresenter/tools/questionTool.ts
Introduces QUESTION_TOOL_NAME, questionToolSchema, and parseQuestionToolArgs for schema-based validation. Adds getQuestionToolDefinitions method and integrates question tool into getAllToolDefinitions and callTool flows.
Tool Call Processing & Routing
src/main/presenter/agentPresenter/loop/toolCallProcessor.ts, src/main/presenter/agentPresenter/loop/toolCallHandler.ts
Adds specialized handling for question_request tool calls: validates standalone requirement, parses arguments, emits question-required event, and introduces processQuestionRequest to finalize blocks and create question_request action blocks.
Session & State Management
src/main/presenter/agentPresenter/session/sessionContext.ts, src/main/presenter/agentPresenter/session/sessionManager.ts
Extends SessionStatus with 'waiting_question' state. Adds pendingQuestion tracking to runtime context with hydration logic to recover pending questions from persisted state.
Event Streaming & Delta Processing
src/main/presenter/agentPresenter/streaming/llmEventHandler.ts, src/main/presenter/agentPresenter/streaming/streamUpdateScheduler.ts
Handles question_request and question_error events in streaming pipeline. Tracks pending questions alongside permissions and manages state transitions between waiting_permission and waiting_question.
Agent Presenter Public API
src/main/presenter/agentPresenter/index.ts, src/main/presenter/sessionPresenter/index.ts, src/main/presenter/sqlitePresenter/*
Adds resolveQuestion, rejectQuestion public methods for question resolution. Adds getLastAssistantMessage methods across presenter hierarchy for message retrieval.
Shared Type Definitions
src/shared/types/core/question.ts, src/shared/types/core/agent-events.ts, src/shared/types/core/chat.ts, src/shared/chat.d.ts
Defines QuestionOption, QuestionInfo, QuestionResolution, QuestionAnswer types. Extends LLMAgentEventData and AssistantMessageBlock with question_request field and new action_type variant.
Public Presenter Interfaces
src/shared/types/presenters/agent.presenter.d.ts, src/shared/types/presenters/session.presenter.d.ts, src/shared/types/presenters/legacy.presenters.d.ts
Updates IAgentPresenter, ISessionPresenter, and ISQLitePresenter interfaces to expose new question resolution methods and getLastAssistantMessage capabilities.
Renderer Chat Store & Message Flow
src/renderer/src/stores/chat.ts, src/renderer/src/components/message/MessageBlockQuestionRequest.vue, src/renderer/src/components/message/MessageItemAssistant.vue
Adds question_request field to streaming message payload. Creates MessageBlockQuestionRequest component for rendering selectable question options with submit/reject actions. Integrates component into MessageItemAssistant rendering flow.
Internationalization
src/renderer/src/i18n/{da-DK,en-US,fa-IR,fr-FR,he-IL,ja-JP,ko-KR,pt-BR,ru-RU,zh-CN,zh-HK,zh-TW}/common.json, src/renderer/src/i18n/{da-DK,en-US,fa-IR,fr-FR,he-IL,ja-JP,ko-KR,pt-BR,ru-RU,zh-CN,zh-HK,zh-TW}/components.json, src/types/i18n.d.ts
Adds invalidQuestionRequest error message and messageBlockQuestionRequest UI strings (title, send, reject, selected, rejected, customPlaceholder, answerLabel) across 12 language locales. Updates i18n TypeScript definitions.
Message Block Finalization & Status Transitions
src/shared/chat/messageBlocks.ts, test/renderer/message/messageBlockSnapshot.test.ts, test/renderer/message/eventMappingTable.test.ts, test/renderer/message/rendererContract.test.ts
Extends block finalization to preserve question_request blocks. Expands status transition rules to allow pending → success. Adds test coverage for question-required tool call mapping and status transitions.
Tool Call Processing Tests
test/main/presenter/agentPresenter/loop/toolCallProcessor.test.ts
Introduces test suite covering question tool emission (pausing loop, no tool invocation) and rejection of non-standalone question calls (error emission, loop continuation).

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant LLM as LLM Agent
    participant ToolProcessor as Tool Processor
    participant SessionManager as Session Manager
    participant Renderer as Renderer

    LLM->>ToolProcessor: emit tool_call<br/>(tool_name: 'question', args: {...})
    activate ToolProcessor
    ToolProcessor->>ToolProcessor: validate standalone<br/>parse args
    ToolProcessor->>LLM: emit question_required<br/>event
    deactivate ToolProcessor
    
    LLM->>SessionManager: record pendingQuestion<br/>(messageId, toolCallId)
    SessionManager->>SessionManager: set status to<br/>waiting_question
    
    SessionManager->>Renderer: update session<br/>status & pendingQuestion
    Renderer->>Renderer: render MessageBlockQuestionRequest<br/>with options
    Renderer->>User: display question & selectable options
    
    User->>Renderer: select option & submit
    Renderer->>SessionManager: resolveQuestion<br/>(messageId, toolCallId, answerText)
    activate SessionManager
    SessionManager->>SessionManager: update question block<br/>resolution status
    SessionManager->>SessionManager: clear pendingQuestion<br/>set status to idle
    deactivate SessionManager
    SessionManager->>Renderer: update message block
    Renderer->>User: display answered state
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • #1265: Modifies agent presenter streaming and tool-call handling code (toolCallHandler.ts, llmEventHandler.ts) with overlapping event processing changes.
  • #1266: Modifies AgentToolManager tooling flow including getAllToolDefinitions and callTool methods that are also extended in this PR.
  • #1276: Modifies AgentToolManager with new tool behaviors and discovery flow similar to question tool integration in this PR.

Suggested labels

codex, feature, tool-integration

Poem

🐰 Questions hop into the flow,
Waiting gently, high and low,
Options dance, then answers fly,
Assistant asks, "Oh, tell me why?" ✨

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% 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 accurately reflects the main change: introducing a new question tool feature with comprehensive implementation across agent, presenter, and renderer layers.

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

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feat/question-tool

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

Note

Due to the large number of review comments, Critical, Major severity comments were prioritized as inline comments.

🤖 Fix all issues with AI agents
In `@src/renderer/src/i18n/fa-IR/components.json`:
- Around line 61-69: Translate the English values in the
messageBlockQuestionRequest localization object to Persian: replace
messageBlockQuestionRequest.title, .send, .reject, .selected, .rejected,
.customPlaceholder, and .answerLabel with appropriate Farsi strings (e.g.,
"سؤال", "ارسال", "رد کردن", "انتخاب شد", "رد شد", "پاسخ خود را بنویسید...",
"پاسخ") ensuring key names remain unchanged and consistent with other locale
files; verify translations with a native Persian speaker.

In `@src/renderer/src/i18n/he-IL/components.json`:
- Around line 61-69: Replace the English values under the
messageBlockQuestionRequest object with Hebrew translations: update "title",
"send", "reject", "selected", "rejected", "customPlaceholder", and "answerLabel"
to their Hebrew equivalents (e.g., "שאלה", "שלח", "דחה", "נבחר", "נדחה", "הקלד
את תגובתך...", "תשובה" or other approved translations), ensuring the keys
(messageBlockQuestionRequest.*) remain unchanged and the file stays valid UTF-8
JSON.

In `@src/renderer/src/i18n/ru-RU/common.json`:
- Around line 41-42: The localization entries for the key
"invalidQuestionRequest" are left in English in multiple non‑English locale
JSONs (e.g., ru‑RU, da‑DK, fa‑IR, fr‑FR, he‑IL, ja‑JP, ko‑KR, pt‑BR); update
each locale's JSON to replace "Invalid question request" with an appropriate
native translation (for ru‑RU use "Неверный запрос вопроса"), keeping the exact
key name "invalidQuestionRequest", preserving valid JSON syntax and punctuation,
and ensure all other locale files that currently contain the English string are
similarly updated.

In `@src/renderer/src/i18n/zh-TW/components.json`:
- Around line 61-69: The translations under the messageBlockQuestionRequest
object use Simplified Chinese; update each key value to Traditional Chinese:
replace "问题"→"問題" for title, "发送"→"發送" for send, "拒绝"→"拒絕" for reject,
"已选择"→"已選擇" for selected, "已拒绝"→"已拒絕" for rejected, and "输入你的回答..."→"輸入你的回答..."
for customPlaceholder while keeping answerLabel unchanged; ensure only the
string values are modified in messageBlockQuestionRequest.

In `@src/renderer/src/stores/chat.ts`:
- Around line 1038-1046: The renderer currently pushes raw msg.question_error
strings (in the block that calls finalizeAssistantMessageBlocks and pushes an
'error' message into assistantMsg.content) which causes untranslated
external-agent errors to show; update llmEventHandler (preferred) or the
renderer path before assistantMsg.content.push so that incoming
msg.question_error values are mapped to the existing i18n key
'common.error.invalidQuestionRequest' (or other appropriate keys) by calling the
app's i18n translate function (e.g., t or i18n.t) or by mapping known external
error strings to i18n keys; ensure the converted/translated string is what is
pushed into assistantMsg.content to match toolCallProcessor behavior.
🟡 Minor comments (20)
src/renderer/src/i18n/zh-TW/chat.json-151-160 (1)

151-160: ⚠️ Potential issue | 🟡 Minor

Align new keys with the dot‑separated naming convention.

The new audio and call keys are nested objects rather than dot‑separated keys. If the i18n layer expects dot‑separated keys, consider flattening these entries (e.g., audio.play, call.title) and updating references accordingly.

As per coding guidelines: Use dot-separated hierarchical structure for translation key naming with lowercase letters and descriptive names grouped by feature/context.

src/renderer/src/components/icons/ModelIcon.vue-71-74 (1)

71-74: ⚠️ Potential issue | 🟡 Minor

Translate the nearby comment to English.

The comment at Line 73 is non-English. Please translate it for consistency.

Suggested change
-// 导入所有图标
+// Import all icons

As per coding guidelines: 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.

src/renderer/src/i18n/fa-IR/settings.json-11-11 (1)

11-11: ⚠️ Potential issue | 🟡 Minor

Rename autoScrollEnabled to a lowercase dot‑separated key.
This key violates the required naming convention and should be updated (and referenced paths adjusted) to match the lowercase dot‑separated structure.

Proposed update (also update call sites + other locales)
-    "autoScrollEnabled": "پیمایش خودکار هنگام تولید",
+    "auto": {
+      "scroll": {
+        "enabled": "پیمایش خودکار هنگام تولید"
+      }
+    },

As per coding guidelines: Use dot-separated hierarchical structure for translation key naming with lowercase letters and descriptive names grouped by feature/context (e.g., common.button.submit, chat.send.placeholder).

src/renderer/src/i18n/ru-RU/components.json-61-69 (1)

61-69: ⚠️ Potential issue | 🟡 Minor

Russian locale file contains untranslated English strings.

The messageBlockQuestionRequest block has English values instead of Russian translations. Russian users will see English text for the question request UI.

🌐 Suggested Russian translations
   "messageBlockQuestionRequest": {
-    "title": "Question",
-    "send": "Send",
-    "reject": "Reject",
-    "selected": "Selected",
-    "rejected": "Rejected",
-    "customPlaceholder": "Type your answer...",
-    "answerLabel": "Answer"
+    "title": "Вопрос",
+    "send": "Отправить",
+    "reject": "Отклонить",
+    "selected": "Выбрано",
+    "rejected": "Отклонено",
+    "customPlaceholder": "Введите ответ...",
+    "answerLabel": "Ответ"
   }
src/renderer/src/i18n/zh-HK/common.json-44-45 (1)

44-45: ⚠️ Potential issue | 🟡 Minor

Use Traditional Chinese characters for zh-HK locale.

The value "问题请求无效" uses Simplified Chinese characters, but this is the zh-HK (Hong Kong) locale which should use Traditional Chinese. The rest of the file consistently uses Traditional Chinese.

🔤 Proposed fix
-    "invalidQuestionRequest": "问题请求无效"
+    "invalidQuestionRequest": "問題請求無效"
src/renderer/src/i18n/fa-IR/common.json-45-46 (1)

45-46: ⚠️ Potential issue | 🟡 Minor

Translate to Persian for fa-IR locale.

The value "Invalid question request" is in English, but this is the Persian (fa-IR) locale. All other error messages in this file are properly translated to Persian.

🌍 Proposed fix
-    "invalidQuestionRequest": "Invalid question request"
+    "invalidQuestionRequest": "درخواست سؤال نامعتبر"
src/renderer/src/i18n/zh-TW/common.json-41-42 (1)

41-42: ⚠️ Potential issue | 🟡 Minor

Use Traditional Chinese for zh‑TW.
The new value is in Simplified Chinese; it should be Traditional for this locale.

✅ Suggested Traditional Chinese value
-    "invalidQuestionRequest": "问题请求无效"
+    "invalidQuestionRequest": "問題請求無效"
src/renderer/src/i18n/he-IL/common.json-45-46 (1)

45-46: ⚠️ Potential issue | 🟡 Minor

Localize invalidQuestionRequest for he‑IL.
The value is still English, which will show mixed-language errors in Hebrew UI.

✅ Suggested translation (please verify with locale reviewer)
-    "invalidQuestionRequest": "Invalid question request"
+    "invalidQuestionRequest": "בקשת שאלה לא תקינה"
src/renderer/src/i18n/ja-JP/common.json-45-46 (1)

45-46: ⚠️ Potential issue | 🟡 Minor

Localize invalidQuestionRequest for ja-JP.
The value is still English, which will surface mixed-language errors in Japanese UI.

✅ Suggested translation (please verify with locale reviewer)
-    "invalidQuestionRequest": "Invalid question request"
+    "invalidQuestionRequest": "無効な質問リクエスト"
src/renderer/src/i18n/fr-FR/common.json-45-46 (1)

45-46: ⚠️ Potential issue | 🟡 Minor

Localize invalidQuestionRequest for fr-FR.

The new key is still in English, which will surface mixed-language UI in the French locale.

🌍 Suggested translation
-    "invalidQuestionRequest": "Invalid question request"
+    "invalidQuestionRequest": "Demande de question invalide"

Based on learnings: Add new translations to ALL language files (da-DK, en-US, fa-IR, fr-FR, he-IL, ja-JP, ko-KR, pt-BR, ru-RU, zh-CN, zh-HK, zh-TW) with consistent key names across all locales.

src/renderer/src/i18n/pt-BR/components.json-61-68 (1)

61-68: ⚠️ Potential issue | 🟡 Minor

Translate messageBlockQuestionRequest for pt-BR.

The new block is still in English, which breaks locale consistency.

🌍 Suggested translation
-  "messageBlockQuestionRequest": {
-    "title": "Question",
-    "send": "Send",
-    "reject": "Reject",
-    "selected": "Selected",
-    "rejected": "Rejected",
-    "customPlaceholder": "Type your answer...",
-    "answerLabel": "Answer"
-  }
+  "messageBlockQuestionRequest": {
+    "title": "Pergunta",
+    "send": "Enviar",
+    "reject": "Rejeitar",
+    "selected": "Selecionado",
+    "rejected": "Rejeitado",
+    "customPlaceholder": "Digite sua resposta...",
+    "answerLabel": "Resposta"
+  }

Based on learnings: Use English (en-US) as the reference for translation accuracy when adding new keys.

src/renderer/src/i18n/ko-KR/common.json-46-47 (1)

46-47: ⚠️ Potential issue | 🟡 Minor

Localize invalidQuestionRequest for ko-KR.

The new error string is still English in the Korean locale.

🌍 Suggested translation
-    "invalidQuestionRequest": "Invalid question request"
+    "invalidQuestionRequest": "잘못된 질문 요청"

Based on learnings: Add new translations to ALL language files (da-DK, en-US, fa-IR, fr-FR, he-IL, ja-JP, ko-KR, pt-BR, ru-RU, zh-CN, zh-HK, zh-TW) with consistent key names across all locales.

src/renderer/src/i18n/ja-JP/components.json-61-69 (1)

61-69: ⚠️ Potential issue | 🟡 Minor

Missing Japanese translations for messageBlockQuestionRequest block.

All values in this block are in English instead of Japanese. This locale file should contain Japanese translations for these UI strings to provide a proper localized experience for Japanese users.

Expected translations (example):

  • "title": "質問" instead of "Question"
  • "send": "送信" instead of "Send"
  • "reject": "拒否" instead of "Reject"
  • "selected": "選択済み" instead of "Selected"
  • "rejected": "拒否済み" instead of "Rejected"
  • "customPlaceholder": "回答を入力..." instead of "Type your answer..."
  • "answerLabel": "回答" instead of "Answer"

Based on learnings: "Add new translations to ALL language files (da-DK, en-US, fa-IR, fr-FR, he-IL, ja-JP, ko-KR, pt-BR, ru-RU, zh-CN, zh-HK, zh-TW) with consistent key names across all locales."

src/renderer/src/i18n/da-DK/common.json-48-48 (1)

48-48: ⚠️ Potential issue | 🟡 Minor

Missing Danish translation for invalidQuestionRequest.

The value is in English instead of Danish. This should be translated to Danish for consistency with the rest of the locale file.

Suggested translation: "invalidQuestionRequest": "Ugyldig spørgsmålsanmodning"

Based on learnings: "Add new translations to ALL language files with consistent key names across all locales."

src/renderer/src/i18n/da-DK/components.json-61-69 (1)

61-69: ⚠️ Potential issue | 🟡 Minor

Missing Danish translations for messageBlockQuestionRequest block.

All values in this block are in English instead of Danish. This locale file should contain Danish translations for these UI strings.

Expected translations (example):

  • "title": "Spørgsmål" instead of "Question"
  • "send": "Send" (same in Danish)
  • "reject": "Afvis" instead of "Reject"
  • "selected": "Valgt" instead of "Selected"
  • "rejected": "Afvist" instead of "Rejected"
  • "customPlaceholder": "Skriv dit svar..." instead of "Type your answer..."
  • "answerLabel": "Svar" instead of "Answer"

Based on learnings: "Add new translations to ALL language files (da-DK, en-US, fa-IR, fr-FR, he-IL, ja-JP, ko-KR, pt-BR, ru-RU, zh-CN, zh-HK, zh-TW) with consistent key names across all locales."

src/renderer/src/i18n/pt-BR/common.json-46-46 (1)

46-46: ⚠️ Potential issue | 🟡 Minor

Translation not localized to Portuguese.

The value "Invalid question request" is in English but this is the pt-BR (Portuguese-Brazil) locale file. All other error messages in this file are properly translated to Portuguese.

Suggested translation:

-    "invalidQuestionRequest": "Invalid question request"
+    "invalidQuestionRequest": "Solicitação de pergunta inválida"
src/renderer/src/i18n/ko-KR/components.json-61-69 (1)

61-69: ⚠️ Potential issue | 🟡 Minor

Translation not localized to Korean.

All values in the messageBlockQuestionRequest block are in English, but this is the ko-KR (Korean) locale file. Other entries in this file have proper Korean translations.

Suggested Korean translations:

   "messageBlockQuestionRequest": {
-    "title": "Question",
-    "send": "Send",
-    "reject": "Reject",
-    "selected": "Selected",
-    "rejected": "Rejected",
-    "customPlaceholder": "Type your answer...",
-    "answerLabel": "Answer"
+    "title": "질문",
+    "send": "보내기",
+    "reject": "거부",
+    "selected": "선택됨",
+    "rejected": "거부됨",
+    "customPlaceholder": "답변을 입력하세요...",
+    "answerLabel": "답변"
   }
src/renderer/src/i18n/zh-HK/components.json-61-68 (1)

61-68: ⚠️ Potential issue | 🟡 Minor

Use Traditional Chinese for zh-HK question-request strings.

The new values are Simplified Chinese; zh-HK should be Traditional.

Suggested translation
   "messageBlockQuestionRequest": {
-    "title": "问题",
-    "send": "发送",
-    "reject": "拒绝",
-    "selected": "已选择",
-    "rejected": "已拒绝",
-    "customPlaceholder": "输入你的回答...",
-    "answerLabel": "回答"
+    "title": "問題",
+    "send": "發送",
+    "reject": "拒絕",
+    "selected": "已選擇",
+    "rejected": "已拒絕",
+    "customPlaceholder": "輸入你的回答...",
+    "answerLabel": "回答"
   }
src/renderer/src/i18n/fr-FR/components.json-61-68 (1)

61-68: ⚠️ Potential issue | 🟡 Minor

Localize the new question-request strings to French.

The added values are still in English, which will surface mixed-language UI in fr-FR.

Suggested translation
   "messageBlockQuestionRequest": {
     "title": "Question",
-    "send": "Send",
-    "reject": "Reject",
-    "selected": "Selected",
-    "rejected": "Rejected",
-    "customPlaceholder": "Type your answer...",
-    "answerLabel": "Answer"
+    "send": "Envoyer",
+    "reject": "Refuser",
+    "selected": "Sélectionné",
+    "rejected": "Refusé",
+    "customPlaceholder": "Saisissez votre réponse...",
+    "answerLabel": "Réponse"
   }
src/main/presenter/agentPresenter/index.ts-402-405 (1)

402-405: ⚠️ Potential issue | 🟡 Minor

Use the injected SessionManager instead of the global presenter.

handleQuestionResolution should clear pending questions on the instance passed into AgentPresenter, otherwise tests or alternate presenter setups can end up with stale session state.

🛠️ Suggested fix
-    presenter.sessionManager.clearPendingQuestion(message.conversationId)
-    presenter.sessionManager.setStatus(message.conversationId, 'idle')
+    this.sessionManager.clearPendingQuestion(message.conversationId)
+    this.sessionManager.setStatus(message.conversationId, 'idle')
🧹 Nitpick comments (13)
src/renderer/src/i18n/zh-TW/chat.json (1)

156-158: Prefer ‘智能體’ for Agent terminology in zh‑TW.

Line 157 uses “語音代理”. To align with the Chinese locale guidance for “Agents”, consider “語音智能體” (keeping “Voice.ai” as a proper noun).

✏️ Suggested tweak
-    "description": "點擊連接 Voice.ai 語音代理,通話接通後即可說話。",
+    "description": "點擊連接 Voice.ai 語音智能體,通話接通後即可說話。",
As per coding guidelines: For Chinese translations, consider using '智能体' for 'Agents' instead of the English term.
test/renderer/message/messageBlockSnapshot.test.ts (1)

5-8: Comments should be in English per coding guidelines.

The comment block is in Chinese. As per the coding guidelines for **/*.{js,ts,tsx,jsx,vue,mjs,cjs}, all logs and comments must be in English.

🔧 Suggested fix
-/**
- * 消息块数据结构快照测试
- * 确保事件到UI块的映射结构保持一致
- */
+/**
+ * Message Block Data Structure Snapshot Tests
+ * Ensures event-to-UI-block mapping structures remain consistent
+ */
src/renderer/src/components/chat-input/VoiceCallWidget.vue (1)

49-74: Align props/emits typing and Shadcn import alias with project conventions.

  • Please switch the inline props/emits types to named interfaces for consistency across components.
  • Also confirm Shadcn imports should use the @/shadcn/... alias per renderer conventions (this file currently uses @shadcn/...).
♻️ Suggested refactor for props/emits typing
+interface VoiceCallWidgetProps {
+  variant: 'chat' | 'newThread'
+  activeProviderId?: string | null
+  isStreaming?: boolean
+}
+
+interface VoiceCallWidgetEmits {
+  (event: 'active-change', value: boolean): void
+}
+
 const props = withDefaults(
-  defineProps<{
-    variant: 'chat' | 'newThread'
-    activeProviderId?: string | null
-    isStreaming?: boolean
-  }>(),
+  defineProps<VoiceCallWidgetProps>(),
   {
     activeProviderId: null,
     isStreaming: false
   }
 )
 
-const emit = defineEmits<{
-  (event: 'active-change', value: boolean): void
-}>()
+const emit = defineEmits<VoiceCallWidgetEmits>()

As per coding guidelines: Define props and emits explicitly in Vue components using defineProps and defineEmits with TypeScript interfaces; Import Shadcn Vue components from @/shadcn/components/ui/ path alias.

src/main/presenter/sessionPresenter/types.ts (1)

1-7: Consolidate SessionStatus type to single source of truth in @shared/types.

SessionStatus is defined identically in three locations: src/main/presenter/sessionPresenter/types.ts, src/main/presenter/agentPresenter/session/sessionContext.ts, and src/shared/types/presenters/session.presenter.d.ts. To reduce duplication and prevent type drift, import from src/shared/types/presenters/session.presenter.d.ts in the two main process files instead of defining locally.

src/main/presenter/agentPresenter/session/sessionContext.ts (1)

37-41: Consider whether pendingQuestionInitialized is necessary.

The pendingQuestion structure follows the existing pendingPermission pattern, which is good for consistency. However, pendingQuestionInitialized seems redundant if the presence of pendingQuestion already indicates an active question flow.

Could this flag be derived from pendingQuestion !== undefined instead? If there's a specific use case where a question exists but isn't "initialized," it would be helpful to document that distinction.

src/renderer/settings/components/OllamaProviderSettingsDetail.vue (1)

302-307: Align the Tooltip import with the shadcn alias.

The new tooltip import uses @shadcn/..., but the project guideline prefers @/shadcn/.... Consider updating this import (and optionally the other shadcn imports in this file) for consistency.

Suggested update
-import {
-  Tooltip,
-  TooltipContent,
-  TooltipProvider,
-  TooltipTrigger
-} from '@shadcn/components/ui/tooltip'
+import {
+  Tooltip,
+  TooltipContent,
+  TooltipProvider,
+  TooltipTrigger
+} from '@/shadcn/components/ui/tooltip'
Based on learnings: Applies to src/renderer/src/**/*.{vue,ts,tsx} : Import Shadcn Vue components from `@/shadcn/components/ui/` path alias
src/renderer/settings/components/VoiceAIProviderConfig.vue (1)

236-248: Consider consolidating slider handlers.

Both onTemperatureChange and onTopPChange follow identical patterns. A minor refactor could reduce duplication.

♻️ Optional: Consolidate slider handlers
+const createSliderHandler = (
+  ref: Ref<number>,
+  key: keyof VoiceAIConfigUpdates
+) => (value: number[] | undefined) => {
+  if (!value || value[0] === undefined) return
+  ref.value = value[0]
+  if (isHydrating.value) return
+  void persistUpdates({ [key]: value[0] })
+}
+
+const onTemperatureChange = createSliderHandler(temperature, 'temperature')
+const onTopPChange = createSliderHandler(topP, 'topP')
-const onTemperatureChange = (value: number[] | undefined) => {
-  if (!value || value[0] === undefined) return
-  temperature.value = value[0]
-  if (isHydrating.value) return
-  void persistUpdates({ temperature: value[0] })
-}
-
-const onTopPChange = (value: number[] | undefined) => {
-  if (!value || value[0] === undefined) return
-  topP.value = value[0]
-  if (isHydrating.value) return
-  void persistUpdates({ topP: value[0] })
-}
src/renderer/src/components/message/MessageBlockQuestionRequest.vue (1)

52-56: Unused props messageId and conversationId.

The props messageId and conversationId are defined but never used within the component. If these are intended for future use or passed by parent convention, consider adding a comment. Otherwise, remove them to keep the interface clean.

src/renderer/settings/components/AnthropicProviderSettingsDetail.vue (1)

428-429: hasDefaultBaseUrl is always true.

Since defaultBaseUrl is a hardcoded non-empty string, hasDefaultBaseUrl will always be true. This is fine if the intent is to always show the tooltip for Anthropic. However, if consistency with other providers (like ProviderApiConfig.vue which derives it from props) is desired, consider making this computed from a configurable source.

src/renderer/src/stores/providerStore.ts (1)

353-386: Consider adding error handling for Voice AI config operations.

Both getVoiceAIConfig and updateVoiceAIConfig make multiple async calls to configP.getSetting/setSetting without try-catch blocks. If the config presenter throws, the error will propagate unhandled. Consider wrapping in try-catch for resilience, consistent with patterns like loadProviderOrder.

Example error handling pattern
 const getVoiceAIConfig = async (): Promise<VoiceAIConfig> => {
+  try {
     const config = {
       audioFormat: (await configP.getSetting<string>('voiceAI_audioFormat')) || 'mp3',
       // ... rest of config
     }
     voiceAIConfig.value = config
     return config
+  } catch (error) {
+    console.error('Failed to load Voice AI config:', error)
+    const defaultConfig: VoiceAIConfig = {
+      audioFormat: 'mp3',
+      model: 'voiceai-tts-v1-latest',
+      language: 'en',
+      temperature: 1,
+      topP: 0.8,
+      agentId: ''
+    }
+    voiceAIConfig.value = defaultConfig
+    return defaultConfig
+  }
 }
src/renderer/src/components/message/MessageBlockAudio.vue (1)

53-57: Define a props interface for this component.

A named props type keeps the component aligned with the Vue typing guideline and improves reuse.

♻️ Suggested refactor
-const props = defineProps<{
-  block: AssistantMessageBlock
-  messageId?: string
-  threadId?: string
-}>()
+type MessageBlockAudioProps = {
+  block: AssistantMessageBlock
+  messageId?: string
+  threadId?: string
+}
+
+const props = defineProps<MessageBlockAudioProps>()

Based on learnings: Define TypeScript interfaces for Vue component props and data structures.

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

403-410: Consider consolidating status assignment for clarity.

The current flow always sets waiting_permission first, then conditionally overwrites with waiting_question. While functionally correct, inverting the condition would make the intent clearer.

♻️ Optional refactor for clarity
-        presenter.sessionManager.setStatus(state.conversationId, 'waiting_permission')
-        if (!hasPendingPermissions) {
-          presenter.sessionManager.setStatus(state.conversationId, 'waiting_question')
-        }
+        presenter.sessionManager.setStatus(
+          state.conversationId,
+          hasPendingPermissions ? 'waiting_permission' : 'waiting_question'
+        )
src/main/presenter/agentPresenter/tools/questionTool.ts (1)

22-40: Redundant trimming and boolean conversion.

The schema already applies .trim() transforms (lines 8, 9, 13, 14), so manual trimming in the normalization function is unnecessary. Additionally, Boolean(input.multiple) is redundant since zod with .default(false) already ensures a boolean, and input.custom !== false could simply be input.custom.

♻️ Optional simplification
 const normalizeQuestionInfo = (input: QuestionToolInput): QuestionInfo => {
-  const header = input.header?.trim()
-  const question = input.question.trim()
-  const options = input.options.map((option) => {
-    const description = option.description?.trim()
-    return {
-      label: option.label.trim(),
-      ...(description ? { description } : {})
-    }
-  })
+  const header = input.header
+  const options = input.options.map((option) => ({
+    label: option.label,
+    ...(option.description ? { description: option.description } : {})
+  }))
 
   return {
     ...(header ? { header } : {}),
-    question,
+    question: input.question,
     options,
-    multiple: Boolean(input.multiple),
-    custom: input.custom !== false
+    multiple: input.multiple,
+    custom: input.custom
   }
 }

@zerob13 zerob13 merged commit c6db02f into dev Feb 5, 2026
2 checks passed
zerob13 added a commit that referenced this pull request Feb 5, 2026
* chore: typecheck with tsgo (#1278)

* feat: image left align

* feat: app settings skills (#1283)

* docs: add spec for app-settings

* feat: implement chat-driven settings control with skill gating

Add a safe, validated API for modifying DeepChat application settings via natural language. Settings changes are controlled by a dedicated 'deepchat-settings' skill to ensure tools are only available when contextually relevant.

Key features:
- Skill-gated tool injection: settings tools only appear when deepchat-settings skill is active
- Safe settings apply API with Zod validation and strict allowlist
- Support for toggles (sound, copy COT, chat mode) and enums (language, theme, font size)
- Defense-in-depth: runtime skill verification before applying changes
- Deep-linked settings navigation for unsupported/complex settings
- Comprehensive test coverage for validation, mapping, and skill gating

Changes:
- Add ChatSettingsToolHandler with validated apply/open methods
- Integrate with AgentToolManager for tool definition gating
- Create deepchat-settings built-in skill with clear activation rules
- Add shared types for requests/responses (chatSettings.ts)
- Implement settings window navigation with SECTION_ALIASES
- Add unit tests for handler and integration tests for tool gating
- Translate spec documents (plan.md, spec.md, tasks.md) to Chinese
- Fix type errors in getCurrentValue and OPEN_SECTION_VALUES

* refactor: remove chatMode from settings control and add permission service

Remove chatMode setting from the allowlist as it requires conversation-scoped updates that are better handled separately. Add permission checking for settings window opening to provide user control over settings navigation.

Key changes:
- Remove setChatMode tool and related schemas from ChatSettingsToolHandler
- Add SettingsPermissionService for managing tool approvals (one-time and session)
- Add permission check for deepchat_settings_open tool
- Update PermissionHandler to handle settings permission grants
- Add rememberable flag to permission request structure
- Update AgentToolManager to consume approvals before opening settings
- Add settingsPermissionService to main presenter index
- Clear settings approvals when conversation ends
- Update spec documents to reflect removed chatMode feature
- Remove chatMode-related tests and types

This ensures settings window opening requires explicit user approval and provides a cleaner separation of concerns for chat mode management.

* docs: translate to en

* chore: yo browser less context (#1284)

* docs: add specs for yo browesr context manager

* feat(yo-browser): improve skill description and add yo_browser_cdp_send to offload whitelist

* refactor(yobrowser): remove skill gating and make CDP tools always available in agent mode

* refactor(yobrowser): add CDP method schema validation with strict enums

- Add enum-based validation for cdp_send method (11 common CDP methods)
- Add detailed union schemas for each method's parameters with examples
- Add normalizeCdpParams method to handle both object and JSON string inputs
- Prevent method typos and provide better type safety for CDP interactions

* fix(yobrowser): add strict tab ID validation in CDP send handler

* chore: update deps

* refactor: agent provider simplification (#1286)

* docs: add spec for agent provider

* refactor(agent): remove BaseAgentProvider layer and simplify provider hierarchy

* feat(mcp): implement real Apple Maps search using URL scheme (#1289)

* feat: support voice.ai (#1291)

* feat: remove custome tiptap (#1295)

* feat: settings auto scroll toggle (#1293)

* feat: settings auto scroll toggle

* feat: i18n support

* fix(renderer): remove specific event listeners instead of all

* feat: add tooltip for filling default API URL in settings (#1296)

* fix: fix model list refresh failed (#1297)

* feat: add question tools  (#1298)

* feat(agent): add question tool flow

* feat(mcp): implement real Apple Maps search using URL scheme (#1289)

* feat: support voice.ai (#1291)

* feat: remove custome tiptap (#1295)

* feat: settings auto scroll toggle (#1293)

* feat: settings auto scroll toggle

* feat: i18n support

* fix(renderer): remove specific event listeners instead of all

* feat: add tooltip for filling default API URL in settings (#1296)

* refactor(question): simplify question request UI to single-choice interface

* fix(chat): restore pending question state

* fix: review issues

---------

Co-authored-by: Qi Jin <jin.qi1@northeastern.edu>
Co-authored-by: xiaomo <wegi866@gmail.com>

* chore: integrated vue-tsgo (#1299)

* chore: integrated vue-tsgo

* chore: update recommendation exteion

* chore: update

* feat: add prompt for ask tool

* chore: version to 0.5.7

---------

Co-authored-by: xiaomo <wegi866@gmail.com>
Co-authored-by: wanna <wanna.w@binarywalk.com>
Co-authored-by: Qi Jin <jin.qi1@northeastern.edu>
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.

3 participants