@reclaimprotocol/agent 0.1.23 → 0.1.24
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +55 -20
- package/lib/authoring/backend.d.ts +5 -4
- package/lib/authoring/tools.js +30 -30
- package/lib/cdp/browser-proxy.d.ts +78 -0
- package/lib/cdp/browser-proxy.js +186 -0
- package/lib/cdp/capture-engine.js +8 -7
- package/lib/cdp/client.d.ts +10 -0
- package/lib/cdp/geo.d.ts +10 -0
- package/lib/cdp/geo.js +36 -0
- package/lib/cdp/index.d.ts +1 -0
- package/lib/cdp/index.js +1 -0
- package/lib/live/cloudflared.d.ts +15 -0
- package/lib/live/cloudflared.js +268 -0
- package/lib/live/page.d.ts +20 -0
- package/lib/live/page.js +134 -0
- package/lib/live/tunnel.d.ts +23 -0
- package/lib/live/tunnel.js +120 -0
- package/lib/live/viewer.d.ts +28 -0
- package/lib/live/viewer.js +232 -0
- package/lib/mcp/api/operations.gen.js +34 -26
- package/lib/mcp/api/to-zod.d.ts +1 -1
- package/lib/mcp/api/to-zod.js +1 -1
- package/lib/mcp/api/tools.js +1 -1
- package/lib/mcp/instructions.d.ts +15 -2
- package/lib/mcp/instructions.js +101 -38
- package/lib/mcp/start.js +4 -3
- package/lib/mcp/tools/agent/analyze.js +5 -1
- package/lib/mcp/tools/agent/attach.d.ts +8 -1
- package/lib/mcp/tools/agent/attach.js +166 -22
- package/lib/mcp/tools/agent/build.d.ts +13 -8
- package/lib/mcp/tools/agent/build.js +22 -11
- package/lib/mcp/tools/agent/capture.js +5 -1
- package/lib/mcp/tools/agent/eval.js +3 -1
- package/lib/mcp/tools/agent/find.js +6 -5
- package/lib/mcp/tools/agent/how-it-works.d.ts +15 -7
- package/lib/mcp/tools/agent/how-it-works.js +57 -17
- package/lib/mcp/tools/agent/live-view.d.ts +21 -0
- package/lib/mcp/tools/agent/live-view.js +153 -0
- package/lib/mcp/tools/agent/pod-proof.d.ts +8 -0
- package/lib/mcp/tools/agent/pod-proof.js +64 -0
- package/lib/mcp/tools/agent/propose.js +4 -2
- package/lib/mcp/tools/agent/proxy.d.ts +3 -0
- package/lib/mcp/tools/agent/proxy.js +199 -0
- package/lib/mcp/tools/agent/publish.js +47 -77
- package/lib/mcp/tools/agent/session.d.ts +2 -1
- package/lib/mcp/tools/agent/session.js +26 -7
- package/lib/mcp/tools/authenticate/authenticate.js +26 -13
- package/lib/mcp/tools/mode/mode.d.ts +1 -1
- package/lib/mcp/tools/mode/mode.js +10 -22
- package/lib/old/client.d.ts +1 -1
- package/lib/old/client.js +1 -1
- package/lib/old/instructions.d.ts +9 -1
- package/lib/old/instructions.js +4 -6
- package/lib/old/login-cdp.d.ts +2 -2
- package/lib/old/login-cdp.js +46 -6
- package/lib/old/to-register.d.ts +3 -2
- package/lib/old/to-register.js +1 -1
- package/lib/old/tools/authenticate.js +9 -1
- package/lib/old/tools/get-provider-info.js +6 -5
- package/lib/old/tools/issue-app-credentials.js +3 -2
- package/lib/old/tools/publish.d.ts +1 -1
- package/lib/old/tools/publish.js +90 -131
- package/lib/old/tools/update-provider-metadata.d.ts +1 -1
- package/lib/old/tools/update-provider-metadata.js +1 -1
- package/lib/old/tools/verify-sdk-setup.js +1 -1
- package/lib/proof/attestor.d.ts +3 -3
- package/lib/proof/debug-response-dump.js +4 -4
- package/lib/proof/index.d.ts +1 -0
- package/lib/proof/index.js +1 -0
- package/lib/proof/page-replay.d.ts +1 -1
- package/lib/proof/page-replay.js +1 -1
- package/lib/proof/pod-prover.d.ts +47 -0
- package/lib/proof/pod-prover.js +155 -0
- package/lib/provider/dom-xpath.js +11 -9
- package/lib/provider/draft.d.ts +10 -7
- package/lib/provider/draft.js +11 -9
- package/lib/provider/injection-doc.d.ts +23 -1
- package/lib/provider/injection-doc.js +34 -21
- package/lib/provider/match-redaction-warning.d.ts +2 -2
- package/lib/provider/match-redaction-warning.js +3 -3
- package/lib/provider/matcher.js +13 -12
- package/lib/provider/secrets.d.ts +3 -3
- package/lib/provider/secrets.js +3 -3
- package/lib/provider/to-version.d.ts +7 -6
- package/lib/provider/to-version.js +3 -3
- package/lib/tools/registry.d.ts +5 -5
- package/lib/tools/registry.js +5 -5
- package/package.json +3 -3
- package/samples/demo-server/README.md +7 -6
- package/skill/SKILL.builder.md +356 -189
- package/skill/SKILL.common.md +323 -97
- package/skill/SKILL.old.md +340 -155
- package/skill/builder-mode.md +58 -17
- package/skill/hash-validation.md +170 -0
- package/skill/interception.md +235 -0
- package/skill/js-sdk-integration.md +13 -7
- package/skill/old-mode.md +60 -21
- package/skill/user-script.md +61 -0
- package/src/authoring/backend.ts +5 -4
- package/src/authoring/tools.ts +31 -30
- package/src/cdp/capture-engine.ts +8 -7
- package/src/cdp/client.ts +8 -0
- package/src/cdp/geo.ts +52 -0
- package/src/cdp/index.ts +1 -0
- package/src/consts.ts +1 -0
- package/src/live/cloudflared.ts +348 -0
- package/src/live/page.ts +145 -0
- package/src/live/tunnel.ts +157 -0
- package/src/live/viewer.ts +293 -0
- package/src/mcp/api/operations.gen.ts +34 -26
- package/src/mcp/api/to-zod.ts +1 -1
- package/src/mcp/api/tools.ts +1 -1
- package/src/mcp/instructions.ts +121 -48
- package/src/mcp/start.ts +4 -3
- package/src/mcp/tools/agent/analyze.ts +5 -1
- package/src/mcp/tools/agent/attach.ts +194 -22
- package/src/mcp/tools/agent/build.ts +26 -15
- package/src/mcp/tools/agent/capture.ts +5 -1
- package/src/mcp/tools/agent/eval.ts +4 -1
- package/src/mcp/tools/agent/find.ts +6 -5
- package/src/mcp/tools/agent/how-it-works.ts +62 -21
- package/src/mcp/tools/agent/live-view.ts +198 -0
- package/src/mcp/tools/agent/pod-proof.ts +90 -0
- package/src/mcp/tools/agent/propose.ts +4 -2
- package/src/mcp/tools/agent/publish.ts +52 -77
- package/src/mcp/tools/agent/session.ts +26 -5
- package/src/mcp/tools/authenticate/authenticate.ts +28 -13
- package/src/mcp/tools/mode/mode.ts +10 -22
- package/src/old/client.ts +2 -2
- package/src/old/instructions.ts +4 -10
- package/src/old/login-cdp.ts +50 -6
- package/src/old/to-register.ts +3 -2
- package/src/old/tools/authenticate.ts +9 -1
- package/src/old/tools/get-provider-info.ts +6 -5
- package/src/old/tools/issue-app-credentials.ts +3 -2
- package/src/old/tools/publish.ts +95 -131
- package/src/old/tools/update-provider-metadata.ts +1 -1
- package/src/old/tools/verify-sdk-setup.ts +1 -1
- package/src/proof/attestor.ts +3 -3
- package/src/proof/debug-response-dump.ts +4 -4
- package/src/proof/index.ts +8 -0
- package/src/proof/page-replay.ts +1 -1
- package/src/proof/pod-prover.ts +231 -0
- package/src/provider/dom-xpath.ts +11 -9
- package/src/provider/draft.ts +13 -10
- package/src/provider/injection-doc.ts +37 -24
- package/src/provider/match-redaction-warning.ts +3 -3
- package/src/provider/matcher.ts +13 -12
- package/src/provider/secrets.ts +3 -3
- package/src/provider/to-version.ts +11 -9
- package/src/tools/registry.ts +5 -5
- package/skill/injection-api.md +0 -364
package/skill/SKILL.common.md
CHANGED
|
@@ -1,111 +1,337 @@
|
|
|
1
|
-
<!-- section:
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
>
|
|
5
|
-
>
|
|
6
|
-
>
|
|
7
|
-
>
|
|
8
|
-
> "
|
|
9
|
-
>
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
1
|
+
<!-- section: terminology -->
|
|
2
|
+
> **provider** = the recipe you author (the `responseMatches`/`responseRedactions`
|
|
3
|
+
> spec behind a `providerId`). **verification** = the runtime act of an end-user
|
|
4
|
+
> proving a data point *using* a provider. Never call a provider a
|
|
5
|
+
> "verification". If the developer does, use "provider" yourself.
|
|
6
|
+
>
|
|
7
|
+
> **user script** = the plain browser JS that runs before every page load
|
|
8
|
+
> ("JS injection", "custom injection", "web script" all mean this). Builder's
|
|
9
|
+
> field is `jsUserScripts`; old-devtools' is `customInjection`. Same thing.
|
|
10
|
+
|
|
11
|
+
**Your stance is driver, not orchestrator.** Navigation, reloads, DOM
|
|
12
|
+
inspection, request capture — all yours. Pause only for what needs a human:
|
|
13
|
+
typing credentials, 2FA, captchas, confirming a genuinely ambiguous value.
|
|
14
|
+
|
|
15
|
+
**When you're stuck, read before you guess.** Every failure mode here has a
|
|
16
|
+
topic. Pull the matching one with `how_it_works({ topic })` and offer the
|
|
17
|
+
developer what it says — a site that won't load points at `interception`, a
|
|
18
|
+
failing proof at `prove`, a dead browser at `browser`, an expired draft at
|
|
19
|
+
`troubleshooting`. Inventing a cause wastes their time; the guide usually
|
|
20
|
+
already names it.
|
|
21
|
+
|
|
22
|
+
<!-- section: browser -->
|
|
23
|
+
## Choosing a browser
|
|
24
|
+
|
|
25
|
+
**Ask the developer which browser they want, then call `attach_browser` ONCE
|
|
26
|
+
with their chosen `mode`.** Never pick for them, and never assume last session's
|
|
27
|
+
choice still applies. You don't need a discovery call — the four options are
|
|
28
|
+
spelled out in the tool's own description (and summarized below). Calling with
|
|
29
|
+
no `mode` returns the same list as `needsChoice: true` + `options`, if you'd
|
|
30
|
+
rather relay it verbatim.
|
|
31
|
+
|
|
32
|
+
| mode | what it is | needs | live view |
|
|
33
|
+
| --- | --- | --- | --- |
|
|
34
|
+
| `dedicated` | Local Chrome the agent launches with its own profile (`~/.reclaim/chrome-profile`). Easiest local path. | nothing | `share_browser_view` on request |
|
|
35
|
+
| `attach` | A Chrome the developer is already running. | `port` (default 9222) | `share_browser_view` on request |
|
|
36
|
+
| `builder` | Quota-accounted remote Popcorn browser. Only way to browse from a **specific country**. | Builder auth, `orgId` | hosted `liveViewUrl`, returned by `attach_browser` |
|
|
37
|
+
| `custom` | Any CDP websocket the developer supplies. | `cdpUrl` | `share_browser_view` on request |
|
|
38
|
+
|
|
39
|
+
**Say the live-view column out loud when you offer the choice.** How the
|
|
40
|
+
developer watches the browser is part of picking one, and a local browser looks
|
|
41
|
+
like it can't be shared at all unless you tell them `share_browser_view`
|
|
42
|
+
exists.
|
|
43
|
+
|
|
44
|
+
**`attach` gotcha (Chrome 136+, May 2025):** the developer's Chrome must be
|
|
45
|
+
started with BOTH `--remote-debugging-port=9222` AND
|
|
46
|
+
`--user-data-dir=<non-default-path>`. The debug flag is silently ignored on the
|
|
47
|
+
default profile. If `attach` fails, say this verbatim and offer `dedicated`.
|
|
48
|
+
|
|
49
|
+
**`builder` specifics:**
|
|
50
|
+
|
|
51
|
+
- 401 → authenticate first (`authenticate_builder` in old-devtools mode, the
|
|
52
|
+
native `authenticate` flow in Builder mode), then retry.
|
|
53
|
+
- `orgId` may be omitted: it auto-resolves when the account has exactly one org,
|
|
54
|
+
otherwise comes back `needsChoice` with an `organizations` list — **ask the
|
|
55
|
+
developer which one**, don't guess or take the first.
|
|
56
|
+
- **Geolocation is optional.** Omitting `countryCode` returns `needsChoice` with
|
|
57
|
+
a `suggestedCountryCode` (the developer's own country, from their public IP)
|
|
58
|
+
and costs nothing — no browser is allocated. Offer the developer three things:
|
|
59
|
+
the suggested country, a country the site must be seen from, or skipping geo
|
|
60
|
+
entirely (`skipCountry: true`, the deployment's default egress). The country
|
|
61
|
+
is **fixed for the session's lifetime** and can't be changed after allocation.
|
|
62
|
+
When a site is geo-restricted or a recipe pins a `geoLocation`, recommend a
|
|
63
|
+
remote browser in that country rather than working around it locally. If the
|
|
64
|
+
deployment has no proxy configured the browser is still allocated, just
|
|
65
|
+
without the requested country.
|
|
66
|
+
- Costs one VERIFICATION-pool use under the org's normal plan restrictions.
|
|
67
|
+
- Return the FULL `liveViewUrl` to the developer **verbatim, on its own line** —
|
|
68
|
+
never abbreviated, elided, or wrapped in prose that truncates it.
|
|
69
|
+
- Close it when the work is done (`dispose_browser`) so the quota isn't wasted —
|
|
70
|
+
but only after the developer confirms, never unprompted.
|
|
71
|
+
|
|
72
|
+
**`custom` specifics:** the `cdpUrl` is a credential — never repeat, log, or
|
|
73
|
+
persist it. If a `liveViewUrl` comes back, share it full and verbatim.
|
|
74
|
+
|
|
75
|
+
### Sharing a local browser (`share_browser_view`)
|
|
76
|
+
|
|
77
|
+
A `dedicated`, `attach`, or `custom` browser runs on the developer's machine,
|
|
78
|
+
so only they can see it. `share_browser_view` puts the tab behind a public
|
|
79
|
+
HTTPS URL through a cloudflared quick tunnel — use it when the person who has
|
|
80
|
+
to act is not at that machine: a login or 2FA only they can complete, or a
|
|
81
|
+
colleague watching a capture.
|
|
82
|
+
|
|
83
|
+
- **`builder` mode already has one.** The Popcorn browser returns a hosted
|
|
84
|
+
`liveViewUrl` from `attach_browser`; share that instead. The tool refuses
|
|
85
|
+
here rather than opening a second tunnel.
|
|
86
|
+
- **The URL is a credential.** It carries an unguessable token and needs no
|
|
87
|
+
login, so whoever holds it can see the tab — and, by default, click and type
|
|
88
|
+
in it as the signed-in user. Give it to one person, and say plainly what it
|
|
89
|
+
grants.
|
|
90
|
+
- **Return it verbatim, on its own line**, exactly as with `liveViewUrl`.
|
|
91
|
+
- `interactive: false` streams a watch-only view. Prefer it when the developer
|
|
92
|
+
only needs to see progress.
|
|
93
|
+
- **Call `stop_browser_view` as soon as they're done.** The tunnel stays open
|
|
94
|
+
until you do. `dispose_browser` and `reset_session` also close it.
|
|
95
|
+
- **cloudflared is handled for you.** An existing install on PATH wins;
|
|
96
|
+
otherwise it's fetched once into `~/.reclaim/bin` on the first share, never
|
|
97
|
+
at install time. Say so when it happens — it's a ~37 MB download onto their
|
|
98
|
+
machine. `RECLAIM_CLOUDFLARED_PATH` points at their own copy instead.
|
|
99
|
+
|
|
100
|
+
**When a local attach errors or `waitForCdpReady` reports a dead port**, walk
|
|
101
|
+
these before retrying:
|
|
102
|
+
|
|
103
|
+
- **Chrome binary not found** (the error lists the paths it tried). Set
|
|
104
|
+
`RECLAIM_AGENT_CHROME_PATH` to the real binary — on Windows
|
|
105
|
+
`setx RECLAIM_AGENT_CHROME_PATH "C:\path\to\chrome.exe"` (`setx`, not `$env:`,
|
|
106
|
+
so future shells inherit it); on macOS/Linux `export` it. **Then fully restart
|
|
107
|
+
Claude Code** — MCP servers snapshot env vars at launch.
|
|
108
|
+
- **Pre-existing `chrome.exe` hijacks the launch (Windows).** A new launch with
|
|
109
|
+
`--remote-debugging-port` gets delegated to the running process group and the
|
|
110
|
+
port silently never opens. `taskkill /F /IM chrome.exe`, then retry.
|
|
111
|
+
- **Chrome 136+ default-profile rule** (above) — `dedicated` is always safe.
|
|
112
|
+
|
|
113
|
+
<!-- section: login-detect -->
|
|
114
|
+
**Check login state, wait if needed.** `eval_in_page(captureId, ...)` with a
|
|
115
|
+
site-specific check.
|
|
116
|
+
|
|
117
|
+
**Combine a positive signal (element present only when logged in) with a
|
|
118
|
+
negative one (login button absent), AND them together.** Single signals are too
|
|
119
|
+
prone to false positives — `<meta>` tags in particular exist on every page
|
|
120
|
+
regardless of login state; only their `.content` is populated, so
|
|
121
|
+
`!!document.querySelector('meta[name="user-login"]')` is true when logged out.
|
|
122
|
+
Never use existence-only checks on meta tags or other persistent-but-empty
|
|
123
|
+
elements.
|
|
124
|
+
|
|
125
|
+
- GitHub: `(document.querySelector('meta[name="user-login"]')?.content?.length || 0) > 0 && !document.querySelector('a.HeaderMenu-link[href="/login"]')`
|
|
126
|
+
- X / Twitter: `!!document.querySelector('[data-testid="AppTabBar_Profile_Link"]') && !document.querySelector('[data-testid="loginButton"]')`
|
|
127
|
+
- Generic: `<logged-in-only-element> && !<logged-out-only-element>`
|
|
128
|
+
|
|
129
|
+
Truthy → move on. Falsy → **navigate to the login URL yourself** so the
|
|
130
|
+
developer lands on the form (`https://<host>/login`, `/signin`,
|
|
131
|
+
`https://<host>/i/flow/login` for X). Tell them once: _"I've opened the login
|
|
132
|
+
page on `<site>` — please log in (2FA included). I'll detect when you're
|
|
133
|
+
done."_ Then `wait_for_page(captureId, expression="<same expression>",
|
|
134
|
+
timeoutMs=180000)`; it polls every second. **Never** say "tell me when you're
|
|
135
|
+
done" — wait with the tool.
|
|
136
|
+
|
|
137
|
+
**Self-check:** if your detector says "already logged in" on a freshly launched
|
|
138
|
+
dedicated profile, the detector is wrong — a new profile has an empty cookie
|
|
139
|
+
jar. Rewrite the expression.
|
|
140
|
+
|
|
141
|
+
<!-- section: identify-value -->
|
|
142
|
+
**Identify the value from the DOM.** `eval_in_page` to read what's displayed
|
|
143
|
+
(for example, the username from `meta[name="user-login"]`'s `content`).
|
|
144
|
+
Unambiguous → proceed silently. Only ask the developer when there are genuinely
|
|
145
|
+
multiple readings (the page shows both "John Doe" and "@jdoe" and you must pick
|
|
146
|
+
one).
|
|
147
|
+
|
|
148
|
+
<!-- section: find-request -->
|
|
149
|
+
**Find the request.** `find_requests_containing(captureId, value)`. For each top
|
|
150
|
+
candidate (≤5) call `analyze_request_constraints` and surface its `concerns` to
|
|
151
|
+
the developer **verbatim** — don't paraphrase or omit; they're the developer's
|
|
152
|
+
signal that a provider is worth pursuing. Skip candidates flagged
|
|
153
|
+
`signedRequest` or `botChallenge` unless nothing else exists; `csrfDependency`,
|
|
154
|
+
`shortLivedToken` and `graphql` are informational — surface and proceed.
|
|
155
|
+
|
|
156
|
+
**Strongly prefer authenticated, user-specific endpoints over public pages.** A
|
|
157
|
+
proof against `https://github.com/<username>` attests only "this URL returned
|
|
158
|
+
this value at this time" — anyone could produce it, so it says nothing about who
|
|
159
|
+
the prover is. Rank candidates that:
|
|
160
|
+
|
|
161
|
+
- carry `Cookie`, `Authorization`, or other secret headers;
|
|
162
|
+
- have auth-shaped paths (`/me`, `/user`, `/settings`, `/api/...`, a `viewer`
|
|
163
|
+
GraphQL query);
|
|
164
|
+
- return JSON keyed to the developer's identity (login/username/id next to the
|
|
165
|
+
value).
|
|
166
|
+
|
|
167
|
+
If every candidate is a public HTML page, tell the developer: _"The value
|
|
168
|
+
appears on a public page, so a proof against this endpoint won't bind to your
|
|
169
|
+
identity — anyone could prove the same fact. I can still build it if you only
|
|
170
|
+
want to attest the value, but an identity-bound proof needs an authenticated
|
|
171
|
+
request. Keep digging, or proceed anyway?"_ Then wait for their call.
|
|
172
|
+
|
|
173
|
+
<!-- section: oprf -->
|
|
174
|
+
**Decide whether to OPRF the value** (privacy from the attestor) before calling
|
|
175
|
+
`propose_provider`:
|
|
176
|
+
|
|
177
|
+
- **No hash** (default) — value travels in `paramValues` + `{{name}}`; the
|
|
178
|
+
attestor sees the cleartext substring. Right for non-sensitive values (public
|
|
179
|
+
IPs, public usernames, public repo names).
|
|
180
|
+
- **`oprf-raw`** — server-side OPRF. **The only variant that runs on Windows /
|
|
181
|
+
without external deps.** Weakest privacy of the three (the attestor still
|
|
182
|
+
participates) — say so honestly.
|
|
183
|
+
- **`oprf`** — client-side ZK OPRF using gnark. Strongest. Needs a gnark install
|
|
184
|
+
(Linux/macOS in practice). Don't suggest it on Windows.
|
|
185
|
+
- **`oprf-mpc`** — TEE-based MPC. Niche; only if the developer raises it.
|
|
186
|
+
|
|
187
|
+
**Skip the question** when the value is clearly non-sensitive, and default to
|
|
188
|
+
no hash. When it is or could be sensitive (email, real name, private id —
|
|
189
|
+
anything the developer wouldn't post publicly), put the four options to them.
|
|
190
|
+
|
|
191
|
+
**Hard limits:**
|
|
192
|
+
|
|
193
|
+
1. OPRF-hashed values cap at **62 bytes**. Longer → propose a tighter `target`
|
|
194
|
+
(for example, only the email local-part) or no hash.
|
|
195
|
+
2. **OPRF and the request URL are mutually exclusive for the same value.** Any
|
|
196
|
+
value templated into the request URL is signed into the attestor's context in
|
|
197
|
+
the clear regardless of hashing, so hashing it doesn't just fail to help — it
|
|
198
|
+
doesn't verify. If the target's value also appears in the captured URL, drop
|
|
199
|
+
`hash` or capture a request whose URL doesn't carry it.
|
|
200
|
+
3. Never pass `hash` for a free-form target (unicode / spaces / punctuation).
|
|
201
|
+
|
|
202
|
+
`propose_provider` throws a clean error on all three — retrying the same call
|
|
203
|
+
won't help.
|
|
48
204
|
|
|
49
205
|
<!-- section: rewrite-shape-block -->
|
|
50
|
-
**Do NOT rewrite the shape.** The synthesizer's output
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
-
|
|
55
|
-
-
|
|
206
|
+
**Do NOT rewrite the shape.** The synthesizer's output IS the canonical
|
|
207
|
+
Reclaim shape — `contains` template + paramValues + xPath/jsonPath redaction
|
|
208
|
+
(+ regex/hash). Specifically:
|
|
209
|
+
|
|
210
|
+
- Don't replace the `contains` template with a hand-written regex.
|
|
211
|
+
- Don't hand-write a named-capture regex unless `hash` was requested AND the
|
|
212
|
+
synthesizer didn't already emit one (it does, for any digit/slug/email
|
|
213
|
+
target).
|
|
214
|
+
- Don't remove `paramValues` — that's how the template resolves at proof
|
|
215
|
+
time.
|
|
216
|
+
- The redaction's xPath/jsonPath is what the prover *reveals*; the match's
|
|
217
|
+
template is what the attestor *verifies* post-substitution. Different
|
|
218
|
+
jobs — don't conflate them.
|
|
219
|
+
- Don't prefix the match template's param with `%CTX%` — that namespace is
|
|
220
|
+
for consumer-supplied URL/body params only. Match templates are extraction
|
|
221
|
+
params and stay bare.
|
|
222
|
+
|
|
223
|
+
Permitted edits: narrowing the `contains` template, a more distinctive
|
|
224
|
+
xPath class/id when the synthesizer's pick is too generic, fixing `name`,
|
|
225
|
+
adding/removing secret headers. Tempted to rewrite from scratch? Call
|
|
226
|
+
`propose_provider` again with different inputs instead.
|
|
227
|
+
|
|
228
|
+
<!-- section: secrets -->
|
|
229
|
+
**Marking a value secret.** Name ANY param with `SECRET` anywhere in the key
|
|
230
|
+
(case-insensitive) — a bare `{{SECRET_x}}` as much as a consumer-supplied
|
|
231
|
+
`{{%CTX%SECRET_apiKey}}` — and it routes to `secretParams.paramValues`
|
|
232
|
+
(`classifyParamName`/`splitSecretParamValues`), the same convention
|
|
233
|
+
`reclaim-inapp-sdk`/`reclaim-portal` use. **A secret param's value never appears
|
|
234
|
+
in the final proof.**
|
|
235
|
+
|
|
236
|
+
**Headers work by the OPPOSITE rule — an allowlist, not a name check.** Any
|
|
237
|
+
header not on a small fixed public list (`user-agent`, `accept`,
|
|
238
|
+
`accept-language`, `accept-encoding`, `sec-fetch-*`, `origin`,
|
|
239
|
+
`x-requested-with`, `sec-ch-ua*`) is secret by default, including
|
|
240
|
+
`Cookie`/`Authorization` and anything custom (`classifyHeader`/`splitSecrets`).
|
|
56
241
|
|
|
57
|
-
|
|
242
|
+
**The URL character budget.** A secret value CAN be used in the URL,
|
|
243
|
+
`geoLocation`, body, matches and redactions — write-redaction genuinely hides
|
|
244
|
+
it. But in the URL it only holds up to a small **TOTAL** budget summed across
|
|
245
|
+
every secret value in that URL (`secretUrlCharBudget`), not a per-value
|
|
246
|
+
allowance: **2 characters under `key-update`** (the default), **12 under `zk`**.
|
|
247
|
+
Over budget the value is effectively visible to the attestor no matter how it's
|
|
248
|
+
marked, and claim creation fails outright. Since the real value doesn't exist
|
|
249
|
+
until a verification session supplies one, this is checked at **verification**
|
|
250
|
+
time (`assertSecretParamValuesFitUrlBudget`), not publish time — except for a
|
|
251
|
+
captured secret *header* whose value also sits in the URL, which is already
|
|
252
|
+
known and so gets checked at draft time (`assertNoSecretUrlConflict`).
|
|
58
253
|
|
|
59
254
|
<!-- section: no-rename-param -->
|
|
60
|
-
**Do not rename the match-template param.** The synthesizer's `{{name}}` in
|
|
255
|
+
**Do not rename the match-template param.** The synthesizer's `{{name}}` in
|
|
256
|
+
`responseMatches[].value` is an _extraction_ template — it stays bare and never
|
|
257
|
+
takes the `%CTX%` prefix. Only URL/body placeholders whose values the consumer
|
|
258
|
+
supplies are context params.
|
|
61
259
|
|
|
62
260
|
<!-- section: prove-tail -->
|
|
63
|
-
**On any `verified: false`
|
|
64
|
-
|
|
65
|
-
-
|
|
261
|
+
**On any `verified: false` the result includes, inline:** `error` (the SDK's
|
|
262
|
+
thrown message verbatim) and `sentParams` (the exact `params` the SDK saw,
|
|
263
|
+
post-substitution — this is where you spot mutation surprises).
|
|
66
264
|
|
|
67
|
-
**
|
|
265
|
+
**The attestor is reliable. If it errors, the cause is the provider, the
|
|
266
|
+
network, or prover-side code — not "the attestor can't handle this site".**
|
|
267
|
+
Do not invent failure modes like "the HTML is too large", "TLS records are
|
|
268
|
+
chunked", "it matches against gzipped bytes" (it doesn't —
|
|
269
|
+
`propose_provider` always sets `Accept-Encoding: identity`), or "xPath
|
|
270
|
+
doesn't work on this DOM". They're wrong and they waste the developer's
|
|
271
|
+
time. Instead: (1) show `error` verbatim; (2) if `sentParams` differs from
|
|
272
|
+
what you expected, surface that; (3) only then hypothesize, tied to that
|
|
273
|
+
evidence.
|
|
68
274
|
|
|
69
|
-
**
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
275
|
+
**Deeper tracing (opt-in).** With `RECLAIM_AGENT_TRACE=1` in the MCP
|
|
276
|
+
server's env, `run_proof` also captures the SDK's full pino stream into
|
|
277
|
+
`result.attestorLogs` and dumps `~/.reclaim/proof-runs/<timestamp>.json`
|
|
278
|
+
(`runLogPath`). Off by default — suggest it for a recurring failure.
|
|
73
279
|
|
|
74
|
-
**Do not auto-retry with a different `attestorUrl`.** There is one live
|
|
280
|
+
**Do not auto-retry with a different `attestorUrl`.** There is one live
|
|
281
|
+
public attestor; backup hostnames (`attestor-2.*`, and so on) **do not
|
|
282
|
+
exist**. `attestorUrl` is only for a developer-supplied private/staging URL.
|
|
75
283
|
|
|
76
|
-
**
|
|
284
|
+
**"ZK circuit resources are missing"** (points at a `circuit.wasm` path) —
|
|
285
|
+
the npm postinstall didn't run (`--ignore-scripts`, or corp policy). Tell
|
|
286
|
+
the developer to run `npm run download:zk-files` from the project root and
|
|
287
|
+
retry `run_proof`. No re-capture, no server restart.
|
|
77
288
|
|
|
78
|
-
<!-- section:
|
|
79
|
-
|
|
289
|
+
<!-- section: never-do-common -->
|
|
290
|
+
- Never echo a raw `Authorization`, `Cookie`, or `X-*-Token` value back to the
|
|
291
|
+
developer or write one to a file. `list_requests`,
|
|
292
|
+
`find_requests_containing`, `propose_provider` and every draft/replay/proof
|
|
293
|
+
result are secret-free by construction. `get_request` is the one exception —
|
|
294
|
+
it returns the request's RAW headers so you can inspect them. Read them,
|
|
295
|
+
never repeat them.
|
|
296
|
+
- Never call `run_proof` before `replay_request` succeeds. Attestor calls are
|
|
297
|
+
expensive.
|
|
298
|
+
- Never pretend a proof succeeded when `verified: false`. Show the error and
|
|
299
|
+
iterate.
|
|
300
|
+
- Never write a literal value into `responseMatches.value` — that bakes the
|
|
301
|
+
original user into the provider. The synthesizer always emits `{{name}}` +
|
|
302
|
+
paramValues.
|
|
303
|
+
- **Never hand-craft `responseMatches`/`responseRedactions`.** Typing a regex by
|
|
304
|
+
hand, writing `(?<name>...)` by hand, or copying the captured value into the
|
|
305
|
+
match — all stop signs. And never accept "the xPath looks too narrow, let me
|
|
306
|
+
write a regex": the synthesizer already pairs the anchor with a
|
|
307
|
+
structure-anchored named-capture regex. If extraction looks wrong, re-run
|
|
308
|
+
`propose_provider` with a better request/target.
|
|
309
|
+
- Never rename or `%CTX%`-prefix the match-template param (see `params`).
|
|
80
310
|
|
|
81
|
-
|
|
311
|
+
<!-- section: credentials -->
|
|
312
|
+
## Signing credential for the proof
|
|
82
313
|
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
- **
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
<!-- section: credential-step2 -->
|
|
108
|
-
2. All fully local — no network, no server registration. The key is read live from `.env` on each `run_proof`, so no restart is needed after issuing/importing.
|
|
109
|
-
|
|
110
|
-
<!-- section: mcp-config-bullet -->
|
|
111
|
-
- **Multiple MCP config files exist on Windows.** `~/.claude.json` takes precedence over `~/.claude/mcp.json`. When advising config edits, point them at the right file or tell them to use `claude mcp list` / `claude mcp add --scope user` rather than editing JSON by hand.
|
|
314
|
+
`run_proof` needs an `ownerAddress` — the eth proof-owner key whose private half
|
|
315
|
+
signs the claim. It does **no** resolution itself. The private key is resolved
|
|
316
|
+
locally (`RECLAIM_PRIVATE_KEY` env → a `RECLAIM_PRIVATE_KEY=0x…` line in the
|
|
317
|
+
project `.env` → a file at `RECLAIM_PRIVATE_KEY_FILE`) and never enters the
|
|
318
|
+
conversation.
|
|
319
|
+
|
|
320
|
+
1. **`resolve_owner_key`** returns only the derived address.
|
|
321
|
+
- `{ ready: true, address }` → call `run_proof` with it. No prompts.
|
|
322
|
+
- `{ ready: false }` → ask the developer once: **their own key** →
|
|
323
|
+
`import_credentials({ privateKey: "0x…" })` (a `keyFile` path also works),
|
|
324
|
+
or **a fresh one** → `issue_credentials({})` (a fine default for test
|
|
325
|
+
proofs — owner identity doesn't matter). Either writes to the project
|
|
326
|
+
`.env` (mode 0600, added to `.gitignore`), never to `~/.reclaim`. Then
|
|
327
|
+
re-run `resolve_owner_key`.
|
|
328
|
+
2. Fully local — no network, no registration. The key is read live from `.env`
|
|
329
|
+
on every `run_proof`, so **no restart** is needed after issuing/importing,
|
|
330
|
+
and the existing draftId stays valid.
|
|
331
|
+
|
|
332
|
+
- **Advanced:** `RECLAIM_PRIVATE_KEY` in the MCP server env works too, but env
|
|
333
|
+
vars are snapshotted at Claude Code startup — that route needs a full restart,
|
|
334
|
+
which invalidates every draftId. Prefer the `.env` flow.
|
|
335
|
+
- **Multiple MCP config files exist on Windows.** `~/.claude.json` takes
|
|
336
|
+
precedence over `~/.claude/mcp.json`. Point developers at `claude mcp list` /
|
|
337
|
+
`claude mcp add --scope user` rather than hand-editing JSON.
|