@pheem49/mint 1.8.0 → 1.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/release.yml +1 -1
- package/Cargo.lock +34 -3
- package/Cargo.toml +2 -2
- package/README.md +20 -6
- package/Release_Note.md +129 -0
- package/assets/Mint_CLI.png +0 -0
- package/bin/mint +0 -0
- package/crates/mint-cli/Cargo.toml +1 -0
- package/crates/mint-cli/src/agent.rs +242 -54
- package/crates/mint-cli/src/main.rs +397 -74
- package/crates/mint-core/src/agent_loop.rs +1 -0
- package/crates/mint-core/src/api_server.rs +64 -2
- package/crates/mint-core/src/browser.rs +456 -0
- package/crates/mint-core/src/channels.rs +1 -0
- package/crates/mint-core/src/chat.rs +3 -0
- package/crates/mint-core/src/config.rs +130 -1
- package/crates/mint-core/src/lib.rs +19 -0
- package/crates/mint-core/src/orchestration.rs +317 -12
- package/crates/mint-core/src/skills.rs +113 -4
- package/crates/mint-core/src/tts.rs +3 -1
- package/crates/mint-core/src/workflows.rs +1 -22
- package/flatpak/com.pheem49.mint.desktop +9 -0
- package/flatpak/com.pheem49.mint.metainfo.xml +35 -0
- package/flatpak/com.pheem49.mint.yaml +52 -0
- package/package.json +1 -1
- package/shared-modules/.github/workflows/ci.yml +120 -0
- package/shared-modules/.github/workflows/update.yaml +28 -0
- package/shared-modules/CODEOWNERS +23 -0
- package/shared-modules/README.md +79 -0
- package/shared-modules/SDL/README.md +3 -0
- package/shared-modules/SDL/SDL-1.2.15.json +51 -0
- package/shared-modules/SDL/SDL_Pango-0.1.2-API-adds.patch +118 -0
- package/shared-modules/SDL/SDL_image-1.2.12.json +25 -0
- package/shared-modules/SDL/SDL_mixer-1.2.12.json +27 -0
- package/shared-modules/SDL/SDL_net-1.2.8.json +19 -0
- package/shared-modules/SDL/SDL_pango-0.1.2.json +26 -0
- package/shared-modules/SDL/SDL_ttf-2.0.11.json +28 -0
- package/shared-modules/SDL/libsdl-1.2.15-strict-prototypes.patch +316 -0
- package/shared-modules/SDL/sdl-check-for-SDL_VIDEO_X11_BACKINGSTORE.patch +24 -0
- package/shared-modules/SDL/sdl-libx11-build.patch +59 -0
- package/shared-modules/SDL/sdl-sysloadso-buffer-length.patch +27 -0
- package/shared-modules/SDL/sdl12-compat.json +22 -0
- package/shared-modules/SDL2/SDL2_image.json +27 -0
- package/shared-modules/SDL2/SDL2_ttf.json +29 -0
- package/shared-modules/cld2/CMakeLists.txt +157 -0
- package/shared-modules/cld2/cld2.json +24 -0
- package/shared-modules/clutter/clutter.json +72 -0
- package/shared-modules/dbus-glib/dbus-glib.json +29 -0
- package/shared-modules/glew/glew.json +36 -0
- package/shared-modules/glu/glu-9.json +12 -0
- package/shared-modules/gtk2/arc-gtk2-theme-Replace-Inkscape-dependency-with-rsvg-convert.patch +180 -0
- package/shared-modules/gtk2/gtk2-common-themes.json +360 -0
- package/shared-modules/gtk2/gtk2-fix-crash-in-show-uri.patch +30 -0
- package/shared-modules/gtk2/gtk2-gcc14.patch +281 -0
- package/shared-modules/gtk2/gtk2-gcc15.patch +250 -0
- package/shared-modules/gtk2/gtk2-murrine-c99.patch +34 -0
- package/shared-modules/gtk2/gtk2-use-adwaita-theme.patch +80 -0
- package/shared-modules/gtk2/gtk2.json +120 -0
- package/shared-modules/gtk2/ibus-gcc15.patch +13 -0
- package/shared-modules/gtk2/murrine-engine-fix-crash.patch +12 -0
- package/shared-modules/gtk2/readme.md +10 -0
- package/shared-modules/intltool/intltool-0.51.json +20 -0
- package/shared-modules/intltool/intltool-perl5.26-regex-fixes.patch +59 -0
- package/shared-modules/lame/0001-Build-xmm_quantize_sub.c-with-msse.patch +34 -0
- package/shared-modules/lame/0002-Include-GTK-1-autoconf-directives-in-build-system.patch +222 -0
- package/shared-modules/lame/0003-Patch-out-remaining-ansi2knr.patch +70 -0
- package/shared-modules/lame/0004-initscr-is-not-used-anywhere-in-lame-sourcetree.patch +41 -0
- package/shared-modules/lame/0005-Update-autoconf.patch +81 -0
- package/shared-modules/lame/lame-3.99.5.json +32 -0
- package/shared-modules/libappindicator/libappindicator-gtk2-12.10.json +140 -0
- package/shared-modules/libappindicator/libappindicator-gtk3-12.10.json +140 -0
- package/shared-modules/libappindicator/libappindicator-gtk3-introspection-12.10.json +140 -0
- package/shared-modules/libappindicator/patches/0001-libappindicator-ftbfs.patch +20 -0
- package/shared-modules/libappindicator/patches/0002-libappindicator-no-python.patch +888 -0
- package/shared-modules/libappindicator/patches/0003-libappindicator-fix-crash-from-incorrect-signal-emission.patch +74 -0
- package/shared-modules/libappindicator/patches/0004-libappindicator-fix-crash-iterating-icon-themes.patch +11 -0
- package/shared-modules/libayatana-appindicator/0001-Fix-HAVE_VALGRIND-AM_CONDITIONAL.patch +51 -0
- package/shared-modules/libayatana-appindicator/0001-Make-introspection-configurable.patch +44 -0
- package/shared-modules/libayatana-appindicator/libayatana-appindicator-gtk3.json +121 -0
- package/shared-modules/libcanberra/0001-gtk-Don-t-assume-all-GdkDisplays-are-GdkX11Displays-.patch +123 -0
- package/shared-modules/libcanberra/libcanberra.json +44 -0
- package/shared-modules/libmad/libmad.json +19 -0
- package/shared-modules/libsecret/libsecret.json +23 -0
- package/shared-modules/libsoup/libsoup-2.4.json +14 -0
- package/shared-modules/libusb/libusb.json +27 -0
- package/shared-modules/linux-audio/dssi.json +31 -0
- package/shared-modules/linux-audio/fftw3f-static.json +44 -0
- package/shared-modules/linux-audio/fftw3f.json +42 -0
- package/shared-modules/linux-audio/fluidsynth2-static.json +30 -0
- package/shared-modules/linux-audio/fluidsynth2.json +19 -0
- package/shared-modules/linux-audio/ladspa.json +26 -0
- package/shared-modules/linux-audio/lash.json +35 -0
- package/shared-modules/linux-audio/libinstpatch.json +16 -0
- package/shared-modules/linux-audio/liblo-static.json +22 -0
- package/shared-modules/linux-audio/liblo.json +17 -0
- package/shared-modules/linux-audio/lilv-static.json +94 -0
- package/shared-modules/linux-audio/lilv.json +82 -0
- package/shared-modules/linux-audio/lrdf.json +57 -0
- package/shared-modules/linux-audio/lv2.json +21 -0
- package/shared-modules/linux-audio/patches/lash/0001-configure-remove-aclocal-m4.patch +29 -0
- package/shared-modules/linux-audio/patches/lash/0002-Fix-build-with-recent-gcc.patch +73 -0
- package/shared-modules/linux-audio/patches/lash/0003-Fix-linking.patch +136 -0
- package/shared-modules/linux-audio/patches/raptor-libxml.patch +31 -0
- package/shared-modules/linux-audio/patches/stk-install.sh +54 -0
- package/shared-modules/linux-audio/stk.json +28 -0
- package/shared-modules/lua5.1/0004-Fix-stack-overflow-in-vararg-functions.patch +21 -0
- package/shared-modules/lua5.1/lua-5.1.5-so.patch +43 -0
- package/shared-modules/lua5.1/lua-5.1.5.json +42 -0
- package/shared-modules/lua5.3/lua-5.3.5.json +41 -0
- package/shared-modules/lua5.3/lua.pc.in +8 -0
- package/shared-modules/lua5.3/luaconf.patch +47 -0
- package/shared-modules/lua5.3/makefile-add-liblua.so-rule.patch +47 -0
- package/shared-modules/lua5.4/lua-5.4.json +41 -0
- package/shared-modules/lua5.4/lua.pc.in +8 -0
- package/shared-modules/lua5.4/luaconf.patch +34 -0
- package/shared-modules/lua5.4/makefile-add-liblua.so-rule.patch +42 -0
- package/shared-modules/luajit/luajit.json +30 -0
- package/shared-modules/lzo/lzo.json +27 -0
- package/shared-modules/mac/mac.json +18 -0
- package/shared-modules/physfs/physfs.json +26 -0
- package/shared-modules/pygame/audiofile-gcc6.patch +21 -0
- package/shared-modules/pygame/cython-fixes.patch +34 -0
- package/shared-modules/pygame/fluidsynth-no-rawmidi.patch +69 -0
- package/shared-modules/pygame/glib.patch +25 -0
- package/shared-modules/pygame/pygame1-python2.json +157 -0
- package/shared-modules/pygtk/pygtk-c99.patch +15 -0
- package/shared-modules/pygtk/pygtk.json +117 -0
- package/shared-modules/pygtk/pygtk.patch +39 -0
- package/shared-modules/python2.7/python-2.7.json +48 -0
- package/shared-modules/smpeg/smpeg-0.4.5.json +50 -0
- package/shared-modules/smpeg/smpeg-am-prog-as.patch +12 -0
- package/shared-modules/smpeg/smpeg-export-mpegaudio-class.patch +17 -0
- package/shared-modules/smpeg/smpeg-gcc6.patch +40 -0
- package/shared-modules/smpeg/smpeg-no-gtk.patch +37 -0
- package/shared-modules/squashfs-tools/squashfs-tools.json +25 -0
- package/shared-modules/update-checker.sh +50 -0
- package/shared-modules/vorbisgain/0001-temp_files.patch +75 -0
- package/shared-modules/vorbisgain/0002-errno.patch +33 -0
- package/shared-modules/vorbisgain/0003-manpage.patch +248 -0
- package/shared-modules/vorbisgain/0004-vorbisgain_mtime.patch +183 -0
- package/shared-modules/vorbisgain/0005-double_fclose.patch +21 -0
- package/shared-modules/vorbisgain/0006-manpage_hyphens.patch +56 -0
- package/shared-modules/vorbisgain/0007-recursively_spelling.patch +14 -0
- package/shared-modules/vorbisgain/0008-manpage_recursion_mistake.patch +23 -0
- package/shared-modules/vorbisgain/0009-hardening.patch +13 -0
- package/shared-modules/vorbisgain/0010-fclose.patch +13 -0
- package/shared-modules/vorbisgain/README.md +23 -0
- package/shared-modules/vorbisgain/vorbisgain_0.37-2.json +29 -0
- package/src/renderer/shared/agentProgress.ts +145 -0
- package/src/renderer/shared/calculator.ts +52 -0
- package/src/renderer/shared/components/AgentActivityDrawer.tsx +72 -0
- package/src/renderer/shared/components/AgentActivityTable.tsx +66 -0
- package/src/renderer/shared/components/ApprovalCard.tsx +93 -0
- package/src/renderer/shared/components/ChatCodeBlock.tsx +75 -0
- package/src/renderer/shared/components/ThinkingBlock.tsx +101 -0
- package/src/renderer/shared/constants/models.ts +91 -0
- package/src/renderer/shared/platform.ts +82 -0
- package/src/renderer/shared/types.ts +140 -0
- package/src/renderer/shared/utils/agentActivity.ts +187 -0
- package/src/renderer/shared/utils/approval.ts +1 -0
- package/src/renderer/shared/utils/approval.tsx +64 -0
- package/src/renderer/shared/utils/markdown.tsx +347 -0
- package/src/renderer/shared/utils/providers.ts +36 -0
- package/src/renderer/shared/utils/speech.ts +216 -0
- package/src/renderer/shared/utils/ui.ts +147 -0
- package/src/renderer/src/agentProgress.ts +11 -39
- package/src/renderer/src/calculator.ts +3 -46
- package/src/renderer/src/components/ChatPanel.tsx +230 -1202
- package/src/renderer/src/components/ImageStudioPanel.tsx +11 -1
- package/src/renderer/src/components/MintDashboard.tsx +63 -171
- package/src/renderer/src/components/PicturesLibrary.tsx +32 -8
- package/src/renderer/src/components/Settings/AgentsTab.tsx +362 -0
- package/src/renderer/src/components/Settings/AutomationTab.tsx +0 -22
- package/src/renderer/src/components/Settings/PluginsTab.tsx +221 -41
- package/src/renderer/src/components/SettingsWindow.tsx +54 -12
- package/src/renderer/src/components/WorkspacePanel.tsx +66 -9
- package/src/renderer/src/css/image-studio.css +6 -0
- package/src/renderer/src/css/styles.css +148 -11
- package/src/renderer/src/index.css +12 -1
- package/src/renderer/src/tauri.ts +169 -143
- package/src/renderer/src-web/agentProgress.ts +11 -39
- package/src/renderer/src-web/calculator.ts +3 -46
- package/src/renderer/src-web/components/ChatPanel.tsx +223 -1199
- package/src/renderer/src-web/components/ImageStudioPanel.tsx +11 -1
- package/src/renderer/src-web/components/MintDashboard.tsx +31 -172
- package/src/renderer/src-web/components/PicturesLibrary.tsx +23 -29
- package/src/renderer/src-web/components/Settings/AgentsTab.tsx +362 -0
- package/src/renderer/src-web/components/Settings/AutomationTab.tsx +0 -22
- package/src/renderer/src-web/components/Settings/PluginsTab.tsx +221 -41
- package/src/renderer/src-web/components/SettingsWindow.tsx +54 -12
- package/src/renderer/src-web/css/image-studio.css +6 -0
- package/src/renderer/src-web/css/styles.css +184 -12
- package/src/renderer/src-web/index.css +12 -1
- package/src/renderer/src-web/tauri.ts +204 -104
- package/src-tauri/src/browser.rs +1 -141
- package/src-tauri/src/headless.rs +3 -2
- package/src-tauri/src/lib.rs +254 -59
- package/src-tauri/src/webhooks.rs +1 -0
- package/src-tauri/src/workflows.rs +57 -4
- package/src-tauri/tauri.conf.json +1 -1
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* shared/components/ThinkingBlock.tsx
|
|
3
|
+
* Renders the collapsible reasoning thoughts block from the LLM.
|
|
4
|
+
* Shared by both Desktop and Web ChatPanel — do NOT duplicate this.
|
|
5
|
+
*/
|
|
6
|
+
import { useEffect, useRef, useState } from 'react'
|
|
7
|
+
import { renderFormattedMessage } from '../utils/markdown'
|
|
8
|
+
|
|
9
|
+
const THINKING_LABELS = {
|
|
10
|
+
live: 'Thinking…',
|
|
11
|
+
completed: (count: number) => `Thinking process (${count} steps)`,
|
|
12
|
+
step: (index: number) => `Step ${index}`,
|
|
13
|
+
emptyHint: 'Model didn\'t send thinking steps this time',
|
|
14
|
+
} as const
|
|
15
|
+
|
|
16
|
+
interface Props {
|
|
17
|
+
thoughts: string[]
|
|
18
|
+
isLive?: boolean
|
|
19
|
+
blockKey: string
|
|
20
|
+
expanded?: boolean
|
|
21
|
+
onExpandedChange?: (key: string, open: boolean) => void
|
|
22
|
+
showEmptyHint?: boolean
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
export function ThinkingBlock({
|
|
26
|
+
thoughts,
|
|
27
|
+
isLive = false,
|
|
28
|
+
blockKey,
|
|
29
|
+
expanded,
|
|
30
|
+
onExpandedChange,
|
|
31
|
+
showEmptyHint = false,
|
|
32
|
+
}: Props) {
|
|
33
|
+
const contentRef = useRef<HTMLDivElement>(null)
|
|
34
|
+
const isControlled = expanded !== undefined && Boolean(onExpandedChange)
|
|
35
|
+
const [localOpen, setLocalOpen] = useState(isLive)
|
|
36
|
+
const isOpen = isControlled ? Boolean(expanded) : localOpen
|
|
37
|
+
|
|
38
|
+
const setOpen = (open: boolean) => {
|
|
39
|
+
if (isControlled && onExpandedChange) onExpandedChange(blockKey, open)
|
|
40
|
+
else setLocalOpen(open)
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
useEffect(() => {
|
|
44
|
+
if (isLive && !isControlled) setLocalOpen(true)
|
|
45
|
+
}, [isLive, isControlled])
|
|
46
|
+
|
|
47
|
+
useEffect(() => {
|
|
48
|
+
if (!isLive || !isOpen || !contentRef.current) return
|
|
49
|
+
contentRef.current.scrollTop = contentRef.current.scrollHeight
|
|
50
|
+
}, [thoughts, isLive, isOpen])
|
|
51
|
+
|
|
52
|
+
if (thoughts.length === 0 && !showEmptyHint) return null
|
|
53
|
+
|
|
54
|
+
if (thoughts.length === 0) {
|
|
55
|
+
return (
|
|
56
|
+
<div className="thinking-block thinking-block-empty-state" data-live={isLive ? 'true' : 'false'}>
|
|
57
|
+
<span className="thinking-block-empty">{THINKING_LABELS.emptyHint}</span>
|
|
58
|
+
</div>
|
|
59
|
+
)
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<div className={`thinking-block${isLive ? ' is-live' : ''}`}>
|
|
64
|
+
<button
|
|
65
|
+
type="button"
|
|
66
|
+
className="thinking-block-header"
|
|
67
|
+
onClick={() => setOpen(!isOpen)}
|
|
68
|
+
aria-expanded={isOpen}
|
|
69
|
+
>
|
|
70
|
+
<svg width="13" height="13" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" style={{ flexShrink: 0 }}>
|
|
71
|
+
<path d="M9.09 9a3 3 0 0 1 5.83 1c0 2-3 3-3 3" />
|
|
72
|
+
<line x1="12" y1="17" x2="12.01" y2="17" />
|
|
73
|
+
<circle cx="12" cy="12" r="10" />
|
|
74
|
+
</svg>
|
|
75
|
+
<span className="thinking-block-label">
|
|
76
|
+
{isLive ? THINKING_LABELS.live : THINKING_LABELS.completed(thoughts.length)}
|
|
77
|
+
</span>
|
|
78
|
+
{isLive && <span className="thinking-block-live-dot" aria-hidden="true" />}
|
|
79
|
+
<span className={`thinking-block-chevron${isOpen ? ' is-open' : ''}`} aria-hidden="true">
|
|
80
|
+
<svg width="10" height="10" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" strokeLinejoin="round">
|
|
81
|
+
<polyline points="9 18 15 12 9 6" />
|
|
82
|
+
</svg>
|
|
83
|
+
</span>
|
|
84
|
+
</button>
|
|
85
|
+
{isOpen && (
|
|
86
|
+
<div className="thinking-block-body">
|
|
87
|
+
<div className="thinking-block-content" ref={contentRef}>
|
|
88
|
+
{thoughts.map((thought, index) => (
|
|
89
|
+
<div className="thinking-block-step" key={`${blockKey}-${index}`}>
|
|
90
|
+
{thoughts.length > 1 && (
|
|
91
|
+
<div className="thinking-block-step-label">{THINKING_LABELS.step(index + 1)}</div>
|
|
92
|
+
)}
|
|
93
|
+
<div className="thinking-block-step-body">{renderFormattedMessage(thought)}</div>
|
|
94
|
+
</div>
|
|
95
|
+
))}
|
|
96
|
+
</div>
|
|
97
|
+
</div>
|
|
98
|
+
)}
|
|
99
|
+
</div>
|
|
100
|
+
)
|
|
101
|
+
}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* shared/constants/models.ts
|
|
3
|
+
* Canonical AI model lists — single source of truth for both Desktop and Web renderers.
|
|
4
|
+
*
|
|
5
|
+
* To add or update models, edit only this file.
|
|
6
|
+
* Both src/components/ChatPanel.tsx and src-web/components/ChatPanel.tsx
|
|
7
|
+
* import from here via their respective re-export files.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export const GEMINI_MODELS = [
|
|
11
|
+
'gemini-2.5-flash',
|
|
12
|
+
'gemini-2.5-pro',
|
|
13
|
+
'gemini-2.0-flash',
|
|
14
|
+
'gemini-2.0-flash-lite',
|
|
15
|
+
'gemini-1.5-flash',
|
|
16
|
+
'gemini-1.5-pro',
|
|
17
|
+
] as const
|
|
18
|
+
|
|
19
|
+
export const OPENAI_MODELS = [
|
|
20
|
+
'gpt-4.1',
|
|
21
|
+
'gpt-4.1-mini',
|
|
22
|
+
'gpt-4.1-nano',
|
|
23
|
+
'gpt-4o',
|
|
24
|
+
'gpt-4o-mini',
|
|
25
|
+
'o3',
|
|
26
|
+
'o4-mini',
|
|
27
|
+
'gpt-4-turbo',
|
|
28
|
+
] as const
|
|
29
|
+
|
|
30
|
+
export const ANTHROPIC_MODELS = [
|
|
31
|
+
'claude-sonnet-4-20250514',
|
|
32
|
+
'claude-opus-4-20250514',
|
|
33
|
+
'claude-haiku-35-20241022',
|
|
34
|
+
'claude-3-7-sonnet-latest',
|
|
35
|
+
'claude-3-5-sonnet-latest',
|
|
36
|
+
'claude-3-5-haiku-latest',
|
|
37
|
+
] as const
|
|
38
|
+
|
|
39
|
+
export const OPENROUTER_MODELS = [
|
|
40
|
+
'openai/gpt-4o-mini',
|
|
41
|
+
'openai/gpt-4o',
|
|
42
|
+
'anthropic/claude-sonnet-4',
|
|
43
|
+
'anthropic/claude-haiku-3.5',
|
|
44
|
+
'anthropic/claude-3.5-sonnet',
|
|
45
|
+
'anthropic/claude-3.5-haiku',
|
|
46
|
+
'google/gemini-2.5-flash',
|
|
47
|
+
'meta-llama/llama-3.3-70b-instruct',
|
|
48
|
+
'mistralai/mistral-large',
|
|
49
|
+
] as const
|
|
50
|
+
|
|
51
|
+
export const DEEPSEEK_MODELS = [
|
|
52
|
+
'deepseek-v4-flash',
|
|
53
|
+
'deepseek-v4-pro',
|
|
54
|
+
'deepseek-chat',
|
|
55
|
+
'deepseek-reasoner',
|
|
56
|
+
] as const
|
|
57
|
+
|
|
58
|
+
export const HF_MODELS = [
|
|
59
|
+
'meta-llama/Llama-3.3-70B-Instruct',
|
|
60
|
+
'meta-llama/Meta-Llama-3-8B-Instruct',
|
|
61
|
+
'meta-llama/Llama-3.2-3B-Instruct',
|
|
62
|
+
'Qwen/Qwen2.5-72B-Instruct',
|
|
63
|
+
'Qwen/Qwen2.5-Coder-32B-Instruct',
|
|
64
|
+
'Qwen/Qwen3-235B-A22B',
|
|
65
|
+
'mistralai/Mistral-Small-24B-Instruct-2501',
|
|
66
|
+
'mistralai/Mistral-7B-Instruct-v0.3',
|
|
67
|
+
'google/gemma-3-27b-it',
|
|
68
|
+
'google/gemma-2-9b-it',
|
|
69
|
+
] as const
|
|
70
|
+
|
|
71
|
+
export const LOCAL_MODELS = [
|
|
72
|
+
'local-model',
|
|
73
|
+
'Qwen/Qwen2.5-7B-Instruct-GGUF',
|
|
74
|
+
'meta-llama/Llama-3.2-3B-Instruct-GGUF',
|
|
75
|
+
'lmstudio-community/gemma-2-9b-it-GGUF',
|
|
76
|
+
] as const
|
|
77
|
+
|
|
78
|
+
/** Mutable list — populated at runtime via Ollama API */
|
|
79
|
+
export const OLLAMA_MODELS: string[] = []
|
|
80
|
+
|
|
81
|
+
/** Map from provider key → model list for easy lookup */
|
|
82
|
+
export const PROVIDER_MODELS = {
|
|
83
|
+
gemini: GEMINI_MODELS,
|
|
84
|
+
openai: OPENAI_MODELS,
|
|
85
|
+
anthropic: ANTHROPIC_MODELS,
|
|
86
|
+
openrouter: OPENROUTER_MODELS,
|
|
87
|
+
deepseek: DEEPSEEK_MODELS,
|
|
88
|
+
huggingface: HF_MODELS,
|
|
89
|
+
local_openai: LOCAL_MODELS,
|
|
90
|
+
ollama: OLLAMA_MODELS,
|
|
91
|
+
} as const
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* shared/platform.ts
|
|
3
|
+
* Unified platform API interface for both Desktop and Web renderers.
|
|
4
|
+
* Source of truth: Both tauri.ts files implement this API surface.
|
|
5
|
+
*/
|
|
6
|
+
import type {
|
|
7
|
+
RuntimeStatus,
|
|
8
|
+
ChatResponse,
|
|
9
|
+
TtsUrl,
|
|
10
|
+
InteractionMemory,
|
|
11
|
+
ChatSession,
|
|
12
|
+
PictureEntry,
|
|
13
|
+
ImageGenRequest,
|
|
14
|
+
ImageGenProviders,
|
|
15
|
+
ImageGenResponse,
|
|
16
|
+
WorkspaceTreeEntry,
|
|
17
|
+
CodeEdit,
|
|
18
|
+
CodeEditProposal,
|
|
19
|
+
DetectedTools,
|
|
20
|
+
LearnedSkill,
|
|
21
|
+
AgentProgress,
|
|
22
|
+
} from './types'
|
|
23
|
+
|
|
24
|
+
export interface MintPlatformApi {
|
|
25
|
+
getRuntimeStatus(): Promise<RuntimeStatus>
|
|
26
|
+
detectSystemTools(): Promise<DetectedTools>
|
|
27
|
+
sendChatMessage(
|
|
28
|
+
message: string,
|
|
29
|
+
imageDataUri?: string | null,
|
|
30
|
+
audioDataUri?: string | null,
|
|
31
|
+
documentAttachment?: any | null,
|
|
32
|
+
workspacePath?: string | null,
|
|
33
|
+
chatId?: string | null,
|
|
34
|
+
agentId?: string | null,
|
|
35
|
+
): Promise<ChatResponse>
|
|
36
|
+
streamChatMessage(
|
|
37
|
+
message: string,
|
|
38
|
+
onChunk: (chunk: string) => void,
|
|
39
|
+
imageDataUri?: string | null,
|
|
40
|
+
audioDataUri?: string | null,
|
|
41
|
+
systemInstruction?: string,
|
|
42
|
+
onProgress?: (progress: AgentProgress) => void,
|
|
43
|
+
documentAttachment?: any | null,
|
|
44
|
+
workspacePath?: string | null,
|
|
45
|
+
chatId?: string | null,
|
|
46
|
+
agentId?: string | null,
|
|
47
|
+
): Promise<ChatResponse>
|
|
48
|
+
getTtsUrls(text: string): Promise<TtsUrl[]>
|
|
49
|
+
cancelChatMessage(chatId: string): Promise<void>
|
|
50
|
+
getRecentInteractions(limit?: number, chatId?: string | null): Promise<InteractionMemory[]>
|
|
51
|
+
saveSystemInteraction(
|
|
52
|
+
chatId: string,
|
|
53
|
+
userText: string,
|
|
54
|
+
provider: string,
|
|
55
|
+
model: string,
|
|
56
|
+
): Promise<any>
|
|
57
|
+
|
|
58
|
+
saveInteractionAgentActivity(interactionId: number, progress: any[]): Promise<void>
|
|
59
|
+
listChatSessions(): Promise<ChatSession[]>
|
|
60
|
+
deleteChatSession(chatId: string): Promise<number>
|
|
61
|
+
renameChatSession(chatId: string, newTitle: string): Promise<number>
|
|
62
|
+
getProfileValue(key: string): Promise<string>
|
|
63
|
+
setProfileValue(key: string, value: string): Promise<boolean>
|
|
64
|
+
listLearnedSkills(workspacePath?: string): Promise<LearnedSkill[]>
|
|
65
|
+
addLearnedSkill(name: string, content: string): Promise<LearnedSkill>
|
|
66
|
+
deleteLearnedSkill(name: string): Promise<number>
|
|
67
|
+
clearChatHistory(chatId?: string | null): Promise<number>
|
|
68
|
+
listSavedPictures(): Promise<PictureEntry[]>
|
|
69
|
+
generateImages(req: ImageGenRequest): Promise<ImageGenResponse>
|
|
70
|
+
getImageGenProviders(): Promise<ImageGenProviders>
|
|
71
|
+
setDefaultImageProvider(provider: string): Promise<boolean>
|
|
72
|
+
getWorkspaceTree(path?: string | null): Promise<WorkspaceTreeEntry>
|
|
73
|
+
createWorkspaceFile(path: string): Promise<void>
|
|
74
|
+
createWorkspaceFolder(path: string): Promise<void>
|
|
75
|
+
deleteWorkspaceItem(path: string): Promise<void>
|
|
76
|
+
selectWorkspaceDirectory(): Promise<string | null>
|
|
77
|
+
submitToolApproval(token: string, approved: boolean, answer?: string): Promise<void>
|
|
78
|
+
proposeCodeEdits(root: string, edits: CodeEdit[]): Promise<CodeEditProposal>
|
|
79
|
+
applyCodeEdits(root: string, edits: CodeEdit[], approvalToken: string): Promise<any>
|
|
80
|
+
listen<T>(event: string, handler: (event: { payload: T }) => void): Promise<() => void>
|
|
81
|
+
readClipboardImage(): Promise<string | null>
|
|
82
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* shared/types.ts
|
|
3
|
+
* Shared TypeScript interfaces and types for both Desktop and Web renderers.
|
|
4
|
+
* Source of truth: Import from here, not from local declarations in tauri.ts.
|
|
5
|
+
*/
|
|
6
|
+
|
|
7
|
+
export interface RuntimeStatus {
|
|
8
|
+
backend: string
|
|
9
|
+
configPath: string
|
|
10
|
+
activeProvider: string
|
|
11
|
+
availableProviders: string[]
|
|
12
|
+
integrations: Record<string, unknown>
|
|
13
|
+
localIp?: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface ChatResponse {
|
|
17
|
+
provider: string
|
|
18
|
+
model: string
|
|
19
|
+
text: string
|
|
20
|
+
fallbackProvider?: string | null
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export interface TtsUrl {
|
|
24
|
+
shortText: string
|
|
25
|
+
url: string
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
export interface DocumentAttachment {
|
|
29
|
+
filename: string
|
|
30
|
+
dataUri: string
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export type AgentProgress =
|
|
34
|
+
| { type: 'Thinking'; data: { elapsed_secs: number; agent_name?: string; model_name?: string } }
|
|
35
|
+
| { type: 'Thought'; data: { thought: string } }
|
|
36
|
+
| { type: 'ToolStart'; data: { action: string; input: Record<string, unknown> } }
|
|
37
|
+
| { type: 'ToolEnd'; data: { action: string; input: Record<string, unknown>; result: string } }
|
|
38
|
+
|
|
39
|
+
export interface InteractionMemory {
|
|
40
|
+
id: number
|
|
41
|
+
chatId: string
|
|
42
|
+
userText: string
|
|
43
|
+
aiText: string
|
|
44
|
+
provider: string
|
|
45
|
+
model: string
|
|
46
|
+
fallbackProvider?: string | null
|
|
47
|
+
createdAt: string
|
|
48
|
+
agentActivity?: AgentProgress[] | null
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export interface ChatSession {
|
|
52
|
+
id: string
|
|
53
|
+
title: string
|
|
54
|
+
kind: string
|
|
55
|
+
createdAt: string
|
|
56
|
+
updatedAt: string
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export interface PictureEntry {
|
|
60
|
+
id: string
|
|
61
|
+
filename: string
|
|
62
|
+
path: string
|
|
63
|
+
mimeType: string
|
|
64
|
+
createdAt: string
|
|
65
|
+
source: string
|
|
66
|
+
message: string
|
|
67
|
+
thumbnailPath?: string
|
|
68
|
+
url?: string
|
|
69
|
+
thumbnailUrl?: string
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
export interface ImageGenRequest {
|
|
73
|
+
prompt: string
|
|
74
|
+
negativePrompt?: string
|
|
75
|
+
aspectRatio?: '1:1' | '16:9' | '9:16' | '4:3'
|
|
76
|
+
numImages?: number
|
|
77
|
+
model?: string
|
|
78
|
+
provider?: string
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
export interface ImageGenProviders {
|
|
82
|
+
active: string
|
|
83
|
+
available: string[]
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export interface ImageGenResponse {
|
|
87
|
+
images: PictureEntry[]
|
|
88
|
+
model: string
|
|
89
|
+
provider: string
|
|
90
|
+
prompt: string
|
|
91
|
+
description?: string | null
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
export interface CodeEdit {
|
|
95
|
+
path: string
|
|
96
|
+
content: string
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export interface CodeEditProposal {
|
|
100
|
+
approvalRequired: boolean
|
|
101
|
+
approvalToken: string
|
|
102
|
+
edits: Array<{ path: string; existed: boolean; diff: string }>
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
export interface WorkspaceTreeEntry {
|
|
106
|
+
name: string
|
|
107
|
+
path: string
|
|
108
|
+
kind: 'file' | 'directory'
|
|
109
|
+
children: WorkspaceTreeEntry[]
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export interface DetectedTools {
|
|
113
|
+
docker: boolean
|
|
114
|
+
git: boolean
|
|
115
|
+
gh: boolean
|
|
116
|
+
node: boolean
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
export interface LearnedSkill {
|
|
120
|
+
id: number
|
|
121
|
+
name: string
|
|
122
|
+
sourcePath: string
|
|
123
|
+
content: string
|
|
124
|
+
updatedAt: string
|
|
125
|
+
location?: string
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
export interface DiffHunk {
|
|
129
|
+
oldText: string
|
|
130
|
+
newText: string
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
export interface FileChange {
|
|
134
|
+
path: string
|
|
135
|
+
created: boolean
|
|
136
|
+
additions: number
|
|
137
|
+
deletions: number
|
|
138
|
+
hunks: DiffHunk[]
|
|
139
|
+
}
|
|
140
|
+
|
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* shared/utils/agentActivity.ts
|
|
3
|
+
* Agent activity parsing and transformation logic.
|
|
4
|
+
* Shared by both Desktop and Web ChatPanel — do NOT duplicate this.
|
|
5
|
+
*/
|
|
6
|
+
import type { AgentProgress } from '../types'
|
|
7
|
+
|
|
8
|
+
export interface AgentActivity {
|
|
9
|
+
label: string
|
|
10
|
+
target: string
|
|
11
|
+
kind: 'file' | 'folder' | 'search' | 'terminal' | 'tool'
|
|
12
|
+
state: 'active' | 'done' | 'error'
|
|
13
|
+
action?: string
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface AgentActivityView {
|
|
17
|
+
summary: string
|
|
18
|
+
items: AgentActivity[]
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
function activityDetail(input: Record<string, unknown>, key: string): string {
|
|
22
|
+
const value = input[key]
|
|
23
|
+
return typeof value === 'string' && value.trim() ? value : ''
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export function formatActivityTarget(value: string): string {
|
|
27
|
+
const compact = value.replace(/^\/home\/([^/]+)/, '~')
|
|
28
|
+
return compact || 'workspace'
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function activityKind(action: string, target: string): AgentActivity['kind'] {
|
|
32
|
+
if (['search_code', 'semantic_search', 'knowledge_search', 'web_search', 'memory_recall'].includes(action)) return 'search'
|
|
33
|
+
if (['run_shell', 'verify'].includes(action)) return 'terminal'
|
|
34
|
+
if (['list_files', 'detect_project'].includes(action)) return 'folder'
|
|
35
|
+
if (['read_file', 'symbols', 'read_diagnostics', 'git_diff', 'apply_patch', 'write_file', 'note_write', 'view_image'].includes(action)) return 'file'
|
|
36
|
+
return target.includes('/') && !/\.[^/]+$/.test(target) ? 'folder' : 'tool'
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
export function describeTool(action: string, input: Record<string, unknown>): AgentActivity {
|
|
40
|
+
const path = activityDetail(input, 'path')
|
|
41
|
+
const query = activityDetail(input, 'query')
|
|
42
|
+
const command = activityDetail(input, 'command')
|
|
43
|
+
const name = activityDetail(input, 'name')
|
|
44
|
+
const tool = activityDetail(input, 'tool')
|
|
45
|
+
const rawTarget = path || query || command || name || tool || action.replaceAll('_', ' ')
|
|
46
|
+
|
|
47
|
+
// Append line range for read_file when startLine / endLine are available
|
|
48
|
+
let target = rawTarget
|
|
49
|
+
if (action === 'read_file' && path) {
|
|
50
|
+
const startLine = typeof input.startLine === 'number' ? input.startLine : undefined
|
|
51
|
+
const endLine = typeof input.endLine === 'number' ? input.endLine : undefined
|
|
52
|
+
if (startLine !== undefined && endLine !== undefined) {
|
|
53
|
+
target = `${rawTarget} #L${startLine}-${endLine}`
|
|
54
|
+
} else if (startLine !== undefined) {
|
|
55
|
+
target = `${rawTarget} #L${startLine}`
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const labels: Record<string, string> = {
|
|
60
|
+
apply_patch: 'Applying patch',
|
|
61
|
+
ask_user: 'Asking user',
|
|
62
|
+
create_plan: 'Creating plan',
|
|
63
|
+
detect_project: 'Detecting project',
|
|
64
|
+
git_branch: 'Reading branch',
|
|
65
|
+
git_diff: 'Reading diff',
|
|
66
|
+
git_log: 'Reading log',
|
|
67
|
+
git_status: 'Reading git status',
|
|
68
|
+
knowledge_search: 'Searching knowledge',
|
|
69
|
+
list_files: 'Listing files',
|
|
70
|
+
list_tests: 'Listing tests',
|
|
71
|
+
mcp_tool: 'Calling MCP tool',
|
|
72
|
+
memory_recall: 'Recalling memory',
|
|
73
|
+
note_write: 'Writing note',
|
|
74
|
+
read_diagnostics: 'Reading diagnostics',
|
|
75
|
+
read_file: 'Reading file',
|
|
76
|
+
request_user_approval: 'Requesting approval',
|
|
77
|
+
run_plugin: 'Running plugin',
|
|
78
|
+
run_shell: 'Running command',
|
|
79
|
+
search_code: 'Searching code',
|
|
80
|
+
semantic_index: 'Indexing code',
|
|
81
|
+
semantic_search: 'Searching code',
|
|
82
|
+
symbols: 'Inspecting symbols',
|
|
83
|
+
update_plan: 'Updating plan',
|
|
84
|
+
verify: 'Verifying',
|
|
85
|
+
view_image: 'Viewing image',
|
|
86
|
+
web_search: 'Searching web',
|
|
87
|
+
write_file: 'Writing file',
|
|
88
|
+
}
|
|
89
|
+
return {
|
|
90
|
+
label: labels[action] ?? 'Using tool',
|
|
91
|
+
target: formatActivityTarget(target),
|
|
92
|
+
kind: activityKind(action, target),
|
|
93
|
+
state: 'active',
|
|
94
|
+
action,
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
export function activitySummary(items: AgentActivity[]): string {
|
|
99
|
+
const files = new Set<string>()
|
|
100
|
+
const folders = new Set<string>()
|
|
101
|
+
for (const item of items) {
|
|
102
|
+
if (item.kind === 'file') files.add(item.target)
|
|
103
|
+
if (item.kind === 'folder') folders.add(item.target)
|
|
104
|
+
}
|
|
105
|
+
const parts = [
|
|
106
|
+
files.size ? `${files.size} ${files.size === 1 ? 'file' : 'files'}` : '',
|
|
107
|
+
folders.size ? `${folders.size} ${folders.size === 1 ? 'folder' : 'folders'}` : '',
|
|
108
|
+
].filter(Boolean)
|
|
109
|
+
return parts.length ? `Exploring ${parts.join(', ')}` : 'Working through task'
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export function activitiesFrom(progress: AgentProgress[]): AgentActivityView {
|
|
113
|
+
const activities: AgentActivity[] = []
|
|
114
|
+
for (const event of progress) {
|
|
115
|
+
if (event.type === 'ToolStart') {
|
|
116
|
+
activities.push(describeTool(event.data.action, event.data.input))
|
|
117
|
+
} else if (event.type === 'ToolEnd') {
|
|
118
|
+
for (let index = activities.length - 1; index >= 0; index -= 1) {
|
|
119
|
+
if (activities[index].state !== 'active') continue
|
|
120
|
+
activities[index].state = event.data.result.startsWith('Error:') ? 'error' : 'done'
|
|
121
|
+
if (activities[index].state === 'error') {
|
|
122
|
+
activities[index].label = 'Failed'
|
|
123
|
+
}
|
|
124
|
+
if (activities[index].action === 'ask_user' && event.data.result.startsWith('User answered:')) {
|
|
125
|
+
const answer = event.data.result.replace('User answered:', '').trim()
|
|
126
|
+
activities[index].target = `(Answered: "${answer}") ${activities[index].target}`
|
|
127
|
+
}
|
|
128
|
+
break
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
const items = activities.slice(-12)
|
|
133
|
+
return { summary: activitySummary(activities), items }
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export interface WebSearchSource {
|
|
137
|
+
title: string
|
|
138
|
+
url: string
|
|
139
|
+
snippet: string
|
|
140
|
+
domain: string
|
|
141
|
+
faviconUrl: string
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* Scans AgentProgress events and extracts web search sources from ToolEnd results.
|
|
146
|
+
* Parses the formatted text produced by orchestration.rs for `web_search` actions.
|
|
147
|
+
*/
|
|
148
|
+
export function parseWebSearchSources(progress: AgentProgress[]): WebSearchSource[] {
|
|
149
|
+
const sources: WebSearchSource[] = []
|
|
150
|
+
for (const event of progress) {
|
|
151
|
+
if (event.type !== 'ToolEnd') continue
|
|
152
|
+
if (event.data.action !== 'web_search') continue
|
|
153
|
+
|
|
154
|
+
const result = event.data.result
|
|
155
|
+
if (!result || result.startsWith('Web search error:') || result === 'No web search results found.') continue
|
|
156
|
+
|
|
157
|
+
// Each result block looks like:
|
|
158
|
+
// 1. Title text
|
|
159
|
+
// URL: https://example.com
|
|
160
|
+
// Snippet text
|
|
161
|
+
const blocks = result.split(/\n\n+/)
|
|
162
|
+
for (const block of blocks) {
|
|
163
|
+
const lines = block.split('\n').map((l: string) => l.trim()).filter(Boolean)
|
|
164
|
+
const titleLine = lines.find((l: string) => /^\d+\.\s/.test(l))
|
|
165
|
+
const urlLine = lines.find((l: string) => l.startsWith('URL:'))
|
|
166
|
+
if (!titleLine || !urlLine) continue
|
|
167
|
+
|
|
168
|
+
const title = titleLine.replace(/^\d+\.\s/, '').trim()
|
|
169
|
+
const url = urlLine.replace(/^URL:\s*/, '').trim()
|
|
170
|
+
const snippet = lines
|
|
171
|
+
.filter((l: string) => l !== titleLine && l !== urlLine)
|
|
172
|
+
.join(' ')
|
|
173
|
+
.trim()
|
|
174
|
+
|
|
175
|
+
try {
|
|
176
|
+
const { hostname } = new URL(url)
|
|
177
|
+
const domain = hostname.replace(/^www\./, '')
|
|
178
|
+
const faviconUrl = `https://www.google.com/s2/favicons?sz=32&domain=${hostname}`
|
|
179
|
+
sources.push({ title, url, snippet, domain, faviconUrl })
|
|
180
|
+
} catch {
|
|
181
|
+
// skip malformed URLs
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
return sources
|
|
186
|
+
}
|
|
187
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './approval.tsx'
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* shared/utils/approval.tsx
|
|
3
|
+
* Approval detail parsing and diff rendering helpers.
|
|
4
|
+
* Shared by both Desktop and Web ChatPanel — do NOT duplicate this.
|
|
5
|
+
*/
|
|
6
|
+
import React, { type ReactNode } from 'react'
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
export interface ApprovalDetails {
|
|
10
|
+
title: string
|
|
11
|
+
body: string
|
|
12
|
+
reason?: string
|
|
13
|
+
isDangerous: boolean
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
17
|
+
export function renderApprovalDetails(approval: any): ApprovalDetails {
|
|
18
|
+
if (!approval) return { title: 'Action Pending Approval', body: 'No action details available.', isDangerous: false }
|
|
19
|
+
if (approval.WriteFile) return { title: 'Write File', body: `Path: ${approval.WriteFile.path}`, reason: approval.WriteFile.diff ? `Diff:\n${approval.WriteFile.diff}` : 'Writing new file content.', isDangerous: false }
|
|
20
|
+
if (approval.ApplyPatch) return { title: 'Apply Patch', body: `Path: ${approval.ApplyPatch.path}`, reason: approval.ApplyPatch.diff ? `Diff:\n${approval.ApplyPatch.diff}` : 'Applying code patch.', isDangerous: false }
|
|
21
|
+
if (approval.RunShell) return { title: 'Run Shell Command', body: approval.RunShell.command, reason: 'Executing shell commands can modify your system.', isDangerous: true }
|
|
22
|
+
if (approval.NoteWrite) return { title: 'Write Note', body: `Path: ${approval.NoteWrite.path}`, reason: 'Creating or updating workspace notes.', isDangerous: false }
|
|
23
|
+
if (approval.RunPlugin) return { title: `Run Plugin: ${approval.RunPlugin.name}`, body: approval.RunPlugin.instruction, reason: 'Executing a native plugin action.', isDangerous: false }
|
|
24
|
+
if (approval.McpTool) {
|
|
25
|
+
const { server, tool, arguments: args } = approval.McpTool
|
|
26
|
+
return { title: `Run MCP Tool: ${server}/${tool}`, body: typeof args === 'string' ? args : JSON.stringify(args, null, 2), reason: 'Running external MCP tool.', isDangerous: false }
|
|
27
|
+
}
|
|
28
|
+
if (approval.UserApproval) return { title: approval.UserApproval.title, body: approval.UserApproval.prompt, reason: 'The agent requested explicit approval.', isDangerous: false }
|
|
29
|
+
if (approval.AskUser) return { title: 'Question From Agent', body: approval.AskUser.question, reason: 'Type your answer below and submit to respond to the agent.', isDangerous: false }
|
|
30
|
+
return { title: 'Unknown Action', body: JSON.stringify(approval, null, 2), reason: 'Requires approval to proceed.', isDangerous: false }
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function renderDiff(diffText: string): ReactNode {
|
|
34
|
+
if (!diffText) return null
|
|
35
|
+
const lines = diffText.split('\n')
|
|
36
|
+
return (
|
|
37
|
+
<div style={{
|
|
38
|
+
background: '#0b0f19',
|
|
39
|
+
borderRadius: '6px',
|
|
40
|
+
padding: '8px',
|
|
41
|
+
border: '1px solid rgba(255, 255, 255, 0.08)',
|
|
42
|
+
overflowX: 'auto',
|
|
43
|
+
maxHeight: '400px',
|
|
44
|
+
fontFamily: 'monospace',
|
|
45
|
+
fontSize: '0.74rem',
|
|
46
|
+
lineHeight: '1.4',
|
|
47
|
+
}}>
|
|
48
|
+
{lines.map((line, idx) => {
|
|
49
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
50
|
+
let style: any = { whiteSpace: 'pre-wrap', padding: '2px 6px' }
|
|
51
|
+
if (line.startsWith('@@') || line.startsWith('---') || line.startsWith('+++')) {
|
|
52
|
+
style = { ...style, color: '#64748b', fontWeight: 'bold' }
|
|
53
|
+
} else if (line.startsWith('+')) {
|
|
54
|
+
style = { ...style, background: 'rgba(16, 185, 129, 0.12)', borderLeft: '3px solid #10b981', color: '#a7f3d0' }
|
|
55
|
+
} else if (line.startsWith('-')) {
|
|
56
|
+
style = { ...style, background: 'rgba(239, 68, 68, 0.12)', borderLeft: '3px solid #ef4444', color: '#fca5a5' }
|
|
57
|
+
} else {
|
|
58
|
+
style = { ...style, color: '#e2e8f0' }
|
|
59
|
+
}
|
|
60
|
+
return <div key={idx} style={style}>{line}</div>
|
|
61
|
+
})}
|
|
62
|
+
</div>
|
|
63
|
+
)
|
|
64
|
+
}
|