@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
@@ -16,7 +16,6 @@ import { errorMessage } from '@/lib/shared-utils'
16
16
  import { getAgents } from '@/lib/server/agents/agent-repository'
17
17
  import { upsertTask } from '@/lib/server/tasks/task-repository'
18
18
  import { notify } from '@/lib/server/ws-hub'
19
- import { ensureMissionForTask } from '@/lib/server/missions/mission-service'
20
19
  import { enqueueTask } from '@/lib/server/runtime/queue'
21
20
  import { cleanText, isDiscussionStepKind, now, uniqueIds } from '@/lib/server/protocols/protocol-types'
22
21
  import type { ProtocolRunDeps } from '@/lib/server/protocols/protocol-types'
@@ -227,7 +226,6 @@ export async function processEmitTasksPhase(run: ProtocolRun, phase: ProtocolPha
227
226
  ...extracted.map((item) => item.agentId || ''),
228
227
  ], 64))
229
228
  const createdTaskIds: string[] = []
230
- const linkedMissionId = typeof run.missionId === 'string' ? run.missionId : null
231
229
  const taskProjectId = run.config?.taskProjectId || null
232
230
  for (const item of extracted) {
233
231
  const assignedAgentId = item.agentId && agents[item.agentId] ? item.agentId : fallbackAssignee
@@ -238,7 +236,6 @@ export async function processEmitTasksPhase(run: ProtocolRun, phase: ProtocolPha
238
236
  description: cleanText(item.description, 1_000) || cleanText(artifact.content, 800),
239
237
  status: 'backlog',
240
238
  agentId: assignedAgentId,
241
- missionId: linkedMissionId,
242
239
  projectId: taskProjectId || undefined,
243
240
  createdByAgentId: chooseFacilitator(run),
244
241
  createdInSessionId: run.sessionId || null,
@@ -248,7 +245,6 @@ export async function processEmitTasksPhase(run: ProtocolRun, phase: ProtocolPha
248
245
  tags: ['structured-session'],
249
246
  }
250
247
  upsertTask(task.id, task)
251
- ensureMissionForTask(task, { source: 'manual' })
252
248
  createdTaskIds.push(task.id)
253
249
  appendProtocolEvent(run.id, {
254
250
  type: 'task_emitted',
@@ -607,7 +603,6 @@ export function processDispatchTaskPhase(run: ProtocolRun, phase: ProtocolPhaseD
607
603
  status: 'queued',
608
604
  agentId,
609
605
  protocolRunId: run.id,
610
- missionId: run.missionId || null,
611
606
  queuedAt: now(deps),
612
607
  createdAt: now(deps),
613
608
  updatedAt: now(deps),
@@ -656,7 +651,6 @@ export function processDispatchDelegationPhase(run: ProtocolRun, phase: Protocol
656
651
  status: 'queued',
657
652
  agentId: config.agentId,
658
653
  protocolRunId: run.id,
659
- missionId: run.missionId || null,
660
654
  sourceType: 'delegation',
661
655
  queuedAt: now(deps),
662
656
  createdAt: now(deps),
@@ -90,7 +90,6 @@ export async function processSwarmStep(run: ProtocolRun, step: ProtocolStepDefin
90
90
  status: 'queued',
91
91
  agentId,
92
92
  protocolRunId: started.id,
93
- missionId: started.missionId || null,
94
93
  sourceType: 'delegation',
95
94
  queuedAt: now(deps),
96
95
  createdAt: now(deps),
@@ -189,7 +188,6 @@ export function claimSwarmWorkItem(
189
188
  status: 'queued',
190
189
  agentId,
191
190
  protocolRunId: runId,
192
- missionId: run.missionId || null,
193
191
  sourceType: 'delegation',
194
192
  queuedAt: now(deps),
195
193
  createdAt: now(deps),
@@ -6,7 +6,6 @@ import type {
6
6
  BoardTask,
7
7
  Chatroom,
8
8
  MessageToolEvent,
9
- Mission,
10
9
  ProtocolBranchCase,
11
10
  ProtocolPhaseDefinition,
12
11
  ProtocolRepeatConfig,
@@ -31,7 +30,6 @@ export interface ProtocolRunDetail {
31
30
  template: ProtocolTemplate | null
32
31
  transcript: Chatroom | null
33
32
  parentChatroom: Chatroom | null
34
- linkedMission: Mission | null
35
33
  linkedTask: BoardTask | null
36
34
  events: ProtocolRunEvent[]
37
35
  }
@@ -92,15 +92,6 @@ export function markProviderSuccess(providerId: string, credentialId?: string |
92
92
  const { upsertStoredItem } = require('@/lib/server/storage')
93
93
  upsertStoredItem('provider_health', key, states.get(key)!)
94
94
  } catch {}
95
- queueMicrotask(() => {
96
- import('@/lib/server/missions/mission-service')
97
- .then(({ requestMissionTicksForProviderRecovery }) => {
98
- requestMissionTicksForProviderRecovery(providerId)
99
- })
100
- .catch(() => {
101
- // Mission recovery is best-effort only.
102
- })
103
- })
104
95
  }
105
96
 
106
97
  export function isProviderCoolingDown(providerId: string, credentialId?: string | null): boolean {
@@ -66,7 +66,6 @@ import { loadEstopState } from '@/lib/server/runtime/estop'
66
66
  import { classifyRuntimeFailure, recordSupervisorIncident } from '@/lib/server/autonomy/supervisor-reflection'
67
67
  import { getMemoryDb } from '@/lib/server/memory/memory-db'
68
68
  import { clearLogsByAge } from '@/lib/server/execution-log'
69
- import { runMissionControllerStartupRecovery } from '@/lib/server/missions/mission-service'
70
69
 
71
70
  const TAG = 'daemon-state'
72
71
 
@@ -293,14 +292,6 @@ export function startDaemon(options?: { source?: string; manualStart?: boolean }
293
292
  if (lost > 0) log.info(TAG, `[daemon] Marked ${lost} in-flight swarm(s) as lost after restart`)
294
293
  } catch { /* best-effort */ }
295
294
  resumeQueue()
296
- const missionRecovery = runMissionControllerStartupRecovery()
297
- if (missionRecovery.recovered > 0 || missionRecovery.rerunVerification > 0) {
298
- log.info(
299
- TAG,
300
- `[daemon] Recovered ${missionRecovery.recovered} mission(s) on startup`
301
- + ` (${missionRecovery.rerunVerification} queued for verification replay)`,
302
- )
303
- }
304
295
  startScheduler()
305
296
  startQueueProcessor()
306
297
  startBrowserSweep()
@@ -245,41 +245,6 @@ describe('daemon start/stop lifecycle', () => {
245
245
  }
246
246
  })
247
247
 
248
- it('startDaemon runs mission startup recovery once the daemon owns startup', async () => {
249
- const storage = await import('@/lib/server/storage')
250
- storage.saveMissions({
251
- missionA: {
252
- id: 'missionA',
253
- source: 'chat',
254
- sourceRef: { kind: 'chat', sessionId: 'sessionA' },
255
- objective: 'Recover after daemon start',
256
- status: 'active',
257
- phase: 'executing',
258
- sessionId: 'sessionA',
259
- taskIds: [],
260
- controllerState: {
261
- activeRunId: 'run-stale',
262
- currentTaskId: 'task-stale',
263
- },
264
- createdAt: 1,
265
- updatedAt: 1,
266
- },
267
- })
268
-
269
- await mod.stopDaemon({ source: 'test-prestart' })
270
- mod.startDaemon({ source: 'test-mission-recovery', manualStart: true })
271
- try {
272
- const missions = await import('@/lib/server/missions/mission-service')
273
- const mission = missions.loadMissionById('missionA')
274
- assert.equal(mission?.status, 'active')
275
- assert.equal(mission?.phase, 'planning')
276
- const events = missions.listMissionEventsForMission('missionA')
277
- assert.ok(events.some((event) => event.type === 'interrupted'))
278
- } finally {
279
- await mod.stopDaemon({ source: 'test-mission-recovery' })
280
- }
281
- })
282
-
283
248
  it('stopDaemon sets running to false', async () => {
284
249
  mod.startDaemon({ source: 'test', manualStart: true })
285
250
  await mod.stopDaemon({ source: 'test' })
@@ -10,17 +10,15 @@ import { logActivity } from '@/lib/server/activity/activity-log'
10
10
  import { loadApprovals } from '@/lib/server/approvals/approval-repository'
11
11
  import { loadAgents, patchAgent } from '@/lib/server/agents/agent-repository'
12
12
  import { loadChatrooms } from '@/lib/server/chatrooms/chatroom-repository'
13
- import { loadMission } from '@/lib/server/missions/mission-repository'
14
13
  import { loadSessions, patchSession } from '@/lib/server/sessions/session-repository'
15
14
  import { loadSettings } from '@/lib/server/settings/settings-repository'
16
- import { buildGoalAncestrySection, buildPlatformStatusSummary } from '@/lib/server/chat-execution/situational-awareness'
15
+ import { buildPlatformStatusSummary } from '@/lib/server/chat-execution/situational-awareness'
17
16
  import { drainDeferredWakes, hasDeferredWakes } from '@/lib/server/runtime/wake-dispatcher'
18
17
  import { buildWakeTriggerContext } from '@/lib/server/runtime/heartbeat-wake'
19
18
  import { enqueueSessionRun, getSessionRunState } from '@/lib/server/runtime/session-run-manager'
20
19
  import { log } from '@/lib/server/logger'
21
20
  import { WORKSPACE_DIR } from '@/lib/server/data-dir'
22
21
  import { drainSystemEvents, drainOrchestratorEvents } from '@/lib/server/runtime/system-events'
23
- import { buildMissionContextBlock } from '@/lib/server/missions/mission-service'
24
22
  import { getMessages, getRecentMessages, clearMessages } from '@/lib/server/messages/message-repository'
25
23
  import type { Agent, AppSettings, ApprovalRequest, Chatroom, Message, Session } from '@/types'
26
24
  import { isOrchestratorEligible } from '@/lib/orchestrator-config'
@@ -357,12 +355,7 @@ export function buildAgentHeartbeatPrompt(
357
355
  }
358
356
  }
359
357
 
360
- // ── Phase 3: Goal ancestry ──
361
- const missionId = (session.missionId || (agent as Record<string, unknown>).missionId || null) as string | null
362
- const goalAncestry = buildGoalAncestrySection(missionId)
363
- if (goalAncestry) sections.push(goalAncestry)
364
-
365
- // ── Phase 4: Active task checkout & events ──
358
+ // ── Phase 3: Active task checkout & events ──
366
359
  const events = drainSystemEvents(session.id!)
367
360
  if (events.length > 0) {
368
361
  const eventBlock = events.map((e) => `- [${new Date(e.timestamp).toISOString()}] ${e.text}`).join('\n')
@@ -983,20 +976,7 @@ export function buildOrchestratorWakePrompt(session: any, agent: Agent): string
983
976
  addSection(`## System Events\n${eventBlock}`)
984
977
  }
985
978
 
986
- // 7. Active mission state
987
- const missionId = session.missionId || null
988
- if (missionId) {
989
- try {
990
- const missionBlock = buildMissionContextBlock(loadMission(missionId))
991
- if (missionBlock) addSection(missionBlock)
992
- } catch { /* ignore */ }
993
- }
994
-
995
- // 8. Goal ancestry
996
- const goalAncestry = buildGoalAncestrySection(missionId)
997
- if (goalAncestry) addSection(goalAncestry)
998
-
999
- // 9. Chatroom membership
979
+ // 7. Chatroom membership
1000
980
  try {
1001
981
  const chatrooms = Object.values(loadChatrooms()) as Chatroom[]
1002
982
  const myChatrooms = chatrooms.filter((c) => !c.archivedAt && c.agentIds?.includes(agent.id))
@@ -23,6 +23,7 @@ import type { ExecuteChatTurnResult } from '@/lib/server/chat-execution/chat-exe
23
23
  import { checkAgentBudgetLimits } from '@/lib/server/cost'
24
24
  import { enqueueExecution } from '@/lib/server/execution-engine'
25
25
  import { extractTaskResult, formatResultBody } from '@/lib/server/tasks/task-result'
26
+ import { checkoutTask } from '@/lib/server/tasks/task-checkout'
26
27
  import {
27
28
  classifyRuntimeFailure,
28
29
  observeAutonomyRunOutcome,
@@ -51,7 +52,6 @@ import {
51
52
  markValidatedTaskCompleted,
52
53
  refreshTaskCompletionValidation,
53
54
  } from '@/lib/server/tasks/task-lifecycle'
54
- import { noteMissionTaskFinished, noteMissionTaskStarted } from '@/lib/server/missions/mission-service'
55
55
 
56
56
  const TAG = 'queue'
57
57
 
@@ -1210,25 +1210,16 @@ export async function processNext() {
1210
1210
  } catch {}
1211
1211
  }
1212
1212
 
1213
- const beforeStartTasks = loadTasks() as Record<string, BoardTask>
1214
- task = beforeStartTasks[taskId] as BoardTask | undefined
1215
- if (!task || task.status !== 'queued') {
1216
- return
1217
- }
1218
-
1219
- // Mark as running
1213
+ // Atomic checkout prevents two runners from starting the same task
1214
+ const runId = genId()
1215
+ task = checkoutTask(taskId, runId) as BoardTask | undefined
1216
+ if (!task) return
1220
1217
  applyTaskPolicyDefaults(task)
1221
- task.status = 'running'
1222
- task.startedAt = Date.now()
1223
- task.lastActivityAt = Date.now()
1224
- task.retryScheduledAt = null
1225
- task.deadLetteredAt = null
1226
- // Clear transient failure fields so validation/error state reflects only this attempt.
1227
- task.error = null
1228
- task.validation = null
1229
- task.updatedAt = Date.now()
1230
1218
  logActivity({ entityType: 'task', entityId: taskId, action: 'running', actor: 'system', actorId: task.agentId, summary: `Task started: "${task.title}"` })
1231
1219
 
1220
+ // Reload tasks map for resolution functions and final save (checkoutTask already saved the running status)
1221
+ const allTasks = loadTasks() as Record<string, BoardTask>
1222
+ allTasks[taskId] = task
1232
1223
  const sessionsForCwd = loadSessions() as Record<string, SessionLike>
1233
1224
  const taskCwd = resolveTaskExecutionCwd(task as ScheduleTaskMeta, sessionsForCwd)
1234
1225
  task.cwd = taskCwd
@@ -1238,8 +1229,8 @@ export async function processNext() {
1238
1229
  const sourceScheduleId = typeof scheduleTask.sourceScheduleId === 'string'
1239
1230
  ? scheduleTask.sourceScheduleId
1240
1231
  : ''
1241
- const reusableTaskSessionId = resolveReusableTaskSessionId(task, beforeStartTasks, sessionsForCwd)
1242
- const resumeContext = resolveTaskResumeContext(task, beforeStartTasks, sessionsForCwd as Record<string, SessionLike | Session>)
1232
+ const reusableTaskSessionId = resolveReusableTaskSessionId(task, allTasks, sessionsForCwd)
1233
+ const resumeContext = resolveTaskResumeContext(task, allTasks, sessionsForCwd as Record<string, SessionLike | Session>)
1243
1234
 
1244
1235
  // Resolve the agent's persistent thread session to use as parentSessionId
1245
1236
  const agentThreadSessionId = agent.threadSessionId || null
@@ -1307,8 +1298,7 @@ export async function processNext() {
1307
1298
  note: `Attempt ${(task.attempts || 0) + 1}/${task.maxAttempts || '?'} started${continuationBits.length ? ` (${continuationBits.join('; ')})` : ''}`,
1308
1299
  updatedAt: Date.now(),
1309
1300
  }
1310
- saveTasks(beforeStartTasks)
1311
- noteMissionTaskStarted(task, task.id)
1301
+ saveTasks(allTasks)
1312
1302
  pushMainLoopEventToMainSessions({
1313
1303
  type: 'task_running',
1314
1304
  text: `Task running: "${task.title}" (${task.id}) with ${agent.name}`,
@@ -1485,13 +1475,6 @@ export async function processNext() {
1485
1475
  disableSessionHeartbeat(t2[taskId].sessionId)
1486
1476
  }
1487
1477
  const doneTask = t2[taskId]
1488
- if (doneTask?.status === 'completed') {
1489
- noteMissionTaskFinished(doneTask, 'completed', taskRunId)
1490
- } else if (doneTask?.status === 'failed') {
1491
- noteMissionTaskFinished(doneTask, 'failed', taskRunId)
1492
- } else if (doneTask?.status === 'cancelled') {
1493
- noteMissionTaskFinished(doneTask, 'cancelled', taskRunId)
1494
- }
1495
1478
  queueTaskAutonomyObservation({
1496
1479
  runId: taskRunId,
1497
1480
  sessionId,
@@ -1636,11 +1619,6 @@ export async function processNext() {
1636
1619
  })
1637
1620
  }
1638
1621
  saveTasks(t3)
1639
- if (t3[taskId].status === 'failed') {
1640
- noteMissionTaskFinished(t3[taskId], 'failed', taskRunId)
1641
- } else if (t3[taskId].status === 'cancelled') {
1642
- noteMissionTaskFinished(t3[taskId], 'cancelled', taskRunId)
1643
- }
1644
1622
  notify('tasks')
1645
1623
  notify('runs')
1646
1624
  disableSessionHeartbeat(t3[taskId].sessionId)
@@ -9,7 +9,7 @@ function readRepoSource(relativePath: string): string {
9
9
  return fs.readFileSync(path.join(repoRoot, relativePath), 'utf-8')
10
10
  }
11
11
 
12
- test('runtime hot paths use row-level task, schedule, and agent writes', () => {
12
+ test('runtime hot paths use row-level task, schedule, and wallet writes', () => {
13
13
  const expectations = [
14
14
  {
15
15
  file: 'src/lib/server/runtime/scheduler.ts',
@@ -17,14 +17,14 @@ test('runtime hot paths use row-level task, schedule, and agent writes', () => {
17
17
  forbidden: ['saveTasks(', 'saveSchedules('],
18
18
  },
19
19
  {
20
- file: 'src/app/api/schedules/[id]/run/route.ts',
21
- required: ['upsertTask(', 'upsertSchedule('],
20
+ file: 'src/lib/server/schedules/schedule-route-service.ts',
21
+ required: ['saveTask(', 'upsertSchedule('],
22
22
  forbidden: ['saveTasks(', 'saveSchedules('],
23
23
  },
24
24
  {
25
- file: 'src/lib/server/wallet/wallet-service.ts',
26
- required: ['loadAgent(', 'upsertAgent('],
27
- forbidden: ['saveAgents('],
25
+ file: 'src/lib/server/wallets/wallet-service.ts',
26
+ required: ['saveWallet(', 'deleteWallet('],
27
+ forbidden: ['saveWallets('],
28
28
  },
29
29
  ] as const
30
30
 
@@ -10,7 +10,6 @@ import { processDueWatchJobs } from '@/lib/server/runtime/watch-jobs'
10
10
  import { isAgentDisabled } from '@/lib/server/agents/agent-availability'
11
11
  import { prepareScheduledTaskRun } from '@/lib/server/tasks/task-lifecycle'
12
12
  import { ensureAgentThreadSession } from '@/lib/server/agents/agent-thread-session'
13
- import { ensureMissionForTask, noteScheduleMissionTriggered } from '@/lib/server/missions/mission-service'
14
13
  import { hasActiveProtocolRunForSchedule, launchProtocolRunForSchedule } from '@/lib/server/protocols/protocol-service'
15
14
  import { hmrSingleton } from '@/lib/shared-utils'
16
15
  import { log } from '@/lib/server/logger'
@@ -168,10 +167,6 @@ async function tick(now = Date.now()) {
168
167
  // Wake-only: no board task, just heartbeat the agent
169
168
  upsertSchedule(schedule.id, schedule)
170
169
  const wakeSessionId = resolveScheduleWakeSessionId(schedule, agents as Record<string, unknown>)
171
- noteScheduleMissionTriggered(schedule, {
172
- wakeOnly: true,
173
- sessionId: wakeSessionId || schedule.createdInSessionId || null,
174
- })
175
170
 
176
171
  const wakeMessage = schedule.message || `Schedule triggered: ${schedule.name}`
177
172
  pushMainLoopEventToMainSessions({
@@ -205,16 +200,8 @@ async function tick(now = Date.now()) {
205
200
  now,
206
201
  scheduleSignature,
207
202
  })
208
- const mission = noteScheduleMissionTriggered(schedule, {
209
- taskId,
210
- sessionId: schedule.createdInSessionId || null,
211
- })
212
- if (mission) {
213
- tasks[taskId].missionId = mission.id
214
- }
215
203
 
216
204
  upsertTask(taskId, tasks[taskId])
217
- ensureMissionForTask(tasks[taskId], { source: 'schedule' })
218
205
  upsertSchedule(schedule.id, schedule)
219
206
 
220
207
  enqueueTask(taskId)
@@ -90,7 +90,6 @@ export async function drainExecution(
90
90
  })
91
91
 
92
92
  let runtimeTimer: ReturnType<typeof setTimeout> | null = null
93
- let finishedMissionId: string | null = null
94
93
  if (next.maxRuntimeMs && next.maxRuntimeMs > 0) {
95
94
  runtimeTimer = setTimeout(() => {
96
95
  next.signalController.abort()
@@ -119,8 +118,6 @@ export async function drainExecution(
119
118
  next.run.status = aborted ? 'cancelled' : (failed ? 'failed' : 'completed')
120
119
  next.run.endedAt = next.run.endedAt || now()
121
120
  next.run.error = aborted ? (next.run.error || 'Cancelled') : result.error
122
- next.run.missionId = result.missionId || next.run.missionId || null
123
- finishedMissionId = next.run.missionId || null
124
121
  next.run.resultPreview = result.text?.slice(0, 280)
125
122
  if (typeof result.inputTokens === 'number') next.run.totalInputTokens = result.inputTokens
126
123
  if (typeof result.outputTokens === 'number') next.run.totalOutputTokens = result.outputTokens
@@ -186,7 +183,6 @@ export async function drainExecution(
186
183
  next.run.status = aborted ? 'cancelled' : 'failed'
187
184
  next.run.endedAt = now()
188
185
  next.run.error = errorMessage(err)
189
- finishedMissionId = next.run.missionId || null
190
186
  syncRunRecord(next.run)
191
187
  emitRunMeta(next, next.run.status, { error: next.run.error })
192
188
  log.error('session-run', `Run failed ${next.run.id}`, {
@@ -216,26 +212,6 @@ export async function drainExecution(
216
212
  decrementNonHeartbeatWork(next)
217
213
  reconcileSessionActivityLease(next.run.sessionId)
218
214
  notify(`stream-end:${next.run.sessionId}`)
219
- if (finishedMissionId && next.run.source !== 'chat') {
220
- const missionId = finishedMissionId
221
- queueMicrotask(() => {
222
- import('@/lib/server/missions/mission-service')
223
- .then(({ loadMissionById, requestMissionTick }) => {
224
- const mission = loadMissionById(missionId)
225
- if (!mission) return
226
- if (mission.status !== 'active') return
227
- if (mission.phase === 'dispatching' || mission.phase === 'executing') return
228
- requestMissionTick(missionId, 'run_drained', {
229
- runId: next.run.id,
230
- source: next.run.source,
231
- status: next.run.status,
232
- })
233
- })
234
- .catch((err: unknown) => {
235
- log.warn('session-run', 'Mission tick failed', { missionId, runId: next.run.id, error: errorMessage(err) })
236
- })
237
- })
238
- }
239
215
  void drainExecution(executionKey, deps)
240
216
  }
241
217
  } finally {
@@ -215,7 +215,6 @@ export function enqueueSessionRun(
215
215
  const run: SessionRunRecord = {
216
216
  id: runId,
217
217
  sessionId: input.sessionId,
218
- missionId: input.missionId ?? getSession(input.sessionId)?.missionId ?? null,
219
218
  kind: 'session_turn',
220
219
  ownerType: 'session',
221
220
  ownerId: input.sessionId,
@@ -37,7 +37,6 @@ function toQueuedTurn(entry: SessionRunQueueEntry, index: number): SessionQueued
37
37
  return {
38
38
  runId: entry.run.id,
39
39
  sessionId: entry.run.sessionId,
40
- missionId: entry.run.missionId || null,
41
40
  text: entry.message,
42
41
  queuedAt: entry.run.queuedAt,
43
42
  position: index + 1,
@@ -48,7 +48,6 @@ function resolveRecoveredQueuedEntry(entry: SessionRunQueueEntry, reason: string
48
48
  entry.resolve({
49
49
  runId: entry.run.id,
50
50
  sessionId: entry.run.sessionId,
51
- ...(entry.run.missionId ? { missionId: entry.run.missionId } : {}),
52
51
  text: '',
53
52
  persisted: false,
54
53
  toolEvents: [],
@@ -249,34 +249,6 @@ describe('session-run-manager', () => {
249
249
  assert.ok(run.queuedAt > 0)
250
250
  })
251
251
 
252
- it('copies the session mission id into queued run snapshots', () => {
253
- storage.upsertSession('sess-mission', {
254
- id: 'sess-mission',
255
- cwd: process.cwd(),
256
- user: 'tester',
257
- provider: 'ollama',
258
- model: 'test-model',
259
- claudeSessionId: null,
260
- messages: [],
261
- createdAt: Date.now(),
262
- lastActiveAt: Date.now(),
263
- agentId: 'test-agent',
264
- missionId: 'mission-123',
265
- })
266
- const release = mgr.acquireExternalSessionExecutionHold('sess-mission')
267
-
268
- const result = enqueue({
269
- sessionId: 'sess-mission',
270
- message: 'Continue the release mission',
271
- })
272
-
273
- const run = mgr.getRunById(result.runId)
274
- const snapshot = mgr.getSessionQueueSnapshot('sess-mission')
275
- assert.equal(run?.missionId, 'mission-123')
276
- assert.equal(snapshot.items[0]?.missionId, 'mission-123')
277
- release()
278
- })
279
-
280
252
  it('persists run records and replay events in storage', () => {
281
253
  const result = enqueue({
282
254
  sessionId: 'sess-persisted',
@@ -38,7 +38,6 @@ import {
38
38
  deriveTaskTitle,
39
39
  prepareTaskCreation,
40
40
  } from '@/lib/server/tasks/task-service'
41
- import { ensureMissionForTask, enrichTaskWithMissionSummary } from '@/lib/server/missions/mission-service'
42
41
  import { classifyMessage } from '@/lib/server/chat-execution/message-classifier'
43
42
  import {
44
43
  buildDelegationTaskProfile,
@@ -742,19 +741,6 @@ export function buildCrudTools(bctx: ToolBuildContext): StructuredToolInterface[
742
741
 
743
742
  res.save(all)
744
743
  if (toolKey === 'manage_tasks') {
745
- const mission = ensureMissionForTask(entry as BoardTask, {
746
- source: entry.sourceType === 'schedule'
747
- ? 'schedule'
748
- : entry.sourceType === 'delegation'
749
- ? 'delegation'
750
- : 'manual',
751
- })
752
- if (mission) {
753
- responseEntry = enrichTaskWithMissionSummary({
754
- ...(responseEntry as BoardTask),
755
- missionId: mission.id,
756
- })
757
- }
758
744
  if (taskDelegationAdvisory && responseEntry && typeof responseEntry === 'object') {
759
745
  responseEntry = {
760
746
  ...(responseEntry as Record<string, unknown>),
@@ -21,7 +21,6 @@ import {
21
21
  registerDelegationRuntime,
22
22
  startDelegationJob,
23
23
  } from '@/lib/server/agents/delegation-jobs'
24
- import { loadSession } from '@/lib/server/sessions/session-repository'
25
24
  import { markProviderFailure, markProviderSuccess } from '../provider-health'
26
25
  import { loadRuntimeSettings } from '../runtime/runtime-settings'
27
26
  import { getSessionDepth } from '../agents/subagent-runtime'
@@ -463,8 +462,6 @@ async function executeDelegateAction(args: Record<string, unknown>, bctx: Delega
463
462
  const jobId = typeof normalized.jobId === 'string' ? normalized.jobId.trim() : ''
464
463
  const waitForCompletion = normalized.waitForCompletion !== false && normalized.background !== true
465
464
  const parentSessionId = resolveDelegateSessionId(bctx)
466
- const parentMissionId = parentSessionId ? loadSession(parentSessionId)?.missionId || null : null
467
-
468
465
  recoverStaleDelegationJobs()
469
466
 
470
467
  if (action === 'status') {
@@ -505,7 +502,6 @@ async function executeDelegateAction(args: Record<string, unknown>, bctx: Delega
505
502
  const job = createDelegationJob({
506
503
  kind: 'delegate',
507
504
  parentSessionId,
508
- parentMissionId,
509
505
  backend: requestedBackend,
510
506
  task,
511
507
  cwd: bctx.cwd || null,
@@ -19,8 +19,6 @@ import { buildMemoryTools } from './memory'
19
19
  import { buildChatroomTools } from './chatroom'
20
20
  import { buildProtocolTools } from './protocol'
21
21
  import { buildSubagentTools } from './subagent'
22
- import { buildCanvasTools } from './canvas'
23
- import { buildWalletTools } from './wallet'
24
22
  import { buildOpenClawWorkspaceTools } from './openclaw-workspace'
25
23
  import { buildScheduleTools } from './schedule'
26
24
  import { buildPlatformTools } from './platform'
@@ -187,8 +185,6 @@ export async function buildSessionTools(cwd: string, enabledExtensions: string[]
187
185
  ['manage_chatrooms', buildChatroomTools],
188
186
  ['manage_protocols', buildProtocolTools],
189
187
  ['spawn_subagent', buildSubagentTools],
190
- ['canvas', buildCanvasTools],
191
- ['wallet', buildWalletTools],
192
188
  ['openclaw_workspace', buildOpenClawWorkspaceTools],
193
189
  ['schedule', buildScheduleTools],
194
190
  ['manage_sessions', buildSessionInfoTools],
@@ -168,9 +168,6 @@ async function formatPeerContext(agentId: string, peerId: string): Promise<strin
168
168
  const activeSession = Object.values(sessions).find(
169
169
  (s) => s.agentId === peerId && s.active,
170
170
  )
171
- if (activeSession?.missionId) {
172
- result.activeMission = { missionId: activeSession.missionId }
173
- }
174
171
  } catch { /* non-critical */ }
175
172
 
176
173
  // Enforce output cap — drop large fields instead of slicing mid-JSON
@@ -435,6 +435,7 @@ export function normalizeStoredRecord(
435
435
  && table !== 'schedules' && table !== 'sessions'
436
436
  && table !== 'provider_configs'
437
437
  && table !== 'runtime_runs' && table !== 'runtime_run_events'
438
+ && table !== 'wallets'
438
439
  ) {
439
440
  return { value, changed: false }
440
441
  }
@@ -573,6 +574,13 @@ function normalizeStoredRecordInner(
573
574
  return normalizeStoredScheduleRecord(value, loadItem)
574
575
  }
575
576
 
577
+ if (table === 'wallets') {
578
+ const wallet = value as StoredObject
579
+ if (wallet.chain !== 'base') wallet.chain = 'base'
580
+ if (typeof wallet.createdAt !== 'number') wallet.createdAt = Date.now()
581
+ return wallet
582
+ }
583
+
576
584
  // sessions
577
585
  const session = value as StoredObject
578
586
  // Migrate legacy 'orchestrated' → 'delegated'