@pheem49/mint 1.8.1 → 1.9.0

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 (200) 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 +137 -58
  5. package/bin/mint +0 -0
  6. package/crates/mint-cli/src/agent.rs +150 -45
  7. package/crates/mint-cli/src/image.rs +8 -8
  8. package/crates/mint-cli/src/main.rs +929 -412
  9. package/crates/mint-cli/src/onboard.rs +108 -108
  10. package/crates/mint-cli/src/setup.rs +143 -143
  11. package/crates/mint-core/src/agent_loop.rs +1 -0
  12. package/crates/mint-core/src/api_server.rs +108 -52
  13. package/crates/mint-core/src/browser.rs +56 -1
  14. package/crates/mint-core/src/channels.rs +1 -0
  15. package/crates/mint-core/src/chat.rs +126 -8
  16. package/crates/mint-core/src/code_tools.rs +18 -20
  17. package/crates/mint-core/src/config.rs +245 -17
  18. package/crates/mint-core/src/image_gen.rs +5 -5
  19. package/crates/mint-core/src/lib.rs +16 -2
  20. package/crates/mint-core/src/memory.rs +6 -1
  21. package/crates/mint-core/src/orchestration.rs +260 -72
  22. package/crates/mint-core/src/pictures.rs +3 -4
  23. package/crates/mint-core/src/semantic.rs +1 -1
  24. package/crates/mint-core/src/shell.rs +1 -1
  25. package/crates/mint-core/src/skills.rs +46 -19
  26. package/crates/mint-core/src/tts.rs +3 -1
  27. package/flatpak/com.pheem49.mint.desktop +9 -0
  28. package/flatpak/com.pheem49.mint.metainfo.xml +35 -0
  29. package/flatpak/com.pheem49.mint.yaml +52 -0
  30. package/package.json +1 -1
  31. package/shared-modules/.github/workflows/ci.yml +120 -0
  32. package/shared-modules/.github/workflows/update.yaml +28 -0
  33. package/shared-modules/CODEOWNERS +23 -0
  34. package/shared-modules/README.md +79 -0
  35. package/shared-modules/SDL/README.md +3 -0
  36. package/shared-modules/SDL/SDL-1.2.15.json +51 -0
  37. package/shared-modules/SDL/SDL_Pango-0.1.2-API-adds.patch +118 -0
  38. package/shared-modules/SDL/SDL_image-1.2.12.json +25 -0
  39. package/shared-modules/SDL/SDL_mixer-1.2.12.json +27 -0
  40. package/shared-modules/SDL/SDL_net-1.2.8.json +19 -0
  41. package/shared-modules/SDL/SDL_pango-0.1.2.json +26 -0
  42. package/shared-modules/SDL/SDL_ttf-2.0.11.json +28 -0
  43. package/shared-modules/SDL/libsdl-1.2.15-strict-prototypes.patch +316 -0
  44. package/shared-modules/SDL/sdl-check-for-SDL_VIDEO_X11_BACKINGSTORE.patch +24 -0
  45. package/shared-modules/SDL/sdl-libx11-build.patch +59 -0
  46. package/shared-modules/SDL/sdl-sysloadso-buffer-length.patch +27 -0
  47. package/shared-modules/SDL/sdl12-compat.json +22 -0
  48. package/shared-modules/SDL2/SDL2_image.json +27 -0
  49. package/shared-modules/SDL2/SDL2_ttf.json +29 -0
  50. package/shared-modules/cld2/CMakeLists.txt +157 -0
  51. package/shared-modules/cld2/cld2.json +24 -0
  52. package/shared-modules/clutter/clutter.json +72 -0
  53. package/shared-modules/dbus-glib/dbus-glib.json +29 -0
  54. package/shared-modules/glew/glew.json +36 -0
  55. package/shared-modules/glu/glu-9.json +12 -0
  56. package/shared-modules/gtk2/arc-gtk2-theme-Replace-Inkscape-dependency-with-rsvg-convert.patch +180 -0
  57. package/shared-modules/gtk2/gtk2-common-themes.json +360 -0
  58. package/shared-modules/gtk2/gtk2-fix-crash-in-show-uri.patch +30 -0
  59. package/shared-modules/gtk2/gtk2-gcc14.patch +281 -0
  60. package/shared-modules/gtk2/gtk2-gcc15.patch +250 -0
  61. package/shared-modules/gtk2/gtk2-murrine-c99.patch +34 -0
  62. package/shared-modules/gtk2/gtk2-use-adwaita-theme.patch +80 -0
  63. package/shared-modules/gtk2/gtk2.json +120 -0
  64. package/shared-modules/gtk2/ibus-gcc15.patch +13 -0
  65. package/shared-modules/gtk2/murrine-engine-fix-crash.patch +12 -0
  66. package/shared-modules/gtk2/readme.md +10 -0
  67. package/shared-modules/intltool/intltool-0.51.json +20 -0
  68. package/shared-modules/intltool/intltool-perl5.26-regex-fixes.patch +59 -0
  69. package/shared-modules/lame/0001-Build-xmm_quantize_sub.c-with-msse.patch +34 -0
  70. package/shared-modules/lame/0002-Include-GTK-1-autoconf-directives-in-build-system.patch +222 -0
  71. package/shared-modules/lame/0003-Patch-out-remaining-ansi2knr.patch +70 -0
  72. package/shared-modules/lame/0004-initscr-is-not-used-anywhere-in-lame-sourcetree.patch +41 -0
  73. package/shared-modules/lame/0005-Update-autoconf.patch +81 -0
  74. package/shared-modules/lame/lame-3.99.5.json +32 -0
  75. package/shared-modules/libappindicator/libappindicator-gtk2-12.10.json +140 -0
  76. package/shared-modules/libappindicator/libappindicator-gtk3-12.10.json +140 -0
  77. package/shared-modules/libappindicator/libappindicator-gtk3-introspection-12.10.json +140 -0
  78. package/shared-modules/libappindicator/patches/0001-libappindicator-ftbfs.patch +20 -0
  79. package/shared-modules/libappindicator/patches/0002-libappindicator-no-python.patch +888 -0
  80. package/shared-modules/libappindicator/patches/0003-libappindicator-fix-crash-from-incorrect-signal-emission.patch +74 -0
  81. package/shared-modules/libappindicator/patches/0004-libappindicator-fix-crash-iterating-icon-themes.patch +11 -0
  82. package/shared-modules/libayatana-appindicator/0001-Fix-HAVE_VALGRIND-AM_CONDITIONAL.patch +51 -0
  83. package/shared-modules/libayatana-appindicator/0001-Make-introspection-configurable.patch +44 -0
  84. package/shared-modules/libayatana-appindicator/libayatana-appindicator-gtk3.json +121 -0
  85. package/shared-modules/libcanberra/0001-gtk-Don-t-assume-all-GdkDisplays-are-GdkX11Displays-.patch +123 -0
  86. package/shared-modules/libcanberra/libcanberra.json +44 -0
  87. package/shared-modules/libmad/libmad.json +19 -0
  88. package/shared-modules/libsecret/libsecret.json +23 -0
  89. package/shared-modules/libsoup/libsoup-2.4.json +14 -0
  90. package/shared-modules/libusb/libusb.json +27 -0
  91. package/shared-modules/linux-audio/dssi.json +31 -0
  92. package/shared-modules/linux-audio/fftw3f-static.json +44 -0
  93. package/shared-modules/linux-audio/fftw3f.json +42 -0
  94. package/shared-modules/linux-audio/fluidsynth2-static.json +36 -0
  95. package/shared-modules/linux-audio/fluidsynth2.json +25 -0
  96. package/shared-modules/linux-audio/ladspa.json +26 -0
  97. package/shared-modules/linux-audio/lash.json +35 -0
  98. package/shared-modules/linux-audio/libinstpatch.json +16 -0
  99. package/shared-modules/linux-audio/liblo-static.json +22 -0
  100. package/shared-modules/linux-audio/liblo.json +17 -0
  101. package/shared-modules/linux-audio/lilv-static.json +94 -0
  102. package/shared-modules/linux-audio/lilv.json +89 -0
  103. package/shared-modules/linux-audio/lrdf.json +57 -0
  104. package/shared-modules/linux-audio/lv2.json +20 -0
  105. package/shared-modules/linux-audio/patches/lash/0001-configure-remove-aclocal-m4.patch +29 -0
  106. package/shared-modules/linux-audio/patches/lash/0002-Fix-build-with-recent-gcc.patch +73 -0
  107. package/shared-modules/linux-audio/patches/lash/0003-Fix-linking.patch +136 -0
  108. package/shared-modules/linux-audio/patches/raptor-libxml.patch +31 -0
  109. package/shared-modules/linux-audio/patches/stk-install.sh +54 -0
  110. package/shared-modules/linux-audio/stk.json +28 -0
  111. package/shared-modules/lua5.1/0004-Fix-stack-overflow-in-vararg-functions.patch +21 -0
  112. package/shared-modules/lua5.1/lua-5.1.5-so.patch +43 -0
  113. package/shared-modules/lua5.1/lua-5.1.5.json +42 -0
  114. package/shared-modules/lua5.3/lua-5.3.5.json +41 -0
  115. package/shared-modules/lua5.3/lua.pc.in +8 -0
  116. package/shared-modules/lua5.3/luaconf.patch +47 -0
  117. package/shared-modules/lua5.3/makefile-add-liblua.so-rule.patch +47 -0
  118. package/shared-modules/lua5.4/lua-5.4.json +41 -0
  119. package/shared-modules/lua5.4/lua.pc.in +8 -0
  120. package/shared-modules/lua5.4/luaconf.patch +34 -0
  121. package/shared-modules/lua5.4/makefile-add-liblua.so-rule.patch +42 -0
  122. package/shared-modules/luajit/luajit.json +30 -0
  123. package/shared-modules/lzo/lzo.json +27 -0
  124. package/shared-modules/mac/mac.json +18 -0
  125. package/shared-modules/physfs/physfs.json +26 -0
  126. package/shared-modules/pygame/audiofile-gcc6.patch +21 -0
  127. package/shared-modules/pygame/cython-fixes.patch +34 -0
  128. package/shared-modules/pygame/fluidsynth-no-rawmidi.patch +69 -0
  129. package/shared-modules/pygame/glib.patch +25 -0
  130. package/shared-modules/pygame/pygame1-python2.json +157 -0
  131. package/shared-modules/pygtk/pygtk-c99.patch +15 -0
  132. package/shared-modules/pygtk/pygtk.json +117 -0
  133. package/shared-modules/pygtk/pygtk.patch +39 -0
  134. package/shared-modules/python2.7/python-2.7.json +48 -0
  135. package/shared-modules/smpeg/smpeg-0.4.5.json +50 -0
  136. package/shared-modules/smpeg/smpeg-am-prog-as.patch +12 -0
  137. package/shared-modules/smpeg/smpeg-export-mpegaudio-class.patch +17 -0
  138. package/shared-modules/smpeg/smpeg-gcc6.patch +40 -0
  139. package/shared-modules/smpeg/smpeg-no-gtk.patch +37 -0
  140. package/shared-modules/squashfs-tools/squashfs-tools.json +25 -0
  141. package/shared-modules/update-checker.sh +50 -0
  142. package/shared-modules/vorbisgain/0001-temp_files.patch +75 -0
  143. package/shared-modules/vorbisgain/0002-errno.patch +33 -0
  144. package/shared-modules/vorbisgain/0003-manpage.patch +248 -0
  145. package/shared-modules/vorbisgain/0004-vorbisgain_mtime.patch +183 -0
  146. package/shared-modules/vorbisgain/0005-double_fclose.patch +21 -0
  147. package/shared-modules/vorbisgain/0006-manpage_hyphens.patch +56 -0
  148. package/shared-modules/vorbisgain/0007-recursively_spelling.patch +14 -0
  149. package/shared-modules/vorbisgain/0008-manpage_recursion_mistake.patch +23 -0
  150. package/shared-modules/vorbisgain/0009-hardening.patch +13 -0
  151. package/shared-modules/vorbisgain/0010-fclose.patch +13 -0
  152. package/shared-modules/vorbisgain/README.md +23 -0
  153. package/shared-modules/vorbisgain/vorbisgain_0.37-2.json +29 -0
  154. package/src/renderer/shared/agentProgress.ts +145 -0
  155. package/src/renderer/shared/calculator.ts +52 -0
  156. package/src/renderer/shared/components/AgentActivityDrawer.tsx +72 -0
  157. package/src/renderer/shared/components/AgentActivityTable.tsx +85 -0
  158. package/src/renderer/shared/components/ApprovalCard.tsx +93 -0
  159. package/src/renderer/shared/components/ChatCodeBlock.tsx +75 -0
  160. package/src/renderer/shared/components/ThinkingBlock.tsx +101 -0
  161. package/src/renderer/shared/constants/models.ts +91 -0
  162. package/src/renderer/shared/platform.ts +82 -0
  163. package/src/renderer/shared/types.ts +140 -0
  164. package/src/renderer/shared/utils/agentActivity.ts +187 -0
  165. package/src/renderer/shared/utils/approval.ts +1 -0
  166. package/src/renderer/shared/utils/approval.tsx +64 -0
  167. package/src/renderer/shared/utils/fileIcons.ts +135 -0
  168. package/src/renderer/shared/utils/markdown.tsx +347 -0
  169. package/src/renderer/shared/utils/providers.ts +36 -0
  170. package/src/renderer/shared/utils/speech.ts +216 -0
  171. package/src/renderer/shared/utils/ui.ts +147 -0
  172. package/src/renderer/src/agentProgress.ts +11 -39
  173. package/src/renderer/src/calculator.ts +3 -46
  174. package/src/renderer/src/components/ChatPanel.tsx +219 -1308
  175. package/src/renderer/src/components/MintDashboard.tsx +67 -170
  176. package/src/renderer/src/components/PicturesLibrary.tsx +32 -8
  177. package/src/renderer/src/components/Settings/AgentsTab.tsx +381 -0
  178. package/src/renderer/src/components/Settings/AutomationTab.tsx +0 -22
  179. package/src/renderer/src/components/Settings/GeneralTab.tsx +256 -2
  180. package/src/renderer/src/components/SettingsWindow.tsx +120 -3
  181. package/src/renderer/src/components/WorkspacePanel.tsx +7 -125
  182. package/src/renderer/src/css/styles.css +100 -0
  183. package/src/renderer/src/tauri.ts +128 -143
  184. package/src/renderer/src-web/agentProgress.ts +11 -39
  185. package/src/renderer/src-web/calculator.ts +3 -46
  186. package/src/renderer/src-web/components/ChatPanel.tsx +219 -1306
  187. package/src/renderer/src-web/components/MintDashboard.tsx +42 -170
  188. package/src/renderer/src-web/components/PicturesLibrary.tsx +23 -29
  189. package/src/renderer/src-web/components/Settings/AgentsTab.tsx +381 -0
  190. package/src/renderer/src-web/components/Settings/AutomationTab.tsx +0 -22
  191. package/src/renderer/src-web/components/Settings/GeneralTab.tsx +254 -3
  192. package/src/renderer/src-web/components/SettingsWindow.tsx +127 -3
  193. package/src/renderer/src-web/css/styles.css +100 -0
  194. package/src/renderer/src-web/tauri.ts +180 -102
  195. package/src-tauri/src/desktop.rs +18 -16
  196. package/src-tauri/src/discord_rpc.rs +1 -1
  197. package/src-tauri/src/lib.rs +168 -40
  198. package/src-tauri/src/webhooks.rs +1 -0
  199. package/src-tauri/src/workflows.rs +1 -0
  200. package/src-tauri/tauri.conf.json +1 -1
@@ -0,0 +1,381 @@
1
+ import React, { useState } from 'react'
2
+ import {
3
+ DEFAULT_CONFIG,
4
+ GEMINI_MODELS,
5
+ OPENAI_MODELS,
6
+ OPENROUTER_MODELS,
7
+ DEEPSEEK_MODELS,
8
+ ANTHROPIC_MODELS,
9
+ HF_MODELS,
10
+ LOCAL_MODELS,
11
+ CustomProviderConfig,
12
+ } from '../SettingsWindow'
13
+
14
+ interface Agent {
15
+ id: string
16
+ name: string
17
+ provider: string
18
+ model: string
19
+ apiKey?: string
20
+ systemInstruction: string
21
+ enabled: boolean
22
+ }
23
+
24
+ interface AgentsTabProps {
25
+ config: typeof DEFAULT_CONFIG & { agents?: Agent[], customProviders?: CustomProviderConfig[] }
26
+ updateField: (field: any, value: any) => void
27
+ dynamicOllamaModels?: string[]
28
+ }
29
+
30
+ export default function AgentsTab({ config, updateField, dynamicOllamaModels = [] }: AgentsTabProps) {
31
+ const agents = config.agents || []
32
+ const [editingAgent, setEditingAgent] = useState<Agent | null>(null)
33
+ const [name, setName] = useState('')
34
+ const [provider, setProvider] = useState('gemini')
35
+ const [model, setModel] = useState('gemini-2.5-flash')
36
+ const [apiKey, setApiKey] = useState('')
37
+ const [systemInstruction, setSystemInstruction] = useState('')
38
+ const [isEditing, setIsEditing] = useState(false)
39
+
40
+ const handleProviderChange = (newProvider: string) => {
41
+ setProvider(newProvider)
42
+ let defaultModel = ''
43
+ if (newProvider === 'gemini') defaultModel = config.geminiModel || GEMINI_MODELS[0]
44
+ else if (newProvider === 'openai') defaultModel = config.openaiModel || OPENAI_MODELS[0]
45
+ else if (newProvider === 'anthropic') defaultModel = config.anthropicModel || ANTHROPIC_MODELS[0]
46
+ else if (newProvider === 'openrouter') defaultModel = config.openrouterModel || OPENROUTER_MODELS[0]
47
+ else if (newProvider === 'deepseek') defaultModel = config.deepseekModel || DEEPSEEK_MODELS[0]
48
+ else if (newProvider === 'huggingface') defaultModel = config.hfModel || HF_MODELS[0]
49
+ else if (newProvider === 'local_openai') defaultModel = config.localModelName || LOCAL_MODELS[0]
50
+ else if (newProvider === 'ollama') defaultModel = config.ollamaModel || dynamicOllamaModels[0] || 'llama3:latest'
51
+ else if (newProvider.startsWith('custom:')) {
52
+ const cpId = newProvider.replace(/^custom:/, '')
53
+ const cp = (config.customProviders ?? []).find(p => p.id === cpId)
54
+ defaultModel = cp?.models[0]?.modelId ?? ''
55
+ }
56
+ setModel(defaultModel)
57
+ }
58
+
59
+ const handleEdit = (agent: Agent) => {
60
+ setEditingAgent(agent)
61
+ setName(agent.name)
62
+ setProvider(agent.provider)
63
+ setModel(agent.model)
64
+ setApiKey(agent.apiKey || '')
65
+ setSystemInstruction(agent.systemInstruction)
66
+ setIsEditing(true)
67
+ }
68
+
69
+ const handleSaveAgent = () => {
70
+ if (!name.trim()) {
71
+ alert('Please enter an agent name.')
72
+ return
73
+ }
74
+
75
+ let updatedAgents: Agent[] = []
76
+ if (editingAgent) {
77
+ // Update existing agent
78
+ updatedAgents = agents.map(a =>
79
+ a.id === editingAgent.id
80
+ ? { ...a, name, provider, model, apiKey: apiKey || undefined, systemInstruction }
81
+ : a
82
+ )
83
+ } else {
84
+ // Add new agent
85
+ const newAgent: Agent = {
86
+ id: `agent-${Date.now()}`,
87
+ name,
88
+ provider,
89
+ model,
90
+ apiKey: apiKey || undefined,
91
+ systemInstruction,
92
+ enabled: true
93
+ }
94
+ updatedAgents = [...agents, newAgent]
95
+ }
96
+
97
+ updateField('agents', updatedAgents)
98
+ closeForm()
99
+ }
100
+
101
+ const handleDeleteAgent = (id: string) => {
102
+ if (confirm('Are you sure you want to delete this agent?')) {
103
+ const updatedAgents = agents.filter(a => a.id !== id)
104
+ updateField('agents', updatedAgents)
105
+ }
106
+ }
107
+
108
+ const handleToggleAgent = (id: string, enabled: boolean) => {
109
+ const updatedAgents = agents.map(a =>
110
+ a.id === id ? { ...a, enabled } : a
111
+ )
112
+ updateField('agents', updatedAgents)
113
+ }
114
+
115
+ const handleDisableAll = () => {
116
+ const updatedAgents = agents.map(a => ({ ...a, enabled: false }))
117
+ updateField('agents', updatedAgents)
118
+ }
119
+
120
+ const closeForm = () => {
121
+ setEditingAgent(null)
122
+ setName('')
123
+ setProvider('gemini')
124
+ setModel('gemini-2.5-flash')
125
+ setApiKey('')
126
+ setSystemInstruction('')
127
+ setIsEditing(false)
128
+ }
129
+
130
+ const handleAddClick = () => {
131
+ setEditingAgent(null)
132
+ setName('')
133
+ setProvider('gemini')
134
+ setModel('gemini-2.5-flash')
135
+ setApiKey('')
136
+ setSystemInstruction('')
137
+ setIsEditing(true)
138
+ }
139
+
140
+ // Filter providers that have configured API keys, plus local ones
141
+ const availableProviders = [
142
+ { value: 'gemini', label: 'Google Gemini', hasKey: !!config.apiKey },
143
+ { value: 'openai', label: 'OpenAI', hasKey: !!config.openaiApiKey },
144
+ { value: 'anthropic', label: 'Anthropic Claude', hasKey: !!config.anthropicApiKey },
145
+ { value: 'openrouter', label: 'OpenRouter', hasKey: !!config.openrouterApiKey },
146
+ { value: 'deepseek', label: 'DeepSeek', hasKey: !!config.deepseekApiKey },
147
+ { value: 'ollama', label: 'Ollama (Local)', hasKey: true },
148
+ { value: 'huggingface', label: 'Hugging Face', hasKey: !!config.hfApiKey },
149
+ { value: 'local_openai', label: 'Local (LM Studio / OpenAI Compatible)', hasKey: true },
150
+ // Custom providers — available whenever they have a base URL configured
151
+ ...(config.customProviders ?? [])
152
+ .filter(cp => cp.baseUrl.trim().length > 0)
153
+ .map(cp => ({
154
+ value: `custom:${cp.id}`,
155
+ label: `${cp.displayName || cp.id} (Custom)`,
156
+ hasKey: true,
157
+ })),
158
+ ].filter(p => p.hasKey);
159
+
160
+ // Find out what models list corresponds to the selected provider
161
+ let modelList: string[] = []
162
+ let defaultGeneralModel = ''
163
+ if (provider === 'gemini') {
164
+ modelList = GEMINI_MODELS
165
+ defaultGeneralModel = config.geminiModel
166
+ } else if (provider === 'openai') {
167
+ modelList = OPENAI_MODELS
168
+ defaultGeneralModel = config.openaiModel
169
+ } else if (provider === 'openrouter') {
170
+ modelList = OPENROUTER_MODELS
171
+ defaultGeneralModel = config.openrouterModel
172
+ } else if (provider === 'deepseek') {
173
+ modelList = DEEPSEEK_MODELS
174
+ defaultGeneralModel = config.deepseekModel
175
+ } else if (provider === 'anthropic') {
176
+ modelList = ANTHROPIC_MODELS
177
+ defaultGeneralModel = config.anthropicModel
178
+ } else if (provider === 'huggingface') {
179
+ modelList = HF_MODELS
180
+ defaultGeneralModel = config.hfModel
181
+ } else if (provider === 'local_openai') {
182
+ modelList = LOCAL_MODELS
183
+ defaultGeneralModel = config.localModelName
184
+ } else if (provider === 'ollama') {
185
+ modelList = dynamicOllamaModels
186
+ defaultGeneralModel = config.ollamaModel
187
+ } else if (provider.startsWith('custom:')) {
188
+ const cpId = provider.replace(/^custom:/, '')
189
+ const cp = (config.customProviders ?? []).find(p => p.id === cpId)
190
+ modelList = cp?.models.map(m => m.modelId) ?? []
191
+ defaultGeneralModel = modelList[0] ?? ''
192
+ }
193
+
194
+ // Sort modelList so defaultGeneralModel is first (at the top)
195
+ const sortedModelList = defaultGeneralModel
196
+ ? [defaultGeneralModel, ...modelList.filter(m => m !== defaultGeneralModel)]
197
+ : modelList;
198
+
199
+ const isCustomModel = !sortedModelList.includes(model)
200
+
201
+ return (
202
+ <div className="tab-pane active animate-fade-in" style={{ display: 'flex', flexDirection: 'column', gap: '24px' }}>
203
+ <section className="setting-section">
204
+ <div className="section-heading" style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
205
+ <div>
206
+ <p className="section-kicker">Multi-Agent System</p>
207
+ <h2 className="section-title">Agent Collaboration Settings</h2>
208
+ <p className="section-description">
209
+ Configure multiple distinct AI agents with specific roles. You can adjust the models, system instructions, and credentials for each agent.
210
+ </p>
211
+ </div>
212
+ {!isEditing && (
213
+ <div style={{ display: 'flex', gap: '10px', height: 'fit-content', flexShrink: 0 }}>
214
+ <button className="btn btn-secondary" onClick={handleDisableAll} style={{ whiteSpace: 'nowrap' }}>
215
+ Disable All
216
+ </button>
217
+ <button className="btn btn-primary" onClick={handleAddClick} style={{ whiteSpace: 'nowrap' }}>
218
+ + Add Custom Agent
219
+ </button>
220
+ </div>
221
+ )}
222
+ </div>
223
+
224
+ <div className="toggle-row" style={{ margin: '16px 0 24px 0' }}>
225
+ <div>
226
+ <label>Enable Multi-Agent Collaboration</label>
227
+ <p className="hint">Allow multiple specialized agents to collaborate sequentially (Planner → Coder → Reviewer).</p>
228
+ </div>
229
+ <label className="settings-toggle-switch">
230
+ <input
231
+ type="checkbox"
232
+ checked={config.enableAgentCollaboration}
233
+ onChange={(e) => updateField('enableAgentCollaboration', e.target.checked)}
234
+ />
235
+ <span className="settings-toggle-slider"></span>
236
+ </label>
237
+ </div>
238
+
239
+ {isEditing ? (
240
+ <div className="form-grid single" style={{ background: 'var(--panel-soft)', padding: '20px', borderRadius: '8px', border: '1px solid var(--panel-raised)', marginTop: '10px' }}>
241
+ <h3 style={{ margin: '0 0 16px 0', fontSize: '18px', color: 'var(--accent)' }}>
242
+ {editingAgent ? `Edit Agent: ${editingAgent.name}` : 'Create New Agent'}
243
+ </h3>
244
+
245
+ <div className="setting-row">
246
+ <label>Agent Name</label>
247
+ <input
248
+ type="text"
249
+ value={name}
250
+ onChange={(e) => setName(e.target.value)}
251
+ placeholder="e.g. Code Reviewer"
252
+ />
253
+ </div>
254
+
255
+ <div className="setting-row">
256
+ <label>AI Provider</label>
257
+ <select value={provider} onChange={(e) => handleProviderChange(e.target.value)}>
258
+ {availableProviders.map(p => (
259
+ <option key={p.value} value={p.value}>{p.label}</option>
260
+ ))}
261
+ </select>
262
+ </div>
263
+
264
+ <div className="setting-row">
265
+ <label>Model Name</label>
266
+ <select
267
+ value={isCustomModel ? 'custom' : model}
268
+ onChange={(e) => {
269
+ const val = e.target.value
270
+ if (val === 'custom') {
271
+ setModel('')
272
+ } else {
273
+ setModel(val)
274
+ }
275
+ }}
276
+ >
277
+ {sortedModelList.map(m => (
278
+ <option key={m} value={m}>{m}</option>
279
+ ))}
280
+ <option value="custom">Custom...</option>
281
+ </select>
282
+ </div>
283
+
284
+ {isCustomModel && (
285
+ <div className="setting-row">
286
+ <label>Custom Model Name</label>
287
+ <input
288
+ type="text"
289
+ value={model}
290
+ onChange={(e) => setModel(e.target.value)}
291
+ placeholder="Enter custom model name..."
292
+ />
293
+ </div>
294
+ )}
295
+
296
+ <div className="setting-row">
297
+ <label>Custom API Key (Optional)</label>
298
+ <input
299
+ type="password"
300
+ value={apiKey}
301
+ onChange={(e) => setApiKey(e.target.value)}
302
+ placeholder="Leave blank to use global provider API key"
303
+ />
304
+ </div>
305
+
306
+ <div className="setting-row">
307
+ <label>System Instructions / Role</label>
308
+ <textarea
309
+ value={systemInstruction}
310
+ onChange={(e) => setSystemInstruction(e.target.value)}
311
+ placeholder="Describe this agent's goal, instructions, and personality..."
312
+ style={{ minHeight: '120px', resize: 'vertical' }}
313
+ />
314
+ </div>
315
+
316
+ <div style={{ display: 'flex', gap: '10px', marginTop: '16px', justifyContent: 'flex-end' }}>
317
+ <button className="btn btn-secondary" onClick={closeForm}>Cancel</button>
318
+ <button className="btn btn-primary" onClick={handleSaveAgent}>Save Agent</button>
319
+ </div>
320
+ </div>
321
+ ) : (
322
+ <div style={{ display: 'flex', flexDirection: 'column', gap: '16px', marginTop: '16px' }}>
323
+ {agents.map((agent) => (
324
+ <div
325
+ key={agent.id}
326
+ style={{
327
+ display: 'flex',
328
+ flexDirection: 'column',
329
+ padding: '16px',
330
+ background: 'var(--panel-bg)',
331
+ border: '1px solid var(--panel-raised)',
332
+ borderRadius: '8px',
333
+ gap: '8px'
334
+ }}
335
+ >
336
+ <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
337
+ <div style={{ display: 'flex', alignItems: 'center', gap: '10px' }}>
338
+ <strong style={{ fontSize: '18px' }}>{agent.name}</strong>
339
+ <span
340
+ style={{
341
+ fontSize: '11px',
342
+ padding: '2px 6px',
343
+ borderRadius: '4px',
344
+ background: 'var(--panel-raised)',
345
+ color: 'var(--accent)'
346
+ }}
347
+ >
348
+ {agent.provider} ({agent.model})
349
+ </span>
350
+ </div>
351
+ <div style={{ display: 'flex', alignItems: 'center', gap: '14px' }}>
352
+ <label className="settings-toggle-switch">
353
+ <input
354
+ type="checkbox"
355
+ checked={agent.enabled}
356
+ onChange={(e) => handleToggleAgent(agent.id, e.target.checked)}
357
+ />
358
+ <span className="settings-toggle-slider"></span>
359
+ </label>
360
+ <button className="btn btn-secondary" style={{ padding: '4px 8px', fontSize: '12px' }} onClick={() => handleEdit(agent)}>Edit</button>
361
+ {!['planner', 'coder', 'reviewer'].includes(agent.id) && (
362
+ <button className="btn btn-danger" style={{ padding: '4px 8px', fontSize: '12px' }} onClick={() => handleDeleteAgent(agent.id)}>Delete</button>
363
+ )}
364
+ </div>
365
+ </div>
366
+ <p style={{ margin: '4px 0 0 0', fontSize: '14px', color: 'var(--text-soft)', fontStyle: 'italic' }}>
367
+ "{agent.systemInstruction}"
368
+ </p>
369
+ </div>
370
+ ))}
371
+ {agents.length === 0 && (
372
+ <div style={{ textAlign: 'center', padding: '32px', color: 'var(--text-soft)', fontStyle: 'italic' }}>
373
+ No agents configured. Click "+ Add Custom Agent" to get started.
374
+ </div>
375
+ )}
376
+ </div>
377
+ )}
378
+ </section>
379
+ </div>
380
+ )
381
+ }
@@ -169,28 +169,6 @@ export default function AutomationTab({
169
169
  {!isDesktopApp && <p className="hint">Opening and reloading local workflow files requires the desktop app.</p>}
170
170
  </section>
171
171
 
172
- <section className="setting-section">
173
- <div className="section-heading">
174
- <div>
175
- <p className="section-kicker">Code mode</p>
176
- <h2 className="section-title">Agent Collaboration</h2>
177
- </div>
178
- </div>
179
- <div className="toggle-row">
180
- <div>
181
- <label>Enable Multi-Agent Review</label>
182
- <p className="hint">Allow a secondary model to review code written by the primary model.</p>
183
- </div>
184
- <label className="settings-toggle-switch">
185
- <input
186
- type="checkbox"
187
- checked={config.enableAgentCollaboration}
188
- onChange={(e) => updateField('enableAgentCollaboration', e.target.checked)}
189
- />
190
- <span className="settings-toggle-slider"></span>
191
- </label>
192
- </div>
193
- </section>
194
172
  </div>
195
173
  )
196
174
  }