@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,569 +0,0 @@
1
- import { HumanMessage } from '@langchain/core/messages'
2
- import { z } from 'zod'
3
- import type {
4
- Message,
5
- MessageToolEvent,
6
- Mission,
7
- MissionPhase,
8
- MissionSummary,
9
- MissionWaitKind,
10
- Session,
11
- SessionQueuedTurn,
12
- SessionRunRecord,
13
- } from '@/types'
14
- import { buildLLM } from '@/lib/server/build-llm'
15
-
16
- const MissionTurnDecisionSchema = z.object({
17
- action: z.enum(['none', 'attach_current', 'create_new']),
18
- confidence: z.number().min(0).max(1).optional(),
19
- objective: z.string().optional().nullable(),
20
- successCriteria: z.array(z.string()).optional(),
21
- currentStep: z.string().optional().nullable(),
22
- plannerSummary: z.string().optional().nullable(),
23
- })
24
-
25
- const MissionOutcomeSchema = z.object({
26
- verdict: z.enum(['continue', 'waiting', 'completed', 'failed', 'replan']),
27
- confidence: z.number().min(0).max(1).optional(),
28
- phase: z.enum(['planning', 'executing', 'verifying', 'waiting', 'completed', 'failed']).optional(),
29
- currentStep: z.string().optional().nullable(),
30
- verifierSummary: z.string().optional().nullable(),
31
- waitKind: z.enum(['human_reply', 'approval', 'external_dependency', 'provider', 'blocked_task', 'blocked_mission', 'scheduled', 'other']).optional(),
32
- waitReason: z.string().optional().nullable(),
33
- })
34
-
35
- const MissionPlannerSchema = z.object({
36
- decision: z.enum(['dispatch_task', 'dispatch_session_turn', 'spawn_child_mission', 'wait', 'verify_now', 'complete_candidate', 'replan', 'fail_terminal']),
37
- confidence: z.number().min(0).max(1).optional(),
38
- summary: z.string().optional().nullable(),
39
- currentStep: z.string().optional().nullable(),
40
- waitKind: z.enum(['human_reply', 'approval', 'external_dependency', 'provider', 'blocked_task', 'blocked_mission', 'scheduled', 'other']).optional(),
41
- waitReason: z.string().optional().nullable(),
42
- taskId: z.string().optional().nullable(),
43
- sessionMessage: z.string().optional().nullable(),
44
- childObjective: z.string().optional().nullable(),
45
- childSuccessCriteria: z.array(z.string()).optional(),
46
- childCurrentStep: z.string().optional().nullable(),
47
- childPlannerSummary: z.string().optional().nullable(),
48
- })
49
-
50
- export type MissionTurnDecision =
51
- | { action: 'none'; confidence: number }
52
- | {
53
- action: 'attach_current'
54
- confidence: number
55
- currentStep?: string
56
- plannerSummary?: string
57
- }
58
- | {
59
- action: 'create_new'
60
- confidence: number
61
- objective: string
62
- successCriteria: string[]
63
- currentStep?: string
64
- plannerSummary?: string
65
- }
66
-
67
- export type MissionOutcomeDecision = {
68
- verdict: 'continue' | 'waiting' | 'completed' | 'failed' | 'replan'
69
- confidence: number
70
- phase?: MissionPhase
71
- currentStep?: string
72
- verifierSummary?: string
73
- waitKind?: MissionWaitKind
74
- waitReason?: string
75
- }
76
-
77
- export type MissionPlannerDecisionResult =
78
- | {
79
- decision: 'dispatch_task'
80
- confidence: number
81
- summary?: string
82
- currentStep?: string
83
- taskId: string
84
- }
85
- | {
86
- decision: 'dispatch_session_turn'
87
- confidence: number
88
- summary?: string
89
- currentStep?: string
90
- sessionMessage: string
91
- }
92
- | {
93
- decision: 'spawn_child_mission'
94
- confidence: number
95
- summary?: string
96
- currentStep?: string
97
- childObjective: string
98
- childSuccessCriteria: string[]
99
- childCurrentStep?: string
100
- childPlannerSummary?: string
101
- }
102
- | {
103
- decision: 'wait'
104
- confidence: number
105
- summary?: string
106
- currentStep?: string
107
- waitKind?: MissionWaitKind
108
- waitReason?: string
109
- }
110
- | {
111
- decision: 'verify_now' | 'complete_candidate' | 'replan' | 'fail_terminal'
112
- confidence: number
113
- summary?: string
114
- currentStep?: string
115
- }
116
-
117
- export interface MissionTurnClassifierInput {
118
- sessionId: string
119
- agentId?: string | null
120
- message: string
121
- recentMessages?: Message[]
122
- currentMission?: MissionSummary | Mission | null
123
- session?: Session | null
124
- }
125
-
126
- export interface MissionOutcomeClassifierInput {
127
- sessionId: string
128
- agentId?: string | null
129
- userMessage: string
130
- assistantText?: string | null
131
- error?: string | null
132
- toolEvents?: MessageToolEvent[]
133
- currentMission: MissionSummary | Mission
134
- linkedTaskSummaries?: Array<{
135
- id: string
136
- title: string
137
- status: string
138
- result?: string | null
139
- error?: string | null
140
- }>
141
- }
142
-
143
- export interface MissionPlannerInput {
144
- sessionId: string
145
- agentId?: string | null
146
- mission: MissionSummary | Mission
147
- linkedTaskSummaries?: Array<{
148
- id: string
149
- title: string
150
- status: string
151
- result?: string | null
152
- error?: string | null
153
- }>
154
- childMissionSummaries?: MissionSummary[]
155
- recentRuns?: Array<Pick<SessionRunRecord, 'id' | 'status' | 'source' | 'queuedAt' | 'messagePreview' | 'resultPreview' | 'error'>>
156
- queuedTurns?: Array<Pick<SessionQueuedTurn, 'runId' | 'text' | 'queuedAt' | 'source'>>
157
- recentEvents?: Array<{
158
- type: string
159
- summary: string
160
- createdAt: number
161
- }>
162
- }
163
-
164
- function normalizeText(value: unknown, max = 400): string {
165
- return typeof value === 'string' ? value.replace(/\s+/g, ' ').trim().slice(0, max) : ''
166
- }
167
-
168
- function normalizeLines(values: unknown, maxItems: number, maxChars = 160): string[] {
169
- const source = Array.isArray(values) ? values : []
170
- const seen = new Set<string>()
171
- const out: string[] = []
172
- for (const value of source) {
173
- const normalized = normalizeText(value, maxChars)
174
- if (!normalized) continue
175
- const key = normalized.toLowerCase()
176
- if (seen.has(key)) continue
177
- seen.add(key)
178
- out.push(normalized)
179
- if (out.length >= maxItems) break
180
- }
181
- return out
182
- }
183
-
184
- function extractModelText(content: unknown): string {
185
- if (typeof content === 'string') return content
186
- if (!Array.isArray(content)) return ''
187
- return content
188
- .map((part) => (part && typeof part === 'object' && 'text' in part && typeof part.text === 'string') ? part.text : '')
189
- .join('')
190
- }
191
-
192
- function extractFirstJsonObject(text: string): string | null {
193
- const source = normalizeText(text, 12_000)
194
- if (!source) return null
195
- let start = -1
196
- let depth = 0
197
- let inString = false
198
- let escaped = false
199
- for (let index = 0; index < source.length; index += 1) {
200
- const char = source[index]
201
- if (start === -1) {
202
- if (char === '{') {
203
- start = index
204
- depth = 1
205
- }
206
- continue
207
- }
208
- if (inString) {
209
- if (escaped) escaped = false
210
- else if (char === '\\') escaped = true
211
- else if (char === '"') inString = false
212
- continue
213
- }
214
- if (char === '"') {
215
- inString = true
216
- continue
217
- }
218
- if (char === '{') depth += 1
219
- else if (char === '}') depth -= 1
220
- if (depth === 0) return source.slice(start, index + 1)
221
- }
222
- return null
223
- }
224
-
225
- function parseJsonObject(text: string): Record<string, unknown> | null {
226
- try {
227
- const parsed = JSON.parse(text) as unknown
228
- if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) return null
229
- return parsed as Record<string, unknown>
230
- } catch {
231
- return null
232
- }
233
- }
234
-
235
- export function parseMissionTurnDecision(text: string): MissionTurnDecision | null {
236
- const jsonText = extractFirstJsonObject(text)
237
- if (!jsonText) return null
238
- const jsonValue = parseJsonObject(jsonText)
239
- if (!jsonValue) return null
240
- const parsed = MissionTurnDecisionSchema.safeParse(jsonValue)
241
- if (!parsed.success) return null
242
- const confidence = typeof parsed.data.confidence === 'number' ? parsed.data.confidence : 0
243
- if (parsed.data.action === 'none') return { action: 'none', confidence }
244
- if (parsed.data.action === 'attach_current') {
245
- return {
246
- action: 'attach_current',
247
- confidence,
248
- ...(normalizeText(parsed.data.currentStep, 200) ? { currentStep: normalizeText(parsed.data.currentStep, 200) } : {}),
249
- ...(normalizeText(parsed.data.plannerSummary, 320) ? { plannerSummary: normalizeText(parsed.data.plannerSummary, 320) } : {}),
250
- }
251
- }
252
- const objective = normalizeText(parsed.data.objective, 300)
253
- if (!objective) return null
254
- return {
255
- action: 'create_new',
256
- confidence,
257
- objective,
258
- successCriteria: normalizeLines(parsed.data.successCriteria, 6, 180),
259
- ...(normalizeText(parsed.data.currentStep, 200) ? { currentStep: normalizeText(parsed.data.currentStep, 200) } : {}),
260
- ...(normalizeText(parsed.data.plannerSummary, 320) ? { plannerSummary: normalizeText(parsed.data.plannerSummary, 320) } : {}),
261
- }
262
- }
263
-
264
- export function parseMissionOutcomeDecision(text: string): MissionOutcomeDecision | null {
265
- const jsonText = extractFirstJsonObject(text)
266
- if (!jsonText) return null
267
- const jsonValue = parseJsonObject(jsonText)
268
- if (!jsonValue) return null
269
- const parsed = MissionOutcomeSchema.safeParse(jsonValue)
270
- if (!parsed.success) return null
271
- return {
272
- verdict: parsed.data.verdict,
273
- confidence: typeof parsed.data.confidence === 'number' ? parsed.data.confidence : 0,
274
- ...(parsed.data.phase ? { phase: parsed.data.phase } : {}),
275
- ...(normalizeText(parsed.data.currentStep, 200) ? { currentStep: normalizeText(parsed.data.currentStep, 200) } : {}),
276
- ...(normalizeText(parsed.data.verifierSummary, 360) ? { verifierSummary: normalizeText(parsed.data.verifierSummary, 360) } : {}),
277
- ...(parsed.data.waitKind ? { waitKind: parsed.data.waitKind } : {}),
278
- ...(normalizeText(parsed.data.waitReason, 240) ? { waitReason: normalizeText(parsed.data.waitReason, 240) } : {}),
279
- }
280
- }
281
-
282
- export function parseMissionPlannerDecision(text: string): MissionPlannerDecisionResult | null {
283
- const jsonText = extractFirstJsonObject(text)
284
- if (!jsonText) return null
285
- const jsonValue = parseJsonObject(jsonText)
286
- if (!jsonValue) return null
287
- const parsed = MissionPlannerSchema.safeParse(jsonValue)
288
- if (!parsed.success) return null
289
- const confidence = typeof parsed.data.confidence === 'number' ? parsed.data.confidence : 0
290
- const summary = normalizeText(parsed.data.summary, 360) || undefined
291
- const currentStep = normalizeText(parsed.data.currentStep, 200) || undefined
292
-
293
- if (parsed.data.decision === 'dispatch_task') {
294
- const taskId = normalizeText(parsed.data.taskId, 64)
295
- if (!taskId) return null
296
- return {
297
- decision: 'dispatch_task',
298
- confidence,
299
- ...(summary ? { summary } : {}),
300
- ...(currentStep ? { currentStep } : {}),
301
- taskId,
302
- }
303
- }
304
-
305
- if (parsed.data.decision === 'dispatch_session_turn') {
306
- const sessionMessage = normalizeText(parsed.data.sessionMessage, 1600)
307
- if (!sessionMessage) return null
308
- return {
309
- decision: 'dispatch_session_turn',
310
- confidence,
311
- ...(summary ? { summary } : {}),
312
- ...(currentStep ? { currentStep } : {}),
313
- sessionMessage,
314
- }
315
- }
316
-
317
- if (parsed.data.decision === 'spawn_child_mission') {
318
- const childObjective = normalizeText(parsed.data.childObjective, 300)
319
- if (!childObjective) return null
320
- return {
321
- decision: 'spawn_child_mission',
322
- confidence,
323
- ...(summary ? { summary } : {}),
324
- ...(currentStep ? { currentStep } : {}),
325
- childObjective,
326
- childSuccessCriteria: normalizeLines(parsed.data.childSuccessCriteria, 6, 180),
327
- ...(normalizeText(parsed.data.childCurrentStep, 200) ? { childCurrentStep: normalizeText(parsed.data.childCurrentStep, 200) } : {}),
328
- ...(normalizeText(parsed.data.childPlannerSummary, 320) ? { childPlannerSummary: normalizeText(parsed.data.childPlannerSummary, 320) } : {}),
329
- }
330
- }
331
-
332
- if (parsed.data.decision === 'wait') {
333
- return {
334
- decision: 'wait',
335
- confidence,
336
- ...(summary ? { summary } : {}),
337
- ...(currentStep ? { currentStep } : {}),
338
- ...(parsed.data.waitKind ? { waitKind: parsed.data.waitKind } : {}),
339
- ...(normalizeText(parsed.data.waitReason, 240) ? { waitReason: normalizeText(parsed.data.waitReason, 240) } : {}),
340
- }
341
- }
342
-
343
- return {
344
- decision: parsed.data.decision,
345
- confidence,
346
- ...(summary ? { summary } : {}),
347
- ...(currentStep ? { currentStep } : {}),
348
- }
349
- }
350
-
351
- function buildRecentMessageContext(messages: Message[] | undefined): string {
352
- if (!Array.isArray(messages) || messages.length === 0) return '(none)'
353
- return messages.slice(-6).map((message) => {
354
- const role = message.role === 'assistant' ? 'assistant' : 'user'
355
- return `- ${role}: ${JSON.stringify(normalizeText(message.text, 220) || '(empty)')}`
356
- }).join('\n')
357
- }
358
-
359
- function summarizeMission(mission: MissionSummary | Mission | null | undefined): string {
360
- if (!mission) return '(none)'
361
- const rawSuccessCriteria = 'successCriteria' in mission ? mission.successCriteria : undefined
362
- const successCriteria = Array.isArray(rawSuccessCriteria) && rawSuccessCriteria.length > 0
363
- ? rawSuccessCriteria.join(' | ')
364
- : '(none)'
365
- return [
366
- `objective=${JSON.stringify(normalizeText(mission.objective, 260) || '(none)')}`,
367
- `status=${mission.status}`,
368
- `phase=${mission.phase}`,
369
- `current_step=${JSON.stringify(normalizeText(mission.currentStep, 160) || '(none)')}`,
370
- `success_criteria=${JSON.stringify(successCriteria)}`,
371
- ].join('\n')
372
- }
373
-
374
- function summarizeToolEvents(toolEvents: MessageToolEvent[] | undefined): string {
375
- if (!Array.isArray(toolEvents) || toolEvents.length === 0) return '(none)'
376
- return toolEvents.slice(0, 8).map((event) => JSON.stringify({
377
- name: normalizeText(event.name, 120) || 'unknown',
378
- input: normalizeText(event.input, 160) || null,
379
- output: normalizeText(event.output, 220) || null,
380
- error: event.error === true,
381
- })).join('\n')
382
- }
383
-
384
- function summarizeLinkedTasks(input: MissionOutcomeClassifierInput['linkedTaskSummaries']): string {
385
- if (!Array.isArray(input) || input.length === 0) return '(none)'
386
- return input.slice(0, 8).map((task) => JSON.stringify({
387
- id: task.id,
388
- title: normalizeText(task.title, 160),
389
- status: task.status,
390
- result: normalizeText(task.result, 180) || null,
391
- error: normalizeText(task.error, 180) || null,
392
- })).join('\n')
393
- }
394
-
395
- function summarizeChildMissions(children: MissionSummary[] | undefined): string {
396
- if (!Array.isArray(children) || children.length === 0) return '(none)'
397
- return children.slice(0, 8).map((child) => JSON.stringify({
398
- id: child.id,
399
- objective: normalizeText(child.objective, 180),
400
- status: child.status,
401
- phase: child.phase,
402
- currentStep: normalizeText(child.currentStep, 140) || null,
403
- waitingReason: normalizeText(child.waitingReason, 160) || null,
404
- })).join('\n')
405
- }
406
-
407
- function summarizeRecentRuns(runs: MissionPlannerInput['recentRuns']): string {
408
- if (!Array.isArray(runs) || runs.length === 0) return '(none)'
409
- return runs.slice(0, 6).map((run) => JSON.stringify({
410
- id: run.id,
411
- status: run.status,
412
- source: run.source,
413
- queuedAt: run.queuedAt,
414
- messagePreview: normalizeText(run.messagePreview, 180) || null,
415
- resultPreview: normalizeText(run.resultPreview, 180) || null,
416
- error: normalizeText(run.error, 160) || null,
417
- })).join('\n')
418
- }
419
-
420
- function summarizeQueuedTurns(turns: MissionPlannerInput['queuedTurns']): string {
421
- if (!Array.isArray(turns) || turns.length === 0) return '(none)'
422
- return turns.slice(0, 6).map((turn) => JSON.stringify({
423
- runId: turn.runId,
424
- source: turn.source || 'chat',
425
- queuedAt: turn.queuedAt,
426
- text: normalizeText(turn.text, 220) || '(empty)',
427
- })).join('\n')
428
- }
429
-
430
- function summarizeRecentEvents(events: MissionPlannerInput['recentEvents']): string {
431
- if (!Array.isArray(events) || events.length === 0) return '(none)'
432
- return events.slice(-8).map((event) => JSON.stringify({
433
- type: event.type,
434
- summary: normalizeText(event.summary, 220),
435
- createdAt: event.createdAt,
436
- })).join('\n')
437
- }
438
-
439
- function buildMissionPlannerPrompt(input: MissionPlannerInput): string {
440
- const verification = 'verificationState' in input.mission ? input.mission.verificationState : undefined
441
- return [
442
- 'Decide the next durable mission-controller action.',
443
- 'Return JSON only.',
444
- '',
445
- 'Controller rules:',
446
- '- Choose exactly one decision.',
447
- '- Use "dispatch_task" only when there is a specific linked backlog task to queue now. You must provide taskId.',
448
- '- Use "dispatch_session_turn" when the next durable step should run as one mission-linked follow-up turn in the existing session. Provide sessionMessage.',
449
- '- Use "spawn_child_mission" only when the work should split into a durable child objective with its own lifecycle.',
450
- '- Use "wait" only for a real blocker, dependency, approval, human reply, provider outage, or scheduled resume. Provide waitKind and waitReason.',
451
- '- Use "verify_now" when the existing durable evidence is sufficient to verify completion without more execution.',
452
- '- Use "complete_candidate" only when the objective appears complete and should move into verification next.',
453
- '- Use "replan" when the mission should stay active but you do not want to dispatch work from this tick. Update currentStep or summary if helpful.',
454
- '- Use "fail_terminal" only for a genuine terminal failure that should stop the mission.',
455
- '- Do not mark work complete based on promises, planning prose, or partial progress.',
456
- '',
457
- 'Output shape:',
458
- '{"decision":"dispatch_task|dispatch_session_turn|spawn_child_mission|wait|verify_now|complete_candidate|replan|fail_terminal","confidence":0-1,"summary":"optional","currentStep":"optional","taskId":"required for dispatch_task","sessionMessage":"required for dispatch_session_turn","waitKind":"required for wait","waitReason":"required for wait","childObjective":"required for spawn_child_mission","childSuccessCriteria":["optional"],"childCurrentStep":"optional","childPlannerSummary":"optional"}',
459
- '',
460
- `mission:\n${summarizeMission(input.mission)}`,
461
- verification ? `verification:\n${JSON.stringify({
462
- candidate: verification.candidate === true,
463
- requiredTaskIds: verification.requiredTaskIds || [],
464
- requiredChildMissionIds: verification.requiredChildMissionIds || [],
465
- requiredArtifacts: verification.requiredArtifacts || [],
466
- evidenceSummary: normalizeText(verification.evidenceSummary, 220) || null,
467
- lastVerdict: verification.lastVerdict || null,
468
- })}` : 'verification:\n(none)',
469
- `linked_tasks:\n${summarizeLinkedTasks(input.linkedTaskSummaries)}`,
470
- `child_missions:\n${summarizeChildMissions(input.childMissionSummaries)}`,
471
- `recent_runs:\n${summarizeRecentRuns(input.recentRuns)}`,
472
- `queued_turns:\n${summarizeQueuedTurns(input.queuedTurns)}`,
473
- `recent_events:\n${summarizeRecentEvents(input.recentEvents)}`,
474
- ].join('\n')
475
- }
476
-
477
- function buildMissionTurnPrompt(input: MissionTurnClassifierInput): string {
478
- return [
479
- 'Classify whether the latest user turn should use durable mission tracking.',
480
- 'Return JSON only.',
481
- '',
482
- 'Mission policy:',
483
- '- Choose "create_new" only when the request is clearly multi-step, durable, resumable, deliverable-oriented, or likely to require follow-up action.',
484
- '- A small request that can be fully satisfied in the current turn should usually stay ordinary chat, even if it writes a file, produces an artifact, or could have future follow-up.',
485
- '- Do not create a mission just because the user might say more later. Unspecified future instructions alone are not durable mission state.',
486
- '- Choose "attach_current" when the new user turn is a continuation, refinement, correction, or next step for the current mission.',
487
- '- Choose "none" for one-shot questions, casual chat, simple factual replies, or turns that should not create durable execution state.',
488
- '- Be conservative. If the turn is not clearly mission-worthy, return {"action":"none","confidence":0}.',
489
- '- For "create_new", provide a concise durable objective, up to 6 success criteria, and an optional currentStep/plannerSummary.',
490
- '- For "attach_current", optionally provide a better currentStep/plannerSummary if the new turn changes the immediate next step.',
491
- '- Never rely on literal wording like "continue" alone. Decide from intent and context.',
492
- '',
493
- 'Output shape:',
494
- '{"action":"none|attach_current|create_new","confidence":0-1,"objective":"for create_new","successCriteria":["optional"],"currentStep":"optional","plannerSummary":"optional"}',
495
- '',
496
- `current_mission:\n${summarizeMission(input.currentMission)}`,
497
- `recent_messages:\n${buildRecentMessageContext(input.recentMessages)}`,
498
- `latest_user_message: ${JSON.stringify(normalizeText(input.message, 500) || '(empty)')}`,
499
- ].join('\n')
500
- }
501
-
502
- function buildMissionOutcomePrompt(input: MissionOutcomeClassifierInput): string {
503
- return [
504
- 'Evaluate the latest mission-scoped run outcome and decide the durable mission state.',
505
- 'Return JSON only.',
506
- '',
507
- 'Rules:',
508
- '- Choose "completed" only when the mission objective is actually satisfied by the latest work or the linked tasks are clearly complete.',
509
- '- Choose "waiting" when progress is blocked on a real external dependency, approval, human reply, provider outage, blocked task, or scheduled future action.',
510
- '- Choose "replan" when the mission should remain active but the next controller step needs to change before more work is dispatched.',
511
- '- Choose "failed" only for a real terminal failure that should stop the mission instead of waiting or continuing.',
512
- '- Choose "continue" for incomplete but still actionable work.',
513
- '- Be conservative about completion. Planning, partial edits, vague promises, or "I will" language are not completion.',
514
- '- Use the linked task summaries and tool results as stronger evidence than conversational tone.',
515
- '- Provide a short verifierSummary and an optional currentStep.',
516
- '',
517
- 'Output shape:',
518
- '{"verdict":"continue|waiting|completed|failed|replan","confidence":0-1,"phase":"planning|executing|verifying|waiting|completed|failed","currentStep":"optional","verifierSummary":"optional","waitKind":"human_reply|approval|external_dependency|provider|blocked_task|blocked_mission|scheduled|other","waitReason":"optional"}',
519
- '',
520
- `mission:\n${summarizeMission(input.currentMission)}`,
521
- `linked_tasks:\n${summarizeLinkedTasks(input.linkedTaskSummaries)}`,
522
- `user_message: ${JSON.stringify(normalizeText(input.userMessage, 500) || '(empty)')}`,
523
- `assistant_text: ${JSON.stringify(normalizeText(input.assistantText, 1200) || '(none)')}`,
524
- `assistant_error: ${JSON.stringify(normalizeText(input.error, 320) || '(none)')}`,
525
- `tool_events:\n${summarizeToolEvents(input.toolEvents)}`,
526
- ].join('\n')
527
- }
528
-
529
- async function generateClassifierText(sessionId: string, agentId: string | null | undefined, prompt: string): Promise<string> {
530
- const { llm } = await buildLLM({
531
- sessionId,
532
- agentId: agentId || null,
533
- })
534
- const response = await llm.invoke([new HumanMessage(prompt)])
535
- return extractModelText(response.content)
536
- }
537
-
538
- export async function classifyMissionTurn(
539
- input: MissionTurnClassifierInput,
540
- options?: { generateText?: (prompt: string) => Promise<string> },
541
- ): Promise<MissionTurnDecision | null> {
542
- const prompt = buildMissionTurnPrompt(input)
543
- const responseText = options?.generateText
544
- ? await options.generateText(prompt)
545
- : await generateClassifierText(input.sessionId, input.agentId, prompt)
546
- return parseMissionTurnDecision(responseText)
547
- }
548
-
549
- export async function verifyMissionOutcome(
550
- input: MissionOutcomeClassifierInput,
551
- options?: { generateText?: (prompt: string) => Promise<string> },
552
- ): Promise<MissionOutcomeDecision | null> {
553
- const prompt = buildMissionOutcomePrompt(input)
554
- const responseText = options?.generateText
555
- ? await options.generateText(prompt)
556
- : await generateClassifierText(input.sessionId, input.agentId, prompt)
557
- return parseMissionOutcomeDecision(responseText)
558
- }
559
-
560
- export async function planMissionTick(
561
- input: MissionPlannerInput,
562
- options?: { generateText?: (prompt: string) => Promise<string> },
563
- ): Promise<MissionPlannerDecisionResult | null> {
564
- const prompt = buildMissionPlannerPrompt(input)
565
- const responseText = options?.generateText
566
- ? await options.generateText(prompt)
567
- : await generateClassifierText(input.sessionId, input.agentId, prompt)
568
- return parseMissionPlannerDecision(responseText)
569
- }
@@ -1,74 +0,0 @@
1
- import type { Mission, MissionEvent } from '@/types'
2
-
3
- import {
4
- deleteMission as deleteStoredMission,
5
- loadMission as loadStoredMission,
6
- loadMissionEvent as loadStoredMissionEvent,
7
- loadMissionEvents as loadStoredMissionEvents,
8
- loadMissions as loadStoredMissions,
9
- patchMission as patchStoredMission,
10
- saveMissionEvents as saveStoredMissionEvents,
11
- saveMissions as saveStoredMissions,
12
- upsertMission as upsertStoredMission,
13
- upsertMissionEvent as upsertStoredMissionEvent,
14
- upsertMissionEvents as upsertStoredMissionEvents,
15
- } from '@/lib/server/storage'
16
- import { createRecordRepository } from '@/lib/server/persistence/repository-utils'
17
-
18
- export const missionRepository = createRecordRepository<Mission>(
19
- 'missions',
20
- {
21
- get(id) {
22
- return loadStoredMission(id) as Mission | null
23
- },
24
- list() {
25
- return loadStoredMissions() as Record<string, Mission>
26
- },
27
- upsert(id, value) {
28
- upsertStoredMission(id, value as Mission)
29
- },
30
- replace(data) {
31
- saveStoredMissions(data as Record<string, Mission>)
32
- },
33
- patch(id, updater) {
34
- return patchStoredMission(id, updater as (current: Mission | null) => Mission | null) as Mission | null
35
- },
36
- delete(id) {
37
- deleteStoredMission(id)
38
- },
39
- },
40
- )
41
-
42
- export const missionEventRepository = createRecordRepository<MissionEvent>(
43
- 'mission-events',
44
- {
45
- get(id) {
46
- return loadStoredMissionEvent(id) as MissionEvent | null
47
- },
48
- list() {
49
- return loadStoredMissionEvents() as Record<string, MissionEvent>
50
- },
51
- upsert(id, value) {
52
- upsertStoredMissionEvent(id, value as MissionEvent)
53
- },
54
- upsertMany(entries) {
55
- upsertStoredMissionEvents(entries as Array<[string, MissionEvent]>)
56
- },
57
- replace(data) {
58
- saveStoredMissionEvents(data as Record<string, MissionEvent>)
59
- },
60
- },
61
- )
62
-
63
- export const loadMissions = () => missionRepository.list()
64
- export const loadMission = (id: string) => missionRepository.get(id)
65
- export const saveMissions = (items: Record<string, Mission | Record<string, unknown>>) => missionRepository.replace(items as Record<string, Mission>)
66
- export const upsertMission = (id: string, value: Mission | Record<string, unknown>) => missionRepository.upsert(id, value as Mission)
67
- export const patchMission = (id: string, updater: (current: Mission | null) => Mission | null) => missionRepository.patch(id, updater)
68
- export const deleteMission = (id: string) => missionRepository.delete(id)
69
-
70
- export const loadMissionEvents = () => missionEventRepository.list()
71
- export const loadMissionEvent = (id: string) => missionEventRepository.get(id)
72
- export const saveMissionEvents = (items: Record<string, MissionEvent | Record<string, unknown>>) => missionEventRepository.replace(items as Record<string, MissionEvent>)
73
- export const upsertMissionEvent = (id: string, value: MissionEvent | Record<string, unknown>) => missionEventRepository.upsert(id, value as MissionEvent)
74
- export const upsertMissionEvents = (entries: Array<[string, MissionEvent | Record<string, unknown>]>) => missionEventRepository.upsertMany(entries as Array<[string, MissionEvent]>)
@@ -1,6 +0,0 @@
1
- export {
2
- appendMissionEvent,
3
- ensureDelegationMission,
4
- performMissionAction,
5
- syncDelegationMissionFromJob,
6
- } from './core'
@@ -1,9 +0,0 @@
1
- export {
2
- bindMissionToSession,
3
- bindMissionToTask,
4
- ensureMissionForSchedule,
5
- ensureMissionForTask,
6
- noteMissionTaskFinished,
7
- noteMissionTaskStarted,
8
- noteScheduleMissionTriggered,
9
- } from './core'
@@ -1,4 +0,0 @@
1
- export {
2
- buildMissionContextBlock,
3
- buildMissionHeartbeatPrompt,
4
- } from './core'