diff --git a/server/routes/company.js b/server/routes/company.js
index 2b495a9..90478c8 100644
--- a/server/routes/company.js
+++ b/server/routes/company.js
@@ -285,6 +285,32 @@ router.post('/:companyId/agents/:agentId/heartbeat/stop', (req, res) => {
});
// ═══════════════════════════════════════
+// Agent 技能查询
+// ═══════════════════════════════════════
+
+// GET /api/company/:companyId/agents/:agentId/skills — 获取 Agent 技能列表
+router.get('/:companyId/agents/:agentId/skills', (req, res) => {
+ try {
+ const path = require('path');
+ const fs = require('fs');
+ const DATA_DIR = path.resolve(__dirname, '../../data');
+ const skillsDir = path.join(DATA_DIR, req.params.companyId, 'agents', req.params.agentId, 'skills');
+
+ if (!fs.existsSync(skillsDir)) {
+ return res.json([]);
+ }
+
+ const skills = fs.readdirSync(skillsDir, { withFileTypes: true })
+ .filter(d => d.isDirectory())
+ .map(d => d.name);
+
+ res.json(skills);
+ } catch (err) {
+ res.status(500).json({ error: err.message });
+ }
+});
+
+// ═══════════════════════════════════════
// Agent 招聘
// ═══════════════════════════════════════
diff --git a/web/src/views/CompanyDashboard.vue b/web/src/views/CompanyDashboard.vue
index 0f4b413..e677a2a 100644
--- a/web/src/views/CompanyDashboard.vue
+++ b/web/src/views/CompanyDashboard.vue
@@ -123,48 +123,68 @@
心跳
操作
-
-
-
- {{ agent.name }}
-
-
- {{ agent.type }}
-
-
- {{ agent.status }}
-
-
-
-
-
- 💓 运行中
- ⏸️ 停止
-
-
-
-
-
-
+
+
+
+
+ {{ agent.name }}
+
+
+ {{ agent.type }}
+
+
+ {{ agent.status }}
+
+
+
+
+
+ 💓 运行中
+ ⏸️ 停止
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
📋 {{ agent.name }} 的技能列表
+
+
+
+
暂无技能(可通过 Innovator 训练获得)
+
+
暂无 Agent
@@ -312,6 +332,8 @@ const templates = ref([])
const activeTab = ref('overview')
const showTemplates = ref(false)
const showDropdown = ref(false)
+const expandedAgent = ref(null)
+const agentSkills = ref([])
function statusLabel(status) {
const map = { creating: '创建中', running: '运行中', paused: '已暂停', shutdown: '已关停' }
@@ -482,6 +504,41 @@ async function stopAgentHeartbeat(agentId) {
} catch (e) { console.error(e) }
}
+function goToAgentChat(agentId) {
+ if (!selectedCompany.value) return
+ router.push(`/${selectedCompany.value.id}/agents/${agentId}`)
+}
+
+async function toggleAgentSkills(agentId) {
+ if (expandedAgent.value === agentId) {
+ expandedAgent.value = null
+ return
+ }
+ // Fetch skills from backend
+ try {
+ const { data } = await axios.get(`/api/company/${selectedCompany.value.id}/agents/${agentId}/skills`)
+ agentSkills.value = data
+ } catch {
+ agentSkills.value = []
+ }
+ expandedAgent.value = agentId
+}
+
+async function triggerTrain(agentId) {
+ if (!selectedCompany.value) return
+ const namespacedId = `${selectedCompany.value.id}:${agentId}`
+ if (!confirm(`确定要对 ${agentId} 发起 Innovator 训练?这将触发 agent-creator 重新定义并由 Innovator 训练该 Agent。`)) return
+ try {
+ await axios.post(`/api/agent/${selectedCompany.value.id}:ceo/chat`, {
+ message: `请安排 Innovator 对 ${namespacedId} 进行一次完整的技能训练/再训练。要求:1)评估当前技能缺口 2)使用 Innovator 进行 FDE 训练 3)训练完成后汇报结果`,
+ conversationId: `train-${agentId}-${Date.now().toString(36)}`,
+ })
+ alert(`✅ 训练指令已发送给 CEO,CEO 将安排 Innovator 训练 ${agentId}`)
+ } catch (e) {
+ alert(`❌ 发送训练指令失败: ${e.message}`)
+ }
+}
+
// switchTab removed, handled by goToTab and router watcher
onMounted(() => {
@@ -963,4 +1020,48 @@ function closeDropdown() {
.hb-active { color: var(--accent-light); font-size: 12px; }
.hb-inactive { color: var(--text-muted); font-size: 12px; }
+
+/* Agent Name Link */
+.agent-name-link {
+ color: var(--text-primary); cursor: pointer; text-decoration: none;
+ transition: color 0.15s;
+}
+.agent-name-link:hover { color: var(--accent-light); text-decoration: underline; }
+
+/* Action Button Group */
+.action-group { display: flex; gap: 4px; align-items: center; }
+.action-group button {
+ width: 28px; height: 28px; border-radius: 6px; border: 1px solid var(--border-primary);
+ background: var(--bg-primary); cursor: pointer; font-size: 13px;
+ display: flex; align-items: center; justify-content: center; transition: all 0.15s;
+ padding: 0;
+}
+.action-group button:hover { background: var(--bg-surface-hover); border-color: var(--border-active); transform: scale(1.1); }
+
+.btn-action-chat { color: var(--accent-light); }
+.btn-action-skills { color: var(--blue-text, #60a5fa); }
+.btn-action-train { color: var(--purple-text, #a78bfa); }
+
+/* Expandable Skills Panel */
+.agent-expand-panel {
+ padding: 16px 20px; background: var(--bg-primary);
+ border-bottom: 1px solid var(--border-primary);
+ animation: fadeIn 0.2s ease;
+}
+.expand-header {
+ display: flex; justify-content: space-between; align-items: center; margin-bottom: 12px;
+}
+.expand-header h4 { font-size: 13px; font-weight: 700; margin: 0; }
+.btn-close-expand {
+ background: none; border: none; color: var(--text-muted); cursor: pointer; font-size: 14px;
+ padding: 4px 8px; border-radius: 4px; transition: all 0.15s;
+}
+.btn-close-expand:hover { background: var(--bg-surface-hover); color: var(--text-primary); }
+
+.skills-grid { display: flex; flex-wrap: wrap; gap: 8px; }
+.skill-chip {
+ padding: 4px 12px; border-radius: 8px; font-size: 12px; font-weight: 600;
+ background: rgba(139,92,246,0.08); color: var(--purple-text, #a78bfa);
+ border: 1px solid rgba(139,92,246,0.15);
+}