opc-agent 1.2.0 → 1.2.1

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 (155) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/CONTRIBUTING.md +75 -75
  3. package/README.md +429 -429
  4. package/README.zh-CN.md +415 -415
  5. package/dist/channels/web.js +256 -256
  6. package/dist/core/knowledge.d.ts +0 -5
  7. package/dist/core/knowledge.js +2 -39
  8. package/dist/core/streaming.d.ts +56 -0
  9. package/dist/core/streaming.js +160 -0
  10. package/dist/deploy/hermes.js +22 -22
  11. package/dist/deploy/openclaw.js +31 -31
  12. package/dist/index.d.ts +4 -0
  13. package/dist/index.js +7 -1
  14. package/dist/providers/index.d.ts +1 -1
  15. package/dist/providers/index.js +14 -158
  16. package/dist/schema/oad.d.ts +3 -3
  17. package/dist/templates/code-reviewer.js +5 -5
  18. package/dist/templates/customer-service.js +2 -2
  19. package/dist/templates/data-analyst.js +5 -5
  20. package/dist/templates/knowledge-base.js +2 -2
  21. package/dist/templates/sales-assistant.js +4 -4
  22. package/dist/templates/teacher.js +6 -6
  23. package/dist/tools/gateway.d.ts +28 -0
  24. package/dist/tools/gateway.js +177 -0
  25. package/docs/.vitepress/config.ts +103 -103
  26. package/docs/api/cli.md +48 -48
  27. package/docs/api/oad-schema.md +64 -64
  28. package/docs/api/sdk.md +80 -80
  29. package/docs/guide/concepts.md +51 -51
  30. package/docs/guide/configuration.md +79 -79
  31. package/docs/guide/deployment.md +42 -42
  32. package/docs/guide/getting-started.md +44 -44
  33. package/docs/guide/templates.md +28 -28
  34. package/docs/guide/testing.md +84 -84
  35. package/docs/index.md +27 -27
  36. package/docs/zh/api/cli.md +54 -54
  37. package/docs/zh/api/oad-schema.md +87 -87
  38. package/docs/zh/api/sdk.md +102 -102
  39. package/docs/zh/guide/concepts.md +104 -104
  40. package/docs/zh/guide/configuration.md +135 -135
  41. package/docs/zh/guide/deployment.md +81 -81
  42. package/docs/zh/guide/getting-started.md +82 -82
  43. package/docs/zh/guide/templates.md +84 -84
  44. package/docs/zh/guide/testing.md +88 -88
  45. package/docs/zh/index.md +27 -27
  46. package/examples/customer-service-demo/README.md +90 -90
  47. package/examples/customer-service-demo/oad.yaml +107 -107
  48. package/package.json +50 -50
  49. package/src/analytics/index.ts +66 -66
  50. package/src/channels/discord.ts +192 -192
  51. package/src/channels/email.ts +177 -177
  52. package/src/channels/feishu.ts +236 -236
  53. package/src/channels/index.ts +15 -15
  54. package/src/channels/slack.ts +160 -160
  55. package/src/channels/telegram.ts +90 -90
  56. package/src/channels/voice.ts +106 -106
  57. package/src/channels/webhook.ts +199 -199
  58. package/src/channels/websocket.ts +87 -87
  59. package/src/channels/wechat.ts +149 -149
  60. package/src/core/a2a.ts +143 -143
  61. package/src/core/agent.ts +152 -152
  62. package/src/core/analytics-engine.ts +186 -186
  63. package/src/core/auth.ts +57 -57
  64. package/src/core/cache.ts +141 -141
  65. package/src/core/compose.ts +77 -77
  66. package/src/core/config.ts +14 -14
  67. package/src/core/errors.ts +148 -148
  68. package/src/core/hitl.ts +138 -138
  69. package/src/core/logger.ts +57 -57
  70. package/src/core/orchestrator.ts +215 -215
  71. package/src/core/performance.ts +187 -187
  72. package/src/core/rate-limiter.ts +128 -128
  73. package/src/core/room.ts +109 -109
  74. package/src/core/runtime.ts +152 -152
  75. package/src/core/sandbox.ts +101 -101
  76. package/src/core/security.ts +171 -171
  77. package/src/core/streaming.ts +195 -0
  78. package/src/core/types.ts +68 -68
  79. package/src/core/versioning.ts +106 -106
  80. package/src/core/watch.ts +178 -178
  81. package/src/core/workflow.ts +235 -235
  82. package/src/deploy/hermes.ts +156 -156
  83. package/src/deploy/openclaw.ts +200 -200
  84. package/src/dtv/data.ts +29 -29
  85. package/src/dtv/trust.ts +43 -43
  86. package/src/dtv/value.ts +47 -47
  87. package/src/i18n/index.ts +216 -216
  88. package/src/index.ts +6 -0
  89. package/src/marketplace/index.ts +223 -223
  90. package/src/memory/deepbrain.ts +108 -108
  91. package/src/memory/index.ts +34 -34
  92. package/src/plugins/index.ts +208 -208
  93. package/src/schema/oad.ts +155 -155
  94. package/src/skills/base.ts +16 -16
  95. package/src/skills/document.ts +100 -100
  96. package/src/skills/http.ts +35 -35
  97. package/src/skills/index.ts +27 -27
  98. package/src/skills/scheduler.ts +80 -80
  99. package/src/skills/webhook-trigger.ts +59 -59
  100. package/src/templates/code-reviewer.ts +34 -34
  101. package/src/templates/customer-service.ts +80 -80
  102. package/src/templates/data-analyst.ts +70 -70
  103. package/src/templates/executive-assistant.ts +71 -71
  104. package/src/templates/financial-advisor.ts +60 -60
  105. package/src/templates/knowledge-base.ts +31 -31
  106. package/src/templates/legal-assistant.ts +71 -71
  107. package/src/templates/sales-assistant.ts +79 -79
  108. package/src/templates/teacher.ts +79 -79
  109. package/src/testing/index.ts +181 -181
  110. package/src/tools/calculator.ts +73 -73
  111. package/src/tools/datetime.ts +149 -149
  112. package/src/tools/gateway.ts +220 -0
  113. package/src/tools/json-transform.ts +187 -187
  114. package/src/tools/mcp.ts +76 -76
  115. package/src/tools/text-analysis.ts +116 -116
  116. package/templates/Dockerfile +15 -15
  117. package/templates/code-reviewer/README.md +27 -27
  118. package/templates/code-reviewer/oad.yaml +41 -41
  119. package/templates/customer-service/README.md +22 -22
  120. package/templates/customer-service/oad.yaml +36 -36
  121. package/templates/docker-compose.yml +21 -21
  122. package/templates/ecommerce-assistant/README.md +45 -45
  123. package/templates/ecommerce-assistant/oad.yaml +47 -47
  124. package/templates/knowledge-base/README.md +28 -28
  125. package/templates/knowledge-base/oad.yaml +38 -38
  126. package/templates/sales-assistant/README.md +26 -26
  127. package/templates/sales-assistant/oad.yaml +43 -43
  128. package/templates/tech-support/README.md +43 -43
  129. package/templates/tech-support/oad.yaml +45 -45
  130. package/tests/a2a.test.ts +66 -66
  131. package/tests/agent.test.ts +72 -72
  132. package/tests/analytics.test.ts +50 -50
  133. package/tests/channel.test.ts +39 -39
  134. package/tests/e2e.test.ts +134 -134
  135. package/tests/errors.test.ts +83 -83
  136. package/tests/gateway.test.ts +71 -0
  137. package/tests/hitl.test.ts +71 -71
  138. package/tests/i18n.test.ts +41 -41
  139. package/tests/mcp.test.ts +54 -54
  140. package/tests/oad.test.ts +68 -68
  141. package/tests/performance.test.ts +115 -115
  142. package/tests/plugin.test.ts +74 -74
  143. package/tests/room.test.ts +106 -106
  144. package/tests/runtime.test.ts +42 -42
  145. package/tests/sandbox.test.ts +46 -46
  146. package/tests/security.test.ts +60 -60
  147. package/tests/streaming.test.ts +109 -0
  148. package/tests/templates.test.ts +77 -77
  149. package/tests/v070.test.ts +76 -76
  150. package/tests/versioning.test.ts +75 -75
  151. package/tests/voice.test.ts +61 -61
  152. package/tests/webhook.test.ts +29 -29
  153. package/tests/workflow.test.ts +143 -143
  154. package/tsconfig.json +19 -19
  155. package/vitest.config.ts +9 -9
@@ -20,264 +20,264 @@ const AGENT_TEMPLATES = [
20
20
  { id: 'hr-recruiter', name: 'HR Recruiter', description: 'Resume screening, interview scheduling, candidate comms', icon: '👥', category: 'HR' },
21
21
  { id: 'legal-assistant', name: 'Legal Assistant', description: 'Contract review, compliance checks, legal research', icon: '⚖️', category: 'Legal' },
22
22
  ];
23
- const TEMPLATES_HTML = `<!DOCTYPE html>
24
- <html lang="en">
25
- <head>
26
- <meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0">
27
- <title>Agent Templates</title>
28
- <style>
29
- *{margin:0;padding:0;box-sizing:border-box}
30
- body{background:#0f0f23;color:#e0e0e0;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;padding:24px}
31
- h1{font-size:28px;margin-bottom:8px;color:#fff}
32
- .sub{color:#8a8aa0;margin-bottom:32px;font-size:14px}
33
- nav{margin-bottom:24px}
34
- nav a{color:#818cf8;text-decoration:none;margin-right:16px;font-size:14px}
35
- .grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:16px}
36
- .card{background:#1a1a3a;border:1px solid #2d2d4e;border-radius:14px;padding:24px;cursor:pointer;transition:all .2s}
37
- .card:hover{border-color:#818cf8;transform:translateY(-2px);box-shadow:0 4px 20px rgba(129,140,248,.15)}
38
- .card .icon{font-size:32px;margin-bottom:12px}
39
- .card h3{font-size:16px;color:#fff;margin-bottom:8px}
40
- .card p{font-size:13px;color:#888;line-height:1.5}
41
- .card .cat{font-size:11px;color:#818cf8;text-transform:uppercase;letter-spacing:1px;margin-top:12px}
42
- .btn{display:inline-block;background:#2563eb;color:#fff;border:none;border-radius:8px;padding:8px 16px;font-size:13px;cursor:pointer;margin-top:12px}
43
- .btn:hover{background:#1d4ed8}
44
- </style>
45
- </head>
46
- <body>
47
- <nav><a href="/">← Chat</a><a href="/dashboard">Dashboard</a><a href="/templates">Templates</a></nav>
48
- <h1>🧩 Agent Templates</h1>
49
- <p class="sub">Create a new agent from a pre-built template in one click.</p>
50
- <div class="grid" id="grid"></div>
51
- <script>
52
- fetch('/api/templates').then(r=>r.json()).then(d=>{
53
- const g=document.getElementById('grid');
54
- d.templates.forEach(t=>{
55
- g.innerHTML+=\`<div class="card"><div class="icon">\${t.icon}</div><h3>\${t.name}</h3><p>\${t.description}</p><div class="cat">\${t.category}</div><button class="btn" onclick="alert('Creating agent from template: '+'\${t.id}'+'\\\\nRun: opc init --template \${t.id}')">Use Template</button></div>\`;
56
- });
57
- });
58
- </script>
59
- </body>
23
+ const TEMPLATES_HTML = `<!DOCTYPE html>
24
+ <html lang="en">
25
+ <head>
26
+ <meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1.0">
27
+ <title>Agent Templates</title>
28
+ <style>
29
+ *{margin:0;padding:0;box-sizing:border-box}
30
+ body{background:#0a0a0f;color:#e0e0e0;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;padding:24px}
31
+ h1{font-size:28px;margin-bottom:8px;color:#fff}
32
+ .sub{color:#888;margin-bottom:32px;font-size:14px}
33
+ nav{margin-bottom:24px}
34
+ nav a{color:#818cf8;text-decoration:none;margin-right:16px;font-size:14px}
35
+ .grid{display:grid;grid-template-columns:repeat(auto-fill,minmax(280px,1fr));gap:16px}
36
+ .card{background:#12121a;border:1px solid #1e1e2e;border-radius:12px;padding:24px;cursor:pointer;transition:all .2s}
37
+ .card:hover{border-color:#818cf8;transform:translateY(-2px)}
38
+ .card .icon{font-size:32px;margin-bottom:12px}
39
+ .card h3{font-size:16px;color:#fff;margin-bottom:8px}
40
+ .card p{font-size:13px;color:#888;line-height:1.5}
41
+ .card .cat{font-size:11px;color:#818cf8;text-transform:uppercase;letter-spacing:1px;margin-top:12px}
42
+ .btn{display:inline-block;background:#2563eb;color:#fff;border:none;border-radius:8px;padding:8px 16px;font-size:13px;cursor:pointer;margin-top:12px}
43
+ .btn:hover{background:#1d4ed8}
44
+ </style>
45
+ </head>
46
+ <body>
47
+ <nav><a href="/">← Chat</a><a href="/dashboard">Dashboard</a><a href="/templates">Templates</a></nav>
48
+ <h1>🧩 Agent Templates</h1>
49
+ <p class="sub">Create a new agent from a pre-built template in one click.</p>
50
+ <div class="grid" id="grid"></div>
51
+ <script>
52
+ fetch('/api/templates').then(r=>r.json()).then(d=>{
53
+ const g=document.getElementById('grid');
54
+ d.templates.forEach(t=>{
55
+ g.innerHTML+=\`<div class="card"><div class="icon">\${t.icon}</div><h3>\${t.name}</h3><p>\${t.description}</p><div class="cat">\${t.category}</div><button class="btn" onclick="alert('Creating agent from template: '+'\${t.id}'+'\\\\nRun: opc init --template \${t.id}')">Use Template</button></div>\`;
56
+ });
57
+ });
58
+ </script>
59
+ </body>
60
60
  </html>`;
61
- const CHAT_HTML = `<!DOCTYPE html>
62
- <html lang="en">
63
- <head>
64
- <meta charset="UTF-8">
65
- <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
66
- <title>OPC Agent</title>
67
- <style>
68
- :root{--bg:#0f0f23;--surface:#1a1a3a;--border:#2d2d4e;--text:#e0e0e0;--text-dim:#8a8aa0;--accent:#818cf8;--accent-hover:#6366f1;--user-bg:#667eea;--user-hover:#5a6fd6;--error-bg:#7f1d1d;--error-text:#fca5a5;--success:#22c55e;--radius:14px}
69
- *{margin:0;padding:0;box-sizing:border-box}
70
- body{background:var(--bg);color:var(--text);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',sans-serif;height:100vh;height:100dvh;display:flex;flex-direction:column;overflow:hidden}
71
- header{background:var(--surface);padding:14px 20px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:12px;flex-shrink:0;backdrop-filter:blur(12px)}
72
- header .avatar{width:36px;height:36px;border-radius:50%;background:linear-gradient(135deg,var(--accent),#6366f1);display:flex;align-items:center;justify-content:center;font-size:16px;flex-shrink:0}
73
- header .info{flex:1;min-width:0}
74
- header h1{font-size:16px;font-weight:600;color:#fff;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
75
- header .status{font-size:12px;color:var(--success);display:flex;align-items:center;gap:4px}
76
- header .status .dot{width:6px;height:6px;border-radius:50%;background:var(--success);animation:pulse 2s infinite}
77
- nav.header-nav{display:flex;gap:4px}
78
- nav.header-nav a{color:var(--text-dim);text-decoration:none;font-size:12px;padding:4px 10px;border-radius:6px;transition:all .2s}
79
- nav.header-nav a:hover{color:#fff;background:rgba(255,255,255,.06)}
80
- @keyframes pulse{0%,100%{opacity:1}50%{opacity:.3}}
81
- @keyframes fadeIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}
82
- @keyframes slideIn{from{opacity:0;transform:scale(.96)}to{opacity:1;transform:scale(1)}}
83
- #messages{flex:1;overflow-y:auto;padding:20px;display:flex;flex-direction:column;gap:12px;scroll-behavior:smooth}
84
- #messages::-webkit-scrollbar{width:4px}
85
- #messages::-webkit-scrollbar-track{background:transparent}
86
- #messages::-webkit-scrollbar-thumb{background:var(--border);border-radius:4px}
87
- .msg-wrap{display:flex;flex-direction:column;animation:fadeIn .3s ease-out}
88
- .msg-wrap.user{align-items:flex-end}
89
- .msg-wrap.assistant{align-items:flex-start}
90
- .msg{max-width:min(720px,85%);padding:10px 14px;border-radius:var(--radius);line-height:1.7;font-size:14px;word-break:break-word;position:relative;transition:all .2s}
91
- .msg.user{background:linear-gradient(135deg,#667eea,#764ba2);color:#fff;border-bottom-right-radius:4px;box-shadow:0 2px 8px rgba(102,126,234,.35)}
92
- .msg.assistant{background:#2a2a4a;color:var(--text);border:1px solid var(--border);border-bottom-left-radius:4px;box-shadow:0 2px 8px rgba(0,0,0,.25)}
93
- .msg.error{background:var(--error-bg);color:var(--error-text);border:1px solid rgba(239,68,68,.3)}
94
- .msg pre{background:rgba(0,0,0,.4);padding:12px;border-radius:8px;overflow-x:auto;margin:8px 0;font-size:13px;font-family:'JetBrains Mono','Fira Code','Cascadia Code',monospace;line-height:1.5}
95
- .msg code{font-family:'JetBrains Mono','Fira Code','Cascadia Code',monospace;font-size:13px;background:rgba(0,0,0,.3);padding:1px 5px;border-radius:4px}
96
- .msg pre code{background:none;padding:0}
97
- .msg .cursor{display:inline-block;width:2px;height:14px;background:var(--accent);animation:blink .6s infinite;vertical-align:text-bottom;margin-left:2px}
98
- @keyframes blink{0%,100%{opacity:1}50%{opacity:0}}
99
- .typing{display:flex;gap:4px;padding:12px 16px;align-items:center}
100
- .typing span{width:6px;height:6px;border-radius:50%;background:var(--text-dim);animation:typingDot 1.4s infinite}
101
- .typing span:nth-child(2){animation-delay:.2s}
102
- .typing span:nth-child(3){animation-delay:.4s}
103
- @keyframes typingDot{0%,60%,100%{opacity:.3;transform:scale(.8)}30%{opacity:1;transform:scale(1)}}
104
- .reactions{display:flex;gap:4px;margin-top:4px}
105
- .reactions button{background:rgba(255,255,255,.06);border:1px solid transparent;border-radius:16px;padding:2px 8px;font-size:13px;cursor:pointer;transition:all .15s;color:var(--text-dim)}
106
- .reactions button:hover{background:rgba(255,255,255,.12);border-color:var(--border)}
107
- .reactions button.active{background:rgba(99,102,241,.2);border-color:var(--accent);color:var(--accent)}
108
- .msg-time{font-size:11px;color:var(--text-dim);margin-top:2px;opacity:0;transition:opacity .2s}
109
- .msg-wrap:hover .msg-time{opacity:1}
110
- .attachment{display:flex;align-items:center;gap:8px;background:rgba(0,0,0,.3);padding:8px 12px;border-radius:8px;margin-top:6px;font-size:13px}
111
- .attachment .icon{font-size:18px}
112
- #input-area{background:var(--surface);padding:12px 20px 16px;border-top:1px solid var(--border);display:flex;gap:10px;align-items:flex-end;flex-shrink:0}
113
- #input{flex:1;background:var(--bg);border:1px solid var(--border);border-radius:var(--radius);padding:10px 14px;color:#fff;font-size:14px;outline:none;resize:none;max-height:150px;min-height:42px;font-family:inherit;line-height:1.5;transition:border-color .2s}
114
- #input:focus{border-color:var(--accent)}
115
- #input::placeholder{color:var(--text-dim)}
116
- #send{background:linear-gradient(135deg,#667eea,#764ba2);color:#fff;border:none;border-radius:var(--radius);padding:0 16px;height:42px;font-size:14px;font-weight:600;cursor:pointer;transition:all .2s;display:flex;align-items:center;justify-content:center;flex-shrink:0;letter-spacing:.5px}
117
- #send:hover{background:var(--user-hover);transform:scale(1.05)}
118
- #send:disabled{background:#334155;cursor:not-allowed;transform:none}
119
- .empty-state{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;color:var(--text-dim);gap:12px;padding:40px;text-align:center}
120
- .empty-state .logo{font-size:48px;opacity:.6}
121
- .empty-state h2{color:var(--text);font-size:20px;font-weight:500}
122
- .empty-state p{font-size:14px;max-width:400px;line-height:1.6}
123
- @media(max-width:640px){
124
- header{padding:10px 14px}
125
- #messages{padding:12px}
126
- #input-area{padding:10px 14px 14px}
127
- .msg{max-width:90%;font-size:14px}
128
- nav.header-nav{display:none}
129
- }
130
- </style>
131
- </head>
132
- <body>
133
- <header>
134
- <div class="avatar" id="avatar">🤖</div>
135
- <div class="info"><h1 id="title">OPC Agent</h1><div class="status"><span class="dot"></span>在线</div></div>
136
- <nav class="header-nav"><a href="/dashboard">Dashboard</a><a href="/templates">Templates</a></nav>
137
- </header>
138
- <div id="messages">
139
- <div class="empty-state" id="empty"><div class="logo">💬</div><h2>开始对话</h2><p>在下方输入消息与 AI 助手对话。</p></div>
140
- </div>
141
- <div id="input-area">
142
- <textarea id="input" rows="1" placeholder="输入消息…" autocomplete="off"></textarea>
143
- <button id="send" aria-label="发送">发送</button>
144
- </div>
145
- <script>
146
- const msgs=document.getElementById('messages'),input=document.getElementById('input'),btn=document.getElementById('send'),empty=document.getElementById('empty');
147
- let sessionId=crypto.randomUUID(),sending=false;
148
-
149
- function esc(s){const d=document.createElement('div');d.textContent=s;return d.innerHTML}
150
- function fmtTime(){return new Date().toLocaleTimeString([],{hour:'2-digit',minute:'2-digit'})}
151
- function renderMd(text){
152
- let h=esc(text);
153
- h=h.replace(/\`\`\`(\\w*)\\n([\\s\\S]*?)\`\`\`/g,'<pre><code>$2</code></pre>');
154
- h=h.replace(/\`([^\`]+)\`/g,'<code>$1</code>');
155
- h=h.replace(/\\*\\*(.+?)\\*\\*/g,'<strong>$1</strong>');
156
- h=h.replace(/\\n/g,'<br>');
157
- return h;
158
- }
159
-
160
- function addMsg(role,text,opts){
161
- if(empty)empty.remove();
162
- const wrap=document.createElement('div');
163
- wrap.className='msg-wrap '+role;
164
- const d=document.createElement('div');
165
- d.className='msg '+role;
166
- if(opts?.html)d.innerHTML=text;else if(role==='assistant'&&text)d.innerHTML=renderMd(text);else d.textContent=text;
167
- wrap.appendChild(d);
168
- const time=document.createElement('div');
169
- time.className='msg-time';
170
- time.textContent=fmtTime();
171
- wrap.appendChild(time);
172
- if(role==='assistant'&&text){
173
- const rx=document.createElement('div');rx.className='reactions';
174
- rx.innerHTML='<button data-r="👍" onclick="react(this)">👍</button><button data-r="👎" onclick="react(this)">👎</button>';
175
- wrap.appendChild(rx);
176
- }
177
- msgs.appendChild(wrap);
178
- msgs.scrollTop=msgs.scrollHeight;
179
- return d;
180
- }
181
-
182
- window.react=function(el){el.classList.toggle('active')};
183
-
184
- input.addEventListener('keydown',e=>{if(e.key==='Enter'&&!e.shiftKey){e.preventDefault();send()}});
185
- input.addEventListener('input',()=>{input.style.height='auto';input.style.height=Math.min(input.scrollHeight,150)+'px'});
186
- btn.addEventListener('click',send);
187
-
188
- async function send(){
189
- const text=input.value.trim();
190
- if(!text||sending)return;
191
- sending=true;btn.disabled=true;
192
- input.value='';input.style.height='auto';
193
- addMsg('user',text);
194
- const wrap=document.createElement('div');wrap.className='msg-wrap assistant';
195
- const d=document.createElement('div');d.className='msg assistant';
196
- d.innerHTML='<div class="typing"><span></span><span></span><span></span><small style="margin-left:6px;font-size:12px;color:#8a8aa0">思考中…</small></div>';
197
- wrap.appendChild(d);
198
- const time=document.createElement('div');time.className='msg-time';time.textContent=fmtTime();
199
- wrap.appendChild(time);
200
- msgs.appendChild(wrap);msgs.scrollTop=msgs.scrollHeight;
201
- try{
202
- const res=await fetch('/api/chat',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({message:text,sessionId})});
203
- if(!res.ok)throw new Error('HTTP '+res.status);
204
- const reader=res.body.getReader(),dec=new TextDecoder();
205
- let full='';
206
- while(true){
207
- const{done,value}=await reader.read();
208
- if(done)break;
209
- const chunk=dec.decode(value,{stream:true});
210
- const lines=chunk.split('\\n');
211
- for(const line of lines){
212
- if(!line.startsWith('data: '))continue;
213
- const dd=line.slice(6);if(dd==='[DONE]')continue;
214
- try{const j=JSON.parse(dd);if(j.content)full+=j.content;if(j.error)full='Error: '+j.error;}catch{}
215
- }
216
- d.innerHTML=renderMd(full)+'<span class="cursor"></span>';
217
- msgs.scrollTop=msgs.scrollHeight;
218
- }
219
- if(!full){d.textContent='(empty response)';}else{d.innerHTML=renderMd(full);}
220
- const rx=document.createElement('div');rx.className='reactions';
221
- rx.innerHTML='<button data-r="👍" onclick="react(this)">👍</button><button data-r="👎" onclick="react(this)">👎</button>';
222
- wrap.appendChild(rx);
223
- }catch(e){d.className='msg error';d.textContent='Error: '+e.message;}
224
- sending=false;btn.disabled=false;input.focus();
225
- }
226
-
227
- fetch('/api/info').then(r=>r.json()).then(d=>{if(d.name)document.getElementById('title').textContent=d.name}).catch(()=>{});
228
- </script>
229
- </body>
61
+ const CHAT_HTML = `<!DOCTYPE html>
62
+ <html lang="en">
63
+ <head>
64
+ <meta charset="UTF-8">
65
+ <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no">
66
+ <title>OPC Agent</title>
67
+ <style>
68
+ :root{--bg:#0a0a0f;--surface:#12121a;--border:#1e1e2e;--text:#e0e0e0;--text-dim:#888;--accent:#818cf8;--accent-hover:#6366f1;--user-bg:#2563eb;--user-hover:#1d4ed8;--error-bg:#7f1d1d;--error-text:#fca5a5;--success:#22c55e;--radius:12px}
69
+ *{margin:0;padding:0;box-sizing:border-box}
70
+ body{background:var(--bg);color:var(--text);font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,'Helvetica Neue',sans-serif;height:100vh;height:100dvh;display:flex;flex-direction:column;overflow:hidden}
71
+ header{background:var(--surface);padding:14px 20px;border-bottom:1px solid var(--border);display:flex;align-items:center;gap:12px;flex-shrink:0;backdrop-filter:blur(12px)}
72
+ header .avatar{width:36px;height:36px;border-radius:50%;background:linear-gradient(135deg,var(--accent),#6366f1);display:flex;align-items:center;justify-content:center;font-size:16px;flex-shrink:0}
73
+ header .info{flex:1;min-width:0}
74
+ header h1{font-size:16px;font-weight:600;color:#fff;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}
75
+ header .status{font-size:12px;color:var(--success);display:flex;align-items:center;gap:4px}
76
+ header .status .dot{width:6px;height:6px;border-radius:50%;background:var(--success);animation:pulse 2s infinite}
77
+ nav.header-nav{display:flex;gap:4px}
78
+ nav.header-nav a{color:var(--text-dim);text-decoration:none;font-size:12px;padding:4px 10px;border-radius:6px;transition:all .2s}
79
+ nav.header-nav a:hover{color:#fff;background:rgba(255,255,255,.06)}
80
+ @keyframes pulse{0%,100%{opacity:1}50%{opacity:.3}}
81
+ @keyframes fadeIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:translateY(0)}}
82
+ @keyframes slideIn{from{opacity:0;transform:scale(.96)}to{opacity:1;transform:scale(1)}}
83
+ #messages{flex:1;overflow-y:auto;padding:20px;display:flex;flex-direction:column;gap:12px;scroll-behavior:smooth}
84
+ #messages::-webkit-scrollbar{width:4px}
85
+ #messages::-webkit-scrollbar-track{background:transparent}
86
+ #messages::-webkit-scrollbar-thumb{background:var(--border);border-radius:4px}
87
+ .msg-wrap{display:flex;flex-direction:column;animation:fadeIn .3s ease-out}
88
+ .msg-wrap.user{align-items:flex-end}
89
+ .msg-wrap.assistant{align-items:flex-start}
90
+ .msg{max-width:min(720px,85%);padding:10px 14px;border-radius:var(--radius);line-height:1.7;font-size:14px;word-break:break-word;position:relative;transition:all .2s}
91
+ .msg.user{background:var(--user-bg);color:#fff;border-bottom-right-radius:4px}
92
+ .msg.assistant{background:var(--surface);color:var(--text);border:1px solid var(--border);border-bottom-left-radius:4px}
93
+ .msg.error{background:var(--error-bg);color:var(--error-text);border:1px solid rgba(239,68,68,.3)}
94
+ .msg pre{background:rgba(0,0,0,.4);padding:12px;border-radius:8px;overflow-x:auto;margin:8px 0;font-size:13px;font-family:'JetBrains Mono','Fira Code','Cascadia Code',monospace;line-height:1.5}
95
+ .msg code{font-family:'JetBrains Mono','Fira Code','Cascadia Code',monospace;font-size:13px;background:rgba(0,0,0,.3);padding:1px 5px;border-radius:4px}
96
+ .msg pre code{background:none;padding:0}
97
+ .msg .cursor{display:inline-block;width:2px;height:14px;background:var(--accent);animation:blink .6s infinite;vertical-align:text-bottom;margin-left:2px}
98
+ @keyframes blink{0%,100%{opacity:1}50%{opacity:0}}
99
+ .typing{display:flex;gap:4px;padding:12px 16px;align-items:center}
100
+ .typing span{width:6px;height:6px;border-radius:50%;background:var(--text-dim);animation:typingDot 1.4s infinite}
101
+ .typing span:nth-child(2){animation-delay:.2s}
102
+ .typing span:nth-child(3){animation-delay:.4s}
103
+ @keyframes typingDot{0%,60%,100%{opacity:.3;transform:scale(.8)}30%{opacity:1;transform:scale(1)}}
104
+ .reactions{display:flex;gap:4px;margin-top:4px}
105
+ .reactions button{background:rgba(255,255,255,.06);border:1px solid transparent;border-radius:16px;padding:2px 8px;font-size:13px;cursor:pointer;transition:all .15s;color:var(--text-dim)}
106
+ .reactions button:hover{background:rgba(255,255,255,.12);border-color:var(--border)}
107
+ .reactions button.active{background:rgba(99,102,241,.2);border-color:var(--accent);color:var(--accent)}
108
+ .msg-time{font-size:11px;color:var(--text-dim);margin-top:2px;opacity:0;transition:opacity .2s}
109
+ .msg-wrap:hover .msg-time{opacity:1}
110
+ .attachment{display:flex;align-items:center;gap:8px;background:rgba(0,0,0,.3);padding:8px 12px;border-radius:8px;margin-top:6px;font-size:13px}
111
+ .attachment .icon{font-size:18px}
112
+ #input-area{background:var(--surface);padding:12px 20px 16px;border-top:1px solid var(--border);display:flex;gap:10px;align-items:flex-end;flex-shrink:0}
113
+ #input{flex:1;background:var(--bg);border:1px solid var(--border);border-radius:var(--radius);padding:10px 14px;color:#fff;font-size:14px;outline:none;resize:none;max-height:150px;min-height:42px;font-family:inherit;line-height:1.5;transition:border-color .2s}
114
+ #input:focus{border-color:var(--accent)}
115
+ #input::placeholder{color:var(--text-dim)}
116
+ #send{background:var(--user-bg);color:#fff;border:none;border-radius:var(--radius);width:42px;height:42px;font-size:18px;cursor:pointer;transition:all .2s;display:flex;align-items:center;justify-content:center;flex-shrink:0}
117
+ #send:hover{background:var(--user-hover);transform:scale(1.05)}
118
+ #send:disabled{background:#334155;cursor:not-allowed;transform:none}
119
+ .empty-state{flex:1;display:flex;flex-direction:column;align-items:center;justify-content:center;color:var(--text-dim);gap:12px;padding:40px;text-align:center}
120
+ .empty-state .logo{font-size:48px;opacity:.6}
121
+ .empty-state h2{color:var(--text);font-size:20px;font-weight:500}
122
+ .empty-state p{font-size:14px;max-width:400px;line-height:1.6}
123
+ @media(max-width:640px){
124
+ header{padding:10px 14px}
125
+ #messages{padding:12px}
126
+ #input-area{padding:10px 14px 14px}
127
+ .msg{max-width:90%;font-size:14px}
128
+ nav.header-nav{display:none}
129
+ }
130
+ </style>
131
+ </head>
132
+ <body>
133
+ <header>
134
+ <div class="avatar" id="avatar">🤖</div>
135
+ <div class="info"><h1 id="title">OPC Agent</h1><div class="status"><span class="dot"></span>Online</div></div>
136
+ <nav class="header-nav"><a href="/dashboard">Dashboard</a><a href="/templates">Templates</a></nav>
137
+ </header>
138
+ <div id="messages">
139
+ <div class="empty-state" id="empty"><div class="logo">💬</div><h2>Start a conversation</h2><p>Type a message below to chat with your AI agent.</p></div>
140
+ </div>
141
+ <div id="input-area">
142
+ <textarea id="input" rows="1" placeholder="Type a message…" autocomplete="off"></textarea>
143
+ <button id="send" aria-label="Send">↑</button>
144
+ </div>
145
+ <script>
146
+ const msgs=document.getElementById('messages'),input=document.getElementById('input'),btn=document.getElementById('send'),empty=document.getElementById('empty');
147
+ let sessionId=crypto.randomUUID(),sending=false;
148
+
149
+ function esc(s){const d=document.createElement('div');d.textContent=s;return d.innerHTML}
150
+ function fmtTime(){return new Date().toLocaleTimeString([],{hour:'2-digit',minute:'2-digit'})}
151
+ function renderMd(text){
152
+ let h=esc(text);
153
+ h=h.replace(/\`\`\`(\\w*)\\n([\\s\\S]*?)\`\`\`/g,'<pre><code>$2</code></pre>');
154
+ h=h.replace(/\`([^\`]+)\`/g,'<code>$1</code>');
155
+ h=h.replace(/\\*\\*(.+?)\\*\\*/g,'<strong>$1</strong>');
156
+ h=h.replace(/\\n/g,'<br>');
157
+ return h;
158
+ }
159
+
160
+ function addMsg(role,text,opts){
161
+ if(empty)empty.remove();
162
+ const wrap=document.createElement('div');
163
+ wrap.className='msg-wrap '+role;
164
+ const d=document.createElement('div');
165
+ d.className='msg '+role;
166
+ if(opts?.html)d.innerHTML=text;else if(role==='assistant'&&text)d.innerHTML=renderMd(text);else d.textContent=text;
167
+ wrap.appendChild(d);
168
+ const time=document.createElement('div');
169
+ time.className='msg-time';
170
+ time.textContent=fmtTime();
171
+ wrap.appendChild(time);
172
+ if(role==='assistant'&&text){
173
+ const rx=document.createElement('div');rx.className='reactions';
174
+ rx.innerHTML='<button data-r="👍" onclick="react(this)">👍</button><button data-r="👎" onclick="react(this)">👎</button>';
175
+ wrap.appendChild(rx);
176
+ }
177
+ msgs.appendChild(wrap);
178
+ msgs.scrollTop=msgs.scrollHeight;
179
+ return d;
180
+ }
181
+
182
+ window.react=function(el){el.classList.toggle('active')};
183
+
184
+ input.addEventListener('keydown',e=>{if(e.key==='Enter'&&!e.shiftKey){e.preventDefault();send()}});
185
+ input.addEventListener('input',()=>{input.style.height='auto';input.style.height=Math.min(input.scrollHeight,150)+'px'});
186
+ btn.addEventListener('click',send);
187
+
188
+ async function send(){
189
+ const text=input.value.trim();
190
+ if(!text||sending)return;
191
+ sending=true;btn.disabled=true;
192
+ input.value='';input.style.height='auto';
193
+ addMsg('user',text);
194
+ const wrap=document.createElement('div');wrap.className='msg-wrap assistant';
195
+ const d=document.createElement('div');d.className='msg assistant';
196
+ d.innerHTML='<div class="typing"><span></span><span></span><span></span></div>';
197
+ wrap.appendChild(d);
198
+ const time=document.createElement('div');time.className='msg-time';time.textContent=fmtTime();
199
+ wrap.appendChild(time);
200
+ msgs.appendChild(wrap);msgs.scrollTop=msgs.scrollHeight;
201
+ try{
202
+ const res=await fetch('/api/chat',{method:'POST',headers:{'Content-Type':'application/json'},body:JSON.stringify({message:text,sessionId})});
203
+ if(!res.ok)throw new Error('HTTP '+res.status);
204
+ const reader=res.body.getReader(),dec=new TextDecoder();
205
+ let full='';
206
+ while(true){
207
+ const{done,value}=await reader.read();
208
+ if(done)break;
209
+ const chunk=dec.decode(value,{stream:true});
210
+ const lines=chunk.split('\\n');
211
+ for(const line of lines){
212
+ if(!line.startsWith('data: '))continue;
213
+ const dd=line.slice(6);if(dd==='[DONE]')continue;
214
+ try{const j=JSON.parse(dd);if(j.content)full+=j.content;if(j.error)full='Error: '+j.error;}catch{}
215
+ }
216
+ d.innerHTML=renderMd(full)+'<span class="cursor"></span>';
217
+ msgs.scrollTop=msgs.scrollHeight;
218
+ }
219
+ if(!full){d.textContent='(empty response)';}else{d.innerHTML=renderMd(full);}
220
+ const rx=document.createElement('div');rx.className='reactions';
221
+ rx.innerHTML='<button data-r="👍" onclick="react(this)">👍</button><button data-r="👎" onclick="react(this)">👎</button>';
222
+ wrap.appendChild(rx);
223
+ }catch(e){d.className='msg error';d.textContent='Error: '+e.message;}
224
+ sending=false;btn.disabled=false;input.focus();
225
+ }
226
+
227
+ fetch('/api/info').then(r=>r.json()).then(d=>{if(d.name)document.getElementById('title').textContent=d.name}).catch(()=>{});
228
+ </script>
229
+ </body>
230
230
  </html>`;
231
- const DASHBOARD_HTML = `<!DOCTYPE html>
232
- <html lang="en">
233
- <head>
234
- <meta charset="UTF-8">
235
- <meta name="viewport" content="width=device-width, initial-scale=1.0">
236
- <title>OPC Dashboard</title>
237
- <style>
238
- *{margin:0;padding:0;box-sizing:border-box}
239
- body{background:#0f0f23;color:#e0e0e0;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;padding:24px}
240
- h1{font-size:24px;margin-bottom:24px;color:#fff}
241
- .grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:16px;margin-bottom:32px}
242
- .card{background:#1a1a3a;border:1px solid #2d2d4e;border-radius:14px;padding:20px}
243
- .card .label{font-size:12px;color:#8a8aa0;text-transform:uppercase;letter-spacing:1px}
244
- .card .value{font-size:32px;font-weight:700;color:#818cf8;margin-top:4px}
245
- .card .sub{font-size:12px;color:#555;margin-top:4px}
246
- nav{margin-bottom:24px}
247
- nav a{color:#818cf8;text-decoration:none;margin-right:16px;font-size:14px}
248
- nav a:hover{text-decoration:underline}
249
- .chart{background:#1a1a3a;border:1px solid #2d2d4e;border-radius:14px;padding:20px;margin-bottom:16px}
250
- .chart h3{font-size:14px;color:#888;margin-bottom:12px}
251
- </style>
252
- </head>
253
- <body>
254
- <nav><a href="/">← Chat</a><a href="/dashboard">Dashboard</a></nav>
255
- <h1>📊 Agent Dashboard</h1>
256
- <div class="grid">
257
- <div class="card"><div class="label">Sessions</div><div class="value" id="sessions">0</div></div>
258
- <div class="card"><div class="label">Messages</div><div class="value" id="messages">0</div></div>
259
- <div class="card"><div class="label">Avg Response</div><div class="value" id="avgMs">0ms</div></div>
260
- <div class="card"><div class="label">Token Usage</div><div class="value" id="tokens">0</div></div>
261
- <div class="card"><div class="label">Uptime</div><div class="value" id="uptime">0m</div></div>
262
- <div class="card"><div class="label">Knowledge Files</div><div class="value" id="kb">0</div></div>
263
- </div>
264
- <div class="chart"><h3>Messages Over Time</h3><svg id="chart" width="100%" height="120" viewBox="0 0 600 120"></svg></div>
265
- <script>
266
- async function refresh(){
267
- try{
268
- const r=await fetch('/api/dashboard');const d=await r.json();
269
- document.getElementById('sessions').textContent=d.sessions;
270
- document.getElementById('messages').textContent=d.messages;
271
- document.getElementById('avgMs').textContent=d.messages>0?Math.round(d.totalResponseMs/d.messages)+'ms':'0ms';
272
- document.getElementById('tokens').textContent=d.tokenUsage.toLocaleString();
273
- document.getElementById('kb').textContent=d.knowledgeFiles;
274
- const mins=Math.round((Date.now()-d.startedAt)/60000);
275
- document.getElementById('uptime').textContent=mins<60?mins+'m':Math.round(mins/60)+'h '+mins%60+'m';
276
- }catch{}
277
- }
278
- refresh();setInterval(refresh,5000);
279
- </script>
280
- </body>
231
+ const DASHBOARD_HTML = `<!DOCTYPE html>
232
+ <html lang="en">
233
+ <head>
234
+ <meta charset="UTF-8">
235
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
236
+ <title>OPC Dashboard</title>
237
+ <style>
238
+ *{margin:0;padding:0;box-sizing:border-box}
239
+ body{background:#0a0a0f;color:#e0e0e0;font-family:-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;padding:24px}
240
+ h1{font-size:24px;margin-bottom:24px;color:#fff}
241
+ .grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(220px,1fr));gap:16px;margin-bottom:32px}
242
+ .card{background:#12121a;border:1px solid #1e1e2e;border-radius:12px;padding:20px}
243
+ .card .label{font-size:12px;color:#888;text-transform:uppercase;letter-spacing:1px}
244
+ .card .value{font-size:32px;font-weight:700;color:#818cf8;margin-top:4px}
245
+ .card .sub{font-size:12px;color:#555;margin-top:4px}
246
+ nav{margin-bottom:24px}
247
+ nav a{color:#818cf8;text-decoration:none;margin-right:16px;font-size:14px}
248
+ nav a:hover{text-decoration:underline}
249
+ .chart{background:#12121a;border:1px solid #1e1e2e;border-radius:12px;padding:20px;margin-bottom:16px}
250
+ .chart h3{font-size:14px;color:#888;margin-bottom:12px}
251
+ </style>
252
+ </head>
253
+ <body>
254
+ <nav><a href="/">← Chat</a><a href="/dashboard">Dashboard</a></nav>
255
+ <h1>📊 Agent Dashboard</h1>
256
+ <div class="grid">
257
+ <div class="card"><div class="label">Sessions</div><div class="value" id="sessions">0</div></div>
258
+ <div class="card"><div class="label">Messages</div><div class="value" id="messages">0</div></div>
259
+ <div class="card"><div class="label">Avg Response</div><div class="value" id="avgMs">0ms</div></div>
260
+ <div class="card"><div class="label">Token Usage</div><div class="value" id="tokens">0</div></div>
261
+ <div class="card"><div class="label">Uptime</div><div class="value" id="uptime">0m</div></div>
262
+ <div class="card"><div class="label">Knowledge Files</div><div class="value" id="kb">0</div></div>
263
+ </div>
264
+ <div class="chart"><h3>Messages Over Time</h3><svg id="chart" width="100%" height="120" viewBox="0 0 600 120"></svg></div>
265
+ <script>
266
+ async function refresh(){
267
+ try{
268
+ const r=await fetch('/api/dashboard');const d=await r.json();
269
+ document.getElementById('sessions').textContent=d.sessions;
270
+ document.getElementById('messages').textContent=d.messages;
271
+ document.getElementById('avgMs').textContent=d.messages>0?Math.round(d.totalResponseMs/d.messages)+'ms':'0ms';
272
+ document.getElementById('tokens').textContent=d.tokenUsage.toLocaleString();
273
+ document.getElementById('kb').textContent=d.knowledgeFiles;
274
+ const mins=Math.round((Date.now()-d.startedAt)/60000);
275
+ document.getElementById('uptime').textContent=mins<60?mins+'m':Math.round(mins/60)+'h '+mins%60+'m';
276
+ }catch{}
277
+ }
278
+ refresh();setInterval(refresh,5000);
279
+ </script>
280
+ </body>
281
281
  </html>`;
282
282
  class WebChannel extends index_1.BaseChannel {
283
283
  type = 'web';
@@ -17,11 +17,6 @@ export declare class KnowledgeBase {
17
17
  }>>;
18
18
  /** Build context string for injection into LLM calls */
19
19
  getContext(query: string, topK?: number, minScore?: number): Promise<string>;
20
- /**
21
- * Query DeepBrain for semantic search enhancement.
22
- * Activated when OPC_DEEPBRAIN_ENABLED=true and deepbrain CLI is globally installed.
23
- */
24
- private queryDeepBrain;
25
20
  getStats(): {
26
21
  totalEntries: number;
27
22
  sources: string[];
@@ -36,12 +36,10 @@ Object.defineProperty(exports, "__esModule", { value: true });
36
36
  exports.KnowledgeBase = void 0;
37
37
  /**
38
38
  * Knowledge Base / RAG - Local vector storage with semantic search
39
- * Supports optional DeepBrain semantic search enhancement via OPC_DEEPBRAIN_ENABLED=true
40
39
  */
41
40
  const fs = __importStar(require("fs"));
42
41
  const path = __importStar(require("path"));
43
42
  const crypto = __importStar(require("crypto"));
44
- const child_process_1 = require("child_process");
45
43
  const CHUNK_SIZE = 500; // chars per chunk
46
44
  const CHUNK_OVERLAP = 50;
47
45
  const STORE_FILE = '.opc-knowledge.json';
@@ -187,44 +185,9 @@ class KnowledgeBase {
187
185
  async getContext(query, topK = 3, minScore = 0.1) {
188
186
  const results = await this.search(query, topK);
189
187
  const relevant = results.filter(r => r.score >= minScore);
190
- let context = '';
191
- if (relevant.length > 0) {
192
- context = `\n\n--- Relevant Knowledge ---\n${relevant.map((r, i) => `[${i + 1}] (source: ${r.source}, relevance: ${(r.score * 100).toFixed(0)}%)\n${r.content}`).join('\n\n')}\n--- End Knowledge ---\n`;
193
- }
194
- // Enhance with DeepBrain semantic search if enabled
195
- const deepBrainCtx = this.queryDeepBrain(query, topK);
196
- return context + deepBrainCtx;
197
- }
198
- /**
199
- * Query DeepBrain for semantic search enhancement.
200
- * Activated when OPC_DEEPBRAIN_ENABLED=true and deepbrain CLI is globally installed.
201
- */
202
- queryDeepBrain(query, topK) {
203
- if (process.env.OPC_DEEPBRAIN_ENABLED !== 'true')
188
+ if (relevant.length === 0)
204
189
  return '';
205
- // Verify deepbrain is installed
206
- const check = (0, child_process_1.spawnSync)('deepbrain', ['--version'], { encoding: 'utf-8', timeout: 3000 });
207
- if (check.error || check.status !== 0)
208
- return '';
209
- try {
210
- const result = (0, child_process_1.spawnSync)('deepbrain', ['query', query, '--top', String(topK), '--format', 'json'], { encoding: 'utf-8', timeout: 5000 });
211
- if (result.status !== 0 || !result.stdout?.trim())
212
- return '';
213
- const parsed = JSON.parse(result.stdout);
214
- const items = Array.isArray(parsed)
215
- ? parsed
216
- : (parsed.results ?? []);
217
- if (items.length === 0)
218
- return '';
219
- return `\n\n--- DeepBrain Knowledge ---\n${items.map((r, i) => {
220
- const relevance = r.score != null ? `${(r.score * 100).toFixed(0)}%` : 'n/a';
221
- const text = r.content ?? r.text ?? '';
222
- return `[${i + 1}] (source: ${r.source ?? 'deepbrain'}, relevance: ${relevance})\n${text}`;
223
- }).join('\n\n')}\n--- End DeepBrain Knowledge ---\n`;
224
- }
225
- catch {
226
- return '';
227
- }
190
+ return `\n\n--- Relevant Knowledge ---\n${relevant.map((r, i) => `[${i + 1}] (source: ${r.source}, relevance: ${(r.score * 100).toFixed(0)}%)\n${r.content}`).join('\n\n')}\n--- End Knowledge ---\n`;
228
191
  }
229
192
  getStats() {
230
193
  const sources = [...new Set(this.store.entries.map(e => String(e.metadata.source)))];