agentfootprint-lens 0.47.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.
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
 
@@ -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` / `messageDigestInput`.
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 has no hash for this 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 too, because the library hashes them over a canonical JSON it does not export, and a lookalike serializer would be a second copy of the rule. |
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
 
@@ -2967,10 +2967,15 @@ function servedRowForEpoch(recording, epoch) {
2967
2967
  }
2968
2968
 
2969
2969
  // src/core/served/verify.ts
2970
+ import * as agentfootprint2 from "agentfootprint";
2970
2971
  import {
2971
2972
  messageDigestInput,
2972
2973
  receiptHash
2973
2974
  } from "agentfootprint";
2975
+ function toolDigestOf() {
2976
+ const digest = agentfootprint2.toolDigestInput;
2977
+ return typeof digest === "function" ? digest : void 0;
2978
+ }
2974
2979
  var EXCUSING_GAPS = Object.freeze([
2975
2980
  "no-fold-base",
2976
2981
  "no-run-log",
@@ -3035,7 +3040,8 @@ function withoutReceipt(view, absent) {
3035
3040
  pieces: 0,
3036
3041
  messages: 0,
3037
3042
  requestOnly: 0,
3038
- toolNames: Object.freeze([])
3043
+ toolNames: Object.freeze([]),
3044
+ schemas: Object.freeze([])
3039
3045
  });
3040
3046
  return Object.freeze({
3041
3047
  system: check(absent),
@@ -3103,24 +3109,36 @@ function verify(view, receipt, runId, cause) {
3103
3109
  const namesOnReceiptOnly = receipt.tools.names.filter((n) => !rebuiltNames.has(n));
3104
3110
  const namesContradict = namesRebuiltOnly.length > 0 || namesOnReceiptOnly.length > 0 && !namesExcused;
3105
3111
  const toolNames = check(namesContradict ? "damaged" : "reconstructed");
3112
+ const schemasExcused = excusedOn(view, "tools.schemaHashes");
3113
+ const digest = toolDigestOf();
3106
3114
  const schemas = {};
3107
3115
  for (const schema of view.tools.schemas) {
3108
- schemas[schema.name] = check("reconstructed", void 0, receipt.tools.schemaHashes[schema.name]);
3116
+ const onReceipt = receipt.tools.schemaHashes[schema.name];
3117
+ schemas[schema.name] = digest === void 0 ? check("reconstructed", void 0, onReceipt) : against(hash(digest(schema)), onReceipt, "damaged", "damaged");
3109
3118
  }
3119
+ const rebuiltSchemaNames = new Set(view.tools.schemas.map((s) => s.name));
3120
+ const schemasRebuiltOnly = [...rebuiltSchemaNames].filter(
3121
+ (n) => receipt.tools.schemaHashes[n] === void 0
3122
+ );
3123
+ const schemasOnReceiptOnly = Object.keys(receipt.tools.schemaHashes).filter(
3124
+ (n) => !rebuiltSchemaNames.has(n)
3125
+ );
3110
3126
  const onReceiptOnly = Object.freeze({
3111
3127
  pieces: unpairedOnReceipt(piecePairs, receipt.system.pieces.length),
3112
3128
  messages: unpairedOnReceipt(messagePairs, receipt.messages.entries.length),
3113
3129
  requestOnly: unpairedOnReceipt(requestOnlyPairs, receipt.messages.requestOnly.length),
3114
- toolNames: Object.freeze(namesOnReceiptOnly)
3130
+ toolNames: Object.freeze(namesOnReceiptOnly),
3131
+ schemas: Object.freeze(schemasOnReceiptOnly)
3115
3132
  });
3116
3133
  const rebuiltOnly = Object.freeze({
3117
3134
  pieces: unpairedRebuilt(piecePairs),
3118
3135
  messages: unpairedRebuilt(messagePairs),
3119
3136
  requestOnly: unpairedRebuilt(requestOnlyPairs),
3120
- toolNames: Object.freeze(namesRebuiltOnly)
3137
+ toolNames: Object.freeze(namesRebuiltOnly),
3138
+ schemas: Object.freeze(schemasRebuiltOnly)
3121
3139
  });
3122
- const unexcusedShort = onReceiptOnly.pieces > 0 && !piecesExcused || onReceiptOnly.messages > 0 && !entriesExcused || onReceiptOnly.requestOnly > 0 && !requestOnlyExcused;
3123
- const all = [system, ...pieces, ...messages, ...requestOnly, toolNames];
3140
+ const unexcusedShort = onReceiptOnly.pieces > 0 && !piecesExcused || onReceiptOnly.messages > 0 && !entriesExcused || onReceiptOnly.requestOnly > 0 && !requestOnlyExcused || onReceiptOnly.schemas.length > 0 && !schemasExcused;
3141
+ const all = [system, ...pieces, ...messages, ...requestOnly, toolNames, ...Object.values(schemas)];
3124
3142
  return Object.freeze({
3125
3143
  system,
3126
3144
  pieces: Object.freeze(pieces),
@@ -3321,15 +3339,14 @@ var FOLD_FACT_KEYS = Object.freeze([
3321
3339
  "hiddenSkillIds"
3322
3340
  ]);
3323
3341
  function skippedIndicesOf(folded) {
3324
- const rows = folded.skipped;
3325
- if (!Array.isArray(rows)) return void 0;
3326
- const indices = rows.map((r) => r?.index).filter((i) => typeof i === "number");
3327
- return indices.length > 0 ? Object.freeze(indices) : void 0;
3342
+ const gaps = folded.skipped;
3343
+ return gaps !== void 0 && gaps.length > 0 ? Object.freeze(gaps.map((g) => g.index)) : void 0;
3328
3344
  }
3329
3345
  function foldFactsAt(recording, cursor) {
3346
+ const source = typeof recording === "object" && recording !== null ? recording : void 0;
3330
3347
  let folded;
3331
3348
  try {
3332
- folded = stateAt(recording, cursor.commitIdx);
3349
+ folded = stateAt(source, cursor.commitIdx);
3333
3350
  } catch (e) {
3334
3351
  return Object.freeze({
3335
3352
  redacted: false,
@@ -3415,4 +3432,4 @@ export {
3415
3432
  FOLD_FACT_KEYS,
3416
3433
  foldFactsAt
3417
3434
  };
3418
- //# sourceMappingURL=chunk-E5ZRMHYY.js.map
3435
+ //# sourceMappingURL=chunk-4PENVNUY.js.map