@swarmclawai/swarmclaw 1.2.6 → 1.2.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (269) hide show
  1. package/README.md +54 -23
  2. package/next.config.ts +1 -0
  3. package/package.json +4 -3
  4. package/scripts/easy-setup.mjs +1 -1
  5. package/scripts/postinstall.mjs +1 -1
  6. package/skills/swarmclaw.md +115 -0
  7. package/skills/tools/browser.md +131 -0
  8. package/skills/tools/execute.md +98 -0
  9. package/skills/tools/files.md +98 -0
  10. package/skills/tools/memory.md +104 -0
  11. package/skills/tools/platform.md +144 -0
  12. package/skills/tools/skills.md +83 -0
  13. package/src/app/agents/[id]/page.tsx +1 -18
  14. package/src/app/api/agents/thread-route.test.ts +0 -1
  15. package/src/app/api/approvals/route.test.ts +6 -22
  16. package/src/app/api/chats/[id]/messages/route.ts +23 -19
  17. package/src/app/api/chats/messages-route.test.ts +105 -51
  18. package/src/app/api/connectors/route.ts +2 -2
  19. package/src/app/api/mcp-servers/[id]/test/route.ts +3 -2
  20. package/src/app/api/openclaw/deploy/route.ts +2 -0
  21. package/src/app/api/portability/export/route.ts +8 -0
  22. package/src/app/api/portability/import/route.test.ts +80 -0
  23. package/src/app/api/portability/import/route.ts +28 -0
  24. package/src/app/api/settings/route.ts +0 -2
  25. package/src/app/api/setup/doctor/route.ts +4 -4
  26. package/src/app/api/wallets/[id]/route.ts +15 -157
  27. package/src/app/api/wallets/generate/route.ts +22 -0
  28. package/src/app/api/wallets/route.test.ts +147 -0
  29. package/src/app/api/wallets/route.ts +13 -95
  30. package/src/app/autonomy/page.tsx +2 -57
  31. package/src/app/protocols/page.tsx +2 -21
  32. package/src/app/settings/page.tsx +0 -9
  33. package/src/app/wallets/page.tsx +105 -5
  34. package/src/cli/index.js +21 -33
  35. package/src/cli/spec.js +19 -30
  36. package/src/components/agents/agent-chat-list.tsx +23 -1
  37. package/src/components/agents/agent-sheet.tsx +2 -40
  38. package/src/components/agents/inspector-panel.tsx +165 -131
  39. package/src/components/chat/chat-area.tsx +38 -9
  40. package/src/components/chat/chat-card.tsx +0 -31
  41. package/src/components/chat/message-bubble.tsx +1 -108
  42. package/src/components/chat/message-list.tsx +33 -19
  43. package/src/components/connectors/connector-sheet.tsx +25 -1
  44. package/src/components/gateways/gateway-sheet.tsx +5 -2
  45. package/src/components/layout/sidebar-rail.tsx +6 -10
  46. package/src/components/projects/project-detail.tsx +3 -35
  47. package/src/components/projects/tabs/overview-tab.tsx +3 -59
  48. package/src/components/projects/tabs/work-tab.tsx +7 -77
  49. package/src/components/protocols/structured-session-launcher.tsx +1 -22
  50. package/src/components/shared/connector-platform-icon.tsx +1 -0
  51. package/src/components/tasks/task-card.tsx +4 -34
  52. package/src/components/tasks/task-sheet.tsx +6 -36
  53. package/src/components/wallets/wallet-list.tsx +150 -0
  54. package/src/lib/agent-execute-defaults.test.ts +24 -0
  55. package/src/lib/agent-execute-defaults.ts +62 -0
  56. package/src/lib/app/navigation.test.ts +0 -13
  57. package/src/lib/app/navigation.ts +2 -7
  58. package/src/lib/app/view-constants.ts +14 -19
  59. package/src/lib/chat/queued-message-queue.test.ts +134 -1
  60. package/src/lib/chat/queued-message-queue.ts +77 -2
  61. package/src/lib/server/agents/agent-service.ts +5 -0
  62. package/src/lib/server/agents/agent-thread-session.ts +0 -1
  63. package/src/lib/server/agents/delegation-advisory.test.ts +0 -1
  64. package/src/lib/server/agents/delegation-jobs.test.ts +0 -69
  65. package/src/lib/server/agents/delegation-jobs.ts +0 -25
  66. package/src/lib/server/agents/main-agent-loop.ts +1 -49
  67. package/src/lib/server/agents/subagent-runtime.ts +0 -1
  68. package/src/lib/server/approval-match.ts +0 -85
  69. package/src/lib/server/approvals.test.ts +6 -6
  70. package/src/lib/server/approvals.ts +0 -6
  71. package/src/lib/server/autonomy/supervisor-reflection.test.ts +0 -1
  72. package/src/lib/server/builtin-extensions.ts +1 -2
  73. package/src/lib/server/capability-router.test.ts +0 -2
  74. package/src/lib/server/chat-execution/chat-execution-advanced.test.ts +1 -1
  75. package/src/lib/server/chat-execution/chat-execution-tool-events.test.ts +15 -14
  76. package/src/lib/server/chat-execution/chat-execution-types.ts +0 -2
  77. package/src/lib/server/chat-execution/chat-execution-utils.ts +2 -4
  78. package/src/lib/server/chat-execution/chat-streaming-utils.ts +2 -30
  79. package/src/lib/server/chat-execution/chat-turn-finalization.ts +1 -36
  80. package/src/lib/server/chat-execution/chat-turn-preparation.ts +81 -64
  81. package/src/lib/server/chat-execution/chat-turn-stream-execution.ts +4 -0
  82. package/src/lib/server/chat-execution/continuation-evaluator.ts +8 -0
  83. package/src/lib/server/chat-execution/iteration-event-handler.ts +0 -24
  84. package/src/lib/server/chat-execution/memory-mutation-tools.ts +1 -1
  85. package/src/lib/server/chat-execution/message-classifier.test.ts +0 -45
  86. package/src/lib/server/chat-execution/message-classifier.ts +11 -16
  87. package/src/lib/server/chat-execution/prompt-builder.test.ts +27 -0
  88. package/src/lib/server/chat-execution/prompt-builder.ts +14 -31
  89. package/src/lib/server/chat-execution/prompt-mode.test.ts +24 -0
  90. package/src/lib/server/chat-execution/prompt-mode.ts +5 -1
  91. package/src/lib/server/chat-execution/prompt-sections.ts +0 -1
  92. package/src/lib/server/chat-execution/situational-awareness.test.ts +2 -73
  93. package/src/lib/server/chat-execution/situational-awareness.ts +4 -38
  94. package/src/lib/server/chat-execution/stream-agent-chat.test.ts +13 -126
  95. package/src/lib/server/chat-execution/stream-agent-chat.ts +46 -21
  96. package/src/lib/server/chat-execution/stream-continuation.test.ts +4 -52
  97. package/src/lib/server/chat-execution/stream-continuation.ts +6 -48
  98. package/src/lib/server/chatrooms/chatroom-routing.test.ts +4 -0
  99. package/src/lib/server/chatrooms/session-mailbox.ts +0 -10
  100. package/src/lib/server/chats/chat-session-service.ts +3 -5
  101. package/src/lib/server/connectors/connector-inbound.ts +0 -1
  102. package/src/lib/server/connectors/connector-lifecycle.ts +19 -3
  103. package/src/lib/server/connectors/connector-service.ts +39 -9
  104. package/src/lib/server/connectors/discord.ts +2 -2
  105. package/src/lib/server/connectors/matrix.ts +3 -2
  106. package/src/lib/server/connectors/signal.ts +5 -4
  107. package/src/lib/server/connectors/slack.ts +10 -9
  108. package/src/lib/server/connectors/swarmdock-bidding.ts +74 -0
  109. package/src/lib/server/connectors/swarmdock-payloads.test.ts +85 -0
  110. package/src/lib/server/connectors/swarmdock-secret.test.ts +128 -0
  111. package/src/lib/server/connectors/swarmdock-secret.ts +152 -0
  112. package/src/lib/server/connectors/swarmdock-tasks.ts +119 -0
  113. package/src/lib/server/connectors/swarmdock.ts +255 -0
  114. package/src/lib/server/connectors/teams.ts +3 -2
  115. package/src/lib/server/connectors/telegram.ts +4 -4
  116. package/src/lib/server/connectors/whatsapp.ts +2 -2
  117. package/src/lib/server/daemon/controller.ts +7 -0
  118. package/src/lib/server/execution-brief.test.ts +2 -25
  119. package/src/lib/server/execution-brief.ts +12 -35
  120. package/src/lib/server/execution-engine/task-attempt.ts +0 -1
  121. package/src/lib/server/gateways/gateway-profile-service.ts +19 -1
  122. package/src/lib/server/messages/message-repository.test.ts +70 -0
  123. package/src/lib/server/messages/message-repository.ts +11 -6
  124. package/src/lib/server/openclaw/deploy.ts +32 -2
  125. package/src/lib/server/persistence/storage-context.ts +0 -5
  126. package/src/lib/server/plugins-advanced.test.ts +1 -2
  127. package/src/lib/server/portability/export.ts +109 -0
  128. package/src/lib/server/portability/import.ts +159 -0
  129. package/src/lib/server/protocols/protocol-normalization.ts +0 -4
  130. package/src/lib/server/protocols/protocol-queries.ts +0 -6
  131. package/src/lib/server/protocols/protocol-run-lifecycle.ts +4 -32
  132. package/src/lib/server/protocols/protocol-service.ts +0 -1
  133. package/src/lib/server/protocols/protocol-step-helpers.ts +0 -4
  134. package/src/lib/server/protocols/protocol-step-processors.ts +0 -6
  135. package/src/lib/server/protocols/protocol-swarm.ts +0 -2
  136. package/src/lib/server/protocols/protocol-types.ts +0 -2
  137. package/src/lib/server/provider-health.ts +1 -10
  138. package/src/lib/server/runtime/daemon-state/core.ts +0 -9
  139. package/src/lib/server/runtime/daemon-state.test.ts +0 -35
  140. package/src/lib/server/runtime/heartbeat-service.ts +3 -23
  141. package/src/lib/server/runtime/process-manager.ts +13 -9
  142. package/src/lib/server/runtime/queue/core.ts +11 -33
  143. package/src/lib/server/runtime/runtime-storage-write-paths.test.ts +6 -6
  144. package/src/lib/server/runtime/scheduler.ts +0 -13
  145. package/src/lib/server/runtime/session-run-manager/drain.ts +0 -24
  146. package/src/lib/server/runtime/session-run-manager/enqueue.ts +0 -1
  147. package/src/lib/server/runtime/session-run-manager/queries.ts +15 -1
  148. package/src/lib/server/runtime/session-run-manager/recovery.ts +0 -1
  149. package/src/lib/server/runtime/session-run-manager.test.ts +58 -28
  150. package/src/lib/server/sandbox/session-runtime.test.ts +18 -1
  151. package/src/lib/server/sandbox/session-runtime.ts +40 -28
  152. package/src/lib/server/session-tools/autonomy-tools.test.ts +7 -9
  153. package/src/lib/server/session-tools/context.ts +1 -1
  154. package/src/lib/server/session-tools/credential-env.ts +109 -0
  155. package/src/lib/server/session-tools/crud.ts +3 -17
  156. package/src/lib/server/session-tools/delegate.ts +0 -4
  157. package/src/lib/server/session-tools/edit_file.ts +3 -2
  158. package/src/lib/server/session-tools/execute.test.ts +58 -0
  159. package/src/lib/server/session-tools/execute.ts +334 -0
  160. package/src/lib/server/session-tools/files-tool.ts +635 -0
  161. package/src/lib/server/session-tools/index.ts +14 -8
  162. package/src/lib/server/session-tools/memory-tool.ts +242 -0
  163. package/src/lib/server/session-tools/memory.ts +1 -1
  164. package/src/lib/server/session-tools/openclaw-nodes.ts +3 -2
  165. package/src/lib/server/session-tools/openclaw-workspace.ts +3 -2
  166. package/src/lib/server/session-tools/platform-tool.ts +617 -0
  167. package/src/lib/server/session-tools/session-info.ts +3 -2
  168. package/src/lib/server/session-tools/session-tools-wiring.test.ts +3 -4
  169. package/src/lib/server/session-tools/shell.ts +7 -122
  170. package/src/lib/server/session-tools/skills-tool.ts +396 -0
  171. package/src/lib/server/session-tools/team-context.ts +0 -3
  172. package/src/lib/server/session-tools/web.ts +2 -2
  173. package/src/lib/server/storage-normalization.ts +10 -0
  174. package/src/lib/server/storage.ts +18 -45
  175. package/src/lib/server/tasks/task-checkout.ts +59 -0
  176. package/src/lib/server/tasks/task-lifecycle.ts +2 -0
  177. package/src/lib/server/tasks/task-route-service.ts +4 -26
  178. package/src/lib/server/tasks/task-service.ts +0 -7
  179. package/src/lib/server/tool-aliases.ts +2 -2
  180. package/src/lib/server/tool-capability-policy-advanced.test.ts +13 -6
  181. package/src/lib/server/tool-capability-policy.test.ts +2 -1
  182. package/src/lib/server/tool-capability-policy.ts +60 -35
  183. package/src/lib/server/tool-planning.ts +11 -12
  184. package/src/lib/server/universal-tool-access.ts +0 -1
  185. package/src/lib/server/wallets/wallet-crypto.ts +33 -0
  186. package/src/lib/server/wallets/wallet-repository.ts +24 -0
  187. package/src/lib/server/wallets/wallet-service.ts +119 -0
  188. package/src/lib/server/working-state/extraction.ts +8 -42
  189. package/src/lib/server/working-state/normalization.ts +10 -103
  190. package/src/lib/server/working-state/service.ts +12 -21
  191. package/src/lib/setup-defaults.ts +5 -0
  192. package/src/lib/strip-internal-metadata.test.ts +1 -1
  193. package/src/lib/strip-internal-metadata.ts +1 -1
  194. package/src/lib/tool-definitions.ts +1 -1
  195. package/src/lib/validation/schemas.test.ts +16 -0
  196. package/src/lib/validation/schemas.ts +49 -2
  197. package/src/stores/slices/data-slice.ts +5 -1
  198. package/src/stores/slices/ui-slice.ts +0 -4
  199. package/src/stores/use-chat-store.test.ts +231 -0
  200. package/src/stores/use-chat-store.ts +62 -13
  201. package/src/types/agent.ts +264 -0
  202. package/src/types/app-settings.ts +173 -0
  203. package/src/types/approval.ts +25 -0
  204. package/src/types/connector.ts +188 -0
  205. package/src/types/extension.ts +386 -0
  206. package/src/types/index.ts +16 -3555
  207. package/src/types/message.ts +56 -0
  208. package/src/types/misc.ts +737 -0
  209. package/src/types/protocol.ts +420 -0
  210. package/src/types/provider.ts +52 -0
  211. package/src/types/run.ts +180 -0
  212. package/src/types/schedule.ts +59 -0
  213. package/src/types/session.ts +215 -0
  214. package/src/types/skill.ts +157 -0
  215. package/src/types/swarmdock.ts +29 -0
  216. package/src/types/task.ts +144 -0
  217. package/src/types/working-state.ts +204 -0
  218. package/src/views/settings/section-heartbeat.tsx +2 -2
  219. package/src/views/settings/section-runtime-loop.tsx +0 -14
  220. package/src/app/api/canvas/[sessionId]/route.ts +0 -35
  221. package/src/app/api/missions/[id]/actions/route.ts +0 -31
  222. package/src/app/api/missions/[id]/events/route.ts +0 -14
  223. package/src/app/api/missions/[id]/route.ts +0 -10
  224. package/src/app/api/missions/route.test.ts +0 -244
  225. package/src/app/api/missions/route.ts +0 -57
  226. package/src/app/api/wallets/[id]/approve/route.ts +0 -79
  227. package/src/app/api/wallets/[id]/balance-history/route.ts +0 -18
  228. package/src/app/api/wallets/[id]/send/route.ts +0 -113
  229. package/src/app/api/wallets/[id]/transactions/route.ts +0 -18
  230. package/src/app/missions/[id]/page.tsx +0 -3
  231. package/src/app/missions/page.tsx +0 -685
  232. package/src/components/canvas/canvas-panel.tsx +0 -267
  233. package/src/components/wallets/wallet-approval-dialog.tsx +0 -107
  234. package/src/components/wallets/wallet-panel.tsx +0 -1010
  235. package/src/components/wallets/wallet-section.tsx +0 -260
  236. package/src/features/missions/queries.ts +0 -23
  237. package/src/lib/canvas-content.test.ts +0 -360
  238. package/src/lib/canvas-content.ts +0 -198
  239. package/src/lib/server/canvas-content.test.ts +0 -32
  240. package/src/lib/server/canvas-content.ts +0 -6
  241. package/src/lib/server/ethereum.ts +0 -591
  242. package/src/lib/server/evm-swap.ts +0 -476
  243. package/src/lib/server/missions/mission-intent.test.ts +0 -63
  244. package/src/lib/server/missions/mission-intent.ts +0 -569
  245. package/src/lib/server/missions/mission-repository.ts +0 -74
  246. package/src/lib/server/missions/mission-service/actions.ts +0 -6
  247. package/src/lib/server/missions/mission-service/bindings.ts +0 -9
  248. package/src/lib/server/missions/mission-service/context.ts +0 -4
  249. package/src/lib/server/missions/mission-service/core.ts +0 -2271
  250. package/src/lib/server/missions/mission-service/queries.ts +0 -12
  251. package/src/lib/server/missions/mission-service/recovery.ts +0 -5
  252. package/src/lib/server/missions/mission-service/ticks.ts +0 -9
  253. package/src/lib/server/missions/mission-service.test.ts +0 -888
  254. package/src/lib/server/missions/mission-service.ts +0 -6
  255. package/src/lib/server/session-tools/canvas.ts +0 -105
  256. package/src/lib/server/session-tools/sandbox.ts +0 -281
  257. package/src/lib/server/session-tools/wallet-tool.test.ts +0 -150
  258. package/src/lib/server/session-tools/wallet.ts +0 -1287
  259. package/src/lib/server/solana.ts +0 -327
  260. package/src/lib/server/wallet/wallet-execution.test.ts +0 -198
  261. package/src/lib/server/wallet/wallet-portfolio.test.ts +0 -98
  262. package/src/lib/server/wallet/wallet-portfolio.ts +0 -772
  263. package/src/lib/server/wallet/wallet-service.test.ts +0 -81
  264. package/src/lib/server/wallet/wallet-service.ts +0 -225
  265. package/src/lib/wallet/wallet-transactions.test.ts +0 -75
  266. package/src/lib/wallet/wallet-transactions.ts +0 -43
  267. package/src/lib/wallet/wallet.test.ts +0 -333
  268. package/src/lib/wallet/wallet.ts +0 -183
  269. package/src/views/settings/section-wallets.tsx +0 -35
@@ -24,8 +24,6 @@ import type {
24
24
  GuardianCheckpoint,
25
25
  LearnedSkill,
26
26
  Message,
27
- Mission,
28
- MissionEvent,
29
27
  ProtocolTemplate,
30
28
  ProtocolRun,
31
29
  ProtocolRunEvent,
@@ -138,9 +136,6 @@ const COLLECTIONS = [
138
136
  'webhook_retry_queue',
139
137
  'notifications',
140
138
  'chatrooms',
141
- 'wallets',
142
- 'wallet_transactions',
143
- 'wallet_balance_history',
144
139
  'moderation_logs',
145
140
  'connector_health',
146
141
  'connector_outbox',
@@ -152,8 +147,6 @@ const COLLECTIONS = [
152
147
  'watch_jobs',
153
148
  'delegation_jobs',
154
149
  'external_agents',
155
- 'missions',
156
- 'mission_events',
157
150
  'protocol_templates',
158
151
  'protocol_runs',
159
152
  'protocol_run_events',
@@ -162,6 +155,8 @@ const COLLECTIONS = [
162
155
  'main_loop_states',
163
156
  'working_states',
164
157
  'daemon_status',
158
+ 'wallets',
159
+ 'wallet_transactions',
165
160
  ] as const
166
161
 
167
162
  export type StorageCollection = (typeof COLLECTIONS)[number]
@@ -545,8 +540,6 @@ const JSON_FILES: Record<string, string> = {
545
540
  documents: path.join(DATA_DIR, 'documents.json'),
546
541
  webhooks: path.join(DATA_DIR, 'webhooks.json'),
547
542
  external_agents: path.join(DATA_DIR, 'external-agents.json'),
548
- missions: path.join(DATA_DIR, 'missions.json'),
549
- mission_events: path.join(DATA_DIR, 'mission-events.json'),
550
543
  protocol_templates: path.join(DATA_DIR, 'protocol-templates.json'),
551
544
  protocol_runs: path.join(DATA_DIR, 'protocol-runs.json'),
552
545
  protocol_run_events: path.join(DATA_DIR, 'protocol-run-events.json'),
@@ -1208,25 +1201,6 @@ export const loadProjects = projectsStore.load
1208
1201
  export const saveProjects = projectsStore.save
1209
1202
  export const deleteProject = projectsStore.deleteItem
1210
1203
 
1211
- // --- Missions ---
1212
- const missionsStore = createCollectionStore('missions')
1213
- export const loadMissions = missionsStore.load as () => Record<string, Mission>
1214
- export const saveMissions = missionsStore.save as (items: Record<string, Mission>) => void
1215
- export const loadMission = missionsStore.loadItem as (id: string) => Mission | null
1216
- export const upsertMission = missionsStore.upsert as (id: string, value: Mission) => void
1217
- export const patchMission = missionsStore.patch as (
1218
- id: string,
1219
- updater: (current: Mission | null) => Mission | null,
1220
- ) => Mission | null
1221
- export const deleteMission = missionsStore.deleteItem
1222
-
1223
- const missionEventsStore = createCollectionStore('mission_events')
1224
- export const loadMissionEvents = missionEventsStore.load as () => Record<string, MissionEvent>
1225
- export const saveMissionEvents = missionEventsStore.save as (items: Record<string, MissionEvent>) => void
1226
- export const loadMissionEvent = missionEventsStore.loadItem as (id: string) => MissionEvent | null
1227
- export const upsertMissionEvent = missionEventsStore.upsert as (id: string, value: MissionEvent) => void
1228
- export const upsertMissionEvents = missionEventsStore.upsertMany as (entries: Array<[string, MissionEvent]>) => void
1229
-
1230
1204
  const protocolTemplatesStore = createCollectionStore('protocol_templates')
1231
1205
  export const loadProtocolTemplates = protocolTemplatesStore.load as () => Record<string, ProtocolTemplate>
1232
1206
  export const saveProtocolTemplates = protocolTemplatesStore.save as (items: Record<string, ProtocolTemplate>) => void
@@ -1541,23 +1515,6 @@ export function markNotificationRead(id: string) {
1541
1515
  }
1542
1516
  }
1543
1517
 
1544
- // --- Wallets ---
1545
- const walletsStore = createCollectionStore('wallets')
1546
- export const loadWallets = walletsStore.load
1547
- export const upsertWallet = walletsStore.upsert
1548
- export const deleteWallet = walletsStore.deleteItem
1549
-
1550
- // --- Wallet Transactions ---
1551
- const walletTransactionsStore = createCollectionStore('wallet_transactions')
1552
- export const loadWalletTransactions = walletTransactionsStore.load
1553
- export const upsertWalletTransaction = walletTransactionsStore.upsert
1554
- export const deleteWalletTransaction = walletTransactionsStore.deleteItem
1555
-
1556
- // --- Wallet Balance History ---
1557
- const walletBalanceHistoryStore = createCollectionStore('wallet_balance_history')
1558
- export const loadWalletBalanceHistory = walletBalanceHistoryStore.load
1559
- export const upsertWalletBalanceSnapshot = walletBalanceHistoryStore.upsert
1560
-
1561
1518
  // --- Moderation Logs ---
1562
1519
  const moderationLogsStore = createCollectionStore('moderation_logs')
1563
1520
  export const loadModerationLogs = moderationLogsStore.load
@@ -1613,6 +1570,22 @@ export const loadPersistedWorkingState = workingStatesStore.loadItem
1613
1570
  export const upsertPersistedWorkingState = workingStatesStore.upsert
1614
1571
  export const deletePersistedWorkingState = workingStatesStore.deleteItem
1615
1572
 
1573
+ // --- Wallets ---
1574
+ const walletsStore = createCollectionStore('wallets')
1575
+ export const loadWallets = walletsStore.load
1576
+ export const saveWallets = walletsStore.save
1577
+ export const loadWallet = walletsStore.loadItem
1578
+ export const upsertWallet = walletsStore.upsert
1579
+ export const deleteWalletItem = walletsStore.deleteItem
1580
+
1581
+ // --- Wallet Transactions ---
1582
+ const walletTransactionsStore = createCollectionStore('wallet_transactions')
1583
+ export const loadWalletTransactions = walletTransactionsStore.load
1584
+ export const saveWalletTransactions = walletTransactionsStore.save
1585
+ export const loadWalletTransaction = walletTransactionsStore.loadItem
1586
+ export const upsertWalletTransaction = walletTransactionsStore.upsert
1587
+ export const deleteWalletTransaction = walletTransactionsStore.deleteItem
1588
+
1616
1589
  export function getSessionMessages(sessionId: string): Message[] {
1617
1590
  const session = loadSession(sessionId)
1618
1591
  return Array.isArray(session?.messages) ? session.messages : []
@@ -0,0 +1,59 @@
1
+ import type { BoardTask } from '@/types'
2
+ import { withTransaction } from '@/lib/server/persistence/transaction'
3
+ import { loadTasks, saveTasks } from '@/lib/server/tasks/task-repository'
4
+
5
+ /**
6
+ * Atomically transition a task from queued → running with a checkout run ID.
7
+ *
8
+ * Uses a SQLite IMMEDIATE transaction to prevent two runners from starting the
9
+ * same task concurrently (Paperclip-inspired atomic checkout pattern).
10
+ *
11
+ * Returns the checked-out task on success, or null if the task was already
12
+ * taken, missing, or no longer in queued status.
13
+ */
14
+ export function checkoutTask(
15
+ taskId: string,
16
+ runId: string,
17
+ ): BoardTask | null {
18
+ return withTransaction(() => {
19
+ const tasks = loadTasks() as Record<string, BoardTask>
20
+ const task = tasks[taskId]
21
+ if (!task || task.status !== 'queued') return null
22
+ if (task.checkoutRunId) return null // already checked out
23
+
24
+ const now = Date.now()
25
+ task.status = 'running'
26
+ task.checkoutRunId = runId
27
+ task.startedAt = now
28
+ task.lastActivityAt = now
29
+ task.retryScheduledAt = null
30
+ task.deadLetteredAt = null
31
+ task.error = null
32
+ task.validation = null
33
+ task.updatedAt = now
34
+
35
+ saveTasks(tasks)
36
+ return { ...task }
37
+ })
38
+ }
39
+
40
+ /**
41
+ * Release a checkout after task completion or failure.
42
+ * Only the holder of the checkout (matching runId) can release it.
43
+ */
44
+ export function releaseCheckout(
45
+ taskId: string,
46
+ runId: string,
47
+ ): boolean {
48
+ return withTransaction(() => {
49
+ const tasks = loadTasks() as Record<string, BoardTask>
50
+ const task = tasks[taskId]
51
+ if (!task) return false
52
+ if (task.checkoutRunId !== runId) return false
53
+
54
+ task.checkoutRunId = null
55
+ task.updatedAt = Date.now()
56
+ saveTasks(tasks)
57
+ return true
58
+ })
59
+ }
@@ -176,6 +176,7 @@ export function markValidatedTaskCompleted(
176
176
  task.completedAt = options.preserveCompletedAt ? (task.completedAt || options.now) : options.now
177
177
  task.updatedAt = options.now
178
178
  task.error = null
179
+ task.checkoutRunId = null
179
180
  return task
180
181
  }
181
182
 
@@ -187,6 +188,7 @@ export function markInvalidCompletedTaskFailed(
187
188
  task.status = 'failed'
188
189
  task.completedAt = null
189
190
  task.updatedAt = options.now
191
+ task.checkoutRunId = null
190
192
  task.error = formatValidationFailure(validation.reasons).slice(0, 500)
191
193
  if (options.comment) {
192
194
  if (!task.comments) task.comments = []
@@ -6,11 +6,6 @@ import { logActivity } from '@/lib/server/activity/activity-log'
6
6
  import { createNotification } from '@/lib/server/create-notification'
7
7
  import { validateDag, cascadeUnblock } from '@/lib/server/dag-validation'
8
8
  import { getExtensionManager } from '@/lib/server/extensions'
9
- import {
10
- enrichTaskWithMissionSummary,
11
- ensureMissionForTask,
12
- noteMissionTaskFinished,
13
- } from '@/lib/server/missions/mission-service'
14
9
  import {
15
10
  disableSessionHeartbeat,
16
11
  enqueueTask,
@@ -64,9 +59,7 @@ export function prepareTasksForListing() {
64
59
  validateCompletedTasksQueue()
65
60
  recoverStalledRunningTasks()
66
61
  const allTasks = loadTasks()
67
- return Object.fromEntries(
68
- Object.entries(allTasks).map(([id, task]) => [id, enrichTaskWithMissionSummary(task)]),
69
- )
62
+ return allTasks
70
63
  }
71
64
 
72
65
  export function updateTaskFromRoute(id: string, body: Record<string, unknown>): ServiceResult<BoardTask> {
@@ -128,10 +121,6 @@ export function updateTaskFromRoute(id: string, body: Record<string, unknown>):
128
121
  }
129
122
 
130
123
  saveTask(id, tasks[id])
131
- const mission = ensureMissionForTask(tasks[id], { source: 'manual' })
132
- if (tasks[id].status === 'completed' || tasks[id].status === 'failed' || tasks[id].status === 'cancelled') {
133
- noteMissionTaskFinished(tasks[id], tasks[id].status, tasks[id].id)
134
- }
135
124
  logActivity({ entityType: 'task', entityId: id, action: 'updated', actor: 'user', summary: `Task updated: "${tasks[id].title}" (${prevStatus} → ${tasks[id].status})` })
136
125
  if (prevStatus !== tasks[id].status) {
137
126
  pushMainLoopEventToMainSessions({
@@ -143,10 +132,7 @@ export function updateTaskFromRoute(id: string, body: Record<string, unknown>):
143
132
  if (prevStatus !== tasks[id].status && tasks[id].status === 'cancelled') {
144
133
  disableSessionHeartbeat(tasks[id].sessionId)
145
134
  notify('tasks')
146
- return serviceOk(enrichTaskWithMissionSummary({
147
- ...tasks[id],
148
- missionId: mission?.id || tasks[id].missionId || null,
149
- }))
135
+ return serviceOk(tasks[id])
150
136
  }
151
137
 
152
138
  if (prevStatus !== tasks[id].status && (tasks[id].status === 'completed' || tasks[id].status === 'failed')) {
@@ -216,10 +202,7 @@ export function updateTaskFromRoute(id: string, body: Record<string, unknown>):
216
202
  }
217
203
 
218
204
  notify('tasks')
219
- return serviceOk(enrichTaskWithMissionSummary({
220
- ...tasks[id],
221
- missionId: mission?.id || tasks[id].missionId || null,
222
- }))
205
+ return serviceOk(tasks[id])
223
206
  }
224
207
 
225
208
  export function archiveTaskFromRoute(id: string): ServiceResult<BoardTask> {
@@ -346,11 +329,6 @@ export function createTaskFromRoute(body: Record<string, unknown>): ServiceResul
346
329
  }
347
330
 
348
331
  saveTask(id, task)
349
- const mission = ensureMissionForTask(task, { source: 'manual' })
350
- const finalTask = enrichTaskWithMissionSummary({
351
- ...task,
352
- missionId: mission?.id || task.missionId || null,
353
- })
354
332
  logActivity({ entityType: 'task', entityId: id, action: 'created', actor: 'user', summary: `Task created: "${task.title}"` })
355
333
  pushMainLoopEventToMainSessions({
356
334
  type: 'task_created',
@@ -360,7 +338,7 @@ export function createTaskFromRoute(body: Record<string, unknown>): ServiceResul
360
338
  enqueueTask(id)
361
339
  }
362
340
  notify('tasks')
363
- return serviceOk(finalTask)
341
+ return serviceOk(task)
364
342
  }
365
343
 
366
344
  export function bulkUpdateTasksFromRoute(body: Record<string, unknown>): ServiceResult<{ updated: number; ids: string[] }> {
@@ -106,13 +106,6 @@ export function applyTaskContinuationDefaults(
106
106
  if (!Object.prototype.hasOwnProperty.call(explicit, 'cwd') && typeof sourceTask.cwd === 'string' && sourceTask.cwd.trim()) {
107
107
  parsed.cwd = sourceTask.cwd.trim()
108
108
  }
109
- if (
110
- !Object.prototype.hasOwnProperty.call(explicit, 'missionId')
111
- && typeof sourceTask.missionId === 'string'
112
- && sourceTask.missionId.trim()
113
- ) {
114
- parsed.missionId = sourceTask.missionId.trim()
115
- }
116
109
  const sourceSessionId = typeof sourceTask.checkpoint?.lastSessionId === 'string' && sourceTask.checkpoint.lastSessionId.trim()
117
110
  ? sourceTask.checkpoint.lastSessionId.trim()
118
111
  : typeof sourceTask.sessionId === 'string' && sourceTask.sessionId.trim()
@@ -1,5 +1,6 @@
1
1
  const EXTENSION_ALIAS_GROUPS: string[][] = [
2
- ['shell', 'execute_command', 'process_tool', 'git', 'sandbox', 'sandbox_exec', 'sandbox_list_runtimes'],
2
+ ['shell', 'execute_command', 'process_tool', 'git'],
3
+ ['execute', 'sandbox'],
3
4
  ['files', 'read_file', 'write_file', 'list_files', 'copy_file', 'move_file', 'delete_file', 'send_file'],
4
5
  ['edit_file'],
5
6
  ['web', 'web_search', 'web_fetch', 'http_request', 'http'],
@@ -21,7 +22,6 @@ const EXTENSION_ALIAS_GROUPS: string[][] = [
21
22
  ['schedule_wake', 'schedule'],
22
23
  // http_request/http now aliased into web group above
23
24
  ['memory', 'memory_tool', 'memory_search', 'memory_get', 'memory_store', 'memory_update'],
24
- ['wallet', 'wallet_tool'],
25
25
  ['monitor', 'monitor_tool'],
26
26
  ['context_mgmt', 'context_status', 'context_summarize'],
27
27
  ['openclaw_workspace'],
@@ -92,6 +92,13 @@ describe('strict mode', () => {
92
92
  assert.match(d.blockedExtensions[0].reason, /strict policy/)
93
93
  })
94
94
 
95
+ it('blocks execute (execution category)', () => {
96
+ const d = resolveSessionToolPolicy(['execute'], mode)
97
+ assert.equal(d.blockedExtensions.length, 1)
98
+ assert.equal(d.blockedExtensions[0].tool, 'execute')
99
+ assert.match(d.blockedExtensions[0].reason, /strict policy/)
100
+ })
101
+
95
102
  it('blocks files (filesystem category)', () => {
96
103
  const d = resolveSessionToolPolicy(['files'], mode)
97
104
  assert.equal(d.blockedExtensions.length, 1)
@@ -168,7 +175,7 @@ describe('safety blocks', () => {
168
175
  assert.equal(d.blockedExtensions[0].source, 'safety')
169
176
  })
170
177
 
171
- it('safety block on concrete web_search blocks the web_search family', () => {
178
+ it('safety block on concrete web_search blocks the requested extension', () => {
172
179
  const d = resolveSessionToolPolicy(['web_search'], {
173
180
  safetyBlockedTools: ['web_search'],
174
181
  })
@@ -186,7 +193,7 @@ describe('safety blocks', () => {
186
193
  assert.equal(d.blockedExtensions[0].source, 'safety')
187
194
  })
188
195
 
189
- it('safety block on delegate_to_claude_code blocks claude_code', () => {
196
+ it('safety block on delegate_to_claude_code blocks the requested extension', () => {
190
197
  const d = resolveSessionToolPolicy(['claude_code'], {
191
198
  safetyBlockedTools: ['delegate_to_claude_code'],
192
199
  })
@@ -416,17 +423,17 @@ describe('compound scenarios', () => {
416
423
  assert.match(tasksBlock.reason, /task management is disabled/)
417
424
  })
418
425
 
419
- it('20 tools requested: correctly partitioned into enabled vs blocked', () => {
426
+ it('19 tools requested: correctly partitioned into enabled vs blocked', () => {
420
427
  const tools = [
421
428
  'shell', 'files', 'web', 'web_search', 'web_fetch', 'browser',
422
429
  'memory', 'delegate', 'manage_platform', 'manage_tasks',
423
- 'manage_schedules', 'wallet', 'delete_file', 'canvas',
430
+ 'manage_schedules', 'wallet', 'delete_file',
424
431
  'manage_connectors', 'git', 'sandbox', 'claude_code',
425
432
  'monitor', 'http_request',
426
433
  ]
427
434
  const d = resolveSessionToolPolicy(tools, { capabilityPolicyMode: 'strict' })
428
- assert.equal(d.requestedExtensions.length, 20)
429
- assert.equal(d.enabledExtensions.length + d.blockedExtensions.length, 20)
435
+ assert.equal(d.requestedExtensions.length, 19)
436
+ assert.equal(d.enabledExtensions.length + d.blockedExtensions.length, 19)
430
437
 
431
438
  // memory, web, web_search, web_fetch should be enabled
432
439
  assert.ok(d.enabledExtensions.includes('memory'))
@@ -20,11 +20,12 @@ test('capability policy balanced mode blocks destructive delete_file', () => {
20
20
 
21
21
  test('capability policy strict mode blocks execution/platform families', () => {
22
22
  const decision = resolveSessionToolPolicy(
23
- ['shell', 'manage_tasks', 'web_search', 'memory'],
23
+ ['shell', 'execute', 'manage_tasks', 'web_search', 'memory'],
24
24
  { capabilityPolicyMode: 'strict' },
25
25
  )
26
26
  assert.deepEqual(decision.enabledExtensions, ['web_search', 'memory'])
27
27
  assert.equal(decision.blockedExtensions.some((entry) => entry.tool === 'shell'), true)
28
+ assert.equal(decision.blockedExtensions.some((entry) => entry.tool === 'execute'), true)
28
29
  assert.equal(decision.blockedExtensions.some((entry) => entry.tool === 'manage_tasks'), true)
29
30
  })
30
31
 
@@ -1,5 +1,6 @@
1
1
  import type { AppSettings } from '@/types'
2
2
  import { dedup } from '@/lib/shared-utils'
3
+ import { canonicalizeExtensionId } from './tool-aliases'
3
4
 
4
5
  export type CapabilityPolicyMode = 'permissive' | 'balanced' | 'strict'
5
6
 
@@ -37,6 +38,7 @@ interface ToolDescriptor {
37
38
 
38
39
  const TOOL_DESCRIPTORS: Record<string, ToolDescriptor> = {
39
40
  shell: { categories: ['execution'], concreteTools: ['shell', 'execute_command'] },
41
+ execute: { categories: ['execution'], concreteTools: ['execute'] },
40
42
  process: { categories: ['execution'], concreteTools: ['process', 'process_tool'] },
41
43
  files: { categories: ['filesystem'], concreteTools: ['files', 'read_file', 'write_file', 'list_files', 'send_file'] },
42
44
  read_file: { categories: ['filesystem'], concreteTools: ['read_file'] },
@@ -57,10 +59,7 @@ const TOOL_DESCRIPTORS: Record<string, ToolDescriptor> = {
57
59
  opencode_cli: { categories: ['delegation', 'execution'], concreteTools: ['delegate_to_opencode_cli'] },
58
60
  gemini_cli: { categories: ['delegation', 'execution'], concreteTools: ['delegate_to_gemini_cli'] },
59
61
  memory: { categories: ['memory'], concreteTools: ['memory', 'memory_tool', 'memory_search', 'memory_get', 'memory_store', 'memory_update', 'context_status', 'context_summarize'] },
60
- // sandbox_exec/sandbox_list_runtimes routed through shell; git uses shell
61
62
  // http_request consolidated into web 'api' action — no separate descriptor
62
- canvas: { categories: ['filesystem'], concreteTools: ['canvas'] },
63
- wallet: { categories: ['outbound'], concreteTools: ['wallet', 'wallet_tool'] },
64
63
  monitor: { categories: ['execution'], concreteTools: ['monitor', 'monitor_tool'] },
65
64
  openclaw_workspace: { categories: ['filesystem', 'platform'], concreteTools: ['openclaw_workspace'] },
66
65
  openclaw_nodes: { categories: ['platform'], concreteTools: ['openclaw_nodes'] },
@@ -118,6 +117,55 @@ function normalizeList(value: unknown): string[] {
118
117
  return dedup(names)
119
118
  }
120
119
 
120
+ function getDescriptor(toolName: string): ToolDescriptor | undefined {
121
+ const normalized = normalizeName(toolName)
122
+ if (!normalized) return undefined
123
+ return TOOL_DESCRIPTORS[normalized] || TOOL_DESCRIPTORS[normalizeName(canonicalizeExtensionId(normalized))]
124
+ }
125
+
126
+ function addComparableName(names: Set<string>, value: string | null | undefined): void {
127
+ const normalized = normalizeName(value)
128
+ if (!normalized) return
129
+ names.add(normalized)
130
+ const canonical = normalizeName(canonicalizeExtensionId(normalized))
131
+ if (canonical) names.add(canonical)
132
+ for (const mappedTool of CONCRETE_TOOL_TO_SESSION_TOOLS.get(normalized) || []) {
133
+ names.add(mappedTool)
134
+ }
135
+ }
136
+
137
+ function collectRequestedExtensionNames(toolName: string, descriptor?: ToolDescriptor): string[] {
138
+ const names = new Set<string>()
139
+ addComparableName(names, toolName)
140
+ for (const concreteName of descriptor?.concreteTools || []) {
141
+ addComparableName(names, concreteName)
142
+ }
143
+ return Array.from(names)
144
+ }
145
+
146
+ function entryMatchesSessionTool(entry: string, sessionTool: string): boolean {
147
+ const normalizedEntry = normalizeName(entry)
148
+ const normalizedTool = normalizeName(sessionTool)
149
+ if (!normalizedEntry || !normalizedTool) return false
150
+ if (normalizedEntry === normalizedTool) return true
151
+ if (!CONCRETE_TOOL_TO_SESSION_TOOLS.has(normalizedEntry)) {
152
+ return normalizeName(canonicalizeExtensionId(normalizedEntry)) === normalizedTool
153
+ }
154
+ return false
155
+ }
156
+
157
+ function matchesConcreteToolSetting(configuredNames: Set<string>, concreteToolName: string): boolean {
158
+ const normalizedName = normalizeName(concreteToolName)
159
+ if (!normalizedName || configuredNames.size === 0) return false
160
+ if (configuredNames.has(normalizedName)) return true
161
+ for (const sessionTool of CONCRETE_TOOL_TO_SESSION_TOOLS.get(normalizedName) || []) {
162
+ for (const entry of configuredNames) {
163
+ if (entryMatchesSessionTool(entry, sessionTool)) return true
164
+ }
165
+ }
166
+ return false
167
+ }
168
+
121
169
  function getSettingsList(settings: Record<string, unknown>, key: string): string[] {
122
170
  return normalizeList(settings[key])
123
171
  }
@@ -138,29 +186,11 @@ function modeBlocksTool(mode: CapabilityPolicyMode, toolName: string, descriptor
138
186
  }
139
187
 
140
188
  function safetyMatchesTool(safetyBlocked: Set<string>, toolName: string, descriptor?: ToolDescriptor): boolean {
141
- if (safetyBlocked.has(toolName)) return true
142
- if (!descriptor) return false
143
- for (const concreteName of descriptor.concreteTools) {
144
- if (safetyBlocked.has(concreteName)) return true
145
- }
146
- if (toolName === 'memory' && safetyBlocked.has('memory_tool')) return true
147
- if (toolName === 'manage_connectors' && safetyBlocked.has('connector_message_tool')) return true
148
- if (toolName === 'manage_sessions' && (
149
- safetyBlocked.has('sessions_tool')
150
- || safetyBlocked.has('search_history_tool')
151
- || safetyBlocked.has('whoami_tool')
152
- )) return true
153
- if (toolName === 'claude_code' && safetyBlocked.has('delegate_to_claude_code')) return true
154
- if (toolName === 'codex_cli' && safetyBlocked.has('delegate_to_codex_cli')) return true
155
- if (toolName === 'opencode_cli' && safetyBlocked.has('delegate_to_opencode_cli')) return true
156
- if (toolName === 'gemini_cli' && safetyBlocked.has('delegate_to_gemini_cli')) return true
157
- return false
189
+ return collectRequestedExtensionNames(toolName, descriptor).some((name) => safetyBlocked.has(name))
158
190
  }
159
191
 
160
192
  function policyMatchesTool(blockedNames: Set<string>, toolName: string, descriptor?: ToolDescriptor): boolean {
161
- if (blockedNames.has(toolName)) return true
162
- if (!descriptor) return false
163
- return descriptor.concreteTools.some((concreteName) => blockedNames.has(concreteName))
193
+ return collectRequestedExtensionNames(toolName, descriptor).some((name) => blockedNames.has(name))
164
194
  }
165
195
 
166
196
  function categoryBlockReason(blockedCategories: Set<string>, descriptor?: ToolDescriptor): string | null {
@@ -232,7 +262,7 @@ export function resolveSessionToolPolicy(
232
262
  const blockedExtensions: CapabilityPolicyBlock[] = []
233
263
 
234
264
  for (const extensionName of requestedExtensions) {
235
- const descriptor = TOOL_DESCRIPTORS[extensionName]
265
+ const descriptor = getDescriptor(extensionName)
236
266
  const settingsReason = settingsBlockReason(extensionName, normalizedSettings)
237
267
 
238
268
  if (settingsReason) {
@@ -296,24 +326,19 @@ export function resolveConcreteToolPolicyBlock(
296
326
 
297
327
  if (settingsReason) return settingsReason
298
328
 
299
- if (safetyBlocked.has(name)) return 'blocked by safety policy'
300
-
301
329
  const mappedTools = CONCRETE_TOOL_TO_SESSION_TOOLS.get(name) || []
302
- const safetyBlockedFamily = mappedTools.find((tool) => safetyBlocked.has(tool))
303
- if (safetyBlockedFamily) return `blocked because "${safetyBlockedFamily}" is safety-blocked`
304
-
305
- if (policyBlockedNames.has(name)) return 'blocked by explicit policy rule'
306
- const policyBlockedFamily = mappedTools.find((tool) => policyBlockedNames.has(tool) && !policyAllowedNames.has(tool))
307
- if (policyBlockedFamily) {
308
- return `blocked because "${policyBlockedFamily}" is policy-blocked`
330
+ if (matchesConcreteToolSetting(safetyBlocked, name)) return 'blocked by safety policy'
331
+ const explicitlyAllowed = matchesConcreteToolSetting(policyAllowedNames, name)
332
+ if (matchesConcreteToolSetting(policyBlockedNames, name) && !explicitlyAllowed) {
333
+ return 'blocked by explicit policy rule'
309
334
  }
310
335
 
311
336
  if (mappedTools.length > 0) {
312
- const enabledRoot = mappedTools.find((tool) => decision.enabledExtensions.includes(tool))
337
+ const enabledRoot = mappedTools.find((tool) => decision.enabledExtensions.some((entry) => entryMatchesSessionTool(entry, tool)))
313
338
  if (enabledRoot) return null
314
339
 
315
340
  const blockedRoot = mappedTools
316
- .map((tool) => decision.blockedExtensions.find((entry) => entry.tool === tool))
341
+ .map((tool) => decision.blockedExtensions.find((entry) => entryMatchesSessionTool(entry.tool, tool)))
317
342
  .find(Boolean)
318
343
  if (blockedRoot) return blockedRoot.reason
319
344
 
@@ -13,8 +13,6 @@ export const TOOL_CAPABILITY = {
13
13
  deliveryMessage: 'delivery.message',
14
14
  deliveryMedia: 'delivery.media',
15
15
  deliveryVoiceNote: 'delivery.voice_note',
16
- walletInspect: 'wallet.inspect',
17
- walletExecute: 'wallet.execute',
18
16
  } as const
19
17
 
20
18
  export interface ToolPlanningEntry {
@@ -58,6 +56,17 @@ const CORE_TOOL_PLANNING: Record<string, LegacyToolPlanningEntry[]> = {
58
56
  requestMatchers: [],
59
57
  },
60
58
  ],
59
+ execute: [
60
+ {
61
+ toolName: 'execute',
62
+ capabilities: ['runtime.execute'],
63
+ disciplineGuidance: [
64
+ 'For `execute`, pass the full bash script in `{"code":"..."}`. Use it for sandboxed command execution, curl-based fetches, and one-shot scripts.',
65
+ 'Use `persistent=true` only when the agent is explicitly configured for host execution. Otherwise use `files` for persistent writes.',
66
+ ],
67
+ requestMatchers: [],
68
+ },
69
+ ],
61
70
  web: [
62
71
  {
63
72
  toolName: 'web_search',
@@ -362,16 +371,6 @@ const CORE_TOOL_PLANNING: Record<string, LegacyToolPlanningEntry[]> = {
362
371
  requestMatchers: [],
363
372
  },
364
373
  ],
365
- wallet: [
366
- {
367
- toolName: 'wallet_tool',
368
- capabilities: [TOOL_CAPABILITY.walletInspect, TOOL_CAPABILITY.walletExecute],
369
- disciplineGuidance: [
370
- 'Inspect wallet state once, then act. Use `simulate_transaction` to validate before executing. Do not re-inspect balances between each operation unless the operation changes them.',
371
- ],
372
- requestMatchers: [],
373
- },
374
- ],
375
374
  image_gen: [
376
375
  {
377
376
  toolName: 'generate_image',
@@ -22,7 +22,6 @@ const UNIVERSAL_CORE_EXTENSION_IDS = [
22
22
  'manage_secrets',
23
23
  'manage_chatrooms',
24
24
  'spawn_subagent',
25
- 'canvas',
26
25
  'http_request',
27
26
  'wallet',
28
27
  'monitor',
@@ -0,0 +1,33 @@
1
+ import { encryptKey, decryptKey } from '@/lib/server/storage'
2
+
3
+ /**
4
+ * Generate a new Ethereum wallet (Base L2 compatible) with an encrypted private key.
5
+ * Uses ethers v6 for keypair generation and the existing AES-256-GCM encryption
6
+ * from storage.ts (CREDENTIAL_SECRET env var).
7
+ */
8
+ export async function generateEthereumWallet(): Promise<{ address: string; encryptedPrivateKey: string }> {
9
+ const { Wallet } = await import('ethers')
10
+ const wallet = Wallet.createRandom()
11
+ const encryptedPrivateKey = encryptKey(wallet.privateKey)
12
+ return {
13
+ address: wallet.address,
14
+ encryptedPrivateKey,
15
+ }
16
+ }
17
+
18
+ export async function normalizeEthereumAddress(address: string): Promise<string | null> {
19
+ const { getAddress } = await import('ethers')
20
+ try {
21
+ return getAddress(address.trim())
22
+ } catch {
23
+ return null
24
+ }
25
+ }
26
+
27
+ /**
28
+ * Decrypt a wallet's private key for server-side use only.
29
+ * Never expose the result to API responses or the frontend.
30
+ */
31
+ export function decryptWalletPrivateKey(encrypted: string): string {
32
+ return decryptKey(encrypted)
33
+ }
@@ -0,0 +1,24 @@
1
+ import {
2
+ loadWallets as loadWalletsStore,
3
+ saveWallets as saveWalletsStore,
4
+ loadWallet as loadWalletItem,
5
+ upsertWallet,
6
+ deleteWalletItem,
7
+ } from '@/lib/server/storage'
8
+ import type { AgentWallet } from '@/types/swarmdock'
9
+
10
+ export function loadWallets(): Record<string, AgentWallet> {
11
+ return loadWalletsStore() as Record<string, AgentWallet>
12
+ }
13
+
14
+ export function loadWallet(id: string): AgentWallet | null {
15
+ return loadWalletItem(id) as AgentWallet | null
16
+ }
17
+
18
+ export function saveWallet(id: string, wallet: AgentWallet): void {
19
+ upsertWallet(id, wallet)
20
+ }
21
+
22
+ export function deleteWallet(id: string): void {
23
+ deleteWalletItem(id)
24
+ }