@swestash/agent-lens 0.6.0 → 0.7.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.
@@ -4487,6 +4487,53 @@ function pct(sorted, p) {
4487
4487
  const rank = Math.ceil(p / 100 * sorted.length) - 1;
4488
4488
  return sorted[Math.min(Math.max(rank, 0), sorted.length - 1)];
4489
4489
  }
4490
+ function tableExists2(db, name) {
4491
+ return !!db.prepare("SELECT 1 FROM sqlite_master WHERE type='table' AND name = ?").get(name);
4492
+ }
4493
+ function workflowWhere(f) {
4494
+ const where = [];
4495
+ const params = [];
4496
+ if (f.source)
4497
+ where.push("source_id = ?"), params.push(f.source);
4498
+ if (f.from)
4499
+ where.push("date(started_at) >= date(?)"), params.push(f.from);
4500
+ if (f.to)
4501
+ where.push("date(started_at) <= date(?)"), params.push(f.to);
4502
+ return { sql: where.length ? `WHERE ${where.join(" AND ")}` : "", params };
4503
+ }
4504
+ function workflowAgg(db, f) {
4505
+ const empty = { total: 0, by_status: [], completed: 0, failed: 0, success_rate: 0, total_tokens: 0, avg_duration_ms: 0 };
4506
+ if (!tableExists2(db, "workflow_results"))
4507
+ return empty;
4508
+ const ww = workflowWhere(f);
4509
+ const rows = db.prepare(`SELECT COALESCE(status, '(unknown)') status, COUNT(*) n,
4510
+ SUM(COALESCE(total_tokens, 0)) tokens,
4511
+ SUM(COALESCE(duration_ms, 0)) dur,
4512
+ SUM(CASE WHEN duration_ms IS NOT NULL THEN 1 ELSE 0 END) dur_n
4513
+ FROM workflow_results ${ww.sql} GROUP BY status ORDER BY n DESC`).all(...ww.params);
4514
+ let total = 0, completed = 0, failed = 0, tokens = 0, durSum = 0, durN = 0;
4515
+ const by_status = rows.map((r) => {
4516
+ total += r.n;
4517
+ if (r.status === "completed")
4518
+ completed += r.n;
4519
+ else if (r.status === "failed")
4520
+ failed += r.n;
4521
+ tokens += r.tokens ?? 0;
4522
+ durSum += r.dur ?? 0;
4523
+ durN += r.dur_n ?? 0;
4524
+ return { status: r.status, n: r.n };
4525
+ });
4526
+ const decided = completed + failed;
4527
+ return {
4528
+ total,
4529
+ by_status,
4530
+ completed,
4531
+ failed,
4532
+ success_rate: decided ? completed / decided : 0,
4533
+ total_tokens: tokens,
4534
+ avg_duration_ms: durN ? Math.round(durSum / durN) : 0
4535
+ };
4536
+ }
4490
4537
  function dashboardOverview(db, f) {
4491
4538
  const w = sessionWhere(f);
4492
4539
  const counts = db.prepare(`SELECT
@@ -4513,6 +4560,8 @@ function dashboardOverview(db, f) {
4513
4560
  const totalTokens = tokens.input + tokens.output + tokens.cache_creation + tokens.cache_read;
4514
4561
  const durs = db.prepare(`SELECT tn.duration_ms d FROM turns tn JOIN sessions s ON s.id = tn.session_id
4515
4562
  ${w.sql ? w.sql + " AND" : "WHERE"} tn.duration_ms IS NOT NULL ORDER BY tn.duration_ms`).all(...w.params).map((r) => r.d);
4563
+ const sessDurs = db.prepare(`SELECT s.duration_ms d FROM sessions s
4564
+ ${w.sql ? w.sql + " AND" : "WHERE"} s.is_sidechain = 0 AND s.duration_ms IS NOT NULL ORDER BY s.duration_ms`).all(...w.params).map((r) => r.d);
4516
4565
  return {
4517
4566
  range: { from: f.from ?? null, to: f.to ?? null, source: f.source ?? null },
4518
4567
  sessions: counts.sessions ?? 0,
@@ -4527,7 +4576,9 @@ function dashboardOverview(db, f) {
4527
4576
  cache_read_ratio: totalTokens ? tokens.cache_read / totalTokens : 0,
4528
4577
  cost: Number(cost.toFixed(4)),
4529
4578
  unpriced_models: [...new Set(unpriced)].sort(),
4530
- turn_duration_ms: { p50: pct(durs, 50), p95: pct(durs, 95), count: durs.length }
4579
+ turn_duration_ms: { p50: pct(durs, 50), p95: pct(durs, 95), count: durs.length },
4580
+ session_duration_ms: { p50: pct(sessDurs, 50), p95: pct(sessDurs, 95), count: sessDurs.length },
4581
+ workflows: workflowAgg(db, f)
4531
4582
  };
4532
4583
  }
4533
4584
  function chooseBucket(db, f) {
@@ -4751,12 +4802,33 @@ function listMutes(db) {
4751
4802
  return db.prepare("SELECT rule_id, scope, scope_id, note, muted_at FROM muted_rules ORDER BY muted_at DESC").all();
4752
4803
  }
4753
4804
 
4805
+ // ../server/dist/prefs.js
4806
+ var PREFS_SCHEMA_SQL = (
4807
+ /* sql */
4808
+ `
4809
+ CREATE TABLE IF NOT EXISTS ui_prefs (
4810
+ key TEXT PRIMARY KEY, -- e.g. 'dashboard.charts', 'sessions.columns'
4811
+ value TEXT NOT NULL, -- JSON blob, opaque to the server
4812
+ updated_at TEXT NOT NULL
4813
+ );
4814
+ `
4815
+ );
4816
+ function getPref(db, key) {
4817
+ const row = db.prepare("SELECT value FROM ui_prefs WHERE key = ?").get(key);
4818
+ return row ? row.value : null;
4819
+ }
4820
+ function setPref(db, key, value) {
4821
+ db.prepare(`INSERT INTO ui_prefs (key, value, updated_at) VALUES (?, ?, ?)
4822
+ ON CONFLICT(key) DO UPDATE SET value = excluded.value, updated_at = excluded.updated_at`).run(key, value, (/* @__PURE__ */ new Date()).toISOString());
4823
+ }
4824
+
4754
4825
  // ../server/dist/app.js
4755
4826
  async function createApp(db, opts = {}) {
4756
4827
  const app = Fastify({ logger: false });
4757
4828
  let triageDb = null;
4758
4829
  if (opts.triageDbPath) {
4759
4830
  triageDb = openTriage(opts.triageDbPath);
4831
+ triageDb.exec(PREFS_SCHEMA_SQL);
4760
4832
  db.exec(`ATTACH DATABASE '${opts.triageDbPath.replace(/'/g, "''")}' AS triage`);
4761
4833
  app.addHook("onClose", async () => triageDb?.close());
4762
4834
  }
@@ -4803,6 +4875,26 @@ async function createApp(db, opts = {}) {
4803
4875
  return dashboardTimeseries(db, dashFilters(req), q.bucket);
4804
4876
  });
4805
4877
  app.get("/api/dashboard/breakdowns", async (req) => dashboardBreakdowns(db, dashFilters(req)));
4878
+ const PREF_KEY = /^[a-z0-9._-]{1,64}$/i;
4879
+ app.get("/api/prefs/:key", async (req, reply) => {
4880
+ const { key } = req.params;
4881
+ if (!PREF_KEY.test(key))
4882
+ return reply.code(400).send({ error: { code: "BAD_KEY", message: "invalid pref key" } });
4883
+ if (!triageDb)
4884
+ return { value: null };
4885
+ const raw = getPref(triageDb, key);
4886
+ return { value: raw == null ? null : JSON.parse(raw) };
4887
+ });
4888
+ app.put("/api/prefs/:key", async (req, reply) => {
4889
+ const { key } = req.params;
4890
+ if (!PREF_KEY.test(key))
4891
+ return reply.code(400).send({ error: { code: "BAD_KEY", message: "invalid pref key" } });
4892
+ if (!guardWrite(req, reply))
4893
+ return;
4894
+ const body = req.body;
4895
+ setPref(triageDb, key, JSON.stringify(body?.value ?? null));
4896
+ return { ok: true };
4897
+ });
4806
4898
  app.get("/api/sessions", async (req) => {
4807
4899
  const q = req.query;
4808
4900
  return listSessions(db, {