-
Notifications
You must be signed in to change notification settings - Fork 649
Xuefeng #762
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
Xuefeng #762
Changes from all commits
0787176
466045f
3b038eb
6dd8883
f74587a
7c7af12
dc67ee0
54f90f9
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,104 @@ | ||
| // electron.vite.config.ts | ||
| import { resolve } from "path"; | ||
| import { defineConfig, externalizeDepsPlugin } from "electron-vite"; | ||
| import vue from "@vitejs/plugin-vue"; | ||
| import autoprefixer from "autoprefixer"; | ||
| import tailwind from "tailwindcss"; | ||
| import vueDevTools from "vite-plugin-vue-devtools"; | ||
| import svgLoader from "vite-svg-loader"; | ||
| import monacoEditorPlugin from "vite-plugin-monaco-editor-esm"; | ||
| import path from "node:path"; | ||
| var electron_vite_config_default = defineConfig({ | ||
| main: { | ||
| plugins: [ | ||
| externalizeDepsPlugin({ | ||
| exclude: ["mermaid", "dompurify"] | ||
| }) | ||
| ], | ||
| resolve: { | ||
| alias: { | ||
| "@": resolve("src/main/"), | ||
| "@shared": resolve("src/shared") | ||
| } | ||
| }, | ||
| build: { | ||
| rollupOptions: { | ||
| external: ["sharp", "@duckdb/node-api"], | ||
| output: { | ||
| inlineDynamicImports: true, | ||
| manualChunks: void 0 | ||
| // Disable automatic chunk splitting | ||
| } | ||
| } | ||
| } | ||
| }, | ||
| preload: { | ||
| plugins: [externalizeDepsPlugin()], | ||
| resolve: { | ||
| alias: { | ||
| "@shared": resolve("src/shared") | ||
| } | ||
| }, | ||
| build: { | ||
| rollupOptions: { | ||
| input: { | ||
| index: resolve("src/preload/index.ts"), | ||
| floating: resolve("src/preload/floating-preload.ts") | ||
| } | ||
| } | ||
| } | ||
| }, | ||
| renderer: { | ||
| optimizeDeps: { | ||
| include: [ | ||
| "monaco-editor", | ||
| "axios" | ||
| ] | ||
| }, | ||
| resolve: { | ||
| alias: { | ||
| "@": resolve("src/renderer/src"), | ||
| "@shell": resolve("src/renderer/shell"), | ||
| "@shared": resolve("src/shared"), | ||
| vue: "vue/dist/vue.esm-bundler.js" | ||
| } | ||
| }, | ||
| css: { | ||
| postcss: { | ||
| // @ts-ignore | ||
| plugins: [tailwind(), autoprefixer()] | ||
| } | ||
| }, | ||
| server: { | ||
| host: "0.0.0.0" | ||
| // 防止代理干扰,导致vite-electron之间ws://localhost:5713和http://localhost:5713通信失败、页面组件无法加载 | ||
| }, | ||
| plugins: [ | ||
| monacoEditorPlugin({ | ||
| languageWorkers: ["editorWorkerService", "typescript", "css", "html", "json"], | ||
| customDistPath(_root, buildOutDir, _base) { | ||
| return path.resolve(buildOutDir, "monacoeditorwork"); | ||
| } | ||
| }), | ||
| vue(), | ||
| svgLoader(), | ||
| vueDevTools({ | ||
| // use export LAUNCH_EDITOR=cursor instead | ||
| // launchEditor: 'cursor' | ||
| }) | ||
| ], | ||
| build: { | ||
| minify: "esbuild", | ||
| rollupOptions: { | ||
| input: { | ||
| shell: resolve("src/renderer/shell/index.html"), | ||
| index: resolve("src/renderer/index.html"), | ||
| floating: resolve("src/renderer/floating/index.html") | ||
| } | ||
| } | ||
| } | ||
| } | ||
| }); | ||
| export { | ||
| electron_vite_config_default as default | ||
| }; |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,104 @@ | ||
| // electron.vite.config.ts | ||
| import { resolve } from "path"; | ||
| import { defineConfig, externalizeDepsPlugin } from "electron-vite"; | ||
| import vue from "@vitejs/plugin-vue"; | ||
| import autoprefixer from "autoprefixer"; | ||
| import tailwind from "tailwindcss"; | ||
| import vueDevTools from "vite-plugin-vue-devtools"; | ||
| import svgLoader from "vite-svg-loader"; | ||
| import monacoEditorPlugin from "vite-plugin-monaco-editor-esm"; | ||
| import path from "node:path"; | ||
| var electron_vite_config_default = defineConfig({ | ||
| main: { | ||
| plugins: [ | ||
| externalizeDepsPlugin({ | ||
| exclude: ["mermaid", "dompurify"] | ||
| }) | ||
| ], | ||
| resolve: { | ||
| alias: { | ||
| "@": resolve("src/main/"), | ||
| "@shared": resolve("src/shared") | ||
| } | ||
| }, | ||
| build: { | ||
| rollupOptions: { | ||
| external: ["sharp", "@duckdb/node-api"], | ||
| output: { | ||
| inlineDynamicImports: true, | ||
| manualChunks: void 0 | ||
| // Disable automatic chunk splitting | ||
| } | ||
| } | ||
| } | ||
| }, | ||
| preload: { | ||
| plugins: [externalizeDepsPlugin()], | ||
| resolve: { | ||
| alias: { | ||
| "@shared": resolve("src/shared") | ||
| } | ||
| }, | ||
| build: { | ||
| rollupOptions: { | ||
| input: { | ||
| index: resolve("src/preload/index.ts"), | ||
| floating: resolve("src/preload/floating-preload.ts") | ||
| } | ||
| } | ||
| } | ||
| }, | ||
| renderer: { | ||
| optimizeDeps: { | ||
| include: [ | ||
| "monaco-editor", | ||
| "axios" | ||
| ] | ||
| }, | ||
| resolve: { | ||
| alias: { | ||
| "@": resolve("src/renderer/src"), | ||
| "@shell": resolve("src/renderer/shell"), | ||
| "@shared": resolve("src/shared"), | ||
| vue: "vue/dist/vue.esm-bundler.js" | ||
| } | ||
| }, | ||
| css: { | ||
| postcss: { | ||
| // @ts-ignore | ||
| plugins: [tailwind(), autoprefixer()] | ||
| } | ||
| }, | ||
| server: { | ||
| host: "0.0.0.0" | ||
| // 防止代理干扰,导致vite-electron之间ws://localhost:5713和http://localhost:5713通信失败、页面组件无法加载 | ||
| }, | ||
| plugins: [ | ||
| monacoEditorPlugin({ | ||
| languageWorkers: ["editorWorkerService", "typescript", "css", "html", "json"], | ||
| customDistPath(_root, buildOutDir, _base) { | ||
| return path.resolve(buildOutDir, "monacoeditorwork"); | ||
| } | ||
| }), | ||
| vue(), | ||
| svgLoader(), | ||
| vueDevTools({ | ||
| // use export LAUNCH_EDITOR=cursor instead | ||
| // launchEditor: 'cursor' | ||
| }) | ||
| ], | ||
| build: { | ||
| minify: "esbuild", | ||
| rollupOptions: { | ||
| input: { | ||
| shell: resolve("src/renderer/shell/index.html"), | ||
| index: resolve("src/renderer/index.html"), | ||
| floating: resolve("src/renderer/floating/index.html") | ||
| } | ||
| } | ||
| } | ||
| } | ||
| }); | ||
| export { | ||
| electron_vite_config_default as default | ||
| }; | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -64,6 +64,7 @@ | |
| "@google/genai": "^1.13.0", | ||
| "@jxa/run": "^1.4.0", | ||
| "@modelcontextprotocol/sdk": "^1.17.2", | ||
| "@nut-tree/nut-js": "^4.2.0", | ||
| "axios": "^1.7.9", | ||
| "better-sqlite3-multiple-ciphers": "11.10.0", | ||
| "cheerio": "^1.0.0", | ||
|
|
@@ -80,6 +81,7 @@ | |
| "https-proxy-agent": "^7.0.6", | ||
| "jsonrepair": "^3.13.0", | ||
| "mammoth": "^1.9.0", | ||
| "marked": "^16.1.2", | ||
| "mime-types": "^2.1.35", | ||
|
Comment on lines
+84
to
85
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainSanitize Markdown rendering to prevent XSS in the renderer Adding If you’re using import DOMPurify from 'dompurify'
const html = DOMPurify.sanitize(marked(markdown))Quick check for sanitization paths: 🏁 Script executed: #!/bin/bash
rg -nP -C2 '\bmarked\(|MarkdownRenderer|v-html' src/renderer | sed -n '1,120p'
rg -nP -C2 'DOMPurify|sanitize' src/renderer | sed -n '1,120p'Length of output: 10324 🏁 Script executed: #!/bin/bash
# Locate renderMarkdown imports and definitions
rg -nP 'import\s+.*renderMarkdown' -n src/renderer
rg -nP 'export\s+function\s+renderMarkdown' -C4 -n src/renderer
# Check if MarkdownRenderer.vue uses v-html without sanitization
rg -nP 'v-html' -C3 src/renderer/src/components/markdown/MarkdownRenderer.vueLength of output: 743 Enforce sanitization on all v-html renderings We’ve identified several places where untrusted HTML is rendered via v-html without any sanitization, which poses an XSS risk. Please apply DOMPurify.sanitize (or a comparable HTML sanitizer) to every such output: • src/renderer/src/components/settings/AboutUsSettings.vue (line 126) - <div class="… overflow-y-auto" v-html="disclaimerContent"></div>
+ <div class="… overflow-y-auto"
+ v-html="DOMPurify.sanitize(disclaimerContent)"></div>Add at top: import DOMPurify from 'dompurify'• src/renderer/src/components/ui/UpdateDialog.vue (line 13) - v-html="renderMarkdown(getCommonMarkdown(), upgrade.updateInfo?.releaseNotes || '')"
+ v-html="DOMPurify.sanitize(
+ renderMarkdown(getCommonMarkdown(), upgrade.updateInfo?.releaseNotes || '')
+ )"Ensure • src/renderer/src/components/message/MessageBlockThink.vue (line 24) - <div … v-html="renderedContent"></div>
+ <div … v-html="DOMPurify.sanitize(renderedContent)"></div>• src/renderer/src/components/artifacts/SvgArtifact.vue (line 2) - const sanitizedContent = computed(() => props.block.content || '')
+ const sanitizedContent = computed(() =>
+ DOMPurify.sanitize(props.block.content || '', { SAFE_FOR_SVG: true })
+ )• src/renderer/src/components/artifacts/HTMLArtifact.vue - // const sanitizedContent = computed(() => {
- // if (!props.block.content) return ''
- // return DOMPurify.sanitize(props.block.content, {
- // WHOLE_DOCUMENT: true,
- // ADD_TAGS: ['script', 'style'],
- // })
- // })
+ const sanitizedContent = computed(() =>
+ DOMPurify.sanitize(props.block.content || '', {
+ WHOLE_DOCUMENT: true,
+ ADD_TAGS: ['script', 'style'],
+ })
+ )Optional cleanup: remove the 🤖 Prompt for AI Agents |
||
| "nanoid": "^5.1.5", | ||
| "ollama": "^0.5.16", | ||
|
|
@@ -152,10 +154,10 @@ | |
| "vite-plugin-monaco-editor-esm": "^2.0.2", | ||
| "vite-plugin-vue-devtools": "^8.0.0", | ||
| "vite-svg-loader": "^5.1.0", | ||
| "vue-renderer-markdown": "^0.0.34", | ||
| "vitest": "^3.2.4", | ||
| "vue": "^3.5.18", | ||
| "vue-i18n": "^11.1.11", | ||
| "vue-renderer-markdown": "^0.0.34", | ||
| "vue-router": "4", | ||
| "vue-tsc": "^2.2.10", | ||
| "vue-use-monaco": "^0.0.6", | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -11,7 +11,7 @@ import { LLMProviderPresenter } from './llmProviderPresenter' | |
| import { ConfigPresenter } from './configPresenter' | ||
| import { ThreadPresenter } from './threadPresenter' | ||
| import { DevicePresenter } from './devicePresenter' | ||
| import { UpgradePresenter } from './upgradePresenter' | ||
| // import { UpgradePresenter } from './upgradePresenter' | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 💡 Verification agent 🧩 Analysis chainRemove updater references rather than commenting them out; verify no remaining usages If the updater is intentionally disabled, delete the import, property, and instantiation to avoid drift and accidental reintroduction. Commented-out members on a central class invite merge conflicts later. Apply removals in these spots: -// import { UpgradePresenter } from './upgradePresenter'- // upgradePresenter: UpgradePresenter- // this.upgradePresenter = new UpgradePresenter()And confirm there are no lingering references: Also applies to: 48-48, 80-81 🏁 Script executed: #!/bin/bash
rg -n -C2 '\bupgradePresenter\b|UpgradePresenter' --glob '!**/node_modules/**'Length of output: 3848 Remove all commented-out The upgrade functionality appears to be fully disabled, yet there are numerous commented-out references, type declarations, store hooks, and documentation entries that should be cleaned up to prevent code drift and merge conflicts. Please apply the following removals and refactorings: • In • Delete the entire • In • In • In documentation files: After applying these changes, run: rg -n '\bupgradePresenter\b|UpgradePresenter\b' --glob '!**/node_modules/**'to confirm no residual references remain. 🤖 Prompt for AI Agents |
||
| import { FilePresenter } from './filePresenter/FilePresenter' | ||
| import { McpPresenter } from './mcpPresenter' | ||
| import { SyncPresenter } from './syncPresenter' | ||
|
|
@@ -45,7 +45,7 @@ export class Presenter implements IPresenter { | |
| configPresenter: ConfigPresenter | ||
| threadPresenter: ThreadPresenter | ||
| devicePresenter: DevicePresenter | ||
| upgradePresenter: UpgradePresenter | ||
| // upgradePresenter: UpgradePresenter | ||
| shortcutPresenter: ShortcutPresenter | ||
| filePresenter: FilePresenter | ||
| mcpPresenter: McpPresenter | ||
|
|
@@ -77,7 +77,7 @@ export class Presenter implements IPresenter { | |
| this.configPresenter | ||
| ) | ||
| this.mcpPresenter = new McpPresenter(this.configPresenter) | ||
| this.upgradePresenter = new UpgradePresenter() | ||
| // this.upgradePresenter = new UpgradePresenter() | ||
| this.shortcutPresenter = new ShortcutPresenter(this.configPresenter) | ||
| this.filePresenter = new FilePresenter() | ||
| this.syncPresenter = new SyncPresenter(this.configPresenter, this.sqlitePresenter) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -821,6 +821,41 @@ export class LLMProviderPresenter implements ILlmProviderPresenter { | |
|
|
||
| if (abortController.signal.aborted) break // Check after tool call returns | ||
|
|
||
| // 检查是否需要直接返回工具调用结果,不经过AI二次处理 | ||
| const directReturnValue = | ||
| typeof toolResponse.rawData.directReturn === 'boolean' | ||
| ? toolResponse.rawData.directReturn | ||
| : toolResponse.rawData.directReturn?.aiChange | ||
| console.log('index-toolResponse.rawData.directReturn', directReturnValue) | ||
| if (directReturnValue) { | ||
| console.log( | ||
| `[Agent Loop] Direct return tool result for ${toolCall.name}, skipping AI processing` | ||
|
Comment on lines
+825
to
+832
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion Direct-return detection reads rawData only; also check top-level 'directReturn'. ToolManager now sets directReturn at top-level and (after the fix) inside rawData. Make detection robust by checking both. Apply this diff: - const directReturnValue =
- typeof toolResponse.rawData.directReturn === 'boolean'
- ? toolResponse.rawData.directReturn
- : toolResponse.rawData.directReturn?.aiChange
+ const directReturnValue =
+ (typeof (toolResponse as any).directReturn === 'boolean'
+ ? (toolResponse as any).directReturn
+ : undefined) ??
+ (typeof toolResponse.rawData?.directReturn === 'boolean'
+ ? toolResponse.rawData.directReturn
+ : toolResponse.rawData?.directReturn?.aiChange)Also applies to: 835-857 🤖 Prompt for AI Agents |
||
| ) | ||
|
|
||
| // 直接返回工具调用结果,不继续对话 | ||
| yield { | ||
| type: 'response', | ||
| data: { | ||
| eventId, | ||
| tool_call: 'end', | ||
| tool_call_id: toolCall.id, | ||
| tool_call_name: toolCall.name, | ||
| tool_call_params: toolCall.arguments, | ||
| tool_call_server_name: toolDef.server.name, | ||
| tool_call_server_icons: toolDef.server.icons, | ||
| tool_call_server_description: toolDef.server.description, | ||
| tool_call_response: toolResponse.content, | ||
| tool_call_response_raw: toolResponse.rawData, | ||
| direct_return: true | ||
| } | ||
| } | ||
|
|
||
| // 结束agent循环 | ||
| console.log(`[Agent Loop] Ending agent loop for direct return, event: ${eventId}`) | ||
| needContinueConversation = false | ||
| break | ||
| } | ||
|
|
||
| // Check if permission is required | ||
| if (toolResponse.rawData.requiresPermission) { | ||
| console.log( | ||
|
|
@@ -919,6 +954,19 @@ export class LLMProviderPresenter implements ILlmProviderPresenter { | |
| tool_call_response_raw: toolResponse.rawData // Full raw data | ||
| } | ||
| } | ||
|
|
||
| // 如果工具响应中包含 directReturn 标志,则直接返回结果,不继续对话 | ||
| if (toolResponse.rawData && toolResponse.rawData.directReturn === true) { | ||
| console.log( | ||
| `[LLMProviderPresenter] Tool response has directReturn flag, ending agent loop` | ||
| ) | ||
| // 发送结束事件 | ||
| yield { | ||
| type: 'end', | ||
| data: { eventId, userStop: false } | ||
| } | ||
| return // 结束代理循环 | ||
| } | ||
| } else { | ||
| // Non-native FC: Add tool execution record to conversation history for next LLM turn. | ||
|
|
||
|
|
@@ -981,6 +1029,19 @@ export class LLMProviderPresenter implements ILlmProviderPresenter { | |
| tool_call_response_raw: toolResponse.rawData // Full raw data for ThreadPresenter to store | ||
| } | ||
| } | ||
|
|
||
| // 如果工具响应中包含 directReturn 标志,则直接返回结果,不继续对话 | ||
| if (toolResponse.rawData && toolResponse.rawData.directReturn === true) { | ||
| console.log( | ||
| `[LLMProviderPresenter] Tool response has directReturn flag, ending agent loop` | ||
| ) | ||
| // 发送结束事件 | ||
| yield { | ||
| type: 'end', | ||
| data: { eventId, userStop: false } | ||
| } | ||
| return // 结束代理循环 | ||
| } | ||
| } | ||
| } catch (toolError) { | ||
| if (abortController.signal.aborted) break // Check after tool error | ||
|
|
||
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
Duplicate configuration file with identical content
This file appears to be an exact duplicate of
electron.vite.config.1755071894873.mjs. Having multiple identical configuration files can lead to confusion and maintenance issues.Consider removing the duplicate file and keeping only one configuration file to avoid confusion.
🏁 Script executed:
Length of output: 644
🏁 Script executed:
Length of output: 968
Remove duplicate timestamped Vite config files
Both
electron.vite.config.1755071894873.mjsandelectron.vite.config.1755572548960.mjsare bit-for-bit identical (same SHA-256 checksum) and aren’t referenced anywhere in the codebase. Keeping both can cause confusion and accidental drift.• Delete one (or both) of the timestamped
.mjsfiles – e.g.:electron.vite.config.1755071894873.mjselectron.vite.config.1755572548960.mjs• Consolidate on the single canonical config:
electron.vite.config.tsas your primary Vite/Electron configuration..tsfile directly, if they currently point to a timestamped.mjs.This cleanup will reduce maintenance overhead and eliminate ambiguity about which config is “authoritative.”
🤖 Prompt for AI Agents