soloforge 1.2.1 → 1.2.3
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +78 -18
- package/dist/adapters/claude_code/server.d.ts.map +1 -1
- package/dist/adapters/claude_code/server.js +2 -25
- package/dist/adapters/claude_code/server.js.map +1 -1
- package/dist/adapters/claude_code/tools.d.ts +8 -13
- package/dist/adapters/claude_code/tools.d.ts.map +1 -1
- package/dist/adapters/claude_code/tools.js +508 -87
- package/dist/adapters/claude_code/tools.js.map +1 -1
- package/dist/adapters/codex/codex_rules.d.ts.map +1 -1
- package/dist/adapters/codex/codex_rules.js +23 -1
- package/dist/adapters/codex/codex_rules.js.map +1 -1
- package/dist/adapters/shared/workflow_template.d.ts.map +1 -1
- package/dist/adapters/shared/workflow_template.js +25 -0
- package/dist/adapters/shared/workflow_template.js.map +1 -1
- package/dist/bin/soloforge.js +169 -18
- package/dist/bin/soloforge.js.map +1 -1
- package/dist/engine/audit_pool.d.ts +36 -0
- package/dist/engine/audit_pool.d.ts.map +1 -0
- package/dist/engine/audit_pool.js +83 -0
- package/dist/engine/audit_pool.js.map +1 -0
- package/dist/engine/audit_sampler.d.ts +15 -0
- package/dist/engine/audit_sampler.d.ts.map +1 -0
- package/dist/engine/audit_sampler.js +26 -0
- package/dist/engine/audit_sampler.js.map +1 -0
- package/dist/engine/audit_verifier.d.ts +1 -1
- package/dist/engine/audit_verifier.js +1 -1
- package/dist/engine/audit_verifier.js.map +1 -1
- package/dist/engine/capability_action_advisor.d.ts +24 -0
- package/dist/engine/capability_action_advisor.d.ts.map +1 -0
- package/dist/engine/capability_action_advisor.js +147 -0
- package/dist/engine/capability_action_advisor.js.map +1 -0
- package/dist/engine/capability_registry.d.ts +58 -0
- package/dist/engine/capability_registry.d.ts.map +1 -0
- package/dist/engine/capability_registry.js +625 -0
- package/dist/engine/capability_registry.js.map +1 -0
- package/dist/engine/capability_state_store.d.ts +50 -0
- package/dist/engine/capability_state_store.d.ts.map +1 -0
- package/dist/engine/capability_state_store.js +123 -0
- package/dist/engine/capability_state_store.js.map +1 -0
- package/dist/engine/cognitive_anchor.d.ts +59 -0
- package/dist/engine/cognitive_anchor.d.ts.map +1 -0
- package/dist/engine/cognitive_anchor.js +68 -0
- package/dist/engine/cognitive_anchor.js.map +1 -0
- package/dist/engine/conflict_gate.d.ts +36 -0
- package/dist/engine/conflict_gate.d.ts.map +1 -0
- package/dist/engine/conflict_gate.js +73 -0
- package/dist/engine/conflict_gate.js.map +1 -0
- package/dist/engine/decision_contract.d.ts +29 -0
- package/dist/engine/decision_contract.d.ts.map +1 -0
- package/dist/engine/decision_contract.js +41 -0
- package/dist/engine/decision_contract.js.map +1 -0
- package/dist/engine/delivery.d.ts.map +1 -1
- package/dist/engine/delivery.js +83 -0
- package/dist/engine/delivery.js.map +1 -1
- package/dist/engine/developer_sovereignty.d.ts +62 -0
- package/dist/engine/developer_sovereignty.d.ts.map +1 -0
- package/dist/engine/developer_sovereignty.js +134 -0
- package/dist/engine/developer_sovereignty.js.map +1 -0
- package/dist/engine/diff_ownership.d.ts +74 -0
- package/dist/engine/diff_ownership.d.ts.map +1 -0
- package/dist/engine/diff_ownership.js +143 -0
- package/dist/engine/diff_ownership.js.map +1 -0
- package/dist/engine/diff_ownership_store.d.ts +76 -0
- package/dist/engine/diff_ownership_store.d.ts.map +1 -0
- package/dist/engine/diff_ownership_store.js +264 -0
- package/dist/engine/diff_ownership_store.js.map +1 -0
- package/dist/engine/escape_report.d.ts +45 -0
- package/dist/engine/escape_report.d.ts.map +1 -0
- package/dist/engine/escape_report.js +97 -0
- package/dist/engine/escape_report.js.map +1 -0
- package/dist/engine/exploration.d.ts +54 -0
- package/dist/engine/exploration.d.ts.map +1 -1
- package/dist/engine/exploration.js +138 -0
- package/dist/engine/exploration.js.map +1 -1
- package/dist/engine/governance_report.d.ts +36 -0
- package/dist/engine/governance_report.d.ts.map +1 -0
- package/dist/engine/governance_report.js +79 -0
- package/dist/engine/governance_report.js.map +1 -0
- package/dist/engine/java_quality_guard.d.ts +52 -0
- package/dist/engine/java_quality_guard.d.ts.map +1 -0
- package/dist/engine/java_quality_guard.js +237 -0
- package/dist/engine/java_quality_guard.js.map +1 -0
- package/dist/engine/job_manager.d.ts +76 -0
- package/dist/engine/job_manager.d.ts.map +1 -0
- package/dist/engine/job_manager.js +225 -0
- package/dist/engine/job_manager.js.map +1 -0
- package/dist/engine/knowledge_config_loader.d.ts +1 -1
- package/dist/engine/knowledge_config_loader.js +1 -1
- package/dist/engine/knowledge_sovereignty.d.ts +61 -0
- package/dist/engine/knowledge_sovereignty.d.ts.map +1 -0
- package/dist/engine/knowledge_sovereignty.js +190 -0
- package/dist/engine/knowledge_sovereignty.js.map +1 -0
- package/dist/engine/llm_gateway.js +2 -2
- package/dist/engine/llm_gateway.js.map +1 -1
- package/dist/engine/mutation_audit.d.ts +43 -0
- package/dist/engine/mutation_audit.d.ts.map +1 -0
- package/dist/engine/mutation_audit.js +118 -0
- package/dist/engine/mutation_audit.js.map +1 -0
- package/dist/engine/policy_drift_detector.d.ts +46 -0
- package/dist/engine/policy_drift_detector.d.ts.map +1 -0
- package/dist/engine/policy_drift_detector.js +181 -0
- package/dist/engine/policy_drift_detector.js.map +1 -0
- package/dist/engine/regression_matrix.d.ts +102 -0
- package/dist/engine/regression_matrix.d.ts.map +1 -0
- package/dist/engine/regression_matrix.js +380 -0
- package/dist/engine/regression_matrix.js.map +1 -0
- package/dist/engine/risk_sampler.d.ts +37 -0
- package/dist/engine/risk_sampler.d.ts.map +1 -0
- package/dist/engine/risk_sampler.js +69 -0
- package/dist/engine/risk_sampler.js.map +1 -0
- package/dist/engine/runtime_safety.d.ts +80 -0
- package/dist/engine/runtime_safety.d.ts.map +1 -0
- package/dist/engine/runtime_safety.js +195 -0
- package/dist/engine/runtime_safety.js.map +1 -0
- package/dist/engine/scope_lease.d.ts +45 -0
- package/dist/engine/scope_lease.d.ts.map +1 -0
- package/dist/engine/scope_lease.js +122 -0
- package/dist/engine/scope_lease.js.map +1 -0
- package/dist/engine/semantic_evidence.d.ts +23 -0
- package/dist/engine/semantic_evidence.d.ts.map +1 -0
- package/dist/engine/semantic_evidence.js +81 -0
- package/dist/engine/semantic_evidence.js.map +1 -0
- package/dist/engine/task_context.d.ts +16 -0
- package/dist/engine/task_context.d.ts.map +1 -1
- package/dist/engine/task_context.js +59 -1
- package/dist/engine/task_context.js.map +1 -1
- package/dist/engine/test_quality.js +2 -2
- package/dist/engine/test_quality.js.map +1 -1
- package/dist/engine/verifier.d.ts.map +1 -1
- package/dist/engine/verifier.js +113 -20
- package/dist/engine/verifier.js.map +1 -1
- package/dist/engine/workspace_resumer.d.ts +43 -0
- package/dist/engine/workspace_resumer.d.ts.map +1 -1
- package/dist/engine/workspace_resumer.js +119 -4
- package/dist/engine/workspace_resumer.js.map +1 -1
- package/dist/engine/zero_config_init.d.ts +87 -0
- package/dist/engine/zero_config_init.d.ts.map +1 -0
- package/dist/engine/zero_config_init.js +427 -0
- package/dist/engine/zero_config_init.js.map +1 -0
- package/dist/knowledge/index_manager.d.ts +19 -0
- package/dist/knowledge/index_manager.d.ts.map +1 -1
- package/dist/knowledge/index_manager.js +31 -0
- package/dist/knowledge/index_manager.js.map +1 -1
- package/dist/knowledge/writer.d.ts.map +1 -1
- package/dist/knowledge/writer.js +1 -0
- package/dist/knowledge/writer.js.map +1 -1
- package/dist/types.d.ts +61 -85
- package/dist/types.d.ts.map +1 -1
- package/package.json +2 -2
- package/templates/knowledge/domain//345/256/241/350/256/241/346/227/245/345/277/227.md +6 -7
- package/templates/knowledge/domain//345/257/274/345/205/245/345/257/274/345/207/272/350/247/204/345/210/231.md +3 -3
- package/templates/knowledge/domain//351/200/232/347/224/250/346/234/272/346/242/260/346/235/241/346/254/276.md +24 -14
- package/templates/knowledge/patterns/core/Diff/345/275/222/345/261/236/350/277/275/350/270/252.md +47 -0
- package/templates/knowledge/patterns/core/Java/350/264/250/351/207/217/351/227/250/347/246/201.md +46 -0
- package/templates/knowledge/patterns/core/LLM/351/242/204/347/256/227/347/275/221/345/205/263.md +46 -0
- package/templates/knowledge/patterns/core//344/273/273/345/212/241/344/270/212/344/270/213/346/226/207/347/224/237/345/221/275/345/221/250/346/234/237.md +47 -0
- package/templates/knowledge/patterns/core//344/273/273/345/212/241/347/256/241/347/220/206/345/231/250.md +47 -0
- package/templates/knowledge/patterns/core//344/275/234/347/224/250/345/237/237/344/270/216/345/257/206/351/222/245/346/213/246/346/210/252.md +46 -0
- package/templates/knowledge/patterns/core//344/275/234/347/224/250/345/237/237/347/247/237/347/272/246.md +47 -0
- package/templates/knowledge/patterns/core//345/206/262/347/252/201/351/227/250/347/246/201.md +47 -0
- package/templates/knowledge/patterns/core//345/206/263/347/255/226/347/275/221/345/205/263.md +52 -0
- package/templates/knowledge/patterns/core/{mutation_audit.md → /345/217/230/345/274/202/345/256/241/350/256/241.md} +20 -0
- package/templates/knowledge/patterns/core//345/233/236/345/275/222/347/237/251/351/230/265.md +46 -0
- package/templates/knowledge/patterns/core//345/267/245/344/275/234/345/214/272/344/272/222/346/226/245/351/224/201.md +44 -0
- package/templates/knowledge/patterns/core//345/267/245/344/275/234/345/214/272/345/224/244/351/206/222.md +46 -0
- package/templates/knowledge/patterns/core//345/271/266/345/217/221/351/224/201.md +49 -0
- package/templates/knowledge/patterns/core/{developer_constitution.md → /345/274/200/345/217/221/350/200/205/345/256/252/346/263/225.md} +20 -0
- package/templates/knowledge/patterns/core//346/225/217/346/204/237/344/277/241/346/201/257/346/211/253/346/217/217.md +45 -0
- package/templates/knowledge/patterns/core//346/262/273/347/220/206/350/277/220/350/241/214/346/227/266/345/276/252/347/216/257.md +48 -0
- package/templates/knowledge/patterns/core/{streaming_protocol.md → /346/265/201/345/274/217/345/277/203/350/267/263.md} +20 -0
- package/templates/knowledge/patterns/core/{authority.md → /347/237/245/350/257/206/344/270/273/346/235/203.md} +20 -0
- package/templates/knowledge/patterns/core//350/257/255/344/271/211/350/257/201/346/215/256.md +47 -0
- package/templates/knowledge/patterns/core//350/277/220/350/241/214/345/256/211/345/205/250/345/214/205.md +50 -0
- package/templates/knowledge/patterns/core//351/233/266/351/205/215/347/275/256/345/210/235/345/247/213/345/214/226.md +47 -0
- package/templates/knowledge/patterns/core//351/252/214/350/257/201/345/221/275/344/273/244/347/224/237/346/210/220.md +46 -0
- package/templates/knowledge/procedures//347/264/247/346/200/245/344/277/256/345/244/215/346/265/201/346/260/264/347/272/277.md +1 -1
- package/templates/knowledge/procedures//347/264/247/346/200/245/344/277/256/345/244/215/346/265/201/347/250/213.md +1 -1
- package/templates/knowledge/review_rules//345/271/266/345/217/221/345/256/241/346/237/245/350/247/204/345/210/231.md +1 -1
- package/templates/knowledge/review_rules//346/200/247/350/203/275/345/256/241/346/237/245/350/247/204/345/210/231.md +1 -1
- package/templates/knowledge/review_rules//346/216/245/345/217/243/345/245/221/347/272/246/345/256/241/346/237/245/350/247/204/345/210/231.md +1 -1
- package/templates/knowledge/review_rules//346/236/266/346/236/204/345/256/241/346/237/245/350/247/204/345/210/231.md +1 -1
- package/templates/knowledge/review_rules//350/264/250/351/207/217/345/256/241/346/237/245/350/247/204/345/210/231.md +1 -1
- package/templates/knowledge/patterns/core/concurrency_lock.md +0 -36
- package/templates/knowledge/patterns/core/decision_gateway.md +0 -33
- /package/templates/knowledge/checklists/{session_recovery.md → /344/274/232/350/257/235/346/201/242/345/244/215.md"} +0 -0
|
@@ -1,4 +1,6 @@
|
|
|
1
1
|
import { z } from "zod";
|
|
2
|
+
import path from "node:path";
|
|
3
|
+
import fss from "node:fs";
|
|
2
4
|
import { classify } from "../../engine/classifier.js";
|
|
3
5
|
import { expand } from "../../engine/intent_expander.js";
|
|
4
6
|
import { generateVerifyCommands } from "../../engine/verifier.js";
|
|
@@ -18,6 +20,7 @@ import { auditKnowledge, addKnowledge, updateKnowledge } from "../../engine/know
|
|
|
18
20
|
import { checkFeasibility } from "../../engine/feasibility_checker.js";
|
|
19
21
|
import { debugError } from "../../engine/debugger.js";
|
|
20
22
|
import { generateReport } from "../../engine/observability.js";
|
|
23
|
+
import { generateReport as generateGovernanceReport } from "../../engine/governance_report.js";
|
|
21
24
|
import { analyzeMigration } from "../../engine/migration_guard.js";
|
|
22
25
|
import { generateTestGuide } from "../../engine/test_generator.js";
|
|
23
26
|
import { analyzeTestQuality } from "../../engine/test_quality.js";
|
|
@@ -25,6 +28,17 @@ import { scanDependencies } from "../../engine/dependency_scanner.js";
|
|
|
25
28
|
import { generateDebtReport } from "../../engine/debt_reporter.js";
|
|
26
29
|
import { exploreSolutions } from "../../engine/exploration.js";
|
|
27
30
|
import { scanAndResume } from "../../engine/workspace_resumer.js";
|
|
31
|
+
import { LLMGateway } from "../../engine/llm_gateway.js";
|
|
32
|
+
import { IOController } from "../../engine/io_controller.js";
|
|
33
|
+
import { getSummary as getCapabilitySummary, getAllCapabilities } from "../../engine/capability_registry.js";
|
|
34
|
+
import { validateDecisionOutput } from "../../engine/decision_contract.js";
|
|
35
|
+
import { readMapJson, checkAnchorStaleness, filterRelevantAnchors } from "../../engine/cognitive_anchor.js";
|
|
36
|
+
import { JobManager } from "../../engine/job_manager.js";
|
|
37
|
+
import { AuditPool } from "../../engine/audit_pool.js";
|
|
38
|
+
import { EscapeReportStore } from "../../engine/escape_report.js";
|
|
39
|
+
import { sampleAuditItems } from "../../engine/audit_sampler.js";
|
|
40
|
+
import { decideAction } from "../../engine/capability_action_advisor.js";
|
|
41
|
+
import { CapabilityStateStore } from "../../engine/capability_state_store.js";
|
|
28
42
|
// ── Zod Schema 定义 ──
|
|
29
43
|
const ClassifySchema = {
|
|
30
44
|
intent: z.string().describe("开发者意图描述"),
|
|
@@ -51,6 +65,8 @@ const LearnSchema = {
|
|
|
51
65
|
const StatusSchema = {
|
|
52
66
|
task_id: z.string().optional().describe("任务 ID(不传则查当前任务)"),
|
|
53
67
|
action: z.enum(["current", "recent", "resume", "cancel"]).optional().describe("操作类型"),
|
|
68
|
+
job_id: z.string().optional().describe("Job ID(用于 job_status / job_resume / job_cancel)"),
|
|
69
|
+
job_action: z.enum(["job_status", "job_resume", "job_cancel"]).optional().describe("Job 操作: 查询状态、从 checkpoint 恢复、取消 job(advisory)"),
|
|
54
70
|
};
|
|
55
71
|
const PlanSchema = {
|
|
56
72
|
task_id: z.string().describe("sf_classify 返回的任务 ID"),
|
|
@@ -126,14 +142,17 @@ const ExploreSchema = {
|
|
|
126
142
|
* sf_deliver、sf_coord_check、sf_team_status、sf_contract_check、
|
|
127
143
|
* sf_onboard、sf_feasibility_check、sf_debug、sf_observability、
|
|
128
144
|
* sf_migration_check、sf_test_guide、sf_test_quality、sf_dependency_scan、
|
|
129
|
-
* sf_debt_report
|
|
145
|
+
* sf_debt_report、sf_explore、sf_knowledge_audit/add/update、sf_resume_workspace。
|
|
130
146
|
* @param server - MCP 服务器实例
|
|
131
147
|
* @param deps - SoloForge 核心依赖项
|
|
132
|
-
* @param options - 注册选项(控制自维护工具是否注册)
|
|
133
148
|
*/
|
|
134
|
-
export function registerTools(server, deps
|
|
149
|
+
export function registerTools(server, deps) {
|
|
135
150
|
const { config, knowledgeIndex, taskContext } = deps;
|
|
136
151
|
const projectPath = config._projectPath || process.cwd();
|
|
152
|
+
// H1: LLM Gateway — Token 预算熔断,防止单任务 Token 超支
|
|
153
|
+
const gateway = deps.gateway ?? new LLMGateway();
|
|
154
|
+
// H4: IO Controller — 工作区互斥锁,防止外部修改导致的静默覆盖
|
|
155
|
+
const ioController = new IOController(projectPath);
|
|
137
156
|
// 统一错误处理包装器,捕获工具执行中的异常并返回结构化错误响应
|
|
138
157
|
function registerSafeTool(name, desc, schema, handler) {
|
|
139
158
|
server.tool(name, desc, schema, async (args) => {
|
|
@@ -148,6 +167,58 @@ export function registerTools(server, deps, options) {
|
|
|
148
167
|
}
|
|
149
168
|
});
|
|
150
169
|
}
|
|
170
|
+
// 加载认知锚点上下文 — 必须提供相关性参数,禁止全量加载
|
|
171
|
+
function loadRelevantAnchors(query) {
|
|
172
|
+
const stateDir = taskContext.getStateDir();
|
|
173
|
+
const mapPath = path.join(stateDir, "cognitive.map.json");
|
|
174
|
+
const mapData = readMapJson(mapPath);
|
|
175
|
+
if (!mapData || mapData.anchors.length === 0)
|
|
176
|
+
return undefined;
|
|
177
|
+
const existingFiles = new Set();
|
|
178
|
+
try {
|
|
179
|
+
for (const anchor of mapData.anchors) {
|
|
180
|
+
for (const ref of anchor.source_refs) {
|
|
181
|
+
if (ref.type === "file" && fss.existsSync(path.join(projectPath, ref.ref))) {
|
|
182
|
+
existingFiles.add(ref.ref);
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
}
|
|
186
|
+
}
|
|
187
|
+
catch { /* ignore */ }
|
|
188
|
+
const checked = checkAnchorStaleness(mapData.anchors, existingFiles);
|
|
189
|
+
// 必须有 module 或 file_paths,否则不返回任何锚点(禁止全量加载)
|
|
190
|
+
if (!query.module && (!query.file_paths || query.file_paths.length === 0))
|
|
191
|
+
return undefined;
|
|
192
|
+
// Collect anchors matching ANY of the provided file_paths or module
|
|
193
|
+
const seen = new Set();
|
|
194
|
+
const relevant = [];
|
|
195
|
+
if (query.module) {
|
|
196
|
+
for (const a of filterRelevantAnchors(checked, { module: query.module })) {
|
|
197
|
+
if (!seen.has(a.anchor_id)) {
|
|
198
|
+
seen.add(a.anchor_id);
|
|
199
|
+
relevant.push(a);
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
if (query.file_paths) {
|
|
204
|
+
for (const fp of query.file_paths) {
|
|
205
|
+
for (const a of filterRelevantAnchors(checked, { file_path: fp })) {
|
|
206
|
+
if (!seen.has(a.anchor_id)) {
|
|
207
|
+
seen.add(a.anchor_id);
|
|
208
|
+
relevant.push(a);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
if (relevant.length === 0)
|
|
214
|
+
return undefined;
|
|
215
|
+
return relevant.map((a) => ({
|
|
216
|
+
anchor_id: a.anchor_id,
|
|
217
|
+
module: a.module,
|
|
218
|
+
summary: a.core_contracts.slice(0, 3).join("; "),
|
|
219
|
+
stale_anchor: a.stale_anchor,
|
|
220
|
+
}));
|
|
221
|
+
}
|
|
151
222
|
// ── sf_classify: 意图分类入口,创建任务上下文并返回分类结果 ──
|
|
152
223
|
registerSafeTool("sf_classify", "分析开发者意图,返回任务类型、风险、复杂度和执行策略", ClassifySchema, async (args) => {
|
|
153
224
|
// 检查是否存在进行中的任务
|
|
@@ -173,8 +244,9 @@ export function registerTools(server, deps, options) {
|
|
|
173
244
|
const classification = classify(input);
|
|
174
245
|
// 存储分类结果
|
|
175
246
|
await taskContext.setClassification(ctx.task_id, classification);
|
|
176
|
-
|
|
177
|
-
|
|
247
|
+
if (classification.strategy === "skip_soloForge") {
|
|
248
|
+
await taskContext.updateStatus(ctx.task_id, "done");
|
|
249
|
+
}
|
|
178
250
|
return {
|
|
179
251
|
content: [{
|
|
180
252
|
type: "text",
|
|
@@ -194,6 +266,16 @@ export function registerTools(server, deps, options) {
|
|
|
194
266
|
isError: true,
|
|
195
267
|
};
|
|
196
268
|
}
|
|
269
|
+
// 状态守卫:classifying → expanding 或允许 expanding 恢复(中断重试)
|
|
270
|
+
if (ctx.status === "classifying") {
|
|
271
|
+
await taskContext.updateStatus(args.task_id, "expanding");
|
|
272
|
+
}
|
|
273
|
+
else if (ctx.status !== "expanding") {
|
|
274
|
+
return {
|
|
275
|
+
content: [{ type: "text", text: JSON.stringify({ error: `任务状态 ${ctx.status} 不可膨胀,需要 classifying 或 expanding`, status: ctx.status }) }],
|
|
276
|
+
isError: true,
|
|
277
|
+
};
|
|
278
|
+
}
|
|
197
279
|
// 构建计划上下文(如果存在计划)
|
|
198
280
|
let planContext;
|
|
199
281
|
if (ctx.planning && ctx.planning.sub_tasks.length > 0 && ctx.planning.current_step_index < ctx.planning.sub_tasks.length) {
|
|
@@ -215,24 +297,93 @@ export function registerTools(server, deps, options) {
|
|
|
215
297
|
}
|
|
216
298
|
planContext = { current_step: currentStep, previous_outputs: previousOutputs };
|
|
217
299
|
}
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
}
|
|
300
|
+
// H1: Token 预算检查 — advisory 模式,仅警告不阻断
|
|
301
|
+
gateway.beginTask(args.task_id);
|
|
302
|
+
let h1Warning;
|
|
303
|
+
try {
|
|
304
|
+
const gateResult = gateway.request("solution_brainstorm");
|
|
305
|
+
if (!gateResult.allowed) {
|
|
306
|
+
h1Warning = { warning: `H1 advisory: ${gateResult.reason}`, budget_remaining: gateResult.remaining_budget };
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
catch (e) {
|
|
310
|
+
h1Warning = { warning: `H1 advisory: ${e.message}`, budget_remaining: 0 };
|
|
311
|
+
}
|
|
312
|
+
// H4: advisory-only — 不获取持久锁,仅检查当前锁定状态
|
|
313
|
+
let h4LockWarning;
|
|
314
|
+
const lockStatus = await ioController.isLocked();
|
|
315
|
+
if (lockStatus.locked && lockStatus.task_id !== args.task_id) {
|
|
316
|
+
h4LockWarning = `H4 advisory: 工作区已被任务 ${lockStatus.task_id} 锁定(${lockStatus.reason ?? "未知"}),当前 H4 未进入强制层`;
|
|
317
|
+
}
|
|
318
|
+
let expansion;
|
|
319
|
+
try {
|
|
320
|
+
expansion = await expand({
|
|
321
|
+
intent: ctx.intent,
|
|
322
|
+
classification: ctx.classification,
|
|
323
|
+
projectPath,
|
|
324
|
+
config,
|
|
325
|
+
knowledgeIndex,
|
|
326
|
+
clarificationAnswers: args.clarification_answers,
|
|
327
|
+
plan_context: planContext,
|
|
328
|
+
});
|
|
329
|
+
}
|
|
330
|
+
catch (expandErr) {
|
|
331
|
+
// 尝试回退到 failed 状态,避免任务停留在 expanding
|
|
332
|
+
try {
|
|
333
|
+
await taskContext.updateStatus(args.task_id, "failed");
|
|
334
|
+
}
|
|
335
|
+
catch {
|
|
336
|
+
// 状态转换非法(如已非 expanding),忽略
|
|
337
|
+
}
|
|
338
|
+
throw expandErr;
|
|
339
|
+
}
|
|
340
|
+
finally {
|
|
341
|
+
if (!h1Warning)
|
|
342
|
+
gateway.completeOperation();
|
|
343
|
+
gateway.endTask();
|
|
344
|
+
}
|
|
227
345
|
expansion.task_id = args.task_id;
|
|
346
|
+
// 注入 H1/H4 advisory warnings
|
|
347
|
+
const advisories = {};
|
|
348
|
+
if (h1Warning)
|
|
349
|
+
advisories.h1_advisory = h1Warning;
|
|
350
|
+
if (h4LockWarning)
|
|
351
|
+
advisories.h4_advisory = h4LockWarning;
|
|
352
|
+
// 决策契约 advisory: 校验 expand 输出是否包含默认决策字段
|
|
353
|
+
const decisionContract = validateDecisionOutput(expansion);
|
|
354
|
+
if (!decisionContract.passed) {
|
|
355
|
+
advisories.decision_contract_advisory = decisionContract.advisory;
|
|
356
|
+
}
|
|
228
357
|
// 存储膨胀结果
|
|
229
|
-
|
|
358
|
+
try {
|
|
359
|
+
await taskContext.setExpansion(args.task_id, expansion);
|
|
360
|
+
}
|
|
361
|
+
catch (setErr) {
|
|
362
|
+
try {
|
|
363
|
+
await taskContext.updateStatus(args.task_id, "failed");
|
|
364
|
+
}
|
|
365
|
+
catch {
|
|
366
|
+
// 状态转换非法,忽略
|
|
367
|
+
}
|
|
368
|
+
throw setErr;
|
|
369
|
+
}
|
|
230
370
|
// 转换到执行阶段
|
|
231
|
-
|
|
371
|
+
try {
|
|
372
|
+
await taskContext.updateStatus(args.task_id, "executing");
|
|
373
|
+
}
|
|
374
|
+
catch (updateErr) {
|
|
375
|
+
try {
|
|
376
|
+
await taskContext.updateStatus(args.task_id, "failed");
|
|
377
|
+
}
|
|
378
|
+
catch {
|
|
379
|
+
// 状态转换非法,忽略
|
|
380
|
+
}
|
|
381
|
+
throw updateErr;
|
|
382
|
+
}
|
|
232
383
|
return {
|
|
233
384
|
content: [{
|
|
234
385
|
type: "text",
|
|
235
|
-
text: JSON.stringify(expansion, null, 2),
|
|
386
|
+
text: JSON.stringify(Object.keys(advisories).length > 0 ? { ...expansion, advisories } : expansion, null, 2),
|
|
236
387
|
}],
|
|
237
388
|
};
|
|
238
389
|
});
|
|
@@ -245,9 +396,21 @@ export function registerTools(server, deps, options) {
|
|
|
245
396
|
isError: true,
|
|
246
397
|
};
|
|
247
398
|
}
|
|
399
|
+
const VALID_VERIFY_STATES = ["executing", "retrying"];
|
|
400
|
+
if (!VALID_VERIFY_STATES.includes(ctx.status)) {
|
|
401
|
+
return {
|
|
402
|
+
content: [{ type: "text", text: JSON.stringify({ error: `任务状态 ${ctx.status} 不可验证,需要 executing 或 retrying`, status: ctx.status }) }],
|
|
403
|
+
isError: true,
|
|
404
|
+
};
|
|
405
|
+
}
|
|
248
406
|
const acceptanceItems = ctx.expansion
|
|
249
407
|
? [...ctx.expansion.acceptance.automated, ...ctx.expansion.acceptance.manual]
|
|
250
408
|
: [];
|
|
409
|
+
// H4: 工作区完整性检查 — advisory 模式,仅警告不阻断
|
|
410
|
+
const integrity = await ioController.verify();
|
|
411
|
+
const h4Warning = !integrity.clean
|
|
412
|
+
? { warning: `H4 advisory: ${integrity.message}`, dirty_files: integrity.dirty_files }
|
|
413
|
+
: undefined;
|
|
251
414
|
const verifyResult = generateVerifyCommands(config, args.changed_files, acceptanceItems);
|
|
252
415
|
verifyResult.task_id = args.task_id;
|
|
253
416
|
// 记录变更文件
|
|
@@ -256,10 +419,17 @@ export function registerTools(server, deps, options) {
|
|
|
256
419
|
await taskContext.updateStatus(args.task_id, "verifying");
|
|
257
420
|
// 存储验证结果
|
|
258
421
|
await taskContext.setVerification(args.task_id, verifyResult);
|
|
422
|
+
// 认知锚点上下文回放(按变更文件相关性)
|
|
423
|
+
const verifyAnchors = loadRelevantAnchors({ file_paths: args.changed_files });
|
|
424
|
+
const verifyExtras = {};
|
|
425
|
+
if (h4Warning)
|
|
426
|
+
verifyExtras.h4_advisory = h4Warning;
|
|
427
|
+
if (verifyAnchors && verifyAnchors.length > 0)
|
|
428
|
+
verifyExtras.cognitive_anchors = verifyAnchors;
|
|
259
429
|
return {
|
|
260
430
|
content: [{
|
|
261
431
|
type: "text",
|
|
262
|
-
text: JSON.stringify(verifyResult, null, 2),
|
|
432
|
+
text: JSON.stringify(Object.keys(verifyExtras).length > 0 ? { ...verifyResult, ...verifyExtras } : verifyResult, null, 2),
|
|
263
433
|
}],
|
|
264
434
|
};
|
|
265
435
|
});
|
|
@@ -272,6 +442,13 @@ export function registerTools(server, deps, options) {
|
|
|
272
442
|
isError: true,
|
|
273
443
|
};
|
|
274
444
|
}
|
|
445
|
+
const VALID_LEARN_STATES = ["verifying", "executing"];
|
|
446
|
+
if (!VALID_LEARN_STATES.includes(ctx.status)) {
|
|
447
|
+
return {
|
|
448
|
+
content: [{ type: "text", text: JSON.stringify({ error: `任务状态 ${ctx.status} 不可学习,需要 verifying 或 executing`, status: ctx.status }) }],
|
|
449
|
+
isError: true,
|
|
450
|
+
};
|
|
451
|
+
}
|
|
275
452
|
// 如适用,记录失败
|
|
276
453
|
if (args.result === "failure" && args.failure_type) {
|
|
277
454
|
await taskContext.setExecution(args.task_id, [], {
|
|
@@ -315,6 +492,64 @@ export function registerTools(server, deps, options) {
|
|
|
315
492
|
// ── sf_status: 任务状态查询(current/recent/resume/cancel) ──
|
|
316
493
|
registerSafeTool("sf_status", "查询当前任务状态、列出近期任务、恢复中断任务或取消任务", StatusSchema, async (args) => {
|
|
317
494
|
const action = args.action || "current";
|
|
495
|
+
// Job actions — advisory, independent of task actions
|
|
496
|
+
if (args.job_action) {
|
|
497
|
+
if (!args.job_id) {
|
|
498
|
+
return {
|
|
499
|
+
content: [{ type: "text", text: JSON.stringify({ error: "job 操作需要 job_id" }) }],
|
|
500
|
+
isError: true,
|
|
501
|
+
};
|
|
502
|
+
}
|
|
503
|
+
const jm = new JobManager(taskContext.getStateDir());
|
|
504
|
+
const taskId = args.task_id ?? await jm.resolveTaskId(args.job_id) ?? "";
|
|
505
|
+
if (args.job_action === "job_status") {
|
|
506
|
+
const status = await jm.getJobStatus(args.job_id, taskId);
|
|
507
|
+
if (!status) {
|
|
508
|
+
return {
|
|
509
|
+
content: [{ type: "text", text: JSON.stringify({ error: `job ${args.job_id} 不存在或已过期` }) }],
|
|
510
|
+
isError: true,
|
|
511
|
+
};
|
|
512
|
+
}
|
|
513
|
+
return {
|
|
514
|
+
content: [{ type: "text", text: JSON.stringify({ job: status }, null, 2) }],
|
|
515
|
+
};
|
|
516
|
+
}
|
|
517
|
+
if (args.job_action === "job_resume") {
|
|
518
|
+
const checkpoint = await jm.resumeFromCheckpoint(args.job_id, taskId);
|
|
519
|
+
if (!checkpoint) {
|
|
520
|
+
return {
|
|
521
|
+
content: [{ type: "text", text: JSON.stringify({ error: `job ${args.job_id} 无可恢复的 checkpoint` }) }],
|
|
522
|
+
isError: true,
|
|
523
|
+
};
|
|
524
|
+
}
|
|
525
|
+
return {
|
|
526
|
+
content: [{
|
|
527
|
+
type: "text",
|
|
528
|
+
text: JSON.stringify({
|
|
529
|
+
advisory: "job 恢复为 advisory 操作,需确认 checkpoint 数据与当前状态一致",
|
|
530
|
+
job_id: checkpoint.job_id,
|
|
531
|
+
task_id: checkpoint.task_id,
|
|
532
|
+
phase: checkpoint.phase,
|
|
533
|
+
checkpoint_number: checkpoint.checkpoint_number,
|
|
534
|
+
cognitive_anchor: checkpoint.cognitive_anchor,
|
|
535
|
+
}, null, 2),
|
|
536
|
+
}],
|
|
537
|
+
};
|
|
538
|
+
}
|
|
539
|
+
if (args.job_action === "job_cancel") {
|
|
540
|
+
const removed = await jm.cancelJob(args.job_id, taskId);
|
|
541
|
+
return {
|
|
542
|
+
content: [{
|
|
543
|
+
type: "text",
|
|
544
|
+
text: JSON.stringify({
|
|
545
|
+
advisory: "job 取消为 advisory 操作",
|
|
546
|
+
job_id: args.job_id,
|
|
547
|
+
cancelled: removed,
|
|
548
|
+
}),
|
|
549
|
+
}],
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
}
|
|
318
553
|
switch (action) {
|
|
319
554
|
case "current": {
|
|
320
555
|
const ctx = args.task_id
|
|
@@ -345,6 +580,24 @@ export function registerTools(server, deps, options) {
|
|
|
345
580
|
result.changed_files = ctx.execution.changed_files;
|
|
346
581
|
result.attempt_count = ctx.execution.attempt_count;
|
|
347
582
|
}
|
|
583
|
+
// JobManager advisory: 查询关联 job 状态
|
|
584
|
+
try {
|
|
585
|
+
const jm = new JobManager(taskContext.getStateDir());
|
|
586
|
+
const activeJobs = await jm.listActiveJobs();
|
|
587
|
+
const relatedJob = activeJobs.find((j) => j.task_id === ctx.task_id);
|
|
588
|
+
if (relatedJob) {
|
|
589
|
+
result.job = {
|
|
590
|
+
job_id: relatedJob.job_id,
|
|
591
|
+
phase: relatedJob.current_phase,
|
|
592
|
+
checkpoint_count: relatedJob.checkpoint_count,
|
|
593
|
+
is_alive: relatedJob.is_alive,
|
|
594
|
+
latest_anchor_summary: relatedJob.latest_anchor?.summary,
|
|
595
|
+
};
|
|
596
|
+
}
|
|
597
|
+
}
|
|
598
|
+
catch {
|
|
599
|
+
// JobManager advisory — ignore errors
|
|
600
|
+
}
|
|
348
601
|
return {
|
|
349
602
|
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
350
603
|
};
|
|
@@ -392,6 +645,9 @@ export function registerTools(server, deps, options) {
|
|
|
392
645
|
changed_files: ctx.execution.changed_files,
|
|
393
646
|
attempt_count: ctx.execution.attempt_count,
|
|
394
647
|
} : undefined,
|
|
648
|
+
cognitive_anchors: ctx.execution?.changed_files?.length
|
|
649
|
+
? loadRelevantAnchors({ file_paths: ctx.execution.changed_files })
|
|
650
|
+
: undefined,
|
|
395
651
|
}, null, 2),
|
|
396
652
|
}],
|
|
397
653
|
};
|
|
@@ -504,8 +760,14 @@ export function registerTools(server, deps, options) {
|
|
|
504
760
|
projectPath,
|
|
505
761
|
});
|
|
506
762
|
result.task_id = args.task_id;
|
|
763
|
+
// 决策契约 advisory: 校验 analyze 输出
|
|
764
|
+
const decisionContract = validateDecisionOutput(result);
|
|
765
|
+
const advisoryExtras = {};
|
|
766
|
+
if (!decisionContract.passed) {
|
|
767
|
+
advisoryExtras.decision_contract_advisory = decisionContract.advisory;
|
|
768
|
+
}
|
|
507
769
|
return {
|
|
508
|
-
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
770
|
+
content: [{ type: "text", text: JSON.stringify(Object.keys(advisoryExtras).length > 0 ? { ...result, ...advisoryExtras } : result, null, 2) }],
|
|
509
771
|
};
|
|
510
772
|
});
|
|
511
773
|
// ── sf_review: 代码审查(质量、安全、性能、技术债务) ──
|
|
@@ -529,8 +791,14 @@ export function registerTools(server, deps, options) {
|
|
|
529
791
|
}, tracker);
|
|
530
792
|
result.task_id = args.task_id;
|
|
531
793
|
await taskContext.setCodeReview(args.task_id, result);
|
|
794
|
+
// 决策契约 advisory: 校验 review 输出
|
|
795
|
+
const decisionContract = validateDecisionOutput(result);
|
|
796
|
+
const reviewExtras = {};
|
|
797
|
+
if (!decisionContract.passed) {
|
|
798
|
+
reviewExtras.decision_contract_advisory = decisionContract.advisory;
|
|
799
|
+
}
|
|
532
800
|
return {
|
|
533
|
-
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
801
|
+
content: [{ type: "text", text: JSON.stringify(Object.keys(reviewExtras).length > 0 ? { ...result, ...reviewExtras } : result, null, 2) }],
|
|
534
802
|
};
|
|
535
803
|
});
|
|
536
804
|
// ── sf_scaffold: 生成标准化代码骨架 ──
|
|
@@ -574,6 +842,11 @@ export function registerTools(server, deps, options) {
|
|
|
574
842
|
isError: true,
|
|
575
843
|
};
|
|
576
844
|
}
|
|
845
|
+
// H4: 交付前工作区完整性检查 — advisory 模式,仅警告不阻断
|
|
846
|
+
const deliverIntegrity = await ioController.verify();
|
|
847
|
+
const h4DeliverWarning = !deliverIntegrity.clean
|
|
848
|
+
? { warning: `H4 advisory: ${deliverIntegrity.message}`, dirty_files: deliverIntegrity.dirty_files }
|
|
849
|
+
: undefined;
|
|
577
850
|
const result = await deliver({
|
|
578
851
|
taskContext: ctx,
|
|
579
852
|
config,
|
|
@@ -585,7 +858,10 @@ export function registerTools(server, deps, options) {
|
|
|
585
858
|
});
|
|
586
859
|
await taskContext.setDelivery(args.task_id, result);
|
|
587
860
|
return {
|
|
588
|
-
content: [{
|
|
861
|
+
content: [{
|
|
862
|
+
type: "text",
|
|
863
|
+
text: JSON.stringify(h4DeliverWarning ? { ...result, h4_advisory: h4DeliverWarning } : result, null, 2),
|
|
864
|
+
}],
|
|
589
865
|
};
|
|
590
866
|
});
|
|
591
867
|
// ── sf_coord_check: 预测性冲突检测和跨仓库协调 ──
|
|
@@ -670,11 +946,13 @@ export function registerTools(server, deps, options) {
|
|
|
670
946
|
};
|
|
671
947
|
});
|
|
672
948
|
// ── sf_observability: 系统可观测和运行报告 ──
|
|
673
|
-
registerSafeTool("sf_observability", "
|
|
949
|
+
registerSafeTool("sf_observability", "系统可观测:运行指标、成本估算、告警检测、能力状态、周期报告", ObservabilitySchema, async (args) => {
|
|
674
950
|
const stateDir = taskContext.getStateDir();
|
|
675
951
|
const result = await generateReport(stateDir, args.period_days);
|
|
952
|
+
const capability = getCapabilitySummary();
|
|
953
|
+
const output = { ...result, capability };
|
|
676
954
|
return {
|
|
677
|
-
content: [{ type: "text", text: JSON.stringify(
|
|
955
|
+
content: [{ type: "text", text: JSON.stringify(output, null, 2) }],
|
|
678
956
|
};
|
|
679
957
|
});
|
|
680
958
|
// ── sf_migration_check: 数据库迁移安全性分析 ──
|
|
@@ -726,76 +1004,219 @@ export function registerTools(server, deps, options) {
|
|
|
726
1004
|
classification,
|
|
727
1005
|
knowledgeIndex,
|
|
728
1006
|
});
|
|
1007
|
+
// 决策契约 advisory: sf_explore 应始终通过(10 字段已内置)
|
|
1008
|
+
const decisionContract = validateDecisionOutput(result);
|
|
1009
|
+
const exploreExtras = {};
|
|
1010
|
+
if (!decisionContract.passed) {
|
|
1011
|
+
exploreExtras.decision_contract_advisory = decisionContract.advisory;
|
|
1012
|
+
}
|
|
1013
|
+
return {
|
|
1014
|
+
content: [{ type: "text", text: JSON.stringify(Object.keys(exploreExtras).length > 0 ? { ...result, ...exploreExtras } : result, null, 2) }],
|
|
1015
|
+
};
|
|
1016
|
+
});
|
|
1017
|
+
// ── sf_knowledge_audit: 知识库健康审计 ──
|
|
1018
|
+
registerSafeTool("sf_knowledge_audit", "审计知识库:识别过时条目、重复触发词、格式缺失、覆盖缺口。定期执行或手动触发", {}, async () => {
|
|
1019
|
+
const result = await auditKnowledge(knowledgeIndex, config);
|
|
729
1020
|
return {
|
|
730
1021
|
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
731
1022
|
};
|
|
732
1023
|
});
|
|
733
|
-
// ──
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
1024
|
+
// ── sf_knowledge_add: 新增知识条目(默认保存为草稿) ──
|
|
1025
|
+
const KnowledgeAddSchema = {
|
|
1026
|
+
title: z.string().describe("知识条目标题(英文 kebab-case,如 payment-rules)"),
|
|
1027
|
+
type: z.enum(["pattern", "procedure", "pipeline_procedure", "domain", "acceptance_template", "review_rule"]).describe("知识类型"),
|
|
1028
|
+
scope: z.array(z.string()).describe("适用范围(如 [backend]、[frontend]、[backend, frontend])"),
|
|
1029
|
+
when_triggers: z.string().describe("触发关键词(逗号分隔)"),
|
|
1030
|
+
decision_rules: z.string().optional().describe("决策规则(每行一条)"),
|
|
1031
|
+
save_to_drafts: z.boolean().optional().describe("是否保存为草稿(默认 true,需人工 review 后移入正式目录)"),
|
|
1032
|
+
auto_enrich: z.boolean().optional().describe("是否返回行业最佳实践探索指引(默认 true,宿主 AI 将自动执行探索并填充内容)"),
|
|
1033
|
+
};
|
|
1034
|
+
registerSafeTool("sf_knowledge_add", "新增知识条目。默认保存为草稿到 .soloforge/knowledge/drafts/,人工确认后移入正式目录", KnowledgeAddSchema, async (args) => {
|
|
1035
|
+
const result = await addKnowledge({
|
|
1036
|
+
title: args.title,
|
|
1037
|
+
type: args.type,
|
|
1038
|
+
scope: args.scope,
|
|
1039
|
+
when_triggers: args.when_triggers,
|
|
1040
|
+
decision_rules: args.decision_rules,
|
|
1041
|
+
save_to_drafts: args.save_to_drafts,
|
|
1042
|
+
auto_enrich: args.auto_enrich,
|
|
1043
|
+
}, config);
|
|
1044
|
+
return {
|
|
1045
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
1046
|
+
};
|
|
1047
|
+
});
|
|
1048
|
+
// ── sf_knowledge_update: 更新已有知识条目(自动创建备份) ──
|
|
1049
|
+
const KnowledgeUpdateSchema = {
|
|
1050
|
+
entry_name: z.string().describe("要更新的知识条目名称(如 payment-rules)"),
|
|
1051
|
+
when_triggers: z.string().optional().describe("新的触发关键词(替换现有)"),
|
|
1052
|
+
add_decision_rules: z.array(z.string()).optional().describe("追加的决策规则"),
|
|
1053
|
+
add_acceptance_criteria: z.array(z.string()).optional().describe("追加的验收项"),
|
|
1054
|
+
status: z.enum(["active", "deprecated"]).optional().describe("更新条目状态"),
|
|
1055
|
+
};
|
|
1056
|
+
registerSafeTool("sf_knowledge_update", "更新已有知识条目:追加规则、更新触发词、标记废弃。自动创建备份", KnowledgeUpdateSchema, async (args) => {
|
|
1057
|
+
const result = await updateKnowledge({
|
|
1058
|
+
entry_name: args.entry_name,
|
|
1059
|
+
updates: {
|
|
757
1060
|
when_triggers: args.when_triggers,
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
},
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
}
|
|
783
|
-
}, knowledgeIndex, config);
|
|
784
|
-
// 重载知识索引
|
|
785
|
-
if (result.success) {
|
|
786
|
-
await knowledgeIndex.reload();
|
|
1061
|
+
add_decision_rules: args.add_decision_rules,
|
|
1062
|
+
add_acceptance_criteria: args.add_acceptance_criteria,
|
|
1063
|
+
status: args.status,
|
|
1064
|
+
},
|
|
1065
|
+
}, knowledgeIndex, config);
|
|
1066
|
+
// 重载知识索引
|
|
1067
|
+
if (result.success) {
|
|
1068
|
+
await knowledgeIndex.reload();
|
|
1069
|
+
}
|
|
1070
|
+
return {
|
|
1071
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
1072
|
+
};
|
|
1073
|
+
});
|
|
1074
|
+
// ── sf_resume_workspace: 工作区状态唤醒(解决前端刷新导致UUID丢失) ──
|
|
1075
|
+
registerSafeTool("sf_resume_workspace", "扫描 .soloforge/state/ 目录,恢复中断工单并播报当前进度。新会话启动时必须优先调用", {}, async () => {
|
|
1076
|
+
const result = await scanAndResume(taskContext);
|
|
1077
|
+
// 认知锚点:按恢复任务的 changed_files 相关性加载,禁止全量
|
|
1078
|
+
if (result.task) {
|
|
1079
|
+
const ctx = await taskContext.load(result.task.task_id);
|
|
1080
|
+
const changedFiles = ctx?.execution?.changed_files;
|
|
1081
|
+
if (changedFiles && changedFiles.length > 0) {
|
|
1082
|
+
const anchors = loadRelevantAnchors({ file_paths: changedFiles });
|
|
1083
|
+
if (anchors && anchors.length > 0) {
|
|
1084
|
+
result.cognitive_anchors = anchors;
|
|
1085
|
+
}
|
|
787
1086
|
}
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
1087
|
+
}
|
|
1088
|
+
// JobManager advisory: 查询活跃 jobs
|
|
1089
|
+
try {
|
|
1090
|
+
const jm = new JobManager(taskContext.getStateDir());
|
|
1091
|
+
const activeJobs = await jm.listActiveJobs();
|
|
1092
|
+
if (activeJobs.length > 0) {
|
|
1093
|
+
result.active_jobs = activeJobs.map((j) => ({
|
|
1094
|
+
job_id: j.job_id,
|
|
1095
|
+
task_id: j.task_id,
|
|
1096
|
+
phase: j.current_phase,
|
|
1097
|
+
is_alive: j.is_alive,
|
|
1098
|
+
latest_anchor_summary: j.latest_anchor?.summary,
|
|
1099
|
+
checkpoint_count: j.checkpoint_count,
|
|
1100
|
+
}));
|
|
1101
|
+
}
|
|
1102
|
+
}
|
|
1103
|
+
catch {
|
|
1104
|
+
// JobManager advisory — ignore errors
|
|
1105
|
+
}
|
|
1106
|
+
return {
|
|
1107
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
1108
|
+
};
|
|
1109
|
+
});
|
|
1110
|
+
// ── sf_audit_sample: 从审计池按风险加权抽样生成抽检清单 ──
|
|
1111
|
+
const AuditSampleSchema = {
|
|
1112
|
+
seed: z.number().optional().describe("抽样种子(默认 0,同种子可复现)"),
|
|
1113
|
+
};
|
|
1114
|
+
registerSafeTool("sf_audit_sample", "从审计池按风险加权抽样,生成抽检清单(SamplingDecision 列表)。只读,不落盘,不生成逃逸报告,不改能力状态", AuditSampleSchema, async (args) => {
|
|
1115
|
+
const stateDir = taskContext.getStateDir();
|
|
1116
|
+
const pool = new AuditPool(stateDir);
|
|
1117
|
+
const items = pool.list();
|
|
1118
|
+
const seed = args.seed ?? 0;
|
|
1119
|
+
const result = sampleAuditItems(items, seed);
|
|
1120
|
+
return {
|
|
1121
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
1122
|
+
};
|
|
1123
|
+
});
|
|
1124
|
+
// ── sf_escape_report: 记录逃逸/误伤/工具故障报告 ──
|
|
1125
|
+
const EscapeReportSchema = {
|
|
1126
|
+
escape_id: z.string().describe("逃逸报告唯一 ID"),
|
|
1127
|
+
task_id: z.string().describe("关联任务 ID"),
|
|
1128
|
+
policy_id: z.string().describe("关联能力 policy ID"),
|
|
1129
|
+
failure_type: z.enum(["rule_gap", "weak_test", "bad_evidence", "ai_escape", "human_miss", "tool_bug", "false_positive"]).describe("失败类型"),
|
|
1130
|
+
expected_guard: z.string().describe("期望的守卫行为"),
|
|
1131
|
+
actual_escape: z.string().describe("实际的逃逸行为"),
|
|
1132
|
+
evidence: z.string().describe("证据"),
|
|
1133
|
+
root_cause: z.string().describe("根因分析"),
|
|
1134
|
+
fix_required: z.string().describe("修复要求"),
|
|
1135
|
+
owner: z.string().describe("负责人"),
|
|
1136
|
+
due_date: z.string().describe("修复截止日期"),
|
|
1137
|
+
capability_action: z.enum(["keep", "downgrade", "disable", "promote_blocked"]).describe("建议的能力动作"),
|
|
1138
|
+
created_at: z.string().optional().describe("报告时间(ISO,默认当前时间)"),
|
|
1139
|
+
};
|
|
1140
|
+
registerSafeTool("sf_escape_report", "记录逃逸、误伤或工具故障报告。只落盘记录,不自动降级,不改能力状态", EscapeReportSchema, async (args) => {
|
|
1141
|
+
const stateDir = taskContext.getStateDir();
|
|
1142
|
+
const store = new EscapeReportStore(stateDir);
|
|
1143
|
+
const report = {
|
|
1144
|
+
escape_id: args.escape_id,
|
|
1145
|
+
task_id: args.task_id,
|
|
1146
|
+
policy_id: args.policy_id,
|
|
1147
|
+
failure_type: args.failure_type,
|
|
1148
|
+
expected_guard: args.expected_guard,
|
|
1149
|
+
actual_escape: args.actual_escape,
|
|
1150
|
+
evidence: args.evidence,
|
|
1151
|
+
root_cause: args.root_cause,
|
|
1152
|
+
fix_required: args.fix_required,
|
|
1153
|
+
owner: args.owner,
|
|
1154
|
+
due_date: args.due_date,
|
|
1155
|
+
capability_action: args.capability_action,
|
|
1156
|
+
created_at: args.created_at ?? new Date().toISOString(),
|
|
1157
|
+
};
|
|
1158
|
+
store.append(report);
|
|
1159
|
+
return {
|
|
1160
|
+
content: [{ type: "text", text: JSON.stringify({ recorded: true, escape_id: report.escape_id }, null, 2) }],
|
|
1161
|
+
};
|
|
1162
|
+
});
|
|
1163
|
+
// ── sf_capability_update: 根据复盘结果更新 Capability Registry ──
|
|
1164
|
+
const CapabilityUpdateSchema = {
|
|
1165
|
+
policy_id: z.string().describe("要更新的 policy ID(如 CAP-003)"),
|
|
1166
|
+
target_state: z.enum(["enforced", "advisory", "experimental", "removed"]).describe("目标状态"),
|
|
1167
|
+
reason: z.string().describe("变更原因"),
|
|
1168
|
+
evidence_ids: z.array(z.string()).describe("支撑此变更的 escape report ID 列表"),
|
|
1169
|
+
confirmed_by: z.string().describe("人工确认者标识(如操作者名字)"),
|
|
1170
|
+
dry_run: z.boolean().optional().describe("dry_run 模式:只校验不持久化(默认 false)"),
|
|
1171
|
+
confirm: z.boolean().describe("人工确认标志,必须为 true 才能执行变更"),
|
|
1172
|
+
};
|
|
1173
|
+
registerSafeTool("sf_capability_update", "根据复盘结果更新 Capability Registry 状态。支持 dry_run 校验、证据校验、人工确认。不会自动触发,必须显式调用", CapabilityUpdateSchema, async (args) => {
|
|
1174
|
+
const stateDir = taskContext.getStateDir();
|
|
1175
|
+
const escapeStore = new EscapeReportStore(stateDir);
|
|
1176
|
+
const stateStore = new CapabilityStateStore(stateDir);
|
|
1177
|
+
const escapeReports = escapeStore.list();
|
|
1178
|
+
const capabilities = getAllCapabilities();
|
|
1179
|
+
const cap = capabilities.find((c) => c.policy_id === args.policy_id);
|
|
1180
|
+
const currentState = stateStore.getEffectiveState(args.policy_id, cap?.state ?? "experimental");
|
|
1181
|
+
const result = stateStore.apply({
|
|
1182
|
+
policy_id: args.policy_id,
|
|
1183
|
+
target_state: args.target_state,
|
|
1184
|
+
reason: args.reason,
|
|
1185
|
+
evidence_ids: args.evidence_ids,
|
|
1186
|
+
confirmed_by: args.confirmed_by,
|
|
1187
|
+
dry_run: args.dry_run ?? false,
|
|
1188
|
+
confirm: args.confirm,
|
|
1189
|
+
}, currentState, cap?.id ?? null, escapeReports);
|
|
1190
|
+
return {
|
|
1191
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
1192
|
+
};
|
|
1193
|
+
});
|
|
1194
|
+
// ── sf_governance_report: 治理健康报告 ──
|
|
1195
|
+
const GovernanceReportSchema = {
|
|
1196
|
+
seed: z.number().optional().describe("抽样种子(用于计算 sampled_count,默认 0)"),
|
|
1197
|
+
};
|
|
1198
|
+
registerSafeTool("sf_governance_report", "生成治理健康报告:汇总审计池、逃逸报告、能力动作决策。只读,不改状态", GovernanceReportSchema, async (args) => {
|
|
1199
|
+
const stateDir = taskContext.getStateDir();
|
|
1200
|
+
const seed = args.seed ?? 0;
|
|
1201
|
+
const pool = new AuditPool(stateDir);
|
|
1202
|
+
const escapeStore = new EscapeReportStore(stateDir);
|
|
1203
|
+
const auditItems = pool.list();
|
|
1204
|
+
const auditStats = pool.stats();
|
|
1205
|
+
const escapeReports = escapeStore.list();
|
|
1206
|
+
const escapeStats = escapeStore.stats();
|
|
1207
|
+
// Generate action decisions for all capabilities (using effective states)
|
|
1208
|
+
const capabilities = getAllCapabilities();
|
|
1209
|
+
const stateStore = new CapabilityStateStore(stateDir);
|
|
1210
|
+
const decisions = capabilities.map((cap) => {
|
|
1211
|
+
const effectiveState = stateStore.getEffectiveState(cap.policy_id, cap.state);
|
|
1212
|
+
return decideAction(escapeReports, effectiveState, cap.policy_id);
|
|
798
1213
|
});
|
|
799
|
-
|
|
1214
|
+
// Generate sample decisions for sampled_count
|
|
1215
|
+
const sampleResult = sampleAuditItems(auditItems, seed);
|
|
1216
|
+
const report = generateGovernanceReport(auditStats, auditItems, escapeReports, escapeStats, decisions, new Date(), sampleResult.decisions);
|
|
1217
|
+
return {
|
|
1218
|
+
content: [{ type: "text", text: JSON.stringify(report, null, 2) }],
|
|
1219
|
+
};
|
|
1220
|
+
});
|
|
800
1221
|
}
|
|
801
1222
|
//# sourceMappingURL=tools.js.map
|