principles-disciple 1.72.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 +459 -439
- 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 +59 -16
- 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 +32 -17
- 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 +3 -1
- 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
|
@@ -21,44 +21,19 @@ import {
|
|
|
21
21
|
CORRECTION_SEED_KEYWORDS,
|
|
22
22
|
MAX_CORRECTION_KEYWORDS,
|
|
23
23
|
} from './correction-types.js';
|
|
24
|
-
import { checkKeywordOptCooldown, recordKeywordOptRun } from '../service/nocturnal-runtime.js';
|
|
25
24
|
import { atomicWriteFileSync } from '../utils/io.js';
|
|
26
25
|
|
|
27
26
|
const KEYWORD_STORE_FILE = 'correction_keywords.json';
|
|
28
27
|
|
|
29
|
-
// CORR-08: Daily optimization throttle (uses checkCooldown in nocturnal-runtime.ts)
|
|
30
|
-
// Note: throttle state is stored in nocturnal-runtime.json, not a separate file.
|
|
31
|
-
|
|
32
|
-
// Weight bounds for correction keywords (D-39-03, D-39-15)
|
|
33
28
|
const MIN_KEYWORD_WEIGHT = 0.1;
|
|
34
29
|
const MAX_KEYWORD_WEIGHT = 0.9;
|
|
35
30
|
|
|
36
|
-
// =========================================================================
|
|
37
|
-
// Module-level cache (D-04, D-05)
|
|
38
|
-
// =========================================================================
|
|
39
|
-
|
|
40
|
-
/**
|
|
41
|
-
* Invalidated on every successful save so the next load re-reads from disk.
|
|
42
|
-
* Set to null intentionally — never assume disk and memory are in sync after a write.
|
|
43
|
-
*/
|
|
44
31
|
let _correctionCueCache: CorrectionKeywordStore | null = null;
|
|
45
32
|
|
|
46
|
-
/**
|
|
47
|
-
* Resets the module-level cache (for testing only).
|
|
48
|
-
* @internal
|
|
49
|
-
*/
|
|
50
33
|
export function _resetCorrectionCueCache(): void {
|
|
51
34
|
_correctionCueCache = null;
|
|
52
35
|
}
|
|
53
36
|
|
|
54
|
-
// =========================================================================
|
|
55
|
-
// Default store factory
|
|
56
|
-
// =========================================================================
|
|
57
|
-
|
|
58
|
-
/**
|
|
59
|
-
* Creates a fresh store populated with the 16 seed keywords (D-08, D-09).
|
|
60
|
-
* addedAt is stamped with the current ISO timestamp.
|
|
61
|
-
*/
|
|
62
37
|
function createDefaultStore(): CorrectionKeywordStore {
|
|
63
38
|
const now = new Date().toISOString();
|
|
64
39
|
const keywords: CorrectionKeyword[] = CORRECTION_SEED_KEYWORDS.map((k) => ({
|
|
@@ -69,14 +44,6 @@ function createDefaultStore(): CorrectionKeywordStore {
|
|
|
69
44
|
return { keywords, version: 1, lastOptimizedAt: now };
|
|
70
45
|
}
|
|
71
46
|
|
|
72
|
-
// =========================================================================
|
|
73
|
-
// Load / save
|
|
74
|
-
// =========================================================================
|
|
75
|
-
|
|
76
|
-
/**
|
|
77
|
-
* Loads the keyword store from disk.
|
|
78
|
-
* On first run (file absent) or parse failure, creates and persists the default store.
|
|
79
|
-
*/
|
|
80
47
|
export function loadCorrectionKeywordStore(stateDir: string): CorrectionKeywordStore {
|
|
81
48
|
if (_correctionCueCache) return _correctionCueCache;
|
|
82
49
|
|
|
@@ -88,23 +55,16 @@ export function loadCorrectionKeywordStore(stateDir: string): CorrectionKeywordS
|
|
|
88
55
|
_correctionCueCache = JSON.parse(raw) as CorrectionKeywordStore;
|
|
89
56
|
return _correctionCueCache;
|
|
90
57
|
} catch {
|
|
91
|
-
|
|
58
|
+
void 0;
|
|
92
59
|
}
|
|
93
60
|
}
|
|
94
61
|
|
|
95
|
-
// File absent or corrupt: seed the store and persist it (D-01)
|
|
96
62
|
const defaultStore = createDefaultStore();
|
|
97
63
|
saveCorrectionKeywordStore(stateDir, defaultStore);
|
|
98
64
|
_correctionCueCache = defaultStore;
|
|
99
65
|
return _correctionCueCache;
|
|
100
66
|
}
|
|
101
67
|
|
|
102
|
-
/**
|
|
103
|
-
* Atomically saves the keyword store to disk (D-03, T-38-02).
|
|
104
|
-
* Uses temp-file-then-rename to ensure the file is always valid JSON or
|
|
105
|
-
* the previous valid state if a crash occurs mid-write.
|
|
106
|
-
* MUST invalidate the cache after the rename (D-05).
|
|
107
|
-
*/
|
|
108
68
|
export function saveCorrectionKeywordStore(
|
|
109
69
|
stateDir: string,
|
|
110
70
|
store: CorrectionKeywordStore
|
|
@@ -114,29 +74,17 @@ export function saveCorrectionKeywordStore(
|
|
|
114
74
|
fs.mkdirSync(stateDir, { recursive: true });
|
|
115
75
|
atomicWriteFileSync(filePath, JSON.stringify(store, null, 2));
|
|
116
76
|
|
|
117
|
-
// Invalidate cache so the next read re-loads from disk (D-05)
|
|
118
77
|
_correctionCueCache = null;
|
|
119
78
|
}
|
|
120
79
|
|
|
121
|
-
// =========================================================================
|
|
122
|
-
// Throttle helpers (CORR-08)
|
|
123
|
-
// =========================================================================
|
|
124
|
-
// Singleton state
|
|
125
|
-
// =========================================================================
|
|
126
|
-
|
|
127
80
|
let _instance: CorrectionCueLearner | null = null;
|
|
128
81
|
let _lastStateDir: string | null = null;
|
|
129
82
|
|
|
130
|
-
/** Resets singleton state (for testing only). @internal */
|
|
131
83
|
export function _resetCorrectionCueLearnerInstance(): void {
|
|
132
84
|
_instance = null;
|
|
133
85
|
_lastStateDir = null;
|
|
134
86
|
}
|
|
135
87
|
|
|
136
|
-
// =========================================================================
|
|
137
|
-
// CorrectionCueLearner class
|
|
138
|
-
// =========================================================================
|
|
139
|
-
|
|
140
88
|
export class CorrectionCueLearner {
|
|
141
89
|
private readonly store: CorrectionKeywordStore;
|
|
142
90
|
private readonly stateDir: string;
|
|
@@ -146,17 +94,6 @@ export class CorrectionCueLearner {
|
|
|
146
94
|
this.store = loadCorrectionKeywordStore(stateDir);
|
|
147
95
|
}
|
|
148
96
|
|
|
149
|
-
// ── Public API ──────────────────────────────────────────────────────────
|
|
150
|
-
|
|
151
|
-
/**
|
|
152
|
-
* Checks whether text contains a correction cue (D-11).
|
|
153
|
-
* Pure read-only — does NOT modify the store.
|
|
154
|
-
* Normalisation is equivalent to the original detectCorrectionCue():
|
|
155
|
-
* trim → lowercase → strip punctuation
|
|
156
|
-
* Returns weighted score based on keyword accuracy (D-39-03, D-39-04).
|
|
157
|
-
*
|
|
158
|
-
* To record hits/TPs, call recordHit() and recordTruePositive() separately.
|
|
159
|
-
*/
|
|
160
97
|
match(text: string): CorrectionMatchResult {
|
|
161
98
|
const normalized = text
|
|
162
99
|
.trim()
|
|
@@ -168,9 +105,6 @@ export class CorrectionCueLearner {
|
|
|
168
105
|
|
|
169
106
|
for (const keyword of this.store.keywords) {
|
|
170
107
|
if (normalized.includes(keyword.term.toLowerCase())) {
|
|
171
|
-
// D-39-03, D-39-04: Weighted score formula
|
|
172
|
-
// No history (tp=0, fp=0) → accuracy = 1 (trust raw weight)
|
|
173
|
-
// Has history → accuracy = tp / (tp + fp) (proportional to true positive rate)
|
|
174
108
|
const tp = keyword.truePositiveCount ?? 0;
|
|
175
109
|
const fp = keyword.falsePositiveCount ?? 0;
|
|
176
110
|
const accuracy = (tp + fp) > 0 ? tp / (tp + fp) : 1;
|
|
@@ -184,7 +118,6 @@ export class CorrectionCueLearner {
|
|
|
184
118
|
const cappedScore = Math.min(1, totalScore);
|
|
185
119
|
const isMatched = matchedTerms.length > 0;
|
|
186
120
|
|
|
187
|
-
// D-39-04: Confidence derived from multiple signals
|
|
188
121
|
const termConfidence = Math.min(1, matchedTerms.length / 3);
|
|
189
122
|
const scoreConfidence = Math.min(1, cappedScore / 0.8);
|
|
190
123
|
const confidence = Math.max(termConfidence, scoreConfidence);
|
|
@@ -197,12 +130,6 @@ export class CorrectionCueLearner {
|
|
|
197
130
|
};
|
|
198
131
|
}
|
|
199
132
|
|
|
200
|
-
/**
|
|
201
|
-
* Records a keyword hit (for hitCount/FPR tracking).
|
|
202
|
-
* Increments hitCount and updates lastHitAt for all matched terms.
|
|
203
|
-
* Intentionally does NOT flush — hitCount is best-effort analytics,
|
|
204
|
-
* persisted by the next recordTruePositive() or flush() call.
|
|
205
|
-
*/
|
|
206
133
|
recordHits(terms: string[]): void {
|
|
207
134
|
for (const term of terms) {
|
|
208
135
|
const keywordIndex = this.store.keywords.findIndex(k => k.term.toLowerCase() === term.toLowerCase());
|
|
@@ -216,17 +143,12 @@ export class CorrectionCueLearner {
|
|
|
216
143
|
}
|
|
217
144
|
}
|
|
218
145
|
|
|
219
|
-
/**
|
|
220
|
-
* Records a confirmed true positive for the given keyword term.
|
|
221
|
-
* Increments truePositiveCount atomically.
|
|
222
|
-
*/
|
|
223
146
|
recordTruePositive(term: string): void {
|
|
224
147
|
const keyword = this.store.keywords.find(k => k.term.toLowerCase() === term.toLowerCase());
|
|
225
148
|
if (!keyword) return;
|
|
226
149
|
|
|
227
150
|
keyword.truePositiveCount = (keyword.truePositiveCount ?? 0) + 1;
|
|
228
151
|
|
|
229
|
-
// Update in-store reference
|
|
230
152
|
const keywordIndex = this.store.keywords.findIndex(k => k.term.toLowerCase() === term.toLowerCase());
|
|
231
153
|
if (keywordIndex >= 0) {
|
|
232
154
|
this.store.keywords[keywordIndex] = { ...keyword };
|
|
@@ -235,61 +157,23 @@ export class CorrectionCueLearner {
|
|
|
235
157
|
this.flush();
|
|
236
158
|
}
|
|
237
159
|
|
|
238
|
-
/**
|
|
239
|
-
* Records a confirmed false positive for the given keyword term.
|
|
240
|
-
* CORR-10: Decreases keyword weight by 20% (x0.8 multiplicative factor).
|
|
241
|
-
* D-39-17: Keywords at very low weight (<0.1) still match but contribute minimally.
|
|
242
|
-
*/
|
|
243
160
|
recordFalsePositive(term: string): void {
|
|
244
161
|
const keyword = this.store.keywords.find(k => k.term.toLowerCase() === term.toLowerCase());
|
|
245
162
|
if (!keyword) return;
|
|
246
163
|
|
|
247
164
|
keyword.falsePositiveCount = (keyword.falsePositiveCount ?? 0) + 1;
|
|
248
165
|
|
|
249
|
-
// D-39-15: Multiplicative weight decay x0.8 on confirmed FP
|
|
250
166
|
keyword.weight = Math.max(MIN_KEYWORD_WEIGHT, keyword.weight * 0.8);
|
|
251
167
|
keyword.lastHitAt = new Date().toISOString();
|
|
252
168
|
|
|
253
|
-
// Update in-store reference
|
|
254
169
|
const keywordIndex = this.store.keywords.findIndex(k => k.term.toLowerCase() === term.toLowerCase());
|
|
255
170
|
if (keywordIndex >= 0) {
|
|
256
171
|
this.store.keywords[keywordIndex] = { ...keyword };
|
|
257
172
|
}
|
|
258
173
|
|
|
259
|
-
// D-39-16: Apply decay BEFORE flush to disk
|
|
260
174
|
this.flush();
|
|
261
175
|
}
|
|
262
176
|
|
|
263
|
-
/**
|
|
264
|
-
* Returns true if optimization is allowed (within daily throttle limit).
|
|
265
|
-
* CORR-08: Max 4 optimizations per day across all triggers.
|
|
266
|
-
*/
|
|
267
|
-
canRunKeywordOptimization(): boolean {
|
|
268
|
-
// D-39-12, D-39-13: Per-workspace throttle, 4 calls/day
|
|
269
|
-
// Uses dedicated keywordOptRunTimestamps array to avoid pollution from regular nocturnal runs (#321)
|
|
270
|
-
const cooldown = checkKeywordOptCooldown(this.stateDir, {
|
|
271
|
-
maxRunsPerWindow: 4,
|
|
272
|
-
quotaWindowMs: 24 * 60 * 60 * 1000,
|
|
273
|
-
});
|
|
274
|
-
return !cooldown.quotaExhausted;
|
|
275
|
-
}
|
|
276
|
-
|
|
277
|
-
/**
|
|
278
|
-
* Records that an optimization was performed.
|
|
279
|
-
* Updates lastOptimizedAt for the store and records the run in the
|
|
280
|
-
* keyword-optimization quota (dedicated from regular nocturnal quota).
|
|
281
|
-
* @throws Error if quota recording fails — caller should propagate
|
|
282
|
-
*/
|
|
283
|
-
async recordOptimizationPerformed(): Promise<void> {
|
|
284
|
-
this.store.lastOptimizedAt = new Date().toISOString();
|
|
285
|
-
this.flush();
|
|
286
|
-
await recordKeywordOptRun(this.stateDir);
|
|
287
|
-
}
|
|
288
|
-
|
|
289
|
-
/**
|
|
290
|
-
* Adds a new keyword to the store and immediately flushes (D-06, D-07).
|
|
291
|
-
* Throws if the 200-term limit would be exceeded.
|
|
292
|
-
*/
|
|
293
177
|
add(keyword: Omit<CorrectionKeyword, 'addedAt'>): void {
|
|
294
178
|
if (this.store.keywords.length >= MAX_CORRECTION_KEYWORDS) {
|
|
295
179
|
throw new Error('Correction keyword store limit reached (200 terms)');
|
|
@@ -304,11 +188,6 @@ export class CorrectionCueLearner {
|
|
|
304
188
|
this.flush();
|
|
305
189
|
}
|
|
306
190
|
|
|
307
|
-
/**
|
|
308
|
-
* Updates the weight of an existing keyword.
|
|
309
|
-
* Weight is clamped to 0.1-0.9 range.
|
|
310
|
-
* Throws if keyword not found.
|
|
311
|
-
*/
|
|
312
191
|
updateWeight(term: string, weight: number): void {
|
|
313
192
|
const keyword = this.store.keywords.find(
|
|
314
193
|
k => k.term.toLowerCase() === term.toLowerCase()
|
|
@@ -317,7 +196,7 @@ export class CorrectionCueLearner {
|
|
|
317
196
|
throw new Error(`Keyword not found: ${term}`);
|
|
318
197
|
}
|
|
319
198
|
|
|
320
|
-
keyword.weight = Math.max(MIN_KEYWORD_WEIGHT, Math.min(MAX_KEYWORD_WEIGHT, weight));
|
|
199
|
+
keyword.weight = Math.max(MIN_KEYWORD_WEIGHT, Math.min(MAX_KEYWORD_WEIGHT, weight));
|
|
321
200
|
const idx = this.store.keywords.findIndex(
|
|
322
201
|
k => k.term.toLowerCase() === term.toLowerCase()
|
|
323
202
|
);
|
|
@@ -327,10 +206,6 @@ export class CorrectionCueLearner {
|
|
|
327
206
|
this.flush();
|
|
328
207
|
}
|
|
329
208
|
|
|
330
|
-
/**
|
|
331
|
-
* Removes a keyword from the store by term.
|
|
332
|
-
* Throws if keyword not found.
|
|
333
|
-
*/
|
|
334
209
|
remove(term: string): void {
|
|
335
210
|
const idx = this.store.keywords.findIndex(
|
|
336
211
|
k => k.term.toLowerCase() === term.toLowerCase()
|
|
@@ -342,27 +217,18 @@ export class CorrectionCueLearner {
|
|
|
342
217
|
this.flush();
|
|
343
218
|
}
|
|
344
219
|
|
|
345
|
-
/** Returns a reference to the in-memory store. */
|
|
346
220
|
getStore(): CorrectionKeywordStore {
|
|
347
221
|
return this.store;
|
|
348
222
|
}
|
|
349
223
|
|
|
350
|
-
/** Returns the lastOptimizedAt timestamp. */
|
|
351
224
|
getLastOptimizedAt(): string {
|
|
352
225
|
return this.store.lastOptimizedAt;
|
|
353
226
|
}
|
|
354
227
|
|
|
355
|
-
/** Persists the current in-memory store to disk atomically. */
|
|
356
228
|
flush(): void {
|
|
357
229
|
saveCorrectionKeywordStore(this.stateDir, this.store);
|
|
358
230
|
}
|
|
359
231
|
|
|
360
|
-
// ── Singleton factory ───────────────────────────────────────────────────
|
|
361
|
-
|
|
362
|
-
/**
|
|
363
|
-
* Returns the shared CorrectionCueLearner instance for a given stateDir.
|
|
364
|
-
* Re-creates the instance if stateDir changes (e.g. workspace switch).
|
|
365
|
-
*/
|
|
366
232
|
static get(stateDir: string): CorrectionCueLearner {
|
|
367
233
|
if (!_instance || _lastStateDir !== stateDir) {
|
|
368
234
|
_instance = new CorrectionCueLearner(stateDir);
|
|
@@ -1,88 +1,16 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
export
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
weight: number;
|
|
18
|
-
/** How this keyword was introduced */
|
|
19
|
-
source: 'seed' | 'llm' | 'user';
|
|
20
|
-
/** ISO 8601 timestamp of when this keyword was added */
|
|
21
|
-
addedAt: string;
|
|
22
|
-
/** Total times this keyword has matched (default: 0) */
|
|
23
|
-
hitCount?: number;
|
|
24
|
-
/** Confirmed correct matches (default: 0) */
|
|
25
|
-
truePositiveCount?: number;
|
|
26
|
-
/** Confirmed incorrect matches (default: 0) */
|
|
27
|
-
falsePositiveCount?: number;
|
|
28
|
-
/** Last time this keyword matched (ISO timestamp) */
|
|
29
|
-
lastHitAt?: string;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
export interface CorrectionKeywordStore {
|
|
33
|
-
/** All correction keywords */
|
|
34
|
-
keywords: CorrectionKeyword[];
|
|
35
|
-
/** Schema version */
|
|
36
|
-
version: number;
|
|
37
|
-
/** Last time keyword optimization was performed (ISO timestamp) */
|
|
38
|
-
lastOptimizedAt: string;
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
// =========================================================================
|
|
42
|
-
// Match Result
|
|
43
|
-
// =========================================================================
|
|
44
|
-
|
|
45
|
-
export interface CorrectionMatchResult {
|
|
46
|
-
/** Whether any keyword matched */
|
|
47
|
-
matched: boolean;
|
|
48
|
-
/** Matched terms (empty array when no match; may be truncated to first N items) */
|
|
49
|
-
matchedTerms: string[];
|
|
50
|
-
/** Weighted score (0-1) based on keyword weight and accuracy */
|
|
51
|
-
score: number;
|
|
52
|
-
/** Confidence in the match result (0-1) */
|
|
53
|
-
confidence: number;
|
|
54
|
-
}
|
|
55
|
-
|
|
56
|
-
// =========================================================================
|
|
57
|
-
// Seed Keywords (16 terms — sourced from detectCorrectionCue)
|
|
58
|
-
// =========================================================================
|
|
59
|
-
|
|
60
|
-
/** Maximum number of keywords the store may hold (D-06). */
|
|
61
|
-
export const MAX_CORRECTION_KEYWORDS = 200;
|
|
62
|
-
|
|
63
|
-
/**
|
|
64
|
-
* Preset seed keywords for correction cue detection.
|
|
65
|
-
* Mirrors the hardcoded list in detectCorrectionCue() exactly (D-08).
|
|
66
|
-
* addedAt is intentionally empty here — it is filled in at runtime by
|
|
67
|
-
* createDefaultStore() when the store is first persisted to disk.
|
|
68
|
-
*/
|
|
69
|
-
export const CORRECTION_SEED_KEYWORDS: CorrectionKeyword[] = [
|
|
70
|
-
// Chinese (8)
|
|
71
|
-
{ term: '不是这个', weight: 0.6, source: 'seed', addedAt: '' },
|
|
72
|
-
{ term: '不对', weight: 0.5, source: 'seed', addedAt: '' },
|
|
73
|
-
{ term: '错了', weight: 0.5, source: 'seed', addedAt: '' },
|
|
74
|
-
{ term: '搞错了', weight: 0.7, source: 'seed', addedAt: '' },
|
|
75
|
-
{ term: '理解错了', weight: 0.7, source: 'seed', addedAt: '' },
|
|
76
|
-
{ term: '你理解错了', weight: 0.8, source: 'seed', addedAt: '' },
|
|
77
|
-
{ term: '重新来', weight: 0.6, source: 'seed', addedAt: '' },
|
|
78
|
-
{ term: '再试一次', weight: 0.4, source: 'seed', addedAt: '' },
|
|
79
|
-
// English (8)
|
|
80
|
-
{ term: 'you are wrong', weight: 0.7, source: 'seed', addedAt: '' },
|
|
81
|
-
{ term: 'wrong file', weight: 0.6, source: 'seed', addedAt: '' },
|
|
82
|
-
{ term: 'not this', weight: 0.4, source: 'seed', addedAt: '' },
|
|
83
|
-
{ term: 'redo', weight: 0.6, source: 'seed', addedAt: '' },
|
|
84
|
-
{ term: 'try again', weight: 0.4, source: 'seed', addedAt: '' },
|
|
85
|
-
{ term: 'again', weight: 0.3, source: 'seed', addedAt: '' },
|
|
86
|
-
{ term: 'please redo', weight: 0.6, source: 'seed', addedAt: '' },
|
|
87
|
-
{ term: 'please try again', weight: 0.5, source: 'seed', addedAt: '' },
|
|
88
|
-
];
|
|
1
|
+
import type {
|
|
2
|
+
CorrectionKeyword,
|
|
3
|
+
CorrectionKeywordStore,
|
|
4
|
+
CorrectionMatchResult,
|
|
5
|
+
} from '@principles/core/runtime-v2';
|
|
6
|
+
|
|
7
|
+
export type {
|
|
8
|
+
CorrectionKeyword,
|
|
9
|
+
CorrectionKeywordStore,
|
|
10
|
+
CorrectionMatchResult,
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
export {
|
|
14
|
+
MAX_CORRECTION_KEYWORDS,
|
|
15
|
+
CORRECTION_SEED_KEYWORDS,
|
|
16
|
+
} from '@principles/core/runtime-v2';
|
package/src/core/dictionary.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
|
|
2
2
|
import * as fs from 'fs';
|
|
3
3
|
import * as path from 'path';
|
|
4
|
-
import {
|
|
4
|
+
import { JsonFileStore } from './file-store.js';
|
|
5
5
|
|
|
6
6
|
export type RuleType = 'regex' | 'exact_match';
|
|
7
7
|
|
|
@@ -64,25 +64,31 @@ const DEFAULT_RULES: Record<string, PainRule> = {
|
|
|
64
64
|
|
|
65
65
|
export class PainDictionary {
|
|
66
66
|
private data: PainDictionaryData = { rules: {} };
|
|
67
|
-
private readonly
|
|
67
|
+
private readonly store: JsonFileStore<PainDictionaryData>;
|
|
68
68
|
private readonly compiledRegex: Map<string, RegExp> = new Map();
|
|
69
69
|
|
|
70
70
|
constructor(private readonly stateDir: string) {
|
|
71
|
-
|
|
71
|
+
const filePath = path.join(stateDir, 'pain_dictionary.json');
|
|
72
|
+
this.store = new JsonFileStore<PainDictionaryData>(filePath, () => ({ rules: { ...DEFAULT_RULES } }));
|
|
72
73
|
}
|
|
73
74
|
|
|
74
75
|
load(): void {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
76
|
+
const filePath = path.join(this.stateDir, 'pain_dictionary.json');
|
|
77
|
+
const fileExisted = fs.existsSync(filePath);
|
|
78
|
+
const loaded = this.store.load();
|
|
79
|
+
// Check if we got real data (has at least one rule from file) or just defaults
|
|
80
|
+
const hasRules = loaded.rules && Object.keys(loaded.rules).length > 0;
|
|
81
|
+
if (hasRules) {
|
|
82
|
+
this.data = loaded;
|
|
82
83
|
} else {
|
|
83
84
|
this.data = { rules: { ...DEFAULT_RULES } };
|
|
84
|
-
|
|
85
|
-
|
|
85
|
+
// Only overwrite if file didn't previously exist — preserve corrupt files
|
|
86
|
+
if (!fileExisted) {
|
|
87
|
+
console.log(`[PD:Dictionary] Dictionary not found, creating with default rules`);
|
|
88
|
+
this.flush();
|
|
89
|
+
} else {
|
|
90
|
+
console.warn(`[PD:Dictionary] Dictionary corrupt or empty, preserving file and using defaults`);
|
|
91
|
+
}
|
|
86
92
|
}
|
|
87
93
|
this.compile();
|
|
88
94
|
}
|
|
@@ -152,14 +158,7 @@ export class PainDictionary {
|
|
|
152
158
|
}
|
|
153
159
|
|
|
154
160
|
flush(): void {
|
|
155
|
-
|
|
156
|
-
if (!fs.existsSync(this.stateDir)) {
|
|
157
|
-
fs.mkdirSync(this.stateDir, { recursive: true });
|
|
158
|
-
}
|
|
159
|
-
atomicWriteFileSync(this.filePath, JSON.stringify(this.data, null, 2));
|
|
160
|
-
} catch (e) {
|
|
161
|
-
console.error('[PD] Failed to flush pain_dictionary.json:', e);
|
|
162
|
-
}
|
|
161
|
+
this.store.save(this.data);
|
|
163
162
|
}
|
|
164
163
|
|
|
165
164
|
getStats(): { totalRules: number; totalHits: number } {
|