pi-agent-browser-native 0.6.17 → 0.7.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/CHANGELOG.md +19 -0
- package/README.md +55 -167
- package/dist/extensions/agent-browser/index.js +312 -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 +138 -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 +35 -21
- package/docs/REQUIREMENTS.md +18 -14
- package/docs/SUPPORT_MATRIX.md +33 -15
- package/docs/TOOL_CONTRACT.md +216 -405
- package/docs/platform-smoke.md +2 -2
- package/package.json +1 -1
- package/scripts/doctor.mjs +2 -2
|
@@ -2,7 +2,7 @@ import { writePersistentSessionArtifactFile, writeSecureTempFile, } from "../../
|
|
|
2
2
|
import { buildEvictedSessionArtifactEntries } from "../artifact-manifest.js";
|
|
3
3
|
import { countLines, truncateText } from "../text.js";
|
|
4
4
|
import { applyArtifactManifest } from "./artifacts.js";
|
|
5
|
-
import { getPresentationText } from "./content.js";
|
|
5
|
+
import { getPresentationText, projectAgentBrowserObservation } from "./content.js";
|
|
6
6
|
import { redactModelFacingText, stringifyModelFacing } from "./common.js";
|
|
7
7
|
const LARGE_OUTPUT_INLINE_MAX_CHARS = 8_000;
|
|
8
8
|
const LARGE_OUTPUT_INLINE_MAX_LINES = 120;
|
|
@@ -88,6 +88,46 @@ function buildSpillArtifactEntries(options) {
|
|
|
88
88
|
...buildEvictedSessionArtifactEntries(options.evictedArtifacts, nowMs),
|
|
89
89
|
];
|
|
90
90
|
}
|
|
91
|
+
export const OBSERVATION_INLINE_MAX_CHARS = 16_000;
|
|
92
|
+
/** Bound the final model-visible result, after recovery/diagnostic assembly. Never truncate executable actions. */
|
|
93
|
+
export async function renderAgentBrowserObservation(options) {
|
|
94
|
+
const observation = projectAgentBrowserObservation(options.details, options.succeeded);
|
|
95
|
+
const images = options.content.filter(part => part.type === "image");
|
|
96
|
+
const prose = options.content.filter(part => part.type === "text").map(part => part.text).join("\n\n");
|
|
97
|
+
const { data: _data, error: _error, summary: _summary, ...metadata } = observation;
|
|
98
|
+
let text = options.json ? JSON.stringify(observation, null, 2) : `${prose}\n\nObservation: ${JSON.stringify(metadata)}`;
|
|
99
|
+
let artifactManifest = options.details.artifactManifest;
|
|
100
|
+
if (text.length > OBSERVATION_INLINE_MAX_CHARS) {
|
|
101
|
+
let spill;
|
|
102
|
+
let spillError;
|
|
103
|
+
try {
|
|
104
|
+
spill = await writeLargeOutputSpillFile({ data: { ...observation, ...(!options.json ? { text: prose } : {}) }, persistentArtifactStore: options.persistentArtifactStore, text });
|
|
105
|
+
artifactManifest = applyArtifactManifest({ content: [], summary: "" }, artifactManifest, buildSpillArtifactEntries({
|
|
106
|
+
commandInfo: { command: typeof options.details.command === "string" ? options.details.command : undefined },
|
|
107
|
+
evictedArtifacts: spill.evictedArtifacts, path: spill.path, storageScope: spill.storageScope,
|
|
108
|
+
})).artifactManifest;
|
|
109
|
+
}
|
|
110
|
+
catch (error) {
|
|
111
|
+
spillError = error instanceof Error ? error.message : String(error);
|
|
112
|
+
}
|
|
113
|
+
const compact = {
|
|
114
|
+
success: options.succeeded, resultCategory: observation.resultCategory,
|
|
115
|
+
failureCategory: observation.failureCategory, successCategory: observation.successCategory,
|
|
116
|
+
summary: typeof observation.summary === "string" ? truncateText(observation.summary, 700) : undefined,
|
|
117
|
+
compacted: true,
|
|
118
|
+
...(spill ? { observationPath: spill.path, retrieve: "Read observationPath for the complete redacted observation, including exact recovery actions and requested data." }
|
|
119
|
+
: { observationUnavailable: truncateText(redactModelFacingText(spillError ?? "Spill could not be written; request a smaller result."), 1_000) }),
|
|
120
|
+
};
|
|
121
|
+
for (const key of ["sessionName", "namespace", "codeRun", "error", "failures", "nextActions", "artifactVerification", "imageObservations", "data", "fullOutputPath", "fullOutputPaths"]) {
|
|
122
|
+
if (observation[key] !== undefined && JSON.stringify({ ...compact, [key]: observation[key] }, null, 2).length <= OBSERVATION_INLINE_MAX_CHARS - 500)
|
|
123
|
+
compact[key] = observation[key];
|
|
124
|
+
}
|
|
125
|
+
text = JSON.stringify(compact, null, 2);
|
|
126
|
+
if (!options.json)
|
|
127
|
+
text = `Browser observation compacted.\n${text}`;
|
|
128
|
+
}
|
|
129
|
+
return { content: [{ type: "text", text }, ...images], artifactManifest };
|
|
130
|
+
}
|
|
91
131
|
export async function compactLargePresentationOutput(options) {
|
|
92
132
|
const text = getPresentationText(options.presentation);
|
|
93
133
|
if (text.length === 0 || !shouldCompactLargeOutput(text)) {
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { getScreenshotCapture } from "../orchestration/browser-run/screenshot-observation.js";
|
|
1
2
|
import { isRecord } from "../parsing.js";
|
|
2
3
|
import { buildReadConfirmationNextActions, nextReadConfirmation } from "../read-confirmation.js";
|
|
3
4
|
import { extractUpstreamCommandTokens, parseCommandInfo, redactInvocationArgs } from "../runtime.js";
|
|
@@ -56,13 +57,9 @@ export async function buildToolPresentation(options) {
|
|
|
56
57
|
const recordingBatch = commandInfo.command === "batch" && isAgentBrowserBatchResultArray(envelope?.data)
|
|
57
58
|
&& envelope.data.some((row) => row.command?.[0] === "record");
|
|
58
59
|
if (errorText && !recordingCommand && !recordingBatch) {
|
|
59
|
-
return buildErrorPresentation({
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
errorText,
|
|
63
|
-
presentationCommand: presentationCommandInfo.command,
|
|
64
|
-
sessionName,
|
|
65
|
-
});
|
|
60
|
+
return { ...buildErrorPresentation({ args, commandInfo, errorText, presentationCommand: presentationCommandInfo.command, sessionName }),
|
|
61
|
+
data: redactPresentationData(commandInfoWithTokens, envelope?.data),
|
|
62
|
+
};
|
|
66
63
|
}
|
|
67
64
|
let data = enrichStreamStatusData(commandInfoWithTokens, envelope?.data);
|
|
68
65
|
if (commandInfo.command === "session" && commandInfo.subcommand === "info" && isRecord(data)) {
|
|
@@ -85,6 +82,7 @@ export async function buildToolPresentation(options) {
|
|
|
85
82
|
artifactMinUpdatedAtMs: options.artifactMinUpdatedAtMs,
|
|
86
83
|
artifactRequests: options.batchArtifactRequests,
|
|
87
84
|
buildNestedToolPresentation: buildToolPresentation,
|
|
85
|
+
modelVisible: options.modelVisible,
|
|
88
86
|
cwd,
|
|
89
87
|
data,
|
|
90
88
|
namespace,
|
|
@@ -95,14 +93,14 @@ export async function buildToolPresentation(options) {
|
|
|
95
93
|
summary,
|
|
96
94
|
});
|
|
97
95
|
}
|
|
98
|
-
else if (commandInfo.command === "snapshot" && isRecord(data)) {
|
|
96
|
+
else if (options.modelVisible !== false && commandInfo.command === "snapshot" && isRecord(data)) {
|
|
99
97
|
presentation = await buildSnapshotPresentation(data, persistentArtifactStore, artifactManifest);
|
|
100
98
|
}
|
|
101
99
|
else {
|
|
102
100
|
presentation = {
|
|
103
101
|
artifactVerification,
|
|
104
102
|
artifacts: artifacts.length > 0 ? artifacts : undefined,
|
|
105
|
-
content: [{ type: "text", text: artifactText ?? formatPresentationContentText(commandInfoWithTokens, data, compiledSemanticAction) }],
|
|
103
|
+
content: options.modelVisible === false ? [] : [{ type: "text", text: artifactText ?? formatPresentationContentText(commandInfoWithTokens, data, compiledSemanticAction) }],
|
|
106
104
|
data: presentationData,
|
|
107
105
|
summary,
|
|
108
106
|
};
|
|
@@ -143,8 +141,13 @@ export async function buildToolPresentation(options) {
|
|
|
143
141
|
}
|
|
144
142
|
const imagePath = artifactRequest?.absolutePath ?? extractImagePath(commandInfo, cwd, data)
|
|
145
143
|
?? (recordingCommand ? artifacts.find((artifact) => artifact.kind === "image" && artifact.status === "saved")?.absolutePath : undefined);
|
|
146
|
-
const
|
|
147
|
-
const
|
|
144
|
+
const attachImage = imagePath && !(isRecord(data) && data.changed === false) && !artifacts.some(artifact => artifact.absolutePath === imagePath && ["missing", "stale", "failed"].includes(artifact.status ?? ""));
|
|
145
|
+
const presentationWithImage = attachImage ? await attachInlineImage(presentation, imagePath, options.modelVisible) : presentation;
|
|
146
|
+
if (commandInfo.command === "screenshot" && presentationWithImage.imageObservations) {
|
|
147
|
+
for (const image of presentationWithImage.imageObservations)
|
|
148
|
+
image.capture = getScreenshotCapture(commandInfoWithTokens.commandTokens ?? []).kind;
|
|
149
|
+
}
|
|
150
|
+
const compactedPresentation = options.modelVisible === false ? presentationWithImage : await compactLargePresentationOutput({
|
|
148
151
|
artifactManifest,
|
|
149
152
|
commandInfo,
|
|
150
153
|
data: presentationData,
|
|
@@ -241,5 +244,7 @@ export async function buildToolPresentation(options) {
|
|
|
241
244
|
if (presentationWithManifest.pageChangeSummary?.observed === false && presentationCommandInfo.command !== "batch" && presentationWithManifest.content[0]?.type === "text") {
|
|
242
245
|
presentationWithManifest.content[0] = { ...presentationWithManifest.content[0], text: `${presentationWithManifest.content[0].text}\n\nAction dispatched; application change unverified. Verify the expected URL, text, state, or external receipt before relying on it.` };
|
|
243
246
|
}
|
|
247
|
+
if (options.modelVisible === false)
|
|
248
|
+
presentationWithManifest.content = [];
|
|
244
249
|
return sanitizeModelFacingPresentation(presentationWithManifest);
|
|
245
250
|
}
|
|
@@ -2,8 +2,10 @@ import { createHash, randomUUID } from "node:crypto";
|
|
|
2
2
|
import { basename } from "node:path";
|
|
3
3
|
import { extractUpstreamCommandTokens, findCommandStartIndex, parseArgvDescriptor, parseCommandInfo, } from "./argv-descriptor.js";
|
|
4
4
|
import { batchHasSuccessfulCloseAll, getSuccessfulBatchCloseLifecycle } from "./batch-lifecycle.js";
|
|
5
|
-
import {
|
|
5
|
+
import { getBrowserResultMessage } from "./browser-transcript.js";
|
|
6
|
+
import { canonicalizeAgentBrowserNamespace, extractExplicitNamespace, extractExplicitSessionName, getAgentBrowserSessionIdentityKey, getBooleanFlagValue, GLOBAL_BOOLEAN_FLAGS_WITH_OPTIONAL_VALUES, GLOBAL_VALUE_FLAGS_ALLOWING_DASH_VALUE, isAgentBrowserSessionIdentityKeyInNamespace, isUpstreamEnvFlagEnabled, PREVALIDATED_VALUE_FLAGS, projectUpstreamGlobalFlags, resolveAgentBrowserNamespace, scanUpstreamGlobalFlagOccurrences, stripUpstreamGlobalFlags, } from "./argv-grammar.js";
|
|
6
7
|
import { needsManagedSession } from "./command-policy.js";
|
|
8
|
+
import { parseBatchCommandArgument } from "./orchestration/batch-stdin.js";
|
|
7
9
|
import { isCloseAllCommand, isCloseCommand, isOpenNavigationCommand } from "./command-taxonomy.js";
|
|
8
10
|
import { hasLaunchScopedFlagToken, LAUNCH_SCOPED_FLAG_DEFINITIONS, LAUNCH_SCOPED_FLAG_LABEL, } from "./launch-scoped-flags.js";
|
|
9
11
|
import { MANAGED_SESSION_NAME_PREFIX, } from "./managed-session-restore.js";
|
|
@@ -356,6 +358,19 @@ export function redactInvocationArgs(args) {
|
|
|
356
358
|
redacted[index] = "[REDACTED]";
|
|
357
359
|
}
|
|
358
360
|
}
|
|
361
|
+
const batch = projectUpstreamGlobalFlags(args);
|
|
362
|
+
if (batch.tokens[0] === "batch") {
|
|
363
|
+
for (let index = 1; index < batch.tokens.length; index++) {
|
|
364
|
+
if (batch.tokens[index] === "--bail")
|
|
365
|
+
continue;
|
|
366
|
+
const step = parseBatchCommandArgument(batch.tokens[index]).step;
|
|
367
|
+
if (!step)
|
|
368
|
+
continue;
|
|
369
|
+
const safe = redactInvocationArgs(step);
|
|
370
|
+
if (safe.some((token, offset) => token !== step[offset]))
|
|
371
|
+
redacted[batch.indices[index]] = safe.map(token => `'${token.replaceAll("'", "'\\''")}'`).join(" ");
|
|
372
|
+
}
|
|
373
|
+
}
|
|
359
374
|
return redacted;
|
|
360
375
|
}
|
|
361
376
|
export function isPlainTextInspectionArgs(args) {
|
|
@@ -478,11 +493,8 @@ export function restoreManagedSessionStateFromBranch(branch, fallbackSessionName
|
|
|
478
493
|
closedSessionName = sessionName;
|
|
479
494
|
};
|
|
480
495
|
for (const entry of branch) {
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
}
|
|
484
|
-
const message = isRecord(entry.message) ? entry.message : undefined;
|
|
485
|
-
if (!message || message.toolName !== "agent_browser") {
|
|
496
|
+
const message = getBrowserResultMessage(entry);
|
|
497
|
+
if (!message) {
|
|
486
498
|
continue;
|
|
487
499
|
}
|
|
488
500
|
const details = isRecord(message.details) ? message.details : undefined;
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { extractUpstreamCommandTokens } from "./argv-descriptor.js";
|
|
2
2
|
import { getAgentBrowserSessionIdentityKey, isAgentBrowserSessionIdentityKeyInNamespace } from "./argv-grammar.js";
|
|
3
3
|
import { batchHasSuccessfulCloseAll, getSuccessfulBatchCloseLifecycle } from "./batch-lifecycle.js";
|
|
4
|
+
import { getBrowserResultMessage } from "./browser-transcript.js";
|
|
4
5
|
import { isCloseAllCommand, isCloseCommand, isReadOnlyDiagnosticSessionTargetCommand, isRecordPageTransitionCommand, isUnverifiedPageTransitionCommand, isWebMcpPageMutationCommand, isWindowOrDiffPageTransitionCommand } from "./command-taxonomy.js";
|
|
5
6
|
import { isRecord } from "./parsing.js";
|
|
6
7
|
import { findReadConfirmation as findPendingReadConfirmation, parseReadConfirmation } from "./read-confirmation.js";
|
|
@@ -351,10 +352,8 @@ export class SessionPageState {
|
|
|
351
352
|
const state = new SessionPageState();
|
|
352
353
|
let restoredOrder = 0;
|
|
353
354
|
for (const entry of branch) {
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
const message = isRecord(entry.message) ? entry.message : undefined;
|
|
357
|
-
if (!message || message.toolName !== "agent_browser")
|
|
355
|
+
const message = getBrowserResultMessage(entry);
|
|
356
|
+
if (!message)
|
|
358
357
|
continue;
|
|
359
358
|
const details = isRecord(message.details) ? message.details : undefined;
|
|
360
359
|
if (!details)
|
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
import { Text } from "@earendil-works/pi-tui";
|
|
2
|
+
import { formatAgentBrowserRenderCall } from "./pi-tool-rendering.js";
|
|
3
|
+
import { AGENT_BROWSER_PARAMS, AGENT_BROWSER_CODE_PARAMS, AGENT_BROWSER_ACTION_PARAMS, AGENT_BROWSER_QA_PARAMS, AGENT_BROWSER_ELECTRON_PARAMS, AGENT_BROWSER_SOURCE_PARAMS, AGENT_BROWSER_NETWORK_SOURCE_PARAMS, AGENT_BROWSER_TOOLS_PARAMS, } from "./input-modes/params.js";
|
|
4
|
+
import { ADVANCED_TOOL_PROMPT_GUIDELINES, buildToolPromptGuidelines } from "./playbook.js";
|
|
5
|
+
export const AGENT_BROWSER_TOOL_INVENTORY = {
|
|
6
|
+
action: { name: "agent_browser_action", description: "Click, check, fill, or select a unique accessible target using stable locators or current refs." },
|
|
7
|
+
qa: { name: "agent_browser_qa", description: "Check a URL or attached page with visible assertions and a diagnostic pass/fail verdict." },
|
|
8
|
+
electron: { name: "agent_browser_electron", description: "Discover, launch, probe, and clean up owned Electron desktop applications." },
|
|
9
|
+
source: { name: "agent_browser_source", description: "Find experimental DOM/React/local-source candidates with a bounded workspace scan." },
|
|
10
|
+
network: { name: "agent_browser_network_source", description: "Correlate failed requests with initiator and local-source candidates." },
|
|
11
|
+
};
|
|
12
|
+
const advancedNames = new Set(Object.values(AGENT_BROWSER_TOOL_INVENTORY).map(({ name }) => name));
|
|
13
|
+
/** Register once; advanced calls adapt only their input shape and reuse the ordinary executor. */
|
|
14
|
+
export function registerAgentBrowserToolSurface(pi, options) {
|
|
15
|
+
pi.registerTool({
|
|
16
|
+
name: "agent_browser",
|
|
17
|
+
label: "Agent Browser",
|
|
18
|
+
description: "Browse and interact through native agent-browser commands. One command in args; fixed sequences use batch --bail and JSON-array stdin. Use agent_browser_code for loops/branches and agent_browser_tools for advanced capabilities.",
|
|
19
|
+
promptSnippet: "Browse pages, inspect current refs, interact, and run native command batches.",
|
|
20
|
+
promptGuidelines: options.promptGuidelines ?? buildToolPromptGuidelines({ includeWebSearch: false }),
|
|
21
|
+
parameters: AGENT_BROWSER_PARAMS,
|
|
22
|
+
renderCall: options.renderCall,
|
|
23
|
+
renderResult: options.renderResult,
|
|
24
|
+
execute: options.execute,
|
|
25
|
+
executionMode: options.executionMode,
|
|
26
|
+
});
|
|
27
|
+
pi.registerTool({
|
|
28
|
+
name: "agent_browser_code",
|
|
29
|
+
label: "Agent Browser Code",
|
|
30
|
+
description: "Run fresh JavaScript against a persistent browser. await browser({args,stdin?,timeoutMs?}) returns success/data/error/nextActions and imageObservations; emit(selected JSON) and emitImage(image handle) explicitly choose output. No host APIs or imports. Use native batch for fixed sequences.",
|
|
31
|
+
promptSnippet: "Branch, loop, and aggregate browser observations with explicit JSON/image output.",
|
|
32
|
+
parameters: AGENT_BROWSER_CODE_PARAMS,
|
|
33
|
+
constrainedSampling: { type: "json_schema", strict: "prefer" },
|
|
34
|
+
execute: options.executeCode,
|
|
35
|
+
executionMode: options.executionMode,
|
|
36
|
+
renderCall(args, theme, context) {
|
|
37
|
+
const text = context.lastComponent instanceof Text ? context.lastComponent : new Text("", 0, 0);
|
|
38
|
+
text.setText(formatAgentBrowserRenderCall(args, theme, context.expanded));
|
|
39
|
+
return text;
|
|
40
|
+
},
|
|
41
|
+
renderResult: options.renderResult,
|
|
42
|
+
});
|
|
43
|
+
pi.registerTool({
|
|
44
|
+
...AGENT_BROWSER_TOOL_INVENTORY.action,
|
|
45
|
+
label: "Browser Action",
|
|
46
|
+
promptGuidelines: [...ADVANCED_TOOL_PROMPT_GUIDELINES.action],
|
|
47
|
+
parameters: AGENT_BROWSER_ACTION_PARAMS,
|
|
48
|
+
renderResult: options.renderResult,
|
|
49
|
+
executionMode: options.executionMode,
|
|
50
|
+
execute(id, { outputPath, timeoutMs, ...semanticAction }, signal, onUpdate, ctx) {
|
|
51
|
+
return options.execute(id, { semanticAction, outputPath, timeoutMs }, signal, onUpdate, ctx);
|
|
52
|
+
},
|
|
53
|
+
});
|
|
54
|
+
pi.registerTool({
|
|
55
|
+
...AGENT_BROWSER_TOOL_INVENTORY.qa,
|
|
56
|
+
label: "Browser QA",
|
|
57
|
+
promptGuidelines: [...ADVANCED_TOOL_PROMPT_GUIDELINES.qa],
|
|
58
|
+
parameters: AGENT_BROWSER_QA_PARAMS,
|
|
59
|
+
renderResult: options.renderResult,
|
|
60
|
+
executionMode: options.executionMode,
|
|
61
|
+
execute(id, { outputPath, timeoutMs, sessionMode, ...qa }, signal, onUpdate, ctx) {
|
|
62
|
+
return options.execute(id, { qa, outputPath, timeoutMs, sessionMode }, signal, onUpdate, ctx);
|
|
63
|
+
},
|
|
64
|
+
});
|
|
65
|
+
pi.registerTool({
|
|
66
|
+
...AGENT_BROWSER_TOOL_INVENTORY.electron,
|
|
67
|
+
label: "Browser Electron",
|
|
68
|
+
promptGuidelines: [...ADVANCED_TOOL_PROMPT_GUIDELINES.electron],
|
|
69
|
+
parameters: AGENT_BROWSER_ELECTRON_PARAMS,
|
|
70
|
+
renderResult: options.renderResult,
|
|
71
|
+
executionMode: options.executionMode,
|
|
72
|
+
execute(id, { outputPath, ...electron }, signal, onUpdate, ctx) {
|
|
73
|
+
return options.execute(id, { electron, outputPath }, signal, onUpdate, ctx);
|
|
74
|
+
},
|
|
75
|
+
});
|
|
76
|
+
pi.registerTool({
|
|
77
|
+
...AGENT_BROWSER_TOOL_INVENTORY.source,
|
|
78
|
+
label: "Browser Source",
|
|
79
|
+
promptGuidelines: [...ADVANCED_TOOL_PROMPT_GUIDELINES.source],
|
|
80
|
+
parameters: AGENT_BROWSER_SOURCE_PARAMS,
|
|
81
|
+
renderResult: options.renderResult,
|
|
82
|
+
executionMode: options.executionMode,
|
|
83
|
+
execute(id, { outputPath, timeoutMs, sessionMode, ...sourceLookup }, signal, onUpdate, ctx) {
|
|
84
|
+
return options.execute(id, { sourceLookup, outputPath, timeoutMs, sessionMode }, signal, onUpdate, ctx);
|
|
85
|
+
},
|
|
86
|
+
});
|
|
87
|
+
pi.registerTool({
|
|
88
|
+
...AGENT_BROWSER_TOOL_INVENTORY.network,
|
|
89
|
+
label: "Browser Network Source",
|
|
90
|
+
promptGuidelines: [...ADVANCED_TOOL_PROMPT_GUIDELINES.network],
|
|
91
|
+
parameters: AGENT_BROWSER_NETWORK_SOURCE_PARAMS,
|
|
92
|
+
renderResult: options.renderResult,
|
|
93
|
+
executionMode: options.executionMode,
|
|
94
|
+
execute(id, { outputPath, timeoutMs, sessionMode, ...networkSourceLookup }, signal, onUpdate, ctx) {
|
|
95
|
+
return options.execute(id, { networkSourceLookup, outputPath, timeoutMs, sessionMode }, signal, onUpdate, ctx);
|
|
96
|
+
},
|
|
97
|
+
});
|
|
98
|
+
pi.registerTool({
|
|
99
|
+
name: "agent_browser_tools",
|
|
100
|
+
label: "Browser Tools",
|
|
101
|
+
description: "List or enable advanced browser tools: action → agent_browser_action; qa → agent_browser_qa; electron → agent_browser_electron; source → agent_browser_source; network → agent_browser_network_source. Omit enable for inventory. Activation only adds tools; it preserves other active tools.",
|
|
102
|
+
promptSnippet: "Discover and enable specialized browser actions, QA, Electron, and source tools.",
|
|
103
|
+
parameters: AGENT_BROWSER_TOOLS_PARAMS,
|
|
104
|
+
async execute(_id, params) {
|
|
105
|
+
const available = new Set(pi.getAllTools().map(({ name }) => name));
|
|
106
|
+
const active = new Set(pi.getActiveTools());
|
|
107
|
+
const added = (params.enable ?? []).map((key) => AGENT_BROWSER_TOOL_INVENTORY[key].name)
|
|
108
|
+
.filter((name) => available.has(name) && !active.has(name));
|
|
109
|
+
if (added.length > 0)
|
|
110
|
+
pi.setActiveTools([...new Set([...active, ...added])]);
|
|
111
|
+
const current = new Set(pi.getActiveTools());
|
|
112
|
+
const inventory = Object.entries(AGENT_BROWSER_TOOL_INVENTORY).map(([key, tool]) => ({
|
|
113
|
+
key, ...tool, available: available.has(tool.name), active: current.has(tool.name),
|
|
114
|
+
}));
|
|
115
|
+
return {
|
|
116
|
+
content: [{ type: "text", text: inventory.map((tool) => `${tool.key}: ${tool.name} (${!tool.available ? "unavailable in this Pi tool selection" : tool.active ? "active" : "inactive"}) — ${tool.description}`).join("\n") }],
|
|
117
|
+
details: { inventory, added: [...new Set(added)] },
|
|
118
|
+
};
|
|
119
|
+
},
|
|
120
|
+
});
|
|
121
|
+
pi.on("session_start", async (_event, ctx) => {
|
|
122
|
+
// Pi owns selected tools and their durable system-message history; keep no parallel registry.
|
|
123
|
+
const argv = process.argv.slice(2);
|
|
124
|
+
const delimiter = argv.indexOf("--");
|
|
125
|
+
if ((delimiter < 0 ? argv : argv.slice(0, delimiter)).some((arg) => arg === "--tools" || arg === "-t"))
|
|
126
|
+
return;
|
|
127
|
+
// A host-filtered catalog is an explicit selection, not our default surface.
|
|
128
|
+
const available = new Set(pi.getAllTools().map(({ name }) => name));
|
|
129
|
+
if (!["agent_browser", "agent_browser_code", "agent_browser_tools", ...advancedNames].every(name => available.has(name)))
|
|
130
|
+
return;
|
|
131
|
+
// Keep Pi's replay helper behind the awaited restoration boundary, not factory registration.
|
|
132
|
+
const { getCurrentSystemMessage } = await import("@earendil-works/pi-ai");
|
|
133
|
+
const current = getCurrentSystemMessage(ctx.sessionManager.buildSessionProjection().messages);
|
|
134
|
+
const restored = new Set(current?.toolsAdded?.map(({ name }) => name));
|
|
135
|
+
const active = pi.getActiveTools().filter((name) => !advancedNames.has(name) || restored.has(name));
|
|
136
|
+
pi.setActiveTools([...new Set([...active, ...[...restored].filter((name) => advancedNames.has(name) && available.has(name))])]);
|
|
137
|
+
});
|
|
138
|
+
}
|
|
@@ -56,11 +56,14 @@ runInContext("const " + stateName + " = (() => {\n" +
|
|
|
56
56
|
" });\n" +
|
|
57
57
|
" };\n" +
|
|
58
58
|
" const emit = function emit(value) { sendValue({ type: 'emit', value }); };\n" +
|
|
59
|
+
" const emitImage = function emitImage(value) { sendValue({ type: 'image', value }); };\n" +
|
|
59
60
|
" Object.setPrototypeOf(browser, null);\n" +
|
|
60
61
|
" Object.setPrototypeOf(emit, null);\n" +
|
|
62
|
+
" Object.setPrototypeOf(emitImage, null);\n" +
|
|
61
63
|
" Object.freeze(browser);\n" +
|
|
62
64
|
" Object.freeze(emit);\n" +
|
|
63
|
-
" Object.
|
|
65
|
+
" Object.freeze(emitImage);\n" +
|
|
66
|
+
" Object.defineProperties(globalThis, { browser: { value: browser, writable: false, configurable: false }, emit: { value: emit, writable: false, configurable: false }, emitImage: { value: emitImage, writable: false, configurable: false } });\n" +
|
|
64
67
|
" const complete = (ok, value) => {\n" +
|
|
65
68
|
" if (ok) {\n" +
|
|
66
69
|
" try { sendValue(value === undefined ? { type: 'complete', hasValue: false } : { type: 'complete', hasValue: true, value }); }\n" +
|