autosnippet 3.3.8 → 3.4.0
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/README.md +1 -1
- package/config/default.json +1 -1
- package/dashboard/dist/assets/{index-DV8biUkH.js → index-8b1Gf3Bb.js} +4 -4
- package/dashboard/dist/index.html +1 -1
- package/dist/lib/agent/AgentRuntime.js +13 -1
- package/dist/lib/agent/AgentRuntimeTypes.d.ts +2 -0
- package/dist/lib/agent/PipelineStrategy.js +32 -2
- package/dist/lib/agent/context/ContextWindow.d.ts +2 -1
- package/dist/lib/agent/context/ContextWindow.js +18 -4
- package/dist/lib/agent/context/ExplorationTracker.js +6 -1
- package/dist/lib/agent/context/exploration/ExplorationStrategies.js +2 -1
- package/dist/lib/agent/core/LoopContext.d.ts +3 -0
- package/dist/lib/agent/core/LoopContext.js +3 -0
- package/dist/lib/agent/domain/EpisodicConsolidator.d.ts +5 -0
- package/dist/lib/agent/domain/EpisodicConsolidator.js +60 -5
- package/dist/lib/agent/domain/insight-analyst.d.ts +16 -0
- package/dist/lib/agent/domain/insight-analyst.js +38 -0
- package/dist/lib/agent/domain/insight-gate.js +12 -0
- package/dist/lib/agent/memory/MemoryConsolidator.js +17 -0
- package/dist/lib/bootstrap.js +6 -1
- package/dist/lib/cli/SetupService.js +5 -4
- package/dist/lib/domain/dimension/DimensionRegistry.d.ts +6 -4
- package/dist/lib/domain/dimension/DimensionRegistry.js +19 -23
- package/dist/lib/external/ai/AiProvider.d.ts +2 -0
- package/dist/lib/external/ai/AiProvider.js +4 -0
- package/dist/lib/external/ai/providers/ClaudeProvider.d.ts +1 -1
- package/dist/lib/external/ai/providers/ClaudeProvider.js +6 -2
- package/dist/lib/external/ai/providers/GoogleGeminiProvider.d.ts +1 -1
- package/dist/lib/external/ai/providers/GoogleGeminiProvider.js +6 -2
- package/dist/lib/external/ai/providers/OpenAiProvider.d.ts +1 -1
- package/dist/lib/external/ai/providers/OpenAiProvider.js +7 -3
- package/dist/lib/external/mcp/McpServer.js +19 -2
- package/dist/lib/external/mcp/handlers/bootstrap/MissionBriefingBuilder.js +10 -2
- package/dist/lib/external/mcp/handlers/bootstrap/pipeline/mock-pipeline.js +10 -29
- package/dist/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +37 -4
- package/dist/lib/http/routes/ai.js +2 -2
- package/dist/lib/infrastructure/database/DatabaseConnection.js +7 -6
- package/dist/lib/service/delivery/CursorDeliveryPipeline.js +15 -1
- package/dist/lib/shared/PathGuard.js +16 -10
- package/dist/lib/shared/isOwnDevRepo.d.ts +29 -4
- package/dist/lib/shared/isOwnDevRepo.js +64 -4
- package/package.json +1 -1
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<link rel="icon" type="image/svg+xml" href="/vite.svg" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
7
|
<title>AutoSnippet Dashboard</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-8b1Gf3Bb.js"></script>
|
|
9
9
|
<link rel="modulepreload" crossorigin href="/assets/yaml-qRaU8Ldn.js">
|
|
10
10
|
<link rel="modulepreload" crossorigin href="/assets/vendor-BZEJEVBn.js">
|
|
11
11
|
<link rel="modulepreload" crossorigin href="/assets/icons-BMNb0V6L.js">
|
|
@@ -252,7 +252,7 @@ export class AgentRuntime {
|
|
|
252
252
|
// ─── 提取方法: reactLoop 内部阶段 ────────────
|
|
253
253
|
/** 初始化循环上下文 — 封装 reactLoop 前 ~60 行初始化逻辑 */
|
|
254
254
|
#initLoop(prompt, opts) {
|
|
255
|
-
const { history = [], context = {}, capabilityOverride, budgetOverride, systemPromptOverride, onToolCall, contextWindow, tracker, trace, memoryCoordinator, sharedState, source, toolChoiceOverride, } = opts;
|
|
255
|
+
const { history = [], context = {}, capabilityOverride, budgetOverride, systemPromptOverride, onToolCall, contextWindow, tracker, trace, memoryCoordinator, sharedState, source, toolChoiceOverride, abortSignal, } = opts;
|
|
256
256
|
// 解析 capabilities
|
|
257
257
|
const caps = capabilityOverride
|
|
258
258
|
? this.#resolveCapabilities(capabilityOverride)
|
|
@@ -314,6 +314,7 @@ export class AgentRuntime {
|
|
|
314
314
|
context: context || {},
|
|
315
315
|
contextWindow: contextWindow || null,
|
|
316
316
|
toolChoiceOverride: toolChoiceOverride || null,
|
|
317
|
+
abortSignal: abortSignal || null,
|
|
317
318
|
});
|
|
318
319
|
}
|
|
319
320
|
/**
|
|
@@ -321,6 +322,11 @@ export class AgentRuntime {
|
|
|
321
322
|
* @returns true = 应退出循环
|
|
322
323
|
*/
|
|
323
324
|
#shouldExit(ctx) {
|
|
325
|
+
// 外部中止信号 — 立即退出
|
|
326
|
+
if (ctx.abortSignal?.aborted) {
|
|
327
|
+
this.logger.info('[AgentRuntime] ⛔ abortSignal fired — exiting loop');
|
|
328
|
+
return true;
|
|
329
|
+
}
|
|
324
330
|
// ExplorationTracker: tick + 退出检查
|
|
325
331
|
if (ctx.tracker) {
|
|
326
332
|
ctx.tracker.tick();
|
|
@@ -451,6 +457,7 @@ export class AgentRuntime {
|
|
|
451
457
|
systemPrompt: effectiveSystemPrompt,
|
|
452
458
|
temperature: ctx.budget.temperature ?? (ctx.isSystem ? 0.3 : 0.7),
|
|
453
459
|
maxTokens: ctx.budget.maxTokens ?? (ctx.isSystem ? 8192 : 4096),
|
|
460
|
+
abortSignal: ctx.abortSignal ?? undefined,
|
|
454
461
|
}));
|
|
455
462
|
ctx.consecutiveAiErrors = 0;
|
|
456
463
|
}
|
|
@@ -518,6 +525,11 @@ export class AgentRuntime {
|
|
|
518
525
|
* @returns continueResult() 或 null (退出)
|
|
519
526
|
*/
|
|
520
527
|
async #handleAiError(ctx, aiErr) {
|
|
528
|
+
// AbortError — 外部中止信号已触发,不计入错误计数,立即退出
|
|
529
|
+
if (ctx.abortSignal?.aborted) {
|
|
530
|
+
this.logger.info('[AgentRuntime] ⛔ abortSignal fired during LLM call — exiting');
|
|
531
|
+
return null;
|
|
532
|
+
}
|
|
521
533
|
ctx.consecutiveAiErrors++;
|
|
522
534
|
this.logger.warn(`[AgentRuntime] AI call failed (attempt ${ctx.consecutiveAiErrors}): ${aiErr.message}`);
|
|
523
535
|
ctx.tracker?.rollbackTick?.();
|
|
@@ -106,6 +106,8 @@ export interface ReactLoopOpts {
|
|
|
106
106
|
sharedState?: Record<string, unknown>;
|
|
107
107
|
source?: string;
|
|
108
108
|
toolChoiceOverride?: string | null;
|
|
109
|
+
/** 外部中止信号 — PipelineStrategy hard timeout 时取消进行中的 LLM 调用 */
|
|
110
|
+
abortSignal?: AbortSignal;
|
|
109
111
|
[key: string]: unknown;
|
|
110
112
|
}
|
|
111
113
|
/** 单次迭代允许的最大工具调用数 */
|
|
@@ -208,7 +208,30 @@ export class PipelineStrategy extends Strategy {
|
|
|
208
208
|
`tracker: ${stageTracker?.constructor?.name || 'none'}` +
|
|
209
209
|
`${submitToolName ? `, submitTool: ${submitToolName}` : ''}`);
|
|
210
210
|
// 执行 reactLoop (含 per-stage 硬超时保护)
|
|
211
|
-
|
|
211
|
+
let stageResult = await this.#runWithTimeout(runtime, stagePrompt, message, stage, effectiveBudget, ctxWin, stageTracker, strategyContext, phaseResults, bus);
|
|
212
|
+
// ── 超时零输出快速重试 ──
|
|
213
|
+
// 当阶段 hard timeout 且 0 tool calls(LLM 完全卡住),
|
|
214
|
+
// 如果有 retryBudget 且本次非 retry,立即以降级预算重跑一次,
|
|
215
|
+
// 跳过 gate 往返,争取在更短时限内拿到输出。
|
|
216
|
+
if (stageResult.timedOut && !stageResult.toolCalls?.length && !isRetry && stage.retryBudget) {
|
|
217
|
+
_pipelineLogger.info(`[PipelineStrategy] ♻️ Stage "${stage.name}" timed out with 0 tool calls — fast-retrying with retryBudget`);
|
|
218
|
+
bus.publish(AgentEvents.PROGRESS, {
|
|
219
|
+
type: 'pipeline_stage_fast_retry',
|
|
220
|
+
stage: stage.name,
|
|
221
|
+
});
|
|
222
|
+
// 重置 ContextWindow (清空上一轮的空消息)
|
|
223
|
+
if (ctxWin) {
|
|
224
|
+
ctxWin.resetForNewStage();
|
|
225
|
+
}
|
|
226
|
+
// 重建 tracker — 用 retryBudget 的更短限制
|
|
227
|
+
const retryTracker = this.#resolveStageTracker(stage, ctx, strategyContext, stage.retryBudget);
|
|
228
|
+
// 构建简化 prompt(如果有 retryPromptBuilder 则使用)
|
|
229
|
+
let retryPrompt = stagePrompt;
|
|
230
|
+
if (typeof stage.retryPromptBuilder === 'function') {
|
|
231
|
+
retryPrompt = stage.retryPromptBuilder({ reason: 'Stage hard timeout with 0 tool calls', artifact: null }, message.content, phaseResults);
|
|
232
|
+
}
|
|
233
|
+
stageResult = await this.#runWithTimeout(runtime, retryPrompt, message, stage, stage.retryBudget, ctxWin, retryTracker, strategyContext, phaseResults, bus);
|
|
234
|
+
}
|
|
212
235
|
// 累计结果
|
|
213
236
|
phaseResults[stage.name] = stageResult;
|
|
214
237
|
ctx.totalToolCalls.push(...(stageResult.toolCalls || []));
|
|
@@ -283,6 +306,8 @@ export class PipelineStrategy extends Strategy {
|
|
|
283
306
|
}
|
|
284
307
|
/** 执行 reactLoop 并添加硬超时保护 */
|
|
285
308
|
async #runWithTimeout(runtime, stagePrompt, message, stage, effectiveBudget, ctxWin, stageTracker, strategyContext, phaseResults, bus) {
|
|
309
|
+
// 创建 AbortController — hard timeout 时取消进行中的 LLM 请求
|
|
310
|
+
const abortController = new AbortController();
|
|
286
311
|
const reactPromise = runtime.reactLoop(stagePrompt, {
|
|
287
312
|
history: message.history,
|
|
288
313
|
context: {
|
|
@@ -300,6 +325,7 @@ export class PipelineStrategy extends Strategy {
|
|
|
300
325
|
memoryCoordinator: strategyContext.memoryCoordinator || null,
|
|
301
326
|
sharedState: strategyContext.sharedState || null,
|
|
302
327
|
source: strategyContext.source || null,
|
|
328
|
+
abortSignal: abortController.signal,
|
|
303
329
|
});
|
|
304
330
|
const stageTimeoutMs = effectiveBudget?.timeoutMs;
|
|
305
331
|
if (!stageTimeoutMs) {
|
|
@@ -311,7 +337,11 @@ export class PipelineStrategy extends Strategy {
|
|
|
311
337
|
return Promise.race([
|
|
312
338
|
reactPromise,
|
|
313
339
|
new Promise((_, reject) => {
|
|
314
|
-
hardTimer = setTimeout(() =>
|
|
340
|
+
hardTimer = setTimeout(() => {
|
|
341
|
+
// 先中止进行中的 LLM HTTP 请求,再触发 reject
|
|
342
|
+
abortController.abort();
|
|
343
|
+
reject(new Error('__STAGE_HARD_TIMEOUT__'));
|
|
344
|
+
}, hardLimitMs);
|
|
315
345
|
}),
|
|
316
346
|
])
|
|
317
347
|
.catch((err) => {
|
|
@@ -56,12 +56,13 @@ export declare class ContextWindow {
|
|
|
56
56
|
* 根据模型名称解析合适的 ContextWindow token 预算。
|
|
57
57
|
*
|
|
58
58
|
* 策略: 取模型最大上下文窗口的一个安全分片,
|
|
59
|
+
* - 超大窗口 (≥400k): 预算 48000(1M 级模型可容纳更多上下文)
|
|
59
60
|
* - 大窗口 (≥200k): 预算 32000(tool schemas + system prompt 占显著空间)
|
|
60
61
|
* - 中窗口 (≥64k): 预算 24000
|
|
61
62
|
* - 小窗口 (≥16k): 预算 12000
|
|
62
63
|
* - 微窗口 (<16k): 预算 = 窗口 × 0.7(留 30% 给 prompt/tool schema)
|
|
63
64
|
*
|
|
64
|
-
* @param modelName 模型名称,如 'gemini-3-flash-preview', 'gpt-
|
|
65
|
+
* @param modelName 模型名称,如 'gemini-3-flash-preview', 'gpt-5.4-mini'
|
|
65
66
|
* @param [opts] - isSystem 为 true 时给予更高预算
|
|
66
67
|
* @returns 建议的 token 预算
|
|
67
68
|
*/
|
|
@@ -53,6 +53,8 @@ export class ContextWindow {
|
|
|
53
53
|
[/gemini-1\.0/i, 32_000],
|
|
54
54
|
[/gemini/i, 1_000_000], // 未知版本回退
|
|
55
55
|
// ── OpenAI ──
|
|
56
|
+
[/gpt-5\.4-(?:mini|nano)/i, 400_000],
|
|
57
|
+
[/gpt-5/i, 1_000_000],
|
|
56
58
|
[/gpt-4o/i, 128_000],
|
|
57
59
|
[/gpt-4-turbo/i, 128_000],
|
|
58
60
|
[/gpt-4-(?!turbo)/i, 8_192],
|
|
@@ -60,6 +62,7 @@ export class ContextWindow {
|
|
|
60
62
|
[/gpt-3\.5/i, 4_096],
|
|
61
63
|
[/o1|o3|o4/i, 200_000], // OpenAI reasoning models
|
|
62
64
|
// ── Anthropic ──
|
|
65
|
+
[/claude-(?:opus|sonnet)-4[.-]6/i, 1_000_000], // Opus 4.6 / Sonnet 4.6
|
|
63
66
|
[/claude-.*sonnet-4/i, 200_000],
|
|
64
67
|
[/claude-3[.-]5/i, 200_000],
|
|
65
68
|
[/claude-3[.-]opus/i, 200_000],
|
|
@@ -81,12 +84,13 @@ export class ContextWindow {
|
|
|
81
84
|
* 根据模型名称解析合适的 ContextWindow token 预算。
|
|
82
85
|
*
|
|
83
86
|
* 策略: 取模型最大上下文窗口的一个安全分片,
|
|
87
|
+
* - 超大窗口 (≥400k): 预算 48000(1M 级模型可容纳更多上下文)
|
|
84
88
|
* - 大窗口 (≥200k): 预算 32000(tool schemas + system prompt 占显著空间)
|
|
85
89
|
* - 中窗口 (≥64k): 预算 24000
|
|
86
90
|
* - 小窗口 (≥16k): 预算 12000
|
|
87
91
|
* - 微窗口 (<16k): 预算 = 窗口 × 0.7(留 30% 给 prompt/tool schema)
|
|
88
92
|
*
|
|
89
|
-
* @param modelName 模型名称,如 'gemini-3-flash-preview', 'gpt-
|
|
93
|
+
* @param modelName 模型名称,如 'gemini-3-flash-preview', 'gpt-5.4-mini'
|
|
90
94
|
* @param [opts] - isSystem 为 true 时给予更高预算
|
|
91
95
|
* @returns 建议的 token 预算
|
|
92
96
|
*/
|
|
@@ -104,7 +108,10 @@ export class ContextWindow {
|
|
|
104
108
|
}
|
|
105
109
|
// 2. 按分级策略计算 token 预算
|
|
106
110
|
let budget;
|
|
107
|
-
if (contextSize >=
|
|
111
|
+
if (contextSize >= 400_000) {
|
|
112
|
+
budget = isSystem ? 48_000 : 36_000;
|
|
113
|
+
}
|
|
114
|
+
else if (contextSize >= 200_000) {
|
|
108
115
|
budget = isSystem ? 32_000 : 24_000;
|
|
109
116
|
}
|
|
110
117
|
else if (contextSize >= 64_000) {
|
|
@@ -209,7 +216,10 @@ export class ContextWindow {
|
|
|
209
216
|
}
|
|
210
217
|
}
|
|
211
218
|
if (truncated > 0) {
|
|
212
|
-
this
|
|
219
|
+
const afterTokens = this.estimateTokens();
|
|
220
|
+
const ratio = this.getTokenUsageRatio();
|
|
221
|
+
this.#logger.info(`[ContextWindow] L1 compact: truncated ${truncated} tool results | ` +
|
|
222
|
+
`tokens≈${afterTokens}/${this.#tokenBudget} (${(ratio * 100).toFixed(1)}%)`);
|
|
213
223
|
}
|
|
214
224
|
return { level: 1, removed: truncated };
|
|
215
225
|
}
|
|
@@ -285,7 +295,11 @@ export class ContextWindow {
|
|
|
285
295
|
});
|
|
286
296
|
const removedCount = keepFrom - 1;
|
|
287
297
|
this.#compactionLog.push(`L${level}: removed ${removedCount} messages (${toolCallCount} rounds)`);
|
|
288
|
-
this
|
|
298
|
+
const afterTokens = this.estimateTokens();
|
|
299
|
+
const ratio = this.getTokenUsageRatio();
|
|
300
|
+
this.#logger.info(`[ContextWindow] L${level} compact: removed ${removedCount} messages (${toolCallCount} rounds), ` +
|
|
301
|
+
`kept last ${level === 2 ? 2 : 1} round(s) | ` +
|
|
302
|
+
`tokens≈${afterTokens}/${this.#tokenBudget} (${(ratio * 100).toFixed(1)}%)`);
|
|
289
303
|
return { level, removed: removedCount };
|
|
290
304
|
}
|
|
291
305
|
// ─── 查询 API ─────────────────────────────────────────
|
|
@@ -70,6 +70,8 @@ export class ExplorationTracker {
|
|
|
70
70
|
#submitToolName = 'submit_knowledge';
|
|
71
71
|
/** 管线类型标识 — 统一场景判别(替代 submitToolName / strategy.name 字符串比较) */
|
|
72
72
|
#pipelineType;
|
|
73
|
+
/** 当前阶段开始时间(用于 dwell time 统计) */
|
|
74
|
+
#phaseStartTime = Date.now();
|
|
73
75
|
/**
|
|
74
76
|
* @param strategy 策略配置对象
|
|
75
77
|
* @param budget 预算配置 { maxIterations, searchBudget, ... }
|
|
@@ -446,8 +448,10 @@ export class ExplorationTracker {
|
|
|
446
448
|
}
|
|
447
449
|
#transitionTo(newPhase) {
|
|
448
450
|
const oldPhase = this.#phase;
|
|
451
|
+
const dwellMs = Date.now() - this.#phaseStartTime;
|
|
449
452
|
this.#transitionFromPhase = oldPhase;
|
|
450
453
|
this.#phase = newPhase;
|
|
454
|
+
this.#phaseStartTime = Date.now();
|
|
451
455
|
this.#metrics.phaseRounds = 0;
|
|
452
456
|
this.#metrics.searchRoundsInPhase = 0;
|
|
453
457
|
// 重置停滞计数器 — 防止跨阶段累积导致级联式过早转换
|
|
@@ -456,7 +460,8 @@ export class ExplorationTracker {
|
|
|
456
460
|
this.#metrics.roundsSinceSubmit = 0;
|
|
457
461
|
this.#metrics.consecutiveIdleRounds = 0;
|
|
458
462
|
this.#justTransitioned = true;
|
|
459
|
-
this.#logger.info(`[ExplorationTracker] ${oldPhase} → ${newPhase} (iter=${this.#metrics.iteration}, submits=${this.#metrics.submitCount},
|
|
463
|
+
this.#logger.info(`[ExplorationTracker] ${oldPhase} → ${newPhase} (iter=${this.#metrics.iteration}, submits=${this.#metrics.submitCount}, ` +
|
|
464
|
+
`dwellMs=${dwellMs}, files=${this.#metrics.uniqueFiles.size}, patterns=${this.#metrics.uniquePatterns.size})`);
|
|
460
465
|
// Phase 3: 发射阶段转换信号
|
|
461
466
|
if (this.#signalBus) {
|
|
462
467
|
const terminalPhase = this.#getTerminalPhase();
|
|
@@ -66,7 +66,8 @@ export const STRATEGY_ANALYST = {
|
|
|
66
66
|
phases: ['SCAN', 'EXPLORE', 'VERIFY', 'SUMMARIZE'],
|
|
67
67
|
transitions: {
|
|
68
68
|
'SCAN→EXPLORE': {
|
|
69
|
-
|
|
69
|
+
// 2 轮结构扫描足够获取项目骨架(目录 + 关键文件列表),将 1 轮还给 EXPLORE
|
|
70
|
+
onMetrics: (m) => m.iteration >= 2,
|
|
70
71
|
onTextResponse: false,
|
|
71
72
|
},
|
|
72
73
|
'EXPLORE→VERIFY': {
|
|
@@ -62,6 +62,7 @@ interface LoopContextConfig {
|
|
|
62
62
|
context?: Record<string, unknown>;
|
|
63
63
|
contextWindow?: ContextWindow | null;
|
|
64
64
|
toolChoiceOverride?: string | null;
|
|
65
|
+
abortSignal?: AbortSignal | null;
|
|
65
66
|
}
|
|
66
67
|
export declare class LoopContext {
|
|
67
68
|
/** 统一消息适配器 */
|
|
@@ -110,6 +111,8 @@ export declare class LoopContext {
|
|
|
110
111
|
contextWindow: ContextWindow | null;
|
|
111
112
|
/** 首轮 toolChoice 覆盖 ('required'/'auto'/'none') */
|
|
112
113
|
toolChoiceOverride: string | null;
|
|
114
|
+
/** 外部中止信号 — hard timeout 时取消进行中的 LLM 调用 */
|
|
115
|
+
abortSignal: AbortSignal | null;
|
|
113
116
|
constructor(config: LoopContextConfig);
|
|
114
117
|
/** 是否为 system 场景 */
|
|
115
118
|
get isSystem(): boolean;
|
|
@@ -60,6 +60,8 @@ export class LoopContext {
|
|
|
60
60
|
contextWindow;
|
|
61
61
|
/** 首轮 toolChoice 覆盖 ('required'/'auto'/'none') */
|
|
62
62
|
toolChoiceOverride;
|
|
63
|
+
/** 外部中止信号 — hard timeout 时取消进行中的 LLM 调用 */
|
|
64
|
+
abortSignal;
|
|
63
65
|
constructor(config) {
|
|
64
66
|
this.messages = config.messages;
|
|
65
67
|
this.tracker = (config.tracker || null);
|
|
@@ -76,6 +78,7 @@ export class LoopContext {
|
|
|
76
78
|
this.context = config.context || {};
|
|
77
79
|
this.contextWindow = config.contextWindow || null;
|
|
78
80
|
this.toolChoiceOverride = config.toolChoiceOverride || null;
|
|
81
|
+
this.abortSignal = (config.abortSignal || null);
|
|
79
82
|
this.loopStartTime = Date.now();
|
|
80
83
|
}
|
|
81
84
|
// ─── 计算属性 ───
|
|
@@ -97,6 +97,11 @@ export declare class EpisodicConsolidator {
|
|
|
97
97
|
};
|
|
98
98
|
total: ConsolidateResult;
|
|
99
99
|
durationMs: number;
|
|
100
|
+
perDimension: {
|
|
101
|
+
[k: string]: number;
|
|
102
|
+
};
|
|
103
|
+
importanceDistribution: Record<number, number>;
|
|
104
|
+
entityCount: number;
|
|
100
105
|
};
|
|
101
106
|
}
|
|
102
107
|
export default EpisodicConsolidator;
|
|
@@ -25,16 +25,18 @@ import Logger from '#infra/logging/Logger.js';
|
|
|
25
25
|
* - "XX 采用了 YY"
|
|
26
26
|
*/
|
|
27
27
|
const FACT_PATTERNS = [
|
|
28
|
-
//
|
|
28
|
+
// Chinese
|
|
29
29
|
/(?:项目|工程|代码库)(?:使用|采用|基于|遵循)了?\s*([^,。,.\n]{5,60})/g,
|
|
30
|
-
// "主要/核心 XXX 是 YYY"
|
|
31
30
|
/(?:主要|核心|主|主力)\s*(\S+)\s*(?:是|为|使用)\s*([^,。,.\n]{3,40})/g,
|
|
32
|
-
// "发现了? N 个 XXX"
|
|
33
31
|
/(?:发现|找到|扫描到|识别|共有|包含)\s*了?\s*(\d+)\s*个?\s*([^,。,.\n]{2,30})/g,
|
|
34
|
-
// "XXX 是唯一的/主要的 YYY"
|
|
35
32
|
/(\S{2,20})\s*是\s*(?:唯一的?|主要的?|核心的?|全局的?)\s*([^,。,.\n]{3,30})/g,
|
|
36
|
-
// "使用了 XXX 前缀/后缀/命名"
|
|
37
33
|
/(?:使用|采用|遵循)了?\s*(\S{1,10})\s*(?:前缀|后缀|命名|约定|规范)/g,
|
|
34
|
+
// English
|
|
35
|
+
/(?:the\s+)?project\s+(?:uses?|adopts?|relies\s+on|follows?)\s+([^.,\n]{5,60})/gi,
|
|
36
|
+
/(?:found|discovered|identified|detected)\s+(\d+)\s+([^.,\n]{3,40})/gi,
|
|
37
|
+
/(?:the\s+)?(?:primary|main|core)\s+(\S+)\s+(?:is|are)\s+([^.,\n]{3,40})/gi,
|
|
38
|
+
/(?:all|every)\s+([^.,\n]{3,30})\s+(?:use|adopt|follow|implement)\s+([^.,\n]{3,40})/gi,
|
|
39
|
+
/(?:there\s+(?:is|are))\s+(\d+)\s+([^.,\n]{3,40})/gi,
|
|
38
40
|
];
|
|
39
41
|
/**
|
|
40
42
|
* 匹配洞察性陈述:
|
|
@@ -43,9 +45,14 @@ const FACT_PATTERNS = [
|
|
|
43
45
|
* - "建议/推荐 XXX"
|
|
44
46
|
*/
|
|
45
47
|
const INSIGHT_PATTERNS = [
|
|
48
|
+
// Chinese
|
|
46
49
|
/([^,。,.\n]{5,40})(?:暗示|表明|说明|意味着|揭示)\s*([^,。,.\n]{5,60})/g,
|
|
47
50
|
/([^,。,.\n]{3,20})\s*(?:与|和)\s*([^,。,.\n]{3,20})\s*(?:耦合|关联|存在依赖|有关系)/g,
|
|
48
51
|
/(?:建议|推荐|应该|需要)\s*([^,。,.\n]{5,60})/g,
|
|
52
|
+
// English
|
|
53
|
+
/([^.,\n]{5,40})\s+(?:suggests?|indicates?|implies?|reveals?)\s+(?:that\s+)?([^.,\n]{5,60})/gi,
|
|
54
|
+
/([^.,\n]{3,20})\s+(?:is|are)\s+(?:tightly\s+)?(?:coupled|linked|related)\s+(?:to|with)\s+([^.,\n]{3,30})/gi,
|
|
55
|
+
/(?:recommend|should|consider|suggest)\s+([^.,\n]{5,60})/gi,
|
|
49
56
|
];
|
|
50
57
|
// ──────────────────────────────────────────────────────────────
|
|
51
58
|
// EpisodicConsolidator 类
|
|
@@ -81,9 +88,15 @@ export class EpisodicConsolidator {
|
|
|
81
88
|
const textFactMemories = this.#extractFromAnalysisText(sessionStore);
|
|
82
89
|
// 5. 合并所有候选, 使用 consolidate 去重
|
|
83
90
|
const allCandidates = [...findingMemories, ...insightMemories, ...textFactMemories];
|
|
91
|
+
// ── 结构化统计日志 ──
|
|
92
|
+
const dimStats = this.#computeDimStats(allCandidates);
|
|
93
|
+
const importanceDist = this.#computeImportanceDistribution(allCandidates);
|
|
94
|
+
const entityCount = allCandidates.reduce((sum, c) => sum + (c.relatedEntities?.length || 0), 0);
|
|
84
95
|
this.#logger.info(`[Consolidator] Extracted ${allCandidates.length} candidate memories: ` +
|
|
85
96
|
`${findingMemories.length} findings, ${insightMemories.length} insights, ` +
|
|
86
97
|
`${textFactMemories.length} text facts`);
|
|
98
|
+
this.#logger.info(`[Consolidator] Per-dimension: ${dimStats.map((d) => `${d.dim}=${d.count}`).join(', ')}`);
|
|
99
|
+
this.#logger.info(`[Consolidator] Importance distribution: ${importanceDist} | Entities extracted: ${entityCount}`);
|
|
87
100
|
const result = this.#semanticMemory.consolidate(allCandidates, { bootstrapSession });
|
|
88
101
|
const durationMs = Date.now() - t0;
|
|
89
102
|
this.#logger.info(`[Consolidator] Consolidation complete in ${durationMs}ms: ` +
|
|
@@ -95,6 +108,9 @@ export class EpisodicConsolidator {
|
|
|
95
108
|
textFacts: { extracted: textFactMemories.length },
|
|
96
109
|
total: result,
|
|
97
110
|
durationMs,
|
|
111
|
+
perDimension: Object.fromEntries(dimStats.map((d) => [d.dim, d.count])),
|
|
112
|
+
importanceDistribution: this.#importanceHistogram(allCandidates),
|
|
113
|
+
entityCount,
|
|
98
114
|
};
|
|
99
115
|
}
|
|
100
116
|
// ─── 提取器 ───────────────────────────────────────────
|
|
@@ -273,6 +289,45 @@ export class EpisodicConsolidator {
|
|
|
273
289
|
return memories;
|
|
274
290
|
}
|
|
275
291
|
// ─── 辅助方法 ─────────────────────────────────────────
|
|
292
|
+
/** 按维度聚合候选数量 */
|
|
293
|
+
#computeDimStats(candidates) {
|
|
294
|
+
const counts = new Map();
|
|
295
|
+
for (const c of candidates) {
|
|
296
|
+
const dim = c.sourceDimension || 'unknown';
|
|
297
|
+
counts.set(dim, (counts.get(dim) || 0) + 1);
|
|
298
|
+
}
|
|
299
|
+
return [...counts.entries()]
|
|
300
|
+
.map(([dim, count]) => ({ dim, count }))
|
|
301
|
+
.sort((a, b) => b.count - a.count);
|
|
302
|
+
}
|
|
303
|
+
/** 生成重要性分布字符串: "[1-3]=N [4-6]=N [7-10]=N" */
|
|
304
|
+
#computeImportanceDistribution(candidates) {
|
|
305
|
+
let low = 0;
|
|
306
|
+
let mid = 0;
|
|
307
|
+
let high = 0;
|
|
308
|
+
for (const c of candidates) {
|
|
309
|
+
const imp = c.importance || 5;
|
|
310
|
+
if (imp <= 3) {
|
|
311
|
+
low++;
|
|
312
|
+
}
|
|
313
|
+
else if (imp <= 6) {
|
|
314
|
+
mid++;
|
|
315
|
+
}
|
|
316
|
+
else {
|
|
317
|
+
high++;
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
return `[1-3]=${low} [4-6]=${mid} [7-10]=${high}`;
|
|
321
|
+
}
|
|
322
|
+
/** 构建重要性直方图对象 (供返回值使用) */
|
|
323
|
+
#importanceHistogram(candidates) {
|
|
324
|
+
const hist = {};
|
|
325
|
+
for (const c of candidates) {
|
|
326
|
+
const imp = c.importance || 5;
|
|
327
|
+
hist[imp] = (hist[imp] || 0) + 1;
|
|
328
|
+
}
|
|
329
|
+
return hist;
|
|
330
|
+
}
|
|
276
331
|
/**
|
|
277
332
|
* 从文本中提取实体名 (类名/文件名/模块名)
|
|
278
333
|
*
|
|
@@ -61,6 +61,7 @@ interface CodeEntityGraphLike {
|
|
|
61
61
|
}
|
|
62
62
|
export declare const ANALYST_SYSTEM_PROMPT = "\u4F60\u662F\u4E00\u4F4D\u9AD8\u7EA7\u8F6F\u4EF6\u67B6\u6784\u5E08\uFF0C\u6B63\u5728\u6DF1\u5EA6\u5206\u6790\u4E00\u4E2A\u771F\u5B9E\u9879\u76EE\u7684\u67D0\u4E2A\u7EF4\u5EA6\u3002\n\n## \u6267\u884C\u8BA1\u5212\n\u4F60\u6709 **N \u8F6E**\u5DE5\u5177\u8C03\u7528\u673A\u4F1A\uFF08\u7CFB\u7EDF\u4F1A\u544A\u77E5\u5177\u4F53\u6570\u5B57\uFF09\u3002\u8BF7\u4E25\u683C\u6309\u4EE5\u4E0B\u8282\u594F\u5206\u914D\uFF1A\n\n| \u9636\u6BB5 | \u8F6E\u6B21\u5360\u6BD4 | \u76EE\u6807 |\n|------|---------|------|\n| 1. \u5168\u5C40\u626B\u63CF | \u7B2C 1-3 \u8F6E | get_project_overview + list_project_structure \u4E86\u89E3\u9879\u76EE\u7ED3\u6784 |\n| 2. \u7ED3\u6784\u5316\u63A2\u7D22 | \u7B2C 4-N\u00D760% \u8F6E | get_class_hierarchy / get_class_info \u7406\u89E3\u6838\u5FC3\u7C7B\uFF1Bsearch_project_code \u6279\u91CF\u641C\u7D22\u5173\u952E\u6A21\u5F0F |\n| 3. \u6DF1\u5EA6\u9A8C\u8BC1 | \u7B2C N\u00D760%-N\u00D780% \u8F6E | read_project_file \u9605\u8BFB\u5173\u952E\u5B9E\u73B0\uFF0C\u786E\u8BA4\u7EC6\u8282 |\n| 4. \u8F93\u51FA\u603B\u7ED3 | \u6700\u540E 20% | **\u505C\u6B62\u8C03\u7528\u5DE5\u5177**\uFF0C\u76F4\u63A5\u8F93\u51FA\u4F60\u7684\u5206\u6790\u6587\u672C |\n\n## \u5173\u952E\u89C4\u5219\n- **\u5230\u8FBE 80% \u8F6E\u6B21\u65F6\u5FC5\u987B\u5F00\u59CB\u5199\u603B\u7ED3**\uFF0C\u4E0D\u8981\u7B49\u7CFB\u7EDF\u63D0\u9192\n- \u6BCF\u4E00\u8F6E\u90FD\u5FC5\u987B\u8C03\u7528\u5DE5\u5177\u83B7\u53D6\u65B0\u4FE1\u606F\uFF0C\u4E0D\u8981\u82B1\u8F6E\u6B21\u5728\u7EAF\u6587\u672C\u601D\u8003\u4E0A\n- \u4E0D\u8981\u91CD\u590D\u641C\u7D22\u76F8\u540C\u5173\u952E\u8BCD\u6216\u8BFB\u53D6\u76F8\u540C\u6587\u4EF6\uFF08\u7CFB\u7EDF\u4F1A\u8FD4\u56DE\u7F13\u5B58\u5E76\u6263\u8F6E\u6B21\uFF09\n\n## \u5DE5\u5177\u6548\u7387\n- **\u6279\u91CF\u641C\u7D22**: search_project_code({ patterns: [\"keywordA\", \"keywordB\", \"keywordC\"] }) \u2014 \u4E00\u6B21\u641C 3-5 \u4E2A\n- **\u6279\u91CF\u8BFB\u6587\u4EF6**: read_project_file({ filePaths: [\"a.m\", \"b.m\", \"c.m\"] }) \u2014 \u4E00\u6B21\u8BFB 3-5 \u4E2A\n- **\u7ED3\u6784\u5316\u67E5\u8BE2\u4F18\u5148**: get_class_hierarchy / get_class_info \u6BD4\u6587\u672C\u641C\u7D22\u66F4\u7CBE\u786E\u9AD8\u6548\n\n## \u8F93\u51FA\u8981\u6C42\n\u8F93\u51FA\u4F60\u7684\u5206\u6790\u53D1\u73B0\uFF0C\u5305\u62EC\u5177\u4F53\u7684\u6587\u4EF6\u5B8C\u6574\u76F8\u5BF9\u8DEF\u5F84\uFF08\u4ECE\u9879\u76EE\u6839\u76EE\u5F55\u5F00\u59CB\uFF09\u548C\u884C\u53F7\u3002\n\u6BCF\u4E2A\u6587\u4EF6\u5F15\u7528\u683C\u5F0F: (\u6765\u6E90: Full/Relative/Path/FileName.ext:\u884C\u53F7)\n\u7981\u6B62\u53EA\u5199\u6587\u4EF6\u540D\uFF0C\u5FC5\u987B\u5199\u4ECE\u9879\u76EE\u6839\u5F00\u59CB\u7684\u5B8C\u6574\u8DEF\u5F84\u3002\n\u6807\u6CE8\u6BCF\u4E2A\u53D1\u73B0\u6240\u5C5E\u7684\u6A21\u5757/\u5305\u540D\u3002\n\u7528\u81EA\u7136\u8BED\u8A00\u63CF\u8FF0\u4F60\u7684\u7406\u89E3\uFF0C\u4E0D\u9700\u8981\u7279\u5B9A\u683C\u5F0F\u3002";
|
|
63
63
|
export declare const ANALYST_TOOLS: string[];
|
|
64
|
+
/** 默认 Analyst 预算(24 轮基线) */
|
|
64
65
|
export declare const ANALYST_BUDGET: {
|
|
65
66
|
maxIterations: number;
|
|
66
67
|
searchBudget: number;
|
|
@@ -69,6 +70,21 @@ export declare const ANALYST_BUDGET: {
|
|
|
69
70
|
softSubmitLimit: number;
|
|
70
71
|
idleRoundsToExit: number;
|
|
71
72
|
};
|
|
73
|
+
/**
|
|
74
|
+
* 根据项目规模自适应计算 Analyst 预算
|
|
75
|
+
*
|
|
76
|
+
* 策略: 以文件数为主要缩放因子,保持 searchBudget/maxIterations 的比例关系。
|
|
77
|
+
* - ≤40 文件: 基线 24 轮(小型项目无需额外预算)
|
|
78
|
+
* - 41~100 文件: 线性插值到 32 轮
|
|
79
|
+
* - 101~200 文件: 线性插值到 40 轮
|
|
80
|
+
* - >200 文件: 封顶 40 轮(避免单维度成本失控)
|
|
81
|
+
*
|
|
82
|
+
* searchBudget 按比例随 maxIterations 缩放(保持 75%)。
|
|
83
|
+
* timeoutMs 按比例随 maxIterations 缩放(基线 300s 对应 24 轮)。
|
|
84
|
+
*/
|
|
85
|
+
export declare function computeAnalystBudget(fileCount: number): typeof ANALYST_BUDGET & {
|
|
86
|
+
timeoutMs: number;
|
|
87
|
+
};
|
|
72
88
|
/** Panorama context — module role, layer, coupling, gaps */
|
|
73
89
|
interface PanoramaContextLike {
|
|
74
90
|
moduleRole: string | null;
|
|
@@ -72,6 +72,7 @@ export const ANALYST_TOOLS = [
|
|
|
72
72
|
// ──────────────────────────────────────────────────────────────────
|
|
73
73
|
// Analyst 预算 — 使用 analyst 策略(自由探索,无阶段约束)
|
|
74
74
|
// ──────────────────────────────────────────────────────────────────
|
|
75
|
+
/** 默认 Analyst 预算(24 轮基线) */
|
|
75
76
|
export const ANALYST_BUDGET = {
|
|
76
77
|
maxIterations: 24,
|
|
77
78
|
searchBudget: 18,
|
|
@@ -80,6 +81,43 @@ export const ANALYST_BUDGET = {
|
|
|
80
81
|
softSubmitLimit: 0,
|
|
81
82
|
idleRoundsToExit: 2,
|
|
82
83
|
};
|
|
84
|
+
/**
|
|
85
|
+
* 根据项目规模自适应计算 Analyst 预算
|
|
86
|
+
*
|
|
87
|
+
* 策略: 以文件数为主要缩放因子,保持 searchBudget/maxIterations 的比例关系。
|
|
88
|
+
* - ≤40 文件: 基线 24 轮(小型项目无需额外预算)
|
|
89
|
+
* - 41~100 文件: 线性插值到 32 轮
|
|
90
|
+
* - 101~200 文件: 线性插值到 40 轮
|
|
91
|
+
* - >200 文件: 封顶 40 轮(避免单维度成本失控)
|
|
92
|
+
*
|
|
93
|
+
* searchBudget 按比例随 maxIterations 缩放(保持 75%)。
|
|
94
|
+
* timeoutMs 按比例随 maxIterations 缩放(基线 300s 对应 24 轮)。
|
|
95
|
+
*/
|
|
96
|
+
export function computeAnalystBudget(fileCount) {
|
|
97
|
+
const clamped = Math.max(0, fileCount);
|
|
98
|
+
let maxIter;
|
|
99
|
+
if (clamped <= 40) {
|
|
100
|
+
maxIter = 24;
|
|
101
|
+
}
|
|
102
|
+
else if (clamped <= 100) {
|
|
103
|
+
// 40→100 文件: 24→32 轮(线性插值)
|
|
104
|
+
maxIter = Math.round(24 + ((clamped - 40) / 60) * 8);
|
|
105
|
+
}
|
|
106
|
+
else if (clamped <= 200) {
|
|
107
|
+
// 100→200 文件: 32→40 轮
|
|
108
|
+
maxIter = Math.round(32 + ((clamped - 100) / 100) * 8);
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
maxIter = 40;
|
|
112
|
+
}
|
|
113
|
+
return {
|
|
114
|
+
...ANALYST_BUDGET,
|
|
115
|
+
maxIterations: maxIter,
|
|
116
|
+
searchBudget: Math.round(maxIter * 0.75),
|
|
117
|
+
// 超时随轮次等比缩放: 24轮→300s, 40轮→500s
|
|
118
|
+
timeoutMs: Math.round((maxIter / 24) * 300_000),
|
|
119
|
+
};
|
|
120
|
+
}
|
|
83
121
|
/**
|
|
84
122
|
* 构建 Analyst Prompt
|
|
85
123
|
*
|
|
@@ -14,7 +14,9 @@
|
|
|
14
14
|
*
|
|
15
15
|
* @module insight-gate
|
|
16
16
|
*/
|
|
17
|
+
import Logger from '#infra/logging/Logger.js';
|
|
17
18
|
import { EvidenceCollector, } from './EvidenceCollector.js';
|
|
19
|
+
const logger = Logger.getInstance();
|
|
18
20
|
// ──────────────────────────────────────────────────────────────────
|
|
19
21
|
// AnalysisReport 构建
|
|
20
22
|
// ──────────────────────────────────────────────────────────────────
|
|
@@ -376,6 +378,16 @@ export function insightGateEvaluator(source, phaseResults, strategyContext = {})
|
|
|
376
378
|
? buildAnalysisArtifact(source, dimId, projectGraph, activeContext)
|
|
377
379
|
: buildAnalysisReport(source, dimId, projectGraph);
|
|
378
380
|
const gate = analysisQualityGate(artifact, { outputType: outputType || 'analysis' });
|
|
381
|
+
const qr = artifact.qualityReport;
|
|
382
|
+
if (qr?.scores) {
|
|
383
|
+
logger.info(`[QualityGate] dim="${dimId}" action=${gate.pass ? 'pass' : gate.action} ` +
|
|
384
|
+
`total=${qr.totalScore} depth=${qr.scores.depthScore} breadth=${qr.scores.breadthScore} ` +
|
|
385
|
+
`evidence=${qr.scores.evidenceScore} coherence=${qr.scores.coherenceScore}` +
|
|
386
|
+
(qr.suggestions.length > 0 ? ` suggestions=[${qr.suggestions.join('; ')}]` : ''));
|
|
387
|
+
}
|
|
388
|
+
else {
|
|
389
|
+
logger.info(`[QualityGate] dim="${dimId}" action=${gate.pass ? 'pass' : gate.action} reason="${gate.reason || 'v1-rules'}" (v1 fallback)`);
|
|
390
|
+
}
|
|
379
391
|
return {
|
|
380
392
|
action: gate.action || (gate.pass ? 'pass' : 'retry'),
|
|
381
393
|
reason: gate.reason || '',
|
|
@@ -17,6 +17,8 @@ import { MemoryStore } from './MemoryStore.js';
|
|
|
17
17
|
/** 相似度阈值 */
|
|
18
18
|
const SIMILARITY_UPDATE = 0.85; // ≥85% 同义 → UPDATE
|
|
19
19
|
const SIMILARITY_MERGE = 0.6; // ≥60% 相关 → MERGE
|
|
20
|
+
/** 详细日志开关 (合并时记录每次 MERGE/UPDATE/REPLACE 的内容摘要) */
|
|
21
|
+
const VERBOSE_CONSOLIDATION = true;
|
|
20
22
|
// ─── 矛盾检测模式 (Mem0 风格冲突解决) ─────────────────
|
|
21
23
|
/** 中文否定/禁止模式 */
|
|
22
24
|
const NEGATION_PATTERNS_ZH = /不(再)?使用|不(再)?用|禁止|废弃|移除|取消|停止|不要|不采用|弃用|淘汰/;
|
|
@@ -68,6 +70,9 @@ export class MemoryConsolidator {
|
|
|
68
70
|
accessCount: topMatch.access_count + 1,
|
|
69
71
|
});
|
|
70
72
|
stats.updated++;
|
|
73
|
+
if (VERBOSE_CONSOLIDATION) {
|
|
74
|
+
this.#logDebug(`UPDATE sim=${(topMatch.similarity ?? 0).toFixed(2)}: "${content.substring(0, 40)}..." → existing "${topMatch.content.substring(0, 40)}..."`);
|
|
75
|
+
}
|
|
71
76
|
}
|
|
72
77
|
else if ((topMatch.similarity ?? 0) >= SIMILARITY_MERGE) {
|
|
73
78
|
// MERGE: 相关但不同 → 合并信息
|
|
@@ -79,6 +84,9 @@ export class MemoryConsolidator {
|
|
|
79
84
|
relatedMemories: [...existingRelated, `merged:${Date.now()}`],
|
|
80
85
|
});
|
|
81
86
|
stats.merged++;
|
|
87
|
+
if (VERBOSE_CONSOLIDATION) {
|
|
88
|
+
this.#logDebug(`MERGE sim=${(topMatch.similarity ?? 0).toFixed(2)}: "${content.substring(0, 40)}..." ⊕ "${topMatch.content.substring(0, 40)}..."`);
|
|
89
|
+
}
|
|
82
90
|
}
|
|
83
91
|
else {
|
|
84
92
|
this.#store.add({ ...candidate, bootstrapSession });
|
|
@@ -299,6 +307,15 @@ export class MemoryConsolidator {
|
|
|
299
307
|
return 'fact';
|
|
300
308
|
}
|
|
301
309
|
}
|
|
310
|
+
#logDebug(msg) {
|
|
311
|
+
const formatted = `[MemoryConsolidator] ${msg}`;
|
|
312
|
+
if (this.#logger?.debug) {
|
|
313
|
+
this.#logger.debug(formatted);
|
|
314
|
+
}
|
|
315
|
+
else if (this.#logger?.info) {
|
|
316
|
+
this.#logger.info(formatted);
|
|
317
|
+
}
|
|
318
|
+
}
|
|
302
319
|
#log(msg) {
|
|
303
320
|
const formatted = `[MemoryConsolidator] ${msg}`;
|
|
304
321
|
if (this.#logger?.info) {
|
package/dist/lib/bootstrap.js
CHANGED
|
@@ -44,7 +44,12 @@ export class Bootstrap {
|
|
|
44
44
|
// 0.5 确保 PathGuard 已配置(如果调用方未提前配置)
|
|
45
45
|
// MCP 服务器会在 initialize() 之前配置,但 CLI/测试可能跳过
|
|
46
46
|
if (!pathGuard.configured) {
|
|
47
|
-
const
|
|
47
|
+
const isMcpMode = process.env.ASD_MCP_MODE === '1';
|
|
48
|
+
const projectRoot = process.env.ASD_PROJECT_DIR || (isMcpMode ? undefined : process.cwd());
|
|
49
|
+
if (!projectRoot) {
|
|
50
|
+
throw new Error('[Bootstrap] MCP 模式下缺少 ASD_PROJECT_DIR 环境变量,' +
|
|
51
|
+
'且 PathGuard 未提前配置。请在 .vscode/mcp.json 中设置 ASD_PROJECT_DIR。');
|
|
52
|
+
}
|
|
48
53
|
Bootstrap.configurePathGuard(projectRoot);
|
|
49
54
|
}
|
|
50
55
|
// 1. 加载配置
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
import { execSync } from 'node:child_process';
|
|
47
47
|
import { copyFileSync, cpSync, existsSync, mkdirSync, readdirSync, renameSync, rmdirSync, rmSync, writeFileSync, } from 'node:fs';
|
|
48
48
|
import { join, resolve } from 'node:path';
|
|
49
|
-
import {
|
|
49
|
+
import { isExcludedProject } from '../shared/isOwnDevRepo.js';
|
|
50
50
|
import { DEFAULT_KNOWLEDGE_BASE_DIR, DEFAULT_SUB_REPO_DIR, isGitRepo, } from '../shared/ProjectMarkers.js';
|
|
51
51
|
import { PACKAGE_ROOT } from '../shared/package-root.js';
|
|
52
52
|
import { FileDeployer } from './deploy/FileDeployer.js';
|
|
@@ -81,9 +81,10 @@ export class SetupService {
|
|
|
81
81
|
this.seed = options.seed || false;
|
|
82
82
|
this.subRepoDir = options.subRepoDir || DEFAULT_SUB_REPO_DIR;
|
|
83
83
|
this.subRepoUrl = options.subRepoUrl;
|
|
84
|
-
// ──
|
|
85
|
-
|
|
86
|
-
|
|
84
|
+
// ── 排除项目保护 ──────────────────────────────────
|
|
85
|
+
const exclusion = isExcludedProject(this.projectRoot);
|
|
86
|
+
if (exclusion.excluded) {
|
|
87
|
+
throw new Error(`[SetupService] 检测到当前目录是排除项目(${exclusion.reason}),` +
|
|
87
88
|
'拒绝执行 setup 以避免创建 .autosnippet/ 和 AutoSnippet/ 运行时数据。' +
|
|
88
89
|
'\n提示: 请在用户项目目录中运行 asd setup。');
|
|
89
90
|
}
|
|
@@ -38,10 +38,12 @@ export declare function resolveActiveDimensions(primaryLang: string, detectedFra
|
|
|
38
38
|
/**
|
|
39
39
|
* 构建 Tier 分层调度计划
|
|
40
40
|
*
|
|
41
|
-
* 基于每个维度的 tierHint
|
|
42
|
-
* -
|
|
43
|
-
* -
|
|
44
|
-
* -
|
|
41
|
+
* 基于每个维度的 tierHint 字段动态分为 N 层 (不再硬编码 3 层):
|
|
42
|
+
* - tierHint=1: 基础数据层 — architecture + 语言/框架条件维度
|
|
43
|
+
* - tierHint=2: 规范+设计层 — coding-standards, design-patterns 等
|
|
44
|
+
* - tierHint=3+: 实践+质量层 — 按声明值自动分桶
|
|
45
|
+
*
|
|
46
|
+
* 未声明 tierHint 的维度默认归入最后一层 (tierHint=max 或 3)。
|
|
45
47
|
*/
|
|
46
48
|
export declare function buildTierPlan(activeDims?: readonly UnifiedDimension[]): string[][];
|
|
47
49
|
/**
|