@geminilight/mindos 0.5.64 → 0.5.65

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 (85) hide show
  1. package/README.md +4 -0
  2. package/README_zh.md +4 -0
  3. package/app/app/api/ask/route.ts +12 -0
  4. package/app/app/api/file/route.ts +9 -0
  5. package/app/app/api/mcp/agents/route.ts +27 -1
  6. package/app/app/api/skills/route.ts +18 -2
  7. package/app/app/api/tree-version/route.ts +8 -0
  8. package/app/components/ActivityBar.tsx +2 -2
  9. package/app/components/Backlinks.tsx +5 -5
  10. package/app/components/CreateSpaceModal.tsx +3 -2
  11. package/app/components/DirPicker.tsx +1 -1
  12. package/app/components/DirView.tsx +2 -3
  13. package/app/components/EditorWrapper.tsx +3 -3
  14. package/app/components/FileTree.tsx +25 -10
  15. package/app/components/GuideCard.tsx +4 -4
  16. package/app/components/HomeContent.tsx +6 -11
  17. package/app/components/MarkdownView.tsx +2 -2
  18. package/app/components/OnboardingView.tsx +1 -1
  19. package/app/components/Panel.tsx +1 -1
  20. package/app/components/RightAgentDetailPanel.tsx +1 -1
  21. package/app/components/RightAskPanel.tsx +1 -1
  22. package/app/components/SearchModal.tsx +10 -2
  23. package/app/components/SidebarLayout.tsx +35 -10
  24. package/app/components/ThemeToggle.tsx +1 -1
  25. package/app/components/agents/AgentDetailContent.tsx +454 -59
  26. package/app/components/agents/AgentsContentPage.tsx +70 -5
  27. package/app/components/agents/AgentsMcpSection.tsx +474 -159
  28. package/app/components/agents/AgentsOverviewSection.tsx +418 -59
  29. package/app/components/agents/AgentsPrimitives.tsx +335 -0
  30. package/app/components/agents/AgentsSkillsSection.tsx +739 -121
  31. package/app/components/agents/SkillDetailPopover.tsx +416 -0
  32. package/app/components/agents/agents-content-model.ts +292 -10
  33. package/app/components/ask/AskContent.tsx +34 -5
  34. package/app/components/ask/FileChip.tsx +1 -0
  35. package/app/components/ask/MentionPopover.tsx +13 -1
  36. package/app/components/ask/MessageList.tsx +5 -7
  37. package/app/components/ask/ToolCallBlock.tsx +4 -4
  38. package/app/components/changes/ChangesBanner.tsx +1 -2
  39. package/app/components/echo/EchoHero.tsx +10 -24
  40. package/app/components/echo/EchoInsightCollapsible.tsx +52 -43
  41. package/app/components/echo/EchoPageSections.tsx +13 -9
  42. package/app/components/echo/EchoSegmentNav.tsx +14 -11
  43. package/app/components/echo/EchoSegmentPageClient.tsx +64 -43
  44. package/app/components/explore/ExploreContent.tsx +3 -7
  45. package/app/components/explore/UseCaseCard.tsx +4 -15
  46. package/app/components/panels/AgentsPanel.tsx +12 -104
  47. package/app/components/panels/AgentsPanelAgentDetail.tsx +2 -2
  48. package/app/components/panels/AgentsPanelAgentGroups.tsx +3 -7
  49. package/app/components/panels/AgentsPanelAgentListRow.tsx +9 -11
  50. package/app/components/panels/EchoPanel.tsx +8 -10
  51. package/app/components/panels/PanelNavRow.tsx +9 -2
  52. package/app/components/panels/PluginsPanel.tsx +2 -2
  53. package/app/components/renderers/agent-inspector/AgentInspectorRenderer.tsx +30 -8
  54. package/app/components/renderers/agent-inspector/manifest.ts +3 -3
  55. package/app/components/renderers/todo/manifest.ts +1 -0
  56. package/app/components/settings/AiTab.tsx +3 -3
  57. package/app/components/settings/AppearanceTab.tsx +2 -2
  58. package/app/components/settings/KnowledgeTab.tsx +3 -3
  59. package/app/components/settings/McpAgentInstall.tsx +3 -6
  60. package/app/components/settings/McpSkillCreateForm.tsx +2 -3
  61. package/app/components/settings/McpSkillRow.tsx +2 -3
  62. package/app/components/settings/McpSkillsSection.tsx +2 -2
  63. package/app/components/settings/McpTab.tsx +12 -13
  64. package/app/components/settings/MonitoringTab.tsx +13 -13
  65. package/app/components/settings/PluginsTab.tsx +2 -2
  66. package/app/components/settings/Primitives.tsx +3 -4
  67. package/app/components/settings/SettingsContent.tsx +3 -3
  68. package/app/components/settings/SyncTab.tsx +11 -17
  69. package/app/components/settings/UpdateTab.tsx +18 -21
  70. package/app/components/settings/types.ts +14 -0
  71. package/app/components/setup/StepKB.tsx +1 -1
  72. package/app/hooks/useMcpData.tsx +4 -2
  73. package/app/hooks/useMention.ts +25 -8
  74. package/app/lib/agent/log.ts +15 -18
  75. package/app/lib/agent/stream-consumer.ts +3 -0
  76. package/app/lib/agent/to-agent-messages.ts +6 -4
  77. package/app/lib/core/agent-audit-log.ts +280 -0
  78. package/app/lib/core/index.ts +11 -0
  79. package/app/lib/fs.ts +9 -0
  80. package/app/lib/i18n-en.ts +259 -33
  81. package/app/lib/i18n-zh.ts +258 -32
  82. package/app/lib/mcp-agents.ts +231 -2
  83. package/app/lib/types.ts +2 -0
  84. package/package.json +1 -1
  85. package/scripts/migrate-agent-audit-log.js +170 -0
@@ -0,0 +1,335 @@
1
+ 'use client';
2
+
3
+ import { Loader2, Plus, X } from 'lucide-react';
4
+ import { useEffect, useRef } from 'react';
5
+
6
+ /* ────────── Pill / Status / Search ────────── */
7
+
8
+ export function PillButton({ active, label, onClick }: { active: boolean; label: string; onClick: () => void }) {
9
+ return (
10
+ <button
11
+ type="button"
12
+ onClick={onClick}
13
+ aria-pressed={active}
14
+ className={`px-2.5 min-h-[28px] rounded text-xs cursor-pointer transition-colors duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring ${
15
+ active ? 'bg-[var(--amber-dim)] text-[var(--amber)] font-medium' : 'text-muted-foreground hover:text-foreground hover:bg-muted'
16
+ }`}
17
+ >
18
+ {label}
19
+ </button>
20
+ );
21
+ }
22
+
23
+ export function StatusDot({ tone, label, count }: { tone: 'ok' | 'warn' | 'neutral'; label: string; count: number }) {
24
+ const dotCls = tone === 'ok' ? 'bg-[var(--success)]' : tone === 'warn' ? 'bg-[var(--amber)]' : 'bg-muted-foreground';
25
+ return (
26
+ <span className="inline-flex items-center gap-1.5 text-muted-foreground">
27
+ <span className={`w-1.5 h-1.5 rounded-full ${dotCls}`} aria-hidden="true" />
28
+ {label} <span className="tabular-nums text-foreground">{count}</span>
29
+ </span>
30
+ );
31
+ }
32
+
33
+ export function SearchInput({
34
+ value,
35
+ onChange,
36
+ placeholder,
37
+ ariaLabel,
38
+ icon: Icon,
39
+ }: {
40
+ value: string;
41
+ onChange: (v: string) => void;
42
+ placeholder: string;
43
+ ariaLabel: string;
44
+ icon: React.ComponentType<{ size?: number; className?: string }>;
45
+ }) {
46
+ return (
47
+ <label className="relative block">
48
+ <Icon size={14} className="absolute left-2.5 top-1/2 -translate-y-1/2 text-muted-foreground pointer-events-none" />
49
+ <input
50
+ value={value}
51
+ onChange={(e) => onChange(e.target.value)}
52
+ placeholder={placeholder}
53
+ aria-label={ariaLabel}
54
+ className="w-full h-9 rounded-md border border-border bg-background pl-8 pr-8 text-sm text-foreground placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring transition-colors duration-150"
55
+ />
56
+ {value.length > 0 && (
57
+ <button
58
+ type="button"
59
+ onClick={() => onChange('')}
60
+ aria-label="Clear search"
61
+ className="absolute right-2 top-1/2 -translate-y-1/2 p-0.5 rounded-sm text-muted-foreground hover:text-foreground cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring transition-colors duration-150"
62
+ >
63
+ <X size={14} />
64
+ </button>
65
+ )}
66
+ </label>
67
+ );
68
+ }
69
+
70
+ /* ────────── Action / Bulk / Empty ────────── */
71
+
72
+ export function ActionButton({
73
+ onClick,
74
+ disabled,
75
+ busy,
76
+ label,
77
+ busyLabel,
78
+ variant = 'default',
79
+ }: {
80
+ onClick: () => void;
81
+ disabled: boolean;
82
+ busy: boolean;
83
+ label: string;
84
+ busyLabel?: string;
85
+ variant?: 'default' | 'primary';
86
+ }) {
87
+ const base = 'inline-flex items-center justify-center gap-1.5 text-2xs min-h-[28px] px-2.5 rounded-md cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring disabled:opacity-50 disabled:cursor-not-allowed transition-colors duration-150';
88
+ const variants = {
89
+ default: 'border border-border hover:bg-muted',
90
+ primary: 'bg-[var(--amber)] text-background hover:bg-[var(--amber)]/90',
91
+ };
92
+ return (
93
+ <button
94
+ type="button"
95
+ onClick={onClick}
96
+ disabled={disabled}
97
+ aria-busy={busy}
98
+ className={`${base} ${variants[variant]}`}
99
+ >
100
+ {busy && <Loader2 size={12} className="animate-spin" aria-hidden="true" />}
101
+ {busy ? (busyLabel ?? label) : label}
102
+ </button>
103
+ );
104
+ }
105
+
106
+ export function BulkMessage({ message }: { message: string | null }) {
107
+ if (!message) return null;
108
+ return (
109
+ <span role="status" aria-live="polite" className="text-2xs text-muted-foreground animate-in fade-in duration-200">
110
+ {message}
111
+ </span>
112
+ );
113
+ }
114
+
115
+ export function EmptyState({ message, className }: { message: string; className?: string }) {
116
+ return (
117
+ <div className={`rounded-lg border border-dashed border-border bg-card/50 p-8 text-center ${className ?? ''}`}>
118
+ <p className="text-sm text-muted-foreground">{message}</p>
119
+ </div>
120
+ );
121
+ }
122
+
123
+ /* ────────── Agent Avatar ────────── */
124
+
125
+ const AVATAR_COLORS = [
126
+ 'bg-blue-500', 'bg-emerald-500', 'bg-violet-500', 'bg-rose-500',
127
+ 'bg-amber-600', 'bg-cyan-600', 'bg-indigo-500', 'bg-pink-500',
128
+ 'bg-teal-500', 'bg-orange-500', 'bg-sky-500', 'bg-fuchsia-500',
129
+ ];
130
+
131
+ function hashName(str: string): number {
132
+ let h = 0;
133
+ for (let i = 0; i < str.length; i++) h = (h * 31 + str.charCodeAt(i)) | 0;
134
+ return Math.abs(h);
135
+ }
136
+
137
+ function initials(name: string): string {
138
+ const parts = name.split(/[\s\-_]+/).filter(Boolean);
139
+ if (parts.length >= 2) return (parts[0][0] + parts[1][0]).toUpperCase();
140
+ return name.slice(0, 2).toUpperCase();
141
+ }
142
+
143
+ export function AgentAvatar({
144
+ name,
145
+ status,
146
+ size = 'md',
147
+ onRemove,
148
+ href,
149
+ }: {
150
+ name: string;
151
+ status?: 'connected' | 'detected' | 'notFound';
152
+ size?: 'sm' | 'md';
153
+ onRemove?: () => void;
154
+ href?: string;
155
+ }) {
156
+ const color = AVATAR_COLORS[hashName(name) % AVATAR_COLORS.length];
157
+ const sizeClasses = size === 'sm' ? 'w-7 h-7 text-[10px]' : 'w-9 h-9 text-xs';
158
+ const dotColor = status === 'connected' ? 'bg-[var(--success)]' : status === 'detected' ? 'bg-[var(--amber)]' : 'bg-muted-foreground';
159
+
160
+ return (
161
+ <div className="relative group/avatar" title={name}>
162
+ <div className={`${sizeClasses} ${color} rounded-full flex items-center justify-center text-white font-medium select-none shadow-sm`}>
163
+ {initials(name)}
164
+ </div>
165
+ {status && (
166
+ <span className={`absolute -bottom-0.5 -right-0.5 w-2.5 h-2.5 rounded-full border-2 border-card ${dotColor}`} aria-hidden="true" />
167
+ )}
168
+ {onRemove && (
169
+ <button
170
+ type="button"
171
+ onClick={(e) => { e.preventDefault(); e.stopPropagation(); onRemove(); }}
172
+ className="absolute -top-1 -right-1 w-4 h-4 rounded-full bg-destructive text-white flex items-center justify-center opacity-0 scale-75 group-hover/avatar:opacity-100 group-hover/avatar:scale-100 transition-all duration-150 cursor-pointer focus-visible:opacity-100 focus-visible:scale-100 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
173
+ aria-label={`Remove ${name}`}
174
+ >
175
+ <X size={9} />
176
+ </button>
177
+ )}
178
+ </div>
179
+ );
180
+ }
181
+
182
+ export function AddAvatarButton({
183
+ onClick,
184
+ label,
185
+ size = 'md',
186
+ }: {
187
+ onClick: () => void;
188
+ label: string;
189
+ size?: 'sm' | 'md';
190
+ }) {
191
+ const sizeClasses = size === 'sm' ? 'w-7 h-7' : 'w-9 h-9';
192
+ return (
193
+ <button
194
+ type="button"
195
+ onClick={onClick}
196
+ title={label}
197
+ aria-label={label}
198
+ className={`${sizeClasses} rounded-full border-2 border-dashed border-border flex items-center justify-center text-muted-foreground hover:text-foreground hover:border-foreground/30 hover:bg-muted cursor-pointer transition-all duration-150 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring`}
199
+ >
200
+ <Plus size={14} />
201
+ </button>
202
+ );
203
+ }
204
+
205
+ /* ────────── Confirm Dialog ────────── */
206
+
207
+ export function ConfirmDialog({
208
+ open,
209
+ title,
210
+ message,
211
+ confirmLabel,
212
+ cancelLabel,
213
+ onConfirm,
214
+ onCancel,
215
+ variant = 'destructive',
216
+ }: {
217
+ open: boolean;
218
+ title: string;
219
+ message: string;
220
+ confirmLabel: string;
221
+ cancelLabel: string;
222
+ onConfirm: () => void;
223
+ onCancel: () => void;
224
+ variant?: 'destructive' | 'default';
225
+ }) {
226
+ const cancelRef = useRef<HTMLButtonElement>(null);
227
+
228
+ useEffect(() => {
229
+ if (open) cancelRef.current?.focus();
230
+ }, [open]);
231
+
232
+ useEffect(() => {
233
+ if (!open) return;
234
+ function onKey(e: KeyboardEvent) {
235
+ if (e.key === 'Escape') onCancel();
236
+ }
237
+ document.addEventListener('keydown', onKey);
238
+ return () => document.removeEventListener('keydown', onKey);
239
+ }, [open, onCancel]);
240
+
241
+ if (!open) return null;
242
+
243
+ return (
244
+ <div className="fixed inset-0 z-50 flex items-center justify-center" role="alertdialog" aria-modal="true" aria-label={title}>
245
+ <div className="absolute inset-0 bg-black/40 backdrop-blur-[2px]" onClick={onCancel} aria-hidden="true" />
246
+ <div className="relative bg-card border border-border rounded-lg shadow-xl p-5 max-w-sm w-full mx-4 animate-in fade-in zoom-in-95 duration-200">
247
+ <h3 className="text-sm font-medium text-foreground mb-1.5">{title}</h3>
248
+ <p className="text-sm text-muted-foreground mb-4 leading-relaxed">{message}</p>
249
+ <div className="flex justify-end gap-2">
250
+ <button
251
+ ref={cancelRef}
252
+ type="button"
253
+ onClick={onCancel}
254
+ className="px-3 min-h-[32px] text-sm rounded-md border border-border hover:bg-muted cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring transition-colors duration-150"
255
+ >
256
+ {cancelLabel}
257
+ </button>
258
+ <button
259
+ type="button"
260
+ onClick={onConfirm}
261
+ className={`px-3 min-h-[32px] text-sm rounded-md cursor-pointer focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring transition-colors duration-150 ${
262
+ variant === 'destructive'
263
+ ? 'bg-destructive text-destructive-foreground hover:bg-destructive/90'
264
+ : 'bg-[var(--amber)] text-background hover:bg-[var(--amber)]/90'
265
+ }`}
266
+ >
267
+ {confirmLabel}
268
+ </button>
269
+ </div>
270
+ </div>
271
+ </div>
272
+ );
273
+ }
274
+
275
+ /* ────────── Agent Picker Popover ────────── */
276
+
277
+ export function AgentPickerPopover({
278
+ open,
279
+ agents,
280
+ emptyLabel,
281
+ onSelect,
282
+ onClose,
283
+ }: {
284
+ open: boolean;
285
+ agents: Array<{ key: string; name: string }>;
286
+ emptyLabel: string;
287
+ onSelect: (agentKey: string) => void;
288
+ onClose: () => void;
289
+ }) {
290
+ const ref = useRef<HTMLDivElement>(null);
291
+
292
+ useEffect(() => {
293
+ if (!open) return;
294
+ function onClick(e: MouseEvent) {
295
+ if (ref.current && !ref.current.contains(e.target as Node)) onClose();
296
+ }
297
+ document.addEventListener('mousedown', onClick);
298
+ return () => document.removeEventListener('mousedown', onClick);
299
+ }, [open, onClose]);
300
+
301
+ useEffect(() => {
302
+ if (!open) return;
303
+ function onKey(e: KeyboardEvent) {
304
+ if (e.key === 'Escape') onClose();
305
+ }
306
+ document.addEventListener('keydown', onKey);
307
+ return () => document.removeEventListener('keydown', onKey);
308
+ }, [open, onClose]);
309
+
310
+ if (!open) return null;
311
+
312
+ return (
313
+ <div ref={ref} className="absolute right-0 top-full mt-1 z-30 w-56 rounded-lg border border-border bg-card shadow-lg animate-in fade-in slide-in-from-top-1 duration-150">
314
+ {agents.length === 0 ? (
315
+ <p className="px-3 py-2.5 text-xs text-muted-foreground">{emptyLabel}</p>
316
+ ) : (
317
+ <div className="py-1 max-h-48 overflow-y-auto">
318
+ {agents.map((agent) => (
319
+ <button
320
+ key={agent.key}
321
+ type="button"
322
+ onClick={() => onSelect(agent.key)}
323
+ className="w-full text-left px-3 py-2 text-xs text-foreground hover:bg-muted cursor-pointer flex items-center gap-2 transition-colors duration-100"
324
+ >
325
+ <div className={`w-6 h-6 rounded-full ${AVATAR_COLORS[hashName(agent.name) % AVATAR_COLORS.length]} flex items-center justify-center text-white text-[9px] font-medium shrink-0`}>
326
+ {initials(agent.name)}
327
+ </div>
328
+ {agent.name}
329
+ </button>
330
+ ))}
331
+ </div>
332
+ )}
333
+ </div>
334
+ );
335
+ }