opc-agent 2.0.2 → 3.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +603 -545
- package/dist/channels/voice.d.ts +59 -0
- package/dist/channels/voice.js +351 -1
- package/dist/cli.js +284 -5
- package/dist/core/agent.d.ts +9 -0
- package/dist/core/agent.js +49 -0
- package/dist/core/collaboration.d.ts +89 -0
- package/dist/core/collaboration.js +201 -0
- package/dist/deploy/index.d.ts +40 -0
- package/dist/deploy/index.js +261 -0
- package/dist/index.d.ts +7 -1
- package/dist/index.js +47 -3
- package/dist/mcp/servers/calculator-mcp.d.ts +3 -0
- package/dist/mcp/servers/calculator-mcp.js +65 -0
- package/dist/mcp/servers/crypto-mcp.d.ts +3 -0
- package/dist/mcp/servers/crypto-mcp.js +108 -0
- package/dist/mcp/servers/database-mcp.d.ts +3 -0
- package/dist/mcp/servers/database-mcp.js +73 -0
- package/dist/mcp/servers/datetime-mcp.d.ts +3 -0
- package/dist/mcp/servers/datetime-mcp.js +71 -0
- package/dist/mcp/servers/filesystem.d.ts +3 -0
- package/dist/mcp/servers/filesystem.js +101 -0
- package/dist/mcp/servers/github-mcp.d.ts +3 -0
- package/dist/mcp/servers/github-mcp.js +60 -0
- package/dist/mcp/servers/index.d.ts +21 -0
- package/dist/mcp/servers/index.js +50 -0
- package/dist/mcp/servers/json-mcp.d.ts +3 -0
- package/dist/mcp/servers/json-mcp.js +126 -0
- package/dist/mcp/servers/memory-mcp.d.ts +3 -0
- package/dist/mcp/servers/memory-mcp.js +60 -0
- package/dist/mcp/servers/regex-mcp.d.ts +3 -0
- package/dist/mcp/servers/regex-mcp.js +56 -0
- package/dist/mcp/servers/web-mcp.d.ts +3 -0
- package/dist/mcp/servers/web-mcp.js +51 -0
- package/dist/memory/index.d.ts +2 -0
- package/dist/memory/index.js +4 -1
- package/dist/memory/seed-loader.d.ts +51 -0
- package/dist/memory/seed-loader.js +200 -0
- package/dist/schema/oad.d.ts +292 -12
- package/dist/schema/oad.js +12 -1
- package/dist/security/guardrails.d.ts +50 -0
- package/dist/security/guardrails.js +197 -0
- package/dist/studio/server.d.ts +31 -1
- package/dist/studio/server.js +154 -3
- package/dist/studio-ui/index.html +1278 -662
- package/dist/tools/integrations/calendar.d.ts +3 -0
- package/dist/tools/integrations/calendar.js +73 -0
- package/dist/tools/integrations/code-exec.d.ts +3 -0
- package/dist/tools/integrations/code-exec.js +42 -0
- package/dist/tools/integrations/csv-analyzer.d.ts +3 -0
- package/dist/tools/integrations/csv-analyzer.js +142 -0
- package/dist/tools/integrations/database.d.ts +3 -0
- package/dist/tools/integrations/database.js +44 -0
- package/dist/tools/integrations/email-send.d.ts +3 -0
- package/dist/tools/integrations/email-send.js +104 -0
- package/dist/tools/integrations/git-tool.d.ts +3 -0
- package/dist/tools/integrations/git-tool.js +49 -0
- package/dist/tools/integrations/github-tool.d.ts +3 -0
- package/dist/tools/integrations/github-tool.js +77 -0
- package/dist/tools/integrations/image-gen.d.ts +3 -0
- package/dist/tools/integrations/image-gen.js +58 -0
- package/dist/tools/integrations/index.d.ts +30 -0
- package/dist/tools/integrations/index.js +107 -0
- package/dist/tools/integrations/jira.d.ts +3 -0
- package/dist/tools/integrations/jira.js +85 -0
- package/dist/tools/integrations/notion.d.ts +3 -0
- package/dist/tools/integrations/notion.js +71 -0
- package/dist/tools/integrations/npm-tool.d.ts +3 -0
- package/dist/tools/integrations/npm-tool.js +49 -0
- package/dist/tools/integrations/pdf-reader.d.ts +3 -0
- package/dist/tools/integrations/pdf-reader.js +91 -0
- package/dist/tools/integrations/slack.d.ts +3 -0
- package/dist/tools/integrations/slack.js +67 -0
- package/dist/tools/integrations/summarizer.d.ts +3 -0
- package/dist/tools/integrations/summarizer.js +49 -0
- package/dist/tools/integrations/translator.d.ts +3 -0
- package/dist/tools/integrations/translator.js +48 -0
- package/dist/tools/integrations/trello.d.ts +3 -0
- package/dist/tools/integrations/trello.js +60 -0
- package/dist/tools/integrations/vector-search.d.ts +3 -0
- package/dist/tools/integrations/vector-search.js +44 -0
- package/dist/tools/integrations/web-scraper.d.ts +3 -0
- package/dist/tools/integrations/web-scraper.js +48 -0
- package/dist/tools/integrations/web-search.d.ts +3 -0
- package/dist/tools/integrations/web-search.js +60 -0
- package/dist/tools/integrations/webhook.d.ts +3 -0
- package/dist/tools/integrations/webhook.js +39 -0
- package/dist/ui/components.d.ts +10 -0
- package/dist/ui/components.js +123 -0
- package/package.json +1 -1
- package/src/channels/voice.ts +365 -0
- package/src/cli.ts +294 -6
- package/src/core/agent.ts +56 -0
- package/src/core/collaboration.ts +275 -0
- package/src/deploy/index.ts +255 -0
- package/src/index.ts +21 -1
- package/src/mcp/servers/calculator-mcp.ts +65 -0
- package/src/mcp/servers/crypto-mcp.ts +73 -0
- package/src/mcp/servers/database-mcp.ts +72 -0
- package/src/mcp/servers/datetime-mcp.ts +69 -0
- package/src/mcp/servers/filesystem.ts +66 -0
- package/src/mcp/servers/github-mcp.ts +58 -0
- package/src/mcp/servers/index.ts +63 -0
- package/src/mcp/servers/json-mcp.ts +102 -0
- package/src/mcp/servers/memory-mcp.ts +56 -0
- package/src/mcp/servers/regex-mcp.ts +53 -0
- package/src/mcp/servers/web-mcp.ts +49 -0
- package/src/memory/index.ts +3 -0
- package/src/memory/seed-loader.ts +212 -0
- package/src/schema/oad.ts +13 -0
- package/src/security/guardrails.ts +248 -0
- package/src/studio/server.ts +166 -4
- package/src/studio-ui/index.html +1278 -662
- package/src/tools/integrations/calendar.ts +73 -0
- package/src/tools/integrations/code-exec.ts +39 -0
- package/src/tools/integrations/csv-analyzer.ts +92 -0
- package/src/tools/integrations/database.ts +44 -0
- package/src/tools/integrations/email-send.ts +76 -0
- package/src/tools/integrations/git-tool.ts +42 -0
- package/src/tools/integrations/github-tool.ts +76 -0
- package/src/tools/integrations/image-gen.ts +56 -0
- package/src/tools/integrations/index.ts +92 -0
- package/src/tools/integrations/jira.ts +83 -0
- package/src/tools/integrations/notion.ts +71 -0
- package/src/tools/integrations/npm-tool.ts +48 -0
- package/src/tools/integrations/pdf-reader.ts +58 -0
- package/src/tools/integrations/slack.ts +65 -0
- package/src/tools/integrations/summarizer.ts +49 -0
- package/src/tools/integrations/translator.ts +48 -0
- package/src/tools/integrations/trello.ts +60 -0
- package/src/tools/integrations/vector-search.ts +42 -0
- package/src/tools/integrations/web-scraper.ts +47 -0
- package/src/tools/integrations/web-search.ts +58 -0
- package/src/tools/integrations/webhook.ts +38 -0
- package/src/ui/components.ts +127 -0
- package/tests/brain-seed-extended.test.ts +490 -0
- package/tests/brain-seed.test.ts +239 -0
- package/tests/collaboration.test.ts +319 -0
- package/tests/deploy-and-dag.test.ts +196 -0
- package/tests/guardrails.test.ts +177 -0
- package/tests/integrations.test.ts +249 -0
- package/tests/mcp-servers.test.ts +260 -0
- package/tests/voice-enhanced.test.ts +169 -0
- package/dist/dtv/data.d.ts +0 -18
- package/dist/dtv/data.js +0 -25
- package/dist/dtv/trust.d.ts +0 -19
- package/dist/dtv/trust.js +0 -40
- package/dist/dtv/value.d.ts +0 -23
- package/dist/dtv/value.js +0 -38
- package/dist/marketplace/index.d.ts +0 -34
- package/dist/marketplace/index.js +0 -202
package/src/cli.ts
CHANGED
|
@@ -23,6 +23,7 @@ import { AnalyticsEngine } from './core/analytics-engine';
|
|
|
23
23
|
import { runTests, formatReport } from './testing';
|
|
24
24
|
import { deployToOpenClaw } from './deploy/openclaw';
|
|
25
25
|
import { deployToHermes } from './deploy/hermes';
|
|
26
|
+
import { AgentDeployer } from './deploy/index';
|
|
26
27
|
import { WorkflowEngine } from './core/workflow';
|
|
27
28
|
import { VersionManager } from './core/versioning';
|
|
28
29
|
import { createProvider } from './providers';
|
|
@@ -170,11 +171,25 @@ program
|
|
|
170
171
|
fs.mkdirSync(dir, { recursive: true });
|
|
171
172
|
fs.mkdirSync(path.join(dir, 'src', 'skills'), { recursive: true });
|
|
172
173
|
fs.mkdirSync(path.join(dir, 'data'), { recursive: true });
|
|
174
|
+
fs.mkdirSync(path.join(dir, 'brain-seeds'), { recursive: true });
|
|
173
175
|
|
|
174
176
|
// Get system prompt content
|
|
175
177
|
const systemPromptContent = roleData.files['system-prompt.md'] || roleData.files['prompts/system.md'] || '';
|
|
176
178
|
|
|
177
|
-
//
|
|
179
|
+
// Generate brain-seeds/ files from role data
|
|
180
|
+
const brainSeedContent = roleData.files['brain-seed.md'] || '';
|
|
181
|
+
const industryMatch = brainSeedContent ? brainSeedContent.match(/# Industry Knowledge[\s\S]*?(?=# Job Knowledge|# Workstation Knowledge|$)/i) : null;
|
|
182
|
+
const jobMatch = brainSeedContent ? brainSeedContent.match(/# Job Knowledge[\s\S]*?(?=# Industry Knowledge|# Workstation Knowledge|$)/i) : null;
|
|
183
|
+
const workstationMatch = brainSeedContent ? brainSeedContent.match(/# Workstation Knowledge[\s\S]*?(?=# Industry Knowledge|# Job Knowledge|$)/i) : null;
|
|
184
|
+
|
|
185
|
+
fs.writeFileSync(path.join(dir, 'brain-seeds', 'industry.md'), industryMatch?.[0]?.trim() || `# Industry Knowledge\n\n## Overview\n\nAdd industry-specific knowledge for your domain.\n`);
|
|
186
|
+
fs.writeFileSync(path.join(dir, 'brain-seeds', 'job.md'), jobMatch?.[0]?.trim() || `# Job Knowledge\n\n## Core Skills\n\nAdd role-specific knowledge for ${roleDisplayName}.\n`);
|
|
187
|
+
// workstation.md: public workstation knowledge (tools, workflows, best practices)
|
|
188
|
+
// Company-specific knowledge belongs to Desk (closed-source), not here.
|
|
189
|
+
const workstationSeedFromRole = workstationMatch?.[0]?.trim() || '';
|
|
190
|
+
fs.writeFileSync(path.join(dir, 'brain-seeds', 'workstation.md'), workstationSeedFromRole || `# Workstation Knowledge\n\n## Tools & Environment\n\nCommon tools and setup for this workstation role.\n\n## Workflows\n\nStandard operating procedures and workflows.\n\n## Best Practices\n\nIndustry best practices for this role.\n`);
|
|
191
|
+
|
|
192
|
+
// agent.yaml with role system prompt and brain seeds
|
|
178
193
|
const firstLine = systemPromptContent.split('\n').find((l: string) => l.trim() && !l.startsWith('#'))?.trim() || 'You are a helpful AI assistant.';
|
|
179
194
|
fs.writeFileSync(
|
|
180
195
|
path.join(dir, 'agent.yaml'),
|
|
@@ -198,6 +213,15 @@ spec:
|
|
|
198
213
|
longTerm:
|
|
199
214
|
provider: deepbrain
|
|
200
215
|
database: ./data/brain.db
|
|
216
|
+
brain:
|
|
217
|
+
seeds:
|
|
218
|
+
- brain-seeds/industry.md
|
|
219
|
+
- brain-seeds/job.md
|
|
220
|
+
- brain-seeds/workstation.md
|
|
221
|
+
autoSeed: true
|
|
222
|
+
evolve:
|
|
223
|
+
enabled: true
|
|
224
|
+
direction: bottom-up
|
|
201
225
|
skills: []
|
|
202
226
|
`,
|
|
203
227
|
);
|
|
@@ -317,8 +341,12 @@ export class EchoSkill extends BaseSkill {
|
|
|
317
341
|
console.log(` ${icon.file} agent.yaml - Agent definition with role system prompt`);
|
|
318
342
|
console.log(` ${icon.file} SOUL.md - Role personality (${systemPromptContent.split('\n').length} lines)`);
|
|
319
343
|
console.log(` ${icon.file} CONTEXT.md - Role context & documentation`);
|
|
344
|
+
console.log(` ${icon.file} brain-seeds/ - 3-tier brain seed knowledge`);
|
|
345
|
+
console.log(` ${color.dim('├')} industry.md - Industry knowledge`);
|
|
346
|
+
console.log(` ${color.dim('├')} job.md - Job/role knowledge`);
|
|
347
|
+
console.log(` ${color.dim('└')} workstation.md - Workstation knowledge`);
|
|
320
348
|
if (roleData.files['brain-seed.md']) {
|
|
321
|
-
console.log(` ${icon.file} data/brain-seed.md - Role brain seed knowledge`);
|
|
349
|
+
console.log(` ${icon.file} data/brain-seed.md - Role brain seed knowledge (legacy)`);
|
|
322
350
|
}
|
|
323
351
|
console.log(` ${icon.file} src/index.ts - Entry point`);
|
|
324
352
|
console.log(` ${icon.file} package.json - Dependencies`);
|
|
@@ -1063,9 +1091,68 @@ program
|
|
|
1063
1091
|
.option('-t, --target <target>', 'Deploy target', 'openclaw')
|
|
1064
1092
|
.option('-o, --output <dir>', 'Output directory')
|
|
1065
1093
|
.option('--install', 'Also register in OpenClaw config')
|
|
1066
|
-
.
|
|
1094
|
+
.option('--docker', 'Generate Dockerfile + docker-compose.yml')
|
|
1095
|
+
.option('--railway', 'Deploy to Railway')
|
|
1096
|
+
.option('--fly', 'Deploy to Fly.io')
|
|
1097
|
+
.option('--local', 'Deploy locally via Docker Compose')
|
|
1098
|
+
.option('-p, --port <port>', 'Port number', '3000')
|
|
1099
|
+
.option('--replicas <n>', 'Number of replicas', '1')
|
|
1100
|
+
.action(async (opts: { file: string; target: string; output?: string; install?: boolean; docker?: boolean; railway?: boolean; fly?: boolean; local?: boolean; port: string; replicas: string }) => {
|
|
1101
|
+
const deployer = new AgentDeployer();
|
|
1102
|
+
const agentDir = path.resolve(opts.output || '.');
|
|
1103
|
+
|
|
1104
|
+
// New deploy modes
|
|
1105
|
+
if (opts.docker) {
|
|
1106
|
+
console.log(`\n${icon.rocket} ${color.bold('Generating Docker deployment files')}\n`);
|
|
1107
|
+
const result = await deployer.generateFiles(agentDir, { port: parseInt(opts.port), replicas: parseInt(opts.replicas) });
|
|
1108
|
+
console.log(`${icon.success} ${result.message}`);
|
|
1109
|
+
for (const f of (result.files || [])) console.log(` ${icon.file} ${f}`);
|
|
1110
|
+
console.log();
|
|
1111
|
+
return;
|
|
1112
|
+
}
|
|
1113
|
+
|
|
1114
|
+
if (opts.railway) {
|
|
1115
|
+
console.log(`\n${icon.rocket} ${color.bold('Deploying to Railway')}\n`);
|
|
1116
|
+
const result = await deployer.deployRailway(agentDir);
|
|
1117
|
+
if (result.success) {
|
|
1118
|
+
console.log(`${icon.success} ${result.message}`);
|
|
1119
|
+
if (result.url) console.log(` URL: ${color.cyan(result.url)}`);
|
|
1120
|
+
} else {
|
|
1121
|
+
console.error(`${icon.error} ${result.message}`);
|
|
1122
|
+
process.exit(1);
|
|
1123
|
+
}
|
|
1124
|
+
return;
|
|
1125
|
+
}
|
|
1126
|
+
|
|
1127
|
+
if (opts.fly) {
|
|
1128
|
+
console.log(`\n${icon.rocket} ${color.bold('Deploying to Fly.io')}\n`);
|
|
1129
|
+
const result = await deployer.deployFly(agentDir);
|
|
1130
|
+
if (result.success) {
|
|
1131
|
+
console.log(`${icon.success} ${result.message}`);
|
|
1132
|
+
if (result.url) console.log(` URL: ${color.cyan(result.url)}`);
|
|
1133
|
+
} else {
|
|
1134
|
+
console.error(`${icon.error} ${result.message}`);
|
|
1135
|
+
process.exit(1);
|
|
1136
|
+
}
|
|
1137
|
+
return;
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
if (opts.local) {
|
|
1141
|
+
console.log(`\n${icon.rocket} ${color.bold('Deploying locally via Docker')}\n`);
|
|
1142
|
+
const result = await deployer.deployLocal(agentDir, { port: parseInt(opts.port), replicas: parseInt(opts.replicas) });
|
|
1143
|
+
if (result.success) {
|
|
1144
|
+
console.log(`${icon.success} ${result.message}`);
|
|
1145
|
+
if (result.url) console.log(` URL: ${color.cyan(result.url)}`);
|
|
1146
|
+
} else {
|
|
1147
|
+
console.error(`${icon.error} ${result.message}`);
|
|
1148
|
+
process.exit(1);
|
|
1149
|
+
}
|
|
1150
|
+
return;
|
|
1151
|
+
}
|
|
1152
|
+
|
|
1153
|
+
// Legacy deploy modes
|
|
1067
1154
|
if (opts.target !== 'openclaw' && opts.target !== 'hermes') {
|
|
1068
|
-
console.error(`${icon.error} Unknown target: ${color.bold(opts.target)}. Supported: openclaw, hermes`);
|
|
1155
|
+
console.error(`${icon.error} Unknown target: ${color.bold(opts.target)}. Supported: openclaw, hermes, --docker, --railway, --fly, --local`);
|
|
1069
1156
|
process.exit(1);
|
|
1070
1157
|
}
|
|
1071
1158
|
try {
|
|
@@ -1516,9 +1603,13 @@ program
|
|
|
1516
1603
|
|
|
1517
1604
|
// ── Brain command ────────────────────────────────────────────
|
|
1518
1605
|
|
|
1519
|
-
program
|
|
1606
|
+
const brainCmd = program
|
|
1520
1607
|
.command('brain')
|
|
1521
|
-
.description('
|
|
1608
|
+
.description('Manage agent brain (memory, seeds, evolve)');
|
|
1609
|
+
|
|
1610
|
+
brainCmd
|
|
1611
|
+
.command('status')
|
|
1612
|
+
.description('Show brain stats (pages, tiers, last evolve)')
|
|
1522
1613
|
.option('--url <url>', 'DeepBrain server URL', 'http://localhost:3333')
|
|
1523
1614
|
.action(async (opts: { url: string }) => {
|
|
1524
1615
|
console.log(`\n${icon.gear} ${color.bold('DeepBrain Status')} — ${color.dim(opts.url)}\n`);
|
|
@@ -1549,6 +1640,89 @@ program
|
|
|
1549
1640
|
}
|
|
1550
1641
|
});
|
|
1551
1642
|
|
|
1643
|
+
brainCmd
|
|
1644
|
+
.command('seed')
|
|
1645
|
+
.description('Import brain seed files into memory')
|
|
1646
|
+
.option('-f, --file <file>', 'OAD file', 'agent.yaml')
|
|
1647
|
+
.option('--status', 'Check if seeds have been imported')
|
|
1648
|
+
.option('--reset', 'Re-import seeds (clear marker and re-seed)')
|
|
1649
|
+
.action(async (opts: { file: string; status?: boolean; reset?: boolean }) => {
|
|
1650
|
+
const { BrainSeedLoader } = require('./memory/seed-loader');
|
|
1651
|
+
let config: any = {};
|
|
1652
|
+
try { config = yaml.load(fs.readFileSync(opts.file, 'utf-8')) as any; } catch { /* ignore */ }
|
|
1653
|
+
const brainConfig = config?.spec?.brain;
|
|
1654
|
+
if (!brainConfig?.seeds?.length) {
|
|
1655
|
+
console.log(`${icon.info} No brain seeds configured in ${opts.file}.`);
|
|
1656
|
+
console.log(` Add spec.brain.seeds to your agent.yaml.`);
|
|
1657
|
+
return;
|
|
1658
|
+
}
|
|
1659
|
+
|
|
1660
|
+
const loader = new BrainSeedLoader(process.cwd(), {
|
|
1661
|
+
seeds: brainConfig.seeds,
|
|
1662
|
+
autoSeed: brainConfig.autoSeed !== false,
|
|
1663
|
+
});
|
|
1664
|
+
|
|
1665
|
+
if (opts.status) {
|
|
1666
|
+
const seeded = await loader.isSeeded();
|
|
1667
|
+
console.log(`\n Brain seed status: ${seeded ? color.green('seeded ✔') : color.yellow('not seeded')}`);
|
|
1668
|
+
console.log(` Seeds configured: ${brainConfig.seeds.map((s: string) => color.cyan(s)).join(', ')}\n`);
|
|
1669
|
+
return;
|
|
1670
|
+
}
|
|
1671
|
+
|
|
1672
|
+
if (opts.reset) {
|
|
1673
|
+
const markerPath = path.resolve(process.cwd(), '.brain-seeded');
|
|
1674
|
+
if (fs.existsSync(markerPath)) {
|
|
1675
|
+
fs.unlinkSync(markerPath);
|
|
1676
|
+
console.log(` ${icon.success} Cleared seed marker.`);
|
|
1677
|
+
}
|
|
1678
|
+
}
|
|
1679
|
+
|
|
1680
|
+
if (await loader.isSeeded() && !opts.reset) {
|
|
1681
|
+
console.log(`${icon.info} Brain already seeded. Use --reset to re-import.`);
|
|
1682
|
+
return;
|
|
1683
|
+
}
|
|
1684
|
+
|
|
1685
|
+
console.log(`\n${icon.gear} Importing brain seeds...\n`);
|
|
1686
|
+
// Use a simple mock brain that logs imports (real usage would connect to DeepBrain)
|
|
1687
|
+
const pages: string[] = [];
|
|
1688
|
+
const mockBrain = {
|
|
1689
|
+
learn: async (content: string, meta: any) => { pages.push(meta?.slug || 'unknown'); },
|
|
1690
|
+
};
|
|
1691
|
+
const result = await loader.seedBrain(mockBrain);
|
|
1692
|
+
await loader.markSeeded();
|
|
1693
|
+
|
|
1694
|
+
console.log(` ${icon.success} Imported ${color.bold(String(result.imported))} pages from ${brainConfig.seeds.length} seed files.`);
|
|
1695
|
+
for (const p of result.pages) {
|
|
1696
|
+
console.log(` ${color.dim('•')} ${p}`);
|
|
1697
|
+
}
|
|
1698
|
+
console.log();
|
|
1699
|
+
});
|
|
1700
|
+
|
|
1701
|
+
brainCmd
|
|
1702
|
+
.command('evolve')
|
|
1703
|
+
.description('Trigger manual knowledge evolution cycle')
|
|
1704
|
+
.option('--dry-run', 'Show what would be promoted without doing it')
|
|
1705
|
+
.action(async (opts: { dryRun?: boolean }) => {
|
|
1706
|
+
const { KnowledgeEvolver } = require('./memory/seed-loader');
|
|
1707
|
+
const evolver = new KnowledgeEvolver();
|
|
1708
|
+
console.log(`\n${icon.gear} ${color.bold('Knowledge Evolution')}\n`);
|
|
1709
|
+
console.log(` ${icon.info} Checking for promotion candidates...`);
|
|
1710
|
+
// Would connect to real brain in production
|
|
1711
|
+
const result = await evolver.checkPromotion(null);
|
|
1712
|
+
if (result.candidates.length === 0) {
|
|
1713
|
+
console.log(` ${icon.info} No knowledge ready for promotion yet.\n`);
|
|
1714
|
+
} else {
|
|
1715
|
+
for (const c of result.candidates) {
|
|
1716
|
+
console.log(` ${color.cyan(c.slug)} → ${c.fromTier} → ${c.toTier} (confidence: ${(c.confidence * 100).toFixed(0)}%)`);
|
|
1717
|
+
}
|
|
1718
|
+
if (opts.dryRun) {
|
|
1719
|
+
console.log(`\n ${icon.info} Dry run — no changes made.\n`);
|
|
1720
|
+
} else {
|
|
1721
|
+
console.log(`\n ${icon.success} Promoted ${result.promoted} knowledge entries.\n`);
|
|
1722
|
+
}
|
|
1723
|
+
}
|
|
1724
|
+
});
|
|
1725
|
+
|
|
1552
1726
|
// ── Logs command ─────────────────────────────────────────────
|
|
1553
1727
|
|
|
1554
1728
|
program
|
|
@@ -1989,6 +2163,86 @@ program
|
|
|
1989
2163
|
console.log(`\n${color.bold('Summary:')} ${allPassed}/${allTotal} passed (${allTotal ? Math.round(allPassed / allTotal * 100) : 0}%)`);
|
|
1990
2164
|
});
|
|
1991
2165
|
|
|
2166
|
+
// ── Guardrails command ────────────────────────────────────────
|
|
2167
|
+
|
|
2168
|
+
const guardrailsCmd = program.command('guardrails').description('Guardrail utilities');
|
|
2169
|
+
|
|
2170
|
+
guardrailsCmd
|
|
2171
|
+
.command('test <message>')
|
|
2172
|
+
.description('Test guardrails against a message')
|
|
2173
|
+
.option('-c, --config <file>', 'OAD config file with guardrails')
|
|
2174
|
+
.action(async (message: string, opts: any) => {
|
|
2175
|
+
const { GuardrailManager, createGuardrailsFromConfig } = await import('./security/guardrails');
|
|
2176
|
+
|
|
2177
|
+
let manager: InstanceType<typeof GuardrailManager>;
|
|
2178
|
+
if (opts.config) {
|
|
2179
|
+
const raw = fs.readFileSync(opts.config, 'utf-8');
|
|
2180
|
+
const doc = yaml.load(raw) as any;
|
|
2181
|
+
manager = createGuardrailsFromConfig(doc.spec?.guardrails ?? {});
|
|
2182
|
+
} else {
|
|
2183
|
+
// Default: all built-in rules
|
|
2184
|
+
manager = new GuardrailManager({
|
|
2185
|
+
input: [
|
|
2186
|
+
{ name: 'pii-detector', type: 'regex', action: 'redact' },
|
|
2187
|
+
{ name: 'prompt-injection', type: 'keyword', action: 'block' },
|
|
2188
|
+
{ name: 'toxicity', type: 'keyword', action: 'block' },
|
|
2189
|
+
{ name: 'compliance-filter', type: 'keyword', action: 'block' },
|
|
2190
|
+
],
|
|
2191
|
+
output: [],
|
|
2192
|
+
});
|
|
2193
|
+
}
|
|
2194
|
+
|
|
2195
|
+
console.log(color.bold('Testing guardrails against:'), message);
|
|
2196
|
+
console.log();
|
|
2197
|
+
|
|
2198
|
+
const result = await manager.checkInput(message);
|
|
2199
|
+
if (result.passed) {
|
|
2200
|
+
console.log(color.green('✓ PASSED — no violations'));
|
|
2201
|
+
} else {
|
|
2202
|
+
if (result.blocked) console.log(color.red('✗ BLOCKED'));
|
|
2203
|
+
if (result.warned) console.log(color.yellow('⚠ WARNING'));
|
|
2204
|
+
if (result.redacted) {
|
|
2205
|
+
console.log(color.yellow('✎ REDACTED'));
|
|
2206
|
+
console.log(' Redacted text:', result.redactedText);
|
|
2207
|
+
}
|
|
2208
|
+
for (const v of result.violations) {
|
|
2209
|
+
console.log(` [${v.action}] ${v.rule}: ${v.detail}`);
|
|
2210
|
+
}
|
|
2211
|
+
}
|
|
2212
|
+
});
|
|
2213
|
+
|
|
2214
|
+
// ── Voice command ─────────────────────────────────────────────
|
|
2215
|
+
|
|
2216
|
+
program
|
|
2217
|
+
.command('voice')
|
|
2218
|
+
.description('Voice conversation utilities')
|
|
2219
|
+
.command('start')
|
|
2220
|
+
.description('Start voice conversation (requires STT/TTS providers)')
|
|
2221
|
+
.option('--stt <provider>', 'STT provider: whisper, deepgram', 'whisper')
|
|
2222
|
+
.option('--tts <provider>', 'TTS provider: edge-tts, openai-tts, elevenlabs', 'edge-tts')
|
|
2223
|
+
.option('--voice <name>', 'Voice name/id')
|
|
2224
|
+
.option('--language <lang>', 'Language code', 'en')
|
|
2225
|
+
.action(async (opts: any) => {
|
|
2226
|
+
console.log(color.bold('🎤 Voice Conversation Mode'));
|
|
2227
|
+
console.log(` STT: ${opts.stt} | TTS: ${opts.tts} | Voice: ${opts.voice ?? 'default'} | Language: ${opts.language}`);
|
|
2228
|
+
console.log(color.dim(' (Voice conversation requires audio input integration — use as library)'));
|
|
2229
|
+
console.log();
|
|
2230
|
+
console.log('To use voice in your agent:');
|
|
2231
|
+
console.log(color.cyan(`
|
|
2232
|
+
import { VoiceChannel, createVoiceProviders } from 'opc-agent';
|
|
2233
|
+
|
|
2234
|
+
const { stt, tts } = createVoiceProviders({
|
|
2235
|
+
sttProvider: '${opts.stt}',
|
|
2236
|
+
ttsProvider: '${opts.tts}',
|
|
2237
|
+
voice: '${opts.voice ?? 'en-US-AriaNeural'}',
|
|
2238
|
+
language: '${opts.language}',
|
|
2239
|
+
});
|
|
2240
|
+
|
|
2241
|
+
const voice = new VoiceChannel({ sttProvider: stt, ttsProvider: tts });
|
|
2242
|
+
await voice.start();
|
|
2243
|
+
`));
|
|
2244
|
+
});
|
|
2245
|
+
|
|
1992
2246
|
program.parse();
|
|
1993
2247
|
|
|
1994
2248
|
// ── Keys command ──────────────────────────────────────────────
|
|
@@ -2279,3 +2533,37 @@ mcpCmd
|
|
|
2279
2533
|
}
|
|
2280
2534
|
console.log();
|
|
2281
2535
|
});
|
|
2536
|
+
|
|
2537
|
+
mcpCmd
|
|
2538
|
+
.command('list')
|
|
2539
|
+
.description('List available pre-built MCP servers')
|
|
2540
|
+
.action(() => {
|
|
2541
|
+
const { listMCPServers } = require('./mcp/servers');
|
|
2542
|
+
const servers = listMCPServers();
|
|
2543
|
+
console.log(`\n${icon.gear} Available MCP Servers:\n`);
|
|
2544
|
+
for (const s of servers) {
|
|
2545
|
+
console.log(` ${color.green(s.name.padEnd(14))} ${s.description} ${color.dim(`(${s.toolCount} tools, v${s.version})`)}`);
|
|
2546
|
+
}
|
|
2547
|
+
console.log(`\n Total: ${servers.length} servers\n`);
|
|
2548
|
+
});
|
|
2549
|
+
|
|
2550
|
+
mcpCmd
|
|
2551
|
+
.command('start')
|
|
2552
|
+
.argument('<name>', 'Server name (e.g. filesystem, github, calculator)')
|
|
2553
|
+
.option('--port <port>', 'Start in HTTP+SSE mode on given port')
|
|
2554
|
+
.description('Start a pre-built MCP server (stdio by default)')
|
|
2555
|
+
.action(async (name: string, opts: { port?: string }) => {
|
|
2556
|
+
const { getMCPServer } = require('./mcp/servers');
|
|
2557
|
+
const { MCPServer } = require('./protocols/mcp');
|
|
2558
|
+
const config = getMCPServer(name);
|
|
2559
|
+
const server = new MCPServer(config);
|
|
2560
|
+
if (opts.port) {
|
|
2561
|
+
const port = parseInt(opts.port) || 3100;
|
|
2562
|
+
await server.serveHTTP(port);
|
|
2563
|
+
console.log(`${icon.success} MCP server ${color.cyan(name)} running on http://localhost:${port}`);
|
|
2564
|
+
console.log(`${icon.info} Tools: ${server.getToolCount()}`);
|
|
2565
|
+
} else {
|
|
2566
|
+
console.error(`${icon.success} MCP server ${color.cyan(name)} (stdio) — ${server.getToolCount()} tools`);
|
|
2567
|
+
await server.serveStdio();
|
|
2568
|
+
}
|
|
2569
|
+
});
|
package/src/core/agent.ts
CHANGED
|
@@ -8,6 +8,8 @@ import { MCPToolRegistry } from '../tools/mcp';
|
|
|
8
8
|
import { SubAgentManager, type SubAgentConfig, type SubAgentResult } from './subagent';
|
|
9
9
|
import { Tracer } from '../telemetry';
|
|
10
10
|
import type { Span as TelemetrySpan } from '../telemetry';
|
|
11
|
+
import { BrainSeedLoader, type BrainSeedConfig } from '../memory/seed-loader';
|
|
12
|
+
import { GuardrailManager, type GuardrailConfig } from '../security/guardrails';
|
|
11
13
|
|
|
12
14
|
export class BaseAgent extends EventEmitter implements IAgent {
|
|
13
15
|
readonly name: string;
|
|
@@ -26,6 +28,9 @@ export class BaseAgent extends EventEmitter implements IAgent {
|
|
|
26
28
|
private longTermMemory?: any;
|
|
27
29
|
private longTermMemoryConfig: { autoLearn: boolean; autoRecall: boolean } = { autoLearn: true, autoRecall: true };
|
|
28
30
|
private tracer?: Tracer;
|
|
31
|
+
private brainSeedConfig?: BrainSeedConfig;
|
|
32
|
+
private agentDir: string;
|
|
33
|
+
private guardrails?: GuardrailManager;
|
|
29
34
|
|
|
30
35
|
constructor(options: {
|
|
31
36
|
name: string;
|
|
@@ -42,6 +47,8 @@ export class BaseAgent extends EventEmitter implements IAgent {
|
|
|
42
47
|
};
|
|
43
48
|
maxToolRounds?: number;
|
|
44
49
|
tracer?: Tracer;
|
|
50
|
+
agentDir?: string;
|
|
51
|
+
brainSeedConfig?: BrainSeedConfig;
|
|
45
52
|
}) {
|
|
46
53
|
super();
|
|
47
54
|
this.name = options.name;
|
|
@@ -59,6 +66,8 @@ export class BaseAgent extends EventEmitter implements IAgent {
|
|
|
59
66
|
this.skillLearner = new SkillLearner(options.skillsDir);
|
|
60
67
|
}
|
|
61
68
|
this.tracer = options.tracer;
|
|
69
|
+
this.agentDir = options.agentDir ?? process.cwd();
|
|
70
|
+
this.brainSeedConfig = options.brainSeedConfig;
|
|
62
71
|
}
|
|
63
72
|
|
|
64
73
|
setLongTermMemory(brain: any, config?: { autoLearn?: boolean; autoRecall?: boolean }): void {
|
|
@@ -71,6 +80,14 @@ export class BaseAgent extends EventEmitter implements IAgent {
|
|
|
71
80
|
}
|
|
72
81
|
}
|
|
73
82
|
|
|
83
|
+
setGuardrails(config: GuardrailConfig): void {
|
|
84
|
+
this.guardrails = new GuardrailManager(config);
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
getGuardrails(): GuardrailManager | undefined {
|
|
88
|
+
return this.guardrails;
|
|
89
|
+
}
|
|
90
|
+
|
|
74
91
|
getLongTermMemory(): any {
|
|
75
92
|
return this.longTermMemory;
|
|
76
93
|
}
|
|
@@ -125,6 +142,17 @@ export class BaseAgent extends EventEmitter implements IAgent {
|
|
|
125
142
|
if (this.skillLearner) {
|
|
126
143
|
await this.skillLearner.loadLearnedSkills();
|
|
127
144
|
}
|
|
145
|
+
|
|
146
|
+
// Auto-seed brain if configured
|
|
147
|
+
if (this.brainSeedConfig?.autoSeed && this.longTermMemory) {
|
|
148
|
+
const loader = new BrainSeedLoader(this.agentDir, this.brainSeedConfig);
|
|
149
|
+
if (!await loader.isSeeded()) {
|
|
150
|
+
const result = await loader.seedBrain(this.longTermMemory);
|
|
151
|
+
this.emit('brain:seeded', result);
|
|
152
|
+
await loader.markSeeded();
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
|
|
128
156
|
this.transition('ready');
|
|
129
157
|
}
|
|
130
158
|
|
|
@@ -193,6 +221,24 @@ export class BaseAgent extends EventEmitter implements IAgent {
|
|
|
193
221
|
const sessionId = (message.metadata?.sessionId as string) ?? 'default';
|
|
194
222
|
await this.memory.addMessage(sessionId, message);
|
|
195
223
|
|
|
224
|
+
// === Guardrails: check input ===
|
|
225
|
+
if (this.guardrails) {
|
|
226
|
+
const inputCheck = await this.guardrails.checkInput(message.content);
|
|
227
|
+
if (inputCheck.blocked) {
|
|
228
|
+
const blockedResponse = this.createResponse(inputCheck.message ?? 'Message blocked by guardrails.', message);
|
|
229
|
+
await this.memory.addMessage(sessionId, blockedResponse);
|
|
230
|
+
this.emit('message:out', blockedResponse);
|
|
231
|
+
if (rootSpan && this.tracer) {
|
|
232
|
+
this.tracer.addEvent(rootSpan, 'guardrail.blocked', { rule: inputCheck.violations[0]?.rule ?? 'unknown' });
|
|
233
|
+
this.tracer.endSpan(rootSpan, 'ok');
|
|
234
|
+
}
|
|
235
|
+
return blockedResponse;
|
|
236
|
+
}
|
|
237
|
+
if (inputCheck.redacted && inputCheck.redactedText) {
|
|
238
|
+
message = { ...message, content: inputCheck.redactedText };
|
|
239
|
+
}
|
|
240
|
+
}
|
|
241
|
+
|
|
196
242
|
// === Recall from long-term memory ===
|
|
197
243
|
let memoryContext = '';
|
|
198
244
|
if (this.longTermMemory && this.longTermMemoryConfig.autoRecall) {
|
|
@@ -323,6 +369,16 @@ export class BaseAgent extends EventEmitter implements IAgent {
|
|
|
323
369
|
});
|
|
324
370
|
}
|
|
325
371
|
|
|
372
|
+
// === Guardrails: check output ===
|
|
373
|
+
if (this.guardrails) {
|
|
374
|
+
const outputCheck = await this.guardrails.checkOutput(finalResponse);
|
|
375
|
+
if (outputCheck.blocked) {
|
|
376
|
+
finalResponse = outputCheck.message ?? 'Response blocked by guardrails.';
|
|
377
|
+
} else if (outputCheck.redacted && outputCheck.redactedText) {
|
|
378
|
+
finalResponse = outputCheck.redactedText;
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
326
382
|
const response = this.createResponse(finalResponse, message);
|
|
327
383
|
await this.memory.addMessage(sessionId, response);
|
|
328
384
|
this.emit('message:out', response);
|