pi-agent-browser-native 0.2.48 → 0.2.49
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 +17 -0
- package/README.md +16 -6
- package/dist/extensions/agent-browser/index.js +785 -0
- package/dist/extensions/agent-browser/lib/argv-descriptor.js +71 -0
- package/dist/extensions/agent-browser/lib/argv-grammar.js +121 -0
- package/dist/extensions/agent-browser/lib/bash-guard.js +190 -0
- package/dist/extensions/agent-browser/lib/command-policy.js +85 -0
- package/dist/extensions/agent-browser/lib/command-taxonomy.js +302 -0
- package/dist/extensions/agent-browser/lib/config-policy.js +686 -0
- package/dist/extensions/agent-browser/lib/config.js +122 -0
- package/dist/extensions/agent-browser/lib/electron/cdp.js +51 -0
- package/dist/extensions/agent-browser/lib/electron/cleanup.js +212 -0
- package/dist/extensions/agent-browser/lib/electron/discovery.js +633 -0
- package/dist/extensions/agent-browser/lib/electron/launch.js +351 -0
- package/{extensions/agent-browser/lib/electron/text.ts → dist/extensions/agent-browser/lib/electron/text.js} +5 -5
- package/dist/extensions/agent-browser/lib/executable-path.js +20 -0
- package/dist/extensions/agent-browser/lib/fs-utils.js +18 -0
- package/dist/extensions/agent-browser/lib/input-modes/electron.js +165 -0
- package/dist/extensions/agent-browser/lib/input-modes/job.js +519 -0
- package/dist/extensions/agent-browser/lib/input-modes/lookups.js +440 -0
- package/dist/extensions/agent-browser/lib/input-modes/params.js +164 -0
- package/dist/extensions/agent-browser/lib/input-modes/semantic-action.js +119 -0
- package/dist/extensions/agent-browser/lib/input-modes/shared.js +42 -0
- package/dist/extensions/agent-browser/lib/input-modes/types.js +21 -0
- package/dist/extensions/agent-browser/lib/input-modes.js +10 -0
- package/dist/extensions/agent-browser/lib/json-schema.js +58 -0
- package/dist/extensions/agent-browser/lib/launch-scoped-flags.js +59 -0
- package/dist/extensions/agent-browser/lib/navigation-policy.js +83 -0
- package/dist/extensions/agent-browser/lib/orchestration/batch-stdin.js +62 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/artifact-paths.js +39 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/click-dispatch.js +276 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.js +909 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result.js +443 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/index.js +47 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/direct-anchor-download.js +141 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/network-page-filter.js +108 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/scroll-shims.js +112 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/snapshot-filter.js +158 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/wait-timeouts.js +54 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare.js +762 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output.js +491 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prompt-guards.js +40 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-artifacts.js +5 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-state.js +731 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/types.js +1 -0
- package/dist/extensions/agent-browser/lib/orchestration/electron-host/index.js +718 -0
- package/dist/extensions/agent-browser/lib/orchestration/input-plan.js +247 -0
- package/dist/extensions/agent-browser/lib/orchestration/output-file.js +68 -0
- package/{extensions/agent-browser/lib/parsing.ts → dist/extensions/agent-browser/lib/parsing.js} +12 -11
- package/dist/extensions/agent-browser/lib/pi-tool-rendering.js +241 -0
- package/dist/extensions/agent-browser/lib/playbook.js +121 -0
- package/dist/extensions/agent-browser/lib/process.js +448 -0
- package/dist/extensions/agent-browser/lib/prompt-policy.js +91 -0
- package/dist/extensions/agent-browser/lib/results/action-recommendations.js +220 -0
- package/dist/extensions/agent-browser/lib/results/artifact-manifest.js +111 -0
- package/{extensions/agent-browser/lib/results/artifact-state.ts → dist/extensions/agent-browser/lib/results/artifact-state.js} +4 -8
- package/dist/extensions/agent-browser/lib/results/categories.js +76 -0
- package/dist/extensions/agent-browser/lib/results/confirmation.js +63 -0
- package/dist/extensions/agent-browser/lib/results/contracts.js +8 -0
- package/dist/extensions/agent-browser/lib/results/editable-ref-evidence.js +74 -0
- package/dist/extensions/agent-browser/lib/results/envelope.js +166 -0
- package/dist/extensions/agent-browser/lib/results/network-routes.js +92 -0
- package/dist/extensions/agent-browser/lib/results/network.js +73 -0
- package/dist/extensions/agent-browser/lib/results/next-actions.js +72 -0
- package/dist/extensions/agent-browser/lib/results/presentation/artifacts.js +515 -0
- package/dist/extensions/agent-browser/lib/results/presentation/batch.js +397 -0
- package/dist/extensions/agent-browser/lib/results/presentation/browser-profile-recovery.js +55 -0
- package/dist/extensions/agent-browser/lib/results/presentation/common.js +46 -0
- package/dist/extensions/agent-browser/lib/results/presentation/content.js +24 -0
- package/dist/extensions/agent-browser/lib/results/presentation/diagnostics.js +960 -0
- package/dist/extensions/agent-browser/lib/results/presentation/errors.js +205 -0
- package/dist/extensions/agent-browser/lib/results/presentation/large-output.js +134 -0
- package/dist/extensions/agent-browser/lib/results/presentation/navigation.js +159 -0
- package/dist/extensions/agent-browser/lib/results/presentation/registry.js +216 -0
- package/dist/extensions/agent-browser/lib/results/presentation/semantic-action.js +104 -0
- package/dist/extensions/agent-browser/lib/results/presentation/skills.js +152 -0
- package/dist/extensions/agent-browser/lib/results/presentation.js +177 -0
- package/dist/extensions/agent-browser/lib/results/recovery-actions.js +107 -0
- package/dist/extensions/agent-browser/lib/results/recovery-next-actions.js +50 -0
- package/dist/extensions/agent-browser/lib/results/selector-recovery.js +225 -0
- package/{extensions/agent-browser/lib/results/shared.ts → dist/extensions/agent-browser/lib/results/shared.js} +0 -1
- package/dist/extensions/agent-browser/lib/results/snapshot-high-value-controls.js +208 -0
- package/dist/extensions/agent-browser/lib/results/snapshot-refs.js +78 -0
- package/dist/extensions/agent-browser/lib/results/snapshot-segments.js +331 -0
- package/dist/extensions/agent-browser/lib/results/snapshot-spill.js +40 -0
- package/dist/extensions/agent-browser/lib/results/snapshot.js +264 -0
- package/dist/extensions/agent-browser/lib/results/text.js +40 -0
- package/{extensions/agent-browser/lib/results.ts → dist/extensions/agent-browser/lib/results.js} +2 -32
- package/dist/extensions/agent-browser/lib/runtime.js +816 -0
- package/dist/extensions/agent-browser/lib/session-page-state.js +411 -0
- package/dist/extensions/agent-browser/lib/string-enum-schema.js +13 -0
- package/dist/extensions/agent-browser/lib/temp.js +498 -0
- package/dist/extensions/agent-browser/lib/web-search.js +562 -0
- package/docs/RELEASE.md +22 -11
- package/docs/SUPPORT_MATRIX.md +4 -3
- package/package.json +9 -5
- package/scripts/config.mjs +8 -2
- package/scripts/doctor.mjs +8 -7
- package/extensions/agent-browser/index.ts +0 -961
- package/extensions/agent-browser/lib/argv-descriptor.ts +0 -90
- package/extensions/agent-browser/lib/argv-grammar.ts +0 -128
- package/extensions/agent-browser/lib/bash-guard.ts +0 -205
- package/extensions/agent-browser/lib/command-policy.ts +0 -71
- package/extensions/agent-browser/lib/command-taxonomy.ts +0 -336
- package/extensions/agent-browser/lib/config-policy.js +0 -690
- package/extensions/agent-browser/lib/config.ts +0 -211
- package/extensions/agent-browser/lib/electron/cdp.ts +0 -69
- package/extensions/agent-browser/lib/electron/cleanup.ts +0 -235
- package/extensions/agent-browser/lib/electron/discovery.ts +0 -710
- package/extensions/agent-browser/lib/electron/launch.ts +0 -499
- package/extensions/agent-browser/lib/executable-path.ts +0 -19
- package/extensions/agent-browser/lib/fs-utils.ts +0 -18
- package/extensions/agent-browser/lib/input-modes/electron.ts +0 -170
- package/extensions/agent-browser/lib/input-modes/job.ts +0 -527
- package/extensions/agent-browser/lib/input-modes/lookups.ts +0 -447
- package/extensions/agent-browser/lib/input-modes/params.ts +0 -205
- package/extensions/agent-browser/lib/input-modes/semantic-action.ts +0 -127
- package/extensions/agent-browser/lib/input-modes/shared.ts +0 -46
- package/extensions/agent-browser/lib/input-modes/types.ts +0 -225
- package/extensions/agent-browser/lib/input-modes.ts +0 -45
- package/extensions/agent-browser/lib/json-schema.ts +0 -73
- package/extensions/agent-browser/lib/launch-scoped-flags.ts +0 -67
- package/extensions/agent-browser/lib/navigation-policy.ts +0 -95
- package/extensions/agent-browser/lib/orchestration/batch-stdin.ts +0 -65
- package/extensions/agent-browser/lib/orchestration/browser-run/artifact-paths.ts +0 -44
- package/extensions/agent-browser/lib/orchestration/browser-run/click-dispatch.ts +0 -280
- package/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.ts +0 -914
- package/extensions/agent-browser/lib/orchestration/browser-run/final-result.ts +0 -521
- package/extensions/agent-browser/lib/orchestration/browser-run/index.ts +0 -53
- package/extensions/agent-browser/lib/orchestration/browser-run/prepare/direct-anchor-download.ts +0 -158
- package/extensions/agent-browser/lib/orchestration/browser-run/prepare/network-page-filter.ts +0 -116
- package/extensions/agent-browser/lib/orchestration/browser-run/prepare/scroll-shims.ts +0 -147
- package/extensions/agent-browser/lib/orchestration/browser-run/prepare/snapshot-filter.ts +0 -183
- package/extensions/agent-browser/lib/orchestration/browser-run/prepare/wait-timeouts.ts +0 -58
- package/extensions/agent-browser/lib/orchestration/browser-run/prepare.ts +0 -847
- package/extensions/agent-browser/lib/orchestration/browser-run/process-output.ts +0 -559
- package/extensions/agent-browser/lib/orchestration/browser-run/prompt-guards.ts +0 -47
- package/extensions/agent-browser/lib/orchestration/browser-run/session-artifacts.ts +0 -8
- package/extensions/agent-browser/lib/orchestration/browser-run/session-state.ts +0 -868
- package/extensions/agent-browser/lib/orchestration/browser-run/types.ts +0 -565
- package/extensions/agent-browser/lib/orchestration/electron-host/index.ts +0 -855
- package/extensions/agent-browser/lib/orchestration/input-plan.ts +0 -375
- package/extensions/agent-browser/lib/orchestration/output-file.ts +0 -86
- package/extensions/agent-browser/lib/pi-tool-rendering.ts +0 -267
- package/extensions/agent-browser/lib/playbook.ts +0 -142
- package/extensions/agent-browser/lib/process.ts +0 -516
- package/extensions/agent-browser/lib/prompt-policy.ts +0 -105
- package/extensions/agent-browser/lib/results/action-recommendations.ts +0 -264
- package/extensions/agent-browser/lib/results/artifact-manifest.ts +0 -111
- package/extensions/agent-browser/lib/results/categories.ts +0 -106
- package/extensions/agent-browser/lib/results/confirmation.ts +0 -76
- package/extensions/agent-browser/lib/results/contracts.ts +0 -241
- package/extensions/agent-browser/lib/results/editable-ref-evidence.ts +0 -72
- package/extensions/agent-browser/lib/results/envelope.ts +0 -195
- package/extensions/agent-browser/lib/results/network-routes.ts +0 -83
- package/extensions/agent-browser/lib/results/network.ts +0 -78
- package/extensions/agent-browser/lib/results/next-actions.ts +0 -117
- package/extensions/agent-browser/lib/results/presentation/artifacts.ts +0 -588
- package/extensions/agent-browser/lib/results/presentation/batch.ts +0 -450
- package/extensions/agent-browser/lib/results/presentation/browser-profile-recovery.ts +0 -67
- package/extensions/agent-browser/lib/results/presentation/common.ts +0 -53
- package/extensions/agent-browser/lib/results/presentation/content.ts +0 -36
- package/extensions/agent-browser/lib/results/presentation/diagnostics.ts +0 -923
- package/extensions/agent-browser/lib/results/presentation/errors.ts +0 -227
- package/extensions/agent-browser/lib/results/presentation/large-output.ts +0 -182
- package/extensions/agent-browser/lib/results/presentation/navigation.ts +0 -184
- package/extensions/agent-browser/lib/results/presentation/registry.ts +0 -242
- package/extensions/agent-browser/lib/results/presentation/semantic-action.ts +0 -131
- package/extensions/agent-browser/lib/results/presentation/skills.ts +0 -143
- package/extensions/agent-browser/lib/results/presentation.ts +0 -257
- package/extensions/agent-browser/lib/results/recovery-actions.ts +0 -139
- package/extensions/agent-browser/lib/results/recovery-next-actions.ts +0 -71
- package/extensions/agent-browser/lib/results/selector-recovery.ts +0 -320
- package/extensions/agent-browser/lib/results/snapshot-high-value-controls.ts +0 -273
- package/extensions/agent-browser/lib/results/snapshot-refs.ts +0 -100
- package/extensions/agent-browser/lib/results/snapshot-segments.ts +0 -366
- package/extensions/agent-browser/lib/results/snapshot-spill.ts +0 -63
- package/extensions/agent-browser/lib/results/snapshot.ts +0 -329
- package/extensions/agent-browser/lib/results/text.ts +0 -40
- package/extensions/agent-browser/lib/runtime.ts +0 -988
- package/extensions/agent-browser/lib/session-page-state.ts +0 -512
- package/extensions/agent-browser/lib/string-enum-schema.ts +0 -20
- package/extensions/agent-browser/lib/temp.ts +0 -577
- package/extensions/agent-browser/lib/web-search.ts +0 -728
- /package/{extensions/agent-browser/lib/orchestration/browser-run.ts → dist/extensions/agent-browser/lib/orchestration/browser-run.js} +0 -0
|
@@ -1,227 +0,0 @@
|
|
|
1
|
-
import { isOpenNavigationCommand } from "../../command-taxonomy.js";
|
|
2
|
-
import { redactSensitiveText, type CommandInfo } from "../../runtime.js";
|
|
3
|
-
import { buildBrowserProfileConfigRecovery } from "./browser-profile-recovery.js";
|
|
4
|
-
import { redactModelFacingText } from "./common.js";
|
|
5
|
-
import { buildAgentBrowserNextActions } from "../action-recommendations.js";
|
|
6
|
-
import { buildAgentBrowserResultCategoryDetails } from "../categories.js";
|
|
7
|
-
import type { AgentBrowserNextAction, ToolPresentation } from "../contracts.js";
|
|
8
|
-
import { withOptionalSessionArgs } from "../next-actions.js";
|
|
9
|
-
|
|
10
|
-
const STALE_REF_ERROR_HINT = [
|
|
11
|
-
"Agent-browser hint: This ref may be stale after navigation, scrolling, or re-rendering.",
|
|
12
|
-
"Run `snapshot -i` again and retry with a current `@e…` ref; for less ref churn, use `find role|text|label|placeholder|alt|title|testid ...` or `scrollintoview` before interacting with off-screen elements.",
|
|
13
|
-
].join(" ");
|
|
14
|
-
|
|
15
|
-
const SELECTOR_DIALECT_ERROR_HINT = [
|
|
16
|
-
"Agent-browser hint: This selector may use an unsupported selector dialect.",
|
|
17
|
-
"Prefer refs from `snapshot -i`, or use supported `find role|text|label|placeholder|alt|title|testid ...` locators; use `scrollintoview` before interacting with off-screen elements.",
|
|
18
|
-
].join(" ");
|
|
19
|
-
|
|
20
|
-
const CLIPBOARD_PERMISSION_ERROR_HINT = [
|
|
21
|
-
"Agent-browser clipboard hint: Clipboard read/write access is environment-dependent and often fails in headless, managed, remote-profile, or file:// sessions.",
|
|
22
|
-
"If you see `NotAllowedError` or `permission denied`, treat it as a browser/OS permission limitation rather than proof that page state changed.",
|
|
23
|
-
"When possible, prefer page-native reads (`snapshot -i`, `get text`, `eval --stdin`) or direct input (`keyboard inserttext` / `keyboard type`) instead of relying on OS clipboard access.",
|
|
24
|
-
"If true clipboard access is required, retry in a browser/profile/session with explicit clipboard permission on a normal http(s) page.",
|
|
25
|
-
].join(" ");
|
|
26
|
-
|
|
27
|
-
const KEYBOARD_PRESS_ERROR_HINT = [
|
|
28
|
-
"Agent-browser keyboard hint: upstream keyboard commands are `keyboard type <text>` and `keyboard inserttext <text>`; `keyboard press` is not a supported subcommand in the targeted upstream version.",
|
|
29
|
-
'For Enter in text fields, use `keyboard type "\\n"` after focusing the intended control, then verify with a fresh snapshot, URL, or page-state check.',
|
|
30
|
-
].join(" ");
|
|
31
|
-
|
|
32
|
-
function isRecord(value: unknown): value is Record<string, unknown> {
|
|
33
|
-
return typeof value === "object" && value !== null;
|
|
34
|
-
}
|
|
35
|
-
|
|
36
|
-
function getSelectorRecoveryHint(errorText: string): string | undefined {
|
|
37
|
-
const normalized = errorText.trim();
|
|
38
|
-
if (normalized.length === 0) return undefined;
|
|
39
|
-
|
|
40
|
-
if (/\bUnknown ref\b|\bstale ref\b|\bref\b.*\b(?:not found|missing|expired)\b/i.test(normalized)) {
|
|
41
|
-
return STALE_REF_ERROR_HINT;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
const mentionsPlaywrightSelectorDialect = /(?:\btext=|:has-text\(|\bgetByRole\b|\bgetByText\b)/i.test(normalized);
|
|
45
|
-
const reportsSelectorMatchFailure =
|
|
46
|
-
/\b(?:no elements? found|failed to find|could not find|unable to find)\b.*\b(?:selector|locator)\b/i.test(normalized) ||
|
|
47
|
-
/\b(?:selector|locator)\b.*\b(?:no elements? found|not found|missing|failed to find|could not find|unable to find)\b/i.test(normalized);
|
|
48
|
-
|
|
49
|
-
if (
|
|
50
|
-
/\b(?:unsupported|unknown|invalid)\s+(?:selector|locator)\b/i.test(normalized) ||
|
|
51
|
-
/\bfailed to parse selector\b/i.test(normalized) ||
|
|
52
|
-
/\bselector\b.*\b(?:parse|syntax|unsupported|invalid)\b/i.test(normalized) ||
|
|
53
|
-
(mentionsPlaywrightSelectorDialect && reportsSelectorMatchFailure)
|
|
54
|
-
) {
|
|
55
|
-
return SELECTOR_DIALECT_ERROR_HINT;
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
return undefined;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function getClipboardPermissionHint(commandInfo: CommandInfo, errorText: string): string | undefined {
|
|
62
|
-
if (commandInfo.command !== "clipboard") return undefined;
|
|
63
|
-
if (!/\bNotAllowedError\b|\bclipboard\b.*\bpermission denied\b|\bpermission denied\b.*\bclipboard\b/i.test(errorText)) {
|
|
64
|
-
return undefined;
|
|
65
|
-
}
|
|
66
|
-
return CLIPBOARD_PERMISSION_ERROR_HINT;
|
|
67
|
-
}
|
|
68
|
-
|
|
69
|
-
function getKeyboardPressHint(commandInfo: CommandInfo, errorText: string): string | undefined {
|
|
70
|
-
if (commandInfo.command !== "keyboard" || commandInfo.subcommand !== "press") return undefined;
|
|
71
|
-
if (!/\bunknown\s+subcommand\b|\bvalid options?\b/i.test(errorText)) return undefined;
|
|
72
|
-
return KEYBOARD_PRESS_ERROR_HINT;
|
|
73
|
-
}
|
|
74
|
-
|
|
75
|
-
export function redactClipboardPermissionEcho(commandInfo: CommandInfo, errorText: string): string {
|
|
76
|
-
if (commandInfo.command !== "clipboard") return errorText;
|
|
77
|
-
return errorText
|
|
78
|
-
.replace(/(\b(?:read|write)\s+permission denied\b(?:\s+for)?\s+)([\s\S]+)$/gi, "$1[REDACTED]")
|
|
79
|
-
.replace(/(\bFailed to execute '[^']+' on 'Clipboard':\s*)([\s\S]+)$/gi, (match, prefix: string, suffix: string) => {
|
|
80
|
-
if (!/\bpermission denied\b/i.test(suffix)) return match;
|
|
81
|
-
return `${prefix}${suffix.replace(/(\bpermission denied\b(?:\s+for)?\s+)([\s\S]+)$/i, "$1[REDACTED]")}`;
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
export function getClipboardWritePayloadCandidates(commandTokens: readonly string[]): string[] {
|
|
86
|
-
if (commandTokens[0] !== "clipboard" || commandTokens[1] !== "write") return [];
|
|
87
|
-
const payloadTokens = commandTokens.slice(2).filter((value) => value.length > 0);
|
|
88
|
-
return [...new Set([...payloadTokens, payloadTokens.join(" ")].filter((value) => value.length > 0))];
|
|
89
|
-
}
|
|
90
|
-
|
|
91
|
-
function shouldRedactClipboardPayloadField(key: string, value: string, payloadCandidates: readonly string[]): boolean {
|
|
92
|
-
return payloadCandidates.some((candidate) => {
|
|
93
|
-
if (value === candidate) return true;
|
|
94
|
-
if (candidate.length < 8 || !/payload|clipboard|argument/i.test(key)) return false;
|
|
95
|
-
return value.includes(candidate);
|
|
96
|
-
});
|
|
97
|
-
}
|
|
98
|
-
|
|
99
|
-
export function redactClipboardPermissionErrorValue(commandInfo: CommandInfo, value: unknown, payloadCandidates: readonly string[] = []): unknown {
|
|
100
|
-
if (commandInfo.command !== "clipboard") return value;
|
|
101
|
-
if (typeof value === "string") return payloadCandidates.includes(value) ? "[REDACTED]" : redactClipboardPermissionEcho(commandInfo, value);
|
|
102
|
-
if (Array.isArray(value)) return value.map((item) => redactClipboardPermissionErrorValue(commandInfo, item, payloadCandidates));
|
|
103
|
-
if (!isRecord(value)) return value;
|
|
104
|
-
return Object.fromEntries(Object.entries(value).map(([key, entryValue]) => [
|
|
105
|
-
key,
|
|
106
|
-
typeof entryValue === "string" && shouldRedactClipboardPayloadField(key, entryValue, payloadCandidates)
|
|
107
|
-
? "[REDACTED]"
|
|
108
|
-
: redactClipboardPermissionErrorValue(commandInfo, entryValue, payloadCandidates),
|
|
109
|
-
]));
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
interface CommandSuggestion {
|
|
113
|
-
args?: string[];
|
|
114
|
-
description: string;
|
|
115
|
-
id?: string;
|
|
116
|
-
}
|
|
117
|
-
|
|
118
|
-
const UNKNOWN_COMMAND_SUGGESTIONS: Record<string, CommandSuggestion[]> = {
|
|
119
|
-
attr: [{ description: "Use `get attr <selector> <name>` to read an attribute from a selector or current `@ref`." }],
|
|
120
|
-
count: [{ description: "Use `get count <selector>` to count matching elements." }],
|
|
121
|
-
html: [{ description: "Use `get html <selector>` to read element HTML, or `get html` for the page when upstream supports it." }],
|
|
122
|
-
text: [{ description: "Use `get text <selector>` to read text from a selector or current `@ref`; run `snapshot -i` first when you need a safe `@ref`." }],
|
|
123
|
-
title: [{ args: ["get", "title"], description: "Use `get title` to read the current page title.", id: "use-get-title" }],
|
|
124
|
-
url: [{ args: ["get", "url"], description: "Use `get url` to read the current page URL.", id: "use-get-url" }],
|
|
125
|
-
value: [{ description: "Use `get value <selector>` to read form control value from a selector or current `@ref`." }],
|
|
126
|
-
};
|
|
127
|
-
|
|
128
|
-
function getUnknownCommandSuggestions(command: string | undefined, errorText: string): CommandSuggestion[] {
|
|
129
|
-
if (!command) return [];
|
|
130
|
-
const normalizedCommand = command.trim().toLowerCase();
|
|
131
|
-
if (!/\bunknown\s+command\b|\bunknown\s+subcommand\b|\bunrecognized\s+command\b/i.test(errorText)) return [];
|
|
132
|
-
return UNKNOWN_COMMAND_SUGGESTIONS[normalizedCommand] ?? [];
|
|
133
|
-
}
|
|
134
|
-
|
|
135
|
-
function formatUnknownCommandSuggestionText(suggestions: CommandSuggestion[]): string | undefined {
|
|
136
|
-
if (suggestions.length === 0) return undefined;
|
|
137
|
-
return ["Agent-browser hint: This looks like a getter shortcut, but upstream getter commands are grouped under `get`.", ...suggestions.map((suggestion) => suggestion.description)].join(" ");
|
|
138
|
-
}
|
|
139
|
-
|
|
140
|
-
function buildUnknownCommandSuggestionActions(suggestions: CommandSuggestion[], sessionName: string | undefined): AgentBrowserNextAction[] | undefined {
|
|
141
|
-
const actions = suggestions
|
|
142
|
-
.filter((suggestion): suggestion is CommandSuggestion & { args: string[]; id: string } => suggestion.args !== undefined && suggestion.id !== undefined)
|
|
143
|
-
.map((suggestion) => ({
|
|
144
|
-
id: suggestion.id,
|
|
145
|
-
params: { args: withOptionalSessionArgs(sessionName, suggestion.args) },
|
|
146
|
-
reason: suggestion.description,
|
|
147
|
-
safety: "Read-only getter command; safe to retry when you intended to inspect page state.",
|
|
148
|
-
tool: "agent_browser" as const,
|
|
149
|
-
}));
|
|
150
|
-
return actions.length > 0 ? actions : undefined;
|
|
151
|
-
}
|
|
152
|
-
|
|
153
|
-
function getLocalhostNavigationHint(commandInfo: CommandInfo, errorText: string): string | undefined {
|
|
154
|
-
if (!commandInfo.command || !isOpenNavigationCommand(commandInfo.command) || !commandInfo.subcommand) return undefined;
|
|
155
|
-
if (!/\bnet::ERR_(?:EMPTY_RESPONSE|CONNECTION_REFUSED|ADDRESS_UNREACHABLE|TIMED_OUT|CONNECTION_RESET)\b/i.test(errorText)) return undefined;
|
|
156
|
-
|
|
157
|
-
let targetUrl: URL;
|
|
158
|
-
try {
|
|
159
|
-
targetUrl = new URL(commandInfo.subcommand);
|
|
160
|
-
} catch {
|
|
161
|
-
return undefined;
|
|
162
|
-
}
|
|
163
|
-
|
|
164
|
-
if (!["localhost", "127.0.0.1", "::1", "[::1]"].includes(targetUrl.hostname.toLowerCase())) return undefined;
|
|
165
|
-
|
|
166
|
-
return [
|
|
167
|
-
"Agent-browser local fixture hint: the browser process could not read a loopback URL from its own network namespace or browser host.",
|
|
168
|
-
"Verify the server is still running and bound to an address the browser host can reach; if curl works from the shell but browser navigation fails, try the other loopback alias, add a proxy bypass for localhost/127.0.0.1 if a proxy is configured, or use a browser-host-reachable URL.",
|
|
169
|
-
"Use file:// only for static fallback fixtures and clean up any temporary server process outside agent_browser when the check is done.",
|
|
170
|
-
].join(" ");
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
export function appendSelectorRecoveryHint(errorText: string): string {
|
|
174
|
-
const hint = getSelectorRecoveryHint(errorText);
|
|
175
|
-
if (!hint || errorText.includes("Agent-browser hint:")) return errorText;
|
|
176
|
-
return `${errorText}\n\n${hint}`;
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
export function buildErrorPresentation(options: {
|
|
180
|
-
args?: string[];
|
|
181
|
-
commandInfo: CommandInfo;
|
|
182
|
-
errorText: string;
|
|
183
|
-
sessionName?: string;
|
|
184
|
-
}): ToolPresentation {
|
|
185
|
-
const { args, commandInfo, errorText, sessionName } = options;
|
|
186
|
-
const safeErrorText = redactModelFacingText(
|
|
187
|
-
redactSensitiveText(redactClipboardPermissionEcho(commandInfo, errorText)),
|
|
188
|
-
);
|
|
189
|
-
const selectorHintedErrorText = appendSelectorRecoveryHint(safeErrorText);
|
|
190
|
-
const unknownCommandSuggestions = getUnknownCommandSuggestions(commandInfo.command, safeErrorText);
|
|
191
|
-
const unknownCommandSuggestionText = formatUnknownCommandSuggestionText(unknownCommandSuggestions);
|
|
192
|
-
const browserProfileConfigRecovery = buildBrowserProfileConfigRecovery({ args, commandInfo, errorText: safeErrorText });
|
|
193
|
-
const localhostNavigationHint = getLocalhostNavigationHint(commandInfo, safeErrorText);
|
|
194
|
-
const clipboardPermissionHint = getClipboardPermissionHint(commandInfo, safeErrorText);
|
|
195
|
-
const keyboardPressHint = getKeyboardPressHint(commandInfo, safeErrorText);
|
|
196
|
-
const hintedErrorParts = [
|
|
197
|
-
selectorHintedErrorText,
|
|
198
|
-
unknownCommandSuggestionText && !selectorHintedErrorText.includes("Agent-browser hint:") ? unknownCommandSuggestionText : undefined,
|
|
199
|
-
browserProfileConfigRecovery?.hint,
|
|
200
|
-
localhostNavigationHint,
|
|
201
|
-
clipboardPermissionHint,
|
|
202
|
-
keyboardPressHint,
|
|
203
|
-
].filter((part): part is string => Boolean(part));
|
|
204
|
-
const hintedErrorText = hintedErrorParts.join("\n\n");
|
|
205
|
-
const categoryDetails = buildAgentBrowserResultCategoryDetails({
|
|
206
|
-
args: [commandInfo.command, commandInfo.subcommand].filter((item): item is string => item !== undefined),
|
|
207
|
-
command: commandInfo.command,
|
|
208
|
-
errorText: hintedErrorText,
|
|
209
|
-
succeeded: false,
|
|
210
|
-
});
|
|
211
|
-
const nextActions = [
|
|
212
|
-
...(buildUnknownCommandSuggestionActions(unknownCommandSuggestions, sessionName) ?? []),
|
|
213
|
-
...(browserProfileConfigRecovery?.actions ?? []),
|
|
214
|
-
...(buildAgentBrowserNextActions({
|
|
215
|
-
args,
|
|
216
|
-
command: commandInfo.command,
|
|
217
|
-
failureCategory: categoryDetails.failureCategory,
|
|
218
|
-
resultCategory: "failure",
|
|
219
|
-
}) ?? []),
|
|
220
|
-
];
|
|
221
|
-
return {
|
|
222
|
-
...categoryDetails,
|
|
223
|
-
content: [{ type: "text", text: hintedErrorText }],
|
|
224
|
-
nextActions: nextActions.length > 0 ? nextActions : undefined,
|
|
225
|
-
summary: hintedErrorText,
|
|
226
|
-
};
|
|
227
|
-
}
|
|
@@ -1,182 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Purpose: Compact oversized model-facing tool output into bounded previews and spill artifacts.
|
|
3
|
-
* Responsibilities: Write full output to persistent/session temp storage, update artifact manifests, and preserve safe previews.
|
|
4
|
-
* Scope: Large-output compaction only.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import type { CommandInfo } from "../../runtime.js";
|
|
8
|
-
import {
|
|
9
|
-
type PersistentSessionArtifactEviction,
|
|
10
|
-
type PersistentSessionArtifactStore,
|
|
11
|
-
writePersistentSessionArtifactFile,
|
|
12
|
-
writeSecureTempFile,
|
|
13
|
-
} from "../../temp.js";
|
|
14
|
-
import { buildEvictedSessionArtifactEntries } from "../artifact-manifest.js";
|
|
15
|
-
import type { ArtifactStorageScope, SessionArtifactManifest, SessionArtifactManifestEntry, ToolPresentation } from "../contracts.js";
|
|
16
|
-
import { countLines, truncateText } from "../text.js";
|
|
17
|
-
import { applyArtifactManifest } from "./artifacts.js";
|
|
18
|
-
import { getPresentationText } from "./content.js";
|
|
19
|
-
import { redactModelFacingText, stringifyModelFacing } from "./common.js";
|
|
20
|
-
|
|
21
|
-
const LARGE_OUTPUT_INLINE_MAX_CHARS = 8_000;
|
|
22
|
-
|
|
23
|
-
const LARGE_OUTPUT_INLINE_MAX_LINES = 120;
|
|
24
|
-
|
|
25
|
-
const LARGE_OUTPUT_PREVIEW_MAX_CHARS = 2_500;
|
|
26
|
-
|
|
27
|
-
const LARGE_OUTPUT_PREVIEW_MAX_LINES = 40;
|
|
28
|
-
|
|
29
|
-
const LARGE_OUTPUT_FILE_PREFIX = "pi-agent-browser-output";
|
|
30
|
-
|
|
31
|
-
function shouldCompactLargeOutput(text: string): boolean {
|
|
32
|
-
return text.length > LARGE_OUTPUT_INLINE_MAX_CHARS || countLines(text) > LARGE_OUTPUT_INLINE_MAX_LINES;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
function buildLargeOutputPreview(text: string): { omittedLineCount: number; previewText: string } {
|
|
36
|
-
const lines = text.split("\n");
|
|
37
|
-
const previewLines: string[] = [];
|
|
38
|
-
let previewChars = 0;
|
|
39
|
-
for (const line of lines) {
|
|
40
|
-
if (previewLines.length >= LARGE_OUTPUT_PREVIEW_MAX_LINES || previewChars >= LARGE_OUTPUT_PREVIEW_MAX_CHARS) {
|
|
41
|
-
break;
|
|
42
|
-
}
|
|
43
|
-
const remainingChars = LARGE_OUTPUT_PREVIEW_MAX_CHARS - previewChars;
|
|
44
|
-
const previewLine = truncateText(line, Math.max(40, remainingChars));
|
|
45
|
-
previewLines.push(previewLine);
|
|
46
|
-
previewChars += previewLine.length + 1;
|
|
47
|
-
}
|
|
48
|
-
return {
|
|
49
|
-
omittedLineCount: Math.max(0, lines.length - previewLines.length),
|
|
50
|
-
previewText: previewLines.join("\n"),
|
|
51
|
-
};
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
interface LargeOutputSpillWriteResult {
|
|
55
|
-
evictedArtifacts: PersistentSessionArtifactEviction[];
|
|
56
|
-
path: string;
|
|
57
|
-
storageScope: ArtifactStorageScope;
|
|
58
|
-
}
|
|
59
|
-
|
|
60
|
-
async function writeLargeOutputSpillFile(options: {
|
|
61
|
-
data: unknown;
|
|
62
|
-
persistentArtifactStore?: PersistentSessionArtifactStore;
|
|
63
|
-
text: string;
|
|
64
|
-
}): Promise<LargeOutputSpillWriteResult> {
|
|
65
|
-
const payload =
|
|
66
|
-
typeof options.data === "string"
|
|
67
|
-
? redactModelFacingText(options.data)
|
|
68
|
-
: typeof options.data === "number" || typeof options.data === "boolean"
|
|
69
|
-
? String(options.data)
|
|
70
|
-
: options.data === undefined
|
|
71
|
-
? redactModelFacingText(options.text)
|
|
72
|
-
: stringifyModelFacing(options.data);
|
|
73
|
-
const isStructuredPayload = typeof options.data !== "string" && typeof options.data !== "number" && typeof options.data !== "boolean";
|
|
74
|
-
const fileOptions = {
|
|
75
|
-
content: payload,
|
|
76
|
-
prefix: LARGE_OUTPUT_FILE_PREFIX,
|
|
77
|
-
suffix: isStructuredPayload ? ".json" : ".txt",
|
|
78
|
-
};
|
|
79
|
-
if (options.persistentArtifactStore) {
|
|
80
|
-
const result = await writePersistentSessionArtifactFile({ ...fileOptions, store: options.persistentArtifactStore });
|
|
81
|
-
return { ...result, storageScope: "persistent-session" };
|
|
82
|
-
}
|
|
83
|
-
return { evictedArtifacts: [], path: await writeSecureTempFile(fileOptions), storageScope: "process-temp" };
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function buildSpillArtifactEntries(options: {
|
|
87
|
-
commandInfo: CommandInfo;
|
|
88
|
-
evictedArtifacts: PersistentSessionArtifactEviction[];
|
|
89
|
-
path: string;
|
|
90
|
-
storageScope: ArtifactStorageScope;
|
|
91
|
-
}): SessionArtifactManifestEntry[] {
|
|
92
|
-
const nowMs = Date.now();
|
|
93
|
-
return [
|
|
94
|
-
{
|
|
95
|
-
command: options.commandInfo.command,
|
|
96
|
-
createdAtMs: nowMs,
|
|
97
|
-
kind: "spill",
|
|
98
|
-
path: options.path,
|
|
99
|
-
retentionState: options.storageScope === "persistent-session" ? "live" : "ephemeral",
|
|
100
|
-
storageScope: options.storageScope,
|
|
101
|
-
subcommand: options.commandInfo.subcommand,
|
|
102
|
-
},
|
|
103
|
-
...buildEvictedSessionArtifactEntries(options.evictedArtifacts, nowMs),
|
|
104
|
-
];
|
|
105
|
-
}
|
|
106
|
-
|
|
107
|
-
export async function compactLargePresentationOutput(options: {
|
|
108
|
-
artifactManifest?: SessionArtifactManifest;
|
|
109
|
-
commandInfo: CommandInfo;
|
|
110
|
-
data: unknown;
|
|
111
|
-
persistentArtifactStore?: PersistentSessionArtifactStore;
|
|
112
|
-
presentation: ToolPresentation;
|
|
113
|
-
}): Promise<ToolPresentation> {
|
|
114
|
-
const text = getPresentationText(options.presentation);
|
|
115
|
-
if (text.length === 0 || !shouldCompactLargeOutput(text)) {
|
|
116
|
-
return options.presentation;
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
let fullOutputPath: string | undefined;
|
|
120
|
-
let spill: LargeOutputSpillWriteResult | undefined;
|
|
121
|
-
let spillErrorText: string | undefined;
|
|
122
|
-
try {
|
|
123
|
-
spill = await writeLargeOutputSpillFile({
|
|
124
|
-
data: options.data,
|
|
125
|
-
persistentArtifactStore: options.persistentArtifactStore,
|
|
126
|
-
text,
|
|
127
|
-
});
|
|
128
|
-
fullOutputPath = spill.path;
|
|
129
|
-
} catch (error) {
|
|
130
|
-
spillErrorText = error instanceof Error ? error.message : String(error);
|
|
131
|
-
}
|
|
132
|
-
|
|
133
|
-
const { omittedLineCount, previewText } = buildLargeOutputPreview(text);
|
|
134
|
-
const commandLabel = options.commandInfo.command ?? "agent-browser";
|
|
135
|
-
const lines = [
|
|
136
|
-
`Large ${commandLabel} output compacted.`,
|
|
137
|
-
"",
|
|
138
|
-
"Preview:",
|
|
139
|
-
previewText,
|
|
140
|
-
];
|
|
141
|
-
if (omittedLineCount > 0) {
|
|
142
|
-
lines.push(`- ... (${omittedLineCount} additional lines omitted)`);
|
|
143
|
-
}
|
|
144
|
-
lines.push(
|
|
145
|
-
"",
|
|
146
|
-
fullOutputPath
|
|
147
|
-
? `Full output path: ${fullOutputPath}`
|
|
148
|
-
: `Full output unavailable: ${spillErrorText ?? "spill file could not be created."}`,
|
|
149
|
-
);
|
|
150
|
-
|
|
151
|
-
const firstTextIndex = options.presentation.content.findIndex((part) => part.type === "text");
|
|
152
|
-
const compactedText = lines.join("\n");
|
|
153
|
-
if (firstTextIndex >= 0) {
|
|
154
|
-
options.presentation.content[firstTextIndex] = { type: "text", text: compactedText };
|
|
155
|
-
} else {
|
|
156
|
-
options.presentation.content.unshift({ type: "text", text: compactedText });
|
|
157
|
-
}
|
|
158
|
-
options.presentation.data = {
|
|
159
|
-
compacted: true,
|
|
160
|
-
fullOutputPath,
|
|
161
|
-
outputCharCount: text.length,
|
|
162
|
-
outputLineCount: countLines(text),
|
|
163
|
-
previewCharCount: previewText.length,
|
|
164
|
-
previewLineCount: countLines(previewText),
|
|
165
|
-
spillError: spillErrorText,
|
|
166
|
-
};
|
|
167
|
-
options.presentation.fullOutputPath = fullOutputPath;
|
|
168
|
-
options.presentation.summary = `${options.presentation.summary} (compact)`;
|
|
169
|
-
if (fullOutputPath && spill) {
|
|
170
|
-
return applyArtifactManifest(
|
|
171
|
-
options.presentation,
|
|
172
|
-
options.presentation.artifactManifest ?? options.artifactManifest,
|
|
173
|
-
buildSpillArtifactEntries({
|
|
174
|
-
commandInfo: options.commandInfo,
|
|
175
|
-
evictedArtifacts: spill.evictedArtifacts,
|
|
176
|
-
path: fullOutputPath,
|
|
177
|
-
storageScope: spill.storageScope,
|
|
178
|
-
}),
|
|
179
|
-
);
|
|
180
|
-
}
|
|
181
|
-
return options.presentation;
|
|
182
|
-
}
|
|
@@ -1,184 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Purpose: Format scalar extraction results, navigation summaries, and page-change summaries.
|
|
3
|
-
* Responsibilities: Keep navigation/extraction presentation separate from core tool result orchestration.
|
|
4
|
-
* Scope: Navigation and get/eval extraction formatting only.
|
|
5
|
-
*/
|
|
6
|
-
|
|
7
|
-
import { isNavigationObservableCommandName, isPageChangeSummaryCommand } from "../../command-taxonomy.js";
|
|
8
|
-
import { isRecord } from "../../parsing.js";
|
|
9
|
-
import type { CommandInfo } from "../../runtime.js";
|
|
10
|
-
import { detectConfirmationRequired } from "../confirmation.js";
|
|
11
|
-
import type { AgentBrowserPageChangeSummary, FileArtifactMetadata } from "../contracts.js";
|
|
12
|
-
import { redactModelFacingText, stringifyModelFacing } from "./common.js";
|
|
13
|
-
|
|
14
|
-
const NAVIGATION_SUMMARY_FIELD = "navigationSummary";
|
|
15
|
-
|
|
16
|
-
interface NavigationSummary {
|
|
17
|
-
title?: string;
|
|
18
|
-
url?: string;
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
function getScalarExtractionResult(data: Record<string, unknown>): string | undefined {
|
|
22
|
-
const { result } = data;
|
|
23
|
-
if (typeof result === "string") {
|
|
24
|
-
return result.trim().length > 0 ? result : undefined;
|
|
25
|
-
}
|
|
26
|
-
if (typeof result === "number" || typeof result === "boolean") {
|
|
27
|
-
return String(result);
|
|
28
|
-
}
|
|
29
|
-
return undefined;
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
function getExtractionOrigin(data: Record<string, unknown>): string | undefined {
|
|
33
|
-
if (typeof data.origin === "string" && data.origin.trim().length > 0) {
|
|
34
|
-
return data.origin.trim();
|
|
35
|
-
}
|
|
36
|
-
if (typeof data.url === "string" && data.url.trim().length > 0) {
|
|
37
|
-
return data.url.trim();
|
|
38
|
-
}
|
|
39
|
-
return undefined;
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
function formatGetSummaryLabel(subcommand: string | undefined): string {
|
|
43
|
-
if (!subcommand) {
|
|
44
|
-
return "Get result";
|
|
45
|
-
}
|
|
46
|
-
if (subcommand.toLowerCase() === "url") {
|
|
47
|
-
return "URL";
|
|
48
|
-
}
|
|
49
|
-
return `${subcommand.slice(0, 1).toUpperCase()}${subcommand.slice(1)}`;
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
export function formatExtractionSummary(commandInfo: CommandInfo, data: Record<string, unknown>): string | undefined {
|
|
53
|
-
const scalarResult = getScalarExtractionResult(data);
|
|
54
|
-
if (!scalarResult) {
|
|
55
|
-
return undefined;
|
|
56
|
-
}
|
|
57
|
-
const safeScalarResult = redactModelFacingText(scalarResult);
|
|
58
|
-
const firstResultLine = safeScalarResult.split("\n", 1)[0] ?? safeScalarResult;
|
|
59
|
-
if (commandInfo.command === "get") {
|
|
60
|
-
return `${formatGetSummaryLabel(commandInfo.subcommand)}: ${firstResultLine}`;
|
|
61
|
-
}
|
|
62
|
-
if (commandInfo.command === "eval") {
|
|
63
|
-
return `Eval result: ${firstResultLine}`;
|
|
64
|
-
}
|
|
65
|
-
return undefined;
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
export function formatExtractionText(commandInfo: CommandInfo, data: Record<string, unknown>): string | undefined {
|
|
69
|
-
if (commandInfo.command !== "get" && commandInfo.command !== "eval") {
|
|
70
|
-
return undefined;
|
|
71
|
-
}
|
|
72
|
-
const scalarResult = getScalarExtractionResult(data);
|
|
73
|
-
if (!scalarResult) {
|
|
74
|
-
return undefined;
|
|
75
|
-
}
|
|
76
|
-
const origin = getExtractionOrigin(data);
|
|
77
|
-
const safeScalarResult = redactModelFacingText(scalarResult);
|
|
78
|
-
const safeOrigin = origin ? redactModelFacingText(origin) : undefined;
|
|
79
|
-
return safeOrigin && safeOrigin !== safeScalarResult ? `${safeScalarResult}\n\nOrigin: ${safeOrigin}` : safeScalarResult;
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
export function isNavigationObservableCommand(command: string | undefined): boolean {
|
|
83
|
-
return isNavigationObservableCommandName(command);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
function isNavigationSummary(value: unknown): value is NavigationSummary {
|
|
87
|
-
return isRecord(value) && (typeof value.title === "string" || typeof value.url === "string");
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
export function getNavigationSummary(data: Record<string, unknown>): NavigationSummary | undefined {
|
|
91
|
-
const candidate = data[NAVIGATION_SUMMARY_FIELD];
|
|
92
|
-
return isNavigationSummary(candidate) ? candidate : undefined;
|
|
93
|
-
}
|
|
94
|
-
|
|
95
|
-
function getTopLevelNavigationSummary(data: Record<string, unknown>): NavigationSummary | undefined {
|
|
96
|
-
return isNavigationSummary(data)
|
|
97
|
-
? {
|
|
98
|
-
title: typeof data.title === "string" ? data.title : undefined,
|
|
99
|
-
url: typeof data.url === "string" ? data.url : undefined,
|
|
100
|
-
}
|
|
101
|
-
: undefined;
|
|
102
|
-
}
|
|
103
|
-
|
|
104
|
-
function getNormalizedNavigationSummary(summary: NavigationSummary | undefined): { title?: string; url?: string } | undefined {
|
|
105
|
-
const title = typeof summary?.title === "string" && summary.title.trim().length > 0 ? summary.title.trim() : undefined;
|
|
106
|
-
const url = typeof summary?.url === "string" && summary.url.trim().length > 0 ? summary.url.trim() : undefined;
|
|
107
|
-
return title || url ? { title, url } : undefined;
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
export function formatNavigationSummary(summary: NavigationSummary): string | undefined {
|
|
111
|
-
const normalized = getNormalizedNavigationSummary(summary);
|
|
112
|
-
if (!normalized) return undefined;
|
|
113
|
-
if (normalized.title && normalized.url) return `${normalized.title}\n${normalized.url}`;
|
|
114
|
-
return normalized.title ?? normalized.url;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
export function buildPageChangeSummary(options: {
|
|
118
|
-
artifacts?: FileArtifactMetadata[];
|
|
119
|
-
commandInfo: CommandInfo;
|
|
120
|
-
data: unknown;
|
|
121
|
-
nextActions?: Array<{ id: string }>;
|
|
122
|
-
savedFilePath?: string;
|
|
123
|
-
summary: string;
|
|
124
|
-
}): AgentBrowserPageChangeSummary | undefined {
|
|
125
|
-
const { artifacts, commandInfo, data, nextActions, savedFilePath } = options;
|
|
126
|
-
const artifactCount = artifacts?.length ?? 0;
|
|
127
|
-
const navigation = isRecord(data)
|
|
128
|
-
? getNormalizedNavigationSummary(getNavigationSummary(data) ?? (isPageChangeSummaryCommand(commandInfo.command) ? getTopLevelNavigationSummary(data) : undefined))
|
|
129
|
-
: undefined;
|
|
130
|
-
const confirmationRequired = detectConfirmationRequired(data) !== undefined;
|
|
131
|
-
if (!navigation && !confirmationRequired && artifactCount === 0 && !savedFilePath && !isPageChangeSummaryCommand(commandInfo.command)) {
|
|
132
|
-
return undefined;
|
|
133
|
-
}
|
|
134
|
-
const changeType: AgentBrowserPageChangeSummary["changeType"] = savedFilePath || artifactCount > 0
|
|
135
|
-
? "artifact"
|
|
136
|
-
: navigation
|
|
137
|
-
? "navigation"
|
|
138
|
-
: confirmationRequired
|
|
139
|
-
? "confirmation"
|
|
140
|
-
: "mutation";
|
|
141
|
-
const parts = [commandInfo.command ?? "agent-browser", changeType];
|
|
142
|
-
if (navigation?.title) parts.push(navigation.title);
|
|
143
|
-
if (navigation?.url) parts.push(navigation.url);
|
|
144
|
-
if (savedFilePath) parts.push(savedFilePath);
|
|
145
|
-
else if (artifactCount > 0) parts.push(`${artifactCount} artifact${artifactCount === 1 ? "" : "s"}`);
|
|
146
|
-
return {
|
|
147
|
-
...(artifactCount > 0 ? { artifactCount } : {}),
|
|
148
|
-
changeType,
|
|
149
|
-
...(commandInfo.command ? { command: commandInfo.command } : {}),
|
|
150
|
-
...(nextActions ? { nextActionIds: nextActions.map((action) => action.id) } : {}),
|
|
151
|
-
...(savedFilePath ? { savedFilePath } : {}),
|
|
152
|
-
summary: parts.join(" → "),
|
|
153
|
-
...(navigation?.title ? { title: navigation.title } : {}),
|
|
154
|
-
...(navigation?.url ? { url: navigation.url } : {}),
|
|
155
|
-
};
|
|
156
|
-
}
|
|
157
|
-
|
|
158
|
-
function stripNavigationSummary(data: Record<string, unknown>): Record<string, unknown> {
|
|
159
|
-
const { [NAVIGATION_SUMMARY_FIELD]: _navigationSummary, ...rest } = data;
|
|
160
|
-
return rest;
|
|
161
|
-
}
|
|
162
|
-
|
|
163
|
-
export function formatNavigationActionResult(data: Record<string, unknown>): string | undefined {
|
|
164
|
-
const actionData = stripNavigationSummary(data);
|
|
165
|
-
const lines: string[] = [];
|
|
166
|
-
if (typeof actionData.clicked === "string" || typeof actionData.clicked === "boolean") {
|
|
167
|
-
lines.push(`Clicked: ${String(actionData.clicked)}`);
|
|
168
|
-
}
|
|
169
|
-
if (typeof actionData.href === "string") {
|
|
170
|
-
lines.push(`Href: ${redactModelFacingText(actionData.href)}`);
|
|
171
|
-
}
|
|
172
|
-
if (typeof actionData.navigated === "boolean") {
|
|
173
|
-
lines.push(`Navigated: ${actionData.navigated}`);
|
|
174
|
-
}
|
|
175
|
-
if (lines.length > 0) {
|
|
176
|
-
return lines.join("\n");
|
|
177
|
-
}
|
|
178
|
-
|
|
179
|
-
const actionText = stringifyModelFacing(actionData).trim();
|
|
180
|
-
if (actionText.length === 0 || actionText === "{}") {
|
|
181
|
-
return undefined;
|
|
182
|
-
}
|
|
183
|
-
return actionText;
|
|
184
|
-
}
|