@trusty-squire/mcp 0.8.2-rc.11 → 0.8.2-rc.13

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.
@@ -200,6 +200,8 @@ export declare function parsePostVerifyStep(raw: string, allowedSelectors?: Read
200
200
  export declare function isTruncatedCapture(sourceText: string, capturedKey: string): boolean;
201
201
  export declare function extractQuotedTokenFromReason(reason: string, pageText: string): string | null;
202
202
  export declare function extractAllLabeledTokensFromReason(reason: string, pageText: string): Record<string, string>;
203
+ export declare function hasAnyExtractedCredential(creds: Record<string, string>): boolean;
204
+ export declare function isMultiCredBundle(creds: Record<string, string>): boolean;
203
205
  export declare function extractApiKeyFromText(text: string): string | null;
204
206
  export declare function pickVerificationLink(links: readonly string[]): string | null;
205
207
  export declare class SignupAgent {
@@ -1 +1 @@
1
- {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/bot/agent.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/bot/agent.ts"],"names":[],"mappings":"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"}
package/dist/bot/agent.js CHANGED
@@ -192,8 +192,14 @@ const STUCK_LOOP_FALLBACK_PATHS = [
192
192
  // describes the same shape.
193
193
  const EXISTING_KEY_URL_HINT = /(?:api[-_/]keys?|api[-_/]tokens?|auth[-_/]tokens?|personal[-_/]access[-_/]tokens?|\/keys(?:\b|\/|$)|\/tokens(?:\b|\/|$)|\/settings\/keys\b|\/settings\/tokens\b|#api[-_/]keys\b|#api[-_/]tokens\b)/i;
194
194
  const MASKED_KEY_GLYPHS = /(?:•{3,}|\*{3,}|─•|·{3,}|•{3,}|x{6,}|[A-Za-z0-9]{2,4}[•*]{5,})/;
195
- const EXISTING_KEY_WORDS = /\b(?:existing\s+(?:api\s+)?(?:key|token)|previously\s+created|created\s+by\b|api\s+keys?\s*\(\d+\)|tokens?\s*\(\d+\)|reveal|copy\s+key)\b/i;
196
- const NO_CREATE_AFFORDANCE_HINT = /\b(?:cannot\s+(?:reveal|extract|read)|values?\s+(?:is\s+)?masked|only\s+shown\s+once|cannot\s+(?:see|view|copy)\s+(?:the\s+)?(?:key|secret|value))\b/i;
195
+ // 0.8.2-rc.12 widened to catch Neon's existing-key list shape
196
+ // (the per-row layout has a "Key name" header + "Created <date>" +
197
+ // "Last used <date|never>" — no glyph, no "existing" word, just the
198
+ // columns of an API-key listing table). The conservative AND with
199
+ // EXISTING_KEY_URL_HINT keeps this from misfiring on marketing copy
200
+ // elsewhere on a non-keys URL.
201
+ const EXISTING_KEY_WORDS = /\b(?:existing\s+(?:api\s+)?(?:key|token)|previously\s+created|created\s+by\b|api\s+keys?\s*\(\d+\)|tokens?\s*\(\d+\)|reveal|copy\s+key|key\s+name\b|last\s+used\b|created(?:\s+\w+){0,3}\s+(?:\d{1,2},?\s+)?\d{4}\b)/i;
202
+ const NO_CREATE_AFFORDANCE_HINT = /\b(?:cannot\s+(?:reveal|extract|read)|values?\s+(?:is\s+)?masked|only\s+shown\s+once|cannot\s+(?:see|view|copy)\s+(?:the\s+)?(?:key|secret|value)|key\s+(?:value|secret)\s+(?:is\s+)?(?:not\s+)?(?:available|recoverable|extractable|shown))\b/i;
197
203
  export function detectExistingAccountNoExtract(input) {
198
204
  if (!EXISTING_KEY_URL_HINT.test(input.url))
199
205
  return false;
@@ -202,12 +208,38 @@ export function detectExistingAccountNoExtract(input) {
202
208
  if (NO_CREATE_AFFORDANCE_HINT.test(input.lastPlannerReason)) {
203
209
  return true;
204
210
  }
205
- // Otherwise require BOTH a mask glyph and an existing-key word in
206
- // the page text. Either alone is too weak: vendors decorate
207
- // marketing tiles with bullet-pointed dots; "existing" appears in
208
- // unrelated dashboard copy.
209
- return (MASKED_KEY_GLYPHS.test(input.pageText) &&
210
- EXISTING_KEY_WORDS.test(input.pageText));
211
+ // 0.8.2-rc.12 three independent positive paths, ANY of which is
212
+ // enough since we already gated on the URL matching an API-keys
213
+ // page (which alone weeds out the marketing-tile false-positives
214
+ // the conservative pre-rc.12 path was protecting against):
215
+ // 1. Mask glyphs in the page (•••, asterisks, ··· — the literal
216
+ // "value is hidden" decoration most vendors use).
217
+ // 2. Two or more existing-key word patterns matched (a key
218
+ // LISTING shape: "Key name" + "Last used" + "Created <date>"
219
+ // is unmistakable when found on a /keys-style URL).
220
+ // 3. Mask glyph PLUS any existing-key word (the original
221
+ // detector — keeps the conservative behavior for vendors
222
+ // whose listing UI uses different column labels).
223
+ const hasMaskGlyph = MASKED_KEY_GLYPHS.test(input.pageText);
224
+ // Tally up to 5 distinct existing-key signals; 2+ is enough.
225
+ const existingKeyMatches = [];
226
+ const allWords = input.pageText.match(new RegExp(EXISTING_KEY_WORDS, "gi"));
227
+ if (allWords !== null) {
228
+ const distinct = new Set();
229
+ for (const m of allWords) {
230
+ distinct.add(m.toLowerCase().replace(/\s+/g, " "));
231
+ if (distinct.size >= 5)
232
+ break;
233
+ }
234
+ existingKeyMatches.push(...distinct);
235
+ }
236
+ if (hasMaskGlyph && existingKeyMatches.length >= 1)
237
+ return true;
238
+ if (existingKeyMatches.length >= 2)
239
+ return true;
240
+ if (hasMaskGlyph && /\bAPI\s+keys?\b/i.test(input.pageText))
241
+ return true;
242
+ return false;
211
243
  }
212
244
  // Pick the next fallback URL to try from STUCK_LOOP_FALLBACK_PATHS
213
245
  // keyed against the origin of the currently-stuck URL. Returns null
@@ -1559,6 +1591,43 @@ export function extractAllLabeledTokensFromReason(reason, pageText) {
1559
1591
  function escapeRegex(s) {
1560
1592
  return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
1561
1593
  }
1594
+ // Keys that the postVerifyLoop's accumulator stores for housekeeping —
1595
+ // they're NOT extracted credentials and must NOT count as "we found
1596
+ // something" when deciding whether an extract round succeeded.
1597
+ const NON_CREDENTIAL_KEYS = new Set([
1598
+ "api_key_truncated", // truncated stub from extractCredentials Pass 1
1599
+ "password", // signup form metadata (email-verification path)
1600
+ "email", // signup form metadata
1601
+ ]);
1602
+ // True iff the credentials Record holds at least one extracted value
1603
+ // (api_key, username, or any labeled multi-cred field). Excludes
1604
+ // metadata + truncated stubs. Used to decide "this extract round
1605
+ // produced something — continue the loop / capture a synthetic extract
1606
+ // round" vs "every tier missed — try the planner-quoted fallback".
1607
+ export function hasAnyExtractedCredential(creds) {
1608
+ for (const key of Object.keys(creds)) {
1609
+ if (NON_CREDENTIAL_KEYS.has(key))
1610
+ continue;
1611
+ return true;
1612
+ }
1613
+ return false;
1614
+ }
1615
+ // True iff the credentials Record contains a multi-credential bundle
1616
+ // — anything beyond the legacy single api_key/username pair. Used by
1617
+ // the post-verify loop's early-exit so a partial multi-cred capture
1618
+ // doesn't return prematurely (Cloudinary's api_key surfaces 4-5
1619
+ // rounds before api_secret; the legacy exit fired the moment api_key
1620
+ // was set, losing cloud_name + api_secret).
1621
+ export function isMultiCredBundle(creds) {
1622
+ for (const key of Object.keys(creds)) {
1623
+ if (NON_CREDENTIAL_KEYS.has(key))
1624
+ continue;
1625
+ if (key === "api_key" || key === "username")
1626
+ continue;
1627
+ return true;
1628
+ }
1629
+ return false;
1630
+ }
1562
1631
  export function extractApiKeyFromText(text) {
1563
1632
  const prefixed = [
1564
1633
  /\bre_[a-zA-Z0-9_]{20,}\b/, // Resend (key body contains underscores)
@@ -3952,11 +4021,44 @@ ${formatInventory(input.inventory)}`,
3952
4021
  // contiguous 0..N-1 chain regardless of how many planner re-plans
3953
4022
  // happen mid-run.
3954
4023
  let capturedRound = 0;
4024
+ // 0.8.2-rc.12 — multi-cred-aware loop exit. Track the number of
4025
+ // distinct credential keys we've accumulated; if we're in a
4026
+ // multi-cred bundle (cloud_name, api_secret, application_id, …)
4027
+ // keep planning past the first api_key surfacing so siblings can
4028
+ // accumulate. Bounded by `roundsSinceLastNewCredential` so a
4029
+ // page that never produces a sibling doesn't loop forever.
4030
+ let lastCredentialKeyCount = Object.keys(credentials).filter((k) => !NON_CREDENTIAL_KEYS.has(k)).length;
4031
+ let roundsSinceLastNewCredential = 0;
4032
+ const MAX_ROUNDS_AWAITING_MORE_CREDENTIALS = 3;
3955
4033
  for (let round = 0; round < args.maxRounds; round++) {
3956
- if (credentials.api_key !== undefined || credentials.username !== undefined) {
4034
+ const currentCredentialKeyCount = Object.keys(credentials).filter((k) => !NON_CREDENTIAL_KEYS.has(k)).length;
4035
+ if (currentCredentialKeyCount > lastCredentialKeyCount) {
4036
+ roundsSinceLastNewCredential = 0;
4037
+ lastCredentialKeyCount = currentCredentialKeyCount;
4038
+ }
4039
+ else if (lastCredentialKeyCount > 0) {
4040
+ roundsSinceLastNewCredential += 1;
4041
+ }
4042
+ // Multi-cred services hold the loop open until either the
4043
+ // planner returns `done`, the budget expires, or we've made
4044
+ // no credential progress for MAX_ROUNDS_AWAITING_MORE_CREDENTIALS
4045
+ // consecutive rounds. Single-cred services keep the legacy
4046
+ // behavior of returning the moment api_key surfaces.
4047
+ const inMultiCredMode = isMultiCredBundle(credentials);
4048
+ if (!inMultiCredMode &&
4049
+ (credentials.api_key !== undefined || credentials.username !== undefined)) {
3957
4050
  args.steps.push(`Post-verify: credentials found on round ${round}.`);
3958
4051
  return credentials;
3959
4052
  }
4053
+ if (inMultiCredMode &&
4054
+ roundsSinceLastNewCredential >= MAX_ROUNDS_AWAITING_MORE_CREDENTIALS &&
4055
+ (credentials.api_key !== undefined || credentials.username !== undefined)) {
4056
+ const summary = Object.keys(credentials)
4057
+ .filter((k) => !NON_CREDENTIAL_KEYS.has(k))
4058
+ .join(", ");
4059
+ args.steps.push(`Post-verify: multi-cred bundle stable for ${roundsSinceLastNewCredential} rounds — returning what we have (${summary}).`);
4060
+ return credentials;
4061
+ }
3960
4062
  // Settle the page first — the previous round's click may have
3961
4063
  // triggered a navigation, and reading a page mid-navigation
3962
4064
  // throws "execution context destroyed". waitForFormReady is
@@ -4269,6 +4371,36 @@ ${formatInventory(input.inventory)}`,
4269
4371
  // root happens to share /settings with the API-keys page
4270
4372
  // doesn't land short of the actual page.
4271
4373
  if (stuckFiresAtUrl >= 2) {
4374
+ // 0.8.2-rc.12 — when the bot is ALREADY on a URL that names
4375
+ // an API-keys page (path contains /keys, /tokens, /api-keys,
4376
+ // etc.) AND the page text shows masked-credential markers,
4377
+ // the dashboard is genuinely showing a pre-existing key
4378
+ // we can't unmask (Neon's `ts-7229` is the canonical case —
4379
+ // the value was revealed once at create-time and is gone).
4380
+ // Skip the fallback-URL navigate entirely (it would land
4381
+ // on a 404 for vendors whose api-keys page lives at an
4382
+ // org-scoped URL like `/app/<org>/settings#api-keys`) and
4383
+ // classify as existing_account_no_extract directly.
4384
+ try {
4385
+ const stuckPageText = await this.browser
4386
+ .extractText()
4387
+ .catch(() => "");
4388
+ if (detectExistingAccountNoExtract({
4389
+ url: state.url,
4390
+ pageText: stuckPageText,
4391
+ lastPlannerReason: nextStep.reason,
4392
+ })) {
4393
+ this.lastPostVerifyDoneReason =
4394
+ `[existing_account_no_extract] stuck-loop at ${state.url} on an existing API-keys page with masked credentials; ` +
4395
+ `latest planner reason: ${nextStep.reason}`;
4396
+ args.steps.push(`Post-verify: stuck-loop on an existing-keys page — classified as existing_account_no_extract, breaking out.`);
4397
+ break;
4398
+ }
4399
+ }
4400
+ catch {
4401
+ // best-effort — fall through to the regular fallback path
4402
+ // if the page-text read failed.
4403
+ }
4272
4404
  const fallback = pickStuckLoopFallbackUrl(state.url, triedFallbackUrls);
4273
4405
  if (fallback !== null) {
4274
4406
  triedFallbackUrls.add(fallback);
@@ -4288,7 +4420,12 @@ ${formatInventory(input.inventory)}`,
4288
4420
  prevSignature = null;
4289
4421
  prevInventorySize = -1;
4290
4422
  hint = undefined;
4291
- capturedRound += 1;
4423
+ // Don't bump capturedRound captureOnboardingRound above
4424
+ // already wrote a capture for this round (the stuck-loop
4425
+ // detector runs AFTER the capture, so the planner's
4426
+ // observed step IS on disk). Bumping again here would
4427
+ // leave a phantom gap in the chain that verifyCaptureChain
4428
+ // rejects as missing_round.
4292
4429
  continue;
4293
4430
  }
4294
4431
  // Every plausible fallback URL has been tried and we're
@@ -4371,126 +4508,121 @@ ${formatInventory(input.inventory)}`,
4371
4508
  hint = undefined;
4372
4509
  try {
4373
4510
  if (nextStep.kind === "extract") {
4374
- credentials = await this.extractCredentials();
4375
- if (credentials.api_key === undefined) {
4376
- // rc.28 planner-quoted-token fallback. The regex
4377
- // library missed (IPInfo's 14-char hex; some other
4378
- // shape) but the planner's reason often literally
4379
- // quotes the value. Accept it IF it's also present
4380
- // verbatim in the visible page text that's the
4381
- // anti-hallucination guardrail.
4382
- // rc.38 verify the planner-quoted value against both
4383
- // visible text AND every input's `value` attribute. The
4384
- // rc.37 Upstash retest showed the bot quoting a bare UUID
4385
- // it observed in a create-key modal whose UUID lived in
4386
- // an <input readonly value="…"> textContent doesn't
4387
- // include input values, so the verbatim-in-page check
4388
- // rejected a real credential. Concatenating input values
4389
- // closes the gap without weakening the anti-hallucination
4390
- // guarantee (the candidate still has to appear SOMEWHERE
4391
- // verifiable on the page).
4392
- const [pageText, inputValues] = await Promise.all([
4393
- this.browser.extractText().catch(() => ""),
4394
- this.browser.extractAllInputValues().catch(() => []),
4395
- ]);
4396
- const verifySource = pageText + "\n" + inputValues.join("\n");
4397
- // Phase E multi-cred-aware extraction. Try the labeled
4398
- // multi-credential parser FIRST. If the planner labeled
4399
- // 2+ distinct credentials in its reason, fold them all
4400
- // into the credentials Record. If the parser found at
4401
- // least one new value (cloud_name, api_secret, etc. —
4402
- // anything beyond the single api_key the legacy path
4403
- // captures), prefer this. Falls through to the single-
4404
- // value extractQuotedTokenFromReason when no labeled
4405
- // tokens parsed (single-cred services, ad-hoc planner
4406
- // prose without explicit labels).
4407
- const labeled = extractAllLabeledTokensFromReason(nextStep.reason, verifySource);
4408
- const labeledKeys = Object.keys(labeled);
4409
- if (labeledKeys.length >= 2 || (labeledKeys.length === 1 && labeled["api_key"] === undefined)) {
4410
- credentials = { ...credentials, ...labeled };
4411
- const summary = labeledKeys
4412
- .map((k) => `${k}=${labeled[k].slice(0, 4)}…${labeled[k].slice(-4)}`)
4413
- .join(", ");
4414
- args.steps.push(`Post-verify ${round + 1}/${args.maxRounds}: extracted ${labeledKeys.length} labeled credential(s) ` +
4415
- `via Phase E parser (${summary})`);
4416
- // When the planner's reason explicitly flags a masked
4417
- // credential ("api_secret is masked", "hidden behind
4418
- // asterisks", "click Reveal to show"), Phase E only
4419
- // captured the visible valuestry to reveal + extract
4420
- // the rest on the same round before continuing. Without
4421
- // this, the loop returns success with a partial bundle
4422
- // and never tries the reveal click.
4423
- const MASKED_HINT = /\b(?:masked|hidden|bullets?|asterisks?|••+|\*{3,}|reveal|unmask)\b/i;
4424
- if (MASKED_HINT.test(nextStep.reason)) {
4425
- try {
4426
- const revealRes = await this.browser.revealMaskedCredentials();
4427
- args.steps.push(`Post-verify ${round + 1}/${args.maxRounds}: reveal pass clicked=${revealRes.clicked} diagnostic=[${revealRes.diagnostic.join("; ")}]`);
4428
- if (revealRes.clicked > 0) {
4429
- const labeledAfter = await this.extractFromDomProximity();
4430
- const newKeys = Object.keys(labeledAfter).filter((k) => credentials[k] === undefined);
4431
- if (newKeys.length > 0) {
4432
- for (const k of newKeys)
4433
- credentials[k] = labeledAfter[k];
4434
- args.steps.push(`Post-verify ${round + 1}/${args.maxRounds}: post-reveal DOM-proximity extracted ${newKeys.length} more (${newKeys.join(", ")})`);
4435
- }
4436
- else {
4437
- // Surface ALL labeled candidates we found, so
4438
- // we can see whether the value is on-page but
4439
- // mislabeled vs. genuinely not surfaced.
4440
- const allLabeled = await this.browser.extractLabeledCredentialCandidates();
4441
- const summary = allLabeled
4442
- .filter((c) => !c.isMasked)
4443
- .slice(0, 8)
4444
- .map((c) => `${c.value.slice(0, 6)}…(${c.value.length}ch)/${c.label ?? "no-label"}`)
4445
- .join(", ");
4446
- args.steps.push(`Post-verify ${round + 1}/${args.maxRounds}: post-reveal had ${allLabeled.length} candidates; visible: ${summary}`);
4447
- }
4448
- }
4511
+ // 0.8.2-rc.12 multi-cred preservation + always-on Phase E.
4512
+ //
4513
+ // Pre-rc.12 the extract step was a tower of "if no api_key,
4514
+ // try Phase E; else done." That short-circuit silently lost
4515
+ // cloud_name + api_secret on Cloudinary-class services whose
4516
+ // api_key is plain-visible to the legacy regex extractor
4517
+ // the legacy path filled credentials.api_key, the if-branch
4518
+ // skipped Phase E entirely, and the loop's top-of-iter exit
4519
+ // returned a partial bundle.
4520
+ //
4521
+ // New shape: run the legacy extractor, Phase E, the reveal
4522
+ // pass, and DOM-proximity UNCONDITIONALLY on every extract
4523
+ // round, merging each into `credentials` first-wins. A later
4524
+ // pass never clobbers a value an earlier pass labeled. This
4525
+ // mirrors the design doc: Phase E is the multi-cred surface;
4526
+ // single-cred is just multi-cred-with-one-key.
4527
+ const [pageText, inputValues] = await Promise.all([
4528
+ this.browser.extractText().catch(() => ""),
4529
+ this.browser.extractAllInputValues().catch(() => []),
4530
+ ]);
4531
+ const verifySource = pageText + "\n" + inputValues.join("\n");
4532
+ // Tier 1 — legacy single-cred extractor (api_key by shape).
4533
+ // Merge into the running accumulator instead of overwriting;
4534
+ // a Phase E label captured on a prior round wins over a
4535
+ // later legacy regex hit.
4536
+ const legacy = await this.extractCredentials();
4537
+ for (const [k, v] of Object.entries(legacy)) {
4538
+ if (credentials[k] === undefined)
4539
+ credentials[k] = v;
4540
+ }
4541
+ // Tier 2 Phase E labeled-token parser over the planner's
4542
+ // reason. Picks up cloud_name='dlq4xgrca' / api_key='4917…'
4543
+ // / application_id='X' / admin_api_key='…' style narrative.
4544
+ const labeled = extractAllLabeledTokensFromReason(nextStep.reason, verifySource);
4545
+ const labeledNewKeys = Object.keys(labeled).filter((k) => credentials[k] === undefined);
4546
+ if (labeledNewKeys.length > 0) {
4547
+ for (const k of labeledNewKeys)
4548
+ credentials[k] = labeled[k];
4549
+ const summary = labeledNewKeys
4550
+ .map((k) => `${k}=${labeled[k].slice(0, 4)}…${labeled[k].slice(-4)}`)
4551
+ .join(", ");
4552
+ args.steps.push(`Post-verify ${round + 1}/${args.maxRounds}: Phase E surfaced ${labeledNewKeys.length} labeled credential(s) (${summary})`);
4553
+ }
4554
+ // Tier 2.5 reveal-then-extract when the planner explicitly
4555
+ // flagged a masked credential. Fires whether or not we
4556
+ // already have other credentialsCloudinary's api_secret
4557
+ // sits beside an already-visible api_key in the table.
4558
+ const MASKED_HINT = /\b(?:masked|hidden|bullets?|asterisks?|••+|\*{3,}|reveal|unmask)\b/i;
4559
+ if (MASKED_HINT.test(nextStep.reason)) {
4560
+ try {
4561
+ const revealRes = await this.browser.revealMaskedCredentials();
4562
+ args.steps.push(`Post-verify ${round + 1}/${args.maxRounds}: reveal pass clicked=${revealRes.clicked} diagnostic=[${revealRes.diagnostic.join("; ")}]`);
4563
+ if (revealRes.clicked > 0) {
4564
+ const labeledAfter = await this.extractFromDomProximity();
4565
+ const afterNewKeys = Object.keys(labeledAfter).filter((k) => credentials[k] === undefined);
4566
+ if (afterNewKeys.length > 0) {
4567
+ for (const k of afterNewKeys)
4568
+ credentials[k] = labeledAfter[k];
4569
+ args.steps.push(`Post-verify ${round + 1}/${args.maxRounds}: post-reveal DOM-proximity extracted ${afterNewKeys.length} more (${afterNewKeys.join(", ")})`);
4449
4570
  }
4450
- catch (err) {
4451
- args.steps.push(`Post-verify ${round + 1}/${args.maxRounds}: reveal pass error (${err instanceof Error ? err.message : String(err)})`);
4571
+ else {
4572
+ // Diagnostic: which candidates were seen on the page?
4573
+ // Helps debug "Reveal click landed but the value
4574
+ // didn't appear in proximity to a known label".
4575
+ const allLabeled = await this.browser.extractLabeledCredentialCandidates();
4576
+ const candSummary = allLabeled
4577
+ .filter((c) => !c.isMasked)
4578
+ .slice(0, 8)
4579
+ .map((c) => `${c.value.slice(0, 6)}…(${c.value.length}ch)/${c.label ?? "no-label"}`)
4580
+ .join(", ");
4581
+ args.steps.push(`Post-verify ${round + 1}/${args.maxRounds}: post-reveal had ${allLabeled.length} candidates; visible: ${candSummary}`);
4452
4582
  }
4453
4583
  }
4454
- consecutiveFailedExtracts = 0;
4455
- continue;
4456
4584
  }
4457
- const quoted = extractQuotedTokenFromReason(nextStep.reason, verifySource);
4458
- if (quoted !== null) {
4459
- credentials = { ...credentials, api_key: quoted };
4460
- args.steps.push(`Post-verify ${round + 1}/${args.maxRounds}: extracted token via ` +
4461
- `planner-quoted fallback (${quoted.slice(0, 4)}…${quoted.slice(-4)})`);
4462
- consecutiveFailedExtracts = 0;
4463
- continue;
4464
- }
4465
- // Tier 4 — DOM-proximity labeled credential extraction.
4466
- // Run BEFORE bailing the extract. Walks the visible DOM,
4467
- // finds credential-shape strings, pairs each with its
4468
- // nearest credential-label text by Euclidean center
4469
- // distance. Catches multi-cred pages where the planner
4470
- // mentioned ONE value but the DOM shows several (the
4471
- // planner's narrative-style extract reason missed the
4472
- // sibling labels). Also tries to unmask hidden secrets
4473
- // first by clicking visible Reveal/Eye/Copy buttons.
4474
- try {
4475
- await this.browser.revealMaskedCredentials();
4476
- }
4477
- catch {
4478
- // Best-effort; never block the extract pass on a
4479
- // reveal-click failure.
4585
+ catch (err) {
4586
+ args.steps.push(`Post-verify ${round + 1}/${args.maxRounds}: reveal pass error (${err instanceof Error ? err.message : String(err)})`);
4480
4587
  }
4588
+ }
4589
+ // Tier 3 — DOM-proximity labeled extractor. Walks the
4590
+ // visible DOM, pairs credential-shape strings with their
4591
+ // nearest credential-label text. Catches services whose
4592
+ // planner-reason narrative missed sibling labels but whose
4593
+ // DOM still has them as <td>/<dt> pairs.
4594
+ try {
4481
4595
  const labeledFromDom = await this.extractFromDomProximity();
4482
- const newKeys = Object.keys(labeledFromDom).filter((k) => credentials[k] === undefined);
4483
- if (newKeys.length > 0) {
4484
- for (const k of newKeys)
4596
+ const domNewKeys = Object.keys(labeledFromDom).filter((k) => credentials[k] === undefined);
4597
+ if (domNewKeys.length > 0) {
4598
+ for (const k of domNewKeys)
4485
4599
  credentials[k] = labeledFromDom[k];
4486
- const summary = newKeys
4487
- .map((k) => {
4488
- const v = labeledFromDom[k];
4489
- return `${k}=${v.slice(0, 4)}…${v.slice(-4)}`;
4490
- })
4600
+ const summary = domNewKeys
4601
+ .map((k) => `${k}=${labeledFromDom[k].slice(0, 4)}…${labeledFromDom[k].slice(-4)}`)
4491
4602
  .join(", ");
4492
- args.steps.push(`Post-verify ${round + 1}/${args.maxRounds}: extracted ${newKeys.length} labeled credential(s) ` +
4493
- `via DOM-proximity fallback (${summary})`);
4603
+ args.steps.push(`Post-verify ${round + 1}/${args.maxRounds}: DOM-proximity surfaced ${domNewKeys.length} more (${summary})`);
4604
+ }
4605
+ }
4606
+ catch {
4607
+ // best-effort; never abort an extract pass on DOM-proximity
4608
+ // failure (page mid-navigation etc).
4609
+ }
4610
+ // Anything found across all tiers? hasMultiCredCredentials
4611
+ // also catches non-api_key labels (cloud_name, application_id).
4612
+ if (hasAnyExtractedCredential(credentials)) {
4613
+ consecutiveFailedExtracts = 0;
4614
+ continue;
4615
+ }
4616
+ // True extract failure — every tier missed. Try the legacy
4617
+ // single-value planner-quoted fallback for services whose
4618
+ // planner prose just bare-quotes the value without a known
4619
+ // label vocabulary (Railway UUID-only, IPInfo 14-hex).
4620
+ {
4621
+ const quoted = extractQuotedTokenFromReason(nextStep.reason, verifySource);
4622
+ if (quoted !== null) {
4623
+ credentials.api_key = quoted;
4624
+ args.steps.push(`Post-verify ${round + 1}/${args.maxRounds}: extracted token via ` +
4625
+ `planner-quoted fallback (${quoted.slice(0, 4)}…${quoted.slice(-4)})`);
4494
4626
  consecutiveFailedExtracts = 0;
4495
4627
  continue;
4496
4628
  }
@@ -4558,9 +4690,6 @@ ${formatInventory(input.inventory)}`,
4558
4690
  "generate a fresh one — its full value is shown once, on creation.";
4559
4691
  }
4560
4692
  }
4561
- else {
4562
- consecutiveFailedExtracts = 0;
4563
- }
4564
4693
  }
4565
4694
  else if (nextStep.kind === "click") {
4566
4695
  await this.browser.click(nextStep.selector);
@@ -4576,13 +4705,23 @@ ${formatInventory(input.inventory)}`,
4576
4705
  // services without modal-delay returns in <1s. Saves both
4577
4706
  // time (no overshoot wait) and correctness (catches the
4578
4707
  // modal-render race).
4708
+ // 0.8.2-rc.12 — merge polled extract into the running
4709
+ // credentials accumulator (was previously assigned to a
4710
+ // throwaway `pollExtract` local). On modal-key reveal
4711
+ // flows (OpenRouter, Anthropic, OpenAI) the credential
4712
+ // appears only here, and the legacy assignment was lost
4713
+ // unless the next round's top-of-iter re-read just
4714
+ // happened to find it again — a flaky guarantee.
4579
4715
  const credentialDeadline = Date.now() + 8000;
4580
- let pollExtract = {};
4581
4716
  while (Date.now() < credentialDeadline) {
4582
4717
  await this.browser.wait(0.5);
4583
4718
  try {
4584
- pollExtract = await this.extractCredentials();
4585
- if (pollExtract.api_key !== undefined)
4719
+ const pollExtract = await this.extractCredentials();
4720
+ for (const [k, v] of Object.entries(pollExtract)) {
4721
+ if (credentials[k] === undefined)
4722
+ credentials[k] = v;
4723
+ }
4724
+ if (credentials.api_key !== undefined)
4586
4725
  break;
4587
4726
  }
4588
4727
  catch {
@@ -4667,9 +4806,24 @@ ${formatInventory(input.inventory)}`,
4667
4806
  }
4668
4807
  // Re-extract — but tolerate the page still navigating from the
4669
4808
  // step just taken; the next round settles and re-reads.
4670
- const hadCredentialsBefore = credentials.api_key !== undefined || credentials.username !== undefined;
4809
+ // 0.8.2-rc.12 MERGE into the running accumulator. The pre-
4810
+ // rc.12 unconditional assignment wiped multi-cred fields the
4811
+ // explicit extract round just accumulated (cloud_name, api_secret,
4812
+ // etc.); on the next round's top-of-iter early-exit, only the
4813
+ // legacy single api_key survived.
4814
+ // 0.8.2-rc.12 — count distinct credential keys before re-extract
4815
+ // so the synthetic-extract trigger fires on ANY new key, not just
4816
+ // the legacy api_key / username pair. A cloudinary reveal click
4817
+ // can produce a fresh api_secret while api_key was already set;
4818
+ // the pre-rc.12 trigger silently skipped the synthetic capture
4819
+ // and the synthesizer then rejected on no_extract_step.
4820
+ const credCountBefore = Object.keys(credentials).filter((k) => !NON_CREDENTIAL_KEYS.has(k)).length;
4671
4821
  try {
4672
- credentials = await this.extractCredentials();
4822
+ const reExtract = await this.extractCredentials();
4823
+ for (const [k, v] of Object.entries(reExtract)) {
4824
+ if (credentials[k] === undefined)
4825
+ credentials[k] = v;
4826
+ }
4673
4827
  }
4674
4828
  catch {
4675
4829
  // page mid-navigation — next round's waitForFormReady handles it
@@ -4687,8 +4841,8 @@ ${formatInventory(input.inventory)}`,
4687
4841
  // RIGHT NOW (the action just ran, the token row is now visible).
4688
4842
  // Best-effort — a capture failure must never block returning the
4689
4843
  // credential we already have.
4690
- const haveNewCredentials = !hadCredentialsBefore &&
4691
- (credentials.api_key !== undefined || credentials.username !== undefined);
4844
+ const credCountAfter = Object.keys(credentials).filter((k) => !NON_CREDENTIAL_KEYS.has(k)).length;
4845
+ const haveNewCredentials = credCountAfter > credCountBefore;
4692
4846
  if (haveNewCredentials && nextStep.kind !== "extract") {
4693
4847
  try {
4694
4848
  const [postState, postInventory] = await Promise.all([