hyacinth-ai 0.9.19 → 0.9.20
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/dist/agents/delegate-tool.d.ts +47 -1
- package/dist/agents/delegate-tool.js +122 -4
- package/dist/gateway/factory.js +4 -0
- package/dist/lifecycle/supervisor.js +3 -0
- package/dist/orchestrator/loop.d.ts +8 -0
- package/dist/orchestrator/loop.js +19 -0
- package/dist/registry/tool.registry.js +4 -1
- package/dist/tools/runtime-control.d.ts +8 -0
- package/dist/tools/runtime-control.js +64 -0
- package/package.json +1 -1
|
@@ -25,6 +25,14 @@ export interface SubAgentContext {
|
|
|
25
25
|
sandboxRoot?: string;
|
|
26
26
|
/** 创建带独立 userId 的 Provider(子Agent 之间 + 与主Agent 的 KVCache 隔离) */
|
|
27
27
|
createSubProvider(userId: string): Provider;
|
|
28
|
+
/** 异步子Agent 结果推送队列(主 loop 的 pendingAsyncResults,完成后自动注入对话) */
|
|
29
|
+
pendingAsyncResults?: Array<{
|
|
30
|
+
handle: string;
|
|
31
|
+
agentName: string;
|
|
32
|
+
status: 'completed' | 'failed';
|
|
33
|
+
result?: string;
|
|
34
|
+
error?: string;
|
|
35
|
+
}>;
|
|
28
36
|
}
|
|
29
37
|
/** 注册正在运行的子 Agent loop */
|
|
30
38
|
export declare function registerRunningLoop(instanceId: string, loop: AgentLoop): void;
|
|
@@ -32,6 +40,29 @@ export declare function registerRunningLoop(instanceId: string, loop: AgentLoop)
|
|
|
32
40
|
export declare function unregisterRunningLoop(instanceId: string): void;
|
|
33
41
|
/** 中断指定子 Agent */
|
|
34
42
|
export declare function interruptSubAgentLoop(instanceId: string): boolean;
|
|
43
|
+
export interface AsyncSubAgentTask {
|
|
44
|
+
handle: string;
|
|
45
|
+
agentName: string;
|
|
46
|
+
instanceId: string;
|
|
47
|
+
task: string;
|
|
48
|
+
status: 'running' | 'completed' | 'failed';
|
|
49
|
+
startTime: string;
|
|
50
|
+
endTime?: string;
|
|
51
|
+
result?: string;
|
|
52
|
+
error?: string;
|
|
53
|
+
}
|
|
54
|
+
/** 注册异步子 Agent 任务,返回 handle */
|
|
55
|
+
export declare function registerAsyncTask(agentName: string, instanceId: string, task: string): string;
|
|
56
|
+
/** 标记异步任务为已完成 */
|
|
57
|
+
export declare function completeAsyncTask(handle: string, result: string): void;
|
|
58
|
+
/** 标记异步任务为失败 */
|
|
59
|
+
export declare function failAsyncTask(handle: string, error: string): void;
|
|
60
|
+
/** 获取所有异步任务列表 */
|
|
61
|
+
export declare function listAsyncTasks(): AsyncSubAgentTask[];
|
|
62
|
+
/** 按 handle 获取异步任务 */
|
|
63
|
+
export declare function getAsyncTask(handle: string): AsyncSubAgentTask | undefined;
|
|
64
|
+
/** 等待所有运行中的异步子 Agent 任务完成(用于优雅关闭) */
|
|
65
|
+
export declare function waitForAsyncTasks(timeoutMs?: number): Promise<void>;
|
|
35
66
|
/** 销毁子 Agent session 目录(清理 conversation/events/stats/meta) */
|
|
36
67
|
export declare function destroySubAgentSession(parentSessionDir: string, instanceId: string): Promise<boolean>;
|
|
37
68
|
/**
|
|
@@ -73,6 +104,14 @@ export declare class DelegateToAgentTool implements Tool {
|
|
|
73
104
|
type: "string";
|
|
74
105
|
description: string;
|
|
75
106
|
};
|
|
107
|
+
async: {
|
|
108
|
+
type: "boolean";
|
|
109
|
+
description: string;
|
|
110
|
+
};
|
|
111
|
+
across_turns: {
|
|
112
|
+
type: "boolean";
|
|
113
|
+
description: string;
|
|
114
|
+
};
|
|
76
115
|
};
|
|
77
116
|
required: string[];
|
|
78
117
|
};
|
|
@@ -81,7 +120,14 @@ export declare class DelegateToAgentTool implements Tool {
|
|
|
81
120
|
constructor(agentRegistry: AgentRegistry, parentContext: SubAgentContext);
|
|
82
121
|
execute(args: Record<string, unknown>): Promise<string>;
|
|
83
122
|
/**
|
|
84
|
-
*
|
|
123
|
+
* 异步委托:在后台启动子 Agent,立即返回任务句柄。
|
|
124
|
+
* 子 Agent 的 loop.run() 在后台 Promise 中执行,完成后结果写入 AsyncSubAgentTask。
|
|
125
|
+
*/
|
|
126
|
+
private executeAsync;
|
|
127
|
+
/** 后台执行子 Agent 任务 */
|
|
128
|
+
private runAsyncInBackground;
|
|
129
|
+
/**
|
|
130
|
+
* 委托模式:单个子 Agent 执行任务(同步阻塞)
|
|
85
131
|
*/
|
|
86
132
|
private executeDelegate;
|
|
87
133
|
/**
|
|
@@ -33,6 +33,57 @@ export function interruptSubAgentLoop(instanceId) {
|
|
|
33
33
|
loop.interrupt();
|
|
34
34
|
return true;
|
|
35
35
|
}
|
|
36
|
+
const asyncTasks = new Map();
|
|
37
|
+
let asyncTaskCounter = 0;
|
|
38
|
+
/** 注册异步子 Agent 任务,返回 handle */
|
|
39
|
+
export function registerAsyncTask(agentName, instanceId, task) {
|
|
40
|
+
const handle = `sub_${String(++asyncTaskCounter).padStart(3, '0')}`;
|
|
41
|
+
asyncTasks.set(handle, {
|
|
42
|
+
handle,
|
|
43
|
+
agentName,
|
|
44
|
+
instanceId,
|
|
45
|
+
task,
|
|
46
|
+
status: 'running',
|
|
47
|
+
startTime: new Date().toISOString(),
|
|
48
|
+
});
|
|
49
|
+
return handle;
|
|
50
|
+
}
|
|
51
|
+
/** 标记异步任务为已完成 */
|
|
52
|
+
export function completeAsyncTask(handle, result) {
|
|
53
|
+
const t = asyncTasks.get(handle);
|
|
54
|
+
if (t) {
|
|
55
|
+
t.status = 'completed';
|
|
56
|
+
t.endTime = new Date().toISOString();
|
|
57
|
+
t.result = result;
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
/** 标记异步任务为失败 */
|
|
61
|
+
export function failAsyncTask(handle, error) {
|
|
62
|
+
const t = asyncTasks.get(handle);
|
|
63
|
+
if (t) {
|
|
64
|
+
t.status = 'failed';
|
|
65
|
+
t.endTime = new Date().toISOString();
|
|
66
|
+
t.error = error;
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
/** 获取所有异步任务列表 */
|
|
70
|
+
export function listAsyncTasks() {
|
|
71
|
+
return [...asyncTasks.values()];
|
|
72
|
+
}
|
|
73
|
+
/** 按 handle 获取异步任务 */
|
|
74
|
+
export function getAsyncTask(handle) {
|
|
75
|
+
return asyncTasks.get(handle);
|
|
76
|
+
}
|
|
77
|
+
/** 等待所有运行中的异步子 Agent 任务完成(用于优雅关闭) */
|
|
78
|
+
export async function waitForAsyncTasks(timeoutMs = 5000) {
|
|
79
|
+
const start = Date.now();
|
|
80
|
+
while (Date.now() - start < timeoutMs) {
|
|
81
|
+
const running = listAsyncTasks().filter(t => t.status === 'running');
|
|
82
|
+
if (running.length === 0)
|
|
83
|
+
return;
|
|
84
|
+
await new Promise(resolve => setTimeout(resolve, 200));
|
|
85
|
+
}
|
|
86
|
+
}
|
|
36
87
|
/** 销毁子 Agent session 目录(清理 conversation/events/stats/meta) */
|
|
37
88
|
export async function destroySubAgentSession(parentSessionDir, instanceId) {
|
|
38
89
|
const subDir = path.join(parentSessionDir, 'sub-agents', instanceId);
|
|
@@ -57,6 +108,11 @@ export async function createSubAgentLoop(agentDef, task, parentContext) {
|
|
|
57
108
|
const instanceId = agentDef.instanceId ?? `${agentDef.name}-default`;
|
|
58
109
|
const subSessionDir = path.join(parentContext.sessionDir, 'sub-agents', instanceId);
|
|
59
110
|
const ttlMinutes = agentDef.sessionTtlMinutes ?? 10;
|
|
111
|
+
// 并发保护:同一 instanceId 已有活跃 loop 时拒绝,防止会话文件并发写入冲突
|
|
112
|
+
if (runningLoops.has(instanceId)) {
|
|
113
|
+
throw new Error(`子 Agent "${agentDef.name}" (${instanceId}) 正在执行中,不能同时委派第二个任务。` +
|
|
114
|
+
'等待当前任务完成后重试,或使用 spawn_sub_agent 创建独立实例来并行执行。');
|
|
115
|
+
}
|
|
60
116
|
// TTL 检查:过期则清理重建
|
|
61
117
|
let isNew = false;
|
|
62
118
|
try {
|
|
@@ -163,6 +219,7 @@ export class DelegateToAgentTool {
|
|
|
163
219
|
'"parallel"(并行分工):同时启动多个子 Agent 并行执行,汇总结果。' +
|
|
164
220
|
'编排场景:当你有一个复杂计划时,自己负责规划和决策,将其中可并行的子任务分别委派给多个子 Agent 同步执行——spawn_sub_agent 可克隆多份实例,配合不同的 instance_id 并发调度,大幅缩短总耗时。' +
|
|
165
221
|
'会话复用:子 Agent 会话在 TTL 窗口内持久化(默认 10 分钟),相同 instance_id 再次委派时自动恢复完整对话记忆,无需重新交代背景。' +
|
|
222
|
+
'异步执行:设置 async=true 后子 Agent 在后台运行,主 Agent 立即获得任务句柄(如 sub_001)并可继续其他工作。之后用 list_sub_agent_tasks 查看所有异步任务状态,get_sub_agent_result <handle> 获取已完成任务的结果。默认 async=false(同步阻塞,等待结果返回)。' +
|
|
166
223
|
'使用 list_sub_agents 查看可用 Agent 及其实例 ID,create_sub_agent 创建新 Agent,spawn_sub_agent 克隆以支持并行,destroy_sub_agent 清理不再需要的实例。';
|
|
167
224
|
inputSchema = {
|
|
168
225
|
type: 'object',
|
|
@@ -183,6 +240,14 @@ export class DelegateToAgentTool {
|
|
|
183
240
|
type: 'string',
|
|
184
241
|
description: '可选的附加上下文(如相关代码片段、文件路径、背景信息)。',
|
|
185
242
|
},
|
|
243
|
+
async: {
|
|
244
|
+
type: 'boolean',
|
|
245
|
+
description: '是否异步执行。默认 false(阻塞等待结果)。设为 true 时立即返回任务句柄(如 sub_001),子 Agent 在后台执行,主 Agent 可继续其他工具调用。',
|
|
246
|
+
},
|
|
247
|
+
across_turns: {
|
|
248
|
+
type: 'boolean',
|
|
249
|
+
description: '异步结果是否允许跨 turn。默认 false——子Agent完成后结果在当前 turn 内自动注入对话,LLM 立即看到并继续处理。设为 true 时结果不自动注入,需手动用 get_sub_agent_result 获取(适用于需要跨多轮对话的后台任务)。仅在 async=true 时生效。',
|
|
250
|
+
},
|
|
186
251
|
},
|
|
187
252
|
required: ['task'],
|
|
188
253
|
};
|
|
@@ -197,6 +262,8 @@ export class DelegateToAgentTool {
|
|
|
197
262
|
const agentName = args.agent_name;
|
|
198
263
|
const task = args.task;
|
|
199
264
|
const context = args.context;
|
|
265
|
+
const runAsync = args.async === true;
|
|
266
|
+
const acrossTurns = args.across_turns === true;
|
|
200
267
|
if (!agentName && !instanceId) {
|
|
201
268
|
return 'Error: either agent_name or instance_id is required. Use list_sub_agents to see available agents and their instance IDs.';
|
|
202
269
|
}
|
|
@@ -219,19 +286,23 @@ export class DelegateToAgentTool {
|
|
|
219
286
|
if (!agentDef) {
|
|
220
287
|
return 'Error: no sub-agent found matching the provided criteria.';
|
|
221
288
|
}
|
|
289
|
+
// 异步模式下不支持 adversarial 和 parallel(这两种本身就是多Agent协同,异步应逐个个委派)
|
|
290
|
+
if (runAsync && agentDef.collaborationMode !== 'delegate') {
|
|
291
|
+
return `错误:异步模式仅支持 collaborationMode="delegate"。当前模式为 "${agentDef.collaborationMode}"。请用 spawn_sub_agent 克隆独立实例,然后对每个实例分别用 async=true 委派。`;
|
|
292
|
+
}
|
|
222
293
|
// 构造完整任务描述
|
|
223
294
|
const fullTask = context ? `${task}\n\nAdditional Context:\n${context}` : task;
|
|
224
295
|
try {
|
|
296
|
+
if (runAsync) {
|
|
297
|
+
return await this.executeAsync(agentDef, fullTask, acrossTurns);
|
|
298
|
+
}
|
|
225
299
|
if (agentDef.collaborationMode === 'adversarial') {
|
|
226
|
-
// 对抗审查模式:找一个不同角色的子 Agent 同时审查
|
|
227
300
|
return await this.executeAdversarial(agentDef, fullTask);
|
|
228
301
|
}
|
|
229
302
|
else if (agentDef.collaborationMode === 'parallel') {
|
|
230
|
-
// 并行分工模式:所有 parallel 模式的子 Agent 并行执行
|
|
231
303
|
return await this.executeParallel(agentDef, fullTask);
|
|
232
304
|
}
|
|
233
305
|
else {
|
|
234
|
-
// 委托模式:直接委托给子 Agent
|
|
235
306
|
return await this.executeDelegate(agentDef, fullTask);
|
|
236
307
|
}
|
|
237
308
|
}
|
|
@@ -241,7 +312,54 @@ export class DelegateToAgentTool {
|
|
|
241
312
|
}
|
|
242
313
|
}
|
|
243
314
|
/**
|
|
244
|
-
*
|
|
315
|
+
* 异步委托:在后台启动子 Agent,立即返回任务句柄。
|
|
316
|
+
* 子 Agent 的 loop.run() 在后台 Promise 中执行,完成后结果写入 AsyncSubAgentTask。
|
|
317
|
+
*/
|
|
318
|
+
async executeAsync(agentDef, task, acrossTurns) {
|
|
319
|
+
const instanceId = agentDef.instanceId;
|
|
320
|
+
const handle = registerAsyncTask(agentDef.name, instanceId, task);
|
|
321
|
+
// 在后台启动子 Agent,不阻塞。catch 兜底防止未处理的 Promise rejection
|
|
322
|
+
this.runAsyncInBackground(handle, agentDef, task, acrossTurns).catch((err) => {
|
|
323
|
+
const message = err instanceof Error ? err.message : String(err);
|
|
324
|
+
failAsyncTask(handle, message);
|
|
325
|
+
if (!acrossTurns) {
|
|
326
|
+
this.parentContext.pendingAsyncResults?.push({ handle, agentName: agentDef.name, status: 'failed', error: message });
|
|
327
|
+
}
|
|
328
|
+
});
|
|
329
|
+
const hint = acrossTurns
|
|
330
|
+
? `跨 turn 模式——任务完成后需手动用 get_sub_agent_result ${handle} 获取结果。`
|
|
331
|
+
: '回合内模式——子Agent完成后结果将自动注入当前对话。';
|
|
332
|
+
return `异步子 Agent 任务已启动。\n句柄: ${handle}\nAgent: ${agentDef.name} (${instanceId})\n${hint}\n任务: ${task.slice(0, 100)}${task.length > 100 ? '...' : ''}`;
|
|
333
|
+
}
|
|
334
|
+
/** 后台执行子 Agent 任务 */
|
|
335
|
+
async runAsyncInBackground(handle, agentDef, task, acrossTurns) {
|
|
336
|
+
const instanceId = agentDef.instanceId;
|
|
337
|
+
try {
|
|
338
|
+
const { loop, sessionDir, isNew } = await createSubAgentLoop(agentDef, task, this.parentContext);
|
|
339
|
+
registerRunningLoop(instanceId, loop);
|
|
340
|
+
if (!isNew) {
|
|
341
|
+
await fs.utimes(path.join(sessionDir, 'meta.json'), new Date(), new Date()).catch(() => { });
|
|
342
|
+
}
|
|
343
|
+
await loop.run(task);
|
|
344
|
+
unregisterRunningLoop(instanceId);
|
|
345
|
+
const result = await this.collectResult(agentDef, task);
|
|
346
|
+
completeAsyncTask(handle, result);
|
|
347
|
+
if (!acrossTurns) {
|
|
348
|
+
// 回合内模式:推送到主 loop 队列,当前 turn 自动注入结果
|
|
349
|
+
this.parentContext.pendingAsyncResults?.push({ handle, agentName: agentDef.name, status: 'completed', result });
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
catch (error) {
|
|
353
|
+
unregisterRunningLoop(instanceId);
|
|
354
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
355
|
+
failAsyncTask(handle, message);
|
|
356
|
+
if (!acrossTurns) {
|
|
357
|
+
this.parentContext.pendingAsyncResults?.push({ handle, agentName: agentDef.name, status: 'failed', error: message });
|
|
358
|
+
}
|
|
359
|
+
}
|
|
360
|
+
}
|
|
361
|
+
/**
|
|
362
|
+
* 委托模式:单个子 Agent 执行任务(同步阻塞)
|
|
245
363
|
*/
|
|
246
364
|
async executeDelegate(agentDef, task) {
|
|
247
365
|
const instanceId = agentDef.instanceId;
|
package/dist/gateway/factory.js
CHANGED
|
@@ -424,12 +424,15 @@ export async function createAgent(options, supervisor) {
|
|
|
424
424
|
for (const agent of agents) {
|
|
425
425
|
agentRegistry.register(agent);
|
|
426
426
|
}
|
|
427
|
+
// 异步子Agent 结果队列——先创建引用,后续赋给 loop
|
|
428
|
+
const pendingAsyncResults = [];
|
|
427
429
|
const delegateTool = new DelegateToAgentTool(agentRegistry, {
|
|
428
430
|
modelRouter,
|
|
429
431
|
toolRegistry,
|
|
430
432
|
sessionDir,
|
|
431
433
|
maxContextTokens: maxContext,
|
|
432
434
|
dependencyAnalyzer,
|
|
435
|
+
pendingAsyncResults,
|
|
433
436
|
createSubProvider: (userId) => {
|
|
434
437
|
if (!subProviderApiKey || !subProviderBaseType) {
|
|
435
438
|
throw new Error('Cannot create sub-agent provider: main provider not configured.');
|
|
@@ -517,6 +520,7 @@ export async function createAgent(options, supervisor) {
|
|
|
517
520
|
const loop = new AgentLoop(provider, contextComposer, compressor, orchestrator, toolExecutor, toolRegistry, conversationStore, eventStore, statsManager, sessionDir, summaryStore, effectiveMaxTurns, maxContext, outputHandler, skillRegistry, mcpSystem.getBridge(), dependencyAnalyzer, agentRegistry, effectivePersonaDir, flowRegistry, providerRouter, new Set(config.safety?.dangerousTools ?? ['write', 'bash']), new Set(), configCenter, modelRouter, turnRecorder);
|
|
518
521
|
// 注入知识库状态引用
|
|
519
522
|
loop.kbState = kbState;
|
|
523
|
+
loop.pendingAsyncResults = pendingAsyncResults; // 异步子Agent结果队列(和 delegateTool 共享引用)
|
|
520
524
|
loopRef = loop; // wire fallback notification
|
|
521
525
|
// 注入旁路 Provider 引用(供模式切换时 setBypassUserId 使用)
|
|
522
526
|
if (bypassProvider)
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ProcessManager } from './manager.js';
|
|
2
2
|
import { LocalModelManager } from './local-model.js';
|
|
3
|
+
import { waitForAsyncTasks } from '../agents/delegate-tool.js';
|
|
3
4
|
/**
|
|
4
5
|
* LifecycleSupervisor — 统一管理所有受管进程的生命周期。
|
|
5
6
|
*
|
|
@@ -184,6 +185,8 @@ export class LifecycleSupervisor {
|
|
|
184
185
|
if (this.backgroundRegistry) {
|
|
185
186
|
await this.backgroundRegistry.shutdownAll().catch(() => { });
|
|
186
187
|
}
|
|
188
|
+
// 等待异步子 Agent 任务完成(最多等 5 秒)
|
|
189
|
+
await waitForAsyncTasks(5000).catch(() => { });
|
|
187
190
|
const results = this.entities.map((e) => e.manager.stop().catch(() => {
|
|
188
191
|
// 单个关闭失败不影响其他
|
|
189
192
|
}));
|
|
@@ -151,6 +151,14 @@ export declare class AgentLoop {
|
|
|
151
151
|
name: string;
|
|
152
152
|
firedAt: string;
|
|
153
153
|
}>;
|
|
154
|
+
/** 异步子Agent 完成后的结果队列(delegate-tool 写入,主循环消费) */
|
|
155
|
+
pendingAsyncResults: Array<{
|
|
156
|
+
handle: string;
|
|
157
|
+
agentName: string;
|
|
158
|
+
status: 'completed' | 'failed';
|
|
159
|
+
result?: string;
|
|
160
|
+
error?: string;
|
|
161
|
+
}>;
|
|
154
162
|
/** 当前正在执行的定时任务名(供 TUI 显示上下文),run 前设置,run 后清除 */
|
|
155
163
|
pendingTaskName: string | null;
|
|
156
164
|
/** 知识库状态引用(factory 注入) */
|
|
@@ -203,6 +203,8 @@ export class AgentLoop {
|
|
|
203
203
|
schedulerInitialized = false;
|
|
204
204
|
/** 定时任务触发后待注入对话的通知 */
|
|
205
205
|
pendingTaskNotifications = [];
|
|
206
|
+
/** 异步子Agent 完成后的结果队列(delegate-tool 写入,主循环消费) */
|
|
207
|
+
pendingAsyncResults = [];
|
|
206
208
|
/** 当前正在执行的定时任务名(供 TUI 显示上下文),run 前设置,run 后清除 */
|
|
207
209
|
pendingTaskName = null;
|
|
208
210
|
/** 知识库状态引用(factory 注入) */
|
|
@@ -1159,6 +1161,23 @@ export class AgentLoop {
|
|
|
1159
1161
|
turnCount++;
|
|
1160
1162
|
if (result.toolCalled)
|
|
1161
1163
|
toolWasCalled = true;
|
|
1164
|
+
// ── 异步子Agent 结果回合内注入 ─────────────────────────
|
|
1165
|
+
// delegate-tool 中异步任务完成后会将结果推送到此队列。
|
|
1166
|
+
// 本轮迭代结束后检查:有已完成的结果→注入对话→强制继续迭代,
|
|
1167
|
+
// 让 LLM 在当前 turn 内拿到结果并做出反应,无需跨 turn 手动查。
|
|
1168
|
+
if (this.pendingAsyncResults.length > 0) {
|
|
1169
|
+
const results = this.pendingAsyncResults.splice(0);
|
|
1170
|
+
for (const r of results) {
|
|
1171
|
+
const content = r.status === 'completed'
|
|
1172
|
+
? `[异步子Agent ${r.handle} (${r.agentName}) 已完成]\n${r.result ?? ''}`
|
|
1173
|
+
: `[异步子Agent ${r.handle} (${r.agentName}) 执行失败]\n${r.error ?? ''}`;
|
|
1174
|
+
await this.conversationStore.append(this.sessionDir, {
|
|
1175
|
+
role: 'user',
|
|
1176
|
+
content: [{ type: 'text', text: content }],
|
|
1177
|
+
});
|
|
1178
|
+
}
|
|
1179
|
+
result.stop = false; // 强制继续迭代,让 LLM 看到注入的异步结果
|
|
1180
|
+
}
|
|
1162
1181
|
// 更新 stats
|
|
1163
1182
|
await this.statsManager.increment(this.sessionDir, 'turn_count', 1);
|
|
1164
1183
|
// ── 旁路Agent 迭代审查(每次 LLM 回复后) ──────────
|
|
@@ -3,7 +3,7 @@ import { getActiveRouterName } from '../context/profiles.js';
|
|
|
3
3
|
import path from 'node:path';
|
|
4
4
|
import fs from 'node:fs';
|
|
5
5
|
import { createGetConfigTool, createUpdateConfigTool, createConfigSchemaTool, createResetConfigTool, } from '../tools/config.js';
|
|
6
|
-
import { createSwitchProviderTool, createListProvidersTool, createProviderInfoTool, createSwitchToAutoRouteTool, createToggleToolTool, createListToolsTool, createToggleSkillTool, createListSkillsTool, createToggleSubAgentTool, createListSubAgentsTool, createSpawnSubAgentTool, createCreateSubAgentTool, createUpdateSubAgentTool, createInterruptTool, createSessionStatsTool, createCurrentSessionTool, createListSessionsTool, createNewSessionTool, createSwitchSessionTool, createDeleteSessionTool, createAllowToolTool, createDisallowToolTool, createListAllowlistTool, createAddTaskTool, createRemoveTaskTool, createListTasksTool, createToggleTaskTool, createMcpStatusTool, createListModelChannelsTool, createAddModelChannelTool, createRemoveModelChannelTool, createSetChannelRoleTool, createSetChannelModelTool, createResetChannelModelTool, createChannelInfoTool, } from '../tools/runtime-control.js';
|
|
6
|
+
import { createSwitchProviderTool, createListProvidersTool, createProviderInfoTool, createSwitchToAutoRouteTool, createToggleToolTool, createListToolsTool, createToggleSkillTool, createListSkillsTool, createToggleSubAgentTool, createListSubAgentsTool, createSpawnSubAgentTool, createCreateSubAgentTool, createUpdateSubAgentTool, createListSubAgentTasksTool, createGetSubAgentResultTool, createInterruptTool, createSessionStatsTool, createCurrentSessionTool, createListSessionsTool, createNewSessionTool, createSwitchSessionTool, createDeleteSessionTool, createAllowToolTool, createDisallowToolTool, createListAllowlistTool, createAddTaskTool, createRemoveTaskTool, createListTasksTool, createToggleTaskTool, createMcpStatusTool, createListModelChannelsTool, createAddModelChannelTool, createRemoveModelChannelTool, createSetChannelRoleTool, createSetChannelModelTool, createResetChannelModelTool, createChannelInfoTool, } from '../tools/runtime-control.js';
|
|
7
7
|
/**
|
|
8
8
|
* 工具注册表
|
|
9
9
|
* 负责注册、查询工具,以及生成 LLM 格式的工具定义
|
|
@@ -110,6 +110,9 @@ export class ToolRegistry extends GenericRegistry {
|
|
|
110
110
|
this.register(createSpawnSubAgentTool(agentRegistry));
|
|
111
111
|
this.register(createCreateSubAgentTool(agentRegistry, cwd));
|
|
112
112
|
this.register(createUpdateSubAgentTool(agentRegistry));
|
|
113
|
+
// ── 异步子 Agent 任务工具 (2) ─────────────────────────────────
|
|
114
|
+
this.register(createListSubAgentTasksTool());
|
|
115
|
+
this.register(createGetSubAgentResultTool());
|
|
113
116
|
// destroy_sub_agent needs factory.ts sessionDir; register in factory.ts after loop is created
|
|
114
117
|
// (handled by importing and registering createDestroySubAgentTool directly in factory.ts)
|
|
115
118
|
// ── Session control tools (7) ────────────────────────
|
|
@@ -51,6 +51,14 @@ export declare function createToggleSubAgentTool(agentRegistry: AgentRegistry, c
|
|
|
51
51
|
* list_sub_agents — list all registered sub-agents with their enabled/disabled status and instance IDs.
|
|
52
52
|
*/
|
|
53
53
|
export declare function createListSubAgentsTool(agentRegistry: any): Tool;
|
|
54
|
+
/**
|
|
55
|
+
* list_sub_agent_tasks — 列出所有异步子 Agent 任务及其状态。
|
|
56
|
+
*/
|
|
57
|
+
export declare function createListSubAgentTasksTool(): Tool;
|
|
58
|
+
/**
|
|
59
|
+
* get_sub_agent_result — 获取异步子 Agent 任务的执行结果。
|
|
60
|
+
*/
|
|
61
|
+
export declare function createGetSubAgentResultTool(): Tool;
|
|
54
62
|
/**
|
|
55
63
|
* spawn_sub_agent — clone an existing sub-agent to create a parallel instance (分身).
|
|
56
64
|
*/
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { clearPromptCache } from '../prompts/loader.js';
|
|
2
2
|
import { switchRouter } from '../context/profiles.js';
|
|
3
|
+
import { listAsyncTasks, getAsyncTask } from '../agents/delegate-tool.js';
|
|
3
4
|
import fs from 'node:fs';
|
|
4
5
|
import path from 'node:path';
|
|
5
6
|
import os from 'node:os';
|
|
@@ -359,6 +360,69 @@ export function createListSubAgentsTool(agentRegistry) {
|
|
|
359
360
|
},
|
|
360
361
|
};
|
|
361
362
|
}
|
|
363
|
+
// ── 异步子 Agent 任务管理工具 ──
|
|
364
|
+
/**
|
|
365
|
+
* list_sub_agent_tasks — 列出所有异步子 Agent 任务及其状态。
|
|
366
|
+
*/
|
|
367
|
+
export function createListSubAgentTasksTool() {
|
|
368
|
+
return {
|
|
369
|
+
name: 'list_sub_agent_tasks',
|
|
370
|
+
description: '列出所有通过 delegate_to_agent(async=true) 启动的异步子 Agent 任务。包含句柄、Agent 名称、任务描述、状态(running/completed/failed)、启动时间和结果摘要。',
|
|
371
|
+
inputSchema: { type: 'object', properties: {} },
|
|
372
|
+
async execute(_args) {
|
|
373
|
+
const tasks = listAsyncTasks();
|
|
374
|
+
if (tasks.length === 0) {
|
|
375
|
+
return '暂无异步子 Agent 任务。使用 delegate_to_agent 并设置 async=true 来启动异步任务。';
|
|
376
|
+
}
|
|
377
|
+
const lines = [`异步子 Agent 任务(共 ${tasks.length} 个):`];
|
|
378
|
+
for (const t of tasks) {
|
|
379
|
+
const statusLabel = t.status === 'running' ? '执行中' : t.status === 'completed' ? '已完成' : '失败';
|
|
380
|
+
const statusIcon = t.status === 'running' ? '🔄' : t.status === 'completed' ? '✅' : '❌';
|
|
381
|
+
lines.push(` ${statusIcon} ${t.handle} [${statusLabel}] ${t.agentName} (instance: ${t.instanceId}): ${t.task.slice(0, 60)}${t.task.length > 60 ? '...' : ''}`);
|
|
382
|
+
if (t.status !== 'running' && t.result) {
|
|
383
|
+
const summary = t.result.split('\n').find(l => l.trim())?.slice(0, 80) ?? '';
|
|
384
|
+
lines.push(` ↳ ${summary}${summary.length >= 80 ? '...' : ''}`);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
387
|
+
lines.push('');
|
|
388
|
+
lines.push('使用 get_sub_agent_result <handle> 获取已完成任务的完整结果。');
|
|
389
|
+
return lines.join('\n');
|
|
390
|
+
},
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* get_sub_agent_result — 获取异步子 Agent 任务的执行结果。
|
|
395
|
+
*/
|
|
396
|
+
export function createGetSubAgentResultTool() {
|
|
397
|
+
return {
|
|
398
|
+
name: 'get_sub_agent_result',
|
|
399
|
+
description: '获取指定异步子 Agent 任务的执行结果。running 时返回仍在执行中,completed 时返回完整结果,failed 时返回错误信息。句柄从 list_sub_agent_tasks 获取。',
|
|
400
|
+
inputSchema: {
|
|
401
|
+
type: 'object',
|
|
402
|
+
properties: {
|
|
403
|
+
handle: {
|
|
404
|
+
type: 'string',
|
|
405
|
+
description: '异步任务句柄(如 sub_001),从 list_sub_agent_tasks 获取。',
|
|
406
|
+
},
|
|
407
|
+
},
|
|
408
|
+
required: ['handle'],
|
|
409
|
+
},
|
|
410
|
+
async execute(args) {
|
|
411
|
+
const handle = args.handle;
|
|
412
|
+
const task = getAsyncTask(handle);
|
|
413
|
+
if (!task) {
|
|
414
|
+
return `错误:未找到异步任务 "${handle}"。使用 list_sub_agent_tasks 查看所有任务及其句柄。`;
|
|
415
|
+
}
|
|
416
|
+
if (task.status === 'running') {
|
|
417
|
+
return `任务 ${handle}(${task.agentName})仍在执行中。任务:${task.task.slice(0, 100)}${task.task.length > 100 ? '...' : ''}\n启动时间:${task.startTime}\n请稍后再次调用 get_sub_agent_result 查询。`;
|
|
418
|
+
}
|
|
419
|
+
if (task.status === 'failed') {
|
|
420
|
+
return `任务 ${handle}(${task.agentName})执行失败。\n错误:${task.error}\n任务:${task.task}`;
|
|
421
|
+
}
|
|
422
|
+
return `任务 ${handle}(${task.agentName})已完成。\n\n${task.result}`;
|
|
423
|
+
},
|
|
424
|
+
};
|
|
425
|
+
}
|
|
362
426
|
/**
|
|
363
427
|
* spawn_sub_agent — clone an existing sub-agent to create a parallel instance (分身).
|
|
364
428
|
*/
|