@reclaimprotocol/agent 0.1.27 → 0.1.28
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 +5 -5
- package/lib/authoring/tools.js +2 -1
- package/lib/consts.d.ts +1 -1
- package/lib/consts.js +1 -1
- package/lib/live/tunnel.d.ts +4 -0
- package/lib/live/tunnel.js +21 -12
- package/lib/mcp/api/operations.gen.d.ts +8 -8
- package/lib/mcp/api/operations.gen.js +339 -581
- package/lib/mcp/api/tools.d.ts +1 -1
- package/lib/mcp/instructions.js +5 -3
- package/lib/mcp/remap_errors.js +9 -5
- package/lib/mcp/start.js +1 -1
- package/lib/mcp/tools/agent/attach.d.ts +2 -2
- package/lib/mcp/tools/agent/attach.js +4 -0
- package/lib/mcp/tools/agent/build.d.ts +1 -1
- package/lib/mcp/tools/agent/live-view-url.d.ts +2 -0
- package/lib/mcp/tools/agent/live-view-url.js +11 -0
- package/lib/mcp/tools/agent/live-view.d.ts +4 -2
- package/lib/mcp/tools/agent/live-view.js +42 -39
- package/lib/mcp/tools/agent/propose.js +10 -4
- package/lib/mcp/tools/agent/publish.d.ts +1 -1
- package/lib/mcp/tools/agent/publish.js +49 -26
- package/lib/mcp/tools/agent/shared.js +0 -9
- package/lib/mcp/tools/authenticate/authenticate.d.ts +1 -1
- package/lib/mcp/tools/authenticate/build.d.ts +1 -1
- package/lib/mcp/tools/authenticate/builder-access.d.ts +1 -1
- package/lib/mcp/tools/verification/result.js +12 -13
- package/lib/old/login-cdp.d.ts +17 -6
- package/lib/old/login-cdp.js +28 -11
- package/lib/old/to-register.d.ts +6 -0
- package/lib/old/to-register.js +35 -5
- package/lib/old/tools/authenticate.d.ts +5 -4
- package/lib/old/tools/authenticate.js +9 -5
- package/lib/old/tools/publish.d.ts +2 -2
- package/lib/old/tools/publish.js +47 -21
- package/lib/proof/attestor-auth.d.ts +13 -0
- package/lib/proof/attestor-auth.js +16 -0
- package/lib/proof/attestor.d.ts +7 -4
- package/lib/proof/attestor.js +7 -4
- package/lib/proof/index.d.ts +1 -0
- package/lib/proof/index.js +1 -0
- package/lib/proof/pod-prover.d.ts +2 -0
- package/lib/proof/pod-prover.js +5 -5
- package/lib/provider/injection-doc.d.ts +2 -0
- package/lib/provider/injection-doc.js +7 -1
- package/lib/provider/post-publish-browser.d.ts +12 -0
- package/lib/provider/post-publish-browser.js +40 -0
- package/lib/provider/to-version.d.ts +19 -7
- package/lib/provider/to-version.js +67 -25
- package/lib/tools/registry.d.ts +1 -1
- package/lib/tools/registry.js +7 -4
- package/package.json +2 -2
- package/skill/SKILL.builder.md +118 -13
- package/skill/SKILL.common.md +30 -19
- package/skill/SKILL.old.md +22 -12
- package/skill/builder-mode.md +19 -9
- package/skill/community-contributions.md +108 -0
- package/skill/hash-validation.md +8 -5
- package/skill/interception.md +35 -22
- package/skill/old-mode.md +11 -5
- package/skill/user-script.md +41 -10
- package/src/authoring/tools.ts +2 -1
- package/src/consts.ts +1 -2
- package/src/live/tunnel.ts +28 -14
- package/src/mcp/api/operations.gen.ts +341 -585
- package/src/mcp/api/tools.ts +1 -1
- package/src/mcp/instructions.ts +5 -3
- package/src/mcp/remap_errors.ts +9 -7
- package/src/mcp/start.ts +1 -1
- package/src/mcp/tools/agent/attach.ts +6 -2
- package/src/mcp/tools/agent/build.ts +1 -1
- package/src/mcp/tools/agent/live-view-url.ts +13 -0
- package/src/mcp/tools/agent/live-view.ts +44 -39
- package/src/mcp/tools/agent/propose.ts +10 -4
- package/src/mcp/tools/agent/publish.ts +63 -28
- package/src/mcp/tools/agent/shared.ts +5 -0
- package/src/mcp/tools/authenticate/authenticate.ts +1 -1
- package/src/mcp/tools/authenticate/build.ts +1 -1
- package/src/mcp/tools/authenticate/builder-access.ts +1 -1
- package/src/mcp/tools/verification/result.ts +11 -13
- package/src/old/login-cdp.ts +38 -11
- package/src/old/to-register.ts +56 -7
- package/src/old/tools/authenticate.ts +9 -5
- package/src/old/tools/publish.ts +64 -22
- package/src/proof/attestor-auth.ts +33 -0
- package/src/proof/attestor.ts +14 -8
- package/src/proof/index.ts +4 -0
- package/src/proof/pod-prover.ts +7 -5
- package/src/provider/injection-doc.ts +9 -1
- package/src/provider/post-publish-browser.ts +56 -0
- package/src/provider/to-version.ts +104 -27
- package/src/tools/registry.ts +12 -9
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { ReclaimClient } from '@reclaimprotocol/client';
|
|
1
|
+
import type { ReclaimClient } from '@reclaimprotocol/client/api';
|
|
2
2
|
import type { CreateProviderVersionRequest, ProviderVersionRef } from '@reclaimprotocol/client/openapi';
|
|
3
3
|
import type { DraftResponseMatch, HttpMethod, OldUrlType, ReclaimProvider, ResponseRedaction, WebCredentials } from './schema.ts';
|
|
4
4
|
/** One entry of the version's `requests[]` (a `RequestSelection`), derived
|
|
@@ -50,6 +50,9 @@ export interface AllowedJsRequest extends DraftRequest {
|
|
|
50
50
|
export interface CreateVersionOptions {
|
|
51
51
|
notes?: string;
|
|
52
52
|
version?: ProviderVersionRef;
|
|
53
|
+
/** Complete context schema for this new immutable version. When omitted,
|
|
54
|
+
* it is derived from `{{context.<name>}}` placeholders. */
|
|
55
|
+
requiredContext?: CreateProviderVersionRequest['requiredContext'];
|
|
53
56
|
/** JS injected before every page load (`webSettings.jsUserScripts`). */
|
|
54
57
|
jsUserScripts?: string;
|
|
55
58
|
/** Requests the injection script may fire, as `RequestSelectionTemplate`s. */
|
|
@@ -74,7 +77,8 @@ export interface CreateVersionOptions {
|
|
|
74
77
|
* injected requests and the injection script) — mechanical, so a published
|
|
75
78
|
* recipe can never reference a context value the Builder doesn't require.
|
|
76
79
|
* Returns undefined when nothing references context (the field is optional).
|
|
77
|
-
* Authors can
|
|
80
|
+
* Authors can pass a complete schema when creating the new version to refine
|
|
81
|
+
* types or descriptions. Never edit a version in place after creation.
|
|
78
82
|
*
|
|
79
83
|
* A SECRET-named context param (see `classifyParamName`) is NOT rejected here
|
|
80
84
|
* even when it's templated into the URL/geoLocation — write-redaction can
|
|
@@ -102,10 +106,18 @@ export declare function deriveRequiredContext(providers: ReclaimProvider[], inje
|
|
|
102
106
|
export declare function providerToRequestSelection(provider: DraftRequest): RequestSelection;
|
|
103
107
|
export declare function providerToCreateVersionRequest(providers: ReclaimProvider[], initialUrl: string, opts?: CreateVersionOptions): CreateProviderVersionRequest;
|
|
104
108
|
/**
|
|
105
|
-
* Fetch
|
|
106
|
-
*
|
|
107
|
-
*
|
|
108
|
-
* resetting to 1.0.0
|
|
109
|
+
* Fetch every current version and resolve a strictly newer immutable version.
|
|
110
|
+
* The default bump is patch. Callers can request major/minor/patch or provide
|
|
111
|
+
* an exact version; an exact version must be higher than every existing one.
|
|
112
|
+
* A failed lookup propagates — silently resetting to 1.0.0 could collide with
|
|
113
|
+
* an existing version.
|
|
109
114
|
*/
|
|
110
|
-
export declare function nextVersion(client: ReclaimClient, providerId: string): Promise<ProviderVersionRef>;
|
|
115
|
+
export declare function nextVersion(client: ReclaimClient, providerId: string, bump?: VersionBump): Promise<ProviderVersionRef>;
|
|
116
|
+
export type VersionBump = 'major' | 'minor' | 'patch';
|
|
117
|
+
/** Resolve either an explicit version or a requested semantic bump. */
|
|
118
|
+
export declare function resolveNewVersion(client: ReclaimClient, providerId: string, input?: {
|
|
119
|
+
version?: ProviderVersionRef;
|
|
120
|
+
bump?: VersionBump;
|
|
121
|
+
}): Promise<ProviderVersionRef>;
|
|
122
|
+
export declare function bumpVersion(versions: ProviderVersionRef[], bump?: VersionBump): ProviderVersionRef;
|
|
111
123
|
export {};
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { badRequest, conflict } from '@hapi/boom';
|
|
2
|
+
import assert from 'node:assert';
|
|
1
3
|
import { DYNAMIC_GEO, normalizeGeoLocation } from "./schema.js";
|
|
2
4
|
/**
|
|
3
5
|
* Translate a drafted ReclaimProvider into the body for
|
|
@@ -21,7 +23,8 @@ const CONTEXT_PARAM_RE = /\{\{context\.([A-Za-z_][A-Za-z0-9_]*)\}\}/g;
|
|
|
21
23
|
* injected requests and the injection script) — mechanical, so a published
|
|
22
24
|
* recipe can never reference a context value the Builder doesn't require.
|
|
23
25
|
* Returns undefined when nothing references context (the field is optional).
|
|
24
|
-
* Authors can
|
|
26
|
+
* Authors can pass a complete schema when creating the new version to refine
|
|
27
|
+
* types or descriptions. Never edit a version in place after creation.
|
|
25
28
|
*
|
|
26
29
|
* A SECRET-named context param (see `classifyParamName`) is NOT rejected here
|
|
27
30
|
* even when it's templated into the URL/geoLocation — write-redaction can
|
|
@@ -101,11 +104,8 @@ function pickGeoLocation(providers) {
|
|
|
101
104
|
return DYNAMIC_GEO;
|
|
102
105
|
}
|
|
103
106
|
export function providerToCreateVersionRequest(providers, initialUrl, opts = {}) {
|
|
104
|
-
const { notes, version, jsUserScripts, allowedJsRequests, interceptorOptions, } = opts;
|
|
105
|
-
const requiredContext = deriveRequiredContext(providers, {
|
|
106
|
-
allowedJsRequests,
|
|
107
|
-
jsUserScripts,
|
|
108
|
-
});
|
|
107
|
+
const { notes, version, requiredContext: requestedContext, jsUserScripts, allowedJsRequests, interceptorOptions, } = opts;
|
|
108
|
+
const requiredContext = requestedContext ?? deriveRequiredContext(providers, { allowedJsRequests, jsUserScripts });
|
|
109
109
|
// Each injected request carries its template flags (default true/true per
|
|
110
110
|
// RequestSelectionTemplate) on top of the shared request shape.
|
|
111
111
|
const allowedJs = (allowedJsRequests ?? []).map((req) => ({
|
|
@@ -166,28 +166,70 @@ export function providerToCreateVersionRequest(providers, initialUrl, opts = {})
|
|
|
166
166
|
};
|
|
167
167
|
}
|
|
168
168
|
/**
|
|
169
|
-
* Fetch
|
|
170
|
-
*
|
|
171
|
-
*
|
|
172
|
-
* resetting to 1.0.0
|
|
169
|
+
* Fetch every current version and resolve a strictly newer immutable version.
|
|
170
|
+
* The default bump is patch. Callers can request major/minor/patch or provide
|
|
171
|
+
* an exact version; an exact version must be higher than every existing one.
|
|
172
|
+
* A failed lookup propagates — silently resetting to 1.0.0 could collide with
|
|
173
|
+
* an existing version.
|
|
173
174
|
*/
|
|
174
|
-
export async function nextVersion(client, providerId) {
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
175
|
+
export async function nextVersion(client, providerId, bump = 'patch') {
|
|
176
|
+
return bumpVersion(await listVersions(client, providerId), bump);
|
|
177
|
+
}
|
|
178
|
+
/** Resolve either an explicit version or a requested semantic bump. */
|
|
179
|
+
export async function resolveNewVersion(client, providerId, input = {}) {
|
|
180
|
+
assert(!(input.version && input.bump), badRequest('Pass either `version` or `bump`, not both.'));
|
|
181
|
+
const versions = await listVersions(client, providerId);
|
|
182
|
+
if (!input.version) {
|
|
183
|
+
return bumpVersion(versions, input.bump ?? 'patch');
|
|
184
|
+
}
|
|
185
|
+
const highest = highestVersion(versions);
|
|
186
|
+
assert(!highest || compareVersions(input.version, highest) > 0, conflict(`Version ${formatVersion(input.version)} must be higher than the ` +
|
|
187
|
+
`current highest version ${highest ? formatVersion(highest) : 'none'}.`));
|
|
188
|
+
return input.version;
|
|
189
|
+
}
|
|
190
|
+
export function bumpVersion(versions, bump = 'patch') {
|
|
191
|
+
const highest = highestVersion(versions);
|
|
192
|
+
if (!highest) {
|
|
180
193
|
return { major: 1, minor: 0, patch: 0 };
|
|
181
194
|
}
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
195
|
+
switch (bump) {
|
|
196
|
+
case 'major':
|
|
197
|
+
return { major: highest.major + 1, minor: 0, patch: 0 };
|
|
198
|
+
case 'minor':
|
|
199
|
+
return { major: highest.major, minor: highest.minor + 1, patch: 0 };
|
|
200
|
+
case 'patch':
|
|
201
|
+
return {
|
|
202
|
+
major: highest.major,
|
|
203
|
+
minor: highest.minor,
|
|
204
|
+
patch: highest.patch + 1,
|
|
205
|
+
};
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
async function listVersions(client, providerId) {
|
|
209
|
+
const versions = [];
|
|
210
|
+
let cursor;
|
|
211
|
+
do {
|
|
212
|
+
const { data } = await client.call('ListProviderVersions', {
|
|
213
|
+
params: { providerId },
|
|
214
|
+
query: { cursor },
|
|
215
|
+
});
|
|
216
|
+
versions.push(...data.items.map((item) => item.version));
|
|
217
|
+
cursor = data.nextPageCursor;
|
|
218
|
+
} while (cursor);
|
|
219
|
+
return versions;
|
|
220
|
+
}
|
|
221
|
+
function highestVersion(versions) {
|
|
222
|
+
let highest;
|
|
223
|
+
for (const version of versions) {
|
|
224
|
+
if (!highest || compareVersions(version, highest) > 0) {
|
|
225
|
+
highest = version;
|
|
190
226
|
}
|
|
191
227
|
}
|
|
192
|
-
return
|
|
228
|
+
return highest;
|
|
229
|
+
}
|
|
230
|
+
function compareVersions(a, b) {
|
|
231
|
+
return a.major - b.major || a.minor - b.minor || a.patch - b.patch;
|
|
232
|
+
}
|
|
233
|
+
function formatVersion(version) {
|
|
234
|
+
return `${version.major}.${version.minor}.${version.patch}`;
|
|
193
235
|
}
|
package/lib/tools/registry.d.ts
CHANGED
|
@@ -38,6 +38,6 @@ export declare function errorAsToolText(err: unknown): string;
|
|
|
38
38
|
*/
|
|
39
39
|
export declare function extendTool(tool: RegisteredTool, extra: {
|
|
40
40
|
description?: string;
|
|
41
|
-
properties?:
|
|
41
|
+
properties?: JSONSchema.ObjectSchema['properties'];
|
|
42
42
|
required?: string[];
|
|
43
43
|
}): RegisteredTool;
|
package/lib/tools/registry.js
CHANGED
|
@@ -12,6 +12,7 @@
|
|
|
12
12
|
* re-exports this primitive for the MCP-side tool files.
|
|
13
13
|
*/
|
|
14
14
|
import { isBoom } from '@hapi/boom';
|
|
15
|
+
import { isProblemDetails, isProblemError } from '@reclaimprotocol/client/api';
|
|
15
16
|
import { toZod } from "../mcp/api/to-zod.js";
|
|
16
17
|
/**
|
|
17
18
|
* Define a tool with a single inputSchema (JSON Schema) as the source of truth.
|
|
@@ -35,8 +36,12 @@ export function defineTool(definition, handler) {
|
|
|
35
36
|
* else falls back to the plain message.
|
|
36
37
|
*/
|
|
37
38
|
export function errorAsToolText(err) {
|
|
39
|
+
if (isProblemError(err)) {
|
|
40
|
+
const detail = err.data.detail;
|
|
41
|
+
return detail ? `${err.message}: ${detail}` : err.message;
|
|
42
|
+
}
|
|
38
43
|
if (isBoom(err) && err.output?.statusCode) {
|
|
39
|
-
const detail = err.data
|
|
44
|
+
const detail = isProblemDetails(err.data) ? err.data.detail : undefined;
|
|
40
45
|
return detail ? `${err.message}: ${detail}` : err.message;
|
|
41
46
|
}
|
|
42
47
|
return err instanceof Error ? err.message : String(err);
|
|
@@ -56,9 +61,7 @@ export function extendTool(tool, extra) {
|
|
|
56
61
|
...(base.inputSchema.required ?? []),
|
|
57
62
|
...(extra.required ?? []),
|
|
58
63
|
];
|
|
59
|
-
//
|
|
60
|
-
// which don't structurally match ObjectSchema's strict per-property schema
|
|
61
|
-
// type — assert the merged shape.
|
|
64
|
+
// Keep the merged schema checked against Zod's JSON Schema representation.
|
|
62
65
|
const inputSchema = {
|
|
63
66
|
...base.inputSchema,
|
|
64
67
|
properties: {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@reclaimprotocol/agent",
|
|
3
3
|
"title": "Reclaim Agent",
|
|
4
|
-
"version": "0.1.
|
|
4
|
+
"version": "0.1.28",
|
|
5
5
|
"description": "Reclaim Protocol tools for AI coding agents. Reclaim lets your app verify data points about its users, such as a follower count, an account balance, or an order history. This package is the Reclaim MCP server: it gives your agent the tools to find, build, test, and fix data verification providers by capturing and replaying browser requests, drafting provider recipes, running zkTLS proofs, and publishing provider versions.",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"main": "./lib/index.js",
|
|
@@ -83,7 +83,7 @@
|
|
|
83
83
|
"@modelcontextprotocol/sdk": "^1.29.0",
|
|
84
84
|
"@noble/hashes": "^2.2.0",
|
|
85
85
|
"@reclaimprotocol/attestor-core": "^5.0.8",
|
|
86
|
-
"@reclaimprotocol/client": "0.1.
|
|
86
|
+
"@reclaimprotocol/client": "0.1.28",
|
|
87
87
|
"@reclaimprotocol/tls": "^0.1.3",
|
|
88
88
|
"chrome-remote-interface": "^0.33.0",
|
|
89
89
|
"domhandler": "^6.0.0",
|
package/skill/SKILL.builder.md
CHANGED
|
@@ -1,6 +1,20 @@
|
|
|
1
1
|
<!-- topic: overview -->
|
|
2
2
|
# Authoring a Reclaim provider (Builder backend)
|
|
3
3
|
|
|
4
|
+
For claimant-facing Builder-client integration, result verification, or optional
|
|
5
|
+
TEE binding, read topic `integration`. Keep URLs without `api=2` on the
|
|
6
|
+
existing legacy path.
|
|
7
|
+
|
|
8
|
+
<!-- topic: contributions -->
|
|
9
|
+
## Public provider contributions
|
|
10
|
+
|
|
11
|
+
For an asynchronous volunteer contribution, read
|
|
12
|
+
[community-contributions.md](community-contributions.md) before using Builder.
|
|
13
|
+
It defines the history-first workflow, approved-field allowlist, fixed-US
|
|
14
|
+
egress, privacy boundaries, generalized recipe checks, and candidate review.
|
|
15
|
+
Never modify a published version in place; create a candidate and submit it
|
|
16
|
+
for review.
|
|
17
|
+
|
|
4
18
|
<!-- common: terminology -->
|
|
5
19
|
|
|
6
20
|
## The loop, at a glance
|
|
@@ -108,7 +122,7 @@ integration. Prefer Builder for new integrations.
|
|
|
108
122
|
under that `draftId`; the secrets never enter the transcript, so you carry the
|
|
109
123
|
`draftId`, not the provider JSON, into replay/prove.
|
|
110
124
|
|
|
111
|
-
**Extracting
|
|
125
|
+
**Extracting more than one value?** Prefer a **single request whose response
|
|
112
126
|
contains all of them** and draft them together — one `responseMatches` +
|
|
113
127
|
`responseRedactions` pair per value. Find that request by running
|
|
114
128
|
`find_requests_containing` for each value and picking a `requestId` common to
|
|
@@ -171,10 +185,10 @@ changes.
|
|
|
171
185
|
## Parameters: extracted, consumer-supplied, and secret
|
|
172
186
|
|
|
173
187
|
**Parameterize user-specific URL segments** so the provider isn't pinned to one
|
|
174
|
-
user. The matcher already puts the *captured* value in `paramValues`. If the
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
`context`, not from extraction.
|
|
188
|
+
user. The matcher already puts the *captured* value in `paramValues`. If the
|
|
189
|
+
consumer knows a URL value before verification and the proof must be pinned to
|
|
190
|
+
it, make it a **consumer-supplied context param**. At verification time, its
|
|
191
|
+
value comes from the consumer's session `context`, not from extraction.
|
|
178
192
|
|
|
179
193
|
Replace them with `{{context.<name>}}`, for example
|
|
180
194
|
`https://github.com/Scratch-net/repos` →
|
|
@@ -187,6 +201,12 @@ placeholder exactly, dot included).
|
|
|
187
201
|
- No user-identifying segments (for example, `/settings/profile`) → leave it
|
|
188
202
|
alone.
|
|
189
203
|
|
|
204
|
+
Use a bare `{{name}}` only when the value can be observed in the same live
|
|
205
|
+
request. A multi-request recipe does not bind a value extracted from one
|
|
206
|
+
response to a later request. If proving that relationship is required, use a
|
|
207
|
+
request that exposes both values, require the value as consumer context, or
|
|
208
|
+
stop and explain that the current recipe format cannot prove the dependency.
|
|
209
|
+
|
|
190
210
|
Declare them in the version's `requiredContext` at publish time (topic
|
|
191
211
|
`publish`).
|
|
192
212
|
|
|
@@ -218,6 +238,14 @@ extracted-from-traffic). The attestor itself only looks up `{{whatever}}` in
|
|
|
218
238
|
proof should be **pinned** to it; bare when the value should come from
|
|
219
239
|
whatever the logged-in user's traffic shows.
|
|
220
240
|
|
|
241
|
+
Builder-mode Verification Clients include one migration fallback for old
|
|
242
|
+
DevTools providers: when a recipe uses bare `{{name}}` instead of
|
|
243
|
+
`{{context.name}}`, the client seeds a missing `parameters.name` from scalar
|
|
244
|
+
session `context.name`. An explicit parameter wins. Do not rely on this
|
|
245
|
+
fallback for new providers; keep using `{{context.<name>}}` so
|
|
246
|
+
`requiredContext` is derived and validated before the session starts.
|
|
247
|
+
Builder-owned `reclaimSessionId` and `attestationNonce` remain context-only.
|
|
248
|
+
|
|
221
249
|
`propose_provider` returns `verificationWarnings` for calls only an author can
|
|
222
250
|
make (currently: a captured value baked literally into the URL, needing the
|
|
223
251
|
`{{context.<name>}}`-vs-bare-vs-recapture decision). Advisory, never blocking —
|
|
@@ -284,13 +312,18 @@ duplicates in "My providers".
|
|
|
284
312
|
**2. Persist the version.**
|
|
285
313
|
`create_provider_version_from_capture(providerId, provider, initialUrl, notes)`
|
|
286
314
|
— `notes` such as `"Initial draft from browser capture"`. It lands on the
|
|
287
|
-
`draft` branch.
|
|
315
|
+
`draft` branch. This always creates a new immutable semantic version. Omit
|
|
316
|
+
`version` and `bump` for the default patch bump; set `bump` to `major`, `minor`,
|
|
317
|
+
or `patch`, or pass the exact higher `version` requested by the user. Never edit
|
|
318
|
+
or archive an existing version to apply provider changes.
|
|
288
319
|
|
|
289
320
|
- **Multi-request provider** (landing call + details call, a
|
|
290
321
|
CSRF-preflight/refresh-token chain, …): run capture→draft→replay→prove ONCE
|
|
291
322
|
PER REQUEST, then pass `providers: [draftA, draftB, …]` instead of the single
|
|
292
323
|
`provider`. Each becomes its own request on the version and **all** are
|
|
293
|
-
required at verification time.
|
|
324
|
+
required at verification time. This proves that every request matched; it
|
|
325
|
+
does not automatically bind a value extracted from one response to another
|
|
326
|
+
request.
|
|
294
327
|
- **Login/navigation-gated data:** supply `jsUserScripts` (plain page JS that
|
|
295
328
|
drives login/waits/navigation so the target request fires), or
|
|
296
329
|
`allowedJsRequests` (extra requests the script may fire, for example,
|
|
@@ -303,8 +336,9 @@ url/body for `{{context.<name>}}` placeholders and emits a JSON Schema requiring
|
|
|
303
336
|
each — the Builder then validates every session-create's `context` against it (a
|
|
304
337
|
clear 400 instead of a late attestor failure). Extra context fields are always
|
|
305
338
|
allowed; it's optional when the recipe has no `{{context.*}}` placeholders.
|
|
306
|
-
Derived properties are plain strings
|
|
307
|
-
|
|
339
|
+
Derived properties are plain strings. To refine types or add descriptions,
|
|
340
|
+
pass the complete `requiredContext` in the same
|
|
341
|
+
`create_provider_version_from_capture` call:
|
|
308
342
|
|
|
309
343
|
```json
|
|
310
344
|
{ "requiredContext": { "type": "object", "required": ["username"], "properties": { "username": { "type": "string", "description": "GitHub username to verify" } } } }
|
|
@@ -320,10 +354,15 @@ context params — for example, `context: { "username": "…" }` in
|
|
|
320
354
|
version verbatim — a session missing those fields gets a 400, and verification
|
|
321
355
|
matches the supplied values exactly.
|
|
322
356
|
|
|
323
|
-
**6.
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
`
|
|
357
|
+
**6. If the response includes `_notes` for an attached browser, follow them for
|
|
358
|
+
an optional authenticated-state retest.** Only when its profile is still signed
|
|
359
|
+
in, navigate to `initialUrl`; if a user script is present, run
|
|
360
|
+
`test_user_script` with that script and `initialUrl`. When the existing capture
|
|
361
|
+
has enough data, replay and prove every required request. Do not clear
|
|
362
|
+
cookies or force another sign-in. Treat an unavailable or inconclusive retest as
|
|
363
|
+
advisory and do not block publication. Ask whether the developer wants to keep
|
|
364
|
+
the session for further edits; call `dispose_browser` only after they confirm.
|
|
365
|
+
A `builder` session is quota-accounted, so don't leave it idle.
|
|
327
366
|
|
|
328
367
|
<!-- topic: discover -->
|
|
329
368
|
## Discover and resolve providers for the developer's app
|
|
@@ -344,6 +383,12 @@ use, run the same flow the Discover UI does:
|
|
|
344
383
|
`merge.mergeable` only signals the set *could* collapse into one provider — you
|
|
345
384
|
still pass them all; the Builder runs them in one session.
|
|
346
385
|
|
|
386
|
+
For client integration, result verification, or TEE binding, read topic
|
|
387
|
+
`integration`.
|
|
388
|
+
|
|
389
|
+
<!-- topic: integration -->
|
|
390
|
+
## Integrate a Builder Verification Client
|
|
391
|
+
|
|
347
392
|
**Verification sessions.** Consumers authenticate with a per-org token
|
|
348
393
|
(`rorg_…`, issued in the dashboard via `POST /orgs/{orgId}/token`) and create
|
|
349
394
|
sessions via `POST /verifications/sessions`. Results are ECIES-encrypted
|
|
@@ -353,6 +398,66 @@ callback subscriptions; plaintext otherwise. Troubleshoot with
|
|
|
353
398
|
`get_verification_session`, `list_verification_events`,
|
|
354
399
|
`list_org_verification_sessions`.
|
|
355
400
|
|
|
401
|
+
For a claimant-facing Builder-mode integration, share the returned
|
|
402
|
+
`verificationUrl` (it contains the session id and `api=2`). Append client-owned
|
|
403
|
+
redirect parameters only after session creation. Append `diag=1` only for a
|
|
404
|
+
deliberate troubleshooting launch; it can enable logs containing personal data.
|
|
405
|
+
The registered client resolves its own `x-reclaim-vc-id`; do not invent a
|
|
406
|
+
client token or put callback/redirect fields in the session request. Consumers
|
|
407
|
+
should verify terminal deliveries with `verifyResultFull` and the session's
|
|
408
|
+
trusted issuer.
|
|
409
|
+
|
|
410
|
+
An optional `themeId` on session creation selects an active theme owned by the
|
|
411
|
+
organization. Omit it to snapshot the organization's most recently updated
|
|
412
|
+
active theme. A theme's localized `consent` block makes supported clients show
|
|
413
|
+
consent before the first provider; no block means no consent screen. Theme and
|
|
414
|
+
branding data never affect proof trust.
|
|
415
|
+
|
|
416
|
+
**Browser extension.** For the exact `api=2` URL, use
|
|
417
|
+
`reclaimExtensionSDK.fromVerificationUrl` or `initBuilder` with the session's
|
|
418
|
+
`verificationClientId`, then call `startVerification()`. The extension
|
|
419
|
+
generates and persists `claimantClientId` when omitted, resolves recipes from
|
|
420
|
+
bootstrap, runs them sequentially, and sends `x-reclaim-vc-id` to every bridge
|
|
421
|
+
route, including session-bound `attestor-auth`. It forwards raw legacy proofs
|
|
422
|
+
inside the Builder result and does not perform client-side `allowedJsRequests`
|
|
423
|
+
validation. Builder recipes support dotted `{{context.key}}` templates; the
|
|
424
|
+
extension forwards matching session context to claim creation. Builder mode
|
|
425
|
+
skips legacy offscreen session-status calls and uses the Builder bridge for
|
|
426
|
+
session lifecycle updates. It emits `verification_result_submitting` and
|
|
427
|
+
`verification_result_submission_failed` when applicable. It does not send
|
|
428
|
+
terminal success or error as a separate best-effort event; the results request
|
|
429
|
+
makes the bridge record the matching terminal event strongly before signing
|
|
430
|
+
and storing result deliveries. Missing or unknown `api` values stay on the
|
|
431
|
+
existing `init` / `fromJsonString` / direct-callback path.
|
|
432
|
+
|
|
433
|
+
**Builder client bridge.** In `api=2` mode, the registered client UUID is sent
|
|
434
|
+
in `x-reclaim-vc-id` to these routes:
|
|
435
|
+
|
|
436
|
+
| Route | Purpose |
|
|
437
|
+
| --- | --- |
|
|
438
|
+
| `GET /api/sdk/builder/v2/sessions/{sessionId}/bootstrap` | Load the session and resolved recipe. |
|
|
439
|
+
| `PATCH /api/sdk/builder/v2/sessions/{sessionId}/claimant` | Update claimant diagnostics. |
|
|
440
|
+
| `POST /api/sdk/builder/v2/sessions/{sessionId}/events` | Report canonical lower-snake-case events. |
|
|
441
|
+
| `POST /api/sdk/builder/v2/sessions/{sessionId}/attestor-auth` | Request session-bound attestor authentication. Do not send a legacy app secret or provider signature. |
|
|
442
|
+
| `POST /api/sdk/builder/v2/sessions/{sessionId}/results` | Submit exact legacy proof objects; the bridge records the matching terminal event strongly before signing and storing result deliveries. |
|
|
443
|
+
|
|
444
|
+
The bridge preserves each legacy proof and signs only the outer Verification
|
|
445
|
+
Client JWS. `verifyResultFull` validates the issuer, JWS, session, audience,
|
|
446
|
+
and expiry, then passes each exact proof to `verifyProof`. Keep TEE settings in
|
|
447
|
+
the legacy verifier configuration; do not add verification hooks. Treat outer
|
|
448
|
+
`extracted_parameters` and `content_hash` as diagnostics, not trust inputs.
|
|
449
|
+
|
|
450
|
+
**Optional TEE binding.** A consumer can pass
|
|
451
|
+
`teeAttestation: { appSecret }` to `sessions.create`. The SDK derives the
|
|
452
|
+
hash nonce locally with the pinned `@reclaimprotocol/js-sdk` formula, signs
|
|
453
|
+
the binding payload with EIP-191, and registers it with
|
|
454
|
+
`PUT /verifications/sessions/{sessionId}/tee-nonce` using the organization
|
|
455
|
+
token. The private key stays in the consumer process. Bind before proof
|
|
456
|
+
generation. The binding is idempotent for the same values and immutable
|
|
457
|
+
afterward; this helper registers only `RECLAIM_TEE_NONCE_V1` (`v3`). Legacy
|
|
458
|
+
`verifyProof` accepts the older signature nonce, but this helper does not
|
|
459
|
+
generate or register it.
|
|
460
|
+
|
|
356
461
|
<!-- topic: never-do -->
|
|
357
462
|
## Things to never do
|
|
358
463
|
|
package/skill/SKILL.common.md
CHANGED
|
@@ -22,11 +22,6 @@ already names it.
|
|
|
22
22
|
<!-- section: browser -->
|
|
23
23
|
## Choosing a browser
|
|
24
24
|
|
|
25
|
-
- Choosing one — ask who signs in, then offer the matching pair
|
|
26
|
-
- Sharing the container's live view with someone remote
|
|
27
|
-
- What happens when the session ends
|
|
28
|
-
- When the container will not start
|
|
29
|
-
|
|
30
25
|
**Ask one question first, before you list anything:**
|
|
31
26
|
|
|
32
27
|
> Will you be doing this yourself, or does someone else need to sign in — the
|
|
@@ -51,7 +46,7 @@ are the only modes that produce a link another person can open:
|
|
|
51
46
|
|
|
52
47
|
| mode | what it is | needs | costs |
|
|
53
48
|
| --- | --- | --- | --- |
|
|
54
|
-
| `container` | The Reclaim runtime in Docker, on their machine. `share_browser_view` turns its viewer into a public link. | Docker running | free — their Docker
|
|
49
|
+
| `container` | The Reclaim runtime in Docker, on their machine. `share_browser_view` turns its viewer into a public link. | Docker running | free — their Docker and bandwidth |
|
|
55
50
|
| `builder` | A browser in Reclaim's cloud. The link comes back immediately, with nothing to install. Also the only way to browse from a **specific country**. | Builder auth, `orgId` | **chargeable** — billed against the org's quota |
|
|
56
51
|
|
|
57
52
|
**Say which one costs money.** They are otherwise interchangeable from the
|
|
@@ -62,7 +57,7 @@ decision. Do not quietly pick the billed one.
|
|
|
62
57
|
was handed an endpoint and knows why. Don't offer it unprompted; it cannot be
|
|
63
58
|
shared either.
|
|
64
59
|
|
|
65
|
-
**`attach` gotcha (Chrome 136
|
|
60
|
+
**`attach` gotcha (Chrome 136 and later):** the developer's Chrome must be
|
|
66
61
|
started with BOTH `--remote-debugging-port=9222` AND
|
|
67
62
|
`--user-data-dir=<non-default-path>`. The debug flag is silently ignored on the
|
|
68
63
|
default profile. If `attach` fails, say this verbatim and offer `dedicated`.
|
|
@@ -109,8 +104,8 @@ Ask plainly:
|
|
|
109
104
|
> whoever holds the login — they can watch and click in it, including from a
|
|
110
105
|
> phone.
|
|
111
106
|
|
|
112
|
-
If they say yes, call `share_browser_view` and **relay the full URL
|
|
113
|
-
its own line.**
|
|
107
|
+
If they say yes, call `share_browser_view` and **relay the full returned URL
|
|
108
|
+
verbatim, on its own line.** It includes `liveview.html?magnify=1`.
|
|
114
109
|
|
|
115
110
|
- **`container` is the only mode this applies to.** `builder` returns a hosted
|
|
116
111
|
`liveViewUrl` from `attach_browser` — relay that instead, full and verbatim.
|
|
@@ -131,16 +126,15 @@ its own line.**
|
|
|
131
126
|
- **Call `stop_browser_view` as soon as they're done.** That closes the tunnel
|
|
132
127
|
only — the browser keeps running. `dispose_browser` and `reset_session` stop
|
|
133
128
|
the container itself.
|
|
134
|
-
- **If the URL will not load**,
|
|
135
|
-
Cloudflare
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
the reason.
|
|
129
|
+
- **If the URL will not load**, check `propagating`. The hostname can exist
|
|
130
|
+
before Cloudflare finishes routing it. Keep the returned share open, wait a
|
|
131
|
+
few seconds, and reload; don't start a second tunnel while it propagates.
|
|
132
|
+
`cloudflaredLog` contains bounded diagnostic output. If it still fails after
|
|
133
|
+
60 seconds, call `stop_browser_view`, then retry `share_browser_view` once.
|
|
140
134
|
- **cloudflared is handled for you.** An existing install on PATH wins;
|
|
141
135
|
otherwise it's fetched once into `~/.reclaim/bin` on the first share, never at
|
|
142
|
-
install time.
|
|
143
|
-
|
|
136
|
+
install time. Tell the developer when the download starts.
|
|
137
|
+
`RECLAIM_CLOUDFLARED_PATH` points at their own copy instead.
|
|
144
138
|
|
|
145
139
|
### How containers are shared, and when they close
|
|
146
140
|
|
|
@@ -169,8 +163,8 @@ its own line.**
|
|
|
169
163
|
runs translated. OrbStack does this itself; Docker Desktop needs "Use Rosetta
|
|
170
164
|
for x86/amd64 emulation" turned on. Under QEMU instead of Rosetta, Chromium
|
|
171
165
|
crashes on launch.
|
|
172
|
-
- **First run looks stuck.** It is downloading
|
|
173
|
-
letting it look hung.
|
|
166
|
+
- **First run looks stuck.** It is downloading the browser image. Say that
|
|
167
|
+
rather than letting it look hung.
|
|
174
168
|
- A container left over from a crashed run is **reused if it is still serving**,
|
|
175
169
|
and replaced if it is not — a second attach does not need a different port.
|
|
176
170
|
|
|
@@ -329,6 +323,23 @@ captured secret *header* whose value also sits in the URL, which is already
|
|
|
329
323
|
known and so gets checked at draft time (`assertNoSecretUrlConflict`).
|
|
330
324
|
|
|
331
325
|
<!-- section: no-rename-param -->
|
|
326
|
+
**Keep internal claim-construction fields out of claimant-facing UI.** When a
|
|
327
|
+
named regex group or `{{parameter}}` only helps construct another claim request,
|
|
328
|
+
consider an internal prefix:
|
|
329
|
+
|
|
330
|
+
- `REQ_` for request/body/header or other intermediate values.
|
|
331
|
+
- `URL_` for URL path/query values.
|
|
332
|
+
|
|
333
|
+
Builder data-point and SEO/catalog surfaces, and compatible claimant UIs, omit
|
|
334
|
+
these names case-insensitively. The prefix is presentation metadata, not a
|
|
335
|
+
security boundary: the exact parameter remains in the recipe, proof, and
|
|
336
|
+
proof-time substitution maps. Keep the same full name everywhere it is
|
|
337
|
+
referenced: the named group, template, and `paramValues`; for an
|
|
338
|
+
`allowedJsRequests` template, also use it in `templateParams`. With
|
|
339
|
+
`propose_provider`, pass the prefixed target name and `shortenNames: false`;
|
|
340
|
+
otherwise name shortening drops the prefix. Don't prefix an actual
|
|
341
|
+
consumer-facing result merely to conceal it.
|
|
342
|
+
|
|
332
343
|
**Do not rename the match-template param.** The synthesizer's `{{name}}` in
|
|
333
344
|
`responseMatches[].value` is an _extraction_ template — it stays bare and never
|
|
334
345
|
takes the `%CTX%` prefix. Only URL/body placeholders whose values the consumer
|
package/skill/SKILL.old.md
CHANGED
|
@@ -45,10 +45,12 @@ emitted. Read both from topic `troubleshooting`.
|
|
|
45
45
|
## Getting authenticated
|
|
46
46
|
|
|
47
47
|
Call `reclaim_authenticate({})` with no arguments. It opens the Reclaim
|
|
48
|
-
dashboard in Chrome, waits for the developer to
|
|
49
|
-
the page and closes the tab
|
|
50
|
-
|
|
51
|
-
|
|
48
|
+
dashboard only in a local Chrome over loopback CDP, waits for the developer to
|
|
49
|
+
sign in, reads the token from the page, and closes the tab. It never uses a
|
|
50
|
+
Builder, container, custom-CDP, or shared browser. A still-valid cached identity
|
|
51
|
+
is reused first, so it's often instant. Tell the developer once: _"A local
|
|
52
|
+
Chrome window is opening the Reclaim dashboard — please sign in and I'll
|
|
53
|
+
continue automatically."_
|
|
52
54
|
|
|
53
55
|
- Force a fresh login: `reclaim_authenticate({ useBrowser: true })`.
|
|
54
56
|
- Skip the browser: `reclaim_authenticate({ token: "<firebase-id-token>" })`, or
|
|
@@ -64,7 +66,7 @@ and are NOT interchangeable.
|
|
|
64
66
|
- **`reclaim_authenticate`** authorizes everything in this mode:
|
|
65
67
|
`create_provider_version_from_capture`, `get_me_providers`, and the rest.
|
|
66
68
|
- **`authenticate_builder`** covers exactly two Builder-exclusive features:
|
|
67
|
-
allocating a
|
|
69
|
+
allocating a Builder remote browser (`attach_browser mode="builder"`) and
|
|
68
70
|
`list_builder_organizations`. It authorizes nothing else.
|
|
69
71
|
|
|
70
72
|
`get_devtools_mode` reports the presence of both. If a publish or
|
|
@@ -115,7 +117,7 @@ it. Don't assume `authenticate_builder` fixes it.
|
|
|
115
117
|
under that `draftId`; the secrets never enter the transcript, so you carry the
|
|
116
118
|
`draftId`, not the provider JSON, into replay/prove.
|
|
117
119
|
|
|
118
|
-
**Extracting
|
|
120
|
+
**Extracting more than one value?** Prefer a **single request whose response
|
|
119
121
|
contains all of them** and draft them together — one `responseMatches` +
|
|
120
122
|
`responseRedactions` pair per value. Find that request by running
|
|
121
123
|
`find_requests_containing` for each value and picking a `requestId` common to
|
|
@@ -256,8 +258,11 @@ Call `create_provider_version_from_capture`:
|
|
|
256
258
|
`geoLocation`, `providerType` (`PRIVATE` default | `PUBLIC`).
|
|
257
259
|
⚠️ Re-running without `providerId` registers ANOTHER provider — that's how
|
|
258
260
|
duplicates appear. Check `get_me_providers` first.
|
|
259
|
-
- **New version of an existing provider:** pass its `providerId
|
|
260
|
-
|
|
261
|
+
- **New version of an existing provider:** pass its `providerId`. Omit
|
|
262
|
+
`version` and `bump` for the default patch bump; set `bump` to `major`,
|
|
263
|
+
`minor`, or `patch`, or pass the exact higher `version` requested by the
|
|
264
|
+
user. Every change creates a new immutable version; never edit an existing
|
|
265
|
+
version. Pass `notes` (≥10 chars).
|
|
261
266
|
- **Multi-request provider** (landing call + details call, a
|
|
262
267
|
CSRF-preflight/refresh-token chain, …): run capture→draft→replay→prove ONCE
|
|
263
268
|
PER REQUEST, then pass `providers: [draftA, draftB, …]` instead of the single
|
|
@@ -304,10 +309,15 @@ There's no create-then-version step and no org / public-review workflow here.
|
|
|
304
309
|
When done, tell the developer the version is live and that they can integrate
|
|
305
310
|
using the `providerId`.
|
|
306
311
|
|
|
307
|
-
**
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
`
|
|
312
|
+
**If the response includes `_notes` for an attached browser, follow them for an
|
|
313
|
+
optional authenticated-state retest.** Only when its profile is still signed in,
|
|
314
|
+
navigate to `initialUrl`; if `customInjection` is present, run
|
|
315
|
+
`test_user_script` with that script and `initialUrl`. When the existing capture
|
|
316
|
+
has enough data, replay and prove every required request. Do not clear
|
|
317
|
+
cookies or force another sign-in. Treat an unavailable or inconclusive retest as
|
|
318
|
+
advisory and do not block the completed provider. Ask whether the developer
|
|
319
|
+
wants to keep the session for further edits; call `dispose_browser` only after
|
|
320
|
+
they confirm.
|
|
311
321
|
|
|
312
322
|
<!-- topic: never-do -->
|
|
313
323
|
## Things to never do
|