@cicctencent/agent-server 0.2.81 → 0.2.83
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/cjs/agent/agent-server.d.ts.map +1 -1
- package/dist/cjs/agent/agent-server.js +175 -57
- package/dist/cjs/agent/agent-server.js.map +1 -1
- package/dist/cjs/connectors/calendar-adapter.d.ts +1 -1
- package/dist/cjs/connectors/calendar-adapter.d.ts.map +1 -1
- package/dist/cjs/connectors/calendar-adapter.js +2 -10
- package/dist/cjs/connectors/calendar-adapter.js.map +1 -1
- package/dist/cjs/connectors/db-adapter.d.ts +1 -1
- package/dist/cjs/connectors/db-adapter.d.ts.map +1 -1
- package/dist/cjs/connectors/db-adapter.js +2 -19
- package/dist/cjs/connectors/db-adapter.js.map +1 -1
- package/dist/cjs/connectors/email-adapter.d.ts +1 -1
- package/dist/cjs/connectors/email-adapter.d.ts.map +1 -1
- package/dist/cjs/connectors/email-adapter.js +2 -10
- package/dist/cjs/connectors/email-adapter.js.map +1 -1
- package/dist/cjs/connectors/fs-adapter.d.ts +1 -1
- package/dist/cjs/connectors/fs-adapter.d.ts.map +1 -1
- package/dist/cjs/connectors/fs-adapter.js +4 -12
- package/dist/cjs/connectors/fs-adapter.js.map +1 -1
- package/dist/cjs/connectors/http-adapter.js +2 -4
- package/dist/cjs/connectors/http-adapter.js.map +1 -1
- package/dist/cjs/connectors/mq-adapter.d.ts +1 -1
- package/dist/cjs/connectors/mq-adapter.d.ts.map +1 -1
- package/dist/cjs/connectors/mq-adapter.js +2 -10
- package/dist/cjs/connectors/mq-adapter.js.map +1 -1
- package/dist/cjs/mcp/api-bridge-client.d.ts +1 -0
- package/dist/cjs/mcp/api-bridge-client.d.ts.map +1 -1
- package/dist/cjs/mcp/api-bridge-client.js +60 -2
- package/dist/cjs/mcp/api-bridge-client.js.map +1 -1
- package/dist/cjs/prompt/prompt-builder.d.ts +6 -0
- package/dist/cjs/prompt/prompt-builder.d.ts.map +1 -1
- package/dist/cjs/prompt/prompt-builder.js +10 -1
- package/dist/cjs/prompt/prompt-builder.js.map +1 -1
- package/dist/cjs/service/artifact.service.d.ts.map +1 -1
- package/dist/cjs/service/artifact.service.js +0 -26
- package/dist/cjs/service/artifact.service.js.map +1 -1
- package/dist/cjs/service/cache.service.d.ts +2 -0
- package/dist/cjs/service/cache.service.d.ts.map +1 -1
- package/dist/cjs/service/cache.service.js +8 -1
- package/dist/cjs/service/cache.service.js.map +1 -1
- package/dist/cjs/service/chat.service.d.ts.map +1 -1
- package/dist/cjs/service/chat.service.js +1 -3
- package/dist/cjs/service/chat.service.js.map +1 -1
- package/dist/cjs/service/safety-audit.middleware.d.ts.map +1 -1
- package/dist/cjs/service/safety-audit.middleware.js +12 -1
- package/dist/cjs/service/safety-audit.middleware.js.map +1 -1
- package/dist/cjs/service/settings.service.d.ts.map +1 -1
- package/dist/cjs/service/settings.service.js +2 -2
- package/dist/cjs/service/settings.service.js.map +1 -1
- package/dist/cjs/sse/sse-handler.d.ts.map +1 -1
- package/dist/cjs/sse/sse-handler.js +3 -1
- package/dist/cjs/sse/sse-handler.js.map +1 -1
- package/dist/cjs/tool/local-executors.d.ts +1 -0
- package/dist/cjs/tool/local-executors.d.ts.map +1 -1
- package/dist/cjs/tool/local-executors.js +8 -3
- package/dist/cjs/tool/local-executors.js.map +1 -1
- package/dist/cjs/tool/tools/git.d.ts.map +1 -1
- package/dist/cjs/tool/tools/git.js +34 -7
- package/dist/cjs/tool/tools/git.js.map +1 -1
- package/dist/cjs/tool/tools/sandbox.js.map +1 -1
- package/dist/cjs/types.d.ts +17 -1
- package/dist/cjs/types.d.ts.map +1 -1
- package/dist/esm/agent/agent-server.d.ts.map +1 -1
- package/dist/esm/agent/agent-server.js +175 -57
- package/dist/esm/agent/agent-server.js.map +1 -1
- package/dist/esm/connectors/calendar-adapter.d.ts +1 -1
- package/dist/esm/connectors/calendar-adapter.d.ts.map +1 -1
- package/dist/esm/connectors/calendar-adapter.js +2 -10
- package/dist/esm/connectors/calendar-adapter.js.map +1 -1
- package/dist/esm/connectors/db-adapter.d.ts +1 -1
- package/dist/esm/connectors/db-adapter.d.ts.map +1 -1
- package/dist/esm/connectors/db-adapter.js +2 -19
- package/dist/esm/connectors/db-adapter.js.map +1 -1
- package/dist/esm/connectors/email-adapter.d.ts +1 -1
- package/dist/esm/connectors/email-adapter.d.ts.map +1 -1
- package/dist/esm/connectors/email-adapter.js +2 -10
- package/dist/esm/connectors/email-adapter.js.map +1 -1
- package/dist/esm/connectors/fs-adapter.d.ts +1 -1
- package/dist/esm/connectors/fs-adapter.d.ts.map +1 -1
- package/dist/esm/connectors/fs-adapter.js +4 -12
- package/dist/esm/connectors/fs-adapter.js.map +1 -1
- package/dist/esm/connectors/http-adapter.js +2 -4
- package/dist/esm/connectors/http-adapter.js.map +1 -1
- package/dist/esm/connectors/mq-adapter.d.ts +1 -1
- package/dist/esm/connectors/mq-adapter.d.ts.map +1 -1
- package/dist/esm/connectors/mq-adapter.js +2 -10
- package/dist/esm/connectors/mq-adapter.js.map +1 -1
- package/dist/esm/mcp/api-bridge-client.d.ts +1 -0
- package/dist/esm/mcp/api-bridge-client.d.ts.map +1 -1
- package/dist/esm/mcp/api-bridge-client.js +27 -2
- package/dist/esm/mcp/api-bridge-client.js.map +1 -1
- package/dist/esm/prompt/prompt-builder.d.ts +6 -0
- package/dist/esm/prompt/prompt-builder.d.ts.map +1 -1
- package/dist/esm/prompt/prompt-builder.js +10 -1
- package/dist/esm/prompt/prompt-builder.js.map +1 -1
- package/dist/esm/service/artifact.service.d.ts.map +1 -1
- package/dist/esm/service/artifact.service.js +0 -26
- package/dist/esm/service/artifact.service.js.map +1 -1
- package/dist/esm/service/cache.service.d.ts +2 -0
- package/dist/esm/service/cache.service.d.ts.map +1 -1
- package/dist/esm/service/cache.service.js +8 -1
- package/dist/esm/service/cache.service.js.map +1 -1
- package/dist/esm/service/chat.service.d.ts.map +1 -1
- package/dist/esm/service/chat.service.js +1 -3
- package/dist/esm/service/chat.service.js.map +1 -1
- package/dist/esm/service/safety-audit.middleware.d.ts.map +1 -1
- package/dist/esm/service/safety-audit.middleware.js +12 -1
- package/dist/esm/service/safety-audit.middleware.js.map +1 -1
- package/dist/esm/service/settings.service.d.ts.map +1 -1
- package/dist/esm/service/settings.service.js +2 -2
- package/dist/esm/service/settings.service.js.map +1 -1
- package/dist/esm/sse/sse-handler.d.ts.map +1 -1
- package/dist/esm/sse/sse-handler.js +3 -1
- package/dist/esm/sse/sse-handler.js.map +1 -1
- package/dist/esm/tool/local-executors.d.ts +1 -0
- package/dist/esm/tool/local-executors.d.ts.map +1 -1
- package/dist/esm/tool/local-executors.js +8 -3
- package/dist/esm/tool/local-executors.js.map +1 -1
- package/dist/esm/tool/tools/git.d.ts.map +1 -1
- package/dist/esm/tool/tools/git.js +34 -7
- package/dist/esm/tool/tools/git.js.map +1 -1
- package/dist/esm/tool/tools/sandbox.js.map +1 -1
- package/dist/esm/types.d.ts +17 -1
- package/dist/esm/types.d.ts.map +1 -1
- package/package.json +2 -2
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent-server.d.ts","sourceRoot":"","sources":["../../../src/agent/agent-server.ts"],"names":[],"mappings":"AA4CA,OAAO,KAAK,EACV,WAAW,EACX,kBAAkB,EAMnB,MAAM,aAAa,CAAC;
|
|
1
|
+
{"version":3,"file":"agent-server.d.ts","sourceRoot":"","sources":["../../../src/agent/agent-server.ts"],"names":[],"mappings":"AA4CA,OAAO,KAAK,EACV,WAAW,EACX,kBAAkB,EAMnB,MAAM,aAAa,CAAC;AA6ErB;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,OAAO,EAAE,kBAAkB,GAAG,WAAW,CAy9C1E"}
|
|
@@ -23,6 +23,53 @@ const cache_service_ts_1 = require("../service/cache.service.js");
|
|
|
23
23
|
const data_dir_ts_1 = require("../utils/data-dir.js");
|
|
24
24
|
const env_collector_ts_1 = require("../service/env-collector.js");
|
|
25
25
|
const interaction_service_ts_1 = require("../service/interaction.service.js");
|
|
26
|
+
/**
|
|
27
|
+
* 子 Agent 事件全局去重包装器。
|
|
28
|
+
*
|
|
29
|
+
* 远程 A2A 服务(REST / JSON-RPC)有时会把同一事件重复下发——例如重复下发同一
|
|
30
|
+
* toolCallId 的 tool_call(导致前端出现「两张相同的工具卡片」),或重复下发
|
|
31
|
+
* start / done。这里按事件 key 在整条事件流生命周期内去重:
|
|
32
|
+
* - tool_call / tool_result:key = `${type}:${toolCallId}`(同一调用只转发一次)
|
|
33
|
+
* - start / done:key = `${type}:${parentToolCallId}`(每个父调用只一次)
|
|
34
|
+
* - content / thinking / progress:相邻去重(避免逐段重复下发,前端另有累计增量兜底)
|
|
35
|
+
*
|
|
36
|
+
* 既挡住顶层的重复 tool_call 卡片,也避免重复事件污染 fullContent / toolCallCount 等
|
|
37
|
+
* 聚合状态。
|
|
38
|
+
*
|
|
39
|
+
* @param emit 底层 SubAgentEventEmitter(通常是 ctx.emitSubEvent)
|
|
40
|
+
* @param getKeyContext 提供 parentToolCallId / agentName 的回调(用于 start/done 去重)
|
|
41
|
+
*/
|
|
42
|
+
function dedupeSubEventEmitter(emit, getKeyContext) {
|
|
43
|
+
const seenGlobal = new Set();
|
|
44
|
+
let lastAdjKey = null;
|
|
45
|
+
return (evt) => {
|
|
46
|
+
const { eventType, data } = evt;
|
|
47
|
+
let dedupKey = null;
|
|
48
|
+
if (eventType === 'tool_call' || eventType === 'tool_result') {
|
|
49
|
+
const id = data?.toolCallId || data?.callId;
|
|
50
|
+
if (id)
|
|
51
|
+
dedupKey = `${eventType}:${id}`;
|
|
52
|
+
}
|
|
53
|
+
else if (eventType === 'start' || eventType === 'done') {
|
|
54
|
+
const ctx = getKeyContext();
|
|
55
|
+
dedupKey = `${eventType}:${ctx.parentToolCallId}`;
|
|
56
|
+
}
|
|
57
|
+
else if (eventType === 'content' || eventType === 'thinking' || eventType === 'progress') {
|
|
58
|
+
// 相邻去重:同一文本连续下发则跳过(避免逐段叠加噪声)
|
|
59
|
+
const text = eventType === 'progress' ? JSON.stringify(data ?? {}) : (data?.content || '');
|
|
60
|
+
const adjKey = `${eventType}:${text}`;
|
|
61
|
+
if (adjKey === lastAdjKey)
|
|
62
|
+
return;
|
|
63
|
+
lastAdjKey = adjKey;
|
|
64
|
+
}
|
|
65
|
+
if (dedupKey) {
|
|
66
|
+
if (seenGlobal.has(dedupKey))
|
|
67
|
+
return;
|
|
68
|
+
seenGlobal.add(dedupKey);
|
|
69
|
+
}
|
|
70
|
+
emit?.(evt);
|
|
71
|
+
};
|
|
72
|
+
}
|
|
26
73
|
/**
|
|
27
74
|
* 远程 Agent A2A 调试槽(per-request)。
|
|
28
75
|
* 委派远程 Agent 的 runner 在引擎 build 期创建并缓存,运行时无法持有具体请求的
|
|
@@ -137,12 +184,21 @@ function createAgentServer(options) {
|
|
|
137
184
|
toolRegistry.register(askUserTool);
|
|
138
185
|
toolRegistry.registerAll([...builtinTools, ...extraTools]);
|
|
139
186
|
skillManager = new agent_core_1.SkillManager();
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
187
|
+
if (options.skills?.length) {
|
|
188
|
+
// DB 源:直接注入 Skills(以 name 为唯一标识,更扁平,不依赖文件路径)
|
|
189
|
+
for (const s of options.skills) {
|
|
190
|
+
skillManager.register(s);
|
|
191
|
+
}
|
|
192
|
+
console.log(`[AgentServer] 已从注入源加载 ${options.skills.length} 个 Skills`);
|
|
143
193
|
}
|
|
144
|
-
|
|
145
|
-
|
|
194
|
+
else {
|
|
195
|
+
const skillsDir = options.skillsDir || process.env.SKILLS_DIR || './data/skills';
|
|
196
|
+
try {
|
|
197
|
+
skillManager.loadFromDir(skillsDir, 'system');
|
|
198
|
+
}
|
|
199
|
+
catch (e) {
|
|
200
|
+
console.warn('[AgentServer] Failed to load skills from', skillsDir, e);
|
|
201
|
+
}
|
|
146
202
|
}
|
|
147
203
|
contextManager = new agent_core_1.ContextManager();
|
|
148
204
|
const securityGuard = new agent_core_1.SecurityGuard();
|
|
@@ -227,8 +283,11 @@ function createAgentServer(options) {
|
|
|
227
283
|
async function buildEngineForProfile(numericProfileId, profileOverride, skipCache, tenant, cacheKey, isSubAgent = false, allowedSpecialistIds, sessionId, canDelegateOverride) {
|
|
228
284
|
const { toolRegistry: baseRegistry, skillManager: baseSkillManager, contextManager: ctxManager } = getAgentSystem();
|
|
229
285
|
const profile = profileOverride || (numericProfileId ? await agentProfileProvider.getProfile(numericProfileId, tenant) : await agentProfileProvider.getDefaultProfile(tenant));
|
|
230
|
-
if (!profile)
|
|
231
|
-
|
|
286
|
+
if (!profile) {
|
|
287
|
+
throw new Error(numericProfileId
|
|
288
|
+
? `Agent profile not found: ${numericProfileId}`
|
|
289
|
+
: 'Default agent profile not found');
|
|
290
|
+
}
|
|
232
291
|
// canDelegate: 非 remote Agent 均可委派子任务(BI 项目不依赖 isDefault 字段)
|
|
233
292
|
const agentToolRegistry = new agent_core_1.ToolRegistry();
|
|
234
293
|
baseRegistry.getDefinitions().forEach(def => {
|
|
@@ -316,12 +375,17 @@ function createAgentServer(options) {
|
|
|
316
375
|
scope: sp.scope,
|
|
317
376
|
keywords: sp.keywords,
|
|
318
377
|
exclusiveScope: sp.exclusiveScope,
|
|
378
|
+
// 该子 Agent 自身的委派超时(来自 Fdelegate_timeout_ms),覆盖全局默认。
|
|
379
|
+
// 注:AgentProfileProvider.listProfiles 已在源头把未配置(0/null)归一化为
|
|
380
|
+
// undefined,此处 || undefined 为防御性兜底(防止其它未归一化的 provider 传入 0
|
|
381
|
+
// 被 `?? timeoutMs` 保留为 0 并当作 falsy → 完全不加超时)。
|
|
382
|
+
timeoutMs: sp.delegateTimeoutMs || undefined,
|
|
319
383
|
run: async (task, _ctx, _runOptions) => {
|
|
320
384
|
// 从 ToolContext 中提取父会话 ID(用于建立父子关系,
|
|
321
385
|
// 使 RateLimitManager 能在父会话 abort 时终止子会话)
|
|
322
386
|
const parentSessionId = _ctx?.sessionId;
|
|
323
387
|
const parentCallId = (_ctx?.currentToolCallId ?? _ctx?.callId);
|
|
324
|
-
const emit = _ctx?.emitSubEvent;
|
|
388
|
+
const emit = dedupeSubEventEmitter(_ctx?.emitSubEvent, () => ({ parentToolCallId: parentCallId ?? '', agentName: sp.name }));
|
|
325
389
|
// 部分结果注入回调:子 Agent 流式输出时实时注入主 Agent 历史
|
|
326
390
|
const injectPartialResult = _ctx?.injectPartialResult;
|
|
327
391
|
const subSessionId = `sub_${sp.id}_${Date.now()}`;
|
|
@@ -673,38 +737,46 @@ function createAgentServer(options) {
|
|
|
673
737
|
const waA2AClientPool = new Map();
|
|
674
738
|
function waClientForKey(kind, profile) {
|
|
675
739
|
const auth = profile.remoteAuth?.value ? { t: profile.remoteAuth.type, n: profile.remoteAuth.name } : null;
|
|
676
|
-
|
|
740
|
+
const t = a2aEffectiveTimeouts(profile);
|
|
741
|
+
return `${kind}|${JSON.stringify({ auth, h: buildExtraHeaders(profile.remoteAuth), t })}`;
|
|
742
|
+
}
|
|
743
|
+
/** 解析 Profile 上的 A2A 超时配置(带默认值),0/未设置回退到默认值 */
|
|
744
|
+
function a2aEffectiveTimeouts(profile) {
|
|
745
|
+
return {
|
|
746
|
+
timeout: profile.a2aTimeout || 300_000,
|
|
747
|
+
connectTimeout: profile.a2aConnectTimeout || 30_000,
|
|
748
|
+
chunkTimeout: profile.a2aChunkTimeout || 60_000,
|
|
749
|
+
};
|
|
750
|
+
}
|
|
751
|
+
/** 按 profile 构造 A2A/REST 客户端配置:profile 自身的 A2A 超时优先,缺省走 A2AClient 默认 */
|
|
752
|
+
function buildA2AClientOpts(profile, onDebugTrace) {
|
|
753
|
+
const t = a2aEffectiveTimeouts(profile);
|
|
754
|
+
const opts = {
|
|
755
|
+
timeout: t.timeout,
|
|
756
|
+
connectTimeout: t.connectTimeout,
|
|
757
|
+
chunkTimeout: t.chunkTimeout,
|
|
758
|
+
};
|
|
759
|
+
if (profile.remoteAuth?.value) {
|
|
760
|
+
opts.auth = { type: profile.remoteAuth.type, value: profile.remoteAuth.value, name: profile.remoteAuth.name };
|
|
761
|
+
opts.headers = buildExtraHeaders(profile.remoteAuth);
|
|
762
|
+
}
|
|
763
|
+
else if (profile.remoteAuth?.extraHeaders?.length) {
|
|
764
|
+
opts.headers = buildExtraHeaders(profile.remoteAuth);
|
|
765
|
+
}
|
|
766
|
+
if (onDebugTrace)
|
|
767
|
+
opts.onDebugTrace = onDebugTrace;
|
|
768
|
+
return opts;
|
|
677
769
|
}
|
|
678
770
|
function getWaRestClient(profile, onDebugTrace) {
|
|
679
771
|
// debug trace handler 是 per-run 回调,不能池化,否则会复用旧 handler 导致调试 trace 串流
|
|
680
772
|
if (onDebugTrace) {
|
|
681
|
-
|
|
682
|
-
if (profile.remoteAuth?.value) {
|
|
683
|
-
opts = { timeout: 300_000, auth: { type: profile.remoteAuth.type, value: profile.remoteAuth.value, name: profile.remoteAuth.name }, headers: buildExtraHeaders(profile.remoteAuth), onDebugTrace };
|
|
684
|
-
}
|
|
685
|
-
else if (profile.remoteAuth?.extraHeaders?.length) {
|
|
686
|
-
opts = { timeout: 300_000, headers: buildExtraHeaders(profile.remoteAuth), onDebugTrace };
|
|
687
|
-
}
|
|
688
|
-
else {
|
|
689
|
-
opts = { timeout: 300_000, onDebugTrace };
|
|
690
|
-
}
|
|
691
|
-
return new agent_core_1.RestA2AClient(opts);
|
|
773
|
+
return new agent_core_1.RestA2AClient(buildA2AClientOpts(profile, onDebugTrace));
|
|
692
774
|
}
|
|
693
775
|
const key = waClientForKey('rest', profile);
|
|
694
776
|
const hit = waRestClientPool.get(key);
|
|
695
777
|
if (hit && Date.now() - hit.ts < WA_CLIENT_POOL_TTL)
|
|
696
778
|
return hit.client;
|
|
697
|
-
|
|
698
|
-
if (profile.remoteAuth?.value) {
|
|
699
|
-
opts = { timeout: 300_000, auth: { type: profile.remoteAuth.type, value: profile.remoteAuth.value, name: profile.remoteAuth.name }, headers: buildExtraHeaders(profile.remoteAuth) };
|
|
700
|
-
}
|
|
701
|
-
else if (profile.remoteAuth?.extraHeaders?.length) {
|
|
702
|
-
opts = { timeout: 300_000, headers: buildExtraHeaders(profile.remoteAuth) };
|
|
703
|
-
}
|
|
704
|
-
else {
|
|
705
|
-
opts = { timeout: 300_000 };
|
|
706
|
-
}
|
|
707
|
-
const client = new agent_core_1.RestA2AClient(opts);
|
|
779
|
+
const client = new agent_core_1.RestA2AClient(buildA2AClientOpts(profile));
|
|
708
780
|
waRestClientPool.set(key, { client, ts: Date.now() });
|
|
709
781
|
if (waRestClientPool.size > 100)
|
|
710
782
|
for (const [k, v] of waRestClientPool)
|
|
@@ -715,33 +787,13 @@ function createAgentServer(options) {
|
|
|
715
787
|
function getWaA2AClient(profile, onDebugTrace) {
|
|
716
788
|
// debug trace handler 是 per-run 回调,不能池化,否则会复用旧 handler 导致调试 trace 串流
|
|
717
789
|
if (onDebugTrace) {
|
|
718
|
-
|
|
719
|
-
if (profile.remoteAuth?.value) {
|
|
720
|
-
opts = { timeout: 300_000, auth: { type: profile.remoteAuth.type, value: profile.remoteAuth.value, name: profile.remoteAuth.name }, headers: buildExtraHeaders(profile.remoteAuth), onDebugTrace };
|
|
721
|
-
}
|
|
722
|
-
else if (profile.remoteAuth?.extraHeaders?.length) {
|
|
723
|
-
opts = { timeout: 300_000, headers: buildExtraHeaders(profile.remoteAuth), onDebugTrace };
|
|
724
|
-
}
|
|
725
|
-
else {
|
|
726
|
-
opts = { timeout: 300_000, onDebugTrace };
|
|
727
|
-
}
|
|
728
|
-
return new agent_core_1.A2AClient(opts);
|
|
790
|
+
return new agent_core_1.A2AClient(buildA2AClientOpts(profile, onDebugTrace));
|
|
729
791
|
}
|
|
730
792
|
const key = waClientForKey('a2a', profile);
|
|
731
793
|
const hit = waA2AClientPool.get(key);
|
|
732
794
|
if (hit && Date.now() - hit.ts < WA_CLIENT_POOL_TTL)
|
|
733
795
|
return hit.client;
|
|
734
|
-
|
|
735
|
-
if (profile.remoteAuth?.value) {
|
|
736
|
-
opts = { timeout: 300_000, auth: { type: profile.remoteAuth.type, value: profile.remoteAuth.value, name: profile.remoteAuth.name }, headers: buildExtraHeaders(profile.remoteAuth) };
|
|
737
|
-
}
|
|
738
|
-
else if (profile.remoteAuth?.extraHeaders?.length) {
|
|
739
|
-
opts = { timeout: 300_000, headers: buildExtraHeaders(profile.remoteAuth) };
|
|
740
|
-
}
|
|
741
|
-
else {
|
|
742
|
-
opts = { timeout: 300_000 };
|
|
743
|
-
}
|
|
744
|
-
const client = new agent_core_1.A2AClient(opts);
|
|
796
|
+
const client = new agent_core_1.A2AClient(buildA2AClientOpts(profile));
|
|
745
797
|
waA2AClientPool.set(key, { client, ts: Date.now() });
|
|
746
798
|
if (waA2AClientPool.size > 100)
|
|
747
799
|
for (const [k, v] of waA2AClientPool)
|
|
@@ -750,6 +802,10 @@ function createAgentServer(options) {
|
|
|
750
802
|
return client;
|
|
751
803
|
}
|
|
752
804
|
function createRemoteRunnerFromProfile(profile, onDebugTrace) {
|
|
805
|
+
// 委派超时优先级:agent 自身配置(Fdelegate_timeout_ms)> 远程 agent 自身 A2A 超时(Fa2a_timeout)> 全局默认。
|
|
806
|
+
// 用 || 而非 ??:provider 会在源头把未配置(0/null)归一化为 undefined,但此处仍用 || 兜底,
|
|
807
|
+
// 把任何残留的 0 视为「未设置」逐级回退,最终 undefined 时由 delegate 工具回退到全局默认。
|
|
808
|
+
const runnerTimeoutMs = profile.delegateTimeoutMs || profile.a2aTimeout || undefined;
|
|
753
809
|
// REST 风格 A2A
|
|
754
810
|
if (profile.a2aProtocol === 'rest') {
|
|
755
811
|
const restConfig = profile.restA2AConfig;
|
|
@@ -757,7 +813,7 @@ function createAgentServer(options) {
|
|
|
757
813
|
throw new Error(`REST A2A profile '${profile.name}' missing capabilityId in restA2AConfig`);
|
|
758
814
|
}
|
|
759
815
|
const restClient = getWaRestClient(profile, onDebugTrace);
|
|
760
|
-
|
|
816
|
+
const runner = (0, agent_core_1.createRestA2ARemoteRunner)({
|
|
761
817
|
baseUrl: profile.remoteUrl,
|
|
762
818
|
capabilityId: restConfig.capabilityId,
|
|
763
819
|
name: `[Remote] ${profile.name}`,
|
|
@@ -770,7 +826,11 @@ function createAgentServer(options) {
|
|
|
770
826
|
fallbackToSync: restConfig.fallbackToSync,
|
|
771
827
|
streamAction: restConfig.streamAction,
|
|
772
828
|
invokeAction: restConfig.invokeAction,
|
|
829
|
+
// 透传输出模式:merge=完整内容回传主 Agent;panel=仅回传、完整内容保留在子面板
|
|
830
|
+
outputMode: profile.a2aOutputMode || 'merge',
|
|
773
831
|
});
|
|
832
|
+
runner.timeoutMs = runnerTimeoutMs;
|
|
833
|
+
return runner;
|
|
774
834
|
}
|
|
775
835
|
// 标准 JSON-RPC A2A(默认)
|
|
776
836
|
const agentUrl = profile.remoteCard?.url || profile.remoteUrl;
|
|
@@ -778,21 +838,27 @@ function createAgentServer(options) {
|
|
|
778
838
|
throw new Error(`Remote profile '${profile.name}' missing remoteUrl`);
|
|
779
839
|
}
|
|
780
840
|
let agentClient;
|
|
841
|
+
const hasCustomA2ATimeout = !!(profile.a2aTimeout || profile.a2aConnectTimeout || profile.a2aChunkTimeout);
|
|
781
842
|
// 调试模式(onDebugTrace 非空)下不能用共享 options.a2aClient--它未注入 handler,
|
|
782
843
|
// 会导致 A2A 请求 trace 永不触发;改用 getWaA2AClient 创建带 handler 的客户端。
|
|
783
|
-
|
|
844
|
+
// profile 配置了自定义 A2A 超时时也必须走 getWaA2AClient(共享 client 写死 300_000,无法按 profile 覆盖)。
|
|
845
|
+
if (options.a2aClient && !onDebugTrace && !hasCustomA2ATimeout) {
|
|
784
846
|
agentClient = options.a2aClient;
|
|
785
847
|
}
|
|
786
848
|
else {
|
|
787
849
|
agentClient = getWaA2AClient(profile, onDebugTrace);
|
|
788
850
|
}
|
|
789
|
-
|
|
851
|
+
const runner = (0, agent_core_1.createA2ARemoteRunner)({
|
|
790
852
|
agentUrl,
|
|
791
853
|
name: `[Remote] ${profile.name}`,
|
|
792
854
|
description: profile.description || profile.domain || 'Remote specialist agent',
|
|
793
855
|
streaming: true,
|
|
794
856
|
client: agentClient,
|
|
857
|
+
// 透传输出模式:merge=完整内容回传主 Agent;panel=仅回传、完整内容保留在子面板
|
|
858
|
+
outputMode: profile.a2aOutputMode || 'merge',
|
|
795
859
|
});
|
|
860
|
+
runner.timeoutMs = runnerTimeoutMs;
|
|
861
|
+
return runner;
|
|
796
862
|
}
|
|
797
863
|
/**
|
|
798
864
|
* 运行远程 Agent(直接通过 A2A 协议与远程端点通信,不初始化本地引擎)
|
|
@@ -887,7 +953,10 @@ function createAgentServer(options) {
|
|
|
887
953
|
};
|
|
888
954
|
// 确保 abortSignal 始终有值(runner 依赖 ctx.abortSignal 进行中止控制)
|
|
889
955
|
const effectiveAbortSignal = abortSignal ?? new AbortController().signal;
|
|
890
|
-
|
|
956
|
+
// 包裹全局去重层:远程 A2A 服务重复下发的 tool_call / start / done 在源头被挡掉,
|
|
957
|
+
// 避免前端出现重复的工具卡片与进度噪声。
|
|
958
|
+
const dedupedEmitSubEvent = dedupeSubEventEmitter(emitSubEvent, () => ({ parentToolCallId: parentCallId, agentName: profile.name }));
|
|
959
|
+
const mockCtx = { currentToolCallId: parentCallId, abortSignal: effectiveAbortSignal, emitSubEvent: dedupedEmitSubEvent };
|
|
891
960
|
console.log(`[AgentServer] Remote agent stream: profile=${profile.name}(${profile.id}), url=${profile.remoteUrl}, protocol=${profile.a2aProtocol || 'json-rpc'}`);
|
|
892
961
|
try {
|
|
893
962
|
const result = await runner.run(messageText, mockCtx, {});
|
|
@@ -1039,6 +1108,10 @@ function createAgentServer(options) {
|
|
|
1039
1108
|
catch (err) {
|
|
1040
1109
|
console.warn('[AgentServer] Memory recall failed:', err?.message);
|
|
1041
1110
|
}
|
|
1111
|
+
// panel 模式追踪:记录哪些子 Agent 调用已进入摘要模式
|
|
1112
|
+
const subAgentSummaryActive = new Set();
|
|
1113
|
+
const subAgentContentMap = new Map();
|
|
1114
|
+
const subAgentMainRelayed = new Set();
|
|
1042
1115
|
const emitSubEvent = (evt) => {
|
|
1043
1116
|
// 子 Agent 的 interaction_request 事件需要特殊处理,转发为顶层 interaction-request
|
|
1044
1117
|
if (evt.eventType === 'interaction_request') {
|
|
@@ -1058,7 +1131,52 @@ function createAgentServer(options) {
|
|
|
1058
1131
|
});
|
|
1059
1132
|
}
|
|
1060
1133
|
else {
|
|
1134
|
+
// 始终转发到子 Agent 面板(sub-agent 事件供前端构建子面板卡片)
|
|
1061
1135
|
registry.addEvent(threadIdNum, 'sub-agent', evt);
|
|
1136
|
+
// ── panel 模式逻辑 ──
|
|
1137
|
+
// 远程/子 Agent 在 start 事件声明 outputMode='panel' 时进入摘要模式:
|
|
1138
|
+
// 此后其原始 content 不再 relay 进主气泡(原文留在 sub-agent 面板),
|
|
1139
|
+
// 主气泡改由 kind=summary 的 content 或 summary(mode=final) 事件填充。
|
|
1140
|
+
if (evt.eventType === 'start' && evt.data?.outputMode === 'panel') {
|
|
1141
|
+
subAgentSummaryActive.add(evt.parentToolCallId);
|
|
1142
|
+
}
|
|
1143
|
+
// content 事件:panel 模式下抑制过程性内容,仅 kind=summary 进主气泡
|
|
1144
|
+
if (evt.eventType === 'content' && typeof evt.data?.content === 'string' && evt.data.content) {
|
|
1145
|
+
const current = evt.data.content;
|
|
1146
|
+
const isFirst = !subAgentContentMap.has(evt.parentToolCallId);
|
|
1147
|
+
const prev = subAgentContentMap.get(evt.parentToolCallId) ?? '';
|
|
1148
|
+
// 累积式内容(本地 specialist):current 以 prev 为前缀且更长 -> 取后缀
|
|
1149
|
+
// 增量式内容(远程 A2A):直接用作 delta
|
|
1150
|
+
const delta = (prev.length > 0 && current.length > prev.length && current.startsWith(prev))
|
|
1151
|
+
? current.slice(prev.length)
|
|
1152
|
+
: current;
|
|
1153
|
+
subAgentContentMap.set(evt.parentToolCallId, prev + delta);
|
|
1154
|
+
if ((!subAgentSummaryActive.has(evt.parentToolCallId) || evt.data?.kind === 'summary') && delta) {
|
|
1155
|
+
const prefix = isFirst ? '\n\n' : '';
|
|
1156
|
+
registry.addEvent(threadIdNum, 'content', prefix + delta);
|
|
1157
|
+
subAgentMainRelayed.add(evt.parentToolCallId);
|
|
1158
|
+
}
|
|
1159
|
+
}
|
|
1160
|
+
// summary 事件:mode='final' 写入主气泡作为"分析结论"
|
|
1161
|
+
if (evt.eventType === 'summary' && typeof evt.data?.content === 'string' && evt.data.content) {
|
|
1162
|
+
subAgentSummaryActive.add(evt.parentToolCallId);
|
|
1163
|
+
const mode = evt.data.mode || 'replace';
|
|
1164
|
+
if (mode === 'final') {
|
|
1165
|
+
const isFirstMain = !subAgentMainRelayed.has(evt.parentToolCallId);
|
|
1166
|
+
const prefix = isFirstMain ? '\n\n' : '';
|
|
1167
|
+
registry.addEvent(threadIdNum, 'content', prefix + evt.data.content);
|
|
1168
|
+
subAgentMainRelayed.add(evt.parentToolCallId);
|
|
1169
|
+
}
|
|
1170
|
+
}
|
|
1171
|
+
// done/error:清理追踪 + 尾部分隔
|
|
1172
|
+
if (evt.eventType === 'done' || evt.eventType === 'error') {
|
|
1173
|
+
if (subAgentMainRelayed.has(evt.parentToolCallId)) {
|
|
1174
|
+
registry.addEvent(threadIdNum, 'content', '\n\n');
|
|
1175
|
+
subAgentMainRelayed.delete(evt.parentToolCallId);
|
|
1176
|
+
}
|
|
1177
|
+
subAgentContentMap.delete(evt.parentToolCallId);
|
|
1178
|
+
subAgentSummaryActive.delete(evt.parentToolCallId);
|
|
1179
|
+
}
|
|
1062
1180
|
}
|
|
1063
1181
|
};
|
|
1064
1182
|
customContext.emitSubEvent = emitSubEvent;
|