@swarmclawai/swarmclaw 0.6.7 → 0.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.
- package/README.md +82 -39
- package/next.config.ts +31 -6
- package/package.json +3 -2
- package/src/app/api/agents/[id]/thread/route.ts +1 -0
- package/src/app/api/agents/route.ts +19 -5
- package/src/app/api/approvals/route.ts +22 -0
- package/src/app/api/chatrooms/[id]/chat/route.ts +4 -0
- package/src/app/api/clawhub/install/route.ts +2 -2
- package/src/app/api/eval/run/route.ts +37 -0
- package/src/app/api/eval/scenarios/route.ts +24 -0
- package/src/app/api/eval/suite/route.ts +29 -0
- package/src/app/api/mcp-servers/[id]/conformance/route.ts +26 -0
- package/src/app/api/mcp-servers/[id]/invoke/route.ts +81 -0
- package/src/app/api/memory/graph/route.ts +46 -0
- package/src/app/api/memory/route.ts +36 -5
- package/src/app/api/notifications/route.ts +3 -0
- package/src/app/api/plugins/install/route.ts +57 -5
- package/src/app/api/plugins/marketplace/route.ts +73 -22
- package/src/app/api/plugins/route.ts +61 -1
- package/src/app/api/plugins/ui/route.ts +34 -0
- package/src/app/api/sessions/[id]/checkpoints/route.ts +31 -0
- package/src/app/api/sessions/[id]/restore/route.ts +36 -0
- package/src/app/api/settings/route.ts +62 -0
- package/src/app/api/setup/doctor/route.ts +22 -5
- package/src/app/api/souls/[id]/route.ts +65 -0
- package/src/app/api/souls/route.ts +70 -0
- package/src/app/api/tasks/[id]/approve/route.ts +4 -3
- package/src/app/api/tasks/[id]/route.ts +16 -3
- package/src/app/api/tasks/route.ts +10 -2
- package/src/app/api/usage/route.ts +9 -2
- package/src/app/globals.css +27 -0
- package/src/app/page.tsx +10 -5
- package/src/cli/index.js +37 -0
- package/src/components/activity/activity-feed.tsx +9 -2
- package/src/components/agents/agent-avatar.tsx +5 -1
- package/src/components/agents/agent-card.tsx +55 -9
- package/src/components/agents/agent-sheet.tsx +112 -34
- package/src/components/agents/inspector-panel.tsx +1 -1
- package/src/components/agents/soul-library-picker.tsx +84 -13
- package/src/components/auth/access-key-gate.tsx +63 -54
- package/src/components/auth/user-picker.tsx +37 -32
- package/src/components/chat/activity-moment.tsx +2 -0
- package/src/components/chat/chat-area.tsx +11 -0
- package/src/components/chat/chat-header.tsx +69 -25
- package/src/components/chat/chat-tool-toggles.tsx +2 -2
- package/src/components/chat/checkpoint-timeline.tsx +112 -0
- package/src/components/chat/code-block.tsx +3 -1
- package/src/components/chat/exec-approval-card.tsx +8 -1
- package/src/components/chat/message-bubble.tsx +164 -4
- package/src/components/chat/message-list.tsx +46 -4
- package/src/components/chat/session-approval-card.tsx +80 -0
- package/src/components/chat/session-debug-panel.tsx +106 -84
- package/src/components/chat/streaming-bubble.tsx +6 -5
- package/src/components/chat/task-approval-card.tsx +78 -0
- package/src/components/chat/thinking-indicator.tsx +48 -12
- package/src/components/chat/tool-call-bubble.tsx +3 -0
- package/src/components/chat/tool-request-banner.tsx +39 -20
- package/src/components/chatrooms/chatroom-list.tsx +11 -4
- package/src/components/chatrooms/chatroom-sheet.tsx +7 -2
- package/src/components/connectors/connector-list.tsx +33 -11
- package/src/components/connectors/connector-sheet.tsx +37 -7
- package/src/components/home/home-view.tsx +54 -24
- package/src/components/input/chat-input.tsx +22 -1
- package/src/components/knowledge/knowledge-list.tsx +17 -18
- package/src/components/knowledge/knowledge-sheet.tsx +9 -5
- package/src/components/layout/app-layout.tsx +87 -19
- package/src/components/mcp-servers/mcp-server-list.tsx +352 -50
- package/src/components/mcp-servers/mcp-server-sheet.tsx +25 -9
- package/src/components/memory/memory-browser.tsx +73 -45
- package/src/components/memory/memory-graph-view.tsx +203 -0
- package/src/components/memory/memory-list.tsx +20 -13
- package/src/components/plugins/plugin-list.tsx +214 -60
- package/src/components/plugins/plugin-sheet.tsx +119 -24
- package/src/components/projects/project-list.tsx +17 -9
- package/src/components/providers/provider-list.tsx +21 -6
- package/src/components/providers/provider-sheet.tsx +42 -25
- package/src/components/runs/run-list.tsx +17 -13
- package/src/components/schedules/schedule-card.tsx +10 -3
- package/src/components/schedules/schedule-list.tsx +2 -2
- package/src/components/schedules/schedule-sheet.tsx +28 -9
- package/src/components/secrets/secret-sheet.tsx +7 -2
- package/src/components/secrets/secrets-list.tsx +18 -5
- package/src/components/sessions/new-session-sheet.tsx +183 -376
- package/src/components/sessions/session-card.tsx +10 -2
- package/src/components/settings/gateway-connection-panel.tsx +9 -8
- package/src/components/shared/command-palette.tsx +13 -5
- package/src/components/shared/empty-state.tsx +20 -8
- package/src/components/shared/hint-tip.tsx +31 -0
- package/src/components/shared/notification-center.tsx +134 -86
- package/src/components/shared/profile-sheet.tsx +4 -0
- package/src/components/shared/settings/plugin-manager.tsx +360 -135
- package/src/components/shared/settings/section-capability-policy.tsx +3 -3
- package/src/components/shared/settings/section-runtime-loop.tsx +149 -4
- package/src/components/skills/clawhub-browser.tsx +1 -0
- package/src/components/skills/skill-list.tsx +31 -12
- package/src/components/skills/skill-sheet.tsx +20 -7
- package/src/components/tasks/approvals-panel.tsx +224 -0
- package/src/components/tasks/task-board.tsx +20 -12
- package/src/components/tasks/task-card.tsx +21 -7
- package/src/components/tasks/task-column.tsx +4 -3
- package/src/components/tasks/task-list.tsx +1 -1
- package/src/components/tasks/task-sheet.tsx +130 -1
- package/src/components/ui/dialog.tsx +1 -0
- package/src/components/ui/sheet.tsx +1 -0
- package/src/components/usage/metrics-dashboard.tsx +72 -48
- package/src/components/wallets/wallet-panel.tsx +65 -41
- package/src/components/wallets/wallet-section.tsx +9 -3
- package/src/components/webhooks/webhook-list.tsx +21 -12
- package/src/components/webhooks/webhook-sheet.tsx +13 -3
- package/src/lib/approval-display.test.ts +45 -0
- package/src/lib/approval-display.ts +62 -0
- package/src/lib/clipboard.ts +38 -0
- package/src/lib/memory.ts +8 -0
- package/src/lib/providers/claude-cli.ts +5 -3
- package/src/lib/providers/index.ts +67 -21
- package/src/lib/runtime-loop.ts +3 -2
- package/src/lib/server/approvals.ts +150 -0
- package/src/lib/server/chat-execution.ts +319 -74
- package/src/lib/server/chatroom-helpers.ts +63 -5
- package/src/lib/server/chatroom-orchestration.ts +74 -0
- package/src/lib/server/clawhub-client.ts +82 -6
- package/src/lib/server/connectors/manager.ts +27 -1
- package/src/lib/server/context-manager.ts +132 -50
- package/src/lib/server/cost.test.ts +73 -0
- package/src/lib/server/cost.ts +165 -34
- package/src/lib/server/daemon-state.ts +112 -1
- package/src/lib/server/data-dir.ts +18 -1
- package/src/lib/server/eval/runner.ts +126 -0
- package/src/lib/server/eval/scenarios.ts +218 -0
- package/src/lib/server/eval/scorer.ts +96 -0
- package/src/lib/server/eval/store.ts +37 -0
- package/src/lib/server/eval/types.ts +48 -0
- package/src/lib/server/execution-log.ts +12 -8
- package/src/lib/server/guardian.ts +34 -0
- package/src/lib/server/heartbeat-service.ts +53 -1
- package/src/lib/server/integrity-monitor.ts +208 -0
- package/src/lib/server/langgraph-checkpoint.ts +10 -0
- package/src/lib/server/link-understanding.ts +55 -0
- package/src/lib/server/llm-response-cache.test.ts +102 -0
- package/src/lib/server/llm-response-cache.ts +227 -0
- package/src/lib/server/main-agent-loop.ts +115 -16
- package/src/lib/server/main-session.ts +6 -3
- package/src/lib/server/mcp-conformance.test.ts +18 -0
- package/src/lib/server/mcp-conformance.ts +233 -0
- package/src/lib/server/memory-db.ts +193 -19
- package/src/lib/server/memory-retrieval.test.ts +56 -0
- package/src/lib/server/mmr.ts +73 -0
- package/src/lib/server/orchestrator-lg.ts +7 -1
- package/src/lib/server/orchestrator.ts +4 -3
- package/src/lib/server/plugins.ts +662 -132
- package/src/lib/server/process-manager.ts +18 -0
- package/src/lib/server/query-expansion.ts +57 -0
- package/src/lib/server/queue.ts +280 -11
- package/src/lib/server/runtime-settings.ts +9 -0
- package/src/lib/server/session-run-manager.test.ts +23 -0
- package/src/lib/server/session-run-manager.ts +32 -2
- package/src/lib/server/session-tools/canvas.ts +85 -50
- package/src/lib/server/session-tools/chatroom.ts +130 -127
- package/src/lib/server/session-tools/connector.ts +233 -454
- package/src/lib/server/session-tools/context-mgmt.ts +87 -105
- package/src/lib/server/session-tools/crud.ts +84 -7
- package/src/lib/server/session-tools/delegate.ts +351 -752
- package/src/lib/server/session-tools/discovery.ts +198 -0
- package/src/lib/server/session-tools/edit_file.ts +82 -0
- package/src/lib/server/session-tools/file-send.test.ts +39 -0
- package/src/lib/server/session-tools/file.ts +257 -425
- package/src/lib/server/session-tools/git.ts +87 -47
- package/src/lib/server/session-tools/http.ts +95 -33
- package/src/lib/server/session-tools/index.ts +217 -138
- package/src/lib/server/session-tools/memory.ts +154 -239
- package/src/lib/server/session-tools/monitor.ts +126 -0
- package/src/lib/server/session-tools/normalize-tool-args.test.ts +61 -0
- package/src/lib/server/session-tools/normalize-tool-args.ts +48 -0
- package/src/lib/server/session-tools/openclaw-nodes.ts +82 -99
- package/src/lib/server/session-tools/openclaw-workspace.ts +103 -93
- package/src/lib/server/session-tools/platform.ts +86 -0
- package/src/lib/server/session-tools/plugin-creator.ts +239 -0
- package/src/lib/server/session-tools/sample-ui.ts +97 -0
- package/src/lib/server/session-tools/sandbox.ts +175 -148
- package/src/lib/server/session-tools/schedule.ts +78 -0
- package/src/lib/server/session-tools/session-info.ts +104 -410
- package/src/lib/server/session-tools/shell-normalize.test.ts +43 -0
- package/src/lib/server/session-tools/shell.ts +171 -143
- package/src/lib/server/session-tools/subagent.ts +77 -77
- package/src/lib/server/session-tools/wallet.ts +182 -106
- package/src/lib/server/session-tools/web.ts +181 -327
- package/src/lib/server/storage.ts +36 -0
- package/src/lib/server/stream-agent-chat.ts +348 -242
- package/src/lib/server/task-quality-gate.test.ts +44 -0
- package/src/lib/server/task-quality-gate.ts +67 -0
- package/src/lib/server/task-validation.test.ts +78 -0
- package/src/lib/server/task-validation.ts +67 -2
- package/src/lib/server/tool-aliases.ts +68 -0
- package/src/lib/server/tool-capability-policy.ts +24 -5
- package/src/lib/server/tool-retry.ts +62 -0
- package/src/lib/server/transcript-repair.ts +72 -0
- package/src/lib/setup-defaults.ts +1 -0
- package/src/lib/tasks.ts +7 -1
- package/src/lib/tool-definitions.ts +24 -23
- package/src/lib/validation/schemas.ts +13 -0
- package/src/lib/view-routes.ts +2 -23
- package/src/stores/use-app-store.ts +23 -1
- package/src/types/index.ts +155 -10
|
@@ -5,6 +5,7 @@ import { searchMemory } from '@/lib/memory'
|
|
|
5
5
|
import { useAppStore } from '@/stores/use-app-store'
|
|
6
6
|
import { MemoryCard } from './memory-card'
|
|
7
7
|
import { MemoryDetail } from './memory-detail'
|
|
8
|
+
import { MemoryGraphView } from './memory-graph-view'
|
|
8
9
|
import type { MemoryEntry } from '@/types'
|
|
9
10
|
|
|
10
11
|
export function MemoryBrowser() {
|
|
@@ -19,6 +20,7 @@ export function MemoryBrowser() {
|
|
|
19
20
|
const [loaded, setLoaded] = useState(false)
|
|
20
21
|
const [error, setError] = useState<string | null>(null)
|
|
21
22
|
const [categoryFilter, setCategoryFilter] = useState<string>('')
|
|
23
|
+
const [viewMode, setViewMode] = useState<'list' | 'graph'>('list')
|
|
22
24
|
const searchRef = useRef(search)
|
|
23
25
|
|
|
24
26
|
// Derive the API agentId from the filter
|
|
@@ -102,12 +104,26 @@ export function MemoryBrowser() {
|
|
|
102
104
|
|
|
103
105
|
return (
|
|
104
106
|
<div className="flex-1 flex h-full min-w-0">
|
|
105
|
-
{/* Left: Memory card list */}
|
|
107
|
+
{/* Left: Memory card list or Graph Toggle */}
|
|
106
108
|
<div className="w-[360px] shrink-0 border-r border-white/[0.06] flex flex-col overflow-hidden">
|
|
107
109
|
{/* Header + search */}
|
|
108
110
|
<div className="px-3 pt-3 pb-1 shrink-0">
|
|
109
111
|
<div className="flex items-center gap-2 mb-2">
|
|
110
112
|
<h3 className="font-display text-[13px] font-600 text-text-2 tracking-[-0.01em] flex-1 truncate">{filterLabel}</h3>
|
|
113
|
+
<div className="flex bg-white/[0.04] p-0.5 rounded-[8px]">
|
|
114
|
+
<button
|
|
115
|
+
onClick={() => setViewMode('list')}
|
|
116
|
+
className={`p-1 rounded-[6px] transition-colors ${viewMode === 'list' ? 'bg-white/[0.08] text-text' : 'text-text-3 hover:text-text-2'}`}
|
|
117
|
+
>
|
|
118
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><line x1="8" y1="6" x2="21" y2="6"/><line x1="8" y1="12" x2="21" y2="12"/><line x1="8" y1="18" x2="21" y2="18"/><line x1="3" y1="6" x2="3.01" y2="6"/><line x1="3" y1="12" x2="3.01" y2="12"/><line x1="3" y1="18" x2="3.01" y2="18"/></svg>
|
|
119
|
+
</button>
|
|
120
|
+
<button
|
|
121
|
+
onClick={() => setViewMode('graph')}
|
|
122
|
+
className={`p-1 rounded-[6px] transition-colors ${viewMode === 'graph' ? 'bg-white/[0.08] text-text' : 'text-text-3 hover:text-text-2'}`}
|
|
123
|
+
>
|
|
124
|
+
<svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round"><circle cx="18" cy="5" r="3"/><circle cx="6" cy="12" r="3"/><circle cx="18" cy="19" r="3"/><line x1="8.59" y1="13.51" x2="15.42" y2="17.49"/><line x1="15.41" y1="6.51" x2="8.59" y2="10.49"/></svg>
|
|
125
|
+
</button>
|
|
126
|
+
</div>
|
|
111
127
|
<span className="text-[10px] font-mono tabular-nums text-text-3/50">{filtered.length}</span>
|
|
112
128
|
</div>
|
|
113
129
|
<input
|
|
@@ -150,56 +166,68 @@ export function MemoryBrowser() {
|
|
|
150
166
|
|
|
151
167
|
{/* Cards */}
|
|
152
168
|
<div className="flex-1 overflow-y-auto">
|
|
153
|
-
{
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
) : error ? (
|
|
173
|
-
<div className="flex-1 flex flex-col items-center justify-center gap-3 text-text-3 p-8 text-center">
|
|
174
|
-
<p className="font-display text-[14px] font-600 text-text-2">Couldn't load memories</p>
|
|
175
|
-
<p className="text-[12px] text-text-3/60">{error}</p>
|
|
176
|
-
<button
|
|
177
|
-
onClick={() => { void load(search) }}
|
|
178
|
-
className="px-3 py-1.5 rounded-[8px] bg-accent-soft text-accent-bright text-[12px] font-600 cursor-pointer border-none"
|
|
179
|
-
style={{ fontFamily: 'inherit' }}
|
|
180
|
-
>
|
|
181
|
-
Retry
|
|
182
|
-
</button>
|
|
183
|
-
</div>
|
|
184
|
-
) : loaded ? (
|
|
185
|
-
<div className="flex flex-col items-center justify-center gap-3 text-text-3 p-8 text-center">
|
|
186
|
-
<div className="w-10 h-10 rounded-[12px] bg-accent-soft flex items-center justify-center">
|
|
187
|
-
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" className="text-accent-bright">
|
|
188
|
-
<ellipse cx="12" cy="5" rx="9" ry="3" />
|
|
189
|
-
<path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3" />
|
|
190
|
-
<path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5" />
|
|
191
|
-
</svg>
|
|
169
|
+
{viewMode === 'list' ? (
|
|
170
|
+
filtered.length > 0 ? (
|
|
171
|
+
<div className="flex flex-col gap-0.5 px-2 pb-4">
|
|
172
|
+
{filtered.map((e) => {
|
|
173
|
+
// Show agent info on cards when in "All Memories" view
|
|
174
|
+
const showAgent = !memoryAgentFilter
|
|
175
|
+
const agent = showAgent && e.agentId ? agents[e.agentId] : null
|
|
176
|
+
return (
|
|
177
|
+
<MemoryCard
|
|
178
|
+
key={e.id}
|
|
179
|
+
entry={e}
|
|
180
|
+
active={e.id === selectedMemoryId}
|
|
181
|
+
agentName={showAgent ? (agent?.name || null) : undefined}
|
|
182
|
+
agentAvatarSeed={showAgent ? (agent?.avatarSeed || null) : undefined}
|
|
183
|
+
agentAvatarUrl={showAgent ? (agent?.avatarUrl || null) : undefined}
|
|
184
|
+
onClick={() => setSelectedMemoryId(e.id)}
|
|
185
|
+
/>
|
|
186
|
+
)
|
|
187
|
+
})}
|
|
192
188
|
</div>
|
|
193
|
-
|
|
194
|
-
<
|
|
189
|
+
) : error ? (
|
|
190
|
+
<div className="flex-1 flex flex-col items-center justify-center gap-3 text-text-3 p-8 text-center">
|
|
191
|
+
<p className="font-display text-[14px] font-600 text-text-2">Couldn't load memories</p>
|
|
192
|
+
<p className="text-[12px] text-text-3/60">{error}</p>
|
|
193
|
+
<button
|
|
194
|
+
onClick={() => { void load(search) }}
|
|
195
|
+
className="px-3 py-1.5 rounded-[8px] bg-accent-soft text-accent-bright text-[12px] font-600 cursor-pointer border-none"
|
|
196
|
+
style={{ fontFamily: 'inherit' }}
|
|
197
|
+
>
|
|
198
|
+
Retry
|
|
199
|
+
</button>
|
|
200
|
+
</div>
|
|
201
|
+
) : loaded ? (
|
|
202
|
+
<div className="flex flex-col items-center justify-center gap-3 text-text-3 p-8 text-center">
|
|
203
|
+
<div className="w-10 h-10 rounded-[12px] bg-accent-soft flex items-center justify-center">
|
|
204
|
+
<svg width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" className="text-accent-bright">
|
|
205
|
+
<ellipse cx="12" cy="5" rx="9" ry="3" />
|
|
206
|
+
<path d="M21 12c0 1.66-4 3-9 3s-9-1.34-9-3" />
|
|
207
|
+
<path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5" />
|
|
208
|
+
</svg>
|
|
209
|
+
</div>
|
|
210
|
+
<p className="font-display text-[14px] font-600 text-text-2">No memories yet</p>
|
|
211
|
+
<p className="text-[12px] text-text-3/50">Agents store knowledge here as they learn</p>
|
|
212
|
+
</div>
|
|
213
|
+
) : null
|
|
214
|
+
) : (
|
|
215
|
+
<div className="p-4 text-[12px] text-text-3 italic">
|
|
216
|
+
Graph view enabled in main area.
|
|
195
217
|
</div>
|
|
196
|
-
)
|
|
218
|
+
)}
|
|
197
219
|
</div>
|
|
198
220
|
</div>
|
|
199
221
|
|
|
200
|
-
{/* Right: Detail */}
|
|
222
|
+
{/* Right: Detail or Graph */}
|
|
201
223
|
<div className="flex-1 flex flex-col min-w-0">
|
|
202
|
-
|
|
224
|
+
{viewMode === 'graph' ? (
|
|
225
|
+
<div className="flex-1 p-4 flex flex-col">
|
|
226
|
+
<MemoryGraphView />
|
|
227
|
+
</div>
|
|
228
|
+
) : (
|
|
229
|
+
<MemoryDetail />
|
|
230
|
+
)}
|
|
203
231
|
</div>
|
|
204
232
|
</div>
|
|
205
233
|
)
|
|
@@ -0,0 +1,203 @@
|
|
|
1
|
+
'use client'
|
|
2
|
+
|
|
3
|
+
import { useEffect, useRef, useState } from 'react'
|
|
4
|
+
import { api } from '@/lib/api-client'
|
|
5
|
+
import { useAppStore } from '@/stores/use-app-store'
|
|
6
|
+
|
|
7
|
+
interface Node {
|
|
8
|
+
id: string
|
|
9
|
+
title: string
|
|
10
|
+
category: string
|
|
11
|
+
agentId?: string | null
|
|
12
|
+
x: number
|
|
13
|
+
y: number
|
|
14
|
+
vx: number
|
|
15
|
+
vy: number
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface Link {
|
|
19
|
+
source: string
|
|
20
|
+
target: string
|
|
21
|
+
type: string
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
export function MemoryGraphView() {
|
|
25
|
+
const [data, setData] = useState<{ nodes: Node[]; links: Link[] }>({ nodes: [], links: [] })
|
|
26
|
+
const [loading, setLoading] = useState(true)
|
|
27
|
+
const [hoveredNode, setHoveredNode] = useState<string | null>(null)
|
|
28
|
+
const containerRef = useRef<HTMLDivElement>(null)
|
|
29
|
+
const requestRef = useRef<number>(null)
|
|
30
|
+
|
|
31
|
+
const selectedMemoryId = useAppStore((s) => s.selectedMemoryId)
|
|
32
|
+
const setSelectedMemoryId = useAppStore((s) => s.setSelectedMemoryId)
|
|
33
|
+
const memoryAgentFilter = useAppStore((s) => s.memoryAgentFilter)
|
|
34
|
+
|
|
35
|
+
useEffect(() => {
|
|
36
|
+
async function load() {
|
|
37
|
+
setLoading(true)
|
|
38
|
+
try {
|
|
39
|
+
const url = `/memory/graph${memoryAgentFilter ? `?agentId=${memoryAgentFilter}` : ''}`
|
|
40
|
+
const res = await api<{ nodes: Node[]; links: Link[] }>('GET', url)
|
|
41
|
+
|
|
42
|
+
// Initialize positions
|
|
43
|
+
const nodes = res.nodes.map(n => ({
|
|
44
|
+
...n,
|
|
45
|
+
x: Math.random() * 800,
|
|
46
|
+
y: Math.random() * 600,
|
|
47
|
+
vx: 0,
|
|
48
|
+
vy: 0
|
|
49
|
+
}))
|
|
50
|
+
|
|
51
|
+
setData({ nodes, links: res.links })
|
|
52
|
+
} catch (err) {
|
|
53
|
+
console.error('Failed to load memory graph', err)
|
|
54
|
+
} finally {
|
|
55
|
+
setLoading(false)
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
load()
|
|
59
|
+
}, [memoryAgentFilter])
|
|
60
|
+
|
|
61
|
+
// Simple Force-Directed Simulation
|
|
62
|
+
useEffect(() => {
|
|
63
|
+
if (data.nodes.length === 0) return
|
|
64
|
+
|
|
65
|
+
const animate = () => {
|
|
66
|
+
setData(prev => {
|
|
67
|
+
const nodes = [...prev.nodes]
|
|
68
|
+
const links = prev.links
|
|
69
|
+
|
|
70
|
+
// 1. Repulsion between all nodes
|
|
71
|
+
for (let i = 0; i < nodes.length; i++) {
|
|
72
|
+
for (let j = i + 1; j < nodes.length; j++) {
|
|
73
|
+
const dx = nodes[i].x - nodes[j].x
|
|
74
|
+
const dy = nodes[i].y - nodes[j].y
|
|
75
|
+
const distSq = dx * dx + dy * dy + 0.1
|
|
76
|
+
const force = 400 / distSq
|
|
77
|
+
const fx = dx * force
|
|
78
|
+
const fy = dy * force
|
|
79
|
+
nodes[i].vx += fx
|
|
80
|
+
nodes[i].vy += fy
|
|
81
|
+
nodes[j].vx -= fx
|
|
82
|
+
nodes[j].vy -= fy
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
// 2. Attraction along links
|
|
87
|
+
for (const link of links) {
|
|
88
|
+
const source = nodes.find(n => n.id === link.source)
|
|
89
|
+
const target = nodes.find(n => n.id === link.target)
|
|
90
|
+
if (source && target) {
|
|
91
|
+
const dx = target.x - source.x
|
|
92
|
+
const dy = target.y - source.y
|
|
93
|
+
const dist = Math.sqrt(dx * dx + dy * dy) + 0.1
|
|
94
|
+
const force = (dist - 100) * 0.02
|
|
95
|
+
const fx = (dx / dist) * force
|
|
96
|
+
const fy = (dy / dist) * force
|
|
97
|
+
source.vx += fx
|
|
98
|
+
source.vy += fy
|
|
99
|
+
target.vx -= fx
|
|
100
|
+
target.vy -= fy
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
// 3. Centering force
|
|
105
|
+
const cx = 400
|
|
106
|
+
const cy = 300
|
|
107
|
+
for (const node of nodes) {
|
|
108
|
+
node.vx += (cx - node.x) * 0.01
|
|
109
|
+
node.vy += (cy - node.y) * 0.01
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
// 4. Update positions with damping
|
|
113
|
+
for (const node of nodes) {
|
|
114
|
+
node.x += node.vx
|
|
115
|
+
node.y += node.vy
|
|
116
|
+
node.vx *= 0.8
|
|
117
|
+
node.vy *= 0.8
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
return { nodes, links }
|
|
121
|
+
})
|
|
122
|
+
requestRef.current = requestAnimationFrame(animate)
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
requestRef.current = requestAnimationFrame(animate)
|
|
126
|
+
return () => {
|
|
127
|
+
if (requestRef.current) cancelAnimationFrame(requestRef.current)
|
|
128
|
+
}
|
|
129
|
+
}, [data.nodes.length])
|
|
130
|
+
|
|
131
|
+
if (loading) {
|
|
132
|
+
return (
|
|
133
|
+
<div className="flex-1 flex items-center justify-center">
|
|
134
|
+
<div className="animate-spin rounded-full h-8 w-8 border-b-2 border-accent-bright"></div>
|
|
135
|
+
</div>
|
|
136
|
+
)
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
return (
|
|
140
|
+
<div ref={containerRef} className="flex-1 relative overflow-hidden bg-black/20 rounded-[16px] border border-white/[0.06]">
|
|
141
|
+
<svg width="100%" height="100%" viewBox="0 0 800 600" preserveAspectRatio="xMidYMid meet">
|
|
142
|
+
{/* Links */}
|
|
143
|
+
{data.links.map((link, i) => {
|
|
144
|
+
const s = data.nodes.find(n => n.id === link.source)
|
|
145
|
+
const t = data.nodes.find(n => n.id === link.target)
|
|
146
|
+
if (!s || !t) return null
|
|
147
|
+
return (
|
|
148
|
+
<line
|
|
149
|
+
key={i}
|
|
150
|
+
x1={s.x} y1={s.y}
|
|
151
|
+
x2={t.x} y2={t.y}
|
|
152
|
+
stroke="white"
|
|
153
|
+
strokeOpacity="0.1"
|
|
154
|
+
strokeWidth="1"
|
|
155
|
+
/>
|
|
156
|
+
)
|
|
157
|
+
})}
|
|
158
|
+
|
|
159
|
+
{/* Nodes */}
|
|
160
|
+
{data.nodes.map(node => (
|
|
161
|
+
<g
|
|
162
|
+
key={node.id}
|
|
163
|
+
transform={`translate(${node.x},${node.y})`}
|
|
164
|
+
onMouseEnter={() => setHoveredNode(node.id)}
|
|
165
|
+
onMouseLeave={() => setHoveredNode(null)}
|
|
166
|
+
onClick={() => setSelectedMemoryId(node.id)}
|
|
167
|
+
className="cursor-pointer"
|
|
168
|
+
>
|
|
169
|
+
<circle
|
|
170
|
+
r={selectedMemoryId === node.id ? 8 : 5}
|
|
171
|
+
fill={node.category === 'knowledge' ? '#10B981' : '#6366F1'}
|
|
172
|
+
stroke="white"
|
|
173
|
+
strokeWidth={selectedMemoryId === node.id ? 2 : 0}
|
|
174
|
+
className="transition-all"
|
|
175
|
+
/>
|
|
176
|
+
{(hoveredNode === node.id || selectedMemoryId === node.id) && (
|
|
177
|
+
<text
|
|
178
|
+
y="-12"
|
|
179
|
+
textAnchor="middle"
|
|
180
|
+
className="text-[10px] fill-text font-600 pointer-events-none"
|
|
181
|
+
style={{ filter: 'drop-shadow(0 2px 4px rgba(0,0,0,0.5))' }}
|
|
182
|
+
>
|
|
183
|
+
{node.title}
|
|
184
|
+
</text>
|
|
185
|
+
)}
|
|
186
|
+
</g>
|
|
187
|
+
))}
|
|
188
|
+
</svg>
|
|
189
|
+
|
|
190
|
+
{/* Legend */}
|
|
191
|
+
<div className="absolute bottom-4 left-4 p-3 bg-surface/80 backdrop-blur rounded-[12px] border border-white/[0.06] flex flex-col gap-2">
|
|
192
|
+
<div className="flex items-center gap-2">
|
|
193
|
+
<div className="w-3 h-3 rounded-full bg-[#10B981]" />
|
|
194
|
+
<span className="text-[11px] text-text-3">Knowledge</span>
|
|
195
|
+
</div>
|
|
196
|
+
<div className="flex items-center gap-2">
|
|
197
|
+
<div className="w-3 h-3 rounded-full bg-[#6366F1]" />
|
|
198
|
+
<span className="text-[11px] text-text-3">Note / Working</span>
|
|
199
|
+
</div>
|
|
200
|
+
</div>
|
|
201
|
+
</div>
|
|
202
|
+
)
|
|
203
|
+
}
|
|
@@ -80,7 +80,7 @@ export function MemoryList({ inSidebar: _inSidebar, onSelect }: Props) {
|
|
|
80
80
|
return (
|
|
81
81
|
<div className="flex-1 flex flex-col overflow-y-auto">
|
|
82
82
|
{/* Search */}
|
|
83
|
-
<div className="px-3 py-2 shrink-0">
|
|
83
|
+
<div className="px-3 py-2 shrink-0" style={{ animation: 'fade-up 0.4s var(--ease-spring)' }}>
|
|
84
84
|
<input
|
|
85
85
|
type="text"
|
|
86
86
|
value={search}
|
|
@@ -94,7 +94,7 @@ export function MemoryList({ inSidebar: _inSidebar, onSelect }: Props) {
|
|
|
94
94
|
|
|
95
95
|
{/* Agent filter tabs */}
|
|
96
96
|
{entries.length > 0 && hasMultipleAgents && (
|
|
97
|
-
<div className="px-3 pb-1.5 shrink-0">
|
|
97
|
+
<div className="px-3 pb-1.5 shrink-0" style={{ animation: 'fade-up 0.4s var(--ease-spring) 0.05s both' }}>
|
|
98
98
|
<div className="flex gap-1 flex-wrap">
|
|
99
99
|
<button
|
|
100
100
|
onClick={() => setMemoryAgentFilter(null)}
|
|
@@ -125,7 +125,7 @@ export function MemoryList({ inSidebar: _inSidebar, onSelect }: Props) {
|
|
|
125
125
|
|
|
126
126
|
{/* Category filter */}
|
|
127
127
|
{entries.length > 0 && uniqueCategories.length > 1 && (
|
|
128
|
-
<div className="px-3 pb-1.5 shrink-0">
|
|
128
|
+
<div className="px-3 pb-1.5 shrink-0" style={{ animation: 'fade-up 0.4s var(--ease-spring) 0.1s both' }}>
|
|
129
129
|
<div className="flex gap-1 flex-wrap">
|
|
130
130
|
<button
|
|
131
131
|
onClick={() => setCategoryFilter('')}
|
|
@@ -153,21 +153,28 @@ export function MemoryList({ inSidebar: _inSidebar, onSelect }: Props) {
|
|
|
153
153
|
{/* Memory cards */}
|
|
154
154
|
{filtered.length > 0 ? (
|
|
155
155
|
<div className="flex flex-col gap-0.5 px-2 pb-4">
|
|
156
|
-
{filtered.map((e) => (
|
|
157
|
-
<
|
|
156
|
+
{filtered.map((e, idx) => (
|
|
157
|
+
<div
|
|
158
158
|
key={e.id}
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
onClick={() => {
|
|
163
|
-
setSelectedMemoryId(e.id)
|
|
164
|
-
onSelect?.()
|
|
159
|
+
style={{
|
|
160
|
+
animation: 'fade-up 0.4s var(--ease-spring) both',
|
|
161
|
+
animationDelay: `${0.15 + idx * 0.02}s`
|
|
165
162
|
}}
|
|
166
|
-
|
|
163
|
+
>
|
|
164
|
+
<MemoryCard
|
|
165
|
+
entry={e}
|
|
166
|
+
active={e.id === selectedMemoryId}
|
|
167
|
+
agentName={e.agentId ? (agents[e.agentId]?.name || null) : null}
|
|
168
|
+
onClick={() => {
|
|
169
|
+
setSelectedMemoryId(e.id)
|
|
170
|
+
onSelect?.()
|
|
171
|
+
}}
|
|
172
|
+
/>
|
|
173
|
+
</div>
|
|
167
174
|
))}
|
|
168
175
|
</div>
|
|
169
176
|
) : error ? (
|
|
170
|
-
<div className="flex-1 flex flex-col items-center justify-center gap-3 text-text-3 p-8 text-center">
|
|
177
|
+
<div className="flex-1 flex flex-col items-center justify-center gap-3 text-text-3 p-8 text-center" style={{ animation: 'fade-up 0.5s var(--ease-spring)' }}>
|
|
171
178
|
<p className="font-display text-[14px] font-600 text-text-2">Couldn't load memories</p>
|
|
172
179
|
<p className="text-[12px] text-text-3/60">{error}</p>
|
|
173
180
|
<button
|