ideaco 1.1.5
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/.dockerignore +33 -0
- package/.nvmrc +1 -0
- package/ARCHITECTURE.md +394 -0
- package/Dockerfile +50 -0
- package/LICENSE +29 -0
- package/README.md +206 -0
- package/bin/i18n.js +46 -0
- package/bin/ideaco.js +494 -0
- package/deploy.sh +15 -0
- package/docker-compose.yml +30 -0
- package/electron/main.cjs +986 -0
- package/electron/preload.cjs +14 -0
- package/electron/web-backends.cjs +854 -0
- package/jsconfig.json +8 -0
- package/next.config.mjs +34 -0
- package/package.json +134 -0
- package/postcss.config.mjs +6 -0
- package/public/demo/dashboard.png +0 -0
- package/public/demo/employee.png +0 -0
- package/public/demo/messages.png +0 -0
- package/public/demo/office.png +0 -0
- package/public/demo/requirement.png +0 -0
- package/public/logo.jpeg +0 -0
- package/public/logo.png +0 -0
- package/scripts/prepare-electron.js +67 -0
- package/scripts/release.js +76 -0
- package/src/app/api/agents/[agentId]/chat/route.js +70 -0
- package/src/app/api/agents/[agentId]/conversations/route.js +35 -0
- package/src/app/api/agents/[agentId]/route.js +106 -0
- package/src/app/api/avatar/route.js +104 -0
- package/src/app/api/browse-dir/route.js +44 -0
- package/src/app/api/chat/route.js +265 -0
- package/src/app/api/company/factory-reset/route.js +43 -0
- package/src/app/api/company/route.js +82 -0
- package/src/app/api/departments/[deptId]/agents/[agentId]/dismiss/route.js +19 -0
- package/src/app/api/departments/route.js +92 -0
- package/src/app/api/group-chat-loop/events/route.js +70 -0
- package/src/app/api/group-chat-loop/route.js +94 -0
- package/src/app/api/mailbox/route.js +100 -0
- package/src/app/api/messages/route.js +14 -0
- package/src/app/api/providers/[id]/configure/route.js +21 -0
- package/src/app/api/providers/[id]/refresh-cookie/route.js +38 -0
- package/src/app/api/providers/[id]/test-cookie/route.js +28 -0
- package/src/app/api/providers/route.js +11 -0
- package/src/app/api/requirements/route.js +242 -0
- package/src/app/api/secretary/route.js +65 -0
- package/src/app/api/system/cli-backends/route.js +91 -0
- package/src/app/api/system/cron/route.js +110 -0
- package/src/app/api/system/knowledge/route.js +104 -0
- package/src/app/api/system/plugins/route.js +40 -0
- package/src/app/api/system/skills/route.js +46 -0
- package/src/app/api/system/status/route.js +46 -0
- package/src/app/api/talent-market/[profileId]/recall/route.js +22 -0
- package/src/app/api/talent-market/[profileId]/route.js +17 -0
- package/src/app/api/talent-market/route.js +26 -0
- package/src/app/api/teams/route.js +773 -0
- package/src/app/api/ws-files/[departmentId]/file/route.js +27 -0
- package/src/app/api/ws-files/[departmentId]/files/route.js +22 -0
- package/src/app/globals.css +130 -0
- package/src/app/layout.jsx +40 -0
- package/src/app/page.jsx +97 -0
- package/src/components/AgentChatModal.jsx +164 -0
- package/src/components/AgentDetailModal.jsx +425 -0
- package/src/components/AgentSpyModal.jsx +481 -0
- package/src/components/AvatarGrid.jsx +29 -0
- package/src/components/BossProfileModal.jsx +162 -0
- package/src/components/CachedAvatar.jsx +77 -0
- package/src/components/ChatPanel.jsx +219 -0
- package/src/components/ChatShared.jsx +255 -0
- package/src/components/DepartmentDetail.jsx +842 -0
- package/src/components/DepartmentView.jsx +367 -0
- package/src/components/FileReference.jsx +260 -0
- package/src/components/FilesView.jsx +465 -0
- package/src/components/GroupChatView.jsx +799 -0
- package/src/components/Mailbox.jsx +926 -0
- package/src/components/MessagesView.jsx +112 -0
- package/src/components/OnboardingGuide.jsx +209 -0
- package/src/components/OrgTree.jsx +151 -0
- package/src/components/Overview.jsx +391 -0
- package/src/components/PixelOffice.jsx +2281 -0
- package/src/components/ProviderGrid.jsx +551 -0
- package/src/components/ProvidersBoard.jsx +16 -0
- package/src/components/RequirementDetail.jsx +1279 -0
- package/src/components/RequirementsBoard.jsx +187 -0
- package/src/components/SecretarySettings.jsx +295 -0
- package/src/components/SetupWizard.jsx +388 -0
- package/src/components/Sidebar.jsx +169 -0
- package/src/components/SystemMonitor.jsx +808 -0
- package/src/components/TalentMarket.jsx +183 -0
- package/src/components/TeamDetail.jsx +697 -0
- package/src/core/agent/base-agent.js +104 -0
- package/src/core/agent/chat-store.js +602 -0
- package/src/core/agent/cli-agent/backends/claude-code/README.md +52 -0
- package/src/core/agent/cli-agent/backends/claude-code/config.js +27 -0
- package/src/core/agent/cli-agent/backends/codebuddy/README.md +236 -0
- package/src/core/agent/cli-agent/backends/codebuddy/config.js +27 -0
- package/src/core/agent/cli-agent/backends/codex/README.md +51 -0
- package/src/core/agent/cli-agent/backends/codex/config.js +27 -0
- package/src/core/agent/cli-agent/backends/index.js +27 -0
- package/src/core/agent/cli-agent/backends/registry.js +580 -0
- package/src/core/agent/cli-agent/index.js +154 -0
- package/src/core/agent/index.js +60 -0
- package/src/core/agent/llm-agent/client.js +320 -0
- package/src/core/agent/llm-agent/index.js +97 -0
- package/src/core/agent/message-bus.js +211 -0
- package/src/core/agent/session.js +608 -0
- package/src/core/agent/tools.js +596 -0
- package/src/core/agent/web-agent/backends/base-backend.js +180 -0
- package/src/core/agent/web-agent/backends/chatgpt/client.js +146 -0
- package/src/core/agent/web-agent/backends/chatgpt/config.js +148 -0
- package/src/core/agent/web-agent/backends/chatgpt/dom-scripts.js +303 -0
- package/src/core/agent/web-agent/backends/index.js +91 -0
- package/src/core/agent/web-agent/index.js +278 -0
- package/src/core/agent/web-agent/web-client.js +407 -0
- package/src/core/employee/base-employee.js +1088 -0
- package/src/core/employee/index.js +35 -0
- package/src/core/employee/knowledge.js +327 -0
- package/src/core/employee/lifecycle.js +990 -0
- package/src/core/employee/memory/index.js +642 -0
- package/src/core/employee/memory/store.js +143 -0
- package/src/core/employee/performance.js +224 -0
- package/src/core/employee/secretary.js +625 -0
- package/src/core/employee/skills.js +398 -0
- package/src/core/index.js +38 -0
- package/src/core/organization/company.js +2600 -0
- package/src/core/organization/department.js +737 -0
- package/src/core/organization/group-chat-loop.js +264 -0
- package/src/core/organization/index.js +8 -0
- package/src/core/organization/persistence.js +111 -0
- package/src/core/organization/team.js +267 -0
- package/src/core/organization/workforce/hr.js +377 -0
- package/src/core/organization/workforce/providers.js +468 -0
- package/src/core/organization/workforce/role-archetypes.js +805 -0
- package/src/core/organization/workforce/talent-market.js +205 -0
- package/src/core/prompts.js +532 -0
- package/src/core/requirement.js +1789 -0
- package/src/core/system/audit.js +483 -0
- package/src/core/system/cron.js +449 -0
- package/src/core/system/index.js +7 -0
- package/src/core/system/plugin.js +2183 -0
- package/src/core/utils/json-parse.js +188 -0
- package/src/core/workspace.js +239 -0
- package/src/lib/api-i18n.js +211 -0
- package/src/lib/avatar.js +268 -0
- package/src/lib/client-store.js +1025 -0
- package/src/lib/config-validator.js +483 -0
- package/src/lib/format-time.js +22 -0
- package/src/lib/hooks.js +414 -0
- package/src/lib/i18n.js +134 -0
- package/src/lib/paths.js +23 -0
- package/src/lib/store.js +72 -0
- package/src/locales/de.js +393 -0
- package/src/locales/en.js +1054 -0
- package/src/locales/es.js +393 -0
- package/src/locales/fr.js +393 -0
- package/src/locales/ja.js +501 -0
- package/src/locales/ko.js +513 -0
- package/src/locales/zh.js +828 -0
- package/tailwind.config.mjs +11 -0
|
@@ -0,0 +1,425 @@
|
|
|
1
|
+
'use client';
|
|
2
|
+
|
|
3
|
+
import { useState, useEffect } from 'react';
|
|
4
|
+
import { useStore } from '@/lib/client-store';
|
|
5
|
+
import { useI18n } from '@/lib/i18n';
|
|
6
|
+
import AgentChatModal from './AgentChatModal';
|
|
7
|
+
import AgentSpyModal from './AgentSpyModal';
|
|
8
|
+
import ReactMarkdown from 'react-markdown';
|
|
9
|
+
import CachedAvatar from './CachedAvatar';
|
|
10
|
+
|
|
11
|
+
export default function AgentDetailModal({ agentId, onClose }) {
|
|
12
|
+
const { t } = useI18n();
|
|
13
|
+
const { fetchAgentDetail } = useStore();
|
|
14
|
+
const [agent, setAgent] = useState(null);
|
|
15
|
+
const [activeTab, setActiveTab] = useState('info');
|
|
16
|
+
const [memorySubTab, setMemorySubTab] = useState('personal');
|
|
17
|
+
const [loadingDetail, setLoadingDetail] = useState(true);
|
|
18
|
+
const [showChat, setShowChat] = useState(false);
|
|
19
|
+
const [showSpy, setShowSpy] = useState(false);
|
|
20
|
+
|
|
21
|
+
useEffect(() => {
|
|
22
|
+
(async () => {
|
|
23
|
+
setLoadingDetail(true);
|
|
24
|
+
try {
|
|
25
|
+
const data = await fetchAgentDetail(agentId);
|
|
26
|
+
setAgent(data);
|
|
27
|
+
} catch (e) { /* handled */ }
|
|
28
|
+
setLoadingDetail(false);
|
|
29
|
+
})();
|
|
30
|
+
}, [agentId, fetchAgentDetail]);
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
if (loadingDetail) {
|
|
35
|
+
return (
|
|
36
|
+
<div className="fixed inset-0 bg-black/60 flex items-center justify-center z-50 !m-0" onClick={onClose}>
|
|
37
|
+
<div className="card p-8 text-center" onClick={e => e.stopPropagation()}>
|
|
38
|
+
<div className="text-2xl animate-pulse">⏳</div>
|
|
39
|
+
<p className="text-sm text-[var(--muted)] mt-2">{t('common.loading')}</p>
|
|
40
|
+
</div>
|
|
41
|
+
</div>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
if (!agent) {
|
|
46
|
+
return (
|
|
47
|
+
<div className="fixed inset-0 bg-black/60 flex items-center justify-center z-50 !m-0" onClick={onClose}>
|
|
48
|
+
<div className="card p-8 text-center" onClick={e => e.stopPropagation()}>
|
|
49
|
+
<p>{t('agent.notFound')}</p>
|
|
50
|
+
<button className="btn-secondary mt-4" onClick={onClose}>{t('common.close')}</button>
|
|
51
|
+
</div>
|
|
52
|
+
</div>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
const tabs = [
|
|
57
|
+
{ id: 'info', label: t('agent.tabs.info') },
|
|
58
|
+
{ id: 'memory', label: t('agent.tabs.memory') },
|
|
59
|
+
{ id: 'performance', label: t('agent.tabs.performance') },
|
|
60
|
+
{ id: 'tasks', label: t('agent.tabs.tasks') },
|
|
61
|
+
{ id: 'usage', label: t('agent.tabs.usage') },
|
|
62
|
+
];
|
|
63
|
+
|
|
64
|
+
return (
|
|
65
|
+
<div className="fixed inset-0 bg-black/60 flex items-center justify-center z-50 !m-0" onClick={onClose}>
|
|
66
|
+
<div className="card max-w-2xl w-full mx-4 max-h-[80vh] overflow-hidden flex flex-col" onClick={e => e.stopPropagation()}>
|
|
67
|
+
{/* Header */}
|
|
68
|
+
<div className="flex items-start gap-4 pb-4 border-b border-[var(--border)]">
|
|
69
|
+
<div className="relative shrink-0">
|
|
70
|
+
<CachedAvatar src={agent.avatar} alt={agent.name} className="w-24 h-24 rounded-full bg-[var(--border)]" />
|
|
71
|
+
{agent.avgScore >= 80 && (
|
|
72
|
+
<span className="absolute -top-1 -right-1 text-base animate-pulse drop-shadow-lg" title={t('agent.highPerformer')}>🌸</span>
|
|
73
|
+
)}
|
|
74
|
+
</div>
|
|
75
|
+
<div className="flex-1">
|
|
76
|
+
<div className="flex items-center gap-2">
|
|
77
|
+
<h2 className="text-xl font-bold">{agent.name}</h2>
|
|
78
|
+
<span className={`status-dot ${agent.status}`} />
|
|
79
|
+
<button
|
|
80
|
+
className="text-xs px-2 py-0.5 rounded-full bg-blue-500/15 text-blue-400 hover:bg-blue-500/25 transition-colors flex items-center gap-1"
|
|
81
|
+
onClick={() => setShowChat(true)}
|
|
82
|
+
>
|
|
83
|
+
💬 {t('agentChat.chatBtn').replace('💬 ', '')}
|
|
84
|
+
</button>
|
|
85
|
+
<button
|
|
86
|
+
className="text-xs px-2 py-0.5 rounded-full bg-purple-500/15 text-purple-400 hover:bg-purple-500/25 transition-colors flex items-center gap-1"
|
|
87
|
+
onClick={() => setShowSpy(true)}
|
|
88
|
+
>
|
|
89
|
+
🕵️ {t('agent.spyBtn')}
|
|
90
|
+
</button>
|
|
91
|
+
</div>
|
|
92
|
+
<div className="text-sm text-[var(--muted)]">{agent.role} · {agent.department}</div>
|
|
93
|
+
<div className="text-xs text-[var(--muted)] mt-1">
|
|
94
|
+
{agent.gender === 'female' ? '👩' : '👨'} {agent.age ? t('display.ageYears', { n: agent.age }) : ''}
|
|
95
|
+
{agent.personality?.trait && <span className="ml-2 text-purple-400">· {agent.personality.trait}</span>}
|
|
96
|
+
</div>
|
|
97
|
+
<div className="text-xs text-[var(--muted)] italic mt-1">"{agent.signature}"</div>
|
|
98
|
+
<div className="flex items-center gap-2 mt-2 flex-wrap">
|
|
99
|
+
<span className={`text-xs px-2 py-0.5 rounded ${agent.cliBackend ? 'bg-emerald-900/30 text-emerald-400' : 'bg-blue-900/30 text-blue-400'}`}>
|
|
100
|
+
{agent.cliBackend ? '🖥️ ' : ''}{agent.provider.name}
|
|
101
|
+
</span>
|
|
102
|
+
{agent.avgScore && (
|
|
103
|
+
<span className={`text-xs px-2 py-0.5 rounded ${
|
|
104
|
+
agent.avgScore >= 80 ? 'bg-green-900/30 text-green-400' :
|
|
105
|
+
agent.avgScore >= 60 ? 'bg-yellow-900/30 text-yellow-400' :
|
|
106
|
+
'bg-red-900/30 text-red-400'
|
|
107
|
+
}`}>
|
|
108
|
+
{t('agent.avgPerformance', { score: agent.avgScore })}
|
|
109
|
+
</span>
|
|
110
|
+
)}
|
|
111
|
+
</div>
|
|
112
|
+
</div>
|
|
113
|
+
<button onClick={onClose} className="text-[var(--muted)] hover:text-white text-xl">✕</button>
|
|
114
|
+
</div>
|
|
115
|
+
|
|
116
|
+
{/* Tabs */}
|
|
117
|
+
<div className="flex gap-1 pt-3 pb-2 border-b border-[var(--border)]">
|
|
118
|
+
{tabs.map(t => (
|
|
119
|
+
<button
|
|
120
|
+
key={t.id}
|
|
121
|
+
onClick={() => setActiveTab(t.id)}
|
|
122
|
+
className={`px-3 py-1.5 text-sm rounded-lg transition-all ${
|
|
123
|
+
activeTab === t.id ? 'bg-[var(--accent)]/10 text-[var(--accent)]' : 'text-[var(--muted)] hover:text-white'
|
|
124
|
+
}`}
|
|
125
|
+
>
|
|
126
|
+
{t.label}
|
|
127
|
+
</button>
|
|
128
|
+
))}
|
|
129
|
+
</div>
|
|
130
|
+
|
|
131
|
+
{/* Content area */}
|
|
132
|
+
<div className="flex-1 overflow-auto px-0 py-4">
|
|
133
|
+
{activeTab === 'info' && (
|
|
134
|
+
<div className="space-y-4 animate-fade-in">
|
|
135
|
+
{/* 基本信息 */}
|
|
136
|
+
<div className="grid grid-cols-3 gap-3">
|
|
137
|
+
<div className="bg-[var(--background)] border border-[var(--border)] rounded-lg p-3">
|
|
138
|
+
<div className="text-xs text-[var(--muted)] mb-1">{t('setup.gender')}</div>
|
|
139
|
+
<div className="text-sm font-medium">{agent.gender === 'female' ? t('display.genderFemaleText') : t('display.genderMaleText')}</div>
|
|
140
|
+
</div>
|
|
141
|
+
<div className="bg-[var(--background)] border border-[var(--border)] rounded-lg p-3">
|
|
142
|
+
<div className="text-xs text-[var(--muted)] mb-1">{t('display.ageLabel')}</div>
|
|
143
|
+
<div className="text-sm font-medium">{agent.age ? t('display.ageYears', { n: agent.age }) : t('display.ageUnknown')}</div>
|
|
144
|
+
</div>
|
|
145
|
+
<div className="bg-[var(--background)] border border-[var(--border)] rounded-lg p-3">
|
|
146
|
+
<div className="text-xs text-[var(--muted)] mb-1">{t('display.personality')}</div>
|
|
147
|
+
<div className="text-sm font-medium text-purple-400">{agent.personality?.trait || t('display.ageUnknown')}</div>
|
|
148
|
+
</div>
|
|
149
|
+
</div>
|
|
150
|
+
<div>
|
|
151
|
+
<h4 className="text-sm font-medium mb-2 text-[var(--muted)]">{t('agent.rolePrompt')}</h4>
|
|
152
|
+
<div className="bg-[var(--background)] p-3 rounded-lg text-sm whitespace-pre-wrap max-h-40 overflow-auto">
|
|
153
|
+
{agent.prompt}
|
|
154
|
+
</div>
|
|
155
|
+
</div>
|
|
156
|
+
<div>
|
|
157
|
+
<h4 className="text-sm font-medium mb-2 text-[var(--muted)]">{t('agent.skills')}</h4>
|
|
158
|
+
<div className="flex flex-wrap gap-2">
|
|
159
|
+
{agent.skills.map((s, i) => (
|
|
160
|
+
<span key={i} className="text-sm bg-[var(--accent)]/10 text-[var(--accent)] px-2 py-1 rounded-lg">{s}</span>
|
|
161
|
+
))}
|
|
162
|
+
</div>
|
|
163
|
+
</div>
|
|
164
|
+
</div>
|
|
165
|
+
)}
|
|
166
|
+
|
|
167
|
+
{activeTab === 'memory' && (
|
|
168
|
+
<div className="space-y-4 animate-fade-in">
|
|
169
|
+
{/* Memory Sub-tabs */}
|
|
170
|
+
<div className="flex gap-1 pb-2 border-b border-[var(--border)]">
|
|
171
|
+
<button
|
|
172
|
+
onClick={() => setMemorySubTab('personal')}
|
|
173
|
+
className={`px-3 py-1 text-xs rounded-lg transition-all ${memorySubTab === 'personal' ? 'bg-purple-500/15 text-purple-400' : 'text-[var(--muted)] hover:text-white'}`}
|
|
174
|
+
>
|
|
175
|
+
{t('agent.memorySubTabs.personal')}
|
|
176
|
+
</button>
|
|
177
|
+
<button
|
|
178
|
+
onClick={() => setMemorySubTab('social')}
|
|
179
|
+
className={`px-3 py-1 text-xs rounded-lg transition-all ${memorySubTab === 'social' ? 'bg-pink-500/15 text-pink-400' : 'text-[var(--muted)] hover:text-white'}`}
|
|
180
|
+
>
|
|
181
|
+
{t('agent.memorySubTabs.social')} ({agent.memory.relationships?.length || 0})
|
|
182
|
+
</button>
|
|
183
|
+
</div>
|
|
184
|
+
|
|
185
|
+
{memorySubTab === 'personal' && (
|
|
186
|
+
<div className="space-y-4">
|
|
187
|
+
<div>
|
|
188
|
+
<h4 className="text-sm font-medium mb-2 text-yellow-400">{t('agent.shortTermMemory', { n: agent.memory.shortTermCount })}</h4>
|
|
189
|
+
<div className="space-y-1.5">
|
|
190
|
+
{agent.memory.shortTerm.length === 0 ? (
|
|
191
|
+
<p className="text-xs text-[var(--muted)]">{t('agent.noShortTerm')}</p>
|
|
192
|
+
) : agent.memory.shortTerm.map((m) => (
|
|
193
|
+
<div key={m.id} className="bg-yellow-900/10 border border-yellow-900/20 rounded-lg p-2 text-xs">
|
|
194
|
+
<span className="text-yellow-500">[{m.category}]</span> {m.content}
|
|
195
|
+
</div>
|
|
196
|
+
))}
|
|
197
|
+
</div>
|
|
198
|
+
</div>
|
|
199
|
+
<div>
|
|
200
|
+
<h4 className="text-sm font-medium mb-2 text-purple-400">{t('agent.longTermMemory', { n: agent.memory.longTermCount })}</h4>
|
|
201
|
+
<div className="space-y-1.5 max-h-60 overflow-auto">
|
|
202
|
+
{agent.memory.longTerm.length === 0 ? (
|
|
203
|
+
<p className="text-xs text-[var(--muted)]">{t('agent.noLongTerm')}</p>
|
|
204
|
+
) : agent.memory.longTerm.map((m) => (
|
|
205
|
+
<div key={m.id} className="bg-purple-900/10 border border-purple-900/20 rounded-lg p-2 text-xs">
|
|
206
|
+
<span className="text-purple-500">[{m.category}]</span> {m.content}
|
|
207
|
+
</div>
|
|
208
|
+
))}
|
|
209
|
+
</div>
|
|
210
|
+
</div>
|
|
211
|
+
</div>
|
|
212
|
+
)}
|
|
213
|
+
|
|
214
|
+
{memorySubTab === 'social' && (
|
|
215
|
+
<div>
|
|
216
|
+
{(!agent.memory.relationships || agent.memory.relationships.length === 0) ? (
|
|
217
|
+
<p className="text-sm text-[var(--muted)] text-center py-8">{t('agent.noRelationships')}</p>
|
|
218
|
+
) : (
|
|
219
|
+
<div className="overflow-auto max-h-80">
|
|
220
|
+
<table className="w-full text-xs">
|
|
221
|
+
<thead>
|
|
222
|
+
<tr className="text-[var(--muted)] border-b border-[var(--border)]">
|
|
223
|
+
<th className="text-left py-2 px-2 w-10"></th>
|
|
224
|
+
<th className="text-left py-2 px-2">{t('agent.relationshipName')}</th>
|
|
225
|
+
<th className="text-left py-2 px-2">{t('agent.relationshipAffinity')}</th>
|
|
226
|
+
<th className="text-left py-2 px-2">{t('agent.relationshipImpression')}</th>
|
|
227
|
+
</tr>
|
|
228
|
+
</thead>
|
|
229
|
+
<tbody>
|
|
230
|
+
{agent.memory.relationships.map((rel) => {
|
|
231
|
+
const aff = rel.affinity || 50;
|
|
232
|
+
const heart = aff >= 80 ? '❤️' : aff >= 60 ? '😊' : aff >= 40 ? '😐' : aff >= 20 ? '😒' : '💢';
|
|
233
|
+
const barColor = aff >= 80 ? 'bg-red-400' : aff >= 60 ? 'bg-green-400' : aff >= 40 ? 'bg-yellow-400' : aff >= 20 ? 'bg-orange-400' : 'bg-red-600';
|
|
234
|
+
return (
|
|
235
|
+
<tr key={rel.employeeId} className="border-b border-[var(--border)] hover:bg-white/[0.02] transition-colors">
|
|
236
|
+
<td className="py-2 px-2">
|
|
237
|
+
<div className="w-7 h-7 rounded-full bg-gradient-to-br from-indigo-600 to-blue-700 flex items-center justify-center text-[10px]">
|
|
238
|
+
{rel.name?.charAt(0) || '?'}
|
|
239
|
+
</div>
|
|
240
|
+
</td>
|
|
241
|
+
<td className="py-2 px-2 font-medium text-white">{rel.name}</td>
|
|
242
|
+
<td className="py-2 px-2">
|
|
243
|
+
<div className="flex items-center gap-1.5">
|
|
244
|
+
<span className="text-sm">{heart}</span>
|
|
245
|
+
<div className="w-16 h-1.5 bg-white/10 rounded-full overflow-hidden">
|
|
246
|
+
<div className={`h-full ${barColor} rounded-full transition-all`} style={{ width: `${aff}%` }} />
|
|
247
|
+
</div>
|
|
248
|
+
<span className="text-[10px] text-[var(--muted)]">{aff}</span>
|
|
249
|
+
</div>
|
|
250
|
+
</td>
|
|
251
|
+
<td className="py-2 px-2 text-[var(--muted)] italic">{rel.impression}</td>
|
|
252
|
+
</tr>
|
|
253
|
+
);
|
|
254
|
+
})}
|
|
255
|
+
</tbody>
|
|
256
|
+
</table>
|
|
257
|
+
</div>
|
|
258
|
+
)}
|
|
259
|
+
</div>
|
|
260
|
+
)}
|
|
261
|
+
</div>
|
|
262
|
+
)}
|
|
263
|
+
|
|
264
|
+
{activeTab === 'performance' && (
|
|
265
|
+
<div className="space-y-3 animate-fade-in">
|
|
266
|
+
{/* 激励展示区 */}
|
|
267
|
+
{agent.incentives?.length > 0 && (
|
|
268
|
+
<div className="bg-gradient-to-r from-pink-900/15 to-orange-900/15 border border-pink-500/20 rounded-xl p-3">
|
|
269
|
+
<div className="flex items-center gap-2 mb-2">
|
|
270
|
+
<span className="text-base">🌸</span>
|
|
271
|
+
<span className="text-sm font-semibold text-pink-300">{t('agent.incentiveTitle', { n: agent.incentives.length })}</span>
|
|
272
|
+
</div>
|
|
273
|
+
<div className="flex flex-wrap gap-2">
|
|
274
|
+
{agent.incentives.map((inc, i) => (
|
|
275
|
+
<div key={i} className="flex items-center gap-1.5 bg-pink-500/10 border border-pink-500/15 rounded-lg px-2.5 py-1.5">
|
|
276
|
+
<span className="text-sm">{inc.emoji}</span>
|
|
277
|
+
<div>
|
|
278
|
+
<div className="text-xs font-medium text-pink-200">{t(`agent.incentive_${inc.label}`)}</div>
|
|
279
|
+
<div className="text-[10px] text-pink-300/60 truncate max-w-[120px]" title={inc.task}>{inc.task}</div>
|
|
280
|
+
</div>
|
|
281
|
+
<span className="text-[10px] text-pink-400 font-bold ml-1">{t('agent.scorePoints', { score: inc.score })}</span>
|
|
282
|
+
</div>
|
|
283
|
+
))}
|
|
284
|
+
</div>
|
|
285
|
+
</div>
|
|
286
|
+
)}
|
|
287
|
+
{agent.reviews.length === 0 ? (
|
|
288
|
+
<p className="text-sm text-[var(--muted)]">{t('agent.noPerformance')}</p>
|
|
289
|
+
) : agent.reviews.map((r) => (
|
|
290
|
+
<div key={r.id} className="bg-[var(--background)] border border-[var(--border)] rounded-lg p-3">
|
|
291
|
+
<div className="flex items-center justify-between mb-2">
|
|
292
|
+
<span className="text-sm font-medium">{r.task}</span>
|
|
293
|
+
<span className={`text-sm font-bold ${
|
|
294
|
+
r.overallScore >= 80 ? 'text-green-400' :
|
|
295
|
+
r.overallScore >= 60 ? 'text-yellow-400' : 'text-red-400'
|
|
296
|
+
}`}>
|
|
297
|
+
{t('agent.score', { score: r.overallScore, level: r.level })}
|
|
298
|
+
</span>
|
|
299
|
+
</div>
|
|
300
|
+
<div className="grid grid-cols-5 gap-1 mb-2">
|
|
301
|
+
{Object.entries(r.scores).map(([dim, score]) => (
|
|
302
|
+
<div key={dim} className="text-center">
|
|
303
|
+
<div className="text-[10px] text-[var(--muted)]">{dim}</div>
|
|
304
|
+
<div className="text-xs font-medium">{score}</div>
|
|
305
|
+
</div>
|
|
306
|
+
))}
|
|
307
|
+
</div>
|
|
308
|
+
{r.comment && <p className="text-xs text-[var(--muted)]">📝 {r.comment}</p>}
|
|
309
|
+
{r.selfReflection && <p className="text-xs text-blue-400 mt-1">💭 {r.selfReflection}</p>}
|
|
310
|
+
</div>
|
|
311
|
+
))}
|
|
312
|
+
</div>
|
|
313
|
+
)}
|
|
314
|
+
|
|
315
|
+
{activeTab === 'tasks' && (
|
|
316
|
+
<div className="space-y-2 animate-fade-in">
|
|
317
|
+
{agent.taskHistory.length === 0 ? (
|
|
318
|
+
<p className="text-sm text-[var(--muted)]">{t('agent.noTasks')}</p>
|
|
319
|
+
) : agent.taskHistory.map((t, i) => (
|
|
320
|
+
<div key={i} className="bg-[var(--background)] border border-[var(--border)] rounded-lg p-3 text-sm flex items-center gap-3">
|
|
321
|
+
<span>{t.success ? '✅' : '❌'}</span>
|
|
322
|
+
<div className="flex-1">
|
|
323
|
+
<div className="font-medium">{t.task}</div>
|
|
324
|
+
<div className="text-xs text-[var(--muted)]">
|
|
325
|
+
{t.toolsUsed > 0 && t('agent.toolCalls', { n: t.toolsUsed })}
|
|
326
|
+
{t.completedAt && ` · ${new Date(t.completedAt).toLocaleString()}`}
|
|
327
|
+
</div>
|
|
328
|
+
</div>
|
|
329
|
+
</div>
|
|
330
|
+
))}
|
|
331
|
+
</div>
|
|
332
|
+
)}
|
|
333
|
+
|
|
334
|
+
{activeTab === 'usage' && (
|
|
335
|
+
<div className="space-y-4 animate-fade-in">
|
|
336
|
+
<div className="grid grid-cols-2 gap-3">
|
|
337
|
+
<div className="bg-[var(--background)] border border-[var(--border)] rounded-lg p-3">
|
|
338
|
+
<div className="text-xs text-[var(--muted)] mb-1">{t('agent.totalCost')}</div>
|
|
339
|
+
<div className="text-2xl font-bold text-green-400">
|
|
340
|
+
${(agent.tokenUsage?.totalCost || 0).toFixed(4)}
|
|
341
|
+
</div>
|
|
342
|
+
</div>
|
|
343
|
+
<div className="bg-[var(--background)] border border-[var(--border)] rounded-lg p-3">
|
|
344
|
+
<div className="text-xs text-[var(--muted)] mb-1">{t('agent.totalTokens')}</div>
|
|
345
|
+
<div className="text-2xl font-bold text-blue-400">
|
|
346
|
+
{(agent.tokenUsage?.totalTokens || 0).toLocaleString()}
|
|
347
|
+
</div>
|
|
348
|
+
</div>
|
|
349
|
+
<div className="bg-[var(--background)] border border-[var(--border)] rounded-lg p-3">
|
|
350
|
+
<div className="text-xs text-[var(--muted)] mb-1">{t('agent.promptTokens')}</div>
|
|
351
|
+
<div className="text-lg font-bold text-purple-400">
|
|
352
|
+
{(agent.tokenUsage?.promptTokens || 0).toLocaleString()}
|
|
353
|
+
</div>
|
|
354
|
+
</div>
|
|
355
|
+
<div className="bg-[var(--background)] border border-[var(--border)] rounded-lg p-3">
|
|
356
|
+
<div className="text-xs text-[var(--muted)] mb-1">{t('agent.completionTokens')}</div>
|
|
357
|
+
<div className="text-lg font-bold text-orange-400">
|
|
358
|
+
{(agent.tokenUsage?.completionTokens || 0).toLocaleString()}
|
|
359
|
+
</div>
|
|
360
|
+
</div>
|
|
361
|
+
</div>
|
|
362
|
+
<div className="bg-[var(--background)] border border-[var(--border)] rounded-lg p-3">
|
|
363
|
+
<div className="text-xs text-[var(--muted)] mb-1">{t('agent.callCount')}</div>
|
|
364
|
+
<div className="text-lg font-bold">
|
|
365
|
+
{agent.tokenUsage?.callCount || 0} {t('agent.callUnit')}
|
|
366
|
+
</div>
|
|
367
|
+
</div>
|
|
368
|
+
<div className="text-xs text-[var(--muted)]">
|
|
369
|
+
💡 {t('agent.usageHint')}
|
|
370
|
+
</div>
|
|
371
|
+
</div>
|
|
372
|
+
)}
|
|
373
|
+
</div>
|
|
374
|
+
|
|
375
|
+
{/* Agent Chat Modal */}
|
|
376
|
+
{showChat && agent && (
|
|
377
|
+
<AgentChatModal
|
|
378
|
+
agentId={agent.id}
|
|
379
|
+
agentName={agent.name}
|
|
380
|
+
agentAvatar={agent.avatar}
|
|
381
|
+
agentRole={agent.role}
|
|
382
|
+
agentSignature={agent.signature}
|
|
383
|
+
agentDepartment={agent.department}
|
|
384
|
+
onClose={() => setShowChat(false)}
|
|
385
|
+
/>
|
|
386
|
+
)}
|
|
387
|
+
|
|
388
|
+
{/* Agent Spy Modal — 偷窥IM */}
|
|
389
|
+
{showSpy && agent && (
|
|
390
|
+
<AgentSpyModal
|
|
391
|
+
agentId={agent.id}
|
|
392
|
+
agentName={agent.name}
|
|
393
|
+
agentAvatar={agent.avatar}
|
|
394
|
+
onClose={() => setShowSpy(false)}
|
|
395
|
+
/>
|
|
396
|
+
)}
|
|
397
|
+
</div>
|
|
398
|
+
</div>
|
|
399
|
+
);
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* AgentMention - 渲染 @Name 为高亮标签
|
|
404
|
+
*/
|
|
405
|
+
function AgentMention({ content }) {
|
|
406
|
+
if (!content || typeof content !== 'string') return <span>{content}</span>;
|
|
407
|
+
|
|
408
|
+
// 匹配 @Name 格式(支持中英文名字、空格)
|
|
409
|
+
const parts = content.split(/(@[\w\u4e00-\u9fa5][\w\u4e00-\u9fa5\s]*?)(?=\s|$|[,,.。!!??])/g);
|
|
410
|
+
|
|
411
|
+
return (
|
|
412
|
+
<span className="break-words">
|
|
413
|
+
{parts.map((part, i) => {
|
|
414
|
+
if (part && part.startsWith('@')) {
|
|
415
|
+
return (
|
|
416
|
+
<span key={i} className="inline-flex items-center bg-blue-500/20 text-blue-300 px-1 py-0.5 rounded text-xs font-medium mx-0.5">
|
|
417
|
+
{part}
|
|
418
|
+
</span>
|
|
419
|
+
);
|
|
420
|
+
}
|
|
421
|
+
return <span key={i}>{part}</span>;
|
|
422
|
+
})}
|
|
423
|
+
</span>
|
|
424
|
+
);
|
|
425
|
+
}
|