opc-agent 4.1.23 → 4.2.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 (103) hide show
  1. package/CHANGELOG.md +59 -119
  2. package/COMPETITIVE-GAP.md +92 -92
  3. package/CONTRIBUTING.md +36 -36
  4. package/README.md +290 -290
  5. package/README.zh-CN.md +269 -269
  6. package/dist/channels/telegram.d.ts +0 -5
  7. package/dist/channels/telegram.d.ts.map +1 -1
  8. package/dist/channels/telegram.js +0 -108
  9. package/dist/channels/telegram.js.map +1 -1
  10. package/dist/channels/voice.d.ts +97 -71
  11. package/dist/channels/voice.d.ts.map +1 -1
  12. package/dist/channels/voice.js +347 -369
  13. package/dist/channels/voice.js.map +1 -1
  14. package/dist/channels/web.d.ts.map +1 -1
  15. package/dist/channels/web.js +2 -8
  16. package/dist/channels/web.js.map +1 -1
  17. package/dist/channels/wechat.js +6 -6
  18. package/dist/cli/chat.d.ts +1 -4
  19. package/dist/cli/chat.d.ts.map +1 -1
  20. package/dist/cli/chat.js +73 -680
  21. package/dist/cli/chat.js.map +1 -1
  22. package/dist/cli/setup.js +1 -1
  23. package/dist/cli/setup.js.map +1 -1
  24. package/dist/cli.js +280 -373
  25. package/dist/cli.js.map +1 -1
  26. package/dist/core/agent.d.ts +0 -1
  27. package/dist/core/agent.d.ts.map +1 -1
  28. package/dist/core/agent.js +0 -3
  29. package/dist/core/agent.js.map +1 -1
  30. package/dist/core/runtime.d.ts.map +1 -1
  31. package/dist/core/runtime.js +22 -192
  32. package/dist/core/runtime.js.map +1 -1
  33. package/dist/deploy/index.js +56 -56
  34. package/dist/doctor.d.ts +0 -1
  35. package/dist/doctor.d.ts.map +1 -1
  36. package/dist/doctor.js +10 -155
  37. package/dist/doctor.js.map +1 -1
  38. package/dist/index.d.ts +3 -4
  39. package/dist/index.d.ts.map +1 -1
  40. package/dist/index.js +9 -9
  41. package/dist/index.js.map +1 -1
  42. package/dist/memory/deepbrain.d.ts +1 -1
  43. package/dist/memory/deepbrain.d.ts.map +1 -1
  44. package/dist/memory/deepbrain.js +4 -95
  45. package/dist/memory/deepbrain.js.map +1 -1
  46. package/dist/memory/index.d.ts +0 -2
  47. package/dist/memory/index.d.ts.map +1 -1
  48. package/dist/memory/index.js +1 -3
  49. package/dist/memory/index.js.map +1 -1
  50. package/dist/memory/user-profiler.d.ts +0 -8
  51. package/dist/memory/user-profiler.d.ts.map +1 -1
  52. package/dist/memory/user-profiler.js +0 -89
  53. package/dist/memory/user-profiler.js.map +1 -1
  54. package/dist/scheduler/cron-engine.d.ts.map +1 -1
  55. package/dist/scheduler/cron-engine.js +36 -3
  56. package/dist/scheduler/cron-engine.js.map +1 -1
  57. package/dist/skills/auto-learn.d.ts.map +1 -1
  58. package/dist/skills/auto-learn.js +11 -65
  59. package/dist/skills/auto-learn.js.map +1 -1
  60. package/dist/skills/builtin/index.d.ts.map +1 -1
  61. package/dist/skills/builtin/index.js +30 -163
  62. package/dist/skills/builtin/index.js.map +1 -1
  63. package/dist/skills/types.d.ts +1 -1
  64. package/dist/skills/types.d.ts.map +1 -1
  65. package/dist/skills/types.js +0 -1
  66. package/dist/skills/types.js.map +1 -1
  67. package/dist/studio/server.d.ts +0 -1
  68. package/dist/studio/server.d.ts.map +1 -1
  69. package/dist/studio/server.js +12 -142
  70. package/dist/studio/server.js.map +1 -1
  71. package/dist/studio-ui/index.html +26 -365
  72. package/dist/ui/components.js +105 -105
  73. package/examples/README.md +22 -22
  74. package/examples/basic-agent.ts +90 -90
  75. package/examples/brain-integration.ts +71 -71
  76. package/examples/multi-channel.ts +74 -74
  77. package/install.ps1 +127 -127
  78. package/install.sh +154 -154
  79. package/models.json +164 -164
  80. package/package.json +63 -66
  81. package/scripts/install.ps1 +31 -31
  82. package/scripts/install.sh +40 -40
  83. package/templates/ecommerce-assistant/README.md +45 -45
  84. package/templates/ecommerce-assistant/oad.yaml +47 -47
  85. package/templates/tech-support/README.md +43 -43
  86. package/templates/tech-support/oad.yaml +45 -45
  87. package/.opc/memory.db +0 -0
  88. package/dist/core/model-recommender.d.ts +0 -40
  89. package/dist/core/model-recommender.d.ts.map +0 -1
  90. package/dist/core/model-recommender.js +0 -186
  91. package/dist/core/model-recommender.js.map +0 -1
  92. package/dist/memory/evolve-engine.d.ts +0 -113
  93. package/dist/memory/evolve-engine.d.ts.map +0 -1
  94. package/dist/memory/evolve-engine.js +0 -549
  95. package/dist/memory/evolve-engine.js.map +0 -1
  96. package/dist/memory/sqlite-store.d.ts +0 -40
  97. package/dist/memory/sqlite-store.d.ts.map +0 -1
  98. package/dist/memory/sqlite-store.js +0 -269
  99. package/dist/memory/sqlite-store.js.map +0 -1
  100. package/dist/scheduler/proactive.d.ts +0 -62
  101. package/dist/scheduler/proactive.d.ts.map +0 -1
  102. package/dist/scheduler/proactive.js +0 -185
  103. package/dist/scheduler/proactive.js.map +0 -1
@@ -388,9 +388,6 @@
388
388
  <div class="nav-item" data-page="global-models" onclick="navigate('global-models')">
389
389
  <span class="icon">🧠</span> Models
390
390
  </div>
391
- <div class="nav-item" data-page="global-channels" onclick="navigate('global-channels')">
392
- <span class="icon">📡</span> Channels
393
- </div>
394
391
  <div class="nav-item" data-page="global-memory" onclick="navigate('global-memory')">
395
392
  <span class="icon">💾</span> Memory
396
393
  </div>
@@ -422,14 +419,6 @@
422
419
  </div>
423
420
  <button class="btn btn-primary" onclick="navigate('create')">✨ Create New Agent</button>
424
421
  </div>
425
- <!-- Dashboard Stats -->
426
- <div class="card-grid" style="margin-bottom:24px;" id="dashboard-stats"></div>
427
- <!-- Quick Actions -->
428
- <div style="display:flex;gap:12px;flex-wrap:wrap;margin-bottom:24px;" id="dashboard-actions">
429
- <button class="btn btn-secondary" onclick="navigate('create')">✨ Create Agent</button>
430
- <button class="btn btn-secondary" onclick="navigate('global-models')">🧠 Configure Model</button>
431
- <button class="btn btn-secondary" onclick="navigate('templates')">📋 Browse Templates</button>
432
- </div>
433
422
  <!-- Health Status Section -->
434
423
  <div id="health-section" style="margin-bottom:24px;"></div>
435
424
  <div id="agents-list" class="card-grid"></div>
@@ -481,46 +470,31 @@
481
470
  <div class="agent-settings-content" id="agent-settings-content">
482
471
  <div class="agent-tab-panel active" id="atab-role">
483
472
  <h3>角色配置</h3>
484
- <div class="form-group"><label class="label">Agent 名称</label><input class="input" id="atab-role-name" placeholder="Agent name"></div>
485
- <div class="form-group"><label class="label">描述</label><textarea class="input" id="atab-role-desc" rows="2" placeholder="Brief description..."></textarea></div>
486
- <div class="form-group"><label class="label">System Prompt</label><textarea class="input" id="atab-role-prompt" rows="6" placeholder="You are a helpful assistant..."></textarea></div>
487
- <button class="btn btn-primary" onclick="saveAgentRole()">💾 保存</button>
488
- <span id="atab-role-status" style="margin-left:12px;font-size:18px;"></span>
473
+ <p style="color:var(--text-muted)">Agent 角色和人设配置(即将上线)</p>
489
474
  </div>
490
475
  <div class="agent-tab-panel" id="atab-models">
491
476
  <h3>模型配置</h3>
492
- <div class="form-group">
493
- <label style="display:flex;align-items:center;gap:8px;cursor:pointer;font-size:18px;"><input type="checkbox" id="atab-model-override"> 覆盖全局模型设置</label>
494
- </div>
495
- <div id="atab-model-fields" style="display:none;">
496
- <div class="form-group"><label class="label">Provider</label><select class="input" id="atab-model-provider"><option value="ollama">Ollama (Local)</option><option value="openai">OpenAI</option><option value="anthropic">Anthropic</option><option value="deepseek">DeepSeek</option></select></div>
497
- <div class="form-group"><label class="label">Model</label><input class="input" id="atab-model-name" placeholder="e.g. gpt-4o-mini"></div>
498
- <div class="form-group"><label class="label">Temperature</label><input class="input" id="atab-model-temp" type="number" min="0" max="2" step="0.1" value="0.7"></div>
499
- </div>
500
- <button class="btn btn-primary" onclick="saveAgentModel()">💾 保存</button>
501
- <span id="atab-model-status" style="margin-left:12px;font-size:18px;"></span>
477
+ <p style="color:var(--text-muted)">Agent 使用的模型配置(即将上线)</p>
502
478
  </div>
503
479
  <div class="agent-tab-panel" id="atab-channels">
504
480
  <h3>渠道配置</h3>
505
- <div id="atab-channels-list" style="font-size:18px;color:var(--text-muted);">加载中...</div>
506
- <button class="btn btn-primary" style="margin-top:16px;" onclick="saveAgentChannels()">💾 保存</button>
507
- <span id="atab-channels-status" style="margin-left:12px;font-size:18px;"></span>
481
+ <p style="color:var(--text-muted)">Agent 接入的渠道配置(即将上线)</p>
508
482
  </div>
509
483
  <div class="agent-tab-panel" id="atab-memory">
510
484
  <h3>记忆管理</h3>
511
- <div id="atab-memory-list" style="font-size:18px;color:var(--text-muted);">加载中...</div>
485
+ <p style="color:var(--text-muted)">Agent 记忆和知识库(即将上线)</p>
512
486
  </div>
513
487
  <div class="agent-tab-panel" id="atab-skills">
514
488
  <h3>技能配置</h3>
515
- <div id="atab-skills-list" style="font-size:18px;color:var(--text-muted);">加载中...</div>
489
+ <p style="color:var(--text-muted)">Agent 已安装的技能(即将上线)</p>
516
490
  </div>
517
491
  <div class="agent-tab-panel" id="atab-schedules">
518
492
  <h3>定时任务</h3>
519
- <div id="atab-schedules-list" style="font-size:18px;color:var(--text-muted);">加载中...</div>
493
+ <p style="color:var(--text-muted)">Agent 定时任务配置(即将上线)</p>
520
494
  </div>
521
495
  <div class="agent-tab-panel" id="atab-usage">
522
496
  <h3>用量统计</h3>
523
- <div id="atab-usage-content" style="font-size:18px;color:var(--text-muted);">加载中...</div>
497
+ <p style="color:var(--text-muted)">Agent 使用量和成本(即将上线)</p>
524
498
  </div>
525
499
  </div>
526
500
  </div>
@@ -633,42 +607,22 @@
633
607
  <label class="label">Company / Business Description</label>
634
608
  <textarea class="input" id="agent-desc" rows="3" placeholder="Brief description of your business so the agent can better help you..."></textarea>
635
609
  </div>
636
-
637
- <!-- AI Prompt Generator -->
638
- <div class="form-group">
639
- <label class="label">What should this agent do? <span style="color:var(--text-dim);font-size:13px;">(Describe in plain words, AI generates the prompt)</span></label>
640
- <textarea class="input" id="agent-task-desc" rows="2" placeholder="e.g. Help me answer customer questions about our products, be friendly and professional..."></textarea>
641
- <button class="btn btn-sm" style="margin-top:8px;background:var(--accent-light);color:var(--accent);border:1px solid var(--border);" onclick="generatePrompt()">✨ AI Generate Prompt</button>
642
- <textarea class="input" id="agent-prompt" rows="4" placeholder="System prompt will appear here (or write your own)..." style="margin-top:8px;display:none;"></textarea>
643
- </div>
644
-
645
610
  <div class="form-group">
646
611
  <label class="label">AI Model</label>
647
612
  <select class="input" id="agent-model">
648
- <option value="auto">🤖 Auto-detect (Ollama local first)</option>
649
- <option value="ollama/qwen2.5:7b">🏠 Ollama: Qwen 2.5 7B (Free, Local)</option>
650
- <option value="ollama/llama3.1:8b">🏠 Ollama: Llama 3.1 8B (Free, Local)</option>
651
- <option value="ollama/qwen2.5:0.5b">🏠 Ollama: Qwen 2.5 0.5B (Ultra-fast)</option>
652
- <option value="gpt-4o-mini">☁️ GPT-4o Mini (Fast & Affordable)</option>
653
- <option value="gpt-4o">☁️ GPT-4o (Most Capable)</option>
654
- <option value="claude-sonnet-4">☁️ Claude Sonnet (Balanced)</option>
655
- <option value="deepseek-v3">☁️ DeepSeek V3 (Affordable)</option>
656
- <option value="gemini-2.0-flash">☁️ Gemini Flash (Google)</option>
613
+ <option value="gpt-4o-mini">GPT-4o Mini (Fast & Affordable) ⭐ Recommended</option>
614
+ <option value="gpt-4o">GPT-4o (Most Capable)</option>
615
+ <option value="claude-sonnet-4">Claude Sonnet (Balanced)</option>
616
+ <option value="claude-haiku">Claude Haiku (Fast)</option>
617
+ <option value="gemini-2.0-flash">Gemini 2.0 Flash (Google)</option>
618
+ <option value="deepseek-v3">DeepSeek V3 (Open Source)</option>
657
619
  </select>
658
- <p style="color:var(--text-dim);font-size:12px;margin-top:4px;">🏠 = runs locally, free forever. ☁️ = needs API key.</p>
659
620
  </div>
660
-
661
- <!-- Skill Toggles -->
662
- <div class="form-group">
663
- <label class="label">Enable Skills <span style="color:var(--text-dim);font-size:13px;">(What can your agent do?)</span></label>
664
- <div id="skill-toggles" style="display:grid;grid-template-columns:repeat(auto-fill,minmax(200px,1fr));gap:8px;max-height:200px;overflow-y:auto;padding:4px;"></div>
665
- </div>
666
-
667
621
  <div class="form-group">
668
622
  <label class="label">Language Preference</label>
669
623
  <select class="input" id="agent-lang">
670
- <option value="zh">中文</option>
671
624
  <option value="en">English</option>
625
+ <option value="zh">中文</option>
672
626
  <option value="auto">Auto-detect</option>
673
627
  </select>
674
628
  </div>
@@ -781,10 +735,6 @@
781
735
  <label class="label">Description</label>
782
736
  <textarea class="input" id="sched-desc" rows="2" placeholder="e.g. Send a news summary every morning at 8am"></textarea>
783
737
  </div>
784
- <div class="form-group">
785
- <label class="label">Agent</label>
786
- <select class="input" id="sched-agent"></select>
787
- </div>
788
738
  <div class="form-group">
789
739
  <label class="label">Output Channel</label>
790
740
  <select class="input" id="sched-channel">
@@ -818,7 +768,7 @@
818
768
  <div id="channel-dialog-body" style="margin-bottom:4px;"></div>
819
769
  <div class="dialog-actions">
820
770
  <button class="btn btn-secondary btn-sm" onclick="closeChannelDialog()">取消</button>
821
- <button class="btn btn-primary btn-sm" onclick="saveChannel()">💾 保存</button>
771
+ <button class="btn btn-primary btn-sm" onclick="saveCurrentChannel()">💾 保存</button>
822
772
  </div>
823
773
  <!-- Settings Page -->
824
774
  <div class="page" id="page-settings">
@@ -1177,7 +1127,6 @@
1177
1127
  async function init() {
1178
1128
  await Promise.all([loadTemplates(), loadAgents(), loadSidebarAgents()]);
1179
1129
  loadSidebarGroups();
1180
- loadDashboardStats();
1181
1130
  handleRoute();
1182
1131
  window.addEventListener('popstate', handleRoute);
1183
1132
  checkFirstRun();
@@ -1441,12 +1390,11 @@
1441
1390
  const navItem = document.querySelector(`.nav-item[data-page="${page}"]`);
1442
1391
  if (navItem) navItem.classList.add('active');
1443
1392
 
1444
- if (page === 'dashboard') { loadAgents(); loadHealthDashboard(); loadSidebarAgents(); loadDashboardStats(); }
1393
+ if (page === 'dashboard') { loadAgents(); loadHealthDashboard(); loadSidebarAgents(); }
1445
1394
  if (page === 'create') { renderWizard(); renderWizardTemplates(); }
1446
1395
  if (page === 'settings') { showSettings(currentSettingsTab || 'models'); }
1447
1396
  if (page === 'global-runtime') { currentSettingsTab='status'; showSettings('status'); showPage('settings'); return; }
1448
1397
  if (page === 'global-models') { currentSettingsTab='models'; showSettings('models'); showPage('settings'); return; }
1449
- if (page === 'global-channels') { currentSettingsTab='channels'; showSettings('channels'); showPage('settings'); return; }
1450
1398
  if (page === 'global-memory') { currentSettingsTab='memory'; showSettings('memory'); showPage('settings'); return; }
1451
1399
  if (page === 'global-templates') { navigate('templates'); return; }
1452
1400
  if (page === 'create-group') { loadGroupAgentSelect(); }
@@ -1657,112 +1605,13 @@
1657
1605
  }
1658
1606
  if (wizardStep === 2 && selectedTemplate) {
1659
1607
  document.getElementById('agent-name').placeholder = selectedTemplate.name;
1660
- document.getElementById('agent-model').value = selectedTemplate.suggestedModel || 'auto';
1661
- renderSkillToggles();
1608
+ document.getElementById('agent-model').value = selectedTemplate.suggestedModel;
1662
1609
  }
1663
1610
  if (wizardStep === 3) {
1664
1611
  renderConfirmCard();
1665
1612
  }
1666
1613
  }
1667
1614
 
1668
- // --- Skill toggle rendering ---
1669
- const WIZARD_SKILL_CATEGORIES = {
1670
- productivity: { icon: '📋', label: 'Productivity' },
1671
- knowledge: { icon: '📚', label: 'Knowledge' },
1672
- creative: { icon: '✍️', label: 'Creative' },
1673
- developer: { icon: '💻', label: 'Developer' },
1674
- lifestyle: { icon: '🏠', label: 'Lifestyle' },
1675
- business: { icon: '💼', label: 'Business' },
1676
- education: { icon: '🎓', label: 'Education' },
1677
- };
1678
- const PRESET_SKILLS = [
1679
- { id: 'note-assistant', name: '📝 Notes', cat: 'productivity', default: true },
1680
- { id: 'todo-manager', name: '✅ Todo', cat: 'productivity', default: true },
1681
- { id: 'reminder', name: '🔔 Reminder', cat: 'productivity', default: true },
1682
- { id: 'reading-summary', name: '📚 Summarize', cat: 'knowledge', default: true },
1683
- { id: 'knowledge-search', name: '🔍 Search', cat: 'knowledge', default: true },
1684
- { id: 'dictionary-translator', name: '📖 Translate', cat: 'knowledge', default: false },
1685
- { id: 'calculator', name: '🧮 Calculate', cat: 'knowledge', default: false },
1686
- { id: 'writing-assistant', name: '✍️ Writing', cat: 'creative', default: true },
1687
- { id: 'social-media', name: '📱 Social', cat: 'creative', default: false },
1688
- { id: 'code-assistant', name: '💻 Code', cat: 'developer', default: false },
1689
- { id: 'data-analysis', name: '📊 Data', cat: 'knowledge', default: false },
1690
- { id: 'customer-followup', name: '🤝 CRM', cat: 'business', default: false },
1691
- { id: 'weather-query', name: '🌤 Weather', cat: 'lifestyle', default: false },
1692
- { id: 'language-tutor', name: '🗣 Language', cat: 'education', default: false },
1693
- { id: 'meeting-summarizer', name: '📋 Meeting', cat: 'productivity', default: false },
1694
- { id: 'brainstorm', name: '💡 Brainstorm', cat: 'creative', default: false },
1695
- ];
1696
- let selectedSkills = new Set(PRESET_SKILLS.filter(s => s.default).map(s => s.id));
1697
-
1698
- function renderSkillToggles() {
1699
- const grid = document.getElementById('skill-toggles');
1700
- if (!grid) return;
1701
- grid.innerHTML = PRESET_SKILLS.map(s => `
1702
- <label style="display:flex;align-items:center;gap:8px;padding:8px 12px;border-radius:10px;border:1px solid ${selectedSkills.has(s.id) ? 'var(--accent)' : 'var(--border)'};background:${selectedSkills.has(s.id) ? 'var(--accent-light)' : 'transparent'};cursor:pointer;font-size:14px;transition:all 0.2s;" onclick="toggleSkill('${s.id}',this)">
1703
- <input type="checkbox" ${selectedSkills.has(s.id) ? 'checked' : ''} style="display:none;">
1704
- <span>${s.name}</span>
1705
- </label>
1706
- `).join('');
1707
- }
1708
-
1709
- function toggleSkill(id, el) {
1710
- if (selectedSkills.has(id)) { selectedSkills.delete(id); } else { selectedSkills.add(id); }
1711
- renderSkillToggles();
1712
- }
1713
-
1714
- // --- AI Prompt Generator ---
1715
- async function generatePrompt() {
1716
- const taskDesc = document.getElementById('agent-task-desc').value.trim();
1717
- const tplName = selectedTemplate?.name || '';
1718
- const agentName = document.getElementById('agent-name').value.trim();
1719
- const bizDesc = document.getElementById('agent-desc').value.trim();
1720
-
1721
- if (!taskDesc && !tplName) { alert('Please describe what your agent should do'); return; }
1722
-
1723
- const promptArea = document.getElementById('agent-prompt');
1724
- promptArea.style.display = 'block';
1725
- promptArea.value = '✨ Generating prompt...';
1726
-
1727
- // Build the meta-prompt
1728
- const context = [
1729
- tplName ? `Role template: ${tplName}` : '',
1730
- agentName ? `Agent name: ${agentName}` : '',
1731
- bizDesc ? `Business: ${bizDesc}` : '',
1732
- taskDesc ? `User request: ${taskDesc}` : '',
1733
- `Enabled skills: ${[...selectedSkills].join(', ')}`,
1734
- ].filter(Boolean).join('\n');
1735
-
1736
- const metaPrompt = `Generate a system prompt for an AI agent with these requirements:\n${context}\n\nRules:\n- Be specific and actionable\n- Include personality traits\n- Mention the skills it can use\n- Keep it under 300 words\n- Write in the same language as the user's request\n\nOutput ONLY the system prompt, no explanation.`;
1737
-
1738
- try {
1739
- const res = await fetch(`${API}/api/agents/generate-prompt`, {
1740
- method: 'POST',
1741
- headers: { 'Content-Type': 'application/json' },
1742
- body: JSON.stringify({ prompt: metaPrompt }),
1743
- });
1744
- if (res.ok) {
1745
- const data = await res.json();
1746
- promptArea.value = data.prompt || data.text || 'Failed to generate. Try writing manually.';
1747
- } else {
1748
- // Fallback: generate a simple prompt locally
1749
- promptArea.value = generateLocalPrompt(tplName, taskDesc, bizDesc);
1750
- }
1751
- } catch {
1752
- // Offline fallback
1753
- promptArea.value = generateLocalPrompt(tplName, taskDesc, bizDesc);
1754
- }
1755
- }
1756
-
1757
- function generateLocalPrompt(role, task, biz) {
1758
- const parts = [`You are ${role || 'an AI assistant'}.`];
1759
- if (biz) parts.push(`You work for: ${biz}.`);
1760
- if (task) parts.push(`Your main job: ${task}.`);
1761
- parts.push('Be helpful, professional, and friendly. Ask clarifying questions when needed.');
1762
- if (selectedSkills.size > 0) parts.push(`You have access to these skills: ${[...selectedSkills].join(', ')}.`);
1763
- return parts.join('\n');
1764
- }
1765
-
1766
1615
  function wizardNext() {
1767
1616
  if (wizardStep === 1 && !selectedTemplate) { alert('Please select a template first'); return; }
1768
1617
  if (wizardStep < 3) { wizardStep++; renderWizard(); }
@@ -2615,31 +2464,8 @@
2615
2464
  let frCreatedAgentId = null;
2616
2465
 
2617
2466
  async function checkFirstRun() {
2618
- try {
2619
- const res = await fetch(`${API}/api/first-run/status`);
2620
- const data = await res.json();
2621
- if (data.completed) return; // already configured
2622
- // Check if models are configured — if so, skip
2623
- const modelRes = await fetch(`${API}/api/settings/models`);
2624
- const modelData = await modelRes.json();
2625
- const hasCloudKey = modelData.providers && Object.values(modelData.providers).some(p => p && p.apiKey);
2626
- if (hasCloudKey) return; // user already has API keys
2627
- // Check if Ollama is running with models
2628
- const ollamaRes = await fetch(`${API}/api/settings/models/local`);
2629
- const ollamaData = await ollamaRes.json();
2630
- if (ollamaData.running && ollamaData.models?.length > 0) {
2631
- // Ollama ready — auto-set local mode and skip wizard
2632
- await fetch(`${API}/api/settings/models`, {
2633
- method: 'PUT', headers: {'Content-Type':'application/json'},
2634
- body: JSON.stringify({ mode: 'local', provider: 'ollama', chatModel: ollamaData.models[0]?.name || 'qwen2.5:7b' })
2635
- });
2636
- return;
2637
- }
2638
- // Nothing configured — show wizard
2639
- showFirstRunWizard({ ollamaDetected: ollamaData.running, ollamaModels: ollamaData.models });
2640
- } catch {
2641
- // API not ready, skip
2642
- }
2467
+ // Skip first-run wizard — agents are configured via init
2468
+ return;
2643
2469
  }
2644
2470
 
2645
2471
  function showFirstRunWizard(data) {
@@ -2770,167 +2596,6 @@
2770
2596
  });
2771
2597
  }
2772
2598
 
2773
- // === Dashboard Stats ===
2774
- async function loadDashboardStats() {
2775
- const el = document.getElementById('dashboard-stats');
2776
- if (!el) return;
2777
- try {
2778
- const [agentsRes, modelsRes] = await Promise.all([
2779
- fetch('/api/agents').then(r=>r.json()).catch(()=>({})),
2780
- fetch('/api/settings/models').then(r=>r.json()).catch(()=>({})),
2781
- ]);
2782
- const agentList = agentsRes.agents || [];
2783
- const currentModel = modelsRes.chatModel || 'Not configured';
2784
- el.innerHTML = `
2785
- <div class="card stat-card"><div class="stat-value">${agentList.length}</div><div class="stat-label">Agents</div></div>
2786
- <div class="card stat-card"><div class="stat-value" style="font-size:18px;">${currentModel}</div><div class="stat-label">Current Model</div></div>
2787
- <div class="card stat-card"><div class="stat-value">${agentList.filter(a=>(a.status||'').toLowerCase()==='online').length}</div><div class="stat-label">Online</div></div>
2788
- `;
2789
- } catch { el.innerHTML = ''; }
2790
- }
2791
-
2792
- // === Agent Detail Settings Tab Logic ===
2793
- function switchAgentTabOrig(tab) {
2794
- document.querySelectorAll('.agent-tab').forEach(t => t.classList.remove('active'));
2795
- document.querySelector(`.agent-tab[data-atab="${tab}"]`)?.classList.add('active');
2796
- document.querySelectorAll('.agent-tab-panel').forEach(p => p.classList.remove('active'));
2797
- document.getElementById(`atab-${tab}`)?.classList.add('active');
2798
- loadAgentTabData(tab);
2799
- }
2800
- // Override original
2801
- switchAgentTab = switchAgentTabOrig;
2802
-
2803
- async function loadAgentTabData(tab) {
2804
- if (!selectedAgentId) return;
2805
- const id = selectedAgentId;
2806
- try {
2807
- if (tab === 'role') {
2808
- const res = await fetch(`/api/agents/${id}`);
2809
- const a = await res.json();
2810
- document.getElementById('atab-role-name').value = a.name || '';
2811
- document.getElementById('atab-role-desc').value = a.description || '';
2812
- document.getElementById('atab-role-prompt').value = a.systemPrompt || a.prompt || '';
2813
- } else if (tab === 'models') {
2814
- const res = await fetch(`/api/agents/${id}`);
2815
- const a = await res.json();
2816
- const ov = document.getElementById('atab-model-override');
2817
- const fields = document.getElementById('atab-model-fields');
2818
- ov.checked = !!a.modelOverride;
2819
- fields.style.display = ov.checked ? '' : 'none';
2820
- ov.onchange = () => { fields.style.display = ov.checked ? '' : 'none'; };
2821
- if (a.modelOverride) {
2822
- document.getElementById('atab-model-provider').value = a.provider || 'ollama';
2823
- document.getElementById('atab-model-name').value = a.model || '';
2824
- document.getElementById('atab-model-temp').value = a.temperature ?? 0.7;
2825
- }
2826
- } else if (tab === 'channels') {
2827
- const chList = ['web','telegram','discord','slack','feishu','email','whatsapp'];
2828
- const res = await fetch(`/api/agents/${id}`);
2829
- const a = await res.json();
2830
- const enabled = a.channels || ['web'];
2831
- document.getElementById('atab-channels-list').innerHTML = chList.map(ch =>
2832
- `<label style="display:flex;align-items:center;gap:10px;padding:8px 0;font-size:18px;cursor:pointer;">
2833
- <input type="checkbox" class="atab-ch-cb" value="${ch}" ${enabled.includes(ch)?'checked':''}>
2834
- ${ch.charAt(0).toUpperCase()+ch.slice(1)}
2835
- </label>`
2836
- ).join('');
2837
- } else if (tab === 'memory') {
2838
- const el = document.getElementById('atab-memory-list');
2839
- el.innerHTML = '<span style="color:var(--text-muted);">⏳ Loading...</span>';
2840
- try {
2841
- const res = await fetch(`/api/agents/${id}/memory`);
2842
- const data = await res.json();
2843
- const entries = data.entries || [];
2844
- if (!entries.length) { el.innerHTML = '<p style="color:var(--text-muted);">No memories yet.</p>'; return; }
2845
- el.innerHTML = '<div class="timeline">' + entries.map(e =>
2846
- `<div class="timeline-item"><div class="timeline-date">${new Date(e.timestamp).toLocaleString()}</div><div class="timeline-content">${esc(e.summary||e.content||'')}</div></div>`
2847
- ).join('') + '</div>';
2848
- } catch { el.innerHTML = '<p style="color:var(--text-muted);">Failed to load memories.</p>'; }
2849
- } else if (tab === 'skills') {
2850
- const el = document.getElementById('atab-skills-list');
2851
- el.innerHTML = '<span style="color:var(--text-muted);">⏳ Loading...</span>';
2852
- try {
2853
- const res = await fetch(`/api/agents/${id}`);
2854
- const a = await res.json();
2855
- const skills = a.skills || [];
2856
- if (!skills.length) { el.innerHTML = '<p style="color:var(--text-muted);">No skills installed.</p>'; return; }
2857
- el.innerHTML = skills.map(s => `<div class="card" style="margin-bottom:8px;padding:12px;"><span style="font-size:18px;font-weight:600;">${s.name||s}</span></div>`).join('');
2858
- } catch { el.innerHTML = '<p style="color:var(--text-muted);">Failed to load skills.</p>'; }
2859
- } else if (tab === 'schedules') {
2860
- const el = document.getElementById('atab-schedules-list');
2861
- el.innerHTML = '<span style="color:var(--text-muted);">⏳ Loading...</span>';
2862
- try {
2863
- const res = await fetch('/api/schedules');
2864
- const all = await res.json();
2865
- const tasks = (Array.isArray(all)?all:[]).filter(t => t.agentId === id);
2866
- if (!tasks.length) { el.innerHTML = '<p style="color:var(--text-muted);">No schedules for this agent.</p>'; return; }
2867
- el.innerHTML = tasks.map(t => `<div class="card" style="margin-bottom:8px;padding:12px;display:flex;align-items:center;gap:12px;">
2868
- <span>⏰</span><div style="flex:1;"><div style="font-weight:600;font-size:18px;">${esc(t.name)}</div><div style="font-size:18px;color:var(--text-dim);">${esc(t.schedule||t.frequency||'')}</div></div>
2869
- <span style="font-size:18px;color:${t.enabled?'var(--green)':'var(--text-dim)'};">${t.enabled?'Active':'Paused'}</span>
2870
- </div>`).join('');
2871
- } catch { el.innerHTML = '<p style="color:var(--text-muted);">Failed to load schedules.</p>'; }
2872
- } else if (tab === 'usage') {
2873
- const el = document.getElementById('atab-usage-content');
2874
- el.innerHTML = '<span style="color:var(--text-muted);">⏳ Loading...</span>';
2875
- try {
2876
- const res = await fetch('/api/settings/usage');
2877
- const data = await res.json();
2878
- const tokens = data.totalTokens || 0;
2879
- const cost = data.totalCost || 0;
2880
- el.innerHTML = `<div class="card-grid"><div class="card stat-card"><div class="stat-value">${tokens>1000?(tokens/1000).toFixed(1)+'K':tokens}</div><div class="stat-label">Total Tokens</div></div><div class="card stat-card"><div class="stat-value">$${cost.toFixed(4)}</div><div class="stat-label">Est. Cost</div></div></div>`;
2881
- } catch { el.innerHTML = '<p style="color:var(--text-muted);">Failed to load usage data.</p>'; }
2882
- }
2883
- } catch(e) { console.error('loadAgentTabData error:', e); }
2884
- }
2885
-
2886
- async function saveAgentRole() {
2887
- if (!selectedAgentId) return;
2888
- const st = document.getElementById('atab-role-status');
2889
- try {
2890
- await fetch(`/api/agents/${selectedAgentId}`, { method:'PUT', headers:{'Content-Type':'application/json'}, body:JSON.stringify({
2891
- name: document.getElementById('atab-role-name').value.trim(),
2892
- description: document.getElementById('atab-role-desc').value.trim(),
2893
- systemPrompt: document.getElementById('atab-role-prompt').value.trim(),
2894
- })});
2895
- st.textContent = '✅ Saved'; st.style.color = 'var(--green)';
2896
- loadSidebarAgents();
2897
- } catch { st.textContent = '❌ Failed'; st.style.color = 'var(--red)'; }
2898
- }
2899
-
2900
- async function saveAgentModel() {
2901
- if (!selectedAgentId) return;
2902
- const st = document.getElementById('atab-model-status');
2903
- const ov = document.getElementById('atab-model-override').checked;
2904
- const body = { modelOverride: ov };
2905
- if (ov) { body.provider = document.getElementById('atab-model-provider').value; body.model = document.getElementById('atab-model-name').value; body.temperature = parseFloat(document.getElementById('atab-model-temp').value); }
2906
- try {
2907
- await fetch(`/api/agents/${selectedAgentId}`, { method:'PUT', headers:{'Content-Type':'application/json'}, body:JSON.stringify(body) });
2908
- st.textContent = '✅ Saved'; st.style.color = 'var(--green)';
2909
- } catch { st.textContent = '❌ Failed'; st.style.color = 'var(--red)'; }
2910
- }
2911
-
2912
- async function saveAgentChannels() {
2913
- if (!selectedAgentId) return;
2914
- const st = document.getElementById('atab-channels-status');
2915
- const channels = [...document.querySelectorAll('.atab-ch-cb:checked')].map(cb => cb.value);
2916
- try {
2917
- await fetch(`/api/agents/${selectedAgentId}`, { method:'PUT', headers:{'Content-Type':'application/json'}, body:JSON.stringify({ channels }) });
2918
- st.textContent = '✅ Saved'; st.style.color = 'var(--green)';
2919
- } catch { st.textContent = '❌ Failed'; st.style.color = 'var(--red)'; }
2920
- }
2921
-
2922
- // === Populate schedule form agent select ===
2923
- async function populateSchedAgentSelect() {
2924
- const sel = document.getElementById('sched-agent');
2925
- if (!sel) return;
2926
- try {
2927
- const res = await fetch('/api/agents');
2928
- const data = await res.json();
2929
- const list = data.agents || [];
2930
- sel.innerHTML = list.map(a => `<option value="${a.id}">${a.name||a.id}</option>`).join('');
2931
- } catch { sel.innerHTML = '<option value="">No agents</option>'; }
2932
- }
2933
-
2934
2599
  // === Start ===
2935
2600
  init();
2936
2601
 
@@ -2997,9 +2662,6 @@
2997
2662
  document.getElementById('sched-desc').value = task ? task.description : '';
2998
2663
  document.getElementById('sched-channel').value = task ? task.outputChannel : 'web';
2999
2664
  onSchedFreqChange();
3000
- populateSchedAgentSelect().then(() => {
3001
- if (task?.agentId) document.getElementById('sched-agent').value = task.agentId;
3002
- });
3003
2665
  }
3004
2666
 
3005
2667
  function hideScheduleForm() {
@@ -3020,7 +2682,6 @@
3020
2682
  time: document.getElementById('sched-time').value,
3021
2683
  schedule: document.getElementById('sched-frequency').value === 'custom' ? document.getElementById('sched-cron').value.trim() : '',
3022
2684
  description: document.getElementById('sched-desc').value.trim(),
3023
- agentId: document.getElementById('sched-agent').value,
3024
2685
  outputChannel: document.getElementById('sched-channel').value,
3025
2686
  enabled: true,
3026
2687
  };
@@ -3176,17 +2837,17 @@
3176
2837
  // =============================================
3177
2838
  async function saveImageGenConfig() {
3178
2839
  const data = {
3179
- openaiApiKey: document.getElementById('ig-openai-key')?.value?.trim() || '',
3180
- sdApiUrl: document.getElementById('ig-sd-url')?.value?.trim() || '',
3181
- replicateApiKey: document.getElementById('ig-replicate-key')?.value?.trim() || '',
2840
+ openaiApiKey: document.getElementById('ig-openai-key').value.trim(),
2841
+ sdApiUrl: document.getElementById('ig-sd-url').value.trim(),
2842
+ replicateApiKey: document.getElementById('ig-replicate-key').value.trim(),
3182
2843
  };
3183
2844
  try {
3184
2845
  await fetch('/api/image-gen/config', { method: 'PUT', headers: {'Content-Type':'application/json'}, body: JSON.stringify(data) });
3185
- const st = document.getElementById('ig-status');
3186
- if (st) { st.textContent = '✅ Configuration saved'; st.style.color = 'var(--green)'; }
2846
+ document.getElementById('ig-status').textContent = '✅ Configuration saved!';
2847
+ document.getElementById('ig-status').style.color = 'var(--green)';
3187
2848
  } catch(e) {
3188
- const st = document.getElementById('ig-status');
3189
- if (st) { st.textContent = '❌ Failed: ' + e.message; st.style.color = 'var(--red)'; }
2849
+ document.getElementById('ig-status').textContent = '❌ Failed: ' + e.message;
2850
+ document.getElementById('ig-status').style.color = 'var(--red)';
3190
2851
  }
3191
2852
  }
3192
2853