@faithfulalabi/agent-lens 0.1.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.
Files changed (76) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +124 -0
  3. package/bin/agent-lens.js +40 -0
  4. package/bin/package.json +4 -0
  5. package/dist/src/archive/cron-log.js +41 -0
  6. package/dist/src/archive/discover.js +89 -0
  7. package/dist/src/archive/index.js +7 -0
  8. package/dist/src/archive/lock.js +119 -0
  9. package/dist/src/archive/log.js +15 -0
  10. package/dist/src/archive/mirror.js +366 -0
  11. package/dist/src/archive/paths.js +159 -0
  12. package/dist/src/archive/read.js +133 -0
  13. package/dist/src/archive/report.js +272 -0
  14. package/dist/src/archive/seal.js +76 -0
  15. package/dist/src/archive/sidecar.js +39 -0
  16. package/dist/src/cli/args.js +47 -0
  17. package/dist/src/cli/commands/archive.js +65 -0
  18. package/dist/src/cli/commands/doctor.js +191 -0
  19. package/dist/src/cli/commands/prune.js +159 -0
  20. package/dist/src/cli/commands/rebuild.js +98 -0
  21. package/dist/src/cli/commands/schedule.js +325 -0
  22. package/dist/src/cli/commands/start.js +83 -0
  23. package/dist/src/cli/commands/warm.js +96 -0
  24. package/dist/src/cli/index.js +102 -0
  25. package/dist/src/content/resolve.js +163 -0
  26. package/dist/src/corpus/env.js +32 -0
  27. package/dist/src/corpus/paths.js +70 -0
  28. package/dist/src/corpus/scan.js +85 -0
  29. package/dist/src/corpus/watch.js +189 -0
  30. package/dist/src/db/freshness.js +82 -0
  31. package/dist/src/db/open.js +74 -0
  32. package/dist/src/db/read.js +266 -0
  33. package/dist/src/db/schema.js +312 -0
  34. package/dist/src/db/sidecars.js +216 -0
  35. package/dist/src/db/spill-index.js +68 -0
  36. package/dist/src/db/write.js +279 -0
  37. package/dist/src/project/pipeline.js +307 -0
  38. package/dist/src/project/subagents.js +41 -0
  39. package/dist/src/project/tools.js +94 -0
  40. package/dist/src/server/api.js +249 -0
  41. package/dist/src/server/app.js +28 -0
  42. package/dist/src/server/config.js +24 -0
  43. package/dist/src/server/drift-report.js +35 -0
  44. package/dist/src/server/index.js +1 -0
  45. package/dist/src/server/live.js +109 -0
  46. package/dist/src/server/middleware/host-guard.js +42 -0
  47. package/dist/src/server/middleware/token-auth.js +19 -0
  48. package/dist/src/server/start.js +150 -0
  49. package/dist/src/server/static-ui.js +97 -0
  50. package/dist/src/server/stream.js +50 -0
  51. package/dist/src/server/warm.js +48 -0
  52. package/dist/src/shared/api.js +1 -0
  53. package/dist/src/shared/entities.js +1 -0
  54. package/dist/src/shared/index.js +2 -0
  55. package/dist/src/shared/pricing.js +68 -0
  56. package/dist/src/shared/token.js +39 -0
  57. package/dist/src/transcript/accessors.js +28 -0
  58. package/dist/src/transcript/agents.js +44 -0
  59. package/dist/src/transcript/blocks.js +75 -0
  60. package/dist/src/transcript/drift.js +42 -0
  61. package/dist/src/transcript/human.js +65 -0
  62. package/dist/src/transcript/line.js +251 -0
  63. package/dist/src/transcript/raw-types.js +1 -0
  64. package/dist/src/transcript/spill.js +122 -0
  65. package/dist/src/transcript/usage.js +63 -0
  66. package/dist/src/transcript/version.js +1 -0
  67. package/package.json +70 -0
  68. package/ui/dist/assets/index-CKKoKUCq.js +254 -0
  69. package/ui/dist/assets/index-Chza4fL6.css +1 -0
  70. package/ui/dist/assets/inter-latin-ext-wght-normal-DO1Apj_S.woff2 +0 -0
  71. package/ui/dist/assets/inter-latin-wght-normal-Dx4kXJAl.woff2 +0 -0
  72. package/ui/dist/assets/jetbrains-mono-latin-400-normal-V6pRDFza.woff2 +0 -0
  73. package/ui/dist/assets/jetbrains-mono-latin-500-normal-BWZEU5yA.woff2 +0 -0
  74. package/ui/dist/assets/jetbrains-mono-latin-ext-400-normal-Bc8Ftmh3.woff2 +0 -0
  75. package/ui/dist/assets/jetbrains-mono-latin-ext-500-normal-Cut-4mMH.woff2 +0 -0
  76. package/ui/dist/index.html +21 -0
@@ -0,0 +1,65 @@
1
+ import { obj, str } from './accessors.js';
2
+ import { contentBlocks } from './blocks.js';
3
+ export const MACHINERY_TAGS = [
4
+ '<task-notification>',
5
+ '<command-message>',
6
+ '<local-command-caveat>',
7
+ '<command-name>',
8
+ '<local-command-stdout>',
9
+ '<command-args>',
10
+ '<local-command-stderr>',
11
+ '<system-reminder>',
12
+ '<user-prompt-submit-hook>',
13
+ '<bash-input>',
14
+ '<bash-stdout>',
15
+ ];
16
+ export const MACHINERY_PREFIXES = [
17
+ '[Request interrupted',
18
+ 'Caveat: The messages below',
19
+ 'This session is being continued',
20
+ 'API Error',
21
+ ];
22
+ const LEADING_TAG = /^<[a-z][a-z0-9-]*>/;
23
+ function promptText(line) {
24
+ return contentBlocks(line)
25
+ .flatMap((block) => (block.kind === 'text' ? [block.text] : []))
26
+ .join('\n')
27
+ .trim();
28
+ }
29
+ function startsWithMachineryTag(text) {
30
+ return MACHINERY_TAGS.some((tag) => text.startsWith(tag)) || LEADING_TAG.test(text);
31
+ }
32
+ function fallbackRejection(line) {
33
+ if (line.kind !== 'user')
34
+ return 'not-a-user-line';
35
+ if (line.raw.isSidechain === true)
36
+ return 'sidechain';
37
+ if (line.raw.isMeta === true)
38
+ return 'meta';
39
+ if (line.raw.isCompactSummary === true)
40
+ return 'compact-summary';
41
+ const text = promptText(line);
42
+ if (text === '')
43
+ return 'no-text';
44
+ if (startsWithMachineryTag(text))
45
+ return 'machinery-tag';
46
+ const prefix = MACHINERY_PREFIXES.find((opener) => text.startsWith(opener));
47
+ return prefix === undefined ? '' : `machinery-prefix:${prefix}`;
48
+ }
49
+ export function fallbackIsHuman(line) {
50
+ return fallbackRejection(line) === '';
51
+ }
52
+ export function isHumanPrompt(line) {
53
+ if (line.kind !== 'user')
54
+ return { human: false, path: 'kind', reason: 'not-a-user-line' };
55
+ const originKind = str(obj(line.raw.origin, undefined)?.kind, undefined);
56
+ if (originKind !== undefined) {
57
+ return { human: originKind === 'human', path: 'origin', reason: `origin:${originKind}` };
58
+ }
59
+ const rejection = fallbackRejection(line);
60
+ return {
61
+ human: rejection === '',
62
+ path: 'fallback',
63
+ reason: rejection === '' ? 'prose' : rejection,
64
+ };
65
+ }
@@ -0,0 +1,251 @@
1
+ import { isoTs, num, obj, str } from './accessors.js';
2
+ const SYSTEM_SUBTYPES = [
3
+ 'turn_duration',
4
+ 'stop_hook_summary',
5
+ 'away_summary',
6
+ 'local_command',
7
+ 'compact_boundary',
8
+ 'informational',
9
+ 'api_error',
10
+ ];
11
+ const SYSTEM_SUBTYPE_SET = new Set(SYSTEM_SUBTYPES);
12
+ function isSystemSubtype(value) {
13
+ return SYSTEM_SUBTYPE_SET.has(value);
14
+ }
15
+ const ENVELOPE = [
16
+ 'type',
17
+ 'uuid',
18
+ 'parentUuid',
19
+ 'sessionId',
20
+ 'session_id',
21
+ 'timestamp',
22
+ 'version',
23
+ 'userType',
24
+ 'isSidechain',
25
+ 'isMeta',
26
+ 'agentId',
27
+ 'cwd',
28
+ 'gitBranch',
29
+ 'slug',
30
+ 'entrypoint',
31
+ ];
32
+ function fields(...groups) {
33
+ return new Set(groups.flat());
34
+ }
35
+ const LINE_TYPES = new Map([
36
+ [
37
+ 'assistant',
38
+ {
39
+ kind: 'assistant',
40
+ knownFields: fields(ENVELOPE, [
41
+ 'message',
42
+ 'requestId',
43
+ 'effort',
44
+ 'attributionAgent',
45
+ 'attributionSkill',
46
+ 'attributionPlugin',
47
+ 'isApiErrorMessage',
48
+ 'error',
49
+ ]),
50
+ },
51
+ ],
52
+ [
53
+ 'user',
54
+ {
55
+ kind: 'user',
56
+ knownFields: fields(ENVELOPE, [
57
+ 'message',
58
+ 'origin',
59
+ 'toolUseResult',
60
+ 'sourceToolAssistantUUID',
61
+ 'sourceToolUseID',
62
+ 'promptId',
63
+ 'promptSource',
64
+ 'permissionMode',
65
+ 'isCompactSummary',
66
+ 'isVisibleInTranscriptOnly',
67
+ 'classifierMetaLines',
68
+ 'toolEndsTurn',
69
+ 'toolDenialKind',
70
+ 'imagePasteIds',
71
+ ]),
72
+ },
73
+ ],
74
+ [
75
+ 'system',
76
+ {
77
+ kind: 'system',
78
+ knownFields: fields(ENVELOPE, [
79
+ 'subtype',
80
+ 'content',
81
+ 'level',
82
+ 'durationMs',
83
+ 'messageCount',
84
+ 'stopReason',
85
+ 'preventedContinuation',
86
+ 'compactMetadata',
87
+ 'logicalParentUuid',
88
+ 'toolUseID',
89
+ 'hasOutput',
90
+ 'hookCount',
91
+ 'hookErrors',
92
+ 'hookInfos',
93
+ 'hookAdditionalContext',
94
+ 'pendingBackgroundAgentCount',
95
+ 'pendingWorkflowCount',
96
+ ]),
97
+ },
98
+ ],
99
+ ['attachment', { kind: 'attachment', knownFields: fields(ENVELOPE, ['attachment']) }],
100
+ ['mode', { kind: 'mode', knownFields: fields(['type', 'mode', 'sessionId']) }],
101
+ [
102
+ 'last-prompt',
103
+ {
104
+ kind: 'last-prompt',
105
+ knownFields: fields(['type', 'lastPrompt', 'leafUuid', 'sessionId']),
106
+ },
107
+ ],
108
+ [
109
+ 'permission-mode',
110
+ {
111
+ kind: 'permission-mode',
112
+ knownFields: fields(['type', 'permissionMode', 'sessionId']),
113
+ },
114
+ ],
115
+ ['ai-title', { kind: 'ai-title', knownFields: fields(['type', 'aiTitle', 'sessionId']) }],
116
+ [
117
+ 'file-history-snapshot',
118
+ {
119
+ kind: 'file-history-snapshot',
120
+ knownFields: fields(['type', 'messageId', 'snapshot', 'isSnapshotUpdate']),
121
+ },
122
+ ],
123
+ [
124
+ 'file-history-delta',
125
+ {
126
+ kind: 'file-history-delta',
127
+ knownFields: fields([
128
+ 'type',
129
+ 'messageId',
130
+ 'snapshotMessageId',
131
+ 'trackingPath',
132
+ 'backup',
133
+ 'timestamp',
134
+ ]),
135
+ },
136
+ ],
137
+ [
138
+ 'queue-operation',
139
+ {
140
+ kind: 'queue-operation',
141
+ knownFields: fields(['type', 'operation', 'content', 'timestamp', 'sessionId']),
142
+ },
143
+ ],
144
+ [
145
+ 'pr-link',
146
+ {
147
+ kind: 'pr-link',
148
+ knownFields: fields(['type', 'sessionId', 'prNumber', 'prUrl', 'prRepository', 'timestamp']),
149
+ },
150
+ ],
151
+ ['started', { kind: 'started', knownFields: fields(['type', 'key', 'agentId']) }],
152
+ ['result', { kind: 'result', knownFields: fields(['type', 'key', 'agentId', 'result']) }],
153
+ ]);
154
+ const EMPTY_RECORD = Object.freeze({});
155
+ function rawTypeKey(value) {
156
+ if (typeof value === 'string')
157
+ return value;
158
+ return value === null ? '<null>' : `<${typeof value}>`;
159
+ }
160
+ export function classifyLine(json, ctx) {
161
+ const raw = obj(json, EMPTY_RECORD);
162
+ const base = {
163
+ byte_offset: ctx.byteOffset,
164
+ byte_length: ctx.byteLength,
165
+ uuid: str(raw.uuid, undefined),
166
+ session_id: str(raw.sessionId, undefined),
167
+ timestamp: isoTs(raw.timestamp, undefined),
168
+ raw,
169
+ };
170
+ const entry = LINE_TYPES.get(str(raw.type, ''));
171
+ if (entry === undefined) {
172
+ const raw_type = rawTypeKey(raw.type);
173
+ ctx.drift.noteUnknownType(raw_type);
174
+ return { ...base, kind: 'unknown', raw_type, raw_subtype: str(raw.subtype, '') };
175
+ }
176
+ ctx.drift.noteLine(raw, entry.knownFields);
177
+ if (entry.kind === 'system') {
178
+ const subtype = str(raw.subtype, '');
179
+ if (!isSystemSubtype(subtype)) {
180
+ ctx.drift.noteUnknownType(`system.${subtype}`);
181
+ return { ...base, kind: 'unknown', raw_type: 'system', raw_subtype: subtype };
182
+ }
183
+ return { ...base, kind: 'system', subtype };
184
+ }
185
+ return { ...base, kind: entry.kind };
186
+ }
187
+ export function foldControlLines(lines) {
188
+ const projection = {
189
+ ai_title: undefined,
190
+ last_prompt: undefined,
191
+ last_prompt_leaf_uuid: undefined,
192
+ };
193
+ for (const line of lines) {
194
+ if (line.kind === 'ai-title') {
195
+ projection.ai_title = str(line.raw.aiTitle, undefined);
196
+ }
197
+ else if (line.kind === 'last-prompt') {
198
+ projection.last_prompt = str(line.raw.lastPrompt, undefined);
199
+ projection.last_prompt_leaf_uuid = str(line.raw.leafUuid, undefined);
200
+ }
201
+ }
202
+ return projection;
203
+ }
204
+ export function promptGroupId(line) {
205
+ return str(line.raw.promptId, undefined);
206
+ }
207
+ export function toolDenialKind(line) {
208
+ return str(line.raw.toolDenialKind, undefined);
209
+ }
210
+ export function turnDurationMs(line) {
211
+ return line.kind === 'system' && line.subtype === 'turn_duration'
212
+ ? num(line.raw.durationMs, undefined)
213
+ : undefined;
214
+ }
215
+ const SYNTHETIC_MODEL = '<synthetic>';
216
+ export function foldSessionEnvelope(lines) {
217
+ const envelope = {
218
+ project_path: undefined,
219
+ git_branch: undefined,
220
+ harness_version: undefined,
221
+ model: undefined,
222
+ started_at: undefined,
223
+ last_activity_at: undefined,
224
+ };
225
+ const linesPerModel = new Map();
226
+ for (const line of lines) {
227
+ envelope.project_path = str(line.raw.cwd, envelope.project_path);
228
+ envelope.git_branch = str(line.raw.gitBranch, envelope.git_branch);
229
+ envelope.harness_version = str(line.raw.version, envelope.harness_version);
230
+ const model = str(obj(line.raw.message, undefined)?.model, undefined);
231
+ if (model !== undefined && model !== SYNTHETIC_MODEL) {
232
+ linesPerModel.set(model, (linesPerModel.get(model) ?? 0) + 1);
233
+ }
234
+ const at = line.timestamp;
235
+ if (at === undefined)
236
+ continue;
237
+ if (envelope.started_at === undefined || at < envelope.started_at)
238
+ envelope.started_at = at;
239
+ if (envelope.last_activity_at === undefined || at > envelope.last_activity_at) {
240
+ envelope.last_activity_at = at;
241
+ }
242
+ }
243
+ let mostLines = 0;
244
+ for (const [model, count] of linesPerModel) {
245
+ if (count > mostLines) {
246
+ envelope.model = model;
247
+ mostLines = count;
248
+ }
249
+ }
250
+ return envelope;
251
+ }
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,122 @@
1
+ import { basename, join } from 'node:path';
2
+ import { num, obj, str } from './accessors.js';
3
+ const PERSISTED_MARKER = '<persisted-output>';
4
+ const SAVED_TO = /Full output saved to: (.+)$/m;
5
+ const SPILL_SUBDIR = 'tool-results';
6
+ export const TRUNCATION_BYTES = 30000;
7
+ function toolResultText(line) {
8
+ const content = obj(obj(line, undefined)?.message, undefined)?.content;
9
+ const blocks = Array.isArray(content) ? content : undefined;
10
+ if (blocks === undefined)
11
+ return undefined;
12
+ for (const block of blocks) {
13
+ const fields = obj(block, undefined);
14
+ if (str(fields?.type, undefined) !== 'tool_result')
15
+ continue;
16
+ const inner = fields?.content;
17
+ const direct = str(inner, undefined);
18
+ if (direct !== undefined)
19
+ return direct;
20
+ if (!Array.isArray(inner))
21
+ continue;
22
+ for (const part of inner) {
23
+ const text = str(obj(part, undefined)?.text, undefined);
24
+ if (text !== undefined)
25
+ return text;
26
+ }
27
+ }
28
+ return undefined;
29
+ }
30
+ function declaredPathOf(line) {
31
+ const tur = obj(line, undefined)?.toolUseResult;
32
+ const pointer = str(obj(tur, undefined)?.['persistedOutputPath'], undefined);
33
+ if (pointer !== undefined) {
34
+ const size = num(obj(tur, undefined)?.['persistedOutputSize'], undefined);
35
+ return {
36
+ path: pointer,
37
+ source: 'pointer',
38
+ declaredSize: size !== undefined && Number.isInteger(size) ? size : undefined,
39
+ };
40
+ }
41
+ const text = toolResultText(line);
42
+ if (text === undefined || !text.startsWith(PERSISTED_MARKER))
43
+ return undefined;
44
+ const marked = SAVED_TO.exec(text)?.[1];
45
+ return marked === undefined ? undefined : { path: marked, source: 'marker' };
46
+ }
47
+ function reanchor(name, env) {
48
+ if (name === '')
49
+ return undefined;
50
+ for (const root of [env.sessionRoot, env.archiveRoot]) {
51
+ if (root === undefined)
52
+ continue;
53
+ const candidate = join(root, SPILL_SUBDIR, name);
54
+ if (env.exists(candidate))
55
+ return candidate;
56
+ }
57
+ return undefined;
58
+ }
59
+ export function resolvePersistedOutput(line, env) {
60
+ let declared;
61
+ try {
62
+ declared = declaredPathOf(line);
63
+ }
64
+ catch {
65
+ return { kind: 'none' };
66
+ }
67
+ if (declared === undefined) {
68
+ return hasMarker(line) ? { kind: 'missing', reason: 'no-path' } : { kind: 'none' };
69
+ }
70
+ const probe = (path) => {
71
+ try {
72
+ return env.exists(path) === true;
73
+ }
74
+ catch {
75
+ return false;
76
+ }
77
+ };
78
+ const resolvedAt = (path) => ({
79
+ kind: 'resolved',
80
+ path,
81
+ source: declared.source,
82
+ ...(declared.declaredSize !== undefined && { declaredSize: declared.declaredSize }),
83
+ });
84
+ const contained = (path) => {
85
+ if (env.withinRoots === undefined)
86
+ return true;
87
+ try {
88
+ return env.withinRoots(path) === true;
89
+ }
90
+ catch {
91
+ return false;
92
+ }
93
+ };
94
+ if (contained(declared.path) && probe(declared.path))
95
+ return resolvedAt(declared.path);
96
+ let rescued;
97
+ try {
98
+ rescued = reanchor(basename(declared.path), env);
99
+ }
100
+ catch {
101
+ rescued = undefined;
102
+ }
103
+ if (rescued !== undefined)
104
+ return resolvedAt(rescued);
105
+ return { kind: 'missing', reason: 'not-on-disk', declaredPath: declared.path };
106
+ }
107
+ function hasMarker(line) {
108
+ try {
109
+ return toolResultText(line)?.startsWith(PERSISTED_MARKER) === true;
110
+ }
111
+ catch {
112
+ return false;
113
+ }
114
+ }
115
+ export function isHarnessTruncated(text) {
116
+ const value = str(text, undefined);
117
+ if (value === undefined)
118
+ return false;
119
+ if (value.startsWith(PERSISTED_MARKER))
120
+ return true;
121
+ return Buffer.byteLength(value, 'utf8') >= TRUNCATION_BYTES;
122
+ }
@@ -0,0 +1,63 @@
1
+ import { num, obj, str } from './accessors.js';
2
+ const ONCE_PER_GROUP = [
3
+ 'input_tokens',
4
+ 'cache_creation_input_tokens',
5
+ 'cache_read_input_tokens',
6
+ ];
7
+ const ZERO = {
8
+ input_tokens: 0,
9
+ output_tokens: 0,
10
+ cache_creation_input_tokens: 0,
11
+ cache_read_input_tokens: 0,
12
+ };
13
+ function usageOf(line) {
14
+ return obj(obj(obj(line, undefined)?.message, undefined)?.usage, undefined);
15
+ }
16
+ export function requestIdOf(line) {
17
+ return str(obj(line, undefined)?.requestId, undefined);
18
+ }
19
+ export function foldRequestGroup(lines) {
20
+ const folded = { ...ZERO };
21
+ const taken = new Set();
22
+ for (const line of lines) {
23
+ const usage = usageOf(line);
24
+ if (usage === undefined)
25
+ continue;
26
+ for (const field of ONCE_PER_GROUP) {
27
+ if (taken.has(field))
28
+ continue;
29
+ const value = num(usage[field], undefined);
30
+ if (value === undefined)
31
+ continue;
32
+ folded[field] = value;
33
+ taken.add(field);
34
+ }
35
+ const output = num(usage['output_tokens'], undefined);
36
+ if (output !== undefined)
37
+ folded.output_tokens = output;
38
+ }
39
+ return folded;
40
+ }
41
+ export function modelOfRequestGroup(lines) {
42
+ for (const line of lines) {
43
+ const model = str(obj(obj(line, undefined)?.message, undefined)?.model, undefined);
44
+ if (model !== undefined)
45
+ return model;
46
+ }
47
+ return undefined;
48
+ }
49
+ export function groupByRequestId(lines) {
50
+ const groups = [];
51
+ let currentId;
52
+ for (const line of lines) {
53
+ const id = requestIdOf(line);
54
+ const last = groups[groups.length - 1];
55
+ if (id !== undefined && id === currentId && last !== undefined) {
56
+ last.push(line);
57
+ continue;
58
+ }
59
+ groups.push([line]);
60
+ currentId = id;
61
+ }
62
+ return groups;
63
+ }
@@ -0,0 +1 @@
1
+ export const PROJECTOR_VERSION = 10;
package/package.json ADDED
@@ -0,0 +1,70 @@
1
+ {
2
+ "name": "@faithfulalabi/agent-lens",
3
+ "version": "0.1.0",
4
+ "description": "Local-first agentic tracing platform: inspect everything your agent did in a session.",
5
+ "type": "module",
6
+ "license": "MIT",
7
+ "author": "Faithful Alabi",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/faithfulalabi/agent-lens.git"
11
+ },
12
+ "homepage": "https://github.com/faithfulalabi/agent-lens#readme",
13
+ "bugs": "https://github.com/faithfulalabi/agent-lens/issues",
14
+ "keywords": [
15
+ "claude-code",
16
+ "agent",
17
+ "tracing",
18
+ "observability",
19
+ "transcripts",
20
+ "local-first",
21
+ "cli"
22
+ ],
23
+ "engines": {
24
+ "node": ">=24"
25
+ },
26
+ "bin": {
27
+ "agent-lens": "bin/agent-lens.js"
28
+ },
29
+ "files": [
30
+ "bin",
31
+ "dist",
32
+ "ui/dist"
33
+ ],
34
+ "publishConfig": {
35
+ "access": "public"
36
+ },
37
+ "scripts": {
38
+ "dev": "tsx src/dev/server.ts",
39
+ "dev:ui": "npm --prefix ui run dev",
40
+ "render-gate": "tsx src/render-gate/index.ts",
41
+ "test": "vitest run",
42
+ "typecheck": "tsc -p tsconfig.node.json --noEmit && npm --prefix ui run typecheck",
43
+ "build": "rm -rf dist && tsc -p tsconfig.build.json && npm --prefix ui run build",
44
+ "smoke:pack": "node --import tsx scripts/pack-smoke.mjs",
45
+ "smoke:ui": "playwright test",
46
+ "lint": "eslint .",
47
+ "prepublishOnly": "npm run build",
48
+ "format": "prettier --write .",
49
+ "prepare": "npm --prefix ui install"
50
+ },
51
+ "dependencies": {
52
+ "@hono/node-server": "^2.0.11",
53
+ "hono": "^4.6.0"
54
+ },
55
+ "devDependencies": {
56
+ "@eslint/js": "^9.0.0",
57
+ "@playwright/test": "^1.62.1",
58
+ "@types/node": "^24.0.0",
59
+ "eslint": "^9.0.0",
60
+ "eslint-config-prettier": "^9.1.0",
61
+ "fast-check": "^3.23.2",
62
+ "globals": "^15.15.0",
63
+ "playwright-core": "^1.62.1",
64
+ "prettier": "^3.3.0",
65
+ "tsx": "^4.19.0",
66
+ "typescript": "^5.6.0",
67
+ "typescript-eslint": "^8.0.0",
68
+ "vitest": "^3.2.0"
69
+ }
70
+ }