@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
@@ -2,8 +2,7 @@ import { mkdirSync, writeFileSync } from 'node:fs';
2
2
  import { join } from 'node:path';
3
3
  import { agentTraceEnabled } from "../../../consts.js";
4
4
  import { reclaimHomeDirPath } from "../../../paths.js";
5
- import { runAttestorProof, tailLogs, warnsAndErrors, } from "../../../proof/attestor.js";
6
- import { defineTool } from "../../server.js";
5
+ import { tailLogs, warnsAndErrors, } from "../../../proof/attestor.js";
7
6
  function claimsDir() {
8
7
  return join(reclaimHomeDirPath(), 'claims');
9
8
  }
@@ -73,138 +72,88 @@ function isLikelyByteArray(v) {
73
72
  }
74
73
  return true;
75
74
  }
76
- export function proofTool(secrets) {
77
- return defineTool({
78
- name: 'run_proof',
79
- description: 'Run a full zkTLS proof through the Reclaim attestor SDK. ' +
80
- 'Use only after replay_request ' +
81
- 'returns matchesOriginal=true — attestor calls are expensive. ' +
82
- 'Writes the full claim to ' +
83
- '~/.reclaim/claims/<timestamp>.json and ' +
84
- 'returns identifier + extractedParameters + ' +
85
- 'claimPath + a prettified `claim` field ' +
86
- '(request dropped, byte arrays as base64) suitable ' +
87
- 'for displaying directly to the dev. ' +
88
- 'When the MCP server is started with ' +
89
- '`RECLAIM_AGENT_TRACE=1`, ALSO writes ' +
90
- 'a per-call diagnostic dump (input provider, ' +
91
- 'sentParams the SDK saw post-substitution, ' +
92
- 'FULL attestor log stream, result) to ' +
93
- '~/.reclaim/proof-runs/<timestamp>.json and ' +
94
- 'returns the path as `runLogPath`. ' +
95
- '`attestorUrl` overrides the default attestor URL only when the dev ' +
96
- 'supplies a specific ' +
97
- 'alternative — do NOT invent backup URLs ' +
98
- '(there is one live public attestor right now; ' +
99
- 'surface real errors to the dev instead of guessing). ' +
100
- '`ownerAddress` (the credential to sign with) is REQUIRED — ' +
101
- 'call resolve_owner_key first to obtain it, then pass it here. ' +
102
- 'errorKind="missing-owner-key" means that address has no known ' +
103
- 'private key; re-run resolve_owner_key or issue/import a credential.',
104
- inputSchema: {
105
- type: 'object',
106
- properties: {
107
- provider: { type: 'object', properties: {} },
108
- secretParamsRef: { type: 'string' },
109
- attestorUrl: { type: 'string' },
110
- ownerAddress: {
111
- type: 'string',
112
- pattern: '^0x[0-9a-fA-F]{40}$',
113
- description: 'ETH address of the credential to sign the claim with. ' +
114
- 'Get it from resolve_owner_key first. The private key is ' +
115
- 'resolved locally (cache / env / .env) — never passed here.',
116
- },
117
- },
118
- required: ['provider', 'ownerAddress'],
119
- },
120
- }, async ({ provider, secretParamsRef, attestorUrl, ownerAddress }) => {
121
- let resolved = {};
122
- if (secretParamsRef !== undefined) {
123
- const r = secrets.resolve(secretParamsRef);
124
- if (!r) {
125
- throw new Error(`unknown secretParamsRef "${secretParamsRef}" — ` +
126
- 'the MCP server has restarted or reset_session was called. ' +
127
- 'Re-run propose_provider to mint a fresh ref.');
128
- }
129
- resolved = r;
130
- }
131
- const result = await runAttestorProof(provider, resolved, { attestorUrl, ownerAddress });
132
- // Persist the full claim to disk for the dev to inspect/share.
133
- let claimPath;
134
- if (result.proof !== undefined && result.proof !== null) {
135
- try {
136
- mkdirSync(claimsDir(), { recursive: true });
137
- claimPath = join(claimsDir(), `${Date.now()}.json`);
138
- writeFileSync(claimPath, JSON.stringify(result.proof, null, 2), 'utf8');
139
- }
140
- catch {
141
- // Persistence is best-effort; don't fail the proof for it.
142
- claimPath = undefined;
143
- }
144
- }
145
- const out = {
146
- verified: result.verified,
147
- extractedValue: result.extractedValue,
148
- extractedParameters: result.extractedParameters,
149
- };
150
- if (result.identifier !== undefined) {
151
- out.identifier = result.identifier;
152
- }
153
- if (result.owner !== undefined) {
154
- out.owner = result.owner;
155
- }
156
- if (claimPath !== undefined) {
157
- out.claimPath = claimPath;
158
- }
159
- if (result.proof !== undefined && result.proof !== null) {
160
- out.claim = prettifyClaim(result.proof);
161
- }
162
- if (result.error !== undefined) {
163
- out.error = result.error;
164
- }
165
- if (result.errorKind !== undefined) {
166
- out.errorKind = result.errorKind;
75
+ /**
76
+ * The MCP's rich `run_proof` view: persist the full claim to disk, return
77
+ * identifier + extractedParameters + a prettified claim (request dropped, byte
78
+ * arrays as base64) + trimmed logs, and — under `RECLAIM_AGENT_TRACE=1` — a
79
+ * forensic run dump. This is the developer-facing shape; the cloud backend
80
+ * projects the same {@link ProofResult} to a lean summary instead.
81
+ */
82
+ export function shapeRichProofResult(result, opts) {
83
+ // Persist the full claim to disk for the dev to inspect/share.
84
+ let claimPath;
85
+ if (result.proof !== undefined && result.proof !== null) {
86
+ try {
87
+ mkdirSync(claimsDir(), { recursive: true });
88
+ claimPath = join(claimsDir(), `${Date.now()}.json`);
89
+ writeFileSync(claimPath, JSON.stringify(result.proof, null, 2), 'utf8');
167
90
  }
168
- // Trim the LLM-visible logs — full stream goes to runLogPath.
169
- if (result.attestorLogs !== undefined && result.attestorLogs.length > 0) {
170
- out.attestorLogs = result.verified
171
- ? warnsAndErrors(result.attestorLogs)
172
- : tailLogs(result.attestorLogs);
173
- if (out.attestorLogs.length === 0) {
174
- delete out.attestorLogs;
175
- }
91
+ catch {
92
+ // Persistence is best-effort; don't fail the proof for it.
93
+ claimPath = undefined;
176
94
  }
177
- if (result.sentParams !== undefined) {
178
- out.sentParams = result.sentParams;
95
+ }
96
+ const out = {
97
+ verified: result.verified,
98
+ extractedValue: result.extractedValue,
99
+ extractedParameters: result.extractedParameters,
100
+ };
101
+ if (result.identifier !== undefined) {
102
+ out.identifier = result.identifier;
103
+ }
104
+ if (result.owner !== undefined) {
105
+ out.owner = result.owner;
106
+ }
107
+ if (claimPath !== undefined) {
108
+ out.claimPath = claimPath;
109
+ }
110
+ if (result.proof !== undefined && result.proof !== null) {
111
+ out.claim = prettifyClaim(result.proof);
112
+ }
113
+ if (result.error !== undefined) {
114
+ out.error = result.error;
115
+ }
116
+ if (result.errorKind !== undefined) {
117
+ out.errorKind = result.errorKind;
118
+ }
119
+ // Trim the LLM-visible logs — full stream goes to runLogPath.
120
+ if (result.attestorLogs !== undefined && result.attestorLogs.length > 0) {
121
+ out.attestorLogs = result.verified
122
+ ? warnsAndErrors(result.attestorLogs)
123
+ : tailLogs(result.attestorLogs);
124
+ if (out.attestorLogs.length === 0) {
125
+ delete out.attestorLogs;
179
126
  }
180
- // Persist a diagnostic dump for forensics — gated behind
181
- // RECLAIM_AGENT_TRACE=1 (matches the attestor logger capture).
182
- // Off by default since happy-path runs don't need it; turn on
183
- // when investigating proof failures and the file at runLogPath
184
- // will carry the input provider, sentParams, full attestor log
185
- // stream, and result.
186
- if (agentTraceEnabled()) {
187
- const runLogPath = writeProofRunDump({
188
- provider,
189
- hasSecretParamsRef: secretParamsRef !== undefined,
190
- attestorUrl,
191
- result: {
192
- verified: result.verified,
193
- error: result.error,
194
- errorKind: result.errorKind,
195
- extractedValue: result.extractedValue,
196
- extractedParameters: result.extractedParameters,
197
- identifier: result.identifier,
198
- owner: result.owner,
199
- sentParams: result.sentParams,
200
- attestorLogs: result.attestorLogs,
201
- claimPath,
202
- },
203
- });
204
- if (runLogPath !== undefined) {
205
- out.runLogPath = runLogPath;
206
- }
127
+ }
128
+ if (result.sentParams !== undefined) {
129
+ out.sentParams = result.sentParams;
130
+ }
131
+ // Persist a diagnostic dump for forensics — gated behind
132
+ // RECLAIM_AGENT_TRACE=1 (matches the attestor logger capture). Off by
133
+ // default since happy-path runs don't need it; turn on when investigating
134
+ // proof failures and the file at runLogPath will carry the input provider,
135
+ // sentParams, full attestor log stream, and result.
136
+ if (agentTraceEnabled()) {
137
+ const runLogPath = writeProofRunDump({
138
+ provider: opts.provider,
139
+ hasSecretParamsRef: opts.hasSecrets,
140
+ attestorUrl: opts.attestorUrl,
141
+ result: {
142
+ verified: result.verified,
143
+ error: result.error,
144
+ errorKind: result.errorKind,
145
+ extractedValue: result.extractedValue,
146
+ extractedParameters: result.extractedParameters,
147
+ identifier: result.identifier,
148
+ owner: result.owner,
149
+ sentParams: result.sentParams,
150
+ attestorLogs: result.attestorLogs,
151
+ claimPath,
152
+ },
153
+ });
154
+ if (runLogPath !== undefined) {
155
+ out.runLogPath = runLogPath;
207
156
  }
208
- return out;
209
- });
157
+ }
158
+ return out;
210
159
  }
@@ -1,9 +1,10 @@
1
- import { type RegisteredTool } from '../../server.ts';
2
- import type { CaptureStore } from './capture.ts';
3
- export interface SecretStore {
4
- put: (values: Record<string, string>) => string;
5
- resolve: (ref: string) => Record<string, string> | undefined;
6
- clear: () => void;
7
- }
8
- export declare function makeSecretStore(): SecretStore;
9
- export declare function proposeTool(captures: CaptureStore, secrets: SecretStore): RegisteredTool;
1
+ /**
2
+ * `propose_provider` — draft a provider (recipe + captured secrets) from ONE
3
+ * captured request, one match + redaction per target value. Authored once as a
4
+ * plain {@link RegisteredTool}; hosts pass their own backend resolver. The
5
+ * draft (recipe + real secret header values) is held server-side under an
6
+ * opaque `draftId`; the result carries only the secret-free provider.
7
+ */
8
+ import { type ResolveBackend } from '../../../authoring/util.ts';
9
+ import { type RegisteredTool } from '../../../tools/registry.ts';
10
+ export declare function proposeTool(resolveBackend: ResolveBackend): RegisteredTool;
@@ -1,85 +1,108 @@
1
- import { randomUUID } from 'node:crypto';
1
+ /**
2
+ * `propose_provider` — draft a provider (recipe + captured secrets) from ONE
3
+ * captured request, one match + redaction per target value. Authored once as a
4
+ * plain {@link RegisteredTool}; hosts pass their own backend resolver. The
5
+ * draft (recipe + real secret header values) is held server-side under an
6
+ * opaque `draftId`; the result carries only the secret-free provider.
7
+ */
8
+ import { objectSchema } from "../../../authoring/util.js";
2
9
  import { draftProvider } from "../../../provider/draft.js";
3
10
  import { OPRF_MAX_BYTES } from "../../../provider/matcher.js";
4
11
  import { verificationWarnings } from "../../../provider/verification-checks.js";
5
- import { defineTool } from "../../server.js";
6
- export function makeSecretStore() {
7
- const map = new Map();
8
- return {
9
- put: (values) => {
10
- const id = `sref_${randomUUID()}`;
11
- map.set(id, values);
12
- return id;
13
- },
14
- resolve: (ref) => map.get(ref),
15
- clear: () => map.clear(),
16
- };
17
- }
18
- export function proposeTool(captures, secrets) {
12
+ import { defineTool } from "../../../tools/registry.js";
13
+ export function proposeTool(resolveBackend) {
19
14
  return defineTool({
20
15
  name: 'propose_provider',
21
- description: 'Draft a Reclaim provider JSON from a captured request + ' +
22
- 'a target value. ' +
23
- 'Secret header values are stored server-side and replaced with an ' +
24
- 'opaque secretParamsRef ' +
25
- 'in the response — raw secret values never appear in tool output. ' +
26
- "`hash` is opt-in: when set, the captured value is OPRF'd so " +
27
- 'the attestor never sees ' +
28
- 'cleartext. Skill should ask the dev first. `oprf-raw` ' +
29
- 'is the only variant that runs ' +
30
- 'without external deps; `oprf` needs gnark (Linux/macOS), ' +
31
- '`oprf-mpc` needs a TEE attestor. ' +
32
- 'The response may include `verificationWarnings` — advisory ' +
33
- 'author-only decisions (e.g. a user-specific URL part needing ' +
34
- 'the {{context.<name>}}-vs-bare call). Surface each to the ' +
35
- 'dev verbatim and resolve before publishing.',
36
- inputSchema: {
37
- type: 'object',
38
- properties: {
39
- captureId: { type: 'string' },
40
- requestId: { type: 'string' },
41
- target: { type: 'string' },
42
- name: { type: 'string' },
43
- hash: { type: 'string', enum: ['oprf-raw', 'oprf', 'oprf-mpc'] },
16
+ description: 'Draft a provider from ONE captured request. Pass `targets` (an '
17
+ + 'array): one entry per value to extract, each with its `value` '
18
+ + '(verbatim) and a distinct `name` (its param). The provider gets '
19
+ + 'one match + redaction per target. Returns a draftId + recipe '
20
+ + '(never secrets). Prefer a SINGLE request holding ALL values. '
21
+ + 'Single-value shorthand: `target` + `name`. Per-target `hash` '
22
+ + 'OPRF-hashes that value.',
23
+ inputSchema: objectSchema({
24
+ requestId: { type: 'string' },
25
+ name: { type: 'string' },
26
+ targets: {
27
+ type: 'array',
28
+ items: {
29
+ type: 'object',
30
+ properties: {
31
+ value: { type: 'string' },
32
+ name: { type: 'string' },
33
+ hash: {
34
+ type: 'string',
35
+ enum: ['oprf-raw', 'oprf', 'oprf-mpc'],
36
+ },
37
+ },
38
+ required: ['value', 'name'],
39
+ },
44
40
  },
45
- required: ['captureId', 'requestId', 'target', 'name'],
46
- },
47
- }, async ({ captureId, requestId, target, name, hash }) => {
48
- if (hash !== undefined && target.length > OPRF_MAX_BYTES) {
49
- throw new Error(`OPRF caps hashed values at ${OPRF_MAX_BYTES} bytes; ` +
50
- `got ${target.length}-byte target ` +
51
- `"${target}". Try a tighter target ` +
52
- '(e.g. just the relevant substring) or call ' +
53
- 'propose_provider without `hash`.');
41
+ target: { type: 'string' },
42
+ hash: { type: 'string', enum: ['oprf-raw', 'oprf', 'oprf-mpc'] },
43
+ }, ['requestId', 'name']),
44
+ }, async (args) => {
45
+ const backend = resolveBackend(args);
46
+ const requestId = String(args.requestId);
47
+ const providerName = String(args.name);
48
+ const defaultHash = args.hash;
49
+ // Explicit multi-value `targets`, else the single `target` shorthand.
50
+ const raw = Array.isArray(args.targets) ? args.targets : [];
51
+ const targets = raw.length
52
+ ? raw.map((t) => {
53
+ const o = t;
54
+ return {
55
+ value: String(o.value),
56
+ name: String(o.name),
57
+ ...(typeof o.hash === 'string'
58
+ ? { hash: o.hash }
59
+ : {}),
60
+ };
61
+ })
62
+ : typeof args.target === 'string'
63
+ ? [{
64
+ value: args.target,
65
+ name: providerName,
66
+ ...(defaultHash ? { hash: defaultHash } : {}),
67
+ }]
68
+ : [];
69
+ if (targets.length === 0) {
70
+ throw new Error('propose_provider needs a `target` value or a non-empty '
71
+ + '`targets` array');
54
72
  }
55
- const session = captures.sessions.get(captureId);
56
- if (!session) {
57
- throw new Error(`no capture ${captureId}`);
73
+ for (const t of targets) {
74
+ if ((t.hash ?? defaultHash) !== undefined
75
+ && t.value.length > OPRF_MAX_BYTES) {
76
+ throw new Error(`OPRF caps hashed values at ${OPRF_MAX_BYTES} bytes; got `
77
+ + `${t.value.length}-byte "${t.value}". Use a tighter `
78
+ + 'target or drop `hash`.');
79
+ }
58
80
  }
59
- const r = session.requests.get(requestId);
60
- if (!r) {
61
- throw new Error(`no request ${requestId}`);
81
+ const captured = backend.capture.requests.get(requestId);
82
+ if (!captured) {
83
+ throw new Error(`no captured request ${requestId}`);
62
84
  }
63
- const body = session.bodies.get(requestId);
64
- if (!body.available) {
65
- throw new Error(`request body unavailable: ${body.reason}`);
85
+ if (!captured.responseBody) {
86
+ throw new Error(`request body unavailable for ${requestId}`);
66
87
  }
67
- const reqWithBody = { ...r, responseBody: body.body };
68
- const { provider, secretRefs } = draftProvider(reqWithBody, target, name, hash);
69
- // Stash real secret values for later replay/proof.
70
- const realSecrets = {};
88
+ const { provider, secretRefs, extractions } = draftProvider(captured, targets, providerName, defaultHash);
89
+ // Materialize the real secret header values for later replay/prove;
90
+ // they stay in the server-side draft, never in this result.
91
+ const secrets = {};
71
92
  for (const ref of secretRefs) {
72
- const real = r.requestHeaders[ref.name];
93
+ const real = captured.requestHeaders[ref.name];
73
94
  if (real !== undefined) {
74
- realSecrets[ref.name] = real;
95
+ secrets[ref.name] = real;
75
96
  }
76
97
  }
77
- const secretParamsRef = secrets.put(realSecrets);
98
+ const draftId = backend.drafts.put({
99
+ provider, secretRefs, secrets, requestId, extractions,
100
+ });
78
101
  const warnings = verificationWarnings(provider);
79
102
  return {
103
+ draftId,
80
104
  provider,
81
105
  secretRefs,
82
- secretParamsRef,
83
106
  ...(warnings.length ? { verificationWarnings: warnings } : {}),
84
107
  };
85
108
  });
@@ -1,3 +1,10 @@
1
- import { type RegisteredTool } from '../../server.ts';
2
- import type { SecretStore } from './propose.ts';
3
- export declare function replayTool(secrets: SecretStore): RegisteredTool;
1
+ /**
2
+ * `replay_request` — validate a drafted recipe before proving: confirm its
3
+ * selectors extract the target(s), and (with `withoutSecrets`) that the
4
+ * endpoint is auth-bound. Authored once as a plain {@link RegisteredTool}; the
5
+ * backend does the actual replay (MCP re-runs locally, cloud re-issues in the
6
+ * TEE pod).
7
+ */
8
+ import { type ResolveBackend } from '../../../authoring/util.ts';
9
+ import { type RegisteredTool } from '../../../tools/registry.ts';
10
+ export declare function replayTool(resolveBackend: ResolveBackend): RegisteredTool;
@@ -1,41 +1,34 @@
1
- import { replayProvider } from "../../../proof/replay.js";
2
- import { defineTool } from "../../server.js";
3
- export function replayTool(secrets) {
1
+ /**
2
+ * `replay_request` — validate a drafted recipe before proving: confirm its
3
+ * selectors extract the target(s), and (with `withoutSecrets`) that the
4
+ * endpoint is auth-bound. Authored once as a plain {@link RegisteredTool}; the
5
+ * backend does the actual replay (MCP re-runs locally, cloud re-issues in the
6
+ * TEE pod).
7
+ */
8
+ import { objectSchema } from "../../../authoring/util.js";
9
+ import { defineTool } from "../../../tools/registry.js";
10
+ export function replayTool(resolveBackend) {
4
11
  return defineTool({
5
12
  name: 'replay_request',
6
- description: 'Replay a drafted provider against the live endpoint ' +
7
- 'using Node fetch (no attestor). ' +
8
- 'Pre-flight gate before run_proof — ' +
9
- 'diagnoses CSRF / token-rotation / matcher-too-tight ' +
10
- 'issues with richer hints than the attestor would produce.',
11
- inputSchema: {
12
- type: 'object',
13
- properties: {
14
- provider: { type: 'object', properties: {} },
15
- secretParamsRef: { type: 'string' },
16
- },
17
- required: ['provider'],
18
- },
19
- }, async ({ provider, secretParamsRef }) => {
20
- let resolved = {};
21
- if (secretParamsRef !== undefined) {
22
- const r = secrets.resolve(secretParamsRef);
23
- if (!r) {
24
- throw new Error(`unknown secretParamsRef "${secretParamsRef}" — ` +
25
- 'the MCP server has restarted or reset_session was called. ' +
26
- 'Re-run propose_provider to mint a fresh ref.');
27
- }
28
- resolved = r;
13
+ description: 'Validate the drafted recipe before proving — confirm its selectors '
14
+ + 'extract the target value. Returns diagnostics; run before '
15
+ + 'proving. Set `withoutSecrets: true` for the AUTH-BOUND check: it '
16
+ + 're-issues the request with NO secrets/cookies (an anonymous '
17
+ + 'visitor). If the values still extract (valid:true), the endpoint '
18
+ + 'is PUBLIC and the proof would not bind to the user — reject it '
19
+ + 'and find an auth-bound request instead.',
20
+ inputSchema: objectSchema({
21
+ draftId: { type: 'string' },
22
+ withoutSecrets: { type: 'boolean' },
23
+ }, ['draftId']),
24
+ }, async (args) => {
25
+ const backend = resolveBackend(args);
26
+ const draft = backend.drafts.get(String(args.draftId));
27
+ if (!draft) {
28
+ throw new Error(`no draft ${String(args.draftId)}`);
29
29
  }
30
- const result = await replayProvider(provider, resolved);
31
- const out = {
32
- status: result.status,
33
- matchesOriginal: result.matchesOriginal,
34
- bodySnippet: result.body.slice(0, 256),
35
- };
36
- if (result.hints !== undefined) {
37
- out.hints = result.hints;
38
- }
39
- return out;
30
+ return backend.replay(draft, {
31
+ withoutSecrets: Boolean(args.withoutSecrets),
32
+ });
40
33
  });
41
34
  }
@@ -5,6 +5,4 @@ import type { CaptureStore } from './capture.ts';
5
5
  export declare function sessionTools(attachRef: {
6
6
  current?: AttachState;
7
7
  config: ConfigStore;
8
- }, captures: CaptureStore, secrets: {
9
- clear: () => void;
10
- }): RegisteredTool[];
8
+ }, captures: CaptureStore): RegisteredTool[];
@@ -1,5 +1,5 @@
1
1
  import { defineTool } from "../../server.js";
2
- export function sessionTools(attachRef, captures, secrets) {
2
+ export function sessionTools(attachRef, captures) {
3
3
  return [
4
4
  defineTool({
5
5
  name: 'get_session',
@@ -14,15 +14,15 @@ export function sessionTools(attachRef, captures, secrets) {
14
14
  })),
15
15
  defineTool({
16
16
  name: 'reset_session',
17
- description: 'Stop all captures, drop in-memory secrets, and clear capture state.',
17
+ description: 'Stop all captures and clear capture state — drops every '
18
+ + 'session\'s drafts and their in-memory secrets.',
18
19
  inputSchema: { type: 'object', properties: {} },
19
20
  }, async () => {
20
- for (const s of captures.sessions.values()) {
21
- await s.stop().catch(() => { });
21
+ for (const entry of captures.sessions.values()) {
22
+ await entry.session.stop().catch(() => { });
22
23
  }
23
24
  captures.sessions.clear();
24
25
  delete captures.activeTab;
25
- secrets.clear();
26
26
  return { reset: true };
27
27
  }),
28
28
  ];
@@ -1,6 +1,8 @@
1
1
  import type { RegisteredTool } from '../../server.ts';
2
2
  import type { CaptureStore } from './capture.ts';
3
- /** The shared authoring tools as MCP `RegisteredTool`s: each resolves its
4
- * capture's local backend from `args.captureId`; the schema carries
5
- * `captureId` plus the rich `run_proof` contract. */
3
+ /** The shared authoring tools mapped to the MCP's shape: resolve each call's
4
+ * local backend from `args.captureId`, and widen the input schema — every tool
5
+ * takes `captureId`; `run_proof` also gets the credential-aware contract. The
6
+ * tools are authored plain in `../../../authoring`; this only wraps them with
7
+ * `extendTool` (handlers untouched — loose arg parse keeps `captureId`). */
6
8
  export declare function sharedAuthoringTools(captures: CaptureStore): RegisteredTool[];
@@ -7,7 +7,7 @@
7
7
  * `start_capture`, since one capture bundles the tab, its requests, and its
8
8
  * drafts) and the rich, credential-aware `run_proof` schema.
9
9
  */
10
- import { buildAuthoringTools, } from "../../../authoring/index.js";
10
+ import { authoringTools, extendTool, } from "../../../authoring/index.js";
11
11
  import { runAttestorProof } from "../../../proof/attestor.js";
12
12
  import { replayProvider } from "../../../proof/replay.js";
13
13
  import { shapeRichProofResult } from "./proof.js";
@@ -91,25 +91,35 @@ const RUN_PROOF_EXTRAS = {
91
91
  },
92
92
  required: ['ownerAddress'],
93
93
  };
94
- /** The shared authoring tools as MCP `RegisteredTool`s: each resolves its
95
- * capture's local backend from `args.captureId`; the schema carries
96
- * `captureId` plus the rich `run_proof` contract. */
94
+ /** The shared authoring tools mapped to the MCP's shape: resolve each call's
95
+ * local backend from `args.captureId`, and widen the input schema — every tool
96
+ * takes `captureId`; `run_proof` also gets the credential-aware contract. The
97
+ * tools are authored plain in `../../../authoring`; this only wraps them with
98
+ * `extendTool` (handlers untouched — loose arg parse keeps `captureId`). */
97
99
  export function sharedAuthoringTools(captures) {
98
- const host = {
99
- resolveBackend: (args) => {
100
- const captureId = String(args.captureId);
101
- const entry = captures.sessions.get(captureId);
102
- if (!entry) {
103
- throw new Error(`no capture ${captureId} — call start_capture first `
104
- + '(it has been reset or never started).');
105
- }
106
- return localBackend(entry);
107
- },
108
- sessionSchema: {
100
+ const resolveBackend = (args) => {
101
+ const captureId = String(args.captureId);
102
+ const entry = captures.sessions.get(captureId);
103
+ if (!entry) {
104
+ throw new Error(`no capture ${captureId} — call start_capture first `
105
+ + '(it has been reset or never started).');
106
+ }
107
+ return localBackend(entry);
108
+ };
109
+ return authoringTools(resolveBackend).map((tool) => {
110
+ if (tool.definition.name === 'run_proof') {
111
+ return extendTool(tool, {
112
+ description: RUN_PROOF_EXTRAS.description,
113
+ properties: {
114
+ captureId: { type: 'string' },
115
+ ...RUN_PROOF_EXTRAS.properties,
116
+ },
117
+ required: ['captureId', ...RUN_PROOF_EXTRAS.required],
118
+ });
119
+ }
120
+ return extendTool(tool, {
109
121
  properties: { captureId: { type: 'string' } },
110
122
  required: ['captureId'],
111
- },
112
- toolExtras: { run_proof: RUN_PROOF_EXTRAS },
113
- };
114
- return buildAuthoringTools(host);
123
+ });
124
+ });
115
125
  }