pi-agent-browser-native 0.2.71 → 0.2.74

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 (52) hide show
  1. package/CHANGELOG.md +46 -0
  2. package/README.md +14 -12
  3. package/dist/extensions/agent-browser/index.js +104 -16
  4. package/dist/extensions/agent-browser/lib/argv-grammar.js +124 -0
  5. package/dist/extensions/agent-browser/lib/command-taxonomy.js +12 -1
  6. package/dist/extensions/agent-browser/lib/electron/cdp.js +2 -2
  7. package/dist/extensions/agent-browser/lib/electron/launch.js +48 -12
  8. package/dist/extensions/agent-browser/lib/input-modes/params.js +96 -98
  9. package/dist/extensions/agent-browser/lib/launch-scoped-flags.js +88 -2
  10. package/dist/extensions/agent-browser/lib/managed-session-capabilities.js +22 -0
  11. package/dist/extensions/agent-browser/lib/managed-session-policy-lock.js +432 -0
  12. package/dist/extensions/agent-browser/lib/managed-session-restore.js +367 -0
  13. package/dist/extensions/agent-browser/lib/managed-session-snapshots.js +367 -0
  14. package/dist/extensions/agent-browser/lib/managed-session-state-policy.js +589 -0
  15. package/dist/extensions/agent-browser/lib/managed-session-storage.js +299 -0
  16. package/dist/extensions/agent-browser/lib/orchestration/batch-stdin.js +35 -0
  17. package/dist/extensions/agent-browser/lib/orchestration/browser-run/artifact-paths.js +9 -2
  18. package/dist/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.js +40 -22
  19. package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result.js +15 -6
  20. package/dist/extensions/agent-browser/lib/orchestration/browser-run/index.js +54 -33
  21. package/dist/extensions/agent-browser/lib/orchestration/browser-run/managed-session-daemon-policy.js +182 -0
  22. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/direct-anchor-download.js +1 -1
  23. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/network-page-filter.js +1 -1
  24. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/scroll-shims.js +1 -1
  25. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/snapshot-filter.js +1 -1
  26. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare.js +625 -429
  27. package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output.js +136 -56
  28. package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-state.js +28 -40
  29. package/dist/extensions/agent-browser/lib/orchestration/electron-host/index.js +102 -19
  30. package/dist/extensions/agent-browser/lib/orchestration/output-file.js +13 -1
  31. package/dist/extensions/agent-browser/lib/playbook.js +10 -9
  32. package/dist/extensions/agent-browser/lib/process-identity.js +82 -0
  33. package/dist/extensions/agent-browser/lib/process.js +270 -34
  34. package/dist/extensions/agent-browser/lib/results/artifact-manifest.js +5 -3
  35. package/dist/extensions/agent-browser/lib/results/categories.js +21 -2
  36. package/dist/extensions/agent-browser/lib/results/presentation/common.js +2 -1
  37. package/dist/extensions/agent-browser/lib/results/presentation/diagnostics.js +80 -12
  38. package/dist/extensions/agent-browser/lib/results/presentation/managed-list-filter.js +42 -0
  39. package/dist/extensions/agent-browser/lib/results/recovery-actions.js +7 -0
  40. package/dist/extensions/agent-browser/lib/runtime.js +85 -85
  41. package/dist/extensions/agent-browser/lib/session-page-state.js +48 -17
  42. package/dist/extensions/agent-browser/lib/temp.js +13 -25
  43. package/docs/ARCHITECTURE.md +9 -8
  44. package/docs/COMMAND_REFERENCE.md +97 -32
  45. package/docs/ELECTRON.md +10 -10
  46. package/docs/RELEASE.md +3 -2
  47. package/docs/SUPPORT_MATRIX.md +22 -19
  48. package/docs/TOOL_CONTRACT.md +31 -28
  49. package/docs/platform-smoke.md +5 -5
  50. package/package.json +1 -1
  51. package/platform-smoke.config.mjs +3 -1
  52. package/scripts/agent-browser-capability-baseline.mjs +45 -3
@@ -0,0 +1,182 @@
1
+ /**
2
+ * Purpose: Coordinate wrapper-owned managed-session daemon inspection, reuse, and cleanup policy.
3
+ * Responsibilities: Serialize daemon policy decisions, verify live restore keys, and close owned sessions with snapshot cleanup.
4
+ * Scope: Managed-session daemon policy only; page state and browser-result shaping live in sibling modules.
5
+ * Usage: Called by browser-run preparation, Electron host probes, result handling, and extension shutdown cleanup.
6
+ * Invariants/Assumptions: Every owned daemon decision holds the cross-process policy lock until its caller finishes the related operation.
7
+ */
8
+ import { rm } from "node:fs/promises";
9
+ import { acquireManagedSessionPolicyLock } from "../../managed-session-policy-lock.js";
10
+ import { pruneOwnedManagedSessionRestoreSnapshots, } from "../../managed-session-restore.js";
11
+ import { isManagedSessionRestoreKey } from "../../managed-session-storage.js";
12
+ import { isRecord } from "../../parsing.js";
13
+ import { runAgentBrowserProcess } from "../../process.js";
14
+ import { getAgentBrowserErrorText, parseAgentBrowserEnvelope } from "../../results.js";
15
+ import { redactInvocationArgs } from "../../runtime.js";
16
+ const MANAGED_SESSION_DAEMON_INSPECTION_TIMEOUT_MS = 5_000;
17
+ export async function inspectManagedSessionDaemon(options) {
18
+ const processResult = await runAgentBrowserProcess({
19
+ allowManagedSessionTarget: options.allowManagedSessionTarget,
20
+ args: ["--json", "--namespace", options.namespace ?? "", "--session", options.sessionName, "session", "info"],
21
+ cwd: options.cwd,
22
+ signal: options.signal,
23
+ timeoutMs: options.timeoutMs ?? MANAGED_SESSION_DAEMON_INSPECTION_TIMEOUT_MS,
24
+ });
25
+ try {
26
+ if (processResult.spawnError?.code === "ENOENT")
27
+ return { status: "missing-binary" };
28
+ if (processResult.aborted || processResult.spawnError || processResult.exitCode !== 0)
29
+ return { status: "unknown" };
30
+ const parsed = await parseAgentBrowserEnvelope({ stdout: processResult.stdout, stdoutPath: processResult.stdoutSpillPath });
31
+ const data = parsed.parseError || parsed.envelope?.success === false ? undefined : parsed.envelope?.data;
32
+ if (!isRecord(data) || typeof data.active !== "boolean")
33
+ return { status: "unknown" };
34
+ if (!data.active)
35
+ return { status: "inactive" };
36
+ if (!isRecord(data.runtime))
37
+ return { status: "unknown" };
38
+ if (typeof data.runtime.restoreKey === "string" && data.runtime.restoreKey.length > 0)
39
+ return { restoreKey: data.runtime.restoreKey, status: "active" };
40
+ return data.runtime.restoreKey === null ? { restoreKey: null, status: "active" } : { status: "unknown" };
41
+ }
42
+ finally {
43
+ if (processResult.stdoutSpillPath)
44
+ await rm(processResult.stdoutSpillPath, { force: true }).catch(() => undefined);
45
+ }
46
+ }
47
+ export async function acquireOwnedManagedSessionDaemonPolicy(options) {
48
+ const { context, signal } = options;
49
+ if (!context.cwd)
50
+ return { error: "Managed-session policy validation requires the wrapper-owned session cwd." };
51
+ const lock = await acquireManagedSessionPolicyLock({
52
+ namespace: context.namespace,
53
+ sessionName: context.sessionName,
54
+ signal,
55
+ });
56
+ if (!lock) {
57
+ return signal?.aborted
58
+ ? {}
59
+ : {
60
+ 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.",
61
+ };
62
+ }
63
+ try {
64
+ const daemon = await inspectManagedSessionDaemon({
65
+ allowManagedSessionTarget: true,
66
+ cwd: context.cwd,
67
+ namespace: context.namespace,
68
+ sessionName: context.sessionName,
69
+ signal,
70
+ });
71
+ if (daemon.status === "inactive")
72
+ context.restoreState.forgetDaemonRestoreKey(context.sessionName, context.namespace);
73
+ if (options.mode === "close") {
74
+ if (daemon.status === "active")
75
+ context.restoreState.recordDaemonRestoreKey(context.sessionName, context.namespace, daemon.restoreKey);
76
+ return { lock };
77
+ }
78
+ const stickyDisabled = context.restoreState.isDisabled(context.sessionName, context.namespace);
79
+ const hasKnownDaemonRestoreKey = context.restoreState.hasDaemonRestoreKey(context.sessionName, context.namespace);
80
+ const knownDaemonRestoreKey = context.restoreState.getDaemonRestoreKey(context.sessionName, context.namespace);
81
+ const requestedDaemonRestoreKey = context.restoreDecision === "enabled" && stickyDisabled
82
+ ? knownDaemonRestoreKey ?? null
83
+ : context.expectedDaemonRestoreKey;
84
+ if (daemon.status === "unknown") {
85
+ return {
86
+ error: "The wrapper could not verify this managed session's live daemon restore policy. Retry, close that session, or use sessionMode: \"fresh\".",
87
+ lock,
88
+ };
89
+ }
90
+ const restoreDisabledPolicyNeedsProvenance = stickyDisabled || context.restoreDecision !== "enabled";
91
+ const activePolicyMatches = daemon.status === "active"
92
+ && (!restoreDisabledPolicyNeedsProvenance || hasKnownDaemonRestoreKey)
93
+ && daemon.restoreKey === requestedDaemonRestoreKey;
94
+ if (activePolicyMatches)
95
+ context.restoreState.recordDaemonRestoreKey(context.sessionName, context.namespace, daemon.restoreKey);
96
+ return !["inactive", "missing-binary"].includes(daemon.status) && !activePolicyMatches
97
+ ? {
98
+ error: [
99
+ "This wrapper-owned session's live daemon does not match the requested managed-restore policy.",
100
+ "Close that session first, retry with sessionMode: \"fresh\", or use a distinct explicit --session.",
101
+ ].join(" "),
102
+ lock,
103
+ }
104
+ : { lock };
105
+ }
106
+ catch (error) {
107
+ await lock.release();
108
+ throw error;
109
+ }
110
+ }
111
+ export async function closeManagedSession(options) {
112
+ const controller = new AbortController();
113
+ const timer = setTimeout(() => controller.abort(), options.timeoutMs);
114
+ let stdoutSpillPath;
115
+ const closeArgs = [...(options.namespace ? ["--namespace", options.namespace] : []), "--session", options.sessionName, "close"];
116
+ const policyLock = options.policyLock ?? await acquireManagedSessionPolicyLock({
117
+ namespace: options.namespace,
118
+ sessionName: options.sessionName,
119
+ signal: controller.signal,
120
+ timeoutMs: Math.min(options.timeoutMs, 1_000),
121
+ });
122
+ if (!policyLock) {
123
+ clearTimeout(timer);
124
+ return "Managed-session policy coordination is unavailable or busy; cleanup did not run. Retry after the current operation finishes or repair the private policy-lock directory.";
125
+ }
126
+ try {
127
+ const daemon = await inspectManagedSessionDaemon({
128
+ allowManagedSessionTarget: true,
129
+ cwd: options.cwd,
130
+ namespace: options.namespace,
131
+ sessionName: options.sessionName,
132
+ signal: controller.signal,
133
+ timeoutMs: Math.min(options.timeoutMs, 2_000),
134
+ });
135
+ if (daemon.status === "active")
136
+ options.restoreState.recordDaemonRestoreKey(options.sessionName, options.namespace, daemon.restoreKey);
137
+ const daemonRestoreKey = options.restoreState.getDaemonRestoreKey(options.sessionName, options.namespace);
138
+ const ownedRestoreKey = !options.restoreState.isDisabled(options.sessionName, options.namespace)
139
+ && isManagedSessionRestoreKey(daemonRestoreKey) ? daemonRestoreKey : null;
140
+ const processResult = await runAgentBrowserProcess({
141
+ args: closeArgs,
142
+ cwd: options.cwd,
143
+ env: { AGENT_BROWSER_JSON: "1" },
144
+ managedSessionRestoreState: options.restoreState,
145
+ ownedManagedSession: true,
146
+ signal: controller.signal,
147
+ });
148
+ stdoutSpillPath = processResult.stdoutSpillPath;
149
+ if (!processResult.aborted && !processResult.spawnError && processResult.exitCode === 0) {
150
+ const parsed = await parseAgentBrowserEnvelope({ stdout: processResult.stdout, stdoutPath: processResult.stdoutSpillPath });
151
+ const data = parsed.envelope?.success === true && isRecord(parsed.envelope.data) ? parsed.envelope.data : undefined;
152
+ options.restoreState.clear(options.sessionName, options.namespace);
153
+ pruneOwnedManagedSessionRestoreSnapshots({
154
+ cwd: options.cwd,
155
+ namespace: options.namespace,
156
+ restoreKey: ownedRestoreKey,
157
+ statePath: typeof data?.statePath === "string" ? data.statePath : undefined,
158
+ });
159
+ }
160
+ return getAgentBrowserErrorText({
161
+ aborted: processResult.aborted,
162
+ command: "close",
163
+ effectiveArgs: redactInvocationArgs(closeArgs),
164
+ exitCode: processResult.exitCode,
165
+ plainTextInspection: false,
166
+ spawnError: processResult.spawnError,
167
+ stderr: processResult.stderr,
168
+ timedOut: processResult.timedOut,
169
+ timeoutMs: processResult.timeoutMs,
170
+ });
171
+ }
172
+ catch (error) {
173
+ return error instanceof Error ? error.message : String(error);
174
+ }
175
+ finally {
176
+ clearTimeout(timer);
177
+ if (stdoutSpillPath)
178
+ await rm(stdoutSpillPath, { force: true }).catch(() => undefined);
179
+ if (!options.policyLock)
180
+ await policyLock.release();
181
+ }
182
+ }
@@ -129,7 +129,7 @@ export async function tryDirectAnchorDownload(options) {
129
129
  savedFilePath: absolutePath,
130
130
  sessionMode: options.sessionMode,
131
131
  ...buildAgentBrowserResultCategoryDetails({ artifacts: [artifact], args: options.effectiveArgs, command: "download", savedFile, succeeded: true }),
132
- ...buildSessionDetailFields(options.sessionName, options.usedImplicitSession, options.namespace),
132
+ ...buildSessionDetailFields(options.sessionName, options.usedImplicitSession, options.namespace, options.managedSessionRestoreDisabled()),
133
133
  summary: `Download completed: ${absolutePath}`,
134
134
  },
135
135
  isError: false,
@@ -100,7 +100,7 @@ export async function tryNetworkRequestsPageFilter(options) {
100
100
  networkRequestsPageFilter: { cleanArgs: request.cleanArgs, currentUrl: redactSensitiveText(currentUrl), matchedRows: filtered.matchedRows, mode: request.mode, totalRows: filtered.totalRows },
101
101
  sessionMode: options.sessionMode,
102
102
  ...buildAgentBrowserResultCategoryDetails({ args: options.effectiveArgs, command: "network", succeeded: true }),
103
- ...buildSessionDetailFields(options.sessionName, options.usedImplicitSession, options.namespace),
103
+ ...buildSessionDetailFields(options.sessionName, options.usedImplicitSession, options.namespace, options.managedSessionRestoreDisabled()),
104
104
  summary,
105
105
  },
106
106
  isError: false,
@@ -58,7 +58,7 @@ function buildScrollResult(options) {
58
58
  [options.scrollField]: options.scrollValue,
59
59
  sessionMode: options.sessionMode,
60
60
  ...buildAgentBrowserResultCategoryDetails({ args: options.effectiveArgs, command: options.command, errorText: options.succeeded ? undefined : options.message, succeeded: options.succeeded, validationError: options.succeeded ? undefined : options.message }),
61
- ...buildSessionDetailFields(options.sessionName, options.usedImplicitSession, options.namespace),
61
+ ...buildSessionDetailFields(options.sessionName, options.usedImplicitSession, options.namespace, options.managedSessionRestoreDisabled()),
62
62
  summary: options.message,
63
63
  validationError: options.succeeded ? undefined : options.message,
64
64
  },
@@ -149,7 +149,7 @@ export async function trySnapshotFilter(options) {
149
149
  snapshotFilter: request.role || request.search ? { cleanArgs: request.cleanArgs, matchedRefs: filtered.matchedRefs, role: request.role, search: request.search, totalLines: filtered.totalLines, totalRefs: filtered.totalRefs, visibleLines: filtered.visibleLines } : undefined,
150
150
  snapshotViewport: viewport,
151
151
  ...buildAgentBrowserResultCategoryDetails({ args: options.effectiveArgs, command: "snapshot", succeeded: true }),
152
- ...buildSessionDetailFields(options.sessionName, options.usedImplicitSession, options.namespace),
152
+ ...buildSessionDetailFields(options.sessionName, options.usedImplicitSession, options.namespace, options.managedSessionRestoreDisabled()),
153
153
  summary,
154
154
  },
155
155
  isError: false,