@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,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
- })