@kirosnn/mosaic 0.0.91 → 0.73.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 (99) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +2 -6
  3. package/package.json +55 -48
  4. package/src/agent/Agent.ts +353 -131
  5. package/src/agent/context.ts +4 -4
  6. package/src/agent/prompts/systemPrompt.ts +209 -70
  7. package/src/agent/prompts/toolsPrompt.ts +285 -138
  8. package/src/agent/provider/anthropic.ts +109 -105
  9. package/src/agent/provider/google.ts +111 -107
  10. package/src/agent/provider/mistral.ts +95 -95
  11. package/src/agent/provider/ollama.ts +73 -17
  12. package/src/agent/provider/openai.ts +146 -102
  13. package/src/agent/provider/rateLimit.ts +178 -0
  14. package/src/agent/provider/reasoning.ts +29 -0
  15. package/src/agent/provider/xai.ts +108 -104
  16. package/src/agent/tools/definitions.ts +15 -1
  17. package/src/agent/tools/executor.ts +717 -98
  18. package/src/agent/tools/exploreExecutor.ts +20 -22
  19. package/src/agent/tools/fetch.ts +58 -0
  20. package/src/agent/tools/glob.ts +20 -4
  21. package/src/agent/tools/grep.ts +64 -9
  22. package/src/agent/tools/plan.ts +27 -0
  23. package/src/agent/tools/question.ts +7 -1
  24. package/src/agent/tools/read.ts +2 -0
  25. package/src/agent/types.ts +15 -14
  26. package/src/components/App.tsx +50 -8
  27. package/src/components/CustomInput.tsx +461 -77
  28. package/src/components/Main.tsx +1459 -1112
  29. package/src/components/Setup.tsx +1 -1
  30. package/src/components/ShortcutsModal.tsx +11 -8
  31. package/src/components/Welcome.tsx +1 -1
  32. package/src/components/main/ApprovalPanel.tsx +4 -3
  33. package/src/components/main/ChatPage.tsx +858 -516
  34. package/src/components/main/HomePage.tsx +58 -39
  35. package/src/components/main/QuestionPanel.tsx +52 -7
  36. package/src/components/main/ThinkingIndicator.tsx +13 -2
  37. package/src/components/main/types.ts +11 -10
  38. package/src/index.tsx +53 -25
  39. package/src/mcp/approvalPolicy.ts +148 -0
  40. package/src/mcp/cli/add.ts +185 -0
  41. package/src/mcp/cli/doctor.ts +77 -0
  42. package/src/mcp/cli/index.ts +85 -0
  43. package/src/mcp/cli/list.ts +50 -0
  44. package/src/mcp/cli/logs.ts +24 -0
  45. package/src/mcp/cli/manage.ts +99 -0
  46. package/src/mcp/cli/show.ts +53 -0
  47. package/src/mcp/cli/tools.ts +77 -0
  48. package/src/mcp/config.ts +223 -0
  49. package/src/mcp/index.ts +80 -0
  50. package/src/mcp/processManager.ts +299 -0
  51. package/src/mcp/rateLimiter.ts +50 -0
  52. package/src/mcp/registry.ts +151 -0
  53. package/src/mcp/schemaConverter.ts +100 -0
  54. package/src/mcp/servers/navigation.ts +854 -0
  55. package/src/mcp/toolCatalog.ts +169 -0
  56. package/src/mcp/types.ts +95 -0
  57. package/src/utils/approvalBridge.ts +45 -12
  58. package/src/utils/approvalModeBridge.ts +17 -0
  59. package/src/utils/commands/approvals.ts +48 -0
  60. package/src/utils/commands/compact.ts +30 -0
  61. package/src/utils/commands/echo.ts +1 -1
  62. package/src/utils/commands/image.ts +109 -0
  63. package/src/utils/commands/index.ts +9 -7
  64. package/src/utils/commands/new.ts +15 -0
  65. package/src/utils/commands/types.ts +3 -0
  66. package/src/utils/config.ts +3 -1
  67. package/src/utils/diffRendering.tsx +13 -16
  68. package/src/utils/exploreBridge.ts +10 -0
  69. package/src/utils/history.ts +82 -40
  70. package/src/utils/imageBridge.ts +28 -0
  71. package/src/utils/images.ts +31 -0
  72. package/src/utils/markdown.tsx +163 -99
  73. package/src/utils/models.ts +31 -16
  74. package/src/utils/notificationBridge.ts +23 -0
  75. package/src/utils/questionBridge.ts +36 -1
  76. package/src/utils/tokenEstimator.ts +32 -0
  77. package/src/utils/toolFormatting.ts +428 -48
  78. package/src/web/app.tsx +65 -5
  79. package/src/web/assets/css/ChatPage.css +102 -30
  80. package/src/web/assets/css/MessageItem.css +26 -29
  81. package/src/web/assets/css/ThinkingIndicator.css +44 -6
  82. package/src/web/assets/css/ToolMessage.css +36 -14
  83. package/src/web/components/ChatPage.tsx +228 -105
  84. package/src/web/components/HomePage.tsx +3 -3
  85. package/src/web/components/MessageItem.tsx +80 -81
  86. package/src/web/components/QuestionPanel.tsx +72 -12
  87. package/src/web/components/Setup.tsx +1 -1
  88. package/src/web/components/Sidebar.tsx +1 -3
  89. package/src/web/components/ThinkingIndicator.tsx +41 -21
  90. package/src/web/router.ts +1 -1
  91. package/src/web/server.tsx +894 -662
  92. package/src/web/storage.ts +23 -1
  93. package/src/web/types.ts +7 -6
  94. package/src/utils/commands/redo.ts +0 -74
  95. package/src/utils/commands/sessions.ts +0 -129
  96. package/src/utils/commands/undo.ts +0 -75
  97. package/src/utils/undoRedo.ts +0 -429
  98. package/src/utils/undoRedoBridge.ts +0 -45
  99. package/src/utils/undoRedoDb.ts +0 -338
@@ -26,6 +26,28 @@ export function getAllConversations(): Conversation[] {
26
26
  }
27
27
  }
28
28
 
29
+ export function mergeConversations(incoming: Conversation[]): boolean {
30
+ if (!incoming.length) return false;
31
+
32
+ const existing = getAllConversations();
33
+ const byId = new Map(existing.map((conv) => [conv.id, conv]));
34
+ let changed = false;
35
+
36
+ for (const conv of incoming) {
37
+ const current = byId.get(conv.id);
38
+ if (!current || conv.updatedAt > current.updatedAt) {
39
+ byId.set(conv.id, conv);
40
+ changed = true;
41
+ }
42
+ }
43
+
44
+ if (changed) {
45
+ localStorage.setItem(STORAGE_KEY, JSON.stringify(Array.from(byId.values())));
46
+ }
47
+
48
+ return changed;
49
+ }
50
+
29
51
  export function getConversation(id: string): Conversation | null {
30
52
  const conversations = getAllConversations();
31
53
  return conversations.find(c => c.id === id) || null;
@@ -89,4 +111,4 @@ export function formatWorkspace(path: string | null | undefined): string {
89
111
  }
90
112
 
91
113
  return normalized;
92
- }
114
+ }
package/src/web/types.ts CHANGED
@@ -1,8 +1,9 @@
1
- export interface Message {
2
- id: string;
3
- role: 'user' | 'assistant' | 'tool';
4
- content: string;
5
- displayContent?: string;
1
+ export interface Message {
2
+ id: string;
3
+ role: 'user' | 'assistant' | 'tool';
4
+ content: string;
5
+ images?: import("../utils/images").ImageAttachment[];
6
+ displayContent?: string;
6
7
  isError?: boolean;
7
8
  toolName?: string;
8
9
  toolArgs?: Record<string, unknown>;
@@ -14,4 +15,4 @@ export interface Message {
14
15
  runningStartTime?: number;
15
16
  responseDuration?: number;
16
17
  blendWord?: string;
17
- }
18
+ }
@@ -1,74 +0,0 @@
1
- import type { Command, CommandResult } from './types';
2
- import { redo, canRedo, isGitRepository } from '../undoRedo';
3
- import { notifyUndoRedo } from '../undoRedoBridge';
4
-
5
- export const redoCommand: Command = {
6
- name: 'redo',
7
- description: 'Redo a previously undone message. Only available after using /undo. Any file changes will also be restored. Internally, this uses Git to manage the file changes if available.',
8
- usage: '/redo',
9
- aliases: ['r'],
10
- execute: async (): Promise<CommandResult> => {
11
- if (!canRedo()) {
12
- return {
13
- success: false,
14
- content: 'Nothing to redo. Use /undo first, or the redo stack is empty.',
15
- shouldAddToHistory: false
16
- };
17
- }
18
-
19
- const result = redo();
20
- if (!result) {
21
- return {
22
- success: false,
23
- content: 'Failed to redo. Could not retrieve state.',
24
- shouldAddToHistory: false
25
- };
26
- }
27
-
28
- if (!result.success) {
29
- return {
30
- success: false,
31
- content: `Failed to redo file changes:\n${result.error || 'Unknown error'}`,
32
- shouldAddToHistory: false
33
- };
34
- }
35
-
36
- notifyUndoRedo(result.state, 'redo');
37
-
38
- const messageCountText = result.state.messages.length === 0 ? 'conversation restored' : `${result.state.messages.length} message(s) restored`;
39
-
40
- let details = '';
41
-
42
- if (result.gitChanges && result.gitChanges.length > 0) {
43
- const fileDetails = result.gitChanges.map(f => {
44
- switch (f.status) {
45
- case 'M': return ` • ${f.path} (changes reapplied)`;
46
- case 'A': return ` • ${f.path} (created/restored)`;
47
- case 'D': return ` • ${f.path} (deleted)`;
48
- case 'R': return ` • ${f.path} (renamed)`;
49
- default: return ` • ${f.path} (status: ${f.status})`;
50
- }
51
- }).join('\n');
52
- details = `\n- Files affected (${result.gitChanges.length}):\n${fileDetails}`;
53
- } else if (result.state.fileSnapshots.length > 0) {
54
- const fileDetails = result.state.fileSnapshots.map(f => {
55
- if (!f.existed) {
56
- return ` • ${f.path} (recreated)`;
57
- } else if (f.content === '') {
58
- return ` • ${f.path} (deleted)`;
59
- } else {
60
- return ` • ${f.path} (restored)`;
61
- }
62
- }).join('\n');
63
- details = `\n- Files affected (${result.state.fileSnapshots.length}):\n${fileDetails}`;
64
- } else if (result.state.gitCommitHash) {
65
- details = `\n- Files restored via Git (commit: ${result.state.gitCommitHash.slice(0, 7)})`;
66
- }
67
-
68
- return {
69
- success: true,
70
- content: `Redone: conversation and file changes restored.${details}\n- ${messageCountText}`,
71
- shouldAddToHistory: false
72
- };
73
- }
74
- };
@@ -1,129 +0,0 @@
1
- import type { Command, CommandResult } from './types';
2
- import { getAllSessions, setCurrentSession, deleteSession } from '../undoRedoDb';
3
-
4
- export const sessionsCommand: Command = {
5
- name: 'sessions',
6
- description: 'Manage undo/redo sessions. List all sessions or switch to a specific session.',
7
- usage: '/sessions [list|switch <session-id>|delete <session-id>]',
8
- aliases: ['s'],
9
- execute: async (args: string[], _fullCommand: string): Promise<CommandResult> => {
10
- const parts = args;
11
- const action = parts[0] || 'list';
12
-
13
- if (action === 'list') {
14
- const sessions = getAllSessions();
15
-
16
- if (sessions.length === 0) {
17
- return {
18
- success: true,
19
- content: 'No sessions found.',
20
- shouldAddToHistory: false
21
- };
22
- }
23
-
24
- const lines = ['Available sessions:\n'];
25
- for (const session of sessions) {
26
- const current = session.isCurrent ? ' (current)' : '';
27
- const date = new Date(session.lastAccessedAt).toLocaleString();
28
- lines.push(`- ${session.id}${current}`);
29
- lines.push(` Last accessed: ${date}`);
30
- }
31
-
32
- return {
33
- success: true,
34
- content: lines.join('\n'),
35
- shouldAddToHistory: false
36
- };
37
- }
38
-
39
- if (action === 'switch') {
40
- const sessionId = parts[1];
41
- if (!sessionId) {
42
- return {
43
- success: false,
44
- content: 'Please provide a session ID to switch to.\nUsage: /sessions switch <session-id>',
45
- shouldAddToHistory: false
46
- };
47
- }
48
-
49
- const sessions = getAllSessions();
50
- const targetSession = sessions.find(s => s.id === sessionId);
51
-
52
- if (!targetSession) {
53
- return {
54
- success: false,
55
- content: `Session not found: ${sessionId}`,
56
- shouldAddToHistory: false
57
- };
58
- }
59
-
60
- try {
61
- setCurrentSession(sessionId);
62
- return {
63
- success: true,
64
- content: `Switched to session: ${sessionId}`,
65
- shouldAddToHistory: false
66
- };
67
- } catch (error) {
68
- const errorMsg = error instanceof Error ? error.message : 'Unknown error';
69
- return {
70
- success: false,
71
- content: `Failed to switch session: ${errorMsg}`,
72
- shouldAddToHistory: false
73
- };
74
- }
75
- }
76
-
77
- if (action === 'delete') {
78
- const sessionId = parts[1];
79
- if (!sessionId) {
80
- return {
81
- success: false,
82
- content: 'Please provide a session ID to delete.\nUsage: /sessions delete <session-id>',
83
- shouldAddToHistory: false
84
- };
85
- }
86
-
87
- const sessions = getAllSessions();
88
- const targetSession = sessions.find(s => s.id === sessionId);
89
-
90
- if (!targetSession) {
91
- return {
92
- success: false,
93
- content: `Session not found: ${sessionId}`,
94
- shouldAddToHistory: false
95
- };
96
- }
97
-
98
- if (targetSession.isCurrent) {
99
- return {
100
- success: false,
101
- content: 'Cannot delete the current session. Switch to another session first.',
102
- shouldAddToHistory: false
103
- };
104
- }
105
-
106
- try {
107
- deleteSession(sessionId);
108
- return {
109
- success: true,
110
- content: `Deleted session: ${sessionId}`,
111
- shouldAddToHistory: false
112
- };
113
- } catch (error) {
114
- const errorMsg = error instanceof Error ? error.message : 'Unknown error';
115
- return {
116
- success: false,
117
- content: `Failed to delete session: ${errorMsg}`,
118
- shouldAddToHistory: false
119
- };
120
- }
121
- }
122
-
123
- return {
124
- success: false,
125
- content: `Unknown action: ${action}\nUsage: /sessions [list|switch <session-id>|delete <session-id>]`,
126
- shouldAddToHistory: false
127
- };
128
- }
129
- };
@@ -1,75 +0,0 @@
1
- import type { Command, CommandResult } from './types';
2
- import { undo, canUndo, isGitRepository } from '../undoRedo';
3
- import { notifyUndoRedo } from '../undoRedoBridge';
4
-
5
- export const undoCommand: Command = {
6
- name: 'undo',
7
- description: 'Undo last message in the conversation. Removes the most recent user message, all subsequent responses, and any file changes. Any file changes made will also be reverted. Internally, this uses Git to manage the file changes if available (local repository works too).',
8
- usage: '/undo',
9
- aliases: ['u'],
10
- execute: async (): Promise<CommandResult> => {
11
- if (!canUndo()) {
12
- return {
13
- success: false,
14
- content: 'Nothing to undo. The undo stack is empty.',
15
- shouldAddToHistory: false
16
- };
17
- }
18
-
19
- const result = undo();
20
- if (!result) {
21
- return {
22
- success: false,
23
- content: 'Failed to undo. Could not retrieve previous state.',
24
- shouldAddToHistory: false
25
- };
26
- }
27
-
28
- if (!result.success) {
29
- return {
30
- success: false,
31
- content: `Failed to undo file changes:\n${result.error || 'Unknown error'}`,
32
- shouldAddToHistory: false
33
- };
34
- }
35
-
36
- notifyUndoRedo(result.state, 'undo');
37
-
38
- const messageCountText = result.state.messages.length === 0 ? 'conversation cleared' : `${result.state.messages.length} message(s) restored`;
39
-
40
- let details = '';
41
-
42
- if (result.gitChanges && result.gitChanges.length > 0) {
43
- const fileDetails = result.gitChanges.map(f => {
44
- switch (f.status) {
45
- case 'M': return ` • ${f.path} (reverted changes)`;
46
- case 'A': return ` • ${f.path} (restored - was deleted)`;
47
- case 'D': return ` • ${f.path} (deleted - was created)`;
48
- case 'R': return ` • ${f.path} (renamed back)`;
49
- default: return ` • ${f.path} (status: ${f.status})`;
50
- }
51
- }).join('\n');
52
- details = `\n- Files affected (${result.gitChanges.length}):\n${fileDetails}`;
53
- } else if (result.state.fileSnapshots.length > 0) {
54
- const filesAffected = result.state.fileSnapshots.map(f => f.path).join(', ');
55
- const fileDetails = result.state.fileSnapshots.map(f => {
56
- if (!f.existed) {
57
- return ` • ${f.path} (deleted - was created)`;
58
- } else if (f.content === '') {
59
- return ` • ${f.path} (restored - was deleted)`;
60
- } else {
61
- return ` • ${f.path} (restored)`;
62
- }
63
- }).join('\n');
64
- details = `\n- Files affected (${result.state.fileSnapshots.length}):\n${fileDetails}`;
65
- } else if (result.state.gitCommitHash) {
66
- details = `\n- Files reverted via Git (commit: ${result.state.gitCommitHash.slice(0, 7)})`;
67
- }
68
-
69
- return {
70
- success: true,
71
- content: `Undone last user message and all responses.${details}\n- ${messageCountText}`,
72
- shouldAddToHistory: false
73
- };
74
- }
75
- };