@pheem49/mint 1.6.3 → 1.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (63) hide show
  1. package/Cargo.lock +3 -3
  2. package/Cargo.toml +1 -1
  3. package/README.md +82 -21
  4. package/assets/Mint_CLI.png +0 -0
  5. package/assets/Mint_Desktop.png +0 -0
  6. package/assets/Mint_Web.png +0 -0
  7. package/assets/Mint_WorkSpace.png +0 -0
  8. package/assets/Setting.png +0 -0
  9. package/assets/bridges.svg +4 -0
  10. package/assets/chat.svg +3 -0
  11. package/assets/code.svg +4 -0
  12. package/assets/features.svg +3 -0
  13. package/assets/imagegen.svg +5 -0
  14. package/assets/live2d.svg +6 -0
  15. package/assets/memory.svg +5 -0
  16. package/assets/screencapture.svg +4 -0
  17. package/assets/setup.svg +3 -0
  18. package/assets/tools.svg +4 -0
  19. package/bin/mint +0 -0
  20. package/crates/mint-cli/src/agent.rs +47 -15
  21. package/crates/mint-cli/src/main.rs +534 -7
  22. package/crates/mint-cli/src/onboard.rs +134 -1
  23. package/crates/mint-cli/src/setup.rs +173 -32
  24. package/crates/mint-core/src/api_server.rs +147 -3
  25. package/crates/mint-core/src/config.rs +22 -3
  26. package/crates/mint-core/src/image_gen.rs +895 -0
  27. package/crates/mint-core/src/lib.rs +4 -0
  28. package/crates/mint-core/src/memory.rs +27 -1
  29. package/crates/mint-core/src/orchestration.rs +1 -1
  30. package/crates/mint-core/src/plugins.rs +32 -0
  31. package/crates/mint-core/tests/memory_persistence.rs +28 -0
  32. package/package.json +1 -1
  33. package/src/bin/index.js +0 -0
  34. package/src/renderer/src/agentProgress.ts +39 -0
  35. package/src/renderer/src/components/ChatPanel.tsx +547 -47
  36. package/src/renderer/src/components/DashboardSidebar.tsx +12 -4
  37. package/src/renderer/src/components/ImageStudioPanel.tsx +638 -0
  38. package/src/renderer/src/components/MintDashboard.tsx +84 -31
  39. package/src/renderer/src/components/ModelPanel.tsx +2 -2
  40. package/src/renderer/src/components/PicturesLibrary.tsx +5 -1
  41. package/src/renderer/src/components/SettingsWindow.tsx +29 -20
  42. package/src/renderer/src/components/SpotlightWindow.tsx +8 -8
  43. package/src/renderer/src/components/WorkspacePanel.tsx +1 -1
  44. package/src/renderer/src/css/image-studio.css +722 -0
  45. package/src/renderer/src/css/settings.css +13 -3
  46. package/src/renderer/src/css/styles.css +245 -43
  47. package/src/renderer/src/index.css +152 -0
  48. package/src/renderer/src/tauri.ts +148 -0
  49. package/src/renderer/src-web/App.tsx +16 -1
  50. package/src/renderer/src-web/agentProgress.ts +39 -0
  51. package/src/renderer/src-web/components/ChatPanel.tsx +568 -46
  52. package/src/renderer/src-web/components/DashboardSidebar.tsx +10 -2
  53. package/src/renderer/src-web/components/ImageStudioPanel.tsx +638 -0
  54. package/src/renderer/src-web/components/MintDashboard.tsx +65 -19
  55. package/src/renderer/src-web/components/PicturesLibrary.tsx +5 -1
  56. package/src/renderer/src-web/components/SettingsWindow.tsx +29 -20
  57. package/src/renderer/src-web/css/image-studio.css +722 -0
  58. package/src/renderer/src-web/css/settings.css +14 -4
  59. package/src/renderer/src-web/css/styles.css +292 -44
  60. package/src/renderer/src-web/index.css +152 -0
  61. package/src/renderer/src-web/tauri.ts +149 -0
  62. package/src-tauri/src/lib.rs +65 -5
  63. package/src-tauri/tauri.conf.json +1 -1
@@ -5,6 +5,7 @@ pub mod chat;
5
5
  pub mod code_tools;
6
6
  pub mod config;
7
7
  pub mod files;
8
+ pub mod image_gen;
8
9
  pub mod knowledge;
9
10
  pub mod mcp;
10
11
  pub mod memory;
@@ -39,6 +40,9 @@ pub use config::{
39
40
  set_config_value,
40
41
  };
41
42
  pub use files::{FileOperationError, PathKind, PathMatch, create_folder, find_paths};
43
+ pub use image_gen::{
44
+ GeneratedImage, ImageGenError, ImageGenRequest, ImageGenResponse, generate_images,
45
+ };
42
46
  pub use knowledge::{
43
47
  KnowledgeError, KnowledgeHit, KnowledgeSource, KnowledgeStore, extract_document_text,
44
48
  };
@@ -36,6 +36,8 @@ pub struct InteractionMemory {
36
36
  pub model: String,
37
37
  pub fallback_provider: Option<String>,
38
38
  pub created_at: String,
39
+ #[serde(default, skip_serializing_if = "Option::is_none")]
40
+ pub agent_activity: Option<serde_json::Value>,
39
41
  }
40
42
 
41
43
  #[derive(Debug, Clone, Serialize, PartialEq, Eq)]
@@ -172,7 +174,7 @@ impl MemoryStore {
172
174
  ensure_builtin_chat_sessions(&connection)?;
173
175
  ensure_chat_session_row(&connection, &chat_id)?;
174
176
  let mut statement = connection.prepare(
175
- "SELECT id, chat_id, user_text, ai_text, provider, model, fallback_provider, created_at
177
+ "SELECT id, chat_id, user_text, ai_text, provider, model, fallback_provider, created_at, agent_activity_json
176
178
  FROM interaction_memories
177
179
  WHERE chat_id = ?1
178
180
  ORDER BY id DESC
@@ -202,6 +204,20 @@ impl MemoryStore {
202
204
  rows.collect::<Result<Vec<_>, _>>().map_err(Into::into)
203
205
  }
204
206
 
207
+ pub fn set_interaction_agent_activity_json(
208
+ &self,
209
+ interaction_id: i64,
210
+ activity_json: &str,
211
+ ) -> Result<usize, MemoryError> {
212
+ let connection = self.connection()?;
213
+ Ok(connection.execute(
214
+ "UPDATE interaction_memories
215
+ SET agent_activity_json = ?2
216
+ WHERE id = ?1",
217
+ params![interaction_id, activity_json],
218
+ )?)
219
+ }
220
+
205
221
  pub fn clear_interactions(&self) -> Result<usize, MemoryError> {
206
222
  self.clear_interactions_for_chat(DEFAULT_CONVERSATION_ID)
207
223
  }
@@ -551,6 +567,12 @@ fn initialize(
551
567
  "fallback_provider",
552
568
  "TEXT DEFAULT NULL",
553
569
  )?;
570
+ ensure_column(
571
+ connection,
572
+ "interaction_memories",
573
+ "agent_activity_json",
574
+ "TEXT DEFAULT NULL",
575
+ )?;
554
576
  ensure_column(
555
577
  connection,
556
578
  "chat_sessions",
@@ -612,6 +634,9 @@ fn learned_skill_row(row: &rusqlite::Row<'_>) -> Result<LearnedSkill, rusqlite::
612
634
  }
613
635
 
614
636
  fn interaction_row(row: &rusqlite::Row<'_>) -> Result<InteractionMemory, rusqlite::Error> {
637
+ let agent_activity = row
638
+ .get::<_, Option<String>>(8)?
639
+ .and_then(|raw| serde_json::from_str(&raw).ok());
615
640
  Ok(InteractionMemory {
616
641
  id: row.get(0)?,
617
642
  chat_id: row.get(1)?,
@@ -621,6 +646,7 @@ fn interaction_row(row: &rusqlite::Row<'_>) -> Result<InteractionMemory, rusqlit
621
646
  model: row.get(5)?,
622
647
  fallback_provider: row.get(6)?,
623
648
  created_at: row.get(7)?,
649
+ agent_activity,
624
650
  })
625
651
  }
626
652
 
@@ -385,7 +385,7 @@ pub fn build_system_prompt(config: &MintConfig) -> String {
385
385
  "- write_file: {\"path\":\"new/relative/path\",\"fileContent\":\"full file content\"}",
386
386
  );
387
387
  }
388
- input_formats.push("- finish: {\"summary\":\"concise final answer\",\"verification\":\"checks run or not run\"}");
388
+ input_formats.push("- finish: {\"summary\":\"concise final answer in Thai when the user writes Thai\",\"verification\":\"checks run or not run\"}");
389
389
 
390
390
  let input_formats_str = input_formats.join("\n");
391
391
 
@@ -84,6 +84,10 @@ pub fn native_plugins() -> Vec<NativePlugin> {
84
84
  name: "notion",
85
85
  description: "Create notes, read databases, and append blocks through Notion API.",
86
86
  },
87
+ NativePlugin {
88
+ name: "github",
89
+ description: "List issues, view issues, list pull requests, or check PR status via gh CLI.",
90
+ },
87
91
  ]
88
92
  }
89
93
 
@@ -104,6 +108,7 @@ pub async fn execute_native_plugin(
104
108
  "gmail" => gmail(config, instruction).await,
105
109
  "google_calendar" => calendar(config, instruction).await,
106
110
  "notion" => notion(config, instruction).await,
111
+ "github" => github(instruction),
107
112
  _ => Err(PluginError::UnknownPlugin(name.into())),
108
113
  }
109
114
  }
@@ -229,6 +234,33 @@ fn spotify(instruction: &str) -> Result<String, PluginError> {
229
234
  run("playerctl", &args)
230
235
  }
231
236
 
237
+ fn github(instruction: &str) -> Result<String, PluginError> {
238
+ let parts = instruction.split_whitespace().collect::<Vec<_>>();
239
+ match parts.as_slice() {
240
+ ["list_issues"] | ["issue", "list"] => run("gh", &["issue", "list", "--limit", "10"]),
241
+ ["view_issue", id] | ["issue", "view", id] => {
242
+ if id.chars().all(|c| c.is_ascii_digit()) {
243
+ run("gh", &["issue", "view", id])
244
+ } else {
245
+ invalid("github", "view_issue/issue view requires a numeric issue ID")
246
+ }
247
+ }
248
+ ["list_prs"] | ["pr", "list"] => run("gh", &["pr", "list", "--limit", "10"]),
249
+ ["pr_status"] | ["pr", "status"] => run("gh", &["pr", "status"]),
250
+ ["view_pr", id] | ["pr", "view", id] => {
251
+ if id.chars().all(|c| c.is_ascii_digit()) {
252
+ run("gh", &["pr", "view", id])
253
+ } else {
254
+ invalid("github", "view_pr/pr view requires a numeric PR ID")
255
+ }
256
+ }
257
+ _ => invalid(
258
+ "github",
259
+ "expected list_issues, view_issue <id>, list_prs, pr_status, or view_pr <id>",
260
+ ),
261
+ }
262
+ }
263
+
232
264
  fn system_metrics(instruction: &str) -> Result<String, PluginError> {
233
265
  let uptime = fs::read_to_string("/proc/uptime")
234
266
  .ok()
@@ -57,6 +57,34 @@ fn stores_recent_interactions_with_provider_metadata() {
57
57
  assert_eq!(interactions[1].fallback_provider, None);
58
58
  }
59
59
 
60
+ #[test]
61
+ fn stores_and_reads_interaction_agent_activity() {
62
+ let store = store("agent-activity");
63
+ let interaction_id = store
64
+ .add_interaction_with_metadata("hello", "hi", "gemini", "gemini-test")
65
+ .unwrap();
66
+ store
67
+ .set_interaction_agent_activity_json(
68
+ interaction_id,
69
+ r#"[{"type":"Thought","data":{"thought":"step one"}}]"#,
70
+ )
71
+ .unwrap();
72
+
73
+ let interactions = store.recent_interactions(1).unwrap();
74
+ assert_eq!(interactions.len(), 1);
75
+ let activity = interactions[0]
76
+ .agent_activity
77
+ .as_ref()
78
+ .and_then(|value| value.as_array())
79
+ .cloned()
80
+ .unwrap_or_default();
81
+ assert_eq!(activity.len(), 1);
82
+ assert_eq!(
83
+ activity[0].get("type").and_then(|value| value.as_str()),
84
+ Some("Thought")
85
+ );
86
+ }
87
+
60
88
  #[test]
61
89
  fn preserves_chat_history_when_store_is_reopened() {
62
90
  let path = test_path("reopen-history", "sqlite");
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pheem49/mint",
3
- "version": "1.6.3",
3
+ "version": "1.7.0",
4
4
  "description": "A native Tauri desktop AI assistant with a Rust backend and React UI.",
5
5
  "scripts": {
6
6
  "dev:web": "vite --config vite.config.web.ts",
package/src/bin/index.js CHANGED
File without changes
@@ -0,0 +1,39 @@
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
+ }