@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
@@ -1,7 +1,6 @@
1
1
  import type { ProviderId, OllamaMode } from './provider'
2
2
  import type { ConnectorPlatform } from './connector'
3
3
  import type { Message } from './message'
4
- import type { MissionSummary } from './mission'
5
4
 
6
5
  export type SessionResetMode = 'idle' | 'daily' | 'isolated'
7
6
  export type SessionResetType = 'direct' | 'group' | 'thread' | 'main'
@@ -33,51 +32,6 @@ export interface SessionSkillRuntimeState {
33
32
  lastRunToolName?: string | null
34
33
  }
35
34
 
36
- export interface CanvasMetricItem {
37
- label: string
38
- value: string
39
- detail?: string
40
- tone?: 'default' | 'positive' | 'negative' | 'warning'
41
- }
42
-
43
- export interface CanvasCardItem {
44
- title: string
45
- body?: string
46
- meta?: string
47
- tone?: 'default' | 'positive' | 'negative' | 'warning'
48
- }
49
-
50
- export interface CanvasActionItem {
51
- label: string
52
- href?: string
53
- note?: string
54
- intent?: 'primary' | 'secondary' | 'success' | 'danger'
55
- }
56
-
57
- export interface CanvasTableData {
58
- columns: string[]
59
- rows: Array<Array<string | number | boolean | null>>
60
- caption?: string
61
- }
62
-
63
- export type CanvasBlock =
64
- | { type: 'markdown'; title?: string; markdown: string }
65
- | { type: 'metrics'; title?: string; items: CanvasMetricItem[] }
66
- | { type: 'cards'; title?: string; items: CanvasCardItem[] }
67
- | { type: 'table'; title?: string; table: CanvasTableData }
68
- | { type: 'code'; title?: string; code: string; language?: string }
69
- | { type: 'actions'; title?: string; items: CanvasActionItem[] }
70
-
71
- export interface CanvasDocument {
72
- kind: 'structured'
73
- title?: string
74
- subtitle?: string
75
- theme?: 'slate' | 'sky' | 'emerald' | 'amber' | 'rose'
76
- blocks: CanvasBlock[]
77
- updatedAt?: number | null
78
- }
79
-
80
- export type CanvasContent = string | CanvasDocument | null
81
35
 
82
36
  export interface MailboxEnvelope {
83
37
  id: string
@@ -204,8 +158,6 @@ export interface Session {
204
158
  lastSessionResetReason?: string | null
205
159
  identityState?: IdentityContinuityState | null
206
160
  sessionArchiveState?: SessionArchiveState | null
207
- missionId?: string | null
208
- missionSummary?: MissionSummary | null
209
161
  skillRuntimeState?: SessionSkillRuntimeState | null
210
162
  pinned?: boolean
211
163
  file?: string | null
@@ -217,7 +169,6 @@ export interface Session {
217
169
  vibe?: string
218
170
  theme?: string
219
171
  avatar?: string
220
- canvasContent?: CanvasContent
221
172
  /** Tracks how many times each memory ID has been injected via proactive recall in this session. */
222
173
  injectedMemoryIds?: Record<string, number>
223
174
  /** Structured working memory that survives compaction and flows through delegation. */
@@ -251,7 +202,6 @@ export type SessionTool =
251
202
  | 'edit_file'
252
203
  | 'process'
253
204
  | 'spawn_subagent'
254
- | 'canvas'
255
205
  | 'http_request'
256
206
  | 'git'
257
207
  | 'mailbox'
@@ -262,4 +212,4 @@ export type SessionTool =
262
212
  | 'crawl'
263
213
 
264
214
  export type SessionType = 'human'
265
- export type AppView = 'home' | 'agents' | 'org_chart' | 'inbox' | 'chatrooms' | 'protocols' | 'schedules' | 'memory' | 'missions' | 'tasks' | 'secrets' | 'providers' | 'skills' | 'connectors' | 'webhooks' | 'mcp_servers' | 'knowledge' | 'extensions' | 'usage' | 'wallets' | 'runs' | 'autonomy' | 'logs' | 'settings' | 'projects' | 'activity'
215
+ export type AppView = 'home' | 'agents' | 'org_chart' | 'inbox' | 'chatrooms' | 'protocols' | 'schedules' | 'memory' | 'tasks' | 'secrets' | 'wallets' | 'providers' | 'skills' | 'connectors' | 'webhooks' | 'mcp_servers' | 'knowledge' | 'extensions' | 'usage' | 'runs' | 'autonomy' | 'logs' | 'settings' | 'projects' | 'activity'
@@ -0,0 +1,29 @@
1
+ // --- SwarmDock Marketplace Types ---
2
+
3
+ export interface AgentWallet {
4
+ id: string
5
+ agentId: string
6
+ walletAddress: string
7
+ chain: 'base'
8
+ label?: string
9
+ encryptedPrivateKey?: string | null
10
+ spendingLimitUsdc?: string | null
11
+ dailyLimitUsdc?: string | null
12
+ requireApproval?: boolean
13
+ swarmdockAgentId?: string | null
14
+ swarmdockDid?: string | null
15
+ createdAt: number
16
+ }
17
+
18
+ export type SafeWallet = Omit<AgentWallet, 'encryptedPrivateKey'>
19
+
20
+ export interface WalletTransaction {
21
+ id: string
22
+ walletId: string
23
+ swarmdockTaskId?: string | null
24
+ amount: string
25
+ direction: 'in' | 'out'
26
+ txHash?: string | null
27
+ status: 'pending' | 'confirmed' | 'failed'
28
+ createdAt: number
29
+ }
package/src/types/task.ts CHANGED
@@ -1,4 +1,3 @@
1
- import type { MissionSummary } from './mission'
2
1
  import type { GoalContract } from './app-settings'
3
2
 
4
3
  // --- Task Board ---
@@ -28,9 +27,12 @@ export interface BoardTask {
28
27
  description: string
29
28
  status: BoardTaskStatus
30
29
  agentId: string
31
- missionId?: string | null
32
30
  protocolRunId?: string | null
33
- missionSummary?: MissionSummary | null
31
+ // Objective tracking (absorbed from missions)
32
+ objective?: string | null
33
+ successCriteria?: string[] | null
34
+ verificationSummary?: string | null
35
+ rootTaskId?: string | null
34
36
  projectId?: string
35
37
  goalContract?: GoalContract | null
36
38
  cwd?: string | null
@@ -137,4 +139,6 @@ export interface BoardTask {
137
139
  }>
138
140
  repairRunId?: string | null
139
141
  lastRepairAttemptAt?: number | null
142
+ // Atomic checkout — prevents two runners from starting the same task
143
+ checkoutRunId?: string | null
140
144
  }
@@ -1,18 +1,16 @@
1
- import type { MissionStatus, MissionPhase, MissionWaitState } from './mission'
2
1
 
3
2
  export type WorkingStateStatus = 'idle' | 'progress' | 'blocked' | 'waiting' | 'completed'
4
3
  export type WorkingStateItemStatus = 'active' | 'resolved' | 'superseded'
5
4
 
6
5
  export interface EvidenceRef {
7
6
  id: string
8
- type: 'tool' | 'message' | 'mission' | 'task' | 'artifact' | 'error' | 'approval'
7
+ type: 'tool' | 'message' | 'task' | 'artifact' | 'error' | 'approval'
9
8
  summary: string
10
9
  value?: string | null
11
10
  toolName?: string | null
12
11
  toolCallId?: string | null
13
12
  runId?: string | null
14
13
  sessionId?: string | null
15
- missionId?: string | null
16
14
  taskId?: string | null
17
15
  createdAt: number
18
16
  }
@@ -28,7 +26,7 @@ export interface WorkingPlanStep {
28
26
  export interface WorkingFact {
29
27
  id: string
30
28
  statement: string
31
- source: 'user' | 'tool' | 'assistant' | 'mission' | 'system'
29
+ source: 'user' | 'tool' | 'assistant' | 'system'
32
30
  status: WorkingStateItemStatus
33
31
  evidenceIds?: string[]
34
32
  createdAt: number
@@ -165,7 +163,6 @@ export interface WorkingStatePatch {
165
163
 
166
164
  export interface SessionWorkingState {
167
165
  sessionId: string
168
- missionId?: string | null
169
166
  objective?: string | null
170
167
  summary?: string | null
171
168
  constraints: string[]
@@ -192,7 +189,6 @@ export interface ExecutionBriefPlanStep {
192
189
 
193
190
  export interface ExecutionBrief {
194
191
  sessionId?: string | null
195
- missionId?: string | null
196
192
  objective: string | null
197
193
  summary: string | null
198
194
  status: WorkingStateStatus
@@ -203,9 +199,6 @@ export interface ExecutionBrief {
203
199
  artifacts: string[]
204
200
  constraints: string[]
205
201
  successCriteria: string[]
206
- missionStatus?: MissionStatus | null
207
- missionPhase?: MissionPhase | null
208
- waitState?: MissionWaitState | null
209
202
  evidenceRefs: EvidenceRef[]
210
203
  parentContext: string | null
211
204
  }
@@ -55,20 +55,6 @@ export function RuntimeLoopSection({ appSettings, patchSettings, inputClass }: S
55
55
  </div>
56
56
  </div>
57
57
 
58
- <label className="block font-display text-[11px] font-600 text-text-3 uppercase tracking-[0.08em] mb-3">Mission Human Loop</label>
59
- <div className="flex items-center gap-3 mb-6">
60
- <button
61
- onClick={() => patchSettings({ missionHumanLoopEnabled: !(appSettings.missionHumanLoopEnabled ?? false) })}
62
- className={`relative w-10 h-[22px] rounded-full transition-colors duration-200 cursor-pointer ${(appSettings.missionHumanLoopEnabled ?? false) ? 'bg-accent' : 'bg-white/[0.12]'}`}
63
- >
64
- <span className={`absolute top-[3px] left-[3px] w-4 h-4 rounded-full bg-white transition-transform duration-200 ${(appSettings.missionHumanLoopEnabled ?? false) ? 'translate-x-[18px]' : ''}`} />
65
- </button>
66
- <div>
67
- <div className="text-[12px] text-text-2">Allow missions to stay open waiting for a human follow-up</div>
68
- <div className="text-[11px] text-text-3/60 mt-1">Off by default for new users. When disabled, generic mission handoffs like “waiting for your next instruction” are closed instead of lingering as open waiting missions. Explicit tool approvals and real external blockers still apply.</div>
69
- </div>
70
- </div>
71
-
72
58
  <label className="flex items-center gap-1.5 font-display text-[11px] font-600 text-text-3 uppercase tracking-[0.08em] mb-3">Loop Mode <HintTip text="Bounded = fixed max steps. Ongoing = runs until the task completes (with a safety cap)" /></label>
73
59
  <div className="grid grid-cols-2 gap-2 mb-5">
74
60
  {([
@@ -1,35 +0,0 @@
1
- import { NextResponse } from 'next/server'
2
- import { loadSessions, saveSessions } from '@/lib/server/storage'
3
- import { notify } from '@/lib/server/ws-hub'
4
- import { normalizeCanvasContent } from '@/lib/canvas-content'
5
- import { safeParseBody } from '@/lib/server/safe-parse-body'
6
-
7
- export async function GET(_req: Request, { params }: { params: Promise<{ sessionId: string }> }) {
8
- const { sessionId } = await params
9
- const sessions = loadSessions()
10
- const session = sessions[sessionId]
11
- if (!session) return NextResponse.json({ error: 'Session not found' }, { status: 404 })
12
-
13
- return NextResponse.json({
14
- sessionId,
15
- content: (session as unknown as Record<string, unknown>).canvasContent || null,
16
- })
17
- }
18
-
19
- export async function POST(req: Request, { params }: { params: Promise<{ sessionId: string }> }) {
20
- const { sessionId } = await params
21
- const { data: body, error } = await safeParseBody(req)
22
- if (error) return error
23
- const sessions = loadSessions()
24
- const session = sessions[sessionId]
25
- if (!session) return NextResponse.json({ error: 'Session not found' }, { status: 404 })
26
-
27
- const nextContent = normalizeCanvasContent(body.document ?? body.content)
28
- ;(session as unknown as Record<string, unknown>).canvasContent = nextContent
29
- session.lastActiveAt = Date.now()
30
- sessions[sessionId] = session
31
- saveSessions(sessions)
32
-
33
- notify(`canvas:${sessionId}`)
34
- return NextResponse.json({ ok: true, sessionId })
35
- }
@@ -1,31 +0,0 @@
1
- import { NextResponse } from 'next/server'
2
- import { notFound } from '@/lib/server/collection-helpers'
3
- import { loadMissionById, performMissionAction } from '@/lib/server/missions/mission-service'
4
-
5
- export async function POST(req: Request, { params }: { params: Promise<{ id: string }> }) {
6
- const { id } = await params
7
- const mission = loadMissionById(id)
8
- if (!mission) return notFound()
9
-
10
- const body = await req.json().catch(() => ({}))
11
- const action = body?.action
12
- if (action !== 'resume' && action !== 'replan' && action !== 'cancel' && action !== 'retry_verification' && action !== 'wait') {
13
- return NextResponse.json({ error: 'Invalid mission action.' }, { status: 400 })
14
- }
15
-
16
- const result = performMissionAction({
17
- missionId: id,
18
- action,
19
- reason: typeof body.reason === 'string' ? body.reason : null,
20
- waitKind: typeof body.waitKind === 'string' ? body.waitKind : undefined,
21
- untilAt: typeof body.untilAt === 'number' ? body.untilAt : null,
22
- })
23
- if (!result) {
24
- return NextResponse.json({ error: 'Unable to update mission.' }, { status: 409 })
25
- }
26
- return NextResponse.json({
27
- ok: true,
28
- mission: result.mission,
29
- appendedEvent: result.event,
30
- })
31
- }
@@ -1,14 +0,0 @@
1
- import { NextResponse } from 'next/server'
2
- import { notFound } from '@/lib/server/collection-helpers'
3
- import { listMissionEventsForMission, loadMissionById } from '@/lib/server/missions/mission-service'
4
-
5
- export async function GET(req: Request, { params }: { params: Promise<{ id: string }> }) {
6
- const { id } = await params
7
- const mission = loadMissionById(id)
8
- if (!mission) return notFound()
9
-
10
- const { searchParams } = new URL(req.url)
11
- const limitParam = searchParams.get('limit')
12
- const limit = limitParam ? Number.parseInt(limitParam, 10) : undefined
13
- return NextResponse.json(listMissionEventsForMission(id, Number.isFinite(limit) ? limit : undefined))
14
- }
@@ -1,10 +0,0 @@
1
- import { NextResponse } from 'next/server'
2
- import { notFound } from '@/lib/server/collection-helpers'
3
- import { getMissionDetail } from '@/lib/server/missions/mission-service'
4
-
5
- export async function GET(_req: Request, { params }: { params: Promise<{ id: string }> }) {
6
- const { id } = await params
7
- const mission = getMissionDetail(id)
8
- if (!mission) return notFound()
9
- return NextResponse.json(mission)
10
- }
@@ -1,244 +0,0 @@
1
- import assert from 'node:assert/strict'
2
- import test from 'node:test'
3
-
4
- import { runWithTempDataDir } from '@/lib/server/test-utils/run-with-temp-data-dir'
5
-
6
- test('missions routes list, detail, and events expose durable mission state', () => {
7
- const output = runWithTempDataDir<{
8
- listCount: number
9
- firstMissionId: string | null
10
- detailMissionId: string | null
11
- linkedTaskId: string | null
12
- parentMissionId: string | null
13
- eventsCount: number
14
- latestEventType: string | null
15
- }>(`
16
- const storageMod = await import('./src/lib/server/storage')
17
- const listRouteMod = await import('./src/app/api/missions/route')
18
- const detailRouteMod = await import('./src/app/api/missions/[id]/route')
19
- const eventsRouteMod = await import('./src/app/api/missions/[id]/events/route')
20
- const storage = storageMod.default || storageMod
21
- const listRoute = listRouteMod.default || listRouteMod
22
- const detailRoute = detailRouteMod.default || detailRouteMod
23
- const eventsRoute = eventsRouteMod.default || eventsRouteMod
24
-
25
- storage.saveAgents({
26
- agentA: {
27
- id: 'agentA',
28
- name: 'Agent A',
29
- provider: 'ollama',
30
- model: 'test-model',
31
- systemPrompt: 'test',
32
- },
33
- })
34
-
35
- storage.saveTasks({
36
- taskA: {
37
- id: 'taskA',
38
- title: 'Prepare release summary',
39
- description: 'Create the release summary.',
40
- status: 'backlog',
41
- agentId: 'agentA',
42
- createdAt: 1,
43
- updatedAt: 1,
44
- missionId: 'missionA',
45
- },
46
- })
47
-
48
- storage.saveMissions({
49
- missionParent: {
50
- id: 'missionParent',
51
- source: 'chat',
52
- sourceRef: { kind: 'chat', sessionId: 'sessionA' },
53
- objective: 'Parent mission',
54
- status: 'active',
55
- phase: 'planning',
56
- sessionId: 'sessionA',
57
- agentId: 'agentA',
58
- taskIds: [],
59
- childMissionIds: ['missionA'],
60
- dependencyMissionIds: [],
61
- dependencyTaskIds: [],
62
- createdAt: 1,
63
- updatedAt: 1,
64
- },
65
- missionA: {
66
- id: 'missionA',
67
- source: 'chat',
68
- sourceRef: { kind: 'chat', sessionId: 'sessionA' },
69
- objective: 'Prepare the release handoff',
70
- status: 'waiting',
71
- phase: 'waiting',
72
- sessionId: 'sessionA',
73
- agentId: 'agentA',
74
- parentMissionId: 'missionParent',
75
- rootMissionId: 'missionParent',
76
- taskIds: ['taskA'],
77
- childMissionIds: [],
78
- dependencyMissionIds: [],
79
- dependencyTaskIds: [],
80
- waitState: { kind: 'approval', reason: 'Waiting for release approval.' },
81
- plannerSummary: 'Track the release handoff.',
82
- currentStep: 'Wait for approval',
83
- createdAt: 2,
84
- updatedAt: 3,
85
- },
86
- missionB: {
87
- id: 'missionB',
88
- source: 'manual',
89
- sourceRef: { kind: 'manual' },
90
- objective: 'Unrelated completed mission',
91
- status: 'completed',
92
- phase: 'completed',
93
- sessionId: 'sessionB',
94
- agentId: 'agentA',
95
- taskIds: [],
96
- childMissionIds: [],
97
- dependencyMissionIds: [],
98
- dependencyTaskIds: [],
99
- createdAt: 1,
100
- updatedAt: 1,
101
- },
102
- })
103
-
104
- storage.saveMissionEvents({
105
- eventCreated: {
106
- id: 'eventCreated',
107
- missionId: 'missionA',
108
- type: 'created',
109
- source: 'chat',
110
- summary: 'Mission created.',
111
- sessionId: 'sessionA',
112
- createdAt: 2,
113
- },
114
- eventWaiting: {
115
- id: 'eventWaiting',
116
- missionId: 'missionA',
117
- type: 'waiting',
118
- source: 'system',
119
- summary: 'Waiting for release approval.',
120
- sessionId: 'sessionA',
121
- createdAt: 3,
122
- },
123
- })
124
-
125
- const listResponse = await listRoute.GET(new Request('http://local/api/missions?status=waiting&sessionId=sessionA&limit=1'))
126
- const listPayload = await listResponse.json()
127
-
128
- const detailResponse = await detailRoute.GET(
129
- new Request('http://local/api/missions/missionA'),
130
- { params: Promise.resolve({ id: 'missionA' }) },
131
- )
132
- const detailPayload = await detailResponse.json()
133
-
134
- const eventsResponse = await eventsRoute.GET(
135
- new Request('http://local/api/missions/missionA/events?limit=1'),
136
- { params: Promise.resolve({ id: 'missionA' }) },
137
- )
138
- const eventsPayload = await eventsResponse.json()
139
-
140
- console.log(JSON.stringify({
141
- listCount: Array.isArray(listPayload) ? listPayload.length : -1,
142
- firstMissionId: Array.isArray(listPayload) ? listPayload[0]?.id || null : null,
143
- detailMissionId: detailPayload?.mission?.id || null,
144
- linkedTaskId: Array.isArray(detailPayload?.linkedTasks) ? detailPayload.linkedTasks[0]?.id || null : null,
145
- parentMissionId: detailPayload?.parent?.id || null,
146
- eventsCount: Array.isArray(eventsPayload) ? eventsPayload.length : -1,
147
- latestEventType: Array.isArray(eventsPayload) ? eventsPayload[0]?.type || null : null,
148
- }))
149
- `, { prefix: 'swarmclaw-missions-route-' })
150
-
151
- assert.equal(output.listCount, 1)
152
- assert.equal(output.firstMissionId, 'missionA')
153
- assert.equal(output.detailMissionId, 'missionA')
154
- assert.equal(output.linkedTaskId, 'taskA')
155
- assert.equal(output.parentMissionId, 'missionParent')
156
- assert.equal(output.eventsCount, 1)
157
- assert.equal(output.latestEventType, 'waiting')
158
- })
159
-
160
- test('mission actions route validates input and persists operator wait actions', () => {
161
- const output = runWithTempDataDir<{
162
- invalidStatus: number
163
- invalidError: string | null
164
- waitStatus: number
165
- waitOk: boolean
166
- missionStatus: string | null
167
- missionPhase: string | null
168
- waitKind: string | null
169
- waitReason: string | null
170
- eventType: string | null
171
- eventAction: string | null
172
- }>(`
173
- const storageMod = await import('./src/lib/server/storage')
174
- const actionsRouteMod = await import('./src/app/api/missions/[id]/actions/route')
175
- const storage = storageMod.default || storageMod
176
- const actionsRoute = actionsRouteMod.default || actionsRouteMod
177
-
178
- storage.saveMissions({
179
- missionA: {
180
- id: 'missionA',
181
- source: 'chat',
182
- sourceRef: { kind: 'chat', sessionId: 'sessionA' },
183
- objective: 'Prepare the release handoff',
184
- status: 'active',
185
- phase: 'planning',
186
- sessionId: 'sessionA',
187
- taskIds: [],
188
- childMissionIds: [],
189
- dependencyMissionIds: [],
190
- dependencyTaskIds: [],
191
- createdAt: 1,
192
- updatedAt: 1,
193
- },
194
- })
195
-
196
- const invalidResponse = await actionsRoute.POST(
197
- new Request('http://local/api/missions/missionA/actions', {
198
- method: 'POST',
199
- headers: { 'content-type': 'application/json' },
200
- body: JSON.stringify({ action: 'ship_it' }),
201
- }),
202
- { params: Promise.resolve({ id: 'missionA' }) },
203
- )
204
- const invalidPayload = await invalidResponse.json()
205
-
206
- const waitResponse = await actionsRoute.POST(
207
- new Request('http://local/api/missions/missionA/actions', {
208
- method: 'POST',
209
- headers: { 'content-type': 'application/json' },
210
- body: JSON.stringify({
211
- action: 'wait',
212
- reason: 'Waiting for operator confirmation.',
213
- waitKind: 'approval',
214
- }),
215
- }),
216
- { params: Promise.resolve({ id: 'missionA' }) },
217
- )
218
- const waitPayload = await waitResponse.json()
219
-
220
- console.log(JSON.stringify({
221
- invalidStatus: invalidResponse.status,
222
- invalidError: invalidPayload?.error || null,
223
- waitStatus: waitResponse.status,
224
- waitOk: waitPayload?.ok === true,
225
- missionStatus: waitPayload?.mission?.status || null,
226
- missionPhase: waitPayload?.mission?.phase || null,
227
- waitKind: waitPayload?.mission?.waitState?.kind || null,
228
- waitReason: waitPayload?.mission?.waitState?.reason || null,
229
- eventType: waitPayload?.appendedEvent?.type || null,
230
- eventAction: waitPayload?.appendedEvent?.data?.action || null,
231
- }))
232
- `, { prefix: 'swarmclaw-missions-route-' })
233
-
234
- assert.equal(output.invalidStatus, 400)
235
- assert.match(String(output.invalidError || ''), /invalid mission action/i)
236
- assert.equal(output.waitStatus, 200)
237
- assert.equal(output.waitOk, true)
238
- assert.equal(output.missionStatus, 'waiting')
239
- assert.equal(output.missionPhase, 'waiting')
240
- assert.equal(output.waitKind, 'approval')
241
- assert.equal(output.waitReason, 'Waiting for operator confirmation.')
242
- assert.equal(output.eventType, 'operator_action')
243
- assert.equal(output.eventAction, 'wait')
244
- })
@@ -1,57 +0,0 @@
1
- import { NextResponse } from 'next/server'
2
- import type { MissionPhase, MissionSource, MissionStatus } from '@/types'
3
- import { listMissions } from '@/lib/server/missions/mission-service'
4
-
5
- export const dynamic = 'force-dynamic'
6
-
7
- export async function GET(req: Request) {
8
- const { searchParams } = new URL(req.url)
9
- const sessionId = searchParams.get('sessionId')
10
- const agentId = searchParams.get('agentId')
11
- const projectId = searchParams.get('projectId')
12
- const parentMissionId = searchParams.get('parentMissionId')
13
- const limitParam = searchParams.get('limit')
14
- const rawStatus = searchParams.get('status')
15
- const rawPhase = searchParams.get('phase')
16
- const rawSource = searchParams.get('source')
17
- const limit = limitParam ? Number.parseInt(limitParam, 10) : undefined
18
- const status = rawStatus === 'non_terminal'
19
- || rawStatus === 'active'
20
- || rawStatus === 'waiting'
21
- || rawStatus === 'completed'
22
- || rawStatus === 'failed'
23
- || rawStatus === 'cancelled'
24
- ? rawStatus as MissionStatus | 'non_terminal'
25
- : undefined
26
- const phase = rawPhase === 'intake'
27
- || rawPhase === 'planning'
28
- || rawPhase === 'dispatching'
29
- || rawPhase === 'executing'
30
- || rawPhase === 'verifying'
31
- || rawPhase === 'waiting'
32
- || rawPhase === 'completed'
33
- || rawPhase === 'failed'
34
- ? rawPhase as MissionPhase
35
- : undefined
36
- const source = rawSource === 'chat'
37
- || rawSource === 'connector'
38
- || rawSource === 'heartbeat'
39
- || rawSource === 'main-loop-followup'
40
- || rawSource === 'task'
41
- || rawSource === 'schedule'
42
- || rawSource === 'delegation'
43
- || rawSource === 'manual'
44
- ? rawSource as MissionSource
45
- : undefined
46
-
47
- return NextResponse.json(listMissions({
48
- ...(sessionId ? { sessionId } : {}),
49
- ...(agentId ? { agentId } : {}),
50
- ...(projectId ? { projectId } : {}),
51
- ...(parentMissionId ? { parentMissionId } : {}),
52
- ...(status ? { status } : {}),
53
- ...(phase ? { phase } : {}),
54
- ...(source ? { source } : {}),
55
- ...(Number.isFinite(limit) ? { limit } : {}),
56
- }))
57
- }