@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
@@ -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()
@@ -22,7 +22,6 @@ const EXTENSION_ALIAS_GROUPS: string[][] = [
22
22
  ['schedule_wake', 'schedule'],
23
23
  // http_request/http now aliased into web group above
24
24
  ['memory', 'memory_tool', 'memory_search', 'memory_get', 'memory_store', 'memory_update'],
25
- ['wallet', 'wallet_tool'],
26
25
  ['monitor', 'monitor_tool'],
27
26
  ['context_mgmt', 'context_status', 'context_summarize'],
28
27
  ['openclaw_workspace'],
@@ -423,17 +423,17 @@ describe('compound scenarios', () => {
423
423
  assert.match(tasksBlock.reason, /task management is disabled/)
424
424
  })
425
425
 
426
- it('20 tools requested: correctly partitioned into enabled vs blocked', () => {
426
+ it('19 tools requested: correctly partitioned into enabled vs blocked', () => {
427
427
  const tools = [
428
428
  'shell', 'files', 'web', 'web_search', 'web_fetch', 'browser',
429
429
  'memory', 'delegate', 'manage_platform', 'manage_tasks',
430
- 'manage_schedules', 'wallet', 'delete_file', 'canvas',
430
+ 'manage_schedules', 'wallet', 'delete_file',
431
431
  'manage_connectors', 'git', 'sandbox', 'claude_code',
432
432
  'monitor', 'http_request',
433
433
  ]
434
434
  const d = resolveSessionToolPolicy(tools, { capabilityPolicyMode: 'strict' })
435
- assert.equal(d.requestedExtensions.length, 20)
436
- 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)
437
437
 
438
438
  // memory, web, web_search, web_fetch should be enabled
439
439
  assert.ok(d.enabledExtensions.includes('memory'))
@@ -60,8 +60,6 @@ const TOOL_DESCRIPTORS: Record<string, ToolDescriptor> = {
60
60
  gemini_cli: { categories: ['delegation', 'execution'], concreteTools: ['delegate_to_gemini_cli'] },
61
61
  memory: { categories: ['memory'], concreteTools: ['memory', 'memory_tool', 'memory_search', 'memory_get', 'memory_store', 'memory_update', 'context_status', 'context_summarize'] },
62
62
  // http_request consolidated into web 'api' action — no separate descriptor
63
- canvas: { categories: ['filesystem'], concreteTools: ['canvas'] },
64
- wallet: { categories: ['outbound'], concreteTools: ['wallet', 'wallet_tool'] },
65
63
  monitor: { categories: ['execution'], concreteTools: ['monitor', 'monitor_tool'] },
66
64
  openclaw_workspace: { categories: ['filesystem', 'platform'], concreteTools: ['openclaw_workspace'] },
67
65
  openclaw_nodes: { categories: ['platform'], concreteTools: ['openclaw_nodes'] },
@@ -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 {
@@ -373,16 +371,6 @@ const CORE_TOOL_PLANNING: Record<string, LegacyToolPlanningEntry[]> = {
373
371
  requestMatchers: [],
374
372
  },
375
373
  ],
376
- wallet: [
377
- {
378
- toolName: 'wallet_tool',
379
- capabilities: [TOOL_CAPABILITY.walletInspect, TOOL_CAPABILITY.walletExecute],
380
- disciplineGuidance: [
381
- '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.',
382
- ],
383
- requestMatchers: [],
384
- },
385
- ],
386
374
  image_gen: [
387
375
  {
388
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
+ }
@@ -0,0 +1,119 @@
1
+ import { genId } from '@/lib/id'
2
+ import { loadWallets, loadWallet, saveWallet, deleteWallet } from './wallet-repository'
3
+ import { generateEthereumWallet, normalizeEthereumAddress } from './wallet-crypto'
4
+ import { logActivity } from '@/lib/server/activity/activity-log'
5
+ import { loadAgent } from '@/lib/server/agents/agent-repository'
6
+ import type { AgentWallet, SafeWallet } from '@/types/swarmdock'
7
+
8
+ export class WalletServiceError extends Error {
9
+ status: number
10
+
11
+ constructor(status: number, message: string) {
12
+ super(message)
13
+ this.name = 'WalletServiceError'
14
+ this.status = status
15
+ }
16
+ }
17
+
18
+ function stripPrivateKey(wallet: AgentWallet): SafeWallet {
19
+ const { encryptedPrivateKey: _, ...safe } = wallet
20
+ return safe
21
+ }
22
+
23
+ function requireAgent(agentId: string): string {
24
+ const normalizedAgentId = agentId.trim()
25
+ if (!normalizedAgentId) {
26
+ throw new WalletServiceError(400, 'agentId is required')
27
+ }
28
+ if (!loadAgent(normalizedAgentId)) {
29
+ throw new WalletServiceError(404, `Agent not found: ${normalizedAgentId}`)
30
+ }
31
+ return normalizedAgentId
32
+ }
33
+
34
+ async function requireWalletAddress(walletAddress: string): Promise<string> {
35
+ const normalizedAddress = walletAddress.trim()
36
+ if (!normalizedAddress) {
37
+ throw new WalletServiceError(400, 'walletAddress is required')
38
+ }
39
+ const checksumAddress = await normalizeEthereumAddress(normalizedAddress)
40
+ if (!checksumAddress) {
41
+ throw new WalletServiceError(400, 'walletAddress must be a valid Base/Ethereum address')
42
+ }
43
+ return checksumAddress
44
+ }
45
+
46
+ export function listWalletsSafe(): Record<string, SafeWallet> {
47
+ const wallets = loadWallets()
48
+ const safe: Record<string, SafeWallet> = {}
49
+ for (const [id, w] of Object.entries(wallets)) {
50
+ safe[id] = stripPrivateKey(w)
51
+ }
52
+ return safe
53
+ }
54
+
55
+ export function getWalletSafe(id: string): SafeWallet | null {
56
+ const wallet = loadWallet(id)
57
+ return wallet ? stripPrivateKey(wallet) : null
58
+ }
59
+
60
+ /** Generate a new Base L2 Ethereum wallet with encrypted private key storage. */
61
+ export async function generateWallet(params: { agentId: string; label?: string }): Promise<SafeWallet> {
62
+ const agentId = requireAgent(params.agentId)
63
+ const { address, encryptedPrivateKey } = await generateEthereumWallet()
64
+ const id = genId()
65
+ const wallet: AgentWallet = {
66
+ id,
67
+ agentId,
68
+ walletAddress: address,
69
+ chain: 'base',
70
+ label: params.label || 'Base L2 Wallet',
71
+ encryptedPrivateKey,
72
+ requireApproval: true,
73
+ createdAt: Date.now(),
74
+ }
75
+ saveWallet(id, wallet)
76
+ logActivity({ entityType: 'wallet', entityId: id, action: 'created', actor: 'user', summary: `Wallet generated: ${address}` })
77
+ return stripPrivateKey(wallet)
78
+ }
79
+
80
+ /** Create a wallet from an existing address (no key generation). */
81
+ export async function createWallet(params: { agentId: string; walletAddress: string; label?: string }): Promise<SafeWallet> {
82
+ const agentId = requireAgent(params.agentId)
83
+ const walletAddress = await requireWalletAddress(params.walletAddress)
84
+ const id = genId()
85
+ const wallet: AgentWallet = {
86
+ id,
87
+ agentId,
88
+ walletAddress,
89
+ chain: 'base',
90
+ label: params.label,
91
+ createdAt: Date.now(),
92
+ }
93
+ saveWallet(id, wallet)
94
+ logActivity({ entityType: 'wallet', entityId: id, action: 'created', actor: 'user', summary: `Wallet created: ${walletAddress}` })
95
+ return stripPrivateKey(wallet)
96
+ }
97
+
98
+ /** Update wallet settings (label, limits, approval). */
99
+ export function updateWallet(
100
+ id: string,
101
+ patch: Partial<Pick<AgentWallet, 'label' | 'spendingLimitUsdc' | 'dailyLimitUsdc' | 'requireApproval'>>,
102
+ ): SafeWallet | null {
103
+ const wallet = loadWallet(id)
104
+ if (!wallet) return null
105
+ if (patch.label !== undefined) wallet.label = patch.label
106
+ if (patch.spendingLimitUsdc !== undefined) wallet.spendingLimitUsdc = patch.spendingLimitUsdc
107
+ if (patch.dailyLimitUsdc !== undefined) wallet.dailyLimitUsdc = patch.dailyLimitUsdc
108
+ if (patch.requireApproval !== undefined) wallet.requireApproval = patch.requireApproval
109
+ saveWallet(id, wallet)
110
+ return stripPrivateKey(wallet)
111
+ }
112
+
113
+ export function removeWallet(id: string): boolean {
114
+ const wallet = loadWallet(id)
115
+ if (!wallet) return false
116
+ deleteWallet(id)
117
+ logActivity({ entityType: 'wallet', entityId: id, action: 'deleted', actor: 'user', summary: `Wallet deleted: ${wallet.walletAddress}` })
118
+ return true
119
+ }
@@ -22,7 +22,6 @@ import type {
22
22
  import {
23
23
  EXTRACTION_TIMEOUT_MS,
24
24
  WorkingStatePatchSchema,
25
- missionStatusToWorkingStateStatus,
26
25
  normalizeEvidenceIds,
27
26
  now,
28
27
  } from '@/lib/server/working-state/normalization'
@@ -268,9 +267,9 @@ export function buildWorkingStatePatchPrompt(input: WorkingStateExtractionInput)
268
267
  'You maintain a structured working-state object for an autonomous agent.',
269
268
  'Return JSON only.',
270
269
  '',
271
- 'Update the state using only evidence from the latest turn, tool results, and mission snapshot.',
270
+ 'Update the state using only evidence from the latest turn and tool results.',
272
271
  'Rules:',
273
- '- Facts must be confirmed by explicit user text, mission state, or tool evidence. Do not turn guesses into facts.',
272
+ '- Facts must be confirmed by explicit user text or tool evidence. Do not turn guesses into facts.',
274
273
  '- Put uncertain leads into hypotheses, not facts.',
275
274
  '- Use blockers for approvals, credentials, human input, external waits, and explicit execution failures.',
276
275
  '- nextAction must be one concrete immediate action, not a broad plan.',
@@ -288,7 +287,7 @@ export function buildWorkingStatePatchPrompt(input: WorkingStateExtractionInput)
288
287
  status: 'idle|progress|blocked|waiting|completed',
289
288
  nextAction: 'optional',
290
289
  planSteps: [{ id: 'optional', text: 'step', status: 'active|resolved|superseded' }],
291
- factsUpsert: [{ id: 'optional', statement: 'confirmed fact', source: 'user|tool|assistant|mission|system', status: 'active|resolved|superseded', evidenceIds: ['optional'] }],
290
+ factsUpsert: [{ id: 'optional', statement: 'confirmed fact', source: 'user|tool|assistant|system', status: 'active|resolved|superseded', evidenceIds: ['optional'] }],
292
291
  artifactsUpsert: [{ id: 'optional', label: 'artifact', kind: 'file|url|approval|message|other', path: 'optional', url: 'optional', sourceTool: 'optional', status: 'active|resolved|superseded', evidenceIds: ['optional'] }],
293
292
  decisionsAppend: [{ summary: 'decision', rationale: 'optional', status: 'active|resolved|superseded', evidenceIds: ['optional'] }],
294
293
  blockersUpsert: [{ summary: 'blocker', kind: 'approval|credential|human_input|external_dependency|error|other', nextAction: 'optional', status: 'active|resolved|superseded', evidenceIds: ['optional'] }],
@@ -298,21 +297,6 @@ export function buildWorkingStatePatchPrompt(input: WorkingStateExtractionInput)
298
297
  }),
299
298
  '',
300
299
  `source: ${JSON.stringify(cleanText(input.source, 80) || 'chat')}`,
301
- `mission: ${JSON.stringify(input.mission ? {
302
- id: input.mission.id,
303
- objective: cleanMultiline(input.mission.objective, 600),
304
- status: input.mission.status,
305
- phase: input.mission.phase,
306
- currentStep: cleanText(input.mission.currentStep, 240) || null,
307
- plannerSummary: cleanText(input.mission.plannerSummary, 280) || null,
308
- verifierSummary: cleanText(input.mission.verifierSummary, 280) || null,
309
- blockerSummary: cleanText(input.mission.blockerSummary, 240) || null,
310
- waitState: input.mission.waitState ? {
311
- kind: input.mission.waitState.kind,
312
- reason: cleanText(input.mission.waitState.reason, 240),
313
- approvalId: cleanText(input.mission.waitState.approvalId, 120) || null,
314
- } : null,
315
- } : null)}`,
316
300
  `current_state:\n${renderStateForExtraction(input.currentState)}`,
317
301
  `user_message: ${JSON.stringify(cleanMultiline(input.message, 1200) || null)}`,
318
302
  `assistant_text: ${JSON.stringify(cleanMultiline(input.assistantText, 1200) || null)}`,
@@ -398,7 +382,6 @@ export function deterministicEvidencePatch(input: WorkingStateDeterministicUpdat
398
382
  value: input.runId,
399
383
  runId: input.runId,
400
384
  sessionId: input.sessionId,
401
- missionId: input.mission?.id || null,
402
385
  createdAt: nowTs,
403
386
  })
404
387
  }
@@ -420,7 +403,6 @@ export function deterministicEvidencePatch(input: WorkingStateDeterministicUpdat
420
403
  toolCallId: cleanText(event.toolCallId, 120) || null,
421
404
  runId: input.runId || null,
422
405
  sessionId: input.sessionId,
423
- missionId: input.mission?.id || null,
424
406
  createdAt: nowTs + index,
425
407
  })
426
408
 
@@ -428,7 +410,7 @@ export function deterministicEvidencePatch(input: WorkingStateDeterministicUpdat
428
410
  blockersUpsert.push({
429
411
  summary: output || `Tool ${toolName} failed.`,
430
412
  kind: 'error',
431
- nextAction: cleanText(input.mission?.currentStep, 240) || null,
413
+ nextAction: null,
432
414
  status: 'active',
433
415
  evidenceIds: [evidenceId],
434
416
  })
@@ -521,7 +503,6 @@ export function deterministicEvidencePatch(input: WorkingStateDeterministicUpdat
521
503
  value: cleanText(input.error, 240) || null,
522
504
  runId: input.runId || null,
523
505
  sessionId: input.sessionId,
524
- missionId: input.mission?.id || null,
525
506
  createdAt: nowTs + 100,
526
507
  })
527
508
  blockersUpsert.push({
@@ -531,26 +512,11 @@ export function deterministicEvidencePatch(input: WorkingStateDeterministicUpdat
531
512
  })
532
513
  }
533
514
 
534
- if (input.mission) {
535
- evidenceAppend.push({
536
- id: genId(12),
537
- type: 'mission',
538
- summary: `Mission state updated: ${input.mission.status}/${input.mission.phase}.`,
539
- value: cleanText(input.mission.currentStep || input.mission.objective, 240) || null,
540
- runId: input.runId || null,
541
- sessionId: input.sessionId,
542
- missionId: input.mission.id,
543
- createdAt: nowTs + 200,
544
- })
545
- }
546
-
547
515
  return {
548
516
  status: input.error
549
517
  ? 'blocked'
550
- : input.mission
551
- ? missionStatusToWorkingStateStatus(input.mission)
552
- : undefined,
553
- nextAction: cleanText(input.mission?.currentStep, 240) || undefined,
518
+ : undefined,
519
+ nextAction: undefined,
554
520
  factsUpsert: factsUpsert.length > 0 ? factsUpsert : undefined,
555
521
  artifactsUpsert: artifactsUpsert.length > 0 ? artifactsUpsert : undefined,
556
522
  blockersUpsert: blockersUpsert.length > 0 ? blockersUpsert : undefined,
@@ -608,7 +574,7 @@ export function shouldExtractStructuredPatch(input: SynchronizeWorkingStateForTu
608
574
  const hasAssistantText = cleanMultiline(input.assistantText, 400).length > 0
609
575
  const hasError = cleanText(input.error, 120).length > 0
610
576
  if (cleanText(input.source, 80) === 'heartbeat') {
611
- return hasToolEvents || hasError || Boolean(input.mission)
577
+ return hasToolEvents || hasError
612
578
  }
613
- return hasToolEvents || hasAssistantText || hasMessage || hasError || Boolean(input.mission)
579
+ return hasToolEvents || hasAssistantText || hasMessage || hasError
614
580
  }