@faithfulalabi/agent-lens 0.14.0 → 0.15.1

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.
@@ -139,7 +139,9 @@ const EVENT_COLUMNS = `id, turn_id, seq, kind, ts, request_id, block_index, name
139
139
  text, text_bytes, output_storage, spill_path, spill_bytes,
140
140
  model, tokens_in, tokens_out, tokens_cache_read, tokens_cache_write, est_cost,
141
141
  child_session_id, agent_type, agent_status, raw_type, raw_subtype, image_count,
142
- json_extract(attrs, '$.renderedRole') AS rendered_role`;
142
+ json_extract(attrs, '$.renderedRole') AS rendered_role,
143
+ json_extract(attrs, '$.permissionSource') AS permission_source,
144
+ json_extract(attrs, '$.permissionReasonType') AS permission_reason_type`;
143
145
  export function buildEventPageSql(session_id, query) {
144
146
  return {
145
147
  sql: `SELECT ${EVENT_COLUMNS} FROM events WHERE session_id = ? AND seq >= ?` +
@@ -190,7 +192,7 @@ const SPILL_SNIPPET = `snippet(spill_fts, 3, '<mark>', '</mark>', '…', 12)`;
190
192
  function ftsPhrase(q) {
191
193
  return `"${q.replaceAll('"', '""')}"`;
192
194
  }
193
- const SEARCHABLE = `e.kind <> 'unknown'`;
195
+ const SEARCHABLE = `(e.kind <> 'unknown' OR json_extract(e.attrs, '$.filename') IS NOT NULL)`;
194
196
  function searchSql(scoped) {
195
197
  const scope = scoped ? ' AND e.session_id = :session' : '';
196
198
  const events = `SELECT e.session_id AS session_id, s.title AS session_title, s.project_path AS project_path,` +
@@ -272,7 +272,8 @@ CREATE TABLE events (
272
272
  attrs TEXT NOT NULL DEFAULT '{}' -- ALLOWLIST ONLY: attributionSkill,
273
273
  -- attributionAgent, effort, permissionMode,
274
274
  -- compactMetadata, toolDenialKind, renderedRole,
275
- -- unknown_fields[]
275
+ -- permissionSource, permissionReasonType,
276
+ -- filename, unknown_fields[]
276
277
  );
277
278
  CREATE UNIQUE INDEX idx_events_session_seq ON events(session_id, seq);
278
279
  CREATE INDEX idx_events_turn ON events(turn_id, seq);
@@ -2,7 +2,7 @@ import { asyncAgentLaunch, taskNotification } from '../transcript/agents.js';
2
2
  import { attachmentRecord } from '../transcript/attachments.js';
3
3
  import { contentBlocks, imageCount, resultText } from '../transcript/blocks.js';
4
4
  import { isHumanPrompt, MACHINERY_TAGS } from '../transcript/human.js';
5
- import { apiErrorText, foldControlLines, foldSessionEnvelope, permissionDecision, promptGroupId, toolDenialKind, turnDurationMs, } from '../transcript/line.js';
5
+ import { apiErrorText, foldControlLines, foldSessionEnvelope, permissionDecision, permissionGrounds, promptGroupId, toolDenialKind, turnDurationMs, } from '../transcript/line.js';
6
6
  import { foldRequestGroup, groupByRequestId, modelOfRequestGroup, requestIdOf, } from '../transcript/usage.js';
7
7
  import { joinToolCalls, NO_INPUT, NO_WIRE_INPUT, toolInput, wireInput, } from './tools.js';
8
8
  import { wireInputOf } from '../transcript/wire.js';
@@ -138,6 +138,7 @@ export function runPipeline(lines, ctx) {
138
138
  is_error: block.is_error,
139
139
  denial: toolDenialKind(line),
140
140
  decision: permissionDecision(line),
141
+ grounds: permissionGrounds(line),
141
142
  launch: asyncAgentLaunch(line, output),
142
143
  ts: line.timestamp ?? '',
143
144
  result_offset: line.byte_offset,
@@ -180,7 +181,7 @@ export function runPipeline(lines, ctx) {
180
181
  const usage = usageAt.get(index);
181
182
  const record = blocks.length === 0 ? attachmentRecord(line) : undefined;
182
183
  const lineText = blocks.length === 0 ? (record?.text ?? apiErrorText(line)) : undefined;
183
- const attrs = record?.role === undefined ? '{}' : JSON.stringify({ renderedRole: record.role });
184
+ const attrs = JSON.stringify({ renderedRole: record?.role, filename: record?.filename });
184
185
  let stamped = false;
185
186
  const emit = (block, blockIndex) => {
186
187
  if (block?.kind === 'unknown_block')
@@ -113,6 +113,14 @@ export function joinToolCalls(events, results, notifications, drift) {
113
113
  statedDenial(result.decision) !== (result.denial !== undefined)) {
114
114
  drift.notePermissionDecisionMismatch();
115
115
  }
116
+ const { source, reasonType } = result.grounds;
117
+ if (source !== undefined || reasonType !== undefined) {
118
+ event.attrs = JSON.stringify({
119
+ ...JSON.parse(event.attrs),
120
+ permissionSource: source,
121
+ permissionReasonType: reasonType,
122
+ });
123
+ }
116
124
  if (event.name === 'Agent' && result.launch !== undefined) {
117
125
  backPatch(event, answers.get(event.id));
118
126
  continue;
@@ -6,12 +6,14 @@ function placeholder(subtype) {
6
6
  export function attachmentRecord(line) {
7
7
  if (line.kind !== 'attachment')
8
8
  return undefined;
9
- const subtype = str(obj(line.raw.attachment, undefined)?.type, '');
9
+ const payload = obj(line.raw.attachment, undefined);
10
+ const subtype = str(payload?.type, '');
10
11
  const first = arr(line.raw.rendered, NO_ELEMENTS)[0];
11
12
  const rendered = str(obj(first, undefined)?.content, undefined);
12
13
  return {
13
14
  subtype,
14
15
  text: rendered ?? placeholder(subtype),
15
16
  role: str(line.raw.renderedRole, undefined),
17
+ filename: str(payload?.filename, undefined),
16
18
  };
17
19
  }
@@ -73,7 +73,8 @@ export function classifyContent(content) {
73
73
  export function resultText(children) {
74
74
  const texts = children.flatMap((child) => (child.kind === 'text' ? [child.text] : []));
75
75
  const images = children.flatMap((child) => (child.kind === 'image' ? [child.placeholder] : []));
76
- return [...texts, ...images].join('\n');
76
+ const refs = children.flatMap((child) => child.kind === 'tool_reference' ? [`[tool_reference ${child.name}]`] : []);
77
+ return [...texts, ...images, ...refs].join('\n');
77
78
  }
78
79
  export function contentBlocks(line) {
79
80
  return classifyContent(rawContent(line));
@@ -308,6 +308,13 @@ export function permissionDecision(line) {
308
308
  return undefined;
309
309
  return str(obj(line.raw.permissionDecision, undefined)?.decision, '');
310
310
  }
311
+ export function permissionGrounds(line) {
312
+ const stated = obj(line.raw.permissionDecision, undefined);
313
+ return {
314
+ source: str(stated?.source, '') || undefined,
315
+ reasonType: str(stated?.reasonType, '') || undefined,
316
+ };
317
+ }
311
318
  export function turnDurationMs(line) {
312
319
  return line.kind === 'system' && line.subtype === 'turn_duration'
313
320
  ? num(line.raw.durationMs, undefined)
@@ -1 +1 @@
1
- export const PROJECTOR_VERSION = 19;
1
+ export const PROJECTOR_VERSION = 21;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@faithfulalabi/agent-lens",
3
- "version": "0.14.0",
3
+ "version": "0.15.1",
4
4
  "description": "Local-first agentic tracing platform: inspect everything your agent did in a session.",
5
5
  "type": "module",
6
6
  "license": "MIT",