pi-agent-browser-native 0.2.32 → 0.2.33
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 +17 -0
- package/README.md +27 -16
- package/docs/ARCHITECTURE.md +3 -2
- package/docs/COMMAND_REFERENCE.md +18 -10
- package/docs/ELECTRON.md +23 -4
- package/docs/RELEASE.md +4 -2
- package/docs/REQUIREMENTS.md +1 -1
- package/docs/SUPPORT_MATRIX.md +28 -16
- package/docs/TOOL_CONTRACT.md +29 -24
- package/extensions/agent-browser/index.ts +404 -4371
- package/extensions/agent-browser/lib/input-modes/electron.ts +170 -0
- package/extensions/agent-browser/lib/input-modes/job.ts +203 -0
- package/extensions/agent-browser/lib/input-modes/lookups.ts +447 -0
- package/extensions/agent-browser/lib/input-modes/params.ts +188 -0
- package/extensions/agent-browser/lib/input-modes/semantic-action.ts +107 -0
- package/extensions/agent-browser/lib/input-modes/shared.ts +46 -0
- package/extensions/agent-browser/lib/input-modes/types.ts +221 -0
- package/extensions/agent-browser/lib/input-modes.ts +41 -0
- package/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.ts +696 -0
- package/extensions/agent-browser/lib/orchestration/browser-run/final-result.ts +450 -0
- package/extensions/agent-browser/lib/orchestration/browser-run/index.ts +46 -0
- package/extensions/agent-browser/lib/orchestration/browser-run/prepare.ts +711 -0
- package/extensions/agent-browser/lib/orchestration/browser-run/process-output.ts +386 -0
- package/extensions/agent-browser/lib/orchestration/browser-run/session-state.ts +868 -0
- package/extensions/agent-browser/lib/orchestration/browser-run/types.ts +476 -0
- package/extensions/agent-browser/lib/orchestration/browser-run.ts +1 -0
- package/extensions/agent-browser/lib/orchestration/input-plan.ts +338 -0
- package/extensions/agent-browser/lib/playbook.ts +12 -11
- package/extensions/agent-browser/lib/process.ts +106 -4
- package/extensions/agent-browser/lib/results/action-recommendations.ts +269 -0
- package/extensions/agent-browser/lib/results/artifact-manifest.ts +114 -0
- package/extensions/agent-browser/lib/results/artifact-state.ts +13 -0
- package/extensions/agent-browser/lib/results/categories.ts +106 -0
- package/extensions/agent-browser/lib/results/contracts.ts +220 -0
- package/extensions/agent-browser/lib/results/editable-ref-evidence.ts +72 -0
- package/extensions/agent-browser/lib/results/envelope.ts +2 -1
- package/extensions/agent-browser/lib/results/network.ts +64 -0
- package/extensions/agent-browser/lib/results/next-actions.ts +117 -0
- package/extensions/agent-browser/lib/results/presentation/artifacts.ts +506 -0
- package/extensions/agent-browser/lib/results/presentation/batch.ts +355 -0
- package/extensions/agent-browser/lib/results/presentation/common.ts +53 -0
- package/extensions/agent-browser/lib/results/presentation/content.ts +36 -0
- package/extensions/agent-browser/lib/results/presentation/diagnostics.ts +730 -0
- package/extensions/agent-browser/lib/results/presentation/errors.ts +125 -0
- package/extensions/agent-browser/lib/results/presentation/large-output.ts +182 -0
- package/extensions/agent-browser/lib/results/presentation/navigation.ts +216 -0
- package/extensions/agent-browser/lib/results/presentation/registry.ts +154 -0
- package/extensions/agent-browser/lib/results/presentation/skills.ts +143 -0
- package/extensions/agent-browser/lib/results/presentation.ts +87 -2399
- package/extensions/agent-browser/lib/results/recovery-actions.ts +139 -0
- package/extensions/agent-browser/lib/results/recovery-next-actions.ts +71 -0
- package/extensions/agent-browser/lib/results/selector-recovery.ts +312 -0
- package/extensions/agent-browser/lib/results/shared.ts +17 -789
- package/extensions/agent-browser/lib/results/snapshot-high-value-controls.ts +262 -0
- package/extensions/agent-browser/lib/results/snapshot-refs.ts +100 -0
- package/extensions/agent-browser/lib/results/snapshot-segments.ts +366 -0
- package/extensions/agent-browser/lib/results/snapshot-spill.ts +63 -0
- package/extensions/agent-browser/lib/results/snapshot.ts +37 -489
- package/extensions/agent-browser/lib/results/text.ts +40 -0
- package/extensions/agent-browser/lib/results.ts +16 -5
- package/extensions/agent-browser/lib/session-page-state.ts +486 -0
- package/package.json +2 -1
|
@@ -0,0 +1,220 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Purpose: Define stable result-rendering data contracts shared across focused result modules.
|
|
3
|
+
* Responsibilities: Keep upstream envelope, presentation, artifact, category, and network shapes in one type-only surface.
|
|
4
|
+
* Scope: Types only; runtime classifiers, manifests, network rules, and text helpers live in neighboring modules.
|
|
5
|
+
* Usage: Imported with `import type` by result modules and re-exported by the public results facade.
|
|
6
|
+
* Invariants/Assumptions: This file has no runtime policy so adding fields cannot hide behavior in a catch-all module.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import type { AgentBrowserNextAction } from "./next-actions.js";
|
|
10
|
+
|
|
11
|
+
export type { AgentBrowserNextAction } from "./next-actions.js";
|
|
12
|
+
|
|
13
|
+
export interface AgentBrowserEnvelope {
|
|
14
|
+
data?: unknown;
|
|
15
|
+
error?: unknown;
|
|
16
|
+
success: boolean;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export interface AgentBrowserBatchResult {
|
|
20
|
+
command?: string[];
|
|
21
|
+
error?: unknown;
|
|
22
|
+
result?: unknown;
|
|
23
|
+
success?: boolean;
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
export type AgentBrowserResultCategory = "failure" | "success";
|
|
27
|
+
|
|
28
|
+
export type AgentBrowserSuccessCategory = "artifact-saved" | "artifact-unverified" | "completed" | "inspection";
|
|
29
|
+
|
|
30
|
+
export type AgentBrowserFailureCategory =
|
|
31
|
+
| "aborted"
|
|
32
|
+
| "cleanup-failed"
|
|
33
|
+
| "confirmation-required"
|
|
34
|
+
| "download-not-verified"
|
|
35
|
+
| "missing-binary"
|
|
36
|
+
| "parse-failure"
|
|
37
|
+
| "policy-blocked"
|
|
38
|
+
| "qa-failure"
|
|
39
|
+
| "selector-not-found"
|
|
40
|
+
| "selector-unsupported"
|
|
41
|
+
| "stale-ref"
|
|
42
|
+
| "tab-drift"
|
|
43
|
+
| "timeout"
|
|
44
|
+
| "upstream-error"
|
|
45
|
+
| "validation-error";
|
|
46
|
+
|
|
47
|
+
export interface AgentBrowserResultCategoryDetails {
|
|
48
|
+
failureCategory?: AgentBrowserFailureCategory;
|
|
49
|
+
resultCategory: AgentBrowserResultCategory;
|
|
50
|
+
successCategory?: AgentBrowserSuccessCategory;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
export interface AgentBrowserPageChangeSummary {
|
|
54
|
+
artifactCount?: number;
|
|
55
|
+
changeType: "artifact" | "confirmation" | "mutation" | "navigation";
|
|
56
|
+
command?: string;
|
|
57
|
+
nextActionIds?: string[];
|
|
58
|
+
savedFilePath?: string;
|
|
59
|
+
summary: string;
|
|
60
|
+
title?: string;
|
|
61
|
+
url?: string;
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export type FileArtifactKind = "download" | "file" | "har" | "image" | "pdf" | "profile" | "trace" | "video";
|
|
65
|
+
|
|
66
|
+
export type FileArtifactStatus = "missing" | "repaired-from-temp" | "saved" | "upstream-temp-only";
|
|
67
|
+
|
|
68
|
+
export interface FileArtifactMetadata {
|
|
69
|
+
absolutePath: string;
|
|
70
|
+
artifactType?: FileArtifactKind;
|
|
71
|
+
command?: string;
|
|
72
|
+
cwd?: string;
|
|
73
|
+
exists?: boolean;
|
|
74
|
+
extension?: string;
|
|
75
|
+
kind: FileArtifactKind;
|
|
76
|
+
mediaType?: string;
|
|
77
|
+
path: string;
|
|
78
|
+
requestedPath?: string;
|
|
79
|
+
session?: string;
|
|
80
|
+
sizeBytes?: number;
|
|
81
|
+
status?: FileArtifactStatus;
|
|
82
|
+
subcommand?: string;
|
|
83
|
+
tempPath?: string;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
export type ArtifactVerificationState = "missing" | "pending" | "unverified" | "verified";
|
|
87
|
+
|
|
88
|
+
export interface ArtifactVerificationEntry {
|
|
89
|
+
absolutePath?: string;
|
|
90
|
+
exists?: boolean;
|
|
91
|
+
kind: FileArtifactKind | "spill";
|
|
92
|
+
limitation?: string;
|
|
93
|
+
mediaType?: string;
|
|
94
|
+
path: string;
|
|
95
|
+
requestedPath?: string;
|
|
96
|
+
retentionState?: ArtifactRetentionState;
|
|
97
|
+
sizeBytes?: number;
|
|
98
|
+
state: ArtifactVerificationState;
|
|
99
|
+
status?: FileArtifactStatus;
|
|
100
|
+
storageScope?: ArtifactStorageScope;
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
export interface ArtifactVerificationSummary {
|
|
104
|
+
artifacts: ArtifactVerificationEntry[];
|
|
105
|
+
missingCount: number;
|
|
106
|
+
pendingCount: number;
|
|
107
|
+
unverifiedCount: number;
|
|
108
|
+
verified: boolean;
|
|
109
|
+
verifiedCount: number;
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
export interface SavedFilePresentationDetails {
|
|
113
|
+
command: "download" | "pdf" | "wait";
|
|
114
|
+
kind: "download" | "pdf";
|
|
115
|
+
metadata?: Record<string, unknown>;
|
|
116
|
+
path: string;
|
|
117
|
+
subcommand?: string;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
export type ArtifactRetentionState = "evicted" | "ephemeral" | "live" | "missing";
|
|
121
|
+
|
|
122
|
+
export type ArtifactStorageScope = "explicit-path" | "persistent-session" | "process-temp";
|
|
123
|
+
|
|
124
|
+
export interface SessionArtifactManifestEntry {
|
|
125
|
+
absolutePath?: string;
|
|
126
|
+
command?: string;
|
|
127
|
+
createdAtMs: number;
|
|
128
|
+
cwd?: string;
|
|
129
|
+
evictedAtMs?: number;
|
|
130
|
+
exists?: boolean;
|
|
131
|
+
extension?: string;
|
|
132
|
+
kind: FileArtifactKind | "spill";
|
|
133
|
+
mediaType?: string;
|
|
134
|
+
path: string;
|
|
135
|
+
requestedPath?: string;
|
|
136
|
+
retentionState: ArtifactRetentionState;
|
|
137
|
+
session?: string;
|
|
138
|
+
sizeBytes?: number;
|
|
139
|
+
storageScope: ArtifactStorageScope;
|
|
140
|
+
subcommand?: string;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
export interface SessionArtifactManifest {
|
|
144
|
+
entries: SessionArtifactManifestEntry[];
|
|
145
|
+
evictedCount: number;
|
|
146
|
+
liveCount: number;
|
|
147
|
+
maxEntries: number;
|
|
148
|
+
updatedAtMs: number;
|
|
149
|
+
version: 1;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export interface BatchStepPresentationDetails {
|
|
153
|
+
artifactVerification?: ArtifactVerificationSummary;
|
|
154
|
+
artifacts?: FileArtifactMetadata[];
|
|
155
|
+
command?: string[];
|
|
156
|
+
commandText: string;
|
|
157
|
+
data?: unknown;
|
|
158
|
+
failureCategory?: AgentBrowserFailureCategory;
|
|
159
|
+
fullOutputPath?: string;
|
|
160
|
+
fullOutputPaths?: string[];
|
|
161
|
+
imagePath?: string;
|
|
162
|
+
imagePaths?: string[];
|
|
163
|
+
index: number;
|
|
164
|
+
nextActions?: AgentBrowserNextAction[];
|
|
165
|
+
pageChangeSummary?: AgentBrowserPageChangeSummary;
|
|
166
|
+
resultCategory: AgentBrowserResultCategory;
|
|
167
|
+
savedFile?: SavedFilePresentationDetails;
|
|
168
|
+
savedFilePath?: string;
|
|
169
|
+
success: boolean;
|
|
170
|
+
successCategory?: AgentBrowserSuccessCategory;
|
|
171
|
+
summary: string;
|
|
172
|
+
text: string;
|
|
173
|
+
}
|
|
174
|
+
|
|
175
|
+
export interface BatchFailurePresentationDetails {
|
|
176
|
+
failedStep: BatchStepPresentationDetails;
|
|
177
|
+
failureCount: number;
|
|
178
|
+
successCount: number;
|
|
179
|
+
totalCount: number;
|
|
180
|
+
}
|
|
181
|
+
|
|
182
|
+
export interface ToolPresentation {
|
|
183
|
+
artifactManifest?: SessionArtifactManifest;
|
|
184
|
+
artifactRetentionSummary?: string;
|
|
185
|
+
artifactVerification?: ArtifactVerificationSummary;
|
|
186
|
+
artifacts?: FileArtifactMetadata[];
|
|
187
|
+
batchFailure?: BatchFailurePresentationDetails;
|
|
188
|
+
batchSteps?: BatchStepPresentationDetails[];
|
|
189
|
+
content: Array<{ text: string; type: "text" } | { data: string; mimeType: string; type: "image" }>;
|
|
190
|
+
data?: unknown;
|
|
191
|
+
failureCategory?: AgentBrowserFailureCategory;
|
|
192
|
+
fullOutputPath?: string;
|
|
193
|
+
fullOutputPaths?: string[];
|
|
194
|
+
imagePath?: string;
|
|
195
|
+
imagePaths?: string[];
|
|
196
|
+
nextActions?: AgentBrowserNextAction[];
|
|
197
|
+
pageChangeSummary?: AgentBrowserPageChangeSummary;
|
|
198
|
+
resultCategory?: AgentBrowserResultCategory;
|
|
199
|
+
savedFile?: SavedFilePresentationDetails;
|
|
200
|
+
savedFilePath?: string;
|
|
201
|
+
successCategory?: AgentBrowserSuccessCategory;
|
|
202
|
+
summary: string;
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
export type NetworkFailureImpact = "actionable" | "benign";
|
|
206
|
+
|
|
207
|
+
export interface NetworkFailureClassification {
|
|
208
|
+
impact: NetworkFailureImpact;
|
|
209
|
+
reason: string;
|
|
210
|
+
resourceType?: string;
|
|
211
|
+
status?: number;
|
|
212
|
+
url?: string;
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export interface NetworkFailureSummary {
|
|
216
|
+
actionableCount: number;
|
|
217
|
+
benignCount: number;
|
|
218
|
+
failures: NetworkFailureClassification[];
|
|
219
|
+
totalCount: number;
|
|
220
|
+
}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Purpose: Detect whether upstream ref metadata or snapshot text proves an element is editable.
|
|
3
|
+
* Responsibilities: Parse structured ref flags and snapshot-line contenteditable/editable markers into a conservative boolean/unknown signal.
|
|
4
|
+
* Scope: Editable evidence only; callers decide how to rank controls or build recovery actions from the signal.
|
|
5
|
+
* Usage: Imported by snapshot compaction and selector/fill recovery diagnostics.
|
|
6
|
+
* Invariants/Assumptions: Explicit false evidence wins over positive hints; unknown remains undefined.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
const EDITABLE_REF_EVIDENCE_KEYS = ["editable", "contentEditable", "contenteditable", "isContentEditable"] as const;
|
|
10
|
+
const EDITABLE_FALSE_TEXT_PATTERN = /\b(?:contenteditable|editable)\s*=\s*["']?(?:false|0)["']?/i;
|
|
11
|
+
const EDITABLE_ASSIGNMENT_TEXT_PATTERN = /\b(contenteditable|editable)\s*=\s*("[^"]*"|'[^']*'|[^\s,\]]+)/gi;
|
|
12
|
+
const EDITABLE_BARE_TEXT_PATTERN = /\b(?:contenteditable|editable)\b(?!\s*=)/i;
|
|
13
|
+
|
|
14
|
+
function parseEditableEvidenceValue(value: unknown): boolean | undefined {
|
|
15
|
+
if (typeof value === "boolean") return value;
|
|
16
|
+
if (typeof value === "number") {
|
|
17
|
+
if (value === 1) return true;
|
|
18
|
+
if (value === 0) return false;
|
|
19
|
+
return undefined;
|
|
20
|
+
}
|
|
21
|
+
if (typeof value !== "string") return undefined;
|
|
22
|
+
const normalized = value.trim().replace(/^["']|["']$/g, "").toLowerCase();
|
|
23
|
+
if (["false", "0", "no"].includes(normalized)) return false;
|
|
24
|
+
if (["", "true", "1", "yes", "plaintext-only"].includes(normalized)) return true;
|
|
25
|
+
return undefined;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function stripLeadingSnapshotAccessibleName(text: string): string {
|
|
29
|
+
return text.replace(/^(\s*[-*]\s+\S+\s+)(?:"[^"]*"|'[^']*')/, "$1");
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
function parseEditableEvidenceText(text: string | undefined): boolean | undefined {
|
|
33
|
+
if (!text) return undefined;
|
|
34
|
+
const markerText = stripLeadingSnapshotAccessibleName(text);
|
|
35
|
+
if (EDITABLE_FALSE_TEXT_PATTERN.test(markerText)) return false;
|
|
36
|
+
let hasPositiveAssignment = false;
|
|
37
|
+
for (const match of markerText.matchAll(EDITABLE_ASSIGNMENT_TEXT_PATTERN)) {
|
|
38
|
+
const key = match[1]?.toLowerCase();
|
|
39
|
+
const evidence = parseEditableEvidenceValue(match[2]);
|
|
40
|
+
if (evidence === false) return false;
|
|
41
|
+
if (evidence === true && (key === "contenteditable" || key === "editable")) {
|
|
42
|
+
hasPositiveAssignment = true;
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
if (hasPositiveAssignment) return true;
|
|
46
|
+
return EDITABLE_BARE_TEXT_PATTERN.test(markerText) ? true : undefined;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
export function getEditableRefEvidence(options: {
|
|
50
|
+
ref?: Record<string, unknown>;
|
|
51
|
+
text?: string;
|
|
52
|
+
}): boolean | undefined {
|
|
53
|
+
let hasPositiveEvidence = false;
|
|
54
|
+
if (options.ref) {
|
|
55
|
+
for (const key of EDITABLE_REF_EVIDENCE_KEYS) {
|
|
56
|
+
const evidence = parseEditableEvidenceValue(options.ref[key]);
|
|
57
|
+
if (evidence === false) return false;
|
|
58
|
+
if (evidence === true) hasPositiveEvidence = true;
|
|
59
|
+
}
|
|
60
|
+
}
|
|
61
|
+
const textEvidence = parseEditableEvidenceText(options.text);
|
|
62
|
+
if (textEvidence === false) return false;
|
|
63
|
+
if (textEvidence === true) hasPositiveEvidence = true;
|
|
64
|
+
return hasPositiveEvidence ? true : undefined;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
export function hasPositiveEditableRefEvidence(options: {
|
|
68
|
+
ref?: Record<string, unknown>;
|
|
69
|
+
text?: string;
|
|
70
|
+
}): boolean {
|
|
71
|
+
return getEditableRefEvidence(options) === true;
|
|
72
|
+
}
|
|
@@ -10,7 +10,8 @@ import { readFile } from "node:fs/promises";
|
|
|
10
10
|
|
|
11
11
|
import { isRecord } from "../parsing.js";
|
|
12
12
|
import { detectConfirmationRequired } from "./confirmation.js";
|
|
13
|
-
import {
|
|
13
|
+
import type { AgentBrowserBatchResult, AgentBrowserEnvelope } from "./contracts.js";
|
|
14
|
+
import { stringifyUnknown } from "./text.js";
|
|
14
15
|
|
|
15
16
|
function hasStructuredBatchStepFailure(data: unknown): data is AgentBrowserBatchResult[] {
|
|
16
17
|
return Array.isArray(data) && data.some((item) => isRecord(item) && item.success === false);
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Purpose: Classify failed network requests into actionable vs benign diagnostics.
|
|
3
|
+
* Responsibilities: Recognize failed request rows, de-prioritize browser icon misses, and summarize failure counts.
|
|
4
|
+
* Scope: Network diagnostic classification only.
|
|
5
|
+
* Usage: QA preset analysis and presentation network summaries.
|
|
6
|
+
* Invariants/Assumptions: Browser favicon/apple-touch icon misses are warnings; API/document/script failures are actionable.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
import { isRecord } from "../parsing.js";
|
|
10
|
+
import type { NetworkFailureClassification, NetworkFailureSummary } from "./contracts.js";
|
|
11
|
+
|
|
12
|
+
function getStringRecordField(value: Record<string, unknown>, key: string): string | undefined {
|
|
13
|
+
const field = value[key];
|
|
14
|
+
return typeof field === "string" && field.trim().length > 0 ? field.trim() : undefined;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
function getNetworkRequestUrlPath(url: string | undefined): string | undefined {
|
|
18
|
+
if (!url) return undefined;
|
|
19
|
+
try {
|
|
20
|
+
return new URL(url).pathname;
|
|
21
|
+
} catch {
|
|
22
|
+
const withoutQuery = url.split(/[?#]/, 1)[0];
|
|
23
|
+
return withoutQuery.length > 0 ? withoutQuery : undefined;
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
function isFailedNetworkRequest(request: Record<string, unknown>): boolean {
|
|
28
|
+
return (typeof request.status === "number" && request.status >= 400) || request.failed === true || typeof request.error === "string";
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
function isBenignAssetFailure(request: Record<string, unknown>, url: string | undefined, resourceType: string | undefined): boolean {
|
|
32
|
+
const path = getNetworkRequestUrlPath(url);
|
|
33
|
+
if (!path) return false;
|
|
34
|
+
const normalizedResourceType = resourceType?.toLowerCase();
|
|
35
|
+
return /(?:^|\/)(?:favicon(?:[-.\w]*)?\.(?:ico|png|svg)|apple-touch-icon(?:[-.\w]*)?\.png)$/i.test(path)
|
|
36
|
+
&& (request.status === 404 || request.failed === true || typeof request.error === "string")
|
|
37
|
+
&& (!normalizedResourceType || ["image", "img", "other"].includes(normalizedResourceType) || normalizedResourceType.startsWith("image/"));
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function classifyNetworkRequestFailure(request: Record<string, unknown>): NetworkFailureClassification | undefined {
|
|
41
|
+
if (!isFailedNetworkRequest(request)) return undefined;
|
|
42
|
+
const url = getStringRecordField(request, "url");
|
|
43
|
+
const resourceType = getStringRecordField(request, "resourceType") ?? getStringRecordField(request, "mimeType");
|
|
44
|
+
const status = typeof request.status === "number" ? request.status : undefined;
|
|
45
|
+
if (isBenignAssetFailure(request, url, resourceType)) {
|
|
46
|
+
return { impact: "benign", reason: "low-impact browser icon asset", resourceType, status, url };
|
|
47
|
+
}
|
|
48
|
+
return { impact: "actionable", reason: "document, script, API, or non-benign request failure", resourceType, status, url };
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function summarizeNetworkFailures(requests: unknown[]): NetworkFailureSummary {
|
|
52
|
+
const failures = requests.flatMap((request) => {
|
|
53
|
+
if (!isRecord(request)) return [];
|
|
54
|
+
const classification = classifyNetworkRequestFailure(request);
|
|
55
|
+
return classification ? [classification] : [];
|
|
56
|
+
});
|
|
57
|
+
const benignCount = failures.filter((failure) => failure.impact === "benign").length;
|
|
58
|
+
return {
|
|
59
|
+
actionableCount: failures.length - benignCount,
|
|
60
|
+
benignCount,
|
|
61
|
+
failures,
|
|
62
|
+
totalCount: failures.length,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Purpose: Own machine-readable agent_browser next-action contracts and merge policy.
|
|
3
|
+
* Responsibilities: Define the stable nextAction shape, build basic argv follow-ups, and provide deterministic action-list collection helpers.
|
|
4
|
+
* Scope: Result follow-up action mechanics only; command-specific recovery and artifact policies live in neighboring modules.
|
|
5
|
+
* Usage: Imported by result presentation helpers and the extension entrypoint when attaching details.nextActions.
|
|
6
|
+
* Invariants/Assumptions: Action ids are stable machine-readable contracts; dedupe preserves first occurrence order.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
export interface AgentBrowserNextAction {
|
|
10
|
+
artifactPath?: string;
|
|
11
|
+
id: string;
|
|
12
|
+
params?: {
|
|
13
|
+
args?: string[];
|
|
14
|
+
electron?: {
|
|
15
|
+
action: "cleanup" | "list" | "launch" | "probe" | "status";
|
|
16
|
+
all?: boolean;
|
|
17
|
+
handoff?: "connect" | "snapshot" | "tabs";
|
|
18
|
+
launchId?: string;
|
|
19
|
+
};
|
|
20
|
+
networkSourceLookup?: {
|
|
21
|
+
filter?: string;
|
|
22
|
+
requestId?: string;
|
|
23
|
+
session?: string;
|
|
24
|
+
url?: string;
|
|
25
|
+
};
|
|
26
|
+
sessionMode?: "auto" | "fresh";
|
|
27
|
+
stdin?: string;
|
|
28
|
+
};
|
|
29
|
+
reason: string;
|
|
30
|
+
safety?: string;
|
|
31
|
+
tool: "agent_browser";
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function withOptionalSessionArgs(sessionName: string | undefined, args: string[]): string[] {
|
|
35
|
+
return sessionName && args[0] !== "--session" ? ["--session", sessionName, ...args] : args;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function buildNextToolAction(options: {
|
|
39
|
+
args: string[];
|
|
40
|
+
id: string;
|
|
41
|
+
reason: string;
|
|
42
|
+
safety?: string;
|
|
43
|
+
sessionMode?: "auto" | "fresh";
|
|
44
|
+
stdin?: string;
|
|
45
|
+
}): AgentBrowserNextAction {
|
|
46
|
+
return {
|
|
47
|
+
id: options.id,
|
|
48
|
+
params: {
|
|
49
|
+
args: options.args,
|
|
50
|
+
...(options.sessionMode ? { sessionMode: options.sessionMode } : {}),
|
|
51
|
+
...(options.stdin ? { stdin: options.stdin } : {}),
|
|
52
|
+
},
|
|
53
|
+
reason: options.reason,
|
|
54
|
+
...(options.safety ? { safety: options.safety } : {}),
|
|
55
|
+
tool: "agent_browser",
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
export function appendUniqueAgentBrowserNextActions(
|
|
60
|
+
target: AgentBrowserNextAction[],
|
|
61
|
+
additions: AgentBrowserNextAction[] | undefined,
|
|
62
|
+
): AgentBrowserNextAction[] {
|
|
63
|
+
if (!additions || additions.length === 0) return target;
|
|
64
|
+
const existingIds = new Set(target.map((action) => action.id));
|
|
65
|
+
for (const action of additions) {
|
|
66
|
+
if (existingIds.has(action.id)) continue;
|
|
67
|
+
target.push(action);
|
|
68
|
+
existingIds.add(action.id);
|
|
69
|
+
}
|
|
70
|
+
return target;
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
export function isStandaloneSnapshotNextAction(action: AgentBrowserNextAction): boolean {
|
|
74
|
+
const args = action.params?.args;
|
|
75
|
+
if (!args || action.params?.stdin) return false;
|
|
76
|
+
const commandIndex = args[0] === "--session" ? 2 : 0;
|
|
77
|
+
return args[commandIndex] === "snapshot";
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export function alignPageChangeSummaryNextActionIds<T extends { nextActionIds?: string[] }>(
|
|
81
|
+
summary: T | undefined,
|
|
82
|
+
nextActions: AgentBrowserNextAction[] | undefined,
|
|
83
|
+
): T | undefined {
|
|
84
|
+
if (!summary?.nextActionIds || !nextActions) return summary;
|
|
85
|
+
const nextActionIds = new Set(nextActions.map((action) => action.id));
|
|
86
|
+
const alignedIds = summary.nextActionIds.filter((id) => nextActionIds.has(id));
|
|
87
|
+
return alignedIds.length > 0 ? { ...summary, nextActionIds: alignedIds } : { ...summary, nextActionIds: undefined };
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export class AgentBrowserNextActionCollector {
|
|
91
|
+
private actions: AgentBrowserNextAction[];
|
|
92
|
+
|
|
93
|
+
constructor(initialActions: AgentBrowserNextAction[] | undefined = undefined) {
|
|
94
|
+
this.actions = initialActions ? [...initialActions] : [];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
append(actions: AgentBrowserNextAction[] | undefined): void {
|
|
98
|
+
if (!actions || actions.length === 0) return;
|
|
99
|
+
this.actions.push(...actions);
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
appendUnique(actions: AgentBrowserNextAction[] | undefined): void {
|
|
103
|
+
appendUniqueAgentBrowserNextActions(this.actions, actions);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
replace(actions: AgentBrowserNextAction[] | undefined): void {
|
|
107
|
+
this.actions = actions ? [...actions] : [];
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
removeWhere(predicate: (action: AgentBrowserNextAction) => boolean): void {
|
|
111
|
+
this.actions = this.actions.filter((action) => !predicate(action));
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
toArray(): AgentBrowserNextAction[] | undefined {
|
|
115
|
+
return this.actions.length > 0 ? [...this.actions] : undefined;
|
|
116
|
+
}
|
|
117
|
+
}
|