@reclaimprotocol/agent 0.1.5 → 0.1.7

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 (117) hide show
  1. package/lib/authoring/backend.d.ts +0 -24
  2. package/lib/authoring/find.d.ts +3 -0
  3. package/lib/authoring/find.js +54 -0
  4. package/lib/authoring/index.d.ts +3 -3
  5. package/lib/authoring/index.js +4 -3
  6. package/lib/authoring/propose.d.ts +3 -0
  7. package/lib/authoring/propose.js +109 -0
  8. package/lib/authoring/replay.d.ts +10 -0
  9. package/lib/authoring/replay.js +34 -0
  10. package/lib/authoring/tools.d.ts +13 -6
  11. package/lib/authoring/tools.js +108 -293
  12. package/lib/authoring/util.d.ts +27 -0
  13. package/lib/authoring/util.js +21 -0
  14. package/lib/consts.d.ts +6 -1
  15. package/lib/consts.js +9 -2
  16. package/lib/mcp/server.d.ts +2 -41
  17. package/lib/mcp/server.js +6 -34
  18. package/lib/mcp/start.js +4 -2
  19. package/lib/mcp/tools/agent/analyze.js +3 -3
  20. package/lib/mcp/tools/agent/build.d.ts +4 -4
  21. package/lib/mcp/tools/agent/build.js +13 -15
  22. package/lib/mcp/tools/agent/capture.d.ts +13 -1
  23. package/lib/mcp/tools/agent/capture.js +18 -85
  24. package/lib/mcp/tools/agent/eval.d.ts +5 -2
  25. package/lib/mcp/tools/agent/eval.js +11 -121
  26. package/lib/mcp/tools/agent/find.d.ts +10 -3
  27. package/lib/mcp/tools/agent/find.js +25 -31
  28. package/lib/mcp/tools/agent/proof.d.ts +13 -3
  29. package/lib/mcp/tools/agent/proof.js +81 -132
  30. package/lib/mcp/tools/agent/propose.d.ts +10 -9
  31. package/lib/mcp/tools/agent/propose.js +87 -64
  32. package/lib/mcp/tools/agent/replay.d.ts +10 -3
  33. package/lib/mcp/tools/agent/replay.js +29 -36
  34. package/lib/mcp/tools/agent/session.d.ts +1 -3
  35. package/lib/mcp/tools/agent/session.js +5 -5
  36. package/lib/mcp/tools/agent/shared.d.ts +5 -3
  37. package/lib/mcp/tools/agent/shared.js +29 -19
  38. package/lib/old/client.d.ts +39 -0
  39. package/lib/old/client.js +18 -0
  40. package/lib/old/login-cdp.d.ts +24 -5
  41. package/lib/old/login-cdp.js +29 -15
  42. package/lib/old/sdk-client.d.ts +39 -0
  43. package/lib/old/sdk-client.js +85 -0
  44. package/lib/old/to-register.d.ts +69 -0
  45. package/lib/old/to-register.js +55 -2
  46. package/lib/old/tools/authenticate.js +3 -7
  47. package/lib/old/tools/build.d.ts +6 -1
  48. package/lib/old/tools/build.js +11 -1
  49. package/lib/old/tools/get-provider-info.d.ts +16 -0
  50. package/lib/old/tools/get-provider-info.js +73 -0
  51. package/lib/old/tools/get-version-config.d.ts +17 -0
  52. package/lib/old/tools/get-version-config.js +47 -0
  53. package/lib/old/tools/publish.js +208 -2
  54. package/lib/old/tools/update-provider-metadata.d.ts +16 -0
  55. package/lib/old/tools/update-provider-metadata.js +61 -0
  56. package/lib/proof/attestor.d.ts +6 -3
  57. package/lib/proof/attestor.js +10 -1
  58. package/lib/proof/debug-response-dump.d.ts +4 -0
  59. package/lib/proof/debug-response-dump.js +55 -0
  60. package/lib/proof/patch-domhandler.d.ts +1 -0
  61. package/lib/proof/patch-domhandler.js +59 -0
  62. package/lib/proof/zk-files.js +7 -6
  63. package/lib/provider/dom-xpath.d.ts +17 -0
  64. package/lib/provider/dom-xpath.js +187 -0
  65. package/lib/provider/draft.d.ts +30 -2
  66. package/lib/provider/draft.js +117 -28
  67. package/lib/provider/index.d.ts +4 -0
  68. package/lib/provider/index.js +7 -0
  69. package/lib/provider/injection-doc.js +6 -0
  70. package/lib/provider/matcher.d.ts +17 -20
  71. package/lib/provider/matcher.js +394 -280
  72. package/lib/provider/schema.d.ts +7 -5
  73. package/lib/tools/registry.d.ts +14 -0
  74. package/lib/tools/registry.js +32 -0
  75. package/package.json +9 -1
  76. package/skill/SKILL.builder.md +42 -23
  77. package/skill/SKILL.old.md +6 -2
  78. package/skill/builder-mode.md +1 -1
  79. package/skill/injection-api.md +117 -0
  80. package/src/authoring/backend.ts +98 -0
  81. package/src/authoring/index.ts +20 -0
  82. package/src/authoring/tools.ts +273 -0
  83. package/src/authoring/util.ts +42 -0
  84. package/src/consts.ts +15 -2
  85. package/src/mcp/server.ts +11 -78
  86. package/src/mcp/start.ts +4 -1
  87. package/src/mcp/tools/agent/analyze.ts +3 -3
  88. package/src/mcp/tools/agent/build.ts +13 -21
  89. package/src/mcp/tools/agent/capture.ts +31 -115
  90. package/src/mcp/tools/agent/eval.ts +19 -155
  91. package/src/mcp/tools/agent/find.ts +33 -36
  92. package/src/mcp/tools/agent/proof.ts +87 -160
  93. package/src/mcp/tools/agent/propose.ts +93 -85
  94. package/src/mcp/tools/agent/replay.ts +32 -42
  95. package/src/mcp/tools/agent/session.ts +4 -5
  96. package/src/mcp/tools/agent/shared.ts +152 -0
  97. package/src/old/client.ts +60 -0
  98. package/src/old/login-cdp.ts +29 -23
  99. package/src/old/sdk-client.ts +105 -0
  100. package/src/old/to-register.ts +102 -6
  101. package/src/old/tools/authenticate.ts +3 -11
  102. package/src/old/tools/build.ts +12 -0
  103. package/src/old/tools/get-provider-info.ts +91 -0
  104. package/src/old/tools/get-version-config.ts +56 -0
  105. package/src/old/tools/publish.ts +264 -1
  106. package/src/old/tools/update-provider-metadata.ts +72 -0
  107. package/src/proof/attestor.ts +16 -4
  108. package/src/proof/debug-response-dump.ts +70 -0
  109. package/src/proof/patch-domhandler.ts +68 -0
  110. package/src/proof/zk-files.ts +7 -6
  111. package/src/provider/dom-xpath.ts +225 -0
  112. package/src/provider/draft.ts +168 -35
  113. package/src/provider/index.ts +8 -0
  114. package/src/provider/injection-doc.ts +6 -0
  115. package/src/provider/matcher.ts +477 -357
  116. package/src/provider/schema.ts +7 -5
  117. package/src/tools/registry.ts +92 -0
@@ -1,9 +1,22 @@
1
+ import { type DraftStore, makeDraftStore } from '../../../authoring/index.ts'
1
2
  import { type CaptureSession, startCapture } from '../../../cdp/capture.ts'
2
3
  import { defineTool, type RegisteredTool } from '../../server.ts'
3
4
  import type { AttachState } from './attach.ts'
4
5
 
6
+ /**
7
+ * One capture == a session (network requests) + the tab CDP client it runs on +
8
+ * a draft store. Bundling the tab client is what lets the shared authoring
9
+ * tools treat a `captureId` as a full session (drive the browser AND read
10
+ * capture AND hold drafts) behind one id.
11
+ */
12
+ export interface CaptureEntry {
13
+ session: CaptureSession
14
+ tabClient: Awaited<ReturnType<AttachState['handles']['attachToTab']>>
15
+ drafts: DraftStore
16
+ }
17
+
5
18
  export interface CaptureStore {
6
- sessions: Map<string, CaptureSession>
19
+ sessions: Map<string, CaptureEntry>
7
20
  activeTab?: string
8
21
  }
9
22
 
@@ -23,8 +36,9 @@ export function captureTools(
23
36
  {
24
37
  name: 'start_capture',
25
38
  description:
26
- 'Begin recording network requests on the specified tab. ' +
27
- 'Returns a captureId.',
39
+ 'Begin recording network requests on the specified tab. '
40
+ + 'Returns a captureId — the session handle the authoring tools '
41
+ + '(navigate, propose_provider, run_proof, …) all take.',
28
42
  inputSchema: {
29
43
  type: 'object',
30
44
  properties: { tabId: { type: 'string' } },
@@ -35,7 +49,9 @@ export function captureTools(
35
49
  const attached = requireAttached()
36
50
  const tabClient = await attached.handles.attachToTab(tabId)
37
51
  const session = await startCapture(tabClient)
38
- captures.sessions.set(session.captureId, session)
52
+ captures.sessions.set(session.captureId, {
53
+ session, tabClient, drafts: makeDraftStore(),
54
+ })
39
55
  captures.activeTab = tabId
40
56
  return { captureId: session.captureId }
41
57
  },
@@ -45,8 +61,8 @@ export function captureTools(
45
61
  {
46
62
  name: 'stop_capture',
47
63
  description:
48
- 'Stop recording on the given capture. ' +
49
- 'Captured data remains queryable until reset_session.',
64
+ 'Stop recording on the given capture. '
65
+ + 'Captured data remains queryable until reset_session.',
50
66
  inputSchema: {
51
67
  type: 'object',
52
68
  properties: { captureId: { type: 'string' } },
@@ -54,104 +70,13 @@ export function captureTools(
54
70
  },
55
71
  },
56
72
  async({ captureId }) => {
57
- const session = captures.sessions.get(captureId)
58
- if(!session) {
73
+ const entry = captures.sessions.get(captureId)
74
+ if(!entry) {
59
75
  throw new Error(`no capture ${captureId}`)
60
76
  }
61
77
 
62
- await session.stop()
63
- return { stopped: true, requestCount: session.requests.size }
64
- },
65
- )
66
-
67
- const listTool = defineTool<{
68
- captureId: string
69
- filter?: {
70
- method?: string
71
- urlGlob?: string
72
- statusMin?: number
73
- statusMax?: number
74
- contentType?: string
75
- graphqlOp?: string
76
- }
77
- }>(
78
- {
79
- name: 'list_requests',
80
- description:
81
- 'List metadata for captured requests. ' +
82
- 'Filter by method, URL glob, status, content-type, ' +
83
- 'or graphqlOp. Returns only metadata, never bodies — ' +
84
- 'use get_request for those.',
85
- inputSchema: {
86
- type: 'object',
87
- properties: {
88
- captureId: { type: 'string' },
89
- filter: {
90
- type: 'object',
91
- properties: {
92
- method: { type: 'string' },
93
- urlGlob: { type: 'string' },
94
- statusMin: { type: 'number' },
95
- statusMax: { type: 'number' },
96
- contentType: { type: 'string' },
97
- graphqlOp: { type: 'string' },
98
- },
99
- },
100
- },
101
- required: ['captureId'],
102
- },
103
- },
104
- async({ captureId, filter }) => {
105
- const session = captures.sessions.get(captureId)
106
- if(!session) {
107
- throw new Error(`no capture ${captureId}`)
108
- }
109
-
110
- const out = []
111
- for(const r of session.requests.values()) {
112
- if(filter?.method && r.method !== filter.method.toUpperCase()) {
113
- continue
114
- }
115
-
116
- if(filter?.urlGlob && !globMatch(filter.urlGlob, r.url)) {
117
- continue
118
- }
119
-
120
- if(filter?.statusMin !== undefined && r.status < filter.statusMin) {
121
- continue
122
- }
123
-
124
- if(filter?.statusMax !== undefined && r.status > filter.statusMax) {
125
- continue
126
- }
127
-
128
- if(
129
- filter?.contentType &&
130
- !r.contentType.includes(filter.contentType)
131
- ) {
132
- continue
133
- }
134
-
135
- if(filter?.graphqlOp && r.graphqlOp !== filter.graphqlOp) {
136
- continue
137
- }
138
-
139
- const item: Record<string, unknown> = {
140
- requestId: r.requestId,
141
- method: r.method,
142
- url: r.url,
143
- status: r.status,
144
- contentType: r.contentType,
145
- size: r.size,
146
- }
147
- if(r.graphqlOp !== undefined) {
148
- item.graphqlOp = r.graphqlOp
149
- }
150
-
151
- out.push(item)
152
- }
153
-
154
- return { requests: out }
78
+ await entry.session.stop()
79
+ return { stopped: true, requestCount: entry.session.requests.size }
155
80
  },
156
81
  )
157
82
 
@@ -170,17 +95,17 @@ export function captureTools(
170
95
  },
171
96
  },
172
97
  async({ captureId, requestId }) => {
173
- const session = captures.sessions.get(captureId)
174
- if(!session) {
98
+ const entry = captures.sessions.get(captureId)
99
+ if(!entry) {
175
100
  throw new Error(`no capture ${captureId}`)
176
101
  }
177
102
 
178
- const r = session.requests.get(requestId)
103
+ const r = entry.session.requests.get(requestId)
179
104
  if(!r) {
180
105
  throw new Error(`no request ${requestId} in capture ${captureId}`)
181
106
  }
182
107
 
183
- const body = session.bodies.get(requestId)
108
+ const body = entry.session.bodies.get(requestId)
184
109
  if(!body.available) {
185
110
  return { ...r, bodyAvailable: false, reason: body.reason }
186
111
  }
@@ -189,14 +114,5 @@ export function captureTools(
189
114
  },
190
115
  )
191
116
 
192
- return [startTool, stopTool, listTool, getTool]
193
- }
194
-
195
- function globMatch(pattern: string, str: string): boolean {
196
- const re = new RegExp(
197
- '^' +
198
- pattern.replace(/[.+^${}()|[\]\\]/g, '\\$&').replace(/\*/g, '.*') +
199
- '$',
200
- )
201
- return re.test(str)
117
+ return [startTool, stopTool, getTool]
202
118
  }
@@ -2,160 +2,24 @@ import { listTabs } from '../../../cdp/tabs.ts'
2
2
  import { defineTool, type RegisteredTool } from '../../server.ts'
3
3
  import type { AttachState } from './attach.ts'
4
4
 
5
- export function browserTools(attachRef: {
5
+ /** The one browser tool that isn't session-bound: enumerate tabs so the caller
6
+ * can pick one to `start_capture` on. Navigate/eval/wait are the shared
7
+ * authoring tools (keyed by captureId — see `shared.ts`). */
8
+ export function listTabsTool(attachRef: {
6
9
  current?: AttachState
7
- }): RegisteredTool[] {
8
- const requireAttached = (): AttachState => {
9
- if(!attachRef.current) {
10
- throw new Error('Not attached. Call attach_chrome first.')
11
- }
12
-
13
- return attachRef.current
14
- }
15
-
16
- return [
17
- defineTool<Record<string, never>>(
18
- {
19
- name: 'list_tabs',
20
- description: 'List open Chrome tabs.',
21
- inputSchema: { type: 'object', properties: {} },
22
- },
23
- async() => {
24
- const attached = requireAttached()
25
- return { tabs: await listTabs(attached.port) }
26
- },
27
- ),
28
- defineTool<{ tabId: string, expression: string }>(
29
- {
30
- name: 'eval_in_page',
31
- description:
32
- 'Run a JS expression in a tab via Runtime.evaluate ' +
33
- 'and return the result. ' +
34
- 'Useful for reading visible values from the DOM.',
35
- inputSchema: {
36
- type: 'object',
37
- properties: {
38
- tabId: { type: 'string' },
39
- expression: { type: 'string' },
40
- },
41
- required: ['tabId', 'expression'],
42
- },
43
- },
44
- async({ tabId, expression }) => {
45
- const attached = requireAttached()
46
- const { Runtime } = await attached.handles.attachToTab(tabId)
47
- const result = await Runtime.evaluate({
48
- expression,
49
- returnByValue: true,
50
- })
51
- if(result.exceptionDetails) {
52
- return { error: result.exceptionDetails.text }
53
- }
54
-
55
- return { value: result.result.value }
56
- },
57
- ),
58
- defineTool<{ tabId: string, url: string, timeoutMs?: number }>(
59
- {
60
- name: 'navigate',
61
- description:
62
- 'Navigate a tab to a URL via Page.navigate, ' +
63
- 'waiting for the load event. ' +
64
- 'Use this to drive the browser yourself ' +
65
- 'instead of asking the developer to navigate. ' +
66
- 'Returns the final URL after navigation ' +
67
- '(which may differ from the requested URL due to redirects).',
68
- inputSchema: {
69
- type: 'object',
70
- properties: {
71
- tabId: { type: 'string' },
72
- url: { type: 'string' },
73
- timeoutMs: { type: 'number' },
74
- },
75
- required: ['tabId', 'url'],
76
- },
77
- },
78
- async({ tabId, url, timeoutMs }) => {
79
- const attached = requireAttached()
80
- const { Page, Runtime } = await attached.handles.attachToTab(tabId)
81
-
82
- await Page.enable()
83
- const loadPromise = new Promise<void>((resolve) => {
84
- Page.loadEventFired(() => resolve())
85
- })
86
- const nav = await Page.navigate({ url })
87
- if(nav.errorText) {
88
- return { ok: false, error: nav.errorText }
89
- }
90
-
91
- const t = timeoutMs ?? 30_000
92
- const raced = await Promise.race([
93
- loadPromise.then(() => 'loaded' as const),
94
- new Promise<'timeout'>(
95
- (resolve) => setTimeout(() => resolve('timeout'), t),
96
- ),
97
- ])
98
- const final = await Runtime.evaluate({
99
- expression: 'location.href',
100
- returnByValue: true,
101
- })
102
- return {
103
- ok: true,
104
- loadStatus: raced,
105
- finalUrl: final.result.value,
106
- }
107
- },
108
- ),
109
- defineTool<{
110
- tabId: string
111
- expression: string
112
- timeoutMs?: number
113
- pollMs?: number
114
- }>(
115
- {
116
- name: 'wait_for_page',
117
- description:
118
- 'Poll a JS expression in a tab until it ' +
119
- 'returns truthy (or timeout). ' +
120
- 'Use this for steps that require the ' +
121
- 'developer to do something in the browser — ' +
122
- 'most commonly waiting for them to finish logging in. ' +
123
- 'Pass an expression that ' +
124
- 'evaluates true once the desired state is reached, e.g. ' +
125
- '`!!document.querySelector' +
126
- '(\'summary[aria-label*="View profile"]\')`' +
127
- ' for GitHub post-login.',
128
- inputSchema: {
129
- type: 'object',
130
- properties: {
131
- tabId: { type: 'string' },
132
- expression: { type: 'string' },
133
- timeoutMs: { type: 'number' },
134
- pollMs: { type: 'number' },
135
- },
136
- required: ['tabId', 'expression'],
137
- },
138
- },
139
- async({ tabId, expression, timeoutMs, pollMs }) => {
140
- const attached = requireAttached()
141
- const { Runtime } = await attached.handles.attachToTab(tabId)
142
- const deadline = Date.now() + (timeoutMs ?? 120_000)
143
- const interval = pollMs ?? 1000
144
- while(Date.now() < deadline) {
145
- const r = await Runtime.evaluate({ expression, returnByValue: true })
146
- if(r.exceptionDetails) {
147
- return { matched: false, error: r.exceptionDetails.text }
148
- }
149
-
150
- if(r.result.value) {
151
- return { matched: true, value: r.result.value }
152
- }
153
-
154
- await new Promise((resolve) => setTimeout(resolve, interval))
155
- }
156
-
157
- return { matched: false, reason: 'timeout' }
158
- },
159
- ),
160
- ]
10
+ }): RegisteredTool {
11
+ return defineTool<Record<string, never>>(
12
+ {
13
+ name: 'list_tabs',
14
+ description: 'List open Chrome tabs.',
15
+ inputSchema: { type: 'object', properties: {} },
16
+ },
17
+ async() => {
18
+ if(!attachRef.current) {
19
+ throw new Error('Not attached. Call attach_chrome first.')
20
+ }
21
+
22
+ return { tabs: await listTabs(attachRef.current.port) }
23
+ },
24
+ )
161
25
  }
@@ -1,36 +1,37 @@
1
+ /**
2
+ * `find_requests_containing` — search captured responses for any encoding of a
3
+ * value and rank the requests that return it. Authored once as a plain
4
+ * {@link RegisteredTool}; the MCP and cloud loop each pass their own backend
5
+ * resolver (see authoring/tools.ts / shared.ts). Server-side: response bodies
6
+ * stay here, only metadata + a short snippet leave.
7
+ */
8
+ import {
9
+ objectSchema,
10
+ publicRequest,
11
+ type ResolveBackend,
12
+ } from '../../../authoring/util.ts'
1
13
  import { normalizeCandidates } from '../../../provider/normalize.ts'
2
- import { defineTool, type RegisteredTool } from '../../server.ts'
3
- import type { CaptureStore } from './capture.ts'
14
+ import { defineTool, type RegisteredTool } from '../../../tools/registry.ts'
4
15
 
5
- export function findTool(captures: CaptureStore): RegisteredTool {
6
- return defineTool<{ captureId: string, value: string, limit?: number }>(
16
+ export function findTool(resolveBackend: ResolveBackend): RegisteredTool {
17
+ return defineTool<Record<string, unknown>>(
7
18
  {
8
19
  name: 'find_requests_containing',
9
20
  description:
10
- 'Search captured responses for any encoding of `value`.' +
11
- 'Returns a ranked candidate list ' +
12
- 'with the JSON path (when applicable), score, and a short snippet.' +
13
- 'Server-side; bodies stay here.',
14
- inputSchema: {
15
- type: 'object',
16
- properties: {
17
- captureId: { type: 'string' },
18
- value: { type: 'string' },
19
- limit: { type: 'number' },
20
- },
21
- required: ['captureId', 'value'],
22
- },
21
+ 'Search captured responses for any encoding of `value` (raw, '
22
+ + 'URL/JSON/HTML-escaped, …). Returns a ranked candidate list with '
23
+ + 'the matched variant, score, and a short snippet — used to locate '
24
+ + 'the request that returns a target. Server-side; bodies stay here.',
25
+ inputSchema: objectSchema(
26
+ { value: { type: 'string' }, limit: { type: 'number' } },
27
+ ['value'],
28
+ ),
23
29
  },
24
- async({ captureId, value, limit }) => {
25
- const session = captures.sessions.get(captureId)
26
- if(!session) {
27
- throw new Error(`no capture ${captureId}`)
28
- }
29
-
30
- const variants = normalizeCandidates(value)
31
-
30
+ async(args) => {
31
+ const backend = resolveBackend(args)
32
+ const variants = normalizeCandidates(String(args.value))
32
33
  const ranked = []
33
- for(const r of session.requests.values()) {
34
+ for(const r of backend.capture.requests.values()) {
34
35
  if(!r.responseBody) {
35
36
  continue
36
37
  }
@@ -50,16 +51,11 @@ export function findTool(captures: CaptureStore): RegisteredTool {
50
51
  continue
51
52
  }
52
53
 
53
- // Score: prefer JSON responses + smaller bodies (likely API endpoints).
54
- const score =
55
- (r.contentType.includes('json') ? 10 : 0) +
56
- Math.max(0, 5 - Math.log10(Math.max(r.size, 1)))
54
+ // Prefer JSON responses + smaller bodies (likely API endpoints).
55
+ const score = (r.contentType.includes('json') ? 10 : 0)
56
+ + Math.max(0, 5 - Math.log10(Math.max(r.size, 1)))
57
57
  ranked.push({
58
- requestId: r.requestId,
59
- url: r.url,
60
- method: r.method,
61
- status: r.status,
62
- contentType: r.contentType,
58
+ ...publicRequest(r),
63
59
  matchedVariant: hit.variant,
64
60
  snippet: hit.snippet,
65
61
  score,
@@ -67,7 +63,8 @@ export function findTool(captures: CaptureStore): RegisteredTool {
67
63
  }
68
64
 
69
65
  ranked.sort((a, b) => b.score - a.score)
70
- return { candidates: ranked.slice(0, limit ?? 10) }
66
+ const limit = Number(args.limit) || 10
67
+ return { candidates: ranked.slice(0, limit) }
71
68
  },
72
69
  )
73
70
  }