@reclaimprotocol/agent 0.1.29 → 0.1.31

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 (89) hide show
  1. package/README.md +28 -9
  2. package/lib/consts.d.ts +5 -5
  3. package/lib/consts.js +7 -7
  4. package/lib/live/tunnel-watchdog.d.ts +1 -0
  5. package/lib/live/tunnel-watchdog.js +66 -0
  6. package/lib/live/tunnel.d.ts +30 -7
  7. package/lib/live/tunnel.js +217 -40
  8. package/lib/mcp/api/operations.gen.d.ts +12 -0
  9. package/lib/mcp/api/operations.gen.js +537 -59
  10. package/lib/mcp/api/tools.js +27 -5
  11. package/lib/mcp/instructions.js +23 -32
  12. package/lib/mcp/remap_errors.js +6 -0
  13. package/lib/mcp/server.js +2 -8
  14. package/lib/mcp/start.js +3 -5
  15. package/lib/mcp/success-response.d.ts +5 -0
  16. package/lib/mcp/success-response.js +21 -0
  17. package/lib/mcp/tools/agent/attach.js +9 -66
  18. package/lib/mcp/tools/agent/build.d.ts +0 -3
  19. package/lib/mcp/tools/agent/build.js +3 -4
  20. package/lib/mcp/tools/agent/how-it-works.d.ts +5 -15
  21. package/lib/mcp/tools/agent/how-it-works.js +9 -22
  22. package/lib/mcp/tools/agent/live-view.d.ts +1 -15
  23. package/lib/mcp/tools/agent/live-view.js +71 -96
  24. package/lib/mcp/tools/agent/propose.js +7 -23
  25. package/lib/mcp/tools/agent/publish.js +80 -18
  26. package/lib/mcp/tools/agent/session.d.ts +4 -7
  27. package/lib/mcp/tools/agent/session.js +8 -23
  28. package/lib/mcp/tools/verification/result.d.ts +2 -1
  29. package/lib/mcp/tools/verification/result.js +29 -28
  30. package/lib/old/client.d.ts +2 -2
  31. package/lib/old/flag.d.ts +2 -5
  32. package/lib/old/flag.js +2 -5
  33. package/lib/old/instructions.d.ts +1 -1
  34. package/lib/old/instructions.js +1 -1
  35. package/lib/old/to-register.d.ts +4 -3
  36. package/lib/old/tools/publish.js +11 -14
  37. package/lib/proof/attestor-auth.d.ts +9 -1
  38. package/lib/proof/attestor-auth.js +15 -2
  39. package/lib/proof/attestor.d.ts +2 -2
  40. package/lib/proof/attestor.js +20 -2
  41. package/lib/proof/pod-prover.js +1 -1
  42. package/lib/proof/proof-core.d.ts +4 -0
  43. package/lib/proof/proof-core.js +20 -0
  44. package/lib/provider/injection-doc.d.ts +3 -2
  45. package/lib/provider/injection-doc.js +12 -10
  46. package/lib/provider/matcher.js +19 -15
  47. package/lib/provider/to-version.d.ts +3 -0
  48. package/lib/provider/to-version.js +2 -1
  49. package/package.json +3 -3
  50. package/skill/SKILL.builder.md +30 -83
  51. package/skill/SKILL.common.md +38 -22
  52. package/skill/SKILL.old.md +17 -3
  53. package/skill/builder-mode.md +37 -4
  54. package/skill/community-contributions.md +12 -3
  55. package/skill/hash-validation.md +9 -9
  56. package/skill/interception.md +39 -46
  57. package/skill/old-mode.md +13 -0
  58. package/skill/user-script.md +24 -8
  59. package/skill/verification-client-integration.md +130 -0
  60. package/src/consts.ts +9 -7
  61. package/src/mcp/api/operations.gen.ts +539 -59
  62. package/src/mcp/api/tools.ts +38 -7
  63. package/src/mcp/instructions.ts +33 -32
  64. package/src/mcp/remap_errors.ts +6 -0
  65. package/src/mcp/server.ts +2 -8
  66. package/src/mcp/start.ts +3 -5
  67. package/src/mcp/success-response.ts +26 -0
  68. package/src/mcp/tools/agent/attach.ts +9 -71
  69. package/src/mcp/tools/agent/build.ts +4 -7
  70. package/src/mcp/tools/agent/how-it-works.ts +9 -22
  71. package/src/mcp/tools/agent/live-view.ts +76 -116
  72. package/src/mcp/tools/agent/propose.ts +7 -23
  73. package/src/mcp/tools/agent/publish.ts +89 -18
  74. package/src/mcp/tools/agent/session.ts +6 -23
  75. package/src/mcp/tools/verification/result.ts +30 -29
  76. package/src/old/client.ts +2 -2
  77. package/src/old/flag.ts +2 -5
  78. package/src/old/instructions.ts +1 -1
  79. package/src/old/to-register.ts +4 -3
  80. package/src/old/tools/publish.ts +16 -14
  81. package/src/proof/attestor-auth.ts +18 -1
  82. package/src/proof/attestor.ts +24 -3
  83. package/src/proof/pod-prover.ts +1 -1
  84. package/src/proof/proof-core.ts +28 -0
  85. package/src/provider/injection-doc.ts +12 -10
  86. package/src/provider/matcher.ts +25 -17
  87. package/src/provider/to-version.ts +7 -1
  88. package/src/live/cloudflared.ts +0 -348
  89. package/src/live/tunnel.ts +0 -254
package/lib/old/flag.js CHANGED
@@ -2,11 +2,8 @@
2
2
  * `USE_OLD_DEVTOOLS` — single source of truth for which backend the MCP
3
3
  * server targets.
4
4
  *
5
- * Defaults to OLD devtools (the system still in production) while the new
6
- * "builder" backend is under development. Builder mode must be opted into
7
- * EXPLICITLY by setting `USE_OLD_DEVTOOLS=false`. Anything else — unset,
8
- * empty, `true`, `1`, a typo — keeps old mode on, so a forgotten env var
9
- * can never silently route a developer at the half-built builder backend.
5
+ * Builder is the default. Only `true` or `1` explicitly selects old devtools;
6
+ * an unset value or typo stays on Builder.
10
7
  *
11
8
  * The actual env read lives in `../consts.ts` (the single env-access module);
12
9
  * this widely-imported path re-exports it for back-compat.
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * MCP server instructions shown to the LLM when running in OLD-devtools mode
3
- * (`USE_OLD_DEVTOOLS` unset or not `false`). Describes the REDUCED tool
3
+ * (`USE_OLD_DEVTOOLS=true` or `1`). Describes the REDUCED tool
4
4
  * surface — there are no org/credential-link/review flows and no generated
5
5
  * API tools here. Both modes' instructions load together in
6
6
  * `mcp/instructions` (alongside the topic-split authoring guide); re-exported
@@ -1,6 +1,6 @@
1
1
  /**
2
2
  * MCP server instructions shown to the LLM when running in OLD-devtools mode
3
- * (`USE_OLD_DEVTOOLS` unset or not `false`). Describes the REDUCED tool
3
+ * (`USE_OLD_DEVTOOLS=true` or `1`). Describes the REDUCED tool
4
4
  * surface — there are no org/credential-link/review flows and no generated
5
5
  * API tools here. Both modes' instructions load together in
6
6
  * `mcp/instructions` (alongside the topic-split authoring guide); re-exported
@@ -61,9 +61,10 @@ export interface OldProviderOpts {
61
61
  * .interceptorOptions.isDocumentRequestReplayEnabled`, is likewise always
62
62
  * force-disabled by `providerToCreateVersionRequest`. */
63
63
  disableRequestReplay?: boolean;
64
- /** Interception mechanism — `NONE` (the publish tool's default) for the
65
- * standard capture→replay flow; `HAWKEYE`/`MSWJS`/`XHOOK`/`CDP` for a
66
- * genuine live-interception provider. Old-devtools calls this
64
+ /** Interception mechanism. Omit it to use the backend's `HAWKEYE` default;
65
+ * use `NONE` only when disabling interception after diagnosis.
66
+ * `HAWKEYE`/`MSWJS`/`XHOOK`/`CDP` enable live interception. Old-devtools
67
+ * calls this
67
68
  * `injectionType`; builder's equivalent is `webSettings.clientOptions
68
69
  * .inapp.interceptorOptions.interceptorType` (no `NONE` value there —
69
70
  * omitting the whole object is builder's no-interception signal
@@ -373,20 +373,12 @@ export function createVersionFromCaptureTool(client, attachRef) {
373
373
  injectionType: {
374
374
  type: 'string',
375
375
  enum: ['NONE', 'MSWJS', 'XHOOK', 'CDP', 'HAWKEYE'],
376
- description: 'Interception mechanism for this version. Omit to leave it ' +
377
- 'unset — the devtools backend\'s own column default ' +
378
- '(`HAWKEYE`) applies, which is inert and harmless for the ' +
379
- 'standard capture→replay flow with no interceptor script ' +
380
- 'installed. Only set `HAWKEYE`/`MSWJS`/`XHOOK` when you are ' +
381
- 'ALSO supplying a `customInjection` script that calls ' +
382
- '`window.reclaimInterceptor.addResponseMiddleware(...)` for ' +
383
- 'genuine live interception, or set `NONE` explicitly to be ' +
384
- 'certain none is configured. Builder calls the equivalent ' +
385
- 'concept `interceptorType` (no `NONE` there — omitting the ' +
386
- 'whole `interceptorOptions` object is its no-interception ' +
387
- 'signal instead). ' + COOKIE_ATTACHMENT_HINT +
388
- 'What each value actually does: ' +
389
- 'how_it_works({ topic: "interception" }).',
376
+ description: 'Live-interception mechanism. Omit it for the standard flow. ' +
377
+ 'Use `NONE` only to confirm a suspected interception problem; ' +
378
+ 'if that fixes the provider, keep `NONE` and use ' +
379
+ '`window.Reclaim.requestClaim(...)`. For selection rules, call ' +
380
+ 'how_it_works({ topic: "interception" }). ' +
381
+ COOKIE_ATTACHMENT_HINT,
390
382
  },
391
383
  disableRequestReplay: {
392
384
  type: 'boolean',
@@ -407,6 +399,11 @@ export function createVersionFromCaptureTool(client, attachRef) {
407
399
  assert(!(args.version && args.bump), badRequest('Pass either `version` or `bump`, not both.'));
408
400
  assert(args.providerId || (!args.version && !args.bump), badRequest('`version` and `bump` require `providerId`; new-provider ' +
409
401
  'registration starts at the backend-defined version.'));
402
+ assert(args.injectionType !== 'NONE'
403
+ || !args.customInjection?.includes('addResponseMiddleware'), badRequest('`injectionType: NONE` cannot deliver ' +
404
+ '`addResponseMiddleware` traffic. Use an interceptor type, or ' +
405
+ 'keep `NONE` and replace intercepted requests with ' +
406
+ '`window.Reclaim.requestClaim(...)`.'));
410
407
  // `provider` / `providers` are just two spellings of the same input —
411
408
  // fold them into one array. Zero drafts is allowed ONLY when adding a
412
409
  // version to an EXISTING provider (`providerId` set): that publishes
@@ -9,5 +9,13 @@ export interface AttestorAuthentication {
9
9
  /**
10
10
  * Create legacy-compatible attestor authorization when the deployment has an
11
11
  * auth private key. An absent key intentionally means authentication is off.
12
+ *
13
+ * `expiresInSeconds` overrides the attestor library's default lifetime. A
14
+ * zkFetch authorization uses a very short one: the attestor validates expiry
15
+ * once, when it authenticates the connection, and never again for the life of
16
+ * that connection — so the window only has to cover the caller receiving the
17
+ * token and opening its socket, not the proof that follows.
12
18
  */
13
- export declare function createAttestorAuthentication(sessionId: string, privateKey: string | undefined): Promise<AttestorAuthentication | undefined>;
19
+ export declare function createAttestorAuthentication(sessionId: string, privateKey: string | undefined, opts?: {
20
+ expiresInSeconds?: number;
21
+ }): Promise<AttestorAuthentication | undefined>;
@@ -2,12 +2,25 @@ import { createAuthRequest } from '@reclaimprotocol/attestor-core';
2
2
  /**
3
3
  * Create legacy-compatible attestor authorization when the deployment has an
4
4
  * auth private key. An absent key intentionally means authentication is off.
5
+ *
6
+ * `expiresInSeconds` overrides the attestor library's default lifetime. A
7
+ * zkFetch authorization uses a very short one: the attestor validates expiry
8
+ * once, when it authenticates the connection, and never again for the life of
9
+ * that connection — so the window only has to cover the caller receiving the
10
+ * token and opening its socket, not the proof that follows.
5
11
  */
6
- export async function createAttestorAuthentication(sessionId, privateKey) {
12
+ export async function createAttestorAuthentication(sessionId, privateKey, opts = {}) {
7
13
  if (!privateKey?.trim()) {
8
14
  return undefined;
9
15
  }
10
- const request = await createAuthRequest({ id: sessionId }, privateKey);
16
+ const request = await createAuthRequest({
17
+ id: sessionId,
18
+ ...(opts.expiresInSeconds === undefined
19
+ ? {}
20
+ : {
21
+ expiresAt: Math.floor(Date.now() / 1000) + opts.expiresInSeconds,
22
+ }),
23
+ }, privateKey);
11
24
  const token = Buffer.from(JSON.stringify({
12
25
  ...request,
13
26
  signature: Buffer.from(request.signature).toString('base64'),
@@ -36,7 +36,7 @@ export interface RunAttestorProofOptions {
36
36
  * want gnark's speed pin it explicitly (for example, the verify runner). */
37
37
  zkEngine?: 'gnark' | 'snarkjs' | 'stwo';
38
38
  /** Extract the structured {@link AttestorClaim} (claim + attestor signature)
39
- * onto the result for legacy Proof construction. */
39
+ * onto the result for Proof construction. */
40
40
  includeAttestorClaim?: boolean;
41
41
  }
42
42
  export interface ProofResult {
@@ -47,7 +47,7 @@ export interface ProofResult {
47
47
  owner?: string;
48
48
  verified: boolean;
49
49
  error?: string;
50
- /** Structured claim + signature for legacy Proof verification. Set only when
50
+ /** Structured claim + signature for Proof verification. Set only when
51
51
  * `includeAttestorClaim` was requested and the claim carries signatures. */
52
52
  attestorClaim?: AttestorClaim;
53
53
  /** Set to `'missing-owner-key'` when no credential was available to
@@ -14,7 +14,7 @@ import "./patch-domhandler.js";
14
14
  import { agentTraceEnabled } from "../consts.js";
15
15
  import { resolveEthKey } from "../mcp/tools/authenticate/eth-key.js";
16
16
  import { installResponseDump } from "./debug-response-dump.js";
17
- import { buildHttpParams, ensureAttestorCrypto, parseClaim, resolveAttestorUrl } from "./proof-core.js";
17
+ import { buildHttpParams, claimRejection, ensureAttestorCrypto, parseClaim, resolveAttestorUrl } from "./proof-core.js";
18
18
  import { ensureZkFiles } from "./zk-files.js";
19
19
  /** Trim a captured log stream to the last N entries — for shrinking the
20
20
  * LLM-visible response while the full stream stays in the on-disk dump. */
@@ -180,6 +180,24 @@ export async function runAttestorProof(provider, secrets, opts = {}) {
180
180
  }
181
181
  return errorResult;
182
182
  }
183
+ // The attestor reports a rejected claim (for example, an invalid ZK proof)
184
+ // in the response rather than by throwing, so a returned value alone is
185
+ // not a proof: it needs a claim, a claim signature, and no error.
186
+ const rejection = claimRejection(claim);
187
+ if (rejection !== undefined) {
188
+ const rejected = {
189
+ proof: claim,
190
+ extractedValue: '',
191
+ extractedParameters: {},
192
+ verified: false,
193
+ error: rejection,
194
+ sentParams: params,
195
+ };
196
+ if (attestorLogs !== undefined) {
197
+ rejected.attestorLogs = attestorLogs;
198
+ }
199
+ return rejected;
200
+ }
183
201
  const { extractedValue, extractedParameters, identifier, owner, claimData } = parseClaim(claim);
184
202
  const result = {
185
203
  proof: claim,
@@ -194,7 +212,7 @@ export async function runAttestorProof(provider, secrets, opts = {}) {
194
212
  result.owner = owner;
195
213
  }
196
214
  // Structured claim + signature so the result JWS can carry the exact
197
- // legacy Proof the Consumer verifies independently.
215
+ // Proof the Consumer verifies independently.
198
216
  if (opts.includeAttestorClaim) {
199
217
  const sigs = claim.signatures;
200
218
  if (claimData
@@ -27,7 +27,7 @@
27
27
  * The pod API is unauthenticated (gateway/session is the trust boundary); call
28
28
  * it over the same internal host used for CDP. Consumers verify
29
29
  * `claim_signature` with the legacy SDK's `verifyProof`, so this hands back an
30
- * {@link AttestorClaim} that the app places in the exact legacy `Proof` shape.
30
+ * {@link AttestorClaim} that the app places in the exact `Proof` shape.
31
31
  * `result_signature` is unused by consumers.
32
32
  */
33
33
  import { bytesToHex } from '@noble/hashes/utils.js';
@@ -29,6 +29,10 @@ export interface ParsedClaim {
29
29
  identifier?: string;
30
30
  owner?: string;
31
31
  }
32
+ /** Why the attestor did not sign `claim`, or `undefined` when it did. The
33
+ * attestor returns rejections (for example, an invalid ZK proof) as a
34
+ * response with `error` set and no claim signature, not by throwing. */
35
+ export declare function claimRejection(claim: unknown): string | undefined;
32
36
  /** Pull claimData + decoded `context.extractedParameters` (plus identifier /
33
37
  * owner) out of a `createClaimOnAttestor` result. The full result still
34
38
  * carries signatures the caller may need for nested-proof building. */
@@ -46,6 +46,26 @@ export function buildHttpParams(provider) {
46
46
  ...(geoLocation !== undefined && { geoLocation }),
47
47
  };
48
48
  }
49
+ /** Why the attestor did not sign `claim`, or `undefined` when it did. The
50
+ * attestor returns rejections (for example, an invalid ZK proof) as a
51
+ * response with `error` set and no claim signature, not by throwing. */
52
+ export function claimRejection(claim) {
53
+ const response = claim;
54
+ const error = response?.error;
55
+ if (error?.message || error?.code) {
56
+ return typeof error.message === 'string' && error.message
57
+ ? error.message
58
+ : `attestor error code ${String(error.code)}`;
59
+ }
60
+ if (!response?.claim) {
61
+ return 'attestor returned no claim';
62
+ }
63
+ const signature = response.signatures?.claimSignature;
64
+ if (!(signature instanceof Uint8Array) || !signature.length) {
65
+ return 'attestor did not sign the claim';
66
+ }
67
+ return undefined;
68
+ }
49
69
  /** Pull claimData + decoded `context.extractedParameters` (plus identifier /
50
70
  * owner) out of a `createClaimOnAttestor` result. The full result still
51
71
  * carries signatures the caller may need for nested-proof building. */
@@ -20,6 +20,7 @@ export declare const COOKIE_ATTACHMENT_HINT: string;
20
20
  export declare const USER_SCRIPT_HINT: string;
21
21
  /** Prefixes the `allowedJsRequests` / `allowedInjectedRequestData` field. The
22
22
  * trap it describes is silent and total — a mis-declared entry makes
23
- * `verifyProof({ providerId })` reject EVERY real proof — so the warning
24
- * itself stays in the schema even though its explanation moved to a topic. */
23
+ * `verifyRequestProof({ providerId })` reject EVERY real proof — so the
24
+ * warning itself stays in the schema even though its explanation moved to a
25
+ * topic. */
25
26
  export declare const ALLOWED_REQUESTS_HINT: string;
@@ -28,16 +28,18 @@ export const USER_SCRIPT_HINT = 'Plain JavaScript injected into the provider pag
28
28
  + 'Full guide: how_it_works({ topic: "user-script" }).';
29
29
  /** Prefixes the `allowedJsRequests` / `allowedInjectedRequestData` field. The
30
30
  * trap it describes is silent and total — a mis-declared entry makes
31
- * `verifyProof({ providerId })` reject EVERY real proof — so the warning
32
- * itself stays in the schema even though its explanation moved to a topic. */
31
+ * `verifyRequestProof({ providerId })` reject EVERY real proof — so the
32
+ * warning itself stays in the schema even though its explanation moved to a
33
+ * topic. */
33
34
  export const ALLOWED_REQUESTS_HINT = '⚠️ Only populate this if you intend to gate '
34
- + '`verifyProof(proof, { providerId })`: each entry feeds a client-side '
35
- + 'hash-equality check and its `url` is hashed VERBATIM (no regex/template '
36
- + 'resolution, even with urlType REGEX/TEMPLATE). If the real captured URL '
37
- + 'carries ANY per-session part (nonce, CSRF token, cache-busting '
38
- + 'timestamp), no declared spec can ever hash-match a real proof — '
39
- + 'populating it then makes verifyProof reject every real proof, which is '
40
- + 'worse than leaving it empty. Diff the same endpoint\'s captured URL '
41
- + 'across two sessions first. Mechanics, the body-templating rules and the '
35
+ + '`verifyRequestProof(proof, { providerId })`: each entry feeds a '
36
+ + 'client-side hash-equality check and its `url` is hashed VERBATIM (no '
37
+ + 'regex/template resolution, even with urlType REGEX/TEMPLATE). If the '
38
+ + 'real captured URL carries ANY per-session part (nonce, CSRF token, '
39
+ + 'cache-busting timestamp), no declared spec can ever hash-match a real '
40
+ + 'proof — populating it then makes verifyRequestProof reject every real '
41
+ + 'proof, which is worse than leaving it empty. Diff the same endpoint\'s '
42
+ + 'captured URL across two sessions first. Mechanics, the '
43
+ + 'body-templating rules and the '
42
44
  + 'dangerouslyDisableContentValidation fallback: '
43
45
  + 'how_it_works({ topic: "hash-validation" }).';
@@ -237,42 +237,46 @@ function findJsonPath(body, target) {
237
237
  return undefined;
238
238
  }
239
239
  const candidates = normalizeCandidates(target);
240
- return walk(parsed, '$', candidates);
240
+ const paths = [];
241
+ collectJsonPaths(parsed, '$', candidates, paths);
242
+ if (paths.length > 1) {
243
+ throw new Error(`target value "${target}" occurs at multiple JSON paths `
244
+ + `(${paths.join(', ')}) — choose a value that identifies one field`);
245
+ }
246
+ return paths[0];
241
247
  }
242
- function walk(node, path, candidates) {
248
+ function collectJsonPaths(node, path, candidates, paths) {
249
+ // Two paths are enough to prove ambiguity and keep traversal bounded for a
250
+ // response containing the same primitive many times.
251
+ if (paths.length > 1) {
252
+ return;
253
+ }
243
254
  if (node === null || node === undefined) {
244
- return undefined;
255
+ return;
245
256
  }
246
257
  if (typeof node === 'string' ||
247
258
  typeof node === 'number' ||
248
259
  typeof node === 'boolean') {
249
260
  const str = String(node);
250
261
  if (candidates.includes(str)) {
251
- return path;
262
+ paths.push(path);
252
263
  }
253
- return undefined;
264
+ return;
254
265
  }
255
266
  if (Array.isArray(node)) {
256
267
  for (const [i, element] of node.entries()) {
257
- const found = walk(element, `${path}[${i}]`, candidates);
258
- if (found) {
259
- return found;
260
- }
268
+ collectJsonPaths(element, `${path}[${i}]`, candidates, paths);
261
269
  }
262
- return undefined;
270
+ return;
263
271
  }
264
272
  if (typeof node === 'object') {
265
273
  for (const [k, v] of Object.entries(node)) {
266
274
  const childPath = /^[A-Za-z_][A-Za-z0-9_]*$/.test(k)
267
275
  ? `${path}.${k}`
268
276
  : `${path}["${k}"]`;
269
- const found = walk(v, childPath, candidates);
270
- if (found) {
271
- return found;
272
- }
277
+ collectJsonPaths(v, childPath, candidates, paths);
273
278
  }
274
279
  }
275
- return undefined;
276
280
  }
277
281
  // ---------------------------------------------------------------------------
278
282
  // HTML: element → xPath redaction + `contains "<tag …>{{v}}</tag>"`
@@ -57,6 +57,9 @@ export interface CreateVersionOptions {
57
57
  jsUserScripts?: string;
58
58
  /** Requests the injection script may fire, as `RequestSelectionTemplate`s. */
59
59
  allowedJsRequests?: AllowedJsRequest[];
60
+ /** Portal-only authenticated actions exposed as
61
+ * `window.Reclaim.browserRequest`. */
62
+ browserRequestActions?: NonNullable<CreateProviderVersionRequest['webSettings']['browserRequestActions']>;
60
63
  /** In-app interception options, mapped to
61
64
  * `webSettings.clientOptions.inapp.interceptorOptions` — ONLY when this
62
65
  * is explicitly passed (builder's schema has no `NONE` interceptorType;
@@ -104,7 +104,7 @@ function pickGeoLocation(providers) {
104
104
  return DYNAMIC_GEO;
105
105
  }
106
106
  export function providerToCreateVersionRequest(providers, initialUrl, opts = {}) {
107
- const { notes, version, requiredContext: requestedContext, jsUserScripts, allowedJsRequests, interceptorOptions, } = opts;
107
+ const { notes, version, requiredContext: requestedContext, jsUserScripts, allowedJsRequests, browserRequestActions, interceptorOptions, } = opts;
108
108
  const requiredContext = requestedContext ?? deriveRequiredContext(providers, { allowedJsRequests, jsUserScripts });
109
109
  // Each injected request carries its template flags (default true/true per
110
110
  // RequestSelectionTemplate) on top of the shared request shape.
@@ -157,6 +157,7 @@ export function providerToCreateVersionRequest(providers, initialUrl, opts = {})
157
157
  },
158
158
  // Only set when non-empty; an empty string would store a no-op script.
159
159
  ...(script ? { jsUserScripts: script } : {}),
160
+ ...(browserRequestActions?.length ? { browserRequestActions } : {}),
160
161
  },
161
162
  // Derived from `{{context.<name>}}` placeholders only — a captured
162
163
  // provider's `paramValues` are extracted response parameters, not
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "@reclaimprotocol/agent",
3
3
  "title": "Reclaim Agent",
4
- "version": "0.1.29",
4
+ "version": "0.1.31",
5
5
  "description": "Reclaim Protocol tools for AI coding agents. Reclaim lets your app verify data points about its users, such as a follower count, an account balance, or an order history. This package is the Reclaim MCP server: it gives your agent the tools to find, build, test, and fix data verification providers by capturing and replaying browser requests, drafting provider recipes, running zkTLS proofs, and publishing provider versions.",
6
6
  "type": "module",
7
7
  "main": "./lib/index.js",
@@ -82,8 +82,8 @@
82
82
  "@hapi/boom": "^9.1.4",
83
83
  "@modelcontextprotocol/sdk": "^1.29.0",
84
84
  "@noble/hashes": "^2.2.0",
85
- "@reclaimprotocol/attestor-core": "^5.0.8",
86
- "@reclaimprotocol/client": "0.1.29",
85
+ "@reclaimprotocol/attestor-core": "^5.1.5",
86
+ "@reclaimprotocol/client": "0.1.31",
87
87
  "@reclaimprotocol/tls": "^0.1.3",
88
88
  "chrome-remote-interface": "^0.33.0",
89
89
  "domhandler": "^6.0.0",
@@ -3,7 +3,9 @@
3
3
 
4
4
  For claimant-facing Builder-client integration, result verification, or optional
5
5
  TEE binding, read topic `integration`. Keep URLs without `api=2` on the
6
- existing legacy path.
6
+ existing legacy path. For an asynchronous volunteer contribution to a
7
+ public-contribution-enabled provider, read topic `contributions` before using
8
+ Builder.
7
9
 
8
10
  <!-- common: terminology -->
9
11
 
@@ -37,14 +39,6 @@ Multi-request provider? Run steps 5–8 once per request, then publish every
37
39
  proven draft in one call. See topic `publish`.
38
40
 
39
41
  <!-- topic: contributions -->
40
- ## Public provider contributions
41
-
42
- For an asynchronous volunteer contribution, read
43
- [community-contributions.md](community-contributions.md) before using Builder.
44
- It defines the history-first workflow, approved-field allowlist, fixed-US
45
- egress, privacy boundaries, generalized recipe checks, and candidate review.
46
- Never modify a published version in place; create a candidate and submit it
47
- for review.
48
42
 
49
43
  <!-- topic: auth -->
50
44
  ## Getting authenticated
@@ -114,8 +108,18 @@ integration. Prefer Builder for new integrations.
114
108
  <!-- topic: draft -->
115
109
  ## Draft the provider
116
110
 
111
+ ## Contents
112
+
113
+ - [Choose a privacy mode](#choose-a-privacy-mode)
114
+ - [Draft and inspect the request](#draft-and-inspect-the-request)
115
+ - [Preserve the canonical shape](#preserve-the-canonical-shape)
116
+
117
+ ### Choose a privacy mode
118
+
117
119
  <!-- common: oprf -->
118
120
 
121
+ ### Draft and inspect the request
122
+
119
123
  **Call `propose_provider`.** It returns a **`draftId`** — the handle for
120
124
  `replay_request` and `run_proof` — plus the secret-free `provider` and
121
125
  `secretRefs`. The recipe **and the captured secret values** live server-side
@@ -179,11 +183,22 @@ synthesizer does this for you.
179
183
  With `hash: 'oprf-raw'`, the redaction gains `"hash": "oprf-raw"`. Nothing else
180
184
  changes.
181
185
 
186
+ ### Preserve the canonical shape
187
+
182
188
  <!-- common: rewrite-shape-block -->
183
189
 
184
190
  <!-- topic: params -->
185
191
  ## Parameters: extracted, consumer-supplied, and secret
186
192
 
193
+ ## Contents
194
+
195
+ - [Choose the parameter source](#choose-the-parameter-source)
196
+ - [Protect secret values](#protect-secret-values)
197
+ - [Hide internal fields from claimant UI](#hide-internal-fields-from-claimant-ui)
198
+ - [Understand runtime resolution](#how-the-verification-client-resolves-these-at-runtime)
199
+
200
+ ### Choose the parameter source
201
+
187
202
  **Parameterize user-specific URL segments** so the provider isn't pinned to one
188
203
  user. The matcher already puts the *captured* value in `paramValues`. If the
189
204
  consumer knows a URL value before verification and the proof must be pinned to
@@ -328,8 +343,8 @@ or archive an existing version to apply provider changes.
328
343
  drives login/waits/navigation so the target request fires), or
329
344
  `allowedJsRequests` (extra requests the script may fire, for example,
330
345
  pagination). → topic `user-script`. Before populating `allowedJsRequests` for
331
- a `verifyProof({ providerId })` use case, read topic `hash-validation` — a
332
- session-unique URL there makes verifyProof reject every real proof.
346
+ a `verifyRequestProof({ providerId })` use case, read topic `hash-validation` — a
347
+ session-unique URL there makes verifyRequestProof reject every real proof.
333
348
 
334
349
  **3. `requiredContext` is derived for you.** The publish tool scans the recipe's
335
350
  url/body for `{{context.<name>}}` placeholders and emits a JSON Schema requiring
@@ -387,76 +402,6 @@ For client integration, result verification, or TEE binding, read topic
387
402
  `integration`.
388
403
 
389
404
  <!-- topic: integration -->
390
- ## Integrate a Builder Verification Client
391
-
392
- **Verification sessions.** Consumers authenticate with a per-org token
393
- (`rorg_…`, issued in the dashboard via `POST /orgs/{orgId}/token`) and create
394
- sessions via `POST /verifications/sessions`. Results are ECIES-encrypted
395
- (secp256k1 + AES-256-GCM) to the org's eth public key when `canEncryptResult` is
396
- enabled (set via `PUT /orgs/{orgId}/keypair`) and delivered to the org's
397
- callback subscriptions; plaintext otherwise. Troubleshoot with
398
- `get_verification_session`, `list_verification_events`,
399
- `list_org_verification_sessions`.
400
-
401
- For a claimant-facing Builder-mode integration, share the returned
402
- `verificationUrl` (it contains the session id and `api=2`). Append client-owned
403
- redirect parameters only after session creation. Append `diag=1` only for a
404
- deliberate troubleshooting launch; it can enable logs containing personal data.
405
- The registered client resolves its own `x-reclaim-vc-id`; do not invent a
406
- client token or put callback/redirect fields in the session request. Consumers
407
- should verify terminal deliveries with `verifyResultFull` and the session's
408
- trusted issuer.
409
-
410
- An optional `themeId` on session creation selects an active theme owned by the
411
- organization. Omit it to snapshot the organization's most recently updated
412
- active theme. A theme's localized `consent` block makes supported clients show
413
- consent before the first provider; no block means no consent screen. Theme and
414
- branding data never affect proof trust.
415
-
416
- **Browser extension.** For the exact `api=2` URL, use
417
- `reclaimExtensionSDK.fromVerificationUrl` or `initBuilder` with the session's
418
- `verificationClientId`, then call `startVerification()`. The extension
419
- generates and persists `claimantClientId` when omitted, resolves recipes from
420
- bootstrap, runs them sequentially, and sends `x-reclaim-vc-id` to every bridge
421
- route, including session-bound `attestor-auth`. It forwards raw legacy proofs
422
- inside the Builder result and does not perform client-side `allowedJsRequests`
423
- validation. Builder recipes support dotted `{{context.key}}` templates; the
424
- extension forwards matching session context to claim creation. Builder mode
425
- skips legacy offscreen session-status calls and uses the Builder bridge for
426
- session lifecycle updates. It emits `verification_result_submitting` and
427
- `verification_result_submission_failed` when applicable. It does not send
428
- terminal success or error as a separate best-effort event; the results request
429
- makes the bridge record the matching terminal event strongly before signing
430
- and storing result deliveries. Missing or unknown `api` values stay on the
431
- existing `init` / `fromJsonString` / direct-callback path.
432
-
433
- **Builder client bridge.** In `api=2` mode, the registered client UUID is sent
434
- in `x-reclaim-vc-id` to these routes:
435
-
436
- | Route | Purpose |
437
- | --- | --- |
438
- | `GET /api/sdk/builder/v2/sessions/{sessionId}/bootstrap` | Load the session and resolved recipe. |
439
- | `PATCH /api/sdk/builder/v2/sessions/{sessionId}/claimant` | Update claimant diagnostics. |
440
- | `POST /api/sdk/builder/v2/sessions/{sessionId}/events` | Report canonical lower-snake-case events. |
441
- | `POST /api/sdk/builder/v2/sessions/{sessionId}/attestor-auth` | Request session-bound attestor authentication. Do not send a legacy app secret or provider signature. |
442
- | `POST /api/sdk/builder/v2/sessions/{sessionId}/results` | Submit exact legacy proof objects; the bridge records the matching terminal event strongly before signing and storing result deliveries. |
443
-
444
- The bridge preserves each legacy proof and signs only the outer Verification
445
- Client JWS. `verifyResultFull` validates the issuer, JWS, session, audience,
446
- and expiry, then passes each exact proof to `verifyProof`. Keep TEE settings in
447
- the legacy verifier configuration; do not add verification hooks. Treat outer
448
- `extracted_parameters` and `content_hash` as diagnostics, not trust inputs.
449
-
450
- **Optional TEE binding.** A consumer can pass
451
- `teeAttestation: { appSecret }` to `sessions.create`. The SDK derives the
452
- hash nonce locally with the pinned `@reclaimprotocol/js-sdk` formula, signs
453
- the binding payload with EIP-191, and registers it with
454
- `PUT /verifications/sessions/{sessionId}/tee-nonce` using the organization
455
- token. The private key stays in the consumer process. Bind before proof
456
- generation. The binding is idempotent for the same values and immutable
457
- afterward; this helper registers only `RECLAIM_TEE_NONCE_V1` (`v3`). Legacy
458
- `verifyProof` accepts the older signature nonce, but this helper does not
459
- generate or register it.
460
405
 
461
406
  <!-- topic: never-do -->
462
407
  ## Things to never do
@@ -491,8 +436,10 @@ turn
491
436
  document replay off and keep the interceptor (`disableRequestReplay: true`, or
492
437
  `isDocumentRequestReplayEnabled: false` on builder) — often that alone is what
493
438
  breaks the page, and it's the smallest change. Only if that doesn't help, turn
494
- interception off entirely (`injectionType: NONE`, or omit
495
- `interceptorOptions`). Full checklist and remedies: topic `interception`.
439
+ interception off temporarily to confirm it is the cause (`injectionType: NONE`,
440
+ or omit `interceptorOptions`). If that fixes the provider, keep interception
441
+ off and replace intercepted claims with `window.Reclaim.requestClaim`. Full
442
+ checklist and remedies: topic `interception`.
496
443
 
497
444
  **Proof failures / attestor errors** → topic `prove`.
498
445
  **Browser won't launch or attach** → topic `browser`.