agentboss 0.1.2 → 0.1.4
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/client/dist/assets/{index-DxoLOxZ8.js → index-sks7Tuv7.js} +52 -52
- package/client/dist/index.html +1 -1
- package/package.json +1 -1
- package/server/analysis/report-builder.js +28 -1
- package/server/api/execution.js +4 -4
- package/server/api/overview.js +25 -14
- package/server/api/settings.js +139 -119
- package/server/db/queries.js +1108 -1051
- package/server/execution/job.js +63 -12
- package/server/llm/advice.js +15 -7
- package/server/llm/cli-runner.js +316 -265
- package/server/llm/judge.js +149 -123
- package/server/llm/project-advice.js +15 -7
- package/server/llm/session-analyzer.js +141 -131
package/client/dist/index.html
CHANGED
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
} catch (e) {}
|
|
26
26
|
})();
|
|
27
27
|
</script>
|
|
28
|
-
<script type="module" crossorigin src="/assets/index-
|
|
28
|
+
<script type="module" crossorigin src="/assets/index-sks7Tuv7.js"></script>
|
|
29
29
|
<link rel="stylesheet" crossorigin href="/assets/index-C1wFD_Vo.css">
|
|
30
30
|
</head>
|
|
31
31
|
<body>
|
package/package.json
CHANGED
|
@@ -15,6 +15,7 @@ const {
|
|
|
15
15
|
getDailySummaries,
|
|
16
16
|
getAnalysisState,
|
|
17
17
|
getOverviewTopProjects,
|
|
18
|
+
getUserMessageCountsByDateRange,
|
|
18
19
|
} = require('../db/queries');
|
|
19
20
|
const { mapTopProjects } = require('../utils/project');
|
|
20
21
|
|
|
@@ -202,11 +203,19 @@ function buildStats(db, fromDate, toDate) {
|
|
|
202
203
|
*/
|
|
203
204
|
function buildSessionList(db, fromDate, toDate) {
|
|
204
205
|
const sessions = getSessionsByDateRange(db, fromDate, toDate, undefined, 10000, 0);
|
|
206
|
+
// One aggregate query for the whole range — cheaper than N getMessagesBySession.
|
|
207
|
+
const userMsgCounts = getUserMessageCountsByDateRange(db, fromDate, toDate);
|
|
205
208
|
const list = [];
|
|
206
209
|
|
|
207
210
|
for (const s of sessions) {
|
|
208
211
|
if (s.parent_session_id) continue; // skip subagents
|
|
209
212
|
const analysis = getAnalysisBySession(db, s.id);
|
|
213
|
+
const userMessageCount = userMsgCounts.get(s.id) || 0;
|
|
214
|
+
const messageCount = s.message_count || 0;
|
|
215
|
+
// message_count = user + assistant turns (per ETL), so assistant is the
|
|
216
|
+
// remainder; clamp at 0 in case of edge-case data.
|
|
217
|
+
const assistantMessageCount = Math.max(0, messageCount - userMessageCount);
|
|
218
|
+
const activeMinutes = s.active_minutes || 0;
|
|
210
219
|
list.push({
|
|
211
220
|
id: s.id,
|
|
212
221
|
title: s.title || '(untitled)',
|
|
@@ -216,6 +225,10 @@ function buildSessionList(db, fromDate, toDate) {
|
|
|
216
225
|
startedAt: s.started_at,
|
|
217
226
|
cost: Math.round((s.cost_usd || 0) * 100) / 100,
|
|
218
227
|
duration: s.duration_minutes || 0,
|
|
228
|
+
activeMinutes,
|
|
229
|
+
userMessageCount,
|
|
230
|
+
assistantMessageCount,
|
|
231
|
+
messageCount,
|
|
219
232
|
// v2 main-axis scores (UI averages H1/H2/H3/ENV/O1 into a single
|
|
220
233
|
// composite column). ENV is derived from E1 + E2 client-side via
|
|
221
234
|
// its own column would explode the row width.
|
|
@@ -228,6 +241,20 @@ function buildSessionList(db, fromDate, toDate) {
|
|
|
228
241
|
});
|
|
229
242
|
}
|
|
230
243
|
|
|
244
|
+
// Composite "投入度" sort: userMessageCount × activeMinutes, desc.
|
|
245
|
+
// Both factors are non-negative; multiplying keeps sessions that are
|
|
246
|
+
// weak on either axis (no user turns OR no active time) at the bottom
|
|
247
|
+
// without needing min-max normalisation that would shift on every
|
|
248
|
+
// filter change. Tie-break by started_at desc so the existing
|
|
249
|
+
// chronological order shows through when investment is equal (e.g.
|
|
250
|
+
// brand-new sessions with 0 active minutes).
|
|
251
|
+
list.sort((a, b) => {
|
|
252
|
+
const sa = (a.userMessageCount || 0) * (a.activeMinutes || 0);
|
|
253
|
+
const sb = (b.userMessageCount || 0) * (b.activeMinutes || 0);
|
|
254
|
+
if (sb !== sa) return sb - sa;
|
|
255
|
+
return (b.startedAt || '').localeCompare(a.startedAt || '');
|
|
256
|
+
});
|
|
257
|
+
|
|
231
258
|
return list;
|
|
232
259
|
}
|
|
233
260
|
|
|
@@ -402,7 +429,7 @@ function buildSessionDetail(db, sessionId) {
|
|
|
402
429
|
// Per-tool sample invocations (up to N most recent) for UI hover details.
|
|
403
430
|
// We fetch a flat list ordered by tool then time-desc, then bucket in JS so
|
|
404
431
|
// we don't issue one query per tool.
|
|
405
|
-
const SAMPLE_LIMIT_PER_TOOL =
|
|
432
|
+
const SAMPLE_LIMIT_PER_TOOL = 100;
|
|
406
433
|
const allCalls = queryAll(
|
|
407
434
|
db,
|
|
408
435
|
`SELECT tool_name, status, error_message, target_file, timestamp
|
package/server/api/execution.js
CHANGED
|
@@ -241,7 +241,7 @@ module.exports = function (db) {
|
|
|
241
241
|
// lookups don't collide with run-id lookups.
|
|
242
242
|
// -------------------------------------------------------------------------
|
|
243
243
|
|
|
244
|
-
router.get('/preview', (req, res) => {
|
|
244
|
+
router.get('/preview', async (req, res) => {
|
|
245
245
|
const sessionId = req.query.sessionId;
|
|
246
246
|
const adviceKey = req.query.adviceKey;
|
|
247
247
|
const executor = req.query.executor;
|
|
@@ -260,7 +260,7 @@ module.exports = function (db) {
|
|
|
260
260
|
ok: false, error: { code: 'BAD_REQUEST', message: 'executor must be opencode or claude' },
|
|
261
261
|
});
|
|
262
262
|
}
|
|
263
|
-
const r = job.previewExecution(db, { sessionId, adviceKey, executor });
|
|
263
|
+
const r = await job.previewExecution(db, { sessionId, adviceKey, executor });
|
|
264
264
|
if (!r.ok) return failure(res, r.reason, r);
|
|
265
265
|
res.json({
|
|
266
266
|
ok: true,
|
|
@@ -277,7 +277,7 @@ module.exports = function (db) {
|
|
|
277
277
|
});
|
|
278
278
|
});
|
|
279
279
|
|
|
280
|
-
router.get('/project/preview', (req, res) => {
|
|
280
|
+
router.get('/project/preview', async (req, res) => {
|
|
281
281
|
const project = req.query.project;
|
|
282
282
|
const scope = req.query.scope;
|
|
283
283
|
const from = req.query.from || '';
|
|
@@ -304,7 +304,7 @@ module.exports = function (db) {
|
|
|
304
304
|
ok: false, error: { code: 'BAD_REQUEST', message: 'executor must be opencode or claude' },
|
|
305
305
|
});
|
|
306
306
|
}
|
|
307
|
-
const r = job.previewProjectExecution(db, {
|
|
307
|
+
const r = await job.previewProjectExecution(db, {
|
|
308
308
|
project, scope, windowFrom: from, windowTo: to, adviceKey, executor,
|
|
309
309
|
});
|
|
310
310
|
if (!r.ok) return failure(res, r.reason, r);
|
package/server/api/overview.js
CHANGED
|
@@ -32,6 +32,7 @@ const {
|
|
|
32
32
|
getOverviewErrorRate,
|
|
33
33
|
getOverviewTopTools,
|
|
34
34
|
getEarliestSessionDate,
|
|
35
|
+
getMessageRoleCounts,
|
|
35
36
|
} = require('../db/queries');
|
|
36
37
|
const { getCurrentDimensionsV2 } = require('../analysis/report-builder');
|
|
37
38
|
const { canonicalProject, mapTopProjects } = require('../utils/project');
|
|
@@ -65,19 +66,26 @@ function daysAgo(n) {
|
|
|
65
66
|
* @param {Object[]} rows
|
|
66
67
|
* @returns {Object[]}
|
|
67
68
|
*/
|
|
68
|
-
function mapRecentSessions(rows) {
|
|
69
|
-
return rows.map((r) =>
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
69
|
+
function mapRecentSessions(rows, roleCounts) {
|
|
70
|
+
return rows.map((r) => {
|
|
71
|
+
// Prefer real message-row counts (user + assistant) over the ETL-stored
|
|
72
|
+
// message_count, which can over-count and disagree with the detail page.
|
|
73
|
+
const rc = (roleCounts && roleCounts.get(r.id)) || { user: 0, assistant: 0 };
|
|
74
|
+
return {
|
|
75
|
+
id: r.id,
|
|
76
|
+
source: r.source,
|
|
77
|
+
startedAt: r.started_at,
|
|
78
|
+
project: r.project,
|
|
79
|
+
title: r.title,
|
|
80
|
+
model: r.model,
|
|
81
|
+
userMessageCount: rc.user,
|
|
82
|
+
assistantMessageCount: rc.assistant,
|
|
83
|
+
messageCount: (rc.user + rc.assistant) || r.message_count || 0,
|
|
84
|
+
cost: Math.round((r.cost_usd || 0) * 10000) / 10000,
|
|
85
|
+
errorCount: r.error_count || 0,
|
|
86
|
+
reverted: !!r.reverted,
|
|
87
|
+
};
|
|
88
|
+
});
|
|
81
89
|
}
|
|
82
90
|
|
|
83
91
|
/**
|
|
@@ -288,7 +296,10 @@ module.exports = function (db) {
|
|
|
288
296
|
rows: timeseries,
|
|
289
297
|
},
|
|
290
298
|
topProjects: mapTopProjects(topProjectsRaw, TOP_N),
|
|
291
|
-
recentSessions: mapRecentSessions(
|
|
299
|
+
recentSessions: mapRecentSessions(
|
|
300
|
+
recentSessions,
|
|
301
|
+
getMessageRoleCounts(db, recentSessions.map((r) => r.id))
|
|
302
|
+
),
|
|
292
303
|
topTools,
|
|
293
304
|
capabilityRadar,
|
|
294
305
|
},
|
package/server/api/settings.js
CHANGED
|
@@ -1,119 +1,139 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Settings API routes for Agent Boss.
|
|
3
|
-
*
|
|
4
|
-
* Provides endpoints for reading/writing user settings and listing
|
|
5
|
-
* tool configurations (data source status).
|
|
6
|
-
*
|
|
7
|
-
* @author Felix
|
|
8
|
-
*/
|
|
9
|
-
|
|
10
|
-
const router = require('express').Router();
|
|
11
|
-
|
|
12
|
-
const {
|
|
13
|
-
getAllSettings,
|
|
14
|
-
setSetting,
|
|
15
|
-
getAllToolConfigs,
|
|
16
|
-
} = require('../db/queries');
|
|
17
|
-
const { diagnose: diagnoseLlm, invalidateSettingsCache } = require('../llm/judge');
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
});
|
|
115
|
-
}
|
|
116
|
-
});
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
1
|
+
/**
|
|
2
|
+
* Settings API routes for Agent Boss.
|
|
3
|
+
*
|
|
4
|
+
* Provides endpoints for reading/writing user settings and listing
|
|
5
|
+
* tool configurations (data source status).
|
|
6
|
+
*
|
|
7
|
+
* @author Felix
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
const router = require('express').Router();
|
|
11
|
+
|
|
12
|
+
const {
|
|
13
|
+
getAllSettings,
|
|
14
|
+
setSetting,
|
|
15
|
+
getAllToolConfigs,
|
|
16
|
+
} = require('../db/queries');
|
|
17
|
+
const { diagnose: diagnoseLlm, invalidateSettingsCache } = require('../llm/judge');
|
|
18
|
+
const { invalidateAdviceSettingsCache } = require('../llm/advice');
|
|
19
|
+
const { invalidateAnalyzerSettingsCache } = require('../llm/session-analyzer');
|
|
20
|
+
const { invalidateProjectAdviceSettingsCache } = require('../llm/project-advice');
|
|
21
|
+
const { _resetCache: resetCliDetectionCache } = require('../llm/cli-runner');
|
|
22
|
+
|
|
23
|
+
// Settings keys that invalidate per-module caches when they change.
|
|
24
|
+
const SETTINGS_CACHE_KEYS = new Set(['enable_llm_judge', 'llm_tool_preference']);
|
|
25
|
+
|
|
26
|
+
// ---------------------------------------------------------------------------
|
|
27
|
+
// Routes
|
|
28
|
+
// ---------------------------------------------------------------------------
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* Create the settings router with database access.
|
|
32
|
+
*
|
|
33
|
+
* @param {object} db sql.js Database instance
|
|
34
|
+
* @returns {import('express').Router}
|
|
35
|
+
*/
|
|
36
|
+
module.exports = function (db) {
|
|
37
|
+
// GET /api/settings
|
|
38
|
+
router.get('/', (_req, res) => {
|
|
39
|
+
try {
|
|
40
|
+
const settings = getAllSettings(db);
|
|
41
|
+
const tools = getAllToolConfigs(db);
|
|
42
|
+
|
|
43
|
+
res.json({
|
|
44
|
+
ok: true,
|
|
45
|
+
data: { settings, tools },
|
|
46
|
+
meta: {
|
|
47
|
+
generated_at: new Date().toISOString(),
|
|
48
|
+
},
|
|
49
|
+
});
|
|
50
|
+
} catch (err) {
|
|
51
|
+
res.status(500).json({
|
|
52
|
+
ok: false,
|
|
53
|
+
error: { code: 'SETTINGS_ERROR', message: err.message },
|
|
54
|
+
});
|
|
55
|
+
}
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// PUT /api/settings
|
|
59
|
+
router.put('/', (req, res) => {
|
|
60
|
+
try {
|
|
61
|
+
const body = req.body;
|
|
62
|
+
if (!body || typeof body !== 'object') {
|
|
63
|
+
return res.status(400).json({
|
|
64
|
+
ok: false,
|
|
65
|
+
error: { code: 'BAD_REQUEST', message: 'Request body must be a JSON object of {key: value} pairs' },
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
const keys = Object.keys(body);
|
|
70
|
+
for (const key of keys) {
|
|
71
|
+
setSetting(db, key, String(body[key]));
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
// Invalidate every LLM module's settings cache so toggles
|
|
75
|
+
// (`enable_llm_judge`) and CLI picks (`llm_tool_preference`) take
|
|
76
|
+
// effect immediately. Also drop the cli-runner detection cache so
|
|
77
|
+
// a switched preference doesn't reuse an old `detectAvailableCli`.
|
|
78
|
+
if (keys.some((k) => SETTINGS_CACHE_KEYS.has(k))) {
|
|
79
|
+
invalidateSettingsCache();
|
|
80
|
+
invalidateAdviceSettingsCache();
|
|
81
|
+
invalidateAnalyzerSettingsCache();
|
|
82
|
+
invalidateProjectAdviceSettingsCache();
|
|
83
|
+
}
|
|
84
|
+
if (keys.includes('llm_tool_preference')) {
|
|
85
|
+
resetCliDetectionCache();
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const settings = getAllSettings(db);
|
|
89
|
+
|
|
90
|
+
res.json({
|
|
91
|
+
ok: true,
|
|
92
|
+
data: { settings, updated: keys },
|
|
93
|
+
meta: {
|
|
94
|
+
generated_at: new Date().toISOString(),
|
|
95
|
+
},
|
|
96
|
+
});
|
|
97
|
+
} catch (err) {
|
|
98
|
+
res.status(500).json({
|
|
99
|
+
ok: false,
|
|
100
|
+
error: { code: 'SETTINGS_ERROR', message: err.message },
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
// GET /api/settings/llm/diagnose — detect available CLI for LLM judge.
|
|
106
|
+
// Returns { available, name, active, preference, source, detected[] }
|
|
107
|
+
// so the Settings page can render every CLI's install status plus
|
|
108
|
+
// the user's current pick.
|
|
109
|
+
router.get('/llm/diagnose', async (_req, res) => {
|
|
110
|
+
try {
|
|
111
|
+
const info = await diagnoseLlm(db);
|
|
112
|
+
res.json({ ok: true, data: info, meta: { generated_at: new Date().toISOString() } });
|
|
113
|
+
} catch (err) {
|
|
114
|
+
res.status(500).json({ ok: false, error: { code: 'LLM_DIAGNOSE_ERROR', message: err.message } });
|
|
115
|
+
}
|
|
116
|
+
});
|
|
117
|
+
|
|
118
|
+
// GET /api/settings/tools
|
|
119
|
+
router.get('/tools', (_req, res) => {
|
|
120
|
+
try {
|
|
121
|
+
const data = getAllToolConfigs(db);
|
|
122
|
+
|
|
123
|
+
res.json({
|
|
124
|
+
ok: true,
|
|
125
|
+
data,
|
|
126
|
+
meta: {
|
|
127
|
+
generated_at: new Date().toISOString(),
|
|
128
|
+
},
|
|
129
|
+
});
|
|
130
|
+
} catch (err) {
|
|
131
|
+
res.status(500).json({
|
|
132
|
+
ok: false,
|
|
133
|
+
error: { code: 'SETTINGS_ERROR', message: err.message },
|
|
134
|
+
});
|
|
135
|
+
}
|
|
136
|
+
});
|
|
137
|
+
|
|
138
|
+
return router;
|
|
139
|
+
};
|