@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,81 +0,0 @@
1
- import assert from 'node:assert/strict'
2
- import { describe, it } from 'node:test'
3
-
4
- import type { AgentWallet, WalletTransaction } from '@/types'
5
-
6
- import {
7
- validateWalletSendLimits,
8
- walletApprovalsGloballyEnabled,
9
- walletRequiresApproval,
10
- } from '@/lib/server/wallet/wallet-service'
11
-
12
- function buildWallet(overrides: Partial<AgentWallet> = {}): AgentWallet {
13
- return {
14
- id: 'wallet-1',
15
- agentId: 'agent-1',
16
- chain: 'ethereum',
17
- publicKey: '0x0000000000000000000000000000000000000001',
18
- encryptedPrivateKey: 'secret',
19
- requireApproval: true,
20
- spendingLimitAtomic: '1000000000000000000',
21
- dailyLimitAtomic: '1500000000000000000',
22
- createdAt: 1,
23
- updatedAt: 1,
24
- ...overrides,
25
- }
26
- }
27
-
28
- function buildTransaction(overrides: Partial<WalletTransaction> = {}): WalletTransaction {
29
- return {
30
- id: 'tx-1',
31
- walletId: 'wallet-1',
32
- agentId: 'agent-1',
33
- chain: 'ethereum',
34
- type: 'send',
35
- signature: '0xhash',
36
- fromAddress: '0xfrom',
37
- toAddress: '0xto',
38
- amountAtomic: '1000000000000000000',
39
- status: 'confirmed',
40
- timestamp: Date.now(),
41
- ...overrides,
42
- }
43
- }
44
-
45
- describe('validateWalletSendLimits', () => {
46
- it('blocks approvals that would exceed the current daily limit', () => {
47
- const wallet = buildWallet()
48
- const transactions = [
49
- buildTransaction({ id: 'existing', amountAtomic: '1000000000000000000' }),
50
- ]
51
-
52
- const error = validateWalletSendLimits({
53
- wallet,
54
- amountAtomic: '600000000000000000',
55
- transactions,
56
- now: Date.now(),
57
- })
58
-
59
- assert.match(error || '', /Daily limit exceeded/)
60
- })
61
- })
62
-
63
- describe('wallet approval helpers', () => {
64
- it('treats missing app setting as globally enabled', () => {
65
- assert.equal(walletApprovalsGloballyEnabled(undefined), true)
66
- assert.equal(walletApprovalsGloballyEnabled({}), true)
67
- })
68
-
69
- it('lets the global setting disable approval even for approval-required wallets', () => {
70
- const wallet = buildWallet({ requireApproval: true })
71
-
72
- assert.equal(walletRequiresApproval(wallet, { walletApprovalsEnabled: true }), true)
73
- assert.equal(walletRequiresApproval(wallet, { walletApprovalsEnabled: false }), false)
74
- })
75
-
76
- it('still respects the per-wallet toggle when global approvals remain enabled', () => {
77
- const wallet = buildWallet({ requireApproval: false })
78
-
79
- assert.equal(walletRequiresApproval(wallet, { walletApprovalsEnabled: true }), false)
80
- })
81
- })
@@ -1,225 +0,0 @@
1
- import { genId } from '@/lib/id'
2
- import {
3
- formatWalletAmount,
4
- getWalletAssetSymbol,
5
- getWalletAtomicAmount,
6
- getWalletChainOrDefault,
7
- getWalletDefaultLimitAtomic,
8
- getWalletLimitAtomic,
9
- normalizeAtomicString,
10
- } from '@/lib/wallet/wallet'
11
- import type { Agent, AgentWallet, WalletChain, WalletTransaction } from '@/types'
12
- import { dedup } from '@/lib/shared-utils'
13
- import { loadAgent, loadAgents, loadWalletTransactions, loadWallets, upsertAgent, upsertWallet } from '@/lib/server/storage'
14
- import { generateEthereumWallet, isValidEthereumAddress, sendEth } from '@/lib/server/ethereum'
15
- import { generateSolanaKeypair, isValidSolanaAddress, sendSol } from '@/lib/server/solana'
16
- import { notify } from '@/lib/server/ws-hub'
17
- import { clearWalletPortfolioCache, getWalletPortfolio, type GetWalletPortfolioOptions, type WalletPortfolio } from '@/lib/server/wallet/wallet-portfolio'
18
-
19
- function generateWalletCredentials(chain: WalletChain): { publicKey: string; encryptedPrivateKey: string } {
20
- if (chain === 'ethereum') return generateEthereumWallet()
21
- return generateSolanaKeypair()
22
- }
23
-
24
- export function stripWalletPrivateKey<T extends Record<string, unknown>>(wallet: T): Omit<T, 'encryptedPrivateKey'> {
25
- return Object.fromEntries(Object.entries(wallet).filter(([key]) => key !== 'encryptedPrivateKey')) as Omit<T, 'encryptedPrivateKey'>
26
- }
27
-
28
- export function getAgentWalletIds(agent: Pick<Agent, 'walletIds' | 'walletId'> | null | undefined): string[] {
29
- const ids = Array.isArray(agent?.walletIds)
30
- ? agent.walletIds.filter((value): value is string => typeof value === 'string' && value.trim().length > 0)
31
- : []
32
- const legacy = typeof agent?.walletId === 'string' && agent.walletId.trim()
33
- ? [agent.walletId.trim()]
34
- : []
35
- return dedup([...ids, ...legacy])
36
- }
37
-
38
- export function getAgentActiveWalletId(
39
- agent: Pick<Agent, 'walletIds' | 'walletId' | 'activeWalletId'> | null | undefined,
40
- walletIds = getAgentWalletIds(agent),
41
- ): string | null {
42
- if (typeof agent?.activeWalletId === 'string' && walletIds.includes(agent.activeWalletId)) return agent.activeWalletId
43
- if (typeof agent?.walletId === 'string' && walletIds.includes(agent.walletId)) return agent.walletId
44
- return walletIds[0] || null
45
- }
46
-
47
- function syncAgentWalletPointers(agent: Agent, walletIds: string[], activeWalletId?: string | null): Agent {
48
- const normalizedIds = dedup(walletIds.filter(Boolean))
49
- const normalizedActive = activeWalletId && normalizedIds.includes(activeWalletId)
50
- ? activeWalletId
51
- : normalizedIds[0] || null
52
- agent.walletIds = normalizedIds
53
- agent.activeWalletId = normalizedActive
54
- agent.walletId = normalizedActive
55
- return agent
56
- }
57
-
58
- export function linkWalletToAgent(agent: Agent, walletId: string, makeActive = false): Agent {
59
- const walletIds = getAgentWalletIds(agent)
60
- if (!walletIds.includes(walletId)) walletIds.push(walletId)
61
- const activeWalletId = makeActive ? walletId : getAgentActiveWalletId(agent, walletIds)
62
- return syncAgentWalletPointers(agent, walletIds, activeWalletId)
63
- }
64
-
65
- export function unlinkWalletFromAgent(agent: Agent, walletId: string): Agent {
66
- const walletIds = getAgentWalletIds(agent).filter((id) => id !== walletId)
67
- const activeWalletId = getAgentActiveWalletId(agent, walletIds)
68
- return syncAgentWalletPointers(agent, walletIds, activeWalletId)
69
- }
70
-
71
- export function setAgentActiveWallet(agent: Agent, walletId: string | null): Agent {
72
- const walletIds = getAgentWalletIds(agent)
73
- const activeWalletId = walletId && walletIds.includes(walletId) ? walletId : walletIds[0] || null
74
- return syncAgentWalletPointers(agent, walletIds, activeWalletId)
75
- }
76
-
77
- export function getWalletsByAgentId(agentId: string): AgentWallet[] {
78
- const wallets = loadWallets() as Record<string, AgentWallet>
79
- return Object.values(wallets)
80
- .filter((wallet) => wallet.agentId === agentId)
81
- .sort((a, b) => a.createdAt - b.createdAt)
82
- }
83
-
84
- export function getWalletByAgentId(agentId: string, chain?: WalletChain | null): AgentWallet | null {
85
- const wallets = getWalletsByAgentId(agentId)
86
- if (chain) return wallets.find((wallet) => wallet.chain === chain) ?? null
87
-
88
- const agents = loadAgents()
89
- const agent = agents[agentId]
90
- const activeWalletId = getAgentActiveWalletId(agent)
91
- return wallets.find((wallet) => wallet.id === activeWalletId) ?? wallets[0] ?? null
92
- }
93
-
94
- export function walletApprovalsGloballyEnabled(
95
- settings?: { walletApprovalsEnabled?: boolean | null } | null,
96
- ): boolean {
97
- return settings?.walletApprovalsEnabled !== false
98
- }
99
-
100
- export function walletRequiresApproval(
101
- wallet: Pick<AgentWallet, 'requireApproval'>,
102
- settings?: { walletApprovalsEnabled?: boolean | null } | null,
103
- ): boolean {
104
- return walletApprovalsGloballyEnabled(settings) && wallet.requireApproval !== false
105
- }
106
-
107
- export function createAgentWallet(input: {
108
- agentId: string
109
- chain?: WalletChain | string | null
110
- provider?: WalletChain | string | null
111
- label?: string
112
- requireApproval?: boolean
113
- spendingLimitAtomic?: string | number | null
114
- dailyLimitAtomic?: string | number | null
115
- }): AgentWallet {
116
- const agentId = String(input.agentId || '').trim()
117
- if (!agentId) throw new Error('agentId is required')
118
-
119
- const agent = loadAgent(agentId)
120
- if (!agent) throw new Error('Agent not found')
121
-
122
- const chain = getWalletChainOrDefault(input.chain ?? input.provider, 'solana')
123
- const existing = getWalletByAgentId(agentId, chain)
124
- if (existing) throw new Error(`Agent already has a ${chain} wallet`)
125
- const { publicKey, encryptedPrivateKey } = generateWalletCredentials(chain)
126
- const id = genId()
127
- const now = Date.now()
128
- const wallet: AgentWallet = {
129
- id,
130
- agentId,
131
- chain,
132
- publicKey,
133
- encryptedPrivateKey,
134
- label: typeof input.label === 'string' && input.label.trim() ? input.label.trim() : undefined,
135
- spendingLimitAtomic: normalizeAtomicString(input.spendingLimitAtomic, getWalletDefaultLimitAtomic(chain, 'perTx')),
136
- dailyLimitAtomic: normalizeAtomicString(input.dailyLimitAtomic, getWalletDefaultLimitAtomic(chain, 'daily')),
137
- requireApproval: input.requireApproval !== false,
138
- createdAt: now,
139
- updatedAt: now,
140
- }
141
-
142
- upsertWallet(id, wallet)
143
- clearWalletPortfolioCache(id)
144
-
145
- linkWalletToAgent(agent as any, id, getAgentActiveWalletId(agent as any) == null)
146
- agent.updatedAt = now
147
- upsertAgent(agentId, agent)
148
-
149
- notify('wallets')
150
- notify('agents')
151
-
152
- return wallet
153
- }
154
-
155
- export async function getWalletBalanceAtomic(wallet: AgentWallet): Promise<string> {
156
- return (await getWalletPortfolio(wallet)).balanceAtomic
157
- }
158
-
159
- export async function getWalletPortfolioSnapshot(
160
- wallet: AgentWallet,
161
- options?: GetWalletPortfolioOptions,
162
- ): Promise<WalletPortfolio> {
163
- return getWalletPortfolio(wallet, options)
164
- }
165
-
166
- export function validateWalletSendLimits(params: {
167
- wallet: AgentWallet
168
- amountAtomic: string
169
- transactions?: WalletTransaction[]
170
- now?: number
171
- excludeTransactionId?: string
172
- }): string | null {
173
- const { wallet } = params
174
- const amountAtomic = normalizeAtomicString(params.amountAtomic, '0')
175
- const assetSymbol = getWalletAssetSymbol(wallet.chain)
176
-
177
- if (BigInt(amountAtomic) <= BigInt(0)) {
178
- return 'Amount must be positive'
179
- }
180
-
181
- const perTxLimitAtomic = getWalletLimitAtomic(wallet, 'perTx')
182
- if (BigInt(amountAtomic) > BigInt(perTxLimitAtomic)) {
183
- return `Amount ${formatWalletAmount(wallet.chain, amountAtomic, { maxFractionDigits: 6 })} ${assetSymbol} exceeds per-transaction limit of ${formatWalletAmount(wallet.chain, perTxLimitAtomic, { maxFractionDigits: 6 })} ${assetSymbol}`
184
- }
185
-
186
- const dailyLimitAtomic = getWalletLimitAtomic(wallet, 'daily')
187
- const oneDayAgo = (params.now ?? Date.now()) - 24 * 60 * 60 * 1000
188
- const transactions = params.transactions
189
- ?? Object.values(loadWalletTransactions() as Record<string, WalletTransaction>)
190
- const dailySpentAtomic = transactions
191
- .filter((tx) => tx.walletId === wallet.id)
192
- .filter((tx) => tx.id !== params.excludeTransactionId)
193
- .filter((tx) => tx.type === 'send' && tx.status === 'confirmed' && tx.timestamp > oneDayAgo)
194
- .reduce((sum, tx) => sum + BigInt(getWalletAtomicAmount(tx)), BigInt(0))
195
-
196
- if (dailySpentAtomic + BigInt(amountAtomic) > BigInt(dailyLimitAtomic)) {
197
- return `Daily limit exceeded. Spent ${formatWalletAmount(wallet.chain, dailySpentAtomic.toString(), { maxFractionDigits: 6 })} ${assetSymbol} in the last 24h, limit is ${formatWalletAmount(wallet.chain, dailyLimitAtomic, { maxFractionDigits: 6 })} ${assetSymbol}`
198
- }
199
-
200
- return null
201
- }
202
-
203
- export function isValidWalletAddress(chain: WalletChain, address: string): boolean {
204
- if (chain === 'ethereum') return isValidEthereumAddress(address)
205
- return isValidSolanaAddress(address)
206
- }
207
-
208
- export async function sendWalletNativeAsset(
209
- wallet: AgentWallet,
210
- toAddress: string,
211
- amountAtomic: string,
212
- ): Promise<{ signature: string; feeAtomic?: string }> {
213
- if (wallet.chain === 'ethereum') {
214
- const result = await sendEth(wallet.encryptedPrivateKey, toAddress, amountAtomic)
215
- clearWalletPortfolioCache(wallet.id)
216
- return { signature: result.signature, feeAtomic: result.fee }
217
- }
218
-
219
- const result = await sendSol(wallet.encryptedPrivateKey, toAddress, Number.parseInt(amountAtomic, 10))
220
- clearWalletPortfolioCache(wallet.id)
221
- return {
222
- signature: result.signature,
223
- feeAtomic: String(result.fee),
224
- }
225
- }
@@ -1,75 +0,0 @@
1
- import assert from 'node:assert/strict'
2
- import { describe, it } from 'node:test'
3
-
4
- import type { WalletTransaction } from '@/types'
5
-
6
- import {
7
- filterWalletTransactions,
8
- getWalletTransactionStatusGroup,
9
- matchesWalletTransactionFilter,
10
- matchesWalletTransactionQuery,
11
- } from '@/lib/wallet/wallet-transactions'
12
-
13
- function buildTransaction(overrides: Partial<WalletTransaction> = {}): WalletTransaction {
14
- return {
15
- id: 'tx-1',
16
- walletId: 'wallet-1',
17
- agentId: 'agent-1',
18
- chain: 'ethereum',
19
- type: 'swap',
20
- signature: '0xabc123',
21
- fromAddress: '0xfrom000000000000000000000000000000000001',
22
- toAddress: '0xto0000000000000000000000000000000000002',
23
- amountAtomic: '1000000',
24
- status: 'confirmed',
25
- memo: 'Swapped 1 USDC to ETH',
26
- timestamp: 1,
27
- ...overrides,
28
- }
29
- }
30
-
31
- describe('wallet transaction filters', () => {
32
- it('groups pending and pending_approval together', () => {
33
- assert.equal(getWalletTransactionStatusGroup('pending'), 'pending')
34
- assert.equal(getWalletTransactionStatusGroup('pending_approval'), 'pending')
35
- assert.equal(getWalletTransactionStatusGroup('confirmed'), 'confirmed')
36
- assert.equal(getWalletTransactionStatusGroup('failed'), 'failed')
37
- assert.equal(getWalletTransactionStatusGroup('denied'), 'failed')
38
- })
39
-
40
- it('matches filters by type and status group', () => {
41
- assert.equal(matchesWalletTransactionFilter(buildTransaction({ type: 'swap' }), 'swap'), true)
42
- assert.equal(matchesWalletTransactionFilter(buildTransaction({ type: 'send' }), 'send'), true)
43
- assert.equal(matchesWalletTransactionFilter(buildTransaction({ status: 'pending_approval' }), 'pending'), true)
44
- assert.equal(matchesWalletTransactionFilter(buildTransaction({ status: 'denied' }), 'failed'), true)
45
- })
46
-
47
- it('matches search queries against signature, memo, and addresses', () => {
48
- const tx = buildTransaction()
49
- assert.equal(matchesWalletTransactionQuery(tx, 'usdc'), true)
50
- assert.equal(matchesWalletTransactionQuery(tx, '0xabc123'), true)
51
- assert.equal(matchesWalletTransactionQuery(tx, '0xfrom0000'), true)
52
- assert.equal(matchesWalletTransactionQuery(tx, 'missing-text'), false)
53
- })
54
-
55
- it('filters transactions by combined status/type and search query', () => {
56
- const transactions = [
57
- buildTransaction({ id: 'tx-confirmed', status: 'confirmed', memo: 'Swap USDC to ETH' }),
58
- buildTransaction({ id: 'tx-pending', status: 'pending_approval', memo: 'Awaiting approval' }),
59
- buildTransaction({ id: 'tx-send', type: 'send', status: 'confirmed', memo: 'Send ETH to treasury' }),
60
- ]
61
-
62
- assert.deepEqual(
63
- filterWalletTransactions(transactions, { filter: 'pending' }).map((tx) => tx.id),
64
- ['tx-pending'],
65
- )
66
- assert.deepEqual(
67
- filterWalletTransactions(transactions, { filter: 'send', query: 'treasury' }).map((tx) => tx.id),
68
- ['tx-send'],
69
- )
70
- assert.deepEqual(
71
- filterWalletTransactions(transactions, { filter: 'confirmed', query: 'usdc' }).map((tx) => tx.id),
72
- ['tx-confirmed'],
73
- )
74
- })
75
- })
@@ -1,43 +0,0 @@
1
- import type { WalletTransaction, WalletTransactionStatus } from '@/types'
2
-
3
- export type WalletTransactionFilter = 'all' | 'confirmed' | 'pending' | 'failed' | 'send' | 'receive' | 'swap'
4
-
5
- export function getWalletTransactionStatusGroup(status: WalletTransactionStatus): 'confirmed' | 'pending' | 'failed' {
6
- if (status === 'confirmed') return 'confirmed'
7
- if (status === 'pending' || status === 'pending_approval') return 'pending'
8
- return 'failed'
9
- }
10
-
11
- export function matchesWalletTransactionFilter(tx: WalletTransaction, filter: WalletTransactionFilter): boolean {
12
- if (filter === 'all') return true
13
- if (filter === 'send' || filter === 'receive' || filter === 'swap') return tx.type === filter
14
- return getWalletTransactionStatusGroup(tx.status) === filter
15
- }
16
-
17
- export function matchesWalletTransactionQuery(tx: WalletTransaction, query: string): boolean {
18
- const normalized = query.trim().toLowerCase()
19
- if (!normalized) return true
20
- const haystack = [
21
- tx.id,
22
- tx.signature,
23
- tx.memo || '',
24
- tx.fromAddress,
25
- tx.toAddress,
26
- tx.status,
27
- tx.type,
28
- tx.tokenMint || '',
29
- tx.approvedBy || '',
30
- ]
31
- .join(' ')
32
- .toLowerCase()
33
- return haystack.includes(normalized)
34
- }
35
-
36
- export function filterWalletTransactions(
37
- transactions: WalletTransaction[],
38
- options?: { filter?: WalletTransactionFilter; query?: string },
39
- ): WalletTransaction[] {
40
- const filter = options?.filter || 'all'
41
- const query = options?.query || ''
42
- return transactions.filter((tx) => matchesWalletTransactionFilter(tx, filter) && matchesWalletTransactionQuery(tx, query))
43
- }