@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
@@ -0,0 +1,147 @@
1
+ import assert from 'node:assert/strict'
2
+ import fs from 'node:fs'
3
+ import os from 'node:os'
4
+ import path from 'node:path'
5
+ import { spawnSync } from 'node:child_process'
6
+ import test from 'node:test'
7
+
8
+ const repoRoot = path.resolve(path.dirname(new URL(import.meta.url).pathname), '../../../..')
9
+
10
+ function runWithTempDataDir(script: string) {
11
+ const tempDir = fs.mkdtempSync(path.join(os.tmpdir(), 'swarmclaw-wallet-routes-'))
12
+ try {
13
+ const result = spawnSync(process.execPath, ['--import', 'tsx', '--input-type=module', '--eval', script], {
14
+ cwd: repoRoot,
15
+ env: {
16
+ ...process.env,
17
+ CREDENTIAL_SECRET: 'test-credential-secret',
18
+ DATA_DIR: path.join(tempDir, 'data'),
19
+ WORKSPACE_DIR: path.join(tempDir, 'workspace'),
20
+ },
21
+ encoding: 'utf-8',
22
+ })
23
+ assert.equal(result.status, 0, result.stderr || result.stdout || 'subprocess failed')
24
+ const lines = (result.stdout || '')
25
+ .trim()
26
+ .split('\n')
27
+ .map((line) => line.trim())
28
+ .filter(Boolean)
29
+ const jsonLine = [...lines].reverse().find((line) => line.startsWith('{'))
30
+ return JSON.parse(jsonLine || '{}')
31
+ } finally {
32
+ fs.rmSync(tempDir, { recursive: true, force: true })
33
+ }
34
+ }
35
+
36
+ test('wallet routes reject unknown agents and invalid addresses while returning safe payloads', () => {
37
+ const output = runWithTempDataDir(`
38
+ const storageMod = await import('./src/lib/server/storage')
39
+ const walletsRouteMod = await import('./src/app/api/wallets/route')
40
+ const walletsGenerateRouteMod = await import('./src/app/api/wallets/generate/route')
41
+ const storage = storageMod.default || storageMod
42
+ const walletsRoute = walletsRouteMod.default || walletsRouteMod
43
+ const walletsGenerateRoute = walletsGenerateRouteMod.default || walletsGenerateRouteMod
44
+
45
+ storage.saveAgents({
46
+ agent_1: {
47
+ id: 'agent_1',
48
+ name: 'Agent One',
49
+ },
50
+ })
51
+
52
+ const missingAgentResponse = await walletsRoute.POST(new Request('http://local/api/wallets', {
53
+ method: 'POST',
54
+ headers: { 'content-type': 'application/json' },
55
+ body: JSON.stringify({
56
+ agentId: 'missing-agent',
57
+ walletAddress: '0x000000000000000000000000000000000000dEaD',
58
+ }),
59
+ }))
60
+ const missingAgentPayload = await missingAgentResponse.json()
61
+
62
+ const blankAddressResponse = await walletsRoute.POST(new Request('http://local/api/wallets', {
63
+ method: 'POST',
64
+ headers: { 'content-type': 'application/json' },
65
+ body: JSON.stringify({
66
+ agentId: 'agent_1',
67
+ walletAddress: ' ',
68
+ }),
69
+ }))
70
+ const blankAddressPayload = await blankAddressResponse.json()
71
+
72
+ const invalidAddressResponse = await walletsRoute.POST(new Request('http://local/api/wallets', {
73
+ method: 'POST',
74
+ headers: { 'content-type': 'application/json' },
75
+ body: JSON.stringify({
76
+ agentId: 'agent_1',
77
+ walletAddress: '0x1234',
78
+ }),
79
+ }))
80
+ const invalidAddressPayload = await invalidAddressResponse.json()
81
+
82
+ const createResponse = await walletsRoute.POST(new Request('http://local/api/wallets', {
83
+ method: 'POST',
84
+ headers: { 'content-type': 'application/json' },
85
+ body: JSON.stringify({
86
+ agentId: 'agent_1',
87
+ walletAddress: '0x000000000000000000000000000000000000dead',
88
+ label: 'Manual Wallet',
89
+ }),
90
+ }))
91
+ const createPayload = await createResponse.json()
92
+
93
+ const generateMissingResponse = await walletsGenerateRoute.POST(new Request('http://local/api/wallets/generate', {
94
+ method: 'POST',
95
+ headers: { 'content-type': 'application/json' },
96
+ body: JSON.stringify({
97
+ agentId: 'missing-agent',
98
+ }),
99
+ }))
100
+ const generateMissingPayload = await generateMissingResponse.json()
101
+
102
+ const generateResponse = await walletsGenerateRoute.POST(new Request('http://local/api/wallets/generate', {
103
+ method: 'POST',
104
+ headers: { 'content-type': 'application/json' },
105
+ body: JSON.stringify({
106
+ agentId: 'agent_1',
107
+ label: 'Generated Wallet',
108
+ }),
109
+ }))
110
+ const generatePayload = await generateResponse.json()
111
+
112
+ const storedWallets = Object.values(storage.loadWallets())
113
+ const generatedStoredWallet = storedWallets.find((wallet) => wallet.label === 'Generated Wallet') || null
114
+
115
+ console.log(JSON.stringify({
116
+ missingAgentStatus: missingAgentResponse.status,
117
+ missingAgentError: missingAgentPayload?.error || null,
118
+ blankAddressStatus: blankAddressResponse.status,
119
+ blankAddressError: blankAddressPayload?.error || null,
120
+ invalidAddressStatus: invalidAddressResponse.status,
121
+ invalidAddressError: invalidAddressPayload?.error || null,
122
+ createStatus: createResponse.status,
123
+ createAddress: createPayload?.walletAddress || null,
124
+ createHasPrivateKey: Boolean(createPayload && Object.prototype.hasOwnProperty.call(createPayload, 'encryptedPrivateKey')),
125
+ generateMissingStatus: generateMissingResponse.status,
126
+ generateMissingError: generateMissingPayload?.error || null,
127
+ generateStatus: generateResponse.status,
128
+ generateHasPrivateKey: Boolean(generatePayload && Object.prototype.hasOwnProperty.call(generatePayload, 'encryptedPrivateKey')),
129
+ storedGeneratedHasPrivateKey: Boolean(generatedStoredWallet?.encryptedPrivateKey),
130
+ }))
131
+ `)
132
+
133
+ assert.equal(output.missingAgentStatus, 404)
134
+ assert.match(String(output.missingAgentError || ''), /Agent not found/i)
135
+ assert.equal(output.blankAddressStatus, 400)
136
+ assert.equal(output.blankAddressError, 'walletAddress is required')
137
+ assert.equal(output.invalidAddressStatus, 400)
138
+ assert.match(String(output.invalidAddressError || ''), /valid Base\/Ethereum address/i)
139
+ assert.equal(output.createStatus, 201)
140
+ assert.equal(output.createAddress, '0x000000000000000000000000000000000000dEaD')
141
+ assert.equal(output.createHasPrivateKey, false)
142
+ assert.equal(output.generateMissingStatus, 404)
143
+ assert.match(String(output.generateMissingError || ''), /Agent not found/i)
144
+ assert.equal(output.generateStatus, 201)
145
+ assert.equal(output.generateHasPrivateKey, false)
146
+ assert.equal(output.storedGeneratedHasPrivateKey, true)
147
+ })
@@ -1,109 +1,27 @@
1
1
  import { NextResponse } from 'next/server'
2
2
  import { safeParseBody } from '@/lib/server/safe-parse-body'
3
- import { loadAgents, loadSettings, loadWallets } from '@/lib/server/storage'
4
- import { createAgentWallet, getAgentActiveWalletId, getWalletPortfolioSnapshot, stripWalletPrivateKey } from '@/lib/server/wallet/wallet-service'
5
- import { buildEmptyWalletPortfolio } from '@/lib/server/wallet/wallet-portfolio'
6
- import type { AgentWallet, WalletPortfolioSummary } from '@/types'
7
- import { errorMessage } from '@/lib/shared-utils'
3
+ import { listWalletsSafe, createWallet, WalletServiceError } from '@/lib/server/wallets/wallet-service'
8
4
  export const dynamic = 'force-dynamic'
9
- const WALLET_LIST_PORTFOLIO_TIMEOUT_MS = 1500
10
5
 
11
- function withPortfolio(
12
- wallet: AgentWallet,
13
- portfolio: {
14
- balanceAtomic: string
15
- balanceFormatted: string
16
- balanceSymbol: string
17
- balanceDisplay: string
18
- balanceLamports?: number
19
- balanceSol?: number
20
- assets: unknown[]
21
- summary: WalletPortfolioSummary
22
- },
23
- isActive: boolean,
24
- ) {
25
- return {
26
- ...stripWalletPrivateKey(wallet as unknown as Record<string, unknown>),
27
- balanceAtomic: portfolio.balanceAtomic,
28
- balanceFormatted: portfolio.balanceFormatted,
29
- balanceSymbol: portfolio.balanceSymbol,
30
- balanceDisplay: portfolio.balanceDisplay,
31
- balanceLamports: portfolio.balanceLamports,
32
- balanceSol: portfolio.balanceSol,
33
- assets: portfolio.assets,
34
- portfolioSummary: portfolio.summary,
35
- isActive,
36
- }
37
- }
38
-
39
- export async function GET(req: Request) {
40
- const wallets = loadWallets() as Record<string, AgentWallet>
41
- const agents = loadAgents()
42
- const { searchParams } = new URL(req.url)
43
- const agentId = searchParams.get('agentId')?.trim() || ''
44
- const walletEntries = Object.entries(wallets)
45
- .filter(([, wallet]) => !agentId || wallet.agentId === agentId)
46
- const entries = await Promise.all(
47
- walletEntries.map(async ([id, wallet]) => {
48
- let portfolio = buildEmptyWalletPortfolio(wallet)
49
- try {
50
- portfolio = await getWalletPortfolioSnapshot(wallet, {
51
- timeoutMs: WALLET_LIST_PORTFOLIO_TIMEOUT_MS,
52
- allowStale: true,
53
- })
54
- } catch {
55
- // Slow or failed RPC discovery — return empty/stale portfolio for list view
56
- }
57
- const activeWalletId = getAgentActiveWalletId(agents[wallet.agentId])
58
- return [id, withPortfolio(wallet, portfolio, activeWalletId === wallet.id)] as const
59
- }),
60
- )
61
- return NextResponse.json(Object.fromEntries(entries))
62
- }
63
-
64
- interface WalletCreateBody {
65
- agentId: string
66
- chain?: string | null
67
- provider?: string | null
68
- label?: string
69
- requireApproval?: boolean
70
- spendingLimitAtomic?: string | number | null
71
- spendingLimitLamports?: string | number | null
72
- dailyLimitAtomic?: string | number | null
73
- dailyLimitLamports?: string | number | null
6
+ export async function GET() {
7
+ return NextResponse.json(listWalletsSafe())
74
8
  }
75
9
 
76
10
  export async function POST(req: Request) {
77
- const { data: body, error } = await safeParseBody<WalletCreateBody>(req)
11
+ const { data: body, error } = await safeParseBody(req)
78
12
  if (error) return error
79
- const settings = loadSettings()
80
13
  try {
81
- const wallet = createAgentWallet({
82
- agentId: body.agentId,
83
- chain: body.chain,
84
- provider: body.provider,
85
- label: body.label,
86
- requireApproval: typeof body.requireApproval === 'boolean'
87
- ? body.requireApproval
88
- : settings.walletApprovalsEnabled !== false,
89
- spendingLimitAtomic: body.spendingLimitAtomic ?? body.spendingLimitLamports,
90
- dailyLimitAtomic: body.dailyLimitAtomic ?? body.dailyLimitLamports,
14
+ const wallet = await createWallet({
15
+ agentId: typeof body.agentId === 'string' ? body.agentId : '',
16
+ walletAddress: typeof body.walletAddress === 'string' ? body.walletAddress : '',
17
+ label: typeof body.label === 'string' ? body.label : undefined,
91
18
  })
92
- return NextResponse.json(stripWalletPrivateKey(wallet as unknown as Record<string, unknown>))
93
- } catch (err: unknown) {
94
- const message = errorMessage(err)
95
- if (message === 'agentId is required') {
96
- return NextResponse.json({ error: message }, { status: 400 })
97
- }
98
- if (/^Unsupported wallet chain or provider: /.test(message)) {
99
- return NextResponse.json({ error: message }, { status: 400 })
100
- }
101
- if (message === 'Agent not found') {
102
- return NextResponse.json({ error: message }, { status: 404 })
103
- }
104
- if (/^Agent already has a (solana|ethereum) wallet$/.test(message)) {
105
- return NextResponse.json({ error: message }, { status: 409 })
19
+ return NextResponse.json(wallet, { status: 201 })
20
+ } catch (err) {
21
+ if (err instanceof WalletServiceError) {
22
+ return NextResponse.json({ error: err.message }, { status: err.status })
106
23
  }
24
+ const message = err instanceof Error ? err.message : 'Failed to create wallet'
107
25
  return NextResponse.json({ error: message }, { status: 500 })
108
26
  }
109
27
  }
@@ -7,7 +7,7 @@ import { StatCard } from '@/components/ui/stat-card'
7
7
  import { isOrchestratorEligible } from '@/lib/orchestrator-config'
8
8
  import { timeAgo } from '@/lib/time-format'
9
9
  import { useWs } from '@/hooks/use-ws'
10
- import type { Agent, ApprovalRequest, EstopState, Mission, SupervisorIncident } from '@/types'
10
+ import type { Agent, ApprovalRequest, EstopState, SupervisorIncident } from '@/types'
11
11
 
12
12
  type EstopResponse = EstopState & {
13
13
  ok?: boolean
@@ -108,7 +108,6 @@ function previewIncidentDetails(value: string): string {
108
108
  export default function AutonomyPage() {
109
109
  const [estop, setEstop] = useState<EstopResponse | null>(null)
110
110
  const [incidents, setIncidents] = useState<SupervisorIncident[]>([])
111
- const [missions, setMissions] = useState<Mission[]>([])
112
111
  const [agents, setAgents] = useState<Agent[]>([])
113
112
  const [loading, setLoading] = useState(true)
114
113
  const [refreshing, setRefreshing] = useState(false)
@@ -122,15 +121,13 @@ export default function AutonomyPage() {
122
121
  if (mode === 'initial') setLoading(true)
123
122
  else setRefreshing(true)
124
123
  try {
125
- const [estopState, incidentList, missionList, agentMap] = await Promise.all([
124
+ const [estopState, incidentList, agentMap] = await Promise.all([
126
125
  api<EstopResponse>('GET', '/autonomy/estop'),
127
126
  api<SupervisorIncident[]>('GET', '/autonomy/incidents?limit=60'),
128
- api<Mission[]>('GET', '/missions?status=non_terminal&limit=20'),
129
127
  api<Record<string, Agent>>('GET', '/agents'),
130
128
  ])
131
129
  setEstop(estopState)
132
130
  setIncidents(Array.isArray(incidentList) ? incidentList : [])
133
- setMissions(Array.isArray(missionList) ? missionList : [])
134
131
  setAgents(agentMap ? Object.values(agentMap) : [])
135
132
  setRefreshedAt(Date.now())
136
133
  setError(null)
@@ -287,14 +284,6 @@ export default function AutonomyPage() {
287
284
  () => [...incidents].sort((left, right) => right.createdAt - left.createdAt),
288
285
  [incidents],
289
286
  )
290
- const activeMissions = useMemo(
291
- () => [...missions].sort((left, right) => right.updatedAt - left.updatedAt),
292
- [missions],
293
- )
294
- const blockedMissions = activeMissions.filter((mission) =>
295
- mission.status === 'waiting' || mission.status === 'failed' || mission.status === 'cancelled',
296
- )
297
-
298
287
  const filteredIncidents = useMemo(() => {
299
288
  if (incidentFilter === 'high') return sortedIncidents.filter((incident) => incident.severity === 'high')
300
289
  if (incidentFilter === 'runtime_failure') return sortedIncidents.filter((incident) => incident.kind === 'runtime_failure')
@@ -491,50 +480,6 @@ export default function AutonomyPage() {
491
480
  </div>
492
481
  )}
493
482
 
494
- <section className="rounded-[20px] border border-white/[0.06] bg-surface p-5">
495
- <div className="mb-4 flex items-start justify-between gap-4">
496
- <div>
497
- <h2 className="font-display text-[18px] font-700 tracking-[-0.02em] text-text">Active Missions</h2>
498
- <p className="mt-1 text-[12px] leading-[1.7] text-text-3/72">
499
- Durable goals that are still running or waiting. Waiting missions surface here so operators can see blockers without digging through incidents.
500
- </p>
501
- </div>
502
- <div className="rounded-full border border-white/[0.08] bg-white/[0.03] px-3 py-1 text-[11px] text-text-3/72">
503
- {activeMissions.length} active
504
- {blockedMissions.length > 0 ? ` · ${blockedMissions.length} waiting` : ''}
505
- </div>
506
- </div>
507
-
508
- {activeMissions.length === 0 ? (
509
- <div className="rounded-[16px] border border-white/[0.06] bg-white/[0.02] px-4 py-3 text-[12px] text-text-3/70">
510
- No durable missions are active right now.
511
- </div>
512
- ) : (
513
- <div className="grid gap-3 lg:grid-cols-2">
514
- {activeMissions.slice(0, 6).map((mission) => {
515
- const waiting = mission.status === 'waiting' || mission.status === 'failed' || mission.status === 'cancelled'
516
- return (
517
- <div
518
- key={mission.id}
519
- className={`rounded-[16px] border p-4 ${waiting ? 'border-amber-500/18 bg-amber-500/[0.05]' : 'border-white/[0.06] bg-white/[0.02]'}`}
520
- >
521
- <div className="mb-2 flex items-center justify-between gap-3">
522
- <span className={`rounded-full px-2.5 py-1 text-[10px] font-700 uppercase tracking-[0.08em] ${waiting ? 'bg-amber-500/12 text-amber-300' : 'bg-emerald-500/12 text-emerald-300'}`}>
523
- {mission.status}
524
- </span>
525
- <span className="text-[11px] text-text-3/65">{timeAgo(mission.updatedAt, now)}</span>
526
- </div>
527
- <div className="text-[14px] font-600 text-text">{mission.objective}</div>
528
- <div className="mt-2 text-[12px] leading-[1.7] text-text-3/72">
529
- {mission.waitState?.reason || mission.currentStep || mission.verifierSummary || mission.plannerSummary || 'Mission active.'}
530
- </div>
531
- </div>
532
- )
533
- })}
534
- </div>
535
- )}
536
- </section>
537
-
538
483
  <section className="rounded-[20px] border border-white/[0.06] bg-surface p-5">
539
484
  <div className="mb-4 flex items-start justify-between gap-4">
540
485
  <div>
@@ -4,7 +4,6 @@ import { useCallback, useEffect, useMemo, useState } from 'react'
4
4
  import { useRouter, useSearchParams } from 'next/navigation'
5
5
  import { useAgentsQuery } from '@/features/agents/queries'
6
6
  import { useChatroomsQuery } from '@/features/chatrooms/queries'
7
- import { useMissionsQuery } from '@/features/missions/queries'
8
7
  import {
9
8
  useCreateProtocolRunMutation,
10
9
  useDeleteProtocolTemplateMutation,
@@ -21,7 +20,6 @@ import { timeAgo } from '@/lib/time-format'
21
20
  import type {
22
21
  BoardTask,
23
22
  Chatroom,
24
- Mission,
25
23
  ProtocolRun,
26
24
  ProtocolRunEvent,
27
25
  ProtocolStepDefinition,
@@ -33,7 +31,6 @@ type ProtocolRunDetail = {
33
31
  template: ProtocolTemplate | null
34
32
  transcript: Chatroom | null
35
33
  parentChatroom: Chatroom | null
36
- linkedMission: Mission | null
37
34
  linkedTask: BoardTask | null
38
35
  events: ProtocolRunEvent[]
39
36
  }
@@ -153,7 +150,6 @@ export default function ProtocolsPage() {
153
150
  facilitatorAgentId: '',
154
151
  participantAgentIds: [] as string[],
155
152
  parentChatroomId: '',
156
- missionId: '',
157
153
  taskId: '',
158
154
  autoStart: true,
159
155
  })
@@ -162,7 +158,6 @@ export default function ProtocolsPage() {
162
158
  const runsQuery = useProtocolRunsQuery({ limit: 120 })
163
159
  const agentsQuery = useAgentsQuery()
164
160
  const chatroomsQuery = useChatroomsQuery()
165
- const missionsQuery = useMissionsQuery({ limit: 80 })
166
161
  const tasksQuery = useTasksQuery({ includeArchived: true })
167
162
  const detailQuery = useProtocolRunDetailQuery(selectedRunId, { enabled: Boolean(selectedRunId) })
168
163
  const createRunMutation = useCreateProtocolRunMutation()
@@ -174,14 +169,12 @@ export default function ProtocolsPage() {
174
169
  const detail = detailQuery.data ?? null
175
170
  const agents = agentsQuery.data ?? {}
176
171
  const chatrooms = chatroomsQuery.data ?? {}
177
- const missions = missionsQuery.data ?? []
178
172
  const tasks = tasksQuery.data ?? {}
179
173
  const loading = (
180
174
  templatesQuery.isLoading
181
175
  || runsQuery.isLoading
182
176
  || agentsQuery.isLoading
183
177
  || chatroomsQuery.isLoading
184
- || missionsQuery.isLoading
185
178
  || tasksQuery.isLoading
186
179
  )
187
180
  const detailLoading = detailQuery.isLoading || detailQuery.isFetching
@@ -190,7 +183,6 @@ export default function ProtocolsPage() {
190
183
  runsQuery.error,
191
184
  agentsQuery.error,
192
185
  chatroomsQuery.error,
193
- missionsQuery.error,
194
186
  tasksQuery.error,
195
187
  detailQuery.error,
196
188
  ].find(Boolean)
@@ -237,7 +229,6 @@ export default function ProtocolsPage() {
237
229
  participantAgentIds: form.participantAgentIds,
238
230
  facilitatorAgentId: form.facilitatorAgentId || null,
239
231
  parentChatroomId: form.parentChatroomId || null,
240
- missionId: form.missionId || null,
241
232
  taskId: form.taskId || null,
242
233
  autoStart: form.autoStart,
243
234
  config: {
@@ -364,7 +355,7 @@ export default function ProtocolsPage() {
364
355
  </div>
365
356
  <h1 className="mt-4 font-display text-[34px] font-700 tracking-[-0.03em] text-text">Bounded Collaboration Runs</h1>
366
357
  <p className="mt-3 max-w-[720px] text-[15px] leading-relaxed text-text-3/72">
367
- Start structured sessions from chats, chatrooms, tasks, missions, or schedules. Runs stay temporary and bounded, while templates remain reusable for the next time you need them.
358
+ Start structured sessions from chats, chatrooms, tasks, or schedules. Runs stay temporary and bounded, while templates remain reusable for the next time you need them.
368
359
  </p>
369
360
  </div>
370
361
  <div className="w-full max-w-[520px] rounded-[20px] border border-white/[0.06] bg-surface/70 p-4">
@@ -372,7 +363,7 @@ export default function ProtocolsPage() {
372
363
  <div>
373
364
  <div className="text-[11px] font-700 uppercase tracking-[0.12em] text-text-3/55">Launch and Templates</div>
374
365
  <div className="mt-1 text-[13px] leading-relaxed text-text-3/68">
375
- Start from here when you need a blank run. Normal use should start from the chat, task, mission, or chatroom you are already in.
366
+ Start from here when you need a blank run. Normal use should start from the chat, task, or chatroom you are already in.
376
367
  </div>
377
368
  </div>
378
369
  <div className="flex flex-wrap gap-2">
@@ -471,16 +462,6 @@ export default function ProtocolsPage() {
471
462
  <option key={chatroom.id} value={chatroom.id}>{chatroom.name}</option>
472
463
  ))}
473
464
  </select>
474
- <select
475
- value={form.missionId}
476
- onChange={(event) => setForm((current) => ({ ...current, missionId: event.target.value }))}
477
- className="rounded-[12px] border border-white/[0.06] bg-white/[0.04] px-3 py-2.5 text-[14px] text-text outline-none"
478
- >
479
- <option value="">No linked mission</option>
480
- {missions.map((mission) => (
481
- <option key={mission.id} value={mission.id}>{mission.objective}</option>
482
- ))}
483
- </select>
484
465
  <select
485
466
  value={form.taskId}
486
467
  onChange={(event) => setForm((current) => ({ ...current, taskId: event.target.value }))}
@@ -11,7 +11,6 @@ import { ThemeSection } from '@/views/settings/section-theme'
11
11
  import { RuntimeLoopSection } from '@/views/settings/section-runtime-loop'
12
12
  import { SupervisorReflectionSection } from '@/views/settings/section-supervisor-reflection'
13
13
  import { CapabilityPolicySection } from '@/views/settings/section-capability-policy'
14
- import { WalletsSection } from '@/views/settings/section-wallets'
15
14
  import { StorageSection } from '@/views/settings/section-storage'
16
15
  import { VoiceSection } from '@/views/settings/section-voice'
17
16
  import { WebSearchSection } from '@/views/settings/section-web-search'
@@ -149,14 +148,6 @@ export default function SettingsRoute() {
149
148
  keywords: ['storage', 'uploads', 'disk', 'cleanup', 'files'],
150
149
  render: () => <StorageSection {...sectionProps} />,
151
150
  },
152
- {
153
- id: 'wallets',
154
- tabId: 'general',
155
- title: 'Wallets',
156
- description: 'Control global wallet approval behavior and auto-execution defaults.',
157
- keywords: ['wallet', 'wallets', 'approval', 'approvals', 'crypto', 'send'],
158
- render: () => <WalletsSection {...sectionProps} />,
159
- },
160
151
  {
161
152
  id: 'theme',
162
153
  tabId: 'appearance',
@@ -1,12 +1,112 @@
1
1
  'use client'
2
2
 
3
- import { WalletPanel } from '@/components/wallets/wallet-panel'
4
- import { MainContent } from '@/components/layout/main-content'
3
+ import { useEffect, useState } from 'react'
4
+ import { useAppStore } from '@/stores/use-app-store'
5
+ import { WalletList } from '@/components/wallets/wallet-list'
6
+ import { api } from '@/lib/app/api-client'
7
+ import type { SafeWallet } from '@/types'
5
8
 
6
9
  export default function WalletsPage() {
10
+ const agents = useAppStore((s) => s.agents)
11
+ const loadAgents = useAppStore((s) => s.loadAgents)
12
+ const loadWallets = useAppStore((s) => s.loadWallets)
13
+ const [generating, setGenerating] = useState(false)
14
+ const [showPicker, setShowPicker] = useState(false)
15
+ const [error, setError] = useState<string | null>(null)
16
+
17
+ useEffect(() => {
18
+ loadAgents()
19
+ loadWallets()
20
+ }, [loadAgents, loadWallets])
21
+
22
+ const agentList = Object.values(agents)
23
+
24
+ const handleGenerate = async (agentId?: string) => {
25
+ const targetId = agentId || agentList[0]?.id
26
+ if (!targetId) {
27
+ setError('Create an agent first')
28
+ setTimeout(() => setError(null), 3000)
29
+ return
30
+ }
31
+ setGenerating(true)
32
+ setShowPicker(false)
33
+ setError(null)
34
+ try {
35
+ await api<SafeWallet>('POST', '/wallets/generate', { agentId: targetId })
36
+ loadWallets()
37
+ } catch (err) {
38
+ setError(err instanceof Error ? err.message : 'Failed to generate wallet')
39
+ setTimeout(() => setError(null), 4000)
40
+ } finally {
41
+ setGenerating(false)
42
+ }
43
+ }
44
+
7
45
  return (
8
- <MainContent>
9
- <WalletPanel />
10
- </MainContent>
46
+ <div className="flex-1 flex flex-col h-full">
47
+ <div className="flex items-center px-6 pt-5 pb-3 shrink-0">
48
+ <h2 className="font-display text-[14px] font-600 text-text-2 tracking-[-0.01em] capitalize flex-1">
49
+ Wallets
50
+ </h2>
51
+ <div className="relative">
52
+ <button
53
+ onClick={() => {
54
+ if (agentList.length <= 1) {
55
+ handleGenerate()
56
+ } else {
57
+ setShowPicker(!showPicker)
58
+ }
59
+ }}
60
+ disabled={generating}
61
+ className="flex items-center gap-1.5 px-2.5 py-1.5 rounded-[8px] text-[11px] font-600 text-accent-bright bg-accent-soft hover:bg-accent-bright/15 transition-all cursor-pointer disabled:opacity-50"
62
+ style={{ fontFamily: 'inherit' }}
63
+ >
64
+ {generating ? (
65
+ <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round" className="animate-spin">
66
+ <path d="M21 12a9 9 0 1 1-6.219-8.56" />
67
+ </svg>
68
+ ) : (
69
+ <svg width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2.5" strokeLinecap="round">
70
+ <line x1="12" y1="5" x2="12" y2="19" /><line x1="5" y1="12" x2="19" y2="12" />
71
+ </svg>
72
+ )}
73
+ {generating ? 'Generating...' : 'Generate Wallet'}
74
+ </button>
75
+
76
+ {/* Agent picker dropdown */}
77
+ {showPicker && (
78
+ <>
79
+ <div className="fixed inset-0 z-40" onClick={() => setShowPicker(false)} />
80
+ <div className="absolute right-0 top-full mt-1 z-50 w-56 rounded-[12px] bg-surface border border-white/[0.08] shadow-xl overflow-hidden">
81
+ <div className="px-3 py-2 border-b border-white/[0.06]">
82
+ <span className="text-[11px] font-600 text-text-3">Select Agent</span>
83
+ </div>
84
+ <div className="max-h-48 overflow-y-auto py-1">
85
+ {agentList.map((agent) => (
86
+ <button
87
+ key={agent.id}
88
+ onClick={() => handleGenerate(agent.id)}
89
+ className="w-full text-left px-3 py-2 flex items-center gap-2 hover:bg-white/[0.04] transition-colors cursor-pointer"
90
+ style={{ fontFamily: 'inherit' }}
91
+ >
92
+ <span className="text-[14px]">{agent.emoji || '🤖'}</span>
93
+ <span className="text-[12px] font-600 text-text truncate">{agent.name}</span>
94
+ </button>
95
+ ))}
96
+ </div>
97
+ </div>
98
+ </>
99
+ )}
100
+ </div>
101
+ </div>
102
+
103
+ {error && (
104
+ <div className="mx-6 mb-2 px-3 py-2 rounded-[10px] bg-red-500/10 border border-red-500/20 text-[12px] text-red-400 font-600">
105
+ {error}
106
+ </div>
107
+ )}
108
+
109
+ <WalletList />
110
+ </div>
11
111
  )
12
112
  }