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.
Files changed (309) hide show
  1. package/openclaw.plugin.json +10 -5
  2. package/package.json +17 -19
  3. package/scripts/acceptance-test.mjs +16 -73
  4. package/scripts/sync-plugin.mjs +382 -77
  5. package/src/commands/archive-impl.ts +2 -1
  6. package/src/commands/capabilities.ts +2 -2
  7. package/src/commands/context.ts +2 -2
  8. package/src/commands/disable-impl.ts +2 -1
  9. package/src/commands/evolution-status.ts +16 -16
  10. package/src/commands/export.ts +12 -67
  11. package/src/commands/pain.ts +91 -1
  12. package/src/commands/principle-rollback.ts +2 -1
  13. package/src/commands/promote-impl.ts +7 -43
  14. package/src/commands/rollback-impl.ts +2 -1
  15. package/src/commands/rollback.ts +2 -1
  16. package/src/commands/samples.ts +2 -1
  17. package/src/commands/thinking-os.ts +2 -1
  18. package/src/config/errors.ts +18 -2
  19. package/src/constants/diagnostician.ts +2 -2
  20. package/src/constants/tools.ts +2 -1
  21. package/src/core/__tests__/focus-history.test.ts +210 -0
  22. package/src/core/config.ts +1 -1
  23. package/src/core/confirm-first-gate.ts +255 -0
  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 +38 -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/pain-diagnostic-gate.ts +154 -0
  37. package/src/core/pain-signal.ts +21 -138
  38. package/src/core/pain.ts +15 -88
  39. package/src/core/pd-task-reconciler.ts +26 -115
  40. package/src/core/pd-task-service.ts +9 -9
  41. package/src/core/pd-task-types.ts +23 -127
  42. package/src/core/principle-compiler/__tests__/compiler-replay-gate.test.ts +174 -0
  43. package/src/core/principle-compiler/code-validator.ts +15 -42
  44. package/src/core/principle-compiler/compiler.ts +100 -15
  45. package/src/core/principle-compiler/index.ts +5 -2
  46. package/src/core/principle-compiler/template-generator.ts +4 -104
  47. package/src/core/principle-injection.ts +10 -202
  48. package/src/core/principle-internalization/filesystem-lifecycle-datasource.ts +42 -0
  49. package/src/core/principle-internalization/lifecycle-read-model.ts +39 -242
  50. package/src/core/principle-internalization/principle-lifecycle-service.ts +12 -10
  51. package/src/core/principle-tree-ledger-adapter.ts +145 -0
  52. package/src/core/principle-tree-ledger.ts +8 -6
  53. package/src/core/reflection/reflection-context.ts +14 -109
  54. package/src/core/replay-engine.ts +8 -500
  55. package/src/core/rule-host-helpers.ts +5 -35
  56. package/src/core/rule-host-types.ts +10 -82
  57. package/src/core/rule-host.ts +6 -63
  58. package/src/core/runtime-v2-prompt-activation-reader.ts +231 -0
  59. package/src/core/session-tracker.ts +87 -101
  60. package/src/core/shadow-observation-registry.ts +19 -48
  61. package/src/core/trajectory.ts +3 -1
  62. package/src/core/workflow-funnel-loader.ts +62 -68
  63. package/src/core/workspace-context.ts +46 -0
  64. package/src/core/workspace-dir-service.ts +1 -1
  65. package/src/core/workspace-dir-validation.ts +18 -9
  66. package/src/hooks/AGENTS.md +1 -1
  67. package/src/hooks/gate-block-helper.ts +46 -44
  68. package/src/hooks/gate.ts +207 -7
  69. package/src/hooks/lifecycle.ts +30 -32
  70. package/src/hooks/llm.ts +60 -32
  71. package/src/hooks/pain.ts +297 -103
  72. package/src/hooks/prompt.ts +469 -339
  73. package/src/hooks/subagent.ts +2 -29
  74. package/src/i18n/commands.ts +2 -10
  75. package/src/index.ts +95 -85
  76. package/src/openclaw-sdk.ts +311 -0
  77. package/src/service/central-database.ts +8 -4
  78. package/src/service/evolution-queue-migration.ts +2 -1
  79. package/src/service/evolution-worker.ts +163 -1786
  80. package/src/service/internalization-trigger-adapter.ts +302 -0
  81. package/src/service/keyword-optimization-service.ts +4 -4
  82. package/src/service/monitoring-query-service.ts +1 -215
  83. package/src/service/queue-io.ts +60 -331
  84. package/src/service/runtime-summary-service.ts +115 -18
  85. package/src/service/subagent-workflow/index.ts +0 -41
  86. package/src/service/subagent-workflow/types.ts +9 -120
  87. package/src/service/subagent-workflow/workflow-store.ts +2 -119
  88. package/src/service/workflow-watchdog.ts +0 -43
  89. package/src/types/event-payload.ts +16 -74
  90. package/src/types/event-types.ts +39 -547
  91. package/src/types/hygiene-types.ts +7 -30
  92. package/src/types/principle-tree-schema.ts +20 -222
  93. package/src/types/queue.ts +15 -70
  94. package/src/types/runtime-summary.ts +5 -49
  95. package/src/utils/io.ts +10 -0
  96. package/src/utils/retry.ts +1 -1
  97. package/src/utils/shadow-fingerprint.ts +2 -2
  98. package/src/utils/workspace-resolver.ts +50 -0
  99. package/templates/langs/en/core/AGENTS.md +2 -2
  100. package/templates/langs/en/core/BOOT.md +1 -1
  101. package/templates/langs/en/core/HEARTBEAT.md +2 -2
  102. package/templates/langs/en/skills/ai-sprint-orchestration/references/agent-registry.json +1 -72
  103. package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/bugfix-complex-template.json +6 -6
  104. package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/feature-complex-template.json +6 -6
  105. package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal-verify.json +2 -12
  106. package/templates/langs/en/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal.json +2 -12
  107. package/templates/langs/en/skills/ai-sprint-orchestration/runtime/.gitignore +2 -2
  108. package/templates/langs/en/skills/ai-sprint-orchestration/scripts/run.mjs +51 -15
  109. package/templates/langs/en/skills/evolve-task/SKILL.md +1 -1
  110. package/templates/langs/en/skills/pd-cli-operator/SKILL.md +67 -0
  111. package/templates/langs/en/skills/pd-diagnostician/SKILL.md +1 -1
  112. package/templates/langs/en/skills/pd-mentor/SKILL.md +1 -1
  113. package/templates/langs/en/skills/pd-pain-signal/SKILL.md +17 -39
  114. package/templates/langs/en/skills/pd-runtime-v2/SKILL.md +61 -0
  115. package/templates/langs/zh/core/AGENTS.md +2 -2
  116. package/templates/langs/zh/core/BOOT.md +1 -1
  117. package/templates/langs/zh/core/HEARTBEAT.md +2 -2
  118. package/templates/langs/zh/skills/ai-sprint-orchestration/references/agent-registry.json +1 -72
  119. package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/bugfix-complex-template.json +6 -6
  120. package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/feature-complex-template.json +6 -6
  121. package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/nocturnal-trinity-quality-enhancement.json +8 -8
  122. package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal-verify.json +2 -12
  123. package/templates/langs/zh/skills/ai-sprint-orchestration/references/specs/workflow-validation-minimal.json +2 -12
  124. package/templates/langs/zh/skills/ai-sprint-orchestration/runtime/.gitignore +2 -2
  125. package/templates/langs/zh/skills/ai-sprint-orchestration/scripts/run.mjs +51 -15
  126. package/templates/langs/zh/skills/ai-sprint-orchestration/test/run.test.mjs +21 -5
  127. package/templates/langs/zh/skills/evolve-task/SKILL.md +2 -2
  128. package/templates/langs/zh/skills/pd-cli-operator/SKILL.md +67 -0
  129. package/templates/langs/zh/skills/pd-diagnostician/SKILL.md +1 -1
  130. package/templates/langs/zh/skills/pd-mentor/SKILL.md +1 -1
  131. package/templates/langs/zh/skills/pd-pain-signal/SKILL.md +17 -38
  132. package/templates/langs/zh/skills/pd-runtime-v2/SKILL.md +61 -0
  133. package/tests/build-artifacts.test.ts +1 -3
  134. package/tests/commands/evolution-status.test.ts +0 -118
  135. package/tests/core/bootstrap-rules.test.ts +1 -1
  136. package/tests/core/config.test.ts +1 -1
  137. package/tests/core/event-log.test.ts +35 -0
  138. package/tests/core/evolution-engine.test.ts +610 -0
  139. package/tests/core/file-store.test.ts +102 -0
  140. package/tests/core/focus-history.test.ts +203 -11
  141. package/tests/core/merge-gate-audit.test.ts +2 -169
  142. package/tests/core/model-deployment-registry.test.ts +7 -1
  143. package/tests/core/model-training-registry.test.ts +19 -0
  144. package/tests/core/observability.test.ts +0 -1
  145. package/tests/core/pain-diagnostic-gate.test.ts +498 -0
  146. package/tests/core/pain.test.ts +0 -1
  147. package/tests/core/principle-internalization/deprecated-readiness.test.ts +2 -2
  148. package/tests/core/principle-internalization/lifecycle-metrics.test.ts +2 -2
  149. package/tests/core/principle-internalization/{internalization-routing-policy.test.ts → lifecycle-routing-policy.test.ts} +6 -6
  150. package/tests/core/principle-internalization/lineage-source-retired.test.ts +56 -0
  151. package/tests/core/principle-internalization/principle-lifecycle-service.test.ts +1 -23
  152. package/tests/core/principle-tree-ledger-adapter.test.ts +253 -0
  153. package/tests/core/reflection-context.test.ts +0 -14
  154. package/tests/core/replay-engine.test.ts +127 -215
  155. package/tests/core/rule-host-helpers.test.ts +2 -2
  156. package/tests/core/rule-implementation-runtime.test.ts +0 -27
  157. package/tests/core/workflow-funnel-loader.test.ts +162 -0
  158. package/tests/core/workspace-dir-validation.test.ts +8 -1
  159. package/tests/core-anti-growth.test.ts +192 -0
  160. package/tests/hook-workspace-nextaction-contract.test.ts +42 -0
  161. package/tests/hooks/confirm-first-gate.test.ts +333 -0
  162. package/tests/hooks/gate-auto-correct-shadow.test.ts +310 -0
  163. package/tests/hooks/gate-auto-correct.test.ts +665 -0
  164. package/tests/hooks/gate-rule-host-pipeline.test.ts +2 -1
  165. package/tests/hooks/pain.test.ts +269 -12
  166. package/tests/hooks/prompt-characterization.test.ts +500 -0
  167. package/tests/hooks/prompt-size-guard.test.ts +329 -0
  168. package/tests/hooks/runtime-v2-prompt-activation.test.ts +869 -0
  169. package/tests/index.test.ts +94 -1
  170. package/tests/integration/auto-entry-gate.test.ts +248 -0
  171. package/tests/integration/internalization-trigger-guard.test.ts +69 -0
  172. package/tests/integration/m8-legacy-paths.test.ts +63 -0
  173. package/tests/integration/runtime-v2-pain-guard.test.ts +125 -0
  174. package/tests/plugin-config-resolution-cutover.test.ts +359 -0
  175. package/tests/runtime-v2-discovery-guard.test.ts +154 -0
  176. package/tests/service/central-database.test.ts +457 -0
  177. package/tests/service/evolution-worker.correction-observer.test.ts +173 -0
  178. package/tests/service/evolution-worker.timeout.test.ts +11 -129
  179. package/tests/service/internalization-trigger-adapter.test.ts +251 -0
  180. package/tests/service/monitoring-query-service.test.ts +1 -47
  181. package/tests/service/queue-io.test.ts +1 -62
  182. package/tests/service/runtime-summary-service.test.ts +184 -3
  183. package/tests/service/workflow-watchdog.test.ts +0 -91
  184. package/tests/utils/file-lock.test.ts +5 -3
  185. package/tests/utils/session-key.test.ts +52 -0
  186. package/tests/utils/subagent-probe.test.ts +48 -1
  187. package/vitest.config.ts +4 -11
  188. package/.planning/codebase/ARCHITECTURE.md +0 -157
  189. package/.planning/codebase/CONCERNS.md +0 -145
  190. package/.planning/codebase/CONVENTIONS.md +0 -148
  191. package/.planning/codebase/INTEGRATIONS.md +0 -81
  192. package/.planning/codebase/STACK.md +0 -87
  193. package/.planning/codebase/STRUCTURE.md +0 -193
  194. package/.planning/codebase/TESTING.md +0 -243
  195. package/.planning/phases/01-basic-visualization/01-GAP-CLOSURE-VERIFICATION.md +0 -113
  196. package/docs/COMMAND_REFERENCE.md +0 -76
  197. package/docs/COMMAND_REFERENCE_EN.md +0 -79
  198. package/scripts/build-web.mjs +0 -46
  199. package/scripts/diagnose-nocturnal.mjs +0 -537
  200. package/scripts/seed-nocturnal-scenarios.mjs +0 -384
  201. package/src/commands/nocturnal-review.ts +0 -322
  202. package/src/commands/nocturnal-rollout.ts +0 -790
  203. package/src/commands/nocturnal-train.ts +0 -986
  204. package/src/commands/pd-reflect.ts +0 -88
  205. package/src/core/adaptive-thresholds.ts +0 -478
  206. package/src/core/diagnostician-task-store.ts +0 -192
  207. package/src/core/nocturnal-arbiter.ts +0 -715
  208. package/src/core/nocturnal-artifact-lineage.ts +0 -116
  209. package/src/core/nocturnal-artificer.ts +0 -257
  210. package/src/core/nocturnal-candidate-scoring.ts +0 -530
  211. package/src/core/nocturnal-compliance.ts +0 -1146
  212. package/src/core/nocturnal-dataset.ts +0 -763
  213. package/src/core/nocturnal-executability.ts +0 -428
  214. package/src/core/nocturnal-export.ts +0 -499
  215. package/src/core/nocturnal-paths.ts +0 -240
  216. package/src/core/nocturnal-reasoning-deriver.ts +0 -343
  217. package/src/core/nocturnal-rule-implementation-validator.ts +0 -246
  218. package/src/core/nocturnal-snapshot-contract.ts +0 -99
  219. package/src/core/nocturnal-trajectory-extractor.ts +0 -512
  220. package/src/core/nocturnal-trinity-types.ts +0 -218
  221. package/src/core/nocturnal-trinity.ts +0 -2680
  222. package/src/core/principle-internalization/deprecated-readiness.ts +0 -93
  223. package/src/core/principle-internalization/internalization-routing-policy.ts +0 -208
  224. package/src/core/principle-internalization/lifecycle-metrics.ts +0 -152
  225. package/src/http/principles-console-route.ts +0 -709
  226. package/src/service/central-health-service.ts +0 -49
  227. package/src/service/central-overview-service.ts +0 -138
  228. package/src/service/control-ui-query-service.ts +0 -900
  229. package/src/service/cooldown-strategy.ts +0 -97
  230. package/src/service/evolution-pain-context.ts +0 -79
  231. package/src/service/evolution-query-service.ts +0 -407
  232. package/src/service/health-query-service.ts +0 -1038
  233. package/src/service/nocturnal-config.ts +0 -214
  234. package/src/service/nocturnal-runtime.ts +0 -734
  235. package/src/service/nocturnal-service.ts +0 -1605
  236. package/src/service/nocturnal-target-selector.ts +0 -545
  237. package/src/service/sleep-cycle.ts +0 -157
  238. package/src/service/startup-reconciler.ts +0 -112
  239. package/src/service/subagent-workflow/correction-observer-types.ts +0 -82
  240. package/src/service/subagent-workflow/correction-observer-workflow-manager.ts +0 -250
  241. package/src/service/subagent-workflow/deep-reflect-workflow-manager.ts +0 -1
  242. package/src/service/subagent-workflow/dynamic-timeout.ts +0 -30
  243. package/src/service/subagent-workflow/empathy-observer-workflow-manager.ts +0 -268
  244. package/src/service/subagent-workflow/nocturnal-workflow-manager.ts +0 -795
  245. package/src/service/subagent-workflow/runtime-direct-driver.ts +0 -268
  246. package/src/service/subagent-workflow/workflow-manager-base.ts +0 -580
  247. package/src/tools/write-pain-flag.ts +0 -215
  248. package/tests/commands/nocturnal-review.test.ts +0 -448
  249. package/tests/commands/nocturnal-train.test.ts +0 -97
  250. package/tests/commands/pd-reflect.test.ts +0 -49
  251. package/tests/core/adaptive-thresholds.test.ts +0 -261
  252. package/tests/core/nocturnal-arbiter.test.ts +0 -559
  253. package/tests/core/nocturnal-artifact-lineage.test.ts +0 -53
  254. package/tests/core/nocturnal-artificer.test.ts +0 -241
  255. package/tests/core/nocturnal-candidate-scoring.test.ts +0 -532
  256. package/tests/core/nocturnal-compliance-p-principles.test.ts +0 -133
  257. package/tests/core/nocturnal-compliance.test.ts +0 -646
  258. package/tests/core/nocturnal-dataset.test.ts +0 -892
  259. package/tests/core/nocturnal-e2e.test.ts +0 -234
  260. package/tests/core/nocturnal-executability.test.ts +0 -357
  261. package/tests/core/nocturnal-export.test.ts +0 -517
  262. package/tests/core/nocturnal-reasoning-deriver.test.ts +0 -372
  263. package/tests/core/nocturnal-reviewed-subset-comparison.test.ts +0 -428
  264. package/tests/core/nocturnal-rule-implementation-validator.test.ts +0 -127
  265. package/tests/core/nocturnal-snapshot-contract.test.ts +0 -121
  266. package/tests/core/nocturnal-trajectory-extractor.test.ts +0 -634
  267. package/tests/core/nocturnal-trinity.test.ts +0 -2053
  268. package/tests/core/pain-auto-repair.test.ts +0 -96
  269. package/tests/core/pain-integration.test.ts +0 -510
  270. package/tests/fixtures/nocturnal-reviewed-subset.json +0 -183
  271. package/tests/http/principles-console-route.test.ts +0 -162
  272. package/tests/integration/chaos-resilience.test.ts +0 -348
  273. package/tests/integration/empathy-workflow-integration.test.ts +0 -626
  274. package/tests/integration/pain-diagnostician-loop.e2e.test.ts +0 -380
  275. package/tests/service/control-ui-query-service.test.ts +0 -121
  276. package/tests/service/cooldown-strategy.test.ts +0 -164
  277. package/tests/service/data-endpoints-regression.test.ts +0 -834
  278. package/tests/service/empathy-observer-workflow-manager.test.ts +0 -175
  279. package/tests/service/evolution-worker.nocturnal.test.ts +0 -601
  280. package/tests/service/nocturnal-runtime-hardening.test.ts +0 -118
  281. package/tests/service/nocturnal-runtime.test.ts +0 -473
  282. package/tests/service/nocturnal-service-code-candidate.test.ts +0 -330
  283. package/tests/service/nocturnal-target-selector.test.ts +0 -615
  284. package/tests/service/startup-reconciler.test.ts +0 -148
  285. package/tests/tools/write-pain-flag.test.ts +0 -358
  286. package/ui/src/App.tsx +0 -45
  287. package/ui/src/api.ts +0 -220
  288. package/ui/src/charts.tsx +0 -955
  289. package/ui/src/components/ErrorState.tsx +0 -6
  290. package/ui/src/components/Loading.tsx +0 -13
  291. package/ui/src/components/ProtectedRoute.tsx +0 -12
  292. package/ui/src/components/Shell.tsx +0 -91
  293. package/ui/src/components/WorkspaceConfig.tsx +0 -178
  294. package/ui/src/components/index.ts +0 -5
  295. package/ui/src/context/auth.tsx +0 -80
  296. package/ui/src/context/theme.tsx +0 -66
  297. package/ui/src/hooks/useAutoRefresh.ts +0 -39
  298. package/ui/src/i18n/ui.ts +0 -473
  299. package/ui/src/main.tsx +0 -16
  300. package/ui/src/pages/EvolutionPage.tsx +0 -333
  301. package/ui/src/pages/FeedbackPage.tsx +0 -138
  302. package/ui/src/pages/GateMonitorPage.tsx +0 -136
  303. package/ui/src/pages/LoginPage.tsx +0 -89
  304. package/ui/src/pages/OverviewPage.tsx +0 -599
  305. package/ui/src/pages/SamplesPage.tsx +0 -174
  306. package/ui/src/pages/ThinkingModelsPage.tsx +0 -702
  307. package/ui/src/styles.css +0 -2020
  308. package/ui/src/types.ts +0 -384
  309. package/ui/src/utils/format.ts +0 -15
@@ -1,900 +0,0 @@
1
- import { ControlUiDatabase } from '../core/control-ui-db.js';
2
- import { getThinkingModel, listThinkingModels, getThinkingModelDefinitions } from '../core/thinking-models.js';
3
- import { WorkspaceContext } from '../core/workspace-context.js';
4
-
5
- /** Time window (in minutes) for querying principle events related to a sample */
6
- const PRINCIPLE_EVENT_WINDOW_MINUTES = 10;
7
-
8
- export interface OverviewResponse {
9
- workspaceDir: string;
10
- generatedAt: string;
11
- dataFreshness: string | null;
12
- dataSource: 'trajectory_db_analytics';
13
- runtimeControlPlaneSource: 'pd_evolution_status';
14
- summary: {
15
- repeatErrorRate: number;
16
- userCorrectionRate: number;
17
- pendingSamples: number;
18
- approvedSamples: number;
19
- thinkingCoverageRate: number;
20
- painEvents: number;
21
- principleEventCount: number;
22
- gateBlocks: number;
23
- taskOutcomes: number;
24
- };
25
- dailyTrend: {
26
- day: string;
27
- toolCalls: number;
28
- failures: number;
29
- userCorrections: number;
30
- thinkingTurns: number;
31
- }[];
32
- topRegressions: {
33
- toolName: string;
34
- errorType: string;
35
- occurrences: number;
36
- }[];
37
- sampleQueue: {
38
- counters: Record<string, number>;
39
- preview: {
40
- sampleId: string;
41
- sessionId: string;
42
- qualityScore: number;
43
- reviewStatus: string;
44
- createdAt: string;
45
- }[];
46
- };
47
- thinkingSummary: {
48
- activeModels: number;
49
- dormantModels: number;
50
- effectiveModels: number;
51
- coverageRate: number;
52
- modelBreakdown?: { modelId: string; hits: number }[];
53
- modelDefinitions?: { modelId: string; name: string; description: string }[];
54
- };
55
- }
56
-
57
- export interface SampleListFilters {
58
- status?: string;
59
- qualityMin?: number;
60
- dateFrom?: string;
61
- dateTo?: string;
62
- failureMode?: string;
63
- page?: number;
64
- pageSize?: number;
65
- }
66
-
67
- export interface SamplesResponse {
68
- counters: Record<string, number>;
69
- items: {
70
- sampleId: string;
71
- sessionId: string;
72
- reviewStatus: string;
73
- qualityScore: number;
74
- failureMode: string;
75
- relatedThinkingCount: number;
76
- createdAt: string;
77
- updatedAt: string;
78
- diffExcerpt: string;
79
- }[];
80
- pagination: {
81
- page: number;
82
- pageSize: number;
83
- total: number;
84
- totalPages: number;
85
- };
86
- }
87
-
88
- export interface SampleDetailResponse {
89
- sampleId: string;
90
- sessionId: string;
91
- reviewStatus: string;
92
- qualityScore: number;
93
- createdAt: string;
94
- updatedAt: string;
95
- badAttempt: {
96
- assistantTurnId: number;
97
- rawText: string;
98
- sanitizedText: string;
99
- createdAt: string;
100
- };
101
- userCorrection: {
102
- userTurnId: number;
103
- rawText: string;
104
- correctionCue: string | null;
105
- createdAt: string;
106
- };
107
- recoveryToolSpan: { id: number; toolName: string }[];
108
- relatedPrinciples: {
109
- principleId: string | null;
110
- eventType: string;
111
- createdAt: string;
112
- }[];
113
- relatedThinkingHits: {
114
- id: number;
115
- modelId: string;
116
- modelName: string;
117
- matchedPattern: string;
118
- scenarios: string[];
119
- createdAt: string;
120
- triggerExcerpt: string;
121
- }[];
122
- reviewHistory: {
123
- reviewStatus: string;
124
- note: string | null;
125
- createdAt: string;
126
- }[];
127
- }
128
-
129
- export interface ThinkingModelSummary {
130
- modelId: string;
131
- name: string;
132
- description: string;
133
- hits: number;
134
- coverageRate: number;
135
- successRate: number;
136
- failureRate: number;
137
- painRate: number;
138
- correctionRate: number;
139
- correctionSampleRate: number;
140
- commonScenarios: string[];
141
- recommendation: 'reinforce' | 'rework' | 'archive';
142
- }
143
-
144
- export interface ThinkingOverviewResponse {
145
- summary: {
146
- totalModels: number;
147
- activeModels: number;
148
- dormantModels: number;
149
- effectiveModels: number;
150
- coverageRate: number;
151
- };
152
- topModels: ThinkingModelSummary[];
153
- dormantModels: {
154
- modelId: string;
155
- name: string;
156
- description: string;
157
- }[];
158
- effectiveModels: ThinkingModelSummary[];
159
- scenarioMatrix: {
160
- modelId: string;
161
- modelName: string;
162
- scenario: string;
163
- hits: number;
164
- }[];
165
- coverageTrend: {
166
- day: string;
167
- assistantTurns: number;
168
- thinkingTurns: number;
169
- coverageRate: number;
170
- }[];
171
- }
172
-
173
- export interface ThinkingModelDetailResponse {
174
- modelMeta: {
175
- modelId: string;
176
- name: string;
177
- description: string;
178
- hits: number;
179
- coverageRate: number;
180
- recommendation: 'reinforce' | 'rework' | 'archive';
181
- };
182
- usageTrend: {
183
- day: string;
184
- hits: number;
185
- }[];
186
- scenarioDistribution: {
187
- scenario: string;
188
- hits: number;
189
- }[];
190
- outcomeStats: {
191
- events: number;
192
- successRate: number;
193
- failureRate: number;
194
- painRate: number;
195
- correctionRate: number;
196
- correctionSampleRate: number;
197
- };
198
- recentEvents: {
199
- id: number;
200
- createdAt: string;
201
- matchedPattern: string;
202
- scenarios: string[];
203
- triggerExcerpt: string;
204
- toolContext: { toolName: string; outcome: string; errorType?: string | null }[];
205
- painContext: { source: string; score: number }[];
206
- principleContext: { principleId: string | null; eventType: string }[];
207
- }[];
208
- }
209
-
210
- function parseJson<T>(raw: string | null | undefined, fallback: T): T {
211
- if (!raw) return fallback;
212
- try {
213
- return JSON.parse(raw) as T;
214
- } catch {
215
- return fallback;
216
- }
217
- }
218
-
219
- function roundRate(numerator: number, denominator: number): number {
220
- if (!denominator) return 0;
221
- return Number((numerator / denominator).toFixed(4));
222
- }
223
-
224
- function clampPageSize(input?: number): number {
225
- if (!Number.isFinite(input)) return 20;
226
- return Math.min(100, Math.max(1, Number(input)));
227
- }
228
-
229
- function summarizeRecommendation(model: {
230
- hits: number;
231
- successRate: number;
232
- failureRate: number;
233
- painRate: number;
234
- correctionRate: number;
235
- }): 'reinforce' | 'rework' | 'archive' {
236
- if (model.hits === 0) return 'archive';
237
- if (model.failureRate > model.successRate || model.correctionRate >= 0.35 || model.painRate >= 0.3) {
238
- return 'rework';
239
- }
240
- return 'reinforce';
241
- }
242
-
243
- export class ControlUiQueryService {
244
- private readonly workspaceDir: string;
245
- private readonly trajectory;
246
- private readonly uiDb: ControlUiDatabase;
247
-
248
- constructor(workspaceDir: string) {
249
- this.workspaceDir = workspaceDir;
250
- this.trajectory = WorkspaceContext.fromHookContext({ workspaceDir }).trajectory;
251
- this.uiDb = new ControlUiDatabase({ workspaceDir });
252
- }
253
-
254
- dispose(): void {
255
- this.uiDb.dispose();
256
- }
257
-
258
-
259
- getOverview(days = 30): OverviewResponse {
260
- const stats = this.trajectory.getDataStats();
261
- const regressionRows = this.uiDb.all<{
262
- tool_name: string;
263
- error_type: string;
264
- occurrences: number;
265
- }>('SELECT tool_name, error_type, occurrences FROM v_error_clusters ORDER BY occurrences DESC LIMIT 5');
266
- const failureStats = this.uiDb.get<{ total_failures: number; repeated_failures: number }>(`
267
- SELECT
268
- COALESCE(SUM(occurrences), 0) AS total_failures,
269
- COALESCE(SUM(CASE WHEN occurrences > 1 THEN occurrences ELSE 0 END), 0) AS repeated_failures
270
- FROM v_error_clusters
271
- `) ?? { total_failures: 0, repeated_failures: 0 };
272
- const correctionTotal = this.uiDb.get<{ count: number }>(
273
- 'SELECT COUNT(*) AS count FROM user_turns WHERE correction_detected = 1',
274
- )?.count ?? 0;
275
- const principleEventCount = this.uiDb.get<{ count: number }>(
276
- 'SELECT COUNT(*) AS count FROM principle_events',
277
- )?.count ?? 0;
278
- const gateBlockCount = this.uiDb.get<{ count: number }>(
279
- 'SELECT COUNT(*) AS count FROM gate_blocks',
280
- )?.count ?? 0;
281
- const taskOutcomeCount = this.uiDb.get<{ count: number }>(
282
- 'SELECT COUNT(*) AS count FROM task_outcomes',
283
- )?.count ?? 0;
284
- const sampleCounters = this.uiDb.all<{ review_status: string; total: number }>(
285
- 'SELECT review_status, total FROM v_sample_queue',
286
- );
287
- const samplePreview = this.uiDb.all<{
288
- sample_id: string;
289
- session_id: string;
290
- quality_score: number;
291
- review_status: string;
292
- created_at: string;
293
- }>(`
294
- SELECT sample_id, session_id, quality_score, review_status, created_at
295
- FROM correction_samples
296
- ORDER BY created_at DESC
297
- LIMIT 5
298
- `);
299
- const coverageRow = this.uiDb.get<{ thinking_turns: number; assistant_turns: number }>(`
300
- SELECT
301
- COUNT(DISTINCT assistant_turn_id) AS thinking_turns,
302
- (SELECT COUNT(*) FROM assistant_turns) AS assistant_turns
303
- FROM thinking_model_events
304
- `) ?? { thinking_turns: 0, assistant_turns: 0 };
305
- const effectiveCount = this.uiDb.all<{
306
- events: number;
307
- success_windows: number;
308
- failure_windows: number;
309
- pain_windows: number;
310
- correction_windows: number;
311
- }>('SELECT events, success_windows, failure_windows, pain_windows, correction_windows FROM v_thinking_model_effectiveness')
312
- .filter((row) => summarizeRecommendation({
313
- hits: Number(row.events),
314
- successRate: roundRate(Number(row.success_windows), Number(row.events)),
315
- failureRate: roundRate(Number(row.failure_windows), Number(row.events)),
316
- painRate: roundRate(Number(row.pain_windows), Number(row.events)),
317
- correctionRate: roundRate(Number(row.correction_windows), Number(row.events)),
318
- }) === 'reinforce').length;
319
- const modelBreakdown = this.uiDb.all<{ modelId: string; hits: number }>(`
320
- SELECT model_id as modelId, COUNT(*) as hits
321
- FROM thinking_model_events
322
- GROUP BY model_id
323
- ORDER BY hits DESC
324
- `).map(row => ({ modelId: row.modelId, hits: Number(row.hits) }));
325
- const dailyTrend = this.uiDb.all<{
326
- day: string;
327
- tool_calls: number;
328
- failures: number;
329
- user_corrections: number;
330
- thinking_turns: number;
331
- }>(`
332
- WITH thinking_daily AS (
333
- SELECT substr(created_at, 1, 10) AS day, COUNT(DISTINCT assistant_turn_id) AS thinking_turns
334
- FROM thinking_model_events
335
- GROUP BY substr(created_at, 1, 10)
336
- )
337
- SELECT
338
- dm.day AS day,
339
- dm.tool_calls AS tool_calls,
340
- dm.failures AS failures,
341
- dm.user_corrections AS user_corrections,
342
- COALESCE(td.thinking_turns, 0) AS thinking_turns
343
- FROM v_daily_metrics dm
344
- LEFT JOIN thinking_daily td ON td.day = dm.day
345
- ORDER BY dm.day DESC
346
- LIMIT ?
347
- `, days).reverse();
348
- const counters = Object.fromEntries(sampleCounters.map((row) => [row.review_status, Number(row.total)]));
349
- const activeModels = this.uiDb.get<{ count: number }>(
350
- 'SELECT COUNT(DISTINCT model_id) AS count FROM thinking_model_events',
351
- )?.count ?? 0;
352
-
353
- return {
354
- workspaceDir: this.workspaceDir,
355
- generatedAt: new Date().toISOString(),
356
- dataFreshness: stats.lastIngestAt,
357
- dataSource: 'trajectory_db_analytics',
358
- runtimeControlPlaneSource: 'pd_evolution_status',
359
- summary: {
360
- repeatErrorRate: roundRate(Number(failureStats.repeated_failures), Number(failureStats.total_failures)),
361
- userCorrectionRate: roundRate(correctionTotal, stats.userTurns),
362
- pendingSamples: stats.pendingSamples,
363
- approvedSamples: stats.approvedSamples,
364
- thinkingCoverageRate: roundRate(coverageRow.thinking_turns, coverageRow.assistant_turns),
365
- painEvents: stats.painEvents,
366
- principleEventCount,
367
- gateBlocks: Number(gateBlockCount),
368
- taskOutcomes: Number(taskOutcomeCount),
369
- },
370
- dailyTrend: dailyTrend.map((row) => ({
371
- day: row.day,
372
- toolCalls: Number(row.tool_calls),
373
- failures: Number(row.failures),
374
- userCorrections: Number(row.user_corrections),
375
- thinkingTurns: Number(row.thinking_turns),
376
- })),
377
- topRegressions: regressionRows.map((row) => ({
378
- toolName: row.tool_name,
379
- errorType: row.error_type,
380
- occurrences: Number(row.occurrences),
381
- })),
382
- sampleQueue: {
383
- counters,
384
- preview: samplePreview.map((row) => ({
385
- sampleId: row.sample_id,
386
- sessionId: row.session_id,
387
- qualityScore: Number(row.quality_score),
388
- reviewStatus: row.review_status,
389
- createdAt: row.created_at,
390
- })),
391
- },
392
- thinkingSummary: {
393
- activeModels,
394
- dormantModels: Math.max(0, listThinkingModels().length - activeModels),
395
- effectiveModels: effectiveCount,
396
- coverageRate: roundRate(coverageRow.thinking_turns, coverageRow.assistant_turns),
397
- modelBreakdown,
398
- modelDefinitions: getThinkingModelDefinitions(),
399
- },
400
- };
401
- }
402
-
403
-
404
- listSamples(filters: SampleListFilters = {}): SamplesResponse {
405
- const page = Math.max(1, Number(filters.page ?? 1));
406
- const pageSize = clampPageSize(filters.pageSize);
407
- const offset = (page - 1) * pageSize;
408
- const where: string[] = [];
409
- const params: unknown[] = [];
410
-
411
- if (filters.status && filters.status !== 'all') {
412
- where.push('cs.review_status = ?');
413
- params.push(filters.status);
414
- }
415
- if (Number.isFinite(filters.qualityMin)) {
416
- where.push('cs.quality_score >= ?');
417
- params.push(Number(filters.qualityMin));
418
- }
419
- if (filters.dateFrom) {
420
- where.push('cs.created_at >= ?');
421
- params.push(filters.dateFrom);
422
- }
423
- if (filters.dateTo) {
424
- where.push('cs.created_at <= ?');
425
- params.push(filters.dateTo);
426
- }
427
- if (filters.failureMode) {
428
- where.push(`
429
- COALESCE(
430
- (
431
- SELECT COALESCE(tc.error_type, tc.tool_name)
432
- FROM tool_calls tc
433
- WHERE tc.session_id = cs.session_id
434
- AND tc.outcome = 'failure'
435
- AND tc.created_at <= ut.created_at
436
- ORDER BY tc.created_at DESC
437
- LIMIT 1
438
- ),
439
- 'unknown'
440
- ) = ?
441
- `);
442
- params.push(filters.failureMode);
443
- }
444
-
445
- const whereClause = where.length > 0 ? `WHERE ${where.join(' AND ')}` : '';
446
- const total = Number(this.uiDb.get<{ count: number }>(`
447
- SELECT COUNT(*) AS count
448
- FROM correction_samples cs
449
- JOIN user_turns ut ON ut.id = cs.user_correction_turn_id
450
- ${whereClause}
451
- `, ...params)?.count ?? 0);
452
-
453
- const items = this.uiDb.all<{
454
- sample_id: string;
455
- session_id: string;
456
- review_status: string;
457
- quality_score: number;
458
- created_at: string;
459
- updated_at: string;
460
- diff_excerpt: string;
461
- failure_mode: string;
462
- related_thinking_count: number;
463
- }>(`
464
- SELECT
465
- cs.sample_id,
466
- cs.session_id,
467
- cs.review_status,
468
- cs.quality_score,
469
- cs.created_at,
470
- cs.updated_at,
471
- cs.diff_excerpt,
472
- COALESCE(
473
- (
474
- SELECT COALESCE(tc.error_type, tc.tool_name)
475
- FROM tool_calls tc
476
- WHERE tc.session_id = cs.session_id
477
- AND tc.outcome = 'failure'
478
- AND tc.created_at <= ut.created_at
479
- ORDER BY tc.created_at DESC
480
- LIMIT 1
481
- ),
482
- 'unknown'
483
- ) AS failure_mode,
484
- (
485
- SELECT COUNT(*)
486
- FROM thinking_model_events tme
487
- JOIN assistant_turns at2 ON at2.id = cs.bad_assistant_turn_id
488
- WHERE tme.session_id = cs.session_id
489
- AND tme.created_at >= at2.created_at
490
- AND tme.created_at <= ut.created_at
491
- ) AS related_thinking_count
492
- FROM correction_samples cs
493
- JOIN user_turns ut ON ut.id = cs.user_correction_turn_id
494
- ${whereClause}
495
- ORDER BY cs.created_at DESC
496
- LIMIT ? OFFSET ?
497
- `, ...params, pageSize, offset);
498
- const counters = this.uiDb.all<{ review_status: string; count: number }>(`
499
- SELECT review_status, COUNT(*) AS count
500
- FROM correction_samples
501
- GROUP BY review_status
502
- `);
503
-
504
- return {
505
- counters: Object.fromEntries(counters.map((row) => [row.review_status, Number(row.count)])),
506
- items: items.map((row) => ({
507
- sampleId: row.sample_id,
508
- sessionId: row.session_id,
509
- reviewStatus: row.review_status,
510
- qualityScore: Number(row.quality_score),
511
- failureMode: row.failure_mode,
512
- relatedThinkingCount: Number(row.related_thinking_count),
513
- createdAt: row.created_at,
514
- updatedAt: row.updated_at,
515
- diffExcerpt: row.diff_excerpt,
516
- })),
517
- pagination: {
518
- page,
519
- pageSize,
520
- total,
521
- totalPages: total === 0 ? 0 : Math.ceil(total / pageSize),
522
- },
523
- };
524
- }
525
-
526
- getSampleDetail(sampleId: string): SampleDetailResponse | null {
527
- const row = this.uiDb.get<{
528
- sample_id: string;
529
- session_id: string;
530
- review_status: string;
531
- quality_score: number;
532
- created_at: string;
533
- updated_at: string;
534
- recovery_tool_span_json: string;
535
- principle_ids_json: string;
536
- bad_turn_id: number;
537
- bad_raw_text: string | null;
538
- bad_blob_ref: string | null;
539
- bad_sanitized_text: string;
540
- bad_created_at: string;
541
- user_turn_id: number;
542
- user_raw_text: string | null;
543
- user_blob_ref: string | null;
544
- user_correction_cue: string | null;
545
- user_created_at: string;
546
- }>(`
547
- SELECT
548
- cs.sample_id,
549
- cs.session_id,
550
- cs.review_status,
551
- cs.quality_score,
552
- cs.created_at,
553
- cs.updated_at,
554
- cs.recovery_tool_span_json,
555
- cs.principle_ids_json,
556
- at.id AS bad_turn_id,
557
- at.raw_text AS bad_raw_text,
558
- at.blob_ref AS bad_blob_ref,
559
- at.sanitized_text AS bad_sanitized_text,
560
- at.created_at AS bad_created_at,
561
- ut.id AS user_turn_id,
562
- ut.raw_text AS user_raw_text,
563
- ut.blob_ref AS user_blob_ref,
564
- ut.correction_cue AS user_correction_cue,
565
- ut.created_at AS user_created_at
566
- FROM correction_samples cs
567
- JOIN assistant_turns at ON at.id = cs.bad_assistant_turn_id
568
- JOIN user_turns ut ON ut.id = cs.user_correction_turn_id
569
- WHERE cs.sample_id = ?
570
- `, sampleId);
571
- if (!row) return null;
572
-
573
- const reviewHistory = this.uiDb.all<{
574
- review_status: string;
575
- note: string | null;
576
- created_at: string;
577
- }>(`
578
- SELECT review_status, note, created_at
579
- FROM sample_reviews
580
- WHERE sample_id = ?
581
- ORDER BY created_at DESC
582
- `, sampleId);
583
- const relatedThinkingHits = this.uiDb.all<{
584
- id: number;
585
- model_id: string;
586
- matched_pattern: string;
587
- scenario_json: string;
588
- created_at: string;
589
- trigger_excerpt: string;
590
- }>(`
591
- SELECT id, model_id, matched_pattern, scenario_json, created_at, trigger_excerpt
592
- FROM thinking_model_events
593
- WHERE session_id = ?
594
- AND created_at >= ?
595
- AND created_at <= ?
596
- ORDER BY created_at DESC
597
- LIMIT 20
598
- `, row.session_id, row.bad_created_at, row.user_created_at);
599
- const relatedPrinciples = this.uiDb.all<{
600
- principle_id: string | null;
601
- event_type: string;
602
- created_at: string;
603
- }>(`
604
- SELECT principle_id, event_type, created_at
605
- FROM principle_events
606
- WHERE created_at >= ?
607
- AND created_at <= datetime(?, '+' || ? || ' minutes')
608
- ORDER BY created_at DESC
609
- LIMIT 20
610
- `, row.bad_created_at, row.user_created_at, PRINCIPLE_EVENT_WINDOW_MINUTES);
611
- const seededPrincipleIds = parseJson<string[]>(row.principle_ids_json, []).map((principleId) => ({
612
- principleId,
613
- eventType: 'seeded_from_sample',
614
- createdAt: row.created_at,
615
- }));
616
-
617
- return {
618
- sampleId: row.sample_id,
619
- sessionId: row.session_id,
620
- reviewStatus: row.review_status,
621
- qualityScore: Number(row.quality_score),
622
- createdAt: row.created_at,
623
- updatedAt: row.updated_at,
624
- badAttempt: {
625
- assistantTurnId: Number(row.bad_turn_id),
626
- rawText: this.uiDb.restoreRawText(row.bad_raw_text, row.bad_blob_ref),
627
- sanitizedText: row.bad_sanitized_text,
628
- createdAt: row.bad_created_at,
629
- },
630
- userCorrection: {
631
- userTurnId: Number(row.user_turn_id),
632
- rawText: this.uiDb.restoreRawText(row.user_raw_text, row.user_blob_ref),
633
- correctionCue: row.user_correction_cue,
634
- createdAt: row.user_created_at,
635
- },
636
- recoveryToolSpan: parseJson<{ id: number; toolName: string }[]>(row.recovery_tool_span_json, []),
637
- relatedPrinciples: [
638
- ...seededPrincipleIds,
639
- ...relatedPrinciples.map((item) => ({
640
- principleId: item.principle_id,
641
- eventType: item.event_type,
642
- createdAt: item.created_at,
643
- })),
644
- ],
645
- relatedThinkingHits: relatedThinkingHits.map((item) => ({
646
- id: Number(item.id),
647
- modelId: item.model_id,
648
- modelName: getThinkingModel(item.model_id)?.name ?? item.model_id,
649
- matchedPattern: item.matched_pattern,
650
- scenarios: parseJson<string[]>(item.scenario_json, []),
651
- createdAt: item.created_at,
652
- triggerExcerpt: item.trigger_excerpt,
653
- })),
654
- reviewHistory: reviewHistory.map((item) => ({
655
- reviewStatus: item.review_status,
656
- note: item.note,
657
- createdAt: item.created_at,
658
- })),
659
- };
660
- }
661
-
662
- reviewSample(sampleId: string, decision: 'approved' | 'rejected', note?: string) {
663
- return this.trajectory.reviewCorrectionSample(sampleId, decision, note);
664
- }
665
-
666
- exportCorrections(mode: 'raw' | 'redacted') {
667
- return this.trajectory.exportCorrections({ mode, approvedOnly: true });
668
- }
669
-
670
- getThinkingOverview(): ThinkingOverviewResponse {
671
- const topModels = this.loadThinkingModelSummaries();
672
- const knownModels = listThinkingModels();
673
- const activeIds = new Set(topModels.filter((model) => model.hits > 0).map((model) => model.modelId));
674
- const dormantModels = knownModels
675
- .filter((model) => !activeIds.has(model.id))
676
- .map((model) => ({
677
- modelId: model.id,
678
- name: model.name,
679
- description: model.description,
680
- }));
681
- const coverageRow = this.uiDb.get<{ thinking_turns: number; assistant_turns: number }>(`
682
- SELECT
683
- COUNT(DISTINCT assistant_turn_id) AS thinking_turns,
684
- (SELECT COUNT(*) FROM assistant_turns) AS assistant_turns
685
- FROM thinking_model_events
686
- `) ?? { thinking_turns: 0, assistant_turns: 0 };
687
- const coverageTrend = this.uiDb.all<{
688
- day: string;
689
- assistant_turns: number;
690
- thinking_turns: number;
691
- }>(`
692
- WITH assistant_daily AS (
693
- SELECT substr(created_at, 1, 10) AS day, COUNT(*) AS assistant_turns
694
- FROM assistant_turns
695
- GROUP BY substr(created_at, 1, 10)
696
- ),
697
- thinking_daily AS (
698
- SELECT substr(created_at, 1, 10) AS day, COUNT(DISTINCT assistant_turn_id) AS thinking_turns
699
- FROM thinking_model_events
700
- GROUP BY substr(created_at, 1, 10)
701
- )
702
- SELECT
703
- assistant_daily.day AS day,
704
- assistant_daily.assistant_turns AS assistant_turns,
705
- COALESCE(thinking_daily.thinking_turns, 0) AS thinking_turns
706
- FROM assistant_daily
707
- LEFT JOIN thinking_daily ON thinking_daily.day = assistant_daily.day
708
- ORDER BY assistant_daily.day ASC
709
- `);
710
- const scenarioMatrix = this.uiDb.all<{
711
- model_id: string;
712
- scenario: string;
713
- hits: number;
714
- }>('SELECT model_id, scenario, hits FROM v_thinking_model_scenarios ORDER BY hits DESC, model_id ASC');
715
-
716
- return {
717
- summary: {
718
- totalModels: knownModels.length,
719
- activeModels: activeIds.size,
720
- dormantModels: dormantModels.length,
721
- effectiveModels: topModels.filter((model) => model.recommendation === 'reinforce').length,
722
- coverageRate: roundRate(coverageRow.thinking_turns, coverageRow.assistant_turns),
723
- },
724
- topModels,
725
- dormantModels,
726
- effectiveModels: topModels.filter((model) => model.recommendation === 'reinforce'),
727
- scenarioMatrix: scenarioMatrix.map((row) => ({
728
- modelId: row.model_id,
729
- modelName: getThinkingModel(row.model_id)?.name ?? row.model_id,
730
- scenario: row.scenario,
731
- hits: Number(row.hits),
732
- })),
733
- coverageTrend: coverageTrend.map((row) => ({
734
- day: row.day,
735
- assistantTurns: Number(row.assistant_turns),
736
- thinkingTurns: Number(row.thinking_turns),
737
- coverageRate: roundRate(Number(row.thinking_turns), Number(row.assistant_turns)),
738
- })),
739
- };
740
- }
741
-
742
- getThinkingModelDetail(modelId: string): ThinkingModelDetailResponse | null {
743
- if (!getThinkingModel(modelId)) {
744
- return null;
745
- }
746
-
747
- const summary = this.loadThinkingModelSummaries().find((item) => item.modelId === modelId) ?? {
748
- modelId,
749
- name: getThinkingModel(modelId)?.name ?? modelId,
750
- description: getThinkingModel(modelId)?.description ?? 'Unknown thinking model.',
751
- hits: 0,
752
- coverageRate: 0,
753
- successRate: 0,
754
- failureRate: 0,
755
- painRate: 0,
756
- correctionRate: 0,
757
- correctionSampleRate: 0,
758
- commonScenarios: [],
759
- recommendation: 'archive' as const,
760
- };
761
- const usageTrend = this.uiDb.all<{ day: string; hits: number }>(`
762
- SELECT day, hits
763
- FROM v_thinking_model_daily_trend
764
- WHERE model_id = ?
765
- ORDER BY day ASC
766
- `, modelId);
767
- const scenarioDistribution = this.uiDb.all<{ scenario: string; hits: number }>(`
768
- SELECT scenario, hits
769
- FROM v_thinking_model_scenarios
770
- WHERE model_id = ?
771
- ORDER BY hits DESC, scenario ASC
772
- `, modelId);
773
- const effect = this.uiDb.get<{
774
- events: number;
775
- success_windows: number;
776
- failure_windows: number;
777
- pain_windows: number;
778
- correction_windows: number;
779
- correction_sample_windows: number;
780
- }>('SELECT * FROM v_thinking_model_effectiveness WHERE model_id = ?', modelId) ?? {
781
- events: 0,
782
- success_windows: 0,
783
- failure_windows: 0,
784
- pain_windows: 0,
785
- correction_windows: 0,
786
- correction_sample_windows: 0,
787
- };
788
- const recentEvents = this.uiDb.all<{
789
- id: number;
790
- created_at: string;
791
- matched_pattern: string;
792
- scenario_json: string;
793
- trigger_excerpt: string;
794
- tool_context_json: string;
795
- pain_context_json: string;
796
- principle_context_json: string;
797
- }>(`
798
- SELECT id, created_at, matched_pattern, scenario_json, trigger_excerpt,
799
- tool_context_json, pain_context_json, principle_context_json
800
- FROM thinking_model_events
801
- WHERE model_id = ?
802
- ORDER BY created_at DESC
803
- LIMIT 20
804
- `, modelId);
805
-
806
- return {
807
- modelMeta: {
808
- modelId: summary.modelId,
809
- name: summary.name,
810
- description: summary.description,
811
- hits: summary.hits,
812
- coverageRate: summary.coverageRate,
813
- recommendation: summary.recommendation,
814
- },
815
- usageTrend: usageTrend.map((row) => ({
816
- day: row.day,
817
- hits: Number(row.hits),
818
- })),
819
- scenarioDistribution: scenarioDistribution.map((row) => ({
820
- scenario: row.scenario,
821
- hits: Number(row.hits),
822
- })),
823
- outcomeStats: {
824
- events: Number(effect.events),
825
- successRate: roundRate(Number(effect.success_windows), Number(effect.events)),
826
- failureRate: roundRate(Number(effect.failure_windows), Number(effect.events)),
827
- painRate: roundRate(Number(effect.pain_windows), Number(effect.events)),
828
- correctionRate: roundRate(Number(effect.correction_windows), Number(effect.events)),
829
- correctionSampleRate: roundRate(Number(effect.correction_sample_windows), Number(effect.events)),
830
- },
831
- recentEvents: recentEvents.map((row) => ({
832
- id: Number(row.id),
833
- createdAt: row.created_at,
834
- matchedPattern: row.matched_pattern,
835
- scenarios: parseJson<string[]>(row.scenario_json, []),
836
- triggerExcerpt: row.trigger_excerpt,
837
- toolContext: parseJson<{ toolName: string; outcome: string; errorType?: string | null }[]>(row.tool_context_json, []),
838
- painContext: parseJson<{ source: string; score: number }[]>(row.pain_context_json, []),
839
- principleContext: parseJson<{ principleId: string | null; eventType: string }[]>(row.principle_context_json, []),
840
- })),
841
- };
842
- }
843
-
844
- private loadThinkingModelSummaries(): ThinkingModelSummary[] {
845
- const knownModels = listThinkingModels();
846
- const usageRows = new Map(this.uiDb.all<{
847
- model_id: string;
848
- hits: number;
849
- coverage_rate: number;
850
- }>('SELECT model_id, hits, coverage_rate FROM v_thinking_model_usage').map((row) => [row.model_id, row]));
851
- const effectRows = new Map(this.uiDb.all<{
852
- model_id: string;
853
- events: number;
854
- success_windows: number;
855
- failure_windows: number;
856
- pain_windows: number;
857
- correction_windows: number;
858
- correction_sample_windows: number;
859
- }>('SELECT * FROM v_thinking_model_effectiveness').map((row) => [row.model_id, row]));
860
- const scenarioRows = this.uiDb.all<{
861
- model_id: string;
862
- scenario: string;
863
- hits: number;
864
- }>('SELECT model_id, scenario, hits FROM v_thinking_model_scenarios ORDER BY hits DESC');
865
-
866
- return knownModels.map((model) => {
867
- const usage = usageRows.get(model.id);
868
- const effect = effectRows.get(model.id);
869
- const events = Number(effect?.events ?? usage?.hits ?? 0);
870
- const successRate = roundRate(Number(effect?.success_windows ?? 0), events);
871
- const failureRate = roundRate(Number(effect?.failure_windows ?? 0), events);
872
- const painRate = roundRate(Number(effect?.pain_windows ?? 0), events);
873
- const correctionRate = roundRate(Number(effect?.correction_windows ?? 0), events);
874
- const correctionSampleRate = roundRate(Number(effect?.correction_sample_windows ?? 0), events);
875
- return {
876
- modelId: model.id,
877
- name: model.name,
878
- description: model.description,
879
- hits: Number(usage?.hits ?? 0),
880
- coverageRate: Number(usage?.coverage_rate ?? 0),
881
- successRate,
882
- failureRate,
883
- painRate,
884
- correctionRate,
885
- correctionSampleRate,
886
- commonScenarios: scenarioRows
887
- .filter((row) => row.model_id === model.id)
888
- .slice(0, 3)
889
- .map((row) => row.scenario),
890
- recommendation: summarizeRecommendation({
891
- hits: Number(usage?.hits ?? 0),
892
- successRate,
893
- failureRate,
894
- painRate,
895
- correctionRate,
896
- }),
897
- };
898
- }).sort((left, right) => right.hits - left.hits || left.modelId.localeCompare(right.modelId));
899
- }
900
- }