@reclaimprotocol/agent 0.1.4 → 0.1.6

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 (126) hide show
  1. package/README.md +5 -5
  2. package/lib/authoring/backend.d.ts +71 -0
  3. package/lib/authoring/backend.js +24 -0
  4. package/lib/authoring/find.d.ts +3 -0
  5. package/lib/authoring/find.js +54 -0
  6. package/lib/authoring/index.d.ts +4 -0
  7. package/lib/authoring/index.js +6 -0
  8. package/lib/authoring/propose.d.ts +3 -0
  9. package/lib/authoring/propose.js +109 -0
  10. package/lib/authoring/replay.d.ts +10 -0
  11. package/lib/authoring/replay.js +34 -0
  12. package/lib/authoring/tools.d.ts +16 -0
  13. package/lib/authoring/tools.js +193 -0
  14. package/lib/authoring/util.d.ts +27 -0
  15. package/lib/authoring/util.js +21 -0
  16. package/lib/cdp/capture-engine.js +1 -1
  17. package/lib/consts.d.ts +6 -1
  18. package/lib/consts.js +9 -2
  19. package/lib/mcp/api/operations.gen.js +46 -18
  20. package/lib/mcp/server.d.ts +2 -25
  21. package/lib/mcp/server.js +4 -17
  22. package/lib/mcp/start.js +4 -2
  23. package/lib/mcp/tools/agent/analyze.js +3 -3
  24. package/lib/mcp/tools/agent/build.d.ts +4 -4
  25. package/lib/mcp/tools/agent/build.js +13 -15
  26. package/lib/mcp/tools/agent/capture.d.ts +13 -1
  27. package/lib/mcp/tools/agent/capture.js +18 -85
  28. package/lib/mcp/tools/agent/eval.d.ts +5 -2
  29. package/lib/mcp/tools/agent/eval.js +11 -121
  30. package/lib/mcp/tools/agent/find.d.ts +10 -3
  31. package/lib/mcp/tools/agent/find.js +25 -31
  32. package/lib/mcp/tools/agent/proof.d.ts +13 -3
  33. package/lib/mcp/tools/agent/proof.js +81 -132
  34. package/lib/mcp/tools/agent/propose.d.ts +10 -9
  35. package/lib/mcp/tools/agent/propose.js +87 -64
  36. package/lib/mcp/tools/agent/publish.js +2 -2
  37. package/lib/mcp/tools/agent/replay.d.ts +10 -3
  38. package/lib/mcp/tools/agent/replay.js +29 -36
  39. package/lib/mcp/tools/agent/session.d.ts +1 -3
  40. package/lib/mcp/tools/agent/session.js +5 -5
  41. package/lib/mcp/tools/agent/shared.d.ts +8 -0
  42. package/lib/mcp/tools/agent/shared.js +125 -0
  43. package/lib/old/client.d.ts +35 -0
  44. package/lib/old/client.js +18 -0
  45. package/lib/old/login-cdp.d.ts +24 -0
  46. package/lib/old/login-cdp.js +28 -3
  47. package/lib/old/sdk-client.d.ts +39 -0
  48. package/lib/old/sdk-client.js +85 -0
  49. package/lib/old/to-register.d.ts +69 -0
  50. package/lib/old/to-register.js +52 -2
  51. package/lib/old/tools/build.d.ts +6 -1
  52. package/lib/old/tools/build.js +11 -1
  53. package/lib/old/tools/get-provider-info.d.ts +16 -0
  54. package/lib/old/tools/get-provider-info.js +73 -0
  55. package/lib/old/tools/get-version-config.d.ts +17 -0
  56. package/lib/old/tools/get-version-config.js +47 -0
  57. package/lib/old/tools/publish.js +189 -2
  58. package/lib/old/tools/update-provider-metadata.d.ts +16 -0
  59. package/lib/old/tools/update-provider-metadata.js +61 -0
  60. package/lib/proof/attestor.d.ts +6 -3
  61. package/lib/proof/attestor.js +10 -1
  62. package/lib/proof/debug-response-dump.d.ts +4 -0
  63. package/lib/proof/debug-response-dump.js +55 -0
  64. package/lib/proof/patch-domhandler.d.ts +1 -0
  65. package/lib/proof/patch-domhandler.js +59 -0
  66. package/lib/proof/proof-core.d.ts +1 -1
  67. package/lib/proof/proof-core.js +1 -1
  68. package/lib/proof/zk-files.js +7 -6
  69. package/lib/provider/dom-xpath.d.ts +17 -0
  70. package/lib/provider/dom-xpath.js +187 -0
  71. package/lib/provider/draft.d.ts +30 -2
  72. package/lib/provider/draft.js +117 -28
  73. package/lib/provider/index.d.ts +4 -0
  74. package/lib/provider/index.js +7 -0
  75. package/lib/provider/injection-doc.js +6 -0
  76. package/lib/provider/matcher.d.ts +17 -20
  77. package/lib/provider/matcher.js +394 -280
  78. package/lib/provider/schema.d.ts +7 -5
  79. package/lib/tools/registry.d.ts +50 -0
  80. package/lib/tools/registry.js +49 -0
  81. package/package.json +9 -1
  82. package/skill/SKILL.builder.md +55 -32
  83. package/skill/SKILL.old.md +20 -12
  84. package/skill/builder-mode.md +1 -1
  85. package/skill/injection-api.md +117 -0
  86. package/src/authoring/backend.ts +98 -0
  87. package/src/authoring/index.ts +20 -0
  88. package/src/authoring/tools.ts +273 -0
  89. package/src/authoring/util.ts +42 -0
  90. package/src/cdp/capture-engine.ts +1 -1
  91. package/src/consts.ts +15 -2
  92. package/src/mcp/api/operations.gen.ts +46 -18
  93. package/src/mcp/server.ts +9 -39
  94. package/src/mcp/start.ts +4 -1
  95. package/src/mcp/tools/agent/analyze.ts +3 -3
  96. package/src/mcp/tools/agent/build.ts +13 -21
  97. package/src/mcp/tools/agent/capture.ts +31 -115
  98. package/src/mcp/tools/agent/eval.ts +19 -155
  99. package/src/mcp/tools/agent/find.ts +33 -36
  100. package/src/mcp/tools/agent/proof.ts +87 -160
  101. package/src/mcp/tools/agent/propose.ts +93 -85
  102. package/src/mcp/tools/agent/publish.ts +2 -2
  103. package/src/mcp/tools/agent/replay.ts +32 -42
  104. package/src/mcp/tools/agent/session.ts +4 -5
  105. package/src/mcp/tools/agent/shared.ts +152 -0
  106. package/src/old/client.ts +56 -0
  107. package/src/old/login-cdp.ts +28 -3
  108. package/src/old/sdk-client.ts +105 -0
  109. package/src/old/to-register.ts +98 -6
  110. package/src/old/tools/build.ts +12 -0
  111. package/src/old/tools/get-provider-info.ts +91 -0
  112. package/src/old/tools/get-version-config.ts +56 -0
  113. package/src/old/tools/publish.ts +244 -1
  114. package/src/old/tools/update-provider-metadata.ts +72 -0
  115. package/src/proof/attestor.ts +16 -4
  116. package/src/proof/debug-response-dump.ts +70 -0
  117. package/src/proof/patch-domhandler.ts +68 -0
  118. package/src/proof/proof-core.ts +1 -1
  119. package/src/proof/zk-files.ts +7 -6
  120. package/src/provider/dom-xpath.ts +225 -0
  121. package/src/provider/draft.ts +168 -35
  122. package/src/provider/index.ts +8 -0
  123. package/src/provider/injection-doc.ts +6 -0
  124. package/src/provider/matcher.ts +477 -357
  125. package/src/provider/schema.ts +7 -5
  126. package/src/tools/registry.ts +92 -0
@@ -0,0 +1,98 @@
1
+ /**
2
+ * Backend seam for the authoring tools. The tool LOGIC (drive the browser →
3
+ * capture → propose → replay → prove) is written once against this interface;
4
+ * each environment supplies a backend:
5
+ * - local (MCP): a Chrome tab over CDP + the in-process attestor SDK.
6
+ * - remote (cloud): the user's Popcorn browser over CDP + the TEE prover.
7
+ *
8
+ * One backend instance == one live session (a page + its capture). Multi
9
+ * session falls out of instancing: the MCP resolves a backend per tab/capture,
10
+ * the cloud one per run — so the shared tools never carry a session id and hold
11
+ * no cross-session state.
12
+ */
13
+ import { randomUUID } from 'node:crypto'
14
+ import type { CaptureSession } from '../cdp/capture-engine.ts'
15
+ import type { Extraction } from '../provider/draft.ts'
16
+ import type { ReclaimProvider, SecretParamRef } from '../provider/schema.ts'
17
+
18
+ /** Secret request params (cookie/auth headers), materialized only for the
19
+ * replay/prove call — never surfaced in tool output. */
20
+ export interface SecretParams {
21
+ headers: Record<string, string>
22
+ }
23
+
24
+ /** A drafted recipe held server-side so the model only handles an opaque
25
+ * `draftId` — the provider is secret-free but the captured secrets never enter
26
+ * the transcript. Shared by propose → replay → run_proof. */
27
+ export interface Draft {
28
+ provider: ReclaimProvider
29
+ secretRefs: SecretParamRef[]
30
+ /** Real secret header values (cookie/auth) for replay/prove — never returned
31
+ * to the model. */
32
+ secrets: Record<string, string>
33
+ /** The captured request this draft was proposed from. */
34
+ requestId: string
35
+ /** One entry per extracted value (value + its redaction), so replay can
36
+ * validate each against its own selector. */
37
+ extractions: Extraction[]
38
+ }
39
+
40
+ /** Per-session draft store: `propose` puts, `replay`/`run_proof` get by id. */
41
+ export interface DraftStore {
42
+ put(draft: Draft): string
43
+ get(id: string): Draft | undefined
44
+ }
45
+
46
+ export function makeDraftStore(): DraftStore {
47
+ const map = new Map<string, Draft>()
48
+ return {
49
+ put: (draft) => {
50
+ const id = `draft_${randomUUID()}`
51
+ map.set(id, draft)
52
+ return id
53
+ },
54
+ get: (id) => map.get(id),
55
+ }
56
+ }
57
+
58
+ /** Options for a proof run. `ownerAddress` selects the local signing credential
59
+ * and `attestorUrl` overrides the attestor endpoint (both MCP-only); the
60
+ * remote TEE backend ignores them (it signs in-enclave, own attestor). */
61
+ export interface ProveOptions {
62
+ ownerAddress?: string
63
+ attestorUrl?: string
64
+ }
65
+
66
+ /**
67
+ * Everything the shared authoring tools need from one live session. Browser ops
68
+ * go through `sendCdp` (+ `waitForEvent` for load-style waits); capture reads
69
+ * use `capture`; proof/replay are whole operations because they genuinely
70
+ * differ (local attestor + live re-fetch vs remote TEE) — each backend shapes
71
+ * its own result, passed straight through as the tool's output.
72
+ */
73
+ export interface AuthoringBackend {
74
+ /** Send a raw CDP command scoped to this session's page. */
75
+ sendCdp(method: string, params?: Record<string, unknown>): Promise<unknown>
76
+ /** Resolve once `method` (a CDP event) fires on this session's page, or
77
+ * after `timeoutMs`. Used for `Page.loadEventFired`-style waits. */
78
+ waitForEvent(method: string, timeoutMs: number): Promise<'fired' | 'timeout'>
79
+ /** This session's captured requests (the shared capture engine). */
80
+ readonly capture: CaptureSession
81
+ /** This session's drafts (propose → replay/run_proof). */
82
+ readonly drafts: DraftStore
83
+ /** Prove a drafted recipe. Local: attestor SDK (rich claim view). Remote: TEE
84
+ * `/reclaim/prove` (lean, secret-free summary). Returns the environment's
85
+ * display shape. A backend may record the proven recipe internally (e.g.
86
+ * the cloud publish step) — that side effect is the backend's, not ours. */
87
+ prove(draft: Draft, opts?: ProveOptions): Promise<unknown>
88
+ /**
89
+ * Validate/replay a drafted recipe before proving. With `withoutSecrets` it
90
+ * is the AUTH-BOUND check: re-issue the request stripped of the captured
91
+ * secrets (local: a Node re-fetch without them; remote: an anonymous `fetch`
92
+ * in the Popcorn page, `credentials: 'omit'`) and report whether the values
93
+ * are STILL extractable — i.e. the endpoint is public. Without it, validate
94
+ * extraction for the authenticated request. The shape is environment-specific
95
+ * and passed straight through as the tool's result.
96
+ */
97
+ replay(draft: Draft, opts?: { withoutSecrets?: boolean }): Promise<unknown>
98
+ }
@@ -0,0 +1,20 @@
1
+ // Shared authoring tools, backend-injectable. Consumed by the MCP server
2
+ // (local Chrome + attestor backend) and the cloud loop (Popcorn + TEE backend).
3
+ export type {
4
+ AuthoringBackend,
5
+ Draft,
6
+ DraftStore,
7
+ ProveOptions,
8
+ SecretParams,
9
+ } from './backend.ts'
10
+ export { makeDraftStore } from './backend.ts'
11
+ export { authoringTools } from './tools.ts'
12
+ // The shared tool primitive — re-exported so consumers (the cloud loop) get the
13
+ // RegisteredTool type/`defineTool`/`extendTool` without importing the MCP
14
+ // subsystem.
15
+ export {
16
+ defineTool,
17
+ extendTool,
18
+ type RegisteredTool,
19
+ type ToolDefinition,
20
+ } from '../tools/registry.ts'
@@ -0,0 +1,273 @@
1
+ /**
2
+ * The authoring tools, authored ONCE — each directly with the shared
3
+ * `defineTool` primitive (no parallel tool spec). `authoringTools(resolve)`
4
+ * returns them as plain {@link RegisteredTool}s; the MCP (local Chrome +
5
+ * attestor) and the cloud loop (Popcorn + TEE) each pass their own backend
6
+ * resolver, so navigate/inspect/propose/replay/prove logic lives in one place.
7
+ *
8
+ * The resolver hands back the backend per call — the MCP keys it by `captureId`
9
+ * (multi-session), the cloud returns its one run backend. A host that needs
10
+ * extra input schema (the MCP's `captureId` + rich `run_proof` contract) MAPS
11
+ * these tools through `extendTool` at registration; the tools stay plain.
12
+ *
13
+ * Secret guarantee: captured cookie/auth values never enter a tool result. The
14
+ * draft (recipe + secrets) is held server-side; the model only sees an opaque
15
+ * `draftId` and the secret-free provider.
16
+ */
17
+ import { findTool } from '../mcp/tools/agent/find.ts'
18
+ import { proposeTool } from '../mcp/tools/agent/propose.ts'
19
+ import { replayTool } from '../mcp/tools/agent/replay.ts'
20
+ import type { CapturedRequest } from '../provider/schema.ts'
21
+ import { defineTool, type RegisteredTool } from '../tools/registry.ts'
22
+ import type { AuthoringBackend } from './backend.ts'
23
+ import { objectSchema, publicRequest, type ResolveBackend } from './util.ts'
24
+
25
+ const sleep = (ms: number) => new Promise<void>((r) => setTimeout(r, ms))
26
+
27
+ interface RequestFilter {
28
+ method?: string
29
+ urlGlob?: string
30
+ statusMin?: number
31
+ statusMax?: number
32
+ contentType?: string
33
+ graphqlOp?: string
34
+ }
35
+
36
+ function matchesFilter(r: CapturedRequest, f: RequestFilter): boolean {
37
+ if(f.method && r.method !== f.method.toUpperCase()) {
38
+ return false
39
+ }
40
+
41
+ if(f.urlGlob && !globMatch(f.urlGlob, r.url)) {
42
+ return false
43
+ }
44
+
45
+ if(f.statusMin !== undefined && r.status < f.statusMin) {
46
+ return false
47
+ }
48
+
49
+ if(f.statusMax !== undefined && r.status > f.statusMax) {
50
+ return false
51
+ }
52
+
53
+ if(f.contentType && !r.contentType.includes(f.contentType)) {
54
+ return false
55
+ }
56
+
57
+ if(f.graphqlOp && r.graphqlOp !== f.graphqlOp) {
58
+ return false
59
+ }
60
+
61
+ return true
62
+ }
63
+
64
+ function globMatch(pattern: string, str: string): boolean {
65
+ const re = new RegExp(
66
+ '^'
67
+ + pattern.replace(/[.+^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*')
68
+ + '$',
69
+ )
70
+ return re.test(str)
71
+ }
72
+
73
+ interface EvalResult {
74
+ result?: { value?: unknown }
75
+ exceptionDetails?: { exception?: { description?: string }, text?: string }
76
+ }
77
+
78
+ async function evalInPage(
79
+ backend: AuthoringBackend,
80
+ expression: string,
81
+ ): Promise<{ value?: unknown, error?: string }> {
82
+ const res = (await backend.sendCdp('Runtime.evaluate', {
83
+ expression, returnByValue: true, awaitPromise: true,
84
+ })) as EvalResult
85
+ if(res.exceptionDetails) {
86
+ return {
87
+ error: res.exceptionDetails.exception?.description
88
+ ?? res.exceptionDetails.text
89
+ ?? 'evaluation error',
90
+ }
91
+ }
92
+
93
+ return { value: res.result?.value }
94
+ }
95
+
96
+ /**
97
+ * The authoring tools — navigate/inspect authored inline here; find/propose/
98
+ * replay live in their own files ({@link findTool}/{@link proposeTool}/
99
+ * {@link replayTool}) and are composed in below. Each is a plain `defineTool`
100
+ * primitive, so there is one canonical `RegisteredTool` per tool and no
101
+ * parallel tool shape. Every handler resolves its session backend via
102
+ * `resolveBackend(args)`: the cloud passes `() => backend` (one backend per
103
+ * run); the MCP passes an `args.captureId`-keyed resolver. Hosts that need
104
+ * extra input schema (the MCP's `captureId` session handle + the
105
+ * credential-aware `run_proof` contract) MAP these tools through `extendTool`
106
+ * (see mcp/tools/agent/shared.ts) — the tools themselves stay plain, and args
107
+ * parse loosely so the extra fields still reach the resolver/handler.
108
+ */
109
+ export function authoringTools(
110
+ resolveBackend: ResolveBackend,
111
+ ): RegisteredTool[] {
112
+ return [
113
+ defineTool<Record<string, unknown>>(
114
+ {
115
+ name: 'navigate',
116
+ description: 'Navigate the browser tab to a URL, waiting for load.',
117
+ inputSchema: objectSchema(
118
+ { url: { type: 'string' }, timeoutMs: { type: 'number' } },
119
+ ['url'],
120
+ ),
121
+ },
122
+ async(args) => {
123
+ const backend = resolveBackend(args)
124
+ const timeoutMs = Number(args.timeoutMs) || 30_000
125
+ await backend.sendCdp('Page.enable')
126
+ // Start listening BEFORE navigating so the load event isn't missed.
127
+ const load = backend.waitForEvent('Page.loadEventFired', timeoutMs)
128
+ const nav = (await backend.sendCdp(
129
+ 'Page.navigate', { url: String(args.url) },
130
+ )) as { errorText?: string }
131
+ if(nav.errorText) {
132
+ return { ok: false, error: nav.errorText }
133
+ }
134
+
135
+ const loadStatus = await load
136
+ const final = await evalInPage(backend, 'location.href')
137
+ return { ok: true, loadStatus, finalUrl: final.value }
138
+ },
139
+ ),
140
+ defineTool<Record<string, unknown>>(
141
+ {
142
+ name: 'eval_in_page',
143
+ description:
144
+ 'Evaluate a JavaScript expression in the page and return its value '
145
+ + '(use to read the displayed value, check login state, etc.).',
146
+ inputSchema: objectSchema(
147
+ { expression: { type: 'string' } }, ['expression'],
148
+ ),
149
+ },
150
+ (args) => evalInPage(resolveBackend(args), String(args.expression)),
151
+ ),
152
+ defineTool<Record<string, unknown>>(
153
+ {
154
+ name: 'wait_for_page',
155
+ description:
156
+ 'Poll a JavaScript expression until it is truthy or the timeout '
157
+ + 'elapses (e.g. wait for the user to finish logging in).',
158
+ inputSchema: objectSchema(
159
+ { expression: { type: 'string' }, timeoutMs: { type: 'number' } },
160
+ ['expression'],
161
+ ),
162
+ },
163
+ async(args) => {
164
+ const backend = resolveBackend(args)
165
+ const expr = String(args.expression)
166
+ const deadline = Date.now() + (Number(args.timeoutMs) || 180_000)
167
+ while(Date.now() < deadline) {
168
+ const { value } = await evalInPage(backend, expr)
169
+ if(value) {
170
+ return { ready: true }
171
+ }
172
+
173
+ await sleep(1000)
174
+ }
175
+
176
+ return { ready: false }
177
+ },
178
+ ),
179
+ defineTool<Record<string, unknown>>(
180
+ {
181
+ name: 'get_cookies',
182
+ description:
183
+ 'List the NAMES of cookies currently set for the page (values are '
184
+ + 'NEVER returned). The most reliable, language- and DOM-independent '
185
+ + 'login signal: after the user logs in, an auth/session cookie '
186
+ + 'appears (e.g. session/sid/token/auth/jwt/_user/logged_in). '
187
+ + 'Includes httpOnly cookies the page JS cannot see.',
188
+ inputSchema: objectSchema({}),
189
+ },
190
+ async(args) => {
191
+ const backend = resolveBackend(args)
192
+ const res = (await backend.sendCdp('Network.getCookies')) as {
193
+ cookies?: {
194
+ name: string
195
+ domain?: string
196
+ session?: boolean
197
+ httpOnly?: boolean
198
+ }[]
199
+ }
200
+ return (res.cookies ?? []).map((c) => ({
201
+ name: c.name,
202
+ ...(c.domain ? { domain: c.domain } : {}),
203
+ ...(c.session ? { session: true } : {}),
204
+ ...(c.httpOnly ? { httpOnly: true } : {}),
205
+ }))
206
+ },
207
+ ),
208
+ defineTool<Record<string, unknown>>(
209
+ {
210
+ name: 'list_requests',
211
+ description:
212
+ 'List captured network requests (method/url/status/size), newest '
213
+ + 'last. Optionally filter by method / urlGlob / status range / '
214
+ + 'contentType / graphqlOp. Metadata only — secrets never included.',
215
+ inputSchema: objectSchema({
216
+ filter: {
217
+ type: 'object',
218
+ properties: {
219
+ method: { type: 'string' },
220
+ urlGlob: { type: 'string' },
221
+ statusMin: { type: 'number' },
222
+ statusMax: { type: 'number' },
223
+ contentType: { type: 'string' },
224
+ graphqlOp: { type: 'string' },
225
+ },
226
+ },
227
+ }),
228
+ },
229
+ async(args) => {
230
+ const backend = resolveBackend(args)
231
+ const filter = (args.filter ?? {}) as RequestFilter
232
+ return [...backend.capture.requests.values()]
233
+ .filter((r) => matchesFilter(r, filter))
234
+ .map(publicRequest)
235
+ },
236
+ ),
237
+ findTool(resolveBackend),
238
+ proposeTool(resolveBackend),
239
+ replayTool(resolveBackend),
240
+ defineTool<Record<string, unknown>>(
241
+ {
242
+ name: 'run_proof',
243
+ description:
244
+ 'Prove the drafted recipe (zkTLS). Only call after replay_request '
245
+ + 'looks right. Returns the proof result.',
246
+ inputSchema: objectSchema({ draftId: { type: 'string' } }, ['draftId']),
247
+ },
248
+ // The backend shapes its own result: the cloud returns a lean,
249
+ // secret-free summary (and records the proven recipe for publish); the
250
+ // MCP persists the claim + returns the rich developer view. The MCP adds
251
+ // `ownerAddress`/`attestorUrl` to the schema via `extendTool`; the
252
+ // handler reads them from args (loose parse keeps them).
253
+ async(args) => {
254
+ const backend = resolveBackend(args)
255
+ const draft = backend.drafts.get(String(args.draftId))
256
+ if(!draft) {
257
+ throw new Error(`no draft ${String(args.draftId)}`)
258
+ }
259
+
260
+ const opts: { ownerAddress?: string, attestorUrl?: string } = {}
261
+ if(typeof args.ownerAddress === 'string') {
262
+ opts.ownerAddress = args.ownerAddress
263
+ }
264
+
265
+ if(typeof args.attestorUrl === 'string') {
266
+ opts.attestorUrl = args.attestorUrl
267
+ }
268
+
269
+ return backend.prove(draft, opts)
270
+ },
271
+ ),
272
+ ]
273
+ }
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Small shared helpers for the authoring tools (see tools.ts). Kept separate so
3
+ * the per-tool files (find.ts, propose.ts, replay.ts) and the inline tools in
4
+ * tools.ts share one copy without a circular import.
5
+ */
6
+ import type { JSONSchema } from 'zod/v4/core'
7
+ import type { CapturedRequest } from '../provider/schema.ts'
8
+ import type { AuthoringBackend } from './backend.ts'
9
+
10
+ /**
11
+ * Resolves the session backend for a tool call. The MCP keys it by
12
+ * `args.captureId` (multi-session); the cloud loop returns its one run backend.
13
+ * Each authoring tool closes over this so the same tool works for both hosts.
14
+ */
15
+ export type ResolveBackend =
16
+ (args: Record<string, unknown>) => AuthoringBackend
17
+
18
+ /** Build an `object` JSON Schema for a tool input — a small literal shortener,
19
+ * not a tool abstraction. */
20
+ export function objectSchema(
21
+ properties: Record<string, unknown>,
22
+ required?: string[],
23
+ ): JSONSchema.ObjectSchema {
24
+ return {
25
+ type: 'object',
26
+ properties,
27
+ ...(required?.length ? { required } : {}),
28
+ } as unknown as JSONSchema.ObjectSchema
29
+ }
30
+
31
+ /** Secret-free projection of a captured request for tool output. */
32
+ export function publicRequest(r: CapturedRequest) {
33
+ return {
34
+ requestId: r.requestId,
35
+ method: r.method,
36
+ url: r.url,
37
+ status: r.status,
38
+ contentType: r.contentType,
39
+ size: r.size,
40
+ ...(r.graphqlOp ? { graphqlOp: r.graphqlOp } : {}),
41
+ }
42
+ }
@@ -75,7 +75,7 @@ export function isStaticAsset(url: string): boolean {
75
75
  try {
76
76
  path = new URL(url).pathname
77
77
  } catch{
78
- path = url.split(/[?#]/)[0] ?? url
78
+ path = url.split(/[?#]/)[0] || url
79
79
  }
80
80
 
81
81
  // Extension of the last path segment only, so `/v1.2/data` isn't misread.
package/src/consts.ts CHANGED
@@ -19,6 +19,7 @@ import { DEFAULT_BASE_URL } from '@reclaimprotocol/client'
19
19
  import { homedir } from 'node:os'
20
20
  import { join } from 'node:path'
21
21
  import { DEFAULT_OLD_API_URL, DEFAULT_OLD_LOGS_URL } from './old/client.ts'
22
+ import { DEFAULT_SDK_API_URL } from './old/sdk-client.ts'
22
23
 
23
24
  // ---------------------------------------------------------------------------
24
25
  // Default / fallback literals (used only as env fallbacks).
@@ -30,9 +31,14 @@ export const FALLBACK_ATTESTOR_URL = 'wss://attestor.reclaimprotocol.org:444/ws'
30
31
  /** The devtools dashboard login page — fallback for `RECLAIM_OLD_LOGIN_URL`. */
31
32
  export const DEFAULT_LOGIN_URL = 'https://dev.reclaimprotocol.org'
32
33
 
33
- // Re-export the two non-env-fallback defaults so callers can keep importing
34
+ // Re-export the non-env-fallback defaults so callers can keep importing
34
35
  // their URL defaults from a single place if they wish.
35
- export { DEFAULT_BASE_URL, DEFAULT_OLD_API_URL, DEFAULT_OLD_LOGS_URL }
36
+ export {
37
+ DEFAULT_BASE_URL,
38
+ DEFAULT_OLD_API_URL,
39
+ DEFAULT_OLD_LOGS_URL,
40
+ DEFAULT_SDK_API_URL,
41
+ }
36
42
 
37
43
  // ---------------------------------------------------------------------------
38
44
  // Logging.
@@ -101,6 +107,13 @@ export function oldLogsUrl(): string {
101
107
  return process.env.RECLAIM_OLD_LOGS_URL || DEFAULT_OLD_LOGS_URL
102
108
  }
103
109
 
110
+ /** Public production SDK-backend base URL — the service the verification
111
+ * SDK/attestor itself reads a provider's recipe from at runtime. A
112
+ * DIFFERENT host from the old-devtools dashboard backend (`oldApiUrl`). */
113
+ export function sdkApiUrl(): string {
114
+ return process.env.RECLAIM_SDK_API_URL || DEFAULT_SDK_API_URL
115
+ }
116
+
104
117
  /** Builder backend base URL. Shared by mcp/start and the verification tool. */
105
118
  export function apiUrl(): string {
106
119
  return process.env.RECLAIM_API_URL || DEFAULT_BASE_URL