autosnippet 3.2.8 → 3.2.10
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 +6 -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 +23 -26
- package/lib/cli/SetupService.js +1 -1
- package/lib/cli/deploy/FileManifest.js +1 -1
- package/lib/core/AstAnalyzer.js +1 -1
- package/lib/core/discovery/index.js +2 -2
- package/lib/external/ai/AiProvider.js +66 -172
- package/lib/external/ai/providers/GoogleGeminiProvider.js +29 -5
- 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 +1 -1
- package/lib/external/mcp/handlers/bootstrap/pipeline/IncrementalBootstrap.js +1 -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 +291 -204
- package/lib/external/mcp/handlers/bootstrap/shared/bootstrap-phases.js +7 -6
- package/lib/external/mcp/handlers/bootstrap/shared/dimension-sop.js +1 -1
- package/lib/external/mcp/handlers/bootstrap-internal.js +2 -2
- package/lib/external/mcp/handlers/dimension-complete-external.js +6 -6
- 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 +5 -5
- package/lib/http/routes/remote.js +134 -255
- 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 +64 -17
- package/lib/platform/ScreenCaptureService.js +177 -0
- package/lib/platform/ios/routes/spm.js +2 -2
- package/lib/service/agent/AgentEventBus.js +207 -0
- package/lib/service/agent/AgentFactory.js +535 -0
- package/lib/service/agent/AgentMessage.js +240 -0
- package/lib/service/agent/AgentRouter.js +228 -0
- package/lib/service/agent/AgentRuntime.js +1056 -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 +409 -0
- package/lib/service/{chat → agent/context}/ContextWindow.js +37 -12
- package/lib/service/{chat → agent/context}/ExplorationTracker.js +112 -33
- package/lib/service/{chat → agent/core}/ChatAgentPrompts.js +5 -3
- 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 +15 -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} +85 -135
- package/lib/service/agent/domain/insight-producer.js +270 -0
- package/lib/service/agent/domain/scan-prompts.js +444 -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 +29 -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 +1 -1
- package/lib/service/{chat → agent}/memory/index.js +1 -1
- package/lib/service/agent/policies.js +442 -0
- package/lib/service/agent/presets.js +305 -0
- package/lib/service/agent/strategies.js +756 -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/composite.js +2 -1
- package/lib/service/{chat → agent}/tools/guard.js +1 -121
- package/lib/service/{chat → agent}/tools/index.js +27 -21
- package/lib/service/{chat → agent}/tools/infrastructure.js +1 -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/module/ModuleService.js +40 -73
- package/lib/service/skills/SignalCollector.js +26 -19
- package/lib/service/snippet/codecs/VSCodeCodec.js +1 -1
- package/lib/shared/FieldSpec.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-D5jiDBQG.css +0 -1
- package/dashboard/dist/assets/index-e5OKj-Ni.js +0 -128
- package/lib/core/discovery/SpmDiscoverer.js +0 -5
- package/lib/external/mcp/handlers/bootstrap/pipeline/EpisodicMemory.js +0 -750
- 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 -359
- 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/ast-graph.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
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
*
|
|
4
4
|
* 合并了三个原本各自为政的系统:
|
|
5
5
|
* 1. PhaseRouter (ContextWindow.js) — 阶段状态机
|
|
6
|
-
* 2. 探索进度追踪 (
|
|
6
|
+
* 2. 探索进度追踪 (原内联逻辑) — 信息增量检测
|
|
7
7
|
* 3. ReasoningLayer 行为控制部分 — 反思/规划/停滞 nudge
|
|
8
8
|
*
|
|
9
9
|
* 职责:
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
* @module ExplorationTracker
|
|
22
22
|
*/
|
|
23
23
|
|
|
24
|
-
import Logger from '
|
|
24
|
+
import Logger from '../../../infrastructure/logging/Logger.js';
|
|
25
25
|
|
|
26
26
|
// ─── 常量 ──────────────────────────────────────────────
|
|
27
27
|
|
|
@@ -39,11 +39,13 @@ const DEFAULT_DEVIATION_THRESHOLD = 0.6;
|
|
|
39
39
|
const DEFAULT_MIN_EXPLORE_ITERS = 10;
|
|
40
40
|
/** 默认停滞收敛阈值 */
|
|
41
41
|
const DEFAULT_CONVERGENCE_STALE_THRESHOLD = 3;
|
|
42
|
+
/** 最少经过 N 轮后才允许再次触发 replan(防止 replan 风暴) */
|
|
43
|
+
const MIN_REPLAN_GAP = 3;
|
|
42
44
|
|
|
43
45
|
// ─── 内置策略 ────────────────────────────────────────────
|
|
44
46
|
|
|
45
47
|
/**
|
|
46
|
-
* Bootstrap
|
|
48
|
+
* Bootstrap 策略(有 submit 阶段)
|
|
47
49
|
* @param {boolean} isSkillOnly — skill-only 维度跳过 PRODUCE 阶段
|
|
48
50
|
* @returns {object} 策略配置
|
|
49
51
|
*/
|
|
@@ -227,6 +229,8 @@ export class ExplorationTracker {
|
|
|
227
229
|
|
|
228
230
|
/** @type {boolean} tick 是否已调用(用于 rollback) */
|
|
229
231
|
#ticked = false;
|
|
232
|
+
/** @type {string} 提交工具名(用于 nudge 文本生成) */
|
|
233
|
+
#submitToolName = 'submit_knowledge';
|
|
230
234
|
|
|
231
235
|
/**
|
|
232
236
|
* @param {object} strategy — 策略配置对象
|
|
@@ -234,7 +238,20 @@ export class ExplorationTracker {
|
|
|
234
238
|
*/
|
|
235
239
|
constructor(strategy, budget) {
|
|
236
240
|
this.#strategy = strategy;
|
|
237
|
-
|
|
241
|
+
// 合并默认值: PipelineStrategy 创建 per-stage tracker 时,
|
|
242
|
+
// budget 可能仅含 maxIterations/temperature (来自 stage.budget),
|
|
243
|
+
// 需要补全 tracker 所需的 searchBudget、maxSubmits 等字段。
|
|
244
|
+
this.#budget = {
|
|
245
|
+
maxIterations: 24,
|
|
246
|
+
searchBudget: 18,
|
|
247
|
+
searchBudgetGrace: 10,
|
|
248
|
+
maxSubmits: 10,
|
|
249
|
+
softSubmitLimit: 8,
|
|
250
|
+
idleRoundsToExit: 3,
|
|
251
|
+
...budget,
|
|
252
|
+
};
|
|
253
|
+
/** @type {string} 提交工具名 — bootstrap 用 submit_knowledge,scan 用 collect_scan_recipe */
|
|
254
|
+
this.#submitToolName = budget.submitToolName || 'submit_knowledge';
|
|
238
255
|
this.#phase = strategy.phases[0];
|
|
239
256
|
this.#logger = Logger.getInstance();
|
|
240
257
|
}
|
|
@@ -243,7 +260,7 @@ export class ExplorationTracker {
|
|
|
243
260
|
|
|
244
261
|
/**
|
|
245
262
|
* 根据调用参数解析应使用的策略
|
|
246
|
-
* @param {object} opts —
|
|
263
|
+
* @param {object} opts — AgentRuntime execute 的选项
|
|
247
264
|
* @param {object} budget — 预算配置
|
|
248
265
|
* @returns {ExplorationTracker|null} — User 模式返回 null
|
|
249
266
|
*/
|
|
@@ -295,11 +312,20 @@ export class ExplorationTracker {
|
|
|
295
312
|
}
|
|
296
313
|
}
|
|
297
314
|
|
|
315
|
+
/** 提交工具名 (供外部判断是否为 scan / bootstrap pipeline) */
|
|
316
|
+
get submitToolName() {
|
|
317
|
+
return this.#submitToolName;
|
|
318
|
+
}
|
|
319
|
+
|
|
298
320
|
/**
|
|
299
321
|
* 是否应退出主循环
|
|
300
322
|
* @returns {boolean}
|
|
301
323
|
*/
|
|
302
324
|
shouldExit() {
|
|
325
|
+
// Scan pipeline: SUMMARIZE 无消费方,recipes 已通过工具调用收集,直接退出
|
|
326
|
+
if (this.#isTerminalPhase() && this.#submitToolName === 'collect_scan_recipe') {
|
|
327
|
+
return true;
|
|
328
|
+
}
|
|
303
329
|
// 终结阶段 + 已给了 2 轮 grace → 退出
|
|
304
330
|
if (this.#isTerminalPhase() && this.#metrics.phaseRounds >= 2) {
|
|
305
331
|
return true;
|
|
@@ -336,7 +362,7 @@ export class ExplorationTracker {
|
|
|
336
362
|
* 4. reflection — 周期反思 / 停滞反思
|
|
337
363
|
* 5. planning — 首轮规划 / 偏差重规划
|
|
338
364
|
*
|
|
339
|
-
* @param {import('
|
|
365
|
+
* @param {import('../memory/ActiveContext.js').ActiveContext} trace — 推理链(供反思用)
|
|
340
366
|
* @returns {{ type: string, text: string }|null}
|
|
341
367
|
*/
|
|
342
368
|
getNudge(trace) {
|
|
@@ -346,7 +372,7 @@ export class ExplorationTracker {
|
|
|
346
372
|
// 1. 强制退出
|
|
347
373
|
if (this.#gracefulExitRound != null && m.iteration === this.#gracefulExitRound) {
|
|
348
374
|
const submitCount = m.submitCount;
|
|
349
|
-
// v5.1: Analyst
|
|
375
|
+
// v5.1: Analyst 策略使用纯文本输出
|
|
350
376
|
if (this.#strategy.name === 'analyst') {
|
|
351
377
|
return {
|
|
352
378
|
type: 'force_exit',
|
|
@@ -360,6 +386,17 @@ export class ExplorationTracker {
|
|
|
360
386
|
`⛔ 严禁在回复中复制或引用本条指令的任何文字,只输出你自己的分析。`,
|
|
361
387
|
};
|
|
362
388
|
}
|
|
389
|
+
// Scan pipeline (collect_scan_recipe): 使用纯文本总结,不需要 dimensionDigest
|
|
390
|
+
if (this.#submitToolName === 'collect_scan_recipe') {
|
|
391
|
+
return {
|
|
392
|
+
type: 'force_exit',
|
|
393
|
+
text: `⚠️ **轮次耗尽** (${m.iteration}/${b.maxIterations})。你必须**立即停止工具调用**。\n\n` +
|
|
394
|
+
`已通过 collect_scan_recipe 提交了 ${submitCount} 个知识候选。` +
|
|
395
|
+
`请直接输出你的分析总结(Markdown 格式),列出已发现和未覆盖的关键模式。\n` +
|
|
396
|
+
`⛔ 不要再调用任何工具,直接输出文本。`,
|
|
397
|
+
};
|
|
398
|
+
}
|
|
399
|
+
// Bootstrap 策略: 使用 dimensionDigest JSON (供维度编排消费)
|
|
363
400
|
return {
|
|
364
401
|
type: 'force_exit',
|
|
365
402
|
text: `⚠️ 你已使用 ${m.iteration}/${b.maxIterations} 轮次,**必须立即结束**。请在回复中直接输出 dimensionDigest JSON 总结(用 \`\`\`json 包裹),不要再调用任何工具。\n` +
|
|
@@ -459,7 +496,7 @@ export class ExplorationTracker {
|
|
|
459
496
|
|
|
460
497
|
/**
|
|
461
498
|
* 记录一次工具调用结果,更新内部指标
|
|
462
|
-
*
|
|
499
|
+
* 替代原内联的 ~120 行 if-else 逻辑
|
|
463
500
|
*
|
|
464
501
|
* @param {string} toolName
|
|
465
502
|
* @param {object} args
|
|
@@ -470,8 +507,8 @@ export class ExplorationTracker {
|
|
|
470
507
|
this.#metrics.totalToolCalls++;
|
|
471
508
|
const isNew = this.#detectNewInfo(toolName, args, result);
|
|
472
509
|
|
|
473
|
-
// Submit
|
|
474
|
-
if (toolName === 'submit_knowledge' || toolName === 'submit_with_check') {
|
|
510
|
+
// Submit 追踪(只记成功提交)— 含 scan 阶段的 collect_scan_recipe
|
|
511
|
+
if (toolName === 'submit_knowledge' || toolName === 'submit_with_check' || toolName === 'collect_scan_recipe') {
|
|
475
512
|
const status = typeof result === 'object' ? result?.status : 'ok';
|
|
476
513
|
const isRejected = status === 'rejected';
|
|
477
514
|
const isError = status === 'error';
|
|
@@ -526,6 +563,13 @@ export class ExplorationTracker {
|
|
|
526
563
|
// 4. 如果发生了转换,生成 nudge 立即返回给主循环注入
|
|
527
564
|
if (this.#justTransitioned) {
|
|
528
565
|
this.#justTransitioned = false;
|
|
566
|
+
// Scan pipeline: 不注入转换 nudge,shouldExit 会在下一轮直接退出
|
|
567
|
+
if (this.#submitToolName === 'collect_scan_recipe' && this.#isTerminalPhase()) {
|
|
568
|
+
this.#logger.info(
|
|
569
|
+
`[ExplorationTracker] scan pipeline: skip SUMMARIZE nudge, will exit on next tick (submits=${this.#metrics.submitCount})`
|
|
570
|
+
);
|
|
571
|
+
return null;
|
|
572
|
+
}
|
|
529
573
|
return {
|
|
530
574
|
type: 'phase_transition',
|
|
531
575
|
text: this.#buildTransitionNudge(),
|
|
@@ -557,8 +601,16 @@ export class ExplorationTracker {
|
|
|
557
601
|
}
|
|
558
602
|
|
|
559
603
|
if (isTerminal && transitioned) {
|
|
560
|
-
// 刚转入终结阶段
|
|
604
|
+
// 刚转入终结阶段
|
|
561
605
|
const submitCount = m.submitCount;
|
|
606
|
+
|
|
607
|
+
// Scan pipeline (collect_scan_recipe): 不需要 dimensionDigest
|
|
608
|
+
// 所有 recipes 已通过工具调用收集,LLM 当前输出的文本就是最终分析报告
|
|
609
|
+
// 直接标记 isFinalAnswer,避免浪费一轮迭代要求无用的 JSON digest
|
|
610
|
+
if (this.#submitToolName === 'collect_scan_recipe') {
|
|
611
|
+
return { isFinalAnswer: true, needsDigestNudge: false, shouldContinue: false, nudge: null };
|
|
612
|
+
}
|
|
613
|
+
|
|
562
614
|
// v5.1: Analyst 策略要求自然语言输出
|
|
563
615
|
const nudge = this.#strategy.name === 'analyst'
|
|
564
616
|
? `请**停止调用工具**,直接输出你的完整分析报告。用 Markdown 格式,包含具体文件路径、类名和代码模式。至少涵盖 3 个核心发现。\n\n**现在开始输出你的分析报告。**\n⚠️ 严禁在回复中复制本条指令文字,只输出你自己的分析。`
|
|
@@ -575,9 +627,10 @@ export class ExplorationTracker {
|
|
|
575
627
|
|
|
576
628
|
// 非终结阶段收到文本
|
|
577
629
|
if (this.#phase === 'PRODUCE' || this.#phase === 'EXPLORE') {
|
|
578
|
-
// PRODUCE
|
|
579
|
-
|
|
580
|
-
|
|
630
|
+
// Scan pipeline: PRODUCE 阶段不注入 continue nudge — 避免与即将到来的转换 nudge 矛盾
|
|
631
|
+
// Bootstrap pipeline: 保留提交引导
|
|
632
|
+
const nudge = (this.#phase === 'PRODUCE' && this.#submitToolName !== 'collect_scan_recipe')
|
|
633
|
+
? `你的分析很好。请继续调用 ${this.#submitToolName} 提交你发现的知识候选,每个值得记录的模式/实践都应该提交。`
|
|
581
634
|
: null;
|
|
582
635
|
return { isFinalAnswer: false, needsDigestNudge: false, shouldContinue: true, nudge };
|
|
583
636
|
}
|
|
@@ -641,6 +694,7 @@ export class ExplorationTracker {
|
|
|
641
694
|
return {
|
|
642
695
|
iteration: this.#metrics.iteration,
|
|
643
696
|
phase: this.#phase,
|
|
697
|
+
phaseRounds: this.#metrics.phaseRounds,
|
|
644
698
|
submitCount: this.#metrics.submitCount,
|
|
645
699
|
uniqueFiles: this.#metrics.uniqueFiles.size,
|
|
646
700
|
uniquePatterns: this.#metrics.uniquePatterns.size,
|
|
@@ -650,6 +704,11 @@ export class ExplorationTracker {
|
|
|
650
704
|
};
|
|
651
705
|
}
|
|
652
706
|
|
|
707
|
+
/** 当前指标快照 (便捷 getter, 与 getMetrics() 相同) */
|
|
708
|
+
get metrics() {
|
|
709
|
+
return this.getMetrics();
|
|
710
|
+
}
|
|
711
|
+
|
|
653
712
|
/**
|
|
654
713
|
* 获取计划进度
|
|
655
714
|
* @returns {object}
|
|
@@ -662,7 +721,7 @@ export class ExplorationTracker {
|
|
|
662
721
|
|
|
663
722
|
/**
|
|
664
723
|
* 检测工具调用是否产生了新信息
|
|
665
|
-
*
|
|
724
|
+
* 合并了原内联的探索追踪 + ReasoningLayer.buildObservationMeta 的逻辑
|
|
666
725
|
*
|
|
667
726
|
* @param {string} toolName
|
|
668
727
|
* @param {object} args
|
|
@@ -861,7 +920,7 @@ export class ExplorationTracker {
|
|
|
861
920
|
const toPhase = this.#phase;
|
|
862
921
|
|
|
863
922
|
if (toPhase === 'PRODUCE') {
|
|
864
|
-
return
|
|
923
|
+
return `你已充分探索了项目代码,现在请开始调用 ${this.#submitToolName} 工具来提交你发现的知识候选。不要再搜索,直接提交。`;
|
|
865
924
|
}
|
|
866
925
|
|
|
867
926
|
if (toPhase === 'SUMMARIZE') {
|
|
@@ -878,6 +937,13 @@ export class ExplorationTracker {
|
|
|
878
937
|
`**现在开始输出你的分析报告。不要再调用任何工具。**\n` +
|
|
879
938
|
`⚠️ 以上是行为指令,严禁在回复中复制或引用这段文字,只输出你自己的分析内容。`;
|
|
880
939
|
}
|
|
940
|
+
// Scan pipeline (collect_scan_recipe): 使用纯文本总结
|
|
941
|
+
if (this.#submitToolName === 'collect_scan_recipe') {
|
|
942
|
+
return `你已通过 collect_scan_recipe 提交了 ${submitCount} 个知识候选。` +
|
|
943
|
+
`请**停止调用工具**,直接输出你的分析总结(Markdown 格式)。\n` +
|
|
944
|
+
`⚠️ 不要再调用任何工具,直接输出文本。`;
|
|
945
|
+
}
|
|
946
|
+
// Bootstrap: 使用 dimensionDigest JSON (供维度编排消费)
|
|
881
947
|
return `你已完成分析探索。请在回复中直接输出 dimensionDigest JSON(用 \`\`\`json 包裹),包含以下字段:\n` +
|
|
882
948
|
`\`\`\`json\n{"dimensionDigest":{"summary":"分析总结(100-200字)","candidateCount":${submitCount},"keyFindings":["关键发现"],"crossRefs":{},"gaps":["未覆盖方面"],"remainingTasks":[{"signal":"未处理的信号/主题","reason":"未完成原因(如:提交上限已达)","priority":"high|medium|low","searchHints":["建议搜索词"]}]}}\n\`\`\`\n> 如果所有信号都已覆盖,remainingTasks 留空数组 \`[]\`。如果有未来得及处理的信号,请在此标记,系统会在下次运行时续传。\n` +
|
|
883
949
|
`⚠️ 严禁在回复中复制本条指令文字,只输出 JSON。`;
|
|
@@ -912,10 +978,13 @@ export class ExplorationTracker {
|
|
|
912
978
|
|
|
913
979
|
case 'PRODUCE':
|
|
914
980
|
if (m.submitCount === 0 && m.phaseRounds >= 1) {
|
|
915
|
-
return
|
|
981
|
+
return `⚠️ 探索阶段已结束。你已收集了足够的项目信息,请 **立即** 调用 ${this.#submitToolName} 提交候选。不要继续搜索,直接提交。`;
|
|
916
982
|
}
|
|
917
983
|
if (m.submitCount >= b.softSubmitLimit && b.softSubmitLimit > 0) {
|
|
918
984
|
const remaining = b.maxSubmits - m.submitCount;
|
|
985
|
+
if (this.#submitToolName === 'collect_scan_recipe') {
|
|
986
|
+
return `已提交 ${m.submitCount} 个候选(上限 ${b.maxSubmits})。${remaining > 0 ? `还可提交 ${remaining} 个。` : ''}如果还有值得记录的发现可以继续提交,否则请输出分析总结。`;
|
|
987
|
+
}
|
|
919
988
|
return `已提交 ${m.submitCount} 个候选(上限 ${b.maxSubmits})。${remaining > 0 ? `还可提交 ${remaining} 个。` : ''}如果还有值得记录的发现可以继续提交,否则请产出 dimensionDigest 总结。\n⚠️ 如果还有未处理的信号,请在 dimensionDigest 的 remainingTasks 字段中标记,下次运行时会续传。`;
|
|
920
989
|
}
|
|
921
990
|
return null;
|
|
@@ -961,7 +1030,7 @@ export class ExplorationTracker {
|
|
|
961
1030
|
|
|
962
1031
|
/**
|
|
963
1032
|
* 检查是否需要触发反思 + 生成反思 nudge
|
|
964
|
-
* @param {import('
|
|
1033
|
+
* @param {import('../memory/ActiveContext.js').ActiveContext} trace
|
|
965
1034
|
* @returns {{ type: string, text: string }|null}
|
|
966
1035
|
* @private
|
|
967
1036
|
*/
|
|
@@ -1039,7 +1108,7 @@ export class ExplorationTracker {
|
|
|
1039
1108
|
|
|
1040
1109
|
/**
|
|
1041
1110
|
* 检查是否需要触发规划 + 生成规划 nudge
|
|
1042
|
-
* @param {import('
|
|
1111
|
+
* @param {import('../memory/ActiveContext.js').ActiveContext} trace
|
|
1043
1112
|
* @returns {{ type: string, text: string }|null}
|
|
1044
1113
|
* @private
|
|
1045
1114
|
*/
|
|
@@ -1049,6 +1118,7 @@ export class ExplorationTracker {
|
|
|
1049
1118
|
|
|
1050
1119
|
// 第 1 轮: plan elicitation
|
|
1051
1120
|
if (m.iteration === 1) {
|
|
1121
|
+
trace?.expectPlan?.(); // 授权 ActiveContext 捕获计划
|
|
1052
1122
|
return {
|
|
1053
1123
|
type: 'planning',
|
|
1054
1124
|
text: this.#buildPlanElicitationPrompt(),
|
|
@@ -1071,6 +1141,12 @@ export class ExplorationTracker {
|
|
|
1071
1141
|
|
|
1072
1142
|
if (!periodicTrigger && !deviationTrigger) return null;
|
|
1073
1143
|
|
|
1144
|
+
// 冷却间隔: 上次 replan 后至少 MIN_REPLAN_GAP 轮才允许再次触发
|
|
1145
|
+
// 防止“坏 plan 步骤永远不匹配 → 每轮 deviation → replan 风暴”
|
|
1146
|
+
if (progress.lastReplanIteration && m.iteration - progress.lastReplanIteration < MIN_REPLAN_GAP) {
|
|
1147
|
+
return null;
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1074
1150
|
const remaining = b.maxIterations - m.iteration;
|
|
1075
1151
|
const parts = [];
|
|
1076
1152
|
if (deviationTrigger) {
|
|
@@ -1102,6 +1178,7 @@ export class ExplorationTracker {
|
|
|
1102
1178
|
|
|
1103
1179
|
progress.lastReplanIteration = m.iteration;
|
|
1104
1180
|
this.#pendingReplan = true;
|
|
1181
|
+
trace?.expectPlan?.(); // 授权 ActiveContext 捕获 replan
|
|
1105
1182
|
|
|
1106
1183
|
this.#logger.info(
|
|
1107
1184
|
`[ExplorationTracker] 📋 replan triggered at iteration ${m.iteration} (${deviationTrigger ? 'deviation' : 'periodic'})`
|
|
@@ -1140,7 +1217,7 @@ export class ExplorationTracker {
|
|
|
1140
1217
|
* 更新计划进度(从 ReasoningLayer 迁入)
|
|
1141
1218
|
* 将本轮工具调用与 plan 步骤进行模糊匹配
|
|
1142
1219
|
*
|
|
1143
|
-
* @param {import('
|
|
1220
|
+
* @param {import('../memory/ActiveContext.js').ActiveContext} trace
|
|
1144
1221
|
*/
|
|
1145
1222
|
updatePlanProgress(trace) {
|
|
1146
1223
|
if (!this.#strategy.enablePlanning) return;
|
|
@@ -1148,6 +1225,20 @@ export class ExplorationTracker {
|
|
|
1148
1225
|
const steps = trace?.getPlanStepsMutable?.() || [];
|
|
1149
1226
|
if (steps.length === 0) return;
|
|
1150
1227
|
|
|
1228
|
+
// 处理 pending replan — 必须在 actions 检查前处理
|
|
1229
|
+
// 因为 LLM 回复 replan 时可能会同时输出新计划 + 纯文本(无工具调用)
|
|
1230
|
+
// 此时 extractAndSetPlan 已更新 plan steps,需要重新计算进度
|
|
1231
|
+
if (this.#pendingReplan) {
|
|
1232
|
+
const plan = trace?.getPlan?.();
|
|
1233
|
+
if (plan) {
|
|
1234
|
+
this.#planProgress.coveredSteps = plan.steps.filter((s) => s.status === 'done').length;
|
|
1235
|
+
this.#planProgress.totalSteps = plan.steps.length;
|
|
1236
|
+
this.#planProgress.unplannedActions = 0;
|
|
1237
|
+
this.#planProgress.consecutiveOffPlan = 0;
|
|
1238
|
+
this.#pendingReplan = false;
|
|
1239
|
+
}
|
|
1240
|
+
}
|
|
1241
|
+
|
|
1151
1242
|
const actions = trace?.getCurrentRoundActions?.() || [];
|
|
1152
1243
|
if (actions.length === 0) return;
|
|
1153
1244
|
|
|
@@ -1173,18 +1264,6 @@ export class ExplorationTracker {
|
|
|
1173
1264
|
this.#planProgress.totalSteps = steps.length;
|
|
1174
1265
|
this.#planProgress.deviationScore =
|
|
1175
1266
|
steps.length > 0 ? 1 - this.#planProgress.coveredSteps / steps.length : 0;
|
|
1176
|
-
|
|
1177
|
-
// 处理 pending replan
|
|
1178
|
-
if (this.#pendingReplan) {
|
|
1179
|
-
const plan = trace?.getPlan?.();
|
|
1180
|
-
if (plan) {
|
|
1181
|
-
this.#planProgress.coveredSteps = plan.steps.filter((s) => s.status === 'done').length;
|
|
1182
|
-
this.#planProgress.totalSteps = plan.steps.length;
|
|
1183
|
-
this.#planProgress.unplannedActions = 0;
|
|
1184
|
-
this.#planProgress.consecutiveOffPlan = 0;
|
|
1185
|
-
this.#pendingReplan = false;
|
|
1186
|
-
}
|
|
1187
|
-
}
|
|
1188
1267
|
}
|
|
1189
1268
|
|
|
1190
1269
|
/**
|
|
@@ -1242,7 +1321,7 @@ export class ExplorationTracker {
|
|
|
1242
1321
|
|
|
1243
1322
|
/**
|
|
1244
1323
|
* 推理质量评分
|
|
1245
|
-
* @param {import('
|
|
1324
|
+
* @param {import('../memory/ActiveContext.js').ActiveContext} trace
|
|
1246
1325
|
* @returns {{ score: number, breakdown: object }}
|
|
1247
1326
|
*/
|
|
1248
1327
|
getQualityMetrics(trace) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* ChatAgentPrompts —
|
|
2
|
+
* ChatAgentPrompts — Agent 提示词构建和文本处理方法
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
5
|
import fs from 'node:fs';
|
|
@@ -152,8 +152,10 @@ export function cleanFinalAnswer(response) {
|
|
|
152
152
|
.replace(/^\*{0,2}(?:请在|请直接|请确保|请务必|现在开始|输出你的|不要输出|不要再|不要包含|重要\s*[::]).*(?:分析文本|分析总结|JSON|工具|输出|文本|报告)\*{0,2}[。.]?\s*$/gm, '')
|
|
153
153
|
.replace(/^注意[::]\s*到达第\s*\d+\s*轮时.*$/gm, '')
|
|
154
154
|
.replace(/^第\s*\d+\/\d+\s*轮\s*\|[^\n]*$/gm, '')
|
|
155
|
-
//
|
|
156
|
-
|
|
155
|
+
// v5.2: 移除 dimensionDigest JSON 剥离
|
|
156
|
+
// 之前会把 SUMMARIZE 阶段 LLM 按要求产出的 dimensionDigest JSON 全部删掉 → 0 chars
|
|
157
|
+
// dimensionDigest 是 SUMMARIZE 的预期输出,不应被 cleanFinalAnswer 清理
|
|
158
|
+
// Analyst 策略的 SUMMARIZE 使用自然语言 Markdown(不含 dimensionDigest),不受影响
|
|
157
159
|
.replace(/\n{3,}/g, '\n\n')
|
|
158
160
|
.trim();
|
|
159
161
|
}
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* LoopContext — reactLoop 单次执行的完整状态
|
|
3
|
+
*
|
|
4
|
+
* 封装原 reactLoop 内散落的 10+ 局部变量:
|
|
5
|
+
* - 注入依赖 (messages, tracker, trace, memoryCoordinator, sharedState)
|
|
6
|
+
* - 循环状态 (iteration, lastReply, toolCalls, tokenUsage)
|
|
7
|
+
* - 错误恢复 (consecutiveAiErrors, consecutiveEmptyResponses)
|
|
8
|
+
* - 配置 (source, budget, capabilities, baseSystemPrompt, toolSchemas, prompt)
|
|
9
|
+
*
|
|
10
|
+
* 使 reactLoop 的提取方法只需接收一个 ctx 参数。
|
|
11
|
+
*
|
|
12
|
+
* @module core/LoopContext
|
|
13
|
+
*/
|
|
14
|
+
|
|
15
|
+
/**
|
|
16
|
+
* @typedef {Object} LoopContextConfig
|
|
17
|
+
* @property {import('./MessageAdapter.js').MessageAdapter} messages — 统一消息适配器
|
|
18
|
+
* @property {Object|null} [tracker] — ExplorationTracker 实例
|
|
19
|
+
* @property {Object|null} [trace] — ActiveContext 实例
|
|
20
|
+
* @property {Object|null} [memoryCoordinator] — MemoryCoordinator 实例
|
|
21
|
+
* @property {Object|null} [sharedState] — 共享状态 { submittedTitles, submittedPatterns }
|
|
22
|
+
* @property {string} [source] — 'user' | 'system'
|
|
23
|
+
* @property {Object} budget — 预算配置
|
|
24
|
+
* @property {import('../capabilities.js').Capability[]} capabilities — 本轮使用的 capabilities
|
|
25
|
+
* @property {string} baseSystemPrompt — 基础系统提示词
|
|
26
|
+
* @property {Array} toolSchemas — 工具 schema 列表
|
|
27
|
+
* @property {string} prompt — 原始用户提示
|
|
28
|
+
* @property {Function|null} [onToolCall] — 本轮工具调用钩子
|
|
29
|
+
* @property {Object} [context] — 额外上下文
|
|
30
|
+
* @property {import('../context/ContextWindow.js').ContextWindow|null} [contextWindow] — 原始 ContextWindow (供 forced-summary 等外部逻辑)
|
|
31
|
+
*/
|
|
32
|
+
|
|
33
|
+
export class LoopContext {
|
|
34
|
+
// ─── 注入依赖 ───
|
|
35
|
+
|
|
36
|
+
/** @type {import('./MessageAdapter.js').MessageAdapter} 统一消息适配器 */
|
|
37
|
+
messages;
|
|
38
|
+
|
|
39
|
+
/** @type {Object|null} ExplorationTracker 实例 */
|
|
40
|
+
tracker;
|
|
41
|
+
|
|
42
|
+
/** @type {Object|null} ActiveContext 实例 */
|
|
43
|
+
trace;
|
|
44
|
+
|
|
45
|
+
/** @type {Object|null} MemoryCoordinator 实例 */
|
|
46
|
+
memoryCoordinator;
|
|
47
|
+
|
|
48
|
+
/** @type {Object|null} 共享状态 */
|
|
49
|
+
sharedState;
|
|
50
|
+
|
|
51
|
+
// ─── 循环状态 ───
|
|
52
|
+
|
|
53
|
+
/** @type {number} 当前迭代次数 */
|
|
54
|
+
iteration = 0;
|
|
55
|
+
|
|
56
|
+
/** @type {string} 最终回复文本 */
|
|
57
|
+
lastReply = '';
|
|
58
|
+
|
|
59
|
+
/** @type {Array} 本轮工具调用记录 */
|
|
60
|
+
toolCalls = [];
|
|
61
|
+
|
|
62
|
+
/** @type {{input: number, output: number}} 本轮 token 用量 */
|
|
63
|
+
tokenUsage = { input: 0, output: 0 };
|
|
64
|
+
|
|
65
|
+
/** @type {number} 循环开始时间戳 */
|
|
66
|
+
loopStartTime = 0;
|
|
67
|
+
|
|
68
|
+
// ─── 错误恢复 ───
|
|
69
|
+
|
|
70
|
+
/** @type {number} 连续 AI 错误计数 (2-strike 策略) */
|
|
71
|
+
consecutiveAiErrors = 0;
|
|
72
|
+
|
|
73
|
+
/** @type {number} 连续空响应计数 */
|
|
74
|
+
consecutiveEmptyResponses = 0;
|
|
75
|
+
|
|
76
|
+
// ─── 配置 (只读) ───
|
|
77
|
+
|
|
78
|
+
/** @type {string} 来源 'user' | 'system' */
|
|
79
|
+
source;
|
|
80
|
+
|
|
81
|
+
/** @type {Object} 预算配置 */
|
|
82
|
+
budget;
|
|
83
|
+
|
|
84
|
+
/** @type {import('../capabilities.js').Capability[]} */
|
|
85
|
+
capabilities;
|
|
86
|
+
|
|
87
|
+
/** @type {string} 基础系统提示词 */
|
|
88
|
+
baseSystemPrompt;
|
|
89
|
+
|
|
90
|
+
/** @type {Array} 工具 schemas */
|
|
91
|
+
toolSchemas;
|
|
92
|
+
|
|
93
|
+
/** @type {string} 原始用户提示 */
|
|
94
|
+
prompt;
|
|
95
|
+
|
|
96
|
+
/** @type {Function|null} 工具调用钩子 */
|
|
97
|
+
onToolCall;
|
|
98
|
+
|
|
99
|
+
/** @type {Object} 额外上下文 */
|
|
100
|
+
context;
|
|
101
|
+
|
|
102
|
+
/** @type {import('../../chat/ContextWindow.js').ContextWindow|null} 原始 ContextWindow 引用 */
|
|
103
|
+
contextWindow;
|
|
104
|
+
|
|
105
|
+
/** @type {string|null} 首轮 toolChoice 覆盖 ('required'/'auto'/'none') */
|
|
106
|
+
toolChoiceOverride;
|
|
107
|
+
|
|
108
|
+
/**
|
|
109
|
+
* @param {LoopContextConfig} config
|
|
110
|
+
*/
|
|
111
|
+
constructor(config) {
|
|
112
|
+
this.messages = config.messages;
|
|
113
|
+
this.tracker = config.tracker || null;
|
|
114
|
+
this.trace = config.trace || null;
|
|
115
|
+
this.memoryCoordinator = config.memoryCoordinator || null;
|
|
116
|
+
this.sharedState = config.sharedState || null;
|
|
117
|
+
this.source = config.source || 'user';
|
|
118
|
+
this.budget = config.budget;
|
|
119
|
+
this.capabilities = config.capabilities;
|
|
120
|
+
this.baseSystemPrompt = config.baseSystemPrompt;
|
|
121
|
+
this.toolSchemas = config.toolSchemas;
|
|
122
|
+
this.prompt = config.prompt;
|
|
123
|
+
this.onToolCall = config.onToolCall || null;
|
|
124
|
+
this.context = config.context || {};
|
|
125
|
+
this.contextWindow = config.contextWindow || null;
|
|
126
|
+
this.toolChoiceOverride = config.toolChoiceOverride || null;
|
|
127
|
+
this.loopStartTime = Date.now();
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
// ─── 计算属性 ───
|
|
131
|
+
|
|
132
|
+
/** 是否为 system 场景 */
|
|
133
|
+
get isSystem() {
|
|
134
|
+
return this.source === 'system';
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
/** 最大迭代数 */
|
|
138
|
+
get maxIterations() {
|
|
139
|
+
return this.budget.maxIterations || 20;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
// ─── Token 累计辅助 ───
|
|
143
|
+
|
|
144
|
+
/**
|
|
145
|
+
* 累加 token 用量到循环级统计
|
|
146
|
+
* @param {Object} usage — { inputTokens, outputTokens }
|
|
147
|
+
*/
|
|
148
|
+
addTokenUsage(usage) {
|
|
149
|
+
if (!usage) return;
|
|
150
|
+
const inTok = usage.inputTokens || 0;
|
|
151
|
+
const outTok = usage.outputTokens || 0;
|
|
152
|
+
this.tokenUsage.input += inTok;
|
|
153
|
+
this.tokenUsage.output += outTok;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// ─── 结果构建 ───
|
|
157
|
+
|
|
158
|
+
/**
|
|
159
|
+
* 构建循环返回值
|
|
160
|
+
* @returns {{ reply: string, toolCalls: Array, tokenUsage: Object, iterations: number }}
|
|
161
|
+
*/
|
|
162
|
+
buildResult() {
|
|
163
|
+
return {
|
|
164
|
+
reply: this.lastReply,
|
|
165
|
+
toolCalls: [...this.toolCalls],
|
|
166
|
+
tokenUsage: { ...this.tokenUsage },
|
|
167
|
+
iterations: this.iteration,
|
|
168
|
+
};
|
|
169
|
+
}
|
|
170
|
+
}
|