opc-agent 4.2.13 → 5.0.0-rc.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 (207) hide show
  1. package/.opc/memory.db +0 -0
  2. package/.opc/schedules.json +784 -0
  3. package/.opc/voice-tmp/tts-1776835848670.mp3 +0 -0
  4. package/.opc/voice-tmp/tts-1776835873696.mp3 +0 -0
  5. package/README.md +125 -141
  6. package/README.zh-CN.md +75 -160
  7. package/TASK.md +34 -0
  8. package/data/brain.db/deepbrain.sqlite +0 -0
  9. package/dist/channels/api.d.ts +14 -0
  10. package/dist/channels/api.d.ts.map +1 -0
  11. package/dist/channels/api.js +58 -0
  12. package/dist/channels/api.js.map +1 -0
  13. package/dist/channels/voice.d.ts +21 -0
  14. package/dist/channels/voice.d.ts.map +1 -1
  15. package/dist/channels/voice.js +31 -1
  16. package/dist/channels/voice.js.map +1 -1
  17. package/dist/cli/chat.d.ts.map +1 -1
  18. package/dist/cli/chat.js +104 -0
  19. package/dist/cli/chat.js.map +1 -1
  20. package/dist/cli/setup.d.ts +1 -1
  21. package/dist/cli/setup.d.ts.map +1 -1
  22. package/dist/cli/setup.js +263 -33
  23. package/dist/cli/setup.js.map +1 -1
  24. package/dist/cli.js +201 -116
  25. package/dist/cli.js.map +1 -1
  26. package/dist/core/agent-loop.d.ts +3 -0
  27. package/dist/core/agent-loop.d.ts.map +1 -0
  28. package/dist/core/agent-loop.js +51 -0
  29. package/dist/core/agent-loop.js.map +1 -0
  30. package/dist/core/context-assembler.d.ts +12 -0
  31. package/dist/core/context-assembler.d.ts.map +1 -0
  32. package/dist/core/context-assembler.js +81 -0
  33. package/dist/core/context-assembler.js.map +1 -0
  34. package/dist/core/guardrails.d.ts +16 -0
  35. package/dist/core/guardrails.d.ts.map +1 -0
  36. package/dist/core/guardrails.js +62 -0
  37. package/dist/core/guardrails.js.map +1 -0
  38. package/dist/core/index.d.ts +1 -0
  39. package/dist/core/index.d.ts.map +1 -0
  40. package/dist/core/index.js +3 -0
  41. package/dist/core/index.js.map +1 -0
  42. package/dist/core/iteration-budget.d.ts +12 -0
  43. package/dist/core/iteration-budget.d.ts.map +1 -0
  44. package/dist/core/iteration-budget.js +26 -0
  45. package/dist/core/iteration-budget.js.map +1 -0
  46. package/dist/core/runtime.js +1 -1
  47. package/dist/core/runtime.js.map +1 -1
  48. package/dist/core/types.d.ts +412 -0
  49. package/dist/core/types.d.ts.map +1 -1
  50. package/dist/deepbrain/embedding.d.ts +7 -0
  51. package/dist/deepbrain/embedding.d.ts.map +1 -0
  52. package/dist/deepbrain/embedding.js +108 -0
  53. package/dist/deepbrain/embedding.js.map +1 -0
  54. package/dist/deepbrain/index.d.ts +8 -0
  55. package/dist/deepbrain/index.d.ts.map +1 -0
  56. package/dist/deepbrain/index.js +19 -0
  57. package/dist/deepbrain/index.js.map +1 -0
  58. package/dist/deepbrain/migrate.d.ts +2 -0
  59. package/dist/deepbrain/migrate.d.ts.map +1 -0
  60. package/dist/deepbrain/migrate.js +139 -0
  61. package/dist/deepbrain/migrate.js.map +1 -0
  62. package/dist/deepbrain/provider.d.ts +13 -0
  63. package/dist/deepbrain/provider.d.ts.map +1 -0
  64. package/dist/deepbrain/provider.js +85 -0
  65. package/dist/deepbrain/provider.js.map +1 -0
  66. package/dist/deepbrain/recall.d.ts +9 -0
  67. package/dist/deepbrain/recall.d.ts.map +1 -0
  68. package/dist/deepbrain/recall.js +48 -0
  69. package/dist/deepbrain/recall.js.map +1 -0
  70. package/dist/deepbrain/store.d.ts +36 -0
  71. package/dist/deepbrain/store.d.ts.map +1 -0
  72. package/dist/deepbrain/store.js +342 -0
  73. package/dist/deepbrain/store.js.map +1 -0
  74. package/dist/deepbrain/workspace-files.d.ts +5 -0
  75. package/dist/deepbrain/workspace-files.d.ts.map +1 -0
  76. package/dist/deepbrain/workspace-files.js +159 -0
  77. package/dist/deepbrain/workspace-files.js.map +1 -0
  78. package/dist/evolution/index.d.ts +1 -0
  79. package/dist/evolution/index.d.ts.map +1 -0
  80. package/dist/evolution/index.js +3 -0
  81. package/dist/evolution/index.js.map +1 -0
  82. package/dist/evolution/l1-experience.d.ts +11 -0
  83. package/dist/evolution/l1-experience.d.ts.map +1 -0
  84. package/dist/evolution/l1-experience.js +185 -0
  85. package/dist/evolution/l1-experience.js.map +1 -0
  86. package/dist/evolution/l2-consolidation.d.ts +4 -0
  87. package/dist/evolution/l2-consolidation.d.ts.map +1 -0
  88. package/dist/evolution/l2-consolidation.js +106 -0
  89. package/dist/evolution/l2-consolidation.js.map +1 -0
  90. package/dist/evolution/l2-memskill.d.ts +12 -0
  91. package/dist/evolution/l2-memskill.d.ts.map +1 -0
  92. package/dist/evolution/l2-memskill.js +57 -0
  93. package/dist/evolution/l2-memskill.js.map +1 -0
  94. package/dist/evolution/l3-skill-discover.d.ts +4 -0
  95. package/dist/evolution/l3-skill-discover.d.ts.map +1 -0
  96. package/dist/evolution/l3-skill-discover.js +139 -0
  97. package/dist/evolution/l3-skill-discover.js.map +1 -0
  98. package/dist/evolution/l3-skill-verify.d.ts +12 -0
  99. package/dist/evolution/l3-skill-verify.d.ts.map +1 -0
  100. package/dist/evolution/l3-skill-verify.js +122 -0
  101. package/dist/evolution/l3-skill-verify.js.map +1 -0
  102. package/dist/evolution/l4-desensitize.d.ts +7 -0
  103. package/dist/evolution/l4-desensitize.d.ts.map +1 -0
  104. package/dist/evolution/l4-desensitize.js +30 -0
  105. package/dist/evolution/l4-desensitize.js.map +1 -0
  106. package/dist/evolution/l4-group-evolve.d.ts +8 -0
  107. package/dist/evolution/l4-group-evolve.d.ts.map +1 -0
  108. package/dist/evolution/l4-group-evolve.js +15 -0
  109. package/dist/evolution/l4-group-evolve.js.map +1 -0
  110. package/dist/evolution/maturity-scorer.d.ts +11 -0
  111. package/dist/evolution/maturity-scorer.d.ts.map +1 -0
  112. package/dist/evolution/maturity-scorer.js +21 -0
  113. package/dist/evolution/maturity-scorer.js.map +1 -0
  114. package/dist/index.d.ts +7 -1
  115. package/dist/index.d.ts.map +1 -1
  116. package/dist/index.js +33 -5
  117. package/dist/index.js.map +1 -1
  118. package/dist/providers/agentkits.d.ts +20 -0
  119. package/dist/providers/agentkits.d.ts.map +1 -0
  120. package/dist/providers/agentkits.js +173 -0
  121. package/dist/providers/agentkits.js.map +1 -0
  122. package/dist/providers/model-provider.d.ts +16 -0
  123. package/dist/providers/model-provider.d.ts.map +1 -0
  124. package/dist/providers/model-provider.js +13 -0
  125. package/dist/providers/model-provider.js.map +1 -0
  126. package/dist/providers/model-recommender.d.ts +15 -0
  127. package/dist/providers/model-recommender.d.ts.map +1 -0
  128. package/dist/providers/model-recommender.js +71 -0
  129. package/dist/providers/model-recommender.js.map +1 -0
  130. package/dist/providers/ollama.d.ts +22 -0
  131. package/dist/providers/ollama.d.ts.map +1 -0
  132. package/dist/providers/ollama.js +176 -0
  133. package/dist/providers/ollama.js.map +1 -0
  134. package/dist/providers/openai-compat.d.ts +23 -0
  135. package/dist/providers/openai-compat.d.ts.map +1 -0
  136. package/dist/providers/openai-compat.js +184 -0
  137. package/dist/providers/openai-compat.js.map +1 -0
  138. package/dist/providers/router.d.ts +11 -0
  139. package/dist/providers/router.d.ts.map +1 -0
  140. package/dist/providers/router.js +48 -0
  141. package/dist/providers/router.js.map +1 -0
  142. package/dist/scheduler/cron.d.ts +19 -0
  143. package/dist/scheduler/cron.d.ts.map +1 -0
  144. package/dist/scheduler/cron.js +64 -0
  145. package/dist/scheduler/cron.js.map +1 -0
  146. package/dist/schema/oad.d.ts +72 -72
  147. package/dist/skills/loader.d.ts +16 -0
  148. package/dist/skills/loader.d.ts.map +1 -0
  149. package/dist/skills/loader.js +114 -0
  150. package/dist/skills/loader.js.map +1 -0
  151. package/dist/skills/matcher.d.ts +18 -0
  152. package/dist/skills/matcher.d.ts.map +1 -0
  153. package/dist/skills/matcher.js +70 -0
  154. package/dist/skills/matcher.js.map +1 -0
  155. package/dist/studio/agent-pool.d.ts +17 -0
  156. package/dist/studio/agent-pool.d.ts.map +1 -0
  157. package/dist/studio/agent-pool.js +35 -0
  158. package/dist/studio/agent-pool.js.map +1 -0
  159. package/dist/studio/assistant-tools.d.ts +4 -0
  160. package/dist/studio/assistant-tools.d.ts.map +1 -0
  161. package/dist/studio/assistant-tools.js +36 -0
  162. package/dist/studio/assistant-tools.js.map +1 -0
  163. package/dist/studio/index.d.ts +1 -0
  164. package/dist/studio/index.d.ts.map +1 -0
  165. package/dist/studio/index.js +3 -0
  166. package/dist/studio/index.js.map +1 -0
  167. package/dist/studio/server.d.ts +3 -0
  168. package/dist/studio/server.d.ts.map +1 -1
  169. package/dist/studio/server.js +156 -24
  170. package/dist/studio/server.js.map +1 -1
  171. package/dist/templates/index.d.ts +1 -0
  172. package/dist/templates/index.d.ts.map +1 -0
  173. package/dist/templates/index.js +3 -0
  174. package/dist/templates/index.js.map +1 -0
  175. package/dist/templates/roles/index.d.ts +4 -0
  176. package/dist/templates/roles/index.d.ts.map +1 -0
  177. package/dist/templates/roles/index.js +46 -0
  178. package/dist/templates/roles/index.js.map +1 -0
  179. package/dist/templates/template-provider.d.ts +16 -0
  180. package/dist/templates/template-provider.d.ts.map +1 -0
  181. package/dist/templates/template-provider.js +60 -0
  182. package/dist/templates/template-provider.js.map +1 -0
  183. package/dist/tools/builtin/definitions.d.ts +7 -0
  184. package/dist/tools/builtin/definitions.d.ts.map +1 -0
  185. package/dist/tools/builtin/definitions.js +60 -0
  186. package/dist/tools/builtin/definitions.js.map +1 -0
  187. package/dist/tools/execute-code.d.ts +20 -0
  188. package/dist/tools/execute-code.d.ts.map +1 -0
  189. package/dist/tools/execute-code.js +92 -0
  190. package/dist/tools/execute-code.js.map +1 -0
  191. package/dist/tools/hooks.d.ts +47 -0
  192. package/dist/tools/hooks.d.ts.map +1 -0
  193. package/dist/tools/hooks.js +69 -0
  194. package/dist/tools/hooks.js.map +1 -0
  195. package/dist/tools/index.d.ts +9 -0
  196. package/dist/tools/index.d.ts.map +1 -0
  197. package/dist/tools/index.js +16 -0
  198. package/dist/tools/index.js.map +1 -0
  199. package/dist/tools/permission.d.ts +20 -0
  200. package/dist/tools/permission.d.ts.map +1 -0
  201. package/dist/tools/permission.js +35 -0
  202. package/dist/tools/permission.js.map +1 -0
  203. package/dist/tools/registry.d.ts +25 -0
  204. package/dist/tools/registry.d.ts.map +1 -0
  205. package/dist/tools/registry.js +42 -0
  206. package/dist/tools/registry.js.map +1 -0
  207. package/package.json +1 -1
@@ -106,10 +106,45 @@ class StudioServer {
106
106
  const cfgPath = (0, path_1.join)(opcDir, 'config.json');
107
107
  if (!(0, fs_1.existsSync)(cfgPath))
108
108
  (0, fs_1.writeFileSync)(cfgPath, JSON.stringify({}, null, 2));
109
- this.server = (0, http_1.createServer)((req, res) => this.handleRequest(req, res));
110
- this.server.listen(this.config.port, '0.0.0.0');
111
- this.cronEngine.start();
112
- console.log(`🎨 OPC Studio: http://localhost:${this.config.port}`);
109
+ this.server = (0, http_1.createServer)((req, res) => {
110
+ this.handleRequest(req, res).catch(err => {
111
+ console.error('[Studio] Request handler error:', err);
112
+ if (!res.headersSent) {
113
+ res.writeHead(500, { 'Content-Type': 'application/json' });
114
+ res.end(JSON.stringify({ error: 'Internal server error' }));
115
+ }
116
+ });
117
+ });
118
+ await new Promise((resolve, reject) => {
119
+ this.server.once('listening', () => {
120
+ console.log(`[Studio] Listening on port ${this.config.port}`);
121
+ resolve();
122
+ });
123
+ this.server.once('error', (err) => {
124
+ console.error('[Studio] Server failed to bind port:', err.message);
125
+ reject(err);
126
+ });
127
+ this.server.listen(this.config.port, '0.0.0.0');
128
+ });
129
+ try {
130
+ this.cronEngine.start();
131
+ }
132
+ catch (err) {
133
+ console.error('[Studio] Cron engine failed to start:', err?.message || err);
134
+ }
135
+ await this.startSubModules();
136
+ const url = `http://localhost:${this.config.port}`;
137
+ console.log(`\n✓ OPC Studio ready → ${url}`);
138
+ if (this.config.openBrowser) {
139
+ try {
140
+ const { exec } = require('child_process');
141
+ const openCmd = process.platform === 'darwin' ? 'open' : process.platform === 'win32' ? 'start ""' : 'xdg-open';
142
+ exec(`${openCmd} ${url}`);
143
+ }
144
+ catch { }
145
+ }
146
+ console.log('Press Ctrl+C to stop');
147
+ await new Promise(() => { });
113
148
  }
114
149
  async stop() {
115
150
  this.cronEngine.stop();
@@ -122,6 +157,42 @@ class StudioServer {
122
157
  }
123
158
  });
124
159
  }
160
+ async startSubModules() {
161
+ const subModules = [
162
+ { name: 'DeepBrain', icon: '🧠', pkg: 'deepbrain', port: 4001, serveMethod: 'serveUI' },
163
+ { name: 'AgentKits', icon: '📊', pkg: 'agentkits', port: 4002, serveMethod: 'serveUI' },
164
+ { name: 'Workstation', icon: '👤', pkg: 'agent-workstation', port: 4003, serveMethod: 'serveUI' },
165
+ ];
166
+ const moduleStatuses = [];
167
+ for (const mod of subModules) {
168
+ try {
169
+ const already = await this.checkPort(mod.port);
170
+ if (already) {
171
+ moduleStatuses.push(` ✓ ${mod.icon} ${mod.name} already running on :${mod.port}`);
172
+ continue;
173
+ }
174
+ const modExports = await dynamicImport(mod.pkg);
175
+ if (typeof modExports[mod.serveMethod] === 'function') {
176
+ modExports[mod.serveMethod]({ port: mod.port });
177
+ await new Promise(r => setTimeout(r, 600));
178
+ const started = await this.checkPort(mod.port);
179
+ moduleStatuses.push(started
180
+ ? ` ✓ ${mod.icon} ${mod.name} started on :${mod.port}`
181
+ : ` ⚠ ${mod.icon} ${mod.name} failed to start`);
182
+ }
183
+ else {
184
+ moduleStatuses.push(` ✓ ${mod.icon} ${mod.name} installed`);
185
+ }
186
+ }
187
+ catch {
188
+ moduleStatuses.push(` ○ ${mod.icon} ${mod.name} not installed (npm i ${mod.pkg})`);
189
+ }
190
+ }
191
+ if (moduleStatuses.length > 0) {
192
+ console.log('\nModules:');
193
+ moduleStatuses.forEach(s => console.log(s));
194
+ }
195
+ }
125
196
  async handleRequest(req, res) {
126
197
  const url = new URL(req.url || '/', `http://localhost`);
127
198
  // Handle CORS preflight
@@ -326,7 +397,7 @@ class StudioServer {
326
397
  // --- Settings API routes ---
327
398
  if (route === 'settings/models' && req.method === 'GET') {
328
399
  const cfg = loadSettingsConfig();
329
- data = cfg.models || { mode: 'local', provider: 'ollama', chatModel: 'qwen2.5:7b', embeddingModel: 'nomic-embed-text', providers: {} };
400
+ data = cfg.models || { mode: 'local', provider: 'ollama', chatModel: 'qwen2.5:0.5b', embeddingModel: 'nomic-embed-text', providers: {} };
330
401
  res.writeHead(200, { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' });
331
402
  res.end(JSON.stringify(data));
332
403
  return;
@@ -631,6 +702,10 @@ class StudioServer {
631
702
  return;
632
703
  }
633
704
  }
705
+ // Simple chat endpoint: POST /api/chat { message, agentId }
706
+ if (route === 'chat' && req.method === 'POST') {
707
+ return this.handleSimpleChat(req, res);
708
+ }
634
709
  switch (route) {
635
710
  case 'modules':
636
711
  data = await this.getModulesStatus();
@@ -960,7 +1035,7 @@ class StudioServer {
960
1035
  // Try to use the configured LLM to generate the prompt
961
1036
  const config = this.getConfig();
962
1037
  const provider = config.provider || 'ollama';
963
- const model = config.model || 'qwen2.5:7b';
1038
+ const model = config.model || 'qwen2.5:0.5b';
964
1039
  const baseUrl = config.baseUrl || process.env.OPC_LLM_BASE_URL || 'http://localhost:11434/v1';
965
1040
  const apiKey = config.apiKey || process.env.OPC_LLM_API_KEY || 'ollama';
966
1041
  const response = await fetch(`${baseUrl}/chat/completions`, {
@@ -1043,16 +1118,17 @@ class StudioServer {
1043
1118
  try {
1044
1119
  const { BaseAgent } = require('../core/agent');
1045
1120
  const { InMemoryStore } = require('../memory');
1046
- // Determine provider config
1121
+ // Determine provider config from OAD
1047
1122
  let providerName = agent.provider || process.env.OPC_LLM_PROVIDER;
1123
+ let oadConfig = null;
1048
1124
  if (!providerName) {
1049
1125
  try {
1050
1126
  for (const fname of ['oad.yaml', 'agent.yaml']) {
1051
1127
  const oadPath = (0, path_1.join)(this.config.agentDir, fname);
1052
1128
  if ((0, fs_1.existsSync)(oadPath)) {
1053
1129
  const yaml = require('js-yaml');
1054
- const oad = yaml.load((0, fs_1.readFileSync)(oadPath, 'utf-8'));
1055
- providerName = oad?.spec?.provider?.default;
1130
+ oadConfig = yaml.load((0, fs_1.readFileSync)(oadPath, 'utf-8'));
1131
+ providerName = oadConfig?.spec?.provider?.default;
1056
1132
  if (providerName)
1057
1133
  break;
1058
1134
  }
@@ -1061,24 +1137,20 @@ class StudioServer {
1061
1137
  catch { }
1062
1138
  }
1063
1139
  providerName = providerName || 'auto';
1064
- // Build agent dir for this specific agent (for skills, tools, etc.)
1065
- const agentWorkDir = (0, path_1.join)(this.getAgentsDir(), '..', 'workspaces', agentId);
1066
- const skillsDir = (0, fs_1.existsSync)((0, path_1.join)(agentWorkDir, 'skills')) ? (0, path_1.join)(agentWorkDir, 'skills') : undefined;
1067
1140
  const runtimeAgent = new BaseAgent({
1068
1141
  name: agent.name || agentId,
1069
- systemPrompt: agent.systemPrompt || 'You are a helpful assistant. Please reply in Chinese.',
1142
+ systemPrompt: agent.systemPrompt || oadConfig?.spec?.systemPrompt || 'You are a helpful assistant. Please reply in Chinese.',
1070
1143
  provider: providerName,
1071
- model: agent.model !== 'auto' ? agent.model : undefined,
1144
+ model: agent.model !== 'auto' ? agent.model : oadConfig?.spec?.model,
1072
1145
  memory: new InMemoryStore(),
1073
- skillsDir,
1074
- agentDir: agentWorkDir,
1146
+ historyLimit: 50,
1147
+ agentDir: this.config.agentDir,
1148
+ maxToolRounds: 0, // Disable tool calling for now until streaming tool support is added
1075
1149
  });
1076
1150
  await runtimeAgent.init();
1151
+ // TODO: Connect DeepBrain long-term memory when streaming handleMessage is ready
1077
1152
  // TODO: Add function calling tools for OPC assistant
1078
- // Tools needed: createAgent, deleteAgent, listAgents, configureChannel, updateAgent
1079
- // Reference: Hermes Agent pattern - expose Studio APIs as LLM tools
1080
1153
  // Use handleMessage for full pipeline (skills → memory → tools → guardrails → LLM)
1081
- // Then stream the response to client
1082
1154
  const msg = {
1083
1155
  id: `msg_${Date.now()}`,
1084
1156
  role: 'user',
@@ -1088,11 +1160,10 @@ class StudioServer {
1088
1160
  };
1089
1161
  try {
1090
1162
  const response = await runtimeAgent.handleMessage(msg);
1091
- // Stream response word by word for SSE feel
1092
- const words = response.content.split('');
1163
+ const text = response.content;
1093
1164
  const chunkSize = 3;
1094
- for (let i = 0; i < words.length; i += chunkSize) {
1095
- const chunk = words.slice(i, i + chunkSize).join('');
1165
+ for (let i = 0; i < text.length; i += chunkSize) {
1166
+ const chunk = text.slice(i, i + chunkSize);
1096
1167
  const sseData = JSON.stringify({
1097
1168
  choices: [{ delta: { content: chunk }, index: 0 }],
1098
1169
  });
@@ -1109,7 +1180,6 @@ class StudioServer {
1109
1180
  res.end();
1110
1181
  }
1111
1182
  catch (err) {
1112
- // Provider creation failed — send error as SSE so frontend can display it
1113
1183
  const errData = JSON.stringify({
1114
1184
  choices: [{ delta: { content: `⚠️ Provider error: ${err.message}\n\nTip: Install Ollama or set OPENAI_API_KEY.` }, index: 0 }],
1115
1185
  });
@@ -1118,6 +1188,68 @@ class StudioServer {
1118
1188
  res.end();
1119
1189
  }
1120
1190
  }
1191
+ async handleSimpleChat(req, res) {
1192
+ const corsHeaders = { 'Content-Type': 'application/json', 'Access-Control-Allow-Origin': '*' };
1193
+ let body;
1194
+ try {
1195
+ body = JSON.parse(await this.readBody(req));
1196
+ }
1197
+ catch {
1198
+ res.writeHead(400, corsHeaders);
1199
+ res.end(JSON.stringify({ error: 'Invalid JSON body' }));
1200
+ return;
1201
+ }
1202
+ const { message, agentId } = body;
1203
+ if (!message) {
1204
+ res.writeHead(400, corsHeaders);
1205
+ res.end(JSON.stringify({ error: 'message is required' }));
1206
+ return;
1207
+ }
1208
+ // Resolve agent's system prompt
1209
+ let systemPrompt = 'You are a helpful assistant. Please reply in Chinese.';
1210
+ let model = 'qwen2.5:0.5b';
1211
+ if (agentId) {
1212
+ const agent = this.getAgentById(agentId);
1213
+ if (!agent.error) {
1214
+ if (agent.systemPrompt)
1215
+ systemPrompt = agent.systemPrompt;
1216
+ if (agent.model && agent.model !== 'auto')
1217
+ model = agent.model;
1218
+ }
1219
+ }
1220
+ // Read model from settings if available
1221
+ const cfg = loadSettingsConfig();
1222
+ if (cfg.models?.chatModel)
1223
+ model = cfg.models.chatModel;
1224
+ const ollamaBase = process.env.OPC_LLM_BASE_URL || 'http://localhost:11434/v1';
1225
+ try {
1226
+ const ollamaRes = await fetch(`${ollamaBase}/chat/completions`, {
1227
+ method: 'POST',
1228
+ headers: { 'Content-Type': 'application/json', Authorization: 'Bearer ollama' },
1229
+ body: JSON.stringify({
1230
+ model,
1231
+ messages: [
1232
+ { role: 'system', content: systemPrompt },
1233
+ { role: 'user', content: message },
1234
+ ],
1235
+ }),
1236
+ });
1237
+ if (!ollamaRes.ok) {
1238
+ const errText = await ollamaRes.text();
1239
+ res.writeHead(502, corsHeaders);
1240
+ res.end(JSON.stringify({ error: `Ollama error ${ollamaRes.status}: ${errText}` }));
1241
+ return;
1242
+ }
1243
+ const data = await ollamaRes.json();
1244
+ const reply = data.choices?.[0]?.message?.content || '';
1245
+ res.writeHead(200, corsHeaders);
1246
+ res.end(JSON.stringify({ reply, agentId: agentId || 'default', model }));
1247
+ }
1248
+ catch (e) {
1249
+ res.writeHead(502, corsHeaders);
1250
+ res.end(JSON.stringify({ error: `Failed to reach Ollama: ${e.message}` }));
1251
+ }
1252
+ }
1121
1253
  sendSimulatedResponse(res, lastMsg, agent) {
1122
1254
  const response = `Hello! I'm ${agent.name}. You said: "${lastMsg}"\n\nI'm ready to help you. (Note: Connect a model provider for real AI responses)`;
1123
1255
  const words = response.split(' ');