@swarmclawai/swarmclaw 0.6.7 → 0.7.0
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 +82 -39
- package/next.config.ts +31 -6
- package/package.json +3 -2
- package/src/app/api/agents/[id]/thread/route.ts +1 -0
- package/src/app/api/agents/route.ts +19 -5
- package/src/app/api/approvals/route.ts +22 -0
- package/src/app/api/chatrooms/[id]/chat/route.ts +4 -0
- package/src/app/api/clawhub/install/route.ts +2 -2
- package/src/app/api/eval/run/route.ts +37 -0
- package/src/app/api/eval/scenarios/route.ts +24 -0
- package/src/app/api/eval/suite/route.ts +29 -0
- package/src/app/api/mcp-servers/[id]/conformance/route.ts +26 -0
- package/src/app/api/mcp-servers/[id]/invoke/route.ts +81 -0
- package/src/app/api/memory/graph/route.ts +46 -0
- package/src/app/api/memory/route.ts +36 -5
- package/src/app/api/notifications/route.ts +3 -0
- package/src/app/api/plugins/install/route.ts +57 -5
- package/src/app/api/plugins/marketplace/route.ts +73 -22
- package/src/app/api/plugins/route.ts +61 -1
- package/src/app/api/plugins/ui/route.ts +34 -0
- package/src/app/api/sessions/[id]/checkpoints/route.ts +31 -0
- package/src/app/api/sessions/[id]/restore/route.ts +36 -0
- package/src/app/api/settings/route.ts +62 -0
- package/src/app/api/setup/doctor/route.ts +22 -5
- package/src/app/api/souls/[id]/route.ts +65 -0
- package/src/app/api/souls/route.ts +70 -0
- package/src/app/api/tasks/[id]/approve/route.ts +4 -3
- package/src/app/api/tasks/[id]/route.ts +16 -3
- package/src/app/api/tasks/route.ts +10 -2
- package/src/app/api/usage/route.ts +9 -2
- package/src/app/globals.css +27 -0
- package/src/app/page.tsx +10 -5
- package/src/cli/index.js +37 -0
- package/src/components/activity/activity-feed.tsx +9 -2
- package/src/components/agents/agent-avatar.tsx +5 -1
- package/src/components/agents/agent-card.tsx +55 -9
- package/src/components/agents/agent-sheet.tsx +112 -34
- package/src/components/agents/inspector-panel.tsx +1 -1
- package/src/components/agents/soul-library-picker.tsx +84 -13
- package/src/components/auth/access-key-gate.tsx +63 -54
- package/src/components/auth/user-picker.tsx +37 -32
- package/src/components/chat/activity-moment.tsx +2 -0
- package/src/components/chat/chat-area.tsx +11 -0
- package/src/components/chat/chat-header.tsx +69 -25
- package/src/components/chat/chat-tool-toggles.tsx +2 -2
- package/src/components/chat/checkpoint-timeline.tsx +112 -0
- package/src/components/chat/code-block.tsx +3 -1
- package/src/components/chat/exec-approval-card.tsx +8 -1
- package/src/components/chat/message-bubble.tsx +164 -4
- package/src/components/chat/message-list.tsx +46 -4
- package/src/components/chat/session-approval-card.tsx +80 -0
- package/src/components/chat/session-debug-panel.tsx +106 -84
- package/src/components/chat/streaming-bubble.tsx +6 -5
- package/src/components/chat/task-approval-card.tsx +78 -0
- package/src/components/chat/thinking-indicator.tsx +48 -12
- package/src/components/chat/tool-call-bubble.tsx +3 -0
- package/src/components/chat/tool-request-banner.tsx +39 -20
- package/src/components/chatrooms/chatroom-list.tsx +11 -4
- package/src/components/chatrooms/chatroom-sheet.tsx +7 -2
- package/src/components/connectors/connector-list.tsx +33 -11
- package/src/components/connectors/connector-sheet.tsx +37 -7
- package/src/components/home/home-view.tsx +54 -24
- package/src/components/input/chat-input.tsx +22 -1
- package/src/components/knowledge/knowledge-list.tsx +17 -18
- package/src/components/knowledge/knowledge-sheet.tsx +9 -5
- package/src/components/layout/app-layout.tsx +87 -19
- package/src/components/mcp-servers/mcp-server-list.tsx +352 -50
- package/src/components/mcp-servers/mcp-server-sheet.tsx +25 -9
- package/src/components/memory/memory-browser.tsx +73 -45
- package/src/components/memory/memory-graph-view.tsx +203 -0
- package/src/components/memory/memory-list.tsx +20 -13
- package/src/components/plugins/plugin-list.tsx +214 -60
- package/src/components/plugins/plugin-sheet.tsx +119 -24
- package/src/components/projects/project-list.tsx +17 -9
- package/src/components/providers/provider-list.tsx +21 -6
- package/src/components/providers/provider-sheet.tsx +42 -25
- package/src/components/runs/run-list.tsx +17 -13
- package/src/components/schedules/schedule-card.tsx +10 -3
- package/src/components/schedules/schedule-list.tsx +2 -2
- package/src/components/schedules/schedule-sheet.tsx +28 -9
- package/src/components/secrets/secret-sheet.tsx +7 -2
- package/src/components/secrets/secrets-list.tsx +18 -5
- package/src/components/sessions/new-session-sheet.tsx +183 -376
- package/src/components/sessions/session-card.tsx +10 -2
- package/src/components/settings/gateway-connection-panel.tsx +9 -8
- package/src/components/shared/command-palette.tsx +13 -5
- package/src/components/shared/empty-state.tsx +20 -8
- package/src/components/shared/hint-tip.tsx +31 -0
- package/src/components/shared/notification-center.tsx +134 -86
- package/src/components/shared/profile-sheet.tsx +4 -0
- package/src/components/shared/settings/plugin-manager.tsx +360 -135
- package/src/components/shared/settings/section-capability-policy.tsx +3 -3
- package/src/components/shared/settings/section-runtime-loop.tsx +149 -4
- package/src/components/skills/clawhub-browser.tsx +1 -0
- package/src/components/skills/skill-list.tsx +31 -12
- package/src/components/skills/skill-sheet.tsx +20 -7
- package/src/components/tasks/approvals-panel.tsx +224 -0
- package/src/components/tasks/task-board.tsx +20 -12
- package/src/components/tasks/task-card.tsx +21 -7
- package/src/components/tasks/task-column.tsx +4 -3
- package/src/components/tasks/task-list.tsx +1 -1
- package/src/components/tasks/task-sheet.tsx +130 -1
- package/src/components/ui/dialog.tsx +1 -0
- package/src/components/ui/sheet.tsx +1 -0
- package/src/components/usage/metrics-dashboard.tsx +72 -48
- package/src/components/wallets/wallet-panel.tsx +65 -41
- package/src/components/wallets/wallet-section.tsx +9 -3
- package/src/components/webhooks/webhook-list.tsx +21 -12
- package/src/components/webhooks/webhook-sheet.tsx +13 -3
- package/src/lib/approval-display.test.ts +45 -0
- package/src/lib/approval-display.ts +62 -0
- package/src/lib/clipboard.ts +38 -0
- package/src/lib/memory.ts +8 -0
- package/src/lib/providers/claude-cli.ts +5 -3
- package/src/lib/providers/index.ts +67 -21
- package/src/lib/runtime-loop.ts +3 -2
- package/src/lib/server/approvals.ts +150 -0
- package/src/lib/server/chat-execution.ts +319 -74
- package/src/lib/server/chatroom-helpers.ts +63 -5
- package/src/lib/server/chatroom-orchestration.ts +74 -0
- package/src/lib/server/clawhub-client.ts +82 -6
- package/src/lib/server/connectors/manager.ts +27 -1
- package/src/lib/server/context-manager.ts +132 -50
- package/src/lib/server/cost.test.ts +73 -0
- package/src/lib/server/cost.ts +165 -34
- package/src/lib/server/daemon-state.ts +112 -1
- package/src/lib/server/data-dir.ts +18 -1
- package/src/lib/server/eval/runner.ts +126 -0
- package/src/lib/server/eval/scenarios.ts +218 -0
- package/src/lib/server/eval/scorer.ts +96 -0
- package/src/lib/server/eval/store.ts +37 -0
- package/src/lib/server/eval/types.ts +48 -0
- package/src/lib/server/execution-log.ts +12 -8
- package/src/lib/server/guardian.ts +34 -0
- package/src/lib/server/heartbeat-service.ts +53 -1
- package/src/lib/server/integrity-monitor.ts +208 -0
- package/src/lib/server/langgraph-checkpoint.ts +10 -0
- package/src/lib/server/link-understanding.ts +55 -0
- package/src/lib/server/llm-response-cache.test.ts +102 -0
- package/src/lib/server/llm-response-cache.ts +227 -0
- package/src/lib/server/main-agent-loop.ts +115 -16
- package/src/lib/server/main-session.ts +6 -3
- package/src/lib/server/mcp-conformance.test.ts +18 -0
- package/src/lib/server/mcp-conformance.ts +233 -0
- package/src/lib/server/memory-db.ts +193 -19
- package/src/lib/server/memory-retrieval.test.ts +56 -0
- package/src/lib/server/mmr.ts +73 -0
- package/src/lib/server/orchestrator-lg.ts +7 -1
- package/src/lib/server/orchestrator.ts +4 -3
- package/src/lib/server/plugins.ts +662 -132
- package/src/lib/server/process-manager.ts +18 -0
- package/src/lib/server/query-expansion.ts +57 -0
- package/src/lib/server/queue.ts +280 -11
- package/src/lib/server/runtime-settings.ts +9 -0
- package/src/lib/server/session-run-manager.test.ts +23 -0
- package/src/lib/server/session-run-manager.ts +32 -2
- package/src/lib/server/session-tools/canvas.ts +85 -50
- package/src/lib/server/session-tools/chatroom.ts +130 -127
- package/src/lib/server/session-tools/connector.ts +233 -454
- package/src/lib/server/session-tools/context-mgmt.ts +87 -105
- package/src/lib/server/session-tools/crud.ts +84 -7
- package/src/lib/server/session-tools/delegate.ts +351 -752
- package/src/lib/server/session-tools/discovery.ts +198 -0
- package/src/lib/server/session-tools/edit_file.ts +82 -0
- package/src/lib/server/session-tools/file-send.test.ts +39 -0
- package/src/lib/server/session-tools/file.ts +257 -425
- package/src/lib/server/session-tools/git.ts +87 -47
- package/src/lib/server/session-tools/http.ts +95 -33
- package/src/lib/server/session-tools/index.ts +217 -138
- package/src/lib/server/session-tools/memory.ts +154 -239
- package/src/lib/server/session-tools/monitor.ts +126 -0
- package/src/lib/server/session-tools/normalize-tool-args.test.ts +61 -0
- package/src/lib/server/session-tools/normalize-tool-args.ts +48 -0
- package/src/lib/server/session-tools/openclaw-nodes.ts +82 -99
- package/src/lib/server/session-tools/openclaw-workspace.ts +103 -93
- package/src/lib/server/session-tools/platform.ts +86 -0
- package/src/lib/server/session-tools/plugin-creator.ts +239 -0
- package/src/lib/server/session-tools/sample-ui.ts +97 -0
- package/src/lib/server/session-tools/sandbox.ts +175 -148
- package/src/lib/server/session-tools/schedule.ts +78 -0
- package/src/lib/server/session-tools/session-info.ts +104 -410
- package/src/lib/server/session-tools/shell-normalize.test.ts +43 -0
- package/src/lib/server/session-tools/shell.ts +171 -143
- package/src/lib/server/session-tools/subagent.ts +77 -77
- package/src/lib/server/session-tools/wallet.ts +182 -106
- package/src/lib/server/session-tools/web.ts +181 -327
- package/src/lib/server/storage.ts +36 -0
- package/src/lib/server/stream-agent-chat.ts +348 -242
- package/src/lib/server/task-quality-gate.test.ts +44 -0
- package/src/lib/server/task-quality-gate.ts +67 -0
- package/src/lib/server/task-validation.test.ts +78 -0
- package/src/lib/server/task-validation.ts +67 -2
- package/src/lib/server/tool-aliases.ts +68 -0
- package/src/lib/server/tool-capability-policy.ts +24 -5
- package/src/lib/server/tool-retry.ts +62 -0
- package/src/lib/server/transcript-repair.ts +72 -0
- package/src/lib/setup-defaults.ts +1 -0
- package/src/lib/tasks.ts +7 -1
- package/src/lib/tool-definitions.ts +24 -23
- package/src/lib/validation/schemas.ts +13 -0
- package/src/lib/view-routes.ts +2 -23
- package/src/stores/use-app-store.ts +23 -1
- package/src/types/index.ts +155 -10
|
@@ -0,0 +1,239 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import { tool, type StructuredToolInterface } from '@langchain/core/tools'
|
|
3
|
+
import fs from 'fs'
|
|
4
|
+
import path from 'path'
|
|
5
|
+
import { DATA_DIR } from '../data-dir'
|
|
6
|
+
import type { ToolBuildContext } from './context'
|
|
7
|
+
import type { Plugin, PluginHooks } from '@/types'
|
|
8
|
+
import { getPluginManager } from '../plugins'
|
|
9
|
+
import { normalizeToolInputArgs } from './normalize-tool-args'
|
|
10
|
+
|
|
11
|
+
const PLUGINS_DIR = path.join(DATA_DIR, 'plugins')
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Core Plugin Creator Execution Logic
|
|
15
|
+
*/
|
|
16
|
+
interface PluginCreatorContext {
|
|
17
|
+
agentId?: string | null
|
|
18
|
+
sessionId?: string | null
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
async function executePluginCreatorAction(args: Record<string, unknown>, ctxOrBctx?: ToolBuildContext | PluginCreatorContext) {
|
|
22
|
+
const normalized = normalizeToolInputArgs(args)
|
|
23
|
+
// Normalize context from either ToolBuildContext or simple { agentId, sessionId }
|
|
24
|
+
const pctx: PluginCreatorContext = ctxOrBctx && 'ctx' in ctxOrBctx
|
|
25
|
+
? { agentId: (ctxOrBctx as ToolBuildContext).ctx?.agentId, sessionId: (ctxOrBctx as ToolBuildContext).ctx?.sessionId }
|
|
26
|
+
: (ctxOrBctx as PluginCreatorContext) || {}
|
|
27
|
+
const action = normalized.action as string | undefined
|
|
28
|
+
const filename = (normalized.filename ?? normalized.fileName) as string | undefined
|
|
29
|
+
const code = (normalized.code ?? normalized.content) as string | undefined
|
|
30
|
+
const approved = normalized.approved as boolean | undefined
|
|
31
|
+
|
|
32
|
+
try {
|
|
33
|
+
if (!fs.existsSync(PLUGINS_DIR)) {
|
|
34
|
+
fs.mkdirSync(PLUGINS_DIR, { recursive: true })
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
if (action === 'scaffold') {
|
|
38
|
+
if (!filename || !code) return 'Error: filename and code are required for scaffold.'
|
|
39
|
+
if (!filename.endsWith('.js')) return 'Error: filename must end with .js'
|
|
40
|
+
|
|
41
|
+
// REQUIRE USER APPROVAL
|
|
42
|
+
if (approved !== true) {
|
|
43
|
+
const { requestApproval } = await import('../approvals')
|
|
44
|
+
requestApproval({
|
|
45
|
+
category: 'plugin_scaffold',
|
|
46
|
+
title: `Scaffold Plugin: ${filename}`,
|
|
47
|
+
description: `Create new plugin file with ${code.length} chars of code.`,
|
|
48
|
+
data: { filename, code, createdByAgentId: pctx.agentId || null },
|
|
49
|
+
agentId: pctx.agentId,
|
|
50
|
+
sessionId: pctx.sessionId,
|
|
51
|
+
})
|
|
52
|
+
return JSON.stringify({
|
|
53
|
+
type: 'plugin_scaffold_request',
|
|
54
|
+
filename,
|
|
55
|
+
message: `I've submitted a request to create plugin "${filename}". The user needs to approve it via the Approvals page or the approval card in chat. Once approved, the plugin file will be written automatically — no need to call this tool again.`
|
|
56
|
+
})
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
const filePath = path.join(PLUGINS_DIR, filename)
|
|
60
|
+
fs.writeFileSync(filePath, code, 'utf8')
|
|
61
|
+
|
|
62
|
+
// Reload the plugin manager so the new plugin is discovered
|
|
63
|
+
const manager = getPluginManager()
|
|
64
|
+
manager.reload()
|
|
65
|
+
|
|
66
|
+
// Auto-enable the plugin for the agent that created it
|
|
67
|
+
if (pctx.agentId && pctx.sessionId) {
|
|
68
|
+
try {
|
|
69
|
+
const { loadSessions, saveSessions } = await import('../storage')
|
|
70
|
+
const sessions = loadSessions()
|
|
71
|
+
const session = sessions[pctx.sessionId!]
|
|
72
|
+
if (session) {
|
|
73
|
+
const currentTools = session.tools || []
|
|
74
|
+
if (!currentTools.includes(filename)) {
|
|
75
|
+
session.tools = [...currentTools, filename]
|
|
76
|
+
saveSessions(sessions)
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
} catch { /* best effort */ }
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
return `Plugin saved to ${filePath} and PluginManager reloaded. It is now enabled for this chat.`
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
if (action === 'get_spec') {
|
|
86
|
+
return `
|
|
87
|
+
SwarmClaw Plugin Specification:
|
|
88
|
+
A plugin is a CommonJS module (.js) that must be DUAL-COMPATIBLE with both SwarmClaw and OpenClaw platforms.
|
|
89
|
+
|
|
90
|
+
\`\`\`js
|
|
91
|
+
module.exports = {
|
|
92
|
+
// --- Metadata ---
|
|
93
|
+
id: 'my-plugin',
|
|
94
|
+
name: 'My Plugin', // Required
|
|
95
|
+
description: 'What it does',
|
|
96
|
+
version: '1.0.0',
|
|
97
|
+
openclaw: true, // Mark as OpenClaw-compatible
|
|
98
|
+
|
|
99
|
+
// --- SwarmClaw Format (hooks + tools) ---
|
|
100
|
+
hooks: {
|
|
101
|
+
beforeAgentStart: async ({ session, message }) => {},
|
|
102
|
+
afterAgentComplete: async ({ session, response }) => {},
|
|
103
|
+
beforeToolExec: async ({ session, toolName, args }) => {},
|
|
104
|
+
afterToolExec: async ({ session, toolName, result }) => {},
|
|
105
|
+
transformInboundMessage: async ({ session, text }) => { return text; },
|
|
106
|
+
transformOutboundMessage: async ({ session, text }) => { return text; },
|
|
107
|
+
},
|
|
108
|
+
|
|
109
|
+
tools: [
|
|
110
|
+
{
|
|
111
|
+
name: 'my_custom_tool',
|
|
112
|
+
description: 'Does something useful',
|
|
113
|
+
parameters: {
|
|
114
|
+
type: 'object',
|
|
115
|
+
properties: {
|
|
116
|
+
input: { type: 'string', description: 'The input to process' }
|
|
117
|
+
},
|
|
118
|
+
required: ['input']
|
|
119
|
+
},
|
|
120
|
+
execute: async (args, ctx) => {
|
|
121
|
+
return 'Result: ' + args.input;
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
],
|
|
125
|
+
|
|
126
|
+
// --- Real OpenClaw Format (register API) ---
|
|
127
|
+
register(api) {
|
|
128
|
+
api.registerHook('agent:start', (ctx) => {
|
|
129
|
+
// Hook events: agent:start, agent:complete, tool:call, tool:result, message:inbound, message:outbound
|
|
130
|
+
});
|
|
131
|
+
api.registerTool({
|
|
132
|
+
name: 'my_custom_tool',
|
|
133
|
+
description: 'Does something useful',
|
|
134
|
+
parameters: { type: 'object', properties: { input: { type: 'string' } } },
|
|
135
|
+
execute: (args) => 'Result: ' + args.input
|
|
136
|
+
});
|
|
137
|
+
api.log.info('Plugin activated');
|
|
138
|
+
},
|
|
139
|
+
};
|
|
140
|
+
\`\`\`
|
|
141
|
+
|
|
142
|
+
Key rules:
|
|
143
|
+
- Export BOTH SwarmClaw hooks/tools AND a register(api) method for cross-platform compatibility
|
|
144
|
+
- SwarmClaw checks for hooks/tools first; OpenClaw checks for register()
|
|
145
|
+
- Tools must have name, description, parameters (JSON Schema), and execute function
|
|
146
|
+
- Hooks are optional — only include the ones you need
|
|
147
|
+
- Keep plugins focused: one clear purpose per plugin
|
|
148
|
+
`
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (action === 'read') {
|
|
152
|
+
if (!filename) return 'Error: filename required.'
|
|
153
|
+
const filePath = path.join(PLUGINS_DIR, filename)
|
|
154
|
+
if (!fs.existsSync(filePath)) return `File not found: ${filename}`
|
|
155
|
+
return fs.readFileSync(filePath, 'utf8')
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
if (action === 'edit') {
|
|
159
|
+
if (!filename || !code) return 'Error: filename and code are required for edit.'
|
|
160
|
+
const filePath = path.join(PLUGINS_DIR, filename)
|
|
161
|
+
if (!fs.existsSync(filePath)) return `File not found: ${filename}. Use scaffold to create new plugins.`
|
|
162
|
+
fs.writeFileSync(filePath, code, 'utf8')
|
|
163
|
+
getPluginManager().reload()
|
|
164
|
+
return `Updated ${filename} and reloaded plugin manager.`
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
if (action === 'delete') {
|
|
168
|
+
if (!filename) return 'Error: filename required.'
|
|
169
|
+
const filePath = path.join(PLUGINS_DIR, filename)
|
|
170
|
+
if (fs.existsSync(filePath)) {
|
|
171
|
+
fs.unlinkSync(filePath)
|
|
172
|
+
getPluginManager().reload()
|
|
173
|
+
return `Deleted ${filename} and reloaded manager.`
|
|
174
|
+
}
|
|
175
|
+
return `File not found: ${filename}`
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
return `Unknown action "${action}". Valid actions: get_spec, scaffold, read, edit, delete`
|
|
179
|
+
} catch (err: unknown) {
|
|
180
|
+
return `Error: ${err instanceof Error ? err.message : String(err)}`
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* Register as a Built-in Plugin
|
|
186
|
+
*/
|
|
187
|
+
const PluginCreatorPlugin: Plugin = {
|
|
188
|
+
name: 'Plugin Creator',
|
|
189
|
+
description: 'Design, write, and test custom SwarmClaw plugins dynamically.',
|
|
190
|
+
hooks: {} as PluginHooks,
|
|
191
|
+
tools: [
|
|
192
|
+
{
|
|
193
|
+
name: 'plugin_creator_tool',
|
|
194
|
+
description: 'Create, read, edit, delete, or get the spec for writing new SwarmClaw plugins. Always call get_spec first to learn the correct plugin format.',
|
|
195
|
+
parameters: {
|
|
196
|
+
type: 'object',
|
|
197
|
+
properties: {
|
|
198
|
+
action: { type: 'string', enum: ['get_spec', 'scaffold', 'read', 'edit', 'delete'], description: 'get_spec: learn format. scaffold: create (needs approval). read: view code. edit: update existing. delete: remove.' },
|
|
199
|
+
filename: { type: 'string', description: 'Plugin filename, e.g. my-plugin.js. Required for scaffold and delete.' },
|
|
200
|
+
code: { type: 'string', description: 'The raw JavaScript code for the plugin. Required for scaffold.' },
|
|
201
|
+
approved: { type: 'boolean', description: 'Internal flag — do NOT set this. The approval system handles it automatically.' }
|
|
202
|
+
},
|
|
203
|
+
required: ['action']
|
|
204
|
+
},
|
|
205
|
+
execute: async (args, ctx) => {
|
|
206
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
207
|
+
const session = (ctx as any)?.session
|
|
208
|
+
return executePluginCreatorAction(
|
|
209
|
+
args as Record<string, unknown>,
|
|
210
|
+
{ agentId: session?.agentId as string | undefined, sessionId: session?.id as string | undefined }
|
|
211
|
+
)
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
]
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
getPluginManager().registerBuiltin('plugin_creator', PluginCreatorPlugin)
|
|
218
|
+
|
|
219
|
+
/**
|
|
220
|
+
* Legacy Bridge
|
|
221
|
+
*/
|
|
222
|
+
export function buildPluginCreatorTools(bctx: ToolBuildContext): StructuredToolInterface[] {
|
|
223
|
+
if (!bctx.hasTool('plugin_creator')) return []
|
|
224
|
+
return [
|
|
225
|
+
tool(
|
|
226
|
+
async (args) => executePluginCreatorAction(args, bctx),
|
|
227
|
+
{
|
|
228
|
+
name: 'plugin_creator_tool',
|
|
229
|
+
description: PluginCreatorPlugin.tools![0].description,
|
|
230
|
+
schema: z.object({
|
|
231
|
+
action: z.enum(['get_spec', 'scaffold', 'read', 'edit', 'delete']),
|
|
232
|
+
filename: z.string().optional(),
|
|
233
|
+
code: z.string().optional(),
|
|
234
|
+
approved: z.boolean().optional()
|
|
235
|
+
})
|
|
236
|
+
}
|
|
237
|
+
)
|
|
238
|
+
]
|
|
239
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
import { tool } from '@langchain/core/tools'
|
|
3
|
+
import { getPluginManager } from '../plugins'
|
|
4
|
+
import type { Plugin, PluginHooks } from '@/types'
|
|
5
|
+
import { normalizeToolInputArgs } from './normalize-tool-args'
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Sample UI Extension Plugin
|
|
9
|
+
* This demonstrates how a plugin can add a sidebar item,
|
|
10
|
+
* a chat header widget, and a custom message type.
|
|
11
|
+
*/
|
|
12
|
+
const SampleUIPlugin: Plugin = {
|
|
13
|
+
name: 'Sample UI',
|
|
14
|
+
description: 'Demonstration of plugin-driven UI: Sidebar, Header, and Chat.',
|
|
15
|
+
ui: {
|
|
16
|
+
sidebarItems: [
|
|
17
|
+
{
|
|
18
|
+
id: 'sample-dashboard',
|
|
19
|
+
label: 'Plugin View',
|
|
20
|
+
href: 'https://openclaw.ai',
|
|
21
|
+
position: 'top'
|
|
22
|
+
}
|
|
23
|
+
],
|
|
24
|
+
headerWidgets: [
|
|
25
|
+
{
|
|
26
|
+
id: 'sample-status',
|
|
27
|
+
label: '🔌 Plugin Active'
|
|
28
|
+
}
|
|
29
|
+
],
|
|
30
|
+
chatInputActions: [
|
|
31
|
+
{
|
|
32
|
+
id: 'sample-action',
|
|
33
|
+
label: 'Quick Scan',
|
|
34
|
+
tooltip: 'Run a sample system scan',
|
|
35
|
+
action: 'message',
|
|
36
|
+
value: 'Please perform a quick system scan and report the health.'
|
|
37
|
+
}
|
|
38
|
+
]
|
|
39
|
+
},
|
|
40
|
+
hooks: {
|
|
41
|
+
transformInboundMessage: async ({ text }) => {
|
|
42
|
+
console.log('[plugin:sample_ui] Transforming inbound message')
|
|
43
|
+
return text // No-op but demonstrates hook
|
|
44
|
+
},
|
|
45
|
+
transformOutboundMessage: async ({ text }) => {
|
|
46
|
+
console.log('[plugin:sample_ui] Transforming outbound message')
|
|
47
|
+
return text + '\n\n*-- Sent via Sample UI Plugin --*'
|
|
48
|
+
}
|
|
49
|
+
} as PluginHooks,
|
|
50
|
+
tools: [
|
|
51
|
+
{
|
|
52
|
+
name: 'show_plugin_card',
|
|
53
|
+
description: 'Trigger a rich UI card in the chat using the plugin-ui message kind.',
|
|
54
|
+
parameters: {
|
|
55
|
+
type: 'object',
|
|
56
|
+
properties: {
|
|
57
|
+
title: { type: 'string' },
|
|
58
|
+
content: { type: 'string' }
|
|
59
|
+
},
|
|
60
|
+
required: ['title', 'content']
|
|
61
|
+
},
|
|
62
|
+
execute: async (args) => {
|
|
63
|
+
const normalized = normalizeToolInputArgs((args ?? {}) as Record<string, unknown>)
|
|
64
|
+
const title = normalized.title as string
|
|
65
|
+
const content = normalized.content as string
|
|
66
|
+
// Return a structured payload that the frontend MessageBubble will interpret
|
|
67
|
+
return JSON.stringify({
|
|
68
|
+
kind: 'plugin-ui',
|
|
69
|
+
text: `### ${title}\n\n${content}`,
|
|
70
|
+
actions: [
|
|
71
|
+
{ id: 'view-more', label: 'View Details', href: 'https://openclaw.ai' }
|
|
72
|
+
]
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
]
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
// Auto-register
|
|
80
|
+
getPluginManager().registerBuiltin('sample_ui', SampleUIPlugin)
|
|
81
|
+
|
|
82
|
+
export function buildSampleUITools(bctx: any) {
|
|
83
|
+
if (!bctx.hasTool('sample_ui')) return []
|
|
84
|
+
return [
|
|
85
|
+
tool(
|
|
86
|
+
async (args) => SampleUIPlugin.tools![0].execute(args as any, bctx),
|
|
87
|
+
{
|
|
88
|
+
name: 'show_plugin_card',
|
|
89
|
+
description: SampleUIPlugin.tools![0].description,
|
|
90
|
+
schema: z.object({
|
|
91
|
+
title: z.string(),
|
|
92
|
+
content: z.string()
|
|
93
|
+
})
|
|
94
|
+
}
|
|
95
|
+
)
|
|
96
|
+
]
|
|
97
|
+
}
|