@1mancompany/onemancompany 0.7.77 → 0.7.80

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/frontend/app.js CHANGED
@@ -2265,12 +2265,14 @@ class AppController {
2265
2265
  this._inputHistoryIdx = this._inputHistory.length;
2266
2266
  const doSend = async () => {
2267
2267
  const text = (input?.value || '').trim();
2268
- if (!text) return;
2268
+ const hasPendingFiles = (this._ceoPendingFiles || []).length > 0;
2269
+ if (!text && !hasPendingFiles) return;
2269
2270
 
2270
2271
  // Show typing indicator while waiting for agent response
2271
2272
  this._showCeoTyping();
2272
2273
 
2273
- // Execute slash command if input starts with /command
2274
+ // Execute slash command if input starts with /command (slash actions
2275
+ // pick up pending files via _attachPendingFilesToFormData when needed).
2274
2276
  if (text.startsWith('/')) {
2275
2277
  const cmdText = text.split(' ')[0].toLowerCase();
2276
2278
  const argText = text.slice(cmdText.length).trim();
@@ -2284,8 +2286,8 @@ class AppController {
2284
2286
  }
2285
2287
  }
2286
2288
 
2287
- // Save to input history
2288
- if (!this._inputHistory.length || this._inputHistory[this._inputHistory.length - 1] !== text) {
2289
+ // Save to input history (only when there is actual text)
2290
+ if (text && (!this._inputHistory.length || this._inputHistory[this._inputHistory.length - 1] !== text)) {
2289
2291
  this._inputHistory.push(text);
2290
2292
  if (this._inputHistory.length > 100) this._inputHistory.shift();
2291
2293
  localStorage.setItem('ceo-input-history', JSON.stringify(this._inputHistory));
@@ -2293,8 +2295,20 @@ class AppController {
2293
2295
  this._inputHistoryIdx = this._inputHistory.length;
2294
2296
  input.value = '';
2295
2297
 
2298
+ // Snapshot pending files for this send (display chip + dispatch)
2299
+ const pendingFiles = this._ceoPendingFiles || [];
2300
+ const fileNames = pendingFiles.map(f => f.name);
2301
+
2296
2302
  // Show CEO message immediately in terminal
2297
- this._ceoTerm?.appendCeoMessage(text);
2303
+ const displayText = text || '(attachment)';
2304
+ this._ceoTerm?.appendCeoMessage(displayText);
2305
+ if (fileNames.length) {
2306
+ this._ceoTerm?.appendMessage({
2307
+ role: 'system',
2308
+ text: `\u{1F4CE} ${fileNames.join(', ')}`,
2309
+ source: 'upload',
2310
+ });
2311
+ }
2298
2312
 
2299
2313
  // /iter mode: create new iteration on pending project
2300
2314
  if (this._pendingIterProject) {
@@ -2308,6 +2322,7 @@ class AppController {
2308
2322
  formData.append('mode', 'standard');
2309
2323
  const productId = document.getElementById('ceo-product-select')?.value || '';
2310
2324
  if (productId) formData.append('product_id', productId);
2325
+ this._attachPendingFilesToFormData(formData);
2311
2326
  await fetch('/api/ceo/task', { method: 'POST', body: formData });
2312
2327
  await this._refreshCeoProjectList();
2313
2328
  this._ceoTerm?.appendMessage({ role: 'system', text: 'New iteration created.', source: 'system' });
@@ -2316,7 +2331,7 @@ class AppController {
2316
2331
  return;
2317
2332
  }
2318
2333
 
2319
- // Meeting mode: send via meeting/chat API
2334
+ // Meeting mode: send via meeting/chat API (no attachment support yet)
2320
2335
  if (this._currentConvType === 'meeting') {
2321
2336
  try {
2322
2337
  const res = await fetch('/api/meeting/chat', {
@@ -2346,10 +2361,14 @@ class AppController {
2346
2361
  // 1-on-1 conversation mode: send via conversation API
2347
2362
  if (this._currentConvType === 'oneonone' && this._currentConvId) {
2348
2363
  try {
2364
+ const uploaded = await this._uploadCeoPendingFiles();
2349
2365
  await fetch(`/api/conversation/${this._currentConvId}/message`, {
2350
2366
  method: 'POST',
2351
2367
  headers: {'Content-Type': 'application/json'},
2352
- body: JSON.stringify({ text }),
2368
+ body: JSON.stringify({
2369
+ text: text || '(attachment)',
2370
+ attachments: uploaded.map(a => a.path),
2371
+ }),
2353
2372
  });
2354
2373
  } catch (e) { console.error('Failed to send 1-on-1 message:', e); }
2355
2374
  input?.focus();
@@ -2359,10 +2378,14 @@ class AppController {
2359
2378
  if (this._currentCeoProject === this._EA_CHAT && this._eaChatConvId) {
2360
2379
  // EA Chat: send as conversation message — EA decides whether to create project
2361
2380
  try {
2381
+ const uploaded = await this._uploadCeoPendingFiles();
2362
2382
  await fetch(`/api/conversation/${this._eaChatConvId}/message`, {
2363
2383
  method: 'POST',
2364
2384
  headers: {'Content-Type': 'application/json'},
2365
- body: JSON.stringify({ text }),
2385
+ body: JSON.stringify({
2386
+ text: text || '(attachment)',
2387
+ attachments: uploaded.map(a => a.path),
2388
+ }),
2366
2389
  });
2367
2390
  // EA response arrives via WebSocket conversation_message event
2368
2391
  } catch (e) { console.error('Failed to send EA chat message:', e); }
@@ -2376,15 +2399,21 @@ class AppController {
2376
2399
  formData.append('mode', mode);
2377
2400
  const productId2 = document.getElementById('ceo-product-select')?.value || '';
2378
2401
  if (productId2) formData.append('product_id', productId2);
2402
+ this._attachPendingFilesToFormData(formData);
2379
2403
  await fetch('/api/ceo/task', { method: 'POST', body: formData });
2380
2404
  await this._refreshCeoProjectList();
2381
2405
  } catch (e) { console.error('Failed to submit task:', e); }
2382
2406
  } else {
2383
2407
  try {
2408
+ const projectId = this._currentCeoProject.split('/')[0];
2409
+ const uploaded = await this._uploadCeoPendingFiles(projectId);
2384
2410
  await fetch(`/api/ceo/sessions/${encodeURIComponent(this._currentCeoProject)}/message`, {
2385
2411
  method: 'POST',
2386
2412
  headers: {'Content-Type': 'application/json'},
2387
- body: JSON.stringify({text}),
2413
+ body: JSON.stringify({
2414
+ text: text || '(attachment)',
2415
+ attachments: uploaded.map(a => a.path),
2416
+ }),
2388
2417
  });
2389
2418
  await this._refreshCeoProjectList();
2390
2419
  } catch (e) { console.error('Failed to send:', e); }
@@ -2471,16 +2500,12 @@ class AppController {
2471
2500
  this._handleMentionInput(input);
2472
2501
  });
2473
2502
 
2474
- // File upload
2503
+ // File upload — files queue locally and dispatch with the next message
2504
+ this._ceoPendingFiles = this._ceoPendingFiles || [];
2475
2505
  const fileInput = document.getElementById('ceo-file-input');
2476
2506
  fileInput?.addEventListener('change', () => {
2477
2507
  if (!fileInput.files?.length) return;
2478
- const names = Array.from(fileInput.files).map(f => f.name).join(', ');
2479
- this._ceoTerm?.appendMessage({
2480
- role: 'system', text: `Attached: ${names}`, source: 'upload',
2481
- });
2482
- // Store files for next send
2483
- this._pendingFiles = Array.from(fileInput.files);
2508
+ this._handleCeoFileSelect(fileInput.files);
2484
2509
  fileInput.value = '';
2485
2510
  });
2486
2511
 
@@ -2574,6 +2599,7 @@ class AppController {
2574
2599
  const formData = new FormData();
2575
2600
  formData.append('task', arg);
2576
2601
  formData.append('mode', 'standard');
2602
+ this._attachPendingFilesToFormData(formData);
2577
2603
  fetch('/api/ceo/task', { method: 'POST', body: formData })
2578
2604
  .then(() => { this._refreshCeoProjectList(); this._ceoTerm?.appendMessage({ role: 'system', text: '✓ Project created', source: 'system' }); })
2579
2605
  .catch(e => { this._ceoTerm?.appendMessage({ role: 'system', text: `✗ Failed: ${e.message}`, source: 'system' }); });
@@ -2595,6 +2621,7 @@ class AppController {
2595
2621
  formData.append('task', arg);
2596
2622
  formData.append('project_id', pid.split('/')[0]);
2597
2623
  formData.append('mode', 'standard');
2624
+ this._attachPendingFilesToFormData(formData);
2598
2625
  fetch('/api/ceo/task', { method: 'POST', body: formData })
2599
2626
  .then(() => { this._refreshCeoProjectList(); this._ceoTerm?.appendMessage({ role: 'system', text: '✓ New iteration created', source: 'system' }); })
2600
2627
  .catch(e => { this._ceoTerm?.appendMessage({ role: 'system', text: `✗ Failed: ${e.message}`, source: 'system' }); });
@@ -2622,6 +2649,7 @@ class AppController {
2622
2649
  const formData = new FormData();
2623
2650
  formData.append('task', arg);
2624
2651
  formData.append('mode', 'simple');
2652
+ this._attachPendingFilesToFormData(formData);
2625
2653
  fetch('/api/ceo/task', { method: 'POST', body: formData })
2626
2654
  .then(() => { this._refreshCeoProjectList(); this._ceoTerm?.appendMessage({ role: 'system', text: '✓ Simple task created', source: 'system' }); })
2627
2655
  .catch(e => { this._ceoTerm?.appendMessage({ role: 'system', text: `✗ Failed: ${e.message}`, source: 'system' }); });
@@ -2668,7 +2696,6 @@ class AppController {
2668
2696
  { cmd: '/discuss', desc: 'Start discussion meeting (open floor)', action: async (arg) => {
2669
2697
  await this._startMeetingInConsole('discussion', arg);
2670
2698
  }},
2671
- { cmd: '/attach', desc: 'Attach file or image', action: () => document.getElementById('ceo-file-input')?.click() },
2672
2699
  { cmd: '/clear', desc: 'Clear EA chat history', action: async () => {
2673
2700
  if (this._currentCeoProject !== this._EA_CHAT) {
2674
2701
  this._ceoTerm?.appendMessage({ role: 'system', text: '/clear only works in EA chat.', source: 'system' });
@@ -6519,6 +6546,97 @@ class AppController {
6519
6546
  return uploaded;
6520
6547
  }
6521
6548
 
6549
+ // ===== CEO Console File Upload (queued, dispatched with the next message) =====
6550
+ _handleCeoFileSelect(files) {
6551
+ if (!this._ceoPendingFiles) this._ceoPendingFiles = [];
6552
+ for (const file of files) {
6553
+ const reader = new FileReader();
6554
+ reader.onload = (e) => {
6555
+ let type = 'file';
6556
+ if (file.type.startsWith('image/')) type = 'image';
6557
+ else if (file.type.startsWith('video/')) type = 'video';
6558
+ this._ceoPendingFiles.push({
6559
+ name: file.name,
6560
+ type,
6561
+ dataUrl: e.target.result,
6562
+ file,
6563
+ });
6564
+ this._updateCeoPreviewBar();
6565
+ };
6566
+ reader.readAsDataURL(file);
6567
+ }
6568
+ }
6569
+
6570
+ _updateCeoPreviewBar() {
6571
+ const bar = document.getElementById('ceo-preview-bar');
6572
+ if (!bar) return;
6573
+ if (!this._ceoPendingFiles || !this._ceoPendingFiles.length) {
6574
+ bar.classList.add('hidden');
6575
+ bar.innerHTML = '';
6576
+ return;
6577
+ }
6578
+ bar.classList.remove('hidden');
6579
+ bar.innerHTML = '';
6580
+ this._ceoPendingFiles.forEach((f, idx) => {
6581
+ const item = document.createElement('div');
6582
+ item.className = 'chat-preview-item';
6583
+ if (f.type === 'image') {
6584
+ item.innerHTML = `<img class="chat-preview-thumb" src="${f.dataUrl}" alt="${this._escapeHtml(f.name)}" />`;
6585
+ } else if (f.type === 'video') {
6586
+ item.innerHTML = `<div class="chat-preview-file">\u{1F3AC}<br>${this._escapeHtml(f.name.substring(0, 12))}</div>`;
6587
+ } else {
6588
+ item.innerHTML = `<div class="chat-preview-file">\u{1F4C4}<br>${this._escapeHtml(f.name.substring(0, 12))}</div>`;
6589
+ }
6590
+ const removeBtn = document.createElement('button');
6591
+ removeBtn.className = 'chat-preview-remove';
6592
+ removeBtn.textContent = '×';
6593
+ removeBtn.title = `Remove ${f.name}`;
6594
+ removeBtn.onclick = () => {
6595
+ this._ceoPendingFiles.splice(idx, 1);
6596
+ this._updateCeoPreviewBar();
6597
+ };
6598
+ item.appendChild(removeBtn);
6599
+ bar.appendChild(item);
6600
+ });
6601
+ }
6602
+
6603
+ async _uploadCeoPendingFiles(projectId = '') {
6604
+ if (!this._ceoPendingFiles || !this._ceoPendingFiles.length) return [];
6605
+ const uploaded = [];
6606
+ const url = projectId
6607
+ ? `/api/upload?project_id=${encodeURIComponent(projectId)}`
6608
+ : '/api/upload';
6609
+ for (const f of this._ceoPendingFiles) {
6610
+ const formData = new FormData();
6611
+ formData.append('file', f.file, f.name);
6612
+ try {
6613
+ const resp = await fetch(url, { method: 'POST', body: formData });
6614
+ const data = await resp.json();
6615
+ if (data.path) {
6616
+ uploaded.push({
6617
+ path: data.path,
6618
+ filename: data.filename || f.name,
6619
+ content_type: data.content_type || '',
6620
+ });
6621
+ }
6622
+ } catch (err) {
6623
+ console.error('CEO upload failed:', err);
6624
+ }
6625
+ }
6626
+ this._ceoPendingFiles = [];
6627
+ this._updateCeoPreviewBar();
6628
+ return uploaded;
6629
+ }
6630
+
6631
+ _attachPendingFilesToFormData(formData) {
6632
+ if (!this._ceoPendingFiles || !this._ceoPendingFiles.length) return;
6633
+ for (const f of this._ceoPendingFiles) {
6634
+ formData.append('files', f.file, f.name);
6635
+ }
6636
+ this._ceoPendingFiles = [];
6637
+ this._updateCeoPreviewBar();
6638
+ }
6639
+
6522
6640
  // ===== Tool Detail — Dynamic Section Renderer Framework =====
6523
6641
  //
6524
6642
  // Each tool's definition returns a `sections` array from the backend.
@@ -117,9 +117,13 @@
117
117
  <option value="">No Product</option>
118
118
  </select>
119
119
  </div>
120
+ <div id="ceo-preview-bar" class="chat-preview-bar hidden"></div>
120
121
  <div id="ceo-conv-input-row">
122
+ <label class="chat-attach-btn" id="ceo-attach-btn" title="Attach file or image — sent with your next message">
123
+ <input type="file" id="ceo-file-input" multiple hidden />
124
+ &#128206;
125
+ </label>
121
126
  <textarea id="ceo-conv-input" placeholder="$ Type message, / for commands (Enter to send)" rows="1" aria-label="CEO message input"></textarea>
122
- <input type="file" id="ceo-file-input" multiple hidden />
123
127
  </div>
124
128
  <div id="ceo-slash-menu" class="hidden"></div>
125
129
  </div>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@1mancompany/onemancompany",
3
- "version": "0.7.77",
3
+ "version": "0.7.80",
4
4
  "description": "The AI Operating System for One-Person Companies",
5
5
  "bin": {
6
6
  "onemancompany": "bin/cli.js"
package/pyproject.toml CHANGED
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "onemancompany"
3
- version = "0.7.77"
3
+ version = "0.7.80"
4
4
  description = "A one-man company simulation with pixel art visualization and LangChain AI agents"
5
5
  requires-python = ">=3.12"
6
6
  dependencies = [
@@ -3,6 +3,7 @@
3
3
  from __future__ import annotations
4
4
 
5
5
  import asyncio
6
+ import json as _json
6
7
  import re
7
8
  import shutil
8
9
  import uuid as _uuid
@@ -136,6 +137,11 @@ def _save_file_deduped(upload_dir: Path, filename: str, content: bytes) -> Path:
136
137
  return dest
137
138
 
138
139
 
140
+ from onemancompany.core.attachments import (
141
+ build_attachment_prompt as _build_attachment_prompt,
142
+ )
143
+
144
+
139
145
  def _get_employee_manager():
140
146
  """Lazy import to avoid circular dependency."""
141
147
  from onemancompany.core.vessel import employee_manager
@@ -572,10 +578,7 @@ async def ceo_submit_task(
572
578
  ctx_id = f"{pid}/{iter_id}" if iter_id else pid
573
579
 
574
580
  # Build attachment info string for EA
575
- attach_info = ""
576
- if attachments:
577
- lines = [f"- Attachment: {a['filename']} (saved at {a['path']})" for a in attachments]
578
- attach_info = "\n\nCEO attached the following files:\n" + "\n".join(lines)
581
+ attach_info = _build_attachment_prompt(attachments)
579
582
 
580
583
  loop = get_agent_loop(EA_ID)
581
584
  if loop:
@@ -723,6 +726,9 @@ async def task_followup(project_id: str, body: dict) -> dict:
723
726
  if work_summary_lines:
724
727
  context_parts.append(f"Previous work results:\n" + "\n".join(work_summary_lines) + "\n")
725
728
  context_parts.append(f"CEO follow-up instructions: {instructions}\n")
729
+ attach_info = _build_attachment_prompt(body.get("attachments") or [])
730
+ if attach_info:
731
+ context_parts.append(attach_info.lstrip("\n") + "\n")
726
732
  context_parts.append(
727
733
  f"\nBuild on the existing work — do NOT redo completed subtasks unless the CEO explicitly asks."
728
734
  f" Use dispatch_child() if subtasks are needed.\n\n"
@@ -833,10 +839,7 @@ async def oneonone_chat(body: dict) -> dict:
833
839
  return {"error": f"Employee '{employee_id}' not found"}
834
840
 
835
841
  # Build attachment info string for prompt injection
836
- attach_info = ""
837
- if attachments:
838
- lines = [f"- Attachment: {a.get('filename', 'file')} (saved at {a.get('path', '')})" for a in attachments]
839
- attach_info = "\n\nCEO attached the following files:\n" + "\n".join(lines)
842
+ attach_info = _build_attachment_prompt(attachments)
840
843
 
841
844
  # On first message (empty history), mark employee as in meeting
842
845
  if not history and emp_data:
@@ -908,7 +911,7 @@ async def oneonone_chat(body: dict) -> dict:
908
911
  messages.append(HumanMessage(content=entry["content"]))
909
912
  elif entry.get("role") == "employee":
910
913
  messages.append(AIMessage(content=entry["content"]))
911
- messages.append(HumanMessage(content=message))
914
+ messages.append(HumanMessage(content=message + attach_info))
912
915
 
913
916
  llm = make_llm(employee_id)
914
917
  result = await _llm_invoke_with_retry(llm, messages, category="oneonone", employee_id=employee_id)
@@ -6364,13 +6367,17 @@ async def send_ceo_session_message(project_id: str, body: dict):
6364
6367
 
6365
6368
  # Persist CEO message (masked for credential requests)
6366
6369
  display_text = result.get("display_text", text)
6367
- await service.send_message(conv.id, "ceo", "CEO", display_text, mentions=mentions)
6370
+ await service.send_message(
6371
+ conv.id, "ceo", "CEO", display_text,
6372
+ mentions=mentions, attachments=body.get("attachments") or [],
6373
+ )
6368
6374
 
6369
6375
  if result["type"] == "followup":
6370
6376
  # Dispatch as a CEO_FOLLOWUP via the existing task_followup logic.
6371
6377
  try:
6372
6378
  followup_result = await task_followup(
6373
- project_id, {"instructions": text}
6379
+ project_id,
6380
+ {"instructions": text, "attachments": body.get("attachments") or []},
6374
6381
  )
6375
6382
  result["followup"] = followup_result
6376
6383
  result["message"] = "Follow-up instruction dispatched"
@@ -0,0 +1,53 @@
1
+ """Shared helpers for rendering attachment instructions into LLM prompts.
2
+
3
+ The CEO submits attachments through multiple paths (task creation, 1-on-1
4
+ chat, EA / project conversations). All of them need to tell the agent to
5
+ read each attached file before responding. The renderer is centralized
6
+ here so prompt wording and quoting rules stay consistent.
7
+ """
8
+
9
+ from __future__ import annotations
10
+
11
+ import json as _json
12
+ from pathlib import Path
13
+
14
+
15
+ def build_attachment_prompt(attachments: list[dict]) -> str:
16
+ """Describe attachments with both display and read-safe path formats.
17
+
18
+ The plain path in ``(saved at ...)`` is for human readability. The
19
+ ``read("...")`` argument and attachment display name use JSON quoting so
20
+ backslashes, quotes, and newlines stay safe/parsable without inventing
21
+ custom escaping rules.
22
+ """
23
+ if not attachments:
24
+ return ""
25
+
26
+ lines: list[str] = []
27
+ for attachment in attachments:
28
+ raw_filename = attachment.get("filename", "file")
29
+ quoted_filename = _json.dumps("file" if raw_filename is None else str(raw_filename))
30
+ raw_path = attachment.get("path", "")
31
+ path = "" if raw_path is None else str(raw_path).strip()
32
+ if path:
33
+ lines.append(f"- Attachment: {quoted_filename} (saved at {path}) [read({_json.dumps(path)})]")
34
+ else:
35
+ lines.append(f"- Attachment: {quoted_filename}")
36
+
37
+ return (
38
+ "\n\nCEO attached the following files:\n"
39
+ + "\n".join(lines)
40
+ + "\nRead each attachment with read() before responding so you can inspect the actual file contents."
41
+ )
42
+
43
+
44
+ def build_attachment_prompt_from_paths(paths: list[str]) -> str:
45
+ """Same as :func:`build_attachment_prompt`, but accepts bare path strings.
46
+
47
+ Used by the conversation flow where ``Message.attachments`` stores just
48
+ saved-file paths. The display filename is derived from the basename.
49
+ """
50
+ if not paths:
51
+ return ""
52
+ attachments = [{"filename": Path(p).name, "path": p} for p in paths if p]
53
+ return build_attachment_prompt(attachments)
@@ -151,6 +151,9 @@ def _build_conversation_prompt(
151
151
  lines.append(f"[{msg.role}]: {msg.text}")
152
152
 
153
153
  lines.append(f"\n[{new_message.role}]: {new_message.text}")
154
+ if new_message.attachments:
155
+ from onemancompany.core.attachments import build_attachment_prompt_from_paths
156
+ lines.append(build_attachment_prompt_from_paths(list(new_message.attachments)))
154
157
  lines.append("\nPlease respond:")
155
158
  return "\n".join(lines)
156
159
 
@@ -741,7 +741,7 @@ def _step_execute(
741
741
 
742
742
  # 6. Generate MCP configs for founding employees
743
743
  with console.status(" Generating MCP configs..."):
744
- _generate_mcp_configs(extras.get(ENV_KEY_SKILLSMP, ""))
744
+ _generate_mcp_configs(extras.get(ENV_KEY_SKILLSMP, ""), host, port)
745
745
  console.print(" [green]\u2714[/green] MCP configs generated for founding employees")
746
746
 
747
747
  # 7. Apply agent family (hosting) assignments to founding employees
@@ -830,13 +830,15 @@ def _assign_default_avatars(console: Console) -> None:
830
830
  console.print(" [green]\u2714[/green] Founding employees already have avatars")
831
831
 
832
832
 
833
- def _generate_mcp_configs(skillsmp_key: str) -> None:
833
+ def _generate_mcp_configs(skillsmp_key: str, host: str, port: int) -> None:
834
834
  """Generate mcp_config.json for founding employees."""
835
835
  import sys
836
836
 
837
837
  python_path = sys.executable
838
838
  from onemancompany.core.config import EXEC_IDS
839
839
  exec_ids = sorted(EXEC_IDS)
840
+ server_host = "localhost" if host == "0.0.0.0" else host
841
+ server_url = f"http://{server_host}:{port}"
840
842
 
841
843
  for emp_id in exec_ids:
842
844
  emp_dir = EMPLOYEES_DIR / emp_id
@@ -852,7 +854,7 @@ def _generate_mcp_configs(skillsmp_key: str) -> None:
852
854
  ENV_OMC_TASK_ID: "",
853
855
  ENV_OMC_PROJECT_ID: "",
854
856
  ENV_OMC_PROJECT_DIR: "",
855
- ENV_OMC_SERVER_URL: "http://localhost:8000",
857
+ ENV_OMC_SERVER_URL: server_url,
856
858
  },
857
859
  },
858
860
  }