@zhin.js/agent 0.1.0 → 0.1.3
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/lib/cron-engine.d.ts +20 -2
- package/lib/cron-engine.d.ts.map +1 -1
- package/lib/cron-engine.js +59 -18
- package/lib/cron-engine.js.map +1 -1
- package/lib/discover-skills.d.ts +3 -1
- package/lib/discover-skills.d.ts.map +1 -1
- package/lib/discover-skills.js +7 -9
- package/lib/discover-skills.js.map +1 -1
- package/lib/discover-tools.d.ts +1 -6
- package/lib/discover-tools.d.ts.map +1 -1
- package/lib/discover-tools.js +2 -6
- package/lib/discover-tools.js.map +1 -1
- package/lib/index.d.ts +2 -4
- package/lib/index.d.ts.map +1 -1
- package/lib/index.js +1 -2
- package/lib/index.js.map +1 -1
- package/lib/init/create-zhin-agent.d.ts.map +1 -1
- package/lib/init/create-zhin-agent.js +42 -20
- package/lib/init/create-zhin-agent.js.map +1 -1
- package/lib/init/register-ai-trigger.d.ts.map +1 -1
- package/lib/init/register-ai-trigger.js +10 -3
- package/lib/init/register-ai-trigger.js.map +1 -1
- package/lib/init/register-builtin-tools.d.ts.map +1 -1
- package/lib/init/register-builtin-tools.js +85 -15
- package/lib/init/register-builtin-tools.js.map +1 -1
- package/lib/init/register-db-models.d.ts.map +1 -1
- package/lib/init/register-db-models.js +1 -3
- package/lib/init/register-db-models.js.map +1 -1
- package/lib/init/register-db-upgrade.d.ts.map +1 -1
- package/lib/init/register-db-upgrade.js +1 -8
- package/lib/init/register-db-upgrade.js.map +1 -1
- package/lib/init/register-management-tools.d.ts.map +1 -1
- package/lib/init/register-management-tools.js +33 -20
- package/lib/init/register-management-tools.js.map +1 -1
- package/lib/service.d.ts.map +1 -1
- package/lib/service.js +0 -8
- package/lib/service.js.map +1 -1
- package/lib/zhin-agent/builtin-tools.d.ts +0 -2
- package/lib/zhin-agent/builtin-tools.d.ts.map +1 -1
- package/lib/zhin-agent/builtin-tools.js +0 -55
- package/lib/zhin-agent/builtin-tools.js.map +1 -1
- package/lib/zhin-agent/config.d.ts +2 -1
- package/lib/zhin-agent/config.d.ts.map +1 -1
- package/lib/zhin-agent/config.js +1 -1
- package/lib/zhin-agent/config.js.map +1 -1
- package/lib/zhin-agent/index.d.ts +1 -6
- package/lib/zhin-agent/index.d.ts.map +1 -1
- package/lib/zhin-agent/index.js +26 -34
- package/lib/zhin-agent/index.js.map +1 -1
- package/lib/zhin-agent/prompt.d.ts.map +1 -1
- package/lib/zhin-agent/prompt.js +31 -76
- package/lib/zhin-agent/prompt.js.map +1 -1
- package/lib/zhin-agent/tool-collector.d.ts.map +1 -1
- package/lib/zhin-agent/tool-collector.js +7 -7
- package/lib/zhin-agent/tool-collector.js.map +1 -1
- package/package.json +7 -4
- package/CHANGELOG.md +0 -190
- package/lib/follow-up.d.ts +0 -131
- package/lib/follow-up.d.ts.map +0 -1
- package/lib/follow-up.js +0 -265
- package/lib/follow-up.js.map +0 -1
- package/src/agent.ts +0 -6
- package/src/bootstrap.ts +0 -309
- package/src/builtin-tools.ts +0 -958
- package/src/compaction.ts +0 -28
- package/src/context-manager.ts +0 -15
- package/src/conversation-memory.ts +0 -5
- package/src/cron-engine.ts +0 -338
- package/src/discover-agents.ts +0 -138
- package/src/discover-skills.ts +0 -325
- package/src/discover-tools.ts +0 -302
- package/src/discovery-utils.ts +0 -96
- package/src/file-policy.ts +0 -333
- package/src/follow-up.ts +0 -357
- package/src/hooks.ts +0 -223
- package/src/index.ts +0 -183
- package/src/init/create-zhin-agent.ts +0 -161
- package/src/init/register-ai-service.ts +0 -53
- package/src/init/register-ai-trigger.ts +0 -253
- package/src/init/register-builtin-tools.ts +0 -308
- package/src/init/register-db-models.ts +0 -31
- package/src/init/register-db-upgrade.ts +0 -77
- package/src/init/register-management-tools.ts +0 -71
- package/src/init/register-message-recorder.ts +0 -31
- package/src/init/register-tool-service.ts +0 -9
- package/src/init/shared-refs.ts +0 -20
- package/src/init/types.ts +0 -18
- package/src/init.ts +0 -50
- package/src/output.ts +0 -15
- package/src/rate-limiter.ts +0 -5
- package/src/service.ts +0 -228
- package/src/session.ts +0 -13
- package/src/storage.ts +0 -9
- package/src/subagent.ts +0 -209
- package/src/tone-detector.ts +0 -5
- package/src/tools.ts +0 -214
- package/src/user-profile.ts +0 -182
- package/src/zhin-agent/builtin-tools.ts +0 -247
- package/src/zhin-agent/config.ts +0 -124
- package/src/zhin-agent/exec-policy.ts +0 -285
- package/src/zhin-agent/index.ts +0 -633
- package/src/zhin-agent/prompt.ts +0 -305
- package/src/zhin-agent/tool-collector.ts +0 -249
- package/tests/ai/follow-up.test.ts +0 -175
- package/tests/ai/integration.test.ts +0 -582
- package/tests/ai/multimodal.test.ts +0 -106
- package/tests/ai/setup.ts +0 -186
- package/tests/ai/subagent.test.ts +0 -270
- package/tests/ai/tools-builtin.test.ts +0 -310
- package/tests/ai/user-profile.test.ts +0 -73
- package/tests/ai/zhin-agent.test.ts +0 -306
- package/tests/exec-policy.test.ts +0 -355
- package/tests/file-policy.test.ts +0 -405
- package/tsconfig.json +0 -22
package/lib/follow-up.js
DELETED
|
@@ -1,265 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* FollowUpStore — 定时跟进任务的持久化存储
|
|
3
|
-
*
|
|
4
|
-
* 解决纯 setTimeout 重启即丢失的问题:
|
|
5
|
-
* 1. 创建任务时同步写入数据库
|
|
6
|
-
* 2. 执行完成 / 过期后从数据库中删除
|
|
7
|
-
* 3. 机器人启动时从数据库加载所有未完成任务,重新挂定时器
|
|
8
|
-
* 4. 同一会话创建新任务时,自动取消旧的 pending 任务(防止重复提醒)
|
|
9
|
-
*
|
|
10
|
-
* ai_followups 表:
|
|
11
|
-
* ┌──────────────────────────────────────────────────────────────────┐
|
|
12
|
-
* │ id (PK) | session_id | platform | sender_id | scene_id │
|
|
13
|
-
* │ | message | fire_at | created_at | status │
|
|
14
|
-
* └──────────────────────────────────────────────────────────────────┘
|
|
15
|
-
*/
|
|
16
|
-
import { Logger } from '@zhin.js/core';
|
|
17
|
-
const logger = new Logger(null, 'FollowUp');
|
|
18
|
-
// ============================================================================
|
|
19
|
-
// 数据库模型
|
|
20
|
-
// ============================================================================
|
|
21
|
-
export const AI_FOLLOWUP_MODEL = {
|
|
22
|
-
session_id: { type: 'text', nullable: false },
|
|
23
|
-
platform: { type: 'text', nullable: false },
|
|
24
|
-
bot_id: { type: 'text', nullable: false },
|
|
25
|
-
sender_id: { type: 'text', nullable: false },
|
|
26
|
-
scene_id: { type: 'text', nullable: false },
|
|
27
|
-
scene_type: { type: 'text', nullable: false },
|
|
28
|
-
message: { type: 'text', nullable: false },
|
|
29
|
-
/** 触发时间戳 (ms) */
|
|
30
|
-
fire_at: { type: 'integer', nullable: false },
|
|
31
|
-
created_at: { type: 'integer', default: 0 },
|
|
32
|
-
/** pending | fired | cancelled */
|
|
33
|
-
status: { type: 'text', default: 'pending' },
|
|
34
|
-
};
|
|
35
|
-
// ============================================================================
|
|
36
|
-
// 内存实现
|
|
37
|
-
// ============================================================================
|
|
38
|
-
class MemoryFollowUpStore {
|
|
39
|
-
records = [];
|
|
40
|
-
nextId = 1;
|
|
41
|
-
async create(record) {
|
|
42
|
-
const full = { ...record, id: this.nextId++ };
|
|
43
|
-
this.records.push(full);
|
|
44
|
-
return full;
|
|
45
|
-
}
|
|
46
|
-
async markFired(id) {
|
|
47
|
-
const r = this.records.find(r => r.id === id);
|
|
48
|
-
if (r)
|
|
49
|
-
r.status = 'fired';
|
|
50
|
-
}
|
|
51
|
-
async cancel(id) {
|
|
52
|
-
const r = this.records.find(r => r.id === id);
|
|
53
|
-
if (r)
|
|
54
|
-
r.status = 'cancelled';
|
|
55
|
-
}
|
|
56
|
-
async getPending() {
|
|
57
|
-
return this.records.filter(r => r.status === 'pending');
|
|
58
|
-
}
|
|
59
|
-
async getPendingBySession(sessionId) {
|
|
60
|
-
return this.records.filter(r => r.status === 'pending' && r.session_id === sessionId);
|
|
61
|
-
}
|
|
62
|
-
dispose() {
|
|
63
|
-
this.records = [];
|
|
64
|
-
}
|
|
65
|
-
}
|
|
66
|
-
// ============================================================================
|
|
67
|
-
// 数据库实现
|
|
68
|
-
// ============================================================================
|
|
69
|
-
class DatabaseFollowUpStore {
|
|
70
|
-
model;
|
|
71
|
-
constructor(model) {
|
|
72
|
-
this.model = model;
|
|
73
|
-
}
|
|
74
|
-
async create(record) {
|
|
75
|
-
const created = await this.model.create(record);
|
|
76
|
-
return { ...record, id: created.id ?? created };
|
|
77
|
-
}
|
|
78
|
-
async markFired(id) {
|
|
79
|
-
await this.model.update({ status: 'fired' }).where({ id });
|
|
80
|
-
}
|
|
81
|
-
async cancel(id) {
|
|
82
|
-
await this.model.update({ status: 'cancelled' }).where({ id });
|
|
83
|
-
}
|
|
84
|
-
async getPending() {
|
|
85
|
-
return this.model.select().where({ status: 'pending' });
|
|
86
|
-
}
|
|
87
|
-
async getPendingBySession(sessionId) {
|
|
88
|
-
return this.model.select().where({ status: 'pending', session_id: sessionId });
|
|
89
|
-
}
|
|
90
|
-
dispose() { }
|
|
91
|
-
}
|
|
92
|
-
// ============================================================================
|
|
93
|
-
// FollowUpManager
|
|
94
|
-
// ============================================================================
|
|
95
|
-
export class FollowUpManager {
|
|
96
|
-
store;
|
|
97
|
-
/** 内存中活跃的定时器: recordId → timer */
|
|
98
|
-
timers = new Map();
|
|
99
|
-
sender = null;
|
|
100
|
-
constructor() {
|
|
101
|
-
this.store = new MemoryFollowUpStore();
|
|
102
|
-
}
|
|
103
|
-
/** 注入消息发送回调 */
|
|
104
|
-
setSender(sender) {
|
|
105
|
-
this.sender = sender;
|
|
106
|
-
}
|
|
107
|
-
/** 升级到数据库存储 */
|
|
108
|
-
upgradeToDatabase(model) {
|
|
109
|
-
const old = this.store;
|
|
110
|
-
this.store = new DatabaseFollowUpStore(model);
|
|
111
|
-
old.dispose();
|
|
112
|
-
logger.debug('FollowUpManager: 已升级到数据库存储');
|
|
113
|
-
}
|
|
114
|
-
/**
|
|
115
|
-
* 创建一个跟进任务
|
|
116
|
-
*
|
|
117
|
-
* 重要:同一会话的旧 pending 任务会被自动取消,防止重复提醒。
|
|
118
|
-
*
|
|
119
|
-
* @returns 人类可读的确认文本
|
|
120
|
-
*/
|
|
121
|
-
async schedule(params) {
|
|
122
|
-
const { sessionId, platform, botId, senderId, sceneId, sceneType, message, delayMinutes } = params;
|
|
123
|
-
// ── 详细参数日志(方便排查问题) ──
|
|
124
|
-
logger.debug(`[跟进] 收到请求: delay_minutes=${delayMinutes}, message="${message}", session=${sessionId}`);
|
|
125
|
-
// 限制最大延迟 7 天
|
|
126
|
-
const maxDelay = 7 * 24 * 60;
|
|
127
|
-
const actualDelay = Math.min(Math.max(delayMinutes, 1), maxDelay);
|
|
128
|
-
const delayMs = actualDelay * 60 * 1000;
|
|
129
|
-
const fireAt = Date.now() + delayMs;
|
|
130
|
-
// ── 自动取消同一会话的旧 pending 任务 ──
|
|
131
|
-
const existingPending = await this.store.getPendingBySession(sessionId);
|
|
132
|
-
if (existingPending.length > 0) {
|
|
133
|
-
for (const old of existingPending) {
|
|
134
|
-
if (old.id != null) {
|
|
135
|
-
// 取消数据库记录
|
|
136
|
-
await this.store.cancel(old.id);
|
|
137
|
-
// 清除内存中的定时器
|
|
138
|
-
const timer = this.timers.get(old.id);
|
|
139
|
-
if (timer) {
|
|
140
|
-
clearTimeout(timer);
|
|
141
|
-
this.timers.delete(old.id);
|
|
142
|
-
}
|
|
143
|
-
logger.debug(`[跟进] 自动取消旧任务: id=${old.id}, "${old.message}"`);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
const record = await this.store.create({
|
|
148
|
-
session_id: sessionId,
|
|
149
|
-
platform,
|
|
150
|
-
bot_id: botId,
|
|
151
|
-
sender_id: senderId,
|
|
152
|
-
scene_id: sceneId,
|
|
153
|
-
scene_type: sceneType,
|
|
154
|
-
message,
|
|
155
|
-
fire_at: fireAt,
|
|
156
|
-
created_at: Date.now(),
|
|
157
|
-
status: 'pending',
|
|
158
|
-
});
|
|
159
|
-
// 挂定时器
|
|
160
|
-
this.scheduleTimer(record);
|
|
161
|
-
const readableTime = actualDelay >= 1440
|
|
162
|
-
? `${(actualDelay / 1440).toFixed(1)} 天后`
|
|
163
|
-
: actualDelay >= 60
|
|
164
|
-
? `${(actualDelay / 60).toFixed(1)} 小时后`
|
|
165
|
-
: `${actualDelay} 分钟后`;
|
|
166
|
-
// 精确触发时间(方便日志对照)
|
|
167
|
-
const fireDate = new Date(fireAt);
|
|
168
|
-
const fireTimeStr = fireDate.toLocaleString('zh-CN', { hour12: false });
|
|
169
|
-
logger.debug(`[跟进] 已创建: id=${record.id}, delay=${actualDelay}分钟(${delayMs}ms), 触发时间=${fireTimeStr}, "${message}"`);
|
|
170
|
-
return `✅ 已安排提醒,将在 ${readableTime}(${fireTimeStr})提醒你:${message}`;
|
|
171
|
-
}
|
|
172
|
-
/**
|
|
173
|
-
* 取消指定会话的所有 pending 任务
|
|
174
|
-
*/
|
|
175
|
-
async cancelBySession(sessionId) {
|
|
176
|
-
const pending = await this.store.getPendingBySession(sessionId);
|
|
177
|
-
let count = 0;
|
|
178
|
-
for (const record of pending) {
|
|
179
|
-
if (record.id != null) {
|
|
180
|
-
await this.store.cancel(record.id);
|
|
181
|
-
const timer = this.timers.get(record.id);
|
|
182
|
-
if (timer) {
|
|
183
|
-
clearTimeout(timer);
|
|
184
|
-
this.timers.delete(record.id);
|
|
185
|
-
}
|
|
186
|
-
count++;
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
if (count > 0) {
|
|
190
|
-
logger.debug(`[跟进] 已取消 ${count} 个待执行任务 (session=${sessionId})`);
|
|
191
|
-
}
|
|
192
|
-
return count;
|
|
193
|
-
}
|
|
194
|
-
/**
|
|
195
|
-
* 启动时从数据库恢复所有未完成的跟进任务
|
|
196
|
-
*/
|
|
197
|
-
async restore() {
|
|
198
|
-
const pending = await this.store.getPending();
|
|
199
|
-
const now = Date.now();
|
|
200
|
-
let restored = 0;
|
|
201
|
-
for (const record of pending) {
|
|
202
|
-
if (record.fire_at <= now) {
|
|
203
|
-
// 已过期但未执行 → 立即触发(延迟 2 秒,等系统完全就绪)
|
|
204
|
-
const overdueSec = Math.round((now - record.fire_at) / 1000);
|
|
205
|
-
logger.debug(`[跟进恢复] id=${record.id} 已过期 ${overdueSec}s,立即触发`);
|
|
206
|
-
this.scheduleTimerWithDelay(record, 2000);
|
|
207
|
-
restored++;
|
|
208
|
-
}
|
|
209
|
-
else {
|
|
210
|
-
// 还没到时间 → 重新挂定时器
|
|
211
|
-
this.scheduleTimer(record);
|
|
212
|
-
const remainMs = record.fire_at - now;
|
|
213
|
-
const remainMin = (remainMs / 60_000).toFixed(1);
|
|
214
|
-
logger.debug(`[跟进恢复] id=${record.id} 剩余 ${remainMin} 分钟, "${record.message}"`);
|
|
215
|
-
restored++;
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
if (restored > 0) {
|
|
219
|
-
logger.info(`[跟进恢复] 共恢复 ${restored} 个待执行任务`);
|
|
220
|
-
}
|
|
221
|
-
return restored;
|
|
222
|
-
}
|
|
223
|
-
/**
|
|
224
|
-
* 为一条记录挂定时器
|
|
225
|
-
*/
|
|
226
|
-
scheduleTimer(record) {
|
|
227
|
-
const delay = Math.max(record.fire_at - Date.now(), 0);
|
|
228
|
-
this.scheduleTimerWithDelay(record, delay);
|
|
229
|
-
}
|
|
230
|
-
scheduleTimerWithDelay(record, delayMs) {
|
|
231
|
-
if (!record.id)
|
|
232
|
-
return;
|
|
233
|
-
// 清除旧定时器(如果有)
|
|
234
|
-
const existing = this.timers.get(record.id);
|
|
235
|
-
if (existing)
|
|
236
|
-
clearTimeout(existing);
|
|
237
|
-
const timer = setTimeout(async () => {
|
|
238
|
-
try {
|
|
239
|
-
this.timers.delete(record.id);
|
|
240
|
-
// 发送提醒
|
|
241
|
-
if (this.sender) {
|
|
242
|
-
await this.sender(record);
|
|
243
|
-
logger.info(`[跟进提醒] 已发送: id=${record.id}, "${record.message}"`);
|
|
244
|
-
}
|
|
245
|
-
else {
|
|
246
|
-
logger.warn(`[跟进提醒] 无法发送 (sender 未注入): id=${record.id}`);
|
|
247
|
-
}
|
|
248
|
-
// 标记完成
|
|
249
|
-
await this.store.markFired(record.id);
|
|
250
|
-
}
|
|
251
|
-
catch (e) {
|
|
252
|
-
logger.warn(`[跟进提醒] 发送失败: id=${record.id}`, e);
|
|
253
|
-
}
|
|
254
|
-
}, delayMs);
|
|
255
|
-
this.timers.set(record.id, timer);
|
|
256
|
-
}
|
|
257
|
-
dispose() {
|
|
258
|
-
for (const timer of this.timers.values()) {
|
|
259
|
-
clearTimeout(timer);
|
|
260
|
-
}
|
|
261
|
-
this.timers.clear();
|
|
262
|
-
this.store.dispose();
|
|
263
|
-
}
|
|
264
|
-
}
|
|
265
|
-
//# sourceMappingURL=follow-up.js.map
|
package/lib/follow-up.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"follow-up.js","sourceRoot":"","sources":["../src/follow-up.ts"],"names":[],"mappings":"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"}
|
package/src/agent.ts
DELETED
|
@@ -1,6 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Re-export from @zhin.js/ai for backward compatibility.
|
|
3
|
-
* The Agent engine has been extracted to the framework-agnostic @zhin.js/ai package.
|
|
4
|
-
*/
|
|
5
|
-
export { Agent, createAgent, formatToolTitle } from '@zhin.js/ai';
|
|
6
|
-
export type { AgentState, AgentEvents } from '@zhin.js/ai';
|
package/src/bootstrap.ts
DELETED
|
@@ -1,309 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Workspace Bootstrap Files
|
|
3
|
-
*
|
|
4
|
-
* Injectable prompt files inspired by OpenClaw's workspace bootstrap design:
|
|
5
|
-
*
|
|
6
|
-
* AGENTS.md — persistent memory / instructions (AI read-write)
|
|
7
|
-
* SOUL.md — persona definition (read-only)
|
|
8
|
-
* TOOLS.md — tool usage guidelines (read-only)
|
|
9
|
-
*
|
|
10
|
-
* Key design:
|
|
11
|
-
* 1. mtime-based file cache to avoid redundant disk reads
|
|
12
|
-
* 2. Missing files are silently skipped
|
|
13
|
-
* 3. Per-file and total size limits to prevent prompt injection
|
|
14
|
-
* 4. Unified ContextFile format for system prompt injection
|
|
15
|
-
*/
|
|
16
|
-
|
|
17
|
-
import * as fs from 'fs';
|
|
18
|
-
import * as path from 'path';
|
|
19
|
-
import { Logger } from '@zhin.js/core';
|
|
20
|
-
|
|
21
|
-
const logger = new Logger(null, 'Bootstrap');
|
|
22
|
-
|
|
23
|
-
// ============================================================================
|
|
24
|
-
// 常量
|
|
25
|
-
// ============================================================================
|
|
26
|
-
|
|
27
|
-
/** 支持的引导文件名(顺序:SOUL → AGENTS → TOOLS) */
|
|
28
|
-
export const BOOTSTRAP_FILENAMES = [
|
|
29
|
-
'SOUL.md',
|
|
30
|
-
'AGENTS.md',
|
|
31
|
-
'TOOLS.md',
|
|
32
|
-
] as const;
|
|
33
|
-
|
|
34
|
-
export type BootstrapFileName = typeof BOOTSTRAP_FILENAMES[number];
|
|
35
|
-
|
|
36
|
-
/** 单文件最大字符数(默认 16KB) */
|
|
37
|
-
const DEFAULT_MAX_CHARS = 16 * 1024;
|
|
38
|
-
|
|
39
|
-
/** 所有引导文件总最大字符数(默认 48KB) */
|
|
40
|
-
const DEFAULT_TOTAL_MAX_CHARS = 48 * 1024;
|
|
41
|
-
|
|
42
|
-
// ============================================================================
|
|
43
|
-
// 类型
|
|
44
|
-
// ============================================================================
|
|
45
|
-
|
|
46
|
-
/** 引导文件信息 */
|
|
47
|
-
export interface BootstrapFile {
|
|
48
|
-
name: BootstrapFileName;
|
|
49
|
-
path: string;
|
|
50
|
-
content?: string;
|
|
51
|
-
missing: boolean;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
/** 上下文文件(用于注入到 system prompt) */
|
|
55
|
-
export interface ContextFile {
|
|
56
|
-
path: string;
|
|
57
|
-
content: string;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
// ============================================================================
|
|
61
|
-
// 文件缓存(基于 mtime)
|
|
62
|
-
// ============================================================================
|
|
63
|
-
|
|
64
|
-
const fileCache = new Map<string, { content: string; mtimeMs: number }>();
|
|
65
|
-
|
|
66
|
-
/**
|
|
67
|
-
* 读文件,带 mtime 缓存
|
|
68
|
-
*/
|
|
69
|
-
async function readFileWithCache(filePath: string): Promise<string> {
|
|
70
|
-
try {
|
|
71
|
-
const stats = await fs.promises.stat(filePath);
|
|
72
|
-
const mtimeMs = stats.mtimeMs;
|
|
73
|
-
const cached = fileCache.get(filePath);
|
|
74
|
-
|
|
75
|
-
if (cached && cached.mtimeMs === mtimeMs) {
|
|
76
|
-
return cached.content;
|
|
77
|
-
}
|
|
78
|
-
|
|
79
|
-
const content = await fs.promises.readFile(filePath, 'utf-8');
|
|
80
|
-
fileCache.set(filePath, { content, mtimeMs });
|
|
81
|
-
return content;
|
|
82
|
-
} catch {
|
|
83
|
-
fileCache.delete(filePath);
|
|
84
|
-
throw new Error(`Failed to read file: ${filePath}`);
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
* 清除文件缓存(热重载时调用)
|
|
90
|
-
*/
|
|
91
|
-
export function clearBootstrapCache(): void {
|
|
92
|
-
fileCache.clear();
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
// ============================================================================
|
|
96
|
-
// 文件加载
|
|
97
|
-
// ============================================================================
|
|
98
|
-
|
|
99
|
-
/**
|
|
100
|
-
* 获取数据目录
|
|
101
|
-
*/
|
|
102
|
-
function getDataDir(workspaceDir?: string): string {
|
|
103
|
-
const cwd = workspaceDir || process.cwd();
|
|
104
|
-
return path.join(cwd, 'data');
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
/**
|
|
108
|
-
* 获取文件制长期记忆目录(data/memory),不存在则创建
|
|
109
|
-
*/
|
|
110
|
-
export function getMemoryDir(workspaceDir?: string): string {
|
|
111
|
-
const dir = path.join(getDataDir(workspaceDir), 'memory');
|
|
112
|
-
if (!fs.existsSync(dir)) {
|
|
113
|
-
fs.mkdirSync(dir, { recursive: true });
|
|
114
|
-
}
|
|
115
|
-
return dir;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
function todayDate(): string {
|
|
119
|
-
return new Date().toISOString().split('T')[0];
|
|
120
|
-
}
|
|
121
|
-
|
|
122
|
-
/**
|
|
123
|
-
* 读取文件制长期记忆 + 当日笔记,拼成注入 system prompt 的字符串(与 miniclawd 一致)
|
|
124
|
-
* 同步读取,供 buildRichSystemPrompt 等同步调用
|
|
125
|
-
*/
|
|
126
|
-
export function getFileMemoryContext(workspaceDir?: string): string {
|
|
127
|
-
const memoryDir = getMemoryDir(workspaceDir);
|
|
128
|
-
const parts: string[] = [];
|
|
129
|
-
|
|
130
|
-
const memoryFile = path.join(memoryDir, 'MEMORY.md');
|
|
131
|
-
if (fs.existsSync(memoryFile)) {
|
|
132
|
-
try {
|
|
133
|
-
const longTerm = fs.readFileSync(memoryFile, 'utf-8').trim();
|
|
134
|
-
if (longTerm) parts.push('## Long-term Memory\n' + longTerm);
|
|
135
|
-
} catch {
|
|
136
|
-
// ignore read errors
|
|
137
|
-
}
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
const todayFile = path.join(memoryDir, `${todayDate()}.md`);
|
|
141
|
-
if (fs.existsSync(todayFile)) {
|
|
142
|
-
try {
|
|
143
|
-
const today = fs.readFileSync(todayFile, 'utf-8').trim();
|
|
144
|
-
if (today) parts.push("## Today's Notes\n" + today);
|
|
145
|
-
} catch {
|
|
146
|
-
// ignore read errors
|
|
147
|
-
}
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
return parts.length > 0 ? parts.join('\n\n') : '';
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* 加载工作区引导文件
|
|
155
|
-
*
|
|
156
|
-
* 搜索顺序:项目根目录 → data/ 目录
|
|
157
|
-
*/
|
|
158
|
-
export async function loadBootstrapFiles(
|
|
159
|
-
workspaceDir?: string,
|
|
160
|
-
): Promise<BootstrapFile[]> {
|
|
161
|
-
const cwd = workspaceDir || process.cwd();
|
|
162
|
-
const dataDir = getDataDir(cwd);
|
|
163
|
-
|
|
164
|
-
const result: BootstrapFile[] = [];
|
|
165
|
-
|
|
166
|
-
for (const name of BOOTSTRAP_FILENAMES) {
|
|
167
|
-
// 优先项目根目录
|
|
168
|
-
const rootPath = path.join(cwd, name);
|
|
169
|
-
const dataPath = path.join(dataDir, name);
|
|
170
|
-
|
|
171
|
-
let found = false;
|
|
172
|
-
for (const filePath of [rootPath, dataPath]) {
|
|
173
|
-
try {
|
|
174
|
-
const content = await readFileWithCache(filePath);
|
|
175
|
-
result.push({ name, path: filePath, content, missing: false });
|
|
176
|
-
found = true;
|
|
177
|
-
break; // 找到就不再搜索
|
|
178
|
-
} catch {
|
|
179
|
-
// 继续尝试下一个路径
|
|
180
|
-
}
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
if (!found) {
|
|
184
|
-
result.push({ name, path: rootPath, missing: true });
|
|
185
|
-
}
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
return result;
|
|
189
|
-
}
|
|
190
|
-
|
|
191
|
-
/**
|
|
192
|
-
* 将引导文件转为上下文文件列表(用于注入到 system prompt)
|
|
193
|
-
*
|
|
194
|
-
* 自动裁剪超长内容,跳过缺失文件
|
|
195
|
-
*/
|
|
196
|
-
export function buildContextFiles(
|
|
197
|
-
bootstrapFiles: BootstrapFile[],
|
|
198
|
-
options?: {
|
|
199
|
-
maxChars?: number;
|
|
200
|
-
totalMaxChars?: number;
|
|
201
|
-
},
|
|
202
|
-
): ContextFile[] {
|
|
203
|
-
const maxChars = options?.maxChars ?? DEFAULT_MAX_CHARS;
|
|
204
|
-
const totalMaxChars = options?.totalMaxChars ?? DEFAULT_TOTAL_MAX_CHARS;
|
|
205
|
-
|
|
206
|
-
const contextFiles: ContextFile[] = [];
|
|
207
|
-
let totalChars = 0;
|
|
208
|
-
|
|
209
|
-
for (const file of bootstrapFiles) {
|
|
210
|
-
if (file.missing || !file.content) continue;
|
|
211
|
-
|
|
212
|
-
let content = file.content.trim();
|
|
213
|
-
if (!content) continue;
|
|
214
|
-
|
|
215
|
-
// 单文件裁剪
|
|
216
|
-
if (content.length > maxChars) {
|
|
217
|
-
content = content.slice(0, maxChars) + '\n...(truncated)';
|
|
218
|
-
logger.warn(`Bootstrap file ${file.name} exceeds ${maxChars} chars, truncated`);
|
|
219
|
-
}
|
|
220
|
-
|
|
221
|
-
// 总量限制
|
|
222
|
-
if (totalChars + content.length > totalMaxChars) {
|
|
223
|
-
logger.warn(`Bootstrap total exceeds ${totalMaxChars} chars, skipping ${file.name}`);
|
|
224
|
-
break;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
contextFiles.push({ path: file.name, content });
|
|
228
|
-
totalChars += content.length;
|
|
229
|
-
}
|
|
230
|
-
|
|
231
|
-
return contextFiles;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
/**
|
|
235
|
-
* 加载 SOUL.md 人格定义
|
|
236
|
-
*/
|
|
237
|
-
export async function loadSoulPersona(workspaceDir?: string): Promise<string | null> {
|
|
238
|
-
const files = await loadBootstrapFiles(workspaceDir);
|
|
239
|
-
const soulFile = files.find(f => f.name === 'SOUL.md' && !f.missing);
|
|
240
|
-
return soulFile?.content?.trim() || null;
|
|
241
|
-
}
|
|
242
|
-
|
|
243
|
-
/**
|
|
244
|
-
* 加载 TOOLS.md 工具使用指引
|
|
245
|
-
*/
|
|
246
|
-
export async function loadToolsGuide(workspaceDir?: string): Promise<string | null> {
|
|
247
|
-
const files = await loadBootstrapFiles(workspaceDir);
|
|
248
|
-
const toolsFile = files.find(f => f.name === 'TOOLS.md' && !f.missing);
|
|
249
|
-
return toolsFile?.content?.trim() || null;
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
/**
|
|
253
|
-
* 加载 AGENTS.md 持久化记忆
|
|
254
|
-
*/
|
|
255
|
-
export async function loadAgentsMemory(workspaceDir?: string): Promise<string | null> {
|
|
256
|
-
const files = await loadBootstrapFiles(workspaceDir);
|
|
257
|
-
const agentsFile = files.find(f => f.name === 'AGENTS.md' && !f.missing);
|
|
258
|
-
return agentsFile?.content?.trim() || null;
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
// ============================================================================
|
|
262
|
-
// System Prompt 构建帮助函数
|
|
263
|
-
// ============================================================================
|
|
264
|
-
|
|
265
|
-
/**
|
|
266
|
-
* 构建引导文件上下文段(注入到 system prompt 末尾)
|
|
267
|
-
*
|
|
268
|
-
* 格式与 OpenClaw 一致:
|
|
269
|
-
* ```
|
|
270
|
-
* # Project Context
|
|
271
|
-
*
|
|
272
|
-
* The following project context files have been loaded:
|
|
273
|
-
* If SOUL.md is present, embody its persona and tone.
|
|
274
|
-
*
|
|
275
|
-
* ## SOUL.md
|
|
276
|
-
*
|
|
277
|
-
* <content>
|
|
278
|
-
*
|
|
279
|
-
* ## TOOLS.md
|
|
280
|
-
*
|
|
281
|
-
* <content>
|
|
282
|
-
* ```
|
|
283
|
-
*/
|
|
284
|
-
export function buildBootstrapContextSection(contextFiles: ContextFile[]): string {
|
|
285
|
-
if (contextFiles.length === 0) return '';
|
|
286
|
-
|
|
287
|
-
const hasSoul = contextFiles.some(f =>
|
|
288
|
-
f.path.toLowerCase().endsWith('soul.md'),
|
|
289
|
-
);
|
|
290
|
-
|
|
291
|
-
const lines: string[] = [
|
|
292
|
-
'# Project Context',
|
|
293
|
-
'',
|
|
294
|
-
'The following project context files have been loaded:',
|
|
295
|
-
];
|
|
296
|
-
|
|
297
|
-
if (hasSoul) {
|
|
298
|
-
lines.push(
|
|
299
|
-
'If SOUL.md is present, embody its persona and tone. Avoid generic responses.',
|
|
300
|
-
);
|
|
301
|
-
}
|
|
302
|
-
lines.push('');
|
|
303
|
-
|
|
304
|
-
for (const file of contextFiles) {
|
|
305
|
-
lines.push(`## ${file.path}`, '', file.content, '');
|
|
306
|
-
}
|
|
307
|
-
|
|
308
|
-
return lines.join('\n');
|
|
309
|
-
}
|