@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.
- package/Cargo.lock +3 -3
- package/Cargo.toml +1 -1
- package/README.md +0 -17
- package/Release_Note.md +137 -58
- package/bin/mint +0 -0
- package/crates/mint-cli/src/agent.rs +150 -45
- package/crates/mint-cli/src/image.rs +8 -8
- package/crates/mint-cli/src/main.rs +929 -412
- package/crates/mint-cli/src/onboard.rs +108 -108
- package/crates/mint-cli/src/setup.rs +143 -143
- package/crates/mint-core/src/agent_loop.rs +1 -0
- package/crates/mint-core/src/api_server.rs +108 -52
- package/crates/mint-core/src/browser.rs +56 -1
- package/crates/mint-core/src/channels.rs +1 -0
- package/crates/mint-core/src/chat.rs +126 -8
- package/crates/mint-core/src/code_tools.rs +18 -20
- package/crates/mint-core/src/config.rs +245 -17
- package/crates/mint-core/src/image_gen.rs +5 -5
- package/crates/mint-core/src/lib.rs +16 -2
- package/crates/mint-core/src/memory.rs +6 -1
- package/crates/mint-core/src/orchestration.rs +260 -72
- package/crates/mint-core/src/pictures.rs +3 -4
- package/crates/mint-core/src/semantic.rs +1 -1
- package/crates/mint-core/src/shell.rs +1 -1
- package/crates/mint-core/src/skills.rs +46 -19
- package/crates/mint-core/src/tts.rs +3 -1
- package/flatpak/com.pheem49.mint.desktop +9 -0
- package/flatpak/com.pheem49.mint.metainfo.xml +35 -0
- package/flatpak/com.pheem49.mint.yaml +52 -0
- package/package.json +1 -1
- package/shared-modules/.github/workflows/ci.yml +120 -0
- package/shared-modules/.github/workflows/update.yaml +28 -0
- package/shared-modules/CODEOWNERS +23 -0
- package/shared-modules/README.md +79 -0
- package/shared-modules/SDL/README.md +3 -0
- package/shared-modules/SDL/SDL-1.2.15.json +51 -0
- package/shared-modules/SDL/SDL_Pango-0.1.2-API-adds.patch +118 -0
- package/shared-modules/SDL/SDL_image-1.2.12.json +25 -0
- package/shared-modules/SDL/SDL_mixer-1.2.12.json +27 -0
- package/shared-modules/SDL/SDL_net-1.2.8.json +19 -0
- package/shared-modules/SDL/SDL_pango-0.1.2.json +26 -0
- package/shared-modules/SDL/SDL_ttf-2.0.11.json +28 -0
- package/shared-modules/SDL/libsdl-1.2.15-strict-prototypes.patch +316 -0
- package/shared-modules/SDL/sdl-check-for-SDL_VIDEO_X11_BACKINGSTORE.patch +24 -0
- package/shared-modules/SDL/sdl-libx11-build.patch +59 -0
- package/shared-modules/SDL/sdl-sysloadso-buffer-length.patch +27 -0
- package/shared-modules/SDL/sdl12-compat.json +22 -0
- package/shared-modules/SDL2/SDL2_image.json +27 -0
- package/shared-modules/SDL2/SDL2_ttf.json +29 -0
- package/shared-modules/cld2/CMakeLists.txt +157 -0
- package/shared-modules/cld2/cld2.json +24 -0
- package/shared-modules/clutter/clutter.json +72 -0
- package/shared-modules/dbus-glib/dbus-glib.json +29 -0
- package/shared-modules/glew/glew.json +36 -0
- package/shared-modules/glu/glu-9.json +12 -0
- package/shared-modules/gtk2/arc-gtk2-theme-Replace-Inkscape-dependency-with-rsvg-convert.patch +180 -0
- package/shared-modules/gtk2/gtk2-common-themes.json +360 -0
- package/shared-modules/gtk2/gtk2-fix-crash-in-show-uri.patch +30 -0
- package/shared-modules/gtk2/gtk2-gcc14.patch +281 -0
- package/shared-modules/gtk2/gtk2-gcc15.patch +250 -0
- package/shared-modules/gtk2/gtk2-murrine-c99.patch +34 -0
- package/shared-modules/gtk2/gtk2-use-adwaita-theme.patch +80 -0
- package/shared-modules/gtk2/gtk2.json +120 -0
- package/shared-modules/gtk2/ibus-gcc15.patch +13 -0
- package/shared-modules/gtk2/murrine-engine-fix-crash.patch +12 -0
- package/shared-modules/gtk2/readme.md +10 -0
- package/shared-modules/intltool/intltool-0.51.json +20 -0
- package/shared-modules/intltool/intltool-perl5.26-regex-fixes.patch +59 -0
- package/shared-modules/lame/0001-Build-xmm_quantize_sub.c-with-msse.patch +34 -0
- package/shared-modules/lame/0002-Include-GTK-1-autoconf-directives-in-build-system.patch +222 -0
- package/shared-modules/lame/0003-Patch-out-remaining-ansi2knr.patch +70 -0
- package/shared-modules/lame/0004-initscr-is-not-used-anywhere-in-lame-sourcetree.patch +41 -0
- package/shared-modules/lame/0005-Update-autoconf.patch +81 -0
- package/shared-modules/lame/lame-3.99.5.json +32 -0
- package/shared-modules/libappindicator/libappindicator-gtk2-12.10.json +140 -0
- package/shared-modules/libappindicator/libappindicator-gtk3-12.10.json +140 -0
- package/shared-modules/libappindicator/libappindicator-gtk3-introspection-12.10.json +140 -0
- package/shared-modules/libappindicator/patches/0001-libappindicator-ftbfs.patch +20 -0
- package/shared-modules/libappindicator/patches/0002-libappindicator-no-python.patch +888 -0
- package/shared-modules/libappindicator/patches/0003-libappindicator-fix-crash-from-incorrect-signal-emission.patch +74 -0
- package/shared-modules/libappindicator/patches/0004-libappindicator-fix-crash-iterating-icon-themes.patch +11 -0
- package/shared-modules/libayatana-appindicator/0001-Fix-HAVE_VALGRIND-AM_CONDITIONAL.patch +51 -0
- package/shared-modules/libayatana-appindicator/0001-Make-introspection-configurable.patch +44 -0
- package/shared-modules/libayatana-appindicator/libayatana-appindicator-gtk3.json +121 -0
- package/shared-modules/libcanberra/0001-gtk-Don-t-assume-all-GdkDisplays-are-GdkX11Displays-.patch +123 -0
- package/shared-modules/libcanberra/libcanberra.json +44 -0
- package/shared-modules/libmad/libmad.json +19 -0
- package/shared-modules/libsecret/libsecret.json +23 -0
- package/shared-modules/libsoup/libsoup-2.4.json +14 -0
- package/shared-modules/libusb/libusb.json +27 -0
- package/shared-modules/linux-audio/dssi.json +31 -0
- package/shared-modules/linux-audio/fftw3f-static.json +44 -0
- package/shared-modules/linux-audio/fftw3f.json +42 -0
- package/shared-modules/linux-audio/fluidsynth2-static.json +36 -0
- package/shared-modules/linux-audio/fluidsynth2.json +25 -0
- package/shared-modules/linux-audio/ladspa.json +26 -0
- package/shared-modules/linux-audio/lash.json +35 -0
- package/shared-modules/linux-audio/libinstpatch.json +16 -0
- package/shared-modules/linux-audio/liblo-static.json +22 -0
- package/shared-modules/linux-audio/liblo.json +17 -0
- package/shared-modules/linux-audio/lilv-static.json +94 -0
- package/shared-modules/linux-audio/lilv.json +89 -0
- package/shared-modules/linux-audio/lrdf.json +57 -0
- package/shared-modules/linux-audio/lv2.json +20 -0
- package/shared-modules/linux-audio/patches/lash/0001-configure-remove-aclocal-m4.patch +29 -0
- package/shared-modules/linux-audio/patches/lash/0002-Fix-build-with-recent-gcc.patch +73 -0
- package/shared-modules/linux-audio/patches/lash/0003-Fix-linking.patch +136 -0
- package/shared-modules/linux-audio/patches/raptor-libxml.patch +31 -0
- package/shared-modules/linux-audio/patches/stk-install.sh +54 -0
- package/shared-modules/linux-audio/stk.json +28 -0
- package/shared-modules/lua5.1/0004-Fix-stack-overflow-in-vararg-functions.patch +21 -0
- package/shared-modules/lua5.1/lua-5.1.5-so.patch +43 -0
- package/shared-modules/lua5.1/lua-5.1.5.json +42 -0
- package/shared-modules/lua5.3/lua-5.3.5.json +41 -0
- package/shared-modules/lua5.3/lua.pc.in +8 -0
- package/shared-modules/lua5.3/luaconf.patch +47 -0
- package/shared-modules/lua5.3/makefile-add-liblua.so-rule.patch +47 -0
- package/shared-modules/lua5.4/lua-5.4.json +41 -0
- package/shared-modules/lua5.4/lua.pc.in +8 -0
- package/shared-modules/lua5.4/luaconf.patch +34 -0
- package/shared-modules/lua5.4/makefile-add-liblua.so-rule.patch +42 -0
- package/shared-modules/luajit/luajit.json +30 -0
- package/shared-modules/lzo/lzo.json +27 -0
- package/shared-modules/mac/mac.json +18 -0
- package/shared-modules/physfs/physfs.json +26 -0
- package/shared-modules/pygame/audiofile-gcc6.patch +21 -0
- package/shared-modules/pygame/cython-fixes.patch +34 -0
- package/shared-modules/pygame/fluidsynth-no-rawmidi.patch +69 -0
- package/shared-modules/pygame/glib.patch +25 -0
- package/shared-modules/pygame/pygame1-python2.json +157 -0
- package/shared-modules/pygtk/pygtk-c99.patch +15 -0
- package/shared-modules/pygtk/pygtk.json +117 -0
- package/shared-modules/pygtk/pygtk.patch +39 -0
- package/shared-modules/python2.7/python-2.7.json +48 -0
- package/shared-modules/smpeg/smpeg-0.4.5.json +50 -0
- package/shared-modules/smpeg/smpeg-am-prog-as.patch +12 -0
- package/shared-modules/smpeg/smpeg-export-mpegaudio-class.patch +17 -0
- package/shared-modules/smpeg/smpeg-gcc6.patch +40 -0
- package/shared-modules/smpeg/smpeg-no-gtk.patch +37 -0
- package/shared-modules/squashfs-tools/squashfs-tools.json +25 -0
- package/shared-modules/update-checker.sh +50 -0
- package/shared-modules/vorbisgain/0001-temp_files.patch +75 -0
- package/shared-modules/vorbisgain/0002-errno.patch +33 -0
- package/shared-modules/vorbisgain/0003-manpage.patch +248 -0
- package/shared-modules/vorbisgain/0004-vorbisgain_mtime.patch +183 -0
- package/shared-modules/vorbisgain/0005-double_fclose.patch +21 -0
- package/shared-modules/vorbisgain/0006-manpage_hyphens.patch +56 -0
- package/shared-modules/vorbisgain/0007-recursively_spelling.patch +14 -0
- package/shared-modules/vorbisgain/0008-manpage_recursion_mistake.patch +23 -0
- package/shared-modules/vorbisgain/0009-hardening.patch +13 -0
- package/shared-modules/vorbisgain/0010-fclose.patch +13 -0
- package/shared-modules/vorbisgain/README.md +23 -0
- package/shared-modules/vorbisgain/vorbisgain_0.37-2.json +29 -0
- package/src/renderer/shared/agentProgress.ts +145 -0
- package/src/renderer/shared/calculator.ts +52 -0
- package/src/renderer/shared/components/AgentActivityDrawer.tsx +72 -0
- package/src/renderer/shared/components/AgentActivityTable.tsx +85 -0
- package/src/renderer/shared/components/ApprovalCard.tsx +93 -0
- package/src/renderer/shared/components/ChatCodeBlock.tsx +75 -0
- package/src/renderer/shared/components/ThinkingBlock.tsx +101 -0
- package/src/renderer/shared/constants/models.ts +91 -0
- package/src/renderer/shared/platform.ts +82 -0
- package/src/renderer/shared/types.ts +140 -0
- package/src/renderer/shared/utils/agentActivity.ts +187 -0
- package/src/renderer/shared/utils/approval.ts +1 -0
- package/src/renderer/shared/utils/approval.tsx +64 -0
- package/src/renderer/shared/utils/fileIcons.ts +135 -0
- package/src/renderer/shared/utils/markdown.tsx +347 -0
- package/src/renderer/shared/utils/providers.ts +36 -0
- package/src/renderer/shared/utils/speech.ts +216 -0
- package/src/renderer/shared/utils/ui.ts +147 -0
- package/src/renderer/src/agentProgress.ts +11 -39
- package/src/renderer/src/calculator.ts +3 -46
- package/src/renderer/src/components/ChatPanel.tsx +219 -1308
- package/src/renderer/src/components/MintDashboard.tsx +67 -170
- package/src/renderer/src/components/PicturesLibrary.tsx +32 -8
- package/src/renderer/src/components/Settings/AgentsTab.tsx +381 -0
- package/src/renderer/src/components/Settings/AutomationTab.tsx +0 -22
- package/src/renderer/src/components/Settings/GeneralTab.tsx +256 -2
- package/src/renderer/src/components/SettingsWindow.tsx +120 -3
- package/src/renderer/src/components/WorkspacePanel.tsx +7 -125
- package/src/renderer/src/css/styles.css +100 -0
- package/src/renderer/src/tauri.ts +128 -143
- package/src/renderer/src-web/agentProgress.ts +11 -39
- package/src/renderer/src-web/calculator.ts +3 -46
- package/src/renderer/src-web/components/ChatPanel.tsx +219 -1306
- package/src/renderer/src-web/components/MintDashboard.tsx +42 -170
- package/src/renderer/src-web/components/PicturesLibrary.tsx +23 -29
- package/src/renderer/src-web/components/Settings/AgentsTab.tsx +381 -0
- package/src/renderer/src-web/components/Settings/AutomationTab.tsx +0 -22
- package/src/renderer/src-web/components/Settings/GeneralTab.tsx +254 -3
- package/src/renderer/src-web/components/SettingsWindow.tsx +127 -3
- package/src/renderer/src-web/css/styles.css +100 -0
- package/src/renderer/src-web/tauri.ts +180 -102
- package/src-tauri/src/desktop.rs +18 -16
- package/src-tauri/src/discord_rpc.rs +1 -1
- package/src-tauri/src/lib.rs +168 -40
- package/src-tauri/src/webhooks.rs +1 -0
- package/src-tauri/src/workflows.rs +1 -0
- package/src-tauri/tauri.conf.json +1 -1
|
@@ -71,15 +71,14 @@ pub async fn resolve_github_links(message: &str, config: &MintConfig) -> String
|
|
|
71
71
|
.collect();
|
|
72
72
|
|
|
73
73
|
let repo_key = format!("{owner}/{repo_clean}");
|
|
74
|
-
if resolved_repos.insert(repo_key.clone())
|
|
75
|
-
|
|
74
|
+
if resolved_repos.insert(repo_key.clone())
|
|
75
|
+
&& let Ok(summary) =
|
|
76
76
|
crate::code_tools::fetch_github_repo_summary(owner, &repo_clean).await
|
|
77
|
-
|
|
78
|
-
|
|
77
|
+
{
|
|
78
|
+
resolved_msg.push_str(&format!(
|
|
79
79
|
"\n\n--- Auto-Resolved GitHub Metadata for {} ---\n{}\n--------------------------------------------",
|
|
80
80
|
repo_key, summary
|
|
81
81
|
));
|
|
82
|
-
}
|
|
83
82
|
}
|
|
84
83
|
}
|
|
85
84
|
}
|
|
@@ -93,7 +92,7 @@ pub async fn orchestrate_chat(
|
|
|
93
92
|
let mut resolved_request = request.clone();
|
|
94
93
|
resolved_request.message = resolve_github_links(&request.message, config).await;
|
|
95
94
|
let memory = MemoryStore::open_default()?;
|
|
96
|
-
let enriched = enrich_request(&memory, &resolved_request)?;
|
|
95
|
+
let enriched = enrich_request(config, &memory, &resolved_request)?;
|
|
97
96
|
let response = send_chat(config, &enriched).await?;
|
|
98
97
|
memory.add_interaction_for_chat_with_fallback(
|
|
99
98
|
request_chat_id(request),
|
|
@@ -122,7 +121,7 @@ where
|
|
|
122
121
|
let mut resolved_request = request.clone();
|
|
123
122
|
resolved_request.message = resolve_github_links(&request.message, config).await;
|
|
124
123
|
let memory = MemoryStore::open_default()?;
|
|
125
|
-
let enriched = enrich_request(&memory, &resolved_request)?;
|
|
124
|
+
let enriched = enrich_request(config, &memory, &resolved_request)?;
|
|
126
125
|
let response = stream_chat(config, &enriched, on_chunk).await?;
|
|
127
126
|
memory.add_interaction_for_chat_with_fallback(
|
|
128
127
|
request_chat_id(request),
|
|
@@ -147,7 +146,7 @@ pub async fn orchestrate_chat_with_fallback(
|
|
|
147
146
|
let mut resolved_request = request.clone();
|
|
148
147
|
resolved_request.message = resolve_github_links(&request.message, config).await;
|
|
149
148
|
let memory = MemoryStore::open_default()?;
|
|
150
|
-
let enriched = enrich_request(&memory, &resolved_request)?;
|
|
149
|
+
let enriched = enrich_request(config, &memory, &resolved_request)?;
|
|
151
150
|
let (response, fallback) = send_chat_with_fallback(config, &enriched).await?;
|
|
152
151
|
memory.add_interaction_for_chat_with_fallback(
|
|
153
152
|
request_chat_id(request),
|
|
@@ -176,7 +175,7 @@ where
|
|
|
176
175
|
let mut resolved_request = request.clone();
|
|
177
176
|
resolved_request.message = resolve_github_links(&request.message, config).await;
|
|
178
177
|
let memory = MemoryStore::open_default()?;
|
|
179
|
-
let enriched = enrich_request(&memory, &resolved_request)?;
|
|
178
|
+
let enriched = enrich_request(config, &memory, &resolved_request)?;
|
|
180
179
|
let (response, fallback) = stream_chat_with_fallback(config, &enriched, on_chunk).await?;
|
|
181
180
|
memory.add_interaction_for_chat_with_fallback(
|
|
182
181
|
request_chat_id(request),
|
|
@@ -194,7 +193,11 @@ where
|
|
|
194
193
|
Ok((response, fallback))
|
|
195
194
|
}
|
|
196
195
|
|
|
197
|
-
fn enrich_request(
|
|
196
|
+
fn enrich_request(
|
|
197
|
+
config: &MintConfig,
|
|
198
|
+
memory: &MemoryStore,
|
|
199
|
+
request: &ChatRequest,
|
|
200
|
+
) -> Result<ChatRequest, MemoryError> {
|
|
198
201
|
let mut interactions =
|
|
199
202
|
memory.recent_interactions_for_chat(request_chat_id(request), CONTEXT_LIMIT)?;
|
|
200
203
|
interactions.reverse();
|
|
@@ -206,18 +209,18 @@ fn enrich_request(memory: &MemoryStore, request: &ChatRequest) -> Result<ChatReq
|
|
|
206
209
|
let mut enriched = request.clone();
|
|
207
210
|
|
|
208
211
|
let mut profile_instructions = String::new();
|
|
209
|
-
if let Ok(Some(name)) = memory.get_profile("name")
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
}
|
|
212
|
+
if let Ok(Some(name)) = memory.get_profile("name")
|
|
213
|
+
&& !name.trim().is_empty()
|
|
214
|
+
{
|
|
215
|
+
profile_instructions.push_str(&format!("User Name: {}\n", name.trim()));
|
|
213
216
|
}
|
|
214
|
-
if let Ok(Some(preferences)) = memory.get_profile("preferences")
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
)
|
|
220
|
-
|
|
217
|
+
if let Ok(Some(preferences)) = memory.get_profile("preferences")
|
|
218
|
+
&& !preferences.trim().is_empty()
|
|
219
|
+
{
|
|
220
|
+
profile_instructions.push_str(&format!(
|
|
221
|
+
"User Preferences & Profile:\n{}\n",
|
|
222
|
+
preferences.trim()
|
|
223
|
+
));
|
|
221
224
|
}
|
|
222
225
|
|
|
223
226
|
if !profile_instructions.is_empty() {
|
|
@@ -230,6 +233,16 @@ fn enrich_request(memory: &MemoryStore, request: &ChatRequest) -> Result<ChatReq
|
|
|
230
233
|
.to_owned();
|
|
231
234
|
}
|
|
232
235
|
|
|
236
|
+
// Inject active AI model/provider context to system instructions
|
|
237
|
+
let active_model_info = format!(
|
|
238
|
+
"\n\n[Active Environment Context]\n\
|
|
239
|
+
You are running on: {}\n\
|
|
240
|
+
Using AI Model: {}\n",
|
|
241
|
+
config.ai_provider,
|
|
242
|
+
config.active_model()
|
|
243
|
+
);
|
|
244
|
+
enriched.system_instruction.push_str(&active_model_info);
|
|
245
|
+
|
|
233
246
|
if !transcript.is_empty() {
|
|
234
247
|
enriched.system_instruction = format!(
|
|
235
248
|
"{}\n\nRecent conversation context:\n{}",
|
|
@@ -549,6 +562,10 @@ pub enum ApprovalOutcome {
|
|
|
549
562
|
pub enum AgentProgress {
|
|
550
563
|
Thinking {
|
|
551
564
|
elapsed_secs: u64,
|
|
565
|
+
#[serde(skip_serializing_if = "Option::is_none")]
|
|
566
|
+
agent_name: Option<String>,
|
|
567
|
+
#[serde(skip_serializing_if = "Option::is_none")]
|
|
568
|
+
model_name: Option<String>,
|
|
552
569
|
},
|
|
553
570
|
Thought {
|
|
554
571
|
thought: String,
|
|
@@ -658,12 +675,105 @@ where
|
|
|
658
675
|
Ok(Option::<AgentInput>::deserialize(deserializer)?.unwrap_or_default())
|
|
659
676
|
}
|
|
660
677
|
|
|
678
|
+
fn resolve_agent_config(
|
|
679
|
+
config: &MintConfig,
|
|
680
|
+
agent_id: Option<&str>,
|
|
681
|
+
trajectory: &[String],
|
|
682
|
+
) -> (MintConfig, String, Option<String>, Option<String>) {
|
|
683
|
+
if !config.enable_agent_collaboration {
|
|
684
|
+
return (config.clone(), "".to_string(), None, None);
|
|
685
|
+
}
|
|
686
|
+
|
|
687
|
+
let enabled_agents: Vec<&crate::config::AgentConfig> =
|
|
688
|
+
config.agents.iter().filter(|a| a.enabled).collect();
|
|
689
|
+
if enabled_agents.is_empty() {
|
|
690
|
+
return (config.clone(), "".to_string(), None, None);
|
|
691
|
+
}
|
|
692
|
+
|
|
693
|
+
let active_agent = if let Some(id) = agent_id {
|
|
694
|
+
enabled_agents.iter().find(|a| a.id == id).copied()
|
|
695
|
+
} else {
|
|
696
|
+
// Multi-Agent Pipeline Collaboration (Planner -> Coder -> Reviewer)
|
|
697
|
+
let plan_created = trajectory
|
|
698
|
+
.iter()
|
|
699
|
+
.any(|step| step.contains("- Action: create_plan"));
|
|
700
|
+
if !plan_created {
|
|
701
|
+
if let Some(planner) = enabled_agents.iter().find(|a| a.id == "planner") {
|
|
702
|
+
Some(*planner)
|
|
703
|
+
} else {
|
|
704
|
+
enabled_agents.iter().find(|a| a.id == "coder").copied()
|
|
705
|
+
}
|
|
706
|
+
} else {
|
|
707
|
+
// Check if edits have been made to verify
|
|
708
|
+
let edits_made = trajectory.iter().any(|step| {
|
|
709
|
+
step.contains("- Action: write_file") || step.contains("- Action: apply_patch")
|
|
710
|
+
});
|
|
711
|
+
if edits_made {
|
|
712
|
+
if let Some(last_step) = trajectory.last() {
|
|
713
|
+
if last_step.contains("- Action: write_file")
|
|
714
|
+
|| last_step.contains("- Action: apply_patch")
|
|
715
|
+
{
|
|
716
|
+
if let Some(reviewer) = enabled_agents.iter().find(|a| a.id == "reviewer") {
|
|
717
|
+
Some(*reviewer)
|
|
718
|
+
} else {
|
|
719
|
+
enabled_agents.iter().find(|a| a.id == "coder").copied()
|
|
720
|
+
}
|
|
721
|
+
} else {
|
|
722
|
+
enabled_agents.iter().find(|a| a.id == "coder").copied()
|
|
723
|
+
}
|
|
724
|
+
} else {
|
|
725
|
+
enabled_agents.iter().find(|a| a.id == "coder").copied()
|
|
726
|
+
}
|
|
727
|
+
} else {
|
|
728
|
+
enabled_agents.iter().find(|a| a.id == "coder").copied()
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
};
|
|
732
|
+
|
|
733
|
+
let Some(agent) = active_agent else {
|
|
734
|
+
return (config.clone(), "".to_string(), None, None);
|
|
735
|
+
};
|
|
736
|
+
|
|
737
|
+
let mut cfg_clone = config.clone();
|
|
738
|
+
cfg_clone.ai_provider = agent.provider.clone();
|
|
739
|
+
cfg_clone.gemini_model = agent.model.clone();
|
|
740
|
+
cfg_clone.openai_model = agent.model.clone();
|
|
741
|
+
cfg_clone.anthropic_model = agent.model.clone();
|
|
742
|
+
cfg_clone.openrouter_model = agent.model.clone();
|
|
743
|
+
cfg_clone.deepseek_model = agent.model.clone();
|
|
744
|
+
cfg_clone.hf_model = agent.model.clone();
|
|
745
|
+
cfg_clone.local_model_name = agent.model.clone();
|
|
746
|
+
cfg_clone.ollama_model = agent.model.clone();
|
|
747
|
+
|
|
748
|
+
if let Some(key) = &agent.api_key
|
|
749
|
+
&& !key.trim().is_empty()
|
|
750
|
+
{
|
|
751
|
+
match agent.provider.as_str() {
|
|
752
|
+
"gemini" => cfg_clone.api_key = key.clone(),
|
|
753
|
+
"openai" => cfg_clone.openai_api_key = key.clone(),
|
|
754
|
+
"anthropic" => cfg_clone.anthropic_api_key = key.clone(),
|
|
755
|
+
"openrouter" => cfg_clone.openrouter_api_key = key.clone(),
|
|
756
|
+
"deepseek" => cfg_clone.deepseek_api_key = key.clone(),
|
|
757
|
+
"huggingface" => cfg_clone.hf_api_key = key.clone(),
|
|
758
|
+
_ => {}
|
|
759
|
+
}
|
|
760
|
+
}
|
|
761
|
+
|
|
762
|
+
(
|
|
763
|
+
cfg_clone,
|
|
764
|
+
agent.system_instruction.clone(),
|
|
765
|
+
Some(agent.name.clone()),
|
|
766
|
+
Some(agent.model.clone()),
|
|
767
|
+
)
|
|
768
|
+
}
|
|
769
|
+
|
|
661
770
|
pub async fn orchestrate_agent_loop<Approve, Progress, Chunk>(
|
|
662
771
|
config: &MintConfig,
|
|
663
772
|
task: &str,
|
|
664
773
|
root: &Path,
|
|
665
774
|
image_data_uri: Option<String>,
|
|
666
775
|
chat_id: Option<&str>,
|
|
776
|
+
agent_id: Option<&str>,
|
|
667
777
|
fast_mode: bool,
|
|
668
778
|
mut approve: Approve,
|
|
669
779
|
mut progress: Progress,
|
|
@@ -695,18 +805,18 @@ where
|
|
|
695
805
|
|
|
696
806
|
if let Ok(memory) = MemoryStore::open_default() {
|
|
697
807
|
let mut profile_instructions = String::new();
|
|
698
|
-
if let Ok(Some(name)) = memory.get_profile("name")
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
}
|
|
808
|
+
if let Ok(Some(name)) = memory.get_profile("name")
|
|
809
|
+
&& !name.trim().is_empty()
|
|
810
|
+
{
|
|
811
|
+
profile_instructions.push_str(&format!("User Name: {}\n", name.trim()));
|
|
702
812
|
}
|
|
703
|
-
if let Ok(Some(preferences)) = memory.get_profile("preferences")
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
)
|
|
709
|
-
|
|
813
|
+
if let Ok(Some(preferences)) = memory.get_profile("preferences")
|
|
814
|
+
&& !preferences.trim().is_empty()
|
|
815
|
+
{
|
|
816
|
+
profile_instructions.push_str(&format!(
|
|
817
|
+
"User Preferences & Profile:\n{}\n",
|
|
818
|
+
preferences.trim()
|
|
819
|
+
));
|
|
710
820
|
}
|
|
711
821
|
|
|
712
822
|
if !profile_instructions.is_empty() {
|
|
@@ -743,20 +853,42 @@ where
|
|
|
743
853
|
let mut trajectory: Vec<String> = Vec::new();
|
|
744
854
|
|
|
745
855
|
for step in 1..=MAX_STEPS {
|
|
856
|
+
let (active_config, agent_instruction, active_agent_name, active_model_name) =
|
|
857
|
+
resolve_agent_config(config, agent_id, &trajectory);
|
|
858
|
+
|
|
746
859
|
progress(AgentProgress::Thinking {
|
|
747
860
|
elapsed_secs: started_at.elapsed().as_secs(),
|
|
861
|
+
agent_name: active_agent_name,
|
|
862
|
+
model_name: active_model_name.clone(),
|
|
748
863
|
});
|
|
749
864
|
|
|
865
|
+
let mut active_system_prompt = system_prompt.clone();
|
|
866
|
+
if let Some(ref model_name) = active_model_name {
|
|
867
|
+
active_system_prompt.push_str(&format!(
|
|
868
|
+
"\n\n[Active Environment Context]\n\
|
|
869
|
+
You are running on: {}\n\
|
|
870
|
+
Using AI Model: {}\n",
|
|
871
|
+
active_config.ai_provider, model_name
|
|
872
|
+
));
|
|
873
|
+
}
|
|
874
|
+
if !agent_instruction.is_empty() {
|
|
875
|
+
active_system_prompt.push_str(&format!(
|
|
876
|
+
"\n\nYour Current Role & System Instructions:\n{}",
|
|
877
|
+
agent_instruction
|
|
878
|
+
));
|
|
879
|
+
}
|
|
880
|
+
|
|
750
881
|
let (response, fallback) = send_chat_with_fallback(
|
|
751
|
-
|
|
882
|
+
&active_config,
|
|
752
883
|
&ChatRequest {
|
|
753
884
|
message: observation.clone(),
|
|
754
|
-
system_instruction:
|
|
885
|
+
system_instruction: active_system_prompt.clone(),
|
|
755
886
|
chat_id: Some(chat_id.to_owned()),
|
|
756
887
|
image_data_uri: pending_image.take(),
|
|
757
888
|
audio_data_uri: None,
|
|
758
889
|
document_attachment: None,
|
|
759
890
|
workspace_path: None,
|
|
891
|
+
agent_id: None,
|
|
760
892
|
},
|
|
761
893
|
)
|
|
762
894
|
.await?;
|
|
@@ -771,7 +903,7 @@ where
|
|
|
771
903
|
Ok(decision) => decision,
|
|
772
904
|
Err(_) => {
|
|
773
905
|
let (repaired, _) = send_chat_with_fallback(
|
|
774
|
-
|
|
906
|
+
&active_config,
|
|
775
907
|
&ChatRequest {
|
|
776
908
|
message: format!(
|
|
777
909
|
"Your previous response was not valid Mint agent JSON.\n\
|
|
@@ -779,12 +911,13 @@ where
|
|
|
779
911
|
Do not use markdown.\n\nPrevious response:\n{}",
|
|
780
912
|
truncate(&response.text)
|
|
781
913
|
),
|
|
782
|
-
system_instruction:
|
|
914
|
+
system_instruction: active_system_prompt.clone(),
|
|
783
915
|
chat_id: Some(chat_id.to_owned()),
|
|
784
916
|
image_data_uri: None,
|
|
785
917
|
audio_data_uri: None,
|
|
786
918
|
document_attachment: None,
|
|
787
919
|
workspace_path: None,
|
|
920
|
+
agent_id: None,
|
|
788
921
|
},
|
|
789
922
|
)
|
|
790
923
|
.await?;
|
|
@@ -1115,7 +1248,7 @@ where
|
|
|
1115
1248
|
.collect::<Vec<_>>()
|
|
1116
1249
|
.join("\n");
|
|
1117
1250
|
Ok(format!(
|
|
1118
|
-
"{formatted}\n\nNote: Web search succeeded using {provider} Search. In your finish summary, you MUST
|
|
1251
|
+
"{formatted}\n\nNote: Web search succeeded using {provider} Search. In your finish summary, you MUST:\n1. Answer the user's question using the information above.\n2. Mention that you found this information via {provider} Search (e.g. \"I found this information using {provider} Search.\").\nDo NOT list sources manually — the UI will display them automatically."
|
|
1119
1252
|
))
|
|
1120
1253
|
}
|
|
1121
1254
|
}
|
|
@@ -1140,14 +1273,14 @@ where
|
|
|
1140
1273
|
if crate::is_browser_running(config).await {
|
|
1141
1274
|
let result = crate::browser::navigate(config, url)
|
|
1142
1275
|
.await
|
|
1143
|
-
.map_err(
|
|
1276
|
+
.map_err(OrchestrationError::Agent)?;
|
|
1144
1277
|
Ok(result)
|
|
1145
1278
|
} else {
|
|
1146
1279
|
let opened = if cfg!(target_os = "macos") {
|
|
1147
1280
|
std::process::Command::new("open").arg(url).spawn().is_ok()
|
|
1148
1281
|
} else if cfg!(target_os = "windows") {
|
|
1149
1282
|
std::process::Command::new("cmd")
|
|
1150
|
-
.args(
|
|
1283
|
+
.args(["/C", "start", url])
|
|
1151
1284
|
.spawn()
|
|
1152
1285
|
.is_ok()
|
|
1153
1286
|
} else {
|
|
@@ -1179,7 +1312,7 @@ where
|
|
|
1179
1312
|
};
|
|
1180
1313
|
let result = crate::browser::click(config, selector)
|
|
1181
1314
|
.await
|
|
1182
|
-
.map_err(
|
|
1315
|
+
.map_err(OrchestrationError::Agent)?;
|
|
1183
1316
|
Ok(result)
|
|
1184
1317
|
}
|
|
1185
1318
|
"browser_type" => {
|
|
@@ -1203,13 +1336,13 @@ where
|
|
|
1203
1336
|
};
|
|
1204
1337
|
let result = crate::browser::type_text(config, selector, text)
|
|
1205
1338
|
.await
|
|
1206
|
-
.map_err(
|
|
1339
|
+
.map_err(OrchestrationError::Agent)?;
|
|
1207
1340
|
Ok(result)
|
|
1208
1341
|
}
|
|
1209
1342
|
"browser_read" => {
|
|
1210
1343
|
let result = crate::browser::read_page_text(config)
|
|
1211
1344
|
.await
|
|
1212
|
-
.map_err(
|
|
1345
|
+
.map_err(OrchestrationError::Agent)?;
|
|
1213
1346
|
Ok(result)
|
|
1214
1347
|
}
|
|
1215
1348
|
"memory_recall" => {
|
|
@@ -1365,7 +1498,7 @@ where
|
|
|
1365
1498
|
path: file_name.to_owned(),
|
|
1366
1499
|
content: input.file_content.clone(),
|
|
1367
1500
|
})
|
|
1368
|
-
.map_err(
|
|
1501
|
+
.map_err(OrchestrationError::Agent)?;
|
|
1369
1502
|
|
|
1370
1503
|
match approved {
|
|
1371
1504
|
ApprovalOutcome::Approved => {
|
|
@@ -1388,7 +1521,7 @@ where
|
|
|
1388
1521
|
name: name.to_owned(),
|
|
1389
1522
|
instruction: instruction.to_owned(),
|
|
1390
1523
|
})
|
|
1391
|
-
.map_err(
|
|
1524
|
+
.map_err(OrchestrationError::Agent)?;
|
|
1392
1525
|
|
|
1393
1526
|
match approved {
|
|
1394
1527
|
ApprovalOutcome::Approved => Ok(execute_native_plugin(config, name, instruction)
|
|
@@ -1406,7 +1539,7 @@ where
|
|
|
1406
1539
|
tool: tool.to_owned(),
|
|
1407
1540
|
arguments: input.arguments.clone(),
|
|
1408
1541
|
})
|
|
1409
|
-
.map_err(
|
|
1542
|
+
.map_err(OrchestrationError::Agent)?;
|
|
1410
1543
|
|
|
1411
1544
|
match approved {
|
|
1412
1545
|
ApprovalOutcome::Approved => Ok(serde_json::to_string_pretty(
|
|
@@ -1427,7 +1560,7 @@ where
|
|
|
1427
1560
|
command: command.to_owned(),
|
|
1428
1561
|
mode,
|
|
1429
1562
|
})
|
|
1430
|
-
.map_err(
|
|
1563
|
+
.map_err(OrchestrationError::Agent)?;
|
|
1431
1564
|
|
|
1432
1565
|
match approved {
|
|
1433
1566
|
ApprovalOutcome::Approved => run_shell(root, config, command),
|
|
@@ -1472,7 +1605,7 @@ where
|
|
|
1472
1605
|
hunks: patch.hunks.clone(),
|
|
1473
1606
|
diff,
|
|
1474
1607
|
})
|
|
1475
|
-
.map_err(
|
|
1608
|
+
.map_err(OrchestrationError::Agent)?;
|
|
1476
1609
|
|
|
1477
1610
|
match approved {
|
|
1478
1611
|
ApprovalOutcome::Approved => {
|
|
@@ -1508,7 +1641,7 @@ where
|
|
|
1508
1641
|
content: input.file_content.clone(),
|
|
1509
1642
|
diff,
|
|
1510
1643
|
})
|
|
1511
|
-
.map_err(
|
|
1644
|
+
.map_err(OrchestrationError::Agent)?;
|
|
1512
1645
|
|
|
1513
1646
|
match approved {
|
|
1514
1647
|
ApprovalOutcome::Approved => {
|
|
@@ -1702,13 +1835,13 @@ fn view_image(path: &Path, config: &MintConfig) -> Result<String, OrchestrationE
|
|
|
1702
1835
|
}
|
|
1703
1836
|
let bytes = std::fs::read(&path)
|
|
1704
1837
|
.map_err(|e| OrchestrationError::Agent(format!("cannot read image: {e}")))?;
|
|
1705
|
-
|
|
1838
|
+
serde_json::to_string_pretty(&serde_json::json!({
|
|
1706
1839
|
"path": path,
|
|
1707
1840
|
"bytes": bytes.len(),
|
|
1708
1841
|
"mime": mime,
|
|
1709
1842
|
"dataUri": format!("data:{mime};base64,{}", BASE64_STANDARD.encode(bytes)),
|
|
1710
1843
|
}))
|
|
1711
|
-
.map_err(|e| OrchestrationError::Agent(e.to_string()))
|
|
1844
|
+
.map_err(|e| OrchestrationError::Agent(e.to_string()))
|
|
1712
1845
|
}
|
|
1713
1846
|
|
|
1714
1847
|
fn run_shell(
|
|
@@ -1724,14 +1857,13 @@ fn run_shell(
|
|
|
1724
1857
|
|
|
1725
1858
|
let mut hint = "";
|
|
1726
1859
|
let cmd_lower = command.to_lowercase();
|
|
1727
|
-
if output.success
|
|
1728
|
-
|
|
1860
|
+
if output.success
|
|
1861
|
+
&& (cmd_lower.contains("open")
|
|
1729
1862
|
|| cmd_lower.contains("launch")
|
|
1730
1863
|
|| cmd_lower.contains("chrome")
|
|
1731
|
-
|| cmd_lower.contains("firefox")
|
|
1732
|
-
|
|
1733
|
-
|
|
1734
|
-
}
|
|
1864
|
+
|| cmd_lower.contains("firefox"))
|
|
1865
|
+
{
|
|
1866
|
+
hint = "\nNote: Opening URLs, files, folders, or launching applications are background processes. Even if there are warnings or stdout/stderr outputs, since the command exited successfully with status 0, the operation has succeeded and you should now use the 'finish' action to inform the user.";
|
|
1735
1867
|
}
|
|
1736
1868
|
|
|
1737
1869
|
Ok(format!(
|
|
@@ -1776,8 +1908,11 @@ fn action_fingerprint(decision: &AgentDecision) -> String {
|
|
|
1776
1908
|
}
|
|
1777
1909
|
|
|
1778
1910
|
fn initial_observation(task: &str, root: &Path, skills: &str) -> String {
|
|
1911
|
+
let now = chrono::Local::now()
|
|
1912
|
+
.format("%Y-%m-%d %H:%M:%S %Z")
|
|
1913
|
+
.to_string();
|
|
1779
1914
|
let mut observation = format!(
|
|
1780
|
-
"
|
|
1915
|
+
"Current Time: {now}\nTask: {task}\nWorkspace: {}\nLearned skills:\n{}\n",
|
|
1781
1916
|
root.display(),
|
|
1782
1917
|
if skills.trim().is_empty() {
|
|
1783
1918
|
"(none)"
|
|
@@ -2127,6 +2262,7 @@ Example response:
|
|
|
2127
2262
|
audio_data_uri: None,
|
|
2128
2263
|
document_attachment: None,
|
|
2129
2264
|
workspace_path: None,
|
|
2265
|
+
agent_id: None,
|
|
2130
2266
|
};
|
|
2131
2267
|
|
|
2132
2268
|
// Send the chat request to LLM
|
|
@@ -2148,19 +2284,19 @@ Example response:
|
|
|
2148
2284
|
text_reply.to_string()
|
|
2149
2285
|
};
|
|
2150
2286
|
|
|
2151
|
-
if let Ok(value) = serde_json::from_str::<serde_json::Value>(&clean_json)
|
|
2152
|
-
|
|
2153
|
-
|
|
2154
|
-
|
|
2155
|
-
|
|
2156
|
-
|
|
2157
|
-
|
|
2287
|
+
if let Ok(value) = serde_json::from_str::<serde_json::Value>(&clean_json)
|
|
2288
|
+
&& let Some(obj) = value.as_object()
|
|
2289
|
+
{
|
|
2290
|
+
if let Some(new_name) = obj.get("name").and_then(|v| v.as_str()) {
|
|
2291
|
+
let trimmed_name = new_name.trim();
|
|
2292
|
+
if !trimmed_name.is_empty() && trimmed_name != current_name {
|
|
2293
|
+
memory.set_profile("name", trimmed_name)?;
|
|
2158
2294
|
}
|
|
2159
|
-
|
|
2160
|
-
|
|
2161
|
-
|
|
2162
|
-
|
|
2163
|
-
|
|
2295
|
+
}
|
|
2296
|
+
if let Some(new_pref) = obj.get("preferences").and_then(|v| v.as_str()) {
|
|
2297
|
+
let trimmed_pref = new_pref.trim();
|
|
2298
|
+
if !trimmed_pref.is_empty() && trimmed_pref != current_pref {
|
|
2299
|
+
memory.set_profile("preferences", trimmed_pref)?;
|
|
2164
2300
|
}
|
|
2165
2301
|
}
|
|
2166
2302
|
}
|
|
@@ -2171,6 +2307,7 @@ Example response:
|
|
|
2171
2307
|
#[cfg(test)]
|
|
2172
2308
|
mod tests {
|
|
2173
2309
|
use super::*;
|
|
2310
|
+
use crate::config::AgentConfig;
|
|
2174
2311
|
|
|
2175
2312
|
#[test]
|
|
2176
2313
|
fn preserves_request_without_history() {
|
|
@@ -2185,10 +2322,14 @@ mod tests {
|
|
|
2185
2322
|
audio_data_uri: None,
|
|
2186
2323
|
document_attachment: None,
|
|
2187
2324
|
workspace_path: None,
|
|
2325
|
+
agent_id: None,
|
|
2188
2326
|
};
|
|
2189
|
-
|
|
2190
|
-
|
|
2191
|
-
|
|
2327
|
+
let config = MintConfig::default();
|
|
2328
|
+
assert!(
|
|
2329
|
+
enrich_request(&config, &store, &request)
|
|
2330
|
+
.unwrap()
|
|
2331
|
+
.system_instruction
|
|
2332
|
+
.starts_with("system")
|
|
2192
2333
|
);
|
|
2193
2334
|
}
|
|
2194
2335
|
|
|
@@ -2290,4 +2431,51 @@ mod tests {
|
|
|
2290
2431
|
|
|
2291
2432
|
assert_eq!(classification.mode.as_str(), "readOnly");
|
|
2292
2433
|
}
|
|
2434
|
+
|
|
2435
|
+
#[test]
|
|
2436
|
+
fn test_resolve_agent_config() {
|
|
2437
|
+
let mut config = MintConfig::default();
|
|
2438
|
+
config.enable_agent_collaboration = true;
|
|
2439
|
+
config.agents = vec![
|
|
2440
|
+
AgentConfig {
|
|
2441
|
+
id: "planner".into(),
|
|
2442
|
+
name: "Planner".into(),
|
|
2443
|
+
provider: "openai".into(),
|
|
2444
|
+
model: "gpt-4o".into(),
|
|
2445
|
+
api_key: Some("test-key".into()),
|
|
2446
|
+
system_instruction: "Planner instruction".into(),
|
|
2447
|
+
enabled: true,
|
|
2448
|
+
},
|
|
2449
|
+
AgentConfig {
|
|
2450
|
+
id: "coder".into(),
|
|
2451
|
+
name: "Coder".into(),
|
|
2452
|
+
provider: "gemini".into(),
|
|
2453
|
+
model: "gemini-2.5-flash".into(),
|
|
2454
|
+
api_key: None,
|
|
2455
|
+
system_instruction: "Coder instruction".into(),
|
|
2456
|
+
enabled: true,
|
|
2457
|
+
},
|
|
2458
|
+
];
|
|
2459
|
+
|
|
2460
|
+
let (cfg, instr, name, model) = resolve_agent_config(&config, Some("planner"), &[]);
|
|
2461
|
+
assert_eq!(cfg.ai_provider, "openai");
|
|
2462
|
+
assert_eq!(cfg.openai_model, "gpt-4o");
|
|
2463
|
+
assert_eq!(cfg.openai_api_key, "test-key");
|
|
2464
|
+
assert_eq!(instr, "Planner instruction");
|
|
2465
|
+
assert_eq!(name, Some("Planner".to_string()));
|
|
2466
|
+
assert_eq!(model, Some("gpt-4o".to_string()));
|
|
2467
|
+
|
|
2468
|
+
let (cfg, instr, name, _model) = resolve_agent_config(&config, None, &[]);
|
|
2469
|
+
assert_eq!(cfg.ai_provider, "openai");
|
|
2470
|
+
assert_eq!(instr, "Planner instruction");
|
|
2471
|
+
assert_eq!(name, Some("Planner".to_string()));
|
|
2472
|
+
|
|
2473
|
+
let trajectory =
|
|
2474
|
+
vec!["Step 1:\n- Action: create_plan\n- Observation: all planned".to_string()];
|
|
2475
|
+
let (cfg, instr, name, model) = resolve_agent_config(&config, None, &trajectory);
|
|
2476
|
+
assert_eq!(cfg.ai_provider, "gemini");
|
|
2477
|
+
assert_eq!(instr, "Coder instruction");
|
|
2478
|
+
assert_eq!(name, Some("Coder".to_string()));
|
|
2479
|
+
assert_eq!(model, Some("gemini-2.5-flash".to_string()));
|
|
2480
|
+
}
|
|
2293
2481
|
}
|
|
@@ -79,11 +79,10 @@ pub fn list_saved_pictures() -> Result<Vec<PictureEntry>, PictureError> {
|
|
|
79
79
|
.thumbnail_path
|
|
80
80
|
.as_ref()
|
|
81
81
|
.is_none_or(|thumbnail_path| !thumbnail_path.exists())
|
|
82
|
+
&& let Some(thumbnail_path) = create_thumbnail_from_file(&directory, &entry)
|
|
82
83
|
{
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
changed = true;
|
|
86
|
-
}
|
|
84
|
+
entry.thumbnail_path = Some(thumbnail_path);
|
|
85
|
+
changed = true;
|
|
87
86
|
}
|
|
88
87
|
entry.url = Some(file_url(&entry.path));
|
|
89
88
|
entry.thumbnail_url = entry.thumbnail_path.as_deref().map(file_url);
|
|
@@ -169,7 +169,7 @@ fn chunk_text(content: &str) -> Vec<(usize, usize, String)> {
|
|
|
169
169
|
while start < lines.len() {
|
|
170
170
|
let mut end = start;
|
|
171
171
|
let mut chars = 0;
|
|
172
|
-
while end < lines.len() && (end == start || chars + lines[end].len()
|
|
172
|
+
while end < lines.len() && (end == start || chars + lines[end].len() < MAX_CHARS) {
|
|
173
173
|
chars += lines[end].len() + 1;
|
|
174
174
|
end += 1;
|
|
175
175
|
}
|