pi-agent-browser-native 0.2.77 → 0.5.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 +154 -0
- package/README.md +69 -35
- package/dist/extensions/agent-browser/index.js +911 -121
- package/dist/extensions/agent-browser/lib/argv-descriptor.js +35 -8
- package/dist/extensions/agent-browser/lib/argv-grammar.js +48 -7
- package/dist/extensions/agent-browser/lib/batch-lifecycle.js +71 -0
- package/dist/extensions/agent-browser/lib/command-policy.js +1 -6
- package/dist/extensions/agent-browser/lib/command-taxonomy.js +35 -8
- package/dist/extensions/agent-browser/lib/config-policy.js +0 -5
- package/dist/extensions/agent-browser/lib/config.js +0 -6
- package/dist/extensions/agent-browser/lib/electron/cdp.js +6 -7
- package/dist/extensions/agent-browser/lib/electron/cleanup.js +0 -7
- package/dist/extensions/agent-browser/lib/electron/discovery.js +0 -7
- package/dist/extensions/agent-browser/lib/electron/launch.js +0 -7
- package/dist/extensions/agent-browser/lib/input-modes/electron.js +0 -5
- package/dist/extensions/agent-browser/lib/input-modes/job.js +61 -9
- package/dist/extensions/agent-browser/lib/input-modes/lookups.js +2 -7
- package/dist/extensions/agent-browser/lib/input-modes/params.js +23 -29
- package/dist/extensions/agent-browser/lib/input-modes/script.js +462 -0
- package/dist/extensions/agent-browser/lib/input-modes/semantic-action.js +51 -17
- package/dist/extensions/agent-browser/lib/input-modes/shared.js +0 -5
- package/dist/extensions/agent-browser/lib/input-modes/types.js +0 -5
- package/dist/extensions/agent-browser/lib/json-schema.js +0 -5
- package/dist/extensions/agent-browser/lib/launch-scoped-flags.js +12 -5
- package/dist/extensions/agent-browser/lib/managed-session-capabilities.js +0 -2
- package/dist/extensions/agent-browser/lib/managed-session-policy-lock.js +3 -4
- package/dist/extensions/agent-browser/lib/managed-session-restore.js +55 -48
- package/dist/extensions/agent-browser/lib/managed-session-snapshots.js +2 -9
- package/dist/extensions/agent-browser/lib/managed-session-state-policy.js +48 -36
- package/dist/extensions/agent-browser/lib/managed-session-storage.js +50 -29
- package/dist/extensions/agent-browser/lib/navigation-policy.js +0 -5
- package/dist/extensions/agent-browser/lib/orchestration/batch-stdin.js +26 -5
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/artifact-paths.js +110 -30
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/click-dispatch.js +2 -1
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.js +27 -26
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result.js +58 -33
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/index.js +2 -1
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/managed-session-daemon-policy.js +22 -14
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/direct-anchor-download.js +1 -1
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/network-page-filter.js +1 -1
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/scroll-shims.js +1 -1
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/snapshot-filter.js +1 -1
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare.js +148 -34
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output.js +225 -61
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-state.js +74 -30
- package/dist/extensions/agent-browser/lib/orchestration/electron-host/index.js +47 -26
- package/dist/extensions/agent-browser/lib/orchestration/input-plan.js +40 -19
- package/dist/extensions/agent-browser/lib/orchestration/output-file.js +42 -1
- package/dist/extensions/agent-browser/lib/orchestration/script-mode.js +299 -0
- package/dist/extensions/agent-browser/lib/parsing.js +0 -7
- package/dist/extensions/agent-browser/lib/pi-tool-rendering.js +36 -11
- package/dist/extensions/agent-browser/lib/playbook.js +20 -24
- package/dist/extensions/agent-browser/lib/process-environment.js +14 -0
- package/dist/extensions/agent-browser/lib/process-identity.js +4 -9
- package/dist/extensions/agent-browser/lib/process.js +131 -48
- package/dist/extensions/agent-browser/lib/prompt-policy.js +171 -27
- package/dist/extensions/agent-browser/lib/recording-reservations.js +183 -0
- package/dist/extensions/agent-browser/lib/results/action-recommendations.js +63 -13
- package/dist/extensions/agent-browser/lib/results/artifact-manifest.js +68 -11
- package/dist/extensions/agent-browser/lib/results/categories.js +7 -9
- package/dist/extensions/agent-browser/lib/results/confirmation.js +0 -7
- package/dist/extensions/agent-browser/lib/results/contracts.js +0 -7
- package/dist/extensions/agent-browser/lib/results/editable-ref-evidence.js +0 -7
- package/dist/extensions/agent-browser/lib/results/envelope.js +0 -7
- package/dist/extensions/agent-browser/lib/results/network.js +0 -7
- package/dist/extensions/agent-browser/lib/results/next-actions.js +19 -35
- package/dist/extensions/agent-browser/lib/results/presentation/artifacts.js +85 -44
- package/dist/extensions/agent-browser/lib/results/presentation/batch.js +66 -11
- package/dist/extensions/agent-browser/lib/results/presentation/browser-profile-recovery.js +0 -5
- package/dist/extensions/agent-browser/lib/results/presentation/common.js +18 -5
- package/dist/extensions/agent-browser/lib/results/presentation/content.js +0 -5
- package/dist/extensions/agent-browser/lib/results/presentation/diagnostics.js +7 -6
- package/dist/extensions/agent-browser/lib/results/presentation/errors.js +2 -1
- package/dist/extensions/agent-browser/lib/results/presentation/large-output.js +0 -5
- package/dist/extensions/agent-browser/lib/results/presentation/managed-list-filter.js +0 -5
- package/dist/extensions/agent-browser/lib/results/presentation/navigation.js +26 -16
- package/dist/extensions/agent-browser/lib/results/presentation/registry.js +58 -13
- package/dist/extensions/agent-browser/lib/results/presentation/semantic-action.js +1 -15
- package/dist/extensions/agent-browser/lib/results/presentation/skills.js +0 -5
- package/dist/extensions/agent-browser/lib/results/presentation.js +6 -8
- package/dist/extensions/agent-browser/lib/results/recovery-actions.js +2 -7
- package/dist/extensions/agent-browser/lib/results/recovery-next-actions.js +0 -7
- package/dist/extensions/agent-browser/lib/results/selector-recovery.js +51 -15
- package/dist/extensions/agent-browser/lib/results/snapshot-high-value-controls.js +13 -14
- package/dist/extensions/agent-browser/lib/results/snapshot-refs.js +0 -7
- package/dist/extensions/agent-browser/lib/results/snapshot-segments.js +0 -7
- package/dist/extensions/agent-browser/lib/results/snapshot-spill.js +0 -7
- package/dist/extensions/agent-browser/lib/results/snapshot.js +0 -7
- package/dist/extensions/agent-browser/lib/results/text.js +0 -7
- package/dist/extensions/agent-browser/lib/runtime.js +119 -52
- package/dist/extensions/agent-browser/lib/session-page-state.js +62 -17
- package/dist/extensions/agent-browser/lib/string-enum-schema.js +0 -5
- package/dist/extensions/agent-browser/lib/temp.js +0 -7
- package/dist/extensions/agent-browser/lib/upstream-version.js +14 -0
- package/dist/extensions/agent-browser/lib/web-search.js +0 -5
- package/dist/extensions/agent-browser/script-worker.js +169 -0
- package/dist/scripts/agent-browser-target.mjs +3 -0
- package/docs/ARCHITECTURE.md +50 -31
- package/docs/COMMAND_REFERENCE.md +97 -41
- package/docs/ELECTRON.md +3 -19
- package/docs/RELEASE.md +8 -20
- package/docs/REQUIREMENTS.md +8 -6
- package/docs/SUPPORT_MATRIX.md +32 -25
- package/docs/TOOL_CONTRACT.md +107 -64
- package/docs/platform-smoke.md +1 -1
- package/package.json +7 -6
- package/platform-smoke.config.mjs +2 -2
- package/scripts/agent-browser-capability-baseline.mjs +24 -6
- package/scripts/agent-browser-target.mjs +3 -0
- package/scripts/build.mjs +41 -0
- package/scripts/doctor.mjs +8 -7
- package/scripts/platform-smoke/browser-dogfood-windows.ps1 +9 -3
- package/scripts/platform-smoke/targets.mjs +12 -6
- package/dist/extensions/agent-browser/lib/electron/text.js +0 -13
- package/dist/extensions/agent-browser/lib/input-modes.js +0 -10
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-artifacts.js +0 -5
- package/dist/extensions/agent-browser/lib/results/artifact-state.js +0 -13
- package/dist/extensions/agent-browser/lib/results.js +0 -14
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Purpose: Build the small JSON Schema subset used by Pi tool schemas without importing TypeBox at runtime.
|
|
3
|
-
* Responsibilities: Preserve plain JSON Schema objects Pi consumes while keeping extension startup cheap.
|
|
4
|
-
* Scope: Schema construction only; runtime validation still belongs to Pi and the tool input compilers.
|
|
5
|
-
*/
|
|
6
1
|
const OPTIONAL_SCHEMA = Symbol("pi-agent-browser-optional-schema");
|
|
7
2
|
function withOptions(schema, options) {
|
|
8
3
|
return { ...schema, ...(options ?? {}) };
|
|
@@ -1,8 +1,3 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Purpose: Canonical launch-scoped agent-browser flag metadata shared by runtime planning and agent-facing guidance.
|
|
3
|
-
* Responsibilities: Define which upstream flags require a fresh launch, explain why, expose stable guidance labels, and share launch-token detection used by runtime and managed restore policy.
|
|
4
|
-
* Scope: Launch-scoped metadata and token classification; broader execution planning lives in runtime.ts.
|
|
5
|
-
*/
|
|
6
1
|
import { findCommandStartIndex } from "./argv-descriptor.js";
|
|
7
2
|
import { isBooleanFlagEnabled, optionalGlobalValueFlagConsumesNext } from "./argv-grammar.js";
|
|
8
3
|
export const LAUNCH_SCOPED_FLAG_DEFINITIONS = [
|
|
@@ -42,6 +37,18 @@ export const LAUNCH_SCOPED_FLAG_DEFINITIONS = [
|
|
|
42
37
|
flag: "--idle-timeout",
|
|
43
38
|
reason: "configures background browser lifecycle for the launched session",
|
|
44
39
|
},
|
|
40
|
+
{
|
|
41
|
+
flag: "--args",
|
|
42
|
+
reason: "selects raw Chrome arguments for the browser launch",
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
flag: "--user-agent",
|
|
46
|
+
reason: "selects the browser user agent at launch time",
|
|
47
|
+
},
|
|
48
|
+
{
|
|
49
|
+
flag: "--headed",
|
|
50
|
+
reason: "selects whether the launched browser has a visible window",
|
|
51
|
+
},
|
|
45
52
|
{
|
|
46
53
|
flag: "--device",
|
|
47
54
|
reason: "selects the provider device for the upstream launch",
|
|
@@ -1,5 +1,3 @@
|
|
|
1
|
-
// Purpose: Identify and redact wrapper-owned managed restore capabilities.
|
|
2
|
-
// Scope: Pure string helpers shared by storage, policy, and presentation paths.
|
|
3
1
|
const MANAGED_SESSION_RESTORE_KEY_PATTERN = /piab-r(?:2)?-[a-f\d]{32}/gi;
|
|
4
2
|
const MANAGED_SESSION_RESTORE_KEY_EXACT_PATTERN = /^piab-r2-[a-f\d]{32}$/i;
|
|
5
3
|
const WRAPPER_MANAGED_SESSION_PREFIX = "piab-";
|
|
@@ -1,6 +1,3 @@
|
|
|
1
|
-
// Purpose: Serialize wrapper-owned daemon policy decisions across cooperating Pi processes.
|
|
2
|
-
// Responsibilities: Elect one live claim, bridge the pre-update v2 path, recover proven-dead current-protocol artifacts, and release only the caller's immutable token.
|
|
3
|
-
// Scope: Wrapper coordination only; agent-browser never reads these claims.
|
|
4
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
5
2
|
import { lstat, mkdir, readFile, readdir, rename, rm, writeFile } from "node:fs/promises";
|
|
6
3
|
import { tmpdir } from "node:os";
|
|
@@ -15,7 +12,9 @@ const LOCK_TICKET_FILE = "ticket.json";
|
|
|
15
12
|
function getCoordinationDirectory(platform = process.platform) {
|
|
16
13
|
if (platform !== "win32") {
|
|
17
14
|
const uid = typeof process.getuid === "function" ? process.getuid() : undefined;
|
|
18
|
-
return
|
|
15
|
+
return platform === "android"
|
|
16
|
+
? join(tmpdir(), `pi-agent-browser-policy${uid === undefined ? "" : `-${uid}`}`)
|
|
17
|
+
: `/tmp/pi-agent-browser-policy${uid === undefined ? "" : `-${uid}`}`;
|
|
19
18
|
}
|
|
20
19
|
const user = process.env.USERNAME ?? process.env.USER ?? "unknown";
|
|
21
20
|
const suffix = createHash("sha256").update(user).digest("hex").slice(0, 12);
|
|
@@ -1,18 +1,13 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Purpose: Own automatic restore policy for wrapper-managed browser sessions.
|
|
3
|
-
* Responsibilities: Resolve owned session identity, isolate per-call policy, reject incompatible argv/env/config, and persist sticky restore-disable state.
|
|
4
|
-
* Scope: Restore policy only; storage identity and snapshot retention live in focused sibling modules.
|
|
5
|
-
*/
|
|
6
1
|
import { AsyncLocalStorage } from "node:async_hooks";
|
|
7
2
|
import { chmodSync, lstatSync, readFileSync, unlinkSync } from "node:fs";
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import { canonicalizeAgentBrowserNamespace, extractExplicitNamespace, extractExplicitSessionName, extractRequestedRestoreKey, getAgentBrowserSessionIdentityKey, scanUpstreamGlobalFlagOccurrences, } from "./argv-grammar.js";
|
|
3
|
+
import { extractUpstreamCommandTokens, parseCommandInfo } from "./argv-descriptor.js";
|
|
4
|
+
import { canonicalizeAgentBrowserNamespace, extractExplicitNamespace, extractExplicitSessionName, extractRequestedRestoreKey, getAgentBrowserSessionIdentityKey, isUpstreamEnvFlagEnabled, scanUpstreamGlobalFlagOccurrences, } from "./argv-grammar.js";
|
|
11
5
|
import { hasLaunchScopedFlagToken, MANAGED_RESTORE_INCOMPATIBLE_BOOLEAN_ENVS, MANAGED_RESTORE_INCOMPATIBLE_ENVS, MANAGED_RESTORE_INCOMPATIBLE_FLAGS, } from "./launch-scoped-flags.js";
|
|
12
|
-
import { createManagedSessionRestoreKey, ensureManagedSessionRestoreStorageIsSecure, getManagedSessionRestoreProtectedStorageEnv, hasManagedSessionRestoreProjectIdentity, resolveManagedSessionRestoreHome, } from "./managed-session-storage.js";
|
|
6
|
+
import { createManagedSessionRestoreKey, ensureManagedSessionRestoreStorageIsSecure, getManagedSessionRestoreScope, getManagedSessionRestoreProtectedStorageEnv, hasManagedSessionRestoreProjectIdentity, resolveManagedSessionRestoreHome, } from "./managed-session-storage.js";
|
|
13
7
|
import { parseUserBatchStdin } from "./orchestration/batch-stdin.js";
|
|
8
|
+
import { getAgentBrowserProcessEnvironment } from "./process-environment.js";
|
|
14
9
|
import { writeSecureTempFile } from "./temp.js";
|
|
15
|
-
export { createManagedSessionRestoreKey, ensureManagedSessionRestoreStorageIsSecure } from "./managed-session-storage.js";
|
|
10
|
+
export { createManagedSessionRestoreKey, ensureManagedSessionRestoreStorageIsSecure, getManagedSessionRestoreScope } from "./managed-session-storage.js";
|
|
16
11
|
export { pruneOwnedManagedSessionRestoreSnapshots } from "./managed-session-snapshots.js";
|
|
17
12
|
const AGENT_BROWSER_CONFIG_ENV = "AGENT_BROWSER_CONFIG";
|
|
18
13
|
const AGENT_BROWSER_RESTORE_ENV = "AGENT_BROWSER_RESTORE";
|
|
@@ -28,10 +23,6 @@ function isDisabledEnvFlag(value) {
|
|
|
28
23
|
return false;
|
|
29
24
|
return ["0", "false", "no", "off"].includes(value.trim().toLowerCase());
|
|
30
25
|
}
|
|
31
|
-
/** Match upstream env_var_is_truthy exactly: lowercase only, without trimming or accepting "off". */
|
|
32
|
-
function isUpstreamEnvFlagEnabled(value) {
|
|
33
|
-
return value !== undefined && !["", "0", "false", "no"].includes(value.toLowerCase());
|
|
34
|
-
}
|
|
35
26
|
function hasUpstreamEnvValue(env, name) {
|
|
36
27
|
return env?.[name] !== undefined;
|
|
37
28
|
}
|
|
@@ -70,7 +61,7 @@ export class ManagedSessionRestoreState {
|
|
|
70
61
|
if (sessionName)
|
|
71
62
|
this.#daemonRestoreKeys.set(getAgentBrowserSessionIdentityKey(sessionName, namespace), restoreKey);
|
|
72
63
|
}
|
|
73
|
-
replace(identities, options = {}) {
|
|
64
|
+
replace(identities = [], options = {}) {
|
|
74
65
|
if (!options.preserveDaemonRestoreKeys)
|
|
75
66
|
this.#daemonRestoreKeys.clear();
|
|
76
67
|
this.#disabled.clear();
|
|
@@ -82,6 +73,9 @@ const ownedManagedSessionStorage = new AsyncLocalStorage();
|
|
|
82
73
|
export async function withOwnedManagedSessionContext(context, run) {
|
|
83
74
|
return await ownedManagedSessionStorage.run(context, run);
|
|
84
75
|
}
|
|
76
|
+
export function getOwnedManagedSessionRestoreKey() {
|
|
77
|
+
return ownedManagedSessionStorage.getStore()?.restoreKey;
|
|
78
|
+
}
|
|
85
79
|
export function resolveOwnedManagedSessionContext(options) {
|
|
86
80
|
const namespace = canonicalizeAgentBrowserNamespace(options.namespace);
|
|
87
81
|
const currentNamespace = canonicalizeAgentBrowserNamespace(options.currentManagedSessionNamespace);
|
|
@@ -105,33 +99,18 @@ function ownedContextMatches(sessionName, namespace) {
|
|
|
105
99
|
export function isOwnedManagedSessionTarget(args) {
|
|
106
100
|
return ownedContextMatches(extractExplicitSessionName(args), extractExplicitNamespace(args)) !== undefined;
|
|
107
101
|
}
|
|
108
|
-
function pathExistsOrIsUnreadable(path) {
|
|
109
|
-
try {
|
|
110
|
-
lstatSync(path);
|
|
111
|
-
return true;
|
|
112
|
-
}
|
|
113
|
-
catch (error) {
|
|
114
|
-
return error.code !== "ENOENT";
|
|
115
|
-
}
|
|
116
|
-
}
|
|
117
102
|
function hasExplicitConfigArg(args) {
|
|
118
103
|
return scanUpstreamGlobalFlagOccurrences(args, "--config").length > 0;
|
|
119
104
|
}
|
|
120
105
|
function closesBrowserSession(args) {
|
|
121
106
|
return ["close", "exit", "quit"].includes(parseCommandInfo(args).command ?? "");
|
|
122
107
|
}
|
|
123
|
-
export function
|
|
124
|
-
|
|
125
|
-
return true;
|
|
126
|
-
const paths = [join(cwd, "agent-browser.json")];
|
|
127
|
-
const home = resolveManagedSessionRestoreHome(parentEnv, platform);
|
|
128
|
-
if (home)
|
|
129
|
-
paths.push(join(home, ".agent-browser", "config.json"));
|
|
130
|
-
return paths.some(pathExistsOrIsUnreadable);
|
|
108
|
+
export function agentBrowserExplicitConfigIsPresent(parentEnv = getAgentBrowserProcessEnvironment(), args = []) {
|
|
109
|
+
return hasExplicitConfigArg(args) || hasUpstreamEnvValue(parentEnv, AGENT_BROWSER_CONFIG_ENV);
|
|
131
110
|
}
|
|
132
|
-
/**
|
|
133
|
-
export function agentBrowserConfigBlocksManagedRestore(
|
|
134
|
-
return !resolveManagedSessionRestoreHome(parentEnv, platform) ||
|
|
111
|
+
/** Explicit config overrides disable restore; passive files are ignored because every browser-backed subprocess pins a protected empty config. */
|
|
112
|
+
export function agentBrowserConfigBlocksManagedRestore(_cwd, parentEnv = getAgentBrowserProcessEnvironment(), args = [], platform = process.platform) {
|
|
113
|
+
return !resolveManagedSessionRestoreHome(parentEnv, platform) || agentBrowserExplicitConfigIsPresent(parentEnv, args);
|
|
135
114
|
}
|
|
136
115
|
function omitWrapperInjectedUserAgent(args, enabled) {
|
|
137
116
|
if (!enabled)
|
|
@@ -140,7 +119,7 @@ function omitWrapperInjectedUserAgent(args, enabled) {
|
|
|
140
119
|
return index < 0 ? args : [...args.slice(0, index), ...args.slice(index + 2)];
|
|
141
120
|
}
|
|
142
121
|
function batchHasManagedSessionRestoreConflict(args, stdin) {
|
|
143
|
-
const [command, ...commandArgs] =
|
|
122
|
+
const [command, ...commandArgs] = extractUpstreamCommandTokens(args);
|
|
144
123
|
if (command !== "batch")
|
|
145
124
|
return false;
|
|
146
125
|
if (commandArgs.some((token) => token !== "--bail"))
|
|
@@ -151,7 +130,7 @@ function batchHasManagedSessionRestoreConflict(args, stdin) {
|
|
|
151
130
|
return parsed.steps.some((step) => ["connect", "batch"].includes(parseCommandInfo(step).command ?? ""));
|
|
152
131
|
}
|
|
153
132
|
function hasManagedSessionRestoreLaunchConflict(options) {
|
|
154
|
-
const parentEnv = options.parentEnv ??
|
|
133
|
+
const parentEnv = options.parentEnv ?? getAgentBrowserProcessEnvironment();
|
|
155
134
|
const effectiveEnv = { ...parentEnv, ...options.env };
|
|
156
135
|
const args = omitWrapperInjectedUserAgent(options.args, options.wrapperInjectedUserAgent);
|
|
157
136
|
if (MANAGED_RESTORE_INCOMPATIBLE_ENVS.some((name) => hasUpstreamEnvValue(effectiveEnv, name)))
|
|
@@ -165,7 +144,7 @@ function hasManagedSessionRestoreLaunchConflict(options) {
|
|
|
165
144
|
return hasExplicitConfigArg(args) || hasUpstreamEnvValue({ ...parentEnv, ...options.env }, AGENT_BROWSER_CONFIG_ENV);
|
|
166
145
|
}
|
|
167
146
|
function managedSessionRestoreOptedOut(options) {
|
|
168
|
-
const effectiveEnv = { ...(options.parentEnv ??
|
|
147
|
+
const effectiveEnv = { ...(options.parentEnv ?? getAgentBrowserProcessEnvironment()), ...options.env };
|
|
169
148
|
return isDisabledEnvFlag(effectiveEnv[MANAGED_SESSION_RESTORE_ENV]);
|
|
170
149
|
}
|
|
171
150
|
function isManagedSessionRestoreIncompatible(options) {
|
|
@@ -173,7 +152,7 @@ function isManagedSessionRestoreIncompatible(options) {
|
|
|
173
152
|
return true;
|
|
174
153
|
if (managedSessionRestoreOptedOut(options))
|
|
175
154
|
return false;
|
|
176
|
-
const effectiveEnv = { ...(options.parentEnv ??
|
|
155
|
+
const effectiveEnv = { ...(options.parentEnv ?? getAgentBrowserProcessEnvironment()), ...options.env };
|
|
177
156
|
const args = omitWrapperInjectedUserAgent(options.args, options.wrapperInjectedUserAgent);
|
|
178
157
|
if (options.cwd && !hasManagedSessionRestoreProjectIdentity(options.cwd))
|
|
179
158
|
return true;
|
|
@@ -182,7 +161,7 @@ function isManagedSessionRestoreIncompatible(options) {
|
|
|
182
161
|
return !ensureManagedSessionRestoreStorageIsSecure(effectiveEnv, process.platform, extractExplicitNamespace(args));
|
|
183
162
|
}
|
|
184
163
|
function resolveManagedSessionRestorePolicy(options) {
|
|
185
|
-
const parentEnv = options.parentEnv ??
|
|
164
|
+
const parentEnv = options.parentEnv ?? getAgentBrowserProcessEnvironment();
|
|
186
165
|
const sessionName = extractExplicitSessionName(options.args);
|
|
187
166
|
const namespace = extractExplicitNamespace(options.args);
|
|
188
167
|
const ownedContext = ownedContextMatches(sessionName, namespace);
|
|
@@ -194,11 +173,35 @@ export function getOwnedManagedSessionNamespaceEnv(options) {
|
|
|
194
173
|
const { namespace, owned, ownedContext } = resolveManagedSessionRestorePolicy(options);
|
|
195
174
|
return owned ? { AGENT_BROWSER_NAMESPACE: ownedContext?.namespace ?? namespace ?? "" } : {};
|
|
196
175
|
}
|
|
176
|
+
const DEFAULT_AUTOSAVE_INTERVAL_MS = "30000";
|
|
177
|
+
const MAX_U64 = 18446744073709551615n;
|
|
178
|
+
export function resolveExplicitAutosaveInterval(value) {
|
|
179
|
+
if (value === undefined)
|
|
180
|
+
return undefined;
|
|
181
|
+
if (!/^\+?\d+$/.test(value))
|
|
182
|
+
return DEFAULT_AUTOSAVE_INTERVAL_MS;
|
|
183
|
+
try {
|
|
184
|
+
const parsed = BigInt(value);
|
|
185
|
+
return parsed <= MAX_U64 ? parsed.toString() : DEFAULT_AUTOSAVE_INTERVAL_MS;
|
|
186
|
+
}
|
|
187
|
+
catch {
|
|
188
|
+
return DEFAULT_AUTOSAVE_INTERVAL_MS;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
197
191
|
export function getOwnedManagedSessionCompatibilityEnv(options) {
|
|
198
192
|
const { owned, ownedContext } = resolveManagedSessionRestorePolicy(options);
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
193
|
+
if (!owned || !ownedContext)
|
|
194
|
+
return {};
|
|
195
|
+
const parentEnv = options.parentEnv ?? getAgentBrowserProcessEnvironment();
|
|
196
|
+
const callEnv = options.env ?? {};
|
|
197
|
+
const explicitRawInterval = Object.hasOwn(callEnv, "AGENT_BROWSER_AUTOSAVE_INTERVAL_MS")
|
|
198
|
+
? callEnv.AGENT_BROWSER_AUTOSAVE_INTERVAL_MS
|
|
199
|
+
: parentEnv.AGENT_BROWSER_AUTOSAVE_INTERVAL_MS;
|
|
200
|
+
const explicitIntervalMatches = resolveExplicitAutosaveInterval(explicitRawInterval) === ownedContext.headedManagedAutosaveInterval;
|
|
201
|
+
return {
|
|
202
|
+
...(ownedContext.compatibilityUserAgent ? { AGENT_BROWSER_USER_AGENT: ownedContext.compatibilityUserAgent } : {}),
|
|
203
|
+
...(ownedContext.headedManagedAutosaveInterval !== undefined && !explicitIntervalMatches ? { AGENT_BROWSER_AUTOSAVE_INTERVAL_MS: ownedContext.headedManagedAutosaveInterval } : {}),
|
|
204
|
+
};
|
|
202
205
|
}
|
|
203
206
|
export function shouldOmitOwnedManagedSessionRestoreEnv(options) {
|
|
204
207
|
return resolveManagedSessionRestorePolicy(options).owned && closesBrowserSession(options.args);
|
|
@@ -280,7 +283,7 @@ export function getManagedSessionRestoreProtectedEnv(options, restoreEnv) {
|
|
|
280
283
|
return {};
|
|
281
284
|
if (ownedContext?.protectedStorageEnv)
|
|
282
285
|
return { ...ownedContext.protectedStorageEnv };
|
|
283
|
-
const effectiveEnv = { ...(options.parentEnv ??
|
|
286
|
+
const effectiveEnv = { ...(options.parentEnv ?? getAgentBrowserProcessEnvironment()), ...options.env };
|
|
284
287
|
return getManagedSessionRestoreProtectedStorageEnv(true, effectiveEnv);
|
|
285
288
|
}
|
|
286
289
|
export function validateManagedSessionRestoreContextForSpawn(options) {
|
|
@@ -288,7 +291,7 @@ export function validateManagedSessionRestoreContextForSpawn(options) {
|
|
|
288
291
|
if (closesBrowserSession(options.args) || ownedContext?.restoreDecision !== "enabled")
|
|
289
292
|
return true;
|
|
290
293
|
const ownedCwd = ownedContext.cwd ?? options.cwd;
|
|
291
|
-
if (!ownedContext.restoreKey || createManagedSessionRestoreKey(ownedCwd) !== ownedContext.restoreKey || !hasManagedSessionRestoreProjectIdentity(ownedCwd))
|
|
294
|
+
if (!ownedContext.restoreKey || !ownedContext.restoreScope || createManagedSessionRestoreKey(ownedCwd, ownedContext.restoreScope) !== ownedContext.restoreKey || !hasManagedSessionRestoreProjectIdentity(ownedCwd))
|
|
292
295
|
return false;
|
|
293
296
|
const effectiveEnv = { ...parentEnv, ...options.env };
|
|
294
297
|
if (isDisabledEnvFlag(effectiveEnv[MANAGED_SESSION_RESTORE_ENV]))
|
|
@@ -315,7 +318,7 @@ export function getManagedSessionRestoreEnv(options) {
|
|
|
315
318
|
return {};
|
|
316
319
|
if (restoreState.isDisabled(sessionName, namespace) || !sessionName)
|
|
317
320
|
return {};
|
|
318
|
-
return { [AGENT_BROWSER_RESTORE_ENV]: createManagedSessionRestoreKey(options.cwd) };
|
|
321
|
+
return { [AGENT_BROWSER_RESTORE_ENV]: createManagedSessionRestoreKey(options.cwd, getManagedSessionRestoreScope(sessionName)) };
|
|
319
322
|
}
|
|
320
323
|
/** Commit sticky suppression only after an owned-context subprocess has actually started. */
|
|
321
324
|
export function commitManagedSessionRestoreSuppression(options) {
|
|
@@ -360,15 +363,19 @@ export function buildOwnedManagedSessionRestoreContext(options) {
|
|
|
360
363
|
const projectIdentityAvailable = !optedOut && hasManagedSessionRestoreProjectIdentity(ownedCwd);
|
|
361
364
|
const incompatible = !optedOut && isManagedSessionRestoreIncompatible(policyOptions);
|
|
362
365
|
const enabled = !optedOut && !incompatible;
|
|
363
|
-
const effectiveEnv = { ...(options.parentEnv ??
|
|
364
|
-
const
|
|
366
|
+
const effectiveEnv = { ...(options.parentEnv ?? getAgentBrowserProcessEnvironment()), ...options.env };
|
|
367
|
+
const restoreScope = getManagedSessionRestoreScope(owned.sessionName);
|
|
368
|
+
const restoreKey = projectIdentityAvailable ? createManagedSessionRestoreKey(ownedCwd, restoreScope) : undefined;
|
|
365
369
|
return {
|
|
366
370
|
...owned,
|
|
367
371
|
compatibilityUserAgent: options.compatibilityUserAgent,
|
|
372
|
+
headedManagedAutosaveDisabled: options.headedManagedAutosaveDisabled,
|
|
373
|
+
headedManagedAutosaveInterval: options.headedManagedAutosaveInterval,
|
|
368
374
|
expectedDaemonRestoreKey: enabled ? restoreKey : extractRequestedRestoreKey(options.args, owned.sessionName, effectiveEnv[AGENT_BROWSER_RESTORE_ENV]),
|
|
369
375
|
protectedStorageEnv: enabled ? getManagedSessionRestoreProtectedStorageEnv(true, effectiveEnv) : undefined,
|
|
370
376
|
restoreDecision: optedOut ? "opted-out" : incompatible ? "incompatible" : "enabled",
|
|
371
377
|
restoreKey,
|
|
378
|
+
restoreScope,
|
|
372
379
|
restoreSuppressed: optedOut || incompatible,
|
|
373
380
|
};
|
|
374
381
|
}
|
|
@@ -1,12 +1,7 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Purpose: Retain only wrapper-owned managed-restore snapshots after successful close operations.
|
|
3
|
-
* Responsibilities: Persist atomic per-snapshot ownership records, self-heal malformed records, and apply per-key plus superseded-generation retention without deleting unproven paths.
|
|
4
|
-
* Scope: Snapshot ownership and pruning only; restore eligibility and daemon policy live in sibling modules.
|
|
5
|
-
*/
|
|
6
1
|
import { createHash, randomUUID } from "node:crypto";
|
|
7
2
|
import { chmodSync, lstatSync, mkdirSync, readFileSync, readdirSync, realpathSync, renameSync, rmdirSync, unlinkSync, writeFileSync } from "node:fs";
|
|
8
3
|
import { basename, dirname, isAbsolute, join } from "node:path";
|
|
9
|
-
import { createManagedSessionRestoreKey, directoryContainsSymlink, ensureManagedSessionRestoreStorageIsSecure, ensureOwnerOnlyDirectory, getManagedRestoreSessionsDirectory,
|
|
4
|
+
import { createManagedSessionRestoreKey, directoryContainsSymlink, ensureManagedSessionRestoreStorageIsSecure, hasManagedSessionRestoreProjectIdentity, ensureOwnerOnlyDirectory, getManagedRestoreSessionsDirectory, isManagedSessionRestoreKey, resolveManagedSessionRestoreCheckoutRoot, resolveManagedSessionRestoreHome, } from "./managed-session-storage.js";
|
|
10
5
|
const OWNED_RESTORE_SNAPSHOT_FAMILIES_TO_KEEP = 2;
|
|
11
6
|
const OWNED_RESTORE_SNAPSHOT_MAX_RECORDS = 256;
|
|
12
7
|
const OWNED_RESTORE_SNAPSHOT_RECORD_MAX_BYTES = 16 * 1_024;
|
|
@@ -349,9 +344,7 @@ export function pruneOwnedManagedSessionRestoreSnapshots(options) {
|
|
|
349
344
|
if (!changed)
|
|
350
345
|
break;
|
|
351
346
|
}
|
|
352
|
-
const protectedRestoreKey =
|
|
353
|
-
? createManagedSessionRestoreKey(options.cwd)
|
|
354
|
-
: restoreKey;
|
|
347
|
+
const protectedRestoreKey = restoreKey;
|
|
355
348
|
if (!lineage)
|
|
356
349
|
return removed;
|
|
357
350
|
removed += pruneExpiredOtherRestoreKeys({
|
|
@@ -1,24 +1,23 @@
|
|
|
1
|
-
// Purpose: Prevent native tool calls from crossing wrapper-owned managed-state boundaries.
|
|
2
|
-
// Responsibilities: Guard destructive global state operations, foreign checkout restore/state references, and browser file access to local agent-browser state.
|
|
3
|
-
// Scope: Pre-spawn policy only; result redaction and managed snapshot retention live elsewhere.
|
|
4
1
|
import { realpathSync } from "node:fs";
|
|
5
2
|
import { basename, dirname, isAbsolute, resolve } from "node:path";
|
|
6
3
|
import { fileURLToPath } from "node:url";
|
|
7
4
|
import { parseArgvDescriptor } from "./argv-descriptor.js";
|
|
8
5
|
import { needsManagedSession } from "./command-policy.js";
|
|
9
6
|
import { getScreenshotPathTokenIndex } from "./orchestration/browser-run/artifact-paths.js";
|
|
10
|
-
import { parseBatchCommandArgument, parseUserBatchStdin } from "./orchestration/batch-stdin.js";
|
|
7
|
+
import { getUpstreamEffectiveBatchSteps, parseBatchCommandArgument, parseUserBatchStdin } from "./orchestration/batch-stdin.js";
|
|
11
8
|
import { isUnverifiedPageTransitionCommand } from "./command-taxonomy.js";
|
|
12
|
-
import { GLOBAL_BOOLEAN_FLAGS_WITH_OPTIONAL_VALUES, VALUE_FLAGS, extractExplicitSessionName, optionalGlobalValueFlagConsumesNext, } from "./argv-grammar.js";
|
|
9
|
+
import { GLOBAL_BOOLEAN_FLAGS_WITH_OPTIONAL_VALUES, VALUE_FLAGS, extractExplicitSessionName, isUpstreamEnvFlagEnabled, optionalGlobalValueFlagConsumesNext, } from "./argv-grammar.js";
|
|
13
10
|
import { extractManagedSessionRestoreKeys, isWrapperManagedSessionName } from "./managed-session-capabilities.js";
|
|
14
|
-
import {
|
|
15
|
-
import {
|
|
11
|
+
import { agentBrowserExplicitConfigIsPresent } from "./managed-session-restore.js";
|
|
12
|
+
import { hasManagedSessionRestoreProjectIdentity } from "./managed-session-storage.js";
|
|
13
|
+
import { getAgentBrowserProcessEnvironment } from "./process-environment.js";
|
|
16
14
|
const BLOCKED_GLOBAL_STATE_MESSAGE = "This operation could read or modify wrapper-owned browser state outside the current checkout. Use a caller-owned state name or path instead.";
|
|
17
15
|
const BLOCKED_MANAGED_BROWSER_FILE_MESSAGE = "Browser access to local .agent-browser storage is blocked because state files can contain authenticated cookies and storage. Use guarded state commands instead.";
|
|
18
16
|
const BLOCKED_MANAGED_SESSION_MESSAGE = "This session name is reserved for a browser managed by this extension instance. Use the current managed session or a caller-owned session name instead.";
|
|
19
17
|
const FILE_ACCESS_FLAG_MESSAGE = "Browser file-access enablement is blocked because a local page could read and exfiltrate authenticated .agent-browser state.";
|
|
20
|
-
const UPSTREAM_CONFIG_MESSAGE = "
|
|
18
|
+
const UPSTREAM_CONFIG_MESSAGE = "Explicit upstream agent-browser config from --config or AGENT_BROWSER_CONFIG is blocked for browser-backed native calls because it can load protected state, profiles, extensions, or file-access settings. Remove that override and pass safe settings explicitly. Passive agent-browser.json files are ignored through the wrapper's protected empty config.";
|
|
21
19
|
const UNVERIFIED_PAGE_MESSAGE = "The active page became unverified after a tab, attachment, history, script, or state-load transition. Run get url or navigate explicitly to a safe URL before page-content inspection.";
|
|
20
|
+
const BATCH_UNVERIFIED_PAGE_MESSAGE = `${UNVERIFIED_PAGE_MESSAGE} In a batch, put get url after the transition before later content steps, or split the batch at that boundary.`;
|
|
22
21
|
const UNSAFE_BATCH_ARGUMENT_MESSAGE = "Batch command arguments could not be safely inspected. Use batch stdin JSON command arrays instead.";
|
|
23
22
|
const NESTED_BATCH_ARGUMENT_MESSAGE = "Nested batch commands are blocked by the wrapper's page-state safety policy. Flatten the batch steps instead.";
|
|
24
23
|
const NON_BAIL_BATCH_NAVIGATION_MESSAGE = "Batches that navigate before page-content access must use exact batch --bail so a failed navigation cannot expose the prior page.";
|
|
@@ -62,7 +61,8 @@ function hasAgentBrowserPathComponent(value) {
|
|
|
62
61
|
function getFileUrlPath(value) {
|
|
63
62
|
if (!value)
|
|
64
63
|
return undefined;
|
|
65
|
-
const
|
|
64
|
+
const trimmedValue = value.replaceAll(/[\t\n\r]/g, "").trim();
|
|
65
|
+
const nestedFileUrl = /^(?:(?:blob|filesystem|view-source):)*(file:.*)$/i.exec(trimmedValue)?.[1];
|
|
66
66
|
if (!nestedFileUrl)
|
|
67
67
|
return undefined;
|
|
68
68
|
try {
|
|
@@ -73,7 +73,7 @@ function getFileUrlPath(value) {
|
|
|
73
73
|
return decodeUrlComponent(new URL(nestedFileUrl).pathname);
|
|
74
74
|
}
|
|
75
75
|
catch {
|
|
76
|
-
return
|
|
76
|
+
return trimmedValue;
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
}
|
|
@@ -151,7 +151,7 @@ function getPositionalOperands(commandTokens) {
|
|
|
151
151
|
}
|
|
152
152
|
function getExplicitNavigationTarget(args) {
|
|
153
153
|
const descriptor = parseArgvDescriptor(args);
|
|
154
|
-
const positionals = getPositionalOperands(descriptor.
|
|
154
|
+
const positionals = getPositionalOperands(descriptor.upstreamCommandTokens);
|
|
155
155
|
if (EXPLICIT_NAVIGATION_COMMANDS.has(descriptor.commandInfo.command ?? ""))
|
|
156
156
|
return positionals[0];
|
|
157
157
|
if (descriptor.commandInfo.command === "tab" && descriptor.commandInfo.subcommand === "new")
|
|
@@ -182,11 +182,11 @@ function getResultingExplicitNavigationTarget(args, currentPageUrl) {
|
|
|
182
182
|
function getBrowserContentUrlOperands(args) {
|
|
183
183
|
const descriptor = parseArgvDescriptor(args);
|
|
184
184
|
const command = descriptor.commandInfo.command;
|
|
185
|
-
const positionals = getPositionalOperands(descriptor.
|
|
185
|
+
const positionals = getPositionalOperands(descriptor.upstreamCommandTokens);
|
|
186
186
|
if (["a11y", "read", "vitals", "web-vitals"].includes(command ?? ""))
|
|
187
187
|
return positionals.slice(0, 1);
|
|
188
188
|
if (command === "auth" && descriptor.commandInfo.subcommand === "login")
|
|
189
|
-
return getFlagValues(descriptor.
|
|
189
|
+
return getFlagValues(descriptor.upstreamCommandTokens, "--url", true);
|
|
190
190
|
if (command === "diff" && descriptor.commandInfo.subcommand === "url")
|
|
191
191
|
return positionals.slice(1, 3);
|
|
192
192
|
if (command === "record" && ["start", "restart"].includes(descriptor.commandInfo.subcommand ?? ""))
|
|
@@ -224,7 +224,7 @@ function getBrowserFileOperands(args, env) {
|
|
|
224
224
|
const descriptor = parseArgvDescriptor(args);
|
|
225
225
|
const command = descriptor.commandInfo.command;
|
|
226
226
|
const subcommand = descriptor.commandInfo.subcommand;
|
|
227
|
-
const positionals = getPositionalOperands(descriptor.
|
|
227
|
+
const positionals = getPositionalOperands(descriptor.upstreamCommandTokens);
|
|
228
228
|
const values = [
|
|
229
229
|
getExplicitNavigationTarget(args),
|
|
230
230
|
...getBrowserContentUrlOperands(args),
|
|
@@ -241,30 +241,27 @@ function getBrowserFileOperands(args, env) {
|
|
|
241
241
|
if (command === "pdf")
|
|
242
242
|
values.push(positionals[0]);
|
|
243
243
|
if (command === "screenshot") {
|
|
244
|
-
const pathIndex = getScreenshotPathTokenIndex(descriptor.
|
|
245
|
-
values.push(pathIndex === undefined ? undefined : descriptor.
|
|
244
|
+
const pathIndex = getScreenshotPathTokenIndex(descriptor.upstreamCommandTokens);
|
|
245
|
+
values.push(pathIndex === undefined ? undefined : descriptor.upstreamCommandTokens[pathIndex]);
|
|
246
246
|
}
|
|
247
247
|
if (["profiler", "trace"].includes(command ?? "") && subcommand === "stop")
|
|
248
248
|
values.push(...positionals.slice(1));
|
|
249
249
|
if (command === "network" && subcommand === "har")
|
|
250
250
|
values.push(...positionals.slice(2));
|
|
251
251
|
if (command === "wait")
|
|
252
|
-
values.push(...getFlagValues(descriptor.
|
|
252
|
+
values.push(...getFlagValues(descriptor.upstreamCommandTokens, "--download", true), ...getFlagValues(descriptor.upstreamCommandTokens, "-d", true));
|
|
253
253
|
if (command === "cookies" && subcommand === "set")
|
|
254
|
-
values.push(...getFlagValues(descriptor.
|
|
254
|
+
values.push(...getFlagValues(descriptor.upstreamCommandTokens, "--curl", true));
|
|
255
255
|
if (command === "state" && ["load", "rename", "save", "show"].includes(subcommand ?? ""))
|
|
256
256
|
values.push(...positionals.slice(1));
|
|
257
257
|
if (command === "diff" && subcommand === "url")
|
|
258
258
|
values.push(...positionals.slice(1));
|
|
259
259
|
if (command === "diff")
|
|
260
|
-
values.push(...getFlagValues(descriptor.
|
|
260
|
+
values.push(...getFlagValues(descriptor.upstreamCommandTokens, "--baseline", true), ...getFlagValues(descriptor.upstreamCommandTokens, "--output", true), ...getFlagValues(descriptor.upstreamCommandTokens, "-o", true));
|
|
261
261
|
if (command === "record" && ["start", "restart"].includes(subcommand ?? ""))
|
|
262
262
|
values.push(...positionals.slice(1));
|
|
263
263
|
return values.filter((value) => typeof value === "string" && value.length > 0);
|
|
264
264
|
}
|
|
265
|
-
function isUpstreamEnvFlagEnabled(value) {
|
|
266
|
-
return value !== undefined && !["", "0", "false", "no"].includes(value.toLowerCase());
|
|
267
|
-
}
|
|
268
265
|
function rawBrowserArgsEnableFileAccess(args, env) {
|
|
269
266
|
return getRawBrowserArgsValues(args, env).some((value) => typeof value === "string" && /(?:^|[,\p{White_Space}])--(?:allow-file-access(?:-from-files)?|disable-web-security)(?=$|[=,\p{White_Space}])/iu.test(value));
|
|
270
267
|
}
|
|
@@ -312,13 +309,21 @@ function getManagedBrowserFileAccessValidationError(options) {
|
|
|
312
309
|
return BLOCKED_MANAGED_BROWSER_FILE_MESSAGE;
|
|
313
310
|
return undefined;
|
|
314
311
|
}
|
|
312
|
+
// Deliberately unions raw argv steps AND parseable stdin steps even though
|
|
313
|
+
// upstream executes argv steps exclusively when any exist: this pre-spawn
|
|
314
|
+
// validator must stay a superset of anything upstream could run, so it fails
|
|
315
|
+
// closed on unsafe content in either source. Stdin parse failures are fatal
|
|
316
|
+
// only when upstream would actually read stdin (no argv steps); ignored
|
|
317
|
+
// unparseable stdin cannot execute and must not reject a valid raw-argv call.
|
|
318
|
+
// The upstream-exact selection lives in getUpstreamEffectiveBatchSteps
|
|
319
|
+
// (batch-stdin.ts) for guard/folding paths.
|
|
315
320
|
function getBatchCommandSteps(args, stdin) {
|
|
316
321
|
const descriptor = parseArgvDescriptor(args);
|
|
317
322
|
if (descriptor.commandInfo.command !== "batch")
|
|
318
323
|
return { steps: [] };
|
|
319
324
|
const steps = [];
|
|
320
|
-
for (const command of descriptor.
|
|
321
|
-
if (command === "--bail"
|
|
325
|
+
for (const command of descriptor.upstreamCommandTokens.slice(1)) {
|
|
326
|
+
if (command === "--bail")
|
|
322
327
|
continue;
|
|
323
328
|
if (decodeUrlComponent(command).toLowerCase().includes(".agent-browser"))
|
|
324
329
|
return { error: BLOCKED_MANAGED_BROWSER_FILE_MESSAGE, steps: [] };
|
|
@@ -330,7 +335,7 @@ function getBatchCommandSteps(args, stdin) {
|
|
|
330
335
|
steps.push(parsed.step);
|
|
331
336
|
}
|
|
332
337
|
const parsedStdin = parseUserBatchStdin(stdin);
|
|
333
|
-
if (parsedStdin.error)
|
|
338
|
+
if (parsedStdin.error && steps.length === 0)
|
|
334
339
|
return { error: parsedStdin.error, steps: [] };
|
|
335
340
|
for (const step of parsedStdin.steps ?? []) {
|
|
336
341
|
if (parseArgvDescriptor(step).commandInfo.command === "batch")
|
|
@@ -339,9 +344,16 @@ function getBatchCommandSteps(args, stdin) {
|
|
|
339
344
|
}
|
|
340
345
|
return { steps };
|
|
341
346
|
}
|
|
347
|
+
export function invocationMayNavigateToLocalFile(args, stdin) {
|
|
348
|
+
if (isFileUrl(getResultingExplicitNavigationTarget(args)))
|
|
349
|
+
return true;
|
|
350
|
+
const descriptor = parseArgvDescriptor(args);
|
|
351
|
+
return getUpstreamEffectiveBatchSteps(descriptor.upstreamCommandTokens, stdin)
|
|
352
|
+
.some((step) => isFileUrl(getResultingExplicitNavigationTarget(step)));
|
|
353
|
+
}
|
|
342
354
|
function batchBailsOnFirstError(args) {
|
|
343
355
|
const descriptor = parseArgvDescriptor(args);
|
|
344
|
-
return descriptor.commandInfo.command === "batch" && descriptor.
|
|
356
|
+
return descriptor.commandInfo.command === "batch" && descriptor.upstreamCommandTokens.slice(1).includes("--bail");
|
|
345
357
|
}
|
|
346
358
|
function commandMayChangePageTarget(args, trustedBatchTabSelection) {
|
|
347
359
|
const descriptor = parseArgvDescriptor(args);
|
|
@@ -425,11 +437,11 @@ export function getCallerOwnedSessionLivePageVerificationRequirement(options) {
|
|
|
425
437
|
trustedFirstBatchTabSelection: options.trustedFirstBatchTabSelection,
|
|
426
438
|
trustedPinnedEmptyConfig: true,
|
|
427
439
|
});
|
|
428
|
-
return validationError === UNVERIFIED_PAGE_MESSAGE || validationError === NON_BAIL_BATCH_NAVIGATION_MESSAGE
|
|
440
|
+
return validationError === UNVERIFIED_PAGE_MESSAGE || validationError === BATCH_UNVERIFIED_PAGE_MESSAGE || validationError === NON_BAIL_BATCH_NAVIGATION_MESSAGE
|
|
429
441
|
? UNVERIFIED_PAGE_MESSAGE
|
|
430
442
|
: undefined;
|
|
431
443
|
}
|
|
432
|
-
export function getManagedSessionTargetAccessValidationError(args, ownedManagedSession, env =
|
|
444
|
+
export function getManagedSessionTargetAccessValidationError(args, ownedManagedSession, env = getAgentBrowserProcessEnvironment()) {
|
|
433
445
|
const sessionName = extractExplicitSessionName(args) ?? env.AGENT_BROWSER_SESSION;
|
|
434
446
|
return sessionName && isWrapperManagedSessionName(sessionName) && !ownedManagedSession
|
|
435
447
|
? BLOCKED_MANAGED_SESSION_MESSAGE
|
|
@@ -471,7 +483,7 @@ function getReferencedValues(args, cwd, env) {
|
|
|
471
483
|
env.AGENT_BROWSER_STATE,
|
|
472
484
|
].filter((value) => typeof value === "string" && value.length > 0);
|
|
473
485
|
if (descriptor.commandInfo.command === "state" && ["clear", "load", "rename", "save", "show"].includes(descriptor.commandInfo.subcommand ?? "")) {
|
|
474
|
-
values.push(...descriptor.
|
|
486
|
+
values.push(...descriptor.upstreamCommandTokens.slice(2));
|
|
475
487
|
}
|
|
476
488
|
for (const value of [...values]) {
|
|
477
489
|
const realPath = resolveExistingPath(cwd, value);
|
|
@@ -481,13 +493,13 @@ function getReferencedValues(args, cwd, env) {
|
|
|
481
493
|
return values;
|
|
482
494
|
}
|
|
483
495
|
export function getManagedSessionStateAccessValidationError(options) {
|
|
484
|
-
const effectiveEnv = { ...(options.parentEnv ??
|
|
496
|
+
const effectiveEnv = { ...(options.parentEnv ?? getAgentBrowserProcessEnvironment()), ...options.env };
|
|
485
497
|
const descriptor = parseArgvDescriptor(options.args);
|
|
486
498
|
const command = descriptor.commandInfo.command;
|
|
487
499
|
const subcommand = descriptor.commandInfo.subcommand;
|
|
488
500
|
if (["close", "exit", "quit"].includes(command ?? ""))
|
|
489
501
|
return undefined;
|
|
490
|
-
if (!options.trustedPinnedEmptyConfig && needsManagedSession(descriptor) &&
|
|
502
|
+
if (!options.trustedPinnedEmptyConfig && needsManagedSession(descriptor) && agentBrowserExplicitConfigIsPresent(effectiveEnv, options.args))
|
|
491
503
|
return UPSTREAM_CONFIG_MESSAGE;
|
|
492
504
|
if (command === "batch") {
|
|
493
505
|
const batch = getBatchCommandSteps(options.args, options.stdin);
|
|
@@ -526,7 +538,7 @@ export function getManagedSessionStateAccessValidationError(options) {
|
|
|
526
538
|
}
|
|
527
539
|
}
|
|
528
540
|
if (directError)
|
|
529
|
-
return directError;
|
|
541
|
+
return directError === UNVERIFIED_PAGE_MESSAGE ? BATCH_UNVERIFIED_PAGE_MESSAGE : directError;
|
|
530
542
|
if (failedNavigationHazard)
|
|
531
543
|
return NON_BAIL_BATCH_NAVIGATION_MESSAGE;
|
|
532
544
|
const mayChangePageTarget = commandMayChangePageTarget(step, trustedBatchTabSelection);
|
|
@@ -568,8 +580,8 @@ export function getManagedSessionStateAccessValidationError(options) {
|
|
|
568
580
|
if (command === "state" && subcommand === "clean")
|
|
569
581
|
return BLOCKED_GLOBAL_STATE_MESSAGE;
|
|
570
582
|
if (command === "state" && subcommand === "clear") {
|
|
571
|
-
const target = descriptor.
|
|
572
|
-
if (!target || descriptor.
|
|
583
|
+
const target = descriptor.upstreamCommandTokens.slice(2).find((token) => !token.startsWith("-"));
|
|
584
|
+
if (!target || descriptor.upstreamCommandTokens.includes("--all") || descriptor.upstreamCommandTokens.includes("-a") || isWrapperManagedSessionName(target)) {
|
|
573
585
|
return BLOCKED_GLOBAL_STATE_MESSAGE;
|
|
574
586
|
}
|
|
575
587
|
}
|
|
@@ -582,8 +594,8 @@ export function getManagedSessionStateAccessValidationError(options) {
|
|
|
582
594
|
return undefined;
|
|
583
595
|
if (command === "state" && ["rename", "save"].includes(subcommand ?? ""))
|
|
584
596
|
return BLOCKED_GLOBAL_STATE_MESSAGE;
|
|
585
|
-
if (!hasManagedSessionRestoreProjectIdentity(options.cwd))
|
|
597
|
+
if (!hasManagedSessionRestoreProjectIdentity(options.cwd) || !options.managedSessionRestoreKey)
|
|
586
598
|
return BLOCKED_GLOBAL_STATE_MESSAGE;
|
|
587
|
-
const currentKey =
|
|
599
|
+
const currentKey = options.managedSessionRestoreKey.toLowerCase();
|
|
588
600
|
return referencedKeys.every((key) => key === currentKey) ? undefined : BLOCKED_GLOBAL_STATE_MESSAGE;
|
|
589
601
|
}
|