@zhin.js/core 1.0.26 → 1.0.28
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/CHANGELOG.md +18 -0
- package/lib/ai/agent.d.ts.map +1 -1
- package/lib/ai/agent.js +4 -0
- package/lib/ai/agent.js.map +1 -1
- package/lib/ai/bootstrap.d.ts +82 -0
- package/lib/ai/bootstrap.d.ts.map +1 -0
- package/lib/ai/bootstrap.js +199 -0
- package/lib/ai/bootstrap.js.map +1 -0
- package/lib/ai/builtin-tools.d.ts +36 -0
- package/lib/ai/builtin-tools.d.ts.map +1 -0
- package/lib/ai/builtin-tools.js +509 -0
- package/lib/ai/builtin-tools.js.map +1 -0
- package/lib/ai/compaction.d.ts +132 -0
- package/lib/ai/compaction.d.ts.map +1 -0
- package/lib/ai/compaction.js +370 -0
- package/lib/ai/compaction.js.map +1 -0
- package/lib/ai/hooks.d.ts +143 -0
- package/lib/ai/hooks.d.ts.map +1 -0
- package/lib/ai/hooks.js +108 -0
- package/lib/ai/hooks.js.map +1 -0
- package/lib/ai/index.d.ts +6 -0
- package/lib/ai/index.d.ts.map +1 -1
- package/lib/ai/index.js +6 -0
- package/lib/ai/index.js.map +1 -1
- package/lib/ai/init.d.ts.map +1 -1
- package/lib/ai/init.js +120 -3
- package/lib/ai/init.js.map +1 -1
- package/lib/ai/types.d.ts +2 -0
- package/lib/ai/types.d.ts.map +1 -1
- package/lib/ai/zhin-agent.d.ts +28 -1
- package/lib/ai/zhin-agent.d.ts.map +1 -1
- package/lib/ai/zhin-agent.js +196 -57
- package/lib/ai/zhin-agent.js.map +1 -1
- package/lib/built/config.d.ts +10 -0
- package/lib/built/config.d.ts.map +1 -1
- package/lib/built/config.js +54 -3
- package/lib/built/config.js.map +1 -1
- package/lib/built/tool.d.ts +6 -0
- package/lib/built/tool.d.ts.map +1 -1
- package/lib/built/tool.js +12 -0
- package/lib/built/tool.js.map +1 -1
- package/lib/cron.d.ts +0 -27
- package/lib/cron.d.ts.map +1 -1
- package/lib/cron.js +28 -27
- package/lib/cron.js.map +1 -1
- package/lib/types-generator.js +1 -1
- package/lib/types-generator.js.map +1 -1
- package/lib/types.d.ts +7 -0
- package/lib/types.d.ts.map +1 -1
- package/package.json +6 -6
- package/src/ai/agent.ts +6 -0
- package/src/ai/bootstrap.ts +263 -0
- package/src/ai/builtin-tools.ts +569 -0
- package/src/ai/compaction.ts +529 -0
- package/src/ai/hooks.ts +223 -0
- package/src/ai/index.ts +58 -0
- package/src/ai/init.ts +127 -3
- package/src/ai/types.ts +2 -0
- package/src/ai/zhin-agent.ts +226 -54
- package/src/built/config.ts +53 -3
- package/src/built/tool.ts +12 -0
- package/src/cron.ts +28 -27
- package/src/types-generator.ts +1 -1
- package/src/types.ts +8 -0
- package/tests/adapter.test.ts +1 -1
- package/tests/config.test.ts +2 -2
- package/test/minimal-bot.ts +0 -31
- package/test/stress-test.ts +0 -123
package/src/ai/hooks.ts
ADDED
|
@@ -0,0 +1,223 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* AI Hooks — AI 生命周期事件钩子系统
|
|
3
|
+
*
|
|
4
|
+
* 借鉴 OpenClaw 的 Hooks 设计,提供可扩展的事件驱动钩子:
|
|
5
|
+
*
|
|
6
|
+
* - message:received — 收到消息时触发
|
|
7
|
+
* - message:sent — 发送消息时触发
|
|
8
|
+
* - session:compact — 会话压缩时触发
|
|
9
|
+
* - session:new — 新建会话时触发
|
|
10
|
+
* - agent:bootstrap — Agent 初始化时触发
|
|
11
|
+
* - agent:tool-call — 工具调用时触发
|
|
12
|
+
*
|
|
13
|
+
* 钩子按注册顺序执行,错误不会中断其他钩子。
|
|
14
|
+
*/
|
|
15
|
+
|
|
16
|
+
import { Logger } from '@zhin.js/logger';
|
|
17
|
+
|
|
18
|
+
const logger = new Logger(null, 'AI-Hooks');
|
|
19
|
+
|
|
20
|
+
// ============================================================================
|
|
21
|
+
// 类型定义
|
|
22
|
+
// ============================================================================
|
|
23
|
+
|
|
24
|
+
/** 事件类型 */
|
|
25
|
+
export type AIHookEventType =
|
|
26
|
+
| 'message'
|
|
27
|
+
| 'session'
|
|
28
|
+
| 'agent'
|
|
29
|
+
| 'tool';
|
|
30
|
+
|
|
31
|
+
/** Hook 事件基础接口 */
|
|
32
|
+
export interface AIHookEvent {
|
|
33
|
+
/** 事件类型 */
|
|
34
|
+
type: AIHookEventType;
|
|
35
|
+
/** 具体动作 */
|
|
36
|
+
action: string;
|
|
37
|
+
/** 会话标识 */
|
|
38
|
+
sessionId?: string;
|
|
39
|
+
/** 附加上下文 */
|
|
40
|
+
context: Record<string, unknown>;
|
|
41
|
+
/** 事件时间戳 */
|
|
42
|
+
timestamp: Date;
|
|
43
|
+
/** 钩子可以推送消息到这个数组 */
|
|
44
|
+
messages: string[];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/** 消息接收事件 */
|
|
48
|
+
export interface MessageReceivedEvent extends AIHookEvent {
|
|
49
|
+
type: 'message';
|
|
50
|
+
action: 'received';
|
|
51
|
+
context: {
|
|
52
|
+
from: string;
|
|
53
|
+
content: string;
|
|
54
|
+
platform: string;
|
|
55
|
+
channelId?: string;
|
|
56
|
+
messageId?: string;
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
/** 消息发送事件 */
|
|
61
|
+
export interface MessageSentEvent extends AIHookEvent {
|
|
62
|
+
type: 'message';
|
|
63
|
+
action: 'sent';
|
|
64
|
+
context: {
|
|
65
|
+
to: string;
|
|
66
|
+
content: string;
|
|
67
|
+
success: boolean;
|
|
68
|
+
error?: string;
|
|
69
|
+
platform: string;
|
|
70
|
+
};
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
/** 会话压缩事件 */
|
|
74
|
+
export interface SessionCompactEvent extends AIHookEvent {
|
|
75
|
+
type: 'session';
|
|
76
|
+
action: 'compact';
|
|
77
|
+
context: {
|
|
78
|
+
compactedCount: number;
|
|
79
|
+
savedTokens: number;
|
|
80
|
+
summary: string;
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
/** 会话新建事件 */
|
|
85
|
+
export interface SessionNewEvent extends AIHookEvent {
|
|
86
|
+
type: 'session';
|
|
87
|
+
action: 'new';
|
|
88
|
+
context: {
|
|
89
|
+
previousSessionId?: string;
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/** Agent 初始化事件 */
|
|
94
|
+
export interface AgentBootstrapEvent extends AIHookEvent {
|
|
95
|
+
type: 'agent';
|
|
96
|
+
action: 'bootstrap';
|
|
97
|
+
context: {
|
|
98
|
+
workspaceDir: string;
|
|
99
|
+
toolCount: number;
|
|
100
|
+
skillCount: number;
|
|
101
|
+
bootstrapFiles: string[];
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/** 工具调用事件 */
|
|
106
|
+
export interface ToolCallEvent extends AIHookEvent {
|
|
107
|
+
type: 'tool';
|
|
108
|
+
action: 'call';
|
|
109
|
+
context: {
|
|
110
|
+
toolName: string;
|
|
111
|
+
args: Record<string, unknown>;
|
|
112
|
+
result?: string;
|
|
113
|
+
durationMs?: number;
|
|
114
|
+
success: boolean;
|
|
115
|
+
error?: string;
|
|
116
|
+
};
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Hook 处理函数 */
|
|
120
|
+
export type AIHookHandler = (event: AIHookEvent) => Promise<void> | void;
|
|
121
|
+
|
|
122
|
+
// ============================================================================
|
|
123
|
+
// Hook 管理器
|
|
124
|
+
// ============================================================================
|
|
125
|
+
|
|
126
|
+
/** 按事件 key 存储的处理函数注册表 */
|
|
127
|
+
const handlers = new Map<string, AIHookHandler[]>();
|
|
128
|
+
|
|
129
|
+
/**
|
|
130
|
+
* 注册 Hook 处理函数
|
|
131
|
+
*
|
|
132
|
+
* @param eventKey - 事件类型(如 'message')或具体动作(如 'message:received')
|
|
133
|
+
* @param handler - 处理函数
|
|
134
|
+
*
|
|
135
|
+
* @example
|
|
136
|
+
* ```ts
|
|
137
|
+
* // 监听所有消息事件
|
|
138
|
+
* registerAIHook('message', async (event) => {
|
|
139
|
+
* console.log('消息事件:', event.action);
|
|
140
|
+
* });
|
|
141
|
+
*
|
|
142
|
+
* // 只监听消息接收
|
|
143
|
+
* registerAIHook('message:received', async (event) => {
|
|
144
|
+
* console.log('收到消息:', event.context.content);
|
|
145
|
+
* });
|
|
146
|
+
* ```
|
|
147
|
+
*/
|
|
148
|
+
export function registerAIHook(eventKey: string, handler: AIHookHandler): () => void {
|
|
149
|
+
if (!handlers.has(eventKey)) {
|
|
150
|
+
handlers.set(eventKey, []);
|
|
151
|
+
}
|
|
152
|
+
handlers.get(eventKey)!.push(handler);
|
|
153
|
+
|
|
154
|
+
// 返回注销函数
|
|
155
|
+
return () => unregisterAIHook(eventKey, handler);
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* 注销 Hook 处理函数
|
|
160
|
+
*/
|
|
161
|
+
export function unregisterAIHook(eventKey: string, handler: AIHookHandler): void {
|
|
162
|
+
const eventHandlers = handlers.get(eventKey);
|
|
163
|
+
if (!eventHandlers) return;
|
|
164
|
+
|
|
165
|
+
const index = eventHandlers.indexOf(handler);
|
|
166
|
+
if (index !== -1) eventHandlers.splice(index, 1);
|
|
167
|
+
if (eventHandlers.length === 0) handlers.delete(eventKey);
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/**
|
|
171
|
+
* 清除所有 Hook(测试用)
|
|
172
|
+
*/
|
|
173
|
+
export function clearAIHooks(): void {
|
|
174
|
+
handlers.clear();
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* 获取已注册的事件 key 列表(调试用)
|
|
179
|
+
*/
|
|
180
|
+
export function getRegisteredAIHookKeys(): string[] {
|
|
181
|
+
return Array.from(handlers.keys());
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
/**
|
|
185
|
+
* 触发 Hook 事件
|
|
186
|
+
*
|
|
187
|
+
* 同时调用通用类型(如 'message')和具体动作(如 'message:received')的处理函数。
|
|
188
|
+
* 处理函数按注册顺序执行,错误被捕获并记录,不影响其他处理函数。
|
|
189
|
+
*/
|
|
190
|
+
export async function triggerAIHook(event: AIHookEvent): Promise<void> {
|
|
191
|
+
const typeHandlers = handlers.get(event.type) ?? [];
|
|
192
|
+
const specificHandlers = handlers.get(`${event.type}:${event.action}`) ?? [];
|
|
193
|
+
const allHandlers = [...typeHandlers, ...specificHandlers];
|
|
194
|
+
|
|
195
|
+
if (allHandlers.length === 0) return;
|
|
196
|
+
|
|
197
|
+
for (const handler of allHandlers) {
|
|
198
|
+
try {
|
|
199
|
+
await handler(event);
|
|
200
|
+
} catch (err: any) {
|
|
201
|
+
logger.error(`Hook 错误 [${event.type}:${event.action}]: ${err.message}`);
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* 创建 Hook 事件(辅助函数)
|
|
208
|
+
*/
|
|
209
|
+
export function createAIHookEvent(
|
|
210
|
+
type: AIHookEventType,
|
|
211
|
+
action: string,
|
|
212
|
+
sessionId?: string,
|
|
213
|
+
context: Record<string, unknown> = {},
|
|
214
|
+
): AIHookEvent {
|
|
215
|
+
return {
|
|
216
|
+
type,
|
|
217
|
+
action,
|
|
218
|
+
sessionId,
|
|
219
|
+
context,
|
|
220
|
+
timestamp: new Date(),
|
|
221
|
+
messages: [],
|
|
222
|
+
};
|
|
223
|
+
}
|
package/src/ai/index.ts
CHANGED
|
@@ -126,3 +126,61 @@ export type {
|
|
|
126
126
|
|
|
127
127
|
// ── 初始化 ──
|
|
128
128
|
export { initAIModule } from './init.js';
|
|
129
|
+
|
|
130
|
+
// ── Hook 系统 (借鉴 OpenClaw) ──
|
|
131
|
+
export {
|
|
132
|
+
registerAIHook,
|
|
133
|
+
unregisterAIHook,
|
|
134
|
+
triggerAIHook,
|
|
135
|
+
createAIHookEvent,
|
|
136
|
+
clearAIHooks,
|
|
137
|
+
getRegisteredAIHookKeys,
|
|
138
|
+
} from './hooks.js';
|
|
139
|
+
export type {
|
|
140
|
+
AIHookEvent,
|
|
141
|
+
AIHookEventType,
|
|
142
|
+
AIHookHandler,
|
|
143
|
+
MessageReceivedEvent,
|
|
144
|
+
MessageSentEvent,
|
|
145
|
+
SessionCompactEvent,
|
|
146
|
+
SessionNewEvent,
|
|
147
|
+
AgentBootstrapEvent,
|
|
148
|
+
ToolCallEvent,
|
|
149
|
+
} from './hooks.js';
|
|
150
|
+
|
|
151
|
+
// ── 会话压缩 (借鉴 OpenClaw) ──
|
|
152
|
+
export {
|
|
153
|
+
estimateTokens,
|
|
154
|
+
estimateMessagesTokens,
|
|
155
|
+
splitMessagesByTokenShare,
|
|
156
|
+
chunkMessagesByMaxTokens,
|
|
157
|
+
computeAdaptiveChunkRatio,
|
|
158
|
+
resolveContextWindowTokens,
|
|
159
|
+
evaluateContextWindowGuard,
|
|
160
|
+
summarizeWithFallback,
|
|
161
|
+
summarizeInStages,
|
|
162
|
+
pruneHistoryForContext,
|
|
163
|
+
compactSession,
|
|
164
|
+
DEFAULT_CONTEXT_TOKENS,
|
|
165
|
+
} from './compaction.js';
|
|
166
|
+
export type {
|
|
167
|
+
ContextWindowSource,
|
|
168
|
+
ContextWindowInfo,
|
|
169
|
+
ContextWindowGuardResult,
|
|
170
|
+
PruneResult,
|
|
171
|
+
} from './compaction.js';
|
|
172
|
+
|
|
173
|
+
// ── 引导文件管理 (借鉴 OpenClaw) ──
|
|
174
|
+
export {
|
|
175
|
+
loadBootstrapFiles,
|
|
176
|
+
buildContextFiles,
|
|
177
|
+
buildBootstrapContextSection,
|
|
178
|
+
loadSoulPersona,
|
|
179
|
+
loadToolsGuide,
|
|
180
|
+
loadAgentsMemory,
|
|
181
|
+
clearBootstrapCache,
|
|
182
|
+
} from './bootstrap.js';
|
|
183
|
+
export type {
|
|
184
|
+
BootstrapFile,
|
|
185
|
+
ContextFile,
|
|
186
|
+
} from './bootstrap.js';
|
package/src/ai/init.ts
CHANGED
|
@@ -26,6 +26,9 @@ import type { MessageDispatcherService } from '../built/dispatcher.js';
|
|
|
26
26
|
import type { SkillFeature } from '../built/skill.js';
|
|
27
27
|
import { AIService } from './service.js';
|
|
28
28
|
import { ZhinAgent } from './zhin-agent.js';
|
|
29
|
+
import { createBuiltinTools, discoverWorkspaceSkills, loadSoulPersona } from './builtin-tools.js';
|
|
30
|
+
import { loadBootstrapFiles, buildContextFiles, buildBootstrapContextSection, loadToolsGuide } from './bootstrap.js';
|
|
31
|
+
import { triggerAIHook, createAIHookEvent } from './hooks.js';
|
|
29
32
|
import { SessionManager, createDatabaseSessionManager } from './session.js';
|
|
30
33
|
import { AI_SESSION_MODEL } from './session.js';
|
|
31
34
|
import {
|
|
@@ -131,7 +134,7 @@ export function initAIModule(): void {
|
|
|
131
134
|
async mounted(p: Plugin) {
|
|
132
135
|
const configService = root.inject('config');
|
|
133
136
|
const appConfig =
|
|
134
|
-
configService?.
|
|
137
|
+
configService?.getPrimary<{ ai?: AIConfig }>() || {};
|
|
135
138
|
const config = appConfig.ai || {};
|
|
136
139
|
|
|
137
140
|
if (config.enabled === false) {
|
|
@@ -146,7 +149,7 @@ export function initAIModule(): void {
|
|
|
146
149
|
const providers = service.listProviders();
|
|
147
150
|
if (providers.length === 0) {
|
|
148
151
|
logger.warn(
|
|
149
|
-
'No AI providers configured. Please add API keys in zhin.config
|
|
152
|
+
'No AI providers configured. Please add API keys in zhin.config (yml/json/toml)',
|
|
150
153
|
);
|
|
151
154
|
} else {
|
|
152
155
|
logger.info(
|
|
@@ -358,7 +361,7 @@ export function initAIModule(): void {
|
|
|
358
361
|
if (!aiServiceInstance) return;
|
|
359
362
|
const configService = root.inject('config');
|
|
360
363
|
const appConfig =
|
|
361
|
-
configService?.
|
|
364
|
+
configService?.getPrimary<{ ai?: AIConfig }>() || {};
|
|
362
365
|
const config = appConfig.ai || {};
|
|
363
366
|
|
|
364
367
|
if (config.sessions?.useDatabase === false) return;
|
|
@@ -499,4 +502,125 @@ export function initAIModule(): void {
|
|
|
499
502
|
logger.debug(`Registered ${tools.length} AI management tools`);
|
|
500
503
|
return () => disposers.forEach(d => d());
|
|
501
504
|
});
|
|
505
|
+
|
|
506
|
+
// ── 内置系统工具(文件/Shell/网络/计划/记忆/技能) ──
|
|
507
|
+
useContext('ai', 'tool', (ai, toolService) => {
|
|
508
|
+
if (!ai || !toolService) return;
|
|
509
|
+
|
|
510
|
+
// 注册工具
|
|
511
|
+
const builtinTools = createBuiltinTools();
|
|
512
|
+
const disposers: (() => void)[] = [];
|
|
513
|
+
for (const tool of builtinTools) disposers.push(toolService.addTool(tool, root.name));
|
|
514
|
+
logger.info(`Registered ${builtinTools.length} built-in system tools`);
|
|
515
|
+
|
|
516
|
+
// 异步发现工作区技能 + 加载引导文件(不阻塞注册流程)
|
|
517
|
+
(async () => {
|
|
518
|
+
// ── 第一步:发现和注册工作区技能 ──
|
|
519
|
+
try {
|
|
520
|
+
const skills = await discoverWorkspaceSkills();
|
|
521
|
+
const skillFeature = root.inject?.('skill') as SkillFeature | undefined;
|
|
522
|
+
if (skillFeature && skills.length > 0) {
|
|
523
|
+
logger.debug(`[技能注册] 开始注册 ${skills.length} 个技能...`);
|
|
524
|
+
// 构建所有已注册工具名的索引(用于模糊匹配)
|
|
525
|
+
const allRegisteredTools = toolService.getAll();
|
|
526
|
+
const toolNameIndex = new Map<string, Tool>();
|
|
527
|
+
for (const t of allRegisteredTools) {
|
|
528
|
+
toolNameIndex.set(t.name, t);
|
|
529
|
+
// 建立反向别名索引:read_file ↔ file_read 等
|
|
530
|
+
const parts = t.name.split('_');
|
|
531
|
+
if (parts.length === 2) {
|
|
532
|
+
toolNameIndex.set(`${parts[1]}_${parts[0]}`, t);
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
|
|
536
|
+
for (const s of skills) {
|
|
537
|
+
// 从 toolService 中查找技能声明的关联工具(支持模糊匹配)
|
|
538
|
+
const associatedTools: Tool[] = [];
|
|
539
|
+
const toolNames = s.toolNames || [];
|
|
540
|
+
if (toolNames.length > 0 && toolService) {
|
|
541
|
+
logger.debug(`[技能注册] 技能 '${s.name}' 声明的工具: ${toolNames.join(', ')}`);
|
|
542
|
+
for (const toolName of toolNames) {
|
|
543
|
+
// 精确匹配
|
|
544
|
+
let tool = toolService.get(toolName);
|
|
545
|
+
// 若精确匹配失败,尝试反向别名(file_read → read_file)
|
|
546
|
+
if (!tool) {
|
|
547
|
+
tool = toolNameIndex.get(toolName) || undefined;
|
|
548
|
+
}
|
|
549
|
+
if (tool) {
|
|
550
|
+
associatedTools.push(tool);
|
|
551
|
+
const matchType = toolService.get(toolName) ? '精确' : '模糊';
|
|
552
|
+
logger.debug(`[技能注册] ✅ 找到工具: ${toolName}${matchType === '模糊' ? ` → ${tool.name} (模糊匹配)` : ''}`);
|
|
553
|
+
} else {
|
|
554
|
+
logger.warn(`[技能注册] ❌ 工具 '${toolName}' 未找到(已注册: ${allRegisteredTools.map(t => t.name).join(', ')})`);
|
|
555
|
+
}
|
|
556
|
+
}
|
|
557
|
+
}
|
|
558
|
+
skillFeature.add({
|
|
559
|
+
name: s.name,
|
|
560
|
+
description: s.description,
|
|
561
|
+
tools: associatedTools,
|
|
562
|
+
keywords: s.keywords || [],
|
|
563
|
+
tags: s.tags || [],
|
|
564
|
+
pluginName: root.name,
|
|
565
|
+
}, root.name);
|
|
566
|
+
logger.debug(`[技能注册] 技能 '${s.name}' 已注册 (${associatedTools.length} 个工具)`);
|
|
567
|
+
}
|
|
568
|
+
logger.info(`✅ Registered ${skills.length} workspace skills with ${skills.reduce((sum, s) => sum + ((s.toolNames || []).length), 0)} total tool references`);
|
|
569
|
+
}
|
|
570
|
+
} catch (e: any) {
|
|
571
|
+
logger.warn(`Failed to discover workspace skills: ${e.message}`);
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
// ── 第二步:加载引导文件 ──
|
|
575
|
+
const loadedFiles: string[] = [];
|
|
576
|
+
try {
|
|
577
|
+
// 使用项目根目录或当前工作目录作为工作区目录
|
|
578
|
+
const workspaceDir = process.cwd();
|
|
579
|
+
const bootstrapFiles = await loadBootstrapFiles(workspaceDir);
|
|
580
|
+
const contextFiles = buildContextFiles(bootstrapFiles);
|
|
581
|
+
|
|
582
|
+
logger.debug(`Bootstrap files loaded (cwd: ${workspaceDir}): ${bootstrapFiles.map(f => f.name + (f.missing ? ' (missing)' : '')).join(', ')}`);
|
|
583
|
+
|
|
584
|
+
// SOUL.md → 注入到 agent persona
|
|
585
|
+
const soulFile = contextFiles.find(f => f.path === 'SOUL.md');
|
|
586
|
+
if (soulFile && zhinAgentInstance) {
|
|
587
|
+
logger.info('Loaded SOUL.md persona → agent prompt');
|
|
588
|
+
loadedFiles.push('SOUL.md');
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
// TOOLS.md → 记录已加载
|
|
592
|
+
const toolsFile = contextFiles.find(f => f.path === 'TOOLS.md');
|
|
593
|
+
if (toolsFile) {
|
|
594
|
+
logger.info('Loaded TOOLS.md tool guidance → agent prompt');
|
|
595
|
+
loadedFiles.push('TOOLS.md');
|
|
596
|
+
}
|
|
597
|
+
|
|
598
|
+
// AGENTS.md → 记录已加载
|
|
599
|
+
const agentsFile = contextFiles.find(f => f.path === 'AGENTS.md');
|
|
600
|
+
if (agentsFile) {
|
|
601
|
+
logger.info('Loaded AGENTS.md memory → agent prompt');
|
|
602
|
+
loadedFiles.push('AGENTS.md');
|
|
603
|
+
}
|
|
604
|
+
|
|
605
|
+
// 注入引导上下文到 ZhinAgent
|
|
606
|
+
if (zhinAgentInstance && contextFiles.length > 0) {
|
|
607
|
+
const contextSection = buildBootstrapContextSection(contextFiles);
|
|
608
|
+
zhinAgentInstance.setBootstrapContext(contextSection);
|
|
609
|
+
}
|
|
610
|
+
} catch (e: any) {
|
|
611
|
+
logger.debug(`Bootstrap files not loaded: ${e.message}`);
|
|
612
|
+
}
|
|
613
|
+
|
|
614
|
+
// 触发 agent:bootstrap Hook
|
|
615
|
+
const skillFeature2 = (root as any).inject?.('skill') as SkillFeature | undefined;
|
|
616
|
+
await triggerAIHook(createAIHookEvent('agent', 'bootstrap', undefined, {
|
|
617
|
+
workspaceDir: process.cwd(),
|
|
618
|
+
toolCount: builtinTools.length,
|
|
619
|
+
skillCount: skillFeature2?.size ?? 0,
|
|
620
|
+
bootstrapFiles: loadedFiles,
|
|
621
|
+
}));
|
|
622
|
+
})();
|
|
623
|
+
|
|
624
|
+
return () => disposers.forEach(d => d());
|
|
625
|
+
});
|
|
502
626
|
}
|
package/src/ai/types.ts
CHANGED