@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.
- package/README.md +30 -6
- package/package.json +2 -2
- package/src/app/agents/[id]/page.tsx +1 -18
- package/src/app/api/agents/thread-route.test.ts +0 -1
- package/src/app/api/approvals/route.test.ts +6 -22
- package/src/app/api/connectors/route.ts +2 -2
- package/src/app/api/portability/export/route.ts +8 -0
- package/src/app/api/portability/import/route.test.ts +80 -0
- package/src/app/api/portability/import/route.ts +28 -0
- package/src/app/api/settings/route.ts +0 -2
- package/src/app/api/wallets/[id]/route.ts +15 -157
- package/src/app/api/wallets/generate/route.ts +22 -0
- package/src/app/api/wallets/route.test.ts +147 -0
- package/src/app/api/wallets/route.ts +13 -95
- package/src/app/autonomy/page.tsx +2 -57
- package/src/app/protocols/page.tsx +2 -21
- package/src/app/settings/page.tsx +0 -9
- package/src/app/wallets/page.tsx +105 -5
- package/src/cli/index.js +21 -33
- package/src/cli/spec.js +19 -30
- package/src/components/agents/agent-sheet.tsx +2 -40
- package/src/components/agents/inspector-panel.tsx +0 -83
- package/src/components/chat/chat-card.tsx +0 -31
- package/src/components/chat/message-bubble.tsx +1 -108
- package/src/components/connectors/connector-sheet.tsx +25 -1
- package/src/components/layout/sidebar-rail.tsx +6 -10
- package/src/components/projects/project-detail.tsx +3 -35
- package/src/components/projects/tabs/overview-tab.tsx +3 -59
- package/src/components/projects/tabs/work-tab.tsx +7 -77
- package/src/components/protocols/structured-session-launcher.tsx +1 -22
- package/src/components/shared/connector-platform-icon.tsx +1 -0
- package/src/components/tasks/task-card.tsx +4 -34
- package/src/components/tasks/task-sheet.tsx +6 -36
- package/src/components/wallets/wallet-list.tsx +150 -0
- package/src/lib/app/navigation.test.ts +0 -13
- package/src/lib/app/navigation.ts +2 -7
- package/src/lib/app/view-constants.ts +14 -19
- package/src/lib/server/agents/agent-thread-session.ts +0 -1
- package/src/lib/server/agents/delegation-advisory.test.ts +0 -1
- package/src/lib/server/agents/delegation-jobs.test.ts +0 -69
- package/src/lib/server/agents/delegation-jobs.ts +0 -25
- package/src/lib/server/agents/main-agent-loop.ts +1 -49
- package/src/lib/server/agents/subagent-runtime.ts +0 -1
- package/src/lib/server/approval-match.ts +0 -85
- package/src/lib/server/approvals.test.ts +6 -6
- package/src/lib/server/approvals.ts +0 -6
- package/src/lib/server/autonomy/supervisor-reflection.test.ts +0 -1
- package/src/lib/server/builtin-extensions.ts +0 -2
- package/src/lib/server/capability-router.test.ts +0 -2
- package/src/lib/server/chat-execution/chat-execution-tool-events.test.ts +14 -14
- package/src/lib/server/chat-execution/chat-execution-types.ts +0 -2
- package/src/lib/server/chat-execution/chat-execution-utils.ts +0 -2
- package/src/lib/server/chat-execution/chat-streaming-utils.ts +2 -30
- package/src/lib/server/chat-execution/chat-turn-finalization.ts +1 -36
- package/src/lib/server/chat-execution/chat-turn-preparation.ts +2 -22
- package/src/lib/server/chat-execution/iteration-event-handler.ts +0 -24
- package/src/lib/server/chat-execution/message-classifier.test.ts +0 -45
- package/src/lib/server/chat-execution/message-classifier.ts +1 -16
- package/src/lib/server/chat-execution/prompt-builder.test.ts +0 -1
- package/src/lib/server/chat-execution/prompt-builder.ts +0 -30
- package/src/lib/server/chat-execution/prompt-sections.ts +0 -1
- package/src/lib/server/chat-execution/situational-awareness.test.ts +2 -73
- package/src/lib/server/chat-execution/situational-awareness.ts +4 -38
- package/src/lib/server/chat-execution/stream-agent-chat.test.ts +8 -123
- package/src/lib/server/chat-execution/stream-agent-chat.ts +1 -5
- package/src/lib/server/chat-execution/stream-continuation.test.ts +4 -52
- package/src/lib/server/chat-execution/stream-continuation.ts +6 -48
- package/src/lib/server/chatrooms/session-mailbox.ts +0 -10
- package/src/lib/server/chats/chat-session-service.ts +3 -5
- package/src/lib/server/connectors/connector-inbound.ts +0 -1
- package/src/lib/server/connectors/connector-lifecycle.ts +19 -3
- package/src/lib/server/connectors/connector-service.ts +39 -9
- package/src/lib/server/connectors/swarmdock-bidding.ts +74 -0
- package/src/lib/server/connectors/swarmdock-payloads.test.ts +85 -0
- package/src/lib/server/connectors/swarmdock-secret.test.ts +128 -0
- package/src/lib/server/connectors/swarmdock-secret.ts +152 -0
- package/src/lib/server/connectors/swarmdock-tasks.ts +119 -0
- package/src/lib/server/connectors/swarmdock.ts +255 -0
- package/src/lib/server/execution-brief.test.ts +2 -25
- package/src/lib/server/execution-brief.ts +12 -35
- package/src/lib/server/execution-engine/task-attempt.ts +0 -1
- package/src/lib/server/persistence/storage-context.ts +0 -5
- package/src/lib/server/portability/export.ts +109 -0
- package/src/lib/server/portability/import.ts +159 -0
- package/src/lib/server/protocols/protocol-normalization.ts +0 -4
- package/src/lib/server/protocols/protocol-queries.ts +0 -6
- package/src/lib/server/protocols/protocol-run-lifecycle.ts +4 -32
- package/src/lib/server/protocols/protocol-service.ts +0 -1
- package/src/lib/server/protocols/protocol-step-helpers.ts +0 -4
- package/src/lib/server/protocols/protocol-step-processors.ts +0 -6
- package/src/lib/server/protocols/protocol-swarm.ts +0 -2
- package/src/lib/server/protocols/protocol-types.ts +0 -2
- package/src/lib/server/provider-health.ts +0 -9
- package/src/lib/server/runtime/daemon-state/core.ts +0 -9
- package/src/lib/server/runtime/daemon-state.test.ts +0 -35
- package/src/lib/server/runtime/heartbeat-service.ts +3 -23
- package/src/lib/server/runtime/queue/core.ts +11 -33
- package/src/lib/server/runtime/runtime-storage-write-paths.test.ts +6 -6
- package/src/lib/server/runtime/scheduler.ts +0 -13
- package/src/lib/server/runtime/session-run-manager/drain.ts +0 -24
- package/src/lib/server/runtime/session-run-manager/enqueue.ts +0 -1
- package/src/lib/server/runtime/session-run-manager/queries.ts +0 -1
- package/src/lib/server/runtime/session-run-manager/recovery.ts +0 -1
- package/src/lib/server/runtime/session-run-manager.test.ts +0 -28
- package/src/lib/server/session-tools/crud.ts +0 -14
- package/src/lib/server/session-tools/delegate.ts +0 -4
- package/src/lib/server/session-tools/index.ts +0 -4
- package/src/lib/server/session-tools/team-context.ts +0 -3
- package/src/lib/server/storage-normalization.ts +8 -0
- package/src/lib/server/storage.ts +18 -45
- package/src/lib/server/tasks/task-checkout.ts +59 -0
- package/src/lib/server/tasks/task-lifecycle.ts +2 -0
- package/src/lib/server/tasks/task-route-service.ts +4 -26
- package/src/lib/server/tasks/task-service.ts +0 -7
- package/src/lib/server/tool-aliases.ts +0 -1
- package/src/lib/server/tool-capability-policy-advanced.test.ts +4 -4
- package/src/lib/server/tool-capability-policy.ts +0 -2
- package/src/lib/server/tool-planning.ts +0 -12
- package/src/lib/server/universal-tool-access.ts +0 -1
- package/src/lib/server/wallets/wallet-crypto.ts +33 -0
- package/src/lib/server/wallets/wallet-repository.ts +24 -0
- package/src/lib/server/wallets/wallet-service.ts +119 -0
- package/src/lib/server/working-state/extraction.ts +8 -42
- package/src/lib/server/working-state/normalization.ts +10 -103
- package/src/lib/server/working-state/service.ts +12 -21
- package/src/lib/strip-internal-metadata.test.ts +1 -1
- package/src/lib/strip-internal-metadata.ts +1 -1
- package/src/lib/tool-definitions.ts +0 -1
- package/src/lib/validation/schemas.ts +33 -2
- package/src/stores/slices/data-slice.ts +5 -1
- package/src/stores/slices/ui-slice.ts +0 -4
- package/src/types/agent.ts +0 -84
- package/src/types/app-settings.ts +0 -2
- package/src/types/approval.ts +0 -2
- package/src/types/connector.ts +1 -0
- package/src/types/index.ts +1 -1
- package/src/types/message.ts +0 -1
- package/src/types/misc.ts +0 -2
- package/src/types/protocol.ts +0 -2
- package/src/types/run.ts +0 -3
- package/src/types/session.ts +1 -51
- package/src/types/swarmdock.ts +29 -0
- package/src/types/task.ts +7 -3
- package/src/types/working-state.ts +2 -9
- package/src/views/settings/section-runtime-loop.tsx +0 -14
- package/src/app/api/canvas/[sessionId]/route.ts +0 -35
- package/src/app/api/missions/[id]/actions/route.ts +0 -31
- package/src/app/api/missions/[id]/events/route.ts +0 -14
- package/src/app/api/missions/[id]/route.ts +0 -10
- package/src/app/api/missions/route.test.ts +0 -244
- package/src/app/api/missions/route.ts +0 -57
- package/src/app/api/wallets/[id]/approve/route.ts +0 -79
- package/src/app/api/wallets/[id]/balance-history/route.ts +0 -18
- package/src/app/api/wallets/[id]/send/route.ts +0 -113
- package/src/app/api/wallets/[id]/transactions/route.ts +0 -18
- package/src/app/missions/[id]/page.tsx +0 -3
- package/src/app/missions/page.tsx +0 -685
- package/src/components/canvas/canvas-panel.tsx +0 -267
- package/src/components/wallets/wallet-approval-dialog.tsx +0 -107
- package/src/components/wallets/wallet-panel.tsx +0 -1010
- package/src/components/wallets/wallet-section.tsx +0 -260
- package/src/features/missions/queries.ts +0 -23
- package/src/lib/canvas-content.test.ts +0 -360
- package/src/lib/canvas-content.ts +0 -198
- package/src/lib/server/canvas-content.test.ts +0 -32
- package/src/lib/server/canvas-content.ts +0 -6
- package/src/lib/server/ethereum.ts +0 -591
- package/src/lib/server/evm-swap.ts +0 -476
- package/src/lib/server/missions/mission-intent.test.ts +0 -63
- package/src/lib/server/missions/mission-intent.ts +0 -569
- package/src/lib/server/missions/mission-repository.ts +0 -74
- package/src/lib/server/missions/mission-service/actions.ts +0 -6
- package/src/lib/server/missions/mission-service/bindings.ts +0 -9
- package/src/lib/server/missions/mission-service/context.ts +0 -4
- package/src/lib/server/missions/mission-service/core.ts +0 -2271
- package/src/lib/server/missions/mission-service/queries.ts +0 -12
- package/src/lib/server/missions/mission-service/recovery.ts +0 -5
- package/src/lib/server/missions/mission-service/ticks.ts +0 -9
- package/src/lib/server/missions/mission-service.test.ts +0 -888
- package/src/lib/server/missions/mission-service.ts +0 -6
- package/src/lib/server/session-tools/canvas.ts +0 -105
- package/src/lib/server/session-tools/wallet-tool.test.ts +0 -150
- package/src/lib/server/session-tools/wallet.ts +0 -1287
- package/src/lib/server/solana.ts +0 -327
- package/src/lib/server/wallet/wallet-execution.test.ts +0 -198
- package/src/lib/server/wallet/wallet-portfolio.test.ts +0 -98
- package/src/lib/server/wallet/wallet-portfolio.ts +0 -772
- package/src/lib/server/wallet/wallet-service.test.ts +0 -81
- package/src/lib/server/wallet/wallet-service.ts +0 -225
- package/src/lib/wallet/wallet-transactions.test.ts +0 -75
- package/src/lib/wallet/wallet-transactions.ts +0 -43
- package/src/lib/wallet/wallet.test.ts +0 -333
- package/src/lib/wallet/wallet.ts +0 -183
- package/src/types/mission.ts +0 -185
- package/src/views/settings/section-wallets.tsx +0 -35
|
@@ -46,36 +46,6 @@ describe('stream-continuation', () => {
|
|
|
46
46
|
})
|
|
47
47
|
})
|
|
48
48
|
|
|
49
|
-
// ---- looksLikeExternalWalletTask ----
|
|
50
|
-
describe('looksLikeExternalWalletTask', () => {
|
|
51
|
-
it('matches wallet keywords', () => {
|
|
52
|
-
assert.equal(mod.looksLikeExternalWalletTask('swap 100 USDC on Arbitrum'), true)
|
|
53
|
-
assert.equal(mod.looksLikeExternalWalletTask('check my wallet balance'), true)
|
|
54
|
-
assert.equal(mod.looksLikeExternalWalletTask('trade ETH for SOL'), true)
|
|
55
|
-
})
|
|
56
|
-
|
|
57
|
-
it('returns false for empty/unrelated text', () => {
|
|
58
|
-
assert.equal(mod.looksLikeExternalWalletTask(''), false)
|
|
59
|
-
assert.equal(mod.looksLikeExternalWalletTask('write me a poem about cats'), false)
|
|
60
|
-
})
|
|
61
|
-
})
|
|
62
|
-
|
|
63
|
-
// ---- looksLikeBoundedExternalExecutionTask ----
|
|
64
|
-
describe('looksLikeBoundedExternalExecutionTask', () => {
|
|
65
|
-
it('requires wallet keywords + action verbs', () => {
|
|
66
|
-
assert.equal(mod.looksLikeBoundedExternalExecutionTask('swap 100 USDC on Arbitrum'), true)
|
|
67
|
-
assert.equal(mod.looksLikeBoundedExternalExecutionTask('buy ETH on the exchange'), true)
|
|
68
|
-
})
|
|
69
|
-
|
|
70
|
-
it('returns false without action verbs', () => {
|
|
71
|
-
assert.equal(mod.looksLikeBoundedExternalExecutionTask('check my wallet balance'), false)
|
|
72
|
-
})
|
|
73
|
-
|
|
74
|
-
it('returns false for non-wallet text', () => {
|
|
75
|
-
assert.equal(mod.looksLikeBoundedExternalExecutionTask('execute the test suite'), false)
|
|
76
|
-
})
|
|
77
|
-
})
|
|
78
|
-
|
|
79
49
|
// ---- looksLikeOpenEndedDeliverableTask ----
|
|
80
50
|
describe('looksLikeOpenEndedDeliverableTask', () => {
|
|
81
51
|
it('matches deliverable patterns', () => {
|
|
@@ -112,24 +82,6 @@ describe('stream-continuation', () => {
|
|
|
112
82
|
})
|
|
113
83
|
})
|
|
114
84
|
|
|
115
|
-
// ---- hasStateChangingWalletEvidence ----
|
|
116
|
-
describe('hasStateChangingWalletEvidence', () => {
|
|
117
|
-
it('detects send_transaction action in wallet_tool', () => {
|
|
118
|
-
const events = [{ name: 'wallet_tool', input: '{"action":"send_transaction"}', output: '{}' }]
|
|
119
|
-
assert.equal(mod.hasStateChangingWalletEvidence(events), true)
|
|
120
|
-
})
|
|
121
|
-
|
|
122
|
-
it('returns false for non-wallet tools', () => {
|
|
123
|
-
const events = [{ name: 'web', input: 'search', output: 'results' }]
|
|
124
|
-
assert.equal(mod.hasStateChangingWalletEvidence(events), false)
|
|
125
|
-
})
|
|
126
|
-
|
|
127
|
-
it('returns false for read-only wallet actions', () => {
|
|
128
|
-
const events = [{ name: 'wallet_tool', input: '{"action":"balance"}', output: '{}' }]
|
|
129
|
-
assert.equal(mod.hasStateChangingWalletEvidence(events), false)
|
|
130
|
-
})
|
|
131
|
-
})
|
|
132
|
-
|
|
133
85
|
// ---- countExternalExecutionResearchSteps ----
|
|
134
86
|
describe('countExternalExecutionResearchSteps', () => {
|
|
135
87
|
it('counts http/web/browser tools', () => {
|
|
@@ -141,12 +93,12 @@ describe('stream-continuation', () => {
|
|
|
141
93
|
assert.equal(mod.countExternalExecutionResearchSteps(events), 2)
|
|
142
94
|
})
|
|
143
95
|
|
|
144
|
-
it('
|
|
96
|
+
it('does not count non-research tools', () => {
|
|
145
97
|
const events = [
|
|
146
|
-
{ name: '
|
|
147
|
-
{ name: '
|
|
98
|
+
{ name: 'shell', input: 'ls', output: 'files' },
|
|
99
|
+
{ name: 'files', input: 'read', output: 'content' },
|
|
148
100
|
]
|
|
149
|
-
assert.equal(mod.countExternalExecutionResearchSteps(events),
|
|
101
|
+
assert.equal(mod.countExternalExecutionResearchSteps(events), 0)
|
|
150
102
|
})
|
|
151
103
|
})
|
|
152
104
|
|
|
@@ -194,25 +194,9 @@ function getRequestedArtifactStatus(params: {
|
|
|
194
194
|
// Tool evidence analysis
|
|
195
195
|
// ---------------------------------------------------------------------------
|
|
196
196
|
|
|
197
|
-
export function hasStateChangingWalletEvidence(toolEvents: MessageToolEvent[]): boolean {
|
|
198
|
-
return toolEvents.some((event) => {
|
|
199
|
-
const input = `${event.input || ''}\n${event.output || ''}`
|
|
200
|
-
return event.name === 'wallet_tool' && (
|
|
201
|
-
/"action":"send_transaction"/.test(input)
|
|
202
|
-
|| /"action":"send"/.test(input)
|
|
203
|
-
|| /"action":"sign_transaction"/.test(input)
|
|
204
|
-
|| /"type":"extension_wallet_action_request"/.test(input)
|
|
205
|
-
|| /"type":"extension_wallet_transfer_request"/.test(input)
|
|
206
|
-
|| /"status":"broadcast"/.test(input)
|
|
207
|
-
)
|
|
208
|
-
})
|
|
209
|
-
}
|
|
210
|
-
|
|
211
197
|
export function countExternalExecutionResearchSteps(toolEvents: MessageToolEvent[]): number {
|
|
212
198
|
return toolEvents.filter((event) => {
|
|
213
|
-
|
|
214
|
-
if (event.name !== 'wallet_tool') return false
|
|
215
|
-
return /"action":"(balance|address|transactions|call_contract|encode_contract_call)"/.test(event.input || '')
|
|
199
|
+
return ['http_request', 'web', 'web_search', 'web_fetch', 'browser'].includes(event.name)
|
|
216
200
|
}).length
|
|
217
201
|
}
|
|
218
202
|
|
|
@@ -238,49 +222,24 @@ export function countDistinctExternalResearchHosts(toolEvents: MessageToolEvent[
|
|
|
238
222
|
// Continuation decision helpers
|
|
239
223
|
// ---------------------------------------------------------------------------
|
|
240
224
|
|
|
241
|
-
export function shouldForceExternalExecutionFollowthrough(
|
|
225
|
+
export function shouldForceExternalExecutionFollowthrough(_params: {
|
|
242
226
|
userMessage: string
|
|
243
227
|
finalResponse: string
|
|
244
228
|
hasToolCalls: boolean
|
|
245
229
|
toolEvents: MessageToolEvent[]
|
|
246
230
|
classification?: MessageClassification | null
|
|
247
231
|
}): boolean {
|
|
248
|
-
|
|
249
|
-
if (!isTransactional) return false
|
|
250
|
-
if (!params.hasToolCalls || params.toolEvents.length < 4) return false
|
|
251
|
-
if (hasStateChangingWalletEvidence(params.toolEvents)) return false
|
|
252
|
-
const distinctHosts = countDistinctExternalResearchHosts(params.toolEvents)
|
|
253
|
-
const trimmed = params.finalResponse.trim()
|
|
254
|
-
if (!trimmed) return countExternalExecutionResearchSteps(params.toolEvents) >= 4 || distinctHosts >= 3
|
|
255
|
-
if (/\b(last reversible step|exact blocker|safest next action|blocked|cannot|can't|missing capability|no-key route unavailable)\b/i.test(trimmed)) {
|
|
256
|
-
return false
|
|
257
|
-
}
|
|
258
|
-
if (countExternalExecutionResearchSteps(params.toolEvents) < 4 && distinctHosts < 3) return false
|
|
259
|
-
return /(let me|i'll|i will|trying|research|query|check|look|promising|now let me|good -|good,)/i.test(trimmed) || trimmed.length < 500
|
|
232
|
+
return false
|
|
260
233
|
}
|
|
261
234
|
|
|
262
|
-
export function shouldForceExternalExecutionKickoffFollowthrough(
|
|
235
|
+
export function shouldForceExternalExecutionKickoffFollowthrough(_params: {
|
|
263
236
|
userMessage: string
|
|
264
237
|
finalResponse: string
|
|
265
238
|
hasToolCalls: boolean
|
|
266
239
|
toolEvents: MessageToolEvent[]
|
|
267
240
|
classification?: MessageClassification | null
|
|
268
241
|
}): boolean {
|
|
269
|
-
|
|
270
|
-
if (!isTransactional) return false
|
|
271
|
-
if (params.hasToolCalls || params.toolEvents.length > 0) return false
|
|
272
|
-
|
|
273
|
-
const trimmed = params.finalResponse.trim()
|
|
274
|
-
if (!trimmed) return true
|
|
275
|
-
if (/^(?:HEARTBEAT_OK|NO_MESSAGE)\b/i.test(trimmed)) return false
|
|
276
|
-
if (/\?\s*$/.test(trimmed)) return false
|
|
277
|
-
if (/\b(last reversible step|exact blocker|blocked|cannot|can't|missing capability|need approval|requires approval|approval boundary|requires human|ask_human|credential|authentication|login|2fa|mfa|captcha)\b/i.test(trimmed)) {
|
|
278
|
-
return false
|
|
279
|
-
}
|
|
280
|
-
if (/\b(done|completed|finished|sent|broadcast|minted|purchased|bought|swapped|claimed)\b/i.test(trimmed)) {
|
|
281
|
-
return false
|
|
282
|
-
}
|
|
283
|
-
return looksLikeIncompleteDeliverableResponse(trimmed) || trimmed.length < 220
|
|
242
|
+
return false
|
|
284
243
|
}
|
|
285
244
|
|
|
286
245
|
export function shouldForceDeliverableFollowthrough(params: {
|
|
@@ -454,8 +413,7 @@ function buildExternalExecutionFollowthroughPrompt(params: {
|
|
|
454
413
|
'You are in a bounded external execution task and have already done enough research.',
|
|
455
414
|
'Do not restart broad discovery. Do not ask the user for another prompt.',
|
|
456
415
|
'Do not spend this continuation on more venue shopping. Use the already confirmed route unless one last fetch is strictly required to prepare execution.',
|
|
457
|
-
'If several venue or aggregator APIs already failed, stop searching for more venues
|
|
458
|
-
'A prose approval request does not count as completion. If the next step is a sign/send/approve action, call the real wallet tool action so the runtime can create the approval request.',
|
|
416
|
+
'If several venue or aggregator APIs already failed, stop searching for more venues and state the blocker.',
|
|
459
417
|
'Do not mutate already confirmed token addresses, router addresses, spender addresses, or network identifiers unless newer tool evidence proves the earlier value was wrong.',
|
|
460
418
|
'Within this continuation, do exactly one of the following:',
|
|
461
419
|
'1. Take the next concrete execution step now using the existing tools and stop at the first approval boundary for a state-changing action.',
|
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { genId } from '@/lib/id'
|
|
2
2
|
import type { MailboxEnvelope } from '@/types'
|
|
3
3
|
import { loadSession, patchSession } from '@/lib/server/sessions/session-repository'
|
|
4
|
-
import { requestMissionTicksForHumanReply } from '@/lib/server/missions/mission-service'
|
|
5
4
|
|
|
6
5
|
interface MailboxOptions {
|
|
7
6
|
limit?: number
|
|
@@ -167,15 +166,6 @@ export function sendMailboxEnvelope(input: {
|
|
|
167
166
|
lastActiveAt: now,
|
|
168
167
|
}
|
|
169
168
|
})
|
|
170
|
-
if (envelope.type === 'human_reply') {
|
|
171
|
-
requestMissionTicksForHumanReply({
|
|
172
|
-
sessionId: input.toSessionId,
|
|
173
|
-
correlationId: envelope.correlationId || null,
|
|
174
|
-
envelopeId: envelope.id,
|
|
175
|
-
payload: envelope.payload,
|
|
176
|
-
fromSessionId: envelope.fromSessionId || null,
|
|
177
|
-
})
|
|
178
|
-
}
|
|
179
169
|
import('@/lib/server/runtime/watch-jobs')
|
|
180
170
|
.then(({ triggerMailboxWatchJobs }) => {
|
|
181
171
|
triggerMailboxWatchJobs({ sessionId: input.toSessionId, envelope })
|
|
@@ -7,7 +7,6 @@ import { buildAgentDisabledMessage, isAgentDisabled } from '@/lib/server/agents/
|
|
|
7
7
|
import { loadAgent } from '@/lib/server/agents/agent-repository'
|
|
8
8
|
import { clearMainLoopStateForSession } from '@/lib/server/agents/main-agent-loop'
|
|
9
9
|
import { applyResolvedRoute, resolvePrimaryAgentRoute } from '@/lib/server/agents/agent-runtime-config'
|
|
10
|
-
import { enrichSessionWithMissionSummary } from '@/lib/server/missions/mission-service'
|
|
11
10
|
import { cleanupSessionProcesses } from '@/lib/server/runtime/process-manager'
|
|
12
11
|
import { stopActiveSessionProcess } from '@/lib/server/runtime/runtime-state'
|
|
13
12
|
import {
|
|
@@ -60,7 +59,7 @@ export function listChatsForApi(): Record<string, ReturnType<typeof buildSession
|
|
|
60
59
|
sessions[id].currentRunId = run.runningRunId || null
|
|
61
60
|
}
|
|
62
61
|
return Object.fromEntries(
|
|
63
|
-
Object.entries(sessions).map(([id, session]) => [id, buildSessionListSummary(
|
|
62
|
+
Object.entries(sessions).map(([id, session]) => [id, buildSessionListSummary(session)]),
|
|
64
63
|
)
|
|
65
64
|
}
|
|
66
65
|
|
|
@@ -72,7 +71,7 @@ export function getChatSessionForApi(sessionId: string): Session | null {
|
|
|
72
71
|
session.active = !!run.runningRunId
|
|
73
72
|
session.queuedCount = queue.queueLength
|
|
74
73
|
session.currentRunId = run.runningRunId || null
|
|
75
|
-
return
|
|
74
|
+
return session
|
|
76
75
|
}
|
|
77
76
|
|
|
78
77
|
export function createChatSession(input: Record<string, unknown>): ServiceResult<Session> {
|
|
@@ -285,7 +284,7 @@ export function updateChatSession(sessionId: string, updates: Record<string, unk
|
|
|
285
284
|
|
|
286
285
|
saveSession(sessionId, original)
|
|
287
286
|
notify('sessions')
|
|
288
|
-
return
|
|
287
|
+
return original
|
|
289
288
|
}
|
|
290
289
|
|
|
291
290
|
export function deleteChatSession(sessionId: string): boolean {
|
|
@@ -320,7 +319,6 @@ export function queueChatMessage(sessionId: string, body: Record<string, unknown
|
|
|
320
319
|
}
|
|
321
320
|
const queued = enqueueSessionRun({
|
|
322
321
|
sessionId,
|
|
323
|
-
missionId: session.missionId || null,
|
|
324
322
|
message,
|
|
325
323
|
imagePath,
|
|
326
324
|
imageUrl,
|
|
@@ -1095,7 +1095,6 @@ If media sending fails, report the exact error and retry with a corrected path/t
|
|
|
1095
1095
|
|
|
1096
1096
|
const queued = enqueueSessionRun({
|
|
1097
1097
|
sessionId: session.id,
|
|
1098
|
-
missionId: session.missionId || null,
|
|
1099
1098
|
message: modelInputText,
|
|
1100
1099
|
imagePath: firstImagePath,
|
|
1101
1100
|
imageUrl: firstImageUrl,
|
|
@@ -19,6 +19,7 @@ import {
|
|
|
19
19
|
setReconnectState,
|
|
20
20
|
} from './reconnect-state'
|
|
21
21
|
import { connectorRuntimeState, runningConnectors } from './runtime-state'
|
|
22
|
+
import { ensureSwarmdockConnectorCredential } from './swarmdock-secret'
|
|
22
23
|
|
|
23
24
|
const TAG = 'connector-lifecycle'
|
|
24
25
|
|
|
@@ -70,6 +71,7 @@ export async function getPlatform(platform: string) {
|
|
|
70
71
|
case 'googlechat': return (await import('./googlechat')).default
|
|
71
72
|
case 'matrix': return (await import('./matrix')).default
|
|
72
73
|
case 'email': return (await import('./email')).default
|
|
74
|
+
case 'swarmdock': return (await import('./swarmdock')).default
|
|
73
75
|
}
|
|
74
76
|
|
|
75
77
|
// 2. Check Extension-provided connectors
|
|
@@ -134,8 +136,9 @@ async function _startConnectorImpl(connectorId: string): Promise<void> {
|
|
|
134
136
|
}
|
|
135
137
|
|
|
136
138
|
const connectors = loadConnectors()
|
|
137
|
-
const
|
|
138
|
-
if (!
|
|
139
|
+
const storedConnector = connectors[connectorId] as Connector | undefined
|
|
140
|
+
if (!storedConnector) throw new Error('Connector not found')
|
|
141
|
+
let connector: Connector = storedConnector
|
|
139
142
|
|
|
140
143
|
// Starting a connector expresses durable intent: keep it enabled across
|
|
141
144
|
// transient failures so daemon recovery and server restarts can retry it.
|
|
@@ -148,6 +151,16 @@ async function _startConnectorImpl(connectorId: string): Promise<void> {
|
|
|
148
151
|
}
|
|
149
152
|
|
|
150
153
|
try {
|
|
154
|
+
let swarmdockFallbackPrivateKey: string | null = null
|
|
155
|
+
if (connector.platform === 'swarmdock') {
|
|
156
|
+
const prepared = ensureSwarmdockConnectorCredential(connector, {
|
|
157
|
+
allowMigrationFailureFallback: true,
|
|
158
|
+
})
|
|
159
|
+
connector = prepared.connector
|
|
160
|
+
connectors[connectorId] = connector
|
|
161
|
+
swarmdockFallbackPrivateKey = prepared.fallbackPrivateKey
|
|
162
|
+
}
|
|
163
|
+
|
|
151
164
|
// Resolve bot token from credential
|
|
152
165
|
let botToken = ''
|
|
153
166
|
if (connector.credentialId) {
|
|
@@ -164,8 +177,11 @@ async function _startConnectorImpl(connectorId: string): Promise<void> {
|
|
|
164
177
|
if (!botToken && connector.platform === 'bluebubbles' && connector.config.password) {
|
|
165
178
|
botToken = connector.config.password
|
|
166
179
|
}
|
|
180
|
+
if (!botToken && swarmdockFallbackPrivateKey) {
|
|
181
|
+
botToken = swarmdockFallbackPrivateKey
|
|
182
|
+
}
|
|
167
183
|
|
|
168
|
-
if (!botToken && connector.platform !== 'whatsapp' && connector.platform !== 'openclaw' && connector.platform !== 'signal' && connector.platform !== 'email') {
|
|
184
|
+
if (!botToken && connector.platform !== 'whatsapp' && connector.platform !== 'openclaw' && connector.platform !== 'signal' && connector.platform !== 'email' && connector.platform !== 'swarmdock') {
|
|
169
185
|
throw new Error('No bot token configured')
|
|
170
186
|
}
|
|
171
187
|
|
|
@@ -43,6 +43,11 @@ import type {
|
|
|
43
43
|
} from '@/types'
|
|
44
44
|
import type { ServiceResult } from '@/lib/server/service-result'
|
|
45
45
|
import type { DaemonConnectorRuntimeState } from '@/lib/server/daemon/types'
|
|
46
|
+
import {
|
|
47
|
+
ensureSwarmdockConnectorCredential,
|
|
48
|
+
prepareSwarmdockConnectorInput,
|
|
49
|
+
redactConnectorSecrets,
|
|
50
|
+
} from './swarmdock-secret'
|
|
46
51
|
|
|
47
52
|
function cloneConnector<T extends Connector>(connector: T): T {
|
|
48
53
|
return {
|
|
@@ -124,35 +129,52 @@ function requireSenderId(body: Record<string, unknown>): string {
|
|
|
124
129
|
export async function listConnectorsWithRuntime(): Promise<Record<string, Connector>> {
|
|
125
130
|
await ensureDaemonProcessRunning('api/connectors:get')
|
|
126
131
|
const connectors = Object.fromEntries(
|
|
127
|
-
Object.entries(loadConnectors()).map(([id, connector]) =>
|
|
132
|
+
Object.entries(loadConnectors()).map(([id, connector]) => {
|
|
133
|
+
const prepared = ensureSwarmdockConnectorCredential(connector, {
|
|
134
|
+
allowMigrationFailureFallback: true,
|
|
135
|
+
})
|
|
136
|
+
return [id, cloneConnector(prepared.connector)]
|
|
137
|
+
}),
|
|
128
138
|
) as Record<string, Connector>
|
|
129
139
|
const runtimeByConnector = await listDaemonConnectorRuntime()
|
|
130
140
|
for (const connector of Object.values(connectors)) {
|
|
131
141
|
applyRuntimeFields(connector, runtimeByConnector[connector.id] || null)
|
|
132
142
|
}
|
|
133
|
-
return
|
|
143
|
+
return Object.fromEntries(
|
|
144
|
+
Object.entries(connectors).map(([id, connector]) => [id, redactConnectorSecrets(connector)]),
|
|
145
|
+
)
|
|
134
146
|
}
|
|
135
147
|
|
|
136
148
|
export async function getConnectorWithRuntime(id: string): Promise<Connector | null> {
|
|
137
149
|
await ensureDaemonProcessRunning('api/connectors/[id]:get')
|
|
138
150
|
const connector = loadConnector(id)
|
|
139
151
|
if (!connector) return null
|
|
140
|
-
const
|
|
141
|
-
|
|
152
|
+
const prepared = ensureSwarmdockConnectorCredential(connector, {
|
|
153
|
+
allowMigrationFailureFallback: true,
|
|
154
|
+
})
|
|
155
|
+
const current = cloneConnector(prepared.connector)
|
|
156
|
+
return redactConnectorSecrets(applyRuntimeFields(current, await getDaemonConnectorRuntime(id)))
|
|
142
157
|
}
|
|
143
158
|
|
|
144
159
|
export function createConnector(body: Record<string, unknown>): Connector {
|
|
145
160
|
const id = genId()
|
|
161
|
+
const rawConfig = body.config && typeof body.config === 'object' && !Array.isArray(body.config)
|
|
162
|
+
? body.config as Record<string, string>
|
|
163
|
+
: {}
|
|
164
|
+
const prepared = prepareSwarmdockConnectorInput({
|
|
165
|
+
platform: body.platform as Connector['platform'],
|
|
166
|
+
name: (body.name as string) || `${String(body.platform || '')} Connector`,
|
|
167
|
+
credentialId: (body.credentialId as string | null | undefined) || null,
|
|
168
|
+
config: rawConfig,
|
|
169
|
+
})
|
|
146
170
|
const connector: Connector = {
|
|
147
171
|
id,
|
|
148
172
|
name: (body.name as string) || `${String(body.platform || '')} Connector`,
|
|
149
173
|
platform: body.platform as Connector['platform'],
|
|
150
174
|
agentId: (body.agentId as string | null | undefined) || null,
|
|
151
175
|
chatroomId: (body.chatroomId as string | null | undefined) || null,
|
|
152
|
-
credentialId:
|
|
153
|
-
config:
|
|
154
|
-
? body.config as Record<string, string>
|
|
155
|
-
: {},
|
|
176
|
+
credentialId: prepared.credentialId,
|
|
177
|
+
config: prepared.config,
|
|
156
178
|
isEnabled: false,
|
|
157
179
|
status: 'stopped',
|
|
158
180
|
lastError: null,
|
|
@@ -210,6 +232,14 @@ export async function updateConnectorFromRoute(id: string, body: Record<string,
|
|
|
210
232
|
if (body.credentialId !== undefined) next.credentialId = typeof body.credentialId === 'string' || body.credentialId === null ? body.credentialId : next.credentialId
|
|
211
233
|
if (body.config !== undefined) next.config = body.config && typeof body.config === 'object' && !Array.isArray(body.config) ? body.config as Record<string, string> : next.config
|
|
212
234
|
if (body.isEnabled !== undefined) next.isEnabled = typeof body.isEnabled === 'boolean' ? body.isEnabled : next.isEnabled
|
|
235
|
+
const prepared = prepareSwarmdockConnectorInput({
|
|
236
|
+
platform: next.platform,
|
|
237
|
+
name: next.name,
|
|
238
|
+
credentialId: next.credentialId || null,
|
|
239
|
+
config: next.config,
|
|
240
|
+
})
|
|
241
|
+
next.credentialId = prepared.credentialId
|
|
242
|
+
next.config = prepared.config
|
|
213
243
|
persistConnector(next)
|
|
214
244
|
|
|
215
245
|
try {
|
|
@@ -235,7 +265,7 @@ export async function updateConnectorFromRoute(id: string, body: Record<string,
|
|
|
235
265
|
}
|
|
236
266
|
|
|
237
267
|
notify('connectors')
|
|
238
|
-
return serviceOk(await getConnectorWithRuntime(id) || next)
|
|
268
|
+
return serviceOk(await getConnectorWithRuntime(id) || redactConnectorSecrets(next))
|
|
239
269
|
}
|
|
240
270
|
|
|
241
271
|
export async function deleteConnectorFromRoute(id: string): Promise<ServiceResult<{ ok: true }>> {
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
import { log } from '@/lib/server/logger'
|
|
2
|
+
import type { BidCreateInput } from '@swarmdock/shared'
|
|
3
|
+
|
|
4
|
+
const TAG = 'swarmdock-bid'
|
|
5
|
+
|
|
6
|
+
interface SwarmDockTask {
|
|
7
|
+
id: string
|
|
8
|
+
title: string
|
|
9
|
+
skillRequirements: string[]
|
|
10
|
+
budgetMax: string
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
interface SwarmDockConfig {
|
|
14
|
+
skills: string
|
|
15
|
+
maxBudget: string
|
|
16
|
+
autoDiscover: boolean
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Determine if the agent should auto-bid on a discovered task.
|
|
21
|
+
* Checks skill overlap and budget limits.
|
|
22
|
+
*/
|
|
23
|
+
export function shouldAutoBid(task: SwarmDockTask, config: SwarmDockConfig): boolean {
|
|
24
|
+
if (!config.autoDiscover) return false
|
|
25
|
+
|
|
26
|
+
// Check budget
|
|
27
|
+
const maxBudget = BigInt(config.maxBudget || '0')
|
|
28
|
+
if (maxBudget > BigInt(0)) {
|
|
29
|
+
const taskBudget = BigInt(task.budgetMax || '0')
|
|
30
|
+
if (taskBudget > maxBudget) {
|
|
31
|
+
log.debug(TAG, `Skipping "${task.title}" — budget ${task.budgetMax} exceeds max ${config.maxBudget}`)
|
|
32
|
+
return false
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
// Check skill overlap
|
|
37
|
+
const agentSkills = new Set(
|
|
38
|
+
config.skills.split(',').map((s) => s.trim().toLowerCase()).filter(Boolean),
|
|
39
|
+
)
|
|
40
|
+
if (agentSkills.size === 0) return false
|
|
41
|
+
|
|
42
|
+
const hasMatchingSkill = task.skillRequirements.some(
|
|
43
|
+
(req) => agentSkills.has(req.toLowerCase()),
|
|
44
|
+
)
|
|
45
|
+
if (!hasMatchingSkill) {
|
|
46
|
+
log.debug(TAG, `Skipping "${task.title}" — no matching skills`)
|
|
47
|
+
return false
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
return true
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
/**
|
|
54
|
+
* Submit an auto-bid on a SwarmDock task.
|
|
55
|
+
* Uses the task's max budget as the proposed price (simple strategy).
|
|
56
|
+
*/
|
|
57
|
+
export async function submitAutoBid(
|
|
58
|
+
client: { tasks: { bid: (taskId: string, input: BidCreateInput) => Promise<unknown> } },
|
|
59
|
+
taskId: string,
|
|
60
|
+
config: SwarmDockConfig,
|
|
61
|
+
): Promise<void> {
|
|
62
|
+
const agentSkills = config.skills.split(',').map((s) => s.trim()).filter(Boolean)
|
|
63
|
+
|
|
64
|
+
const bid: BidCreateInput = {
|
|
65
|
+
proposedPrice: config.maxBudget || '1000000',
|
|
66
|
+
confidenceScore: 0.8,
|
|
67
|
+
proposal: `SwarmClaw agent with skills: ${agentSkills.join(', ')}. Ready to start immediately.`,
|
|
68
|
+
portfolioRefs: [],
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
await client.tasks.bid(taskId, bid)
|
|
72
|
+
|
|
73
|
+
log.info(TAG, `Auto-bid submitted for task ${taskId}`)
|
|
74
|
+
}
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
import assert from 'node:assert/strict'
|
|
2
|
+
import test from 'node:test'
|
|
3
|
+
|
|
4
|
+
import { submitAutoBid } from '@/lib/server/connectors/swarmdock-bidding'
|
|
5
|
+
import { submitSwarmdockTaskResult } from '@/lib/server/connectors/swarmdock'
|
|
6
|
+
|
|
7
|
+
test('submitAutoBid includes empty portfolio refs for SDK compatibility', async () => {
|
|
8
|
+
const seen: {
|
|
9
|
+
taskId?: string
|
|
10
|
+
bid?: { proposedPrice: string; portfolioRefs: string[] }
|
|
11
|
+
} = {}
|
|
12
|
+
|
|
13
|
+
await submitAutoBid(
|
|
14
|
+
{
|
|
15
|
+
tasks: {
|
|
16
|
+
bid: async (taskId, input) => {
|
|
17
|
+
seen.taskId = taskId
|
|
18
|
+
seen.bid = {
|
|
19
|
+
proposedPrice: input.proposedPrice,
|
|
20
|
+
portfolioRefs: [...input.portfolioRefs],
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
'task-123',
|
|
26
|
+
{
|
|
27
|
+
skills: 'typescript,automation',
|
|
28
|
+
maxBudget: '2500000',
|
|
29
|
+
autoDiscover: true,
|
|
30
|
+
},
|
|
31
|
+
)
|
|
32
|
+
|
|
33
|
+
assert.equal(seen.taskId, 'task-123')
|
|
34
|
+
assert.deepEqual(seen.bid, {
|
|
35
|
+
proposedPrice: '2500000',
|
|
36
|
+
portfolioRefs: [],
|
|
37
|
+
})
|
|
38
|
+
})
|
|
39
|
+
|
|
40
|
+
test('submitSwarmdockTaskResult includes empty files and propagates submit errors', async () => {
|
|
41
|
+
const seen: {
|
|
42
|
+
taskId?: string
|
|
43
|
+
payload?: { files: unknown[]; artifacts: Array<{ type: string; content: string }> }
|
|
44
|
+
} = {}
|
|
45
|
+
|
|
46
|
+
await submitSwarmdockTaskResult(
|
|
47
|
+
{
|
|
48
|
+
tasks: {
|
|
49
|
+
submit: async (taskId, input) => {
|
|
50
|
+
seen.taskId = taskId
|
|
51
|
+
seen.payload = {
|
|
52
|
+
files: [...input.files],
|
|
53
|
+
artifacts: input.artifacts.map((artifact) => ({
|
|
54
|
+
type: artifact.type,
|
|
55
|
+
content: String(artifact.content),
|
|
56
|
+
})),
|
|
57
|
+
}
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
'task-456',
|
|
62
|
+
'Result body',
|
|
63
|
+
)
|
|
64
|
+
|
|
65
|
+
assert.equal(seen.taskId, 'task-456')
|
|
66
|
+
assert.deepEqual(seen.payload, {
|
|
67
|
+
files: [],
|
|
68
|
+
artifacts: [{ type: 'text/markdown', content: 'Result body' }],
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
await assert.rejects(
|
|
72
|
+
submitSwarmdockTaskResult(
|
|
73
|
+
{
|
|
74
|
+
tasks: {
|
|
75
|
+
submit: async () => {
|
|
76
|
+
throw new Error('submit failed')
|
|
77
|
+
},
|
|
78
|
+
},
|
|
79
|
+
},
|
|
80
|
+
'task-456',
|
|
81
|
+
'Result body',
|
|
82
|
+
),
|
|
83
|
+
/submit failed/,
|
|
84
|
+
)
|
|
85
|
+
})
|