@ddwang/magnitude-core 0.3.1-ddwang.1
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/actions/desktopActions.d.ts +58 -0
- package/dist/actions/desktopActions.js +154 -0
- package/dist/actions/index.d.ts +26 -0
- package/dist/actions/index.js +11 -0
- package/dist/actions/taskActions.d.ts +8 -0
- package/dist/actions/taskActions.js +27 -0
- package/dist/actions/types.d.ts +31 -0
- package/dist/actions/types.js +5 -0
- package/dist/actions/util.d.ts +20 -0
- package/dist/actions/util.js +297 -0
- package/dist/actions/webActions.d.ts +79 -0
- package/dist/actions/webActions.js +190 -0
- package/dist/agent/browserAgent.d.ts +29 -0
- package/dist/agent/browserAgent.js +122 -0
- package/dist/agent/errors.d.ts +8 -0
- package/dist/agent/errors.js +68 -0
- package/dist/agent/index.d.ts +53 -0
- package/dist/agent/index.js +377 -0
- package/dist/agent/narrator.d.ts +4 -0
- package/dist/agent/narrator.js +63 -0
- package/dist/agent/pause.test.d.ts +1 -0
- package/dist/agent/pause.test.js +194 -0
- package/dist/ai/baml_client/async_client.d.ts +73 -0
- package/dist/ai/baml_client/async_client.js +152 -0
- package/dist/ai/baml_client/async_request.d.ts +35 -0
- package/dist/ai/baml_client/async_request.js +98 -0
- package/dist/ai/baml_client/config.d.ts +11 -0
- package/dist/ai/baml_client/config.js +20 -0
- package/dist/ai/baml_client/globals.d.ts +16 -0
- package/dist/ai/baml_client/globals.js +37 -0
- package/dist/ai/baml_client/index.d.ts +16 -0
- package/dist/ai/baml_client/index.js +41 -0
- package/dist/ai/baml_client/inlinedbaml.d.ts +19 -0
- package/dist/ai/baml_client/inlinedbaml.js +31 -0
- package/dist/ai/baml_client/parser.d.ts +53 -0
- package/dist/ai/baml_client/parser.js +86 -0
- package/dist/ai/baml_client/partial_types.d.ts +61 -0
- package/dist/ai/baml_client/partial_types.js +10 -0
- package/dist/ai/baml_client/sync_client.d.ts +50 -0
- package/dist/ai/baml_client/sync_client.js +100 -0
- package/dist/ai/baml_client/sync_request.d.ts +35 -0
- package/dist/ai/baml_client/sync_request.js +98 -0
- package/dist/ai/baml_client/tracing.d.ts +16 -0
- package/dist/ai/baml_client/tracing.js +18 -0
- package/dist/ai/baml_client/type_builder.d.ts +38 -0
- package/dist/ai/baml_client/type_builder.js +96 -0
- package/dist/ai/baml_client/types.d.ts +69 -0
- package/dist/ai/baml_client/types.js +15 -0
- package/dist/ai/claudeCode.d.ts +1 -0
- package/dist/ai/claudeCode.js +186 -0
- package/dist/ai/modelHarness.d.ts +39 -0
- package/dist/ai/modelHarness.js +189 -0
- package/dist/ai/multiModelHarness.d.ts +27 -0
- package/dist/ai/multiModelHarness.js +52 -0
- package/dist/ai/types.d.ts +90 -0
- package/dist/ai/types.js +5 -0
- package/dist/ai/util.d.ts +13 -0
- package/dist/ai/util.js +165 -0
- package/dist/cli.d.ts +0 -0
- package/dist/cli.js +3 -0
- package/dist/common/actions.d.ts +0 -0
- package/dist/common/actions.js +14 -0
- package/dist/common/events.d.ts +15 -0
- package/dist/common/events.js +3 -0
- package/dist/common/failure.d.ts +68 -0
- package/dist/common/failure.js +1 -0
- package/dist/common/index.d.ts +3 -0
- package/dist/common/index.js +3 -0
- package/dist/common/retry.d.ts +16 -0
- package/dist/common/retry.js +30 -0
- package/dist/common/util.d.ts +23 -0
- package/dist/common/util.js +132 -0
- package/dist/connectors/browserConnector.d.ts +40 -0
- package/dist/connectors/browserConnector.js +127 -0
- package/dist/connectors/desktopConnector.d.ts +58 -0
- package/dist/connectors/desktopConnector.js +68 -0
- package/dist/connectors/index.d.ts +10 -0
- package/dist/connectors/index.js +2 -0
- package/dist/index.cjs +6143 -0
- package/dist/index.d.cts +825 -0
- package/dist/index.d.ts +20 -0
- package/dist/index.js +26 -0
- package/dist/index.mjs +6097 -0
- package/dist/logger.d.ts +3 -0
- package/dist/logger.js +15 -0
- package/dist/memory/agentMemory.d.ts +37 -0
- package/dist/memory/agentMemory.js +121 -0
- package/dist/memory/image.d.ts +26 -0
- package/dist/memory/image.js +106 -0
- package/dist/memory/index.d.ts +3 -0
- package/dist/memory/index.js +3 -0
- package/dist/memory/masking.d.ts +7 -0
- package/dist/memory/masking.js +141 -0
- package/dist/memory/masking.test.d.ts +1 -0
- package/dist/memory/masking.test.js +117 -0
- package/dist/memory/observation.d.ts +38 -0
- package/dist/memory/observation.js +79 -0
- package/dist/memory/rendering/index.d.ts +10 -0
- package/dist/memory/rendering/index.js +13 -0
- package/dist/memory/rendering/renderJsonParts.d.ts +3 -0
- package/dist/memory/rendering/renderJsonParts.js +115 -0
- package/dist/memory/rendering/renderJsonParts.test.d.ts +1 -0
- package/dist/memory/rendering/renderJsonParts.test.js +217 -0
- package/dist/memory/rendering/renderXmlParts.d.ts +7 -0
- package/dist/memory/rendering/renderXmlParts.js +127 -0
- package/dist/memory/serde.d.ts +20 -0
- package/dist/memory/serde.js +81 -0
- package/dist/memory/util.d.ts +2 -0
- package/dist/memory/util.js +17 -0
- package/dist/telemetry/events.d.ts +9 -0
- package/dist/telemetry/events.js +80 -0
- package/dist/telemetry/index.d.ts +6 -0
- package/dist/telemetry/index.js +91 -0
- package/dist/types.d.ts +30 -0
- package/dist/types.js +1 -0
- package/dist/util.d.ts +1 -0
- package/dist/util.js +12 -0
- package/dist/version.d.ts +1 -0
- package/dist/version.js +3 -0
- package/dist/web/browserProvider.d.ts +23 -0
- package/dist/web/browserProvider.js +153 -0
- package/dist/web/harness.d.ts +102 -0
- package/dist/web/harness.js +340 -0
- package/dist/web/stability.d.ts +57 -0
- package/dist/web/stability.js +316 -0
- package/dist/web/tabs.d.ts +58 -0
- package/dist/web/tabs.js +293 -0
- package/dist/web/transformer.d.ts +7 -0
- package/dist/web/transformer.js +41 -0
- package/dist/web/types.d.ts +32 -0
- package/dist/web/types.js +2 -0
- package/dist/web/util.d.ts +3 -0
- package/dist/web/util.js +81 -0
- package/dist/web/visualizer/cursor.d.ts +16 -0
- package/dist/web/visualizer/cursor.js +152 -0
- package/dist/web/visualizer/index.d.ts +22 -0
- package/dist/web/visualizer/index.js +47 -0
- package/dist/web/visualizer/mouseEffects.d.ts +18 -0
- package/dist/web/visualizer/mouseEffects.js +287 -0
- package/dist/web/visualizer/typeEffects.d.ts +10 -0
- package/dist/web/visualizer/typeEffects.js +190 -0
- package/package.json +71 -0
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
export type BrowserAgentRole = 'act' | 'extract' | 'query';
|
|
2
|
+
export declare const allBrowserAgentRoles: BrowserAgentRole[];
|
|
3
|
+
export type LLMClient = (AnthropicClient | ClaudeCodeClient | BedrockClient | GoogleAIClient | GoogleVertexClient | OpenAIClient | OpenAIGenericClient | AzureOpenAIClient) & {
|
|
4
|
+
roles?: BrowserAgentRole[];
|
|
5
|
+
};
|
|
6
|
+
export interface AnthropicClient {
|
|
7
|
+
provider: 'anthropic';
|
|
8
|
+
options: {
|
|
9
|
+
model: string;
|
|
10
|
+
apiKey?: string;
|
|
11
|
+
temperature?: number;
|
|
12
|
+
promptCaching?: boolean;
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
export interface ClaudeCodeClient {
|
|
16
|
+
provider: 'claude-code';
|
|
17
|
+
options: {
|
|
18
|
+
model: string;
|
|
19
|
+
temperature?: number;
|
|
20
|
+
promptCaching?: boolean;
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export interface BedrockClient {
|
|
24
|
+
provider: 'aws-bedrock';
|
|
25
|
+
options: {
|
|
26
|
+
model: string;
|
|
27
|
+
temperature?: number;
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
export interface GoogleAIClient {
|
|
31
|
+
provider: 'google-ai';
|
|
32
|
+
options: {
|
|
33
|
+
model: string;
|
|
34
|
+
apiKey?: string;
|
|
35
|
+
temperature?: number;
|
|
36
|
+
baseUrl?: string;
|
|
37
|
+
};
|
|
38
|
+
}
|
|
39
|
+
export interface GoogleVertexClient {
|
|
40
|
+
provider: 'vertex-ai';
|
|
41
|
+
options: {
|
|
42
|
+
model: string;
|
|
43
|
+
location?: string;
|
|
44
|
+
baseUrl?: string;
|
|
45
|
+
projectId?: string;
|
|
46
|
+
credentials?: string | object;
|
|
47
|
+
anthropicVersion?: string;
|
|
48
|
+
temperature?: number;
|
|
49
|
+
};
|
|
50
|
+
}
|
|
51
|
+
export interface OpenAIClient {
|
|
52
|
+
provider: 'openai';
|
|
53
|
+
options: {
|
|
54
|
+
model: string;
|
|
55
|
+
apiKey?: string;
|
|
56
|
+
temperature?: number;
|
|
57
|
+
};
|
|
58
|
+
}
|
|
59
|
+
export interface AzureOpenAIClient {
|
|
60
|
+
provider: 'azure-openai';
|
|
61
|
+
options: {
|
|
62
|
+
resourceName: string;
|
|
63
|
+
deploymentId: string;
|
|
64
|
+
apiVersion: string;
|
|
65
|
+
apiKey: string;
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
export interface OpenAIGenericClient {
|
|
69
|
+
provider: 'openai-generic';
|
|
70
|
+
options: {
|
|
71
|
+
model: string;
|
|
72
|
+
baseUrl: string;
|
|
73
|
+
apiKey?: string;
|
|
74
|
+
temperature?: number;
|
|
75
|
+
headers?: Record<string, string>;
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
export interface LLMClientIdentifier {
|
|
79
|
+
provider: string;
|
|
80
|
+
model: string;
|
|
81
|
+
}
|
|
82
|
+
export interface ModelUsage {
|
|
83
|
+
llm: LLMClientIdentifier;
|
|
84
|
+
inputTokens: number;
|
|
85
|
+
outputTokens: number;
|
|
86
|
+
cacheWriteInputTokens?: number;
|
|
87
|
+
cacheReadInputTokens?: number;
|
|
88
|
+
inputCost?: number;
|
|
89
|
+
outputCost?: number;
|
|
90
|
+
}
|
package/dist/ai/types.js
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { type LLMClient } from '@/ai/types';
|
|
2
|
+
import { AgentOptions } from "@/agent";
|
|
3
|
+
import { BrowserConnectorOptions } from "@/connectors/browserConnector";
|
|
4
|
+
export declare function convertToBamlClientOptions(client: LLMClient): Promise<Record<string, any>>;
|
|
5
|
+
export declare function tryDeriveUIGroundedClient(): LLMClient | null;
|
|
6
|
+
export declare function isClaude(llm: LLMClient): boolean;
|
|
7
|
+
export declare function buildDefaultBrowserAgentOptions({ agentOptions, browserOptions }: {
|
|
8
|
+
agentOptions: AgentOptions;
|
|
9
|
+
browserOptions: BrowserConnectorOptions;
|
|
10
|
+
}): {
|
|
11
|
+
agentOptions: AgentOptions;
|
|
12
|
+
browserOptions: BrowserConnectorOptions;
|
|
13
|
+
};
|
package/dist/ai/util.js
ADDED
|
@@ -0,0 +1,165 @@
|
|
|
1
|
+
import { completeClaudeCodeAuthFlow } from './claudeCode';
|
|
2
|
+
function cleanNestedObject(obj) {
|
|
3
|
+
// Remove null/undefined key values entirely
|
|
4
|
+
return Object.fromEntries(Object.entries(obj)
|
|
5
|
+
// Filter out null/undefined values
|
|
6
|
+
.filter(([_, value]) => value !== null && value !== undefined)
|
|
7
|
+
// Process nested objects recursively
|
|
8
|
+
.map(([key, value]) => [
|
|
9
|
+
key,
|
|
10
|
+
typeof value === 'object' ? cleanNestedObject(value) : value
|
|
11
|
+
]));
|
|
12
|
+
}
|
|
13
|
+
export async function convertToBamlClientOptions(client) {
|
|
14
|
+
// extract options compatible with https://docs.boundaryml.com/ref/llm-client-providers/overview
|
|
15
|
+
// Default to temperature 0.0
|
|
16
|
+
// Some client options (e.g. azure) do not have a temperature setting
|
|
17
|
+
const temp = 'temperature' in client.options ?
|
|
18
|
+
(client.options.temperature ?? 0.0) : 0.0;
|
|
19
|
+
let options;
|
|
20
|
+
if (client.provider === 'claude-code') {
|
|
21
|
+
// Special case - oauth with claude code max anthropic account
|
|
22
|
+
const oauthToken = await completeClaudeCodeAuthFlow();
|
|
23
|
+
options = {
|
|
24
|
+
model: client.options.model,
|
|
25
|
+
temperature: temp,
|
|
26
|
+
headers: {
|
|
27
|
+
'Authorization': `Bearer ${oauthToken}`,
|
|
28
|
+
'anthropic-beta': 'oauth-2025-04-20' + (client.options.promptCaching ? ',prompt-caching-2024-07-31' : ''),
|
|
29
|
+
// Overrides this header from being automatically derived from ANTHROPIC_API_KEY
|
|
30
|
+
'X-API-Key': ''
|
|
31
|
+
},
|
|
32
|
+
...(client.options.promptCaching ? { allowed_role_metadata: "all" } : {}),
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
else if (client.provider === 'anthropic') {
|
|
36
|
+
options = {
|
|
37
|
+
api_key: client.options.apiKey,
|
|
38
|
+
model: client.options.model,
|
|
39
|
+
temperature: temp,
|
|
40
|
+
...(client.options.promptCaching ? {
|
|
41
|
+
allowed_role_metadata: "all",
|
|
42
|
+
headers: { 'anthropic-beta': 'prompt-caching-2024-07-31' }
|
|
43
|
+
} : {}),
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
else if (client.provider === 'aws-bedrock') {
|
|
47
|
+
options = {
|
|
48
|
+
model_id: client.options.model,
|
|
49
|
+
inference_configuration: {
|
|
50
|
+
temperature: temp,
|
|
51
|
+
}
|
|
52
|
+
};
|
|
53
|
+
}
|
|
54
|
+
else if (client.provider === 'google-ai') {
|
|
55
|
+
options = {
|
|
56
|
+
base_url: client.options.baseUrl,
|
|
57
|
+
model: client.options.model,
|
|
58
|
+
api_key: client.options.apiKey,
|
|
59
|
+
generationConfig: {
|
|
60
|
+
temperature: temp,
|
|
61
|
+
//thinking_budget: 0
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
else if (client.provider === 'vertex-ai') {
|
|
66
|
+
options = {
|
|
67
|
+
location: client.options.location,
|
|
68
|
+
base_url: client.options.baseUrl,
|
|
69
|
+
project_id: client.options.projectId,
|
|
70
|
+
credentials: client.options.credentials,
|
|
71
|
+
anthropic_version: client.options.anthropicVersion,
|
|
72
|
+
model: client.options.model,
|
|
73
|
+
// generationConfig: {
|
|
74
|
+
// temperature: temp,
|
|
75
|
+
// }
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
else if (client.provider === 'openai') {
|
|
79
|
+
options = {
|
|
80
|
+
api_key: client.options.apiKey,
|
|
81
|
+
model: client.options.model,
|
|
82
|
+
temperature: temp,
|
|
83
|
+
};
|
|
84
|
+
}
|
|
85
|
+
else if (client.provider === 'openai-generic') {
|
|
86
|
+
options = {
|
|
87
|
+
base_url: client.options.baseUrl,
|
|
88
|
+
api_key: client.options.apiKey,
|
|
89
|
+
model: client.options.model,
|
|
90
|
+
temperature: temp,
|
|
91
|
+
headers: {
|
|
92
|
+
"HTTP-Referer": "https://magnitude.run",
|
|
93
|
+
"X-Title": "Magnitude",
|
|
94
|
+
...client.options.headers
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
}
|
|
98
|
+
else if (client.provider === 'azure-openai') {
|
|
99
|
+
options = {
|
|
100
|
+
resource_name: client.options.resourceName,
|
|
101
|
+
deployment_id: client.options.deploymentId,
|
|
102
|
+
api_version: client.options.apiVersion,
|
|
103
|
+
api_key: client.options.apiKey
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
else {
|
|
107
|
+
throw new Error(`Invalid provider: ${client.provider}`);
|
|
108
|
+
}
|
|
109
|
+
return cleanNestedObject(options);
|
|
110
|
+
}
|
|
111
|
+
export function tryDeriveUIGroundedClient() {
|
|
112
|
+
if (process.env.ANTHROPIC_API_KEY) {
|
|
113
|
+
return {
|
|
114
|
+
provider: 'anthropic',
|
|
115
|
+
options: {
|
|
116
|
+
// TODO: do more testing on best claude model for visuals
|
|
117
|
+
// model: 'claude-3-5-sonnet-20240620', // <- definitely not, pre computer use
|
|
118
|
+
// model: 'claude-3-5-sonnet-20241022', // <- not great on rescaling res
|
|
119
|
+
//model: 'claude-3-7-sonnet-latest', // <- underplans
|
|
120
|
+
// model: 'claude-sonnet-4-20250514', // <- underplans, also supposedly worse at visual reasoning
|
|
121
|
+
model: 'claude-haiku-4-5-20251001', // <- fast, cost-effective, good performance
|
|
122
|
+
apiKey: process.env.ANTHROPIC_API_KEY
|
|
123
|
+
}
|
|
124
|
+
};
|
|
125
|
+
}
|
|
126
|
+
else {
|
|
127
|
+
return null;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
export function isClaude(llm) {
|
|
131
|
+
if ('model' in llm.options) { //if (llm.provider === 'anthropic' || llm.provider === 'aws-bedrock' || llm.provider === 'vertex-ai') {
|
|
132
|
+
const model = llm.options.model;
|
|
133
|
+
if (model.includes('claude'))
|
|
134
|
+
return true;
|
|
135
|
+
}
|
|
136
|
+
return false;
|
|
137
|
+
}
|
|
138
|
+
const DEFAULT_BROWSER_AGENT_TEMP = 0.2;
|
|
139
|
+
export function buildDefaultBrowserAgentOptions({ agentOptions, browserOptions }) {
|
|
140
|
+
/**
|
|
141
|
+
* Given any provided options for agent or browser connector, fill out additional key fields using environment,
|
|
142
|
+
* or any model-specific constraints (e.g. Claude needing 1024x768 virtual screen space)
|
|
143
|
+
*/
|
|
144
|
+
const envLlm = tryDeriveUIGroundedClient();
|
|
145
|
+
let llms = agentOptions.llm ? (Array.isArray(agentOptions.llm) ? agentOptions.llm : [agentOptions.llm]) : (envLlm ? [envLlm] : []);
|
|
146
|
+
if (llms.length == 0) {
|
|
147
|
+
throw new Error("No LLM configured or available from environment. Set environment variable ANTHROPIC_API_KEY and try again. See https://docs.magnitude.run/customizing/llm-configuration for details");
|
|
148
|
+
}
|
|
149
|
+
// Set reasonable temp if not provided
|
|
150
|
+
let virtualScreenDimensions = null;
|
|
151
|
+
for (const llm of llms) {
|
|
152
|
+
let llmOptions = { temperature: DEFAULT_BROWSER_AGENT_TEMP, ...(llm?.options ?? {}) };
|
|
153
|
+
//let modifiedLlm = {...llm, options: llmOptions as any }
|
|
154
|
+
llm.options = llmOptions;
|
|
155
|
+
if (isClaude(llm)) {
|
|
156
|
+
// Claude only really works on 1024x768 screenshots
|
|
157
|
+
// if any model is claude, use virtual screen dimensions
|
|
158
|
+
virtualScreenDimensions = { width: 1024, height: 768 };
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
return {
|
|
162
|
+
agentOptions: { ...agentOptions, llm: llms },
|
|
163
|
+
browserOptions: { ...browserOptions, virtualScreenDimensions: virtualScreenDimensions ?? undefined }
|
|
164
|
+
};
|
|
165
|
+
}
|
package/dist/cli.d.ts
ADDED
|
File without changes
|
package/dist/cli.js
ADDED
|
File without changes
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
// import { ClickWebAction, NavigateWebAction, ScrollWebAction, SwitchTabWebAction, TypeWebAction } from "@/web/types";
|
|
3
|
+
// import { ClickIntent, TypeIntent, ScrollIntent, SwitchTabIntent, Action } from "@/actions/types";
|
|
4
|
+
// //'load' | 'click' | 'hover' | 'type' | 'scroll' | 'wait' | 'back';
|
|
5
|
+
// // move somewhere else, to action types, why is this here
|
|
6
|
+
// export type ActionDescriptor = Action & { screenshot: string };
|
|
7
|
+
// // export type ActionDescriptor = NavigateActionDescriptor | ClickActionDescriptor | TypeActionDescriptor | ScrollActionDescriptor | SwitchTabDescriptor;
|
|
8
|
+
// // export type ActionVariant = ActionDescriptor['variant'];
|
|
9
|
+
// // export type NavigateActionDescriptor = NavigateWebAction & { screenshot: string };
|
|
10
|
+
// // export type ClickActionDescriptor = ClickIntent & ClickWebAction & { screenshot: string };
|
|
11
|
+
// // export type TypeActionDescriptor = TypeIntent & TypeWebAction & { screenshot: string };
|
|
12
|
+
// // export type ScrollActionDescriptor = ScrollIntent & ScrollWebAction & { screenshot: string };
|
|
13
|
+
// // export type SwitchTabDescriptor = SwitchTabIntent & SwitchTabWebAction & { screenshot: string };
|
|
14
|
+
// //const a: ClickActionDescriptor = { variant: 'click', x: 1, y: 2, target: ''}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import { Action } from "@/actions/types";
|
|
2
|
+
import { ActOptions } from "@/agent";
|
|
3
|
+
import { ModelUsage } from "@/ai/types";
|
|
4
|
+
export interface AgentEvents {
|
|
5
|
+
'start': () => void;
|
|
6
|
+
'stop': () => void;
|
|
7
|
+
'thought': (thought: string) => void;
|
|
8
|
+
'actStarted': (task: string, options: ActOptions) => void;
|
|
9
|
+
'actDone': (task: string, options: ActOptions) => void;
|
|
10
|
+
'actionStarted': (action: Action) => void;
|
|
11
|
+
'actionDone': (action: Action) => void;
|
|
12
|
+
'pause': () => void;
|
|
13
|
+
'resume': () => void;
|
|
14
|
+
'tokensUsed': (usage: ModelUsage) => void;
|
|
15
|
+
}
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Represents any reason why a test case could have failed, for example:
|
|
3
|
+
* - Step could not be completed
|
|
4
|
+
* - Check did not pass
|
|
5
|
+
* - Could not navigate to starting URL
|
|
6
|
+
* - Time or action based timeout
|
|
7
|
+
* - Operation cancelled by signal
|
|
8
|
+
* - ...
|
|
9
|
+
*/
|
|
10
|
+
export type FailureDescriptor = BugDetectedFailure | MisalignmentFailure | NetworkFailure | BrowserFailure | RateLimitFailure | ApiKeyFailure | UnknownFailure | CancelledFailure;
|
|
11
|
+
export type BugSeverity = 'critical' | 'high' | 'medium' | 'low';
|
|
12
|
+
/**
|
|
13
|
+
* Step and check failures are classified into one of:
|
|
14
|
+
* BugDetectedFailure: seems to be something wrong with the application itself
|
|
15
|
+
* MisalignmentFailure: seems to be a discrepency between the test case and the interface
|
|
16
|
+
* OR the agent did not properly recognize the relationship between test case and interface
|
|
17
|
+
*/
|
|
18
|
+
export interface BugDetectedFailure {
|
|
19
|
+
variant: 'bug';
|
|
20
|
+
title: string;
|
|
21
|
+
expectedResult: string;
|
|
22
|
+
actualResult: string;
|
|
23
|
+
severity: BugSeverity;
|
|
24
|
+
}
|
|
25
|
+
export interface MisalignmentFailure {
|
|
26
|
+
/**
|
|
27
|
+
* Major misalignment: when a step/check fails due to:
|
|
28
|
+
* 1. Poorly written step/check that is completely unrelated to the interface
|
|
29
|
+
* 2. Or interface has changed so much that step/check no longer applicable
|
|
30
|
+
* 3. Planner did not do good enough job adjusting recipe for minor misalignment
|
|
31
|
+
* Misalignment could be due to a poorly written test case OR bad agent behavior.
|
|
32
|
+
*/
|
|
33
|
+
variant: 'misalignment';
|
|
34
|
+
message: string;
|
|
35
|
+
}
|
|
36
|
+
export interface NetworkFailure {
|
|
37
|
+
/**
|
|
38
|
+
* For example, failure to connect to starting URL, or any other network errors
|
|
39
|
+
* that would completely prevent the test from executing.
|
|
40
|
+
*/
|
|
41
|
+
variant: 'network';
|
|
42
|
+
message: string;
|
|
43
|
+
}
|
|
44
|
+
export interface BrowserFailure {
|
|
45
|
+
/**
|
|
46
|
+
* E.g. something goes wrong with playwright interactions, any DOM manipulation, etc.
|
|
47
|
+
*/
|
|
48
|
+
variant: 'browser';
|
|
49
|
+
message: string;
|
|
50
|
+
}
|
|
51
|
+
export interface RateLimitFailure {
|
|
52
|
+
variant: 'rate_limit';
|
|
53
|
+
message: string;
|
|
54
|
+
}
|
|
55
|
+
export interface ApiKeyFailure {
|
|
56
|
+
variant: 'api_key';
|
|
57
|
+
message: string;
|
|
58
|
+
}
|
|
59
|
+
export interface UnknownFailure {
|
|
60
|
+
variant: 'unknown';
|
|
61
|
+
message: string;
|
|
62
|
+
}
|
|
63
|
+
export interface CancelledFailure {
|
|
64
|
+
/**
|
|
65
|
+
* Operation was cancelled, typically by an AbortSignal from a controlling process (e.g., test runner pool).
|
|
66
|
+
*/
|
|
67
|
+
variant: 'cancelled';
|
|
68
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
type RetryOptions = {
|
|
2
|
+
retries?: number;
|
|
3
|
+
delay?: number;
|
|
4
|
+
maxDelay?: number;
|
|
5
|
+
exponential?: boolean | number;
|
|
6
|
+
throwOnExhaustion?: boolean;
|
|
7
|
+
retryIf?: (error: Error) => boolean;
|
|
8
|
+
onRetry?: (error: Error, attempt: number) => void;
|
|
9
|
+
};
|
|
10
|
+
export declare function retry<T>(fn: () => Promise<T>, options: RetryOptions & {
|
|
11
|
+
throwOnExhaustion: false;
|
|
12
|
+
}): Promise<T | null>;
|
|
13
|
+
export declare function retry<T>(fn: () => Promise<T>, options?: RetryOptions & {
|
|
14
|
+
throwOnExhaustion?: true;
|
|
15
|
+
}): Promise<T>;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
export async function retry(fn, options = {}) {
|
|
2
|
+
const { retries = 3, delay = 0, maxDelay = Infinity, exponential = false, throwOnExhaustion = true, // Now defaults to true
|
|
3
|
+
retryIf = () => true, onRetry } = options;
|
|
4
|
+
const multiplier = exponential === true ? 2 : exponential || 1;
|
|
5
|
+
for (let attempt = 0; attempt <= retries; attempt++) {
|
|
6
|
+
try {
|
|
7
|
+
return await fn();
|
|
8
|
+
}
|
|
9
|
+
catch (error) {
|
|
10
|
+
if (!(error instanceof Error)) {
|
|
11
|
+
throw new Error(`Non-Error thrown: ${String(error)}`);
|
|
12
|
+
}
|
|
13
|
+
if (attempt === retries || !retryIf(error)) {
|
|
14
|
+
if (throwOnExhaustion) {
|
|
15
|
+
throw error;
|
|
16
|
+
}
|
|
17
|
+
return null;
|
|
18
|
+
}
|
|
19
|
+
if (onRetry) {
|
|
20
|
+
onRetry(error, attempt + 1);
|
|
21
|
+
}
|
|
22
|
+
if (delay > 0) {
|
|
23
|
+
const currentDelay = Math.min(delay * Math.pow(multiplier, attempt), maxDelay);
|
|
24
|
+
await new Promise(resolve => setTimeout(resolve, currentDelay));
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
// Unreachable
|
|
29
|
+
return null;
|
|
30
|
+
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
export type RetryMode = {
|
|
2
|
+
mode: 'retry_on_partial_message';
|
|
3
|
+
errorSubstrings: string[];
|
|
4
|
+
} | {
|
|
5
|
+
mode: 'retry_all';
|
|
6
|
+
};
|
|
7
|
+
export type RetryParams = {
|
|
8
|
+
retryLimit: number;
|
|
9
|
+
delayMs: number;
|
|
10
|
+
showWarnOnRetry: boolean;
|
|
11
|
+
};
|
|
12
|
+
export type RetryOptions = RetryMode & RetryParams;
|
|
13
|
+
export declare function retryOnErrorIsSuccess<T>(fnToRetry: () => Promise<T>, retryOptions: RetryMode & Partial<RetryParams>): Promise<boolean>;
|
|
14
|
+
export declare function retryOnError<T>(fnToRetry: () => Promise<T>, retryOptions: RetryMode & Partial<RetryParams>): Promise<T>;
|
|
15
|
+
/**
|
|
16
|
+
* Performs a deep comparison between two values to determine if they are equivalent.
|
|
17
|
+
*
|
|
18
|
+
* @param a The first value to compare.
|
|
19
|
+
* @param b The second value to compare.
|
|
20
|
+
* @param cache A WeakMap to handle circular references. Should not be provided by the caller.
|
|
21
|
+
* @returns `true` if the values are deeply equal, `false` otherwise.
|
|
22
|
+
*/
|
|
23
|
+
export declare function deepEquals(a: any, b: any, cache?: WeakMap<object, WeakSet<object>>): boolean;
|
|
@@ -0,0 +1,132 @@
|
|
|
1
|
+
import logger from "@/logger";
|
|
2
|
+
export async function retryOnErrorIsSuccess(fnToRetry, retryOptions) {
|
|
3
|
+
try {
|
|
4
|
+
await retryOnError(fnToRetry, retryOptions);
|
|
5
|
+
return true;
|
|
6
|
+
}
|
|
7
|
+
catch (error) {
|
|
8
|
+
return false;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export async function retryOnError(fnToRetry, retryOptions) {
|
|
12
|
+
let lastError;
|
|
13
|
+
const options = {
|
|
14
|
+
...retryOptions,
|
|
15
|
+
retryLimit: retryOptions.retryLimit ?? 3,
|
|
16
|
+
delayMs: retryOptions.delayMs ?? 200,
|
|
17
|
+
showWarnOnRetry: retryOptions.showWarnOnRetry ?? false,
|
|
18
|
+
//throw: retryOptions.throw ?? true
|
|
19
|
+
};
|
|
20
|
+
if (options.retryLimit < 0) {
|
|
21
|
+
options.retryLimit = 0;
|
|
22
|
+
}
|
|
23
|
+
for (let attempt = 0; attempt <= options.retryLimit; attempt++) {
|
|
24
|
+
try {
|
|
25
|
+
return await fnToRetry();
|
|
26
|
+
}
|
|
27
|
+
catch (error) {
|
|
28
|
+
lastError = error;
|
|
29
|
+
const errorMessage = String(error?.message ?? error);
|
|
30
|
+
if (options.mode === 'retry_all') {
|
|
31
|
+
if (options.showWarnOnRetry) {
|
|
32
|
+
logger.warn(`Retrying on: ${errorMessage}`);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
else if (options.mode === 'retry_on_partial_message') {
|
|
36
|
+
const includesSubstring = options.errorSubstrings.some((substring) => errorMessage.includes(substring));
|
|
37
|
+
if (includesSubstring) {
|
|
38
|
+
if (options.showWarnOnRetry) {
|
|
39
|
+
logger.warn(`Retrying on: ${errorMessage}`);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
else {
|
|
43
|
+
// Error message does NOT contain the target substring. This error is not retryable.
|
|
44
|
+
throw lastError; // Throw this current error immediately.
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
await new Promise((resolve) => setTimeout(resolve, options.delayMs));
|
|
49
|
+
}
|
|
50
|
+
throw lastError;
|
|
51
|
+
//if (options.throw) throw lastError;
|
|
52
|
+
}
|
|
53
|
+
/**
|
|
54
|
+
* Performs a deep comparison between two values to determine if they are equivalent.
|
|
55
|
+
*
|
|
56
|
+
* @param a The first value to compare.
|
|
57
|
+
* @param b The second value to compare.
|
|
58
|
+
* @param cache A WeakMap to handle circular references. Should not be provided by the caller.
|
|
59
|
+
* @returns `true` if the values are deeply equal, `false` otherwise.
|
|
60
|
+
*/
|
|
61
|
+
export function deepEquals(a, b, cache = new WeakMap()) {
|
|
62
|
+
// 1. Strict equality check (handles primitives, same object references, null, undefined)
|
|
63
|
+
if (a === b) {
|
|
64
|
+
return true;
|
|
65
|
+
}
|
|
66
|
+
// 2. If a or b is null or not an object, and they weren't ===, they are not equal.
|
|
67
|
+
// (This covers cases like comparing a primitive to an object, or one null and one object)
|
|
68
|
+
if (a == null || typeof a !== 'object' || b == null || typeof b !== 'object') {
|
|
69
|
+
return false;
|
|
70
|
+
}
|
|
71
|
+
// 3. Handle circular references:
|
|
72
|
+
// If we've already started comparing 'a' and 'b' in the current path,
|
|
73
|
+
// assume they are equal to break the cycle.
|
|
74
|
+
if (cache.has(a) && cache.get(a).has(b)) {
|
|
75
|
+
return true;
|
|
76
|
+
}
|
|
77
|
+
// Add to cache before recursive calls
|
|
78
|
+
if (!cache.has(a)) {
|
|
79
|
+
cache.set(a, new WeakSet());
|
|
80
|
+
}
|
|
81
|
+
cache.get(a).add(b);
|
|
82
|
+
// Symmetrically for b -> a to catch cycles like a.c = b; b.c = a;
|
|
83
|
+
if (!cache.has(b)) {
|
|
84
|
+
cache.set(b, new WeakSet());
|
|
85
|
+
}
|
|
86
|
+
cache.get(b).add(a);
|
|
87
|
+
// 4. Handle Dates
|
|
88
|
+
if (a instanceof Date && b instanceof Date) {
|
|
89
|
+
return a.getTime() === b.getTime();
|
|
90
|
+
}
|
|
91
|
+
// 5. Handle Regular Expressions
|
|
92
|
+
if (a instanceof RegExp && b instanceof RegExp) {
|
|
93
|
+
return a.source === b.source && a.flags === b.flags;
|
|
94
|
+
}
|
|
95
|
+
// 6. Handle Arrays
|
|
96
|
+
if (Array.isArray(a) && Array.isArray(b)) {
|
|
97
|
+
if (a.length !== b.length) {
|
|
98
|
+
return false;
|
|
99
|
+
}
|
|
100
|
+
for (let i = 0; i < a.length; i++) {
|
|
101
|
+
if (!deepEquals(a[i], b[i], cache)) {
|
|
102
|
+
return false;
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
return true;
|
|
106
|
+
}
|
|
107
|
+
// 7. Handle plain Objects
|
|
108
|
+
// At this point, 'a' and 'b' are objects but not arrays, dates, or regexps.
|
|
109
|
+
// If one is an array and the other an object, they are not equal.
|
|
110
|
+
if (Array.isArray(a) || Array.isArray(b)) {
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
113
|
+
const keysA = Object.keys(a);
|
|
114
|
+
const keysB = Object.keys(b);
|
|
115
|
+
if (keysA.length !== keysB.length) {
|
|
116
|
+
return false;
|
|
117
|
+
}
|
|
118
|
+
for (const key of keysA) {
|
|
119
|
+
// Check if key exists in B. `Object.keys` only returns own enumerable properties.
|
|
120
|
+
// `hasOwnProperty` is a more robust check if b might have non-enumerable properties from its prototype with the same name.
|
|
121
|
+
// However, since we compare keysA.length and keysB.length, if key is in keysA but not in keysB,
|
|
122
|
+
// lengths would mismatch or the loop over keysB (if we did that) would find a key in B not in A.
|
|
123
|
+
// The most crucial part is `Object.prototype.hasOwnProperty.call(b, key)`
|
|
124
|
+
if (!Object.prototype.hasOwnProperty.call(b, key)) {
|
|
125
|
+
return false;
|
|
126
|
+
}
|
|
127
|
+
if (!deepEquals(a[key], b[key], cache)) {
|
|
128
|
+
return false;
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
return true;
|
|
132
|
+
}
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
import { AgentConnector } from ".";
|
|
2
|
+
import { WebHarness } from "@/web/harness";
|
|
3
|
+
import { ActionDefinition } from '@/actions';
|
|
4
|
+
import { BrowserOptions } from "@/web/browserProvider";
|
|
5
|
+
import { TabState } from '@/web/tabs';
|
|
6
|
+
import { Observation } from "@/memory/observation";
|
|
7
|
+
import { Image } from "@/memory/image";
|
|
8
|
+
import { ActionVisualizerOptions } from "@/web/visualizer";
|
|
9
|
+
export interface BrowserConnectorOptions {
|
|
10
|
+
browser?: BrowserOptions;
|
|
11
|
+
url?: string;
|
|
12
|
+
virtualScreenDimensions?: {
|
|
13
|
+
width: number;
|
|
14
|
+
height: number;
|
|
15
|
+
};
|
|
16
|
+
minScreenshots?: number;
|
|
17
|
+
visuals?: ActionVisualizerOptions;
|
|
18
|
+
}
|
|
19
|
+
export interface BrowserConnectorStateData {
|
|
20
|
+
screenshot: Image;
|
|
21
|
+
tabs: TabState;
|
|
22
|
+
}
|
|
23
|
+
export declare class BrowserConnector implements AgentConnector {
|
|
24
|
+
readonly id: string;
|
|
25
|
+
private harness;
|
|
26
|
+
private options;
|
|
27
|
+
private browser?;
|
|
28
|
+
private context;
|
|
29
|
+
private logger;
|
|
30
|
+
constructor(options?: BrowserConnectorOptions);
|
|
31
|
+
onStart(): Promise<void>;
|
|
32
|
+
onStop(): Promise<void>;
|
|
33
|
+
getActionSpace(): ActionDefinition<any>[];
|
|
34
|
+
getHarness(): WebHarness;
|
|
35
|
+
private captureCurrentState;
|
|
36
|
+
transformScreenshot(screenshot: Image): Promise<Image>;
|
|
37
|
+
getLastScreenshot(): Promise<Image>;
|
|
38
|
+
collectObservations(): Promise<Observation[]>;
|
|
39
|
+
getInstructions(): Promise<void | string>;
|
|
40
|
+
}
|