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.
- package/CHANGELOG.md +26 -0
- package/README.md +66 -0
- package/esbuild.config.js +1 -1
- package/openclaw.plugin.json +4 -4
- package/package.json +2 -3
- package/run-nocturnal.mjs +30 -0
- package/scripts/db-migrate.mjs +170 -0
- package/scripts/sync-plugin.mjs +250 -6
- package/src/commands/archive-impl.ts +136 -0
- package/src/commands/capabilities.ts +4 -2
- package/src/commands/context.ts +5 -1
- package/src/commands/disable-impl.ts +151 -0
- package/src/commands/evolution-status.ts +64 -19
- package/src/commands/export.ts +8 -6
- package/src/commands/focus.ts +8 -20
- package/src/commands/nocturnal-review.ts +5 -7
- package/src/commands/nocturnal-rollout.ts +1 -12
- package/src/commands/nocturnal-train.ts +21 -47
- package/src/commands/pain.ts +10 -5
- package/src/commands/principle-rollback.ts +4 -2
- package/src/commands/promote-impl.ts +274 -0
- package/src/commands/rollback-impl.ts +234 -0
- package/src/commands/rollback.ts +6 -3
- package/src/commands/samples.ts +2 -0
- package/src/commands/thinking-os.ts +3 -4
- package/src/commands/workflow-debug.ts +2 -1
- package/src/config/errors.ts +1 -0
- package/src/core/AGENTS.md +34 -0
- package/src/core/adaptive-thresholds.ts +4 -3
- package/src/core/code-implementation-storage.ts +241 -0
- package/src/core/config.ts +5 -2
- package/src/core/control-ui-db.ts +29 -10
- package/src/core/detection-funnel.ts +12 -7
- package/src/core/diagnostician-task-store.ts +156 -0
- package/src/core/dictionary.ts +4 -4
- package/src/core/empathy-keyword-matcher.ts +7 -3
- package/src/core/empathy-types.ts +13 -2
- package/src/core/event-log.ts +14 -6
- package/src/core/evolution-engine.ts +27 -31
- package/src/core/evolution-logger.ts +3 -2
- package/src/core/evolution-reducer.ts +110 -31
- package/src/core/evolution-types.ts +10 -0
- package/src/core/external-training-contract.ts +1 -0
- package/src/core/focus-history.ts +38 -24
- package/src/core/hygiene/tracker.ts +10 -6
- package/src/core/init.ts +5 -2
- package/src/core/migration.ts +3 -3
- package/src/core/model-deployment-registry.ts +6 -4
- package/src/core/model-training-registry.ts +5 -3
- package/src/core/nocturnal-arbiter.ts +13 -14
- package/src/core/nocturnal-artifact-lineage.ts +117 -0
- package/src/core/nocturnal-artificer.ts +257 -0
- package/src/core/nocturnal-candidate-scoring.ts +4 -2
- package/src/core/nocturnal-compliance.ts +67 -19
- package/src/core/nocturnal-dataset.ts +95 -2
- package/src/core/nocturnal-executability.ts +2 -3
- package/src/core/nocturnal-export.ts +6 -3
- package/src/core/nocturnal-rule-implementation-validator.ts +245 -0
- package/src/core/nocturnal-trajectory-extractor.ts +10 -3
- package/src/core/nocturnal-trinity.ts +319 -61
- package/src/core/pain-context-extractor.ts +29 -15
- package/src/core/pain.ts +7 -5
- package/src/core/path-resolver.ts +16 -15
- package/src/core/paths.ts +2 -1
- package/src/core/pd-task-reconciler.ts +463 -0
- package/src/core/pd-task-service.ts +42 -0
- package/src/core/pd-task-store.ts +77 -0
- package/src/core/pd-task-types.ts +128 -0
- package/src/core/principle-internalization/deprecated-readiness.ts +91 -0
- package/src/core/principle-internalization/internalization-routing-policy.ts +208 -0
- package/src/core/principle-internalization/lifecycle-metrics.ts +149 -0
- package/src/core/principle-internalization/lifecycle-read-model.ts +243 -0
- package/src/core/principle-internalization/lifecycle-refresh.ts +11 -0
- package/src/core/principle-internalization/principle-lifecycle-service.ts +167 -0
- package/src/core/principle-training-state.ts +95 -370
- package/src/core/principle-tree-ledger.ts +733 -0
- package/src/core/principle-tree-migration.ts +195 -0
- package/src/core/profile.ts +3 -1
- package/src/core/promotion-gate.ts +14 -18
- package/src/core/replay-engine.ts +562 -0
- package/src/core/risk-calculator.ts +6 -4
- package/src/core/rule-host-helpers.ts +39 -0
- package/src/core/rule-host-types.ts +82 -0
- package/src/core/rule-host.ts +245 -0
- package/src/core/rule-implementation-runtime.ts +38 -0
- package/src/core/schema/db-types.ts +16 -0
- package/src/core/schema/index.ts +26 -0
- package/src/core/schema/migration-runner.ts +207 -0
- package/src/core/schema/migrations/001-init-trajectory.ts +211 -0
- package/src/core/schema/migrations/002-init-central.ts +122 -0
- package/src/core/schema/migrations/003-init-workflow.ts +55 -0
- package/src/core/schema/migrations/004-add-thinking-and-gfi.ts +74 -0
- package/src/core/schema/migrations/index.ts +31 -0
- package/src/core/schema/schema-definitions.ts +650 -0
- package/src/core/session-tracker.ts +6 -4
- package/src/core/shadow-observation-registry.ts +6 -3
- package/src/core/system-logger.ts +2 -2
- package/src/core/thinking-models.ts +182 -46
- package/src/core/thinking-os-parser.ts +156 -0
- package/src/core/training-program.ts +7 -7
- package/src/core/trajectory.ts +42 -36
- package/src/core/workspace-context.ts +77 -11
- package/src/core/workspace-dir-validation.ts +152 -0
- package/src/hooks/AGENTS.md +31 -0
- package/src/hooks/bash-risk.ts +3 -1
- package/src/hooks/edit-verification.ts +9 -5
- package/src/hooks/gate-block-helper.ts +5 -1
- package/src/hooks/gate.ts +152 -5
- package/src/hooks/gfi-gate.ts +9 -2
- package/src/hooks/lifecycle-routing.ts +124 -0
- package/src/hooks/lifecycle.ts +12 -12
- package/src/hooks/llm.ts +17 -109
- package/src/hooks/message-sanitize.ts +5 -3
- package/src/hooks/pain.ts +19 -15
- package/src/hooks/progressive-trust-gate.ts +7 -1
- package/src/hooks/prompt.ts +169 -60
- package/src/hooks/subagent.ts +5 -4
- package/src/hooks/thinking-checkpoint.ts +2 -0
- package/src/hooks/trajectory-collector.ts +15 -12
- package/src/http/principles-console-route.ts +31 -68
- package/src/i18n/commands.ts +2 -2
- package/src/index.ts +130 -40
- package/src/service/central-database.ts +131 -43
- package/src/service/central-health-service.ts +47 -0
- package/src/service/central-overview-service.ts +135 -0
- package/src/service/central-sync-service.ts +87 -0
- package/src/service/control-ui-query-service.ts +46 -36
- package/src/service/event-log-auditor.ts +261 -0
- package/src/service/evolution-query-service.ts +23 -22
- package/src/service/evolution-worker.ts +565 -261
- package/src/service/health-query-service.ts +213 -36
- package/src/service/nocturnal-runtime.ts +8 -4
- package/src/service/nocturnal-service.ts +503 -59
- package/src/service/nocturnal-target-selector.ts +5 -7
- package/src/service/runtime-summary-service.ts +2 -1
- package/src/service/subagent-workflow/deep-reflect-workflow-manager.ts +25 -336
- package/src/service/subagent-workflow/dynamic-timeout.ts +30 -0
- package/src/service/subagent-workflow/empathy-observer-workflow-manager.ts +48 -386
- package/src/service/subagent-workflow/index.ts +2 -0
- package/src/service/subagent-workflow/nocturnal-workflow-manager.ts +169 -284
- package/src/service/subagent-workflow/runtime-direct-driver.ts +114 -16
- package/src/service/subagent-workflow/subagent-error-utils.ts +25 -0
- package/src/service/subagent-workflow/types.ts +9 -4
- package/src/service/subagent-workflow/workflow-manager-base.ts +573 -0
- package/src/service/subagent-workflow/workflow-store.ts +71 -11
- package/src/service/trajectory-service.ts +2 -1
- package/src/tools/critique-prompt.ts +1 -1
- package/src/tools/deep-reflect.ts +175 -209
- package/src/tools/model-index.ts +2 -1
- package/src/types/event-types.ts +2 -2
- package/src/types/principle-tree-schema.ts +29 -23
- package/src/utils/file-lock.ts +5 -3
- package/src/utils/io.ts +5 -2
- package/src/utils/nlp.ts +5 -46
- package/src/utils/node-vm-polyfill.ts +11 -0
- package/src/utils/plugin-logger.ts +2 -0
- package/src/utils/retry.ts +572 -0
- package/src/utils/subagent-probe.ts +1 -1
- package/templates/langs/en/core/AGENTS.md +0 -13
- package/templates/langs/en/core/SOUL.md +1 -31
- package/templates/langs/en/core/TOOLS.md +0 -4
- package/templates/langs/en/principles/THINKING_OS.md +77 -0
- package/templates/langs/en/skills/admin/SKILL.md +0 -1
- package/templates/langs/en/skills/evolution-framework-update/SKILL.md +1 -1
- package/templates/langs/en/skills/pd-diagnostician/SKILL.md +18 -5
- package/templates/langs/zh/core/AGENTS.md +0 -22
- package/templates/langs/zh/core/SOUL.md +1 -31
- package/templates/langs/zh/core/TOOLS.md +0 -4
- package/templates/langs/zh/principles/THINKING_OS.md +77 -0
- package/templates/langs/zh/skills/admin/SKILL.md +0 -1
- package/templates/langs/zh/skills/evolution-framework-update/SKILL.md +1 -1
- package/templates/langs/zh/skills/pd-diagnostician/SKILL.md +25 -4
- package/tests/commands/evolution-status.test.ts +119 -0
- package/tests/commands/implementation-lifecycle.test.ts +362 -0
- package/tests/core/code-implementation-storage.test.ts +398 -0
- package/tests/core/evolution-reducer.detector-metadata.test.ts +28 -28
- package/tests/core/nocturnal-artifact-lineage.test.ts +53 -0
- package/tests/core/nocturnal-artificer.test.ts +241 -0
- package/tests/core/nocturnal-compliance-p-principles.test.ts +133 -0
- package/tests/core/nocturnal-rule-implementation-validator.test.ts +127 -0
- package/tests/core/pd-task-store.test.ts +126 -0
- package/tests/core/principle-internalization/deprecated-readiness.test.ts +193 -0
- package/tests/core/principle-internalization/internalization-routing-policy.test.ts +212 -0
- package/tests/core/principle-internalization/lifecycle-metrics.test.ts +350 -0
- package/tests/core/principle-internalization/principle-lifecycle-service.test.ts +211 -0
- package/tests/core/principle-training-state.test.ts +228 -1
- package/tests/core/principle-tree-ledger.test.ts +423 -0
- package/tests/core/regression-v1-9-1.test.ts +265 -0
- package/tests/core/replay-engine.test.ts +234 -0
- package/tests/core/rule-host-helpers.test.ts +120 -0
- package/tests/core/rule-host.test.ts +389 -0
- package/tests/core/rule-implementation-runtime.test.ts +64 -0
- package/tests/core/workspace-context.test.ts +53 -0
- package/tests/core/workspace-dir-validation.test.ts +272 -0
- package/tests/hooks/gate-rule-host-pipeline.test.ts +385 -0
- package/tests/hooks/pain.test.ts +74 -10
- package/tests/hooks/prompt.test.ts +63 -1
- package/tests/integration/principle-lifecycle.e2e.test.ts +197 -0
- package/tests/integration/tool-hooks-workspace-dir.e2e.test.ts +211 -0
- package/tests/service/data-endpoints-regression.test.ts +834 -0
- package/tests/service/evolution-worker.test.ts +0 -123
- package/tests/service/nocturnal-service-code-candidate.test.ts +330 -0
- package/tests/utils/nlp.test.ts +1 -19
- package/tests/utils/retry.test.ts +327 -0
- package/ui/src/App.tsx +1 -1
- package/ui/src/api.ts +4 -0
- package/ui/src/charts.tsx +366 -0
- package/ui/src/components/WorkspaceConfig.tsx +107 -75
- package/ui/src/i18n/ui.ts +89 -31
- package/ui/src/pages/EvolutionPage.tsx +1 -1
- package/ui/src/pages/OverviewPage.tsx +441 -81
- package/ui/src/pages/ThinkingModelsPage.tsx +287 -69
- package/ui/src/styles.css +43 -0
- package/ui/src/types.ts +17 -1
- package/src/agents/nocturnal-dreamer.md +0 -152
- package/src/agents/nocturnal-philosopher.md +0 -138
- package/src/agents/nocturnal-reflector.md +0 -126
- package/src/agents/nocturnal-scribe.md +0 -164
- package/templates/workspace/.principles/00-kernel.md +0 -51
- package/templates/workspace/.principles/DECISION_POLICY.json +0 -44
- package/templates/workspace/.principles/PRINCIPLES.md +0 -20
- package/templates/workspace/.principles/PROFILE.json +0 -54
- package/templates/workspace/.principles/PROFILE.schema.json +0 -56
- package/templates/workspace/.principles/THINKING_OS.md +0 -64
- package/templates/workspace/.principles/THINKING_OS_ARCHIVE.md +0 -7
- package/templates/workspace/.principles/THINKING_OS_CANDIDATES.md +0 -9
- package/templates/workspace/.principles/models/_INDEX.md +0 -27
- package/templates/workspace/.principles/models/first_principles.md +0 -62
- package/templates/workspace/.principles/models/marketing_4p.md +0 -52
- package/templates/workspace/.principles/models/porter_five.md +0 -63
- package/templates/workspace/.principles/models/swot.md +0 -60
- package/templates/workspace/.principles/models/user_story_map.md +0 -63
- 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
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
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
|
-
|
|
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
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
<
|
|
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
|
-
|
|
75
|
-
onClick={() =>
|
|
76
|
-
|
|
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
|
-
|
|
190
|
+
{key === 'all' ? 'All' : REC_BADGE[key]?.label(t)}
|
|
79
191
|
</button>
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
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
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
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
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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
|
-
|
|
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
|
-
|
|
120
|
-
</
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
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
|
-
```
|