@swarmclawai/swarmclaw 1.2.8 → 1.3.0

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 (214) hide show
  1. package/README.md +39 -6
  2. package/package.json +2 -2
  3. package/src/app/agents/[id]/page.tsx +1 -18
  4. package/src/app/api/activity/route.ts +9 -23
  5. package/src/app/api/agents/route.ts +17 -1
  6. package/src/app/api/agents/thread-route.test.ts +0 -1
  7. package/src/app/api/approvals/route.test.ts +6 -22
  8. package/src/app/api/approvals/route.ts +13 -5
  9. package/src/app/api/connectors/route.ts +2 -2
  10. package/src/app/api/credentials/[id]/route.ts +2 -0
  11. package/src/app/api/credentials/route.ts +4 -1
  12. package/src/app/api/goals/[id]/route.ts +28 -0
  13. package/src/app/api/goals/route.ts +33 -0
  14. package/src/app/api/portability/export/route.ts +8 -0
  15. package/src/app/api/portability/import/route.test.ts +80 -0
  16. package/src/app/api/portability/import/route.ts +28 -0
  17. package/src/app/api/protocols/templates/[id]/route.ts +2 -1
  18. package/src/app/api/protocols/templates/route.ts +2 -1
  19. package/src/app/api/settings/route.ts +13 -2
  20. package/src/app/api/wallets/[id]/route.ts +15 -157
  21. package/src/app/api/wallets/generate/route.ts +22 -0
  22. package/src/app/api/wallets/route.test.ts +147 -0
  23. package/src/app/api/wallets/route.ts +13 -95
  24. package/src/app/autonomy/page.tsx +2 -57
  25. package/src/app/home/page.tsx +3 -0
  26. package/src/app/protocols/page.tsx +2 -21
  27. package/src/app/settings/page.tsx +0 -9
  28. package/src/app/wallets/page.tsx +105 -5
  29. package/src/cli/index.js +32 -33
  30. package/src/cli/spec.js +26 -27
  31. package/src/components/agents/agent-sheet.tsx +2 -40
  32. package/src/components/agents/inspector-panel.tsx +0 -83
  33. package/src/components/chat/chat-card.tsx +0 -31
  34. package/src/components/chat/message-bubble.tsx +1 -108
  35. package/src/components/connectors/connector-sheet.tsx +25 -1
  36. package/src/components/layout/sidebar-rail.tsx +6 -10
  37. package/src/components/projects/project-detail.tsx +3 -35
  38. package/src/components/projects/tabs/overview-tab.tsx +3 -59
  39. package/src/components/projects/tabs/work-tab.tsx +7 -77
  40. package/src/components/protocols/structured-session-launcher.tsx +1 -22
  41. package/src/components/shared/connector-platform-icon.tsx +1 -0
  42. package/src/components/tasks/task-card.tsx +4 -34
  43. package/src/components/tasks/task-sheet.tsx +6 -36
  44. package/src/components/wallets/wallet-list.tsx +150 -0
  45. package/src/lib/app/navigation.test.ts +0 -13
  46. package/src/lib/app/navigation.ts +2 -7
  47. package/src/lib/app/view-constants.ts +14 -19
  48. package/src/lib/server/activity/activity-log.ts +16 -1
  49. package/src/lib/server/agents/agent-service.ts +24 -11
  50. package/src/lib/server/agents/agent-thread-session.ts +0 -1
  51. package/src/lib/server/agents/delegation-advisory.test.ts +0 -1
  52. package/src/lib/server/agents/delegation-jobs.test.ts +0 -69
  53. package/src/lib/server/agents/delegation-jobs.ts +0 -25
  54. package/src/lib/server/agents/main-agent-loop.ts +1 -49
  55. package/src/lib/server/agents/subagent-runtime.ts +0 -1
  56. package/src/lib/server/approval-match.ts +14 -85
  57. package/src/lib/server/approvals/approval-hooks.ts +81 -0
  58. package/src/lib/server/approvals.test.ts +6 -6
  59. package/src/lib/server/approvals.ts +11 -6
  60. package/src/lib/server/autonomy/supervisor-reflection.test.ts +0 -1
  61. package/src/lib/server/builtin-extensions.ts +0 -2
  62. package/src/lib/server/capability-router.test.ts +0 -2
  63. package/src/lib/server/chat-execution/chat-execution-tool-events.test.ts +14 -14
  64. package/src/lib/server/chat-execution/chat-execution-types.ts +0 -2
  65. package/src/lib/server/chat-execution/chat-execution-utils.ts +0 -2
  66. package/src/lib/server/chat-execution/chat-streaming-utils.ts +2 -30
  67. package/src/lib/server/chat-execution/chat-turn-finalization.ts +1 -36
  68. package/src/lib/server/chat-execution/chat-turn-preparation.ts +2 -22
  69. package/src/lib/server/chat-execution/iteration-event-handler.ts +0 -24
  70. package/src/lib/server/chat-execution/message-classifier.test.ts +0 -45
  71. package/src/lib/server/chat-execution/message-classifier.ts +1 -16
  72. package/src/lib/server/chat-execution/prompt-builder.test.ts +0 -1
  73. package/src/lib/server/chat-execution/prompt-builder.ts +0 -30
  74. package/src/lib/server/chat-execution/prompt-sections.ts +0 -1
  75. package/src/lib/server/chat-execution/situational-awareness.test.ts +2 -73
  76. package/src/lib/server/chat-execution/situational-awareness.ts +4 -38
  77. package/src/lib/server/chat-execution/stream-agent-chat.test.ts +8 -123
  78. package/src/lib/server/chat-execution/stream-agent-chat.ts +1 -5
  79. package/src/lib/server/chat-execution/stream-continuation.test.ts +4 -52
  80. package/src/lib/server/chat-execution/stream-continuation.ts +6 -48
  81. package/src/lib/server/chatrooms/session-mailbox.ts +0 -10
  82. package/src/lib/server/chats/chat-session-service.ts +3 -5
  83. package/src/lib/server/connectors/connector-inbound.ts +0 -1
  84. package/src/lib/server/connectors/connector-lifecycle.ts +19 -3
  85. package/src/lib/server/connectors/connector-service.ts +39 -9
  86. package/src/lib/server/connectors/swarmdock-bidding.ts +74 -0
  87. package/src/lib/server/connectors/swarmdock-payloads.test.ts +85 -0
  88. package/src/lib/server/connectors/swarmdock-secret.test.ts +128 -0
  89. package/src/lib/server/connectors/swarmdock-secret.ts +152 -0
  90. package/src/lib/server/connectors/swarmdock-tasks.ts +127 -0
  91. package/src/lib/server/connectors/swarmdock.ts +285 -0
  92. package/src/lib/server/execution-brief.test.ts +2 -25
  93. package/src/lib/server/execution-brief.ts +30 -35
  94. package/src/lib/server/execution-engine/task-attempt.ts +0 -1
  95. package/src/lib/server/goals/goal-repository.ts +19 -0
  96. package/src/lib/server/goals/goal-service.ts +143 -0
  97. package/src/lib/server/persistence/storage-context.ts +0 -5
  98. package/src/lib/server/portability/export.ts +109 -0
  99. package/src/lib/server/portability/import.ts +159 -0
  100. package/src/lib/server/protocols/protocol-normalization.ts +0 -4
  101. package/src/lib/server/protocols/protocol-queries.ts +0 -6
  102. package/src/lib/server/protocols/protocol-run-lifecycle.ts +4 -32
  103. package/src/lib/server/protocols/protocol-service.ts +0 -1
  104. package/src/lib/server/protocols/protocol-step-helpers.ts +0 -4
  105. package/src/lib/server/protocols/protocol-step-processors.ts +0 -6
  106. package/src/lib/server/protocols/protocol-swarm.ts +0 -2
  107. package/src/lib/server/protocols/protocol-types.ts +0 -2
  108. package/src/lib/server/provider-health.ts +0 -9
  109. package/src/lib/server/runtime/daemon-state/core.ts +0 -9
  110. package/src/lib/server/runtime/daemon-state.test.ts +0 -35
  111. package/src/lib/server/runtime/heartbeat-service.ts +3 -23
  112. package/src/lib/server/runtime/queue/core.ts +11 -33
  113. package/src/lib/server/runtime/runtime-storage-write-paths.test.ts +6 -6
  114. package/src/lib/server/runtime/scheduler.ts +0 -13
  115. package/src/lib/server/runtime/session-run-manager/drain.ts +0 -24
  116. package/src/lib/server/runtime/session-run-manager/enqueue.ts +0 -1
  117. package/src/lib/server/runtime/session-run-manager/queries.ts +0 -1
  118. package/src/lib/server/runtime/session-run-manager/recovery.ts +0 -1
  119. package/src/lib/server/runtime/session-run-manager.test.ts +0 -28
  120. package/src/lib/server/session-tools/crud.ts +0 -14
  121. package/src/lib/server/session-tools/delegate.ts +0 -4
  122. package/src/lib/server/session-tools/index.ts +0 -4
  123. package/src/lib/server/session-tools/team-context.ts +0 -3
  124. package/src/lib/server/storage-normalization.ts +13 -0
  125. package/src/lib/server/storage.ts +75 -45
  126. package/src/lib/server/tasks/task-checkout.ts +59 -0
  127. package/src/lib/server/tasks/task-lifecycle.ts +2 -0
  128. package/src/lib/server/tasks/task-route-service.ts +4 -26
  129. package/src/lib/server/tasks/task-service.ts +0 -7
  130. package/src/lib/server/tool-aliases.ts +0 -1
  131. package/src/lib/server/tool-capability-policy-advanced.test.ts +4 -4
  132. package/src/lib/server/tool-capability-policy.ts +0 -2
  133. package/src/lib/server/tool-planning.ts +0 -12
  134. package/src/lib/server/universal-tool-access.ts +0 -1
  135. package/src/lib/server/usage/cost-rollup.ts +124 -0
  136. package/src/lib/server/usage/usage-repository.ts +6 -0
  137. package/src/lib/server/wallets/wallet-crypto.ts +33 -0
  138. package/src/lib/server/wallets/wallet-repository.ts +24 -0
  139. package/src/lib/server/wallets/wallet-service.ts +119 -0
  140. package/src/lib/server/working-state/extraction.ts +8 -42
  141. package/src/lib/server/working-state/normalization.ts +10 -103
  142. package/src/lib/server/working-state/service.ts +12 -21
  143. package/src/lib/strip-internal-metadata.test.ts +1 -1
  144. package/src/lib/strip-internal-metadata.ts +1 -1
  145. package/src/lib/tool-definitions.ts +0 -1
  146. package/src/lib/validation/schemas.ts +36 -32
  147. package/src/lib/validation/server-schemas.ts +35 -0
  148. package/src/stores/slices/data-slice.ts +5 -1
  149. package/src/stores/slices/ui-slice.ts +0 -4
  150. package/src/types/agent.ts +10 -84
  151. package/src/types/app-settings.ts +6 -2
  152. package/src/types/approval.ts +3 -2
  153. package/src/types/connector.ts +1 -0
  154. package/src/types/goal.ts +30 -0
  155. package/src/types/index.ts +2 -1
  156. package/src/types/message.ts +0 -1
  157. package/src/types/misc.ts +2 -4
  158. package/src/types/protocol.ts +0 -2
  159. package/src/types/run.ts +0 -3
  160. package/src/types/session.ts +1 -51
  161. package/src/types/swarmdock.ts +29 -0
  162. package/src/types/task.ts +9 -3
  163. package/src/types/working-state.ts +2 -9
  164. package/src/views/settings/section-runtime-loop.tsx +0 -14
  165. package/src/app/api/canvas/[sessionId]/route.ts +0 -35
  166. package/src/app/api/missions/[id]/actions/route.ts +0 -31
  167. package/src/app/api/missions/[id]/events/route.ts +0 -14
  168. package/src/app/api/missions/[id]/route.ts +0 -10
  169. package/src/app/api/missions/route.test.ts +0 -244
  170. package/src/app/api/missions/route.ts +0 -57
  171. package/src/app/api/wallets/[id]/approve/route.ts +0 -79
  172. package/src/app/api/wallets/[id]/balance-history/route.ts +0 -18
  173. package/src/app/api/wallets/[id]/send/route.ts +0 -113
  174. package/src/app/api/wallets/[id]/transactions/route.ts +0 -18
  175. package/src/app/missions/[id]/page.tsx +0 -3
  176. package/src/app/missions/page.tsx +0 -685
  177. package/src/components/canvas/canvas-panel.tsx +0 -267
  178. package/src/components/wallets/wallet-approval-dialog.tsx +0 -107
  179. package/src/components/wallets/wallet-panel.tsx +0 -1010
  180. package/src/components/wallets/wallet-section.tsx +0 -260
  181. package/src/features/missions/queries.ts +0 -23
  182. package/src/lib/canvas-content.test.ts +0 -360
  183. package/src/lib/canvas-content.ts +0 -198
  184. package/src/lib/server/canvas-content.test.ts +0 -32
  185. package/src/lib/server/canvas-content.ts +0 -6
  186. package/src/lib/server/ethereum.ts +0 -591
  187. package/src/lib/server/evm-swap.ts +0 -476
  188. package/src/lib/server/missions/mission-intent.test.ts +0 -63
  189. package/src/lib/server/missions/mission-intent.ts +0 -569
  190. package/src/lib/server/missions/mission-repository.ts +0 -74
  191. package/src/lib/server/missions/mission-service/actions.ts +0 -6
  192. package/src/lib/server/missions/mission-service/bindings.ts +0 -9
  193. package/src/lib/server/missions/mission-service/context.ts +0 -4
  194. package/src/lib/server/missions/mission-service/core.ts +0 -2271
  195. package/src/lib/server/missions/mission-service/queries.ts +0 -12
  196. package/src/lib/server/missions/mission-service/recovery.ts +0 -5
  197. package/src/lib/server/missions/mission-service/ticks.ts +0 -9
  198. package/src/lib/server/missions/mission-service.test.ts +0 -888
  199. package/src/lib/server/missions/mission-service.ts +0 -6
  200. package/src/lib/server/session-tools/canvas.ts +0 -105
  201. package/src/lib/server/session-tools/wallet-tool.test.ts +0 -150
  202. package/src/lib/server/session-tools/wallet.ts +0 -1287
  203. package/src/lib/server/solana.ts +0 -327
  204. package/src/lib/server/wallet/wallet-execution.test.ts +0 -198
  205. package/src/lib/server/wallet/wallet-portfolio.test.ts +0 -98
  206. package/src/lib/server/wallet/wallet-portfolio.ts +0 -772
  207. package/src/lib/server/wallet/wallet-service.test.ts +0 -81
  208. package/src/lib/server/wallet/wallet-service.ts +0 -225
  209. package/src/lib/wallet/wallet-transactions.test.ts +0 -75
  210. package/src/lib/wallet/wallet-transactions.ts +0 -43
  211. package/src/lib/wallet/wallet.test.ts +0 -333
  212. package/src/lib/wallet/wallet.ts +0 -183
  213. package/src/types/mission.ts +0 -185
  214. package/src/views/settings/section-wallets.tsx +0 -35
@@ -225,73 +225,4 @@ describe('delegation-jobs', () => {
225
225
  )
226
226
  })
227
227
 
228
- it('creates and syncs a child mission for delegation jobs', async () => {
229
- const storage = await import('@/lib/server/storage')
230
- const missions = await import('@/lib/server/missions/mission-service')
231
-
232
- storage.saveSessions({
233
- 'mission-parent-session': {
234
- id: 'mission-parent-session',
235
- name: 'Parent Session',
236
- cwd: process.cwd(),
237
- user: 'tester',
238
- provider: 'ollama',
239
- model: 'test-model',
240
- messages: [],
241
- createdAt: Date.now(),
242
- lastActiveAt: Date.now(),
243
- agentId: 'agent-parent',
244
- },
245
- })
246
- storage.saveAgents({
247
- 'agent-parent': {
248
- id: 'agent-parent',
249
- name: 'Parent Agent',
250
- provider: 'ollama',
251
- model: 'test-model',
252
- systemPrompt: 'test',
253
- },
254
- 'agent-child': {
255
- id: 'agent-child',
256
- name: 'Child Agent',
257
- provider: 'ollama',
258
- model: 'test-model',
259
- systemPrompt: 'test',
260
- },
261
- })
262
-
263
- const parentMission = await missions.resolveMissionForTurn({
264
- session: storage.loadSessions()['mission-parent-session'],
265
- message: 'Build the release pipeline and delegate the test matrix.',
266
- source: 'chat',
267
- internal: false,
268
- runId: 'run-parent',
269
- generateText: async () => JSON.stringify({
270
- action: 'create_new',
271
- objective: 'Build the release pipeline',
272
- successCriteria: ['delegated matrix completes'],
273
- currentStep: 'Delegate the test matrix',
274
- }),
275
- })
276
-
277
- const job = delegationJobs.createDelegationJob({
278
- kind: 'subagent',
279
- parentSessionId: 'mission-parent-session',
280
- parentMissionId: parentMission?.id || null,
281
- childSessionId: 'child-session-1',
282
- agentId: 'agent-child',
283
- task: 'Run the test matrix',
284
- })
285
-
286
- const started = delegationJobs.startDelegationJob(job.id, { childSessionId: 'child-session-1' })
287
- const completed = delegationJobs.completeDelegationJob(job.id, 'Matrix passed')
288
- const childMission = completed?.missionId ? missions.loadMissionById(completed.missionId) : null
289
- const refreshedParent = parentMission?.id ? missions.loadMissionById(parentMission.id) : null
290
-
291
- assert.ok(started?.missionId)
292
- assert.equal(started?.parentMissionId, parentMission?.id || null)
293
- assert.equal(childMission?.parentMissionId, parentMission?.id || null)
294
- assert.equal(childMission?.status, 'completed')
295
- assert.ok(refreshedParent?.childMissionIds?.includes(childMission?.id || ''))
296
- })
297
228
  })
@@ -13,8 +13,6 @@ import { log } from '@/lib/server/logger'
13
13
  import { debug } from '@/lib/server/debug'
14
14
  import { createNotification } from '@/lib/server/create-notification'
15
15
  import { enqueueSystemEvent } from '@/lib/server/runtime/system-events'
16
- import { ensureDelegationMission, syncDelegationMissionFromJob } from '@/lib/server/missions/mission-service'
17
- import { loadSession } from '@/lib/server/sessions/session-repository'
18
16
  import { notify } from '@/lib/server/ws-hub'
19
17
 
20
18
  interface DelegationRuntimeHandle {
@@ -42,8 +40,6 @@ export interface CreateDelegationJobInput {
42
40
  kind: DelegationJobRecord['kind']
43
41
  task: string
44
42
  backend?: DelegationJobRecord['backend']
45
- missionId?: string | null
46
- parentMissionId?: string | null
47
43
  parentSessionId?: string | null
48
44
  childSessionId?: string | null
49
45
  agentId?: string | null
@@ -53,15 +49,11 @@ export interface CreateDelegationJobInput {
53
49
 
54
50
  export function createDelegationJob(input: CreateDelegationJobInput): DelegationJobRecord {
55
51
  const createdAt = Date.now()
56
- const inferredParentMissionId = input.parentMissionId
57
- || (input.parentSessionId ? loadSession(input.parentSessionId)?.missionId || null : null)
58
52
  const job: DelegationJobRecord = {
59
53
  id: genId(10),
60
54
  kind: input.kind,
61
55
  status: 'queued',
62
56
  backend: input.backend ?? null,
63
- missionId: input.missionId ?? null,
64
- parentMissionId: inferredParentMissionId,
65
57
  parentSessionId: input.parentSessionId ?? null,
66
58
  childSessionId: input.childSessionId ?? null,
67
59
  agentId: input.agentId ?? null,
@@ -85,22 +77,6 @@ export function createDelegationJob(input: CreateDelegationJobInput): Delegation
85
77
  completedAt: null,
86
78
  }
87
79
  upsertDelegationJob(job.id, job)
88
- if (!job.missionId && inferredParentMissionId) {
89
- const mission = ensureDelegationMission({
90
- task: job.task,
91
- backend: job.backend,
92
- parentSessionId: job.parentSessionId || null,
93
- childSessionId: job.childSessionId || null,
94
- agentId: job.agentId || null,
95
- parentMissionId: inferredParentMissionId,
96
- jobId: job.id,
97
- })
98
- if (mission) {
99
- job.missionId = mission.id
100
- upsertDelegationJob(job.id, job)
101
- }
102
- }
103
- syncDelegationMissionFromJob(job.id)
104
80
  notifyDelegationJobsChanged()
105
81
 
106
82
  const sid = job.childSessionId || job.parentSessionId || ''
@@ -151,7 +127,6 @@ export function updateDelegationJob(
151
127
  }
152
128
  })
153
129
  if (!result) return null
154
- syncDelegationMissionFromJob(id)
155
130
  notifyDelegationJobsChanged()
156
131
  return getDelegationJob(id) || result
157
132
  }
@@ -10,7 +10,6 @@ import {
10
10
  import { loadAgents } from '@/lib/server/agents/agent-repository'
11
11
  import { assessAutonomyRun } from '@/lib/server/autonomy/supervisor-reflection'
12
12
  import { enqueueSystemEvent } from '@/lib/server/runtime/system-events'
13
- import { buildMissionHeartbeatPrompt as buildMissionHeartbeatPromptFromMission, getMissionForSession } from '@/lib/server/missions/mission-service'
14
13
  import { loadSettings } from '@/lib/server/settings/settings-repository'
15
14
  import { getSession, loadSessions } from '@/lib/server/sessions/session-repository'
16
15
  import { deleteSessionWorkingState, loadSessionWorkingState, syncWorkingStateFromMainLoopState } from '@/lib/server/working-state/service'
@@ -37,7 +36,6 @@ export interface MainLoopState {
37
36
  currentPlanStep: string | null
38
37
  reviewNote: string | null
39
38
  reviewConfidence: number | null
40
- missionTaskId: string | null
41
39
  momentumScore: number
42
40
  paused: boolean
43
41
  status: 'idle' | 'progress' | 'blocked' | 'ok'
@@ -55,8 +53,6 @@ export interface MainLoopState {
55
53
  note: string
56
54
  status?: 'idle' | 'progress' | 'blocked' | 'ok' | 'reflection'
57
55
  }>
58
- missionTokens: number
59
- missionCostUsd: number
60
56
  followupChainCount: number
61
57
  lifetimeIterations: number
62
58
  metaMissCount: number
@@ -137,15 +133,12 @@ function defaultState(): MainLoopState {
137
133
  currentPlanStep: null,
138
134
  reviewNote: null,
139
135
  reviewConfidence: null,
140
- missionTaskId: null,
141
136
  momentumScore: 0,
142
137
  paused: false,
143
138
  status: 'idle',
144
139
  autonomyMode: 'assist',
145
140
  pendingEvents: [],
146
141
  timeline: [],
147
- missionTokens: 0,
148
- missionCostUsd: 0,
149
142
  followupChainCount: 0,
150
143
  lifetimeIterations: 0,
151
144
  metaMissCount: 0,
@@ -304,8 +297,6 @@ function clampState(state: MainLoopState): MainLoopState {
304
297
  state.followupChainCount = Math.max(0, Math.min(10, Math.trunc(state.followupChainCount || 0)))
305
298
  state.lifetimeIterations = Math.max(0, Math.trunc(state.lifetimeIterations || 0))
306
299
  state.metaMissCount = Math.max(0, Math.min(100, Math.trunc(state.metaMissCount || 0)))
307
- state.missionTokens = Math.max(0, Math.trunc(state.missionTokens || 0))
308
- state.missionCostUsd = Math.max(0, Number.isFinite(state.missionCostUsd) ? Number(state.missionCostUsd) : 0)
309
300
  state.skillBlocker = normalizeSkillBlocker(state.skillBlocker)
310
301
  state.updatedAt = typeof state.updatedAt === 'number' && Number.isFinite(state.updatedAt) ? Math.trunc(state.updatedAt) : now()
311
302
  return state
@@ -325,15 +316,12 @@ function normalizeState(input?: Partial<MainLoopState> | null): MainLoopState {
325
316
  if (typeof input.reviewConfidence === 'number' || typeof input.reviewConfidence === 'string' || input.reviewConfidence === null) {
326
317
  next.reviewConfidence = normalizeConfidence(input.reviewConfidence)
327
318
  }
328
- if (typeof input.missionTaskId === 'string' || input.missionTaskId === null) next.missionTaskId = input.missionTaskId
329
319
  if (typeof input.momentumScore === 'number') next.momentumScore = input.momentumScore
330
320
  if (typeof input.paused === 'boolean') next.paused = input.paused
331
321
  if (input.status) next.status = normalizeStatus(input.status, next.status)
332
322
  if (input.autonomyMode) next.autonomyMode = normalizeAutonomyMode(input.autonomyMode, next.autonomyMode)
333
323
  if (Array.isArray(input.pendingEvents)) next.pendingEvents = [...input.pendingEvents]
334
324
  if (Array.isArray(input.timeline)) next.timeline = [...input.timeline]
335
- if (typeof input.missionTokens === 'number') next.missionTokens = input.missionTokens
336
- if (typeof input.missionCostUsd === 'number') next.missionCostUsd = input.missionCostUsd
337
325
  if (typeof input.followupChainCount === 'number') next.followupChainCount = input.followupChainCount
338
326
  if (typeof input.lifetimeIterations === 'number') next.lifetimeIterations = input.lifetimeIterations
339
327
  if (typeof input.metaMissCount === 'number') next.metaMissCount = input.metaMissCount
@@ -435,10 +423,8 @@ function persistState(sessionId: string, state: MainLoopState): void {
435
423
  upsertPersistedMainLoopState(sessionId, normalized as unknown as Record<string, unknown>)
436
424
  const session = getSession(sessionId)
437
425
  if (!session) return
438
- const mission = getMissionForSession(session)
439
426
  void syncWorkingStateFromMainLoopState({
440
427
  sessionId,
441
- mission,
442
428
  goal: normalized.goal,
443
429
  summary: normalized.summary,
444
430
  status: normalized.status === 'ok'
@@ -797,11 +783,6 @@ export function buildMainLoopHeartbeatPrompt(session: unknown, fallbackPrompt: s
797
783
  const candidate = asSession(session)
798
784
  if (!candidate?.id) return fallbackPrompt
799
785
  const persistedSession = getSession(String(candidate.id)) as Session | undefined
800
- const missionPrompt = buildMissionHeartbeatPromptFromMission(
801
- persistedSession || candidate as Session,
802
- fallbackPrompt,
803
- )
804
- if (missionPrompt) return missionPrompt
805
786
  const state = getOrCreateState(String(candidate.id))
806
787
  if (!state) return fallbackPrompt
807
788
  const latestExternalGoal = extractLatestGoal(Array.isArray(candidate.messages) ? candidate.messages as Message[] : [])
@@ -812,7 +793,6 @@ export function buildMainLoopHeartbeatPrompt(session: unknown, fallbackPrompt: s
812
793
  const heartbeatSession = (persistedSession || candidate as Session)
813
794
  const executionBrief = buildExecutionBrief({
814
795
  session: heartbeatSession,
815
- mission: getMissionForSession(heartbeatSession),
816
796
  })
817
797
  const executionBriefBlock = buildExecutionBriefContextBlock(executionBrief)
818
798
  const boundedFallbackPrompt = cleanMultiline(fallbackPrompt, 500)
@@ -969,8 +949,6 @@ export function handleMainLoopRunResult(input: HandleMainLoopRunResultInput): Ma
969
949
  const messageGoal = shouldCaptureMessageGoal ? parseGoalContractFromText(input.message || '') : null
970
950
  const nowTs = now()
971
951
  state.lifetimeIterations++
972
- const mission = session ? getMissionForSession(session) : null
973
-
974
952
  if (messageGoal) state.goalContract = mergeGoalContracts(state.goalContract, messageGoal)
975
953
  if (!state.goal && shouldCaptureMessageGoal) state.goal = cleanMultiline(input.message, 900)
976
954
  if (heartbeat?.goal) state.goal = heartbeat.goal
@@ -1011,8 +989,6 @@ export function handleMainLoopRunResult(input: HandleMainLoopRunResultInput): Ma
1011
989
 
1012
990
  state.lastTickAt = nowTs
1013
991
  state.updatedAt = nowTs
1014
- state.missionTokens += Math.max(0, Math.trunc((input.inputTokens || 0) + (input.outputTokens || 0)))
1015
- state.missionCostUsd += Math.max(0, Number(input.estimatedCost || 0))
1016
992
  const cleanedResult = persistedText.trim()
1017
993
  const waitingForExternal = extractWaitSignal(resultText)
1018
994
  const gotTerminalAck = /^HEARTBEAT_OK$/i.test(cleanedResult) || /^NO_MESSAGE$/i.test(cleanedResult)
@@ -1073,32 +1049,8 @@ export function handleMainLoopRunResult(input: HandleMainLoopRunResultInput): Ma
1073
1049
  const needsReplan = review?.needs_replan === true || ((review?.confidence ?? 1) < 0.45)
1074
1050
  const limit = followupLimit(input.sessionId)
1075
1051
 
1076
- if (mission) {
1077
- state.goal = cleanMultiline(mission.objective, 900)
1078
- state.missionTaskId = mission.rootTaskId || null
1079
- state.summary = cleanMultiline(mission.verifierSummary || mission.plannerSummary || state.summary, 500)
1080
- state.nextAction = cleanText(mission.currentStep, 280) || null
1081
- state.currentPlanStep = cleanText(mission.currentStep, 280) || null
1082
- state.planSteps = mission.currentStep ? [mission.currentStep] : []
1083
- state.status = mission.status === 'completed'
1084
- ? 'ok'
1085
- : mission.status === 'waiting' || mission.status === 'failed' || mission.status === 'cancelled'
1086
- ? 'blocked'
1087
- : 'progress'
1088
- state.paused = mission.status === 'waiting' || mission.status === 'failed' || mission.status === 'cancelled'
1089
- }
1090
-
1091
1052
  let followup: MainLoopFollowupRequest | null = null
1092
- if (mission) {
1093
- state.followupChainCount = 0
1094
- if (mission.status === 'completed') {
1095
- state.status = 'ok'
1096
- state.paused = false
1097
- }
1098
- if (mission.status === 'waiting' || mission.status === 'failed' || mission.status === 'cancelled') {
1099
- state.paused = true
1100
- }
1101
- } else if (isDirectUserChat) {
1053
+ if (isDirectUserChat) {
1102
1054
  state.followupChainCount = 0
1103
1055
  state.lifetimeIterations = 0
1104
1056
  if (successfulChatDelivery) {
@@ -237,7 +237,6 @@ async function spawnSubagentImpl(
237
237
  const job = createDelegationJob({
238
238
  kind: 'subagent',
239
239
  parentSessionId: context.sessionId || null,
240
- parentMissionId: typeof parent?.missionId === 'string' ? parent.missionId : null,
241
240
  agentId: input.agentId,
242
241
  task: input.message,
243
242
  cwd: input.cwd || context.cwd,
@@ -34,80 +34,6 @@ function canonicalizeValue(value: unknown): unknown {
34
34
  }, {})
35
35
  }
36
36
 
37
- function canonicalizeEthereumTransaction(value: unknown): Record<string, unknown> | null {
38
- if (!isPlainRecord(value)) return null
39
- const tx = value
40
- const comparable: Record<string, unknown> = {}
41
- const to = trimString(tx.to)
42
- const data = trimString(tx.data)
43
- const valueAtomic = tx.value
44
- const type = normalizeScalar(tx.type)
45
- const chainId = normalizeScalar(tx.chainId)
46
-
47
- if (to) comparable.to = /^0x[0-9a-f]+$/i.test(to) ? to.toLowerCase() : to
48
- if (data) comparable.data = /^0x[0-9a-f]+$/i.test(data) ? data.toLowerCase() : data
49
- if (valueAtomic !== undefined && valueAtomic !== null && valueAtomic !== '') comparable.value = normalizeScalar(valueAtomic)
50
- if (type !== null && type !== undefined && type !== '') comparable.type = type
51
- if (chainId !== null && chainId !== undefined && chainId !== '') comparable.chainId = chainId
52
-
53
- return Object.keys(comparable).length > 0 ? comparable : null
54
- }
55
-
56
- function comparableWalletActionPayload(data: Record<string, unknown>): Record<string, unknown> {
57
- const action = trimString(data.action)
58
- const chain = trimString(data.chain)
59
- const network = trimString(data.network)
60
- const payload: Record<string, unknown> = {
61
- action,
62
- chain,
63
- network,
64
- }
65
-
66
- const transaction = canonicalizeEthereumTransaction(data.transaction)
67
- if (transaction) payload.transaction = transaction
68
-
69
- const signedTransactionFingerprint = trimString(data.signedTransactionFingerprint)
70
- if (signedTransactionFingerprint) payload.signedTransactionFingerprint = signedTransactionFingerprint
71
-
72
- const transactionFingerprint = trimString(data.transactionFingerprint)
73
- if (transactionFingerprint) payload.transactionFingerprint = transactionFingerprint
74
-
75
- const messageDigest = trimString(data.messageDigest)
76
- if (messageDigest) payload.messageDigest = messageDigest
77
-
78
- const domain = canonicalizeValue(data.domain)
79
- if (domain && typeof domain === 'object') payload.domain = domain
80
-
81
- const types = canonicalizeValue(data.types)
82
- if (types && typeof types === 'object') payload.types = types
83
-
84
- const value = canonicalizeValue(data.value)
85
- if (value && typeof value === 'object') payload.value = value
86
-
87
- const toAddress = trimString(data.toAddress)
88
- if (toAddress) payload.toAddress = /^0x[0-9a-f]+$/i.test(toAddress) ? toAddress.toLowerCase() : toAddress
89
-
90
- const amountAtomic = normalizeScalar(data.amountAtomic)
91
- if (amountAtomic !== null && amountAtomic !== undefined && amountAtomic !== '') payload.amountAtomic = amountAtomic
92
-
93
- const sellToken = trimString(data.sellToken)
94
- if (sellToken) payload.sellToken = /^0x[0-9a-f]+$/i.test(sellToken) ? sellToken.toLowerCase() : sellToken
95
-
96
- const buyToken = trimString(data.buyToken)
97
- if (buyToken) payload.buyToken = /^0x[0-9a-f]+$/i.test(buyToken) ? buyToken.toLowerCase() : buyToken
98
-
99
- const recipient = trimString(data.recipient)
100
- if (recipient) payload.recipient = /^0x[0-9a-f]+$/i.test(recipient) ? recipient.toLowerCase() : recipient
101
-
102
- const routeProvider = trimString(data.routeProvider)
103
- if (routeProvider) payload.routeProvider = routeProvider
104
-
105
- const slippageBps = normalizeScalar(data.slippageBps)
106
- if (slippageBps !== null && slippageBps !== undefined && slippageBps !== '') payload.slippageBps = slippageBps
107
-
108
- return payload
109
- }
110
-
111
37
  export function buildApprovalComparablePayload(
112
38
  category: ApprovalCategory,
113
39
  data: Record<string, unknown>,
@@ -128,17 +54,6 @@ export function buildApprovalComparablePayload(
128
54
  extensionId: trimString(data.extensionId),
129
55
  filename: trimString(data.filename),
130
56
  }
131
- case 'wallet_transfer': {
132
- const toAddress = trimString(data.toAddress)
133
- return {
134
- chain: trimString(data.chain),
135
- toAddress: /^0x[0-9a-f]+$/i.test(toAddress) ? toAddress.toLowerCase() : toAddress,
136
- amountAtomic: normalizeScalar(data.amountAtomic),
137
- memo: trimString(data.memo),
138
- }
139
- }
140
- case 'wallet_action':
141
- return comparableWalletActionPayload(data)
142
57
  case 'human_loop':
143
58
  return {
144
59
  question: trimString(data.question),
@@ -156,6 +71,20 @@ export function buildApprovalComparablePayload(
156
71
  toolName: trimString(data.toolName),
157
72
  args: canonicalizeValue(data.args),
158
73
  }
74
+ case 'agent_create':
75
+ return {
76
+ agentName: trimString(data.agentName),
77
+ provider: trimString(data.provider),
78
+ }
79
+ case 'budget_change':
80
+ return {
81
+ agentId: trimString(data.agentId),
82
+ budgetChanges: canonicalizeValue(data.budgetChanges),
83
+ }
84
+ case 'delegation_enable':
85
+ return {
86
+ agentId: trimString(data.agentId),
87
+ }
159
88
  default:
160
89
  return canonicalizeValue(data) as Record<string, unknown>
161
90
  }
@@ -0,0 +1,81 @@
1
+ import type { ApprovalRequest } from '@/types'
2
+ import { logActivity } from '@/lib/server/activity/activity-log'
3
+ import { log } from '@/lib/server/logger'
4
+
5
+ const TAG = 'approval-hooks'
6
+
7
+ type ApprovalHookHandler = (request: ApprovalRequest) => void
8
+
9
+ const approvalHandlers: Partial<Record<string, ApprovalHookHandler>> = {
10
+ agent_create: onAgentCreateDecided,
11
+ budget_change: onBudgetChangeDecided,
12
+ delegation_enable: onDelegationEnableDecided,
13
+ }
14
+
15
+ /**
16
+ * Dispatch lifecycle hooks when an approval decision is made.
17
+ * Called from submitDecision() after the approval is persisted.
18
+ */
19
+ export function onApprovalDecision(request: ApprovalRequest): void {
20
+ const handler = approvalHandlers[request.category]
21
+ if (!handler) return
22
+ try {
23
+ handler(request)
24
+ } catch (err) {
25
+ log.error(TAG, `Error in approval hook for ${request.category}: ${err}`)
26
+ }
27
+ }
28
+
29
+ function onAgentCreateDecided(request: ApprovalRequest): void {
30
+ if (request.status !== 'approved') return
31
+ const pendingConfig = request.data.pendingAgentConfig as Record<string, unknown> | undefined
32
+ if (!pendingConfig) return
33
+
34
+ // Dynamically import to avoid circular dependency
35
+ import('@/lib/server/agents/agent-service').then(({ createAgent }) => {
36
+ const agent = createAgent({ body: pendingConfig })
37
+ logActivity({
38
+ entityType: 'agent',
39
+ entityId: agent.id,
40
+ action: 'created',
41
+ actor: 'system',
42
+ summary: `Agent "${agent.name}" created after approval ${request.id}`,
43
+ detail: { approvalId: request.id },
44
+ })
45
+ }).catch((err) => {
46
+ log.error(TAG, `Failed to create agent after approval: ${err}`)
47
+ })
48
+ }
49
+
50
+ function onBudgetChangeDecided(request: ApprovalRequest): void {
51
+ if (request.status !== 'approved') return
52
+ const agentId = request.data.agentId as string | undefined
53
+ const budgetChanges = request.data.budgetChanges as Record<string, unknown> | undefined
54
+ if (!agentId || !budgetChanges) return
55
+
56
+ import('@/lib/server/agents/agent-service').then(({ updateAgent }) => {
57
+ updateAgent(agentId, budgetChanges)
58
+ logActivity({
59
+ entityType: 'budget',
60
+ entityId: agentId,
61
+ action: 'configured',
62
+ actor: 'system',
63
+ summary: `Budget updated for agent after approval ${request.id}`,
64
+ detail: { approvalId: request.id, budgetChanges },
65
+ })
66
+ }).catch((err) => {
67
+ log.error(TAG, `Failed to apply budget change after approval: ${err}`)
68
+ })
69
+ }
70
+
71
+ function onDelegationEnableDecided(request: ApprovalRequest): void {
72
+ if (request.status !== 'approved') return
73
+ const agentId = request.data.agentId as string | undefined
74
+ if (!agentId) return
75
+
76
+ import('@/lib/server/agents/agent-service').then(({ updateAgent }) => {
77
+ updateAgent(agentId, { delegationEnabled: true })
78
+ }).catch((err) => {
79
+ log.error(TAG, `Failed to enable delegation after approval: ${err}`)
80
+ })
81
+ }
@@ -56,19 +56,19 @@ describe('approvals', () => {
56
56
  title: 'Human approval',
57
57
  data: { question: 'Proceed?' },
58
58
  })
59
- const wallet = approvals.requestApproval({
60
- category: 'wallet_action',
61
- title: 'Legacy wallet approval',
62
- data: { action: 'sign_message' },
59
+ const other = approvals.requestApproval({
60
+ category: 'human_loop',
61
+ title: 'Another approval',
62
+ data: { question: 'Continue?' },
63
63
  })
64
64
 
65
- await approvals.submitDecision(wallet.id, true)
65
+ await approvals.submitDecision(other.id, true)
66
66
 
67
67
  const pending = approvals.listPendingApprovals()
68
68
  const humanPending = approvals.listPendingApprovals('human_loop')
69
69
 
70
70
  assert.equal(pending.some((entry) => entry.id === human.id), true)
71
- assert.equal(pending.some((entry) => entry.id === wallet.id), false)
71
+ assert.equal(pending.some((entry) => entry.id === other.id), false)
72
72
  assert.equal(humanPending.some((entry) => entry.id === human.id), true)
73
73
  assert.equal(humanPending.every((entry) => entry.category === 'human_loop'), true)
74
74
  })
@@ -5,7 +5,8 @@ import { notify } from './ws-hub'
5
5
  import { dispatchWake } from '@/lib/server/runtime/wake-dispatcher'
6
6
  import { enqueueSystemEvent } from '@/lib/server/runtime/system-events'
7
7
  import { enqueueSessionRun } from '@/lib/server/runtime/session-run-manager'
8
- import { requestMissionTicksForApprovalDecision } from '@/lib/server/missions/mission-service'
8
+ import { logActivity } from '@/lib/server/activity/activity-log'
9
+ import { onApprovalDecision } from '@/lib/server/approvals/approval-hooks'
9
10
 
10
11
  function trimToString(value: unknown): string {
11
12
  return typeof value === 'string' ? value.trim() : ''
@@ -122,11 +123,6 @@ async function persistApprovalDecision(request: ApprovalRequest, approved: boole
122
123
  // best-effort trigger only
123
124
  })
124
125
  if (request.sessionId) notify(`session:${request.sessionId}`)
125
- requestMissionTicksForApprovalDecision({
126
- approvalId: request.id,
127
- status: approved ? 'approved' : 'rejected',
128
- sessionId: request.sessionId || null,
129
- })
130
126
  return request
131
127
  }
132
128
 
@@ -137,6 +133,15 @@ export async function submitDecision(id: string, approved: boolean): Promise<App
137
133
  if (request.status === (approved ? 'approved' : 'rejected')) return request
138
134
  if (request.status !== 'pending') return request
139
135
  const updated = await persistApprovalDecision(request, approved)
136
+ logActivity({
137
+ entityType: 'approval',
138
+ entityId: id,
139
+ action: approved ? 'approved' : 'rejected',
140
+ actor: 'user',
141
+ summary: `Approval ${approved ? 'approved' : 'rejected'}: ${request.title}`,
142
+ detail: { category: request.category, agentId: request.agentId, sessionId: request.sessionId },
143
+ })
144
+ onApprovalDecision(updated)
140
145
  wakeForApprovalDecision(updated, approved)
141
146
  return updated
142
147
  }
@@ -235,7 +235,6 @@ describe('supervisor-reflection', () => {
235
235
  semantics: {
236
236
  taskIntent: 'general',
237
237
  workType: 'general',
238
- walletIntent: 'none',
239
238
  isDeliverableTask: false,
240
239
  isBroadGoal: false,
241
240
  isResearchSynthesis: false,
@@ -7,10 +7,8 @@ import '@/lib/server/session-tools/memory'
7
7
  import '@/lib/server/session-tools/platform'
8
8
  import '@/lib/server/session-tools/monitor'
9
9
  import '@/lib/server/session-tools/discovery'
10
- import '@/lib/server/session-tools/wallet'
11
10
  import '@/lib/server/session-tools/connector'
12
11
  // http_request consolidated into web 'api' action
13
- import '@/lib/server/session-tools/canvas'
14
12
  import '@/lib/server/session-tools/chatroom'
15
13
  import '@/lib/server/session-tools/delegate'
16
14
  import '@/lib/server/session-tools/schedule'
@@ -80,7 +80,6 @@ test('routeTaskIntent uses structured classification when available', () => {
80
80
  taskIntent: 'browsing',
81
81
  isDeliverableTask: true,
82
82
  isBroadGoal: false,
83
- walletIntent: 'none',
84
83
  hasHumanSignals: false,
85
84
  hasSignificantEvent: false,
86
85
  isResearchSynthesis: true,
@@ -108,7 +107,6 @@ function makeClassification(overrides: Partial<MessageClassification>): MessageC
108
107
  taskIntent: 'general',
109
108
  isDeliverableTask: false,
110
109
  isBroadGoal: false,
111
- walletIntent: 'none',
112
110
  hasHumanSignals: false,
113
111
  hasSignificantEvent: false,
114
112
  isResearchSynthesis: false,
@@ -108,31 +108,31 @@ describe('collectToolEvent', () => {
108
108
  const bag: MessageToolEvent[] = []
109
109
  collectToolEvent({
110
110
  t: 'tool_call',
111
- toolName: 'wallet_tool',
112
- toolInput: '{"action":"balance","chain":"solana"}',
113
- toolCallId: 'call-sol',
111
+ toolName: 'http_request',
112
+ toolInput: '{"method":"GET","url":"https://api-a.example.com"}',
113
+ toolCallId: 'call-a',
114
114
  }, bag)
115
115
  collectToolEvent({
116
116
  t: 'tool_call',
117
- toolName: 'wallet_tool',
118
- toolInput: '{"action":"balance","chain":"ethereum"}',
119
- toolCallId: 'call-eth',
117
+ toolName: 'http_request',
118
+ toolInput: '{"method":"GET","url":"https://api-b.example.com"}',
119
+ toolCallId: 'call-b',
120
120
  }, bag)
121
121
  collectToolEvent({
122
122
  t: 'tool_result',
123
- toolName: 'wallet_tool',
124
- toolOutput: '{"chain":"solana"}',
125
- toolCallId: 'call-sol',
123
+ toolName: 'http_request',
124
+ toolOutput: '{"source":"api-a"}',
125
+ toolCallId: 'call-a',
126
126
  }, bag)
127
127
  collectToolEvent({
128
128
  t: 'tool_result',
129
- toolName: 'wallet_tool',
130
- toolOutput: '{"chain":"ethereum"}',
131
- toolCallId: 'call-eth',
129
+ toolName: 'http_request',
130
+ toolOutput: '{"source":"api-b"}',
131
+ toolCallId: 'call-b',
132
132
  }, bag)
133
133
 
134
- assert.equal(bag[0].output, '{"chain":"solana"}')
135
- assert.equal(bag[1].output, '{"chain":"ethereum"}')
134
+ assert.equal(bag[0].output, '{"source":"api-a"}')
135
+ assert.equal(bag[1].output, '{"source":"api-b"}')
136
136
  })
137
137
  })
138
138
 
@@ -3,7 +3,6 @@ import type { MessageToolEvent, SSEEvent } from '@/types'
3
3
  export interface ExecuteChatTurnInput {
4
4
  sessionId: string
5
5
  message: string
6
- missionId?: string | null
7
6
  imagePath?: string
8
7
  imageUrl?: string
9
8
  attachedFiles?: string[]
@@ -27,7 +26,6 @@ export interface ExecuteChatTurnInput {
27
26
  export interface ExecuteChatTurnResult {
28
27
  runId?: string
29
28
  sessionId: string
30
- missionId?: string | null
31
29
  text: string
32
30
  persisted: boolean
33
31
  toolEvents: MessageToolEvent[]