@trusty-squire/mcp 0.8.2-rc.1 → 0.8.2-rc.11
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/dist/bot/agent.d.ts +8 -0
- package/dist/bot/agent.d.ts.map +1 -1
- package/dist/bot/agent.js +406 -13
- package/dist/bot/agent.js.map +1 -1
- package/dist/bot/browser.d.ts +2 -0
- package/dist/bot/browser.d.ts.map +1 -1
- package/dist/bot/browser.js +184 -2
- package/dist/bot/browser.js.map +1 -1
- package/dist/bot/llm-client.d.ts.map +1 -1
- package/dist/bot/llm-client.js +119 -23
- package/dist/bot/llm-client.js.map +1 -1
- package/dist/bot/promote-to-skill.d.ts.map +1 -1
- package/dist/bot/promote-to-skill.js +106 -11
- package/dist/bot/promote-to-skill.js.map +1 -1
- package/dist/bot/replay-skill.js +124 -8
- package/dist/bot/replay-skill.js.map +1 -1
- package/dist/tools/provision-any.d.ts +16 -1
- package/dist/tools/provision-any.d.ts.map +1 -1
- package/dist/tools/provision-any.js +21 -16
- package/dist/tools/provision-any.js.map +1 -1
- package/package.json +1 -1
package/dist/bot/agent.d.ts
CHANGED
|
@@ -22,6 +22,12 @@ export declare function expectsVerificationEmail(pageText: string): boolean;
|
|
|
22
22
|
export declare class LLMCallBudgetExceeded extends Error {
|
|
23
23
|
constructor(budget: number);
|
|
24
24
|
}
|
|
25
|
+
export declare function detectExistingAccountNoExtract(input: {
|
|
26
|
+
url: string;
|
|
27
|
+
pageText: string;
|
|
28
|
+
lastPlannerReason: string;
|
|
29
|
+
}): boolean;
|
|
30
|
+
export declare function pickStuckLoopFallbackUrl(currentUrl: string, alreadyTried: ReadonlySet<string>): string | null;
|
|
25
31
|
export declare function guessSignupUrl(service: string): string;
|
|
26
32
|
export declare function isKnownDomainFullUrlMatch(service: string, url: string): boolean;
|
|
27
33
|
export declare function isGoogleSearchUrl(url: string): boolean;
|
|
@@ -223,7 +229,9 @@ export declare class SignupAgent {
|
|
|
223
229
|
extractFailureUploader?: ExtractFailureUploader;
|
|
224
230
|
roundUploader?: RoundUploader;
|
|
225
231
|
captchaSolver?: TwoCaptchaSolver;
|
|
232
|
+
googleChallengeTimeoutMs?: number;
|
|
226
233
|
});
|
|
234
|
+
private googleChallengeTimeoutMs;
|
|
227
235
|
get backends(): readonly string[];
|
|
228
236
|
private callLLM;
|
|
229
237
|
private callLLMInner;
|
package/dist/bot/agent.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"agent.d.ts","sourceRoot":"","sources":["../../src/bot/agent.ts"],"names":[],"mappings":"AAUA,OAAO,KAAK,EACV,iBAAiB,EAGjB,cAAc,EACd,kBAAkB,EACnB,MAAM,cAAc,CAAC;AAEtB,OAAO,EAKL,KAAK,eAAe,EACrB,MAAM,sBAAsB,CAAC;AAI9B,OAAO,EAAE,gBAAgB,EAAE,MAAM,8BAA8B,CAAC;AAOhE,OAAO,EAGL,KAAK,SAAS,EACd,KAAK,OAAO,EACb,MAAM,iBAAiB,CAAC;AAMzB,MAAM,WAAW,UAAU;IACzB,YAAY,CAAC,KAAK,EAAE;QAClB,KAAK,EAAE,MAAM,CAAC;QACd,OAAO,EAAE;YAAE,OAAO,CAAC,EAAE,MAAM,CAAC;YAAC,IAAI,CAAC,EAAE,MAAM,CAAC;YAAC,aAAa,CAAC,EAAE,MAAM,CAAA;SAAE,CAAC;QACrE,eAAe,EAAE,MAAM,CAAC;KACzB,GAAG,OAAO,CAAC;QACV,OAAO,EAAE,MAAM,CAAC;QAChB,YAAY,EAAE,MAAM,CAAC;QACrB,YAAY,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC;KACrC,CAAC,CAAC;CACJ;
|
|
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
|
@@ -93,6 +93,22 @@ const ONBOARDING_PAYWALL_PATTERNS = [
|
|
|
93
93
|
/\b(?:plan\s+|account\s+)?payment\s+required\b/i,
|
|
94
94
|
/\bcomplet(?:e|ing)\s+(?:billing|payment)\b/i,
|
|
95
95
|
/\bbilling\s+setup\s+(?:is\s+)?required\b/i,
|
|
96
|
+
// 0.8.2-rc.5 — Together.ai's post-OAuth landing surfaces a "payment
|
|
97
|
+
// form" gate that the post-verify planner reliably describes in its
|
|
98
|
+
// `done` reason:
|
|
99
|
+
//
|
|
100
|
+
// "This page shows a payment form, and it's not possible to proceed
|
|
101
|
+
// further without inputting payment information."
|
|
102
|
+
//
|
|
103
|
+
// None of the rc.39 patterns covered "payment form" / "payment
|
|
104
|
+
// information" — together fell through to `oauth_onboarding_failed`,
|
|
105
|
+
// which is misleading (the OAuth handshake succeeded; the wall is
|
|
106
|
+
// billing). Patterns are scoped to the "form/information requirement"
|
|
107
|
+
// shape so a marketing tile mentioning "payment information" doesn't
|
|
108
|
+
// false-positive.
|
|
109
|
+
/\bpayment\s+form\b/i,
|
|
110
|
+
/\binput(?:ting)?\s+payment\s+information\b/i,
|
|
111
|
+
/\benter(?:ing)?\s+payment\s+information\b/i,
|
|
96
112
|
];
|
|
97
113
|
// Negators that, if they appear in the ~30 characters immediately
|
|
98
114
|
// before a paywall pattern match, flip its meaning from a demand
|
|
@@ -129,6 +145,104 @@ export class LLMCallBudgetExceeded extends Error {
|
|
|
129
145
|
this.name = "LLMCallBudgetExceeded";
|
|
130
146
|
}
|
|
131
147
|
}
|
|
148
|
+
// 0.8.2-rc.10 — common dashboard paths that vendors host their
|
|
149
|
+
// per-account API key UI at. Ordered most-specific first so a
|
|
150
|
+
// fallback navigate doesn't land short of the actual page. Returned
|
|
151
|
+
// as an array of path-strings; the caller composes them onto the
|
|
152
|
+
// origin of the currently-stuck URL and skips any already tried.
|
|
153
|
+
//
|
|
154
|
+
// Patterns harvested from Anthropic (settings/keys), Sentry
|
|
155
|
+
// (settings/account/api/auth-tokens), Neon (settings#api-keys),
|
|
156
|
+
// Render (account/api-keys), Postmark (account/api_tokens),
|
|
157
|
+
// OpenRouter (keys), and a long tail of vendors converging on the
|
|
158
|
+
// same conventions.
|
|
159
|
+
const STUCK_LOOP_FALLBACK_PATHS = [
|
|
160
|
+
"/settings/keys",
|
|
161
|
+
"/settings/api-keys",
|
|
162
|
+
"/settings/api_keys",
|
|
163
|
+
"/settings/tokens",
|
|
164
|
+
"/settings/api-tokens",
|
|
165
|
+
"/settings/account/api/auth-tokens/",
|
|
166
|
+
"/account/api-keys",
|
|
167
|
+
"/account/api_tokens",
|
|
168
|
+
"/account/keys",
|
|
169
|
+
"/account/tokens",
|
|
170
|
+
"/api-keys",
|
|
171
|
+
"/api_keys",
|
|
172
|
+
"/keys",
|
|
173
|
+
"/tokens",
|
|
174
|
+
"/auth-tokens",
|
|
175
|
+
"/dashboard/api-keys",
|
|
176
|
+
"/dashboard/keys",
|
|
177
|
+
];
|
|
178
|
+
// 0.8.2-rc.10 — heuristic for "this account already exists on the
|
|
179
|
+
// service and its API keys are masked, with no path to reveal them."
|
|
180
|
+
// The test identity (methoxine@gmail.com) accumulates state across
|
|
181
|
+
// batches; subsequent runs land on a dashboard whose API-keys page
|
|
182
|
+
// shows only the NAMES of existing keys (the values were revealed
|
|
183
|
+
// once at create-time and aren't recoverable). Without this
|
|
184
|
+
// classifier those runs fall through to a generic
|
|
185
|
+
// oauth_onboarding_failed and the harvester treats them like a
|
|
186
|
+
// repairable bug.
|
|
187
|
+
//
|
|
188
|
+
// Conservative rules: must be on a URL that names an API-key page
|
|
189
|
+
// (keys / api-keys / api-tokens / auth-tokens / api_keys), AND the
|
|
190
|
+
// page text shows BOTH a masking glyph pattern (•••, ***, ─•) AND
|
|
191
|
+
// an existing-key word, OR the planner's last reason explicitly
|
|
192
|
+
// describes the same shape.
|
|
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
|
+
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;
|
|
197
|
+
export function detectExistingAccountNoExtract(input) {
|
|
198
|
+
if (!EXISTING_KEY_URL_HINT.test(input.url))
|
|
199
|
+
return false;
|
|
200
|
+
// Planner reason naming the no-reveal shape is the strongest single
|
|
201
|
+
// signal — the planner has SEEN the page and is describing it.
|
|
202
|
+
if (NO_CREATE_AFFORDANCE_HINT.test(input.lastPlannerReason)) {
|
|
203
|
+
return true;
|
|
204
|
+
}
|
|
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
|
+
}
|
|
212
|
+
// Pick the next fallback URL to try from STUCK_LOOP_FALLBACK_PATHS
|
|
213
|
+
// keyed against the origin of the currently-stuck URL. Returns null
|
|
214
|
+
// when every path has already been attempted. Exported for unit tests.
|
|
215
|
+
export function pickStuckLoopFallbackUrl(currentUrl, alreadyTried) {
|
|
216
|
+
let origin;
|
|
217
|
+
try {
|
|
218
|
+
origin = new URL(currentUrl).origin;
|
|
219
|
+
}
|
|
220
|
+
catch {
|
|
221
|
+
return null;
|
|
222
|
+
}
|
|
223
|
+
// Skip a candidate when the current URL's path ALREADY matches it
|
|
224
|
+
// (case-insensitive, trailing-slash tolerant). The planner is stuck
|
|
225
|
+
// ON the page the candidate points to — navigating to the same URL
|
|
226
|
+
// again won't break the cycle, only a different path will.
|
|
227
|
+
const currentPath = (() => {
|
|
228
|
+
try {
|
|
229
|
+
return new URL(currentUrl).pathname.replace(/\/+$/, "").toLowerCase();
|
|
230
|
+
}
|
|
231
|
+
catch {
|
|
232
|
+
return "";
|
|
233
|
+
}
|
|
234
|
+
})();
|
|
235
|
+
for (const path of STUCK_LOOP_FALLBACK_PATHS) {
|
|
236
|
+
const candidate = `${origin}${path}`;
|
|
237
|
+
if (alreadyTried.has(candidate))
|
|
238
|
+
continue;
|
|
239
|
+
const candidatePath = path.replace(/\/+$/, "").toLowerCase();
|
|
240
|
+
if (candidatePath === currentPath)
|
|
241
|
+
continue;
|
|
242
|
+
return candidate;
|
|
243
|
+
}
|
|
244
|
+
return null;
|
|
245
|
+
}
|
|
132
246
|
// Best-effort canonical signup URL for a service when the caller
|
|
133
247
|
// didn't pass one. Most dev-SaaS targets (Resend, Postmark, Mailgun,
|
|
134
248
|
// MailerSend, IPInfo, Stripe, PostHog) live at <name>.com/signup —
|
|
@@ -746,6 +860,46 @@ export function detectAlreadySignedIn(args) {
|
|
|
746
860
|
})) {
|
|
747
861
|
return true;
|
|
748
862
|
}
|
|
863
|
+
// 0.8.2-rc.5 — PostHog-class onboarding wizard. When the URL is
|
|
864
|
+
// dashboard-y (path like /project/<id>/onboarding) and the page
|
|
865
|
+
// shows project-picker / account-menu / onboarding-skip
|
|
866
|
+
// affordances WITHOUT a credential input or OAuth provider button,
|
|
867
|
+
// the user is authenticated and the wizard is interstitial. The
|
|
868
|
+
// rc.3 overnight run for posthog landed exactly here and bailed
|
|
869
|
+
// `oauth_required` because the inventory had only:
|
|
870
|
+
// - "Default project" (project picker)
|
|
871
|
+
// - "BBento" (account avatar toggle)
|
|
872
|
+
// - "Hand off setup" (skip-onboarding affordance)
|
|
873
|
+
//
|
|
874
|
+
// Detect this shape via a second-tier signal set. Conservative —
|
|
875
|
+
// we already gated on "no credential inputs" and "dashboardyPath",
|
|
876
|
+
// so a true signup chooser (which has neither of those AND the
|
|
877
|
+
// path is /signup or /login) cannot reach this branch.
|
|
878
|
+
const POST_AUTH_AFFORDANCE = /^\s*(?:hand\s*off\s*setup|skip\s*(?:onboarding|setup|for\s*now)|invite\s*(?:teammates|members|your\s*team)|set\s*up\s*billing|finish\s*setup|get\s*started|continue\s*to\s*(?:dashboard|app|console))\s*$/i;
|
|
879
|
+
// Workspace / project / org picker shape. We pattern-match
|
|
880
|
+
// generously because PostHog's reads "Default project" but other
|
|
881
|
+
// SaaS dashboards read "My workspace" / "Acme org" / similar. The
|
|
882
|
+
// structural cue is "button with one of the workspace-noun words"
|
|
883
|
+
// — see TS-1923 (PostHog rc.3 regression).
|
|
884
|
+
const WORKSPACE_PICKER = /\b(?:workspace|workspaces|project(?:s)?|organization|organizations|team(?:s)?)\b/i;
|
|
885
|
+
const hasPostAuthAffordance = inventory.some((e) => POST_AUTH_AFFORDANCE.test((e.visibleText ?? e.ariaLabel ?? "").trim()));
|
|
886
|
+
if (hasPostAuthAffordance) {
|
|
887
|
+
// Single signal — the skip-onboarding / handoff verb is strong
|
|
888
|
+
// enough on its own. No login page ever offers "Hand off setup".
|
|
889
|
+
return true;
|
|
890
|
+
}
|
|
891
|
+
// Weaker pair: a workspace-picker shape AND the page lacks a
|
|
892
|
+
// primary call-to-action that reads as signup ("Continue with
|
|
893
|
+
// Google", "Sign up", etc.). Used as a backstop for SPA dashboards
|
|
894
|
+
// whose only visible buttons are picker toggles.
|
|
895
|
+
const hasWorkspacePicker = inventory.some((e) => WORKSPACE_PICKER.test((e.visibleText ?? e.ariaLabel ?? "").trim()));
|
|
896
|
+
const hasSignupOrOAuthAffordance = inventory.some((e) => {
|
|
897
|
+
const t = (e.visibleText ?? e.ariaLabel ?? "").trim();
|
|
898
|
+
return /\b(?:sign[\s-]*up|signup|continue\s+with|log\s+in\s+with|sign\s+in\s+with)\b/i.test(t);
|
|
899
|
+
});
|
|
900
|
+
if (hasWorkspacePicker && !hasSignupOrOAuthAffordance) {
|
|
901
|
+
return true;
|
|
902
|
+
}
|
|
749
903
|
}
|
|
750
904
|
return false;
|
|
751
905
|
}
|
|
@@ -1894,12 +2048,27 @@ export class SignupAgent {
|
|
|
1894
2048
|
// Parse/validation failure — includes a hallucinated selector
|
|
1895
2049
|
// rejected by the inventory check. An error replan.
|
|
1896
2050
|
const reason = err instanceof Error ? err.message : String(err);
|
|
1897
|
-
|
|
2051
|
+
// 0.8.2-rc.6 — mirror the post-verify upstream-blip carve-out.
|
|
2052
|
+
// The form-fill planner is also vulnerable to sustained
|
|
2053
|
+
// upstream-proxy degradation: the rc.3 + rc.5 batch runs
|
|
2054
|
+
// showed openrouter / resend / sentry losing 4+ consecutive
|
|
2055
|
+
// proxy calls in a row when the free-tier upstream was
|
|
2056
|
+
// throttling. Don't punt to planning_failed for upstream
|
|
2057
|
+
// weather — keep re-planning until the budget runs out at the
|
|
2058
|
+
// top-level F2 deadline, OR a true logic failure shows up.
|
|
2059
|
+
const isUpstreamBlip = /\b50[234]\b/.test(reason) ||
|
|
2060
|
+
/\bupstream_(?:error|unreachable)\b/i.test(reason) ||
|
|
2061
|
+
/\bnetwork error\b/i.test(reason);
|
|
2062
|
+
if (!isUpstreamBlip && ++errorReplans > MAX_ERROR_REPLANS) {
|
|
1898
2063
|
return { kind: "planning_failed", reason: `planner output never validated: ${reason}` };
|
|
1899
2064
|
}
|
|
1900
|
-
steps.push(
|
|
1901
|
-
|
|
1902
|
-
|
|
2065
|
+
steps.push(isUpstreamBlip
|
|
2066
|
+
? `⚠ planner request hit a transient upstream blip (${reason}) — retrying`
|
|
2067
|
+
: `⚠ plan rejected (${reason}) — re-planning`);
|
|
2068
|
+
if (!isUpstreamBlip) {
|
|
2069
|
+
hint =
|
|
2070
|
+
"Your previous plan used a selector not in the inventory. Use ONLY selectors copied verbatim from a `selector=` field.";
|
|
2071
|
+
}
|
|
1903
2072
|
continue;
|
|
1904
2073
|
}
|
|
1905
2074
|
steps.push(`Plan: ${plan.actions.length} action(s), confidence=${plan.confidence}` +
|
|
@@ -2316,7 +2485,13 @@ export class SignupAgent {
|
|
|
2316
2485
|
this.roundUploader = opts.roundUploader;
|
|
2317
2486
|
}
|
|
2318
2487
|
this.captchaSolver = opts.captchaSolver ?? new TwoCaptchaSolver();
|
|
2488
|
+
if (opts.googleChallengeTimeoutMs !== undefined) {
|
|
2489
|
+
this.googleChallengeTimeoutMs = opts.googleChallengeTimeoutMs;
|
|
2490
|
+
}
|
|
2319
2491
|
}
|
|
2492
|
+
// Default: 2 minutes — enough time for the human to unlock phone,
|
|
2493
|
+
// open the Google app, and tap a verification number.
|
|
2494
|
+
googleChallengeTimeoutMs = 120_000;
|
|
2320
2495
|
// Read-only view of how many calls landed on which backend. Exported
|
|
2321
2496
|
// through SignupResult.llm_backends so tests and ops can verify the
|
|
2322
2497
|
// dual-mode fallback is actually engaging when expected.
|
|
@@ -2338,12 +2513,22 @@ export class SignupAgent {
|
|
|
2338
2513
|
if (this.llmCallCount >= MAX_LLM_CALLS_PER_SIGNUP) {
|
|
2339
2514
|
throw new LLMCallBudgetExceeded(MAX_LLM_CALLS_PER_SIGNUP);
|
|
2340
2515
|
}
|
|
2341
|
-
|
|
2516
|
+
// 0.8.2-rc.8 — count the call only AFTER the upstream actually
|
|
2517
|
+
// replied. The old code incremented before the proxy fetch which
|
|
2518
|
+
// means a proxy 502 (caught & surfaced after the 4-attempt retry
|
|
2519
|
+
// budget exhausts) cost the same budget unit as a real planner
|
|
2520
|
+
// reply. The rc.7 sentry batch run hit this: 2 upstream-blip
|
|
2521
|
+
// retries consumed 2 of the 15 calls on top of the 9 successful
|
|
2522
|
+
// post-verify rounds — the planner ran out of budget on the 8th
|
|
2523
|
+
// permission scope, 5 short of the API key. Failed calls produce
|
|
2524
|
+
// no progress; charging them against the budget is wrong. Behave
|
|
2525
|
+
// like a meter: only count consumption that actually delivered.
|
|
2342
2526
|
const resp = await client.createMessage({
|
|
2343
2527
|
system: args.system,
|
|
2344
2528
|
user: args.userBlocks,
|
|
2345
2529
|
max_tokens: args.maxTokens,
|
|
2346
2530
|
});
|
|
2531
|
+
this.llmCallCount += 1;
|
|
2347
2532
|
this.backendsUsed.push(resp.backend);
|
|
2348
2533
|
return resp.text;
|
|
2349
2534
|
};
|
|
@@ -2679,6 +2864,34 @@ export class SignupAgent {
|
|
|
2679
2864
|
...this.resultTail(),
|
|
2680
2865
|
};
|
|
2681
2866
|
}
|
|
2867
|
+
// 0.8.2-rc.10 — same sentinel-pattern routing the runOAuthFlow
|
|
2868
|
+
// path uses. The post-verify loop sets lastPostVerifyDoneReason
|
|
2869
|
+
// with [stuck_loop] or [existing_account_no_extract] markers
|
|
2870
|
+
// when it bails on a planner-loop or pre-existing-key state;
|
|
2871
|
+
// surface those distinctly rather than as the generic
|
|
2872
|
+
// no_credentials_after_already_signed_in.
|
|
2873
|
+
if (this.lastPostVerifyDoneReason !== null &&
|
|
2874
|
+
this.lastPostVerifyDoneReason.startsWith("[stuck_loop]")) {
|
|
2875
|
+
return {
|
|
2876
|
+
success: false,
|
|
2877
|
+
error: `planner_stuck: ${task.service}'s dashboard re-picked the same step repeatedly ` +
|
|
2878
|
+
`with no inventory change and the bot's hardcoded API-key URL fallbacks did not ` +
|
|
2879
|
+
`advance the page — finish the signup manually.`,
|
|
2880
|
+
steps,
|
|
2881
|
+
...this.resultTail(),
|
|
2882
|
+
};
|
|
2883
|
+
}
|
|
2884
|
+
if (this.lastPostVerifyDoneReason !== null &&
|
|
2885
|
+
this.lastPostVerifyDoneReason.startsWith("[existing_account_no_extract]")) {
|
|
2886
|
+
return {
|
|
2887
|
+
success: false,
|
|
2888
|
+
error: `existing_account_no_extract: ${task.service}'s dashboard shows pre-existing API ` +
|
|
2889
|
+
`keys for this identity but the values are masked and unrecoverable — wipe the ` +
|
|
2890
|
+
`test identity's account on ${task.service} or sign in manually and reveal the key.`,
|
|
2891
|
+
steps,
|
|
2892
|
+
...this.resultTail(),
|
|
2893
|
+
};
|
|
2894
|
+
}
|
|
2682
2895
|
return {
|
|
2683
2896
|
success: false,
|
|
2684
2897
|
error: "no_credentials_after_already_signed_in: bot detected an authenticated dashboard " +
|
|
@@ -3325,6 +3538,41 @@ export class SignupAgent {
|
|
|
3325
3538
|
...this.resultTail(),
|
|
3326
3539
|
};
|
|
3327
3540
|
}
|
|
3541
|
+
// 0.8.2-rc.10 — planner stuck-loop, fallback URLs exhausted. The
|
|
3542
|
+
// postVerifyLoop marks this with the [stuck_loop] sentinel so the
|
|
3543
|
+
// operator sees a distinct status (it's not an "OAuth onboarding"
|
|
3544
|
+
// failure — OAuth succeeded; the planner got stuck on the
|
|
3545
|
+
// post-OAuth navigation).
|
|
3546
|
+
if (this.lastPostVerifyDoneReason !== null &&
|
|
3547
|
+
this.lastPostVerifyDoneReason.startsWith("[stuck_loop]")) {
|
|
3548
|
+
return {
|
|
3549
|
+
success: false,
|
|
3550
|
+
error: `planner_stuck: ${task.service}'s post-OAuth dashboard re-picked the same step ` +
|
|
3551
|
+
`repeatedly with no inventory change and the bot's hardcoded API-key URL fallbacks ` +
|
|
3552
|
+
`did not advance the page — finish the signup manually.`,
|
|
3553
|
+
steps,
|
|
3554
|
+
...this.resultTail(),
|
|
3555
|
+
};
|
|
3556
|
+
}
|
|
3557
|
+
// 0.8.2-rc.10 — existing-account state with no extractable
|
|
3558
|
+
// credential. The postVerifyLoop's existing-key detector
|
|
3559
|
+
// (detectExistingAccountNoExtract) classifies a run that lands on
|
|
3560
|
+
// an authenticated dashboard whose API-keys page surfaces only
|
|
3561
|
+
// masked existing keys + no path to a fresh value. Surfacing this
|
|
3562
|
+
// distinctly so the harvester can flag it (e.g. periodically wipe
|
|
3563
|
+
// the chrome profile for the test identity) rather than treat it
|
|
3564
|
+
// as a real bot failure.
|
|
3565
|
+
if (this.lastPostVerifyDoneReason !== null &&
|
|
3566
|
+
this.lastPostVerifyDoneReason.startsWith("[existing_account_no_extract]")) {
|
|
3567
|
+
return {
|
|
3568
|
+
success: false,
|
|
3569
|
+
error: `existing_account_no_extract: ${task.service}'s dashboard shows pre-existing API ` +
|
|
3570
|
+
`keys for this identity but the values are masked and unrecoverable — wipe the ` +
|
|
3571
|
+
`test identity's account on ${task.service} or sign in manually and reveal the key.`,
|
|
3572
|
+
steps,
|
|
3573
|
+
...this.resultTail(),
|
|
3574
|
+
};
|
|
3575
|
+
}
|
|
3328
3576
|
return {
|
|
3329
3577
|
success: false,
|
|
3330
3578
|
error: `oauth_onboarding_failed: signed in to ${task.service} via ${provider.label} but ` +
|
|
@@ -3351,7 +3599,7 @@ export class SignupAgent {
|
|
|
3351
3599
|
// unlock a phone, open the Google app, and tap a number; longer
|
|
3352
3600
|
// would mask a stuck/abandoned flow.
|
|
3353
3601
|
async waitForGoogleChallenge(provider, steps) {
|
|
3354
|
-
const deadline = Date.now() +
|
|
3602
|
+
const deadline = Date.now() + this.googleChallengeTimeoutMs;
|
|
3355
3603
|
while (Date.now() < deadline) {
|
|
3356
3604
|
await this.browser.wait(3);
|
|
3357
3605
|
if (this.browser.oauthPageClosed())
|
|
@@ -3605,6 +3853,15 @@ ${formatInventory(input.inventory)}`,
|
|
|
3605
3853
|
let credentials = await this.extractCredentials();
|
|
3606
3854
|
let loginAttempts = 0;
|
|
3607
3855
|
let planFailures = 0;
|
|
3856
|
+
// 0.8.2-rc.6 — separate counter for upstream-blip retries. Doesn't
|
|
3857
|
+
// gate planFailures (so a transient 502 won't push us into the
|
|
3858
|
+
// terminal stop branch after 4 rounds), but is still bounded so a
|
|
3859
|
+
// permanently-down proxy can't loop forever. Generous because each
|
|
3860
|
+
// blip costs ~5s of network + retry-backoff and the run already
|
|
3861
|
+
// has a 10-min top-level timeout — but tight enough that a truly
|
|
3862
|
+
// dead upstream doesn't burn the whole maxRounds budget on noise.
|
|
3863
|
+
let upstreamBlipRetries = 0;
|
|
3864
|
+
const MAX_UPSTREAM_BLIP_RETRIES = 8;
|
|
3608
3865
|
const oauth = args.credentials === undefined;
|
|
3609
3866
|
// Re-plan hint for the next round — set when an `extract` step
|
|
3610
3867
|
// found no key, which means the visible key text is masked /
|
|
@@ -3664,6 +3921,26 @@ ${formatInventory(input.inventory)}`,
|
|
|
3664
3921
|
// navigate produced no progress. Inject a hint forcing a CLICK
|
|
3665
3922
|
// on something visible in the current inventory.
|
|
3666
3923
|
let prevNavigateFromUrl = null;
|
|
3924
|
+
// 0.8.2-rc.10 — escalation for the stuck-loop detector.
|
|
3925
|
+
//
|
|
3926
|
+
// The existing detector injects a re-plan hint when the planner
|
|
3927
|
+
// returns the same kind+selector twice with no inventory change,
|
|
3928
|
+
// but the planner often ignores the "pick a different KIND" hint
|
|
3929
|
+
// and just picks a slightly different SELECTOR for another click.
|
|
3930
|
+
// Anthropic's batch failure (rc.8) showed 6 wasted rounds of this
|
|
3931
|
+
// before a navigate finally broke the cycle: clicking the sidebar
|
|
3932
|
+
// "API Keys" link on a dashboard that wasn't routing to it.
|
|
3933
|
+
//
|
|
3934
|
+
// Escalation strategy: after N stuck-fires within the SAME URL,
|
|
3935
|
+
// try a hard navigate to a guessed API-keys URL (one per origin).
|
|
3936
|
+
// If the URL has already advanced past the stuck zone, reset the
|
|
3937
|
+
// counter. After every fallback URL is exhausted AND we're still
|
|
3938
|
+
// stuck, mark the run [stuck_loop] so the caller surfaces the
|
|
3939
|
+
// dedicated error code instead of the generic
|
|
3940
|
+
// oauth_onboarding_failed.
|
|
3941
|
+
let stuckFiresAtUrl = 0;
|
|
3942
|
+
let lastStuckFireUrl = null;
|
|
3943
|
+
const triedFallbackUrls = new Set();
|
|
3667
3944
|
// 0.8.1 — capture chain index is independent of the planner loop
|
|
3668
3945
|
// round. The loop has two early-`continue` paths (page mid-navigation
|
|
3669
3946
|
// throw, planner-rejection re-plan) that increment `round` WITHOUT
|
|
@@ -3727,17 +4004,47 @@ ${formatInventory(input.inventory)}`,
|
|
|
3727
4004
|
// form-fill planner has. Bounded so a persistently broken
|
|
3728
4005
|
// planner still terminates.
|
|
3729
4006
|
const reason = err instanceof Error ? err.message : String(err);
|
|
3730
|
-
|
|
4007
|
+
// 0.8.2-rc.6 — distinguish upstream-blip from planner-logic
|
|
4008
|
+
// failure. The proxy retry-with-backoff (rc.5) handles most
|
|
4009
|
+
// transient 502s within a single call, but during a sustained
|
|
4010
|
+
// upstream degradation the retry budget exhausts and surfaces
|
|
4011
|
+
// here as a planFailure. Counting those toward the 4x stop
|
|
4012
|
+
// threshold is wrong — they're not the planner's fault, the
|
|
4013
|
+
// upstream is just temporarily unavailable. We allow these to
|
|
4014
|
+
// burn a round (forward progress is impossible without a
|
|
4015
|
+
// planner reply) but don't tick planFailures, so a transient
|
|
4016
|
+
// blip can't push us into the terminal stop branch.
|
|
4017
|
+
const isUpstreamBlip = /\b50[234]\b/.test(reason) ||
|
|
4018
|
+
/\bupstream_(?:error|unreachable)\b/i.test(reason) ||
|
|
4019
|
+
/\bnetwork error\b/i.test(reason);
|
|
4020
|
+
if (isUpstreamBlip) {
|
|
4021
|
+
upstreamBlipRetries += 1;
|
|
4022
|
+
if (upstreamBlipRetries > MAX_UPSTREAM_BLIP_RETRIES) {
|
|
4023
|
+
args.steps.push(`Post-verify round ${round}: upstream proxy degraded for ${upstreamBlipRetries} rounds — stopping (likely sustained outage).`);
|
|
4024
|
+
break;
|
|
4025
|
+
}
|
|
4026
|
+
}
|
|
4027
|
+
else {
|
|
4028
|
+
planFailures += 1;
|
|
4029
|
+
}
|
|
3731
4030
|
if (planFailures > 3) {
|
|
3732
4031
|
args.steps.push(`Post-verify round ${round}: planner failed ${planFailures}x (${reason}) — stopping.`);
|
|
3733
4032
|
break;
|
|
3734
4033
|
}
|
|
3735
|
-
|
|
3736
|
-
|
|
3737
|
-
|
|
3738
|
-
|
|
3739
|
-
"
|
|
3740
|
-
|
|
4034
|
+
const label = isUpstreamBlip ? "transient upstream blip" : "planner output rejected";
|
|
4035
|
+
args.steps.push(`Post-verify round ${round}: ${label} (${reason})` +
|
|
4036
|
+
(isUpstreamBlip
|
|
4037
|
+
? ` — retrying (${upstreamBlipRetries}/${MAX_UPSTREAM_BLIP_RETRIES}).`
|
|
4038
|
+
: " — re-planning."));
|
|
4039
|
+
// No re-plan hint on an upstream blip — the planner's previous
|
|
4040
|
+
// output (if any) was fine; only the request itself failed.
|
|
4041
|
+
if (!isUpstreamBlip) {
|
|
4042
|
+
hint =
|
|
4043
|
+
"Your previous step was REJECTED. A click/fill/select `selector` must be " +
|
|
4044
|
+
"EXACTLY the value after `selector=` on one inventory line — copy only that " +
|
|
4045
|
+
"value (it runs to the end of the line), never the leading `[n] tag …` part " +
|
|
4046
|
+
"and never the whole line.";
|
|
4047
|
+
}
|
|
3741
4048
|
continue;
|
|
3742
4049
|
}
|
|
3743
4050
|
// rc.22 — redact tokens before pushing to the step trail.
|
|
@@ -3942,6 +4249,59 @@ ${formatInventory(input.inventory)}`,
|
|
|
3942
4249
|
const uncheckedBoxHint = uncheckedBoxes.length > 0
|
|
3943
4250
|
? `\n\nVisible checkboxes you haven't ticked yet (often a TOS / agreement gate):\n${uncheckedBoxes.join("\n")}\n\nIssue {"kind":"check"} on any that look like agreements / required confirmations.`
|
|
3944
4251
|
: "";
|
|
4252
|
+
// 0.8.2-rc.10 — escalation. Track stuck-fires per URL so we
|
|
4253
|
+
// can switch tactics once the gentle re-plan hint has clearly
|
|
4254
|
+
// failed (the planner refuses to break the cycle on its own,
|
|
4255
|
+
// see the Anthropic six-round pattern in rc.8).
|
|
4256
|
+
if (lastStuckFireUrl === state.url) {
|
|
4257
|
+
stuckFiresAtUrl += 1;
|
|
4258
|
+
}
|
|
4259
|
+
else {
|
|
4260
|
+
stuckFiresAtUrl = 1;
|
|
4261
|
+
lastStuckFireUrl = state.url;
|
|
4262
|
+
}
|
|
4263
|
+
// After two stuck fires at the same URL, escalate to a
|
|
4264
|
+
// hardcoded /settings/keys-style navigation. Vendors almost
|
|
4265
|
+
// always have ONE canonical path; the dashboard often gates
|
|
4266
|
+
// it behind a sidebar link the planner can't reliably resolve
|
|
4267
|
+
// (Anthropic, Neon, Sentry, Mistral, …). The fallback list is
|
|
4268
|
+
// ordered most-specific first so a service whose dashboard
|
|
4269
|
+
// root happens to share /settings with the API-keys page
|
|
4270
|
+
// doesn't land short of the actual page.
|
|
4271
|
+
if (stuckFiresAtUrl >= 2) {
|
|
4272
|
+
const fallback = pickStuckLoopFallbackUrl(state.url, triedFallbackUrls);
|
|
4273
|
+
if (fallback !== null) {
|
|
4274
|
+
triedFallbackUrls.add(fallback);
|
|
4275
|
+
args.steps.push(`Post-verify: stuck-loop detected ${stuckFiresAtUrl}x at ${state.url} — escalating to a hardcoded API-key URL: ${fallback}`);
|
|
4276
|
+
try {
|
|
4277
|
+
await this.browser.goto(fallback);
|
|
4278
|
+
await this.browser.waitForInteractiveDom(5, 15_000);
|
|
4279
|
+
}
|
|
4280
|
+
catch (err) {
|
|
4281
|
+
args.steps.push(`Post-verify: stuck-loop fallback navigate failed (${err instanceof Error ? err.message : String(err)}) — continuing.`);
|
|
4282
|
+
}
|
|
4283
|
+
// Reset signature tracking so the next round starts clean
|
|
4284
|
+
// against the new URL's inventory. Don't reset
|
|
4285
|
+
// stuckFiresAtUrl here — it's keyed by URL and the URL
|
|
4286
|
+
// about to be observed will be different, which naturally
|
|
4287
|
+
// resets it on the next loop entry.
|
|
4288
|
+
prevSignature = null;
|
|
4289
|
+
prevInventorySize = -1;
|
|
4290
|
+
hint = undefined;
|
|
4291
|
+
capturedRound += 1;
|
|
4292
|
+
continue;
|
|
4293
|
+
}
|
|
4294
|
+
// Every plausible fallback URL has been tried and we're
|
|
4295
|
+
// still stuck. Mark with the [stuck_loop] sentinel so the
|
|
4296
|
+
// caller surfaces planner_stuck instead of the generic
|
|
4297
|
+
// oauth_onboarding_failed, then break out of the loop.
|
|
4298
|
+
this.lastPostVerifyDoneReason =
|
|
4299
|
+
`[stuck_loop] planner re-picked the same ${nextStep.kind} step ${stuckFiresAtUrl} times at ${state.url} with no inventory change; ` +
|
|
4300
|
+
`hardcoded API-key URL fallbacks exhausted (tried: ${[...triedFallbackUrls].join(", ") || "none"}). ` +
|
|
4301
|
+
`Latest planner reason: ${nextStep.reason}`;
|
|
4302
|
+
args.steps.push(`Post-verify: stuck-loop unresolvable — breaking out with planner_stuck.`);
|
|
4303
|
+
break;
|
|
4304
|
+
}
|
|
3945
4305
|
args.steps.push(sameSelector
|
|
3946
4306
|
? `Post-verify: no-progress detected — same ${nextStep.kind} on same selector, inventory unchanged. Re-planning instead of re-running.`
|
|
3947
4307
|
: `Post-verify: no-progress detected — successive click steps with no inventory change. Forcing a non-click action.`);
|
|
@@ -4377,6 +4737,39 @@ ${formatInventory(input.inventory)}`,
|
|
|
4377
4737
|
}
|
|
4378
4738
|
}
|
|
4379
4739
|
}
|
|
4740
|
+
// 0.8.2-rc.10 — existing-account-no-extract classifier. Runs once
|
|
4741
|
+
// at loop exit when no credential surfaced AND no more specific
|
|
4742
|
+
// marker (paywall, anti-bot, stuck_loop) was already set on
|
|
4743
|
+
// lastPostVerifyDoneReason. The test identity
|
|
4744
|
+
// (methoxine@gmail.com) accumulates real signups across batches;
|
|
4745
|
+
// re-running against the same vendor lands the bot on an
|
|
4746
|
+
// authenticated dashboard whose API-keys page shows a masked
|
|
4747
|
+
// pre-existing key it cannot reveal (most vendors only show the
|
|
4748
|
+
// key value once at create-time). Reporting these as
|
|
4749
|
+
// oauth_onboarding_failed is misleading — the bot did navigate
|
|
4750
|
+
// correctly, the state is just unrecoverable for this identity.
|
|
4751
|
+
const alreadyClassified = this.lastPostVerifyDoneReason !== null &&
|
|
4752
|
+
this.lastPostVerifyDoneReason.startsWith("[");
|
|
4753
|
+
if (credentials.api_key === undefined &&
|
|
4754
|
+
credentials.username === undefined &&
|
|
4755
|
+
!alreadyClassified) {
|
|
4756
|
+
try {
|
|
4757
|
+
const finalState = await this.browser.getState();
|
|
4758
|
+
const finalText = await this.browser.extractText().catch(() => "");
|
|
4759
|
+
if (detectExistingAccountNoExtract({
|
|
4760
|
+
url: finalState.url,
|
|
4761
|
+
pageText: finalText,
|
|
4762
|
+
lastPlannerReason: this.lastPostVerifyDoneReason ?? "",
|
|
4763
|
+
})) {
|
|
4764
|
+
this.lastPostVerifyDoneReason =
|
|
4765
|
+
`[existing_account_no_extract] at ${finalState.url}; latest planner reason: ${this.lastPostVerifyDoneReason ?? "(none — loop exhausted)"}`;
|
|
4766
|
+
args.steps.push("Post-verify: classified as existing_account_no_extract — masked pre-existing key on an authenticated dashboard.");
|
|
4767
|
+
}
|
|
4768
|
+
}
|
|
4769
|
+
catch {
|
|
4770
|
+
// best-effort classifier — never block returning the (empty) credentials
|
|
4771
|
+
}
|
|
4772
|
+
}
|
|
4380
4773
|
return credentials;
|
|
4381
4774
|
}
|
|
4382
4775
|
// Sign in with the credentials created during signup, so the
|