@swarmclawai/swarmclaw 0.7.1 → 0.7.3
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 +155 -150
- package/package.json +1 -1
- package/src/app/api/agents/[id]/route.ts +26 -0
- package/src/app/api/agents/[id]/thread/route.ts +37 -9
- package/src/app/api/agents/route.ts +13 -2
- package/src/app/api/auth/route.ts +76 -7
- package/src/app/api/chatrooms/[id]/chat/route.ts +7 -2
- package/src/app/api/{sessions → chats}/[id]/browser/route.ts +5 -1
- package/src/app/api/{sessions → chats}/[id]/chat/route.ts +7 -3
- package/src/app/api/{sessions → chats}/[id]/checkpoints/route.ts +1 -1
- package/src/app/api/chats/[id]/main-loop/route.ts +13 -0
- package/src/app/api/{sessions → chats}/[id]/messages/route.ts +19 -13
- package/src/app/api/{sessions → chats}/[id]/restore/route.ts +1 -1
- package/src/app/api/{sessions → chats}/[id]/route.ts +22 -52
- package/src/app/api/{sessions → chats}/[id]/stop/route.ts +6 -1
- package/src/app/api/{sessions → chats}/route.ts +21 -7
- 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/files/open/route.ts +16 -14
- 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/skills/route.ts +11 -3
- 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 +6 -26
- package/src/app/api/plugins/settings/route.ts +40 -0
- package/src/app/api/plugins/ui/route.ts +1 -0
- package/src/app/api/settings/route.ts +49 -7
- package/src/app/api/tasks/[id]/route.ts +15 -6
- package/src/app/api/tasks/bulk/route.ts +2 -2
- package/src/app/api/tasks/route.ts +9 -4
- package/src/app/api/usage/route.ts +30 -0
- package/src/app/api/webhooks/[id]/route.ts +8 -1
- package/src/app/page.tsx +9 -2
- package/src/cli/index.js +39 -33
- package/src/cli/index.ts +43 -49
- package/src/cli/spec.js +29 -27
- package/src/components/agents/agent-card.tsx +16 -13
- package/src/components/agents/agent-chat-list.tsx +104 -4
- package/src/components/agents/agent-list.tsx +54 -22
- package/src/components/agents/agent-sheet.tsx +209 -18
- package/src/components/agents/cron-job-form.tsx +3 -3
- package/src/components/agents/inspector-panel.tsx +110 -50
- package/src/components/auth/access-key-gate.tsx +36 -97
- package/src/components/auth/setup-wizard.tsx +5 -38
- package/src/components/chat/chat-area.tsx +39 -27
- package/src/components/{sessions/session-card.tsx → chat/chat-card.tsx} +7 -23
- package/src/components/chat/chat-header.tsx +299 -314
- package/src/components/{sessions/session-list.tsx → chat/chat-list.tsx} +11 -14
- 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 +5 -3
- 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/chatrooms/chatroom-view.tsx +347 -205
- package/src/components/connectors/connector-list.tsx +265 -127
- package/src/components/connectors/connector-sheet.tsx +218 -1
- package/src/components/home/home-view.tsx +129 -5
- package/src/components/layout/app-layout.tsx +392 -182
- package/src/components/layout/mobile-header.tsx +26 -8
- package/src/components/plugins/plugin-list.tsx +487 -254
- package/src/components/plugins/plugin-sheet.tsx +236 -13
- package/src/components/projects/project-detail.tsx +183 -0
- package/src/components/settings/gateway-connection-panel.tsx +1 -1
- package/src/components/shared/agent-picker-list.tsx +2 -2
- package/src/components/shared/command-palette.tsx +111 -25
- 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 +78 -1
- package/src/components/shared/settings/section-orchestrator.tsx +3 -3
- package/src/components/shared/settings/section-providers.tsx +1 -1
- 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 +244 -56
- package/src/components/tasks/approvals-panel.tsx +205 -18
- package/src/components/tasks/task-board.tsx +242 -46
- package/src/components/usage/metrics-dashboard.tsx +147 -1
- package/src/components/wallets/wallet-panel.tsx +17 -5
- package/src/components/webhooks/webhook-sheet.tsx +8 -8
- 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/chat.ts +1 -1
- package/src/lib/{sessions.ts → chats.ts} +28 -18
- package/src/lib/openclaw-agent-id.test.ts +14 -0
- package/src/lib/openclaw-agent-id.ts +31 -0
- package/src/lib/providers/claude-cli.ts +1 -1
- package/src/lib/server/agent-assignment.test.ts +112 -0
- package/src/lib/server/agent-assignment.ts +169 -0
- package/src/lib/server/approval-connector-notify.test.ts +253 -0
- package/src/lib/server/approvals-auto-approve.test.ts +205 -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 +36 -0
- package/src/lib/server/build-llm.ts +11 -4
- package/src/lib/server/builtin-plugins.ts +34 -0
- package/src/lib/server/capability-router.ts +10 -8
- package/src/lib/server/chat-execution-heartbeat.test.ts +40 -0
- package/src/lib/server/chat-execution-tool-events.test.ts +134 -0
- package/src/lib/server/chat-execution.ts +285 -165
- 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 +67 -2
- package/src/lib/server/chatroom-helpers.ts +48 -8
- 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 +948 -112
- 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 +188 -9
- 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/cost.ts +34 -1
- package/src/lib/server/daemon-state.ts +61 -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/heartbeat-service.ts +14 -40
- 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 +28 -1103
- 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 +5 -6
- 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 +20 -9
- package/src/lib/server/orchestrator.ts +7 -7
- package/src/lib/server/playwright-proxy.mjs +27 -3
- package/src/lib/server/plugins.test.ts +207 -0
- package/src/lib/server/plugins.ts +927 -66
- package/src/lib/server/provider-health.ts +38 -6
- package/src/lib/server/queue.ts +13 -28
- 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 -82
- package/src/lib/server/session-tools/autonomy-tools.test.ts +105 -0
- package/src/lib/server/session-tools/calendar.ts +366 -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 +114 -10
- package/src/lib/server/session-tools/context.ts +21 -5
- package/src/lib/server/session-tools/crawl.ts +447 -0
- package/src/lib/server/session-tools/crud.ts +74 -28
- package/src/lib/server/session-tools/delegate-fallback.test.ts +219 -0
- package/src/lib/server/session-tools/delegate.ts +497 -24
- package/src/lib/server/session-tools/discovery.ts +24 -6
- package/src/lib/server/session-tools/document.ts +283 -0
- package/src/lib/server/session-tools/edit_file.ts +4 -2
- package/src/lib/server/session-tools/email.ts +320 -0
- package/src/lib/server/session-tools/extract.ts +137 -0
- package/src/lib/server/session-tools/file-normalize.test.ts +93 -0
- package/src/lib/server/session-tools/file-send.test.ts +84 -1
- package/src/lib/server/session-tools/file.ts +241 -25
- 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/human-loop.ts +227 -0
- package/src/lib/server/session-tools/image-gen.ts +380 -0
- package/src/lib/server/session-tools/index.ts +130 -50
- package/src/lib/server/session-tools/mailbox.ts +276 -0
- package/src/lib/server/session-tools/memory.ts +172 -3
- package/src/lib/server/session-tools/monitor.ts +151 -8
- package/src/lib/server/session-tools/normalize-tool-args.ts +17 -14
- 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-normalize.test.ts +142 -0
- package/src/lib/server/session-tools/platform.ts +148 -7
- package/src/lib/server/session-tools/plugin-creator.ts +89 -26
- package/src/lib/server/session-tools/primitive-tools.test.ts +257 -0
- package/src/lib/server/session-tools/replicate.ts +301 -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 +24 -12
- package/src/lib/server/session-tools/session-info.ts +43 -7
- package/src/lib/server/session-tools/session-tools-wiring.test.ts +31 -17
- package/src/lib/server/session-tools/shell.ts +5 -2
- package/src/lib/server/session-tools/subagent.ts +194 -28
- package/src/lib/server/session-tools/table.ts +587 -0
- package/src/lib/server/session-tools/wallet.ts +42 -12
- package/src/lib/server/session-tools/web-browser-config.test.ts +39 -0
- package/src/lib/server/session-tools/web.ts +926 -91
- package/src/lib/server/storage.ts +255 -16
- package/src/lib/server/stream-agent-chat.ts +116 -268
- 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 -10
- package/src/lib/server/tool-aliases.ts +66 -18
- package/src/lib/server/tool-capability-policy.test.ts +9 -9
- package/src/lib/server/tool-capability-policy.ts +38 -27
- 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/tool-definitions.ts +4 -0
- package/src/lib/validation/schemas.test.ts +26 -0
- package/src/lib/validation/schemas.ts +10 -1
- package/src/lib/ws-client.ts +14 -12
- package/src/proxy.ts +5 -5
- package/src/stores/use-app-store.ts +5 -11
- package/src/stores/use-chat-store.ts +38 -9
- package/src/types/index.ts +352 -47
- package/src/app/api/sessions/[id]/main-loop/route.ts +0 -94
- package/src/components/sessions/new-session-sheet.tsx +0 -253
- package/src/lib/server/main-session.ts +0 -24
- package/src/lib/server/session-run-manager.test.ts +0 -23
- /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]/retry/route.ts +0 -0
- /package/src/app/api/{sessions → chats}/heartbeat/route.ts +0 -0
|
@@ -8,16 +8,98 @@ import { getPluginManager } from '../plugins'
|
|
|
8
8
|
import type { Plugin, PluginHooks } from '@/types'
|
|
9
9
|
import { safePath, truncate } from './context'
|
|
10
10
|
import { normalizeToolInputArgs } from './normalize-tool-args'
|
|
11
|
+
import { cancelWatchJob, createWatchJob, getWatchJob, listWatchJobs } from '../watch-jobs'
|
|
12
|
+
import { ensureSessionBrowserProfileId, loadBrowserSessionRecord } from '../browser-state'
|
|
13
|
+
|
|
14
|
+
type WatchKind = 'time' | 'http' | 'file' | 'task' | 'webhook' | 'page'
|
|
15
|
+
|
|
16
|
+
async function createDurableWatch(
|
|
17
|
+
normalized: Record<string, unknown>,
|
|
18
|
+
bctx: { cwd: string; sessionId?: string; agentId?: string | null },
|
|
19
|
+
explicitType?: WatchKind,
|
|
20
|
+
) {
|
|
21
|
+
const watchType = (explicitType || String(normalized.watchType || normalized.type || '').trim().toLowerCase()) as WatchKind
|
|
22
|
+
if (!watchType) return 'Error: watchType is required.'
|
|
23
|
+
if (!['time', 'http', 'file', 'task', 'webhook', 'page'].includes(watchType)) {
|
|
24
|
+
return `Error: Unsupported watchType "${watchType}".`
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const sessionId = typeof normalized.sessionId === 'string' ? normalized.sessionId : bctx.sessionId
|
|
28
|
+
const agentId = typeof normalized.agentId === 'string' ? normalized.agentId : (bctx.agentId || undefined)
|
|
29
|
+
const resumeMessage = String(normalized.resumeMessage || normalized.message || '').trim()
|
|
30
|
+
if (!resumeMessage) return 'Error: resumeMessage is required.'
|
|
31
|
+
|
|
32
|
+
const target = (normalized.target ?? normalized.url ?? normalized.path) as string | undefined
|
|
33
|
+
const delayMinutes = typeof normalized.delayMinutes === 'number' ? normalized.delayMinutes : undefined
|
|
34
|
+
const runAt = typeof normalized.runAt === 'number'
|
|
35
|
+
? normalized.runAt
|
|
36
|
+
: delayMinutes !== undefined
|
|
37
|
+
? Date.now() + Math.max(0, delayMinutes) * 60_000
|
|
38
|
+
: undefined
|
|
39
|
+
const intervalMs = typeof normalized.intervalSec === 'number'
|
|
40
|
+
? Math.max(15, normalized.intervalSec) * 1000
|
|
41
|
+
: typeof normalized.intervalMs === 'number'
|
|
42
|
+
? Math.max(15_000, normalized.intervalMs)
|
|
43
|
+
: undefined
|
|
44
|
+
const timeoutAt = typeof normalized.timeoutMinutes === 'number'
|
|
45
|
+
? Date.now() + Math.max(1, normalized.timeoutMinutes) * 60_000
|
|
46
|
+
: typeof normalized.timeoutAt === 'number'
|
|
47
|
+
? normalized.timeoutAt
|
|
48
|
+
: undefined
|
|
49
|
+
const browserProfileId = sessionId ? ensureSessionBrowserProfileId(sessionId).profileId : null
|
|
50
|
+
const targetPath = watchType === 'file' && target ? safePath(bctx.cwd, target) : target
|
|
51
|
+
const pageUrl = watchType === 'page' && !target && sessionId
|
|
52
|
+
? loadBrowserSessionRecord(sessionId)?.currentUrl || undefined
|
|
53
|
+
: undefined
|
|
54
|
+
const pageTarget = target || pageUrl
|
|
55
|
+
if ((watchType === 'http' || watchType === 'page') && !pageTarget) {
|
|
56
|
+
return `Error: ${watchType === 'page' ? 'url or active browser page' : 'url'} is required.`
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const job = await createWatchJob({
|
|
60
|
+
type: watchType,
|
|
61
|
+
sessionId: sessionId || null,
|
|
62
|
+
agentId: agentId || null,
|
|
63
|
+
createdByAgentId: agentId || null,
|
|
64
|
+
browserProfileId,
|
|
65
|
+
description: typeof normalized.description === 'string' ? normalized.description : null,
|
|
66
|
+
resumeMessage,
|
|
67
|
+
runAt,
|
|
68
|
+
intervalMs,
|
|
69
|
+
timeoutAt,
|
|
70
|
+
target: {
|
|
71
|
+
url: watchType === 'http' || watchType === 'page' ? pageTarget : undefined,
|
|
72
|
+
path: watchType === 'file' ? targetPath : undefined,
|
|
73
|
+
taskId: watchType === 'task' ? String(normalized.taskId || normalized.id || '') : undefined,
|
|
74
|
+
webhookId: watchType === 'webhook' ? String(normalized.webhookId || normalized.id || '') : undefined,
|
|
75
|
+
baselineHash: undefined,
|
|
76
|
+
},
|
|
77
|
+
condition: {
|
|
78
|
+
containsText: typeof normalized.containsText === 'string' ? normalized.containsText : undefined,
|
|
79
|
+
textGone: typeof normalized.textGone === 'string' ? normalized.textGone : undefined,
|
|
80
|
+
regex: typeof normalized.regex === 'string' ? normalized.regex : undefined,
|
|
81
|
+
changed: normalized.changed === true,
|
|
82
|
+
exists: normalized.exists,
|
|
83
|
+
status: typeof normalized.status === 'number' ? normalized.status : undefined,
|
|
84
|
+
statusIn: Array.isArray(normalized.statusIn) ? normalized.statusIn : undefined,
|
|
85
|
+
event: typeof normalized.event === 'string' ? normalized.event : undefined,
|
|
86
|
+
threshold: typeof normalized.threshold === 'number' ? normalized.threshold : undefined,
|
|
87
|
+
},
|
|
88
|
+
})
|
|
89
|
+
return JSON.stringify(job, null, 2)
|
|
90
|
+
}
|
|
11
91
|
|
|
12
92
|
/**
|
|
13
93
|
* Unified Monitoring Logic
|
|
14
94
|
*/
|
|
15
|
-
async function executeMonitorAction(args: any, bctx: { cwd: string }) {
|
|
95
|
+
async function executeMonitorAction(args: any, bctx: { cwd: string; sessionId?: string; agentId?: string | null }) {
|
|
16
96
|
const normalized = normalizeToolInputArgs((args ?? {}) as Record<string, unknown>)
|
|
17
97
|
const action = normalized.action as string | undefined
|
|
18
98
|
const target = (normalized.target ?? normalized.url ?? normalized.path) as string | undefined
|
|
19
99
|
const limit = normalized.limit as number | undefined
|
|
20
100
|
const threshold = normalized.threshold as number | undefined
|
|
101
|
+
const sessionId = typeof normalized.sessionId === 'string' ? normalized.sessionId : bctx.sessionId
|
|
102
|
+
const agentId = typeof normalized.agentId === 'string' ? normalized.agentId : (bctx.agentId || undefined)
|
|
21
103
|
|
|
22
104
|
try {
|
|
23
105
|
switch (action) {
|
|
@@ -65,6 +147,7 @@ async function executeMonitorAction(args: any, bctx: { cwd: string }) {
|
|
|
65
147
|
status: res.status,
|
|
66
148
|
ok: res.ok,
|
|
67
149
|
latency: `${latency}ms`,
|
|
150
|
+
thresholdExceeded: typeof threshold === 'number' ? latency >= threshold : undefined,
|
|
68
151
|
url
|
|
69
152
|
}, null, 2)
|
|
70
153
|
} catch (err: any) {
|
|
@@ -76,6 +159,55 @@ async function executeMonitorAction(args: any, bctx: { cwd: string }) {
|
|
|
76
159
|
}
|
|
77
160
|
}
|
|
78
161
|
|
|
162
|
+
case 'create_watch': {
|
|
163
|
+
return createDurableWatch(normalized, bctx)
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
case 'wait_until': {
|
|
167
|
+
return createDurableWatch(normalized, bctx, 'time')
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
case 'wait_for_http': {
|
|
171
|
+
return createDurableWatch(normalized, bctx, 'http')
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
case 'wait_for_file': {
|
|
175
|
+
return createDurableWatch(normalized, bctx, 'file')
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
case 'wait_for_task': {
|
|
179
|
+
return createDurableWatch(normalized, bctx, 'task')
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
case 'wait_for_webhook': {
|
|
183
|
+
return createDurableWatch(normalized, bctx, 'webhook')
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
case 'wait_for_page_change': {
|
|
187
|
+
return createDurableWatch(normalized, bctx, 'page')
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
case 'list_watches': {
|
|
191
|
+
const filterSessionId = normalized.all === true ? undefined : sessionId
|
|
192
|
+
return JSON.stringify(listWatchJobs({ sessionId: filterSessionId || null }), null, 2)
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
case 'get_watch': {
|
|
196
|
+
const id = String(normalized.id || '').trim()
|
|
197
|
+
if (!id) return 'Error: id is required.'
|
|
198
|
+
const job = getWatchJob(id)
|
|
199
|
+
if (!job) return `Error: watch job "${id}" not found.`
|
|
200
|
+
return JSON.stringify(job, null, 2)
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
case 'cancel_watch': {
|
|
204
|
+
const id = String(normalized.id || '').trim()
|
|
205
|
+
if (!id) return 'Error: id is required.'
|
|
206
|
+
const job = cancelWatchJob(id)
|
|
207
|
+
if (!job) return `Error: watch job "${id}" not found.`
|
|
208
|
+
return JSON.stringify(job, null, 2)
|
|
209
|
+
}
|
|
210
|
+
|
|
79
211
|
default:
|
|
80
212
|
return `Error: Unknown action "${action}"`
|
|
81
213
|
}
|
|
@@ -89,22 +221,29 @@ async function executeMonitorAction(args: any, bctx: { cwd: string }) {
|
|
|
89
221
|
*/
|
|
90
222
|
const MonitorPlugin: Plugin = {
|
|
91
223
|
name: 'Core Monitor',
|
|
92
|
-
description: 'System observability:
|
|
224
|
+
description: 'System observability and durable watch jobs: inspect system state, monitor files/endpoints/tasks, and resume agents when conditions trigger.',
|
|
93
225
|
hooks: {} as PluginHooks,
|
|
94
226
|
tools: [
|
|
95
227
|
{
|
|
96
228
|
name: 'monitor_tool',
|
|
97
|
-
description: 'Observe system health,
|
|
229
|
+
description: 'Observe system health, inspect logs/endpoints, or create durable waits like wait_for_http, wait_for_file, wait_for_webhook, and wait_for_page_change.',
|
|
98
230
|
parameters: {
|
|
99
231
|
type: 'object',
|
|
100
232
|
properties: {
|
|
101
|
-
action: { type: 'string', enum: ['sys_info', 'watch_log', 'ping'] },
|
|
233
|
+
action: { type: 'string', enum: ['sys_info', 'watch_log', 'ping', 'create_watch', 'wait_until', 'wait_for_http', 'wait_for_file', 'wait_for_task', 'wait_for_webhook', 'wait_for_page_change', 'list_watches', 'get_watch', 'cancel_watch'] },
|
|
102
234
|
target: { type: 'string', description: 'Log file path (for watch_log) or URL (for ping)' },
|
|
103
|
-
limit: { type: 'number', description: 'Number of lines or bytes to retrieve' }
|
|
235
|
+
limit: { type: 'number', description: 'Number of lines or bytes to retrieve' },
|
|
236
|
+
watchType: { type: 'string', enum: ['time', 'http', 'file', 'task', 'webhook', 'page'] },
|
|
237
|
+
resumeMessage: { type: 'string', description: 'Message injected when the watch triggers and the agent wakes up.' },
|
|
238
|
+
regex: { type: 'string', description: 'Regex pattern used by file/page/http watchers.' },
|
|
104
239
|
},
|
|
105
240
|
required: ['action']
|
|
106
241
|
},
|
|
107
|
-
execute: async (args, context) => executeMonitorAction(args, {
|
|
242
|
+
execute: async (args, context) => executeMonitorAction(args, {
|
|
243
|
+
cwd: context.session.cwd || process.cwd(),
|
|
244
|
+
sessionId: context.session.id,
|
|
245
|
+
agentId: context.session.agentId,
|
|
246
|
+
})
|
|
108
247
|
}
|
|
109
248
|
]
|
|
110
249
|
}
|
|
@@ -112,10 +251,14 @@ const MonitorPlugin: Plugin = {
|
|
|
112
251
|
getPluginManager().registerBuiltin('monitor', MonitorPlugin)
|
|
113
252
|
|
|
114
253
|
export function buildMonitorTools(bctx: ToolBuildContext): StructuredToolInterface[] {
|
|
115
|
-
if (!bctx.
|
|
254
|
+
if (!bctx.hasPlugin('monitor')) return []
|
|
116
255
|
return [
|
|
117
256
|
tool(
|
|
118
|
-
async (args) => executeMonitorAction(args, {
|
|
257
|
+
async (args) => executeMonitorAction(args, {
|
|
258
|
+
cwd: bctx.cwd,
|
|
259
|
+
sessionId: bctx.ctx?.sessionId || undefined,
|
|
260
|
+
agentId: bctx.ctx?.agentId || undefined,
|
|
261
|
+
}),
|
|
119
262
|
{
|
|
120
263
|
name: 'monitor_tool',
|
|
121
264
|
description: MonitorPlugin.tools![0].description,
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
export type ToolArgsRecord = Record<string, unknown>
|
|
2
|
+
const NESTED_WRAPPER_KEYS = ['input', 'args', 'arguments', 'payload', 'parameters'] as const
|
|
2
3
|
|
|
3
4
|
function parseRecordCandidate(value: unknown): ToolArgsRecord | null {
|
|
4
5
|
if (!value) return null
|
|
@@ -26,22 +27,24 @@ function parseRecordCandidate(value: unknown): ToolArgsRecord | null {
|
|
|
26
27
|
* as either objects or JSON strings.
|
|
27
28
|
*/
|
|
28
29
|
export function normalizeToolInputArgs(rawArgs: ToolArgsRecord): ToolArgsRecord {
|
|
29
|
-
const nestedSources: Array<ToolArgsRecord | null> = [
|
|
30
|
-
parseRecordCandidate(rawArgs.input),
|
|
31
|
-
parseRecordCandidate(rawArgs.args),
|
|
32
|
-
parseRecordCandidate(rawArgs.arguments),
|
|
33
|
-
parseRecordCandidate(rawArgs.payload),
|
|
34
|
-
]
|
|
35
|
-
|
|
36
30
|
const normalized: ToolArgsRecord = {}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
31
|
+
const queue: ToolArgsRecord[] = [rawArgs]
|
|
32
|
+
const visited = new Set<ToolArgsRecord>()
|
|
33
|
+
|
|
34
|
+
while (queue.length > 0) {
|
|
35
|
+
const current = queue.shift()
|
|
36
|
+
if (!current || visited.has(current)) continue
|
|
37
|
+
visited.add(current)
|
|
41
38
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
39
|
+
for (const key of NESTED_WRAPPER_KEYS) {
|
|
40
|
+
const nested = parseRecordCandidate(current[key])
|
|
41
|
+
if (nested) queue.push(nested)
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
for (const [key, value] of Object.entries(current)) {
|
|
45
|
+
if (value === undefined || value === null) continue
|
|
46
|
+
normalized[key] = value
|
|
47
|
+
}
|
|
45
48
|
}
|
|
46
49
|
|
|
47
50
|
return normalized
|
|
@@ -81,7 +81,7 @@ getPluginManager().registerBuiltin('openclaw_nodes', NodesPlugin)
|
|
|
81
81
|
* Legacy Bridge
|
|
82
82
|
*/
|
|
83
83
|
export function buildOpenClawNodeTools(bctx: ToolBuildContext): StructuredToolInterface[] {
|
|
84
|
-
if (!bctx.
|
|
84
|
+
if (!bctx.hasPlugin('openclaw_nodes')) return []
|
|
85
85
|
return [
|
|
86
86
|
tool(
|
|
87
87
|
async (args) => executeNodesAction(args),
|
|
@@ -128,7 +128,7 @@ getPluginManager().registerBuiltin('openclaw_workspace', WorkspacePlugin)
|
|
|
128
128
|
* Legacy Bridge
|
|
129
129
|
*/
|
|
130
130
|
export function buildOpenClawWorkspaceTools(bctx: ToolBuildContext): StructuredToolInterface[] {
|
|
131
|
-
if (!bctx.
|
|
131
|
+
if (!bctx.hasPlugin('openclaw_workspace')) return []
|
|
132
132
|
return [
|
|
133
133
|
tool(
|
|
134
134
|
async (args) => executeWorkspaceAction(args),
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { describe, it } from 'node:test'
|
|
2
|
+
import assert from 'node:assert/strict'
|
|
3
|
+
import { normalizePlatformActionArgs } from './platform'
|
|
4
|
+
|
|
5
|
+
describe('normalizePlatformActionArgs', () => {
|
|
6
|
+
it('packs top-level create fields into data', () => {
|
|
7
|
+
const out = normalizePlatformActionArgs({
|
|
8
|
+
resource: 'tasks',
|
|
9
|
+
action: 'create',
|
|
10
|
+
title: 'Write docs',
|
|
11
|
+
agentId: 'default',
|
|
12
|
+
})
|
|
13
|
+
|
|
14
|
+
assert.equal(out.resource, 'tasks')
|
|
15
|
+
assert.equal(out.action, 'create')
|
|
16
|
+
assert.deepEqual(JSON.parse(String(out.data)), {
|
|
17
|
+
title: 'Write docs',
|
|
18
|
+
agentId: 'default',
|
|
19
|
+
})
|
|
20
|
+
})
|
|
21
|
+
|
|
22
|
+
it('merges object data with top-level overrides', () => {
|
|
23
|
+
const out = normalizePlatformActionArgs({
|
|
24
|
+
resource: 'tasks',
|
|
25
|
+
action: 'create',
|
|
26
|
+
data: { title: 'Old title', agentId: 'coder' },
|
|
27
|
+
title: 'New title',
|
|
28
|
+
})
|
|
29
|
+
|
|
30
|
+
assert.deepEqual(JSON.parse(String(out.data)), {
|
|
31
|
+
title: 'New title',
|
|
32
|
+
agentId: 'coder',
|
|
33
|
+
})
|
|
34
|
+
})
|
|
35
|
+
|
|
36
|
+
it('normalizes legacy resources envelope with parameters payload', () => {
|
|
37
|
+
const out = normalizePlatformActionArgs({
|
|
38
|
+
input: JSON.stringify({
|
|
39
|
+
resources: [
|
|
40
|
+
{
|
|
41
|
+
resource: 'tasks',
|
|
42
|
+
action: 'create',
|
|
43
|
+
parameters: {
|
|
44
|
+
title: 'Legacy task',
|
|
45
|
+
assigned_agent: 'default',
|
|
46
|
+
},
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
}),
|
|
50
|
+
})
|
|
51
|
+
|
|
52
|
+
assert.equal(out.resource, 'tasks')
|
|
53
|
+
assert.equal(out.action, 'create')
|
|
54
|
+
assert.deepEqual(JSON.parse(String(out.data)), {
|
|
55
|
+
title: 'Legacy task',
|
|
56
|
+
assigned_agent: 'default',
|
|
57
|
+
})
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
it('normalizes singular resource names and resource payload objects', () => {
|
|
61
|
+
const out = normalizePlatformActionArgs({
|
|
62
|
+
input: JSON.stringify({
|
|
63
|
+
resource: 'task',
|
|
64
|
+
action: 'create',
|
|
65
|
+
task: {
|
|
66
|
+
title: 'Legacy singular task',
|
|
67
|
+
assigned_to: 'default',
|
|
68
|
+
},
|
|
69
|
+
}),
|
|
70
|
+
})
|
|
71
|
+
|
|
72
|
+
assert.equal(out.resource, 'tasks')
|
|
73
|
+
assert.equal(out.action, 'create')
|
|
74
|
+
assert.deepEqual(JSON.parse(String(out.data)), {
|
|
75
|
+
title: 'Legacy singular task',
|
|
76
|
+
assigned_to: 'default',
|
|
77
|
+
})
|
|
78
|
+
})
|
|
79
|
+
|
|
80
|
+
it('normalizes legacy backlog task resource names to tasks', () => {
|
|
81
|
+
const out = normalizePlatformActionArgs({
|
|
82
|
+
input: JSON.stringify({
|
|
83
|
+
resource: 'backlog_task',
|
|
84
|
+
action: 'create',
|
|
85
|
+
backlog_task: {
|
|
86
|
+
title: 'Legacy backlog task',
|
|
87
|
+
description: 'Keep the intended task payload',
|
|
88
|
+
},
|
|
89
|
+
}),
|
|
90
|
+
})
|
|
91
|
+
|
|
92
|
+
assert.equal(out.resource, 'tasks')
|
|
93
|
+
assert.equal(out.action, 'create')
|
|
94
|
+
assert.deepEqual(JSON.parse(String(out.data)), {
|
|
95
|
+
title: 'Legacy backlog task',
|
|
96
|
+
description: 'Keep the intended task payload',
|
|
97
|
+
})
|
|
98
|
+
})
|
|
99
|
+
|
|
100
|
+
it('normalizes resources entries that use type instead of resource', () => {
|
|
101
|
+
const out = normalizePlatformActionArgs({
|
|
102
|
+
input: JSON.stringify({
|
|
103
|
+
action: 'create',
|
|
104
|
+
resources: [
|
|
105
|
+
{
|
|
106
|
+
type: 'task',
|
|
107
|
+
parameters: {
|
|
108
|
+
title: 'Typed task resource',
|
|
109
|
+
description: 'Created through a typed resources envelope',
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
],
|
|
113
|
+
}),
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
assert.equal(out.resource, 'tasks')
|
|
117
|
+
assert.equal(out.action, 'create')
|
|
118
|
+
assert.deepEqual(JSON.parse(String(out.data)), {
|
|
119
|
+
title: 'Typed task resource',
|
|
120
|
+
description: 'Created through a typed resources envelope',
|
|
121
|
+
})
|
|
122
|
+
})
|
|
123
|
+
|
|
124
|
+
it('infers schedules resource from create_schedule style actions', () => {
|
|
125
|
+
const out = normalizePlatformActionArgs({
|
|
126
|
+
input: JSON.stringify({
|
|
127
|
+
action: 'create_schedule',
|
|
128
|
+
data: {
|
|
129
|
+
name: 'Surgery check-in',
|
|
130
|
+
scheduleType: 'once',
|
|
131
|
+
},
|
|
132
|
+
}),
|
|
133
|
+
})
|
|
134
|
+
|
|
135
|
+
assert.equal(out.resource, 'schedules')
|
|
136
|
+
assert.equal(out.action, 'create')
|
|
137
|
+
assert.deepEqual(JSON.parse(String(out.data)), {
|
|
138
|
+
name: 'Surgery check-in',
|
|
139
|
+
scheduleType: 'once',
|
|
140
|
+
})
|
|
141
|
+
})
|
|
142
|
+
})
|
|
@@ -6,17 +6,155 @@ import type { Plugin, PluginHooks } from '@/types'
|
|
|
6
6
|
import { getPluginManager } from '../plugins'
|
|
7
7
|
import { normalizeToolInputArgs } from './normalize-tool-args'
|
|
8
8
|
|
|
9
|
+
function parsePlatformData(value: unknown): Record<string, unknown> | null {
|
|
10
|
+
if (!value) return null
|
|
11
|
+
if (typeof value === 'object' && !Array.isArray(value)) {
|
|
12
|
+
return value as Record<string, unknown>
|
|
13
|
+
}
|
|
14
|
+
if (typeof value !== 'string') return null
|
|
15
|
+
const trimmed = value.trim()
|
|
16
|
+
if (!trimmed) return null
|
|
17
|
+
try {
|
|
18
|
+
const parsed = JSON.parse(trimmed)
|
|
19
|
+
if (parsed && typeof parsed === 'object' && !Array.isArray(parsed)) {
|
|
20
|
+
return parsed as Record<string, unknown>
|
|
21
|
+
}
|
|
22
|
+
} catch {
|
|
23
|
+
// Preserve non-JSON data strings as-is in the caller.
|
|
24
|
+
}
|
|
25
|
+
return null
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function firstPlatformResource(value: unknown): Record<string, unknown> | null {
|
|
29
|
+
if (!Array.isArray(value)) return null
|
|
30
|
+
const first = value.find((entry) => entry && typeof entry === 'object' && !Array.isArray(entry))
|
|
31
|
+
return first && typeof first === 'object' && !Array.isArray(first)
|
|
32
|
+
? first as Record<string, unknown>
|
|
33
|
+
: null
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function normalizePlatformResourceName(value: unknown): string | undefined {
|
|
37
|
+
if (typeof value !== 'string') return undefined
|
|
38
|
+
const normalized = value.trim().toLowerCase()
|
|
39
|
+
if (!normalized) return undefined
|
|
40
|
+
const singularMap: Record<string, string> = {
|
|
41
|
+
agent: 'agents',
|
|
42
|
+
task: 'tasks',
|
|
43
|
+
backlog_task: 'tasks',
|
|
44
|
+
'backlog-task': 'tasks',
|
|
45
|
+
backlogtask: 'tasks',
|
|
46
|
+
task_backlog: 'tasks',
|
|
47
|
+
'task-backlog': 'tasks',
|
|
48
|
+
work_item: 'tasks',
|
|
49
|
+
'work-item': 'tasks',
|
|
50
|
+
schedule: 'schedules',
|
|
51
|
+
skill: 'skills',
|
|
52
|
+
document: 'documents',
|
|
53
|
+
secret: 'secrets',
|
|
54
|
+
connector: 'connectors',
|
|
55
|
+
session: 'sessions',
|
|
56
|
+
}
|
|
57
|
+
return singularMap[normalized] || normalized
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
function inferPlatformResourceFromAction(value: unknown): { resource?: string; action?: string } {
|
|
61
|
+
if (typeof value !== 'string') return {}
|
|
62
|
+
const normalized = value.trim().toLowerCase().replace(/-/g, '_')
|
|
63
|
+
if (!normalized) return {}
|
|
64
|
+
const match = normalized.match(/^(list|get|create|update|delete)_([a-z_]+)$/)
|
|
65
|
+
if (!match) return {}
|
|
66
|
+
const [, action, rawResource] = match
|
|
67
|
+
const resource = normalizePlatformResourceName(rawResource)
|
|
68
|
+
if (!resource) return {}
|
|
69
|
+
return { resource, action }
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
function extractPlatformFields(value: Record<string, unknown>): Record<string, unknown> {
|
|
73
|
+
const fields: Record<string, unknown> = {}
|
|
74
|
+
for (const [key, fieldValue] of Object.entries(value)) {
|
|
75
|
+
if (fieldValue === undefined || fieldValue === null) continue
|
|
76
|
+
if (['input', 'args', 'arguments', 'payload', 'resources', 'parameters', 'resource', 'type', 'action', 'id'].includes(key)) continue
|
|
77
|
+
fields[key] = fieldValue
|
|
78
|
+
}
|
|
79
|
+
return fields
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
export function normalizePlatformActionArgs(rawArgs: Record<string, unknown>): Record<string, unknown> {
|
|
83
|
+
const normalized = normalizeToolInputArgs(rawArgs)
|
|
84
|
+
const resourceEntry = firstPlatformResource(normalized.resources)
|
|
85
|
+
const { resource, action, id, data, ...rest } = normalized
|
|
86
|
+
const payload: Record<string, unknown> = {}
|
|
87
|
+
const resourceValue = resource ?? resourceEntry?.resource ?? resourceEntry?.type
|
|
88
|
+
const rawResourceName = typeof resourceValue === 'string'
|
|
89
|
+
? String(resourceValue).trim()
|
|
90
|
+
: undefined
|
|
91
|
+
|
|
92
|
+
const rawAction = action ?? resourceEntry?.action
|
|
93
|
+
const inferredFromAction = resourceValue === undefined
|
|
94
|
+
? inferPlatformResourceFromAction(rawAction)
|
|
95
|
+
: {}
|
|
96
|
+
const effectiveResource = normalizePlatformResourceName(resourceValue) ?? inferredFromAction.resource
|
|
97
|
+
const effectiveAction = inferredFromAction.action && resourceValue === undefined
|
|
98
|
+
? inferredFromAction.action
|
|
99
|
+
: rawAction
|
|
100
|
+
const effectiveId = id ?? resourceEntry?.id
|
|
101
|
+
|
|
102
|
+
if (effectiveResource !== undefined) payload.resource = effectiveResource
|
|
103
|
+
if (effectiveAction !== undefined) payload.action = effectiveAction
|
|
104
|
+
if (effectiveId !== undefined) payload.id = effectiveId
|
|
105
|
+
|
|
106
|
+
const directFields = extractPlatformFields(rest)
|
|
107
|
+
const resourcePayloadCandidates = effectiveResource
|
|
108
|
+
? uniqueStrings([
|
|
109
|
+
rawResourceName,
|
|
110
|
+
effectiveResource,
|
|
111
|
+
effectiveResource.replace(/s$/, ''),
|
|
112
|
+
])
|
|
113
|
+
: []
|
|
114
|
+
const directResourcePayload = resourcePayloadCandidates
|
|
115
|
+
.map((candidate) => parsePlatformData(normalized[candidate]))
|
|
116
|
+
.find(Boolean)
|
|
117
|
+
|| null
|
|
118
|
+
if (effectiveResource) {
|
|
119
|
+
for (const candidate of resourcePayloadCandidates) delete directFields[candidate]
|
|
120
|
+
}
|
|
121
|
+
const parameterFields = {
|
|
122
|
+
...(parsePlatformData(resourceEntry?.parameters) || {}),
|
|
123
|
+
...(parsePlatformData(resourceEntry?.params) || {}),
|
|
124
|
+
...(parsePlatformData(normalized.parameters) || {}),
|
|
125
|
+
...(directResourcePayload || {}),
|
|
126
|
+
}
|
|
127
|
+
const parsedData = parsePlatformData(data)
|
|
128
|
+
const mergedData = {
|
|
129
|
+
...(parsedData || {}),
|
|
130
|
+
...parameterFields,
|
|
131
|
+
...directFields,
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
if (Object.keys(mergedData).length > 0) {
|
|
135
|
+
payload.data = JSON.stringify(mergedData)
|
|
136
|
+
} else if (typeof data === 'string' && data.trim()) {
|
|
137
|
+
payload.data = data
|
|
138
|
+
}
|
|
139
|
+
|
|
140
|
+
return payload
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function uniqueStrings(values: Array<string | undefined>): string[] {
|
|
144
|
+
return [...new Set(values.filter((value): value is string => Boolean(value)))]
|
|
145
|
+
}
|
|
146
|
+
|
|
9
147
|
/**
|
|
10
148
|
* Unified Platform Execution Logic
|
|
11
149
|
*/
|
|
12
150
|
async function executePlatformAction(args: any, bctx: any) {
|
|
13
|
-
const normalized =
|
|
14
|
-
const { resource, action, id, data
|
|
151
|
+
const normalized = normalizePlatformActionArgs((args ?? {}) as Record<string, unknown>)
|
|
152
|
+
const { resource, action, id, data } = normalized
|
|
15
153
|
|
|
16
154
|
// We reuse the existing CRUD tool logic but expose it via a single tool
|
|
17
155
|
const crudTools = buildCrudTools({
|
|
18
156
|
...bctx,
|
|
19
|
-
|
|
157
|
+
hasPlugin: (id: string) => [
|
|
20
158
|
'manage_agents',
|
|
21
159
|
'manage_tasks',
|
|
22
160
|
'manage_schedules',
|
|
@@ -36,7 +174,7 @@ async function executePlatformAction(args: any, bctx: any) {
|
|
|
36
174
|
}
|
|
37
175
|
|
|
38
176
|
// Forward to the specific CRUD tool implementation
|
|
39
|
-
return targetTool.invoke({ action, id, data
|
|
177
|
+
return targetTool.invoke({ action, id, data })
|
|
40
178
|
}
|
|
41
179
|
|
|
42
180
|
/**
|
|
@@ -45,11 +183,14 @@ async function executePlatformAction(args: any, bctx: any) {
|
|
|
45
183
|
const PlatformPlugin: Plugin = {
|
|
46
184
|
name: 'Core Platform',
|
|
47
185
|
description: 'Unified management of agents, tasks, schedules, skills, documents, and secrets.',
|
|
48
|
-
hooks: {
|
|
186
|
+
hooks: {
|
|
187
|
+
getCapabilityDescription: () => 'I can create and configure other agents (`manage_agents`), manage tasks (`manage_tasks`), set up schedules (`manage_schedules`), store and search documents (`manage_documents`), register webhooks (`manage_webhooks`), manage reusable skills (`manage_skills`), and store encrypted secrets (`manage_secrets`).',
|
|
188
|
+
getOperatingGuidance: () => ['Create/update tasks for long-lived goals to track progress.', 'Use schedules for follow-ups. Check existing schedules before creating new ones.', 'Inspect existing chats before creating duplicates.'],
|
|
189
|
+
} as PluginHooks,
|
|
49
190
|
tools: [
|
|
50
191
|
{
|
|
51
192
|
name: 'manage_platform',
|
|
52
|
-
description: 'Unified tool for managing all SwarmClaw resources.',
|
|
193
|
+
description: 'Unified tool for managing all SwarmClaw resources. For create/update, pass resource + action, then either put fields inside data, pass them as top-level fields, or use a single resources[0].parameters envelope.',
|
|
53
194
|
parameters: {
|
|
54
195
|
type: 'object',
|
|
55
196
|
properties: {
|
|
@@ -71,7 +212,7 @@ getPluginManager().registerBuiltin('manage_platform', PlatformPlugin)
|
|
|
71
212
|
* Legacy Bridge
|
|
72
213
|
*/
|
|
73
214
|
export function buildPlatformTools(bctx: ToolBuildContext): StructuredToolInterface[] {
|
|
74
|
-
if (!bctx.
|
|
215
|
+
if (!bctx.hasPlugin('manage_platform')) return []
|
|
75
216
|
|
|
76
217
|
return [
|
|
77
218
|
tool(
|