@openagents-org/agent-launcher 0.2.131 → 0.2.132
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/src/adapters/base.js +13 -0
- package/src/adapters/claude.js +334 -274
- package/src/adapters/workspace-prompt.js +54 -4
- package/src/cli.js +85 -0
- package/src/daemon.js +12 -3
- package/src/index.js +11 -0
- package/src/mcp-server.js +255 -88
- package/src/skill-catalog.js +125 -0
- package/src/update-check.js +5 -25
- package/src/workspace-client.js +69 -0
|
@@ -136,6 +136,7 @@ function buildApiSkillsPrompt({ endpoint, workspaceId, token, agentName, channel
|
|
|
136
136
|
const caps = [];
|
|
137
137
|
if (!disabled.has('files')) caps.push('share and read files with other agents and users');
|
|
138
138
|
if (!disabled.has('browser')) caps.push('browse websites in a shared browser');
|
|
139
|
+
if (!disabled.has('knowledge')) caps.push('create and access a shared knowledge base');
|
|
139
140
|
caps.push('discover other agents in the workspace');
|
|
140
141
|
|
|
141
142
|
sections.push(
|
|
@@ -292,7 +293,7 @@ function buildApiSkillsPrompt({ endpoint, workspaceId, token, agentName, channel
|
|
|
292
293
|
}
|
|
293
294
|
|
|
294
295
|
// To-Dos (planning)
|
|
295
|
-
if (!isPlan) {
|
|
296
|
+
if (!isPlan && !disabled.has('todos')) {
|
|
296
297
|
sections.push(
|
|
297
298
|
'\n### To-Do List (Planning)\n\n' +
|
|
298
299
|
'Create or update your to-do list to track progress. The entire list ' +
|
|
@@ -315,7 +316,7 @@ function buildApiSkillsPrompt({ endpoint, workspaceId, token, agentName, channel
|
|
|
315
316
|
}
|
|
316
317
|
|
|
317
318
|
// Timers
|
|
318
|
-
if (!isPlan) {
|
|
319
|
+
if (!isPlan && !disabled.has('timers')) {
|
|
319
320
|
sections.push(
|
|
320
321
|
'\n### Timers\n\n' +
|
|
321
322
|
'Set a timer that will send you a message after a delay, waking you up ' +
|
|
@@ -336,7 +337,7 @@ function buildApiSkillsPrompt({ endpoint, workspaceId, token, agentName, channel
|
|
|
336
337
|
}
|
|
337
338
|
|
|
338
339
|
// Routines (recurring scheduled tasks)
|
|
339
|
-
if (!isPlan) {
|
|
340
|
+
if (!isPlan && !disabled.has('routines')) {
|
|
340
341
|
sections.push(
|
|
341
342
|
'\n### Routines (Recurring Tasks)\n\n' +
|
|
342
343
|
'Create a recurring routine that fires on a schedule. Each routine gets ' +
|
|
@@ -366,6 +367,52 @@ function buildApiSkillsPrompt({ endpoint, workspaceId, token, agentName, channel
|
|
|
366
367
|
);
|
|
367
368
|
}
|
|
368
369
|
|
|
370
|
+
// Notifications / Inbox
|
|
371
|
+
if (!isPlan && !disabled.has('notifications')) {
|
|
372
|
+
sections.push(
|
|
373
|
+
'\n### Notifications (Inbox)\n\n' +
|
|
374
|
+
'Send notifications to the workspace inbox. Notifications appear in a ' +
|
|
375
|
+
'dedicated panel separate from the chat stream. Use for task completions, ' +
|
|
376
|
+
'important findings, or anything that needs human attention.\n\n' +
|
|
377
|
+
'**Send a notification:**\n' +
|
|
378
|
+
`\`${curl} -s -X POST -H "${h}" -H "Content-Type: application/json" ` +
|
|
379
|
+
`${baseUrl}/v1/notifications -d '{"title":"Task Complete","message":"The analysis is ready.",` +
|
|
380
|
+
`"priority":"normal","channel":"${channelName}",` +
|
|
381
|
+
`"network":"${workspaceId}",` +
|
|
382
|
+
`"source":"openagents:${agentName}"}'\`\n\n` +
|
|
383
|
+
'**Priority values:** `low`, `normal`, `high`\n\n' +
|
|
384
|
+
'**List notifications:**\n' +
|
|
385
|
+
`\`${curl} -s -H "${h}" "${baseUrl}/v1/notifications?network=${workspaceId}"\`\n`
|
|
386
|
+
);
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
// Knowledge Base
|
|
390
|
+
if (!isPlan && !disabled.has('knowledge')) {
|
|
391
|
+
sections.push(
|
|
392
|
+
'\n### Knowledge Base\n\n' +
|
|
393
|
+
'The workspace has a shared knowledge base of markdown documents. ' +
|
|
394
|
+
'Use it to store and retrieve shared information like API docs, ' +
|
|
395
|
+
'design decisions, project conventions, and other reference material. ' +
|
|
396
|
+
'Knowledge entries are accessible to all agents via @knowledge:slug mentions.\n\n' +
|
|
397
|
+
'**Create a knowledge entry:**\n' +
|
|
398
|
+
`\`${curl} -s -X POST -H "${h}" -H "Content-Type: application/json" ` +
|
|
399
|
+
`${baseUrl}/v1/knowledge -d '{"title":"API Design Patterns","content":"# API Design Patterns\\n\\n...",` +
|
|
400
|
+
`"description":"Common API patterns used in this project",` +
|
|
401
|
+
`"network":"${workspaceId}",` +
|
|
402
|
+
`"source":"openagents:${agentName}"}'\`\n\n` +
|
|
403
|
+
'**List knowledge entries:**\n' +
|
|
404
|
+
`\`${curl} -s -H "${h}" "${baseUrl}/v1/knowledge?network=${workspaceId}"\`\n\n` +
|
|
405
|
+
'**Read a knowledge entry by slug:**\n' +
|
|
406
|
+
`\`${curl} -s -H "${h}" "${baseUrl}/v1/knowledge/by-slug/api-design-patterns?network=${workspaceId}"\`\n\n` +
|
|
407
|
+
'**Update a knowledge entry:**\n' +
|
|
408
|
+
`\`${curl} -s -X PUT -H "${h}" -H "Content-Type: application/json" ` +
|
|
409
|
+
`${baseUrl}/v1/knowledge/ENTRY_ID -d '{"title":"Updated Title","content":"# Updated\\n\\n...",` +
|
|
410
|
+
`"network":"${workspaceId}","source":"openagents:${agentName}"}'\`\n\n` +
|
|
411
|
+
'**Delete a knowledge entry:**\n' +
|
|
412
|
+
`\`${curl} -s -X DELETE -H "${h}" "${baseUrl}/v1/knowledge/ENTRY_ID?network=${workspaceId}"\`\n`
|
|
413
|
+
);
|
|
414
|
+
}
|
|
415
|
+
|
|
369
416
|
// Discovery
|
|
370
417
|
sections.push(
|
|
371
418
|
'\n### Discover Agents\n\n' +
|
|
@@ -408,7 +455,10 @@ function buildClaudeSystemPrompt({ agentName, workspaceId, channelName, mode = '
|
|
|
408
455
|
'Use workspace_get_agents to see other agents.\n' +
|
|
409
456
|
'Use workspace_put_todos to track your progress. ALWAYS create a to-do list when given multiple tasks or multi-step work.\n' +
|
|
410
457
|
'Use workspace_create_timer to set a reminder that wakes you up later.\n' +
|
|
411
|
-
'Use workspace_create_routine to set up recurring scheduled tasks (e.g. daily reviews).\n'
|
|
458
|
+
'Use workspace_create_routine to set up recurring scheduled tasks (e.g. daily reviews).\n' +
|
|
459
|
+
'Use workspace_send_notification to send a notification to the workspace inbox when you complete a task or have important results.\n' +
|
|
460
|
+
'Use workspace_write_knowledge to create or update shared knowledge base entries that persist across conversations.\n' +
|
|
461
|
+
'Use workspace_read_knowledge to read knowledge entries by ID or slug (from @knowledge:slug mentions).\n'
|
|
412
462
|
);
|
|
413
463
|
parts.push(buildBrowserDirective(browserEnabled));
|
|
414
464
|
parts.push(buildCollaborationPrompt());
|
package/src/cli.js
CHANGED
|
@@ -502,6 +502,89 @@ async function cmdToolMode(connector, _flags, positional) {
|
|
|
502
502
|
}
|
|
503
503
|
}
|
|
504
504
|
|
|
505
|
+
async function cmdSkills(connector, _flags, positional) {
|
|
506
|
+
const { SKILL_CATALOG, getSkillDefaults } = require('./skill-catalog');
|
|
507
|
+
const toggleable = SKILL_CATALOG.filter(s => s.toggleable);
|
|
508
|
+
const first = positional[0];
|
|
509
|
+
const second = positional[1];
|
|
510
|
+
const third = positional[2];
|
|
511
|
+
|
|
512
|
+
// agn skills → list skills for all agents
|
|
513
|
+
if (!first) {
|
|
514
|
+
const agents = connector.config.getAgents();
|
|
515
|
+
if (agents.length === 0) { print('No agents configured'); return; }
|
|
516
|
+
for (const a of agents) {
|
|
517
|
+
const defaults = getSkillDefaults();
|
|
518
|
+
const skills = a.skills || {};
|
|
519
|
+
const parts = toggleable.map(s => {
|
|
520
|
+
const enabled = skills[s.id] !== undefined ? skills[s.id] : defaults[s.id];
|
|
521
|
+
return `${enabled ? '+' : '-'}${s.id}`;
|
|
522
|
+
});
|
|
523
|
+
print(` ${a.name}: ${parts.join(' ')}`);
|
|
524
|
+
}
|
|
525
|
+
print('\nUsage: agn skills <agent> [enable|disable <skill>]');
|
|
526
|
+
print('Available skills: ' + toggleable.map(s => s.id).join(', '));
|
|
527
|
+
return;
|
|
528
|
+
}
|
|
529
|
+
|
|
530
|
+
// agn skills catalog → show full catalog
|
|
531
|
+
if (first === 'catalog') {
|
|
532
|
+
print('Skill Hub — Available Skills:\n');
|
|
533
|
+
for (const s of SKILL_CATALOG) {
|
|
534
|
+
const tag = s.toggleable ? (s.defaultEnabled ? '[on]' : '[off]') : '[always]';
|
|
535
|
+
print(` ${s.id.padEnd(16)} ${tag.padEnd(10)} ${s.name}`);
|
|
536
|
+
print(` ${''.padEnd(16)} ${''.padEnd(10)} ${s.description}`);
|
|
537
|
+
print('');
|
|
538
|
+
}
|
|
539
|
+
return;
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
const agent = connector.config.getAgent(first);
|
|
543
|
+
if (!agent) { print(`Agent '${first}' not found`); process.exitCode = 1; return; }
|
|
544
|
+
|
|
545
|
+
// agn skills <agent> → show agent's skills
|
|
546
|
+
if (!second) {
|
|
547
|
+
const defaults = getSkillDefaults();
|
|
548
|
+
const skills = agent.skills || {};
|
|
549
|
+
print(`Skills for ${first}:\n`);
|
|
550
|
+
for (const s of toggleable) {
|
|
551
|
+
const enabled = skills[s.id] !== undefined ? skills[s.id] : defaults[s.id];
|
|
552
|
+
const marker = enabled ? ' ✓' : ' ✗';
|
|
553
|
+
print(`${marker} ${s.id.padEnd(14)} ${s.name} — ${s.description}`);
|
|
554
|
+
}
|
|
555
|
+
print('\nUsage: agn skills <agent> enable|disable <skill>');
|
|
556
|
+
return;
|
|
557
|
+
}
|
|
558
|
+
|
|
559
|
+
// agn skills <agent> enable|disable <skill>
|
|
560
|
+
if (second !== 'enable' && second !== 'disable') {
|
|
561
|
+
print(`Unknown action: ${second}. Use 'enable' or 'disable'.`);
|
|
562
|
+
process.exitCode = 1;
|
|
563
|
+
return;
|
|
564
|
+
}
|
|
565
|
+
if (!third) {
|
|
566
|
+
print(`Usage: agn skills ${first} ${second} <skill>`);
|
|
567
|
+
print('Available skills: ' + toggleable.map(s => s.id).join(', '));
|
|
568
|
+
process.exitCode = 1;
|
|
569
|
+
return;
|
|
570
|
+
}
|
|
571
|
+
|
|
572
|
+
const skillDef = toggleable.find(s => s.id === third);
|
|
573
|
+
if (!skillDef) {
|
|
574
|
+
print(`Unknown skill: ${third}`);
|
|
575
|
+
print('Available skills: ' + toggleable.map(s => s.id).join(', '));
|
|
576
|
+
process.exitCode = 1;
|
|
577
|
+
return;
|
|
578
|
+
}
|
|
579
|
+
|
|
580
|
+
const current = agent.skills || {};
|
|
581
|
+
const updated = { ...current, [third]: second === 'enable' };
|
|
582
|
+
connector.config.updateAgent(first, { skills: updated });
|
|
583
|
+
try { connector.sendDaemonCommand('reload'); } catch {}
|
|
584
|
+
const verb = second === 'enable' ? 'Enabled' : 'Disabled';
|
|
585
|
+
print(`${verb} '${skillDef.name}' for ${first}`);
|
|
586
|
+
}
|
|
587
|
+
|
|
505
588
|
async function cmdTestLLM(connector, _flags, positional) {
|
|
506
589
|
const type = positional[0];
|
|
507
590
|
if (!type) { print('Usage: agn test-llm <type>'); return; }
|
|
@@ -566,6 +649,7 @@ Commands:
|
|
|
566
649
|
connect <agent> <token> Connect agent to workspace
|
|
567
650
|
disconnect <agent> Disconnect agent from workspace
|
|
568
651
|
env <type> [--set K=V] View/set env vars for agent type
|
|
652
|
+
skills [agent] [action] Manage agent skills (enable/disable)
|
|
569
653
|
tool-mode [agent] [mode] View/set tool mode (mcp or skills)
|
|
570
654
|
autostart [--disable] Enable/disable auto-start on login
|
|
571
655
|
test-llm <type> Test LLM connection
|
|
@@ -652,6 +736,7 @@ async function main() {
|
|
|
652
736
|
autostart: () => cmdAutostart(connector, flags),
|
|
653
737
|
workspace: () => cmdWorkspace(connector, flags, positional),
|
|
654
738
|
env: () => cmdEnv(connector, flags, positional),
|
|
739
|
+
skills: () => cmdSkills(connector, flags, positional),
|
|
655
740
|
'tool-mode': () => cmdToolMode(connector, flags, positional),
|
|
656
741
|
'test-llm': () => cmdTestLLM(connector, flags, positional),
|
|
657
742
|
update: () => cmdUpdate(),
|
package/src/daemon.js
CHANGED
|
@@ -58,12 +58,20 @@ class Daemon {
|
|
|
58
58
|
// Write PID file
|
|
59
59
|
this._writePid();
|
|
60
60
|
|
|
61
|
-
// Periodic status +
|
|
61
|
+
// Periodic status (heavy: JSON serialize + write) every 5s.
|
|
62
62
|
this._statusInterval = setInterval(() => {
|
|
63
63
|
this._writeStatus();
|
|
64
|
-
this._processCommands();
|
|
65
64
|
}, 5000);
|
|
66
65
|
|
|
66
|
+
// Command file poll (cheap: existsSync on a tiny file) every 200ms so
|
|
67
|
+
// start/stop/restart from the launcher feels responsive. With a 5s
|
|
68
|
+
// combined interval, users saw up to 5s before the daemon even noticed
|
|
69
|
+
// a Stop click — this was especially painful on Windows where there's
|
|
70
|
+
// no SIGHUP shortcut and Stop landed near the end of a tick.
|
|
71
|
+
this._cmdInterval = setInterval(() => {
|
|
72
|
+
this._processCommands();
|
|
73
|
+
}, 200);
|
|
74
|
+
|
|
67
75
|
// Watch config file for hot-reload
|
|
68
76
|
this._watchConfig();
|
|
69
77
|
|
|
@@ -429,6 +437,7 @@ class Daemon {
|
|
|
429
437
|
|
|
430
438
|
async _adapterLoop(name, agentCfg, info, network) {
|
|
431
439
|
const { createAdapter } = require('./adapters');
|
|
440
|
+
const { skillsToDisabledModules } = require('./skill-catalog');
|
|
432
441
|
const agentType = agentCfg.type || 'openclaw';
|
|
433
442
|
const endpoint = network.endpoint || 'https://workspace-endpoint.openagents.org';
|
|
434
443
|
|
|
@@ -442,7 +451,7 @@ class Daemon {
|
|
|
442
451
|
endpoint,
|
|
443
452
|
agentType,
|
|
444
453
|
openclawAgentId: agentCfg.openclaw_agent_id || 'main',
|
|
445
|
-
disabledModules:
|
|
454
|
+
disabledModules: skillsToDisabledModules(agentCfg.skills),
|
|
446
455
|
agentEnv: this._buildAgentEnv(agentCfg),
|
|
447
456
|
workingDir: agentCfg.path || undefined,
|
|
448
457
|
toolMode: agentCfg.tool_mode || 'skills',
|
package/src/index.js
CHANGED
|
@@ -126,6 +126,17 @@ class AgentConnector {
|
|
|
126
126
|
return { success: true };
|
|
127
127
|
}
|
|
128
128
|
|
|
129
|
+
/**
|
|
130
|
+
* Delete the entire type-level env file (~/.openagents/env/<type>.env).
|
|
131
|
+
* Used by the launcher's "wipe saved credentials" path on uninstall so
|
|
132
|
+
* sensitive values (API keys) don't survive a reinstall as surprise
|
|
133
|
+
* pre-filled defaults in the setup wizard.
|
|
134
|
+
*/
|
|
135
|
+
deleteAgentEnv(agentType) {
|
|
136
|
+
this.env.delete(agentType);
|
|
137
|
+
return { success: true };
|
|
138
|
+
}
|
|
139
|
+
|
|
129
140
|
saveAgentInstanceEnv(agentName, env) {
|
|
130
141
|
const agent = this.config.getAgent(agentName);
|
|
131
142
|
if (!agent) throw new Error(`Agent '${agentName}' not found`);
|
package/src/mcp-server.js
CHANGED
|
@@ -235,111 +235,202 @@ function buildToolDefs(disabledModules) {
|
|
|
235
235
|
);
|
|
236
236
|
}
|
|
237
237
|
|
|
238
|
-
// -- Todos
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
238
|
+
// -- Todos --
|
|
239
|
+
if (!disabledModules.has('todos')) {
|
|
240
|
+
tools.push(
|
|
241
|
+
{
|
|
242
|
+
name: 'workspace_put_todos',
|
|
243
|
+
description: 'Update your to-do list. Replaces the entire list each time (send full list with current statuses). Channel is auto-resolved.',
|
|
244
|
+
inputSchema: {
|
|
245
|
+
type: 'object',
|
|
246
|
+
properties: {
|
|
247
|
+
todos: {
|
|
248
|
+
type: 'array',
|
|
249
|
+
items: {
|
|
250
|
+
type: 'object',
|
|
251
|
+
properties: {
|
|
252
|
+
content: { type: 'string', description: 'Task description' },
|
|
253
|
+
status: { type: 'string', enum: ['pending', 'in_progress', 'completed'], description: 'Task status' },
|
|
254
|
+
assignee: { type: 'string', description: 'Agent name to assign to (defaults to self)' },
|
|
255
|
+
},
|
|
256
|
+
required: ['content', 'status'],
|
|
254
257
|
},
|
|
255
|
-
|
|
258
|
+
description: 'Full to-do list with current statuses',
|
|
256
259
|
},
|
|
257
|
-
description: 'Full to-do list with current statuses',
|
|
258
260
|
},
|
|
261
|
+
required: ['todos'],
|
|
259
262
|
},
|
|
260
|
-
required: ['todos'],
|
|
261
263
|
},
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
264
|
+
{
|
|
265
|
+
name: 'workspace_get_todos',
|
|
266
|
+
description: 'Get to-do items for the current channel. Use all=true to see all agents\' todos.',
|
|
267
|
+
inputSchema: {
|
|
268
|
+
type: 'object',
|
|
269
|
+
properties: {
|
|
270
|
+
agent: { type: 'string', description: 'Filter by agent name' },
|
|
271
|
+
all: { type: 'boolean', description: 'Get all agents\' todos (default: own only)' },
|
|
272
|
+
},
|
|
271
273
|
},
|
|
272
274
|
},
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
275
|
+
);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
// -- Timers --
|
|
279
|
+
if (!disabledModules.has('timers')) {
|
|
280
|
+
tools.push(
|
|
281
|
+
{
|
|
282
|
+
name: 'workspace_create_timer',
|
|
283
|
+
description: 'Set a timer that posts a message to the channel after the specified delay.',
|
|
284
|
+
inputSchema: {
|
|
285
|
+
type: 'object',
|
|
286
|
+
properties: {
|
|
287
|
+
delay: { type: 'integer', description: 'Seconds until the timer fires (1-86400)' },
|
|
288
|
+
message: { type: 'string', description: 'Message to post when the timer fires' },
|
|
289
|
+
},
|
|
290
|
+
required: ['delay', 'message'],
|
|
282
291
|
},
|
|
283
|
-
required: ['delay', 'message'],
|
|
284
292
|
},
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
293
|
+
{
|
|
294
|
+
name: 'workspace_list_timers',
|
|
295
|
+
description: 'List active timers in the current channel.',
|
|
296
|
+
inputSchema: { type: 'object', properties: {} },
|
|
297
|
+
},
|
|
298
|
+
{
|
|
299
|
+
name: 'workspace_cancel_timer',
|
|
300
|
+
description: 'Cancel an active timer by its ID.',
|
|
301
|
+
inputSchema: {
|
|
302
|
+
type: 'object',
|
|
303
|
+
properties: {
|
|
304
|
+
timer_id: { type: 'string', description: 'Timer ID to cancel' },
|
|
305
|
+
},
|
|
306
|
+
required: ['timer_id'],
|
|
298
307
|
},
|
|
299
|
-
required: ['timer_id'],
|
|
300
308
|
},
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
type: '
|
|
315
|
-
|
|
316
|
-
description: 'Daily mode:
|
|
309
|
+
);
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
// -- Routines --
|
|
313
|
+
if (!disabledModules.has('routines')) {
|
|
314
|
+
tools.push(
|
|
315
|
+
{
|
|
316
|
+
name: 'workspace_create_routine',
|
|
317
|
+
description: 'Create a recurring scheduled routine. Each routine gets its own dedicated thread with full context. Two schedule modes: daily (hour+minute, optional days) or interval (every N minutes).',
|
|
318
|
+
inputSchema: {
|
|
319
|
+
type: 'object',
|
|
320
|
+
properties: {
|
|
321
|
+
name: { type: 'string', description: 'Human-readable label for the routine (e.g. "Daily PR Review")' },
|
|
322
|
+
message: { type: 'string', description: 'Short trigger message posted each time the routine fires' },
|
|
323
|
+
context: { type: 'string', description: 'Comprehensive context for the routine: what it should do, background info, goals, relevant details from the current conversation. This is posted at the start of the routine\'s dedicated thread so you have full background every time it fires. Be thorough — this is your only memory of why this routine exists.' },
|
|
324
|
+
hour: { type: 'integer', description: 'Daily mode: hour in UTC (0-23). Omit if using interval_minutes.' },
|
|
325
|
+
minute: { type: 'integer', description: 'Daily mode: minute (0-59). Omit if using interval_minutes.' },
|
|
326
|
+
days: {
|
|
327
|
+
type: 'array',
|
|
328
|
+
items: { type: 'integer' },
|
|
329
|
+
description: 'Daily mode: days of week to fire (0=Mon, 6=Sun). Omit for every day. Not allowed in interval mode.',
|
|
330
|
+
},
|
|
331
|
+
interval_minutes: {
|
|
332
|
+
type: 'integer',
|
|
333
|
+
description: 'Interval mode: fire every N minutes (1-1440). Mutually exclusive with hour/minute.',
|
|
334
|
+
},
|
|
317
335
|
},
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
336
|
+
required: ['name', 'message', 'context'],
|
|
337
|
+
},
|
|
338
|
+
},
|
|
339
|
+
{
|
|
340
|
+
name: 'workspace_list_routines',
|
|
341
|
+
description: 'List active routines in the current channel.',
|
|
342
|
+
inputSchema: { type: 'object', properties: {} },
|
|
343
|
+
},
|
|
344
|
+
{
|
|
345
|
+
name: 'workspace_cancel_routine',
|
|
346
|
+
description: 'Cancel a recurring routine by its ID.',
|
|
347
|
+
inputSchema: {
|
|
348
|
+
type: 'object',
|
|
349
|
+
properties: {
|
|
350
|
+
routine_id: { type: 'string', description: 'Routine ID to cancel' },
|
|
321
351
|
},
|
|
352
|
+
required: ['routine_id'],
|
|
322
353
|
},
|
|
323
|
-
required: ['name', 'message', 'context'],
|
|
324
354
|
},
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
355
|
+
);
|
|
356
|
+
}
|
|
357
|
+
|
|
358
|
+
// -- Notifications --
|
|
359
|
+
if (!disabledModules.has('notifications')) {
|
|
360
|
+
tools.push(
|
|
361
|
+
{
|
|
362
|
+
name: 'workspace_send_notification',
|
|
363
|
+
description: 'Send a notification to the workspace inbox. Use this to alert humans about completed tasks, important findings, or anything that needs their attention outside the chat stream.',
|
|
364
|
+
inputSchema: {
|
|
365
|
+
type: 'object',
|
|
366
|
+
properties: {
|
|
367
|
+
title: { type: 'string', description: 'Short notification title (e.g. "News scan complete")' },
|
|
368
|
+
message: { type: 'string', description: 'Notification body with details' },
|
|
369
|
+
priority: { type: 'string', enum: ['low', 'normal', 'high'], description: 'Notification priority (default: normal)' },
|
|
370
|
+
channel: { type: 'string', description: 'Related channel/thread name (for navigation link)' },
|
|
371
|
+
},
|
|
372
|
+
required: ['title', 'message'],
|
|
338
373
|
},
|
|
339
|
-
required: ['routine_id'],
|
|
340
374
|
},
|
|
341
|
-
|
|
342
|
-
|
|
375
|
+
{
|
|
376
|
+
name: 'workspace_get_notifications',
|
|
377
|
+
description: 'List notifications you have sent to the workspace inbox.',
|
|
378
|
+
inputSchema: {
|
|
379
|
+
type: 'object',
|
|
380
|
+
properties: {
|
|
381
|
+
limit: { type: 'integer', description: 'Number of notifications to return (default 20)' },
|
|
382
|
+
},
|
|
383
|
+
},
|
|
384
|
+
},
|
|
385
|
+
);
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
// -- Knowledge Base --
|
|
389
|
+
if (!disabledModules.has('knowledge')) {
|
|
390
|
+
tools.push(
|
|
391
|
+
{
|
|
392
|
+
name: 'workspace_list_knowledge',
|
|
393
|
+
description: 'List knowledge base entries shared in the workspace.',
|
|
394
|
+
inputSchema: { type: 'object', properties: {} },
|
|
395
|
+
},
|
|
396
|
+
{
|
|
397
|
+
name: 'workspace_read_knowledge',
|
|
398
|
+
description: 'Read a knowledge base entry by ID or slug. Returns the full markdown content.',
|
|
399
|
+
inputSchema: {
|
|
400
|
+
type: 'object',
|
|
401
|
+
properties: {
|
|
402
|
+
entry_id: { type: 'string', description: 'Knowledge entry ID' },
|
|
403
|
+
slug: { type: 'string', description: 'Knowledge entry slug (alternative to entry_id)' },
|
|
404
|
+
},
|
|
405
|
+
},
|
|
406
|
+
},
|
|
407
|
+
{
|
|
408
|
+
name: 'workspace_write_knowledge',
|
|
409
|
+
description: 'Create or update a knowledge base entry. Knowledge entries are workspace-global markdown documents accessible to all agents via @knowledge:slug mentions.',
|
|
410
|
+
inputSchema: {
|
|
411
|
+
type: 'object',
|
|
412
|
+
properties: {
|
|
413
|
+
title: { type: 'string', description: 'Entry title' },
|
|
414
|
+
content: { type: 'string', description: 'Markdown content of the knowledge entry' },
|
|
415
|
+
description: { type: 'string', description: 'Short summary (optional)' },
|
|
416
|
+
entry_id: { type: 'string', description: 'Existing entry ID to update (omit to create new)' },
|
|
417
|
+
},
|
|
418
|
+
required: ['title', 'content'],
|
|
419
|
+
},
|
|
420
|
+
},
|
|
421
|
+
{
|
|
422
|
+
name: 'workspace_delete_knowledge',
|
|
423
|
+
description: 'Delete a knowledge base entry by ID.',
|
|
424
|
+
inputSchema: {
|
|
425
|
+
type: 'object',
|
|
426
|
+
properties: {
|
|
427
|
+
entry_id: { type: 'string', description: 'Knowledge entry ID to delete' },
|
|
428
|
+
},
|
|
429
|
+
required: ['entry_id'],
|
|
430
|
+
},
|
|
431
|
+
},
|
|
432
|
+
);
|
|
433
|
+
}
|
|
343
434
|
|
|
344
435
|
return tools;
|
|
345
436
|
}
|
|
@@ -808,6 +899,82 @@ class McpServer {
|
|
|
808
899
|
return text(`Routine cancelled: ${args.routine_id}`);
|
|
809
900
|
}
|
|
810
901
|
|
|
902
|
+
case 'workspace_send_notification': {
|
|
903
|
+
const result = await this.ws.createNotification(
|
|
904
|
+
this.workspaceId, this.token,
|
|
905
|
+
{
|
|
906
|
+
title: args.title,
|
|
907
|
+
message: args.message,
|
|
908
|
+
priority: args.priority || 'normal',
|
|
909
|
+
channel: args.channel || this.channelName,
|
|
910
|
+
source: `openagents:${this.agentName}`,
|
|
911
|
+
},
|
|
912
|
+
);
|
|
913
|
+
return text(`Notification sent: "${args.title}" (id: ${result.id})`);
|
|
914
|
+
}
|
|
915
|
+
|
|
916
|
+
case 'workspace_get_notifications': {
|
|
917
|
+
const data = await this.ws.listNotifications(this.workspaceId, this.token, {
|
|
918
|
+
limit: args.limit || 20,
|
|
919
|
+
});
|
|
920
|
+
const notifications = (data && data.notifications) || [];
|
|
921
|
+
if (!notifications.length) return text('No notifications.');
|
|
922
|
+
const lines = notifications.map((n) => {
|
|
923
|
+
const readStatus = n.is_read ? '[read]' : '[unread]';
|
|
924
|
+
const prio = n.priority !== 'normal' ? ` [${n.priority}]` : '';
|
|
925
|
+
return `- ${readStatus}${prio} ${n.title}: ${n.message.slice(0, 80)}${n.message.length > 80 ? '...' : ''} (${n.created_at})`;
|
|
926
|
+
});
|
|
927
|
+
return text(lines.join('\n'));
|
|
928
|
+
}
|
|
929
|
+
|
|
930
|
+
// ── Knowledge Base ──
|
|
931
|
+
|
|
932
|
+
case 'workspace_list_knowledge': {
|
|
933
|
+
const data = await this.ws.listKnowledge(this.workspaceId, this.token);
|
|
934
|
+
const entries = (data && data.entries) || [];
|
|
935
|
+
if (!entries.length) return text('No knowledge entries yet.');
|
|
936
|
+
const lines = entries.map((e) =>
|
|
937
|
+
`- ${e.title} (slug: ${e.slug}, id: ${e.id})`
|
|
938
|
+
);
|
|
939
|
+
return text(lines.join('\n'));
|
|
940
|
+
}
|
|
941
|
+
|
|
942
|
+
case 'workspace_read_knowledge': {
|
|
943
|
+
let data;
|
|
944
|
+
if (args.slug) {
|
|
945
|
+
data = await this.ws.getKnowledgeBySlug(this.workspaceId, this.token, args.slug);
|
|
946
|
+
} else if (args.entry_id) {
|
|
947
|
+
data = await this.ws.getKnowledge(this.workspaceId, this.token, args.entry_id);
|
|
948
|
+
} else {
|
|
949
|
+
throw new Error('Either entry_id or slug is required');
|
|
950
|
+
}
|
|
951
|
+
return text(`# ${data.title}\n\n${data.content || '(empty)'}`);
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
case 'workspace_write_knowledge': {
|
|
955
|
+
let result;
|
|
956
|
+
if (args.entry_id) {
|
|
957
|
+
result = await this.ws.updateKnowledge(
|
|
958
|
+
this.workspaceId, this.token, args.entry_id,
|
|
959
|
+
{ title: args.title, content: args.content, description: args.description,
|
|
960
|
+
source: `openagents:${this.agentName}` },
|
|
961
|
+
);
|
|
962
|
+
return text(`Knowledge updated: "${result.title}" (slug: ${result.slug})`);
|
|
963
|
+
} else {
|
|
964
|
+
result = await this.ws.createKnowledge(
|
|
965
|
+
this.workspaceId, this.token,
|
|
966
|
+
{ title: args.title, content: args.content, description: args.description,
|
|
967
|
+
source: `openagents:${this.agentName}` },
|
|
968
|
+
);
|
|
969
|
+
return text(`Knowledge created: "${result.title}" (slug: ${result.slug}, id: ${result.id})`);
|
|
970
|
+
}
|
|
971
|
+
}
|
|
972
|
+
|
|
973
|
+
case 'workspace_delete_knowledge': {
|
|
974
|
+
await this.ws.deleteKnowledge(this.workspaceId, this.token, args.entry_id);
|
|
975
|
+
return text(`Knowledge entry deleted: ${args.entry_id}`);
|
|
976
|
+
}
|
|
977
|
+
|
|
811
978
|
default:
|
|
812
979
|
throw new Error(`Unknown tool: ${name}`);
|
|
813
980
|
}
|