agentfootprint-lens 0.46.0 → 0.47.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.
Files changed (36) hide show
  1. package/README.md +152 -16
  2. package/dist/{Lens-Bdz-JzVm.d.ts → Lens-9wZ84e70.d.ts} +2 -2
  3. package/dist/{Lens-6elRg27j.d.cts → Lens-Ccs8UUDQ.d.cts} +2 -2
  4. package/dist/{chunk-SJEKKUTI.js → chunk-4PENVNUY.js} +520 -2
  5. package/dist/chunk-4PENVNUY.js.map +1 -0
  6. package/dist/{chunk-A4EIPCLY.js → chunk-UO6IZSBY.js} +1176 -279
  7. package/dist/chunk-UO6IZSBY.js.map +1 -0
  8. package/dist/{chunk-WN2RJKEH.js → chunk-XFNFYUPC.js} +2 -2
  9. package/dist/core.cjs +519 -2
  10. package/dist/core.cjs.map +1 -1
  11. package/dist/core.d.cts +447 -8
  12. package/dist/core.d.ts +447 -8
  13. package/dist/core.js +18 -4
  14. package/dist/index.cjs +2145 -735
  15. package/dist/index.cjs.map +1 -1
  16. package/dist/index.d.cts +139 -10
  17. package/dist/index.d.ts +139 -10
  18. package/dist/index.js +22 -4
  19. package/dist/index.js.map +1 -1
  20. package/dist/{lensStops-hQ10t3Lt.d.cts → lensStops-DfNxsaH0.d.cts} +1 -1
  21. package/dist/{lensStops-DAc3GB8G.d.ts → lensStops-Di41Rg3f.d.ts} +1 -1
  22. package/dist/{resolveNavigation-o4E4cM_1.d.ts → resolveNavigation-Ckr2nXHY.d.cts} +9 -0
  23. package/dist/{resolveNavigation-o4E4cM_1.d.cts → resolveNavigation-Ckr2nXHY.d.ts} +9 -0
  24. package/dist/{selectSkillFrameContext-IPsw4Pbj.d.ts → selectSkillFrameContext-DEk_qpXr.d.ts} +1 -1
  25. package/dist/{selectSkillFrameContext-DJkvrggG.d.cts → selectSkillFrameContext-DjZDveZw.d.cts} +1 -1
  26. package/dist/skillgraph.d.cts +4 -4
  27. package/dist/skillgraph.d.ts +4 -4
  28. package/dist/why.cjs +1600 -206
  29. package/dist/why.cjs.map +1 -1
  30. package/dist/why.d.cts +5 -5
  31. package/dist/why.d.ts +5 -5
  32. package/dist/why.js +2 -2
  33. package/package.json +3 -3
  34. package/dist/chunk-A4EIPCLY.js.map +0 -1
  35. package/dist/chunk-SJEKKUTI.js.map +0 -1
  36. /package/dist/{chunk-WN2RJKEH.js.map → chunk-XFNFYUPC.js.map} +0 -0
package/README.md CHANGED
@@ -891,26 +891,28 @@ if (snapshot) {
891
891
  }
892
892
  ```
893
893
 
894
- A **stored** recording needs one narrowing today, and the reason is honest on
895
- both sides: the lens types `Recording.snapshot.commitLog` as `readonly
896
- unknown[]` — a recording arrives as parsed JSON, and the lens will not claim
897
- that what came back off disk is a `CommitBundle` — while `timeTravel` takes
898
- `TimeTravelSource`, whose `commitLog` is `readonly CommitBundle[]`. So a
899
- replayed run has to say so at the seam:
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
- import type { TimeTravelSource } from 'footprintjs/trace';
903
-
904
- const stored = recording.snapshot as unknown as TimeTravelSource;
905
- const tt = timeTravel(stored, { strategy: lensStopsStrategy(positions) });
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
- That cast disappears — with no change here — the day footprintjs widens
909
- `TimeTravelSource.commitLog` to accept an unvalidated `readonly unknown[]` and
910
- does the per-bundle narrowing where it folds, which is the only place that can
911
- honestly do it. It is reported upstream as a port gap. **Movement never needs
912
- this**: `openLensCursor` reads the stops and no snapshot at all, which is why
913
- the lens's own cursor is unaffected either way.
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
 
@@ -935,6 +937,130 @@ not answer, since it takes an exact stop.
935
937
 
936
938
  ---
937
939
 
940
+ ## The Served tab
941
+
942
+ **At every LLM call, exactly what the model was served — provable from the log.**
943
+ The Why Lens's right rail has a second tab, **Served**, mounted for every run.
944
+ Stand on an LLM turn and it shows the request that call went out with: the system
945
+ prompt (piece by piece), the messages as sent, the tools as sent, the dials, the
946
+ cache breakpoints — and beside every field, what the record can PROVE about it.
947
+
948
+ ### Why
949
+
950
+ The request a provider receives is assembled from committed pieces and is itself
951
+ never committed — the `call-llm` bundle holds the answer, not the ask. Every
952
+ earlier "what did the model see?" panel in this family read an event
953
+ (`llm_start`) that only some runs record, or rebuilt the prompt from the pieces
954
+ with no way to know whether the rebuild matched what went out.
955
+
956
+ agentfootprint 9.88.0 closes that with two halves and one law:
957
+
958
+ ```
959
+ hash(servedAt(k)) === receiptAt(k).hash
960
+ ```
961
+
962
+ `servedAt(k)` REBUILDS the request for epoch `k` (one epoch = one LLM call) from
963
+ the committed pieces. `receiptAt(k)` reads the **receipt** — the hashes-only
964
+ record the call itself committed at the stop (system hash and pieces, one hash
965
+ per message, tool names and schema hashes, the sampling dials, the cache
966
+ verdict). When the two agree the record is complete; when they disagree,
967
+ something reached the model that the run never wrote down. The tab renders both
968
+ halves at the lens's one cursor and checks them with the library's own
969
+ `receiptHash` over `messageDigestInput` (a message) and `toolDigestInput` (a
970
+ tool schema).
971
+
972
+ ### What each badge means
973
+
974
+ | badge | exactly this |
975
+ |---|---|
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. |
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). |
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. |
980
+
981
+ ### The laws the tab keeps
982
+
983
+ - **Omit, never deny.** A field a gap covers is never rendered as "empty" or
984
+ "none". The gap IS the empty state: its `why` sentence, verbatim from
985
+ `SERVED_GAPS`, is printed beside the field it covers, and the section is
986
+ marked with the gap's name.
987
+ - **The tab writes no claim sentences of its own.** Every explanatory sentence
988
+ on it is the library's — `SERVED_GAPS[k].why`, `UNGAPPED_FIELDS[k]`,
989
+ `RECEIPT_BOUNDARY` — or is computed data: a status, a count, a hash, a diff.
990
+ The strings the tab owns are labels (`SERVED_LABELS`), and
991
+ `test/served/no-own-claims.test.ts` walks every literal in the tab's source to
992
+ keep it that way; its header states what such a walk cannot catch.
993
+ - **Authority omissions come from the fold.** A receipt never names what a
994
+ caller's ROLE was not allowed to see (the library's first law). The tab's
995
+ audience is an operator, so it MAY show the skill ids hidden from the model —
996
+ but it reads them from the committed state at the stop through footprintjs's
997
+ `stateAt`, labelled **hidden from the model**, never from the receipt.
998
+ - **One cursor.** The tab takes the lens's position as props and holds none of
999
+ its own. On an llm-turn stop it shows that epoch. On any other stop it shows
1000
+ the nearest PRECEDING call with the note *as of call k — this stop is between
1001
+ calls*. Clicking the call's id asks the lens to move the one cursor there.
1002
+ A resumed leg's first call has no previous epoch IN THIS RECORDING; the tab
1003
+ prints that epoch's number with *Not on record*, never "no previous epoch".
1004
+ - **A record never takes the Lens down.** A half-shaped receipt reads
1005
+ *Damaged*; a commit-log row the fold cannot read is data in the FOLD
1006
+ section; a render throw is caught by a boundary around the tab and printed
1007
+ as the Damaged badge plus the message. The rest of the Lens — its one
1008
+ cursor, its *What happened* tab — stays mounted.
1009
+
1010
+ ### The sections
1011
+
1012
+ **SERVED** — system prompt with per-piece boundaries (slot · source) and a
1013
+ word-level diff against the previous epoch when the text changed; messages as
1014
+ sent (role, tool-call ids, request-only lines marked with the mechanism that
1015
+ composed them); tools as sent (names, expandable schemas, the forced tool
1016
+ marked, `withheld` shown as the library states it). **BASIS** — epoch, call id,
1017
+ commit index, model, provider, params (only the dials the receipt carries — an
1018
+ absent dial is not rendered as a default), cache (transform, breakpoints
1019
+ applied). **FOLD** — `iteration`, `currentSkillId`, `stepPointer`, engagement,
1020
+ active injections, hidden skill ids, read from the fold at the stop; a row the
1021
+ fold could not read is printed there as data (skipped indices, or the fold's
1022
+ error), beside a Damaged badge.
1023
+ **OMISSIONS** — attention drops from the receipt when present, else the
1024
+ library's `UNGAPPED_FIELDS` sentence for the field. **GAPS** — every gap on the
1025
+ view: its kind, the fields it covers, its sentence verbatim, and its `cause`
1026
+ when the library established one (`receipt-shape-rejected` styled as damage).
1027
+ **SINCE PREVIOUS** — messages entered/left, tools added/removed, schemas whose
1028
+ receipt hashes changed, and the system-text word diff.
1029
+
1030
+ ### Headless
1031
+
1032
+ ```ts
1033
+ import {
1034
+ servedRowAt, servedRowForEpoch, verify, sincePrevious, foldFactsAt,
1035
+ } from 'agentfootprint-lens/core';
1036
+
1037
+ const snapshot = runner.getLastSnapshot();
1038
+ const row = servedRowAt(snapshot, { runtimeStageId: 'call-llm#18', commitIdx: 15 });
1039
+ if (row) {
1040
+ row.epoch; // 1
1041
+ row.betweenCalls; // false — on the call itself
1042
+ const checks = verify(row.view, row.receipt, row.receipt?.basis.runId ?? '');
1043
+ checks.system.status; // 'verified'
1044
+ checks.messages.map((c) => c.status); // ['verified']
1045
+ row.view.gaps.map((g) => g.why); // the library's sentences
1046
+ const prev = row.previousEpoch !== undefined
1047
+ ? servedRowForEpoch(snapshot, row.previousEpoch) : undefined;
1048
+ if (prev) sincePrevious(row, prev).tools.added; // ['charge']
1049
+ foldFactsAt(snapshot, { runtimeStageId: 'call-llm#18', commitIdx: 15 }).hiddenSkillIds;
1050
+ }
1051
+ ```
1052
+
1053
+ Every function is pure and every return is frozen. `<ServedTab runner
1054
+ cursorRuntimeStageId commitIdx onJumpTo>` is exported for shells that hold the
1055
+ one cursor themselves.
1056
+
1057
+ **A recording made before agentfootprint 9.88** still renders: the rebuild works
1058
+ on every epoch, no receipt was minted, and the tab says so with the library's
1059
+ `no-receipt-on-chart` gap and `cause: 'no-receipt-committed'` — every row
1060
+ **Reconstructed**, never a fabricated **Verified**.
1061
+
1062
+ ---
1063
+
938
1064
  ## Rendering your own detail pane
939
1065
 
940
1066
  `slots.detail` replaces the CONTENT of the shipped right column. The column
@@ -1154,6 +1280,16 @@ exact `formatSlice` text the LLM tool returns. Honest absence stays honest:
1154
1280
  "never written — initial state / args / a closure", and reads-off runs say
1155
1281
  "unknowable, not absent".
1156
1282
 
1283
+ ### `<ServedTab>` — what the model was served at the cursor's call
1284
+
1285
+ `<ServedTab runner cursorRuntimeStageId commitIdx onJumpTo?>`. The Why Lens
1286
+ mounts it as the right rail's second tab; exported for consumer-built shells.
1287
+ Renders `servedAt(k)` and `receiptAt(k)` (agentfootprint 9.88.0) at the one
1288
+ cursor with a **Verified / Reconstructed / Damaged / Not on record** badge per
1289
+ field, the library's gap sentences verbatim, the fold's hidden skill ids, and a
1290
+ since-previous diff. Headless: `servedRowAt` · `verify` · `sincePrevious` ·
1291
+ `foldFactsAt` in `/core`. See "The Served tab" above.
1292
+
1157
1293
  ### `<BugReportButton>` — report a bug with the run attached, consent first
1158
1294
 
1159
1295
  A small button for a debug UI. The dialog it opens shows every selectable unit
@@ -3,8 +3,8 @@ import { ToolChoiceCall, ToolChoiceSummary } from 'agentfootprint/observe';
3
3
  import * as agentfootprint from 'agentfootprint';
4
4
  import { CommentaryTemplates } from 'agentfootprint';
5
5
  import React__default, { Ref } from 'react';
6
- import { d as NavigationResult, C as CursorPosition, L as LensRecorder } from './resolveNavigation-o4E4cM_1.js';
7
- import { C as ChartGroupHighlight, a as LensCursorPort, H as Humanizer } from './lensStops-DAc3GB8G.js';
6
+ import { d as NavigationResult, C as CursorPosition, L as LensRecorder } from './resolveNavigation-Ckr2nXHY.js';
7
+ import { C as ChartGroupHighlight, a as LensCursorPort, H as Humanizer } from './lensStops-Di41Rg3f.js';
8
8
  import { NodeTypes } from '@xyflow/react';
9
9
  import { TraceGraph, TraceFlowLayout, RuntimeOverlay } from 'footprint-explainable-ui/flowchart';
10
10
 
@@ -3,8 +3,8 @@ import { ToolChoiceCall, ToolChoiceSummary } from 'agentfootprint/observe';
3
3
  import * as agentfootprint from 'agentfootprint';
4
4
  import { CommentaryTemplates } from 'agentfootprint';
5
5
  import React__default, { Ref } from 'react';
6
- import { d as NavigationResult, C as CursorPosition, L as LensRecorder } from './resolveNavigation-o4E4cM_1.cjs';
7
- import { C as ChartGroupHighlight, a as LensCursorPort, H as Humanizer } from './lensStops-hQ10t3Lt.cjs';
6
+ import { d as NavigationResult, C as CursorPosition, L as LensRecorder } from './resolveNavigation-Ckr2nXHY.cjs';
7
+ import { C as ChartGroupHighlight, a as LensCursorPort, H as Humanizer } from './lensStops-DfNxsaH0.cjs';
8
8
  import { NodeTypes } from '@xyflow/react';
9
9
  import { TraceGraph, TraceFlowLayout, RuntimeOverlay } from 'footprint-explainable-ui/flowchart';
10
10