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,101 +1,43 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Empathy Keyword Matcher
|
|
3
|
-
*
|
|
4
|
-
* Fast keyword-based empathy detection that replaces the previous
|
|
5
|
-
* LLM subagent-per-turn approach.
|
|
6
|
-
*
|
|
7
|
-
* Flow:
|
|
8
|
-
* User message → keyword matching → weighted score → GFI penalty
|
|
9
|
-
*
|
|
10
|
-
* The keyword store is periodically optimized by a subagent that analyzes
|
|
11
|
-
* recent conversations and updates keyword weights, discovers new terms,
|
|
12
|
-
* and removes false positives.
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
1
|
import * as fs from 'fs';
|
|
16
2
|
import * as path from 'path';
|
|
17
3
|
import { atomicWriteFileSync } from '../utils/io.js';
|
|
18
|
-
import
|
|
4
|
+
import { createDefaultKeywordStore } from '@principles/core/prompt-builder';
|
|
5
|
+
import type { EmpathyKeywordStore } from '@principles/core/prompt-builder';
|
|
6
|
+
|
|
7
|
+
export {
|
|
8
|
+
matchEmpathyKeywords,
|
|
9
|
+
createDefaultKeywordStore,
|
|
10
|
+
applyKeywordUpdates,
|
|
11
|
+
shouldTriggerOptimization,
|
|
12
|
+
getKeywordStoreSummary,
|
|
13
|
+
} from '@principles/core/prompt-builder';
|
|
14
|
+
|
|
15
|
+
export type {
|
|
19
16
|
EmpathyKeywordStore,
|
|
20
17
|
EmpathyKeywordEntry,
|
|
21
18
|
EmpathyKeywordStats,
|
|
22
19
|
EmpathyMatchResult,
|
|
23
|
-
EmpathyKeywordConfig
|
|
24
|
-
|
|
25
|
-
EMPATHY_SEED_KEYWORDS,
|
|
26
|
-
DEFAULT_EMPATHY_KEYWORD_CONFIG,
|
|
27
|
-
scoreToSeverity,
|
|
28
|
-
} from './empathy-types.js';
|
|
20
|
+
EmpathyKeywordConfig,
|
|
21
|
+
} from '@principles/core/prompt-builder';
|
|
29
22
|
|
|
30
23
|
const KEYWORD_STORE_FILE = 'empathy_keywords.json';
|
|
31
24
|
|
|
32
|
-
// =========================================================================
|
|
33
|
-
// Store Management
|
|
34
|
-
// =========================================================================
|
|
35
|
-
|
|
36
|
-
/**
|
|
37
|
-
* Creates a default keyword store from seed keywords.
|
|
38
|
-
* Supports both Chinese and English keywords.
|
|
39
|
-
*/
|
|
40
|
-
export function createDefaultKeywordStore(language: 'zh' | 'en' = 'zh'): EmpathyKeywordStore {
|
|
41
|
-
const now = new Date().toISOString();
|
|
42
|
-
const terms: Record<string, EmpathyKeywordEntry> = {};
|
|
43
|
-
|
|
44
|
-
// Include all seed keywords (both zh and en)
|
|
45
|
-
for (const seed of EMPATHY_SEED_KEYWORDS) {
|
|
46
|
-
// For Chinese language, include all keywords
|
|
47
|
-
// For English language, include only English keywords
|
|
48
|
-
const isChinese = /[\u4e00-\u9fa5]/.test(seed.term);
|
|
49
|
-
if (language === 'zh' || !isChinese) {
|
|
50
|
-
terms[seed.term] = {
|
|
51
|
-
weight: seed.weight,
|
|
52
|
-
source: 'seed',
|
|
53
|
-
hitCount: 0,
|
|
54
|
-
falsePositiveRate: seed.initialFalsePositiveRate ?? 0.15, // Differentiated FPR (Finding #6)
|
|
55
|
-
};
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
|
|
59
|
-
const stats: EmpathyKeywordStats = {
|
|
60
|
-
totalHits: 0,
|
|
61
|
-
totalFalsePositives: 0,
|
|
62
|
-
optimizationCount: 0,
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
return {
|
|
66
|
-
version: 1,
|
|
67
|
-
lastUpdated: now,
|
|
68
|
-
lastOptimizedAt: now,
|
|
69
|
-
terms,
|
|
70
|
-
stats,
|
|
71
|
-
};
|
|
72
|
-
}
|
|
73
|
-
|
|
74
|
-
/**
|
|
75
|
-
* Loads the keyword store from disk, or creates a default one if not found.
|
|
76
|
-
* Respects the configured language setting.
|
|
77
|
-
*/
|
|
78
25
|
export function loadKeywordStore(stateDir: string, language?: 'zh' | 'en'): EmpathyKeywordStore {
|
|
79
26
|
const filePath = path.join(stateDir, KEYWORD_STORE_FILE);
|
|
80
|
-
|
|
27
|
+
|
|
81
28
|
try {
|
|
82
29
|
if (!fs.existsSync(filePath)) {
|
|
83
30
|
const store = createDefaultKeywordStore(language);
|
|
84
|
-
|
|
85
|
-
|
|
86
31
|
saveKeywordStore(stateDir, store);
|
|
87
32
|
return store;
|
|
88
33
|
}
|
|
89
34
|
|
|
90
35
|
const raw = fs.readFileSync(filePath, 'utf8');
|
|
91
36
|
const parsed = JSON.parse(raw);
|
|
92
|
-
|
|
93
|
-
// Validate structure
|
|
37
|
+
|
|
94
38
|
if (!parsed.terms || !parsed.stats || !parsed.version) {
|
|
95
39
|
console.warn('[PD:Empathy] Invalid keyword store format, creating default');
|
|
96
40
|
const store = createDefaultKeywordStore(language);
|
|
97
|
-
|
|
98
|
-
|
|
99
41
|
saveKeywordStore(stateDir, store);
|
|
100
42
|
return store;
|
|
101
43
|
}
|
|
@@ -104,21 +46,15 @@ export function loadKeywordStore(stateDir: string, language?: 'zh' | 'en'): Empa
|
|
|
104
46
|
} catch (e) {
|
|
105
47
|
console.warn(`[PD:Empathy] Failed to load keyword store: ${e}`);
|
|
106
48
|
const store = createDefaultKeywordStore(language);
|
|
107
|
-
|
|
108
|
-
|
|
109
49
|
saveKeywordStore(stateDir, store);
|
|
110
50
|
return store;
|
|
111
51
|
}
|
|
112
52
|
}
|
|
113
53
|
|
|
114
|
-
/**
|
|
115
|
-
* Saves the keyword store to disk.
|
|
116
|
-
*/
|
|
117
|
-
|
|
118
54
|
export function saveKeywordStore(stateDir: string, store: EmpathyKeywordStore): void {
|
|
119
55
|
const filePath = path.join(stateDir, KEYWORD_STORE_FILE);
|
|
120
56
|
const dir = path.dirname(filePath);
|
|
121
|
-
|
|
57
|
+
|
|
122
58
|
if (!fs.existsSync(dir)) {
|
|
123
59
|
fs.mkdirSync(dir, { recursive: true });
|
|
124
60
|
}
|
|
@@ -126,211 +62,3 @@ export function saveKeywordStore(stateDir: string, store: EmpathyKeywordStore):
|
|
|
126
62
|
store.lastUpdated = new Date().toISOString();
|
|
127
63
|
atomicWriteFileSync(filePath, JSON.stringify(store, null, 2));
|
|
128
64
|
}
|
|
129
|
-
|
|
130
|
-
// =========================================================================
|
|
131
|
-
// Keyword Matching
|
|
132
|
-
// =========================================================================
|
|
133
|
-
|
|
134
|
-
/**
|
|
135
|
-
* Matches text against the keyword store and returns a structured result.
|
|
136
|
-
*
|
|
137
|
-
* This is the core function that replaces the previous LLM-based empathy detection.
|
|
138
|
-
* It runs in < 1ms for typical keyword stores (50-200 terms).
|
|
139
|
-
*/
|
|
140
|
-
export function matchEmpathyKeywords(
|
|
141
|
-
text: string,
|
|
142
|
-
store: EmpathyKeywordStore,
|
|
143
|
-
config: EmpathyKeywordConfig = DEFAULT_EMPATHY_KEYWORD_CONFIG,
|
|
144
|
-
): EmpathyMatchResult {
|
|
145
|
-
if (!text || typeof text !== 'string' || text.trim().length === 0) {
|
|
146
|
-
return {
|
|
147
|
-
matched: false,
|
|
148
|
-
score: 0,
|
|
149
|
-
matchedTerms: [],
|
|
150
|
-
severity: 'mild',
|
|
151
|
-
confidence: 0,
|
|
152
|
-
};
|
|
153
|
-
}
|
|
154
|
-
|
|
155
|
-
const lowerText = text.toLowerCase();
|
|
156
|
-
let totalScore = 0;
|
|
157
|
-
const matchedTerms: string[] = [];
|
|
158
|
-
|
|
159
|
-
for (const [term, entry] of Object.entries(store.terms)) {
|
|
160
|
-
if (lowerText.includes(term.toLowerCase())) {
|
|
161
|
-
// Weight adjusted by false positive rate
|
|
162
|
-
const adjustedWeight = entry.weight * (1 - entry.falsePositiveRate);
|
|
163
|
-
totalScore += adjustedWeight;
|
|
164
|
-
matchedTerms.push(term);
|
|
165
|
-
|
|
166
|
-
// Update hit stats
|
|
167
|
-
entry.hitCount++;
|
|
168
|
-
entry.lastHitAt = new Date().toISOString();
|
|
169
|
-
}
|
|
170
|
-
}
|
|
171
|
-
|
|
172
|
-
// Cap score at 1.0
|
|
173
|
-
const cappedScore = Math.min(1, totalScore);
|
|
174
|
-
|
|
175
|
-
// Only consider matched if score exceeds threshold
|
|
176
|
-
const isMatched = cappedScore >= config.matchThreshold && matchedTerms.length > 0;
|
|
177
|
-
|
|
178
|
-
// Limit matched terms for performance
|
|
179
|
-
const limitedTerms = matchedTerms.slice(0, config.maxTermsPerMessage);
|
|
180
|
-
|
|
181
|
-
// Calculate confidence based on:
|
|
182
|
-
// - Number of matched terms (more terms = higher confidence)
|
|
183
|
-
// - Score relative to threshold (higher score = higher confidence)
|
|
184
|
-
const termConfidence = Math.min(1, limitedTerms.length / 3);
|
|
185
|
-
const scoreConfidence = Math.min(1, cappedScore / 0.8);
|
|
186
|
-
const confidence = Math.max(termConfidence, scoreConfidence);
|
|
187
|
-
|
|
188
|
-
const result: EmpathyMatchResult = {
|
|
189
|
-
matched: isMatched,
|
|
190
|
-
score: cappedScore,
|
|
191
|
-
matchedTerms: limitedTerms,
|
|
192
|
-
severity: scoreToSeverity(cappedScore),
|
|
193
|
-
confidence,
|
|
194
|
-
};
|
|
195
|
-
|
|
196
|
-
// Update store stats
|
|
197
|
-
if (isMatched) {
|
|
198
|
-
store.stats.totalHits += limitedTerms.length;
|
|
199
|
-
}
|
|
200
|
-
|
|
201
|
-
return result;
|
|
202
|
-
}
|
|
203
|
-
|
|
204
|
-
// =========================================================================
|
|
205
|
-
// Keyword Store Updates
|
|
206
|
-
// =========================================================================
|
|
207
|
-
|
|
208
|
-
/**
|
|
209
|
-
* Applies keyword updates from subagent optimization.
|
|
210
|
-
*
|
|
211
|
-
* This is called when the empathy optimizer subagent completes its analysis
|
|
212
|
-
* and returns suggested updates to the keyword store.
|
|
213
|
-
*/
|
|
214
|
-
|
|
215
|
-
export function applyKeywordUpdates(
|
|
216
|
-
store: EmpathyKeywordStore,
|
|
217
|
-
updates: Record<string, {
|
|
218
|
-
action: 'add' | 'update' | 'remove';
|
|
219
|
-
weight?: number;
|
|
220
|
-
falsePositiveRate?: number;
|
|
221
|
-
examples?: string[];
|
|
222
|
-
reasoning?: string;
|
|
223
|
-
}>,
|
|
224
|
-
): { added: number; updated: number; removed: number } {
|
|
225
|
-
let added = 0;
|
|
226
|
-
let updated = 0;
|
|
227
|
-
let removed = 0;
|
|
228
|
-
const now = new Date().toISOString();
|
|
229
|
-
|
|
230
|
-
for (const [term, update] of Object.entries(updates)) {
|
|
231
|
-
switch (update.action) {
|
|
232
|
-
case 'add':
|
|
233
|
-
if (!store.terms[term]) {
|
|
234
|
-
store.terms[term] = {
|
|
235
|
-
weight: update.weight ?? 0.5,
|
|
236
|
-
source: 'llm_discovered',
|
|
237
|
-
hitCount: 0,
|
|
238
|
-
falsePositiveRate: update.falsePositiveRate ?? 0.2,
|
|
239
|
-
examples: update.examples,
|
|
240
|
-
discoveredAt: now,
|
|
241
|
-
};
|
|
242
|
-
added++;
|
|
243
|
-
}
|
|
244
|
-
break;
|
|
245
|
-
|
|
246
|
-
case 'update':
|
|
247
|
-
if (store.terms[term]) {
|
|
248
|
-
if (update.weight !== undefined) {
|
|
249
|
-
store.terms[term].weight = update.weight;
|
|
250
|
-
}
|
|
251
|
-
if (update.falsePositiveRate !== undefined) {
|
|
252
|
-
store.terms[term].falsePositiveRate = update.falsePositiveRate;
|
|
253
|
-
}
|
|
254
|
-
if (update.examples) {
|
|
255
|
-
store.terms[term].examples = update.examples;
|
|
256
|
-
}
|
|
257
|
-
updated++;
|
|
258
|
-
}
|
|
259
|
-
break;
|
|
260
|
-
|
|
261
|
-
case 'remove':
|
|
262
|
-
if (store.terms[term]) {
|
|
263
|
-
delete store.terms[term];
|
|
264
|
-
removed++;
|
|
265
|
-
}
|
|
266
|
-
break;
|
|
267
|
-
}
|
|
268
|
-
}
|
|
269
|
-
|
|
270
|
-
store.lastOptimizedAt = now;
|
|
271
|
-
store.stats.optimizationCount++;
|
|
272
|
-
|
|
273
|
-
return { added, updated, removed };
|
|
274
|
-
}
|
|
275
|
-
|
|
276
|
-
// =========================================================================
|
|
277
|
-
// Optimization Trigger
|
|
278
|
-
// =========================================================================
|
|
279
|
-
|
|
280
|
-
/**
|
|
281
|
-
* Checks if it's time to trigger subagent optimization.
|
|
282
|
-
*
|
|
283
|
-
* Returns true if either:
|
|
284
|
-
* - The number of turns since last optimization exceeds the interval
|
|
285
|
-
* - The time since last optimization exceeds the interval
|
|
286
|
-
*/
|
|
287
|
-
export function shouldTriggerOptimization(
|
|
288
|
-
store: EmpathyKeywordStore,
|
|
289
|
-
turnsSinceLastOptimization: number,
|
|
290
|
-
config: EmpathyKeywordConfig = DEFAULT_EMPATHY_KEYWORD_CONFIG,
|
|
291
|
-
): boolean {
|
|
292
|
-
const turnsExceeded = turnsSinceLastOptimization >= config.optimizationIntervalTurns;
|
|
293
|
-
|
|
294
|
-
const lastOpt = new Date(store.lastOptimizedAt).getTime();
|
|
295
|
-
const now = Date.now();
|
|
296
|
-
const timeExceeded = (now - lastOpt) >= config.optimizationIntervalMs;
|
|
297
|
-
|
|
298
|
-
return turnsExceeded || timeExceeded;
|
|
299
|
-
}
|
|
300
|
-
|
|
301
|
-
// =========================================================================
|
|
302
|
-
// Store Inspection
|
|
303
|
-
// =========================================================================
|
|
304
|
-
|
|
305
|
-
/**
|
|
306
|
-
* Returns a summary of the keyword store for debugging/monitoring.
|
|
307
|
-
*/
|
|
308
|
-
export function getKeywordStoreSummary(store: EmpathyKeywordStore): {
|
|
309
|
-
totalTerms: number;
|
|
310
|
-
seedTerms: number;
|
|
311
|
-
discoveredTerms: number;
|
|
312
|
-
topHitTerms: { term: string; hitCount: number; weight: number }[];
|
|
313
|
-
highFalsePositiveTerms: { term: string; falsePositiveRate: number }[];
|
|
314
|
-
} {
|
|
315
|
-
const terms = Object.entries(store.terms);
|
|
316
|
-
const seedTerms = terms.filter(([, e]) => e.source === 'seed');
|
|
317
|
-
const discoveredTerms = terms.filter(([, e]) => e.source === 'llm_discovered');
|
|
318
|
-
|
|
319
|
-
const topHitTerms = terms
|
|
320
|
-
.map(([term, entry]) => ({ term, hitCount: entry.hitCount, weight: entry.weight }))
|
|
321
|
-
.sort((a, b) => b.hitCount - a.hitCount)
|
|
322
|
-
.slice(0, 10);
|
|
323
|
-
|
|
324
|
-
const highFalsePositiveTerms = terms
|
|
325
|
-
.filter(([, e]) => e.falsePositiveRate > 0.3)
|
|
326
|
-
.map(([term, entry]) => ({ term, falsePositiveRate: entry.falsePositiveRate }))
|
|
327
|
-
.sort((a, b) => b.falsePositiveRate - a.falsePositiveRate);
|
|
328
|
-
|
|
329
|
-
return {
|
|
330
|
-
totalTerms: terms.length,
|
|
331
|
-
seedTerms: seedTerms.length,
|
|
332
|
-
discoveredTerms: discoveredTerms.length,
|
|
333
|
-
topHitTerms,
|
|
334
|
-
highFalsePositiveTerms,
|
|
335
|
-
};
|
|
336
|
-
}
|
|
@@ -1,229 +1,18 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
export interface EmpathyKeywordEntry {
|
|
22
|
-
/** 0-1, contribution to GFI when matched */
|
|
23
|
-
weight: number;
|
|
24
|
-
|
|
25
|
-
/** How this keyword was discovered */
|
|
26
|
-
source: 'seed' | 'llm_discovered' | 'user_reported';
|
|
27
|
-
|
|
28
|
-
/** Total times this keyword has matched */
|
|
29
|
-
hitCount: number;
|
|
30
|
-
|
|
31
|
-
/** Last time this keyword matched */
|
|
32
|
-
lastHitAt?: string;
|
|
33
|
-
|
|
34
|
-
/** 0-1, false positive rate calculated from subagent validation */
|
|
35
|
-
falsePositiveRate: number;
|
|
36
|
-
|
|
37
|
-
/** Example contexts where this keyword appeared */
|
|
38
|
-
examples?: string[];
|
|
39
|
-
|
|
40
|
-
/** When LLM first discovered this keyword */
|
|
41
|
-
discoveredAt?: string;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export interface EmpathyKeywordStats {
|
|
45
|
-
totalHits: number;
|
|
46
|
-
totalFalsePositives: number;
|
|
47
|
-
optimizationCount: number;
|
|
48
|
-
}
|
|
49
|
-
|
|
50
|
-
// =========================================================================
|
|
51
|
-
// Match Result
|
|
52
|
-
// =========================================================================
|
|
53
|
-
|
|
54
|
-
export interface EmpathyMatchResult {
|
|
55
|
-
/** Whether any keywords matched */
|
|
56
|
-
matched: boolean;
|
|
57
|
-
|
|
58
|
-
/** Weighted total score (0-1) */
|
|
59
|
-
score: number;
|
|
60
|
-
|
|
61
|
-
/** List of matched keyword terms */
|
|
62
|
-
matchedTerms: string[];
|
|
63
|
-
|
|
64
|
-
/** Derived severity level */
|
|
65
|
-
severity: 'mild' | 'moderate' | 'severe';
|
|
66
|
-
|
|
67
|
-
/** Confidence in the result (0-1) */
|
|
68
|
-
confidence: number;
|
|
69
|
-
}
|
|
70
|
-
|
|
71
|
-
// =========================================================================
|
|
72
|
-
// Keyword Update (from subagent optimization)
|
|
73
|
-
// =========================================================================
|
|
74
|
-
|
|
75
|
-
export interface EmpathyKeywordUpdate {
|
|
76
|
-
action: 'add' | 'update' | 'remove';
|
|
77
|
-
weight?: number;
|
|
78
|
-
falsePositiveRate?: number;
|
|
79
|
-
examples?: string[];
|
|
80
|
-
reasoning?: string;
|
|
81
|
-
}
|
|
82
|
-
|
|
83
|
-
export interface EmpathyOptimizationResult {
|
|
84
|
-
updates: Record<string, EmpathyKeywordUpdate>;
|
|
85
|
-
reasoning: string;
|
|
86
|
-
analyzedTurns: number;
|
|
87
|
-
newPatternsDiscovered: number;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
// =========================================================================
|
|
91
|
-
// Seed Keywords (preset list)
|
|
92
|
-
// =========================================================================
|
|
93
|
-
|
|
94
|
-
export interface SeedKeywordEntry {
|
|
95
|
-
term: string;
|
|
96
|
-
weight: number;
|
|
97
|
-
category: 'negation' | 'anger' | 'disappointment' | 'escalation';
|
|
98
|
-
/** Initial false positive rate — higher for generic words, lower for specific anger signals */
|
|
99
|
-
initialFalsePositiveRate?: number;
|
|
100
|
-
}
|
|
101
|
-
|
|
102
|
-
/**
|
|
103
|
-
* Preset seed keywords for empathy detection.
|
|
104
|
-
* These are the initial keywords before the LLM starts discovering new ones.
|
|
105
|
-
*/
|
|
106
|
-
export const EMPATHY_SEED_KEYWORDS: SeedKeywordEntry[] = [
|
|
107
|
-
// 否定词 (Negation) — generic, higher FPR
|
|
108
|
-
{ term: '不对', weight: 0.5, category: 'negation', initialFalsePositiveRate: 0.3 },
|
|
109
|
-
{ term: '错了', weight: 0.5, category: 'negation', initialFalsePositiveRate: 0.3 },
|
|
110
|
-
{ term: '搞错了', weight: 0.5, category: 'negation', initialFalsePositiveRate: 0.25 },
|
|
111
|
-
{ term: '不行', weight: 0.4, category: 'negation', initialFalsePositiveRate: 0.35 },
|
|
112
|
-
{ term: '没用', weight: 0.4, category: 'negation', initialFalsePositiveRate: 0.3 },
|
|
113
|
-
{ term: '重做', weight: 0.6, category: 'negation', initialFalsePositiveRate: 0.15 },
|
|
114
|
-
{ term: '重写', weight: 0.6, category: 'negation', initialFalsePositiveRate: 0.15 },
|
|
115
|
-
{ term: 'not right', weight: 0.5, category: 'negation', initialFalsePositiveRate: 0.3 },
|
|
116
|
-
{ term: 'wrong', weight: 0.5, category: 'negation', initialFalsePositiveRate: 0.3 },
|
|
117
|
-
{ term: 'redo', weight: 0.6, category: 'negation', initialFalsePositiveRate: 0.15 },
|
|
118
|
-
{ term: 'start over', weight: 0.6, category: 'negation', initialFalsePositiveRate: 0.15 },
|
|
119
|
-
|
|
120
|
-
// 愤怒表达 (Anger) — specific, lower FPR
|
|
121
|
-
{ term: '垃圾', weight: 0.9, category: 'anger', initialFalsePositiveRate: 0.05 },
|
|
122
|
-
{ term: '蠢', weight: 0.8, category: 'anger', initialFalsePositiveRate: 0.1 },
|
|
123
|
-
{ term: '废物', weight: 0.9, category: 'anger', initialFalsePositiveRate: 0.05 },
|
|
124
|
-
{ term: '白做', weight: 0.7, category: 'anger', initialFalsePositiveRate: 0.15 },
|
|
125
|
-
{ term: '浪费时间', weight: 0.8, category: 'anger', initialFalsePositiveRate: 0.1 },
|
|
126
|
-
{ term: 'garbage', weight: 0.9, category: 'anger', initialFalsePositiveRate: 0.05 },
|
|
127
|
-
{ term: 'stupid', weight: 0.8, category: 'anger', initialFalsePositiveRate: 0.1 },
|
|
128
|
-
{ term: 'useless', weight: 0.7, category: 'anger', initialFalsePositiveRate: 0.15 },
|
|
129
|
-
{ term: 'waste of time', weight: 0.8, category: 'anger', initialFalsePositiveRate: 0.1 },
|
|
130
|
-
|
|
131
|
-
// 失望信号 (Disappointment) — moderate FPR
|
|
132
|
-
{ term: '不行啊', weight: 0.5, category: 'disappointment', initialFalsePositiveRate: 0.25 },
|
|
133
|
-
{ term: '还是不对', weight: 0.6, category: 'disappointment', initialFalsePositiveRate: 0.2 },
|
|
134
|
-
{ term: '没解决', weight: 0.5, category: 'disappointment', initialFalsePositiveRate: 0.25 },
|
|
135
|
-
{ term: '没用上', weight: 0.5, category: 'disappointment', initialFalsePositiveRate: 0.25 },
|
|
136
|
-
{ term: '不能用', weight: 0.5, category: 'disappointment', initialFalsePositiveRate: 0.25 },
|
|
137
|
-
{ term: 'still not working', weight: 0.6, category: 'disappointment', initialFalsePositiveRate: 0.2 },
|
|
138
|
-
{ term: "doesn't help", weight: 0.5, category: 'disappointment', initialFalsePositiveRate: 0.25 },
|
|
139
|
-
{ term: 'not useful', weight: 0.5, category: 'disappointment', initialFalsePositiveRate: 0.25 },
|
|
140
|
-
|
|
141
|
-
// 升级信号 (Escalation) — specific context, lower FPR
|
|
142
|
-
{ term: '你自己看', weight: 0.8, category: 'escalation', initialFalsePositiveRate: 0.1 },
|
|
143
|
-
{ term: '你确定吗', weight: 0.7, category: 'escalation', initialFalsePositiveRate: 0.15 },
|
|
144
|
-
{ term: '你是不是没理解', weight: 0.8, category: 'escalation', initialFalsePositiveRate: 0.1 },
|
|
145
|
-
{ term: '你到底在干什么', weight: 0.9, category: 'escalation', initialFalsePositiveRate: 0.05 },
|
|
146
|
-
{ term: 'are you sure', weight: 0.7, category: 'escalation', initialFalsePositiveRate: 0.15 },
|
|
147
|
-
{ term: 'did you even read', weight: 0.8, category: 'escalation', initialFalsePositiveRate: 0.1 },
|
|
148
|
-
{ term: 'what are you doing', weight: 0.8, category: 'escalation', initialFalsePositiveRate: 0.1 },
|
|
149
|
-
];
|
|
150
|
-
|
|
151
|
-
// =========================================================================
|
|
152
|
-
// Configuration
|
|
153
|
-
// =========================================================================
|
|
154
|
-
|
|
155
|
-
export interface EmpathyKeywordConfig {
|
|
156
|
-
/** Minimum score to consider a match (0-1) */
|
|
157
|
-
matchThreshold: number;
|
|
158
|
-
|
|
159
|
-
/** Maximum number of terms to match per message */
|
|
160
|
-
maxTermsPerMessage: number;
|
|
161
|
-
|
|
162
|
-
/** How often to trigger subagent optimization (number of turns) */
|
|
163
|
-
optimizationIntervalTurns: number;
|
|
164
|
-
|
|
165
|
-
/** How often to trigger subagent optimization (time-based, ms) */
|
|
166
|
-
optimizationIntervalMs: number;
|
|
167
|
-
|
|
168
|
-
/** GFI penalty for mild severity */
|
|
169
|
-
penaltyMild: number;
|
|
170
|
-
|
|
171
|
-
/** GFI penalty for moderate severity */
|
|
172
|
-
penaltyModerate: number;
|
|
173
|
-
|
|
174
|
-
/** GFI penalty for severe severity */
|
|
175
|
-
penaltySevere: number;
|
|
176
|
-
}
|
|
177
|
-
|
|
178
|
-
export const DEFAULT_EMPATHY_KEYWORD_CONFIG: EmpathyKeywordConfig = {
|
|
179
|
-
matchThreshold: 0.3,
|
|
180
|
-
maxTermsPerMessage: 5,
|
|
181
|
-
optimizationIntervalTurns: 50,
|
|
182
|
-
optimizationIntervalMs: 6 * 60 * 60 * 1000, // 6 hours
|
|
183
|
-
penaltyMild: 10,
|
|
184
|
-
penaltyModerate: 25,
|
|
185
|
-
penaltySevere: 40,
|
|
186
|
-
};
|
|
187
|
-
|
|
188
|
-
// =========================================================================
|
|
189
|
-
// Severity Mapping
|
|
190
|
-
// =========================================================================
|
|
191
|
-
|
|
192
|
-
/**
|
|
193
|
-
* Maps a weighted score to a severity level.
|
|
194
|
-
*
|
|
195
|
-
* Score ranges:
|
|
196
|
-
* - 0.0 - 0.3: mild (single low-weight keyword)
|
|
197
|
-
* - 0.3 - 0.6: moderate (multiple keywords or high-weight keyword)
|
|
198
|
-
* - 0.6 - 1.0: severe (multiple high-weight keywords)
|
|
199
|
-
*/
|
|
200
|
-
export function scoreToSeverity(score: number): 'mild' | 'moderate' | 'severe' {
|
|
201
|
-
if (score >= 0.6) return 'severe';
|
|
202
|
-
if (score >= 0.3) return 'moderate';
|
|
203
|
-
return 'mild';
|
|
204
|
-
}
|
|
205
|
-
|
|
206
|
-
/**
|
|
207
|
-
* Maps severity to GFI penalty value.
|
|
208
|
-
*/
|
|
209
|
-
export function severityToPenalty(
|
|
210
|
-
severity: 'mild' | 'moderate' | 'severe',
|
|
211
|
-
config: EmpathyKeywordConfig = DEFAULT_EMPATHY_KEYWORD_CONFIG
|
|
212
|
-
): number {
|
|
213
|
-
switch (severity) {
|
|
214
|
-
case 'mild': return config.penaltyMild;
|
|
215
|
-
case 'moderate': return config.penaltyModerate;
|
|
216
|
-
case 'severe': return config.penaltySevere;
|
|
217
|
-
}
|
|
218
|
-
}
|
|
219
|
-
|
|
220
|
-
/**
|
|
221
|
-
* Normalizes various severity string inputs to the canonical empathy severity type.
|
|
222
|
-
* Handles common aliases: 'high' → 'severe', 'medium' → 'moderate'.
|
|
223
|
-
*/
|
|
224
|
-
export function normalizeSeverity(input?: string): 'mild' | 'moderate' | 'severe' {
|
|
225
|
-
const normalized = (input || '').toLowerCase();
|
|
226
|
-
if (normalized === 'severe' || normalized === 'high') return 'severe';
|
|
227
|
-
if (normalized === 'moderate' || normalized === 'medium') return 'moderate';
|
|
228
|
-
return 'mild';
|
|
229
|
-
}
|
|
1
|
+
export type {
|
|
2
|
+
EmpathyKeywordStore,
|
|
3
|
+
EmpathyKeywordEntry,
|
|
4
|
+
EmpathyKeywordStats,
|
|
5
|
+
EmpathyMatchResult,
|
|
6
|
+
EmpathyKeywordUpdate,
|
|
7
|
+
EmpathyOptimizationResult,
|
|
8
|
+
SeedKeywordEntry,
|
|
9
|
+
EmpathyKeywordConfig,
|
|
10
|
+
} from '@principles/core/prompt-builder';
|
|
11
|
+
|
|
12
|
+
export {
|
|
13
|
+
EMPATHY_SEED_KEYWORDS,
|
|
14
|
+
DEFAULT_EMPATHY_KEYWORD_CONFIG,
|
|
15
|
+
scoreToSeverity,
|
|
16
|
+
severityToPenalty,
|
|
17
|
+
normalizeSeverity,
|
|
18
|
+
} from '@principles/core/prompt-builder';
|