agentfootprint-lens 0.47.0 → 0.48.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 +158 -19
- package/dist/{Lens-Ccs8UUDQ.d.cts → Lens-_IjGLEGt.d.cts} +15 -1
- package/dist/{Lens-9wZ84e70.d.ts → Lens-_hExvOXu.d.ts} +15 -1
- package/dist/{chunk-A3NWHAJ2.js → chunk-DQHZE7V7.js} +2 -2
- package/dist/{chunk-ZNH33A7V.js → chunk-FCXDXUZG.js} +700 -263
- package/dist/chunk-FCXDXUZG.js.map +1 -0
- package/dist/{chunk-E5ZRMHYY.js → chunk-FZHRQNRS.js} +403 -19
- package/dist/chunk-FZHRQNRS.js.map +1 -0
- package/dist/core.cjs +412 -17
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +45 -17
- package/dist/core.d.ts +45 -17
- package/dist/core.js +28 -2
- package/dist/index.cjs +1520 -695
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +146 -7
- package/dist/index.d.ts +146 -7
- package/dist/index.js +39 -3
- package/dist/index.js.map +1 -1
- package/dist/{lensStops-DfNxsaH0.d.cts → tagAxis-B598GxMJ.d.cts} +292 -3
- package/dist/{lensStops-Di41Rg3f.d.ts → tagAxis-C__DMHiD.d.ts} +292 -3
- package/dist/why.cjs +1028 -219
- package/dist/why.cjs.map +1 -1
- package/dist/why.d.cts +4 -4
- package/dist/why.d.ts +4 -4
- package/dist/why.js +22 -4
- package/dist/why.js.map +1 -1
- package/package.json +3 -3
- package/dist/chunk-E5ZRMHYY.js.map +0 -1
- package/dist/chunk-ZNH33A7V.js.map +0 -1
- /package/dist/{chunk-A3NWHAJ2.js.map → chunk-DQHZE7V7.js.map} +0 -0
package/README.md
CHANGED
|
@@ -891,26 +891,28 @@ if (snapshot) {
|
|
|
891
891
|
}
|
|
892
892
|
```
|
|
893
893
|
|
|
894
|
-
A **stored** recording
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
894
|
+
A **stored** recording meets the port with no cast either, since footprintjs
|
|
895
|
+
9.18. The lens types `Recording.snapshot.commitLog` as `readonly unknown[]` —
|
|
896
|
+
a recording arrives as parsed JSON, and the lens will not claim that what came
|
|
897
|
+
back off disk is a `CommitBundle` — and `TimeTravelSource.commitLog` accepts
|
|
898
|
+
exactly that, narrowing per row where it folds, which is the only place that
|
|
899
|
+
can honestly do it. A row that is not a bundle is reported by index in
|
|
900
|
+
`stateAt().skipped`, never crashed on:
|
|
900
901
|
|
|
901
902
|
```ts
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
903
|
+
if (recording.snapshot) {
|
|
904
|
+
const tt = timeTravel(recording.snapshot, { strategy: lensStopsStrategy(positions) });
|
|
905
|
+
tt.jumpTo(7);
|
|
906
|
+
tt.stateAt().skipped; // undefined, or the rows the fold could not read
|
|
907
|
+
}
|
|
906
908
|
```
|
|
907
909
|
|
|
908
|
-
|
|
909
|
-
`
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
910
|
+
(Under footprintjs 9.17 — still the peer floor — the same call needs
|
|
911
|
+
`recording.snapshot as unknown as TimeTravelSource`, and a bad row throws
|
|
912
|
+
inside the fold; a types-only difference the lens's `foldFactsAt` reports as
|
|
913
|
+
`foldError`.) **Movement never needs a snapshot at all**: `openLensCursor`
|
|
914
|
+
reads the stops and nothing else, which is why the lens's own cursor is
|
|
915
|
+
unaffected either way.
|
|
914
916
|
|
|
915
917
|
### Two axes, and drilling
|
|
916
918
|
|
|
@@ -964,14 +966,15 @@ per message, tool names and schema hashes, the sampling dials, the cache
|
|
|
964
966
|
verdict). When the two agree the record is complete; when they disagree,
|
|
965
967
|
something reached the model that the run never wrote down. The tab renders both
|
|
966
968
|
halves at the lens's one cursor and checks them with the library's own
|
|
967
|
-
`receiptHash`
|
|
969
|
+
`receiptHash` over `messageDigestInput` (a message) and `toolDigestInput` (a
|
|
970
|
+
tool schema).
|
|
968
971
|
|
|
969
972
|
### What each badge means
|
|
970
973
|
|
|
971
974
|
| badge | exactly this |
|
|
972
975
|
|---|---|
|
|
973
|
-
| **Verified** | the receipt's hash for the field EQUALS the hash of what the rebuild produced — run-salted SHA-256, computed with agentfootprint's exported `receiptHash` (over `messageDigestInput` for a message). Nothing softer earns it. |
|
|
974
|
-
| **Reconstructed** | rebuilt from the log, but nothing to check it against: this epoch committed no receipt, the receipt
|
|
976
|
+
| **Verified** | the receipt's hash for the field EQUALS the hash of what the rebuild produced — run-salted SHA-256, computed with agentfootprint's exported `receiptHash` (over `messageDigestInput` for a message, `toolDigestInput` for a tool schema). System text, every piece, every message and every tool schema can read it. Nothing softer earns it. |
|
|
977
|
+
| **Reconstructed** | rebuilt from the log, but nothing to check it against: this epoch committed no receipt, the receipt carries no hash for this KIND of row, or a gap on the view covers the field (a declared hole, printed beside it). Tool NAMES are always here — they carry no hash. Tool SCHEMAS are here only under an agentfootprint peer older than 9.89.0, which does not export `toolDigestInput`; the lens detects the export at call time rather than copy the serializer. |
|
|
975
978
|
| **Damaged** | the record contradicts itself: the receipt's hash disagrees with the rebuild and no gap excuses it, the rebuild produced a row the receipt never witnessed (no gap says a rebuild may be LONG — the receipt is the witness in both directions), or something under the receipt key was refused as not-a-receipt (`cause: 'receipt-shape-rejected'` — the library refuses a value with no `basis.epoch`, the lens refuses one missing the containers it reads). |
|
|
976
979
|
| **Not on record** | the field is absent on both sides — a receipt-only field (model, provider, params, cache) on an epoch that minted no receipt. |
|
|
977
980
|
|
|
@@ -1058,6 +1061,121 @@ on every epoch, no receipt was minted, and the tab says so with the library's
|
|
|
1058
1061
|
|
|
1059
1062
|
---
|
|
1060
1063
|
|
|
1064
|
+
## Bookmarks, and scrubbing by declared tag
|
|
1065
|
+
|
|
1066
|
+
**The reader's marks, kept beside the recording — and the author's tags, as a
|
|
1067
|
+
ruler.** Since 0.48.0 the Why Lens's right rail has a third tab, **Bookmarks**,
|
|
1068
|
+
and the grouped ruler carries a **Tags** strip.
|
|
1069
|
+
|
|
1070
|
+
### Why
|
|
1071
|
+
|
|
1072
|
+
footprintjs's design page for declared tags names three marks that end in the
|
|
1073
|
+
same operation ("keep these stops, fold the rest into them") and differ in who
|
|
1074
|
+
puts the mark and when:
|
|
1075
|
+
|
|
1076
|
+
| Mark | Who, when | Lives | In the lens |
|
|
1077
|
+
|---|---|---|---|
|
|
1078
|
+
| **Declared tag** | the author, at build time | the chart's structure → stamped into the commit bundle (`bundle.tags`) | the **Tags** strip: legend and picker |
|
|
1079
|
+
| **Derived tag** | the reader, at read time, from the fold | computed by a strategy; never stored | the lens's own grouping (milestones, banded by iteration) — the default axis |
|
|
1080
|
+
| **Bookmark** | the reader, at read time, by choice | on the cursor (`mark` / `marks` / `jumpToMark`); persisted beside a recording, never in it | the **Bookmarks** tab |
|
|
1081
|
+
|
|
1082
|
+
Only the first two had a place in the lens. footprintjs holds a bookmark on the
|
|
1083
|
+
cursor and, by its own law, nowhere else — the log and the snapshot are the
|
|
1084
|
+
run's record, not the reader's notes — so a bookmark vanished when the page
|
|
1085
|
+
closed. And a run's own tags (agentfootprint 9.90 declares every milestone as
|
|
1086
|
+
one) were in the recording with nothing to read them.
|
|
1087
|
+
|
|
1088
|
+
### Bookmarks
|
|
1089
|
+
|
|
1090
|
+
Stand on a stop and press **Bookmark this stop**. The library names the mark
|
|
1091
|
+
(its own `mark()`, seated on the lens's one cursor); the lens writes it to a
|
|
1092
|
+
**sidecar** — a small document keyed to the run, in a store you choose. Click a
|
|
1093
|
+
bookmark to jump: the port's `jumpToMark`, through the same funnel every other
|
|
1094
|
+
mover uses, so the chart, the timeline and the Served tab all follow. Add a
|
|
1095
|
+
note inline; remove with one click.
|
|
1096
|
+
|
|
1097
|
+
```tsx
|
|
1098
|
+
import { Lens } from 'agentfootprint-lens';
|
|
1099
|
+
import { memoryBookmarkStore } from 'agentfootprint-lens/core';
|
|
1100
|
+
|
|
1101
|
+
<Lens recorder={recorder} runner={agent} granularity="group" /> // localStorage, by default
|
|
1102
|
+
<Lens recorder={recorder} runner={agent} bookmarkStore={memoryBookmarkStore()} /> // this session only
|
|
1103
|
+
```
|
|
1104
|
+
|
|
1105
|
+
The laws the tab keeps:
|
|
1106
|
+
|
|
1107
|
+
- **One cursor.** The tab holds a list, never a position. A jump moves the lens
|
|
1108
|
+
cursor (`onStepChange` fires); a miss never moves.
|
|
1109
|
+
- **Never in the record.** Nothing is written into the snapshot or the log.
|
|
1110
|
+
- **Omit, never deny.** A bookmark whose stop this recording does not hold —
|
|
1111
|
+
a truncated recording, a sidecar filed under another run — is shown greyed as
|
|
1112
|
+
*not in this recording*, never dropped.
|
|
1113
|
+
- **Persistence is a label.** A browser that refuses storage (private mode, a
|
|
1114
|
+
quota, a server render) makes the tab say *bookmarks not saved*; a snapshot
|
|
1115
|
+
with no `runId` says *no run id*. Nothing throws.
|
|
1116
|
+
- **No claim sentences.** Every string the tab prints is a label
|
|
1117
|
+
(`BOOKMARK_LABELS`) or computed data; `test/served/no-own-claims.test.ts`
|
|
1118
|
+
walks the file.
|
|
1119
|
+
|
|
1120
|
+
The root bookends (*Run · start* / *Run · end*) are not bookmarkable — Home and
|
|
1121
|
+
End reach them on every axis, and they share one synthetic address.
|
|
1122
|
+
|
|
1123
|
+
Headless, the sidecar is four functions in `/core`: `bookmarkKey(snapshot)`,
|
|
1124
|
+
`toSidecar(key, bookmarks)`, `fromSidecar(json, snapshot)` (reports
|
|
1125
|
+
`orphaned`), `bookmarksToMarks(bookmarks, positions)` to seed footprintjs's
|
|
1126
|
+
`timeTravel(snapshot, { marks })`; stores are `localStorageBookmarkStore()`,
|
|
1127
|
+
`memoryBookmarkStore()`, `noBookmarkStore()`, or any `BookmarkStore` of your
|
|
1128
|
+
own. See `src/core/bookmarks/README.md`.
|
|
1129
|
+
|
|
1130
|
+
### The Tags strip
|
|
1131
|
+
|
|
1132
|
+
On the grouped ruler, above the chart: one chip per declared tag, labelled with
|
|
1133
|
+
agentfootprint's milestone label when the tag is a milestone (*LLM turn*,
|
|
1134
|
+
*Route*) and its raw name otherwise (*audit*), with the number of bundles this
|
|
1135
|
+
run stamped it on. The list comes from the recording's **structure** — the
|
|
1136
|
+
chart advertises the tags it *can* produce, so the legend is drawn before a
|
|
1137
|
+
stop is reached — and the counts from the **log**. A recording that carried no
|
|
1138
|
+
structure lists only *tags this run hit* and says *no chart in this recording*.
|
|
1139
|
+
A declared tag the run never hit is shown and disabled.
|
|
1140
|
+
|
|
1141
|
+
**Pick a chip and the ruler is rebuilt** through footprintjs 9.21's
|
|
1142
|
+
`tagStops(names)`: the stops whose bundle carries any picked tag, the untagged
|
|
1143
|
+
stages folded into the tagged stop before them, the run's bookends kept. It is
|
|
1144
|
+
the same one cursor on a different list — the cursor **keeps its commit**, not
|
|
1145
|
+
its step number: it is re-seated at the stop that holds its commit (nearest
|
|
1146
|
+
preceding when the new axis has none there — `stepForCommitIdx`, the same rule
|
|
1147
|
+
the `granularity` doc gives a host), and the host hears it as an ordinary
|
|
1148
|
+
`onStepChange`. `stateAt` at a picked stop equals the default axis's fold at
|
|
1149
|
+
the same commit (`src/core/tags/tags.test.ts` pins the equivalence). *Clear*
|
|
1150
|
+
restores the lens's own grouping, by the same rule. The strip appears on
|
|
1151
|
+
`granularity="group"` at the root level only: the per-step reading's ruler is
|
|
1152
|
+
every commit, byte for byte, and a drilled level scrubs its own stops.
|
|
1153
|
+
|
|
1154
|
+
A pick scrubs the run's OWN log (`tagStops` reads no other), so a chip is
|
|
1155
|
+
pickable only when the tag was hit on the root log; a tag hit only inside
|
|
1156
|
+
mounted subflows (the grouped turn's *LLM turn*, the context slots) is shown
|
|
1157
|
+
disabled with *in subflows* — real, said, and not a stop on this axis. Counts
|
|
1158
|
+
walk each mount's log once (`subflowResults` is dual-keyed; only the `#n`
|
|
1159
|
+
entries are read).
|
|
1160
|
+
|
|
1161
|
+
```ts
|
|
1162
|
+
import { tagLegend, tagAxisPositions, scrubAxisFor } from 'agentfootprint-lens/core';
|
|
1163
|
+
|
|
1164
|
+
const legend = tagLegend(runner.getSpec().buildTimeStructure, runner.getLastSnapshot());
|
|
1165
|
+
legend.source; // 'structure' | 'log'
|
|
1166
|
+
legend.entries.map((e) => [e.label, e.hits]); // [['Iteration', 2], ['LLM turn', 2], ['audit', 0], …]
|
|
1167
|
+
|
|
1168
|
+
const axis = tagAxisPositions(snapshot, ['milestone:llm-turn'], scrubAxisFor(recorder, 'group'));
|
|
1169
|
+
axis?.map((p) => p.label); // ['Run · start', 'LLM turn 1', 'LLM turn 2', 'Run · end']
|
|
1170
|
+
```
|
|
1171
|
+
|
|
1172
|
+
**Older peers.** `tagStops` (footprintjs 9.21) and `milestoneFromTags`
|
|
1173
|
+
(agentfootprint 9.90) are read off their module namespaces at call time: on a
|
|
1174
|
+
peer without them the legend prints raw names, the strip says *tag axis
|
|
1175
|
+
unavailable*, and nothing else changes. The peer ranges are untouched.
|
|
1176
|
+
|
|
1177
|
+
---
|
|
1178
|
+
|
|
1061
1179
|
## Rendering your own detail pane
|
|
1062
1180
|
|
|
1063
1181
|
`slots.detail` replaces the CONTENT of the shipped right column. The column
|
|
@@ -1287,6 +1405,27 @@ field, the library's gap sentences verbatim, the fold's hidden skill ids, and a
|
|
|
1287
1405
|
since-previous diff. Headless: `servedRowAt` · `verify` · `sincePrevious` ·
|
|
1288
1406
|
`foldFactsAt` in `/core`. See "The Served tab" above.
|
|
1289
1407
|
|
|
1408
|
+
### `<BookmarksTab>` — the reader's marks, riding the one cursor
|
|
1409
|
+
|
|
1410
|
+
`<BookmarksTab bookmarks orphaned persistence positions step port moveTo onAdd
|
|
1411
|
+
onRemove onNote>`. The Why Lens mounts it as the right rail's third tab
|
|
1412
|
+
(`<Lens bookmarkStore?>` picks the store; default `localStorageBookmarkStore()`);
|
|
1413
|
+
exported for consumer-built shells, with `useBookmarkSidecar({ snapshot, store })`
|
|
1414
|
+
as the list and `openLensCursor(positions, { marks })` as the port. A jump is
|
|
1415
|
+
the port's `toMark` through `moveTo`; orphans are greyed and labelled; the
|
|
1416
|
+
store's availability is a label. Headless: `bookmarkKey` · `toSidecar` ·
|
|
1417
|
+
`fromSidecar` · `bookmarksToMarks` and the three stores in `/core`. See
|
|
1418
|
+
"Bookmarks, and scrubbing by declared tag" above.
|
|
1419
|
+
|
|
1420
|
+
### `<TagPicker>` — the declared-tag legend and picker
|
|
1421
|
+
|
|
1422
|
+
`<TagPicker legend picked onPick available>`. The engineer view mounts it above
|
|
1423
|
+
the chart on `granularity="group"` at the root level; `legend` is
|
|
1424
|
+
`tagLegend(structure, snapshot)` (`source: 'structure' | 'log'`), a pick is a
|
|
1425
|
+
list of tag names the lens hands to `tagAxisPositions(snapshot, picked, base)`
|
|
1426
|
+
(footprintjs 9.21's `tagStops`). Picking never moves the cursor; it changes the
|
|
1427
|
+
list the one cursor is a step into.
|
|
1428
|
+
|
|
1290
1429
|
### `<BugReportButton>` — report a bug with the run attached, consent first
|
|
1291
1430
|
|
|
1292
1431
|
A small button for a debug UI. The dialog it opens shows every selectable unit
|
|
@@ -4,7 +4,7 @@ import * as agentfootprint from 'agentfootprint';
|
|
|
4
4
|
import { CommentaryTemplates } from 'agentfootprint';
|
|
5
5
|
import React__default, { Ref } from 'react';
|
|
6
6
|
import { d as NavigationResult, C as CursorPosition, L as LensRecorder } from './resolveNavigation-Ckr2nXHY.cjs';
|
|
7
|
-
import { C as ChartGroupHighlight,
|
|
7
|
+
import { C as ChartGroupHighlight, c as LensCursorPort, H as Humanizer, b as BookmarkStore } from './tagAxis-B598GxMJ.cjs';
|
|
8
8
|
import { NodeTypes } from '@xyflow/react';
|
|
9
9
|
import { TraceGraph, TraceFlowLayout, RuntimeOverlay } from 'footprint-explainable-ui/flowchart';
|
|
10
10
|
|
|
@@ -523,8 +523,22 @@ interface LensProps {
|
|
|
523
523
|
* granularities carries it by `commitIdx`: build the target axis with
|
|
524
524
|
* `scrubAxisFor(recorder, granularity)` and resolve it with
|
|
525
525
|
* `stepForCommitIdx(positions, at.commitIdx)`.
|
|
526
|
+
*
|
|
527
|
+
* (0.48.0) The TAGS strip — the declared-tag legend and the picker that
|
|
528
|
+
* rebuilds the ruler through `tagStops` — appears on `'group'` at the root
|
|
529
|
+
* level only; `'step'` scrubs every commit and carries no strip. A pick
|
|
530
|
+
* re-seats the cursor by the same `stepForCommitIdx` rule.
|
|
526
531
|
*/
|
|
527
532
|
readonly granularity?: 'step' | 'group';
|
|
533
|
+
/**
|
|
534
|
+
* Where the reader's BOOKMARKS persist (0.48.0). A bookmark is the reader's
|
|
535
|
+
* mark — never written into the recording — kept in a sidecar keyed to the
|
|
536
|
+
* run and seeded onto the cursor when the recording is opened. Default:
|
|
537
|
+
* `localStorageBookmarkStore()`; a browser that refuses storage makes the
|
|
538
|
+
* Bookmarks tab say "bookmarks not saved" rather than throw. Pass
|
|
539
|
+
* `memoryBookmarkStore()` for a session-only reading, or your own store.
|
|
540
|
+
*/
|
|
541
|
+
readonly bookmarkStore?: BookmarkStore;
|
|
528
542
|
/**
|
|
529
543
|
* Controlled cursor (omit for uncontrolled). Omit it and the lens is
|
|
530
544
|
* self-driving exactly as it has always been: it holds the position itself,
|
|
@@ -4,7 +4,7 @@ import * as agentfootprint from 'agentfootprint';
|
|
|
4
4
|
import { CommentaryTemplates } from 'agentfootprint';
|
|
5
5
|
import React__default, { Ref } from 'react';
|
|
6
6
|
import { d as NavigationResult, C as CursorPosition, L as LensRecorder } from './resolveNavigation-Ckr2nXHY.js';
|
|
7
|
-
import { C as ChartGroupHighlight,
|
|
7
|
+
import { C as ChartGroupHighlight, c as LensCursorPort, H as Humanizer, b as BookmarkStore } from './tagAxis-C__DMHiD.js';
|
|
8
8
|
import { NodeTypes } from '@xyflow/react';
|
|
9
9
|
import { TraceGraph, TraceFlowLayout, RuntimeOverlay } from 'footprint-explainable-ui/flowchart';
|
|
10
10
|
|
|
@@ -523,8 +523,22 @@ interface LensProps {
|
|
|
523
523
|
* granularities carries it by `commitIdx`: build the target axis with
|
|
524
524
|
* `scrubAxisFor(recorder, granularity)` and resolve it with
|
|
525
525
|
* `stepForCommitIdx(positions, at.commitIdx)`.
|
|
526
|
+
*
|
|
527
|
+
* (0.48.0) The TAGS strip — the declared-tag legend and the picker that
|
|
528
|
+
* rebuilds the ruler through `tagStops` — appears on `'group'` at the root
|
|
529
|
+
* level only; `'step'` scrubs every commit and carries no strip. A pick
|
|
530
|
+
* re-seats the cursor by the same `stepForCommitIdx` rule.
|
|
526
531
|
*/
|
|
527
532
|
readonly granularity?: 'step' | 'group';
|
|
533
|
+
/**
|
|
534
|
+
* Where the reader's BOOKMARKS persist (0.48.0). A bookmark is the reader's
|
|
535
|
+
* mark — never written into the recording — kept in a sidecar keyed to the
|
|
536
|
+
* run and seeded onto the cursor when the recording is opened. Default:
|
|
537
|
+
* `localStorageBookmarkStore()`; a browser that refuses storage makes the
|
|
538
|
+
* Bookmarks tab say "bookmarks not saved" rather than throw. Pass
|
|
539
|
+
* `memoryBookmarkStore()` for a session-only reading, or your own store.
|
|
540
|
+
*/
|
|
541
|
+
readonly bookmarkStore?: BookmarkStore;
|
|
528
542
|
/**
|
|
529
543
|
* Controlled cursor (omit for uncontrolled). Omit it and the lens is
|
|
530
544
|
* self-driving exactly as it has always been: it holds the position itself,
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import {
|
|
2
2
|
formatBytes,
|
|
3
3
|
structureGraphFromRunner
|
|
4
|
-
} from "./chunk-
|
|
4
|
+
} from "./chunk-FZHRQNRS.js";
|
|
5
5
|
|
|
6
6
|
// src/core/buildStepGraphFromSnapshot.ts
|
|
7
7
|
function buildStepGraphFromSnapshot(snapshot) {
|
|
@@ -1189,4 +1189,4 @@ export {
|
|
|
1189
1189
|
decisionSentence,
|
|
1190
1190
|
isConsentDecision
|
|
1191
1191
|
};
|
|
1192
|
-
//# sourceMappingURL=chunk-
|
|
1192
|
+
//# sourceMappingURL=chunk-DQHZE7V7.js.map
|