@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.
- package/README.md +4 -0
- package/README_zh.md +4 -0
- package/app/app/api/ask/route.ts +12 -0
- package/app/app/api/file/route.ts +9 -0
- package/app/app/api/mcp/agents/route.ts +27 -1
- package/app/app/api/skills/route.ts +18 -2
- package/app/app/api/tree-version/route.ts +8 -0
- package/app/components/ActivityBar.tsx +2 -2
- package/app/components/Backlinks.tsx +5 -5
- package/app/components/CreateSpaceModal.tsx +3 -2
- package/app/components/DirPicker.tsx +1 -1
- package/app/components/DirView.tsx +2 -3
- package/app/components/EditorWrapper.tsx +3 -3
- package/app/components/FileTree.tsx +25 -10
- package/app/components/GuideCard.tsx +4 -4
- package/app/components/HomeContent.tsx +6 -11
- package/app/components/MarkdownView.tsx +2 -2
- package/app/components/OnboardingView.tsx +1 -1
- package/app/components/Panel.tsx +1 -1
- package/app/components/RightAgentDetailPanel.tsx +1 -1
- package/app/components/RightAskPanel.tsx +1 -1
- package/app/components/SearchModal.tsx +10 -2
- package/app/components/SidebarLayout.tsx +35 -10
- package/app/components/ThemeToggle.tsx +1 -1
- package/app/components/agents/AgentDetailContent.tsx +454 -59
- package/app/components/agents/AgentsContentPage.tsx +70 -5
- package/app/components/agents/AgentsMcpSection.tsx +474 -159
- package/app/components/agents/AgentsOverviewSection.tsx +418 -59
- package/app/components/agents/AgentsPrimitives.tsx +335 -0
- package/app/components/agents/AgentsSkillsSection.tsx +739 -121
- package/app/components/agents/SkillDetailPopover.tsx +416 -0
- package/app/components/agents/agents-content-model.ts +292 -10
- package/app/components/ask/AskContent.tsx +34 -5
- package/app/components/ask/FileChip.tsx +1 -0
- package/app/components/ask/MentionPopover.tsx +13 -1
- package/app/components/ask/MessageList.tsx +5 -7
- package/app/components/ask/ToolCallBlock.tsx +4 -4
- package/app/components/changes/ChangesBanner.tsx +1 -2
- package/app/components/echo/EchoHero.tsx +10 -24
- package/app/components/echo/EchoInsightCollapsible.tsx +52 -43
- package/app/components/echo/EchoPageSections.tsx +13 -9
- package/app/components/echo/EchoSegmentNav.tsx +14 -11
- package/app/components/echo/EchoSegmentPageClient.tsx +64 -43
- package/app/components/explore/ExploreContent.tsx +3 -7
- package/app/components/explore/UseCaseCard.tsx +4 -15
- package/app/components/panels/AgentsPanel.tsx +12 -104
- package/app/components/panels/AgentsPanelAgentDetail.tsx +2 -2
- package/app/components/panels/AgentsPanelAgentGroups.tsx +3 -7
- package/app/components/panels/AgentsPanelAgentListRow.tsx +9 -11
- package/app/components/panels/EchoPanel.tsx +8 -10
- package/app/components/panels/PanelNavRow.tsx +9 -2
- package/app/components/panels/PluginsPanel.tsx +2 -2
- package/app/components/renderers/agent-inspector/AgentInspectorRenderer.tsx +30 -8
- package/app/components/renderers/agent-inspector/manifest.ts +3 -3
- package/app/components/renderers/todo/manifest.ts +1 -0
- package/app/components/settings/AiTab.tsx +3 -3
- package/app/components/settings/AppearanceTab.tsx +2 -2
- package/app/components/settings/KnowledgeTab.tsx +3 -3
- package/app/components/settings/McpAgentInstall.tsx +3 -6
- package/app/components/settings/McpSkillCreateForm.tsx +2 -3
- package/app/components/settings/McpSkillRow.tsx +2 -3
- package/app/components/settings/McpSkillsSection.tsx +2 -2
- package/app/components/settings/McpTab.tsx +12 -13
- package/app/components/settings/MonitoringTab.tsx +13 -13
- package/app/components/settings/PluginsTab.tsx +2 -2
- package/app/components/settings/Primitives.tsx +3 -4
- package/app/components/settings/SettingsContent.tsx +3 -3
- package/app/components/settings/SyncTab.tsx +11 -17
- package/app/components/settings/UpdateTab.tsx +18 -21
- package/app/components/settings/types.ts +14 -0
- package/app/components/setup/StepKB.tsx +1 -1
- package/app/hooks/useMcpData.tsx +4 -2
- package/app/hooks/useMention.ts +25 -8
- package/app/lib/agent/log.ts +15 -18
- package/app/lib/agent/stream-consumer.ts +3 -0
- package/app/lib/agent/to-agent-messages.ts +6 -4
- package/app/lib/core/agent-audit-log.ts +280 -0
- package/app/lib/core/index.ts +11 -0
- package/app/lib/fs.ts +9 -0
- package/app/lib/i18n-en.ts +259 -33
- package/app/lib/i18n-zh.ts +258 -32
- package/app/lib/mcp-agents.ts +231 -2
- package/app/lib/types.ts +2 -0
- package/package.json +1 -1
- package/scripts/migrate-agent-audit-log.js +170 -0
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { useState } from 'react';
|
|
4
|
-
import {
|
|
4
|
+
import { usePathname } from 'next/navigation';
|
|
5
|
+
import { Loader2, RefreshCw, Settings } from 'lucide-react';
|
|
5
6
|
import { useMcpData } from '@/hooks/useMcpData';
|
|
6
7
|
import { useLocale } from '@/lib/LocaleContext';
|
|
7
|
-
import { Toggle } from '../settings/Primitives';
|
|
8
|
-
import type { SkillInfo } from '../settings/types';
|
|
9
8
|
import PanelHeader from './PanelHeader';
|
|
10
9
|
import { AgentsPanelHubNav } from './AgentsPanelHubNav';
|
|
11
10
|
import { AgentsPanelAgentGroups } from './AgentsPanelAgentGroups';
|
|
@@ -14,9 +13,7 @@ interface AgentsPanelProps {
|
|
|
14
13
|
active: boolean;
|
|
15
14
|
maximized?: boolean;
|
|
16
15
|
onMaximize?: () => void;
|
|
17
|
-
/** Highlights the row for the agent whose detail is open in the right dock. */
|
|
18
16
|
selectedAgentKey?: string | null;
|
|
19
|
-
onOpenAgentDetail?: (key: string) => void;
|
|
20
17
|
}
|
|
21
18
|
|
|
22
19
|
export default function AgentsPanel({
|
|
@@ -24,14 +21,13 @@ export default function AgentsPanel({
|
|
|
24
21
|
maximized,
|
|
25
22
|
onMaximize,
|
|
26
23
|
selectedAgentKey = null,
|
|
27
|
-
onOpenAgentDetail,
|
|
28
24
|
}: AgentsPanelProps) {
|
|
29
25
|
const { t } = useLocale();
|
|
30
26
|
const p = t.panels.agents;
|
|
31
27
|
const mcp = useMcpData();
|
|
28
|
+
const pathname = usePathname();
|
|
32
29
|
const [refreshing, setRefreshing] = useState(false);
|
|
33
30
|
const [showNotDetected, setShowNotDetected] = useState(false);
|
|
34
|
-
const [showBuiltinSkills, setShowBuiltinSkills] = useState(false);
|
|
35
31
|
|
|
36
32
|
const handleRefresh = async () => {
|
|
37
33
|
setRefreshing(true);
|
|
@@ -47,15 +43,17 @@ export default function AgentsPanel({
|
|
|
47
43
|
const detected = mcp.agents.filter(a => a.present && !a.installed);
|
|
48
44
|
const notFound = mcp.agents.filter(a => !a.present);
|
|
49
45
|
|
|
50
|
-
const customSkills = mcp.skills.filter(s => s.source === 'user');
|
|
51
|
-
const builtinSkills = mcp.skills.filter(s => s.source === 'builtin');
|
|
52
|
-
const activeSkillCount = mcp.skills.filter(s => s.enabled).length;
|
|
53
46
|
const installAgentWithRefresh = async (key: string) => {
|
|
54
47
|
const ok = await mcp.installAgent(key);
|
|
55
48
|
if (ok) await mcp.refresh();
|
|
56
49
|
return ok;
|
|
57
50
|
};
|
|
58
51
|
|
|
52
|
+
const routeSelectedAgentKey = pathname?.startsWith('/agents/')
|
|
53
|
+
? decodeURIComponent(pathname.slice('/agents/'.length))
|
|
54
|
+
: null;
|
|
55
|
+
const effectiveSelectedAgentKey = routeSelectedAgentKey ?? selectedAgentKey;
|
|
56
|
+
|
|
59
57
|
const listCopy = {
|
|
60
58
|
installing: p.installing,
|
|
61
59
|
install: p.install,
|
|
@@ -108,28 +106,13 @@ export default function AgentsPanel({
|
|
|
108
106
|
<div className="flex flex-col gap-2 py-4 px-0">
|
|
109
107
|
{hub}
|
|
110
108
|
<div className="mx-4 border-t border-border" />
|
|
111
|
-
<div className="mx-3 rounded-lg border border-border
|
|
112
|
-
<
|
|
113
|
-
{mcp.status?.running ? (
|
|
114
|
-
<span className="flex items-center gap-1.5 text-[11px]">
|
|
115
|
-
<span className="w-1.5 h-1.5 rounded-full bg-emerald-500 inline-block" />
|
|
116
|
-
<span className="text-emerald-600 dark:text-emerald-400">:{mcp.status.port}</span>
|
|
117
|
-
<span className="text-muted-foreground">· {mcp.status.toolCount} tools</span>
|
|
118
|
-
</span>
|
|
119
|
-
) : (
|
|
120
|
-
<span className="flex items-center gap-1.5 text-[11px]">
|
|
121
|
-
<span className="w-1.5 h-1.5 rounded-full bg-zinc-400 inline-block" />
|
|
122
|
-
<span className="text-muted-foreground">{p.stopped}</span>
|
|
123
|
-
</span>
|
|
124
|
-
)}
|
|
125
|
-
</div>
|
|
126
|
-
<div className="mx-3 rounded-lg border border-dashed border-border px-3 py-3 text-center">
|
|
127
|
-
<p className="text-xs text-muted-foreground mb-2">{p.noAgents}</p>
|
|
109
|
+
<div className="mx-3 rounded-lg border border-dashed border-border px-3 py-4 text-center">
|
|
110
|
+
<p className="text-xs text-muted-foreground mb-1.5">{p.noAgents}</p>
|
|
128
111
|
<p className="text-2xs text-muted-foreground mb-3">{p.skillsEmptyHint}</p>
|
|
129
112
|
<button
|
|
130
113
|
onClick={handleRefresh}
|
|
131
114
|
type="button"
|
|
132
|
-
className="inline-flex items-center gap-1.5 px-2.5 py-1 text-xs rounded-lg border border-border text-muted-foreground hover:text-foreground hover:bg-muted transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
115
|
+
className="inline-flex items-center gap-1.5 px-2.5 py-1.5 text-xs rounded-lg border border-border text-muted-foreground hover:text-foreground hover:bg-muted transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
133
116
|
>
|
|
134
117
|
<RefreshCw size={11} /> {p.retry}
|
|
135
118
|
</button>
|
|
@@ -142,28 +125,11 @@ export default function AgentsPanel({
|
|
|
142
125
|
<div className="mx-4 border-t border-border" />
|
|
143
126
|
|
|
144
127
|
<div className="px-3 py-3 space-y-4">
|
|
145
|
-
<div className="rounded-lg border border-border bg-card/50 px-3 py-2.5 flex items-center justify-between">
|
|
146
|
-
<span className="text-xs font-medium text-foreground">{p.mcpServer}</span>
|
|
147
|
-
{mcp.status?.running ? (
|
|
148
|
-
<span className="flex items-center gap-1.5 text-[11px]">
|
|
149
|
-
<span className="w-1.5 h-1.5 rounded-full bg-emerald-500 inline-block" />
|
|
150
|
-
<span className="text-emerald-600 dark:text-emerald-400">:{mcp.status.port}</span>
|
|
151
|
-
<span className="text-muted-foreground">· {mcp.status.toolCount} tools</span>
|
|
152
|
-
</span>
|
|
153
|
-
) : (
|
|
154
|
-
<span className="flex items-center gap-1.5 text-[11px]">
|
|
155
|
-
<span className="w-1.5 h-1.5 rounded-full bg-zinc-400 inline-block" />
|
|
156
|
-
<span className="text-muted-foreground">{p.stopped}</span>
|
|
157
|
-
</span>
|
|
158
|
-
)}
|
|
159
|
-
</div>
|
|
160
|
-
|
|
161
128
|
<AgentsPanelAgentGroups
|
|
162
129
|
connected={connected}
|
|
163
130
|
detected={detected}
|
|
164
131
|
notFound={notFound}
|
|
165
|
-
|
|
166
|
-
selectedAgentKey={selectedAgentKey}
|
|
132
|
+
selectedAgentKey={effectiveSelectedAgentKey}
|
|
167
133
|
listCopy={listCopy}
|
|
168
134
|
onInstallAgent={installAgentWithRefresh}
|
|
169
135
|
showNotDetected={showNotDetected}
|
|
@@ -175,55 +141,6 @@ export default function AgentsPanel({
|
|
|
175
141
|
sectionNotDetected: p.sectionNotDetected,
|
|
176
142
|
}}
|
|
177
143
|
/>
|
|
178
|
-
|
|
179
|
-
<section>
|
|
180
|
-
{mcp.skills.length > 0 ? (
|
|
181
|
-
<>
|
|
182
|
-
<div className="flex items-center justify-between mb-2">
|
|
183
|
-
<h3 className="text-[11px] font-medium text-muted-foreground uppercase tracking-wider">
|
|
184
|
-
{p.skillsTitle} <span className="normal-case font-normal">{activeSkillCount} {p.skillsActive}</span>
|
|
185
|
-
</h3>
|
|
186
|
-
<button
|
|
187
|
-
type="button"
|
|
188
|
-
onClick={openAdvancedConfig}
|
|
189
|
-
className="text-2xs text-muted-foreground hover:text-foreground transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm"
|
|
190
|
-
>
|
|
191
|
-
{p.newSkill}
|
|
192
|
-
</button>
|
|
193
|
-
</div>
|
|
194
|
-
|
|
195
|
-
{customSkills.length > 0 && (
|
|
196
|
-
<div className="space-y-0.5 mb-2">
|
|
197
|
-
{customSkills.map(skill => (
|
|
198
|
-
<SkillRow key={skill.name} skill={skill} onToggle={mcp.toggleSkill} />
|
|
199
|
-
))}
|
|
200
|
-
</div>
|
|
201
|
-
)}
|
|
202
|
-
|
|
203
|
-
{builtinSkills.length > 0 && (
|
|
204
|
-
<>
|
|
205
|
-
<button
|
|
206
|
-
type="button"
|
|
207
|
-
onClick={() => setShowBuiltinSkills(!showBuiltinSkills)}
|
|
208
|
-
className="flex items-center gap-1 text-2xs text-muted-foreground hover:text-foreground transition-colors mb-1 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring rounded-sm"
|
|
209
|
-
>
|
|
210
|
-
{showBuiltinSkills ? <ChevronDown size={10} /> : <ChevronRight size={10} />}
|
|
211
|
-
{p.builtinSkills} ({builtinSkills.length})
|
|
212
|
-
</button>
|
|
213
|
-
{showBuiltinSkills && (
|
|
214
|
-
<div className="space-y-0.5">
|
|
215
|
-
{builtinSkills.map(skill => (
|
|
216
|
-
<SkillRow key={skill.name} skill={skill} onToggle={mcp.toggleSkill} />
|
|
217
|
-
))}
|
|
218
|
-
</div>
|
|
219
|
-
)}
|
|
220
|
-
</>
|
|
221
|
-
)}
|
|
222
|
-
</>
|
|
223
|
-
) : (
|
|
224
|
-
<p className="text-2xs text-muted-foreground py-1">{p.skillsEmptyHint}</p>
|
|
225
|
-
)}
|
|
226
|
-
</section>
|
|
227
144
|
</div>
|
|
228
145
|
</div>
|
|
229
146
|
)}
|
|
@@ -242,12 +159,3 @@ export default function AgentsPanel({
|
|
|
242
159
|
</div>
|
|
243
160
|
);
|
|
244
161
|
}
|
|
245
|
-
|
|
246
|
-
function SkillRow({ skill, onToggle }: { skill: SkillInfo; onToggle: (name: string, enabled: boolean) => void }) {
|
|
247
|
-
return (
|
|
248
|
-
<div className="flex items-center justify-between gap-2 px-2 py-1.5 rounded-md hover:bg-muted/30 transition-colors">
|
|
249
|
-
<span className="text-xs text-foreground truncate">{skill.name}</span>
|
|
250
|
-
<Toggle size="sm" checked={skill.enabled} onChange={v => onToggle(skill.name, v)} />
|
|
251
|
-
</div>
|
|
252
|
-
);
|
|
253
|
-
}
|
|
@@ -77,7 +77,7 @@ export default function AgentsPanelAgentDetail({
|
|
|
77
77
|
};
|
|
78
78
|
|
|
79
79
|
const dot =
|
|
80
|
-
agentStatus === 'connected' ? 'bg-
|
|
80
|
+
agentStatus === 'connected' ? 'bg-[var(--success)]' : agentStatus === 'detected' ? 'bg-[var(--amber)]' : 'bg-muted-foreground';
|
|
81
81
|
|
|
82
82
|
return (
|
|
83
83
|
<div className="flex flex-col h-full min-h-0">
|
|
@@ -125,7 +125,7 @@ export default function AgentsPanelAgentDetail({
|
|
|
125
125
|
</button>
|
|
126
126
|
{result && (
|
|
127
127
|
<span
|
|
128
|
-
className={`flex items-center gap-1 text-2xs ${result.type === 'success' ? 'text-
|
|
128
|
+
className={`flex items-center gap-1 text-2xs ${result.type === 'success' ? 'text-success' : 'text-destructive'}`}
|
|
129
129
|
>
|
|
130
130
|
{result.type === 'success' ? <CheckCircle2 size={12} /> : <AlertCircle size={12} />}
|
|
131
131
|
{result.text}
|
|
@@ -15,7 +15,6 @@ export function AgentsPanelAgentGroups({
|
|
|
15
15
|
connected,
|
|
16
16
|
detected,
|
|
17
17
|
notFound,
|
|
18
|
-
onOpenDetail,
|
|
19
18
|
selectedAgentKey,
|
|
20
19
|
onInstallAgent,
|
|
21
20
|
listCopy,
|
|
@@ -26,7 +25,6 @@ export function AgentsPanelAgentGroups({
|
|
|
26
25
|
connected: AgentInfo[];
|
|
27
26
|
detected: AgentInfo[];
|
|
28
27
|
notFound: AgentInfo[];
|
|
29
|
-
onOpenDetail?: (key: string) => void;
|
|
30
28
|
selectedAgentKey?: string | null;
|
|
31
29
|
onInstallAgent: (key: string) => Promise<boolean>;
|
|
32
30
|
listCopy: AgentsPanelAgentListRowCopy;
|
|
@@ -34,8 +32,6 @@ export function AgentsPanelAgentGroups({
|
|
|
34
32
|
setShowNotDetected: (v: boolean | ((prev: boolean) => boolean)) => void;
|
|
35
33
|
p: AgentsCopy;
|
|
36
34
|
}) {
|
|
37
|
-
const open = onOpenDetail ?? (() => {});
|
|
38
|
-
|
|
39
35
|
return (
|
|
40
36
|
<div>
|
|
41
37
|
<div className="px-0 py-1 mb-0.5">
|
|
@@ -53,7 +49,7 @@ export function AgentsPanelAgentGroups({
|
|
|
53
49
|
agent={agent}
|
|
54
50
|
agentStatus="connected"
|
|
55
51
|
selected={selectedAgentKey === agent.key}
|
|
56
|
-
|
|
52
|
+
detailHref={`/agents/${encodeURIComponent(agent.key)}`}
|
|
57
53
|
onInstallAgent={onInstallAgent}
|
|
58
54
|
copy={listCopy}
|
|
59
55
|
/>
|
|
@@ -74,7 +70,7 @@ export function AgentsPanelAgentGroups({
|
|
|
74
70
|
agent={agent}
|
|
75
71
|
agentStatus="detected"
|
|
76
72
|
selected={selectedAgentKey === agent.key}
|
|
77
|
-
|
|
73
|
+
detailHref={`/agents/${encodeURIComponent(agent.key)}`}
|
|
78
74
|
onInstallAgent={onInstallAgent}
|
|
79
75
|
copy={listCopy}
|
|
80
76
|
/>
|
|
@@ -101,7 +97,7 @@ export function AgentsPanelAgentGroups({
|
|
|
101
97
|
agent={agent}
|
|
102
98
|
agentStatus="notFound"
|
|
103
99
|
selected={selectedAgentKey === agent.key}
|
|
104
|
-
|
|
100
|
+
detailHref={`/agents/${encodeURIComponent(agent.key)}`}
|
|
105
101
|
onInstallAgent={onInstallAgent}
|
|
106
102
|
copy={listCopy}
|
|
107
103
|
/>
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
'use client';
|
|
2
2
|
|
|
3
3
|
import { useState } from 'react';
|
|
4
|
+
import Link from 'next/link';
|
|
4
5
|
import { Check, ChevronRight, Loader2, RotateCw } from 'lucide-react';
|
|
5
6
|
import type { AgentInfo } from '../settings/types';
|
|
7
|
+
import { AgentAvatar } from '../agents/AgentsPrimitives';
|
|
6
8
|
|
|
7
9
|
export type AgentsPanelAgentListStatus = 'connected' | 'detected' | 'notFound';
|
|
8
10
|
|
|
@@ -18,20 +20,17 @@ export default function AgentsPanelAgentListRow({
|
|
|
18
20
|
agent,
|
|
19
21
|
agentStatus,
|
|
20
22
|
selected = false,
|
|
21
|
-
|
|
23
|
+
detailHref,
|
|
22
24
|
onInstallAgent,
|
|
23
25
|
copy,
|
|
24
26
|
}: {
|
|
25
27
|
agent: AgentInfo;
|
|
26
28
|
agentStatus: AgentsPanelAgentListStatus;
|
|
27
29
|
selected?: boolean;
|
|
28
|
-
|
|
30
|
+
detailHref: string;
|
|
29
31
|
onInstallAgent: (key: string) => Promise<boolean>;
|
|
30
32
|
copy: AgentsPanelAgentListRowCopy;
|
|
31
33
|
}) {
|
|
32
|
-
const dot =
|
|
33
|
-
agentStatus === 'connected' ? 'bg-emerald-500' : agentStatus === 'detected' ? 'bg-amber-500' : 'bg-zinc-400';
|
|
34
|
-
|
|
35
34
|
return (
|
|
36
35
|
<div
|
|
37
36
|
className={`
|
|
@@ -41,12 +40,11 @@ export default function AgentsPanelAgentListRow({
|
|
|
41
40
|
: 'border-border/70 bg-card/50 hover:border-border hover:bg-muted/25'}
|
|
42
41
|
`}
|
|
43
42
|
>
|
|
44
|
-
<
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
className="flex flex-1 min-w-0 items-center gap-2.5 text-left rounded-xl pl-3 pr-2 py-2.5 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
43
|
+
<Link
|
|
44
|
+
href={detailHref}
|
|
45
|
+
className="flex flex-1 min-w-0 items-center gap-2.5 text-left rounded-xl pl-2.5 pr-2 py-2 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
|
|
48
46
|
>
|
|
49
|
-
<
|
|
47
|
+
<AgentAvatar name={agent.name} status={agentStatus} size="sm" />
|
|
50
48
|
<span className="text-sm font-medium text-foreground truncate leading-tight">{agent.name}</span>
|
|
51
49
|
{agentStatus === 'connected' && agent.transport && (
|
|
52
50
|
<span className="text-2xs font-mono tabular-nums px-1.5 py-0.5 rounded-md bg-muted/90 text-muted-foreground shrink-0 border border-border/50">
|
|
@@ -59,7 +57,7 @@ export default function AgentsPanelAgentListRow({
|
|
|
59
57
|
className={`shrink-0 transition-opacity duration-150 ${selected ? 'text-[var(--amber)] opacity-90' : 'text-muted-foreground/45 group-hover:text-muted-foreground/80'}`}
|
|
60
58
|
aria-hidden
|
|
61
59
|
/>
|
|
62
|
-
</
|
|
60
|
+
</Link>
|
|
63
61
|
|
|
64
62
|
{agentStatus === 'detected' && (
|
|
65
63
|
<div className="pr-2 py-2 shrink-0">
|
|
@@ -19,27 +19,25 @@ export default function EchoPanel({ active, maximized, onMaximize }: EchoPanelPr
|
|
|
19
19
|
const e = t.panels.echo;
|
|
20
20
|
const pathname = usePathname() ?? '';
|
|
21
21
|
|
|
22
|
-
const rowBySegment: Record<EchoSegment, { icon: ReactNode; title: string }> = {
|
|
23
|
-
'about-you': { icon: <UserRound size={14} />, title: e.aboutYouTitle },
|
|
24
|
-
continued: { icon: <Bookmark size={14} />, title: e.continuedTitle },
|
|
25
|
-
daily: { icon: <Sun size={14} />, title: e.dailyEchoTitle },
|
|
26
|
-
'past-you': { icon: <History size={14} />, title: e.pastYouTitle },
|
|
27
|
-
growth: { icon: <Brain size={14} />, title: e.intentGrowthTitle },
|
|
22
|
+
const rowBySegment: Record<EchoSegment, { icon: ReactNode; title: string; subtitle: string }> = {
|
|
23
|
+
'about-you': { icon: <UserRound size={14} />, title: e.aboutYouTitle, subtitle: e.aboutYouDesc },
|
|
24
|
+
continued: { icon: <Bookmark size={14} />, title: e.continuedTitle, subtitle: e.continuedDesc },
|
|
25
|
+
daily: { icon: <Sun size={14} />, title: e.dailyEchoTitle, subtitle: e.dailyDesc },
|
|
26
|
+
'past-you': { icon: <History size={14} />, title: e.pastYouTitle, subtitle: e.pastYouDesc },
|
|
27
|
+
growth: { icon: <Brain size={14} />, title: e.intentGrowthTitle, subtitle: e.growthDesc },
|
|
28
28
|
};
|
|
29
29
|
|
|
30
30
|
return (
|
|
31
31
|
<div className={`flex flex-col h-full ${active ? '' : 'hidden'}`}>
|
|
32
32
|
<PanelHeader title={e.title} maximized={maximized} onMaximize={onMaximize} />
|
|
33
33
|
<div className="flex-1 overflow-y-auto min-h-0">
|
|
34
|
-
<div className="flex flex-col py-1">
|
|
34
|
+
<div className="flex flex-col gap-0.5 py-1.5">
|
|
35
35
|
{ECHO_SEGMENT_ORDER.map((segment) => {
|
|
36
36
|
const row = rowBySegment[segment];
|
|
37
37
|
const href = ECHO_SEGMENT_HREF[segment];
|
|
38
38
|
const isActive = pathname === href || pathname.startsWith(`${href}/`);
|
|
39
39
|
return (
|
|
40
|
-
<
|
|
41
|
-
<PanelNavRow href={href} icon={row.icon} title={row.title} active={isActive} />
|
|
42
|
-
</div>
|
|
40
|
+
<PanelNavRow key={segment} href={href} icon={row.icon} title={row.title} subtitle={row.subtitle} active={isActive} />
|
|
43
41
|
);
|
|
44
42
|
})}
|
|
45
43
|
</div>
|
|
@@ -5,10 +5,11 @@ import Link from 'next/link';
|
|
|
5
5
|
import { ChevronRight } from 'lucide-react';
|
|
6
6
|
import { cn } from '@/lib/utils';
|
|
7
7
|
|
|
8
|
-
/** Row matching Discover panel nav: icon tile, title, optional badge, chevron. */
|
|
8
|
+
/** Row matching Discover panel nav: icon tile, title, optional subtitle, optional badge, chevron. */
|
|
9
9
|
export function PanelNavRow({
|
|
10
10
|
icon,
|
|
11
11
|
title,
|
|
12
|
+
subtitle,
|
|
12
13
|
badge,
|
|
13
14
|
href,
|
|
14
15
|
onClick,
|
|
@@ -16,6 +17,7 @@ export function PanelNavRow({
|
|
|
16
17
|
}: {
|
|
17
18
|
icon: ReactNode;
|
|
18
19
|
title: string;
|
|
20
|
+
subtitle?: string;
|
|
19
21
|
badge?: React.ReactNode;
|
|
20
22
|
href?: string;
|
|
21
23
|
onClick?: () => void;
|
|
@@ -25,7 +27,12 @@ export function PanelNavRow({
|
|
|
25
27
|
const content = (
|
|
26
28
|
<>
|
|
27
29
|
<span className="flex h-7 w-7 shrink-0 items-center justify-center rounded-md bg-muted">{icon}</span>
|
|
28
|
-
<span className="flex-1
|
|
30
|
+
<span className="flex-1 min-w-0">
|
|
31
|
+
<span className="block text-left text-sm font-medium text-foreground truncate">{title}</span>
|
|
32
|
+
{subtitle ? (
|
|
33
|
+
<span className="block text-left text-2xs text-muted-foreground truncate">{subtitle}</span>
|
|
34
|
+
) : null}
|
|
35
|
+
</span>
|
|
29
36
|
{badge}
|
|
30
37
|
<ChevronRight size={14} className="shrink-0 text-muted-foreground" />
|
|
31
38
|
</>
|
|
@@ -142,12 +142,12 @@ export default function PluginsPanel({ active, maximized, onMaximize }: PluginsP
|
|
|
142
142
|
</span>
|
|
143
143
|
))}
|
|
144
144
|
{r.entryPath && enabled && !fileExists && (
|
|
145
|
-
<span className="text-2xs
|
|
145
|
+
<span className="text-2xs text-[var(--amber)]">
|
|
146
146
|
{(p.createFile ?? 'Create {file}').replace('{file}', r.entryPath)}
|
|
147
147
|
</span>
|
|
148
148
|
)}
|
|
149
149
|
{canOpen && (
|
|
150
|
-
<span className="text-2xs
|
|
150
|
+
<span className="text-2xs text-[var(--amber)]">
|
|
151
151
|
→ {r.entryPath}
|
|
152
152
|
</span>
|
|
153
153
|
)}
|
|
@@ -6,13 +6,13 @@ import { Terminal, FileEdit, FilePlus, Trash2, Search, Clock, ChevronDown, Alert
|
|
|
6
6
|
import type { RendererContext } from '@/lib/renderers/registry';
|
|
7
7
|
|
|
8
8
|
// ─── Log entry format ─────────────────────────────────────────────────────────
|
|
9
|
-
//
|
|
9
|
+
// Primary format:
|
|
10
|
+
// {
|
|
11
|
+
// "version": 1,
|
|
12
|
+
// "events": [{ "ts": "...", "tool": "mindos_write_file", "params": {}, "result": "ok" }]
|
|
13
|
+
// }
|
|
10
14
|
//
|
|
11
|
-
//
|
|
12
|
-
// { "ts": "2025-01-15T10:30:00Z", "tool": "mindos_write_file",
|
|
13
|
-
// "params": { "path": "...", "content": "..." },
|
|
14
|
-
// "result": "ok" | "error", "message": "..." }
|
|
15
|
-
// ```
|
|
15
|
+
// Legacy format (still supported for compatibility): JSON Lines.
|
|
16
16
|
|
|
17
17
|
interface AgentOp {
|
|
18
18
|
ts: string;
|
|
@@ -22,9 +22,14 @@ interface AgentOp {
|
|
|
22
22
|
message?: string;
|
|
23
23
|
}
|
|
24
24
|
|
|
25
|
+
interface AgentAuditState {
|
|
26
|
+
version?: number;
|
|
27
|
+
events?: AgentOp[];
|
|
28
|
+
}
|
|
29
|
+
|
|
25
30
|
// ─── Parser ───────────────────────────────────────────────────────────────────
|
|
26
31
|
|
|
27
|
-
function
|
|
32
|
+
function parseJsonLines(content: string): AgentOp[] {
|
|
28
33
|
const ops: AgentOp[] = [];
|
|
29
34
|
|
|
30
35
|
// JSON Lines format: each line is a JSON object
|
|
@@ -36,6 +41,23 @@ function parseOps(content: string): AgentOp[] {
|
|
|
36
41
|
if (op.tool && op.ts) ops.push(op);
|
|
37
42
|
} catch { /* skip non-JSON lines */ }
|
|
38
43
|
}
|
|
44
|
+
return ops;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
function parseOps(content: string): AgentOp[] {
|
|
48
|
+
// New format: JSON object with events array.
|
|
49
|
+
try {
|
|
50
|
+
const parsed = JSON.parse(content) as AgentAuditState;
|
|
51
|
+
if (Array.isArray(parsed.events)) {
|
|
52
|
+
return parsed.events
|
|
53
|
+
.filter((op) => Boolean(op?.tool) && Boolean(op?.ts))
|
|
54
|
+
.sort((a, b) => new Date(b.ts).getTime() - new Date(a.ts).getTime());
|
|
55
|
+
}
|
|
56
|
+
} catch {
|
|
57
|
+
// Fallback to legacy JSONL.
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
const ops = parseJsonLines(content);
|
|
39
61
|
|
|
40
62
|
// newest first
|
|
41
63
|
return ops.sort((a, b) => new Date(b.ts).getTime() - new Date(a.ts).getTime());
|
|
@@ -236,7 +258,7 @@ export function AgentInspectorRenderer({ content }: RendererContext) {
|
|
|
236
258
|
<Terminal size={28} style={{ margin: '0 auto 10px', opacity: 0.3 }} />
|
|
237
259
|
<p>No agent operations logged yet.</p>
|
|
238
260
|
<p style={{ marginTop: 6, opacity: 0.6, fontSize: 11 }}>
|
|
239
|
-
Agent writes appear here
|
|
261
|
+
Agent writes appear here from <code style={{ background: 'var(--muted)', padding: '1px 5px', borderRadius: 4 }}>.mindos/agent-audit-log.json</code>.
|
|
240
262
|
</p>
|
|
241
263
|
</div>
|
|
242
264
|
);
|
|
@@ -3,14 +3,14 @@ import type { RendererDefinition } from '@/lib/renderers/registry';
|
|
|
3
3
|
export const manifest: RendererDefinition = {
|
|
4
4
|
id: 'agent-inspector',
|
|
5
5
|
name: 'Agent Inspector',
|
|
6
|
-
description: 'Visualizes agent tool-call logs as a filterable timeline. Auto-activates on .agent-log.json
|
|
6
|
+
description: 'Visualizes agent tool-call logs as a filterable timeline. Auto-activates on .mindos/agent-audit-log.json and supports legacy .agent-log.json.',
|
|
7
7
|
author: 'MindOS',
|
|
8
8
|
icon: '🔍',
|
|
9
9
|
tags: ['agent', 'inspector', 'log', 'mcp', 'tools'],
|
|
10
10
|
builtin: true,
|
|
11
11
|
core: true,
|
|
12
12
|
appBuiltinFeature: true,
|
|
13
|
-
entryPath: '.agent-log.json',
|
|
14
|
-
match: ({ filePath }) => /\.agent-log\.json$/i.test(filePath),
|
|
13
|
+
entryPath: '.mindos/agent-audit-log.json',
|
|
14
|
+
match: ({ filePath }) => /(^|\/)\.mindos\/agent-audit-log\.json$/i.test(filePath) || /\.agent-log\.json$/i.test(filePath),
|
|
15
15
|
load: () => import('./AgentInspectorRenderer').then(m => ({ default: m.AgentInspectorRenderer })),
|
|
16
16
|
};
|
|
@@ -9,6 +9,7 @@ export const manifest: RendererDefinition = {
|
|
|
9
9
|
tags: ['productivity', 'tasks', 'markdown'],
|
|
10
10
|
builtin: true,
|
|
11
11
|
core: true,
|
|
12
|
+
appBuiltinFeature: true,
|
|
12
13
|
entryPath: 'TODO.md',
|
|
13
14
|
match: ({ filePath }) => /\bTODO\b.*\.(md|csv)$/i.test(filePath),
|
|
14
15
|
load: () => import('./TodoRenderer').then(m => ({ default: m.TodoRenderer })),
|
|
@@ -121,7 +121,7 @@ export function AiTab({ data, updateAi, updateAgent, t }: AiTabProps) {
|
|
|
121
121
|
type="button"
|
|
122
122
|
disabled={disabled}
|
|
123
123
|
onClick={() => handleTestKey(providerName)}
|
|
124
|
-
className="inline-flex items-center gap-1.5 px-2.5 py-1 text-xs rounded-
|
|
124
|
+
className="inline-flex items-center gap-1.5 px-2.5 py-1 text-xs rounded-lg border border-border text-muted-foreground hover:text-foreground hover:border-foreground/20 transition-colors disabled:opacity-40 disabled:cursor-not-allowed"
|
|
125
125
|
>
|
|
126
126
|
{result.state === 'testing' ? (
|
|
127
127
|
<>
|
|
@@ -143,7 +143,7 @@ export function AiTab({ data, updateAi, updateAgent, t }: AiTabProps) {
|
|
|
143
143
|
};
|
|
144
144
|
|
|
145
145
|
return (
|
|
146
|
-
<div className="space-y-
|
|
146
|
+
<div className="space-y-6">
|
|
147
147
|
<Field label={<>{t.settings.ai.provider} <EnvBadge overridden={env.AI_PROVIDER} /></>}>
|
|
148
148
|
<Select
|
|
149
149
|
value={provider}
|
|
@@ -214,7 +214,7 @@ export function AiTab({ data, updateAi, updateAgent, t }: AiTabProps) {
|
|
|
214
214
|
)}
|
|
215
215
|
|
|
216
216
|
{Object.values(env).some(Boolean) && (
|
|
217
|
-
<div className="flex items-start gap-2 text-xs text-amber
|
|
217
|
+
<div className="flex items-start gap-2 text-xs text-[var(--amber)] bg-[var(--amber-subtle)] border border-[var(--amber)]/20 rounded-lg px-3 py-2.5">
|
|
218
218
|
<AlertCircle size={13} className="shrink-0 mt-0.5" />
|
|
219
219
|
<span>{t.settings.ai.envHint}</span>
|
|
220
220
|
</div>
|
|
@@ -67,7 +67,7 @@ export function AppearanceTab({ font, setFont, contentWidth, setContentWidth, da
|
|
|
67
67
|
onClick={() => setFont(f.value)}
|
|
68
68
|
className={`px-3 py-1.5 text-xs rounded-lg border transition-all ${
|
|
69
69
|
font === f.value
|
|
70
|
-
? 'border-amber
|
|
70
|
+
? 'border-[var(--amber)] bg-[var(--amber-subtle)] text-foreground font-medium shadow-sm'
|
|
71
71
|
: 'border-border text-muted-foreground hover:text-foreground hover:bg-muted'
|
|
72
72
|
}`}
|
|
73
73
|
style={{ fontFamily: f.style.fontFamily }}
|
|
@@ -94,7 +94,7 @@ export function AppearanceTab({ font, setFont, contentWidth, setContentWidth, da
|
|
|
94
94
|
onClick={() => setContentWidth(w.value)}
|
|
95
95
|
className={`px-3 py-1.5 text-xs rounded-lg border transition-all ${
|
|
96
96
|
contentWidth === w.value
|
|
97
|
-
? 'border-amber
|
|
97
|
+
? 'border-[var(--amber)] bg-[var(--amber-subtle)] text-foreground font-medium shadow-sm'
|
|
98
98
|
: 'border-border text-muted-foreground hover:text-foreground hover:bg-muted'
|
|
99
99
|
}`}
|
|
100
100
|
>
|
|
@@ -116,7 +116,7 @@ export function KnowledgeTab({ data, setData, t }: KnowledgeTabProps) {
|
|
|
116
116
|
}
|
|
117
117
|
|
|
118
118
|
return (
|
|
119
|
-
<div className="space-y-
|
|
119
|
+
<div className="space-y-6">
|
|
120
120
|
<SectionLabel>Knowledge Base</SectionLabel>
|
|
121
121
|
|
|
122
122
|
<Field
|
|
@@ -208,7 +208,7 @@ export function KnowledgeTab({ data, setData, t }: KnowledgeTabProps) {
|
|
|
208
208
|
)}
|
|
209
209
|
</div>
|
|
210
210
|
{revealedToken && (
|
|
211
|
-
<p className="text-xs text-amber
|
|
211
|
+
<p className="text-xs text-[var(--amber)]">
|
|
212
212
|
New token generated. Copy it now — it won't be shown in full again.
|
|
213
213
|
</p>
|
|
214
214
|
)}
|
|
@@ -221,7 +221,7 @@ export function KnowledgeTab({ data, setData, t }: KnowledgeTabProps) {
|
|
|
221
221
|
<SectionLabel>{t.guide?.title ?? 'Getting Started'}</SectionLabel>
|
|
222
222
|
<div className="flex items-center justify-between py-2">
|
|
223
223
|
<div className="flex items-center gap-2">
|
|
224
|
-
<Sparkles size={14}
|
|
224
|
+
<Sparkles size={14} className="text-[var(--amber)]" />
|
|
225
225
|
<div>
|
|
226
226
|
<div className="text-sm text-foreground">{t.guide?.showGuide ?? 'Show getting started guide'}</div>
|
|
227
227
|
</div>
|
|
@@ -94,8 +94,7 @@ export default function AgentInstall({ agents, t, onRefresh }: McpAgentInstallPr
|
|
|
94
94
|
style={{ accentColor: 'var(--amber)' }}
|
|
95
95
|
/>
|
|
96
96
|
<span className="w-28 shrink-0 text-xs">{agent.name}</span>
|
|
97
|
-
<span className="text-2xs px-1.5 py-0.5 rounded font-mono"
|
|
98
|
-
style={{ background: 'rgba(100,100,120,0.08)' }}>
|
|
97
|
+
<span className="text-2xs px-1.5 py-0.5 rounded font-mono bg-muted">
|
|
99
98
|
{getEffectiveTransport(agent)}
|
|
100
99
|
</span>
|
|
101
100
|
{agent.installed ? (
|
|
@@ -131,8 +130,7 @@ export default function AgentInstall({ agents, t, onRefresh }: McpAgentInstallPr
|
|
|
131
130
|
onClick={() => setSelected(new Set(
|
|
132
131
|
agents.filter(a => !a.installed && a.present).map(a => a.key)
|
|
133
132
|
))}
|
|
134
|
-
className="px-2.5 py-1 rounded-md border transition-colors hover:bg-muted/50"
|
|
135
|
-
style={{ borderColor: 'var(--amber)', color: 'var(--amber)' }}>
|
|
133
|
+
className="px-2.5 py-1 rounded-md border border-[var(--amber)] text-[var(--amber)] transition-colors hover:bg-muted/50">
|
|
136
134
|
{m?.selectDetected ?? 'Select Detected'}
|
|
137
135
|
</button>
|
|
138
136
|
<button type="button"
|
|
@@ -205,8 +203,7 @@ export default function AgentInstall({ agents, t, onRefresh }: McpAgentInstallPr
|
|
|
205
203
|
<button
|
|
206
204
|
onClick={handleInstall}
|
|
207
205
|
disabled={selected.size === 0 || installing}
|
|
208
|
-
className="flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-lg transition-colors disabled:opacity-40 disabled:cursor-not-allowed"
|
|
209
|
-
style={{ background: 'var(--amber)', color: 'var(--amber-foreground)' }}
|
|
206
|
+
className="flex items-center gap-1.5 px-3 py-1.5 text-xs rounded-lg transition-colors disabled:opacity-40 disabled:cursor-not-allowed bg-[var(--amber)] text-[var(--amber-foreground)]"
|
|
210
207
|
>
|
|
211
208
|
{installing && <Loader2 size={12} className="animate-spin" />}
|
|
212
209
|
{installing ? (m?.installing ?? 'Installing...') : (m?.installSelected ?? 'Install Selected')}
|
|
@@ -129,7 +129,7 @@ export default function SkillCreateForm({ onSave, onCancel, saving, error, m }:
|
|
|
129
129
|
onClick={() => handleTemplateChange(tmpl)}
|
|
130
130
|
className={`px-2.5 py-1 text-xs transition-colors ${i > 0 ? 'border-l border-border' : ''} ${
|
|
131
131
|
selectedTemplate === tmpl
|
|
132
|
-
? 'bg-amber-
|
|
132
|
+
? 'bg-[var(--amber-subtle)] text-[var(--amber)] font-medium'
|
|
133
133
|
: 'text-muted-foreground hover:bg-muted'
|
|
134
134
|
}`}
|
|
135
135
|
>
|
|
@@ -160,8 +160,7 @@ export default function SkillCreateForm({ onSave, onCancel, saving, error, m }:
|
|
|
160
160
|
<button
|
|
161
161
|
onClick={() => onSave(newName.trim(), newContent || getTemplate(newName.trim() || 'my-skill'))}
|
|
162
162
|
disabled={!newName.trim() || saving}
|
|
163
|
-
className="flex items-center gap-1 px-2.5 py-1 text-xs rounded-md disabled:opacity-40 disabled:cursor-not-allowed transition-colors"
|
|
164
|
-
style={{ background: 'var(--amber)', color: 'var(--amber-foreground)' }}
|
|
163
|
+
className="flex items-center gap-1 px-2.5 py-1 text-xs rounded-md disabled:opacity-40 disabled:cursor-not-allowed transition-colors bg-[var(--amber)] text-[var(--amber-foreground)]"
|
|
165
164
|
>
|
|
166
165
|
{saving && <Loader2 size={10} className="animate-spin" />}
|
|
167
166
|
{m?.saveSkill ?? 'Save'}
|