@faithfulalabi/agent-lens 0.4.1 → 0.5.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/src/cli/commands/doctor.js +29 -11
- package/dist/src/db/read.js +2 -1
- package/dist/src/project/pipeline.js +6 -2
- package/dist/src/server/config.js +1 -1
- package/dist/src/server/drift-report.js +1 -1
- package/dist/src/server/middleware/host-guard.js +1 -6
- package/dist/src/shared/token.js +1 -1
- package/dist/src/transcript/attachments.js +13 -0
- package/dist/src/transcript/version.js +1 -1
- package/package.json +3 -2
- package/ui/dist/assets/{index-T2tFdorC.js → index-khXVDRM8.js} +61 -61
- package/ui/dist/index.html +1 -1
|
@@ -2,7 +2,7 @@ import { join } from 'node:path';
|
|
|
2
2
|
import { buildDoctorReport, readCronLogStatus, resolveDataDir, } from '../../archive/index.js';
|
|
3
3
|
import { CACHE_DB_FILE, openReadOnlyDb } from '../../db/open.js';
|
|
4
4
|
import { readDriftRows, readHealthCounts, readMeta } from '../../db/read.js';
|
|
5
|
-
import { aggregateDrift } from '../../server/drift-report.js';
|
|
5
|
+
import { aggregateDrift, mergeBucket } from '../../server/drift-report.js';
|
|
6
6
|
import { parseStringFlag } from './archive.js';
|
|
7
7
|
const MAX_LISTED = 20;
|
|
8
8
|
export const COVERAGE_GAP_STATEMENT = 'agent-lens can only archive what exists while it runs — a gap in uptime is a gap in the record.';
|
|
@@ -17,6 +17,11 @@ function listCapped(items, indent) {
|
|
|
17
17
|
function files(n) {
|
|
18
18
|
return n === 1 ? '1 file' : `${n} files`;
|
|
19
19
|
}
|
|
20
|
+
function mergeNamed(row, key, from) {
|
|
21
|
+
if (from === undefined || Object.keys(from).length === 0)
|
|
22
|
+
return;
|
|
23
|
+
mergeBucket((row[key] ??= {}), from);
|
|
24
|
+
}
|
|
20
25
|
export function readCacheStats(dataDir) {
|
|
21
26
|
const dir = resolveDataDir(dataDir);
|
|
22
27
|
const path = join(dir, CACHE_DB_FILE);
|
|
@@ -27,10 +32,17 @@ export function readCacheStats(dataDir) {
|
|
|
27
32
|
return { state: 'absent', path };
|
|
28
33
|
const counts = readHealthCounts(db);
|
|
29
34
|
const drift = aggregateDrift(readDriftRows(db));
|
|
30
|
-
const
|
|
35
|
+
const census = new Map();
|
|
36
|
+
for (const [version, projected] of Object.entries(drift.harness_versions)) {
|
|
37
|
+
census.set(version, { version, projected, drifting: 0 });
|
|
38
|
+
}
|
|
31
39
|
for (const session of drift.sessions_with_drift) {
|
|
32
|
-
const
|
|
33
|
-
|
|
40
|
+
const row = census.get(session.harness_version ?? 'unknown');
|
|
41
|
+
if (row === undefined)
|
|
42
|
+
continue;
|
|
43
|
+
row.drifting += 1;
|
|
44
|
+
mergeNamed(row, 'unknown_line_types', session.counts.unknown_line_types);
|
|
45
|
+
mergeNamed(row, 'unknown_top_level_fields', session.counts.unknown_top_level_fields);
|
|
34
46
|
}
|
|
35
47
|
return {
|
|
36
48
|
state: 'ready',
|
|
@@ -41,13 +53,7 @@ export function readCacheStats(dataDir) {
|
|
|
41
53
|
sessions_projected: counts.sessions_projected,
|
|
42
54
|
schema_version: readMeta(db, 'schema_version'),
|
|
43
55
|
projector_version: readMeta(db, 'projector_version'),
|
|
44
|
-
harness:
|
|
45
|
-
.map(([version, projected]) => ({
|
|
46
|
-
version,
|
|
47
|
-
projected,
|
|
48
|
-
drifting: drifting.get(version) ?? 0,
|
|
49
|
-
}))
|
|
50
|
-
.sort((a, b) => (a.version < b.version ? -1 : 1)),
|
|
56
|
+
harness: [...census.values()].sort((a, b) => (a.version < b.version ? -1 : 1)),
|
|
51
57
|
},
|
|
52
58
|
};
|
|
53
59
|
}
|
|
@@ -59,6 +65,16 @@ export function readCacheStats(dataDir) {
|
|
|
59
65
|
db.close();
|
|
60
66
|
}
|
|
61
67
|
}
|
|
68
|
+
const MAX_NAMED_DRIFT = 3;
|
|
69
|
+
function namedDrift(label, bucket) {
|
|
70
|
+
const names = Object.entries(bucket ?? {}).sort((a, b) => b[1] - a[1] || (a[0] < b[0] ? -1 : 1));
|
|
71
|
+
if (names.length === 0)
|
|
72
|
+
return [];
|
|
73
|
+
const shown = names.slice(0, MAX_NAMED_DRIFT).map(([name, n]) => `${name} ${n}`);
|
|
74
|
+
if (names.length > MAX_NAMED_DRIFT)
|
|
75
|
+
shown.push(`… and ${names.length - MAX_NAMED_DRIFT} more`);
|
|
76
|
+
return [` ${label} (${names.length}): ${shown.join(', ')}`];
|
|
77
|
+
}
|
|
62
78
|
export function formatCacheSection(cache) {
|
|
63
79
|
if (cache.state === 'absent') {
|
|
64
80
|
return ['', `cache: ${cache.path} — not created yet; \`agent-lens start\` builds it`];
|
|
@@ -86,6 +102,8 @@ export function formatCacheSection(cache) {
|
|
|
86
102
|
lines.push(' drift by harness version (the census counts clean rows too):');
|
|
87
103
|
for (const row of stats.harness) {
|
|
88
104
|
lines.push(` ${row.version}: ${row.projected} projected, ${row.drifting} drifting`);
|
|
105
|
+
lines.push(...namedDrift('unknown record types', row.unknown_line_types));
|
|
106
|
+
lines.push(...namedDrift('unknown top-level fields', row.unknown_top_level_fields));
|
|
89
107
|
}
|
|
90
108
|
return lines;
|
|
91
109
|
}
|
package/dist/src/db/read.js
CHANGED
|
@@ -181,6 +181,7 @@ const SPILL_SNIPPET = `snippet(spill_fts, 3, '<mark>', '</mark>', '…', 12)`;
|
|
|
181
181
|
function ftsPhrase(q) {
|
|
182
182
|
return `"${q.replaceAll('"', '""')}"`;
|
|
183
183
|
}
|
|
184
|
+
const SEARCHABLE = `e.kind <> 'unknown'`;
|
|
184
185
|
function searchSql(scoped) {
|
|
185
186
|
const scope = scoped ? ' AND e.session_id = :session' : '';
|
|
186
187
|
const events = `SELECT e.session_id AS session_id, s.title AS session_title, s.project_path AS project_path,` +
|
|
@@ -188,7 +189,7 @@ function searchSql(scoped) {
|
|
|
188
189
|
` e.ts AS ts, ${SNIPPET} AS snippet, rank AS score` +
|
|
189
190
|
` FROM events_fts JOIN events e ON e.rowid = events_fts.rowid` +
|
|
190
191
|
` JOIN sessions s ON s.id = e.session_id` +
|
|
191
|
-
` WHERE events_fts MATCH :q${scope}`;
|
|
192
|
+
` WHERE events_fts MATCH :q AND ${SEARCHABLE}${scope}`;
|
|
192
193
|
const spills = `SELECT e.session_id, s.title, s.project_path, e.turn_id, e.id, e.seq, e.kind, e.name,` +
|
|
193
194
|
` e.ts, ${SPILL_SNIPPET}, rank` +
|
|
194
195
|
` FROM spill_fts JOIN events e ON e.id = spill_fts.event_id` +
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { asyncAgentLaunch, taskNotification } from '../transcript/agents.js';
|
|
2
|
+
import { attachmentRecord } from '../transcript/attachments.js';
|
|
2
3
|
import { contentBlocks } from '../transcript/blocks.js';
|
|
3
4
|
import { isHumanPrompt, MACHINERY_TAGS } from '../transcript/human.js';
|
|
4
5
|
import { foldControlLines, foldSessionEnvelope, promptGroupId, toolDenialKind, turnDurationMs, } from '../transcript/line.js';
|
|
@@ -75,7 +76,9 @@ function rawTypeOf(line) {
|
|
|
75
76
|
function rawSubtypeOf(line) {
|
|
76
77
|
if (line.kind === 'unknown')
|
|
77
78
|
return line.raw_subtype;
|
|
78
|
-
|
|
79
|
+
if (line.kind === 'system')
|
|
80
|
+
return line.subtype;
|
|
81
|
+
return attachmentRecord(line)?.subtype;
|
|
79
82
|
}
|
|
80
83
|
function turnKind(lead, human, text, compacted) {
|
|
81
84
|
if (human)
|
|
@@ -176,6 +179,7 @@ export function runPipeline(lines, ctx) {
|
|
|
176
179
|
const blocks = blocksAt[index];
|
|
177
180
|
const human = humanAt[index];
|
|
178
181
|
const usage = usageAt.get(index);
|
|
182
|
+
const lineText = blocks.length === 0 ? attachmentRecord(line)?.text : undefined;
|
|
179
183
|
let stamped = false;
|
|
180
184
|
const emit = (block, blockIndex) => {
|
|
181
185
|
if (block?.kind === 'unknown_block')
|
|
@@ -201,7 +205,7 @@ export function runPipeline(lines, ctx) {
|
|
|
201
205
|
child_session_id: undefined,
|
|
202
206
|
agent_type: undefined,
|
|
203
207
|
...(block?.kind === 'tool_use' ? toolInput(block.input) : NO_INPUT),
|
|
204
|
-
text: kind === 'tool_call' ? undefined : blockText(block),
|
|
208
|
+
text: kind === 'tool_call' ? undefined : (blockText(block) ?? lineText),
|
|
205
209
|
text_bytes: undefined,
|
|
206
210
|
output_storage: kind === 'tool_call' ? 'absent' : undefined,
|
|
207
211
|
spill_path: undefined,
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { chmodSync, readFileSync, renameSync, rmSync, writeFileSync
|
|
1
|
+
import { chmodSync, readFileSync, renameSync, rmSync, writeFileSync } from 'node:fs';
|
|
2
2
|
import { join } from 'node:path';
|
|
3
3
|
const CONFIG_FILE = 'config.json';
|
|
4
4
|
export function writeConfig(dataDir, config) {
|
|
@@ -1,10 +1,5 @@
|
|
|
1
1
|
import { networkInterfaces } from 'node:os';
|
|
2
|
-
const ALLOWED_HOSTNAMES = new Set([
|
|
3
|
-
'localhost',
|
|
4
|
-
'127.0.0.1',
|
|
5
|
-
'[::1]',
|
|
6
|
-
'::1',
|
|
7
|
-
]);
|
|
2
|
+
const ALLOWED_HOSTNAMES = new Set(['localhost', '127.0.0.1', '[::1]', '::1']);
|
|
8
3
|
function isLoopbackBind(host) {
|
|
9
4
|
return ALLOWED_HOSTNAMES.has(host);
|
|
10
5
|
}
|
package/dist/src/shared/token.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { randomBytes } from 'node:crypto';
|
|
2
|
-
import { chmodSync, mkdirSync, readFileSync, renameSync, writeFileSync
|
|
2
|
+
import { chmodSync, mkdirSync, readFileSync, renameSync, writeFileSync } from 'node:fs';
|
|
3
3
|
import { homedir } from 'node:os';
|
|
4
4
|
import { join } from 'node:path';
|
|
5
5
|
export const TOKEN_HEADER = 'x-agentlens-token';
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { arr, obj, str } from './accessors.js';
|
|
2
|
+
const NO_ELEMENTS = Object.freeze([]);
|
|
3
|
+
function placeholder(subtype) {
|
|
4
|
+
return subtype === '' ? '[attachment]' : `[attachment ${subtype}]`;
|
|
5
|
+
}
|
|
6
|
+
export function attachmentRecord(line) {
|
|
7
|
+
if (line.kind !== 'attachment')
|
|
8
|
+
return undefined;
|
|
9
|
+
const subtype = str(obj(line.raw.attachment, undefined)?.type, '');
|
|
10
|
+
const first = arr(line.raw.rendered, NO_ELEMENTS)[0];
|
|
11
|
+
const rendered = str(obj(first, undefined)?.content, undefined);
|
|
12
|
+
return { subtype, text: rendered ?? placeholder(subtype) };
|
|
13
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
export const PROJECTOR_VERSION =
|
|
1
|
+
export const PROJECTOR_VERSION = 12;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@faithfulalabi/agent-lens",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.5.0",
|
|
4
4
|
"description": "Local-first agentic tracing platform: inspect everything your agent did in a session.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
@@ -47,6 +47,7 @@
|
|
|
47
47
|
"lint": "eslint .",
|
|
48
48
|
"prepublishOnly": "npm run build",
|
|
49
49
|
"format": "prettier --write .",
|
|
50
|
+
"format:check": "prettier --check .",
|
|
50
51
|
"prepare": "npm --prefix ui install"
|
|
51
52
|
},
|
|
52
53
|
"dependencies": {
|
|
@@ -62,7 +63,7 @@
|
|
|
62
63
|
"fast-check": "^3.23.2",
|
|
63
64
|
"globals": "^15.15.0",
|
|
64
65
|
"playwright-core": "^1.62.1",
|
|
65
|
-
"prettier": "
|
|
66
|
+
"prettier": "3.9.6",
|
|
66
67
|
"tsx": "^4.19.0",
|
|
67
68
|
"typescript": "^5.6.0",
|
|
68
69
|
"typescript-eslint": "^8.0.0",
|