@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.
Files changed (200) hide show
  1. package/.github/workflows/release.yml +1 -1
  2. package/Cargo.lock +34 -3
  3. package/Cargo.toml +2 -2
  4. package/README.md +20 -6
  5. package/Release_Note.md +129 -0
  6. package/assets/Mint_CLI.png +0 -0
  7. package/bin/mint +0 -0
  8. package/crates/mint-cli/Cargo.toml +1 -0
  9. package/crates/mint-cli/src/agent.rs +242 -54
  10. package/crates/mint-cli/src/main.rs +397 -74
  11. package/crates/mint-core/src/agent_loop.rs +1 -0
  12. package/crates/mint-core/src/api_server.rs +64 -2
  13. package/crates/mint-core/src/browser.rs +456 -0
  14. package/crates/mint-core/src/channels.rs +1 -0
  15. package/crates/mint-core/src/chat.rs +3 -0
  16. package/crates/mint-core/src/config.rs +130 -1
  17. package/crates/mint-core/src/lib.rs +19 -0
  18. package/crates/mint-core/src/orchestration.rs +317 -12
  19. package/crates/mint-core/src/skills.rs +113 -4
  20. package/crates/mint-core/src/tts.rs +3 -1
  21. package/crates/mint-core/src/workflows.rs +1 -22
  22. package/flatpak/com.pheem49.mint.desktop +9 -0
  23. package/flatpak/com.pheem49.mint.metainfo.xml +35 -0
  24. package/flatpak/com.pheem49.mint.yaml +52 -0
  25. package/package.json +1 -1
  26. package/shared-modules/.github/workflows/ci.yml +120 -0
  27. package/shared-modules/.github/workflows/update.yaml +28 -0
  28. package/shared-modules/CODEOWNERS +23 -0
  29. package/shared-modules/README.md +79 -0
  30. package/shared-modules/SDL/README.md +3 -0
  31. package/shared-modules/SDL/SDL-1.2.15.json +51 -0
  32. package/shared-modules/SDL/SDL_Pango-0.1.2-API-adds.patch +118 -0
  33. package/shared-modules/SDL/SDL_image-1.2.12.json +25 -0
  34. package/shared-modules/SDL/SDL_mixer-1.2.12.json +27 -0
  35. package/shared-modules/SDL/SDL_net-1.2.8.json +19 -0
  36. package/shared-modules/SDL/SDL_pango-0.1.2.json +26 -0
  37. package/shared-modules/SDL/SDL_ttf-2.0.11.json +28 -0
  38. package/shared-modules/SDL/libsdl-1.2.15-strict-prototypes.patch +316 -0
  39. package/shared-modules/SDL/sdl-check-for-SDL_VIDEO_X11_BACKINGSTORE.patch +24 -0
  40. package/shared-modules/SDL/sdl-libx11-build.patch +59 -0
  41. package/shared-modules/SDL/sdl-sysloadso-buffer-length.patch +27 -0
  42. package/shared-modules/SDL/sdl12-compat.json +22 -0
  43. package/shared-modules/SDL2/SDL2_image.json +27 -0
  44. package/shared-modules/SDL2/SDL2_ttf.json +29 -0
  45. package/shared-modules/cld2/CMakeLists.txt +157 -0
  46. package/shared-modules/cld2/cld2.json +24 -0
  47. package/shared-modules/clutter/clutter.json +72 -0
  48. package/shared-modules/dbus-glib/dbus-glib.json +29 -0
  49. package/shared-modules/glew/glew.json +36 -0
  50. package/shared-modules/glu/glu-9.json +12 -0
  51. package/shared-modules/gtk2/arc-gtk2-theme-Replace-Inkscape-dependency-with-rsvg-convert.patch +180 -0
  52. package/shared-modules/gtk2/gtk2-common-themes.json +360 -0
  53. package/shared-modules/gtk2/gtk2-fix-crash-in-show-uri.patch +30 -0
  54. package/shared-modules/gtk2/gtk2-gcc14.patch +281 -0
  55. package/shared-modules/gtk2/gtk2-gcc15.patch +250 -0
  56. package/shared-modules/gtk2/gtk2-murrine-c99.patch +34 -0
  57. package/shared-modules/gtk2/gtk2-use-adwaita-theme.patch +80 -0
  58. package/shared-modules/gtk2/gtk2.json +120 -0
  59. package/shared-modules/gtk2/ibus-gcc15.patch +13 -0
  60. package/shared-modules/gtk2/murrine-engine-fix-crash.patch +12 -0
  61. package/shared-modules/gtk2/readme.md +10 -0
  62. package/shared-modules/intltool/intltool-0.51.json +20 -0
  63. package/shared-modules/intltool/intltool-perl5.26-regex-fixes.patch +59 -0
  64. package/shared-modules/lame/0001-Build-xmm_quantize_sub.c-with-msse.patch +34 -0
  65. package/shared-modules/lame/0002-Include-GTK-1-autoconf-directives-in-build-system.patch +222 -0
  66. package/shared-modules/lame/0003-Patch-out-remaining-ansi2knr.patch +70 -0
  67. package/shared-modules/lame/0004-initscr-is-not-used-anywhere-in-lame-sourcetree.patch +41 -0
  68. package/shared-modules/lame/0005-Update-autoconf.patch +81 -0
  69. package/shared-modules/lame/lame-3.99.5.json +32 -0
  70. package/shared-modules/libappindicator/libappindicator-gtk2-12.10.json +140 -0
  71. package/shared-modules/libappindicator/libappindicator-gtk3-12.10.json +140 -0
  72. package/shared-modules/libappindicator/libappindicator-gtk3-introspection-12.10.json +140 -0
  73. package/shared-modules/libappindicator/patches/0001-libappindicator-ftbfs.patch +20 -0
  74. package/shared-modules/libappindicator/patches/0002-libappindicator-no-python.patch +888 -0
  75. package/shared-modules/libappindicator/patches/0003-libappindicator-fix-crash-from-incorrect-signal-emission.patch +74 -0
  76. package/shared-modules/libappindicator/patches/0004-libappindicator-fix-crash-iterating-icon-themes.patch +11 -0
  77. package/shared-modules/libayatana-appindicator/0001-Fix-HAVE_VALGRIND-AM_CONDITIONAL.patch +51 -0
  78. package/shared-modules/libayatana-appindicator/0001-Make-introspection-configurable.patch +44 -0
  79. package/shared-modules/libayatana-appindicator/libayatana-appindicator-gtk3.json +121 -0
  80. package/shared-modules/libcanberra/0001-gtk-Don-t-assume-all-GdkDisplays-are-GdkX11Displays-.patch +123 -0
  81. package/shared-modules/libcanberra/libcanberra.json +44 -0
  82. package/shared-modules/libmad/libmad.json +19 -0
  83. package/shared-modules/libsecret/libsecret.json +23 -0
  84. package/shared-modules/libsoup/libsoup-2.4.json +14 -0
  85. package/shared-modules/libusb/libusb.json +27 -0
  86. package/shared-modules/linux-audio/dssi.json +31 -0
  87. package/shared-modules/linux-audio/fftw3f-static.json +44 -0
  88. package/shared-modules/linux-audio/fftw3f.json +42 -0
  89. package/shared-modules/linux-audio/fluidsynth2-static.json +30 -0
  90. package/shared-modules/linux-audio/fluidsynth2.json +19 -0
  91. package/shared-modules/linux-audio/ladspa.json +26 -0
  92. package/shared-modules/linux-audio/lash.json +35 -0
  93. package/shared-modules/linux-audio/libinstpatch.json +16 -0
  94. package/shared-modules/linux-audio/liblo-static.json +22 -0
  95. package/shared-modules/linux-audio/liblo.json +17 -0
  96. package/shared-modules/linux-audio/lilv-static.json +94 -0
  97. package/shared-modules/linux-audio/lilv.json +82 -0
  98. package/shared-modules/linux-audio/lrdf.json +57 -0
  99. package/shared-modules/linux-audio/lv2.json +21 -0
  100. package/shared-modules/linux-audio/patches/lash/0001-configure-remove-aclocal-m4.patch +29 -0
  101. package/shared-modules/linux-audio/patches/lash/0002-Fix-build-with-recent-gcc.patch +73 -0
  102. package/shared-modules/linux-audio/patches/lash/0003-Fix-linking.patch +136 -0
  103. package/shared-modules/linux-audio/patches/raptor-libxml.patch +31 -0
  104. package/shared-modules/linux-audio/patches/stk-install.sh +54 -0
  105. package/shared-modules/linux-audio/stk.json +28 -0
  106. package/shared-modules/lua5.1/0004-Fix-stack-overflow-in-vararg-functions.patch +21 -0
  107. package/shared-modules/lua5.1/lua-5.1.5-so.patch +43 -0
  108. package/shared-modules/lua5.1/lua-5.1.5.json +42 -0
  109. package/shared-modules/lua5.3/lua-5.3.5.json +41 -0
  110. package/shared-modules/lua5.3/lua.pc.in +8 -0
  111. package/shared-modules/lua5.3/luaconf.patch +47 -0
  112. package/shared-modules/lua5.3/makefile-add-liblua.so-rule.patch +47 -0
  113. package/shared-modules/lua5.4/lua-5.4.json +41 -0
  114. package/shared-modules/lua5.4/lua.pc.in +8 -0
  115. package/shared-modules/lua5.4/luaconf.patch +34 -0
  116. package/shared-modules/lua5.4/makefile-add-liblua.so-rule.patch +42 -0
  117. package/shared-modules/luajit/luajit.json +30 -0
  118. package/shared-modules/lzo/lzo.json +27 -0
  119. package/shared-modules/mac/mac.json +18 -0
  120. package/shared-modules/physfs/physfs.json +26 -0
  121. package/shared-modules/pygame/audiofile-gcc6.patch +21 -0
  122. package/shared-modules/pygame/cython-fixes.patch +34 -0
  123. package/shared-modules/pygame/fluidsynth-no-rawmidi.patch +69 -0
  124. package/shared-modules/pygame/glib.patch +25 -0
  125. package/shared-modules/pygame/pygame1-python2.json +157 -0
  126. package/shared-modules/pygtk/pygtk-c99.patch +15 -0
  127. package/shared-modules/pygtk/pygtk.json +117 -0
  128. package/shared-modules/pygtk/pygtk.patch +39 -0
  129. package/shared-modules/python2.7/python-2.7.json +48 -0
  130. package/shared-modules/smpeg/smpeg-0.4.5.json +50 -0
  131. package/shared-modules/smpeg/smpeg-am-prog-as.patch +12 -0
  132. package/shared-modules/smpeg/smpeg-export-mpegaudio-class.patch +17 -0
  133. package/shared-modules/smpeg/smpeg-gcc6.patch +40 -0
  134. package/shared-modules/smpeg/smpeg-no-gtk.patch +37 -0
  135. package/shared-modules/squashfs-tools/squashfs-tools.json +25 -0
  136. package/shared-modules/update-checker.sh +50 -0
  137. package/shared-modules/vorbisgain/0001-temp_files.patch +75 -0
  138. package/shared-modules/vorbisgain/0002-errno.patch +33 -0
  139. package/shared-modules/vorbisgain/0003-manpage.patch +248 -0
  140. package/shared-modules/vorbisgain/0004-vorbisgain_mtime.patch +183 -0
  141. package/shared-modules/vorbisgain/0005-double_fclose.patch +21 -0
  142. package/shared-modules/vorbisgain/0006-manpage_hyphens.patch +56 -0
  143. package/shared-modules/vorbisgain/0007-recursively_spelling.patch +14 -0
  144. package/shared-modules/vorbisgain/0008-manpage_recursion_mistake.patch +23 -0
  145. package/shared-modules/vorbisgain/0009-hardening.patch +13 -0
  146. package/shared-modules/vorbisgain/0010-fclose.patch +13 -0
  147. package/shared-modules/vorbisgain/README.md +23 -0
  148. package/shared-modules/vorbisgain/vorbisgain_0.37-2.json +29 -0
  149. package/src/renderer/shared/agentProgress.ts +145 -0
  150. package/src/renderer/shared/calculator.ts +52 -0
  151. package/src/renderer/shared/components/AgentActivityDrawer.tsx +72 -0
  152. package/src/renderer/shared/components/AgentActivityTable.tsx +66 -0
  153. package/src/renderer/shared/components/ApprovalCard.tsx +93 -0
  154. package/src/renderer/shared/components/ChatCodeBlock.tsx +75 -0
  155. package/src/renderer/shared/components/ThinkingBlock.tsx +101 -0
  156. package/src/renderer/shared/constants/models.ts +91 -0
  157. package/src/renderer/shared/platform.ts +82 -0
  158. package/src/renderer/shared/types.ts +140 -0
  159. package/src/renderer/shared/utils/agentActivity.ts +187 -0
  160. package/src/renderer/shared/utils/approval.ts +1 -0
  161. package/src/renderer/shared/utils/approval.tsx +64 -0
  162. package/src/renderer/shared/utils/markdown.tsx +347 -0
  163. package/src/renderer/shared/utils/providers.ts +36 -0
  164. package/src/renderer/shared/utils/speech.ts +216 -0
  165. package/src/renderer/shared/utils/ui.ts +147 -0
  166. package/src/renderer/src/agentProgress.ts +11 -39
  167. package/src/renderer/src/calculator.ts +3 -46
  168. package/src/renderer/src/components/ChatPanel.tsx +230 -1202
  169. package/src/renderer/src/components/ImageStudioPanel.tsx +11 -1
  170. package/src/renderer/src/components/MintDashboard.tsx +63 -171
  171. package/src/renderer/src/components/PicturesLibrary.tsx +32 -8
  172. package/src/renderer/src/components/Settings/AgentsTab.tsx +362 -0
  173. package/src/renderer/src/components/Settings/AutomationTab.tsx +0 -22
  174. package/src/renderer/src/components/Settings/PluginsTab.tsx +221 -41
  175. package/src/renderer/src/components/SettingsWindow.tsx +54 -12
  176. package/src/renderer/src/components/WorkspacePanel.tsx +66 -9
  177. package/src/renderer/src/css/image-studio.css +6 -0
  178. package/src/renderer/src/css/styles.css +148 -11
  179. package/src/renderer/src/index.css +12 -1
  180. package/src/renderer/src/tauri.ts +169 -143
  181. package/src/renderer/src-web/agentProgress.ts +11 -39
  182. package/src/renderer/src-web/calculator.ts +3 -46
  183. package/src/renderer/src-web/components/ChatPanel.tsx +223 -1199
  184. package/src/renderer/src-web/components/ImageStudioPanel.tsx +11 -1
  185. package/src/renderer/src-web/components/MintDashboard.tsx +31 -172
  186. package/src/renderer/src-web/components/PicturesLibrary.tsx +23 -29
  187. package/src/renderer/src-web/components/Settings/AgentsTab.tsx +362 -0
  188. package/src/renderer/src-web/components/Settings/AutomationTab.tsx +0 -22
  189. package/src/renderer/src-web/components/Settings/PluginsTab.tsx +221 -41
  190. package/src/renderer/src-web/components/SettingsWindow.tsx +54 -12
  191. package/src/renderer/src-web/css/image-studio.css +6 -0
  192. package/src/renderer/src-web/css/styles.css +184 -12
  193. package/src/renderer/src-web/index.css +12 -1
  194. package/src/renderer/src-web/tauri.ts +204 -104
  195. package/src-tauri/src/browser.rs +1 -141
  196. package/src-tauri/src/headless.rs +3 -2
  197. package/src-tauri/src/lib.rs +254 -59
  198. package/src-tauri/src/webhooks.rs +1 -0
  199. package/src-tauri/src/workflows.rs +57 -4
  200. package/src-tauri/tauri.conf.json +1 -1
@@ -1,115 +1,46 @@
1
- export interface RuntimeStatus {
2
- backend: string
3
- configPath: string
4
- activeProvider: string
5
- availableProviders: string[]
6
- integrations: Record<string, unknown>
7
- }
1
+ /** Returns true when running inside the Tauri webview (desktop app). */
2
+ export const isTauriRuntime = (): boolean =>
3
+ typeof window !== 'undefined' && Boolean((window as any).__TAURI_INTERNALS__)
8
4
 
9
- export interface ChatResponse {
10
- provider: string
11
- model: string
12
- text: string
13
- fallbackProvider?: string | null
5
+ /**
6
+ * Returns the local API base URL.
7
+ * Uses the current hostname so LAN access (e.g. from another device) works correctly.
8
+ * Mirrors the same helper in src-web/tauri.ts.
9
+ */
10
+ export const getLocalApiBase = (): string => {
11
+ const host = typeof window !== 'undefined' ? window.location.hostname : 'localhost'
12
+ return `http://${host}:3000/api`
14
13
  }
15
14
 
16
- export interface TtsUrl {
17
- shortText: string
18
- url: string
19
- }
15
+ export * from '../shared/types'
16
+ import type { MintPlatformApi } from '../shared/platform'
17
+ import type {
20
18
 
21
- export interface DocumentAttachment {
22
- filename: string
23
- dataUri: string
24
- }
19
+ RuntimeStatus,
20
+ ChatResponse,
21
+ TtsUrl,
22
+ DocumentAttachment,
23
+ AgentProgress,
24
+ InteractionMemory,
25
+ ChatSession,
26
+ PictureEntry,
27
+ ImageGenRequest,
28
+ ImageGenProviders,
29
+ ImageGenResponse,
30
+ WorkspaceTreeEntry,
31
+ CodeEdit,
32
+ CodeEditProposal,
33
+ } from '../shared/types'
25
34
 
26
- export type AgentProgress =
27
- | { type: 'Thinking'; data: { elapsed_secs: number } }
28
- | { type: 'Thought'; data: { thought: string } }
29
- | { type: 'ToolStart'; data: { action: string; input: Record<string, unknown> } }
30
- | { type: 'ToolEnd'; data: { action: string; input: Record<string, unknown>; result: string } }
31
35
 
32
36
  type DesktopStreamEvent =
33
37
  | { type: 'chunk'; chunk: string }
34
38
  | { type: 'progress'; progress: AgentProgress }
35
39
 
36
- export interface InteractionMemory {
37
- id: number
38
- chatId: string
39
- userText: string
40
- aiText: string
41
- provider: string
42
- model: string
43
- fallbackProvider?: string | null
44
- createdAt: string
45
- agentActivity?: AgentProgress[] | null
46
- }
47
-
48
- export interface ChatSession {
49
- id: string
50
- title: string
51
- kind: string
52
- createdAt: string
53
- updatedAt: string
54
- }
55
-
56
- export interface PictureEntry {
57
- id: string
58
- filename: string
59
- path: string
60
- mimeType: string
61
- createdAt: string
62
- source: string
63
- message: string
64
- thumbnailPath?: string
65
- url?: string
66
- thumbnailUrl?: string
67
- }
68
-
69
- export interface ImageGenRequest {
70
- prompt: string
71
- negativePrompt?: string
72
- aspectRatio?: '1:1' | '16:9' | '9:16' | '4:3'
73
- numImages?: number
74
- model?: string
75
- /** Which image generation provider to use. Falls back to config default when omitted. */
76
- provider?: string
77
- }
78
-
79
- export interface ImageGenProviders {
80
- active: string
81
- available: string[]
82
- }
83
-
84
- export interface ImageGenResponse {
85
- images: PictureEntry[]
86
- model: string
87
- provider: string
88
- prompt: string
89
- description?: string | null
90
- }
91
-
92
- export interface WorkspaceTreeEntry {
93
- name: string
94
- path: string
95
- kind: 'file' | 'directory'
96
- children: WorkspaceTreeEntry[]
97
- }
98
-
99
- export interface CodeEdit {
100
- path: string
101
- content: string
102
- }
103
-
104
- export interface CodeEditProposal {
105
- approvalRequired: boolean
106
- approvalToken: string
107
- edits: Array<{ path: string; existed: boolean; diff: string }>
108
- }
109
40
 
110
41
  export async function getRuntimeStatus(): Promise<RuntimeStatus> {
111
- if (typeof window === 'undefined' || !(window as any).__TAURI_INTERNALS__) {
112
- const API_BASE = "http://localhost:3000/api";
42
+ if (!isTauriRuntime()) {
43
+ const API_BASE = getLocalApiBase();
113
44
  try {
114
45
  const res = await fetch(`${API_BASE}/status`);
115
46
  return await res.json();
@@ -128,6 +59,28 @@ export async function getRuntimeStatus(): Promise<RuntimeStatus> {
128
59
  return invoke<RuntimeStatus>('get_runtime_status')
129
60
  }
130
61
 
62
+ export interface DetectedTools {
63
+ docker: boolean
64
+ git: boolean
65
+ gh: boolean
66
+ node: boolean
67
+ }
68
+
69
+ export async function detectSystemTools(): Promise<DetectedTools> {
70
+ if (!isTauriRuntime()) {
71
+ const API_BASE = getLocalApiBase();
72
+ try {
73
+ const res = await fetch(`${API_BASE}/detect-tools`);
74
+ return await res.json();
75
+ } catch (e) {
76
+ console.error("Failed to detect tools from local server:", e);
77
+ return { docker: false, git: false, gh: false, node: false };
78
+ }
79
+ }
80
+ const { invoke } = await import('@tauri-apps/api/core')
81
+ return invoke<DetectedTools>('detect_system_tools')
82
+ }
83
+
131
84
  export async function sendChatMessage(
132
85
  message: string,
133
86
  imageDataUri?: string | null,
@@ -135,15 +88,16 @@ export async function sendChatMessage(
135
88
  documentAttachment?: DocumentAttachment | null,
136
89
  workspacePath?: string | null,
137
90
  chatId?: string | null,
91
+ agentId?: string | null,
138
92
  ): Promise<ChatResponse> {
139
93
  const outgoingMessage = withImagePlaceholder(message, imageDataUri)
140
- if (typeof window === 'undefined' || !(window as any).__TAURI_INTERNALS__) {
141
- const API_BASE = "http://localhost:3000/api";
94
+ if (!isTauriRuntime()) {
95
+ const API_BASE = getLocalApiBase();
142
96
  try {
143
97
  const res = await fetch(`${API_BASE}/chat`, {
144
98
  method: 'POST',
145
99
  headers: { 'Content-Type': 'application/json' },
146
- body: JSON.stringify({ message: outgoingMessage, systemInstruction: '', chatId, imageDataUri, audioDataUri, documentAttachment })
100
+ body: JSON.stringify({ message: outgoingMessage, systemInstruction: '', chatId, imageDataUri, audioDataUri, documentAttachment, agentId })
147
101
  });
148
102
  const data = await res.json().catch(() => null);
149
103
  if (!res.ok) {
@@ -164,7 +118,7 @@ export async function sendChatMessage(
164
118
  }
165
119
  const { invoke } = await import('@tauri-apps/api/core')
166
120
  const response = await invoke<ChatResponse>('send_chat_message', {
167
- request: { message: outgoingMessage, systemInstruction: '', chatId, imageDataUri, audioDataUri, documentAttachment, workspacePath },
121
+ request: { message: outgoingMessage, systemInstruction: '', chatId, imageDataUri, audioDataUri, documentAttachment, workspacePath, agentId },
168
122
  })
169
123
  if (imageDataUri) {
170
124
  await invoke('save_pictures', {
@@ -186,14 +140,15 @@ export async function streamChatMessage(
186
140
  documentAttachment?: DocumentAttachment | null,
187
141
  workspacePath?: string | null,
188
142
  chatId?: string | null,
143
+ agentId?: string | null,
189
144
  ): Promise<ChatResponse> {
190
- if (typeof window === 'undefined' || !(window as any).__TAURI_INTERNALS__) {
191
- const API_BASE = "http://localhost:3000/api";
145
+ if (!isTauriRuntime()) {
146
+ const API_BASE = getLocalApiBase();
192
147
  const outgoingMessage = withImagePlaceholder(message, imageDataUri);
193
148
  const res = await fetch(`${API_BASE}/chat-stream`, {
194
149
  method: 'POST',
195
150
  headers: { 'Content-Type': 'application/json' },
196
- body: JSON.stringify({ message: outgoingMessage, systemInstruction, chatId, imageDataUri, audioDataUri, documentAttachment })
151
+ body: JSON.stringify({ message: outgoingMessage, systemInstruction, chatId, imageDataUri, audioDataUri, documentAttachment, agentId })
197
152
  });
198
153
  if (!res.ok) {
199
154
  const data = await res.json().catch(() => null);
@@ -239,7 +194,7 @@ export async function streamChatMessage(
239
194
  else onProgress?.(event.progress)
240
195
  }
241
196
  const response = await invoke<ChatResponse>('stream_chat_message', {
242
- request: { message: outgoingMessage, systemInstruction, chatId, imageDataUri, audioDataUri, documentAttachment, workspacePath },
197
+ request: { message: outgoingMessage, systemInstruction, chatId, imageDataUri, audioDataUri, documentAttachment, workspacePath, agentId },
243
198
  onEvent,
244
199
  })
245
200
  if (imageDataUri) {
@@ -265,9 +220,23 @@ export async function getTtsUrls(text: string): Promise<TtsUrl[]> {
265
220
  return invoke<TtsUrl[]>('get_tts_urls', { text })
266
221
  }
267
222
 
223
+ export async function cancelChatMessage(chatId: string): Promise<void> {
224
+ if (!isTauriRuntime()) {
225
+ const API_BASE = getLocalApiBase();
226
+ await fetch(`${API_BASE}/cancel-chat`, {
227
+ method: 'POST',
228
+ headers: { 'Content-Type': 'application/json' },
229
+ body: JSON.stringify({ chatId })
230
+ });
231
+ return;
232
+ }
233
+ const { invoke } = await import('@tauri-apps/api/core')
234
+ await invoke('cancel_chat_message', { chatId })
235
+ }
236
+
268
237
  export async function getRecentInteractions(limit = 50, chatId?: string | null): Promise<InteractionMemory[]> {
269
- if (typeof window === 'undefined' || !(window as any).__TAURI_INTERNALS__) {
270
- const API_BASE = "http://localhost:3000/api";
238
+ if (!isTauriRuntime()) {
239
+ const API_BASE = getLocalApiBase();
271
240
  try {
272
241
  const params = new URLSearchParams({ limit: String(limit) });
273
242
  if (chatId) params.set('chatId', chatId);
@@ -288,8 +257,8 @@ export async function saveSystemInteraction(
288
257
  provider: string,
289
258
  model: string,
290
259
  ): Promise<any> {
291
- if (typeof window === 'undefined' || !(window as any).__TAURI_INTERNALS__) {
292
- const API_BASE = "http://localhost:3000/api";
260
+ if (!isTauriRuntime()) {
261
+ const API_BASE = getLocalApiBase();
293
262
  try {
294
263
  const res = await fetch(`${API_BASE}/interactions`, {
295
264
  method: 'POST',
@@ -310,8 +279,8 @@ export async function saveInteractionAgentActivity(
310
279
  interactionId: number,
311
280
  activity: AgentProgress[],
312
281
  ): Promise<void> {
313
- if (typeof window === 'undefined' || !(window as any).__TAURI_INTERNALS__) {
314
- const API_BASE = "http://localhost:3000/api";
282
+ if (!isTauriRuntime()) {
283
+ const API_BASE = getLocalApiBase();
315
284
  try {
316
285
  const res = await fetch(`${API_BASE}/interactions/agent-activity`, {
317
286
  method: 'POST',
@@ -331,8 +300,8 @@ export async function saveInteractionAgentActivity(
331
300
  }
332
301
 
333
302
  export async function listChatSessions(): Promise<ChatSession[]> {
334
- if (typeof window === 'undefined' || !(window as any).__TAURI_INTERNALS__) {
335
- const API_BASE = "http://localhost:3000/api";
303
+ if (!isTauriRuntime()) {
304
+ const API_BASE = getLocalApiBase();
336
305
  try {
337
306
  const res = await fetch(`${API_BASE}/chat-sessions`);
338
307
  const data = await res.json();
@@ -347,8 +316,8 @@ export async function listChatSessions(): Promise<ChatSession[]> {
347
316
  }
348
317
 
349
318
  export async function deleteChatSession(chatId: string): Promise<number> {
350
- if (typeof window === 'undefined' || !(window as any).__TAURI_INTERNALS__) {
351
- const API_BASE = "http://localhost:3000/api";
319
+ if (!isTauriRuntime()) {
320
+ const API_BASE = getLocalApiBase();
352
321
  try {
353
322
  const params = new URLSearchParams({ chatId });
354
323
  const res = await fetch(`${API_BASE}/chat-sessions/delete?${params.toString()}`, { method: 'POST' });
@@ -364,8 +333,8 @@ export async function deleteChatSession(chatId: string): Promise<number> {
364
333
  }
365
334
 
366
335
  export async function renameChatSession(chatId: string, newTitle: string): Promise<number> {
367
- if (typeof window === 'undefined' || !(window as any).__TAURI_INTERNALS__) {
368
- const API_BASE = "http://localhost:3000/api";
336
+ if (!isTauriRuntime()) {
337
+ const API_BASE = getLocalApiBase();
369
338
  try {
370
339
  const res = await fetch(`${API_BASE}/chat-sessions/rename`, {
371
340
  method: 'POST',
@@ -384,8 +353,8 @@ export async function renameChatSession(chatId: string, newTitle: string): Promi
384
353
  }
385
354
 
386
355
  export async function getProfileValue(key: string): Promise<string> {
387
- if (typeof window === 'undefined' || !(window as any).__TAURI_INTERNALS__) {
388
- const API_BASE = "http://localhost:3000/api";
356
+ if (!isTauriRuntime()) {
357
+ const API_BASE = getLocalApiBase();
389
358
  try {
390
359
  const params = new URLSearchParams({ key });
391
360
  const res = await fetch(`${API_BASE}/profile?${params.toString()}`);
@@ -401,8 +370,8 @@ export async function getProfileValue(key: string): Promise<string> {
401
370
  }
402
371
 
403
372
  export async function setProfileValue(key: string, value: string): Promise<boolean> {
404
- if (typeof window === 'undefined' || !(window as any).__TAURI_INTERNALS__) {
405
- const API_BASE = "http://localhost:3000/api";
373
+ if (!isTauriRuntime()) {
374
+ const API_BASE = getLocalApiBase();
406
375
  try {
407
376
  const res = await fetch(`${API_BASE}/profile`, {
408
377
  method: 'POST',
@@ -426,9 +395,10 @@ export interface LearnedSkill {
426
395
  sourcePath: string
427
396
  content: string
428
397
  updatedAt: string
398
+ location?: string
429
399
  }
430
400
 
431
- export async function listLearnedSkills(): Promise<LearnedSkill[]> {
401
+ export async function listLearnedSkills(workspacePath?: string): Promise<LearnedSkill[]> {
432
402
  if (typeof window === 'undefined' || !(window as any).__TAURI_INTERNALS__) {
433
403
  try {
434
404
  const val = await getProfileValue('learned-skills-web-mock')
@@ -442,7 +412,7 @@ export async function listLearnedSkills(): Promise<LearnedSkill[]> {
442
412
  }
443
413
  }
444
414
  const { invoke } = await import('@tauri-apps/api/core')
445
- return invoke<LearnedSkill[]>('list_learned_skills')
415
+ return invoke<LearnedSkill[]>('list_learned_skills', { workspacePath })
446
416
  }
447
417
 
448
418
  export async function addLearnedSkill(name: string, content: string): Promise<LearnedSkill> {
@@ -486,8 +456,8 @@ export async function deleteLearnedSkill(name: string): Promise<number> {
486
456
  }
487
457
 
488
458
  export async function clearChatHistory(chatId?: string | null): Promise<number> {
489
- if (typeof window === 'undefined' || !(window as any).__TAURI_INTERNALS__) {
490
- const API_BASE = "http://localhost:3000/api";
459
+ if (!isTauriRuntime()) {
460
+ const API_BASE = getLocalApiBase();
491
461
  try {
492
462
  const params = new URLSearchParams();
493
463
  if (chatId) params.set('chatId', chatId);
@@ -505,14 +475,15 @@ export async function clearChatHistory(chatId?: string | null): Promise<number>
505
475
  }
506
476
 
507
477
  export async function listSavedPictures(): Promise<PictureEntry[]> {
508
- if (typeof window === 'undefined' || !(window as any).__TAURI_INTERNALS__) {
509
- const API_BASE = "http://localhost:3000/api";
478
+ if (!isTauriRuntime()) {
479
+ const API_BASE = getLocalApiBase();
510
480
  try {
511
- const res = await fetch(`${API_BASE}/pictures`);
481
+ const res = await fetch(`${API_BASE}/pictures?_t=${Date.now()}`);
512
482
  const pictures = await res.json();
483
+ const timestamp = Date.now();
513
484
  return Array.isArray(pictures)
514
485
  ? pictures.map((picture) => {
515
- const pictureUrl = picture.url ? `${API_BASE.replace('/api', '')}${picture.url}` : undefined
486
+ const pictureUrl = picture.url ? `${API_BASE.replace('/api', '')}${picture.url}?_t=${timestamp}` : undefined
516
487
  return {
517
488
  ...picture,
518
489
  path: pictureUrl || picture.path,
@@ -534,8 +505,8 @@ export async function listSavedPictures(): Promise<PictureEntry[]> {
534
505
  export async function generateImages(
535
506
  request: ImageGenRequest
536
507
  ): Promise<ImageGenResponse> {
537
- if (typeof window === 'undefined' || !(window as any).__TAURI_INTERNALS__) {
538
- const API_BASE = 'http://localhost:3000/api'
508
+ if (!isTauriRuntime()) {
509
+ const API_BASE = getLocalApiBase()
539
510
  const res = await fetch(`${API_BASE}/image-generate`, {
540
511
  method: 'POST',
541
512
  headers: { 'Content-Type': 'application/json' },
@@ -569,8 +540,8 @@ export async function generateImages(
569
540
 
570
541
  /** Fetch which image-generation providers are currently configured on the backend. */
571
542
  export async function getImageGenProviders(): Promise<ImageGenProviders> {
572
- if (typeof window === 'undefined' || !(window as any).__TAURI_INTERNALS__) {
573
- const API_BASE = 'http://localhost:3000/api'
543
+ if (!isTauriRuntime()) {
544
+ const API_BASE = getLocalApiBase()
574
545
  try {
575
546
  const res = await fetch(`${API_BASE}/image-gen/providers`)
576
547
  if (res.ok) return await res.json()
@@ -599,8 +570,8 @@ export async function getImageGenProviders(): Promise<ImageGenProviders> {
599
570
 
600
571
  /** Updates the default image generation provider in the configuration. */
601
572
  export async function setDefaultImageProvider(provider: string): Promise<boolean> {
602
- if (typeof window === 'undefined' || !(window as any).__TAURI_INTERNALS__) {
603
- const API_BASE = "http://localhost:3000/api";
573
+ if (!isTauriRuntime()) {
574
+ const API_BASE = getLocalApiBase();
604
575
  try {
605
576
  const getRes = await fetch(`${API_BASE}/config`)
606
577
  if (!getRes.ok) return false
@@ -647,6 +618,24 @@ export async function getWorkspaceTree(path?: string | null): Promise<WorkspaceT
647
618
  return invoke<WorkspaceTreeEntry>('get_workspace_tree', { path })
648
619
  }
649
620
 
621
+ export async function createWorkspaceFile(path: string): Promise<void> {
622
+ if (typeof window === 'undefined' || !(window as any).__TAURI_INTERNALS__) return
623
+ const { invoke } = await import('@tauri-apps/api/core')
624
+ return invoke('create_workspace_file', { path })
625
+ }
626
+
627
+ export async function createWorkspaceFolder(path: string): Promise<void> {
628
+ if (typeof window === 'undefined' || !(window as any).__TAURI_INTERNALS__) return
629
+ const { invoke } = await import('@tauri-apps/api/core')
630
+ return invoke('create_workspace_folder', { path })
631
+ }
632
+
633
+ export async function deleteWorkspaceItem(path: string): Promise<void> {
634
+ if (typeof window === 'undefined' || !(window as any).__TAURI_INTERNALS__) return
635
+ const { invoke } = await import('@tauri-apps/api/core')
636
+ return invoke('delete_workspace_item', { path })
637
+ }
638
+
650
639
  export async function selectWorkspaceDirectory(): Promise<string | null> {
651
640
  if (typeof window === 'undefined' || !(window as any).__TAURI_INTERNALS__) {
652
641
  return null
@@ -656,12 +645,12 @@ export async function selectWorkspaceDirectory(): Promise<string | null> {
656
645
  return selected?.trim() || null
657
646
  }
658
647
 
659
- export async function submitToolApproval(token: string, approved: boolean): Promise<void> {
648
+ export async function submitToolApproval(token: string, approved: boolean, answer?: string): Promise<void> {
660
649
  if (typeof window === 'undefined' || !(window as any).__TAURI_INTERNALS__) {
661
650
  return;
662
651
  }
663
652
  const { invoke } = await import('@tauri-apps/api/core')
664
- return invoke('submit_tool_approval', { token, approved })
653
+ return invoke('submit_tool_approval', { token, approved, answer })
665
654
  }
666
655
 
667
656
  export async function proposeCodeEdits(root: string, edits: CodeEdit[]): Promise<CodeEditProposal> {
@@ -701,9 +690,9 @@ export function convertFileSrc(filePath: string, protocol = 'asset'): string {
701
690
  }
702
691
 
703
692
  export function installTauriAdapters() {
704
- if (typeof window === 'undefined' || !(window as any).__TAURI_INTERNALS__) {
705
- console.warn("Not running inside Tauri. Connecting to local API server fallback at http://localhost:3000/api.");
706
- const API_BASE = "http://localhost:3000/api";
693
+ if (!isTauriRuntime()) {
694
+ const API_BASE = getLocalApiBase();
695
+ console.warn(`Not running inside Tauri. Connecting to local API server fallback at ${API_BASE}.`);
707
696
 
708
697
  (window as any).settingsApi = {
709
698
  getSettings: async () => {
@@ -1239,3 +1228,40 @@ export async function readClipboardImage(): Promise<string | null> {
1239
1228
  return null
1240
1229
  }
1241
1230
  }
1231
+
1232
+ // Enforce compile-time check against the shared platform interface
1233
+ const _apiCheck: MintPlatformApi = {
1234
+ getRuntimeStatus,
1235
+ detectSystemTools,
1236
+ sendChatMessage,
1237
+ streamChatMessage,
1238
+ getTtsUrls,
1239
+ cancelChatMessage,
1240
+ getRecentInteractions,
1241
+ saveSystemInteraction,
1242
+ saveInteractionAgentActivity,
1243
+ listChatSessions,
1244
+ deleteChatSession,
1245
+ renameChatSession,
1246
+ getProfileValue,
1247
+ setProfileValue,
1248
+ listLearnedSkills,
1249
+ addLearnedSkill,
1250
+ deleteLearnedSkill,
1251
+ clearChatHistory,
1252
+ listSavedPictures,
1253
+ generateImages,
1254
+ getImageGenProviders,
1255
+ setDefaultImageProvider,
1256
+ getWorkspaceTree,
1257
+ createWorkspaceFile,
1258
+ createWorkspaceFolder,
1259
+ deleteWorkspaceItem,
1260
+ selectWorkspaceDirectory,
1261
+ submitToolApproval,
1262
+ proposeCodeEdits,
1263
+ applyCodeEdits,
1264
+ listen,
1265
+ readClipboardImage,
1266
+ }
1267
+
@@ -1,39 +1,11 @@
1
- import type { AgentProgress, InteractionMemory } from './tauri'
2
-
3
- export function thoughtsFrom(progress: AgentProgress[]): string[] {
4
- return progress
5
- .filter((event) => event.type === 'Thought')
6
- .map((event) => (event as Extract<AgentProgress, { type: 'Thought' }>).data.thought)
7
- .filter(Boolean)
8
- }
9
-
10
- export function trimAgentProgress(progress: AgentProgress[], maxNonThought = 20): AgentProgress[] {
11
- if (progress.length === 0) return progress
12
- const keptNonThought = new Set(
13
- progress.filter((event) => event.type !== 'Thought').slice(-maxNonThought),
14
- )
15
- return progress.filter((event) => event.type === 'Thought' || keptNonThought.has(event))
16
- }
17
-
18
- export function hasAgentToolActivity(progress: AgentProgress[]): boolean {
19
- return progress.some((event) => event.type === 'ToolStart' || event.type === 'ToolEnd')
20
- }
21
-
22
- export function mergeActivitySnapshots(
23
- current: Record<string, AgentProgress[]>,
24
- interactions: InteractionMemory[],
25
- ): Record<string, AgentProgress[]> {
26
- const merged = { ...current }
27
- for (const interaction of interactions) {
28
- const key = String(interaction.id)
29
- if (interaction.agentActivity?.length && !merged[key]) {
30
- merged[key] = interaction.agentActivity
31
- }
32
- }
33
- return merged
34
- }
35
-
36
- export function parseAgentActivity(value: unknown): AgentProgress[] | undefined {
37
- if (!Array.isArray(value)) return undefined
38
- return value as AgentProgress[]
39
- }
1
+ // Re-export everything from the shared module — do NOT add logic here.
2
+ // Edit src/renderer/shared/agentProgress.ts instead.
3
+ export type { AgentProgress, InteractionMemory } from '../shared/agentProgress'
4
+ export {
5
+ thoughtsFrom,
6
+ trimAgentProgress,
7
+ hasAgentToolActivity,
8
+ mergeActivitySnapshots,
9
+ parseAgentActivity,
10
+ parseFileChangesFromProgress,
11
+ } from '../shared/agentProgress'
@@ -1,47 +1,4 @@
1
- export function evaluateArithmetic(source: string): number {
2
- const tokens = source.replace(/\s+/g, '')
3
- let index = 0
1
+ // Re-export from the shared module — do NOT add logic here.
2
+ // Edit src/renderer/shared/calculator.ts instead.
3
+ export { evaluateArithmetic } from '../shared/calculator'
4
4
 
5
- function parseExpression(): number {
6
- let value = parseTerm()
7
- while (tokens[index] === '+' || tokens[index] === '-') {
8
- const operator = tokens[index++]
9
- const right = parseTerm()
10
- value = operator === '+' ? value + right : value - right
11
- }
12
- return value
13
- }
14
-
15
- function parseTerm(): number {
16
- let value = parseFactor()
17
- while (tokens[index] === '*' || tokens[index] === '/') {
18
- const operator = tokens[index++]
19
- const right = parseFactor()
20
- value = operator === '*' ? value * right : value / right
21
- }
22
- return value
23
- }
24
-
25
- function parseFactor(): number {
26
- if (tokens[index] === '+' || tokens[index] === '-') {
27
- return tokens[index++] === '-' ? -parseFactor() : parseFactor()
28
- }
29
- if (tokens[index] === '(') {
30
- index += 1
31
- const value = parseExpression()
32
- if (tokens[index++] !== ')') throw new Error('missing closing parenthesis')
33
- return value
34
- }
35
- const match = tokens.slice(index).match(/^(?:\d+(?:\.\d*)?|\.\d+)/)
36
- if (!match) throw new Error('expected a number')
37
- index += match[0].length
38
- return Number(match[0])
39
- }
40
-
41
- if (!tokens) throw new Error('expression is empty')
42
- const result = parseExpression()
43
- if (index !== tokens.length || !Number.isFinite(result)) {
44
- throw new Error('invalid arithmetic expression')
45
- }
46
- return result
47
- }