@swarmclawai/swarmclaw 0.7.2 → 0.7.4
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 +116 -50
- package/bin/package-manager.js +157 -0
- package/bin/package-manager.test.js +90 -0
- package/bin/server-cmd.js +38 -7
- package/bin/swarmclaw.js +54 -4
- package/bin/update-cmd.js +48 -10
- package/bin/update-cmd.test.js +55 -0
- package/package.json +8 -3
- package/scripts/postinstall.mjs +26 -0
- package/src/app/api/agents/[id]/route.ts +43 -0
- package/src/app/api/agents/[id]/thread/route.ts +39 -8
- package/src/app/api/agents/route.ts +35 -2
- package/src/app/api/auth/route.ts +77 -8
- package/src/app/api/chatrooms/[id]/chat/route.ts +22 -6
- package/src/app/api/chatrooms/[id]/pins/route.ts +2 -1
- package/src/app/api/chatrooms/[id]/reactions/route.ts +2 -1
- package/src/app/api/chatrooms/[id]/route.ts +6 -0
- package/src/app/api/chats/[id]/browser/route.ts +5 -1
- package/src/app/api/chats/[id]/chat/route.ts +7 -3
- package/src/app/api/chats/[id]/messages/route.ts +19 -13
- package/src/app/api/chats/[id]/route.ts +30 -0
- package/src/app/api/chats/[id]/stop/route.ts +6 -1
- package/src/app/api/chats/heartbeat/route.ts +2 -1
- package/src/app/api/chats/route.ts +23 -1
- package/src/app/api/connectors/[id]/doctor/route.ts +26 -0
- package/src/app/api/connectors/doctor/route.ts +13 -0
- package/src/app/api/external-agents/[id]/heartbeat/route.ts +33 -0
- package/src/app/api/external-agents/[id]/route.ts +31 -0
- package/src/app/api/external-agents/register/route.ts +3 -0
- package/src/app/api/external-agents/route.ts +66 -0
- package/src/app/api/files/open/route.ts +16 -14
- package/src/app/api/gateways/[id]/health/route.ts +28 -0
- package/src/app/api/gateways/[id]/route.ts +79 -0
- package/src/app/api/gateways/route.ts +57 -0
- package/src/app/api/memory/maintenance/route.ts +11 -1
- package/src/app/api/openclaw/agent-files/route.ts +27 -4
- package/src/app/api/openclaw/gateway/route.ts +10 -7
- package/src/app/api/openclaw/skills/route.ts +12 -4
- package/src/app/api/plugins/dependencies/route.ts +24 -0
- package/src/app/api/plugins/install/route.ts +15 -92
- package/src/app/api/plugins/route.ts +3 -26
- package/src/app/api/plugins/settings/route.ts +17 -12
- package/src/app/api/plugins/ui/route.ts +1 -0
- package/src/app/api/providers/[id]/discover-models/route.ts +27 -0
- package/src/app/api/schedules/[id]/route.ts +38 -9
- package/src/app/api/schedules/route.ts +51 -28
- package/src/app/api/settings/route.ts +55 -17
- package/src/app/api/setup/doctor/route.ts +6 -4
- package/src/app/api/tasks/[id]/route.ts +16 -6
- package/src/app/api/tasks/bulk/route.ts +3 -3
- package/src/app/api/tasks/route.ts +9 -4
- package/src/app/api/webhooks/[id]/route.ts +8 -1
- package/src/app/page.tsx +135 -17
- package/src/cli/binary.test.js +142 -0
- package/src/cli/index.js +38 -11
- package/src/cli/index.test.js +195 -0
- package/src/cli/index.ts +21 -12
- package/src/cli/server-cmd.test.js +59 -0
- package/src/cli/spec.js +20 -2
- package/src/components/agents/agent-card.tsx +15 -12
- package/src/components/agents/agent-chat-list.tsx +101 -1
- package/src/components/agents/agent-list.tsx +46 -9
- package/src/components/agents/agent-sheet.tsx +456 -23
- package/src/components/agents/inspector-panel.tsx +110 -49
- package/src/components/agents/sandbox-env-panel.tsx +4 -1
- package/src/components/auth/access-key-gate.tsx +36 -97
- package/src/components/auth/setup-wizard.tsx +970 -275
- package/src/components/chat/chat-area.tsx +70 -27
- package/src/components/chat/chat-card.tsx +6 -21
- package/src/components/chat/chat-header.tsx +263 -366
- package/src/components/chat/chat-list.tsx +62 -26
- package/src/components/chat/checkpoint-timeline.tsx +1 -1
- package/src/components/chat/message-list.tsx +145 -19
- package/src/components/chatrooms/chatroom-input.tsx +96 -33
- package/src/components/chatrooms/chatroom-list.tsx +141 -72
- package/src/components/chatrooms/chatroom-message.tsx +7 -6
- package/src/components/chatrooms/chatroom-sheet.tsx +13 -1
- package/src/components/chatrooms/chatroom-tool-request-banner.tsx +5 -2
- package/src/components/chatrooms/chatroom-view.tsx +422 -209
- package/src/components/chatrooms/reaction-picker.tsx +38 -33
- package/src/components/connectors/connector-list.tsx +265 -127
- package/src/components/connectors/connector-sheet.tsx +217 -0
- package/src/components/gateways/gateway-sheet.tsx +567 -0
- package/src/components/home/home-view.tsx +128 -4
- package/src/components/input/chat-input.tsx +135 -86
- package/src/components/layout/app-layout.tsx +385 -194
- package/src/components/layout/mobile-header.tsx +26 -8
- package/src/components/memory/memory-browser.tsx +71 -6
- package/src/components/memory/memory-card.tsx +18 -0
- package/src/components/memory/memory-detail.tsx +58 -31
- package/src/components/memory/memory-sheet.tsx +32 -4
- package/src/components/plugins/plugin-list.tsx +15 -3
- package/src/components/plugins/plugin-sheet.tsx +118 -9
- package/src/components/projects/project-detail.tsx +189 -1
- package/src/components/providers/provider-list.tsx +158 -2
- package/src/components/providers/provider-sheet.tsx +81 -70
- package/src/components/shared/agent-picker-list.tsx +2 -2
- package/src/components/shared/bottom-sheet.tsx +31 -15
- package/src/components/shared/command-palette.tsx +111 -24
- package/src/components/shared/confirm-dialog.tsx +45 -30
- package/src/components/shared/model-combobox.tsx +90 -8
- package/src/components/shared/settings/plugin-manager.tsx +20 -4
- package/src/components/shared/settings/section-capability-policy.tsx +105 -0
- package/src/components/shared/settings/section-heartbeat.tsx +88 -6
- package/src/components/shared/settings/section-orchestrator.tsx +6 -3
- package/src/components/shared/settings/section-runtime-loop.tsx +5 -5
- package/src/components/shared/settings/section-secrets.tsx +6 -6
- package/src/components/shared/settings/section-user-preferences.tsx +1 -1
- package/src/components/shared/settings/section-voice.tsx +5 -1
- package/src/components/shared/settings/section-web-search.tsx +10 -2
- package/src/components/shared/settings/settings-page.tsx +248 -47
- package/src/components/tasks/approvals-panel.tsx +211 -18
- package/src/components/tasks/task-board.tsx +242 -46
- package/src/components/ui/dialog.tsx +2 -2
- package/src/components/usage/metrics-dashboard.tsx +74 -1
- package/src/components/wallets/wallet-approval-dialog.tsx +59 -54
- package/src/components/wallets/wallet-panel.tsx +17 -5
- package/src/components/webhooks/webhook-sheet.tsx +7 -7
- package/src/lib/auth.ts +17 -0
- package/src/lib/chat-streaming-state.test.ts +108 -0
- package/src/lib/chat-streaming-state.ts +108 -0
- package/src/lib/heartbeat-defaults.ts +48 -0
- package/src/lib/memory-presentation.ts +59 -0
- package/src/lib/openclaw-agent-id.test.ts +14 -0
- package/src/lib/openclaw-agent-id.ts +31 -0
- package/src/lib/provider-model-discovery-client.ts +29 -0
- package/src/lib/providers/index.ts +12 -5
- package/src/lib/runtime-loop.ts +105 -3
- package/src/lib/safe-storage.ts +6 -1
- package/src/lib/server/agent-assignment.test.ts +112 -0
- package/src/lib/server/agent-assignment.ts +169 -0
- package/src/lib/server/agent-runtime-config.test.ts +141 -0
- package/src/lib/server/agent-runtime-config.ts +277 -0
- package/src/lib/server/approval-connector-notify.test.ts +253 -0
- package/src/lib/server/approvals-auto-approve.test.ts +264 -0
- package/src/lib/server/approvals.ts +483 -75
- package/src/lib/server/autonomy-runtime.test.ts +341 -0
- package/src/lib/server/browser-state.test.ts +118 -0
- package/src/lib/server/browser-state.ts +123 -0
- package/src/lib/server/build-llm.test.ts +44 -0
- package/src/lib/server/build-llm.ts +11 -4
- package/src/lib/server/builtin-plugins.ts +34 -0
- package/src/lib/server/chat-execution-heartbeat.test.ts +40 -0
- package/src/lib/server/chat-execution-tool-events.test.ts +219 -0
- package/src/lib/server/chat-execution.ts +402 -125
- package/src/lib/server/chatroom-health.test.ts +26 -0
- package/src/lib/server/chatroom-health.ts +2 -3
- package/src/lib/server/chatroom-helpers.test.ts +74 -2
- package/src/lib/server/chatroom-helpers.ts +144 -11
- package/src/lib/server/chatroom-session-persistence.test.ts +87 -0
- package/src/lib/server/connectors/discord.ts +175 -11
- package/src/lib/server/connectors/doctor.test.ts +80 -0
- package/src/lib/server/connectors/doctor.ts +116 -0
- package/src/lib/server/connectors/manager.ts +994 -130
- package/src/lib/server/connectors/policy.test.ts +222 -0
- package/src/lib/server/connectors/policy.ts +452 -0
- package/src/lib/server/connectors/slack.ts +189 -10
- package/src/lib/server/connectors/telegram.ts +65 -15
- package/src/lib/server/connectors/thread-context.test.ts +44 -0
- package/src/lib/server/connectors/thread-context.ts +72 -0
- package/src/lib/server/connectors/types.ts +41 -11
- package/src/lib/server/daemon-state.ts +62 -3
- package/src/lib/server/data-dir.ts +13 -0
- package/src/lib/server/delegation-jobs.test.ts +140 -0
- package/src/lib/server/delegation-jobs.ts +248 -0
- package/src/lib/server/document-utils.test.ts +47 -0
- package/src/lib/server/document-utils.ts +397 -0
- package/src/lib/server/eval/agent-regression.test.ts +47 -0
- package/src/lib/server/eval/agent-regression.ts +1742 -0
- package/src/lib/server/eval/runner.ts +11 -1
- package/src/lib/server/eval/store.ts +2 -1
- package/src/lib/server/heartbeat-service.ts +23 -43
- package/src/lib/server/heartbeat-source.test.ts +22 -0
- package/src/lib/server/heartbeat-source.ts +7 -0
- package/src/lib/server/identity-continuity.test.ts +77 -0
- package/src/lib/server/identity-continuity.ts +127 -0
- package/src/lib/server/mailbox-utils.ts +347 -0
- package/src/lib/server/main-agent-loop.ts +31 -964
- package/src/lib/server/memory-db.ts +4 -6
- package/src/lib/server/memory-tiers.ts +40 -0
- package/src/lib/server/openclaw-agent-resolver.test.ts +70 -0
- package/src/lib/server/openclaw-agent-resolver.ts +128 -0
- package/src/lib/server/openclaw-exec-config.ts +6 -5
- package/src/lib/server/openclaw-gateway.ts +123 -36
- package/src/lib/server/openclaw-skills-normalize.test.ts +56 -0
- package/src/lib/server/openclaw-skills-normalize.ts +136 -0
- package/src/lib/server/openclaw-sync.ts +3 -2
- package/src/lib/server/orchestrator-lg.ts +18 -8
- package/src/lib/server/orchestrator.ts +5 -4
- package/src/lib/server/playwright-proxy.mjs +27 -3
- package/src/lib/server/plugins.test.ts +215 -0
- package/src/lib/server/plugins.ts +832 -69
- package/src/lib/server/provider-health.ts +33 -3
- package/src/lib/server/provider-model-discovery.ts +481 -0
- package/src/lib/server/queue.ts +4 -21
- package/src/lib/server/runtime-settings.test.ts +119 -0
- package/src/lib/server/runtime-settings.ts +12 -92
- package/src/lib/server/schedule-normalization.ts +187 -0
- package/src/lib/server/scheduler.ts +2 -0
- package/src/lib/server/session-archive-memory.test.ts +85 -0
- package/src/lib/server/session-archive-memory.ts +230 -0
- package/src/lib/server/session-mailbox.ts +8 -18
- package/src/lib/server/session-reset-policy.test.ts +99 -0
- package/src/lib/server/session-reset-policy.ts +311 -0
- package/src/lib/server/session-run-manager.ts +33 -80
- package/src/lib/server/session-tools/autonomy-tools.test.ts +128 -0
- package/src/lib/server/session-tools/calendar.ts +2 -12
- package/src/lib/server/session-tools/connector.ts +109 -8
- package/src/lib/server/session-tools/context.ts +14 -2
- package/src/lib/server/session-tools/crawl.ts +447 -0
- package/src/lib/server/session-tools/crud.ts +96 -34
- package/src/lib/server/session-tools/delegate-fallback.test.ts +219 -0
- package/src/lib/server/session-tools/delegate.ts +406 -20
- package/src/lib/server/session-tools/discovery-approvals.test.ts +170 -0
- package/src/lib/server/session-tools/discovery.ts +40 -12
- package/src/lib/server/session-tools/document.ts +283 -0
- package/src/lib/server/session-tools/email.ts +1 -3
- package/src/lib/server/session-tools/extract.ts +137 -0
- package/src/lib/server/session-tools/file-normalize.test.ts +98 -0
- package/src/lib/server/session-tools/file-send.test.ts +84 -1
- package/src/lib/server/session-tools/file.ts +243 -24
- package/src/lib/server/session-tools/http.ts +9 -3
- package/src/lib/server/session-tools/human-loop.ts +227 -0
- package/src/lib/server/session-tools/image-gen.ts +1 -3
- package/src/lib/server/session-tools/index.ts +87 -2
- package/src/lib/server/session-tools/mailbox.ts +276 -0
- package/src/lib/server/session-tools/manage-schedules.test.ts +137 -0
- package/src/lib/server/session-tools/memory.ts +35 -3
- package/src/lib/server/session-tools/monitor.ts +162 -12
- package/src/lib/server/session-tools/normalize-tool-args.ts +17 -14
- package/src/lib/server/session-tools/openclaw-nodes.test.ts +111 -0
- package/src/lib/server/session-tools/openclaw-nodes.ts +86 -20
- package/src/lib/server/session-tools/platform-normalize.test.ts +142 -0
- package/src/lib/server/session-tools/platform.ts +142 -4
- package/src/lib/server/session-tools/plugin-creator.ts +95 -25
- package/src/lib/server/session-tools/primitive-tools.test.ts +257 -0
- package/src/lib/server/session-tools/replicate.ts +1 -3
- package/src/lib/server/session-tools/sandbox.ts +51 -92
- package/src/lib/server/session-tools/schedule.ts +20 -10
- package/src/lib/server/session-tools/session-info.ts +58 -4
- package/src/lib/server/session-tools/session-tools-wiring.test.ts +54 -17
- package/src/lib/server/session-tools/shell.ts +2 -2
- package/src/lib/server/session-tools/subagent.ts +195 -27
- package/src/lib/server/session-tools/table.ts +587 -0
- package/src/lib/server/session-tools/wallet.ts +13 -10
- package/src/lib/server/session-tools/web-browser-config.test.ts +39 -0
- package/src/lib/server/session-tools/web.ts +947 -108
- package/src/lib/server/storage.ts +255 -10
- package/src/lib/server/stream-agent-chat.test.ts +61 -0
- package/src/lib/server/stream-agent-chat.ts +185 -25
- package/src/lib/server/structured-extract.test.ts +72 -0
- package/src/lib/server/structured-extract.ts +373 -0
- package/src/lib/server/task-mention.test.ts +16 -2
- package/src/lib/server/task-mention.ts +61 -11
- package/src/lib/server/tool-aliases.ts +80 -12
- package/src/lib/server/tool-capability-policy.ts +7 -1
- package/src/lib/server/tool-retry.ts +2 -0
- package/src/lib/server/watch-jobs.test.ts +173 -0
- package/src/lib/server/watch-jobs.ts +532 -0
- package/src/lib/server/ws-hub.ts +5 -3
- package/src/lib/setup-defaults.ts +352 -11
- package/src/lib/tool-definitions.ts +3 -4
- package/src/lib/validation/schemas.test.ts +26 -0
- package/src/lib/validation/schemas.ts +62 -1
- package/src/lib/ws-client.ts +14 -12
- package/src/proxy.ts +5 -5
- package/src/stores/use-app-store.ts +43 -7
- package/src/stores/use-chat-store.ts +31 -2
- package/src/stores/use-chatroom-store.ts +153 -26
- package/src/types/index.ts +470 -44
- package/src/app/api/chats/[id]/main-loop/route.ts +0 -94
- package/src/components/chat/new-chat-sheet.tsx +0 -253
- package/src/lib/server/main-session.ts +0 -17
- package/src/lib/server/session-run-manager.test.ts +0 -26
|
@@ -14,22 +14,20 @@ function getDenoPath(): string | null {
|
|
|
14
14
|
return findBinaryOnPath('deno')
|
|
15
15
|
}
|
|
16
16
|
|
|
17
|
-
function getNodePath(): string | null {
|
|
18
|
-
return findBinaryOnPath('node')
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function getTsxPath(): string | null {
|
|
22
|
-
return findBinaryOnPath('tsx')
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
function getPythonPath(): string | null {
|
|
26
|
-
return findBinaryOnPath('python3') ?? findBinaryOnPath('python')
|
|
27
|
-
}
|
|
28
|
-
|
|
29
17
|
const EXT_MAP: Record<string, string> = {
|
|
30
18
|
javascript: 'js',
|
|
31
19
|
typescript: 'ts',
|
|
32
|
-
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
function sandboxUnavailableError(reason: string): string {
|
|
23
|
+
return JSON.stringify({
|
|
24
|
+
error: reason,
|
|
25
|
+
guidance: [
|
|
26
|
+
'Install Deno or run `npm run setup:easy` to enable sandbox_exec.',
|
|
27
|
+
'Use http_request for straightforward API calls.',
|
|
28
|
+
'Use plugin_creator plus manage_schedules for recurring automations.',
|
|
29
|
+
],
|
|
30
|
+
})
|
|
33
31
|
}
|
|
34
32
|
|
|
35
33
|
/**
|
|
@@ -45,18 +43,13 @@ async function executeSandboxExec(args: any, context: { sessionId?: string; cwd?
|
|
|
45
43
|
const sessionId = context.sessionId ?? 'unknown'
|
|
46
44
|
const sandboxDir = path.join('/tmp', `swarmclaw-sandbox-${sessionId}-${Date.now()}`)
|
|
47
45
|
const denoPath = getDenoPath()
|
|
48
|
-
const nodePath = getNodePath()
|
|
49
|
-
const tsxPath = getTsxPath()
|
|
50
|
-
const pythonPath = getPythonPath()
|
|
51
46
|
|
|
52
|
-
if (language
|
|
53
|
-
return
|
|
54
|
-
}
|
|
55
|
-
if (language === 'typescript' && !denoPath && !tsxPath) {
|
|
56
|
-
return JSON.stringify({ error: 'No TypeScript runtime available. Install Deno or tsx.' })
|
|
47
|
+
if (language !== 'javascript' && language !== 'typescript') {
|
|
48
|
+
return sandboxUnavailableError('sandbox_exec currently supports only JavaScript and TypeScript via Deno.')
|
|
57
49
|
}
|
|
58
|
-
|
|
59
|
-
|
|
50
|
+
|
|
51
|
+
if (!denoPath) {
|
|
52
|
+
return sandboxUnavailableError('Deno is required for sandbox_exec. Unsafe Node/Python fallbacks are disabled.')
|
|
60
53
|
}
|
|
61
54
|
|
|
62
55
|
try {
|
|
@@ -65,36 +58,15 @@ async function executeSandboxExec(args: any, context: { sessionId?: string; cwd?
|
|
|
65
58
|
const scriptPath = path.join(sandboxDir, scriptFile)
|
|
66
59
|
fs.writeFileSync(scriptPath, code, 'utf-8')
|
|
67
60
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
cwd: sandboxDir, encoding: 'utf-8', timeout, maxBuffer: MAX_OUTPUT,
|
|
78
|
-
env: { PATH: process.env.PATH || '/usr/bin:/bin' } as any,
|
|
79
|
-
})
|
|
80
|
-
}
|
|
81
|
-
} else if (language === 'typescript') {
|
|
82
|
-
if (denoPath) {
|
|
83
|
-
result = spawnSync(denoPath, [
|
|
84
|
-
'run', '--allow-read=.', '--allow-write=.', '--allow-net', '--deny-env', '--no-prompt', scriptFile,
|
|
85
|
-
], { cwd: sandboxDir, encoding: 'utf-8', timeout, maxBuffer: MAX_OUTPUT })
|
|
86
|
-
} else {
|
|
87
|
-
result = spawnSync(tsxPath!, [scriptPath], {
|
|
88
|
-
cwd: sandboxDir, encoding: 'utf-8', timeout, maxBuffer: MAX_OUTPUT,
|
|
89
|
-
env: { PATH: process.env.PATH || '/usr/bin:/bin' } as any,
|
|
90
|
-
})
|
|
91
|
-
}
|
|
92
|
-
} else {
|
|
93
|
-
result = spawnSync(pythonPath!, [scriptPath], {
|
|
94
|
-
cwd: sandboxDir, encoding: 'utf-8', timeout, maxBuffer: MAX_OUTPUT,
|
|
95
|
-
env: { PATH: process.env.PATH || '/usr/bin:/bin' } as any,
|
|
96
|
-
})
|
|
97
|
-
}
|
|
61
|
+
const result = spawnSync(denoPath, [
|
|
62
|
+
'run',
|
|
63
|
+
'--allow-read=.',
|
|
64
|
+
'--allow-write=.',
|
|
65
|
+
'--allow-net',
|
|
66
|
+
'--deny-env',
|
|
67
|
+
'--no-prompt',
|
|
68
|
+
scriptFile,
|
|
69
|
+
], { cwd: sandboxDir, encoding: 'utf-8', timeout, maxBuffer: MAX_OUTPUT })
|
|
98
70
|
|
|
99
71
|
const stdout = truncate((result.stdout || '').toString(), MAX_OUTPUT)
|
|
100
72
|
const stderr = truncate((result.stderr || '').toString(), MAX_OUTPUT)
|
|
@@ -125,16 +97,18 @@ async function executeSandboxExec(args: any, context: { sessionId?: string; cwd?
|
|
|
125
97
|
}
|
|
126
98
|
|
|
127
99
|
async function executeListRuntimes() {
|
|
128
|
-
const
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
const ver = spawnSync(bin, ['--version'], { encoding: 'utf-8', timeout: 3000 })
|
|
132
|
-
runtimes[name] = { available: true, version: (ver.stdout || '').split('\n')[0]?.trim() || null }
|
|
133
|
-
} else {
|
|
134
|
-
runtimes[name] = { available: false }
|
|
135
|
-
}
|
|
100
|
+
const denoPath = getDenoPath()
|
|
101
|
+
if (!denoPath) {
|
|
102
|
+
return sandboxUnavailableError('Deno is not available for sandbox_exec.')
|
|
136
103
|
}
|
|
137
|
-
|
|
104
|
+
const ver = spawnSync(denoPath, ['--version'], { encoding: 'utf-8', timeout: 3000 })
|
|
105
|
+
return JSON.stringify({
|
|
106
|
+
deno: {
|
|
107
|
+
available: true,
|
|
108
|
+
version: (ver.stdout || '').split('\n')[0]?.trim() || null,
|
|
109
|
+
},
|
|
110
|
+
sandboxReady: true,
|
|
111
|
+
})
|
|
138
112
|
}
|
|
139
113
|
|
|
140
114
|
/**
|
|
@@ -142,18 +116,23 @@ async function executeListRuntimes() {
|
|
|
142
116
|
*/
|
|
143
117
|
const SandboxPlugin: Plugin = {
|
|
144
118
|
name: 'Core Sandbox',
|
|
145
|
-
description: '
|
|
119
|
+
description: 'Deno-based isolated code execution for JavaScript and TypeScript when custom code is necessary.',
|
|
146
120
|
hooks: {
|
|
147
|
-
getCapabilityDescription: () => 'I can run
|
|
121
|
+
getCapabilityDescription: () => 'I can run JavaScript or TypeScript in a Deno sandbox (`sandbox_exec`) when custom code is necessary. For straightforward API calls, use `http_request` instead.',
|
|
122
|
+
getOperatingGuidance: () => [
|
|
123
|
+
'Use `http_request` for straightforward REST/JSON API calls instead of writing code in `sandbox_exec`.',
|
|
124
|
+
'Use `sandbox_exec` only when custom parsing or transformation code is actually needed.',
|
|
125
|
+
'For recurring automations, prefer `plugin_creator` plus `manage_schedules` over repeated sandbox runs.',
|
|
126
|
+
],
|
|
148
127
|
} as PluginHooks,
|
|
149
128
|
tools: [
|
|
150
129
|
{
|
|
151
130
|
name: 'sandbox_exec',
|
|
152
|
-
description: 'Execute
|
|
131
|
+
description: 'Execute JavaScript or TypeScript in a Deno sandbox when custom code is necessary.',
|
|
153
132
|
parameters: {
|
|
154
133
|
type: 'object',
|
|
155
134
|
properties: {
|
|
156
|
-
language: { type: 'string', enum: ['javascript', 'typescript'
|
|
135
|
+
language: { type: 'string', enum: ['javascript', 'typescript'] },
|
|
157
136
|
code: { type: 'string' },
|
|
158
137
|
timeoutSec: { type: 'number' }
|
|
159
138
|
},
|
|
@@ -163,7 +142,7 @@ const SandboxPlugin: Plugin = {
|
|
|
163
142
|
},
|
|
164
143
|
{
|
|
165
144
|
name: 'sandbox_list_runtimes',
|
|
166
|
-
description: '
|
|
145
|
+
description: 'Report whether the Deno sandbox runtime is available.',
|
|
167
146
|
parameters: { type: 'object', properties: {} },
|
|
168
147
|
execute: async () => executeListRuntimes()
|
|
169
148
|
}
|
|
@@ -185,7 +164,11 @@ export function buildSandboxTools(bctx: ToolBuildContext): StructuredToolInterfa
|
|
|
185
164
|
{
|
|
186
165
|
name: 'sandbox_exec',
|
|
187
166
|
description: SandboxPlugin.tools![0].description,
|
|
188
|
-
schema: z.object({
|
|
167
|
+
schema: z.object({
|
|
168
|
+
language: z.enum(['javascript', 'typescript']),
|
|
169
|
+
code: z.string(),
|
|
170
|
+
timeoutSec: z.number().optional(),
|
|
171
|
+
})
|
|
189
172
|
}
|
|
190
173
|
),
|
|
191
174
|
tool(
|
|
@@ -198,29 +181,5 @@ export function buildSandboxTools(bctx: ToolBuildContext): StructuredToolInterfa
|
|
|
198
181
|
)
|
|
199
182
|
)
|
|
200
183
|
|
|
201
|
-
const openclawPath = findBinaryOnPath('openclaw') || findBinaryOnPath('clawdbot')
|
|
202
|
-
if (openclawPath) {
|
|
203
|
-
tools.push(
|
|
204
|
-
tool(
|
|
205
|
-
async (rawArgs) => {
|
|
206
|
-
const normalized = normalizeToolInputArgs((rawArgs ?? {}) as Record<string, unknown>)
|
|
207
|
-
const code = normalized.code as string | undefined
|
|
208
|
-
const explain = normalized.explain as boolean | undefined
|
|
209
|
-
try {
|
|
210
|
-
if (!code) return JSON.stringify({ error: 'code is required' })
|
|
211
|
-
const args = explain ? ['sandbox', 'explain', code] : ['sandbox', 'run', code]
|
|
212
|
-
const result = spawnSync(openclawPath, args, { encoding: 'utf-8', timeout: 60_000, maxBuffer: MAX_OUTPUT })
|
|
213
|
-
return JSON.stringify({ exitCode: result.status ?? 0, stdout: truncate(result.stdout || '', MAX_OUTPUT), stderr: truncate(result.stderr || '', MAX_OUTPUT) })
|
|
214
|
-
} catch (err: any) { return JSON.stringify({ error: err.message }) }
|
|
215
|
-
},
|
|
216
|
-
{
|
|
217
|
-
name: 'openclaw_sandbox',
|
|
218
|
-
description: 'Execute or explain code through OpenClaw CLI.',
|
|
219
|
-
schema: z.object({ code: z.string(), explain: z.boolean().optional() }),
|
|
220
|
-
}
|
|
221
|
-
)
|
|
222
|
-
)
|
|
223
|
-
}
|
|
224
|
-
|
|
225
184
|
return tools
|
|
226
185
|
}
|
|
@@ -6,6 +6,7 @@ import type { ToolBuildContext } from './context'
|
|
|
6
6
|
import type { Plugin, PluginHooks } from '@/types'
|
|
7
7
|
import { getPluginManager } from '../plugins'
|
|
8
8
|
import { normalizeToolInputArgs } from './normalize-tool-args'
|
|
9
|
+
import { createWatchJob } from '../watch-jobs'
|
|
9
10
|
|
|
10
11
|
/**
|
|
11
12
|
* Core Schedule Execution Logic
|
|
@@ -15,7 +16,7 @@ async function executeScheduleWake(args: { delayMinutes: number; message: string
|
|
|
15
16
|
const delayMinutes = normalized.delayMinutes as number
|
|
16
17
|
const message = normalized.message as string
|
|
17
18
|
if (!context.sessionId) return 'Cannot schedule wake: no session context.'
|
|
18
|
-
if (delayMinutes < 0 || delayMinutes >
|
|
19
|
+
if (delayMinutes < 0 || delayMinutes > 43_200) return 'delayMinutes must be between 0 and 43200 (30 days).'
|
|
19
20
|
|
|
20
21
|
if (delayMinutes === 0) {
|
|
21
22
|
enqueueSystemEvent(context.sessionId, `[Scheduled Wake Event / Reminder] ${message}`)
|
|
@@ -23,15 +24,24 @@ async function executeScheduleWake(args: { delayMinutes: number; message: string
|
|
|
23
24
|
return 'Successfully scheduled an immediate wake event.'
|
|
24
25
|
}
|
|
25
26
|
|
|
26
|
-
const
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
}
|
|
32
|
-
|
|
27
|
+
const runAt = Date.now() + delayMinutes * 60 * 1000
|
|
28
|
+
const watch = await createWatchJob({
|
|
29
|
+
type: 'time',
|
|
30
|
+
sessionId: context.sessionId,
|
|
31
|
+
resumeMessage: message,
|
|
32
|
+
description: `Scheduled wake in ${delayMinutes} minutes`,
|
|
33
|
+
target: { source: 'schedule_wake' },
|
|
34
|
+
condition: {},
|
|
35
|
+
runAt,
|
|
36
|
+
})
|
|
33
37
|
|
|
34
|
-
return
|
|
38
|
+
return JSON.stringify({
|
|
39
|
+
ok: true,
|
|
40
|
+
jobId: watch.id,
|
|
41
|
+
delayMinutes,
|
|
42
|
+
runAt,
|
|
43
|
+
message,
|
|
44
|
+
})
|
|
35
45
|
}
|
|
36
46
|
|
|
37
47
|
/**
|
|
@@ -39,7 +49,7 @@ async function executeScheduleWake(args: { delayMinutes: number; message: string
|
|
|
39
49
|
*/
|
|
40
50
|
const SchedulePlugin: Plugin = {
|
|
41
51
|
name: 'Core Scheduler',
|
|
42
|
-
description: 'Schedule wake events and reminders for agents.',
|
|
52
|
+
description: 'Schedule durable wake events and reminders for agents.',
|
|
43
53
|
hooks: {
|
|
44
54
|
getCapabilityDescription: () => 'I can set a conversational timer (`schedule_wake`) to remind myself to check back on something later in this chat.',
|
|
45
55
|
} as PluginHooks,
|
|
@@ -25,14 +25,36 @@ async function executeWhoAmI(context: { sessionId?: string; agentId?: string })
|
|
|
25
25
|
} catch (err: any) { return `Error: ${err.message}` }
|
|
26
26
|
}
|
|
27
27
|
|
|
28
|
+
function inferSessionsAction(
|
|
29
|
+
normalized: Record<string, unknown>,
|
|
30
|
+
context: { sessionId?: string; agentId?: string },
|
|
31
|
+
): string | undefined {
|
|
32
|
+
const explicit = typeof normalized.action === 'string' ? normalized.action.trim() : ''
|
|
33
|
+
if (explicit) return explicit
|
|
34
|
+
|
|
35
|
+
const hasUpdates = !!normalized.updates && typeof normalized.updates === 'object'
|
|
36
|
+
const hasSpawnTarget = typeof normalized.agentId === 'string' || typeof normalized.agent_id === 'string'
|
|
37
|
+
const hasHistoryTarget =
|
|
38
|
+
typeof normalized.sessionId === 'string'
|
|
39
|
+
|| typeof normalized.session_id === 'string'
|
|
40
|
+
|| typeof normalized.limit === 'number'
|
|
41
|
+
|| !!context.sessionId
|
|
42
|
+
|
|
43
|
+
if (hasUpdates) return 'update'
|
|
44
|
+
if (hasSpawnTarget) return 'spawn'
|
|
45
|
+
if (hasHistoryTarget) return 'history'
|
|
46
|
+
return 'list'
|
|
47
|
+
}
|
|
48
|
+
|
|
28
49
|
async function executeSessionsAction(args: any, context: { sessionId?: string; agentId?: string; cwd: string }) {
|
|
29
50
|
const normalized = normalizeToolInputArgs((args ?? {}) as Record<string, unknown>)
|
|
30
|
-
const action = normalized
|
|
51
|
+
const action = inferSessionsAction(normalized, context)
|
|
31
52
|
const sessionId = (normalized.sessionId ?? normalized.session_id) as string | undefined
|
|
32
53
|
const message = normalized.message as string | undefined
|
|
33
54
|
const limit = normalized.limit as number | undefined
|
|
34
55
|
const agentId = (normalized.agentId ?? normalized.agent_id) as string | undefined
|
|
35
56
|
const name = normalized.name as string | undefined
|
|
57
|
+
const updates = normalized.updates as Record<string, unknown> | undefined
|
|
36
58
|
try {
|
|
37
59
|
const sessions = loadSessions()
|
|
38
60
|
if (action === 'list') {
|
|
@@ -53,12 +75,43 @@ async function executeSessionsAction(args: any, context: { sessionId?: string; a
|
|
|
53
75
|
sessions[id] = {
|
|
54
76
|
id, name: (name || `${agent.name} Chat`).trim(), cwd: context.cwd, user: 'system',
|
|
55
77
|
provider: agent.provider, model: agent.model, credentialId: agent.credentialId || null,
|
|
56
|
-
messages: [], createdAt: now, lastActiveAt: now, sessionType: '
|
|
78
|
+
messages: [], createdAt: now, lastActiveAt: now, sessionType: 'human',
|
|
57
79
|
agentId: agent.id, parentSessionId: context.sessionId || undefined, plugins: agent.plugins || agent.tools || [],
|
|
58
80
|
}
|
|
59
81
|
saveSessions(sessions)
|
|
60
82
|
return JSON.stringify({ sessionId: id, name: agent.name })
|
|
61
83
|
}
|
|
84
|
+
if (action === 'update') {
|
|
85
|
+
const targetId = sessionId || context.sessionId || ''
|
|
86
|
+
if (!targetId) return 'sessionId required.'
|
|
87
|
+
const target = sessions[targetId]
|
|
88
|
+
if (!target) return 'Not found.'
|
|
89
|
+
const allowedKeys = new Set([
|
|
90
|
+
'thinkingLevel',
|
|
91
|
+
'connectorThinkLevel',
|
|
92
|
+
'sessionResetMode',
|
|
93
|
+
'sessionIdleTimeoutSec',
|
|
94
|
+
'sessionMaxAgeSec',
|
|
95
|
+
'sessionDailyResetAt',
|
|
96
|
+
'sessionResetTimezone',
|
|
97
|
+
'connectorSessionScope',
|
|
98
|
+
'connectorReplyMode',
|
|
99
|
+
'connectorThreadBinding',
|
|
100
|
+
'connectorGroupPolicy',
|
|
101
|
+
'connectorIdleTimeoutSec',
|
|
102
|
+
'connectorMaxAgeSec',
|
|
103
|
+
'identityState',
|
|
104
|
+
'provider',
|
|
105
|
+
'model',
|
|
106
|
+
])
|
|
107
|
+
const patch = updates && typeof updates === 'object' ? updates : {}
|
|
108
|
+
for (const [key, value] of Object.entries(patch)) {
|
|
109
|
+
if (!allowedKeys.has(key)) continue
|
|
110
|
+
target[key] = value
|
|
111
|
+
}
|
|
112
|
+
saveSessions(sessions)
|
|
113
|
+
return JSON.stringify({ sessionId: targetId, updated: Object.keys(patch).filter((key) => allowedKeys.has(key)) })
|
|
114
|
+
}
|
|
62
115
|
return `Unknown action "${action}".`
|
|
63
116
|
} catch (err: any) { return `Error: ${err.message}` }
|
|
64
117
|
}
|
|
@@ -86,11 +139,12 @@ const SessionInfoPlugin: Plugin = {
|
|
|
86
139
|
parameters: {
|
|
87
140
|
type: 'object',
|
|
88
141
|
properties: {
|
|
89
|
-
action: { type: 'string', enum: ['list', 'history', 'spawn', 'status', 'stop'] },
|
|
142
|
+
action: { type: 'string', enum: ['list', 'history', 'spawn', 'status', 'stop', 'update'] },
|
|
90
143
|
sessionId: { type: 'string' },
|
|
91
144
|
agentId: { type: 'string' },
|
|
92
145
|
message: { type: 'string' },
|
|
93
|
-
limit: { type: 'number' }
|
|
146
|
+
limit: { type: 'number' },
|
|
147
|
+
updates: { type: 'object' },
|
|
94
148
|
},
|
|
95
149
|
required: ['action']
|
|
96
150
|
},
|
|
@@ -24,6 +24,23 @@ describe('module exports', () => {
|
|
|
24
24
|
const mem = await import('./memory')
|
|
25
25
|
assert.equal(typeof mem.buildMemoryTools, 'function')
|
|
26
26
|
})
|
|
27
|
+
|
|
28
|
+
it('primitive tool builders are exported', async () => {
|
|
29
|
+
const document = await import('./document')
|
|
30
|
+
const extract = await import('./extract')
|
|
31
|
+
const table = await import('./table')
|
|
32
|
+
const crawl = await import('./crawl')
|
|
33
|
+
const mailbox = await import('./mailbox')
|
|
34
|
+
const humanLoop = await import('./human-loop')
|
|
35
|
+
const sandbox = await import('./sandbox')
|
|
36
|
+
assert.equal(typeof document.buildDocumentTools, 'function')
|
|
37
|
+
assert.equal(typeof extract.buildExtractTools, 'function')
|
|
38
|
+
assert.equal(typeof table.buildTableTools, 'function')
|
|
39
|
+
assert.equal(typeof crawl.buildCrawlTools, 'function')
|
|
40
|
+
assert.equal(typeof mailbox.buildMailboxTools, 'function')
|
|
41
|
+
assert.equal(typeof humanLoop.buildHumanLoopTools, 'function')
|
|
42
|
+
assert.equal(typeof sandbox.buildSandboxTools, 'function')
|
|
43
|
+
})
|
|
27
44
|
})
|
|
28
45
|
|
|
29
46
|
// ---------------------------------------------------------------------------
|
|
@@ -57,49 +74,69 @@ describe('buildSessionTools signature', () => {
|
|
|
57
74
|
// Verify the function has arity of at least 2
|
|
58
75
|
assert.ok(buildSessionTools.length >= 2, 'buildSessionTools should accept at least 2 params')
|
|
59
76
|
})
|
|
77
|
+
|
|
78
|
+
it('sandbox builder exposes only the local Deno sandbox tools', async () => {
|
|
79
|
+
const { buildSandboxTools } = await import('./sandbox')
|
|
80
|
+
const bctx: import('./context').ToolBuildContext = {
|
|
81
|
+
cwd: process.cwd(),
|
|
82
|
+
ctx: { sessionId: 'sandbox-test' },
|
|
83
|
+
hasPlugin: (name) => name === 'sandbox',
|
|
84
|
+
hasTool: (name) => name === 'sandbox',
|
|
85
|
+
cleanupFns: [],
|
|
86
|
+
commandTimeoutMs: 1_000,
|
|
87
|
+
claudeTimeoutMs: 1_000,
|
|
88
|
+
cliProcessTimeoutMs: 1_000,
|
|
89
|
+
persistDelegateResumeId: () => {},
|
|
90
|
+
readStoredDelegateResumeId: () => null,
|
|
91
|
+
resolveCurrentSession: () => null,
|
|
92
|
+
activePlugins: ['sandbox'],
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const tools = buildSandboxTools(bctx).map((tool) => tool.name).sort()
|
|
96
|
+
assert.deepEqual(tools, ['sandbox_exec', 'sandbox_list_runtimes'])
|
|
97
|
+
})
|
|
60
98
|
})
|
|
61
99
|
|
|
62
100
|
// ---------------------------------------------------------------------------
|
|
63
|
-
// 4. Memory tool schema
|
|
101
|
+
// 4. Memory tool schema
|
|
64
102
|
// buildMemoryTools calls getMemoryDb() eagerly so we cannot invoke it
|
|
65
103
|
// without a real SQLite DB. Instead we read the source and verify the
|
|
66
|
-
// action enum
|
|
104
|
+
// declared action enum matches the current JSON schema definition.
|
|
67
105
|
// ---------------------------------------------------------------------------
|
|
68
106
|
describe('memory tool knowledge actions (source verification)', () => {
|
|
69
|
-
it('action enum in memory.ts includes
|
|
107
|
+
it('action enum in memory.ts includes the declared base actions', async () => {
|
|
70
108
|
const fs = await import('fs')
|
|
71
109
|
const src = fs.readFileSync(
|
|
72
110
|
new URL('./memory.ts', import.meta.url).pathname,
|
|
73
111
|
'utf-8',
|
|
74
112
|
)
|
|
75
113
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
assert.ok(enumMatch, 'Should find a z.enum() for the action field')
|
|
114
|
+
const enumMatch = src.match(/action:\s*\{\s*type:\s*'string',\s*enum:\s*\[([^\]]+)\]/s)
|
|
115
|
+
assert.ok(enumMatch, 'Should find the action enum in the memory tool schema')
|
|
79
116
|
|
|
80
117
|
const enumBody = enumMatch![1]
|
|
81
|
-
|
|
82
|
-
|
|
118
|
+
const expectedActions = ['store', 'get', 'search', 'list', 'delete']
|
|
119
|
+
for (const action of expectedActions) {
|
|
120
|
+
assert.ok(
|
|
121
|
+
enumBody.includes(`'${action}'`),
|
|
122
|
+
`action enum should include '${action}'`,
|
|
123
|
+
)
|
|
124
|
+
}
|
|
83
125
|
})
|
|
84
126
|
|
|
85
|
-
it('action enum
|
|
127
|
+
it('action enum does not advertise removed knowledge actions', async () => {
|
|
86
128
|
const fs = await import('fs')
|
|
87
129
|
const src = fs.readFileSync(
|
|
88
130
|
new URL('./memory.ts', import.meta.url).pathname,
|
|
89
131
|
'utf-8',
|
|
90
132
|
)
|
|
91
133
|
|
|
92
|
-
const enumMatch = src.match(/
|
|
134
|
+
const enumMatch = src.match(/action:\s*\{\s*type:\s*'string',\s*enum:\s*\[([^\]]+)\]/s)
|
|
93
135
|
assert.ok(enumMatch)
|
|
94
136
|
const enumBody = enumMatch![1]
|
|
95
137
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
assert.ok(
|
|
99
|
-
enumBody.includes(`'${action}'`),
|
|
100
|
-
`action enum should include '${action}'`,
|
|
101
|
-
)
|
|
102
|
-
}
|
|
138
|
+
assert.equal(enumBody.includes("'knowledge_store'"), false)
|
|
139
|
+
assert.equal(enumBody.includes("'knowledge_search'"), false)
|
|
103
140
|
})
|
|
104
141
|
})
|
|
105
142
|
|
|
@@ -163,8 +163,8 @@ const ShellPlugin: Plugin = {
|
|
|
163
163
|
name: 'Core Shell',
|
|
164
164
|
description: 'Execute shell commands and manage background processes.',
|
|
165
165
|
hooks: {
|
|
166
|
-
getCapabilityDescription: () => 'I can run shell commands
|
|
167
|
-
getOperatingGuidance: () => ['Shell: use `
|
|
166
|
+
getCapabilityDescription: () => 'I can run shell commands with the unified `shell` tool. Use action `execute` for commands, and `list` / `status` / `poll` / `log` for long-lived processes.',
|
|
167
|
+
getOperatingGuidance: () => ['Shell: use `shell` with `{"action":"execute","command":"..."}` for servers, installs, scripts, and git. Use `background=true` for long-lived processes.', 'Verify servers with `shell` status/log actions and liveness probes before claiming success.', 'Resolve IPs/URLs via shell — never use placeholders. Retry path errors without workdir override.'],
|
|
168
168
|
} as PluginHooks,
|
|
169
169
|
tools: [
|
|
170
170
|
{
|