autosnippet 3.3.5 → 3.3.6
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/dashboard/dist/assets/icons-D1aVZYFW.js +1 -0
- package/dashboard/dist/assets/index-CxHOu8Hd.css +1 -0
- package/dashboard/dist/assets/index-DDdAOpYT.js +128 -0
- package/dashboard/dist/index.html +3 -3
- package/dist/bin/api-server.js +1 -0
- package/dist/bin/cli.d.ts +1 -0
- package/dist/bin/cli.js +136 -9
- package/dist/lib/agent/AgentFactory.d.ts +0 -17
- package/dist/lib/agent/AgentFactory.js +1 -25
- package/dist/lib/agent/capabilities.d.ts +11 -0
- package/dist/lib/agent/capabilities.js +29 -5
- package/dist/lib/agent/context/ExplorationTracker.js +10 -1
- package/dist/lib/agent/context/exploration/ExplorationStrategies.d.ts +2 -0
- package/dist/lib/agent/context/exploration/ExplorationStrategies.js +2 -2
- package/dist/lib/agent/domain/insight-analyst.d.ts +47 -3
- package/dist/lib/agent/domain/insight-analyst.js +111 -11
- package/dist/lib/agent/domain/insight-evolver.d.ts +69 -0
- package/dist/lib/agent/domain/insight-evolver.js +230 -0
- package/dist/lib/agent/domain/insight-gate.d.ts +42 -0
- package/dist/lib/agent/domain/insight-gate.js +41 -0
- package/dist/lib/agent/domain/insight-producer.d.ts +27 -2
- package/dist/lib/agent/domain/insight-producer.js +60 -5
- package/dist/lib/agent/domain/scan-prompts.js +10 -7
- package/dist/lib/agent/memory/ActiveContext.d.ts +2 -28
- package/dist/lib/agent/memory/MemoryCoordinator.d.ts +2 -2
- package/dist/lib/agent/memory/SessionStore.d.ts +6 -12
- package/dist/lib/agent/memory/SessionStore.js +9 -15
- package/dist/lib/agent/memory/memory-flush-contract.d.ts +49 -0
- package/dist/lib/agent/memory/memory-flush-contract.js +16 -0
- package/dist/lib/agent/memory/session-store-schema.d.ts +20 -0
- package/dist/lib/agent/memory/session-store-schema.js +41 -0
- package/dist/lib/agent/presets.d.ts +89 -1
- package/dist/lib/agent/presets.js +53 -5
- package/dist/lib/agent/tools/_shared.d.ts +7 -15
- package/dist/lib/agent/tools/_shared.js +20 -21
- package/dist/lib/agent/tools/composite.d.ts +25 -22
- package/dist/lib/agent/tools/composite.js +108 -109
- package/dist/lib/agent/tools/evolution-tools.d.ts +145 -0
- package/dist/lib/agent/tools/evolution-tools.js +161 -0
- package/dist/lib/agent/tools/index.d.ts +163 -92
- package/dist/lib/agent/tools/index.js +9 -1
- package/dist/lib/agent/tools/lifecycle.d.ts +7 -1
- package/dist/lib/agent/tools/lifecycle.js +59 -75
- package/dist/lib/cli/AiScanService.js +1 -1
- package/dist/lib/cli/KnowledgeSyncService.js +1 -1
- package/dist/lib/core/AstAnalyzer.d.ts +1 -0
- package/dist/lib/{service/bootstrap/DimensionCopyRegistry.d.ts → domain/dimension/DimensionCopy.d.ts} +2 -2
- package/dist/lib/{service/bootstrap/DimensionCopyRegistry.js → domain/dimension/DimensionCopy.js} +22 -72
- package/dist/lib/domain/dimension/DimensionRegistry.d.ts +54 -0
- package/dist/lib/domain/dimension/DimensionRegistry.js +620 -0
- package/dist/lib/domain/dimension/DimensionSop.d.ts +55 -0
- package/dist/lib/domain/dimension/DimensionSop.js +1604 -0
- package/dist/lib/domain/dimension/UnifiedDimension.d.ts +61 -0
- package/dist/lib/domain/dimension/UnifiedDimension.js +53 -0
- package/dist/lib/domain/dimension/index.d.ts +10 -0
- package/dist/lib/domain/dimension/index.js +9 -0
- package/dist/lib/domain/knowledge/FieldSpec.d.ts +1 -1
- package/dist/lib/domain/knowledge/FieldSpec.js +29 -16
- package/dist/lib/domain/knowledge/KnowledgeEntry.d.ts +33 -111
- package/dist/lib/domain/knowledge/KnowledgeEntry.js +27 -6
- package/dist/lib/domain/knowledge/KnowledgeRepository.d.ts +1 -0
- package/dist/lib/domain/knowledge/KnowledgeRepository.js +3 -0
- package/dist/lib/domain/knowledge/Lifecycle.js +1 -1
- package/dist/lib/domain/knowledge/StyleGuide.d.ts +1 -1
- package/dist/lib/domain/knowledge/StyleGuide.js +1 -1
- package/dist/lib/domain/knowledge/UnifiedValidator.js +15 -0
- package/dist/lib/external/mcp/McpServer.js +4 -0
- package/dist/lib/external/mcp/handlers/TargetClassifier.d.ts +1 -1
- package/dist/lib/external/mcp/handlers/bootstrap/BootstrapSession.d.ts +8 -16
- package/dist/lib/external/mcp/handlers/bootstrap/BootstrapSession.js +10 -10
- package/dist/lib/external/mcp/handlers/bootstrap/ExternalSubmissionTracker.d.ts +7 -0
- package/dist/lib/external/mcp/handlers/bootstrap/ExternalSubmissionTracker.js +20 -0
- package/dist/lib/external/mcp/handlers/bootstrap/MissionBriefingBuilder.d.ts +52 -132
- package/dist/lib/external/mcp/handlers/bootstrap/MissionBriefingBuilder.js +204 -17
- package/dist/lib/external/mcp/handlers/bootstrap/base-dimensions.d.ts +11 -75
- package/dist/lib/external/mcp/handlers/bootstrap/base-dimensions.js +40 -191
- package/dist/lib/external/mcp/handlers/bootstrap/pipeline/dimension-configs.d.ts +13 -78
- package/dist/lib/external/mcp/handlers/bootstrap/pipeline/dimension-configs.js +30 -52
- package/dist/lib/external/mcp/handlers/bootstrap/pipeline/dimension-context.d.ts +0 -1
- package/dist/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.d.ts +99 -12
- package/dist/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +172 -161
- package/dist/lib/external/mcp/handlers/bootstrap/pipeline/tier-scheduler.js +7 -17
- package/dist/lib/external/mcp/handlers/bootstrap/shared/async-fill-helpers.d.ts +46 -0
- package/dist/lib/external/mcp/handlers/bootstrap/shared/async-fill-helpers.js +58 -0
- package/dist/lib/external/mcp/handlers/bootstrap/shared/audit-helpers.d.ts +25 -0
- package/dist/lib/external/mcp/handlers/bootstrap/shared/audit-helpers.js +47 -0
- package/dist/lib/external/mcp/handlers/bootstrap/shared/bootstrap-phases.d.ts +50 -12
- package/dist/lib/external/mcp/handlers/bootstrap/shared/bootstrap-phases.js +30 -10
- package/dist/lib/external/mcp/handlers/bootstrap/shared/dimension-text.js +1 -1
- package/dist/lib/external/mcp/handlers/bootstrap/shared/handler-types.d.ts +24 -0
- package/dist/lib/external/mcp/handlers/bootstrap/shared/handler-types.js +14 -0
- package/dist/lib/external/mcp/handlers/bootstrap/shared/panorama-utils.d.ts +14 -0
- package/dist/lib/external/mcp/handlers/bootstrap/shared/panorama-utils.js +48 -0
- package/dist/lib/external/mcp/handlers/bootstrap/shared/session-helpers.d.ts +21 -0
- package/dist/lib/external/mcp/handlers/bootstrap/shared/session-helpers.js +45 -0
- package/dist/lib/external/mcp/handlers/bootstrap/shared/skill-generator.d.ts +1 -1
- package/dist/lib/external/mcp/handlers/bootstrap/shared/target-file-map.d.ts +27 -0
- package/dist/lib/external/mcp/handlers/bootstrap/shared/target-file-map.js +44 -0
- package/dist/lib/external/mcp/handlers/bootstrap-external.d.ts +14 -10
- package/dist/lib/external/mcp/handlers/bootstrap-external.js +39 -51
- package/dist/lib/external/mcp/handlers/bootstrap-internal.d.ts +2 -0
- package/dist/lib/external/mcp/handlers/bootstrap-internal.js +115 -82
- package/dist/lib/external/mcp/handlers/consolidated.d.ts +4 -4
- package/dist/lib/external/mcp/handlers/consolidated.js +107 -332
- package/dist/lib/external/mcp/handlers/dimension-complete-external.js +69 -1
- package/dist/lib/external/mcp/handlers/evolve-external.d.ts +54 -0
- package/dist/lib/external/mcp/handlers/evolve-external.js +226 -0
- package/dist/lib/external/mcp/handlers/knowledge.js +26 -2
- package/dist/lib/external/mcp/handlers/rescan-external.d.ts +76 -0
- package/dist/lib/external/mcp/handlers/rescan-external.js +335 -0
- package/dist/lib/external/mcp/handlers/rescan-internal.d.ts +120 -0
- package/dist/lib/external/mcp/handlers/rescan-internal.js +359 -0
- package/dist/lib/external/mcp/handlers/search.d.ts +6 -5
- package/dist/lib/external/mcp/handlers/search.js +6 -5
- package/dist/lib/external/mcp/handlers/types.d.ts +2 -1
- package/dist/lib/external/mcp/handlers/wiki-external.js +2 -2
- package/dist/lib/external/mcp/tools.d.ts +8 -18
- package/dist/lib/external/mcp/tools.js +58 -2
- package/dist/lib/http/routes/knowledge.js +122 -1
- package/dist/lib/http/routes/modules.js +25 -3
- package/dist/lib/http/routes/panorama.js +16 -4
- package/dist/lib/infrastructure/cache/CacheCoordinator.d.ts +41 -0
- package/dist/lib/infrastructure/cache/CacheCoordinator.js +105 -0
- package/dist/lib/infrastructure/database/migrations/006_lifecycle_transition_events.d.ts +7 -0
- package/dist/lib/infrastructure/database/migrations/006_lifecycle_transition_events.js +28 -0
- package/dist/lib/infrastructure/vector/HnswVectorAdapter.js +1 -1
- package/dist/lib/injection/ServiceContainer.js +55 -0
- package/dist/lib/injection/ServiceMap.d.ts +8 -1
- package/dist/lib/injection/modules/KnowledgeModule.js +15 -1
- package/dist/lib/repository/knowledge/KnowledgeRepository.impl.d.ts +4 -0
- package/dist/lib/repository/knowledge/KnowledgeRepository.impl.js +16 -1
- package/dist/lib/service/bootstrap/BootstrapEventEmitter.d.ts +3 -2
- package/dist/lib/service/bootstrap/BootstrapEventEmitter.js +1 -1
- package/dist/lib/service/bootstrap/DeliveryVerifier.d.ts +51 -0
- package/dist/lib/service/bootstrap/DeliveryVerifier.js +163 -0
- package/dist/lib/service/bootstrap/UiStartupTasks.d.ts +5 -0
- package/dist/lib/service/bootstrap/UiStartupTasks.js +20 -0
- package/dist/lib/service/bootstrap/bootstrap-event-types.d.ts +54 -0
- package/dist/lib/service/bootstrap/bootstrap-event-types.js +10 -0
- package/dist/lib/service/cleanup/CleanupService.d.ts +85 -0
- package/dist/lib/service/cleanup/CleanupService.js +324 -0
- package/dist/lib/service/delivery/AgentInstructionsGenerator.js +39 -43
- package/dist/lib/service/delivery/FileProtection.d.ts +20 -0
- package/dist/lib/service/delivery/FileProtection.js +54 -0
- package/dist/lib/service/delivery/SkillsSyncer.js +16 -21
- package/dist/lib/service/evolution/ContentPatcher.d.ts +44 -0
- package/dist/lib/service/evolution/ContentPatcher.js +310 -0
- package/dist/lib/service/evolution/ProposalExecutor.d.ts +4 -0
- package/dist/lib/service/evolution/ProposalExecutor.js +77 -13
- package/dist/lib/service/evolution/RecipeLifecycleSupervisor.d.ts +64 -0
- package/dist/lib/service/evolution/RecipeLifecycleSupervisor.js +458 -0
- package/dist/lib/service/evolution/RecipeRelevanceAuditor.d.ts +89 -0
- package/dist/lib/service/evolution/RecipeRelevanceAuditor.js +492 -0
- package/dist/lib/service/evolution/createSupersedeProposal.d.ts +44 -0
- package/dist/lib/service/evolution/createSupersedeProposal.js +81 -0
- package/dist/lib/service/guard/ComplianceReporter.d.ts +4 -0
- package/dist/lib/service/guard/ComplianceReporter.js +51 -0
- package/dist/lib/service/guard/GuardCheckEngine.js +5 -4
- package/dist/lib/service/knowledge/ConfidenceRouter.js +1 -1
- package/dist/lib/service/knowledge/KnowledgeService.d.ts +11 -1
- package/dist/lib/service/knowledge/KnowledgeService.js +44 -4
- package/dist/lib/service/knowledge/RecipeProductionGateway.d.ts +225 -0
- package/dist/lib/service/knowledge/RecipeProductionGateway.js +384 -0
- package/dist/lib/service/panorama/DimensionAnalyzer.d.ts +3 -2
- package/dist/lib/service/panorama/DimensionAnalyzer.js +15 -140
- package/dist/lib/service/search/BM25Scorer.d.ts +2 -2
- package/dist/lib/service/search/SearchEngine.d.ts +11 -10
- package/dist/lib/service/search/SearchEngine.js +38 -36
- package/dist/lib/service/search/SearchTypes.d.ts +14 -8
- package/dist/lib/service/search/SearchTypes.js +1 -1
- package/dist/lib/service/search/tokenizer.d.ts +1 -1
- package/dist/lib/service/search/tokenizer.js +2 -2
- package/dist/lib/shared/schemas/common.d.ts +4 -4
- package/dist/lib/shared/schemas/http-requests.d.ts +12 -1
- package/dist/lib/shared/schemas/http-requests.js +8 -0
- package/dist/lib/shared/schemas/mcp-tools.d.ts +32 -2
- package/dist/lib/shared/schemas/mcp-tools.js +38 -0
- package/dist/lib/types/evolution.d.ts +135 -0
- package/dist/lib/types/evolution.js +6 -0
- package/dist/lib/types/graph-shared.d.ts +25 -0
- package/dist/lib/types/graph-shared.js +7 -0
- package/dist/lib/types/knowledge-wire.d.ts +131 -0
- package/dist/lib/types/knowledge-wire.js +7 -0
- package/dist/lib/types/project-snapshot-builder.d.ts +19 -0
- package/dist/lib/types/project-snapshot-builder.js +189 -0
- package/dist/lib/types/project-snapshot.d.ts +399 -0
- package/dist/lib/types/project-snapshot.js +17 -0
- package/dist/lib/types/search-wire.d.ts +46 -0
- package/dist/lib/types/search-wire.js +7 -0
- package/dist/lib/types/snapshot-views.d.ts +58 -0
- package/dist/lib/types/snapshot-views.js +103 -0
- package/package.json +1 -1
- package/skills/autosnippet-recipes/SKILL.md +1 -1
- package/templates/instructions/agent-static.md +2 -0
- package/templates/instructions/conventions.md +3 -1
- package/templates/recipes-setup/README.md +2 -2
- package/dashboard/dist/assets/icons-BJ2mUBi8.js +0 -1
- package/dashboard/dist/assets/index-B659K9t5.js +0 -128
- package/dashboard/dist/assets/index-NCm40PMD.css +0 -1
- package/dist/lib/external/mcp/handlers/bootstrap/pipeline/noAiFallback.d.ts +0 -169
- package/dist/lib/external/mcp/handlers/bootstrap/pipeline/noAiFallback.js +0 -727
- package/dist/lib/external/mcp/handlers/bootstrap/shared/dimension-sop.d.ts +0 -370
- package/dist/lib/external/mcp/handlers/bootstrap/shared/dimension-sop.js +0 -821
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ContentPatcher — Proposal suggestedChanges 消费引擎
|
|
3
|
+
*
|
|
4
|
+
* 核心职责:
|
|
5
|
+
* 1. 从 Proposal.evidence 提取 suggestedChanges
|
|
6
|
+
* 2. 解析为结构化 Patch(JSON 或降级为纯文本)
|
|
7
|
+
* 3. 创建 Recipe 内容快照(before)
|
|
8
|
+
* 4. 应用 patch 到 Recipe 字段
|
|
9
|
+
* 5. 创建快照(after)
|
|
10
|
+
* 6. 持久化更新
|
|
11
|
+
*
|
|
12
|
+
* 安全边界:
|
|
13
|
+
* - 只修改 Patch 指定的字段,不擅自变更其他内容
|
|
14
|
+
* - suggestedChanges 缺失或格式不合规时降级跳过(不阻塞状态转移)
|
|
15
|
+
* - 所有变更在 before/after 快照中可追溯
|
|
16
|
+
*
|
|
17
|
+
* @module service/evolution/ContentPatcher
|
|
18
|
+
*/
|
|
19
|
+
import type { ContentPatchResult } from '../../types/evolution.js';
|
|
20
|
+
interface DatabaseLike {
|
|
21
|
+
prepare(sql: string): {
|
|
22
|
+
all(...params: unknown[]): Record<string, unknown>[];
|
|
23
|
+
get(...params: unknown[]): Record<string, unknown> | undefined;
|
|
24
|
+
run(...params: unknown[]): {
|
|
25
|
+
changes: number;
|
|
26
|
+
};
|
|
27
|
+
};
|
|
28
|
+
}
|
|
29
|
+
export declare class ContentPatcher {
|
|
30
|
+
#private;
|
|
31
|
+
constructor(db: DatabaseLike);
|
|
32
|
+
/**
|
|
33
|
+
* 从 Proposal evidence 提取 suggestedChanges 并应用到 Recipe
|
|
34
|
+
*
|
|
35
|
+
* @returns ContentPatchResult — success: 是否成功应用了至少一个 patch
|
|
36
|
+
*/
|
|
37
|
+
applyProposal(proposal: {
|
|
38
|
+
id: string;
|
|
39
|
+
type: string;
|
|
40
|
+
targetRecipeId: string;
|
|
41
|
+
evidence: Record<string, unknown>[];
|
|
42
|
+
}, patchSource?: 'agent-suggestion' | 'correction' | 'merge'): ContentPatchResult;
|
|
43
|
+
}
|
|
44
|
+
export {};
|
|
@@ -0,0 +1,310 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* ContentPatcher — Proposal suggestedChanges 消费引擎
|
|
3
|
+
*
|
|
4
|
+
* 核心职责:
|
|
5
|
+
* 1. 从 Proposal.evidence 提取 suggestedChanges
|
|
6
|
+
* 2. 解析为结构化 Patch(JSON 或降级为纯文本)
|
|
7
|
+
* 3. 创建 Recipe 内容快照(before)
|
|
8
|
+
* 4. 应用 patch 到 Recipe 字段
|
|
9
|
+
* 5. 创建快照(after)
|
|
10
|
+
* 6. 持久化更新
|
|
11
|
+
*
|
|
12
|
+
* 安全边界:
|
|
13
|
+
* - 只修改 Patch 指定的字段,不擅自变更其他内容
|
|
14
|
+
* - suggestedChanges 缺失或格式不合规时降级跳过(不阻塞状态转移)
|
|
15
|
+
* - 所有变更在 before/after 快照中可追溯
|
|
16
|
+
*
|
|
17
|
+
* @module service/evolution/ContentPatcher
|
|
18
|
+
*/
|
|
19
|
+
import Logger from '../../infrastructure/logging/Logger.js';
|
|
20
|
+
/* ────────────────────── Patchable Fields ────────────────────── */
|
|
21
|
+
/** 允许 patch 的顶层字段白名单 */
|
|
22
|
+
const PATCHABLE_FIELDS = new Set([
|
|
23
|
+
'coreCode',
|
|
24
|
+
'doClause',
|
|
25
|
+
'dontClause',
|
|
26
|
+
'whenClause',
|
|
27
|
+
'content.markdown',
|
|
28
|
+
'content.rationale',
|
|
29
|
+
'sourceRefs',
|
|
30
|
+
'headers',
|
|
31
|
+
]);
|
|
32
|
+
/* ────────────────────── Class ────────────────────── */
|
|
33
|
+
export class ContentPatcher {
|
|
34
|
+
#db;
|
|
35
|
+
#logger = Logger.getInstance();
|
|
36
|
+
constructor(db) {
|
|
37
|
+
this.#db = db;
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* 从 Proposal evidence 提取 suggestedChanges 并应用到 Recipe
|
|
41
|
+
*
|
|
42
|
+
* @returns ContentPatchResult — success: 是否成功应用了至少一个 patch
|
|
43
|
+
*/
|
|
44
|
+
applyProposal(proposal, patchSource = 'agent-suggestion') {
|
|
45
|
+
const recipeId = proposal.targetRecipeId;
|
|
46
|
+
// 1. 获取 Recipe 当前内容
|
|
47
|
+
const recipe = this.#getRecipe(recipeId);
|
|
48
|
+
if (!recipe) {
|
|
49
|
+
return this.#skipResult(recipeId, patchSource, 'Recipe not found');
|
|
50
|
+
}
|
|
51
|
+
// 2. 提取 suggestedChanges
|
|
52
|
+
const rawChanges = this.#extractSuggestedChanges(proposal.evidence);
|
|
53
|
+
if (!rawChanges) {
|
|
54
|
+
return this.#skipResult(recipeId, patchSource, 'No suggestedChanges in proposal evidence');
|
|
55
|
+
}
|
|
56
|
+
// 3. 解析为结构化 Patch
|
|
57
|
+
const patch = this.#parsePatch(rawChanges);
|
|
58
|
+
if (!patch || patch.changes.length === 0) {
|
|
59
|
+
return this.#skipResult(recipeId, patchSource, 'suggestedChanges could not be parsed or empty');
|
|
60
|
+
}
|
|
61
|
+
// 4. 创建 before 快照
|
|
62
|
+
const beforeSnapshot = this.#createSnapshot(recipe);
|
|
63
|
+
// 5. 应用 patch
|
|
64
|
+
const fieldsPatched = this.#applyPatch(recipe, patch.changes);
|
|
65
|
+
if (fieldsPatched.length === 0) {
|
|
66
|
+
return this.#skipResult(recipeId, patchSource, 'No valid fields to patch');
|
|
67
|
+
}
|
|
68
|
+
// 6. 持久化
|
|
69
|
+
this.#persistRecipe(recipe);
|
|
70
|
+
// 7. 创建 after 快照
|
|
71
|
+
const afterSnapshot = this.#createSnapshot(recipe);
|
|
72
|
+
this.#logger.info(`[ContentPatcher] Applied ${fieldsPatched.length} patches to recipe ${recipeId}: ${fieldsPatched.join(', ')}`);
|
|
73
|
+
return {
|
|
74
|
+
success: true,
|
|
75
|
+
recipeId,
|
|
76
|
+
fieldsPatched,
|
|
77
|
+
beforeSnapshot,
|
|
78
|
+
afterSnapshot,
|
|
79
|
+
patchSource,
|
|
80
|
+
skipped: false,
|
|
81
|
+
};
|
|
82
|
+
}
|
|
83
|
+
/* ═══════════════════ Extract ═══════════════════ */
|
|
84
|
+
#extractSuggestedChanges(evidence) {
|
|
85
|
+
for (const ev of evidence) {
|
|
86
|
+
const cast = ev;
|
|
87
|
+
if (cast.suggestedChanges &&
|
|
88
|
+
typeof cast.suggestedChanges === 'string' &&
|
|
89
|
+
cast.suggestedChanges.trim().length > 0) {
|
|
90
|
+
return cast.suggestedChanges;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
return null;
|
|
94
|
+
}
|
|
95
|
+
/* ═══════════════════ Parse ═══════════════════ */
|
|
96
|
+
/**
|
|
97
|
+
* 解析 suggestedChanges — 优先 JSON,降级为纯文本全量替换
|
|
98
|
+
*/
|
|
99
|
+
#parsePatch(raw) {
|
|
100
|
+
// 尝试 JSON 解析
|
|
101
|
+
const trimmed = raw.trim();
|
|
102
|
+
if (trimmed.startsWith('{')) {
|
|
103
|
+
try {
|
|
104
|
+
const parsed = JSON.parse(trimmed);
|
|
105
|
+
if (parsed.changes && Array.isArray(parsed.changes)) {
|
|
106
|
+
// JSON 有效但 changes 为空 → 视为无 patch
|
|
107
|
+
if (parsed.changes.length === 0) {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
return {
|
|
111
|
+
patchVersion: parsed.patchVersion ?? 1,
|
|
112
|
+
changes: parsed.changes,
|
|
113
|
+
reasoning: parsed.reasoning ?? '',
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
}
|
|
117
|
+
catch {
|
|
118
|
+
// JSON 解析失败,降级到纯文本
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
// 降级:纯文本视为 content.markdown 全量替换(适用于旧格式 Agent 输出)
|
|
122
|
+
if (trimmed.length >= 20) {
|
|
123
|
+
return {
|
|
124
|
+
patchVersion: 1,
|
|
125
|
+
changes: [
|
|
126
|
+
{
|
|
127
|
+
field: 'content.markdown',
|
|
128
|
+
action: 'replace',
|
|
129
|
+
newValue: trimmed,
|
|
130
|
+
},
|
|
131
|
+
],
|
|
132
|
+
reasoning: 'Fallback: raw text treated as content.markdown replacement',
|
|
133
|
+
};
|
|
134
|
+
}
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
137
|
+
/* ═══════════════════ Apply ═══════════════════ */
|
|
138
|
+
#applyPatch(recipe, changes) {
|
|
139
|
+
const patched = [];
|
|
140
|
+
for (const change of changes) {
|
|
141
|
+
if (!PATCHABLE_FIELDS.has(change.field)) {
|
|
142
|
+
this.#logger.warn(`[ContentPatcher] Skipping non-patchable field: ${change.field}`);
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
const success = this.#applyOneChange(recipe, change);
|
|
146
|
+
if (success) {
|
|
147
|
+
patched.push(change.field);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
return patched;
|
|
151
|
+
}
|
|
152
|
+
#applyOneChange(recipe, change) {
|
|
153
|
+
const { field, action } = change;
|
|
154
|
+
// content.* 嵌套字段
|
|
155
|
+
if (field.startsWith('content.')) {
|
|
156
|
+
return this.#applyContentChange(recipe, change);
|
|
157
|
+
}
|
|
158
|
+
// 顶层字段
|
|
159
|
+
if (field === 'sourceRefs' || field === 'headers') {
|
|
160
|
+
return this.#applyArrayChange(recipe, field, change);
|
|
161
|
+
}
|
|
162
|
+
// 简单字符串字段
|
|
163
|
+
const key = field;
|
|
164
|
+
if (action === 'replace' && change.newValue !== undefined) {
|
|
165
|
+
recipe[key] = change.newValue;
|
|
166
|
+
return true;
|
|
167
|
+
}
|
|
168
|
+
if (action === 'append' && change.newValue !== undefined) {
|
|
169
|
+
recipe[key] = `${recipe[key]}\n${change.newValue}`;
|
|
170
|
+
return true;
|
|
171
|
+
}
|
|
172
|
+
return false;
|
|
173
|
+
}
|
|
174
|
+
#applyContentChange(recipe, change) {
|
|
175
|
+
const contentObj = safeJsonParse(recipe.content, {});
|
|
176
|
+
const subField = change.field.split('.')[1]; // 'markdown' | 'rationale'
|
|
177
|
+
if (!subField) {
|
|
178
|
+
return false;
|
|
179
|
+
}
|
|
180
|
+
if (change.action === 'replace' && change.newValue !== undefined) {
|
|
181
|
+
contentObj[subField] = change.newValue;
|
|
182
|
+
recipe.content = JSON.stringify(contentObj);
|
|
183
|
+
return true;
|
|
184
|
+
}
|
|
185
|
+
if (change.action === 'replace-section' && change.section && change.newContent) {
|
|
186
|
+
const current = contentObj[subField] ?? '';
|
|
187
|
+
const updated = this.#replaceSection(current, change.section, change.newContent);
|
|
188
|
+
if (updated !== current) {
|
|
189
|
+
contentObj[subField] = updated;
|
|
190
|
+
recipe.content = JSON.stringify(contentObj);
|
|
191
|
+
return true;
|
|
192
|
+
}
|
|
193
|
+
return false;
|
|
194
|
+
}
|
|
195
|
+
if (change.action === 'append' && change.newValue !== undefined) {
|
|
196
|
+
const current = contentObj[subField] ?? '';
|
|
197
|
+
contentObj[subField] = `${current}\n${change.newValue}`;
|
|
198
|
+
recipe.content = JSON.stringify(contentObj);
|
|
199
|
+
return true;
|
|
200
|
+
}
|
|
201
|
+
return false;
|
|
202
|
+
}
|
|
203
|
+
#applyArrayChange(recipe, field, change) {
|
|
204
|
+
if (change.action === 'replace' && change.newValue !== undefined) {
|
|
205
|
+
try {
|
|
206
|
+
const newArr = JSON.parse(change.newValue);
|
|
207
|
+
if (Array.isArray(newArr)) {
|
|
208
|
+
recipe[field] = JSON.stringify(newArr);
|
|
209
|
+
return true;
|
|
210
|
+
}
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
// invalid JSON array
|
|
214
|
+
}
|
|
215
|
+
return false;
|
|
216
|
+
}
|
|
217
|
+
return false;
|
|
218
|
+
}
|
|
219
|
+
/**
|
|
220
|
+
* 替换 Markdown 中指定 section(基于标题行匹配)
|
|
221
|
+
*/
|
|
222
|
+
#replaceSection(markdown, sectionTitle, newContent) {
|
|
223
|
+
const lines = markdown.split('\n');
|
|
224
|
+
const titleLine = lines.findIndex((line) => line.trim() === sectionTitle.trim());
|
|
225
|
+
if (titleLine === -1) {
|
|
226
|
+
// Section 不存在 → 追加
|
|
227
|
+
return `${markdown}\n\n${newContent}`;
|
|
228
|
+
}
|
|
229
|
+
// 找到 section 结尾(下一个同级或更高级标题)
|
|
230
|
+
const headingLevel = (sectionTitle.match(/^#+/) ?? [''])[0].length;
|
|
231
|
+
let endLine = lines.length;
|
|
232
|
+
for (let i = titleLine + 1; i < lines.length; i++) {
|
|
233
|
+
const match = lines[i].match(/^(#+)\s/);
|
|
234
|
+
if (match && match[1].length <= headingLevel) {
|
|
235
|
+
endLine = i;
|
|
236
|
+
break;
|
|
237
|
+
}
|
|
238
|
+
}
|
|
239
|
+
const before = lines.slice(0, titleLine);
|
|
240
|
+
const after = lines.slice(endLine);
|
|
241
|
+
return [...before, newContent, ...after].join('\n');
|
|
242
|
+
}
|
|
243
|
+
/* ═══════════════════ Snapshot ═══════════════════ */
|
|
244
|
+
#createSnapshot(recipe) {
|
|
245
|
+
const contentObj = safeJsonParse(recipe.content, {});
|
|
246
|
+
return {
|
|
247
|
+
coreCode: recipe.coreCode,
|
|
248
|
+
doClause: recipe.doClause,
|
|
249
|
+
dontClause: recipe.dontClause,
|
|
250
|
+
whenClause: recipe.whenClause,
|
|
251
|
+
content: {
|
|
252
|
+
markdown: contentObj.markdown ?? undefined,
|
|
253
|
+
rationale: contentObj.rationale ?? undefined,
|
|
254
|
+
},
|
|
255
|
+
sourceRefs: safeJsonParse(recipe.sourceRefs, []),
|
|
256
|
+
headers: safeJsonParse(recipe.headers, []),
|
|
257
|
+
};
|
|
258
|
+
}
|
|
259
|
+
/* ═══════════════════ DB ═══════════════════ */
|
|
260
|
+
#getRecipe(recipeId) {
|
|
261
|
+
const row = this.#db
|
|
262
|
+
.prepare(`SELECT id, title, coreCode, doClause, dontClause, whenClause, content, sourceRefs, headers
|
|
263
|
+
FROM knowledge_entries WHERE id = ?`)
|
|
264
|
+
.get(recipeId);
|
|
265
|
+
return row ?? null;
|
|
266
|
+
}
|
|
267
|
+
#persistRecipe(recipe) {
|
|
268
|
+
this.#db
|
|
269
|
+
.prepare(`UPDATE knowledge_entries
|
|
270
|
+
SET coreCode = ?, doClause = ?, dontClause = ?, whenClause = ?,
|
|
271
|
+
content = ?, sourceRefs = ?, headers = ?, updatedAt = ?
|
|
272
|
+
WHERE id = ?`)
|
|
273
|
+
.run(recipe.coreCode, recipe.doClause, recipe.dontClause, recipe.whenClause, recipe.content, recipe.sourceRefs, recipe.headers, Date.now(), recipe.id);
|
|
274
|
+
}
|
|
275
|
+
/* ═══════════════════ Helpers ═══════════════════ */
|
|
276
|
+
#skipResult(recipeId, patchSource, reason) {
|
|
277
|
+
this.#logger.info(`[ContentPatcher] Skipped for ${recipeId}: ${reason}`);
|
|
278
|
+
const emptySnapshot = {
|
|
279
|
+
coreCode: '',
|
|
280
|
+
doClause: '',
|
|
281
|
+
dontClause: '',
|
|
282
|
+
whenClause: '',
|
|
283
|
+
content: {},
|
|
284
|
+
sourceRefs: [],
|
|
285
|
+
headers: [],
|
|
286
|
+
};
|
|
287
|
+
return {
|
|
288
|
+
success: false,
|
|
289
|
+
recipeId,
|
|
290
|
+
fieldsPatched: [],
|
|
291
|
+
beforeSnapshot: emptySnapshot,
|
|
292
|
+
afterSnapshot: emptySnapshot,
|
|
293
|
+
patchSource,
|
|
294
|
+
skipped: true,
|
|
295
|
+
skipReason: reason,
|
|
296
|
+
};
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
/* ────────────────────── Util ────────────────────── */
|
|
300
|
+
function safeJsonParse(json, fallback) {
|
|
301
|
+
if (!json) {
|
|
302
|
+
return fallback;
|
|
303
|
+
}
|
|
304
|
+
try {
|
|
305
|
+
return JSON.parse(json);
|
|
306
|
+
}
|
|
307
|
+
catch {
|
|
308
|
+
return fallback;
|
|
309
|
+
}
|
|
310
|
+
}
|
|
@@ -17,6 +17,8 @@
|
|
|
17
17
|
*/
|
|
18
18
|
import type { SignalBus } from '../../infrastructure/signal/SignalBus.js';
|
|
19
19
|
import type { ProposalRepository, ProposalType } from '../../repository/evolution/ProposalRepository.js';
|
|
20
|
+
import type { ContentPatcher } from './ContentPatcher.js';
|
|
21
|
+
import type { RecipeLifecycleSupervisor } from './RecipeLifecycleSupervisor.js';
|
|
20
22
|
interface DatabaseLike {
|
|
21
23
|
prepare(sql: string): {
|
|
22
24
|
all(...params: unknown[]): Record<string, unknown>[];
|
|
@@ -51,6 +53,8 @@ export declare class ProposalExecutor {
|
|
|
51
53
|
#private;
|
|
52
54
|
constructor(db: DatabaseLike, repo: ProposalRepository, options?: {
|
|
53
55
|
signalBus?: SignalBus;
|
|
56
|
+
contentPatcher?: ContentPatcher;
|
|
57
|
+
supervisor?: RecipeLifecycleSupervisor;
|
|
54
58
|
});
|
|
55
59
|
/**
|
|
56
60
|
* 定期调用(UiStartupTasks Stage 5)
|
|
@@ -26,11 +26,15 @@ export class ProposalExecutor {
|
|
|
26
26
|
#db;
|
|
27
27
|
#repo;
|
|
28
28
|
#signalBus;
|
|
29
|
+
#contentPatcher;
|
|
30
|
+
#supervisor;
|
|
29
31
|
#logger = Logger.getInstance();
|
|
30
32
|
constructor(db, repo, options = {}) {
|
|
31
33
|
this.#db = db;
|
|
32
34
|
this.#repo = repo;
|
|
33
35
|
this.#signalBus = options.signalBus ?? null;
|
|
36
|
+
this.#contentPatcher = options.contentPatcher ?? null;
|
|
37
|
+
this.#supervisor = options.supervisor ?? null;
|
|
34
38
|
}
|
|
35
39
|
/**
|
|
36
40
|
* 定期调用(UiStartupTasks Stage 5)
|
|
@@ -100,9 +104,22 @@ export class ProposalExecutor {
|
|
|
100
104
|
const fpOk = metrics.ruleFalsePositiveRate < 0.4;
|
|
101
105
|
const hasUsage = metrics.guardHits > 0 || metrics.searchHits > 0;
|
|
102
106
|
if (fpOk && hasUsage) {
|
|
103
|
-
// 通过 →
|
|
104
|
-
this.#transitionRecipe(proposal.targetRecipeId, '
|
|
105
|
-
this.#
|
|
107
|
+
// 通过 → evolving → ContentPatcher → staging(重走 Grace Period)
|
|
108
|
+
this.#transitionRecipe(proposal.targetRecipeId, 'evolving', 'proposal-attach', proposal.id);
|
|
109
|
+
const patchResult = this.#tryApplyPatch(proposal, 'agent-suggestion');
|
|
110
|
+
if (patchResult?.skipped || (!patchResult?.success && patchResult !== null)) {
|
|
111
|
+
// Patch 跳过或失败 → 回退到 active,避免无变更进入 staging 导致空进化循环
|
|
112
|
+
this.#transitionRecipe(proposal.targetRecipeId, 'active', 'content-patch-complete', proposal.id);
|
|
113
|
+
const skipInfo = patchResult?.skipReason ? `: ${patchResult.skipReason}` : '';
|
|
114
|
+
this.#repo.markExecuted(proposal.id, `观察期合格但 patch 未生效${skipInfo}, 回退 active`);
|
|
115
|
+
}
|
|
116
|
+
else {
|
|
117
|
+
this.#transitionRecipe(proposal.targetRecipeId, 'staging', 'content-patch-complete', proposal.id);
|
|
118
|
+
const patchInfo = patchResult?.success
|
|
119
|
+
? `, patched=[${patchResult.fieldsPatched.join(',')}]`
|
|
120
|
+
: '';
|
|
121
|
+
this.#repo.markExecuted(proposal.id, `观察期表现合格: FP=${(metrics.ruleFalsePositiveRate * 100).toFixed(0)}%, hits=${metrics.guardHits + metrics.searchHits}${patchInfo}`);
|
|
122
|
+
}
|
|
106
123
|
result.executed.push({
|
|
107
124
|
id: proposal.id,
|
|
108
125
|
type: proposal.type,
|
|
@@ -151,7 +168,7 @@ export class ProposalExecutor {
|
|
|
151
168
|
const newUsage = newMetrics.guardHits + newMetrics.searchHits;
|
|
152
169
|
if (newUsage >= oldUsage * 0.5 || oldUsage === 0) {
|
|
153
170
|
// 新 Recipe 表现足够 → 旧 Recipe → decaying,建立 deprecated_by
|
|
154
|
-
this.#transitionRecipe(proposal.targetRecipeId, 'decaying');
|
|
171
|
+
this.#transitionRecipe(proposal.targetRecipeId, 'decaying', 'proposal-execution', proposal.id);
|
|
155
172
|
this.#createDeprecatedByEdge(newRecipeId, proposal.targetRecipeId);
|
|
156
173
|
this.#repo.markExecuted(proposal.id, `supersede executed: new usage=${newUsage}, old usage=${oldUsage}`);
|
|
157
174
|
result.executed.push({
|
|
@@ -192,12 +209,12 @@ export class ProposalExecutor {
|
|
|
192
209
|
// 无回升 → 根据 decayScore 决定操作
|
|
193
210
|
if (currentDecay <= 19) {
|
|
194
211
|
// 死亡 → 直接 deprecated
|
|
195
|
-
this.#transitionRecipe(proposal.targetRecipeId, 'deprecated');
|
|
212
|
+
this.#transitionRecipe(proposal.targetRecipeId, 'deprecated', 'proposal-execution', proposal.id);
|
|
196
213
|
this.#repo.markExecuted(proposal.id, `deprecated (dead): decayScore=${currentDecay}`);
|
|
197
214
|
}
|
|
198
215
|
else if (currentDecay <= 40) {
|
|
199
216
|
// 严重 → decaying
|
|
200
|
-
this.#transitionRecipe(proposal.targetRecipeId, 'decaying');
|
|
217
|
+
this.#transitionRecipe(proposal.targetRecipeId, 'decaying', 'proposal-execution', proposal.id);
|
|
201
218
|
this.#repo.markExecuted(proposal.id, `decaying (severe): decayScore=${currentDecay}`);
|
|
202
219
|
}
|
|
203
220
|
else {
|
|
@@ -221,11 +238,24 @@ export class ProposalExecutor {
|
|
|
221
238
|
}
|
|
222
239
|
/* ── correction ── */
|
|
223
240
|
#executeCorrection(proposal, metrics, result) {
|
|
224
|
-
// correction 低风险,到期直接执行(Recipe → staging 重新审核)
|
|
241
|
+
// correction 低风险,到期直接执行(Recipe → evolving → patch → staging 重新审核)
|
|
225
242
|
const hasUsage = metrics.guardHits > 0 || metrics.searchHits > 0;
|
|
226
243
|
if (hasUsage) {
|
|
227
|
-
this.#transitionRecipe(proposal.targetRecipeId, '
|
|
228
|
-
this.#
|
|
244
|
+
this.#transitionRecipe(proposal.targetRecipeId, 'evolving', 'proposal-attach', proposal.id);
|
|
245
|
+
const patchResult = this.#tryApplyPatch(proposal, 'correction');
|
|
246
|
+
if (patchResult?.skipped || (!patchResult?.success && patchResult !== null)) {
|
|
247
|
+
// Patch 跳过或失败 → 回退到 active
|
|
248
|
+
this.#transitionRecipe(proposal.targetRecipeId, 'active', 'content-patch-complete', proposal.id);
|
|
249
|
+
const skipInfo = patchResult?.skipReason ? `: ${patchResult.skipReason}` : '';
|
|
250
|
+
this.#repo.markExecuted(proposal.id, `correction patch 未生效${skipInfo}, 回退 active`);
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
this.#transitionRecipe(proposal.targetRecipeId, 'staging', 'content-patch-complete', proposal.id);
|
|
254
|
+
const patchInfo = patchResult?.success
|
|
255
|
+
? `, patched=[${patchResult.fieldsPatched.join(',')}]`
|
|
256
|
+
: '';
|
|
257
|
+
this.#repo.markExecuted(proposal.id, `correction applied, recipe → evolving → staging for re-review${patchInfo}`);
|
|
258
|
+
}
|
|
229
259
|
result.executed.push({
|
|
230
260
|
id: proposal.id,
|
|
231
261
|
type: proposal.type,
|
|
@@ -306,10 +336,28 @@ export class ProposalExecutor {
|
|
|
306
336
|
.get(recipeId);
|
|
307
337
|
return row ?? null;
|
|
308
338
|
}
|
|
309
|
-
#transitionRecipe(recipeId, newLifecycle) {
|
|
310
|
-
this.#
|
|
311
|
-
|
|
312
|
-
|
|
339
|
+
#transitionRecipe(recipeId, newLifecycle, trigger = 'proposal-execution', proposalId) {
|
|
340
|
+
if (this.#supervisor) {
|
|
341
|
+
const result = this.#supervisor.transition({
|
|
342
|
+
recipeId,
|
|
343
|
+
targetState: newLifecycle,
|
|
344
|
+
trigger,
|
|
345
|
+
proposalId,
|
|
346
|
+
operatorId: 'system',
|
|
347
|
+
});
|
|
348
|
+
if (!result.success) {
|
|
349
|
+
this.#logger.warn(`[ProposalExecutor] Supervisor rejected transition ${recipeId} → ${newLifecycle}: ${result.error}`);
|
|
350
|
+
// Fallback to raw DB update for backward compatibility
|
|
351
|
+
this.#db
|
|
352
|
+
.prepare(`UPDATE knowledge_entries SET lifecycle = ?, updatedAt = ? WHERE id = ?`)
|
|
353
|
+
.run(newLifecycle, Date.now(), recipeId);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
else {
|
|
357
|
+
this.#db
|
|
358
|
+
.prepare(`UPDATE knowledge_entries SET lifecycle = ?, updatedAt = ? WHERE id = ?`)
|
|
359
|
+
.run(newLifecycle, Date.now(), recipeId);
|
|
360
|
+
}
|
|
313
361
|
}
|
|
314
362
|
#restoreRecipe(recipeId) {
|
|
315
363
|
// 恢复到 active(evolving/decaying → active)
|
|
@@ -318,6 +366,22 @@ export class ProposalExecutor {
|
|
|
318
366
|
this.#transitionRecipe(recipeId, 'active');
|
|
319
367
|
}
|
|
320
368
|
}
|
|
369
|
+
/**
|
|
370
|
+
* 尝试通过 ContentPatcher 应用 Proposal 中的 suggestedChanges
|
|
371
|
+
* 降级容忍:无 ContentPatcher 或 patch 失败时返回 null/skipped,不阻塞状态转移
|
|
372
|
+
*/
|
|
373
|
+
#tryApplyPatch(proposal, patchSource) {
|
|
374
|
+
if (!this.#contentPatcher) {
|
|
375
|
+
return null;
|
|
376
|
+
}
|
|
377
|
+
try {
|
|
378
|
+
return this.#contentPatcher.applyProposal(proposal, patchSource);
|
|
379
|
+
}
|
|
380
|
+
catch (err) {
|
|
381
|
+
this.#logger.warn(`[ProposalExecutor] ContentPatcher failed for proposal ${proposal.id}: ${err instanceof Error ? err.message : String(err)}`);
|
|
382
|
+
return null;
|
|
383
|
+
}
|
|
384
|
+
}
|
|
321
385
|
#createDeprecatedByEdge(newRecipeId, oldRecipeId) {
|
|
322
386
|
const now = Date.now();
|
|
323
387
|
try {
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RecipeLifecycleSupervisor — 统一状态转移管理层
|
|
3
|
+
*
|
|
4
|
+
* 核心职责:
|
|
5
|
+
* 1. Guard 前置检查 — 验证转移是否合法(VALID_TRANSITIONS + 扩展条件)
|
|
6
|
+
* 2. Entry/Exit Actions — 进入/离开状态的固定副作用
|
|
7
|
+
* 3. Event 记录 — 每次转移记录为不可变 TransitionEvent
|
|
8
|
+
* 4. 超时监控 — 中间态超时自动处理
|
|
9
|
+
* 5. 健康摘要 — 全局状态分布与卡死检测
|
|
10
|
+
*
|
|
11
|
+
* 所有状态变更建议通过 Supervisor,目前作为可选增强层存在。
|
|
12
|
+
* 不改变现有 ProposalExecutor/StagingManager 的内部逻辑,
|
|
13
|
+
* 而是在它们之上提供审计、超时检查和健康监控。
|
|
14
|
+
*
|
|
15
|
+
* 触发时机:UiStartupTasks Stage 6(新增)
|
|
16
|
+
*
|
|
17
|
+
* @module service/evolution/RecipeLifecycleSupervisor
|
|
18
|
+
*/
|
|
19
|
+
import type { SignalBus } from '../../infrastructure/signal/SignalBus.js';
|
|
20
|
+
import type { LifecycleHealthSummary, TimeoutCheckResult, TransitionEvent, TransitionRequest, TransitionResult } from '../../types/evolution.js';
|
|
21
|
+
interface DatabaseLike {
|
|
22
|
+
prepare(sql: string): {
|
|
23
|
+
all(...params: unknown[]): Record<string, unknown>[];
|
|
24
|
+
get(...params: unknown[]): Record<string, unknown> | undefined;
|
|
25
|
+
run(...params: unknown[]): {
|
|
26
|
+
changes: number;
|
|
27
|
+
};
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export declare class RecipeLifecycleSupervisor {
|
|
31
|
+
#private;
|
|
32
|
+
constructor(db: DatabaseLike, options?: {
|
|
33
|
+
signalBus?: SignalBus;
|
|
34
|
+
});
|
|
35
|
+
/**
|
|
36
|
+
* 执行状态转移 — 统一入口
|
|
37
|
+
*
|
|
38
|
+
* 1. 获取当前状态
|
|
39
|
+
* 2. Guard 检查(合法转移 + 扩展条件)
|
|
40
|
+
* 3. Exit Action(离开旧状态)
|
|
41
|
+
* 4. 更新 lifecycle
|
|
42
|
+
* 5. Entry Action(进入新状态)
|
|
43
|
+
* 6. 记录 TransitionEvent
|
|
44
|
+
* 7. 发射信号
|
|
45
|
+
*/
|
|
46
|
+
transition(request: TransitionRequest): TransitionResult;
|
|
47
|
+
/**
|
|
48
|
+
* 检查中间态超时 + 自动处理
|
|
49
|
+
*
|
|
50
|
+
* 处理范围:
|
|
51
|
+
* - evolving > 7d → active(回退)
|
|
52
|
+
* - decaying > 30d → deprecated
|
|
53
|
+
*/
|
|
54
|
+
checkTimeouts(): TimeoutCheckResult;
|
|
55
|
+
/**
|
|
56
|
+
* 查询 Recipe 的转移历史
|
|
57
|
+
*/
|
|
58
|
+
getTransitionHistory(recipeId: string, limit?: number): TransitionEvent[];
|
|
59
|
+
/**
|
|
60
|
+
* 获取全局状态健康摘要
|
|
61
|
+
*/
|
|
62
|
+
getHealthSummary(): LifecycleHealthSummary;
|
|
63
|
+
}
|
|
64
|
+
export {};
|