@pheem49/mint 1.8.1 → 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.
Files changed (184) hide show
  1. package/Cargo.lock +3 -3
  2. package/Cargo.toml +1 -1
  3. package/README.md +0 -17
  4. package/Release_Note.md +92 -58
  5. package/bin/mint +0 -0
  6. package/crates/mint-cli/src/agent.rs +100 -6
  7. package/crates/mint-cli/src/main.rs +166 -52
  8. package/crates/mint-core/src/agent_loop.rs +1 -0
  9. package/crates/mint-core/src/api_server.rs +55 -2
  10. package/crates/mint-core/src/browser.rs +55 -0
  11. package/crates/mint-core/src/channels.rs +1 -0
  12. package/crates/mint-core/src/chat.rs +3 -0
  13. package/crates/mint-core/src/config.rs +61 -0
  14. package/crates/mint-core/src/lib.rs +14 -0
  15. package/crates/mint-core/src/orchestration.rs +180 -11
  16. package/crates/mint-core/src/tts.rs +3 -1
  17. package/flatpak/com.pheem49.mint.desktop +9 -0
  18. package/flatpak/com.pheem49.mint.metainfo.xml +35 -0
  19. package/flatpak/com.pheem49.mint.yaml +52 -0
  20. package/package.json +1 -1
  21. package/shared-modules/.github/workflows/ci.yml +120 -0
  22. package/shared-modules/.github/workflows/update.yaml +28 -0
  23. package/shared-modules/CODEOWNERS +23 -0
  24. package/shared-modules/README.md +79 -0
  25. package/shared-modules/SDL/README.md +3 -0
  26. package/shared-modules/SDL/SDL-1.2.15.json +51 -0
  27. package/shared-modules/SDL/SDL_Pango-0.1.2-API-adds.patch +118 -0
  28. package/shared-modules/SDL/SDL_image-1.2.12.json +25 -0
  29. package/shared-modules/SDL/SDL_mixer-1.2.12.json +27 -0
  30. package/shared-modules/SDL/SDL_net-1.2.8.json +19 -0
  31. package/shared-modules/SDL/SDL_pango-0.1.2.json +26 -0
  32. package/shared-modules/SDL/SDL_ttf-2.0.11.json +28 -0
  33. package/shared-modules/SDL/libsdl-1.2.15-strict-prototypes.patch +316 -0
  34. package/shared-modules/SDL/sdl-check-for-SDL_VIDEO_X11_BACKINGSTORE.patch +24 -0
  35. package/shared-modules/SDL/sdl-libx11-build.patch +59 -0
  36. package/shared-modules/SDL/sdl-sysloadso-buffer-length.patch +27 -0
  37. package/shared-modules/SDL/sdl12-compat.json +22 -0
  38. package/shared-modules/SDL2/SDL2_image.json +27 -0
  39. package/shared-modules/SDL2/SDL2_ttf.json +29 -0
  40. package/shared-modules/cld2/CMakeLists.txt +157 -0
  41. package/shared-modules/cld2/cld2.json +24 -0
  42. package/shared-modules/clutter/clutter.json +72 -0
  43. package/shared-modules/dbus-glib/dbus-glib.json +29 -0
  44. package/shared-modules/glew/glew.json +36 -0
  45. package/shared-modules/glu/glu-9.json +12 -0
  46. package/shared-modules/gtk2/arc-gtk2-theme-Replace-Inkscape-dependency-with-rsvg-convert.patch +180 -0
  47. package/shared-modules/gtk2/gtk2-common-themes.json +360 -0
  48. package/shared-modules/gtk2/gtk2-fix-crash-in-show-uri.patch +30 -0
  49. package/shared-modules/gtk2/gtk2-gcc14.patch +281 -0
  50. package/shared-modules/gtk2/gtk2-gcc15.patch +250 -0
  51. package/shared-modules/gtk2/gtk2-murrine-c99.patch +34 -0
  52. package/shared-modules/gtk2/gtk2-use-adwaita-theme.patch +80 -0
  53. package/shared-modules/gtk2/gtk2.json +120 -0
  54. package/shared-modules/gtk2/ibus-gcc15.patch +13 -0
  55. package/shared-modules/gtk2/murrine-engine-fix-crash.patch +12 -0
  56. package/shared-modules/gtk2/readme.md +10 -0
  57. package/shared-modules/intltool/intltool-0.51.json +20 -0
  58. package/shared-modules/intltool/intltool-perl5.26-regex-fixes.patch +59 -0
  59. package/shared-modules/lame/0001-Build-xmm_quantize_sub.c-with-msse.patch +34 -0
  60. package/shared-modules/lame/0002-Include-GTK-1-autoconf-directives-in-build-system.patch +222 -0
  61. package/shared-modules/lame/0003-Patch-out-remaining-ansi2knr.patch +70 -0
  62. package/shared-modules/lame/0004-initscr-is-not-used-anywhere-in-lame-sourcetree.patch +41 -0
  63. package/shared-modules/lame/0005-Update-autoconf.patch +81 -0
  64. package/shared-modules/lame/lame-3.99.5.json +32 -0
  65. package/shared-modules/libappindicator/libappindicator-gtk2-12.10.json +140 -0
  66. package/shared-modules/libappindicator/libappindicator-gtk3-12.10.json +140 -0
  67. package/shared-modules/libappindicator/libappindicator-gtk3-introspection-12.10.json +140 -0
  68. package/shared-modules/libappindicator/patches/0001-libappindicator-ftbfs.patch +20 -0
  69. package/shared-modules/libappindicator/patches/0002-libappindicator-no-python.patch +888 -0
  70. package/shared-modules/libappindicator/patches/0003-libappindicator-fix-crash-from-incorrect-signal-emission.patch +74 -0
  71. package/shared-modules/libappindicator/patches/0004-libappindicator-fix-crash-iterating-icon-themes.patch +11 -0
  72. package/shared-modules/libayatana-appindicator/0001-Fix-HAVE_VALGRIND-AM_CONDITIONAL.patch +51 -0
  73. package/shared-modules/libayatana-appindicator/0001-Make-introspection-configurable.patch +44 -0
  74. package/shared-modules/libayatana-appindicator/libayatana-appindicator-gtk3.json +121 -0
  75. package/shared-modules/libcanberra/0001-gtk-Don-t-assume-all-GdkDisplays-are-GdkX11Displays-.patch +123 -0
  76. package/shared-modules/libcanberra/libcanberra.json +44 -0
  77. package/shared-modules/libmad/libmad.json +19 -0
  78. package/shared-modules/libsecret/libsecret.json +23 -0
  79. package/shared-modules/libsoup/libsoup-2.4.json +14 -0
  80. package/shared-modules/libusb/libusb.json +27 -0
  81. package/shared-modules/linux-audio/dssi.json +31 -0
  82. package/shared-modules/linux-audio/fftw3f-static.json +44 -0
  83. package/shared-modules/linux-audio/fftw3f.json +42 -0
  84. package/shared-modules/linux-audio/fluidsynth2-static.json +30 -0
  85. package/shared-modules/linux-audio/fluidsynth2.json +19 -0
  86. package/shared-modules/linux-audio/ladspa.json +26 -0
  87. package/shared-modules/linux-audio/lash.json +35 -0
  88. package/shared-modules/linux-audio/libinstpatch.json +16 -0
  89. package/shared-modules/linux-audio/liblo-static.json +22 -0
  90. package/shared-modules/linux-audio/liblo.json +17 -0
  91. package/shared-modules/linux-audio/lilv-static.json +94 -0
  92. package/shared-modules/linux-audio/lilv.json +82 -0
  93. package/shared-modules/linux-audio/lrdf.json +57 -0
  94. package/shared-modules/linux-audio/lv2.json +21 -0
  95. package/shared-modules/linux-audio/patches/lash/0001-configure-remove-aclocal-m4.patch +29 -0
  96. package/shared-modules/linux-audio/patches/lash/0002-Fix-build-with-recent-gcc.patch +73 -0
  97. package/shared-modules/linux-audio/patches/lash/0003-Fix-linking.patch +136 -0
  98. package/shared-modules/linux-audio/patches/raptor-libxml.patch +31 -0
  99. package/shared-modules/linux-audio/patches/stk-install.sh +54 -0
  100. package/shared-modules/linux-audio/stk.json +28 -0
  101. package/shared-modules/lua5.1/0004-Fix-stack-overflow-in-vararg-functions.patch +21 -0
  102. package/shared-modules/lua5.1/lua-5.1.5-so.patch +43 -0
  103. package/shared-modules/lua5.1/lua-5.1.5.json +42 -0
  104. package/shared-modules/lua5.3/lua-5.3.5.json +41 -0
  105. package/shared-modules/lua5.3/lua.pc.in +8 -0
  106. package/shared-modules/lua5.3/luaconf.patch +47 -0
  107. package/shared-modules/lua5.3/makefile-add-liblua.so-rule.patch +47 -0
  108. package/shared-modules/lua5.4/lua-5.4.json +41 -0
  109. package/shared-modules/lua5.4/lua.pc.in +8 -0
  110. package/shared-modules/lua5.4/luaconf.patch +34 -0
  111. package/shared-modules/lua5.4/makefile-add-liblua.so-rule.patch +42 -0
  112. package/shared-modules/luajit/luajit.json +30 -0
  113. package/shared-modules/lzo/lzo.json +27 -0
  114. package/shared-modules/mac/mac.json +18 -0
  115. package/shared-modules/physfs/physfs.json +26 -0
  116. package/shared-modules/pygame/audiofile-gcc6.patch +21 -0
  117. package/shared-modules/pygame/cython-fixes.patch +34 -0
  118. package/shared-modules/pygame/fluidsynth-no-rawmidi.patch +69 -0
  119. package/shared-modules/pygame/glib.patch +25 -0
  120. package/shared-modules/pygame/pygame1-python2.json +157 -0
  121. package/shared-modules/pygtk/pygtk-c99.patch +15 -0
  122. package/shared-modules/pygtk/pygtk.json +117 -0
  123. package/shared-modules/pygtk/pygtk.patch +39 -0
  124. package/shared-modules/python2.7/python-2.7.json +48 -0
  125. package/shared-modules/smpeg/smpeg-0.4.5.json +50 -0
  126. package/shared-modules/smpeg/smpeg-am-prog-as.patch +12 -0
  127. package/shared-modules/smpeg/smpeg-export-mpegaudio-class.patch +17 -0
  128. package/shared-modules/smpeg/smpeg-gcc6.patch +40 -0
  129. package/shared-modules/smpeg/smpeg-no-gtk.patch +37 -0
  130. package/shared-modules/squashfs-tools/squashfs-tools.json +25 -0
  131. package/shared-modules/update-checker.sh +50 -0
  132. package/shared-modules/vorbisgain/0001-temp_files.patch +75 -0
  133. package/shared-modules/vorbisgain/0002-errno.patch +33 -0
  134. package/shared-modules/vorbisgain/0003-manpage.patch +248 -0
  135. package/shared-modules/vorbisgain/0004-vorbisgain_mtime.patch +183 -0
  136. package/shared-modules/vorbisgain/0005-double_fclose.patch +21 -0
  137. package/shared-modules/vorbisgain/0006-manpage_hyphens.patch +56 -0
  138. package/shared-modules/vorbisgain/0007-recursively_spelling.patch +14 -0
  139. package/shared-modules/vorbisgain/0008-manpage_recursion_mistake.patch +23 -0
  140. package/shared-modules/vorbisgain/0009-hardening.patch +13 -0
  141. package/shared-modules/vorbisgain/0010-fclose.patch +13 -0
  142. package/shared-modules/vorbisgain/README.md +23 -0
  143. package/shared-modules/vorbisgain/vorbisgain_0.37-2.json +29 -0
  144. package/src/renderer/shared/agentProgress.ts +145 -0
  145. package/src/renderer/shared/calculator.ts +52 -0
  146. package/src/renderer/shared/components/AgentActivityDrawer.tsx +72 -0
  147. package/src/renderer/shared/components/AgentActivityTable.tsx +66 -0
  148. package/src/renderer/shared/components/ApprovalCard.tsx +93 -0
  149. package/src/renderer/shared/components/ChatCodeBlock.tsx +75 -0
  150. package/src/renderer/shared/components/ThinkingBlock.tsx +101 -0
  151. package/src/renderer/shared/constants/models.ts +91 -0
  152. package/src/renderer/shared/platform.ts +82 -0
  153. package/src/renderer/shared/types.ts +140 -0
  154. package/src/renderer/shared/utils/agentActivity.ts +187 -0
  155. package/src/renderer/shared/utils/approval.ts +1 -0
  156. package/src/renderer/shared/utils/approval.tsx +64 -0
  157. package/src/renderer/shared/utils/markdown.tsx +347 -0
  158. package/src/renderer/shared/utils/providers.ts +36 -0
  159. package/src/renderer/shared/utils/speech.ts +216 -0
  160. package/src/renderer/shared/utils/ui.ts +147 -0
  161. package/src/renderer/src/agentProgress.ts +11 -39
  162. package/src/renderer/src/calculator.ts +3 -46
  163. package/src/renderer/src/components/ChatPanel.tsx +194 -1303
  164. package/src/renderer/src/components/MintDashboard.tsx +23 -169
  165. package/src/renderer/src/components/PicturesLibrary.tsx +32 -8
  166. package/src/renderer/src/components/Settings/AgentsTab.tsx +362 -0
  167. package/src/renderer/src/components/Settings/AutomationTab.tsx +0 -22
  168. package/src/renderer/src/components/SettingsWindow.tsx +53 -3
  169. package/src/renderer/src/css/styles.css +84 -0
  170. package/src/renderer/src/tauri.ts +128 -143
  171. package/src/renderer/src-web/agentProgress.ts +11 -39
  172. package/src/renderer/src-web/calculator.ts +3 -46
  173. package/src/renderer/src-web/components/ChatPanel.tsx +194 -1301
  174. package/src/renderer/src-web/components/MintDashboard.tsx +23 -169
  175. package/src/renderer/src-web/components/PicturesLibrary.tsx +23 -29
  176. package/src/renderer/src-web/components/Settings/AgentsTab.tsx +362 -0
  177. package/src/renderer/src-web/components/Settings/AutomationTab.tsx +0 -22
  178. package/src/renderer/src-web/components/SettingsWindow.tsx +53 -3
  179. package/src/renderer/src-web/css/styles.css +84 -0
  180. package/src/renderer/src-web/tauri.ts +180 -102
  181. package/src-tauri/src/lib.rs +137 -33
  182. package/src-tauri/src/webhooks.rs +1 -0
  183. package/src-tauri/src/workflows.rs +1 -0
  184. 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
+ }