principles-disciple 1.72.0 → 1.74.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.
Files changed (319) hide show
  1. package/INSTALL.md +1 -3
  2. package/openclaw.plugin.json +10 -5
  3. package/package.json +17 -19
  4. package/scripts/acceptance-test.mjs +16 -73
  5. package/scripts/sync-plugin.mjs +382 -77
  6. package/src/commands/archive-impl.ts +2 -1
  7. package/src/commands/capabilities.ts +2 -2
  8. package/src/commands/context.ts +2 -2
  9. package/src/commands/disable-impl.ts +2 -1
  10. package/src/commands/evolution-status.ts +16 -16
  11. package/src/commands/export.ts +12 -67
  12. package/src/commands/pain.ts +91 -1
  13. package/src/commands/principle-rollback.ts +2 -1
  14. package/src/commands/promote-impl.ts +7 -43
  15. package/src/commands/rollback-impl.ts +2 -1
  16. package/src/commands/rollback.ts +2 -1
  17. package/src/commands/samples.ts +2 -1
  18. package/src/commands/thinking-os.ts +2 -1
  19. package/src/config/errors.ts +18 -2
  20. package/src/constants/diagnostician.ts +2 -2
  21. package/src/constants/tools.ts +2 -1
  22. package/src/core/__tests__/focus-history.test.ts +210 -0
  23. package/src/core/config.ts +1 -1
  24. package/src/core/correction-cue-learner.ts +2 -136
  25. package/src/core/correction-types.ts +16 -88
  26. package/src/core/dictionary.ts +19 -20
  27. package/src/core/empathy-keyword-matcher.ts +17 -289
  28. package/src/core/empathy-types.ts +18 -229
  29. package/src/core/event-log.ts +29 -132
  30. package/src/core/evolution-reducer.ts +21 -2
  31. package/src/core/evolution-types.ts +76 -464
  32. package/src/core/file-store.ts +80 -0
  33. package/src/core/focus-history.ts +228 -955
  34. package/src/core/local-worker-routing.ts +34 -314
  35. package/src/core/merge-gate-audit.ts +0 -195
  36. package/src/core/migration.ts +0 -1
  37. package/src/core/pain-diagnostic-gate.ts +154 -0
  38. package/src/core/pain-signal.ts +21 -138
  39. package/src/core/pain.ts +15 -88
  40. package/src/core/path-resolver.ts +0 -1
  41. package/src/core/paths.ts +0 -1
  42. package/src/core/pd-task-reconciler.ts +26 -115
  43. package/src/core/pd-task-service.ts +9 -9
  44. package/src/core/pd-task-types.ts +23 -127
  45. package/src/core/principle-compiler/__tests__/compiler-replay-gate.test.ts +174 -0
  46. package/src/core/principle-compiler/code-validator.ts +15 -42
  47. package/src/core/principle-compiler/compiler.ts +100 -15
  48. package/src/core/principle-compiler/index.ts +5 -2
  49. package/src/core/principle-compiler/template-generator.ts +4 -104
  50. package/src/core/principle-injection.ts +10 -202
  51. package/src/core/principle-internalization/filesystem-lifecycle-datasource.ts +42 -0
  52. package/src/core/principle-internalization/lifecycle-read-model.ts +39 -242
  53. package/src/core/principle-internalization/principle-lifecycle-service.ts +12 -10
  54. package/src/core/principle-tree-ledger-adapter.ts +145 -0
  55. package/src/core/principle-tree-ledger.ts +8 -6
  56. package/src/core/reflection/reflection-context.ts +14 -109
  57. package/src/core/replay-engine.ts +8 -500
  58. package/src/core/rule-host-helpers.ts +5 -35
  59. package/src/core/rule-host-types.ts +10 -82
  60. package/src/core/rule-host.ts +6 -63
  61. package/src/core/runtime-v2-prompt-activation-reader.ts +231 -0
  62. package/src/core/session-tracker.ts +87 -101
  63. package/src/core/shadow-observation-registry.ts +19 -48
  64. package/src/core/trajectory.ts +3 -1
  65. package/src/core/workflow-funnel-loader.ts +62 -68
  66. package/src/core/workspace-context.ts +46 -0
  67. package/src/core/workspace-dir-service.ts +1 -1
  68. package/src/core/workspace-dir-validation.ts +18 -9
  69. package/src/hooks/AGENTS.md +1 -1
  70. package/src/hooks/gate-block-helper.ts +71 -64
  71. package/src/hooks/gate.ts +183 -31
  72. package/src/hooks/lifecycle.ts +30 -32
  73. package/src/hooks/llm.ts +60 -32
  74. package/src/hooks/pain.ts +297 -103
  75. package/src/hooks/prompt.ts +400 -440
  76. package/src/hooks/subagent.ts +2 -29
  77. package/src/i18n/commands.ts +2 -10
  78. package/src/index.ts +95 -85
  79. package/src/openclaw-sdk.ts +311 -0
  80. package/src/service/central-database.ts +8 -4
  81. package/src/service/evolution-queue-migration.ts +2 -1
  82. package/src/service/evolution-worker.ts +163 -1786
  83. package/src/service/internalization-trigger-adapter.ts +302 -0
  84. package/src/service/keyword-optimization-service.ts +4 -4
  85. package/src/service/monitoring-query-service.ts +1 -215
  86. package/src/service/queue-io.ts +60 -331
  87. package/src/service/runtime-summary-service.ts +59 -16
  88. package/src/service/subagent-workflow/index.ts +0 -41
  89. package/src/service/subagent-workflow/types.ts +9 -120
  90. package/src/service/subagent-workflow/workflow-store.ts +2 -119
  91. package/src/service/workflow-watchdog.ts +0 -43
  92. package/src/types/event-payload.ts +16 -74
  93. package/src/types/event-types.ts +38 -547
  94. package/src/types/hygiene-types.ts +7 -30
  95. package/src/types/principle-tree-schema.ts +20 -222
  96. package/src/types/queue.ts +15 -70
  97. package/src/types/runtime-summary.ts +5 -49
  98. package/src/utils/io.ts +8 -20
  99. package/src/utils/retry.ts +1 -1
  100. package/src/utils/shadow-fingerprint.ts +2 -2
  101. package/src/utils/workspace-resolver.ts +50 -0
  102. package/templates/langs/en/core/AGENTS.md +7 -7
  103. package/templates/langs/en/core/BOOT.md +1 -1
  104. package/templates/langs/en/core/HEARTBEAT.md +2 -2
  105. package/templates/langs/en/principles/THINKING_OS.md +3 -2
  106. package/templates/langs/en/skills/ai-sprint-orchestration/references/agent-registry.json +1 -72
  107. package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/bugfix-complex-template.json +6 -6
  108. package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/feature-complex-template.json +6 -6
  109. package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal-verify.json +2 -12
  110. package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal.json +2 -12
  111. package/templates/langs/en/skills/ai-sprint-orchestration/scripts/run.mjs +51 -15
  112. package/templates/langs/en/skills/evolve-task/SKILL.md +3 -3
  113. package/templates/langs/en/skills/pd-cli-operator/SKILL.md +67 -0
  114. package/templates/langs/en/skills/pd-diagnostician/SKILL.md +1 -1
  115. package/templates/langs/en/skills/pd-mentor/SKILL.md +2 -3
  116. package/templates/langs/en/skills/pd-pain-signal/SKILL.md +17 -39
  117. package/templates/langs/en/skills/pd-runtime-v2/SKILL.md +61 -0
  118. package/templates/langs/zh/core/AGENTS.md +7 -7
  119. package/templates/langs/zh/core/BOOT.md +1 -1
  120. package/templates/langs/zh/core/HEARTBEAT.md +2 -2
  121. package/templates/langs/zh/principles/THINKING_OS.md +3 -2
  122. package/templates/langs/zh/skills/ai-sprint-orchestration/references/agent-registry.json +1 -72
  123. package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/bugfix-complex-template.json +6 -6
  124. package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/feature-complex-template.json +6 -6
  125. package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/nocturnal-trinity-quality-enhancement.json +8 -8
  126. package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal-verify.json +2 -12
  127. package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal.json +2 -12
  128. package/templates/langs/zh/skills/ai-sprint-orchestration/scripts/run.mjs +51 -15
  129. package/templates/langs/zh/skills/ai-sprint-orchestration/test/run.test.mjs +21 -5
  130. package/templates/langs/zh/skills/evolve-task/SKILL.md +4 -4
  131. package/templates/langs/zh/skills/pd-cli-operator/SKILL.md +67 -0
  132. package/templates/langs/zh/skills/pd-diagnostician/SKILL.md +1 -1
  133. package/templates/langs/zh/skills/pd-mentor/SKILL.md +2 -3
  134. package/templates/langs/zh/skills/pd-pain-signal/SKILL.md +17 -38
  135. package/templates/langs/zh/skills/pd-runtime-v2/SKILL.md +61 -0
  136. package/tests/build-artifacts.test.ts +1 -3
  137. package/tests/commands/evolution-status.test.ts +0 -118
  138. package/tests/core/bootstrap-rules.test.ts +1 -1
  139. package/tests/core/config.test.ts +1 -1
  140. package/tests/core/event-log.test.ts +35 -0
  141. package/tests/core/evolution-engine.test.ts +610 -0
  142. package/tests/core/file-store.test.ts +102 -0
  143. package/tests/core/focus-history.test.ts +203 -11
  144. package/tests/core/merge-gate-audit.test.ts +2 -169
  145. package/tests/core/migration.test.ts +7 -7
  146. package/tests/core/model-deployment-registry.test.ts +7 -1
  147. package/tests/core/model-training-registry.test.ts +19 -0
  148. package/tests/core/observability.test.ts +0 -1
  149. package/tests/core/pain-diagnostic-gate.test.ts +498 -0
  150. package/tests/core/pain.test.ts +0 -1
  151. package/tests/core/path-resolver.test.ts +1 -1
  152. package/tests/core/paths-refactor.test.ts +0 -22
  153. package/tests/core/principle-internalization/deprecated-readiness.test.ts +2 -2
  154. package/tests/core/principle-internalization/lifecycle-metrics.test.ts +2 -2
  155. package/tests/core/principle-internalization/{internalization-routing-policy.test.ts → lifecycle-routing-policy.test.ts} +6 -6
  156. package/tests/core/principle-internalization/lineage-source-retired.test.ts +56 -0
  157. package/tests/core/principle-internalization/principle-lifecycle-service.test.ts +1 -23
  158. package/tests/core/principle-tree-ledger-adapter.test.ts +253 -0
  159. package/tests/core/reflection-context.test.ts +0 -14
  160. package/tests/core/replay-engine.test.ts +127 -215
  161. package/tests/core/rule-host-helpers.test.ts +2 -2
  162. package/tests/core/rule-implementation-runtime.test.ts +0 -27
  163. package/tests/core/workflow-funnel-loader.test.ts +162 -0
  164. package/tests/core/workspace-context.test.ts +2 -2
  165. package/tests/core/workspace-dir-validation.test.ts +8 -1
  166. package/tests/core-anti-growth.test.ts +191 -0
  167. package/tests/hook-workspace-nextaction-contract.test.ts +42 -0
  168. package/tests/hooks/confirm-first-removal.test.ts +188 -0
  169. package/tests/hooks/gate-auto-correct-shadow.test.ts +310 -0
  170. package/tests/hooks/gate-auto-correct.test.ts +665 -0
  171. package/tests/hooks/gate-no-path-write-tool.test.ts +172 -0
  172. package/tests/hooks/gate-rule-host-pipeline.test.ts +2 -1
  173. package/tests/hooks/pain.test.ts +269 -12
  174. package/tests/hooks/prompt-characterization.test.ts +500 -0
  175. package/tests/hooks/prompt-size-guard.test.ts +32 -17
  176. package/tests/hooks/runtime-v2-prompt-activation.test.ts +869 -0
  177. package/tests/index.test.ts +94 -1
  178. package/tests/integration/auto-entry-gate.test.ts +248 -0
  179. package/tests/integration/internalization-trigger-guard.test.ts +69 -0
  180. package/tests/integration/m8-legacy-paths.test.ts +63 -0
  181. package/tests/integration/runtime-v2-pain-guard.test.ts +125 -0
  182. package/tests/plugin-config-resolution-cutover.test.ts +359 -0
  183. package/tests/runtime-v2-discovery-guard.test.ts +154 -0
  184. package/tests/service/central-database.test.ts +457 -0
  185. package/tests/service/evolution-worker.correction-observer.test.ts +173 -0
  186. package/tests/service/evolution-worker.timeout.test.ts +11 -129
  187. package/tests/service/internalization-trigger-adapter.test.ts +251 -0
  188. package/tests/service/monitoring-query-service.test.ts +1 -47
  189. package/tests/service/queue-io.test.ts +1 -62
  190. package/tests/service/runtime-summary-service.test.ts +3 -1
  191. package/tests/service/workflow-watchdog.test.ts +0 -91
  192. package/tests/utils/file-lock.test.ts +5 -3
  193. package/tests/utils/session-key.test.ts +52 -0
  194. package/tests/utils/subagent-probe.test.ts +48 -1
  195. package/vitest.config.ts +4 -11
  196. package/.planning/codebase/ARCHITECTURE.md +0 -157
  197. package/.planning/codebase/CONCERNS.md +0 -145
  198. package/.planning/codebase/CONVENTIONS.md +0 -148
  199. package/.planning/codebase/INTEGRATIONS.md +0 -81
  200. package/.planning/codebase/STACK.md +0 -87
  201. package/.planning/codebase/STRUCTURE.md +0 -193
  202. package/.planning/codebase/TESTING.md +0 -243
  203. package/.planning/phases/01-basic-visualization/01-GAP-CLOSURE-VERIFICATION.md +0 -113
  204. package/docs/COMMAND_REFERENCE.md +0 -76
  205. package/docs/COMMAND_REFERENCE_EN.md +0 -79
  206. package/scripts/build-web.mjs +0 -46
  207. package/scripts/diagnose-nocturnal.mjs +0 -537
  208. package/scripts/seed-nocturnal-scenarios.mjs +0 -384
  209. package/src/commands/nocturnal-review.ts +0 -322
  210. package/src/commands/nocturnal-rollout.ts +0 -790
  211. package/src/commands/nocturnal-train.ts +0 -986
  212. package/src/commands/pd-reflect.ts +0 -88
  213. package/src/core/adaptive-thresholds.ts +0 -478
  214. package/src/core/diagnostician-task-store.ts +0 -192
  215. package/src/core/nocturnal-arbiter.ts +0 -715
  216. package/src/core/nocturnal-artifact-lineage.ts +0 -116
  217. package/src/core/nocturnal-artificer.ts +0 -257
  218. package/src/core/nocturnal-candidate-scoring.ts +0 -530
  219. package/src/core/nocturnal-compliance.ts +0 -1146
  220. package/src/core/nocturnal-dataset.ts +0 -763
  221. package/src/core/nocturnal-executability.ts +0 -428
  222. package/src/core/nocturnal-export.ts +0 -499
  223. package/src/core/nocturnal-paths.ts +0 -240
  224. package/src/core/nocturnal-reasoning-deriver.ts +0 -343
  225. package/src/core/nocturnal-rule-implementation-validator.ts +0 -246
  226. package/src/core/nocturnal-snapshot-contract.ts +0 -99
  227. package/src/core/nocturnal-trajectory-extractor.ts +0 -512
  228. package/src/core/nocturnal-trinity-types.ts +0 -218
  229. package/src/core/nocturnal-trinity.ts +0 -2680
  230. package/src/core/principle-internalization/deprecated-readiness.ts +0 -93
  231. package/src/core/principle-internalization/internalization-routing-policy.ts +0 -208
  232. package/src/core/principle-internalization/lifecycle-metrics.ts +0 -152
  233. package/src/http/principles-console-route.ts +0 -709
  234. package/src/service/central-health-service.ts +0 -49
  235. package/src/service/central-overview-service.ts +0 -138
  236. package/src/service/control-ui-query-service.ts +0 -900
  237. package/src/service/cooldown-strategy.ts +0 -97
  238. package/src/service/evolution-pain-context.ts +0 -79
  239. package/src/service/evolution-query-service.ts +0 -407
  240. package/src/service/health-query-service.ts +0 -1038
  241. package/src/service/nocturnal-config.ts +0 -214
  242. package/src/service/nocturnal-runtime.ts +0 -734
  243. package/src/service/nocturnal-service.ts +0 -1605
  244. package/src/service/nocturnal-target-selector.ts +0 -545
  245. package/src/service/sleep-cycle.ts +0 -157
  246. package/src/service/startup-reconciler.ts +0 -112
  247. package/src/service/subagent-workflow/correction-observer-types.ts +0 -82
  248. package/src/service/subagent-workflow/correction-observer-workflow-manager.ts +0 -250
  249. package/src/service/subagent-workflow/deep-reflect-workflow-manager.ts +0 -1
  250. package/src/service/subagent-workflow/dynamic-timeout.ts +0 -30
  251. package/src/service/subagent-workflow/empathy-observer-workflow-manager.ts +0 -268
  252. package/src/service/subagent-workflow/nocturnal-workflow-manager.ts +0 -795
  253. package/src/service/subagent-workflow/runtime-direct-driver.ts +0 -268
  254. package/src/service/subagent-workflow/workflow-manager-base.ts +0 -580
  255. package/src/tools/write-pain-flag.ts +0 -215
  256. package/templates/langs/en/skills/plan-script/SKILL.md +0 -32
  257. package/templates/langs/zh/skills/plan-script/SKILL.md +0 -32
  258. package/tests/commands/nocturnal-review.test.ts +0 -448
  259. package/tests/commands/nocturnal-train.test.ts +0 -97
  260. package/tests/commands/pd-reflect.test.ts +0 -49
  261. package/tests/core/adaptive-thresholds.test.ts +0 -261
  262. package/tests/core/nocturnal-arbiter.test.ts +0 -559
  263. package/tests/core/nocturnal-artifact-lineage.test.ts +0 -53
  264. package/tests/core/nocturnal-artificer.test.ts +0 -241
  265. package/tests/core/nocturnal-candidate-scoring.test.ts +0 -532
  266. package/tests/core/nocturnal-compliance-p-principles.test.ts +0 -133
  267. package/tests/core/nocturnal-compliance.test.ts +0 -646
  268. package/tests/core/nocturnal-dataset.test.ts +0 -892
  269. package/tests/core/nocturnal-e2e.test.ts +0 -234
  270. package/tests/core/nocturnal-executability.test.ts +0 -357
  271. package/tests/core/nocturnal-export.test.ts +0 -517
  272. package/tests/core/nocturnal-reasoning-deriver.test.ts +0 -372
  273. package/tests/core/nocturnal-reviewed-subset-comparison.test.ts +0 -428
  274. package/tests/core/nocturnal-rule-implementation-validator.test.ts +0 -127
  275. package/tests/core/nocturnal-snapshot-contract.test.ts +0 -121
  276. package/tests/core/nocturnal-trajectory-extractor.test.ts +0 -634
  277. package/tests/core/nocturnal-trinity.test.ts +0 -2053
  278. package/tests/core/pain-auto-repair.test.ts +0 -96
  279. package/tests/core/pain-integration.test.ts +0 -510
  280. package/tests/fixtures/nocturnal-reviewed-subset.json +0 -183
  281. package/tests/http/principles-console-route.test.ts +0 -162
  282. package/tests/integration/chaos-resilience.test.ts +0 -348
  283. package/tests/integration/empathy-workflow-integration.test.ts +0 -626
  284. package/tests/integration/pain-diagnostician-loop.e2e.test.ts +0 -380
  285. package/tests/service/control-ui-query-service.test.ts +0 -121
  286. package/tests/service/cooldown-strategy.test.ts +0 -164
  287. package/tests/service/data-endpoints-regression.test.ts +0 -834
  288. package/tests/service/empathy-observer-workflow-manager.test.ts +0 -175
  289. package/tests/service/evolution-worker.nocturnal.test.ts +0 -601
  290. package/tests/service/nocturnal-runtime-hardening.test.ts +0 -118
  291. package/tests/service/nocturnal-runtime.test.ts +0 -473
  292. package/tests/service/nocturnal-service-code-candidate.test.ts +0 -330
  293. package/tests/service/nocturnal-target-selector.test.ts +0 -615
  294. package/tests/service/startup-reconciler.test.ts +0 -148
  295. package/tests/tools/write-pain-flag.test.ts +0 -358
  296. package/ui/src/App.tsx +0 -45
  297. package/ui/src/api.ts +0 -220
  298. package/ui/src/charts.tsx +0 -955
  299. package/ui/src/components/ErrorState.tsx +0 -6
  300. package/ui/src/components/Loading.tsx +0 -13
  301. package/ui/src/components/ProtectedRoute.tsx +0 -12
  302. package/ui/src/components/Shell.tsx +0 -91
  303. package/ui/src/components/WorkspaceConfig.tsx +0 -178
  304. package/ui/src/components/index.ts +0 -5
  305. package/ui/src/context/auth.tsx +0 -80
  306. package/ui/src/context/theme.tsx +0 -66
  307. package/ui/src/hooks/useAutoRefresh.ts +0 -39
  308. package/ui/src/i18n/ui.ts +0 -473
  309. package/ui/src/main.tsx +0 -16
  310. package/ui/src/pages/EvolutionPage.tsx +0 -333
  311. package/ui/src/pages/FeedbackPage.tsx +0 -138
  312. package/ui/src/pages/GateMonitorPage.tsx +0 -136
  313. package/ui/src/pages/LoginPage.tsx +0 -89
  314. package/ui/src/pages/OverviewPage.tsx +0 -599
  315. package/ui/src/pages/SamplesPage.tsx +0 -174
  316. package/ui/src/pages/ThinkingModelsPage.tsx +0 -702
  317. package/ui/src/styles.css +0 -2020
  318. package/ui/src/types.ts +0 -384
  319. 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 type {
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} from './empathy-types.js';
24
- import {
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
- * Empathy Keyword Matching Types
3
- *
4
- * Types for the dynamic keyword-based empathy detection system.
5
- * Replaces the previous LLM subagent-per-turn approach with fast keyword
6
- * matching + periodic subagent optimization of the keyword store.
7
- */
8
-
9
- // =========================================================================
10
- // Keyword Store
11
- // =========================================================================
12
-
13
- export interface EmpathyKeywordStore {
14
- version: number;
15
- lastUpdated: string;
16
- lastOptimizedAt: string;
17
- terms: Record<string, EmpathyKeywordEntry>;
18
- stats: EmpathyKeywordStats;
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';