soloforge 1.2.17 → 1.2.19
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/dist/adapters/claude_code/pre_prompt_contract.js +2 -2
- package/dist/adapters/claude_code/pre_prompt_contract.js.map +1 -1
- package/dist/adapters/claude_code/server.d.ts.map +1 -1
- package/dist/adapters/claude_code/server.js +4 -2
- package/dist/adapters/claude_code/server.js.map +1 -1
- package/dist/adapters/claude_code/tools.d.ts +2 -2
- package/dist/adapters/claude_code/tools.d.ts.map +1 -1
- package/dist/adapters/claude_code/tools.js +189 -170
- package/dist/adapters/claude_code/tools.js.map +1 -1
- package/dist/adapters/codex/codex_config.d.ts.map +1 -1
- package/dist/adapters/codex/codex_config.js +16 -4
- package/dist/adapters/codex/codex_config.js.map +1 -1
- package/dist/adapters/trae/trae_config.d.ts +2 -1
- package/dist/adapters/trae/trae_config.d.ts.map +1 -1
- package/dist/adapters/trae/trae_config.js +4 -3
- package/dist/adapters/trae/trae_config.js.map +1 -1
- package/dist/bin/config_commands.d.ts +1 -1
- package/dist/bin/config_commands.js +4 -4
- package/dist/bin/config_commands.js.map +1 -1
- package/dist/bin/soloforge.d.ts.map +1 -1
- package/dist/bin/soloforge.js +85 -110
- package/dist/bin/soloforge.js.map +1 -1
- package/dist/engine/artifact_contract_registry.d.ts.map +1 -1
- package/dist/engine/artifact_contract_registry.js.map +1 -1
- package/dist/engine/audit_pool.d.ts.map +1 -1
- package/dist/engine/audit_pool.js +0 -1
- package/dist/engine/audit_pool.js.map +1 -1
- package/dist/engine/audit_sampler.d.ts.map +1 -1
- package/dist/engine/audit_sampler.js +0 -4
- package/dist/engine/audit_sampler.js.map +1 -1
- package/dist/engine/batch1_manifest.d.ts.map +1 -1
- package/dist/engine/batch1_manifest.js +0 -7
- package/dist/engine/batch1_manifest.js.map +1 -1
- package/dist/engine/batch1_scenario_registry.js +7 -7
- package/dist/engine/batch1_scenario_registry.js.map +1 -1
- package/dist/engine/batch1_scenario_runners.js +32 -32
- package/dist/engine/batch1_scenario_runners.js.map +1 -1
- package/dist/engine/capability_action_advisor.js +16 -16
- package/dist/engine/capability_action_advisor.js.map +1 -1
- package/dist/engine/classifier.d.ts.map +1 -1
- package/dist/engine/classifier.js +4 -3
- package/dist/engine/classifier.js.map +1 -1
- package/dist/engine/code_reviewer.d.ts.map +1 -1
- package/dist/engine/code_reviewer.js +10 -9
- package/dist/engine/code_reviewer.js.map +1 -1
- package/dist/engine/command_execution_contract.js +14 -14
- package/dist/engine/command_execution_contract.js.map +1 -1
- package/dist/engine/debug_log.d.ts +2 -0
- package/dist/engine/debug_log.d.ts.map +1 -0
- package/dist/engine/debug_log.js +7 -0
- package/dist/engine/debug_log.js.map +1 -0
- package/dist/engine/developer_sovereignty.js +15 -15
- package/dist/engine/developer_sovereignty.js.map +1 -1
- package/dist/engine/diff_ownership_store.d.ts.map +1 -1
- package/dist/engine/diff_ownership_store.js +1 -2
- package/dist/engine/diff_ownership_store.js.map +1 -1
- package/dist/engine/dual_layer_mechanism_registry.js +1 -1
- package/dist/engine/dual_layer_mechanism_registry.js.map +1 -1
- package/dist/engine/evolver.d.ts.map +1 -1
- package/dist/engine/evolver.js +11 -10
- package/dist/engine/evolver.js.map +1 -1
- package/dist/engine/governance_report.js +9 -9
- package/dist/engine/governance_report.js.map +1 -1
- package/dist/engine/implementation_roadmap_registry.js +5 -5
- package/dist/engine/implementation_roadmap_registry.js.map +1 -1
- package/dist/engine/input_material_extractor.js +3 -3
- package/dist/engine/input_material_extractor.js.map +1 -1
- package/dist/engine/intent_expander.d.ts.map +1 -1
- package/dist/engine/intent_expander.js +15 -14
- package/dist/engine/intent_expander.js.map +1 -1
- package/dist/engine/intent_router.d.ts.map +1 -1
- package/dist/engine/intent_router.js +17 -16
- package/dist/engine/intent_router.js.map +1 -1
- package/dist/engine/java_quality_guard.js +10 -10
- package/dist/engine/java_quality_guard.js.map +1 -1
- package/dist/engine/knowledge_injection_boundary.d.ts +1 -1
- package/dist/engine/knowledge_injection_boundary.d.ts.map +1 -1
- package/dist/engine/knowledge_injection_boundary.js +61 -60
- package/dist/engine/knowledge_injection_boundary.js.map +1 -1
- package/dist/engine/main_path_integration_contract.js +16 -16
- package/dist/engine/main_path_integration_contract.js.map +1 -1
- package/dist/engine/mechanism_contract_registry.d.ts +3 -4
- package/dist/engine/mechanism_contract_registry.d.ts.map +1 -1
- package/dist/engine/mechanism_contract_registry.js +23 -24
- package/dist/engine/mechanism_contract_registry.js.map +1 -1
- package/dist/engine/onboarding.js +20 -20
- package/dist/engine/onboarding.js.map +1 -1
- package/dist/engine/privacy_secret_contract.d.ts.map +1 -1
- package/dist/engine/privacy_secret_contract.js +49 -48
- package/dist/engine/privacy_secret_contract.js.map +1 -1
- package/dist/engine/regression_matrix.js +21 -21
- package/dist/engine/regression_matrix.js.map +1 -1
- package/dist/engine/route_decision_contract_verifier.js +24 -24
- package/dist/engine/route_decision_contract_verifier.js.map +1 -1
- package/dist/engine/scope_controller.d.ts.map +1 -1
- package/dist/engine/scope_controller.js +4 -3
- package/dist/engine/scope_controller.js.map +1 -1
- package/dist/engine/scope_lease.d.ts.map +1 -1
- package/dist/engine/scope_lease.js +0 -1
- package/dist/engine/scope_lease.js.map +1 -1
- package/dist/engine/task_context.js +3 -3
- package/dist/engine/task_context.js.map +1 -1
- package/dist/engine/test_generator.js +8 -8
- package/dist/engine/test_generator.js.map +1 -1
- package/dist/engine/tool_invocation_contract_registry.js +18 -18
- package/dist/engine/tool_invocation_contract_registry.js.map +1 -1
- package/dist/engine/traceability.js +6 -6
- package/dist/engine/traceability.js.map +1 -1
- package/dist/engine/user_feedback_contract.js +9 -9
- package/dist/engine/user_feedback_contract.js.map +1 -1
- package/dist/engine/verifier.d.ts.map +1 -1
- package/dist/engine/verifier.js +9 -8
- package/dist/engine/verifier.js.map +1 -1
- package/dist/engine/zero_config_init.d.ts.map +1 -1
- package/dist/engine/zero_config_init.js +84 -84
- package/dist/engine/zero_config_init.js.map +1 -1
- package/dist/git/operations.js +1 -1
- package/dist/git/operations.js.map +1 -1
- package/dist/index.d.ts +4 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +16 -11
- package/dist/index.js.map +1 -1
- package/dist/knowledge/index_manager.d.ts.map +1 -1
- package/dist/knowledge/index_manager.js +18 -17
- package/dist/knowledge/index_manager.js.map +1 -1
- package/dist/knowledge/loader.js +2 -2
- package/dist/knowledge/loader.js.map +1 -1
- package/package.json +4 -2
|
@@ -2,52 +2,63 @@ import { z } from "zod";
|
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import fss from "node:fs";
|
|
4
4
|
import crypto from "node:crypto";
|
|
5
|
+
import { verifyArtifact } from "../../engine/artifact_contract_registry.js";
|
|
5
6
|
import { verifyRouteDecisionContract } from "../../engine/route_decision_contract_verifier.js";
|
|
6
7
|
import { findToolInvocationContractByName, createToolTrace, validateToolInvocation, } from "../../engine/tool_invocation_contract_registry.js";
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
8
|
+
function createLazy(loader) {
|
|
9
|
+
let mod = { loaded: null, promise: null };
|
|
10
|
+
return () => {
|
|
11
|
+
if (mod.loaded)
|
|
12
|
+
return Promise.resolve(mod.loaded);
|
|
13
|
+
if (!mod.promise)
|
|
14
|
+
mod.promise = loader().then(m => { mod.loaded = m; return m; });
|
|
15
|
+
return mod.promise;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
const lazyClassifier = createLazy(() => import("../../engine/classifier.js"));
|
|
19
|
+
const lazyIntentRouter = createLazy(() => import("../../engine/intent_router.js"));
|
|
20
|
+
const lazyExpander = createLazy(() => import("../../engine/intent_expander.js"));
|
|
21
|
+
const lazyVerifier = createLazy(() => import("../../engine/verifier.js"));
|
|
22
|
+
const lazyEvolver = createLazy(() => import("../../engine/evolver.js"));
|
|
23
|
+
const lazyPlanner = createLazy(() => import("../../engine/task_planner.js"));
|
|
24
|
+
const lazyImpact = createLazy(() => import("../../engine/impact_analyzer.js"));
|
|
25
|
+
const lazyReviewer = createLazy(() => import("../../engine/code_reviewer.js"));
|
|
26
|
+
const lazyDebt = createLazy(() => import("../../engine/debt_tracker.js"));
|
|
27
|
+
const lazyScaffolder = createLazy(() => import("../../engine/scaffolder.js"));
|
|
28
|
+
const lazyDelivery = createLazy(() => import("../../engine/delivery.js"));
|
|
29
|
+
const lazyChangeCoord = createLazy(() => import("../../engine/change_coordinator.js"));
|
|
30
|
+
const lazyTeam = createLazy(() => import("../../engine/team_awareness.js"));
|
|
31
|
+
const lazyContractGuard = createLazy(() => import("../../engine/contract_guard.js"));
|
|
32
|
+
const lazyOnboarding = createLazy(() => import("../../engine/onboarding.js"));
|
|
33
|
+
const lazyGitDeps = createLazy(() => import("../../engine/git_deps.js"));
|
|
34
|
+
const lazyKnowledge = createLazy(() => import("../../engine/knowledge_manager.js"));
|
|
35
|
+
const lazyFeasibility = createLazy(() => import("../../engine/feasibility_checker.js"));
|
|
36
|
+
const lazyDebugger = createLazy(() => import("../../engine/debugger.js"));
|
|
37
|
+
const lazyObservability = createLazy(() => import("../../engine/observability.js"));
|
|
38
|
+
const lazyGovernance = createLazy(() => import("../../engine/governance_report.js"));
|
|
39
|
+
const lazyMigration = createLazy(() => import("../../engine/migration_guard.js"));
|
|
40
|
+
const lazyTestGen = createLazy(() => import("../../engine/test_generator.js"));
|
|
41
|
+
const lazyTestQuality = createLazy(() => import("../../engine/test_quality.js"));
|
|
42
|
+
const lazyDepScan = createLazy(() => import("../../engine/dependency_scanner.js"));
|
|
43
|
+
const lazyDebtReport = createLazy(() => import("../../engine/debt_reporter.js"));
|
|
44
|
+
const lazyExploration = createLazy(() => import("../../engine/exploration.js"));
|
|
45
|
+
const lazyWorkspaceResumer = createLazy(() => import("../../engine/workspace_resumer.js"));
|
|
46
|
+
const lazyLLM = createLazy(() => import("../../engine/llm_gateway.js"));
|
|
47
|
+
const lazyIO = createLazy(() => import("../../engine/io_controller.js"));
|
|
48
|
+
const lazyCapability = createLazy(() => import("../../engine/capability_registry.js"));
|
|
49
|
+
const lazyDecision = createLazy(() => import("../../engine/decision_contract.js"));
|
|
50
|
+
const lazyAnchor = createLazy(() => import("../../engine/cognitive_anchor.js"));
|
|
51
|
+
const lazyJob = createLazy(() => import("../../engine/job_manager.js"));
|
|
52
|
+
const lazyAuditPool = createLazy(() => import("../../engine/audit_pool.js"));
|
|
53
|
+
const lazyEscape = createLazy(() => import("../../engine/escape_report.js"));
|
|
54
|
+
const lazyAuditSampler = createLazy(() => import("../../engine/audit_sampler.js"));
|
|
55
|
+
const lazyCapAdvisor = createLazy(() => import("../../engine/capability_action_advisor.js"));
|
|
56
|
+
const lazyCapState = createLazy(() => import("../../engine/capability_state_store.js"));
|
|
57
|
+
const lazyMainPath = createLazy(() => import("../../engine/main_path_integration_contract.js"));
|
|
58
|
+
const lazyDualLayer = createLazy(() => import("../../engine/dual_layer_mechanism_registry.js"));
|
|
59
|
+
const lazyInputMaterial = createLazy(() => import("../../engine/input_material_contract_registry.js"));
|
|
60
|
+
const lazyConfigCmd = createLazy(() => import("../../bin/config_commands.js"));
|
|
61
|
+
const lazyConfigPrecedence = createLazy(() => import("../../engine/config_precedence_contract.js"));
|
|
51
62
|
// ── Zod Schema 定义 ──
|
|
52
63
|
const ClassifySchema = {
|
|
53
64
|
intent: z.string().describe("开发者意图描述"),
|
|
@@ -161,14 +172,14 @@ const ExploreSchema = {
|
|
|
161
172
|
* @param server - McpServer 实例
|
|
162
173
|
* @param deps - 工具依赖项(配置、知识索引、任务上下文)
|
|
163
174
|
*/
|
|
164
|
-
export function registerTools(server, deps) {
|
|
175
|
+
export async function registerTools(server, deps) {
|
|
165
176
|
console.error("[soloForge] 工具注册: 开始注册 MCP 工具...");
|
|
166
177
|
const { config, knowledgeIndex, taskContext } = deps;
|
|
167
178
|
const projectPath = config._projectPath || process.cwd();
|
|
168
179
|
// H1: LLM Gateway — Token 预算熔断,防止单任务 Token 超支
|
|
169
|
-
const gateway = deps.gateway ?? new LLMGateway();
|
|
180
|
+
const gateway = deps.gateway ?? new (await lazyLLM()).LLMGateway();
|
|
170
181
|
// H4: IO Controller — 工作区互斥锁,防止外部修改导致的静默覆盖
|
|
171
|
-
const ioController = new IOController(projectPath);
|
|
182
|
+
const ioController = new (await lazyIO()).IOController(projectPath);
|
|
172
183
|
// ── 统一网关 : 唯一工具调用边界 ──
|
|
173
184
|
/** 计算 sf_status cancel 的动态 category */
|
|
174
185
|
function computeEffectiveCategory(toolName, args, contract) {
|
|
@@ -197,38 +208,38 @@ export function registerTools(server, deps) {
|
|
|
197
208
|
};
|
|
198
209
|
/** 授权模型: explicit > dynamic write gate > destructive gate > workflow gate > category default */
|
|
199
210
|
function checkAuth(toolName, args, contract, ctx, effectiveCategory) {
|
|
200
|
-
// 1.
|
|
211
|
+
// 1. 显式确认
|
|
201
212
|
if (args.confirm === true || args.authorized === true || args.authorization_token) {
|
|
202
213
|
return { required: true, granted: true, reason: "explicit confirmation" };
|
|
203
214
|
}
|
|
204
|
-
// 2.
|
|
215
|
+
// 2. 动态写入升级: cancel/job_cancel 从 read_only 变为 write → 必须确认
|
|
205
216
|
if (effectiveCategory && effectiveCategory !== contract.category && effectiveCategory !== "read_only_bypass") {
|
|
206
217
|
return { required: true, granted: false, reason: "dynamic write upgrade requires explicit confirmation" };
|
|
207
218
|
}
|
|
208
|
-
// 3.
|
|
219
|
+
// 3. 破坏性副作用始终需要显式确认
|
|
209
220
|
const destructive = ["git_commit", "git_push", "pr_create", "external_write"];
|
|
210
221
|
if (contract.side_effects.some((e) => destructive.includes(e))) {
|
|
211
222
|
return { required: true, granted: false, reason: "destructive side effects require explicit confirmation" };
|
|
212
223
|
}
|
|
213
|
-
// 4.
|
|
224
|
+
// 4. 独立严格工具(无 workflow 要求,无 task context)→ 由 handler 自行检查
|
|
214
225
|
if (contract.category === "strict_controlled" && !ctx && !contract.requires_workflow) {
|
|
215
226
|
return { required: false, granted: true, reason: "standalone strict tool" };
|
|
216
227
|
}
|
|
217
|
-
// 5.
|
|
228
|
+
// 5. strict_controlled 的 workflow 合同门控
|
|
218
229
|
if (contract.category === "strict_controlled" && ctx?.last_tool_trace) {
|
|
219
230
|
const allowed = ctx.last_tool_trace.next_allowed_tools ?? [];
|
|
220
231
|
if (allowed.includes(toolName)) {
|
|
221
232
|
return { required: true, granted: true, reason: "workflow contract gate" };
|
|
222
233
|
}
|
|
223
234
|
}
|
|
224
|
-
// 5b.
|
|
235
|
+
// 5b. 基于状态的回退: 严格工具且任务处于有效状态但无 tool_trace
|
|
225
236
|
if (contract.category === "strict_controlled" && ctx && !ctx.last_tool_trace && contract.requires_workflow) {
|
|
226
237
|
const validStates = STATE_PRECHECKS[toolName];
|
|
227
238
|
if (validStates && validStates.includes(ctx.status)) {
|
|
228
239
|
return { required: true, granted: true, reason: "task in valid state for tool" };
|
|
229
240
|
}
|
|
230
241
|
}
|
|
231
|
-
// 5c.
|
|
242
|
+
// 5c. 非严格工具: 自动授权,除非 requires_authorization
|
|
232
243
|
if (contract.category === "read_only_bypass" || contract.category === "normal_controlled") {
|
|
233
244
|
if (contract.requires_authorization) {
|
|
234
245
|
return { required: true, granted: false, reason: "tool requires explicit authorization" };
|
|
@@ -242,7 +253,7 @@ export function registerTools(server, deps) {
|
|
|
242
253
|
const invocationId = `inv-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
243
254
|
const taskId = args.task_id;
|
|
244
255
|
const contract = findToolInvocationContractByName(name);
|
|
245
|
-
//
|
|
256
|
+
// 无合同 → hard fail
|
|
246
257
|
if (!contract) {
|
|
247
258
|
return {
|
|
248
259
|
content: [{ type: "text", text: JSON.stringify({ error: `工具 ${name} 未注册契约` }) }],
|
|
@@ -251,12 +262,12 @@ export function registerTools(server, deps) {
|
|
|
251
262
|
}
|
|
252
263
|
const effectiveCategory = computeEffectiveCategory(name, args, contract);
|
|
253
264
|
const effectiveSideEffects = computeEffectiveSideEffects(name, args, contract);
|
|
254
|
-
//
|
|
265
|
+
// 加载 TaskContext
|
|
255
266
|
let ctx = null;
|
|
256
267
|
if (taskId) {
|
|
257
268
|
ctx = await taskContext.load(taskId);
|
|
258
269
|
}
|
|
259
|
-
//
|
|
270
|
+
// 状态预检: 在合同守卫之前验证任务状态
|
|
260
271
|
if (taskId && STATE_PRECHECKS[name] && ctx) {
|
|
261
272
|
const validStates = STATE_PRECHECKS[name];
|
|
262
273
|
if (!validStates.includes(ctx.status)) {
|
|
@@ -270,7 +281,7 @@ export function registerTools(server, deps) {
|
|
|
270
281
|
};
|
|
271
282
|
}
|
|
272
283
|
}
|
|
273
|
-
//
|
|
284
|
+
// 严格受控但缺少 task_id
|
|
274
285
|
const isStandaloneStrict = !contract.requires_workflow;
|
|
275
286
|
if (contract.category === "strict_controlled" && !taskId && !isStandaloneStrict) {
|
|
276
287
|
return {
|
|
@@ -278,11 +289,11 @@ export function registerTools(server, deps) {
|
|
|
278
289
|
isError: true,
|
|
279
290
|
};
|
|
280
291
|
}
|
|
281
|
-
//
|
|
292
|
+
// 授权检查
|
|
282
293
|
const authorization = checkAuth(name, args, contract, ctx ?? undefined, effectiveCategory);
|
|
283
|
-
//
|
|
284
|
-
//
|
|
285
|
-
// Handler
|
|
294
|
+
// 授权拒绝 → 仅对网关级拒绝执行 hard fail
|
|
295
|
+
// (动态写入升级、破坏性操作、无授权源的 strict_controlled)
|
|
296
|
+
// Handler 级授权(如 sf_capability_update confirm)由 handler 自行处理
|
|
286
297
|
const gatewayDenial = !authorization.granted &&
|
|
287
298
|
authorization.reason !== "tool requires explicit authorization";
|
|
288
299
|
if (gatewayDenial) {
|
|
@@ -312,17 +323,17 @@ export function registerTools(server, deps) {
|
|
|
312
323
|
isError: true,
|
|
313
324
|
};
|
|
314
325
|
}
|
|
315
|
-
//
|
|
326
|
+
// read_only_bypass 的旁路处理
|
|
316
327
|
const bypass = effectiveCategory === "read_only_bypass"
|
|
317
328
|
? { allowed: true, reason: "read_only" } : undefined;
|
|
318
|
-
//
|
|
329
|
+
// 合同验证
|
|
319
330
|
const lastTrace = ctx?.last_tool_trace;
|
|
320
|
-
//
|
|
331
|
+
// 为动态工具构建有效的合同覆盖(如 sf_status cancel)
|
|
321
332
|
let contractOverride;
|
|
322
333
|
if (effectiveCategory !== contract.category || effectiveSideEffects !== contract.side_effects) {
|
|
323
334
|
contractOverride = { ...contract, category: effectiveCategory, side_effects: effectiveSideEffects };
|
|
324
335
|
}
|
|
325
|
-
// Workflow ID:
|
|
336
|
+
// Workflow ID: 仅来自真实的 expansion trace
|
|
326
337
|
const explicitWorkflowId = ctx?.expansion?.workflow_trace?.workflow_id;
|
|
327
338
|
const violations = validateToolInvocation({
|
|
328
339
|
tool_name: name,
|
|
@@ -334,7 +345,7 @@ export function registerTools(server, deps) {
|
|
|
334
345
|
workflow_id: explicitWorkflowId,
|
|
335
346
|
_contractOverride: contractOverride,
|
|
336
347
|
});
|
|
337
|
-
//
|
|
348
|
+
// 基于状态的回退: 将 contract_state_mismatch 从 hard_fail 降级为 require_human
|
|
338
349
|
if (authorization.reason === "task in valid state for tool") {
|
|
339
350
|
for (const v of violations) {
|
|
340
351
|
if (v.violation_type === "contract_state_mismatch" && v.severity === "hard_fail") {
|
|
@@ -343,7 +354,7 @@ export function registerTools(server, deps) {
|
|
|
343
354
|
}
|
|
344
355
|
}
|
|
345
356
|
}
|
|
346
|
-
// Hard-fail
|
|
357
|
+
// Hard-fail 违规 → 阻止
|
|
347
358
|
const hardFail = violations.find(v => v.severity === "hard_fail");
|
|
348
359
|
if (hardFail) {
|
|
349
360
|
const blockedTrace = createToolTrace({
|
|
@@ -366,17 +377,17 @@ export function registerTools(server, deps) {
|
|
|
366
377
|
isError: true,
|
|
367
378
|
};
|
|
368
379
|
}
|
|
369
|
-
//
|
|
380
|
+
// 执行 handler
|
|
370
381
|
try {
|
|
371
382
|
const raw = await handler(args);
|
|
372
|
-
//
|
|
383
|
+
// 如果 handler 直接返回 { content } 则透传
|
|
373
384
|
if (raw && typeof raw === "object" && "content" in raw && Array.isArray(raw.content)) {
|
|
374
385
|
return raw;
|
|
375
386
|
}
|
|
376
|
-
//
|
|
387
|
+
// 从 { result } 模式中提取数据
|
|
377
388
|
const data = raw?.result !== undefined ? raw.result : raw;
|
|
378
389
|
const hasError = !!(data && typeof data === "object" && "error" in data);
|
|
379
|
-
//
|
|
390
|
+
// 构建 trace
|
|
380
391
|
const trace = createToolTrace({
|
|
381
392
|
tool_name: name, invocation_id: invocationId, task_id: taskId,
|
|
382
393
|
workflow_id: ctx?.expansion?.workflow_trace?.workflow_id,
|
|
@@ -385,18 +396,18 @@ export function registerTools(server, deps) {
|
|
|
385
396
|
forbidden_tools: contract.forbidden_next_tools,
|
|
386
397
|
authorization, bypass,
|
|
387
398
|
});
|
|
388
|
-
//
|
|
399
|
+
// 将 trace 写入 TaskContext
|
|
389
400
|
if (taskId) {
|
|
390
401
|
await taskContext.setToolTrace(taskId, trace, violations.length > 0 ? violations : undefined);
|
|
391
402
|
}
|
|
392
|
-
//
|
|
403
|
+
// 用 trace + next/forbidden 装饰响应
|
|
393
404
|
const response = {
|
|
394
405
|
...data,
|
|
395
406
|
tool_trace: trace,
|
|
396
407
|
next_allowed_tools: contract.default_next_tools,
|
|
397
408
|
forbidden_tools: contract.forbidden_next_tools,
|
|
398
409
|
};
|
|
399
|
-
//
|
|
410
|
+
// 基于状态的回退: 在响应中暴露降级 workflow
|
|
400
411
|
if (authorization.reason === "task in valid state for tool") {
|
|
401
412
|
response.degraded_workflow = true;
|
|
402
413
|
response.workflow_source = "state-based-fallback";
|
|
@@ -433,14 +444,14 @@ export function registerTools(server, deps) {
|
|
|
433
444
|
});
|
|
434
445
|
}
|
|
435
446
|
// 加载认知锚点上下文 — 必须提供相关性参数,禁止全量加载
|
|
436
|
-
function loadRelevantAnchors(query) {
|
|
447
|
+
async function loadRelevantAnchors(query) {
|
|
437
448
|
console.error("[soloForge] 工具注册: 加载认知锚点");
|
|
438
449
|
return _loadRelevantAnchorsInner(query);
|
|
439
450
|
}
|
|
440
|
-
function _loadRelevantAnchorsInner(query) {
|
|
451
|
+
async function _loadRelevantAnchorsInner(query) {
|
|
441
452
|
const stateDir = taskContext.getStateDir();
|
|
442
453
|
const mapPath = path.join(stateDir, "cognitive.map.json");
|
|
443
|
-
const mapData = readMapJson(mapPath);
|
|
454
|
+
const mapData = (await lazyAnchor()).readMapJson(mapPath);
|
|
444
455
|
if (!mapData || mapData.anchors.length === 0)
|
|
445
456
|
return undefined;
|
|
446
457
|
const existingFiles = new Set();
|
|
@@ -454,15 +465,15 @@ export function registerTools(server, deps) {
|
|
|
454
465
|
}
|
|
455
466
|
}
|
|
456
467
|
catch { /* ignore */ }
|
|
457
|
-
const checked = checkAnchorStaleness(mapData.anchors, existingFiles);
|
|
468
|
+
const checked = (await lazyAnchor()).checkAnchorStaleness(mapData.anchors, existingFiles);
|
|
458
469
|
// 必须有 module 或 file_paths,否则不返回任何锚点(禁止全量加载)
|
|
459
470
|
if (!query.module && (!query.file_paths || query.file_paths.length === 0))
|
|
460
471
|
return undefined;
|
|
461
|
-
//
|
|
472
|
+
// 收集与任意提供的 file_paths 或 module 匹配的锚点
|
|
462
473
|
const seen = new Set();
|
|
463
474
|
const relevant = [];
|
|
464
475
|
if (query.module) {
|
|
465
|
-
for (const a of filterRelevantAnchors(checked, { module: query.module })) {
|
|
476
|
+
for (const a of (await lazyAnchor()).filterRelevantAnchors(checked, { module: query.module })) {
|
|
466
477
|
if (!seen.has(a.anchor_id)) {
|
|
467
478
|
seen.add(a.anchor_id);
|
|
468
479
|
relevant.push(a);
|
|
@@ -471,7 +482,7 @@ export function registerTools(server, deps) {
|
|
|
471
482
|
}
|
|
472
483
|
if (query.file_paths) {
|
|
473
484
|
for (const fp of query.file_paths) {
|
|
474
|
-
for (const a of filterRelevantAnchors(checked, { file_path: fp })) {
|
|
485
|
+
for (const a of (await lazyAnchor()).filterRelevantAnchors(checked, { file_path: fp })) {
|
|
475
486
|
if (!seen.has(a.anchor_id)) {
|
|
476
487
|
seen.add(a.anchor_id);
|
|
477
488
|
relevant.push(a);
|
|
@@ -508,10 +519,10 @@ export function registerTools(server, deps) {
|
|
|
508
519
|
// 创建任务上下文
|
|
509
520
|
const ctx = await taskContext.create(args.intent, config.product_profile);
|
|
510
521
|
const input = { intent: args.intent, project_path: projectPath, task_id: ctx.task_id };
|
|
511
|
-
const classification = classify(input);
|
|
522
|
+
const classification = (await lazyClassifier()).classify(input);
|
|
512
523
|
// 存储分类结果
|
|
513
524
|
await taskContext.setClassification(ctx.task_id, classification);
|
|
514
|
-
if (!shouldEnterSoloForge(classification.route_decision)) {
|
|
525
|
+
if (!(await lazyIntentRouter()).shouldEnterSoloForge(classification.route_decision)) {
|
|
515
526
|
await taskContext.updateStatus(ctx.task_id, "done");
|
|
516
527
|
}
|
|
517
528
|
return {
|
|
@@ -577,7 +588,7 @@ export function registerTools(server, deps) {
|
|
|
577
588
|
}
|
|
578
589
|
let expansion;
|
|
579
590
|
try {
|
|
580
|
-
expansion = await expand({
|
|
591
|
+
expansion = await (await lazyExpander()).expand({
|
|
581
592
|
intent: ctx.intent,
|
|
582
593
|
classification: ctx.classification,
|
|
583
594
|
projectPath,
|
|
@@ -610,14 +621,22 @@ export function registerTools(server, deps) {
|
|
|
610
621
|
// Privacy Gate: 隐私/敏感信息策略阻断
|
|
611
622
|
const isBlocked = expansion.prompt.startsWith("## 阻塞:输入材料禁止读取")
|
|
612
623
|
|| expansion.prompt.startsWith("## 阻塞:隐私/敏感信息策略");
|
|
613
|
-
|
|
624
|
+
let isMaterialClarification = false;
|
|
625
|
+
if (expansion.prompt.startsWith("## 澄清请求") && expansion.input_materials) {
|
|
626
|
+
for (const m of expansion.input_materials) {
|
|
627
|
+
if (m.access_mode !== "forbidden" && (await lazyInputMaterial()).classifyIngestionStatus(m.path_or_ref) === "requires_confirmation") {
|
|
628
|
+
isMaterialClarification = true;
|
|
629
|
+
break;
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
}
|
|
614
633
|
// 注入 H1/H4 advisory warnings
|
|
615
634
|
const advisories = {};
|
|
616
635
|
if (h1Warning)
|
|
617
636
|
advisories.h1_advisory = h1Warning;
|
|
618
637
|
if (h4LockWarning)
|
|
619
638
|
advisories.h4_advisory = h4LockWarning;
|
|
620
|
-
//
|
|
639
|
+
// 配置优先级警告
|
|
621
640
|
if (expansion.config_resolution_reports && expansion.config_resolution_reports.length > 0) {
|
|
622
641
|
const configWarnings = expansion.config_resolution_reports
|
|
623
642
|
.filter(r => r.conflicts.length > 0)
|
|
@@ -627,7 +646,7 @@ export function registerTools(server, deps) {
|
|
|
627
646
|
}
|
|
628
647
|
}
|
|
629
648
|
// 决策契约 advisory: 校验 expand 输出是否包含默认决策字段
|
|
630
|
-
const decisionContract = validateDecisionOutput(expansion);
|
|
649
|
+
const decisionContract = (await lazyDecision()).validateDecisionOutput(expansion);
|
|
631
650
|
if (!decisionContract.passed) {
|
|
632
651
|
advisories.decision_contract_advisory = decisionContract.advisory;
|
|
633
652
|
}
|
|
@@ -684,7 +703,7 @@ export function registerTools(server, deps) {
|
|
|
684
703
|
result: { error: "任务不存在" },
|
|
685
704
|
};
|
|
686
705
|
}
|
|
687
|
-
// RouteDecision
|
|
706
|
+
// RouteDecision 合同验证 — 无条件,在状态预检之前
|
|
688
707
|
const rdFromCtx = ctx.route_decision ?? ctx.classification?.route_decision ?? null;
|
|
689
708
|
const wtFromCtx = ctx.workflow_trace ?? ctx.expansion?.workflow_trace ?? null;
|
|
690
709
|
const rdContractInput = {
|
|
@@ -719,13 +738,13 @@ export function registerTools(server, deps) {
|
|
|
719
738
|
const h4Warning = !integrity.clean
|
|
720
739
|
? { warning: `H4 advisory: ${integrity.message}`, dirty_files: integrity.dirty_files }
|
|
721
740
|
: undefined;
|
|
722
|
-
const verifyResult = generateVerifyCommands(config, args.changed_files, acceptanceItems, ctx?.expansion?.workflow_trace?.route);
|
|
741
|
+
const verifyResult = (await lazyVerifier()).generateVerifyCommands(config, args.changed_files, acceptanceItems, ctx?.expansion?.workflow_trace?.route);
|
|
723
742
|
verifyResult.task_id = args.task_id;
|
|
724
|
-
//
|
|
743
|
+
// 产物生命周期检查: draft → verified 需要 ArtifactVerificationResult.passed
|
|
725
744
|
let artifactVerificationResult;
|
|
726
745
|
let artifactFileMissing = false;
|
|
727
746
|
if (ctx.artifact_output && ctx.artifact_output.status === "draft") {
|
|
728
|
-
//
|
|
747
|
+
// 验证前解析产物文件并计算 hash
|
|
729
748
|
const artifact = ctx.artifact_output;
|
|
730
749
|
const fullPath = path.join(projectPath, artifact.path);
|
|
731
750
|
if (fss.existsSync(fullPath)) {
|
|
@@ -735,7 +754,7 @@ export function registerTools(server, deps) {
|
|
|
735
754
|
if (!artifact.evidence_refs.some(r => r.startsWith("file:artifact:"))) {
|
|
736
755
|
artifact.evidence_refs.push(`file:artifact:path=${artifact.path} hash=sha256:${hash}`);
|
|
737
756
|
}
|
|
738
|
-
//
|
|
757
|
+
// 保存更新后的产物(含 hash)
|
|
739
758
|
const ctxForHash = await taskContext.load(args.task_id);
|
|
740
759
|
if (ctxForHash?.artifact_output) {
|
|
741
760
|
ctxForHash.artifact_output.hash = artifact.hash;
|
|
@@ -752,7 +771,7 @@ export function registerTools(server, deps) {
|
|
|
752
771
|
}
|
|
753
772
|
// 记录变更文件
|
|
754
773
|
await taskContext.setExecution(args.task_id, args.changed_files);
|
|
755
|
-
//
|
|
774
|
+
// 无实际文件变更但存在产物时标记 no_change_artifact
|
|
756
775
|
if (args.changed_files.length === 0 && ctx.artifact_output) {
|
|
757
776
|
const execCtx = await taskContext.load(args.task_id);
|
|
758
777
|
if (execCtx?.execution) {
|
|
@@ -767,7 +786,7 @@ export function registerTools(server, deps) {
|
|
|
767
786
|
await taskContext.updateStatus(args.task_id, "verifying");
|
|
768
787
|
// 存储验证结果
|
|
769
788
|
await taskContext.setVerification(args.task_id, verifyResult);
|
|
770
|
-
//
|
|
789
|
+
// 产物生命周期转换: draft → verified 仅在 ArtifactVerificationResult.passed 时
|
|
771
790
|
if (ctx.artifact_output && ctx.artifact_output.status === "draft" && artifactVerificationResult?.passed) {
|
|
772
791
|
const evidenceRef = `verification:${ctx.task_id}:checks=${artifactVerificationResult.checks.length}:summary=${artifactVerificationResult.summary.slice(0, 60)}`;
|
|
773
792
|
try {
|
|
@@ -778,11 +797,11 @@ export function registerTools(server, deps) {
|
|
|
778
797
|
});
|
|
779
798
|
}
|
|
780
799
|
catch {
|
|
781
|
-
//
|
|
800
|
+
// 转换被阻止 — 产物保持 draft,流水线继续
|
|
782
801
|
}
|
|
783
802
|
}
|
|
784
803
|
// 认知锚点上下文回放(按变更文件相关性)
|
|
785
|
-
const verifyAnchors = loadRelevantAnchors({ file_paths: args.changed_files });
|
|
804
|
+
const verifyAnchors = await loadRelevantAnchors({ file_paths: args.changed_files });
|
|
786
805
|
const verifyExtras = {};
|
|
787
806
|
if (h4Warning)
|
|
788
807
|
verifyExtras.h4_advisory = h4Warning;
|
|
@@ -829,7 +848,7 @@ export function registerTools(server, deps) {
|
|
|
829
848
|
}
|
|
830
849
|
// 转换到学习阶段
|
|
831
850
|
await taskContext.updateStatus(args.task_id, "learning");
|
|
832
|
-
const learnResult = await evolve({
|
|
851
|
+
const learnResult = await (await lazyEvolver()).evolve({
|
|
833
852
|
taskContext: ctx,
|
|
834
853
|
result: args.result,
|
|
835
854
|
verifyOutput: args.verify_output,
|
|
@@ -872,14 +891,14 @@ export function registerTools(server, deps) {
|
|
|
872
891
|
// ── sf_status: 任务状态查询(current/recent/resume/cancel) ──
|
|
873
892
|
registerSafeTool("sf_status", "查询当前任务状态、列出近期任务、恢复中断任务或取消任务", StatusSchema, async (args) => {
|
|
874
893
|
const action = args.action || "current";
|
|
875
|
-
// Job
|
|
894
|
+
// Job 操作 — advisory,独立于任务操作
|
|
876
895
|
if (args.job_action) {
|
|
877
896
|
if (!args.job_id) {
|
|
878
897
|
return {
|
|
879
898
|
result: { error: "job 操作需要 job_id" },
|
|
880
899
|
};
|
|
881
900
|
}
|
|
882
|
-
const jm = new JobManager(taskContext.getStateDir());
|
|
901
|
+
const jm = new (await lazyJob()).JobManager(taskContext.getStateDir());
|
|
883
902
|
const taskId = args.task_id ?? await jm.resolveTaskId(args.job_id) ?? "";
|
|
884
903
|
if (args.job_action === "job_status") {
|
|
885
904
|
const status = await jm.getJobStatus(args.job_id, taskId);
|
|
@@ -921,12 +940,12 @@ export function registerTools(server, deps) {
|
|
|
921
940
|
};
|
|
922
941
|
}
|
|
923
942
|
}
|
|
924
|
-
//
|
|
943
|
+
// 读取前自动将过期非终态任务标记为失败
|
|
925
944
|
try {
|
|
926
945
|
await taskContext.autoFailStale();
|
|
927
946
|
}
|
|
928
947
|
catch (e) {
|
|
929
|
-
console.error("[soloForge] autoFailStale
|
|
948
|
+
console.error("[soloForge] autoFailStale 出错:", e);
|
|
930
949
|
}
|
|
931
950
|
switch (action) {
|
|
932
951
|
case "current": {
|
|
@@ -960,7 +979,7 @@ export function registerTools(server, deps) {
|
|
|
960
979
|
}
|
|
961
980
|
// JobManager advisory: 查询关联 job 状态
|
|
962
981
|
try {
|
|
963
|
-
const jm = new JobManager(taskContext.getStateDir());
|
|
982
|
+
const jm = new (await lazyJob()).JobManager(taskContext.getStateDir());
|
|
964
983
|
const activeJobs = await jm.listActiveJobs();
|
|
965
984
|
const relatedJob = activeJobs.find((j) => j.task_id === ctx.task_id);
|
|
966
985
|
if (relatedJob) {
|
|
@@ -974,7 +993,7 @@ export function registerTools(server, deps) {
|
|
|
974
993
|
}
|
|
975
994
|
}
|
|
976
995
|
catch {
|
|
977
|
-
// JobManager advisory —
|
|
996
|
+
// JobManager advisory — 忽略错误
|
|
978
997
|
}
|
|
979
998
|
return {
|
|
980
999
|
result: result,
|
|
@@ -1012,7 +1031,7 @@ export function registerTools(server, deps) {
|
|
|
1012
1031
|
if (ctx.execution)
|
|
1013
1032
|
resumeResult.execution = ctx.execution;
|
|
1014
1033
|
const resumeAnchors = ctx.execution?.changed_files
|
|
1015
|
-
? loadRelevantAnchors({ file_paths: ctx.execution.changed_files })
|
|
1034
|
+
? await loadRelevantAnchors({ file_paths: ctx.execution.changed_files })
|
|
1016
1035
|
: undefined;
|
|
1017
1036
|
if (resumeAnchors && resumeAnchors.length > 0)
|
|
1018
1037
|
resumeResult.cognitive_anchors = resumeAnchors;
|
|
@@ -1043,7 +1062,7 @@ export function registerTools(server, deps) {
|
|
|
1043
1062
|
result: { error: "任务不存在或尚未分类" },
|
|
1044
1063
|
};
|
|
1045
1064
|
}
|
|
1046
|
-
const result = planTask({
|
|
1065
|
+
const result = (await lazyPlanner()).planTask({
|
|
1047
1066
|
intent: ctx.intent,
|
|
1048
1067
|
classification: ctx.classification,
|
|
1049
1068
|
config,
|
|
@@ -1105,7 +1124,7 @@ export function registerTools(server, deps) {
|
|
|
1105
1124
|
result: { error: "任务尚未膨胀,请先调用 sf_expand" },
|
|
1106
1125
|
};
|
|
1107
1126
|
}
|
|
1108
|
-
const result = await analyzeImpact({
|
|
1127
|
+
const result = await (await lazyImpact()).analyzeImpact({
|
|
1109
1128
|
intent: ctx.intent,
|
|
1110
1129
|
classification: ctx.classification,
|
|
1111
1130
|
expansion: ctx.expansion,
|
|
@@ -1114,7 +1133,7 @@ export function registerTools(server, deps) {
|
|
|
1114
1133
|
});
|
|
1115
1134
|
result.task_id = args.task_id;
|
|
1116
1135
|
// 决策契约 advisory: 校验 analyze 输出
|
|
1117
|
-
const decisionContract = validateDecisionOutput(result);
|
|
1136
|
+
const decisionContract = (await lazyDecision()).validateDecisionOutput(result);
|
|
1118
1137
|
const advisoryExtras = {};
|
|
1119
1138
|
if (!decisionContract.passed) {
|
|
1120
1139
|
advisoryExtras.decision_contract_advisory = decisionContract.advisory;
|
|
@@ -1129,8 +1148,8 @@ export function registerTools(server, deps) {
|
|
|
1129
1148
|
result: { error: "任务不存在" },
|
|
1130
1149
|
};
|
|
1131
1150
|
}
|
|
1132
|
-
const tracker = new DebtTracker(projectPath);
|
|
1133
|
-
const result = await reviewCode({
|
|
1151
|
+
const tracker = new (await lazyDebt()).DebtTracker(projectPath);
|
|
1152
|
+
const result = await (await lazyReviewer()).reviewCode({
|
|
1134
1153
|
changedFiles: args.changed_files,
|
|
1135
1154
|
projectPath,
|
|
1136
1155
|
config,
|
|
@@ -1142,7 +1161,7 @@ export function registerTools(server, deps) {
|
|
|
1142
1161
|
result.task_id = args.task_id;
|
|
1143
1162
|
await taskContext.setCodeReview(args.task_id, result);
|
|
1144
1163
|
// 决策契约 advisory: 校验 review 输出
|
|
1145
|
-
const decisionContract = validateDecisionOutput(result);
|
|
1164
|
+
const decisionContract = (await lazyDecision()).validateDecisionOutput(result);
|
|
1146
1165
|
const reviewExtras = {};
|
|
1147
1166
|
if (!decisionContract.passed) {
|
|
1148
1167
|
reviewExtras.decision_contract_advisory = decisionContract.advisory;
|
|
@@ -1165,7 +1184,7 @@ export function registerTools(server, deps) {
|
|
|
1165
1184
|
},
|
|
1166
1185
|
};
|
|
1167
1186
|
}
|
|
1168
|
-
const result = await generateScaffold({
|
|
1187
|
+
const result = await (await lazyScaffolder()).generateScaffold({
|
|
1169
1188
|
intent: ctx.intent,
|
|
1170
1189
|
classification: ctx.classification,
|
|
1171
1190
|
config,
|
|
@@ -1189,7 +1208,7 @@ export function registerTools(server, deps) {
|
|
|
1189
1208
|
const h4DeliverWarning = !deliverIntegrity.clean
|
|
1190
1209
|
? { warning: `H4 advisory: ${deliverIntegrity.message}`, dirty_files: deliverIntegrity.dirty_files }
|
|
1191
1210
|
: undefined;
|
|
1192
|
-
//
|
|
1211
|
+
// 产物状态门控: deliver 需要产物状态为 accepted 且有证据
|
|
1193
1212
|
if (ctx.artifact_output) {
|
|
1194
1213
|
const status = ctx.artifact_output.status;
|
|
1195
1214
|
if (status === "draft") {
|
|
@@ -1210,7 +1229,7 @@ export function registerTools(server, deps) {
|
|
|
1210
1229
|
},
|
|
1211
1230
|
};
|
|
1212
1231
|
}
|
|
1213
|
-
// accepted
|
|
1232
|
+
// accepted 必须有确认证据
|
|
1214
1233
|
if (status === "accepted") {
|
|
1215
1234
|
const hasAcceptanceEvidence = ctx.artifact_output.evidence_refs.some((r) => r.includes("acceptance") || r.includes("workflow_adoption"));
|
|
1216
1235
|
if (!hasAcceptanceEvidence) {
|
|
@@ -1224,12 +1243,12 @@ export function registerTools(server, deps) {
|
|
|
1224
1243
|
}
|
|
1225
1244
|
}
|
|
1226
1245
|
}
|
|
1227
|
-
const result = await deliver({
|
|
1246
|
+
const result = await (await lazyDelivery()).deliver({
|
|
1228
1247
|
taskContext: ctx,
|
|
1229
1248
|
config,
|
|
1230
1249
|
knowledgeIndex,
|
|
1231
1250
|
projectPath,
|
|
1232
|
-
gitOps: realGitOps,
|
|
1251
|
+
gitOps: (await lazyGitDeps()).realGitOps,
|
|
1233
1252
|
skipPush: args.skip_push,
|
|
1234
1253
|
skipPr: args.skip_pr,
|
|
1235
1254
|
});
|
|
@@ -1240,11 +1259,11 @@ export function registerTools(server, deps) {
|
|
|
1240
1259
|
});
|
|
1241
1260
|
// ── sf_coord_check: 预测性冲突检测和跨仓库协调 ──
|
|
1242
1261
|
registerSafeTool("sf_coord_check", "预测性冲突检测和跨仓库协调提示,检查分支状态、本地变更和潜在冲突", CoordCheckSchema, async (args) => {
|
|
1243
|
-
const result = await checkConflicts({
|
|
1262
|
+
const result = await (await lazyChangeCoord()).checkConflicts({
|
|
1244
1263
|
projectPath,
|
|
1245
1264
|
config,
|
|
1246
1265
|
branch: args.branch,
|
|
1247
|
-
gitOps: realGitOps,
|
|
1266
|
+
gitOps: (await lazyGitDeps()).realGitOps,
|
|
1248
1267
|
});
|
|
1249
1268
|
return {
|
|
1250
1269
|
result: result,
|
|
@@ -1252,11 +1271,11 @@ export function registerTools(server, deps) {
|
|
|
1252
1271
|
});
|
|
1253
1272
|
// ── sf_team_status: 团队活动流和工作负载查询 ──
|
|
1254
1273
|
registerSafeTool("sf_team_status", "查询团队活动流、成员工作负载、过期分支和知识库更新状态", TeamStatusSchema, async (args) => {
|
|
1255
|
-
const result = await getTeamStatus({
|
|
1274
|
+
const result = await (await lazyTeam()).getTeamStatus({
|
|
1256
1275
|
projectPath,
|
|
1257
1276
|
config,
|
|
1258
1277
|
since: args.since,
|
|
1259
|
-
gitOps: realGitOps,
|
|
1278
|
+
gitOps: (await lazyGitDeps()).realGitOps,
|
|
1260
1279
|
knowledgeIndex: args.include_knowledge ? knowledgeIndex : undefined,
|
|
1261
1280
|
});
|
|
1262
1281
|
return {
|
|
@@ -1270,11 +1289,11 @@ export function registerTools(server, deps) {
|
|
|
1270
1289
|
const ctx = await taskContext.load(args.task_id);
|
|
1271
1290
|
changedFiles = ctx?.execution?.changed_files ?? [];
|
|
1272
1291
|
}
|
|
1273
|
-
const result = await checkContractChanges({
|
|
1292
|
+
const result = await (await lazyContractGuard()).checkContractChanges({
|
|
1274
1293
|
changedFiles,
|
|
1275
1294
|
projectPath,
|
|
1276
1295
|
config,
|
|
1277
|
-
gitOps: realGitOps,
|
|
1296
|
+
gitOps: (await lazyGitDeps()).realGitOps,
|
|
1278
1297
|
});
|
|
1279
1298
|
return {
|
|
1280
1299
|
result: result,
|
|
@@ -1282,11 +1301,11 @@ export function registerTools(server, deps) {
|
|
|
1282
1301
|
});
|
|
1283
1302
|
// ── sf_onboard: 新人分步引导 ──
|
|
1284
1303
|
registerSafeTool("sf_onboard", "新人分步引导:项目概览 → 代码导览 → 知识回顾 → 首个任务建议", OnboardSchema, async (args) => {
|
|
1285
|
-
const result = await onboard({
|
|
1304
|
+
const result = await (await lazyOnboarding()).onboard({
|
|
1286
1305
|
config,
|
|
1287
1306
|
projectPath,
|
|
1288
1307
|
knowledgeIndex,
|
|
1289
|
-
gitOps: realGitOps,
|
|
1308
|
+
gitOps: (await lazyGitDeps()).realGitOps,
|
|
1290
1309
|
currentStep: args.step,
|
|
1291
1310
|
reset: args.reset,
|
|
1292
1311
|
});
|
|
@@ -1302,7 +1321,7 @@ export function registerTools(server, deps) {
|
|
|
1302
1321
|
result: { error: "任务不存在或未完成分类,请先调用 sf_classify" },
|
|
1303
1322
|
};
|
|
1304
1323
|
}
|
|
1305
|
-
const result = checkFeasibility(ctx.classification, config);
|
|
1324
|
+
const result = (await lazyFeasibility()).checkFeasibility(ctx.classification, config);
|
|
1306
1325
|
return {
|
|
1307
1326
|
result: result,
|
|
1308
1327
|
};
|
|
@@ -1313,7 +1332,7 @@ export function registerTools(server, deps) {
|
|
|
1313
1332
|
...config.scope.backend,
|
|
1314
1333
|
...config.scope.frontend,
|
|
1315
1334
|
];
|
|
1316
|
-
const result = debugError(args.error_output, args.task_id || `debug-${Date.now()}`, projectScope);
|
|
1335
|
+
const result = (await lazyDebugger()).debugError(args.error_output, args.task_id || `debug-${Date.now()}`, projectScope);
|
|
1317
1336
|
return {
|
|
1318
1337
|
result: result,
|
|
1319
1338
|
};
|
|
@@ -1321,8 +1340,8 @@ export function registerTools(server, deps) {
|
|
|
1321
1340
|
// ── sf_observability: 系统可观测和运行报告 ──
|
|
1322
1341
|
registerSafeTool("sf_observability", "系统可观测:运行指标、成本估算、告警检测、能力状态、周期报告", ObservabilitySchema, async (args) => {
|
|
1323
1342
|
const stateDir = taskContext.getStateDir();
|
|
1324
|
-
const result = await generateReport(stateDir, args.period_days);
|
|
1325
|
-
const capability =
|
|
1343
|
+
const result = await (await lazyObservability()).generateReport(stateDir, args.period_days);
|
|
1344
|
+
const capability = (await lazyCapability()).getSummary();
|
|
1326
1345
|
const output = { ...result, capability };
|
|
1327
1346
|
return {
|
|
1328
1347
|
result: output,
|
|
@@ -1330,36 +1349,36 @@ export function registerTools(server, deps) {
|
|
|
1330
1349
|
});
|
|
1331
1350
|
// ── sf_migration_check: 数据库迁移安全性分析 ──
|
|
1332
1351
|
registerSafeTool("sf_migration_check", "分析数据库迁移文件安全性:检测破坏性操作(DROP/DELETE/TRUNCATE)并生成回滚建议", MigrationCheckSchema, async (args) => {
|
|
1333
|
-
const result = analyzeMigration(args.content, args.filename);
|
|
1352
|
+
const result = (await lazyMigration()).analyzeMigration(args.content, args.filename);
|
|
1334
1353
|
return {
|
|
1335
1354
|
result: result,
|
|
1336
1355
|
};
|
|
1337
1356
|
});
|
|
1338
1357
|
// ── sf_test_guide: 基于变更文件生成测试指引 ──
|
|
1339
1358
|
registerSafeTool("sf_test_guide", "根据变更文件类型生成测试指引:推荐测试类型、场景、Mock 点和断言模板", TestGuideSchema, async (args) => {
|
|
1340
|
-
const result = generateTestGuide(args.changed_files, [], config);
|
|
1359
|
+
const result = (await lazyTestGen()).generateTestGuide(args.changed_files, [], config);
|
|
1341
1360
|
return {
|
|
1342
1361
|
result: result,
|
|
1343
1362
|
};
|
|
1344
1363
|
});
|
|
1345
1364
|
// ── sf_test_quality: 测试文件质量五维评分 ──
|
|
1346
1365
|
registerSafeTool("sf_test_quality", "评估测试文件质量:断言密度、边界覆盖、命名、重复率、场景覆盖五维评分", TestQualitySchema, async (args) => {
|
|
1347
|
-
const result = analyzeTestQuality(args.content, args.filename);
|
|
1366
|
+
const result = (await lazyTestQuality()).analyzeTestQuality(args.content, args.filename);
|
|
1348
1367
|
return {
|
|
1349
1368
|
result: result,
|
|
1350
1369
|
};
|
|
1351
1370
|
});
|
|
1352
1371
|
// ── sf_dependency_scan: 依赖漏洞扫描 ──
|
|
1353
1372
|
registerSafeTool("sf_dependency_scan", "扫描依赖声明文件漏洞:已知 CVE 规则匹配、未锁定版本检测、支持 npm/maven/gradle", DependencyScanSchema, async (args) => {
|
|
1354
|
-
const result = scanDependencies(args.content, args.filename);
|
|
1373
|
+
const result = (await lazyDepScan()).scanDependencies(args.content, args.filename);
|
|
1355
1374
|
return {
|
|
1356
1375
|
result: result,
|
|
1357
1376
|
};
|
|
1358
1377
|
});
|
|
1359
1378
|
// ── sf_debt_report: 技术债务报告生成 ──
|
|
1360
1379
|
registerSafeTool("sf_debt_report", "生成技术债务报告:按分类/严重度聚合、30 天趋势、优先级排序 Top 5", {}, async () => {
|
|
1361
|
-
const tracker = new DebtTracker(projectPath);
|
|
1362
|
-
const result = await generateDebtReport(tracker);
|
|
1380
|
+
const tracker = new (await lazyDebt()).DebtTracker(projectPath);
|
|
1381
|
+
const result = await (await lazyDebtReport()).generateDebtReport(tracker);
|
|
1363
1382
|
return {
|
|
1364
1383
|
result: result,
|
|
1365
1384
|
};
|
|
@@ -1371,14 +1390,14 @@ export function registerTools(server, deps) {
|
|
|
1371
1390
|
const ctx = await taskContext.load(args.task_id);
|
|
1372
1391
|
classification = ctx?.classification;
|
|
1373
1392
|
}
|
|
1374
|
-
const result = exploreSolutions({
|
|
1393
|
+
const result = (await lazyExploration()).exploreSolutions({
|
|
1375
1394
|
domain_query: args.domain_query,
|
|
1376
1395
|
projectConfig: config,
|
|
1377
1396
|
classification,
|
|
1378
1397
|
knowledgeIndex,
|
|
1379
1398
|
});
|
|
1380
1399
|
// 决策契约 advisory: sf_explore 应始终通过(10 字段已内置)
|
|
1381
|
-
const decisionContract = validateDecisionOutput(result);
|
|
1400
|
+
const decisionContract = (await lazyDecision()).validateDecisionOutput(result);
|
|
1382
1401
|
const exploreExtras = {};
|
|
1383
1402
|
if (!decisionContract.passed) {
|
|
1384
1403
|
exploreExtras.decision_contract_advisory = decisionContract.advisory;
|
|
@@ -1387,7 +1406,7 @@ export function registerTools(server, deps) {
|
|
|
1387
1406
|
});
|
|
1388
1407
|
// ── sf_knowledge_audit: 知识库健康审计 ──
|
|
1389
1408
|
registerSafeTool("sf_knowledge_audit", "审计知识库:识别过时条目、重复触发词、格式缺失、覆盖缺口。定期执行或手动触发", {}, async () => {
|
|
1390
|
-
const result = await auditKnowledge(knowledgeIndex, config);
|
|
1409
|
+
const result = await (await lazyKnowledge()).auditKnowledge(knowledgeIndex, config);
|
|
1391
1410
|
return {
|
|
1392
1411
|
result: result,
|
|
1393
1412
|
};
|
|
@@ -1403,7 +1422,7 @@ export function registerTools(server, deps) {
|
|
|
1403
1422
|
auto_enrich: z.boolean().optional().describe("是否返回行业最佳实践探索指引(默认 true,宿主 AI 将自动执行探索并填充内容)"),
|
|
1404
1423
|
};
|
|
1405
1424
|
registerSafeTool("sf_knowledge_add", "新增知识条目。默认保存为草稿到 .soloforge/knowledge/drafts/,人工确认后移入正式目录", KnowledgeAddSchema, async (args) => {
|
|
1406
|
-
const result = await addKnowledge({
|
|
1425
|
+
const result = await (await lazyKnowledge()).addKnowledge({
|
|
1407
1426
|
title: args.title,
|
|
1408
1427
|
type: args.type,
|
|
1409
1428
|
scope: args.scope,
|
|
@@ -1425,7 +1444,7 @@ export function registerTools(server, deps) {
|
|
|
1425
1444
|
status: z.enum(["active", "deprecated"]).optional().describe("更新条目状态"),
|
|
1426
1445
|
};
|
|
1427
1446
|
registerSafeTool("sf_knowledge_update", "更新已有知识条目:追加规则、更新触发词、标记废弃。自动创建备份", KnowledgeUpdateSchema, async (args) => {
|
|
1428
|
-
const result = await updateKnowledge({
|
|
1447
|
+
const result = await (await lazyKnowledge()).updateKnowledge({
|
|
1429
1448
|
entry_name: args.entry_name,
|
|
1430
1449
|
updates: {
|
|
1431
1450
|
when_triggers: args.when_triggers,
|
|
@@ -1444,13 +1463,13 @@ export function registerTools(server, deps) {
|
|
|
1444
1463
|
});
|
|
1445
1464
|
// ── sf_resume_workspace: 工作区状态唤醒(解决前端刷新导致UUID丢失) ──
|
|
1446
1465
|
registerSafeTool("sf_resume_workspace", "扫描 .soloforge/state/ 目录,恢复中断工单并播报当前进度。新会话启动时必须优先调用", {}, async () => {
|
|
1447
|
-
const result = await scanAndResume(taskContext);
|
|
1466
|
+
const result = await (await lazyWorkspaceResumer()).scanAndResume(taskContext);
|
|
1448
1467
|
// 认知锚点:按恢复任务的 changed_files 相关性加载,禁止全量
|
|
1449
1468
|
if (result.task) {
|
|
1450
1469
|
const ctx = await taskContext.load(result.task.task_id);
|
|
1451
1470
|
const changedFiles = ctx?.execution?.changed_files;
|
|
1452
1471
|
if (changedFiles && changedFiles.length > 0) {
|
|
1453
|
-
const anchors = loadRelevantAnchors({ file_paths: changedFiles });
|
|
1472
|
+
const anchors = await loadRelevantAnchors({ file_paths: changedFiles });
|
|
1454
1473
|
if (anchors && anchors.length > 0) {
|
|
1455
1474
|
result.cognitive_anchors = anchors;
|
|
1456
1475
|
}
|
|
@@ -1458,7 +1477,7 @@ export function registerTools(server, deps) {
|
|
|
1458
1477
|
}
|
|
1459
1478
|
// JobManager advisory: 查询活跃 jobs
|
|
1460
1479
|
try {
|
|
1461
|
-
const jm = new JobManager(taskContext.getStateDir());
|
|
1480
|
+
const jm = new (await lazyJob()).JobManager(taskContext.getStateDir());
|
|
1462
1481
|
const activeJobs = await jm.listActiveJobs();
|
|
1463
1482
|
if (activeJobs.length > 0) {
|
|
1464
1483
|
result.active_jobs = activeJobs.map((j) => ({
|
|
@@ -1472,7 +1491,7 @@ export function registerTools(server, deps) {
|
|
|
1472
1491
|
}
|
|
1473
1492
|
}
|
|
1474
1493
|
catch {
|
|
1475
|
-
// JobManager advisory —
|
|
1494
|
+
// JobManager advisory — 忽略错误
|
|
1476
1495
|
}
|
|
1477
1496
|
return {
|
|
1478
1497
|
result: result,
|
|
@@ -1484,10 +1503,10 @@ export function registerTools(server, deps) {
|
|
|
1484
1503
|
};
|
|
1485
1504
|
registerSafeTool("sf_audit_sample", "从审计池按风险加权抽样,生成抽检清单(SamplingDecision 列表)。只读,不落盘,不生成逃逸报告,不改能力状态", AuditSampleSchema, async (args) => {
|
|
1486
1505
|
const stateDir = taskContext.getStateDir();
|
|
1487
|
-
const pool = new AuditPool(stateDir);
|
|
1506
|
+
const pool = new (await lazyAuditPool()).AuditPool(stateDir);
|
|
1488
1507
|
const items = pool.list();
|
|
1489
1508
|
const seed = args.seed ?? 0;
|
|
1490
|
-
const result = sampleAuditItems(items, seed);
|
|
1509
|
+
const result = (await lazyAuditSampler()).sampleAuditItems(items, seed);
|
|
1491
1510
|
return {
|
|
1492
1511
|
result: result,
|
|
1493
1512
|
};
|
|
@@ -1510,7 +1529,7 @@ export function registerTools(server, deps) {
|
|
|
1510
1529
|
};
|
|
1511
1530
|
registerSafeTool("sf_escape_report", "记录逃逸、误伤或工具故障报告。只落盘记录,不自动降级,不改能力状态", EscapeReportSchema, async (args) => {
|
|
1512
1531
|
const stateDir = taskContext.getStateDir();
|
|
1513
|
-
const store = new EscapeReportStore(stateDir);
|
|
1532
|
+
const store = new (await lazyEscape()).EscapeReportStore(stateDir);
|
|
1514
1533
|
const report = {
|
|
1515
1534
|
escape_id: args.escape_id,
|
|
1516
1535
|
task_id: args.task_id,
|
|
@@ -1543,10 +1562,10 @@ export function registerTools(server, deps) {
|
|
|
1543
1562
|
};
|
|
1544
1563
|
registerSafeTool("sf_capability_update", "根据复盘结果更新 Capability Registry 状态。支持 dry_run 校验、证据校验、人工确认。不会自动触发,必须显式调用", CapabilityUpdateSchema, async (args) => {
|
|
1545
1564
|
const stateDir = taskContext.getStateDir();
|
|
1546
|
-
const escapeStore = new EscapeReportStore(stateDir);
|
|
1547
|
-
const stateStore = new CapabilityStateStore(stateDir);
|
|
1565
|
+
const escapeStore = new (await lazyEscape()).EscapeReportStore(stateDir);
|
|
1566
|
+
const stateStore = new (await lazyCapState()).CapabilityStateStore(stateDir);
|
|
1548
1567
|
const escapeReports = escapeStore.list();
|
|
1549
|
-
const capabilities = getAllCapabilities();
|
|
1568
|
+
const capabilities = (await lazyCapability()).getAllCapabilities();
|
|
1550
1569
|
const cap = capabilities.find((c) => c.policy_id === args.policy_id);
|
|
1551
1570
|
const currentState = stateStore.getEffectiveState(args.policy_id, cap?.state ?? "experimental");
|
|
1552
1571
|
const result = stateStore.apply({
|
|
@@ -1569,25 +1588,25 @@ export function registerTools(server, deps) {
|
|
|
1569
1588
|
registerSafeTool("sf_governance_report", "生成治理健康报告:汇总审计池、逃逸报告、能力动作决策。只读,不改状态", GovernanceReportSchema, async (args) => {
|
|
1570
1589
|
const stateDir = taskContext.getStateDir();
|
|
1571
1590
|
const seed = args.seed ?? 0;
|
|
1572
|
-
const pool = new AuditPool(stateDir);
|
|
1573
|
-
const escapeStore = new EscapeReportStore(stateDir);
|
|
1591
|
+
const pool = new (await lazyAuditPool()).AuditPool(stateDir);
|
|
1592
|
+
const escapeStore = new (await lazyEscape()).EscapeReportStore(stateDir);
|
|
1574
1593
|
const auditItems = pool.list();
|
|
1575
1594
|
const auditStats = pool.stats();
|
|
1576
1595
|
const escapeReports = escapeStore.list();
|
|
1577
1596
|
const escapeStats = escapeStore.stats();
|
|
1578
|
-
//
|
|
1579
|
-
const capabilities = getAllCapabilities();
|
|
1580
|
-
const stateStore = new CapabilityStateStore(stateDir);
|
|
1581
|
-
const decisions = capabilities.map((cap) => {
|
|
1597
|
+
// 为所有能力生成动作决策(使用有效状态)
|
|
1598
|
+
const capabilities = (await lazyCapability()).getAllCapabilities();
|
|
1599
|
+
const stateStore = new (await lazyCapState()).CapabilityStateStore(stateDir);
|
|
1600
|
+
const decisions = await Promise.all(capabilities.map(async (cap) => {
|
|
1582
1601
|
const effectiveState = stateStore.getEffectiveState(cap.policy_id, cap.state);
|
|
1583
|
-
return decideAction(escapeReports, effectiveState, cap.policy_id);
|
|
1584
|
-
});
|
|
1585
|
-
//
|
|
1586
|
-
const sampleResult = sampleAuditItems(auditItems, seed);
|
|
1587
|
-
//
|
|
1588
|
-
const dlFindings = validateMechanismLayerMaps();
|
|
1589
|
-
const dlMechanismCount = listMechanismLayerMaps().length;
|
|
1590
|
-
//
|
|
1602
|
+
return (await lazyCapAdvisor()).decideAction(escapeReports, effectiveState, cap.policy_id);
|
|
1603
|
+
}));
|
|
1604
|
+
// 为 sampled_count 生成抽样决策
|
|
1605
|
+
const sampleResult = (await lazyAuditSampler()).sampleAuditItems(auditItems, seed);
|
|
1606
|
+
// 双层机制发现
|
|
1607
|
+
const dlFindings = (await lazyDualLayer()).validateMechanismLayerMaps();
|
|
1608
|
+
const dlMechanismCount = (await lazyDualLayer()).listMechanismLayerMaps().length;
|
|
1609
|
+
// 收集近期任务的产物记录
|
|
1591
1610
|
const recentTasks = await taskContext.listRecent(20);
|
|
1592
1611
|
const artifacts = [];
|
|
1593
1612
|
for (const t of recentTasks) {
|
|
@@ -1595,25 +1614,25 @@ export function registerTools(server, deps) {
|
|
|
1595
1614
|
if (tCtx?.artifact_output)
|
|
1596
1615
|
artifacts.push(tCtx.artifact_output);
|
|
1597
1616
|
}
|
|
1598
|
-
//
|
|
1617
|
+
// 收集配置解析报告 — 使用共享函数获取真实数据
|
|
1599
1618
|
let configReports = [];
|
|
1600
1619
|
let configEntries = [];
|
|
1601
1620
|
try {
|
|
1602
|
-
const resolved = await resolveCurrentProjectConfigReports(projectPath);
|
|
1621
|
+
const resolved = await (await lazyConfigPrecedence()).resolveCurrentProjectConfigReports(projectPath);
|
|
1603
1622
|
configReports = resolved.reports;
|
|
1604
1623
|
configEntries = resolved.entries;
|
|
1605
1624
|
}
|
|
1606
1625
|
catch (e) {
|
|
1607
1626
|
console.error(`[soloForge] 配置报告解析失败: ${e instanceof Error ? e.message : String(e)}`);
|
|
1608
1627
|
}
|
|
1609
|
-
//
|
|
1628
|
+
// 同时包含近期任务上下文中的报告
|
|
1610
1629
|
for (const t of recentTasks) {
|
|
1611
1630
|
const tCtx = await taskContext.load(t.task_id);
|
|
1612
1631
|
if (tCtx?.expansion?.config_resolution_reports) {
|
|
1613
1632
|
configReports.push(...tCtx.expansion.config_resolution_reports);
|
|
1614
1633
|
}
|
|
1615
1634
|
}
|
|
1616
|
-
const report =
|
|
1635
|
+
const report = (await lazyGovernance()).generateReport(auditStats, auditItems, escapeReports, escapeStats, decisions, new Date(), sampleResult.decisions, dlFindings, dlMechanismCount, artifacts.length > 0 ? artifacts : undefined, configReports.length > 0 ? configReports : undefined, configEntries.length > 0 ? configEntries : undefined);
|
|
1617
1636
|
return {
|
|
1618
1637
|
result: report,
|
|
1619
1638
|
};
|
|
@@ -1625,7 +1644,7 @@ export function registerTools(server, deps) {
|
|
|
1625
1644
|
};
|
|
1626
1645
|
registerSafeTool("sf_audit_integration", "审计生产代码的主链路集成状态,检测孤岛模块(只被测试导入、无生产代码调用的模块)", AuditIntegrationSchema, async (args) => {
|
|
1627
1646
|
const projectPath = config._projectPath || process.cwd();
|
|
1628
|
-
//
|
|
1647
|
+
// 使用作用域感知的生产文件收集(支持多仓库、多语言)
|
|
1629
1648
|
const { collectAllProductionFiles } = await import("../../engine/main_path_integration_contract.js");
|
|
1630
1649
|
const allProdFiles = collectAllProductionFiles(projectPath, config);
|
|
1631
1650
|
if (allProdFiles.length === 0) {
|
|
@@ -1636,8 +1655,8 @@ export function registerTools(server, deps) {
|
|
|
1636
1655
|
const targetFiles = args.changed_files
|
|
1637
1656
|
? allProdFiles.filter((rel) => args.changed_files.some((cf) => rel === cf || rel.startsWith(cf.replace(/\.(ts|tsx|js|jsx|java|kt|go|py)$/, ""))))
|
|
1638
1657
|
: allProdFiles;
|
|
1639
|
-
const contracts = buildMainPathIntegrationContracts(projectPath, targetFiles, config);
|
|
1640
|
-
const report = auditIntegration(contracts);
|
|
1658
|
+
const contracts = (await lazyMainPath()).buildMainPathIntegrationContracts(projectPath, targetFiles, config);
|
|
1659
|
+
const report = (await lazyMainPath()).auditIntegration(contracts);
|
|
1641
1660
|
if (args.json_output) {
|
|
1642
1661
|
return {
|
|
1643
1662
|
result: report,
|