@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
package/src/web/app.tsx CHANGED
@@ -1,11 +1,11 @@
1
1
  /** @jsxImportSource react */
2
- import { useState, useEffect, useRef, useCallback } from 'react';
2
+ import { useState, useEffect, useCallback } from 'react';
3
3
  import ReactDOM from 'react-dom/client';
4
4
  import { HomePage } from './components/HomePage';
5
5
  import { ChatPage } from './components/ChatPage';
6
6
  import { Message } from './types';
7
7
  import { createId, extractTitle, setDocumentTitle, formatToolMessage, parseToolHeader, formatErrorMessage, DEFAULT_MAX_TOOL_LINES, getRandomBlendWord } from './utils';
8
- import { Conversation, getAllConversations, getConversation, saveConversation, deleteConversation, createNewConversation } from './storage';
8
+ import { Conversation, getAllConversations, getConversation, saveConversation, deleteConversation, createNewConversation, mergeConversations } from './storage';
9
9
  import { QuestionRequest } from '../utils/questionBridge';
10
10
  import { ApprovalRequest } from '../utils/approvalBridge';
11
11
  import { parseRoute, navigateTo, replaceTo, Route } from './router';
@@ -44,6 +44,7 @@ function App() {
44
44
  const [workspace, setWorkspace] = useState<string | null>(null);
45
45
  const [questionRequest, setQuestionRequest] = useState<QuestionRequest | null>(null);
46
46
  const [approvalRequest, setApprovalRequest] = useState<ApprovalRequest | null>(null);
47
+ const [requireApprovals, setRequireApprovals] = useState(true);
47
48
 
48
49
  const refreshConversations = useCallback(() => {
49
50
  setConversations(getAllConversations());
@@ -77,6 +78,27 @@ function App() {
77
78
  .then(res => res.json())
78
79
  .then(data => setWorkspace(data.workspace))
79
80
  .catch(() => { });
81
+
82
+ fetch('/api/tui-conversations')
83
+ .then(res => res.ok ? res.json() : [])
84
+ .then((data: Conversation[]) => {
85
+ if (Array.isArray(data) && data.length > 0) {
86
+ const changed = mergeConversations(data);
87
+ if (changed) {
88
+ refreshConversations();
89
+ }
90
+ }
91
+ })
92
+ .catch(() => { });
93
+
94
+ fetch('/api/approvals')
95
+ .then(res => res.ok ? res.json() : null)
96
+ .then((data) => {
97
+ if (data && typeof data.requireApprovals === 'boolean') {
98
+ setRequireApprovals(data.requireApprovals);
99
+ }
100
+ })
101
+ .catch(() => { });
80
102
  }, [refreshConversations]);
81
103
 
82
104
  useEffect(() => {
@@ -135,13 +157,14 @@ function App() {
135
157
  }
136
158
  }, [messages, currentTitle]);
137
159
 
138
- const handleSendMessage = async (content: string) => {
139
- if (!content.trim() || isProcessing) return;
160
+ const handleSendMessage = async (content: string, images: Message['images'] = []) => {
161
+ if ((!content.trim() && images.length === 0) || isProcessing) return;
140
162
 
141
163
  const userMessage: Message = {
142
164
  id: createId(),
143
165
  role: 'user',
144
166
  content: content,
167
+ images: images.length > 0 ? images : undefined,
145
168
  };
146
169
 
147
170
  let conversation = currentConversation;
@@ -168,9 +191,10 @@ function App() {
168
191
  headers: { 'Content-Type': 'application/json' },
169
192
  body: JSON.stringify({
170
193
  message: userMessage.content,
194
+ images: userMessage.images || [],
171
195
  history: messages
172
196
  .filter((m) => m.role === 'user' || m.role === 'assistant')
173
- .map((m) => ({ role: m.role, content: m.content })),
197
+ .map((m) => ({ role: m.role, content: m.content, images: m.images || [] })),
174
198
  }),
175
199
  });
176
200
 
@@ -470,6 +494,13 @@ function App() {
470
494
  };
471
495
 
472
496
  const handleDeleteConversation = (conversationId: string) => {
497
+ if (conversationId.startsWith('tui_')) {
498
+ fetch('/api/tui-conversation/delete', {
499
+ method: 'POST',
500
+ headers: { 'Content-Type': 'application/json' },
501
+ body: JSON.stringify({ id: conversationId }),
502
+ }).catch(() => { });
503
+ }
473
504
  deleteConversation(conversationId);
474
505
  refreshConversations();
475
506
 
@@ -494,6 +525,33 @@ function App() {
494
525
  setCurrentTitle(newTitle);
495
526
  setDocumentTitle(newTitle);
496
527
  }
528
+
529
+ if (conversationId.startsWith('tui_')) {
530
+ fetch('/api/tui-conversation/rename', {
531
+ method: 'POST',
532
+ headers: { 'Content-Type': 'application/json' },
533
+ body: JSON.stringify({ id: conversationId, title: newTitle }),
534
+ }).catch(() => { });
535
+ }
536
+ }
537
+ };
538
+
539
+ const handleToggleApprovals = async () => {
540
+ try {
541
+ const next = !requireApprovals;
542
+ const res = await fetch('/api/approvals', {
543
+ method: 'POST',
544
+ headers: { 'Content-Type': 'application/json' },
545
+ body: JSON.stringify({ requireApprovals: next }),
546
+ });
547
+ if (res.ok) {
548
+ setRequireApprovals(next);
549
+ if (!next) {
550
+ setApprovalRequest(null);
551
+ }
552
+ }
553
+ } catch (error) {
554
+ console.error('Failed to toggle approvals:', error);
497
555
  }
498
556
  };
499
557
 
@@ -559,6 +617,8 @@ function App() {
559
617
  workspace={workspace}
560
618
  questionRequest={questionRequest}
561
619
  approvalRequest={approvalRequest}
620
+ requireApprovals={requireApprovals}
621
+ onToggleApprovals={handleToggleApprovals}
562
622
  />
563
623
  )}
564
624
 
@@ -7,17 +7,18 @@
7
7
  overflow: hidden;
8
8
  }
9
9
 
10
- .chat-title-bar {
11
- display: flex;
12
- align-items: center;
13
- padding: 0.75rem 1.5rem;
14
- flex-shrink: 0;
15
- width: 100%;
16
- left: 0;
17
- position: sticky;
18
- top: 0;
19
- z-index: 10;
20
- }
10
+ .chat-title-bar {
11
+ display: flex;
12
+ align-items: center;
13
+ padding: 0.75rem 1.5rem;
14
+ flex-shrink: 0;
15
+ width: 100%;
16
+ left: 0;
17
+ position: sticky;
18
+ top: 0;
19
+ z-index: 10;
20
+ gap: 0.75rem;
21
+ }
21
22
 
22
23
  .chat-title {
23
24
  font-size: 0.9rem;
@@ -27,13 +28,42 @@
27
28
  cursor: default;
28
29
  }
29
30
 
30
- .chat-workspace {
31
- margin-left: auto;
32
- font-size: 0.9rem;
33
- font-family: 'Geist Medium Monospace', monospace;
34
- color: var(--text-secondary);
35
- cursor: default;
36
- }
31
+ .chat-workspace {
32
+ font-size: 0.9rem;
33
+ font-family: 'Geist Medium Monospace', monospace;
34
+ color: var(--text-secondary);
35
+ cursor: default;
36
+ }
37
+
38
+ .chat-title-actions {
39
+ margin-left: auto;
40
+ display: flex;
41
+ align-items: center;
42
+ gap: 0.75rem;
43
+ }
44
+
45
+ .approval-toggle {
46
+ border: 1px solid var(--border-subtle);
47
+ background: transparent;
48
+ color: var(--text-secondary);
49
+ font-size: 0.8rem;
50
+ padding: 0.35rem 0.65rem;
51
+ border-radius: 999px;
52
+ cursor: pointer;
53
+ transition: all 0.15s ease;
54
+ }
55
+
56
+ .approval-toggle:hover {
57
+ background: var(--bg-hover);
58
+ color: var(--text-primary);
59
+ border-color: var(--text-muted);
60
+ }
61
+
62
+ .approval-toggle.active {
63
+ background: var(--accent-color);
64
+ border-color: var(--accent-color);
65
+ color: white;
66
+ }
37
67
 
38
68
  .chat-container {
39
69
  display: flex;
@@ -73,11 +103,11 @@
73
103
  gap: 0;
74
104
  }
75
105
 
76
- .input-area textarea {
77
- width: 100%;
78
- background: transparent;
79
- border: none;
80
- color: var(--text-primary);
106
+ .input-area textarea {
107
+ width: 100%;
108
+ background: transparent;
109
+ border: none;
110
+ color: var(--text-primary);
81
111
  font-size: 1rem;
82
112
  font-family: inherit;
83
113
  resize: none;
@@ -85,12 +115,54 @@
85
115
  min-height: 60px;
86
116
  max-height: 200px;
87
117
  padding: 0;
88
- line-height: 1.5;
89
- }
90
-
91
- .input-area textarea::placeholder {
92
- color: var(--text-muted);
93
- }
118
+ line-height: 1.5;
119
+ }
120
+
121
+ .input-area textarea::placeholder {
122
+ color: var(--text-muted);
123
+ }
124
+
125
+ .attachment-strip {
126
+ display: flex;
127
+ gap: 0.5rem;
128
+ flex-wrap: wrap;
129
+ padding-bottom: 0.5rem;
130
+ }
131
+
132
+ .attachment-item {
133
+ position: relative;
134
+ width: 64px;
135
+ height: 64px;
136
+ border-radius: 10px;
137
+ overflow: hidden;
138
+ border: 1px solid var(--border-code);
139
+ background: var(--bg-code);
140
+ }
141
+
142
+ .attachment-item img {
143
+ width: 100%;
144
+ height: 100%;
145
+ object-fit: cover;
146
+ display: block;
147
+ }
148
+
149
+ .attachment-item button {
150
+ position: absolute;
151
+ top: 4px;
152
+ right: 4px;
153
+ width: 18px;
154
+ height: 18px;
155
+ border-radius: 50%;
156
+ border: none;
157
+ background: rgba(0, 0, 0, 0.6);
158
+ color: white;
159
+ font-size: 12px;
160
+ cursor: pointer;
161
+ }
162
+
163
+ .attachment-item button:hover {
164
+ background: rgba(0, 0, 0, 0.8);
165
+ }
94
166
 
95
167
  .input-area textarea:disabled {
96
168
  opacity: 0.5;
@@ -209,4 +281,4 @@
209
281
 
210
282
  .send-btn.stop:hover {
211
283
  background: #ff3333;
212
- }
284
+ }
@@ -59,12 +59,31 @@
59
59
  background: var(--status-error-dark);
60
60
  }
61
61
 
62
- .message-content {
63
- color: var(--text-primary);
64
- white-space: pre-wrap;
65
- word-break: break-word;
66
- flex: 1;
67
- }
62
+ .message-content {
63
+ color: var(--text-primary);
64
+ white-space: pre-wrap;
65
+ word-break: break-word;
66
+ flex: 1;
67
+ }
68
+
69
+ .message-images {
70
+ display: flex;
71
+ gap: 0.5rem;
72
+ flex-wrap: wrap;
73
+ margin-bottom: 0.4rem;
74
+ }
75
+
76
+ .message-images img {
77
+ width: 140px;
78
+ height: auto;
79
+ border-radius: 8px;
80
+ border: 1px solid var(--border-code);
81
+ background: var(--bg-code);
82
+ }
83
+
84
+ .message-text {
85
+ white-space: pre-wrap;
86
+ }
68
87
 
69
88
  .thinking-section {
70
89
  margin-bottom: 0.35rem;
@@ -95,7 +114,6 @@
95
114
  font-size: 0.8rem;
96
115
  color: var(--text-muted);
97
116
  overflow-x: auto;
98
- line-height: 1.4;
99
117
  }
100
118
 
101
119
  .assistant-text {
@@ -136,25 +154,4 @@
136
154
  40% {
137
155
  transform: scale(1);
138
156
  }
139
- }
140
-
141
- .blend-indicator {
142
- display: flex;
143
- align-items: center;
144
- gap: 1rem;
145
- padding: 1rem 1.5rem;
146
- margin: 0.75rem 0;
147
- }
148
-
149
- .blend-icon {
150
- width: 48px;
151
- height: 48px;
152
- fill: var(--text-muted);
153
- flex-shrink: 0;
154
- }
155
-
156
- .blend-text {
157
- font-size: 1.5rem;
158
- color: var(--text-muted);
159
- font-weight: 400;
160
- }
157
+ }
@@ -1,8 +1,14 @@
1
+ .thinking-block {
2
+ display: flex;
3
+ flex-direction: column;
4
+ gap: 0.15rem;
5
+ }
6
+
1
7
  .thinking-indicator {
2
8
  display: flex;
3
9
  align-items: center;
4
10
  gap: 0;
5
- padding: 0.85rem 1.15rem;
11
+ padding: 0.65rem 1.15rem 0.35rem 1.15rem;
6
12
  font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
7
13
  font-size: 0.95rem;
8
14
  }
@@ -31,17 +37,49 @@
31
37
  .thinking-elapsed {
32
38
  color: var(--text-secondary);
33
39
  opacity: 0.7;
34
- margin-left: 0.25rem;
35
40
  }
36
41
 
37
42
  .thinking-hint {
38
43
  color: var(--text-secondary);
39
- opacity: 0.5;
40
- margin-left: 0.25rem;
44
+ opacity: 0.55;
41
45
  }
42
46
 
43
47
  .thinking-tokens {
44
48
  color: var(--text-secondary);
45
49
  opacity: 0.7;
46
- margin-left: 0.25rem;
47
- }
50
+ }
51
+
52
+ .thinking-plan {
53
+ color: var(--text-secondary);
54
+ opacity: 0.7;
55
+ }
56
+
57
+ .thinking-sep {
58
+ color: var(--text-secondary);
59
+ opacity: 0.6;
60
+ }
61
+
62
+ .thinking-next-line {
63
+ display: flex;
64
+ align-items: center;
65
+ gap: 0.35rem;
66
+ padding: 0 1.15rem 0.6rem 1.15rem;
67
+ font-family: 'SF Mono', 'Fira Code', 'Consolas', monospace;
68
+ font-size: 0.9rem;
69
+ color: var(--text-secondary);
70
+ }
71
+
72
+ .thinking-next-icon {
73
+ color: #ffca38;
74
+ font-weight: 700;
75
+ }
76
+
77
+ .thinking-next-label {
78
+ font-weight: 700;
79
+ color: #ffca38;
80
+ }
81
+
82
+ .thinking-next-step {
83
+ color: var(--text-secondary);
84
+ opacity: 0.75;
85
+ }
@@ -12,14 +12,24 @@
12
12
  border: 1px solid var(--border-tool-running);
13
13
  }
14
14
 
15
- .message.tool.error {
16
- background: var(--bg-tool-error);
17
- border: 1px solid var(--border-tool-error);
18
- }
19
-
20
- .message.tool.success .message-bar {
21
- background: var(--status-success);
22
- }
15
+ .message.tool.error {
16
+ background: var(--bg-tool-error);
17
+ border: 1px solid var(--border-tool-error);
18
+ }
19
+
20
+ .message.tool.plan-tool,
21
+ .message.tool.plan-tool.running,
22
+ .message.tool.plan-tool.error,
23
+ .message.tool.plan-tool.success {
24
+ background: transparent;
25
+ border: none;
26
+ backdrop-filter: none;
27
+ -webkit-backdrop-filter: none;
28
+ }
29
+
30
+ .message.tool.success .message-bar {
31
+ background: var(--status-success);
32
+ }
23
33
 
24
34
  .message.tool.running .message-bar {
25
35
  background: var(--status-running);
@@ -107,11 +117,23 @@
107
117
  line-height: 1.4;
108
118
  }
109
119
 
110
- .tool-line {
111
- padding: 0.1rem 0.5rem;
112
- white-space: pre-wrap;
113
- word-break: break-all;
114
- }
120
+ .tool-line {
121
+ padding: 0.1rem 0.5rem;
122
+ white-space: pre-wrap;
123
+ word-break: break-all;
124
+ }
125
+
126
+ .tool-line.plan-line {
127
+ padding-left: 0.9rem;
128
+ }
129
+
130
+ .plan-prefix {
131
+ color: #ffca38;
132
+ }
133
+
134
+ .plan-bracket.active {
135
+ color: #ffca38;
136
+ }
115
137
 
116
138
  .tool-line.diff-line {
117
139
  display: flex;
@@ -145,4 +167,4 @@
145
167
  .diff-content {
146
168
  flex: 1;
147
169
  padding: 0.1rem 0.3rem;
148
- }
170
+ }