agentfootprint-lens 0.48.0 → 0.50.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 +80 -7
- package/dist/{Lens-_IjGLEGt.d.cts → Lens-DJJsMfud.d.cts} +54 -3
- package/dist/{Lens-_hExvOXu.d.ts → Lens-okdu_w0H.d.ts} +54 -3
- package/dist/{chunk-FZHRQNRS.js → chunk-AIZIILHJ.js} +311 -10
- package/dist/chunk-AIZIILHJ.js.map +1 -0
- package/dist/{chunk-DQHZE7V7.js → chunk-K4CTXAB7.js} +2 -2
- package/dist/{chunk-FCXDXUZG.js → chunk-TBYKXQSA.js} +1155 -595
- package/dist/chunk-TBYKXQSA.js.map +1 -0
- package/dist/core.cjs +313 -9
- package/dist/core.cjs.map +1 -1
- package/dist/core.d.cts +232 -2
- package/dist/core.d.ts +232 -2
- package/dist/core.js +10 -2
- package/dist/index.cjs +1783 -914
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +118 -5
- package/dist/index.d.ts +118 -5
- package/dist/index.js +19 -3
- package/dist/index.js.map +1 -1
- package/dist/why.cjs +1477 -626
- package/dist/why.cjs.map +1 -1
- package/dist/why.d.cts +2 -2
- package/dist/why.d.ts +2 -2
- package/dist/why.js +2 -2
- package/package.json +2 -2
- package/dist/chunk-FCXDXUZG.js.map +0 -1
- package/dist/chunk-FZHRQNRS.js.map +0 -1
- /package/dist/{chunk-DQHZE7V7.js.map → chunk-K4CTXAB7.js.map} +0 -0
package/README.md
CHANGED
|
@@ -940,7 +940,9 @@ not answer, since it takes an exact stop.
|
|
|
940
940
|
## The Served tab
|
|
941
941
|
|
|
942
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
|
|
943
|
+
The Why Lens's right rail has a second tab, **Served**, mounted for every run —
|
|
944
|
+
including for a host that supplies `slots.detail`, whose own pane is the tab
|
|
945
|
+
beside it (0.50.0).
|
|
944
946
|
Stand on an LLM turn and it shows the request that call went out with: the system
|
|
945
947
|
prompt (piece by piece), the messages as sent, the tools as sent, the dials, the
|
|
946
948
|
cache breakpoints — and beside every field, what the record can PROVE about it.
|
|
@@ -1059,12 +1061,57 @@ on every epoch, no receipt was minted, and the tab says so with the library's
|
|
|
1059
1061
|
`no-receipt-on-chart` gap and `cause: 'no-receipt-committed'` — every row
|
|
1060
1062
|
**Reconstructed**, never a fabricated **Verified**.
|
|
1061
1063
|
|
|
1064
|
+
### The Served graph (0.49.0)
|
|
1065
|
+
|
|
1066
|
+
A list is right for checking one row and wrong for seeing that the system string
|
|
1067
|
+
was assembled from a base plus one skill body plus two injections while three
|
|
1068
|
+
tools were withheld. That is one picture, and since 0.49.0 the Served tab has a
|
|
1069
|
+
**List ⇄ Graph** toggle (the list stays the default) that draws it: **HELD** —
|
|
1070
|
+
what the record holds at this stop, from the fold — then **SERVED** — what
|
|
1071
|
+
crossed into the call, one edge per system piece, message, request-only line and
|
|
1072
|
+
tool, each carrying its badge and, against the previous epoch, whether it
|
|
1073
|
+
*entered*, *left* or is *unchanged* — then **WITHHELD**: held and not sent. The
|
|
1074
|
+
withheld band is the reason to build it. The model never learns what it was
|
|
1075
|
+
denied; the operator should — the withheld tool list, the attention drops, the
|
|
1076
|
+
skills a role hid (read from the fold, since a receipt carries no authority
|
|
1077
|
+
names), a redacted fold, and every gap the view declares, each with the
|
|
1078
|
+
library's own sentence. Same cursor, same row, same badges: a Damaged row draws
|
|
1079
|
+
Damaged here too, and a field a gap covers draws the gap rather than an empty
|
|
1080
|
+
node.
|
|
1081
|
+
|
|
1082
|
+
| graph | what it draws | the question it answers |
|
|
1083
|
+
|---|---|---|
|
|
1084
|
+
| **skill graph** (`<SkillGraph>`) | the skills, their steps and their edges | where a run MAY go, and where it went |
|
|
1085
|
+
| **data graph** (*Where from*) | a value's writers, back through the commit log | where THIS value came from |
|
|
1086
|
+
| **Served graph** (Served tab · Graph) | one call: held · served · withheld | what this ONE CALL was made of, and what we held back |
|
|
1087
|
+
|
|
1088
|
+
```ts
|
|
1089
|
+
import { foldFactsAt, servedGraphAt, servedRowAt, verify } from 'agentfootprint-lens/core';
|
|
1090
|
+
|
|
1091
|
+
const cursor = { runtimeStageId: 'call-llm#18', commitIdx: 15 };
|
|
1092
|
+
const row = servedRowAt(snapshot, cursor)!;
|
|
1093
|
+
const graph = servedGraphAt({
|
|
1094
|
+
row,
|
|
1095
|
+
fold: foldFactsAt(snapshot, cursor),
|
|
1096
|
+
checks: verify(row.view, row.receipt, row.receipt?.basis.runId ?? '', row.receiptCause),
|
|
1097
|
+
});
|
|
1098
|
+
graph.served.map((s) => `${s.slot} ${s.rebuilt}`); // ['system-prompt 3', …]
|
|
1099
|
+
graph.edges.filter((e) => e.state === 'entered').map((e) => e.origin); // ['instructions']
|
|
1100
|
+
graph.withheld.filter((w) => w.from === 'fold').map((w) => w.name); // ['payroll']
|
|
1101
|
+
```
|
|
1102
|
+
|
|
1103
|
+
`<ServedGraph graph>` is exported for shells that build the input themselves.
|
|
1104
|
+
**Deliberately absent:** an edge from a served piece back to the STAGE that
|
|
1105
|
+
wrote it. It needs a commit-log walk keyed by piece text or slot — the part most
|
|
1106
|
+
likely to rot — and the question above is answered without it.
|
|
1107
|
+
|
|
1062
1108
|
---
|
|
1063
1109
|
|
|
1064
1110
|
## Bookmarks, and scrubbing by declared tag
|
|
1065
1111
|
|
|
1066
1112
|
**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
|
|
1113
|
+
ruler.** Since 0.48.0 the Why Lens's right rail has a third tab, **Bookmarks**
|
|
1114
|
+
(a fourth when a host fills `slots.detail`),
|
|
1068
1115
|
and the grouped ruler carries a **Tags** strip.
|
|
1069
1116
|
|
|
1070
1117
|
### Why
|
|
@@ -1178,9 +1225,17 @@ unavailable*, and nothing else changes. The peer ranges are untouched.
|
|
|
1178
1225
|
|
|
1179
1226
|
## Rendering your own detail pane
|
|
1180
1227
|
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1228
|
+
**A slot fills a PANE, never the CHROME around it.** The right rail's tab strip,
|
|
1229
|
+
the one cursor and the collapse pill belong to the library, because chrome is how
|
|
1230
|
+
the library adds capability over time — a consumer that fills a slot must still
|
|
1231
|
+
receive every reading the rail gains later. That is why `slots.detail` is ONE TAB
|
|
1232
|
+
and not a takeover, and why `detailOnly` is an opt-out rather than the default.
|
|
1233
|
+
|
|
1234
|
+
`slots.detail` renders the right column's FIRST tab. The column itself — its
|
|
1235
|
+
width, its border, its collapse pill, its cursor — is unchanged, and so is the
|
|
1236
|
+
strip: your pane is selected by default, with *What happened*, *Served* and
|
|
1237
|
+
*Bookmarks* one click away beside it. Omit `slots` and the built-in timeline
|
|
1238
|
+
renders exactly as before.
|
|
1184
1239
|
|
|
1185
1240
|
```tsx
|
|
1186
1241
|
const Detail: React.FC<LensDetailSlotProps> = ({ step, cursorRuntimeStageId, node, onNavigate }) => (
|
|
@@ -1191,7 +1246,8 @@ const Detail: React.FC<LensDetailSlotProps> = ({ step, cursorRuntimeStageId, nod
|
|
|
1191
1246
|
</div>
|
|
1192
1247
|
);
|
|
1193
1248
|
|
|
1194
|
-
<Lens recorder={recorder} runner={runner} slots={{ detail: Detail }} />
|
|
1249
|
+
<Lens recorder={recorder} runner={runner} slots={{ detail: Detail, detailLabel: 'My bands' }} />
|
|
1250
|
+
// rail: [ My bands* | What happened | Served | Bookmarks ]
|
|
1195
1251
|
```
|
|
1196
1252
|
|
|
1197
1253
|
The slot receives the cursor in every unit (`step`, `totalSteps`,
|
|
@@ -1200,6 +1256,23 @@ and the ones that ran inside its scope (`node`, `relatedNodes`), the `recorder`
|
|
|
1200
1256
|
for anything else, and `onNavigate` — the same funnel every built-in mover uses,
|
|
1201
1257
|
so your pane moves the ONE cursor rather than starting a second one.
|
|
1202
1258
|
|
|
1259
|
+
| key | what it does |
|
|
1260
|
+
|---|---|
|
|
1261
|
+
| `detail` | your pane, as the rail's first tab, selected by default |
|
|
1262
|
+
| `detailLabel` | what that tab is CALLED. Default `"Details"` — the library's generic word for someone else's pane. Name it and the strip reads in your reader's words. |
|
|
1263
|
+
| `detailOnly` | opt OUT of the strip: your pane takes the whole rail, as it did before 0.50.0. For a full-height custom layout that would read as a second chrome under the library's strip. The cost: nothing the rail gains later can reach you. |
|
|
1264
|
+
|
|
1265
|
+
**Switching tabs moves nothing and remounts nothing.** The cursor stays where it
|
|
1266
|
+
was, and your pane is hidden rather than unmounted — a reader who glances at
|
|
1267
|
+
*Served* and comes back finds their bands open and their scroll where they left
|
|
1268
|
+
it.
|
|
1269
|
+
|
|
1270
|
+
**The worked example (0.50.0).** An app supplies `detail` + `detailLabel` and
|
|
1271
|
+
changes nothing else: it sees its own pane first and selected, exactly as before,
|
|
1272
|
+
and it GAINS Served, the Served graph and Bookmarks on the version bump. Before
|
|
1273
|
+
0.50.0 that same app lost the entire rail — the tab strip included — and could
|
|
1274
|
+
not reach any of them.
|
|
1275
|
+
|
|
1203
1276
|
Keep the slots object stable across renders (module scope or `useMemo`), same as
|
|
1204
1277
|
`<TraceExplorerShell slots>`.
|
|
1205
1278
|
|
|
@@ -1333,7 +1406,7 @@ graph in one call. Returns an unsubscribe. Call it once per run.
|
|
|
1333
1406
|
| `step` | `number?` | Controlled cursor. **Omit it and the lens is self-driving, exactly as before.** Pass it and you own the position; out-of-range values are clamped and reported. See [Driving the cursor](#driving-the-cursor-from-your-app). |
|
|
1334
1407
|
| `onStepChange` | `(step, at) => void?` | Fires on every cursor move — required for movement in controlled mode, an observation hook otherwise. `at` carries `runtimeStageId`, `commitIdx`, `label`, `kind` and `clamped`. |
|
|
1335
1408
|
| `navigatorRef` | `Ref<LensNavigator>?` | Move the cursor to a stage **by its `runtimeStageId`**. `ref.current.navigateTo(id)` returns `{ ok: true, step, match, label }` or `{ ok: false, reason, message, nearest? }` — a miss never moves. See [Pointing at a step](#pointing-at-a-step-navigatorref). |
|
|
1336
|
-
| `slots` | `LensSlots?` | Slot overrides. `slots.detail` renders your content
|
|
1409
|
+
| `slots` | `LensSlots?` | Slot overrides. `slots.detail` renders your content as the right rail's FIRST TAB (`slots.detailLabel` names it; `slots.detailOnly` takes the whole rail instead). The strip stays the library's. Omit for the built-in timeline. See [Rendering your own detail pane](#rendering-your-own-detail-pane). |
|
|
1337
1410
|
|
|
1338
1411
|
### `<LensFlow>` — the chart canvas on its own
|
|
1339
1412
|
|
|
@@ -634,16 +634,67 @@ interface LensProps {
|
|
|
634
634
|
*
|
|
635
635
|
* Same idea as `<TraceExplorerShell slots>`: keep the shipped layout and the
|
|
636
636
|
* shipped cursor, replace what one pane renders.
|
|
637
|
+
*
|
|
638
|
+
* THE LAW: **a slot fills a PANE, never the CHROME around it.** The rail's tab
|
|
639
|
+
* strip, the one cursor and the collapse pill belong to the library, because
|
|
640
|
+
* chrome is how the library adds capability over time — a consumer that fills a
|
|
641
|
+
* slot must still receive every capability the rail gains later. That is why
|
|
642
|
+
* `detail` is ONE TAB and not a takeover, and why `detailOnly` is an opt-out
|
|
643
|
+
* rather than the default.
|
|
637
644
|
*/
|
|
638
645
|
interface LensSlots {
|
|
639
646
|
/**
|
|
640
|
-
*
|
|
641
|
-
*
|
|
642
|
-
*
|
|
647
|
+
* Render the right column's FIRST TAB — your own pane, beside the library's.
|
|
648
|
+
*
|
|
649
|
+
* Your component renders inside the shipped column (its width, borders,
|
|
650
|
+
* collapse pill and scroll behaviour are unchanged) and inside the shipped
|
|
651
|
+
* rail: the tab strip stays, your pane becomes the first tab and is selected
|
|
652
|
+
* by default, and "What happened", "Served" and "Bookmarks" stay reachable
|
|
653
|
+
* beside it. Switching tabs never moves the cursor and never unmounts your
|
|
654
|
+
* pane — it keeps its own state.
|
|
655
|
+
*
|
|
656
|
+
* Before 0.50.0 a supplied slot replaced the whole rail, tab strip included,
|
|
657
|
+
* so a slot consumer could not reach anything the rail gained afterwards.
|
|
658
|
+
* That was the defect this shape fixes; `detailOnly` is the way back.
|
|
643
659
|
*
|
|
644
660
|
* Omit it and the built-in timeline renders exactly as before.
|
|
661
|
+
*
|
|
662
|
+
* ```tsx
|
|
663
|
+
* <Lens recorder={recorder} slots={{ detail: MyBands, detailLabel: 'Bands' }} />
|
|
664
|
+
* // rail: [ Bands* | What happened | Served | Bookmarks ]
|
|
665
|
+
* ```
|
|
645
666
|
*/
|
|
646
667
|
readonly detail?: React__default.ComponentType<LensDetailSlotProps>;
|
|
668
|
+
/**
|
|
669
|
+
* What the `detail` tab is CALLED. Default: `"Details"`.
|
|
670
|
+
*
|
|
671
|
+
* WHY it exists: the tab strip names each reading in the reader's own words,
|
|
672
|
+
* and "Details" is the library's generic word for someone else's pane. An app
|
|
673
|
+
* whose pane is a stack of SEO bands should say so — the strip is read left to
|
|
674
|
+
* right and an unnamed tab makes the app's own reading the anonymous one.
|
|
675
|
+
*
|
|
676
|
+
* A plain string, rendered as the tab's text; it is the accessible name too.
|
|
677
|
+
* Ignored when no `detail` slot is supplied.
|
|
678
|
+
*
|
|
679
|
+
* ```tsx
|
|
680
|
+
* slots={{ detail: MyBands, detailLabel: 'SEO bands' }}
|
|
681
|
+
* ```
|
|
682
|
+
*/
|
|
683
|
+
readonly detailLabel?: string;
|
|
684
|
+
/**
|
|
685
|
+
* OPT OUT of the tab strip: the `detail` slot takes the whole rail, as it did
|
|
686
|
+
* before 0.50.0. Default `false`.
|
|
687
|
+
*
|
|
688
|
+
* For the app with a full-height custom layout of its own — a pane that owns
|
|
689
|
+
* its scroll and its header and would read as a second chrome under the
|
|
690
|
+
* library's strip. The cost is stated plainly: nothing the rail gains later
|
|
691
|
+
* (Served, Bookmarks, whatever follows) can reach that consumer, and neither
|
|
692
|
+
* can the library's own timeline. Prefer the tabs; take this only when the
|
|
693
|
+
* layout genuinely demands it.
|
|
694
|
+
*
|
|
695
|
+
* Ignored when no `detail` slot is supplied.
|
|
696
|
+
*/
|
|
697
|
+
readonly detailOnly?: boolean;
|
|
647
698
|
}
|
|
648
699
|
/**
|
|
649
700
|
* What the `detail` slot receives — the cursor, in the units a detail pane
|
|
@@ -634,16 +634,67 @@ interface LensProps {
|
|
|
634
634
|
*
|
|
635
635
|
* Same idea as `<TraceExplorerShell slots>`: keep the shipped layout and the
|
|
636
636
|
* shipped cursor, replace what one pane renders.
|
|
637
|
+
*
|
|
638
|
+
* THE LAW: **a slot fills a PANE, never the CHROME around it.** The rail's tab
|
|
639
|
+
* strip, the one cursor and the collapse pill belong to the library, because
|
|
640
|
+
* chrome is how the library adds capability over time — a consumer that fills a
|
|
641
|
+
* slot must still receive every capability the rail gains later. That is why
|
|
642
|
+
* `detail` is ONE TAB and not a takeover, and why `detailOnly` is an opt-out
|
|
643
|
+
* rather than the default.
|
|
637
644
|
*/
|
|
638
645
|
interface LensSlots {
|
|
639
646
|
/**
|
|
640
|
-
*
|
|
641
|
-
*
|
|
642
|
-
*
|
|
647
|
+
* Render the right column's FIRST TAB — your own pane, beside the library's.
|
|
648
|
+
*
|
|
649
|
+
* Your component renders inside the shipped column (its width, borders,
|
|
650
|
+
* collapse pill and scroll behaviour are unchanged) and inside the shipped
|
|
651
|
+
* rail: the tab strip stays, your pane becomes the first tab and is selected
|
|
652
|
+
* by default, and "What happened", "Served" and "Bookmarks" stay reachable
|
|
653
|
+
* beside it. Switching tabs never moves the cursor and never unmounts your
|
|
654
|
+
* pane — it keeps its own state.
|
|
655
|
+
*
|
|
656
|
+
* Before 0.50.0 a supplied slot replaced the whole rail, tab strip included,
|
|
657
|
+
* so a slot consumer could not reach anything the rail gained afterwards.
|
|
658
|
+
* That was the defect this shape fixes; `detailOnly` is the way back.
|
|
643
659
|
*
|
|
644
660
|
* Omit it and the built-in timeline renders exactly as before.
|
|
661
|
+
*
|
|
662
|
+
* ```tsx
|
|
663
|
+
* <Lens recorder={recorder} slots={{ detail: MyBands, detailLabel: 'Bands' }} />
|
|
664
|
+
* // rail: [ Bands* | What happened | Served | Bookmarks ]
|
|
665
|
+
* ```
|
|
645
666
|
*/
|
|
646
667
|
readonly detail?: React__default.ComponentType<LensDetailSlotProps>;
|
|
668
|
+
/**
|
|
669
|
+
* What the `detail` tab is CALLED. Default: `"Details"`.
|
|
670
|
+
*
|
|
671
|
+
* WHY it exists: the tab strip names each reading in the reader's own words,
|
|
672
|
+
* and "Details" is the library's generic word for someone else's pane. An app
|
|
673
|
+
* whose pane is a stack of SEO bands should say so — the strip is read left to
|
|
674
|
+
* right and an unnamed tab makes the app's own reading the anonymous one.
|
|
675
|
+
*
|
|
676
|
+
* A plain string, rendered as the tab's text; it is the accessible name too.
|
|
677
|
+
* Ignored when no `detail` slot is supplied.
|
|
678
|
+
*
|
|
679
|
+
* ```tsx
|
|
680
|
+
* slots={{ detail: MyBands, detailLabel: 'SEO bands' }}
|
|
681
|
+
* ```
|
|
682
|
+
*/
|
|
683
|
+
readonly detailLabel?: string;
|
|
684
|
+
/**
|
|
685
|
+
* OPT OUT of the tab strip: the `detail` slot takes the whole rail, as it did
|
|
686
|
+
* before 0.50.0. Default `false`.
|
|
687
|
+
*
|
|
688
|
+
* For the app with a full-height custom layout of its own — a pane that owns
|
|
689
|
+
* its scroll and its header and would read as a second chrome under the
|
|
690
|
+
* library's strip. The cost is stated plainly: nothing the rail gains later
|
|
691
|
+
* (Served, Bookmarks, whatever follows) can reach that consumer, and neither
|
|
692
|
+
* can the library's own timeline. Prefer the tabs; take this only when the
|
|
693
|
+
* layout genuinely demands it.
|
|
694
|
+
*
|
|
695
|
+
* Ignored when no `detail` slot is supplied.
|
|
696
|
+
*/
|
|
697
|
+
readonly detailOnly?: boolean;
|
|
647
698
|
}
|
|
648
699
|
/**
|
|
649
700
|
* What the `detail` slot receives — the cursor, in the units a detail pane
|
|
@@ -3296,16 +3296,16 @@ function diffPromptsBounded(a, b, maxCells = DIFF_CELL_CAP) {
|
|
|
3296
3296
|
}
|
|
3297
3297
|
|
|
3298
3298
|
// src/core/served/sincePrevious.ts
|
|
3299
|
-
function
|
|
3299
|
+
function unmatchedIndexes(a, b) {
|
|
3300
3300
|
const pool = /* @__PURE__ */ new Map();
|
|
3301
3301
|
for (const item of b) pool.set(item, (pool.get(item) ?? 0) + 1);
|
|
3302
|
-
|
|
3303
|
-
for (const item of a) {
|
|
3302
|
+
const out = [];
|
|
3303
|
+
for (const [i, item] of a.entries()) {
|
|
3304
3304
|
const n = pool.get(item) ?? 0;
|
|
3305
3305
|
if (n > 0) pool.set(item, n - 1);
|
|
3306
|
-
else
|
|
3306
|
+
else out.push(i);
|
|
3307
3307
|
}
|
|
3308
|
-
return
|
|
3308
|
+
return out;
|
|
3309
3309
|
}
|
|
3310
3310
|
function sincePrevious(current, previous) {
|
|
3311
3311
|
const now = current.view;
|
|
@@ -3328,6 +3328,10 @@ function sincePrevious(current, previous) {
|
|
|
3328
3328
|
if (a !== void 0 && b !== void 0 && a !== b) schemaChanged.push(name);
|
|
3329
3329
|
}
|
|
3330
3330
|
}
|
|
3331
|
+
const piecesEntered = unmatchedIndexes(nowPieces, beforePieces);
|
|
3332
|
+
const piecesLeft = unmatchedIndexes(beforePieces, nowPieces);
|
|
3333
|
+
const messagesEntered = unmatchedIndexes(nowMessages, beforeMessages);
|
|
3334
|
+
const messagesLeft = unmatchedIndexes(beforeMessages, nowMessages);
|
|
3331
3335
|
const changed = now.system.text !== before.system.text;
|
|
3332
3336
|
const diff = changed ? diffPromptsBounded(before.system.text, now.system.text) : Object.freeze([]);
|
|
3333
3337
|
return Object.freeze({
|
|
@@ -3336,12 +3340,16 @@ function sincePrevious(current, previous) {
|
|
|
3336
3340
|
system: Object.freeze({
|
|
3337
3341
|
changed,
|
|
3338
3342
|
...diff !== void 0 ? { diff: Object.freeze([...diff]) } : {},
|
|
3339
|
-
piecesEntered:
|
|
3340
|
-
piecesLeft:
|
|
3343
|
+
piecesEntered: piecesEntered.length,
|
|
3344
|
+
piecesLeft: piecesLeft.length,
|
|
3345
|
+
enteredIndexes: Object.freeze(piecesEntered),
|
|
3346
|
+
leftPieces: Object.freeze(piecesLeft.map((i) => before.system.pieces[i]))
|
|
3341
3347
|
}),
|
|
3342
3348
|
messages: Object.freeze({
|
|
3343
|
-
entered:
|
|
3344
|
-
left:
|
|
3349
|
+
entered: messagesEntered.length,
|
|
3350
|
+
left: messagesLeft.length,
|
|
3351
|
+
enteredIndexes: Object.freeze(messagesEntered),
|
|
3352
|
+
leftEntries: Object.freeze(messagesLeft.map((i) => before.messages.asSent[i]))
|
|
3345
3353
|
}),
|
|
3346
3354
|
tools: Object.freeze({
|
|
3347
3355
|
added: Object.freeze(added),
|
|
@@ -3404,6 +3412,295 @@ function foldFactsAt(recording, cursor) {
|
|
|
3404
3412
|
});
|
|
3405
3413
|
}
|
|
3406
3414
|
|
|
3415
|
+
// src/core/served/servedGraphAt.ts
|
|
3416
|
+
import {
|
|
3417
|
+
SERVED_GAPS as SERVED_GAPS2,
|
|
3418
|
+
UNGAPPED_FIELDS
|
|
3419
|
+
} from "agentfootprint";
|
|
3420
|
+
var SLOT_ORDER = Object.freeze({
|
|
3421
|
+
"system-prompt": 0,
|
|
3422
|
+
messages: 1,
|
|
3423
|
+
tools: 2
|
|
3424
|
+
});
|
|
3425
|
+
var SERVED_SLOTS = Object.freeze(
|
|
3426
|
+
Object.keys(SLOT_ORDER).sort((a, b) => SLOT_ORDER[a] - SLOT_ORDER[b])
|
|
3427
|
+
);
|
|
3428
|
+
var HELD_HONESTY_KEYS = Object.freeze([
|
|
3429
|
+
"basis",
|
|
3430
|
+
"redacted",
|
|
3431
|
+
"skipped",
|
|
3432
|
+
"foldError"
|
|
3433
|
+
]);
|
|
3434
|
+
var HELD_KEYS = Object.freeze([
|
|
3435
|
+
...FOLD_FACT_KEYS,
|
|
3436
|
+
...HELD_HONESTY_KEYS
|
|
3437
|
+
]);
|
|
3438
|
+
var RECONSTRUCTED = Object.freeze({ status: "reconstructed" });
|
|
3439
|
+
var NOT_ON_RECORD = Object.freeze({ status: "not-on-record" });
|
|
3440
|
+
function slotsOfFields(fields) {
|
|
3441
|
+
const out = [];
|
|
3442
|
+
for (const field of fields) {
|
|
3443
|
+
const slot = field.startsWith("system.") ? "system-prompt" : field.startsWith("messages.") ? "messages" : field.startsWith("tools.") ? "tools" : void 0;
|
|
3444
|
+
if (slot !== void 0 && !out.includes(slot)) out.push(slot);
|
|
3445
|
+
}
|
|
3446
|
+
return out;
|
|
3447
|
+
}
|
|
3448
|
+
function heldNodes(fold) {
|
|
3449
|
+
const node = (key, value, status) => Object.freeze(value === void 0 ? { key, status } : { key, value, status });
|
|
3450
|
+
const out = [];
|
|
3451
|
+
for (const key of FOLD_FACT_KEYS) {
|
|
3452
|
+
const value = fold[key];
|
|
3453
|
+
out.push(value === void 0 ? node(key, void 0, "not-on-record") : node(key, value, "reconstructed"));
|
|
3454
|
+
}
|
|
3455
|
+
out.push(
|
|
3456
|
+
fold.basis === void 0 ? node("basis", void 0, "not-on-record") : node("basis", fold.basis, "reconstructed")
|
|
3457
|
+
);
|
|
3458
|
+
out.push(node("redacted", fold.redacted, "reconstructed"));
|
|
3459
|
+
if (fold.skipped !== void 0) out.push(node("skipped", fold.skipped, "damaged"));
|
|
3460
|
+
if (fold.foldError !== void 0) out.push(node("foldError", fold.foldError, "damaged"));
|
|
3461
|
+
return Object.freeze(out);
|
|
3462
|
+
}
|
|
3463
|
+
function servedEdges(input) {
|
|
3464
|
+
const { row, checks, since } = input;
|
|
3465
|
+
const view = row.view;
|
|
3466
|
+
const edge = (e) => Object.freeze(e);
|
|
3467
|
+
const out = [];
|
|
3468
|
+
const compared = since !== void 0;
|
|
3469
|
+
const enteredPieces = new Set(since?.system.enteredIndexes ?? []);
|
|
3470
|
+
const enteredMessages = new Set(since?.messages.enteredIndexes ?? []);
|
|
3471
|
+
const stateOf = (entered) => compared ? entered ? "entered" : "unchanged" : void 0;
|
|
3472
|
+
const withState = (state) => state !== void 0 ? { state } : {};
|
|
3473
|
+
view.system.pieces.forEach((piece, i) => {
|
|
3474
|
+
out.push(
|
|
3475
|
+
edge({
|
|
3476
|
+
id: `piece:${i}`,
|
|
3477
|
+
kind: "piece",
|
|
3478
|
+
slot: piece.slot,
|
|
3479
|
+
source: piece.source,
|
|
3480
|
+
origin: piece.source,
|
|
3481
|
+
text: piece.text,
|
|
3482
|
+
check: checks.pieces[i] ?? RECONSTRUCTED,
|
|
3483
|
+
...withState(stateOf(enteredPieces.has(i)))
|
|
3484
|
+
})
|
|
3485
|
+
);
|
|
3486
|
+
});
|
|
3487
|
+
(since?.system.leftPieces ?? []).forEach((piece, i) => {
|
|
3488
|
+
out.push(
|
|
3489
|
+
edge({
|
|
3490
|
+
id: `piece-left:${i}`,
|
|
3491
|
+
kind: "piece",
|
|
3492
|
+
slot: piece.slot,
|
|
3493
|
+
source: piece.source,
|
|
3494
|
+
origin: piece.source,
|
|
3495
|
+
text: piece.text,
|
|
3496
|
+
// Not on this request at all: there is no row here for a hash to check.
|
|
3497
|
+
check: NOT_ON_RECORD,
|
|
3498
|
+
state: "left"
|
|
3499
|
+
})
|
|
3500
|
+
);
|
|
3501
|
+
});
|
|
3502
|
+
view.messages.asSent.forEach((message, i) => {
|
|
3503
|
+
const label = message.toolName ?? message.toolCallId;
|
|
3504
|
+
out.push(
|
|
3505
|
+
edge({
|
|
3506
|
+
id: `message:${i}`,
|
|
3507
|
+
kind: "message",
|
|
3508
|
+
slot: "messages",
|
|
3509
|
+
role: message.role,
|
|
3510
|
+
origin: message.role,
|
|
3511
|
+
...label !== void 0 ? { label } : {},
|
|
3512
|
+
text: message.content,
|
|
3513
|
+
check: checks.messages[i] ?? RECONSTRUCTED,
|
|
3514
|
+
...withState(stateOf(enteredMessages.has(i)))
|
|
3515
|
+
})
|
|
3516
|
+
);
|
|
3517
|
+
});
|
|
3518
|
+
(since?.messages.leftEntries ?? []).forEach((message, i) => {
|
|
3519
|
+
out.push(
|
|
3520
|
+
edge({
|
|
3521
|
+
id: `message-left:${i}`,
|
|
3522
|
+
kind: "message",
|
|
3523
|
+
slot: "messages",
|
|
3524
|
+
role: message.role,
|
|
3525
|
+
origin: message.role,
|
|
3526
|
+
text: message.content,
|
|
3527
|
+
check: NOT_ON_RECORD,
|
|
3528
|
+
state: "left"
|
|
3529
|
+
})
|
|
3530
|
+
);
|
|
3531
|
+
});
|
|
3532
|
+
view.messages.requestOnly.forEach((line, i) => {
|
|
3533
|
+
out.push(
|
|
3534
|
+
edge({
|
|
3535
|
+
id: `request-only:${i}`,
|
|
3536
|
+
kind: "request-only",
|
|
3537
|
+
slot: "messages",
|
|
3538
|
+
role: line.role,
|
|
3539
|
+
origin: line.role,
|
|
3540
|
+
label: line.reason,
|
|
3541
|
+
text: line.text,
|
|
3542
|
+
check: checks.requestOnly[i] ?? RECONSTRUCTED
|
|
3543
|
+
})
|
|
3544
|
+
);
|
|
3545
|
+
});
|
|
3546
|
+
view.tools.names.forEach((name) => {
|
|
3547
|
+
const schema = view.tools.schemas.find((s) => s.name === name);
|
|
3548
|
+
const state = compared ? since !== void 0 && since.tools.added.includes(name) ? "entered" : "unchanged" : void 0;
|
|
3549
|
+
out.push(
|
|
3550
|
+
edge({
|
|
3551
|
+
id: `tool:${name}`,
|
|
3552
|
+
kind: "tool",
|
|
3553
|
+
slot: "tools",
|
|
3554
|
+
label: name,
|
|
3555
|
+
// No rebuilt schema means no hash on this side; a NAME is never
|
|
3556
|
+
// verified — the receipt carries names as plain strings.
|
|
3557
|
+
check: schema !== void 0 ? checks.toolSchemas[name] ?? RECONSTRUCTED : RECONSTRUCTED,
|
|
3558
|
+
...view.tools.forced === name ? { forced: true } : {},
|
|
3559
|
+
...checks.onReceiptOnly.schemas.includes(name) ? { schemaOnReceiptOnly: true } : {},
|
|
3560
|
+
...since !== void 0 && since.tools.schemaChanged.includes(name) ? { schemaChanged: true } : {},
|
|
3561
|
+
...withState(state)
|
|
3562
|
+
})
|
|
3563
|
+
);
|
|
3564
|
+
});
|
|
3565
|
+
(since?.tools.removed ?? []).forEach((name) => {
|
|
3566
|
+
out.push(
|
|
3567
|
+
edge({
|
|
3568
|
+
id: `tool-left:${name}`,
|
|
3569
|
+
kind: "tool",
|
|
3570
|
+
slot: "tools",
|
|
3571
|
+
label: name,
|
|
3572
|
+
check: NOT_ON_RECORD,
|
|
3573
|
+
state: "left"
|
|
3574
|
+
})
|
|
3575
|
+
);
|
|
3576
|
+
});
|
|
3577
|
+
return Object.freeze(out);
|
|
3578
|
+
}
|
|
3579
|
+
function slotNodes(input, edges) {
|
|
3580
|
+
const { row, checks } = input;
|
|
3581
|
+
const view = row.view;
|
|
3582
|
+
const receipt = row.receipt;
|
|
3583
|
+
const gapsFor = (slot) => Object.freeze(view.gaps.filter((g) => slotsOfFields(g.fields).includes(slot)));
|
|
3584
|
+
const rebuiltIn = (slot) => edges.filter((e) => e.slot === slot && e.state !== "left").length;
|
|
3585
|
+
const onReceipt = {
|
|
3586
|
+
"system-prompt": receipt?.system.pieces.length,
|
|
3587
|
+
messages: receipt !== void 0 ? receipt.messages.entries.length + receipt.messages.requestOnly.length : void 0,
|
|
3588
|
+
tools: checks.namesOnReceipt?.length
|
|
3589
|
+
};
|
|
3590
|
+
const check2 = {
|
|
3591
|
+
"system-prompt": checks.system,
|
|
3592
|
+
messages: void 0,
|
|
3593
|
+
tools: checks.toolNames
|
|
3594
|
+
};
|
|
3595
|
+
const onReceiptOnly = {
|
|
3596
|
+
"system-prompt": checks.onReceiptOnly.pieces,
|
|
3597
|
+
messages: checks.onReceiptOnly.messages + checks.onReceiptOnly.requestOnly,
|
|
3598
|
+
tools: checks.onReceiptOnly.toolNames.length
|
|
3599
|
+
};
|
|
3600
|
+
const rebuiltOnly = {
|
|
3601
|
+
"system-prompt": checks.rebuiltOnly.pieces,
|
|
3602
|
+
messages: checks.rebuiltOnly.messages + checks.rebuiltOnly.requestOnly,
|
|
3603
|
+
tools: checks.rebuiltOnly.toolNames.length
|
|
3604
|
+
};
|
|
3605
|
+
return Object.freeze(
|
|
3606
|
+
SERVED_SLOTS.map((slot) => {
|
|
3607
|
+
const aggregate = check2[slot];
|
|
3608
|
+
const count = onReceipt[slot];
|
|
3609
|
+
return Object.freeze({
|
|
3610
|
+
slot,
|
|
3611
|
+
...aggregate !== void 0 ? { check: aggregate } : {},
|
|
3612
|
+
rebuilt: rebuiltIn(slot),
|
|
3613
|
+
...count !== void 0 ? { onReceipt: count } : {},
|
|
3614
|
+
onReceiptOnly: onReceiptOnly[slot],
|
|
3615
|
+
rebuiltOnly: rebuiltOnly[slot],
|
|
3616
|
+
gaps: gapsFor(slot)
|
|
3617
|
+
});
|
|
3618
|
+
})
|
|
3619
|
+
);
|
|
3620
|
+
}
|
|
3621
|
+
function withheldNodes(input) {
|
|
3622
|
+
const { row, fold } = input;
|
|
3623
|
+
const view = row.view;
|
|
3624
|
+
const out = [];
|
|
3625
|
+
if (view.tools.withheld !== void 0) {
|
|
3626
|
+
out.push(
|
|
3627
|
+
Object.freeze({
|
|
3628
|
+
kind: "tool-withheld",
|
|
3629
|
+
name: view.tools.withheld,
|
|
3630
|
+
from: "view",
|
|
3631
|
+
slot: "tools"
|
|
3632
|
+
})
|
|
3633
|
+
);
|
|
3634
|
+
}
|
|
3635
|
+
for (const id of fold.hiddenSkillIds ?? []) {
|
|
3636
|
+
out.push(Object.freeze({ kind: "hidden-skill", name: id, from: "fold" }));
|
|
3637
|
+
}
|
|
3638
|
+
const drops = row.receipt?.omittedForAttention;
|
|
3639
|
+
out.push(
|
|
3640
|
+
Object.freeze(
|
|
3641
|
+
drops !== void 0 ? {
|
|
3642
|
+
kind: "attention-drop",
|
|
3643
|
+
name: "omittedForAttention",
|
|
3644
|
+
from: "receipt",
|
|
3645
|
+
count: drops.count,
|
|
3646
|
+
hashes: Object.freeze([...drops.hashes])
|
|
3647
|
+
} : {
|
|
3648
|
+
// Absent is not none: the library's own sentence for a field no gap
|
|
3649
|
+
// explains, printed beside the Not-on-record badge.
|
|
3650
|
+
kind: "attention-drop",
|
|
3651
|
+
name: "omittedForAttention",
|
|
3652
|
+
from: "receipt",
|
|
3653
|
+
status: "not-on-record",
|
|
3654
|
+
why: UNGAPPED_FIELDS.omittedForAttention
|
|
3655
|
+
}
|
|
3656
|
+
)
|
|
3657
|
+
);
|
|
3658
|
+
if (fold.redacted) {
|
|
3659
|
+
out.push(Object.freeze({ kind: "redacted", name: "redacted", from: "fold" }));
|
|
3660
|
+
}
|
|
3661
|
+
for (const gap of view.gaps) {
|
|
3662
|
+
const slots = slotsOfFields(gap.fields);
|
|
3663
|
+
out.push(
|
|
3664
|
+
Object.freeze({
|
|
3665
|
+
kind: "gap",
|
|
3666
|
+
name: gap.gap,
|
|
3667
|
+
from: "view",
|
|
3668
|
+
// One slot named: draw it there. Several, or none: the band itself.
|
|
3669
|
+
...slots.length === 1 ? { slot: slots[0] } : {},
|
|
3670
|
+
why: SERVED_GAPS2[gap.gap].why,
|
|
3671
|
+
fields: gap.fields,
|
|
3672
|
+
...gap.cause !== void 0 ? { cause: gap.cause } : {}
|
|
3673
|
+
})
|
|
3674
|
+
);
|
|
3675
|
+
}
|
|
3676
|
+
return Object.freeze(out);
|
|
3677
|
+
}
|
|
3678
|
+
function callNode(input, edges) {
|
|
3679
|
+
const { row, checks } = input;
|
|
3680
|
+
const basis = row.view.basis;
|
|
3681
|
+
return Object.freeze({
|
|
3682
|
+
epoch: row.epoch,
|
|
3683
|
+
callRuntimeStageId: row.callRuntimeStageId,
|
|
3684
|
+
commitIdx: row.commitIdx,
|
|
3685
|
+
betweenCalls: row.betweenCalls,
|
|
3686
|
+
...row.previousEpoch !== void 0 ? { previousEpoch: row.previousEpoch } : {},
|
|
3687
|
+
...basis !== void 0 ? { basis } : {},
|
|
3688
|
+
basisStatus: basis !== void 0 ? "reconstructed" : "not-on-record",
|
|
3689
|
+
...row.receiptCause !== void 0 ? { receiptCause: row.receiptCause } : {},
|
|
3690
|
+
damaged: checks.damaged || edges.some((e) => e.check.status === "damaged")
|
|
3691
|
+
});
|
|
3692
|
+
}
|
|
3693
|
+
function servedGraphAt(input) {
|
|
3694
|
+
const edges = servedEdges(input);
|
|
3695
|
+
return Object.freeze({
|
|
3696
|
+
held: heldNodes(input.fold),
|
|
3697
|
+
served: slotNodes(input, edges),
|
|
3698
|
+
withheld: withheldNodes(input),
|
|
3699
|
+
call: callNode(input, edges),
|
|
3700
|
+
edges
|
|
3701
|
+
});
|
|
3702
|
+
}
|
|
3703
|
+
|
|
3407
3704
|
// src/core/bookmarks/bookmarkKey.ts
|
|
3408
3705
|
function fnv1a(s) {
|
|
3409
3706
|
let h = 2166136261;
|
|
@@ -3783,6 +4080,10 @@ export {
|
|
|
3783
4080
|
sincePrevious,
|
|
3784
4081
|
FOLD_FACT_KEYS,
|
|
3785
4082
|
foldFactsAt,
|
|
4083
|
+
SERVED_SLOTS,
|
|
4084
|
+
HELD_HONESTY_KEYS,
|
|
4085
|
+
HELD_KEYS,
|
|
4086
|
+
servedGraphAt,
|
|
3786
4087
|
bookmarkKey,
|
|
3787
4088
|
snapshotOfRunner,
|
|
3788
4089
|
snapshotLogKey,
|
|
@@ -3799,4 +4100,4 @@ export {
|
|
|
3799
4100
|
tagStopsFor,
|
|
3800
4101
|
tagAxisPositions
|
|
3801
4102
|
};
|
|
3802
|
-
//# sourceMappingURL=chunk-
|
|
4103
|
+
//# sourceMappingURL=chunk-AIZIILHJ.js.map
|