feat: analytics parity — tool cost, date ranges, budgets, query explorer, replay, snapshots (v2.4.0)
Analytics parity phases 2–7 (docs/plans/analytics-parity/), built on
138e706 and ported onto v2.3.1:
- Cost by AI tool (?tool= on cost/analytics/stats/workflows, by_tool,
Codex fast/priority and flex tiers, agenthub_cost_usd_total{tool,model})
- Date ranges (from/to) on cost/analytics/workflows; Analytics range select
with Today / This week (Monday) / This month (1st) / 7d / 30d / 90d / All /
Custom; workflow filters carried into workflow-stats.js (read worker)
- Budgets and cost alerts as alert rule types (alert_rules rebuilt without
its rule_type CHECK), GET /api/budgets/status, rule preview, spend pace,
status-bar budget chip; rules stay editable in place
- Query Explorer (/query, allowlisted sort, CSV/JSON export) and scheduled
reports (report_definitions/report_runs, Settings → Reports)
- Session Replay tab and immutable snapshots (session_snapshots, /snapshot/:token)
- Remote Codex sync, push ingest with a hashed token, privacy redaction and
ignored folders (Settings → Data), Cursor sessions (no cost)
- Read-only MCP server, statusline, Grafana/Prometheus files
Also: resizable sidebar, pet run/wave animations, the workspace home keeps a
half-typed prompt, and the pre-commit hook runs only the related tests for a
small commit.
This commit is contained in:
@@ -23,21 +23,40 @@ function durationSec(s) {
|
||||
// ═══════════════════════════════════════════════════
|
||||
|
||||
/**
|
||||
* Build a SQL WHERE clause for session status filtering.
|
||||
* Returns { clause, params } where clause is either empty or " AND s.status = ?".
|
||||
* Use `sessionAlias` to match the table alias used in your query (default "s").
|
||||
* Build a SQL WHERE clause for the session filters `{status, tool, range}`
|
||||
* (`range` = `parseRange`'s result: sessions started in it).
|
||||
* Returns { clause, params } where clause is empty or " AND s.status = ? …".
|
||||
* Use `alias` to match the sessions table alias used in your query (default "s").
|
||||
*/
|
||||
function statusClause(statusFilter, alias = "s") {
|
||||
if (!statusFilter || statusFilter === "all") return { clause: "", params: [] };
|
||||
return { clause: ` AND ${alias}.status = ?`, params: [statusFilter] };
|
||||
function statusClause(filter, alias = "s") {
|
||||
const parts = [];
|
||||
const params = [];
|
||||
if (filter?.status && filter.status !== "all") {
|
||||
parts.push(`${alias}.status = ?`);
|
||||
params.push(filter.status);
|
||||
}
|
||||
if (filter?.tool) {
|
||||
parts.push(`${alias}.tool = ?`);
|
||||
params.push(filter.tool);
|
||||
}
|
||||
if (filter?.range?.start) {
|
||||
parts.push(`${alias}.started_at >= ?`);
|
||||
params.push(filter.range.start);
|
||||
}
|
||||
if (filter?.range?.end) {
|
||||
parts.push(`${alias}.started_at < ?`);
|
||||
params.push(filter.range.end);
|
||||
}
|
||||
return { clause: parts.map((p) => ` AND ${p}`).join(""), params };
|
||||
}
|
||||
|
||||
/** Same but for agents table joins where we need to filter via session_id */
|
||||
function sessionIdFilter(statusFilter) {
|
||||
if (!statusFilter || statusFilter === "all") return { clause: "", params: [] };
|
||||
function sessionIdFilter(filter) {
|
||||
const ss = statusClause(filter, "sessions");
|
||||
if (!ss.clause) return { clause: "", params: [] };
|
||||
return {
|
||||
clause: " AND session_id IN (SELECT id FROM sessions WHERE status = ?)",
|
||||
params: [statusFilter],
|
||||
clause: ` AND session_id IN (SELECT id FROM sessions WHERE 1=1${ss.clause})`,
|
||||
params: ss.params,
|
||||
};
|
||||
}
|
||||
|
||||
@@ -691,7 +710,8 @@ const WORKFLOW_SECTIONS = [
|
||||
* The GET /api/workflows bundle, or only the named sections of it.
|
||||
*
|
||||
* @param {object} conn SQLite connection (compat-sqlite Database)
|
||||
* @param {string|null} statusFilter "active"/"completed"/…, or null/"all"
|
||||
* @param {{status?: string|null, tool?: string|null, range?: object|null}|null} statusFilter
|
||||
* the session filters (see `statusClause`); null = every session
|
||||
* @param {string[]|null} sections subset of WORKFLOW_SECTIONS; null = all
|
||||
* @returns {object}
|
||||
*/
|
||||
|
||||
Reference in New Issue
Block a user