pi-agent-browser-native 0.5.0 → 0.6.6

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.
Files changed (64) hide show
  1. package/CHANGELOG.md +144 -0
  2. package/README.md +75 -42
  3. package/dist/extensions/agent-browser/index.js +13 -83
  4. package/dist/extensions/agent-browser/lib/argv-grammar.js +8 -2
  5. package/dist/extensions/agent-browser/lib/batch-lifecycle.js +1 -1
  6. package/dist/extensions/agent-browser/lib/command-policy.js +4 -7
  7. package/dist/extensions/agent-browser/lib/command-taxonomy.js +19 -11
  8. package/dist/extensions/agent-browser/lib/config-policy.js +25 -1
  9. package/dist/extensions/agent-browser/lib/config.js +1 -1
  10. package/dist/extensions/agent-browser/lib/input-modes/job.js +0 -9
  11. package/dist/extensions/agent-browser/lib/input-modes/params.js +1 -1
  12. package/dist/extensions/agent-browser/lib/launch-scoped-flags.js +18 -4
  13. package/dist/extensions/agent-browser/lib/managed-session-policy-lock.js +3 -138
  14. package/dist/extensions/agent-browser/lib/managed-session-restore.js +1 -81
  15. package/dist/extensions/agent-browser/lib/managed-session-storage.js +4 -1
  16. package/dist/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.js +31 -26
  17. package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result.js +60 -8
  18. package/dist/extensions/agent-browser/lib/orchestration/browser-run/managed-session-daemon-policy.js +1 -4
  19. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/snapshot-filter.js +119 -2
  20. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/wait-timeouts.js +7 -6
  21. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare.js +31 -40
  22. package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output.js +76 -70
  23. package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-state.js +6 -8
  24. package/dist/extensions/agent-browser/lib/orchestration/electron-host/index.js +5 -10
  25. package/dist/extensions/agent-browser/lib/orchestration/output-file.js +41 -21
  26. package/dist/extensions/agent-browser/lib/page-target-validation.js +270 -0
  27. package/dist/extensions/agent-browser/lib/playbook.js +13 -12
  28. package/dist/extensions/agent-browser/lib/process-identity.js +11 -10
  29. package/dist/extensions/agent-browser/lib/process.js +18 -82
  30. package/dist/extensions/agent-browser/lib/recording-reservations.js +11 -78
  31. package/dist/extensions/agent-browser/lib/results/action-recommendations.js +1 -1
  32. package/dist/extensions/agent-browser/lib/results/presentation/batch.js +27 -14
  33. package/dist/extensions/agent-browser/lib/results/presentation/common.js +20 -2
  34. package/dist/extensions/agent-browser/lib/results/presentation/diagnostics.js +11 -16
  35. package/dist/extensions/agent-browser/lib/results/presentation/navigation.js +12 -9
  36. package/dist/extensions/agent-browser/lib/results/presentation/registry.js +2 -2
  37. package/dist/extensions/agent-browser/lib/results/presentation.js +31 -4
  38. package/dist/extensions/agent-browser/lib/results/recovery-actions.js +1 -1
  39. package/dist/extensions/agent-browser/lib/results/recovery-next-actions.js +9 -0
  40. package/dist/extensions/agent-browser/lib/results/selector-recovery.js +3 -3
  41. package/dist/extensions/agent-browser/lib/results/snapshot-spill.js +2 -1
  42. package/dist/extensions/agent-browser/lib/results/snapshot.js +4 -4
  43. package/dist/extensions/agent-browser/lib/runtime.js +73 -72
  44. package/dist/extensions/agent-browser/lib/session-page-state.js +12 -3
  45. package/dist/extensions/agent-browser/lib/temp.js +1 -2
  46. package/dist/extensions/agent-browser/lib/upstream-version.js +5 -5
  47. package/dist/extensions/agent-browser/lib/web-search.js +108 -24
  48. package/dist/scripts/agent-browser-target.mjs +19 -1
  49. package/docs/ARCHITECTURE.md +24 -20
  50. package/docs/COMMAND_REFERENCE.md +181 -49
  51. package/docs/ELECTRON.md +2 -2
  52. package/docs/RELEASE.md +10 -8
  53. package/docs/REQUIREMENTS.md +8 -7
  54. package/docs/SUPPORT_MATRIX.md +31 -26
  55. package/docs/TOOL_CONTRACT.md +89 -56
  56. package/package.json +1 -1
  57. package/scripts/agent-browser-capability-baseline.mjs +65 -5
  58. package/scripts/agent-browser-target.mjs +19 -1
  59. package/scripts/config.mjs +1 -0
  60. package/scripts/doctor.mjs +15 -9
  61. package/dist/extensions/agent-browser/lib/managed-session-capabilities.js +0 -20
  62. package/dist/extensions/agent-browser/lib/managed-session-state-policy.js +0 -601
  63. package/dist/extensions/agent-browser/lib/navigation-policy.js +0 -78
  64. package/dist/extensions/agent-browser/lib/results/presentation/managed-list-filter.js +0 -37
@@ -50,9 +50,6 @@ function getPolicyLockDigest(sessionName, namespace) {
50
50
  export function getManagedSessionPolicyLockPath(sessionName, namespace) {
51
51
  return join(getCoordinationDirectory(), `.pi-agent-browser-policy-${getPolicyLockDigest(sessionName, namespace)}.lock-v3`);
52
52
  }
53
- export function getLegacyManagedSessionPolicyLockPath(sessionName, namespace) {
54
- return join(getCoordinationDirectory(), `.pi-agent-browser-policy-${getPolicyLockDigest(sessionName, namespace)}.lock-v2`);
55
- }
56
53
  function parseOwner(content) {
57
54
  if (Buffer.byteLength(content) > POLICY_LOCK_MAX_BYTES)
58
55
  return undefined;
@@ -69,22 +66,6 @@ function parseOwner(content) {
69
66
  return undefined;
70
67
  }
71
68
  }
72
- function parseLegacyBridgeOwner(content) {
73
- if (Buffer.byteLength(content) > POLICY_LOCK_MAX_BYTES)
74
- return undefined;
75
- try {
76
- const parsed = JSON.parse(content);
77
- return (parsed.version === 2 || parsed.version === 3)
78
- && Number.isSafeInteger(parsed.pid) && (parsed.pid ?? 0) > 0
79
- && typeof parsed.startIdentity === "string" && parsed.startIdentity.length > 0
80
- && typeof parsed.token === "string" && parsed.token.length > 0
81
- ? parsed
82
- : undefined;
83
- }
84
- catch {
85
- return undefined;
86
- }
87
- }
88
69
  function parseTicket(content, token) {
89
70
  if (Buffer.byteLength(content) > POLICY_LOCK_MAX_BYTES)
90
71
  return undefined;
@@ -139,26 +120,6 @@ async function readClaim(path) {
139
120
  return undefined;
140
121
  }
141
122
  }
142
- async function readLegacyBridgeOwner(path) {
143
- try {
144
- const directory = await lstat(path);
145
- const ownerPath = join(path, LOCK_OWNER_FILE);
146
- const ownerEntry = await lstat(ownerPath);
147
- if (!directory.isDirectory() || directory.isSymbolicLink() || !ownerEntry.isFile() || ownerEntry.isSymbolicLink())
148
- return undefined;
149
- if (ownerEntry.size > POLICY_LOCK_MAX_BYTES)
150
- return undefined;
151
- if (process.platform !== "win32") {
152
- const uid = typeof process.getuid === "function" ? process.getuid() : undefined;
153
- if (uid === undefined || directory.uid !== uid || ownerEntry.uid !== uid || (directory.mode & 0o077) !== 0 || (ownerEntry.mode & 0o177) !== 0)
154
- return undefined;
155
- }
156
- return parseLegacyBridgeOwner(await readFile(ownerPath, "utf8"));
157
- }
158
- catch {
159
- return undefined;
160
- }
161
- }
162
123
  async function readClaims(basePath) {
163
124
  const directory = dirname(basePath);
164
125
  const prefix = `${basename(basePath)}.claim-`;
@@ -225,28 +186,6 @@ async function removeClaimOwnedBy(path, token) {
225
186
  await rm(movedPath, { force: true, recursive: true });
226
187
  return true;
227
188
  }
228
- async function removeLegacyBridgeOwnedBy(path, token) {
229
- const current = await readLegacyBridgeOwner(path);
230
- if (current?.version !== 3 || current.token !== token)
231
- return false;
232
- const movedPath = join(dirname(path), `.pi-agent-browser-policy-bridge-remove-${token}-${randomUUID()}`);
233
- try {
234
- await rename(path, movedPath);
235
- }
236
- catch (error) {
237
- return error.code === "ENOENT";
238
- }
239
- const moved = await readLegacyBridgeOwner(movedPath);
240
- if (moved?.version !== 3 || moved.token !== token) {
241
- try {
242
- await rename(movedPath, path);
243
- }
244
- catch { }
245
- return false;
246
- }
247
- await rm(movedPath, { force: true, recursive: true });
248
- return true;
249
- }
250
189
  async function cleanDeadPolicyArtifacts(directory) {
251
190
  let names;
252
191
  try {
@@ -256,72 +195,13 @@ async function cleanDeadPolicyArtifacts(directory) {
256
195
  return;
257
196
  }
258
197
  for (const name of names.filter((candidate) => candidate.startsWith(".pi-agent-browser-policy-remove-")
259
- || candidate.startsWith(".pi-agent-browser-policy-bridge-remove-")
260
- || candidate.includes(".lock-v2.bridge-candidate-")
261
- || candidate.includes(".lock-v2.candidate-")
262
198
  || candidate.includes(".lock-v3.candidate-"))) {
263
199
  const path = join(directory, name);
264
- const owner = await readLegacyBridgeOwner(path);
265
- if (owner && await ownerAlive(owner) === false)
200
+ const claim = await readClaim(path);
201
+ if (claim && await ownerAlive(claim.owner) === false)
266
202
  await rm(path, { force: true, recursive: true }).catch(() => undefined);
267
203
  }
268
204
  }
269
- async function hasLegacyV2Contender(path) {
270
- try {
271
- const prefix = `${basename(path)}.candidate-`;
272
- return (await readdir(dirname(path))).some((name) => name.startsWith(prefix));
273
- }
274
- catch {
275
- return undefined;
276
- }
277
- }
278
- async function acquireLegacyPolicyBridge(options) {
279
- const path = getLegacyManagedSessionPolicyLockPath(options.sessionName, options.namespace);
280
- const candidatePath = `${path}.bridge-candidate-${options.owner.token}`;
281
- try {
282
- await mkdir(candidatePath, { mode: 0o700 });
283
- await writeFile(join(candidatePath, LOCK_OWNER_FILE), JSON.stringify(options.owner), { encoding: "utf8", flag: "wx", mode: 0o600 });
284
- while (!options.signal?.aborted) {
285
- const legacyContender = await hasLegacyV2Contender(path);
286
- if (legacyContender === undefined)
287
- return undefined;
288
- if (legacyContender) {
289
- if (Date.now() >= options.deadline)
290
- return undefined;
291
- await waitForRetry(options.signal);
292
- continue;
293
- }
294
- try {
295
- await rename(candidatePath, path);
296
- const installed = await readLegacyBridgeOwner(path);
297
- return installed?.version === 3 && installed.token === options.owner.token
298
- ? { release: async () => { await removeLegacyBridgeOwnedBy(path, options.owner.token); } }
299
- : undefined;
300
- }
301
- catch (error) {
302
- if (!["EACCES", "EEXIST", "ENOTEMPTY", "EPERM"].includes(error.code ?? ""))
303
- return undefined;
304
- }
305
- const observed = await readLegacyBridgeOwner(path);
306
- if (!observed)
307
- return undefined;
308
- if (observed.version === 3 && await ownerAlive(observed) === false) {
309
- if (await removeLegacyBridgeOwnedBy(path, observed.token))
310
- continue;
311
- }
312
- if (Date.now() >= options.deadline)
313
- return undefined;
314
- await waitForRetry(options.signal);
315
- }
316
- return undefined;
317
- }
318
- catch {
319
- return undefined;
320
- }
321
- finally {
322
- await rm(candidatePath, { force: true, recursive: true }).catch(() => undefined);
323
- }
324
- }
325
205
  function claimPrecedes(left, right) {
326
206
  if (left.ticket === null)
327
207
  return true;
@@ -358,7 +238,6 @@ export async function acquireManagedSessionPolicyLock(options) {
358
238
  const candidatePath = `${basePath}.candidate-${token}`;
359
239
  const claimPath = `${basePath}.claim-${token}`;
360
240
  let claimPublished = false;
361
- let legacyBridge;
362
241
  let lockAcquired = false;
363
242
  try {
364
243
  await mkdir(candidatePath, { mode: 0o700 });
@@ -397,20 +276,8 @@ export async function acquireManagedSessionPolicyLock(options) {
397
276
  }
398
277
  if (!blocked) {
399
278
  await cleanDeadPolicyArtifacts(directory);
400
- legacyBridge = await acquireLegacyPolicyBridge({
401
- deadline,
402
- owner,
403
- signal: options.signal,
404
- sessionName: options.sessionName,
405
- namespace: options.namespace,
406
- });
407
- if (!legacyBridge)
408
- return undefined;
409
279
  lockAcquired = true;
410
- return { release: async () => {
411
- await legacyBridge?.release();
412
- await removeClaimOwnedBy(claimPath, token);
413
- } };
280
+ return { release: async () => { await removeClaimOwnedBy(claimPath, token); } };
414
281
  }
415
282
  if (Date.now() >= deadline)
416
283
  return undefined;
@@ -423,8 +290,6 @@ export async function acquireManagedSessionPolicyLock(options) {
423
290
  }
424
291
  finally {
425
292
  await rm(candidatePath, { force: true, recursive: true }).catch(() => undefined);
426
- if (!lockAcquired)
427
- await legacyBridge?.release();
428
293
  if (claimPublished && !lockAcquired)
429
294
  await removeClaimOwnedBy(claimPath, token);
430
295
  }
@@ -1,23 +1,17 @@
1
1
  import { AsyncLocalStorage } from "node:async_hooks";
2
- import { chmodSync, lstatSync, readFileSync, unlinkSync } from "node:fs";
3
2
  import { extractUpstreamCommandTokens, parseCommandInfo } from "./argv-descriptor.js";
4
3
  import { canonicalizeAgentBrowserNamespace, extractExplicitNamespace, extractExplicitSessionName, extractRequestedRestoreKey, getAgentBrowserSessionIdentityKey, isUpstreamEnvFlagEnabled, scanUpstreamGlobalFlagOccurrences, } from "./argv-grammar.js";
5
4
  import { hasLaunchScopedFlagToken, MANAGED_RESTORE_INCOMPATIBLE_BOOLEAN_ENVS, MANAGED_RESTORE_INCOMPATIBLE_ENVS, MANAGED_RESTORE_INCOMPATIBLE_FLAGS, } from "./launch-scoped-flags.js";
6
5
  import { createManagedSessionRestoreKey, ensureManagedSessionRestoreStorageIsSecure, getManagedSessionRestoreScope, getManagedSessionRestoreProtectedStorageEnv, hasManagedSessionRestoreProjectIdentity, resolveManagedSessionRestoreHome, } from "./managed-session-storage.js";
7
6
  import { parseUserBatchStdin } from "./orchestration/batch-stdin.js";
8
7
  import { getAgentBrowserProcessEnvironment } from "./process-environment.js";
9
- import { writeSecureTempFile } from "./temp.js";
10
8
  export { createManagedSessionRestoreKey, ensureManagedSessionRestoreStorageIsSecure, getManagedSessionRestoreScope } from "./managed-session-storage.js";
11
9
  export { pruneOwnedManagedSessionRestoreSnapshots } from "./managed-session-snapshots.js";
12
10
  const AGENT_BROWSER_CONFIG_ENV = "AGENT_BROWSER_CONFIG";
13
11
  const AGENT_BROWSER_RESTORE_ENV = "AGENT_BROWSER_RESTORE";
14
12
  const MANAGED_SESSION_RESTORE_ENV = "PI_AGENT_BROWSER_MANAGED_SESSION_RESTORE";
15
13
  export const MANAGED_SESSION_NAME_PREFIX = "piab-";
16
- const MANAGED_SESSION_RESTORE_EMPTY_CONFIG_CONTENT = "{}\n";
17
- const MANAGED_SESSION_RESTORE_EMPTY_CONFIG_NAME = ".pi-agent-browser-managed-restore-config-v1.json";
18
14
  const MANAGED_SESSION_RESTORE_SPAWN_PINNED_ENVS = new Set([AGENT_BROWSER_CONFIG_ENV, AGENT_BROWSER_RESTORE_ENV, "AGENT_BROWSER_NAMESPACE"]);
19
- let managedSessionRestoreEmptyConfigPath;
20
- let managedSessionRestoreEmptyConfigPromise;
21
15
  function isDisabledEnvFlag(value) {
22
16
  if (value === undefined)
23
17
  return false;
@@ -108,7 +102,7 @@ function closesBrowserSession(args) {
108
102
  export function agentBrowserExplicitConfigIsPresent(parentEnv = getAgentBrowserProcessEnvironment(), args = []) {
109
103
  return hasExplicitConfigArg(args) || hasUpstreamEnvValue(parentEnv, AGENT_BROWSER_CONFIG_ENV);
110
104
  }
111
- /** Explicit config overrides disable restore; passive files are ignored because every browser-backed subprocess pins a protected empty config. */
105
+ /** Caller-selected upstream config disables the wrapper's automatic restore injection without blocking that config. */
112
106
  export function agentBrowserConfigBlocksManagedRestore(_cwd, parentEnv = getAgentBrowserProcessEnvironment(), args = [], platform = process.platform) {
113
107
  return !resolveManagedSessionRestoreHome(parentEnv, platform) || agentBrowserExplicitConfigIsPresent(parentEnv, args);
114
108
  }
@@ -203,80 +197,6 @@ export function getOwnedManagedSessionCompatibilityEnv(options) {
203
197
  ...(ownedContext.headedManagedAutosaveInterval !== undefined && !explicitIntervalMatches ? { AGENT_BROWSER_AUTOSAVE_INTERVAL_MS: ownedContext.headedManagedAutosaveInterval } : {}),
204
198
  };
205
199
  }
206
- export function shouldOmitOwnedManagedSessionRestoreEnv(options) {
207
- return resolveManagedSessionRestorePolicy(options).owned && closesBrowserSession(options.args);
208
- }
209
- export function canonicalizeOwnedManagedSessionCloseArgs(options, force = false) {
210
- const policy = resolveManagedSessionRestorePolicy(options);
211
- if (!policy.owned || !closesBrowserSession(options.args))
212
- return options.args;
213
- const sessionName = policy.ownedContext?.sessionName ?? policy.sessionName;
214
- if (!sessionName)
215
- return options.args;
216
- const namespace = canonicalizeAgentBrowserNamespace(policy.ownedContext?.namespace ?? policy.namespace) ?? "";
217
- const command = options.args.at(-1);
218
- const prefix = options.args.slice(0, -1);
219
- const safePrefixes = [
220
- ["--session", sessionName],
221
- ["--json", "--session", sessionName],
222
- ["--namespace", namespace, "--session", sessionName],
223
- ["--json", "--namespace", namespace, "--session", sessionName],
224
- ];
225
- if (!force && command && ["close", "exit", "quit"].includes(command)
226
- && safePrefixes.some((candidate) => candidate.length === prefix.length && candidate.every((token, index) => token === prefix[index]))) {
227
- return options.args;
228
- }
229
- return ["--json", "--namespace", namespace, "--session", sessionName, "close"];
230
- }
231
- export function cleanupManagedSessionRestoreConfig() {
232
- if (managedSessionRestoreEmptyConfigPath) {
233
- try {
234
- unlinkSync(managedSessionRestoreEmptyConfigPath);
235
- }
236
- catch { }
237
- }
238
- managedSessionRestoreEmptyConfigPath = undefined;
239
- managedSessionRestoreEmptyConfigPromise = undefined;
240
- }
241
- async function ensureManagedSessionRestoreEmptyConfig(platform) {
242
- for (let attempt = 0; attempt < 2; attempt += 1) {
243
- try {
244
- managedSessionRestoreEmptyConfigPromise ??= writeSecureTempFile({
245
- content: MANAGED_SESSION_RESTORE_EMPTY_CONFIG_CONTENT,
246
- prefix: MANAGED_SESSION_RESTORE_EMPTY_CONFIG_NAME.replace(/\.json$/, ""),
247
- suffix: ".json",
248
- }).then((path) => {
249
- if (platform !== "win32")
250
- chmodSync(path, 0o400);
251
- managedSessionRestoreEmptyConfigPath = path;
252
- return path;
253
- });
254
- const path = await managedSessionRestoreEmptyConfigPromise;
255
- let entry = lstatSync(path);
256
- if (entry.isSymbolicLink() || !entry.isFile())
257
- throw new Error("Managed restore config is not a regular file.");
258
- if (platform !== "win32" && (entry.mode & 0o777) !== 0o400) {
259
- chmodSync(path, 0o400);
260
- entry = lstatSync(path);
261
- }
262
- if (entry.isSymbolicLink() || !entry.isFile() || (platform !== "win32" && (entry.mode & 0o777) !== 0o400))
263
- throw new Error("Managed restore config permissions are unsafe.");
264
- if (readFileSync(path, "utf8") !== MANAGED_SESSION_RESTORE_EMPTY_CONFIG_CONTENT)
265
- throw new Error("Managed restore config content changed.");
266
- return path;
267
- }
268
- catch {
269
- cleanupManagedSessionRestoreConfig();
270
- }
271
- }
272
- return undefined;
273
- }
274
- export async function getManagedSessionRestoreConfigEnv(restoreEnv, pinForOwnedClose = false) {
275
- if (restoreEnv[AGENT_BROWSER_RESTORE_ENV] === undefined && !pinForOwnedClose)
276
- return {};
277
- const path = await ensureManagedSessionRestoreEmptyConfig(process.platform);
278
- return path ? { [AGENT_BROWSER_CONFIG_ENV]: path } : undefined;
279
- }
280
200
  export function getManagedSessionRestoreProtectedEnv(options, restoreEnv) {
281
201
  const { ownedContext } = resolveManagedSessionRestorePolicy(options);
282
202
  if (restoreEnv[AGENT_BROWSER_RESTORE_ENV] === undefined)
@@ -3,8 +3,11 @@ import { linkSync, lstatSync, mkdirSync, readFileSync, readdirSync, realpathSync
3
3
  import { homedir } from "node:os";
4
4
  import { dirname, isAbsolute, join, parse, resolve, win32 } from "node:path";
5
5
  import { canonicalizeAgentBrowserNamespace } from "./argv-grammar.js";
6
- export { isManagedSessionRestoreKey } from "./managed-session-capabilities.js";
6
+ const MANAGED_SESSION_RESTORE_KEY_PATTERN = /^piab-r2-[a-f\d]{32}$/i;
7
7
  const MANAGED_SESSION_NAME_PREFIX = "piab-r2-";
8
+ export function isManagedSessionRestoreKey(value) {
9
+ return typeof value === "string" && MANAGED_SESSION_RESTORE_KEY_PATTERN.test(value);
10
+ }
8
11
  const MANAGED_SESSION_FRESH_SUFFIX_PATTERN = /-fresh-[a-f\d]{10}$/i;
9
12
  const MANAGED_SESSION_RESTORE_KEY_HASH_LENGTH = 32;
10
13
  const PROJECT_GENERATION_MARKER_NAME = "pi-agent-browser-project-generation-v1.json";
@@ -3,14 +3,12 @@ import { isAbsolute, resolve } from "node:path";
3
3
  import { isCloseCommand, isOpenNavigationCommand } from "../../command-taxonomy.js";
4
4
  import { boundElectronProbeString } from "../../electron/cdp.js";
5
5
  import { executableExistsOnPath } from "../../executable-path.js";
6
- import { isHttpOrHttpsUrl } from "../../input-modes/job.js";
7
6
  import { formatSessionArtifactRetentionSummary } from "../../results/artifact-manifest.js";
8
7
  import { buildNextToolAction, withOptionalSessionArgs } from "../../results/next-actions.js";
9
8
  import { buildVisibleRefFallbackDiagnosticFromSnapshot, getVisibleRefFallbackTarget } from "../../results/selector-recovery.js";
10
9
  import { extractRefSnapshotFromData, isAboutBlankUrl, normalizeComparableUrl } from "../../session-page-state.js";
11
10
  import { extractUpstreamCommandTokens, parseWaitCommandTokens, redactInvocationArgs, redactSensitiveText } from "../../runtime.js";
12
11
  import { isRecord } from "../../parsing.js";
13
- import { getManagedSessionStateAccessValidationError, isFileUrl } from "../../managed-session-state-policy.js";
14
12
  import { extractBatchResultCommand, extractNavigationSummaryFromData, extractStringResultField, findElectronLaunchRecordForSession, runSessionCommandData, } from "./session-state.js";
15
13
  import { parseValidBatchStepEntries } from "../batch-stdin.js";
16
14
  import { getScreenshotPathTokenIndex } from "./artifact-paths.js";
@@ -22,16 +20,17 @@ export async function collectNavigationSummary(options) {
22
20
  const url = extractStringResultField(await runSessionCommandData({ args: ["get", "url"], cwd: options.cwd, namespace: options.namespace, sessionName: options.sessionName, signal: options.signal }), "url");
23
21
  if (!url || !/^[a-z][a-z0-9+.-]*:/i.test(url))
24
22
  return undefined;
25
- if (isFileUrl(url) || isAboutBlankUrl(url))
26
- return { url };
23
+ const urlChanged = options.priorTarget?.url ? normalizeComparableUrl(options.priorTarget.url) !== normalizeComparableUrl(url) : undefined;
24
+ if (isAboutBlankUrl(url))
25
+ return { url, ...(urlChanged !== undefined ? { urlChanged } : {}) };
27
26
  // Reuse the title already observed for this exact URL instead of spending a second probe. Titles can
28
27
  // change without a URL change on SPAs, but this summary is only a "last observed" page label; the URL
29
28
  // stays live-probed on every call.
30
- if (options.priorTarget?.title && normalizeComparableUrl(options.priorTarget.url) === normalizeComparableUrl(url)) {
31
- return { title: options.priorTarget.title, url };
29
+ if (options.reusePriorTitle !== false && options.priorTarget?.title && normalizeComparableUrl(options.priorTarget.url) === normalizeComparableUrl(url)) {
30
+ return { title: options.priorTarget.title, url, urlChanged: false };
32
31
  }
33
32
  const title = extractStringResultField(await runSessionCommandData({ args: ["get", "title"], cwd: options.cwd, namespace: options.namespace, sessionName: options.sessionName, signal: options.signal }), "title");
34
- return { title, url };
33
+ return { title, url, ...(urlChanged !== undefined ? { urlChanged } : {}) };
35
34
  }
36
35
  function extractScrollPositionSnapshot(data) {
37
36
  const result = isRecord(data) && isRecord(data.result) ? data.result : data;
@@ -89,6 +88,21 @@ function sameScrollPositionSnapshot(left, right) {
89
88
  return other?.id === container.id && other.scrollTop === container.scrollTop && other.scrollLeft === container.scrollLeft;
90
89
  });
91
90
  }
91
+ export function buildUnsupportedScrollIntoViewRecovery(options) {
92
+ if (!["scrollintoview", "scrollinto"].includes(options.commandTokens[0] ?? "") || options.commandTokens.some((token) => token === "--help" || token === "-h"))
93
+ return undefined;
94
+ const match = /^text=(.+)$/s.exec(options.commandTokens[1] ?? "");
95
+ if (!match)
96
+ return undefined;
97
+ const text = redactSensitiveText(match[1]);
98
+ return {
99
+ error: "scrollintoview accepts a CSS selector, xpath=..., or a current @e… ref; text=... is not supported and can falsely report success without moving the page.",
100
+ nextActions: [
101
+ { id: "scroll-semantic-text-target", params: { args: withOptionalSessionArgs(options.sessionName, ["find", "text", text, "hover"]) }, reason: "Use the upstream semantic text locator; hover resolves and scrolls the matched element into view.", safety: "Hover may open a tooltip or menu. Use the snapshot/ref recovery instead when hover state could affect the workflow.", tool: "agent_browser" },
102
+ { id: "refresh-refs-for-scroll-target", params: { args: withOptionalSessionArgs(options.sessionName, ["snapshot", "-i"]) }, reason: "Capture a current element ref, then retry scrollintoview with that @e… ref.", safety: "Read-only snapshot; choose the intended current ref before retrying the scroll.", tool: "agent_browser" },
103
+ ],
104
+ };
105
+ }
92
106
  export function buildScrollNoopDiagnostic(before, after) {
93
107
  if (!before || !after || !sameScrollPositionSnapshot(before, after))
94
108
  return undefined;
@@ -531,14 +545,11 @@ async function collectElectronManagedSessionUrl(options) {
531
545
  export async function collectElectronManagedSessionTarget(options) {
532
546
  if (!options.sessionName)
533
547
  return undefined;
534
- const urlResult = await collectManagedSessionCommandData({ allowManagedSessionTarget: options.allowManagedSessionTarget, args: ["get", "url"], cwd: options.cwd, namespace: options.namespace, sessionName: options.sessionName, signal: options.signal, timeoutMs: options.timeoutMs });
548
+ const urlResult = await collectManagedSessionCommandData({ args: ["get", "url"], cwd: options.cwd, namespace: options.namespace, sessionName: options.sessionName, signal: options.signal, timeoutMs: options.timeoutMs });
535
549
  const url = boundElectronProbeString(extractStringResultField(urlResult.data, "result") ?? extractStringResultField(urlResult.data, "url"), 300);
536
550
  if (urlResult.error || !url)
537
551
  return { error: urlResult.error ?? "get url returned no active page URL.", sessionName: options.sessionName };
538
- const fileAccessError = getManagedSessionStateAccessValidationError({ args: ["get", "title"], currentPageUrl: url, cwd: options.cwd });
539
- if (fileAccessError)
540
- return { error: fileAccessError, sessionName: options.sessionName, url };
541
- const titleResult = await collectManagedSessionCommandData({ allowManagedSessionTarget: options.allowManagedSessionTarget, args: ["get", "title"], cwd: options.cwd, namespace: options.namespace, sessionName: options.sessionName, signal: options.signal, timeoutMs: options.timeoutMs });
552
+ const titleResult = await collectManagedSessionCommandData({ args: ["get", "title"], cwd: options.cwd, namespace: options.namespace, sessionName: options.sessionName, signal: options.signal, timeoutMs: options.timeoutMs });
542
553
  const title = boundElectronProbeString(extractStringResultField(titleResult.data, "result") ?? extractStringResultField(titleResult.data, "title"), 160);
543
554
  return { sessionName: options.sessionName, title, url, ...(titleResult.error ? { error: titleResult.error } : {}) };
544
555
  }
@@ -566,7 +577,7 @@ export function buildQaAttachedRecoveryNextActions(sessionName) {
566
577
  buildNextToolAction({
567
578
  args: sessionArgs(["snapshot", "-i"]),
568
579
  id: "snapshot-before-qa-attached",
569
- reason: "Capture interactive refs on the active http(s) page before retrying qa.attached.",
580
+ reason: "Capture interactive refs on the active page before retrying qa.attached.",
570
581
  safety: "Read-only snapshot; confirms a renderable page is selected.",
571
582
  }),
572
583
  ];
@@ -588,13 +599,7 @@ export async function validateQaAttachedPrecondition(options) {
588
599
  const url = urlProbe.url?.trim();
589
600
  if (!url) {
590
601
  return {
591
- error: "qa.attached requires an attached session with a readable http(s) page URL. Run tab list, select a stable tab, then snapshot -i before retrying.",
592
- nextActions: buildQaAttachedRecoveryNextActions(options.sessionName),
593
- };
594
- }
595
- if (!isHttpOrHttpsUrl(url)) {
596
- return {
597
- error: `qa.attached requires an http(s) page URL; the current attached URL is "${url}". Use tab list and snapshot -i to recover a web surface before retrying.`,
602
+ error: "qa.attached requires an attached session with a readable page URL. Run tab list, select a stable tab, then snapshot -i before retrying.",
598
603
  nextActions: buildQaAttachedRecoveryNextActions(options.sessionName),
599
604
  };
600
605
  }
@@ -686,9 +691,6 @@ export async function collectElectronHandoff(options) {
686
691
  const url = extractStringResultField(urlData, "result") ?? extractStringResultField(urlData, "url");
687
692
  if (!url)
688
693
  throw new Error("Electron handoff get url returned no active page URL.");
689
- const fileAccessError = getManagedSessionStateAccessValidationError({ args: ["snapshot", "-i"], currentPageUrl: url, cwd: options.cwd });
690
- if (fileAccessError)
691
- return { error: fileAccessError, failureCategory: "validation-error", handoff: options.handoff };
692
694
  const tabs = await runSessionCommandData({ args: ["tab", "list"], cwd: options.cwd, namespace: options.namespace, sessionName: options.sessionName, signal: options.signal, throwOnFailure: true });
693
695
  if (options.signal?.aborted)
694
696
  throw new Error("Electron handoff was aborted.");
@@ -875,7 +877,7 @@ export async function collectTimeoutPartialProgress(options) {
875
877
  const artifacts = await collectTimeoutArtifactEvidence(options.cwd, rawSteps);
876
878
  const urlData = await runSessionCommandData({ args: ["get", "url"], cwd: options.cwd, namespace: options.namespace, sessionName: options.sessionName });
877
879
  const recoveredUrl = extractStringResultField(urlData, "result") ?? extractStringResultField(urlData, "url");
878
- const titleData = recoveredUrl && !isFileUrl(recoveredUrl)
880
+ const titleData = recoveredUrl
879
881
  ? await runSessionCommandData({ args: ["get", "title"], cwd: options.cwd, namespace: options.namespace, sessionName: options.sessionName })
880
882
  : undefined;
881
883
  const title = extractStringResultField(titleData, "result") ?? extractStringResultField(titleData, "title");
@@ -910,7 +912,7 @@ function sanitizeCurrentPageUrlForTimeoutDiagnostic(url) {
910
912
  return redactSensitivePathSegmentsForDiagnostic(redactSensitiveText(url));
911
913
  }
912
914
  }
913
- export function formatTimeoutPartialProgressText(progress) {
915
+ export function formatTimeoutPartialProgressText(progress, pageTargetUnknown = false) {
914
916
  const lines = [`Timeout partial progress: ${progress.summary}`];
915
917
  const currentPageTitle = progress.currentPage?.title ? redactSensitivePathSegmentsForDiagnostic(redactSensitiveText(progress.currentPage.title)) : undefined;
916
918
  const currentPageUrl = progress.currentPage?.url ? sanitizeCurrentPageUrlForTimeoutDiagnostic(progress.currentPage.url) : undefined;
@@ -928,7 +930,10 @@ export function formatTimeoutPartialProgressText(progress) {
928
930
  lines.push(`- ... ${progress.steps.length - shownSteps.length} more step${progress.steps.length - shownSteps.length === 1 ? "" : "s"} omitted`);
929
931
  }
930
932
  if (progress.retryStep?.retry?.args) {
931
- lines.push(`Retry failed step: ${JSON.stringify({ args: redactInvocationArgs(progress.retryStep.retry.args) })}`);
933
+ const payload = JSON.stringify({ args: redactInvocationArgs(progress.retryStep.retry.args) });
934
+ lines.push(pageTargetUnknown
935
+ ? `Retry candidate for step ${progress.retryStep.index}: ${payload}. Verify the current URL before running it.`
936
+ : `Retry failed step: ${payload}`);
932
937
  }
933
938
  for (const artifact of progress.artifacts)
934
939
  lines.push(`Artifact from step ${artifact.stepIndex}: ${redactSensitivePathSegmentsForDiagnostic(artifact.path)} (${artifact.exists ? `exists${typeof artifact.sizeBytes === "number" ? `, ${artifact.sizeBytes} bytes` : ""}` : "missing"})`);
@@ -3,9 +3,10 @@ import { getCompiledSemanticActionCommandIndex, getCompiledSemanticActionSession
3
3
  import { redactNetworkSourceLookupSurface } from "../../input-modes/lookups.js";
4
4
  import { buildAgentBrowserNextActions } from "../../results/action-recommendations.js";
5
5
  import { buildAgentBrowserResultCategoryDetails } from "../../results/categories.js";
6
+ import { extractAgentBrowserLifecycle } from "../../results/presentation/common.js";
6
7
  import { formatSessionArtifactRetentionSummary } from "../../results/artifact-manifest.js";
7
8
  import { alignPageChangeSummaryNextActionIds, appendUniqueAgentBrowserNextActions, applyNamespaceToNextActions, isStandaloneSnapshotNextAction, withOptionalSessionArgs, } from "../../results/next-actions.js";
8
- import { buildConnectedSessionNextActions, buildNoActivePageNextActions, buildSessionAwareStaleRefNextActions, buildSessionTabRecoveryNextActions, } from "../../results/recovery-next-actions.js";
9
+ import { buildConnectedSessionNextActions, buildNoActivePageNextActions, buildPendingWebMcpNextActions, buildSessionAwareStaleRefNextActions, buildSessionTabRecoveryNextActions, } from "../../results/recovery-next-actions.js";
9
10
  import { buildRichInputRecoveryDiagnostic, buildRichInputRecoveryNextActions, buildVisibleRefFallbackNextActions, formatRichInputRecoveryText, formatVisibleRefFallbackText, sanitizeVisibleRefFallbackDiagnostic, } from "../../results/selector-recovery.js";
10
11
  import { buildNoActivePageRefSnapshotInvalidation, isNoActivePageSnapshotFailure, } from "../../session-page-state.js";
11
12
  import { extractExplicitSessionName } from "../../argv-grammar.js";
@@ -208,6 +209,18 @@ function buildTimeoutPartialProgressNextActions(options) {
208
209
  const retryArgs = options.timeoutPartialProgress?.retryStep?.retry?.args;
209
210
  const stepIndex = options.timeoutPartialProgress?.retryStep?.index;
210
211
  const freshSessionAbandoned = options.sessionMode === "fresh" && options.timeoutPartialProgress?.liveUrlRecovered !== true;
212
+ if (options.currentSessionTabTargetUnknown && !freshSessionAbandoned && options.executionPlan.sessionName) {
213
+ return [{
214
+ id: "verify-page-target-after-timeout",
215
+ params: {
216
+ args: withOptionalSessionArgs(options.executionPlan.sessionName, ["batch", "--bail"]),
217
+ stdin: JSON.stringify([["get", "url"], ["snapshot", "-i"]]),
218
+ },
219
+ reason: `Verify the current URL, then inspect the page after timeout${stepIndex === undefined ? "" : ` before resuming from incomplete step ${stepIndex}`}.`,
220
+ safety: "Fail-fast read-only recovery: snapshot runs only after get url succeeds, satisfying the wrapper page-target guard without trusting the planned URL.",
221
+ tool: "agent_browser",
222
+ }];
223
+ }
211
224
  if (retryArgs) {
212
225
  return [{
213
226
  id: "retry-timeout-step",
@@ -267,6 +280,10 @@ function buildResultNextActions(options) {
267
280
  const appendUnique = (actions) => {
268
281
  appendUniqueAgentBrowserNextActions(nextActions, actions);
269
282
  };
283
+ if (options.unsettledWebMcpMutation && options.currentSessionTabTargetUnknown) {
284
+ nextActions = nextActions.filter((action) => !isStandaloneSnapshotNextAction(action));
285
+ appendUnique(buildPendingWebMcpNextActions(options.executionPlan.sessionName));
286
+ }
270
287
  if (options.categoryDetails.resultCategory === "success" && options.executionPlan.commandInfo.command === "connect" && !options.electronLaunchRecord)
271
288
  appendUnique(buildConnectedSessionNextActions(options.executionPlan.sessionName));
272
289
  if (options.noActivePageSnapshotFailure)
@@ -320,6 +337,8 @@ function buildResultNextActions(options) {
320
337
  if (options.managedSessionOutcome)
321
338
  appendUnique(buildManagedSessionFreshFailureNextActions(options.managedSessionOutcome));
322
339
  if (options.categoryDetails.failureCategory === "timeout" && options.processResult.timedOut) {
340
+ if (options.currentSessionTabTargetUnknown)
341
+ nextActions = nextActions.filter((action) => !isStandaloneSnapshotNextAction(action));
323
342
  appendUnique(buildTimeoutPartialProgressNextActions(options));
324
343
  appendUnique(buildDialogTimeoutNextActions({ command: options.executionPlan.commandInfo.command, sessionName: options.executionPlan.sessionName }));
325
344
  }
@@ -329,19 +348,45 @@ function buildResultNextActions(options) {
329
348
  append(buildAgentBrowserNextActions({ electron: { launchId: options.electronLaunchRecord.launchId, sessionName: options.electronLaunchRecord.sessionName, status: options.electronLaunchRecord.cleanupState }, failureCategory: options.categoryDetails.failureCategory, resultCategory: options.categoryDetails.resultCategory, successCategory: options.categoryDetails.successCategory }));
330
349
  return nextActions.length > 0 ? nextActions : undefined;
331
350
  }
332
- function formatFailureNextActionsText(options, nextActions) {
333
- if (options.categoryDetails.resultCategory !== "failure" || !nextActions || nextActions.length === 0)
351
+ export function formatAgentBrowserNextActionsText(nextActions) {
352
+ if (!nextActions || nextActions.length === 0)
334
353
  return undefined;
335
354
  const lines = nextActions.slice(0, 6).map((action) => {
336
355
  const params = action.params
337
- ? { ...action.params, ...(action.params.stdin === undefined ? {} : { stdin: "[omitted; use details.nextActions]" }) }
356
+ ? { ...action.params, ...(action.params.stdin !== undefined && action.params.stdin.length > 500 ? { stdin: "[omitted; use details.nextActions]" } : {}) }
338
357
  : undefined;
339
358
  const payload = action.artifactPath ? { artifactPath: action.artifactPath } : params;
340
- return `- ${action.id}${payload ? ` ${JSON.stringify(payload)}` : ""}: ${action.reason}`;
359
+ return `- ${action.id}${payload ? ` ${redactSensitiveText(JSON.stringify(payload))}` : ""}: ${redactSensitiveText(action.reason)}`;
341
360
  });
342
- return ["Next actions:", ...lines, "Use the exact redacted payloads in details.nextActions when available."].join("\n");
361
+ return ["Next actions:", ...lines, "The same redacted payloads are available in details.nextActions."].join("\n");
362
+ }
363
+ function formatFailureNextActionsText(options, nextActions) {
364
+ return options.categoryDetails.resultCategory === "failure" ? formatAgentBrowserNextActionsText(nextActions) : undefined;
365
+ }
366
+ function getReadSource(options) {
367
+ return options.executionPlan.commandInfo.command === "read" && isRecord(options.presentationEnvelope?.data) && typeof options.presentationEnvelope.data.source === "string"
368
+ ? options.presentationEnvelope.data.source
369
+ : undefined;
370
+ }
371
+ function formatReadExecutionText(options, lifecycle) {
372
+ const source = getReadSource(options);
373
+ if (!source)
374
+ return undefined;
375
+ return `Read execution: source ${source}; CLI started: ${options.processResult.agentBrowserStarted ? "yes" : "no"}; managed browser lifecycle active: ${lifecycle?.effectiveLaunch.browserLaunched === true ? "yes" : "no"}; managed session outcome: ${options.managedSessionOutcome?.status ?? "not managed"}.`;
376
+ }
377
+ function buildBrowserWindowStatus(options, lifecycle) {
378
+ if (!options.headedLaunch || options.preserveAttachedBrowserSession || options.providerLaunch || !options.succeeded || lifecycle?.effectiveLaunch.browserLaunched !== true || !options.executionPlan.managedSessionName || !options.managedSessionOutcome || !["created", "replaced"].includes(options.managedSessionOutcome.status))
379
+ return undefined;
380
+ return { mode: "headed", ownership: "wrapper-managed", sessionName: options.executionPlan.managedSessionName, visibility: "unverified" };
381
+ }
382
+ function formatBrowserWindowText(browserWindow) {
383
+ if (!browserWindow)
384
+ return undefined;
385
+ return "Headed browser handoff: wrapper-managed headed window requested; desktop visibility unverified. If login is needed, ask the user to confirm they can see the window and finish signing in there, then continue with sessionMode auto.";
343
386
  }
344
387
  function buildAgentBrowserResultDetails(options, nextActions) {
388
+ const lifecycle = extractAgentBrowserLifecycle(options.presentationEnvelope?.data);
389
+ const browserWindow = buildBrowserWindowStatus(options, lifecycle);
345
390
  const publicVisibleRefFallbackDiagnostic = options.visibleRefFallbackDiagnostic ? sanitizeVisibleRefFallbackDiagnostic(options.visibleRefFallbackDiagnostic) : undefined;
346
391
  const rawPageChangeSummary = (options.scrollNoopDiagnostic || options.comboboxFocusDiagnostic) && options.presentation.pageChangeSummary ? { ...options.presentation.pageChangeSummary, nextActionIds: nextActions?.map((action) => action.id) } : options.presentation.pageChangeSummary;
347
392
  const pageChangeSummary = alignPageChangeSummaryNextActionIds(rawPageChangeSummary, nextActions);
@@ -370,6 +415,9 @@ function buildAgentBrowserResultDetails(options, nextActions) {
370
415
  electron: options.electronLaunchRecord ? { action: "launch", cleanup: options.electronFailedConnectCleanup, handoff: options.electronHandoff, identifiers: buildElectronIdentifiers(options.electronLaunchRecord), launch: options.electronLaunchRecord, profileIsolation: options.electronProfileIsolationDetails, status: options.succeeded ? "succeeded" : "failed", targets: options.electronLaunch?.targets, version: options.electronLaunch?.version } : undefined,
371
416
  ...options.categoryDetails,
372
417
  agentBrowserStarted: options.processResult.agentBrowserStarted,
418
+ browserWindow,
419
+ lifecycle,
420
+ readSource: getReadSource(options),
373
421
  aboutBlankSessionMismatch: options.aboutBlankSessionMismatch,
374
422
  electronPostCommandHealth: options.electronPostCommandHealth,
375
423
  electronRefFreshness: options.electronRefFreshnessDiagnostic,
@@ -429,6 +477,8 @@ function buildAgentBrowserResultDetails(options, nextActions) {
429
477
  }
430
478
  export function buildFinalAgentBrowserToolResult(options) {
431
479
  const nextActions = applyNamespaceToNextActions(buildResultNextActions(options), options.executionPlan.namespace);
480
+ const lifecycle = extractAgentBrowserLifecycle(options.presentationEnvelope?.data);
481
+ const browserWindow = buildBrowserWindowStatus(options, lifecycle);
432
482
  const details = buildAgentBrowserResultDetails(options, nextActions);
433
483
  const visibleRefFallbackText = formatVisibleRefFallbackText(options.visibleRefFallbackDiagnostic);
434
484
  const richInputRecoveryText = formatRichInputRecoveryText(options.richInputRecoveryDiagnostic);
@@ -445,10 +495,12 @@ export function buildFinalAgentBrowserToolResult(options) {
445
495
  const evalStdinHintText = formatEvalStdinHintText(options.evalStdinHint);
446
496
  const evalResultWarningText = formatEvalResultWarningText(options.evalResultWarning);
447
497
  const artifactCleanupText = formatArtifactCleanupGuidanceText(options.artifactCleanup);
448
- const timeoutPartialProgressText = options.timeoutPartialProgress ? formatTimeoutPartialProgressText(options.timeoutPartialProgress) : undefined;
498
+ const timeoutPartialProgressText = options.timeoutPartialProgress ? formatTimeoutPartialProgressText(options.timeoutPartialProgress, options.currentSessionTabTargetUnknown === true && !(options.sessionMode === "fresh" && options.timeoutPartialProgress.liveUrlRecovered !== true)) : undefined;
449
499
  const managedSessionOutcomeText = formatManagedSessionOutcomeText(options.managedSessionOutcome);
500
+ const readExecutionText = formatReadExecutionText(options, lifecycle);
501
+ const browserWindowText = formatBrowserWindowText(browserWindow);
450
502
  const failureNextActionsText = formatFailureNextActionsText(options, nextActions);
451
- const rawAppendedDiagnosticText = [visibleRefFallbackText, richInputRecoveryText, semanticActionCandidateText, clickDispatchText, overlayBlockerText, fillVerificationText, electronRefFreshnessText, selectorTextVisibilityText, electronBroadGetTextScopeText, scrollNoopDiagnosticText, comboboxFocusDiagnosticText, recordingDependencyWarningText, evalStdinHintText, evalResultWarningText, artifactCleanupText, timeoutPartialProgressText, managedSessionOutcomeText, failureNextActionsText].filter((item) => item !== undefined).join("\n\n");
503
+ const rawAppendedDiagnosticText = [visibleRefFallbackText, richInputRecoveryText, semanticActionCandidateText, clickDispatchText, overlayBlockerText, fillVerificationText, electronRefFreshnessText, selectorTextVisibilityText, electronBroadGetTextScopeText, scrollNoopDiagnosticText, comboboxFocusDiagnosticText, recordingDependencyWarningText, evalStdinHintText, evalResultWarningText, artifactCleanupText, timeoutPartialProgressText, managedSessionOutcomeText, readExecutionText, browserWindowText, failureNextActionsText].filter((item) => item !== undefined).join("\n\n");
452
504
  const appendedDiagnosticText = redactSensitiveText(redactExactSensitiveText(rawAppendedDiagnosticText, options.exactSensitiveValues));
453
505
  const shouldAppendDiagnosticText = appendedDiagnosticText.length > 0 && (!options.userRequestedJson || options.plainTextInspection);
454
506
  let content = shouldAppendDiagnosticText && options.redactedContent[0]?.type === "text" ? [{ ...options.redactedContent[0], text: `${options.redactedContent[0].text}\n\n${appendedDiagnosticText}` }, ...options.redactedContent.slice(1)] : options.redactedContent;
@@ -20,7 +20,6 @@ function getHeadedManagedAutosaveEnv(interval) {
20
20
  }
21
21
  export async function inspectManagedSessionDaemon(options) {
22
22
  const processResult = await runAgentBrowserProcess({
23
- allowManagedSessionTarget: options.allowManagedSessionTarget,
24
23
  args: ["--json", "--namespace", options.namespace ?? "", "--session", options.sessionName, "session", "info"],
25
24
  cwd: options.cwd,
26
25
  env: getHeadedManagedAutosaveEnv(options.headedManagedAutosaveInterval),
@@ -63,12 +62,11 @@ export async function acquireOwnedManagedSessionDaemonPolicy(options) {
63
62
  return signal?.aborted
64
63
  ? {}
65
64
  : {
66
- error: "Managed-session policy coordination is unavailable or busy. Retry after the current operation finishes, repair the private policy-lock directory, and on POSIX verify that /bin/ps or /usr/bin/ps is available.",
65
+ error: "Managed-session policy coordination is unavailable or busy. Retry after the current operation finishes, repair the private policy-lock directory, and on POSIX verify that /bin/ps, /usr/bin/ps, or ps through PATH is available.",
67
66
  };
68
67
  }
69
68
  try {
70
69
  const daemon = await inspectManagedSessionDaemon({
71
- allowManagedSessionTarget: true,
72
70
  cwd: context.cwd,
73
71
  headedManagedAutosaveInterval: context.headedManagedAutosaveInterval,
74
72
  namespace: context.namespace,
@@ -133,7 +131,6 @@ export async function closeManagedSession(options) {
133
131
  }
134
132
  try {
135
133
  const daemon = await inspectManagedSessionDaemon({
136
- allowManagedSessionTarget: true,
137
134
  cwd: options.cwd,
138
135
  headedManagedAutosaveInterval: options.headedManagedAutosaveInterval,
139
136
  namespace: options.namespace,