-
Notifications
You must be signed in to change notification settings - Fork 614
Release 0.3.1 #754
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Release 0.3.1 #754
Conversation
…vTool instead of close
#751) * feat: add OpenRouter GPT-5 reasoning effort and verbosity support * feat: add comprehensive OpenAI reasoning models support for OpenRouter
…table installation support (#752) * feat: add architecture compatibility checks and Visual C++ Redistributable installation support * fix: include NSIS installer script in the build configuration
WalkthroughAdds Windows NSIS installer pre-checks, introduces AWS Bedrock and Vercel AI Gateway providers, propagates reasoningEffort/verbosity through presenters, updates DB schema and shared types, expands model configs (GPT‑5/O1/O3, GLM‑4.5V), implements SVG sanitization (main/lib/renderer + i18n), adjusts UI (provider search, Bedrock settings, API key toggles), and bumps dependencies. Changes
Sequence Diagram(s)sequenceDiagram
participant C as SvgArtifact.vue
participant D as DevicePresenter
participant S as SVGSanitizer
C->>D: sanitizeSvgContent(svg)
D->>S: sanitize(svg)
S-->>D: sanitized SVG or null
D-->>C: result
C->>C: Render loading/error/sanitized content
sequenceDiagram
participant T as ThreadPresenter
participant L as LLMProviderPresenter
participant P as Provider (OpenAI/Bedrock/...)
T->>L: startStreamCompletion(..., thinkingBudget, reasoningEffort, verbosity)
L->>P: coreStream(messages, modelConfig{...})
P-->>L: stream events (text/tool/reasoning/usage/error/stop)
L-->>T: forwarded events (including concurrency error when full)
sequenceDiagram
participant User as Installer
participant NSIS as customInit
NSIS->>NSIS: checkArchitectureCompatibility
alt incompatible
NSIS-->>User: Show mismatch message and abort
else compatible
NSIS->>NSIS: checkVCRedist
alt missing
NSIS->>NSIS: Download & run VC++ Redist
NSIS->>NSIS: Re-check or abort on failure
end
end
Estimated code review effort🎯 5 (Critical) | ⏱️ ~120 minutes Possibly related PRs
Poem
Tip 🔌 Remote MCP (Model Context Protocol) integration is now available!Pro plan users can now connect to remote MCP servers from the Integrations page. Connect with popular remote MCPs such as Notion and Linear to add more context to your reviews and chats. ✨ Finishing Touches
🧪 Generate unit tests
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. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. CodeRabbit Commands (Invoked using PR/Issue comments)Type Other keywords and placeholders
CodeRabbit Configuration File (
|
|
@coderabbitai review |
✅ Actions performedReview triggered.
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 22
🔭 Outside diff range comments (9)
src/renderer/src/components/settings/ModelScopeMcpSync.vue (2)
151-155: Coerce and clamp options before invoking presenterEnsure numbers are in the expected domain and avoid passing reactive objects directly (copying avoids accidental mutations downstream).
- // 调用简化的同步API,所有的格式转换和导入都在服务端处理 - const result = await llmP.syncModelScopeMcpServers(props.provider.id, syncOptions) + // 调用简化的同步API,所有的格式转换和导入都在服务端处理 + const allowedSizes = new Set([10, 25, 50, 100]) + const size = Number(syncOptions.page_size) || 50 + const pageSize = allowedSizes.has(size) ? size : 50 + const pageNumber = Math.max(1, Number(syncOptions.page_number) || 1) + const opts = { page_number: pageNumber, page_size: pageSize } + const result = await llmP.syncModelScopeMcpServers(props.provider.id, opts)
121-126: Ensureproviderprops include required fields
LLM_PROVIDER definesid,name, andapiTypebut does not includeapiKey. If this component needsprovider.apiKey, please update one of the following:
- Extend the shared type in
src/shared/presenter.d.ts:export type LLM_PROVIDER = { id: string name: string apiType: string
- apiKey: string
}- Or narrow the props in `ModelScopeMcpSync.vue` to explicitly require `apiKey`: ```ts const props = defineProps<{ provider: Pick<LLM_PROVIDER, 'id'> & { apiKey: string } }>()Files to update:
- src/shared/presenter.d.ts
- src/renderer/src/components/settings/ModelScopeMcpSync.vue
src/main/presenter/deeplinkPresenter/index.ts (5)
265-289: Fix potential TDZ crash in waitForTabReady (clearTimeout before initialization).
clearTimeout(timeoutId)can be executed beforetimeoutIdis initialized, throwing due to TDZ. Hoist and guard.private async waitForTabReady(tabId: number): Promise<void> { return new Promise((resolve) => { let resolved = false + let timeoutId: ReturnType<typeof setTimeout> | null = null const onTabReady = (readyTabId: number) => { if (readyTabId === tabId && !resolved) { resolved = true console.log(`[Deeplink] Tab ${tabId} renderer is ready`) eventBus.off('tab:renderer-ready', onTabReady) - clearTimeout(timeoutId) + if (timeoutId) clearTimeout(timeoutId) resolve() } } eventBus.on('tab:renderer-ready', onTabReady) - const timeoutId = setTimeout(() => { + timeoutId = setTimeout(() => { if (!resolved) { resolved = true eventBus.off('tab:renderer-ready', onTabReady) console.log(`[Deeplink] Timeout waiting for tab ${tabId}, proceeding anyway`) resolve() } }, 3000) }) }
315-334: Support 'http' MCP server type to match shared types and avoid rejecting valid configs.Shared MCPServerConfig allows 'http', but this block rejects anything except 'stdio'/'sse'. This breaks deeplinks for HTTP MCP servers.
- let determinedType: 'sse' | 'stdio' | null = null + let determinedType: 'sse' | 'stdio' | 'http' | null = null ... - if (serverConfig.type) { - if (serverConfig.type === 'stdio' || serverConfig.type === 'sse') { + if (serverConfig.type) { + if (serverConfig.type === 'stdio' || serverConfig.type === 'sse' || serverConfig.type === 'http') { determinedType = serverConfig.type // Validate required fields based on explicit type if (determinedType === 'stdio' && !determinedCommand) { console.error( `Server ${serverName} is type 'stdio' but missing required 'command' field` ) continue } - if (determinedType === 'sse' && !determinedUrl) { - console.error(`Server ${serverName} is type 'sse' but missing required 'url' field`) + if ((determinedType === 'sse' || determinedType === 'http') && !determinedUrl) { + console.error(`Server ${serverName} is type '${determinedType}' but missing required 'url' field`) continue } } else { console.error( - `Server ${serverName} provided invalid 'type' value: ${serverConfig.type}, should be 'stdio' or 'sse'` + `Server ${serverName} provided invalid 'type' value: ${serverConfig.type}, should be 'stdio' | 'sse' | 'http'` ) continue }
368-379: Treat 'http' like network type for defaults and field mapping.Network types ('sse'/'http') should get network icon and baseUrl mapping.
icons: determinedType === 'stdio' ? '🔌' : '🌐', // Different default icons ... - command: determinedType === 'stdio' ? determinedCommand! : defaultConfig.command!, - baseUrl: determinedType === 'sse' ? determinedUrl! : defaultConfig.baseUrl! + command: determinedType === 'stdio' ? determinedCommand! : defaultConfig.command!, + baseUrl: determinedType === 'sse' || determinedType === 'http' ? determinedUrl! : defaultConfig.baseUrl!Also applies to: 398-401
383-391: Guard env merging against undefined and invalid JSON.Spreading
undefinedthrows and JSON.parse can fail. Default to{}and parse safely, otherwise one bad server aborts the whole install.- env: { - ...(typeof defaultConfig.env === 'string' - ? JSON.parse(defaultConfig.env) - : defaultConfig.env), - ...(typeof serverConfig.env === 'string' - ? JSON.parse(serverConfig.env) - : serverConfig.env) - }, + env: { + ...((typeof defaultConfig.env === 'string' + ? JSON.parse(defaultConfig.env) + : defaultConfig.env) || {}), + ...((typeof serverConfig.env === 'string' + ? (() => { + try { return JSON.parse(serverConfig.env as string) } catch { + console.warn(`Invalid env JSON for server ${serverName}, ignoring`) + return {} + } + })() + : serverConfig.env) || {}) + },
403-407: Redact secrets from logs when printing server configs.
env, headers, tokens may appear infinalConfig. Avoid logging full config.- console.log( - `Preparing to install MCP server: ${serverName} (type: ${determinedType})`, - finalConfig - ) + console.log( + `Preparing to install MCP server: ${serverName} (type: ${determinedType})`, + { ...finalConfig, env: '[redacted]' } + )src/main/presenter/threadPresenter/index.ts (1)
1480-1491: Align startStreamCompletion signature across implementation and testsAll thread-presenter calls have been updated to pass the new
eventIdand optional parameters in the correct order, and theLlmProviderPresenterimplementation exactly matches theILlmProviderPresenterinterface. However, the unit tests forllmProviderPresenter.startStreamCompletionare still using the old three-parameter form and will fail to compile/run.Please update the test cases in
test/main/presenter/llmProviderPresenter.test.ts(around lines 172, 218, 258, 364, 412)
to pass at least the required fourth argumenteventId(and any additional optional args as needed), for example:- const stream = llmProviderPresenter.startStreamCompletion( - 'mock-openai-api', - messages, - 'mock-gpt-thinking', - ) + const stream = llmProviderPresenter.startStreamCompletion( + 'mock-openai-api', + messages, + 'mock-gpt-thinking', // modelId + `test-event-${Date.now()}`, // eventId + /* temperature? */, // if needed + /* maxTokens? */, // if needed + /* enabledMcpTools? */, + /* thinkingBudget? */, + /* reasoningEffort? */, + /* verbosity? */ +)src/main/presenter/configPresenter/modelDefaultSettings.ts (1)
878-899: Verify the rename from o1-mini to o1-pro.The model entry has been renamed from
o1-minitoo1-prowith different parameters. This is a breaking change for existing configurations using the o1-mini identifier.This rename could break existing user configurations. Please verify:
- Is this rename intentional?
- Are there migration strategies in place for users who have o1-mini configured?
- Should both entries exist to maintain backward compatibility?
#!/bin/bash # Check for any references to o1-mini in the codebase rg -n "o1-mini" --type ts --type json -g "!node_modules/**" -g "!dist/**"
🧹 Nitpick comments (53)
src/renderer/src/components/settings/ModelScopeMcpSync.vue (4)
135-139: Type syncOptions to prevent accidental type driftExplicitly typing syncOptions guards against v-model induced string values and keeps the contract with the presenter clear.
-// 同步选项 -const syncOptions = reactive({ +// 同步选项 +type SyncOptions = { + page_number: number + page_size: number +} +const syncOptions = reactive<SyncOptions>({ page_number: 1, page_size: 50 })
84-89: Add role and live-region to error message for a11yScreen readers won’t announce updates unless marked as alerts or live regions.
- <div - v-if="errorMessage" - class="p-2 bg-destructive/10 border border-destructive/20 rounded text-xs text-destructive" - > + <div + v-if="errorMessage" + role="alert" + aria-live="polite" + class="p-2 bg-destructive/10 border border-destructive/20 rounded text-xs text-destructive" + >
58-61: Announce sync results to assistive techMake the results region live so changes (imported/skipped/errors) are announced.
- <div v-if="syncResult" class="flex items-center gap-1 text-xs"> + <div v-if="syncResult" class="flex items-center gap-1 text-xs" aria-live="polite">
157-159: Gate console logging behind DEV or switch to a user-facing toastAvoid noisy logs in production builds.
- if (result.imported > 0) { - console.log('MCP servers imported successfully:', result) - } + if (result.imported > 0 && import.meta.env.DEV) { + console.debug('MCP servers imported successfully:', result) + }.oxlintrc.json (1)
10-15: Glob patterns likely only ignore root-level dirs; consider making them repo-wideIf you intend to ignore these folders anywhere in the repo (monorepo/packages/examples), prefer
**/dir/**overdir/**. Current patterns only ignore at repo root.Apply this diff if global ignore is desired:
- "build/**", - "resources/**", - "scripts/**", - "runtime/**", - "docs/**", - "test/**" + "**/build/**", + "**/resources/**", + "**/scripts/**", + "**/runtime/**", + "**/docs/**", + "**/test/**"build/nsis-installer.nsh (4)
77-83: Preserve and restore $5 since it’s now used for redist archIf you adopt arch-aware redist detection,
$5is used. Preserve it like the other registers.Apply this diff:
Push $0 Push $1 Push $2 Push $3 - Push $4 + Push $4 + Push $5 @@ ContinueInstall: + Pop $5 Pop $4 Pop $3 Pop $2 Pop $1 Pop $0Also applies to: 123-128
84-96: Edge: Aborting without popping preserved registers is safe here but can be balancedBecause
Abortexits the installer init, the leaked stack entries won’t be observed. Still, popping beforeAbortkeeps the stack balanced in case of future macro reuse.If you want to be thorough, pop the saved registers just before each
Abort.
98-121: Optional: verify the downloaded VC++ installer’s Authenticode signatureRelying solely on HTTPS and a redirect link is weaker than verifying signature. If feasible, add a signature check (e.g., via a WinVerifyTrust plugin) before
ExecWait.Do you plan to bundle or allow using a signature verification plugin (e.g., WinVerifyTrust) in your NSIS toolchain? If yes, I can draft the snippet.
19-75: Architecture detection approach is reasonableUsing
${RunningX64}plus env var probing for ARM64 is pragmatic and should work across common configurations. If future Windows versions change env var semantics, we can revisit withSystem::CalltoGetNativeSystemInfo/IsWow64Process2.scripts/generate-i18n-types.js (1)
12-12: Regex change LGTM; small hardening: safely quote non-identifier keysThe simplified character classes are equivalent. Consider JSON-stringifying non-identifier keys to handle embedded quotes safely.
- return /^[a-zA-Z$][a-zA-Z0-9_$]*$/.test(key) ? key : `'${key}'` + return /^[a-zA-Z$][a-zA-Z0-9_$]*$/.test(key) ? key : JSON.stringify(key)src/renderer/src/i18n/zh-TW/settings.json (1)
443-444: Consistency + punctuation polish for zh-TW
- Use 「設定」 instead of 「設置」 for zh-TW consistency.
- Use the ideographic comma 「、」 to separate model names inside full-width parentheses.
Apply this diff:
- "settingsApplied": "設置已應用", - "bedrockLimitTip": "* 僅支援 Anthropic Claude(包括 Opus,Sonnet,Haiku 模型)" + "settingsApplied": "設定已套用", + "bedrockLimitTip": "* 僅支援 Anthropic Claude(包括 Opus、Sonnet、Haiku 模型)"package.json (1)
60-62: Align AWS SDK client versions to minimize duplicate depsBedrock and Bedrock Runtime clients are on different patch levels. Aligning them helps avoid duplicate smithy packages and reduces install size.
Apply this diff:
- "@aws-sdk/client-bedrock": "^3.840.0", - "@aws-sdk/client-bedrock-runtime": "^3.842.0", + "@aws-sdk/client-bedrock": "^3.842.0", + "@aws-sdk/client-bedrock-runtime": "^3.842.0",src/renderer/src/i18n/ko-KR/settings.json (1)
228-228: Unify terminology and fix grammar
- Prefer “서비스 제공업체” (already used elsewhere) over “프로바이더” for consistency.
- Past tense is more natural for “settings applied”.
Apply this diff:
- "search": "프로바이더 플랫폼 검색…", + "search": "서비스 제공업체 검색…",- "settingsApplied": "설정이 적용됩니다", + "settingsApplied": "설정이 적용되었습니다",Also applies to: 443-443
src/renderer/src/i18n/zh-TW/artifacts.json (1)
41-42: Typography: use ellipsis characterUse the single-character ellipsis 「…」 to match common zh-TW typography. Optional stylistic tweak.
Apply this diff:
- "sanitizingSvg": "正在淨化SVG內容...", - "svgSanitizationFailed": "SVG內容未通過安全驗證" + "sanitizingSvg": "正在淨化SVG內容…", + "svgSanitizationFailed": "SVG內容未通過安全驗證"src/renderer/src/i18n/ja-JP/artifacts.json (1)
41-42: Natural Japanese phrasing + ellipsis
- Prefer “サニタイズ中…” for ongoing action.
- Fix grammar: 「のセキュリティ検証に失敗しました」.
Apply this diff:
- "sanitizingSvg": "SVGコンテンツの浄化...", - "svgSanitizationFailed": "SVGコンテンツにセキュリティ検証に失敗しました" + "sanitizingSvg": "SVGコンテンツをサニタイズ中…", + "svgSanitizationFailed": "SVGコンテンツのセキュリティ検証に失敗しました"src/renderer/src/i18n/zh-CN/artifacts.json (1)
39-42: SVG sanitization i18n keys added — LGTM with minor zh-CN phrasing/spacing nitpicksThe strings are clear. For zh-CN typography, adding a space around “SVG” improves readability.
Apply this diff for the three keys:
- "sanitizingSvg": "正在净化SVG内容...", - "svgSanitizationFailed": "SVG内容未通过安全验证", - "noSvgContent": "没有可用的SVG内容" + "sanitizingSvg": "正在净化 SVG 内容...", + "svgSanitizationFailed": "SVG 内容未通过安全验证", + "noSvgContent": "没有可用的 SVG 内容"src/renderer/src/components/icons/ModelIcon.vue (1)
154-169: Consolidate dark-mode invert logic and avoid repeated toLowerCase callsMinor cleanup to reduce repetition and make it easier to append future providers.
Apply this diff to simplify the computed block:
const invert = computed(() => { - if (!props.isDark) { - return false - } - if ( - props.modelId.toLowerCase() === 'openai' || - props.modelId.toLowerCase().includes('openai-responses') || - props.modelId.toLowerCase().includes('openrouter') || - props.modelId.toLowerCase().includes('ollama') || - props.modelId.toLowerCase().includes('grok') || - props.modelId.toLowerCase().includes('groq') || - props.modelId.toLowerCase().includes('github') || - props.modelId.toLowerCase().includes('moonshot') || - props.modelId.toLowerCase().includes('lmstudio') || - props.modelId.toLowerCase().includes('aws-bedrock') - ) { - return true - } - return false + if (!props.isDark) return false + const id = props.modelId.toLowerCase() + if (id === 'openai') return true + const invertKeys = [ + 'openai-responses', + 'openrouter', + 'ollama', + 'grok', + 'groq', + 'github', + 'moonshot', + 'lmstudio', + 'aws-bedrock', + ] + return invertKeys.some((k) => id.includes(k)) })src/renderer/src/i18n/ru-RU/settings.json (1)
443-445: Fix Russian phrasing: past-tense toast and Bedrock tip grammar
- “settingsApplied” should be past tense to match other locales (“Settings applied”).
- Bedrock tip: use singular verb with the brand or clarify subject.
- "settingsApplied": "Применяются настройки", - "bedrockLimitTip": "* Поддерживает только Anthropic Claude (включая модели Opus, Sonnet, Haiku)" + "settingsApplied": "Настройки применены", + "bedrockLimitTip": "* Поддерживается только Anthropic Claude (включая модели Opus, Sonnet, Haiku)"src/renderer/src/i18n/zh-CN/settings.json (1)
443-445: Bedrock tip punctuation for zh-CNUse the ‘、’ list separator inside full-width parentheses for zh-CN.
- "bedrockLimitTip": "* 仅支持 Anthropic Claude(包括 Opus,Sonnet,Haiku 模型)" + "bedrockLimitTip": "* 仅支持 Anthropic Claude(包括 Opus、Sonnet、Haiku 模型)"src/renderer/src/i18n/ko-KR/artifacts.json (1)
40-42: Polish Korean phrasing and typographic ellipsis for clarity and correctness
- Prefer “콘텐츠” over “컨텐츠”.
- Use progressive form “정화 중…” and the ellipsis character “…” for UI status.
- Slightly refine failure phrasing.
Apply this diff:
- "noSvgContent": "사용 가능한 SVG 컨텐츠가 없습니다", - "sanitizingSvg": "SVG 컨텐츠 정화 ...", - "svgSanitizationFailed": "SVG 컨텐츠는 보안 검증에 실패했습니다" + "noSvgContent": "사용 가능한 SVG 콘텐츠가 없습니다", + "sanitizingSvg": "SVG 콘텐츠 정화 중…", + "svgSanitizationFailed": "SVG 콘텐츠가 보안 검증을 통과하지 못했습니다"src/main/presenter/llmProviderPresenter/baseProvider.ts (1)
33-33: Clarify time unit in constant nameSuffix with “_MS” and use numeric separators for readability. Keeps the value intact.
Apply this diff:
- protected static readonly DEFAULT_MODEL_FETCH_TIMEOUT = 12000 // 提升到12秒作为通用默认值 + protected static readonly DEFAULT_MODEL_FETCH_TIMEOUT_MS = 12_000 // 提升到12秒作为通用默认值Note: If you apply this rename, also update references (see next comment).
src/renderer/src/i18n/en-US/artifacts.json (1)
39-42: Use Unicode ellipsis and “Failed to …” phrasing in i18n artifacts
Align with project typography and failure-style across all locales:
- Update src/renderer/src/i18n/en-US/artifacts.json:
- "copyImageFailedDesc": "Could not copy image to clipboard.", - "sanitizingSvg": "Sanitizing SVG content...", + "copyImageFailedDesc": "Failed to copy image to clipboard", + "sanitizingSvg": "Sanitizing SVG content…",- In every other artifacts.json (ru-RU, zh-TW, zh-HK, zh-CN, ko-KR, ja-JP, fa-IR, fr-FR), locate the
sanitizingSvgentry and replace any ASCII ellipsis (...or...) with the single Unicode ellipsis character (…) for consistent typography.src/renderer/src/i18n/zh-HK/artifacts.json (1)
40-42: Add spacing around “SVG 內容” for consistency with existing keysElsewhere in this locale (e.g., “SVG 預覽”), “SVG” is spaced from Chinese text. Align these three new strings to that convention.
- "noSvgContent": "沒有可用的SVG內容", - "sanitizingSvg": "正在淨化SVG內容...", - "svgSanitizationFailed": "SVG內容未通過安全驗證" + "noSvgContent": "沒有可用的 SVG 內容", + "sanitizingSvg": "正在淨化 SVG 內容...", + "svgSanitizationFailed": "SVG 內容未通過安全驗證"src/renderer/src/i18n/fa-IR/artifacts.json (1)
40-42: Polish Persian translations for clarity and naturalnessThese read a bit unnatural. Suggested improvements below maintain the intended meaning and better match common UI phrasing.
- "noSvgContent": "بدون محتوای SVG در دسترس نیست", - "sanitizingSvg": "تصفیه محتوای SVG ...", - "svgSanitizationFailed": "محتوای SVG تأیید امنیتی را شکست داد" + "noSvgContent": "هیچ محتوای SVG در دسترس نیست", + "sanitizingSvg": "در حال پاکسازی محتوای SVG…", + "svgSanitizationFailed": "محتوای SVG در تأیید امنیتی پذیرفته نشد"src/renderer/src/i18n/zh-HK/settings.json (1)
228-229: Unify provider search phrasing and punctuation; refine Bedrock tip list punctuation
- Use “服務商” consistently (drop “平台”) to align with the rest of this section.
- Use the same ellipsis style across the file (this file mostly uses “...”, not “…”).
- Prefer the Chinese list separator “、” inside full-width brackets.
- "search": "搜尋服務商平台…", + "search": "搜尋服務商...", @@ - "settingsApplied": "設置已應用", - "bedrockLimitTip": "* 僅支持 Anthropic Claude(包括 Opus,Sonnet,Haiku 模型)" + "settingsApplied": "設置已應用", + "bedrockLimitTip": "* 僅支持 Anthropic Claude(包括 Opus、Sonnet、Haiku)"Follow-up: there’s broader inconsistency in this file between “搜索” and “搜尋”, and “設定” vs “設置”. If desired, I can run through the file to normalize zh‑HK terminology in one pass.
Also applies to: 443-445
src/renderer/src/components/settings/ModelConfigDialog.vue (2)
399-401: Align contextLength max validation with the input max and i18nThe input uses a max of 10,000,000 and the i18n message also references 10,000,000, but validation allows up to 100,000,000. Align to 10,000,000 to avoid confusion.
- } else if (config.value.contextLength > 100_000_000) { + } else if (config.value.contextLength > 10_000_000) {
492-499: Tighten detection of “supportsReasoningEffort” to valid enum valuesA plain null/undefined check can produce false positives if an empty string sneaks in. Consider validating against the allowed set.
-const supportsReasoningEffort = computed(() => { - return config.value.reasoningEffort !== undefined && config.value.reasoningEffort !== null -}) +const supportsReasoningEffort = computed(() => { + const v = config.value.reasoningEffort + return v === 'minimal' || v === 'low' || v === 'medium' || v === 'high' +})src/main/presenter/sqlitePresenter/tables/conversations.ts (1)
117-160: Insert: use SQL NULL for enabled_mcp_tools instead of string "NULL"Minor correctness: storing the literal string "NULL" makes the column a non-null “NULL” string. Prefer actual SQL NULL.
- settings.enabledMcpTools ? JSON.stringify(settings.enabledMcpTools) : 'NULL', + settings.enabledMcpTools ? JSON.stringify(settings.enabledMcpTools) : null,src/main/presenter/deeplinkPresenter/index.ts (1)
295-304: Add size guard before decoding Base64 JSON to avoid memory blowups.A very large
codeparam could spike memory usage.try { + // Basic size guard (Base64 inflates ~33%) + if (jsonBase64.length > 1_500_000) { + console.error('MCP config payload too large') + return + } // 解码 Base64 并解析 JSON const jsonString = Buffer.from(jsonBase64, 'base64').toString('utf-8')src/main/lib/svgSanitizer.ts (2)
59-66: Honor allowScripts option (currently ignored).Option exists but scripts are always stripped. Either remove the option or gate removal by it. Gating keeps defaults secure and makes the option meaningful.
- // 2. Remove script tags and their content - sanitized = this.removeScriptTags(sanitized) + // 2. Remove script tags and their content + if (!this.options.allowScripts) { + sanitized = this.removeScriptTags(sanitized) + }
288-291: Case-insensitive validation of opening tag.Use a regex to accept
<svgin any case and with attributes.- if (!output.trim().startsWith('<svg')) { + if (!/^\s*<svg\b/i.test(output)) { return false }src/shared/presenter.d.ts (1)
132-135: Reduce duplication of union types with type aliases.
'minimal' | 'low' | 'medium' | 'high'and'low' | 'medium' | 'high'appear multiple times. Consider centralizing totype ReasoningEffortandtype Verbosityto keep types DRY.Example:
export type ReasoningEffort = 'minimal' | 'low' | 'medium' | 'high' export type Verbosity = 'low' | 'medium' | 'high'Also applies to: 153-156
src/renderer/src/components/TitleView.vue (1)
261-274: Avoidas anywhen updating store config.Prefer a properly typed payload (e.g., a
Partial<CONVERSATION_SETTINGS>in the store method) to keep type guarantees.If updating the store signature isn’t possible now, consider a local helper with a typed shape to avoid
any.src/renderer/src/components/settings/OllamaProviderSettingsDetail.vue (2)
27-35: Disable autocomplete for API key input.Helps prevent accidental storage/fill of secrets.
<Input :id="`${provider.id}-apikey`" v-model="apiKey" :type="showApiKey ? 'text' : 'password'" :placeholder="t('settings.provider.keyPlaceholder')" style="padding-right: 2.5rem !important" + autocomplete="off" + autocapitalize="off" + spellcheck="false" @blur="handleApiKeyChange(String($event.target.value))" @keyup.enter="handleApiKeyEnter(apiKey)" />
37-41: Minor accessibility and form-safety tweaks to the toggle button.Explicitly set
type="button"and add an aria-label.- <Button + <Button variant="ghost" size="sm" class="absolute right-2 top-1/2 transform -translate-y-1/2 h-7 w-7 p-0 hover:bg-transparent" + type="button" + :aria-label="showApiKey ? t('settings.provider.hideKey') : t('settings.provider.showKey')" @click="showApiKey = !showApiKey" >src/renderer/src/i18n/en-US/settings.json (1)
443-445: Nit: polish Bedrock tip phrasing for clarity.Minor wording improvement for end-user clarity.
Apply:
- "bedrockLimitTip": "* Only Anthropic Claude supported (including Opus, Sonnet, Haiku models)" + "bedrockLimitTip": "* Only Anthropic Claude models are supported (Opus, Sonnet, Haiku)"src/renderer/src/components/settings/ProviderApiConfig.vue (1)
45-67: Good UX addition; add small security & a11y tweaks and avoid inline !important.
- Disable autocomplete/spellcheck for API keys.
- Provide an aria-label for the eye button.
- Replace inline padding with a utility class to avoid !important.
Apply:
- <Input + <Input :id="`${provider.id}-apikey`" :model-value="apiKey" :type="showApiKey ? 'text' : 'password'" :placeholder="t('settings.provider.keyPlaceholder')" - style="padding-right: 2.5rem !important" + class="pr-10" + autocomplete="off" + autocapitalize="off" + spellcheck="false" @blur="handleApiKeyChange($event.target.value)" @keyup.enter="$emit('validate-key', apiKey)" @update:model-value="apiKey = String($event)" /> <Button variant="ghost" size="sm" class="absolute right-2 top-1/2 transform -translate-y-1/2 h-7 w-7 p-0 hover:bg-transparent" @click="showApiKey = !showApiKey" + :aria-label="showApiKey ? 'Hide API key' : 'Show API key'" >src/renderer/src/components/settings/AnthropicProviderSettingsDetail.vue (1)
56-77: Consistent API key visibility toggle; add a11y and disable autocomplete.Mirror the ProviderApiConfig improvements for consistency:
- Turn off autocomplete/spellcheck on the key field.
- Add aria-label to the toggle button.
- Use utility padding instead of inline !important.
Apply:
- <Input + <Input :id="`${provider.id}-apikey`" v-model="apiKey" :type="showApiKey ? 'text' : 'password'" :placeholder="t('settings.provider.keyPlaceholder')" - style="padding-right: 2.5rem !important" + class="pr-10" + autocomplete="off" + autocapitalize="off" + spellcheck="false" @blur="handleApiKeyChange(String($event.target.value))" @keyup.enter="handleApiKeyEnter(apiKey)" /> <Button variant="ghost" size="sm" class="absolute right-2 top-1/2 transform -translate-y-1/2 h-7 w-7 p-0 hover:bg-transparent" @click="showApiKey = !showApiKey" + :aria-label="showApiKey ? 'Hide API key' : 'Show API key'" >src/main/presenter/devicePresenter/index.ts (1)
464-507: Consider moving the size limit to a configuration constant.The 1MB size limit is hardcoded in the method. Consider extracting it to a constant at the class level for better maintainability and configurability.
Apply this refactor to improve maintainability:
export class DevicePresenter implements IDevicePresenter { + private static readonly MAX_SVG_SIZE = 1024 * 1024 // 1MB + static getDefaultHeaders(): Record<string, string> { ... async sanitizeSvgContent(svgContent: string): Promise<string | null> { try { console.log('Sanitizing SVG content, length:', svgContent.length) // 基本输入验证 if (!svgContent || typeof svgContent !== 'string') { console.warn('Invalid SVG content provided') return null } // 长度限制检查 - if (svgContent.length > 1024 * 1024) { - // 1MB limit + if (svgContent.length > DevicePresenter.MAX_SVG_SIZE) { console.warn('SVG content exceeds size limit') return null }src/main/presenter/threadPresenter/index.ts (1)
1470-1491: Reduce duplication when plucking conversation settings before streaming.You repeat the same destructuring in three places. Consider a tiny helper to fetch the latest conversation + settings bundle to keep the call sites cleaner and reduce drift if the interface evolves again.
Also applies to: 1588-1597, 3676-3685
src/renderer/src/stores/settings.ts (1)
1557-1565: Consider secure storage for long-lived cloud credentials.ElectronStore persists in plaintext. If possible, back these with the OS keychain (e.g., keytar) and only cache ephemeral tokens/settings in ElectronStore.
src/renderer/src/components/artifacts/SvgArtifact.vue (1)
76-89: Avoid double-sanitizing on mount (watch with immediate already covers it).The immediate watch triggers sanitize on mount; onMounted triggers it again. Remove one to avoid redundant calls.
-onMounted(() => { - if (props.block.content) { - sanitizeSvgContent(props.block.content) - } -})src/main/presenter/llmProviderPresenter/providers/ollamaProvider.ts (1)
496-506: Gatereasoning_effortbehind a backend‐capability check
Passingreasoning_effortwhenevermodelConfig.reasoningEffortis set can trigger 4xx errors on Ollama builds that don’t recognize it. We should only spread that option if the target build actually supports it.Suggestions:
- Maintain a list or map of models that support
reasoning_effort(keyed bymodelId) and only spread whenmodelConfig.reasoningEffort&&supportedByModel(modelId, 'reasoning_effort').- If the Ollama client offers a metadata or “listModels” endpoint, inspect each model’s capabilities for
"reasoning_effort".- As a fallback, catch a 4xx response and retry the request without the unsupported field.
Location to update:
• src/main/presenter/llmProviderPresenter/providers/ollamaProvider.ts around lines 503–506.src/main/presenter/llmProviderPresenter/providers/openAICompatibleProvider.ts (1)
541-541: Same concern with GPT-5 detection pattern.The same
includes('gpt-5')pattern is used here for determining max_completion_tokens. This should be consistent with the logic at Line 220.Consider extracting this logic into a helper method to maintain consistency:
+ private isGPT5Model(modelId: string): boolean { + return modelId.startsWith('gpt-5') || modelId.includes('/gpt-5'); + } + // Then use it in both places: - modelId.includes('gpt-5') + this.isGPT5Model(modelId)src/renderer/src/components/settings/ModelProviderSettings.vue (1)
196-207: Consider case sensitivity in provider filtering.The
filterProvidersfunction performs case-insensitive filtering, which is good. However, consider trimming the search query to handle accidental spaces.const filterProviders = (providers: LLM_PROVIDER[]) => { - if (!searchQuery.value.trim()) { + const query = searchQuery.value.trim() + if (!query) { return providers } - const query = searchQuery.value.toLowerCase().trim() + const lowerQuery = query.toLowerCase() return providers.filter( (provider) => - t(provider.name).toLowerCase().includes(query) || - provider.id.toLowerCase().includes(query) || - (provider.apiType && provider.apiType.toLowerCase().includes(query)) + t(provider.name).toLowerCase().includes(lowerQuery) || + provider.id.toLowerCase().includes(lowerQuery) || + (provider.apiType && provider.apiType.toLowerCase().includes(lowerQuery)) ) }src/main/presenter/llmProviderPresenter/providers/awsBedrockProvider.ts (8)
96-100: Inconsistent language in error messages and logsThe codebase mixes Chinese and English in error messages and console logs. For better maintainability and international usability, consider standardizing on one language (preferably English for system logs).
- console.error('获取AWS Bedrock Anthropic模型列表出错:', error) + console.error('Failed to fetch AWS Bedrock Anthropic models:', error)- console.log('从API获取模型列表失败,使用默认模型配置') + console.log('Failed to fetch models from API, using default model configuration')
168-169: Localization inconsistency in error messagesSimilar to the previous comment, the error message uses Chinese while most of the codebase uses English.
- return { isOk: false, errorMsg: '未初始化 AWS Bedrock SDK' } + return { isOk: false, errorMsg: 'AWS Bedrock SDK not initialized' }
186-186: Use the instance's default model instead of hardcodingThe check method hardcodes
this.defaultModelbut it's better to use the actual model being configured to ensure the check is valid for the specific model being used.
200-200: Standardize error message language- return { isOk: false, errorMsg: `API 检查失败: ${errorMessage}` } + return { isOk: false, errorMsg: `API check failed: ${errorMessage}` }
640-640: Remove or guard debug console.log statementsDebug logging statements should be removed or wrapped in a debug flag check before production deployment.
- console.log('modelConfig', modelConfig, modelId) + // Only log in debug mode + if (process.env.DEBUG) { + console.log('modelConfig', modelConfig, modelId) + }
676-678: Consider making thinking budget configurableThe thinking budget is hardcoded to 1024 tokens. Consider making this configurable through modelConfig for better flexibility.
if (modelId.includes('claude-3-7')) { - payload.thinking = { budget_tokens: 1024, type: 'enabled' } + payload.thinking = { + budget_tokens: modelConfig.thinkingBudget || 1024, + type: 'enabled' + } }
794-795: Consolidate error handling for JSON parsingThere are nested try-catch blocks with similar error messages for JSON parsing. Consider consolidating the error handling.
try { // Try to parse complete JSON const jsonStr = accumulatedJson.trim() if (jsonStr && (jsonStr.startsWith('{') || jsonStr.startsWith('['))) { - try { - const jsonObject = JSON.parse(jsonStr) - if (jsonObject && typeof jsonObject === 'object') { - currentToolInputs = { ...currentToolInputs, ...jsonObject } - } - } catch (e) { - console.error('解析完整JSON失败:', e) - } + const jsonObject = JSON.parse(jsonStr) + if (jsonObject && typeof jsonObject === 'object') { + currentToolInputs = { ...currentToolInputs, ...jsonObject } + } } } catch (e) { - console.error('处理累积JSON失败:', e) + console.error('Failed to parse accumulated JSON:', e) }Also applies to: 798-799
910-915: Standardize error messages in stream error handlingconsole.error('AWS Bedrock Claude coreStream error:', error) yield { type: 'error', - error_message: error instanceof Error ? error.message : '未知错误' + error_message: error instanceof Error ? error.message : 'Unknown error' }
| Function checkVCRedist | ||
| ReadRegDWORD $0 HKLM "SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\x64" "Installed" | ||
| FunctionEnd |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
VC++ Redistributable check is x64-only; add arch-aware detection (+reg view)
Currently this only inspects the x64 key. ARM64 builds will mis-detect and fail to install the correct redist. Also, force the 64-bit registry view to avoid WOW64 redirection issues.
Apply this diff:
- Function checkVCRedist
- ReadRegDWORD $0 HKLM "SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\x64" "Installed"
- FunctionEnd
+ Function checkVCRedist
+ ; Decide which VC++ runtime architecture to check:
+ ; Prefer app arch ($3). If universal, pick the system arch ($1).
+ StrCpy $5 $3
+ ${If} $5 == "universal"
+ StrCpy $5 $1
+ ${EndIf}
+ ${If} $5 == "arm64"
+ ReadRegDWORD /reg64 $0 HKLM "SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\arm64" "Installed"
+ ${Else}
+ ; Default to x64
+ ReadRegDWORD /reg64 $0 HKLM "SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\x64" "Installed"
+ ${EndIf}
+ FunctionEndNote: This relies on $1 (system arch) and $3 (app arch) set by checkArchitectureCompatibility, which you already call before checkVCRedist.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| Function checkVCRedist | |
| ReadRegDWORD $0 HKLM "SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\x64" "Installed" | |
| FunctionEnd | |
| Function checkVCRedist | |
| ; Decide which VC++ runtime architecture to check: | |
| ; Prefer app arch ($3). If universal, pick the system arch ($1). | |
| StrCpy $5 $3 | |
| ${If} $5 == "universal" | |
| StrCpy $5 $1 | |
| ${EndIf} | |
| ${If} $5 == "arm64" | |
| ReadRegDWORD /reg64 $0 HKLM "SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\arm64" "Installed" | |
| ${Else} | |
| ; Default to x64 | |
| ReadRegDWORD /reg64 $0 HKLM "SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\x64" "Installed" | |
| ${EndIf} | |
| FunctionEnd |
🤖 Prompt for AI Agents
In build/nsis-installer.nsh around lines 15-17, the VC++ Redistributable check
currently only reads the x64 registry key and can mis-detect on ARM64 or when
WOW64 registry redirection occurs; update checkVCRedist to pick the correct
registry subkey based on the arch values set earlier ($1 = system arch, $3 = app
arch) (use "x64", "x86" or "arm64" in the path:
SOFTWARE\Microsoft\VisualStudio\14.0\VC\Runtimes\<arch>) and force the 64-bit
registry view with SetRegView 64 before ReadRegDWORD to avoid WOW64 redirection;
after the read restore the previous view if needed and handle missing key the
same as before.
| MessageBox MB_YESNO "\ | ||
| NOTE: ${PRODUCT_NAME} requires $\r$\n\ | ||
| 'Microsoft Visual C++ Redistributable'$\r$\n\ | ||
| to function properly.$\r$\n$\r$\n\ | ||
| Download and install now?" /SD IDYES IDYES InstallVCRedist IDNO DontInstall | ||
| InstallVCRedist: | ||
| inetc::get /CAPTION " " /BANNER "Downloading Microsoft Visual C++ Redistributable..." "https://aka.ms/vs/17/release/vc_redist.x64.exe" "$TEMP\vc_redist.x64.exe" | ||
| ExecWait "$TEMP\vc_redist.x64.exe /install /norestart" | ||
| ;IfErrors InstallError ContinueInstall ; vc_redist exit code is unreliable :( | ||
| Call checkVCRedist | ||
| ${If} $0 == "1" | ||
| Goto ContinueInstall | ||
| ${EndIf} | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
Download/installer path is hardcoded to x64; select arm64 when needed and handle download errors
On ARM64 app/system, you should fetch and run vc_redist.arm64.exe. Also handle inetc::get result to fail fast if the download is canceled/blocked.
Apply this diff:
- MessageBox MB_YESNO "\
+ MessageBox MB_YESNO "\
NOTE: ${PRODUCT_NAME} requires $\r$\n\
'Microsoft Visual C++ Redistributable'$\r$\n\
to function properly.$\r$\n$\r$\n\
Download and install now?" /SD IDYES IDYES InstallVCRedist IDNO DontInstall
InstallVCRedist:
- inetc::get /CAPTION " " /BANNER "Downloading Microsoft Visual C++ Redistributable..." "https://aka.ms/vs/17/release/vc_redist.x64.exe" "$TEMP\vc_redist.x64.exe"
- ExecWait "$TEMP\vc_redist.x64.exe /install /norestart"
- ;IfErrors InstallError ContinueInstall ; vc_redist exit code is unreliable :(
+ ; $5 is set in checkVCRedist to either "x64" or "arm64"
+ StrCpy $4 "$TEMP\vc_redist.$5.exe"
+ inetc::get /CAPTION " " /BANNER "Downloading Microsoft Visual C++ Redistributable..." "https://aka.ms/vs/17/release/vc_redist.$5.exe" "$4"
+ Pop $2
+ StrCmp $2 "OK" +2
+ Goto InstallError
+ ExecWait "$4 /install /norestart"
+ ; vc_redist exit code is unreliable; re-check the registry instead
Call checkVCRedist
${If} $0 == "1"
Goto ContinueInstall
${EndIf}📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| MessageBox MB_YESNO "\ | |
| NOTE: ${PRODUCT_NAME} requires $\r$\n\ | |
| 'Microsoft Visual C++ Redistributable'$\r$\n\ | |
| to function properly.$\r$\n$\r$\n\ | |
| Download and install now?" /SD IDYES IDYES InstallVCRedist IDNO DontInstall | |
| InstallVCRedist: | |
| inetc::get /CAPTION " " /BANNER "Downloading Microsoft Visual C++ Redistributable..." "https://aka.ms/vs/17/release/vc_redist.x64.exe" "$TEMP\vc_redist.x64.exe" | |
| ExecWait "$TEMP\vc_redist.x64.exe /install /norestart" | |
| ;IfErrors InstallError ContinueInstall ; vc_redist exit code is unreliable :( | |
| Call checkVCRedist | |
| ${If} $0 == "1" | |
| Goto ContinueInstall | |
| ${EndIf} | |
| MessageBox MB_YESNO "\ | |
| NOTE: ${PRODUCT_NAME} requires $\r$\n\ | |
| 'Microsoft Visual C++ Redistributable'$\r$\n\ | |
| to function properly.$\r$\n$\r$\n\ | |
| Download and install now?" /SD IDYES IDYES InstallVCRedist IDNO DontInstall | |
| InstallVCRedist: | |
| ; $5 is set in checkVCRedist to either "x64" or "arm64" | |
| StrCpy $4 "$TEMP\vc_redist.$5.exe" | |
| inetc::get /CAPTION " " /BANNER "Downloading Microsoft Visual C++ Redistributable..." "https://aka.ms/vs/17/release/vc_redist.$5.exe" "$4" | |
| Pop $2 | |
| StrCmp $2 "OK" +2 | |
| Goto InstallError | |
| ExecWait "$4 /install /norestart" | |
| ; vc_redist exit code is unreliable; re-check the registry instead | |
| Call checkVCRedist | |
| ${If} $0 == "1" | |
| Goto ContinueInstall | |
| ${EndIf} |
| // Security: Validate and sanitize message content | ||
| msg = this.sanitizeMessageContent(decodeURIComponent(msg)) | ||
| if (!msg) { | ||
| console.warn('Message content was rejected by security filters') | ||
| return | ||
| } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Avoid double-decoding URLSearchParams values (can throw and drop messages).
URL.searchParams.get() already returns decoded strings. Running decodeURIComponent again can throw on inputs like 100% ready and abort handling. Same for model/system/mentions.
Apply these diffs to rely on the already-decoded values:
- // Security: Validate and sanitize message content
- msg = this.sanitizeMessageContent(decodeURIComponent(msg))
+ // Security: Validate and sanitize message content
+ msg = this.sanitizeMessageContent(msg)📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // Security: Validate and sanitize message content | |
| msg = this.sanitizeMessageContent(decodeURIComponent(msg)) | |
| if (!msg) { | |
| console.warn('Message content was rejected by security filters') | |
| return | |
| } | |
| // Security: Validate and sanitize message content | |
| msg = this.sanitizeMessageContent(msg) | |
| if (!msg) { | |
| console.warn('Message content was rejected by security filters') | |
| return | |
| } |
🤖 Prompt for AI Agents
In src/main/presenter/deeplinkPresenter/index.ts around lines 176 to 182, the
code is double-decoding URLSearchParams values which can throw (e.g., "100%
ready") and drop messages; remove decodeURIComponent calls and use the
already-decoded values returned by URL.searchParams.get() for msg, model,
system, and mentions, then pass those raw values into sanitizeMessageContent
(guarding for null/undefined before sanitization) and keep the existing
security-reject/return flow if sanitizeMessageContent returns falsy.
| modelId = this.sanitizeStringParameter(decodeURIComponent(modelId)) | ||
| } | ||
|
|
||
| let systemPrompt = params.get('system') | ||
| if (systemPrompt && systemPrompt.trim() !== '') { | ||
| systemPrompt = decodeURIComponent(systemPrompt) | ||
| systemPrompt = this.sanitizeStringParameter(decodeURIComponent(systemPrompt)) | ||
| } else { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Remove redundant decodeURIComponent for model/system params.
Double-decoding can cause malformed URI errors and break deeplink processing.
- if (modelId && modelId.trim() !== '') {
- modelId = this.sanitizeStringParameter(decodeURIComponent(modelId))
- }
+ if (modelId && modelId.trim() !== '') {
+ modelId = this.sanitizeStringParameter(modelId)
+ }
- if (systemPrompt && systemPrompt.trim() !== '') {
- systemPrompt = this.sanitizeStringParameter(decodeURIComponent(systemPrompt))
- } else {
+ if (systemPrompt && systemPrompt.trim() !== '') {
+ systemPrompt = this.sanitizeStringParameter(systemPrompt)
+ } else {📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| modelId = this.sanitizeStringParameter(decodeURIComponent(modelId)) | |
| } | |
| let systemPrompt = params.get('system') | |
| if (systemPrompt && systemPrompt.trim() !== '') { | |
| systemPrompt = decodeURIComponent(systemPrompt) | |
| systemPrompt = this.sanitizeStringParameter(decodeURIComponent(systemPrompt)) | |
| } else { | |
| if (modelId && modelId.trim() !== '') { | |
| modelId = this.sanitizeStringParameter(modelId) | |
| } | |
| let systemPrompt = params.get('system') | |
| if (systemPrompt && systemPrompt.trim() !== '') { | |
| systemPrompt = this.sanitizeStringParameter(systemPrompt) | |
| } else { |
🤖 Prompt for AI Agents
In src/main/presenter/deeplinkPresenter/index.ts around lines 186 to 192, the
code double-decodes the model and system query parameters which can cause
malformed URI errors; remove the redundant decodeURIComponent calls (i.e., use
the already-decoded value from params or decode once when reading the param) and
then pass that single-decoded string into sanitizeStringParameter so modelId and
systemPrompt are only decoded a single time before sanitization.
| mentions = decodeURIComponent(mentionsParam) | ||
| .split(',') | ||
| .map((mention) => mention.trim()) | ||
| .map((mention) => this.sanitizeStringParameter(mention.trim())) | ||
| .filter((mention) => mention.length > 0) | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Do not decode mentions twice; sanitize only.
mentionsParam is already decoded. Re-decoding risks exceptions and mis-splitting items containing encoded commas.
- mentions = decodeURIComponent(mentionsParam)
- .split(',')
- .map((mention) => this.sanitizeStringParameter(mention.trim()))
+ mentions = mentionsParam
+ .split(',')
+ .map((mention) => this.sanitizeStringParameter(mention.trim()))📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| mentions = decodeURIComponent(mentionsParam) | |
| .split(',') | |
| .map((mention) => mention.trim()) | |
| .map((mention) => this.sanitizeStringParameter(mention.trim())) | |
| .filter((mention) => mention.length > 0) | |
| } | |
| mentions = mentionsParam | |
| .split(',') | |
| .map((mention) => this.sanitizeStringParameter(mention.trim())) | |
| .filter((mention) => mention.length > 0) | |
| } |
🤖 Prompt for AI Agents
In src/main/presenter/deeplinkPresenter/index.ts around lines 199 to 203, the
code re-decodes mentionsParam which is already decoded; remove the
decodeURIComponent call and operate directly on mentionsParam so you only
sanitize and split once. Replace the current chain with: if (mentionsParam) {
mentions = mentionsParam.split(',').map(m =>
this.sanitizeStringParameter(m.trim())).filter(m => m.length > 0) } ensuring no
double-decoding occurs.
| "sanitizingSvg": "Purification du contenu SVG ...", | ||
| "svgSanitizationFailed": "SVG Contenu Échec Vérification de sécurité" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Correct French phrasing
- “Purification” is uncommon in UI; “Nettoyage …” reads better.
- Fix grammar for the validation failure message.
Apply this diff:
- "sanitizingSvg": "Purification du contenu SVG ...",
- "svgSanitizationFailed": "SVG Contenu Échec Vérification de sécurité"
+ "sanitizingSvg": "Nettoyage du contenu SVG…",
+ "svgSanitizationFailed": "Échec de la vérification de sécurité du contenu SVG"📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| "sanitizingSvg": "Purification du contenu SVG ...", | |
| "svgSanitizationFailed": "SVG Contenu Échec Vérification de sécurité" | |
| "sanitizingSvg": "Nettoyage du contenu SVG…", | |
| "svgSanitizationFailed": "Échec de la vérification de sécurité du contenu SVG" |
🤖 Prompt for AI Agents
In src/renderer/src/i18n/fr-FR/artifacts.json around lines 41 to 42, update the
French strings to use more natural phrasing: replace "Purification du contenu
SVG ..." with "Nettoyage du contenu SVG…" (use an ellipsis or three dots
consistently) and replace "SVG Contenu Échec Vérification de sécurité" with the
grammatically correct "Échec de la vérification de sécurité du contenu SVG".
| "noSvgContent": "Недоступного контента SVG", | ||
| "sanitizingSvg": "Очистить содержание SVG ...", | ||
| "svgSanitizationFailed": "Содержание SVG не удалась проверка безопасности" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Fix Russian grammar and wording
Current strings are ungrammatical. Suggested phrasing is idiomatic, keeps meaning, and improves UX.
Apply this diff:
- "noSvgContent": "Недоступного контента SVG",
- "sanitizingSvg": "Очистить содержание SVG ...",
- "svgSanitizationFailed": "Содержание SVG не удалась проверка безопасности"
+ "noSvgContent": "Нет доступного SVG-контента",
+ "sanitizingSvg": "Очистка содержимого SVG…",
+ "svgSanitizationFailed": "Содержимое SVG не прошло проверку безопасности"🤖 Prompt for AI Agents
In src/renderer/src/i18n/ru-RU/artifacts.json around lines 40 to 42, the Russian
strings are ungrammatical; replace the three values with corrected idiomatic
phrases: set "noSvgContent" to "Нет содержимого SVG", "sanitizingSvg" to
"Очистка содержимого SVG...", and "svgSanitizationFailed" to "Не удалось
безопасно очистить содержимое SVG" so the wording is grammatically correct and
improves UX.
| // 更新 AWS Bedrock Provider 的配置 | ||
| const updateAwsBedrockProviderConfig = async ( | ||
| providerId: string, | ||
| updates: Partial<AWS_BEDROCK_PROVIDER> | ||
| ): Promise<void> => { | ||
| await updateProviderConfig(providerId, updates) | ||
| const currentProvider = providers.value.find((p) => p.id === providerId)! | ||
|
|
||
| // 只在特定条件下刷新模型列表 | ||
| const needRefreshModels = ['accessKeyId', 'secretAccessKey', 'region'].some( | ||
| (key) => key in updates | ||
| ) | ||
| if (needRefreshModels && currentProvider.enable) { | ||
| await refreshAllModels() | ||
| } | ||
| } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Bedrock models may not refresh when credentials change (nested fields not detected).
updates will typically carry credentials under updates.credential; your needRefreshModels check looks for top-level keys and won’t trigger on nested credential changes.
Apply this diff to detect nested credential updates too:
- // 只在特定条件下刷新模型列表
- const needRefreshModels = ['accessKeyId', 'secretAccessKey', 'region'].some(
- (key) => key in updates
- )
+ // 只在特定条件下刷新模型列表(检测嵌套的 credential 变更)
+ const needRefreshModels =
+ 'credential' in updates ||
+ ['accessKeyId', 'secretAccessKey', 'region'].some(
+ (key) => key in (updates.credential ?? {})
+ )📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // 更新 AWS Bedrock Provider 的配置 | |
| const updateAwsBedrockProviderConfig = async ( | |
| providerId: string, | |
| updates: Partial<AWS_BEDROCK_PROVIDER> | |
| ): Promise<void> => { | |
| await updateProviderConfig(providerId, updates) | |
| const currentProvider = providers.value.find((p) => p.id === providerId)! | |
| // 只在特定条件下刷新模型列表 | |
| const needRefreshModels = ['accessKeyId', 'secretAccessKey', 'region'].some( | |
| (key) => key in updates | |
| ) | |
| if (needRefreshModels && currentProvider.enable) { | |
| await refreshAllModels() | |
| } | |
| } | |
| // 更新 AWS Bedrock Provider 的配置 | |
| const updateAwsBedrockProviderConfig = async ( | |
| providerId: string, | |
| updates: Partial<AWS_BEDROCK_PROVIDER> | |
| ): Promise<void> => { | |
| await updateProviderConfig(providerId, updates) | |
| const currentProvider = providers.value.find((p) => p.id === providerId)! | |
| // 只在特定条件下刷新模型列表(检测嵌套的 credential 变更) | |
| const needRefreshModels = | |
| 'credential' in updates || | |
| ['accessKeyId', 'secretAccessKey', 'region'].some( | |
| (key) => key in (updates.credential ?? {}) | |
| ) | |
| if (needRefreshModels && currentProvider.enable) { | |
| await refreshAllModels() | |
| } | |
| } |
🤖 Prompt for AI Agents
In src/renderer/src/stores/settings.ts around lines 865 to 881, the
needRefreshModels boolean only checks for top-level keys and therefore misses
credential changes nested under updates.credential; update the condition to also
look inside updates.credential (e.g., check (updates.credential &&
['accessKeyId','secretAccessKey','region'].some(k => k in updates.credential)))
so that nested credential updates trigger refreshAllModels when the provider is
enabled.
| // AWS Bedrock | ||
| const setAwsBedrockCredential = async (credential: AwsBedrockCredential) => { | ||
| await configP.setSetting('awsBedrockCredential', JSON.stringify({ credential })) | ||
| } | ||
|
|
||
| const getAwsBedrockCredential = async (): Promise<AwsBedrockCredential | undefined> => { | ||
| return await configP.getSetting<AwsBedrockCredential | undefined>('awsBedrockCredential') | ||
| } | ||
|
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Credential persistence bug: stored as JSON string but read as object.
setAwsBedrockCredential serializes to a string, but getAwsBedrockCredential returns it as AwsBedrockCredential. This will break consumers and mixes shapes ({credential} wrapper vs plain object).
Use a plain object and a stable shape:
-const setAwsBedrockCredential = async (credential: AwsBedrockCredential) => {
- await configP.setSetting('awsBedrockCredential', JSON.stringify({ credential }))
-}
+const setAwsBedrockCredential = async (credential: AwsBedrockCredential) => {
+ await configP.setSetting('awsBedrockCredential', credential)
+}
-const getAwsBedrockCredential = async (): Promise<AwsBedrockCredential | undefined> => {
- return await configP.getSetting<AwsBedrockCredential | undefined>('awsBedrockCredential')
-}
+const getAwsBedrockCredential = async (): Promise<AwsBedrockCredential | undefined> => {
+ return await configP.getSetting<AwsBedrockCredential | undefined>('awsBedrockCredential')
+}If you intentionally need the { credential } wrapper, then also adjust the getter to parse and unwrap; otherwise the above is simpler and consistent.
📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| // AWS Bedrock | |
| const setAwsBedrockCredential = async (credential: AwsBedrockCredential) => { | |
| await configP.setSetting('awsBedrockCredential', JSON.stringify({ credential })) | |
| } | |
| const getAwsBedrockCredential = async (): Promise<AwsBedrockCredential | undefined> => { | |
| return await configP.getSetting<AwsBedrockCredential | undefined>('awsBedrockCredential') | |
| } | |
| // AWS Bedrock | |
| const setAwsBedrockCredential = async (credential: AwsBedrockCredential) => { | |
| await configP.setSetting('awsBedrockCredential', credential) | |
| } | |
| const getAwsBedrockCredential = async (): Promise<AwsBedrockCredential | undefined> => { | |
| return await configP.getSetting<AwsBedrockCredential | undefined>('awsBedrockCredential') | |
| } |
🤖 Prompt for AI Agents
In src/renderer/src/stores/settings.ts around lines 1557 to 1565, the setter
persists a JSON string with a { credential } wrapper while the getter expects a
plain AwsBedrockCredential object; change the setter to store the credential as
a plain object (call configP.setSetting('awsBedrockCredential', credential)
without JSON.stringify and without the { credential } wrapper) so the stored
shape matches what getAwsBedrockCredential returns, or alternatively if you want
to keep the wrapper, update the getter to parse/unwrap the JSON string and
return the inner credential—pick one consistent shape and make both setter and
getter follow it.
| thinkingBudget?: number, | ||
| reasoningEffort?: 'minimal' | 'low' | 'medium' | 'high', | ||
| verbosity?: 'low' | 'medium' | 'high' | ||
| ): AsyncGenerator<LLMAgentEvent, void, unknown> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
startStreamCompletion signature extension: request downstream updates.
Optional trailing params are backward-compatible, but ensure all implementations and call sites propagate thinkingBudget, reasoningEffort, and verbosity.
Run this to spot potential mismatches or ignored params:
🏁 Script executed:
#!/bin/bash
# Check ILlmProviderPresenter implementation and invocations for updated params
rg -nP --type=ts -C2 '\bstartStreamCompletion\s*\('
rg -nP --type=ts -C3 'function\s+startStreamCompletion\(|startStreamCompletion\s*=\s*\('Length of output: 7850
Propagate new thinkingBudget, reasoningEffort, and verbosity params in all startStreamCompletion call sites
The signature was extended with three optional settings—ensure every invocation forwards them from the relevant context/settings. Key locations needing updates:
-
Renderer store
src/renderer/src/stores/chat.ts(lines ~251 & ~271):
threadP.startStreamCompletion(...) ⇒ add(…, { thinkingBudget, reasoningEffort, verbosity })pulled from the thread or global settings.
-
ThreadPresenter
src/main/presenter/threadPresenter/index.tsat each call tothis.llmProviderPresenter.startStreamCompletion((around lines 1478–1482, 1656–1660, 2658–2662, 2719–2723, 3730–3734):
pass the current conversation’ssettingsobject’sthinkingBudget,reasoningEffort, andverbosity.
-
LLMProviderPresenter tests
test/main/presenter/llmProviderPresenter.test.ts(multiple blocks at lines ~170–174, ~216–220, ~256–260, ~362–366, ~410–414):
updatestartStreamCompletion('mock-openai-api', messages)calls to include default or test-specific values for the three new params.
🤖 Prompt for AI Agents
In src/shared/presenter.d.ts around lines 579–582 the startStreamCompletion
signature was extended with thinkingBudget, reasoningEffort, and verbosity;
update every call site to forward these three options: in
src/renderer/src/stores/chat.ts (around lines ~251 and ~271) pass the values
from the thread or global settings into threadP.startStreamCompletion(..., {
thinkingBudget, reasoningEffort, verbosity }); in
src/main/presenter/threadPresenter/index.ts at each call to
this.llmProviderPresenter.startStreamCompletion (around lines 1478–1482,
1656–1660, 2658–2662, 2719–2723, 3730–3734) pass the current
conversation.settings.{thinkingBudget, reasoningEffort, verbosity} as the
options argument; and in test/main/presenter/llmProviderPresenter.test.ts
(around lines ~170–174, ~216–220, ~256–260, ~362–366, ~410–414) update
startStreamCompletion('mock-openai-api', messages) calls to include default or
test-specific values for these three params so tests compile and reflect the new
signature.
修复了内存泄漏问题
修复了DeepLink 和 Artifacts 带来的安全漏洞
为提供商设置添加搜索功能
为 API 密钥输入添加密码可见性切换
添加了Vercel AI 和AWS Bedrock 新供应商
为 OpenRouter 添加全面的 OpenAI 推理模型支持
增加了GPT5系列模型的更完善支持
为 GPT-OSS 模型添加 reasoning_effort 参数支持
添加架构兼容性检查和 Visual C++ Redistribu 支持
解决使用 Ctrl+Shift+N 创建新窗口时的焦点闪烁问题
为 PopoverContent 添加可滚动支持以防止溢出
Fixed memory leak issues
Fixed security vulnerabilities caused by DeepLink and Artifacts
Added search functionality for provider settings
Added password visibility toggle for API key inputs
Added new providers: Vercel AI and AWS Bedrock
Added comprehensive OpenAI reasoning models support for OpenRouter
Enhanced support for GPT-5 series models
Added reasoning_effort parameter support for GPT-OSS models
Added architecture compatibility checks and Visual C++ Redistributable support
Resolved focus flicker issue when creating new windows with Ctrl+Shift+N
Added scrollable support to PopoverContent to prevent overflow
Summary by CodeRabbit
New Features
Bug Fixes