autosnippet 3.2.7 → 3.2.9
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/bin/cli.js +13 -5
- package/dashboard/dist/assets/index-BTAsOZv2.js +128 -0
- package/dashboard/dist/assets/index-C_72Ct98.css +1 -0
- package/dashboard/dist/index.html +2 -2
- package/lib/cli/AiScanService.js +26 -29
- package/lib/cli/SetupService.js +1 -1
- package/lib/core/AstAnalyzer.js +27 -5
- package/lib/core/analysis/CallEdgeResolver.js +402 -0
- package/lib/core/analysis/CallGraphAnalyzer.js +367 -0
- package/lib/core/analysis/CallSiteExtractor.js +629 -0
- package/lib/core/analysis/DataFlowInferrer.js +57 -0
- package/lib/core/analysis/ImportPathResolver.js +189 -0
- package/lib/core/analysis/ImportRecord.js +105 -0
- package/lib/core/analysis/SymbolTableBuilder.js +211 -0
- package/lib/core/ast/ProjectGraph.js +8 -0
- package/lib/core/ast/lang-dart.js +352 -5
- package/lib/core/ast/lang-go.js +212 -10
- package/lib/core/ast/lang-java.js +205 -1
- package/lib/core/ast/lang-kotlin.js +330 -1
- package/lib/core/ast/lang-python.js +31 -2
- package/lib/core/ast/lang-rust.js +284 -3
- package/lib/core/ast/lang-swift.js +180 -1
- package/lib/core/ast/lang-typescript.js +290 -1
- package/lib/core/discovery/index.js +2 -2
- package/lib/external/ai/AiProvider.js +66 -172
- package/lib/external/ai/providers/GoogleGeminiProvider.js +23 -1
- package/lib/external/mcp/McpServer.js +1 -0
- package/lib/external/mcp/handlers/bootstrap/BootstrapSession.js +1 -1
- package/lib/external/mcp/handlers/bootstrap/ExternalSubmissionTracker.js +3 -3
- package/lib/external/mcp/handlers/bootstrap/MissionBriefingBuilder.js +22 -1
- package/lib/external/mcp/handlers/bootstrap/pipeline/IncrementalBootstrap.js +1 -1
- package/lib/external/mcp/handlers/bootstrap/pipeline/dimension-configs.js +2 -1
- package/lib/external/mcp/handlers/bootstrap/pipeline/dimension-context.js +8 -8
- package/lib/external/mcp/handlers/bootstrap/pipeline/noAiFallback.js +1 -1
- package/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +311 -162
- package/lib/external/mcp/handlers/bootstrap/shared/bootstrap-phases.js +102 -7
- package/lib/external/mcp/handlers/bootstrap/shared/dimension-sop.js +1 -1
- package/lib/external/mcp/handlers/bootstrap-external.js +9 -2
- package/lib/external/mcp/handlers/bootstrap-internal.js +19 -8
- package/lib/external/mcp/handlers/consolidated.js +9 -0
- package/lib/external/mcp/handlers/dimension-complete-external.js +6 -6
- package/lib/external/mcp/handlers/guard.js +3 -3
- package/lib/external/mcp/handlers/structure.js +62 -0
- package/lib/external/mcp/handlers/wiki-external.js +66 -3
- package/lib/external/mcp/tools.js +36 -1
- package/lib/http/HttpServer.js +1 -1
- package/lib/http/middleware/requestLogger.js +1 -0
- package/lib/http/routes/ai.js +240 -35
- package/lib/http/routes/candidates.js +2 -3
- package/lib/http/routes/extract.js +13 -11
- package/lib/http/routes/modules.js +2 -2
- package/lib/http/routes/recipes.js +9 -5
- package/lib/http/routes/remote.js +149 -270
- package/lib/http/routes/violations.js +0 -54
- package/lib/http/utils/sse-sessions.js +1 -1
- package/lib/infrastructure/logging/Logger.js +5 -4
- package/lib/infrastructure/monitoring/PerformanceMonitor.js +3 -2
- package/lib/injection/ServiceContainer.js +70 -28
- package/lib/platform/ScreenCaptureService.js +177 -0
- package/lib/platform/ios/index.js +2 -2
- package/lib/platform/ios/routes/spm.js +2 -2
- package/lib/platform/ios/spm/PackageSwiftParser.js +14 -3
- package/lib/platform/ios/spm/SpmDiscoverer.js +123 -17
- package/lib/platform/ios/spm/{SpmService.js → SpmHelper.js} +43 -675
- package/lib/platform/ios/xcode/XcodeWriteUtils.js +1 -1
- package/lib/service/agent/AgentEventBus.js +207 -0
- package/lib/service/agent/AgentFactory.js +490 -0
- package/lib/service/agent/AgentMessage.js +240 -0
- package/lib/service/agent/AgentRouter.js +228 -0
- package/lib/service/agent/AgentRuntime.js +1016 -0
- package/lib/service/agent/AgentState.js +217 -0
- package/lib/service/agent/IntentClassifier.js +331 -0
- package/lib/service/agent/LarkTransport.js +389 -0
- package/lib/service/agent/capabilities.js +408 -0
- package/lib/service/{chat → agent/context}/ContextWindow.js +37 -12
- package/lib/service/{chat → agent/context}/ExplorationTracker.js +77 -22
- package/lib/service/{chat → agent/core}/ChatAgentPrompts.js +14 -2
- package/lib/service/agent/core/LoopContext.js +170 -0
- package/lib/service/agent/core/MessageAdapter.js +223 -0
- package/lib/service/agent/core/ToolExecutionPipeline.js +376 -0
- package/lib/service/{chat → agent/domain}/ChatAgentTasks.js +19 -98
- package/lib/service/{chat → agent/domain}/EpisodicConsolidator.js +7 -7
- package/lib/service/{chat → agent/domain}/EvidenceCollector.js +4 -2
- package/lib/service/{chat/AnalystAgent.js → agent/domain/insight-analyst.js} +37 -172
- package/lib/service/{chat/HandoffProtocol.js → agent/domain/insight-gate.js} +91 -123
- package/lib/service/agent/domain/insight-producer.js +267 -0
- package/lib/service/agent/domain/scan-prompts.js +105 -0
- package/lib/service/agent/forced-summary.js +266 -0
- package/lib/service/agent/index.js +91 -0
- package/lib/service/{chat → agent}/memory/ActiveContext.js +3 -1
- package/lib/service/{chat → agent}/memory/MemoryCoordinator.js +7 -7
- package/lib/service/{chat/ProjectSemanticMemory.js → agent/memory/PersistentMemory.js} +359 -89
- package/lib/service/{chat → agent}/memory/SessionStore.js +5 -4
- package/lib/service/{chat → agent}/memory/index.js +1 -1
- package/lib/service/agent/policies.js +442 -0
- package/lib/service/agent/presets.js +303 -0
- package/lib/service/agent/strategies.js +717 -0
- package/lib/service/{chat → agent/tools}/ToolRegistry.js +3 -3
- package/lib/service/agent/tools/ai-analysis.js +75 -0
- package/lib/service/{chat → agent}/tools/ast-graph.js +229 -32
- package/lib/service/{chat → agent}/tools/composite.js +2 -1
- package/lib/service/{chat → agent}/tools/guard.js +1 -121
- package/lib/service/{chat → agent}/tools/index.js +33 -22
- package/lib/service/{chat → agent}/tools/infrastructure.js +6 -1
- package/lib/service/agent/tools/knowledge-graph.js +112 -0
- package/lib/service/agent/tools/scan-recipe.js +189 -0
- package/lib/service/agent/tools/system-interaction.js +476 -0
- package/lib/service/automation/DirectiveDetector.js +0 -1
- package/lib/service/automation/FileWatcher.js +0 -8
- package/lib/service/automation/handlers/CreateHandler.js +7 -3
- package/lib/service/automation/handlers/DraftHandler.js +7 -6
- package/lib/service/cursor/CursorDeliveryPipeline.js +167 -1
- package/lib/service/knowledge/CodeEntityGraph.js +327 -2
- package/lib/service/knowledge/KnowledgeService.js +5 -1
- package/lib/service/module/ModuleService.js +49 -73
- package/lib/service/skills/SignalCollector.js +26 -19
- package/lib/service/snippet/codecs/VSCodeCodec.js +1 -1
- package/lib/service/wiki/WikiGenerator.js +1 -1
- package/lib/shared/FieldSpec.js +1 -1
- package/lib/shared/PathGuard.js +1 -1
- package/lib/shared/StyleGuide.js +1 -1
- package/package.json +4 -1
- package/resources/native-ui/screenshot.swift +228 -0
- package/dashboard/dist/assets/index-BaGY7kJI.css +0 -1
- package/dashboard/dist/assets/index-DfHY_3ln.js +0 -128
- package/lib/core/discovery/SpmDiscoverer.js +0 -5
- package/lib/external/mcp/handlers/bootstrap/pipeline/EpisodicMemory.js +0 -749
- package/lib/external/mcp/handlers/bootstrap/pipeline/ToolResultCache.js +0 -277
- package/lib/http/routes/spm.js +0 -5
- package/lib/infrastructure/external/XcodeAutomation.js +0 -15
- package/lib/service/chat/ChatAgent.js +0 -1602
- package/lib/service/chat/Memory.js +0 -161
- package/lib/service/chat/ProducerAgent.js +0 -431
- package/lib/service/chat/ReasoningTrace.js +0 -523
- package/lib/service/chat/TaskPipeline.js +0 -357
- package/lib/service/chat/WorkingMemory.js +0 -357
- package/lib/service/chat/memory/PersistentMemory.js +0 -450
- package/lib/service/chat/tools/ai-analysis.js +0 -267
- package/lib/service/chat/tools/knowledge-graph.js +0 -234
- package/lib/service/chat/tools.js +0 -18
- package/lib/service/snippet/PlaceholderConverter.js +0 -5
- package/lib/service/snippet/codecs/XcodeCodec.js +0 -5
- /package/lib/service/{chat → agent}/ConversationStore.js +0 -0
- /package/lib/service/{chat → agent}/tools/_shared.js +0 -0
- /package/lib/service/{chat → agent}/tools/lifecycle.js +0 -0
- /package/lib/service/{chat → agent}/tools/project-access.js +0 -0
- /package/lib/service/{chat → agent}/tools/query.js +0 -0
|
@@ -0,0 +1,389 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LarkTransport — 飞书消息传输层
|
|
3
|
+
*
|
|
4
|
+
* 职责: 将飞书 SDK 的原始消息格式转换为统一 AgentMessage,
|
|
5
|
+
* 并把 Agent 的回复写回飞书。
|
|
6
|
+
*
|
|
7
|
+
* 架构位置:
|
|
8
|
+
* 飞书 WS Event → LarkTransport.receive(rawEvent)
|
|
9
|
+
* → 解析文本/附件 → AgentMessage.fromLark(...)
|
|
10
|
+
* → IntentClassifier.classify(text)
|
|
11
|
+
* → 路由到 Bot Agent (服务端) 或 IDE Agent (VSCode)
|
|
12
|
+
* → 回复通过 replyFn/sendFn 写回飞书
|
|
13
|
+
*
|
|
14
|
+
* 与 remote.js 的关系:
|
|
15
|
+
* remote.js 仍然管理飞书 WS 连接和 HTTP 端点,
|
|
16
|
+
* LarkTransport 处理消息语义层 (NL 理解、Agent 路由、回复格式化)。
|
|
17
|
+
*
|
|
18
|
+
* @module LarkTransport
|
|
19
|
+
*/
|
|
20
|
+
|
|
21
|
+
import Logger from '../../infrastructure/logging/Logger.js';
|
|
22
|
+
import { AgentMessage, Channel } from './AgentMessage.js';
|
|
23
|
+
import { IntentClassifier, Intent } from './IntentClassifier.js';
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* @typedef {Object} LarkTransportConfig
|
|
27
|
+
* @property {import('./AgentFactory.js').AgentFactory} agentFactory — Agent 工厂
|
|
28
|
+
* @property {Function} replyFn — (messageId, text) => Promise<void>
|
|
29
|
+
* @property {Function} sendFn — (text) => Promise<void> (主动发送到活跃会话)
|
|
30
|
+
* @property {Function} [sendImageFn] — (caption?) => Promise<{success, message}>
|
|
31
|
+
* @property {Function} [getStatusFn] — () => Promise<string> 获取系统状态
|
|
32
|
+
* @property {Function} [enqueueIdeFn] — (command, meta) => Promise<{id}> 写入 IDE 队列
|
|
33
|
+
* @property {Function} [isUserAllowed] — (userId) => boolean 鉴权
|
|
34
|
+
* @property {import('../../external/ai/AiProvider.js').AiProvider} [aiProvider]
|
|
35
|
+
*/
|
|
36
|
+
|
|
37
|
+
export class LarkTransport {
|
|
38
|
+
#agentFactory;
|
|
39
|
+
#classifier;
|
|
40
|
+
#logger;
|
|
41
|
+
#replyFn;
|
|
42
|
+
#sendFn;
|
|
43
|
+
#sendImageFn;
|
|
44
|
+
#getStatusFn;
|
|
45
|
+
#enqueueIdeFn;
|
|
46
|
+
#isUserAllowed;
|
|
47
|
+
|
|
48
|
+
/** @type {Map<string, Array<{role: string, content: string}>>} chatId → 最近对话 */
|
|
49
|
+
#conversationHistory = new Map();
|
|
50
|
+
/** 对话历史最大轮数 */
|
|
51
|
+
static MAX_HISTORY = 20;
|
|
52
|
+
|
|
53
|
+
/** @type {Map<string, number>} messageId → timestamp, 消息去重 */
|
|
54
|
+
#recentMsgIds = new Map();
|
|
55
|
+
/** 去重 TTL (5 分钟) */
|
|
56
|
+
static DEDUP_TTL = 5 * 60 * 1000;
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* @param {LarkTransportConfig} config
|
|
60
|
+
*/
|
|
61
|
+
constructor(config) {
|
|
62
|
+
this.#agentFactory = config.agentFactory;
|
|
63
|
+
this.#replyFn = config.replyFn;
|
|
64
|
+
this.#sendFn = config.sendFn;
|
|
65
|
+
this.#sendImageFn = config.sendImageFn || null;
|
|
66
|
+
this.#getStatusFn = config.getStatusFn || null;
|
|
67
|
+
this.#enqueueIdeFn = config.enqueueIdeFn || null;
|
|
68
|
+
this.#isUserAllowed = config.isUserAllowed || (() => true);
|
|
69
|
+
this.#logger = Logger.getInstance();
|
|
70
|
+
|
|
71
|
+
this.#classifier = new IntentClassifier({
|
|
72
|
+
aiProvider: config.aiProvider || null,
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* 接收原始飞书消息事件
|
|
78
|
+
*
|
|
79
|
+
* 这是唯一入口 — 替代了 remote.js 中的 handleLarkMessage()
|
|
80
|
+
*
|
|
81
|
+
* @param {Object} data — 飞书 im.message.receive_v1 事件数据
|
|
82
|
+
* @returns {Promise<void>}
|
|
83
|
+
*/
|
|
84
|
+
async receive(data) {
|
|
85
|
+
const message = data?.message || data?.event?.message || {};
|
|
86
|
+
const sender = data?.sender || data?.event?.sender || {};
|
|
87
|
+
const messageId = message.message_id;
|
|
88
|
+
const chatId = message.chat_id;
|
|
89
|
+
const msgType = message.message_type;
|
|
90
|
+
|
|
91
|
+
// ── 消息去重 (defense-in-depth, remote.js 也有外层去重) ──
|
|
92
|
+
if (messageId && this.#recentMsgIds.has(messageId)) {
|
|
93
|
+
this.#logger.debug(`[LarkTransport] Dedup: ${messageId}`);
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (messageId) {
|
|
97
|
+
this.#recentMsgIds.set(messageId, Date.now());
|
|
98
|
+
// 清理过期条目
|
|
99
|
+
if (this.#recentMsgIds.size > 200) {
|
|
100
|
+
const now = Date.now();
|
|
101
|
+
for (const [id, ts] of this.#recentMsgIds) {
|
|
102
|
+
if (now - ts > LarkTransport.DEDUP_TTL) this.#recentMsgIds.delete(id);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
// ── 鉴权 ──
|
|
108
|
+
const senderId = sender.sender_id?.user_id || sender.sender_id?.open_id || '';
|
|
109
|
+
const senderName = sender.sender_id?.user_id || 'lark_user';
|
|
110
|
+
|
|
111
|
+
if (!this.#isUserAllowed(senderId)) {
|
|
112
|
+
this.#logger.warn(`[LarkTransport] Blocked: ${senderId}`);
|
|
113
|
+
await this.#reply(messageId, '🔒 权限不足。');
|
|
114
|
+
return;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
// ── 非文本提示 ──
|
|
118
|
+
if (msgType !== 'text') {
|
|
119
|
+
await this.#reply(messageId, '💬 请发送文字消息,我理解自然语言。');
|
|
120
|
+
return;
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
// ── 解析文本 ──
|
|
124
|
+
let text = '';
|
|
125
|
+
try {
|
|
126
|
+
const content = JSON.parse(message.content || '{}');
|
|
127
|
+
text = (content.text || '').trim();
|
|
128
|
+
} catch {
|
|
129
|
+
text = '';
|
|
130
|
+
}
|
|
131
|
+
text = text.replace(/@_user_\d+/g, '').trim();
|
|
132
|
+
if (!text) return;
|
|
133
|
+
|
|
134
|
+
this.#logger.info(`[LarkTransport] Received: "${text.slice(0, 80)}" from ${senderName}`);
|
|
135
|
+
|
|
136
|
+
// ── 意图分类 ──
|
|
137
|
+
const recentHistory = this.#getRecentHistoryText(chatId);
|
|
138
|
+
const classification = await this.#classifier.classify(text, { recentHistory });
|
|
139
|
+
|
|
140
|
+
this.#logger.info(
|
|
141
|
+
`[LarkTransport] Intent: ${classification.intent} (${classification.confidence.toFixed(2)}) — ${classification.reasoning}`
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
// ── 路由处理 ──
|
|
145
|
+
switch (classification.intent) {
|
|
146
|
+
case Intent.SYSTEM:
|
|
147
|
+
await this.#handleSystem(classification.action, messageId, text);
|
|
148
|
+
break;
|
|
149
|
+
|
|
150
|
+
case Intent.IDE_AGENT:
|
|
151
|
+
await this.#handleIdeAgent(text, messageId, chatId, senderId, senderName);
|
|
152
|
+
break;
|
|
153
|
+
|
|
154
|
+
case Intent.BOT_AGENT:
|
|
155
|
+
default:
|
|
156
|
+
await this.#handleBotAgent(text, messageId, chatId, senderId, senderName);
|
|
157
|
+
break;
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
// ═══════════════════════════════════════════════════
|
|
162
|
+
// 意图处理器
|
|
163
|
+
// ═══════════════════════════════════════════════════
|
|
164
|
+
|
|
165
|
+
/**
|
|
166
|
+
* 系统操作 — 直接处理,不走 Agent
|
|
167
|
+
*/
|
|
168
|
+
async #handleSystem(action, messageId, _text) {
|
|
169
|
+
switch (action) {
|
|
170
|
+
case 'status':
|
|
171
|
+
if (this.#getStatusFn) {
|
|
172
|
+
const status = await this.#getStatusFn();
|
|
173
|
+
await this.#reply(messageId, status);
|
|
174
|
+
} else {
|
|
175
|
+
await this.#reply(messageId, '📊 状态查询暂不可用');
|
|
176
|
+
}
|
|
177
|
+
break;
|
|
178
|
+
|
|
179
|
+
case 'screen':
|
|
180
|
+
if (this.#sendImageFn) {
|
|
181
|
+
await this.#reply(messageId, '📸 正在截取 IDE 画面...');
|
|
182
|
+
const result = await this.#sendImageFn('');
|
|
183
|
+
if (!result.success) {
|
|
184
|
+
await this.#reply(messageId, `❌ 截图失败: ${result.message}`);
|
|
185
|
+
}
|
|
186
|
+
} else {
|
|
187
|
+
await this.#reply(messageId, '📸 截图功能未配置');
|
|
188
|
+
}
|
|
189
|
+
break;
|
|
190
|
+
|
|
191
|
+
case 'help':
|
|
192
|
+
await this.#reply(messageId, [
|
|
193
|
+
'🤖 AutoSnippet 智能助手',
|
|
194
|
+
'',
|
|
195
|
+
'直接用自然语言和我对话即可:',
|
|
196
|
+
'',
|
|
197
|
+
'📚 知识管理 (我来处理):',
|
|
198
|
+
' "搜索项目里关于认证的知识"',
|
|
199
|
+
' "解释一下这个项目的架构"',
|
|
200
|
+
' "帮我创建一个关于缓存策略的知识"',
|
|
201
|
+
' "翻译这段代码注释"',
|
|
202
|
+
'',
|
|
203
|
+
'💻 代码编程 (转发到 IDE):',
|
|
204
|
+
' "修改 src/auth.ts 的 JWT 验证"',
|
|
205
|
+
' "写一个新的 React 组件"',
|
|
206
|
+
' "修复这个 TypeScript 报错"',
|
|
207
|
+
' "运行一下测试"',
|
|
208
|
+
'',
|
|
209
|
+
'🔧 系统操作:',
|
|
210
|
+
' "查看状态" — 连接诊断',
|
|
211
|
+
' "截图" — 截取 IDE 画面',
|
|
212
|
+
' "帮助" — 显示此信息',
|
|
213
|
+
'',
|
|
214
|
+
'💡 我会自动判断你的意图类型。',
|
|
215
|
+
' 知识类任务我直接处理,编程类任务转发到 VSCode。',
|
|
216
|
+
].join('\n'));
|
|
217
|
+
break;
|
|
218
|
+
|
|
219
|
+
case 'queue':
|
|
220
|
+
await this.#reply(messageId, '📋 请说"查看队列状态"获取更多信息。');
|
|
221
|
+
break;
|
|
222
|
+
|
|
223
|
+
case 'cancel':
|
|
224
|
+
await this.#reply(messageId, '🗑 取消操作已发送。');
|
|
225
|
+
break;
|
|
226
|
+
|
|
227
|
+
case 'clear':
|
|
228
|
+
await this.#reply(messageId, '🧹 清理操作已发送。');
|
|
229
|
+
break;
|
|
230
|
+
|
|
231
|
+
case 'ping':
|
|
232
|
+
await this.#reply(messageId, `🏓 pong! (${new Date().toLocaleTimeString('zh-CN')})`);
|
|
233
|
+
break;
|
|
234
|
+
|
|
235
|
+
default:
|
|
236
|
+
await this.#reply(messageId, '❓ 未识别的系统操作。');
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
/**
|
|
241
|
+
* IDE 编程任务 — 转发到 VSCode Copilot 执行
|
|
242
|
+
*/
|
|
243
|
+
async #handleIdeAgent(text, messageId, chatId, senderId, senderName) {
|
|
244
|
+
if (!this.#enqueueIdeFn) {
|
|
245
|
+
await this.#reply(messageId, '❌ IDE 桥接未配置,无法转发编程任务。');
|
|
246
|
+
return;
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
try {
|
|
250
|
+
const result = await this.#enqueueIdeFn(text, {
|
|
251
|
+
chatId, messageId, senderId, senderName,
|
|
252
|
+
});
|
|
253
|
+
|
|
254
|
+
// 记录到对话历史
|
|
255
|
+
this.#appendHistory(chatId, 'user', text);
|
|
256
|
+
this.#appendHistory(chatId, 'assistant', `[IDE Agent] 已转发: ${text.slice(0, 50)}`);
|
|
257
|
+
|
|
258
|
+
await this.#reply(messageId, [
|
|
259
|
+
'💻 编程任务已转发到 IDE',
|
|
260
|
+
'',
|
|
261
|
+
`> ${text.length > 80 ? text.slice(0, 80) + '...' : text}`,
|
|
262
|
+
'',
|
|
263
|
+
'Copilot Agent Mode 将自动处理。',
|
|
264
|
+
'执行结果会回传到这里。',
|
|
265
|
+
].join('\n'));
|
|
266
|
+
} catch (err) {
|
|
267
|
+
this.#logger.error(`[LarkTransport] IDE enqueue failed: ${err.message}`);
|
|
268
|
+
await this.#reply(messageId, `❌ 转发失败: ${err.message}`);
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Bot Agent 知识任务 — 服务端 AgentRuntime 直接处理
|
|
274
|
+
*/
|
|
275
|
+
async #handleBotAgent(text, messageId, chatId, senderId, senderName) {
|
|
276
|
+
// 进度提示
|
|
277
|
+
await this.#reply(messageId, '🤔 正在思考...');
|
|
278
|
+
|
|
279
|
+
try {
|
|
280
|
+
// 获取对话历史
|
|
281
|
+
const history = this.#getHistory(chatId);
|
|
282
|
+
|
|
283
|
+
// 构建 AgentMessage
|
|
284
|
+
// 注意: 不传 replyFn — AgentRuntime.execute() 会自动调用 message.reply(),
|
|
285
|
+
// 但我们需要在外层处理截断逻辑,所以由下面的 #send 手动发送最终回复。
|
|
286
|
+
const agentMessage = AgentMessage.fromLark(
|
|
287
|
+
{
|
|
288
|
+
text,
|
|
289
|
+
chatId,
|
|
290
|
+
senderId,
|
|
291
|
+
senderName,
|
|
292
|
+
messageId,
|
|
293
|
+
messageType: 'text',
|
|
294
|
+
},
|
|
295
|
+
null, // 不设 replyFn — 避免 AgentRuntime 自动回复导致重复发送
|
|
296
|
+
);
|
|
297
|
+
|
|
298
|
+
// 注入对话历史
|
|
299
|
+
agentMessage.session.history = history;
|
|
300
|
+
|
|
301
|
+
// 创建 Chat Runtime 并执行
|
|
302
|
+
const runtime = this.#agentFactory.createChat({
|
|
303
|
+
lang: 'zh',
|
|
304
|
+
onProgress: (event) => {
|
|
305
|
+
// 工具调用时发送进度
|
|
306
|
+
if (event.type === 'tool_call') {
|
|
307
|
+
this.#send(`🔧 调用工具: ${event.tool || 'unknown'}...`).catch(() => {});
|
|
308
|
+
}
|
|
309
|
+
},
|
|
310
|
+
});
|
|
311
|
+
|
|
312
|
+
const result = await runtime.execute(agentMessage);
|
|
313
|
+
|
|
314
|
+
// 提取回复
|
|
315
|
+
const reply = result?.reply || result?.text || '抱歉,没有生成有效回复。';
|
|
316
|
+
|
|
317
|
+
// 记录对话历史
|
|
318
|
+
this.#appendHistory(chatId, 'user', text);
|
|
319
|
+
this.#appendHistory(chatId, 'assistant', reply);
|
|
320
|
+
|
|
321
|
+
// 发送最终回复 (去掉之前的"正在思考",直接发新消息)
|
|
322
|
+
// 飞书回复字数限制 ~4000,需截断
|
|
323
|
+
const MAX_LEN = 3800;
|
|
324
|
+
if (reply.length > MAX_LEN) {
|
|
325
|
+
const truncated = reply.slice(0, MAX_LEN) + '\n\n... (内容过长已截断)';
|
|
326
|
+
await this.#send(truncated);
|
|
327
|
+
} else {
|
|
328
|
+
await this.#send(reply);
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
} catch (err) {
|
|
332
|
+
this.#logger.error(`[LarkTransport] Bot Agent error: ${err.message}\n${err.stack}`);
|
|
333
|
+
await this.#reply(messageId, `❌ 处理失败: ${err.message}`);
|
|
334
|
+
}
|
|
335
|
+
}
|
|
336
|
+
|
|
337
|
+
// ═══════════════════════════════════════════════════
|
|
338
|
+
// 对话历史管理
|
|
339
|
+
// ═══════════════════════════════════════════════════
|
|
340
|
+
|
|
341
|
+
/**
|
|
342
|
+
* 获取指定会话的历史
|
|
343
|
+
*/
|
|
344
|
+
#getHistory(chatId) {
|
|
345
|
+
return this.#conversationHistory.get(chatId) || [];
|
|
346
|
+
}
|
|
347
|
+
|
|
348
|
+
/**
|
|
349
|
+
* 获取最近对话的可读文本 (给 IntentClassifier 提供上下文)
|
|
350
|
+
*/
|
|
351
|
+
#getRecentHistoryText(chatId) {
|
|
352
|
+
const history = this.#getHistory(chatId);
|
|
353
|
+
if (history.length === 0) return '';
|
|
354
|
+
return history
|
|
355
|
+
.slice(-6)
|
|
356
|
+
.map(h => `${h.role}: ${h.content.slice(0, 100)}`)
|
|
357
|
+
.join('\n');
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
/**
|
|
361
|
+
* 追加对话记录
|
|
362
|
+
*/
|
|
363
|
+
#appendHistory(chatId, role, content) {
|
|
364
|
+
if (!chatId) return;
|
|
365
|
+
if (!this.#conversationHistory.has(chatId)) {
|
|
366
|
+
this.#conversationHistory.set(chatId, []);
|
|
367
|
+
}
|
|
368
|
+
const history = this.#conversationHistory.get(chatId);
|
|
369
|
+
history.push({ role, content });
|
|
370
|
+
// 限制历史长度
|
|
371
|
+
if (history.length > LarkTransport.MAX_HISTORY * 2) {
|
|
372
|
+
history.splice(0, history.length - LarkTransport.MAX_HISTORY * 2);
|
|
373
|
+
}
|
|
374
|
+
}
|
|
375
|
+
|
|
376
|
+
// ═══════════════════════════════════════════════════
|
|
377
|
+
// 飞书消息发送
|
|
378
|
+
// ═══════════════════════════════════════════════════
|
|
379
|
+
|
|
380
|
+
async #reply(messageId, text) {
|
|
381
|
+
if (this.#replyFn) await this.#replyFn(messageId, text);
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
async #send(text) {
|
|
385
|
+
if (this.#sendFn) await this.#sendFn(text);
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
|
|
389
|
+
export default LarkTransport;
|