@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.
Files changed (203) hide show
  1. package/README.md +82 -39
  2. package/next.config.ts +31 -6
  3. package/package.json +3 -2
  4. package/src/app/api/agents/[id]/thread/route.ts +1 -0
  5. package/src/app/api/agents/route.ts +19 -5
  6. package/src/app/api/approvals/route.ts +22 -0
  7. package/src/app/api/chatrooms/[id]/chat/route.ts +4 -0
  8. package/src/app/api/clawhub/install/route.ts +2 -2
  9. package/src/app/api/eval/run/route.ts +37 -0
  10. package/src/app/api/eval/scenarios/route.ts +24 -0
  11. package/src/app/api/eval/suite/route.ts +29 -0
  12. package/src/app/api/mcp-servers/[id]/conformance/route.ts +26 -0
  13. package/src/app/api/mcp-servers/[id]/invoke/route.ts +81 -0
  14. package/src/app/api/memory/graph/route.ts +46 -0
  15. package/src/app/api/memory/route.ts +36 -5
  16. package/src/app/api/notifications/route.ts +3 -0
  17. package/src/app/api/plugins/install/route.ts +57 -5
  18. package/src/app/api/plugins/marketplace/route.ts +73 -22
  19. package/src/app/api/plugins/route.ts +61 -1
  20. package/src/app/api/plugins/ui/route.ts +34 -0
  21. package/src/app/api/sessions/[id]/checkpoints/route.ts +31 -0
  22. package/src/app/api/sessions/[id]/restore/route.ts +36 -0
  23. package/src/app/api/settings/route.ts +62 -0
  24. package/src/app/api/setup/doctor/route.ts +22 -5
  25. package/src/app/api/souls/[id]/route.ts +65 -0
  26. package/src/app/api/souls/route.ts +70 -0
  27. package/src/app/api/tasks/[id]/approve/route.ts +4 -3
  28. package/src/app/api/tasks/[id]/route.ts +16 -3
  29. package/src/app/api/tasks/route.ts +10 -2
  30. package/src/app/api/usage/route.ts +9 -2
  31. package/src/app/globals.css +27 -0
  32. package/src/app/page.tsx +10 -5
  33. package/src/cli/index.js +37 -0
  34. package/src/components/activity/activity-feed.tsx +9 -2
  35. package/src/components/agents/agent-avatar.tsx +5 -1
  36. package/src/components/agents/agent-card.tsx +55 -9
  37. package/src/components/agents/agent-sheet.tsx +112 -34
  38. package/src/components/agents/inspector-panel.tsx +1 -1
  39. package/src/components/agents/soul-library-picker.tsx +84 -13
  40. package/src/components/auth/access-key-gate.tsx +63 -54
  41. package/src/components/auth/user-picker.tsx +37 -32
  42. package/src/components/chat/activity-moment.tsx +2 -0
  43. package/src/components/chat/chat-area.tsx +11 -0
  44. package/src/components/chat/chat-header.tsx +69 -25
  45. package/src/components/chat/chat-tool-toggles.tsx +2 -2
  46. package/src/components/chat/checkpoint-timeline.tsx +112 -0
  47. package/src/components/chat/code-block.tsx +3 -1
  48. package/src/components/chat/exec-approval-card.tsx +8 -1
  49. package/src/components/chat/message-bubble.tsx +164 -4
  50. package/src/components/chat/message-list.tsx +46 -4
  51. package/src/components/chat/session-approval-card.tsx +80 -0
  52. package/src/components/chat/session-debug-panel.tsx +106 -84
  53. package/src/components/chat/streaming-bubble.tsx +6 -5
  54. package/src/components/chat/task-approval-card.tsx +78 -0
  55. package/src/components/chat/thinking-indicator.tsx +48 -12
  56. package/src/components/chat/tool-call-bubble.tsx +3 -0
  57. package/src/components/chat/tool-request-banner.tsx +39 -20
  58. package/src/components/chatrooms/chatroom-list.tsx +11 -4
  59. package/src/components/chatrooms/chatroom-sheet.tsx +7 -2
  60. package/src/components/connectors/connector-list.tsx +33 -11
  61. package/src/components/connectors/connector-sheet.tsx +37 -7
  62. package/src/components/home/home-view.tsx +54 -24
  63. package/src/components/input/chat-input.tsx +22 -1
  64. package/src/components/knowledge/knowledge-list.tsx +17 -18
  65. package/src/components/knowledge/knowledge-sheet.tsx +9 -5
  66. package/src/components/layout/app-layout.tsx +87 -19
  67. package/src/components/mcp-servers/mcp-server-list.tsx +352 -50
  68. package/src/components/mcp-servers/mcp-server-sheet.tsx +25 -9
  69. package/src/components/memory/memory-browser.tsx +73 -45
  70. package/src/components/memory/memory-graph-view.tsx +203 -0
  71. package/src/components/memory/memory-list.tsx +20 -13
  72. package/src/components/plugins/plugin-list.tsx +214 -60
  73. package/src/components/plugins/plugin-sheet.tsx +119 -24
  74. package/src/components/projects/project-list.tsx +17 -9
  75. package/src/components/providers/provider-list.tsx +21 -6
  76. package/src/components/providers/provider-sheet.tsx +42 -25
  77. package/src/components/runs/run-list.tsx +17 -13
  78. package/src/components/schedules/schedule-card.tsx +10 -3
  79. package/src/components/schedules/schedule-list.tsx +2 -2
  80. package/src/components/schedules/schedule-sheet.tsx +28 -9
  81. package/src/components/secrets/secret-sheet.tsx +7 -2
  82. package/src/components/secrets/secrets-list.tsx +18 -5
  83. package/src/components/sessions/new-session-sheet.tsx +183 -376
  84. package/src/components/sessions/session-card.tsx +10 -2
  85. package/src/components/settings/gateway-connection-panel.tsx +9 -8
  86. package/src/components/shared/command-palette.tsx +13 -5
  87. package/src/components/shared/empty-state.tsx +20 -8
  88. package/src/components/shared/hint-tip.tsx +31 -0
  89. package/src/components/shared/notification-center.tsx +134 -86
  90. package/src/components/shared/profile-sheet.tsx +4 -0
  91. package/src/components/shared/settings/plugin-manager.tsx +360 -135
  92. package/src/components/shared/settings/section-capability-policy.tsx +3 -3
  93. package/src/components/shared/settings/section-runtime-loop.tsx +149 -4
  94. package/src/components/skills/clawhub-browser.tsx +1 -0
  95. package/src/components/skills/skill-list.tsx +31 -12
  96. package/src/components/skills/skill-sheet.tsx +20 -7
  97. package/src/components/tasks/approvals-panel.tsx +224 -0
  98. package/src/components/tasks/task-board.tsx +20 -12
  99. package/src/components/tasks/task-card.tsx +21 -7
  100. package/src/components/tasks/task-column.tsx +4 -3
  101. package/src/components/tasks/task-list.tsx +1 -1
  102. package/src/components/tasks/task-sheet.tsx +130 -1
  103. package/src/components/ui/dialog.tsx +1 -0
  104. package/src/components/ui/sheet.tsx +1 -0
  105. package/src/components/usage/metrics-dashboard.tsx +72 -48
  106. package/src/components/wallets/wallet-panel.tsx +65 -41
  107. package/src/components/wallets/wallet-section.tsx +9 -3
  108. package/src/components/webhooks/webhook-list.tsx +21 -12
  109. package/src/components/webhooks/webhook-sheet.tsx +13 -3
  110. package/src/lib/approval-display.test.ts +45 -0
  111. package/src/lib/approval-display.ts +62 -0
  112. package/src/lib/clipboard.ts +38 -0
  113. package/src/lib/memory.ts +8 -0
  114. package/src/lib/providers/claude-cli.ts +5 -3
  115. package/src/lib/providers/index.ts +67 -21
  116. package/src/lib/runtime-loop.ts +3 -2
  117. package/src/lib/server/approvals.ts +150 -0
  118. package/src/lib/server/chat-execution.ts +319 -74
  119. package/src/lib/server/chatroom-helpers.ts +63 -5
  120. package/src/lib/server/chatroom-orchestration.ts +74 -0
  121. package/src/lib/server/clawhub-client.ts +82 -6
  122. package/src/lib/server/connectors/manager.ts +27 -1
  123. package/src/lib/server/context-manager.ts +132 -50
  124. package/src/lib/server/cost.test.ts +73 -0
  125. package/src/lib/server/cost.ts +165 -34
  126. package/src/lib/server/daemon-state.ts +112 -1
  127. package/src/lib/server/data-dir.ts +18 -1
  128. package/src/lib/server/eval/runner.ts +126 -0
  129. package/src/lib/server/eval/scenarios.ts +218 -0
  130. package/src/lib/server/eval/scorer.ts +96 -0
  131. package/src/lib/server/eval/store.ts +37 -0
  132. package/src/lib/server/eval/types.ts +48 -0
  133. package/src/lib/server/execution-log.ts +12 -8
  134. package/src/lib/server/guardian.ts +34 -0
  135. package/src/lib/server/heartbeat-service.ts +53 -1
  136. package/src/lib/server/integrity-monitor.ts +208 -0
  137. package/src/lib/server/langgraph-checkpoint.ts +10 -0
  138. package/src/lib/server/link-understanding.ts +55 -0
  139. package/src/lib/server/llm-response-cache.test.ts +102 -0
  140. package/src/lib/server/llm-response-cache.ts +227 -0
  141. package/src/lib/server/main-agent-loop.ts +115 -16
  142. package/src/lib/server/main-session.ts +6 -3
  143. package/src/lib/server/mcp-conformance.test.ts +18 -0
  144. package/src/lib/server/mcp-conformance.ts +233 -0
  145. package/src/lib/server/memory-db.ts +193 -19
  146. package/src/lib/server/memory-retrieval.test.ts +56 -0
  147. package/src/lib/server/mmr.ts +73 -0
  148. package/src/lib/server/orchestrator-lg.ts +7 -1
  149. package/src/lib/server/orchestrator.ts +4 -3
  150. package/src/lib/server/plugins.ts +662 -132
  151. package/src/lib/server/process-manager.ts +18 -0
  152. package/src/lib/server/query-expansion.ts +57 -0
  153. package/src/lib/server/queue.ts +280 -11
  154. package/src/lib/server/runtime-settings.ts +9 -0
  155. package/src/lib/server/session-run-manager.test.ts +23 -0
  156. package/src/lib/server/session-run-manager.ts +32 -2
  157. package/src/lib/server/session-tools/canvas.ts +85 -50
  158. package/src/lib/server/session-tools/chatroom.ts +130 -127
  159. package/src/lib/server/session-tools/connector.ts +233 -454
  160. package/src/lib/server/session-tools/context-mgmt.ts +87 -105
  161. package/src/lib/server/session-tools/crud.ts +84 -7
  162. package/src/lib/server/session-tools/delegate.ts +351 -752
  163. package/src/lib/server/session-tools/discovery.ts +198 -0
  164. package/src/lib/server/session-tools/edit_file.ts +82 -0
  165. package/src/lib/server/session-tools/file-send.test.ts +39 -0
  166. package/src/lib/server/session-tools/file.ts +257 -425
  167. package/src/lib/server/session-tools/git.ts +87 -47
  168. package/src/lib/server/session-tools/http.ts +95 -33
  169. package/src/lib/server/session-tools/index.ts +217 -138
  170. package/src/lib/server/session-tools/memory.ts +154 -239
  171. package/src/lib/server/session-tools/monitor.ts +126 -0
  172. package/src/lib/server/session-tools/normalize-tool-args.test.ts +61 -0
  173. package/src/lib/server/session-tools/normalize-tool-args.ts +48 -0
  174. package/src/lib/server/session-tools/openclaw-nodes.ts +82 -99
  175. package/src/lib/server/session-tools/openclaw-workspace.ts +103 -93
  176. package/src/lib/server/session-tools/platform.ts +86 -0
  177. package/src/lib/server/session-tools/plugin-creator.ts +239 -0
  178. package/src/lib/server/session-tools/sample-ui.ts +97 -0
  179. package/src/lib/server/session-tools/sandbox.ts +175 -148
  180. package/src/lib/server/session-tools/schedule.ts +78 -0
  181. package/src/lib/server/session-tools/session-info.ts +104 -410
  182. package/src/lib/server/session-tools/shell-normalize.test.ts +43 -0
  183. package/src/lib/server/session-tools/shell.ts +171 -143
  184. package/src/lib/server/session-tools/subagent.ts +77 -77
  185. package/src/lib/server/session-tools/wallet.ts +182 -106
  186. package/src/lib/server/session-tools/web.ts +181 -327
  187. package/src/lib/server/storage.ts +36 -0
  188. package/src/lib/server/stream-agent-chat.ts +348 -242
  189. package/src/lib/server/task-quality-gate.test.ts +44 -0
  190. package/src/lib/server/task-quality-gate.ts +67 -0
  191. package/src/lib/server/task-validation.test.ts +78 -0
  192. package/src/lib/server/task-validation.ts +67 -2
  193. package/src/lib/server/tool-aliases.ts +68 -0
  194. package/src/lib/server/tool-capability-policy.ts +24 -5
  195. package/src/lib/server/tool-retry.ts +62 -0
  196. package/src/lib/server/transcript-repair.ts +72 -0
  197. package/src/lib/setup-defaults.ts +1 -0
  198. package/src/lib/tasks.ts +7 -1
  199. package/src/lib/tool-definitions.ts +24 -23
  200. package/src/lib/validation/schemas.ts +13 -0
  201. package/src/lib/view-routes.ts +2 -23
  202. package/src/stores/use-app-store.ts +23 -1
  203. 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
+ }