morpheus-cli 0.9.5 → 0.9.6
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 +63 -43
- package/dist/channels/discord.js +3 -6
- package/dist/channels/telegram.js +3 -6
- package/dist/cli/commands/restart.js +15 -0
- package/dist/cli/commands/start.js +16 -0
- package/dist/config/manager.js +61 -0
- package/dist/config/paths.js +1 -0
- package/dist/config/schemas.js +11 -3
- package/dist/http/api.js +3 -0
- package/dist/http/routers/link.js +239 -0
- package/dist/http/routers/skills.js +1 -8
- package/dist/runtime/apoc.js +1 -1
- package/dist/runtime/audit/repository.js +1 -1
- package/dist/runtime/link-chunker.js +214 -0
- package/dist/runtime/link-repository.js +301 -0
- package/dist/runtime/link-search.js +298 -0
- package/dist/runtime/link-worker.js +284 -0
- package/dist/runtime/link.js +295 -0
- package/dist/runtime/memory/sati/service.js +1 -1
- package/dist/runtime/neo.js +1 -1
- package/dist/runtime/oracle.js +81 -44
- package/dist/runtime/scaffold.js +4 -17
- package/dist/runtime/skills/__tests__/loader.test.js +7 -10
- package/dist/runtime/skills/__tests__/registry.test.js +2 -18
- package/dist/runtime/skills/__tests__/tool.test.js +55 -224
- package/dist/runtime/skills/index.js +1 -2
- package/dist/runtime/skills/loader.js +0 -2
- package/dist/runtime/skills/registry.js +8 -20
- package/dist/runtime/skills/schema.js +0 -4
- package/dist/runtime/skills/tool.js +42 -209
- package/dist/runtime/smiths/delegator.js +1 -1
- package/dist/runtime/smiths/registry.js +1 -1
- package/dist/runtime/tasks/worker.js +12 -44
- package/dist/runtime/trinity.js +1 -1
- package/dist/types/config.js +14 -0
- package/dist/ui/assets/AuditDashboard-93LCGHG1.js +1 -0
- package/dist/ui/assets/{Chat-BNtutgja.js → Chat-CK5sNcQ1.js} +8 -8
- package/dist/ui/assets/{Chronos-3C8RPZcl.js → Chronos-m2h--GEe.js} +1 -1
- package/dist/ui/assets/{ConfirmationModal-ZQPBeJ2Z.js → ConfirmationModal-Dd5pUJme.js} +1 -1
- package/dist/ui/assets/{Dashboard-CqkHzr2F.js → Dashboard-ODwl7d-a.js} +1 -1
- package/dist/ui/assets/{DeleteConfirmationModal-CioxFWn_.js → DeleteConfirmationModal-CCcojDmr.js} +1 -1
- package/dist/ui/assets/Documents-dWnSoxFO.js +7 -0
- package/dist/ui/assets/{Logs-DBVanS0O.js → Logs-Dc9Z2LBj.js} +1 -1
- package/dist/ui/assets/{MCPManager-vXfL3P2U.js → MCPManager-CMkb8vMn.js} +1 -1
- package/dist/ui/assets/{ModelPricing-DyfdunLT.js → ModelPricing-DtHPPbEQ.js} +1 -1
- package/dist/ui/assets/{Notifications-VL-vep6d.js → Notifications-BPvo-DWP.js} +1 -1
- package/dist/ui/assets/{Pagination-oTGieBLM.js → Pagination-BHZKk42X.js} +1 -1
- package/dist/ui/assets/{SatiMemories-jaadkW0U.js → SatiMemories-BUPu1Lxr.js} +1 -1
- package/dist/ui/assets/SessionAudit-CFKF4DA8.js +9 -0
- package/dist/ui/assets/Settings-C4JrXfsR.js +47 -0
- package/dist/ui/assets/{Skills-DE3zziXL.js → Skills-BUlvJgJ4.js} +1 -1
- package/dist/ui/assets/{Smiths-pmogN1mU.js → Smiths-CDtJdY0I.js} +1 -1
- package/dist/ui/assets/{Tasks-Bs8s34Jc.js → Tasks-DK_cOsNK.js} +1 -1
- package/dist/ui/assets/{TrinityDatabases-D7uihcdp.js → TrinityDatabases-X07by-19.js} +1 -1
- package/dist/ui/assets/{UsageStats-B9gePLZ0.js → UsageStats-dYcgckLq.js} +1 -1
- package/dist/ui/assets/{WebhookManager-B2L3rCLM.js → WebhookManager-DDw5eX2R.js} +1 -1
- package/dist/ui/assets/{audit-Cggeu9mM.js → audit-DZ5WLUEm.js} +1 -1
- package/dist/ui/assets/{chronos-D3-sWhfU.js → chronos-B_HI4mlq.js} +1 -1
- package/dist/ui/assets/{config-CBqRUPgn.js → config-B-YxlVrc.js} +1 -1
- package/dist/ui/assets/index-DVjwJ8jT.css +1 -0
- package/dist/ui/assets/{index-zKplfrXZ.js → index-DfJwcKqG.js} +5 -5
- package/dist/ui/assets/{mcp-uL1R9hyA.js → mcp-k-_pwbqA.js} +1 -1
- package/dist/ui/assets/{skills-jmw8yTJs.js → skills-xMXangks.js} +1 -1
- package/dist/ui/assets/{stats-HOms6GnM.js → stats-C4QZIv5O.js} +1 -1
- package/dist/ui/assets/{vendor-icons-DMd9RGvJ.js → vendor-icons-NHF9HNeN.js} +1 -1
- package/dist/ui/index.html +3 -3
- package/dist/ui/sw.js +1 -1
- package/package.json +3 -1
- package/dist/runtime/__tests__/keymaker.test.js +0 -148
- package/dist/runtime/keymaker.js +0 -157
- package/dist/ui/assets/AuditDashboard-DliJ1CX0.js +0 -1
- package/dist/ui/assets/SessionAudit-BsXrWlwz.js +0 -9
- package/dist/ui/assets/Settings-B4eezRcg.js +0 -47
- package/dist/ui/assets/index-D4fzIKy1.css +0 -1
package/README.md
CHANGED
|
@@ -22,8 +22,8 @@ It runs as a daemon and orchestrates LLMs, MCP tools, DevKit tools, memory, and
|
|
|
22
22
|
- `Apoc`: DevTools/browser execution (filesystem, shell, git, network, packages, processes, system, browser automation).
|
|
23
23
|
- `Sati`: long-term memory retrieval/evaluation.
|
|
24
24
|
- `Trinity`: database specialist. Executes queries, introspects schemas, and manages registered databases (PostgreSQL, MySQL, SQLite, MongoDB).
|
|
25
|
+
- `Link`: documentation specialist. RAG over indexed user documents (PDF, Markdown, TXT, DOCX) with hybrid vector + keyword search.
|
|
25
26
|
- `Chronos`: temporal scheduler. Runs Oracle prompts on a recurring or one-time schedule.
|
|
26
|
-
- `Keymaker`: skill executor. Runs user-defined skills with full tool access (DevKit + MCP + internal tools).
|
|
27
27
|
- `Smith`: remote DevKit executor. Runs DevKit operations on isolated machines (Docker, VMs, cloud) via WebSocket.
|
|
28
28
|
|
|
29
29
|
## Installation
|
|
@@ -510,14 +510,15 @@ Precedence order:
|
|
|
510
510
|
|
|
511
511
|
## Skills
|
|
512
512
|
|
|
513
|
-
Skills are user-defined
|
|
513
|
+
Skills are user-defined instruction templates that extend Morpheus capabilities. Each skill is a folder in `~/.morpheus/skills/` containing a single `SKILL.md` file with YAML frontmatter for metadata.
|
|
514
514
|
|
|
515
|
-
###
|
|
515
|
+
### How Skills Work
|
|
516
516
|
|
|
517
|
-
Skills
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
517
|
+
Skills are **instruction templates** loaded into Oracle's context on-demand:
|
|
518
|
+
1. User request matches a skill's examples/description
|
|
519
|
+
2. Oracle calls `load_skill` tool with the skill name
|
|
520
|
+
3. Tool returns the skill's full content (SKILL.md)
|
|
521
|
+
4. Oracle follows the instructions using its existing tools (delegation, MCP, etc.)
|
|
521
522
|
|
|
522
523
|
### Creating a Skill
|
|
523
524
|
|
|
@@ -533,7 +534,6 @@ description: Brief description of what this skill does
|
|
|
533
534
|
version: 1.0.0
|
|
534
535
|
author: your-name
|
|
535
536
|
enabled: true
|
|
536
|
-
execution_mode: sync
|
|
537
537
|
tags:
|
|
538
538
|
- automation
|
|
539
539
|
- example
|
|
@@ -546,44 +546,17 @@ examples:
|
|
|
546
546
|
You are an expert at [domain]. Follow these instructions...
|
|
547
547
|
|
|
548
548
|
## Your Task
|
|
549
|
-
[Instructions for
|
|
550
|
-
```
|
|
551
|
-
|
|
552
|
-
### Async Skill Example
|
|
553
|
-
|
|
554
|
-
For long-running tasks like deployments or builds:
|
|
555
|
-
|
|
556
|
-
```markdown
|
|
557
|
-
---
|
|
558
|
-
name: deploy-staging
|
|
559
|
-
description: Deploy application to staging environment
|
|
560
|
-
execution_mode: async
|
|
561
|
-
tags:
|
|
562
|
-
- deployment
|
|
563
|
-
- devops
|
|
564
|
-
---
|
|
565
|
-
|
|
566
|
-
# Deploy to Staging
|
|
567
|
-
|
|
568
|
-
Execute a full deployment to the staging environment...
|
|
549
|
+
[Instructions for Oracle to execute]
|
|
569
550
|
```
|
|
570
551
|
|
|
571
552
|
### Using Skills
|
|
572
553
|
|
|
573
|
-
Once a skill is loaded, Oracle
|
|
554
|
+
Once a skill is loaded, Oracle follows the instructions using its available tools:
|
|
574
555
|
|
|
575
|
-
**Sync Skills (immediate result):**
|
|
576
556
|
```
|
|
577
557
|
User: "Review the code in src/auth.ts"
|
|
578
|
-
Oracle: [
|
|
579
|
-
|
|
580
|
-
```
|
|
581
|
-
|
|
582
|
-
**Async Skills (background task):**
|
|
583
|
-
```
|
|
584
|
-
User: "Deploy to staging"
|
|
585
|
-
Oracle: [delegates via skill_delegate, task queued]
|
|
586
|
-
Morpheus: [notifies via Telegram/Discord when complete]
|
|
558
|
+
Oracle: [loads code-reviewer skill via load_skill]
|
|
559
|
+
Oracle: [executes review using its tools and returns result]
|
|
587
560
|
```
|
|
588
561
|
|
|
589
562
|
### Managing Skills
|
|
@@ -617,15 +590,61 @@ POST /api/skills/:name/disable - Disable skill
|
|
|
617
590
|
### Sample Skills
|
|
618
591
|
|
|
619
592
|
Example skills are available in `examples/skills/`:
|
|
620
|
-
- `code-reviewer` - Reviews code for issues and best practices
|
|
621
|
-
- `git-helper` - Assists with Git operations
|
|
622
|
-
- `deploy-staging` - Deploy to staging environment (async)
|
|
593
|
+
- `code-reviewer` - Reviews code for issues and best practices
|
|
594
|
+
- `git-helper` - Assists with Git operations
|
|
623
595
|
|
|
624
596
|
Copy them to your skills directory:
|
|
625
597
|
```bash
|
|
626
598
|
cp -r examples/skills/* ~/.morpheus/skills/
|
|
627
599
|
```
|
|
628
600
|
|
|
601
|
+
## Link — Document RAG
|
|
602
|
+
|
|
603
|
+
Link is a documentation specialist subagent that provides RAG (Retrieval-Augmented Generation) over user documents.
|
|
604
|
+
|
|
605
|
+
**Supported formats:** PDF, Markdown, TXT, DOCX
|
|
606
|
+
|
|
607
|
+
**Document storage:** `~/.morpheus/docs/`
|
|
608
|
+
|
|
609
|
+
**Embedding database:** `~/.morpheus/memory/link.db` (SQLite with sqlite-vec)
|
|
610
|
+
|
|
611
|
+
### Using Link
|
|
612
|
+
|
|
613
|
+
Oracle automatically delegates to Link when users ask about their documents:
|
|
614
|
+
|
|
615
|
+
```
|
|
616
|
+
User: "What does my contract say about termination?"
|
|
617
|
+
Oracle: [delegates to Link via link_delegate]
|
|
618
|
+
Link: [searches indexed documents and returns answer with citations]
|
|
619
|
+
```
|
|
620
|
+
|
|
621
|
+
### Managing Documents
|
|
622
|
+
|
|
623
|
+
**Web UI:** Navigate to `/documents` to upload, delete, and reindex documents.
|
|
624
|
+
|
|
625
|
+
**API Endpoints:**
|
|
626
|
+
```
|
|
627
|
+
GET /api/link/documents - List all documents
|
|
628
|
+
POST /api/link/upload - Upload a document
|
|
629
|
+
DELETE /api/link/documents/:id - Delete a document
|
|
630
|
+
POST /api/link/documents/:id/reindex - Reindex a document
|
|
631
|
+
```
|
|
632
|
+
|
|
633
|
+
### Configuration
|
|
634
|
+
|
|
635
|
+
```yaml
|
|
636
|
+
link:
|
|
637
|
+
provider: openai
|
|
638
|
+
model: gpt-4o-mini
|
|
639
|
+
temperature: 0.2
|
|
640
|
+
personality: documentation_specialist
|
|
641
|
+
execution_mode: async # 'sync' = inline, 'async' = background task
|
|
642
|
+
max_results: 10 # max search results per query
|
|
643
|
+
score_threshold: 0.5 # minimum similarity score (0-1)
|
|
644
|
+
chunk_size: 1000 # characters per chunk
|
|
645
|
+
chunk_overlap: 200 # overlap between chunks
|
|
646
|
+
```
|
|
647
|
+
|
|
629
648
|
## MCP Configuration
|
|
630
649
|
|
|
631
650
|
Configure MCP servers in `~/.morpheus/mcps.json`.
|
|
@@ -655,9 +674,10 @@ Authenticated endpoints (`x-architect-pass`):
|
|
|
655
674
|
- Sessions: `/api/sessions*`
|
|
656
675
|
- Chat: `POST /api/chat`
|
|
657
676
|
- Tasks: `GET /api/tasks`, `GET /api/tasks/stats`, `GET /api/tasks/:id`, `POST /api/tasks/:id/retry`
|
|
658
|
-
- Config: `/api/config`, `/api/config/sati`, `/api/config/neo`, `/api/config/apoc`, `/api/config/trinity`, `/api/config/chronos`, `/api/config/smiths`
|
|
677
|
+
- Config: `/api/config`, `/api/config/sati`, `/api/config/neo`, `/api/config/apoc`, `/api/config/trinity`, `/api/config/link`, `/api/config/chronos`, `/api/config/smiths`
|
|
659
678
|
- MCP: `/api/mcp/*` (servers CRUD + reload + status)
|
|
660
679
|
- Sati memories: `/api/sati/memories*`
|
|
680
|
+
- Link documents: `/api/link/documents`, `/api/link/upload`, `/api/link/documents/:id/reindex`
|
|
661
681
|
- Trinity databases: `GET/POST/PUT/DELETE /api/trinity/databases`, `POST /api/trinity/databases/:id/test`, `POST /api/trinity/databases/:id/refresh-schema`
|
|
662
682
|
- Chronos: `GET/POST /api/chronos`, `GET/PUT/DELETE /api/chronos/:id`, `PATCH /api/chronos/:id/enable`, `PATCH /api/chronos/:id/disable`, `GET /api/chronos/:id/executions`, `POST /api/chronos/preview`
|
|
663
683
|
- Smiths: `GET /api/smiths`, `GET/PUT /api/smiths/config`, `GET/DELETE /api/smiths/:name`, `POST /api/smiths/:name/ping`
|
package/dist/channels/discord.js
CHANGED
|
@@ -773,10 +773,9 @@ export class DiscordAdapter {
|
|
|
773
773
|
}
|
|
774
774
|
async cmdSkillReload(interaction) {
|
|
775
775
|
try {
|
|
776
|
-
const { SkillRegistry
|
|
776
|
+
const { SkillRegistry } = await import('../runtime/skills/index.js');
|
|
777
777
|
const registry = SkillRegistry.getInstance();
|
|
778
778
|
const result = await registry.reload();
|
|
779
|
-
updateSkillDelegateDescription();
|
|
780
779
|
const msg = result.errors.length > 0
|
|
781
780
|
? `Reloaded ${result.skills.length} skills with ${result.errors.length} error(s).`
|
|
782
781
|
: `Reloaded ${result.skills.length} skill(s).`;
|
|
@@ -789,14 +788,13 @@ export class DiscordAdapter {
|
|
|
789
788
|
async cmdSkillEnable(interaction) {
|
|
790
789
|
const name = interaction.options.getString('name', true);
|
|
791
790
|
try {
|
|
792
|
-
const { SkillRegistry
|
|
791
|
+
const { SkillRegistry } = await import('../runtime/skills/index.js');
|
|
793
792
|
const registry = SkillRegistry.getInstance();
|
|
794
793
|
const success = registry.enable(name);
|
|
795
794
|
if (!success) {
|
|
796
795
|
await interaction.reply({ content: `Skill "${name}" not found.` });
|
|
797
796
|
return;
|
|
798
797
|
}
|
|
799
|
-
updateSkillDelegateDescription();
|
|
800
798
|
await interaction.reply({ content: `Skill \`${name}\` enabled.` });
|
|
801
799
|
}
|
|
802
800
|
catch (err) {
|
|
@@ -806,14 +804,13 @@ export class DiscordAdapter {
|
|
|
806
804
|
async cmdSkillDisable(interaction) {
|
|
807
805
|
const name = interaction.options.getString('name', true);
|
|
808
806
|
try {
|
|
809
|
-
const { SkillRegistry
|
|
807
|
+
const { SkillRegistry } = await import('../runtime/skills/index.js');
|
|
810
808
|
const registry = SkillRegistry.getInstance();
|
|
811
809
|
const success = registry.disable(name);
|
|
812
810
|
if (!success) {
|
|
813
811
|
await interaction.reply({ content: `Skill "${name}" not found.` });
|
|
814
812
|
return;
|
|
815
813
|
}
|
|
816
|
-
updateSkillDelegateDescription();
|
|
817
814
|
await interaction.reply({ content: `Skill \`${name}\` disabled.` });
|
|
818
815
|
}
|
|
819
816
|
catch (err) {
|
|
@@ -1087,10 +1087,9 @@ export class TelegramAdapter {
|
|
|
1087
1087
|
}
|
|
1088
1088
|
async handleSkillsReload(ctx) {
|
|
1089
1089
|
try {
|
|
1090
|
-
const { SkillRegistry
|
|
1090
|
+
const { SkillRegistry } = await import('../runtime/skills/index.js');
|
|
1091
1091
|
const registry = SkillRegistry.getInstance();
|
|
1092
1092
|
const result = await registry.reload();
|
|
1093
|
-
updateSkillDelegateDescription();
|
|
1094
1093
|
const msg = result.errors.length > 0
|
|
1095
1094
|
? `Reloaded ${result.skills.length} skills with ${result.errors.length} error(s).`
|
|
1096
1095
|
: `Reloaded ${result.skills.length} skill(s).`;
|
|
@@ -1106,14 +1105,13 @@ export class TelegramAdapter {
|
|
|
1106
1105
|
return;
|
|
1107
1106
|
}
|
|
1108
1107
|
try {
|
|
1109
|
-
const { SkillRegistry
|
|
1108
|
+
const { SkillRegistry } = await import('../runtime/skills/index.js');
|
|
1110
1109
|
const registry = SkillRegistry.getInstance();
|
|
1111
1110
|
const success = registry.enable(name);
|
|
1112
1111
|
if (!success) {
|
|
1113
1112
|
await ctx.reply(`Skill "${name}" not found.`);
|
|
1114
1113
|
return;
|
|
1115
1114
|
}
|
|
1116
|
-
updateSkillDelegateDescription();
|
|
1117
1115
|
await ctx.reply(`Skill \`${name}\` enabled.`, { parse_mode: 'Markdown' });
|
|
1118
1116
|
}
|
|
1119
1117
|
catch (err) {
|
|
@@ -1126,14 +1124,13 @@ export class TelegramAdapter {
|
|
|
1126
1124
|
return;
|
|
1127
1125
|
}
|
|
1128
1126
|
try {
|
|
1129
|
-
const { SkillRegistry
|
|
1127
|
+
const { SkillRegistry } = await import('../runtime/skills/index.js');
|
|
1130
1128
|
const registry = SkillRegistry.getInstance();
|
|
1131
1129
|
const success = registry.disable(name);
|
|
1132
1130
|
if (!success) {
|
|
1133
1131
|
await ctx.reply(`Skill "${name}" not found.`);
|
|
1134
1132
|
return;
|
|
1135
1133
|
}
|
|
1136
|
-
updateSkillDelegateDescription();
|
|
1137
1134
|
await ctx.reply(`Skill \`${name}\` disabled.`, { parse_mode: 'Markdown' });
|
|
1138
1135
|
}
|
|
1139
1136
|
catch (err) {
|
|
@@ -15,6 +15,8 @@ import { HttpServer } from '../../http/server.js';
|
|
|
15
15
|
import { getVersion } from '../utils/version.js';
|
|
16
16
|
import { TaskWorker } from '../../runtime/tasks/worker.js';
|
|
17
17
|
import { TaskNotifier } from '../../runtime/tasks/notifier.js';
|
|
18
|
+
import { Link } from '../../runtime/link.js';
|
|
19
|
+
import { LinkWorker } from '../../runtime/link-worker.js';
|
|
18
20
|
export const restartCommand = new Command('restart')
|
|
19
21
|
.description('Restart the Morpheus agent')
|
|
20
22
|
.option('--ui', 'Enable web UI', true)
|
|
@@ -97,6 +99,15 @@ export const restartCommand = new Command('restart')
|
|
|
97
99
|
await clearPid();
|
|
98
100
|
process.exit(1);
|
|
99
101
|
}
|
|
102
|
+
// Initialize Link (Documentation Specialist)
|
|
103
|
+
try {
|
|
104
|
+
const link = Link.getInstance(config);
|
|
105
|
+
await link.initialize();
|
|
106
|
+
display.log(chalk.green('✓ Link initialized'), { source: 'Link' });
|
|
107
|
+
}
|
|
108
|
+
catch (err) {
|
|
109
|
+
display.log(chalk.yellow(`Link initialization warning: ${err.message}`), { source: 'Link' });
|
|
110
|
+
}
|
|
100
111
|
const adapters = [];
|
|
101
112
|
let httpServer;
|
|
102
113
|
const taskWorker = new TaskWorker();
|
|
@@ -135,6 +146,9 @@ export const restartCommand = new Command('restart')
|
|
|
135
146
|
taskWorker.start();
|
|
136
147
|
taskNotifier.start();
|
|
137
148
|
}
|
|
149
|
+
// Start LinkWorker for document indexing
|
|
150
|
+
const linkWorker = LinkWorker.getInstance();
|
|
151
|
+
linkWorker.start();
|
|
138
152
|
// Handle graceful shutdown
|
|
139
153
|
const shutdown = async (signal) => {
|
|
140
154
|
display.stopSpinner();
|
|
@@ -145,6 +159,7 @@ export const restartCommand = new Command('restart')
|
|
|
145
159
|
for (const adapter of adapters) {
|
|
146
160
|
await adapter.disconnect();
|
|
147
161
|
}
|
|
162
|
+
linkWorker.stop();
|
|
148
163
|
if (asyncTasksEnabled) {
|
|
149
164
|
taskWorker.stop();
|
|
150
165
|
taskNotifier.stop();
|
|
@@ -26,6 +26,8 @@ import { ChronosRepository } from '../../runtime/chronos/repository.js';
|
|
|
26
26
|
import { SkillRegistry } from '../../runtime/skills/index.js';
|
|
27
27
|
import { MCPToolCache } from '../../runtime/tools/cache.js';
|
|
28
28
|
import { SmithRegistry } from '../../runtime/smiths/registry.js';
|
|
29
|
+
import { Link } from '../../runtime/link.js';
|
|
30
|
+
import { LinkWorker } from '../../runtime/link-worker.js';
|
|
29
31
|
// Load .env file explicitly in start command
|
|
30
32
|
const envPath = path.join(process.cwd(), '.env');
|
|
31
33
|
if (fs.existsSync(envPath)) {
|
|
@@ -173,6 +175,16 @@ export const startCommand = new Command('start')
|
|
|
173
175
|
catch (err) {
|
|
174
176
|
display.log(chalk.yellow(`Smiths initialization warning: ${err.message}`), { source: 'Smiths' });
|
|
175
177
|
}
|
|
178
|
+
// Initialize Link (Documentation Specialist) before Oracle
|
|
179
|
+
try {
|
|
180
|
+
const linkConfig = ConfigManager.getInstance().getLinkConfig();
|
|
181
|
+
const link = Link.getInstance(config);
|
|
182
|
+
await link.initialize();
|
|
183
|
+
display.log(chalk.green('✓ Link initialized'), { source: 'Link' });
|
|
184
|
+
}
|
|
185
|
+
catch (err) {
|
|
186
|
+
display.log(chalk.yellow(`Link initialization warning: ${err.message}`), { source: 'Link' });
|
|
187
|
+
}
|
|
176
188
|
// Initialize Oracle
|
|
177
189
|
const oracle = new Oracle(config);
|
|
178
190
|
try {
|
|
@@ -259,6 +271,9 @@ export const startCommand = new Command('start')
|
|
|
259
271
|
// Start Background Services
|
|
260
272
|
startSessionEmbeddingScheduler();
|
|
261
273
|
chronosWorker.start();
|
|
274
|
+
// Start LinkWorker for document indexing
|
|
275
|
+
const linkWorker = LinkWorker.getInstance();
|
|
276
|
+
linkWorker.start();
|
|
262
277
|
if (asyncTasksEnabled) {
|
|
263
278
|
taskWorker.start();
|
|
264
279
|
taskNotifier.start();
|
|
@@ -278,6 +293,7 @@ export const startCommand = new Command('start')
|
|
|
278
293
|
await adapter.disconnect();
|
|
279
294
|
}
|
|
280
295
|
chronosWorker.stop();
|
|
296
|
+
linkWorker.stop();
|
|
281
297
|
if (asyncTasksEnabled) {
|
|
282
298
|
taskWorker.stop();
|
|
283
299
|
taskNotifier.stop();
|
package/dist/config/manager.js
CHANGED
|
@@ -53,6 +53,10 @@ export class ConfigManager {
|
|
|
53
53
|
if (decrypted.trinity?.api_key) {
|
|
54
54
|
decrypted.trinity = { ...decrypted.trinity, api_key: tryDecrypt(decrypted.trinity.api_key) };
|
|
55
55
|
}
|
|
56
|
+
// Decrypt Link
|
|
57
|
+
if (decrypted.link?.api_key) {
|
|
58
|
+
decrypted.link = { ...decrypted.link, api_key: tryDecrypt(decrypted.link.api_key) };
|
|
59
|
+
}
|
|
56
60
|
// Decrypt Audio (Telephonist)
|
|
57
61
|
if (decrypted.audio?.apiKey) {
|
|
58
62
|
decrypted.audio = { ...decrypted.audio, apiKey: tryDecrypt(decrypted.audio.apiKey) };
|
|
@@ -95,6 +99,10 @@ export class ConfigManager {
|
|
|
95
99
|
if (encrypted.trinity?.api_key) {
|
|
96
100
|
encrypted.trinity = { ...encrypted.trinity, api_key: tryEncrypt(encrypted.trinity.api_key) };
|
|
97
101
|
}
|
|
102
|
+
// Encrypt Link
|
|
103
|
+
if (encrypted.link?.api_key) {
|
|
104
|
+
encrypted.link = { ...encrypted.link, api_key: tryEncrypt(encrypted.link.api_key) };
|
|
105
|
+
}
|
|
98
106
|
// Encrypt Audio (Telephonist)
|
|
99
107
|
if (encrypted.audio?.apiKey) {
|
|
100
108
|
encrypted.audio = { ...encrypted.audio, apiKey: tryEncrypt(encrypted.audio.apiKey) };
|
|
@@ -240,6 +248,38 @@ export class ConfigManager {
|
|
|
240
248
|
execution_mode: resolveString('MORPHEUS_TRINITY_EXECUTION_MODE', config.trinity?.execution_mode, 'async'),
|
|
241
249
|
};
|
|
242
250
|
}
|
|
251
|
+
// Apply precedence to Link config
|
|
252
|
+
const linkEnvVars = [
|
|
253
|
+
'MORPHEUS_LINK_PROVIDER',
|
|
254
|
+
'MORPHEUS_LINK_MODEL',
|
|
255
|
+
'MORPHEUS_LINK_TEMPERATURE',
|
|
256
|
+
'MORPHEUS_LINK_API_KEY',
|
|
257
|
+
];
|
|
258
|
+
const hasLinkEnvOverrides = linkEnvVars.some((envVar) => process.env[envVar] !== undefined);
|
|
259
|
+
let linkConfig;
|
|
260
|
+
if (config.link || hasLinkEnvOverrides) {
|
|
261
|
+
const linkProvider = resolveProvider('MORPHEUS_LINK_PROVIDER', config.link?.provider, llmConfig.provider);
|
|
262
|
+
const linkMaxTokensFallback = config.link?.max_tokens ?? llmConfig.max_tokens;
|
|
263
|
+
const linkContextWindowFallback = config.link?.context_window ?? llmConfig.context_window;
|
|
264
|
+
linkConfig = {
|
|
265
|
+
provider: linkProvider,
|
|
266
|
+
model: resolveModel(linkProvider, 'MORPHEUS_LINK_MODEL', config.link?.model || llmConfig.model),
|
|
267
|
+
temperature: resolveNumeric('MORPHEUS_LINK_TEMPERATURE', config.link?.temperature, llmConfig.temperature),
|
|
268
|
+
max_tokens: resolveOptionalNumeric('MORPHEUS_LINK_MAX_TOKENS', config.link?.max_tokens, linkMaxTokensFallback),
|
|
269
|
+
api_key: resolveApiKey(linkProvider, 'MORPHEUS_LINK_API_KEY', config.link?.api_key || llmConfig.api_key),
|
|
270
|
+
base_url: config.link?.base_url || config.llm.base_url,
|
|
271
|
+
context_window: resolveOptionalNumeric('MORPHEUS_LINK_CONTEXT_WINDOW', config.link?.context_window, linkContextWindowFallback),
|
|
272
|
+
chunk_size: resolveNumeric('MORPHEUS_LINK_CHUNK_SIZE', config.link?.chunk_size, 500),
|
|
273
|
+
score_threshold: resolveNumeric('MORPHEUS_LINK_SCORE_THRESHOLD', config.link?.score_threshold, 0.5),
|
|
274
|
+
max_results: resolveNumeric('MORPHEUS_LINK_MAX_RESULTS', config.link?.max_results, 10),
|
|
275
|
+
execution_mode: resolveString('MORPHEUS_LINK_EXECUTION_MODE', config.link?.execution_mode, 'async'),
|
|
276
|
+
scan_interval_ms: resolveNumeric('MORPHEUS_LINK_SCAN_INTERVAL_MS', config.link?.scan_interval_ms, 30000),
|
|
277
|
+
max_file_size_mb: resolveNumeric('MORPHEUS_LINK_MAX_FILE_SIZE_MB', config.link?.max_file_size_mb, 50),
|
|
278
|
+
vector_weight: resolveNumeric('MORPHEUS_LINK_VECTOR_WEIGHT', config.link?.vector_weight, 0.8),
|
|
279
|
+
bm25_weight: resolveNumeric('MORPHEUS_LINK_BM25_WEIGHT', config.link?.bm25_weight, 0.2),
|
|
280
|
+
personality: resolveString('MORPHEUS_LINK_PERSONALITY', config.link?.personality, 'documentation_specialist'),
|
|
281
|
+
};
|
|
282
|
+
}
|
|
243
283
|
// Apply precedence to audio config
|
|
244
284
|
const audioProvider = resolveString('MORPHEUS_AUDIO_PROVIDER', config.audio.provider, DEFAULT_CONFIG.audio.provider);
|
|
245
285
|
// AudioProvider uses 'google' but resolveApiKey expects LLMProvider which uses 'gemini'
|
|
@@ -312,6 +352,7 @@ export class ConfigManager {
|
|
|
312
352
|
neo: neoConfig,
|
|
313
353
|
apoc: apocConfig,
|
|
314
354
|
trinity: trinityConfig,
|
|
355
|
+
link: linkConfig,
|
|
315
356
|
audio: audioConfig,
|
|
316
357
|
channels: channelsConfig,
|
|
317
358
|
ui: uiConfig,
|
|
@@ -436,6 +477,26 @@ export class ConfigManager {
|
|
|
436
477
|
}
|
|
437
478
|
return defaults;
|
|
438
479
|
}
|
|
480
|
+
getLinkConfig() {
|
|
481
|
+
const defaults = {
|
|
482
|
+
provider: this.config.llm.provider,
|
|
483
|
+
model: this.config.llm.model,
|
|
484
|
+
temperature: this.config.llm.temperature,
|
|
485
|
+
personality: 'documentation_specialist',
|
|
486
|
+
chunk_size: 500,
|
|
487
|
+
score_threshold: 0.5,
|
|
488
|
+
max_results: 10,
|
|
489
|
+
execution_mode: 'async',
|
|
490
|
+
scan_interval_ms: 30000,
|
|
491
|
+
max_file_size_mb: 50,
|
|
492
|
+
vector_weight: 0.8,
|
|
493
|
+
bm25_weight: 0.2,
|
|
494
|
+
};
|
|
495
|
+
if (this.config.link) {
|
|
496
|
+
return { ...defaults, ...this.config.link };
|
|
497
|
+
}
|
|
498
|
+
return defaults;
|
|
499
|
+
}
|
|
439
500
|
getDevKitConfig() {
|
|
440
501
|
const defaults = {
|
|
441
502
|
sandbox_dir: process.cwd(),
|
package/dist/config/paths.js
CHANGED
package/dist/config/schemas.js
CHANGED
|
@@ -35,8 +35,16 @@ export const NeoConfigSchema = LLMConfigSchema.extend({
|
|
|
35
35
|
export const TrinityConfigSchema = LLMConfigSchema.extend({
|
|
36
36
|
execution_mode: z.enum(['sync', 'async']).default('async'),
|
|
37
37
|
});
|
|
38
|
-
export const
|
|
39
|
-
|
|
38
|
+
export const LinkConfigSchema = LLMConfigSchema.extend({
|
|
39
|
+
personality: z.string().optional(),
|
|
40
|
+
chunk_size: z.number().int().positive().default(500),
|
|
41
|
+
score_threshold: z.number().min(0).max(1).default(0.5),
|
|
42
|
+
max_results: z.number().int().positive().default(10),
|
|
43
|
+
execution_mode: z.enum(['sync', 'async']).default('async'),
|
|
44
|
+
scan_interval_ms: z.number().int().min(5000).default(30000),
|
|
45
|
+
max_file_size_mb: z.number().int().positive().default(50),
|
|
46
|
+
vector_weight: z.number().min(0).max(1).default(0.8),
|
|
47
|
+
bm25_weight: z.number().min(0).max(1).default(0.2),
|
|
40
48
|
});
|
|
41
49
|
export const WebhookConfigSchema = z.object({
|
|
42
50
|
telegram_notify_all: z.boolean().optional(),
|
|
@@ -86,7 +94,7 @@ export const ConfigSchema = z.object({
|
|
|
86
94
|
neo: NeoConfigSchema.optional(),
|
|
87
95
|
apoc: ApocConfigSchema.optional(),
|
|
88
96
|
trinity: TrinityConfigSchema.optional(),
|
|
89
|
-
|
|
97
|
+
link: LinkConfigSchema.optional(),
|
|
90
98
|
webhooks: WebhookConfigSchema,
|
|
91
99
|
audio: AudioConfigSchema.default(DEFAULT_CONFIG.audio),
|
|
92
100
|
memory: z.object({
|
package/dist/http/api.js
CHANGED
|
@@ -21,6 +21,7 @@ import { createChronosJobRouter, createChronosConfigRouter } from './routers/chr
|
|
|
21
21
|
import { createSkillsRouter } from './routers/skills.js';
|
|
22
22
|
import { createSmithsRouter } from './routers/smiths.js';
|
|
23
23
|
import { createDangerRouter } from './routers/danger.js';
|
|
24
|
+
import { createLinkRouter } from './routers/link.js';
|
|
24
25
|
import { getActiveEnvOverrides } from '../config/precedence.js';
|
|
25
26
|
import { hotReloadConfig, getRestartRequiredChanges } from '../runtime/hot-reload.js';
|
|
26
27
|
import { AuditRepository } from '../runtime/audit/repository.js';
|
|
@@ -52,6 +53,8 @@ export function createApiRouter(oracle, chronosWorker) {
|
|
|
52
53
|
router.use('/smiths', createSmithsRouter());
|
|
53
54
|
// Mount Danger Zone router
|
|
54
55
|
router.use('/danger', createDangerRouter());
|
|
56
|
+
// Mount Link router (Documentation management)
|
|
57
|
+
router.use('/link', createLinkRouter());
|
|
55
58
|
// --- Session Management ---
|
|
56
59
|
router.get('/sessions', async (req, res) => {
|
|
57
60
|
try {
|