@openagents-org/agent-launcher 0.2.128 → 0.2.131
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/package.json +1 -1
- package/registry.json +19 -22
- package/src/adapters/base.js +228 -9
- package/src/adapters/claude.js +30 -3
- package/src/adapters/cursor.js +618 -9
- package/src/adapters/utils.js +34 -10
- package/src/adapters/workspace-prompt.js +271 -83
- package/src/github-client.js +165 -0
- package/src/index.js +2 -1
- package/src/installer.js +151 -19
- package/src/mcp-server.js +42 -14
- package/src/paths.js +31 -3
- package/src/registry.js +4 -4
- package/src/tui.js +1 -16
- package/src/workspace-client.js +59 -6
|
@@ -11,6 +11,51 @@
|
|
|
11
11
|
|
|
12
12
|
'use strict';
|
|
13
13
|
|
|
14
|
+
/**
|
|
15
|
+
* Strong directive forcing agents to use the workspace browser when the
|
|
16
|
+
* workspace has Browser Fabric enabled. Emitted high in the system prompt
|
|
17
|
+
* so it wins against any earlier guidance that suggests local browsing
|
|
18
|
+
* tools.
|
|
19
|
+
*
|
|
20
|
+
* Returns '' when the toggle is off; the caller can unconditionally
|
|
21
|
+
* concatenate the result.
|
|
22
|
+
*
|
|
23
|
+
* Mirrors `build_browser_directive` in
|
|
24
|
+
* sdk/src/openagents/adapters/workspace_prompt.py.
|
|
25
|
+
*/
|
|
26
|
+
function buildBrowserDirective(browserEnabled) {
|
|
27
|
+
if (!browserEnabled) return '';
|
|
28
|
+
return (
|
|
29
|
+
'\n## Browser Use (MANDATORY)\n' +
|
|
30
|
+
'This workspace has the **shared Browser Fabric session** enabled. ' +
|
|
31
|
+
'All web browsing MUST go through it so the user can watch the ' +
|
|
32
|
+
'session live in their right-side panel and so cookies / state ' +
|
|
33
|
+
'persist across agents.\n\n' +
|
|
34
|
+
'**Use ONLY these tools for any web browsing:**\n' +
|
|
35
|
+
'- `mcp__openagents-workspace__workspace_browser_open`\n' +
|
|
36
|
+
'- `mcp__openagents-workspace__workspace_browser_navigate`\n' +
|
|
37
|
+
'- `mcp__openagents-workspace__workspace_browser_click`\n' +
|
|
38
|
+
'- `mcp__openagents-workspace__workspace_browser_type`\n' +
|
|
39
|
+
'- `mcp__openagents-workspace__workspace_browser_snapshot`\n' +
|
|
40
|
+
'- `mcp__openagents-workspace__workspace_browser_screenshot`\n' +
|
|
41
|
+
'- `mcp__openagents-workspace__workspace_browser_list_tabs`\n' +
|
|
42
|
+
'- `mcp__openagents-workspace__workspace_browser_close`\n' +
|
|
43
|
+
'\n' +
|
|
44
|
+
'If you don\'t have these MCP tools, use `Bash` + `curl` against ' +
|
|
45
|
+
'`/v1/browser/tabs` (documented below in Shared Browser).\n\n' +
|
|
46
|
+
'**FORBIDDEN — do NOT call any of these:**\n' +
|
|
47
|
+
'- `mcp__browsermcp__*` (any local Browser MCP extension tool)\n' +
|
|
48
|
+
'- `mcp__playwright__*`, `mcp__puppeteer__*`, `mcp__chrome-devtools__*`, or any other local-browser MCP\n' +
|
|
49
|
+
'- `WebFetch`, `WebSearch`, `web_fetch`, `web_search`, or any built-in network/browser tool\n' +
|
|
50
|
+
'\n' +
|
|
51
|
+
'If a local browser tool errors with "extension isn\'t connected" or ' +
|
|
52
|
+
'"connect your browser", do NOT ask the user to connect anything — ' +
|
|
53
|
+
'the local extension is irrelevant here. Immediately switch to the ' +
|
|
54
|
+
'workspace browser tools above. The Browser Fabric session is already ' +
|
|
55
|
+
'running on the backend.\n'
|
|
56
|
+
);
|
|
57
|
+
}
|
|
58
|
+
|
|
14
59
|
/**
|
|
15
60
|
* Build the identity section common to all adapters.
|
|
16
61
|
*/
|
|
@@ -78,11 +123,12 @@ function buildModePrompt(mode) {
|
|
|
78
123
|
*
|
|
79
124
|
* In plan mode, only read-only operations are documented.
|
|
80
125
|
*/
|
|
81
|
-
function buildApiSkillsPrompt({ endpoint, workspaceId, token, agentName, channelName, disabledModules, mode = 'execute' }) {
|
|
126
|
+
function buildApiSkillsPrompt({ endpoint, workspaceId, token, agentName, channelName, disabledModules, mode = 'execute', isWindows = process.platform === 'win32' }) {
|
|
82
127
|
const disabled = disabledModules || new Set();
|
|
83
128
|
const baseUrl = endpoint.replace(/\/+$/, '');
|
|
84
129
|
const isPlan = mode === 'plan';
|
|
85
130
|
const h = `X-Workspace-Token: ${token}`;
|
|
131
|
+
const curl = isWindows ? 'curl.exe' : 'curl';
|
|
86
132
|
|
|
87
133
|
const sections = [];
|
|
88
134
|
|
|
@@ -115,34 +161,53 @@ function buildApiSkillsPrompt({ endpoint, workspaceId, token, agentName, channel
|
|
|
115
161
|
let s = '\n### Shared Files\n\n';
|
|
116
162
|
|
|
117
163
|
if (!isPlan) {
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
164
|
+
if (isWindows) {
|
|
165
|
+
s += (
|
|
166
|
+
'**To upload a file**, exec this (replace filename/content):\n' +
|
|
167
|
+
`$CONTENT = [Convert]::ToBase64String([Text.Encoding]::UTF8.GetBytes('YOUR_CONTENT'))\n` +
|
|
168
|
+
`${curl} -s -X POST ${baseUrl}/v1/files/base64 ` +
|
|
169
|
+
`-H "${h}" ` +
|
|
170
|
+
'-H "Content-Type: application/json" ' +
|
|
171
|
+
`-d "{\\"filename\\":\\"report.md\\",` +
|
|
172
|
+
`\\"content_base64\\":\\"$CONTENT\\",` +
|
|
173
|
+
`\\"content_type\\":\\"text/markdown\\",` +
|
|
174
|
+
`\\"network\\":\\"${workspaceId}\\",` +
|
|
175
|
+
`\\"source\\":\\"openagents:${agentName}\\",` +
|
|
176
|
+
`\\"channel_name\\":\\"${channelName}\\"}"\n\n`
|
|
177
|
+
);
|
|
178
|
+
} else {
|
|
179
|
+
s += (
|
|
180
|
+
'**To upload a file**, exec this (replace filename/content):\n' +
|
|
181
|
+
`CONTENT=$(echo -n 'YOUR_CONTENT' | base64) && ` +
|
|
182
|
+
`${curl} -s -X POST ${baseUrl}/v1/files/base64 ` +
|
|
183
|
+
`-H "${h}" ` +
|
|
184
|
+
'-H "Content-Type: application/json" ' +
|
|
185
|
+
`-d '{"filename":"report.md",` +
|
|
186
|
+
`"content_base64":"'"$CONTENT"'",` +
|
|
187
|
+
`"content_type":"text/markdown",` +
|
|
188
|
+
`"network":"${workspaceId}",` +
|
|
189
|
+
`"source":"openagents:${agentName}",` +
|
|
190
|
+
`"channel_name":"${channelName}"}'\n\n`
|
|
191
|
+
);
|
|
192
|
+
}
|
|
131
193
|
}
|
|
132
194
|
|
|
195
|
+
const tmpDir = isWindows ? '$env:TEMP' : '/tmp';
|
|
133
196
|
s += (
|
|
134
197
|
'**List files:**\n' +
|
|
135
|
-
|
|
136
|
-
'**Download file:**\n' +
|
|
137
|
-
|
|
198
|
+
`\`${curl} -s -H "${h}" ${baseUrl}/v1/files?network=${workspaceId}\`\n\n` +
|
|
199
|
+
'**Download file (text):**\n' +
|
|
200
|
+
`\`${curl} -s -H "${h}" ${baseUrl}/v1/files/{file_id}\`\n\n` +
|
|
201
|
+
'**Download file (binary/images) — save to disk, then use Read tool to view:**\n' +
|
|
202
|
+
`\`${curl} -s -H "${h}" ${baseUrl}/v1/files/{file_id} -o ${tmpDir}/{filename}\`\n\n` +
|
|
138
203
|
'**File info (metadata):**\n' +
|
|
139
|
-
|
|
204
|
+
`\`${curl} -s -H "${h}" ${baseUrl}/v1/files/{file_id}/info\`\n`
|
|
140
205
|
);
|
|
141
206
|
|
|
142
207
|
if (!isPlan) {
|
|
143
208
|
s += (
|
|
144
209
|
'\n**Delete file:**\n' +
|
|
145
|
-
|
|
210
|
+
`\`${curl} -s -X DELETE -H "${h}" ${baseUrl}/v1/files/{file_id}\`\n`
|
|
146
211
|
);
|
|
147
212
|
}
|
|
148
213
|
|
|
@@ -157,48 +222,48 @@ function buildApiSkillsPrompt({ endpoint, workspaceId, token, agentName, channel
|
|
|
157
222
|
s += (
|
|
158
223
|
'**To browse a website**, exec these steps (use exec for each):\n' +
|
|
159
224
|
`Step 1 — open tab: ` +
|
|
160
|
-
|
|
225
|
+
`${curl} -s -X POST ${baseUrl}/v1/browser/tabs ` +
|
|
161
226
|
`-H "${h}" -H "Content-Type: application/json" ` +
|
|
162
227
|
`-d '{"url":"https://example.com","network":"${workspaceId}",` +
|
|
163
228
|
`"source":"openagents:${agentName}"}'\n` +
|
|
164
229
|
`Step 2 — read content: ` +
|
|
165
|
-
|
|
230
|
+
`${curl} -s -H "${h}" ${baseUrl}/v1/browser/tabs/TAB_ID/snapshot\n` +
|
|
166
231
|
`Step 3 — close tab: ` +
|
|
167
|
-
|
|
232
|
+
`${curl} -s -X DELETE -H "${h}" ${baseUrl}/v1/browser/tabs/TAB_ID\n` +
|
|
168
233
|
'(Replace TAB_ID with the id from step 1 response)\n\n'
|
|
169
234
|
);
|
|
170
235
|
}
|
|
171
236
|
|
|
172
237
|
s += (
|
|
173
238
|
'**List open tabs:**\n' +
|
|
174
|
-
|
|
239
|
+
`\`${curl} -s -H "${h}" ${baseUrl}/v1/browser/tabs?network=${workspaceId}\`\n\n` +
|
|
175
240
|
'**Get page content (text):**\n' +
|
|
176
|
-
|
|
241
|
+
`\`${curl} -s -H "${h}" ${baseUrl}/v1/browser/tabs/{tab_id}/snapshot\`\n\n` +
|
|
177
242
|
'**Get screenshot (PNG):**\n' +
|
|
178
|
-
|
|
243
|
+
`\`${curl} -s -H "${h}" ${baseUrl}/v1/browser/tabs/{tab_id}/screenshot\`\n`
|
|
179
244
|
);
|
|
180
245
|
|
|
181
246
|
if (!isPlan) {
|
|
182
247
|
s += (
|
|
183
248
|
'\n**Open tab:**\n' +
|
|
184
|
-
|
|
249
|
+
`\`${curl} -s -X POST -H "${h}" -H "Content-Type: application/json"` +
|
|
185
250
|
` ${baseUrl}/v1/browser/tabs` +
|
|
186
251
|
` -d '{"url":"URL","network":"${workspaceId}",` +
|
|
187
252
|
`"source":"openagents:${agentName}"}'\`\n\n` +
|
|
188
253
|
'**Navigate:**\n' +
|
|
189
|
-
|
|
254
|
+
`\`${curl} -s -X POST -H "${h}" -H "Content-Type: application/json"` +
|
|
190
255
|
` ${baseUrl}/v1/browser/tabs/{tab_id}/navigate` +
|
|
191
256
|
` -d '{"url":"URL"}'\`\n\n` +
|
|
192
257
|
'**Click element:**\n' +
|
|
193
|
-
|
|
258
|
+
`\`${curl} -s -X POST -H "${h}" -H "Content-Type: application/json"` +
|
|
194
259
|
` ${baseUrl}/v1/browser/tabs/{tab_id}/click` +
|
|
195
260
|
` -d '{"selector":"CSS_SELECTOR"}'\`\n\n` +
|
|
196
261
|
'**Type text:**\n' +
|
|
197
|
-
|
|
262
|
+
`\`${curl} -s -X POST -H "${h}" -H "Content-Type: application/json"` +
|
|
198
263
|
` ${baseUrl}/v1/browser/tabs/{tab_id}/type` +
|
|
199
264
|
` -d '{"selector":"CSS_SELECTOR","text":"TEXT"}'\`\n\n` +
|
|
200
265
|
'**Close tab:**\n' +
|
|
201
|
-
|
|
266
|
+
`\`${curl} -s -X DELETE -H "${h}" ${baseUrl}/v1/browser/tabs/{tab_id}\`\n`
|
|
202
267
|
);
|
|
203
268
|
}
|
|
204
269
|
|
|
@@ -209,9 +274,9 @@ function buildApiSkillsPrompt({ endpoint, workspaceId, token, agentName, channel
|
|
|
209
274
|
sections.push(
|
|
210
275
|
'\n### Message History\n\n' +
|
|
211
276
|
'**Get recent messages in the current channel:**\n' +
|
|
212
|
-
|
|
277
|
+
`\`${curl} -s -H "${h}" "${baseUrl}/v1/events?network=${workspaceId}&channel=${channelName}&type=workspace.message&sort=desc&limit=20"\`\n\n` +
|
|
213
278
|
'**Get messages from a specific channel:**\n' +
|
|
214
|
-
|
|
279
|
+
`\`${curl} -s -H "${h}" "${baseUrl}/v1/events?network=${workspaceId}&channel=CHANNEL_NAME&type=workspace.message&sort=desc&limit=20"\`\n`
|
|
215
280
|
);
|
|
216
281
|
|
|
217
282
|
// Post status update
|
|
@@ -219,7 +284,7 @@ function buildApiSkillsPrompt({ endpoint, workspaceId, token, agentName, channel
|
|
|
219
284
|
sections.push(
|
|
220
285
|
'\n### Post Status Update\n\n' +
|
|
221
286
|
'Post a status/thinking message (visible in the workspace UI as an intermediate step):\n' +
|
|
222
|
-
|
|
287
|
+
`\`${curl} -s -X POST -H "${h}" -H "Content-Type: application/json" ` +
|
|
223
288
|
`${baseUrl}/v1/events -d '{"type":"workspace.message.posted",` +
|
|
224
289
|
`"source":"openagents:${agentName}","target":"channel/${channelName}",` +
|
|
225
290
|
`"payload":{"content":"YOUR_STATUS","message_type":"status"}}'\`\n`
|
|
@@ -234,14 +299,14 @@ function buildApiSkillsPrompt({ endpoint, workspaceId, token, agentName, channel
|
|
|
234
299
|
'is replaced each time (send the full list with current statuses).\n\n' +
|
|
235
300
|
'**Status values:** `pending`, `in_progress`, `completed`\n\n' +
|
|
236
301
|
'**Update your to-do list:**\n' +
|
|
237
|
-
|
|
302
|
+
`\`${curl} -s -X PUT -H "${h}" -H "Content-Type: application/json" ` +
|
|
238
303
|
`${baseUrl}/v1/todos -d '{"todos":[` +
|
|
239
304
|
`{"content":"First task","status":"in_progress"},` +
|
|
240
305
|
`{"content":"Second task","status":"pending"}` +
|
|
241
306
|
`],"network":"${workspaceId}","channel":"${channelName}",` +
|
|
242
307
|
`"source":"openagents:${agentName}"}'\`\n\n` +
|
|
243
308
|
'**Get your to-do list:**\n' +
|
|
244
|
-
|
|
309
|
+
`\`${curl} -s -H "${h}" "${baseUrl}/v1/todos?network=${workspaceId}&channel=${channelName}"\`\n\n` +
|
|
245
310
|
'**IMPORTANT:** When you receive a task with multiple steps or a list of things to do, ' +
|
|
246
311
|
'ALWAYS create a to-do list first before starting work. This lets the user see your ' +
|
|
247
312
|
'progress in real time. Update statuses as you work through each item.\n' +
|
|
@@ -259,14 +324,14 @@ function buildApiSkillsPrompt({ endpoint, workspaceId, token, agentName, channel
|
|
|
259
324
|
'Use cases: check back on a deploy, retry after a rate limit, remind ' +
|
|
260
325
|
'yourself to follow up.\n\n' +
|
|
261
326
|
'**Create a timer:**\n' +
|
|
262
|
-
|
|
327
|
+
`\`${curl} -s -X POST -H "${h}" -H "Content-Type: application/json" ` +
|
|
263
328
|
`${baseUrl}/v1/timers -d '{"delay":300,"message":"Check the build",` +
|
|
264
329
|
`"network":"${workspaceId}","channel":"${channelName}",` +
|
|
265
330
|
`"source":"openagents:${agentName}"}'\`\n\n` +
|
|
266
331
|
'**List active timers:**\n' +
|
|
267
|
-
|
|
332
|
+
`\`${curl} -s -H "${h}" "${baseUrl}/v1/timers?network=${workspaceId}&channel=${channelName}"\`\n\n` +
|
|
268
333
|
'**Cancel a timer:**\n' +
|
|
269
|
-
|
|
334
|
+
`\`${curl} -s -X DELETE -H "${h}" ${baseUrl}/v1/timers/TIMER_ID\`\n`
|
|
270
335
|
);
|
|
271
336
|
}
|
|
272
337
|
|
|
@@ -274,27 +339,30 @@ function buildApiSkillsPrompt({ endpoint, workspaceId, token, agentName, channel
|
|
|
274
339
|
if (!isPlan) {
|
|
275
340
|
sections.push(
|
|
276
341
|
'\n### Routines (Recurring Tasks)\n\n' +
|
|
277
|
-
'Create a recurring routine that fires on a schedule
|
|
278
|
-
'
|
|
279
|
-
'
|
|
280
|
-
'**
|
|
281
|
-
'
|
|
282
|
-
'
|
|
283
|
-
|
|
342
|
+
'Create a recurring routine that fires on a schedule. Each routine gets ' +
|
|
343
|
+
'**its own dedicated thread** (`routine:<id>`) so different routines never ' +
|
|
344
|
+
'interfere, and the full context is preserved.\n\n' +
|
|
345
|
+
'**`context` is required** — provide a thorough description of what the ' +
|
|
346
|
+
'routine should do, any background info, and relevant details from the ' +
|
|
347
|
+
'current conversation. This context is posted at the start of the routine\'s ' +
|
|
348
|
+
'thread every time it fires, so you have full background.\n\n' +
|
|
349
|
+
'**Two schedule modes:**\n' +
|
|
350
|
+
'- **Daily**: `hour` (0-23 UTC) + `minute` (0-59), optional `days` ' +
|
|
351
|
+
'array (0=Mon, 6=Sun). Omit `days` for every day.\n' +
|
|
352
|
+
'- **Interval**: `interval_minutes` (1-1440). Fires every N minutes. ' +
|
|
353
|
+
'Mutually exclusive with `hour`/`minute`.\n\n' +
|
|
354
|
+
'**Create a daily routine:**\n' +
|
|
355
|
+
`\`${curl} -s -X POST -H "${h}" -H "Content-Type: application/json" ` +
|
|
284
356
|
`${baseUrl}/v1/routines -d '{"name":"Daily PR Review","message":"Review open PRs",` +
|
|
357
|
+
`"context":"Review all open pull requests on the main repo. Check for merge conflicts, ` +
|
|
358
|
+
`CI failures, and stale PRs older than 3 days. Post a summary to the workspace.",` +
|
|
285
359
|
`"hour":8,"minute":0,` +
|
|
286
|
-
`"network":"${workspaceId}"
|
|
287
|
-
`"source":"openagents:${agentName}"}'\`\n\n` +
|
|
288
|
-
'**Create a weekday-only routine (Mon-Fri):**\n' +
|
|
289
|
-
`\`curl -s -X POST -H "${h}" -H "Content-Type: application/json" ` +
|
|
290
|
-
`${baseUrl}/v1/routines -d '{"name":"Morning Standup","message":"Post standup summary",` +
|
|
291
|
-
`"hour":9,"minute":0,"days":[0,1,2,3,4],` +
|
|
292
|
-
`"network":"${workspaceId}","channel":"${channelName}",` +
|
|
360
|
+
`"network":"${workspaceId}",` +
|
|
293
361
|
`"source":"openagents:${agentName}"}'\`\n\n` +
|
|
294
362
|
'**List active routines:**\n' +
|
|
295
|
-
|
|
363
|
+
`\`${curl} -s -H "${h}" "${baseUrl}/v1/routines?network=${workspaceId}"\`\n\n` +
|
|
296
364
|
'**Cancel a routine:**\n' +
|
|
297
|
-
|
|
365
|
+
`\`${curl} -s -X DELETE -H "${h}" ${baseUrl}/v1/routines/ROUTINE_ID\`\n`
|
|
298
366
|
);
|
|
299
367
|
}
|
|
300
368
|
|
|
@@ -302,17 +370,37 @@ function buildApiSkillsPrompt({ endpoint, workspaceId, token, agentName, channel
|
|
|
302
370
|
sections.push(
|
|
303
371
|
'\n### Discover Agents\n\n' +
|
|
304
372
|
'**List all agents in the workspace (with status and role):**\n' +
|
|
305
|
-
|
|
373
|
+
`\`${curl} -s -H "${h}" ${baseUrl}/v1/discover?network=${workspaceId}\`\n`
|
|
306
374
|
);
|
|
307
375
|
|
|
308
376
|
return sections.join('\n');
|
|
309
377
|
}
|
|
310
378
|
|
|
379
|
+
/**
|
|
380
|
+
* Guardrails shared across all adapter prompt builders.
|
|
381
|
+
*/
|
|
382
|
+
function buildGuardrails() {
|
|
383
|
+
return (
|
|
384
|
+
'\nIMPORTANT: Never use AskUserQuestion. ' +
|
|
385
|
+
'AskUserQuestion blocks the subprocess and will hang the thread. ' +
|
|
386
|
+
'If you need to ask the user something, just write the question ' +
|
|
387
|
+
'as your text response.\n' +
|
|
388
|
+
'\nIMPORTANT: When the user gives you a numbered list, bulleted list, or ' +
|
|
389
|
+
'multiple tasks in a single message, you MUST create a to-do list BEFORE ' +
|
|
390
|
+
'doing any work. This is mandatory — no exceptions, even for simple tasks. ' +
|
|
391
|
+
'The to-do list lets the user track your progress in real time.\n' +
|
|
392
|
+
'\nIMPORTANT: Do NOT use built-in scheduling tools (CronCreate, CronDelete, ' +
|
|
393
|
+
'CronList, ScheduleWakeup). For timers, routines, and recurring tasks, ' +
|
|
394
|
+
'ALWAYS use the workspace REST API (curl commands in your skill instructions). ' +
|
|
395
|
+
'Built-in scheduling is local-only and won\'t appear in the workspace.\n'
|
|
396
|
+
);
|
|
397
|
+
}
|
|
398
|
+
|
|
311
399
|
/**
|
|
312
400
|
* Build the system prompt for Claude adapter (MCP-based).
|
|
313
401
|
* Claude gets identity + collaboration instructions but NOT API skills.
|
|
314
402
|
*/
|
|
315
|
-
function buildClaudeSystemPrompt({ agentName, workspaceId, channelName, mode = 'execute' }) {
|
|
403
|
+
function buildClaudeSystemPrompt({ agentName, workspaceId, channelName, mode = 'execute', browserEnabled = false }) {
|
|
316
404
|
const parts = [];
|
|
317
405
|
parts.push(buildWorkspaceIdentity(agentName, workspaceId, channelName, mode));
|
|
318
406
|
parts.push(
|
|
@@ -322,7 +410,9 @@ function buildClaudeSystemPrompt({ agentName, workspaceId, channelName, mode = '
|
|
|
322
410
|
'Use workspace_create_timer to set a reminder that wakes you up later.\n' +
|
|
323
411
|
'Use workspace_create_routine to set up recurring scheduled tasks (e.g. daily reviews).\n'
|
|
324
412
|
);
|
|
413
|
+
parts.push(buildBrowserDirective(browserEnabled));
|
|
325
414
|
parts.push(buildCollaborationPrompt());
|
|
415
|
+
parts.push(buildA2UIPrompt());
|
|
326
416
|
|
|
327
417
|
if (mode === 'plan') {
|
|
328
418
|
parts.push(
|
|
@@ -331,53 +421,115 @@ function buildClaudeSystemPrompt({ agentName, workspaceId, channelName, mode = '
|
|
|
331
421
|
);
|
|
332
422
|
}
|
|
333
423
|
|
|
334
|
-
parts.push(
|
|
335
|
-
'\nIMPORTANT: Never use AskUserQuestion. ' +
|
|
336
|
-
'AskUserQuestion blocks the subprocess and will hang the thread. ' +
|
|
337
|
-
'If you need to ask the user something, just write the question ' +
|
|
338
|
-
'as your text response.\n'
|
|
339
|
-
);
|
|
424
|
+
parts.push(buildGuardrails());
|
|
340
425
|
|
|
341
|
-
parts.
|
|
342
|
-
|
|
343
|
-
'multiple tasks in a single message, you MUST create a to-do list BEFORE ' +
|
|
344
|
-
'doing any work. This is mandatory — no exceptions, even for simple tasks. ' +
|
|
345
|
-
'The to-do list lets the user track your progress in real time.\n'
|
|
346
|
-
);
|
|
426
|
+
return parts.join('\n');
|
|
427
|
+
}
|
|
347
428
|
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
429
|
+
/**
|
|
430
|
+
* Teach the LLM how to emit interactive UI alongside its text response.
|
|
431
|
+
* The frontend (OpenAgents Go) renders any A2UI-shaped JSON spec inline
|
|
432
|
+
* in the chat bubble; the agent-connector strips the fenced block before
|
|
433
|
+
* posting and ferries the spec via payload.spec.
|
|
434
|
+
*/
|
|
435
|
+
function buildA2UIPrompt() {
|
|
436
|
+
return (
|
|
437
|
+
'\n## Rendering interactive UI\n' +
|
|
438
|
+
'When you want the user to interact with structured UI (a button choice, ' +
|
|
439
|
+
'a form, a chart, a table, a confirmation dialog) instead of just reading ' +
|
|
440
|
+
'text, emit a fenced code block tagged `a2ui` containing a JSON spec. The ' +
|
|
441
|
+
"frontend renders it inline below any markdown narration you write.\n\n" +
|
|
442
|
+
"The spec is a tree of `{ type, props, children?, action? }` nodes. " +
|
|
443
|
+
'Supported component types include `Stack`, `Heading`, `Text`, `Image`, ' +
|
|
444
|
+
'`Icon`, `Button`, `ChoiceList`, `ConfirmDialog`, `AmountInput`, `Card`, ' +
|
|
445
|
+
'`Divider`, `Spacer`, `Alert`, `LineChart`, `PieChart`, `AssetPrice`, ' +
|
|
446
|
+
'`BalanceCard`, `TransactionList`, `TransactionRow`. Unknown types render ' +
|
|
447
|
+
'as a placeholder chip, so feel free to compose freely.\n\n' +
|
|
448
|
+
'Interactive components attach an `action` object with a `name` field you ' +
|
|
449
|
+
'choose. Example:\n\n' +
|
|
450
|
+
'```a2ui\n' +
|
|
451
|
+
'{\n' +
|
|
452
|
+
' "type": "Stack",\n' +
|
|
453
|
+
' "props": { "direction": "vertical", "spacing": 12 },\n' +
|
|
454
|
+
' "children": [\n' +
|
|
455
|
+
' { "type": "Heading", "props": { "text": "Pick a time", "level": 2 } },\n' +
|
|
456
|
+
' { "type": "Button", "props": { "label": "Morning", "action": { "name": "pick_morning" } } },\n' +
|
|
457
|
+
' { "type": "Button", "props": { "label": "Evening", "action": { "name": "pick_evening" } } }\n' +
|
|
458
|
+
' ]\n' +
|
|
459
|
+
'}\n' +
|
|
460
|
+
'```\n\n' +
|
|
461
|
+
'### Exact prop names (these are checked verbatim — guessing will render "No data")\n\n' +
|
|
462
|
+
'Layout / content:\n' +
|
|
463
|
+
'- `Stack` — props: `direction` ("vertical"|"horizontal"), `spacing`, `alignment`\n' +
|
|
464
|
+
'- `Card` — props: `title`, `padding`, `cornerRadius`\n' +
|
|
465
|
+
'- `Divider`, `Spacer` — props: `size`, `orientation`\n' +
|
|
466
|
+
'- `Heading` — props: `text`, `level` (1–4)\n' +
|
|
467
|
+
'- `Text` — props: `content` **(NOT `text`)**, `style`, `weight`, `color`\n' +
|
|
468
|
+
'- `Image` — props: `url`, `name`, `contentMode`, `width`, `height`\n' +
|
|
469
|
+
'- `Icon` — props: `name`, `size`, `color`\n\n' +
|
|
470
|
+
'Interactive:\n' +
|
|
471
|
+
'- `Button` — props: `label`, `style` ("primary"|"secondary"|"destructive"), `icon`, `disabled`, `action: {name, params?}`\n' +
|
|
472
|
+
'- `ConfirmDialog` — props: `title`, `message`, `confirmLabel`, `cancelLabel`, `triggerLabel`, `action`\n' +
|
|
473
|
+
'- `ChoiceList` — props: `question`, `options: [{id, label}]`, `action`\n' +
|
|
474
|
+
'- `AmountInput` — props: `label`, `placeholder`, `currency`, `action`\n' +
|
|
475
|
+
'- `input` (lowercase) — props: `inputType` ("text"|"choice"|"number"|"date"), `id`, `label`, `placeholder`, `options`\n\n' +
|
|
476
|
+
'Feedback:\n' +
|
|
477
|
+
'- `Alert` — props: `title`, `message`, `severity` ("info"|"success"|"warning"|"error"), `dismissible`, `action`\n\n' +
|
|
478
|
+
'Charts (Swift Charts under the hood — needs **non-empty** data with the right key):\n' +
|
|
479
|
+
'- `LineChart` — props: `title`, `color`, `points: [{x, y}]` **(NOT `data`)**\n' +
|
|
480
|
+
'- `PieChart` — props: `title`, `segments: [{label, value, color?}]` **(NOT `slices`)**, `showLegend`\n' +
|
|
481
|
+
'- `chart` (lowercase) — simple sparkline/bar: props: `style` ("sparkline"|"bar"), `data: [number, ...]`, `labels`, `color`\n\n' +
|
|
482
|
+
'Data display:\n' +
|
|
483
|
+
'- `metric` (lowercase) — props: `label`, `value`, `caption`, `captionColor`, `icon`\n' +
|
|
484
|
+
'- `list` (lowercase) — props: `items: [{title, subtitle, trailing, icon}]`, `maxVisible`, `expandLabel`\n' +
|
|
485
|
+
'- `table` (lowercase) — **key-value rows only, not multi-column**: props: `rows: [{label, value}]`, `maxVisible`\n\n' +
|
|
486
|
+
'There is no multi-column data table in the catalog. For tabular data with ' +
|
|
487
|
+
'columns you have two choices: (a) use `list` with title/subtitle/trailing ' +
|
|
488
|
+
'mapped to your columns, or (b) compose a Stack of horizontal Stacks of Text ' +
|
|
489
|
+
'manually. Pick whichever reads better.\n\n' +
|
|
490
|
+
'If your data is empty or you can\'t fit it into the available primitives, ' +
|
|
491
|
+
'fall back to plain markdown — don\'t emit a spec just to wrap text in a card.\n\n' +
|
|
492
|
+
'When the user interacts with a rendered component, you will receive a ' +
|
|
493
|
+
"user message shaped like `[ui_action] action=pick_morning tool_call_id=... value=...`. " +
|
|
494
|
+
"Use the `action` name (which you chose) to decide what to do next; the " +
|
|
495
|
+
"spec you emitted earlier is in your conversation history for context.\n\n" +
|
|
496
|
+
'Use a spec when:\n' +
|
|
497
|
+
'- The user needs to make a discrete choice (offer buttons rather than ' +
|
|
498
|
+
'asking them to type a free-form answer).\n' +
|
|
499
|
+
'- Structured data is easier to scan as a chart or table than as prose.\n' +
|
|
500
|
+
'- You need explicit confirmation before a destructive action.\n' +
|
|
501
|
+
"- A form would gather several fields faster than back-and-forth chat.\n\n" +
|
|
502
|
+
"Don't emit a spec for ordinary prose answers — narration alone is fine.\n"
|
|
353
503
|
);
|
|
354
|
-
|
|
355
|
-
return parts.join('\n');
|
|
356
504
|
}
|
|
357
505
|
|
|
358
506
|
/**
|
|
359
507
|
* Build the full system prompt for OpenClaw/non-MCP agents.
|
|
360
508
|
*/
|
|
361
|
-
function buildOpenclawSystemPrompt({ agentName, workspaceId, channelName, endpoint, token, mode = 'execute', disabledModules }) {
|
|
509
|
+
function buildOpenclawSystemPrompt({ agentName, workspaceId, channelName, endpoint, token, mode = 'execute', disabledModules, browserEnabled = false }) {
|
|
362
510
|
const parts = [];
|
|
363
511
|
parts.push(buildWorkspaceIdentity(agentName, workspaceId, channelName, mode));
|
|
512
|
+
parts.push(buildBrowserDirective(browserEnabled));
|
|
364
513
|
parts.push(buildCollaborationPrompt());
|
|
514
|
+
parts.push(buildA2UIPrompt());
|
|
365
515
|
parts.push(buildModePrompt(mode));
|
|
366
516
|
parts.push(buildApiSkillsPrompt({
|
|
367
517
|
endpoint, workspaceId, token, agentName, channelName, disabledModules, mode,
|
|
368
518
|
}));
|
|
519
|
+
parts.push(buildGuardrails());
|
|
369
520
|
return parts.join('\n');
|
|
370
521
|
}
|
|
371
522
|
|
|
372
523
|
/**
|
|
373
524
|
* Build a SKILL.md file for OpenClaw's skill auto-discovery.
|
|
374
525
|
*/
|
|
375
|
-
function buildOpenclawSkillMd({ endpoint, workspaceId, token, agentName, channelName, disabledModules }) {
|
|
526
|
+
function buildOpenclawSkillMd({ endpoint, workspaceId, token, agentName, channelName, disabledModules, browserEnabled = false }) {
|
|
376
527
|
const body = buildApiSkillsPrompt({
|
|
377
528
|
endpoint, workspaceId, token, agentName, channelName, disabledModules, mode: 'execute',
|
|
378
529
|
});
|
|
379
530
|
|
|
380
531
|
const identity = buildWorkspaceIdentity(agentName, workspaceId, channelName, 'execute');
|
|
532
|
+
const directive = buildBrowserDirective(browserEnabled);
|
|
381
533
|
const collab = buildCollaborationPrompt();
|
|
382
534
|
|
|
383
535
|
const frontmatter = (
|
|
@@ -394,18 +546,19 @@ function buildOpenclawSkillMd({ endpoint, workspaceId, token, agentName, channel
|
|
|
394
546
|
'---\n\n'
|
|
395
547
|
);
|
|
396
548
|
|
|
397
|
-
return frontmatter + identity + '\n' + collab + '\n' + body;
|
|
549
|
+
return frontmatter + identity + directive + '\n' + collab + '\n' + body + '\n' + buildGuardrails();
|
|
398
550
|
}
|
|
399
551
|
|
|
400
552
|
/**
|
|
401
553
|
* Build system prompt for OpenCode adapter.
|
|
402
554
|
*/
|
|
403
|
-
function buildOpenCodeSystemPrompt({ agentName, workspaceId, channelName, endpoint, token, mode = 'execute', disabledModules }) {
|
|
555
|
+
function buildOpenCodeSystemPrompt({ agentName, workspaceId, channelName, endpoint, token, mode = 'execute', disabledModules, browserEnabled = false }) {
|
|
404
556
|
const identity = buildWorkspaceIdentity(agentName, workspaceId, channelName, mode);
|
|
557
|
+
const directive = buildBrowserDirective(browserEnabled);
|
|
405
558
|
const collab = buildCollaborationPrompt();
|
|
406
559
|
const modePrompt = buildModePrompt(mode);
|
|
407
560
|
const api = buildApiSkillsPrompt({ endpoint, workspaceId, token, agentName, channelName, disabledModules, mode });
|
|
408
|
-
return identity + '\n' + collab + '\n' + modePrompt + '\n' + api;
|
|
561
|
+
return identity + directive + '\n' + collab + '\n' + modePrompt + '\n' + api + '\n' + buildGuardrails();
|
|
409
562
|
}
|
|
410
563
|
|
|
411
564
|
/**
|
|
@@ -429,7 +582,7 @@ function buildOpenCodeSkillMd({ endpoint, workspaceId, token, agentName, channel
|
|
|
429
582
|
`You are agent '${agentName}' connected to OpenAgents workspace ${workspaceId}.\n` +
|
|
430
583
|
'Use these APIs via bash + curl to interact with the workspace.\n\n';
|
|
431
584
|
|
|
432
|
-
return frontmatter + identity + api;
|
|
585
|
+
return frontmatter + identity + api + '\n' + buildGuardrails();
|
|
433
586
|
}
|
|
434
587
|
|
|
435
588
|
/**
|
|
@@ -439,7 +592,38 @@ function buildOpenCodeSkillMd({ endpoint, workspaceId, token, agentName, channel
|
|
|
439
592
|
* of spawning an MCP server. Claude Code discovers the skill via its
|
|
440
593
|
* .claude/skills/ directory and uses Bash + curl to call workspace APIs.
|
|
441
594
|
*/
|
|
442
|
-
function buildClaudeSkillMd({ endpoint, workspaceId, token, agentName, channelName, disabledModules }) {
|
|
595
|
+
function buildClaudeSkillMd({ endpoint, workspaceId, token, agentName, channelName, disabledModules, browserEnabled = false }) {
|
|
596
|
+
const api = buildApiSkillsPrompt({
|
|
597
|
+
endpoint, workspaceId, token, agentName,
|
|
598
|
+
channelName: channelName || 'general',
|
|
599
|
+
disabledModules,
|
|
600
|
+
mode: 'execute',
|
|
601
|
+
});
|
|
602
|
+
|
|
603
|
+
const identity = buildWorkspaceIdentity(agentName, workspaceId, channelName, 'execute');
|
|
604
|
+
const directive = buildBrowserDirective(browserEnabled);
|
|
605
|
+
const collab = buildCollaborationPrompt();
|
|
606
|
+
|
|
607
|
+
const frontmatter =
|
|
608
|
+
'---\n' +
|
|
609
|
+
'name: openagents-workspace\n' +
|
|
610
|
+
'description: |\n' +
|
|
611
|
+
' OpenAgents Workspace collaboration tools — shared files, browser,\n' +
|
|
612
|
+
' and multi-agent coordination. Use when: sharing files or reports,\n' +
|
|
613
|
+
' browsing websites, reading shared files, checking workspace agents,\n' +
|
|
614
|
+
' or collaborating with other agents via @mentions.\n' +
|
|
615
|
+
'---\n\n';
|
|
616
|
+
|
|
617
|
+
return frontmatter + identity + directive + '\n' + collab + '\n' + api + '\n' + buildGuardrails();
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
/**
|
|
621
|
+
* Build a SKILL.md file for Cursor CLI's skill auto-discovery.
|
|
622
|
+
*
|
|
623
|
+
* Written to .cursor/skills/openagents-workspace.md before each CLI spawn.
|
|
624
|
+
* Cursor discovers skills from the .cursor/skills/ directory automatically.
|
|
625
|
+
*/
|
|
626
|
+
function buildCursorSkillMd({ endpoint, workspaceId, token, agentName, channelName, disabledModules, browserEnabled = false }) {
|
|
443
627
|
const api = buildApiSkillsPrompt({
|
|
444
628
|
endpoint, workspaceId, token, agentName,
|
|
445
629
|
channelName: channelName || 'general',
|
|
@@ -448,6 +632,7 @@ function buildClaudeSkillMd({ endpoint, workspaceId, token, agentName, channelNa
|
|
|
448
632
|
});
|
|
449
633
|
|
|
450
634
|
const identity = buildWorkspaceIdentity(agentName, workspaceId, channelName, 'execute');
|
|
635
|
+
const directive = buildBrowserDirective(browserEnabled);
|
|
451
636
|
const collab = buildCollaborationPrompt();
|
|
452
637
|
|
|
453
638
|
const frontmatter =
|
|
@@ -460,13 +645,15 @@ function buildClaudeSkillMd({ endpoint, workspaceId, token, agentName, channelNa
|
|
|
460
645
|
' or collaborating with other agents via @mentions.\n' +
|
|
461
646
|
'---\n\n';
|
|
462
647
|
|
|
463
|
-
return frontmatter + identity + '\n' + collab + '\n' + api;
|
|
648
|
+
return frontmatter + identity + directive + '\n' + collab + '\n' + api + '\n' + buildGuardrails();
|
|
464
649
|
}
|
|
465
650
|
|
|
466
651
|
module.exports = {
|
|
467
652
|
buildWorkspaceIdentity,
|
|
653
|
+
buildBrowserDirective,
|
|
468
654
|
buildCollaborationPrompt,
|
|
469
655
|
buildModePrompt,
|
|
656
|
+
buildGuardrails,
|
|
470
657
|
buildApiSkillsPrompt,
|
|
471
658
|
buildClaudeSystemPrompt,
|
|
472
659
|
buildOpenclawSystemPrompt,
|
|
@@ -474,4 +661,5 @@ module.exports = {
|
|
|
474
661
|
buildOpenCodeSystemPrompt,
|
|
475
662
|
buildOpenCodeSkillMd,
|
|
476
663
|
buildClaudeSkillMd,
|
|
664
|
+
buildCursorSkillMd,
|
|
477
665
|
};
|