@swarmclawai/swarmclaw 0.7.2 → 0.7.4

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 (274) hide show
  1. package/README.md +116 -50
  2. package/bin/package-manager.js +157 -0
  3. package/bin/package-manager.test.js +90 -0
  4. package/bin/server-cmd.js +38 -7
  5. package/bin/swarmclaw.js +54 -4
  6. package/bin/update-cmd.js +48 -10
  7. package/bin/update-cmd.test.js +55 -0
  8. package/package.json +8 -3
  9. package/scripts/postinstall.mjs +26 -0
  10. package/src/app/api/agents/[id]/route.ts +43 -0
  11. package/src/app/api/agents/[id]/thread/route.ts +39 -8
  12. package/src/app/api/agents/route.ts +35 -2
  13. package/src/app/api/auth/route.ts +77 -8
  14. package/src/app/api/chatrooms/[id]/chat/route.ts +22 -6
  15. package/src/app/api/chatrooms/[id]/pins/route.ts +2 -1
  16. package/src/app/api/chatrooms/[id]/reactions/route.ts +2 -1
  17. package/src/app/api/chatrooms/[id]/route.ts +6 -0
  18. package/src/app/api/chats/[id]/browser/route.ts +5 -1
  19. package/src/app/api/chats/[id]/chat/route.ts +7 -3
  20. package/src/app/api/chats/[id]/messages/route.ts +19 -13
  21. package/src/app/api/chats/[id]/route.ts +30 -0
  22. package/src/app/api/chats/[id]/stop/route.ts +6 -1
  23. package/src/app/api/chats/heartbeat/route.ts +2 -1
  24. package/src/app/api/chats/route.ts +23 -1
  25. package/src/app/api/connectors/[id]/doctor/route.ts +26 -0
  26. package/src/app/api/connectors/doctor/route.ts +13 -0
  27. package/src/app/api/external-agents/[id]/heartbeat/route.ts +33 -0
  28. package/src/app/api/external-agents/[id]/route.ts +31 -0
  29. package/src/app/api/external-agents/register/route.ts +3 -0
  30. package/src/app/api/external-agents/route.ts +66 -0
  31. package/src/app/api/files/open/route.ts +16 -14
  32. package/src/app/api/gateways/[id]/health/route.ts +28 -0
  33. package/src/app/api/gateways/[id]/route.ts +79 -0
  34. package/src/app/api/gateways/route.ts +57 -0
  35. package/src/app/api/memory/maintenance/route.ts +11 -1
  36. package/src/app/api/openclaw/agent-files/route.ts +27 -4
  37. package/src/app/api/openclaw/gateway/route.ts +10 -7
  38. package/src/app/api/openclaw/skills/route.ts +12 -4
  39. package/src/app/api/plugins/dependencies/route.ts +24 -0
  40. package/src/app/api/plugins/install/route.ts +15 -92
  41. package/src/app/api/plugins/route.ts +3 -26
  42. package/src/app/api/plugins/settings/route.ts +17 -12
  43. package/src/app/api/plugins/ui/route.ts +1 -0
  44. package/src/app/api/providers/[id]/discover-models/route.ts +27 -0
  45. package/src/app/api/schedules/[id]/route.ts +38 -9
  46. package/src/app/api/schedules/route.ts +51 -28
  47. package/src/app/api/settings/route.ts +55 -17
  48. package/src/app/api/setup/doctor/route.ts +6 -4
  49. package/src/app/api/tasks/[id]/route.ts +16 -6
  50. package/src/app/api/tasks/bulk/route.ts +3 -3
  51. package/src/app/api/tasks/route.ts +9 -4
  52. package/src/app/api/webhooks/[id]/route.ts +8 -1
  53. package/src/app/page.tsx +135 -17
  54. package/src/cli/binary.test.js +142 -0
  55. package/src/cli/index.js +38 -11
  56. package/src/cli/index.test.js +195 -0
  57. package/src/cli/index.ts +21 -12
  58. package/src/cli/server-cmd.test.js +59 -0
  59. package/src/cli/spec.js +20 -2
  60. package/src/components/agents/agent-card.tsx +15 -12
  61. package/src/components/agents/agent-chat-list.tsx +101 -1
  62. package/src/components/agents/agent-list.tsx +46 -9
  63. package/src/components/agents/agent-sheet.tsx +456 -23
  64. package/src/components/agents/inspector-panel.tsx +110 -49
  65. package/src/components/agents/sandbox-env-panel.tsx +4 -1
  66. package/src/components/auth/access-key-gate.tsx +36 -97
  67. package/src/components/auth/setup-wizard.tsx +970 -275
  68. package/src/components/chat/chat-area.tsx +70 -27
  69. package/src/components/chat/chat-card.tsx +6 -21
  70. package/src/components/chat/chat-header.tsx +263 -366
  71. package/src/components/chat/chat-list.tsx +62 -26
  72. package/src/components/chat/checkpoint-timeline.tsx +1 -1
  73. package/src/components/chat/message-list.tsx +145 -19
  74. package/src/components/chatrooms/chatroom-input.tsx +96 -33
  75. package/src/components/chatrooms/chatroom-list.tsx +141 -72
  76. package/src/components/chatrooms/chatroom-message.tsx +7 -6
  77. package/src/components/chatrooms/chatroom-sheet.tsx +13 -1
  78. package/src/components/chatrooms/chatroom-tool-request-banner.tsx +5 -2
  79. package/src/components/chatrooms/chatroom-view.tsx +422 -209
  80. package/src/components/chatrooms/reaction-picker.tsx +38 -33
  81. package/src/components/connectors/connector-list.tsx +265 -127
  82. package/src/components/connectors/connector-sheet.tsx +217 -0
  83. package/src/components/gateways/gateway-sheet.tsx +567 -0
  84. package/src/components/home/home-view.tsx +128 -4
  85. package/src/components/input/chat-input.tsx +135 -86
  86. package/src/components/layout/app-layout.tsx +385 -194
  87. package/src/components/layout/mobile-header.tsx +26 -8
  88. package/src/components/memory/memory-browser.tsx +71 -6
  89. package/src/components/memory/memory-card.tsx +18 -0
  90. package/src/components/memory/memory-detail.tsx +58 -31
  91. package/src/components/memory/memory-sheet.tsx +32 -4
  92. package/src/components/plugins/plugin-list.tsx +15 -3
  93. package/src/components/plugins/plugin-sheet.tsx +118 -9
  94. package/src/components/projects/project-detail.tsx +189 -1
  95. package/src/components/providers/provider-list.tsx +158 -2
  96. package/src/components/providers/provider-sheet.tsx +81 -70
  97. package/src/components/shared/agent-picker-list.tsx +2 -2
  98. package/src/components/shared/bottom-sheet.tsx +31 -15
  99. package/src/components/shared/command-palette.tsx +111 -24
  100. package/src/components/shared/confirm-dialog.tsx +45 -30
  101. package/src/components/shared/model-combobox.tsx +90 -8
  102. package/src/components/shared/settings/plugin-manager.tsx +20 -4
  103. package/src/components/shared/settings/section-capability-policy.tsx +105 -0
  104. package/src/components/shared/settings/section-heartbeat.tsx +88 -6
  105. package/src/components/shared/settings/section-orchestrator.tsx +6 -3
  106. package/src/components/shared/settings/section-runtime-loop.tsx +5 -5
  107. package/src/components/shared/settings/section-secrets.tsx +6 -6
  108. package/src/components/shared/settings/section-user-preferences.tsx +1 -1
  109. package/src/components/shared/settings/section-voice.tsx +5 -1
  110. package/src/components/shared/settings/section-web-search.tsx +10 -2
  111. package/src/components/shared/settings/settings-page.tsx +248 -47
  112. package/src/components/tasks/approvals-panel.tsx +211 -18
  113. package/src/components/tasks/task-board.tsx +242 -46
  114. package/src/components/ui/dialog.tsx +2 -2
  115. package/src/components/usage/metrics-dashboard.tsx +74 -1
  116. package/src/components/wallets/wallet-approval-dialog.tsx +59 -54
  117. package/src/components/wallets/wallet-panel.tsx +17 -5
  118. package/src/components/webhooks/webhook-sheet.tsx +7 -7
  119. package/src/lib/auth.ts +17 -0
  120. package/src/lib/chat-streaming-state.test.ts +108 -0
  121. package/src/lib/chat-streaming-state.ts +108 -0
  122. package/src/lib/heartbeat-defaults.ts +48 -0
  123. package/src/lib/memory-presentation.ts +59 -0
  124. package/src/lib/openclaw-agent-id.test.ts +14 -0
  125. package/src/lib/openclaw-agent-id.ts +31 -0
  126. package/src/lib/provider-model-discovery-client.ts +29 -0
  127. package/src/lib/providers/index.ts +12 -5
  128. package/src/lib/runtime-loop.ts +105 -3
  129. package/src/lib/safe-storage.ts +6 -1
  130. package/src/lib/server/agent-assignment.test.ts +112 -0
  131. package/src/lib/server/agent-assignment.ts +169 -0
  132. package/src/lib/server/agent-runtime-config.test.ts +141 -0
  133. package/src/lib/server/agent-runtime-config.ts +277 -0
  134. package/src/lib/server/approval-connector-notify.test.ts +253 -0
  135. package/src/lib/server/approvals-auto-approve.test.ts +264 -0
  136. package/src/lib/server/approvals.ts +483 -75
  137. package/src/lib/server/autonomy-runtime.test.ts +341 -0
  138. package/src/lib/server/browser-state.test.ts +118 -0
  139. package/src/lib/server/browser-state.ts +123 -0
  140. package/src/lib/server/build-llm.test.ts +44 -0
  141. package/src/lib/server/build-llm.ts +11 -4
  142. package/src/lib/server/builtin-plugins.ts +34 -0
  143. package/src/lib/server/chat-execution-heartbeat.test.ts +40 -0
  144. package/src/lib/server/chat-execution-tool-events.test.ts +219 -0
  145. package/src/lib/server/chat-execution.ts +402 -125
  146. package/src/lib/server/chatroom-health.test.ts +26 -0
  147. package/src/lib/server/chatroom-health.ts +2 -3
  148. package/src/lib/server/chatroom-helpers.test.ts +74 -2
  149. package/src/lib/server/chatroom-helpers.ts +144 -11
  150. package/src/lib/server/chatroom-session-persistence.test.ts +87 -0
  151. package/src/lib/server/connectors/discord.ts +175 -11
  152. package/src/lib/server/connectors/doctor.test.ts +80 -0
  153. package/src/lib/server/connectors/doctor.ts +116 -0
  154. package/src/lib/server/connectors/manager.ts +994 -130
  155. package/src/lib/server/connectors/policy.test.ts +222 -0
  156. package/src/lib/server/connectors/policy.ts +452 -0
  157. package/src/lib/server/connectors/slack.ts +189 -10
  158. package/src/lib/server/connectors/telegram.ts +65 -15
  159. package/src/lib/server/connectors/thread-context.test.ts +44 -0
  160. package/src/lib/server/connectors/thread-context.ts +72 -0
  161. package/src/lib/server/connectors/types.ts +41 -11
  162. package/src/lib/server/daemon-state.ts +62 -3
  163. package/src/lib/server/data-dir.ts +13 -0
  164. package/src/lib/server/delegation-jobs.test.ts +140 -0
  165. package/src/lib/server/delegation-jobs.ts +248 -0
  166. package/src/lib/server/document-utils.test.ts +47 -0
  167. package/src/lib/server/document-utils.ts +397 -0
  168. package/src/lib/server/eval/agent-regression.test.ts +47 -0
  169. package/src/lib/server/eval/agent-regression.ts +1742 -0
  170. package/src/lib/server/eval/runner.ts +11 -1
  171. package/src/lib/server/eval/store.ts +2 -1
  172. package/src/lib/server/heartbeat-service.ts +23 -43
  173. package/src/lib/server/heartbeat-source.test.ts +22 -0
  174. package/src/lib/server/heartbeat-source.ts +7 -0
  175. package/src/lib/server/identity-continuity.test.ts +77 -0
  176. package/src/lib/server/identity-continuity.ts +127 -0
  177. package/src/lib/server/mailbox-utils.ts +347 -0
  178. package/src/lib/server/main-agent-loop.ts +31 -964
  179. package/src/lib/server/memory-db.ts +4 -6
  180. package/src/lib/server/memory-tiers.ts +40 -0
  181. package/src/lib/server/openclaw-agent-resolver.test.ts +70 -0
  182. package/src/lib/server/openclaw-agent-resolver.ts +128 -0
  183. package/src/lib/server/openclaw-exec-config.ts +6 -5
  184. package/src/lib/server/openclaw-gateway.ts +123 -36
  185. package/src/lib/server/openclaw-skills-normalize.test.ts +56 -0
  186. package/src/lib/server/openclaw-skills-normalize.ts +136 -0
  187. package/src/lib/server/openclaw-sync.ts +3 -2
  188. package/src/lib/server/orchestrator-lg.ts +18 -8
  189. package/src/lib/server/orchestrator.ts +5 -4
  190. package/src/lib/server/playwright-proxy.mjs +27 -3
  191. package/src/lib/server/plugins.test.ts +215 -0
  192. package/src/lib/server/plugins.ts +832 -69
  193. package/src/lib/server/provider-health.ts +33 -3
  194. package/src/lib/server/provider-model-discovery.ts +481 -0
  195. package/src/lib/server/queue.ts +4 -21
  196. package/src/lib/server/runtime-settings.test.ts +119 -0
  197. package/src/lib/server/runtime-settings.ts +12 -92
  198. package/src/lib/server/schedule-normalization.ts +187 -0
  199. package/src/lib/server/scheduler.ts +2 -0
  200. package/src/lib/server/session-archive-memory.test.ts +85 -0
  201. package/src/lib/server/session-archive-memory.ts +230 -0
  202. package/src/lib/server/session-mailbox.ts +8 -18
  203. package/src/lib/server/session-reset-policy.test.ts +99 -0
  204. package/src/lib/server/session-reset-policy.ts +311 -0
  205. package/src/lib/server/session-run-manager.ts +33 -80
  206. package/src/lib/server/session-tools/autonomy-tools.test.ts +128 -0
  207. package/src/lib/server/session-tools/calendar.ts +2 -12
  208. package/src/lib/server/session-tools/connector.ts +109 -8
  209. package/src/lib/server/session-tools/context.ts +14 -2
  210. package/src/lib/server/session-tools/crawl.ts +447 -0
  211. package/src/lib/server/session-tools/crud.ts +96 -34
  212. package/src/lib/server/session-tools/delegate-fallback.test.ts +219 -0
  213. package/src/lib/server/session-tools/delegate.ts +406 -20
  214. package/src/lib/server/session-tools/discovery-approvals.test.ts +170 -0
  215. package/src/lib/server/session-tools/discovery.ts +40 -12
  216. package/src/lib/server/session-tools/document.ts +283 -0
  217. package/src/lib/server/session-tools/email.ts +1 -3
  218. package/src/lib/server/session-tools/extract.ts +137 -0
  219. package/src/lib/server/session-tools/file-normalize.test.ts +98 -0
  220. package/src/lib/server/session-tools/file-send.test.ts +84 -1
  221. package/src/lib/server/session-tools/file.ts +243 -24
  222. package/src/lib/server/session-tools/http.ts +9 -3
  223. package/src/lib/server/session-tools/human-loop.ts +227 -0
  224. package/src/lib/server/session-tools/image-gen.ts +1 -3
  225. package/src/lib/server/session-tools/index.ts +87 -2
  226. package/src/lib/server/session-tools/mailbox.ts +276 -0
  227. package/src/lib/server/session-tools/manage-schedules.test.ts +137 -0
  228. package/src/lib/server/session-tools/memory.ts +35 -3
  229. package/src/lib/server/session-tools/monitor.ts +162 -12
  230. package/src/lib/server/session-tools/normalize-tool-args.ts +17 -14
  231. package/src/lib/server/session-tools/openclaw-nodes.test.ts +111 -0
  232. package/src/lib/server/session-tools/openclaw-nodes.ts +86 -20
  233. package/src/lib/server/session-tools/platform-normalize.test.ts +142 -0
  234. package/src/lib/server/session-tools/platform.ts +142 -4
  235. package/src/lib/server/session-tools/plugin-creator.ts +95 -25
  236. package/src/lib/server/session-tools/primitive-tools.test.ts +257 -0
  237. package/src/lib/server/session-tools/replicate.ts +1 -3
  238. package/src/lib/server/session-tools/sandbox.ts +51 -92
  239. package/src/lib/server/session-tools/schedule.ts +20 -10
  240. package/src/lib/server/session-tools/session-info.ts +58 -4
  241. package/src/lib/server/session-tools/session-tools-wiring.test.ts +54 -17
  242. package/src/lib/server/session-tools/shell.ts +2 -2
  243. package/src/lib/server/session-tools/subagent.ts +195 -27
  244. package/src/lib/server/session-tools/table.ts +587 -0
  245. package/src/lib/server/session-tools/wallet.ts +13 -10
  246. package/src/lib/server/session-tools/web-browser-config.test.ts +39 -0
  247. package/src/lib/server/session-tools/web.ts +947 -108
  248. package/src/lib/server/storage.ts +255 -10
  249. package/src/lib/server/stream-agent-chat.test.ts +61 -0
  250. package/src/lib/server/stream-agent-chat.ts +185 -25
  251. package/src/lib/server/structured-extract.test.ts +72 -0
  252. package/src/lib/server/structured-extract.ts +373 -0
  253. package/src/lib/server/task-mention.test.ts +16 -2
  254. package/src/lib/server/task-mention.ts +61 -11
  255. package/src/lib/server/tool-aliases.ts +80 -12
  256. package/src/lib/server/tool-capability-policy.ts +7 -1
  257. package/src/lib/server/tool-retry.ts +2 -0
  258. package/src/lib/server/watch-jobs.test.ts +173 -0
  259. package/src/lib/server/watch-jobs.ts +532 -0
  260. package/src/lib/server/ws-hub.ts +5 -3
  261. package/src/lib/setup-defaults.ts +352 -11
  262. package/src/lib/tool-definitions.ts +3 -4
  263. package/src/lib/validation/schemas.test.ts +26 -0
  264. package/src/lib/validation/schemas.ts +62 -1
  265. package/src/lib/ws-client.ts +14 -12
  266. package/src/proxy.ts +5 -5
  267. package/src/stores/use-app-store.ts +43 -7
  268. package/src/stores/use-chat-store.ts +31 -2
  269. package/src/stores/use-chatroom-store.ts +153 -26
  270. package/src/types/index.ts +470 -44
  271. package/src/app/api/chats/[id]/main-loop/route.ts +0 -94
  272. package/src/components/chat/new-chat-sheet.tsx +0 -253
  273. package/src/lib/server/main-session.ts +0 -17
  274. package/src/lib/server/session-run-manager.test.ts +0 -26
@@ -3,15 +3,24 @@
3
3
  import { useAppStore } from '@/stores/use-app-store'
4
4
  import { IconButton } from '@/components/shared/icon-button'
5
5
  import { NotificationCenter } from '@/components/shared/notification-center'
6
+ import { AgentAvatar } from '@/components/agents/agent-avatar'
6
7
 
7
8
  export function MobileHeader() {
8
9
  const toggleSidebar = useAppStore((s) => s.toggleSidebar)
9
10
  const currentSessionId = useAppStore((s) => s.currentSessionId)
10
11
  const sessions = useAppStore((s) => s.sessions)
12
+ const agents = useAppStore((s) => s.agents)
11
13
  const session = currentSessionId ? sessions[currentSessionId] : null
14
+ const agent = session?.agentId ? agents[session.agentId] : null
15
+ const title = agent?.name || session?.name || 'SwarmClaw'
16
+ const subtitle = agent
17
+ ? 'Agent chat'
18
+ : session
19
+ ? 'Direct chat'
20
+ : 'Workspace'
12
21
 
13
22
  return (
14
- <header className="flex items-center gap-3 px-4 py-2.5 border-b border-white/[0.04] bg-bg/80 backdrop-blur-md shrink-0 min-h-[48px]"
23
+ <header className="flex items-center gap-3 px-4 py-2.5 border-b border-white/[0.04] bg-bg/80 backdrop-blur-md shrink-0 min-h-[56px]"
15
24
  style={{ paddingTop: 'max(10px, env(safe-area-inset-top))' }}>
16
25
  <IconButton onClick={toggleSidebar} aria-label="Toggle sidebar">
17
26
  <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round">
@@ -20,13 +29,22 @@ export function MobileHeader() {
20
29
  <line x1="3" y1="17" x2="18" y2="17" />
21
30
  </svg>
22
31
  </IconButton>
23
- <h1 className="font-display text-[14px] font-600 tracking-[-0.02em] flex-1 truncate">
24
- {session ? (
25
- <span className="block truncate">{session.name}</span>
26
- ) : (
27
- <span className="font-700">SwarmClaw</span>
28
- )}
29
- </h1>
32
+ {agent && (
33
+ <AgentAvatar
34
+ seed={agent.avatarSeed || null}
35
+ avatarUrl={agent.avatarUrl}
36
+ name={agent.name}
37
+ size={28}
38
+ />
39
+ )}
40
+ <div className="flex-1 min-w-0">
41
+ <h1 className="font-display text-[14px] font-600 tracking-[-0.02em] truncate">
42
+ {title}
43
+ </h1>
44
+ <p className="text-[10px] text-text-3/60 truncate mt-0.5">
45
+ {subtitle}
46
+ </p>
47
+ </div>
30
48
  <NotificationCenter />
31
49
  </header>
32
50
  )
@@ -2,6 +2,7 @@
2
2
 
3
3
  import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
4
4
  import { searchMemory } from '@/lib/memory'
5
+ import { deriveMemoryScope, getMemoryTier } from '@/lib/memory-presentation'
5
6
  import { useAppStore } from '@/stores/use-app-store'
6
7
  import { MemoryCard } from './memory-card'
7
8
  import { MemoryDetail } from './memory-detail'
@@ -14,6 +15,10 @@ export function MemoryBrowser() {
14
15
  const refreshKey = useAppStore((s) => s.memoryRefreshKey)
15
16
  const agents = useAppStore((s) => s.agents)
16
17
  const memoryAgentFilter = useAppStore((s) => s.memoryAgentFilter)
18
+ const memoryTierFilter = useAppStore((s) => s.memoryTierFilter)
19
+ const setMemoryTierFilter = useAppStore((s) => s.setMemoryTierFilter)
20
+ const memoryScopeFilter = useAppStore((s) => s.memoryScopeFilter)
21
+ const setMemoryScopeFilter = useAppStore((s) => s.setMemoryScopeFilter)
17
22
 
18
23
  const [search, setSearch] = useState('')
19
24
  const [entries, setEntries] = useState<MemoryEntry[]>([])
@@ -32,14 +37,24 @@ export function MemoryBrowser() {
32
37
 
33
38
  const load = useCallback(async (query: string) => {
34
39
  try {
35
- const results = await searchMemory({ q: query || undefined, agentId: apiAgentId })
40
+ const scope = memoryAgentFilter === '_global'
41
+ ? 'global'
42
+ : memoryAgentFilter
43
+ ? 'auto'
44
+ : 'all'
45
+ const results = await searchMemory({
46
+ q: query || undefined,
47
+ agentId: apiAgentId,
48
+ scope,
49
+ limit: 120,
50
+ })
36
51
  setEntries(Array.isArray(results) ? results : [])
37
52
  setError(null)
38
53
  } catch {
39
54
  setError('Unable to load memories right now.')
40
55
  }
41
56
  setLoaded(true)
42
- }, [apiAgentId])
57
+ }, [apiAgentId, memoryAgentFilter])
43
58
 
44
59
  useEffect(() => {
45
60
  searchRef.current = search
@@ -70,12 +85,23 @@ export function MemoryBrowser() {
70
85
 
71
86
  const filtered = useMemo(() => {
72
87
  return entries.filter((e) => {
73
- // Client-side global filter
74
88
  if (memoryAgentFilter === '_global' && e.agentId) return false
89
+ if (memoryAgentFilter && memoryAgentFilter !== '_global') {
90
+ const visibleToAgent = e.agentId === memoryAgentFilter || (Array.isArray(e.sharedWith) && e.sharedWith.includes(memoryAgentFilter)) || !e.agentId
91
+ if (!visibleToAgent) return false
92
+ }
93
+ const scope = deriveMemoryScope(e)
94
+ if (memoryScopeFilter !== 'all') {
95
+ if (memoryScopeFilter === 'global' && scope !== 'global') return false
96
+ if (memoryScopeFilter === 'agent' && scope !== 'agent' && scope !== 'shared') return false
97
+ if (memoryScopeFilter === 'session' && scope !== 'session') return false
98
+ if (memoryScopeFilter === 'project' && scope !== 'project') return false
99
+ }
100
+ if (memoryTierFilter !== 'all' && getMemoryTier(e) !== memoryTierFilter) return false
75
101
  if (categoryFilter && (e.category || 'note') !== categoryFilter) return false
76
102
  return true
77
103
  })
78
- }, [entries, memoryAgentFilter, categoryFilter])
104
+ }, [entries, memoryAgentFilter, memoryScopeFilter, memoryTierFilter, categoryFilter])
79
105
 
80
106
  const filterLabel = useMemo(() => {
81
107
  if (!memoryAgentFilter) return 'All Memories'
@@ -135,6 +161,41 @@ export function MemoryBrowser() {
135
161
  text-[13px] outline-none transition-all duration-200 placeholder:text-text-3/70 focus-glow"
136
162
  style={{ fontFamily: 'inherit' }}
137
163
  />
164
+ <div className="mt-2.5 flex flex-wrap items-center gap-1.5">
165
+ {(['all', 'global', 'agent', 'session', 'project'] as const).map((scope) => (
166
+ <button
167
+ key={scope}
168
+ type="button"
169
+ onClick={() => setMemoryScopeFilter(scope)}
170
+ className={`px-2.5 py-1 rounded-[8px] text-[10px] font-700 uppercase tracking-[0.08em] border transition-all ${
171
+ memoryScopeFilter === scope
172
+ ? 'bg-accent-soft text-accent-bright border-accent-bright/15'
173
+ : 'bg-transparent text-text-3/70 border-white/[0.05] hover:text-text-2 hover:bg-white/[0.03]'
174
+ }`}
175
+ >
176
+ {scope === 'agent' ? 'private/shared' : scope}
177
+ </button>
178
+ ))}
179
+ </div>
180
+ <div className="mt-1.5 flex flex-wrap items-center gap-1.5">
181
+ {(['all', 'working', 'durable', 'archive'] as const).map((tier) => (
182
+ <button
183
+ key={tier}
184
+ type="button"
185
+ onClick={() => setMemoryTierFilter(tier)}
186
+ className={`px-2.5 py-1 rounded-[8px] text-[10px] font-700 uppercase tracking-[0.08em] border transition-all ${
187
+ memoryTierFilter === tier
188
+ ? 'bg-white/[0.08] text-text-2 border-white/[0.10]'
189
+ : 'bg-transparent text-text-3/70 border-white/[0.05] hover:text-text-2 hover:bg-white/[0.03]'
190
+ }`}
191
+ >
192
+ {tier}
193
+ </button>
194
+ ))}
195
+ </div>
196
+ <p className="mt-2 text-[11px] text-text-3/55">
197
+ Scope shows what kind of memory it is. Tier shows how long it should stay salient.
198
+ </p>
138
199
  </div>
139
200
 
140
201
  {/* Category chips */}
@@ -207,8 +268,12 @@ export function MemoryBrowser() {
207
268
  <path d="M3 5v14c0 1.66 4 3 9 3s9-1.34 9-3V5" />
208
269
  </svg>
209
270
  </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>
271
+ <p className="font-display text-[14px] font-600 text-text-2">No memories match these filters</p>
272
+ <p className="text-[12px] text-text-3/50">
273
+ {memoryScopeFilter === 'all' && memoryTierFilter === 'all'
274
+ ? 'Agents store knowledge here as they learn'
275
+ : `Try a different ${memoryScopeFilter !== 'all' ? 'scope' : 'tier'} filter`}
276
+ </p>
212
277
  </div>
213
278
  ) : null
214
279
  ) : (
@@ -2,6 +2,7 @@
2
2
 
3
3
  import type { MemoryEntry } from '@/types'
4
4
  import { AgentAvatar } from '@/components/agents/agent-avatar'
5
+ import { deriveMemoryScope, getMemoryScopeLabel, getMemoryTier } from '@/lib/memory-presentation'
5
6
 
6
7
  function timeAgo(ts: number): string {
7
8
  if (!ts) return ''
@@ -22,6 +23,9 @@ interface Props {
22
23
  }
23
24
 
24
25
  export function MemoryCard({ entry, active, agentName, agentAvatarSeed, agentAvatarUrl, onClick }: Props) {
26
+ const scope = deriveMemoryScope(entry)
27
+ const tier = getMemoryTier(entry)
28
+
25
29
  return (
26
30
  <div
27
31
  onClick={onClick}
@@ -51,6 +55,20 @@ export function MemoryCard({ entry, active, agentName, agentAvatarSeed, agentAva
51
55
  <div className="text-[12px] text-text-2/40 mt-1 line-clamp-3 leading-relaxed">
52
56
  {entry.content || '(empty)'}
53
57
  </div>
58
+ <div className="mt-2 flex flex-wrap items-center gap-1.5">
59
+ <span className="px-1.5 py-0.5 rounded-[5px] text-[9px] font-700 uppercase tracking-[0.08em] bg-white/[0.04] text-text-3/75">
60
+ {getMemoryScopeLabel(scope)}
61
+ </span>
62
+ <span className={`px-1.5 py-0.5 rounded-[5px] text-[9px] font-700 uppercase tracking-[0.08em] ${
63
+ tier === 'working'
64
+ ? 'bg-amber-400/10 text-amber-300'
65
+ : tier === 'archive'
66
+ ? 'bg-sky-400/10 text-sky-300'
67
+ : 'bg-emerald-400/10 text-emerald-300'
68
+ }`}>
69
+ {tier}
70
+ </span>
71
+ </div>
54
72
  {(entry.image?.path || entry.imagePath) && (
55
73
  <div className="mt-2 w-10 h-10 rounded-[6px] overflow-hidden bg-white/[0.04] shrink-0">
56
74
  {/* eslint-disable-next-line @next/next/no-img-element */}
@@ -3,7 +3,9 @@
3
3
  import { useEffect, useState, useCallback } from 'react'
4
4
  import { useAppStore } from '@/stores/use-app-store'
5
5
  import { getMemory, updateMemory, deleteMemory } from '@/lib/memory'
6
+ import { deriveMemoryScope, getMemoryScopeLabel, getMemoryTier } from '@/lib/memory-presentation'
6
7
  import { AgentAvatar } from '@/components/agents/agent-avatar'
8
+ import { ConfirmDialog } from '@/components/shared/confirm-dialog'
7
9
  import type { MemoryEntry } from '@/types'
8
10
 
9
11
  const CATEGORIES = ['note', 'fact', 'preference', 'finding', 'learning', 'general']
@@ -22,6 +24,7 @@ export function MemoryDetail() {
22
24
  const [title, setTitle] = useState('')
23
25
  const [content, setContent] = useState('')
24
26
  const [category, setCategory] = useState('note')
27
+ const [editTier, setEditTier] = useState<'working' | 'durable' | 'archive'>('durable')
25
28
  const [editAgentId, setEditAgentId] = useState<string | null>(null)
26
29
  const [editSharedWith, setEditSharedWith] = useState<string[]>([])
27
30
  const [saving, setSaving] = useState(false)
@@ -53,6 +56,7 @@ export function MemoryDetail() {
53
56
  setTitle(resolved.title)
54
57
  setContent(resolved.content)
55
58
  setCategory(resolved.category || 'note')
59
+ setEditTier(getMemoryTier(resolved))
56
60
  setEditAgentId(resolved.agentId || null)
57
61
  setEditSharedWith(resolved.sharedWith || [])
58
62
  setEditing(false)
@@ -97,6 +101,12 @@ export function MemoryDetail() {
97
101
  category,
98
102
  agentId: editAgentId,
99
103
  sharedWith: editSharedWith.length ? editSharedWith : undefined,
104
+ metadata: {
105
+ ...(entry.metadata || {}),
106
+ tier: editTier,
107
+ scope: editAgentId ? 'agent' : 'global',
108
+ visibility: editAgentId ? (editSharedWith.length ? 'shared' : 'private') : 'global',
109
+ },
100
110
  })
101
111
  setEntry(updated)
102
112
  setEditing(false)
@@ -151,6 +161,8 @@ export function MemoryDetail() {
151
161
 
152
162
  const agentName = entry.agentId ? (agents[entry.agentId]?.name || entry.agentId) : null
153
163
  const sessionName = entry.sessionId ? (sessions[entry.sessionId]?.name || entry.sessionId) : null
164
+ const scope = deriveMemoryScope(entry)
165
+ const tier = getMemoryTier(entry)
154
166
  const imagePath = entry.image?.path || entry.imagePath || null
155
167
  const imageUrl = imagePath
156
168
  ? imagePath.startsWith('data/memory-images/')
@@ -171,6 +183,18 @@ export function MemoryDetail() {
171
183
  <span className="shrink-0 text-[10px] font-700 uppercase tracking-wider text-accent-bright/70 bg-accent-soft px-2 py-0.5 rounded-[6px]">
172
184
  {entry.category || 'note'}
173
185
  </span>
186
+ <span className="shrink-0 text-[10px] font-700 uppercase tracking-wider text-text-3/70 bg-white/[0.04] px-2 py-0.5 rounded-[6px]">
187
+ {getMemoryScopeLabel(scope)}
188
+ </span>
189
+ <span className={`shrink-0 text-[10px] font-700 uppercase tracking-wider px-2 py-0.5 rounded-[6px] ${
190
+ tier === 'working'
191
+ ? 'bg-amber-400/10 text-amber-300'
192
+ : tier === 'archive'
193
+ ? 'bg-sky-400/10 text-sky-300'
194
+ : 'bg-emerald-400/10 text-emerald-300'
195
+ }`}>
196
+ {tier}
197
+ </span>
174
198
  {!editing && (
175
199
  <h2 className="font-display text-[16px] font-700 truncate tracking-[-0.02em]">{entry.title || 'Untitled'}</h2>
176
200
  )}
@@ -216,6 +240,7 @@ export function MemoryDetail() {
216
240
  setTitle(entry.title)
217
241
  setContent(entry.content)
218
242
  setCategory(entry.category || 'note')
243
+ setEditTier(getMemoryTier(entry))
219
244
  setEditAgentId(entry.agentId || null)
220
245
  setEditSharedWith(entry.sharedWith || [])
221
246
  setEditing(false)
@@ -301,9 +326,23 @@ export function MemoryDetail() {
301
326
  </div>
302
327
  </div>
303
328
 
329
+ <div>
330
+ <label className="block text-[11px] font-600 text-text-3/60 uppercase tracking-[0.06em] mb-2">Tier</label>
331
+ <select
332
+ value={editTier}
333
+ onChange={(e) => setEditTier(e.target.value as typeof editTier)}
334
+ className={inputClass}
335
+ style={{ fontFamily: 'inherit' }}
336
+ >
337
+ <option value="working">Working</option>
338
+ <option value="durable">Durable</option>
339
+ <option value="archive">Archive</option>
340
+ </select>
341
+ </div>
342
+
304
343
  {/* Agent assignment */}
305
344
  <div>
306
- <label className="block text-[11px] font-600 text-text-3/60 uppercase tracking-[0.06em] mb-2">Assigned to</label>
345
+ <label className="block text-[11px] font-600 text-text-3/60 uppercase tracking-[0.06em] mb-2">Visibility</label>
307
346
  <div className="flex gap-1.5 flex-wrap">
308
347
  <button
309
348
  onClick={() => setEditAgentId(null)}
@@ -522,10 +561,18 @@ export function MemoryDetail() {
522
561
  </div>
523
562
  {entry.agentId && (
524
563
  <div>
525
- <span className="text-text-3/70 block mb-1">Agent</span>
564
+ <span className="text-text-3/70 block mb-1">Owner</span>
526
565
  <span className="text-text-3/60 font-mono">{agentName}</span>
527
566
  </div>
528
567
  )}
568
+ <div>
569
+ <span className="text-text-3/70 block mb-1">Scope</span>
570
+ <span className="text-text-3/60 font-mono">{getMemoryScopeLabel(scope)}</span>
571
+ </div>
572
+ <div>
573
+ <span className="text-text-3/70 block mb-1">Tier</span>
574
+ <span className="text-text-3/60 font-mono">{tier}</span>
575
+ </div>
529
576
  {entry.sessionId && (
530
577
  <div>
531
578
  <span className="text-text-3/70 block mb-1">Chat</span>
@@ -544,35 +591,15 @@ export function MemoryDetail() {
544
591
  </div>
545
592
  </div>
546
593
 
547
- {/* Delete confirmation */}
548
- {confirmDelete && (
549
- <div className="fixed inset-0 z-50 flex items-center justify-center">
550
- <div className="absolute inset-0 bg-black/60 backdrop-blur-sm" onClick={() => setConfirmDelete(false)} />
551
- <div className="relative bg-raised rounded-[16px] p-6 max-w-[360px] w-full shadow-xl border border-white/[0.06]"
552
- style={{ animation: 'fade-in 0.15s cubic-bezier(0.16, 1, 0.3, 1)' }}>
553
- <h3 className="font-display text-[16px] font-700 mb-2">Delete Memory</h3>
554
- <p className="text-[13px] text-text-3 mb-5">
555
- Delete &ldquo;{entry.title}&rdquo;? This cannot be undone.
556
- </p>
557
- <div className="flex gap-3">
558
- <button
559
- onClick={() => setConfirmDelete(false)}
560
- className="flex-1 py-2.5 rounded-[10px] border border-white/[0.08] bg-transparent text-text-2 text-[13px] font-600 cursor-pointer hover:bg-surface-2 transition-all"
561
- style={{ fontFamily: 'inherit' }}
562
- >
563
- Cancel
564
- </button>
565
- <button
566
- onClick={handleDelete}
567
- className="flex-1 py-2.5 rounded-[10px] border-none bg-red-500/90 text-white text-[13px] font-600 cursor-pointer active:scale-[0.97] transition-all hover:bg-red-500"
568
- style={{ fontFamily: 'inherit' }}
569
- >
570
- Delete
571
- </button>
572
- </div>
573
- </div>
574
- </div>
575
- )}
594
+ <ConfirmDialog
595
+ open={confirmDelete}
596
+ title="Delete Memory"
597
+ message={`Delete "${entry.title}"? This cannot be undone.`}
598
+ confirmLabel="Delete"
599
+ danger
600
+ onCancel={() => setConfirmDelete(false)}
601
+ onConfirm={() => { void handleDelete() }}
602
+ />
576
603
  </div>
577
604
  )
578
605
  }
@@ -3,6 +3,7 @@
3
3
  import { useState } from 'react'
4
4
  import { useAppStore } from '@/stores/use-app-store'
5
5
  import { createMemory } from '@/lib/memory'
6
+ import { getMemoryTierForCategory } from '@/lib/memory-presentation'
6
7
  import { BottomSheet } from '@/components/shared/bottom-sheet'
7
8
  import { AgentAvatar } from '@/components/agents/agent-avatar'
8
9
  import { SheetFooter } from '@/components/shared/sheet-footer'
@@ -24,6 +25,7 @@ export function MemorySheet() {
24
25
  const [title, setTitle] = useState('')
25
26
  const [content, setContent] = useState('')
26
27
  const [category, setCategory] = useState('note')
28
+ const [tier, setTier] = useState<'working' | 'durable' | 'archive'>(getMemoryTierForCategory('note'))
27
29
  const [agentId, setAgentId] = useState<string | null>(defaultAgentId)
28
30
  const [sharedWith, setSharedWith] = useState<string[]>([])
29
31
  const [saving, setSaving] = useState(false)
@@ -36,6 +38,7 @@ export function MemorySheet() {
36
38
  setTitle('')
37
39
  setContent('')
38
40
  setCategory('note')
41
+ setTier(getMemoryTierForCategory('note'))
39
42
  setSaving(false)
40
43
  } else if (!open && prevOpen) {
41
44
  setPrevOpen(false)
@@ -56,6 +59,11 @@ export function MemorySheet() {
56
59
  agentId,
57
60
  sessionId: null,
58
61
  sharedWith: sharedWith.length ? sharedWith : undefined,
62
+ metadata: {
63
+ tier,
64
+ scope: agentId ? 'agent' : 'global',
65
+ visibility: agentId ? (sharedWith.length ? 'shared' : 'private') : 'global',
66
+ },
59
67
  })
60
68
  triggerRefresh()
61
69
  onClose()
@@ -77,7 +85,7 @@ export function MemorySheet() {
77
85
 
78
86
  {/* Agent selector */}
79
87
  <div className="mb-6">
80
- <label className="block font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em] mb-3">Assign to</label>
88
+ <label className="block font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em] mb-3">Visibility</label>
81
89
  <div className="flex gap-2 flex-wrap">
82
90
  <button
83
91
  onClick={() => setAgentId(null)}
@@ -111,12 +119,12 @@ export function MemorySheet() {
111
119
  </div>
112
120
  {selectedAgent && (
113
121
  <p className="text-[11px] text-text-3/50 mt-2">
114
- This memory will be available to <span className="text-text-2">{selectedAgent.name}</span> during conversations
122
+ Owned by <span className="text-text-2">{selectedAgent.name}</span>. Add collaborators below if other agents should be able to recall it too.
115
123
  </p>
116
124
  )}
117
125
  {!agentId && (
118
126
  <p className="text-[11px] text-text-3/50 mt-2">
119
- Global memories are accessible to all agents
127
+ Global memories are accessible to every agent in the workspace.
120
128
  </p>
121
129
  )}
122
130
  </div>
@@ -167,7 +175,10 @@ export function MemorySheet() {
167
175
  {CATEGORIES.map((c) => (
168
176
  <button
169
177
  key={c}
170
- onClick={() => setCategory(c)}
178
+ onClick={() => {
179
+ setCategory(c)
180
+ setTier(getMemoryTierForCategory(c))
181
+ }}
171
182
  className={`px-3 py-1.5 rounded-[8px] text-[12px] font-600 capitalize cursor-pointer transition-all border-none
172
183
  ${category === c
173
184
  ? 'bg-accent-soft text-accent-bright'
@@ -180,6 +191,23 @@ export function MemorySheet() {
180
191
  </div>
181
192
  </div>
182
193
 
194
+ <div className="mb-6">
195
+ <label className="block font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em] mb-3">Tier</label>
196
+ <select
197
+ value={tier}
198
+ onChange={(e) => setTier(e.target.value as typeof tier)}
199
+ className={inputClass}
200
+ style={{ fontFamily: 'inherit' }}
201
+ >
202
+ <option value="working">Working: short-horizon, active context</option>
203
+ <option value="durable">Durable: keep this around as reusable knowledge</option>
204
+ <option value="archive">Archive: preserve, but keep less salient</option>
205
+ </select>
206
+ <p className="text-[11px] text-text-3/50 mt-2">
207
+ Tier controls how aggressively this memory should stay in active recall.
208
+ </p>
209
+ </div>
210
+
183
211
  {/* Content */}
184
212
  <div className="mb-8">
185
213
  <label className="block font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em] mb-3">Content</label>
@@ -62,8 +62,8 @@ export function PluginList({ inSidebar }: { inSidebar?: boolean }) {
62
62
  }, [tab, inSidebar, loadMarketplace])
63
63
 
64
64
  const pluginList = Object.values(plugins)
65
- const corePlugins = useMemo(() => pluginList.filter((p) => p.source === 'local'), [pluginList])
66
- const extensionPlugins = useMemo(() => pluginList.filter((p) => p.source !== 'local'), [pluginList])
65
+ const corePlugins = useMemo(() => pluginList.filter((p) => p.isBuiltin), [pluginList])
66
+ const extensionPlugins = useMemo(() => pluginList.filter((p) => !p.isBuiltin), [pluginList])
67
67
 
68
68
  // Search filtering for installed plugins
69
69
  const filterInstalled = useCallback((list: PluginMeta[]) => {
@@ -293,7 +293,7 @@ function TabButton({ active, onClick, count, children }: {
293
293
  function pluginDescription(plugin: PluginMeta): string {
294
294
  const raw = (plugin.description || '').trim()
295
295
  if (raw) return raw
296
- const sourceLabel = plugin.source === 'local' ? 'core plugin' : 'installed plugin'
296
+ const sourceLabel = plugin.isBuiltin ? 'core plugin' : 'installed plugin'
297
297
  return `No description provided. Click to view metadata and controls for this ${sourceLabel}.`
298
298
  }
299
299
 
@@ -304,6 +304,7 @@ function pluginCapabilityBadges(plugin: PluginMeta): string[] {
304
304
  if (plugin.hasUI) badges.push('UI')
305
305
  if (plugin.providerCount && plugin.providerCount > 0) badges.push(`${plugin.providerCount} provider${plugin.providerCount === 1 ? '' : 's'}`)
306
306
  if (plugin.connectorCount && plugin.connectorCount > 0) badges.push(`${plugin.connectorCount} connector${plugin.connectorCount === 1 ? '' : 's'}`)
307
+ if (plugin.hasDependencyManifest) badges.push(`${plugin.dependencyCount ?? 0} dep${plugin.dependencyCount === 1 ? '' : 's'}`)
307
308
  return badges
308
309
  }
309
310
 
@@ -446,6 +447,17 @@ function PluginCard({ plugin, allowDelete, agents, onEdit, onToggle, onDelete, o
446
447
  {badge}
447
448
  </span>
448
449
  ))}
450
+ {plugin.hasDependencyManifest && (
451
+ <span className={`text-[10px] font-700 px-1.5 py-0.5 rounded-full ${
452
+ plugin.dependencyInstallStatus === 'installed'
453
+ ? 'text-emerald-400 bg-emerald-500/10'
454
+ : plugin.dependencyInstallStatus === 'error'
455
+ ? 'text-red-400 bg-red-500/10'
456
+ : 'text-amber-400 bg-amber-500/10'
457
+ }`}>
458
+ deps {plugin.dependencyInstallStatus || 'ready'}
459
+ </span>
460
+ )}
449
461
  {plugin.author && (
450
462
  <span className="text-[10px] text-text-3/40 ml-auto">
451
463
  {plugin.author}