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,625 @@
|
|
|
1
|
+
import { Employee } from './base-employee.js';
|
|
2
|
+
import { createEmployee } from './index.js';
|
|
3
|
+
|
|
4
|
+
import { knowledgeManager } from './knowledge.js';
|
|
5
|
+
import { chatStore } from '../agent/chat-store.js';
|
|
6
|
+
import { cliBackendRegistry } from '../agent/cli-agent/backends/index.js';
|
|
7
|
+
import { JobTemplates } from '../organization/workforce/hr.js';
|
|
8
|
+
import { robustJSONParse } from '../utils/json-parse.js';
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Secretary's Dedicated HR Assistant
|
|
13
|
+
* Handles recruitment operations, talent market search, and recall
|
|
14
|
+
*/
|
|
15
|
+
export class HRAssistant {
|
|
16
|
+
constructor({ secretary, providerConfig }) {
|
|
17
|
+
// HR assistant is always an Employee (LLM-based)
|
|
18
|
+
this.employee = new Employee({
|
|
19
|
+
name: 'HR-Bot',
|
|
20
|
+
role: 'HR Recruiter',
|
|
21
|
+
prompt: `You are the secretary's dedicated HR assistant, responsible for executing recruitment operations.
|
|
22
|
+
Your duties include: searching for suitable candidates in the talent market, evaluating candidates' historical performance and skill match,
|
|
23
|
+
executing the recruitment process, and coordinating new employee onboarding. You need to make optimal decisions between "recalling former employees" and "hiring new ones" based on position requirements.`,
|
|
24
|
+
skills: ['talent-search', 'resume-screening', 'performance-evaluation', 'recruitment-process', 'onboarding'],
|
|
25
|
+
provider: providerConfig,
|
|
26
|
+
});
|
|
27
|
+
this.secretary = secretary;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
smartRecruit(requirement, hr) {
|
|
31
|
+
const { templateId, name, preferRecall = true } = requirement;
|
|
32
|
+
|
|
33
|
+
if (preferRecall && hr.talentMarket) {
|
|
34
|
+
const template = hr.getTemplate(templateId);
|
|
35
|
+
if (template) {
|
|
36
|
+
const candidates = hr.searchTalentMarket({
|
|
37
|
+
role: template.title, skills: template.skills,
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
if (candidates.length > 0) {
|
|
41
|
+
const best = this._pickBestCandidate(candidates, template);
|
|
42
|
+
if (best) {
|
|
43
|
+
console.log(` š [HR-Bot] Found matching candidate in talent market: ${best.name} (${best.role})`);
|
|
44
|
+
const decision = this._decideRecallOrNew(best, template);
|
|
45
|
+
if (decision === 'recall') {
|
|
46
|
+
console.log(` ā
[HR-Bot] Decided to recall former employee: ${best.name}`);
|
|
47
|
+
return hr.recallFromMarket(best.id);
|
|
48
|
+
} else {
|
|
49
|
+
console.log(` š [HR-Bot] Decided to hire new (former employee not a good match)`);
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
} else {
|
|
53
|
+
console.log(` š [HR-Bot] No matching candidates in talent market, will hire new`);
|
|
54
|
+
}
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return hr.recruit(templateId, name);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
_pickBestCandidate(candidates, template) {
|
|
62
|
+
const scored = candidates.map(c => {
|
|
63
|
+
const allSkills = [...c.skills, ...c.acquiredSkills];
|
|
64
|
+
const matchCount = template.skills.filter(s =>
|
|
65
|
+
allSkills.some(cs => cs.includes(s) || s.includes(cs))
|
|
66
|
+
).length;
|
|
67
|
+
const skillScore = matchCount / template.skills.length;
|
|
68
|
+
const perfScore = c.performanceData?.averageScore ? c.performanceData.averageScore / 100 : 0.5;
|
|
69
|
+
return { ...c, totalScore: skillScore * 0.6 + perfScore * 0.4 };
|
|
70
|
+
});
|
|
71
|
+
scored.sort((a, b) => b.totalScore - a.totalScore);
|
|
72
|
+
return scored[0] || null;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
_decideRecallOrNew(candidate, template) {
|
|
76
|
+
if (candidate.performanceData?.averageScore < 50) return 'new';
|
|
77
|
+
const allSkills = [...candidate.skills, ...candidate.acquiredSkills];
|
|
78
|
+
const matchCount = template.skills.filter(s =>
|
|
79
|
+
allSkills.some(cs => cs.includes(s) || s.includes(cs))
|
|
80
|
+
).length;
|
|
81
|
+
if (matchCount >= template.skills.length * 0.5) return 'recall';
|
|
82
|
+
return 'new';
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
/**
|
|
86
|
+
* Execute recruitment based on a team plan.
|
|
87
|
+
* @param {object} plan - Team plan with members array
|
|
88
|
+
* @param {object} hr - HRSystem instance
|
|
89
|
+
* @returns {Array} Array of recruited employees
|
|
90
|
+
*/
|
|
91
|
+
executeRecruitment(plan, hr) {
|
|
92
|
+
console.log(`\nš [HR] Starting recruitment, HR assistant [${this.employee.name}] handling operations...`);
|
|
93
|
+
|
|
94
|
+
const employees = [];
|
|
95
|
+
const skipped = [];
|
|
96
|
+
|
|
97
|
+
for (const memberPlan of plan.members) {
|
|
98
|
+
console.log(`\n š Position: ${memberPlan.templateTitle} (${memberPlan.name})`);
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
const recruitConfig = this.smartRecruit(
|
|
102
|
+
{ templateId: memberPlan.templateId, name: memberPlan.name, preferRecall: true },
|
|
103
|
+
hr
|
|
104
|
+
);
|
|
105
|
+
const employee = createEmployee(recruitConfig);
|
|
106
|
+
|
|
107
|
+
if (recruitConfig.cliBackend) {
|
|
108
|
+
console.log(` š„ļø [${employee.name}] assigned CLI backend: ${recruitConfig.cliBackend}`);
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
if (recruitConfig.isRecalled) {
|
|
112
|
+
console.log(` š [${employee.name}] is a former employee recalled from talent market, carrying original memories`);
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
employees.push(employee);
|
|
116
|
+
} catch (e) {
|
|
117
|
+
if (e.message.startsWith('PROVIDER_DISABLED:')) {
|
|
118
|
+
const parts = e.message.split(':');
|
|
119
|
+
const category = parts[1];
|
|
120
|
+
const reason = parts[2];
|
|
121
|
+
console.log(` ā ļø [HR-Bot] Cannot hire "${memberPlan.templateTitle}": ${reason}`);
|
|
122
|
+
console.log(` Hint: Please configure API Key for ${category} type providers first`);
|
|
123
|
+
skipped.push({ ...memberPlan, reason });
|
|
124
|
+
employees.push(null);
|
|
125
|
+
} else {
|
|
126
|
+
throw e;
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
const validEmployees = employees.filter(Boolean);
|
|
132
|
+
|
|
133
|
+
for (let i = 0; i < plan.members.length; i++) {
|
|
134
|
+
if (!employees[i]) continue;
|
|
135
|
+
const memberPlan = plan.members[i];
|
|
136
|
+
if (memberPlan.reportsTo !== null && employees[memberPlan.reportsTo]) {
|
|
137
|
+
employees[i].setManager(employees[memberPlan.reportsTo]);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
if (skipped.length > 0) {
|
|
142
|
+
console.log(`\nā ļø [HR] ${skipped.length} positions skipped due to unconfigured providers:`);
|
|
143
|
+
skipped.forEach(s => console.log(` - ${s.templateTitle}: ${s.reason}`));
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
console.log(`\nā
[HR] Recruitment complete! Successfully hired ${validEmployees.length}, skipped ${skipped.length}`);
|
|
147
|
+
return validEmployees;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
/**
|
|
152
|
+
* Secretary ā A specialized Employee.
|
|
153
|
+
* Extends Employee with team design, recruitment coordination,
|
|
154
|
+
* boss message handling, and direct task execution abilities.
|
|
155
|
+
*/
|
|
156
|
+
export class Secretary extends Employee {
|
|
157
|
+
constructor({ company, providerConfig, secretaryName, secretaryAvatar, secretaryGender, secretaryAge }) {
|
|
158
|
+
super({
|
|
159
|
+
name: secretaryName || 'Secretary',
|
|
160
|
+
role: 'Personal Secretary',
|
|
161
|
+
prompt: `You are the boss's personal secretary, the core hub for company operations management.
|
|
162
|
+
|
|
163
|
+
## Your Core Capabilities:
|
|
164
|
+
1. **Organizational Management**: Create new departments, dissolve departments, adjust organizational structure
|
|
165
|
+
2. **Human Resources**: Coordinate HR recruitment, adjust staffing, transfer employees between departments, design team composition
|
|
166
|
+
3. **Task Management**: Understand boss requirements, assign tasks to appropriate departments, track and report progress
|
|
167
|
+
4. **Business Analysis**: Analyze business requirements, plan team size and role types based on project needs
|
|
168
|
+
5. **Team Design**: Design organizational structure (who does what, who reports to whom, how to collaborate), ensure teams can efficiently achieve goals
|
|
169
|
+
|
|
170
|
+
You have a dedicated HR assistant to help you handle specific recruitment tasks, including searching and recalling talent from the talent market.
|
|
171
|
+
|
|
172
|
+
When communicating with the boss, you need to:
|
|
173
|
+
1. Understand the boss's intent (create department, assign task, adjust staffing, progress inquiry, or casual conversation)
|
|
174
|
+
2. For department creation requests: design the team structure and initiate recruitment
|
|
175
|
+
3. For task requests: assign to the corresponding department or handle simple ones yourself
|
|
176
|
+
4. For staffing adjustments: coordinate HR to execute personnel changes
|
|
177
|
+
5. Periodically report department progress to the boss`,
|
|
178
|
+
skills: ['requirements-analysis', 'team-planning', 'org-design', 'hr-coordination', 'project-management', 'task-assignment', 'progress-reporting'],
|
|
179
|
+
provider: providerConfig,
|
|
180
|
+
avatar: secretaryAvatar,
|
|
181
|
+
gender: secretaryGender || 'female',
|
|
182
|
+
age: secretaryAge || 18,
|
|
183
|
+
});
|
|
184
|
+
|
|
185
|
+
this.company = company;
|
|
186
|
+
|
|
187
|
+
// Initialize dedicated HR assistant
|
|
188
|
+
this.hrAssistant = new HRAssistant({
|
|
189
|
+
secretary: this,
|
|
190
|
+
providerConfig,
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
console.log(` š§āš¼ Secretary's dedicated HR assistant is ready: ${this.hrAssistant.employee.name}`);
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// ======================== Boss Message Handling ========================
|
|
197
|
+
|
|
198
|
+
async handleBossMessage(message, company) {
|
|
199
|
+
if (!this.canChat()) {
|
|
200
|
+
if (this.agentType === 'cli') {
|
|
201
|
+
throw new Error('Secretary is in CLI mode. Please use chatWithSecretary() which handles CLI path correctly.');
|
|
202
|
+
}
|
|
203
|
+
throw new Error('Secretary AI is not configured. Please configure a valid API Key for the secretary provider first.');
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
// Session initialization follows lazy-loading principle:
|
|
207
|
+
// _ensureSession() is called automatically inside this.chat()
|
|
208
|
+
// when _llmHandleBossMessage eventually calls this.chat(messages)
|
|
209
|
+
return await this._llmHandleBossMessage(message, company);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
async _llmHandleBossMessage(message, company) {
|
|
213
|
+
this.memory.consolidateMemories();
|
|
214
|
+
|
|
215
|
+
const deptCount = company.departments.size;
|
|
216
|
+
const departments = [...company.departments.values()].map(d => ({
|
|
217
|
+
name: d.name, id: d.id, mission: d.mission, status: d.status,
|
|
218
|
+
memberCount: d.agents.size,
|
|
219
|
+
leader: d.getLeader()?.name || 'Unassigned',
|
|
220
|
+
members: [...d.agents.values()].map(a => ({
|
|
221
|
+
name: a.name, role: a.role, status: a.status,
|
|
222
|
+
})),
|
|
223
|
+
}));
|
|
224
|
+
const agentCount = departments.reduce((s, d) => s + d.memberCount, 0);
|
|
225
|
+
const talentCount = company.talentMarket.listAvailable().length;
|
|
226
|
+
|
|
227
|
+
let recentHistory = [];
|
|
228
|
+
try {
|
|
229
|
+
const recentMessages = chatStore.getRecentMessages(company.chatSessionId, 10);
|
|
230
|
+
recentHistory = recentMessages.map(h => ({
|
|
231
|
+
role: h.role === 'boss' ? 'user' : 'assistant',
|
|
232
|
+
content: h.content,
|
|
233
|
+
}));
|
|
234
|
+
} catch (e) {
|
|
235
|
+
recentHistory = (company.chatHistory || []).slice(-10).map(h => ({
|
|
236
|
+
role: h.role === 'boss' ? 'user' : 'assistant',
|
|
237
|
+
content: h.content,
|
|
238
|
+
}));
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
let searchContextSection = '';
|
|
242
|
+
try {
|
|
243
|
+
const searchResults = chatStore.searchWithContext(company.chatSessionId, message, 3, 1);
|
|
244
|
+
if (searchResults.length > 0) {
|
|
245
|
+
searchContextSection = '\n## Related Historical Context (from past conversations)\n';
|
|
246
|
+
for (const result of searchResults) {
|
|
247
|
+
const contextStr = result.context.map(m =>
|
|
248
|
+
` [${m.role}] ${m.content.slice(0, 150)}${m.content.length > 150 ? '...' : ''}`
|
|
249
|
+
).join('\n');
|
|
250
|
+
searchContextSection += `- Relevance: ${result.score.toFixed(2)}\n${contextStr}\n\n`;
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
} catch (e) {}
|
|
254
|
+
|
|
255
|
+
const secretaryPrompt = this.prompt || '';
|
|
256
|
+
|
|
257
|
+
// Build HR context: available job templates + enabled providers
|
|
258
|
+
const availableRoles = Object.values(JobTemplates).map(t => ({
|
|
259
|
+
id: t.id, title: t.title, category: t.category,
|
|
260
|
+
}));
|
|
261
|
+
const enabledProviders = company.providerRegistry.listEnabled().map(p => ({
|
|
262
|
+
id: p.id, name: p.name, category: p.category,
|
|
263
|
+
isCLI: p.isCLI || false, cliBackendId: p.cliBackendId || null,
|
|
264
|
+
}));
|
|
265
|
+
const availableCategories = [...new Set(enabledProviders.map(p => p.category))];
|
|
266
|
+
|
|
267
|
+
let capabilitiesSection = '';
|
|
268
|
+
try {
|
|
269
|
+
const kbs = knowledgeManager.list();
|
|
270
|
+
if (kbs.length > 0) {
|
|
271
|
+
capabilitiesSection += `\n## Knowledge Bases (${kbs.length})\n`;
|
|
272
|
+
kbs.forEach(kb => { capabilitiesSection += `- š ${kb.name}: ${kb.description} (${kb.entryCount || 0} entries)\n`; });
|
|
273
|
+
}
|
|
274
|
+
} catch {}
|
|
275
|
+
|
|
276
|
+
// Build memory context using the unified Memory system
|
|
277
|
+
const secretaryChatGroupId = `secretary-boss-chat`;
|
|
278
|
+
const memorySection = this.memory.buildMemoryContext(secretaryChatGroupId);
|
|
279
|
+
|
|
280
|
+
const systemPrompt = `You are "${this.name}", the personal secretary of ${company.bossName || 'the Boss'}.
|
|
281
|
+
${secretaryPrompt ? `\nYour core persona: ${secretaryPrompt}\n` : ''}
|
|
282
|
+
Your personality: smart, efficient, approachable. Communicate with the boss like a real, thoughtful secretary ā natural, warm, not robotic.
|
|
283
|
+
${memorySection}
|
|
284
|
+
${searchContextSection}
|
|
285
|
+
Current company "${company.name}" status:
|
|
286
|
+
- Departments: ${deptCount}
|
|
287
|
+
- Active employees: ${agentCount}
|
|
288
|
+
- Talent market: ${talentCount} available
|
|
289
|
+
${departments.length > 0 ? `\nDepartment details:\n${departments.map(d => ` š¢ ${d.name} [${d.status}] - Mission: ${d.mission} | ${d.memberCount} people | Leader: ${d.leader}\n Members: ${d.members.map(m => m.name + '(' + m.role + ')').join(', ')}`).join('\n')}` : '\nNo departments yet.'}
|
|
290
|
+
${capabilitiesSection}
|
|
291
|
+
|
|
292
|
+
You must understand the boss's intent and reply naturally. Your reply MUST be a JSON object (return JSON only, nothing else):
|
|
293
|
+
{
|
|
294
|
+
"content": "Your natural language reply (like a real secretary ā warm, personal, no rigid templates)",
|
|
295
|
+
"memorySummary": "A concise summary of older conversation messages ā keep key facts, decisions, instructions, names. null if conversation just started or no old messages to summarize.",
|
|
296
|
+
"memoryOps": [
|
|
297
|
+
{ "op": "add", "type": "long_term", "content": "Boss prefers weekly reports on Monday", "category": "preference", "importance": 8 },
|
|
298
|
+
{ "op": "add", "type": "short_term", "content": "Boss asked about Q3 revenue data", "category": "task", "importance": 5, "ttl": 3600 },
|
|
299
|
+
{ "op": "update", "id": "existing_mem_id", "content": "Updated content", "importance": 7 },
|
|
300
|
+
{ "op": "delete", "id": "outdated_mem_id" }
|
|
301
|
+
]
|
|
302
|
+
Memory management rules:
|
|
303
|
+
- memoryOps: Array of memory operations to manage your knowledge base
|
|
304
|
+
- "add" + "long_term": Important facts, boss preferences, standing instructions, key decisions (stays forever)
|
|
305
|
+
- "add" + "short_term": Current task context, temporary info (auto-expires, ttl in seconds, default 24h)
|
|
306
|
+
- "update": Modify an existing memory by id when info changes
|
|
307
|
+
- "delete": Remove outdated or incorrect memories by id
|
|
308
|
+
- category: preference | fact | instruction | task | context | relationship | experience | decision
|
|
309
|
+
- importance: 1-10 (higher = more important, less likely to be forgotten)
|
|
310
|
+
- Only add memory when the boss tells you something worth remembering. Do NOT memorize casual greetings.
|
|
311
|
+
- If nothing to add/update/delete, set memoryOps to [].
|
|
312
|
+
"relationshipOps": [
|
|
313
|
+
{ "employeeId": "boss", "name": "Boss", "impression": "Decisive, prefers concise updates", "affinity": 70 }
|
|
314
|
+
]
|
|
315
|
+
Relationship impression rules:
|
|
316
|
+
- relationshipOps: Update your personal impression of the boss or other people mentioned in conversation. Max 200 chars. affinity: 1-100 (50=neutral).
|
|
317
|
+
- affinity should change gradually (+/- 5~15 per interaction). Start from 50 if first meeting.
|
|
318
|
+
- Only update when something noteworthy happened. [] if nothing to update.
|
|
319
|
+
"action": null or one of the following:
|
|
320
|
+
- { "type": "secretary_handle", "taskDescription": "detailed task description for yourself to execute" } - when you can handle this task yourself without needing a department (see rules below)
|
|
321
|
+
- { "type": "task_assigned", "departmentId": "dept ID", "departmentName": "dept name", "taskTitle": "short task title (under 10 words)", "taskDescription": "detailed task description including what to do and what to deliver" } - when the boss wants to assign a task to an existing department
|
|
322
|
+
- { "type": "create_department", "departmentName": "department name", "mission": "department mission/responsibilities", "members": [ { "templateId": "job template id", "name": "creative employee nickname", "isLeader": true/false, "reportsTo": null or member index (0=first member) } ] } - when the boss explicitly requests creating a new department ā you MUST design the team directly
|
|
323
|
+
- { "type": "need_new_department", "suggestedMission": "task description" } - when the boss wants to assign a task but no existing department can handle it (need to create dept first then assign)
|
|
324
|
+
- { "type": "progress_report" } - when the boss wants to see progress reports
|
|
325
|
+
- null - casual chat or no special action needed
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
## Intent Rules (by priority, high to low):
|
|
329
|
+
|
|
330
|
+
**Highest Priority - Organizational Management**:
|
|
331
|
+
When the boss says "create/establish/set up/found + department", this is an org management operation. **MUST** return create_department, **NEVER** return task_assigned.
|
|
332
|
+
- Even if the message contains words like "help me", as long as the core intent is "create/establish a department", return create_department
|
|
333
|
+
- departmentName: intelligently name the department based on boss's description
|
|
334
|
+
- mission: summarize department mission and responsibilities from boss's description
|
|
335
|
+
- members: you MUST design the team directly! Plan 2-6 members based on the mission. Rules:
|
|
336
|
+
* Available job templates (you can ONLY choose from these): ${JSON.stringify(availableRoles)}
|
|
337
|
+
* Currently enabled provider categories: ${availableCategories.join(', ')}
|
|
338
|
+
* You can ONLY use templates whose category has an enabled provider (enabled categories above)
|
|
339
|
+
* The first member MUST be project-leader with isLeader=true
|
|
340
|
+
* Other members' reportsTo = 0 (index of leader)
|
|
341
|
+
* If CLI providers are available and the task is coding-related, prefer cli-* templates
|
|
342
|
+
* Employee names should be creative and fun
|
|
343
|
+
|
|
344
|
+
**High Priority - Secretary Handles Simple Tasks Directly**:
|
|
345
|
+
For simple, straightforward tasks that DON'T require a specialized team, you should handle them yourself using secretary_handle. Examples:
|
|
346
|
+
- Writing/drafting: emails, short messages, announcements, summaries, translations
|
|
347
|
+
- Quick analysis: simple comparisons, brief research, quick calculations
|
|
348
|
+
- Information tasks: looking up info, explaining concepts, answering questions with your knowledge
|
|
349
|
+
- Planning/organizing: making a schedule, creating a checklist, brainstorming ideas
|
|
350
|
+
- Creative writing: slogans, naming suggestions, short copy, social media posts
|
|
351
|
+
- Any task that a competent secretary could do alone in a few minutes using only their own knowledge
|
|
352
|
+
**Tasks you CANNOT handle yourself** (do NOT use secretary_handle for these):
|
|
353
|
+
- Anything requiring deep domain expertise that needs a specialized department team
|
|
354
|
+
- Large-scale projects that need ongoing team collaboration
|
|
355
|
+
Note: You DO have access to tools (shell commands, file operations, etc.) when executing tasks, so you CAN:
|
|
356
|
+
- Look up real-time info via shell commands (curl, etc.)
|
|
357
|
+
- Run code and scripts
|
|
358
|
+
- Read/write files
|
|
359
|
+
When returning secretary_handle:
|
|
360
|
+
- taskDescription should be a clear, detailed description of what you need to do
|
|
361
|
+
- In "content", ONLY give a brief acknowledgement like "Let me check!" or "One moment, I'll handle it right away! š"
|
|
362
|
+
- **ABSOLUTELY DO NOT** attempt to answer the question or provide any result in "content" ā the actual answer will come from executeTaskDirectly using tools
|
|
363
|
+
- **NEVER** include placeholders like [current date], [loading...] in "content" ā just acknowledge and let the execution phase do the real work
|
|
364
|
+
- Example: Boss asks "What's today's date?" ā content: "Let me check for you~ š", action: secretary_handle with taskDescription: "Query the current date and inform the boss"
|
|
365
|
+
|
|
366
|
+
**Medium Priority - Assign Task to Existing Department (check departments first!)**:
|
|
367
|
+
When the boss wants something done that requires specialized team work, you MUST first check ALL existing departments listed above to see if any can handle it.
|
|
368
|
+
Matching criteria (any one is sufficient to assign):
|
|
369
|
+
1. Department name contains task-related keywords (e.g. task is "travel guide", dept named "Travel Guide Dept" ā match)
|
|
370
|
+
2. Department mission/description relates to task content (e.g. dept mission mentions "travel"/"guide", task is also travel-related ā match)
|
|
371
|
+
3. Department name's core words are semantically related to the task (e.g. "Financial Analysis Dept" can handle "stock analysis" tasks)
|
|
372
|
+
Once a matching department is found, you **MUST** return task_assigned, **ABSOLUTELY CANNOT** return need_new_department!
|
|
373
|
+
- departmentId MUST be the real id field from the department info above, don't fabricate
|
|
374
|
+
- departmentName MUST exactly match the name corresponding to the id
|
|
375
|
+
- taskDescription should detail the task content, goals, and deliverables
|
|
376
|
+
- **The department name mentioned in content MUST match the departmentName in action ā you can't say "assigning to Dept A" in content but point action to Dept B**
|
|
377
|
+
|
|
378
|
+
**Low Priority - Need New Department (rarely used!)**:
|
|
379
|
+
**ONLY when you've checked every existing department and confirmed none has even the slightest relation to the task** can you return need_new_department.
|
|
380
|
+
ā ļø Before returning need_new_department, triple-check:
|
|
381
|
+
- Have you checked every existing department?
|
|
382
|
+
- Is there really no department whose name contains task-related words?
|
|
383
|
+
- Is there really no department whose mission relates to the task?
|
|
384
|
+
If in any doubt, assign to the closest department (task_assigned) rather than returning need_new_department
|
|
385
|
+
|
|
386
|
+
**Lowest Priority - View Progress**:
|
|
387
|
+
When the boss asks about progress/status/reports, return progress_report
|
|
388
|
+
|
|
389
|
+
**No Action**: casual chat, greetings, etc. ā set action to null
|
|
390
|
+
|
|
391
|
+
## Notes:
|
|
392
|
+
1. Content should be natural and personal, avoid rigid templates, feel free to add emoji
|
|
393
|
+
2. Keep replies concise, don't be verbose
|
|
394
|
+
3. **Critical**: When the boss's message contains action verbs (like "help me", "build", "develop", "design", "do", "write", "analyze", "research", "create", "produce", "plan", etc.), you **MUST** return an action. **ABSOLUTELY NEVER** just say "I'll arrange it" in content without returning an action. This is the most important rule ā without an action, the task won't actually execute!
|
|
395
|
+
4. **Critical**: Carefully distinguish "creating a department" from "assigning a task" ā "help me set up an XX department" is create_department, while "help me write an analysis report" is task assignment (task_assigned/need_new_department)
|
|
396
|
+
5. **Critical**: When the boss says "do XX", "help me XX" and other clear task directives, return task_assigned if there's a suitable existing department, otherwise return need_new_department. Never just chat without working!
|
|
397
|
+
6. **Critical - Consistency Principle**: Your content and action MUST be consistent! If content says "assigning to XX department", then action's departmentId/departmentName must point to that same department. If content mentions one department but action is need_new_department, that's a serious error!
|
|
398
|
+
7. **Critical - Structured Output**: You MUST always return valid JSON. Do NOT wrap it in markdown code fences. Do NOT add any text outside the JSON object. The response must start with { and end with }.
|
|
399
|
+
8. **Critical - Action Required for Tasks**: If the boss's message expresses ANY intent to get work done (in any language), you MUST return an action. Analyze the semantic meaning, not just keywords. For example: "help me build a website", "write a report", "analyze the data" ā ALL of these require an action, regardless of what language they are expressed in.
|
|
400
|
+
9. **Critical - Language Agnostic**: The boss may speak in any language (Chinese, English, Japanese, etc.). You must understand the intent regardless of language and return the correct structured action.`;
|
|
401
|
+
const messages = [
|
|
402
|
+
{ role: 'system', content: systemPrompt },
|
|
403
|
+
...recentHistory,
|
|
404
|
+
{ role: 'user', content: message },
|
|
405
|
+
];
|
|
406
|
+
|
|
407
|
+
const response = await this.chat(messages, { temperature: 0.8, maxTokens: 2048 });
|
|
408
|
+
|
|
409
|
+
try {
|
|
410
|
+
const parsed = robustJSONParse(response.content);
|
|
411
|
+
let result = { content: parsed.content || response.content, action: parsed.action || null };
|
|
412
|
+
|
|
413
|
+
console.log(`š¤ [Secretary-LLM] action type: ${result.action?.type || 'null'}, departmentId: ${result.action?.departmentId || 'N/A'}`);
|
|
414
|
+
|
|
415
|
+
// Process rolling history summary (new Memory system)
|
|
416
|
+
if (parsed.memorySummary) {
|
|
417
|
+
this.memory.updateHistorySummary(secretaryChatGroupId, parsed.memorySummary);
|
|
418
|
+
console.log(` š [Secretary] History summary updated`);
|
|
419
|
+
}
|
|
420
|
+
|
|
421
|
+
// Process memory operations (new unified format)
|
|
422
|
+
if (parsed.memoryOps && Array.isArray(parsed.memoryOps)) {
|
|
423
|
+
const memResult = this.memory.processMemoryOps(parsed.memoryOps);
|
|
424
|
+
if (memResult.added + memResult.updated + memResult.deleted > 0) {
|
|
425
|
+
console.log(` š§ [Secretary] Memory: +${memResult.added} ~${memResult.updated} -${memResult.deleted}`);
|
|
426
|
+
}
|
|
427
|
+
}
|
|
428
|
+
|
|
429
|
+
// Process relationship impressions
|
|
430
|
+
if (parsed.relationshipOps && Array.isArray(parsed.relationshipOps)) {
|
|
431
|
+
const relResult = this.memory.processRelationshipOps(parsed.relationshipOps);
|
|
432
|
+
if (relResult.updated > 0) {
|
|
433
|
+
console.log(` š„ [Secretary] Relationship updates: ${relResult.updated}`);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
if (result.action?.type === 'task_assigned' && result.action.departmentId) {
|
|
438
|
+
const deptById = company.departments.get(result.action.departmentId);
|
|
439
|
+
if (!deptById) {
|
|
440
|
+
const deptIdValue = result.action.departmentId;
|
|
441
|
+
const deptNameValue = result.action.departmentName || deptIdValue;
|
|
442
|
+
let foundDept = null;
|
|
443
|
+
for (const dept of company.departments.values()) {
|
|
444
|
+
if (dept.name === deptIdValue || dept.name === deptNameValue ||
|
|
445
|
+
dept.name.includes(deptIdValue) || deptIdValue.includes(dept.name)) {
|
|
446
|
+
foundDept = dept; break;
|
|
447
|
+
}
|
|
448
|
+
}
|
|
449
|
+
if (foundDept) {
|
|
450
|
+
console.log(`š§ Fixed departmentId: "${deptIdValue}" ā "${foundDept.id}" (${foundDept.name})`);
|
|
451
|
+
result.action.departmentId = foundDept.id;
|
|
452
|
+
result.action.departmentName = foundDept.name;
|
|
453
|
+
} else {
|
|
454
|
+
console.warn(`ā ļø LLM returned departmentId "${deptIdValue}" doesn't match any department, clearing action`);
|
|
455
|
+
result.action = null;
|
|
456
|
+
}
|
|
457
|
+
} else {
|
|
458
|
+
const contentLower = (result.content || '').toLowerCase();
|
|
459
|
+
const actionDeptName = deptById.name.toLowerCase();
|
|
460
|
+
let contentMentionedDept = null;
|
|
461
|
+
for (const dept of company.departments.values()) {
|
|
462
|
+
if (dept.id !== deptById.id && contentLower.includes(dept.name.toLowerCase())) {
|
|
463
|
+
contentMentionedDept = dept; break;
|
|
464
|
+
}
|
|
465
|
+
}
|
|
466
|
+
if (contentMentionedDept && !contentLower.includes(actionDeptName)) {
|
|
467
|
+
console.log(`š§ Consistency fix: content mentions "${contentMentionedDept.name}" but action points to "${deptById.name}", using content as source of truth`);
|
|
468
|
+
result.action.departmentId = contentMentionedDept.id;
|
|
469
|
+
result.action.departmentName = contentMentionedDept.name;
|
|
470
|
+
}
|
|
471
|
+
}
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
return result;
|
|
475
|
+
} catch (parseError) {
|
|
476
|
+
console.warn('ā ļø Secretary JSON parse failed:', parseError.message, '\nRaw reply:', response.content.slice(0, 200));
|
|
477
|
+
|
|
478
|
+
let displayContent = response.content;
|
|
479
|
+
const contentFieldMatch = response.content.match(/"content"\s*:\s*"((?:[^"\\]|\\.)*)"/);
|
|
480
|
+
if (contentFieldMatch) {
|
|
481
|
+
try { displayContent = JSON.parse('"' + contentFieldMatch[1] + '"'); }
|
|
482
|
+
catch { displayContent = contentFieldMatch[1].replace(/\\n/g, '\n').replace(/\\"/g, '"'); }
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
let action = null;
|
|
486
|
+
const actionTypeMatch = response.content.match(/"type"\s*:\s*"(task_assigned|need_new_department|create_department|progress_report|secretary_handle)"/);
|
|
487
|
+
if (actionTypeMatch) {
|
|
488
|
+
const actionType = actionTypeMatch[1];
|
|
489
|
+
if (actionType === 'task_assigned') {
|
|
490
|
+
const deptNameMatch = response.content.match(/"departmentName"\s*:\s*"([^"]+)"/);
|
|
491
|
+
if (deptNameMatch) {
|
|
492
|
+
const targetName = deptNameMatch[1];
|
|
493
|
+
for (const dept of company.departments.values()) {
|
|
494
|
+
if (dept.name === targetName || dept.name.includes(targetName) || targetName.includes(dept.name)) {
|
|
495
|
+
const titleMatch = response.content.match(/"taskTitle"\s*:\s*"([^"]+)"/);
|
|
496
|
+
const descMatch = response.content.match(/"taskDescription"\s*:\s*"((?:[^"\\]|\\.)*)"/);
|
|
497
|
+
action = {
|
|
498
|
+
type: 'task_assigned', departmentId: dept.id, departmentName: dept.name,
|
|
499
|
+
taskTitle: titleMatch ? titleMatch[1] : message.slice(0, 50),
|
|
500
|
+
taskDescription: descMatch ? descMatch[1].replace(/\\n/g, '\n') : message,
|
|
501
|
+
}; break;
|
|
502
|
+
}
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
} else if (actionType === 'create_department') {
|
|
506
|
+
const deptNameMatch = response.content.match(/"departmentName"\s*:\s*"([^"]+)"/);
|
|
507
|
+
const missionMatch = response.content.match(/"mission"\s*:\s*"((?:[^"\\]|\\.)*)"/);
|
|
508
|
+
action = {
|
|
509
|
+
type: 'create_department',
|
|
510
|
+
departmentName: deptNameMatch ? deptNameMatch[1] : '',
|
|
511
|
+
mission: missionMatch ? missionMatch[1].replace(/\\n/g, '\n') : message,
|
|
512
|
+
};
|
|
513
|
+
} else if (actionType === 'need_new_department') {
|
|
514
|
+
const missionMatch = response.content.match(/"suggestedMission"\s*:\s*"((?:[^"\\]|\\.)*)"/);
|
|
515
|
+
action = { type: 'need_new_department', suggestedMission: missionMatch ? missionMatch[1].replace(/\\n/g, '\n') : message };
|
|
516
|
+
} else if (actionType === 'secretary_handle') {
|
|
517
|
+
const descMatch = response.content.match(/"taskDescription"\s*:\s*"((?:[^"\\]|\\.)*)"/);
|
|
518
|
+
action = { type: 'secretary_handle', taskDescription: descMatch ? descMatch[1].replace(/\\n/g, '\n') : message };
|
|
519
|
+
} else if (actionType === 'progress_report') {
|
|
520
|
+
action = { type: 'progress_report' };
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
let result = { content: displayContent, action };
|
|
525
|
+
console.log(`š¤ [Secretary-LLM-FaultTolerant] action type: ${result.action?.type || 'null'}`);
|
|
526
|
+
|
|
527
|
+
// Try to extract memory ops from malformed JSON (fault-tolerant)
|
|
528
|
+
try {
|
|
529
|
+
const summaryMatch = response.content.match(/"memorySummary"\s*:\s*"((?:[^"\\]|\\.)*)"/);
|
|
530
|
+
if (summaryMatch) {
|
|
531
|
+
this.memory.updateHistorySummary(secretaryChatGroupId, summaryMatch[1].replace(/\\n/g, '\n'));
|
|
532
|
+
}
|
|
533
|
+
const memOpsMatch = response.content.match(/"memoryOps"\s*:\s*(\[\s*\{[\s\S]*?\}\s*\])/);
|
|
534
|
+
if (memOpsMatch) {
|
|
535
|
+
const ops = JSON.parse(memOpsMatch[1]);
|
|
536
|
+
if (Array.isArray(ops)) {
|
|
537
|
+
this.memory.processMemoryOps(ops);
|
|
538
|
+
}
|
|
539
|
+
}
|
|
540
|
+
} catch {}
|
|
541
|
+
|
|
542
|
+
return result;
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
|
|
546
|
+
// ======================== Direct Task Execution ========================
|
|
547
|
+
|
|
548
|
+
async executeTaskDirectly(taskDescription, company) {
|
|
549
|
+
console.log(`\nš [Secretary] Handling task directly: "${taskDescription.slice(0, 50)}..."`);
|
|
550
|
+
|
|
551
|
+
const secretaryPrompt = this.prompt || '';
|
|
552
|
+
|
|
553
|
+
const memoryContext = this.memory.buildMemoryContext('secretary-task-exec');
|
|
554
|
+
|
|
555
|
+
const systemPrompt = `You are "${this.name}", the personal secretary of ${company.bossName || 'the Boss'}.
|
|
556
|
+
${secretaryPrompt ? `\nYour persona: ${secretaryPrompt}\n` : ''}
|
|
557
|
+
You are now personally handling a task from the boss. Complete it thoroughly and deliver a high-quality result.
|
|
558
|
+
${memoryContext}
|
|
559
|
+
## Your Capabilities:
|
|
560
|
+
- You have access to tools: shell_exec (run shell commands like curl, date, node, python, etc.), file_read, file_write, file_list, file_delete
|
|
561
|
+
- You CAN access the internet via shell commands (e.g., curl for APIs, web requests)
|
|
562
|
+
- You CAN run code and scripts to get real-time data
|
|
563
|
+
- You CAN get the current date/time by running: shell_exec({ command: "date" })
|
|
564
|
+
- You CAN fetch weather by running: shell_exec({ command: "curl -s wttr.in/CityName?format=3" })
|
|
565
|
+
|
|
566
|
+
## CRITICAL RULES (MUST follow):
|
|
567
|
+
- **ALWAYS use tools first** before answering questions about real-time or factual data (date, time, weather, calculations, etc.)
|
|
568
|
+
- You do NOT know the current date or time ā you MUST call shell_exec with "date" to find out
|
|
569
|
+
- You do NOT know real-time information ā you MUST call shell_exec with appropriate commands to fetch it
|
|
570
|
+
- NEVER guess, assume, or use placeholders like [current date], [loading...], [TBD]
|
|
571
|
+
- If you're unsure about any factual information, USE A TOOL to verify it
|
|
572
|
+
- If a tool call fails, try alternative approaches before giving up
|
|
573
|
+
|
|
574
|
+
## Guidelines:
|
|
575
|
+
1. Deliver a complete, ready-to-use result (not just a plan or outline)
|
|
576
|
+
2. Be thorough but concise ā quality over quantity
|
|
577
|
+
3. Match the language of the task description (if the boss asked in Chinese, reply in Chinese)
|
|
578
|
+
4. Format the output nicely with markdown if appropriate
|
|
579
|
+
5. If the task involves writing, produce the actual writing (not meta-commentary about it)
|
|
580
|
+
6. Sign off naturally as a secretary would`;
|
|
581
|
+
|
|
582
|
+
try {
|
|
583
|
+
if (this.agentType === 'cli' && this.isAvailable()) {
|
|
584
|
+
try {
|
|
585
|
+
console.log(` š„ļø [Secretary] Executing task via CLI backend: ${this.cliBackend}`);
|
|
586
|
+
const cliResult = await cliBackendRegistry.executeTask(
|
|
587
|
+
this.cliBackend, this,
|
|
588
|
+
{ title: `Secretary task`, description: `${systemPrompt}\n\nTask from the boss:\n${taskDescription}` },
|
|
589
|
+
this.toolKit?.workspaceDir || process.cwd(), {}, { timeout: 120000 }
|
|
590
|
+
);
|
|
591
|
+
const content = cliResult.output || cliResult.errorOutput || '...';
|
|
592
|
+
console.log(`ā
[Secretary] CLI task completed, output ${content.length} chars`);
|
|
593
|
+
return { content, success: true };
|
|
594
|
+
} catch (cliErr) {
|
|
595
|
+
if (!this.canChat()) {
|
|
596
|
+
console.error(` ā [Secretary] CLI task failed, no LLM fallback: ${cliErr.message || cliErr.error}`);
|
|
597
|
+
return { content: `ā ļø CLI task execution error: ${cliErr.message || 'Unknown error'}. Please check that the CLI is running correctly.`, success: false };
|
|
598
|
+
}
|
|
599
|
+
console.warn(` ā ļø [Secretary] CLI task failed, falling back to LLM: ${cliErr.message || cliErr.error}`);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
const toolExecutor = this.toolKit;
|
|
604
|
+
let response;
|
|
605
|
+
|
|
606
|
+
if (toolExecutor) {
|
|
607
|
+
response = await this.chatWithTools(
|
|
608
|
+
[{ role: 'system', content: systemPrompt }, { role: 'user', content: taskDescription }],
|
|
609
|
+
toolExecutor, { temperature: 0.7, maxTokens: 2048, maxIterations: 5, newConversation: true }
|
|
610
|
+
);
|
|
611
|
+
} else {
|
|
612
|
+
response = await this.chat(
|
|
613
|
+
[{ role: 'system', content: systemPrompt }, { role: 'user', content: taskDescription }],
|
|
614
|
+
{ temperature: 0.7, maxTokens: 2048, newConversation: true }
|
|
615
|
+
);
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
console.log(`ā
[Secretary] Task completed, output ${response.content.length} chars`);
|
|
619
|
+
return { content: response.content, success: true };
|
|
620
|
+
} catch (err) {
|
|
621
|
+
console.error(`ā [Secretary] Task execution failed:`, err.message);
|
|
622
|
+
return { content: `Sorry, encountered an issue while executing the task: ${err.message}`, success: false };
|
|
623
|
+
}
|
|
624
|
+
}
|
|
625
|
+
}
|