@reclaimprotocol/agent 0.1.1-dev.1 → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (201) 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/config/store.d.ts +7 -6
  9. package/lib/config/store.js +2 -2
  10. package/lib/index.d.ts +2 -2
  11. package/lib/index.js +1 -1
  12. package/lib/mcp/api/operations.gen.d.ts +101 -45
  13. package/lib/mcp/api/operations.gen.js +996 -126
  14. package/lib/mcp/api/to-zod.d.ts +24 -0
  15. package/lib/mcp/api/to-zod.js +94 -0
  16. package/lib/mcp/api/tools.js +1 -1
  17. package/lib/mcp/credentials/authenticate.d.ts +3 -0
  18. package/lib/mcp/{tools/credentials → credentials}/authenticate.js +3 -3
  19. package/lib/mcp/credentials/build.d.ts +5 -0
  20. package/lib/mcp/credentials/build.js +18 -0
  21. package/lib/mcp/{tools/credentials → credentials}/cache.d.ts +9 -9
  22. package/lib/mcp/{tools/credentials → credentials}/cache.js +36 -19
  23. package/lib/mcp/credentials/import.d.ts +2 -0
  24. package/lib/mcp/{tools/credentials → credentials}/import.js +2 -2
  25. package/lib/mcp/credentials/issue.d.ts +2 -0
  26. package/lib/mcp/{tools/credentials → credentials}/issue.js +4 -4
  27. package/lib/mcp/credentials/keypair.d.ts +17 -0
  28. package/lib/mcp/credentials/keypair.js +48 -0
  29. package/lib/mcp/credentials/link.d.ts +3 -0
  30. package/lib/mcp/credentials/link.js +107 -0
  31. package/lib/mcp/{tools/credentials → credentials}/list-cached.d.ts +1 -1
  32. package/lib/mcp/{tools/credentials → credentials}/list-cached.js +5 -5
  33. package/lib/mcp/credentials/owner-key.d.ts +23 -0
  34. package/lib/mcp/credentials/owner-key.js +58 -0
  35. package/lib/mcp/credentials/paths.d.ts +12 -0
  36. package/lib/mcp/credentials/paths.js +35 -0
  37. package/lib/mcp/credentials/poll.d.ts +27 -0
  38. package/lib/mcp/credentials/poll.js +61 -0
  39. package/lib/mcp/{tools/credentials → credentials}/resolve-owner-key.d.ts +1 -1
  40. package/lib/mcp/{tools/credentials → credentials}/resolve-owner-key.js +2 -2
  41. package/lib/mcp/credentials/session-cache.d.ts +14 -0
  42. package/lib/mcp/credentials/session-cache.js +42 -0
  43. package/lib/mcp/{tools/credentials → credentials}/status.d.ts +1 -1
  44. package/lib/mcp/credentials/status.js +76 -0
  45. package/lib/mcp/instructions.d.ts +1 -0
  46. package/lib/mcp/instructions.js +11 -0
  47. package/lib/mcp/register.d.ts +38 -0
  48. package/lib/mcp/register.js +80 -0
  49. package/lib/mcp/server.d.ts +2 -2
  50. package/lib/mcp/server.js +3 -3
  51. package/lib/mcp/start.js +10 -4
  52. package/lib/mcp/tools/agent/build.js +2 -15
  53. package/lib/mcp/tools/agent/index.d.ts +2 -0
  54. package/lib/mcp/tools/agent/index.js +3 -0
  55. package/lib/mcp/tools/agent/proof.js +1 -6
  56. package/lib/mcp/tools/agent/propose.js +13 -2
  57. package/lib/mcp/tools/agent/publish.js +10 -2
  58. package/lib/mcp/tools/agent/replay.js +0 -5
  59. package/lib/mcp/tools/analyze.d.ts +3 -0
  60. package/lib/mcp/tools/analyze.js +26 -0
  61. package/lib/mcp/tools/attach.d.ts +11 -0
  62. package/lib/mcp/tools/attach.js +72 -0
  63. package/lib/mcp/tools/authenticate/authenticate.js +107 -0
  64. package/lib/mcp/tools/authenticate/build.d.ts +15 -0
  65. package/lib/mcp/tools/authenticate/build.js +28 -0
  66. package/lib/mcp/tools/authenticate/decrypt-key.d.ts +32 -0
  67. package/lib/mcp/tools/authenticate/decrypt-key.js +49 -0
  68. package/lib/mcp/tools/authenticate/eth-key.d.ts +38 -0
  69. package/lib/mcp/tools/authenticate/eth-key.js +130 -0
  70. package/lib/mcp/tools/authenticate/import.js +57 -0
  71. package/lib/mcp/tools/authenticate/index.d.ts +2 -0
  72. package/lib/mcp/tools/authenticate/index.js +4 -0
  73. package/lib/mcp/tools/authenticate/issue.js +42 -0
  74. package/lib/mcp/tools/authenticate/resolve-owner-key.d.ts +13 -0
  75. package/lib/mcp/tools/authenticate/resolve-owner-key.js +53 -0
  76. package/lib/mcp/tools/capture.d.ts +10 -0
  77. package/lib/mcp/tools/capture.js +142 -0
  78. package/lib/mcp/tools/eval.d.ts +5 -0
  79. package/lib/mcp/tools/eval.js +127 -0
  80. package/lib/mcp/tools/find.d.ts +3 -0
  81. package/lib/mcp/tools/find.js +60 -0
  82. package/lib/mcp/tools/index.d.ts +3 -0
  83. package/lib/mcp/tools/index.js +6 -0
  84. package/lib/mcp/tools/owner-key.d.ts +2 -0
  85. package/lib/mcp/tools/owner-key.js +63 -0
  86. package/lib/mcp/tools/proof.d.ts +3 -0
  87. package/lib/mcp/tools/proof.js +214 -0
  88. package/lib/mcp/tools/propose.d.ts +9 -0
  89. package/lib/mcp/tools/propose.js +75 -0
  90. package/lib/mcp/tools/publish.d.ts +3 -0
  91. package/lib/mcp/tools/publish.js +59 -0
  92. package/lib/mcp/tools/replay.d.ts +3 -0
  93. package/lib/mcp/tools/replay.js +46 -0
  94. package/lib/mcp/tools/session.d.ts +10 -0
  95. package/lib/mcp/tools/session.js +29 -0
  96. package/lib/mcp/tools/verification/build.d.ts +12 -0
  97. package/lib/mcp/tools/verification/build.js +14 -0
  98. package/lib/mcp/tools/verification/diagnose.d.ts +11 -0
  99. package/lib/mcp/tools/verification/diagnose.js +132 -0
  100. package/lib/mcp/tools/verification/index.d.ts +1 -0
  101. package/lib/mcp/tools/verification/index.js +2 -0
  102. package/lib/mcp/tools/verification/result.d.ts +12 -0
  103. package/lib/mcp/tools/verification/result.js +126 -0
  104. package/lib/mcp/verifications/build.d.ts +5 -0
  105. package/lib/mcp/verifications/build.js +14 -0
  106. package/lib/mcp/verifications/create.d.ts +3 -0
  107. package/lib/mcp/verifications/create.js +57 -0
  108. package/lib/mcp/verifications/find.d.ts +3 -0
  109. package/lib/mcp/verifications/find.js +37 -0
  110. package/lib/mcp/verifications/get.d.ts +3 -0
  111. package/lib/mcp/verifications/get.js +26 -0
  112. package/lib/mcp/verifications/update.d.ts +3 -0
  113. package/lib/mcp/verifications/update.js +78 -0
  114. package/lib/old/client.d.ts +3 -0
  115. package/lib/old/instructions.d.ts +0 -6
  116. package/lib/old/instructions.js +6 -24
  117. package/lib/old/to-register.d.ts +3 -0
  118. package/lib/old/to-register.js +35 -7
  119. package/lib/old/tools/publish.js +13 -1
  120. package/lib/proof/attestor.d.ts +33 -1
  121. package/lib/proof/attestor.js +65 -63
  122. package/lib/proof/index.d.ts +5 -0
  123. package/lib/proof/index.js +6 -0
  124. package/lib/proof/proof-core.d.ts +35 -0
  125. package/lib/proof/proof-core.js +73 -0
  126. package/lib/provider/index.d.ts +5 -0
  127. package/lib/provider/index.js +9 -0
  128. package/lib/provider/to-version.d.ts +9 -12
  129. package/lib/provider/to-version.js +38 -4
  130. package/lib/provider/verification-checks.d.ts +12 -0
  131. package/lib/provider/verification-checks.js +30 -0
  132. package/lib/routes.gen.d.ts +271 -0
  133. package/lib/routes.gen.js +73 -0
  134. package/lib/types/openapi.gen.d.ts +3967 -0
  135. package/lib/types/openapi.gen.js +5 -0
  136. package/package.json +16 -7
  137. package/skill/SKILL.md +35 -14
  138. package/skill/builder-mode.md +9 -0
  139. package/skill/old-mode.md +13 -0
  140. package/src/cdp/capture-engine.ts +256 -0
  141. package/src/cdp/capture.ts +25 -185
  142. package/src/cdp/index.ts +6 -0
  143. package/src/config/store.ts +7 -6
  144. package/src/index.ts +2 -4
  145. package/src/mcp/api/operations.gen.ts +1023 -121
  146. package/src/mcp/api/to-zod.ts +115 -0
  147. package/src/mcp/api/tools.ts +9 -3
  148. package/src/mcp/instructions.ts +15 -0
  149. package/src/mcp/server.ts +5 -4
  150. package/src/mcp/start.ts +14 -4
  151. package/src/mcp/tools/agent/build.ts +2 -18
  152. package/src/mcp/tools/agent/index.ts +4 -0
  153. package/src/mcp/tools/agent/proof.ts +1 -7
  154. package/src/mcp/tools/agent/propose.ts +13 -2
  155. package/src/mcp/tools/agent/publish.ts +10 -2
  156. package/src/mcp/tools/agent/replay.ts +0 -5
  157. package/src/mcp/tools/{credentials → authenticate}/authenticate.ts +9 -8
  158. package/src/mcp/tools/authenticate/build.ts +32 -0
  159. package/src/mcp/tools/authenticate/decrypt-key.ts +64 -0
  160. package/src/mcp/tools/authenticate/eth-key.ts +182 -0
  161. package/src/mcp/tools/authenticate/import.ts +73 -0
  162. package/src/mcp/tools/authenticate/index.ts +4 -0
  163. package/src/mcp/tools/authenticate/issue.ts +50 -0
  164. package/src/mcp/tools/authenticate/resolve-owner-key.ts +60 -0
  165. package/src/mcp/tools/index.ts +6 -0
  166. package/src/mcp/tools/verification/build.ts +16 -0
  167. package/src/mcp/tools/verification/index.ts +2 -0
  168. package/src/mcp/tools/verification/result.ts +155 -0
  169. package/src/old/client.ts +3 -0
  170. package/src/old/instructions.ts +10 -25
  171. package/src/old/to-register.ts +62 -27
  172. package/src/old/tools/publish.ts +17 -1
  173. package/src/proof/attestor.ts +112 -71
  174. package/src/proof/index.ts +21 -0
  175. package/src/proof/proof-core.ts +109 -0
  176. package/src/provider/index.ts +20 -0
  177. package/src/provider/to-version.ts +52 -11
  178. package/src/provider/verification-checks.ts +36 -0
  179. package/lib/mcp/tools/credentials/build.d.ts +0 -16
  180. package/lib/mcp/tools/credentials/build.js +0 -33
  181. package/lib/mcp/tools/credentials/status.js +0 -62
  182. package/src/mcp/tools/credentials/build.ts +0 -37
  183. package/src/mcp/tools/credentials/cache.ts +0 -161
  184. package/src/mcp/tools/credentials/env-file.ts +0 -101
  185. package/src/mcp/tools/credentials/import.ts +0 -72
  186. package/src/mcp/tools/credentials/issue.ts +0 -107
  187. package/src/mcp/tools/credentials/list-cached.ts +0 -66
  188. package/src/mcp/tools/credentials/register.ts +0 -49
  189. package/src/mcp/tools/credentials/resolve-owner-key.ts +0 -78
  190. package/src/mcp/tools/credentials/status.ts +0 -82
  191. package/src/types/openapi.ts +0 -7
  192. /package/lib/mcp/{tools/credentials → credentials}/env-file.d.ts +0 -0
  193. /package/lib/mcp/{tools/credentials → credentials}/env-file.js +0 -0
  194. /package/lib/mcp/{tools/credentials → credentials}/register.d.ts +0 -0
  195. /package/lib/mcp/{tools/credentials → credentials}/register.js +0 -0
  196. /package/lib/mcp/tools/{credentials → authenticate}/authenticate.d.ts +0 -0
  197. /package/lib/mcp/tools/{credentials → authenticate}/import.d.ts +0 -0
  198. /package/lib/mcp/tools/{credentials → authenticate}/issue.d.ts +0 -0
  199. /package/lib/mcp/tools/{credentials → authenticate}/session-cache.d.ts +0 -0
  200. /package/lib/mcp/tools/{credentials → authenticate}/session-cache.js +0 -0
  201. /package/src/mcp/tools/{credentials → authenticate}/session-cache.ts +0 -0
@@ -1,9 +1,16 @@
1
- import type { ReclaimProvider } from '../provider/schema.ts'
1
+ import type { ReclaimProvider, ResponseRedaction } from '../provider/schema.ts'
2
2
  import type {
3
3
  RegisterProviderBody,
4
4
  UpdateProviderConfigBody,
5
5
  } from './client.ts'
6
6
 
7
+ /** Default web settings, mirroring the builder openapi defaults
8
+ * (`ProviderWebSettings.geoLocation`, `PortalClientOptions.useProxy`). The old
9
+ * backend accepts both: `geoLocation` lives on the providerConfig, `useProxy`
10
+ * is a Provider-level flag (register only). */
11
+ const DEFAULT_GEO_LOCATION = '{{DYNAMIC_GEO}}'
12
+ const DEFAULT_USE_PROXY = true
13
+
7
14
  /**
8
15
  * Translate a drafted `ReclaimProvider` (from `propose_provider`) into the
9
16
  * OLD devtools backend shapes. Deliberately a SEPARATE module from
@@ -22,7 +29,10 @@ import type {
22
29
  export interface OldProviderOpts {
23
30
  initialUrl: string
24
31
  description?: string
32
+ /** Defaults to `{{DYNAMIC_GEO}}` when omitted. */
25
33
  geoLocation?: string
34
+ /** Defaults to `true` when omitted. Register only (Provider-level flag). */
35
+ useProxy?: boolean
26
36
  providerType?: 'PRIVATE' | 'PUBLIC'
27
37
  }
28
38
 
@@ -63,6 +73,20 @@ function requestDataFromProvider(provider: ReclaimProvider): OldRequestData {
63
73
  )
64
74
  }
65
75
 
76
+ // The old backend has no dotted interpolation: consumer-supplied params
77
+ // must use the `{{context_<name>}}` form here, not the builder's
78
+ // `{{context.<name>}}`. Catch the mix-up at publish with a clear error
79
+ // instead of a template that silently never resolves.
80
+ const dotted = `${provider.url}\n${provider.body ?? ''}`
81
+ .match(/\{\{context\.[A-Za-z_][A-Za-z0-9_]*\}\}/)
82
+ if(dotted) {
83
+ throw new Error(
84
+ `Old devtools does not support ${dotted[0]} — rename ` +
85
+ 'consumer-supplied params to the {{context_<name>}} form ' +
86
+ 'for this backend.',
87
+ )
88
+ }
89
+
66
90
  const responseMatches: OldResponseMatch[] = provider.responseMatches.map(
67
91
  (m, i) => ({
68
92
  value: m.value,
@@ -72,29 +96,40 @@ function requestDataFromProvider(provider: ReclaimProvider): OldRequestData {
72
96
  }),
73
97
  )
74
98
 
75
- const responseRedactions: OldResponseRedaction[] = (
76
- provider.responseRedactions ?? []
77
- ).map((r, i) => {
78
- const out: OldResponseRedaction = { order: i }
79
- if(r.xPath !== undefined) {
80
- out.xPath = r.xPath
81
- }
99
+ // Old devtools wants `responseMatches` and `responseRedactions` as PARALLEL
100
+ // arrays. A drafted provider may instead carry each redaction nested in its
101
+ // match's `extract` (the builder shape). Prefer the explicit parallel array;
102
+ // fall back to the nested `extract`s so redactions are never dropped.
103
+ const nestedRedactions = provider.responseMatches
104
+ .map((m) => (m as { extract?: ResponseRedaction }).extract)
105
+ .filter((e): e is ResponseRedaction => Boolean(e))
106
+ const sourceRedactions = provider.responseRedactions?.length
107
+ ? provider.responseRedactions
108
+ : nestedRedactions
82
109
 
83
- if(r.jsonPath !== undefined) {
84
- out.jsonPath = r.jsonPath
85
- }
110
+ const responseRedactions: OldResponseRedaction[] = sourceRedactions.map(
111
+ (r, i) => {
112
+ const out: OldResponseRedaction = { order: i }
113
+ if(r.xPath !== undefined) {
114
+ out.xPath = r.xPath
115
+ }
86
116
 
87
- if(r.regex !== undefined) {
88
- out.regex = r.regex
89
- }
117
+ if(r.jsonPath !== undefined) {
118
+ out.jsonPath = r.jsonPath
119
+ }
90
120
 
91
- // OPRF redactions are supported — pass the mode through unchanged.
92
- if(r.hash !== undefined) {
93
- out.hash = r.hash
94
- }
121
+ if(r.regex !== undefined) {
122
+ out.regex = r.regex
123
+ }
95
124
 
96
- return out
97
- })
125
+ // OPRF redactions are supported — pass the mode through unchanged.
126
+ if(r.hash !== undefined) {
127
+ out.hash = r.hash
128
+ }
129
+
130
+ return out
131
+ },
132
+ )
98
133
 
99
134
  const requestData: OldRequestData = {
100
135
  url: provider.url,
@@ -127,16 +162,15 @@ export function providerToRegisterBody(
127
162
  name: provider.name,
128
163
  loginUrl: opts.initialUrl,
129
164
  providerType: opts.providerType ?? 'PRIVATE',
165
+ // Web-settings defaults mirror the builder openapi defaults.
166
+ geoLocation: opts.geoLocation ?? DEFAULT_GEO_LOCATION,
167
+ useProxy: opts.useProxy ?? DEFAULT_USE_PROXY,
130
168
  requestData: [requestDataFromProvider(provider)],
131
169
  }
132
170
  if(opts.description !== undefined) {
133
171
  body.description = opts.description
134
172
  }
135
173
 
136
- if(opts.geoLocation !== undefined) {
137
- body.geoLocation = opts.geoLocation
138
- }
139
-
140
174
  return body
141
175
  }
142
176
 
@@ -149,13 +183,14 @@ export function providerToConfigBody(
149
183
  version: string,
150
184
  versionInfo: string,
151
185
  ): UpdateProviderConfigBody {
186
+ // `useProxy` is a Provider-level flag on the old backend, not part of a
187
+ // version's providerConfig — so the config (new-version) path only carries
188
+ // `geoLocation`. It defaults the same way as register.
152
189
  const providerConfig: Record<string, unknown> = {
153
190
  loginUrl: opts.initialUrl,
191
+ geoLocation: opts.geoLocation ?? DEFAULT_GEO_LOCATION,
154
192
  requestData: [requestDataFromProvider(provider)],
155
193
  }
156
- if(opts.geoLocation !== undefined) {
157
- providerConfig.geoLocation = opts.geoLocation
158
- }
159
194
 
160
195
  return { providerConfig, version, versionInfo }
161
196
  }
@@ -33,6 +33,7 @@ export function createVersionFromCaptureTool(
33
33
  providerId?: string
34
34
  description?: string
35
35
  geoLocation?: string
36
+ useProxy?: boolean
36
37
  providerType?: 'PRIVATE' | 'PUBLIC'
37
38
  notes?: string
38
39
  }>(
@@ -83,7 +84,18 @@ export function createVersionFromCaptureTool(
83
84
  type: 'string',
84
85
  description: 'Provider description (new-provider register only).',
85
86
  },
86
- geoLocation: { type: 'string' },
87
+ geoLocation: {
88
+ type: 'string',
89
+ description:
90
+ 'Geo the verification appears to originate from. Defaults to ' +
91
+ "'{{DYNAMIC_GEO}}' (the user's geo) when omitted.",
92
+ },
93
+ useProxy: {
94
+ type: 'boolean',
95
+ description:
96
+ 'Route portal/attestation traffic through a proxy. Defaults to ' +
97
+ 'true when omitted. Applied on register only.',
98
+ },
87
99
  providerType: { type: 'string', enum: ['PRIVATE', 'PUBLIC'] },
88
100
  notes: {
89
101
  type: 'string',
@@ -105,6 +117,10 @@ export function createVersionFromCaptureTool(
105
117
  opts.geoLocation = args.geoLocation
106
118
  }
107
119
 
120
+ if(args.useProxy !== undefined) {
121
+ opts.useProxy = args.useProxy
122
+ }
123
+
108
124
  if(args.providerType !== undefined) {
109
125
  opts.providerType = args.providerType
110
126
  }
@@ -6,11 +6,15 @@
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 { resolveEthKey } from '../mcp/tools/authenticate/eth-key.ts'
13
14
  import type { ReclaimProvider } from '../provider/schema.ts'
15
+ import {
16
+ buildHttpParams, ensureAttestorCrypto, parseClaim, resolveAttestorUrl,
17
+ } from './proof-core.ts'
14
18
  import { ensureZkFiles } from './zk-files.ts'
15
19
 
16
20
  export interface AttestorLogEntry {
@@ -19,6 +23,37 @@ export interface AttestorLogEntry {
19
23
  ctx?: Record<string, unknown>
20
24
  }
21
25
 
26
+ /** The attestor's signed claim data + signature, for building the nested proof
27
+ * JWS a Consumer verifies against the attestor's key. Populated when
28
+ * `includeAttestorClaim` is set (the app's verification runner needs it; the
29
+ * MCP `run_proof` tool ignores it). */
30
+ export interface AttestorClaim {
31
+ /** ProviderClaimData: provider, parameters, context (JSON string), owner,
32
+ * timestampS, epoch, identifier. */
33
+ claim: Record<string, unknown>
34
+ /** Hex (0x) attestor ETH address that signed the claim (the nested `kid`). */
35
+ attestorAddress: string
36
+ /** Hex (0x) attestor claim signature (EIP-191 over the claim sign-data). */
37
+ claimSignature: string
38
+ }
39
+
40
+ /** Options for {@link runAttestorProof}. */
41
+ export interface RunAttestorProofOptions {
42
+ attestorUrl?: string
43
+ /** When resolving the key from env/.env/file, the address the caller expects
44
+ * to sign with (sanity check). Ignored when `ownerPrivateKey` is given. */
45
+ ownerAddress?: string
46
+ /** Sign with this key directly (e.g. the app/VC's ephemeral per-session key)
47
+ * instead of resolving from env/.env/file. */
48
+ ownerPrivateKey?: string
49
+ /** ZK engine for `createClaimOnAttestor`. Defaults to `'gnark'`; the app's
50
+ * server-side verification runner uses `'snarkjs'`. */
51
+ zkEngine?: 'gnark' | 'snarkjs'
52
+ /** Extract the structured {@link AttestorClaim} (claim + attestor signature)
53
+ * onto the result for nested-proof construction. */
54
+ includeAttestorClaim?: boolean
55
+ }
56
+
22
57
  export interface ProofResult {
23
58
  proof: unknown
24
59
  extractedValue: string
@@ -27,6 +62,9 @@ export interface ProofResult {
27
62
  owner?: string
28
63
  verified: boolean
29
64
  error?: string
65
+ /** Structured claim + signature for nested-proof verification. Set only when
66
+ * `includeAttestorClaim` was requested and the claim carries signatures. */
67
+ attestorClaim?: AttestorClaim
30
68
  /** Set to `'missing-owner-key'` when no credential was available to
31
69
  * sign with — distinguishes this recoverable case from other proof
32
70
  * errors so the skill can point the dev at `issue_credentials` /
@@ -139,16 +177,6 @@ function makeCaptureLogger(): {
139
177
  return { logger: make({}), entries }
140
178
  }
141
179
 
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
180
  // ---------------------------------------------------------------------------
153
181
  // Main entry point
154
182
  // ---------------------------------------------------------------------------
@@ -156,45 +184,60 @@ function resolveAttestorUrl(override?: string): string {
156
184
  export async function runAttestorProof(
157
185
  provider: ReclaimProvider,
158
186
  secrets: Record<string, string>,
159
- attestorUrl?: string,
160
- ownerAddress?: string,
187
+ opts: RunAttestorProofOptions = {},
161
188
  ): 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',
189
+ // Sign with the caller-supplied key (the app/VC's ephemeral per-session
190
+ // key) when given; otherwise resolve env/.env/file. `ownerAddress` is a
191
+ // sanity check on the resolved key (ignored when a key is supplied).
192
+ let ownerPrivateKey: string
193
+ if(opts.ownerPrivateKey) {
194
+ ownerPrivateKey = opts.ownerPrivateKey
195
+ } else {
196
+ const resolved = resolveEthKey()
197
+ if(!resolved) {
198
+ return {
199
+ proof: undefined,
200
+ extractedValue: '',
201
+ extractedParameters: {},
202
+ verified: false,
203
+ error:
204
+ 'No eth proof-owner key available (no RECLAIM_PRIVATE_KEY in env, ' +
205
+ '.env, or RECLAIM_PRIVATE_KEY_FILE). Call resolve_owner_key, then ' +
206
+ 'issue_credentials (generate) or import_credentials (provide one), ' +
207
+ 'and retry run_proof.',
208
+ errorKind: 'missing-owner-key',
209
+ }
179
210
  }
211
+
212
+ if(
213
+ opts.ownerAddress
214
+ && opts.ownerAddress.toLowerCase() !== resolved.address
215
+ ) {
216
+ return {
217
+ proof: undefined,
218
+ extractedValue: '',
219
+ extractedParameters: {},
220
+ verified: false,
221
+ error:
222
+ `Requested ownerAddress "${opts.ownerAddress}" does not match ` +
223
+ `the resolved eth key (${resolved.address}, source: ` +
224
+ `${resolved.source}). ` +
225
+ 'Call resolve_owner_key to get the current address, or update the ' +
226
+ 'configured key.',
227
+ errorKind: 'missing-owner-key',
228
+ }
229
+ }
230
+
231
+ ownerPrivateKey = resolved.privateKey
180
232
  }
181
233
 
182
234
  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.
235
+ // Init the TLS crypto backend (idempotent), then fetch ZK circuits
236
+ // lazily on the first proof (not at install time).
237
+ ensureAttestorCrypto()
185
238
  await ensureZkFiles()
186
239
 
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
- }
240
+ const params = buildHttpParams(provider)
198
241
 
199
242
  // Diagnostic logger capture is gated behind RECLAIM_AGENT_TRACE so the
200
243
  // happy path doesn't pay the cost (the attestor SDK emits a *lot* of
@@ -214,8 +257,8 @@ export async function runAttestorProof(
214
257
  headers: secrets,
215
258
  },
216
259
  ownerPrivateKey,
217
- client: { url: resolveAttestorUrl(attestorUrl) },
218
- zkEngine: 'snarkjs',
260
+ client: { url: resolveAttestorUrl(opts.attestorUrl) },
261
+ zkEngine: opts.zkEngine ?? 'gnark',
219
262
  ...(capture !== undefined && { logger: capture.logger as Logger }),
220
263
  })
221
264
  } catch(err) {
@@ -234,27 +277,8 @@ export async function runAttestorProof(
234
277
  return errorResult
235
278
  }
236
279
 
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] ?? ''
280
+ const { extractedValue, extractedParameters, identifier, owner, claimData }
281
+ = parseClaim(claim)
258
282
 
259
283
  const result: ProofResult = {
260
284
  proof: claim,
@@ -262,16 +286,33 @@ export async function runAttestorProof(
262
286
  extractedParameters,
263
287
  verified: true,
264
288
  }
265
- const identifier = claimData?.['identifier']
266
- if(typeof identifier === 'string') {
289
+ if(identifier !== undefined) {
267
290
  result.identifier = identifier
268
291
  }
269
292
 
270
- const owner = claimData?.['owner']
271
- if(typeof owner === 'string') {
293
+ if(owner !== undefined) {
272
294
  result.owner = owner
273
295
  }
274
296
 
297
+ // Structured claim + signature so the result JWS can carry a nested,
298
+ // attestor-signed proof the Consumer verifies independently.
299
+ if(opts.includeAttestorClaim) {
300
+ const sigs = (claim as {
301
+ signatures?: { attestorAddress?: unknown, claimSignature?: unknown }
302
+ }).signatures
303
+ if(
304
+ claimData
305
+ && typeof sigs?.attestorAddress === 'string'
306
+ && sigs.claimSignature instanceof Uint8Array
307
+ ) {
308
+ result.attestorClaim = {
309
+ claim: claimData,
310
+ attestorAddress: sigs.attestorAddress,
311
+ claimSignature: '0x' + bytesToHex(sigs.claimSignature),
312
+ }
313
+ }
314
+ }
315
+
275
316
  // Only set when RECLAIM_AGENT_TRACE=1. Full stream — the MCP layer
276
317
  // trims for the LLM-visible response; the on-disk dump gets it all.
277
318
  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,109 @@
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 type { ReclaimProvider } from '../provider/schema.ts'
13
+
14
+ const FALLBACK_ATTESTOR_URL = 'wss://attestor.reclaimprotocol.org:444/ws'
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 ?? process.env['RECLAIM_ATTESTOR_URL'] ?? 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
+ return {
47
+ url: provider.url,
48
+ method: provider.method,
49
+ ...(provider.headers !== undefined && { headers: provider.headers }),
50
+ ...(provider.body !== undefined && { body: provider.body }),
51
+ responseMatches: provider.responseMatches,
52
+ responseRedactions: provider.responseRedactions ?? [],
53
+ ...(provider.paramValues !== undefined && {
54
+ paramValues: provider.paramValues,
55
+ }),
56
+ }
57
+ }
58
+
59
+ export interface ParsedClaim {
60
+ /** ProviderClaimData: provider, parameters, context, owner, identifier, … */
61
+ claimData?: Record<string, unknown>
62
+ extractedParameters: Record<string, string>
63
+ extractedValue: string
64
+ identifier?: string
65
+ owner?: string
66
+ }
67
+
68
+ /** Pull claimData + decoded `context.extractedParameters` (plus identifier /
69
+ * owner) out of a `createClaimOnAttestor` result. The full result still
70
+ * carries signatures the caller may need for nested-proof building. */
71
+ export function parseClaim(claim: unknown): ParsedClaim {
72
+ const claimRecord = claim as Record<string, unknown>
73
+ const claimData = claimRecord['claim'] as Record<string, unknown> | undefined
74
+
75
+ // claimData.context is a JSON-encoded string carrying
76
+ // { extractedParameters: {...}, providerHash, ... }.
77
+ let context: Record<string, unknown> = {}
78
+ const rawContext = claimData?.['context']
79
+ if(typeof rawContext === 'string') {
80
+ try {
81
+ context = JSON.parse(rawContext) as Record<string, unknown>
82
+ } catch{
83
+ // Leave context empty if it isn't valid JSON.
84
+ }
85
+ }
86
+
87
+ const extractedParameters =
88
+ (context['extractedParameters'] as Record<string, string> | undefined) ?? {}
89
+
90
+ const parsed: ParsedClaim = {
91
+ extractedParameters,
92
+ extractedValue: Object.values(extractedParameters)[0] ?? '',
93
+ }
94
+ if(claimData) {
95
+ parsed.claimData = claimData
96
+ }
97
+
98
+ const identifier = claimData?.['identifier']
99
+ if(typeof identifier === 'string') {
100
+ parsed.identifier = identifier
101
+ }
102
+
103
+ const owner = claimData?.['owner']
104
+ if(typeof owner === 'string') {
105
+ parsed.owner = owner
106
+ }
107
+
108
+ return parsed
109
+ }
@@ -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'