@agentdeck/bridge 1.0.23 → 1.1.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/dist/adapters/openclaw.d.ts +74 -4
- package/dist/adapters/openclaw.d.ts.map +1 -1
- package/dist/adapters/openclaw.js +235 -43
- package/dist/adapters/openclaw.js.map +1 -1
- package/dist/apme/adapters/openclaw-hook.d.ts +38 -10
- package/dist/apme/adapters/openclaw-hook.d.ts.map +1 -1
- package/dist/apme/adapters/openclaw-hook.js +181 -37
- package/dist/apme/adapters/openclaw-hook.js.map +1 -1
- package/dist/apme/collector.d.ts +37 -1
- package/dist/apme/collector.d.ts.map +1 -1
- package/dist/apme/collector.js +277 -35
- package/dist/apme/collector.js.map +1 -1
- package/dist/apme/pareto.d.ts +2 -1
- package/dist/apme/pareto.d.ts.map +1 -1
- package/dist/apme/pareto.js +15 -4
- package/dist/apme/pareto.js.map +1 -1
- package/dist/apme/recommend.d.ts +24 -1
- package/dist/apme/recommend.d.ts.map +1 -1
- package/dist/apme/recommend.js +43 -7
- package/dist/apme/recommend.js.map +1 -1
- package/dist/apme/store.d.ts +6 -0
- package/dist/apme/store.d.ts.map +1 -1
- package/dist/apme/store.js +244 -72
- package/dist/apme/store.js.map +1 -1
- package/dist/bridge-core.d.ts +10 -1
- package/dist/bridge-core.d.ts.map +1 -1
- package/dist/bridge-core.js +20 -5
- package/dist/bridge-core.js.map +1 -1
- package/dist/card-feed.d.ts +46 -1
- package/dist/card-feed.d.ts.map +1 -1
- package/dist/card-feed.js +166 -6
- package/dist/card-feed.js.map +1 -1
- package/dist/cli.d.ts +45 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +388 -27
- package/dist/cli.js.map +1 -1
- package/dist/daemon-build-identity.d.ts +124 -0
- package/dist/daemon-build-identity.d.ts.map +1 -0
- package/dist/daemon-build-identity.js +310 -0
- package/dist/daemon-build-identity.js.map +1 -0
- package/dist/daemon-server.d.ts +34 -6
- package/dist/daemon-server.d.ts.map +1 -1
- package/dist/daemon-server.js +613 -108
- package/dist/daemon-server.js.map +1 -1
- package/dist/daemon-takeover.d.ts +11 -0
- package/dist/daemon-takeover.d.ts.map +1 -1
- package/dist/daemon-takeover.js +62 -17
- package/dist/daemon-takeover.js.map +1 -1
- package/dist/daemon.js +1 -1
- package/dist/daemon.js.map +1 -1
- package/dist/esp32-flash.d.ts.map +1 -1
- package/dist/esp32-flash.js +19 -3
- package/dist/esp32-flash.js.map +1 -1
- package/dist/esp32-serial.d.ts +14 -0
- package/dist/esp32-serial.d.ts.map +1 -1
- package/dist/esp32-serial.js +19 -1
- package/dist/esp32-serial.js.map +1 -1
- package/dist/glance-frame.d.ts.map +1 -1
- package/dist/glance-frame.js +3 -1
- package/dist/glance-frame.js.map +1 -1
- package/dist/hook-codex-sessions.d.ts +3 -1
- package/dist/hook-codex-sessions.d.ts.map +1 -1
- package/dist/hook-codex-sessions.js +27 -7
- package/dist/hook-codex-sessions.js.map +1 -1
- package/dist/legacy-rearm-ledger.d.ts +56 -0
- package/dist/legacy-rearm-ledger.d.ts.map +1 -0
- package/dist/legacy-rearm-ledger.js +66 -0
- package/dist/legacy-rearm-ledger.js.map +1 -0
- package/dist/mdns.d.ts +33 -0
- package/dist/mdns.d.ts.map +1 -1
- package/dist/mdns.js +91 -2
- package/dist/mdns.js.map +1 -1
- package/dist/openclaw-session.d.ts.map +1 -1
- package/dist/openclaw-session.js +13 -0
- package/dist/openclaw-session.js.map +1 -1
- package/dist/openclaw-timeline-feed.d.ts +84 -0
- package/dist/openclaw-timeline-feed.d.ts.map +1 -0
- package/dist/openclaw-timeline-feed.js +162 -0
- package/dist/openclaw-timeline-feed.js.map +1 -0
- package/dist/openclaw-transcript-timeline.d.ts +103 -0
- package/dist/openclaw-transcript-timeline.d.ts.map +1 -0
- package/dist/openclaw-transcript-timeline.js +328 -0
- package/dist/openclaw-transcript-timeline.js.map +1 -0
- package/dist/passive-observer.d.ts +21 -1
- package/dist/passive-observer.d.ts.map +1 -1
- package/dist/passive-observer.js +37 -3
- package/dist/passive-observer.js.map +1 -1
- package/dist/relayed-usage.d.ts +68 -0
- package/dist/relayed-usage.d.ts.map +1 -0
- package/dist/relayed-usage.js +80 -0
- package/dist/relayed-usage.js.map +1 -0
- package/dist/session-aggregator.d.ts +3 -0
- package/dist/session-aggregator.d.ts.map +1 -1
- package/dist/session-aggregator.js.map +1 -1
- package/dist/session-registry.d.ts +1 -0
- package/dist/session-registry.d.ts.map +1 -1
- package/dist/surface-protocol.d.ts +84 -0
- package/dist/surface-protocol.d.ts.map +1 -0
- package/dist/surface-protocol.js +235 -0
- package/dist/surface-protocol.js.map +1 -0
- package/dist/timeline-store.d.ts +1 -1
- package/dist/timeline-store.d.ts.map +1 -1
- package/dist/timeline-store.js +16 -1
- package/dist/timeline-store.js.map +1 -1
- package/dist/tui/renderer.d.ts.map +1 -1
- package/dist/tui/renderer.js +35 -0
- package/dist/tui/renderer.js.map +1 -1
- package/dist/weather-config.d.ts +17 -0
- package/dist/weather-config.d.ts.map +1 -0
- package/dist/weather-config.js +54 -0
- package/dist/weather-config.js.map +1 -0
- package/dist/weather.d.ts +44 -4
- package/dist/weather.d.ts.map +1 -1
- package/dist/weather.js +223 -15
- package/dist/weather.js.map +1 -1
- package/dist/ws-server.d.ts.map +1 -1
- package/dist/ws-server.js +55 -5
- package/dist/ws-server.js.map +1 -1
- package/fm-helper/AgentDeckFMHelper.swift +51 -5
- package/package.json +3 -3
package/dist/apme/store.js
CHANGED
|
@@ -16,12 +16,141 @@ import { debug } from '../logger.js';
|
|
|
16
16
|
// repo root, where the symlink doesn't exist).
|
|
17
17
|
const require = createRequire(import.meta.url);
|
|
18
18
|
// ─── Schema ────────────────────────────────────────────────────────────────────
|
|
19
|
+
const SCORECARD_DDL = `
|
|
20
|
+
-- Pre-aggregate metrics at the level they actually describe. New rows prefer
|
|
21
|
+
-- turn attribution; runs with no turn identity remain available as a legacy
|
|
22
|
+
-- fallback instead of being silently dropped after the schema migration.
|
|
23
|
+
CREATE VIEW IF NOT EXISTS v_run_metrics AS
|
|
24
|
+
SELECT
|
|
25
|
+
run_id,
|
|
26
|
+
MAX(CASE WHEN metric='overall' AND layer='llm_judge' THEN score END) AS overall,
|
|
27
|
+
MAX(CASE WHEN metric='tests_pass' AND layer='deterministic' THEN score END) AS tests_pass
|
|
28
|
+
FROM evals
|
|
29
|
+
GROUP BY run_id;
|
|
30
|
+
|
|
31
|
+
CREATE VIEW IF NOT EXISTS v_model_scorecard AS
|
|
32
|
+
WITH turn_eval AS (
|
|
33
|
+
SELECT
|
|
34
|
+
turn_id,
|
|
35
|
+
MAX(CASE WHEN metric='overall' THEN score END) AS overall,
|
|
36
|
+
MAX(CASE WHEN metric='tests_pass' THEN score END) AS tests_pass
|
|
37
|
+
FROM evals
|
|
38
|
+
WHERE turn_id IS NOT NULL
|
|
39
|
+
GROUP BY turn_id
|
|
40
|
+
), turn_cost AS (
|
|
41
|
+
SELECT
|
|
42
|
+
turn_id,
|
|
43
|
+
CASE WHEN MIN(COALESCE(cost_known, 0))=1 THEN SUM(cost_usd) ELSE NULL END AS cost_usd,
|
|
44
|
+
MIN(COALESCE(cost_known, 0)) AS cost_known
|
|
45
|
+
FROM sample_events
|
|
46
|
+
WHERE turn_id IS NOT NULL AND kind='model'
|
|
47
|
+
GROUP BY turn_id
|
|
48
|
+
), attributed_units AS (
|
|
49
|
+
SELECT
|
|
50
|
+
r.agent_type AS agent_type,
|
|
51
|
+
t.run_id AS run_id,
|
|
52
|
+
t.id AS turn_id,
|
|
53
|
+
t.model_id AS model_id,
|
|
54
|
+
COALESCE(t.provider, r.provider) AS provider,
|
|
55
|
+
COALESCE(
|
|
56
|
+
t.composite_score,
|
|
57
|
+
e.overall
|
|
58
|
+
) AS overall,
|
|
59
|
+
e.tests_pass AS tests_pass,
|
|
60
|
+
c.cost_usd AS cost_usd,
|
|
61
|
+
COALESCE(c.cost_known, 0) AS cost_known
|
|
62
|
+
FROM turns t
|
|
63
|
+
JOIN runs r ON r.id=t.run_id
|
|
64
|
+
LEFT JOIN turn_eval e ON e.turn_id=t.id
|
|
65
|
+
LEFT JOIN turn_cost c ON c.turn_id=t.id
|
|
66
|
+
WHERE t.model_id IS NOT NULL AND t.model_id != ''
|
|
67
|
+
), legacy_units AS (
|
|
68
|
+
SELECT
|
|
69
|
+
r.agent_type AS agent_type,
|
|
70
|
+
r.id AS run_id,
|
|
71
|
+
NULL AS turn_id,
|
|
72
|
+
COALESCE(r.model_id, 'unknown') AS model_id,
|
|
73
|
+
r.provider AS provider,
|
|
74
|
+
m.overall AS overall,
|
|
75
|
+
m.tests_pass AS tests_pass,
|
|
76
|
+
CASE WHEN r.cost_known=1 THEN r.cost_usd ELSE NULL END AS cost_usd,
|
|
77
|
+
COALESCE(r.cost_known, 0) AS cost_known
|
|
78
|
+
FROM runs r
|
|
79
|
+
LEFT JOIN v_run_metrics m ON m.run_id=r.id
|
|
80
|
+
WHERE NOT EXISTS (
|
|
81
|
+
SELECT 1 FROM turns t
|
|
82
|
+
WHERE t.run_id=r.id AND t.model_id IS NOT NULL AND t.model_id != ''
|
|
83
|
+
)
|
|
84
|
+
), units AS (
|
|
85
|
+
SELECT * FROM attributed_units
|
|
86
|
+
UNION ALL
|
|
87
|
+
SELECT * FROM legacy_units
|
|
88
|
+
)
|
|
89
|
+
SELECT
|
|
90
|
+
agent_type,
|
|
91
|
+
model_id,
|
|
92
|
+
provider,
|
|
93
|
+
COUNT(DISTINCT run_id) AS runs,
|
|
94
|
+
AVG(overall) AS avg_overall,
|
|
95
|
+
AVG(tests_pass) AS avg_tests_pass,
|
|
96
|
+
CASE WHEN MIN(cost_known)=1 THEN SUM(cost_usd) ELSE NULL END AS total_cost,
|
|
97
|
+
MIN(cost_known) AS cost_known,
|
|
98
|
+
CASE WHEN MIN(cost_known)=1 AND AVG(overall)>0 THEN SUM(cost_usd)/AVG(overall) ELSE NULL END AS cost_per_quality
|
|
99
|
+
FROM units
|
|
100
|
+
GROUP BY agent_type, model_id, provider;
|
|
101
|
+
|
|
102
|
+
CREATE VIEW IF NOT EXISTS v_category_scorecard AS
|
|
103
|
+
WITH task_metrics AS (
|
|
104
|
+
SELECT
|
|
105
|
+
task_id,
|
|
106
|
+
MAX(CASE WHEN metric='tests_pass' AND layer='deterministic' THEN score END) AS tests_pass
|
|
107
|
+
FROM evals
|
|
108
|
+
WHERE task_id IS NOT NULL
|
|
109
|
+
GROUP BY task_id
|
|
110
|
+
)
|
|
111
|
+
SELECT
|
|
112
|
+
t.task_category AS task_category,
|
|
113
|
+
COALESCE(t.model_id, r.model_id, 'unknown') AS model_id,
|
|
114
|
+
COALESCE(t.provider, r.provider) AS provider,
|
|
115
|
+
COUNT(DISTINCT t.run_id) AS runs,
|
|
116
|
+
AVG(t.composite_score) AS avg_overall,
|
|
117
|
+
AVG(m.tests_pass) AS avg_tests_pass,
|
|
118
|
+
CASE WHEN MIN(COALESCE(t.cost_known, 0))=1 THEN SUM(t.cost_usd) ELSE NULL END AS total_cost,
|
|
119
|
+
MIN(COALESCE(t.cost_known, 0)) AS cost_known
|
|
120
|
+
FROM tasks t
|
|
121
|
+
JOIN runs r ON r.id=t.run_id
|
|
122
|
+
LEFT JOIN task_metrics m ON m.task_id=t.id
|
|
123
|
+
WHERE t.task_category IS NOT NULL AND t.task_category != 'unknown'
|
|
124
|
+
GROUP BY t.task_category, COALESCE(t.model_id, r.model_id, 'unknown'), COALESCE(t.provider, r.provider);
|
|
125
|
+
|
|
126
|
+
-- Sample-granularity scorecard: quality vs cost per (agent, provider, model,
|
|
127
|
+
-- category). A task that used more than one model is stored as 'mixed' rather
|
|
128
|
+
-- than attributed to whichever assistant message happened to arrive last.
|
|
129
|
+
CREATE VIEW IF NOT EXISTS v_sample_scorecard AS
|
|
130
|
+
SELECT
|
|
131
|
+
r.agent_type AS agent_type,
|
|
132
|
+
COALESCE(t.model_id, r.model_id, 'unknown') AS model_id,
|
|
133
|
+
COALESCE(t.provider, r.provider) AS provider,
|
|
134
|
+
t.task_category AS task_category,
|
|
135
|
+
COUNT(*) AS samples,
|
|
136
|
+
AVG(t.composite_score) AS avg_quality,
|
|
137
|
+
CASE WHEN MIN(COALESCE(t.cost_known, 0))=1 THEN SUM(t.cost_usd) ELSE NULL END AS total_cost,
|
|
138
|
+
MIN(COALESCE(t.cost_known, 0)) AS cost_known,
|
|
139
|
+
AVG(t.latency_ms) AS avg_latency_ms,
|
|
140
|
+
CASE WHEN MIN(COALESCE(t.cost_known, 0))=1 AND AVG(t.composite_score)>0
|
|
141
|
+
THEN SUM(t.cost_usd)/AVG(t.composite_score) ELSE NULL END AS cost_per_quality
|
|
142
|
+
FROM tasks t
|
|
143
|
+
JOIN runs r ON r.id=t.run_id
|
|
144
|
+
WHERE t.ended_at IS NOT NULL AND t.composite_score IS NOT NULL
|
|
145
|
+
GROUP BY r.agent_type, COALESCE(t.model_id, r.model_id, 'unknown'), COALESCE(t.provider, r.provider), t.task_category;
|
|
146
|
+
`;
|
|
19
147
|
const DDL = `
|
|
20
148
|
CREATE TABLE IF NOT EXISTS runs (
|
|
21
149
|
id TEXT PRIMARY KEY,
|
|
22
150
|
session_id TEXT NOT NULL,
|
|
23
151
|
agent_type TEXT NOT NULL,
|
|
24
152
|
model_id TEXT,
|
|
153
|
+
provider TEXT,
|
|
25
154
|
project_name TEXT,
|
|
26
155
|
project_path TEXT,
|
|
27
156
|
task_prompt TEXT,
|
|
@@ -30,6 +159,7 @@ CREATE TABLE IF NOT EXISTS runs (
|
|
|
30
159
|
input_tokens INTEGER,
|
|
31
160
|
output_tokens INTEGER,
|
|
32
161
|
cost_usd REAL,
|
|
162
|
+
cost_known INTEGER NOT NULL DEFAULT 0,
|
|
33
163
|
exit_code INTEGER,
|
|
34
164
|
git_before TEXT,
|
|
35
165
|
git_after TEXT,
|
|
@@ -57,6 +187,8 @@ CREATE TABLE IF NOT EXISTS turns (
|
|
|
57
187
|
run_id TEXT NOT NULL REFERENCES runs(id) ON DELETE CASCADE,
|
|
58
188
|
task_id TEXT,
|
|
59
189
|
turn_index INTEGER NOT NULL,
|
|
190
|
+
model_id TEXT,
|
|
191
|
+
provider TEXT,
|
|
60
192
|
prompt TEXT,
|
|
61
193
|
response TEXT,
|
|
62
194
|
started_at INTEGER NOT NULL,
|
|
@@ -99,10 +231,12 @@ CREATE TABLE IF NOT EXISTS tasks (
|
|
|
99
231
|
task_category TEXT,
|
|
100
232
|
notes_json TEXT,
|
|
101
233
|
model_id TEXT,
|
|
234
|
+
provider TEXT,
|
|
102
235
|
model_config TEXT,
|
|
103
236
|
input_tokens INTEGER,
|
|
104
237
|
output_tokens INTEGER,
|
|
105
238
|
cost_usd REAL,
|
|
239
|
+
cost_known INTEGER NOT NULL DEFAULT 0,
|
|
106
240
|
latency_ms INTEGER
|
|
107
241
|
);
|
|
108
242
|
|
|
@@ -123,6 +257,7 @@ CREATE TABLE IF NOT EXISTS sample_events (
|
|
|
123
257
|
input_tokens INTEGER,
|
|
124
258
|
output_tokens INTEGER,
|
|
125
259
|
cost_usd REAL,
|
|
260
|
+
cost_known INTEGER NOT NULL DEFAULT 0,
|
|
126
261
|
latency_ms INTEGER,
|
|
127
262
|
tool_name TEXT,
|
|
128
263
|
tool_status TEXT,
|
|
@@ -181,67 +316,7 @@ CREATE INDEX IF NOT EXISTS idx_runs_started ON runs(started_at);
|
|
|
181
316
|
CREATE INDEX IF NOT EXISTS idx_evals_run ON evals(run_id);
|
|
182
317
|
CREATE INDEX IF NOT EXISTS idx_steps_run ON steps(run_id);
|
|
183
318
|
|
|
184
|
-
|
|
185
|
-
-- multiple eval rows exist per run (e.g. 3 deterministic + 5 judge axes).
|
|
186
|
-
CREATE VIEW IF NOT EXISTS v_run_metrics AS
|
|
187
|
-
SELECT
|
|
188
|
-
run_id,
|
|
189
|
-
MAX(CASE WHEN metric='overall' AND layer='llm_judge' THEN score END) AS overall,
|
|
190
|
-
MAX(CASE WHEN metric='tests_pass' AND layer='deterministic' THEN score END) AS tests_pass
|
|
191
|
-
FROM evals
|
|
192
|
-
GROUP BY run_id;
|
|
193
|
-
|
|
194
|
-
CREATE VIEW IF NOT EXISTS v_model_scorecard AS
|
|
195
|
-
SELECT
|
|
196
|
-
r.agent_type AS agent_type,
|
|
197
|
-
COALESCE(r.model_id, 'unknown') AS model_id,
|
|
198
|
-
COUNT(*) AS runs,
|
|
199
|
-
AVG(m.overall) AS avg_overall,
|
|
200
|
-
AVG(m.tests_pass) AS avg_tests_pass,
|
|
201
|
-
SUM(r.cost_usd) AS total_cost,
|
|
202
|
-
CASE
|
|
203
|
-
WHEN AVG(m.overall) > 0
|
|
204
|
-
THEN SUM(r.cost_usd) / AVG(m.overall)
|
|
205
|
-
ELSE NULL
|
|
206
|
-
END AS cost_per_quality
|
|
207
|
-
FROM runs r
|
|
208
|
-
LEFT JOIN v_run_metrics m ON m.run_id = r.id
|
|
209
|
-
GROUP BY r.agent_type, r.model_id;
|
|
210
|
-
|
|
211
|
-
CREATE VIEW IF NOT EXISTS v_category_scorecard AS
|
|
212
|
-
SELECT
|
|
213
|
-
r.task_category AS task_category,
|
|
214
|
-
COALESCE(r.model_id, 'unknown') AS model_id,
|
|
215
|
-
COUNT(*) AS runs,
|
|
216
|
-
AVG(m.overall) AS avg_overall,
|
|
217
|
-
AVG(m.tests_pass) AS avg_tests_pass,
|
|
218
|
-
SUM(r.cost_usd) AS total_cost
|
|
219
|
-
FROM runs r
|
|
220
|
-
LEFT JOIN v_run_metrics m ON m.run_id = r.id
|
|
221
|
-
WHERE r.task_category IS NOT NULL AND r.task_category != 'unknown'
|
|
222
|
-
GROUP BY r.task_category, r.model_id;
|
|
223
|
-
|
|
224
|
-
-- Sample-granularity scorecard: quality vs cost per (agent, model, category).
|
|
225
|
-
-- The recommender + Pareto frontier read this. Uses the task's own model_id /
|
|
226
|
-
-- cost (the sample header), falling back to the run's model when unset.
|
|
227
|
-
CREATE VIEW IF NOT EXISTS v_sample_scorecard AS
|
|
228
|
-
SELECT
|
|
229
|
-
r.agent_type AS agent_type,
|
|
230
|
-
COALESCE(t.model_id, r.model_id, 'unknown') AS model_id,
|
|
231
|
-
t.task_category AS task_category,
|
|
232
|
-
COUNT(*) AS samples,
|
|
233
|
-
AVG(t.composite_score) AS avg_quality,
|
|
234
|
-
SUM(t.cost_usd) AS total_cost,
|
|
235
|
-
AVG(t.latency_ms) AS avg_latency_ms,
|
|
236
|
-
CASE
|
|
237
|
-
WHEN AVG(t.composite_score) > 0
|
|
238
|
-
THEN SUM(t.cost_usd) / AVG(t.composite_score)
|
|
239
|
-
ELSE NULL
|
|
240
|
-
END AS cost_per_quality
|
|
241
|
-
FROM tasks t
|
|
242
|
-
JOIN runs r ON r.id = t.run_id
|
|
243
|
-
WHERE t.ended_at IS NOT NULL AND t.composite_score IS NOT NULL
|
|
244
|
-
GROUP BY r.agent_type, COALESCE(t.model_id, r.model_id, 'unknown'), t.task_category;
|
|
319
|
+
${SCORECARD_DDL}
|
|
245
320
|
`;
|
|
246
321
|
// ─── Default rubric v1 (seeded on first boot) ──────────────────────────────────
|
|
247
322
|
const DEFAULT_RUBRIC_V1 = {
|
|
@@ -517,6 +592,8 @@ export class ApmeStore {
|
|
|
517
592
|
return;
|
|
518
593
|
const cols = this.db.prepare("PRAGMA table_info(runs)").all().map(c => c.name);
|
|
519
594
|
const migrations = [
|
|
595
|
+
['provider', 'ALTER TABLE runs ADD COLUMN provider TEXT'],
|
|
596
|
+
['cost_known', 'ALTER TABLE runs ADD COLUMN cost_known INTEGER NOT NULL DEFAULT 0'],
|
|
520
597
|
['task_signals', 'ALTER TABLE runs ADD COLUMN task_signals TEXT'],
|
|
521
598
|
['task_category', 'ALTER TABLE runs ADD COLUMN task_category TEXT'],
|
|
522
599
|
['task_category_source', "ALTER TABLE runs ADD COLUMN task_category_source TEXT DEFAULT 'auto'"],
|
|
@@ -560,6 +637,18 @@ export class ApmeStore {
|
|
|
560
637
|
}
|
|
561
638
|
catch { /* ignore */ }
|
|
562
639
|
}
|
|
640
|
+
if (!turnCols.includes('model_id')) {
|
|
641
|
+
try {
|
|
642
|
+
this.db.exec('ALTER TABLE turns ADD COLUMN model_id TEXT');
|
|
643
|
+
}
|
|
644
|
+
catch { /* ignore */ }
|
|
645
|
+
}
|
|
646
|
+
if (!turnCols.includes('provider')) {
|
|
647
|
+
try {
|
|
648
|
+
this.db.exec('ALTER TABLE turns ADD COLUMN provider TEXT');
|
|
649
|
+
}
|
|
650
|
+
catch { /* ignore */ }
|
|
651
|
+
}
|
|
563
652
|
const evalCols = this.db.prepare("PRAGMA table_info(evals)").all().map(c => c.name);
|
|
564
653
|
if (!evalCols.includes('task_id')) {
|
|
565
654
|
try {
|
|
@@ -572,10 +661,12 @@ export class ApmeStore {
|
|
|
572
661
|
const taskCols = this.db.prepare("PRAGMA table_info(tasks)").all().map(c => c.name);
|
|
573
662
|
for (const [col, sql] of [
|
|
574
663
|
['model_id', 'ALTER TABLE tasks ADD COLUMN model_id TEXT'],
|
|
664
|
+
['provider', 'ALTER TABLE tasks ADD COLUMN provider TEXT'],
|
|
575
665
|
['model_config', 'ALTER TABLE tasks ADD COLUMN model_config TEXT'],
|
|
576
666
|
['input_tokens', 'ALTER TABLE tasks ADD COLUMN input_tokens INTEGER'],
|
|
577
667
|
['output_tokens', 'ALTER TABLE tasks ADD COLUMN output_tokens INTEGER'],
|
|
578
668
|
['cost_usd', 'ALTER TABLE tasks ADD COLUMN cost_usd REAL'],
|
|
669
|
+
['cost_known', 'ALTER TABLE tasks ADD COLUMN cost_known INTEGER NOT NULL DEFAULT 0'],
|
|
579
670
|
['latency_ms', 'ALTER TABLE tasks ADD COLUMN latency_ms INTEGER'],
|
|
580
671
|
]) {
|
|
581
672
|
if (!taskCols.includes(col)) {
|
|
@@ -597,6 +688,12 @@ export class ApmeStore {
|
|
|
597
688
|
// pointer to the run it continues, so one conversation shows up as N
|
|
598
689
|
// disconnected components. One live session here had 127 such runs.
|
|
599
690
|
const sevCols = this.db.prepare("PRAGMA table_info(sample_events)").all().map(c => c.name);
|
|
691
|
+
if (!sevCols.includes('cost_known')) {
|
|
692
|
+
try {
|
|
693
|
+
this.db.exec('ALTER TABLE sample_events ADD COLUMN cost_known INTEGER NOT NULL DEFAULT 0');
|
|
694
|
+
}
|
|
695
|
+
catch { /* ignore */ }
|
|
696
|
+
}
|
|
600
697
|
if (!sevCols.includes('turn_id')) {
|
|
601
698
|
try {
|
|
602
699
|
this.db.exec('ALTER TABLE sample_events ADD COLUMN turn_id TEXT');
|
|
@@ -626,6 +723,21 @@ export class ApmeStore {
|
|
|
626
723
|
}
|
|
627
724
|
catch { /* ignore */ }
|
|
628
725
|
}
|
|
726
|
+
// A positive historical value proves that a price existed. Historical
|
|
727
|
+
// zeroes remain unknown because they could be either a free local model or
|
|
728
|
+
// the old UNKNOWN_PRICE fallback — the ambiguity this column removes.
|
|
729
|
+
try {
|
|
730
|
+
this.db.exec('UPDATE runs SET cost_known=1 WHERE cost_usd > 0');
|
|
731
|
+
}
|
|
732
|
+
catch { /* ignore */ }
|
|
733
|
+
try {
|
|
734
|
+
this.db.exec('UPDATE tasks SET cost_known=1 WHERE cost_usd > 0');
|
|
735
|
+
}
|
|
736
|
+
catch { /* ignore */ }
|
|
737
|
+
try {
|
|
738
|
+
this.db.exec('UPDATE sample_events SET cost_known=1 WHERE cost_usd > 0');
|
|
739
|
+
}
|
|
740
|
+
catch { /* ignore */ }
|
|
629
741
|
// ── Covering indexes for the per-run/per-task rollups ──
|
|
630
742
|
// better-sqlite3 is synchronous on a single connection, so ANY slow query
|
|
631
743
|
// here stalls the daemon's whole HTTP path — this is a latency budget, not
|
|
@@ -647,6 +759,16 @@ export class ApmeStore {
|
|
|
647
759
|
}
|
|
648
760
|
catch { /* ignore */ }
|
|
649
761
|
}
|
|
762
|
+
// CREATE VIEW IF NOT EXISTS preserves stale executable SQL forever. Rebuild
|
|
763
|
+
// after the ALTERs above so both fresh and upgraded databases group by the
|
|
764
|
+
// turn/provider identity this version actually writes.
|
|
765
|
+
for (const view of ['v_sample_scorecard', 'v_category_scorecard', 'v_model_scorecard', 'v_run_metrics']) {
|
|
766
|
+
try {
|
|
767
|
+
this.db.exec(`DROP VIEW IF EXISTS ${view}`);
|
|
768
|
+
}
|
|
769
|
+
catch { /* ignore */ }
|
|
770
|
+
}
|
|
771
|
+
this.db.exec(SCORECARD_DDL);
|
|
650
772
|
}
|
|
651
773
|
seedDefaultRubric() {
|
|
652
774
|
if (!this.db)
|
|
@@ -688,11 +810,12 @@ export class ApmeStore {
|
|
|
688
810
|
insertRun(row) {
|
|
689
811
|
if (!this.db)
|
|
690
812
|
return;
|
|
813
|
+
const costKnown = row.costKnown === true || (row.costUsd != null && row.costUsd > 0);
|
|
691
814
|
this.db.prepare(`INSERT INTO runs
|
|
692
|
-
(id, session_id, agent_type, model_id, project_name, project_path, task_prompt,
|
|
693
|
-
started_at, ended_at, input_tokens, output_tokens, cost_usd, exit_code,
|
|
815
|
+
(id, session_id, agent_type, model_id, provider, project_name, project_path, task_prompt,
|
|
816
|
+
started_at, ended_at, input_tokens, output_tokens, cost_usd, cost_known, exit_code,
|
|
694
817
|
git_before, git_after, hw_profile)
|
|
695
|
-
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(row.id, row.sessionId, row.agentType, row.modelId ?? null, row.projectName ?? null, row.projectPath ?? null, row.taskPrompt ?? null, row.startedAt, row.endedAt ?? null, row.inputTokens ?? null, row.outputTokens ?? null, row.costUsd ?? null, row.exitCode ?? null, row.gitBefore ?? null, row.gitAfter ?? null, row.hwProfile ?? null);
|
|
818
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)`).run(row.id, row.sessionId, row.agentType, row.modelId ?? null, row.provider ?? null, row.projectName ?? null, row.projectPath ?? null, row.taskPrompt ?? null, row.startedAt, row.endedAt ?? null, row.inputTokens ?? null, row.outputTokens ?? null, row.costUsd ?? null, costKnown ? 1 : 0, row.exitCode ?? null, row.gitBefore ?? null, row.gitAfter ?? null, row.hwProfile ?? null);
|
|
696
819
|
}
|
|
697
820
|
updateRun(id, patch) {
|
|
698
821
|
if (!this.db)
|
|
@@ -701,6 +824,7 @@ export class ApmeStore {
|
|
|
701
824
|
const values = [];
|
|
702
825
|
const map = {
|
|
703
826
|
modelId: 'model_id',
|
|
827
|
+
provider: 'provider',
|
|
704
828
|
projectName: 'project_name',
|
|
705
829
|
projectPath: 'project_path',
|
|
706
830
|
taskPrompt: 'task_prompt',
|
|
@@ -708,6 +832,7 @@ export class ApmeStore {
|
|
|
708
832
|
inputTokens: 'input_tokens',
|
|
709
833
|
outputTokens: 'output_tokens',
|
|
710
834
|
costUsd: 'cost_usd',
|
|
835
|
+
costKnown: 'cost_known',
|
|
711
836
|
exitCode: 'exit_code',
|
|
712
837
|
gitBefore: 'git_before',
|
|
713
838
|
gitAfter: 'git_after',
|
|
@@ -726,7 +851,11 @@ export class ApmeStore {
|
|
|
726
851
|
if (!col || v === undefined)
|
|
727
852
|
continue;
|
|
728
853
|
fields.push(`${col} = ?`);
|
|
729
|
-
values.push(v);
|
|
854
|
+
values.push(k === 'costKnown' ? (v ? 1 : 0) : v);
|
|
855
|
+
}
|
|
856
|
+
if (patch.costUsd != null && patch.costUsd > 0 && patch.costKnown === undefined) {
|
|
857
|
+
fields.push('cost_known = ?');
|
|
858
|
+
values.push(1);
|
|
730
859
|
}
|
|
731
860
|
if (fields.length === 0)
|
|
732
861
|
return;
|
|
@@ -784,6 +913,7 @@ export class ApmeStore {
|
|
|
784
913
|
outcome: 'outcome', compositeScore: 'composite_score', efficiencyJson: 'efficiency_json',
|
|
785
914
|
prompt: 'prompt', response: 'response', taskId: 'task_id',
|
|
786
915
|
endSource: 'end_source',
|
|
916
|
+
modelId: 'model_id', provider: 'provider',
|
|
787
917
|
};
|
|
788
918
|
const sets = [];
|
|
789
919
|
const vals = [];
|
|
@@ -886,10 +1016,12 @@ export class ApmeStore {
|
|
|
886
1016
|
notesJson: 'notes_json',
|
|
887
1017
|
boundarySignal: 'boundary_signal',
|
|
888
1018
|
modelId: 'model_id',
|
|
1019
|
+
provider: 'provider',
|
|
889
1020
|
modelConfig: 'model_config',
|
|
890
1021
|
inputTokens: 'input_tokens',
|
|
891
1022
|
outputTokens: 'output_tokens',
|
|
892
1023
|
costUsd: 'cost_usd',
|
|
1024
|
+
costKnown: 'cost_known',
|
|
893
1025
|
latencyMs: 'latency_ms',
|
|
894
1026
|
};
|
|
895
1027
|
const sets = [];
|
|
@@ -899,7 +1031,11 @@ export class ApmeStore {
|
|
|
899
1031
|
if (!col || v === undefined)
|
|
900
1032
|
continue;
|
|
901
1033
|
sets.push(`${col} = ?`);
|
|
902
|
-
vals.push(v);
|
|
1034
|
+
vals.push(k === 'costKnown' ? (v ? 1 : 0) : v);
|
|
1035
|
+
}
|
|
1036
|
+
if (patch.costUsd != null && patch.costUsd > 0 && patch.costKnown === undefined) {
|
|
1037
|
+
sets.push('cost_known = ?');
|
|
1038
|
+
vals.push(1);
|
|
903
1039
|
}
|
|
904
1040
|
if (sets.length === 0)
|
|
905
1041
|
return;
|
|
@@ -1280,10 +1416,14 @@ export class ApmeStore {
|
|
|
1280
1416
|
insertSampleEvent(row) {
|
|
1281
1417
|
if (!this.db)
|
|
1282
1418
|
return false;
|
|
1419
|
+
// A positive monetary amount is self-proving provenance even for legacy
|
|
1420
|
+
// callers that predate costKnown. Zero remains ambiguous and must only be
|
|
1421
|
+
// marked known by a pricing table / known-local producer.
|
|
1422
|
+
const costKnown = row.costKnown === true || (row.costUsd != null && row.costUsd > 0);
|
|
1283
1423
|
const res = this.db.prepare(`INSERT OR IGNORE INTO sample_events
|
|
1284
1424
|
(task_id, run_id, turn_index, turn_id, seq, ts, kind, model, input_tokens, output_tokens,
|
|
1285
|
-
cost_usd, latency_ms, tool_name, tool_status, tool_error, payload, dedup_key)
|
|
1286
|
-
VALUES (
|
|
1425
|
+
cost_usd, cost_known, latency_ms, tool_name, tool_status, tool_error, payload, dedup_key)
|
|
1426
|
+
VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)`).run(row.taskId, row.runId, row.turnIndex ?? null, row.turnId ?? null, row.seq, row.ts, row.kind, row.model ?? null, row.inputTokens ?? null, row.outputTokens ?? null, row.costUsd ?? null, costKnown ? 1 : 0, row.latencyMs ?? null, row.toolName ?? null, row.toolStatus ?? null, row.toolError ?? null, row.payload ?? null, row.dedupKey ?? null);
|
|
1287
1427
|
return res.changes > 0;
|
|
1288
1428
|
}
|
|
1289
1429
|
/** Update a previously-inserted event (e.g. a tool pending→resolved) by id. */
|
|
@@ -1292,7 +1432,7 @@ export class ApmeStore {
|
|
|
1292
1432
|
return;
|
|
1293
1433
|
const map = {
|
|
1294
1434
|
toolStatus: 'tool_status', toolError: 'tool_error', payload: 'payload',
|
|
1295
|
-
costUsd: 'cost_usd', latencyMs: 'latency_ms', model: 'model',
|
|
1435
|
+
costUsd: 'cost_usd', costKnown: 'cost_known', latencyMs: 'latency_ms', model: 'model',
|
|
1296
1436
|
inputTokens: 'input_tokens', outputTokens: 'output_tokens', ts: 'ts',
|
|
1297
1437
|
};
|
|
1298
1438
|
const sets = [];
|
|
@@ -1302,7 +1442,11 @@ export class ApmeStore {
|
|
|
1302
1442
|
if (!col || v === undefined)
|
|
1303
1443
|
continue;
|
|
1304
1444
|
sets.push(`${col} = ?`);
|
|
1305
|
-
vals.push(v);
|
|
1445
|
+
vals.push(k === 'costKnown' ? (v ? 1 : 0) : v);
|
|
1446
|
+
}
|
|
1447
|
+
if (fields.costUsd != null && fields.costUsd > 0 && fields.costKnown === undefined) {
|
|
1448
|
+
sets.push('cost_known = ?');
|
|
1449
|
+
vals.push(1);
|
|
1306
1450
|
}
|
|
1307
1451
|
if (sets.length === 0)
|
|
1308
1452
|
return;
|
|
@@ -1319,6 +1463,17 @@ export class ApmeStore {
|
|
|
1319
1463
|
ORDER BY seq DESC LIMIT 1`).get(taskId, turnIndex, toolName);
|
|
1320
1464
|
return row ? rowToSampleEvent(row) : null;
|
|
1321
1465
|
}
|
|
1466
|
+
/** One assistant-message slot per turn. Response producers overlap, so text
|
|
1467
|
+
* hashes cannot be the identity: a truncated projection and the full final
|
|
1468
|
+
* are two payload versions of the same logical event. */
|
|
1469
|
+
findAssistantMessageEvent(taskId, turnId) {
|
|
1470
|
+
if (!this.db)
|
|
1471
|
+
return null;
|
|
1472
|
+
const row = this.db.prepare(`SELECT * FROM sample_events
|
|
1473
|
+
WHERE task_id = ? AND turn_id = ? AND kind = 'assistant_message'
|
|
1474
|
+
ORDER BY seq ASC LIMIT 1`).get(taskId, turnId);
|
|
1475
|
+
return row ? rowToSampleEvent(row) : null;
|
|
1476
|
+
}
|
|
1322
1477
|
listSampleEventRows(taskId) {
|
|
1323
1478
|
if (!this.db)
|
|
1324
1479
|
return [];
|
|
@@ -1369,6 +1524,7 @@ export class ApmeStore {
|
|
|
1369
1524
|
inputTokens: task.inputTokens ?? 0,
|
|
1370
1525
|
outputTokens: task.outputTokens ?? 0,
|
|
1371
1526
|
costUsd: task.costUsd ?? 0,
|
|
1527
|
+
costKnown: task.costKnown ?? false,
|
|
1372
1528
|
latencyMs: task.latencyMs ?? 0,
|
|
1373
1529
|
},
|
|
1374
1530
|
summary: task.summary ?? null,
|
|
@@ -1382,10 +1538,15 @@ export class ApmeStore {
|
|
|
1382
1538
|
if (!this.db)
|
|
1383
1539
|
return;
|
|
1384
1540
|
const row = this.db.prepare(`SELECT COALESCE(SUM(input_tokens),0) AS it, COALESCE(SUM(output_tokens),0) AS ot,
|
|
1385
|
-
|
|
1541
|
+
CASE WHEN COUNT(*) > 0 AND MIN(COALESCE(cost_known,0))=1
|
|
1542
|
+
THEN SUM(cost_usd) ELSE NULL END AS cu,
|
|
1543
|
+
CASE WHEN COUNT(*) > 0 AND MIN(COALESCE(cost_known,0))=1
|
|
1544
|
+
THEN 1 ELSE 0 END AS ck,
|
|
1545
|
+
COALESCE(SUM(latency_ms),0) AS lm
|
|
1386
1546
|
FROM sample_events WHERE task_id = ? AND kind = 'model'`).get(taskId);
|
|
1387
1547
|
this.updateTask(taskId, {
|
|
1388
|
-
inputTokens: row.it, outputTokens: row.ot, costUsd: row.cu,
|
|
1548
|
+
inputTokens: row.it, outputTokens: row.ot, costUsd: row.cu,
|
|
1549
|
+
costKnown: row.ck === 1, latencyMs: row.lm,
|
|
1389
1550
|
});
|
|
1390
1551
|
}
|
|
1391
1552
|
sampleScorecard() {
|
|
@@ -1395,10 +1556,12 @@ export class ApmeStore {
|
|
|
1395
1556
|
return rows.map((r) => ({
|
|
1396
1557
|
agentType: r.agent_type,
|
|
1397
1558
|
modelId: r.model_id,
|
|
1559
|
+
provider: r.provider ?? null,
|
|
1398
1560
|
taskCategory: r.task_category ?? null,
|
|
1399
1561
|
samples: r.samples,
|
|
1400
1562
|
avgQuality: r.avg_quality ?? null,
|
|
1401
1563
|
totalCost: r.total_cost ?? null,
|
|
1564
|
+
costKnown: r.cost_known === 1,
|
|
1402
1565
|
avgLatencyMs: r.avg_latency_ms ?? null,
|
|
1403
1566
|
costPerQuality: r.cost_per_quality ?? null,
|
|
1404
1567
|
}));
|
|
@@ -1411,10 +1574,12 @@ export class ApmeStore {
|
|
|
1411
1574
|
return rows.map((r) => ({
|
|
1412
1575
|
agentType: r.agent_type,
|
|
1413
1576
|
modelId: r.model_id,
|
|
1577
|
+
provider: r.provider ?? null,
|
|
1414
1578
|
runs: r.runs,
|
|
1415
1579
|
avgOverall: r.avg_overall ?? null,
|
|
1416
1580
|
avgTestsPass: r.avg_tests_pass ?? null,
|
|
1417
1581
|
totalCost: r.total_cost ?? null,
|
|
1582
|
+
costKnown: r.cost_known === 1,
|
|
1418
1583
|
costPerQuality: r.cost_per_quality ?? null,
|
|
1419
1584
|
}));
|
|
1420
1585
|
}
|
|
@@ -1425,10 +1590,12 @@ export class ApmeStore {
|
|
|
1425
1590
|
return rows.map((r) => ({
|
|
1426
1591
|
taskCategory: r.task_category,
|
|
1427
1592
|
modelId: r.model_id,
|
|
1593
|
+
provider: r.provider ?? null,
|
|
1428
1594
|
runs: r.runs,
|
|
1429
1595
|
avgOverall: r.avg_overall ?? null,
|
|
1430
1596
|
avgTestsPass: r.avg_tests_pass ?? null,
|
|
1431
1597
|
totalCost: r.total_cost ?? null,
|
|
1598
|
+
costKnown: r.cost_known === 1,
|
|
1432
1599
|
}));
|
|
1433
1600
|
}
|
|
1434
1601
|
}
|
|
@@ -1439,6 +1606,7 @@ function rowToRun(r) {
|
|
|
1439
1606
|
sessionId: r.session_id,
|
|
1440
1607
|
agentType: r.agent_type,
|
|
1441
1608
|
modelId: r.model_id ?? null,
|
|
1609
|
+
provider: r.provider ?? null,
|
|
1442
1610
|
projectName: r.project_name ?? null,
|
|
1443
1611
|
projectPath: r.project_path ?? null,
|
|
1444
1612
|
taskPrompt: r.task_prompt ?? null,
|
|
@@ -1447,6 +1615,7 @@ function rowToRun(r) {
|
|
|
1447
1615
|
inputTokens: r.input_tokens ?? null,
|
|
1448
1616
|
outputTokens: r.output_tokens ?? null,
|
|
1449
1617
|
costUsd: r.cost_usd ?? null,
|
|
1618
|
+
costKnown: r.cost_known === 1,
|
|
1450
1619
|
exitCode: r.exit_code ?? null,
|
|
1451
1620
|
gitBefore: r.git_before ?? null,
|
|
1452
1621
|
gitAfter: r.git_after ?? null,
|
|
@@ -1477,10 +1646,12 @@ function rowToTask(r) {
|
|
|
1477
1646
|
taskCategory: r.task_category ?? null,
|
|
1478
1647
|
notesJson: r.notes_json ?? null,
|
|
1479
1648
|
modelId: r.model_id ?? null,
|
|
1649
|
+
provider: r.provider ?? null,
|
|
1480
1650
|
modelConfig: r.model_config ?? null,
|
|
1481
1651
|
inputTokens: r.input_tokens ?? null,
|
|
1482
1652
|
outputTokens: r.output_tokens ?? null,
|
|
1483
1653
|
costUsd: r.cost_usd ?? null,
|
|
1654
|
+
costKnown: r.cost_known === 1,
|
|
1484
1655
|
latencyMs: r.latency_ms ?? null,
|
|
1485
1656
|
};
|
|
1486
1657
|
}
|
|
@@ -1519,6 +1690,7 @@ function rowToSampleEvent(r) {
|
|
|
1519
1690
|
inputTokens: r.input_tokens ?? null,
|
|
1520
1691
|
outputTokens: r.output_tokens ?? null,
|
|
1521
1692
|
costUsd: r.cost_usd ?? null,
|
|
1693
|
+
costKnown: r.cost_known === 1,
|
|
1522
1694
|
latencyMs: r.latency_ms ?? null,
|
|
1523
1695
|
toolName: r.tool_name ?? null,
|
|
1524
1696
|
toolStatus: r.tool_status ?? null,
|