pi-agent-browser-native 0.2.72 → 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.
- package/CHANGELOG.md +27 -0
- package/README.md +14 -12
- package/dist/extensions/agent-browser/index.js +104 -16
- package/dist/extensions/agent-browser/lib/argv-grammar.js +122 -0
- package/dist/extensions/agent-browser/lib/command-taxonomy.js +11 -0
- package/dist/extensions/agent-browser/lib/electron/cdp.js +2 -2
- package/dist/extensions/agent-browser/lib/electron/launch.js +48 -12
- package/dist/extensions/agent-browser/lib/input-modes/params.js +96 -98
- package/dist/extensions/agent-browser/lib/launch-scoped-flags.js +88 -2
- package/dist/extensions/agent-browser/lib/managed-session-capabilities.js +22 -0
- package/dist/extensions/agent-browser/lib/managed-session-policy-lock.js +432 -0
- package/dist/extensions/agent-browser/lib/managed-session-restore.js +367 -0
- package/dist/extensions/agent-browser/lib/managed-session-snapshots.js +367 -0
- package/dist/extensions/agent-browser/lib/managed-session-state-policy.js +589 -0
- package/dist/extensions/agent-browser/lib/managed-session-storage.js +299 -0
- package/dist/extensions/agent-browser/lib/orchestration/batch-stdin.js +35 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/artifact-paths.js +9 -2
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.js +40 -22
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result.js +15 -6
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/index.js +54 -33
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/managed-session-daemon-policy.js +182 -0
- 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 +625 -429
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output.js +136 -56
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-state.js +28 -40
- package/dist/extensions/agent-browser/lib/orchestration/electron-host/index.js +102 -19
- package/dist/extensions/agent-browser/lib/orchestration/output-file.js +13 -1
- package/dist/extensions/agent-browser/lib/playbook.js +9 -8
- package/dist/extensions/agent-browser/lib/process-identity.js +82 -0
- package/dist/extensions/agent-browser/lib/process.js +270 -34
- package/dist/extensions/agent-browser/lib/results/artifact-manifest.js +5 -3
- package/dist/extensions/agent-browser/lib/results/presentation/common.js +2 -1
- package/dist/extensions/agent-browser/lib/results/presentation/diagnostics.js +35 -12
- package/dist/extensions/agent-browser/lib/results/presentation/managed-list-filter.js +42 -0
- package/dist/extensions/agent-browser/lib/results/recovery-actions.js +7 -0
- package/dist/extensions/agent-browser/lib/runtime.js +85 -85
- package/dist/extensions/agent-browser/lib/session-page-state.js +48 -17
- package/dist/extensions/agent-browser/lib/temp.js +13 -25
- package/docs/ARCHITECTURE.md +9 -8
- package/docs/COMMAND_REFERENCE.md +43 -23
- package/docs/ELECTRON.md +10 -10
- package/docs/RELEASE.md +3 -2
- package/docs/SUPPORT_MATRIX.md +19 -18
- package/docs/TOOL_CONTRACT.md +28 -25
- package/docs/platform-smoke.md +2 -2
- package/package.json +1 -1
- package/platform-smoke.config.mjs +1 -1
- package/scripts/agent-browser-capability-baseline.mjs +11 -3
package/dist/extensions/agent-browser/lib/orchestration/browser-run/managed-session-daemon-policy.js
ADDED
|
@@ -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,
|
package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/network-page-filter.js
CHANGED
|
@@ -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
|
},
|
package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/snapshot-filter.js
CHANGED
|
@@ -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,
|