@reclaimprotocol/agent 0.1.1-dev.1 → 0.1.2

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 (232) hide show
  1. package/README.md +1 -1
  2. package/lib/cdp/capture-engine.d.ts +66 -0
  3. package/lib/cdp/capture-engine.js +153 -0
  4. package/lib/cdp/capture.d.ts +9 -20
  5. package/lib/cdp/capture.js +14 -138
  6. package/lib/cdp/index.d.ts +3 -0
  7. package/lib/cdp/index.js +6 -0
  8. package/lib/cdp/launcher.js +4 -3
  9. package/lib/config/store.d.ts +7 -6
  10. package/lib/config/store.js +2 -2
  11. package/lib/consts.d.ts +68 -0
  12. package/lib/consts.js +134 -0
  13. package/lib/index.d.ts +2 -2
  14. package/lib/index.js +1 -1
  15. package/lib/logger.js +2 -1
  16. package/lib/mcp/api/operations.gen.d.ts +100 -48
  17. package/lib/mcp/api/operations.gen.js +1129 -194
  18. package/lib/mcp/api/to-zod.d.ts +24 -0
  19. package/lib/mcp/api/to-zod.js +94 -0
  20. package/lib/mcp/api/tools.js +1 -1
  21. package/lib/mcp/credentials/authenticate.d.ts +3 -0
  22. package/lib/mcp/{tools/credentials → credentials}/authenticate.js +3 -3
  23. package/lib/mcp/credentials/build.d.ts +5 -0
  24. package/lib/mcp/credentials/build.js +18 -0
  25. package/lib/mcp/{tools/credentials → credentials}/cache.d.ts +9 -9
  26. package/lib/mcp/{tools/credentials → credentials}/cache.js +36 -19
  27. package/lib/mcp/credentials/import.d.ts +2 -0
  28. package/lib/mcp/{tools/credentials → credentials}/import.js +2 -2
  29. package/lib/mcp/credentials/issue.d.ts +2 -0
  30. package/lib/mcp/{tools/credentials → credentials}/issue.js +4 -4
  31. package/lib/mcp/credentials/keypair.d.ts +17 -0
  32. package/lib/mcp/credentials/keypair.js +48 -0
  33. package/lib/mcp/credentials/link.d.ts +3 -0
  34. package/lib/mcp/credentials/link.js +107 -0
  35. package/lib/mcp/{tools/credentials → credentials}/list-cached.d.ts +1 -1
  36. package/lib/mcp/{tools/credentials → credentials}/list-cached.js +5 -5
  37. package/lib/mcp/credentials/owner-key.d.ts +23 -0
  38. package/lib/mcp/credentials/owner-key.js +58 -0
  39. package/lib/mcp/credentials/paths.d.ts +12 -0
  40. package/lib/mcp/credentials/paths.js +35 -0
  41. package/lib/mcp/credentials/poll.d.ts +27 -0
  42. package/lib/mcp/credentials/poll.js +61 -0
  43. package/lib/mcp/{tools/credentials → credentials}/resolve-owner-key.d.ts +1 -1
  44. package/lib/mcp/{tools/credentials → credentials}/resolve-owner-key.js +2 -2
  45. package/lib/mcp/credentials/session-cache.d.ts +14 -0
  46. package/lib/mcp/credentials/session-cache.js +42 -0
  47. package/lib/mcp/{tools/credentials → credentials}/status.d.ts +1 -1
  48. package/lib/mcp/credentials/status.js +76 -0
  49. package/lib/mcp/instructions.d.ts +1 -0
  50. package/lib/mcp/instructions.js +11 -0
  51. package/lib/mcp/register.d.ts +38 -0
  52. package/lib/mcp/register.js +80 -0
  53. package/lib/mcp/server.d.ts +2 -8
  54. package/lib/mcp/server.js +3 -33
  55. package/lib/mcp/start.js +17 -11
  56. package/lib/mcp/tools/agent/build.d.ts +5 -9
  57. package/lib/mcp/tools/agent/build.js +7 -24
  58. package/lib/mcp/tools/agent/index.d.ts +2 -0
  59. package/lib/mcp/tools/agent/index.js +3 -0
  60. package/lib/mcp/tools/agent/proof.js +3 -7
  61. package/lib/mcp/tools/agent/propose.js +13 -2
  62. package/lib/mcp/tools/agent/publish.js +172 -75
  63. package/lib/mcp/tools/agent/replay.js +0 -5
  64. package/lib/mcp/tools/analyze.d.ts +3 -0
  65. package/lib/mcp/tools/analyze.js +26 -0
  66. package/lib/mcp/tools/attach.d.ts +11 -0
  67. package/lib/mcp/tools/attach.js +72 -0
  68. package/lib/mcp/tools/authenticate/authenticate.js +107 -0
  69. package/lib/mcp/tools/authenticate/build.d.ts +15 -0
  70. package/lib/mcp/tools/authenticate/build.js +28 -0
  71. package/lib/mcp/tools/authenticate/decrypt-key.d.ts +19 -0
  72. package/lib/mcp/tools/authenticate/decrypt-key.js +46 -0
  73. package/lib/mcp/tools/authenticate/eth-key.d.ts +31 -0
  74. package/lib/mcp/tools/authenticate/eth-key.js +116 -0
  75. package/lib/mcp/tools/authenticate/import.js +57 -0
  76. package/lib/mcp/tools/authenticate/index.d.ts +2 -0
  77. package/lib/mcp/tools/authenticate/index.js +4 -0
  78. package/lib/mcp/tools/authenticate/issue.js +42 -0
  79. package/lib/mcp/tools/authenticate/resolve-owner-key.d.ts +13 -0
  80. package/lib/mcp/tools/authenticate/resolve-owner-key.js +53 -0
  81. package/lib/mcp/tools/capture.d.ts +10 -0
  82. package/lib/mcp/tools/capture.js +142 -0
  83. package/lib/mcp/tools/eval.d.ts +5 -0
  84. package/lib/mcp/tools/eval.js +127 -0
  85. package/lib/mcp/tools/find.d.ts +3 -0
  86. package/lib/mcp/tools/find.js +60 -0
  87. package/lib/mcp/tools/index.d.ts +3 -0
  88. package/lib/mcp/tools/index.js +6 -0
  89. package/lib/mcp/tools/owner-key.d.ts +2 -0
  90. package/lib/mcp/tools/owner-key.js +63 -0
  91. package/lib/mcp/tools/proof.d.ts +3 -0
  92. package/lib/mcp/tools/proof.js +214 -0
  93. package/lib/mcp/tools/propose.d.ts +9 -0
  94. package/lib/mcp/tools/propose.js +75 -0
  95. package/lib/mcp/tools/publish.d.ts +3 -0
  96. package/lib/mcp/tools/publish.js +59 -0
  97. package/lib/mcp/tools/replay.d.ts +3 -0
  98. package/lib/mcp/tools/replay.js +46 -0
  99. package/lib/mcp/tools/session.d.ts +10 -0
  100. package/lib/mcp/tools/session.js +29 -0
  101. package/lib/mcp/tools/verification/build.d.ts +12 -0
  102. package/lib/mcp/tools/verification/build.js +14 -0
  103. package/lib/mcp/tools/verification/diagnose.d.ts +11 -0
  104. package/lib/mcp/tools/verification/diagnose.js +132 -0
  105. package/lib/mcp/tools/verification/index.d.ts +1 -0
  106. package/lib/mcp/tools/verification/index.js +2 -0
  107. package/lib/mcp/tools/verification/result.d.ts +11 -0
  108. package/lib/mcp/tools/verification/result.js +126 -0
  109. package/lib/mcp/verifications/build.d.ts +5 -0
  110. package/lib/mcp/verifications/build.js +14 -0
  111. package/lib/mcp/verifications/create.d.ts +3 -0
  112. package/lib/mcp/verifications/create.js +57 -0
  113. package/lib/mcp/verifications/find.d.ts +3 -0
  114. package/lib/mcp/verifications/find.js +37 -0
  115. package/lib/mcp/verifications/get.d.ts +3 -0
  116. package/lib/mcp/verifications/get.js +26 -0
  117. package/lib/mcp/verifications/update.d.ts +3 -0
  118. package/lib/mcp/verifications/update.js +78 -0
  119. package/lib/old/client.d.ts +3 -0
  120. package/lib/old/flag.d.ts +4 -1
  121. package/lib/old/flag.js +4 -3
  122. package/lib/old/identity.d.ts +0 -10
  123. package/lib/old/identity.js +8 -7
  124. package/lib/old/instructions.d.ts +0 -6
  125. package/lib/old/instructions.js +6 -24
  126. package/lib/old/login-cdp.d.ts +0 -3
  127. package/lib/old/login-cdp.js +2 -4
  128. package/lib/old/to-register.d.ts +3 -0
  129. package/lib/old/to-register.js +32 -7
  130. package/lib/old/tools/authenticate.js +2 -1
  131. package/lib/old/tools/publish.js +71 -10
  132. package/lib/paths.js +2 -2
  133. package/lib/proof/attestor.d.ts +33 -1
  134. package/lib/proof/attestor.js +67 -64
  135. package/lib/proof/index.d.ts +5 -0
  136. package/lib/proof/index.js +6 -0
  137. package/lib/proof/proof-core.d.ts +35 -0
  138. package/lib/proof/proof-core.js +84 -0
  139. package/lib/provider/index.d.ts +5 -0
  140. package/lib/provider/index.js +9 -0
  141. package/lib/provider/injection-doc.d.ts +1 -0
  142. package/lib/provider/injection-doc.js +17 -0
  143. package/lib/provider/match-redaction-warning.d.ts +25 -0
  144. package/lib/provider/match-redaction-warning.js +35 -0
  145. package/lib/provider/schema.d.ts +16 -0
  146. package/lib/provider/schema.js +11 -1
  147. package/lib/provider/to-version.d.ts +61 -12
  148. package/lib/provider/to-version.js +109 -24
  149. package/lib/provider/verification-checks.d.ts +12 -0
  150. package/lib/provider/verification-checks.js +30 -0
  151. package/lib/routes.gen.d.ts +271 -0
  152. package/lib/routes.gen.js +73 -0
  153. package/lib/types/openapi.gen.d.ts +3967 -0
  154. package/lib/types/openapi.gen.js +5 -0
  155. package/package.json +16 -7
  156. package/skill/SKILL.md +35 -14
  157. package/skill/builder-mode.md +13 -0
  158. package/skill/injection-api.md +42 -0
  159. package/skill/old-mode.md +15 -0
  160. package/src/cdp/capture-engine.ts +256 -0
  161. package/src/cdp/capture.ts +25 -185
  162. package/src/cdp/index.ts +6 -0
  163. package/src/cdp/launcher.ts +5 -4
  164. package/src/config/store.ts +7 -6
  165. package/src/consts.ts +164 -0
  166. package/src/index.ts +2 -4
  167. package/src/logger.ts +2 -1
  168. package/src/mcp/api/operations.gen.ts +1155 -194
  169. package/src/mcp/api/to-zod.ts +115 -0
  170. package/src/mcp/api/tools.ts +9 -3
  171. package/src/mcp/instructions.ts +15 -0
  172. package/src/mcp/server.ts +5 -50
  173. package/src/mcp/start.ts +27 -11
  174. package/src/mcp/tools/agent/build.ts +7 -27
  175. package/src/mcp/tools/agent/index.ts +4 -0
  176. package/src/mcp/tools/agent/proof.ts +3 -8
  177. package/src/mcp/tools/agent/propose.ts +13 -2
  178. package/src/mcp/tools/agent/publish.ts +218 -92
  179. package/src/mcp/tools/agent/replay.ts +0 -5
  180. package/src/mcp/tools/{credentials → authenticate}/authenticate.ts +9 -8
  181. package/src/mcp/tools/authenticate/build.ts +32 -0
  182. package/src/mcp/tools/authenticate/decrypt-key.ts +59 -0
  183. package/src/mcp/tools/authenticate/eth-key.ts +162 -0
  184. package/src/mcp/tools/authenticate/import.ts +73 -0
  185. package/src/mcp/tools/authenticate/index.ts +4 -0
  186. package/src/mcp/tools/authenticate/issue.ts +50 -0
  187. package/src/mcp/tools/authenticate/resolve-owner-key.ts +60 -0
  188. package/src/mcp/tools/index.ts +6 -0
  189. package/src/mcp/tools/verification/build.ts +16 -0
  190. package/src/mcp/tools/verification/index.ts +2 -0
  191. package/src/mcp/tools/verification/result.ts +154 -0
  192. package/src/old/client.ts +3 -0
  193. package/src/old/flag.ts +4 -3
  194. package/src/old/identity.ts +8 -7
  195. package/src/old/instructions.ts +10 -25
  196. package/src/old/login-cdp.ts +2 -5
  197. package/src/old/to-register.ts +58 -26
  198. package/src/old/tools/authenticate.ts +2 -1
  199. package/src/old/tools/publish.ts +92 -16
  200. package/src/paths.ts +2 -2
  201. package/src/proof/attestor.ts +114 -72
  202. package/src/proof/index.ts +21 -0
  203. package/src/proof/proof-core.ts +119 -0
  204. package/src/provider/index.ts +20 -0
  205. package/src/provider/injection-doc.ts +21 -0
  206. package/src/provider/match-redaction-warning.ts +53 -0
  207. package/src/provider/schema.ts +23 -0
  208. package/src/provider/to-version.ts +180 -33
  209. package/src/provider/verification-checks.ts +36 -0
  210. package/lib/mcp/tools/credentials/build.d.ts +0 -16
  211. package/lib/mcp/tools/credentials/build.js +0 -33
  212. package/lib/mcp/tools/credentials/status.js +0 -62
  213. package/src/mcp/tools/credentials/build.ts +0 -37
  214. package/src/mcp/tools/credentials/cache.ts +0 -161
  215. package/src/mcp/tools/credentials/env-file.ts +0 -101
  216. package/src/mcp/tools/credentials/import.ts +0 -72
  217. package/src/mcp/tools/credentials/issue.ts +0 -107
  218. package/src/mcp/tools/credentials/list-cached.ts +0 -66
  219. package/src/mcp/tools/credentials/register.ts +0 -49
  220. package/src/mcp/tools/credentials/resolve-owner-key.ts +0 -78
  221. package/src/mcp/tools/credentials/status.ts +0 -82
  222. package/src/types/openapi.ts +0 -7
  223. /package/lib/mcp/{tools/credentials → credentials}/env-file.d.ts +0 -0
  224. /package/lib/mcp/{tools/credentials → credentials}/env-file.js +0 -0
  225. /package/lib/mcp/{tools/credentials → credentials}/register.d.ts +0 -0
  226. /package/lib/mcp/{tools/credentials → credentials}/register.js +0 -0
  227. /package/lib/mcp/tools/{credentials → authenticate}/authenticate.d.ts +0 -0
  228. /package/lib/mcp/tools/{credentials → authenticate}/import.d.ts +0 -0
  229. /package/lib/mcp/tools/{credentials → authenticate}/issue.d.ts +0 -0
  230. /package/lib/mcp/tools/{credentials → authenticate}/session-cache.d.ts +0 -0
  231. /package/lib/mcp/tools/{credentials → authenticate}/session-cache.js +0 -0
  232. /package/src/mcp/tools/{credentials → authenticate}/session-cache.ts +0 -0
@@ -1,4 +1,9 @@
1
+ import { Boom, isBoom } from '@hapi/boom'
1
2
  import { defineTool, type RegisteredTool } from '../../mcp/server.ts'
3
+ import {
4
+ countsFor,
5
+ matchRedactionWarnings,
6
+ } from '../../provider/match-redaction-warning.ts'
2
7
  import type { ReclaimProvider } from '../../provider/schema.ts'
3
8
  import type { ReclaimOldClient } from '../client.ts'
4
9
  import {
@@ -11,6 +16,35 @@ import {
11
16
 
12
17
  const DEFAULT_VERSION_INFO = 'Updated from MCP browser capture'
13
18
 
19
+ /**
20
+ * The old backend lets you add a version only to a provider YOU own. A
21
+ * `providerId` for a public provider (or anyone else's) comes back as
22
+ * 401/403/404 — the dev's likely intent is to publish their OWN version of that
23
+ * site, which on this backend means registering a separate copy. Rethrow those
24
+ * failures with that actionable suggestion; leave other errors (e.g. a 400
25
+ * version collision) untouched so their real detail surfaces.
26
+ */
27
+ function rethrowOwnershipError(err: unknown, providerId: string): never {
28
+ const status = isBoom(err) ? err.output.statusCode : undefined
29
+ if(status !== 401 && status !== 403 && status !== 404) {
30
+ throw err
31
+ }
32
+
33
+ const base = isBoom(err) ? err.message : String(err)
34
+ throw new Boom(
35
+ `${base} — could not add a version to provider ${providerId}. You can ` +
36
+ 'only publish new versions on providers you own. If this is a public ' +
37
+ "provider (or one you don't own), re-run " +
38
+ 'create_provider_version_from_capture WITHOUT `providerId` to register ' +
39
+ "your own copy. If it IS yours, confirm you're authenticated " +
40
+ '(reclaim_authenticate) as the account that created it.',
41
+ {
42
+ statusCode: status,
43
+ data: isBoom(err) ? err.data : { status, detail: base },
44
+ },
45
+ )
46
+ }
47
+
14
48
  /**
15
49
  * Publish a drafted provider to the OLD devtools backend. Keeps the builder
16
50
  * tool NAME (`create_provider_version_from_capture`) so the skill flow is
@@ -33,6 +67,7 @@ export function createVersionFromCaptureTool(
33
67
  providerId?: string
34
68
  description?: string
35
69
  geoLocation?: string
70
+ useProxy?: boolean
36
71
  providerType?: 'PRIVATE' | 'PUBLIC'
37
72
  notes?: string
38
73
  }>(
@@ -47,6 +82,10 @@ export function createVersionFromCaptureTool(
47
82
  '1.0.0; re-running creates a duplicate). Pass `providerId` (from ' +
48
83
  'get_me_providers) to add a NEW VERSION to that existing provider ' +
49
84
  '(the patch is auto-bumped). \n\n' +
85
+ 'You can only add versions to a provider YOU own — the `providerId` ' +
86
+ 'must come from get_me_providers. To publish your own version of a ' +
87
+ "PUBLIC provider you don't own, omit `providerId` to register your " +
88
+ 'own copy (you cannot edit someone else\'s provider here). \n\n' +
50
89
  'Captured requests must be GET/POST. OPRF redactions are supported.',
51
90
  inputSchema: {
52
91
  type: 'object',
@@ -83,7 +122,18 @@ export function createVersionFromCaptureTool(
83
122
  type: 'string',
84
123
  description: 'Provider description (new-provider register only).',
85
124
  },
86
- geoLocation: { type: 'string' },
125
+ geoLocation: {
126
+ type: 'string',
127
+ description:
128
+ 'Geo the verification appears to originate from. Defaults to ' +
129
+ "'{{DYNAMIC_GEO}}' (the user's geo) when omitted.",
130
+ },
131
+ useProxy: {
132
+ type: 'boolean',
133
+ description:
134
+ 'Route portal/attestation traffic through a proxy. Defaults to ' +
135
+ 'true when omitted. Applied on register only.',
136
+ },
87
137
  providerType: { type: 'string', enum: ['PRIVATE', 'PUBLIC'] },
88
138
  notes: {
89
139
  type: 'string',
@@ -105,31 +155,57 @@ export function createVersionFromCaptureTool(
105
155
  opts.geoLocation = args.geoLocation
106
156
  }
107
157
 
158
+ if(args.useProxy !== undefined) {
159
+ opts.useProxy = args.useProxy
160
+ }
161
+
108
162
  if(args.providerType !== undefined) {
109
163
  opts.providerType = args.providerType
110
164
  }
111
165
 
166
+ // Non-blocking advisory — never alters or blocks the publish; the
167
+ // author keeps full control. Attaches under `_notes` when relevant.
168
+ const notes = matchRedactionWarnings([
169
+ countsFor('Request', args.provider),
170
+ ])
171
+ const withNotes = (result: unknown) => {
172
+ if(notes.length && result && typeof result === 'object') {
173
+ return { ...result, _notes: notes }
174
+ }
175
+
176
+ return result
177
+ }
178
+
112
179
  // New provider.
113
180
  if(!args.providerId) {
114
181
  const body = providerToRegisterBody(args.provider, opts)
115
- return client.registerProvider(body)
182
+ return withNotes(await client.registerProvider(body))
116
183
  }
117
184
 
118
185
  // New version of an existing provider — pick the next patch above
119
- // the highest existing version.
120
- const versionsResp = await client.listVersions(args.providerId)
121
- const version = bumpPatchString(extractVersions(versionsResp))
122
- const versionInfo =
123
- args.notes && args.notes.trim().length >= 10
124
- ? args.notes.trim()
125
- : DEFAULT_VERSION_INFO
126
- const body = providerToConfigBody(
127
- args.provider,
128
- opts,
129
- version,
130
- versionInfo,
131
- )
132
- return client.updateProviderConfig(args.providerId, body)
186
+ // the highest existing version. A provider you don't own (e.g. a
187
+ // public one) fails here with 401/403/404; surface the register-a-copy
188
+ // workaround instead of a bare permission error.
189
+ const providerId = args.providerId
190
+ try {
191
+ const versionsResp = await client.listVersions(providerId)
192
+ const version = bumpPatchString(extractVersions(versionsResp))
193
+ const versionInfo =
194
+ args.notes && args.notes.trim().length >= 10
195
+ ? args.notes.trim()
196
+ : DEFAULT_VERSION_INFO
197
+ const body = providerToConfigBody(
198
+ args.provider,
199
+ opts,
200
+ version,
201
+ versionInfo,
202
+ )
203
+ return withNotes(
204
+ await client.updateProviderConfig(providerId, body),
205
+ )
206
+ } catch(err) {
207
+ rethrowOwnershipError(err, providerId)
208
+ }
133
209
  },
134
210
  )
135
211
  }
package/src/paths.ts CHANGED
@@ -1,13 +1,13 @@
1
1
  import { existsSync } from 'node:fs'
2
- import { homedir } from 'node:os'
3
2
  import { dirname, join, resolve } from 'node:path'
3
+ import { reclaimHome } from './consts.ts'
4
4
 
5
5
  /**
6
6
  * Resolve the Reclaim cache directory (`~/.reclaim`). Honours the
7
7
  * `RECLAIM_HOME` env var as an override so tests can sandbox writes.
8
8
  */
9
9
  export function reclaimHomeDirPath() {
10
- return process.env.RECLAIM_HOME || join(homedir(), '.reclaim')
10
+ return reclaimHome()
11
11
  }
12
12
 
13
13
  /**
@@ -6,11 +6,16 @@
6
6
  // revealed slice; jsonPath/xPath live on responseRedactions (what the prover
7
7
  // reveals), never on responseMatches.
8
8
 
9
+ import { bytesToHex } from '@noble/hashes/utils.js'
9
10
  import {
10
- createClaimOnAttestor, type Logger, type ProviderParams,
11
+ createClaimOnAttestor, type Logger,
11
12
  } from '@reclaimprotocol/attestor-core'
12
- import { resolveOwnerPrivateKey } from '../mcp/tools/credentials/cache.ts'
13
+ import { agentTraceEnabled } from '../consts.ts'
14
+ import { resolveEthKey } from '../mcp/tools/authenticate/eth-key.ts'
13
15
  import type { ReclaimProvider } from '../provider/schema.ts'
16
+ import {
17
+ buildHttpParams, ensureAttestorCrypto, parseClaim, resolveAttestorUrl,
18
+ } from './proof-core.ts'
14
19
  import { ensureZkFiles } from './zk-files.ts'
15
20
 
16
21
  export interface AttestorLogEntry {
@@ -19,6 +24,37 @@ export interface AttestorLogEntry {
19
24
  ctx?: Record<string, unknown>
20
25
  }
21
26
 
27
+ /** The attestor's signed claim data + signature, for building the nested proof
28
+ * JWS a Consumer verifies against the attestor's key. Populated when
29
+ * `includeAttestorClaim` is set (the app's verification runner needs it; the
30
+ * MCP `run_proof` tool ignores it). */
31
+ export interface AttestorClaim {
32
+ /** ProviderClaimData: provider, parameters, context (JSON string), owner,
33
+ * timestampS, epoch, identifier. */
34
+ claim: Record<string, unknown>
35
+ /** Hex (0x) attestor ETH address that signed the claim (the nested `kid`). */
36
+ attestorAddress: string
37
+ /** Hex (0x) attestor claim signature (EIP-191 over the claim sign-data). */
38
+ claimSignature: string
39
+ }
40
+
41
+ /** Options for {@link runAttestorProof}. */
42
+ export interface RunAttestorProofOptions {
43
+ attestorUrl?: string
44
+ /** When resolving the key from env/.env/file, the address the caller expects
45
+ * to sign with (sanity check). Ignored when `ownerPrivateKey` is given. */
46
+ ownerAddress?: string
47
+ /** Sign with this key directly (e.g. the app/VC's ephemeral per-session key)
48
+ * instead of resolving from env/.env/file. */
49
+ ownerPrivateKey?: string
50
+ /** ZK engine for `createClaimOnAttestor`. Defaults to `'gnark'`; the app's
51
+ * server-side verification runner uses `'snarkjs'`. */
52
+ zkEngine?: 'gnark' | 'snarkjs'
53
+ /** Extract the structured {@link AttestorClaim} (claim + attestor signature)
54
+ * onto the result for nested-proof construction. */
55
+ includeAttestorClaim?: boolean
56
+ }
57
+
22
58
  export interface ProofResult {
23
59
  proof: unknown
24
60
  extractedValue: string
@@ -27,6 +63,9 @@ export interface ProofResult {
27
63
  owner?: string
28
64
  verified: boolean
29
65
  error?: string
66
+ /** Structured claim + signature for nested-proof verification. Set only when
67
+ * `includeAttestorClaim` was requested and the claim carries signatures. */
68
+ attestorClaim?: AttestorClaim
30
69
  /** Set to `'missing-owner-key'` when no credential was available to
31
70
  * sign with — distinguishes this recoverable case from other proof
32
71
  * errors so the skill can point the dev at `issue_credentials` /
@@ -139,16 +178,6 @@ function makeCaptureLogger(): {
139
178
  return { logger: make({}), entries }
140
179
  }
141
180
 
142
- /** Default public attestor websocket URL. Resolved per-call so an env-var
143
- * change or per-request override takes effect without restarting the
144
- * MCP server. */
145
- const FALLBACK_ATTESTOR_URL = 'wss://attestor.reclaimprotocol.org:444/ws'
146
- function resolveAttestorUrl(override?: string): string {
147
- return (
148
- override ?? process.env['RECLAIM_ATTESTOR_URL'] ?? FALLBACK_ATTESTOR_URL
149
- )
150
- }
151
-
152
181
  // ---------------------------------------------------------------------------
153
182
  // Main entry point
154
183
  // ---------------------------------------------------------------------------
@@ -156,52 +185,67 @@ function resolveAttestorUrl(override?: string): string {
156
185
  export async function runAttestorProof(
157
186
  provider: ReclaimProvider,
158
187
  secrets: Record<string, string>,
159
- attestorUrl?: string,
160
- ownerAddress?: string,
188
+ opts: RunAttestorProofOptions = {},
161
189
  ): Promise<ProofResult> {
162
- // The owner is chosen up front (resolve_owner_key) and passed as
163
- // `ownerAddress`; here we just fetch its private key and sign.
164
- const ownerPrivateKey = ownerAddress
165
- ? resolveOwnerPrivateKey(ownerAddress)
166
- : undefined
167
- if(!ownerPrivateKey) {
168
- return {
169
- proof: undefined,
170
- extractedValue: '',
171
- extractedParameters: {},
172
- verified: false,
173
- error:
174
- `No private key available for ownerAddress "${ownerAddress ?? ''}". ` +
175
- 'Call resolve_owner_key to pick a credential (or ' +
176
- 'issue_credentials / import_credentials to set one up), then ' +
177
- 'retry run_proof with its address as ownerAddress.',
178
- errorKind: 'missing-owner-key',
190
+ // Sign with the caller-supplied key (the app/VC's ephemeral per-session
191
+ // key) when given; otherwise resolve env/.env/file. `ownerAddress` is a
192
+ // sanity check on the resolved key (ignored when a key is supplied).
193
+ let ownerPrivateKey: string
194
+ if(opts.ownerPrivateKey) {
195
+ ownerPrivateKey = opts.ownerPrivateKey
196
+ } else {
197
+ const resolved = resolveEthKey()
198
+ if(!resolved) {
199
+ return {
200
+ proof: undefined,
201
+ extractedValue: '',
202
+ extractedParameters: {},
203
+ verified: false,
204
+ error:
205
+ 'No eth proof-owner key available (no RECLAIM_PRIVATE_KEY in env, ' +
206
+ '.env, or RECLAIM_PRIVATE_KEY_FILE). Call resolve_owner_key, then ' +
207
+ 'issue_credentials (generate) or import_credentials (provide one), ' +
208
+ 'and retry run_proof.',
209
+ errorKind: 'missing-owner-key',
210
+ }
179
211
  }
212
+
213
+ if(
214
+ opts.ownerAddress
215
+ && opts.ownerAddress.toLowerCase() !== resolved.address
216
+ ) {
217
+ return {
218
+ proof: undefined,
219
+ extractedValue: '',
220
+ extractedParameters: {},
221
+ verified: false,
222
+ error:
223
+ `Requested ownerAddress "${opts.ownerAddress}" does not match ` +
224
+ `the resolved eth key (${resolved.address}, source: ` +
225
+ `${resolved.source}). ` +
226
+ 'Call resolve_owner_key to get the current address, or update the ' +
227
+ 'configured key.',
228
+ errorKind: 'missing-owner-key',
229
+ }
230
+ }
231
+
232
+ ownerPrivateKey = resolved.privateKey
180
233
  }
181
234
 
182
235
  try {
183
- // Circuits aren't downloaded at install time (that blocked the MCP
184
- // server's first launch). Fetch them lazily on the first proof.
236
+ // Init the TLS crypto backend (idempotent), then fetch ZK circuits
237
+ // lazily on the first proof (not at install time).
238
+ ensureAttestorCrypto()
185
239
  await ensureZkFiles()
186
240
 
187
- const params: ProviderParams<'http'> = {
188
- url: provider.url,
189
- method: provider.method,
190
- ...(provider.headers !== undefined && { headers: provider.headers }),
191
- ...(provider.body !== undefined && { body: provider.body }),
192
- responseMatches: provider.responseMatches,
193
- responseRedactions: provider.responseRedactions ?? [],
194
- ...(provider.paramValues !== undefined && {
195
- paramValues: provider.paramValues,
196
- }),
197
- }
241
+ const params = buildHttpParams(provider)
198
242
 
199
243
  // Diagnostic logger capture is gated behind RECLAIM_AGENT_TRACE so the
200
244
  // happy path doesn't pay the cost (the attestor SDK emits a *lot* of
201
245
  // trace/debug entries during ZK setup). Flip RECLAIM_AGENT_TRACE=1 to
202
246
  // turn it back on when something's broken; logs flow into the proof
203
247
  // response and into the on-disk proof-runs dump.
204
- const traceEnabled = process.env['RECLAIM_AGENT_TRACE'] === '1'
248
+ const traceEnabled = agentTraceEnabled()
205
249
  const capture = traceEnabled ? makeCaptureLogger() : undefined
206
250
  const attestorLogs = capture?.entries
207
251
 
@@ -214,8 +258,8 @@ export async function runAttestorProof(
214
258
  headers: secrets,
215
259
  },
216
260
  ownerPrivateKey,
217
- client: { url: resolveAttestorUrl(attestorUrl) },
218
- zkEngine: 'snarkjs',
261
+ client: { url: resolveAttestorUrl(opts.attestorUrl) },
262
+ zkEngine: opts.zkEngine ?? 'gnark',
219
263
  ...(capture !== undefined && { logger: capture.logger as Logger }),
220
264
  })
221
265
  } catch(err) {
@@ -234,27 +278,8 @@ export async function runAttestorProof(
234
278
  return errorResult
235
279
  }
236
280
 
237
- // ClaimTunnelResponse carries the claim under `.claim`.
238
- const claimRecord = claim as Record<string, unknown>
239
- const claimData =
240
- claimRecord['claim'] as Record<string, unknown> | undefined
241
-
242
- // claimData.context is a JSON-encoded string carrying
243
- // { extractedParameters: {...}, providerHash, ... }.
244
- let context: Record<string, unknown> = {}
245
- const rawContext = claimData?.['context']
246
- if(typeof rawContext === 'string') {
247
- try {
248
- context = JSON.parse(rawContext) as Record<string, unknown>
249
- } catch{
250
- // Leave context empty if it isn't valid JSON.
251
- }
252
- }
253
-
254
- const extractedParameters =
255
- (context['extractedParameters'] as Record<string, string> | undefined) ??
256
- {}
257
- const extractedValue = Object.values(extractedParameters)[0] ?? ''
281
+ const { extractedValue, extractedParameters, identifier, owner, claimData }
282
+ = parseClaim(claim)
258
283
 
259
284
  const result: ProofResult = {
260
285
  proof: claim,
@@ -262,16 +287,33 @@ export async function runAttestorProof(
262
287
  extractedParameters,
263
288
  verified: true,
264
289
  }
265
- const identifier = claimData?.['identifier']
266
- if(typeof identifier === 'string') {
290
+ if(identifier !== undefined) {
267
291
  result.identifier = identifier
268
292
  }
269
293
 
270
- const owner = claimData?.['owner']
271
- if(typeof owner === 'string') {
294
+ if(owner !== undefined) {
272
295
  result.owner = owner
273
296
  }
274
297
 
298
+ // Structured claim + signature so the result JWS can carry a nested,
299
+ // attestor-signed proof the Consumer verifies independently.
300
+ if(opts.includeAttestorClaim) {
301
+ const sigs = (claim as {
302
+ signatures?: { attestorAddress?: unknown, claimSignature?: unknown }
303
+ }).signatures
304
+ if(
305
+ claimData
306
+ && typeof sigs?.attestorAddress === 'string'
307
+ && sigs.claimSignature instanceof Uint8Array
308
+ ) {
309
+ result.attestorClaim = {
310
+ claim: claimData,
311
+ attestorAddress: sigs.attestorAddress,
312
+ claimSignature: '0x' + bytesToHex(sigs.claimSignature),
313
+ }
314
+ }
315
+ }
316
+
275
317
  // Only set when RECLAIM_AGENT_TRACE=1. Full stream — the MCP layer
276
318
  // trims for the LLM-visible response; the on-disk dump gets it all.
277
319
  if(attestorLogs !== undefined && attestorLogs.length > 0) {
@@ -0,0 +1,21 @@
1
+ // Proof primitives shared with the app's verification runner: the attestor
2
+ // param/claim plumbing, the ZK-file bootstrap, and the agent's own
3
+ // `runAttestorProof`. Exposed as `@reclaimprotocol/agent/proof`.
4
+ export type {
5
+ AttestorClaim,
6
+ AttestorLogEntry,
7
+ ProofResult,
8
+ RunAttestorProofOptions,
9
+ } from './attestor.ts'
10
+ export { runAttestorProof, tailLogs, warnsAndErrors } from './attestor.ts'
11
+ export {
12
+ buildHttpParams,
13
+ ensureAttestorCrypto,
14
+ type ParsedClaim,
15
+ parseClaim,
16
+ resolveAttestorUrl,
17
+ } from './proof-core.ts'
18
+ export { ensureZkFiles } from './zk-files.ts'
19
+ // Re-exported attestor-core claim primitive so the agent is the single
20
+ // integration point with @reclaimprotocol/attestor-core across the builder.
21
+ export { createClaimOnAttestor } from '@reclaimprotocol/attestor-core'
@@ -0,0 +1,119 @@
1
+ /**
2
+ * Shared attestor-claim plumbing used by both proof callers: the agent's MCP
3
+ * `run_proof` (which resolves the owner key from the credential cache and
4
+ * captures trace logs) and the app's verification runner (which signs with an
5
+ * ephemeral per-session key and builds a nested-proof claim). Only those
6
+ * concerns differ — building the HTTP params, resolving the attestor URL, and
7
+ * parsing the claim response are identical, so they live here.
8
+ */
9
+ import type { ProviderParams } from '@reclaimprotocol/attestor-core'
10
+ import { setCryptoImplementation } from '@reclaimprotocol/tls'
11
+ import { webcryptoCrypto } from '@reclaimprotocol/tls/webcrypto'
12
+ import { attestorUrlOverride, FALLBACK_ATTESTOR_URL } from '../consts.ts'
13
+ import type { ReclaimProvider } from '../provider/schema.ts'
14
+ import { normalizeGeoLocation } from '../provider/schema.ts'
15
+
16
+ let cryptoInitialized = false
17
+
18
+ /** One-time crypto backend init for `@reclaimprotocol/tls` (idempotent). The
19
+ * package ships with an empty `crypto = {}` and defers the backend to the
20
+ * caller; without this the first proof dies inside the TLS client with
21
+ * `crypto.randomBytes is not a function`. Shared by every attestor caller —
22
+ * the agent MCP `run_proof` tool and the app's verification runner. */
23
+ export function ensureAttestorCrypto(): void {
24
+ if(cryptoInitialized) {
25
+ return
26
+ }
27
+
28
+ setCryptoImplementation(webcryptoCrypto)
29
+ cryptoInitialized = true
30
+ }
31
+
32
+ /** Resolve the attestor websocket URL: explicit override → env → public
33
+ * default. Resolved per-call so an env change or per-request override takes
34
+ * effect without restarting. */
35
+ export function resolveAttestorUrl(override?: string): string {
36
+ return (
37
+ override ?? attestorUrlOverride() ?? FALLBACK_ATTESTOR_URL
38
+ )
39
+ }
40
+
41
+ /** Map a verification's provider recipe straight to the attestor's HTTP
42
+ * params — never synthesized from captured traffic. */
43
+ export function buildHttpParams(
44
+ provider: ReclaimProvider,
45
+ ): ProviderParams<'http'> {
46
+ // A blank egress is treated as unset: omit it so the local proof uses the
47
+ // tester's own egress rather than shipping a blank or an unresolved template.
48
+ const geoLocation = normalizeGeoLocation(provider.geoLocation)
49
+ return {
50
+ url: provider.url,
51
+ method: provider.method,
52
+ ...(provider.headers !== undefined && { headers: provider.headers }),
53
+ ...(provider.body !== undefined && { body: provider.body }),
54
+ responseMatches: provider.responseMatches,
55
+ responseRedactions: provider.responseRedactions ?? [],
56
+ ...(provider.paramValues !== undefined && {
57
+ paramValues: provider.paramValues,
58
+ }),
59
+ ...(provider.writeRedactionMode !== undefined && {
60
+ writeRedactionMode: provider.writeRedactionMode,
61
+ }),
62
+ ...(provider.additionalClientOptions !== undefined && {
63
+ additionalClientOptions: provider.additionalClientOptions,
64
+ }),
65
+ ...(geoLocation !== undefined && { geoLocation }),
66
+ }
67
+ }
68
+
69
+ export interface ParsedClaim {
70
+ /** ProviderClaimData: provider, parameters, context, owner, identifier, … */
71
+ claimData?: Record<string, unknown>
72
+ extractedParameters: Record<string, string>
73
+ extractedValue: string
74
+ identifier?: string
75
+ owner?: string
76
+ }
77
+
78
+ /** Pull claimData + decoded `context.extractedParameters` (plus identifier /
79
+ * owner) out of a `createClaimOnAttestor` result. The full result still
80
+ * carries signatures the caller may need for nested-proof building. */
81
+ export function parseClaim(claim: unknown): ParsedClaim {
82
+ const claimRecord = claim as Record<string, unknown>
83
+ const claimData = claimRecord['claim'] as Record<string, unknown> | undefined
84
+
85
+ // claimData.context is a JSON-encoded string carrying
86
+ // { extractedParameters: {...}, providerHash, ... }.
87
+ let context: Record<string, unknown> = {}
88
+ const rawContext = claimData?.['context']
89
+ if(typeof rawContext === 'string') {
90
+ try {
91
+ context = JSON.parse(rawContext) as Record<string, unknown>
92
+ } catch{
93
+ // Leave context empty if it isn't valid JSON.
94
+ }
95
+ }
96
+
97
+ const extractedParameters =
98
+ (context['extractedParameters'] as Record<string, string> | undefined) ?? {}
99
+
100
+ const parsed: ParsedClaim = {
101
+ extractedParameters,
102
+ extractedValue: Object.values(extractedParameters)[0] ?? '',
103
+ }
104
+ if(claimData) {
105
+ parsed.claimData = claimData
106
+ }
107
+
108
+ const identifier = claimData?.['identifier']
109
+ if(typeof identifier === 'string') {
110
+ parsed.identifier = identifier
111
+ }
112
+
113
+ const owner = claimData?.['owner']
114
+ if(typeof owner === 'string') {
115
+ parsed.owner = owner
116
+ }
117
+
118
+ return parsed
119
+ }
@@ -0,0 +1,20 @@
1
+ // Provider primitives shared with the app's capture pipeline:
2
+ // the attestor-derived recipe types, secret classification, and the GraphQL
3
+ // op parser. Exposed as `@reclaimprotocol/agent/provider`.
4
+ export { extractGraphqlOp, looksLikeGraphqlEndpoint } from './graphql.ts'
5
+ export type {
6
+ CapturedRequest,
7
+ HttpMethod,
8
+ OprfMode,
9
+ ReclaimProvider,
10
+ RequestConcern,
11
+ ResponseMatch,
12
+ ResponseRedaction,
13
+ SecretParamRef,
14
+ } from './schema.ts'
15
+ export { classifyHeader, splitSecrets } from './secrets.ts'
16
+ // Re-exported attestor-core provider primitives (the registry, the param hash,
17
+ // and the param type) so the builder imports them via the agent rather than
18
+ // depending on @reclaimprotocol/attestor-core directly.
19
+ export { hashProviderParams, providers } from '@reclaimprotocol/attestor-core'
20
+ export type { ProviderParams } from '@reclaimprotocol/attestor-core'
@@ -0,0 +1,21 @@
1
+ /**
2
+ * Guidance for authoring page-injection scripts (`webSettings.jsUserScripts` on
3
+ * the builder, `customInjection` on old-devtools), surfaced to the authoring
4
+ * model from the publish tool description. The prose lives in
5
+ * `skill/injection-api.md` so it can be edited as markdown without touching
6
+ * code — the same pattern as `mcp/instructions.ts`. The path resolves the same
7
+ * from `src/provider` (dev) and `lib/provider` (published): both are two levels
8
+ * below the package root.
9
+ *
10
+ * The guide leads with plain browser JavaScript (navigation/interaction via
11
+ * standard APIs; the client already intercepts page requests) and treats
12
+ * `window.Reclaim` as a rarely-needed edge-case bridge, pointing at the
13
+ * authoritative `src/support/env.d.ts` rather than reproducing its surface.
14
+ */
15
+ import { readFileSync } from 'node:fs'
16
+ import { fileURLToPath } from 'node:url'
17
+
18
+ export const INJECTION_GUIDE = readFileSync(
19
+ fileURLToPath(new URL('../../skill/injection-api.md', import.meta.url)),
20
+ 'utf8',
21
+ ).trim()
@@ -0,0 +1,53 @@
1
+ /**
2
+ * Non-blocking advisory for a request whose `responseMatches` count is less
3
+ * than its `responseRedactions` count.
4
+ *
5
+ * Some verification clients (notably the InApp SDK) pair the two arrays by
6
+ * index and may not behave correctly when there are fewer matches than
7
+ * redactions. This is NOT enforced — the author keeps full control over what
8
+ * they publish in either old-devtools or builder mode. The tools surface these
9
+ * strings so the dev can make an informed decision (e.g. add a functional match
10
+ * per redaction) rather than being blocked.
11
+ */
12
+
13
+ /** One request's match/redaction counts, with a human label for the message. */
14
+ export interface RequestCounts {
15
+ label: string
16
+ matchCount: number
17
+ redactionCount: number
18
+ }
19
+
20
+ /** Build advisory warnings for any request with matchCount < redactionCount.
21
+ * Returns an empty array when everything is fine. */
22
+ export function matchRedactionWarnings(requests: RequestCounts[]): string[] {
23
+ const warnings: string[] = []
24
+ for(const req of requests) {
25
+ if(req.matchCount < req.redactionCount) {
26
+ warnings.push(
27
+ `${req.label}: ${req.matchCount} response match(es) but ` +
28
+ `${req.redactionCount} response redaction(s). Some verification ` +
29
+ 'clients (e.g. the InApp SDK) may not work correctly when ' +
30
+ 'responseMatches is shorter than responseRedactions — they pair the ' +
31
+ 'two by index. This is allowed; add a functional match per redaction ' +
32
+ 'if you hit verification issues.',
33
+ )
34
+ }
35
+ }
36
+
37
+ return warnings
38
+ }
39
+
40
+ /** Counts for a single drafted request shape (provider or injected request). */
41
+ export function countsFor(
42
+ label: string,
43
+ req: {
44
+ responseMatches?: unknown[]
45
+ responseRedactions?: unknown[]
46
+ },
47
+ ): RequestCounts {
48
+ return {
49
+ label,
50
+ matchCount: req.responseMatches?.length ?? 0,
51
+ redactionCount: req.responseRedactions?.length ?? 0,
52
+ }
53
+ }