@swarmclawai/swarmclaw 0.7.1 → 0.7.2
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 +85 -139
- package/package.json +1 -1
- package/src/app/api/agents/[id]/thread/route.ts +1 -2
- package/src/app/api/agents/route.ts +1 -1
- package/src/app/api/{sessions → chats}/[id]/checkpoints/route.ts +1 -1
- package/src/app/api/{sessions → chats}/[id]/main-loop/route.ts +2 -2
- package/src/app/api/{sessions → chats}/[id]/restore/route.ts +1 -1
- package/src/app/api/{sessions → chats}/[id]/route.ts +4 -52
- package/src/app/api/{sessions → chats}/route.ts +5 -7
- package/src/app/api/plugins/route.ts +3 -0
- package/src/app/api/plugins/settings/route.ts +35 -0
- package/src/app/api/usage/route.ts +30 -0
- package/src/cli/index.js +35 -33
- package/src/cli/index.ts +40 -39
- package/src/cli/spec.js +29 -27
- package/src/components/agents/agent-card.tsx +1 -1
- package/src/components/agents/agent-chat-list.tsx +3 -3
- package/src/components/agents/agent-list.tsx +8 -13
- package/src/components/agents/agent-sheet.tsx +2 -2
- package/src/components/agents/cron-job-form.tsx +3 -3
- package/src/components/agents/inspector-panel.tsx +2 -2
- package/src/components/auth/setup-wizard.tsx +5 -38
- package/src/components/chat/chat-area.tsx +10 -14
- package/src/components/{sessions/session-card.tsx → chat/chat-card.tsx} +3 -3
- package/src/components/chat/chat-header.tsx +156 -73
- package/src/components/{sessions/session-list.tsx → chat/chat-list.tsx} +4 -5
- package/src/components/chat/chat-tool-toggles.tsx +26 -17
- package/src/components/chat/checkpoint-timeline.tsx +4 -4
- package/src/components/chat/message-bubble.tsx +4 -1
- package/src/components/chat/message-list.tsx +2 -2
- package/src/components/{sessions/new-session-sheet.tsx → chat/new-chat-sheet.tsx} +6 -6
- package/src/components/chat/session-debug-panel.tsx +1 -1
- package/src/components/chat/tool-request-banner.tsx +3 -3
- package/src/components/chatrooms/agent-hover-card.tsx +3 -3
- package/src/components/chatrooms/chatroom-tool-request-banner.tsx +2 -2
- package/src/components/connectors/connector-sheet.tsx +1 -1
- package/src/components/home/home-view.tsx +1 -1
- package/src/components/layout/app-layout.tsx +23 -2
- package/src/components/plugins/plugin-list.tsx +475 -254
- package/src/components/plugins/plugin-sheet.tsx +124 -10
- package/src/components/settings/gateway-connection-panel.tsx +1 -1
- package/src/components/shared/command-palette.tsx +0 -1
- package/src/components/shared/settings/section-heartbeat.tsx +1 -1
- package/src/components/shared/settings/section-providers.tsx +1 -1
- package/src/components/shared/settings/settings-page.tsx +1 -12
- package/src/components/usage/metrics-dashboard.tsx +73 -0
- package/src/components/webhooks/webhook-sheet.tsx +1 -1
- package/src/lib/chat.ts +1 -1
- package/src/lib/{sessions.ts → chats.ts} +28 -18
- package/src/lib/providers/claude-cli.ts +1 -1
- package/src/lib/server/approvals.ts +4 -4
- package/src/lib/server/capability-router.ts +10 -8
- package/src/lib/server/chat-execution.ts +36 -105
- package/src/lib/server/chatroom-helpers.ts +3 -3
- package/src/lib/server/connectors/manager.ts +4 -4
- package/src/lib/server/cost.ts +34 -1
- package/src/lib/server/daemon-state.ts +2 -2
- package/src/lib/server/heartbeat-service.ts +1 -1
- package/src/lib/server/main-agent-loop.ts +25 -160
- package/src/lib/server/main-session.ts +6 -13
- package/src/lib/server/orchestrator-lg.ts +3 -3
- package/src/lib/server/orchestrator.ts +5 -5
- package/src/lib/server/plugins.ts +112 -4
- package/src/lib/server/provider-health.ts +5 -3
- package/src/lib/server/queue.ts +12 -10
- package/src/lib/server/session-run-manager.test.ts +9 -6
- package/src/lib/server/session-run-manager.ts +1 -3
- package/src/lib/server/session-tools/calendar.ts +376 -0
- package/src/lib/server/session-tools/canvas.ts +1 -1
- package/src/lib/server/session-tools/chatroom.ts +4 -2
- package/src/lib/server/session-tools/connector.ts +5 -2
- package/src/lib/server/session-tools/context.ts +7 -3
- package/src/lib/server/session-tools/crud.ts +14 -6
- package/src/lib/server/session-tools/delegate.ts +95 -8
- package/src/lib/server/session-tools/discovery.ts +2 -2
- package/src/lib/server/session-tools/edit_file.ts +4 -2
- package/src/lib/server/session-tools/email.ts +322 -0
- package/src/lib/server/session-tools/file.ts +5 -2
- package/src/lib/server/session-tools/git.ts +1 -1
- package/src/lib/server/session-tools/http.ts +1 -1
- package/src/lib/server/session-tools/image-gen.ts +382 -0
- package/src/lib/server/session-tools/index.ts +74 -49
- package/src/lib/server/session-tools/memory.ts +139 -2
- package/src/lib/server/session-tools/monitor.ts +1 -1
- package/src/lib/server/session-tools/openclaw-nodes.ts +1 -1
- package/src/lib/server/session-tools/openclaw-workspace.ts +1 -1
- package/src/lib/server/session-tools/platform.ts +6 -3
- package/src/lib/server/session-tools/plugin-creator.ts +3 -3
- package/src/lib/server/session-tools/replicate.ts +303 -0
- package/src/lib/server/session-tools/sample-ui.ts +1 -1
- package/src/lib/server/session-tools/sandbox.ts +4 -2
- package/src/lib/server/session-tools/schedule.ts +4 -2
- package/src/lib/server/session-tools/session-info.ts +7 -4
- package/src/lib/server/session-tools/shell.ts +5 -2
- package/src/lib/server/session-tools/subagent.ts +2 -2
- package/src/lib/server/session-tools/wallet.ts +29 -2
- package/src/lib/server/session-tools/web.ts +44 -5
- package/src/lib/server/storage.ts +29 -9
- package/src/lib/server/stream-agent-chat.ts +72 -249
- package/src/lib/server/tool-aliases.ts +26 -15
- package/src/lib/server/tool-capability-policy.test.ts +9 -9
- package/src/lib/server/tool-capability-policy.ts +32 -27
- package/src/lib/tool-definitions.ts +4 -0
- package/src/lib/validation/schemas.ts +3 -1
- package/src/stores/use-app-store.ts +5 -5
- package/src/stores/use-chat-store.ts +7 -7
- package/src/types/index.ts +65 -3
- /package/src/app/api/{sessions → chats}/[id]/browser/route.ts +0 -0
- /package/src/app/api/{sessions → chats}/[id]/chat/route.ts +0 -0
- /package/src/app/api/{sessions → chats}/[id]/clear/route.ts +0 -0
- /package/src/app/api/{sessions → chats}/[id]/deploy/route.ts +0 -0
- /package/src/app/api/{sessions → chats}/[id]/devserver/route.ts +0 -0
- /package/src/app/api/{sessions → chats}/[id]/edit-resend/route.ts +0 -0
- /package/src/app/api/{sessions → chats}/[id]/fork/route.ts +0 -0
- /package/src/app/api/{sessions → chats}/[id]/mailbox/route.ts +0 -0
- /package/src/app/api/{sessions → chats}/[id]/messages/route.ts +0 -0
- /package/src/app/api/{sessions → chats}/[id]/retry/route.ts +0 -0
- /package/src/app/api/{sessions → chats}/[id]/stop/route.ts +0 -0
- /package/src/app/api/{sessions → chats}/heartbeat/route.ts +0 -0
|
@@ -135,7 +135,34 @@ async function executeWalletAction(args: any, context: { agentId?: string | null
|
|
|
135
135
|
const WalletPlugin: Plugin = {
|
|
136
136
|
name: 'Core Wallet',
|
|
137
137
|
description: 'Manage agent crypto wallet: check balance, send SOL, view transactions.',
|
|
138
|
-
hooks: {
|
|
138
|
+
hooks: {
|
|
139
|
+
getAgentContext: async (ctx) => {
|
|
140
|
+
const agentId = ctx.session.agentId
|
|
141
|
+
if (!agentId) return null
|
|
142
|
+
const { loadWallets } = await import('../storage')
|
|
143
|
+
const wallets = loadWallets() as Record<string, { agentId: string; publicKey: string; chain: string; spendingLimitLamports?: number; dailyLimitLamports?: number; requireApproval: boolean }>
|
|
144
|
+
const wallet = Object.values(wallets).find((w) => w.agentId === agentId)
|
|
145
|
+
if (!wallet) return null
|
|
146
|
+
const { getBalance, lamportsToSol } = await import('../solana')
|
|
147
|
+
let balanceSol = 0
|
|
148
|
+
try {
|
|
149
|
+
const lamports = await getBalance(wallet.publicKey)
|
|
150
|
+
balanceSol = lamportsToSol(lamports)
|
|
151
|
+
} catch { /* RPC failure — show 0 */ }
|
|
152
|
+
const perTxLimit = lamportsToSol(wallet.spendingLimitLamports ?? 100_000_000)
|
|
153
|
+
const dailyLimit = lamportsToSol(wallet.dailyLimitLamports ?? 1_000_000_000)
|
|
154
|
+
return [
|
|
155
|
+
'## Your Wallet',
|
|
156
|
+
`You own a ${wallet.chain} wallet — this is your wallet, your funds, your address. Speak about it in the first person ("my wallet", "my balance", "I have X SOL"). You can spend from it freely within your limits${wallet.requireApproval ? ', though the user needs to approve each send before it goes through' : ''}.`,
|
|
157
|
+
`- Address: ${wallet.publicKey}`,
|
|
158
|
+
`- Balance: ${balanceSol} SOL`,
|
|
159
|
+
`- Per-transaction limit: ${perTxLimit} SOL`,
|
|
160
|
+
`- Daily limit: ${dailyLimit} SOL`,
|
|
161
|
+
'Use the `wallet_tool` to check your balance, send SOL, or view your transaction history.',
|
|
162
|
+
].join('\n')
|
|
163
|
+
},
|
|
164
|
+
getCapabilityDescription: () => 'I have my own crypto wallet (`wallet_tool`) — I can check my balance, send SOL, and review my transaction history.',
|
|
165
|
+
} as PluginHooks,
|
|
139
166
|
ui: {
|
|
140
167
|
sidebarItems: [
|
|
141
168
|
{
|
|
@@ -179,7 +206,7 @@ getPluginManager().registerBuiltin('wallet', WalletPlugin)
|
|
|
179
206
|
* Legacy Bridge
|
|
180
207
|
*/
|
|
181
208
|
export function buildWalletTools(bctx: ToolBuildContext): StructuredToolInterface[] {
|
|
182
|
-
if (!bctx.
|
|
209
|
+
if (!bctx.hasPlugin('wallet')) return []
|
|
183
210
|
return [
|
|
184
211
|
tool(
|
|
185
212
|
async (args) => executeWalletAction(args, { agentId: bctx.ctx?.agentId }),
|
|
@@ -133,7 +133,9 @@ async function executeWebAction(args: Record<string, unknown>, bctx: any) {
|
|
|
133
133
|
const WebPlugin: Plugin = {
|
|
134
134
|
name: 'Core Web',
|
|
135
135
|
description: 'Search the web and fetch content from URLs.',
|
|
136
|
-
hooks: {
|
|
136
|
+
hooks: {
|
|
137
|
+
getCapabilityDescription: () => 'I can search the web (`web_search`) for research, fact-checking, and discovery.',
|
|
138
|
+
} as PluginHooks,
|
|
137
139
|
tools: [
|
|
138
140
|
{
|
|
139
141
|
name: 'web',
|
|
@@ -162,7 +164,7 @@ export function buildWebTools(bctx: ToolBuildContext): StructuredToolInterface[]
|
|
|
162
164
|
const tools: StructuredToolInterface[] = []
|
|
163
165
|
const { cwd, ctx, cleanupFns } = bctx
|
|
164
166
|
|
|
165
|
-
if (bctx.
|
|
167
|
+
if (bctx.hasPlugin('web')) {
|
|
166
168
|
tools.push(
|
|
167
169
|
tool(
|
|
168
170
|
async (args) => executeWebAction(args, bctx),
|
|
@@ -176,7 +178,7 @@ export function buildWebTools(bctx: ToolBuildContext): StructuredToolInterface[]
|
|
|
176
178
|
}
|
|
177
179
|
|
|
178
180
|
// Browser tool (kept as direct injection for now due to complexity)
|
|
179
|
-
if (bctx.
|
|
181
|
+
if (bctx.hasPlugin('browser')) {
|
|
180
182
|
const sessionKey = ctx?.sessionId || `anon-${Date.now()}`
|
|
181
183
|
let mcpClient: any = null
|
|
182
184
|
let mcpServer: any = null
|
|
@@ -309,9 +311,46 @@ export function buildWebTools(bctx: ToolBuildContext): StructuredToolInterface[]
|
|
|
309
311
|
const { action, ...rest } = params
|
|
310
312
|
const mcpTool = MCP_TOOL_MAP[action]
|
|
311
313
|
if (!mcpTool) return `Unknown browser action: "${action}"`
|
|
314
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
312
315
|
const args: Record<string, any> = {}
|
|
313
316
|
for (const [k, v] of Object.entries(rest)) { if (v !== undefined && v !== null && v !== '') args[k] = v }
|
|
314
|
-
|
|
317
|
+
|
|
318
|
+
// If screenshot includes a url, navigate first then capture
|
|
319
|
+
if (action === 'screenshot' && args.url) {
|
|
320
|
+
const navUrl = args.url
|
|
321
|
+
delete args.url
|
|
322
|
+
await callMcpTool('browser_navigate', { url: navUrl })
|
|
323
|
+
try { await dismissCookieBanners(callMcpTool) } catch { /* ignore */ }
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
// Wait for the page to finish rendering before capturing
|
|
327
|
+
if (action === 'screenshot') {
|
|
328
|
+
try {
|
|
329
|
+
await callMcpTool('browser_evaluate', {
|
|
330
|
+
expression: `await new Promise(resolve => {
|
|
331
|
+
if (document.readyState === 'complete') {
|
|
332
|
+
setTimeout(resolve, 1500);
|
|
333
|
+
} else {
|
|
334
|
+
window.addEventListener('load', () => setTimeout(resolve, 1500), { once: true });
|
|
335
|
+
setTimeout(resolve, 5000);
|
|
336
|
+
}
|
|
337
|
+
})`,
|
|
338
|
+
})
|
|
339
|
+
} catch { /* page may not support evaluate — fall back to a flat delay */
|
|
340
|
+
await new Promise((r) => setTimeout(r, 2000))
|
|
341
|
+
}
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
let result = await callMcpTool(mcpTool, args, { saveTo: params.saveTo })
|
|
345
|
+
|
|
346
|
+
// Playwright throws ERR_ABORTED on server-side redirects (e.g. Wikipedia Special:Random).
|
|
347
|
+
// The browser follows the redirect fine — the original navigation just gets "aborted".
|
|
348
|
+
// Recover by taking a snapshot of the page the browser actually landed on.
|
|
349
|
+
if (action === 'navigate' && result.includes('ERR_ABORTED')) {
|
|
350
|
+
await new Promise((r) => setTimeout(r, 1000))
|
|
351
|
+
result = await callMcpTool('browser_snapshot', {})
|
|
352
|
+
}
|
|
353
|
+
|
|
315
354
|
if (action === 'navigate') { try { await dismissCookieBanners(callMcpTool) } catch { /* ignore */ } }
|
|
316
355
|
return result
|
|
317
356
|
} catch (err: unknown) { return `Error: ${err instanceof Error ? err.message : String(err)}` }
|
|
@@ -332,7 +371,7 @@ export function buildWebTools(bctx: ToolBuildContext): StructuredToolInterface[]
|
|
|
332
371
|
|
|
333
372
|
// openclaw_browser CLI passthrough
|
|
334
373
|
const openclawPath = findBinaryOnPath('openclaw') || findBinaryOnPath('clawdbot')
|
|
335
|
-
if (openclawPath && (bctx.
|
|
374
|
+
if (openclawPath && (bctx.hasPlugin('browser') || bctx.hasPlugin('openclaw_browser'))) {
|
|
336
375
|
tools.push(
|
|
337
376
|
tool(
|
|
338
377
|
async (rawArgs) => {
|
|
@@ -6,7 +6,6 @@ import Database from 'better-sqlite3'
|
|
|
6
6
|
|
|
7
7
|
import { DATA_DIR, WORKSPACE_DIR } from './data-dir'
|
|
8
8
|
import type { Message } from '@/types'
|
|
9
|
-
import { ensureMainSessionFlag } from './main-session'
|
|
10
9
|
export const UPLOAD_DIR = path.join(DATA_DIR, 'uploads')
|
|
11
10
|
|
|
12
11
|
// --- LRU Cache ---
|
|
@@ -421,7 +420,7 @@ You have opinions about good agent design. You suggest creative approaches, warn
|
|
|
421
420
|
|
|
422
421
|
Be concise but not curt. Warmth doesn't require verbosity. When someone asks "how do I...?", give them the direct steps. Offer to do things rather than just explaining — if someone wants an agent created, create it. Use your tools when actions speak louder than words. If you don't know something, say so honestly.`,
|
|
423
422
|
isOrchestrator: false,
|
|
424
|
-
|
|
423
|
+
plugins: defaultStarterTools,
|
|
425
424
|
heartbeatEnabled: true,
|
|
426
425
|
platformAssignScope: 'all',
|
|
427
426
|
skillIds: [],
|
|
@@ -435,10 +434,11 @@ Be concise but not curt. Warmth doesn't require verbosity. When someone asks "ho
|
|
|
435
434
|
if (row?.data) {
|
|
436
435
|
try {
|
|
437
436
|
const existing = JSON.parse(row.data) as Record<string, unknown>
|
|
438
|
-
const
|
|
439
|
-
const
|
|
440
|
-
if (JSON.stringify(
|
|
441
|
-
existing.
|
|
437
|
+
const existingPlugins = Array.isArray(existing.plugins) ? existing.plugins : Array.isArray(existing.tools) ? existing.tools : []
|
|
438
|
+
const mergedPlugins = Array.from(new Set([...existingPlugins, ...defaultStarterTools])).filter((t) => t !== 'delete_file')
|
|
439
|
+
if (JSON.stringify(existingPlugins) !== JSON.stringify(mergedPlugins)) {
|
|
440
|
+
existing.plugins = mergedPlugins
|
|
441
|
+
delete existing.tools
|
|
442
442
|
existing.updatedAt = Date.now()
|
|
443
443
|
db.prepare('UPDATE agents SET data = ? WHERE id = ?').run(JSON.stringify(existing), 'default')
|
|
444
444
|
}
|
|
@@ -516,15 +516,19 @@ export function loadSessions(): Record<string, any> {
|
|
|
516
516
|
changed = true
|
|
517
517
|
}
|
|
518
518
|
|
|
519
|
-
const beforeMainFlag = session.mainSession === true
|
|
520
|
-
ensureMainSessionFlag(session)
|
|
521
|
-
if (!beforeMainFlag && session.mainSession === true) changed = true
|
|
522
519
|
|
|
523
520
|
const agentId = typeof session.agentId === 'string' ? session.agentId.trim() : ''
|
|
524
521
|
if (agentId && !Object.prototype.hasOwnProperty.call(agents, agentId)) {
|
|
525
522
|
session.agentId = null
|
|
526
523
|
changed = true
|
|
527
524
|
}
|
|
525
|
+
|
|
526
|
+
// Migrate tools → plugins
|
|
527
|
+
if (Array.isArray(session.tools) && !Array.isArray(session.plugins)) {
|
|
528
|
+
session.plugins = session.tools
|
|
529
|
+
delete session.tools
|
|
530
|
+
changed = true
|
|
531
|
+
}
|
|
528
532
|
}
|
|
529
533
|
|
|
530
534
|
if (changed) saveCollection('sessions', sessions)
|
|
@@ -596,8 +600,24 @@ export function decryptKey(encrypted: string): string {
|
|
|
596
600
|
}
|
|
597
601
|
|
|
598
602
|
// --- Agents ---
|
|
603
|
+
|
|
604
|
+
/** Migrate legacy `tools` field → `plugins` on agent objects. */
|
|
605
|
+
function migrateAgentPlugins(agents: Record<string, Record<string, unknown>>): boolean {
|
|
606
|
+
let changed = false
|
|
607
|
+
for (const agent of Object.values(agents)) {
|
|
608
|
+
if (!agent || typeof agent !== 'object') continue
|
|
609
|
+
if (Array.isArray(agent.tools) && !Array.isArray(agent.plugins)) {
|
|
610
|
+
agent.plugins = agent.tools
|
|
611
|
+
delete agent.tools
|
|
612
|
+
changed = true
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
return changed
|
|
616
|
+
}
|
|
617
|
+
|
|
599
618
|
export function loadAgents(opts?: { includeTrashed?: boolean }): Record<string, any> {
|
|
600
619
|
const all = loadCollection('agents')
|
|
620
|
+
if (migrateAgentPlugins(all)) saveCollection('agents', all)
|
|
601
621
|
if (opts?.includeTrashed) return all
|
|
602
622
|
const result: Record<string, any> = {}
|
|
603
623
|
for (const [id, agent] of Object.entries(all)) {
|