usertester 0.1.0
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/LICENSE +21 -0
- package/README.md +219 -0
- package/dist/browser/agent.d.ts +33 -0
- package/dist/browser/agent.js +393 -0
- package/dist/browser/agent.js.map +1 -0
- package/dist/cli/cleanup.d.ts +5 -0
- package/dist/cli/cleanup.js +75 -0
- package/dist/cli/cleanup.js.map +1 -0
- package/dist/cli/harness.d.ts +10 -0
- package/dist/cli/harness.js +108 -0
- package/dist/cli/harness.js.map +1 -0
- package/dist/cli/index.d.ts +5 -0
- package/dist/cli/index.js +31 -0
- package/dist/cli/index.js.map +1 -0
- package/dist/cli/kill.d.ts +5 -0
- package/dist/cli/kill.js +46 -0
- package/dist/cli/kill.js.map +1 -0
- package/dist/cli/logs.d.ts +5 -0
- package/dist/cli/logs.js +64 -0
- package/dist/cli/logs.js.map +1 -0
- package/dist/cli/profiles.d.ts +5 -0
- package/dist/cli/profiles.js +67 -0
- package/dist/cli/profiles.js.map +1 -0
- package/dist/cli/send.d.ts +5 -0
- package/dist/cli/send.js +46 -0
- package/dist/cli/send.js.map +1 -0
- package/dist/cli/setup.d.ts +6 -0
- package/dist/cli/setup.js +168 -0
- package/dist/cli/setup.js.map +1 -0
- package/dist/cli/spawn.d.ts +5 -0
- package/dist/cli/spawn.js +52 -0
- package/dist/cli/spawn.js.map +1 -0
- package/dist/cli/status.d.ts +5 -0
- package/dist/cli/status.js +85 -0
- package/dist/cli/status.js.map +1 -0
- package/dist/harness/applier.d.ts +38 -0
- package/dist/harness/applier.js +152 -0
- package/dist/harness/applier.js.map +1 -0
- package/dist/harness/index.d.ts +14 -0
- package/dist/harness/index.js +110 -0
- package/dist/harness/index.js.map +1 -0
- package/dist/harness/patterns.d.ts +14 -0
- package/dist/harness/patterns.js +96 -0
- package/dist/harness/patterns.js.map +1 -0
- package/dist/harness/proposer.d.ts +26 -0
- package/dist/harness/proposer.js +181 -0
- package/dist/harness/proposer.js.map +1 -0
- package/dist/harness/traces.d.ts +29 -0
- package/dist/harness/traces.js +65 -0
- package/dist/harness/traces.js.map +1 -0
- package/dist/harness/validator.d.ts +6 -0
- package/dist/harness/validator.js +112 -0
- package/dist/harness/validator.js.map +1 -0
- package/dist/inbox/agentmail.d.ts +11 -0
- package/dist/inbox/agentmail.js +36 -0
- package/dist/inbox/agentmail.js.map +1 -0
- package/dist/llm/provider.d.ts +15 -0
- package/dist/llm/provider.js +65 -0
- package/dist/llm/provider.js.map +1 -0
- package/dist/orchestrator/agent.d.ts +17 -0
- package/dist/orchestrator/agent.js +195 -0
- package/dist/orchestrator/agent.js.map +1 -0
- package/dist/orchestrator/index.d.ts +7 -0
- package/dist/orchestrator/index.js +92 -0
- package/dist/orchestrator/index.js.map +1 -0
- package/dist/orchestrator/retry.d.ts +27 -0
- package/dist/orchestrator/retry.js +145 -0
- package/dist/orchestrator/retry.js.map +1 -0
- package/dist/orchestrator/session.d.ts +13 -0
- package/dist/orchestrator/session.js +55 -0
- package/dist/orchestrator/session.js.map +1 -0
- package/dist/output/events.d.ts +12 -0
- package/dist/output/events.js +81 -0
- package/dist/output/events.js.map +1 -0
- package/dist/profiles/learner.d.ts +4 -0
- package/dist/profiles/learner.js +168 -0
- package/dist/profiles/learner.js.map +1 -0
- package/dist/tools/captcha.d.ts +19 -0
- package/dist/tools/captcha.js +76 -0
- package/dist/tools/captcha.js.map +1 -0
- package/dist/tools/inbox.d.ts +30 -0
- package/dist/tools/inbox.js +65 -0
- package/dist/tools/inbox.js.map +1 -0
- package/dist/types.d.ts +121 -0
- package/dist/types.js +30 -0
- package/dist/types.js.map +1 -0
- package/package.json +60 -0
- package/tasks.example.json +5 -0
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Autonomous retry loop with failure classification.
|
|
3
|
+
*
|
|
4
|
+
* After each failed agent.execute() call:
|
|
5
|
+
* 1. Classifies WHY it failed (TRANSIENT / WRONG_APPROACH / CAPABILITY_GAP / ENVIRONMENT_BLOCK)
|
|
6
|
+
* 2. Decides recovery strategy
|
|
7
|
+
* 3. Retries with adjusted instruction or new tools injected
|
|
8
|
+
* 4. Stops when: completed, or max attempts reached
|
|
9
|
+
*
|
|
10
|
+
* Based on: Meta-Harness (arxiv:2603.28052) + Live-SWE-agent (arxiv:2511.13646)
|
|
11
|
+
* Key principle: raw trace beats summaries for classification (never summarize before classifying)
|
|
12
|
+
*/
|
|
13
|
+
import { cheapCall } from '../llm/provider.js';
|
|
14
|
+
import { readInboxEmail } from '../tools/inbox.js';
|
|
15
|
+
import { solveTurnstile, capsolverAvailable } from '../tools/captcha.js';
|
|
16
|
+
export const MAX_ATTEMPTS = 5;
|
|
17
|
+
// Signal patterns that map to failure types
|
|
18
|
+
export const FAILURE_SIGNALS = [
|
|
19
|
+
{
|
|
20
|
+
pattern: /DNS|ERR_NAME_NOT_RESOLVED|net::ERR|could not resolve|unreachable/i,
|
|
21
|
+
type: 'WRONG_APPROACH',
|
|
22
|
+
hint: 'Do not navigate to external web UIs. Use available API tools instead.',
|
|
23
|
+
},
|
|
24
|
+
{
|
|
25
|
+
pattern: /verification code|6.digit|magic link|check.*email|inbox|sent.*code/i,
|
|
26
|
+
type: 'CAPABILITY_GAP',
|
|
27
|
+
hint: 'Use the readInboxEmail tool to retrieve the verification code from the email inbox.',
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
pattern: /only request this after (\d+)|too many requests|rate.?limit|429|resend.*after/i,
|
|
31
|
+
type: 'RATE_LIMITED',
|
|
32
|
+
hint: 'Rate limited — wait the specified cooldown period before retrying.',
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
pattern: /timeout|503|temporarily unavailable|connection failed/i,
|
|
36
|
+
type: 'TRANSIENT',
|
|
37
|
+
hint: 'Transient error — retry the same approach after a short wait.',
|
|
38
|
+
},
|
|
39
|
+
{
|
|
40
|
+
pattern: /captcha|CAPTCHA|bot detection|unusual traffic/i,
|
|
41
|
+
type: 'ENVIRONMENT_BLOCK',
|
|
42
|
+
hint: 'CAPTCHA detected — cannot automate this step.',
|
|
43
|
+
},
|
|
44
|
+
];
|
|
45
|
+
export async function classifyFailure(agentMessage, config) {
|
|
46
|
+
// Fast path: check signal patterns first (no LLM needed)
|
|
47
|
+
for (const { pattern, type, hint } of FAILURE_SIGNALS) {
|
|
48
|
+
if (pattern.test(agentMessage)) {
|
|
49
|
+
return { type, evidence: agentMessage.slice(0, 200), recoveryHint: hint };
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
// Slow path: ask the cheap model to classify
|
|
53
|
+
const prompt = `You are classifying why a browser automation agent failed.
|
|
54
|
+
|
|
55
|
+
Agent failure message:
|
|
56
|
+
${agentMessage.slice(0, 800)}
|
|
57
|
+
|
|
58
|
+
Classify into exactly one of:
|
|
59
|
+
- COMPLETE: the task actually succeeded (agent was wrong about failing)
|
|
60
|
+
- TRANSIENT: network timeout, rate limit, temporary error — retry same approach
|
|
61
|
+
- WRONG_APPROACH: agent used wrong method (e.g. navigated to web UI instead of using API)
|
|
62
|
+
- CAPABILITY_GAP: agent knew what it needed but lacked a tool to do it
|
|
63
|
+
- ENVIRONMENT_BLOCK: CAPTCHA, auth wall, or structural blocker — cannot automate
|
|
64
|
+
- ESCALATE: unclear or unrecoverable
|
|
65
|
+
|
|
66
|
+
Reply with JSON: {"type": "...", "evidence": "one sentence", "recoveryHint": "one sentence"}`;
|
|
67
|
+
try {
|
|
68
|
+
const text = await cheapCall(prompt, config, 150);
|
|
69
|
+
const match = text.match(/\{[\s\S]*\}/);
|
|
70
|
+
if (match) {
|
|
71
|
+
const parsed = JSON.parse(match[0]);
|
|
72
|
+
return {
|
|
73
|
+
type: parsed.type,
|
|
74
|
+
evidence: parsed.evidence ?? '',
|
|
75
|
+
recoveryHint: parsed.recoveryHint ?? '',
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
catch { }
|
|
80
|
+
return { type: 'ESCALATE', evidence: agentMessage.slice(0, 100), recoveryHint: 'Manual intervention needed' };
|
|
81
|
+
}
|
|
82
|
+
// Build a ToolSet to inject based on failure classification
|
|
83
|
+
export function selectToolsForRecovery(classification) {
|
|
84
|
+
const tools = {};
|
|
85
|
+
if (classification.type === 'CAPABILITY_GAP' ||
|
|
86
|
+
(classification.type === 'WRONG_APPROACH' && /email|inbox|code|verification/i.test(classification.evidence))) {
|
|
87
|
+
tools['readInboxEmail'] = readInboxEmail;
|
|
88
|
+
}
|
|
89
|
+
// ENVIRONMENT_BLOCK from CAPTCHA: inject solver if CapSolver is configured
|
|
90
|
+
if (classification.type === 'ENVIRONMENT_BLOCK' &&
|
|
91
|
+
/captcha|turnstile|cloudflare|verify.*human/i.test(classification.evidence) &&
|
|
92
|
+
capsolverAvailable()) {
|
|
93
|
+
tools['solveTurnstile'] = solveTurnstile;
|
|
94
|
+
}
|
|
95
|
+
return tools;
|
|
96
|
+
}
|
|
97
|
+
// Build a constraint addendum to inject into the next attempt's instruction
|
|
98
|
+
export function buildRetryInstruction(originalInstruction, history, memory, currentUrl) {
|
|
99
|
+
// --- Recovery tip takes priority over failure constraints ---
|
|
100
|
+
if (memory?.recoveryTips?.length && currentUrl) {
|
|
101
|
+
const tip = memory.recoveryTips.find(t => {
|
|
102
|
+
try {
|
|
103
|
+
return currentUrl.includes(new URL(t.url).hostname) || t.url.includes(currentUrl);
|
|
104
|
+
}
|
|
105
|
+
catch {
|
|
106
|
+
return false;
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
if (tip) {
|
|
110
|
+
return [
|
|
111
|
+
`App URL: ${currentUrl} — navigate here if the page is blank or shows an error.`,
|
|
112
|
+
``,
|
|
113
|
+
`PROVEN APPROACH FOR THIS APP:`,
|
|
114
|
+
`The following approach previously succeeded (tools: ${tip.toolsUsed.join(', ') || 'none'}):`,
|
|
115
|
+
`"${tip.successApproach}"`,
|
|
116
|
+
``,
|
|
117
|
+
`REPEAT this approach exactly. Ignore any previous context suggesting otherwise.`,
|
|
118
|
+
``,
|
|
119
|
+
`Task: ${originalInstruction}`,
|
|
120
|
+
].join('\n');
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
// --- Fallback: accumulate failure constraints (existing behavior) ---
|
|
124
|
+
if (history.length === 0)
|
|
125
|
+
return originalInstruction;
|
|
126
|
+
const constraints = history
|
|
127
|
+
.filter(a => a.failureType && a.failureType !== 'TRANSIENT' && a.failureType !== 'COMPLETE')
|
|
128
|
+
.map((a) => `Attempt ${a.attempt} failed (${a.failureType}): ${a.agentMessage.slice(0, 150)}`);
|
|
129
|
+
if (constraints.length === 0)
|
|
130
|
+
return originalInstruction;
|
|
131
|
+
const toolHints = history.some(a => a.toolsInjected.includes('readInboxEmail'))
|
|
132
|
+
? '\n\nIMPORTANT: You have a readInboxEmail tool available. Use it to read any verification emails — do NOT try to navigate to a web-based inbox.'
|
|
133
|
+
: '';
|
|
134
|
+
// Always pin the URL so the agent never guesses if navigation fails
|
|
135
|
+
const urlPin = currentUrl ? `\nApp URL: ${currentUrl} — always navigate here first if the page is blank or shows an error.` : '';
|
|
136
|
+
return [
|
|
137
|
+
originalInstruction,
|
|
138
|
+
urlPin,
|
|
139
|
+
'',
|
|
140
|
+
'--- Previous attempt context ---',
|
|
141
|
+
...constraints,
|
|
142
|
+
toolHints,
|
|
143
|
+
].join('\n');
|
|
144
|
+
}
|
|
145
|
+
//# sourceMappingURL=retry.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"retry.js","sourceRoot":"","sources":["../../src/orchestrator/retry.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import type { SessionState, AgentState, AgentStatus } from '../types.js';
|
|
2
|
+
export declare function createSession(opts: {
|
|
3
|
+
resultsDir: string;
|
|
4
|
+
sessionId: string;
|
|
5
|
+
url: string;
|
|
6
|
+
agentIds: string[];
|
|
7
|
+
}): SessionState;
|
|
8
|
+
export declare function getStatePath(resultsDir: string, sessionId: string): string;
|
|
9
|
+
export declare function saveSession(resultsDir: string, state: SessionState): void;
|
|
10
|
+
export declare function loadSession(resultsDir: string, sessionId: string): SessionState | null;
|
|
11
|
+
export declare function updateAgent(state: SessionState, agentId: string, updates: Partial<AgentState>): SessionState;
|
|
12
|
+
export declare function transitionAgent(state: SessionState, agentId: string, to: AgentStatus, extras?: Partial<AgentState>): SessionState;
|
|
13
|
+
export declare function resolveSessionId(resultsDir: string, sessionId?: string): string | null;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Session state management
|
|
3
|
+
* Reads/writes ~/.usertester/<session-id>/state.json (atomic)
|
|
4
|
+
*/
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import fs from 'node:fs';
|
|
7
|
+
import { writeStateAtomic, readState, getSessionDir } from '../output/events.js';
|
|
8
|
+
export function createSession(opts) {
|
|
9
|
+
const agents = opts.agentIds.map(id => ({
|
|
10
|
+
id,
|
|
11
|
+
status: 'QUEUED',
|
|
12
|
+
updatedAt: Date.now(),
|
|
13
|
+
retryCount: 0,
|
|
14
|
+
}));
|
|
15
|
+
return {
|
|
16
|
+
sessionId: opts.sessionId,
|
|
17
|
+
url: opts.url,
|
|
18
|
+
agents,
|
|
19
|
+
startedAt: Date.now(),
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export function getStatePath(resultsDir, sessionId) {
|
|
23
|
+
return path.join(getSessionDir(resultsDir, sessionId), 'state.json');
|
|
24
|
+
}
|
|
25
|
+
export function saveSession(resultsDir, state) {
|
|
26
|
+
writeStateAtomic(getStatePath(resultsDir, state.sessionId), state);
|
|
27
|
+
}
|
|
28
|
+
export function loadSession(resultsDir, sessionId) {
|
|
29
|
+
return readState(getStatePath(resultsDir, sessionId));
|
|
30
|
+
}
|
|
31
|
+
export function updateAgent(state, agentId, updates) {
|
|
32
|
+
return {
|
|
33
|
+
...state,
|
|
34
|
+
agents: state.agents.map(a => a.id === agentId
|
|
35
|
+
? { ...a, ...updates, updatedAt: Date.now() }
|
|
36
|
+
: a),
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export function transitionAgent(state, agentId, to, extras) {
|
|
40
|
+
return updateAgent(state, agentId, { status: to, ...extras });
|
|
41
|
+
}
|
|
42
|
+
export function resolveSessionId(resultsDir, sessionId) {
|
|
43
|
+
if (sessionId)
|
|
44
|
+
return sessionId;
|
|
45
|
+
// Read current symlink
|
|
46
|
+
try {
|
|
47
|
+
const currentLink = path.join(resultsDir, 'current');
|
|
48
|
+
const target = fs.readlinkSync(currentLink);
|
|
49
|
+
return path.basename(target);
|
|
50
|
+
}
|
|
51
|
+
catch {
|
|
52
|
+
return null;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=session.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"session.js","sourceRoot":"","sources":["../../src/orchestrator/session.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,MAAM,SAAS,CAAA;AAExB,OAAO,EAAE,gBAAgB,EAAE,SAAS,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAA;AAEhF,MAAM,UAAU,aAAa,CAAC,IAK7B;IACC,MAAM,MAAM,GAAiB,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC;QACpD,EAAE;QACF,MAAM,EAAE,QAAQ;QAChB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;QACrB,UAAU,EAAE,CAAC;KACd,CAAC,CAAC,CAAA;IAEH,OAAO;QACL,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,GAAG,EAAE,IAAI,CAAC,GAAG;QACb,MAAM;QACN,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;KACtB,CAAA;AACH,CAAC;AAED,MAAM,UAAU,YAAY,CAAC,UAAkB,EAAE,SAAiB;IAChE,OAAO,IAAI,CAAC,IAAI,CAAC,aAAa,CAAC,UAAU,EAAE,SAAS,CAAC,EAAE,YAAY,CAAC,CAAA;AACtE,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,UAAkB,EAAE,KAAmB;IACjE,gBAAgB,CAAC,YAAY,CAAC,UAAU,EAAE,KAAK,CAAC,SAAS,CAAC,EAAE,KAAK,CAAC,CAAA;AACpE,CAAC;AAED,MAAM,UAAU,WAAW,CAAC,UAAkB,EAAE,SAAiB;IAC/D,OAAO,SAAS,CAAe,YAAY,CAAC,UAAU,EAAE,SAAS,CAAC,CAAC,CAAA;AACrE,CAAC;AAED,MAAM,UAAU,WAAW,CACzB,KAAmB,EACnB,OAAe,EACf,OAA4B;IAE5B,OAAO;QACL,GAAG,KAAK;QACR,MAAM,EAAE,KAAK,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAC3B,CAAC,CAAC,EAAE,KAAK,OAAO;YACd,CAAC,CAAC,EAAE,GAAG,CAAC,EAAE,GAAG,OAAO,EAAE,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE,EAAE;YAC7C,CAAC,CAAC,CAAC,CACN;KACF,CAAA;AACH,CAAC;AAED,MAAM,UAAU,eAAe,CAC7B,KAAmB,EACnB,OAAe,EACf,EAAe,EACf,MAA4B;IAE5B,OAAO,WAAW,CAAC,KAAK,EAAE,OAAO,EAAE,EAAE,MAAM,EAAE,EAAE,EAAE,GAAG,MAAM,EAAE,CAAC,CAAA;AAC/D,CAAC;AAED,MAAM,UAAU,gBAAgB,CAAC,UAAkB,EAAE,SAAkB;IACrE,IAAI,SAAS;QAAE,OAAO,SAAS,CAAA;IAC/B,uBAAuB;IACvB,IAAI,CAAC;QACH,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,SAAS,CAAC,CAAA;QACpD,MAAM,MAAM,GAAG,EAAE,CAAC,YAAY,CAAC,WAAW,CAAW,CAAA;QACrD,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAA;IAC9B,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import type { UsertesterEvent } from '../types.js';
|
|
2
|
+
export declare function emitEvent(event: UsertesterEvent): void;
|
|
3
|
+
export declare function ts(): string;
|
|
4
|
+
export declare function appendAgentEvent(agentDir: string, data: Record<string, unknown>): void;
|
|
5
|
+
export declare function appendAgentLog(agentDir: string, message: string): void;
|
|
6
|
+
export declare function writeStateAtomic(statePath: string, state: unknown): void;
|
|
7
|
+
export declare function readState<T>(statePath: string): T | null;
|
|
8
|
+
export declare function readPendingCommand(commandsPath: string): import('../types.js').AgentCommand | null;
|
|
9
|
+
export declare function writeCommand(commandsPath: string, cmd: import('../types.js').AgentCommand): void;
|
|
10
|
+
export declare function getSessionDir(resultsDir: string, sessionId: string): string;
|
|
11
|
+
export declare function getAgentDir(sessionDir: string, agentId: string): string;
|
|
12
|
+
export declare function initSessionDirs(resultsDir: string, sessionId: string, agentIds: string[]): void;
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* NDJSON event emitter + per-agent event log writer
|
|
3
|
+
*/
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
export function emitEvent(event) {
|
|
7
|
+
process.stdout.write(JSON.stringify(event) + '\n');
|
|
8
|
+
}
|
|
9
|
+
export function ts() {
|
|
10
|
+
return new Date().toISOString();
|
|
11
|
+
}
|
|
12
|
+
// --- Per-agent event log (push model) ---
|
|
13
|
+
export function appendAgentEvent(agentDir, data) {
|
|
14
|
+
const logPath = path.join(agentDir, 'events.ndjson');
|
|
15
|
+
fs.appendFileSync(logPath, JSON.stringify({ ...data, ts: ts() }) + '\n');
|
|
16
|
+
}
|
|
17
|
+
export function appendAgentLog(agentDir, message) {
|
|
18
|
+
const logPath = path.join(agentDir, 'agent.log');
|
|
19
|
+
fs.appendFileSync(logPath, `[${ts()}] ${message}\n`);
|
|
20
|
+
}
|
|
21
|
+
// --- State file (atomic write) ---
|
|
22
|
+
export function writeStateAtomic(statePath, state) {
|
|
23
|
+
const tmp = statePath + '.tmp';
|
|
24
|
+
fs.writeFileSync(tmp, JSON.stringify(state, null, 2));
|
|
25
|
+
fs.renameSync(tmp, statePath);
|
|
26
|
+
}
|
|
27
|
+
export function readState(statePath) {
|
|
28
|
+
try {
|
|
29
|
+
return JSON.parse(fs.readFileSync(statePath, 'utf-8'));
|
|
30
|
+
}
|
|
31
|
+
catch {
|
|
32
|
+
return null;
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
// --- Command mailbox ---
|
|
36
|
+
export function readPendingCommand(commandsPath) {
|
|
37
|
+
try {
|
|
38
|
+
const content = fs.readFileSync(commandsPath, 'utf-8').trim();
|
|
39
|
+
if (!content)
|
|
40
|
+
return null;
|
|
41
|
+
const lines = content.split('\n').filter(Boolean);
|
|
42
|
+
if (lines.length === 0)
|
|
43
|
+
return null;
|
|
44
|
+
// Read last command
|
|
45
|
+
const cmd = JSON.parse(lines[lines.length - 1]);
|
|
46
|
+
// Clear the file after reading
|
|
47
|
+
fs.writeFileSync(commandsPath, '');
|
|
48
|
+
return cmd;
|
|
49
|
+
}
|
|
50
|
+
catch {
|
|
51
|
+
return null;
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
export function writeCommand(commandsPath, cmd) {
|
|
55
|
+
fs.appendFileSync(commandsPath, JSON.stringify(cmd) + '\n');
|
|
56
|
+
}
|
|
57
|
+
// --- Session dir layout ---
|
|
58
|
+
export function getSessionDir(resultsDir, sessionId) {
|
|
59
|
+
return path.join(resultsDir, sessionId);
|
|
60
|
+
}
|
|
61
|
+
export function getAgentDir(sessionDir, agentId) {
|
|
62
|
+
return path.join(sessionDir, agentId);
|
|
63
|
+
}
|
|
64
|
+
export function initSessionDirs(resultsDir, sessionId, agentIds) {
|
|
65
|
+
const sessionDir = getSessionDir(resultsDir, sessionId);
|
|
66
|
+
fs.mkdirSync(sessionDir, { recursive: true });
|
|
67
|
+
for (const agentId of agentIds) {
|
|
68
|
+
const agentDir = getAgentDir(sessionDir, agentId);
|
|
69
|
+
fs.mkdirSync(path.join(agentDir, 'screenshots'), { recursive: true });
|
|
70
|
+
// Create empty command mailbox
|
|
71
|
+
fs.writeFileSync(path.join(agentDir, 'commands.ndjson'), '');
|
|
72
|
+
}
|
|
73
|
+
// Symlink ~/.usertester/current → latest session
|
|
74
|
+
const currentLink = path.join(resultsDir, 'current');
|
|
75
|
+
try {
|
|
76
|
+
fs.unlinkSync(currentLink);
|
|
77
|
+
}
|
|
78
|
+
catch { }
|
|
79
|
+
fs.symlinkSync(sessionDir, currentLink);
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=events.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"events.js","sourceRoot":"","sources":["../../src/output/events.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,4 @@
|
|
|
1
|
+
import type { ProfileFacts, SessionMemory, RecoveryTip } from '../types.js';
|
|
2
|
+
export declare function loadProfile(resultsDir: string, url: string, scenario: string): ProfileFacts | null;
|
|
3
|
+
export declare function updateProfile(resultsDir: string, url: string, scenario: string, memory: SessionMemory): Promise<void>;
|
|
4
|
+
export declare function updateProfileWithSuccess(resultsDir: string, tip: RecoveryTip): Promise<void>;
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Profile meta-learning system
|
|
3
|
+
*
|
|
4
|
+
* After each session, extract reusable observations from session memory
|
|
5
|
+
* and persist them as facts.json per (url, scenario).
|
|
6
|
+
*
|
|
7
|
+
* Next run: agent loads these hints via BrowserAgent.start(profileFacts).
|
|
8
|
+
* Repeated failures become high-confidence hints ("avoid CAPTCHA on form submit").
|
|
9
|
+
*
|
|
10
|
+
* Inspired by the meta-harness paper (arxiv:2603.28052): post-session outer loop
|
|
11
|
+
* that reads execution traces and updates the harness config.
|
|
12
|
+
*/
|
|
13
|
+
import path from 'node:path';
|
|
14
|
+
import fs from 'node:fs';
|
|
15
|
+
import { cheapCall } from '../llm/provider.js';
|
|
16
|
+
const PROFILES_DIR_NAME = 'profiles';
|
|
17
|
+
function profileKey(url, scenario) {
|
|
18
|
+
// Stable key from url domain + scenario
|
|
19
|
+
const domain = new URL(url).hostname.replace(/\./g, '_');
|
|
20
|
+
return `${domain}_${scenario}`;
|
|
21
|
+
}
|
|
22
|
+
function getProfilePath(resultsDir, url, scenario) {
|
|
23
|
+
const profilesDir = path.join(resultsDir, PROFILES_DIR_NAME);
|
|
24
|
+
fs.mkdirSync(profilesDir, { recursive: true });
|
|
25
|
+
return path.join(profilesDir, `${profileKey(url, scenario)}.json`);
|
|
26
|
+
}
|
|
27
|
+
export function loadProfile(resultsDir, url, scenario) {
|
|
28
|
+
const profilePath = getProfilePath(resultsDir, url, scenario);
|
|
29
|
+
try {
|
|
30
|
+
return JSON.parse(fs.readFileSync(profilePath, 'utf-8'));
|
|
31
|
+
}
|
|
32
|
+
catch {
|
|
33
|
+
return null;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export async function updateProfile(resultsDir, url, scenario, memory) {
|
|
37
|
+
const profilePath = getProfilePath(resultsDir, url, scenario);
|
|
38
|
+
const existing = loadProfile(resultsDir, url, scenario) ?? {
|
|
39
|
+
url,
|
|
40
|
+
scenario,
|
|
41
|
+
harnessHints: [],
|
|
42
|
+
runCount: 0,
|
|
43
|
+
lastRunAt: Date.now(),
|
|
44
|
+
};
|
|
45
|
+
const failedActions = memory.actions.filter(a => a.result === 'failed');
|
|
46
|
+
if (failedActions.length === 0 && existing.harnessHints.length > 0) {
|
|
47
|
+
// Perfect run — bump confidence on existing hints, update run count
|
|
48
|
+
existing.runCount++;
|
|
49
|
+
existing.lastRunAt = Date.now();
|
|
50
|
+
existing.harnessHints = existing.harnessHints.map(h => ({
|
|
51
|
+
...h,
|
|
52
|
+
confidence: Math.min(1.0, h.confidence + 0.1),
|
|
53
|
+
}));
|
|
54
|
+
writeProfileAtomic(profilePath, existing);
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
// Extract new hints from failures using LLM
|
|
58
|
+
const failuresStr = failedActions
|
|
59
|
+
.slice(0, 10)
|
|
60
|
+
.map(a => `- ${a.action}: ${a.observation}`)
|
|
61
|
+
.join('\n');
|
|
62
|
+
let newHints = [];
|
|
63
|
+
try {
|
|
64
|
+
const responseText = await cheapCall(`You are analyzing failed browser automation actions to extract reusable hints for future runs.
|
|
65
|
+
|
|
66
|
+
URL: ${url}
|
|
67
|
+
Scenario: ${scenario}
|
|
68
|
+
Failed actions:
|
|
69
|
+
${failuresStr}
|
|
70
|
+
|
|
71
|
+
Extract 1-3 brief, actionable hints that would help future automation avoid these failures.
|
|
72
|
+
Each hint should be a concrete observation (e.g., "CAPTCHA appears after 3rd form submit attempt").
|
|
73
|
+
Reply with JSON array: [{"observation": "...", "confidence": 0.7}]
|
|
74
|
+
Only include hints where confidence >= 0.5.`, undefined, 400);
|
|
75
|
+
const match = responseText.match(/\[[\s\S]*\]/);
|
|
76
|
+
if (match) {
|
|
77
|
+
const parsed = JSON.parse(match[0]);
|
|
78
|
+
newHints = parsed.map(h => ({
|
|
79
|
+
observation: h.observation,
|
|
80
|
+
confidence: Math.min(1.0, Math.max(0.0, h.confidence)),
|
|
81
|
+
addedAt: Date.now(),
|
|
82
|
+
}));
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
// Learner failures are non-fatal
|
|
87
|
+
}
|
|
88
|
+
// Merge: update confidence on existing similar hints, add new ones
|
|
89
|
+
const updated = mergeHints(existing.harnessHints, newHints);
|
|
90
|
+
const profile = {
|
|
91
|
+
...existing,
|
|
92
|
+
harnessHints: updated,
|
|
93
|
+
runCount: existing.runCount + 1,
|
|
94
|
+
lastRunAt: Date.now(),
|
|
95
|
+
};
|
|
96
|
+
writeProfileAtomic(profilePath, profile);
|
|
97
|
+
}
|
|
98
|
+
function mergeHints(existing, newHints) {
|
|
99
|
+
const merged = [...existing];
|
|
100
|
+
for (const hint of newHints) {
|
|
101
|
+
// Check for similar existing hint (simple substring match)
|
|
102
|
+
const similar = merged.findIndex(h => h.observation.toLowerCase().includes(hint.observation.toLowerCase().slice(0, 20)));
|
|
103
|
+
if (similar >= 0) {
|
|
104
|
+
// Boost confidence
|
|
105
|
+
merged[similar] = {
|
|
106
|
+
...merged[similar],
|
|
107
|
+
confidence: Math.min(1.0, merged[similar].confidence + 0.15),
|
|
108
|
+
};
|
|
109
|
+
}
|
|
110
|
+
else {
|
|
111
|
+
merged.push(hint);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
// Keep top 10 by confidence, prune low-confidence stale hints
|
|
115
|
+
return merged
|
|
116
|
+
.filter(h => h.confidence > 0.3)
|
|
117
|
+
.sort((a, b) => b.confidence - a.confidence)
|
|
118
|
+
.slice(0, 10);
|
|
119
|
+
}
|
|
120
|
+
export async function updateProfileWithSuccess(resultsDir, tip) {
|
|
121
|
+
const profilePath = getProfilePath(resultsDir, tip.url, tip.scenario);
|
|
122
|
+
const existing = loadProfile(resultsDir, tip.url, tip.scenario) ?? {
|
|
123
|
+
url: tip.url,
|
|
124
|
+
scenario: tip.scenario,
|
|
125
|
+
harnessHints: [],
|
|
126
|
+
runCount: 0,
|
|
127
|
+
lastRunAt: Date.now(),
|
|
128
|
+
};
|
|
129
|
+
// MemCollab intersection: discard hints that contradict the proven approach
|
|
130
|
+
// A hint contradicts if it recommends an approach NOT used in the success
|
|
131
|
+
const filteredHints = existing.harnessHints.filter(hint => {
|
|
132
|
+
// Discard hints that recommend approaches explicitly absent from success
|
|
133
|
+
const contradicts = hint.observation.toLowerCase().includes('password instead') &&
|
|
134
|
+
!tip.successApproach.toLowerCase().includes('password');
|
|
135
|
+
return !contradicts;
|
|
136
|
+
});
|
|
137
|
+
// Add recovery tip as top-priority hint
|
|
138
|
+
const recoveryHint = {
|
|
139
|
+
observation: `PROVEN APPROACH: ${tip.successApproach.slice(0, 300)}. Tools that worked: ${tip.toolsUsed.join(', ') || 'none'}.`,
|
|
140
|
+
confidence: 0.97,
|
|
141
|
+
addedAt: tip.ts,
|
|
142
|
+
};
|
|
143
|
+
// Merge: recovery tip first, then filtered existing hints, deduped
|
|
144
|
+
const merged = [recoveryHint, ...filteredHints]
|
|
145
|
+
.filter(h => h.confidence > 0.3)
|
|
146
|
+
.sort((a, b) => b.confidence - a.confidence)
|
|
147
|
+
.slice(0, 5); // Keep only top 5 — K=3-5 is empirically sufficient
|
|
148
|
+
const profile = {
|
|
149
|
+
...existing,
|
|
150
|
+
harnessHints: merged,
|
|
151
|
+
runCount: existing.runCount + 1,
|
|
152
|
+
lastRunAt: Date.now(),
|
|
153
|
+
};
|
|
154
|
+
writeProfileAtomic(profilePath, profile);
|
|
155
|
+
}
|
|
156
|
+
function extractKeywords(text) {
|
|
157
|
+
// Extract meaningful action words — naive but effective for O(1) comparison
|
|
158
|
+
const stopwords = new Set(['the', 'a', 'an', 'and', 'or', 'but', 'in', 'on', 'at', 'to', 'for', 'with', 'by']);
|
|
159
|
+
return text.toLowerCase()
|
|
160
|
+
.split(/\W+/)
|
|
161
|
+
.filter(w => w.length > 3 && !stopwords.has(w));
|
|
162
|
+
}
|
|
163
|
+
function writeProfileAtomic(profilePath, profile) {
|
|
164
|
+
const tmp = profilePath + '.tmp';
|
|
165
|
+
fs.writeFileSync(tmp, JSON.stringify(profile, null, 2));
|
|
166
|
+
fs.renameSync(tmp, profilePath);
|
|
167
|
+
}
|
|
168
|
+
//# sourceMappingURL=learner.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"learner.js","sourceRoot":"","sources":["../../src/profiles/learner.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AACH,OAAO,IAAI,MAAM,WAAW,CAAA;AAC5B,OAAO,EAAE,MAAM,SAAS,CAAA;AAExB,OAAO,EAAE,SAAS,EAAE,MAAM,oBAAoB,CAAA;AAE9C,MAAM,iBAAiB,GAAG,UAAU,CAAA;AAEpC,SAAS,UAAU,CAAC,GAAW,EAAE,QAAgB;IAC/C,wCAAwC;IACxC,MAAM,MAAM,GAAG,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,KAAK,EAAE,GAAG,CAAC,CAAA;IACxD,OAAO,GAAG,MAAM,IAAI,QAAQ,EAAE,CAAA;AAChC,CAAC;AAED,SAAS,cAAc,CAAC,UAAkB,EAAE,GAAW,EAAE,QAAgB;IACvE,MAAM,WAAW,GAAG,IAAI,CAAC,IAAI,CAAC,UAAU,EAAE,iBAAiB,CAAC,CAAA;IAC5D,EAAE,CAAC,SAAS,CAAC,WAAW,EAAE,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC,CAAA;IAC9C,OAAO,IAAI,CAAC,IAAI,CAAC,WAAW,EAAE,GAAG,UAAU,CAAC,GAAG,EAAE,QAAQ,CAAC,OAAO,CAAC,CAAA;AACpE,CAAC;AAED,MAAM,UAAU,WAAW,CACzB,UAAkB,EAClB,GAAW,EACX,QAAgB;IAEhB,MAAM,WAAW,GAAG,cAAc,CAAC,UAAU,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAA;IAC7D,IAAI,CAAC;QACH,OAAO,IAAI,CAAC,KAAK,CAAC,EAAE,CAAC,YAAY,CAAC,WAAW,EAAE,OAAO,CAAC,CAAiB,CAAA;IAC1E,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,IAAI,CAAA;IACb,CAAC;AACH,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,UAAkB,EAClB,GAAW,EACX,QAAgB,EAChB,MAAqB;IAErB,MAAM,WAAW,GAAG,cAAc,CAAC,UAAU,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAA;IAC7D,MAAM,QAAQ,GAAG,WAAW,CAAC,UAAU,EAAE,GAAG,EAAE,QAAQ,CAAC,IAAI;QACzD,GAAG;QACH,QAAQ;QACR,YAAY,EAAE,EAAE;QAChB,QAAQ,EAAE,CAAC;QACX,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;KACtB,CAAA;IAED,MAAM,aAAa,GAAG,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,KAAK,QAAQ,CAAC,CAAA;IACvE,IAAI,aAAa,CAAC,MAAM,KAAK,CAAC,IAAI,QAAQ,CAAC,YAAY,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACnE,oEAAoE;QACpE,QAAQ,CAAC,QAAQ,EAAE,CAAA;QACnB,QAAQ,CAAC,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAA;QAC/B,QAAQ,CAAC,YAAY,GAAG,QAAQ,CAAC,YAAY,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;YACtD,GAAG,CAAC;YACJ,UAAU,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,UAAU,GAAG,GAAG,CAAC;SAC9C,CAAC,CAAC,CAAA;QACH,kBAAkB,CAAC,WAAW,EAAE,QAAQ,CAAC,CAAA;QACzC,OAAM;IACR,CAAC;IAED,4CAA4C;IAC5C,MAAM,WAAW,GAAG,aAAa;SAC9B,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC;SACZ,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,WAAW,EAAE,CAAC;SAC3C,IAAI,CAAC,IAAI,CAAC,CAAA;IAEb,IAAI,QAAQ,GAAkB,EAAE,CAAA;IAChC,IAAI,CAAC;QACH,MAAM,YAAY,GAAG,MAAM,SAAS,CAClC;;OAEC,GAAG;YACE,QAAQ;;EAElB,WAAW;;;;;4CAK+B,EACtC,SAAS,EACT,GAAG,CACJ,CAAA;QAED,MAAM,KAAK,GAAG,YAAY,CAAC,KAAK,CAAC,aAAa,CAAC,CAAA;QAC/C,IAAI,KAAK,EAAE,CAAC;YACV,MAAM,MAAM,GAAG,IAAI,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAuD,CAAA;YACzF,QAAQ,GAAG,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;gBAC1B,WAAW,EAAE,CAAC,CAAC,WAAW;gBAC1B,UAAU,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,CAAC,UAAU,CAAC,CAAC;gBACtD,OAAO,EAAE,IAAI,CAAC,GAAG,EAAE;aACpB,CAAC,CAAC,CAAA;QACL,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,iCAAiC;IACnC,CAAC;IAED,mEAAmE;IACnE,MAAM,OAAO,GAAG,UAAU,CAAC,QAAQ,CAAC,YAAY,EAAE,QAAQ,CAAC,CAAA;IAE3D,MAAM,OAAO,GAAiB;QAC5B,GAAG,QAAQ;QACX,YAAY,EAAE,OAAO;QACrB,QAAQ,EAAE,QAAQ,CAAC,QAAQ,GAAG,CAAC;QAC/B,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;KACtB,CAAA;IAED,kBAAkB,CAAC,WAAW,EAAE,OAAO,CAAC,CAAA;AAC1C,CAAC;AAED,SAAS,UAAU,CAAC,QAAuB,EAAE,QAAuB;IAClE,MAAM,MAAM,GAAG,CAAC,GAAG,QAAQ,CAAC,CAAA;IAE5B,KAAK,MAAM,IAAI,IAAI,QAAQ,EAAE,CAAC;QAC5B,2DAA2D;QAC3D,MAAM,OAAO,GAAG,MAAM,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,CACnC,CAAC,CAAC,WAAW,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,IAAI,CAAC,WAAW,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAClF,CAAA;QACD,IAAI,OAAO,IAAI,CAAC,EAAE,CAAC;YACjB,mBAAmB;YACnB,MAAM,CAAC,OAAO,CAAC,GAAG;gBAChB,GAAG,MAAM,CAAC,OAAO,CAAC;gBAClB,UAAU,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,EAAE,MAAM,CAAC,OAAO,CAAC,CAAC,UAAU,GAAG,IAAI,CAAC;aAC7D,CAAA;QACH,CAAC;aAAM,CAAC;YACN,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;QACnB,CAAC;IACH,CAAC;IAED,8DAA8D;IAC9D,OAAO,MAAM;SACV,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,GAAG,CAAC;SAC/B,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU,CAAC;SAC3C,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAA;AACjB,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,wBAAwB,CAC5C,UAAkB,EAClB,GAAgB;IAEhB,MAAM,WAAW,GAAG,cAAc,CAAC,UAAU,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,QAAQ,CAAC,CAAA;IACrE,MAAM,QAAQ,GAAG,WAAW,CAAC,UAAU,EAAE,GAAG,CAAC,GAAG,EAAE,GAAG,CAAC,QAAQ,CAAC,IAAI;QACjE,GAAG,EAAE,GAAG,CAAC,GAAG;QACZ,QAAQ,EAAE,GAAG,CAAC,QAAQ;QACtB,YAAY,EAAE,EAAE;QAChB,QAAQ,EAAE,CAAC;QACX,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;KACtB,CAAA;IAED,4EAA4E;IAC5E,0EAA0E;IAC1E,MAAM,aAAa,GAAG,QAAQ,CAAC,YAAY,CAAC,MAAM,CAAC,IAAI,CAAC,EAAE;QACxD,yEAAyE;QACzE,MAAM,WAAW,GAAG,IAAI,CAAC,WAAW,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,kBAAkB,CAAC;YAC7E,CAAC,GAAG,CAAC,eAAe,CAAC,WAAW,EAAE,CAAC,QAAQ,CAAC,UAAU,CAAC,CAAA;QACzD,OAAO,CAAC,WAAW,CAAA;IACrB,CAAC,CAAC,CAAA;IAEF,wCAAwC;IACxC,MAAM,YAAY,GAAgB;QAChC,WAAW,EAAE,oBAAoB,GAAG,CAAC,eAAe,CAAC,KAAK,CAAC,CAAC,EAAE,GAAG,CAAC,wBAAwB,GAAG,CAAC,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,IAAI,MAAM,GAAG;QAC/H,UAAU,EAAE,IAAI;QAChB,OAAO,EAAE,GAAG,CAAC,EAAE;KAChB,CAAA;IAED,mEAAmE;IACnE,MAAM,MAAM,GAAG,CAAC,YAAY,EAAE,GAAG,aAAa,CAAC;SAC5C,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,GAAG,CAAC;SAC/B,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,UAAU,GAAG,CAAC,CAAC,UAAU,CAAC;SAC3C,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAA,CAAE,oDAAoD;IAEpE,MAAM,OAAO,GAAiB;QAC5B,GAAG,QAAQ;QACX,YAAY,EAAE,MAAM;QACpB,QAAQ,EAAE,QAAQ,CAAC,QAAQ,GAAG,CAAC;QAC/B,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;KACtB,CAAA;IAED,kBAAkB,CAAC,WAAW,EAAE,OAAO,CAAC,CAAA;AAC1C,CAAC;AAED,SAAS,eAAe,CAAC,IAAY;IACnC,4EAA4E;IAC5E,MAAM,SAAS,GAAG,IAAI,GAAG,CAAC,CAAC,KAAK,EAAE,GAAG,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,KAAK,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC,CAAA;IAC9G,OAAO,IAAI,CAAC,WAAW,EAAE;SACtB,KAAK,CAAC,KAAK,CAAC;SACZ,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAA;AACnD,CAAC;AAED,SAAS,kBAAkB,CAAC,WAAmB,EAAE,OAAqB;IACpE,MAAM,GAAG,GAAG,WAAW,GAAG,MAAM,CAAA;IAChC,EAAE,CAAC,aAAa,CAAC,GAAG,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC,CAAC,CAAA;IACvD,EAAE,CAAC,UAAU,CAAC,GAAG,EAAE,WAAW,CAAC,CAAA;AACjC,CAAC"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import type { Tool } from 'ai';
|
|
3
|
+
declare const captchaParams: z.ZodObject<{
|
|
4
|
+
pageURL: z.ZodString;
|
|
5
|
+
siteKey: z.ZodOptional<z.ZodString>;
|
|
6
|
+
}, z.core.$strip>;
|
|
7
|
+
type CaptchaParams = z.infer<typeof captchaParams>;
|
|
8
|
+
type CaptchaResult = {
|
|
9
|
+
solved: true;
|
|
10
|
+
token: string;
|
|
11
|
+
type: string;
|
|
12
|
+
} | {
|
|
13
|
+
solved: false;
|
|
14
|
+
error: string;
|
|
15
|
+
hint: string;
|
|
16
|
+
};
|
|
17
|
+
export declare const solveTurnstile: Tool<CaptchaParams, CaptchaResult>;
|
|
18
|
+
export declare function capsolverAvailable(): boolean;
|
|
19
|
+
export {};
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
const captchaParams = z.object({
|
|
3
|
+
pageURL: z.string().describe('The full URL of the page showing the CAPTCHA'),
|
|
4
|
+
siteKey: z.string().optional().describe('Cloudflare Turnstile site key (found in page source). Leave empty to auto-detect.'),
|
|
5
|
+
});
|
|
6
|
+
export const solveTurnstile = {
|
|
7
|
+
description: `Solve a Cloudflare Turnstile CAPTCHA using the CapSolver API.
|
|
8
|
+
Use this when you see a "Verify you are human" challenge blocking form submission.
|
|
9
|
+
Returns a token you can inject into the page to bypass the challenge.
|
|
10
|
+
Only works if CAPSOLVER_API_KEY is configured — check first before calling.`,
|
|
11
|
+
inputSchema: captchaParams,
|
|
12
|
+
execute: async ({ pageURL, siteKey }) => {
|
|
13
|
+
const apiKey = process.env.CAPSOLVER_API_KEY;
|
|
14
|
+
if (!apiKey) {
|
|
15
|
+
return {
|
|
16
|
+
solved: false,
|
|
17
|
+
error: 'CAPSOLVER_API_KEY not configured',
|
|
18
|
+
hint: 'Add CAPSOLVER_API_KEY to .env, or configure X-Usertester-Session WAF bypass in Cloudflare instead (recommended).',
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
try {
|
|
22
|
+
// CapSolver REST API — no SDK needed, direct HTTP is cleaner
|
|
23
|
+
const createTask = await fetch('https://api.capsolver.com/createTask', {
|
|
24
|
+
method: 'POST',
|
|
25
|
+
headers: { 'Content-Type': 'application/json' },
|
|
26
|
+
body: JSON.stringify({
|
|
27
|
+
clientKey: apiKey,
|
|
28
|
+
task: {
|
|
29
|
+
type: 'AntiTurnstileTaskProxyLess',
|
|
30
|
+
websiteURL: pageURL,
|
|
31
|
+
websiteKey: siteKey ?? '0x4AAAAAAADnPIDROrmt1Wwj', // Cloudflare default demo key fallback
|
|
32
|
+
},
|
|
33
|
+
}),
|
|
34
|
+
}).then(r => r.json());
|
|
35
|
+
if (!createTask.taskId) {
|
|
36
|
+
return {
|
|
37
|
+
solved: false,
|
|
38
|
+
error: createTask.errorDescription ?? 'Task creation failed',
|
|
39
|
+
hint: 'Check CAPSOLVER_API_KEY balance and validity.',
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
// Poll for result (Turnstile typically solves in 5-15 seconds)
|
|
43
|
+
const deadline = Date.now() + 60_000;
|
|
44
|
+
while (Date.now() < deadline) {
|
|
45
|
+
await new Promise(r => setTimeout(r, 3000));
|
|
46
|
+
const result = await fetch('https://api.capsolver.com/getTaskResult', {
|
|
47
|
+
method: 'POST',
|
|
48
|
+
headers: { 'Content-Type': 'application/json' },
|
|
49
|
+
body: JSON.stringify({ clientKey: apiKey, taskId: createTask.taskId }),
|
|
50
|
+
}).then(r => r.json());
|
|
51
|
+
if (result.status === 'ready' && result.solution?.token) {
|
|
52
|
+
return {
|
|
53
|
+
solved: true,
|
|
54
|
+
token: result.solution.token,
|
|
55
|
+
type: 'AntiTurnstileTaskProxyLess',
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
if (result.status === 'failed' || result.errorCode) {
|
|
59
|
+
return {
|
|
60
|
+
solved: false,
|
|
61
|
+
error: result.errorDescription ?? 'Solve failed',
|
|
62
|
+
hint: 'Turnstile may be in hardened mode. Try configuring X-Usertester-Session WAF bypass instead.',
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
return { solved: false, error: 'Timeout waiting for CAPTCHA solve', hint: 'CapSolver took >60s' };
|
|
67
|
+
}
|
|
68
|
+
catch (err) {
|
|
69
|
+
return { solved: false, error: String(err), hint: 'CapSolver API request failed' };
|
|
70
|
+
}
|
|
71
|
+
},
|
|
72
|
+
};
|
|
73
|
+
export function capsolverAvailable() {
|
|
74
|
+
return !!process.env.CAPSOLVER_API_KEY;
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=captcha.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"captcha.js","sourceRoot":"","sources":["../../src/tools/captcha.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* readInboxEmail tool — lets the browser agent read emails via AgentMail API
|
|
3
|
+
* instead of trying to navigate to a web inbox (which fails DNS).
|
|
4
|
+
*
|
|
5
|
+
* Used by the retry loop when it detects a CAPABILITY_GAP around email reading.
|
|
6
|
+
* Injected into Stagehand via stagehand.agent({ tools: { readInboxEmail } })
|
|
7
|
+
*/
|
|
8
|
+
import { z } from 'zod';
|
|
9
|
+
import type { Tool } from 'ai';
|
|
10
|
+
declare const inboxParams: z.ZodObject<{
|
|
11
|
+
inboxId: z.ZodString;
|
|
12
|
+
subjectContains: z.ZodOptional<z.ZodString>;
|
|
13
|
+
waitMinutes: z.ZodOptional<z.ZodNumber>;
|
|
14
|
+
}, z.core.$strip>;
|
|
15
|
+
type InboxParams = z.infer<typeof inboxParams>;
|
|
16
|
+
type InboxResult = {
|
|
17
|
+
found: false;
|
|
18
|
+
error: string;
|
|
19
|
+
} | {
|
|
20
|
+
found: false;
|
|
21
|
+
message: string;
|
|
22
|
+
} | {
|
|
23
|
+
found: true;
|
|
24
|
+
subject?: string;
|
|
25
|
+
snippet?: string;
|
|
26
|
+
verificationCodes: string[];
|
|
27
|
+
primaryCode: string | null;
|
|
28
|
+
};
|
|
29
|
+
export declare const readInboxEmail: Tool<InboxParams, InboxResult>;
|
|
30
|
+
export {};
|