@zhin.js/agent 0.0.20 → 0.1.2
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/README.md +5 -2
- package/lib/cron-engine.d.ts +16 -1
- package/lib/cron-engine.d.ts.map +1 -1
- package/lib/cron-engine.js +47 -13
- 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 +58 -21
- 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 +46 -14
- 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 +4 -0
- package/lib/service.d.ts.map +1 -1
- package/lib/service.js +3 -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 +4 -1
- package/lib/zhin-agent/config.d.ts.map +1 -1
- package/lib/zhin-agent/config.js +2 -1
- package/lib/zhin-agent/config.js.map +1 -1
- package/lib/zhin-agent/index.d.ts +11 -6
- package/lib/zhin-agent/index.d.ts.map +1 -1
- package/lib/zhin-agent/index.js +147 -81
- 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 -170
- 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 -136
- 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 -224
- 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 -121
- package/src/zhin-agent/exec-policy.ts +0 -285
- package/src/zhin-agent/index.ts +0 -559
- 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/src/compaction.ts
DELETED
|
@@ -1,28 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Re-export from @zhin.js/ai for backward compatibility.
|
|
3
|
-
*/
|
|
4
|
-
export {
|
|
5
|
-
DEFAULT_CONTEXT_TOKENS,
|
|
6
|
-
CONTEXT_WINDOW_HARD_MIN_TOKENS,
|
|
7
|
-
CONTEXT_WINDOW_WARN_BELOW_TOKENS,
|
|
8
|
-
BASE_CHUNK_RATIO,
|
|
9
|
-
MIN_CHUNK_RATIO,
|
|
10
|
-
SAFETY_MARGIN,
|
|
11
|
-
estimateTokens,
|
|
12
|
-
estimateMessagesTokens,
|
|
13
|
-
splitMessagesByTokenShare,
|
|
14
|
-
chunkMessagesByMaxTokens,
|
|
15
|
-
computeAdaptiveChunkRatio,
|
|
16
|
-
resolveContextWindowTokens,
|
|
17
|
-
evaluateContextWindowGuard,
|
|
18
|
-
summarizeWithFallback,
|
|
19
|
-
summarizeInStages,
|
|
20
|
-
pruneHistoryForContext,
|
|
21
|
-
compactSession,
|
|
22
|
-
} from '@zhin.js/ai';
|
|
23
|
-
export type {
|
|
24
|
-
ContextWindowSource,
|
|
25
|
-
ContextWindowInfo,
|
|
26
|
-
ContextWindowGuardResult,
|
|
27
|
-
PruneResult,
|
|
28
|
-
} from '@zhin.js/ai';
|
package/src/context-manager.ts
DELETED
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Re-export from @zhin.js/ai for backward compatibility.
|
|
3
|
-
*/
|
|
4
|
-
export {
|
|
5
|
-
ContextManager,
|
|
6
|
-
createContextManager,
|
|
7
|
-
CHAT_MESSAGE_MODEL,
|
|
8
|
-
CONTEXT_SUMMARY_MODEL,
|
|
9
|
-
} from '@zhin.js/ai';
|
|
10
|
-
export type {
|
|
11
|
-
MessageRecord,
|
|
12
|
-
SummaryRecord,
|
|
13
|
-
ContextConfig,
|
|
14
|
-
SceneContext,
|
|
15
|
-
} from '@zhin.js/ai';
|
package/src/cron-engine.ts
DELETED
|
@@ -1,338 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* 持久化定时任务
|
|
3
|
-
*
|
|
4
|
-
* 将定时任务持久化到 data/cron-jobs.json,进程重启后自动加载;
|
|
5
|
-
* 触发时以 prompt 调用 ZhinAgent,实现「到点执行 AI 任务」。
|
|
6
|
-
*
|
|
7
|
-
* - 存储:id, cronExpression, prompt, label?, enabled, createdAt
|
|
8
|
-
* - 启动时:读取文件 → 为每条启用的任务创建 Cron → 注册到 CronFeature
|
|
9
|
-
* - CLI / AI 工具:可对持久化任务做 list / add / remove / pause / resume(AI 侧立即生效)
|
|
10
|
-
*/
|
|
11
|
-
|
|
12
|
-
import * as fs from 'fs';
|
|
13
|
-
import * as path from 'path';
|
|
14
|
-
import { Cron, ZhinTool } from '@zhin.js/core';
|
|
15
|
-
import { Logger } from '@zhin.js/core';
|
|
16
|
-
|
|
17
|
-
const logger = new Logger(null, 'cron-engine');
|
|
18
|
-
|
|
19
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
20
|
-
// 类型与存储路径
|
|
21
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
22
|
-
|
|
23
|
-
export const CRON_JOBS_FILENAME = 'cron-jobs.json';
|
|
24
|
-
|
|
25
|
-
export interface CronJobRecord {
|
|
26
|
-
id: string;
|
|
27
|
-
/** Cron 表达式,5 字段:分 时 日 月 周 */
|
|
28
|
-
cronExpression: string;
|
|
29
|
-
/** 触发时发给 AI 的 prompt */
|
|
30
|
-
prompt: string;
|
|
31
|
-
/** 可选标签,便于识别 */
|
|
32
|
-
label?: string;
|
|
33
|
-
/** 是否启用(暂停的任务不加载) */
|
|
34
|
-
enabled: boolean;
|
|
35
|
-
createdAt: number;
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
export function getCronJobsFilePath(dataDir: string): string {
|
|
39
|
-
return path.join(dataDir, CRON_JOBS_FILENAME);
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
export async function readCronJobsFile(dataDir: string): Promise<CronJobRecord[]> {
|
|
43
|
-
const filePath = getCronJobsFilePath(dataDir);
|
|
44
|
-
try {
|
|
45
|
-
const raw = await fs.promises.readFile(filePath, 'utf-8');
|
|
46
|
-
const data = JSON.parse(raw);
|
|
47
|
-
if (!Array.isArray(data)) return [];
|
|
48
|
-
return data;
|
|
49
|
-
} catch (e: any) {
|
|
50
|
-
if (e?.code === 'ENOENT') return [];
|
|
51
|
-
logger.warn('读取定时任务文件失败: ' + (e?.message || String(e)));
|
|
52
|
-
return [];
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
export async function writeCronJobsFile(dataDir: string, jobs: CronJobRecord[]): Promise<void> {
|
|
57
|
-
const filePath = getCronJobsFilePath(dataDir);
|
|
58
|
-
await fs.promises.mkdir(path.dirname(filePath), { recursive: true });
|
|
59
|
-
await fs.promises.writeFile(filePath, JSON.stringify(jobs, null, 2), 'utf-8');
|
|
60
|
-
}
|
|
61
|
-
|
|
62
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
63
|
-
// 持久化引擎:加载文件并注册到 CronFeature
|
|
64
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
65
|
-
|
|
66
|
-
export type CronRunner = (prompt: string, jobId: string) => void | Promise<void>;
|
|
67
|
-
|
|
68
|
-
export type AddCronFn = (cron: Cron) => () => void;
|
|
69
|
-
|
|
70
|
-
export interface PersistentCronEngineOptions {
|
|
71
|
-
dataDir: string;
|
|
72
|
-
addCron: AddCronFn;
|
|
73
|
-
runner: CronRunner;
|
|
74
|
-
}
|
|
75
|
-
|
|
76
|
-
export class PersistentCronEngine {
|
|
77
|
-
private options: PersistentCronEngineOptions;
|
|
78
|
-
/** jobId -> dispose */
|
|
79
|
-
private disposes = new Map<string, () => void>();
|
|
80
|
-
|
|
81
|
-
constructor(options: PersistentCronEngineOptions) {
|
|
82
|
-
this.options = options;
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
getDataDir(): string {
|
|
86
|
-
return this.options.dataDir;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
/**
|
|
90
|
-
* 从文件加载任务并注册到 CronFeature;仅加载 enabled 的任务。
|
|
91
|
-
*/
|
|
92
|
-
load(): void {
|
|
93
|
-
const { dataDir, addCron, runner } = this.options;
|
|
94
|
-
readCronJobsFile(dataDir).then((jobs) => {
|
|
95
|
-
for (const job of jobs) {
|
|
96
|
-
if (!job.enabled) continue;
|
|
97
|
-
this.registerOne(job, addCron, runner);
|
|
98
|
-
}
|
|
99
|
-
if (jobs.filter((j) => j.enabled).length > 0) {
|
|
100
|
-
logger.info(`已加载 ${this.disposes.size} 个持久化定时任务`);
|
|
101
|
-
}
|
|
102
|
-
}).catch((e) => {
|
|
103
|
-
logger.warn('加载持久化定时任务失败: ' + (e?.message || String(e)));
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
private registerOne(
|
|
108
|
-
job: CronJobRecord,
|
|
109
|
-
addCron: AddCronFn,
|
|
110
|
-
runner: CronRunner,
|
|
111
|
-
): void {
|
|
112
|
-
const { prompt, id: jobId, cronExpression } = job;
|
|
113
|
-
try {
|
|
114
|
-
const cron = new Cron(cronExpression, async () => {
|
|
115
|
-
await runner(prompt, jobId);
|
|
116
|
-
});
|
|
117
|
-
cron.id = jobId;
|
|
118
|
-
const dispose = addCron(cron);
|
|
119
|
-
this.disposes.set(jobId, dispose);
|
|
120
|
-
} catch (e: any) {
|
|
121
|
-
logger.warn(`定时任务加载失败 [${jobId}]: ${e?.message || String(e)}`);
|
|
122
|
-
}
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
/**
|
|
126
|
-
* 列出所有持久化任务(从文件读取)
|
|
127
|
-
*/
|
|
128
|
-
async listJobs(): Promise<CronJobRecord[]> {
|
|
129
|
-
return readCronJobsFile(this.options.dataDir);
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* 添加持久化任务并立即生效
|
|
134
|
-
*/
|
|
135
|
-
async addJob(record: Omit<CronJobRecord, 'createdAt'> & { createdAt?: number }): Promise<CronJobRecord> {
|
|
136
|
-
const jobs = await readCronJobsFile(this.options.dataDir);
|
|
137
|
-
const full: CronJobRecord = {
|
|
138
|
-
...record,
|
|
139
|
-
createdAt: record.createdAt ?? Date.now(),
|
|
140
|
-
enabled: record.enabled ?? true,
|
|
141
|
-
};
|
|
142
|
-
jobs.push(full);
|
|
143
|
-
await writeCronJobsFile(this.options.dataDir, jobs);
|
|
144
|
-
if (full.enabled) {
|
|
145
|
-
this.registerOne(full, this.options.addCron, this.options.runner);
|
|
146
|
-
}
|
|
147
|
-
return full;
|
|
148
|
-
}
|
|
149
|
-
|
|
150
|
-
/**
|
|
151
|
-
* 删除持久化任务并立即生效
|
|
152
|
-
*/
|
|
153
|
-
async removeJob(id: string): Promise<boolean> {
|
|
154
|
-
const jobs = await readCronJobsFile(this.options.dataDir);
|
|
155
|
-
const next = jobs.filter((j) => j.id !== id);
|
|
156
|
-
if (next.length === jobs.length) return false;
|
|
157
|
-
await writeCronJobsFile(this.options.dataDir, next);
|
|
158
|
-
const dispose = this.disposes.get(id);
|
|
159
|
-
if (dispose) {
|
|
160
|
-
dispose();
|
|
161
|
-
this.disposes.delete(id);
|
|
162
|
-
}
|
|
163
|
-
return true;
|
|
164
|
-
}
|
|
165
|
-
|
|
166
|
-
/**
|
|
167
|
-
* 暂停任务(不删除,停止调度)
|
|
168
|
-
*/
|
|
169
|
-
async pauseJob(id: string): Promise<boolean> {
|
|
170
|
-
const jobs = await readCronJobsFile(this.options.dataDir);
|
|
171
|
-
const j = jobs.find((x) => x.id === id);
|
|
172
|
-
if (!j) return false;
|
|
173
|
-
j.enabled = false;
|
|
174
|
-
await writeCronJobsFile(this.options.dataDir, jobs);
|
|
175
|
-
const dispose = this.disposes.get(id);
|
|
176
|
-
if (dispose) {
|
|
177
|
-
dispose();
|
|
178
|
-
this.disposes.delete(id);
|
|
179
|
-
}
|
|
180
|
-
return true;
|
|
181
|
-
}
|
|
182
|
-
|
|
183
|
-
/**
|
|
184
|
-
* 恢复已暂停的任务
|
|
185
|
-
*/
|
|
186
|
-
async resumeJob(id: string): Promise<boolean> {
|
|
187
|
-
const jobs = await readCronJobsFile(this.options.dataDir);
|
|
188
|
-
const j = jobs.find((x) => x.id === id);
|
|
189
|
-
if (!j) return false;
|
|
190
|
-
j.enabled = true;
|
|
191
|
-
await writeCronJobsFile(this.options.dataDir, jobs);
|
|
192
|
-
this.registerOne(j, this.options.addCron, this.options.runner);
|
|
193
|
-
return true;
|
|
194
|
-
}
|
|
195
|
-
|
|
196
|
-
/**
|
|
197
|
-
* 卸载所有由本引擎注册的定时任务(用于 dispose)
|
|
198
|
-
*/
|
|
199
|
-
unload(): void {
|
|
200
|
-
for (const [id, dispose] of this.disposes) {
|
|
201
|
-
try {
|
|
202
|
-
dispose();
|
|
203
|
-
} catch (e) {
|
|
204
|
-
logger.warn(`Cron dispose failed for ${id}:`, e);
|
|
205
|
-
}
|
|
206
|
-
}
|
|
207
|
-
this.disposes.clear();
|
|
208
|
-
}
|
|
209
|
-
}
|
|
210
|
-
|
|
211
|
-
/**
|
|
212
|
-
* 生成唯一 ID(用于 CLI / AI 添加时)
|
|
213
|
-
*/
|
|
214
|
-
export function generateCronJobId(): string {
|
|
215
|
-
return `cron_${Date.now().toString(36)}_${Math.random().toString(36).slice(2, 8)}`;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
219
|
-
// 供 AI 工具使用的 Cron 管理器引用(init 中设置)
|
|
220
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
221
|
-
|
|
222
|
-
export interface CronManager {
|
|
223
|
-
cronFeature: { getStatus(): Array<{ expression: string; running: boolean; nextExecution: Date | null; plugin: string }> };
|
|
224
|
-
engine: PersistentCronEngine | null;
|
|
225
|
-
}
|
|
226
|
-
|
|
227
|
-
let cronManager: CronManager | null = null;
|
|
228
|
-
|
|
229
|
-
export function setCronManager(m: CronManager | null): void {
|
|
230
|
-
cronManager = m;
|
|
231
|
-
}
|
|
232
|
-
|
|
233
|
-
export function getCronManager(): CronManager | null {
|
|
234
|
-
return cronManager;
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
238
|
-
// AI 可调用的定时任务管理工具
|
|
239
|
-
// ─────────────────────────────────────────────────────────────────────────────
|
|
240
|
-
|
|
241
|
-
export function createCronTools(): ZhinTool[] {
|
|
242
|
-
const listTool = new ZhinTool('cron_list')
|
|
243
|
-
.desc('列出所有定时任务:包括插件注册的内存任务与持久化任务(持久化任务有 id,可用于 cron_remove/cron_pause/cron_resume)')
|
|
244
|
-
.keyword('定时任务', 'cron', '计划任务', '任务列表')
|
|
245
|
-
.tag('cron', 'schedule')
|
|
246
|
-
.execute(async () => {
|
|
247
|
-
const m = getCronManager();
|
|
248
|
-
if (!m) {
|
|
249
|
-
return { error: '定时任务服务不可用' };
|
|
250
|
-
}
|
|
251
|
-
const memory = m.cronFeature.getStatus().map((s) => ({
|
|
252
|
-
type: 'memory' as const,
|
|
253
|
-
expression: s.expression,
|
|
254
|
-
running: s.running,
|
|
255
|
-
nextExecution: s.nextExecution?.toISOString() ?? null,
|
|
256
|
-
plugin: s.plugin,
|
|
257
|
-
}));
|
|
258
|
-
const persistent = m.engine
|
|
259
|
-
? (await m.engine.listJobs()).map((j) => ({
|
|
260
|
-
type: 'persistent' as const,
|
|
261
|
-
id: j.id,
|
|
262
|
-
cronExpression: j.cronExpression,
|
|
263
|
-
prompt: j.prompt,
|
|
264
|
-
label: j.label,
|
|
265
|
-
enabled: j.enabled,
|
|
266
|
-
createdAt: j.createdAt,
|
|
267
|
-
}))
|
|
268
|
-
: [];
|
|
269
|
-
return { memory, persistent };
|
|
270
|
-
});
|
|
271
|
-
|
|
272
|
-
const addTool = new ZhinTool('cron_add')
|
|
273
|
-
.desc('添加一条持久化定时任务:到点由 AI 执行指定 prompt,重启后仍保留')
|
|
274
|
-
.keyword('添加定时', '新建定时任务', 'cron add')
|
|
275
|
-
.tag('cron', 'schedule')
|
|
276
|
-
.param('cron_expression', { type: 'string', description: 'Cron 表达式,如 "0 9 * * *" 表示每天 9:00' }, true)
|
|
277
|
-
.param('prompt', { type: 'string', description: '到点触发时发给 AI 的提示词' }, true)
|
|
278
|
-
.param('label', { type: 'string', description: '可选标签,便于识别' })
|
|
279
|
-
.execute(async (args) => {
|
|
280
|
-
const m = getCronManager();
|
|
281
|
-
if (!m?.engine) {
|
|
282
|
-
return { error: '持久化定时任务引擎不可用' };
|
|
283
|
-
}
|
|
284
|
-
const id = generateCronJobId();
|
|
285
|
-
const job = await m.engine.addJob({
|
|
286
|
-
id,
|
|
287
|
-
cronExpression: args.cron_expression as string,
|
|
288
|
-
prompt: args.prompt as string,
|
|
289
|
-
label: args.label as string | undefined,
|
|
290
|
-
enabled: true,
|
|
291
|
-
});
|
|
292
|
-
return { success: true, id: job.id, message: '已添加并立即生效' };
|
|
293
|
-
});
|
|
294
|
-
|
|
295
|
-
const removeTool = new ZhinTool('cron_remove')
|
|
296
|
-
.desc('按 id 删除一条持久化定时任务')
|
|
297
|
-
.keyword('删除定时', '取消定时', 'cron remove')
|
|
298
|
-
.tag('cron', 'schedule')
|
|
299
|
-
.param('id', { type: 'string', description: '任务 ID(cron_list 中 persistent 的 id)' }, true)
|
|
300
|
-
.execute(async (args) => {
|
|
301
|
-
const m = getCronManager();
|
|
302
|
-
if (!m?.engine) {
|
|
303
|
-
return { error: '持久化定时任务引擎不可用' };
|
|
304
|
-
}
|
|
305
|
-
const ok = await m.engine.removeJob(args.id as string);
|
|
306
|
-
return ok ? { success: true, message: '已删除' } : { error: '未找到该任务' };
|
|
307
|
-
});
|
|
308
|
-
|
|
309
|
-
const pauseTool = new ZhinTool('cron_pause')
|
|
310
|
-
.desc('暂停一条持久化定时任务(不删除,可 cron_resume 恢复)')
|
|
311
|
-
.keyword('暂停定时', 'cron pause')
|
|
312
|
-
.tag('cron', 'schedule')
|
|
313
|
-
.param('id', { type: 'string', description: '任务 ID' }, true)
|
|
314
|
-
.execute(async (args) => {
|
|
315
|
-
const m = getCronManager();
|
|
316
|
-
if (!m?.engine) {
|
|
317
|
-
return { error: '持久化定时任务引擎不可用' };
|
|
318
|
-
}
|
|
319
|
-
const ok = await m.engine.pauseJob(args.id as string);
|
|
320
|
-
return ok ? { success: true, message: '已暂停' } : { error: '未找到该任务' };
|
|
321
|
-
});
|
|
322
|
-
|
|
323
|
-
const resumeTool = new ZhinTool('cron_resume')
|
|
324
|
-
.desc('恢复已暂停的持久化定时任务')
|
|
325
|
-
.keyword('恢复定时', 'cron resume')
|
|
326
|
-
.tag('cron', 'schedule')
|
|
327
|
-
.param('id', { type: 'string', description: '任务 ID' }, true)
|
|
328
|
-
.execute(async (args) => {
|
|
329
|
-
const m = getCronManager();
|
|
330
|
-
if (!m?.engine) {
|
|
331
|
-
return { error: '持久化定时任务引擎不可用' };
|
|
332
|
-
}
|
|
333
|
-
const ok = await m.engine.resumeJob(args.id as string);
|
|
334
|
-
return ok ? { success: true, message: '已恢复' } : { error: '未找到该任务' };
|
|
335
|
-
});
|
|
336
|
-
|
|
337
|
-
return [listTool, addTool, removeTool, pauseTool, resumeTool];
|
|
338
|
-
}
|
package/src/discover-agents.ts
DELETED
|
@@ -1,138 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Agent 预设发现(*.agent.md 文件扫描)
|
|
3
|
-
*
|
|
4
|
-
* 加载顺序与 skills 一致:Workspace > ~/.zhin > data > 插件包
|
|
5
|
-
* 同名先发现者优先
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import * as fs from 'fs';
|
|
9
|
-
import * as os from 'os';
|
|
10
|
-
import * as path from 'path';
|
|
11
|
-
import { Logger, type Plugin } from '@zhin.js/core';
|
|
12
|
-
import { getDataDir } from './discovery-utils.js';
|
|
13
|
-
|
|
14
|
-
const logger = new Logger(null, 'builtin-tools');
|
|
15
|
-
|
|
16
|
-
// ============================================================================
|
|
17
|
-
// 类型
|
|
18
|
-
// ============================================================================
|
|
19
|
-
|
|
20
|
-
export interface AgentMeta {
|
|
21
|
-
name: string;
|
|
22
|
-
description: string;
|
|
23
|
-
keywords?: string[];
|
|
24
|
-
tags?: string[];
|
|
25
|
-
/** frontmatter 中声明的工具名列表 */
|
|
26
|
-
toolNames?: string[];
|
|
27
|
-
/** *.agent.md 文件的绝对路径 */
|
|
28
|
-
filePath: string;
|
|
29
|
-
/** 首选模型名 */
|
|
30
|
-
model?: string;
|
|
31
|
-
/** 首选 Provider 名 */
|
|
32
|
-
provider?: string;
|
|
33
|
-
/** 最大工具调用迭代次数 */
|
|
34
|
-
maxIterations?: number;
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
// ============================================================================
|
|
38
|
-
// 发现
|
|
39
|
-
// ============================================================================
|
|
40
|
-
|
|
41
|
-
/**
|
|
42
|
-
* 扫描 agents/ 目录,发现 *.agent.md 文件
|
|
43
|
-
*/
|
|
44
|
-
export async function discoverWorkspaceAgents(root?: Plugin | null): Promise<AgentMeta[]> {
|
|
45
|
-
const agents: AgentMeta[] = [];
|
|
46
|
-
const seenNames = new Set<string>();
|
|
47
|
-
|
|
48
|
-
const agentDirs: string[] = [
|
|
49
|
-
path.join(process.cwd(), 'agents'),
|
|
50
|
-
path.join(os.homedir(), '.zhin', 'agents'),
|
|
51
|
-
path.join(getDataDir(), 'agents'),
|
|
52
|
-
];
|
|
53
|
-
if (root) {
|
|
54
|
-
const addPluginDir = (p: Plugin) => {
|
|
55
|
-
if (!p?.filePath) return;
|
|
56
|
-
const dir = path.dirname(p.filePath);
|
|
57
|
-
const d = path.join(dir, 'agents');
|
|
58
|
-
if (!agentDirs.includes(d)) agentDirs.push(d);
|
|
59
|
-
const dirName = path.basename(dir);
|
|
60
|
-
if (dirName === 'src' || dirName === 'lib') {
|
|
61
|
-
const d2 = path.join(path.dirname(dir), 'agents');
|
|
62
|
-
if (!agentDirs.includes(d2)) agentDirs.push(d2);
|
|
63
|
-
}
|
|
64
|
-
};
|
|
65
|
-
addPluginDir(root);
|
|
66
|
-
for (const child of root.children || []) {
|
|
67
|
-
addPluginDir(child);
|
|
68
|
-
}
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
for (const agentsDir of agentDirs) {
|
|
72
|
-
if (!fs.existsSync(agentsDir)) continue;
|
|
73
|
-
|
|
74
|
-
let entries: fs.Dirent[];
|
|
75
|
-
try {
|
|
76
|
-
entries = await fs.promises.readdir(agentsDir, { withFileTypes: true });
|
|
77
|
-
} catch {
|
|
78
|
-
continue;
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
for (const entry of entries) {
|
|
82
|
-
let agentMdPath: string | undefined;
|
|
83
|
-
if (entry.isFile() && entry.name.endsWith('.agent.md')) {
|
|
84
|
-
agentMdPath = path.join(agentsDir, entry.name);
|
|
85
|
-
} else if (entry.isDirectory()) {
|
|
86
|
-
const nested = path.join(agentsDir, entry.name, `${entry.name}.agent.md`);
|
|
87
|
-
if (fs.existsSync(nested)) agentMdPath = nested;
|
|
88
|
-
}
|
|
89
|
-
if (!agentMdPath) continue;
|
|
90
|
-
|
|
91
|
-
try {
|
|
92
|
-
const content = await fs.promises.readFile(agentMdPath, 'utf-8');
|
|
93
|
-
const match = content.match(/^---\s*\n([\s\S]*?)\n---\s*(?:\n|$)/);
|
|
94
|
-
if (!match) {
|
|
95
|
-
logger.debug(`Agent文件 ${agentMdPath} 没有有效的frontmatter格式`);
|
|
96
|
-
continue;
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
let jsYaml: any;
|
|
100
|
-
try {
|
|
101
|
-
jsYaml = await import('js-yaml');
|
|
102
|
-
if (jsYaml.default) jsYaml = jsYaml.default;
|
|
103
|
-
} catch (e) {
|
|
104
|
-
logger.warn(`Unable to import js-yaml module: ${e}`);
|
|
105
|
-
continue;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
const metadata = jsYaml.load(match[1]);
|
|
109
|
-
if (!metadata || !metadata.name || !metadata.description) {
|
|
110
|
-
logger.debug(`Agent文件 ${agentMdPath} 缺少必需的 name/description 字段`);
|
|
111
|
-
continue;
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
if (seenNames.has(metadata.name)) {
|
|
115
|
-
logger.debug(`Agent '${metadata.name}' 已由先序目录加载,跳过: ${agentMdPath}`);
|
|
116
|
-
continue;
|
|
117
|
-
}
|
|
118
|
-
seenNames.add(metadata.name);
|
|
119
|
-
|
|
120
|
-
agents.push({
|
|
121
|
-
name: metadata.name,
|
|
122
|
-
description: metadata.description,
|
|
123
|
-
keywords: metadata.keywords || [],
|
|
124
|
-
tags: metadata.tags || [],
|
|
125
|
-
toolNames: Array.isArray(metadata.tools) ? metadata.tools : [],
|
|
126
|
-
filePath: agentMdPath,
|
|
127
|
-
model: metadata.model,
|
|
128
|
-
provider: metadata.provider,
|
|
129
|
-
maxIterations: typeof metadata.maxIterations === 'number' ? metadata.maxIterations : undefined,
|
|
130
|
-
});
|
|
131
|
-
logger.debug(`Agent发现成功: ${metadata.name}`);
|
|
132
|
-
} catch (e) {
|
|
133
|
-
logger.warn(`Failed to parse agent.md in ${agentMdPath}:`, e);
|
|
134
|
-
}
|
|
135
|
-
}
|
|
136
|
-
}
|
|
137
|
-
return agents;
|
|
138
|
-
}
|