@reclaimprotocol/agent 0.1.14 → 0.1.15
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/lib/old/tools/publish.js +75 -4
- package/package.json +2 -2
- package/skill/injection-api.md +98 -7
- package/src/old/tools/publish.ts +78 -4
package/lib/old/tools/publish.js
CHANGED
|
@@ -189,15 +189,86 @@ export function createVersionFromCaptureTool(client) {
|
|
|
189
189
|
description: 'Extra requests the customInjection script is allowed to ' +
|
|
190
190
|
'fire (e.g. pagination) — add-version ONLY, the register ' +
|
|
191
191
|
'endpoint ignores this. Each item has the same shape as ' +
|
|
192
|
-
'`provider`, plus `multiple`/`required` (default true).'
|
|
192
|
+
'`provider`, plus `multiple`/`required` (default true). ' +
|
|
193
|
+
'IMPORTANT whenever a claim\'s request HAS a body: set `body` ' +
|
|
194
|
+
'here (see its own description below) — if only a small part ' +
|
|
195
|
+
'of the real body varies per user/run (e.g. one id field, not ' +
|
|
196
|
+
'the whole payload), template just that part with ' +
|
|
197
|
+
'`{{paramName}}` and keep the rest as fixed literal text; see ' +
|
|
198
|
+
'the "bodySniff" section of the customInjection field\'s guide ' +
|
|
199
|
+
'for the exact mechanics and a worked example.',
|
|
193
200
|
items: {
|
|
194
201
|
type: 'object',
|
|
195
202
|
properties: {
|
|
196
203
|
url: { type: 'string' },
|
|
197
204
|
method: { type: 'string' },
|
|
198
|
-
body: {
|
|
199
|
-
|
|
200
|
-
|
|
205
|
+
body: {
|
|
206
|
+
type: 'string',
|
|
207
|
+
description: 'Becomes `bodySniff: { enabled: true, template: <this ' +
|
|
208
|
+
'string> }` — set this whenever the request has a ' +
|
|
209
|
+
'body at all (skip only for a genuinely bodyless ' +
|
|
210
|
+
'request, typically GET). If the real body is FIXED ' +
|
|
211
|
+
'(identical for every user/run), put that literal body ' +
|
|
212
|
+
'text here verbatim. If only PART of it varies (e.g. ' +
|
|
213
|
+
'`{"request":1,"type":"user_info","id":"12345"}` where ' +
|
|
214
|
+
'only `id` differs per user), template ONLY that part: ' +
|
|
215
|
+
'`{"request":1,"type":"user_info",' +
|
|
216
|
+
'"id":"{{REQ_BODY_USER_ID}}"}` — keep everything else ' +
|
|
217
|
+
'as fixed literal text rather than templating the whole ' +
|
|
218
|
+
'body. Confirmed directly against attestor-core\'s ' +
|
|
219
|
+
'`hashProviderParams`: the hash embeds this string ' +
|
|
220
|
+
'LITERALLY, unresolved braces and all, never the real ' +
|
|
221
|
+
'substituted body, so a claim built with the SAME ' +
|
|
222
|
+
'literal template (plus a witness/extracted param per ' +
|
|
223
|
+
'placeholder, supplying the real value for ' +
|
|
224
|
+
'verification) hashes identically regardless of what ' +
|
|
225
|
+
'those real values are. This is the OPPOSITE of ' +
|
|
226
|
+
'`templateParams`\' `${var}` syntax below, which ' +
|
|
227
|
+
'genuinely IS substituted before hashing — do not ' +
|
|
228
|
+
'confuse `{{param}}` (body, never resolved) with ' +
|
|
229
|
+
'`${var}` (responseMatches/responseRedactions, resolved ' +
|
|
230
|
+
'at verify time). Before publishing, hand-verify that ' +
|
|
231
|
+
'substituting real sample values into this template ' +
|
|
232
|
+
'reproduces the real captured request body ' +
|
|
233
|
+
'byte-for-byte. Fallback if per-field templating keeps ' +
|
|
234
|
+
'mismatching (more fields vary than expected, unstable ' +
|
|
235
|
+
'key order, etc.) and you can\'t pin down every ' +
|
|
236
|
+
'variable field: template the WHOLE body as one ' +
|
|
237
|
+
'placeholder instead, `{{REQ_BODY_GRD}}` — the `GRD` ' +
|
|
238
|
+
'suffix makes it a greedy match (captures everything) ' +
|
|
239
|
+
'rather than the default non-greedy match, which is ' +
|
|
240
|
+
'needed when nothing fixed follows the placeholder. ' +
|
|
241
|
+
'This pins down nothing about body structure, so prefer ' +
|
|
242
|
+
'minimal per-field placeholders whenever you can ' +
|
|
243
|
+
'actually identify which fields vary.',
|
|
244
|
+
},
|
|
245
|
+
responseMatches: {
|
|
246
|
+
type: 'array',
|
|
247
|
+
description: 'Each item: `{ value: string, type?: "contains" | ' +
|
|
248
|
+
'"regex", invert?: boolean, isOptional?: boolean }`. ' +
|
|
249
|
+
'`value` must literally appear (or match, if `regex`) ' +
|
|
250
|
+
'somewhere in the response body for the claim to be ' +
|
|
251
|
+
'accepted; it commonly embeds one or more ' +
|
|
252
|
+
'`{{paramName}}` extraction placeholders (e.g. ' +
|
|
253
|
+
'`"name":"{{name_0}}"`) whose real value is redacted ' +
|
|
254
|
+
'out and supplied separately as a witness/extracted ' +
|
|
255
|
+
'param. Use `${var}` (not `{{var}}`) inside this string ' +
|
|
256
|
+
'only if you also declare that name in this entry\'s ' +
|
|
257
|
+
'own `templateParams` below — that\'s a DIFFERENT, ' +
|
|
258
|
+
'verify-time-substituted placeholder, resolved from the ' +
|
|
259
|
+
'submitted proof before hashing (unlike a body ' +
|
|
260
|
+
'`{{param}}`, which never resolves — see `body` above).',
|
|
261
|
+
},
|
|
262
|
+
responseRedactions: {
|
|
263
|
+
type: 'array',
|
|
264
|
+
description: 'Each item: `{ jsonPath?: string, xPath?: string, ' +
|
|
265
|
+
'regex?: string, hash?: string }`. Identifies WHICH ' +
|
|
266
|
+
'part of the response the matching `{{paramName}}` in ' +
|
|
267
|
+
'`responseMatches` actually redacts/extracts — e.g. ' +
|
|
268
|
+
'`jsonPath: "$.data.items[0].name"` for a JSON response. ' +
|
|
269
|
+
'Order/count should line up with `responseMatches`\' ' +
|
|
270
|
+
'placeholders one-to-one for a typical claim.',
|
|
271
|
+
},
|
|
201
272
|
multiple: {
|
|
202
273
|
type: 'boolean',
|
|
203
274
|
description: 'Allow matching many requests. Default true.',
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reclaimprotocol/agent",
|
|
3
3
|
"title": "Reclaim Agent",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.15",
|
|
5
5
|
"description": "Reclaim Protocol's tools for agents — Reclaim Protocol lets your app verify **data points** about its users — a follower count, an account balance, an order history. This package gives your AI coding agent the tools to **find, build, test and fix the _data verification providers_** (the recipes that verify a data point) by inspecting and replaying captured requests, checking sessions, evaluate proofs, publishing updates, and much more.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./lib/index.js",
|
|
@@ -86,7 +86,7 @@
|
|
|
86
86
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
87
87
|
"@noble/hashes": "^2.2.0",
|
|
88
88
|
"@reclaimprotocol/attestor-core": "^5.0.8",
|
|
89
|
-
"@reclaimprotocol/client": "0.1.
|
|
89
|
+
"@reclaimprotocol/client": "0.1.15",
|
|
90
90
|
"@reclaimprotocol/tls": "^0.1.3",
|
|
91
91
|
"chrome-remote-interface": "^0.33.0",
|
|
92
92
|
"domhandler": "^6.0.0",
|
package/skill/injection-api.md
CHANGED
|
@@ -133,13 +133,104 @@ field isn't touched by `create_provider_version_from_capture` at all).
|
|
|
133
133
|
## `bodySniff` — unrelated to the above
|
|
134
134
|
|
|
135
135
|
A per-request field (sits next to `url`/`method` on each `requestData` entry,
|
|
136
|
-
not a version-level setting): `{ enabled: boolean, template: string }`.
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
136
|
+
not a version-level setting): `{ enabled: boolean, template: string }`.
|
|
137
|
+
|
|
138
|
+
**`bodySniff.enabled` should be `true` whenever the request HAS a body at
|
|
139
|
+
all** — this isn't a special case reserved for dynamic bodies. Only skip it
|
|
140
|
+
(leave `enabled: false` / template empty) when the request genuinely sends
|
|
141
|
+
no body (typically a GET). Both publish tools in this package already set
|
|
142
|
+
`enabled`/`template` for you from `provider.body` for a normal
|
|
143
|
+
capture-and-replay provider — you don't need to touch it directly there.
|
|
144
|
+
Read the rest of this section if you're authoring a claim via live
|
|
145
|
+
interception (`window.Reclaim.requestClaim`) whose body isn't simply the
|
|
146
|
+
one literal string you captured once — most commonly because some SMALL
|
|
147
|
+
part of it is dynamic per user/run (e.g. a user id, not necessarily the
|
|
148
|
+
whole payload).
|
|
149
|
+
|
|
150
|
+
**What actually gets hashed.** Confirmed directly from attestor-core
|
|
151
|
+
(`hashProviderParams` in `@reclaimprotocol/attestor-core`): the claim/spec
|
|
152
|
+
hash is `keccak256(canonicalStringify({ url, method, body, responseMatches,
|
|
153
|
+
responseRedactions }))`, where `body` is whatever plain string sits in
|
|
154
|
+
`params.body` — attestor-core does **no** template resolution, substitution,
|
|
155
|
+
or `{{}}`-stripping on it; it hashes exactly the string it's given, verbatim.
|
|
156
|
+
`bodySniff` as a concept (the `{enabled, template}` wrapper) doesn't exist
|
|
157
|
+
inside attestor-core at all — it's translated to a plain `body: string`
|
|
158
|
+
one layer up (old-devtools' `providerUtils.ts`: `body: bodySniff.enabled ?
|
|
159
|
+
bodySniff.template : ''`), and THAT literal string — including any
|
|
160
|
+
unresolved `{{paramName}}` text still in it — is what reaches
|
|
161
|
+
`hashProviderParams`.
|
|
162
|
+
|
|
163
|
+
**The consequence — and why this differs from `templateParams`.** A live
|
|
164
|
+
claim's own `body` field (submitted via `requestClaim`) can ALSO be set to
|
|
165
|
+
a literal template string containing `{{paramName}}`, rather than the real
|
|
166
|
+
substituted content, with the real value supplied separately as a witness
|
|
167
|
+
param. Since attestor-core hashes `body` as an opaque string with no
|
|
168
|
+
resolution, a claim whose `body` is the literal template `T` and a spec
|
|
169
|
+
whose `body`/`bodySniff.template` is that SAME literal template `T` hash
|
|
170
|
+
identically — regardless of what real value the `{{paramName}}` hole stood
|
|
171
|
+
for. This is the OPPOSITE of `templateParams`/`${var}` (used in
|
|
172
|
+
`responseMatches`/`responseRedactions`), which — per the JS SDK's
|
|
173
|
+
`generateSpecsFromRequestSpecTemplate` — genuinely gets substituted with a
|
|
174
|
+
real value at verify time before hashing. Don't conflate the two: `${var}`
|
|
175
|
+
in a match/redaction template IS resolved before hashing; `{{var}}` in a
|
|
176
|
+
body template is NEVER resolved before hashing, on either side.
|
|
177
|
+
|
|
178
|
+
**Practical use**: check exactly which part of the body actually changes
|
|
179
|
+
across users/runs — usually it's one small field (an id, a token, a short
|
|
180
|
+
list), not the whole payload. Template ONLY that minimal changing part;
|
|
181
|
+
leave everything else as fixed literal text. For a body like
|
|
182
|
+
`{"request":1,"type":"user_info","id":"12345"}` where only `id` varies, the
|
|
183
|
+
template is `{"request":1,"type":"user_info","id":"{{REQ_BODY_USER_ID}}"}`
|
|
184
|
+
— NOT e.g. `{{whole_body}}` wrapping the entire object, which would throw
|
|
185
|
+
away the ability to structurally pin down everything else about the
|
|
186
|
+
request. Then:
|
|
187
|
+
1. In the claim you build for `requestClaim`, set `bodySniff: { enabled:
|
|
188
|
+
true, template: '{"request":1,"type":"user_info","id":"{{REQ_BODY_USER_ID}}"}' }`
|
|
189
|
+
and add a matching witness/extracted param
|
|
190
|
+
`REQ_BODY_USER_ID: theRealUserId` so the real value is still
|
|
191
|
+
independently verifiable, just not part of the hash.
|
|
192
|
+
2. In the corresponding `allowedInjectedRequestData` entry, set `body` to
|
|
193
|
+
that EXACT SAME literal template string (byte-for-byte, same
|
|
194
|
+
`{{REQ_BODY_USER_ID}}` placeholder, same everything else) — NOT a
|
|
195
|
+
`${var}` substitution, since `body` doesn't go through that substitution
|
|
196
|
+
step.
|
|
197
|
+
3. Before publishing, sanity-check by hand that
|
|
198
|
+
`template.replace('{{REQ_BODY_USER_ID}}', 'a real sample id')`
|
|
199
|
+
reproduces the real captured/expected request body byte-for-byte — if it
|
|
200
|
+
doesn't (extra whitespace, different key order, a field that turned out
|
|
201
|
+
to ALSO vary but wasn't templated, etc.), the hashes won't match either,
|
|
202
|
+
since the SPEC side uses the literal template as-is and the real claim's
|
|
203
|
+
body must independently be built to match that same shape when the
|
|
204
|
+
placeholder(s) are filled in. If MULTIPLE fields vary independently,
|
|
205
|
+
template each one with its own `{{placeholder}}` and its own witness
|
|
206
|
+
param — you're not limited to one hole per template.
|
|
207
|
+
|
|
208
|
+
**Fallback — whole-body placeholder.** If precise per-field templating
|
|
209
|
+
keeps causing hash mismatches (e.g. more fields turn out to vary than
|
|
210
|
+
expected, key order isn't stable, whitespace differs run-to-run) and you
|
|
211
|
+
can't pin down every variable field individually, fall back to templating
|
|
212
|
+
the ENTIRE body as one placeholder instead: `{{REQ_BODY_GRD}}`. The `GRD`
|
|
213
|
+
suffix matters — per the response-matching template engine
|
|
214
|
+
(`convertTemplateToRegex`), a placeholder name ending in `GRD` is treated as
|
|
215
|
+
a GREEDY match (`(.*)`, matching everything including embedded delimiters)
|
|
216
|
+
rather than the default non-greedy `(.*?)` (which stops at the first
|
|
217
|
+
occurrence of whatever comes after it in the template) — for a placeholder
|
|
218
|
+
covering the entire remaining body with nothing fixed after it, greedy is
|
|
219
|
+
what you want. This is strictly a fallback: it means NOTHING about the
|
|
220
|
+
body's structure is pinned down by the hash anymore (any body at all would
|
|
221
|
+
hash the same), so prefer the minimal-placeholder approach above whenever
|
|
222
|
+
you can actually identify which fields vary.
|
|
223
|
+
|
|
224
|
+
Caveat: I confirmed `hashProviderParams` treats `body` as an opaque string
|
|
225
|
+
with no `{{}}` resolution directly from the attestor-core bundle shipped in
|
|
226
|
+
this repo's `node_modules`. I did NOT find, in this same bundle, the code
|
|
227
|
+
path that actually invokes `hashProviderParams` against a live submitted
|
|
228
|
+
claim to gate proof acceptance — that caller lives elsewhere (the mobile/
|
|
229
|
+
InApp SDK client and/or a server-side verifier not checked into this repo).
|
|
230
|
+
So this is strong but not 100% end-to-end-traced confirmation; treat it as
|
|
231
|
+
"very likely correct, empirically consistent with real proof/verifyProof
|
|
232
|
+
behavior observed in practice" rather than "certain from first-hand
|
|
233
|
+
tracing of the whole call chain."
|
|
143
234
|
|
|
144
235
|
## Recognizing this on a provider you didn't author
|
|
145
236
|
|
package/src/old/tools/publish.ts
CHANGED
|
@@ -240,15 +240,89 @@ export function createVersionFromCaptureTool(
|
|
|
240
240
|
'Extra requests the customInjection script is allowed to ' +
|
|
241
241
|
'fire (e.g. pagination) — add-version ONLY, the register ' +
|
|
242
242
|
'endpoint ignores this. Each item has the same shape as ' +
|
|
243
|
-
'`provider`, plus `multiple`/`required` (default true).'
|
|
243
|
+
'`provider`, plus `multiple`/`required` (default true). ' +
|
|
244
|
+
'IMPORTANT whenever a claim\'s request HAS a body: set `body` ' +
|
|
245
|
+
'here (see its own description below) — if only a small part ' +
|
|
246
|
+
'of the real body varies per user/run (e.g. one id field, not ' +
|
|
247
|
+
'the whole payload), template just that part with ' +
|
|
248
|
+
'`{{paramName}}` and keep the rest as fixed literal text; see ' +
|
|
249
|
+
'the "bodySniff" section of the customInjection field\'s guide ' +
|
|
250
|
+
'for the exact mechanics and a worked example.',
|
|
244
251
|
items: {
|
|
245
252
|
type: 'object',
|
|
246
253
|
properties: {
|
|
247
254
|
url: { type: 'string' },
|
|
248
255
|
method: { type: 'string' },
|
|
249
|
-
body: {
|
|
250
|
-
|
|
251
|
-
|
|
256
|
+
body: {
|
|
257
|
+
type: 'string',
|
|
258
|
+
description:
|
|
259
|
+
'Becomes `bodySniff: { enabled: true, template: <this ' +
|
|
260
|
+
'string> }` — set this whenever the request has a ' +
|
|
261
|
+
'body at all (skip only for a genuinely bodyless ' +
|
|
262
|
+
'request, typically GET). If the real body is FIXED ' +
|
|
263
|
+
'(identical for every user/run), put that literal body ' +
|
|
264
|
+
'text here verbatim. If only PART of it varies (e.g. ' +
|
|
265
|
+
'`{"request":1,"type":"user_info","id":"12345"}` where ' +
|
|
266
|
+
'only `id` differs per user), template ONLY that part: ' +
|
|
267
|
+
'`{"request":1,"type":"user_info",' +
|
|
268
|
+
'"id":"{{REQ_BODY_USER_ID}}"}` — keep everything else ' +
|
|
269
|
+
'as fixed literal text rather than templating the whole ' +
|
|
270
|
+
'body. Confirmed directly against attestor-core\'s ' +
|
|
271
|
+
'`hashProviderParams`: the hash embeds this string ' +
|
|
272
|
+
'LITERALLY, unresolved braces and all, never the real ' +
|
|
273
|
+
'substituted body, so a claim built with the SAME ' +
|
|
274
|
+
'literal template (plus a witness/extracted param per ' +
|
|
275
|
+
'placeholder, supplying the real value for ' +
|
|
276
|
+
'verification) hashes identically regardless of what ' +
|
|
277
|
+
'those real values are. This is the OPPOSITE of ' +
|
|
278
|
+
'`templateParams`\' `${var}` syntax below, which ' +
|
|
279
|
+
'genuinely IS substituted before hashing — do not ' +
|
|
280
|
+
'confuse `{{param}}` (body, never resolved) with ' +
|
|
281
|
+
'`${var}` (responseMatches/responseRedactions, resolved ' +
|
|
282
|
+
'at verify time). Before publishing, hand-verify that ' +
|
|
283
|
+
'substituting real sample values into this template ' +
|
|
284
|
+
'reproduces the real captured request body ' +
|
|
285
|
+
'byte-for-byte. Fallback if per-field templating keeps ' +
|
|
286
|
+
'mismatching (more fields vary than expected, unstable ' +
|
|
287
|
+
'key order, etc.) and you can\'t pin down every ' +
|
|
288
|
+
'variable field: template the WHOLE body as one ' +
|
|
289
|
+
'placeholder instead, `{{REQ_BODY_GRD}}` — the `GRD` ' +
|
|
290
|
+
'suffix makes it a greedy match (captures everything) ' +
|
|
291
|
+
'rather than the default non-greedy match, which is ' +
|
|
292
|
+
'needed when nothing fixed follows the placeholder. ' +
|
|
293
|
+
'This pins down nothing about body structure, so prefer ' +
|
|
294
|
+
'minimal per-field placeholders whenever you can ' +
|
|
295
|
+
'actually identify which fields vary.',
|
|
296
|
+
},
|
|
297
|
+
responseMatches: {
|
|
298
|
+
type: 'array',
|
|
299
|
+
description:
|
|
300
|
+
'Each item: `{ value: string, type?: "contains" | ' +
|
|
301
|
+
'"regex", invert?: boolean, isOptional?: boolean }`. ' +
|
|
302
|
+
'`value` must literally appear (or match, if `regex`) ' +
|
|
303
|
+
'somewhere in the response body for the claim to be ' +
|
|
304
|
+
'accepted; it commonly embeds one or more ' +
|
|
305
|
+
'`{{paramName}}` extraction placeholders (e.g. ' +
|
|
306
|
+
'`"name":"{{name_0}}"`) whose real value is redacted ' +
|
|
307
|
+
'out and supplied separately as a witness/extracted ' +
|
|
308
|
+
'param. Use `${var}` (not `{{var}}`) inside this string ' +
|
|
309
|
+
'only if you also declare that name in this entry\'s ' +
|
|
310
|
+
'own `templateParams` below — that\'s a DIFFERENT, ' +
|
|
311
|
+
'verify-time-substituted placeholder, resolved from the ' +
|
|
312
|
+
'submitted proof before hashing (unlike a body ' +
|
|
313
|
+
'`{{param}}`, which never resolves — see `body` above).',
|
|
314
|
+
},
|
|
315
|
+
responseRedactions: {
|
|
316
|
+
type: 'array',
|
|
317
|
+
description:
|
|
318
|
+
'Each item: `{ jsonPath?: string, xPath?: string, ' +
|
|
319
|
+
'regex?: string, hash?: string }`. Identifies WHICH ' +
|
|
320
|
+
'part of the response the matching `{{paramName}}` in ' +
|
|
321
|
+
'`responseMatches` actually redacts/extracts — e.g. ' +
|
|
322
|
+
'`jsonPath: "$.data.items[0].name"` for a JSON response. ' +
|
|
323
|
+
'Order/count should line up with `responseMatches`\' ' +
|
|
324
|
+
'placeholders one-to-one for a typical claim.',
|
|
325
|
+
},
|
|
252
326
|
multiple: {
|
|
253
327
|
type: 'boolean',
|
|
254
328
|
description: 'Allow matching many requests. Default true.',
|