pi-agent-browser-native 0.2.1 → 0.2.3
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 +20 -2
- package/README.md +9 -3
- package/docs/ARCHITECTURE.md +9 -5
- package/docs/RELEASE.md +11 -2
- package/docs/REQUIREMENTS.md +6 -2
- package/docs/TOOL_CONTRACT.md +22 -5
- package/extensions/agent-browser/index.ts +386 -88
- package/extensions/agent-browser/lib/process.ts +1 -1
- package/extensions/agent-browser/lib/results/presentation.ts +92 -6
- package/extensions/agent-browser/lib/results/snapshot.ts +15 -6
- package/extensions/agent-browser/lib/runtime.ts +400 -7
- package/extensions/agent-browser/lib/temp.ts +94 -12
- package/package.json +1 -1
|
@@ -10,6 +10,7 @@ import { readFile, stat } from "node:fs/promises";
|
|
|
10
10
|
import { resolve } from "node:path";
|
|
11
11
|
|
|
12
12
|
import { parseCommandInfo, type CommandInfo } from "../runtime.js";
|
|
13
|
+
import { type PersistentSessionArtifactStore } from "../temp.js";
|
|
13
14
|
import { buildSnapshotPresentation, formatRawSnapshotText, formatSnapshotSummary } from "./snapshot.js";
|
|
14
15
|
import {
|
|
15
16
|
type AgentBrowserBatchResult,
|
|
@@ -106,6 +107,63 @@ function getScreenshotSummary(data: Record<string, unknown>): string | undefined
|
|
|
106
107
|
return typeof data.path === "string" ? `Saved image: ${data.path}` : undefined;
|
|
107
108
|
}
|
|
108
109
|
|
|
110
|
+
function getScalarExtractionResult(data: Record<string, unknown>): string | undefined {
|
|
111
|
+
const { result } = data;
|
|
112
|
+
if (typeof result === "string") {
|
|
113
|
+
return result.trim().length > 0 ? result : undefined;
|
|
114
|
+
}
|
|
115
|
+
if (typeof result === "number" || typeof result === "boolean") {
|
|
116
|
+
return String(result);
|
|
117
|
+
}
|
|
118
|
+
return undefined;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
function getExtractionOrigin(data: Record<string, unknown>): string | undefined {
|
|
122
|
+
if (typeof data.origin === "string" && data.origin.trim().length > 0) {
|
|
123
|
+
return data.origin.trim();
|
|
124
|
+
}
|
|
125
|
+
if (typeof data.url === "string" && data.url.trim().length > 0) {
|
|
126
|
+
return data.url.trim();
|
|
127
|
+
}
|
|
128
|
+
return undefined;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
function formatGetSummaryLabel(subcommand: string | undefined): string {
|
|
132
|
+
if (!subcommand) {
|
|
133
|
+
return "Get result";
|
|
134
|
+
}
|
|
135
|
+
if (subcommand.toLowerCase() === "url") {
|
|
136
|
+
return "URL";
|
|
137
|
+
}
|
|
138
|
+
return `${subcommand.slice(0, 1).toUpperCase()}${subcommand.slice(1)}`;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
function formatExtractionSummary(commandInfo: CommandInfo, data: Record<string, unknown>): string | undefined {
|
|
142
|
+
const scalarResult = getScalarExtractionResult(data);
|
|
143
|
+
if (!scalarResult) {
|
|
144
|
+
return undefined;
|
|
145
|
+
}
|
|
146
|
+
if (commandInfo.command === "get") {
|
|
147
|
+
return `${formatGetSummaryLabel(commandInfo.subcommand)}: ${scalarResult.split("\n", 1)[0] ?? scalarResult}`;
|
|
148
|
+
}
|
|
149
|
+
if (commandInfo.command === "eval") {
|
|
150
|
+
return `Eval result: ${scalarResult.split("\n", 1)[0] ?? scalarResult}`;
|
|
151
|
+
}
|
|
152
|
+
return undefined;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
function formatExtractionText(commandInfo: CommandInfo, data: Record<string, unknown>): string | undefined {
|
|
156
|
+
if (commandInfo.command !== "get" && commandInfo.command !== "eval") {
|
|
157
|
+
return undefined;
|
|
158
|
+
}
|
|
159
|
+
const scalarResult = getScalarExtractionResult(data);
|
|
160
|
+
if (!scalarResult) {
|
|
161
|
+
return undefined;
|
|
162
|
+
}
|
|
163
|
+
const origin = getExtractionOrigin(data);
|
|
164
|
+
return origin && origin !== scalarResult ? `${scalarResult}\n\nOrigin: ${origin}` : scalarResult;
|
|
165
|
+
}
|
|
166
|
+
|
|
109
167
|
function isNavigationObservableCommand(command: string | undefined): boolean {
|
|
110
168
|
return command !== undefined && NAVIGATION_SUMMARY_COMMANDS.has(command);
|
|
111
169
|
}
|
|
@@ -207,8 +265,9 @@ async function buildBatchStepPresentation(options: {
|
|
|
207
265
|
cwd: string;
|
|
208
266
|
index: number;
|
|
209
267
|
item: AgentBrowserBatchResult;
|
|
268
|
+
persistentArtifactStore?: PersistentSessionArtifactStore;
|
|
210
269
|
}): Promise<{ details: BatchStepPresentationDetails; presentation: ToolPresentation }> {
|
|
211
|
-
const { cwd, index, item } = options;
|
|
270
|
+
const { cwd, index, item, persistentArtifactStore } = options;
|
|
212
271
|
const command = isStringArray(item.command) ? item.command : undefined;
|
|
213
272
|
const commandText = formatBatchStepCommand(command, index);
|
|
214
273
|
|
|
@@ -236,6 +295,7 @@ async function buildBatchStepPresentation(options: {
|
|
|
236
295
|
commandInfo: parseCommandInfo(command ?? []),
|
|
237
296
|
cwd,
|
|
238
297
|
envelope: { data: item.result, success: true },
|
|
298
|
+
persistentArtifactStore,
|
|
239
299
|
});
|
|
240
300
|
const fullOutputPaths = getPresentationPaths({
|
|
241
301
|
primaryPath: presentation.fullOutputPath,
|
|
@@ -268,12 +328,28 @@ async function buildBatchStepPresentation(options: {
|
|
|
268
328
|
async function buildBatchPresentation(options: {
|
|
269
329
|
cwd: string;
|
|
270
330
|
data: AgentBrowserBatchResult[];
|
|
331
|
+
persistentArtifactStore?: PersistentSessionArtifactStore;
|
|
271
332
|
summary: string;
|
|
272
333
|
}): Promise<ToolPresentation> {
|
|
273
|
-
const { cwd, data, summary } = options;
|
|
334
|
+
const { cwd, data, persistentArtifactStore, summary } = options;
|
|
274
335
|
const steps: Array<{ details: BatchStepPresentationDetails; presentation: ToolPresentation }> = [];
|
|
336
|
+
const protectedPersistentPaths: string[] = [];
|
|
275
337
|
for (const [index, item] of data.entries()) {
|
|
276
|
-
|
|
338
|
+
const step = await buildBatchStepPresentation({
|
|
339
|
+
cwd,
|
|
340
|
+
index,
|
|
341
|
+
item,
|
|
342
|
+
persistentArtifactStore: persistentArtifactStore
|
|
343
|
+
? { ...persistentArtifactStore, protectedPaths: protectedPersistentPaths }
|
|
344
|
+
: undefined,
|
|
345
|
+
});
|
|
346
|
+
steps.push(step);
|
|
347
|
+
protectedPersistentPaths.push(
|
|
348
|
+
...getPresentationPaths({
|
|
349
|
+
primaryPath: step.presentation.fullOutputPath,
|
|
350
|
+
secondaryPaths: step.presentation.fullOutputPaths,
|
|
351
|
+
}),
|
|
352
|
+
);
|
|
277
353
|
}
|
|
278
354
|
|
|
279
355
|
const batchFailure = getBatchFailureDetails(steps);
|
|
@@ -354,6 +430,10 @@ function formatSummary(commandInfo: CommandInfo, data: unknown): string {
|
|
|
354
430
|
if (commandInfo.command === "screenshot" && typeof data.path === "string") {
|
|
355
431
|
return `Screenshot saved: ${data.path}`;
|
|
356
432
|
}
|
|
433
|
+
const extractionSummary = formatExtractionSummary(commandInfo, data);
|
|
434
|
+
if (extractionSummary) {
|
|
435
|
+
return extractionSummary;
|
|
436
|
+
}
|
|
357
437
|
const pageSummary = getPageSummary(data);
|
|
358
438
|
if (pageSummary) {
|
|
359
439
|
return pageSummary.split("\n", 1)[0] ?? "agent-browser result";
|
|
@@ -404,6 +484,11 @@ function formatContentText(commandInfo: CommandInfo, data: unknown): string {
|
|
|
404
484
|
if (screenshotSummary) return screenshotSummary;
|
|
405
485
|
}
|
|
406
486
|
|
|
487
|
+
const extractionText = formatExtractionText(commandInfo, data);
|
|
488
|
+
if (extractionText) {
|
|
489
|
+
return extractionText;
|
|
490
|
+
}
|
|
491
|
+
|
|
407
492
|
const pageSummary = getPageSummary(data);
|
|
408
493
|
if (pageSummary) {
|
|
409
494
|
return pageSummary;
|
|
@@ -459,8 +544,9 @@ export async function buildToolPresentation(options: {
|
|
|
459
544
|
cwd: string;
|
|
460
545
|
envelope?: AgentBrowserEnvelope;
|
|
461
546
|
errorText?: string;
|
|
547
|
+
persistentArtifactStore?: PersistentSessionArtifactStore;
|
|
462
548
|
}): Promise<ToolPresentation> {
|
|
463
|
-
const { commandInfo, cwd, envelope, errorText } = options;
|
|
549
|
+
const { commandInfo, cwd, envelope, errorText, persistentArtifactStore } = options;
|
|
464
550
|
if (errorText) {
|
|
465
551
|
return {
|
|
466
552
|
content: [{ type: "text", text: errorText }],
|
|
@@ -472,9 +558,9 @@ export async function buildToolPresentation(options: {
|
|
|
472
558
|
const summary = formatSummary(commandInfo, data);
|
|
473
559
|
const presentation =
|
|
474
560
|
commandInfo.command === "batch" && Array.isArray(data)
|
|
475
|
-
? await buildBatchPresentation({ cwd, data: data as AgentBrowserBatchResult[], summary })
|
|
561
|
+
? await buildBatchPresentation({ cwd, data: data as AgentBrowserBatchResult[], persistentArtifactStore, summary })
|
|
476
562
|
: commandInfo.command === "snapshot" && isRecord(data)
|
|
477
|
-
? await buildSnapshotPresentation(data)
|
|
563
|
+
? await buildSnapshotPresentation(data, persistentArtifactStore)
|
|
478
564
|
: {
|
|
479
565
|
content: [{ type: "text" as const, text: formatContentText(commandInfo, data) }],
|
|
480
566
|
data,
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* Invariants/Assumptions: Snapshot compaction should stay helpful even if upstream snapshot text formatting shifts, so structured parsing is best-effort and always has a resilient raw-outline fallback.
|
|
7
7
|
*/
|
|
8
8
|
|
|
9
|
-
import { writeSecureTempFile } from "../temp.js";
|
|
9
|
+
import { type PersistentSessionArtifactStore, writePersistentSessionArtifactFile, writeSecureTempFile } from "../temp.js";
|
|
10
10
|
import { type ToolPresentation, compareRefIds, countLines, isRecord, normalizeWhitespace, truncateText } from "./shared.js";
|
|
11
11
|
|
|
12
12
|
const SNAPSHOT_INLINE_MAX_CHARS = 6_000;
|
|
@@ -463,12 +463,18 @@ function canUseStructuredSnapshotPreview(snapshotLines: SnapshotLine[], refEntri
|
|
|
463
463
|
);
|
|
464
464
|
}
|
|
465
465
|
|
|
466
|
-
async function writeSnapshotSpillFile(
|
|
467
|
-
|
|
466
|
+
async function writeSnapshotSpillFile(
|
|
467
|
+
data: Record<string, unknown>,
|
|
468
|
+
persistentArtifactStore: PersistentSessionArtifactStore | undefined,
|
|
469
|
+
): Promise<string> {
|
|
470
|
+
const options = {
|
|
468
471
|
content: JSON.stringify(data, null, 2),
|
|
469
472
|
prefix: SNAPSHOT_SPILL_FILE_PREFIX,
|
|
470
473
|
suffix: ".json",
|
|
471
|
-
}
|
|
474
|
+
};
|
|
475
|
+
return persistentArtifactStore
|
|
476
|
+
? await writePersistentSessionArtifactFile({ ...options, store: persistentArtifactStore })
|
|
477
|
+
: await writeSecureTempFile(options);
|
|
472
478
|
}
|
|
473
479
|
|
|
474
480
|
export function formatSnapshotSummary(data: Record<string, unknown>): string {
|
|
@@ -487,7 +493,10 @@ export function formatRawSnapshotText(data: Record<string, unknown>): string {
|
|
|
487
493
|
return `Origin: ${origin}\nRefs: ${refs}\n\n${snapshot}`;
|
|
488
494
|
}
|
|
489
495
|
|
|
490
|
-
export async function buildSnapshotPresentation(
|
|
496
|
+
export async function buildSnapshotPresentation(
|
|
497
|
+
data: Record<string, unknown>,
|
|
498
|
+
persistentArtifactStore: PersistentSessionArtifactStore | undefined = undefined,
|
|
499
|
+
): Promise<ToolPresentation> {
|
|
491
500
|
const summary = formatSnapshotSummary(data);
|
|
492
501
|
const rawText = formatRawSnapshotText(data);
|
|
493
502
|
if (!shouldCompactSnapshot(rawText, data)) {
|
|
@@ -501,7 +510,7 @@ export async function buildSnapshotPresentation(data: Record<string, unknown>):
|
|
|
501
510
|
let fullOutputPath: string | undefined;
|
|
502
511
|
let spillErrorText: string | undefined;
|
|
503
512
|
try {
|
|
504
|
-
fullOutputPath = await writeSnapshotSpillFile(data);
|
|
513
|
+
fullOutputPath = await writeSnapshotSpillFile(data, persistentArtifactStore);
|
|
505
514
|
} catch (error) {
|
|
506
515
|
spillErrorText = error instanceof Error ? error.message : String(error);
|
|
507
516
|
}
|