@faithfulalabi/agent-lens 0.8.0 → 0.10.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.
@@ -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 { foldControlLines, foldSessionEnvelope, promptGroupId, toolDenialKind, turnDurationMs, } from '../transcript/line.js';
5
+ import { foldControlLines, foldSessionEnvelope, permissionDecision, 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';
@@ -51,14 +51,13 @@ function blockText(block) {
51
51
  function eventKind(line, block, human) {
52
52
  switch (block?.kind) {
53
53
  case 'text':
54
+ case 'image':
54
55
  return human ? 'prompt' : 'text';
55
56
  case 'thinking':
56
57
  case 'thinking_elided':
57
58
  return 'thinking';
58
59
  case 'tool_use':
59
60
  return 'tool_call';
60
- case 'image':
61
- return 'text';
62
61
  }
63
62
  if (line.kind === 'system') {
64
63
  if (line.subtype === 'compact_boundary')
@@ -138,6 +137,7 @@ export function runPipeline(lines, ctx) {
138
137
  text: output,
139
138
  is_error: block.is_error,
140
139
  denial: toolDenialKind(line),
140
+ decision: permissionDecision(line),
141
141
  launch: asyncAgentLaunch(line, output),
142
142
  ts: line.timestamp ?? '',
143
143
  result_offset: line.byte_offset,
@@ -49,8 +49,15 @@ function elapsedMs(call, result) {
49
49
  const ms = epochMs(result) - epochMs(call);
50
50
  return ms < 0 ? 0 : ms;
51
51
  }
52
+ function statedDenial(decision) {
53
+ if (decision === 'reject')
54
+ return true;
55
+ if (decision === 'accept')
56
+ return false;
57
+ return undefined;
58
+ }
52
59
  function statusOf(result) {
53
- if (result.denial !== undefined)
60
+ if (statedDenial(result.decision) ?? result.denial !== undefined)
54
61
  return 'denied';
55
62
  return result.is_error ? 'error' : 'ok';
56
63
  }
@@ -102,6 +109,10 @@ export function joinToolCalls(events, results, notifications, drift) {
102
109
  event.duration_ms = elapsedMs(event.ts, result.ts);
103
110
  event.duration_source = 'elapsed';
104
111
  event.status = statusOf(result);
112
+ if (result.decision !== undefined &&
113
+ statedDenial(result.decision) !== (result.denial !== undefined)) {
114
+ drift.notePermissionDecisionMismatch();
115
+ }
105
116
  if (event.name === 'Agent' && result.launch !== undefined) {
106
117
  backPatch(event, answers.get(event.id));
107
118
  continue;
@@ -12,6 +12,7 @@ export class DriftCounter {
12
12
  blockTypes = new Map();
13
13
  unjoinedToolUses = 0;
14
14
  sidecarAgentIdMismatches = 0;
15
+ permissionDecisionMismatches = 0;
15
16
  noteLine(line, knownFields) {
16
17
  for (const name of Object.keys(line)) {
17
18
  if (!knownFields.has(name))
@@ -30,8 +31,12 @@ export class DriftCounter {
30
31
  noteSidecarMismatch() {
31
32
  this.sidecarAgentIdMismatches += 1;
32
33
  }
34
+ notePermissionDecisionMismatch() {
35
+ this.permissionDecisionMismatches += 1;
36
+ }
33
37
  serialize() {
34
38
  return JSON.stringify({
39
+ permission_decision_mismatch: this.permissionDecisionMismatches === 0 ? undefined : this.permissionDecisionMismatches,
35
40
  sidecar_agent_id_mismatch: this.sidecarAgentIdMismatches === 0 ? undefined : this.sidecarAgentIdMismatches,
36
41
  unjoined_tool_uses: this.unjoinedToolUses === 0 ? undefined : this.unjoinedToolUses,
37
42
  unknown_block_types: bucket(this.blockTypes),
@@ -75,6 +75,7 @@ const LINE_TYPES = new Map([
75
75
  'classifierMetaLines',
76
76
  'toolEndsTurn',
77
77
  'toolDenialKind',
78
+ 'permissionDecision',
78
79
  'imagePasteIds',
79
80
  'serverClassifierContext',
80
81
  'turnOrigin',
@@ -264,6 +265,11 @@ export function promptGroupId(line) {
264
265
  export function toolDenialKind(line) {
265
266
  return str(line.raw.toolDenialKind, undefined);
266
267
  }
268
+ export function permissionDecision(line) {
269
+ if (line.raw.permissionDecision === undefined)
270
+ return undefined;
271
+ return str(obj(line.raw.permissionDecision, undefined)?.decision, '');
272
+ }
267
273
  export function turnDurationMs(line) {
268
274
  return line.kind === 'system' && line.subtype === 'turn_duration'
269
275
  ? num(line.raw.durationMs, undefined)
@@ -1 +1 @@
1
- export const PROJECTOR_VERSION = 15;
1
+ export const PROJECTOR_VERSION = 16;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@faithfulalabi/agent-lens",
3
- "version": "0.8.0",
3
+ "version": "0.10.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",