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.
Files changed (151) hide show
  1. package/README.md +603 -545
  2. package/dist/channels/voice.d.ts +59 -0
  3. package/dist/channels/voice.js +351 -1
  4. package/dist/cli.js +284 -5
  5. package/dist/core/agent.d.ts +9 -0
  6. package/dist/core/agent.js +49 -0
  7. package/dist/core/collaboration.d.ts +89 -0
  8. package/dist/core/collaboration.js +201 -0
  9. package/dist/deploy/index.d.ts +40 -0
  10. package/dist/deploy/index.js +261 -0
  11. package/dist/index.d.ts +7 -1
  12. package/dist/index.js +47 -3
  13. package/dist/mcp/servers/calculator-mcp.d.ts +3 -0
  14. package/dist/mcp/servers/calculator-mcp.js +65 -0
  15. package/dist/mcp/servers/crypto-mcp.d.ts +3 -0
  16. package/dist/mcp/servers/crypto-mcp.js +108 -0
  17. package/dist/mcp/servers/database-mcp.d.ts +3 -0
  18. package/dist/mcp/servers/database-mcp.js +73 -0
  19. package/dist/mcp/servers/datetime-mcp.d.ts +3 -0
  20. package/dist/mcp/servers/datetime-mcp.js +71 -0
  21. package/dist/mcp/servers/filesystem.d.ts +3 -0
  22. package/dist/mcp/servers/filesystem.js +101 -0
  23. package/dist/mcp/servers/github-mcp.d.ts +3 -0
  24. package/dist/mcp/servers/github-mcp.js +60 -0
  25. package/dist/mcp/servers/index.d.ts +21 -0
  26. package/dist/mcp/servers/index.js +50 -0
  27. package/dist/mcp/servers/json-mcp.d.ts +3 -0
  28. package/dist/mcp/servers/json-mcp.js +126 -0
  29. package/dist/mcp/servers/memory-mcp.d.ts +3 -0
  30. package/dist/mcp/servers/memory-mcp.js +60 -0
  31. package/dist/mcp/servers/regex-mcp.d.ts +3 -0
  32. package/dist/mcp/servers/regex-mcp.js +56 -0
  33. package/dist/mcp/servers/web-mcp.d.ts +3 -0
  34. package/dist/mcp/servers/web-mcp.js +51 -0
  35. package/dist/memory/index.d.ts +2 -0
  36. package/dist/memory/index.js +4 -1
  37. package/dist/memory/seed-loader.d.ts +51 -0
  38. package/dist/memory/seed-loader.js +200 -0
  39. package/dist/schema/oad.d.ts +292 -12
  40. package/dist/schema/oad.js +12 -1
  41. package/dist/security/guardrails.d.ts +50 -0
  42. package/dist/security/guardrails.js +197 -0
  43. package/dist/studio/server.d.ts +31 -1
  44. package/dist/studio/server.js +154 -3
  45. package/dist/studio-ui/index.html +1278 -662
  46. package/dist/tools/integrations/calendar.d.ts +3 -0
  47. package/dist/tools/integrations/calendar.js +73 -0
  48. package/dist/tools/integrations/code-exec.d.ts +3 -0
  49. package/dist/tools/integrations/code-exec.js +42 -0
  50. package/dist/tools/integrations/csv-analyzer.d.ts +3 -0
  51. package/dist/tools/integrations/csv-analyzer.js +142 -0
  52. package/dist/tools/integrations/database.d.ts +3 -0
  53. package/dist/tools/integrations/database.js +44 -0
  54. package/dist/tools/integrations/email-send.d.ts +3 -0
  55. package/dist/tools/integrations/email-send.js +104 -0
  56. package/dist/tools/integrations/git-tool.d.ts +3 -0
  57. package/dist/tools/integrations/git-tool.js +49 -0
  58. package/dist/tools/integrations/github-tool.d.ts +3 -0
  59. package/dist/tools/integrations/github-tool.js +77 -0
  60. package/dist/tools/integrations/image-gen.d.ts +3 -0
  61. package/dist/tools/integrations/image-gen.js +58 -0
  62. package/dist/tools/integrations/index.d.ts +30 -0
  63. package/dist/tools/integrations/index.js +107 -0
  64. package/dist/tools/integrations/jira.d.ts +3 -0
  65. package/dist/tools/integrations/jira.js +85 -0
  66. package/dist/tools/integrations/notion.d.ts +3 -0
  67. package/dist/tools/integrations/notion.js +71 -0
  68. package/dist/tools/integrations/npm-tool.d.ts +3 -0
  69. package/dist/tools/integrations/npm-tool.js +49 -0
  70. package/dist/tools/integrations/pdf-reader.d.ts +3 -0
  71. package/dist/tools/integrations/pdf-reader.js +91 -0
  72. package/dist/tools/integrations/slack.d.ts +3 -0
  73. package/dist/tools/integrations/slack.js +67 -0
  74. package/dist/tools/integrations/summarizer.d.ts +3 -0
  75. package/dist/tools/integrations/summarizer.js +49 -0
  76. package/dist/tools/integrations/translator.d.ts +3 -0
  77. package/dist/tools/integrations/translator.js +48 -0
  78. package/dist/tools/integrations/trello.d.ts +3 -0
  79. package/dist/tools/integrations/trello.js +60 -0
  80. package/dist/tools/integrations/vector-search.d.ts +3 -0
  81. package/dist/tools/integrations/vector-search.js +44 -0
  82. package/dist/tools/integrations/web-scraper.d.ts +3 -0
  83. package/dist/tools/integrations/web-scraper.js +48 -0
  84. package/dist/tools/integrations/web-search.d.ts +3 -0
  85. package/dist/tools/integrations/web-search.js +60 -0
  86. package/dist/tools/integrations/webhook.d.ts +3 -0
  87. package/dist/tools/integrations/webhook.js +39 -0
  88. package/dist/ui/components.d.ts +10 -0
  89. package/dist/ui/components.js +123 -0
  90. package/package.json +1 -1
  91. package/src/channels/voice.ts +365 -0
  92. package/src/cli.ts +294 -6
  93. package/src/core/agent.ts +56 -0
  94. package/src/core/collaboration.ts +275 -0
  95. package/src/deploy/index.ts +255 -0
  96. package/src/index.ts +21 -1
  97. package/src/mcp/servers/calculator-mcp.ts +65 -0
  98. package/src/mcp/servers/crypto-mcp.ts +73 -0
  99. package/src/mcp/servers/database-mcp.ts +72 -0
  100. package/src/mcp/servers/datetime-mcp.ts +69 -0
  101. package/src/mcp/servers/filesystem.ts +66 -0
  102. package/src/mcp/servers/github-mcp.ts +58 -0
  103. package/src/mcp/servers/index.ts +63 -0
  104. package/src/mcp/servers/json-mcp.ts +102 -0
  105. package/src/mcp/servers/memory-mcp.ts +56 -0
  106. package/src/mcp/servers/regex-mcp.ts +53 -0
  107. package/src/mcp/servers/web-mcp.ts +49 -0
  108. package/src/memory/index.ts +3 -0
  109. package/src/memory/seed-loader.ts +212 -0
  110. package/src/schema/oad.ts +13 -0
  111. package/src/security/guardrails.ts +248 -0
  112. package/src/studio/server.ts +166 -4
  113. package/src/studio-ui/index.html +1278 -662
  114. package/src/tools/integrations/calendar.ts +73 -0
  115. package/src/tools/integrations/code-exec.ts +39 -0
  116. package/src/tools/integrations/csv-analyzer.ts +92 -0
  117. package/src/tools/integrations/database.ts +44 -0
  118. package/src/tools/integrations/email-send.ts +76 -0
  119. package/src/tools/integrations/git-tool.ts +42 -0
  120. package/src/tools/integrations/github-tool.ts +76 -0
  121. package/src/tools/integrations/image-gen.ts +56 -0
  122. package/src/tools/integrations/index.ts +92 -0
  123. package/src/tools/integrations/jira.ts +83 -0
  124. package/src/tools/integrations/notion.ts +71 -0
  125. package/src/tools/integrations/npm-tool.ts +48 -0
  126. package/src/tools/integrations/pdf-reader.ts +58 -0
  127. package/src/tools/integrations/slack.ts +65 -0
  128. package/src/tools/integrations/summarizer.ts +49 -0
  129. package/src/tools/integrations/translator.ts +48 -0
  130. package/src/tools/integrations/trello.ts +60 -0
  131. package/src/tools/integrations/vector-search.ts +42 -0
  132. package/src/tools/integrations/web-scraper.ts +47 -0
  133. package/src/tools/integrations/web-search.ts +58 -0
  134. package/src/tools/integrations/webhook.ts +38 -0
  135. package/src/ui/components.ts +127 -0
  136. package/tests/brain-seed-extended.test.ts +490 -0
  137. package/tests/brain-seed.test.ts +239 -0
  138. package/tests/collaboration.test.ts +319 -0
  139. package/tests/deploy-and-dag.test.ts +196 -0
  140. package/tests/guardrails.test.ts +177 -0
  141. package/tests/integrations.test.ts +249 -0
  142. package/tests/mcp-servers.test.ts +260 -0
  143. package/tests/voice-enhanced.test.ts +169 -0
  144. package/dist/dtv/data.d.ts +0 -18
  145. package/dist/dtv/data.js +0 -25
  146. package/dist/dtv/trust.d.ts +0 -19
  147. package/dist/dtv/trust.js +0 -40
  148. package/dist/dtv/value.d.ts +0 -23
  149. package/dist/dtv/value.js +0 -38
  150. package/dist/marketplace/index.d.ts +0 -34
  151. 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
- // agent.yaml with role system prompt
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
- .action(async (opts: { file: string; target: string; output?: string; install?: boolean }) => {
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('Show agent memory/brain status from DeepBrain')
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);