@swarmclawai/swarmclaw 1.2.8 → 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 (195) hide show
  1. package/README.md +30 -6
  2. package/package.json +2 -2
  3. package/src/app/agents/[id]/page.tsx +1 -18
  4. package/src/app/api/agents/thread-route.test.ts +0 -1
  5. package/src/app/api/approvals/route.test.ts +6 -22
  6. package/src/app/api/connectors/route.ts +2 -2
  7. package/src/app/api/portability/export/route.ts +8 -0
  8. package/src/app/api/portability/import/route.test.ts +80 -0
  9. package/src/app/api/portability/import/route.ts +28 -0
  10. package/src/app/api/settings/route.ts +0 -2
  11. package/src/app/api/wallets/[id]/route.ts +15 -157
  12. package/src/app/api/wallets/generate/route.ts +22 -0
  13. package/src/app/api/wallets/route.test.ts +147 -0
  14. package/src/app/api/wallets/route.ts +13 -95
  15. package/src/app/autonomy/page.tsx +2 -57
  16. package/src/app/protocols/page.tsx +2 -21
  17. package/src/app/settings/page.tsx +0 -9
  18. package/src/app/wallets/page.tsx +105 -5
  19. package/src/cli/index.js +21 -33
  20. package/src/cli/spec.js +19 -30
  21. package/src/components/agents/agent-sheet.tsx +2 -40
  22. package/src/components/agents/inspector-panel.tsx +0 -83
  23. package/src/components/chat/chat-card.tsx +0 -31
  24. package/src/components/chat/message-bubble.tsx +1 -108
  25. package/src/components/connectors/connector-sheet.tsx +25 -1
  26. package/src/components/layout/sidebar-rail.tsx +6 -10
  27. package/src/components/projects/project-detail.tsx +3 -35
  28. package/src/components/projects/tabs/overview-tab.tsx +3 -59
  29. package/src/components/projects/tabs/work-tab.tsx +7 -77
  30. package/src/components/protocols/structured-session-launcher.tsx +1 -22
  31. package/src/components/shared/connector-platform-icon.tsx +1 -0
  32. package/src/components/tasks/task-card.tsx +4 -34
  33. package/src/components/tasks/task-sheet.tsx +6 -36
  34. package/src/components/wallets/wallet-list.tsx +150 -0
  35. package/src/lib/app/navigation.test.ts +0 -13
  36. package/src/lib/app/navigation.ts +2 -7
  37. package/src/lib/app/view-constants.ts +14 -19
  38. package/src/lib/server/agents/agent-thread-session.ts +0 -1
  39. package/src/lib/server/agents/delegation-advisory.test.ts +0 -1
  40. package/src/lib/server/agents/delegation-jobs.test.ts +0 -69
  41. package/src/lib/server/agents/delegation-jobs.ts +0 -25
  42. package/src/lib/server/agents/main-agent-loop.ts +1 -49
  43. package/src/lib/server/agents/subagent-runtime.ts +0 -1
  44. package/src/lib/server/approval-match.ts +0 -85
  45. package/src/lib/server/approvals.test.ts +6 -6
  46. package/src/lib/server/approvals.ts +0 -6
  47. package/src/lib/server/autonomy/supervisor-reflection.test.ts +0 -1
  48. package/src/lib/server/builtin-extensions.ts +0 -2
  49. package/src/lib/server/capability-router.test.ts +0 -2
  50. package/src/lib/server/chat-execution/chat-execution-tool-events.test.ts +14 -14
  51. package/src/lib/server/chat-execution/chat-execution-types.ts +0 -2
  52. package/src/lib/server/chat-execution/chat-execution-utils.ts +0 -2
  53. package/src/lib/server/chat-execution/chat-streaming-utils.ts +2 -30
  54. package/src/lib/server/chat-execution/chat-turn-finalization.ts +1 -36
  55. package/src/lib/server/chat-execution/chat-turn-preparation.ts +2 -22
  56. package/src/lib/server/chat-execution/iteration-event-handler.ts +0 -24
  57. package/src/lib/server/chat-execution/message-classifier.test.ts +0 -45
  58. package/src/lib/server/chat-execution/message-classifier.ts +1 -16
  59. package/src/lib/server/chat-execution/prompt-builder.test.ts +0 -1
  60. package/src/lib/server/chat-execution/prompt-builder.ts +0 -30
  61. package/src/lib/server/chat-execution/prompt-sections.ts +0 -1
  62. package/src/lib/server/chat-execution/situational-awareness.test.ts +2 -73
  63. package/src/lib/server/chat-execution/situational-awareness.ts +4 -38
  64. package/src/lib/server/chat-execution/stream-agent-chat.test.ts +8 -123
  65. package/src/lib/server/chat-execution/stream-agent-chat.ts +1 -5
  66. package/src/lib/server/chat-execution/stream-continuation.test.ts +4 -52
  67. package/src/lib/server/chat-execution/stream-continuation.ts +6 -48
  68. package/src/lib/server/chatrooms/session-mailbox.ts +0 -10
  69. package/src/lib/server/chats/chat-session-service.ts +3 -5
  70. package/src/lib/server/connectors/connector-inbound.ts +0 -1
  71. package/src/lib/server/connectors/connector-lifecycle.ts +19 -3
  72. package/src/lib/server/connectors/connector-service.ts +39 -9
  73. package/src/lib/server/connectors/swarmdock-bidding.ts +74 -0
  74. package/src/lib/server/connectors/swarmdock-payloads.test.ts +85 -0
  75. package/src/lib/server/connectors/swarmdock-secret.test.ts +128 -0
  76. package/src/lib/server/connectors/swarmdock-secret.ts +152 -0
  77. package/src/lib/server/connectors/swarmdock-tasks.ts +119 -0
  78. package/src/lib/server/connectors/swarmdock.ts +255 -0
  79. package/src/lib/server/execution-brief.test.ts +2 -25
  80. package/src/lib/server/execution-brief.ts +12 -35
  81. package/src/lib/server/execution-engine/task-attempt.ts +0 -1
  82. package/src/lib/server/persistence/storage-context.ts +0 -5
  83. package/src/lib/server/portability/export.ts +109 -0
  84. package/src/lib/server/portability/import.ts +159 -0
  85. package/src/lib/server/protocols/protocol-normalization.ts +0 -4
  86. package/src/lib/server/protocols/protocol-queries.ts +0 -6
  87. package/src/lib/server/protocols/protocol-run-lifecycle.ts +4 -32
  88. package/src/lib/server/protocols/protocol-service.ts +0 -1
  89. package/src/lib/server/protocols/protocol-step-helpers.ts +0 -4
  90. package/src/lib/server/protocols/protocol-step-processors.ts +0 -6
  91. package/src/lib/server/protocols/protocol-swarm.ts +0 -2
  92. package/src/lib/server/protocols/protocol-types.ts +0 -2
  93. package/src/lib/server/provider-health.ts +0 -9
  94. package/src/lib/server/runtime/daemon-state/core.ts +0 -9
  95. package/src/lib/server/runtime/daemon-state.test.ts +0 -35
  96. package/src/lib/server/runtime/heartbeat-service.ts +3 -23
  97. package/src/lib/server/runtime/queue/core.ts +11 -33
  98. package/src/lib/server/runtime/runtime-storage-write-paths.test.ts +6 -6
  99. package/src/lib/server/runtime/scheduler.ts +0 -13
  100. package/src/lib/server/runtime/session-run-manager/drain.ts +0 -24
  101. package/src/lib/server/runtime/session-run-manager/enqueue.ts +0 -1
  102. package/src/lib/server/runtime/session-run-manager/queries.ts +0 -1
  103. package/src/lib/server/runtime/session-run-manager/recovery.ts +0 -1
  104. package/src/lib/server/runtime/session-run-manager.test.ts +0 -28
  105. package/src/lib/server/session-tools/crud.ts +0 -14
  106. package/src/lib/server/session-tools/delegate.ts +0 -4
  107. package/src/lib/server/session-tools/index.ts +0 -4
  108. package/src/lib/server/session-tools/team-context.ts +0 -3
  109. package/src/lib/server/storage-normalization.ts +8 -0
  110. package/src/lib/server/storage.ts +18 -45
  111. package/src/lib/server/tasks/task-checkout.ts +59 -0
  112. package/src/lib/server/tasks/task-lifecycle.ts +2 -0
  113. package/src/lib/server/tasks/task-route-service.ts +4 -26
  114. package/src/lib/server/tasks/task-service.ts +0 -7
  115. package/src/lib/server/tool-aliases.ts +0 -1
  116. package/src/lib/server/tool-capability-policy-advanced.test.ts +4 -4
  117. package/src/lib/server/tool-capability-policy.ts +0 -2
  118. package/src/lib/server/tool-planning.ts +0 -12
  119. package/src/lib/server/universal-tool-access.ts +0 -1
  120. package/src/lib/server/wallets/wallet-crypto.ts +33 -0
  121. package/src/lib/server/wallets/wallet-repository.ts +24 -0
  122. package/src/lib/server/wallets/wallet-service.ts +119 -0
  123. package/src/lib/server/working-state/extraction.ts +8 -42
  124. package/src/lib/server/working-state/normalization.ts +10 -103
  125. package/src/lib/server/working-state/service.ts +12 -21
  126. package/src/lib/strip-internal-metadata.test.ts +1 -1
  127. package/src/lib/strip-internal-metadata.ts +1 -1
  128. package/src/lib/tool-definitions.ts +0 -1
  129. package/src/lib/validation/schemas.ts +33 -2
  130. package/src/stores/slices/data-slice.ts +5 -1
  131. package/src/stores/slices/ui-slice.ts +0 -4
  132. package/src/types/agent.ts +0 -84
  133. package/src/types/app-settings.ts +0 -2
  134. package/src/types/approval.ts +0 -2
  135. package/src/types/connector.ts +1 -0
  136. package/src/types/index.ts +1 -1
  137. package/src/types/message.ts +0 -1
  138. package/src/types/misc.ts +0 -2
  139. package/src/types/protocol.ts +0 -2
  140. package/src/types/run.ts +0 -3
  141. package/src/types/session.ts +1 -51
  142. package/src/types/swarmdock.ts +29 -0
  143. package/src/types/task.ts +7 -3
  144. package/src/types/working-state.ts +2 -9
  145. package/src/views/settings/section-runtime-loop.tsx +0 -14
  146. package/src/app/api/canvas/[sessionId]/route.ts +0 -35
  147. package/src/app/api/missions/[id]/actions/route.ts +0 -31
  148. package/src/app/api/missions/[id]/events/route.ts +0 -14
  149. package/src/app/api/missions/[id]/route.ts +0 -10
  150. package/src/app/api/missions/route.test.ts +0 -244
  151. package/src/app/api/missions/route.ts +0 -57
  152. package/src/app/api/wallets/[id]/approve/route.ts +0 -79
  153. package/src/app/api/wallets/[id]/balance-history/route.ts +0 -18
  154. package/src/app/api/wallets/[id]/send/route.ts +0 -113
  155. package/src/app/api/wallets/[id]/transactions/route.ts +0 -18
  156. package/src/app/missions/[id]/page.tsx +0 -3
  157. package/src/app/missions/page.tsx +0 -685
  158. package/src/components/canvas/canvas-panel.tsx +0 -267
  159. package/src/components/wallets/wallet-approval-dialog.tsx +0 -107
  160. package/src/components/wallets/wallet-panel.tsx +0 -1010
  161. package/src/components/wallets/wallet-section.tsx +0 -260
  162. package/src/features/missions/queries.ts +0 -23
  163. package/src/lib/canvas-content.test.ts +0 -360
  164. package/src/lib/canvas-content.ts +0 -198
  165. package/src/lib/server/canvas-content.test.ts +0 -32
  166. package/src/lib/server/canvas-content.ts +0 -6
  167. package/src/lib/server/ethereum.ts +0 -591
  168. package/src/lib/server/evm-swap.ts +0 -476
  169. package/src/lib/server/missions/mission-intent.test.ts +0 -63
  170. package/src/lib/server/missions/mission-intent.ts +0 -569
  171. package/src/lib/server/missions/mission-repository.ts +0 -74
  172. package/src/lib/server/missions/mission-service/actions.ts +0 -6
  173. package/src/lib/server/missions/mission-service/bindings.ts +0 -9
  174. package/src/lib/server/missions/mission-service/context.ts +0 -4
  175. package/src/lib/server/missions/mission-service/core.ts +0 -2271
  176. package/src/lib/server/missions/mission-service/queries.ts +0 -12
  177. package/src/lib/server/missions/mission-service/recovery.ts +0 -5
  178. package/src/lib/server/missions/mission-service/ticks.ts +0 -9
  179. package/src/lib/server/missions/mission-service.test.ts +0 -888
  180. package/src/lib/server/missions/mission-service.ts +0 -6
  181. package/src/lib/server/session-tools/canvas.ts +0 -105
  182. package/src/lib/server/session-tools/wallet-tool.test.ts +0 -150
  183. package/src/lib/server/session-tools/wallet.ts +0 -1287
  184. package/src/lib/server/solana.ts +0 -327
  185. package/src/lib/server/wallet/wallet-execution.test.ts +0 -198
  186. package/src/lib/server/wallet/wallet-portfolio.test.ts +0 -98
  187. package/src/lib/server/wallet/wallet-portfolio.ts +0 -772
  188. package/src/lib/server/wallet/wallet-service.test.ts +0 -81
  189. package/src/lib/server/wallet/wallet-service.ts +0 -225
  190. package/src/lib/wallet/wallet-transactions.test.ts +0 -75
  191. package/src/lib/wallet/wallet-transactions.ts +0 -43
  192. package/src/lib/wallet/wallet.test.ts +0 -333
  193. package/src/lib/wallet/wallet.ts +0 -183
  194. package/src/types/mission.ts +0 -185
  195. 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
- }