autosnippet 3.3.5 → 3.3.7
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-FHns2ypa.js +1 -0
- package/dashboard/dist/assets/index-BRJv5Y3r.js +135 -0
- package/dashboard/dist/assets/index-DzoB7kxK.css +1 -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 +137 -9
- package/dist/lib/agent/AgentFactory.d.ts +0 -17
- package/dist/lib/agent/AgentFactory.js +1 -25
- package/dist/lib/agent/AgentRuntime.d.ts +2 -2
- package/dist/lib/agent/AgentRuntime.js +26 -18
- 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/ChatAgentTasks.js +4 -0
- 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/forced-summary.js +7 -2
- 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 +5 -5
- package/dist/lib/cli/KnowledgeSyncService.js +1 -1
- package/dist/lib/core/AstAnalyzer.d.ts +1 -0
- package/dist/lib/core/discovery/ConfigWatcher.d.ts +64 -0
- package/dist/lib/core/discovery/ConfigWatcher.js +336 -0
- package/dist/lib/core/discovery/CustomConfigDiscoverer.d.ts +30 -0
- package/dist/lib/core/discovery/CustomConfigDiscoverer.js +1305 -0
- package/dist/lib/core/discovery/DiscovererPreference.d.ts +44 -0
- package/dist/lib/core/discovery/DiscovererPreference.js +141 -0
- package/dist/lib/core/discovery/DiscovererRegistry.d.ts +10 -1
- package/dist/lib/core/discovery/DiscovererRegistry.js +42 -2
- package/dist/lib/core/discovery/ProjectDiscoverer.d.ts +19 -0
- package/dist/lib/core/discovery/index.d.ts +2 -0
- package/dist/lib/core/discovery/index.js +4 -0
- package/dist/lib/core/discovery/parsers/CMakeParser.d.ts +32 -0
- package/dist/lib/core/discovery/parsers/CMakeParser.js +148 -0
- package/dist/lib/core/discovery/parsers/GradleDslParser.d.ts +43 -0
- package/dist/lib/core/discovery/parsers/GradleDslParser.js +171 -0
- package/dist/lib/core/discovery/parsers/JsonConfigParser.d.ts +45 -0
- package/dist/lib/core/discovery/parsers/JsonConfigParser.js +122 -0
- package/dist/lib/core/discovery/parsers/RubyDslParser.d.ts +49 -0
- package/dist/lib/core/discovery/parsers/RubyDslParser.js +282 -0
- package/dist/lib/core/discovery/parsers/StarlarkParser.d.ts +33 -0
- package/dist/lib/core/discovery/parsers/StarlarkParser.js +229 -0
- package/dist/lib/core/discovery/parsers/YamlConfigParser.d.ts +37 -0
- package/dist/lib/core/discovery/parsers/YamlConfigParser.js +212 -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 +40 -112
- package/dist/lib/domain/knowledge/KnowledgeEntry.js +44 -9
- 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/ai/AiProvider.d.ts +12 -0
- package/dist/lib/external/ai/AiProvider.js +24 -0
- package/dist/lib/external/ai/AiProviderManager.d.ts +101 -0
- package/dist/lib/external/ai/AiProviderManager.js +193 -0
- package/dist/lib/external/ai/providers/ClaudeProvider.js +11 -0
- package/dist/lib/external/ai/providers/GoogleGeminiProvider.js +18 -0
- package/dist/lib/external/ai/providers/MockProvider.d.ts +21 -3
- package/dist/lib/external/ai/providers/MockProvider.js +290 -14
- package/dist/lib/external/ai/providers/OpenAiProvider.js +16 -0
- package/dist/lib/external/lark/LarkTransport.d.ts +5 -1
- package/dist/lib/external/lark/LarkTransport.js +10 -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/mock-pipeline.d.ts +20 -0
- package/dist/lib/external/mcp/handlers/bootstrap/pipeline/mock-pipeline.js +432 -0
- package/dist/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.d.ts +99 -12
- package/dist/lib/external/mcp/handlers/bootstrap/pipeline/orchestrator.js +188 -169
- package/dist/lib/external/mcp/handlers/bootstrap/pipeline/tier-scheduler.js +7 -17
- package/dist/lib/external/mcp/handlers/bootstrap/refine.js +8 -0
- 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 +23 -10
- package/dist/lib/external/mcp/handlers/bootstrap-external.js +41 -51
- package/dist/lib/external/mcp/handlers/bootstrap-internal.d.ts +2 -0
- package/dist/lib/external/mcp/handlers/bootstrap-internal.js +117 -82
- package/dist/lib/external/mcp/handlers/consolidated.d.ts +4 -4
- package/dist/lib/external/mcp/handlers/consolidated.js +108 -332
- package/dist/lib/external/mcp/handlers/dimension-complete-external.js +71 -2
- package/dist/lib/external/mcp/handlers/evolve-external.d.ts +54 -0
- package/dist/lib/external/mcp/handlers/evolve-external.js +229 -0
- package/dist/lib/external/mcp/handlers/knowledge.js +30 -5
- 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/ai.js +111 -30
- package/dist/lib/http/routes/candidates.js +11 -4
- package/dist/lib/http/routes/commands.js +10 -1
- package/dist/lib/http/routes/health.js +11 -0
- package/dist/lib/http/routes/knowledge.js +122 -1
- package/dist/lib/http/routes/modules.js +52 -3
- package/dist/lib/http/routes/panorama.js +16 -4
- package/dist/lib/http/routes/recipes.js +7 -0
- package/dist/lib/http/utils/routeHelpers.js +2 -1
- 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.d.ts +6 -5
- package/dist/lib/injection/ServiceContainer.js +64 -25
- package/dist/lib/injection/ServiceMap.d.ts +10 -1
- package/dist/lib/injection/modules/AiModule.d.ts +6 -9
- package/dist/lib/injection/modules/AiModule.js +82 -39
- package/dist/lib/injection/modules/KnowledgeModule.js +15 -1
- package/dist/lib/injection/modules/PanoramaModule.js +1 -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 +132 -0
- package/dist/lib/service/cleanup/CleanupService.js +571 -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/CodeEntityGraph.d.ts +6 -0
- package/dist/lib/service/knowledge/CodeEntityGraph.js +16 -0
- 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 +67 -14
- package/dist/lib/service/knowledge/RecipeProductionGateway.d.ts +225 -0
- package/dist/lib/service/knowledge/RecipeProductionGateway.js +384 -0
- package/dist/lib/service/module/ModuleService.js +10 -19
- package/dist/lib/service/panorama/CouplingAnalyzer.d.ts +10 -1
- package/dist/lib/service/panorama/CouplingAnalyzer.js +44 -2
- package/dist/lib/service/panorama/DimensionAnalyzer.d.ts +4 -3
- package/dist/lib/service/panorama/DimensionAnalyzer.js +40 -151
- package/dist/lib/service/panorama/LayerInferrer.d.ts +16 -1
- package/dist/lib/service/panorama/LayerInferrer.js +118 -1
- package/dist/lib/service/panorama/ModuleDiscoverer.d.ts +9 -0
- package/dist/lib/service/panorama/ModuleDiscoverer.js +58 -2
- package/dist/lib/service/panorama/PanoramaAggregator.d.ts +6 -2
- package/dist/lib/service/panorama/PanoramaAggregator.js +84 -6
- package/dist/lib/service/panorama/PanoramaScanner.js +28 -0
- package/dist/lib/service/panorama/PanoramaService.js +10 -5
- package/dist/lib/service/panorama/PanoramaTypes.d.ts +38 -0
- package/dist/lib/service/panorama/RoleRefiner.d.ts +2 -0
- package/dist/lib/service/panorama/RoleRefiner.js +41 -0
- package/dist/lib/service/panorama/TechStackProfiler.d.ts +13 -0
- package/dist/lib/service/panorama/TechStackProfiler.js +191 -0
- 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/service/skills/SignalCollector.d.ts +1 -0
- package/dist/lib/service/skills/SignalCollector.js +6 -5
- package/dist/lib/service/vector/ContextualEnricher.d.ts +1 -0
- package/dist/lib/service/vector/ContextualEnricher.js +4 -0
- package/dist/lib/shared/LanguageService.js +3 -0
- package/dist/lib/shared/developer-identity.d.ts +18 -0
- package/dist/lib/shared/developer-identity.js +62 -0
- package/dist/lib/shared/schemas/common.d.ts +4 -4
- package/dist/lib/shared/schemas/http-requests.d.ts +20 -18
- package/dist/lib/shared/schemas/http-requests.js +17 -6
- 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 +132 -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
|
@@ -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 {};
|
|
@@ -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
|
+
}
|