principles-disciple 1.11.0 → 1.13.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 (233) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +66 -0
  3. package/esbuild.config.js +1 -1
  4. package/openclaw.plugin.json +4 -4
  5. package/package.json +2 -3
  6. package/run-nocturnal.mjs +30 -0
  7. package/scripts/db-migrate.mjs +170 -0
  8. package/scripts/sync-plugin.mjs +250 -6
  9. package/src/commands/archive-impl.ts +136 -0
  10. package/src/commands/capabilities.ts +4 -2
  11. package/src/commands/context.ts +5 -1
  12. package/src/commands/disable-impl.ts +151 -0
  13. package/src/commands/evolution-status.ts +64 -19
  14. package/src/commands/export.ts +8 -6
  15. package/src/commands/focus.ts +8 -20
  16. package/src/commands/nocturnal-review.ts +5 -7
  17. package/src/commands/nocturnal-rollout.ts +1 -12
  18. package/src/commands/nocturnal-train.ts +21 -47
  19. package/src/commands/pain.ts +10 -5
  20. package/src/commands/principle-rollback.ts +4 -2
  21. package/src/commands/promote-impl.ts +274 -0
  22. package/src/commands/rollback-impl.ts +234 -0
  23. package/src/commands/rollback.ts +6 -3
  24. package/src/commands/samples.ts +2 -0
  25. package/src/commands/thinking-os.ts +3 -4
  26. package/src/commands/workflow-debug.ts +2 -1
  27. package/src/config/errors.ts +1 -0
  28. package/src/core/AGENTS.md +34 -0
  29. package/src/core/adaptive-thresholds.ts +4 -3
  30. package/src/core/code-implementation-storage.ts +241 -0
  31. package/src/core/config.ts +5 -2
  32. package/src/core/control-ui-db.ts +29 -10
  33. package/src/core/detection-funnel.ts +12 -7
  34. package/src/core/diagnostician-task-store.ts +156 -0
  35. package/src/core/dictionary.ts +4 -4
  36. package/src/core/empathy-keyword-matcher.ts +7 -3
  37. package/src/core/empathy-types.ts +13 -2
  38. package/src/core/event-log.ts +14 -6
  39. package/src/core/evolution-engine.ts +27 -31
  40. package/src/core/evolution-logger.ts +3 -2
  41. package/src/core/evolution-reducer.ts +110 -31
  42. package/src/core/evolution-types.ts +10 -0
  43. package/src/core/external-training-contract.ts +1 -0
  44. package/src/core/focus-history.ts +38 -24
  45. package/src/core/hygiene/tracker.ts +10 -6
  46. package/src/core/init.ts +5 -2
  47. package/src/core/migration.ts +3 -3
  48. package/src/core/model-deployment-registry.ts +6 -4
  49. package/src/core/model-training-registry.ts +5 -3
  50. package/src/core/nocturnal-arbiter.ts +13 -14
  51. package/src/core/nocturnal-artifact-lineage.ts +117 -0
  52. package/src/core/nocturnal-artificer.ts +257 -0
  53. package/src/core/nocturnal-candidate-scoring.ts +4 -2
  54. package/src/core/nocturnal-compliance.ts +67 -19
  55. package/src/core/nocturnal-dataset.ts +95 -2
  56. package/src/core/nocturnal-executability.ts +2 -3
  57. package/src/core/nocturnal-export.ts +6 -3
  58. package/src/core/nocturnal-rule-implementation-validator.ts +245 -0
  59. package/src/core/nocturnal-trajectory-extractor.ts +10 -3
  60. package/src/core/nocturnal-trinity.ts +319 -61
  61. package/src/core/pain-context-extractor.ts +29 -15
  62. package/src/core/pain.ts +7 -5
  63. package/src/core/path-resolver.ts +16 -15
  64. package/src/core/paths.ts +2 -1
  65. package/src/core/pd-task-reconciler.ts +463 -0
  66. package/src/core/pd-task-service.ts +42 -0
  67. package/src/core/pd-task-store.ts +77 -0
  68. package/src/core/pd-task-types.ts +128 -0
  69. package/src/core/principle-internalization/deprecated-readiness.ts +91 -0
  70. package/src/core/principle-internalization/internalization-routing-policy.ts +208 -0
  71. package/src/core/principle-internalization/lifecycle-metrics.ts +149 -0
  72. package/src/core/principle-internalization/lifecycle-read-model.ts +243 -0
  73. package/src/core/principle-internalization/lifecycle-refresh.ts +11 -0
  74. package/src/core/principle-internalization/principle-lifecycle-service.ts +167 -0
  75. package/src/core/principle-training-state.ts +95 -370
  76. package/src/core/principle-tree-ledger.ts +733 -0
  77. package/src/core/principle-tree-migration.ts +195 -0
  78. package/src/core/profile.ts +3 -1
  79. package/src/core/promotion-gate.ts +14 -18
  80. package/src/core/replay-engine.ts +562 -0
  81. package/src/core/risk-calculator.ts +6 -4
  82. package/src/core/rule-host-helpers.ts +39 -0
  83. package/src/core/rule-host-types.ts +82 -0
  84. package/src/core/rule-host.ts +245 -0
  85. package/src/core/rule-implementation-runtime.ts +38 -0
  86. package/src/core/schema/db-types.ts +16 -0
  87. package/src/core/schema/index.ts +26 -0
  88. package/src/core/schema/migration-runner.ts +207 -0
  89. package/src/core/schema/migrations/001-init-trajectory.ts +211 -0
  90. package/src/core/schema/migrations/002-init-central.ts +122 -0
  91. package/src/core/schema/migrations/003-init-workflow.ts +55 -0
  92. package/src/core/schema/migrations/004-add-thinking-and-gfi.ts +74 -0
  93. package/src/core/schema/migrations/index.ts +31 -0
  94. package/src/core/schema/schema-definitions.ts +650 -0
  95. package/src/core/session-tracker.ts +6 -4
  96. package/src/core/shadow-observation-registry.ts +6 -3
  97. package/src/core/system-logger.ts +2 -2
  98. package/src/core/thinking-models.ts +182 -46
  99. package/src/core/thinking-os-parser.ts +156 -0
  100. package/src/core/training-program.ts +7 -7
  101. package/src/core/trajectory.ts +42 -36
  102. package/src/core/workspace-context.ts +77 -11
  103. package/src/core/workspace-dir-validation.ts +152 -0
  104. package/src/hooks/AGENTS.md +31 -0
  105. package/src/hooks/bash-risk.ts +3 -1
  106. package/src/hooks/edit-verification.ts +9 -5
  107. package/src/hooks/gate-block-helper.ts +5 -1
  108. package/src/hooks/gate.ts +152 -5
  109. package/src/hooks/gfi-gate.ts +9 -2
  110. package/src/hooks/lifecycle-routing.ts +124 -0
  111. package/src/hooks/lifecycle.ts +12 -12
  112. package/src/hooks/llm.ts +17 -109
  113. package/src/hooks/message-sanitize.ts +5 -3
  114. package/src/hooks/pain.ts +19 -15
  115. package/src/hooks/progressive-trust-gate.ts +7 -1
  116. package/src/hooks/prompt.ts +169 -60
  117. package/src/hooks/subagent.ts +5 -4
  118. package/src/hooks/thinking-checkpoint.ts +2 -0
  119. package/src/hooks/trajectory-collector.ts +15 -12
  120. package/src/http/principles-console-route.ts +31 -68
  121. package/src/i18n/commands.ts +2 -2
  122. package/src/index.ts +130 -40
  123. package/src/service/central-database.ts +131 -43
  124. package/src/service/central-health-service.ts +47 -0
  125. package/src/service/central-overview-service.ts +135 -0
  126. package/src/service/central-sync-service.ts +87 -0
  127. package/src/service/control-ui-query-service.ts +46 -36
  128. package/src/service/event-log-auditor.ts +261 -0
  129. package/src/service/evolution-query-service.ts +23 -22
  130. package/src/service/evolution-worker.ts +565 -261
  131. package/src/service/health-query-service.ts +213 -36
  132. package/src/service/nocturnal-runtime.ts +8 -4
  133. package/src/service/nocturnal-service.ts +503 -59
  134. package/src/service/nocturnal-target-selector.ts +5 -7
  135. package/src/service/runtime-summary-service.ts +2 -1
  136. package/src/service/subagent-workflow/deep-reflect-workflow-manager.ts +25 -336
  137. package/src/service/subagent-workflow/dynamic-timeout.ts +30 -0
  138. package/src/service/subagent-workflow/empathy-observer-workflow-manager.ts +48 -386
  139. package/src/service/subagent-workflow/index.ts +2 -0
  140. package/src/service/subagent-workflow/nocturnal-workflow-manager.ts +169 -284
  141. package/src/service/subagent-workflow/runtime-direct-driver.ts +114 -16
  142. package/src/service/subagent-workflow/subagent-error-utils.ts +25 -0
  143. package/src/service/subagent-workflow/types.ts +9 -4
  144. package/src/service/subagent-workflow/workflow-manager-base.ts +573 -0
  145. package/src/service/subagent-workflow/workflow-store.ts +71 -11
  146. package/src/service/trajectory-service.ts +2 -1
  147. package/src/tools/critique-prompt.ts +1 -1
  148. package/src/tools/deep-reflect.ts +175 -209
  149. package/src/tools/model-index.ts +2 -1
  150. package/src/types/event-types.ts +2 -2
  151. package/src/types/principle-tree-schema.ts +29 -23
  152. package/src/utils/file-lock.ts +5 -3
  153. package/src/utils/io.ts +5 -2
  154. package/src/utils/nlp.ts +5 -46
  155. package/src/utils/node-vm-polyfill.ts +11 -0
  156. package/src/utils/plugin-logger.ts +2 -0
  157. package/src/utils/retry.ts +572 -0
  158. package/src/utils/subagent-probe.ts +1 -1
  159. package/templates/langs/en/core/AGENTS.md +0 -13
  160. package/templates/langs/en/core/SOUL.md +1 -31
  161. package/templates/langs/en/core/TOOLS.md +0 -4
  162. package/templates/langs/en/principles/THINKING_OS.md +77 -0
  163. package/templates/langs/en/skills/admin/SKILL.md +0 -1
  164. package/templates/langs/en/skills/evolution-framework-update/SKILL.md +1 -1
  165. package/templates/langs/en/skills/pd-diagnostician/SKILL.md +18 -5
  166. package/templates/langs/zh/core/AGENTS.md +0 -22
  167. package/templates/langs/zh/core/SOUL.md +1 -31
  168. package/templates/langs/zh/core/TOOLS.md +0 -4
  169. package/templates/langs/zh/principles/THINKING_OS.md +77 -0
  170. package/templates/langs/zh/skills/admin/SKILL.md +0 -1
  171. package/templates/langs/zh/skills/evolution-framework-update/SKILL.md +1 -1
  172. package/templates/langs/zh/skills/pd-diagnostician/SKILL.md +25 -4
  173. package/tests/commands/evolution-status.test.ts +119 -0
  174. package/tests/commands/implementation-lifecycle.test.ts +362 -0
  175. package/tests/core/code-implementation-storage.test.ts +398 -0
  176. package/tests/core/evolution-reducer.detector-metadata.test.ts +28 -28
  177. package/tests/core/nocturnal-artifact-lineage.test.ts +53 -0
  178. package/tests/core/nocturnal-artificer.test.ts +241 -0
  179. package/tests/core/nocturnal-compliance-p-principles.test.ts +133 -0
  180. package/tests/core/nocturnal-rule-implementation-validator.test.ts +127 -0
  181. package/tests/core/pd-task-store.test.ts +126 -0
  182. package/tests/core/principle-internalization/deprecated-readiness.test.ts +193 -0
  183. package/tests/core/principle-internalization/internalization-routing-policy.test.ts +212 -0
  184. package/tests/core/principle-internalization/lifecycle-metrics.test.ts +350 -0
  185. package/tests/core/principle-internalization/principle-lifecycle-service.test.ts +211 -0
  186. package/tests/core/principle-training-state.test.ts +228 -1
  187. package/tests/core/principle-tree-ledger.test.ts +423 -0
  188. package/tests/core/regression-v1-9-1.test.ts +265 -0
  189. package/tests/core/replay-engine.test.ts +234 -0
  190. package/tests/core/rule-host-helpers.test.ts +120 -0
  191. package/tests/core/rule-host.test.ts +389 -0
  192. package/tests/core/rule-implementation-runtime.test.ts +64 -0
  193. package/tests/core/workspace-context.test.ts +53 -0
  194. package/tests/core/workspace-dir-validation.test.ts +272 -0
  195. package/tests/hooks/gate-rule-host-pipeline.test.ts +385 -0
  196. package/tests/hooks/pain.test.ts +74 -10
  197. package/tests/hooks/prompt.test.ts +63 -1
  198. package/tests/integration/principle-lifecycle.e2e.test.ts +197 -0
  199. package/tests/integration/tool-hooks-workspace-dir.e2e.test.ts +211 -0
  200. package/tests/service/data-endpoints-regression.test.ts +834 -0
  201. package/tests/service/evolution-worker.test.ts +0 -123
  202. package/tests/service/nocturnal-service-code-candidate.test.ts +330 -0
  203. package/tests/utils/nlp.test.ts +1 -19
  204. package/tests/utils/retry.test.ts +327 -0
  205. package/ui/src/App.tsx +1 -1
  206. package/ui/src/api.ts +4 -0
  207. package/ui/src/charts.tsx +366 -0
  208. package/ui/src/components/WorkspaceConfig.tsx +107 -75
  209. package/ui/src/i18n/ui.ts +89 -31
  210. package/ui/src/pages/EvolutionPage.tsx +1 -1
  211. package/ui/src/pages/OverviewPage.tsx +441 -81
  212. package/ui/src/pages/ThinkingModelsPage.tsx +287 -69
  213. package/ui/src/styles.css +43 -0
  214. package/ui/src/types.ts +17 -1
  215. package/src/agents/nocturnal-dreamer.md +0 -152
  216. package/src/agents/nocturnal-philosopher.md +0 -138
  217. package/src/agents/nocturnal-reflector.md +0 -126
  218. package/src/agents/nocturnal-scribe.md +0 -164
  219. package/templates/workspace/.principles/00-kernel.md +0 -51
  220. package/templates/workspace/.principles/DECISION_POLICY.json +0 -44
  221. package/templates/workspace/.principles/PRINCIPLES.md +0 -20
  222. package/templates/workspace/.principles/PROFILE.json +0 -54
  223. package/templates/workspace/.principles/PROFILE.schema.json +0 -56
  224. package/templates/workspace/.principles/THINKING_OS.md +0 -64
  225. package/templates/workspace/.principles/THINKING_OS_ARCHIVE.md +0 -7
  226. package/templates/workspace/.principles/THINKING_OS_CANDIDATES.md +0 -9
  227. package/templates/workspace/.principles/models/_INDEX.md +0 -27
  228. package/templates/workspace/.principles/models/first_principles.md +0 -62
  229. package/templates/workspace/.principles/models/marketing_4p.md +0 -52
  230. package/templates/workspace/.principles/models/porter_five.md +0 -63
  231. package/templates/workspace/.principles/models/swot.md +0 -60
  232. package/templates/workspace/.principles/models/user_story_map.md +0 -63
  233. package/templates/workspace/.state/WORKBOARD.json +0 -4
@@ -1,12 +1,28 @@
1
- import React, { useEffect, useState } from 'react';
2
- import { ChevronLeft } from 'lucide-react';
1
+ import React, { useEffect, useState, useMemo } from 'react';
2
+ import { ChevronLeft, Search, ArrowUpDown, Info } from 'lucide-react';
3
3
  import { api } from '../api';
4
4
  import type { ThinkingOverviewResponse, ThinkingModelDetailResponse } from '../types';
5
- import { EmptyState } from '../charts';
5
+ import { EmptyState, LineChart, StatusBadge } from '../charts';
6
6
  import { useI18n } from '../i18n/ui';
7
7
  import { formatPercent, formatDate } from '../utils/format';
8
8
  import { Loading, ErrorState } from '../components';
9
9
 
10
+ // ---------------------------------------------------------------------------
11
+ // Recommendation badge helper
12
+ // ---------------------------------------------------------------------------
13
+
14
+ type BadgeVariant = 'success' | 'warning' | 'neutral';
15
+
16
+ const REC_BADGE: Record<string, { variant: BadgeVariant; label: (t: (k: string) => string) => string }> = {
17
+ reinforce: { variant: 'success', label: (t) => t('thinkingModels.reinforce') },
18
+ rework: { variant: 'warning', label: (t) => t('thinkingModels.rework') },
19
+ archive: { variant: 'neutral', label: (t) => t('thinkingModels.archive') },
20
+ };
21
+
22
+ // ---------------------------------------------------------------------------
23
+ // ThinkingModelsPage — Redesigned Layout
24
+ // ---------------------------------------------------------------------------
25
+
10
26
  export function ThinkingModelsPage() {
11
27
  const { t } = useI18n();
12
28
  const [data, setData] = useState<ThinkingOverviewResponse | null>(null);
@@ -14,6 +30,11 @@ export function ThinkingModelsPage() {
14
30
  const [selectedModel, setSelectedModel] = useState('');
15
31
  const [error, setError] = useState('');
16
32
 
33
+ // Filters
34
+ const [recFilter, setRecFilter] = useState('all');
35
+ const [search, setSearch] = useState('');
36
+ const [sortBy, setSortBy] = useState<'hits' | 'successRate' | 'name'>('hits');
37
+
17
38
  useEffect(() => {
18
39
  api.getThinkingOverview().then((value) => {
19
40
  setData(value);
@@ -26,11 +47,37 @@ export function ThinkingModelsPage() {
26
47
  api.getThinkingModelDetail(selectedModel).then(setDetail).catch((err) => setError(String(err)));
27
48
  }, [selectedModel]);
28
49
 
50
+ // Filtered + sorted model list
51
+ const filteredModels = useMemo(() => {
52
+ if (!data) return [];
53
+ let models = [...data.topModels];
54
+ if (recFilter !== 'all') {
55
+ models = models.filter(m => m.recommendation === recFilter);
56
+ }
57
+ if (search) {
58
+ const q = search.toLowerCase();
59
+ models = models.filter(m =>
60
+ m.name.toLowerCase().includes(q) ||
61
+ (m.commonScenarios ?? []).some(s => s.toLowerCase().includes(q))
62
+ );
63
+ }
64
+ models.sort((a, b) => {
65
+ if (sortBy === 'hits') return b.hits - a.hits;
66
+ if (sortBy === 'successRate') return b.successRate - a.successRate;
67
+ return a.name.localeCompare(b.name);
68
+ });
69
+ return models;
70
+ }, [data, recFilter, search, sortBy]);
71
+
29
72
  if (error) return <ErrorState error={error} />;
30
73
  if (!data) return <Loading />;
31
74
 
75
+ const totalHits = data.topModels.reduce((sum, m) => sum + m.hits, 0);
76
+ const hasData = totalHits > 0;
77
+
32
78
  return (
33
79
  <div className="page">
80
+ {/* ── Header ── */}
34
81
  <header className="page-header">
35
82
  <div>
36
83
  <h2>{t('thinkingModels.pageTitle')}</h2>
@@ -43,85 +90,256 @@ export function ThinkingModelsPage() {
43
90
  </div>
44
91
  </header>
45
92
 
46
- <div className="grid two-columns wide-right">
47
- <section className="panel">
48
- <div className="list-table">
49
- {data.topModels.map((item) => (
50
- <button className={`table-row ${selectedModel === item.modelId ? 'active' : ''}`} key={item.modelId} onClick={() => setSelectedModel(item.modelId)}>
51
- <div>
52
- <strong>{item.name}</strong>
53
- <span>{item.commonScenarios.join(', ') || 'No scenarios yet'}</span>
54
- </div>
55
- <div>
56
- <span>{item.hits} hits</span>
57
- <span>{item.recommendation}</span>
58
- </div>
59
- <div className="align-right">
60
- <strong>{formatPercent(item.successRate)}</strong>
61
- <span>{formatPercent(item.failureRate)} failure</span>
93
+ {!hasData ? (
94
+ /* ── No data yet: show model definitions grid ── */
95
+ <section className="panel" style={{ marginBottom: 'var(--space-4)' }}>
96
+ <div style={{ textAlign: 'center', padding: 'var(--space-5)', color: 'var(--text-secondary)' }}>
97
+ <div style={{ fontSize: '2rem', marginBottom: 8 }}>🧠</div>
98
+ <h3 style={{ marginBottom: 4 }}>{t('thinkingModels.noDataTitle') || '思维模型定义'}</h3>
99
+ <p style={{ fontSize: '0.85rem', maxWidth: 500, margin: '0 auto 24px' }}>
100
+ {t('thinkingModels.noDataDesc') || '以下是 10 个思维模型的定义。当 AI 开始使用后,这里会显示每个模型的使用统计。'}
101
+ </p>
102
+ </div>
103
+ <div style={{ display: 'grid', gridTemplateColumns: 'repeat(auto-fill, minmax(280px, 1fr))', gap: 12 }}>
104
+ {data.topModels.map(model => (
105
+ <div
106
+ key={model.modelId}
107
+ style={{
108
+ padding: 12,
109
+ border: '1px solid var(--border)',
110
+ borderRadius: 8,
111
+ background: 'var(--bg-sunken)',
112
+ cursor: 'pointer',
113
+ }}
114
+ onClick={() => { setSelectedModel(model.modelId); setDetail(null); }}
115
+ >
116
+ <div style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'flex-start', marginBottom: 6 }}>
117
+ <strong style={{ fontSize: '0.85rem' }}>{model.modelId}: {model.name}</strong>
62
118
  </div>
63
- </button>
119
+ <p style={{ fontSize: '0.75rem', color: 'var(--text-secondary)', margin: '0 0 8px', lineHeight: 1.4 }}>
120
+ {model.description}
121
+ </p>
122
+ {model.commonScenarios.length > 0 && (
123
+ <div style={{ display: 'flex', flexWrap: 'wrap', gap: 4 }}>
124
+ {model.commonScenarios.slice(0, 3).map(s => (
125
+ <span key={s} style={{ fontSize: '0.65rem', padding: '1px 6px', background: 'rgba(91,139,160,0.1)', borderRadius: 3, color: 'var(--info)' }}>
126
+ {s}
127
+ </span>
128
+ ))}
129
+ </div>
130
+ )}
131
+ </div>
64
132
  ))}
65
133
  </div>
66
134
  </section>
135
+ ) : (
136
+ /* ── Has data: full dashboard ── */
137
+ <>
138
+ {/* Coverage Trend */}
139
+ {data.coverageTrend.length >= 1 && (
140
+ <section className="panel" style={{ marginBottom: 'var(--space-4)' }}>
141
+ <h3 style={{ fontSize: '0.85rem', fontWeight: 600, marginBottom: 8 }}>
142
+ {t('thinkingModels.coverageTrend')}
143
+ </h3>
144
+ <LineChart
145
+ data={data.coverageTrend.map(d => ({ label: d.day.slice(5), value: Math.round(d.coverageRate * 100) }))}
146
+ width={560}
147
+ height={140}
148
+ color="var(--accent)"
149
+ showGrid
150
+ showDots
151
+ showArea
152
+ />
153
+ </section>
154
+ )}
67
155
 
68
- <section className="panel">
69
- {!detail && <EmptyState title={t('thinkingModels.emptyTitle')} description={t('thinkingModels.emptyDesc')} />}
70
- {detail && (
71
- <div className="detail-stack">
72
- <div className="detail-header">
156
+ {/* Search + Sort + Filter */}
157
+ <div style={{ display: 'flex', gap: 8, marginBottom: 'var(--space-3)', alignItems: 'center', flexWrap: 'wrap' }}>
158
+ <div style={{ position: 'relative', flex: '1 1 200px' }}>
159
+ <Search size={14} style={{ position: 'absolute', left: 8, top: '50%', transform: 'translateY(-50%)', color: 'var(--text-secondary)' }} />
160
+ <input
161
+ type="text"
162
+ placeholder={t('common.search') || 'Search...'}
163
+ value={search}
164
+ onChange={e => setSearch(e.target.value)}
165
+ style={{ paddingLeft: 28, width: '100%', padding: '6px 8px 6px 28px', border: '1px solid var(--border)', borderRadius: 6, background: 'var(--bg-panel)', color: 'var(--text-primary)', fontSize: '0.8rem' }}
166
+ />
167
+ </div>
168
+ <button
169
+ onClick={() => setSortBy(prev => prev === 'hits' ? 'successRate' : prev === 'successRate' ? 'name' : 'hits')}
170
+ style={{ display: 'flex', alignItems: 'center', gap: 4, padding: '6px 10px', border: '1px solid var(--border)', borderRadius: 6, background: 'var(--bg-panel)', color: 'var(--text-secondary)', cursor: 'pointer', fontSize: '0.75rem' }}
171
+ >
172
+ <ArrowUpDown size={14} />
173
+ {sortBy === 'hits' ? 'Hits' : sortBy === 'successRate' ? 'Success' : 'Name'}
174
+ </button>
175
+ <div style={{ display: 'flex', gap: 4 }}>
176
+ {['all', 'reinforce', 'rework', 'archive'].map(key => (
73
177
  <button
74
- className="back-button"
75
- onClick={() => setDetail(null)}
76
- title={t('common.back') || 'Back'}
178
+ key={key}
179
+ onClick={() => setRecFilter(key)}
180
+ style={{
181
+ padding: '3px 8px',
182
+ border: `1px solid ${recFilter === key ? 'var(--accent)' : 'var(--border)'}`,
183
+ borderRadius: 4,
184
+ background: recFilter === key ? 'rgba(91, 139, 160, 0.15)' : 'transparent',
185
+ color: recFilter === key ? 'var(--accent)' : 'var(--text-secondary)',
186
+ cursor: 'pointer',
187
+ fontSize: '0.7rem',
188
+ }}
77
189
  >
78
- <ChevronLeft strokeWidth={1.75} size={18} />
190
+ {key === 'all' ? 'All' : REC_BADGE[key]?.label(t)}
79
191
  </button>
80
- <div>
81
- <h3>{detail.modelMeta.name}</h3>
82
- <p>{detail.modelMeta.description}</p>
83
- </div>
84
- <span className="badge">{detail.modelMeta.recommendation}</span>
192
+ ))}
193
+ </div>
194
+ </div>
195
+
196
+ {/* Two-column layout: Model List + Detail */}
197
+ <div className="grid two-columns wide-right">
198
+ {/* Left: Model List */}
199
+ <section className="panel">
200
+ <div className="list-table">
201
+ {filteredModels.map((item) => (
202
+ <button
203
+ className={`table-row ${selectedModel === item.modelId ? 'active' : ''}`}
204
+ key={item.modelId}
205
+ onClick={() => { setSelectedModel(item.modelId); setDetail(null); }}
206
+ >
207
+ <div>
208
+ <strong>{item.name}</strong>
209
+ <span style={{ overflow: 'hidden', textOverflow: 'ellipsis', whiteSpace: 'nowrap', maxWidth: 180, display: 'block' }}>
210
+ {item.commonScenarios.join(', ') || '—'}
211
+ </span>
212
+ </div>
213
+ <div style={{ display: 'flex', alignItems: 'center', gap: 6 }}>
214
+ <span style={{ fontWeight: 600, fontSize: '0.85rem' }}>{item.hits}</span>
215
+ {REC_BADGE[item.recommendation] && (
216
+ <StatusBadge variant={REC_BADGE[item.recommendation].variant}>
217
+ {REC_BADGE[item.recommendation].label(t)}
218
+ </StatusBadge>
219
+ )}
220
+ </div>
221
+ </button>
222
+ ))}
223
+ {filteredModels.length === 0 && (
224
+ <div style={{ padding: 'var(--space-4)', textAlign: 'center', color: 'var(--text-secondary)', fontSize: '0.85rem' }}>
225
+ No models match your filters.
226
+ </div>
227
+ )}
85
228
  </div>
86
- <article>
87
- <h4>{t('thinkingModels.outcomeStats')}</h4>
88
- <div className="pill-row">
89
- <span className="badge">{t('thinkingModels.success')} {formatPercent(detail.outcomeStats.successRate)}</span>
90
- <span className="badge">{t('thinkingModels.failure')} {formatPercent(detail.outcomeStats.failureRate)}</span>
91
- <span className="badge">{t('thinkingModels.pain')} {formatPercent(detail.outcomeStats.painRate)}</span>
92
- <span className="badge">{t('thinkingModels.correction')} {formatPercent(detail.outcomeStats.correctionRate)}</span>
93
- </div>
94
- </article>
95
- <article>
96
- <h4>{t('thinkingModels.scenarioDistribution')}</h4>
97
- <div className="stack">
98
- {detail.scenarioDistribution.map((item) => (
99
- <div className="row-card" key={item.scenario}>
100
- <strong>{item.scenario}</strong>
101
- <span>{item.hits}</span>
229
+ </section>
230
+
231
+ {/* Right: Detail Panel */}
232
+ <section className="panel">
233
+ {!detail && <EmptyState title={t('thinkingModels.emptyTitle')} description={t('thinkingModels.emptyDesc')} />}
234
+ {detail && (
235
+ <div className="detail-stack">
236
+ <div className="detail-header">
237
+ <button className="back-button" onClick={() => setDetail(null)} title="Back">
238
+ <ChevronLeft strokeWidth={1.75} size={18} />
239
+ </button>
240
+ <div>
241
+ <h3>{detail.modelMeta.name}</h3>
242
+ <p style={{ fontSize: '0.8rem', color: 'var(--text-secondary)' }}>{detail.modelMeta.description}</p>
102
243
  </div>
103
- ))}
104
- </div>
105
- </article>
106
- <article>
107
- <h4>{t('thinkingModels.recentEvents')}</h4>
108
- <div className="stack">
109
- {detail.recentEvents.map((event) => (
110
- <div className="row-card vertical" key={event.id}>
111
- <div>
112
- <strong>{formatDate(event.createdAt)}</strong>
113
- <span>{event.scenarios.join(', ') || 'No scenarios'}</span>
244
+ {REC_BADGE[detail.modelMeta.recommendation] && (
245
+ <StatusBadge variant={REC_BADGE[detail.modelMeta.recommendation].variant}>
246
+ {REC_BADGE[detail.modelMeta.recommendation].label(t)}
247
+ </StatusBadge>
248
+ )}
249
+ </div>
250
+
251
+ {/* Usage Trend */}
252
+ {detail.usageTrend.length >= 1 && (
253
+ <article>
254
+ <h4 style={{ fontSize: '0.8rem', fontWeight: 600, marginBottom: 8 }}>
255
+ {t('thinkingModels.usageTrend') || 'Usage Trend'}
256
+ </h4>
257
+ <LineChart
258
+ data={detail.usageTrend.map(d => ({ label: d.day.slice(5), value: d.hits }))}
259
+ width={500}
260
+ height={100}
261
+ color="var(--accent)"
262
+ showGrid
263
+ showDots
264
+ showArea
265
+ />
266
+ </article>
267
+ )}
268
+
269
+ {/* Outcome Stats */}
270
+ <article>
271
+ <h4>{t('thinkingModels.outcomeStats')}</h4>
272
+ <div className="pill-row">
273
+ <span className="badge">{t('thinkingModels.success')} {formatPercent(detail.outcomeStats.successRate)}</span>
274
+ <span className="badge">{t('thinkingModels.failure')} {formatPercent(detail.outcomeStats.failureRate)}</span>
275
+ <span className="badge">{t('thinkingModels.pain')} {formatPercent(detail.outcomeStats.painRate)}</span>
276
+ <span className="badge">{t('thinkingModels.correction')} {formatPercent(detail.outcomeStats.correctionRate)}</span>
277
+ </div>
278
+ </article>
279
+
280
+ {/* Scenario Distribution */}
281
+ {detail.scenarioDistribution.length > 0 && (
282
+ <article>
283
+ <h4>{t('thinkingModels.scenarioDistribution')}</h4>
284
+ <div className="stack">
285
+ {detail.scenarioDistribution.map((item) => (
286
+ <div className="row-card" key={item.scenario}>
287
+ <strong>{item.scenario}</strong>
288
+ <span>{item.hits}</span>
289
+ </div>
290
+ ))}
114
291
  </div>
115
- <pre>{event.triggerExcerpt}</pre>
292
+ </article>
293
+ )}
294
+
295
+ {/* Recent Events */}
296
+ {detail.recentEvents.length > 0 && (
297
+ <article>
298
+ <h4>{t('thinkingModels.recentEvents')}</h4>
299
+ <div className="stack">
300
+ {detail.recentEvents.map((event) => (
301
+ <div className="row-card vertical" key={event.id}>
302
+ <div>
303
+ <strong>{formatDate(event.createdAt)}</strong>
304
+ <span>{event.scenarios.join(', ') || '—'}</span>
305
+ </div>
306
+ {(event as any).toolContext?.length > 0 && (
307
+ <div style={{ fontSize: '0.7rem', color: 'var(--text-secondary)' }}>
308
+ 🛠 {(event as any).toolContext.map((tc: any) => `${tc.toolName} (${tc.outcome})`).join(', ')}
309
+ </div>
310
+ )}
311
+ {(event as any).painContext?.length > 0 && (
312
+ <div style={{ fontSize: '0.7rem', color: 'var(--error)' }}>
313
+ ⚡ {(event as any).painContext.map((pc: any) => `${pc.source} (${pc.score})`).join(', ')}
314
+ </div>
315
+ )}
316
+ {(event as any).principleContext?.length > 0 && (
317
+ <div style={{ fontSize: '0.7rem', color: 'var(--info)' }}>
318
+ 📋 {(event as any).principleContext.map((pr: any) => `${pr.principleId}`).join(', ')}
319
+ </div>
320
+ )}
321
+ <pre style={{ fontSize: '0.7rem', whiteSpace: 'pre-wrap', wordBreak: 'break-word' }}>
322
+ {event.triggerExcerpt}
323
+ </pre>
324
+ </div>
325
+ ))}
326
+ </div>
327
+ </article>
328
+ )}
329
+
330
+ {/* No data message for detail */}
331
+ {detail.usageTrend.length === 0 && detail.recentEvents.length === 0 && (
332
+ <div style={{ textAlign: 'center', padding: 'var(--space-4)', color: 'var(--text-secondary)' }}>
333
+ <Info size={20} style={{ marginBottom: 8 }} />
334
+ <p style={{ fontSize: '0.8rem' }}>No usage data for this model yet.</p>
116
335
  </div>
117
- ))}
336
+ )}
118
337
  </div>
119
- </article>
120
- </div>
121
- )}
122
- </section>
123
- </div>
338
+ )}
339
+ </section>
340
+ </div>
341
+ </>
342
+ )}
124
343
  </div>
125
344
  );
126
345
  }
127
-
package/ui/src/styles.css CHANGED
@@ -1659,3 +1659,46 @@ pre {
1659
1659
  .collapsible-panel.collapsed .panel-header {
1660
1660
  border-bottom: none;
1661
1661
  }
1662
+
1663
+ /* ========================================================================
1664
+ * New Chart Components (Phase: WebUI Redesign)
1665
+ * BulletChart, GaugeChart, PrincipleStack, QueueBar
1666
+ * ======================================================================== */
1667
+
1668
+ /* BulletChart - GFI with threshold zones */
1669
+ .bullet-chart {
1670
+ display: flex;
1671
+ flex-direction: column;
1672
+ }
1673
+
1674
+ .bullet-labels {
1675
+ display: flex;
1676
+ justify-content: space-between;
1677
+ margin-top: 2px;
1678
+ font-size: 0.7rem;
1679
+ color: var(--text-secondary);
1680
+ user-select: none;
1681
+ }
1682
+
1683
+ /* GaugeChart - Trust semi-circle arc */
1684
+ .gauge-chart svg text {
1685
+ font-family: inherit;
1686
+ }
1687
+
1688
+ /* Panel header improvements */
1689
+ .panel-header-left {
1690
+ display: flex;
1691
+ align-items: center;
1692
+ gap: var(--space-2);
1693
+ }
1694
+
1695
+ .panel-header-left h3 {
1696
+ margin: 0;
1697
+ }
1698
+
1699
+ /* Responsive grid for health panels */
1700
+ @media (max-width: 768px) {
1701
+ .grid {
1702
+ grid-template-columns: 1fr !important;
1703
+ }
1704
+ }
package/ui/src/types.ts CHANGED
@@ -42,6 +42,12 @@ export interface OverviewResponse {
42
42
  dormantModels: number;
43
43
  effectiveModels: number;
44
44
  coverageRate: number;
45
+ modelBreakdown?: Array<{ modelId: string; hits: number }>;
46
+ modelDefinitions?: Array<{
47
+ modelId: string;
48
+ name: string;
49
+ description: string;
50
+ }>;
45
51
  };
46
52
  }
47
53
 
@@ -287,7 +293,7 @@ export interface EvolutionStatsResponse {
287
293
  // ===== Phase 5: Health & Circuit API Types =====
288
294
 
289
295
  export interface OverviewHealthResponse {
290
- gfi: { current: number; peakToday: number; threshold: number };
296
+ gfi: { current: number; peakToday: number; threshold: number; trend: Array<{ hour: string; value: number }> };
291
297
  trust: { stage: number; stageLabel: string; score: number };
292
298
  evolution: { tier: string; points: number };
293
299
  painFlag: { active: boolean; source: string | null; score: number | null };
@@ -296,6 +302,16 @@ export interface OverviewHealthResponse {
296
302
  activeStage: string;
297
303
  }
298
304
 
305
+ export interface WorkspaceHealthEntry {
306
+ workspaceName: string;
307
+ health: OverviewHealthResponse;
308
+ }
309
+
310
+ export interface CentralHealthResponse {
311
+ workspaces: WorkspaceHealthEntry[];
312
+ generatedAt: string;
313
+ }
314
+
299
315
  export interface EvolutionPrinciplesResponse {
300
316
  principles: {
301
317
  summary: { candidate: number; probation: number; active: number; deprecated: number };
@@ -1,152 +0,0 @@
1
- # Nocturnal Dreamer — Candidate Generation
2
-
3
- > System prompt for Trinity Dreamer stage.
4
- > Role: Generate multiple alternative "better decision" candidates from a session snapshot.
5
-
6
- ## Role
7
-
8
- You are a principles analyst specializing in identifying decision alternatives.
9
- Your task is to analyze a session trajectory and generate **multiple candidate corrections**,
10
- each representing a different valid approach to the same problem.
11
-
12
- ## Input
13
-
14
- You will receive:
15
- - A **target principle** (principle ID and description)
16
- - A **session trajectory snapshot** containing:
17
- - Assistant turns (sanitized text, no raw content)
18
- - User turns (correction cues only, no raw content)
19
- - Tool calls with outcomes and error messages
20
- - Pain events and gate blocks
21
- - Session metadata
22
-
23
- ## Task
24
-
25
- Analyze the session and generate **2-3 candidate corrections**, each capturing:
26
-
27
- 1. **The bad decision**: What the agent decided or did that violated the target principle
28
- 2. **The better decision**: What the agent should have done instead (unique per candidate)
29
- 3. **The rationale**: Why this alternative is better
30
- 4. **Confidence**: How confident you are this is a valid alternative (0.0-1.0)
31
-
32
- ## Output Format
33
-
34
- You MUST respond with ONLY a valid JSON object. No markdown, no explanation, no preamble.
35
-
36
- ```json
37
- {
38
- "valid": true,
39
- "candidates": [
40
- {
41
- "candidateIndex": 0,
42
- "badDecision": "<what the agent did wrong>",
43
- "betterDecision": "<what the agent should have done>",
44
- "rationale": "<why this is better>",
45
- "confidence": 0.95
46
- },
47
- {
48
- "candidateIndex": 1,
49
- "badDecision": "<same or different bad decision>",
50
- "betterDecision": "<different alternative approach>",
51
- "rationale": "<why this alternative is better>",
52
- "confidence": 0.85
53
- }
54
- ],
55
- "generatedAt": "<ISO timestamp>"
56
- }
57
- ```
58
-
59
- ## Quality Standards
60
-
61
- ### Each candidate MUST:
62
- - Have a `candidateIndex` that is unique within the candidate list
63
- - Describe a **specific, concrete** badDecision (not generic anti-patterns)
64
- - Propose a **specific, actionable** betterDecision (contains an action verb)
65
- - Provide a **principle-grounded** rationale (explicitly references the principle)
66
- - Include a **confidence** score (0.0-1.0, higher = more confident)
67
-
68
- ### Candidates should DIFFER from each other:
69
- - Different candidates should represent genuinely different approaches
70
- - Do not generate candidates with identical betterDecisions
71
- - Vary the confidence scores to reflect genuine uncertainty
72
-
73
- ### Candidates must NOT:
74
- - Contain raw user text or private content
75
- - Reference non-existent tools or impossible actions
76
- - Propose vague improvements ("be more careful")
77
- - Exceed the requested number of candidates
78
-
79
- ## Validation
80
-
81
- If you cannot generate valid candidates (e.g., no clear violation found, insufficient data), respond with:
82
-
83
- ```json
84
- {
85
- "valid": false,
86
- "candidates": [],
87
- "reason": "<why valid candidates cannot be generated>",
88
- "generatedAt": "<ISO timestamp>"
89
- }
90
- ```
91
-
92
- ## Examples
93
-
94
- ### Example: T-01 (Map Before Territory)
95
-
96
- Input principle: `T-01` — "Map Before Territory: Always survey the existing structure before making changes"
97
-
98
- Session: Agent edits `src/main.ts` without reading it first, causing a merge conflict.
99
-
100
- Valid output:
101
- ```json
102
- {
103
- "valid": true,
104
- "candidates": [
105
- {
106
- "candidateIndex": 0,
107
- "badDecision": "Edited src/main.ts without first reading its contents, leading to a merge conflict",
108
- "betterDecision": "Read src/main.ts to understand its current structure before making any edits",
109
- "rationale": "Surveying existing territory prevents conflicts and ensures edits integrate properly",
110
- "confidence": 0.95
111
- },
112
- {
113
- "candidateIndex": 1,
114
- "badDecision": "Made assumptions about function signatures without verifying them",
115
- "betterDecision": "Search for existing function definitions to understand the API contract",
116
- "rationale": "Verifying API contracts before use prevents integration errors",
117
- "confidence": 0.88
118
- }
119
- ],
120
- "generatedAt": "2026-03-27T12:00:00.000Z"
121
- }
122
- ```
123
-
124
- ### Example: T-08 (Pain as Signal)
125
-
126
- Input principle: `T-08` — "Pain as Signal: Treat failures and errors as signals to pause and reflect"
127
-
128
- Session: Agent retries a failing bash command 3 times without any diagnosis.
129
-
130
- Valid output:
131
- ```json
132
- {
133
- "valid": true,
134
- "candidates": [
135
- {
136
- "candidateIndex": 0,
137
- "badDecision": "Retried failing bash command 3 times without diagnosing the root cause",
138
- "betterDecision": "Check the error message and verify tool installation before retrying",
139
- "rationale": "Diagnosing failures prevents repeated failures and respects action cost",
140
- "confidence": 0.92
141
- },
142
- {
143
- "candidateIndex": 1,
144
- "badDecision": "Continued to the next operation after a bash failure without addressing it",
145
- "betterDecision": "Pause and diagnose the failure before continuing with dependent operations",
146
- "rationale": "Unaddressed failures compound and cause larger issues downstream",
147
- "confidence": 0.85
148
- }
149
- ],
150
- "generatedAt": "2026-03-27T12:05:00.000Z"
151
- }
152
- ```