principles-disciple 1.71.0 → 1.73.0
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/openclaw.plugin.json +10 -5
- package/package.json +17 -19
- package/scripts/acceptance-test.mjs +16 -73
- package/scripts/sync-plugin.mjs +382 -77
- package/src/commands/archive-impl.ts +2 -1
- package/src/commands/capabilities.ts +2 -2
- package/src/commands/context.ts +2 -2
- package/src/commands/disable-impl.ts +2 -1
- package/src/commands/evolution-status.ts +16 -16
- package/src/commands/export.ts +12 -67
- package/src/commands/pain.ts +91 -1
- package/src/commands/principle-rollback.ts +2 -1
- package/src/commands/promote-impl.ts +7 -43
- package/src/commands/rollback-impl.ts +2 -1
- package/src/commands/rollback.ts +2 -1
- package/src/commands/samples.ts +2 -1
- package/src/commands/thinking-os.ts +2 -1
- package/src/config/errors.ts +18 -2
- package/src/constants/diagnostician.ts +2 -2
- package/src/constants/tools.ts +2 -1
- package/src/core/__tests__/focus-history.test.ts +210 -0
- package/src/core/config.ts +1 -1
- package/src/core/confirm-first-gate.ts +255 -0
- package/src/core/correction-cue-learner.ts +2 -136
- package/src/core/correction-types.ts +16 -88
- package/src/core/dictionary.ts +19 -20
- package/src/core/empathy-keyword-matcher.ts +17 -289
- package/src/core/empathy-types.ts +18 -229
- package/src/core/event-log.ts +38 -132
- package/src/core/evolution-reducer.ts +21 -2
- package/src/core/evolution-types.ts +76 -464
- package/src/core/file-store.ts +80 -0
- package/src/core/focus-history.ts +228 -955
- package/src/core/local-worker-routing.ts +34 -314
- package/src/core/merge-gate-audit.ts +0 -195
- package/src/core/pain-diagnostic-gate.ts +154 -0
- package/src/core/pain-signal.ts +21 -138
- package/src/core/pain.ts +15 -88
- package/src/core/pd-task-reconciler.ts +26 -115
- package/src/core/pd-task-service.ts +9 -9
- package/src/core/pd-task-types.ts +23 -127
- package/src/core/principle-compiler/__tests__/compiler-replay-gate.test.ts +174 -0
- package/src/core/principle-compiler/code-validator.ts +15 -42
- package/src/core/principle-compiler/compiler.ts +100 -15
- package/src/core/principle-compiler/index.ts +5 -2
- package/src/core/principle-compiler/template-generator.ts +4 -104
- package/src/core/principle-injection.ts +10 -202
- package/src/core/principle-internalization/filesystem-lifecycle-datasource.ts +42 -0
- package/src/core/principle-internalization/lifecycle-read-model.ts +39 -242
- package/src/core/principle-internalization/principle-lifecycle-service.ts +12 -10
- package/src/core/principle-tree-ledger-adapter.ts +145 -0
- package/src/core/principle-tree-ledger.ts +8 -6
- package/src/core/reflection/reflection-context.ts +14 -109
- package/src/core/replay-engine.ts +8 -500
- package/src/core/rule-host-helpers.ts +5 -35
- package/src/core/rule-host-types.ts +10 -82
- package/src/core/rule-host.ts +6 -63
- package/src/core/runtime-v2-prompt-activation-reader.ts +231 -0
- package/src/core/session-tracker.ts +87 -101
- package/src/core/shadow-observation-registry.ts +19 -48
- package/src/core/trajectory.ts +3 -1
- package/src/core/workflow-funnel-loader.ts +62 -68
- package/src/core/workspace-context.ts +46 -0
- package/src/core/workspace-dir-service.ts +1 -1
- package/src/core/workspace-dir-validation.ts +18 -9
- package/src/hooks/AGENTS.md +1 -1
- package/src/hooks/gate-block-helper.ts +46 -44
- package/src/hooks/gate.ts +207 -7
- package/src/hooks/lifecycle.ts +30 -32
- package/src/hooks/llm.ts +60 -32
- package/src/hooks/pain.ts +297 -103
- package/src/hooks/prompt.ts +469 -339
- package/src/hooks/subagent.ts +2 -29
- package/src/i18n/commands.ts +2 -10
- package/src/index.ts +95 -85
- package/src/openclaw-sdk.ts +311 -0
- package/src/service/central-database.ts +8 -4
- package/src/service/evolution-queue-migration.ts +2 -1
- package/src/service/evolution-worker.ts +163 -1786
- package/src/service/internalization-trigger-adapter.ts +302 -0
- package/src/service/keyword-optimization-service.ts +4 -4
- package/src/service/monitoring-query-service.ts +1 -215
- package/src/service/queue-io.ts +60 -331
- package/src/service/runtime-summary-service.ts +115 -18
- package/src/service/subagent-workflow/index.ts +0 -41
- package/src/service/subagent-workflow/types.ts +9 -120
- package/src/service/subagent-workflow/workflow-store.ts +2 -119
- package/src/service/workflow-watchdog.ts +0 -43
- package/src/types/event-payload.ts +16 -74
- package/src/types/event-types.ts +39 -547
- package/src/types/hygiene-types.ts +7 -30
- package/src/types/principle-tree-schema.ts +20 -222
- package/src/types/queue.ts +15 -70
- package/src/types/runtime-summary.ts +5 -49
- package/src/utils/io.ts +10 -0
- package/src/utils/retry.ts +1 -1
- package/src/utils/shadow-fingerprint.ts +2 -2
- package/src/utils/workspace-resolver.ts +50 -0
- package/templates/langs/en/core/AGENTS.md +2 -2
- package/templates/langs/en/core/BOOT.md +1 -1
- package/templates/langs/en/core/HEARTBEAT.md +2 -2
- package/templates/langs/en/skills/ai-sprint-orchestration/references/agent-registry.json +1 -72
- package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/bugfix-complex-template.json +6 -6
- package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/feature-complex-template.json +6 -6
- package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal-verify.json +2 -12
- package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal.json +2 -12
- package/templates/langs/en/skills/ai-sprint-orchestration/runtime/.gitignore +2 -2
- package/templates/langs/en/skills/ai-sprint-orchestration/scripts/run.mjs +51 -15
- package/templates/langs/en/skills/evolve-task/SKILL.md +1 -1
- package/templates/langs/en/skills/pd-cli-operator/SKILL.md +67 -0
- package/templates/langs/en/skills/pd-diagnostician/SKILL.md +1 -1
- package/templates/langs/en/skills/pd-mentor/SKILL.md +1 -1
- package/templates/langs/en/skills/pd-pain-signal/SKILL.md +17 -39
- package/templates/langs/en/skills/pd-runtime-v2/SKILL.md +61 -0
- package/templates/langs/zh/core/AGENTS.md +2 -2
- package/templates/langs/zh/core/BOOT.md +1 -1
- package/templates/langs/zh/core/HEARTBEAT.md +2 -2
- package/templates/langs/zh/skills/ai-sprint-orchestration/references/agent-registry.json +1 -72
- package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/bugfix-complex-template.json +6 -6
- package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/feature-complex-template.json +6 -6
- package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/nocturnal-trinity-quality-enhancement.json +8 -8
- package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal-verify.json +2 -12
- package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal.json +2 -12
- package/templates/langs/zh/skills/ai-sprint-orchestration/runtime/.gitignore +2 -2
- package/templates/langs/zh/skills/ai-sprint-orchestration/scripts/run.mjs +51 -15
- package/templates/langs/zh/skills/ai-sprint-orchestration/test/run.test.mjs +21 -5
- package/templates/langs/zh/skills/evolve-task/SKILL.md +2 -2
- package/templates/langs/zh/skills/pd-cli-operator/SKILL.md +67 -0
- package/templates/langs/zh/skills/pd-diagnostician/SKILL.md +1 -1
- package/templates/langs/zh/skills/pd-mentor/SKILL.md +1 -1
- package/templates/langs/zh/skills/pd-pain-signal/SKILL.md +17 -38
- package/templates/langs/zh/skills/pd-runtime-v2/SKILL.md +61 -0
- package/tests/build-artifacts.test.ts +1 -3
- package/tests/commands/evolution-status.test.ts +0 -118
- package/tests/core/bootstrap-rules.test.ts +1 -1
- package/tests/core/config.test.ts +1 -1
- package/tests/core/event-log.test.ts +35 -0
- package/tests/core/evolution-engine.test.ts +610 -0
- package/tests/core/file-store.test.ts +102 -0
- package/tests/core/focus-history.test.ts +203 -11
- package/tests/core/merge-gate-audit.test.ts +2 -169
- package/tests/core/model-deployment-registry.test.ts +7 -1
- package/tests/core/model-training-registry.test.ts +19 -0
- package/tests/core/observability.test.ts +0 -1
- package/tests/core/pain-diagnostic-gate.test.ts +498 -0
- package/tests/core/pain.test.ts +0 -1
- package/tests/core/principle-internalization/deprecated-readiness.test.ts +2 -2
- package/tests/core/principle-internalization/lifecycle-metrics.test.ts +2 -2
- package/tests/core/principle-internalization/{internalization-routing-policy.test.ts → lifecycle-routing-policy.test.ts} +6 -6
- package/tests/core/principle-internalization/lineage-source-retired.test.ts +56 -0
- package/tests/core/principle-internalization/principle-lifecycle-service.test.ts +1 -23
- package/tests/core/principle-tree-ledger-adapter.test.ts +253 -0
- package/tests/core/reflection-context.test.ts +0 -14
- package/tests/core/replay-engine.test.ts +127 -215
- package/tests/core/rule-host-helpers.test.ts +2 -2
- package/tests/core/rule-implementation-runtime.test.ts +0 -27
- package/tests/core/workflow-funnel-loader.test.ts +162 -0
- package/tests/core/workspace-dir-validation.test.ts +8 -1
- package/tests/core-anti-growth.test.ts +192 -0
- package/tests/hook-workspace-nextaction-contract.test.ts +42 -0
- package/tests/hooks/confirm-first-gate.test.ts +333 -0
- package/tests/hooks/gate-auto-correct-shadow.test.ts +310 -0
- package/tests/hooks/gate-auto-correct.test.ts +665 -0
- package/tests/hooks/gate-rule-host-pipeline.test.ts +2 -1
- package/tests/hooks/pain.test.ts +269 -12
- package/tests/hooks/prompt-characterization.test.ts +500 -0
- package/tests/hooks/prompt-size-guard.test.ts +329 -0
- package/tests/hooks/runtime-v2-prompt-activation.test.ts +869 -0
- package/tests/index.test.ts +94 -1
- package/tests/integration/auto-entry-gate.test.ts +248 -0
- package/tests/integration/internalization-trigger-guard.test.ts +69 -0
- package/tests/integration/m8-legacy-paths.test.ts +63 -0
- package/tests/integration/runtime-v2-pain-guard.test.ts +125 -0
- package/tests/plugin-config-resolution-cutover.test.ts +359 -0
- package/tests/runtime-v2-discovery-guard.test.ts +154 -0
- package/tests/service/central-database.test.ts +457 -0
- package/tests/service/evolution-worker.correction-observer.test.ts +173 -0
- package/tests/service/evolution-worker.timeout.test.ts +11 -129
- package/tests/service/internalization-trigger-adapter.test.ts +251 -0
- package/tests/service/monitoring-query-service.test.ts +1 -47
- package/tests/service/queue-io.test.ts +1 -62
- package/tests/service/runtime-summary-service.test.ts +184 -3
- package/tests/service/workflow-watchdog.test.ts +0 -91
- package/tests/utils/file-lock.test.ts +5 -3
- package/tests/utils/session-key.test.ts +52 -0
- package/tests/utils/subagent-probe.test.ts +48 -1
- package/vitest.config.ts +4 -11
- package/.planning/codebase/ARCHITECTURE.md +0 -157
- package/.planning/codebase/CONCERNS.md +0 -145
- package/.planning/codebase/CONVENTIONS.md +0 -148
- package/.planning/codebase/INTEGRATIONS.md +0 -81
- package/.planning/codebase/STACK.md +0 -87
- package/.planning/codebase/STRUCTURE.md +0 -193
- package/.planning/codebase/TESTING.md +0 -243
- package/.planning/phases/01-basic-visualization/01-GAP-CLOSURE-VERIFICATION.md +0 -113
- package/docs/COMMAND_REFERENCE.md +0 -76
- package/docs/COMMAND_REFERENCE_EN.md +0 -79
- package/scripts/build-web.mjs +0 -46
- package/scripts/diagnose-nocturnal.mjs +0 -537
- package/scripts/seed-nocturnal-scenarios.mjs +0 -384
- package/src/commands/nocturnal-review.ts +0 -322
- package/src/commands/nocturnal-rollout.ts +0 -790
- package/src/commands/nocturnal-train.ts +0 -986
- package/src/commands/pd-reflect.ts +0 -88
- package/src/core/adaptive-thresholds.ts +0 -478
- package/src/core/diagnostician-task-store.ts +0 -192
- package/src/core/nocturnal-arbiter.ts +0 -715
- package/src/core/nocturnal-artifact-lineage.ts +0 -116
- package/src/core/nocturnal-artificer.ts +0 -257
- package/src/core/nocturnal-candidate-scoring.ts +0 -530
- package/src/core/nocturnal-compliance.ts +0 -1146
- package/src/core/nocturnal-dataset.ts +0 -763
- package/src/core/nocturnal-executability.ts +0 -428
- package/src/core/nocturnal-export.ts +0 -499
- package/src/core/nocturnal-paths.ts +0 -240
- package/src/core/nocturnal-reasoning-deriver.ts +0 -343
- package/src/core/nocturnal-rule-implementation-validator.ts +0 -246
- package/src/core/nocturnal-snapshot-contract.ts +0 -99
- package/src/core/nocturnal-trajectory-extractor.ts +0 -512
- package/src/core/nocturnal-trinity-types.ts +0 -218
- package/src/core/nocturnal-trinity.ts +0 -2680
- package/src/core/principle-internalization/deprecated-readiness.ts +0 -93
- package/src/core/principle-internalization/internalization-routing-policy.ts +0 -208
- package/src/core/principle-internalization/lifecycle-metrics.ts +0 -152
- package/src/http/principles-console-route.ts +0 -709
- package/src/service/central-health-service.ts +0 -49
- package/src/service/central-overview-service.ts +0 -138
- package/src/service/control-ui-query-service.ts +0 -900
- package/src/service/cooldown-strategy.ts +0 -97
- package/src/service/evolution-pain-context.ts +0 -79
- package/src/service/evolution-query-service.ts +0 -407
- package/src/service/health-query-service.ts +0 -1038
- package/src/service/nocturnal-config.ts +0 -214
- package/src/service/nocturnal-runtime.ts +0 -734
- package/src/service/nocturnal-service.ts +0 -1605
- package/src/service/nocturnal-target-selector.ts +0 -545
- package/src/service/sleep-cycle.ts +0 -157
- package/src/service/startup-reconciler.ts +0 -112
- package/src/service/subagent-workflow/correction-observer-types.ts +0 -82
- package/src/service/subagent-workflow/correction-observer-workflow-manager.ts +0 -250
- package/src/service/subagent-workflow/deep-reflect-workflow-manager.ts +0 -1
- package/src/service/subagent-workflow/dynamic-timeout.ts +0 -30
- package/src/service/subagent-workflow/empathy-observer-workflow-manager.ts +0 -268
- package/src/service/subagent-workflow/nocturnal-workflow-manager.ts +0 -795
- package/src/service/subagent-workflow/runtime-direct-driver.ts +0 -268
- package/src/service/subagent-workflow/workflow-manager-base.ts +0 -580
- package/src/tools/write-pain-flag.ts +0 -215
- package/tests/commands/nocturnal-review.test.ts +0 -448
- package/tests/commands/nocturnal-train.test.ts +0 -97
- package/tests/commands/pd-reflect.test.ts +0 -49
- package/tests/core/adaptive-thresholds.test.ts +0 -261
- package/tests/core/nocturnal-arbiter.test.ts +0 -559
- package/tests/core/nocturnal-artifact-lineage.test.ts +0 -53
- package/tests/core/nocturnal-artificer.test.ts +0 -241
- package/tests/core/nocturnal-candidate-scoring.test.ts +0 -532
- package/tests/core/nocturnal-compliance-p-principles.test.ts +0 -133
- package/tests/core/nocturnal-compliance.test.ts +0 -646
- package/tests/core/nocturnal-dataset.test.ts +0 -892
- package/tests/core/nocturnal-e2e.test.ts +0 -234
- package/tests/core/nocturnal-executability.test.ts +0 -357
- package/tests/core/nocturnal-export.test.ts +0 -517
- package/tests/core/nocturnal-reasoning-deriver.test.ts +0 -372
- package/tests/core/nocturnal-reviewed-subset-comparison.test.ts +0 -428
- package/tests/core/nocturnal-rule-implementation-validator.test.ts +0 -127
- package/tests/core/nocturnal-snapshot-contract.test.ts +0 -121
- package/tests/core/nocturnal-trajectory-extractor.test.ts +0 -634
- package/tests/core/nocturnal-trinity.test.ts +0 -2053
- package/tests/core/pain-auto-repair.test.ts +0 -96
- package/tests/core/pain-integration.test.ts +0 -510
- package/tests/fixtures/nocturnal-reviewed-subset.json +0 -183
- package/tests/http/principles-console-route.test.ts +0 -162
- package/tests/integration/chaos-resilience.test.ts +0 -348
- package/tests/integration/empathy-workflow-integration.test.ts +0 -626
- package/tests/integration/pain-diagnostician-loop.e2e.test.ts +0 -380
- package/tests/service/control-ui-query-service.test.ts +0 -121
- package/tests/service/cooldown-strategy.test.ts +0 -164
- package/tests/service/data-endpoints-regression.test.ts +0 -834
- package/tests/service/empathy-observer-workflow-manager.test.ts +0 -175
- package/tests/service/evolution-worker.nocturnal.test.ts +0 -601
- package/tests/service/nocturnal-runtime-hardening.test.ts +0 -118
- package/tests/service/nocturnal-runtime.test.ts +0 -473
- package/tests/service/nocturnal-service-code-candidate.test.ts +0 -330
- package/tests/service/nocturnal-target-selector.test.ts +0 -615
- package/tests/service/startup-reconciler.test.ts +0 -148
- package/tests/tools/write-pain-flag.test.ts +0 -358
- package/ui/src/App.tsx +0 -45
- package/ui/src/api.ts +0 -220
- package/ui/src/charts.tsx +0 -955
- package/ui/src/components/ErrorState.tsx +0 -6
- package/ui/src/components/Loading.tsx +0 -13
- package/ui/src/components/ProtectedRoute.tsx +0 -12
- package/ui/src/components/Shell.tsx +0 -91
- package/ui/src/components/WorkspaceConfig.tsx +0 -178
- package/ui/src/components/index.ts +0 -5
- package/ui/src/context/auth.tsx +0 -80
- package/ui/src/context/theme.tsx +0 -66
- package/ui/src/hooks/useAutoRefresh.ts +0 -39
- package/ui/src/i18n/ui.ts +0 -473
- package/ui/src/main.tsx +0 -16
- package/ui/src/pages/EvolutionPage.tsx +0 -333
- package/ui/src/pages/FeedbackPage.tsx +0 -138
- package/ui/src/pages/GateMonitorPage.tsx +0 -136
- package/ui/src/pages/LoginPage.tsx +0 -89
- package/ui/src/pages/OverviewPage.tsx +0 -599
- package/ui/src/pages/SamplesPage.tsx +0 -174
- package/ui/src/pages/ThinkingModelsPage.tsx +0 -702
- package/ui/src/styles.css +0 -2020
- package/ui/src/types.ts +0 -384
- package/ui/src/utils/format.ts +0 -15
|
@@ -1,97 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Cooldown Strategy -- Tiered escalation for persistent task failures
|
|
3
|
-
* ===========================================================
|
|
4
|
-
*
|
|
5
|
-
* Manages cooldown escalation state persisted to nocturnal-runtime.json.
|
|
6
|
-
* When the failure classifier (failure-classifier.ts) detects persistent
|
|
7
|
-
* failure (3+ consecutive failures), this module applies escalating
|
|
8
|
-
* cooldowns: 30min -> 4h -> 24h (cap).
|
|
9
|
-
*
|
|
10
|
-
* State is stored in NocturnalRuntimeState.taskFailureState, keyed by
|
|
11
|
-
* taskKind string. Uses exported readState/writeState from
|
|
12
|
-
* nocturnal-runtime.ts for atomic file access with locking.
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
import { readState as readStateAsync, readStateSync, writeState } from './nocturnal-runtime.js';
|
|
16
|
-
import type { CooldownEscalationConfig } from './nocturnal-config.js';
|
|
17
|
-
import { loadCooldownEscalationConfig } from './nocturnal-config.js';
|
|
18
|
-
import type { ClassifiableTaskKind } from './failure-classifier.js';
|
|
19
|
-
|
|
20
|
-
/**
|
|
21
|
-
* Record a persistent failure and escalate the cooldown tier.
|
|
22
|
-
* Called when the failure classifier detects persistent failure pattern.
|
|
23
|
-
*
|
|
24
|
-
* State transitions:
|
|
25
|
-
* No state -> Tier 1 (30min cooldown)
|
|
26
|
-
* Tier 1 -> Tier 2 (4h cooldown)
|
|
27
|
-
* Tier 2 -> Tier 3 (24h cooldown)
|
|
28
|
-
* Tier 3 -> Tier 3 (24h cooldown, capped)
|
|
29
|
-
*/
|
|
30
|
-
export async function recordPersistentFailure(
|
|
31
|
-
stateDir: string,
|
|
32
|
-
taskKind: ClassifiableTaskKind,
|
|
33
|
-
config?: CooldownEscalationConfig,
|
|
34
|
-
classifierCount?: number,
|
|
35
|
-
): Promise<void> {
|
|
36
|
-
const resolvedConfig = config ?? loadCooldownEscalationConfig(stateDir);
|
|
37
|
-
|
|
38
|
-
const state = await readStateAsync(stateDir);
|
|
39
|
-
if (!state.taskFailureState) state.taskFailureState = {};
|
|
40
|
-
|
|
41
|
-
const current = state.taskFailureState[taskKind] ?? {
|
|
42
|
-
consecutiveFailures: 0,
|
|
43
|
-
escalationTier: 0,
|
|
44
|
-
};
|
|
45
|
-
|
|
46
|
-
current.consecutiveFailures = classifierCount ?? (current.consecutiveFailures + 1);
|
|
47
|
-
current.escalationTier = Math.min(current.escalationTier + 1, 3);
|
|
48
|
-
|
|
49
|
-
const tierKey = current.escalationTier as 1 | 2 | 3;
|
|
50
|
-
const durationMs = resolvedConfig[`tier${tierKey}_ms` as keyof CooldownEscalationConfig] as number;
|
|
51
|
-
current.cooldownUntil = new Date(Date.now() + durationMs).toISOString();
|
|
52
|
-
|
|
53
|
-
state.taskFailureState[taskKind] = current;
|
|
54
|
-
await writeState(stateDir, state);
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
/**
|
|
58
|
-
* Reset failure state for a task kind on successful completion.
|
|
59
|
-
* Resets both consecutiveFailures and escalationTier to 0.
|
|
60
|
-
*/
|
|
61
|
-
export async function resetFailureState(
|
|
62
|
-
stateDir: string,
|
|
63
|
-
taskKind: ClassifiableTaskKind,
|
|
64
|
-
): Promise<void> {
|
|
65
|
-
const state = await readStateAsync(stateDir);
|
|
66
|
-
if (!state.taskFailureState?.[taskKind]) return; // No state to reset
|
|
67
|
-
|
|
68
|
-
state.taskFailureState[taskKind] = {
|
|
69
|
-
consecutiveFailures: 0,
|
|
70
|
-
escalationTier: 0,
|
|
71
|
-
};
|
|
72
|
-
await writeState(stateDir, state);
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
/**
|
|
76
|
-
* Check if a task kind is currently in cooldown.
|
|
77
|
-
* Returns remaining cooldown duration or 0 if not in cooldown.
|
|
78
|
-
*/
|
|
79
|
-
export function isTaskKindInCooldown(
|
|
80
|
-
stateDir: string,
|
|
81
|
-
taskKind: ClassifiableTaskKind,
|
|
82
|
-
): { inCooldown: boolean; remainingMs: number; cooldownUntil: string | null } {
|
|
83
|
-
const state = readStateSync(stateDir);
|
|
84
|
-
|
|
85
|
-
const failureState = state.taskFailureState?.[taskKind];
|
|
86
|
-
if (!failureState?.cooldownUntil) {
|
|
87
|
-
return { inCooldown: false, remainingMs: 0, cooldownUntil: null };
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
const cooldownEnd = new Date(failureState.cooldownUntil).getTime();
|
|
91
|
-
const remaining = cooldownEnd - Date.now();
|
|
92
|
-
if (remaining <= 0) {
|
|
93
|
-
return { inCooldown: false, remainingMs: 0, cooldownUntil: null };
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
return { inCooldown: true, remainingMs: remaining, cooldownUntil: failureState.cooldownUntil };
|
|
97
|
-
}
|
|
@@ -1,79 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Evolution Pain Context Reader
|
|
3
|
-
*
|
|
4
|
-
* Reads and processes pain signal context for task enrichment.
|
|
5
|
-
* Extracted from evolution-worker.ts.
|
|
6
|
-
*/
|
|
7
|
-
|
|
8
|
-
import type { WorkspaceContext } from '../core/workspace-context.js';
|
|
9
|
-
import { readPainFlagContract } from '../core/pain.js';
|
|
10
|
-
import type { EvolutionQueueItem } from './evolution-queue-migration.js';
|
|
11
|
-
import type { RecentPainContext } from './evolution-queue-migration.js';
|
|
12
|
-
import { SLEEP_REFLECTION_DEDUP_WINDOW_MS } from './queue-io.js';
|
|
13
|
-
|
|
14
|
-
/**
|
|
15
|
-
* Read recent pain context from PAIN_FLAG file.
|
|
16
|
-
* Extracts session_id to link to trajectory DB.
|
|
17
|
-
* Returns structured pain metadata for attaching to sleep_reflection tasks.
|
|
18
|
-
* Returns null if no pain flag exists.
|
|
19
|
-
*/
|
|
20
|
-
export function readRecentPainContext(wctx: WorkspaceContext): RecentPainContext {
|
|
21
|
-
const contract = readPainFlagContract(wctx.workspaceDir);
|
|
22
|
-
if (contract.status !== 'valid') {
|
|
23
|
-
return { mostRecent: null, recentPainCount: 0, recentMaxPainScore: 0 };
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
try {
|
|
27
|
-
const score = parseInt(contract.data.score ?? '0', 10) || 0;
|
|
28
|
-
const source = contract.data.source ?? '';
|
|
29
|
-
const reason = contract.data.reason ?? '';
|
|
30
|
-
const timestamp = contract.data.time ?? '';
|
|
31
|
-
const sessionId = contract.data.session_id ?? '';
|
|
32
|
-
|
|
33
|
-
if (score > 0) {
|
|
34
|
-
return {
|
|
35
|
-
mostRecent: { score, source, reason, timestamp, sessionId },
|
|
36
|
-
recentPainCount: 1,
|
|
37
|
-
recentMaxPainScore: score,
|
|
38
|
-
};
|
|
39
|
-
}
|
|
40
|
-
} catch {
|
|
41
|
-
// Best effort — non-fatal
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
return { mostRecent: null, recentPainCount: 0, recentMaxPainScore: 0 };
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
/**
|
|
48
|
-
* Build a dedup key from pain context.
|
|
49
|
-
* Returns null when no pain context is available (bypasses dedup).
|
|
50
|
-
*/
|
|
51
|
-
export function buildPainSourceKey(
|
|
52
|
-
painCtx: ReturnType<typeof readRecentPainContext>,
|
|
53
|
-
): string | null {
|
|
54
|
-
if (!painCtx.mostRecent) return null;
|
|
55
|
-
return `${painCtx.mostRecent.source}::${painCtx.mostRecent.reason?.slice(0, 50) ?? ''}`;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Check whether a similar sleep_reflection task completed recently.
|
|
60
|
-
* Phase 3c: Prevents redundant reflections of the same underlying issue.
|
|
61
|
-
*/
|
|
62
|
-
export function hasRecentSimilarReflection(
|
|
63
|
-
queue: EvolutionQueueItem[],
|
|
64
|
-
painSourceKey: string,
|
|
65
|
-
now: number,
|
|
66
|
-
): EvolutionQueueItem | null {
|
|
67
|
-
return queue.find((t) => {
|
|
68
|
-
if (t.taskKind !== 'sleep_reflection') return false;
|
|
69
|
-
// Only match completed tasks (exclude failed to allow retries)
|
|
70
|
-
if (t.status !== 'completed') return false;
|
|
71
|
-
if (!t.completed_at) return false;
|
|
72
|
-
const age = now - new Date(t.completed_at).getTime();
|
|
73
|
-
if (age > SLEEP_REFLECTION_DEDUP_WINDOW_MS) return false;
|
|
74
|
-
const taskPainKey = buildPainSourceKey(t.recentPainContext ?? { mostRecent: null, recentPainCount: 0, recentMaxPainScore: 0 });
|
|
75
|
-
// If either side has no pain context, they don't match
|
|
76
|
-
if (!taskPainKey) return false;
|
|
77
|
-
return taskPainKey === painSourceKey;
|
|
78
|
-
}) ?? null;
|
|
79
|
-
}
|
|
@@ -1,407 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* EvolutionQueryService - 进化流程查询服务
|
|
3
|
-
*
|
|
4
|
-
* 提供 WebUI 所需的查询 API:
|
|
5
|
-
* - getTasks() - 获取任务列表
|
|
6
|
-
* - getEvents() - 获取事件流
|
|
7
|
-
* - getTrace() - 获取完整追踪
|
|
8
|
-
* - getStats() - 获取统计数据
|
|
9
|
-
*/
|
|
10
|
-
|
|
11
|
-
import type { TrajectoryDatabase, EvolutionTaskRecord } from '../core/trajectory.js';
|
|
12
|
-
import { STAGE_LABELS, STAGE_COLORS } from '../core/evolution-logger.js';
|
|
13
|
-
|
|
14
|
-
export interface TaskListFilters {
|
|
15
|
-
status?: string;
|
|
16
|
-
dateFrom?: string;
|
|
17
|
-
dateTo?: string;
|
|
18
|
-
page?: number;
|
|
19
|
-
pageSize?: number;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface EventFilters {
|
|
23
|
-
traceId?: string;
|
|
24
|
-
stage?: string;
|
|
25
|
-
limit?: number;
|
|
26
|
-
offset?: number;
|
|
27
|
-
}
|
|
28
|
-
|
|
29
|
-
export interface TasksResponse {
|
|
30
|
-
items: {
|
|
31
|
-
taskId: string;
|
|
32
|
-
traceId: string;
|
|
33
|
-
source: string;
|
|
34
|
-
reason: string | null;
|
|
35
|
-
score: number;
|
|
36
|
-
status: string;
|
|
37
|
-
enqueuedAt: string | null;
|
|
38
|
-
startedAt: string | null;
|
|
39
|
-
completedAt: string | null;
|
|
40
|
-
duration: number | null;
|
|
41
|
-
resolution: string | null;
|
|
42
|
-
eventCount: number;
|
|
43
|
-
createdAt: string;
|
|
44
|
-
}[];
|
|
45
|
-
pagination: {
|
|
46
|
-
page: number;
|
|
47
|
-
pageSize: number;
|
|
48
|
-
total: number;
|
|
49
|
-
totalPages: number;
|
|
50
|
-
};
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export interface EventsResponse {
|
|
54
|
-
items: {
|
|
55
|
-
id: number;
|
|
56
|
-
traceId: string;
|
|
57
|
-
taskId: string | null;
|
|
58
|
-
stage: string;
|
|
59
|
-
stageLabel: string;
|
|
60
|
-
stageColor: string;
|
|
61
|
-
level: string;
|
|
62
|
-
message: string;
|
|
63
|
-
summary: string | null;
|
|
64
|
-
metadata: Record<string, unknown>;
|
|
65
|
-
createdAt: string;
|
|
66
|
-
}[];
|
|
67
|
-
pagination: {
|
|
68
|
-
limit: number;
|
|
69
|
-
offset: number;
|
|
70
|
-
hasMore: boolean;
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
export interface TraceDetailResponse {
|
|
75
|
-
traceId: string;
|
|
76
|
-
task: {
|
|
77
|
-
taskId: string;
|
|
78
|
-
traceId: string;
|
|
79
|
-
source: string;
|
|
80
|
-
reason: string | null;
|
|
81
|
-
score: number;
|
|
82
|
-
status: string;
|
|
83
|
-
enqueuedAt: string | null;
|
|
84
|
-
startedAt: string | null;
|
|
85
|
-
completedAt: string | null;
|
|
86
|
-
duration: number | null;
|
|
87
|
-
resolution: string | null;
|
|
88
|
-
createdAt: string;
|
|
89
|
-
updatedAt: string;
|
|
90
|
-
};
|
|
91
|
-
events: {
|
|
92
|
-
id: number;
|
|
93
|
-
traceId: string;
|
|
94
|
-
taskId: string | null;
|
|
95
|
-
stage: string;
|
|
96
|
-
stageLabel: string;
|
|
97
|
-
stageColor: string;
|
|
98
|
-
level: string;
|
|
99
|
-
message: string;
|
|
100
|
-
summary: string | null;
|
|
101
|
-
metadata: Record<string, unknown>;
|
|
102
|
-
createdAt: string;
|
|
103
|
-
}[];
|
|
104
|
-
timeline: {
|
|
105
|
-
stage: string;
|
|
106
|
-
stageLabel: string;
|
|
107
|
-
stageColor: string;
|
|
108
|
-
timestamp: string;
|
|
109
|
-
message: string;
|
|
110
|
-
summary: string | null;
|
|
111
|
-
}[];
|
|
112
|
-
}
|
|
113
|
-
|
|
114
|
-
export interface EvolutionStatsResponse {
|
|
115
|
-
total: number;
|
|
116
|
-
pending: number;
|
|
117
|
-
inProgress: number;
|
|
118
|
-
completed: number;
|
|
119
|
-
failed: number;
|
|
120
|
-
recentActivity: {
|
|
121
|
-
day: string;
|
|
122
|
-
created: number;
|
|
123
|
-
completed: number;
|
|
124
|
-
}[];
|
|
125
|
-
stageDistribution: {
|
|
126
|
-
stage: string;
|
|
127
|
-
stageLabel: string;
|
|
128
|
-
count: number;
|
|
129
|
-
}[];
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
/**
|
|
133
|
-
* 计算任务持续时间(毫秒)
|
|
134
|
-
*/
|
|
135
|
-
function calculateDuration(task: EvolutionTaskRecord): number | null {
|
|
136
|
-
if (!task.startedAt) return null;
|
|
137
|
-
const start = new Date(task.startedAt).getTime();
|
|
138
|
-
const end = task.completedAt ? new Date(task.completedAt).getTime() : Date.now();
|
|
139
|
-
return end - start;
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
/**
|
|
143
|
-
* EvolutionQueryService 类
|
|
144
|
-
* 封装进化流程的查询逻辑
|
|
145
|
-
*/
|
|
146
|
-
export class EvolutionQueryService {
|
|
147
|
-
private readonly trajectory: TrajectoryDatabase;
|
|
148
|
-
|
|
149
|
-
constructor(trajectory: TrajectoryDatabase) {
|
|
150
|
-
this.trajectory = trajectory;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
/**
|
|
154
|
-
* 释放资源
|
|
155
|
-
* 注意:不关闭 trajectory,因为它是单例由 TrajectoryRegistry 管理
|
|
156
|
-
*/
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
dispose(): void {
|
|
160
|
-
// EvolutionQueryService 不拥有 trajectory,所以不关闭它
|
|
161
|
-
// trajectory 是由 TrajectoryRegistry 管理的单例
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
/**
|
|
165
|
-
* 获取任务列表(带分页、筛选)
|
|
166
|
-
*/
|
|
167
|
-
getTasks(filters: TaskListFilters = {}): TasksResponse {
|
|
168
|
-
const page = filters.page ?? 1;
|
|
169
|
-
const pageSize = filters.pageSize ?? 20;
|
|
170
|
-
const offset = (page - 1) * pageSize;
|
|
171
|
-
|
|
172
|
-
// 获取任务列表
|
|
173
|
-
const tasks = this.trajectory.listEvolutionTasks({
|
|
174
|
-
status: filters.status,
|
|
175
|
-
dateFrom: filters.dateFrom,
|
|
176
|
-
dateTo: filters.dateTo,
|
|
177
|
-
limit: pageSize + 1, // 多取一条判断 hasMore
|
|
178
|
-
offset,
|
|
179
|
-
});
|
|
180
|
-
|
|
181
|
-
// 获取总数(简化实现,使用全量计数)
|
|
182
|
-
const allTasks = this.trajectory.listEvolutionTasks({
|
|
183
|
-
status: filters.status,
|
|
184
|
-
dateFrom: filters.dateFrom,
|
|
185
|
-
dateTo: filters.dateTo,
|
|
186
|
-
limit: 10000,
|
|
187
|
-
offset: 0,
|
|
188
|
-
});
|
|
189
|
-
const total = allTasks.length;
|
|
190
|
-
const totalPages = Math.ceil(total / pageSize);
|
|
191
|
-
|
|
192
|
-
// 组装响应
|
|
193
|
-
const items = tasks.slice(0, pageSize).map((task) => {
|
|
194
|
-
// 获取事件数量
|
|
195
|
-
const events = this.trajectory.listEvolutionEvents(task.traceId, { limit: 1000 });
|
|
196
|
-
|
|
197
|
-
return {
|
|
198
|
-
taskId: task.taskId,
|
|
199
|
-
traceId: task.traceId,
|
|
200
|
-
source: task.source,
|
|
201
|
-
reason: task.reason,
|
|
202
|
-
score: task.score,
|
|
203
|
-
status: task.status,
|
|
204
|
-
enqueuedAt: task.enqueuedAt,
|
|
205
|
-
startedAt: task.startedAt,
|
|
206
|
-
completedAt: task.completedAt,
|
|
207
|
-
duration: calculateDuration(task),
|
|
208
|
-
resolution: task.resolution,
|
|
209
|
-
eventCount: events.length,
|
|
210
|
-
createdAt: task.createdAt,
|
|
211
|
-
};
|
|
212
|
-
});
|
|
213
|
-
|
|
214
|
-
return {
|
|
215
|
-
items,
|
|
216
|
-
pagination: {
|
|
217
|
-
page,
|
|
218
|
-
pageSize,
|
|
219
|
-
total,
|
|
220
|
-
totalPages,
|
|
221
|
-
},
|
|
222
|
-
};
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* 获取事件流
|
|
227
|
-
*/
|
|
228
|
-
getEvents(filters: EventFilters = {}): EventsResponse {
|
|
229
|
-
const limit = filters.limit ?? 100;
|
|
230
|
-
const offset = filters.offset ?? 0;
|
|
231
|
-
|
|
232
|
-
// 获取更多事件以支持 stage 过滤
|
|
233
|
-
const fetchLimit = filters.stage ? 500 : limit + 1;
|
|
234
|
-
const events = this.trajectory.listEvolutionEvents(filters.traceId, {
|
|
235
|
-
limit: fetchLimit,
|
|
236
|
-
offset: 0, // 从头获取,后续再过滤和分页
|
|
237
|
-
});
|
|
238
|
-
|
|
239
|
-
// 应用 stage 过滤
|
|
240
|
-
let filteredEvents = events;
|
|
241
|
-
if (filters.stage) {
|
|
242
|
-
filteredEvents = events.filter(event => event.stage === filters.stage);
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
// 应用分页
|
|
246
|
-
const hasMore = filteredEvents.length > offset + limit;
|
|
247
|
-
const items = filteredEvents.slice(offset, offset + limit).map((event) => ({
|
|
248
|
-
id: event.id,
|
|
249
|
-
traceId: event.traceId,
|
|
250
|
-
taskId: event.taskId,
|
|
251
|
-
stage: event.stage,
|
|
252
|
-
stageLabel: STAGE_LABELS[event.stage as keyof typeof STAGE_LABELS] || event.stage,
|
|
253
|
-
stageColor: STAGE_COLORS[event.stage as keyof typeof STAGE_COLORS] || '#6b7280',
|
|
254
|
-
level: event.level,
|
|
255
|
-
message: event.message,
|
|
256
|
-
summary: event.summary,
|
|
257
|
-
metadata: event.metadata,
|
|
258
|
-
createdAt: event.createdAt,
|
|
259
|
-
}));
|
|
260
|
-
|
|
261
|
-
return {
|
|
262
|
-
items,
|
|
263
|
-
pagination: {
|
|
264
|
-
limit,
|
|
265
|
-
offset,
|
|
266
|
-
hasMore,
|
|
267
|
-
},
|
|
268
|
-
};
|
|
269
|
-
}
|
|
270
|
-
|
|
271
|
-
/**
|
|
272
|
-
* 获取完整追踪(单个 trace 的所有事件)
|
|
273
|
-
*/
|
|
274
|
-
getTrace(traceId: string): TraceDetailResponse | null {
|
|
275
|
-
// 获取任务信息
|
|
276
|
-
const task = this.trajectory.getEvolutionTaskByTraceId(traceId);
|
|
277
|
-
if (!task) return null;
|
|
278
|
-
|
|
279
|
-
// 获取所有事件
|
|
280
|
-
const events = this.trajectory.listEvolutionEvents(traceId, { limit: 1000 });
|
|
281
|
-
|
|
282
|
-
// 构建时间线
|
|
283
|
-
const timeline = events.map((event) => ({
|
|
284
|
-
stage: event.stage,
|
|
285
|
-
stageLabel: STAGE_LABELS[event.stage as keyof typeof STAGE_LABELS] || event.stage,
|
|
286
|
-
stageColor: STAGE_COLORS[event.stage as keyof typeof STAGE_COLORS] || '#6b7280',
|
|
287
|
-
timestamp: event.createdAt,
|
|
288
|
-
message: event.message,
|
|
289
|
-
summary: event.summary,
|
|
290
|
-
}));
|
|
291
|
-
|
|
292
|
-
return {
|
|
293
|
-
traceId,
|
|
294
|
-
task: {
|
|
295
|
-
taskId: task.taskId,
|
|
296
|
-
traceId: task.traceId,
|
|
297
|
-
source: task.source,
|
|
298
|
-
reason: task.reason,
|
|
299
|
-
score: task.score,
|
|
300
|
-
status: task.status,
|
|
301
|
-
enqueuedAt: task.enqueuedAt,
|
|
302
|
-
startedAt: task.startedAt,
|
|
303
|
-
completedAt: task.completedAt,
|
|
304
|
-
duration: calculateDuration(task),
|
|
305
|
-
resolution: task.resolution,
|
|
306
|
-
createdAt: task.createdAt,
|
|
307
|
-
updatedAt: task.updatedAt,
|
|
308
|
-
},
|
|
309
|
-
events: events.map((event) => ({
|
|
310
|
-
id: event.id,
|
|
311
|
-
traceId: event.traceId,
|
|
312
|
-
taskId: event.taskId,
|
|
313
|
-
stage: event.stage,
|
|
314
|
-
stageLabel: STAGE_LABELS[event.stage as keyof typeof STAGE_LABELS] || event.stage,
|
|
315
|
-
stageColor: STAGE_COLORS[event.stage as keyof typeof STAGE_COLORS] || '#6b7280',
|
|
316
|
-
level: event.level,
|
|
317
|
-
message: event.message,
|
|
318
|
-
summary: event.summary,
|
|
319
|
-
metadata: event.metadata,
|
|
320
|
-
createdAt: event.createdAt,
|
|
321
|
-
})),
|
|
322
|
-
timeline,
|
|
323
|
-
};
|
|
324
|
-
}
|
|
325
|
-
|
|
326
|
-
/**
|
|
327
|
-
* 获取统计数据
|
|
328
|
-
*/
|
|
329
|
-
getStats(days = 30): EvolutionStatsResponse {
|
|
330
|
-
// 获取基础统计
|
|
331
|
-
const stats = this.trajectory.getEvolutionStats();
|
|
332
|
-
|
|
333
|
-
// 获取近期活动
|
|
334
|
-
const now = new Date();
|
|
335
|
-
const daysAgo = new Date(now.getTime() - days * 24 * 60 * 60 * 1000);
|
|
336
|
-
const recentTasks = this.trajectory.listEvolutionTasks({
|
|
337
|
-
dateFrom: daysAgo.toISOString(),
|
|
338
|
-
limit: 10000,
|
|
339
|
-
});
|
|
340
|
-
|
|
341
|
-
// 按天分组
|
|
342
|
-
const activityByDay = new Map<string, { created: number; completed: number }>();
|
|
343
|
-
for (let i = 0; i < days; i++) {
|
|
344
|
-
const day = new Date(now.getTime() - i * 24 * 60 * 60 * 1000);
|
|
345
|
-
const [dayStr] = day.toISOString().split('T');
|
|
346
|
-
activityByDay.set(dayStr, { created: 0, completed: 0 });
|
|
347
|
-
}
|
|
348
|
-
|
|
349
|
-
for (const task of recentTasks) {
|
|
350
|
-
const [createdDay] = task.createdAt.split('T');
|
|
351
|
-
if (activityByDay.has(createdDay)) {
|
|
352
|
-
|
|
353
|
-
activityByDay.get(createdDay)!.created++;
|
|
354
|
-
}
|
|
355
|
-
if (task.completedAt) {
|
|
356
|
-
const [completedDay] = task.completedAt.split('T');
|
|
357
|
-
if (activityByDay.has(completedDay)) {
|
|
358
|
-
|
|
359
|
-
activityByDay.get(completedDay)!.completed++;
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
}
|
|
363
|
-
|
|
364
|
-
const recentActivity = Array.from(activityByDay.entries())
|
|
365
|
-
.map(([day, data]) => ({ day, ...data }))
|
|
366
|
-
.sort((a, b) => a.day.localeCompare(b.day));
|
|
367
|
-
|
|
368
|
-
// 获取阶段分布
|
|
369
|
-
const allEvents = this.trajectory.listEvolutionEvents(undefined, { limit: 10000 });
|
|
370
|
-
const stageCount = new Map<string, number>();
|
|
371
|
-
for (const event of allEvents) {
|
|
372
|
-
stageCount.set(event.stage, (stageCount.get(event.stage) || 0) + 1);
|
|
373
|
-
}
|
|
374
|
-
|
|
375
|
-
const stageDistribution = Array.from(stageCount.entries())
|
|
376
|
-
.map(([stage, count]) => ({
|
|
377
|
-
stage,
|
|
378
|
-
stageLabel: STAGE_LABELS[stage as keyof typeof STAGE_LABELS] || stage,
|
|
379
|
-
count,
|
|
380
|
-
}))
|
|
381
|
-
.sort((a, b) => b.count - a.count);
|
|
382
|
-
|
|
383
|
-
return {
|
|
384
|
-
...stats,
|
|
385
|
-
recentActivity,
|
|
386
|
-
stageDistribution,
|
|
387
|
-
};
|
|
388
|
-
}
|
|
389
|
-
}
|
|
390
|
-
|
|
391
|
-
// 单例缓存
|
|
392
|
-
const serviceCache = new Map<string, EvolutionQueryService>();
|
|
393
|
-
|
|
394
|
-
/**
|
|
395
|
-
* 获取 EvolutionQueryService 实例(单例)
|
|
396
|
-
*/
|
|
397
|
-
export function getEvolutionQueryService(trajectory: TrajectoryDatabase): EvolutionQueryService {
|
|
398
|
-
// 使用 trajectory 的 dbPath 作为缓存键
|
|
399
|
-
const cacheKey = (trajectory as unknown as { dbPath?: string }).dbPath || 'default';
|
|
400
|
-
const cached = serviceCache.get(cacheKey);
|
|
401
|
-
if (cached) {
|
|
402
|
-
return cached;
|
|
403
|
-
}
|
|
404
|
-
const service = new EvolutionQueryService(trajectory);
|
|
405
|
-
serviceCache.set(cacheKey, service);
|
|
406
|
-
return service;
|
|
407
|
-
}
|