pi-agent-browser-native 0.8.3 → 0.9.1
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 +36 -0
- package/README.md +12 -11
- package/dist/extensions/agent-browser/index.js +96 -320
- package/dist/extensions/agent-browser/lib/argv-descriptor.js +1 -1
- package/dist/extensions/agent-browser/lib/argv-grammar.js +3 -3
- package/dist/extensions/agent-browser/lib/browser-journal.js +33 -12
- package/dist/extensions/agent-browser/lib/browser-transcript.js +1 -1
- package/dist/extensions/agent-browser/lib/command-taxonomy.js +0 -3
- package/dist/extensions/agent-browser/lib/config-policy.js +0 -13
- package/dist/extensions/agent-browser/lib/config.js +3 -48
- package/dist/extensions/agent-browser/lib/electron/launch.js +4 -4
- package/dist/extensions/agent-browser/lib/execution-cwd.js +0 -16
- package/dist/extensions/agent-browser/lib/input-modes/params.js +2 -3
- package/dist/extensions/agent-browser/lib/input-modes/script.js +28 -6
- package/dist/extensions/agent-browser/lib/input-modes/types.js +2 -2
- package/dist/extensions/agent-browser/lib/managed-session-restore.js +1 -4
- package/dist/extensions/agent-browser/lib/native-output.js +46 -0
- package/dist/extensions/agent-browser/lib/orchestration/batch-stdin.js +0 -9
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/artifact-merge.js +22 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.js +1 -1
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result.js +2 -2
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare.js +1 -1
- package/dist/extensions/agent-browser/lib/orchestration/execution-queue.js +86 -0
- package/dist/extensions/agent-browser/lib/pi-tool-rendering.js +31 -17
- package/dist/extensions/agent-browser/lib/process.js +0 -3
- package/dist/extensions/agent-browser/lib/prompt-policy.js +16 -12
- package/dist/extensions/agent-browser/lib/results/artifact-manifest.js +2 -2
- package/dist/extensions/agent-browser/lib/results/network-routes.js +1 -1
- package/dist/extensions/agent-browser/lib/results/network.js +1 -1
- package/dist/extensions/agent-browser/lib/results/presentation/batch.js +1 -1
- package/dist/extensions/agent-browser/lib/results/presentation/content.js +4 -0
- package/dist/extensions/agent-browser/lib/results/presentation/diagnostics.js +1 -1
- package/dist/extensions/agent-browser/lib/results/presentation/large-output.js +15 -8
- package/dist/extensions/agent-browser/lib/results/presentation/semantic-action.js +2 -2
- package/dist/extensions/agent-browser/lib/results/selector-recovery.js +1 -1
- package/dist/extensions/agent-browser/lib/runtime.js +4 -2
- package/dist/extensions/agent-browser/lib/session-page-state.js +1 -1
- package/dist/extensions/agent-browser/lib/tool-surface.js +39 -11
- package/dist/extensions/agent-browser/lib/web-search.js +100 -79
- package/docs/ARCHITECTURE.md +17 -19
- package/docs/COMMAND_REFERENCE.md +6 -2
- package/docs/RELEASE.md +26 -20
- package/docs/REQUIREMENTS.md +5 -5
- package/docs/SESSION_STORAGE.md +4 -4
- package/docs/SUPPORT_MATRIX.md +15 -13
- package/docs/TOOL_CONTRACT.md +12 -8
- package/package.json +6 -6
- package/scripts/doctor.mjs +12 -4
|
@@ -1,7 +1,6 @@
|
|
|
1
|
-
import { AsyncLocalStorage } from "node:async_hooks";
|
|
2
1
|
import { randomUUID } from "node:crypto";
|
|
3
2
|
import { existsSync, readFileSync } from "node:fs";
|
|
4
|
-
import { dirname, join } from "node:path";
|
|
3
|
+
import { dirname, isAbsolute, join } from "node:path";
|
|
5
4
|
import { fileURLToPath } from "node:url";
|
|
6
5
|
import { Text } from "@earendil-works/pi-tui";
|
|
7
6
|
import { batchHasSuccessfulCloseAll, getSuccessfulBatchCloseLifecycle } from "./lib/batch-lifecycle.js";
|
|
@@ -13,17 +12,19 @@ import { buildExecutionPlan, canUseHeadlessCompatibilityUserAgent, createEphemer
|
|
|
13
12
|
import { deleteIdentityKeysInNamespace, extractExplicitNamespace, extractExplicitSessionName, getAgentBrowserSessionIdentityKey, isAgentBrowserSessionIdentityKeyInNamespace, isUpstreamEnvFlagEnabled, resolveAgentBrowserNamespace, scanUpstreamGlobalFlagOccurrences } from "./lib/argv-grammar.js";
|
|
14
13
|
import { parseArgvDescriptor } from "./lib/argv-descriptor.js";
|
|
15
14
|
import { needsManagedSession } from "./lib/command-policy.js";
|
|
16
|
-
import { ManagedSessionRestoreState
|
|
15
|
+
import { ManagedSessionRestoreState } from "./lib/managed-session-restore.js";
|
|
17
16
|
import { isRecord } from "./lib/parsing.js";
|
|
18
17
|
import { getAgentBrowserProcessTimeoutMs, resolveAgentBrowserSocketDir, runAgentBrowserProcess } from "./lib/process.js";
|
|
19
18
|
import { getCommandAwareProcessTimeoutMs } from "./lib/orchestration/browser-run/prepare/wait-timeouts.js";
|
|
20
19
|
import { getAgentBrowserProcessEnvironment, withAgentBrowserProcessEnvironment, withIsolatedAgentBrowserEnvironment } from "./lib/process-environment.js";
|
|
21
20
|
import { withNativeSessionDefaults } from "./lib/orchestration/native-session-defaults.js";
|
|
22
|
-
import { getBrowserCwdError, resolveExecutionCwd
|
|
21
|
+
import { getBrowserCwdError, resolveExecutionCwd } from "./lib/execution-cwd.js";
|
|
23
22
|
import { resolveOperationPaths } from "./lib/orchestration/operation-paths.js";
|
|
23
|
+
import { AsyncExecutionQueue, KeyedAsyncExecutionQueue } from "./lib/orchestration/execution-queue.js";
|
|
24
|
+
import { mergeBrowserRunArtifactManifest } from "./lib/orchestration/browser-run/artifact-merge.js";
|
|
24
25
|
import { closeManagedSession, inspectManagedSessionDaemon } from "./lib/orchestration/browser-run/managed-session-daemon-policy.js";
|
|
25
26
|
import { MINIMUM_AGENT_BROWSER_VERSION, SUPPORTED_AGENT_BROWSER_VERSION_LABEL, TARGET_AGENT_BROWSER_VERSION, getAgentBrowserVersionValidationError, parseAgentBrowserVersionOutput, } from "./lib/upstream-version.js";
|
|
26
|
-
import { buildPromptPolicy,
|
|
27
|
+
import { buildPromptPolicy, getLatestUserMessage, getMessageText } from "./lib/prompt-policy.js";
|
|
27
28
|
import { isCloseAllCommand, isCloseCommand } from "./lib/command-taxonomy.js";
|
|
28
29
|
import { hasLaunchScopedFlagToken } from "./lib/launch-scoped-flags.js";
|
|
29
30
|
import { cleanupSecureTempArtifacts } from "./lib/temp.js";
|
|
@@ -37,14 +38,14 @@ import { applyAgentBrowserOutputPath, canWriteAgentBrowserOutput, normalizeReque
|
|
|
37
38
|
import { appendScriptSessionLease, createBrowserCodeOutput, getScriptSessionLeasesFromBranch } from "./lib/orchestration/script-mode.js";
|
|
38
39
|
import { resolveBrowserExecutionIdentity, withBrowserExecutionLock, withBrowserExecutionLocks } from "./lib/managed-session-policy-lock.js";
|
|
39
40
|
import { AGENT_BROWSER_INSTRUCTION_GROUP, AGENT_BROWSER_TOOL_INVENTORY, registerAgentBrowserToolSurface } from "./lib/tool-surface.js";
|
|
40
|
-
import { formatSessionArtifactRetentionSummary, getSessionArtifactManifestEntryKey,
|
|
41
|
+
import { formatSessionArtifactRetentionSummary, getSessionArtifactManifestEntryKey, isSessionArtifactManifest, retirePendingRecordingManifestEntries } from "./lib/results/artifact-manifest.js";
|
|
41
42
|
import { appendUniqueAgentBrowserNextActions, applyNamespaceToNextActions, applySessionToNextActions, buildNextToolAction } from "./lib/results/next-actions.js";
|
|
42
43
|
import { canRegisterWebSearchTool, loadAgentBrowserConfigSync } from "./lib/config.js";
|
|
43
44
|
import { appendRecordingReservationTransition, applyRecordingArtifactsToReservations, restoreRecordingReservationStateFromBranch, retireRecordingReservation, } from "./lib/recording-reservations.js";
|
|
44
45
|
import { createAgentBrowserWebSearchTool } from "./lib/web-search.js";
|
|
45
46
|
import { scopeReadConfirmationArgs } from "./lib/read-confirmation.js";
|
|
46
47
|
import { isDirectAgentBrowserBashAllowed, isHarmlessAgentBrowserInspectionCommand, looksLikeDirectAgentBrowserBash, } from "./lib/bash-guard.js";
|
|
47
|
-
import { AgentBrowserResultComponent,
|
|
48
|
+
import { AgentBrowserResultComponent, formatAgentBrowserRenderCall, } from "./lib/pi-tool-rendering.js";
|
|
48
49
|
function browserExecutionFailure(error, signal, fallbackCategory = "upstream-error") {
|
|
49
50
|
const timeout = error instanceof Error && error.name === "TimeoutError";
|
|
50
51
|
const summary = redactSensitiveText(error instanceof Error ? error.message : String(error));
|
|
@@ -230,84 +231,6 @@ function getRecognizedCompatibilityWorkaround(value) {
|
|
|
230
231
|
? { id: workaround.id, reason: workaround.reason }
|
|
231
232
|
: undefined;
|
|
232
233
|
}
|
|
233
|
-
function restoreManagedSessionCompatibilityWorkaroundFromBranch(branch, sessionName, namespace) {
|
|
234
|
-
let restored;
|
|
235
|
-
const targetKey = getSessionContextKey(sessionName, namespace);
|
|
236
|
-
for (const entry of branch) {
|
|
237
|
-
const message = getBrowserResultMessage(entry);
|
|
238
|
-
if (!message)
|
|
239
|
-
continue;
|
|
240
|
-
const details = isRecord(message.details) ? message.details : undefined;
|
|
241
|
-
if (!details)
|
|
242
|
-
continue;
|
|
243
|
-
if (getSessionContextKey(typeof details.sessionName === "string" ? details.sessionName : undefined, typeof details.namespace === "string" ? details.namespace : undefined) !== targetKey)
|
|
244
|
-
continue;
|
|
245
|
-
const recognizedWorkaround = getRecognizedCompatibilityWorkaround(details.compatibilityWorkaround);
|
|
246
|
-
const succeeded = getSuccessfulToolResult(details, message);
|
|
247
|
-
const outcome = getManagedSessionOutcome(details);
|
|
248
|
-
const activeAfterFailure = recognizedWorkaround
|
|
249
|
-
&& outcome?.activeAfter === true
|
|
250
|
-
&& typeof outcome.currentSessionName === "string"
|
|
251
|
-
&& getSessionContextKey(outcome.currentSessionName, typeof outcome.currentSessionNamespace === "string" ? outcome.currentSessionNamespace : undefined) === targetKey
|
|
252
|
-
&& (outcome.status === "created" || outcome.status === "replaced" || outcome.status === "unchanged");
|
|
253
|
-
if (!succeeded && !activeAfterFailure)
|
|
254
|
-
continue;
|
|
255
|
-
if (recognizedWorkaround) {
|
|
256
|
-
restored = recognizedWorkaround;
|
|
257
|
-
}
|
|
258
|
-
else if (!canUseHeadlessCompatibilityUserAgent(getToolResultArgs(details))) {
|
|
259
|
-
restored = undefined;
|
|
260
|
-
}
|
|
261
|
-
}
|
|
262
|
-
return restored;
|
|
263
|
-
}
|
|
264
|
-
function restoreManagedSessionHeadedAutosaveDisabledFromBranch(branch, sessionName, namespace) {
|
|
265
|
-
let restored = false;
|
|
266
|
-
const targetKey = getSessionContextKey(sessionName, namespace);
|
|
267
|
-
for (const entry of branch) {
|
|
268
|
-
const message = getBrowserResultMessage(entry);
|
|
269
|
-
if (!message)
|
|
270
|
-
continue;
|
|
271
|
-
const details = isRecord(message.details) ? message.details : undefined;
|
|
272
|
-
if (!details)
|
|
273
|
-
continue;
|
|
274
|
-
if (getSessionContextKey(typeof details.sessionName === "string" ? details.sessionName : undefined, typeof details.namespace === "string" ? details.namespace : undefined) !== targetKey)
|
|
275
|
-
continue;
|
|
276
|
-
const outcome = getManagedSessionOutcome(details);
|
|
277
|
-
const activeAfterFailure = outcome?.activeAfter === true
|
|
278
|
-
&& typeof outcome.currentSessionName === "string"
|
|
279
|
-
&& getSessionContextKey(outcome.currentSessionName, typeof outcome.currentSessionNamespace === "string" ? outcome.currentSessionNamespace : undefined) === targetKey;
|
|
280
|
-
if ((getSuccessfulToolResult(details, message) || activeAfterFailure) && typeof details.managedSessionHeadedAutosaveDisabled === "boolean") {
|
|
281
|
-
restored = details.managedSessionHeadedAutosaveDisabled;
|
|
282
|
-
}
|
|
283
|
-
}
|
|
284
|
-
return restored;
|
|
285
|
-
}
|
|
286
|
-
function restoreManagedSessionHeadedAutosaveIntervalFromBranch(branch, sessionName, namespace) {
|
|
287
|
-
let restored;
|
|
288
|
-
const targetKey = getSessionContextKey(sessionName, namespace);
|
|
289
|
-
for (const entry of branch) {
|
|
290
|
-
const message = getBrowserResultMessage(entry);
|
|
291
|
-
if (!message)
|
|
292
|
-
continue;
|
|
293
|
-
const details = isRecord(message.details) ? message.details : undefined;
|
|
294
|
-
if (!details)
|
|
295
|
-
continue;
|
|
296
|
-
if (getSessionContextKey(typeof details.sessionName === "string" ? details.sessionName : undefined, typeof details.namespace === "string" ? details.namespace : undefined) !== targetKey)
|
|
297
|
-
continue;
|
|
298
|
-
const outcome = getManagedSessionOutcome(details);
|
|
299
|
-
const activeAfterFailure = outcome?.activeAfter === true
|
|
300
|
-
&& typeof outcome.currentSessionName === "string"
|
|
301
|
-
&& getSessionContextKey(outcome.currentSessionName, typeof outcome.currentSessionNamespace === "string" ? outcome.currentSessionNamespace : undefined) === targetKey;
|
|
302
|
-
if (!getSuccessfulToolResult(details, message) && !activeAfterFailure)
|
|
303
|
-
continue;
|
|
304
|
-
if (typeof details.managedSessionHeadedAutosaveInterval === "string")
|
|
305
|
-
restored = details.managedSessionHeadedAutosaveInterval;
|
|
306
|
-
else if (details.managedSessionHeadedAutosaveDisabled === true)
|
|
307
|
-
restored = "0";
|
|
308
|
-
}
|
|
309
|
-
return restored;
|
|
310
|
-
}
|
|
311
234
|
function getToolResultArgs(details) {
|
|
312
235
|
if (Array.isArray(details.args) && details.args.every((arg) => typeof arg === "string"))
|
|
313
236
|
return details.args;
|
|
@@ -385,18 +308,6 @@ function trackOwnedManagedSession(sessions, sessionName, cwd, options = {}) {
|
|
|
385
308
|
const headedManagedAutosaveInterval = options.headedManagedAutosaveInterval ?? existing?.headedManagedAutosaveInterval;
|
|
386
309
|
sessions.set(key, { branchOwned, compatibilityWorkaround, cwd, headedManagedAutosaveDisabled, headedManagedAutosaveInterval, namespace: options.namespace, sessionName, socketDir: options.socketDir ?? existing?.socketDir });
|
|
387
310
|
}
|
|
388
|
-
function restoreManagedSessionSocketDir(branch, sessionName, namespace) {
|
|
389
|
-
const key = getAgentBrowserSessionIdentityKey(sessionName, namespace);
|
|
390
|
-
for (let index = branch.length - 1; index >= 0; index -= 1) {
|
|
391
|
-
const details = getBrowserResultMessage(branch[index])?.details;
|
|
392
|
-
if (!isRecord(details) || typeof details.sessionName !== "string")
|
|
393
|
-
continue;
|
|
394
|
-
if (getAgentBrowserSessionIdentityKey(details.sessionName, typeof details.namespace === "string" ? details.namespace : undefined) === key
|
|
395
|
-
&& typeof details.managedSessionSocketDir === "string")
|
|
396
|
-
return details.managedSessionSocketDir;
|
|
397
|
-
}
|
|
398
|
-
return undefined;
|
|
399
|
-
}
|
|
400
311
|
function untrackOwnedManagedSession(sessions, sessionName, namespace) {
|
|
401
312
|
if (!sessionName)
|
|
402
313
|
return;
|
|
@@ -590,6 +501,7 @@ function collectBranchManagedResourceEvents(branch) {
|
|
|
590
501
|
managedSessionActiveIdentities: new Map(),
|
|
591
502
|
managedSessionActiveRanks: new Map(),
|
|
592
503
|
managedSessionCloseRanks: new Map(),
|
|
504
|
+
managedSessionLaunchState: new Map(),
|
|
593
505
|
};
|
|
594
506
|
let eventRank = 0;
|
|
595
507
|
for (const entry of branch) {
|
|
@@ -611,6 +523,32 @@ function collectBranchManagedResourceEvents(branch) {
|
|
|
611
523
|
const batchCloseLifecycle = getSuccessfulBatchCloseLifecycle(details.batchSteps);
|
|
612
524
|
const closeAllApplied = detailsReportCloseAllApplied(details, succeeded);
|
|
613
525
|
const outcome = getManagedSessionOutcome(details);
|
|
526
|
+
if (sessionName) {
|
|
527
|
+
const key = getSessionContextKey(sessionName, namespace) ?? sessionName;
|
|
528
|
+
const launch = events.managedSessionLaunchState.get(key) ?? { compatibilityWorkaround: undefined, headedManagedAutosaveDisabled: false };
|
|
529
|
+
if (typeof details.managedSessionCwd === "string" && isAbsolute(details.managedSessionCwd))
|
|
530
|
+
launch.cwd = details.managedSessionCwd;
|
|
531
|
+
if (typeof details.managedSessionSocketDir === "string")
|
|
532
|
+
launch.socketDir = details.managedSessionSocketDir;
|
|
533
|
+
const retainedAfterFailure = outcome?.activeAfter === true && typeof outcome.currentSessionName === "string"
|
|
534
|
+
&& getSessionContextKey(outcome.currentSessionName, typeof outcome.currentSessionNamespace === "string" ? outcome.currentSessionNamespace : undefined) === key;
|
|
535
|
+
const workaround = getRecognizedCompatibilityWorkaround(details.compatibilityWorkaround);
|
|
536
|
+
if (succeeded || workaround && retainedAfterFailure && ["created", "replaced", "unchanged"].includes(String(outcome?.status))) {
|
|
537
|
+
if (workaround)
|
|
538
|
+
launch.compatibilityWorkaround = workaround;
|
|
539
|
+
else if (!canUseHeadlessCompatibilityUserAgent(getToolResultArgs(details)))
|
|
540
|
+
launch.compatibilityWorkaround = undefined;
|
|
541
|
+
}
|
|
542
|
+
if (succeeded || retainedAfterFailure) {
|
|
543
|
+
if (typeof details.managedSessionHeadedAutosaveDisabled === "boolean")
|
|
544
|
+
launch.headedManagedAutosaveDisabled = details.managedSessionHeadedAutosaveDisabled;
|
|
545
|
+
if (typeof details.managedSessionHeadedAutosaveInterval === "string")
|
|
546
|
+
launch.headedManagedAutosaveInterval = details.managedSessionHeadedAutosaveInterval;
|
|
547
|
+
else if (details.managedSessionHeadedAutosaveDisabled === true)
|
|
548
|
+
launch.headedManagedAutosaveInterval = "0";
|
|
549
|
+
}
|
|
550
|
+
events.managedSessionLaunchState.set(key, launch);
|
|
551
|
+
}
|
|
614
552
|
const outcomeSucceeded = outcome?.succeeded === true;
|
|
615
553
|
const outcomeStatus = typeof outcome?.status === "string" ? outcome.status : undefined;
|
|
616
554
|
const outcomeCurrentSessionName = typeof outcome?.currentSessionName === "string" ? outcome.currentSessionName : undefined;
|
|
@@ -710,90 +648,6 @@ function shouldSerializeBrowserCommand(options) {
|
|
|
710
648
|
return true;
|
|
711
649
|
return getActiveElectronRecords(options.ownedElectronLaunchRecords).some((record) => record.sessionName === options.explicitSessionName);
|
|
712
650
|
}
|
|
713
|
-
// Serializes managed-session read/modify/write work so overlapping tool calls cannot promote stale state or close an in-use session.
|
|
714
|
-
class AsyncExecutionQueue {
|
|
715
|
-
tail = Promise.resolve();
|
|
716
|
-
active = new AsyncLocalStorage();
|
|
717
|
-
isCurrent() { return this.active.getStore()?.active === true; }
|
|
718
|
-
run(work, signal, barrier = Promise.resolve()) {
|
|
719
|
-
if (this.isCurrent()) {
|
|
720
|
-
signal?.throwIfAborted();
|
|
721
|
-
return work();
|
|
722
|
-
}
|
|
723
|
-
const previous = this.tail;
|
|
724
|
-
let release;
|
|
725
|
-
this.tail = new Promise((resolve) => {
|
|
726
|
-
release = resolve;
|
|
727
|
-
});
|
|
728
|
-
let rejectWaiting;
|
|
729
|
-
const cancelled = new Promise((_resolve, reject) => { rejectWaiting = reject; });
|
|
730
|
-
const abortWaiting = () => rejectWaiting(signal?.reason);
|
|
731
|
-
signal?.addEventListener("abort", abortWaiting, { once: true });
|
|
732
|
-
if (signal?.aborted)
|
|
733
|
-
abortWaiting();
|
|
734
|
-
const execution = (async () => {
|
|
735
|
-
await Promise.all([previous, barrier]);
|
|
736
|
-
signal?.removeEventListener("abort", abortWaiting);
|
|
737
|
-
try {
|
|
738
|
-
signal?.throwIfAborted();
|
|
739
|
-
const scope = { active: true };
|
|
740
|
-
try {
|
|
741
|
-
return await this.active.run(scope, work);
|
|
742
|
-
}
|
|
743
|
-
finally {
|
|
744
|
-
scope.active = false;
|
|
745
|
-
}
|
|
746
|
-
}
|
|
747
|
-
finally {
|
|
748
|
-
release();
|
|
749
|
-
}
|
|
750
|
-
})();
|
|
751
|
-
return signal ? Promise.race([execution, cancelled]) : execution;
|
|
752
|
-
}
|
|
753
|
-
}
|
|
754
|
-
export class KeyedAsyncExecutionQueue {
|
|
755
|
-
barriers = new Map();
|
|
756
|
-
entries = new Map();
|
|
757
|
-
async run(key, namespace, work, signal) {
|
|
758
|
-
const entry = this.entries.get(key) ?? { queue: new AsyncExecutionQueue(), users: 0 };
|
|
759
|
-
if (entry.queue.isCurrent())
|
|
760
|
-
return work();
|
|
761
|
-
const barrier = this.barriers.get(getAgentBrowserSessionIdentityKey("", namespace)) ?? Promise.resolve();
|
|
762
|
-
entry.users += 1;
|
|
763
|
-
this.entries.set(key, entry);
|
|
764
|
-
try {
|
|
765
|
-
return await entry.queue.run(work, signal, barrier);
|
|
766
|
-
}
|
|
767
|
-
finally {
|
|
768
|
-
entry.users -= 1;
|
|
769
|
-
if (entry.users === 0 && this.entries.get(key) === entry)
|
|
770
|
-
this.entries.delete(key);
|
|
771
|
-
}
|
|
772
|
-
}
|
|
773
|
-
async runExclusive(namespace, work) {
|
|
774
|
-
const namespaceKey = getAgentBrowserSessionIdentityKey("", namespace);
|
|
775
|
-
const previous = this.barriers.get(namespaceKey) ?? Promise.resolve();
|
|
776
|
-
let release;
|
|
777
|
-
const blocked = new Promise((resolve) => {
|
|
778
|
-
release = resolve;
|
|
779
|
-
});
|
|
780
|
-
const barrier = previous.then(() => blocked);
|
|
781
|
-
this.barriers.set(namespaceKey, barrier);
|
|
782
|
-
const drains = [...this.entries]
|
|
783
|
-
.filter(([key]) => isAgentBrowserSessionIdentityKeyInNamespace(key, namespace))
|
|
784
|
-
.map(([, { queue }]) => queue.run(async () => undefined));
|
|
785
|
-
await previous;
|
|
786
|
-
await Promise.all(drains);
|
|
787
|
-
try {
|
|
788
|
-
return await work();
|
|
789
|
-
}
|
|
790
|
-
finally {
|
|
791
|
-
release();
|
|
792
|
-
if (this.barriers.get(namespaceKey) === barrier)
|
|
793
|
-
this.barriers.delete(namespaceKey);
|
|
794
|
-
}
|
|
795
|
-
}
|
|
796
|
-
}
|
|
797
651
|
function mergeBrowserRunMap(current, initial, updated) {
|
|
798
652
|
if (updated === initial)
|
|
799
653
|
return current;
|
|
@@ -808,27 +662,6 @@ function mergeBrowserRunMap(current, initial, updated) {
|
|
|
808
662
|
}
|
|
809
663
|
return merged;
|
|
810
664
|
}
|
|
811
|
-
export function mergeBrowserRunArtifactManifest(current, initial, updated) {
|
|
812
|
-
if (!updated || updated === initial)
|
|
813
|
-
return current;
|
|
814
|
-
if (current === initial)
|
|
815
|
-
return updated;
|
|
816
|
-
const initialEntries = new Map((initial?.entries ?? []).map((entry) => [getSessionArtifactManifestEntryKey(entry), entry]));
|
|
817
|
-
const changedEntries = updated.entries
|
|
818
|
-
.map((entry, index) => ({ entry, index }))
|
|
819
|
-
.filter(({ entry }) => initialEntries.get(getSessionArtifactManifestEntryKey(entry)) !== entry)
|
|
820
|
-
.sort((left, right) => left.entry.createdAtMs - right.entry.createdAtMs
|
|
821
|
-
|| Number(isPendingRecordingCommand(left.entry.command, left.entry.subcommand, left.entry.kind)) - Number(isPendingRecordingCommand(right.entry.command, right.entry.subcommand, right.entry.kind))
|
|
822
|
-
|| left.index - right.index)
|
|
823
|
-
.map(({ entry }) => entry);
|
|
824
|
-
return changedEntries.length === 0
|
|
825
|
-
? current
|
|
826
|
-
: mergeSessionArtifactManifest({
|
|
827
|
-
base: current,
|
|
828
|
-
entries: changedEntries,
|
|
829
|
-
nowMs: Math.max(Date.now(), (current?.updatedAtMs ?? 0) + 1, updated.updatedAtMs),
|
|
830
|
-
});
|
|
831
|
-
}
|
|
832
665
|
function findPackageRoot(startDir) {
|
|
833
666
|
let currentDir = startDir;
|
|
834
667
|
while (true) {
|
|
@@ -858,7 +691,7 @@ function hasArgvFlag(argv, longFlag, shortFlag) {
|
|
|
858
691
|
function shouldIncludeProjectConfig(ctx, argv = process.argv) {
|
|
859
692
|
if (hasArgvFlag(argv, "--no-approve", "-na"))
|
|
860
693
|
return false;
|
|
861
|
-
return ctx
|
|
694
|
+
return ctx.isProjectTrusted();
|
|
862
695
|
}
|
|
863
696
|
export default function agentBrowserExtension(pi, { beforeExecute } = {}) {
|
|
864
697
|
const ephemeralSessionSeed = createEphemeralSessionSeed();
|
|
@@ -868,6 +701,21 @@ export default function agentBrowserExtension(pi, { beforeExecute } = {}) {
|
|
|
868
701
|
});
|
|
869
702
|
const implicitSessionIdleTimeoutMs = String(getImplicitSessionIdleTimeoutMs());
|
|
870
703
|
const implicitSessionCloseTimeoutMs = getImplicitSessionCloseTimeoutMs();
|
|
704
|
+
let promptState;
|
|
705
|
+
const restorePromptState = (ctx) => {
|
|
706
|
+
return promptState = { sessionId: ctx.sessionManager.getSessionId(), message: getLatestUserMessage(ctx.sessionManager) };
|
|
707
|
+
};
|
|
708
|
+
const getPromptPolicy = (ctx) => {
|
|
709
|
+
const state = promptState?.sessionId === ctx.sessionManager.getSessionId() ? promptState : restorePromptState(ctx);
|
|
710
|
+
// Native message_end precedes later transforms/persistence; retain the message
|
|
711
|
+
// reference so replacement user text cannot leave policy on the earlier text.
|
|
712
|
+
const text = getMessageText(state.message?.content);
|
|
713
|
+
if (!state.policy || state.text !== text) {
|
|
714
|
+
state.text = text;
|
|
715
|
+
state.policy = buildPromptPolicy(text);
|
|
716
|
+
}
|
|
717
|
+
return state.policy;
|
|
718
|
+
};
|
|
871
719
|
let webSearchToolRegistered = false;
|
|
872
720
|
let managedSessionActive = false;
|
|
873
721
|
let managedSessionBaseName = createImplicitSessionName(undefined, process.cwd(), ephemeralSessionSeed);
|
|
@@ -1121,11 +969,11 @@ export default function agentBrowserExtension(pi, { beforeExecute } = {}) {
|
|
|
1121
969
|
clearSessionScopedBrowserState(sessionName, AGENT_BROWSER_SCRIPT_NAMESPACE);
|
|
1122
970
|
return undefined;
|
|
1123
971
|
};
|
|
1124
|
-
const recoverScriptSessionLeasesWithinQueue = async (ctx) => {
|
|
972
|
+
const recoverScriptSessionLeasesWithinQueue = async (ctx, branch) => {
|
|
1125
973
|
const pendingSessionNames = new Set([...ownedManagedSessions.values()]
|
|
1126
974
|
.map((session) => session.sessionName)
|
|
1127
975
|
.filter(isAgentBrowserScriptSessionName));
|
|
1128
|
-
for (const lease of getScriptSessionLeasesFromBranch(
|
|
976
|
+
for (const lease of getScriptSessionLeasesFromBranch(branch, ctx.sessionManager.getSessionId()).values()) {
|
|
1129
977
|
if (lease.cleanup !== "closed")
|
|
1130
978
|
pendingSessionNames.add(lease.sessionName);
|
|
1131
979
|
}
|
|
@@ -1138,6 +986,7 @@ export default function agentBrowserExtension(pi, { beforeExecute } = {}) {
|
|
|
1138
986
|
const restoreBranchBackedState = async (ctx, options) => {
|
|
1139
987
|
branchRestoreGeneration += 1;
|
|
1140
988
|
branchStateGeneration += 1;
|
|
989
|
+
restorePromptState(ctx);
|
|
1141
990
|
const previousManagedSessionActive = managedSessionActive;
|
|
1142
991
|
const previousManagedSessionName = managedSessionName;
|
|
1143
992
|
const previousFreshSessionOrdinal = freshSessionOrdinal;
|
|
@@ -1179,15 +1028,11 @@ export default function agentBrowserExtension(pi, { beforeExecute } = {}) {
|
|
|
1179
1028
|
: createFreshSessionName(managedSessionBaseName, ephemeralSessionSeed, nextFreshSessionOrdinal)
|
|
1180
1029
|
: restoredState.sessionName;
|
|
1181
1030
|
managedSessionNamespace = shouldReservePostCloseSession ? undefined : restoredState.namespace;
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
managedSessionHeadedAutosaveInterval = managedSessionActive
|
|
1188
|
-
? restoreManagedSessionHeadedAutosaveIntervalFromBranch(branch, managedSessionName, managedSessionNamespace)
|
|
1189
|
-
: undefined;
|
|
1190
|
-
managedSessionCwd = restoreManagedSessionCwd(branch, managedSessionName, managedSessionNamespace, ctx.cwd);
|
|
1031
|
+
const currentLaunch = branchResourceEvents.managedSessionLaunchState.get(getSessionContextKey(managedSessionName, managedSessionNamespace) ?? managedSessionName);
|
|
1032
|
+
managedSessionCompatibilityWorkaround = managedSessionActive ? currentLaunch?.compatibilityWorkaround : undefined;
|
|
1033
|
+
managedSessionHeadedAutosaveDisabled = managedSessionActive && currentLaunch?.headedManagedAutosaveDisabled === true;
|
|
1034
|
+
managedSessionHeadedAutosaveInterval = managedSessionActive ? currentLaunch?.headedManagedAutosaveInterval : undefined;
|
|
1035
|
+
managedSessionCwd = currentLaunch?.cwd ?? ctx.cwd;
|
|
1191
1036
|
freshSessionOrdinal = nextFreshSessionOrdinal;
|
|
1192
1037
|
sessionPageState = SessionPageState.fromBranch(branch);
|
|
1193
1038
|
traceOwners = new Map();
|
|
@@ -1238,13 +1083,14 @@ export default function agentBrowserExtension(pi, { beforeExecute } = {}) {
|
|
|
1238
1083
|
continue;
|
|
1239
1084
|
if (!isRestorableManagedSessionName(identity.sessionName, managedSessionBaseName))
|
|
1240
1085
|
continue;
|
|
1241
|
-
|
|
1086
|
+
const launch = branchResourceEvents.managedSessionLaunchState.get(sessionKey);
|
|
1087
|
+
trackOwnedManagedSession(ownedManagedSessions, identity.sessionName, launch?.cwd ?? ctx.cwd, {
|
|
1242
1088
|
branchOwned: true,
|
|
1243
|
-
compatibilityWorkaround:
|
|
1244
|
-
headedManagedAutosaveDisabled:
|
|
1245
|
-
headedManagedAutosaveInterval:
|
|
1089
|
+
compatibilityWorkaround: launch?.compatibilityWorkaround,
|
|
1090
|
+
headedManagedAutosaveDisabled: launch?.headedManagedAutosaveDisabled ?? false,
|
|
1091
|
+
headedManagedAutosaveInterval: launch?.headedManagedAutosaveInterval,
|
|
1246
1092
|
namespace: identity.namespace,
|
|
1247
|
-
socketDir:
|
|
1093
|
+
socketDir: launch?.socketDir,
|
|
1248
1094
|
});
|
|
1249
1095
|
}
|
|
1250
1096
|
if (restoredState.active) {
|
|
@@ -1254,7 +1100,7 @@ export default function agentBrowserExtension(pi, { beforeExecute } = {}) {
|
|
|
1254
1100
|
headedManagedAutosaveDisabled: managedSessionHeadedAutosaveDisabled,
|
|
1255
1101
|
headedManagedAutosaveInterval: managedSessionHeadedAutosaveInterval,
|
|
1256
1102
|
namespace: restoredState.namespace,
|
|
1257
|
-
socketDir:
|
|
1103
|
+
socketDir: currentLaunch?.socketDir,
|
|
1258
1104
|
});
|
|
1259
1105
|
}
|
|
1260
1106
|
const pendingOwnedBegins = new Map();
|
|
@@ -1290,11 +1136,12 @@ export default function agentBrowserExtension(pi, { beforeExecute } = {}) {
|
|
|
1290
1136
|
const closeRank = branchResourceEvents.managedSessionCloseRanks.get(sessionKey);
|
|
1291
1137
|
if (activeRank === undefined || (closeRank !== undefined && closeRank >= activeRank))
|
|
1292
1138
|
continue;
|
|
1293
|
-
|
|
1139
|
+
const launch = branchResourceEvents.managedSessionLaunchState.get(sessionKey);
|
|
1140
|
+
trackOwnedManagedSession(ownedManagedSessions, record.sessionName, launch?.cwd ?? ctx.cwd, {
|
|
1294
1141
|
branchOwned: true,
|
|
1295
|
-
headedManagedAutosaveDisabled:
|
|
1296
|
-
headedManagedAutosaveInterval:
|
|
1297
|
-
socketDir:
|
|
1142
|
+
headedManagedAutosaveDisabled: launch?.headedManagedAutosaveDisabled ?? false,
|
|
1143
|
+
headedManagedAutosaveInterval: launch?.headedManagedAutosaveInterval,
|
|
1144
|
+
socketDir: launch?.socketDir,
|
|
1298
1145
|
});
|
|
1299
1146
|
}
|
|
1300
1147
|
mergeActiveElectronLaunchRecords(ownedElectronLaunchRecords, new Map([...electronLaunchRecords].filter(([, record]) => record.ownerSessionId === browserOwnerSessionId)), {
|
|
@@ -1323,6 +1170,7 @@ export default function agentBrowserExtension(pi, { beforeExecute } = {}) {
|
|
|
1323
1170
|
attachedSessionKeys.add(sessionKey);
|
|
1324
1171
|
}
|
|
1325
1172
|
}
|
|
1173
|
+
return branch;
|
|
1326
1174
|
};
|
|
1327
1175
|
const registerWebSearchToolIfAvailable = (configState) => {
|
|
1328
1176
|
if (webSearchToolRegistered || !canRegisterWebSearchTool(configState))
|
|
@@ -1339,15 +1187,19 @@ export default function agentBrowserExtension(pi, { beforeExecute } = {}) {
|
|
|
1339
1187
|
});
|
|
1340
1188
|
webSearchToolRegistered = true;
|
|
1341
1189
|
};
|
|
1190
|
+
pi.on("message_end", (event, ctx) => {
|
|
1191
|
+
if (event.message.role === "user")
|
|
1192
|
+
promptState = { sessionId: ctx.sessionManager.getSessionId(), message: event.message };
|
|
1193
|
+
});
|
|
1342
1194
|
pi.on("session_start", async (_event, ctx) => {
|
|
1343
|
-
await restoreBranchBackedState(ctx, { resetRuntimeOwnership: true });
|
|
1344
|
-
electronChildProcesses = new Map();
|
|
1345
|
-
registerWebSearchToolIfAvailable(loadAgentBrowserConfigSync({
|
|
1346
|
-
cwd: ctx.cwd,
|
|
1347
|
-
includeProjectConfig: shouldIncludeProjectConfig(ctx),
|
|
1348
|
-
}));
|
|
1349
1195
|
await managedSessionExecutionQueue.run(() => artifactExecutionQueue.run(async () => {
|
|
1350
|
-
await
|
|
1196
|
+
const branch = await restoreBranchBackedState(ctx, { resetRuntimeOwnership: true });
|
|
1197
|
+
electronChildProcesses = new Map();
|
|
1198
|
+
registerWebSearchToolIfAvailable(loadAgentBrowserConfigSync({
|
|
1199
|
+
cwd: ctx.cwd,
|
|
1200
|
+
includeProjectConfig: shouldIncludeProjectConfig(ctx),
|
|
1201
|
+
}));
|
|
1202
|
+
await recoverScriptSessionLeasesWithinQueue(ctx, branch);
|
|
1351
1203
|
flushRecordingReservations();
|
|
1352
1204
|
notifyRecordingPersistence(ctx);
|
|
1353
1205
|
}));
|
|
@@ -1358,87 +1210,12 @@ export default function agentBrowserExtension(pi, { beforeExecute } = {}) {
|
|
|
1358
1210
|
controller.abort();
|
|
1359
1211
|
await Promise.allSettled([...activeScriptExecutions]);
|
|
1360
1212
|
await managedSessionExecutionQueue.run(() => artifactExecutionQueue.run(async () => {
|
|
1361
|
-
await restoreBranchBackedState(ctx, { resetRuntimeOwnership: false });
|
|
1362
|
-
await recoverScriptSessionLeasesWithinQueue(ctx);
|
|
1213
|
+
const branch = await restoreBranchBackedState(ctx, { resetRuntimeOwnership: false });
|
|
1214
|
+
await recoverScriptSessionLeasesWithinQueue(ctx, branch);
|
|
1363
1215
|
flushRecordingReservations();
|
|
1364
1216
|
notifyRecordingPersistence(ctx);
|
|
1365
1217
|
}));
|
|
1366
1218
|
});
|
|
1367
|
-
// Fork-only checkpoint event; official Pi validation types do not declare it.
|
|
1368
|
-
// Pi owns awaited tools/events (including their execution queues). Detached code
|
|
1369
|
-
// children and retained browser/recording resources still need extension-side checks.
|
|
1370
|
-
const checkpointPi = pi;
|
|
1371
|
-
checkpointPi.on("session_checkpoint", async (event, ctx) => {
|
|
1372
|
-
const blocked = (reason) => ({ sleepReady: false, reason });
|
|
1373
|
-
if (browserReplayError)
|
|
1374
|
-
return blocked(browserReplayError);
|
|
1375
|
-
if (activeScriptControllers.size || activeScriptExecutions.size)
|
|
1376
|
-
return blocked("Browser code execution is pending");
|
|
1377
|
-
flushRecordingReservations();
|
|
1378
|
-
if (recordingReservationsDirty)
|
|
1379
|
-
return blocked("Browser recording journal persistence is dirty");
|
|
1380
|
-
if (activeRecordingReservations.size)
|
|
1381
|
-
return blocked("Browser recording is pending; finish it explicitly before sleep");
|
|
1382
|
-
const entries = await readBrowserEntries(ctx.sessionManager, true);
|
|
1383
|
-
if ([...getScriptSessionLeasesFromBranch(entries).values()].some(lease => lease.cleanup !== "closed")
|
|
1384
|
-
|| [...ownedManagedSessions.values()].some(owner => isAgentBrowserScriptSessionName(owner.sessionName))) {
|
|
1385
|
-
return blocked("Browser script cleanup lease is unresolved");
|
|
1386
|
-
}
|
|
1387
|
-
if (getActiveElectronRecords(ownedElectronLaunchRecords).length || getActiveElectronRecords(electronLaunchRecords).length
|
|
1388
|
-
|| [...electronChildProcesses.values()].some(child => child.exitCode === null && child.signalCode === null)) {
|
|
1389
|
-
return blocked("Electron launch is still active");
|
|
1390
|
-
}
|
|
1391
|
-
if (attachedSessionKeys.size || restoreAttachedSessionKeysFromBranch(entries).size)
|
|
1392
|
-
return blocked("Attached browser state is caller-owned and not checkpointed");
|
|
1393
|
-
if (traceOwners.size || [...networkRoutesBySession.values()].some(routes => routes.length))
|
|
1394
|
-
return blocked("Browser trace or network routes are still active");
|
|
1395
|
-
// Include off-branch and caller-owned/root identities, without acquiring
|
|
1396
|
-
// cleanup ownership. Transcript page/ref details do not serialize a browser.
|
|
1397
|
-
const historicalResources = collectBranchManagedResourceEvents(entries);
|
|
1398
|
-
const sessions = new Map(ownedManagedSessions);
|
|
1399
|
-
if (managedSessionActive)
|
|
1400
|
-
trackOwnedManagedSession(sessions, managedSessionName, managedSessionCwd, { namespace: managedSessionNamespace });
|
|
1401
|
-
for (const entry of entries) {
|
|
1402
|
-
const message = getBrowserResultMessage(entry);
|
|
1403
|
-
if (!message)
|
|
1404
|
-
continue;
|
|
1405
|
-
const details = isRecord(message.details) ? message.details : undefined;
|
|
1406
|
-
// Helpers can launch before a main-process failure. Conversely, native
|
|
1407
|
-
// launch flags on sessionless reads can open the unnamed default browser.
|
|
1408
|
-
const sessionName = typeof details?.sessionName === "string" ? details.sessionName
|
|
1409
|
-
: details?.agentBrowserStarted === true ? "default" : undefined;
|
|
1410
|
-
if (!sessionName)
|
|
1411
|
-
continue;
|
|
1412
|
-
const namespace = typeof details?.namespace === "string" ? details.namespace : undefined;
|
|
1413
|
-
const key = getSessionContextKey(sessionName, namespace) ?? sessionName;
|
|
1414
|
-
if (!sessions.has(key))
|
|
1415
|
-
trackOwnedManagedSession(sessions, sessionName, ctx.cwd, { namespace });
|
|
1416
|
-
}
|
|
1417
|
-
for (const [key, owner] of sessions) {
|
|
1418
|
-
event.signal.throwIfAborted();
|
|
1419
|
-
// An abnormal restart can leave a wrapper-created browser only off-branch.
|
|
1420
|
-
// Reuse native ownership events for inspection, never cleanup ownership.
|
|
1421
|
-
// A later close can refer to the same identity in a different socket root.
|
|
1422
|
-
const historicalIdentity = historicalResources.managedSessionActiveIdentities.get(key);
|
|
1423
|
-
const historicallyOwned = historicalIdentity && isRestorableManagedSessionName(historicalIdentity.sessionName, managedSessionBaseName);
|
|
1424
|
-
const context = resolveOwnedManagedSessionContext({
|
|
1425
|
-
...owner,
|
|
1426
|
-
currentManagedSessionName: managedSessionActive ? managedSessionName : undefined,
|
|
1427
|
-
currentManagedSessionNamespace: managedSessionNamespace,
|
|
1428
|
-
recordedOwnedSession: ownedManagedSessions.get(key) ?? (historicallyOwned ? { ...historicalIdentity, cwd: ctx.cwd } : undefined),
|
|
1429
|
-
restoreState: managedSessionRestoreState,
|
|
1430
|
-
});
|
|
1431
|
-
// Owned routing must not mask an explicit caller's same-name ambient daemon.
|
|
1432
|
-
for (const inspectionContext of context ? [{ ...context, reuseOnly: true }, undefined] : [undefined]) {
|
|
1433
|
-
const daemon = await withAgentBrowserProcessEnvironment(inspectionContext && owner.socketDir ? { PI_AGENT_BROWSER_SOCKET_DIR: owner.socketDir } : {}, () => withOwnedManagedSessionContext(inspectionContext, () => inspectManagedSessionDaemon({ ...owner, signal: event.signal, timeoutMs: 2_000 })));
|
|
1434
|
-
if (daemon.status !== "inactive")
|
|
1435
|
-
return blocked("Browser daemon is live or unverified; finish browser work explicitly before sleep");
|
|
1436
|
-
}
|
|
1437
|
-
}
|
|
1438
|
-
// No detached writer remains to pause/resume. Native ingress stays held;
|
|
1439
|
-
// checkpoint never closes a browser or changes ordinary shutdown ownership.
|
|
1440
|
-
return { sleepReady: true };
|
|
1441
|
-
});
|
|
1442
1219
|
pi.on("session_shutdown", async (event, ctx) => {
|
|
1443
1220
|
for (const controller of activeScriptControllers)
|
|
1444
1221
|
controller.abort();
|
|
@@ -1548,11 +1325,11 @@ export default function agentBrowserExtension(pi, { beforeExecute } = {}) {
|
|
|
1548
1325
|
event.systemPromptOptions.sections.agent_browser = fullInstructions(ctx);
|
|
1549
1326
|
});
|
|
1550
1327
|
pi.on("tool_call", async (event, ctx) => {
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1328
|
+
if (!isBashToolCallEvent(event) || !looksLikeDirectAgentBrowserBash(event.input.command)
|
|
1329
|
+
|| isHarmlessAgentBrowserInspectionCommand(event.input.command))
|
|
1330
|
+
return;
|
|
1331
|
+
const promptPolicy = getPromptPolicy(ctx);
|
|
1332
|
+
if (!promptPolicy.allowLegacyAgentBrowserBash &&
|
|
1556
1333
|
!(await isDirectAgentBrowserBashAllowed(ctx.cwd))) {
|
|
1557
1334
|
return {
|
|
1558
1335
|
block: true,
|
|
@@ -1560,7 +1337,6 @@ export default function agentBrowserExtension(pi, { beforeExecute } = {}) {
|
|
|
1560
1337
|
};
|
|
1561
1338
|
}
|
|
1562
1339
|
});
|
|
1563
|
-
pi.on("tool_result", async (event) => buildAgentBrowserToolResultPatch(event));
|
|
1564
1340
|
let emptyBrowserBranch;
|
|
1565
1341
|
const captureCurrentBrowserBranch = (ctx) => {
|
|
1566
1342
|
const generation = branchRestoreGeneration;
|
|
@@ -1583,7 +1359,7 @@ export default function agentBrowserExtension(pi, { beforeExecute } = {}) {
|
|
|
1583
1359
|
const component = context.lastComponent instanceof AgentBrowserResultComponent
|
|
1584
1360
|
? context.lastComponent
|
|
1585
1361
|
: new AgentBrowserResultComponent();
|
|
1586
|
-
component.
|
|
1362
|
+
component.setResult(result, options, theme, context.isError);
|
|
1587
1363
|
return component;
|
|
1588
1364
|
},
|
|
1589
1365
|
async execute(toolCallId, params, signal, onUpdate, ctx, nativeToolCallId = toolCallId, capturedCwd, modelVisible = true, commandIndex, selectedBranch) {
|
|
@@ -1595,7 +1371,7 @@ export default function agentBrowserExtension(pi, { beforeExecute } = {}) {
|
|
|
1595
1371
|
catch (error) {
|
|
1596
1372
|
return buildValidationFailureResult({ kind: "invalid", status: "invalid", redactedArgs: [], toolArgs: [], validationError: error instanceof Error ? error.message : String(error) });
|
|
1597
1373
|
}
|
|
1598
|
-
const promptPolicy =
|
|
1374
|
+
const promptPolicy = getPromptPolicy(ctx);
|
|
1599
1375
|
const outputPath = isRecord(params) && typeof params.outputPath === "string" ? params.outputPath : undefined;
|
|
1600
1376
|
const admittedInput = resolveAgentBrowserInput({
|
|
1601
1377
|
getBatchPreflightValidationError: (args, stdin) => getArtifactPreflightValidationError({ args, cwd: operationCwd, outputPath, stdin }),
|
|
@@ -2266,10 +2042,10 @@ export default function agentBrowserExtension(pi, { beforeExecute } = {}) {
|
|
|
2266
2042
|
}) };
|
|
2267
2043
|
const rendered = await renderAgentBrowserObservation(renderOptions);
|
|
2268
2044
|
if (!priorManifest && !rendered.artifactManifest)
|
|
2269
|
-
return { ...result, content: rendered.content, details: { ...details, browserEventVersion: 1 } };
|
|
2045
|
+
return { ...result, content: rendered.content, structuredContent: rendered.structuredContent, details: { ...details, browserEventVersion: 1 } };
|
|
2270
2046
|
return artifactExecutionQueue.run(async () => {
|
|
2271
2047
|
if (!branch.isCurrent())
|
|
2272
|
-
return { ...result, content: rendered.content, details: { ...details, browserEventVersion: 1, artifactManifest: rendered.artifactManifest } };
|
|
2048
|
+
return { ...result, content: rendered.content, structuredContent: rendered.structuredContent, details: { ...details, browserEventVersion: 1, artifactManifest: rendered.artifactManifest } };
|
|
2273
2049
|
const before = artifactManifest;
|
|
2274
2050
|
const merged = mergeBrowserRunArtifactManifest(artifactManifest, priorManifest, rendered.artifactManifest);
|
|
2275
2051
|
const changes = artifactChanges(before, merged);
|
|
@@ -2277,10 +2053,10 @@ export default function agentBrowserExtension(pi, { beforeExecute } = {}) {
|
|
|
2277
2053
|
const record = { event: { version: 1, phase: "state", operationId: randomUUID(), toolCallId: "observation", commandIndex: 0, isError: result.isError === true, state: {}, artifacts: changes } };
|
|
2278
2054
|
if (!await appendBrowserRecord(ctx.sessionManager, () => appendBrowserTransition(pi, record), record, branch)
|
|
2279
2055
|
|| !branch.isCurrent())
|
|
2280
|
-
return { ...result, content: rendered.content, details: { ...details, browserEventVersion: 1, artifactManifest: rendered.artifactManifest } };
|
|
2056
|
+
return { ...result, content: rendered.content, structuredContent: rendered.structuredContent, details: { ...details, browserEventVersion: 1, artifactManifest: rendered.artifactManifest } };
|
|
2281
2057
|
}
|
|
2282
2058
|
artifactManifest = mergeBrowserRunArtifactManifest(artifactManifest, priorManifest, rendered.artifactManifest);
|
|
2283
|
-
return { ...result, content: rendered.content, details: { ...details, browserEventVersion: 1, artifactManifest: invocationArtifactManifest(artifactManifest, before, details, priorManifest) } };
|
|
2059
|
+
return { ...result, content: rendered.content, structuredContent: rendered.structuredContent, details: { ...details, browserEventVersion: 1, artifactManifest: invocationArtifactManifest(artifactManifest, before, details, priorManifest) } };
|
|
2284
2060
|
});
|
|
2285
2061
|
};
|
|
2286
2062
|
registerAgentBrowserToolSurface(pi, {
|
|
@@ -76,7 +76,7 @@ function getOpenCommandTarget(commandTokens) {
|
|
|
76
76
|
}
|
|
77
77
|
return undefined;
|
|
78
78
|
}
|
|
79
|
-
|
|
79
|
+
function parseCommandInfoFromTokens(commandTokens) {
|
|
80
80
|
const upstreamCommandTokens = stripUpstreamGlobalFlags(commandTokens);
|
|
81
81
|
const command = upstreamCommandTokens[0];
|
|
82
82
|
return {
|