agentfootprint-lens 0.4.0 → 0.6.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/README.md +181 -148
- package/dist/chunk-3N4WQXQP.js +692 -0
- package/dist/chunk-3N4WQXQP.js.map +1 -0
- package/dist/chunk-VTJ6OQYJ.js +4934 -0
- package/dist/chunk-VTJ6OQYJ.js.map +1 -0
- package/dist/core.cjs +720 -0
- package/dist/core.cjs.map +1 -0
- package/dist/core.d.cts +484 -0
- package/dist/core.d.ts +484 -0
- package/dist/core.js +11 -0
- package/dist/core.js.map +1 -0
- package/dist/index.cjs +2216 -579
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +5 -548
- package/dist/index.d.ts +5 -548
- package/dist/index.js +40 -3966
- package/dist/index.js.map +1 -1
- package/dist/react.cjs +5665 -0
- package/dist/react.cjs.map +1 -0
- package/dist/react.d.cts +584 -0
- package/dist/react.d.ts +584 -0
- package/dist/react.js +59 -0
- package/dist/react.js.map +1 -0
- package/package.json +19 -3
package/README.md
CHANGED
|
@@ -1,227 +1,260 @@
|
|
|
1
1
|
# agentfootprint-lens
|
|
2
2
|
|
|
3
|
-
> **See
|
|
3
|
+
> **See the context engineering as it happens.**
|
|
4
4
|
>
|
|
5
|
-
> React components for
|
|
5
|
+
> React components for watching agents built on [`agentfootprint`](https://www.npmjs.com/package/agentfootprint). Every injection into the Agent's slots (RAG, Memory, Skills, Instructions, Tools) is tagged inline — students and engineers see exactly what was put into the prompt, by whom, on which iteration. No hidden abstractions.
|
|
6
|
+
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
### The pitch
|
|
10
|
+
|
|
11
|
+
agentfootprint = **2 primitives (LLM, Agent) + 3 compositions (Sequence, Parallel, Conditional) + N patterns (ReAct, Reflexion, Tree-of-Thoughts...) + cross-cutting context engineering.** Lens is the surface that makes the context engineering visible — not as a "RAG view" or a "Memory view," but as tagged injections inside the ONE Agent card. That's the whole pedagogy.
|
|
6
12
|
|
|
7
13
|
[](https://www.npmjs.com/package/agentfootprint-lens)
|
|
8
14
|
[](./LICENSE)
|
|
9
15
|
|
|
10
16
|
---
|
|
11
17
|
|
|
12
|
-
##
|
|
18
|
+
## 30-second quick start
|
|
13
19
|
|
|
14
|
-
|
|
20
|
+
```bash
|
|
21
|
+
npm install agentfootprint agentfootprint-lens
|
|
22
|
+
```
|
|
15
23
|
|
|
16
|
-
|
|
24
|
+
```tsx
|
|
25
|
+
import { Agent, anthropic } from 'agentfootprint';
|
|
26
|
+
import { Lens, useLens } from 'agentfootprint-lens';
|
|
17
27
|
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
28
|
+
export function App() {
|
|
29
|
+
const agent = useLens(() =>
|
|
30
|
+
Agent.create({ provider: anthropic('claude-sonnet-4') })
|
|
31
|
+
.system('You are a helpful assistant.')
|
|
32
|
+
.build()
|
|
33
|
+
);
|
|
23
34
|
|
|
24
|
-
|
|
35
|
+
return (
|
|
36
|
+
<>
|
|
37
|
+
<button onClick={() => agent.run('Hello!')}>Run</button>
|
|
38
|
+
<Lens for={agent} />
|
|
39
|
+
</>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
```
|
|
25
43
|
|
|
26
|
-
|
|
27
|
-
2. **Iteration Strip** — horizontal ribbon of every LLM call in the run. Click to jump.
|
|
28
|
-
3. **Tool Call Inspector** — flat sidebar of every tool invocation across all turns.
|
|
44
|
+
That's it. Two lines — `useLens(...)` + `<Lens for={agent} />` — and you get:
|
|
29
45
|
|
|
30
|
-
|
|
46
|
+
- A live **Messages** view (everything the LLM saw and said, per turn)
|
|
47
|
+
- An **Iteration Strip** (one cell per LLM call, tool call, or decision — scrubbable)
|
|
48
|
+
- A **Tool Call Inspector** (args, result, timing for the currently selected step)
|
|
49
|
+
- A **Decision Scope Ribbon** (which skill / decision rule was active)
|
|
50
|
+
- An **Explainable Trace** tab (the full footprintjs stage-level view)
|
|
31
51
|
|
|
32
|
-
|
|
52
|
+
No event wiring, no timeline prop, no snapshot prop. Lens figures it out by watching the runner directly.
|
|
33
53
|
|
|
34
54
|
---
|
|
35
55
|
|
|
36
|
-
##
|
|
56
|
+
## What you actually see
|
|
37
57
|
|
|
38
|
-
|
|
39
|
-
npm install agentfootprint-lens footprint-explainable-ui
|
|
40
|
-
```
|
|
58
|
+
As the agent runs, the three columns of Lens fill in live:
|
|
41
59
|
|
|
42
|
-
|
|
60
|
+
| Column | Shows |
|
|
61
|
+
|---|---|
|
|
62
|
+
| **Messages** | The conversation from the agent's perspective — system prompt, user turns, assistant replies, tool results |
|
|
63
|
+
| **Iteration Strip** | One row per ReAct loop iteration. Each row lists the LLM call that ran it, the tool calls it picked, and the time each took |
|
|
64
|
+
| **Context** | Whichever iteration or tool call is selected — shows the exact prompt the LLM saw, the tools it had available, and what it returned |
|
|
65
|
+
|
|
66
|
+
When the run finishes, the second tab (**Explainable Trace**) lights up with the full stage-level flowchart — same surface `footprint-explainable-ui` ships, zero extra wiring.
|
|
43
67
|
|
|
44
68
|
---
|
|
45
69
|
|
|
46
|
-
##
|
|
70
|
+
## Multiple watchers, one agent
|
|
71
|
+
|
|
72
|
+
`Lens` doesn't own the agent. Anything can observe it — a Lens, a Datadog exporter, a custom logger, or three of them at once.
|
|
47
73
|
|
|
48
74
|
```tsx
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
75
|
+
const agent = useLens(() => Agent.create(...).build());
|
|
76
|
+
|
|
77
|
+
// Lens in the sidebar
|
|
78
|
+
<Lens for={agent} />
|
|
79
|
+
|
|
80
|
+
// At the same time — ship events to your telemetry backend
|
|
81
|
+
useEffect(() => {
|
|
82
|
+
const stop = agent.observe((event) => {
|
|
83
|
+
if (event.type === 'llm_end') {
|
|
84
|
+
telemetry.record('llm.tokens', event.usage?.totalTokens);
|
|
85
|
+
}
|
|
86
|
+
});
|
|
87
|
+
return stop; // auto-unsubscribe on unmount
|
|
88
|
+
}, [agent]);
|
|
89
|
+
```
|
|
53
90
|
|
|
54
|
-
|
|
55
|
-
const [dark, setDark] = useState(true);
|
|
56
|
-
const [snapshot, setSnapshot] = useState(null);
|
|
91
|
+
`agent.observe(handler)` is the single subscribe primitive. It returns a `() => void` unsubscribe function. Add as many observers as you want.
|
|
57
92
|
|
|
58
|
-
|
|
59
|
-
const agent = Agent.create({ provider: anthropic('claude-haiku-4-5') })
|
|
60
|
-
.system('You are a helpful agent.')
|
|
61
|
-
.build();
|
|
62
|
-
agent.run('What time is it?').then(() => setSnapshot(agent.getSnapshot()));
|
|
63
|
-
}, []);
|
|
93
|
+
Event shape:
|
|
64
94
|
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
}
|
|
95
|
+
```ts
|
|
96
|
+
type AgentEvent =
|
|
97
|
+
| { type: 'turn_start'; userMessage: string }
|
|
98
|
+
| { type: 'llm_start'; iteration: number }
|
|
99
|
+
| { type: 'llm_end'; iteration: number; content: string; toolCallCount: number; usage?: TokenUsage; latencyMs: number }
|
|
100
|
+
| { type: 'tool_start'; toolName: string; args: Record<string, unknown> }
|
|
101
|
+
| { type: 'tool_end'; toolName: string; result: { content: string }; latencyMs: number }
|
|
102
|
+
| { type: 'token'; content: string } // streaming
|
|
103
|
+
| { type: 'turn_end'; content: string; iterations: number };
|
|
73
104
|
```
|
|
74
105
|
|
|
75
|
-
That's it. Lens reads the same `FootprintTheme` context explainable-ui reads, so **the consumer owns theming** — flip `coolDark` ↔ `coolLight` at the app root and both the agent view (Lens) and any drill-in trace view (explainable-ui) follow together.
|
|
76
|
-
|
|
77
106
|
---
|
|
78
107
|
|
|
79
|
-
##
|
|
108
|
+
## Works with every agentfootprint runner
|
|
80
109
|
|
|
81
|
-
|
|
110
|
+
`<Lens for={...}>` accepts any agentfootprint runner — the same prop works for all of them, and they all light up Lens identically:
|
|
82
111
|
|
|
83
112
|
```tsx
|
|
84
|
-
|
|
85
|
-
|
|
113
|
+
// Agent — a ReAct loop
|
|
114
|
+
const agent = useLens(() => Agent.create(...).build());
|
|
86
115
|
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
)}
|
|
100
|
-
</>
|
|
101
|
-
);
|
|
102
|
-
}
|
|
116
|
+
// LLMCall — a single prompt-in, response-out
|
|
117
|
+
const caller = useLens(() => LLMCall.create(...).build());
|
|
118
|
+
|
|
119
|
+
// RAG — retrieve + augment + answer
|
|
120
|
+
const rag = useLens(() => RAG.create(...).retriever(...).build());
|
|
121
|
+
|
|
122
|
+
// Swarm — LLM-routed specialists
|
|
123
|
+
const swarm = useLens(() => Swarm.create(...).build());
|
|
124
|
+
|
|
125
|
+
// ...same pattern for FlowChart, Parallel, Conditional
|
|
126
|
+
|
|
127
|
+
<Lens for={caller} /> // pick whichever
|
|
103
128
|
```
|
|
104
129
|
|
|
105
|
-
|
|
130
|
+
One mental model. The runner does the work; Lens watches.
|
|
106
131
|
|
|
107
132
|
---
|
|
108
133
|
|
|
109
|
-
##
|
|
134
|
+
## Theming
|
|
110
135
|
|
|
111
|
-
|
|
136
|
+
Lens inherits from `footprint-explainable-ui`'s theme system. Two built-in presets, or bring your own:
|
|
112
137
|
|
|
113
138
|
```tsx
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
139
|
+
import { coolDark, coolLight } from 'footprint-explainable-ui';
|
|
140
|
+
import { Lens } from 'agentfootprint-lens';
|
|
141
|
+
|
|
142
|
+
<Lens for={agent} theme={isDark ? coolDark : coolLight} />
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
Pass any `ThemeTokens` object. CSS vars work too — handy if your app already flips theme at the `:root` level:
|
|
146
|
+
|
|
147
|
+
```tsx
|
|
148
|
+
<Lens
|
|
149
|
+
for={agent}
|
|
150
|
+
theme={{
|
|
151
|
+
colors: {
|
|
152
|
+
bgPrimary: 'var(--my-bg)',
|
|
153
|
+
textPrimary: 'var(--my-fg)',
|
|
154
|
+
// …
|
|
155
|
+
},
|
|
156
|
+
}}
|
|
117
157
|
/>
|
|
118
158
|
```
|
|
119
159
|
|
|
120
|
-
|
|
121
|
-
- `runtimeSnapshot` (any | null) — raw output of `agent.getSnapshot()`. Null renders an empty state.
|
|
122
|
-
- `timeline` (`AgentTimeline`) — pre-parsed timeline, overrides `runtimeSnapshot`. Useful for sharing across multiple Lens instances.
|
|
123
|
-
- `systemPrompt` (string) — override the system-prompt preview in MessagesPanel. Auto-derived from snapshot otherwise.
|
|
124
|
-
- `onToolCallClick` ((`AgentToolInvocation`) => void) — fires when any tool-call card is clicked.
|
|
160
|
+
---
|
|
125
161
|
|
|
126
|
-
|
|
162
|
+
## Responsive
|
|
127
163
|
|
|
128
|
-
|
|
129
|
-
- `<IterationStrip timeline selectedKey onSelect />`
|
|
130
|
-
- `<ToolCallInspector timeline selectedId onSelect />`
|
|
164
|
+
Lens resizes to whatever space you give it. Below ~640px wide it stacks panels vertically (like `<ExplainableShell>` does). Drop it in a splitter, a drawer, or a full-screen tab — no config needed.
|
|
131
165
|
|
|
132
|
-
|
|
166
|
+
---
|
|
133
167
|
|
|
134
|
-
|
|
168
|
+
## Escape hatches
|
|
135
169
|
|
|
136
|
-
|
|
170
|
+
If you want to manage the timeline yourself (custom ingestion, recording to a file, replaying a stored run), the explicit path is still available:
|
|
137
171
|
|
|
138
|
-
|
|
172
|
+
```tsx
|
|
173
|
+
import { Lens, useLiveTimeline } from 'agentfootprint-lens';
|
|
174
|
+
|
|
175
|
+
const lens = useLiveTimeline();
|
|
176
|
+
|
|
177
|
+
// You control ingestion
|
|
178
|
+
for (const event of storedEvents) lens.ingest(event);
|
|
179
|
+
|
|
180
|
+
<Lens
|
|
181
|
+
timeline={lens.timeline}
|
|
182
|
+
runtimeSnapshot={storedSnapshot}
|
|
183
|
+
/>
|
|
184
|
+
```
|
|
139
185
|
|
|
140
186
|
---
|
|
141
187
|
|
|
142
|
-
##
|
|
188
|
+
## Recorder pattern (power users)
|
|
143
189
|
|
|
144
|
-
|
|
190
|
+
For advanced observability — multiple exporters, buffering, filtering before dispatch — agentfootprint's recorder system is still there:
|
|
145
191
|
|
|
146
192
|
```ts
|
|
147
|
-
|
|
148
|
-
turns: AgentTurn[]; // one per agent.run() call
|
|
149
|
-
messages: AgentMessage[]; // full flat conversation
|
|
150
|
-
tools: AgentToolInvocation[]; // every tool call, across turns
|
|
151
|
-
finalDecision: Record<string, unknown>;
|
|
152
|
-
rawSnapshot: unknown; // escape hatch
|
|
153
|
-
}
|
|
193
|
+
import { createStreamEventRecorder } from 'agentfootprint';
|
|
154
194
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
iterations: AgentIteration[];
|
|
159
|
-
finalContent: string;
|
|
160
|
-
totalInputTokens: number;
|
|
161
|
-
totalOutputTokens: number;
|
|
162
|
-
totalDurationMs: number;
|
|
163
|
-
}
|
|
195
|
+
const myRec = createStreamEventRecorder(myHandler, 'my-telemetry');
|
|
196
|
+
const agent = Agent.create(...).recorder(myRec).build();
|
|
197
|
+
```
|
|
164
198
|
|
|
165
|
-
|
|
166
|
-
index: number;
|
|
167
|
-
model?: string;
|
|
168
|
-
inputTokens?: number;
|
|
169
|
-
outputTokens?: number;
|
|
170
|
-
durationMs?: number;
|
|
171
|
-
stopReason?: string;
|
|
172
|
-
assistantContent: string;
|
|
173
|
-
toolCalls: AgentToolInvocation[];
|
|
174
|
-
decisionAtStart: Record<string, unknown>;
|
|
175
|
-
matchedInstructions?: string[];
|
|
176
|
-
visibleTools: string[]; // tool names visible to the LLM that iter
|
|
177
|
-
}
|
|
199
|
+
`<Lens for={...}>` is just sugar over this internally — the recorder you'd write for Datadog is the same shape Lens uses.
|
|
178
200
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
201
|
+
---
|
|
202
|
+
|
|
203
|
+
## API reference
|
|
204
|
+
|
|
205
|
+
### `useLens(factory)`
|
|
206
|
+
|
|
207
|
+
Memoizes a runner across renders. Call `factory` exactly once on mount; reuses the same instance forever. Works for any agentfootprint runner — `Agent`, `LLMCall`, `RAG`, `Swarm`, `FlowChart`, `Parallel`, `Conditional`.
|
|
208
|
+
|
|
209
|
+
```ts
|
|
210
|
+
const agent = useLens(() => Agent.create(...).build());
|
|
211
|
+
const caller = useLens(() => LLMCall.create(...).build());
|
|
212
|
+
const rag = useLens(() => RAG.create(...).build());
|
|
190
213
|
```
|
|
191
214
|
|
|
192
|
-
|
|
215
|
+
### `<Lens for={runner} />`
|
|
193
216
|
|
|
194
|
-
|
|
217
|
+
The one-prop integration. Subscribes to the runner's events, watches its snapshot, renders both tabs.
|
|
195
218
|
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
219
|
+
| Prop | Type | Description |
|
|
220
|
+
|---|---|---|
|
|
221
|
+
| `for` | `Runner` (any agentfootprint runner) | The agent / caller / swarm / etc. to watch. |
|
|
222
|
+
| `theme` | `ThemeTokens?` | Optional — defaults to `coolDark`. |
|
|
223
|
+
| `appName` | `string?` | Optional brand label in the tab strip. |
|
|
200
224
|
|
|
201
|
-
###
|
|
202
|
-
- **Prompt Composer** — assembled system prompt per iteration with diff-across-iterations highlighting (base + skill body + instruction injections + message window + tool list).
|
|
203
|
-
- **Decision Scope Ribbon** — horizontal pill timeline of decision scope fields; arrows back to the tool call that wrote each via `decisionUpdate`.
|
|
225
|
+
### `runner.observe(handler)`
|
|
204
226
|
|
|
205
|
-
|
|
206
|
-
- **Cost & Latency Attribution** — token burn curve, per-iter/per-tool breakdown, pluggable price table.
|
|
207
|
-
- **Skill Dock** — loaded skills, activation state, tools-per-skill.
|
|
227
|
+
Subscribe to live events. Returns `() => void` (unsubscribe).
|
|
208
228
|
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
229
|
+
```ts
|
|
230
|
+
const stop = agent.observe((event) => { /* ... */ });
|
|
231
|
+
// later:
|
|
232
|
+
stop();
|
|
233
|
+
```
|
|
234
|
+
|
|
235
|
+
### `runner.getSnapshot()`, `runner.getNarrativeEntries()`, `runner.getSpec()`
|
|
236
|
+
|
|
237
|
+
The standard agentfootprint introspection methods. `<Lens for={...}>` reads these automatically. You only call them yourself if you're building a custom UI.
|
|
238
|
+
|
|
239
|
+
### `useLiveTimeline()` (escape hatch)
|
|
240
|
+
|
|
241
|
+
Returns `{ timeline, ingest, startTurn, reset, builder }`. Use when you want to feed Lens from a non-runner source (replayed logs, server-sent events, etc.).
|
|
213
242
|
|
|
214
243
|
---
|
|
215
244
|
|
|
216
|
-
##
|
|
245
|
+
## Why this design
|
|
246
|
+
|
|
247
|
+
**The runner is the single source of truth.** Agents fire events as they work. Lens subscribes to those events. Telemetry exporters subscribe to those events. CLI loggers subscribe to those events. Nobody owns the runner; everyone can watch it.
|
|
248
|
+
|
|
249
|
+
This is the observer pattern, applied consistently across every agentfootprint runner. The outcome:
|
|
217
250
|
|
|
218
|
-
- **
|
|
219
|
-
- **
|
|
220
|
-
- **
|
|
221
|
-
- **
|
|
251
|
+
- **One line to integrate** — `<Lens for={agent} />`
|
|
252
|
+
- **Zero coupling** — the agent doesn't know Lens exists
|
|
253
|
+
- **Composable** — Lens + your telemetry + your logger all watch the same agent with no conflict
|
|
254
|
+
- **Uniform** — any runner works with any observer
|
|
222
255
|
|
|
223
256
|
---
|
|
224
257
|
|
|
225
258
|
## License
|
|
226
259
|
|
|
227
|
-
MIT
|
|
260
|
+
MIT
|