@swarmclawai/swarmclaw 1.2.6 → 1.2.9

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 (269) hide show
  1. package/README.md +54 -23
  2. package/next.config.ts +1 -0
  3. package/package.json +4 -3
  4. package/scripts/easy-setup.mjs +1 -1
  5. package/scripts/postinstall.mjs +1 -1
  6. package/skills/swarmclaw.md +115 -0
  7. package/skills/tools/browser.md +131 -0
  8. package/skills/tools/execute.md +98 -0
  9. package/skills/tools/files.md +98 -0
  10. package/skills/tools/memory.md +104 -0
  11. package/skills/tools/platform.md +144 -0
  12. package/skills/tools/skills.md +83 -0
  13. package/src/app/agents/[id]/page.tsx +1 -18
  14. package/src/app/api/agents/thread-route.test.ts +0 -1
  15. package/src/app/api/approvals/route.test.ts +6 -22
  16. package/src/app/api/chats/[id]/messages/route.ts +23 -19
  17. package/src/app/api/chats/messages-route.test.ts +105 -51
  18. package/src/app/api/connectors/route.ts +2 -2
  19. package/src/app/api/mcp-servers/[id]/test/route.ts +3 -2
  20. package/src/app/api/openclaw/deploy/route.ts +2 -0
  21. package/src/app/api/portability/export/route.ts +8 -0
  22. package/src/app/api/portability/import/route.test.ts +80 -0
  23. package/src/app/api/portability/import/route.ts +28 -0
  24. package/src/app/api/settings/route.ts +0 -2
  25. package/src/app/api/setup/doctor/route.ts +4 -4
  26. package/src/app/api/wallets/[id]/route.ts +15 -157
  27. package/src/app/api/wallets/generate/route.ts +22 -0
  28. package/src/app/api/wallets/route.test.ts +147 -0
  29. package/src/app/api/wallets/route.ts +13 -95
  30. package/src/app/autonomy/page.tsx +2 -57
  31. package/src/app/protocols/page.tsx +2 -21
  32. package/src/app/settings/page.tsx +0 -9
  33. package/src/app/wallets/page.tsx +105 -5
  34. package/src/cli/index.js +21 -33
  35. package/src/cli/spec.js +19 -30
  36. package/src/components/agents/agent-chat-list.tsx +23 -1
  37. package/src/components/agents/agent-sheet.tsx +2 -40
  38. package/src/components/agents/inspector-panel.tsx +165 -131
  39. package/src/components/chat/chat-area.tsx +38 -9
  40. package/src/components/chat/chat-card.tsx +0 -31
  41. package/src/components/chat/message-bubble.tsx +1 -108
  42. package/src/components/chat/message-list.tsx +33 -19
  43. package/src/components/connectors/connector-sheet.tsx +25 -1
  44. package/src/components/gateways/gateway-sheet.tsx +5 -2
  45. package/src/components/layout/sidebar-rail.tsx +6 -10
  46. package/src/components/projects/project-detail.tsx +3 -35
  47. package/src/components/projects/tabs/overview-tab.tsx +3 -59
  48. package/src/components/projects/tabs/work-tab.tsx +7 -77
  49. package/src/components/protocols/structured-session-launcher.tsx +1 -22
  50. package/src/components/shared/connector-platform-icon.tsx +1 -0
  51. package/src/components/tasks/task-card.tsx +4 -34
  52. package/src/components/tasks/task-sheet.tsx +6 -36
  53. package/src/components/wallets/wallet-list.tsx +150 -0
  54. package/src/lib/agent-execute-defaults.test.ts +24 -0
  55. package/src/lib/agent-execute-defaults.ts +62 -0
  56. package/src/lib/app/navigation.test.ts +0 -13
  57. package/src/lib/app/navigation.ts +2 -7
  58. package/src/lib/app/view-constants.ts +14 -19
  59. package/src/lib/chat/queued-message-queue.test.ts +134 -1
  60. package/src/lib/chat/queued-message-queue.ts +77 -2
  61. package/src/lib/server/agents/agent-service.ts +5 -0
  62. package/src/lib/server/agents/agent-thread-session.ts +0 -1
  63. package/src/lib/server/agents/delegation-advisory.test.ts +0 -1
  64. package/src/lib/server/agents/delegation-jobs.test.ts +0 -69
  65. package/src/lib/server/agents/delegation-jobs.ts +0 -25
  66. package/src/lib/server/agents/main-agent-loop.ts +1 -49
  67. package/src/lib/server/agents/subagent-runtime.ts +0 -1
  68. package/src/lib/server/approval-match.ts +0 -85
  69. package/src/lib/server/approvals.test.ts +6 -6
  70. package/src/lib/server/approvals.ts +0 -6
  71. package/src/lib/server/autonomy/supervisor-reflection.test.ts +0 -1
  72. package/src/lib/server/builtin-extensions.ts +1 -2
  73. package/src/lib/server/capability-router.test.ts +0 -2
  74. package/src/lib/server/chat-execution/chat-execution-advanced.test.ts +1 -1
  75. package/src/lib/server/chat-execution/chat-execution-tool-events.test.ts +15 -14
  76. package/src/lib/server/chat-execution/chat-execution-types.ts +0 -2
  77. package/src/lib/server/chat-execution/chat-execution-utils.ts +2 -4
  78. package/src/lib/server/chat-execution/chat-streaming-utils.ts +2 -30
  79. package/src/lib/server/chat-execution/chat-turn-finalization.ts +1 -36
  80. package/src/lib/server/chat-execution/chat-turn-preparation.ts +81 -64
  81. package/src/lib/server/chat-execution/chat-turn-stream-execution.ts +4 -0
  82. package/src/lib/server/chat-execution/continuation-evaluator.ts +8 -0
  83. package/src/lib/server/chat-execution/iteration-event-handler.ts +0 -24
  84. package/src/lib/server/chat-execution/memory-mutation-tools.ts +1 -1
  85. package/src/lib/server/chat-execution/message-classifier.test.ts +0 -45
  86. package/src/lib/server/chat-execution/message-classifier.ts +11 -16
  87. package/src/lib/server/chat-execution/prompt-builder.test.ts +27 -0
  88. package/src/lib/server/chat-execution/prompt-builder.ts +14 -31
  89. package/src/lib/server/chat-execution/prompt-mode.test.ts +24 -0
  90. package/src/lib/server/chat-execution/prompt-mode.ts +5 -1
  91. package/src/lib/server/chat-execution/prompt-sections.ts +0 -1
  92. package/src/lib/server/chat-execution/situational-awareness.test.ts +2 -73
  93. package/src/lib/server/chat-execution/situational-awareness.ts +4 -38
  94. package/src/lib/server/chat-execution/stream-agent-chat.test.ts +13 -126
  95. package/src/lib/server/chat-execution/stream-agent-chat.ts +46 -21
  96. package/src/lib/server/chat-execution/stream-continuation.test.ts +4 -52
  97. package/src/lib/server/chat-execution/stream-continuation.ts +6 -48
  98. package/src/lib/server/chatrooms/chatroom-routing.test.ts +4 -0
  99. package/src/lib/server/chatrooms/session-mailbox.ts +0 -10
  100. package/src/lib/server/chats/chat-session-service.ts +3 -5
  101. package/src/lib/server/connectors/connector-inbound.ts +0 -1
  102. package/src/lib/server/connectors/connector-lifecycle.ts +19 -3
  103. package/src/lib/server/connectors/connector-service.ts +39 -9
  104. package/src/lib/server/connectors/discord.ts +2 -2
  105. package/src/lib/server/connectors/matrix.ts +3 -2
  106. package/src/lib/server/connectors/signal.ts +5 -4
  107. package/src/lib/server/connectors/slack.ts +10 -9
  108. package/src/lib/server/connectors/swarmdock-bidding.ts +74 -0
  109. package/src/lib/server/connectors/swarmdock-payloads.test.ts +85 -0
  110. package/src/lib/server/connectors/swarmdock-secret.test.ts +128 -0
  111. package/src/lib/server/connectors/swarmdock-secret.ts +152 -0
  112. package/src/lib/server/connectors/swarmdock-tasks.ts +119 -0
  113. package/src/lib/server/connectors/swarmdock.ts +255 -0
  114. package/src/lib/server/connectors/teams.ts +3 -2
  115. package/src/lib/server/connectors/telegram.ts +4 -4
  116. package/src/lib/server/connectors/whatsapp.ts +2 -2
  117. package/src/lib/server/daemon/controller.ts +7 -0
  118. package/src/lib/server/execution-brief.test.ts +2 -25
  119. package/src/lib/server/execution-brief.ts +12 -35
  120. package/src/lib/server/execution-engine/task-attempt.ts +0 -1
  121. package/src/lib/server/gateways/gateway-profile-service.ts +19 -1
  122. package/src/lib/server/messages/message-repository.test.ts +70 -0
  123. package/src/lib/server/messages/message-repository.ts +11 -6
  124. package/src/lib/server/openclaw/deploy.ts +32 -2
  125. package/src/lib/server/persistence/storage-context.ts +0 -5
  126. package/src/lib/server/plugins-advanced.test.ts +1 -2
  127. package/src/lib/server/portability/export.ts +109 -0
  128. package/src/lib/server/portability/import.ts +159 -0
  129. package/src/lib/server/protocols/protocol-normalization.ts +0 -4
  130. package/src/lib/server/protocols/protocol-queries.ts +0 -6
  131. package/src/lib/server/protocols/protocol-run-lifecycle.ts +4 -32
  132. package/src/lib/server/protocols/protocol-service.ts +0 -1
  133. package/src/lib/server/protocols/protocol-step-helpers.ts +0 -4
  134. package/src/lib/server/protocols/protocol-step-processors.ts +0 -6
  135. package/src/lib/server/protocols/protocol-swarm.ts +0 -2
  136. package/src/lib/server/protocols/protocol-types.ts +0 -2
  137. package/src/lib/server/provider-health.ts +1 -10
  138. package/src/lib/server/runtime/daemon-state/core.ts +0 -9
  139. package/src/lib/server/runtime/daemon-state.test.ts +0 -35
  140. package/src/lib/server/runtime/heartbeat-service.ts +3 -23
  141. package/src/lib/server/runtime/process-manager.ts +13 -9
  142. package/src/lib/server/runtime/queue/core.ts +11 -33
  143. package/src/lib/server/runtime/runtime-storage-write-paths.test.ts +6 -6
  144. package/src/lib/server/runtime/scheduler.ts +0 -13
  145. package/src/lib/server/runtime/session-run-manager/drain.ts +0 -24
  146. package/src/lib/server/runtime/session-run-manager/enqueue.ts +0 -1
  147. package/src/lib/server/runtime/session-run-manager/queries.ts +15 -1
  148. package/src/lib/server/runtime/session-run-manager/recovery.ts +0 -1
  149. package/src/lib/server/runtime/session-run-manager.test.ts +58 -28
  150. package/src/lib/server/sandbox/session-runtime.test.ts +18 -1
  151. package/src/lib/server/sandbox/session-runtime.ts +40 -28
  152. package/src/lib/server/session-tools/autonomy-tools.test.ts +7 -9
  153. package/src/lib/server/session-tools/context.ts +1 -1
  154. package/src/lib/server/session-tools/credential-env.ts +109 -0
  155. package/src/lib/server/session-tools/crud.ts +3 -17
  156. package/src/lib/server/session-tools/delegate.ts +0 -4
  157. package/src/lib/server/session-tools/edit_file.ts +3 -2
  158. package/src/lib/server/session-tools/execute.test.ts +58 -0
  159. package/src/lib/server/session-tools/execute.ts +334 -0
  160. package/src/lib/server/session-tools/files-tool.ts +635 -0
  161. package/src/lib/server/session-tools/index.ts +14 -8
  162. package/src/lib/server/session-tools/memory-tool.ts +242 -0
  163. package/src/lib/server/session-tools/memory.ts +1 -1
  164. package/src/lib/server/session-tools/openclaw-nodes.ts +3 -2
  165. package/src/lib/server/session-tools/openclaw-workspace.ts +3 -2
  166. package/src/lib/server/session-tools/platform-tool.ts +617 -0
  167. package/src/lib/server/session-tools/session-info.ts +3 -2
  168. package/src/lib/server/session-tools/session-tools-wiring.test.ts +3 -4
  169. package/src/lib/server/session-tools/shell.ts +7 -122
  170. package/src/lib/server/session-tools/skills-tool.ts +396 -0
  171. package/src/lib/server/session-tools/team-context.ts +0 -3
  172. package/src/lib/server/session-tools/web.ts +2 -2
  173. package/src/lib/server/storage-normalization.ts +10 -0
  174. package/src/lib/server/storage.ts +18 -45
  175. package/src/lib/server/tasks/task-checkout.ts +59 -0
  176. package/src/lib/server/tasks/task-lifecycle.ts +2 -0
  177. package/src/lib/server/tasks/task-route-service.ts +4 -26
  178. package/src/lib/server/tasks/task-service.ts +0 -7
  179. package/src/lib/server/tool-aliases.ts +2 -2
  180. package/src/lib/server/tool-capability-policy-advanced.test.ts +13 -6
  181. package/src/lib/server/tool-capability-policy.test.ts +2 -1
  182. package/src/lib/server/tool-capability-policy.ts +60 -35
  183. package/src/lib/server/tool-planning.ts +11 -12
  184. package/src/lib/server/universal-tool-access.ts +0 -1
  185. package/src/lib/server/wallets/wallet-crypto.ts +33 -0
  186. package/src/lib/server/wallets/wallet-repository.ts +24 -0
  187. package/src/lib/server/wallets/wallet-service.ts +119 -0
  188. package/src/lib/server/working-state/extraction.ts +8 -42
  189. package/src/lib/server/working-state/normalization.ts +10 -103
  190. package/src/lib/server/working-state/service.ts +12 -21
  191. package/src/lib/setup-defaults.ts +5 -0
  192. package/src/lib/strip-internal-metadata.test.ts +1 -1
  193. package/src/lib/strip-internal-metadata.ts +1 -1
  194. package/src/lib/tool-definitions.ts +1 -1
  195. package/src/lib/validation/schemas.test.ts +16 -0
  196. package/src/lib/validation/schemas.ts +49 -2
  197. package/src/stores/slices/data-slice.ts +5 -1
  198. package/src/stores/slices/ui-slice.ts +0 -4
  199. package/src/stores/use-chat-store.test.ts +231 -0
  200. package/src/stores/use-chat-store.ts +62 -13
  201. package/src/types/agent.ts +264 -0
  202. package/src/types/app-settings.ts +173 -0
  203. package/src/types/approval.ts +25 -0
  204. package/src/types/connector.ts +188 -0
  205. package/src/types/extension.ts +386 -0
  206. package/src/types/index.ts +16 -3555
  207. package/src/types/message.ts +56 -0
  208. package/src/types/misc.ts +737 -0
  209. package/src/types/protocol.ts +420 -0
  210. package/src/types/provider.ts +52 -0
  211. package/src/types/run.ts +180 -0
  212. package/src/types/schedule.ts +59 -0
  213. package/src/types/session.ts +215 -0
  214. package/src/types/skill.ts +157 -0
  215. package/src/types/swarmdock.ts +29 -0
  216. package/src/types/task.ts +144 -0
  217. package/src/types/working-state.ts +204 -0
  218. package/src/views/settings/section-heartbeat.tsx +2 -2
  219. package/src/views/settings/section-runtime-loop.tsx +0 -14
  220. package/src/app/api/canvas/[sessionId]/route.ts +0 -35
  221. package/src/app/api/missions/[id]/actions/route.ts +0 -31
  222. package/src/app/api/missions/[id]/events/route.ts +0 -14
  223. package/src/app/api/missions/[id]/route.ts +0 -10
  224. package/src/app/api/missions/route.test.ts +0 -244
  225. package/src/app/api/missions/route.ts +0 -57
  226. package/src/app/api/wallets/[id]/approve/route.ts +0 -79
  227. package/src/app/api/wallets/[id]/balance-history/route.ts +0 -18
  228. package/src/app/api/wallets/[id]/send/route.ts +0 -113
  229. package/src/app/api/wallets/[id]/transactions/route.ts +0 -18
  230. package/src/app/missions/[id]/page.tsx +0 -3
  231. package/src/app/missions/page.tsx +0 -685
  232. package/src/components/canvas/canvas-panel.tsx +0 -267
  233. package/src/components/wallets/wallet-approval-dialog.tsx +0 -107
  234. package/src/components/wallets/wallet-panel.tsx +0 -1010
  235. package/src/components/wallets/wallet-section.tsx +0 -260
  236. package/src/features/missions/queries.ts +0 -23
  237. package/src/lib/canvas-content.test.ts +0 -360
  238. package/src/lib/canvas-content.ts +0 -198
  239. package/src/lib/server/canvas-content.test.ts +0 -32
  240. package/src/lib/server/canvas-content.ts +0 -6
  241. package/src/lib/server/ethereum.ts +0 -591
  242. package/src/lib/server/evm-swap.ts +0 -476
  243. package/src/lib/server/missions/mission-intent.test.ts +0 -63
  244. package/src/lib/server/missions/mission-intent.ts +0 -569
  245. package/src/lib/server/missions/mission-repository.ts +0 -74
  246. package/src/lib/server/missions/mission-service/actions.ts +0 -6
  247. package/src/lib/server/missions/mission-service/bindings.ts +0 -9
  248. package/src/lib/server/missions/mission-service/context.ts +0 -4
  249. package/src/lib/server/missions/mission-service/core.ts +0 -2271
  250. package/src/lib/server/missions/mission-service/queries.ts +0 -12
  251. package/src/lib/server/missions/mission-service/recovery.ts +0 -5
  252. package/src/lib/server/missions/mission-service/ticks.ts +0 -9
  253. package/src/lib/server/missions/mission-service.test.ts +0 -888
  254. package/src/lib/server/missions/mission-service.ts +0 -6
  255. package/src/lib/server/session-tools/canvas.ts +0 -105
  256. package/src/lib/server/session-tools/sandbox.ts +0 -281
  257. package/src/lib/server/session-tools/wallet-tool.test.ts +0 -150
  258. package/src/lib/server/session-tools/wallet.ts +0 -1287
  259. package/src/lib/server/solana.ts +0 -327
  260. package/src/lib/server/wallet/wallet-execution.test.ts +0 -198
  261. package/src/lib/server/wallet/wallet-portfolio.test.ts +0 -98
  262. package/src/lib/server/wallet/wallet-portfolio.ts +0 -772
  263. package/src/lib/server/wallet/wallet-service.test.ts +0 -81
  264. package/src/lib/server/wallet/wallet-service.ts +0 -225
  265. package/src/lib/wallet/wallet-transactions.test.ts +0 -75
  266. package/src/lib/wallet/wallet-transactions.ts +0 -43
  267. package/src/lib/wallet/wallet.test.ts +0 -333
  268. package/src/lib/wallet/wallet.ts +0 -183
  269. package/src/views/settings/section-wallets.tsx +0 -35
@@ -1,260 +0,0 @@
1
- 'use client'
2
-
3
- import { useCallback, useEffect, useMemo, useState } from 'react'
4
- import { api } from '@/lib/app/api-client'
5
- import { copyTextToClipboard } from '@/lib/clipboard'
6
- import { useAppStore } from '@/stores/use-app-store'
7
- import type { AgentWallet, WalletAssetBalance, WalletPortfolioSummary, WalletChain } from '@/types'
8
- import { toast } from 'sonner'
9
- import { errorMessage } from '@/lib/shared-utils'
10
- import {
11
- SUPPORTED_WALLET_CHAINS,
12
- formatWalletAmount,
13
- getWalletBalanceAtomic,
14
- getWalletChainMeta,
15
- getWalletLimitAtomic,
16
- } from '@/lib/wallet/wallet'
17
-
18
- type SafeWallet = Omit<AgentWallet, 'encryptedPrivateKey'> & {
19
- balanceAtomic?: string
20
- balanceLamports?: number
21
- balanceFormatted?: string
22
- balanceSymbol?: string
23
- assets?: WalletAssetBalance[]
24
- portfolioSummary?: WalletPortfolioSummary
25
- isActive?: boolean
26
- }
27
-
28
- interface WalletSectionProps {
29
- agentId: string
30
- wallets: SafeWallet[]
31
- activeWalletId: string | null
32
- onWalletCreated: () => void
33
- }
34
-
35
- export function WalletSection({ agentId, wallets, activeWalletId, onWalletCreated }: WalletSectionProps) {
36
- const appSettings = useAppStore((s) => s.appSettings)
37
- const [creating, setCreating] = useState(false)
38
- const [activatingWalletId, setActivatingWalletId] = useState<string | null>(null)
39
- const [error, setError] = useState<string | null>(null)
40
- const [copiedWalletId, setCopiedWalletId] = useState<string | null>(null)
41
-
42
- const connectedChains = useMemo(() => new Set(wallets.map((wallet) => wallet.chain)), [wallets])
43
- const availableChains = useMemo(
44
- () => SUPPORTED_WALLET_CHAINS.filter((chain) => !connectedChains.has(chain)),
45
- [connectedChains],
46
- )
47
- const sortedWallets = useMemo(
48
- () => [...wallets].sort((a, b) => {
49
- const aActive = a.id === activeWalletId || a.isActive === true
50
- const bActive = b.id === activeWalletId || b.isActive === true
51
- if (aActive !== bActive) return aActive ? -1 : 1
52
- return a.chain.localeCompare(b.chain)
53
- }),
54
- [activeWalletId, wallets],
55
- )
56
-
57
- const [chain, setChain] = useState<WalletChain>(availableChains[0] || 'solana')
58
- const walletApprovalsEnabled = appSettings.walletApprovalsEnabled !== false
59
-
60
- useEffect(() => {
61
- if (availableChains.length === 0) return
62
- if (!availableChains.includes(chain)) setChain(availableChains[0])
63
- }, [availableChains, chain])
64
-
65
- const createWallet = useCallback(async () => {
66
- if (!availableChains.includes(chain)) return
67
- setCreating(true)
68
- setError(null)
69
- try {
70
- await api('POST', '/wallets', { agentId, chain })
71
- toast.success('Agent wallet created successfully')
72
- await onWalletCreated()
73
- } catch (err: unknown) {
74
- const msg = errorMessage(err)
75
- setError(msg)
76
- toast.error(msg)
77
- } finally {
78
- setCreating(false)
79
- }
80
- }, [agentId, availableChains, chain, onWalletCreated])
81
-
82
- const copyAddress = useCallback(async (wallet: SafeWallet) => {
83
- const copiedValue = await copyTextToClipboard(wallet.publicKey)
84
- if (!copiedValue) return
85
- setCopiedWalletId(wallet.id)
86
- setTimeout(() => {
87
- setCopiedWalletId((current) => current === wallet.id ? null : current)
88
- }, 2000)
89
- }, [])
90
-
91
- const setActiveWallet = useCallback(async (walletId: string) => {
92
- setActivatingWalletId(walletId)
93
- setError(null)
94
- try {
95
- await api('PATCH', `/wallets/${walletId}`, { makeActive: true })
96
- toast.success('Default wallet updated')
97
- await onWalletCreated()
98
- } catch (err: unknown) {
99
- const msg = errorMessage(err)
100
- setError(msg)
101
- toast.error(msg)
102
- } finally {
103
- setActivatingWalletId(null)
104
- }
105
- }, [onWalletCreated])
106
-
107
- return (
108
- <div className="mb-8">
109
- <div className="flex items-center gap-2 mb-3">
110
- <label className="block font-display text-[12px] font-600 text-text-2 uppercase tracking-[0.08em]">
111
- Wallets
112
- </label>
113
- <span className="px-1.5 py-0.5 rounded-[4px] bg-amber-500/15 text-amber-400 text-[9px] font-600 uppercase tracking-wide">
114
- Experimental
115
- </span>
116
- </div>
117
-
118
- {sortedWallets.length > 0 ? (
119
- <div className="space-y-3">
120
- <div className="p-4 rounded-[12px] border border-white/[0.06] bg-surface-2/50">
121
- <div className="flex items-center justify-between gap-3 mb-2">
122
- <div>
123
- <div className="text-[12px] font-600 text-text-1">Combined Wallet Summary</div>
124
- <p className="text-[11px] text-text-3/70 mt-1">
125
- {sortedWallets.length} wallet{sortedWallets.length === 1 ? '' : 's'} connected. The active wallet is used by default when the agent does not specify a chain explicitly.
126
- </p>
127
- </div>
128
- <div className="text-right">
129
- <div className="text-[18px] font-600 text-text-1">{sortedWallets.length}</div>
130
- <div className="text-[10px] uppercase tracking-wide text-text-3/50">Connected</div>
131
- </div>
132
- </div>
133
- <div className="grid gap-2 md:grid-cols-2">
134
- {sortedWallets.map((wallet) => {
135
- const walletMeta = getWalletChainMeta(wallet.chain)
136
- const balanceFormatted = wallet.balanceFormatted || formatWalletAmount(wallet.chain, getWalletBalanceAtomic(wallet), { minFractionDigits: 4, maxFractionDigits: 6 })
137
- const isActive = wallet.id === activeWalletId || wallet.isActive === true
138
- return (
139
- <div key={wallet.id} className="rounded-[10px] border border-white/[0.06] bg-black/10 px-3 py-2">
140
- <div className="flex items-center gap-2">
141
- <span className="text-[10px] text-text-3/60 uppercase tracking-wide font-600">{walletMeta.label}</span>
142
- {isActive && (
143
- <span className="px-1.5 py-0.5 rounded-[999px] bg-accent-soft text-accent-bright text-[9px] font-600 uppercase tracking-wide">
144
- Active
145
- </span>
146
- )}
147
- </div>
148
- <div className="mt-1 text-[14px] font-600 text-text-1">{balanceFormatted} {walletMeta.symbol}</div>
149
- {wallet.portfolioSummary?.nonZeroAssets ? (
150
- <div className="mt-1 text-[10px] text-text-3/55">
151
- {wallet.portfolioSummary.nonZeroAssets} asset{wallet.portfolioSummary.nonZeroAssets === 1 ? '' : 's'} tracked
152
- </div>
153
- ) : null}
154
- <div className="mt-1 text-[10px] text-text-3/55 font-mono truncate">{wallet.publicKey}</div>
155
- </div>
156
- )
157
- })}
158
- </div>
159
- </div>
160
-
161
- {sortedWallets.map((wallet) => {
162
- const walletMeta = getWalletChainMeta(wallet.chain)
163
- const balanceFormatted = wallet.balanceFormatted || formatWalletAmount(wallet.chain, getWalletBalanceAtomic(wallet), { minFractionDigits: 4, maxFractionDigits: 6 })
164
- const perTxLimit = formatWalletAmount(wallet.chain, getWalletLimitAtomic(wallet, 'perTx'), { maxFractionDigits: 6 })
165
- const dailyLimit = formatWalletAmount(wallet.chain, getWalletLimitAtomic(wallet, 'daily'), { maxFractionDigits: 6 })
166
- const isActive = wallet.id === activeWalletId || wallet.isActive === true
167
- return (
168
- <div key={wallet.id} className="p-4 rounded-[12px] border border-white/[0.06] bg-surface-2/50 space-y-3">
169
- <div className="flex items-center gap-2">
170
- <span className="text-[10px] text-text-3/60 uppercase tracking-wide font-600">
171
- {walletMeta.label}
172
- </span>
173
- {isActive && (
174
- <span className="px-1.5 py-0.5 rounded-[999px] bg-accent-soft text-accent-bright text-[9px] font-600 uppercase tracking-wide">
175
- Default
176
- </span>
177
- )}
178
- <span className="flex-1" />
179
- <span className="text-[13px] font-600 text-text-1">
180
- {balanceFormatted} {walletMeta.symbol}
181
- </span>
182
- </div>
183
- <div className="flex items-center gap-2">
184
- <code className="text-[11px] text-text-3 bg-black/20 px-2 py-1 rounded-[6px] font-mono truncate flex-1">
185
- {wallet.publicKey}
186
- </code>
187
- <button
188
- type="button"
189
- onClick={() => copyAddress(wallet)}
190
- className="shrink-0 px-2 py-1 rounded-[6px] text-[10px] text-text-3 hover:text-text-2 border border-white/[0.08] bg-surface transition-colors cursor-pointer"
191
- style={{ fontFamily: 'inherit' }}
192
- >
193
- {copiedWalletId === wallet.id ? 'Copied!' : 'Copy'}
194
- </button>
195
- {!isActive && (
196
- <button
197
- type="button"
198
- onClick={() => setActiveWallet(wallet.id)}
199
- disabled={activatingWalletId === wallet.id}
200
- className="shrink-0 px-2 py-1 rounded-[6px] text-[10px] font-600 text-accent-bright border border-accent-bright/20 bg-accent-soft/10 hover:bg-accent-soft/20 transition-colors cursor-pointer disabled:opacity-50"
201
- style={{ fontFamily: 'inherit' }}
202
- >
203
- {activatingWalletId === wallet.id ? 'Setting...' : 'Set Default'}
204
- </button>
205
- )}
206
- </div>
207
- <div className="flex flex-wrap items-center gap-3 text-[10px] text-text-3/60">
208
- <span>Limit: {perTxLimit} {walletMeta.symbol}/tx</span>
209
- <span>Daily: {dailyLimit} {walletMeta.symbol}</span>
210
- <span>{!walletApprovalsEnabled ? 'Approvals off globally' : (wallet.requireApproval ? 'Approval required' : 'Auto-send')}</span>
211
- {wallet.portfolioSummary?.nonZeroAssets ? (
212
- <span>{wallet.portfolioSummary.nonZeroAssets} asset{wallet.portfolioSummary.nonZeroAssets === 1 ? '' : 's'} detected</span>
213
- ) : null}
214
- </div>
215
- </div>
216
- )
217
- })}
218
- </div>
219
- ) : null}
220
-
221
- {availableChains.length > 0 ? (
222
- <div className="mt-3 p-4 rounded-[12px] border border-white/[0.06] bg-surface-2/50">
223
- <p className="text-[12px] text-text-3/70 mb-3">
224
- {getWalletChainMeta(chain).createDescription}
225
- </p>
226
- <label className="block text-[11px] text-text-3/70 mb-1">Wallet Type</label>
227
- <select
228
- value={chain}
229
- onChange={(event) => setChain(event.target.value as WalletChain)}
230
- className="w-full mb-3 px-3 py-2 rounded-[8px] border border-white/[0.08] bg-surface text-[12px] text-text-1 outline-none focus:border-accent/40"
231
- style={{ fontFamily: 'inherit' }}
232
- >
233
- {availableChains.map((availableChain) => (
234
- <option key={availableChain} value={availableChain}>
235
- {getWalletChainMeta(availableChain).label}
236
- </option>
237
- ))}
238
- </select>
239
- <button
240
- type="button"
241
- onClick={createWallet}
242
- disabled={creating}
243
- className="px-3 py-1.5 rounded-[8px] bg-accent-soft text-accent-bright text-[11px] font-600 hover:bg-accent-bright/15 transition-all cursor-pointer disabled:opacity-50 border border-accent-bright/20"
244
- style={{ fontFamily: 'inherit' }}
245
- >
246
- {creating ? 'Creating...' : `Create ${getWalletChainMeta(chain).label} Wallet`}
247
- </button>
248
- {error && <p className="text-[11px] text-red-400 mt-2">{error}</p>}
249
- </div>
250
- ) : (
251
- <div className="mt-3 p-4 rounded-[12px] border border-white/[0.06] bg-surface-2/50">
252
- <p className="text-[12px] text-text-3/70">
253
- This agent already has both supported wallet types connected. Use the default toggle above to choose which wallet autonomous actions use when no chain is specified.
254
- </p>
255
- {error && <p className="text-[11px] text-red-400 mt-2">{error}</p>}
256
- </div>
257
- )}
258
- </div>
259
- )
260
- }
@@ -1,23 +0,0 @@
1
- import { useQuery } from '@tanstack/react-query'
2
- import { api } from '@/lib/app/api-client'
3
- import type { Mission } from '@/types'
4
-
5
- type MissionListOptions = {
6
- enabled?: boolean
7
- limit?: number
8
- }
9
-
10
- export const missionQueryKeys = {
11
- all: ['missions'] as const,
12
- list: (limit: number) => ['missions', 'list', { limit }] as const,
13
- }
14
-
15
- export function useMissionsQuery(options: MissionListOptions = {}) {
16
- const limit = options.limit ?? 80
17
- return useQuery<Mission[]>({
18
- queryKey: missionQueryKeys.list(limit),
19
- queryFn: () => api<Mission[]>('GET', `/missions?limit=${limit}`),
20
- enabled: options.enabled,
21
- staleTime: 30_000,
22
- })
23
- }
@@ -1,360 +0,0 @@
1
- import { describe, it } from 'node:test'
2
- import assert from 'node:assert/strict'
3
- import {
4
- normalizeCanvasDocument,
5
- normalizeCanvasContent,
6
- isCanvasDocument,
7
- summarizeCanvasContent,
8
- } from './canvas-content'
9
-
10
- describe('normalizeCanvasContent', () => {
11
- it('returns null for null/undefined', () => {
12
- assert.equal(normalizeCanvasContent(null), null)
13
- assert.equal(normalizeCanvasContent(undefined), null)
14
- })
15
-
16
- it('returns string content as-is', () => {
17
- assert.equal(normalizeCanvasContent('<p>hello</p>'), '<p>hello</p>')
18
- })
19
-
20
- it('returns null for empty string', () => {
21
- assert.equal(normalizeCanvasContent(''), null)
22
- })
23
-
24
- it('normalizes a valid structured document', () => {
25
- const doc = normalizeCanvasContent({
26
- title: 'Test',
27
- blocks: [{ type: 'markdown', markdown: '# Hello' }],
28
- })
29
- assert.ok(doc !== null && typeof doc === 'object')
30
- assert.equal((doc as { kind: string }).kind, 'structured')
31
- })
32
-
33
- it('returns null for object without valid blocks', () => {
34
- assert.equal(normalizeCanvasContent({ blocks: [] }), null)
35
- assert.equal(normalizeCanvasContent({}), null)
36
- assert.equal(normalizeCanvasContent({ blocks: [{ type: 'unknown' }] }), null)
37
- })
38
- })
39
-
40
- describe('normalizeCanvasDocument', () => {
41
- it('returns null for non-object input', () => {
42
- assert.equal(normalizeCanvasDocument('string'), null)
43
- assert.equal(normalizeCanvasDocument(42), null)
44
- assert.equal(normalizeCanvasDocument(null), null)
45
- assert.equal(normalizeCanvasDocument([]), null)
46
- assert.equal(normalizeCanvasDocument(true), null)
47
- })
48
-
49
- it('normalizes a markdown block', () => {
50
- const doc = normalizeCanvasDocument({
51
- blocks: [{ type: 'markdown', markdown: '# Hi' }],
52
- })
53
- assert.ok(doc)
54
- assert.equal(doc.blocks.length, 1)
55
- assert.equal(doc.blocks[0].type, 'markdown')
56
- assert.equal(doc.kind, 'structured')
57
- assert.equal(doc.theme, 'slate') // default theme
58
- })
59
-
60
- it('applies theme when valid', () => {
61
- const doc = normalizeCanvasDocument({
62
- blocks: [{ type: 'markdown', markdown: 'x' }],
63
- theme: 'emerald',
64
- })
65
- assert.ok(doc)
66
- assert.equal(doc.theme, 'emerald')
67
- })
68
-
69
- it('defaults to slate for invalid theme', () => {
70
- const doc = normalizeCanvasDocument({
71
- blocks: [{ type: 'markdown', markdown: 'x' }],
72
- theme: 'neon',
73
- })
74
- assert.ok(doc)
75
- assert.equal(doc.theme, 'slate')
76
- })
77
-
78
- it('truncates title and subtitle', () => {
79
- const doc = normalizeCanvasDocument({
80
- title: 'A'.repeat(300),
81
- subtitle: 'B'.repeat(500),
82
- blocks: [{ type: 'markdown', markdown: 'x' }],
83
- })
84
- assert.ok(doc)
85
- assert.equal(doc.title!.length, 180)
86
- assert.equal(doc.subtitle!.length, 320)
87
- })
88
-
89
- it('skips invalid blocks and keeps valid ones', () => {
90
- const doc = normalizeCanvasDocument({
91
- blocks: [
92
- { type: 'markdown', markdown: 'valid' },
93
- { type: 'unknown_type' },
94
- null,
95
- 'just a string',
96
- { type: 'code', code: 'console.log(1)' },
97
- ],
98
- })
99
- assert.ok(doc)
100
- assert.equal(doc.blocks.length, 2)
101
- assert.equal(doc.blocks[0].type, 'markdown')
102
- assert.equal(doc.blocks[1].type, 'code')
103
- })
104
-
105
- it('enforces max 24 blocks', () => {
106
- const blocks = Array.from({ length: 30 }, () => ({
107
- type: 'markdown',
108
- markdown: 'x',
109
- }))
110
- const doc = normalizeCanvasDocument({ blocks })
111
- assert.ok(doc)
112
- assert.equal(doc.blocks.length, 24)
113
- })
114
-
115
- it('normalizes metrics block', () => {
116
- const doc = normalizeCanvasDocument({
117
- blocks: [{
118
- type: 'metrics',
119
- items: [
120
- { label: 'CPU', value: '90%', tone: 'warning' },
121
- { label: 'Mem', value: '4GB', detail: 'of 16GB', tone: 'positive' },
122
- { label: 'missing value' }, // should be skipped
123
- { value: 'missing label' }, // should be skipped
124
- ],
125
- }],
126
- })
127
- assert.ok(doc)
128
- assert.equal(doc.blocks.length, 1)
129
- const block = doc.blocks[0] as { type: string; items: Array<{ label: string; value: string; tone: string }> }
130
- assert.equal(block.items.length, 2)
131
- assert.equal(block.items[0].tone, 'warning')
132
- assert.equal(block.items[1].tone, 'positive')
133
- })
134
-
135
- it('enforces max 24 metric items', () => {
136
- const items = Array.from({ length: 30 }, (_, i) => ({
137
- label: `L${i}`, value: `V${i}`,
138
- }))
139
- const doc = normalizeCanvasDocument({
140
- blocks: [{ type: 'metrics', items }],
141
- })
142
- assert.ok(doc)
143
- const block = doc.blocks[0] as { items: unknown[] }
144
- assert.equal(block.items.length, 24)
145
- })
146
-
147
- it('normalizes cards block', () => {
148
- const doc = normalizeCanvasDocument({
149
- blocks: [{
150
- type: 'cards',
151
- items: [
152
- { title: 'Card 1', body: 'content', tone: 'negative' },
153
- { body: 'no title' }, // skipped
154
- ],
155
- }],
156
- })
157
- assert.ok(doc)
158
- const block = doc.blocks[0] as { items: Array<{ title: string; tone: string }> }
159
- assert.equal(block.items.length, 1)
160
- assert.equal(block.items[0].tone, 'negative')
161
- })
162
-
163
- it('normalizes table block', () => {
164
- const doc = normalizeCanvasDocument({
165
- blocks: [{
166
- type: 'table',
167
- table: {
168
- columns: ['Name', 'Age'],
169
- rows: [['Alice', 30], ['Bob', true]],
170
- caption: 'Users',
171
- },
172
- }],
173
- })
174
- assert.ok(doc)
175
- assert.equal(doc.blocks[0].type, 'table')
176
- })
177
-
178
- it('rejects table with no columns', () => {
179
- const doc = normalizeCanvasDocument({
180
- blocks: [{
181
- type: 'table',
182
- table: { columns: [], rows: [] },
183
- }],
184
- })
185
- assert.equal(doc, null) // no valid blocks
186
- })
187
-
188
- it('rejects table with no rows', () => {
189
- const doc = normalizeCanvasDocument({
190
- blocks: [{
191
- type: 'table',
192
- table: { columns: ['A'], rows: [] },
193
- }],
194
- })
195
- assert.equal(doc, null)
196
- })
197
-
198
- it('limits table to 20 columns and 100 rows', () => {
199
- const columns = Array.from({ length: 25 }, (_, i) => `Col${i}`)
200
- const rows = Array.from({ length: 110 }, () => columns.map((_, i) => `val${i}`))
201
- const doc = normalizeCanvasDocument({
202
- blocks: [{
203
- type: 'table',
204
- table: { columns, rows },
205
- }],
206
- })
207
- assert.ok(doc)
208
- const block = doc.blocks[0] as { table: { columns: string[]; rows: unknown[][] } }
209
- assert.equal(block.table.columns.length, 20)
210
- assert.equal(block.table.rows.length, 100)
211
- })
212
-
213
- it('normalizes actions block', () => {
214
- const doc = normalizeCanvasDocument({
215
- blocks: [{
216
- type: 'actions',
217
- items: [
218
- { label: 'Submit', intent: 'primary', href: 'https://x.com' },
219
- { label: 'Delete', intent: 'danger' },
220
- { intent: 'primary' }, // no label → skipped
221
- ],
222
- }],
223
- })
224
- assert.ok(doc)
225
- const block = doc.blocks[0] as { items: Array<{ label: string; intent: string }> }
226
- assert.equal(block.items.length, 2)
227
- assert.equal(block.items[0].intent, 'primary')
228
- assert.equal(block.items[1].intent, 'danger')
229
- })
230
-
231
- it('defaults action intent to secondary', () => {
232
- const doc = normalizeCanvasDocument({
233
- blocks: [{
234
- type: 'actions',
235
- items: [{ label: 'Go', intent: 'invalid' }],
236
- }],
237
- })
238
- assert.ok(doc)
239
- const block = doc.blocks[0] as { items: Array<{ intent: string }> }
240
- assert.equal(block.items[0].intent, 'secondary')
241
- })
242
-
243
- it('normalizes code block with language', () => {
244
- const doc = normalizeCanvasDocument({
245
- blocks: [{
246
- type: 'code',
247
- code: 'const x = 1;',
248
- language: 'typescript',
249
- }],
250
- })
251
- assert.ok(doc)
252
- const block = doc.blocks[0] as { type: string; code: string; language: string }
253
- assert.equal(block.code, 'const x = 1;')
254
- assert.equal(block.language, 'typescript')
255
- })
256
-
257
- it('rejects code block with empty code', () => {
258
- const doc = normalizeCanvasDocument({
259
- blocks: [{ type: 'code', code: '' }],
260
- })
261
- assert.equal(doc, null)
262
- })
263
-
264
- it('coerces number/boolean to string in asTrimmedString contexts', () => {
265
- const doc = normalizeCanvasDocument({
266
- blocks: [{
267
- type: 'metrics',
268
- items: [{ label: 42, value: true }],
269
- }],
270
- })
271
- assert.ok(doc)
272
- const block = doc.blocks[0] as { items: Array<{ label: string; value: string }> }
273
- assert.equal(block.items[0].label, '42')
274
- assert.equal(block.items[0].value, 'true')
275
- })
276
-
277
- it('truncates markdown content to 20000 chars', () => {
278
- const longMarkdown = 'x'.repeat(25000)
279
- const doc = normalizeCanvasDocument({
280
- blocks: [{ type: 'markdown', markdown: longMarkdown }],
281
- })
282
- assert.ok(doc)
283
- const block = doc.blocks[0] as { markdown: string }
284
- assert.equal(block.markdown.length, 20000)
285
- })
286
-
287
- it('uses provided updatedAt when valid', () => {
288
- const doc = normalizeCanvasDocument({
289
- blocks: [{ type: 'markdown', markdown: 'x' }],
290
- updatedAt: 1234567890,
291
- })
292
- assert.ok(doc)
293
- assert.equal(doc.updatedAt, 1234567890)
294
- })
295
-
296
- it('falls back to Date.now() for invalid updatedAt', () => {
297
- const before = Date.now()
298
- const doc = normalizeCanvasDocument({
299
- blocks: [{ type: 'markdown', markdown: 'x' }],
300
- updatedAt: 'not a number',
301
- })
302
- const after = Date.now()
303
- assert.ok(doc)
304
- assert.ok(doc.updatedAt! >= before && doc.updatedAt! <= after)
305
- })
306
- })
307
-
308
- describe('isCanvasDocument', () => {
309
- it('returns true for valid document object', () => {
310
- assert.equal(isCanvasDocument({
311
- blocks: [{ type: 'markdown', markdown: 'hi' }],
312
- }), true)
313
- })
314
-
315
- it('returns false for non-documents', () => {
316
- assert.equal(isCanvasDocument('string'), false)
317
- assert.equal(isCanvasDocument(null), false)
318
- assert.equal(isCanvasDocument({}), false)
319
- assert.equal(isCanvasDocument({ blocks: [] }), false)
320
- })
321
- })
322
-
323
- describe('summarizeCanvasContent', () => {
324
- it('summarizes null content', () => {
325
- const summary = summarizeCanvasContent(null)
326
- assert.equal(summary.kind, 'empty')
327
- assert.equal(summary.hasContent, false)
328
- assert.equal(summary.contentLength, 0)
329
- })
330
-
331
- it('summarizes string content', () => {
332
- const summary = summarizeCanvasContent('<p>hello</p>')
333
- assert.equal(summary.kind, 'html')
334
- assert.equal(summary.hasContent, true)
335
- assert.equal(summary.contentLength, 12)
336
- assert.equal(summary.preview, '<p>hello</p>')
337
- })
338
-
339
- it('truncates preview to 500 chars for long strings', () => {
340
- const long = 'x'.repeat(600)
341
- const summary = summarizeCanvasContent(long)
342
- assert.equal((summary.preview as string).length, 500)
343
- })
344
-
345
- it('summarizes structured document', () => {
346
- const doc = normalizeCanvasDocument({
347
- title: 'My Doc',
348
- blocks: [
349
- { type: 'markdown', markdown: '# Heading' },
350
- { type: 'code', code: 'x = 1' },
351
- ],
352
- })!
353
- const summary = summarizeCanvasContent(doc)
354
- assert.equal(summary.kind, 'structured')
355
- assert.equal(summary.hasContent, true)
356
- assert.equal(summary.blockCount, 2)
357
- assert.equal(summary.title, 'My Doc')
358
- assert.deepEqual(summary.blockTypes, ['markdown', 'code'])
359
- })
360
- })