@reclaimprotocol/agent 0.1.28 → 0.1.31

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (89) hide show
  1. package/README.md +28 -9
  2. package/lib/consts.d.ts +5 -5
  3. package/lib/consts.js +7 -7
  4. package/lib/live/tunnel-watchdog.d.ts +1 -0
  5. package/lib/live/tunnel-watchdog.js +66 -0
  6. package/lib/live/tunnel.d.ts +30 -7
  7. package/lib/live/tunnel.js +217 -40
  8. package/lib/mcp/api/operations.gen.d.ts +12 -0
  9. package/lib/mcp/api/operations.gen.js +537 -59
  10. package/lib/mcp/api/tools.js +27 -5
  11. package/lib/mcp/instructions.js +25 -32
  12. package/lib/mcp/remap_errors.js +6 -0
  13. package/lib/mcp/server.js +2 -8
  14. package/lib/mcp/start.js +3 -5
  15. package/lib/mcp/success-response.d.ts +5 -0
  16. package/lib/mcp/success-response.js +21 -0
  17. package/lib/mcp/tools/agent/attach.js +9 -66
  18. package/lib/mcp/tools/agent/build.d.ts +0 -3
  19. package/lib/mcp/tools/agent/build.js +3 -4
  20. package/lib/mcp/tools/agent/how-it-works.d.ts +5 -15
  21. package/lib/mcp/tools/agent/how-it-works.js +9 -22
  22. package/lib/mcp/tools/agent/live-view.d.ts +1 -15
  23. package/lib/mcp/tools/agent/live-view.js +71 -96
  24. package/lib/mcp/tools/agent/propose.js +7 -23
  25. package/lib/mcp/tools/agent/publish.js +80 -18
  26. package/lib/mcp/tools/agent/session.d.ts +4 -7
  27. package/lib/mcp/tools/agent/session.js +8 -23
  28. package/lib/mcp/tools/verification/result.d.ts +2 -1
  29. package/lib/mcp/tools/verification/result.js +29 -28
  30. package/lib/old/client.d.ts +2 -2
  31. package/lib/old/flag.d.ts +2 -5
  32. package/lib/old/flag.js +2 -5
  33. package/lib/old/instructions.d.ts +1 -1
  34. package/lib/old/instructions.js +1 -1
  35. package/lib/old/to-register.d.ts +4 -3
  36. package/lib/old/tools/publish.js +11 -14
  37. package/lib/proof/attestor-auth.d.ts +9 -1
  38. package/lib/proof/attestor-auth.js +15 -2
  39. package/lib/proof/attestor.d.ts +2 -2
  40. package/lib/proof/attestor.js +20 -2
  41. package/lib/proof/pod-prover.js +1 -1
  42. package/lib/proof/proof-core.d.ts +4 -0
  43. package/lib/proof/proof-core.js +20 -0
  44. package/lib/provider/injection-doc.d.ts +3 -2
  45. package/lib/provider/injection-doc.js +12 -10
  46. package/lib/provider/matcher.js +19 -15
  47. package/lib/provider/to-version.d.ts +3 -0
  48. package/lib/provider/to-version.js +2 -1
  49. package/package.json +3 -3
  50. package/skill/SKILL.builder.md +32 -85
  51. package/skill/SKILL.common.md +38 -22
  52. package/skill/SKILL.old.md +17 -3
  53. package/skill/builder-mode.md +37 -4
  54. package/skill/community-contributions.md +12 -3
  55. package/skill/hash-validation.md +9 -9
  56. package/skill/interception.md +39 -46
  57. package/skill/old-mode.md +13 -0
  58. package/skill/user-script.md +24 -8
  59. package/skill/verification-client-integration.md +130 -0
  60. package/src/consts.ts +9 -7
  61. package/src/mcp/api/operations.gen.ts +539 -59
  62. package/src/mcp/api/tools.ts +38 -7
  63. package/src/mcp/instructions.ts +35 -32
  64. package/src/mcp/remap_errors.ts +6 -0
  65. package/src/mcp/server.ts +2 -8
  66. package/src/mcp/start.ts +3 -5
  67. package/src/mcp/success-response.ts +26 -0
  68. package/src/mcp/tools/agent/attach.ts +9 -71
  69. package/src/mcp/tools/agent/build.ts +4 -7
  70. package/src/mcp/tools/agent/how-it-works.ts +9 -22
  71. package/src/mcp/tools/agent/live-view.ts +76 -116
  72. package/src/mcp/tools/agent/propose.ts +7 -23
  73. package/src/mcp/tools/agent/publish.ts +89 -18
  74. package/src/mcp/tools/agent/session.ts +6 -23
  75. package/src/mcp/tools/verification/result.ts +30 -29
  76. package/src/old/client.ts +2 -2
  77. package/src/old/flag.ts +2 -5
  78. package/src/old/instructions.ts +1 -1
  79. package/src/old/to-register.ts +4 -3
  80. package/src/old/tools/publish.ts +16 -14
  81. package/src/proof/attestor-auth.ts +18 -1
  82. package/src/proof/attestor.ts +24 -3
  83. package/src/proof/pod-prover.ts +1 -1
  84. package/src/proof/proof-core.ts +28 -0
  85. package/src/provider/injection-doc.ts +12 -10
  86. package/src/provider/matcher.ts +25 -17
  87. package/src/provider/to-version.ts +7 -1
  88. package/src/live/cloudflared.ts +0 -348
  89. package/src/live/tunnel.ts +0 -254
@@ -1,79 +1,52 @@
1
1
  /**
2
- * `share_browser_view` / `stop_browser_view` — put the attached container
3
- * browser's built-in viewer behind a public URL so someone who is not at this
4
- * machine can watch it and optionally drive it.
2
+ * `share_browser_view` — gives the calling agent what it needs to put the
3
+ * attached container browser's built-in viewer behind a public URL, so someone
4
+ * who is not at this machine can watch it and optionally drive it.
5
5
  *
6
- * Scoped to `container` mode on purpose. A `builder`-mode browser already has
7
- * a hosted live view at `attach_browser`'s `liveViewUrl`; tunnelling a second
8
- * one would duplicate it, so this refuses and points at the existing URL.
6
+ * The server never runs, downloads or resolves cloudflared: the agent runs it
7
+ * itself and passes the resulting `trycloudflare.com` URL back to get the link
8
+ * to hand out.
9
9
  *
10
- * The share is deliberately explicit and single-instance: one live view per
11
- * attached browser, started only when asked for, and torn down by
12
- * `stop_browser_view`, `dispose_browser`, or `reset_session`.
10
+ * Scoped to `container` mode on purpose. A `builder`-mode browser already has
11
+ * a hosted live view at `attach_browser`'s `liveViewUrl`.
13
12
  */
14
- import { badRequest, conflict } from '@hapi/boom'
13
+ import { badRequest } from '@hapi/boom'
15
14
  import assert from 'node:assert'
16
- import { startTunnel, type Tunnel } from '../../../live/tunnel.ts'
17
- import { LOGGER } from '../../../logger.ts'
18
15
  import { defineTool, type RegisteredTool } from '../../server.ts'
19
16
  import { type AttachState, SHAREABLE_MODES } from './attach.ts'
20
17
  import { ensureLiveViewUrl } from './live-view-url.ts'
21
18
 
22
- export interface LiveShare {
23
- tunnel: Tunnel
24
- /** The public URL handed to whoever is watching. */
25
- url: string
26
- }
27
-
28
- export interface LiveShareRef {
29
- current?: LiveShare
30
- }
31
-
32
- type TunnelStarter = typeof startTunnel
33
-
34
- /** Tear down whatever share is running. Safe when none is. Exported so the
35
- * session tools can call it from `dispose_browser` / `reset_session`. */
36
- export async function stopLiveShare(ref: LiveShareRef): Promise<boolean> {
37
- const share = ref.current
38
- if(!share) {
39
- return false
40
- }
41
-
42
- ref.current = undefined
43
- // Only the tunnel is ours to close. The browser and its viewer belong to the
44
- // container, which keeps running until the browser is disposed.
45
- await share.tunnel.stop().catch((err) => {
46
- LOGGER.warn({ err }, 'live view: tunnel stop failed')
47
- })
48
- return true
49
- }
19
+ /** Quick-tunnel hostnames only: `https://<words>.trycloudflare.com`. */
20
+ const QUICK_TUNNEL_HOST = /\.trycloudflare\.com$/i
50
21
 
51
22
  export function liveViewTools(
52
23
  attachRef: { current?: AttachState },
53
- shareRef: LiveShareRef,
54
- openTunnel: TunnelStarter = startTunnel,
55
24
  ): RegisteredTool[] {
56
25
  return [
57
- defineTool<Record<string, never>>(
26
+ defineTool<{ publicUrl?: string }>(
58
27
  {
59
28
  name: 'share_browser_view',
60
29
  description:
61
- 'Opens a public HTTPS tunnel to the attached `container` browser '
62
- + 'viewer. Use it when someone else must sign in or control it. '
63
- + 'Returns `url` with `liveview.html?magnify=1`; relay it verbatim '
64
- + 'on its own line. '
65
- + 'Only `container` supports this. For `builder`, relay '
66
- + 'the `liveViewUrl` from attach_browser; local modes '
67
- + '(`dedicated`, `attach`, `custom`) '
68
- + 'cannot be shared. The URL is a credential: anyone with it can '
69
- + 'control the signed-in browser. Share it only with the intended '
70
- + 'person and call stop_browser_view when done. `propagating: true` '
71
- + 'means the tunnel remains live while the Cloudflare edge starts; '
72
- + 'reuse the URL and reload after a few seconds rather than creating '
73
- + 'another tunnel.',
74
- inputSchema: { type: 'object', properties: {} },
30
+ 'Shares an attached `container` browser with someone who is not at '
31
+ + 'this machine. Step 1: call with no arguments to get the local '
32
+ + 'viewer URL and the `cloudflared` command to run yourself (this '
33
+ + 'server does not run it). Step 2: after cloudflared prints its '
34
+ + '`https://*.trycloudflare.com` URL, call again with it as '
35
+ + '`publicUrl` to get the link to relay. Treat the link as a '
36
+ + 'credential. For `builder`, use the `liveViewUrl` from '
37
+ + '`attach_browser`.',
38
+ inputSchema: {
39
+ type: 'object',
40
+ properties: {
41
+ publicUrl: {
42
+ type: 'string',
43
+ description: 'The `https://*.trycloudflare.com` URL that '
44
+ + 'cloudflared printed.',
45
+ },
46
+ },
47
+ },
75
48
  },
76
- async() => {
49
+ async(parsed) => {
77
50
  const attached = attachRef.current
78
51
  assert(attached, badRequest(
79
52
  'No browser attached. Call attach_browser first.',
@@ -92,79 +65,66 @@ export function liveViewTools(
92
65
  + '"builder" (hosted, no install, chargeable)')
93
66
  + '.',
94
67
  ))
95
- assert(!shareRef.current, conflict(
96
- 'A live view is already running at '
97
- + `${shareRef.current?.url}. Call stop_browser_view first, or `
98
- + 'reuse that URL.',
99
- ))
100
68
 
101
69
  // The container publishes its viewer on loopback and its full CDP
102
- // port beside it. Only the VIEWER is ever tunnelled: the CDP port is
103
- // unauthenticated, total control of the browser, and putting it on a
104
- // public URL would hand that to anyone with the link.
70
+ // port beside it. Only the VIEWER may be exposed: the CDP port is
71
+ // unauthenticated, total control of the browser.
105
72
  const viewerPort = attached.viewerPort
106
73
  assert(viewerPort, badRequest(
107
74
  'This browser reports no live-view port. Re-attach with mode '
108
75
  + '"container".',
109
76
  ))
110
77
 
111
- const tunnel = await openTunnel(viewerPort, {
112
- probePath: '/liveview.html?magnify=1',
113
- })
114
- // The path is the runtime's own viewer page; `magnify=1` is what makes
115
- // it lay out for the device that opens it.
116
- const url = ensureLiveViewUrl(tunnel.url)
117
- const share: LiveShare = {
118
- tunnel, url,
78
+ if(parsed.publicUrl) {
79
+ const publicUrl = parseQuickTunnelUrl(parsed.publicUrl)
80
+ return {
81
+ url: ensureLiveViewUrl(publicUrl.origin),
82
+ _notes: [
83
+ 'Relay `url` to the developer verbatim, on its own line.',
84
+ 'The URL is a credential: it needs no login, and anyone with '
85
+ + 'it can see this browser and act in it as the signed-in user.',
86
+ 'It shows the whole browser, so a sign-in that opens a '
87
+ + 'separate window is visible too.',
88
+ 'Opening it on a phone lays the page out for that phone, with '
89
+ + 'touch, pinch-zoom and the soft keyboard.',
90
+ 'When they are done, stop the cloudflared process you started. '
91
+ + 'The browser keeps running until `dispose_browser`.',
92
+ ],
93
+ }
119
94
  }
120
- shareRef.current = share
121
- LOGGER.info({ mode: attached.mode }, 'live view shared')
122
95
 
96
+ const localUrl = `http://localhost:${viewerPort}`
123
97
  return {
124
- url,
125
- ...(share.tunnel.ready
126
- ? {}
127
- : {
128
- propagating: true,
129
- cloudflaredLog: share.tunnel.cloudflaredLog,
130
- }),
131
- ...(share.tunnel.binary.source === 'downloaded'
132
- ? { cloudflaredInstalled: true }
133
- : {}),
98
+ localViewerUrl: ensureLiveViewUrl(localUrl),
99
+ port: viewerPort,
100
+ command: `cloudflared tunnel --url ${localUrl}`,
134
101
  _notes: [
135
- ...(share.tunnel.binary.source === 'downloaded'
136
- ? ['cloudflared was not installed, so it was downloaded once '
137
- + 'to ~/.reclaim/bin. Mention that; later shares reuse it.']
138
- : []),
139
- ...(share.tunnel.ready
140
- ? []
141
- : ['The tunnel hostname exists but the public viewer is still '
142
- + 'propagating. Relay the URL now; if it errors, wait a few '
143
- + 'seconds and reload. Do not start a second tunnel.']),
144
- 'Relay `url` to the developer verbatim, on its own line.',
145
- 'The URL is a credential: it needs no login, and anyone with it '
146
- + 'can see this browser and act in it as the signed-in user.',
147
- 'It shows the whole browser, so a sign-in that opens a separate '
148
- + 'window is visible too — unlike a single-tab stream.',
149
- 'Opening it on a phone lays the page out for that phone, with '
150
- + 'touch, pinch-zoom and the soft keyboard.',
151
- 'Call stop_browser_view when they are done. That closes the '
152
- + 'tunnel only — the browser keeps running.',
102
+ 'Run `command` yourself in a background process. If `cloudflared` '
103
+ + 'is not installed, tell the developer and ask them to install '
104
+ + 'it (https://developers.cloudflare.com/cloudflare-one/'
105
+ + 'connections/connect-networks/downloads/) — do not install it '
106
+ + 'without their approval.',
107
+ 'Read its output for a line containing '
108
+ + '`https://<words>.trycloudflare.com`. That is the public URL.',
109
+ 'Call share_browser_view again with that value as `publicUrl` '
110
+ + 'to get the link to relay. Never guess or reconstruct the '
111
+ + 'hostname.',
112
+ 'Expose only this port. Never tunnel the browser\'s CDP port.',
153
113
  ],
154
114
  }
155
115
  },
156
116
  ),
157
- defineTool<Record<string, never>>(
158
- {
159
- name: 'stop_browser_view',
160
- description:
161
- 'Stops the public tunnel opened by share_browser_view; the URL stops '
162
- + 'resolving. Call it when the viewer is done. The browser stays '
163
- + 'attached, its local viewer keeps serving, and capture continues. '
164
- + 'Returns `stopped: false` when no public share is active.',
165
- inputSchema: { type: 'object', properties: {} },
166
- },
167
- async() => ({ stopped: await stopLiveShare(shareRef) }),
168
- ),
169
117
  ]
170
118
  }
119
+
120
+ function parseQuickTunnelUrl(value: string): URL {
121
+ const url = URL.parse(value)
122
+ assert(
123
+ url?.protocol === 'https:' && QUICK_TUNNEL_HOST.test(url.hostname),
124
+ badRequest(
125
+ 'publicUrl must be the https://*.trycloudflare.com URL printed by '
126
+ + 'cloudflared.',
127
+ ),
128
+ )
129
+ return url
130
+ }
@@ -17,29 +17,13 @@ export function proposeTool(resolveBackend: ResolveBackend): RegisteredTool {
17
17
  {
18
18
  name: 'propose_provider',
19
19
  description:
20
- 'Draft a provider from ONE captured request. Pass `targets` (an '
21
- + 'array): one entry per value to extract, each with its `value` '
22
- + '(verbatim) and a distinct `name` (its param). The provider gets '
23
- + 'one match + redaction per target. Returns a draftId + recipe '
24
- + '(never secrets). Prefer a SINGLE request holding ALL values. '
25
- + 'Single-value shorthand: `target` + `name`. Per-target `hash` '
26
- + 'OPRF-hashes that value. BY DEFAULT each `name` is shortened to '
27
- + 'its last alphanumeric token, lowercased, to form the param key '
28
- + '(for example, "employer_name" → "name") — pick names that stay '
29
- + 'distinct '
30
- + 'after shortening ("firstName"/"surname", not "first_name"/'
31
- + '"last_name"). Pass `shortenNames: false` to use each `name` '
32
- + 'VERBATIM as the param key instead — it becomes both a regex '
33
- + 'capture-group name and a paramValues key, so it must be a plain '
34
- + 'identifier (letters/digits/underscore, not starting with a '
35
- + 'digit); we recommend lower_snake_case (for example, '
36
- + '"employer_name"), '
37
- + 'but you choose the exact name. For a parameter used only to build a '
38
- + 'later claim request, preserve a `REQ_` prefix (body, header, or '
39
- + 'intermediate value) or `URL_` prefix (path or query value) by '
40
- + 'setting `shortenNames: false`. The prefix hides that internal '
41
- + 'parameter from the data-point UI; it remains available to named '
42
- + 'regexes and `{{param}}` substitutions.',
20
+ 'Drafts a provider from one authenticated captured request and '
21
+ + 'keeps captured secrets server-side. Use `targets` for every value '
22
+ + 'available in the same response; the result contains a `draftId` '
23
+ + 'and secret-free recipe. Set `shortenNames: false` to preserve an '
24
+ + 'explicit parameter name, including a hidden `REQ_` or `URL_` '
25
+ + 'prefix. For matching and naming guidance, call '
26
+ + 'how_it_works({ topic: "draft" }) and topic `params`.',
43
27
  inputSchema: objectSchema(
44
28
  {
45
29
  requestId: { type: 'string' },
@@ -137,6 +137,75 @@ const ALLOWED_JS_REQUEST_SCHEMA: JSONSchema.Schema = {
137
137
  required: ['url', 'method'],
138
138
  }
139
139
 
140
+ const BROWSER_REQUEST_ACTION_SCHEMA: JSONSchema.Schema = {
141
+ type: 'object',
142
+ description:
143
+ 'Portal-only authenticated browser action. The page supplies the action ' +
144
+ 'id and declared primitive inputs; this object fixes the HTTPS origin, ' +
145
+ 'request template, and returned fields.',
146
+ properties: {
147
+ id: { type: 'string', pattern: '^[A-Za-z][A-Za-z0-9_-]{0,99}$' },
148
+ origin: { type: 'string', pattern: '^https://[^/?#]+$' },
149
+ path: { type: 'string', pattern: '^/(?:$|[^/\\\\][^\\\\]*)$' },
150
+ pathTemplate: {
151
+ type: 'string',
152
+ pattern: '^/(?:$|[^/\\\\][^\\\\]*)$',
153
+ description:
154
+ 'Origin-relative path/query template using `{{name}}` inputs.',
155
+ },
156
+ method: {
157
+ type: 'string',
158
+ enum: ['GET', 'POST', 'PUT', 'PATCH', 'DELETE'],
159
+ },
160
+ transport: { type: 'string', enum: ['fetch', 'navigate'] },
161
+ userAgent: {
162
+ type: 'string',
163
+ minLength: 1,
164
+ maxLength: 300,
165
+ pattern: '^[^\\r\\n]+$',
166
+ description:
167
+ 'Browser User-Agent applied before the isolated action page navigates.',
168
+ },
169
+ omitOriginHeader: {
170
+ type: 'boolean',
171
+ description:
172
+ 'Remove Chromium\'s automatic Origin header from a fetch action. ' +
173
+ 'Unsupported for navigate actions.',
174
+ },
175
+ headers: {
176
+ type: 'object',
177
+ additionalProperties: { type: 'string' },
178
+ },
179
+ headersTemplate: {
180
+ type: 'object',
181
+ description: 'Header values may interpolate declared `{{name}}` inputs.',
182
+ additionalProperties: { type: 'string' },
183
+ },
184
+ bodyTemplate: {
185
+ type: 'string',
186
+ description: 'JSON template using declared `{{name}}` inputs.',
187
+ },
188
+ cookieValueHeader: {
189
+ type: 'object',
190
+ properties: {
191
+ name: { type: 'string' },
192
+ cookieName: { type: 'string' },
193
+ },
194
+ required: ['name', 'cookieName'],
195
+ },
196
+ includeBody: { type: 'boolean' },
197
+ inputKeys: {
198
+ type: 'array',
199
+ items: { type: 'string', pattern: '^[A-Za-z][A-Za-z0-9_]{0,99}$' },
200
+ },
201
+ responseFields: {
202
+ type: 'array',
203
+ items: { type: 'string', pattern: '^[A-Za-z][A-Za-z0-9_]{0,99}$' },
204
+ },
205
+ },
206
+ required: ['id', 'origin', 'path', 'method'],
207
+ }
208
+
140
209
  interface PublishArgs {
141
210
  providerId: string
142
211
  /** Single-provider back-compat input; normalized to `[provider]`. */
@@ -150,6 +219,9 @@ interface PublishArgs {
150
219
  requiredContext?: CreateProviderVersionRequest['requiredContext']
151
220
  jsUserScripts?: string
152
221
  allowedJsRequests?: AllowedJsRequest[]
222
+ browserRequestActions?: NonNullable<
223
+ CreateProviderVersionRequest['webSettings']['browserRequestActions']
224
+ >
153
225
  interceptorOptions?: {
154
226
  interceptorType: 'HAWKEYE' | 'MSWJS' | 'CDP'
155
227
  interceptorSettings?: string
@@ -165,24 +237,13 @@ export function createVersionFromCaptureTool(
165
237
  {
166
238
  name: 'create_provider_version_from_capture',
167
239
  description:
168
- 'Persist drafted ReclaimProvider(s) as a provider version, after ' +
169
- 'run_proof succeeds. Pass the `provider` object(s) from ' +
170
- 'propose_provider VERBATIM — this tool builds the version body ' +
171
- '(requests[], webSettings, …) for you; do NOT hand-construct that ' +
172
- 'shape or call create_provider_version directly. Exactly one of ' +
173
- '`provider` (one request) or `providers` (a multi-request provider — ' +
174
- 'every entry is required at verification time). This tool always ' +
175
- 'creates a new immutable version; never edit an existing version. ' +
176
- 'Omit `version` and `bump` for a patch bump, pass `bump` for a ' +
177
- 'major/minor/patch bump, or pass the exact higher `version` the user ' +
178
- 'directed. The version lands on the ' +
179
- 'draft branch: call `publish_provider_version` (PRIVATE) or ' +
180
- '`submit_provider_version_for_review` (PUBLIC) to go live. ' +
181
- '`requiredContext` is derived from any {{context.<name>}} ' +
182
- 'placeholders and returned on the created version — relay it to the ' +
183
- 'dev verbatim, since a session missing those fields is a 400. Always ' +
184
- 'read the response\'s `_notes`. ' +
185
- 'Full walkthrough: how_it_works({ topic: "publish" }).',
240
+ 'Creates a new immutable provider version after `run_proof` succeeds. '
241
+ + 'Pass exactly one `provider` or `providers` value returned by '
242
+ + '`propose_provider`; do not rebuild the recipe. The default is a '
243
+ + 'patch bump, or use `bump` or an explicitly requested higher '
244
+ + '`version`. The new version remains a draft until published or '
245
+ + 'submitted for review. Read `_notes` and '
246
+ + 'how_it_works({ topic: "publish" }).',
186
247
  inputSchema: {
187
248
  type: 'object',
188
249
  properties: {
@@ -264,6 +325,15 @@ export function createVersionFromCaptureTool(
264
325
  ALLOWED_REQUESTS_HINT,
265
326
  items: ALLOWED_JS_REQUEST_SCHEMA,
266
327
  },
328
+ browserRequestActions: {
329
+ type: 'array',
330
+ description:
331
+ 'Portal-only actions available to jsUserScripts through ' +
332
+ 'window.Reclaim.browserRequest. Use these for bounded ' +
333
+ 'same-origin requests that need the browser cookie jar. ' +
334
+ 'Other Verification Clients currently ignore them.',
335
+ items: BROWSER_REQUEST_ACTION_SCHEMA,
336
+ },
267
337
  interceptorOptions: {
268
338
  type: 'object',
269
339
  description:
@@ -328,6 +398,7 @@ export function createVersionFromCaptureTool(
328
398
  requiredContext: args.requiredContext,
329
399
  jsUserScripts: args.jsUserScripts,
330
400
  allowedJsRequests: args.allowedJsRequests,
401
+ browserRequestActions: args.browserRequestActions,
331
402
  interceptorOptions: args.interceptorOptions,
332
403
  })
333
404
  const result = await client.call('CreateProviderVersion', {
@@ -2,12 +2,10 @@ import type { ConfigStore } from '../../../config/store.ts'
2
2
  import { defineTool, type RegisteredTool } from '../../server.ts'
3
3
  import { type AttachState, disposeAttached } from './attach.ts'
4
4
  import { type CaptureStore, resetCaptures } from './capture.ts'
5
- import { type LiveShareRef, stopLiveShare } from './live-view.ts'
6
5
 
7
6
  export function sessionTools(
8
7
  attachRef: { current?: AttachState, config: ConfigStore },
9
8
  captures: CaptureStore,
10
- liveShare: LiveShareRef,
11
9
  ): RegisteredTool[] {
12
10
  return [
13
11
  defineTool<Record<string, never>>(
@@ -29,13 +27,6 @@ export function sessionTools(
29
27
  orgId: attachRef.current?.orgId,
30
28
  captures: [...captures.sessions.keys()],
31
29
  activeTab: captures.activeTab,
32
- // The public share, when one is open — so a caller that lost the
33
- // URL can recover it instead of opening a second tunnel.
34
- ...(liveShare.current
35
- ? {
36
- sharedView: { url: liveShare.current.url },
37
- }
38
- : {}),
39
30
  }),
40
31
  ),
41
32
  defineTool<Record<string, never>>(
@@ -44,12 +35,11 @@ export function sessionTools(
44
35
  description:
45
36
  'Stop captures, disconnect the current browser, and immediately '
46
37
  + 'delete a Builder-hosted Popcorn session. Custom CDP sessions '
47
- + 'are disconnected but never deleted by Reclaim. Also closes a '
48
- + 'public live view opened by share_browser_view.',
38
+ + 'are disconnected but never deleted by Reclaim.',
49
39
  inputSchema: { type: 'object', properties: {} },
50
40
  },
51
41
  async() => {
52
- await resetAll(attachRef, captures, liveShare)
42
+ await resetAll(attachRef, captures)
53
43
  return { disposed: true }
54
44
  },
55
45
  ),
@@ -58,12 +48,11 @@ export function sessionTools(
58
48
  name: 'reset_session',
59
49
  description:
60
50
  'Stop all captures and clear capture state — drops every '
61
- + 'session\'s drafts and their in-memory secrets, and closes a '
62
- + 'public live view opened by share_browser_view.',
51
+ + 'session\'s drafts and their in-memory secrets.',
63
52
  inputSchema: { type: 'object', properties: {} },
64
53
  },
65
54
  async() => {
66
- await resetAll(attachRef, captures, liveShare)
55
+ await resetAll(attachRef, captures)
67
56
  return { reset: true }
68
57
  },
69
58
  ),
@@ -71,23 +60,17 @@ export function sessionTools(
71
60
  }
72
61
 
73
62
  /**
74
- * Release everything a session holds: the public tunnel, the captures, and the
75
- * browser itself.
63
+ * Release everything a session holds: the captures and the browser itself.
76
64
  *
77
65
  * Exported because a session can end WITHOUT anyone calling `dispose_browser` —
78
66
  * the developer closes their agent, the client kills the server, the transport
79
67
  * drops. Nothing runs on its own then, so `start.ts` runs this. Without it a
80
- * container keeps running and, worse, a public URL to a live browser outlives
81
- * the session that created it.
68
+ * container keeps running.
82
69
  */
83
70
  export async function resetAll(
84
71
  attachRef: { current?: AttachState, config: ConfigStore },
85
72
  captures: CaptureStore,
86
- liveShare: LiveShareRef,
87
73
  ) {
88
- // Before the captures: a public URL must never outlive the browser it points
89
- // at.
90
- await stopLiveShare(liveShare)
91
74
  await resetCaptures(captures)
92
75
  await disposeAttached(attachRef)
93
76
  }
@@ -1,16 +1,21 @@
1
1
  import {
2
- createReclaim,
3
2
  decryptCallback,
4
3
  type DecryptionKey,
4
+ ReclaimVerification,
5
5
  } from '@reclaimprotocol/client'
6
6
  import assert from 'node:assert'
7
7
  import { apiUrl, orgSecret } from '../../../consts.ts'
8
8
  import { defineTool, type RegisteredTool } from '../../server.ts'
9
9
  import { resolveDecryptionKey } from '../authenticate/decrypt-key.ts'
10
10
 
11
- /** Statuses that can carry a submitted (terminal) result. `expired`/`pending`
12
- * never have one. */
13
- const TERMINAL = new Set(['success', 'rejected', 'error'])
11
+ /** Statuses that can carry a Builder-managed terminal result. */
12
+ const TERMINAL = new Set([
13
+ 'success',
14
+ 'rejected',
15
+ 'error',
16
+ 'cancelled',
17
+ 'expired',
18
+ ])
14
19
 
15
20
  /**
16
21
  * Resolve the org secret used to authenticate verification reads. Auth is a
@@ -37,27 +42,25 @@ function resolveOrgSecret(): string {
37
42
  * plaintext signed JWS (when the org's encryption is off) or an ECIES
38
43
  * ciphertext that we decrypt here with the optional eth key
39
44
  * (`RECLAIM_DECRYPT_KEY` or the `reclaim-decrypt.key` hex file). The Builder
40
- * only ever relays the payload — decryption, when needed, happens here.
45
+ * creates, signs, optionally encrypts, and stores the delivery payload.
46
+ * Decryption, when needed, happens here.
41
47
  */
42
48
  export function verificationResultTool(): RegisteredTool {
43
- return defineTool<{ sessionId: string }>(
49
+ return defineTool<{ reclaimSessionId: string }>(
44
50
  {
45
51
  name: 'get_verification_result',
46
52
  description:
47
- 'Reads a verification session and locally checks each submitted ' +
48
- 'result. Requires RECLAIM_ORG_SECRET. Encrypted ECIES deliveries ' +
49
- 'also require the matching optional eth key ' +
50
- '(RECLAIM_DECRYPT_KEY or reclaim-decrypt.key); plaintext deliveries ' +
51
- 'are read directly. Returns `ready: false` until the session is ' +
52
- 'terminal and at least one payload is readable. When `ready: true`, ' +
53
- 'act only on result entries where `valid` is true; `ready` means a ' +
54
- 'payload was read, not that verification succeeded. Use ' +
55
- 'list_verification_events to diagnose an unfinished session.',
53
+ 'Fetches and verifies a terminal Builder result for one session. '
54
+ + 'Requires `RECLAIM_ORG_SECRET`; encrypted deliveries also require '
55
+ + 'the matching ETH decryption key. Returns `ready: false` until a '
56
+ + 'payload is available. Invalid results fail verification and are not '
57
+ + 'returned. Use `list_verification_events` to diagnose '
58
+ + 'an unfinished session.',
56
59
  inputSchema: {
57
60
  type: 'object',
58
- required: ['sessionId'],
61
+ required: ['reclaimSessionId'],
59
62
  properties: {
60
- sessionId: {
63
+ reclaimSessionId: {
61
64
  type: 'string',
62
65
  description:
63
66
  'The verification session id from create_verification_session.',
@@ -71,36 +74,36 @@ export function verificationResultTool(): RegisteredTool {
71
74
  const decryptionKey: DecryptionKey | undefined =
72
75
  await resolveDecryptionKey()
73
76
 
74
- const reclaim = createReclaim({
77
+ const reclaim = ReclaimVerification.create({
75
78
  baseUrl: apiUrl(),
76
79
  orgSecret: secret,
77
80
  })
78
81
 
79
82
  const sessionRes = await reclaim.api('GetVerificationSession', {
80
- params: { sessionId: args.sessionId },
83
+ params: { sessionId: args.reclaimSessionId },
81
84
  })
82
85
  const status = sessionRes.data.status
83
86
 
84
87
  if(!TERMINAL.has(status)) {
85
- return { sessionId: args.sessionId, status, ready: false }
88
+ return { reclaimSessionId: args.reclaimSessionId, status, ready: false }
86
89
  }
87
90
 
88
91
  const resultRes = await reclaim.api('GetVerificationResult', {
89
- params: { sessionId: args.sessionId },
92
+ params: { sessionId: args.reclaimSessionId },
90
93
  })
91
94
  const { items } = resultRes.data
92
95
 
93
96
  if(!items.length) {
94
97
  return {
95
- sessionId: args.sessionId,
98
+ reclaimSessionId: args.reclaimSessionId,
96
99
  status,
97
100
  ready: false,
98
101
  note: 'Session is terminal but no result has been submitted yet.',
99
102
  }
100
103
  }
101
104
 
102
- // Each item's `payload` is an ECIES ciphertext (encrypted to the org's
103
- // credential) or the plaintext signed JWS, as raw text. `decryptCallback`
105
+ // Each item's `payload` is an ECIES ciphertext encrypted to the org's
106
+ // public key, or the plaintext signed JWS. `decryptCallback`
104
107
  // branches on the body shape; an encrypted delivery with no key throws
105
108
  // a clear error, which we surface per-subscription rather than skip.
106
109
  const results: unknown[] = []
@@ -116,12 +119,10 @@ export function verificationResultTool(): RegisteredTool {
116
119
  }
117
120
 
118
121
  const outcome = await reclaim.results.verify(jws, {
119
- expectedReclaimSessionId: args.sessionId,
122
+ reclaimSessionId: args.reclaimSessionId,
120
123
  })
121
124
  results.push({
122
125
  subscriptionId: item.subscriptionId,
123
- valid: outcome.valid,
124
- reason: outcome.reason,
125
126
  payload: outcome.payload,
126
127
  proofs: outcome.proofs,
127
128
  })
@@ -129,7 +130,7 @@ export function verificationResultTool(): RegisteredTool {
129
130
 
130
131
  if(!results.length) {
131
132
  return {
132
- sessionId: args.sessionId,
133
+ reclaimSessionId: args.reclaimSessionId,
133
134
  status,
134
135
  ready: false,
135
136
  note:
@@ -141,7 +142,7 @@ export function verificationResultTool(): RegisteredTool {
141
142
  }
142
143
 
143
144
  return {
144
- sessionId: args.sessionId,
145
+ reclaimSessionId: args.reclaimSessionId,
145
146
  status,
146
147
  ready: true,
147
148
  results,
package/src/old/client.ts CHANGED
@@ -47,9 +47,9 @@ export interface RegisterProviderBody {
47
47
  * controller's destructure + its `providerConfig` write, both of which
48
48
  * include it alongside `pageTitle`/`userAgent`. */
49
49
  stepsToFollow?: string
50
- /** Interception mechanism (`NONE` = standard replay flow, the default;
50
+ /** Interception mechanism (`NONE` disables interception;
51
51
  * `HAWKEYE`/`MSWJS`/`XHOOK` = JS-context interception; `CDP` = browser-
52
- * level). Column default is `HAWKEYE` when omitted entirely — unlike
52
+ * level). The column defaults to `HAWKEYE` when omitted — unlike
53
53
  * `stepsToFollow`, and so on. above, whether the register endpoint actually
54
54
  * PERSISTS this field is UNVERIFIED (no source access to confirm); sent
55
55
  * here on the chance it does, but the publish tool always follows up