pi-agent-browser-native 0.2.47 → 0.2.49
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 +63 -19
- package/README.md +52 -19
- package/dist/extensions/agent-browser/index.js +785 -0
- package/dist/extensions/agent-browser/lib/argv-descriptor.js +71 -0
- package/dist/extensions/agent-browser/lib/argv-grammar.js +121 -0
- package/dist/extensions/agent-browser/lib/bash-guard.js +190 -0
- package/dist/extensions/agent-browser/lib/command-policy.js +85 -0
- package/dist/extensions/agent-browser/lib/command-taxonomy.js +302 -0
- package/dist/extensions/agent-browser/lib/config-policy.js +686 -0
- package/dist/extensions/agent-browser/lib/config.js +122 -0
- package/dist/extensions/agent-browser/lib/electron/cdp.js +51 -0
- package/dist/extensions/agent-browser/lib/electron/cleanup.js +212 -0
- package/dist/extensions/agent-browser/lib/electron/discovery.js +633 -0
- package/dist/extensions/agent-browser/lib/electron/launch.js +351 -0
- package/{extensions/agent-browser/lib/electron/text.ts → dist/extensions/agent-browser/lib/electron/text.js} +5 -5
- package/dist/extensions/agent-browser/lib/executable-path.js +20 -0
- package/dist/extensions/agent-browser/lib/fs-utils.js +18 -0
- package/dist/extensions/agent-browser/lib/input-modes/electron.js +165 -0
- package/dist/extensions/agent-browser/lib/input-modes/job.js +519 -0
- package/dist/extensions/agent-browser/lib/input-modes/lookups.js +440 -0
- package/dist/extensions/agent-browser/lib/input-modes/params.js +164 -0
- package/dist/extensions/agent-browser/lib/input-modes/semantic-action.js +119 -0
- package/dist/extensions/agent-browser/lib/input-modes/shared.js +42 -0
- package/dist/extensions/agent-browser/lib/input-modes/types.js +21 -0
- package/dist/extensions/agent-browser/lib/input-modes.js +10 -0
- package/dist/extensions/agent-browser/lib/json-schema.js +58 -0
- package/dist/extensions/agent-browser/lib/launch-scoped-flags.js +59 -0
- package/dist/extensions/agent-browser/lib/navigation-policy.js +83 -0
- package/dist/extensions/agent-browser/lib/orchestration/batch-stdin.js +62 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/artifact-paths.js +39 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/click-dispatch.js +276 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.js +909 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result.js +443 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/index.js +47 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/direct-anchor-download.js +141 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/network-page-filter.js +108 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/scroll-shims.js +112 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/snapshot-filter.js +158 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/wait-timeouts.js +54 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare.js +762 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output.js +491 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prompt-guards.js +40 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-artifacts.js +5 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-state.js +731 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/types.js +1 -0
- package/dist/extensions/agent-browser/lib/orchestration/electron-host/index.js +718 -0
- package/dist/extensions/agent-browser/lib/orchestration/input-plan.js +247 -0
- package/dist/extensions/agent-browser/lib/orchestration/output-file.js +68 -0
- package/{extensions/agent-browser/lib/parsing.ts → dist/extensions/agent-browser/lib/parsing.js} +12 -11
- package/dist/extensions/agent-browser/lib/pi-tool-rendering.js +241 -0
- package/dist/extensions/agent-browser/lib/playbook.js +121 -0
- package/dist/extensions/agent-browser/lib/process.js +448 -0
- package/dist/extensions/agent-browser/lib/prompt-policy.js +91 -0
- package/dist/extensions/agent-browser/lib/results/action-recommendations.js +220 -0
- package/dist/extensions/agent-browser/lib/results/artifact-manifest.js +111 -0
- package/{extensions/agent-browser/lib/results/artifact-state.ts → dist/extensions/agent-browser/lib/results/artifact-state.js} +4 -8
- package/dist/extensions/agent-browser/lib/results/categories.js +76 -0
- package/dist/extensions/agent-browser/lib/results/confirmation.js +63 -0
- package/dist/extensions/agent-browser/lib/results/contracts.js +8 -0
- package/dist/extensions/agent-browser/lib/results/editable-ref-evidence.js +74 -0
- package/dist/extensions/agent-browser/lib/results/envelope.js +166 -0
- package/dist/extensions/agent-browser/lib/results/network-routes.js +92 -0
- package/dist/extensions/agent-browser/lib/results/network.js +73 -0
- package/dist/extensions/agent-browser/lib/results/next-actions.js +72 -0
- package/dist/extensions/agent-browser/lib/results/presentation/artifacts.js +515 -0
- package/dist/extensions/agent-browser/lib/results/presentation/batch.js +397 -0
- package/dist/extensions/agent-browser/lib/results/presentation/browser-profile-recovery.js +55 -0
- package/dist/extensions/agent-browser/lib/results/presentation/common.js +46 -0
- package/dist/extensions/agent-browser/lib/results/presentation/content.js +24 -0
- package/dist/extensions/agent-browser/lib/results/presentation/diagnostics.js +960 -0
- package/dist/extensions/agent-browser/lib/results/presentation/errors.js +205 -0
- package/dist/extensions/agent-browser/lib/results/presentation/large-output.js +134 -0
- package/dist/extensions/agent-browser/lib/results/presentation/navigation.js +159 -0
- package/dist/extensions/agent-browser/lib/results/presentation/registry.js +216 -0
- package/dist/extensions/agent-browser/lib/results/presentation/semantic-action.js +104 -0
- package/dist/extensions/agent-browser/lib/results/presentation/skills.js +152 -0
- package/dist/extensions/agent-browser/lib/results/presentation.js +177 -0
- package/dist/extensions/agent-browser/lib/results/recovery-actions.js +107 -0
- package/dist/extensions/agent-browser/lib/results/recovery-next-actions.js +50 -0
- package/dist/extensions/agent-browser/lib/results/selector-recovery.js +225 -0
- package/{extensions/agent-browser/lib/results/shared.ts → dist/extensions/agent-browser/lib/results/shared.js} +0 -1
- package/dist/extensions/agent-browser/lib/results/snapshot-high-value-controls.js +208 -0
- package/dist/extensions/agent-browser/lib/results/snapshot-refs.js +78 -0
- package/dist/extensions/agent-browser/lib/results/snapshot-segments.js +331 -0
- package/dist/extensions/agent-browser/lib/results/snapshot-spill.js +40 -0
- package/dist/extensions/agent-browser/lib/results/snapshot.js +264 -0
- package/dist/extensions/agent-browser/lib/results/text.js +40 -0
- package/{extensions/agent-browser/lib/results.ts → dist/extensions/agent-browser/lib/results.js} +2 -32
- package/dist/extensions/agent-browser/lib/runtime.js +816 -0
- package/dist/extensions/agent-browser/lib/session-page-state.js +411 -0
- package/dist/extensions/agent-browser/lib/string-enum-schema.js +13 -0
- package/dist/extensions/agent-browser/lib/temp.js +498 -0
- package/dist/extensions/agent-browser/lib/web-search.js +562 -0
- package/docs/ARCHITECTURE.md +10 -10
- package/docs/COMMAND_REFERENCE.md +35 -21
- package/docs/ELECTRON.md +3 -3
- package/docs/RELEASE.md +46 -26
- package/docs/REQUIREMENTS.md +1 -1
- package/docs/SUPPORT_MATRIX.md +35 -106
- package/docs/TOOL_CONTRACT.md +23 -21
- package/package.json +12 -8
- package/scripts/agent-browser-capability-baseline.mjs +6 -3
- package/scripts/config.mjs +8 -2
- package/scripts/doctor.mjs +19 -17
- package/scripts/platform-smoke.mjs +1 -1
- package/extensions/agent-browser/index.ts +0 -952
- package/extensions/agent-browser/lib/argv-descriptor.ts +0 -90
- package/extensions/agent-browser/lib/argv-grammar.ts +0 -128
- package/extensions/agent-browser/lib/bash-guard.ts +0 -205
- package/extensions/agent-browser/lib/command-policy.ts +0 -71
- package/extensions/agent-browser/lib/command-taxonomy.ts +0 -336
- package/extensions/agent-browser/lib/config-policy.js +0 -690
- package/extensions/agent-browser/lib/config.ts +0 -209
- package/extensions/agent-browser/lib/electron/cdp.ts +0 -69
- package/extensions/agent-browser/lib/electron/cleanup.ts +0 -235
- package/extensions/agent-browser/lib/electron/discovery.ts +0 -710
- package/extensions/agent-browser/lib/electron/launch.ts +0 -499
- package/extensions/agent-browser/lib/executable-path.ts +0 -19
- package/extensions/agent-browser/lib/fs-utils.ts +0 -18
- package/extensions/agent-browser/lib/input-modes/electron.ts +0 -170
- package/extensions/agent-browser/lib/input-modes/job.ts +0 -451
- package/extensions/agent-browser/lib/input-modes/lookups.ts +0 -447
- package/extensions/agent-browser/lib/input-modes/params.ts +0 -205
- package/extensions/agent-browser/lib/input-modes/semantic-action.ts +0 -127
- package/extensions/agent-browser/lib/input-modes/shared.ts +0 -46
- package/extensions/agent-browser/lib/input-modes/types.ts +0 -225
- package/extensions/agent-browser/lib/input-modes.ts +0 -45
- package/extensions/agent-browser/lib/json-schema.ts +0 -73
- package/extensions/agent-browser/lib/launch-scoped-flags.ts +0 -67
- package/extensions/agent-browser/lib/navigation-policy.ts +0 -95
- package/extensions/agent-browser/lib/orchestration/batch-stdin.ts +0 -65
- package/extensions/agent-browser/lib/orchestration/browser-run/click-dispatch.ts +0 -257
- package/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.ts +0 -912
- package/extensions/agent-browser/lib/orchestration/browser-run/final-result.ts +0 -512
- package/extensions/agent-browser/lib/orchestration/browser-run/index.ts +0 -53
- package/extensions/agent-browser/lib/orchestration/browser-run/prepare.ts +0 -1481
- package/extensions/agent-browser/lib/orchestration/browser-run/process-output.ts +0 -564
- package/extensions/agent-browser/lib/orchestration/browser-run/prompt-guards.ts +0 -47
- package/extensions/agent-browser/lib/orchestration/browser-run/session-state.ts +0 -868
- package/extensions/agent-browser/lib/orchestration/browser-run/types.ts +0 -564
- package/extensions/agent-browser/lib/orchestration/electron-host/index.ts +0 -855
- package/extensions/agent-browser/lib/orchestration/input-plan.ts +0 -375
- package/extensions/agent-browser/lib/orchestration/output-file.ts +0 -86
- package/extensions/agent-browser/lib/pi-tool-rendering.ts +0 -252
- package/extensions/agent-browser/lib/playbook.ts +0 -142
- package/extensions/agent-browser/lib/process.ts +0 -516
- package/extensions/agent-browser/lib/prompt-policy.ts +0 -105
- package/extensions/agent-browser/lib/results/action-recommendations.ts +0 -264
- package/extensions/agent-browser/lib/results/artifact-manifest.ts +0 -111
- package/extensions/agent-browser/lib/results/categories.ts +0 -106
- package/extensions/agent-browser/lib/results/confirmation.ts +0 -76
- package/extensions/agent-browser/lib/results/contracts.ts +0 -241
- package/extensions/agent-browser/lib/results/editable-ref-evidence.ts +0 -72
- package/extensions/agent-browser/lib/results/envelope.ts +0 -195
- package/extensions/agent-browser/lib/results/network-routes.ts +0 -83
- package/extensions/agent-browser/lib/results/network.ts +0 -78
- package/extensions/agent-browser/lib/results/next-actions.ts +0 -117
- package/extensions/agent-browser/lib/results/presentation/artifacts.ts +0 -588
- package/extensions/agent-browser/lib/results/presentation/batch.ts +0 -450
- package/extensions/agent-browser/lib/results/presentation/browser-profile-recovery.ts +0 -67
- package/extensions/agent-browser/lib/results/presentation/common.ts +0 -53
- package/extensions/agent-browser/lib/results/presentation/content.ts +0 -36
- package/extensions/agent-browser/lib/results/presentation/diagnostics.ts +0 -923
- package/extensions/agent-browser/lib/results/presentation/errors.ts +0 -227
- package/extensions/agent-browser/lib/results/presentation/large-output.ts +0 -182
- package/extensions/agent-browser/lib/results/presentation/navigation.ts +0 -184
- package/extensions/agent-browser/lib/results/presentation/registry.ts +0 -242
- package/extensions/agent-browser/lib/results/presentation/semantic-action.ts +0 -131
- package/extensions/agent-browser/lib/results/presentation/skills.ts +0 -143
- package/extensions/agent-browser/lib/results/presentation.ts +0 -257
- package/extensions/agent-browser/lib/results/recovery-actions.ts +0 -139
- package/extensions/agent-browser/lib/results/recovery-next-actions.ts +0 -71
- package/extensions/agent-browser/lib/results/selector-recovery.ts +0 -320
- package/extensions/agent-browser/lib/results/snapshot-high-value-controls.ts +0 -273
- package/extensions/agent-browser/lib/results/snapshot-refs.ts +0 -100
- package/extensions/agent-browser/lib/results/snapshot-segments.ts +0 -366
- package/extensions/agent-browser/lib/results/snapshot-spill.ts +0 -63
- package/extensions/agent-browser/lib/results/snapshot.ts +0 -329
- package/extensions/agent-browser/lib/results/text.ts +0 -40
- package/extensions/agent-browser/lib/runtime.ts +0 -988
- package/extensions/agent-browser/lib/session-page-state.ts +0 -512
- package/extensions/agent-browser/lib/string-enum-schema.ts +0 -20
- package/extensions/agent-browser/lib/temp.ts +0 -577
- package/extensions/agent-browser/lib/web-search.ts +0 -721
- /package/{extensions/agent-browser/lib/orchestration/browser-run.ts → dist/extensions/agent-browser/lib/orchestration/browser-run.js} +0 -0
|
@@ -1,952 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Purpose: Register the native agent_browser tool for pi so agents can invoke agent-browser without going through bash.
|
|
3
|
-
* Responsibilities: Define the tool schema, inject thin wrapper behavior around the upstream CLI, manage extension-owned browser session convenience, and return pi-friendly content/details.
|
|
4
|
-
* Scope: Native tool registration and orchestration only; the wrapper intentionally stays close to the upstream agent-browser CLI.
|
|
5
|
-
* Usage: Loaded by pi through the package manifest in this package, or explicitly via `pi --no-extensions -e .` during local checkout development.
|
|
6
|
-
* Invariants/Assumptions: agent-browser is installed separately on PATH, the wrapper targets the current locally installed upstream version only, and no backward-compatibility shims are provided.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import type { ChildProcess } from "node:child_process";
|
|
10
|
-
import { dirname, join, resolve } from "node:path";
|
|
11
|
-
import { fileURLToPath } from "node:url";
|
|
12
|
-
|
|
13
|
-
import type {
|
|
14
|
-
AgentToolResult,
|
|
15
|
-
ExtensionAPI,
|
|
16
|
-
ExtensionContext,
|
|
17
|
-
} from "@earendil-works/pi-coding-agent";
|
|
18
|
-
import { Text } from "@earendil-works/pi-tui";
|
|
19
|
-
import {
|
|
20
|
-
PROJECT_RULE_PROMPT,
|
|
21
|
-
buildToolPromptGuidelines,
|
|
22
|
-
} from "./lib/playbook.js";
|
|
23
|
-
import {
|
|
24
|
-
buildToolPresentation,
|
|
25
|
-
type AgentBrowserEnvelope,
|
|
26
|
-
type AgentBrowserPageChangeSummary,
|
|
27
|
-
} from "./lib/results.js";
|
|
28
|
-
import { SessionPageState } from "./lib/session-page-state.js";
|
|
29
|
-
import {
|
|
30
|
-
buildExecutionPlan,
|
|
31
|
-
createEphemeralSessionSeed,
|
|
32
|
-
createFreshSessionName,
|
|
33
|
-
createImplicitSessionName,
|
|
34
|
-
extractCommandTokens,
|
|
35
|
-
getImplicitSessionCloseTimeoutMs,
|
|
36
|
-
getImplicitSessionIdleTimeoutMs,
|
|
37
|
-
hasLaunchScopedTabCorrectionFlag,
|
|
38
|
-
extractExplicitSessionName,
|
|
39
|
-
restoreManagedSessionStateFromBranch,
|
|
40
|
-
resolveManagedSessionState,
|
|
41
|
-
validateToolArgs,
|
|
42
|
-
type CompatibilityWorkaround,
|
|
43
|
-
} from "./lib/runtime.js";
|
|
44
|
-
import { isRecord } from "./lib/parsing.js";
|
|
45
|
-
import { buildPromptPolicy, getLatestUserPrompt, shouldAppendBrowserSystemPrompt } from "./lib/prompt-policy.js";
|
|
46
|
-
import { isCloseCommand } from "./lib/command-taxonomy.js";
|
|
47
|
-
import {
|
|
48
|
-
cleanupSecureTempArtifacts,
|
|
49
|
-
type PersistentSessionArtifactEviction,
|
|
50
|
-
type PersistentSessionArtifactStore,
|
|
51
|
-
writePersistentSessionArtifactFile,
|
|
52
|
-
writeSecureTempFile,
|
|
53
|
-
} from "./lib/temp.js";
|
|
54
|
-
import {
|
|
55
|
-
AGENT_BROWSER_PARAMS,
|
|
56
|
-
analyzeNetworkSourceLookupResults,
|
|
57
|
-
analyzeQaPresetResults,
|
|
58
|
-
analyzeSourceLookupResults,
|
|
59
|
-
compileAgentBrowserElectron,
|
|
60
|
-
compileAgentBrowserJob,
|
|
61
|
-
compileAgentBrowserNetworkSourceLookup,
|
|
62
|
-
compileAgentBrowserQaPreset,
|
|
63
|
-
compileAgentBrowserSemanticAction,
|
|
64
|
-
compileAgentBrowserSourceLookup,
|
|
65
|
-
getCompiledSemanticActionCommandIndex,
|
|
66
|
-
getCompiledSemanticActionSessionPrefix,
|
|
67
|
-
isCompiledSemanticActionFindCommand,
|
|
68
|
-
redactNetworkSourceLookupAnalysis,
|
|
69
|
-
redactNetworkSourceLookupSurface,
|
|
70
|
-
type AgentBrowserNetworkSourceLookupAnalysis,
|
|
71
|
-
type AgentBrowserQaPresetAnalysis,
|
|
72
|
-
type AgentBrowserSourceLookupAnalysis,
|
|
73
|
-
type AgentBrowserSourceLookupElectronContext,
|
|
74
|
-
type CompiledAgentBrowserElectron,
|
|
75
|
-
type CompiledAgentBrowserJob,
|
|
76
|
-
type CompiledAgentBrowserNetworkSourceLookup,
|
|
77
|
-
type CompiledAgentBrowserQaPreset,
|
|
78
|
-
type CompiledAgentBrowserSemanticAction,
|
|
79
|
-
type CompiledAgentBrowserSourceLookup,
|
|
80
|
-
} from "./lib/input-modes.js";
|
|
81
|
-
import { parseAllowedDomainsPolicyFromArgs, type AllowedDomainsPolicy } from "./lib/navigation-policy.js";
|
|
82
|
-
import { closeManagedSession, runAgentBrowserTool, type BrowserRunState, type TraceOwner } from "./lib/orchestration/browser-run.js";
|
|
83
|
-
import { findElectronLaunchRecordForSession, getActiveElectronRecords } from "./lib/orchestration/browser-run/session-state.js";
|
|
84
|
-
import { parseBatchStdinJsonArray } from "./lib/orchestration/batch-stdin.js";
|
|
85
|
-
import {
|
|
86
|
-
ELECTRON_POST_COMMAND_STATUS_SETTLE_MS,
|
|
87
|
-
ELECTRON_PROFILE_ISOLATION_DETAILS,
|
|
88
|
-
cleanupActiveElectronHostLaunches,
|
|
89
|
-
handleElectronHostInput,
|
|
90
|
-
restoreElectronLaunchRecordsFromBranch,
|
|
91
|
-
type ElectronLaunchRecord,
|
|
92
|
-
} from "./lib/orchestration/electron-host/index.js";
|
|
93
|
-
import { buildValidationFailureResult, resolveAgentBrowserInput, type AgentBrowserExecuteParams } from "./lib/orchestration/input-plan.js";
|
|
94
|
-
import { applyAgentBrowserOutputPath } from "./lib/orchestration/output-file.js";
|
|
95
|
-
import type { NetworkRouteRecord } from "./lib/results/contracts.js";
|
|
96
|
-
import type { SessionArtifactManifest } from "./lib/results/contracts.js";
|
|
97
|
-
import {
|
|
98
|
-
buildEvictedSessionArtifactEntries,
|
|
99
|
-
formatSessionArtifactRetentionSummary,
|
|
100
|
-
isSessionArtifactManifest,
|
|
101
|
-
mergeSessionArtifactManifest,
|
|
102
|
-
} from "./lib/results/artifact-manifest.js";
|
|
103
|
-
import {
|
|
104
|
-
buildRichInputRecoveryDiagnostic,
|
|
105
|
-
buildRichInputRecoveryNextActions,
|
|
106
|
-
buildVisibleRefFallbackDiagnosticFromSnapshot,
|
|
107
|
-
buildVisibleRefFallbackNextActions,
|
|
108
|
-
formatRichInputRecoveryText,
|
|
109
|
-
formatVisibleRefFallbackText,
|
|
110
|
-
getVisibleRefFallbackTarget,
|
|
111
|
-
resolveVisibleRefActionFromSnapshot,
|
|
112
|
-
sanitizeVisibleRefFallbackDiagnostic,
|
|
113
|
-
type RichInputRecoveryDiagnostic,
|
|
114
|
-
type VisibleRefFallbackDiagnostic,
|
|
115
|
-
} from "./lib/results/selector-recovery.js";
|
|
116
|
-
import { withOptionalSessionArgs } from "./lib/results/next-actions.js";
|
|
117
|
-
import { canRegisterWebSearchTool, loadAgentBrowserConfigSync } from "./lib/config.js";
|
|
118
|
-
import { createAgentBrowserWebSearchTool } from "./lib/web-search.js";
|
|
119
|
-
import {
|
|
120
|
-
isDirectAgentBrowserBashAllowed,
|
|
121
|
-
isHarmlessAgentBrowserInspectionCommand,
|
|
122
|
-
looksLikeDirectAgentBrowserBash,
|
|
123
|
-
} from "./lib/bash-guard.js";
|
|
124
|
-
import {
|
|
125
|
-
AgentBrowserResultComponent,
|
|
126
|
-
buildAgentBrowserToolResultPatch,
|
|
127
|
-
formatAgentBrowserRenderCall,
|
|
128
|
-
formatAgentBrowserRenderResult,
|
|
129
|
-
} from "./lib/pi-tool-rendering.js";
|
|
130
|
-
|
|
131
|
-
const DEFAULT_SESSION_MODE = "auto" as const;
|
|
132
|
-
|
|
133
|
-
type BashToolCallLike = {
|
|
134
|
-
input: { command: string };
|
|
135
|
-
toolName: "bash";
|
|
136
|
-
};
|
|
137
|
-
|
|
138
|
-
function isBashToolCallEvent(event: unknown): event is BashToolCallLike {
|
|
139
|
-
if (!isRecord(event) || event.toolName !== "bash" || !isRecord(event.input)) return false;
|
|
140
|
-
return typeof event.input.command === "string";
|
|
141
|
-
}
|
|
142
|
-
|
|
143
|
-
type OwnedManagedSession = {
|
|
144
|
-
branchOwned: boolean;
|
|
145
|
-
cwd: string;
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
// Event ranks are local to the branch being restored. Keep them out of owned-resource
|
|
149
|
-
// state so branch switches never compare unrelated branch histories.
|
|
150
|
-
interface BranchManagedResourceEvents {
|
|
151
|
-
electronLaunchActiveRanks: Map<string, number>;
|
|
152
|
-
electronLaunchCleanupRanks: Map<string, number>;
|
|
153
|
-
managedSessionActiveRanks: Map<string, number>;
|
|
154
|
-
managedSessionCloseRanks: Map<string, number>;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
function getBatchPreflightValidationError(args: string[], stdin: string | undefined): string | undefined {
|
|
158
|
-
const commandTokens = extractCommandTokens(args);
|
|
159
|
-
if (commandTokens[0] !== "batch" || stdin === undefined) {
|
|
160
|
-
return undefined;
|
|
161
|
-
}
|
|
162
|
-
const parsed = parseBatchStdinJsonArray(stdin);
|
|
163
|
-
if (parsed.error || parsed.steps === undefined) {
|
|
164
|
-
return undefined;
|
|
165
|
-
}
|
|
166
|
-
for (const [index, step] of parsed.steps.entries()) {
|
|
167
|
-
if (!Array.isArray(step) || !step.every((token) => typeof token === "string") || step.length === 0) continue;
|
|
168
|
-
const stepValidationError = validateToolArgs(step);
|
|
169
|
-
if (stepValidationError) return `Unsupported batch step ${index + 1}: ${stepValidationError}`;
|
|
170
|
-
if (step[0] === "screenshot" && step.includes("--annotate")) {
|
|
171
|
-
return [
|
|
172
|
-
`Unsupported batch screenshot annotation in step ${index + 1}: put --annotate in top-level args, not inside the batch step.`,
|
|
173
|
-
`Use: { "args": ["--annotate", "batch"], "stdin": "[[\\"screenshot\\",\\"/path/to/image.png\\"]]" }`,
|
|
174
|
-
].join("\n");
|
|
175
|
-
}
|
|
176
|
-
}
|
|
177
|
-
return undefined;
|
|
178
|
-
}
|
|
179
|
-
|
|
180
|
-
function restoreArtifactManifestFromBranch(branch: unknown[]): SessionArtifactManifest | undefined {
|
|
181
|
-
let restoredManifest: SessionArtifactManifest | undefined;
|
|
182
|
-
for (const entry of branch) {
|
|
183
|
-
if (!isRecord(entry) || entry.type !== "message") continue;
|
|
184
|
-
const message = isRecord(entry.message) ? entry.message : undefined;
|
|
185
|
-
if (!message || message.toolName !== "agent_browser") continue;
|
|
186
|
-
const details = isRecord(message.details) ? message.details : undefined;
|
|
187
|
-
if (isSessionArtifactManifest(details?.artifactManifest)) {
|
|
188
|
-
restoredManifest = details.artifactManifest;
|
|
189
|
-
}
|
|
190
|
-
}
|
|
191
|
-
return restoredManifest;
|
|
192
|
-
}
|
|
193
|
-
|
|
194
|
-
function getToolResultArgs(details: Record<string, unknown>): string[] {
|
|
195
|
-
if (Array.isArray(details.args) && details.args.every((arg) => typeof arg === "string")) return details.args;
|
|
196
|
-
if (Array.isArray(details.effectiveArgs) && details.effectiveArgs.every((arg) => typeof arg === "string")) return details.effectiveArgs;
|
|
197
|
-
return [];
|
|
198
|
-
}
|
|
199
|
-
|
|
200
|
-
function restoreAllowedDomainsBySessionFromBranch(branch: unknown[]): Map<string, AllowedDomainsPolicy> {
|
|
201
|
-
const restoredPolicies = new Map<string, AllowedDomainsPolicy>();
|
|
202
|
-
for (const entry of branch) {
|
|
203
|
-
if (!isRecord(entry) || entry.type !== "message") continue;
|
|
204
|
-
const message = isRecord(entry.message) ? entry.message : undefined;
|
|
205
|
-
if (!message || message.toolName !== "agent_browser") continue;
|
|
206
|
-
const details = isRecord(message.details) ? message.details : undefined;
|
|
207
|
-
if (!details) continue;
|
|
208
|
-
const succeeded = getSuccessfulToolResult(details, message);
|
|
209
|
-
const args = getToolResultArgs(details);
|
|
210
|
-
const command = typeof details.command === "string" ? details.command : extractCommandTokens(args)[0];
|
|
211
|
-
const sessionName = typeof details.sessionName === "string" ? details.sessionName : undefined;
|
|
212
|
-
const explicitSessionName = extractExplicitSessionName(args);
|
|
213
|
-
const outcome = getManagedSessionOutcome(details);
|
|
214
|
-
const outcomeSucceeded = outcome?.succeeded === true;
|
|
215
|
-
const outcomeStatus = typeof outcome?.status === "string" ? outcome.status : undefined;
|
|
216
|
-
const outcomeCurrentSessionName = typeof outcome?.currentSessionName === "string" ? outcome.currentSessionName : undefined;
|
|
217
|
-
const outcomeAttemptedSessionName = typeof outcome?.attemptedSessionName === "string" ? outcome.attemptedSessionName : undefined;
|
|
218
|
-
if (outcomeSucceeded && outcomeStatus === "closed") {
|
|
219
|
-
const closedSessionName = outcomeAttemptedSessionName ?? outcomeCurrentSessionName ?? sessionName;
|
|
220
|
-
if (closedSessionName) restoredPolicies.delete(closedSessionName);
|
|
221
|
-
}
|
|
222
|
-
if (outcomeSucceeded && outcomeStatus === "replaced") {
|
|
223
|
-
const replacedSessionName = typeof outcome.replacedSessionName === "string" ? outcome.replacedSessionName : undefined;
|
|
224
|
-
if (replacedSessionName) restoredPolicies.delete(replacedSessionName);
|
|
225
|
-
}
|
|
226
|
-
if (succeeded && isCloseCommand(command)) {
|
|
227
|
-
const closedSessionName = explicitSessionName ?? sessionName ?? outcomeAttemptedSessionName ?? outcomeCurrentSessionName;
|
|
228
|
-
if (closedSessionName) restoredPolicies.delete(closedSessionName);
|
|
229
|
-
}
|
|
230
|
-
const electron = isRecord(details.electron) ? details.electron : undefined;
|
|
231
|
-
const cleanup = isRecord(electron?.cleanup) ? electron.cleanup : undefined;
|
|
232
|
-
const cleanupResults = Array.isArray(cleanup?.results) ? cleanup.results : [];
|
|
233
|
-
for (const cleanupResult of cleanupResults) {
|
|
234
|
-
for (const closedSessionName of getCleanupResultClosedManagedSessionNames(cleanupResult)) restoredPolicies.delete(closedSessionName);
|
|
235
|
-
}
|
|
236
|
-
const outcomeKeepsSessionCurrent = outcome?.activeAfter === true
|
|
237
|
-
&& (outcomeStatus === "created" || outcomeStatus === "replaced" || outcomeStatus === "unchanged")
|
|
238
|
-
&& outcomeCurrentSessionName === sessionName;
|
|
239
|
-
const policy = (succeeded || outcomeKeepsSessionCurrent) && sessionName && !isCloseCommand(command) ? parseAllowedDomainsPolicyFromArgs(args) : undefined;
|
|
240
|
-
if (policy && sessionName) restoredPolicies.set(sessionName, policy);
|
|
241
|
-
}
|
|
242
|
-
return restoredPolicies;
|
|
243
|
-
}
|
|
244
|
-
|
|
245
|
-
function trackOwnedManagedSession(
|
|
246
|
-
sessions: Map<string, OwnedManagedSession>,
|
|
247
|
-
sessionName: string | undefined,
|
|
248
|
-
cwd: string,
|
|
249
|
-
options: { branchOwned?: boolean } = {},
|
|
250
|
-
): void {
|
|
251
|
-
if (!sessionName) return;
|
|
252
|
-
const existing = sessions.get(sessionName);
|
|
253
|
-
const branchOwned = existing && !existing.branchOwned ? false : options.branchOwned === true;
|
|
254
|
-
sessions.set(sessionName, { branchOwned, cwd });
|
|
255
|
-
}
|
|
256
|
-
|
|
257
|
-
function untrackOwnedManagedSession(sessions: Map<string, OwnedManagedSession>, sessionName: string | undefined): void {
|
|
258
|
-
if (sessionName) sessions.delete(sessionName);
|
|
259
|
-
}
|
|
260
|
-
|
|
261
|
-
function untrackOwnedManagedSessionFromBranchClose(
|
|
262
|
-
sessions: Map<string, OwnedManagedSession>,
|
|
263
|
-
sessionName: string | undefined,
|
|
264
|
-
activeBranchRank: number | undefined,
|
|
265
|
-
closeBranchRank: number | undefined,
|
|
266
|
-
): void {
|
|
267
|
-
if (!sessionName || closeBranchRank === undefined) return;
|
|
268
|
-
const ownedSession = sessions.get(sessionName);
|
|
269
|
-
if (!ownedSession?.branchOwned) return;
|
|
270
|
-
if (activeBranchRank !== undefined && closeBranchRank <= activeBranchRank) return;
|
|
271
|
-
sessions.delete(sessionName);
|
|
272
|
-
}
|
|
273
|
-
|
|
274
|
-
function syncOwnedManagedSessionsFromResult(sessions: Map<string, OwnedManagedSession>, result: AgentToolResult<unknown>, cwd: string): void {
|
|
275
|
-
const details = isRecord(result.details) ? result.details : undefined;
|
|
276
|
-
const outcome = isRecord(details?.managedSessionOutcome) ? details.managedSessionOutcome : undefined;
|
|
277
|
-
if (!outcome) return;
|
|
278
|
-
const succeeded = outcome.succeeded === true;
|
|
279
|
-
const status = typeof outcome.status === "string" ? outcome.status : undefined;
|
|
280
|
-
const currentSessionName = typeof outcome.currentSessionName === "string" ? outcome.currentSessionName : undefined;
|
|
281
|
-
const attemptedSessionName = typeof outcome.attemptedSessionName === "string" ? outcome.attemptedSessionName : undefined;
|
|
282
|
-
if (outcome.activeAfter === true && (status === "created" || status === "replaced" || status === "unchanged")) {
|
|
283
|
-
trackOwnedManagedSession(sessions, currentSessionName, cwd);
|
|
284
|
-
}
|
|
285
|
-
if (succeeded && status === "closed") {
|
|
286
|
-
untrackOwnedManagedSession(sessions, attemptedSessionName ?? currentSessionName);
|
|
287
|
-
}
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
function getTouchedElectronLaunchIds(sessionName: string | undefined, records: Map<string, ElectronLaunchRecord>): Set<string> | undefined {
|
|
291
|
-
const record = findElectronLaunchRecordForSession(sessionName, records);
|
|
292
|
-
return record ? new Set([record.launchId]) : undefined;
|
|
293
|
-
}
|
|
294
|
-
|
|
295
|
-
function mergeActiveElectronLaunchRecords(
|
|
296
|
-
target: Map<string, ElectronLaunchRecord>,
|
|
297
|
-
source: Map<string, ElectronLaunchRecord>,
|
|
298
|
-
options: {
|
|
299
|
-
branchOwnedLaunchIds?: Set<string>;
|
|
300
|
-
markBranchOwned?: boolean;
|
|
301
|
-
touchedLaunchIds?: Set<string>;
|
|
302
|
-
} = {},
|
|
303
|
-
): void {
|
|
304
|
-
for (const record of getActiveElectronRecords(source)) {
|
|
305
|
-
const alreadyRuntimeOwned = target.has(record.launchId) && options.branchOwnedLaunchIds?.has(record.launchId) === false;
|
|
306
|
-
target.set(record.launchId, record);
|
|
307
|
-
if (options.branchOwnedLaunchIds) {
|
|
308
|
-
if (alreadyRuntimeOwned) {
|
|
309
|
-
// Already runtime-owned from a prior live result; keep it that way.
|
|
310
|
-
} else if (options.markBranchOwned === true) {
|
|
311
|
-
options.branchOwnedLaunchIds.add(record.launchId);
|
|
312
|
-
} else if (options.touchedLaunchIds?.has(record.launchId)) {
|
|
313
|
-
options.branchOwnedLaunchIds.delete(record.launchId);
|
|
314
|
-
}
|
|
315
|
-
}
|
|
316
|
-
}
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
function removeInactiveOwnedElectronLaunchRecords(
|
|
320
|
-
target: Map<string, ElectronLaunchRecord>,
|
|
321
|
-
branchOwnedLaunchIds: Set<string>,
|
|
322
|
-
source: Map<string, ElectronLaunchRecord>,
|
|
323
|
-
activeBranchRanks: Map<string, number>,
|
|
324
|
-
cleanupBranchRanks: Map<string, number>,
|
|
325
|
-
): void {
|
|
326
|
-
const activeLaunchIds = new Set(getActiveElectronRecords(source).map((record) => record.launchId));
|
|
327
|
-
const launchIds = new Set([...source.keys(), ...cleanupBranchRanks.keys()]);
|
|
328
|
-
for (const launchId of launchIds) {
|
|
329
|
-
if (!target.has(launchId) || !branchOwnedLaunchIds.has(launchId)) continue;
|
|
330
|
-
const activeBranchRank = activeBranchRanks.get(launchId);
|
|
331
|
-
const cleanupBranchRank = cleanupBranchRanks.get(launchId);
|
|
332
|
-
const restoredInactiveRecord = source.has(launchId) && !activeLaunchIds.has(launchId);
|
|
333
|
-
const cleanupIsLatest = cleanupBranchRank !== undefined && (activeBranchRank === undefined || cleanupBranchRank > activeBranchRank);
|
|
334
|
-
if (!restoredInactiveRecord && !cleanupIsLatest) continue;
|
|
335
|
-
target.delete(launchId);
|
|
336
|
-
branchOwnedLaunchIds.delete(launchId);
|
|
337
|
-
}
|
|
338
|
-
}
|
|
339
|
-
|
|
340
|
-
function mergeElectronLaunchRecordMaps(...maps: Array<Map<string, ElectronLaunchRecord>>): Map<string, ElectronLaunchRecord> {
|
|
341
|
-
const merged = new Map<string, ElectronLaunchRecord>();
|
|
342
|
-
for (const map of maps) {
|
|
343
|
-
for (const [launchId, record] of map) merged.set(launchId, record);
|
|
344
|
-
}
|
|
345
|
-
return merged;
|
|
346
|
-
}
|
|
347
|
-
|
|
348
|
-
function replaceWithActiveElectronLaunchRecords(
|
|
349
|
-
target: Map<string, ElectronLaunchRecord>,
|
|
350
|
-
source: Map<string, ElectronLaunchRecord>,
|
|
351
|
-
branchOwnedLaunchIds?: Set<string>,
|
|
352
|
-
cleanedLaunchIds?: Set<string>,
|
|
353
|
-
): void {
|
|
354
|
-
target.clear();
|
|
355
|
-
if (branchOwnedLaunchIds) {
|
|
356
|
-
if (cleanedLaunchIds) {
|
|
357
|
-
for (const launchId of cleanedLaunchIds) branchOwnedLaunchIds.delete(launchId);
|
|
358
|
-
} else {
|
|
359
|
-
branchOwnedLaunchIds.clear();
|
|
360
|
-
}
|
|
361
|
-
}
|
|
362
|
-
mergeActiveElectronLaunchRecords(target, source, branchOwnedLaunchIds ? { branchOwnedLaunchIds } : {});
|
|
363
|
-
}
|
|
364
|
-
|
|
365
|
-
function shouldSerializeElectronHostInput(compiledElectron: CompiledAgentBrowserElectron | undefined): boolean {
|
|
366
|
-
return compiledElectron?.action === "status" || compiledElectron?.action === "probe" || compiledElectron?.action === "cleanup";
|
|
367
|
-
}
|
|
368
|
-
|
|
369
|
-
function getElectronHostLaunchRecordsForInput(options: {
|
|
370
|
-
branchRecords: Map<string, ElectronLaunchRecord>;
|
|
371
|
-
compiledElectron: CompiledAgentBrowserElectron | undefined;
|
|
372
|
-
ownedRecords: Map<string, ElectronLaunchRecord>;
|
|
373
|
-
}): Map<string, ElectronLaunchRecord> {
|
|
374
|
-
if (
|
|
375
|
-
options.compiledElectron?.action === "status" ||
|
|
376
|
-
options.compiledElectron?.action === "cleanup" ||
|
|
377
|
-
(options.compiledElectron?.action === "probe" && options.compiledElectron.launchId)
|
|
378
|
-
) {
|
|
379
|
-
return mergeElectronLaunchRecordMaps(options.branchRecords, options.ownedRecords);
|
|
380
|
-
}
|
|
381
|
-
return options.branchRecords;
|
|
382
|
-
}
|
|
383
|
-
|
|
384
|
-
function getCleanupResultClosedManagedSessionNames(result: unknown): string[] {
|
|
385
|
-
if (!isRecord(result) || !Array.isArray(result.steps)) return [];
|
|
386
|
-
const closedSessionNames = new Set<string>();
|
|
387
|
-
const record = isRecord(result.record) ? result.record : undefined;
|
|
388
|
-
for (const step of result.steps) {
|
|
389
|
-
if (!isRecord(step) || step.resource !== "managed-session") continue;
|
|
390
|
-
if (step.state !== "removed" && step.state !== "already-gone") continue;
|
|
391
|
-
const sessionName = typeof step.sessionName === "string"
|
|
392
|
-
? step.sessionName
|
|
393
|
-
: typeof record?.sessionName === "string" ? record.sessionName : undefined;
|
|
394
|
-
if (sessionName) closedSessionNames.add(sessionName);
|
|
395
|
-
}
|
|
396
|
-
return [...closedSessionNames];
|
|
397
|
-
}
|
|
398
|
-
|
|
399
|
-
function getCleanupResultsClosedManagedSessionNames(cleanupResults: unknown[]): string[] {
|
|
400
|
-
const closedSessionNames = new Set<string>();
|
|
401
|
-
for (const result of cleanupResults) {
|
|
402
|
-
for (const sessionName of getCleanupResultClosedManagedSessionNames(result)) closedSessionNames.add(sessionName);
|
|
403
|
-
}
|
|
404
|
-
return [...closedSessionNames];
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
function isElectronLaunchRecord(value: unknown): value is ElectronLaunchRecord {
|
|
408
|
-
if (!isRecord(value)) return false;
|
|
409
|
-
return value.version === 1
|
|
410
|
-
&& value.launchedByWrapper === true
|
|
411
|
-
&& typeof value.launchId === "string"
|
|
412
|
-
&& typeof value.appName === "string"
|
|
413
|
-
&& typeof value.executablePath === "string"
|
|
414
|
-
&& typeof value.userDataDir === "string"
|
|
415
|
-
&& typeof value.port === "number"
|
|
416
|
-
&& typeof value.createdAtMs === "number";
|
|
417
|
-
}
|
|
418
|
-
|
|
419
|
-
function getCleanupResultsElectronRecords(cleanupResults: unknown[]): ElectronLaunchRecord[] {
|
|
420
|
-
return cleanupResults
|
|
421
|
-
.map((result) => isRecord(result) ? result.record : undefined)
|
|
422
|
-
.filter(isElectronLaunchRecord);
|
|
423
|
-
}
|
|
424
|
-
|
|
425
|
-
function mergeElectronCleanupRecords(target: Map<string, ElectronLaunchRecord>, cleanupResults: unknown[]): void {
|
|
426
|
-
for (const record of getCleanupResultsElectronRecords(cleanupResults)) {
|
|
427
|
-
target.set(record.launchId, record);
|
|
428
|
-
}
|
|
429
|
-
}
|
|
430
|
-
|
|
431
|
-
function getManagedSessionOutcome(details: Record<string, unknown>): Record<string, unknown> | undefined {
|
|
432
|
-
return isRecord(details.managedSessionOutcome) ? details.managedSessionOutcome : undefined;
|
|
433
|
-
}
|
|
434
|
-
|
|
435
|
-
function getSuccessfulToolResult(details: Record<string, unknown>, message: Record<string, unknown>): boolean {
|
|
436
|
-
const messageIsError = typeof message.isError === "boolean" ? message.isError : undefined;
|
|
437
|
-
const exitCode = typeof details.exitCode === "number" ? details.exitCode : undefined;
|
|
438
|
-
return messageIsError === undefined ? exitCode === undefined || exitCode === 0 : !messageIsError;
|
|
439
|
-
}
|
|
440
|
-
|
|
441
|
-
function setBranchRankForString(map: Map<string, number>, value: unknown, rank: number): void {
|
|
442
|
-
if (typeof value === "string" && value.length > 0) map.set(value, rank);
|
|
443
|
-
}
|
|
444
|
-
|
|
445
|
-
function collectBranchManagedResourceEvents(branch: unknown[]): BranchManagedResourceEvents {
|
|
446
|
-
const events: BranchManagedResourceEvents = {
|
|
447
|
-
electronLaunchActiveRanks: new Map<string, number>(),
|
|
448
|
-
electronLaunchCleanupRanks: new Map<string, number>(),
|
|
449
|
-
managedSessionActiveRanks: new Map<string, number>(),
|
|
450
|
-
managedSessionCloseRanks: new Map<string, number>(),
|
|
451
|
-
};
|
|
452
|
-
let eventRank = 0;
|
|
453
|
-
for (const entry of branch) {
|
|
454
|
-
if (!isRecord(entry) || entry.type !== "message") continue;
|
|
455
|
-
const message = isRecord(entry.message) ? entry.message : undefined;
|
|
456
|
-
if (!message || message.toolName !== "agent_browser") continue;
|
|
457
|
-
const details = isRecord(message.details) ? message.details : undefined;
|
|
458
|
-
if (!details) continue;
|
|
459
|
-
eventRank += 1;
|
|
460
|
-
const succeeded = getSuccessfulToolResult(details, message);
|
|
461
|
-
const args = Array.isArray(details.args) && details.args.every((arg) => typeof arg === "string") ? details.args : [];
|
|
462
|
-
const command = typeof details.command === "string" ? details.command : extractCommandTokens(args)[0];
|
|
463
|
-
const sessionName = typeof details.sessionName === "string" ? details.sessionName : undefined;
|
|
464
|
-
const sessionMode = details.sessionMode === "fresh" || details.sessionMode === "auto" ? details.sessionMode : undefined;
|
|
465
|
-
const usedImplicitSession = details.usedImplicitSession === true;
|
|
466
|
-
const explicitSessionName = extractExplicitSessionName(args);
|
|
467
|
-
const outcome = getManagedSessionOutcome(details);
|
|
468
|
-
const outcomeSucceeded = outcome?.succeeded === true;
|
|
469
|
-
const outcomeStatus = typeof outcome?.status === "string" ? outcome.status : undefined;
|
|
470
|
-
const outcomeCurrentSessionName = typeof outcome?.currentSessionName === "string" ? outcome.currentSessionName : undefined;
|
|
471
|
-
const outcomeAttemptedSessionName = typeof outcome?.attemptedSessionName === "string" ? outcome.attemptedSessionName : undefined;
|
|
472
|
-
if (outcomeSucceeded && outcome.activeAfter === true && (outcomeStatus === "created" || outcomeStatus === "replaced" || outcomeStatus === "unchanged")) {
|
|
473
|
-
setBranchRankForString(events.managedSessionActiveRanks, outcomeCurrentSessionName, eventRank);
|
|
474
|
-
}
|
|
475
|
-
if (outcomeSucceeded && outcomeStatus === "closed") {
|
|
476
|
-
setBranchRankForString(events.managedSessionCloseRanks, outcomeAttemptedSessionName ?? outcomeCurrentSessionName ?? sessionName, eventRank);
|
|
477
|
-
}
|
|
478
|
-
if (outcomeSucceeded && outcomeStatus === "replaced") {
|
|
479
|
-
setBranchRankForString(events.managedSessionCloseRanks, outcome.replacedSessionName, eventRank);
|
|
480
|
-
}
|
|
481
|
-
if (succeeded && !isCloseCommand(command) && sessionName && (usedImplicitSession || sessionMode === "fresh")) {
|
|
482
|
-
events.managedSessionActiveRanks.set(sessionName, eventRank);
|
|
483
|
-
}
|
|
484
|
-
if (succeeded && isCloseCommand(command)) {
|
|
485
|
-
setBranchRankForString(events.managedSessionCloseRanks, explicitSessionName ?? sessionName ?? outcomeAttemptedSessionName ?? outcomeCurrentSessionName, eventRank);
|
|
486
|
-
}
|
|
487
|
-
|
|
488
|
-
const electron = isRecord(details.electron) ? details.electron : undefined;
|
|
489
|
-
const launch = electron && isElectronLaunchRecord(electron.launch) ? electron.launch : undefined;
|
|
490
|
-
if (launch && getActiveElectronRecords(new Map([[launch.launchId, launch]])).length > 0) {
|
|
491
|
-
events.electronLaunchActiveRanks.set(launch.launchId, eventRank);
|
|
492
|
-
}
|
|
493
|
-
const cleanup = isRecord(electron?.cleanup) ? electron.cleanup : undefined;
|
|
494
|
-
const cleanupRecords = Array.isArray(cleanup?.records) ? cleanup.records : [];
|
|
495
|
-
for (const cleanupRecord of cleanupRecords) {
|
|
496
|
-
if (isElectronLaunchRecord(cleanupRecord)) events.electronLaunchCleanupRanks.set(cleanupRecord.launchId, eventRank);
|
|
497
|
-
}
|
|
498
|
-
const cleanupResults = Array.isArray(cleanup?.results) ? cleanup.results : [];
|
|
499
|
-
for (const cleanupResult of cleanupResults) {
|
|
500
|
-
if (isRecord(cleanupResult) && isElectronLaunchRecord(cleanupResult.record)) {
|
|
501
|
-
events.electronLaunchCleanupRanks.set(cleanupResult.record.launchId, eventRank);
|
|
502
|
-
}
|
|
503
|
-
for (const closedSessionName of getCleanupResultClosedManagedSessionNames(cleanupResult)) {
|
|
504
|
-
events.managedSessionCloseRanks.set(closedSessionName, eventRank);
|
|
505
|
-
}
|
|
506
|
-
}
|
|
507
|
-
}
|
|
508
|
-
return events;
|
|
509
|
-
}
|
|
510
|
-
|
|
511
|
-
function getCleanupResultsPreservedUserDataDirs(cleanupResults: unknown[]): string[] {
|
|
512
|
-
const userDataDirs = new Set<string>();
|
|
513
|
-
for (const result of cleanupResults) {
|
|
514
|
-
if (!isRecord(result) || !Array.isArray(result.steps) || !isElectronLaunchRecord(result.record)) continue;
|
|
515
|
-
const userDataDirStep = result.steps.find((step) => isRecord(step) && step.resource === "user-data-dir");
|
|
516
|
-
if (!isRecord(userDataDirStep)) continue;
|
|
517
|
-
if (userDataDirStep.state === "skipped" || userDataDirStep.state === "failed") userDataDirs.add(result.record.userDataDir);
|
|
518
|
-
}
|
|
519
|
-
return [...userDataDirs];
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
function syncElectronCleanupManagedSessions(sessions: Map<string, OwnedManagedSession>, cleanupResults: unknown[]): void {
|
|
523
|
-
for (const sessionName of getCleanupResultsClosedManagedSessionNames(cleanupResults)) {
|
|
524
|
-
untrackOwnedManagedSession(sessions, sessionName);
|
|
525
|
-
}
|
|
526
|
-
}
|
|
527
|
-
|
|
528
|
-
async function closeOwnedManagedSessionsExcept(sessions: Map<string, OwnedManagedSession>, keepSessionName: string | undefined, timeoutMs: number): Promise<void> {
|
|
529
|
-
for (const [sessionName, owner] of [...sessions]) {
|
|
530
|
-
if (sessionName === keepSessionName) continue;
|
|
531
|
-
const error = await closeManagedSession({ cwd: owner.cwd, sessionName, timeoutMs });
|
|
532
|
-
if (!error) sessions.delete(sessionName);
|
|
533
|
-
}
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
async function closeOwnedManagedSessions(sessions: Map<string, OwnedManagedSession>, timeoutMs: number): Promise<void> {
|
|
537
|
-
await closeOwnedManagedSessionsExcept(sessions, undefined, timeoutMs);
|
|
538
|
-
}
|
|
539
|
-
|
|
540
|
-
function getOffBranchOwnedElectronLaunchRecords(ownedRecords: Map<string, ElectronLaunchRecord>, branchRecords: Map<string, ElectronLaunchRecord>): Map<string, ElectronLaunchRecord> {
|
|
541
|
-
const activeBranchLaunchIds = new Set(getActiveElectronRecords(branchRecords).map((record) => record.launchId));
|
|
542
|
-
const offBranchRecords = new Map<string, ElectronLaunchRecord>();
|
|
543
|
-
for (const record of getActiveElectronRecords(ownedRecords)) {
|
|
544
|
-
if (!activeBranchLaunchIds.has(record.launchId)) offBranchRecords.set(record.launchId, record);
|
|
545
|
-
}
|
|
546
|
-
return offBranchRecords;
|
|
547
|
-
}
|
|
548
|
-
|
|
549
|
-
function shouldSerializeBrowserCommand(options: {
|
|
550
|
-
explicitSessionName?: string;
|
|
551
|
-
managedSessionName: string;
|
|
552
|
-
ownedElectronLaunchRecords: Map<string, ElectronLaunchRecord>;
|
|
553
|
-
ownedManagedSessions: Map<string, OwnedManagedSession>;
|
|
554
|
-
}): boolean {
|
|
555
|
-
if (!options.explicitSessionName) return true;
|
|
556
|
-
if (options.explicitSessionName === options.managedSessionName) return true;
|
|
557
|
-
if (options.ownedManagedSessions.has(options.explicitSessionName)) return true;
|
|
558
|
-
return getActiveElectronRecords(options.ownedElectronLaunchRecords).some((record) => record.sessionName === options.explicitSessionName);
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
// Serializes managed-session read/modify/write work so overlapping tool calls cannot promote stale state or close an in-use session.
|
|
562
|
-
class AsyncExecutionQueue {
|
|
563
|
-
private tail: Promise<void> = Promise.resolve();
|
|
564
|
-
|
|
565
|
-
run<T>(work: () => Promise<T>): Promise<T> {
|
|
566
|
-
const previous = this.tail;
|
|
567
|
-
let release!: () => void;
|
|
568
|
-
this.tail = new Promise<void>((resolve) => {
|
|
569
|
-
release = resolve;
|
|
570
|
-
});
|
|
571
|
-
|
|
572
|
-
return (async () => {
|
|
573
|
-
await previous;
|
|
574
|
-
try {
|
|
575
|
-
return await work();
|
|
576
|
-
} finally {
|
|
577
|
-
release();
|
|
578
|
-
}
|
|
579
|
-
})();
|
|
580
|
-
}
|
|
581
|
-
}
|
|
582
|
-
|
|
583
|
-
function getInstalledDocsPaths(): { readmePath: string; commandReferencePath: string; toolContractPath: string } {
|
|
584
|
-
const packageRoot = resolve(dirname(fileURLToPath(import.meta.url)), "..", "..");
|
|
585
|
-
return {
|
|
586
|
-
readmePath: join(packageRoot, "README.md"),
|
|
587
|
-
commandReferencePath: join(packageRoot, "docs", "COMMAND_REFERENCE.md"),
|
|
588
|
-
toolContractPath: join(packageRoot, "docs", "TOOL_CONTRACT.md"),
|
|
589
|
-
};
|
|
590
|
-
}
|
|
591
|
-
|
|
592
|
-
function hasArgvFlag(argv: readonly string[], longFlag: string, shortFlag: string): boolean {
|
|
593
|
-
return argv.includes(longFlag) || argv.includes(shortFlag);
|
|
594
|
-
}
|
|
595
|
-
|
|
596
|
-
function shouldIncludeProjectConfig(_cwd: string, argv: readonly string[] = process.argv): boolean {
|
|
597
|
-
return !hasArgvFlag(argv, "--no-approve", "-na");
|
|
598
|
-
}
|
|
599
|
-
|
|
600
|
-
export default function agentBrowserExtension(pi: ExtensionAPI) {
|
|
601
|
-
const ephemeralSessionSeed = createEphemeralSessionSeed();
|
|
602
|
-
const agentBrowserConfig = loadAgentBrowserConfigSync({
|
|
603
|
-
cwd: process.cwd(),
|
|
604
|
-
includeProjectConfig: shouldIncludeProjectConfig(process.cwd()),
|
|
605
|
-
});
|
|
606
|
-
const webSearchToolAvailable = canRegisterWebSearchTool(agentBrowserConfig);
|
|
607
|
-
const toolPromptGuidelines = buildToolPromptGuidelines({
|
|
608
|
-
browserDefaultProfile: agentBrowserConfig.trustedBrowserDefaultProfile,
|
|
609
|
-
browserExecutablePath: agentBrowserConfig.trustedBrowserExecutablePath,
|
|
610
|
-
includeWebSearch: webSearchToolAvailable,
|
|
611
|
-
docs: getInstalledDocsPaths(),
|
|
612
|
-
});
|
|
613
|
-
const implicitSessionIdleTimeoutMs = String(getImplicitSessionIdleTimeoutMs());
|
|
614
|
-
const implicitSessionCloseTimeoutMs = getImplicitSessionCloseTimeoutMs();
|
|
615
|
-
let managedSessionActive = false;
|
|
616
|
-
let managedSessionBaseName = createImplicitSessionName(undefined, process.cwd(), ephemeralSessionSeed);
|
|
617
|
-
let managedSessionName = managedSessionBaseName;
|
|
618
|
-
let managedSessionCwd = process.cwd();
|
|
619
|
-
let freshSessionOrdinal = 0;
|
|
620
|
-
let sessionPageState = new SessionPageState();
|
|
621
|
-
let traceOwners = new Map<string, TraceOwner>();
|
|
622
|
-
let artifactManifest: SessionArtifactManifest | undefined;
|
|
623
|
-
let allowedDomainsBySession = new Map<string, AllowedDomainsPolicy>();
|
|
624
|
-
let networkRoutesBySession = new Map<string, NetworkRouteRecord[]>();
|
|
625
|
-
let electronLaunchRecords = new Map<string, ElectronLaunchRecord>();
|
|
626
|
-
let ownedElectronLaunchRecords = new Map<string, ElectronLaunchRecord>();
|
|
627
|
-
let branchOwnedElectronLaunchIds = new Set<string>();
|
|
628
|
-
let electronChildProcesses = new Map<string, ChildProcess>();
|
|
629
|
-
const ownedManagedSessions = new Map<string, OwnedManagedSession>();
|
|
630
|
-
const managedSessionExecutionQueue = new AsyncExecutionQueue();
|
|
631
|
-
let branchStateGeneration = 0;
|
|
632
|
-
|
|
633
|
-
const clearSessionScopedBrowserState = (sessionName: string): void => {
|
|
634
|
-
allowedDomainsBySession.delete(sessionName);
|
|
635
|
-
networkRoutesBySession.delete(sessionName);
|
|
636
|
-
sessionPageState.clearSession(sessionName);
|
|
637
|
-
};
|
|
638
|
-
|
|
639
|
-
const restoreBranchBackedState = (ctx: ExtensionContext, options: { resetRuntimeOwnership: boolean }): void => {
|
|
640
|
-
branchStateGeneration += 1;
|
|
641
|
-
const previousManagedSessionActive = managedSessionActive;
|
|
642
|
-
const previousManagedSessionName = managedSessionName;
|
|
643
|
-
const previousFreshSessionOrdinal = freshSessionOrdinal;
|
|
644
|
-
managedSessionBaseName = createImplicitSessionName(ctx.sessionManager.getSessionId(), ctx.cwd, ephemeralSessionSeed);
|
|
645
|
-
const branch = ctx.sessionManager.getBranch();
|
|
646
|
-
const branchResourceEvents = collectBranchManagedResourceEvents(branch);
|
|
647
|
-
const restoredState = restoreManagedSessionStateFromBranch(branch, managedSessionBaseName);
|
|
648
|
-
managedSessionActive = restoredState.active;
|
|
649
|
-
const restoredFreshSessionOrdinal = options.resetRuntimeOwnership
|
|
650
|
-
? restoredState.freshSessionOrdinal
|
|
651
|
-
: Math.max(previousFreshSessionOrdinal, restoredState.freshSessionOrdinal);
|
|
652
|
-
const shouldReservePostCloseSession = !restoredState.active && restoredState.closedSessionName === restoredState.sessionName;
|
|
653
|
-
const alreadyReservedPostCloseSession = shouldReservePostCloseSession
|
|
654
|
-
&& !options.resetRuntimeOwnership
|
|
655
|
-
&& !previousManagedSessionActive
|
|
656
|
-
&& previousFreshSessionOrdinal > restoredState.freshSessionOrdinal
|
|
657
|
-
&& previousFreshSessionOrdinal === restoredFreshSessionOrdinal
|
|
658
|
-
&& previousManagedSessionName === createFreshSessionName(managedSessionBaseName, ephemeralSessionSeed, restoredFreshSessionOrdinal);
|
|
659
|
-
const nextFreshSessionOrdinal = shouldReservePostCloseSession && !alreadyReservedPostCloseSession
|
|
660
|
-
? restoredFreshSessionOrdinal + 1
|
|
661
|
-
: restoredFreshSessionOrdinal;
|
|
662
|
-
managedSessionName = shouldReservePostCloseSession
|
|
663
|
-
? alreadyReservedPostCloseSession
|
|
664
|
-
? previousManagedSessionName
|
|
665
|
-
: createFreshSessionName(managedSessionBaseName, ephemeralSessionSeed, nextFreshSessionOrdinal)
|
|
666
|
-
: restoredState.sessionName;
|
|
667
|
-
managedSessionCwd = ctx.cwd;
|
|
668
|
-
freshSessionOrdinal = nextFreshSessionOrdinal;
|
|
669
|
-
sessionPageState = SessionPageState.fromBranch(branch);
|
|
670
|
-
traceOwners = new Map<string, TraceOwner>();
|
|
671
|
-
artifactManifest = restoreArtifactManifestFromBranch(branch);
|
|
672
|
-
allowedDomainsBySession = restoreAllowedDomainsBySessionFromBranch(branch);
|
|
673
|
-
networkRoutesBySession = new Map<string, NetworkRouteRecord[]>();
|
|
674
|
-
electronLaunchRecords = restoreElectronLaunchRecordsFromBranch(branch);
|
|
675
|
-
if (options.resetRuntimeOwnership) {
|
|
676
|
-
ownedManagedSessions.clear();
|
|
677
|
-
ownedElectronLaunchRecords = new Map<string, ElectronLaunchRecord>();
|
|
678
|
-
branchOwnedElectronLaunchIds = new Set<string>();
|
|
679
|
-
} else {
|
|
680
|
-
for (const [sessionName, closeRank] of branchResourceEvents.managedSessionCloseRanks) {
|
|
681
|
-
untrackOwnedManagedSessionFromBranchClose(
|
|
682
|
-
ownedManagedSessions,
|
|
683
|
-
sessionName,
|
|
684
|
-
branchResourceEvents.managedSessionActiveRanks.get(sessionName),
|
|
685
|
-
closeRank,
|
|
686
|
-
);
|
|
687
|
-
}
|
|
688
|
-
removeInactiveOwnedElectronLaunchRecords(
|
|
689
|
-
ownedElectronLaunchRecords,
|
|
690
|
-
branchOwnedElectronLaunchIds,
|
|
691
|
-
electronLaunchRecords,
|
|
692
|
-
branchResourceEvents.electronLaunchActiveRanks,
|
|
693
|
-
branchResourceEvents.electronLaunchCleanupRanks,
|
|
694
|
-
);
|
|
695
|
-
}
|
|
696
|
-
if (restoredState.active) {
|
|
697
|
-
trackOwnedManagedSession(ownedManagedSessions, restoredState.sessionName, ctx.cwd, { branchOwned: true });
|
|
698
|
-
}
|
|
699
|
-
mergeActiveElectronLaunchRecords(ownedElectronLaunchRecords, electronLaunchRecords, {
|
|
700
|
-
branchOwnedLaunchIds: branchOwnedElectronLaunchIds,
|
|
701
|
-
markBranchOwned: true,
|
|
702
|
-
});
|
|
703
|
-
};
|
|
704
|
-
|
|
705
|
-
pi.on("session_start", async (_event, ctx) => {
|
|
706
|
-
restoreBranchBackedState(ctx, { resetRuntimeOwnership: true });
|
|
707
|
-
electronChildProcesses = new Map<string, ChildProcess>();
|
|
708
|
-
});
|
|
709
|
-
|
|
710
|
-
pi.on("session_tree", async (_event, ctx) => {
|
|
711
|
-
await managedSessionExecutionQueue.run(async () => {
|
|
712
|
-
restoreBranchBackedState(ctx, { resetRuntimeOwnership: false });
|
|
713
|
-
});
|
|
714
|
-
});
|
|
715
|
-
|
|
716
|
-
pi.on("session_shutdown", async (event, ctx) => {
|
|
717
|
-
let preservedElectronProfileDirs: string[] = [];
|
|
718
|
-
await managedSessionExecutionQueue.run(async () => {
|
|
719
|
-
const shutdownCwd = ctx?.cwd ?? managedSessionCwd;
|
|
720
|
-
const quitting = event?.reason === "quit";
|
|
721
|
-
preservedElectronProfileDirs = quitting
|
|
722
|
-
? []
|
|
723
|
-
: getActiveElectronRecords(electronLaunchRecords).map((record) => record.userDataDir);
|
|
724
|
-
const electronRecordsToCleanup = quitting
|
|
725
|
-
? ownedElectronLaunchRecords
|
|
726
|
-
: getOffBranchOwnedElectronLaunchRecords(ownedElectronLaunchRecords, electronLaunchRecords);
|
|
727
|
-
const electronCleanupResults = await cleanupActiveElectronHostLaunches({
|
|
728
|
-
cwd: shutdownCwd,
|
|
729
|
-
electronChildProcesses,
|
|
730
|
-
electronLaunchRecords: electronRecordsToCleanup,
|
|
731
|
-
timeoutMs: implicitSessionCloseTimeoutMs,
|
|
732
|
-
});
|
|
733
|
-
preservedElectronProfileDirs = [...new Set([
|
|
734
|
-
...preservedElectronProfileDirs,
|
|
735
|
-
...getCleanupResultsPreservedUserDataDirs(electronCleanupResults),
|
|
736
|
-
])];
|
|
737
|
-
syncElectronCleanupManagedSessions(ownedManagedSessions, electronCleanupResults);
|
|
738
|
-
if (quitting) {
|
|
739
|
-
await closeOwnedManagedSessions(ownedManagedSessions, implicitSessionCloseTimeoutMs);
|
|
740
|
-
} else {
|
|
741
|
-
await closeOwnedManagedSessionsExcept(
|
|
742
|
-
ownedManagedSessions,
|
|
743
|
-
managedSessionActive ? managedSessionName : undefined,
|
|
744
|
-
implicitSessionCloseTimeoutMs,
|
|
745
|
-
);
|
|
746
|
-
}
|
|
747
|
-
});
|
|
748
|
-
managedSessionActive = false;
|
|
749
|
-
sessionPageState.reset();
|
|
750
|
-
traceOwners = new Map<string, TraceOwner>();
|
|
751
|
-
artifactManifest = undefined;
|
|
752
|
-
allowedDomainsBySession = new Map<string, AllowedDomainsPolicy>();
|
|
753
|
-
networkRoutesBySession = new Map<string, NetworkRouteRecord[]>();
|
|
754
|
-
electronLaunchRecords = new Map<string, ElectronLaunchRecord>();
|
|
755
|
-
ownedElectronLaunchRecords = new Map<string, ElectronLaunchRecord>();
|
|
756
|
-
branchOwnedElectronLaunchIds = new Set<string>();
|
|
757
|
-
electronChildProcesses = new Map<string, ChildProcess>();
|
|
758
|
-
ownedManagedSessions.clear();
|
|
759
|
-
await cleanupSecureTempArtifacts({ preservePaths: preservedElectronProfileDirs });
|
|
760
|
-
});
|
|
761
|
-
|
|
762
|
-
pi.on("before_agent_start", async (event) => {
|
|
763
|
-
if (!shouldAppendBrowserSystemPrompt(event.prompt)) {
|
|
764
|
-
return undefined;
|
|
765
|
-
}
|
|
766
|
-
return {
|
|
767
|
-
systemPrompt: `${event.systemPrompt}\n\n${PROJECT_RULE_PROMPT}`,
|
|
768
|
-
};
|
|
769
|
-
});
|
|
770
|
-
|
|
771
|
-
pi.on("tool_call", async (event, ctx) => {
|
|
772
|
-
const promptPolicy = buildPromptPolicy(getLatestUserPrompt(ctx.sessionManager.getBranch()));
|
|
773
|
-
if (
|
|
774
|
-
isBashToolCallEvent(event) &&
|
|
775
|
-
!promptPolicy.allowLegacyAgentBrowserBash &&
|
|
776
|
-
looksLikeDirectAgentBrowserBash(event.input.command) &&
|
|
777
|
-
!isHarmlessAgentBrowserInspectionCommand(event.input.command) &&
|
|
778
|
-
!(await isDirectAgentBrowserBashAllowed(ctx.cwd))
|
|
779
|
-
) {
|
|
780
|
-
return {
|
|
781
|
-
block: true,
|
|
782
|
-
reason: "Use the native agent_browser tool instead of bash for agent-browser in this environment.",
|
|
783
|
-
};
|
|
784
|
-
}
|
|
785
|
-
});
|
|
786
|
-
|
|
787
|
-
pi.on("tool_result", async (event) => buildAgentBrowserToolResultPatch(event));
|
|
788
|
-
|
|
789
|
-
pi.registerTool({
|
|
790
|
-
name: "agent_browser",
|
|
791
|
-
label: "Agent Browser",
|
|
792
|
-
description:
|
|
793
|
-
"Browse and interact with websites using agent-browser. Use this for web research, reading live docs, opening pages, taking snapshots or screenshots, clicking links, filling forms, extracting page content, and authenticated/profile-based browser work. Input choice: default `args` for open → snapshot -i → click/fill @refs; `semanticAction` for stable role/text/label targets; `job` or `qa` for multi-step checks; `electron` only for desktop apps; experimental `sourceLookup` / `networkSourceLookup` for candidates only.",
|
|
794
|
-
promptSnippet:
|
|
795
|
-
"Browse websites, read live docs, click and fill pages, extract browser content, take screenshots, and automate real web workflows.",
|
|
796
|
-
promptGuidelines: toolPromptGuidelines,
|
|
797
|
-
parameters: AGENT_BROWSER_PARAMS,
|
|
798
|
-
renderCall(args, theme, context) {
|
|
799
|
-
const text = context.lastComponent instanceof Text ? context.lastComponent : new Text("", 0, 0);
|
|
800
|
-
text.setText(formatAgentBrowserRenderCall(args, theme));
|
|
801
|
-
return text;
|
|
802
|
-
},
|
|
803
|
-
renderResult(result, options, theme, context) {
|
|
804
|
-
const component = context.lastComponent instanceof AgentBrowserResultComponent
|
|
805
|
-
? context.lastComponent
|
|
806
|
-
: new AgentBrowserResultComponent();
|
|
807
|
-
component.setState(formatAgentBrowserRenderResult(result, options, theme, context.isError), options.expanded, theme);
|
|
808
|
-
return component;
|
|
809
|
-
},
|
|
810
|
-
async execute(_toolCallId, params: AgentBrowserExecuteParams, signal, onUpdate, ctx) {
|
|
811
|
-
const promptPolicy = buildPromptPolicy(getLatestUserPrompt(ctx.sessionManager.getBranch()));
|
|
812
|
-
const outputPath = isRecord(params) && typeof params.outputPath === "string" ? params.outputPath : undefined;
|
|
813
|
-
const resolvedInput = resolveAgentBrowserInput({
|
|
814
|
-
getBatchPreflightValidationError,
|
|
815
|
-
managedSessionActive,
|
|
816
|
-
params,
|
|
817
|
-
});
|
|
818
|
-
if (resolvedInput.status === "invalid") {
|
|
819
|
-
return buildValidationFailureResult(resolvedInput);
|
|
820
|
-
}
|
|
821
|
-
const { toolArgs } = resolvedInput;
|
|
822
|
-
const compiledElectron = resolvedInput.kind === "electron" ? resolvedInput.compiledElectron : undefined;
|
|
823
|
-
const redactedCompiledElectron = resolvedInput.kind === "electron" ? resolvedInput.redactedCompiledElectron : undefined;
|
|
824
|
-
const runElectronHostInput = async () => {
|
|
825
|
-
const electronHostLaunchRecords = getElectronHostLaunchRecordsForInput({
|
|
826
|
-
branchRecords: electronLaunchRecords,
|
|
827
|
-
compiledElectron,
|
|
828
|
-
ownedRecords: ownedElectronLaunchRecords,
|
|
829
|
-
});
|
|
830
|
-
const electronHostResult = await handleElectronHostInput({
|
|
831
|
-
compiledElectron,
|
|
832
|
-
cwd: ctx.cwd,
|
|
833
|
-
electronChildProcesses,
|
|
834
|
-
electronLaunchRecords: electronHostLaunchRecords,
|
|
835
|
-
implicitSessionCloseTimeoutMs,
|
|
836
|
-
managedSessionActive,
|
|
837
|
-
managedSessionName,
|
|
838
|
-
redactedCompiledElectron,
|
|
839
|
-
sessionPageState,
|
|
840
|
-
signal,
|
|
841
|
-
});
|
|
842
|
-
if (electronHostResult && compiledElectron?.action === "cleanup") {
|
|
843
|
-
branchStateGeneration += 1;
|
|
844
|
-
const cleanupRecords = isRecord(electronHostResult.details)
|
|
845
|
-
&& isRecord(electronHostResult.details.electron)
|
|
846
|
-
&& isRecord(electronHostResult.details.electron.cleanup)
|
|
847
|
-
&& Array.isArray(electronHostResult.details.electron.cleanup.results)
|
|
848
|
-
? electronHostResult.details.electron.cleanup.results
|
|
849
|
-
: [];
|
|
850
|
-
const cleanedLaunchIds = new Set<string>();
|
|
851
|
-
for (const cleanupResult of cleanupRecords) {
|
|
852
|
-
if (isRecord(cleanupResult) && isElectronLaunchRecord(cleanupResult.record)) {
|
|
853
|
-
cleanedLaunchIds.add(cleanupResult.record.launchId);
|
|
854
|
-
}
|
|
855
|
-
}
|
|
856
|
-
replaceWithActiveElectronLaunchRecords(ownedElectronLaunchRecords, electronHostLaunchRecords, branchOwnedElectronLaunchIds, cleanedLaunchIds);
|
|
857
|
-
mergeElectronCleanupRecords(electronLaunchRecords, cleanupRecords);
|
|
858
|
-
const closedSessionNames = getCleanupResultsClosedManagedSessionNames(cleanupRecords);
|
|
859
|
-
syncElectronCleanupManagedSessions(ownedManagedSessions, cleanupRecords);
|
|
860
|
-
for (const closedSessionName of closedSessionNames) {
|
|
861
|
-
clearSessionScopedBrowserState(closedSessionName);
|
|
862
|
-
if (closedSessionName === managedSessionName) {
|
|
863
|
-
managedSessionActive = false;
|
|
864
|
-
freshSessionOrdinal += 1;
|
|
865
|
-
managedSessionName = createFreshSessionName(managedSessionBaseName, ephemeralSessionSeed, freshSessionOrdinal);
|
|
866
|
-
}
|
|
867
|
-
}
|
|
868
|
-
}
|
|
869
|
-
return electronHostResult;
|
|
870
|
-
};
|
|
871
|
-
const electronHostResult = shouldSerializeElectronHostInput(compiledElectron)
|
|
872
|
-
? await managedSessionExecutionQueue.run(runElectronHostInput)
|
|
873
|
-
: await runElectronHostInput();
|
|
874
|
-
if (electronHostResult) {
|
|
875
|
-
return applyAgentBrowserOutputPath({ cwd: ctx.cwd, outputPath, result: electronHostResult });
|
|
876
|
-
}
|
|
877
|
-
|
|
878
|
-
const explicitSessionName = extractExplicitSessionName(toolArgs);
|
|
879
|
-
const serializeBrowserCommand = shouldSerializeBrowserCommand({
|
|
880
|
-
explicitSessionName,
|
|
881
|
-
managedSessionName,
|
|
882
|
-
ownedElectronLaunchRecords,
|
|
883
|
-
ownedManagedSessions,
|
|
884
|
-
});
|
|
885
|
-
const runBrowserCommand = async () => {
|
|
886
|
-
const generationAtStart = branchStateGeneration;
|
|
887
|
-
const sessionPageStateUpdate = sessionPageState.beginUpdate();
|
|
888
|
-
const browserRunState: BrowserRunState = {
|
|
889
|
-
allowedDomainsBySession,
|
|
890
|
-
artifactManifest,
|
|
891
|
-
closedManagedSessionNames: new Set<string>(),
|
|
892
|
-
electronChildProcesses,
|
|
893
|
-
electronLaunchRecords,
|
|
894
|
-
ephemeralSessionSeed,
|
|
895
|
-
freshSessionOrdinal,
|
|
896
|
-
managedSessionActive,
|
|
897
|
-
managedSessionBaseName,
|
|
898
|
-
managedSessionCwd,
|
|
899
|
-
managedSessionName,
|
|
900
|
-
networkRoutesBySession,
|
|
901
|
-
sessionPageState,
|
|
902
|
-
traceOwners,
|
|
903
|
-
};
|
|
904
|
-
const result = await runAgentBrowserTool({
|
|
905
|
-
ctx,
|
|
906
|
-
cwd: ctx.cwd,
|
|
907
|
-
electronPostCommandStatusSettleMs: ELECTRON_POST_COMMAND_STATUS_SETTLE_MS,
|
|
908
|
-
electronProfileIsolationDetails: ELECTRON_PROFILE_ISOLATION_DETAILS,
|
|
909
|
-
implicitSessionCloseTimeoutMs,
|
|
910
|
-
implicitSessionIdleTimeoutMs,
|
|
911
|
-
input: resolvedInput,
|
|
912
|
-
onUpdate,
|
|
913
|
-
params,
|
|
914
|
-
promptPolicy,
|
|
915
|
-
sessionPageStateUpdate,
|
|
916
|
-
signal,
|
|
917
|
-
state: browserRunState,
|
|
918
|
-
});
|
|
919
|
-
const branchStateStillCurrent = generationAtStart === branchStateGeneration;
|
|
920
|
-
if (serializeBrowserCommand || branchStateStillCurrent) {
|
|
921
|
-
allowedDomainsBySession = browserRunState.allowedDomainsBySession;
|
|
922
|
-
networkRoutesBySession = browserRunState.networkRoutesBySession;
|
|
923
|
-
artifactManifest = browserRunState.artifactManifest;
|
|
924
|
-
freshSessionOrdinal = Math.max(freshSessionOrdinal, browserRunState.freshSessionOrdinal);
|
|
925
|
-
managedSessionActive = browserRunState.managedSessionActive;
|
|
926
|
-
managedSessionCwd = browserRunState.managedSessionCwd;
|
|
927
|
-
managedSessionName = browserRunState.managedSessionName;
|
|
928
|
-
for (const closedSessionName of browserRunState.closedManagedSessionNames) {
|
|
929
|
-
untrackOwnedManagedSession(ownedManagedSessions, closedSessionName);
|
|
930
|
-
}
|
|
931
|
-
syncOwnedManagedSessionsFromResult(ownedManagedSessions, result, browserRunState.managedSessionCwd);
|
|
932
|
-
mergeActiveElectronLaunchRecords(ownedElectronLaunchRecords, electronLaunchRecords, {
|
|
933
|
-
branchOwnedLaunchIds: branchOwnedElectronLaunchIds,
|
|
934
|
-
touchedLaunchIds: !result.isError
|
|
935
|
-
? getTouchedElectronLaunchIds(explicitSessionName ?? browserRunState.managedSessionName, electronLaunchRecords)
|
|
936
|
-
: undefined,
|
|
937
|
-
});
|
|
938
|
-
if (serializeBrowserCommand) branchStateGeneration += 1;
|
|
939
|
-
}
|
|
940
|
-
return applyAgentBrowserOutputPath({ cwd: ctx.cwd, outputPath, preserveTextContent: Array.isArray(params.args) && params.args.includes("--json"), result });
|
|
941
|
-
};
|
|
942
|
-
|
|
943
|
-
return serializeBrowserCommand
|
|
944
|
-
? managedSessionExecutionQueue.run(runBrowserCommand)
|
|
945
|
-
: runBrowserCommand();
|
|
946
|
-
},
|
|
947
|
-
});
|
|
948
|
-
|
|
949
|
-
if (webSearchToolAvailable) {
|
|
950
|
-
pi.registerTool(createAgentBrowserWebSearchTool(agentBrowserConfig));
|
|
951
|
-
}
|
|
952
|
-
}
|