autosnippet 3.3.9 → 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.
Files changed (33) hide show
  1. package/README.md +1 -1
  2. package/config/default.json +1 -1
  3. package/dashboard/dist/assets/{index-DEU4tJtP.js → index-8b1Gf3Bb.js} +1 -1
  4. package/dashboard/dist/index.html +1 -1
  5. package/dist/lib/agent/AgentRuntime.js +13 -1
  6. package/dist/lib/agent/AgentRuntimeTypes.d.ts +2 -0
  7. package/dist/lib/agent/PipelineStrategy.js +32 -2
  8. package/dist/lib/agent/context/ContextWindow.d.ts +2 -1
  9. package/dist/lib/agent/context/ContextWindow.js +18 -4
  10. package/dist/lib/agent/context/ExplorationTracker.js +6 -1
  11. package/dist/lib/agent/context/exploration/ExplorationStrategies.js +2 -1
  12. package/dist/lib/agent/core/LoopContext.d.ts +3 -0
  13. package/dist/lib/agent/core/LoopContext.js +3 -0
  14. package/dist/lib/agent/domain/EpisodicConsolidator.d.ts +5 -0
  15. package/dist/lib/agent/domain/EpisodicConsolidator.js +60 -5
  16. package/dist/lib/agent/domain/insight-analyst.d.ts +16 -0
  17. package/dist/lib/agent/domain/insight-analyst.js +38 -0
  18. package/dist/lib/agent/domain/insight-gate.js +12 -0
  19. package/dist/lib/agent/memory/MemoryConsolidator.js +17 -0
  20. package/dist/lib/domain/dimension/DimensionRegistry.d.ts +6 -4
  21. package/dist/lib/domain/dimension/DimensionRegistry.js +19 -23
  22. package/dist/lib/external/ai/AiProvider.d.ts +2 -0
  23. package/dist/lib/external/ai/AiProvider.js +4 -0
  24. package/dist/lib/external/ai/providers/ClaudeProvider.d.ts +1 -1
  25. package/dist/lib/external/ai/providers/ClaudeProvider.js +6 -2
  26. package/dist/lib/external/ai/providers/GoogleGeminiProvider.d.ts +1 -1
  27. package/dist/lib/external/ai/providers/GoogleGeminiProvider.js +6 -2
  28. package/dist/lib/external/ai/providers/OpenAiProvider.d.ts +1 -1
  29. package/dist/lib/external/ai/providers/OpenAiProvider.js +7 -3
  30. package/dist/lib/external/mcp/handlers/bootstrap/MissionBriefingBuilder.js +10 -2
  31. package/dist/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +37 -4
  32. package/dist/lib/http/routes/ai.js +2 -2
  33. package/package.json +1 -1
@@ -32,6 +32,6 @@ export declare class ClaudeProvider extends AiProvider {
32
32
  summarize(code: string): Promise<{}>;
33
33
  embed(_text: string | string[]): Promise<never[]>;
34
34
  supportsEmbedding(): boolean;
35
- _post(url: string, body: Record<string, unknown>): Promise<ApiResponse>;
35
+ _post(url: string, body: Record<string, unknown>, externalSignal?: AbortSignal): Promise<ApiResponse>;
36
36
  }
37
37
  export default ClaudeProvider;
@@ -100,7 +100,7 @@ export class ClaudeProvider extends AiProvider {
100
100
  body.tool_choice = { type: 'auto' };
101
101
  }
102
102
  }
103
- const data = await this._post(`${CLAUDE_BASE}/messages`, body);
103
+ const data = await this._post(`${CLAUDE_BASE}/messages`, body, opts.abortSignal);
104
104
  return this.#parseToolResponse(data);
105
105
  }
106
106
  // ─── 内部转换方法 ──────────────────────
@@ -241,7 +241,7 @@ export class ClaudeProvider extends AiProvider {
241
241
  supportsEmbedding() {
242
242
  return false;
243
243
  }
244
- async _post(url, body) {
244
+ async _post(url, body, externalSignal) {
245
245
  if (!this.apiKey) {
246
246
  const err = new Error('Claude API Key 未配置。请在 .env 中设置 ASD_CLAUDE_API_KEY,或运行 asd setup 完成配置。');
247
247
  err.code = 'API_KEY_MISSING';
@@ -249,6 +249,9 @@ export class ClaudeProvider extends AiProvider {
249
249
  }
250
250
  const controller = new AbortController();
251
251
  const timer = setTimeout(() => controller.abort(), this.timeout);
252
+ // 外部中止信号 → 联动本地 controller
253
+ const onExternalAbort = () => controller.abort();
254
+ externalSignal?.addEventListener('abort', onExternalAbort, { once: true });
252
255
  try {
253
256
  const res = await this._fetch(url, {
254
257
  method: 'POST',
@@ -269,6 +272,7 @@ export class ClaudeProvider extends AiProvider {
269
272
  }
270
273
  finally {
271
274
  clearTimeout(timer);
275
+ externalSignal?.removeEventListener('abort', onExternalAbort);
272
276
  }
273
277
  }
274
278
  }
@@ -34,6 +34,6 @@ export declare class GoogleGeminiProvider extends AiProvider {
34
34
  */
35
35
  chatWithStructuredOutput(prompt: string, opts?: StructuredOutputOptions): Promise<any>;
36
36
  embed(text: string | string[]): Promise<number[] | number[][]>;
37
- _post(url: string, body: Record<string, unknown>): Promise<ApiResponse>;
37
+ _post(url: string, body: Record<string, unknown>, externalSignal?: AbortSignal): Promise<ApiResponse>;
38
38
  }
39
39
  export default GoogleGeminiProvider;
@@ -108,7 +108,7 @@ export class GoogleGeminiProvider extends AiProvider {
108
108
  body.systemInstruction = { parts: [{ text: systemPrompt }] };
109
109
  }
110
110
  const url = `${GEMINI_BASE}/models/${this.model}:generateContent?key=${this.apiKey}`;
111
- const data = await this._post(url, body);
111
+ const data = await this._post(url, body, opts.abortSignal);
112
112
  return this.#parseToolResponse(data);
113
113
  });
114
114
  }
@@ -356,7 +356,7 @@ export class GoogleGeminiProvider extends AiProvider {
356
356
  }
357
357
  return Array.isArray(text) ? results : results[0] || [];
358
358
  }
359
- async _post(url, body) {
359
+ async _post(url, body, externalSignal) {
360
360
  if (!this.apiKey) {
361
361
  const err = new Error('Google Gemini API Key 未配置。请在 .env 中设置 ASD_GOOGLE_API_KEY,或运行 asd setup 完成配置。');
362
362
  err.code = 'API_KEY_MISSING';
@@ -364,6 +364,9 @@ export class GoogleGeminiProvider extends AiProvider {
364
364
  }
365
365
  const controller = new AbortController();
366
366
  const timer = setTimeout(() => controller.abort(), this.timeout);
367
+ // 外部中止信号 → 联动本地 controller
368
+ const onExternalAbort = () => controller.abort();
369
+ externalSignal?.addEventListener('abort', onExternalAbort, { once: true });
367
370
  try {
368
371
  const res = await this._fetch(url, {
369
372
  method: 'POST',
@@ -401,6 +404,7 @@ export class GoogleGeminiProvider extends AiProvider {
401
404
  }
402
405
  finally {
403
406
  clearTimeout(timer);
407
+ externalSignal?.removeEventListener('abort', onExternalAbort);
404
408
  }
405
409
  }
406
410
  }
@@ -37,6 +37,6 @@ export declare class OpenAiProvider extends AiProvider {
37
37
  */
38
38
  chatWithStructuredOutput(prompt: string, opts?: StructuredOutputOptions): Promise<any>;
39
39
  embed(text: string | string[]): Promise<any>;
40
- _post(url: string, body: Record<string, unknown>): Promise<ApiResponse>;
40
+ _post(url: string, body: Record<string, unknown>, externalSignal?: AbortSignal): Promise<ApiResponse>;
41
41
  }
42
42
  export default OpenAiProvider;
@@ -14,7 +14,7 @@ export class OpenAiProvider extends AiProvider {
14
14
  constructor(config = {}) {
15
15
  super(config);
16
16
  this.name = config.name || 'openai';
17
- this.model = config.model || 'gpt-4o-mini';
17
+ this.model = config.model || 'gpt-5.4-mini';
18
18
  this.apiKey = config.apiKey || process.env.ASD_OPENAI_API_KEY || '';
19
19
  this.baseUrl = config.baseUrl || OPENAI_BASE;
20
20
  this.embedModel = config.embedModel || 'text-embedding-3-small';
@@ -129,7 +129,7 @@ export class OpenAiProvider extends AiProvider {
129
129
  else {
130
130
  body.tool_choice = 'auto';
131
131
  }
132
- const data = await this._post(`${this.baseUrl}/chat/completions`, body);
132
+ const data = await this._post(`${this.baseUrl}/chat/completions`, body, opts.abortSignal);
133
133
  return this.#parseToolResponse(data);
134
134
  });
135
135
  }
@@ -251,7 +251,7 @@ export class OpenAiProvider extends AiProvider {
251
251
  return Array.isArray(text) ? texts.map(() => []) : [];
252
252
  }
253
253
  }
254
- async _post(url, body) {
254
+ async _post(url, body, externalSignal) {
255
255
  // Ollama 使用固定 dummy key,不需要校验
256
256
  if (!this.apiKey && this.name !== 'ollama') {
257
257
  const envKey = this.name === 'deepseek' ? 'ASD_DEEPSEEK_API_KEY' : 'ASD_OPENAI_API_KEY';
@@ -261,6 +261,9 @@ export class OpenAiProvider extends AiProvider {
261
261
  }
262
262
  const controller = new AbortController();
263
263
  const timer = setTimeout(() => controller.abort(), this.timeout);
264
+ // 外部中止信号 → 联动本地 controller
265
+ const onExternalAbort = () => controller.abort();
266
+ externalSignal?.addEventListener('abort', onExternalAbort, { once: true });
264
267
  try {
265
268
  const res = await this._fetch(url, {
266
269
  method: 'POST',
@@ -280,6 +283,7 @@ export class OpenAiProvider extends AiProvider {
280
283
  }
281
284
  finally {
282
285
  clearTimeout(timer);
286
+ externalSignal?.removeEventListener('abort', onExternalAbort);
283
287
  }
284
288
  }
285
289
  }
@@ -760,11 +760,19 @@ function buildExecutionPlan(activeDimensions) {
760
760
  const scheduler = new TierScheduler();
761
761
  const tiers = scheduler.getTiers();
762
762
  const activeDimIds = new Set(activeDimensions.map((d) => d.id));
763
- const tierLabels = ['基础数据层', '规范 + 架构 + 模式', '流转 + 实践 + 总结'];
763
+ const tierLabels = [
764
+ '基础数据层',
765
+ '规范 + 设计 + 网络',
766
+ '核心质量',
767
+ '领域专项',
768
+ '终端优化 + 总结',
769
+ ];
764
770
  const tierNotes = [
765
771
  '这些维度相互独立,可以任意顺序分析。产出的上下文将帮助后续维度。',
766
772
  '建议利用 Tier 1 中了解到的项目结构和代码特征。',
767
- 'agent-guidelines 应在最后分析 — 综合前序所有维度的发现。',
773
+ '利用前两层建立的架构和规范上下文深入分析。',
774
+ '各维度相对独立,可充分利用并行能力。',
775
+ 'agent-guidelines 应综合前序所有维度的发现。',
768
776
  ];
769
777
  const plan = tiers
770
778
  .map((tierDimIds, index) => {
@@ -18,7 +18,7 @@ import path from 'node:path';
18
18
  import { AgentMessage } from '#agent/AgentMessage.js';
19
19
  import { ExplorationTracker } from '#agent/context/ExplorationTracker.js';
20
20
  import { EpisodicConsolidator } from '#agent/domain/EpisodicConsolidator.js';
21
- import { ANALYST_BUDGET } from '#agent/domain/insight-analyst.js';
21
+ import { computeAnalystBudget } from '#agent/domain/insight-analyst.js';
22
22
  import { MemoryCoordinator } from '#agent/memory/MemoryCoordinator.js';
23
23
  import { MemoryEmbeddingStore } from '#agent/memory/MemoryEmbeddingStore.js';
24
24
  import { PersistentMemory } from '#agent/memory/PersistentMemory.js';
@@ -600,9 +600,9 @@ export async function fillDimensionsV3(view, dimensions) {
600
600
  // ── 引擎增强参数 (PipelineStrategy → reactLoop 透传) ──
601
601
  contextWindow: agentFactory?.createContextWindow({ isSystem: true }),
602
602
  // B1 fix: 分析阶段使用 analyst 策略 (SCAN→EXPLORE→VERIFY→SUMMARIZE)
603
- // 而非 bootstrap (EXPLORE→PRODUCE→SUMMARIZE),避免 PRODUCE nudge 浪费轮次
604
- // B3 fix: 透传完整 ANALYST_BUDGET (searchBudget/maxSubmits/softSubmitLimit/idleRoundsToExit)
605
- tracker: ExplorationTracker.resolve({ source: 'system', strategy: 'analyst' }, { ...ANALYST_BUDGET }),
603
+ // B3 fix: 透传完整 ANALYST_BUDGET
604
+ // B4 fix: 自适应预算 根据项目文件数缩放 maxIterations (24~40)
605
+ tracker: ExplorationTracker.resolve({ source: 'system', strategy: 'analyst' }, computeAnalystBudget(projectInfo.fileCount || 0)),
606
606
  trace: memoryCoordinator.getActiveContext(analystScopeId),
607
607
  memoryCoordinator,
608
608
  sharedState: {
@@ -826,6 +826,7 @@ export async function fillDimensionsV3(view, dimensions) {
826
826
  source: 'enhanced-pipeline-strategy',
827
827
  });
828
828
  const dimTokenUsage = combinedTokenUsage;
829
+ const qualityScores = artifact.qualityReport;
829
830
  const dimResult = {
830
831
  candidateCount: producerResult.candidateCount,
831
832
  rejectedCount: producerResult.rejectedCount || 0,
@@ -836,6 +837,13 @@ export async function fillDimensionsV3(view, dimensions) {
836
837
  tokenUsage: dimTokenUsage,
837
838
  analysisText: analysisReport.analysisText,
838
839
  referencedFilesList: analysisReport.referencedFiles || [],
840
+ qualityGate: qualityScores
841
+ ? {
842
+ totalScore: qualityScores.totalScore,
843
+ scores: qualityScores.scores,
844
+ action: gateResult?.action || (runResult?.degraded ? 'degrade' : 'pass'),
845
+ }
846
+ : null,
839
847
  };
840
848
  dimensionStats[dimId] = dimResult;
841
849
  // P3: 保存 checkpoint — 仅当有实质分析内容时(避免 degraded/空结果污染后续 run)
@@ -1061,6 +1069,30 @@ export async function fillDimensionsV3(view, dimensions) {
1061
1069
  `~${consolidationResult.total.updated} UPDATE, ` +
1062
1070
  `⊕${consolidationResult.total.merged} MERGE | ` +
1063
1071
  `Total: ${smStats.total} memories (avg importance: ${smStats.avgImportance})`);
1072
+ // 按类型和来源分布
1073
+ logger.info(`[Insight-v3] Memory by type: ${Object.entries(smStats.byType)
1074
+ .map(([t, n]) => `${t}=${n}`)
1075
+ .join(', ')} | ` +
1076
+ `by source: ${Object.entries(smStats.bySource)
1077
+ .map(([s, n]) => `${s}=${n}`)
1078
+ .join(', ')}`);
1079
+ // 蒸馏管线详情 (来自 EpisodicConsolidator 增强返回值)
1080
+ const cr = consolidationResult;
1081
+ if (cr.perDimension) {
1082
+ const perDim = cr.perDimension;
1083
+ const topDims = Object.entries(perDim)
1084
+ .sort(([, a], [, b]) => b - a)
1085
+ .slice(0, 6);
1086
+ logger.info(`[Insight-v3] Memory per-dimension (top ${topDims.length}): ${topDims.map(([d, n]) => `${d}=${n}`).join(', ')}`);
1087
+ }
1088
+ if (cr.importanceDistribution) {
1089
+ const hist = cr.importanceDistribution;
1090
+ const histStr = Object.entries(hist)
1091
+ .sort(([a], [b]) => Number(a) - Number(b))
1092
+ .map(([k, v]) => `imp${k}=${v}`)
1093
+ .join(' ');
1094
+ logger.info(`[Insight-v3] Importance histogram: ${histStr}`);
1095
+ }
1064
1096
  }
1065
1097
  else {
1066
1098
  logger.warn('[Insight-v3] Database not available — skipping Semantic Memory consolidation');
@@ -1163,6 +1195,7 @@ export async function fillDimensionsV3(view, dimensions) {
1163
1195
  durationMs: stat.durationMs || 0,
1164
1196
  toolCallCount: stat.toolCallCount || 0,
1165
1197
  tokenUsage: stat.tokenUsage || { input: 0, output: 0 },
1198
+ qualityGate: stat.qualityGate || null,
1166
1199
  };
1167
1200
  }
1168
1201
  // Phase E: 附加 Code Entity Graph 拓扑到报告
@@ -70,9 +70,9 @@ router.get('/providers', async (req, res) => {
70
70
  };
71
71
  const providers = [
72
72
  { id: 'google', label: 'Google Gemini', defaultModel: 'gemini-3-flash-preview' },
73
- { id: 'openai', label: 'OpenAI', defaultModel: 'gpt-4o' },
73
+ { id: 'openai', label: 'OpenAI', defaultModel: 'gpt-5.4' },
74
74
  { id: 'deepseek', label: 'DeepSeek', defaultModel: 'deepseek-chat' },
75
- { id: 'claude', label: 'Claude', defaultModel: 'claude-3-5-sonnet-20240620' },
75
+ { id: 'claude', label: 'Claude', defaultModel: 'claude-sonnet-4-20250514' },
76
76
  { id: 'ollama', label: 'Ollama', defaultModel: 'llama3' },
77
77
  { id: 'mock', label: 'Mock (测试)', defaultModel: 'mock-l3' },
78
78
  ].map((p) => ({
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "autosnippet",
3
- "version": "3.3.9",
3
+ "version": "3.4.0",
4
4
  "description": "Extract code patterns into a knowledge base for AI coding assistants",
5
5
  "type": "module",
6
6
  "main": "dist/lib/bootstrap.js",