@swestash/agent-lens 0.13.2 → 0.14.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/agent-lens.js +85 -74
- package/dist/agent-lens.js.map +1 -1
- package/package.json +1 -1
- package/web/404.html +2 -2
- package/web/assets/{AboutView-BeI-5j66.js → AboutView-BT3khsXF.js} +1 -1
- package/web/assets/{Dashboard-CNFiRoDc.js → Dashboard-BpHfCxya.js} +1 -1
- package/web/assets/{FileView-CX6MM-I0.js → FileView-BnBOTV_P.js} +1 -1
- package/web/assets/{FilesView-5efC0Q2V.js → FilesView-DxZpH4mQ.js} +1 -1
- package/web/assets/{SecurityView-_lXarP24.js → SecurityView-EWa7NdWS.js} +1 -1
- package/web/assets/SessionView-DwHI6Yj5.js +15 -0
- package/web/assets/{SkillView-BpcEr9Ho.js → SkillView-BfgC6TUO.js} +1 -1
- package/web/assets/{SkillsView-DmlqdlXF.js → SkillsView-CutJX709.js} +1 -1
- package/web/assets/{WorkflowView-rl-KuYmz.js → WorkflowView-D48zVacX.js} +1 -1
- package/web/assets/index-BPS7n4_B.css +1 -0
- package/web/assets/{index-_eftXBrf.js → index-DAAJAs1W.js} +2 -2
- package/web/assets/{index-BX7yb5zg.js → index-DrIth0LC.js} +1 -1
- package/web/assets/{jsonish-BVkL-KHT.js → jsonish-BI7lbDY8.js} +1 -1
- package/web/assets/{useResetOn-T-HJS_6T.js → useResetOn-DeM1LqwP.js} +1 -1
- package/web/index.html +2 -2
- package/web/assets/SessionView-LbRKoJUo.js +0 -15
- package/web/assets/index-BJ7zMXRA.css +0 -1
package/dist/agent-lens.js
CHANGED
|
@@ -591,7 +591,7 @@ var CAC = class extends EventEmitter {
|
|
|
591
591
|
var cac = (name = "") => new CAC(name);
|
|
592
592
|
|
|
593
593
|
// ../core/dist/schema.js
|
|
594
|
-
var SCHEMA_VERSION =
|
|
594
|
+
var SCHEMA_VERSION = 15;
|
|
595
595
|
var SCHEMA_SQL = (
|
|
596
596
|
/* sql */
|
|
597
597
|
`
|
|
@@ -688,7 +688,8 @@ CREATE TABLE IF NOT EXISTS events (
|
|
|
688
688
|
model TEXT,
|
|
689
689
|
is_sidechain INTEGER NOT NULL DEFAULT 0,
|
|
690
690
|
is_meta INTEGER NOT NULL DEFAULT 0,
|
|
691
|
-
text TEXT, --
|
|
691
|
+
text TEXT, -- visible message text, for search + preview
|
|
692
|
+
thinking TEXT, -- reasoning blocks, kept apart so the read path needs no parsing
|
|
692
693
|
raw_json BLOB NOT NULL, -- original line, verbatim, gzip-compressed (ADR-011); read via unpackRaw()
|
|
693
694
|
source_file TEXT -- archive path the canonical copy came from
|
|
694
695
|
);
|
|
@@ -900,23 +901,27 @@ CREATE TABLE IF NOT EXISTS ingest_state (
|
|
|
900
901
|
);
|
|
901
902
|
|
|
902
903
|
-- Full-text search over event text (contentless external-content FTS). ------
|
|
904
|
+
-- Both columns are indexed: search covered reasoning back when text carried it inline, and the
|
|
905
|
+
-- queries are unqualified MATCHes, which span every column \u2014 so indexing thinking here is what makes
|
|
906
|
+
-- splitting the columns invisible to search.
|
|
903
907
|
CREATE VIRTUAL TABLE IF NOT EXISTS events_fts USING fts5(
|
|
904
908
|
text,
|
|
909
|
+
thinking,
|
|
905
910
|
content='events',
|
|
906
911
|
content_rowid='rowid'
|
|
907
912
|
);
|
|
908
913
|
|
|
909
914
|
-- Keep FTS in sync with events. --------------------------------------------
|
|
910
915
|
CREATE TRIGGER IF NOT EXISTS events_ai AFTER INSERT ON events BEGIN
|
|
911
|
-
INSERT INTO events_fts(rowid, text) VALUES (new.rowid, new.text);
|
|
916
|
+
INSERT INTO events_fts(rowid, text, thinking) VALUES (new.rowid, new.text, new.thinking);
|
|
912
917
|
END;
|
|
913
918
|
CREATE TRIGGER IF NOT EXISTS events_ad AFTER DELETE ON events BEGIN
|
|
914
|
-
INSERT INTO events_fts(events_fts, rowid, text) VALUES ('delete', old.rowid, old.text);
|
|
919
|
+
INSERT INTO events_fts(events_fts, rowid, text, thinking) VALUES ('delete', old.rowid, old.text, old.thinking);
|
|
915
920
|
END;
|
|
916
921
|
CREATE TRIGGER IF NOT EXISTS events_au AFTER UPDATE ON events
|
|
917
|
-
WHEN old.text IS NOT new.text BEGIN
|
|
918
|
-
INSERT INTO events_fts(events_fts, rowid, text) VALUES ('delete', old.rowid, old.text);
|
|
919
|
-
INSERT INTO events_fts(rowid, text) VALUES (new.rowid, new.text);
|
|
922
|
+
WHEN old.text IS NOT new.text OR old.thinking IS NOT new.thinking BEGIN
|
|
923
|
+
INSERT INTO events_fts(events_fts, rowid, text, thinking) VALUES ('delete', old.rowid, old.text, old.thinking);
|
|
924
|
+
INSERT INTO events_fts(rowid, text, thinking) VALUES (new.rowid, new.text, new.thinking);
|
|
920
925
|
END;
|
|
921
926
|
`
|
|
922
927
|
);
|
|
@@ -1371,14 +1376,6 @@ import { gzipSync, gunzipSync } from "node:zlib";
|
|
|
1371
1376
|
function packRaw(json) {
|
|
1372
1377
|
return gzipSync(Buffer.from(json, "utf8"));
|
|
1373
1378
|
}
|
|
1374
|
-
function unpackRaw(value) {
|
|
1375
|
-
if (typeof value === "string")
|
|
1376
|
-
return value;
|
|
1377
|
-
if (value.length >= 2 && value[0] === 31 && value[1] === 139) {
|
|
1378
|
-
return gunzipSync(value).toString("utf8");
|
|
1379
|
-
}
|
|
1380
|
-
return Buffer.from(value).toString("utf8");
|
|
1381
|
-
}
|
|
1382
1379
|
|
|
1383
1380
|
// ../core/dist/paths.js
|
|
1384
1381
|
import { existsSync, readFileSync } from "node:fs";
|
|
@@ -2338,25 +2335,36 @@ function resetSchema(db) {
|
|
|
2338
2335
|
// ../ingest/dist/adapters/claude-code.js
|
|
2339
2336
|
import { readdirSync as readdirSync2 } from "node:fs";
|
|
2340
2337
|
import { basename as basename2, join as join6 } from "node:path";
|
|
2341
|
-
function
|
|
2338
|
+
function extractParts(content) {
|
|
2342
2339
|
if (content == null)
|
|
2343
|
-
return null;
|
|
2340
|
+
return { text: null, thinking: null };
|
|
2344
2341
|
if (typeof content === "string")
|
|
2345
|
-
return content.trim() || null;
|
|
2342
|
+
return { text: content.trim() || null, thinking: null };
|
|
2346
2343
|
if (!Array.isArray(content))
|
|
2347
|
-
return null;
|
|
2348
|
-
const
|
|
2344
|
+
return { text: null, thinking: null };
|
|
2345
|
+
const texts = [];
|
|
2346
|
+
const thoughts = [];
|
|
2349
2347
|
for (const block of content) {
|
|
2350
2348
|
if (!block || typeof block !== "object")
|
|
2351
2349
|
continue;
|
|
2352
2350
|
const b = block;
|
|
2353
2351
|
if (b.type === "text" && typeof b.text === "string")
|
|
2354
|
-
|
|
2352
|
+
texts.push(b.text);
|
|
2355
2353
|
else if (b.type === "thinking" && typeof b.thinking === "string")
|
|
2356
|
-
|
|
2354
|
+
thoughts.push(b.thinking);
|
|
2357
2355
|
}
|
|
2358
|
-
|
|
2359
|
-
|
|
2356
|
+
return { text: texts.join("\n").trim() || null, thinking: thoughts.join("\n").trim() || null };
|
|
2357
|
+
}
|
|
2358
|
+
function flattenText(content) {
|
|
2359
|
+
return extractParts(content).text;
|
|
2360
|
+
}
|
|
2361
|
+
function queuedPromptText(attachment) {
|
|
2362
|
+
if (!attachment || typeof attachment !== "object")
|
|
2363
|
+
return null;
|
|
2364
|
+
const a = attachment;
|
|
2365
|
+
if (a.type !== "queued_command" || a.commandMode !== "prompt")
|
|
2366
|
+
return null;
|
|
2367
|
+
return flattenText(a.prompt);
|
|
2360
2368
|
}
|
|
2361
2369
|
var RESULT_SUMMARY_MAX_LINES = 40;
|
|
2362
2370
|
var RESULT_SUMMARY_MAX_CHARS = 2e3;
|
|
@@ -2484,7 +2492,9 @@ var ClaudeCodeAdapter = class {
|
|
|
2484
2492
|
if (!asString(r.uuid))
|
|
2485
2493
|
return { meta };
|
|
2486
2494
|
const message = r.message ?? {};
|
|
2487
|
-
const
|
|
2495
|
+
const queued = queuedPromptText(r.attachment);
|
|
2496
|
+
const parts = extractParts(message.content);
|
|
2497
|
+
const text = parts.text ?? queued;
|
|
2488
2498
|
const result = {
|
|
2489
2499
|
meta,
|
|
2490
2500
|
event: {
|
|
@@ -2494,12 +2504,15 @@ var ClaudeCodeAdapter = class {
|
|
|
2494
2504
|
parent_uuid: asString(r.parentUuid),
|
|
2495
2505
|
seq,
|
|
2496
2506
|
type: asString(r.type) ?? "unknown",
|
|
2497
|
-
|
|
2507
|
+
// A recovered queued prompt has no `message` to take a role from, but it *is* the user
|
|
2508
|
+
// speaking — without this the transcript labels their own words as a generic attachment.
|
|
2509
|
+
role: asString(message.role) ?? (queued ? "user" : null),
|
|
2498
2510
|
timestamp: asString(r.timestamp),
|
|
2499
2511
|
model: asString(message.model),
|
|
2500
2512
|
is_sidechain: r.isSidechain ? 1 : 0,
|
|
2501
2513
|
is_meta: r.isMeta ? 1 : 0,
|
|
2502
2514
|
text,
|
|
2515
|
+
thinking: parts.thinking,
|
|
2503
2516
|
raw_json: JSON.stringify(raw),
|
|
2504
2517
|
source_file: file.path
|
|
2505
2518
|
}
|
|
@@ -3596,6 +3609,24 @@ function canonicalizeProjects(db, opts = {}) {
|
|
|
3596
3609
|
|
|
3597
3610
|
// ../ingest/dist/pipeline.js
|
|
3598
3611
|
import { createHash as createHash5 } from "node:crypto";
|
|
3612
|
+
|
|
3613
|
+
// ../transcript-format/dist/index.js
|
|
3614
|
+
var COMMAND_OUTPUT_TAGS = ["local-command-stdout", "local-command-stderr", "command-output"];
|
|
3615
|
+
var TASK_NOTIFICATION_TAG = "task-notification";
|
|
3616
|
+
var TASK_STATUS_TAG = "status";
|
|
3617
|
+
var TASK_SUMMARY_TAG = "summary";
|
|
3618
|
+
var TASK_RESULT_TAG = "result";
|
|
3619
|
+
var TASK_FAILURES_TAG = "failures";
|
|
3620
|
+
var COMMAND_OUTPUT_ALTERNATION = COMMAND_OUTPUT_TAGS.join("|");
|
|
3621
|
+
var COMMAND_RESULT_CARRIER = new RegExp(`^<(?:${COMMAND_OUTPUT_ALTERNATION})>`);
|
|
3622
|
+
function isCommandResultCarrier(text) {
|
|
3623
|
+
return COMMAND_RESULT_CARRIER.test(text.trimStart());
|
|
3624
|
+
}
|
|
3625
|
+
function xmlTag(text, tag) {
|
|
3626
|
+
return text.match(new RegExp(`<${tag}>([\\s\\S]*?)</${tag}>`))?.[1]?.trim() ?? null;
|
|
3627
|
+
}
|
|
3628
|
+
|
|
3629
|
+
// ../ingest/dist/pipeline.js
|
|
3599
3630
|
function newStats() {
|
|
3600
3631
|
return { files: 0, skipped: 0, malformed: 0, newEvents: 0 };
|
|
3601
3632
|
}
|
|
@@ -3689,13 +3720,14 @@ function durationMs(start, end) {
|
|
|
3689
3720
|
return Math.max(0, b - a);
|
|
3690
3721
|
}
|
|
3691
3722
|
function buildTurns(sessionId, rows) {
|
|
3692
|
-
rows.sort((x, y) => (x.timestamp ?? "").localeCompare(y.timestamp ?? "") || x.uuid.localeCompare(y.uuid));
|
|
3723
|
+
rows.sort((x, y) => (x.timestamp ?? "").localeCompare(y.timestamp ?? "") || (x.seq ?? 0) - (y.seq ?? 0) || x.uuid.localeCompare(y.uuid));
|
|
3693
3724
|
const turns = [];
|
|
3694
3725
|
const eventTurn = /* @__PURE__ */ new Map();
|
|
3695
3726
|
let cur = null;
|
|
3696
3727
|
let seq = 0;
|
|
3697
3728
|
for (const e of rows) {
|
|
3698
|
-
const
|
|
3729
|
+
const body = e.text?.trim() ?? "";
|
|
3730
|
+
const isPrompt = e.type === "user" && e.is_meta === 0 && body.length > 0 && !isCommandResultCarrier(body);
|
|
3699
3731
|
if (isPrompt) {
|
|
3700
3732
|
cur = {
|
|
3701
3733
|
id: `${sessionId}:${seq}`,
|
|
@@ -3753,7 +3785,7 @@ function expandDirtyScope(db, dirty) {
|
|
|
3753
3785
|
}
|
|
3754
3786
|
}
|
|
3755
3787
|
function rebuildTurns(db, scope, sessionIds) {
|
|
3756
|
-
const selEvents = db.prepare("SELECT uuid, type, is_sidechain, is_meta, timestamp, model, text FROM events WHERE session_id = ?");
|
|
3788
|
+
const selEvents = db.prepare("SELECT uuid, seq, type, is_sidechain, is_meta, timestamp, model, text FROM events WHERE session_id = ?");
|
|
3757
3789
|
const insTurn = db.prepare(`INSERT INTO turns (id, session_id, seq, user_event_uuid, prompt_preview, model, started_at, ended_at, duration_ms)
|
|
3758
3790
|
VALUES (@id, @session_id, @seq, @user_event_uuid, @prompt_preview, @model, @started_at, @ended_at, @duration_ms)`);
|
|
3759
3791
|
const updEventTurn = db.prepare("UPDATE events SET turn_id = ? WHERE uuid = ?");
|
|
@@ -3932,8 +3964,8 @@ function prepareStatements(db) {
|
|
|
3932
3964
|
ON CONFLICT(file_path) DO UPDATE SET
|
|
3933
3965
|
size=excluded.size, mtime_ms=excluded.mtime_ms, sha256=excluded.sha256,
|
|
3934
3966
|
events_ingested=excluded.events_ingested, ingested_at=excluded.ingested_at`),
|
|
3935
|
-
insEvent: db.prepare(`INSERT INTO events (uuid, session_id, turn_id, parent_uuid, seq, type, role, timestamp, model, is_sidechain, is_meta, text, raw_json, source_file)
|
|
3936
|
-
VALUES (@uuid, @session_id, @turn_id, @parent_uuid, @seq, @type, @role, @timestamp, @model, @is_sidechain, @is_meta, @text, @raw_json, @source_file)
|
|
3967
|
+
insEvent: db.prepare(`INSERT INTO events (uuid, session_id, turn_id, parent_uuid, seq, type, role, timestamp, model, is_sidechain, is_meta, text, thinking, raw_json, source_file)
|
|
3968
|
+
VALUES (@uuid, @session_id, @turn_id, @parent_uuid, @seq, @type, @role, @timestamp, @model, @is_sidechain, @is_meta, @text, @thinking, @raw_json, @source_file)
|
|
3937
3969
|
ON CONFLICT(uuid) DO NOTHING`),
|
|
3938
3970
|
insTokens: db.prepare(
|
|
3939
3971
|
// Bare ON CONFLICT (no target) so the insert is skipped on EITHER the event_uuid PK or the
|
|
@@ -4614,25 +4646,6 @@ function lastIngested(db) {
|
|
|
4614
4646
|
const row = db.prepare("SELECT MAX(ingested_at) AS last FROM ingest_state").get();
|
|
4615
4647
|
return row?.last ?? null;
|
|
4616
4648
|
}
|
|
4617
|
-
function extractParts(rawJson) {
|
|
4618
|
-
let text = "";
|
|
4619
|
-
let thinking = "";
|
|
4620
|
-
try {
|
|
4621
|
-
const content = JSON.parse(unpackRaw(rawJson))?.message?.content;
|
|
4622
|
-
if (typeof content === "string")
|
|
4623
|
-
text = content;
|
|
4624
|
-
else if (Array.isArray(content)) {
|
|
4625
|
-
for (const b of content) {
|
|
4626
|
-
if (b?.type === "text" && typeof b.text === "string")
|
|
4627
|
-
text += (text ? "\n" : "") + b.text;
|
|
4628
|
-
else if (b?.type === "thinking" && typeof b.thinking === "string")
|
|
4629
|
-
thinking += (thinking ? "\n" : "") + b.thinking;
|
|
4630
|
-
}
|
|
4631
|
-
}
|
|
4632
|
-
} catch {
|
|
4633
|
-
}
|
|
4634
|
-
return { text: text || null, thinking: thinking || null };
|
|
4635
|
-
}
|
|
4636
4649
|
function listSources(db) {
|
|
4637
4650
|
return queryAll(db, `SELECT s.id, s.label,
|
|
4638
4651
|
(SELECT COUNT(*) FROM sessions x WHERE x.source_id = s.id AND x.is_sidechain = 0) AS session_count
|
|
@@ -4815,7 +4828,7 @@ function toResponseToolCall({ event_uuid: _e, error_type: _t, ...call }) {
|
|
|
4815
4828
|
return call;
|
|
4816
4829
|
}
|
|
4817
4830
|
function loadEvents(db, id, toolRows) {
|
|
4818
|
-
const eventRows = queryAll(db, `SELECT uuid, type, role, timestamp, model, is_sidechain, turn_id,
|
|
4831
|
+
const eventRows = queryAll(db, `SELECT uuid, type, role, timestamp, model, is_sidechain, turn_id, text, thinking
|
|
4819
4832
|
FROM events WHERE session_id = ? ORDER BY timestamp, seq`, id);
|
|
4820
4833
|
const toolsByEvent = /* @__PURE__ */ new Map();
|
|
4821
4834
|
for (const t of toolRows) {
|
|
@@ -4823,21 +4836,18 @@ function loadEvents(db, id, toolRows) {
|
|
|
4823
4836
|
continue;
|
|
4824
4837
|
pushGrouped(toolsByEvent, t.event_uuid, t);
|
|
4825
4838
|
}
|
|
4826
|
-
return eventRows.map((e) => {
|
|
4827
|
-
|
|
4828
|
-
|
|
4829
|
-
|
|
4830
|
-
|
|
4831
|
-
|
|
4832
|
-
|
|
4833
|
-
|
|
4834
|
-
|
|
4835
|
-
|
|
4836
|
-
|
|
4837
|
-
|
|
4838
|
-
toolCalls: (toolsByEvent.get(e.uuid) ?? []).map(toResponseToolCall)
|
|
4839
|
-
};
|
|
4840
|
-
});
|
|
4839
|
+
return eventRows.map((e) => ({
|
|
4840
|
+
uuid: e.uuid,
|
|
4841
|
+
type: e.type,
|
|
4842
|
+
role: e.role,
|
|
4843
|
+
timestamp: e.timestamp,
|
|
4844
|
+
model: e.model,
|
|
4845
|
+
is_sidechain: e.is_sidechain,
|
|
4846
|
+
turn_id: e.turn_id,
|
|
4847
|
+
text: e.text,
|
|
4848
|
+
thinking: e.thinking,
|
|
4849
|
+
toolCalls: (toolsByEvent.get(e.uuid) ?? []).map(toResponseToolCall)
|
|
4850
|
+
}));
|
|
4841
4851
|
}
|
|
4842
4852
|
function loadClassification(db, id) {
|
|
4843
4853
|
const cls = queryGet(db, `SELECT category, complexity_score, complexity_band, signals_json, classifier_version
|
|
@@ -4932,9 +4942,6 @@ function getSession(db, id) {
|
|
|
4932
4942
|
file_changes: loadFileChanges(db, id)
|
|
4933
4943
|
};
|
|
4934
4944
|
}
|
|
4935
|
-
function xmlTag(text, tag) {
|
|
4936
|
-
return text.match(new RegExp(`<${tag}>([\\s\\S]*?)</${tag}>`))?.[1]?.trim() ?? null;
|
|
4937
|
-
}
|
|
4938
4945
|
function safeJson(s) {
|
|
4939
4946
|
if (!s)
|
|
4940
4947
|
return null;
|
|
@@ -4975,14 +4982,18 @@ function loadAgents(db, runId) {
|
|
|
4975
4982
|
function parseCompletion(db, wf) {
|
|
4976
4983
|
if (!wf.tool_use_id || !wf.parent_session_id)
|
|
4977
4984
|
return null;
|
|
4978
|
-
const ev = queryGet(db, `SELECT
|
|
4979
|
-
WHERE session_id = ? AND text LIKE '
|
|
4985
|
+
const ev = queryGet(db, `SELECT text FROM events
|
|
4986
|
+
WHERE session_id = ? AND text LIKE '%<${TASK_NOTIFICATION_TAG}>%' AND text LIKE ?
|
|
4980
4987
|
ORDER BY timestamp DESC LIMIT 1`, wf.parent_session_id, `%${wf.tool_use_id}%`);
|
|
4981
4988
|
if (!ev)
|
|
4982
4989
|
return null;
|
|
4983
|
-
const
|
|
4984
|
-
|
|
4985
|
-
|
|
4990
|
+
const t = ev.text ?? "";
|
|
4991
|
+
return {
|
|
4992
|
+
status: xmlTag(t, TASK_STATUS_TAG),
|
|
4993
|
+
summary: xmlTag(t, TASK_SUMMARY_TAG),
|
|
4994
|
+
result: xmlTag(t, TASK_RESULT_TAG),
|
|
4995
|
+
failures: xmlTag(t, TASK_FAILURES_TAG)
|
|
4996
|
+
};
|
|
4986
4997
|
}
|
|
4987
4998
|
function loadRunSidecar(db, runId, completion) {
|
|
4988
4999
|
const wr = tableExists(db, "workflow_results") ? queryGet(db, `SELECT status, summary, default_model, result_json, phases_json, logs_json, progress_json,
|