@cicctencent/agent-core 0.1.20 → 0.1.22

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.
Files changed (153) hide show
  1. package/dist/cjs/a2a/index.d.ts +4 -0
  2. package/dist/cjs/a2a/index.d.ts.map +1 -1
  3. package/dist/cjs/a2a/index.js +6 -1
  4. package/dist/cjs/a2a/index.js.map +1 -1
  5. package/dist/cjs/a2a/rest-client.d.ts +111 -0
  6. package/dist/cjs/a2a/rest-client.d.ts.map +1 -0
  7. package/dist/cjs/a2a/rest-client.js +342 -0
  8. package/dist/cjs/a2a/rest-client.js.map +1 -0
  9. package/dist/cjs/a2a/rest-runner.d.ts +41 -0
  10. package/dist/cjs/a2a/rest-runner.d.ts.map +1 -0
  11. package/dist/cjs/a2a/rest-runner.js +188 -0
  12. package/dist/cjs/a2a/rest-runner.js.map +1 -0
  13. package/dist/cjs/agent/engine.d.ts +17 -0
  14. package/dist/cjs/agent/engine.d.ts.map +1 -1
  15. package/dist/cjs/agent/engine.js +208 -13
  16. package/dist/cjs/agent/engine.js.map +1 -1
  17. package/dist/cjs/agent/llm-caller.d.ts +12 -1
  18. package/dist/cjs/agent/llm-caller.d.ts.map +1 -1
  19. package/dist/cjs/agent/llm-caller.js +101 -32
  20. package/dist/cjs/agent/llm-caller.js.map +1 -1
  21. package/dist/cjs/agent/types.d.ts +1 -1
  22. package/dist/cjs/agent/types.d.ts.map +1 -1
  23. package/dist/cjs/index.d.ts +3 -0
  24. package/dist/cjs/index.d.ts.map +1 -1
  25. package/dist/cjs/index.js +8 -1
  26. package/dist/cjs/index.js.map +1 -1
  27. package/dist/cjs/kb/in-memory-kb.d.ts.map +1 -1
  28. package/dist/cjs/kb/in-memory-kb.js +2 -1
  29. package/dist/cjs/kb/in-memory-kb.js.map +1 -1
  30. package/dist/cjs/llm/anthropic-provider.d.ts.map +1 -1
  31. package/dist/cjs/llm/anthropic-provider.js +1 -0
  32. package/dist/cjs/llm/anthropic-provider.js.map +1 -1
  33. package/dist/cjs/llm/ollama-provider.d.ts.map +1 -1
  34. package/dist/cjs/llm/ollama-provider.js +1 -0
  35. package/dist/cjs/llm/ollama-provider.js.map +1 -1
  36. package/dist/cjs/llm/openai-provider.d.ts.map +1 -1
  37. package/dist/cjs/llm/openai-provider.js +17 -12
  38. package/dist/cjs/llm/openai-provider.js.map +1 -1
  39. package/dist/cjs/llm/types.d.ts +4 -0
  40. package/dist/cjs/llm/types.d.ts.map +1 -1
  41. package/dist/cjs/llm/types.js.map +1 -1
  42. package/dist/cjs/memory/checkpoint.d.ts.map +1 -1
  43. package/dist/cjs/memory/checkpoint.js +3 -2
  44. package/dist/cjs/memory/checkpoint.js.map +1 -1
  45. package/dist/cjs/memory/extractor.d.ts +7 -0
  46. package/dist/cjs/memory/extractor.d.ts.map +1 -1
  47. package/dist/cjs/memory/extractor.js +18 -1
  48. package/dist/cjs/memory/extractor.js.map +1 -1
  49. package/dist/cjs/memory/persona.d.ts +4 -0
  50. package/dist/cjs/memory/persona.d.ts.map +1 -1
  51. package/dist/cjs/memory/persona.js +15 -1
  52. package/dist/cjs/memory/persona.js.map +1 -1
  53. package/dist/cjs/memory/pipeline.d.ts +3 -0
  54. package/dist/cjs/memory/pipeline.d.ts.map +1 -1
  55. package/dist/cjs/memory/pipeline.js +7 -4
  56. package/dist/cjs/memory/pipeline.js.map +1 -1
  57. package/dist/cjs/memory/scene.d.ts +4 -0
  58. package/dist/cjs/memory/scene.d.ts.map +1 -1
  59. package/dist/cjs/memory/scene.js +15 -1
  60. package/dist/cjs/memory/scene.js.map +1 -1
  61. package/dist/cjs/middleware/audit-log.d.ts.map +1 -1
  62. package/dist/cjs/middleware/audit-log.js +3 -2
  63. package/dist/cjs/middleware/audit-log.js.map +1 -1
  64. package/dist/cjs/store/file-tool-result-store.d.ts.map +1 -1
  65. package/dist/cjs/store/file-tool-result-store.js +2 -1
  66. package/dist/cjs/store/file-tool-result-store.js.map +1 -1
  67. package/dist/cjs/store/json-store.d.ts +5 -1
  68. package/dist/cjs/store/json-store.d.ts.map +1 -1
  69. package/dist/cjs/store/json-store.js +5 -1
  70. package/dist/cjs/store/json-store.js.map +1 -1
  71. package/dist/cjs/tool/delegate.js +1 -1
  72. package/dist/cjs/tool/delegate.js.map +1 -1
  73. package/dist/cjs/utils/date.d.ts +36 -0
  74. package/dist/cjs/utils/date.d.ts.map +1 -0
  75. package/dist/cjs/utils/date.js +52 -0
  76. package/dist/cjs/utils/date.js.map +1 -0
  77. package/dist/esm/a2a/index.d.ts +4 -0
  78. package/dist/esm/a2a/index.d.ts.map +1 -1
  79. package/dist/esm/a2a/index.js +3 -0
  80. package/dist/esm/a2a/index.js.map +1 -1
  81. package/dist/esm/a2a/rest-client.d.ts +111 -0
  82. package/dist/esm/a2a/rest-client.d.ts.map +1 -0
  83. package/dist/esm/a2a/rest-client.js +338 -0
  84. package/dist/esm/a2a/rest-client.js.map +1 -0
  85. package/dist/esm/a2a/rest-runner.d.ts +41 -0
  86. package/dist/esm/a2a/rest-runner.d.ts.map +1 -0
  87. package/dist/esm/a2a/rest-runner.js +185 -0
  88. package/dist/esm/a2a/rest-runner.js.map +1 -0
  89. package/dist/esm/agent/engine.d.ts +17 -0
  90. package/dist/esm/agent/engine.d.ts.map +1 -1
  91. package/dist/esm/agent/engine.js +208 -13
  92. package/dist/esm/agent/engine.js.map +1 -1
  93. package/dist/esm/agent/llm-caller.d.ts +12 -1
  94. package/dist/esm/agent/llm-caller.d.ts.map +1 -1
  95. package/dist/esm/agent/llm-caller.js +101 -32
  96. package/dist/esm/agent/llm-caller.js.map +1 -1
  97. package/dist/esm/agent/types.d.ts +1 -1
  98. package/dist/esm/agent/types.d.ts.map +1 -1
  99. package/dist/esm/index.d.ts +3 -0
  100. package/dist/esm/index.d.ts.map +1 -1
  101. package/dist/esm/index.js +3 -0
  102. package/dist/esm/index.js.map +1 -1
  103. package/dist/esm/kb/in-memory-kb.d.ts.map +1 -1
  104. package/dist/esm/kb/in-memory-kb.js +2 -1
  105. package/dist/esm/kb/in-memory-kb.js.map +1 -1
  106. package/dist/esm/llm/anthropic-provider.d.ts.map +1 -1
  107. package/dist/esm/llm/anthropic-provider.js +1 -0
  108. package/dist/esm/llm/anthropic-provider.js.map +1 -1
  109. package/dist/esm/llm/ollama-provider.d.ts.map +1 -1
  110. package/dist/esm/llm/ollama-provider.js +1 -0
  111. package/dist/esm/llm/ollama-provider.js.map +1 -1
  112. package/dist/esm/llm/openai-provider.d.ts.map +1 -1
  113. package/dist/esm/llm/openai-provider.js +17 -12
  114. package/dist/esm/llm/openai-provider.js.map +1 -1
  115. package/dist/esm/llm/types.d.ts +4 -0
  116. package/dist/esm/llm/types.d.ts.map +1 -1
  117. package/dist/esm/llm/types.js.map +1 -1
  118. package/dist/esm/memory/checkpoint.d.ts.map +1 -1
  119. package/dist/esm/memory/checkpoint.js +3 -2
  120. package/dist/esm/memory/checkpoint.js.map +1 -1
  121. package/dist/esm/memory/extractor.d.ts +7 -0
  122. package/dist/esm/memory/extractor.d.ts.map +1 -1
  123. package/dist/esm/memory/extractor.js +18 -1
  124. package/dist/esm/memory/extractor.js.map +1 -1
  125. package/dist/esm/memory/persona.d.ts +4 -0
  126. package/dist/esm/memory/persona.d.ts.map +1 -1
  127. package/dist/esm/memory/persona.js +15 -1
  128. package/dist/esm/memory/persona.js.map +1 -1
  129. package/dist/esm/memory/pipeline.d.ts +3 -0
  130. package/dist/esm/memory/pipeline.d.ts.map +1 -1
  131. package/dist/esm/memory/pipeline.js +7 -4
  132. package/dist/esm/memory/pipeline.js.map +1 -1
  133. package/dist/esm/memory/scene.d.ts +4 -0
  134. package/dist/esm/memory/scene.d.ts.map +1 -1
  135. package/dist/esm/memory/scene.js +15 -1
  136. package/dist/esm/memory/scene.js.map +1 -1
  137. package/dist/esm/middleware/audit-log.d.ts.map +1 -1
  138. package/dist/esm/middleware/audit-log.js +3 -2
  139. package/dist/esm/middleware/audit-log.js.map +1 -1
  140. package/dist/esm/store/file-tool-result-store.d.ts.map +1 -1
  141. package/dist/esm/store/file-tool-result-store.js +2 -1
  142. package/dist/esm/store/file-tool-result-store.js.map +1 -1
  143. package/dist/esm/store/json-store.d.ts +5 -1
  144. package/dist/esm/store/json-store.d.ts.map +1 -1
  145. package/dist/esm/store/json-store.js +5 -1
  146. package/dist/esm/store/json-store.js.map +1 -1
  147. package/dist/esm/tool/delegate.js +1 -1
  148. package/dist/esm/tool/delegate.js.map +1 -1
  149. package/dist/esm/utils/date.d.ts +36 -0
  150. package/dist/esm/utils/date.d.ts.map +1 -0
  151. package/dist/esm/utils/date.js +48 -0
  152. package/dist/esm/utils/date.js.map +1 -0
  153. package/package.json +2 -2
@@ -0,0 +1,188 @@
1
+ "use strict";
2
+ // ============================================================
3
+ // REST 风格 A2A Remote Runner — 将 REST 风格远程 Agent 包装为 SubAgentRunner
4
+ // 供 delegate_task 使用,与标准 JSON-RPC A2A Runner 并行存在
5
+ // ============================================================
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.createRestA2ARemoteRunner = createRestA2ARemoteRunner;
8
+ /**
9
+ * 创建 REST 风格远程 A2A Agent 的 SubAgentRunner
10
+ *
11
+ * 核心逻辑:
12
+ * - streaming=true && ctx.emitSubEvent 存在 → invokeStreaming + 事件转发
13
+ * - 否则 → invoke 同步阻塞
14
+ *
15
+ * 事件映射:
16
+ * - status(running) → start
17
+ * - stage → progress
18
+ * - tool_call → tool_call
19
+ * - tool_result → tool_result
20
+ * - message_chunk → content
21
+ * - result → done(由 runStreaming 提取最终文本)
22
+ * - error → error
23
+ */
24
+ function createRestA2ARemoteRunner(config) {
25
+ const { baseUrl, capabilityId, name: runnerName, description: runnerDesc, client, staticFields = {}, taskField = 'analysis_focus', streaming = true, fallbackToSync = true, } = config;
26
+ /** 将 task 字符串构建为请求输入 */
27
+ function buildInput(task) {
28
+ return { ...staticFields, [taskField]: task };
29
+ }
30
+ /** 从 RestCapabilityResult 中提取最终文本 */
31
+ function extractResultText(result) {
32
+ if (result.result?.final_text)
33
+ return result.result.final_text;
34
+ if (result.result?.summary)
35
+ return result.result.summary;
36
+ // 降级:JSON 序列化整个 result
37
+ return JSON.stringify(result.result || result, null, 2);
38
+ }
39
+ // ---- 同步执行 ----
40
+ const runSync = async (task) => {
41
+ try {
42
+ const result = await client.invoke(baseUrl, capabilityId, buildInput(task));
43
+ if (result.status === 'failed' || result.error) {
44
+ const errMsg = result.error || 'Unknown error';
45
+ console.error(`[REST-A2A] '${runnerName}' sync failed: ${errMsg}`);
46
+ return `[REST A2A '${runnerName}'] Error: ${errMsg}`;
47
+ }
48
+ return extractResultText(result);
49
+ }
50
+ catch (err) {
51
+ console.error(`[REST-A2A] '${runnerName}' sync error: ${err?.message || err}`);
52
+ return `[REST A2A '${runnerName}'] Error: ${err.message || err}`;
53
+ }
54
+ };
55
+ // ---- 流式执行 ----
56
+ const runStreaming = async (task, ctx) => {
57
+ const emit = ctx.emitSubEvent;
58
+ const parentCallId = ctx.currentToolCallId || `rest_a2a_${Date.now()}`;
59
+ emitStart(emit, parentCallId, runnerName, task);
60
+ try {
61
+ const finalResult = await client.invokeStreaming(baseUrl, capabilityId, buildInput(task), (event) => forwardRestSSEEvent(emit, parentCallId, runnerName, event), ctx.abortSignal);
62
+ if (finalResult?.status === 'failed' || finalResult?.error) {
63
+ const errMsg = finalResult.error || 'Remote agent returned error';
64
+ console.error(`[REST-A2A] '${runnerName}' failed: ${errMsg}`);
65
+ emitError(emit, parentCallId, runnerName, errMsg);
66
+ return `[REST A2A '${runnerName}'] Error: ${errMsg}`;
67
+ }
68
+ const resultText = finalResult
69
+ ? extractResultText(finalResult)
70
+ : 'Task completed with no output.';
71
+ emitDone(emit, parentCallId, runnerName, resultText.length);
72
+ return resultText;
73
+ }
74
+ catch (err) {
75
+ const errMsg = err?.message || String(err);
76
+ console.error(`[REST-A2A] '${runnerName}' streaming error: ${errMsg}`);
77
+ // 流式失败,自动回退到同步模式
78
+ if (fallbackToSync) {
79
+ console.log(`[REST-A2A] '${runnerName}' falling back to sync`);
80
+ return runSync(task);
81
+ }
82
+ emitError(emit, parentCallId, runnerName, errMsg);
83
+ return `[REST A2A '${runnerName}'] Error: ${errMsg}`;
84
+ }
85
+ };
86
+ return {
87
+ name: runnerName,
88
+ description: runnerDesc,
89
+ run: async (task, ctx, _options) => {
90
+ return streaming && ctx?.emitSubEvent
91
+ ? runStreaming(task, ctx)
92
+ : runSync(task);
93
+ },
94
+ };
95
+ }
96
+ // ---- 事件转发辅助函数 ----
97
+ function emitStart(emit, parentCallId, agentName, task) {
98
+ emit?.({
99
+ parentToolCallId: parentCallId,
100
+ agentName,
101
+ eventType: 'start',
102
+ data: { agentName, task, source: 'rest-a2a-streaming' },
103
+ });
104
+ }
105
+ function emitDone(emit, parentCallId, agentName, contentLength) {
106
+ emit?.({
107
+ parentToolCallId: parentCallId,
108
+ agentName,
109
+ eventType: 'done',
110
+ data: { contentLength },
111
+ });
112
+ }
113
+ function emitError(emit, parentCallId, agentName, error) {
114
+ emit?.({
115
+ parentToolCallId: parentCallId,
116
+ agentName,
117
+ eventType: 'error',
118
+ data: { error },
119
+ });
120
+ }
121
+ /**
122
+ * 将 REST 风格 SSE 事件转发为 SubAgentEvent
123
+ *
124
+ * 事件映射:
125
+ * status(running) → progress
126
+ * stage → progress
127
+ * tool_call → tool_call
128
+ * tool_result → tool_result
129
+ * message_chunk → content
130
+ * result → (由 runStreaming 处理,不转发)
131
+ * error → error
132
+ */
133
+ function forwardRestSSEEvent(emit, parentCallId, agentName, event) {
134
+ const { event: eventType, data } = event;
135
+ switch (eventType) {
136
+ case 'status':
137
+ // running → progress;completed → 由 result 事件处理
138
+ if (data.status === 'running') {
139
+ emit?.({
140
+ parentToolCallId: parentCallId,
141
+ agentName,
142
+ eventType: 'progress',
143
+ data: { status: 'running', trace_id: data.trace_id },
144
+ });
145
+ }
146
+ break;
147
+ case 'stage':
148
+ emit?.({
149
+ parentToolCallId: parentCallId,
150
+ agentName,
151
+ eventType: 'progress',
152
+ data: { stage: data.stage, trace_id: data.trace_id },
153
+ });
154
+ break;
155
+ case 'tool_call':
156
+ emit?.({
157
+ parentToolCallId: parentCallId,
158
+ agentName,
159
+ eventType: 'tool_call',
160
+ data,
161
+ });
162
+ break;
163
+ case 'tool_result':
164
+ emit?.({
165
+ parentToolCallId: parentCallId,
166
+ agentName,
167
+ eventType: 'tool_result',
168
+ data,
169
+ });
170
+ break;
171
+ case 'message_chunk':
172
+ emit?.({
173
+ parentToolCallId: parentCallId,
174
+ agentName,
175
+ eventType: 'content',
176
+ data: { content: data.content },
177
+ });
178
+ break;
179
+ // result 和 error 由 runStreaming 的 finalResult 处理,不在此转发
180
+ case 'result':
181
+ case 'error':
182
+ break;
183
+ default:
184
+ // 未知事件类型 → 丢弃
185
+ break;
186
+ }
187
+ }
188
+ //# sourceMappingURL=rest-runner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"rest-runner.js","sourceRoot":"","sources":["../../../src/a2a/rest-runner.ts"],"names":[],"mappings":"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"}
@@ -68,6 +68,23 @@ export declare class AgentEngine {
68
68
  private resolveSkill;
69
69
  /** 构建初始消息列表(system + history + user) */
70
70
  private buildInitialMessages;
71
+ /**
72
+ * 将对话历史压缩为摘要,用 <conversation_history> XML 标签包裹,
73
+ * 注入为 user 消息 + assistant 确认对。
74
+ *
75
+ * 摘要是陈述句,当前消息是指令句,语义上天然区分。
76
+ * ≤2 条消息时直接拼接,省去 LLM 调用;LLM 失败时回退到简单拼接。
77
+ *
78
+ * @param history 历史消息列表
79
+ * @param currentText 当前用户消息文本(用于排除最后一条匹配的 user 消息,null 表示全部纳入摘要)
80
+ * @returns 摘要消息对 [user: <conversation_history>..., assistant: 确认]
81
+ */
82
+ private buildHistoryMessages;
83
+ /**
84
+ * 通过轻量 LLM 调用将多轮对话历史压缩为 ≤800 字的陈述句摘要。
85
+ * LLM 失败时回退到简单拼接。
86
+ */
87
+ private summarizeHistory;
71
88
  /** 获取工具定义列表(含预过滤 + 渐进式发现) */
72
89
  private resolveToolDefs;
73
90
  /** 构建 ToolContext */
@@ -1 +1 @@
1
- {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../../src/agent/engine.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"engine.d.ts","sourceRoot":"","sources":["../../../src/agent/engine.ts"],"names":[],"mappings":"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"}
@@ -39,6 +39,43 @@ function buildSkillCatalog(skillManager) {
39
39
  const lines = manifests.map(m => `- \`${m.id}\`: ${m.name} — ${m.description || '(no description)'}`);
40
40
  return `## Available Skills\n\nYou have access to the following skills. Use \`load_skill(skill_id="<id>")\` to load full instructions when a task matches a skill's purpose.\n\n${lines.join('\n')}`;
41
41
  }
42
+ /**
43
+ * 生成工具调用的简短摘要(用于历史摘要)。
44
+ * 保留工具名和关键参数,让 LLM 在后续轮次中能复用之前的调用参数。
45
+ */
46
+ function summarizeToolCall(name, args) {
47
+ switch (name) {
48
+ case 'send_email': {
49
+ const to = Array.isArray(args.to) ? args.to.join(',') : (args.to || '');
50
+ const subject = (args.subject || '').slice(0, 60);
51
+ const hasAttachments = Array.isArray(args.attachments) && args.attachments.length > 0;
52
+ return `send_email(to=${to}, subject="${subject}"${hasAttachments ? ', with_attachments' : ''})`;
53
+ }
54
+ case 'delegate_task': {
55
+ const agent = args.agent || '';
56
+ const task = typeof args.task === 'string' ? args.task.slice(0, 80) : '';
57
+ return `delegate_task(agent=${agent}, task="${task}...")`;
58
+ }
59
+ case 'create_report':
60
+ case 'create_presentation':
61
+ case 'create_document':
62
+ case 'create_pdf':
63
+ case 'save_artifact': {
64
+ const filename = (args.filename || args.title || '').slice(0, 60);
65
+ return `${name}(filename="${filename}")`;
66
+ }
67
+ default: {
68
+ // 通用:展示前 3 个参数的键名和值摘要
69
+ const keys = Object.keys(args).slice(0, 3);
70
+ const parts = keys.map(k => {
71
+ const v = args[k];
72
+ const s = typeof v === 'string' ? v.slice(0, 50) : JSON.stringify(v)?.slice(0, 50);
73
+ return `${k}=${s}`;
74
+ });
75
+ return `${name}(${parts.join(', ')})`;
76
+ }
77
+ }
78
+ }
42
79
  class AgentEngine {
43
80
  config;
44
81
  /** sessionId → AgentStatus */
@@ -217,7 +254,7 @@ class AgentEngine {
217
254
  return { actualMessage: options.message, skillMatched: false };
218
255
  }
219
256
  /** 构建初始消息列表(system + history + user) */
220
- buildInitialMessages(options, skillContent, actualMessage, skillMatched) {
257
+ async buildInitialMessages(options, skillContent, actualMessage, skillMatched) {
221
258
  const { sessionId } = options;
222
259
  let systemPrompt = this.config.basePrompt || '';
223
260
  if (options.systemPromptOverride) {
@@ -260,8 +297,9 @@ class AgentEngine {
260
297
  systemPrompt += PROGRESS_NARRATION_RULES;
261
298
  }
262
299
  }
263
- // 去重:如果历史最后一条已经是当前用户消息(由 buildThreadHistory 从 DB 恢复),
264
- // 则不再重复追加,避免 LLM 看到两条连续相同的 user 消息导致意图理解偏差
300
+ // 去重检测:如果历史最后一条已经是当前用户消息(由 buildThreadHistory 从 DB 恢复),
301
+ // 则在构建历史摘要时排除该消息,但仍需将当前用户消息作为 userMessage 传入。
302
+ // 之前的实现将 userMessage 设为 undefined,导致 LLM 收不到用户实际请求 → 空响应。
265
303
  const history = this.historyManager.getHistory(sessionId);
266
304
  const lastMsg = history.length > 0 ? history[history.length - 1] : null;
267
305
  // 多模态消息去重:比较文本内容
@@ -269,17 +307,158 @@ class AgentEngine {
269
307
  const isDuplicateUserMessage = lastMsg?.role === 'user'
270
308
  && typeof lastMsg.content === 'string'
271
309
  && lastMsg.content === actualText;
310
+ if (isDuplicateUserMessage) {
311
+ console.log(`[Engine] Detected duplicate user message in history (likely restored from DB), excluding from summary and re-appending as userMessage`);
312
+ }
272
313
  // 非破坏性上下文投影(Context Collapse):
273
314
  // 当历史 token 超过预算时,使用投影而非破坏性压缩,保留原始历史完整
274
315
  const maxContextTokens = this.config.contextBudget?.maxContextTokens ?? 100000;
275
316
  const projectedHistory = this.historyManager.getProjectedHistory
276
317
  ? this.historyManager.getProjectedHistory(sessionId, maxContextTokens)
277
318
  : history;
319
+ // 历史摘要 + XML 标签:将多轮对话历史压缩为 ≤300 字摘要,
320
+ // 用 <conversation_history> 标签包裹注入为 user+assistant 消息对。
321
+ // 摘要是陈述句,当前消息是指令句,语义上天然区分,从结构上消除混淆。
322
+ // 当 isDuplicateUserMessage 时,buildHistoryMessages 会从摘要中排除当前消息,
323
+ // 然后通过 userMessage 参数重新追加,确保 LLM 能看到用户的实际请求。
324
+ const historyMessages = isDuplicateUserMessage
325
+ ? await this.buildHistoryMessages(projectedHistory, actualText)
326
+ : await this.buildHistoryMessages(projectedHistory, null);
278
327
  return this.config.contextManager.buildMessages({
279
328
  systemPrompt,
280
- history: projectedHistory,
281
- userMessage: isDuplicateUserMessage ? undefined : actualMessage,
329
+ history: historyMessages,
330
+ userMessage: actualMessage,
331
+ });
332
+ }
333
+ /**
334
+ * 将对话历史压缩为摘要,用 <conversation_history> XML 标签包裹,
335
+ * 注入为 user 消息 + assistant 确认对。
336
+ *
337
+ * 摘要是陈述句,当前消息是指令句,语义上天然区分。
338
+ * ≤2 条消息时直接拼接,省去 LLM 调用;LLM 失败时回退到简单拼接。
339
+ *
340
+ * @param history 历史消息列表
341
+ * @param currentText 当前用户消息文本(用于排除最后一条匹配的 user 消息,null 表示全部纳入摘要)
342
+ * @returns 摘要消息对 [user: <conversation_history>..., assistant: 确认]
343
+ */
344
+ async buildHistoryMessages(history, currentText) {
345
+ if (history.length === 0)
346
+ return [];
347
+ // 排除最后一条与当前消息匹配的 user 消息(避免重复)
348
+ let effectiveHistory = history;
349
+ if (currentText) {
350
+ for (let i = history.length - 1; i >= 0; i--) {
351
+ if (history[i].role === 'user') {
352
+ const text = typeof history[i].content === 'string'
353
+ ? history[i].content
354
+ : (0, types_ts_1.extractTextFromContent)(history[i].content);
355
+ if (text === currentText) {
356
+ effectiveHistory = history.slice(0, i);
357
+ }
358
+ break;
359
+ }
360
+ }
361
+ }
362
+ if (effectiveHistory.length === 0)
363
+ return [];
364
+ // 提取纯文本用于摘要(包含工具调用和工具结果,否则 LLM 丢失关键上下文)
365
+ const textHistory = effectiveHistory
366
+ .map(m => {
367
+ if (m.role === 'tool') {
368
+ // 工具结果消息:保留状态和摘要
369
+ const text = typeof m.content === 'string'
370
+ ? m.content
371
+ : (0, types_ts_1.extractTextFromContent)(m.content);
372
+ // 保留 artifact URL(关键!否则后续 send_email 无法引用已生成的报告)
373
+ const urlMatch = text.match(/(?:URL|url|artifact)[::]\s*(\/api\/[^\s"']+)/i);
374
+ const urlInfo = urlMatch ? ` [artifact_url: ${urlMatch[1]}]` : '';
375
+ const truncated = text.length > 150
376
+ ? text.slice(0, 150) + '...' + urlInfo
377
+ : text + urlInfo;
378
+ return `[工具结果]: ${truncated}`;
379
+ }
380
+ const text = typeof m.content === 'string'
381
+ ? m.content
382
+ : (0, types_ts_1.extractTextFromContent)(m.content);
383
+ // 为 assistant 消息附加工具调用摘要(关键!否则 send_email 等参数完全丢失)
384
+ let toolInfo = '';
385
+ if (m.role === 'assistant' && m.tool_calls?.length) {
386
+ const callSummaries = m.tool_calls.map(tc => {
387
+ let args = {};
388
+ try {
389
+ args = JSON.parse(tc.function.arguments);
390
+ }
391
+ catch { /* ignore */ }
392
+ return summarizeToolCall(tc.function.name, args);
393
+ });
394
+ toolInfo = ` [调用: ${callSummaries.join('; ')}]`;
395
+ }
396
+ // 截断过长的内容(assistant 保留较短,因为工具调用信息更重要)
397
+ const maxLen = m.role === 'assistant' ? 150 : 200;
398
+ const truncated = text.length > maxLen
399
+ ? text.slice(0, maxLen) + '...'
400
+ : text;
401
+ return `${m.role === 'user' ? '用户' : '助手'}: ${truncated}${toolInfo}`;
282
402
  });
403
+ if (textHistory.length === 0)
404
+ return [];
405
+ // ≤2 条消息直接拼接,省去 LLM 调用
406
+ let summary;
407
+ if (textHistory.length <= 2) {
408
+ summary = textHistory.join('\n');
409
+ }
410
+ else {
411
+ summary = await this.summarizeHistory(textHistory);
412
+ }
413
+ // 用 XML 标签包裹,注入为 user + assistant 确认对
414
+ return [
415
+ {
416
+ role: 'user',
417
+ content: `<conversation_history>\n${summary}\n</conversation_history>`,
418
+ },
419
+ {
420
+ role: 'assistant',
421
+ content: '好的,我已了解之前的对话内容。请告诉我您现在的需求。',
422
+ },
423
+ ];
424
+ }
425
+ /**
426
+ * 通过轻量 LLM 调用将多轮对话历史压缩为 ≤800 字的陈述句摘要。
427
+ * LLM 失败时回退到简单拼接。
428
+ */
429
+ async summarizeHistory(textHistory) {
430
+ try {
431
+ const provider = this.config.llmProvider;
432
+ const prompt = `请将以下对话历史压缩为不超过800字的陈述句摘要。必须保留:
433
+ 1. 用户的核心请求和意图
434
+ 2. 助手调用了哪些工具(工具名+关键参数,如收件人邮箱、邮件主题等)
435
+ 3. 工具调用的结果(成功/失败)
436
+ 4. 已生成的内容摘要(如报告主题、数据要点)
437
+ 5. **artifact URL**(格式如 /api/work/artifacts/...),这是后续发送邮件等操作的关键引用
438
+
439
+ 不要添加任何评价或建议,只做客观压缩:
440
+
441
+ ${textHistory.join('\n')}`;
442
+ const response = await provider.chat({
443
+ messages: [
444
+ { role: 'system', content: '你是一个对话摘要助手。将对话历史压缩为简洁的陈述句摘要,不超过800字。务必保留工具调用名称和关键参数(如邮箱地址、邮件主题等),因为这些是后续操作的重要上下文。' },
445
+ { role: 'user', content: prompt },
446
+ ],
447
+ temperature: 0.3,
448
+ maxTokens: 1000,
449
+ });
450
+ const rawContent = response.choices?.[0]?.message?.content;
451
+ const summary = typeof rawContent === 'string' ? rawContent.trim() : '';
452
+ if (summary && summary.length > 0) {
453
+ return summary.length > 800 ? summary.slice(0, 800) + '...' : summary;
454
+ }
455
+ }
456
+ catch (e) {
457
+ console.warn('[Engine] summarizeHistory failed, falling back to concatenation:', e?.message || e);
458
+ }
459
+ // 回退:简单拼接,截断到 800 字
460
+ const fallback = textHistory.join('\n');
461
+ return fallback.length > 800 ? fallback.slice(0, 800) + '...' : fallback;
283
462
  }
284
463
  // ---- 工具定义 ----
285
464
  /** 获取工具定义列表(含预过滤 + 渐进式发现) */
@@ -345,7 +524,7 @@ class AgentEngine {
345
524
  console.log(`[Engine] session=${options.sessionId} tools(${toolDefs.length}): ${toolDefs.map(t => t.function.name).join(', ') || '(none)'}`);
346
525
  return {
347
526
  sessionId: options.sessionId,
348
- messages: this.buildInitialMessages(options, skillContent, actualMessage, skillMatched),
527
+ messages: await this.buildInitialMessages(options, skillContent, actualMessage, skillMatched),
349
528
  toolDefs,
350
529
  toolContext: this.buildToolContext(options, internalAbort),
351
530
  internalAbort,
@@ -366,7 +545,6 @@ class AgentEngine {
366
545
  async *agentLoop(state) {
367
546
  while (state.iteration < this.config.maxIterations) {
368
547
  state.iteration++;
369
- state.lastIterationHadToolCalls = false; // 每轮迭代重置,工具执行后才置 true
370
548
  // 中断检查
371
549
  if (state.internalAbort.signal.aborted) {
372
550
  yield this.emitDone(state);
@@ -425,13 +603,29 @@ class AgentEngine {
425
603
  state.totalUsage.totalTokens += usage.totalTokens || 0;
426
604
  }
427
605
  if (!assistant) {
428
- // LLM 返回 null(上下文过长/错误)后,如果上一轮有工具调用,注入提示重试
429
- if (state.lastIterationHadToolCalls && state.continuationNudges < 2) {
430
- state.continuationNudges++;
431
- console.log(`[Engine] LLM returned null after tool calls, injecting continuation nudge #${state.continuationNudges} at iteration ${state.iteration}`);
432
- this.injectContinuationNudge(state);
606
+ // 所有 LLM provider 均已失败(callWithRetry 内部已重试+降级),
607
+ // 不再发 done 事件(callWithRetry 已发出带详细错误信息的 error 事件),
608
+ // 仅完成清理,避免 done 事件覆盖前端已展示的错误消息
609
+ console.warn(`[Engine] All LLM providers exhausted at iteration ${state.iteration}, stopping agent loop`);
610
+ this.statusMap.set(state.sessionId, 'idle');
611
+ // 记录 Token 用量到 UsageTracker
612
+ if (this.config.usageTracker && state.totalUsage) {
613
+ const modelInfo = this.config.llmProvider.getModel();
614
+ this.config.usageTracker.record({
615
+ sessionId: state.sessionId,
616
+ provider: modelInfo.provider,
617
+ model: modelInfo.model,
618
+ usage: state.totalUsage,
619
+ iterations: state.iteration,
620
+ });
433
621
  }
434
- continue;
622
+ this.runHooks('on_complete', {
623
+ sessionId: state.sessionId,
624
+ iteration: state.iteration,
625
+ totalIterations: state.iteration,
626
+ usage: state.totalUsage,
627
+ }).catch(() => { });
628
+ return;
435
629
  }
436
630
  // Hook: after_ll_call
437
631
  await this.runHooks('after_ll_call', {
@@ -457,6 +651,7 @@ class AgentEngine {
457
651
  if (!assistant.tool_calls?.length) {
458
652
  if (state.lastIterationHadToolCalls && state.continuationNudges < 2) {
459
653
  state.continuationNudges++;
654
+ state.lastIterationHadToolCalls = false; // 已消费标记,避免重复注入
460
655
  console.log(`[Engine] LLM stopped after tool calls without new tool_calls, injecting continuation nudge #${state.continuationNudges} at iteration ${state.iteration}`);
461
656
  this.injectContinuationNudge(state);
462
657
  // 不退出,继续下一轮迭代让 LLM 继续执行任务