@reclaimprotocol/agent 0.1.22 → 0.1.23
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/authoring/backend.d.ts +9 -7
- package/lib/authoring/tools.js +98 -1
- package/lib/mcp/tools/agent/propose.js +27 -3
- package/lib/mcp/tools/agent/publish.js +7 -5
- package/lib/mcp/tools/agent/shared.js +17 -8
- package/lib/mcp/tools/authenticate/authenticate.d.ts +11 -0
- package/lib/mcp/tools/authenticate/authenticate.js +128 -119
- package/lib/mcp/tools/authenticate/builder-access.d.ts +8 -3
- package/lib/mcp/tools/authenticate/builder-access.js +8 -3
- package/lib/mcp/tools/mode/mode.js +32 -1
- package/lib/old/client.d.ts +9 -0
- package/lib/old/client.js +19 -1
- package/lib/old/to-register.d.ts +17 -6
- package/lib/old/to-register.js +6 -0
- package/lib/old/tools/me-providers.js +3 -1
- package/lib/old/tools/publish.js +82 -38
- package/lib/proof/page-replay.d.ts +36 -0
- package/lib/proof/page-replay.js +76 -0
- package/lib/proof/replay.d.ts +28 -0
- package/lib/proof/replay.js +39 -18
- package/lib/provider/draft.d.ts +3 -1
- package/lib/provider/draft.js +39 -7
- package/lib/provider/matcher.js +116 -7
- package/lib/provider/to-version.d.ts +8 -6
- package/lib/provider/to-version.js +15 -3
- package/package.json +3 -2
- package/skill/SKILL.builder.md +1 -1
- package/skill/SKILL.common.md +9 -0
- package/skill/SKILL.old.md +1 -1
- package/skill/injection-api.md +44 -15
- package/skill/old-mode.md +3 -1
- package/src/authoring/backend.ts +9 -7
- package/src/authoring/tools.ts +119 -1
- package/src/mcp/tools/agent/propose.ts +29 -3
- package/src/mcp/tools/agent/publish.ts +7 -6
- package/src/mcp/tools/agent/shared.ts +19 -8
- package/src/mcp/tools/authenticate/authenticate.ts +150 -163
- package/src/mcp/tools/authenticate/builder-access.ts +8 -3
- package/src/mcp/tools/mode/mode.ts +34 -1
- package/src/old/client.ts +30 -1
- package/src/old/to-register.ts +25 -6
- package/src/old/tools/me-providers.ts +3 -1
- package/src/old/tools/publish.ts +86 -40
- package/src/proof/page-replay.ts +131 -0
- package/src/proof/replay.ts +60 -19
- package/src/provider/draft.ts +46 -6
- package/src/provider/matcher.ts +145 -5
- package/src/provider/to-version.ts +24 -9
package/src/provider/matcher.ts
CHANGED
|
@@ -171,7 +171,7 @@ function jsonMatcher(
|
|
|
171
171
|
return undefined
|
|
172
172
|
}
|
|
173
173
|
|
|
174
|
-
const anchor = jsonKeyPrefix(body, target)
|
|
174
|
+
const anchor = jsonKeyPrefix(body, target, lastPathKey(jsonPath))
|
|
175
175
|
if(!anchor) {
|
|
176
176
|
return undefined
|
|
177
177
|
}
|
|
@@ -182,15 +182,34 @@ function jsonMatcher(
|
|
|
182
182
|
)
|
|
183
183
|
}
|
|
184
184
|
|
|
185
|
+
/** The trailing object-property name of a jsonPath (`$.total_records` →
|
|
186
|
+
* `"total_records"`), or undefined for an array-index/bracket leaf, which
|
|
187
|
+
* has no literal key to anchor a search on. */
|
|
188
|
+
function lastPathKey(jsonPath: string | undefined): string | undefined {
|
|
189
|
+
const m = jsonPath ? /\.([A-Za-z_][A-Za-z0-9_]*)$/.exec(jsonPath) : null
|
|
190
|
+
return m?.[1]
|
|
191
|
+
}
|
|
192
|
+
|
|
185
193
|
/**
|
|
186
194
|
* The literal `"key":"` (or `"key":` for a raw number/bool) immediately before
|
|
187
195
|
* `target` in `text`, plus the closing quote (`"` for a string value, `''`
|
|
188
196
|
* otherwise). Returns undefined when the value isn't preceded by a JSON key.
|
|
197
|
+
*
|
|
198
|
+
* When `key` is given, anchors the search on occurrences of the KEY (not the
|
|
199
|
+
* value) — so a short/common value elsewhere in the body (e.g. the digit "5"
|
|
200
|
+
* inside `"investment_amount":354.85`) can't be mistaken for this field. Only
|
|
201
|
+
* falls back to a whole-body value search when there's no resolvable key
|
|
202
|
+
* (e.g. `findJsonPath` returned an array-index leaf).
|
|
189
203
|
*/
|
|
190
204
|
function jsonKeyPrefix(
|
|
191
205
|
text: string,
|
|
192
206
|
target: string,
|
|
207
|
+
key?: string,
|
|
193
208
|
): { prefix: string, close: string } | undefined {
|
|
209
|
+
if(key !== undefined) {
|
|
210
|
+
return jsonKeyPrefixByKey(text, target, key)
|
|
211
|
+
}
|
|
212
|
+
|
|
194
213
|
const { idx } = firstIndex(text, target)
|
|
195
214
|
if(idx < 0) {
|
|
196
215
|
return undefined
|
|
@@ -227,6 +246,78 @@ function jsonKeyPrefix(
|
|
|
227
246
|
return { prefix: text.slice(keyStart, idx), close: quoted ? '"' : '' }
|
|
228
247
|
}
|
|
229
248
|
|
|
249
|
+
/** `jsonKeyPrefix`'s key-anchored path: scan occurrences of `"key"`, and for
|
|
250
|
+
* each, check whether the value that follows its `:` matches `target` (or a
|
|
251
|
+
* normalized candidate) before accepting it. */
|
|
252
|
+
function jsonKeyPrefixByKey(
|
|
253
|
+
text: string,
|
|
254
|
+
target: string,
|
|
255
|
+
key: string,
|
|
256
|
+
): { prefix: string, close: string } | undefined {
|
|
257
|
+
const keyToken = `"${key}"`
|
|
258
|
+
let from = 0
|
|
259
|
+
for(;;) {
|
|
260
|
+
const keyIdx = text.indexOf(keyToken, from)
|
|
261
|
+
if(keyIdx < 0) {
|
|
262
|
+
return undefined
|
|
263
|
+
}
|
|
264
|
+
|
|
265
|
+
from = keyIdx + keyToken.length
|
|
266
|
+
let i = from
|
|
267
|
+
while(i < text.length && /\s/.test(text[i] ?? '')) {
|
|
268
|
+
i++
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
if(text[i] !== ':') {
|
|
272
|
+
continue
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
i++
|
|
276
|
+
while(i < text.length && /\s/.test(text[i] ?? '')) {
|
|
277
|
+
i++
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
const matched = matchJsonValueAt(text, i, target)
|
|
281
|
+
if(matched) {
|
|
282
|
+
return {
|
|
283
|
+
prefix: text.slice(keyIdx, matched.valueStart),
|
|
284
|
+
close: matched.close,
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
|
|
290
|
+
/** Whether the JSON value starting at `pos` in `text` — quoted string or a
|
|
291
|
+
* raw number/bool/null token — equals `target` (or a normalized candidate).
|
|
292
|
+
* Returns the position right after any opening quote (matching `firstIndex`'s
|
|
293
|
+
* convention) and the delimiter to close the match template with. */
|
|
294
|
+
function matchJsonValueAt(
|
|
295
|
+
text: string,
|
|
296
|
+
pos: number,
|
|
297
|
+
target: string,
|
|
298
|
+
): { valueStart: number, close: string } | undefined {
|
|
299
|
+
const candidates = [target, ...normalizeCandidates(target)]
|
|
300
|
+
if(text[pos] === '"') {
|
|
301
|
+
let j = pos + 1
|
|
302
|
+
while(j < text.length && text[j] !== '"') {
|
|
303
|
+
j += text[j] === '\\' ? 2 : 1
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
const raw = text.slice(pos + 1, j)
|
|
307
|
+
return candidates.includes(raw)
|
|
308
|
+
? { valueStart: pos + 1, close: '"' }
|
|
309
|
+
: undefined
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
let j = pos
|
|
313
|
+
while(j < text.length && !/[,}\]\s]/.test(text[j] ?? '')) {
|
|
314
|
+
j++
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
const raw = text.slice(pos, j)
|
|
318
|
+
return candidates.includes(raw) ? { valueStart: pos, close: '' } : undefined
|
|
319
|
+
}
|
|
320
|
+
|
|
230
321
|
function findJsonPath(body: string, target: string): string | undefined {
|
|
231
322
|
let parsed: unknown
|
|
232
323
|
try {
|
|
@@ -320,7 +411,9 @@ function htmlMatcher(
|
|
|
320
411
|
const scriptJson = scriptJsonContent(revealSlice)
|
|
321
412
|
if(scriptJson) {
|
|
322
413
|
const jsonPath = findJsonPath(scriptJson, target)
|
|
323
|
-
const kp = jsonPath
|
|
414
|
+
const kp = jsonPath
|
|
415
|
+
? jsonKeyPrefix(scriptJson, target, lastPathKey(jsonPath))
|
|
416
|
+
: undefined
|
|
324
417
|
if(jsonPath && kp) {
|
|
325
418
|
return keyValueResult(
|
|
326
419
|
kp.prefix, kp.close, target, paramName, valueClass,
|
|
@@ -550,7 +643,7 @@ function contentMatcher(
|
|
|
550
643
|
return {
|
|
551
644
|
match: {
|
|
552
645
|
type: 'contains',
|
|
553
|
-
value: buildContextualTemplate(body, target, paramName),
|
|
646
|
+
value: buildContextualTemplate(body, target, paramName, valueClass),
|
|
554
647
|
},
|
|
555
648
|
redaction,
|
|
556
649
|
paramValues: { [paramName]: target },
|
|
@@ -577,18 +670,63 @@ function contentMatcher(
|
|
|
577
670
|
* must CONTAIN the match template (buildContextualTemplate), so the trailing
|
|
578
671
|
* delimiter is kept unless it's whitespace (which the template also drops).
|
|
579
672
|
*/
|
|
673
|
+
/** Indices where `cand` occurs in `body` as a WHOLE match of `valueClass` —
|
|
674
|
+
* i.e. not a substring inside a longer run of the same class (the digit "5"
|
|
675
|
+
* inside "354.85" is never a whole `\d+` match; "354" and "85" are). Only
|
|
676
|
+
* meaningful for a structural (non-free-form) class. */
|
|
677
|
+
function wholeTokenIndices(
|
|
678
|
+
body: string,
|
|
679
|
+
cand: string,
|
|
680
|
+
valueClass: string,
|
|
681
|
+
): number[] {
|
|
682
|
+
const indices: number[] = []
|
|
683
|
+
let re: RegExp
|
|
684
|
+
try {
|
|
685
|
+
re = new RegExp(valueClass, 'g')
|
|
686
|
+
} catch{
|
|
687
|
+
return indices
|
|
688
|
+
}
|
|
689
|
+
|
|
690
|
+
let m: RegExpExecArray | null
|
|
691
|
+
while((m = re.exec(body))) {
|
|
692
|
+
if(m[0] === cand) {
|
|
693
|
+
indices.push(m.index)
|
|
694
|
+
}
|
|
695
|
+
|
|
696
|
+
if(re.lastIndex === m.index) {
|
|
697
|
+
re.lastIndex++
|
|
698
|
+
}
|
|
699
|
+
}
|
|
700
|
+
|
|
701
|
+
return indices
|
|
702
|
+
}
|
|
703
|
+
|
|
580
704
|
function buildContentRegex(
|
|
581
705
|
body: string,
|
|
582
706
|
target: string,
|
|
583
707
|
paramName: string,
|
|
584
708
|
valueClass: string,
|
|
585
709
|
): string | undefined {
|
|
710
|
+
const structural = inferValueClass(target) !== undefined
|
|
586
711
|
for(const cand of normalizeCandidates(target)) {
|
|
587
|
-
const
|
|
712
|
+
const occurrences = structural
|
|
713
|
+
? wholeTokenIndices(body, cand, valueClass)
|
|
714
|
+
: undefined
|
|
715
|
+
const idx = occurrences ? occurrences[0] ?? -1 : body.indexOf(cand)
|
|
588
716
|
if(idx < 0) {
|
|
589
717
|
continue
|
|
590
718
|
}
|
|
591
719
|
|
|
720
|
+
if(occurrences && occurrences.length > 1
|
|
721
|
+
&& cand.replace(/[^A-Za-z0-9]/g, '').length < 4) {
|
|
722
|
+
throw new Error(
|
|
723
|
+
`target value "${target}" occurs ${occurrences.length} times as a `
|
|
724
|
+
+ 'standalone token with no JSON key or HTML anchor to '
|
|
725
|
+
+ "disambiguate — capture a request where it's unique, or use a "
|
|
726
|
+
+ 'longer/more specific target.',
|
|
727
|
+
)
|
|
728
|
+
}
|
|
729
|
+
|
|
592
730
|
const endIdx = idx + cand.length
|
|
593
731
|
const rawRight = findRightContext(body, endIdx)
|
|
594
732
|
const right = /^\s/.test(rawRight) ? '' : escapeRegexFlexWs(rawRight)
|
|
@@ -624,8 +762,10 @@ function buildContextualTemplate(
|
|
|
624
762
|
body: string,
|
|
625
763
|
target: string,
|
|
626
764
|
paramName: string,
|
|
765
|
+
valueClass: string,
|
|
627
766
|
): string {
|
|
628
|
-
const idx = body
|
|
767
|
+
const idx = wholeTokenIndices(body, target, valueClass)[0]
|
|
768
|
+
?? body.indexOf(target)
|
|
629
769
|
if(idx < 0) {
|
|
630
770
|
return `{{${paramName}}}`
|
|
631
771
|
}
|
|
@@ -69,14 +69,16 @@ export interface CreateVersionOptions {
|
|
|
69
69
|
jsUserScripts?: string
|
|
70
70
|
/** Requests the injection script may fire, as `RequestSelectionTemplate`s. */
|
|
71
71
|
allowedJsRequests?: AllowedJsRequest[]
|
|
72
|
-
/** In-app interception options, mapped
|
|
73
|
-
* `webSettings.clientOptions.inapp.interceptorOptions
|
|
74
|
-
* is
|
|
75
|
-
* `
|
|
76
|
-
* `
|
|
72
|
+
/** In-app interception options, mapped to
|
|
73
|
+
* `webSettings.clientOptions.inapp.interceptorOptions` — ONLY when this
|
|
74
|
+
* is explicitly passed (builder's schema has no `NONE` interceptorType;
|
|
75
|
+
* omitting `inapp` entirely IS the "no interception" signal, so omitting
|
|
76
|
+
* this must omit `inapp`, not default it to HAWKEYE). When it IS passed,
|
|
77
|
+
* `isDocumentRequestReplayEnabled` is still ALWAYS force-disabled
|
|
78
|
+
* regardless of what's passed here — see `providerToCreateVersionRequest`;
|
|
79
|
+
* there is no author-facing way to enable it. */
|
|
77
80
|
interceptorOptions?: {
|
|
78
81
|
interceptorType: 'HAWKEYE' | 'MSWJS' | 'CDP'
|
|
79
|
-
isDocumentRequestReplayEnabled?: boolean
|
|
80
82
|
interceptorSettings?: string
|
|
81
83
|
}
|
|
82
84
|
}
|
|
@@ -240,14 +242,27 @@ export function providerToCreateVersionRequest(
|
|
|
240
242
|
geoLocation: pickGeoLocation(providers),
|
|
241
243
|
clientOptions: {
|
|
242
244
|
portal: { useProxy: true },
|
|
245
|
+
// Builder's schema has NO "NONE" interceptorType — the real
|
|
246
|
+
// no-interception signal is OMITTING `inapp` entirely (it's not
|
|
247
|
+
// `required` on `InAppClientOptions`). So `inapp` is only built at
|
|
248
|
+
// ALL when the author explicitly opts into a real interceptorType;
|
|
249
|
+
// omitting `interceptorOptions` must omit `inapp` too, or every
|
|
250
|
+
// standard (non-interception) provider would incorrectly publish
|
|
251
|
+
// as HAWKEYE. Within that block, `isDocumentRequestReplayEnabled`
|
|
252
|
+
// is still force-disabled — see `CreateVersionOptions
|
|
253
|
+
// .interceptorOptions`'s doc comment; there is no way to enable it.
|
|
243
254
|
...(interceptorOptions
|
|
244
255
|
? {
|
|
245
256
|
inapp: {
|
|
246
257
|
interceptorOptions: {
|
|
247
|
-
|
|
248
|
-
// requires the key even when the author omits it.
|
|
258
|
+
interceptorType: interceptorOptions.interceptorType,
|
|
249
259
|
isDocumentRequestReplayEnabled: false,
|
|
250
|
-
...interceptorOptions
|
|
260
|
+
...(interceptorOptions.interceptorSettings
|
|
261
|
+
? {
|
|
262
|
+
interceptorSettings:
|
|
263
|
+
interceptorOptions.interceptorSettings,
|
|
264
|
+
}
|
|
265
|
+
: {}),
|
|
251
266
|
},
|
|
252
267
|
},
|
|
253
268
|
}
|