morpheus-cli 0.7.2 → 0.7.4
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 +119 -0
- package/dist/channels/discord.js +109 -0
- package/dist/channels/telegram.js +94 -0
- package/dist/cli/commands/start.js +12 -0
- package/dist/config/manager.js +3 -0
- package/dist/config/paths.js +1 -0
- package/dist/config/schemas.js +11 -2
- package/dist/http/api.js +3 -0
- package/dist/http/routers/skills.js +291 -0
- package/dist/runtime/__tests__/keymaker.test.js +145 -0
- package/dist/runtime/keymaker.js +162 -0
- package/dist/runtime/oracle.js +15 -4
- package/dist/runtime/scaffold.js +75 -0
- package/dist/runtime/skills/__tests__/loader.test.js +187 -0
- package/dist/runtime/skills/__tests__/registry.test.js +201 -0
- package/dist/runtime/skills/__tests__/tool.test.js +266 -0
- package/dist/runtime/skills/index.js +8 -0
- package/dist/runtime/skills/loader.js +213 -0
- package/dist/runtime/skills/registry.js +141 -0
- package/dist/runtime/skills/schema.js +30 -0
- package/dist/runtime/skills/tool.js +204 -0
- package/dist/runtime/skills/types.js +7 -0
- package/dist/runtime/tasks/context.js +16 -0
- package/dist/runtime/tasks/worker.js +22 -0
- package/dist/runtime/tools/apoc-tool.js +28 -1
- package/dist/runtime/tools/morpheus-tools.js +3 -0
- package/dist/runtime/tools/neo-tool.js +32 -0
- package/dist/runtime/tools/trinity-tool.js +27 -0
- package/dist/types/config.js +3 -0
- package/dist/ui/assets/index-CsMDzmtQ.js +117 -0
- package/dist/ui/assets/index-Dz_qYlIb.css +1 -0
- package/dist/ui/index.html +2 -2
- package/dist/ui/sw.js +1 -1
- package/package.json +4 -1
- package/dist/ui/assets/index-7e8TCoiy.js +0 -111
- package/dist/ui/assets/index-B9ngtbja.css +0 -1
package/README.md
CHANGED
|
@@ -22,6 +22,7 @@ It runs as a daemon and orchestrates LLMs, MCP tools, DevKit tools, memory, and
|
|
|
22
22
|
- `Sati`: long-term memory retrieval/evaluation.
|
|
23
23
|
- `Trinity`: database specialist. Executes queries, introspects schemas, and manages registered databases (PostgreSQL, MySQL, SQLite, MongoDB).
|
|
24
24
|
- `Chronos`: temporal scheduler. Runs Oracle prompts on a recurring or one-time schedule.
|
|
25
|
+
- `Keymaker`: skill executor. Runs user-defined skills with full tool access (DevKit + MCP + internal tools).
|
|
25
26
|
|
|
26
27
|
## Installation
|
|
27
28
|
|
|
@@ -441,6 +442,124 @@ Precedence order:
|
|
|
441
442
|
3. `zaion.yaml`
|
|
442
443
|
4. Defaults
|
|
443
444
|
|
|
445
|
+
## Skills
|
|
446
|
+
|
|
447
|
+
Skills are user-defined capabilities that extend Morpheus. Each skill is a folder in `~/.morpheus/skills/` containing a single `SKILL.md` file with YAML frontmatter for metadata.
|
|
448
|
+
|
|
449
|
+
### Execution Modes
|
|
450
|
+
|
|
451
|
+
Skills support two execution modes:
|
|
452
|
+
|
|
453
|
+
- **`sync`** (default) - Executes immediately via `skill_execute`, returns result inline
|
|
454
|
+
- **`async`** - Runs as background task via `skill_delegate`, notifies when complete
|
|
455
|
+
|
|
456
|
+
### Creating a Skill
|
|
457
|
+
|
|
458
|
+
```bash
|
|
459
|
+
mkdir -p ~/.morpheus/skills/my-skill
|
|
460
|
+
```
|
|
461
|
+
|
|
462
|
+
**SKILL.md:**
|
|
463
|
+
```markdown
|
|
464
|
+
---
|
|
465
|
+
name: my-skill
|
|
466
|
+
description: Brief description of what this skill does
|
|
467
|
+
version: 1.0.0
|
|
468
|
+
author: your-name
|
|
469
|
+
enabled: true
|
|
470
|
+
execution_mode: sync
|
|
471
|
+
tags:
|
|
472
|
+
- automation
|
|
473
|
+
- example
|
|
474
|
+
examples:
|
|
475
|
+
- "Example prompt that triggers this skill"
|
|
476
|
+
---
|
|
477
|
+
|
|
478
|
+
# My Skill
|
|
479
|
+
|
|
480
|
+
You are an expert at [domain]. Follow these instructions...
|
|
481
|
+
|
|
482
|
+
## Your Task
|
|
483
|
+
[Instructions for Keymaker to execute]
|
|
484
|
+
```
|
|
485
|
+
|
|
486
|
+
### Async Skill Example
|
|
487
|
+
|
|
488
|
+
For long-running tasks like deployments or builds:
|
|
489
|
+
|
|
490
|
+
```markdown
|
|
491
|
+
---
|
|
492
|
+
name: deploy-staging
|
|
493
|
+
description: Deploy application to staging environment
|
|
494
|
+
execution_mode: async
|
|
495
|
+
tags:
|
|
496
|
+
- deployment
|
|
497
|
+
- devops
|
|
498
|
+
---
|
|
499
|
+
|
|
500
|
+
# Deploy to Staging
|
|
501
|
+
|
|
502
|
+
Execute a full deployment to the staging environment...
|
|
503
|
+
```
|
|
504
|
+
|
|
505
|
+
### Using Skills
|
|
506
|
+
|
|
507
|
+
Once a skill is loaded, Oracle will automatically suggest delegating matching tasks to Keymaker:
|
|
508
|
+
|
|
509
|
+
**Sync Skills (immediate result):**
|
|
510
|
+
```
|
|
511
|
+
User: "Review the code in src/auth.ts"
|
|
512
|
+
Oracle: [executes code-reviewer skill via skill_execute]
|
|
513
|
+
Keymaker: [returns detailed review immediately]
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
**Async Skills (background task):**
|
|
517
|
+
```
|
|
518
|
+
User: "Deploy to staging"
|
|
519
|
+
Oracle: [delegates via skill_delegate, task queued]
|
|
520
|
+
Morpheus: [notifies via Telegram/Discord when complete]
|
|
521
|
+
```
|
|
522
|
+
|
|
523
|
+
### Managing Skills
|
|
524
|
+
|
|
525
|
+
**CLI Commands:**
|
|
526
|
+
```bash
|
|
527
|
+
# View loaded skills
|
|
528
|
+
morpheus skills
|
|
529
|
+
|
|
530
|
+
# Reload skills from disk
|
|
531
|
+
morpheus skills --reload
|
|
532
|
+
```
|
|
533
|
+
|
|
534
|
+
**API Endpoints:**
|
|
535
|
+
```
|
|
536
|
+
GET /api/skills - List all skills
|
|
537
|
+
GET /api/skills/:name - Get skill details
|
|
538
|
+
POST /api/skills/reload - Reload from filesystem
|
|
539
|
+
POST /api/skills/:name/enable - Enable skill
|
|
540
|
+
POST /api/skills/:name/disable - Disable skill
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
**Telegram/Discord Commands:**
|
|
544
|
+
```
|
|
545
|
+
/skills - List skills
|
|
546
|
+
/skill_reload - Reload skills
|
|
547
|
+
/skill_enable <name> - Enable a skill
|
|
548
|
+
/skill_disable <name> - Disable a skill
|
|
549
|
+
```
|
|
550
|
+
|
|
551
|
+
### Sample Skills
|
|
552
|
+
|
|
553
|
+
Example skills are available in `examples/skills/`:
|
|
554
|
+
- `code-reviewer` - Reviews code for issues and best practices (sync)
|
|
555
|
+
- `git-helper` - Assists with Git operations (sync)
|
|
556
|
+
- `deploy-staging` - Deploy to staging environment (async)
|
|
557
|
+
|
|
558
|
+
Copy them to your skills directory:
|
|
559
|
+
```bash
|
|
560
|
+
cp -r examples/skills/* ~/.morpheus/skills/
|
|
561
|
+
```
|
|
562
|
+
|
|
444
563
|
## MCP Configuration
|
|
445
564
|
|
|
446
565
|
Configure MCP servers in `~/.morpheus/mcps.json`.
|
package/dist/channels/discord.js
CHANGED
|
@@ -56,6 +56,24 @@ const SLASH_COMMANDS = [
|
|
|
56
56
|
.setDescription('Delete a Chronos job')
|
|
57
57
|
.addStringOption(opt => opt.setName('id').setDescription('Job ID').setRequired(true))
|
|
58
58
|
.setDMPermission(true),
|
|
59
|
+
new SlashCommandBuilder()
|
|
60
|
+
.setName('skills')
|
|
61
|
+
.setDescription('List all available skills')
|
|
62
|
+
.setDMPermission(true),
|
|
63
|
+
new SlashCommandBuilder()
|
|
64
|
+
.setName('skill_reload')
|
|
65
|
+
.setDescription('Reload skills from filesystem')
|
|
66
|
+
.setDMPermission(true),
|
|
67
|
+
new SlashCommandBuilder()
|
|
68
|
+
.setName('skill_enable')
|
|
69
|
+
.setDescription('Enable a skill')
|
|
70
|
+
.addStringOption(opt => opt.setName('name').setDescription('Skill name').setRequired(true))
|
|
71
|
+
.setDMPermission(true),
|
|
72
|
+
new SlashCommandBuilder()
|
|
73
|
+
.setName('skill_disable')
|
|
74
|
+
.setDescription('Disable a skill')
|
|
75
|
+
.addStringOption(opt => opt.setName('name').setDescription('Skill name').setRequired(true))
|
|
76
|
+
.setDMPermission(true),
|
|
59
77
|
].map(cmd => cmd.toJSON());
|
|
60
78
|
// ─── Adapter ──────────────────────────────────────────────────────────────────
|
|
61
79
|
export class DiscordAdapter {
|
|
@@ -337,6 +355,18 @@ export class DiscordAdapter {
|
|
|
337
355
|
case 'chronos_delete':
|
|
338
356
|
await this.cmdChronosDelete(interaction);
|
|
339
357
|
break;
|
|
358
|
+
case 'skills':
|
|
359
|
+
await this.cmdSkills(interaction);
|
|
360
|
+
break;
|
|
361
|
+
case 'skill_reload':
|
|
362
|
+
await this.cmdSkillReload(interaction);
|
|
363
|
+
break;
|
|
364
|
+
case 'skill_enable':
|
|
365
|
+
await this.cmdSkillEnable(interaction);
|
|
366
|
+
break;
|
|
367
|
+
case 'skill_disable':
|
|
368
|
+
await this.cmdSkillDisable(interaction);
|
|
369
|
+
break;
|
|
340
370
|
}
|
|
341
371
|
}
|
|
342
372
|
async cmdHelp(interaction) {
|
|
@@ -356,6 +386,12 @@ export class DiscordAdapter {
|
|
|
356
386
|
'`/chronos_enable id:` — Enable a job',
|
|
357
387
|
'`/chronos_delete id:` — Delete a job',
|
|
358
388
|
'',
|
|
389
|
+
'**Skills**',
|
|
390
|
+
'`/skills` — List all available skills',
|
|
391
|
+
'`/skill_reload` — Reload skills from filesystem',
|
|
392
|
+
'`/skill_enable name:` — Enable a skill',
|
|
393
|
+
'`/skill_disable name:` — Disable a skill',
|
|
394
|
+
'',
|
|
359
395
|
'You can also send text or voice messages to chat with the Oracle.',
|
|
360
396
|
].join('\n');
|
|
361
397
|
await interaction.reply({ content });
|
|
@@ -548,6 +584,79 @@ export class DiscordAdapter {
|
|
|
548
584
|
await interaction.reply({ content: `Error: ${err.message}` });
|
|
549
585
|
}
|
|
550
586
|
}
|
|
587
|
+
// ─── Skills Commands ─────────────────────────────────────────────────────
|
|
588
|
+
async cmdSkills(interaction) {
|
|
589
|
+
try {
|
|
590
|
+
const { SkillRegistry } = await import('../runtime/skills/index.js');
|
|
591
|
+
const registry = SkillRegistry.getInstance();
|
|
592
|
+
const skills = registry.getAll();
|
|
593
|
+
if (!skills.length) {
|
|
594
|
+
await interaction.reply({ content: 'No skills found. Add skills to `~/.morpheus/skills/`' });
|
|
595
|
+
return;
|
|
596
|
+
}
|
|
597
|
+
const lines = skills.map(s => {
|
|
598
|
+
const status = s.enabled ? '🟢' : '🔴';
|
|
599
|
+
const tags = s.tags?.length ? ` [${s.tags.join(', ')}]` : '';
|
|
600
|
+
return `${status} **${s.name}**${tags}\n _${s.description.slice(0, 50)}${s.description.length > 50 ? '…' : ''}_`;
|
|
601
|
+
});
|
|
602
|
+
const enabled = skills.filter(s => s.enabled).length;
|
|
603
|
+
await interaction.reply({
|
|
604
|
+
content: `**Skills** (${enabled}/${skills.length} enabled)\n\n${lines.join('\n')}`
|
|
605
|
+
});
|
|
606
|
+
}
|
|
607
|
+
catch (err) {
|
|
608
|
+
await interaction.reply({ content: `Error: ${err.message}` });
|
|
609
|
+
}
|
|
610
|
+
}
|
|
611
|
+
async cmdSkillReload(interaction) {
|
|
612
|
+
try {
|
|
613
|
+
const { SkillRegistry, updateSkillDelegateDescription } = await import('../runtime/skills/index.js');
|
|
614
|
+
const registry = SkillRegistry.getInstance();
|
|
615
|
+
const result = await registry.reload();
|
|
616
|
+
updateSkillDelegateDescription();
|
|
617
|
+
const msg = result.errors.length > 0
|
|
618
|
+
? `Reloaded ${result.skills.length} skills with ${result.errors.length} error(s).`
|
|
619
|
+
: `Reloaded ${result.skills.length} skill(s).`;
|
|
620
|
+
await interaction.reply({ content: msg });
|
|
621
|
+
}
|
|
622
|
+
catch (err) {
|
|
623
|
+
await interaction.reply({ content: `Error: ${err.message}` });
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
async cmdSkillEnable(interaction) {
|
|
627
|
+
const name = interaction.options.getString('name', true);
|
|
628
|
+
try {
|
|
629
|
+
const { SkillRegistry, updateSkillDelegateDescription } = await import('../runtime/skills/index.js');
|
|
630
|
+
const registry = SkillRegistry.getInstance();
|
|
631
|
+
const success = registry.enable(name);
|
|
632
|
+
if (!success) {
|
|
633
|
+
await interaction.reply({ content: `Skill "${name}" not found.` });
|
|
634
|
+
return;
|
|
635
|
+
}
|
|
636
|
+
updateSkillDelegateDescription();
|
|
637
|
+
await interaction.reply({ content: `Skill \`${name}\` enabled.` });
|
|
638
|
+
}
|
|
639
|
+
catch (err) {
|
|
640
|
+
await interaction.reply({ content: `Error: ${err.message}` });
|
|
641
|
+
}
|
|
642
|
+
}
|
|
643
|
+
async cmdSkillDisable(interaction) {
|
|
644
|
+
const name = interaction.options.getString('name', true);
|
|
645
|
+
try {
|
|
646
|
+
const { SkillRegistry, updateSkillDelegateDescription } = await import('../runtime/skills/index.js');
|
|
647
|
+
const registry = SkillRegistry.getInstance();
|
|
648
|
+
const success = registry.disable(name);
|
|
649
|
+
if (!success) {
|
|
650
|
+
await interaction.reply({ content: `Skill "${name}" not found.` });
|
|
651
|
+
return;
|
|
652
|
+
}
|
|
653
|
+
updateSkillDelegateDescription();
|
|
654
|
+
await interaction.reply({ content: `Skill \`${name}\` disabled.` });
|
|
655
|
+
}
|
|
656
|
+
catch (err) {
|
|
657
|
+
await interaction.reply({ content: `Error: ${err.message}` });
|
|
658
|
+
}
|
|
659
|
+
}
|
|
551
660
|
// ─── Helpers ──────────────────────────────────────────────────────────────
|
|
552
661
|
isAuthorized(userId) {
|
|
553
662
|
return this.allowedUsers.includes(userId);
|
|
@@ -801,6 +801,22 @@ export class TelegramAdapter {
|
|
|
801
801
|
await this.handleChronosDelete(ctx, id);
|
|
802
802
|
break;
|
|
803
803
|
}
|
|
804
|
+
case '/skills':
|
|
805
|
+
await this.handleSkillsList(ctx);
|
|
806
|
+
break;
|
|
807
|
+
case '/skill_reload':
|
|
808
|
+
await this.handleSkillsReload(ctx);
|
|
809
|
+
break;
|
|
810
|
+
case '/skill_enable': {
|
|
811
|
+
const name = args[0] ?? '';
|
|
812
|
+
await this.handleSkillEnable(ctx, name);
|
|
813
|
+
break;
|
|
814
|
+
}
|
|
815
|
+
case '/skill_disable': {
|
|
816
|
+
const name = args[0] ?? '';
|
|
817
|
+
await this.handleSkillDisable(ctx, name);
|
|
818
|
+
break;
|
|
819
|
+
}
|
|
804
820
|
default:
|
|
805
821
|
await this.handleDefaultCommand(ctx, user, command);
|
|
806
822
|
}
|
|
@@ -1000,6 +1016,84 @@ export class TelegramAdapter {
|
|
|
1000
1016
|
}
|
|
1001
1017
|
}
|
|
1002
1018
|
// ─── End Chronos ──────────────────────────────────────────────────────────────
|
|
1019
|
+
// ─── Skills Command Handlers ─────────────────────────────────────────────────
|
|
1020
|
+
async handleSkillsList(ctx) {
|
|
1021
|
+
try {
|
|
1022
|
+
const { SkillRegistry } = await import('../runtime/skills/index.js');
|
|
1023
|
+
const registry = SkillRegistry.getInstance();
|
|
1024
|
+
const skills = registry.getAll();
|
|
1025
|
+
if (!skills.length) {
|
|
1026
|
+
await ctx.reply('No skills found. Add skills to `~/.morpheus/skills/`', { parse_mode: 'Markdown' });
|
|
1027
|
+
return;
|
|
1028
|
+
}
|
|
1029
|
+
const lines = skills.map(s => {
|
|
1030
|
+
const status = s.enabled ? '🟢' : '🔴';
|
|
1031
|
+
const tags = s.tags?.length ? ` [${s.tags.join(', ')}]` : '';
|
|
1032
|
+
return `${status} *${s.name}*${tags}\n _${s.description.slice(0, 50)}${s.description.length > 50 ? '…' : ''}_`;
|
|
1033
|
+
});
|
|
1034
|
+
const enabled = skills.filter(s => s.enabled).length;
|
|
1035
|
+
await ctx.reply(`*Skills* (${enabled}/${skills.length} enabled)\n\n${lines.join('\n')}`, { parse_mode: 'Markdown' });
|
|
1036
|
+
}
|
|
1037
|
+
catch (err) {
|
|
1038
|
+
await ctx.reply(`Error: ${err.message}`);
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
async handleSkillsReload(ctx) {
|
|
1042
|
+
try {
|
|
1043
|
+
const { SkillRegistry, updateSkillDelegateDescription } = await import('../runtime/skills/index.js');
|
|
1044
|
+
const registry = SkillRegistry.getInstance();
|
|
1045
|
+
const result = await registry.reload();
|
|
1046
|
+
updateSkillDelegateDescription();
|
|
1047
|
+
const msg = result.errors.length > 0
|
|
1048
|
+
? `Reloaded ${result.skills.length} skills with ${result.errors.length} error(s).`
|
|
1049
|
+
: `Reloaded ${result.skills.length} skill(s).`;
|
|
1050
|
+
await ctx.reply(msg);
|
|
1051
|
+
}
|
|
1052
|
+
catch (err) {
|
|
1053
|
+
await ctx.reply(`Error: ${err.message}`);
|
|
1054
|
+
}
|
|
1055
|
+
}
|
|
1056
|
+
async handleSkillEnable(ctx, name) {
|
|
1057
|
+
if (!name) {
|
|
1058
|
+
await ctx.reply('Usage: /skill_enable <name>');
|
|
1059
|
+
return;
|
|
1060
|
+
}
|
|
1061
|
+
try {
|
|
1062
|
+
const { SkillRegistry, updateSkillDelegateDescription } = await import('../runtime/skills/index.js');
|
|
1063
|
+
const registry = SkillRegistry.getInstance();
|
|
1064
|
+
const success = registry.enable(name);
|
|
1065
|
+
if (!success) {
|
|
1066
|
+
await ctx.reply(`Skill "${name}" not found.`);
|
|
1067
|
+
return;
|
|
1068
|
+
}
|
|
1069
|
+
updateSkillDelegateDescription();
|
|
1070
|
+
await ctx.reply(`Skill \`${name}\` enabled.`, { parse_mode: 'Markdown' });
|
|
1071
|
+
}
|
|
1072
|
+
catch (err) {
|
|
1073
|
+
await ctx.reply(`Error: ${err.message}`);
|
|
1074
|
+
}
|
|
1075
|
+
}
|
|
1076
|
+
async handleSkillDisable(ctx, name) {
|
|
1077
|
+
if (!name) {
|
|
1078
|
+
await ctx.reply('Usage: /skill_disable <name>');
|
|
1079
|
+
return;
|
|
1080
|
+
}
|
|
1081
|
+
try {
|
|
1082
|
+
const { SkillRegistry, updateSkillDelegateDescription } = await import('../runtime/skills/index.js');
|
|
1083
|
+
const registry = SkillRegistry.getInstance();
|
|
1084
|
+
const success = registry.disable(name);
|
|
1085
|
+
if (!success) {
|
|
1086
|
+
await ctx.reply(`Skill "${name}" not found.`);
|
|
1087
|
+
return;
|
|
1088
|
+
}
|
|
1089
|
+
updateSkillDelegateDescription();
|
|
1090
|
+
await ctx.reply(`Skill \`${name}\` disabled.`, { parse_mode: 'Markdown' });
|
|
1091
|
+
}
|
|
1092
|
+
catch (err) {
|
|
1093
|
+
await ctx.reply(`Error: ${err.message}`);
|
|
1094
|
+
}
|
|
1095
|
+
}
|
|
1096
|
+
// ─── End Skills ───────────────────────────────────────────────────────────────
|
|
1003
1097
|
async handleNewSessionCommand(ctx, user) {
|
|
1004
1098
|
try {
|
|
1005
1099
|
await ctx.reply("Are you ready to start a new session\\? Please confirm\\.", {
|
|
@@ -22,6 +22,7 @@ import { TaskWorker } from '../../runtime/tasks/worker.js';
|
|
|
22
22
|
import { TaskNotifier } from '../../runtime/tasks/notifier.js';
|
|
23
23
|
import { ChronosWorker } from '../../runtime/chronos/worker.js';
|
|
24
24
|
import { ChronosRepository } from '../../runtime/chronos/repository.js';
|
|
25
|
+
import { SkillRegistry } from '../../runtime/skills/index.js';
|
|
25
26
|
// Load .env file explicitly in start command
|
|
26
27
|
const envPath = path.join(process.cwd(), '.env');
|
|
27
28
|
if (fs.existsSync(envPath)) {
|
|
@@ -126,6 +127,17 @@ export const startCommand = new Command('start')
|
|
|
126
127
|
if (options.ui) {
|
|
127
128
|
display.log(chalk.blue(`Web UI enabled to port ${options.port}`), { source: 'Zaion' });
|
|
128
129
|
}
|
|
130
|
+
// Initialize SkillRegistry before Oracle (so skills are available in system prompt)
|
|
131
|
+
try {
|
|
132
|
+
const skillRegistry = SkillRegistry.getInstance();
|
|
133
|
+
await skillRegistry.load();
|
|
134
|
+
const loadedSkills = skillRegistry.getAll();
|
|
135
|
+
const enabledCount = skillRegistry.getEnabled().length;
|
|
136
|
+
display.log(chalk.green(`✓ Skills loaded: ${loadedSkills.length} total, ${enabledCount} enabled`), { source: 'Skills' });
|
|
137
|
+
}
|
|
138
|
+
catch (err) {
|
|
139
|
+
display.log(chalk.yellow(`Skills initialization warning: ${err.message}`), { source: 'Skills' });
|
|
140
|
+
}
|
|
129
141
|
// Initialize Oracle
|
|
130
142
|
const oracle = new Oracle(config);
|
|
131
143
|
try {
|
package/dist/config/manager.js
CHANGED
|
@@ -169,6 +169,7 @@ export class ConfigManager {
|
|
|
169
169
|
working_dir: resolveString('MORPHEUS_APOC_WORKING_DIR', config.apoc.working_dir, process.cwd()),
|
|
170
170
|
timeout_ms: config.apoc.timeout_ms !== undefined ? resolveNumeric('MORPHEUS_APOC_TIMEOUT_MS', config.apoc.timeout_ms, 30_000) : 30_000,
|
|
171
171
|
personality: resolveString('MORPHEUS_APOC_PERSONALITY', config.apoc.personality, 'pragmatic_dev'),
|
|
172
|
+
execution_mode: resolveString('MORPHEUS_APOC_EXECUTION_MODE', config.apoc.execution_mode, 'async'),
|
|
172
173
|
};
|
|
173
174
|
}
|
|
174
175
|
// Apply precedence to Neo config
|
|
@@ -209,6 +210,7 @@ export class ConfigManager {
|
|
|
209
210
|
base_url: neoBaseUrl || undefined,
|
|
210
211
|
context_window: resolveOptionalNumeric('MORPHEUS_NEO_CONTEXT_WINDOW', config.neo?.context_window, neoContextWindowFallback),
|
|
211
212
|
personality: resolveString('MORPHEUS_NEO_PERSONALITY', config.neo?.personality, 'analytical_engineer'),
|
|
213
|
+
execution_mode: resolveString('MORPHEUS_NEO_EXECUTION_MODE', config.neo?.execution_mode, 'async'),
|
|
212
214
|
};
|
|
213
215
|
}
|
|
214
216
|
// Apply precedence to Trinity config
|
|
@@ -233,6 +235,7 @@ export class ConfigManager {
|
|
|
233
235
|
base_url: config.trinity?.base_url || config.llm.base_url,
|
|
234
236
|
context_window: resolveOptionalNumeric('MORPHEUS_TRINITY_CONTEXT_WINDOW', config.trinity?.context_window, trinityContextWindowFallback),
|
|
235
237
|
personality: resolveString('MORPHEUS_TRINITY_PERSONALITY', config.trinity?.personality, 'data_specialist'),
|
|
238
|
+
execution_mode: resolveString('MORPHEUS_TRINITY_EXECUTION_MODE', config.trinity?.execution_mode, 'async'),
|
|
236
239
|
};
|
|
237
240
|
}
|
|
238
241
|
// Apply precedence to audio config
|
package/dist/config/paths.js
CHANGED
package/dist/config/schemas.js
CHANGED
|
@@ -25,9 +25,17 @@ export const SatiConfigSchema = LLMConfigSchema.extend({
|
|
|
25
25
|
export const ApocConfigSchema = LLMConfigSchema.extend({
|
|
26
26
|
working_dir: z.string().optional(),
|
|
27
27
|
timeout_ms: z.number().int().positive().optional(),
|
|
28
|
+
execution_mode: z.enum(['sync', 'async']).default('async'),
|
|
29
|
+
});
|
|
30
|
+
export const NeoConfigSchema = LLMConfigSchema.extend({
|
|
31
|
+
execution_mode: z.enum(['sync', 'async']).default('async'),
|
|
32
|
+
});
|
|
33
|
+
export const TrinityConfigSchema = LLMConfigSchema.extend({
|
|
34
|
+
execution_mode: z.enum(['sync', 'async']).default('async'),
|
|
35
|
+
});
|
|
36
|
+
export const KeymakerConfigSchema = LLMConfigSchema.extend({
|
|
37
|
+
skills_dir: z.string().optional(),
|
|
28
38
|
});
|
|
29
|
-
export const NeoConfigSchema = LLMConfigSchema;
|
|
30
|
-
export const TrinityConfigSchema = LLMConfigSchema;
|
|
31
39
|
export const WebhookConfigSchema = z.object({
|
|
32
40
|
telegram_notify_all: z.boolean().optional(),
|
|
33
41
|
}).optional();
|
|
@@ -47,6 +55,7 @@ export const ConfigSchema = z.object({
|
|
|
47
55
|
neo: NeoConfigSchema.optional(),
|
|
48
56
|
apoc: ApocConfigSchema.optional(),
|
|
49
57
|
trinity: TrinityConfigSchema.optional(),
|
|
58
|
+
keymaker: KeymakerConfigSchema.optional(),
|
|
50
59
|
webhooks: WebhookConfigSchema,
|
|
51
60
|
audio: AudioConfigSchema.default(DEFAULT_CONFIG.audio),
|
|
52
61
|
memory: z.object({
|
package/dist/http/api.js
CHANGED
|
@@ -18,6 +18,7 @@ import { Trinity } from '../runtime/trinity.js';
|
|
|
18
18
|
import { ChronosRepository } from '../runtime/chronos/repository.js';
|
|
19
19
|
import { ChronosWorker } from '../runtime/chronos/worker.js';
|
|
20
20
|
import { createChronosJobRouter, createChronosConfigRouter } from './routers/chronos.js';
|
|
21
|
+
import { createSkillsRouter } from './routers/skills.js';
|
|
21
22
|
import { getActiveEnvOverrides } from '../config/precedence.js';
|
|
22
23
|
async function readLastLines(filePath, n) {
|
|
23
24
|
try {
|
|
@@ -41,6 +42,8 @@ export function createApiRouter(oracle, chronosWorker) {
|
|
|
41
42
|
router.use('/chronos', createChronosJobRouter(chronosRepo, worker));
|
|
42
43
|
router.use('/config/chronos', createChronosConfigRouter(worker));
|
|
43
44
|
}
|
|
45
|
+
// Mount Skills router
|
|
46
|
+
router.use('/skills', createSkillsRouter());
|
|
44
47
|
// --- Session Management ---
|
|
45
48
|
router.get('/sessions', async (req, res) => {
|
|
46
49
|
try {
|