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
|
@@ -20,6 +20,51 @@ export class AiProvider {
|
|
|
20
20
|
this._circuitThreshold = config.circuitThreshold || 5; // 触发熔断的连续失败次数
|
|
21
21
|
this._circuitOpenedAt = 0; // 熔断打开时间
|
|
22
22
|
this._circuitCooldownMs = 30_000; // 初始冷却 30 秒
|
|
23
|
+
|
|
24
|
+
// ── Provider 级全局并发闸门 + 429 冷却窗 ──
|
|
25
|
+
this._maxConcurrency = Math.max(1, Number(config.maxConcurrency || process.env.ASD_AI_MAX_CONCURRENCY || 4));
|
|
26
|
+
this._activeRequests = 0;
|
|
27
|
+
this._requestQueue = [];
|
|
28
|
+
this._rateLimitedUntil = 0;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
async _acquireRequestSlot() {
|
|
32
|
+
if (this._activeRequests < this._maxConcurrency) {
|
|
33
|
+
this._activeRequests += 1;
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
await new Promise((resolve) => this._requestQueue.push(resolve));
|
|
37
|
+
this._activeRequests += 1;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
_releaseRequestSlot() {
|
|
41
|
+
this._activeRequests = Math.max(0, this._activeRequests - 1);
|
|
42
|
+
const next = this._requestQueue.shift();
|
|
43
|
+
if (next) {
|
|
44
|
+
next();
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async _waitForRateLimitWindow() {
|
|
49
|
+
const waitMs = (this._rateLimitedUntil || 0) - Date.now();
|
|
50
|
+
if (waitMs > 0) {
|
|
51
|
+
await new Promise((r) => setTimeout(r, waitMs));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
_setRateLimitWindow(waitMs) {
|
|
56
|
+
const safeWait = Math.max(0, Number(waitMs) || 0);
|
|
57
|
+
if (safeWait <= 0) {
|
|
58
|
+
return;
|
|
59
|
+
}
|
|
60
|
+
const until = Date.now() + safeWait;
|
|
61
|
+
if (until > (this._rateLimitedUntil || 0)) {
|
|
62
|
+
this._rateLimitedUntil = until;
|
|
63
|
+
this._log?.(
|
|
64
|
+
'warn',
|
|
65
|
+
`[RateLimit] ${this.name} enters cooldown ${Math.round(safeWait / 1000)}s (global)`
|
|
66
|
+
);
|
|
67
|
+
}
|
|
23
68
|
}
|
|
24
69
|
|
|
25
70
|
/**
|
|
@@ -81,9 +126,9 @@ export class AiProvider {
|
|
|
81
126
|
* 带工具声明的结构化对话 — 原生函数调用 API
|
|
82
127
|
*
|
|
83
128
|
* 支持原生函数调用的 Provider(Gemini / OpenAI / Claude)覆盖此方法,
|
|
84
|
-
* 返回结构化 functionCall 而非文本,
|
|
129
|
+
* 返回结构化 functionCall 而非文本,AgentRuntime 据此跳过正则解析。
|
|
85
130
|
*
|
|
86
|
-
* 默认实现降级为 chat(),由
|
|
131
|
+
* 默认实现降级为 chat(),由 AgentRuntime 进行文本解析。
|
|
87
132
|
*
|
|
88
133
|
* 统一消息格式 (Provider-Agnostic):
|
|
89
134
|
* - { role: 'user', content: 'text' }
|
|
@@ -150,35 +195,6 @@ export class AiProvider {
|
|
|
150
195
|
return this.extractJSON(response, openChar, closeChar);
|
|
151
196
|
}
|
|
152
197
|
|
|
153
|
-
/**
|
|
154
|
-
* 从源码文件批量提取 Recipe 结构(AI 驱动)
|
|
155
|
-
* 默认实现使用 chat() + 标准提示词;子类可覆盖以使用专用 API
|
|
156
|
-
* @param {string} targetName - SPM Target 名称
|
|
157
|
-
* @param {Array<{name:string,content:string}>} filesContent
|
|
158
|
-
* @param {object} [options] - 可选参数
|
|
159
|
-
* @returns {Promise<Array<object>>}
|
|
160
|
-
*/
|
|
161
|
-
async extractRecipes(targetName, filesContent, options = {}) {
|
|
162
|
-
const prompt = this._buildExtractPrompt(targetName, filesContent, options);
|
|
163
|
-
const parsed = await this.chatWithStructuredOutput(prompt, {
|
|
164
|
-
openChar: '[',
|
|
165
|
-
closeChar: ']',
|
|
166
|
-
temperature: 0.3,
|
|
167
|
-
maxTokens: 32768,
|
|
168
|
-
});
|
|
169
|
-
if (!Array.isArray(parsed)) {
|
|
170
|
-
this._log(
|
|
171
|
-
'warn',
|
|
172
|
-
`[extractRecipes] structured output parse failed for target: ${targetName}`
|
|
173
|
-
);
|
|
174
|
-
return [];
|
|
175
|
-
}
|
|
176
|
-
if (parsed.length === 0) {
|
|
177
|
-
this._log('info', `[extractRecipes] AI returned empty array for target: ${targetName}`);
|
|
178
|
-
}
|
|
179
|
-
return parsed;
|
|
180
|
-
}
|
|
181
|
-
|
|
182
198
|
/**
|
|
183
199
|
* 内部日志辅助(子类可通过 this.logger 覆盖)
|
|
184
200
|
*/
|
|
@@ -193,147 +209,6 @@ export class AiProvider {
|
|
|
193
209
|
}
|
|
194
210
|
}
|
|
195
211
|
|
|
196
|
-
/**
|
|
197
|
-
* 构建 extractRecipes 标准提示词(语言自适应 + Skill 增强)
|
|
198
|
-
*/
|
|
199
|
-
_buildExtractPrompt(targetName, filesContent, options = {}) {
|
|
200
|
-
const files = filesContent.map((f) => `--- FILE: ${f.name} ---\n${f.content}`).join('\n\n');
|
|
201
|
-
|
|
202
|
-
// 检测文件主要语言
|
|
203
|
-
const langProfile = this._detectLanguageProfile(filesContent);
|
|
204
|
-
|
|
205
|
-
// AST 代码结构分析注入 — 帮助 AI 理解继承体系、设计模式、代码规模
|
|
206
|
-
const astSection = options.astContext
|
|
207
|
-
? `\n# Code Structure Analysis (AST)\nThe following is a Tree-sitter AST analysis of the project. Use this structural context to better understand class hierarchies, design patterns, and code quality when extracting recipes:\n\n${options.astContext.substring(0, 3000)}\n`
|
|
208
|
-
: '';
|
|
209
|
-
|
|
210
|
-
// 用户语言偏好 — 控制 AI 输出人类可读字段的语言
|
|
211
|
-
const langInstruction = this._buildLangInstruction(options.lang);
|
|
212
|
-
|
|
213
|
-
// comprehensive 模式:全量分析整个文件,不跳过任何有意义的方法
|
|
214
|
-
if (options.comprehensive) {
|
|
215
|
-
return `# Role
|
|
216
|
-
You are a ${langProfile.role} performing a **comprehensive full-file analysis**.
|
|
217
|
-
|
|
218
|
-
# Goal
|
|
219
|
-
Thoroughly analyze ALL code in "${targetName}" and create Recipe entries for **every significant method, function, or code block**.
|
|
220
|
-
This is a full-file analysis — do NOT skip methods just because they seem "simple" or "standard".
|
|
221
|
-
${astSection}
|
|
222
|
-
|
|
223
|
-
# What to extract
|
|
224
|
-
- **Every** complete method/function with 5+ lines of implementation
|
|
225
|
-
- Initialization and configuration methods (init, viewDidLoad, setup, configure)
|
|
226
|
-
- Event handlers and action methods
|
|
227
|
-
- Data processing and business logic
|
|
228
|
-
- Protocol/delegate implementations
|
|
229
|
-
- ANY code block that a developer might reference, learn from, or reuse
|
|
230
|
-
|
|
231
|
-
# Extraction Rules
|
|
232
|
-
- Extract **BROADLY** — include all meaningful code units, not just "clever" or "novel" patterns
|
|
233
|
-
- Each recipe must be a **complete, standalone** code unit with full signature and body
|
|
234
|
-
- Put the complete code in \`content.pattern\`, and a meaningful project writeup in \`content.markdown\`
|
|
235
|
-
- Preserve the file's actual code. Use \`<#placeholder#>\` ONLY for literal strings/values a developer would customize
|
|
236
|
-
- Every recipe must be traceable to real code in the file. Do NOT invent code
|
|
237
|
-
- Include relevant \`headers\` (import/require lines) that the code depends on
|
|
238
|
-
- You **MUST** extract at least ONE recipe — every source file has something worth capturing
|
|
239
|
-
- For each recipe, provide a concise \`doClause\` (imperative sentence) and a \`topicHint\` group label
|
|
240
|
-
|
|
241
|
-
${langProfile.extractionExamples}
|
|
242
|
-
|
|
243
|
-
# Output (JSON Array)
|
|
244
|
-
Each item MUST use the following V3 KnowledgeEntry structure:
|
|
245
|
-
- title (string): Descriptive English name
|
|
246
|
-
- description (string): 2-3 sentences explaining what this code does, written as a project-specific guide
|
|
247
|
-
- trigger (string): @shortcut (kebab-case, e.g. "@url-parser")
|
|
248
|
-
- kind (string): "rule" | "pattern" | "fact" — rule = always-do/never-do, pattern = reusable recipe with code, fact = reference info
|
|
249
|
-
- knowledgeType (string): "code-pattern" | "architecture" | "best-practice" | "code-standard" | "code-relation" | "data-flow" | "event-and-data-flow" | "module-dependency" | "boundary-constraint" | "code-style" | "solution" | "anti-pattern"
|
|
250
|
-
- complexity (string): "basic" | "intermediate" | "advanced"
|
|
251
|
-
- scope (string): "universal" | "project-specific" | "team-convention"
|
|
252
|
-
- category: ${langProfile.categories}
|
|
253
|
-
- language: "${langProfile.primaryLanguage}"
|
|
254
|
-
- content (object): { "pattern": "<complete function/method/class from the file>", "markdown": "<project-specific writeup: what this pattern does, when to use it, key design decisions>", "rationale": "<why this pattern is designed this way — design trade-offs, alternatives considered>" }
|
|
255
|
-
- reasoning (object): { "whyStandard": "<why this is a standard/best-practice worth following>", "sources": ["<source file names>"], "confidence": <0.0-1.0> }
|
|
256
|
-
- headers (string[]): Required import/require lines
|
|
257
|
-
- tags (string[]): Search keywords
|
|
258
|
-
- doClause (string): One-sentence imperative: what to do (e.g. "Use dependency injection via constructor")
|
|
259
|
-
- dontClause (string): What NOT to do (e.g. "Don't instantiate services with new directly")
|
|
260
|
-
- whenClause (string): When this pattern applies (e.g. "When creating a new ViewController subclass")
|
|
261
|
-
- topicHint (string): Group label for related patterns (e.g. "Networking", "UI-Layout", "Error-Handling")
|
|
262
|
-
- coreCode (string): Minimal 3-10 line code skeleton that captures the essence. Must be syntactically complete — balanced brackets/parentheses, never start with } or ) and never end with { or (
|
|
263
|
-
- usageGuide (string): ### 章节格式使用指南 — 描述何时使用此模式、步骤和注意事项
|
|
264
|
-
- constraints (object, optional): { "preconditions": ["<conditions that must be true before using this pattern>"], "sideEffects": ["<observable side effects of using this code>"], "boundaries": ["<usage limitations or scope restrictions>"] }
|
|
265
|
-
- aiInsight (string, optional): One-sentence concise insight — the single most important takeaway about this code pattern
|
|
266
|
-
|
|
267
|
-
IMPORTANT: content.pattern must contain the COMPLETE source code. content.markdown must be a meaningful project-specific writeup, NOT just a copy of description. content.rationale must explain WHY this pattern is designed this way. reasoning.whyStandard must explain WHY this pattern matters.
|
|
268
|
-
${langInstruction}
|
|
269
|
-
Return ONLY a JSON array. Do NOT return an empty array.
|
|
270
|
-
|
|
271
|
-
Files Content:
|
|
272
|
-
${files}`;
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
return `# Role
|
|
276
|
-
You are a ${langProfile.role} extracting production-quality reusable code patterns.
|
|
277
|
-
|
|
278
|
-
# Goal
|
|
279
|
-
Extract meaningful, complete code patterns from "${targetName}". Each recipe must provide real value to a developer.
|
|
280
|
-
${astSection}
|
|
281
|
-
|
|
282
|
-
# What makes a GOOD recipe
|
|
283
|
-
- A **complete function/method** or **logical code block** (10-40 lines typically), NOT individual statements
|
|
284
|
-
- Code that demonstrates a **real design pattern**: ${langProfile.patternExamples}
|
|
285
|
-
- Code that a developer would actually **copy-paste and adapt** for a new feature
|
|
286
|
-
|
|
287
|
-
# What makes a BAD recipe (AVOID these)
|
|
288
|
-
- Trivial 2-3 line snippets like just a single assignment or import
|
|
289
|
-
- Overly generic code that doesn't reflect the file's actual logic
|
|
290
|
-
- Breaking a single function into multiple tiny recipes
|
|
291
|
-
|
|
292
|
-
# Extraction Strategy
|
|
293
|
-
For each function/method/class in the file, ask: "Would a developer benefit from having this as a reusable template?" If yes, extract the **complete unit** with its full body.
|
|
294
|
-
|
|
295
|
-
${langProfile.extractionExamples}
|
|
296
|
-
|
|
297
|
-
# Rules
|
|
298
|
-
1. \`content.pattern\` must contain a **complete function/method or logical unit** — include the signature and full body
|
|
299
|
-
2. \`content.markdown\` must be a meaningful project-specific writeup explaining what this code does and when to use it
|
|
300
|
-
3. Preserve the file's actual code. Use \`<#placeholder#>\` ONLY for literal strings/values a developer would customize
|
|
301
|
-
4. Every recipe must be traceable to real code in the file. Do NOT invent code
|
|
302
|
-
5. Include relevant \`headers\` (import/require lines) that the code depends on
|
|
303
|
-
6. Every recipe must have a concise \`doClause\` and a \`topicHint\` group label
|
|
304
|
-
|
|
305
|
-
# Output (JSON Array)
|
|
306
|
-
Each item MUST use the following V3 KnowledgeEntry structure:
|
|
307
|
-
- title (string): Descriptive English name
|
|
308
|
-
- description (string): 2-3 sentences explaining what this code does, written as a project-specific guide
|
|
309
|
-
- trigger (string): @shortcut (kebab-case, e.g. "@url-parser")
|
|
310
|
-
- kind (string): "rule" | "pattern" | "fact" — rule = always-do/never-do, pattern = reusable recipe with code, fact = reference info
|
|
311
|
-
- knowledgeType (string): "code-pattern" | "architecture" | "best-practice" | "code-standard" | "code-relation" | "data-flow" | "event-and-data-flow" | "module-dependency" | "boundary-constraint" | "code-style" | "solution" | "anti-pattern"
|
|
312
|
-
- complexity (string): "basic" | "intermediate" | "advanced"
|
|
313
|
-
- scope (string): "universal" | "project-specific" | "team-convention"
|
|
314
|
-
- category: ${langProfile.categories}
|
|
315
|
-
- language: "${langProfile.primaryLanguage}"
|
|
316
|
-
- content (object): { "pattern": "<complete function/method/class from the file>", "markdown": "<project-specific writeup: what this pattern does, when to use it, key design decisions>", "rationale": "<why this pattern is designed this way — design trade-offs, alternatives considered>" }
|
|
317
|
-
- reasoning (object): { "whyStandard": "<why this is a standard/best-practice worth following>", "sources": ["<source file names>"], "confidence": <0.0-1.0> }
|
|
318
|
-
- headers (string[]): Required import/require lines
|
|
319
|
-
- tags (string[]): Search keywords
|
|
320
|
-
- doClause (string): One-sentence imperative: what to do (e.g. "Use dependency injection via constructor")
|
|
321
|
-
- dontClause (string): What NOT to do (e.g. "Don't instantiate services with new directly")
|
|
322
|
-
- whenClause (string): When this pattern applies (e.g. "When creating a new ViewController subclass")
|
|
323
|
-
- topicHint (string): Group label (e.g. "Networking", "UI-Layout")
|
|
324
|
-
- coreCode (string): Minimal 3-10 line code skeleton that captures the essence. Must be syntactically complete — balanced brackets/parentheses, never start with } or ) and never end with { or (
|
|
325
|
-
- usageGuide (string): ### 章节格式使用指南 — 描述何时使用此模式、步骤和注意事项
|
|
326
|
-
- constraints (object, optional): { "preconditions": ["<conditions that must be true before using this pattern>"], "sideEffects": ["<observable side effects of using this code>"], "boundaries": ["<usage limitations or scope restrictions>"] }
|
|
327
|
-
- aiInsight (string, optional): One-sentence concise insight — the single most important takeaway about this code pattern
|
|
328
|
-
|
|
329
|
-
IMPORTANT: content.pattern must contain the COMPLETE source code. content.markdown must be a meaningful project-specific writeup, NOT just a copy of description. content.rationale must explain WHY this pattern is designed this way. reasoning.whyStandard must explain WHY this pattern matters.
|
|
330
|
-
${langInstruction}
|
|
331
|
-
Return ONLY a JSON array. If no meaningful patterns found, return [].
|
|
332
|
-
|
|
333
|
-
Files Content:
|
|
334
|
-
${files}`;
|
|
335
|
-
}
|
|
336
|
-
|
|
337
212
|
/**
|
|
338
213
|
* 根据用户语言偏好生成输出语言指令
|
|
339
214
|
* @param {string} [lang] - 语言代码,如 'zh', 'en'
|
|
@@ -856,7 +731,12 @@ ${items}`;
|
|
|
856
731
|
}
|
|
857
732
|
|
|
858
733
|
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
734
|
+
let slotAcquired = false;
|
|
859
735
|
try {
|
|
736
|
+
await this._waitForRateLimitWindow();
|
|
737
|
+
await this._acquireRequestSlot();
|
|
738
|
+
slotAcquired = true;
|
|
739
|
+
|
|
860
740
|
const result = await fn();
|
|
861
741
|
// 成功 → 完全重置熔断器(包括冷却时间)
|
|
862
742
|
this._circuitFailures = 0;
|
|
@@ -886,6 +766,16 @@ ${items}`;
|
|
|
886
766
|
causeCode === 'UND_ERR_SOCKET');
|
|
887
767
|
const isRetryable = err.status === 429 || err.status >= 500 || isNetworkError;
|
|
888
768
|
|
|
769
|
+
// 429:触发 provider 级冷却窗,抑制并发重试风暴
|
|
770
|
+
if (err.status === 429) {
|
|
771
|
+
const retryAfterMs = Number(err.retryAfterMs || 0);
|
|
772
|
+
const adaptiveCooldown = Math.max(
|
|
773
|
+
retryAfterMs,
|
|
774
|
+
Math.round(baseDelay * 2 ** attempt * 1.5 + Math.random() * 1000)
|
|
775
|
+
);
|
|
776
|
+
this._setRateLimitWindow(adaptiveCooldown);
|
|
777
|
+
}
|
|
778
|
+
|
|
889
779
|
// 首次失败记录详细诊断(含 cause)
|
|
890
780
|
if (attempt === 0 && (isNetworkError || err.cause)) {
|
|
891
781
|
this._log?.(
|
|
@@ -921,6 +811,10 @@ ${items}`;
|
|
|
921
811
|
`[_withRetry] attempt ${attempt + 1} failed (${err.message}), retrying in ${Math.round(delay / 1000)}s…`
|
|
922
812
|
);
|
|
923
813
|
await new Promise((r) => setTimeout(r, delay));
|
|
814
|
+
} finally {
|
|
815
|
+
if (slotAcquired) {
|
|
816
|
+
this._releaseRequestSlot();
|
|
817
|
+
}
|
|
924
818
|
}
|
|
925
819
|
}
|
|
926
820
|
}
|
|
@@ -15,7 +15,12 @@ const EMBED_MODEL = 'models/gemini-embedding-001';
|
|
|
15
15
|
|
|
16
16
|
export class GoogleGeminiProvider extends AiProvider {
|
|
17
17
|
constructor(config = {}) {
|
|
18
|
-
super(
|
|
18
|
+
super({
|
|
19
|
+
...config,
|
|
20
|
+
maxConcurrency:
|
|
21
|
+
config.maxConcurrency ||
|
|
22
|
+
Number(process.env.ASD_GEMINI_MAX_CONCURRENCY || process.env.ASD_AI_MAX_CONCURRENCY || 2),
|
|
23
|
+
});
|
|
19
24
|
this.name = 'google-gemini';
|
|
20
25
|
this.model = config.model || 'gemini-3-flash-preview';
|
|
21
26
|
this.apiKey = config.apiKey || process.env.ASD_GOOGLE_API_KEY || '';
|
|
@@ -110,10 +115,12 @@ export class GoogleGeminiProvider extends AiProvider {
|
|
|
110
115
|
];
|
|
111
116
|
}
|
|
112
117
|
|
|
113
|
-
// toolChoice → Gemini mode
|
|
114
|
-
body.
|
|
115
|
-
|
|
116
|
-
|
|
118
|
+
// toolChoice → Gemini mode (仅在有工具声明时设置,无工具时设 toolConfig 可能导致空响应)
|
|
119
|
+
if (body.tools) {
|
|
120
|
+
body.toolConfig = {
|
|
121
|
+
functionCallingConfig: { mode: this.#toGeminiMode(toolChoice) },
|
|
122
|
+
};
|
|
123
|
+
}
|
|
117
124
|
|
|
118
125
|
// 系统指令
|
|
119
126
|
if (systemPrompt) {
|
|
@@ -421,6 +428,20 @@ export class GoogleGeminiProvider extends AiProvider {
|
|
|
421
428
|
signal: controller.signal,
|
|
422
429
|
});
|
|
423
430
|
if (!res.ok) {
|
|
431
|
+
const retryAfterHeader = res.headers.get('retry-after');
|
|
432
|
+
let retryAfterMs = 0;
|
|
433
|
+
if (retryAfterHeader) {
|
|
434
|
+
const sec = Number(retryAfterHeader);
|
|
435
|
+
if (Number.isFinite(sec) && sec > 0) {
|
|
436
|
+
retryAfterMs = sec * 1000;
|
|
437
|
+
} else {
|
|
438
|
+
const when = Date.parse(retryAfterHeader);
|
|
439
|
+
if (Number.isFinite(when)) {
|
|
440
|
+
retryAfterMs = Math.max(0, when - Date.now());
|
|
441
|
+
}
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
|
|
424
445
|
let detail = '';
|
|
425
446
|
try {
|
|
426
447
|
const j = await res.json();
|
|
@@ -430,6 +451,9 @@ export class GoogleGeminiProvider extends AiProvider {
|
|
|
430
451
|
}
|
|
431
452
|
const err = new Error(`Gemini API error: ${res.status}${detail ? ` — ${detail}` : ''}`);
|
|
432
453
|
err.status = res.status;
|
|
454
|
+
if (retryAfterMs > 0) {
|
|
455
|
+
err.retryAfterMs = retryAfterMs;
|
|
456
|
+
}
|
|
433
457
|
throw err;
|
|
434
458
|
}
|
|
435
459
|
return await res.json();
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
import crypto from 'node:crypto';
|
|
19
|
-
import { SessionStore } from '../../../../service/
|
|
19
|
+
import { SessionStore } from '../../../../service/agent/memory/SessionStore.js';
|
|
20
20
|
import { ExternalSubmissionTracker } from './ExternalSubmissionTracker.js';
|
|
21
21
|
|
|
22
22
|
// ── 常量 ────────────────────────────────────────────────────
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* ExternalSubmissionTracker — 外部 Agent 提交追踪与质量评估
|
|
3
3
|
*
|
|
4
|
-
*
|
|
5
|
-
* 内部 Agent 使用 EvidenceCollector 从 toolCall
|
|
4
|
+
* 质量门控的外部 Agent 对应模块。
|
|
5
|
+
* 内部 Agent 使用 EvidenceCollector 从 toolCall 中收集证据 (bootstrap-gate.js),
|
|
6
6
|
* 外部 Agent 使用 ExternalSubmissionTracker 从 submit_knowledge 调用中积累证据。
|
|
7
7
|
*
|
|
8
8
|
* 职责:
|
|
9
9
|
* - 追踪每个维度的 submit_knowledge 提交 (recipe 元数据 + 引用文件)
|
|
10
10
|
* - 从提交内容构建 evidenceMap (filePath → 引用摘要)
|
|
11
11
|
* - 从 dimension_complete 的 analysisText 提取负空间信号
|
|
12
|
-
* - 计算维度级质量评分 (
|
|
12
|
+
* - 计算维度级质量评分 (对应 bootstrap-gate.js 的 buildQualityScores)
|
|
13
13
|
* - 为下游维度提供结构化跨维度证据
|
|
14
14
|
*
|
|
15
15
|
* 设计对应关系:
|
|
@@ -37,7 +37,7 @@ const SIZE_THRESHOLDS = {
|
|
|
37
37
|
/**
|
|
38
38
|
* 将 base-dimensions 中的维度定义转换为 Mission Briefing 中的维度任务对象
|
|
39
39
|
*
|
|
40
|
-
* 取自
|
|
40
|
+
* 取自 bootstrap-analyst.buildAnalystPrompt() + DIMENSION_CONFIGS_V3 + StyleGuide
|
|
41
41
|
*
|
|
42
42
|
* @param {object} dim — base-dimensions.js 中的维度定义
|
|
43
43
|
* @param {object[]} skills — 已加载的 bootstrap skills
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
*/
|
|
17
17
|
|
|
18
18
|
import { BootstrapSnapshot } from './BootstrapSnapshot.js';
|
|
19
|
-
import { SessionStore } from '../../../../../service/
|
|
19
|
+
import { SessionStore } from '../../../../../service/agent/memory/SessionStore.js';
|
|
20
20
|
|
|
21
21
|
// ──────────────────────────────────────────────────────────────
|
|
22
22
|
// IncrementalBootstrap 类
|
|
@@ -1,15 +1,15 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* pipeline/dimension-context.js — 跨维度上下文管理 (v6)
|
|
3
3
|
*
|
|
4
|
-
* ⚠️ 内部 Agent 专用 — 被 orchestrator.js 的
|
|
4
|
+
* ⚠️ 内部 Agent 专用 — 被 orchestrator.js 的 PipelineStrategy analyze 阶段调用。
|
|
5
5
|
* 外部 Agent 不使用此模块(外部 Agent 的跨维度上下文由 BootstrapSession.js + EpisodicMemory 管理)。
|
|
6
6
|
*
|
|
7
|
-
*
|
|
7
|
+
* 在 PipelineStrategy 按维度分批执行时,维护跨维度的上下文:
|
|
8
8
|
* - 项目基础信息 (不变)
|
|
9
9
|
* - 已完成维度的 DimensionDigest (累积)
|
|
10
10
|
* - 已提交候选的摘要列表 (累积)
|
|
11
11
|
*
|
|
12
|
-
* 确保每个维度的
|
|
12
|
+
* 确保每个维度的 Analyst 阶段都能看到前序维度的分析结论,
|
|
13
13
|
* 实现跨维度透明互补。
|
|
14
14
|
*
|
|
15
15
|
* @module pipeline/dimension-context
|
|
@@ -73,7 +73,7 @@ export class DimensionContext {
|
|
|
73
73
|
}
|
|
74
74
|
|
|
75
75
|
/**
|
|
76
|
-
* 构建给
|
|
76
|
+
* 构建给 Agent 的上下文快照
|
|
77
77
|
*
|
|
78
78
|
* @param {string} currentDimId — 当前维度 ID
|
|
79
79
|
* @returns {DimensionContextSnapshot}
|
|
@@ -115,7 +115,7 @@ export class DimensionContext {
|
|
|
115
115
|
|
|
116
116
|
/**
|
|
117
117
|
* 获取所有维度摘要的紧凑文本表示
|
|
118
|
-
* 用于
|
|
118
|
+
* 用于 Agent prompt 中注入
|
|
119
119
|
*
|
|
120
120
|
* @returns {string}
|
|
121
121
|
*/
|
|
@@ -176,12 +176,12 @@ export class DimensionContext {
|
|
|
176
176
|
}
|
|
177
177
|
|
|
178
178
|
/**
|
|
179
|
-
* 从
|
|
179
|
+
* 从 Agent 的最终回复中解析 DimensionDigest
|
|
180
180
|
*
|
|
181
|
-
*
|
|
181
|
+
* Agent 被要求在回复末尾包含 JSON 格式的 dimensionDigest。
|
|
182
182
|
* 此函数从自由格式文本中提取该 JSON 块。
|
|
183
183
|
*
|
|
184
|
-
* @param {string} reply —
|
|
184
|
+
* @param {string} reply — Agent 的完整回复文本
|
|
185
185
|
* @returns {DimensionDigest|null}
|
|
186
186
|
*/
|
|
187
187
|
export function parseDimensionDigest(reply) {
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* noAiFallback.js — AI 不可用时的规则化降级知识提取
|
|
3
3
|
*
|
|
4
|
-
* 当
|
|
4
|
+
* 当 AgentRuntime / AI Provider 不可用时,从 Phase 1-4 的结构化数据中
|
|
5
5
|
* 提取基础知识候选和 Project Skill,覆盖以下维度:
|
|
6
6
|
*
|
|
7
7
|
* ✅ project-profile — 从 langStats + depGraph + targets 构建项目技术画像
|