@pheem49/mint 1.8.2 → 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.toml +1 -1
- package/Release_Note.md +48 -3
- package/crates/mint-cli/src/agent.rs +95 -84
- package/crates/mint-cli/src/image.rs +8 -8
- package/crates/mint-cli/src/main.rs +806 -403
- package/crates/mint-cli/src/onboard.rs +108 -108
- package/crates/mint-cli/src/setup.rs +143 -143
- package/crates/mint-core/src/api_server.rs +57 -54
- package/crates/mint-core/src/browser.rs +1 -1
- package/crates/mint-core/src/chat.rs +123 -8
- package/crates/mint-core/src/code_tools.rs +18 -20
- package/crates/mint-core/src/config.rs +184 -17
- package/crates/mint-core/src/image_gen.rs +5 -5
- package/crates/mint-core/src/lib.rs +5 -5
- package/crates/mint-core/src/memory.rs +6 -1
- package/crates/mint-core/src/orchestration.rs +104 -85
- 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/package.json +1 -1
- package/shared-modules/linux-audio/fluidsynth2-static.json +9 -3
- package/shared-modules/linux-audio/fluidsynth2.json +8 -2
- package/shared-modules/linux-audio/lilv.json +26 -19
- package/shared-modules/linux-audio/lv2.json +0 -1
- package/src/renderer/shared/components/AgentActivityTable.tsx +25 -6
- package/src/renderer/shared/utils/fileIcons.ts +135 -0
- package/src/renderer/src/components/ChatPanel.tsx +25 -5
- package/src/renderer/src/components/MintDashboard.tsx +45 -2
- package/src/renderer/src/components/Settings/AgentsTab.tsx +21 -2
- package/src/renderer/src/components/Settings/GeneralTab.tsx +256 -2
- package/src/renderer/src/components/SettingsWindow.tsx +68 -1
- package/src/renderer/src/components/WorkspacePanel.tsx +7 -125
- package/src/renderer/src/css/styles.css +16 -0
- package/src/renderer/src-web/components/ChatPanel.tsx +25 -5
- package/src/renderer/src-web/components/MintDashboard.tsx +19 -1
- package/src/renderer/src-web/components/Settings/AgentsTab.tsx +21 -2
- package/src/renderer/src-web/components/Settings/GeneralTab.tsx +254 -3
- package/src/renderer/src-web/components/SettingsWindow.tsx +75 -1
- package/src/renderer/src-web/css/styles.css +16 -0
- package/src-tauri/src/desktop.rs +18 -16
- package/src-tauri/src/discord_rpc.rs +1 -1
- package/src-tauri/src/lib.rs +41 -17
- 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
|
}
|
|
@@ -210,18 +209,18 @@ fn enrich_request(
|
|
|
210
209
|
let mut enriched = request.clone();
|
|
211
210
|
|
|
212
211
|
let mut profile_instructions = String::new();
|
|
213
|
-
if let Ok(Some(name)) = memory.get_profile("name")
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
}
|
|
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()));
|
|
217
216
|
}
|
|
218
|
-
if let Ok(Some(preferences)) = memory.get_profile("preferences")
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
)
|
|
224
|
-
|
|
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
|
+
));
|
|
225
224
|
}
|
|
226
225
|
|
|
227
226
|
if !profile_instructions.is_empty() {
|
|
@@ -239,8 +238,8 @@ fn enrich_request(
|
|
|
239
238
|
"\n\n[Active Environment Context]\n\
|
|
240
239
|
You are running on: {}\n\
|
|
241
240
|
Using AI Model: {}\n",
|
|
242
|
-
|
|
243
|
-
|
|
241
|
+
config.ai_provider,
|
|
242
|
+
config.active_model()
|
|
244
243
|
);
|
|
245
244
|
enriched.system_instruction.push_str(&active_model_info);
|
|
246
245
|
|
|
@@ -685,7 +684,8 @@ fn resolve_agent_config(
|
|
|
685
684
|
return (config.clone(), "".to_string(), None, None);
|
|
686
685
|
}
|
|
687
686
|
|
|
688
|
-
let enabled_agents: Vec<&crate::config::AgentConfig> =
|
|
687
|
+
let enabled_agents: Vec<&crate::config::AgentConfig> =
|
|
688
|
+
config.agents.iter().filter(|a| a.enabled).collect();
|
|
689
689
|
if enabled_agents.is_empty() {
|
|
690
690
|
return (config.clone(), "".to_string(), None, None);
|
|
691
691
|
}
|
|
@@ -694,7 +694,9 @@ fn resolve_agent_config(
|
|
|
694
694
|
enabled_agents.iter().find(|a| a.id == id).copied()
|
|
695
695
|
} else {
|
|
696
696
|
// Multi-Agent Pipeline Collaboration (Planner -> Coder -> Reviewer)
|
|
697
|
-
let plan_created = trajectory
|
|
697
|
+
let plan_created = trajectory
|
|
698
|
+
.iter()
|
|
699
|
+
.any(|step| step.contains("- Action: create_plan"));
|
|
698
700
|
if !plan_created {
|
|
699
701
|
if let Some(planner) = enabled_agents.iter().find(|a| a.id == "planner") {
|
|
700
702
|
Some(*planner)
|
|
@@ -708,7 +710,9 @@ fn resolve_agent_config(
|
|
|
708
710
|
});
|
|
709
711
|
if edits_made {
|
|
710
712
|
if let Some(last_step) = trajectory.last() {
|
|
711
|
-
if last_step.contains("- Action: write_file")
|
|
713
|
+
if last_step.contains("- Action: write_file")
|
|
714
|
+
|| last_step.contains("- Action: apply_patch")
|
|
715
|
+
{
|
|
712
716
|
if let Some(reviewer) = enabled_agents.iter().find(|a| a.id == "reviewer") {
|
|
713
717
|
Some(*reviewer)
|
|
714
718
|
} else {
|
|
@@ -741,21 +745,26 @@ fn resolve_agent_config(
|
|
|
741
745
|
cfg_clone.local_model_name = agent.model.clone();
|
|
742
746
|
cfg_clone.ollama_model = agent.model.clone();
|
|
743
747
|
|
|
744
|
-
if let Some(key) = &agent.api_key
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
}
|
|
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
|
+
_ => {}
|
|
755
759
|
}
|
|
756
760
|
}
|
|
757
761
|
|
|
758
|
-
(
|
|
762
|
+
(
|
|
763
|
+
cfg_clone,
|
|
764
|
+
agent.system_instruction.clone(),
|
|
765
|
+
Some(agent.name.clone()),
|
|
766
|
+
Some(agent.model.clone()),
|
|
767
|
+
)
|
|
759
768
|
}
|
|
760
769
|
|
|
761
770
|
pub async fn orchestrate_agent_loop<Approve, Progress, Chunk>(
|
|
@@ -796,18 +805,18 @@ where
|
|
|
796
805
|
|
|
797
806
|
if let Ok(memory) = MemoryStore::open_default() {
|
|
798
807
|
let mut profile_instructions = String::new();
|
|
799
|
-
if let Ok(Some(name)) = memory.get_profile("name")
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
}
|
|
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()));
|
|
803
812
|
}
|
|
804
|
-
if let Ok(Some(preferences)) = memory.get_profile("preferences")
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
)
|
|
810
|
-
|
|
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
|
+
));
|
|
811
820
|
}
|
|
812
821
|
|
|
813
822
|
if !profile_instructions.is_empty() {
|
|
@@ -844,7 +853,8 @@ where
|
|
|
844
853
|
let mut trajectory: Vec<String> = Vec::new();
|
|
845
854
|
|
|
846
855
|
for step in 1..=MAX_STEPS {
|
|
847
|
-
let (active_config, agent_instruction, active_agent_name, active_model_name) =
|
|
856
|
+
let (active_config, agent_instruction, active_agent_name, active_model_name) =
|
|
857
|
+
resolve_agent_config(config, agent_id, &trajectory);
|
|
848
858
|
|
|
849
859
|
progress(AgentProgress::Thinking {
|
|
850
860
|
elapsed_secs: started_at.elapsed().as_secs(),
|
|
@@ -858,12 +868,14 @@ where
|
|
|
858
868
|
"\n\n[Active Environment Context]\n\
|
|
859
869
|
You are running on: {}\n\
|
|
860
870
|
Using AI Model: {}\n",
|
|
861
|
-
|
|
862
|
-
model_name
|
|
871
|
+
active_config.ai_provider, model_name
|
|
863
872
|
));
|
|
864
873
|
}
|
|
865
874
|
if !agent_instruction.is_empty() {
|
|
866
|
-
active_system_prompt.push_str(&format!(
|
|
875
|
+
active_system_prompt.push_str(&format!(
|
|
876
|
+
"\n\nYour Current Role & System Instructions:\n{}",
|
|
877
|
+
agent_instruction
|
|
878
|
+
));
|
|
867
879
|
}
|
|
868
880
|
|
|
869
881
|
let (response, fallback) = send_chat_with_fallback(
|
|
@@ -1261,14 +1273,14 @@ where
|
|
|
1261
1273
|
if crate::is_browser_running(config).await {
|
|
1262
1274
|
let result = crate::browser::navigate(config, url)
|
|
1263
1275
|
.await
|
|
1264
|
-
.map_err(
|
|
1276
|
+
.map_err(OrchestrationError::Agent)?;
|
|
1265
1277
|
Ok(result)
|
|
1266
1278
|
} else {
|
|
1267
1279
|
let opened = if cfg!(target_os = "macos") {
|
|
1268
1280
|
std::process::Command::new("open").arg(url).spawn().is_ok()
|
|
1269
1281
|
} else if cfg!(target_os = "windows") {
|
|
1270
1282
|
std::process::Command::new("cmd")
|
|
1271
|
-
.args(
|
|
1283
|
+
.args(["/C", "start", url])
|
|
1272
1284
|
.spawn()
|
|
1273
1285
|
.is_ok()
|
|
1274
1286
|
} else {
|
|
@@ -1300,7 +1312,7 @@ where
|
|
|
1300
1312
|
};
|
|
1301
1313
|
let result = crate::browser::click(config, selector)
|
|
1302
1314
|
.await
|
|
1303
|
-
.map_err(
|
|
1315
|
+
.map_err(OrchestrationError::Agent)?;
|
|
1304
1316
|
Ok(result)
|
|
1305
1317
|
}
|
|
1306
1318
|
"browser_type" => {
|
|
@@ -1324,13 +1336,13 @@ where
|
|
|
1324
1336
|
};
|
|
1325
1337
|
let result = crate::browser::type_text(config, selector, text)
|
|
1326
1338
|
.await
|
|
1327
|
-
.map_err(
|
|
1339
|
+
.map_err(OrchestrationError::Agent)?;
|
|
1328
1340
|
Ok(result)
|
|
1329
1341
|
}
|
|
1330
1342
|
"browser_read" => {
|
|
1331
1343
|
let result = crate::browser::read_page_text(config)
|
|
1332
1344
|
.await
|
|
1333
|
-
.map_err(
|
|
1345
|
+
.map_err(OrchestrationError::Agent)?;
|
|
1334
1346
|
Ok(result)
|
|
1335
1347
|
}
|
|
1336
1348
|
"memory_recall" => {
|
|
@@ -1486,7 +1498,7 @@ where
|
|
|
1486
1498
|
path: file_name.to_owned(),
|
|
1487
1499
|
content: input.file_content.clone(),
|
|
1488
1500
|
})
|
|
1489
|
-
.map_err(
|
|
1501
|
+
.map_err(OrchestrationError::Agent)?;
|
|
1490
1502
|
|
|
1491
1503
|
match approved {
|
|
1492
1504
|
ApprovalOutcome::Approved => {
|
|
@@ -1509,7 +1521,7 @@ where
|
|
|
1509
1521
|
name: name.to_owned(),
|
|
1510
1522
|
instruction: instruction.to_owned(),
|
|
1511
1523
|
})
|
|
1512
|
-
.map_err(
|
|
1524
|
+
.map_err(OrchestrationError::Agent)?;
|
|
1513
1525
|
|
|
1514
1526
|
match approved {
|
|
1515
1527
|
ApprovalOutcome::Approved => Ok(execute_native_plugin(config, name, instruction)
|
|
@@ -1527,7 +1539,7 @@ where
|
|
|
1527
1539
|
tool: tool.to_owned(),
|
|
1528
1540
|
arguments: input.arguments.clone(),
|
|
1529
1541
|
})
|
|
1530
|
-
.map_err(
|
|
1542
|
+
.map_err(OrchestrationError::Agent)?;
|
|
1531
1543
|
|
|
1532
1544
|
match approved {
|
|
1533
1545
|
ApprovalOutcome::Approved => Ok(serde_json::to_string_pretty(
|
|
@@ -1548,7 +1560,7 @@ where
|
|
|
1548
1560
|
command: command.to_owned(),
|
|
1549
1561
|
mode,
|
|
1550
1562
|
})
|
|
1551
|
-
.map_err(
|
|
1563
|
+
.map_err(OrchestrationError::Agent)?;
|
|
1552
1564
|
|
|
1553
1565
|
match approved {
|
|
1554
1566
|
ApprovalOutcome::Approved => run_shell(root, config, command),
|
|
@@ -1593,7 +1605,7 @@ where
|
|
|
1593
1605
|
hunks: patch.hunks.clone(),
|
|
1594
1606
|
diff,
|
|
1595
1607
|
})
|
|
1596
|
-
.map_err(
|
|
1608
|
+
.map_err(OrchestrationError::Agent)?;
|
|
1597
1609
|
|
|
1598
1610
|
match approved {
|
|
1599
1611
|
ApprovalOutcome::Approved => {
|
|
@@ -1629,7 +1641,7 @@ where
|
|
|
1629
1641
|
content: input.file_content.clone(),
|
|
1630
1642
|
diff,
|
|
1631
1643
|
})
|
|
1632
|
-
.map_err(
|
|
1644
|
+
.map_err(OrchestrationError::Agent)?;
|
|
1633
1645
|
|
|
1634
1646
|
match approved {
|
|
1635
1647
|
ApprovalOutcome::Approved => {
|
|
@@ -1823,13 +1835,13 @@ fn view_image(path: &Path, config: &MintConfig) -> Result<String, OrchestrationE
|
|
|
1823
1835
|
}
|
|
1824
1836
|
let bytes = std::fs::read(&path)
|
|
1825
1837
|
.map_err(|e| OrchestrationError::Agent(format!("cannot read image: {e}")))?;
|
|
1826
|
-
|
|
1838
|
+
serde_json::to_string_pretty(&serde_json::json!({
|
|
1827
1839
|
"path": path,
|
|
1828
1840
|
"bytes": bytes.len(),
|
|
1829
1841
|
"mime": mime,
|
|
1830
1842
|
"dataUri": format!("data:{mime};base64,{}", BASE64_STANDARD.encode(bytes)),
|
|
1831
1843
|
}))
|
|
1832
|
-
.map_err(|e| OrchestrationError::Agent(e.to_string()))
|
|
1844
|
+
.map_err(|e| OrchestrationError::Agent(e.to_string()))
|
|
1833
1845
|
}
|
|
1834
1846
|
|
|
1835
1847
|
fn run_shell(
|
|
@@ -1845,14 +1857,13 @@ fn run_shell(
|
|
|
1845
1857
|
|
|
1846
1858
|
let mut hint = "";
|
|
1847
1859
|
let cmd_lower = command.to_lowercase();
|
|
1848
|
-
if output.success
|
|
1849
|
-
|
|
1860
|
+
if output.success
|
|
1861
|
+
&& (cmd_lower.contains("open")
|
|
1850
1862
|
|| cmd_lower.contains("launch")
|
|
1851
1863
|
|| cmd_lower.contains("chrome")
|
|
1852
|
-
|| cmd_lower.contains("firefox")
|
|
1853
|
-
|
|
1854
|
-
|
|
1855
|
-
}
|
|
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.";
|
|
1856
1867
|
}
|
|
1857
1868
|
|
|
1858
1869
|
Ok(format!(
|
|
@@ -1897,7 +1908,9 @@ fn action_fingerprint(decision: &AgentDecision) -> String {
|
|
|
1897
1908
|
}
|
|
1898
1909
|
|
|
1899
1910
|
fn initial_observation(task: &str, root: &Path, skills: &str) -> String {
|
|
1900
|
-
let now = chrono::Local::now()
|
|
1911
|
+
let now = chrono::Local::now()
|
|
1912
|
+
.format("%Y-%m-%d %H:%M:%S %Z")
|
|
1913
|
+
.to_string();
|
|
1901
1914
|
let mut observation = format!(
|
|
1902
1915
|
"Current Time: {now}\nTask: {task}\nWorkspace: {}\nLearned skills:\n{}\n",
|
|
1903
1916
|
root.display(),
|
|
@@ -2271,19 +2284,19 @@ Example response:
|
|
|
2271
2284
|
text_reply.to_string()
|
|
2272
2285
|
};
|
|
2273
2286
|
|
|
2274
|
-
if let Ok(value) = serde_json::from_str::<serde_json::Value>(&clean_json)
|
|
2275
|
-
|
|
2276
|
-
|
|
2277
|
-
|
|
2278
|
-
|
|
2279
|
-
|
|
2280
|
-
|
|
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)?;
|
|
2281
2294
|
}
|
|
2282
|
-
|
|
2283
|
-
|
|
2284
|
-
|
|
2285
|
-
|
|
2286
|
-
|
|
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)?;
|
|
2287
2300
|
}
|
|
2288
2301
|
}
|
|
2289
2302
|
}
|
|
@@ -2294,6 +2307,7 @@ Example response:
|
|
|
2294
2307
|
#[cfg(test)]
|
|
2295
2308
|
mod tests {
|
|
2296
2309
|
use super::*;
|
|
2310
|
+
use crate::config::AgentConfig;
|
|
2297
2311
|
|
|
2298
2312
|
#[test]
|
|
2299
2313
|
fn preserves_request_without_history() {
|
|
@@ -2310,9 +2324,12 @@ mod tests {
|
|
|
2310
2324
|
workspace_path: None,
|
|
2311
2325
|
agent_id: None,
|
|
2312
2326
|
};
|
|
2313
|
-
|
|
2314
|
-
|
|
2315
|
-
|
|
2327
|
+
let config = MintConfig::default();
|
|
2328
|
+
assert!(
|
|
2329
|
+
enrich_request(&config, &store, &request)
|
|
2330
|
+
.unwrap()
|
|
2331
|
+
.system_instruction
|
|
2332
|
+
.starts_with("system")
|
|
2316
2333
|
);
|
|
2317
2334
|
}
|
|
2318
2335
|
|
|
@@ -2418,6 +2435,7 @@ mod tests {
|
|
|
2418
2435
|
#[test]
|
|
2419
2436
|
fn test_resolve_agent_config() {
|
|
2420
2437
|
let mut config = MintConfig::default();
|
|
2438
|
+
config.enable_agent_collaboration = true;
|
|
2421
2439
|
config.agents = vec![
|
|
2422
2440
|
AgentConfig {
|
|
2423
2441
|
id: "planner".into(),
|
|
@@ -2447,12 +2465,13 @@ mod tests {
|
|
|
2447
2465
|
assert_eq!(name, Some("Planner".to_string()));
|
|
2448
2466
|
assert_eq!(model, Some("gpt-4o".to_string()));
|
|
2449
2467
|
|
|
2450
|
-
let (cfg, instr, name,
|
|
2468
|
+
let (cfg, instr, name, _model) = resolve_agent_config(&config, None, &[]);
|
|
2451
2469
|
assert_eq!(cfg.ai_provider, "openai");
|
|
2452
2470
|
assert_eq!(instr, "Planner instruction");
|
|
2453
2471
|
assert_eq!(name, Some("Planner".to_string()));
|
|
2454
2472
|
|
|
2455
|
-
let trajectory =
|
|
2473
|
+
let trajectory =
|
|
2474
|
+
vec!["Step 1:\n- Action: create_plan\n- Observation: all planned".to_string()];
|
|
2456
2475
|
let (cfg, instr, name, model) = resolve_agent_config(&config, None, &trajectory);
|
|
2457
2476
|
assert_eq!(cfg.ai_provider, "gemini");
|
|
2458
2477
|
assert_eq!(instr, "Coder instruction");
|
|
@@ -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
|
}
|
|
@@ -92,6 +92,31 @@ pub fn learned_skills_context(workspace_root: Option<&Path>) -> Result<String, S
|
|
|
92
92
|
Ok(format!("{}\n...<learned skills truncated>", &value[..end]))
|
|
93
93
|
}
|
|
94
94
|
|
|
95
|
+
pub fn parse_skill_description(content: &str) -> Option<String> {
|
|
96
|
+
let content = content.trim_start();
|
|
97
|
+
if !content.starts_with("---") {
|
|
98
|
+
return None;
|
|
99
|
+
}
|
|
100
|
+
let rest = &content[3..];
|
|
101
|
+
let end_idx = rest.find("---")?;
|
|
102
|
+
let frontmatter = &rest[..end_idx];
|
|
103
|
+
for line in frontmatter.lines() {
|
|
104
|
+
let line = line.trim();
|
|
105
|
+
if let Some(desc) = line.strip_prefix("description:") {
|
|
106
|
+
let mut val = desc.trim();
|
|
107
|
+
if (val.starts_with('"') && val.ends_with('"'))
|
|
108
|
+
|| (val.starts_with('\'') && val.ends_with('\''))
|
|
109
|
+
{
|
|
110
|
+
if val.len() >= 2 {
|
|
111
|
+
val = &val[1..val.len() - 1];
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
return Some(val.to_string());
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
None
|
|
118
|
+
}
|
|
119
|
+
|
|
95
120
|
pub fn load_skills_from_dir(dir: &Path, list: &mut Vec<LearnedSkill>) {
|
|
96
121
|
if !dir.is_dir() {
|
|
97
122
|
return;
|
|
@@ -109,36 +134,38 @@ pub fn load_skills_from_dir(dir: &Path, list: &mut Vec<LearnedSkill>) {
|
|
|
109
134
|
.and_then(|n| n.to_str())
|
|
110
135
|
.unwrap_or("skill")
|
|
111
136
|
.to_string();
|
|
137
|
+
let description = parse_skill_description(&content);
|
|
112
138
|
list.push(LearnedSkill {
|
|
113
139
|
id: 0,
|
|
114
140
|
name,
|
|
115
141
|
source_path: skill_file.to_string_lossy().to_string(),
|
|
116
142
|
content,
|
|
117
143
|
created_at: String::new(),
|
|
144
|
+
description,
|
|
118
145
|
});
|
|
119
146
|
}
|
|
120
147
|
break;
|
|
121
148
|
}
|
|
122
149
|
}
|
|
123
|
-
} else if path.is_file()
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
}
|
|
150
|
+
} else if path.is_file()
|
|
151
|
+
&& let Some(ext) = path.extension().and_then(|e| e.to_str())
|
|
152
|
+
&& matches!(ext.to_ascii_lowercase().as_str(), "md" | "txt")
|
|
153
|
+
&& let Ok(content) = fs::read_to_string(&path)
|
|
154
|
+
{
|
|
155
|
+
let name = path
|
|
156
|
+
.file_stem()
|
|
157
|
+
.and_then(|n| n.to_str())
|
|
158
|
+
.unwrap_or("skill")
|
|
159
|
+
.to_string();
|
|
160
|
+
let description = parse_skill_description(&content);
|
|
161
|
+
list.push(LearnedSkill {
|
|
162
|
+
id: 0,
|
|
163
|
+
name,
|
|
164
|
+
source_path: path.to_string_lossy().to_string(),
|
|
165
|
+
content,
|
|
166
|
+
created_at: String::new(),
|
|
167
|
+
description,
|
|
168
|
+
});
|
|
142
169
|
}
|
|
143
170
|
}
|
|
144
171
|
}
|
package/package.json
CHANGED
|
@@ -22,9 +22,15 @@
|
|
|
22
22
|
],
|
|
23
23
|
"sources": [
|
|
24
24
|
{
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
25
|
+
"type": "archive",
|
|
26
|
+
"url": "https://github.com/FluidSynth/fluidsynth/archive/v2.5.6.tar.gz",
|
|
27
|
+
"sha256": "0825f024c9cf7a18073739b83612d46542ecbfb349ae9147a1e9f08e2d524407"
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
"type": "git",
|
|
31
|
+
"url": "https://github.com/kthohr/gcem.git",
|
|
32
|
+
"commit": "012ae73c6d0a2cb09ffe86475f5c6fba3926e200",
|
|
33
|
+
"dest": "gcem"
|
|
28
34
|
}
|
|
29
35
|
]
|
|
30
36
|
}
|
|
@@ -12,8 +12,14 @@
|
|
|
12
12
|
"sources": [
|
|
13
13
|
{
|
|
14
14
|
"type": "archive",
|
|
15
|
-
"url": "https://github.com/FluidSynth/fluidsynth/archive/
|
|
16
|
-
"sha256": "
|
|
15
|
+
"url": "https://github.com/FluidSynth/fluidsynth/archive/v2.5.6.tar.gz",
|
|
16
|
+
"sha256": "0825f024c9cf7a18073739b83612d46542ecbfb349ae9147a1e9f08e2d524407"
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"type": "git",
|
|
20
|
+
"url": "https://github.com/kthohr/gcem.git",
|
|
21
|
+
"commit": "012ae73c6d0a2cb09ffe86475f5c6fba3926e200",
|
|
22
|
+
"dest": "gcem"
|
|
17
23
|
}
|
|
18
24
|
]
|
|
19
25
|
}
|