@sanning/proof 0.19.1 → 0.20.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.
Files changed (98) hide show
  1. package/README.md +30 -29
  2. package/dist/agent-record.d.ts +62 -0
  3. package/dist/agent-record.js +527 -0
  4. package/dist/agent-schemas.generated.d.ts +15 -0
  5. package/dist/agent-schemas.generated.js +5759 -0
  6. package/dist/case-file.d.ts +32 -89
  7. package/dist/case-file.js +41 -93
  8. package/dist/case.d.ts +339 -822
  9. package/dist/case.js +667 -1160
  10. package/dist/chain.d.ts +19 -47
  11. package/dist/chain.js +78 -205
  12. package/dist/claims.d.ts +211 -0
  13. package/dist/claims.js +1081 -0
  14. package/dist/cli.d.ts +45 -68
  15. package/dist/cli.js +618 -1022
  16. package/dist/compose.js +23 -65
  17. package/dist/crypto.d.ts +7 -8
  18. package/dist/crypto.js +60 -147
  19. package/dist/declaration.d.ts +68 -194
  20. package/dist/declaration.js +175 -518
  21. package/dist/environment.d.ts +9 -30
  22. package/dist/environment.js +17 -66
  23. package/dist/evidence.d.ts +119 -160
  24. package/dist/evidence.js +744 -1103
  25. package/dist/exit-codes.d.ts +11 -27
  26. package/dist/exit-codes.js +24 -59
  27. package/dist/file-match.d.ts +33 -79
  28. package/dist/file-match.js +33 -69
  29. package/dist/grading.d.ts +98 -135
  30. package/dist/grading.js +224 -380
  31. package/dist/handoff.d.ts +62 -160
  32. package/dist/handoff.js +153 -393
  33. package/dist/index.d.ts +14 -10
  34. package/dist/index.js +60 -167
  35. package/dist/json-schema.d.ts +29 -0
  36. package/dist/json-schema.js +298 -0
  37. package/dist/merkle.js +16 -28
  38. package/dist/message-parts.d.ts +56 -212
  39. package/dist/message-parts.js +142 -502
  40. package/dist/side-content.d.ts +11 -22
  41. package/dist/side-content.js +22 -65
  42. package/dist/types.d.ts +17 -0
  43. package/dist/types.js +19 -5
  44. package/dist/verifier.d.ts +40 -114
  45. package/dist/verifier.js +196 -535
  46. package/dist/witness-cache.d.ts +24 -45
  47. package/dist/witness-cache.js +38 -81
  48. package/dist/witness-lookup.d.ts +36 -57
  49. package/dist/witness-lookup.js +89 -139
  50. package/dist/zip-reader.d.ts +18 -33
  51. package/dist/zip-reader.js +19 -34
  52. package/package.json +11 -3
  53. package/src/agent-record.ts +608 -0
  54. package/src/agent-schemas.generated.ts +5764 -0
  55. package/src/case-file.ts +45 -109
  56. package/src/case.ts +814 -1328
  57. package/src/chain.ts +85 -224
  58. package/src/claims.ts +1253 -0
  59. package/src/cli.ts +714 -1176
  60. package/src/compose.ts +33 -79
  61. package/src/crypto.ts +60 -147
  62. package/src/declaration.ts +219 -656
  63. package/src/environment.ts +17 -65
  64. package/src/evidence.ts +958 -1381
  65. package/src/exit-codes.ts +24 -63
  66. package/src/file-match.ts +40 -89
  67. package/src/grading.ts +287 -421
  68. package/src/handoff.ts +175 -427
  69. package/src/index.ts +131 -177
  70. package/src/json-schema.ts +295 -0
  71. package/src/merkle.ts +16 -28
  72. package/src/message-parts.ts +155 -533
  73. package/src/side-content.ts +25 -68
  74. package/src/types.ts +55 -36
  75. package/src/verifier.ts +202 -526
  76. package/src/witness-cache.ts +45 -94
  77. package/src/witness-lookup.ts +107 -177
  78. package/src/zip-reader.ts +20 -38
  79. package/vectors/README.md +52 -34
  80. package/vectors/agent-record.json +2175 -0
  81. package/vectors/case-file-index.json +1 -1
  82. package/vectors/case-split.json +2 -2
  83. package/vectors/chain-reading.json +3 -3
  84. package/vectors/file-match.json +285 -148
  85. package/vectors/jcs-reader.json +6 -6
  86. package/vectors/pack-grading.json +102 -25
  87. package/vectors/sandbox-evidence.json +573 -581
  88. package/vectors/stamp-on-chain.json +18 -18
  89. package/vectors/witness-cache.json +49 -49
  90. package/dist/agent-proof.d.ts +0 -28
  91. package/dist/agent-proof.js +0 -226
  92. package/dist/run-adapters.d.ts +0 -10
  93. package/dist/run-adapters.js +0 -35
  94. package/dist/run.d.ts +0 -209
  95. package/dist/run.js +0 -1405
  96. package/src/agent-proof.ts +0 -264
  97. package/src/run-adapters.ts +0 -44
  98. package/src/run.ts +0 -1598
package/dist/merkle.js CHANGED
@@ -1,16 +1,10 @@
1
- // RFC 9162 binary Merkle tree — build, audit paths, inclusion verification.
1
+ // RFC 9162 binary Merkle tree: build, audit paths, inclusion verification.
2
2
  //
3
- // A faithful port of the Go reference (the agent producer's pkg/merkle), in lockstep
4
- // with the Python kernel's sanning_proof.merkle. All hashing is SHA-256 with
5
- // RFC 9162 §2.1 domain separation: leaves prefixed 0x00, interior nodes 0x01.
6
- // A tree of n leaves splits into a left subtree of k leaves (the largest
7
- // power of two < n) and a right subtree of n−k — NOT the Bitcoin
8
- // duplicate-last-leaf variant, which produces different roots for
9
- // non-power-of-two leaf counts.
10
- //
11
- // The empty tree (zero leaves) hashes to SHA-256("") — EMPTY_TREE_ROOT_HEX.
12
- // Conformance is byte-for-byte against the 7 merkle-tree-* vectors of
13
- // test-vectors-v1.0 (test/conformance.test.ts).
3
+ // SHA-256 with RFC 9162 §2.1 domain separation: leaves prefixed 0x00, interior
4
+ // nodes 0x01. A tree of n leaves splits into a left subtree of k leaves (the
5
+ // largest power of two < n) and a right subtree of n-k, not the Bitcoin
6
+ // duplicate-last-leaf variant. The empty tree hashes to SHA-256("")
7
+ // (EMPTY_TREE_ROOT_HEX).
14
8
  import { sha256Bytes } from "./crypto.js";
15
9
  export const EMPTY_TREE_ROOT_HEX = "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855";
16
10
  // SHA-256(0x00 || leafBytes) per RFC 9162 §2.1.
@@ -25,9 +19,8 @@ export async function nodeHash(left, right) {
25
19
  buf.set(right, 1 + left.length);
26
20
  return sha256Bytes(buf);
27
21
  }
28
- // The RFC 9162 Merkle Tree Hash of already-hashed leaves. Callers pass leaf
29
- // hashes — the output of leafHash on each leaf's canonical bytes. Zero leaves
30
- // yields SHA-256("").
22
+ // The RFC 9162 Merkle Tree Hash of already-hashed leaves (the output of
23
+ // `leafHash`). Zero leaves yields SHA-256("").
31
24
  export async function merkleRoot(leafHashes) {
32
25
  const n = leafHashes.length;
33
26
  if (n === 0)
@@ -37,9 +30,8 @@ export async function merkleRoot(leafHashes) {
37
30
  const k = largestPow2LessThan(n);
38
31
  return nodeHash(await merkleRoot(leafHashes.slice(0, k)), await merkleRoot(leafHashes.slice(k)));
39
32
  }
40
- // The inclusion proof for the leaf at index m, per RFC 9162 §2.1.3: sibling
41
- // hashes bottom-up; empty for a single-leaf tree (the leaf hash itself is the
42
- // root). Throws RangeError when m is out of range.
33
+ // The inclusion proof for the leaf at index m (RFC 9162 §2.1.3): sibling hashes
34
+ // bottom-up, empty for a single-leaf tree. Throws RangeError when m is out of range.
43
35
  export async function auditPath(m, leafHashes) {
44
36
  if (!Number.isInteger(m) || m < 0 || m >= leafHashes.length) {
45
37
  throw new RangeError("merkle: leaf index out of range");
@@ -56,28 +48,24 @@ async function path(m, leafHashes) {
56
48
  }
57
49
  return [...(await path(m - k, leafHashes.slice(k))), await merkleRoot(leafHashes.slice(0, k))];
58
50
  }
59
- // Verify an RFC 9162 §2.1.3 inclusion proof. `leaf` is the leaf hash
60
- // (leafHash of the canonical leaf bytes); `path` is the audit path bottom-up;
61
- // `expectedRoot` is the merkleRoot committed by the checkpoint envelope.
62
- // True iff the path reconstructs the expected root. Never throws.
51
+ // Verify an RFC 9162 §2.1.3 inclusion proof. `leaf` is the leaf hash, `path` the
52
+ // audit path bottom-up, `expectedRoot` the root the checkpoint commits to. Never throws.
63
53
  export async function verifyInclusion(leaf, leafIndex, totalLeaves, auditPath, expectedRoot) {
64
54
  if (!Number.isInteger(leafIndex) || leafIndex < 0 || totalLeaves <= 0 || leafIndex >= totalLeaves) {
65
55
  return false;
66
56
  }
67
57
  if (totalLeaves === 1)
68
58
  return auditPath.length === 0 && bytesEqual(leaf, expectedRoot);
69
- // Arithmetic, not bitwise: JavaScript's `&` and `>>=` truncate to 32 bits,
70
- // so a tree past 2**31 leaves walked a different tree here than in the
71
- // Python kernel, whose integers are unbounded. `% 2` and a halving floor
72
- // are exact for every safe integer, and the stated index and count reach
73
- // this walk as safe integers.
59
+ // Arithmetic, not bitwise: `&` and `>>=` truncate to 32 bits, so a tree past
60
+ // 2**31 leaves would walk a different tree. `% 2` and halving floor are exact
61
+ // for every safe integer.
74
62
  let fn = leafIndex;
75
63
  let sn = totalLeaves - 1;
76
64
  let r = leaf;
77
65
  const half = (v) => Math.floor(v / 2);
78
66
  for (const p of auditPath) {
79
67
  if (sn === 0)
80
- return false; // path longer than the tree depth — malformed
68
+ return false; // path longer than the tree depth
81
69
  if (fn % 2 === 1 || fn === sn) {
82
70
  r = await nodeHash(p, r);
83
71
  if (fn % 2 === 0) {
@@ -1,157 +1,71 @@
1
- /**
2
- * The result of a part whose supplied bytes hash to its `content_hash` AND
3
- * count to its `content_length`.
4
- */
1
+ /** The supplied bytes match both `content_hash` and `content_length`. */
5
2
  export declare const PART_MATCHED = "part_matched";
6
3
  /**
7
- * The result of a part whose supplied bytes miss either of those, with no
8
- * respelling of them that explains the digest. The held-message substitution
9
- * applies to this part, and it replaces BOTH values with the held bytes' own.
4
+ * The supplied bytes miss either value, and no respelling explains the digest.
5
+ * The held message replaces both values with the held bytes' own.
10
6
  */
11
7
  export declare const PART_MISMATCHED = "part_mismatched";
12
- /**
13
- * A file part whose bytes were not supplied, or whose supply was REFUSED. It is
14
- * not checked and it is not clean: section 4.1 forbids a reader treating it as
15
- * checked.
16
- */
8
+ /** Bytes not supplied, or the supply was refused. Not checked, and not clean. */
17
9
  export declare const PART_UNCHECKED = "part_unchecked";
18
10
  /**
19
- * A `kind: "file"` part carrying its content inside the message, and ACCEPTED
20
- * (proof#157, ratified by Phil 2026-09-22). It is committed by the MESSAGE
21
- * ITSELF: `message_hash` is SHA-256 over the canonical bytes of the message
22
- * object, so altering that content changes the message, changes the hash, and
23
- * fails the pair. Nothing is hashed against it here and nothing is substituted,
24
- * because there is nothing travelling beside the message for the message to
25
- * disagree with. That is why the swapped-bytes attack cannot reach this shape.
11
+ * A file part with its content inside the message, committed by
12
+ * `message_hash` itself. Nothing is hashed or substituted.
26
13
  */
27
14
  export declare const PART_INLINE_BYTES = "part_inline_bytes";
28
15
  /**
29
- * An inline part whose `content_hash`, `content_length` or both DISAGREE with
30
- * its inline content: no single reading of that content satisfies every claim
31
- * the part writes beside it. The message's own commitment still governs and
32
- * nothing is substituted; this names the SECOND claim that disagrees with it,
33
- * which is a finding in its own right (proof#157).
34
- *
35
- * ONE name for both claims, exactly as `PART_MISMATCHED` is one name for both
36
- * on a descriptor part. It was `part_inline_hash_mismatch` for one round of
37
- * this unmerged PR, when only the hash was compared; a name that says "hash"
38
- * about a result that also covers a length asserts more than its body does, so
39
- * the name was re-cut with the rule (proof#156 second gate, F4).
40
- *
41
- * The check is deliberately permissive ACROSS readings: the raw UTF-8 of the
42
- * value, its base64 decoding and its hex decoding are all tried, so this fires
43
- * only where no reading agrees at all. It is not permissive BETWEEN claims: one
44
- * reading has to satisfy all of them, because a hash that agrees under the
45
- * base64 decoding beside a length that agrees under the raw text describes no
46
- * blob that exists.
16
+ * An inline part whose `content_hash` or `content_length` disagrees with its
17
+ * inline content. The raw UTF-8, base64 and hex readings are all tried, but one
18
+ * reading must satisfy every claim present. Nothing is substituted.
47
19
  */
48
20
  export declare const PART_INLINE_MISMATCHED = "part_inline_mismatched";
49
21
  /**
50
22
  * `kind: "file"` without a conformant `content_hash` and `content_length`. A
51
- * sender's defect rather than a receiver's, so it is named rather than refused:
52
- * the receiver did nothing wrong by holding it.
23
+ * sender's defect, so it is named rather than refused.
53
24
  */
54
25
  export declare const PART_MALFORMED_DESCRIPTOR = "part_malformed_descriptor";
55
- /**
56
- * Any other part. It is committed as ordinary JSON content and this rule checks
57
- * nothing about it.
58
- */
26
+ /** Any other part: ordinary JSON content, not checked by this rule. */
59
27
  export declare const PART_NOT_A_FILE = "part_not_a_file";
60
28
  /**
61
- * REFUSAL of ONE supplied entry. Bytes were supplied for no part of the
62
- * message, or for a part this rule cannot check. The caller and the message
63
- * disagree about what was sent, and hashing bytes against a part that is not
64
- * there establishes nothing. Where the entry names a part that EXISTS but is
65
- * not checkable, the refusal carries that part's index and the part keeps the
66
- * result it classified to; where it names no part at all, the refusal carries
67
- * the entry position and no part. Neither stops the record being written.
29
+ * Refusal of one supplied entry: it names no part, or a part this rule cannot
30
+ * check. Carries that part's index when the part exists, else only the entry
31
+ * position.
68
32
  */
69
33
  export declare const BYTES_FOR_UNKNOWN_PART = "bytes_for_unknown_part";
70
- /**
71
- * REFUSAL. Two DIFFERENT byte strings were supplied for one part. A receiver
72
- * holding two answers has not established which one it held.
73
- */
34
+ /** Refusal: two different byte strings were supplied for one part. */
74
35
  export declare const CONFLICTING_PART_BYTES = "conflicting_part_bytes";
75
36
  /**
76
- * REFUSAL of ONE supplied entry. The supplied bytes miss the descriptor, but a
77
- * base64 or hex RESPELLING of them in EITHER direction hashes to it: either the
78
- * bytes DECODE to the part (the receiver's stack never decoded what it was
79
- * handed) or they ENCODE to it (the receiver's stack decoded bytes the sender
80
- * committed in their encoded spelling, which is what a single-line DER
81
- * attachment, a PEM body and a JWT all are). That is a coding defect in the
82
- * receiver, not a tamper by the sender: a proto3 JSON transcoder re-encodes
83
- * base64 URL-safe and unpadded while behaving honestly, and under the silence
84
- * this amendment removes it produced a permanent tamper record against a sender
85
- * who did nothing. Recording evidence out of a receiver's own bug is the one
86
- * outcome worse than recording nothing, so BOTH directions are guarded
87
- * (proof#156 gate): guarding only the decoding direction left the other half of
88
- * one honest stack's behavior reading as a tamper.
37
+ * Refusal of one supplied entry: the bytes miss the descriptor, but a base64
38
+ * or hex respelling in either direction hashes to it. That is a receiver's
39
+ * coding defect (an undecoded supply, or a decoded PEM, DER or JWT the sender
40
+ * committed as text), not a sender's tamper, so it must not produce a tamper
41
+ * record.
89
42
  */
90
43
  export declare const PART_ENCODING_MISMATCH = "part_encoding_mismatch";
91
44
  /**
92
- * A MESSAGE-LEVEL result. The message carries a `parts` key whose value is
93
- * present and is not an array, which is how proto3 JSON encodes a map and so is
94
- * reachable from the same transport family the motivating example comes from.
95
- * Section 4.1 asks for one result per part; a message shaped like this has
96
- * part-shaped content and no parts, and before proof#156's gate it produced
97
- * zero readings and an accepted record. It is named rather than refused: the
98
- * receiver did nothing wrong by holding it, and nothing in it is checked. A
99
- * `parts` written as `null` is an ABSENT `parts` and is not this, on A14's
100
- * ruling that an absent value and one written as `null` name one thing.
45
+ * Message-level: `parts` is present, not null and not an array (proto3 JSON
46
+ * encodes a map this way). Named, not refused. A `parts` of `null` is absent.
101
47
  */
102
48
  export declare const PARTS_NOT_AN_ARRAY = "parts_not_an_array";
103
- /**
104
- * NOTE, not a refusal. One part supplied more than once with IDENTICAL bytes is
105
- * one supply stated twice, and the part is checked normally. Different bytes for
106
- * one part are `CONFLICTING_PART_BYTES`.
107
- */
49
+ /** Note, not a refusal: one part supplied more than once with identical bytes. */
108
50
  export declare const PART_SUPPLIED_TWICE = "part_supplied_twice";
109
- /**
110
- * The order refusals are reported in, fixed so two kernels report one list
111
- * rather than two orderings of it.
112
- */
51
+ /** The fixed order refusals are reported in. */
113
52
  export declare const REFUSAL_ORDER: readonly ["bytes_for_unknown_part", "conflicting_part_bytes", "part_encoding_mismatch"];
114
- /**
115
- * The order message-level results are reported in, for the same reason as
116
- * `REFUSAL_ORDER`.
117
- */
53
+ /** The fixed order message-level results are reported in. */
118
54
  export declare const MESSAGE_RESULT_ORDER: readonly ["parts_not_an_array"];
119
55
  /**
120
- * The keys that make a part carry its content INLINE, in the order they are
121
- * read. Settled at `bytes` alone (envelope-spec §4.1, A31, proof#163): that
122
- * is A2A's spelling, the one the wedge actually emits, and it is the only
123
- * inline spelling either reference kernel has read from a real producer.
124
- * `content` and `data` were added on inference rather than on an observed
125
- * wire shape, and sanning-io/anchor's `filePart()` and `file_part()` SDK
126
- * helpers, the only producer this plane ships, confirm the inference was
127
- * wrong: both emit a descriptor part, `kind: "file"` plus `content_hash` and
128
- * `content_length`, and neither ever writes a `content` or `data` wire key
129
- * for inline bytes. None is A2A 1.0.0's either: its `Part` has no `kind` and
130
- * carries a file as `raw` or `url`, so a literal A2A part reads
131
- * `part_not_a_file` (envelope-spec §4.1, A19). The one key present with a
132
- * value that is not `null` is the inline content.
133
- *
134
- * A key present with `null` is an ABSENT inline value, on A14's ruling that an
135
- * absent value and one written as `null` name one thing. That matters: a
136
- * proto3 JSON encoder can write an unset field as `"bytes": null`, and reading
137
- * that as inline made a conformant DESCRIPTOR part uncheckable
138
- * (proof#156 gate). Such a part falls through to the descriptor rule, which is
139
- * what it is.
56
+ * The key that makes a part carry its content inline (envelope-spec §4.1).
57
+ * `bytes` is A2A's spelling. A key present with `null` is absent, so a proto3
58
+ * `"bytes": null` on a descriptor part still reads as a descriptor.
140
59
  */
141
60
  export declare const INLINE_BYTES_KEYS: readonly ["bytes"];
142
61
  /**
143
- * The largest integer a double carries exactly, the same bound section 4.1 puts
144
- * on `event.sequence`: a `content_length` outside it is a value the two kernels
145
- * would read as two numbers.
62
+ * The largest exactly representable integer, the bound §4.1 puts on
63
+ * `event.sequence`. A larger `content_length` is malformed.
146
64
  */
147
65
  export declare const MAX_SAFE_INTEGER: number;
148
66
  /**
149
- * One byte string a receiver holds, and the part it holds it for.
150
- *
151
- * `part` is an INDEX into the message's `parts` array. It is an index rather
152
- * than a content hash on purpose: keying the supply by the value the check is
153
- * about to grade would mean a receiver could only supply bytes it had already
154
- * agreed with, which is the whole defect stated backwards.
67
+ * A byte string the receiver holds, keyed by the index of its part. Keyed by
68
+ * index, not by hash, so a receiver can supply bytes that disagree.
155
69
  */
156
70
  export interface SuppliedPart {
157
71
  part: unknown;
@@ -163,13 +77,9 @@ export interface PartReading {
163
77
  result: string;
164
78
  }
165
79
  /**
166
- * A caller error. Nothing is written, nothing is signed, no record made.
167
- *
168
- * `part` is the part index where the refusal is about a part of the message,
169
- * and `entry` is the POSITION of the offending supplied entry in the sequence
170
- * the caller passed. The position is recorded rather than the value the caller
171
- * named, so a caller that names a part with a float or a string cannot make two
172
- * kernels serialize one reading two ways.
80
+ * A caller error. `part` is the part index where there is one; `entry` is the
81
+ * position of the supplied entry, recorded rather than the caller's value so
82
+ * every implementation serializes it the same way.
173
83
  */
174
84
  export interface Refusal {
175
85
  name: string;
@@ -177,28 +87,17 @@ export interface Refusal {
177
87
  entry: number | null;
178
88
  }
179
89
  /**
180
- * Something worth reporting that refuses nothing and checks nothing out. Same
181
- * shape as a `Refusal` and carried in its own list, so no caller can read a
182
- * note as a refusal by looking only at a name.
90
+ * Reported, but refuses nothing. Kept in its own list so a note is never read
91
+ * as a refusal by name alone.
183
92
  */
184
93
  export type Note = Refusal;
185
94
  /**
186
- * The message a receiver commits to, and everything it found on the way.
187
- *
188
- * `heldMessage` is ALWAYS the message the receiver commits to and is never
189
- * null: a refusal is scoped to the supply it is about and never denies the
190
- * receiver a record (proof#156 gate). The caller commits `event.message_hash`
191
- * as SHA-256 over its JCS (RFC 8785) canonical bytes, which is the rule section
192
- * 4.1 already states and the reason this module does not hash anything itself.
193
- *
194
- * `messageResults` carries the message-level named results, in
195
- * `MESSAGE_RESULT_ORDER`. It is empty for every message whose shape the rule
196
- * reads.
95
+ * What a receiver commits to, and what it found.
197
96
  *
198
- * `substituted` carries the indexes of the parts whose descriptor the receiver
199
- * replaced with the held bytes' own hash and length. It is empty for an honest
200
- * hand-off, and where it is not empty the receiving side writes
201
- * `outcome: "rejected"`.
97
+ * `heldMessage` is never null; the caller commits `event.message_hash` as
98
+ * SHA-256 over its JCS bytes. `messageResults` is in `MESSAGE_RESULT_ORDER`.
99
+ * `substituted` lists the parts replaced by held bytes; non-empty means the
100
+ * receiver writes `outcome: "rejected"`.
202
101
  */
203
102
  export interface HeldMessage {
204
103
  parts: PartReading[];
@@ -209,83 +108,28 @@ export interface HeldMessage {
209
108
  substituted: number[];
210
109
  }
211
110
  /**
212
- * Whether this part is one this rule CHECKS against bytes travelling BESIDE the
213
- * message: a conformant descriptor part. Exactly the three conditions section
214
- * 4.1 states, in the order that keeps inline content out. An INLINE part is
215
- * accepted and committed by the message itself (proof#157), so there is nothing
216
- * here for supplied bytes to be checked against and this answers `false`.
111
+ * Whether bytes beside the message are checked against this part: true only
112
+ * for a conformant descriptor part, never for an inline one.
217
113
  */
218
114
  export declare function isFilePart(part: unknown): boolean;
219
115
  /**
220
116
  * What a receiver commits to, given the message and the bytes it holds.
221
117
  *
222
- * `supplied` is the bytes the receiver actually holds, each named by the index
223
- * of the part it belongs to. An empty list is the common case, a receiver that
224
- * holds no part's bytes, and it reads exactly as section 4.1 read before this
225
- * amendment: every file part is `PART_UNCHECKED` and the message a receiver
226
- * commits to is the message it was given.
227
- *
228
- * Where a part's bytes hash to something other than its `content_hash`, or
229
- * where their count is not its `content_length`, the returned `heldMessage`
230
- * carries that part's `content_hash` and `content_length` replaced by the held
231
- * bytes' own. The receiver commits `event.message_hash` over THAT, so its
232
- * record and the sender's disagree and the pair fails on `message_hash`.
233
- *
234
- * A refusal is scoped to the supplied entry it is about: the part it names is
235
- * not checked and not substituted, every other part reads as it would have, and
236
- * `heldMessage` is returned either way. Nothing a caller or a sender can do
237
- * denies the receiver a record.
118
+ * `supplied` names each byte string by its part index. With none, every file
119
+ * part is `PART_UNCHECKED` and the held message is the message given. Where a
120
+ * part's bytes miss its hash or length, `heldMessage` carries the held bytes'
121
+ * own values for that part, so the pair fails on `message_hash`. A refusal is
122
+ * scoped to its entry; `heldMessage` is always returned.
238
123
  */
239
124
  export declare function commitHeldMessage(message: unknown, supplied?: readonly SuppliedPart[]): Promise<HeldMessage>;
240
125
  /**
241
- * The parts the receiver HELD bytes for and did NOT check.
242
- *
243
- * The gap between "nothing was substituted" and "every part is clean", named
244
- * so a caller does not have to re-derive it from the refusal list (proof#156
245
- * second gate, F5). A part is in here where a refusal NAMES it and its reading
246
- * is one of `HELD_UNCHECKED_RESULTS`: the receiver had bytes for that part,
247
- * the rule declined to grade them, and NOTHING about that reaches the
248
- * committed record.
249
- *
250
- * THE RULE, in one sentence, because a filter is not a rule. A part is in here
251
- * where the receiver held bytes for it AND the message itself does not commit
252
- * its content. `PART_UNCHECKED` is a refused supply;
253
- * `PART_MALFORMED_DESCRIPTOR` is a part whose content travels BESIDE the
254
- * message under a claim no receiver can use, so the receiver's bytes are
255
- * exactly as ungraded and the record says as little. The first cut of this
256
- * function omitted the second, which the third gate measured: a `kind: "file"`
257
- * part with a non-conformant `content_hash` or `content_length`, bytes held,
258
- * read `[]` here, so a caller following the rule this module prescribes wrote
259
- * `accepted` over a part it held bytes for and never checked. That is the
260
- * class the mechanism exists to close, and Phil named it in proof#162's
261
- * ratification as the gap that decision inherits unless it is closed here.
262
- *
263
- * Three results are excluded ON PURPOSE. An inline part (`PART_INLINE_BYTES`,
264
- * `PART_INLINE_MISMATCHED`) and a non-file part (`PART_NOT_A_FILE`) carry
265
- * their content INSIDE the message, and `message_hash` commits it on both
266
- * sides: bytes supplied beside one of those grade nothing and hide nothing,
267
- * because a reader of either pack reads the content itself. Naming them would
268
- * fire the caller's obligation on parts the swapped-bytes attack cannot reach
269
- * at all.
270
- *
271
- * Why it is worth a function rather than a note. Delivering a tampered copy of
272
- * a part alone is `part_mismatched`, is substituted, and fails the pair on
273
- * `message_hash`. Delivering the tampered copy AND the true one is
274
- * `conflicting_part_bytes`, is never substituted, and the pair VERIFIES, so
275
- * adding data suppresses detection and the only trace is a refusal the
276
- * receiving side publishes at its own discretion.
277
- *
278
- * NOT the same as "this part reads `PART_UNCHECKED`". A receiver that holds no
279
- * bytes at all reads every file part that way, and that is the COMMON CASE,
280
- * unchanged by A18 and not a reason to withhold `accepted`. Only a part whose
281
- * bytes the receiver actually held and could not grade is in here.
126
+ * The parts the receiver held bytes for and did not check: a refusal names the
127
+ * part and its result is in `HELD_UNCHECKED_RESULTS`, or the part is a URL part
128
+ * that reads `PART_NOT_A_FILE` and the refusal is `BYTES_FOR_UNKNOWN_PART`. A
129
+ * caller writes `accepted` only when this and `substituted` are both empty, and
130
+ * records the positions and their count when it is not zero.
282
131
  *
283
- * What this does NOT do: it cannot stop a caller writing `accepted` anyway.
284
- * This module reads and does not write the record. proof#162 ratified what the
285
- * record layer does instead (Phil, 2026-09-22): `accepted` keeps its meaning
286
- * and the COUNT of these parts rides in an additive `context` key, REQUIRED
287
- * when it is not zero, enforced against SDKs through conformance vectors. So a
288
- * part missing from this list is a part no record built on that rule will ever
289
- * mention.
132
+ * Not the same as "reads `PART_UNCHECKED`": a receiver holding no bytes reads
133
+ * every file part that way, which is the common case.
290
134
  */
291
135
  export declare function uncheckedHeldParts(reading: HeldMessage): number[];