autosnippet 3.3.4 → 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/README.md +174 -83
- package/config/constitution.yaml +2 -0
- 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.d.ts +5 -1
- package/dist/lib/cli/KnowledgeSyncService.js +6 -3
- 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/domain/knowledge/values/Stats.d.ts +1 -1
- package/dist/lib/domain/knowledge/values/Stats.js +2 -2
- 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 +115 -162
- 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 +60 -3
- 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/InfraModule.js +4 -1
- package/dist/lib/injection/modules/KnowledgeModule.js +38 -1
- package/dist/lib/repository/evolution/ProposalRepository.d.ts +99 -0
- package/dist/lib/repository/evolution/ProposalRepository.js +255 -0
- 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 +22 -4
- package/dist/lib/service/bootstrap/UiStartupTasks.js +73 -5
- 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/DecayDetector.d.ts +4 -3
- package/dist/lib/service/evolution/DecayDetector.js +97 -22
- package/dist/lib/service/evolution/KnowledgeMetabolism.d.ts +4 -2
- package/dist/lib/service/evolution/KnowledgeMetabolism.js +29 -2
- package/dist/lib/service/evolution/ProposalExecutor.d.ts +66 -0
- package/dist/lib/service/evolution/ProposalExecutor.js +424 -0
- 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/StagingManager.js +5 -3
- 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/guard/GuardCrossFileChecks.js +2 -0
- package/dist/lib/service/guard/ReverseGuard.d.ts +1 -1
- package/dist/lib/service/guard/ReverseGuard.js +32 -2
- 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/knowledge/SourceRefReconciler.d.ts +2 -0
- package/dist/lib/service/knowledge/SourceRefReconciler.js +48 -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 +33 -2
- package/dist/lib/shared/schemas/mcp-tools.js +42 -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,458 @@
|
|
|
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 { randomUUID } from 'node:crypto';
|
|
20
|
+
import { isValidTransition } from '../../domain/knowledge/Lifecycle.js';
|
|
21
|
+
import Logger from '../../infrastructure/logging/Logger.js';
|
|
22
|
+
/* ────────────────────── Constants ────────────────────── */
|
|
23
|
+
/** 中间态超时配置(毫秒) */
|
|
24
|
+
const TIMEOUT_MS = {
|
|
25
|
+
evolving: 7 * 24 * 60 * 60 * 1000, // 7 天
|
|
26
|
+
decaying: 30 * 24 * 60 * 60 * 1000, // 30 天
|
|
27
|
+
staging: 7 * 24 * 60 * 60 * 1000, // 7 天(安全兜底,正常由 StagingManager 处理)
|
|
28
|
+
pending: 30 * 24 * 60 * 60 * 1000, // 30 天
|
|
29
|
+
};
|
|
30
|
+
/** 超时后的目标状态 */
|
|
31
|
+
const TIMEOUT_TARGET = {
|
|
32
|
+
evolving: 'active', // 回退到 active(内容不变)
|
|
33
|
+
decaying: 'deprecated', // 长期衰退 → 废弃
|
|
34
|
+
pending: 'deprecated', // 30 天未审核 → 废弃
|
|
35
|
+
};
|
|
36
|
+
/** 卡死告警阈值(毫秒) */
|
|
37
|
+
const STUCK_THRESHOLD_MS = {
|
|
38
|
+
evolving: 3 * 24 * 60 * 60 * 1000, // > 3天
|
|
39
|
+
decaying: 15 * 24 * 60 * 60 * 1000, // > 15天
|
|
40
|
+
staging: 3 * 24 * 60 * 60 * 1000, // > 3天
|
|
41
|
+
pending: 7 * 24 * 60 * 60 * 1000, // > 7天
|
|
42
|
+
};
|
|
43
|
+
/* ────────────────────── Entry/Exit Action Types ────────────────────── */
|
|
44
|
+
/** 进入状态时写入 stats 的元数据键 */
|
|
45
|
+
const ENTRY_META_KEYS = {
|
|
46
|
+
staging: 'stagingEnteredAt',
|
|
47
|
+
evolving: 'evolvingStartedAt',
|
|
48
|
+
decaying: 'decayStartedAt',
|
|
49
|
+
active: 'activeSince',
|
|
50
|
+
};
|
|
51
|
+
/* ────────────────────── Class ────────────────────── */
|
|
52
|
+
export class RecipeLifecycleSupervisor {
|
|
53
|
+
#db;
|
|
54
|
+
#signalBus;
|
|
55
|
+
#logger = Logger.getInstance();
|
|
56
|
+
constructor(db, options = {}) {
|
|
57
|
+
this.#db = db;
|
|
58
|
+
this.#signalBus = options.signalBus ?? null;
|
|
59
|
+
}
|
|
60
|
+
/* ═══════════════════ Core Transition ═══════════════════ */
|
|
61
|
+
/**
|
|
62
|
+
* 执行状态转移 — 统一入口
|
|
63
|
+
*
|
|
64
|
+
* 1. 获取当前状态
|
|
65
|
+
* 2. Guard 检查(合法转移 + 扩展条件)
|
|
66
|
+
* 3. Exit Action(离开旧状态)
|
|
67
|
+
* 4. 更新 lifecycle
|
|
68
|
+
* 5. Entry Action(进入新状态)
|
|
69
|
+
* 6. 记录 TransitionEvent
|
|
70
|
+
* 7. 发射信号
|
|
71
|
+
*/
|
|
72
|
+
transition(request) {
|
|
73
|
+
const { recipeId, targetState, trigger, evidence, proposalId, operatorId } = request;
|
|
74
|
+
const opId = operatorId ?? 'system';
|
|
75
|
+
// 1. 获取当前状态
|
|
76
|
+
const current = this.#getRecipeState(recipeId);
|
|
77
|
+
if (!current) {
|
|
78
|
+
return {
|
|
79
|
+
success: false,
|
|
80
|
+
fromState: 'unknown',
|
|
81
|
+
toState: targetState,
|
|
82
|
+
error: 'Recipe not found',
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
const fromState = current.lifecycle;
|
|
86
|
+
// 2. Guard 检查
|
|
87
|
+
if (!isValidTransition(fromState, targetState)) {
|
|
88
|
+
this.#logger.warn(`[Supervisor] Invalid transition: ${recipeId} ${fromState} → ${targetState} (trigger: ${trigger})`);
|
|
89
|
+
return {
|
|
90
|
+
success: false,
|
|
91
|
+
fromState,
|
|
92
|
+
toState: targetState,
|
|
93
|
+
error: `Invalid transition: ${fromState} → ${targetState}`,
|
|
94
|
+
};
|
|
95
|
+
}
|
|
96
|
+
// 3. Exit Action
|
|
97
|
+
this.#executeExitAction(recipeId, fromState);
|
|
98
|
+
// 4. 更新 lifecycle
|
|
99
|
+
const now = Date.now();
|
|
100
|
+
this.#db
|
|
101
|
+
.prepare(`UPDATE knowledge_entries SET lifecycle = ?, updatedAt = ? WHERE id = ?`)
|
|
102
|
+
.run(targetState, now, recipeId);
|
|
103
|
+
// 5. Entry Action
|
|
104
|
+
this.#executeEntryAction(recipeId, targetState, now, proposalId);
|
|
105
|
+
// 6. 记录 TransitionEvent
|
|
106
|
+
const event = this.#recordEvent({
|
|
107
|
+
recipeId,
|
|
108
|
+
fromState,
|
|
109
|
+
toState: targetState,
|
|
110
|
+
trigger,
|
|
111
|
+
evidence: evidence ?? null,
|
|
112
|
+
proposalId: proposalId ?? null,
|
|
113
|
+
operatorId: opId,
|
|
114
|
+
createdAt: now,
|
|
115
|
+
});
|
|
116
|
+
// 7. 发射信号
|
|
117
|
+
this.#emitSignal(recipeId, fromState, targetState, trigger);
|
|
118
|
+
this.#logger.info(`[Supervisor] ${recipeId}: ${fromState} → ${targetState} (trigger: ${trigger})`);
|
|
119
|
+
return { success: true, fromState, toState: targetState, event };
|
|
120
|
+
}
|
|
121
|
+
/* ═══════════════════ Timeout Check ═══════════════════ */
|
|
122
|
+
/**
|
|
123
|
+
* 检查中间态超时 + 自动处理
|
|
124
|
+
*
|
|
125
|
+
* 处理范围:
|
|
126
|
+
* - evolving > 7d → active(回退)
|
|
127
|
+
* - decaying > 30d → deprecated
|
|
128
|
+
*/
|
|
129
|
+
checkTimeouts() {
|
|
130
|
+
const result = { timedOut: [], checked: 0 };
|
|
131
|
+
const now = Date.now();
|
|
132
|
+
for (const [state, timeoutMs] of Object.entries(TIMEOUT_MS)) {
|
|
133
|
+
if (!(state in TIMEOUT_TARGET)) {
|
|
134
|
+
continue;
|
|
135
|
+
}
|
|
136
|
+
const targetState = TIMEOUT_TARGET[state];
|
|
137
|
+
const rows = this.#db
|
|
138
|
+
.prepare(`SELECT id, stats FROM knowledge_entries WHERE lifecycle = ?`)
|
|
139
|
+
.all(state);
|
|
140
|
+
result.checked += rows.length;
|
|
141
|
+
for (const row of rows) {
|
|
142
|
+
const stats = safeJsonParse(row.stats, {});
|
|
143
|
+
const entryKey = ENTRY_META_KEYS[state];
|
|
144
|
+
const enteredAt = (entryKey ? stats[entryKey] : null);
|
|
145
|
+
// 用 updatedAt 作为 fallback
|
|
146
|
+
const stateAge = enteredAt ? now - enteredAt : this.#getRecipeAge(row.id, now);
|
|
147
|
+
if (stateAge > timeoutMs) {
|
|
148
|
+
const transitionResult = this.transition({
|
|
149
|
+
recipeId: row.id,
|
|
150
|
+
targetState,
|
|
151
|
+
trigger: 'timeout-recovery',
|
|
152
|
+
evidence: {
|
|
153
|
+
reason: `${state} timeout after ${Math.round(stateAge / (24 * 60 * 60 * 1000))}d`,
|
|
154
|
+
},
|
|
155
|
+
});
|
|
156
|
+
if (transitionResult.success) {
|
|
157
|
+
result.timedOut.push({
|
|
158
|
+
recipeId: row.id,
|
|
159
|
+
fromState: state,
|
|
160
|
+
toState: targetState,
|
|
161
|
+
age: stateAge,
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
}
|
|
167
|
+
if (result.timedOut.length > 0) {
|
|
168
|
+
this.#logger.info(`[Supervisor] Timeout check: ${result.timedOut.length} recipes timed out (checked: ${result.checked})`);
|
|
169
|
+
}
|
|
170
|
+
return result;
|
|
171
|
+
}
|
|
172
|
+
/* ═══════════════════ Query ═══════════════════ */
|
|
173
|
+
/**
|
|
174
|
+
* 查询 Recipe 的转移历史
|
|
175
|
+
*/
|
|
176
|
+
getTransitionHistory(recipeId, limit = 50) {
|
|
177
|
+
try {
|
|
178
|
+
const rows = this.#db
|
|
179
|
+
.prepare(`SELECT id, recipe_id, from_state, to_state, trigger, operator_id,
|
|
180
|
+
evidence_json, proposal_id, created_at
|
|
181
|
+
FROM lifecycle_transition_events
|
|
182
|
+
WHERE recipe_id = ?
|
|
183
|
+
ORDER BY created_at DESC
|
|
184
|
+
LIMIT ?`)
|
|
185
|
+
.all(recipeId, limit);
|
|
186
|
+
return rows.map((row) => this.#rowToEvent(row));
|
|
187
|
+
}
|
|
188
|
+
catch {
|
|
189
|
+
// 表可能不存在(migration 未运行)
|
|
190
|
+
return [];
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
/**
|
|
194
|
+
* 获取全局状态健康摘要
|
|
195
|
+
*/
|
|
196
|
+
getHealthSummary() {
|
|
197
|
+
const now = Date.now();
|
|
198
|
+
// 状态分布
|
|
199
|
+
const stateDistribution = this.#getStateDistribution();
|
|
200
|
+
// 中间态卡死检测
|
|
201
|
+
const intermediateStates = {
|
|
202
|
+
stuckEvolving: this.#getStuckInfo('evolving', STUCK_THRESHOLD_MS.evolving, now),
|
|
203
|
+
stuckDecaying: this.#getStuckInfo('decaying', STUCK_THRESHOLD_MS.decaying, now),
|
|
204
|
+
stuckStaging: this.#getStuckInfo('staging', STUCK_THRESHOLD_MS.staging, now),
|
|
205
|
+
stuckPending: this.#getStuckInfo('pending', STUCK_THRESHOLD_MS.pending, now),
|
|
206
|
+
};
|
|
207
|
+
// 最近转移统计
|
|
208
|
+
const recentTransitions = this.#getRecentTransitionStats(now);
|
|
209
|
+
// Proposal 指标
|
|
210
|
+
const proposalMetrics = this.#getProposalMetrics();
|
|
211
|
+
return { stateDistribution, intermediateStates, recentTransitions, proposalMetrics };
|
|
212
|
+
}
|
|
213
|
+
/* ═══════════════════ Entry/Exit Actions ═══════════════════ */
|
|
214
|
+
#executeEntryAction(recipeId, state, now, proposalId) {
|
|
215
|
+
const metaKey = ENTRY_META_KEYS[state];
|
|
216
|
+
if (!metaKey) {
|
|
217
|
+
return;
|
|
218
|
+
}
|
|
219
|
+
const statsRow = this.#db
|
|
220
|
+
.prepare(`SELECT stats FROM knowledge_entries WHERE id = ?`)
|
|
221
|
+
.get(recipeId);
|
|
222
|
+
const stats = safeJsonParse(statsRow?.stats, {});
|
|
223
|
+
stats[metaKey] = now;
|
|
224
|
+
if (state === 'evolving' && proposalId) {
|
|
225
|
+
stats.evolvingProposalId = proposalId;
|
|
226
|
+
}
|
|
227
|
+
if (state === 'active') {
|
|
228
|
+
// 清除中间态元数据
|
|
229
|
+
delete stats.evolvingStartedAt;
|
|
230
|
+
delete stats.evolvingProposalId;
|
|
231
|
+
delete stats.decayStartedAt;
|
|
232
|
+
}
|
|
233
|
+
if (state === 'deprecated') {
|
|
234
|
+
stats.deprecatedAt = now;
|
|
235
|
+
}
|
|
236
|
+
this.#db
|
|
237
|
+
.prepare(`UPDATE knowledge_entries SET stats = ? WHERE id = ?`)
|
|
238
|
+
.run(JSON.stringify(stats), recipeId);
|
|
239
|
+
}
|
|
240
|
+
#executeExitAction(recipeId, state) {
|
|
241
|
+
if (state === 'active') {
|
|
242
|
+
// 记录 lastActiveAt
|
|
243
|
+
const statsRow = this.#db
|
|
244
|
+
.prepare(`SELECT stats FROM knowledge_entries WHERE id = ?`)
|
|
245
|
+
.get(recipeId);
|
|
246
|
+
const stats = safeJsonParse(statsRow?.stats, {});
|
|
247
|
+
stats.lastActiveAt = Date.now();
|
|
248
|
+
this.#db
|
|
249
|
+
.prepare(`UPDATE knowledge_entries SET stats = ? WHERE id = ?`)
|
|
250
|
+
.run(JSON.stringify(stats), recipeId);
|
|
251
|
+
}
|
|
252
|
+
// staging exit: 清除 staging 元数据(由 StagingManager 自行处理)
|
|
253
|
+
}
|
|
254
|
+
/* ═══════════════════ Event Recording ═══════════════════ */
|
|
255
|
+
#recordEvent(params) {
|
|
256
|
+
const id = randomUUID();
|
|
257
|
+
const event = {
|
|
258
|
+
id,
|
|
259
|
+
recipeId: params.recipeId,
|
|
260
|
+
fromState: params.fromState,
|
|
261
|
+
toState: params.toState,
|
|
262
|
+
trigger: params.trigger,
|
|
263
|
+
evidence: params.evidence,
|
|
264
|
+
proposalId: params.proposalId,
|
|
265
|
+
operatorId: params.operatorId,
|
|
266
|
+
createdAt: params.createdAt,
|
|
267
|
+
};
|
|
268
|
+
try {
|
|
269
|
+
this.#db
|
|
270
|
+
.prepare(`INSERT INTO lifecycle_transition_events
|
|
271
|
+
(id, recipe_id, from_state, to_state, trigger, operator_id, evidence_json, proposal_id, created_at)
|
|
272
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)`)
|
|
273
|
+
.run(id, params.recipeId, params.fromState, params.toState, params.trigger, params.operatorId, params.evidence ? JSON.stringify(params.evidence) : null, params.proposalId, params.createdAt);
|
|
274
|
+
}
|
|
275
|
+
catch {
|
|
276
|
+
// lifecycle_transition_events 表可能不存在(降级容忍)
|
|
277
|
+
this.#logger.warn(`[Supervisor] Failed to record transition event (table may not exist)`);
|
|
278
|
+
}
|
|
279
|
+
return event;
|
|
280
|
+
}
|
|
281
|
+
/* ═══════════════════ Health Queries ═══════════════════ */
|
|
282
|
+
#getStateDistribution() {
|
|
283
|
+
const dist = {
|
|
284
|
+
pending: 0,
|
|
285
|
+
staging: 0,
|
|
286
|
+
active: 0,
|
|
287
|
+
evolving: 0,
|
|
288
|
+
decaying: 0,
|
|
289
|
+
deprecated: 0,
|
|
290
|
+
};
|
|
291
|
+
try {
|
|
292
|
+
const rows = this.#db
|
|
293
|
+
.prepare(`SELECT lifecycle, COUNT(*) as cnt FROM knowledge_entries GROUP BY lifecycle`)
|
|
294
|
+
.all();
|
|
295
|
+
for (const row of rows) {
|
|
296
|
+
dist[row.lifecycle] = row.cnt;
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
catch {
|
|
300
|
+
// fallback
|
|
301
|
+
}
|
|
302
|
+
return dist;
|
|
303
|
+
}
|
|
304
|
+
#getStuckInfo(state, thresholdMs, now) {
|
|
305
|
+
try {
|
|
306
|
+
const rows = this.#db
|
|
307
|
+
.prepare(`SELECT id, stats, updatedAt FROM knowledge_entries WHERE lifecycle = ?`)
|
|
308
|
+
.all(state);
|
|
309
|
+
let count = 0;
|
|
310
|
+
let oldestAge = 0;
|
|
311
|
+
for (const row of rows) {
|
|
312
|
+
const stats = safeJsonParse(row.stats, {});
|
|
313
|
+
const metaKey = ENTRY_META_KEYS[state];
|
|
314
|
+
const enteredAt = (metaKey ? stats[metaKey] : null);
|
|
315
|
+
const age = enteredAt ? now - enteredAt : now - (row.updatedAt || now);
|
|
316
|
+
if (age > thresholdMs) {
|
|
317
|
+
count++;
|
|
318
|
+
if (age > oldestAge) {
|
|
319
|
+
oldestAge = age;
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
return { count, oldestAge };
|
|
324
|
+
}
|
|
325
|
+
catch {
|
|
326
|
+
return { count: 0, oldestAge: 0 };
|
|
327
|
+
}
|
|
328
|
+
}
|
|
329
|
+
#getRecentTransitionStats(now) {
|
|
330
|
+
try {
|
|
331
|
+
const last24hCount = this.#db
|
|
332
|
+
.prepare(`SELECT COUNT(*) as cnt FROM lifecycle_transition_events WHERE created_at > ?`)
|
|
333
|
+
.get(now - 24 * 60 * 60 * 1000)?.cnt ?? 0;
|
|
334
|
+
const last7dCount = this.#db
|
|
335
|
+
.prepare(`SELECT COUNT(*) as cnt FROM lifecycle_transition_events WHERE created_at > ?`)
|
|
336
|
+
.get(now - 7 * 24 * 60 * 60 * 1000)?.cnt ?? 0;
|
|
337
|
+
const triggerRows = this.#db
|
|
338
|
+
.prepare(`SELECT trigger, COUNT(*) as cnt
|
|
339
|
+
FROM lifecycle_transition_events
|
|
340
|
+
WHERE created_at > ?
|
|
341
|
+
GROUP BY trigger
|
|
342
|
+
ORDER BY cnt DESC
|
|
343
|
+
LIMIT 5`)
|
|
344
|
+
.all(now - 7 * 24 * 60 * 60 * 1000);
|
|
345
|
+
return {
|
|
346
|
+
last24h: last24hCount,
|
|
347
|
+
last7d: last7dCount,
|
|
348
|
+
topTriggers: triggerRows.map((r) => ({ trigger: r.trigger, count: r.cnt })),
|
|
349
|
+
};
|
|
350
|
+
}
|
|
351
|
+
catch {
|
|
352
|
+
return { last24h: 0, last7d: 0, topTriggers: [] };
|
|
353
|
+
}
|
|
354
|
+
}
|
|
355
|
+
#getProposalMetrics() {
|
|
356
|
+
try {
|
|
357
|
+
const statusCounts = this.#db
|
|
358
|
+
.prepare(`SELECT status, COUNT(*) as cnt FROM evolution_proposals GROUP BY status`)
|
|
359
|
+
.all();
|
|
360
|
+
const map = {};
|
|
361
|
+
for (const row of statusCounts) {
|
|
362
|
+
map[row.status] = row.cnt;
|
|
363
|
+
}
|
|
364
|
+
const pending = map.pending ?? 0;
|
|
365
|
+
const observing = map.observing ?? 0;
|
|
366
|
+
const executed = map.executed ?? 0;
|
|
367
|
+
const rejected = map.rejected ?? 0;
|
|
368
|
+
const expired = map.expired ?? 0;
|
|
369
|
+
const total = executed + rejected + expired;
|
|
370
|
+
// contentPatchRate: 有 patch 的事件 / 总 proposal-execution 事件
|
|
371
|
+
let contentPatchRate = 0;
|
|
372
|
+
try {
|
|
373
|
+
const patchEvents = this.#db
|
|
374
|
+
.prepare(`SELECT COUNT(*) as cnt FROM lifecycle_transition_events
|
|
375
|
+
WHERE trigger = 'content-patch-complete'`)
|
|
376
|
+
.get();
|
|
377
|
+
const execEvents = this.#db
|
|
378
|
+
.prepare(`SELECT COUNT(*) as cnt FROM lifecycle_transition_events
|
|
379
|
+
WHERE trigger = 'proposal-execution' OR trigger = 'proposal-attach'`)
|
|
380
|
+
.get();
|
|
381
|
+
const patchCount = patchEvents?.cnt ?? 0;
|
|
382
|
+
const execCount = execEvents?.cnt ?? 0;
|
|
383
|
+
contentPatchRate = execCount > 0 ? patchCount / execCount : 0;
|
|
384
|
+
}
|
|
385
|
+
catch {
|
|
386
|
+
// table may not exist yet
|
|
387
|
+
}
|
|
388
|
+
return {
|
|
389
|
+
pendingCount: pending,
|
|
390
|
+
observingCount: observing,
|
|
391
|
+
executionRate: total > 0 ? executed / total : 0,
|
|
392
|
+
avgObservationDays: 0, // TODO: calculate from resolved proposals
|
|
393
|
+
contentPatchRate,
|
|
394
|
+
};
|
|
395
|
+
}
|
|
396
|
+
catch {
|
|
397
|
+
return {
|
|
398
|
+
pendingCount: 0,
|
|
399
|
+
observingCount: 0,
|
|
400
|
+
executionRate: 0,
|
|
401
|
+
avgObservationDays: 0,
|
|
402
|
+
contentPatchRate: 0,
|
|
403
|
+
};
|
|
404
|
+
}
|
|
405
|
+
}
|
|
406
|
+
/* ═══════════════════ DB Helpers ═══════════════════ */
|
|
407
|
+
#getRecipeState(recipeId) {
|
|
408
|
+
const row = this.#db
|
|
409
|
+
.prepare(`SELECT lifecycle FROM knowledge_entries WHERE id = ?`)
|
|
410
|
+
.get(recipeId);
|
|
411
|
+
return row ?? null;
|
|
412
|
+
}
|
|
413
|
+
#getRecipeAge(recipeId, now) {
|
|
414
|
+
const row = this.#db
|
|
415
|
+
.prepare(`SELECT updatedAt FROM knowledge_entries WHERE id = ?`)
|
|
416
|
+
.get(recipeId);
|
|
417
|
+
return row ? now - row.updatedAt : 0;
|
|
418
|
+
}
|
|
419
|
+
#rowToEvent(row) {
|
|
420
|
+
return {
|
|
421
|
+
id: row.id,
|
|
422
|
+
recipeId: row.recipe_id,
|
|
423
|
+
fromState: row.from_state,
|
|
424
|
+
toState: row.to_state,
|
|
425
|
+
trigger: row.trigger,
|
|
426
|
+
evidence: row.evidence_json ? safeJsonParse(row.evidence_json, null) : null,
|
|
427
|
+
proposalId: row.proposal_id ?? null,
|
|
428
|
+
operatorId: row.operator_id,
|
|
429
|
+
createdAt: row.created_at,
|
|
430
|
+
};
|
|
431
|
+
}
|
|
432
|
+
/* ═══════════════════ Signal ═══════════════════ */
|
|
433
|
+
#emitSignal(recipeId, fromState, toState, trigger) {
|
|
434
|
+
if (!this.#signalBus) {
|
|
435
|
+
return;
|
|
436
|
+
}
|
|
437
|
+
this.#signalBus.send('lifecycle', 'RecipeLifecycleSupervisor', 0.5, {
|
|
438
|
+
target: recipeId,
|
|
439
|
+
metadata: {
|
|
440
|
+
fromState,
|
|
441
|
+
toState,
|
|
442
|
+
trigger,
|
|
443
|
+
},
|
|
444
|
+
});
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
/* ────────────────────── Util ────────────────────── */
|
|
448
|
+
function safeJsonParse(json, fallback) {
|
|
449
|
+
if (!json) {
|
|
450
|
+
return fallback;
|
|
451
|
+
}
|
|
452
|
+
try {
|
|
453
|
+
return JSON.parse(json);
|
|
454
|
+
}
|
|
455
|
+
catch {
|
|
456
|
+
return fallback;
|
|
457
|
+
}
|
|
458
|
+
}
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* RecipeRelevanceAuditor — 基于代码证据验证 Recipe 当前相关性
|
|
3
|
+
*
|
|
4
|
+
* Rescan 时主动触发,检查每个保留 Recipe 的代码证据是否仍然存在:
|
|
5
|
+
* 1. 触发模式匹配 (trigger 引用的文件类型/路径模式是否有匹配)
|
|
6
|
+
* 2. 代码符号存活 (content.pattern 引用的类名/函数名是否在 AST 中)
|
|
7
|
+
* 3. 依赖关系完整 (涉及模块依赖是否在依赖图中)
|
|
8
|
+
* 4. 源代码文件存活 (reasoning.sources + content.codeChanges 的文件是否存在)
|
|
9
|
+
*
|
|
10
|
+
* 评分后驱动快速衰退:
|
|
11
|
+
* - healthy (80-100): 无操作
|
|
12
|
+
* - watch (60-79): 报告警告
|
|
13
|
+
* - decay (40-59): active → decaying (7d grace)
|
|
14
|
+
* - severe (20-39): active → decaying (3d grace)
|
|
15
|
+
* - dead (0-19): active → deprecated (immediate)
|
|
16
|
+
*
|
|
17
|
+
* 支持 Category 权重豁免:架构/规范类 Recipe 侧重触发模式而非具体符号。
|
|
18
|
+
*
|
|
19
|
+
* @module service/evolution/RecipeRelevanceAuditor
|
|
20
|
+
*/
|
|
21
|
+
/** Logger 接口 */
|
|
22
|
+
interface AuditorLogger {
|
|
23
|
+
info(msg: string, meta?: Record<string, unknown>): void;
|
|
24
|
+
warn(msg: string, meta?: Record<string, unknown>): void;
|
|
25
|
+
}
|
|
26
|
+
/** 知识条目快照 (来自 CleanupService.snapshotRecipes) */
|
|
27
|
+
export interface RecipeSnapshotEntry {
|
|
28
|
+
id: string;
|
|
29
|
+
title: string;
|
|
30
|
+
trigger: string;
|
|
31
|
+
category: string;
|
|
32
|
+
dimensionId?: string;
|
|
33
|
+
sourceFile?: string;
|
|
34
|
+
lifecycle: string;
|
|
35
|
+
}
|
|
36
|
+
/** Phase 1-4 分析数据 */
|
|
37
|
+
export interface AnalysisData {
|
|
38
|
+
/** 项目所有文件的相对路径 */
|
|
39
|
+
fileList: string[];
|
|
40
|
+
/** AST 解析的代码实体 (类名/函数名/协议名等) */
|
|
41
|
+
codeEntities: Array<{
|
|
42
|
+
name: string;
|
|
43
|
+
kind?: string;
|
|
44
|
+
file?: string;
|
|
45
|
+
}>;
|
|
46
|
+
/** 依赖关系图 */
|
|
47
|
+
dependencyGraph: Array<{
|
|
48
|
+
from: string;
|
|
49
|
+
to: string;
|
|
50
|
+
}>;
|
|
51
|
+
}
|
|
52
|
+
/** 单个 Recipe 的审计结果 */
|
|
53
|
+
export interface RelevanceAuditResult {
|
|
54
|
+
recipeId: string;
|
|
55
|
+
title: string;
|
|
56
|
+
relevanceScore: number;
|
|
57
|
+
verdict: 'healthy' | 'watch' | 'decay' | 'severe' | 'dead';
|
|
58
|
+
evidence: {
|
|
59
|
+
triggerStillMatches: boolean;
|
|
60
|
+
symbolsAlive: number;
|
|
61
|
+
depsIntact: boolean;
|
|
62
|
+
codeFilesExist: number;
|
|
63
|
+
};
|
|
64
|
+
decayReasons: string[];
|
|
65
|
+
}
|
|
66
|
+
/** 审计汇总 */
|
|
67
|
+
export interface RelevanceAuditSummary {
|
|
68
|
+
totalAudited: number;
|
|
69
|
+
healthy: number;
|
|
70
|
+
watch: number;
|
|
71
|
+
decay: number;
|
|
72
|
+
severe: number;
|
|
73
|
+
dead: number;
|
|
74
|
+
results: RelevanceAuditResult[];
|
|
75
|
+
proposalsCreated: number;
|
|
76
|
+
immediateDeprecated: number;
|
|
77
|
+
}
|
|
78
|
+
export declare class RecipeRelevanceAuditor {
|
|
79
|
+
#private;
|
|
80
|
+
constructor(opts: {
|
|
81
|
+
db: unknown;
|
|
82
|
+
logger?: AuditorLogger;
|
|
83
|
+
});
|
|
84
|
+
/**
|
|
85
|
+
* 审计所有保留 Recipe 的代码证据
|
|
86
|
+
*/
|
|
87
|
+
audit(recipes: RecipeSnapshotEntry[], analysisData: AnalysisData): Promise<RelevanceAuditSummary>;
|
|
88
|
+
}
|
|
89
|
+
export {};
|