soloforge 1.2.18 → 1.2.20
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 +348 -171
- 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_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/contract_registry.d.ts +113 -0
- package/dist/engine/contract_registry.d.ts.map +1 -0
- package/dist/engine/contract_registry.js +1159 -0
- package/dist/engine/contract_registry.js.map +1 -0
- package/dist/engine/contract_state_store.d.ts +67 -0
- package/dist/engine/contract_state_store.d.ts.map +1 -0
- package/dist/engine/contract_state_store.js +167 -0
- package/dist/engine/contract_state_store.js.map +1 -0
- 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/debugger.d.ts.map +1 -1
- package/dist/engine/debugger.js +30 -1
- package/dist/engine/debugger.js.map +1 -1
- package/dist/engine/degradation.d.ts +58 -0
- package/dist/engine/degradation.d.ts.map +1 -0
- package/dist/engine/degradation.js +74 -0
- package/dist/engine/degradation.js.map +1 -0
- package/dist/engine/delivery_readiness.d.ts +66 -0
- package/dist/engine/delivery_readiness.d.ts.map +1 -0
- package/dist/engine/delivery_readiness.js +176 -0
- package/dist/engine/delivery_readiness.js.map +1 -0
- package/dist/engine/deprecated_contract.d.ts +21 -0
- package/dist/engine/deprecated_contract.d.ts.map +1 -0
- package/dist/engine/deprecated_contract.js +14 -0
- package/dist/engine/deprecated_contract.js.map +1 -0
- package/dist/engine/developer_sovereignty.js +15 -15
- package/dist/engine/developer_sovereignty.js.map +1 -1
- package/dist/engine/diagnostic_registry.d.ts +64 -0
- package/dist/engine/diagnostic_registry.d.ts.map +1 -0
- package/dist/engine/diagnostic_registry.js +176 -0
- package/dist/engine/diagnostic_registry.js.map +1 -0
- 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/enforcement_guard.d.ts +84 -0
- package/dist/engine/enforcement_guard.d.ts.map +1 -0
- package/dist/engine/enforcement_guard.js +305 -0
- package/dist/engine/enforcement_guard.js.map +1 -0
- 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/failure_report.d.ts +74 -0
- package/dist/engine/failure_report.d.ts.map +1 -0
- package/dist/engine/failure_report.js +143 -0
- package/dist/engine/failure_report.js.map +1 -0
- package/dist/engine/governance_report.d.ts +9 -1
- package/dist/engine/governance_report.d.ts.map +1 -1
- package/dist/engine/governance_report.js +24 -10
- package/dist/engine/governance_report.js.map +1 -1
- package/dist/engine/implementation_roadmap_registry.d.ts +1 -1
- package/dist/engine/implementation_roadmap_registry.d.ts.map +1 -1
- package/dist/engine/implementation_roadmap_registry.js +37 -29
- 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/knowledge_lifecycle.d.ts +23 -0
- package/dist/engine/knowledge_lifecycle.d.ts.map +1 -0
- package/dist/engine/knowledge_lifecycle.js +13 -0
- package/dist/engine/knowledge_lifecycle.js.map +1 -0
- package/dist/engine/language_policy.d.ts +21 -0
- package/dist/engine/language_policy.d.ts.map +1 -0
- package/dist/engine/language_policy.js +18 -0
- package/dist/engine/language_policy.js.map +1 -0
- package/dist/engine/language_policy_contract.d.ts +52 -0
- package/dist/engine/language_policy_contract.d.ts.map +1 -0
- package/dist/engine/language_policy_contract.js +78 -0
- package/dist/engine/language_policy_contract.js.map +1 -0
- 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/mechanism_family_registry.d.ts +48 -0
- package/dist/engine/mechanism_family_registry.d.ts.map +1 -0
- package/dist/engine/mechanism_family_registry.js +197 -0
- package/dist/engine/mechanism_family_registry.js.map +1 -0
- package/dist/engine/onboarding.js +20 -20
- package/dist/engine/onboarding.js.map +1 -1
- package/dist/engine/policy_drift_detector.d.ts +20 -0
- package/dist/engine/policy_drift_detector.d.ts.map +1 -1
- package/dist/engine/policy_drift_detector.js +79 -0
- package/dist/engine/policy_drift_detector.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/retention_policy.d.ts +46 -0
- package/dist/engine/retention_policy.d.ts.map +1 -0
- package/dist/engine/retention_policy.js +124 -0
- package/dist/engine/retention_policy.js.map +1 -0
- 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/state_update_bypass.d.ts +19 -0
- package/dist/engine/state_update_bypass.d.ts.map +1 -0
- package/dist/engine/state_update_bypass.js +17 -0
- package/dist/engine/state_update_bypass.js.map +1 -0
- package/dist/engine/task_context.d.ts.map +1 -1
- package/dist/engine/task_context.js +11 -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/test_strategy.d.ts +58 -0
- package/dist/engine/test_strategy.d.ts.map +1 -0
- package/dist/engine/test_strategy.js +93 -0
- package/dist/engine/test_strategy.js.map +1 -0
- package/dist/engine/tool_invocation_contract_registry.d.ts.map +1 -1
- package/dist/engine/tool_invocation_contract_registry.js +29 -19
- 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/verification_contract.d.ts +132 -0
- package/dist/engine/verification_contract.d.ts.map +1 -0
- package/dist/engine/verification_contract.js +296 -0
- package/dist/engine/verification_contract.js.map +1 -0
- 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/workspace_lease.d.ts +69 -0
- package/dist/engine/workspace_lease.d.ts.map +1 -0
- package/dist/engine/workspace_lease.js +153 -0
- package/dist/engine/workspace_lease.js.map +1 -0
- 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/dist/types.d.ts +13 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +4 -2
|
@@ -2,52 +2,71 @@ 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"));
|
|
62
|
+
const lazyEnforcementGuard = createLazy(() => import("../../engine/enforcement_guard.js"));
|
|
63
|
+
const lazyWorkspaceLease = createLazy(() => import("../../engine/workspace_lease.js"));
|
|
64
|
+
const lazyDeliveryReadiness = createLazy(() => import("../../engine/delivery_readiness.js"));
|
|
65
|
+
const lazyVerificationContract = createLazy(() => import("../../engine/verification_contract.js"));
|
|
66
|
+
const lazyFailureReport = createLazy(() => import("../../engine/failure_report.js"));
|
|
67
|
+
const lazyDegradation = createLazy(() => import("../../engine/degradation.js"));
|
|
68
|
+
const lazyRetentionPolicy = createLazy(() => import("../../engine/retention_policy.js"));
|
|
69
|
+
const lazyDiagnosticRegistry = createLazy(() => import("../../engine/diagnostic_registry.js"));
|
|
51
70
|
// ── Zod Schema 定义 ──
|
|
52
71
|
const ClassifySchema = {
|
|
53
72
|
intent: z.string().describe("开发者意图描述"),
|
|
@@ -92,6 +111,19 @@ const ReviewSchema = {
|
|
|
92
111
|
const ScaffoldSchema = {
|
|
93
112
|
task_id: z.string().describe("sf_classify 返回的任务 ID(task_type 应为 scaffold)"),
|
|
94
113
|
};
|
|
114
|
+
const RecordVerificationExecutionSchema = {
|
|
115
|
+
task_id: z.string().describe("任务 ID"),
|
|
116
|
+
plan_id: z.string().describe("关联的 VerificationPlan ID"),
|
|
117
|
+
execution_records: z.array(z.object({
|
|
118
|
+
command: z.string().describe("执行的验证命令"),
|
|
119
|
+
exit_code: z.number().describe("命令退出码,0 表示通过"),
|
|
120
|
+
stdout_hash: z.string().describe("stdout 的 SHA256 hash"),
|
|
121
|
+
stderr_hash: z.string().describe("stderr 的 SHA256 hash"),
|
|
122
|
+
started_at: z.string().describe("命令开始时间 ISO 8601"),
|
|
123
|
+
finished_at: z.string().describe("命令结束时间 ISO 8601"),
|
|
124
|
+
evidence_id: z.string().describe("执行证据 ID"),
|
|
125
|
+
})).describe("真实执行记录列表"),
|
|
126
|
+
};
|
|
95
127
|
const DeliverSchema = {
|
|
96
128
|
task_id: z.string().describe("任务 ID"),
|
|
97
129
|
changed_files: z.array(z.string()).optional().describe("变更文件列表(不传则使用任务记录中的文件)"),
|
|
@@ -161,14 +193,14 @@ const ExploreSchema = {
|
|
|
161
193
|
* @param server - McpServer 实例
|
|
162
194
|
* @param deps - 工具依赖项(配置、知识索引、任务上下文)
|
|
163
195
|
*/
|
|
164
|
-
export function registerTools(server, deps) {
|
|
196
|
+
export async function registerTools(server, deps) {
|
|
165
197
|
console.error("[soloForge] 工具注册: 开始注册 MCP 工具...");
|
|
166
198
|
const { config, knowledgeIndex, taskContext } = deps;
|
|
167
199
|
const projectPath = config._projectPath || process.cwd();
|
|
168
200
|
// H1: LLM Gateway — Token 预算熔断,防止单任务 Token 超支
|
|
169
|
-
const gateway = deps.gateway ?? new LLMGateway();
|
|
201
|
+
const gateway = deps.gateway ?? new (await lazyLLM()).LLMGateway();
|
|
170
202
|
// H4: IO Controller — 工作区互斥锁,防止外部修改导致的静默覆盖
|
|
171
|
-
const ioController = new IOController(projectPath);
|
|
203
|
+
const ioController = new (await lazyIO()).IOController(projectPath);
|
|
172
204
|
// ── 统一网关 : 唯一工具调用边界 ──
|
|
173
205
|
/** 计算 sf_status cancel 的动态 category */
|
|
174
206
|
function computeEffectiveCategory(toolName, args, contract) {
|
|
@@ -187,48 +219,50 @@ export function registerTools(server, deps) {
|
|
|
187
219
|
/** State precheck: validate task status before contract guard */
|
|
188
220
|
const STATE_PRECHECKS = {
|
|
189
221
|
sf_verify: ["executing", "retrying"],
|
|
222
|
+
sf_record_verification_execution: ["verifying", "executing"],
|
|
190
223
|
sf_learn: ["verifying", "executing"],
|
|
191
224
|
sf_expand: ["classifying", "expanding", "clarifying"],
|
|
192
225
|
};
|
|
193
226
|
const STATE_ERROR_LABELS = {
|
|
194
227
|
sf_verify: "不可验证",
|
|
228
|
+
sf_record_verification_execution: "不可录入验证结果",
|
|
195
229
|
sf_learn: "不可学习",
|
|
196
230
|
sf_expand: "不可膨胀",
|
|
197
231
|
};
|
|
198
232
|
/** 授权模型: explicit > dynamic write gate > destructive gate > workflow gate > category default */
|
|
199
233
|
function checkAuth(toolName, args, contract, ctx, effectiveCategory) {
|
|
200
|
-
// 1.
|
|
234
|
+
// 1. 显式确认
|
|
201
235
|
if (args.confirm === true || args.authorized === true || args.authorization_token) {
|
|
202
236
|
return { required: true, granted: true, reason: "explicit confirmation" };
|
|
203
237
|
}
|
|
204
|
-
// 2.
|
|
238
|
+
// 2. 动态写入升级: cancel/job_cancel 从 read_only 变为 write → 必须确认
|
|
205
239
|
if (effectiveCategory && effectiveCategory !== contract.category && effectiveCategory !== "read_only_bypass") {
|
|
206
240
|
return { required: true, granted: false, reason: "dynamic write upgrade requires explicit confirmation" };
|
|
207
241
|
}
|
|
208
|
-
// 3.
|
|
242
|
+
// 3. 破坏性副作用始终需要显式确认
|
|
209
243
|
const destructive = ["git_commit", "git_push", "pr_create", "external_write"];
|
|
210
244
|
if (contract.side_effects.some((e) => destructive.includes(e))) {
|
|
211
245
|
return { required: true, granted: false, reason: "destructive side effects require explicit confirmation" };
|
|
212
246
|
}
|
|
213
|
-
// 4.
|
|
247
|
+
// 4. 独立严格工具(无 workflow 要求,无 task context)→ 由 handler 自行检查
|
|
214
248
|
if (contract.category === "strict_controlled" && !ctx && !contract.requires_workflow) {
|
|
215
249
|
return { required: false, granted: true, reason: "standalone strict tool" };
|
|
216
250
|
}
|
|
217
|
-
// 5.
|
|
251
|
+
// 5. strict_controlled 的 workflow 合同门控
|
|
218
252
|
if (contract.category === "strict_controlled" && ctx?.last_tool_trace) {
|
|
219
253
|
const allowed = ctx.last_tool_trace.next_allowed_tools ?? [];
|
|
220
254
|
if (allowed.includes(toolName)) {
|
|
221
255
|
return { required: true, granted: true, reason: "workflow contract gate" };
|
|
222
256
|
}
|
|
223
257
|
}
|
|
224
|
-
// 5b.
|
|
258
|
+
// 5b. 基于状态的回退: 严格工具且任务处于有效状态但无 tool_trace
|
|
225
259
|
if (contract.category === "strict_controlled" && ctx && !ctx.last_tool_trace && contract.requires_workflow) {
|
|
226
260
|
const validStates = STATE_PRECHECKS[toolName];
|
|
227
261
|
if (validStates && validStates.includes(ctx.status)) {
|
|
228
262
|
return { required: true, granted: true, reason: "task in valid state for tool" };
|
|
229
263
|
}
|
|
230
264
|
}
|
|
231
|
-
// 5c.
|
|
265
|
+
// 5c. 非严格工具: 自动授权,除非 requires_authorization
|
|
232
266
|
if (contract.category === "read_only_bypass" || contract.category === "normal_controlled") {
|
|
233
267
|
if (contract.requires_authorization) {
|
|
234
268
|
return { required: true, granted: false, reason: "tool requires explicit authorization" };
|
|
@@ -242,7 +276,7 @@ export function registerTools(server, deps) {
|
|
|
242
276
|
const invocationId = `inv-${Date.now()}-${Math.random().toString(36).slice(2, 8)}`;
|
|
243
277
|
const taskId = args.task_id;
|
|
244
278
|
const contract = findToolInvocationContractByName(name);
|
|
245
|
-
//
|
|
279
|
+
// 无合同 → hard fail
|
|
246
280
|
if (!contract) {
|
|
247
281
|
return {
|
|
248
282
|
content: [{ type: "text", text: JSON.stringify({ error: `工具 ${name} 未注册契约` }) }],
|
|
@@ -251,12 +285,12 @@ export function registerTools(server, deps) {
|
|
|
251
285
|
}
|
|
252
286
|
const effectiveCategory = computeEffectiveCategory(name, args, contract);
|
|
253
287
|
const effectiveSideEffects = computeEffectiveSideEffects(name, args, contract);
|
|
254
|
-
//
|
|
288
|
+
// 加载 TaskContext
|
|
255
289
|
let ctx = null;
|
|
256
290
|
if (taskId) {
|
|
257
291
|
ctx = await taskContext.load(taskId);
|
|
258
292
|
}
|
|
259
|
-
//
|
|
293
|
+
// 状态预检: 在合同守卫之前验证任务状态
|
|
260
294
|
if (taskId && STATE_PRECHECKS[name] && ctx) {
|
|
261
295
|
const validStates = STATE_PRECHECKS[name];
|
|
262
296
|
if (!validStates.includes(ctx.status)) {
|
|
@@ -270,7 +304,7 @@ export function registerTools(server, deps) {
|
|
|
270
304
|
};
|
|
271
305
|
}
|
|
272
306
|
}
|
|
273
|
-
//
|
|
307
|
+
// 严格受控但缺少 task_id
|
|
274
308
|
const isStandaloneStrict = !contract.requires_workflow;
|
|
275
309
|
if (contract.category === "strict_controlled" && !taskId && !isStandaloneStrict) {
|
|
276
310
|
return {
|
|
@@ -278,11 +312,11 @@ export function registerTools(server, deps) {
|
|
|
278
312
|
isError: true,
|
|
279
313
|
};
|
|
280
314
|
}
|
|
281
|
-
//
|
|
315
|
+
// 授权检查
|
|
282
316
|
const authorization = checkAuth(name, args, contract, ctx ?? undefined, effectiveCategory);
|
|
283
|
-
//
|
|
284
|
-
//
|
|
285
|
-
// Handler
|
|
317
|
+
// 授权拒绝 → 仅对网关级拒绝执行 hard fail
|
|
318
|
+
// (动态写入升级、破坏性操作、无授权源的 strict_controlled)
|
|
319
|
+
// Handler 级授权(如 sf_capability_update confirm)由 handler 自行处理
|
|
286
320
|
const gatewayDenial = !authorization.granted &&
|
|
287
321
|
authorization.reason !== "tool requires explicit authorization";
|
|
288
322
|
if (gatewayDenial) {
|
|
@@ -312,17 +346,17 @@ export function registerTools(server, deps) {
|
|
|
312
346
|
isError: true,
|
|
313
347
|
};
|
|
314
348
|
}
|
|
315
|
-
//
|
|
349
|
+
// read_only_bypass 的旁路处理
|
|
316
350
|
const bypass = effectiveCategory === "read_only_bypass"
|
|
317
351
|
? { allowed: true, reason: "read_only" } : undefined;
|
|
318
|
-
//
|
|
352
|
+
// 合同验证
|
|
319
353
|
const lastTrace = ctx?.last_tool_trace;
|
|
320
|
-
//
|
|
354
|
+
// 为动态工具构建有效的合同覆盖(如 sf_status cancel)
|
|
321
355
|
let contractOverride;
|
|
322
356
|
if (effectiveCategory !== contract.category || effectiveSideEffects !== contract.side_effects) {
|
|
323
357
|
contractOverride = { ...contract, category: effectiveCategory, side_effects: effectiveSideEffects };
|
|
324
358
|
}
|
|
325
|
-
// Workflow ID:
|
|
359
|
+
// Workflow ID: 仅来自真实的 expansion trace
|
|
326
360
|
const explicitWorkflowId = ctx?.expansion?.workflow_trace?.workflow_id;
|
|
327
361
|
const violations = validateToolInvocation({
|
|
328
362
|
tool_name: name,
|
|
@@ -334,7 +368,7 @@ export function registerTools(server, deps) {
|
|
|
334
368
|
workflow_id: explicitWorkflowId,
|
|
335
369
|
_contractOverride: contractOverride,
|
|
336
370
|
});
|
|
337
|
-
//
|
|
371
|
+
// 基于状态的回退: 将 contract_state_mismatch 从 hard_fail 降级为 require_human
|
|
338
372
|
if (authorization.reason === "task in valid state for tool") {
|
|
339
373
|
for (const v of violations) {
|
|
340
374
|
if (v.violation_type === "contract_state_mismatch" && v.severity === "hard_fail") {
|
|
@@ -343,7 +377,7 @@ export function registerTools(server, deps) {
|
|
|
343
377
|
}
|
|
344
378
|
}
|
|
345
379
|
}
|
|
346
|
-
// Hard-fail
|
|
380
|
+
// Hard-fail 违规 → 阻止
|
|
347
381
|
const hardFail = violations.find(v => v.severity === "hard_fail");
|
|
348
382
|
if (hardFail) {
|
|
349
383
|
const blockedTrace = createToolTrace({
|
|
@@ -366,17 +400,17 @@ export function registerTools(server, deps) {
|
|
|
366
400
|
isError: true,
|
|
367
401
|
};
|
|
368
402
|
}
|
|
369
|
-
//
|
|
403
|
+
// 执行 handler
|
|
370
404
|
try {
|
|
371
405
|
const raw = await handler(args);
|
|
372
|
-
//
|
|
406
|
+
// 如果 handler 直接返回 { content } 则透传
|
|
373
407
|
if (raw && typeof raw === "object" && "content" in raw && Array.isArray(raw.content)) {
|
|
374
408
|
return raw;
|
|
375
409
|
}
|
|
376
|
-
//
|
|
410
|
+
// 从 { result } 模式中提取数据
|
|
377
411
|
const data = raw?.result !== undefined ? raw.result : raw;
|
|
378
412
|
const hasError = !!(data && typeof data === "object" && "error" in data);
|
|
379
|
-
//
|
|
413
|
+
// 构建 trace
|
|
380
414
|
const trace = createToolTrace({
|
|
381
415
|
tool_name: name, invocation_id: invocationId, task_id: taskId,
|
|
382
416
|
workflow_id: ctx?.expansion?.workflow_trace?.workflow_id,
|
|
@@ -385,18 +419,18 @@ export function registerTools(server, deps) {
|
|
|
385
419
|
forbidden_tools: contract.forbidden_next_tools,
|
|
386
420
|
authorization, bypass,
|
|
387
421
|
});
|
|
388
|
-
//
|
|
422
|
+
// 将 trace 写入 TaskContext
|
|
389
423
|
if (taskId) {
|
|
390
424
|
await taskContext.setToolTrace(taskId, trace, violations.length > 0 ? violations : undefined);
|
|
391
425
|
}
|
|
392
|
-
//
|
|
426
|
+
// 用 trace + next/forbidden 装饰响应
|
|
393
427
|
const response = {
|
|
394
428
|
...data,
|
|
395
429
|
tool_trace: trace,
|
|
396
430
|
next_allowed_tools: contract.default_next_tools,
|
|
397
431
|
forbidden_tools: contract.forbidden_next_tools,
|
|
398
432
|
};
|
|
399
|
-
//
|
|
433
|
+
// 基于状态的回退: 在响应中暴露降级 workflow
|
|
400
434
|
if (authorization.reason === "task in valid state for tool") {
|
|
401
435
|
response.degraded_workflow = true;
|
|
402
436
|
response.workflow_source = "state-based-fallback";
|
|
@@ -433,14 +467,14 @@ export function registerTools(server, deps) {
|
|
|
433
467
|
});
|
|
434
468
|
}
|
|
435
469
|
// 加载认知锚点上下文 — 必须提供相关性参数,禁止全量加载
|
|
436
|
-
function loadRelevantAnchors(query) {
|
|
470
|
+
async function loadRelevantAnchors(query) {
|
|
437
471
|
console.error("[soloForge] 工具注册: 加载认知锚点");
|
|
438
472
|
return _loadRelevantAnchorsInner(query);
|
|
439
473
|
}
|
|
440
|
-
function _loadRelevantAnchorsInner(query) {
|
|
474
|
+
async function _loadRelevantAnchorsInner(query) {
|
|
441
475
|
const stateDir = taskContext.getStateDir();
|
|
442
476
|
const mapPath = path.join(stateDir, "cognitive.map.json");
|
|
443
|
-
const mapData = readMapJson(mapPath);
|
|
477
|
+
const mapData = (await lazyAnchor()).readMapJson(mapPath);
|
|
444
478
|
if (!mapData || mapData.anchors.length === 0)
|
|
445
479
|
return undefined;
|
|
446
480
|
const existingFiles = new Set();
|
|
@@ -454,15 +488,15 @@ export function registerTools(server, deps) {
|
|
|
454
488
|
}
|
|
455
489
|
}
|
|
456
490
|
catch { /* ignore */ }
|
|
457
|
-
const checked = checkAnchorStaleness(mapData.anchors, existingFiles);
|
|
491
|
+
const checked = (await lazyAnchor()).checkAnchorStaleness(mapData.anchors, existingFiles);
|
|
458
492
|
// 必须有 module 或 file_paths,否则不返回任何锚点(禁止全量加载)
|
|
459
493
|
if (!query.module && (!query.file_paths || query.file_paths.length === 0))
|
|
460
494
|
return undefined;
|
|
461
|
-
//
|
|
495
|
+
// 收集与任意提供的 file_paths 或 module 匹配的锚点
|
|
462
496
|
const seen = new Set();
|
|
463
497
|
const relevant = [];
|
|
464
498
|
if (query.module) {
|
|
465
|
-
for (const a of filterRelevantAnchors(checked, { module: query.module })) {
|
|
499
|
+
for (const a of (await lazyAnchor()).filterRelevantAnchors(checked, { module: query.module })) {
|
|
466
500
|
if (!seen.has(a.anchor_id)) {
|
|
467
501
|
seen.add(a.anchor_id);
|
|
468
502
|
relevant.push(a);
|
|
@@ -471,7 +505,7 @@ export function registerTools(server, deps) {
|
|
|
471
505
|
}
|
|
472
506
|
if (query.file_paths) {
|
|
473
507
|
for (const fp of query.file_paths) {
|
|
474
|
-
for (const a of filterRelevantAnchors(checked, { file_path: fp })) {
|
|
508
|
+
for (const a of (await lazyAnchor()).filterRelevantAnchors(checked, { file_path: fp })) {
|
|
475
509
|
if (!seen.has(a.anchor_id)) {
|
|
476
510
|
seen.add(a.anchor_id);
|
|
477
511
|
relevant.push(a);
|
|
@@ -508,10 +542,10 @@ export function registerTools(server, deps) {
|
|
|
508
542
|
// 创建任务上下文
|
|
509
543
|
const ctx = await taskContext.create(args.intent, config.product_profile);
|
|
510
544
|
const input = { intent: args.intent, project_path: projectPath, task_id: ctx.task_id };
|
|
511
|
-
const classification = classify(input);
|
|
545
|
+
const classification = (await lazyClassifier()).classify(input);
|
|
512
546
|
// 存储分类结果
|
|
513
547
|
await taskContext.setClassification(ctx.task_id, classification);
|
|
514
|
-
if (!shouldEnterSoloForge(classification.route_decision)) {
|
|
548
|
+
if (!(await lazyIntentRouter()).shouldEnterSoloForge(classification.route_decision)) {
|
|
515
549
|
await taskContext.updateStatus(ctx.task_id, "done");
|
|
516
550
|
}
|
|
517
551
|
return {
|
|
@@ -577,7 +611,7 @@ export function registerTools(server, deps) {
|
|
|
577
611
|
}
|
|
578
612
|
let expansion;
|
|
579
613
|
try {
|
|
580
|
-
expansion = await expand({
|
|
614
|
+
expansion = await (await lazyExpander()).expand({
|
|
581
615
|
intent: ctx.intent,
|
|
582
616
|
classification: ctx.classification,
|
|
583
617
|
projectPath,
|
|
@@ -610,14 +644,22 @@ export function registerTools(server, deps) {
|
|
|
610
644
|
// Privacy Gate: 隐私/敏感信息策略阻断
|
|
611
645
|
const isBlocked = expansion.prompt.startsWith("## 阻塞:输入材料禁止读取")
|
|
612
646
|
|| expansion.prompt.startsWith("## 阻塞:隐私/敏感信息策略");
|
|
613
|
-
|
|
647
|
+
let isMaterialClarification = false;
|
|
648
|
+
if (expansion.prompt.startsWith("## 澄清请求") && expansion.input_materials) {
|
|
649
|
+
for (const m of expansion.input_materials) {
|
|
650
|
+
if (m.access_mode !== "forbidden" && (await lazyInputMaterial()).classifyIngestionStatus(m.path_or_ref) === "requires_confirmation") {
|
|
651
|
+
isMaterialClarification = true;
|
|
652
|
+
break;
|
|
653
|
+
}
|
|
654
|
+
}
|
|
655
|
+
}
|
|
614
656
|
// 注入 H1/H4 advisory warnings
|
|
615
657
|
const advisories = {};
|
|
616
658
|
if (h1Warning)
|
|
617
659
|
advisories.h1_advisory = h1Warning;
|
|
618
660
|
if (h4LockWarning)
|
|
619
661
|
advisories.h4_advisory = h4LockWarning;
|
|
620
|
-
//
|
|
662
|
+
// 配置优先级警告
|
|
621
663
|
if (expansion.config_resolution_reports && expansion.config_resolution_reports.length > 0) {
|
|
622
664
|
const configWarnings = expansion.config_resolution_reports
|
|
623
665
|
.filter(r => r.conflicts.length > 0)
|
|
@@ -627,7 +669,7 @@ export function registerTools(server, deps) {
|
|
|
627
669
|
}
|
|
628
670
|
}
|
|
629
671
|
// 决策契约 advisory: 校验 expand 输出是否包含默认决策字段
|
|
630
|
-
const decisionContract = validateDecisionOutput(expansion);
|
|
672
|
+
const decisionContract = (await lazyDecision()).validateDecisionOutput(expansion);
|
|
631
673
|
if (!decisionContract.passed) {
|
|
632
674
|
advisories.decision_contract_advisory = decisionContract.advisory;
|
|
633
675
|
}
|
|
@@ -684,7 +726,7 @@ export function registerTools(server, deps) {
|
|
|
684
726
|
result: { error: "任务不存在" },
|
|
685
727
|
};
|
|
686
728
|
}
|
|
687
|
-
// RouteDecision
|
|
729
|
+
// RouteDecision 合同验证 — 无条件,在状态预检之前
|
|
688
730
|
const rdFromCtx = ctx.route_decision ?? ctx.classification?.route_decision ?? null;
|
|
689
731
|
const wtFromCtx = ctx.workflow_trace ?? ctx.expansion?.workflow_trace ?? null;
|
|
690
732
|
const rdContractInput = {
|
|
@@ -719,13 +761,30 @@ export function registerTools(server, deps) {
|
|
|
719
761
|
const h4Warning = !integrity.clean
|
|
720
762
|
? { warning: `H4 advisory: ${integrity.message}`, dirty_files: integrity.dirty_files }
|
|
721
763
|
: undefined;
|
|
722
|
-
const verifyResult = generateVerifyCommands(config, args.changed_files, acceptanceItems, ctx?.expansion?.workflow_trace?.route);
|
|
764
|
+
const verifyResult = (await lazyVerifier()).generateVerifyCommands(config, args.changed_files, acceptanceItems, ctx?.expansion?.workflow_trace?.route);
|
|
723
765
|
verifyResult.task_id = args.task_id;
|
|
724
|
-
//
|
|
766
|
+
// Batch2: 创建 VerificationPlan(plan_only,非执行结果)
|
|
767
|
+
const vcModule = await lazyVerificationContract();
|
|
768
|
+
const allCommands = [
|
|
769
|
+
...(verifyResult.checks?.build ?? []),
|
|
770
|
+
...(verifyResult.checks?.tests ?? []),
|
|
771
|
+
].map((c) => c.command);
|
|
772
|
+
const batch2Plan = vcModule.createVerificationPlan(args.task_id, allCommands, acceptanceItems.map((a) => a.description));
|
|
773
|
+
// Batch2: sf_verify 仅生成计划,不执行命令 — 始终保存 not_executed
|
|
774
|
+
// 真实执行结果必须通过 recordVerificationExecution 单独录入
|
|
775
|
+
const batch2Result = vcModule.createNotExecutedResult(args.task_id, batch2Plan.plan_id, "sf_verify 生成命令,等待执行");
|
|
776
|
+
// 存储 Batch2 验证契约到任务上下文
|
|
777
|
+
const ctxForBatch2 = await taskContext.load(args.task_id);
|
|
778
|
+
if (ctxForBatch2) {
|
|
779
|
+
ctxForBatch2.batch2_verification_plan = batch2Plan;
|
|
780
|
+
ctxForBatch2.batch2_verification_result = batch2Result;
|
|
781
|
+
await taskContext.save(ctxForBatch2);
|
|
782
|
+
}
|
|
783
|
+
// 产物生命周期检查: draft → verified 需要 ArtifactVerificationResult.passed
|
|
725
784
|
let artifactVerificationResult;
|
|
726
785
|
let artifactFileMissing = false;
|
|
727
786
|
if (ctx.artifact_output && ctx.artifact_output.status === "draft") {
|
|
728
|
-
//
|
|
787
|
+
// 验证前解析产物文件并计算 hash
|
|
729
788
|
const artifact = ctx.artifact_output;
|
|
730
789
|
const fullPath = path.join(projectPath, artifact.path);
|
|
731
790
|
if (fss.existsSync(fullPath)) {
|
|
@@ -735,7 +794,7 @@ export function registerTools(server, deps) {
|
|
|
735
794
|
if (!artifact.evidence_refs.some(r => r.startsWith("file:artifact:"))) {
|
|
736
795
|
artifact.evidence_refs.push(`file:artifact:path=${artifact.path} hash=sha256:${hash}`);
|
|
737
796
|
}
|
|
738
|
-
//
|
|
797
|
+
// 保存更新后的产物(含 hash)
|
|
739
798
|
const ctxForHash = await taskContext.load(args.task_id);
|
|
740
799
|
if (ctxForHash?.artifact_output) {
|
|
741
800
|
ctxForHash.artifact_output.hash = artifact.hash;
|
|
@@ -751,8 +810,25 @@ export function registerTools(server, deps) {
|
|
|
751
810
|
artifactVerificationResult = verifyArtifact(artifact.kind, artifact);
|
|
752
811
|
}
|
|
753
812
|
// 记录变更文件
|
|
813
|
+
// Batch2: 写入护栏 — 检查变更文件是否在允许范围
|
|
814
|
+
if (args.changed_files.length > 0) {
|
|
815
|
+
const guardModule = await lazyEnforcementGuard();
|
|
816
|
+
const scopePaths = ctx.expansion?.scope?.allowed_paths ?? [];
|
|
817
|
+
for (const cf of args.changed_files) {
|
|
818
|
+
const writeFindings = guardModule.evaluateWriteGuard(ctx.classification?.route_decision?.route ?? "code_execution", cf, scopePaths, "command_execution_contract");
|
|
819
|
+
if (writeFindings.some((f) => f.blocked)) {
|
|
820
|
+
return {
|
|
821
|
+
result: {
|
|
822
|
+
error: `Batch2 写入护栏阻断: ${writeFindings[0].reason_zh}`,
|
|
823
|
+
blocked_file: cf,
|
|
824
|
+
guard_id: writeFindings[0].guard_id,
|
|
825
|
+
},
|
|
826
|
+
};
|
|
827
|
+
}
|
|
828
|
+
}
|
|
829
|
+
}
|
|
754
830
|
await taskContext.setExecution(args.task_id, args.changed_files);
|
|
755
|
-
//
|
|
831
|
+
// 无实际文件变更但存在产物时标记 no_change_artifact
|
|
756
832
|
if (args.changed_files.length === 0 && ctx.artifact_output) {
|
|
757
833
|
const execCtx = await taskContext.load(args.task_id);
|
|
758
834
|
if (execCtx?.execution) {
|
|
@@ -767,7 +843,7 @@ export function registerTools(server, deps) {
|
|
|
767
843
|
await taskContext.updateStatus(args.task_id, "verifying");
|
|
768
844
|
// 存储验证结果
|
|
769
845
|
await taskContext.setVerification(args.task_id, verifyResult);
|
|
770
|
-
//
|
|
846
|
+
// 产物生命周期转换: draft → verified 仅在 ArtifactVerificationResult.passed 时
|
|
771
847
|
if (ctx.artifact_output && ctx.artifact_output.status === "draft" && artifactVerificationResult?.passed) {
|
|
772
848
|
const evidenceRef = `verification:${ctx.task_id}:checks=${artifactVerificationResult.checks.length}:summary=${artifactVerificationResult.summary.slice(0, 60)}`;
|
|
773
849
|
try {
|
|
@@ -778,11 +854,11 @@ export function registerTools(server, deps) {
|
|
|
778
854
|
});
|
|
779
855
|
}
|
|
780
856
|
catch {
|
|
781
|
-
//
|
|
857
|
+
// 转换被阻止 — 产物保持 draft,流水线继续
|
|
782
858
|
}
|
|
783
859
|
}
|
|
784
860
|
// 认知锚点上下文回放(按变更文件相关性)
|
|
785
|
-
const verifyAnchors = loadRelevantAnchors({ file_paths: args.changed_files });
|
|
861
|
+
const verifyAnchors = await loadRelevantAnchors({ file_paths: args.changed_files });
|
|
786
862
|
const verifyExtras = {};
|
|
787
863
|
if (h4Warning)
|
|
788
864
|
verifyExtras.h4_advisory = h4Warning;
|
|
@@ -795,13 +871,46 @@ export function registerTools(server, deps) {
|
|
|
795
871
|
verifyExtras.artifact_warning = `产物验收检查未通过: ${reason}`;
|
|
796
872
|
verifyExtras.artifact_verification = artifactVerificationResult;
|
|
797
873
|
}
|
|
874
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
798
875
|
const verifyPayload = Object.keys(verifyExtras).length > 0
|
|
799
876
|
? { ...verifyResult, ...verifyExtras }
|
|
800
|
-
: verifyResult;
|
|
877
|
+
: { ...verifyResult };
|
|
878
|
+
// Batch2: 附加验证契约引用到返回结果
|
|
879
|
+
verifyPayload.batch2_verification_plan = batch2Plan;
|
|
880
|
+
verifyPayload.batch2_verification_result = batch2Result;
|
|
801
881
|
return {
|
|
802
882
|
result: verifyPayload,
|
|
803
883
|
};
|
|
804
884
|
});
|
|
885
|
+
// ── sf_record_verification_execution: 录入真实验证执行结果(唯一 passed 入口) ──
|
|
886
|
+
registerSafeTool("sf_record_verification_execution", "录入真实验证执行结果,VerificationResult.status=passed 的唯一合法入口", RecordVerificationExecutionSchema, async (args) => {
|
|
887
|
+
const ctx = await taskContext.load(args.task_id);
|
|
888
|
+
if (!ctx) {
|
|
889
|
+
return { result: { error: "任务不存在" } };
|
|
890
|
+
}
|
|
891
|
+
// 必须有 VerificationPlan
|
|
892
|
+
if (!ctx.batch2_verification_plan) {
|
|
893
|
+
return { result: { error: "任务无 VerificationPlan,先执行 sf_verify 生成计划" } };
|
|
894
|
+
}
|
|
895
|
+
// plan_id 必须与 TaskContext 中当前 plan 一致
|
|
896
|
+
if (args.plan_id !== ctx.batch2_verification_plan.plan_id) {
|
|
897
|
+
return {
|
|
898
|
+
result: {
|
|
899
|
+
error: `plan_id 不匹配: 请求 "${args.plan_id}",当前任务计划 "${ctx.batch2_verification_plan.plan_id}" [SF-VERIFY-5002]`,
|
|
900
|
+
diagnostic_code: "SF-VERIFY-5002",
|
|
901
|
+
},
|
|
902
|
+
};
|
|
903
|
+
}
|
|
904
|
+
const vcModule = await lazyVerificationContract();
|
|
905
|
+
const result = vcModule.recordVerificationExecution(args.task_id, args.plan_id, args.execution_records, ctx.batch2_verification_plan);
|
|
906
|
+
// 持久化到 TaskContext
|
|
907
|
+
const ctxForSave = await taskContext.load(args.task_id);
|
|
908
|
+
if (ctxForSave) {
|
|
909
|
+
ctxForSave.batch2_verification_result = result;
|
|
910
|
+
await taskContext.save(ctxForSave);
|
|
911
|
+
}
|
|
912
|
+
return { result };
|
|
913
|
+
});
|
|
805
914
|
// ── sf_learn: 从执行结果中提取知识,支持重试循环控制 ──
|
|
806
915
|
registerSafeTool("sf_learn", "从任务执行结果中提取知识,低置信度暂存,高置信度直接入库", LearnSchema, async (args) => {
|
|
807
916
|
const ctx = await taskContext.load(args.task_id);
|
|
@@ -826,10 +935,37 @@ export function registerTools(server, deps) {
|
|
|
826
935
|
: "unknown failure",
|
|
827
936
|
timestamp: new Date().toISOString(),
|
|
828
937
|
});
|
|
938
|
+
// Batch2: 创建 FailureReport(脱敏,非 raw error_output)
|
|
939
|
+
const frModule = await lazyFailureReport();
|
|
940
|
+
const failureClass = args.failure_type;
|
|
941
|
+
const errorOutput = args.verify_output
|
|
942
|
+
? `build=${args.verify_output.build_passed} tests=${args.verify_output.tests_passed}`
|
|
943
|
+
: "unknown failure";
|
|
944
|
+
const retryCount = (ctx.execution?.attempt_count ?? 0) + 1;
|
|
945
|
+
const policy = frModule.getRecoveryPolicy(failureClass);
|
|
946
|
+
const batch2FailureReport = frModule.createFailureReport(args.task_id, failureClass, errorOutput, retryCount, policy?.max_retries ?? 0);
|
|
947
|
+
// 存储 FailureReport 到任务上下文
|
|
948
|
+
const ctxForFailure = await taskContext.load(args.task_id);
|
|
949
|
+
if (ctxForFailure) {
|
|
950
|
+
ctxForFailure.batch2_failure_report = batch2FailureReport;
|
|
951
|
+
await taskContext.save(ctxForFailure);
|
|
952
|
+
}
|
|
953
|
+
// Batch2: verifier 不可用时创建 DegradationEvent
|
|
954
|
+
if (!args.verify_output) {
|
|
955
|
+
const degModule = await lazyDegradation();
|
|
956
|
+
const degradationEvent = degModule.createDegradationEvent(args.task_id, "verifier", "full", "验证器输出不可用,无法判断 build/test 结果", ["verification_execution"]);
|
|
957
|
+
if (degradationEvent) {
|
|
958
|
+
const ctxForDeg = await taskContext.load(args.task_id);
|
|
959
|
+
if (ctxForDeg) {
|
|
960
|
+
ctxForDeg.batch2_degradation_event = degradationEvent;
|
|
961
|
+
await taskContext.save(ctxForDeg);
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
}
|
|
829
965
|
}
|
|
830
966
|
// 转换到学习阶段
|
|
831
967
|
await taskContext.updateStatus(args.task_id, "learning");
|
|
832
|
-
const learnResult = await evolve({
|
|
968
|
+
const learnResult = await (await lazyEvolver()).evolve({
|
|
833
969
|
taskContext: ctx,
|
|
834
970
|
result: args.result,
|
|
835
971
|
verifyOutput: args.verify_output,
|
|
@@ -872,14 +1008,14 @@ export function registerTools(server, deps) {
|
|
|
872
1008
|
// ── sf_status: 任务状态查询(current/recent/resume/cancel) ──
|
|
873
1009
|
registerSafeTool("sf_status", "查询当前任务状态、列出近期任务、恢复中断任务或取消任务", StatusSchema, async (args) => {
|
|
874
1010
|
const action = args.action || "current";
|
|
875
|
-
// Job
|
|
1011
|
+
// Job 操作 — advisory,独立于任务操作
|
|
876
1012
|
if (args.job_action) {
|
|
877
1013
|
if (!args.job_id) {
|
|
878
1014
|
return {
|
|
879
1015
|
result: { error: "job 操作需要 job_id" },
|
|
880
1016
|
};
|
|
881
1017
|
}
|
|
882
|
-
const jm = new JobManager(taskContext.getStateDir());
|
|
1018
|
+
const jm = new (await lazyJob()).JobManager(taskContext.getStateDir());
|
|
883
1019
|
const taskId = args.task_id ?? await jm.resolveTaskId(args.job_id) ?? "";
|
|
884
1020
|
if (args.job_action === "job_status") {
|
|
885
1021
|
const status = await jm.getJobStatus(args.job_id, taskId);
|
|
@@ -921,12 +1057,12 @@ export function registerTools(server, deps) {
|
|
|
921
1057
|
};
|
|
922
1058
|
}
|
|
923
1059
|
}
|
|
924
|
-
//
|
|
1060
|
+
// 读取前自动将过期非终态任务标记为失败
|
|
925
1061
|
try {
|
|
926
1062
|
await taskContext.autoFailStale();
|
|
927
1063
|
}
|
|
928
1064
|
catch (e) {
|
|
929
|
-
console.error("[soloForge] autoFailStale
|
|
1065
|
+
console.error("[soloForge] autoFailStale 出错:", e);
|
|
930
1066
|
}
|
|
931
1067
|
switch (action) {
|
|
932
1068
|
case "current": {
|
|
@@ -960,7 +1096,7 @@ export function registerTools(server, deps) {
|
|
|
960
1096
|
}
|
|
961
1097
|
// JobManager advisory: 查询关联 job 状态
|
|
962
1098
|
try {
|
|
963
|
-
const jm = new JobManager(taskContext.getStateDir());
|
|
1099
|
+
const jm = new (await lazyJob()).JobManager(taskContext.getStateDir());
|
|
964
1100
|
const activeJobs = await jm.listActiveJobs();
|
|
965
1101
|
const relatedJob = activeJobs.find((j) => j.task_id === ctx.task_id);
|
|
966
1102
|
if (relatedJob) {
|
|
@@ -974,7 +1110,7 @@ export function registerTools(server, deps) {
|
|
|
974
1110
|
}
|
|
975
1111
|
}
|
|
976
1112
|
catch {
|
|
977
|
-
// JobManager advisory —
|
|
1113
|
+
// JobManager advisory — 忽略错误
|
|
978
1114
|
}
|
|
979
1115
|
return {
|
|
980
1116
|
result: result,
|
|
@@ -1012,7 +1148,7 @@ export function registerTools(server, deps) {
|
|
|
1012
1148
|
if (ctx.execution)
|
|
1013
1149
|
resumeResult.execution = ctx.execution;
|
|
1014
1150
|
const resumeAnchors = ctx.execution?.changed_files
|
|
1015
|
-
? loadRelevantAnchors({ file_paths: ctx.execution.changed_files })
|
|
1151
|
+
? await loadRelevantAnchors({ file_paths: ctx.execution.changed_files })
|
|
1016
1152
|
: undefined;
|
|
1017
1153
|
if (resumeAnchors && resumeAnchors.length > 0)
|
|
1018
1154
|
resumeResult.cognitive_anchors = resumeAnchors;
|
|
@@ -1043,7 +1179,7 @@ export function registerTools(server, deps) {
|
|
|
1043
1179
|
result: { error: "任务不存在或尚未分类" },
|
|
1044
1180
|
};
|
|
1045
1181
|
}
|
|
1046
|
-
const result = planTask({
|
|
1182
|
+
const result = (await lazyPlanner()).planTask({
|
|
1047
1183
|
intent: ctx.intent,
|
|
1048
1184
|
classification: ctx.classification,
|
|
1049
1185
|
config,
|
|
@@ -1105,7 +1241,7 @@ export function registerTools(server, deps) {
|
|
|
1105
1241
|
result: { error: "任务尚未膨胀,请先调用 sf_expand" },
|
|
1106
1242
|
};
|
|
1107
1243
|
}
|
|
1108
|
-
const result = await analyzeImpact({
|
|
1244
|
+
const result = await (await lazyImpact()).analyzeImpact({
|
|
1109
1245
|
intent: ctx.intent,
|
|
1110
1246
|
classification: ctx.classification,
|
|
1111
1247
|
expansion: ctx.expansion,
|
|
@@ -1114,7 +1250,7 @@ export function registerTools(server, deps) {
|
|
|
1114
1250
|
});
|
|
1115
1251
|
result.task_id = args.task_id;
|
|
1116
1252
|
// 决策契约 advisory: 校验 analyze 输出
|
|
1117
|
-
const decisionContract = validateDecisionOutput(result);
|
|
1253
|
+
const decisionContract = (await lazyDecision()).validateDecisionOutput(result);
|
|
1118
1254
|
const advisoryExtras = {};
|
|
1119
1255
|
if (!decisionContract.passed) {
|
|
1120
1256
|
advisoryExtras.decision_contract_advisory = decisionContract.advisory;
|
|
@@ -1129,8 +1265,8 @@ export function registerTools(server, deps) {
|
|
|
1129
1265
|
result: { error: "任务不存在" },
|
|
1130
1266
|
};
|
|
1131
1267
|
}
|
|
1132
|
-
const tracker = new DebtTracker(projectPath);
|
|
1133
|
-
const result = await reviewCode({
|
|
1268
|
+
const tracker = new (await lazyDebt()).DebtTracker(projectPath);
|
|
1269
|
+
const result = await (await lazyReviewer()).reviewCode({
|
|
1134
1270
|
changedFiles: args.changed_files,
|
|
1135
1271
|
projectPath,
|
|
1136
1272
|
config,
|
|
@@ -1142,7 +1278,7 @@ export function registerTools(server, deps) {
|
|
|
1142
1278
|
result.task_id = args.task_id;
|
|
1143
1279
|
await taskContext.setCodeReview(args.task_id, result);
|
|
1144
1280
|
// 决策契约 advisory: 校验 review 输出
|
|
1145
|
-
const decisionContract = validateDecisionOutput(result);
|
|
1281
|
+
const decisionContract = (await lazyDecision()).validateDecisionOutput(result);
|
|
1146
1282
|
const reviewExtras = {};
|
|
1147
1283
|
if (!decisionContract.passed) {
|
|
1148
1284
|
reviewExtras.decision_contract_advisory = decisionContract.advisory;
|
|
@@ -1165,7 +1301,7 @@ export function registerTools(server, deps) {
|
|
|
1165
1301
|
},
|
|
1166
1302
|
};
|
|
1167
1303
|
}
|
|
1168
|
-
const result = await generateScaffold({
|
|
1304
|
+
const result = await (await lazyScaffolder()).generateScaffold({
|
|
1169
1305
|
intent: ctx.intent,
|
|
1170
1306
|
classification: ctx.classification,
|
|
1171
1307
|
config,
|
|
@@ -1189,7 +1325,7 @@ export function registerTools(server, deps) {
|
|
|
1189
1325
|
const h4DeliverWarning = !deliverIntegrity.clean
|
|
1190
1326
|
? { warning: `H4 advisory: ${deliverIntegrity.message}`, dirty_files: deliverIntegrity.dirty_files }
|
|
1191
1327
|
: undefined;
|
|
1192
|
-
//
|
|
1328
|
+
// 产物状态门控: deliver 需要产物状态为 accepted 且有证据
|
|
1193
1329
|
if (ctx.artifact_output) {
|
|
1194
1330
|
const status = ctx.artifact_output.status;
|
|
1195
1331
|
if (status === "draft") {
|
|
@@ -1210,7 +1346,7 @@ export function registerTools(server, deps) {
|
|
|
1210
1346
|
},
|
|
1211
1347
|
};
|
|
1212
1348
|
}
|
|
1213
|
-
// accepted
|
|
1349
|
+
// accepted 必须有确认证据
|
|
1214
1350
|
if (status === "accepted") {
|
|
1215
1351
|
const hasAcceptanceEvidence = ctx.artifact_output.evidence_refs.some((r) => r.includes("acceptance") || r.includes("workflow_adoption"));
|
|
1216
1352
|
if (!hasAcceptanceEvidence) {
|
|
@@ -1224,27 +1360,68 @@ export function registerTools(server, deps) {
|
|
|
1224
1360
|
}
|
|
1225
1361
|
}
|
|
1226
1362
|
}
|
|
1227
|
-
|
|
1363
|
+
// Batch2: 交付 lease 互斥检查
|
|
1364
|
+
const leaseModule = await lazyWorkspaceLease();
|
|
1365
|
+
const deliverLease = leaseModule.acquireLease(args.task_id, "delivery", [projectPath]);
|
|
1366
|
+
if (deliverLease.lease?.status === "conflicted" || deliverLease.conflicts.length > 0) {
|
|
1367
|
+
return {
|
|
1368
|
+
result: {
|
|
1369
|
+
error: `交付 lease 冲突: ${deliverLease.conflicts.map((c) => `${c.conflict_type}: ${c.conflict_path}`).join(", ")}`,
|
|
1370
|
+
conflicts: deliverLease.conflicts,
|
|
1371
|
+
},
|
|
1372
|
+
};
|
|
1373
|
+
}
|
|
1374
|
+
// Batch2: 交付就绪评估 — delivery_allowed=false 必须阻断交付
|
|
1375
|
+
const drModule = await lazyDeliveryReadiness();
|
|
1376
|
+
const guardModule = await lazyEnforcementGuard();
|
|
1377
|
+
const guardFindings = guardModule.evaluateDeliveryReadinessGuard(ctx.batch2_verification_result, ctx.batch2_verification_plan);
|
|
1378
|
+
const readinessReport = drModule.evaluateDeliveryReadiness(args.task_id, ctx.batch2_verification_result, guardFindings, true, // scope_check — 已在 sf_verify 写入护栏中检查
|
|
1379
|
+
deliverLease.conflicts.length === 0, // lease_check — 无冲突即通过
|
|
1380
|
+
true, // privacy_check — 已在任务创建时脱敏
|
|
1381
|
+
true);
|
|
1382
|
+
// 保存就绪报告到任务上下文
|
|
1383
|
+
const ctxForReadiness = await taskContext.load(args.task_id);
|
|
1384
|
+
if (ctxForReadiness) {
|
|
1385
|
+
ctxForReadiness.batch2_delivery_readiness = readinessReport;
|
|
1386
|
+
await taskContext.save(ctxForReadiness);
|
|
1387
|
+
}
|
|
1388
|
+
if (!readinessReport.delivery_allowed) {
|
|
1389
|
+
// 释放 lease 并阻断交付
|
|
1390
|
+
if (deliverLease.lease) {
|
|
1391
|
+
leaseModule.releaseLease(deliverLease.lease.lease_id);
|
|
1392
|
+
}
|
|
1393
|
+
return {
|
|
1394
|
+
result: {
|
|
1395
|
+
error: `Batch2 交付就绪检查未通过: ${readinessReport.blocking_findings.map((f) => f.message_zh).join("; ")}`,
|
|
1396
|
+
delivery_readiness: readinessReport,
|
|
1397
|
+
},
|
|
1398
|
+
};
|
|
1399
|
+
}
|
|
1400
|
+
const result = await (await lazyDelivery()).deliver({
|
|
1228
1401
|
taskContext: ctx,
|
|
1229
1402
|
config,
|
|
1230
1403
|
knowledgeIndex,
|
|
1231
1404
|
projectPath,
|
|
1232
|
-
gitOps: realGitOps,
|
|
1405
|
+
gitOps: (await lazyGitDeps()).realGitOps,
|
|
1233
1406
|
skipPush: args.skip_push,
|
|
1234
1407
|
skipPr: args.skip_pr,
|
|
1235
1408
|
});
|
|
1236
1409
|
await taskContext.setDelivery(args.task_id, result);
|
|
1410
|
+
// Batch2: 交付完成后释放 lease
|
|
1411
|
+
if (deliverLease.lease) {
|
|
1412
|
+
leaseModule.releaseLease(deliverLease.lease.lease_id);
|
|
1413
|
+
}
|
|
1237
1414
|
return {
|
|
1238
1415
|
result: h4DeliverWarning ? { ...result, h4_advisory: h4DeliverWarning } : result,
|
|
1239
1416
|
};
|
|
1240
1417
|
});
|
|
1241
1418
|
// ── sf_coord_check: 预测性冲突检测和跨仓库协调 ──
|
|
1242
1419
|
registerSafeTool("sf_coord_check", "预测性冲突检测和跨仓库协调提示,检查分支状态、本地变更和潜在冲突", CoordCheckSchema, async (args) => {
|
|
1243
|
-
const result = await checkConflicts({
|
|
1420
|
+
const result = await (await lazyChangeCoord()).checkConflicts({
|
|
1244
1421
|
projectPath,
|
|
1245
1422
|
config,
|
|
1246
1423
|
branch: args.branch,
|
|
1247
|
-
gitOps: realGitOps,
|
|
1424
|
+
gitOps: (await lazyGitDeps()).realGitOps,
|
|
1248
1425
|
});
|
|
1249
1426
|
return {
|
|
1250
1427
|
result: result,
|
|
@@ -1252,11 +1429,11 @@ export function registerTools(server, deps) {
|
|
|
1252
1429
|
});
|
|
1253
1430
|
// ── sf_team_status: 团队活动流和工作负载查询 ──
|
|
1254
1431
|
registerSafeTool("sf_team_status", "查询团队活动流、成员工作负载、过期分支和知识库更新状态", TeamStatusSchema, async (args) => {
|
|
1255
|
-
const result = await getTeamStatus({
|
|
1432
|
+
const result = await (await lazyTeam()).getTeamStatus({
|
|
1256
1433
|
projectPath,
|
|
1257
1434
|
config,
|
|
1258
1435
|
since: args.since,
|
|
1259
|
-
gitOps: realGitOps,
|
|
1436
|
+
gitOps: (await lazyGitDeps()).realGitOps,
|
|
1260
1437
|
knowledgeIndex: args.include_knowledge ? knowledgeIndex : undefined,
|
|
1261
1438
|
});
|
|
1262
1439
|
return {
|
|
@@ -1270,11 +1447,11 @@ export function registerTools(server, deps) {
|
|
|
1270
1447
|
const ctx = await taskContext.load(args.task_id);
|
|
1271
1448
|
changedFiles = ctx?.execution?.changed_files ?? [];
|
|
1272
1449
|
}
|
|
1273
|
-
const result = await checkContractChanges({
|
|
1450
|
+
const result = await (await lazyContractGuard()).checkContractChanges({
|
|
1274
1451
|
changedFiles,
|
|
1275
1452
|
projectPath,
|
|
1276
1453
|
config,
|
|
1277
|
-
gitOps: realGitOps,
|
|
1454
|
+
gitOps: (await lazyGitDeps()).realGitOps,
|
|
1278
1455
|
});
|
|
1279
1456
|
return {
|
|
1280
1457
|
result: result,
|
|
@@ -1282,11 +1459,11 @@ export function registerTools(server, deps) {
|
|
|
1282
1459
|
});
|
|
1283
1460
|
// ── sf_onboard: 新人分步引导 ──
|
|
1284
1461
|
registerSafeTool("sf_onboard", "新人分步引导:项目概览 → 代码导览 → 知识回顾 → 首个任务建议", OnboardSchema, async (args) => {
|
|
1285
|
-
const result = await onboard({
|
|
1462
|
+
const result = await (await lazyOnboarding()).onboard({
|
|
1286
1463
|
config,
|
|
1287
1464
|
projectPath,
|
|
1288
1465
|
knowledgeIndex,
|
|
1289
|
-
gitOps: realGitOps,
|
|
1466
|
+
gitOps: (await lazyGitDeps()).realGitOps,
|
|
1290
1467
|
currentStep: args.step,
|
|
1291
1468
|
reset: args.reset,
|
|
1292
1469
|
});
|
|
@@ -1302,7 +1479,7 @@ export function registerTools(server, deps) {
|
|
|
1302
1479
|
result: { error: "任务不存在或未完成分类,请先调用 sf_classify" },
|
|
1303
1480
|
};
|
|
1304
1481
|
}
|
|
1305
|
-
const result = checkFeasibility(ctx.classification, config);
|
|
1482
|
+
const result = (await lazyFeasibility()).checkFeasibility(ctx.classification, config);
|
|
1306
1483
|
return {
|
|
1307
1484
|
result: result,
|
|
1308
1485
|
};
|
|
@@ -1313,7 +1490,7 @@ export function registerTools(server, deps) {
|
|
|
1313
1490
|
...config.scope.backend,
|
|
1314
1491
|
...config.scope.frontend,
|
|
1315
1492
|
];
|
|
1316
|
-
const result = debugError(args.error_output, args.task_id || `debug-${Date.now()}`, projectScope);
|
|
1493
|
+
const result = (await lazyDebugger()).debugError(args.error_output, args.task_id || `debug-${Date.now()}`, projectScope);
|
|
1317
1494
|
return {
|
|
1318
1495
|
result: result,
|
|
1319
1496
|
};
|
|
@@ -1321,8 +1498,8 @@ export function registerTools(server, deps) {
|
|
|
1321
1498
|
// ── sf_observability: 系统可观测和运行报告 ──
|
|
1322
1499
|
registerSafeTool("sf_observability", "系统可观测:运行指标、成本估算、告警检测、能力状态、周期报告", ObservabilitySchema, async (args) => {
|
|
1323
1500
|
const stateDir = taskContext.getStateDir();
|
|
1324
|
-
const result = await generateReport(stateDir, args.period_days);
|
|
1325
|
-
const capability =
|
|
1501
|
+
const result = await (await lazyObservability()).generateReport(stateDir, args.period_days);
|
|
1502
|
+
const capability = (await lazyCapability()).getSummary();
|
|
1326
1503
|
const output = { ...result, capability };
|
|
1327
1504
|
return {
|
|
1328
1505
|
result: output,
|
|
@@ -1330,36 +1507,36 @@ export function registerTools(server, deps) {
|
|
|
1330
1507
|
});
|
|
1331
1508
|
// ── sf_migration_check: 数据库迁移安全性分析 ──
|
|
1332
1509
|
registerSafeTool("sf_migration_check", "分析数据库迁移文件安全性:检测破坏性操作(DROP/DELETE/TRUNCATE)并生成回滚建议", MigrationCheckSchema, async (args) => {
|
|
1333
|
-
const result = analyzeMigration(args.content, args.filename);
|
|
1510
|
+
const result = (await lazyMigration()).analyzeMigration(args.content, args.filename);
|
|
1334
1511
|
return {
|
|
1335
1512
|
result: result,
|
|
1336
1513
|
};
|
|
1337
1514
|
});
|
|
1338
1515
|
// ── sf_test_guide: 基于变更文件生成测试指引 ──
|
|
1339
1516
|
registerSafeTool("sf_test_guide", "根据变更文件类型生成测试指引:推荐测试类型、场景、Mock 点和断言模板", TestGuideSchema, async (args) => {
|
|
1340
|
-
const result = generateTestGuide(args.changed_files, [], config);
|
|
1517
|
+
const result = (await lazyTestGen()).generateTestGuide(args.changed_files, [], config);
|
|
1341
1518
|
return {
|
|
1342
1519
|
result: result,
|
|
1343
1520
|
};
|
|
1344
1521
|
});
|
|
1345
1522
|
// ── sf_test_quality: 测试文件质量五维评分 ──
|
|
1346
1523
|
registerSafeTool("sf_test_quality", "评估测试文件质量:断言密度、边界覆盖、命名、重复率、场景覆盖五维评分", TestQualitySchema, async (args) => {
|
|
1347
|
-
const result = analyzeTestQuality(args.content, args.filename);
|
|
1524
|
+
const result = (await lazyTestQuality()).analyzeTestQuality(args.content, args.filename);
|
|
1348
1525
|
return {
|
|
1349
1526
|
result: result,
|
|
1350
1527
|
};
|
|
1351
1528
|
});
|
|
1352
1529
|
// ── sf_dependency_scan: 依赖漏洞扫描 ──
|
|
1353
1530
|
registerSafeTool("sf_dependency_scan", "扫描依赖声明文件漏洞:已知 CVE 规则匹配、未锁定版本检测、支持 npm/maven/gradle", DependencyScanSchema, async (args) => {
|
|
1354
|
-
const result = scanDependencies(args.content, args.filename);
|
|
1531
|
+
const result = (await lazyDepScan()).scanDependencies(args.content, args.filename);
|
|
1355
1532
|
return {
|
|
1356
1533
|
result: result,
|
|
1357
1534
|
};
|
|
1358
1535
|
});
|
|
1359
1536
|
// ── sf_debt_report: 技术债务报告生成 ──
|
|
1360
1537
|
registerSafeTool("sf_debt_report", "生成技术债务报告:按分类/严重度聚合、30 天趋势、优先级排序 Top 5", {}, async () => {
|
|
1361
|
-
const tracker = new DebtTracker(projectPath);
|
|
1362
|
-
const result = await generateDebtReport(tracker);
|
|
1538
|
+
const tracker = new (await lazyDebt()).DebtTracker(projectPath);
|
|
1539
|
+
const result = await (await lazyDebtReport()).generateDebtReport(tracker);
|
|
1363
1540
|
return {
|
|
1364
1541
|
result: result,
|
|
1365
1542
|
};
|
|
@@ -1371,14 +1548,14 @@ export function registerTools(server, deps) {
|
|
|
1371
1548
|
const ctx = await taskContext.load(args.task_id);
|
|
1372
1549
|
classification = ctx?.classification;
|
|
1373
1550
|
}
|
|
1374
|
-
const result = exploreSolutions({
|
|
1551
|
+
const result = (await lazyExploration()).exploreSolutions({
|
|
1375
1552
|
domain_query: args.domain_query,
|
|
1376
1553
|
projectConfig: config,
|
|
1377
1554
|
classification,
|
|
1378
1555
|
knowledgeIndex,
|
|
1379
1556
|
});
|
|
1380
1557
|
// 决策契约 advisory: sf_explore 应始终通过(10 字段已内置)
|
|
1381
|
-
const decisionContract = validateDecisionOutput(result);
|
|
1558
|
+
const decisionContract = (await lazyDecision()).validateDecisionOutput(result);
|
|
1382
1559
|
const exploreExtras = {};
|
|
1383
1560
|
if (!decisionContract.passed) {
|
|
1384
1561
|
exploreExtras.decision_contract_advisory = decisionContract.advisory;
|
|
@@ -1387,7 +1564,7 @@ export function registerTools(server, deps) {
|
|
|
1387
1564
|
});
|
|
1388
1565
|
// ── sf_knowledge_audit: 知识库健康审计 ──
|
|
1389
1566
|
registerSafeTool("sf_knowledge_audit", "审计知识库:识别过时条目、重复触发词、格式缺失、覆盖缺口。定期执行或手动触发", {}, async () => {
|
|
1390
|
-
const result = await auditKnowledge(knowledgeIndex, config);
|
|
1567
|
+
const result = await (await lazyKnowledge()).auditKnowledge(knowledgeIndex, config);
|
|
1391
1568
|
return {
|
|
1392
1569
|
result: result,
|
|
1393
1570
|
};
|
|
@@ -1403,7 +1580,7 @@ export function registerTools(server, deps) {
|
|
|
1403
1580
|
auto_enrich: z.boolean().optional().describe("是否返回行业最佳实践探索指引(默认 true,宿主 AI 将自动执行探索并填充内容)"),
|
|
1404
1581
|
};
|
|
1405
1582
|
registerSafeTool("sf_knowledge_add", "新增知识条目。默认保存为草稿到 .soloforge/knowledge/drafts/,人工确认后移入正式目录", KnowledgeAddSchema, async (args) => {
|
|
1406
|
-
const result = await addKnowledge({
|
|
1583
|
+
const result = await (await lazyKnowledge()).addKnowledge({
|
|
1407
1584
|
title: args.title,
|
|
1408
1585
|
type: args.type,
|
|
1409
1586
|
scope: args.scope,
|
|
@@ -1425,7 +1602,7 @@ export function registerTools(server, deps) {
|
|
|
1425
1602
|
status: z.enum(["active", "deprecated"]).optional().describe("更新条目状态"),
|
|
1426
1603
|
};
|
|
1427
1604
|
registerSafeTool("sf_knowledge_update", "更新已有知识条目:追加规则、更新触发词、标记废弃。自动创建备份", KnowledgeUpdateSchema, async (args) => {
|
|
1428
|
-
const result = await updateKnowledge({
|
|
1605
|
+
const result = await (await lazyKnowledge()).updateKnowledge({
|
|
1429
1606
|
entry_name: args.entry_name,
|
|
1430
1607
|
updates: {
|
|
1431
1608
|
when_triggers: args.when_triggers,
|
|
@@ -1444,13 +1621,13 @@ export function registerTools(server, deps) {
|
|
|
1444
1621
|
});
|
|
1445
1622
|
// ── sf_resume_workspace: 工作区状态唤醒(解决前端刷新导致UUID丢失) ──
|
|
1446
1623
|
registerSafeTool("sf_resume_workspace", "扫描 .soloforge/state/ 目录,恢复中断工单并播报当前进度。新会话启动时必须优先调用", {}, async () => {
|
|
1447
|
-
const result = await scanAndResume(taskContext);
|
|
1624
|
+
const result = await (await lazyWorkspaceResumer()).scanAndResume(taskContext);
|
|
1448
1625
|
// 认知锚点:按恢复任务的 changed_files 相关性加载,禁止全量
|
|
1449
1626
|
if (result.task) {
|
|
1450
1627
|
const ctx = await taskContext.load(result.task.task_id);
|
|
1451
1628
|
const changedFiles = ctx?.execution?.changed_files;
|
|
1452
1629
|
if (changedFiles && changedFiles.length > 0) {
|
|
1453
|
-
const anchors = loadRelevantAnchors({ file_paths: changedFiles });
|
|
1630
|
+
const anchors = await loadRelevantAnchors({ file_paths: changedFiles });
|
|
1454
1631
|
if (anchors && anchors.length > 0) {
|
|
1455
1632
|
result.cognitive_anchors = anchors;
|
|
1456
1633
|
}
|
|
@@ -1458,7 +1635,7 @@ export function registerTools(server, deps) {
|
|
|
1458
1635
|
}
|
|
1459
1636
|
// JobManager advisory: 查询活跃 jobs
|
|
1460
1637
|
try {
|
|
1461
|
-
const jm = new JobManager(taskContext.getStateDir());
|
|
1638
|
+
const jm = new (await lazyJob()).JobManager(taskContext.getStateDir());
|
|
1462
1639
|
const activeJobs = await jm.listActiveJobs();
|
|
1463
1640
|
if (activeJobs.length > 0) {
|
|
1464
1641
|
result.active_jobs = activeJobs.map((j) => ({
|
|
@@ -1472,7 +1649,7 @@ export function registerTools(server, deps) {
|
|
|
1472
1649
|
}
|
|
1473
1650
|
}
|
|
1474
1651
|
catch {
|
|
1475
|
-
// JobManager advisory —
|
|
1652
|
+
// JobManager advisory — 忽略错误
|
|
1476
1653
|
}
|
|
1477
1654
|
return {
|
|
1478
1655
|
result: result,
|
|
@@ -1484,10 +1661,10 @@ export function registerTools(server, deps) {
|
|
|
1484
1661
|
};
|
|
1485
1662
|
registerSafeTool("sf_audit_sample", "从审计池按风险加权抽样,生成抽检清单(SamplingDecision 列表)。只读,不落盘,不生成逃逸报告,不改能力状态", AuditSampleSchema, async (args) => {
|
|
1486
1663
|
const stateDir = taskContext.getStateDir();
|
|
1487
|
-
const pool = new AuditPool(stateDir);
|
|
1664
|
+
const pool = new (await lazyAuditPool()).AuditPool(stateDir);
|
|
1488
1665
|
const items = pool.list();
|
|
1489
1666
|
const seed = args.seed ?? 0;
|
|
1490
|
-
const result = sampleAuditItems(items, seed);
|
|
1667
|
+
const result = (await lazyAuditSampler()).sampleAuditItems(items, seed);
|
|
1491
1668
|
return {
|
|
1492
1669
|
result: result,
|
|
1493
1670
|
};
|
|
@@ -1510,7 +1687,7 @@ export function registerTools(server, deps) {
|
|
|
1510
1687
|
};
|
|
1511
1688
|
registerSafeTool("sf_escape_report", "记录逃逸、误伤或工具故障报告。只落盘记录,不自动降级,不改能力状态", EscapeReportSchema, async (args) => {
|
|
1512
1689
|
const stateDir = taskContext.getStateDir();
|
|
1513
|
-
const store = new EscapeReportStore(stateDir);
|
|
1690
|
+
const store = new (await lazyEscape()).EscapeReportStore(stateDir);
|
|
1514
1691
|
const report = {
|
|
1515
1692
|
escape_id: args.escape_id,
|
|
1516
1693
|
task_id: args.task_id,
|
|
@@ -1543,10 +1720,10 @@ export function registerTools(server, deps) {
|
|
|
1543
1720
|
};
|
|
1544
1721
|
registerSafeTool("sf_capability_update", "根据复盘结果更新 Capability Registry 状态。支持 dry_run 校验、证据校验、人工确认。不会自动触发,必须显式调用", CapabilityUpdateSchema, async (args) => {
|
|
1545
1722
|
const stateDir = taskContext.getStateDir();
|
|
1546
|
-
const escapeStore = new EscapeReportStore(stateDir);
|
|
1547
|
-
const stateStore = new CapabilityStateStore(stateDir);
|
|
1723
|
+
const escapeStore = new (await lazyEscape()).EscapeReportStore(stateDir);
|
|
1724
|
+
const stateStore = new (await lazyCapState()).CapabilityStateStore(stateDir);
|
|
1548
1725
|
const escapeReports = escapeStore.list();
|
|
1549
|
-
const capabilities = getAllCapabilities();
|
|
1726
|
+
const capabilities = (await lazyCapability()).getAllCapabilities();
|
|
1550
1727
|
const cap = capabilities.find((c) => c.policy_id === args.policy_id);
|
|
1551
1728
|
const currentState = stateStore.getEffectiveState(args.policy_id, cap?.state ?? "experimental");
|
|
1552
1729
|
const result = stateStore.apply({
|
|
@@ -1569,25 +1746,25 @@ export function registerTools(server, deps) {
|
|
|
1569
1746
|
registerSafeTool("sf_governance_report", "生成治理健康报告:汇总审计池、逃逸报告、能力动作决策。只读,不改状态", GovernanceReportSchema, async (args) => {
|
|
1570
1747
|
const stateDir = taskContext.getStateDir();
|
|
1571
1748
|
const seed = args.seed ?? 0;
|
|
1572
|
-
const pool = new AuditPool(stateDir);
|
|
1573
|
-
const escapeStore = new EscapeReportStore(stateDir);
|
|
1749
|
+
const pool = new (await lazyAuditPool()).AuditPool(stateDir);
|
|
1750
|
+
const escapeStore = new (await lazyEscape()).EscapeReportStore(stateDir);
|
|
1574
1751
|
const auditItems = pool.list();
|
|
1575
1752
|
const auditStats = pool.stats();
|
|
1576
1753
|
const escapeReports = escapeStore.list();
|
|
1577
1754
|
const escapeStats = escapeStore.stats();
|
|
1578
|
-
//
|
|
1579
|
-
const capabilities = getAllCapabilities();
|
|
1580
|
-
const stateStore = new CapabilityStateStore(stateDir);
|
|
1581
|
-
const decisions = capabilities.map((cap) => {
|
|
1755
|
+
// 为所有能力生成动作决策(使用有效状态)
|
|
1756
|
+
const capabilities = (await lazyCapability()).getAllCapabilities();
|
|
1757
|
+
const stateStore = new (await lazyCapState()).CapabilityStateStore(stateDir);
|
|
1758
|
+
const decisions = await Promise.all(capabilities.map(async (cap) => {
|
|
1582
1759
|
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
|
-
//
|
|
1760
|
+
return (await lazyCapAdvisor()).decideAction(escapeReports, effectiveState, cap.policy_id);
|
|
1761
|
+
}));
|
|
1762
|
+
// 为 sampled_count 生成抽样决策
|
|
1763
|
+
const sampleResult = (await lazyAuditSampler()).sampleAuditItems(auditItems, seed);
|
|
1764
|
+
// 双层机制发现
|
|
1765
|
+
const dlFindings = (await lazyDualLayer()).validateMechanismLayerMaps();
|
|
1766
|
+
const dlMechanismCount = (await lazyDualLayer()).listMechanismLayerMaps().length;
|
|
1767
|
+
// 收集近期任务的产物记录
|
|
1591
1768
|
const recentTasks = await taskContext.listRecent(20);
|
|
1592
1769
|
const artifacts = [];
|
|
1593
1770
|
for (const t of recentTasks) {
|
|
@@ -1595,25 +1772,25 @@ export function registerTools(server, deps) {
|
|
|
1595
1772
|
if (tCtx?.artifact_output)
|
|
1596
1773
|
artifacts.push(tCtx.artifact_output);
|
|
1597
1774
|
}
|
|
1598
|
-
//
|
|
1775
|
+
// 收集配置解析报告 — 使用共享函数获取真实数据
|
|
1599
1776
|
let configReports = [];
|
|
1600
1777
|
let configEntries = [];
|
|
1601
1778
|
try {
|
|
1602
|
-
const resolved = await resolveCurrentProjectConfigReports(projectPath);
|
|
1779
|
+
const resolved = await (await lazyConfigPrecedence()).resolveCurrentProjectConfigReports(projectPath);
|
|
1603
1780
|
configReports = resolved.reports;
|
|
1604
1781
|
configEntries = resolved.entries;
|
|
1605
1782
|
}
|
|
1606
1783
|
catch (e) {
|
|
1607
1784
|
console.error(`[soloForge] 配置报告解析失败: ${e instanceof Error ? e.message : String(e)}`);
|
|
1608
1785
|
}
|
|
1609
|
-
//
|
|
1786
|
+
// 同时包含近期任务上下文中的报告
|
|
1610
1787
|
for (const t of recentTasks) {
|
|
1611
1788
|
const tCtx = await taskContext.load(t.task_id);
|
|
1612
1789
|
if (tCtx?.expansion?.config_resolution_reports) {
|
|
1613
1790
|
configReports.push(...tCtx.expansion.config_resolution_reports);
|
|
1614
1791
|
}
|
|
1615
1792
|
}
|
|
1616
|
-
const report =
|
|
1793
|
+
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
1794
|
return {
|
|
1618
1795
|
result: report,
|
|
1619
1796
|
};
|
|
@@ -1625,7 +1802,7 @@ export function registerTools(server, deps) {
|
|
|
1625
1802
|
};
|
|
1626
1803
|
registerSafeTool("sf_audit_integration", "审计生产代码的主链路集成状态,检测孤岛模块(只被测试导入、无生产代码调用的模块)", AuditIntegrationSchema, async (args) => {
|
|
1627
1804
|
const projectPath = config._projectPath || process.cwd();
|
|
1628
|
-
//
|
|
1805
|
+
// 使用作用域感知的生产文件收集(支持多仓库、多语言)
|
|
1629
1806
|
const { collectAllProductionFiles } = await import("../../engine/main_path_integration_contract.js");
|
|
1630
1807
|
const allProdFiles = collectAllProductionFiles(projectPath, config);
|
|
1631
1808
|
if (allProdFiles.length === 0) {
|
|
@@ -1636,8 +1813,8 @@ export function registerTools(server, deps) {
|
|
|
1636
1813
|
const targetFiles = args.changed_files
|
|
1637
1814
|
? allProdFiles.filter((rel) => args.changed_files.some((cf) => rel === cf || rel.startsWith(cf.replace(/\.(ts|tsx|js|jsx|java|kt|go|py)$/, ""))))
|
|
1638
1815
|
: allProdFiles;
|
|
1639
|
-
const contracts = buildMainPathIntegrationContracts(projectPath, targetFiles, config);
|
|
1640
|
-
const report = auditIntegration(contracts);
|
|
1816
|
+
const contracts = (await lazyMainPath()).buildMainPathIntegrationContracts(projectPath, targetFiles, config);
|
|
1817
|
+
const report = (await lazyMainPath()).auditIntegration(contracts);
|
|
1641
1818
|
if (args.json_output) {
|
|
1642
1819
|
return {
|
|
1643
1820
|
result: report,
|