pi-agent-browser-native 0.6.17 → 0.7.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/CHANGELOG.md +13 -0
- package/README.md +55 -167
- package/dist/extensions/agent-browser/index.js +317 -223
- package/dist/extensions/agent-browser/lib/browser-transcript.js +33 -0
- package/dist/extensions/agent-browser/lib/execution-cwd.js +4 -2
- package/dist/extensions/agent-browser/lib/input-modes/job.js +2 -213
- package/dist/extensions/agent-browser/lib/input-modes/params.js +146 -142
- package/dist/extensions/agent-browser/lib/input-modes/script.js +64 -92
- package/dist/extensions/agent-browser/lib/managed-session-policy-lock.js +170 -24
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.js +13 -1
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result.js +26 -13
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/index.js +21 -1
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/snapshot-filter.js +4 -2
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare.js +1 -3
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output.js +35 -20
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/recording-recovery.js +3 -1
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/screenshot-observation.js +79 -0
- package/dist/extensions/agent-browser/lib/orchestration/electron-host/index.js +3 -4
- package/dist/extensions/agent-browser/lib/orchestration/input-plan.js +19 -52
- package/dist/extensions/agent-browser/lib/orchestration/native-session-defaults.js +3 -3
- package/dist/extensions/agent-browser/lib/orchestration/output-file.js +3 -3
- package/dist/extensions/agent-browser/lib/orchestration/script-mode.js +91 -278
- package/dist/extensions/agent-browser/lib/pi-tool-rendering.js +9 -9
- package/dist/extensions/agent-browser/lib/playbook.js +46 -25
- package/dist/extensions/agent-browser/lib/process-identity.js +22 -6
- package/dist/extensions/agent-browser/lib/process.js +16 -13
- package/dist/extensions/agent-browser/lib/results/action-recommendations.js +2 -2
- package/dist/extensions/agent-browser/lib/results/next-actions.js +1 -2
- package/dist/extensions/agent-browser/lib/results/presentation/artifacts.js +25 -4
- package/dist/extensions/agent-browser/lib/results/presentation/batch.js +6 -2
- package/dist/extensions/agent-browser/lib/results/presentation/common.js +2 -0
- package/dist/extensions/agent-browser/lib/results/presentation/content.js +39 -0
- package/dist/extensions/agent-browser/lib/results/presentation/diagnostics.js +3 -3
- package/dist/extensions/agent-browser/lib/results/presentation/large-output.js +41 -1
- package/dist/extensions/agent-browser/lib/results/presentation.js +16 -11
- package/dist/extensions/agent-browser/lib/runtime.js +18 -6
- package/dist/extensions/agent-browser/lib/session-page-state.js +3 -4
- package/dist/extensions/agent-browser/lib/tool-surface.js +131 -0
- package/dist/extensions/agent-browser/script-worker.js +4 -1
- package/docs/ARCHITECTURE.md +46 -62
- package/docs/COMMAND_REFERENCE.md +77 -136
- package/docs/ELECTRON.md +51 -49
- package/docs/RELEASE.md +31 -15
- package/docs/REQUIREMENTS.md +18 -14
- package/docs/SUPPORT_MATRIX.md +33 -15
- package/docs/TOOL_CONTRACT.md +216 -405
- package/package.json +1 -1
- package/scripts/doctor.mjs +2 -2
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import { isRecord } from "./parsing.js";
|
|
2
|
+
export const BROWSER_TRANSITION_ENTRY = "agent-browser-transition";
|
|
3
|
+
export const BROWSER_RESULT_TOOLS = new Set([
|
|
4
|
+
"agent_browser", "agent_browser_code", "agent_browser_action", "agent_browser_qa", "agent_browser_electron",
|
|
5
|
+
"agent_browser_source", "agent_browser_network_source",
|
|
6
|
+
]);
|
|
7
|
+
/** Direct tools and ordered code calls replay through the same browser state reducers. */
|
|
8
|
+
export function getBrowserResultMessage(entry) {
|
|
9
|
+
if (!isRecord(entry))
|
|
10
|
+
return undefined;
|
|
11
|
+
if (entry.type === "custom" && entry.customType === BROWSER_TRANSITION_ENTRY && isRecord(entry.data)) {
|
|
12
|
+
return isRecord(entry.data.details) && typeof entry.data.isError === "boolean" ? entry.data : undefined;
|
|
13
|
+
}
|
|
14
|
+
const message = entry.type === "message" && isRecord(entry.message) ? entry.message : undefined;
|
|
15
|
+
return message && typeof message.toolName === "string" && BROWSER_RESULT_TOOLS.has(message.toolName) ? message : undefined;
|
|
16
|
+
}
|
|
17
|
+
// Page data and rendered output are not state. Keep only fields consumed by the
|
|
18
|
+
// existing replay reducers; recording reservations retain their own native journal.
|
|
19
|
+
const TRANSITION_FIELDS = [
|
|
20
|
+
"args", "command", "subcommand", "sessionName", "namespace", "sessionMode", "usedImplicitSession",
|
|
21
|
+
"agentBrowserStarted", "resultCategory", "exitCode", "closeAllApplied", "attachedBrowserSession",
|
|
22
|
+
"sessionTabTarget", "sessionTabTargetUnknown", "sessionTabReopenPending", "refSnapshot", "refSnapshotInvalidation",
|
|
23
|
+
"readConfirmation", "compatibilityWorkaround", "managedSessionHeadedAutosaveDisabled", "managedSessionHeadedAutosaveInterval",
|
|
24
|
+
"managedSessionOutcome", "managedSessionCwd", "managedSessionRestoreDisabled", "artifactManifest", "electron", "compiledNetworkSourceLookup",
|
|
25
|
+
];
|
|
26
|
+
export function appendBrowserTransition(pi, toolCallId, details, isError) {
|
|
27
|
+
const state = Object.fromEntries(TRANSITION_FIELDS.filter(key => details[key] !== undefined).map(key => [key, details[key]]));
|
|
28
|
+
if (Array.isArray(details.batchSteps))
|
|
29
|
+
state.batchSteps = details.batchSteps.filter(isRecord).map(step => ({
|
|
30
|
+
command: step.command, success: step.success, lifecycle: step.lifecycle,
|
|
31
|
+
}));
|
|
32
|
+
pi.appendEntry(BROWSER_TRANSITION_ENTRY, { toolCallId, details: state, isError });
|
|
33
|
+
}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { readFileSync, statSync } from "node:fs";
|
|
2
2
|
import { dirname, isAbsolute, join } from "node:path";
|
|
3
3
|
import { isRecord } from "./parsing.js";
|
|
4
|
+
import { getBrowserResultMessage } from "./browser-transcript.js";
|
|
4
5
|
import { getAgentBrowserSessionIdentityKey } from "./argv-grammar.js";
|
|
5
6
|
function isDirectoryOwner(source) {
|
|
6
7
|
if (/^(?:npm:pi-change-working-dir|git:github\.com\/fitchmultz\/pi-change-working-dir(?:\.git)?)(?:@.+)?$/.test(source.source))
|
|
@@ -52,9 +53,10 @@ export function getBrowserCwdError(cwd) {
|
|
|
52
53
|
export function restoreManagedSessionCwd(branch, sessionName, namespace, fallback) {
|
|
53
54
|
const key = getAgentBrowserSessionIdentityKey(sessionName, namespace);
|
|
54
55
|
for (const entry of [...branch].reverse()) {
|
|
55
|
-
|
|
56
|
+
const message = getBrowserResultMessage(entry);
|
|
57
|
+
if (!message)
|
|
56
58
|
continue;
|
|
57
|
-
const details =
|
|
59
|
+
const details = message.details;
|
|
58
60
|
if (!isRecord(details) || typeof details.sessionName !== "string" || typeof details.managedSessionCwd !== "string" || !isAbsolute(details.managedSessionCwd))
|
|
59
61
|
continue;
|
|
60
62
|
if (getAgentBrowserSessionIdentityKey(details.sessionName, typeof details.namespace === "string" ? details.namespace : undefined) === key)
|
|
@@ -1,218 +1,7 @@
|
|
|
1
1
|
import { isRecord } from "../parsing.js";
|
|
2
2
|
import { summarizeNetworkFailures } from "../results/network.js";
|
|
3
|
-
import { getBatchResultItems, getCommandNameFromBatchItem
|
|
4
|
-
import {
|
|
5
|
-
import { AGENT_BROWSER_JOB_STEP_ACTIONS, AGENT_BROWSER_JOB_TYPE_DELAYED_TEXT_MAX_CHARACTERS, AGENT_BROWSER_QA_LOAD_STATES, } from "./types.js";
|
|
6
|
-
function getRequiredJobString(step, field, action) {
|
|
7
|
-
const value = step[field];
|
|
8
|
-
if (typeof value !== "string" || value.trim().length === 0) {
|
|
9
|
-
return { error: `job step ${action} requires a non-empty ${field} string.` };
|
|
10
|
-
}
|
|
11
|
-
return { value };
|
|
12
|
-
}
|
|
13
|
-
function compileJobClickOrFillStep(step, action) {
|
|
14
|
-
const hasSelector = typeof step.selector === "string" && step.selector.trim().length > 0;
|
|
15
|
-
const hasLocator = step.locator !== undefined || step.role !== undefined || step.name !== undefined || step.value !== undefined;
|
|
16
|
-
if (hasSelector && hasLocator) {
|
|
17
|
-
return { error: `job step ${action} must use either selector or semantic locator fields, not both.` };
|
|
18
|
-
}
|
|
19
|
-
if (hasSelector) {
|
|
20
|
-
if (action === "click")
|
|
21
|
-
return { args: ["click", step.selector] };
|
|
22
|
-
const text = getRequiredJobString(step, "text", action);
|
|
23
|
-
if (text.error)
|
|
24
|
-
return { error: text.error };
|
|
25
|
-
return { args: ["fill", step.selector, text.value] };
|
|
26
|
-
}
|
|
27
|
-
if (!hasLocator) {
|
|
28
|
-
return { error: `job step ${action} requires either a non-empty selector string or semantic locator fields.` };
|
|
29
|
-
}
|
|
30
|
-
const compiled = compileAgentBrowserSemanticAction({
|
|
31
|
-
action,
|
|
32
|
-
locator: step.locator,
|
|
33
|
-
name: step.name,
|
|
34
|
-
role: step.role,
|
|
35
|
-
text: step.text,
|
|
36
|
-
value: step.value,
|
|
37
|
-
});
|
|
38
|
-
if (compiled.error)
|
|
39
|
-
return { error: compiled.error.replaceAll("semanticAction", `job step ${action}`) };
|
|
40
|
-
return { args: compiled.compiled?.args };
|
|
41
|
-
}
|
|
42
|
-
function getUnsupportedJobStepField(step, allowedFields) {
|
|
43
|
-
return Object.keys(step).find((field) => !allowedFields.has(field));
|
|
44
|
-
}
|
|
45
|
-
function getUnsupportedJobStepFieldError(step, action, allowedFields) {
|
|
46
|
-
const unsupportedField = getUnsupportedJobStepField(step, allowedFields);
|
|
47
|
-
if (!unsupportedField)
|
|
48
|
-
return undefined;
|
|
49
|
-
const supportedFields = [...allowedFields].filter((field) => field !== "action");
|
|
50
|
-
const supportedText = supportedFields.length > 0 ? `supported fields are ${supportedFields.join(", ")}.` : "no additional fields are supported.";
|
|
51
|
-
return `job step ${action} does not support ${unsupportedField}; ${supportedText}`;
|
|
52
|
-
}
|
|
53
|
-
const JOB_STEP_ALLOWED_FIELDS = {
|
|
54
|
-
assertText: new Set(["action", "text"]),
|
|
55
|
-
assertUrl: new Set(["action", "url"]),
|
|
56
|
-
click: new Set(["action", "locator", "name", "role", "selector", "value"]),
|
|
57
|
-
fill: new Set(["action", "locator", "name", "role", "selector", "text", "value"]),
|
|
58
|
-
open: new Set(["action", "loadState", "url"]),
|
|
59
|
-
screenshot: new Set(["action", "path"]),
|
|
60
|
-
select: new Set(["action", "selector", "value", "values"]),
|
|
61
|
-
snapshot: new Set(["action"]),
|
|
62
|
-
type: new Set(["action", "delayMs", "press", "selector", "text"]),
|
|
63
|
-
wait: new Set(["action", "milliseconds"]),
|
|
64
|
-
waitForDownload: new Set(["action", "path"]),
|
|
65
|
-
};
|
|
66
|
-
function compileJobTypeSteps(step) {
|
|
67
|
-
const text = getRequiredJobString(step, "text", "type");
|
|
68
|
-
if (text.error)
|
|
69
|
-
return { error: text.error };
|
|
70
|
-
const selector = step.selector;
|
|
71
|
-
if (selector !== undefined && (typeof selector !== "string" || selector.trim().length === 0)) {
|
|
72
|
-
return { error: "job step type selector must be a non-empty string when provided." };
|
|
73
|
-
}
|
|
74
|
-
const delayMs = step.delayMs;
|
|
75
|
-
if (delayMs !== undefined && (typeof delayMs !== "number" || !Number.isInteger(delayMs) || delayMs <= 0)) {
|
|
76
|
-
return { error: "job step type delayMs must be a positive integer when provided." };
|
|
77
|
-
}
|
|
78
|
-
const press = step.press;
|
|
79
|
-
if (press !== undefined && (typeof press !== "string" || press.trim().length === 0)) {
|
|
80
|
-
return { error: "job step type press must be a non-empty key string when provided." };
|
|
81
|
-
}
|
|
82
|
-
const typedText = text.value;
|
|
83
|
-
const typedChars = Array.from(typedText);
|
|
84
|
-
if (typedChars.length === 0)
|
|
85
|
-
return { error: "job step type requires non-empty text." };
|
|
86
|
-
if (delayMs !== undefined && typedChars.length > AGENT_BROWSER_JOB_TYPE_DELAYED_TEXT_MAX_CHARACTERS) {
|
|
87
|
-
return { error: `job step type delayMs supports at most ${AGENT_BROWSER_JOB_TYPE_DELAYED_TEXT_MAX_CHARACTERS} characters; split longer text into shorter calls or omit delayMs.` };
|
|
88
|
-
}
|
|
89
|
-
const compiledSteps = [];
|
|
90
|
-
if (delayMs === undefined) {
|
|
91
|
-
compiledSteps.push({ action: "type", args: typeof selector === "string" ? ["type", selector, typedText] : ["keyboard", "type", typedText] });
|
|
92
|
-
}
|
|
93
|
-
else {
|
|
94
|
-
if (typeof selector === "string")
|
|
95
|
-
compiledSteps.push({ action: "type", args: ["focus", selector], generatedFrom: "type.selector" });
|
|
96
|
-
for (const [index, char] of typedChars.entries()) {
|
|
97
|
-
compiledSteps.push({ action: "type", args: ["keyboard", "type", char], generatedFrom: "type.delayMs" });
|
|
98
|
-
if (index < typedChars.length - 1)
|
|
99
|
-
compiledSteps.push({ action: "wait", args: ["wait", String(delayMs)], generatedFrom: "type.delayMs" });
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
if (typeof press === "string")
|
|
103
|
-
compiledSteps.push({ action: "type", args: ["press", press], generatedFrom: "type.press" });
|
|
104
|
-
return { steps: compiledSteps };
|
|
105
|
-
}
|
|
106
|
-
function compileOpenJobStep(step, index) {
|
|
107
|
-
const result = getRequiredJobString(step, "url", "open");
|
|
108
|
-
if (result.error)
|
|
109
|
-
return { error: result.error };
|
|
110
|
-
const extraSteps = [];
|
|
111
|
-
if (step.loadState !== undefined) {
|
|
112
|
-
if (typeof step.loadState !== "string" || !AGENT_BROWSER_QA_LOAD_STATES.includes(step.loadState)) {
|
|
113
|
-
return { error: `job.steps[${index}].loadState must be one of: ${AGENT_BROWSER_QA_LOAD_STATES.join(", ")}.` };
|
|
114
|
-
}
|
|
115
|
-
extraSteps.push({ action: "wait", args: ["wait", "--load", step.loadState], generatedFrom: "open.loadState" });
|
|
116
|
-
}
|
|
117
|
-
return { args: ["open", result.value], extraSteps };
|
|
118
|
-
}
|
|
119
|
-
function compileClickJobStep(step) {
|
|
120
|
-
return compileJobClickOrFillStep(step, "click");
|
|
121
|
-
}
|
|
122
|
-
function compileFillJobStep(step) {
|
|
123
|
-
return compileJobClickOrFillStep(step, "fill");
|
|
124
|
-
}
|
|
125
|
-
function compileTypeJobStep(step) {
|
|
126
|
-
const result = compileJobTypeSteps(step);
|
|
127
|
-
if (result.error)
|
|
128
|
-
return { error: result.error };
|
|
129
|
-
const [firstStep, ...extraSteps] = result.steps;
|
|
130
|
-
return { args: firstStep.args, extraSteps, generatedFrom: firstStep.generatedFrom };
|
|
131
|
-
}
|
|
132
|
-
function compileSelectJobStep(step, index) {
|
|
133
|
-
const selector = getRequiredJobString(step, "selector", "select");
|
|
134
|
-
if (selector.error)
|
|
135
|
-
return { error: selector.error };
|
|
136
|
-
const values = getSelectValues(step, `job.steps[${index}]`);
|
|
137
|
-
if (values.error)
|
|
138
|
-
return { error: values.error };
|
|
139
|
-
return { args: ["select", selector.value, ...values.values] };
|
|
140
|
-
}
|
|
141
|
-
function compileWaitJobStep(step) {
|
|
142
|
-
const milliseconds = step.milliseconds;
|
|
143
|
-
if (typeof milliseconds !== "number" || !Number.isInteger(milliseconds) || milliseconds <= 0) {
|
|
144
|
-
return { error: "job step wait requires a positive integer milliseconds value." };
|
|
145
|
-
}
|
|
146
|
-
return { args: ["wait", String(milliseconds)] };
|
|
147
|
-
}
|
|
148
|
-
function compileAssertTextJobStep(step) {
|
|
149
|
-
const result = getRequiredJobString(step, "text", "assertText");
|
|
150
|
-
if (result.error)
|
|
151
|
-
return { error: result.error };
|
|
152
|
-
return { args: ["wait", "--text", result.value] };
|
|
153
|
-
}
|
|
154
|
-
function compileAssertUrlJobStep(step) {
|
|
155
|
-
const result = getRequiredJobString(step, "url", "assertUrl");
|
|
156
|
-
if (result.error)
|
|
157
|
-
return { error: result.error };
|
|
158
|
-
return { args: ["wait", "--url", result.value] };
|
|
159
|
-
}
|
|
160
|
-
function compilePathArtifactJobStep(step, action) {
|
|
161
|
-
const result = getRequiredJobString(step, "path", action);
|
|
162
|
-
if (result.error)
|
|
163
|
-
return { error: result.error };
|
|
164
|
-
return { args: action === "waitForDownload" ? ["wait", "--download", result.value] : ["screenshot", result.value] };
|
|
165
|
-
}
|
|
166
|
-
// ponytail: allowedFields for each action live in JOB_STEP_ALLOWED_FIELDS (same key
|
|
167
|
-
// alignment enforced by Record<AgentBrowserJobStepAction, …>), so the compiler map no
|
|
168
|
-
// longer mirrors that set per entry; the call site looks it up by action.
|
|
169
|
-
const JOB_STEP_COMPILERS = {
|
|
170
|
-
assertText: compileAssertTextJobStep,
|
|
171
|
-
assertUrl: compileAssertUrlJobStep,
|
|
172
|
-
click: compileClickJobStep,
|
|
173
|
-
fill: compileFillJobStep,
|
|
174
|
-
open: compileOpenJobStep,
|
|
175
|
-
screenshot: (step) => compilePathArtifactJobStep(step, "screenshot"),
|
|
176
|
-
select: compileSelectJobStep,
|
|
177
|
-
snapshot: () => ({ args: ["snapshot", "-i"] }),
|
|
178
|
-
type: compileTypeJobStep,
|
|
179
|
-
wait: compileWaitJobStep,
|
|
180
|
-
waitForDownload: (step) => compilePathArtifactJobStep(step, "waitForDownload"),
|
|
181
|
-
};
|
|
182
|
-
export function compileAgentBrowserJob(input) {
|
|
183
|
-
if (!isRecord(input)) {
|
|
184
|
-
return { error: "job must be an object." };
|
|
185
|
-
}
|
|
186
|
-
const rawFailFast = input.failFast;
|
|
187
|
-
if (rawFailFast !== undefined && typeof rawFailFast !== "boolean") {
|
|
188
|
-
return { error: "job.failFast must be a boolean when provided." };
|
|
189
|
-
}
|
|
190
|
-
const failFast = rawFailFast !== false;
|
|
191
|
-
const rawSteps = input.steps;
|
|
192
|
-
if (!Array.isArray(rawSteps) || rawSteps.length === 0) {
|
|
193
|
-
return { error: "job.steps must be a non-empty array." };
|
|
194
|
-
}
|
|
195
|
-
const steps = [];
|
|
196
|
-
for (const [index, rawStep] of rawSteps.entries()) {
|
|
197
|
-
if (!isRecord(rawStep)) {
|
|
198
|
-
return { error: `job.steps[${index}] must be an object.` };
|
|
199
|
-
}
|
|
200
|
-
const action = rawStep.action;
|
|
201
|
-
if (typeof action !== "string" || !AGENT_BROWSER_JOB_STEP_ACTIONS.includes(action)) {
|
|
202
|
-
return { error: `job.steps[${index}].action must be one of: ${AGENT_BROWSER_JOB_STEP_ACTIONS.join(", ")}.` };
|
|
203
|
-
}
|
|
204
|
-
const jobAction = action;
|
|
205
|
-
const compile = JOB_STEP_COMPILERS[jobAction];
|
|
206
|
-
const unsupportedFieldError = getUnsupportedJobStepFieldError(rawStep, jobAction, JOB_STEP_ALLOWED_FIELDS[jobAction]);
|
|
207
|
-
if (unsupportedFieldError)
|
|
208
|
-
return { error: `job.steps[${index}]: ${unsupportedFieldError}` };
|
|
209
|
-
const compiledStep = compile(rawStep, index);
|
|
210
|
-
if (compiledStep.error)
|
|
211
|
-
return { error: compiledStep.error.startsWith(`job.steps[${index}]`) ? compiledStep.error : `job.steps[${index}]: ${compiledStep.error}` };
|
|
212
|
-
steps.push({ action: jobAction, args: compiledStep.args, generatedFrom: compiledStep.generatedFrom }, ...(compiledStep.extraSteps ?? []));
|
|
213
|
-
}
|
|
214
|
-
return { compiled: { args: failFast ? ["batch", "--bail"] : ["batch"], failFast, stdin: JSON.stringify(steps.map((step) => step.args)), steps } };
|
|
215
|
-
}
|
|
3
|
+
import { getBatchResultItems, getCommandNameFromBatchItem } from "./shared.js";
|
|
4
|
+
import { AGENT_BROWSER_QA_LOAD_STATES, } from "./types.js";
|
|
216
5
|
function describeQaChecksRun(checks) {
|
|
217
6
|
const parts = [`load:${checks.loadState}`];
|
|
218
7
|
if (checks.expectedText.length > 0)
|
|
@@ -1,149 +1,153 @@
|
|
|
1
1
|
import { JsonSchema } from "../json-schema.js";
|
|
2
2
|
import { StringEnum as localStringEnum } from "../string-enum-schema.js";
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
|
|
3
|
+
import { ELECTRON_DISCOVERY_DEFAULT_MAX_RESULTS, ELECTRON_DISCOVERY_MAX_RESULTS } from "../electron/discovery.js";
|
|
4
|
+
import { AGENT_BROWSER_ELECTRON_HANDOFFS, AGENT_BROWSER_ELECTRON_TARGET_TYPES, AGENT_BROWSER_QA_LOAD_STATES, AGENT_BROWSER_SEMANTIC_ACTIONS, AGENT_BROWSER_SEMANTIC_LOCATORS, DEFAULT_SESSION_MODE, SOURCE_LOOKUP_MAX_WORKSPACE_FILES, } from "./types.js";
|
|
5
|
+
export const AGENT_BROWSER_ADVANCED_TOOLS = ["action", "qa", "electron", "source", "network"];
|
|
6
|
+
export const AGENT_BROWSER_CODE_MAX_TIMEOUT_MS = 300_000;
|
|
7
|
+
function outputProperties(Type) {
|
|
8
|
+
return {
|
|
9
|
+
outputPath: Type.Optional(Type.String({ description: "Result-data path, separate from browser artifact paths.", minLength: 1 })),
|
|
10
|
+
timeoutMs: Type.Optional(Type.Integer({ description: "Timeout in ms; exceed explicit waits.", minimum: 1 })),
|
|
11
|
+
};
|
|
12
|
+
}
|
|
13
|
+
function sessionModeProperty(Type, StringEnum) {
|
|
14
|
+
return Type.Optional(StringEnum(["auto", "fresh"], {
|
|
15
|
+
description: "Native configured sessions win; auto reuses the browser, fresh intentionally launches a separate managed browser.",
|
|
16
|
+
default: DEFAULT_SESSION_MODE,
|
|
17
|
+
}));
|
|
18
|
+
}
|
|
19
|
+
// Keep the always-on schemas small; specialized guidance belongs to each advanced tool.
|
|
7
20
|
export function createAgentBrowserParamsSchema(Type = JsonSchema, StringEnum = localStringEnum) {
|
|
8
|
-
return Type.Object({
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
action: StringEnum(["check", "click", "fill"]),
|
|
37
|
-
}, { not: { required: ["values"] } }),
|
|
38
|
-
],
|
|
39
|
-
description: "Stable locator or direct-selector action. values only with action=select.",
|
|
40
|
-
})),
|
|
41
|
-
qa: Type.Optional(Type.Union([
|
|
42
|
-
Type.Object({
|
|
43
|
-
attached: Type.Literal(true),
|
|
44
|
-
expectedText: Type.Optional(Type.Union([Type.String(), Type.Array(Type.String())])),
|
|
45
|
-
expectedSelector: Type.Optional(Type.String()),
|
|
46
|
-
screenshotPath: Type.Optional(Type.String()),
|
|
47
|
-
checkConsole: Type.Optional(Type.Boolean()),
|
|
48
|
-
checkErrors: Type.Optional(Type.Boolean()),
|
|
49
|
-
checkNetwork: Type.Optional(Type.Boolean()),
|
|
50
|
-
loadState: Type.Optional(StringEnum(AGENT_BROWSER_QA_LOAD_STATES)),
|
|
51
|
-
}, { additionalProperties: false }),
|
|
52
|
-
Type.Object({
|
|
53
|
-
url: Type.String(),
|
|
54
|
-
attached: Type.Optional(Type.Literal(false)),
|
|
55
|
-
expectedText: Type.Optional(Type.Union([Type.String(), Type.Array(Type.String())])),
|
|
56
|
-
expectedSelector: Type.Optional(Type.String()),
|
|
57
|
-
screenshotPath: Type.Optional(Type.String()),
|
|
58
|
-
checkConsole: Type.Optional(Type.Boolean()),
|
|
59
|
-
checkErrors: Type.Optional(Type.Boolean()),
|
|
60
|
-
checkNetwork: Type.Optional(Type.Boolean()),
|
|
61
|
-
loadState: Type.Optional(StringEnum(AGENT_BROWSER_QA_LOAD_STATES)),
|
|
62
|
-
}, { additionalProperties: false }),
|
|
63
|
-
], { description: "QA a URL or current session (attached=true). Default readiness: domcontentloaded; use networkidle only without long-lived requests. URL diagnostics default on, attached diagnostics off." })),
|
|
64
|
-
sourceLookup: Type.Optional(Type.Object({
|
|
65
|
-
selector: Type.Optional(Type.String({ description: "Visible selector or @ref." })),
|
|
66
|
-
reactFiberId: Type.Optional(Type.String({ description: "React fiber id; requires --enable react-devtools." })),
|
|
67
|
-
componentName: Type.Optional(Type.String({ description: "Component for local source search." })),
|
|
68
|
-
includeDomHints: Type.Optional(Type.Boolean({ description: "Inspect DOM source hints; default true." })),
|
|
69
|
-
maxWorkspaceFiles: Type.Optional(Type.Number({ description: "Source scan cap; default 2000.", minimum: 1, maximum: SOURCE_LOOKUP_MAX_WORKSPACE_FILES })),
|
|
70
|
-
}, { additionalProperties: false, description: "EXPERIMENTAL UI-to-source candidates; not guaranteed mappings." })),
|
|
71
|
-
networkSourceLookup: Type.Optional(Type.Object({
|
|
72
|
-
filter: Type.Optional(Type.String({ description: "Network request filter." })),
|
|
73
|
-
namespace: Type.Optional(Type.String()),
|
|
74
|
-
requestId: Type.Optional(Type.String({ description: "Request id to inspect." })),
|
|
75
|
-
session: Type.Optional(Type.String()),
|
|
76
|
-
url: Type.Optional(Type.String({ description: "Failed URL or fragment." })),
|
|
77
|
-
maxWorkspaceFiles: Type.Optional(Type.Number({ description: "Source scan cap; default 2000.", minimum: 1, maximum: SOURCE_LOOKUP_MAX_WORKSPACE_FILES })),
|
|
78
|
-
}, { additionalProperties: false, description: "EXPERIMENTAL failed-request-to-source candidates; not proof." })),
|
|
79
|
-
electron: Type.Optional(Type.Union([
|
|
80
|
-
Type.Object({
|
|
81
|
-
action: StringEnum(["list"]),
|
|
82
|
-
query: Type.Optional(Type.String({ description: "Case-insensitive app filter.", minLength: 1 })),
|
|
83
|
-
maxResults: Type.Optional(Type.Integer({ description: `Result cap; default ${ELECTRON_DISCOVERY_DEFAULT_MAX_RESULTS}, values over ${ELECTRON_DISCOVERY_MAX_RESULTS} are clamped.`, minimum: 1 })),
|
|
84
|
-
}, { additionalProperties: false }),
|
|
85
|
-
Type.Object({
|
|
86
|
-
action: StringEnum(["launch"]),
|
|
87
|
-
appPath: Type.Optional(Type.String({ description: "macOS .app path.", minLength: 1 })),
|
|
88
|
-
appName: Type.Optional(Type.String({ description: "Name from electron.list.", minLength: 1 })),
|
|
89
|
-
bundleId: Type.Optional(Type.String({ description: "Bundle id from electron.list.", minLength: 1 })),
|
|
90
|
-
executablePath: Type.Optional(Type.String({ description: "Executable path.", minLength: 1 })),
|
|
91
|
-
appArgs: Type.Optional(Type.Array(Type.String({ minLength: 1 }))),
|
|
92
|
-
handoff: Type.Optional(StringEnum(AGENT_BROWSER_ELECTRON_HANDOFFS)),
|
|
93
|
-
targetType: Type.Optional(StringEnum(AGENT_BROWSER_ELECTRON_TARGET_TYPES)),
|
|
94
|
-
timeoutMs: Type.Optional(Type.Integer({ minimum: 1 })),
|
|
95
|
-
allow: Type.Optional(Type.Array(Type.String({ minLength: 1 }))),
|
|
96
|
-
deny: Type.Optional(Type.Array(Type.String({ minLength: 1 }))),
|
|
97
|
-
}, { additionalProperties: false }),
|
|
98
|
-
Type.Object({
|
|
99
|
-
action: StringEnum(["status", "cleanup"]),
|
|
100
|
-
launchId: Type.String({ description: "Tracked launch id.", minLength: 1 }),
|
|
101
|
-
timeoutMs: Type.Optional(Type.Integer({ minimum: 1 })),
|
|
102
|
-
}, { additionalProperties: false }),
|
|
103
|
-
Type.Object({
|
|
104
|
-
action: StringEnum(["status", "cleanup"]),
|
|
105
|
-
all: Type.Literal(true),
|
|
106
|
-
timeoutMs: Type.Optional(Type.Integer({ minimum: 1 })),
|
|
107
|
-
}, { additionalProperties: false }),
|
|
108
|
-
Type.Object({
|
|
109
|
-
action: StringEnum(["status", "cleanup"]),
|
|
110
|
-
timeoutMs: Type.Optional(Type.Integer({ minimum: 1 })),
|
|
111
|
-
}, { additionalProperties: false }),
|
|
112
|
-
Type.Object({
|
|
113
|
-
action: StringEnum(["probe"]),
|
|
114
|
-
launchId: Type.Optional(Type.String({ description: "Tracked launch id.", minLength: 1 })),
|
|
115
|
-
timeoutMs: Type.Optional(Type.Integer({ minimum: 1 })),
|
|
116
|
-
}, { additionalProperties: false }),
|
|
117
|
-
], { description: "Electron discovery, isolated-profile launch, status, probe, or cleanup. Launch defaults: handoff=snapshot, targetType=page; deny wins over allow; lifecycle/debug appArgs rejected." })),
|
|
118
|
-
job: Type.Optional(Type.Object({
|
|
119
|
-
failFast: Type.Optional(Type.Boolean({ description: "Stop on first failure; default true." })),
|
|
120
|
-
steps: Type.Array(Type.Object({
|
|
121
|
-
action: StringEnum(AGENT_BROWSER_JOB_STEP_ACTIONS),
|
|
122
|
-
url: Type.Optional(Type.String({ description: "Open URL, or assertUrl exact URL/glob that distinguishes the expected state from the starting page." })),
|
|
123
|
-
loadState: Type.Optional(StringEnum(AGENT_BROWSER_QA_LOAD_STATES, { description: "Readiness wait after open." })),
|
|
124
|
-
selector: Type.Optional(Type.String({ description: "Selector or @ref." })),
|
|
125
|
-
locator: Type.Optional(StringEnum(AGENT_BROWSER_SEMANTIC_LOCATORS, { description: "Locator when selector is omitted." })),
|
|
126
|
-
role: Type.Optional(Type.String({ description: "Role locator." })),
|
|
127
|
-
name: Type.Optional(Type.String({ description: "Accessible name." })),
|
|
128
|
-
text: Type.Optional(Type.String({ description: "Fill/type text; assertText uses only text, not selector/locator fields." })),
|
|
129
|
-
value: Type.Optional(Type.String({ description: "Select option or locator value." })),
|
|
130
|
-
values: Type.Optional(Type.Array(Type.String(), { description: "Select options.", minItems: 1 })),
|
|
131
|
-
path: Type.Optional(Type.String({ description: "Download or screenshot path." })),
|
|
132
|
-
delayMs: Type.Optional(Type.Integer({ description: `Per-character type delay; text is capped at ${AGENT_BROWSER_JOB_TYPE_DELAYED_TEXT_MAX_CHARACTERS} characters.`, minimum: 1 })),
|
|
133
|
-
press: Type.Optional(Type.String({ description: "Key to press after typing." })),
|
|
134
|
-
milliseconds: Type.Optional(Type.Number({ description: "Wait duration in milliseconds." })),
|
|
135
|
-
}, { additionalProperties: false }), { minItems: 1 }),
|
|
136
|
-
}, { additionalProperties: false, description: "Constrained multi-step batch. Clicks can stale later refs; split and re-snapshot before using them." })),
|
|
137
|
-
stdin: Type.Optional(Type.String({ description: "For batch, a JSON array of token arrays, e.g. [[\"get\",\"title\"]]. Raw text only for eval --stdin or auth save --password-stdin; unavailable with structured modes and electron." })),
|
|
138
|
-
outputPath: Type.Optional(Type.String({ description: "Workspace-relative or absolute result-data path; keep it distinct from screenshot, download, recording, and other browser artifact destinations.", minLength: 1 })),
|
|
139
|
-
timeoutMs: Type.Optional(Type.Integer({ description: "Wrapper timeout in ms; exceed explicit waits. electron.list has no configurable timeout; other Electron actions use electron.timeoutMs.", minimum: 1 })),
|
|
140
|
-
sessionMode: Type.Optional(StringEnum(["auto", "fresh"], {
|
|
141
|
-
description: "Native configured sessions win; otherwise auto reuses the managed session and fresh starts a new managed browser for launch-only flags.",
|
|
142
|
-
default: DEFAULT_SESSION_MODE,
|
|
143
|
-
})),
|
|
21
|
+
return Type.Unsafe(Type.Object({
|
|
22
|
+
args: Type.Array(Type.String(), { description: "Native agent-browser argv without the binary or shell syntax. Fixed sequences: batch --bail with JSON-array stdin.", minItems: 1 }),
|
|
23
|
+
stdin: Type.Optional(Type.String({ description: "Native batch JSON, eval --stdin source, or auth save --password-stdin text." })),
|
|
24
|
+
...outputProperties(Type),
|
|
25
|
+
sessionMode: sessionModeProperty(Type, StringEnum),
|
|
26
|
+
}, { additionalProperties: false }));
|
|
27
|
+
}
|
|
28
|
+
export function createAgentBrowserCodeParamsSchema(Type = JsonSchema) {
|
|
29
|
+
return Type.Unsafe(Type.Object({
|
|
30
|
+
code: Type.String({ description: "JavaScript with await browser({args, stdin?, timeoutMs?}), emit(JSON), and emitImage(image handle). Fresh JS context; persistent browser.", minLength: 1, maxLength: 65_536 }),
|
|
31
|
+
session: Type.Optional(Type.String({ description: "Explicit native session; omitted uses the ordinary default browser.", minLength: 1 })),
|
|
32
|
+
namespace: Type.Optional(Type.String({ description: "Native namespace; empty string selects the default namespace." })),
|
|
33
|
+
...outputProperties(Type),
|
|
34
|
+
timeoutMs: Type.Optional(Type.Integer({ minimum: 1, maximum: AGENT_BROWSER_CODE_MAX_TIMEOUT_MS })),
|
|
35
|
+
}, { additionalProperties: false }));
|
|
36
|
+
}
|
|
37
|
+
export function createAgentBrowserActionParamsSchema(Type = JsonSchema, StringEnum = localStringEnum) {
|
|
38
|
+
return Type.Unsafe(Type.Object({
|
|
39
|
+
action: StringEnum(AGENT_BROWSER_SEMANTIC_ACTIONS),
|
|
40
|
+
locator: Type.Optional(StringEnum(AGENT_BROWSER_SEMANTIC_LOCATORS, { description: "Select supports role or label." })),
|
|
41
|
+
value: Type.Optional(Type.String({ description: "Locator value or select option; label text for select by label." })),
|
|
42
|
+
values: Type.Optional(Type.Array(Type.String(), { description: "Select options; required for select by label.", minItems: 1 })),
|
|
43
|
+
selector: Type.Optional(Type.String({ description: "Direct selector or current @ref." })),
|
|
44
|
+
text: Type.Optional(Type.String({ description: "Fill text." })),
|
|
45
|
+
role: Type.Optional(Type.String({ description: "Role locator; select needs combobox or listbox." })),
|
|
46
|
+
name: Type.Optional(Type.String({ description: "Accessible name." })),
|
|
47
|
+
session: Type.Optional(Type.String({ description: "Upstream session name." })),
|
|
48
|
+
...outputProperties(Type),
|
|
144
49
|
}, {
|
|
145
50
|
additionalProperties: false,
|
|
146
|
-
|
|
147
|
-
|
|
51
|
+
// Top-level properties let Pi normalize optional nulls before validating branches.
|
|
52
|
+
anyOf: [
|
|
53
|
+
Type.Object({ action: StringEnum(["select"]), locator: Type.Optional(StringEnum(["role", "label"])) }, { not: { required: ["text"] } }),
|
|
54
|
+
Type.Object({ action: StringEnum(["check", "click", "fill"]) }, { not: { required: ["values"] } }),
|
|
55
|
+
],
|
|
56
|
+
}));
|
|
57
|
+
}
|
|
58
|
+
export function createAgentBrowserQaParamsSchema(Type = JsonSchema, StringEnum = localStringEnum) {
|
|
59
|
+
return Type.Unsafe(Type.Object({
|
|
60
|
+
url: Type.Optional(Type.String()),
|
|
61
|
+
attached: Type.Optional(Type.Boolean()),
|
|
62
|
+
expectedText: Type.Optional(Type.Union([Type.String(), Type.Array(Type.String())])),
|
|
63
|
+
expectedSelector: Type.Optional(Type.String()),
|
|
64
|
+
screenshotPath: Type.Optional(Type.String()),
|
|
65
|
+
checkConsole: Type.Optional(Type.Boolean()),
|
|
66
|
+
checkErrors: Type.Optional(Type.Boolean()),
|
|
67
|
+
checkNetwork: Type.Optional(Type.Boolean()),
|
|
68
|
+
loadState: Type.Optional(StringEnum(AGENT_BROWSER_QA_LOAD_STATES)),
|
|
69
|
+
...outputProperties(Type),
|
|
70
|
+
sessionMode: sessionModeProperty(Type, StringEnum),
|
|
71
|
+
}, {
|
|
72
|
+
additionalProperties: false,
|
|
73
|
+
anyOf: [
|
|
74
|
+
Type.Object({ attached: Type.Literal(true), sessionMode: Type.Optional(StringEnum(["auto"])) }, { not: { required: ["url"] } }),
|
|
75
|
+
Type.Object({ url: Type.String(), attached: Type.Optional(Type.Literal(false)) }),
|
|
76
|
+
],
|
|
77
|
+
}));
|
|
78
|
+
}
|
|
79
|
+
export function createAgentBrowserElectronParamsSchema(Type = JsonSchema, StringEnum = localStringEnum) {
|
|
80
|
+
const common = { outputPath: outputProperties(Type).outputPath };
|
|
81
|
+
const timeoutMs = Type.Optional(Type.Integer({ minimum: 1 }));
|
|
82
|
+
return Type.Unsafe(Type.Union([
|
|
83
|
+
Type.Object({
|
|
84
|
+
action: StringEnum(["list"]),
|
|
85
|
+
query: Type.Optional(Type.String({ description: "Case-insensitive app filter.", minLength: 1 })),
|
|
86
|
+
maxResults: Type.Optional(Type.Integer({ description: `Result cap; default ${ELECTRON_DISCOVERY_DEFAULT_MAX_RESULTS}, clamped to ${ELECTRON_DISCOVERY_MAX_RESULTS}.`, minimum: 1 })),
|
|
87
|
+
...common,
|
|
88
|
+
}, { additionalProperties: false }),
|
|
89
|
+
Type.Object({
|
|
90
|
+
action: StringEnum(["launch"]),
|
|
91
|
+
appPath: Type.Optional(Type.String({ description: "macOS .app path.", minLength: 1 })),
|
|
92
|
+
appName: Type.Optional(Type.String({ description: "Name from agent_browser_electron list.", minLength: 1 })),
|
|
93
|
+
bundleId: Type.Optional(Type.String({ minLength: 1 })),
|
|
94
|
+
executablePath: Type.Optional(Type.String({ minLength: 1 })),
|
|
95
|
+
appArgs: Type.Optional(Type.Array(Type.String({ minLength: 1 }))),
|
|
96
|
+
handoff: Type.Optional(StringEnum(AGENT_BROWSER_ELECTRON_HANDOFFS)),
|
|
97
|
+
targetType: Type.Optional(StringEnum(AGENT_BROWSER_ELECTRON_TARGET_TYPES)),
|
|
98
|
+
timeoutMs,
|
|
99
|
+
allow: Type.Optional(Type.Array(Type.String({ minLength: 1 }))),
|
|
100
|
+
deny: Type.Optional(Type.Array(Type.String({ minLength: 1 }))),
|
|
101
|
+
...common,
|
|
102
|
+
}, { additionalProperties: false }),
|
|
103
|
+
Type.Object({
|
|
104
|
+
action: StringEnum(["status", "cleanup"]),
|
|
105
|
+
launchId: Type.Optional(Type.String({ minLength: 1 })),
|
|
106
|
+
all: Type.Optional(Type.Literal(true)),
|
|
107
|
+
timeoutMs,
|
|
108
|
+
...common,
|
|
109
|
+
}, { additionalProperties: false, not: { required: ["launchId", "all"] } }),
|
|
110
|
+
Type.Object({
|
|
111
|
+
action: StringEnum(["probe"]),
|
|
112
|
+
launchId: Type.Optional(Type.String({ minLength: 1 })),
|
|
113
|
+
timeoutMs,
|
|
114
|
+
...common,
|
|
115
|
+
}, { additionalProperties: false }),
|
|
116
|
+
]));
|
|
117
|
+
}
|
|
118
|
+
export function createAgentBrowserSourceParamsSchema(Type = JsonSchema, StringEnum = localStringEnum) {
|
|
119
|
+
return Type.Unsafe(Type.Object({
|
|
120
|
+
selector: Type.Optional(Type.String({ description: "Visible selector or @ref." })),
|
|
121
|
+
reactFiberId: Type.Optional(Type.String({ description: "React fiber id; requires --enable react-devtools." })),
|
|
122
|
+
componentName: Type.Optional(Type.String({ description: "Component for local source search." })),
|
|
123
|
+
includeDomHints: Type.Optional(Type.Boolean({ description: "Inspect DOM hints; default true." })),
|
|
124
|
+
maxWorkspaceFiles: Type.Optional(Type.Integer({ description: "Source scan cap; default 2000.", minimum: 1, maximum: SOURCE_LOOKUP_MAX_WORKSPACE_FILES })),
|
|
125
|
+
...outputProperties(Type),
|
|
126
|
+
sessionMode: sessionModeProperty(Type, StringEnum),
|
|
127
|
+
}, { additionalProperties: false }));
|
|
128
|
+
}
|
|
129
|
+
export function createAgentBrowserNetworkSourceParamsSchema(Type = JsonSchema, StringEnum = localStringEnum) {
|
|
130
|
+
return Type.Unsafe(Type.Object({
|
|
131
|
+
filter: Type.Optional(Type.String({ description: "Network request filter." })),
|
|
132
|
+
namespace: Type.Optional(Type.String()),
|
|
133
|
+
requestId: Type.Optional(Type.String({ description: "Request id to inspect." })),
|
|
134
|
+
session: Type.Optional(Type.String()),
|
|
135
|
+
url: Type.Optional(Type.String({ description: "Failed URL or fragment." })),
|
|
136
|
+
maxWorkspaceFiles: Type.Optional(Type.Integer({ description: "Source scan cap; default 2000.", minimum: 1, maximum: SOURCE_LOOKUP_MAX_WORKSPACE_FILES })),
|
|
137
|
+
...outputProperties(Type),
|
|
138
|
+
sessionMode: sessionModeProperty(Type, StringEnum),
|
|
139
|
+
}, { additionalProperties: false }));
|
|
140
|
+
}
|
|
141
|
+
export function createAgentBrowserToolsParamsSchema(Type = JsonSchema, StringEnum = localStringEnum) {
|
|
142
|
+
return Type.Unsafe(Type.Object({
|
|
143
|
+
enable: Type.Optional(Type.Array(StringEnum(AGENT_BROWSER_ADVANCED_TOOLS), { description: "Add capabilities to active tools; omit to list inventory." })),
|
|
144
|
+
}, { additionalProperties: false }));
|
|
148
145
|
}
|
|
149
146
|
export const AGENT_BROWSER_PARAMS = createAgentBrowserParamsSchema();
|
|
147
|
+
export const AGENT_BROWSER_CODE_PARAMS = createAgentBrowserCodeParamsSchema();
|
|
148
|
+
export const AGENT_BROWSER_ACTION_PARAMS = createAgentBrowserActionParamsSchema();
|
|
149
|
+
export const AGENT_BROWSER_QA_PARAMS = createAgentBrowserQaParamsSchema();
|
|
150
|
+
export const AGENT_BROWSER_ELECTRON_PARAMS = createAgentBrowserElectronParamsSchema();
|
|
151
|
+
export const AGENT_BROWSER_SOURCE_PARAMS = createAgentBrowserSourceParamsSchema();
|
|
152
|
+
export const AGENT_BROWSER_NETWORK_SOURCE_PARAMS = createAgentBrowserNetworkSourceParamsSchema();
|
|
153
|
+
export const AGENT_BROWSER_TOOLS_PARAMS = createAgentBrowserToolsParamsSchema();
|