pi-agent-browser-native 0.2.48 → 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 +17 -0
- package/README.md +16 -6
- 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/RELEASE.md +22 -11
- package/docs/SUPPORT_MATRIX.md +4 -3
- package/package.json +9 -5
- package/scripts/config.mjs +8 -2
- package/scripts/doctor.mjs +8 -7
- package/extensions/agent-browser/index.ts +0 -961
- 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 -211
- 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 -527
- 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/artifact-paths.ts +0 -44
- package/extensions/agent-browser/lib/orchestration/browser-run/click-dispatch.ts +0 -280
- package/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.ts +0 -914
- package/extensions/agent-browser/lib/orchestration/browser-run/final-result.ts +0 -521
- package/extensions/agent-browser/lib/orchestration/browser-run/index.ts +0 -53
- package/extensions/agent-browser/lib/orchestration/browser-run/prepare/direct-anchor-download.ts +0 -158
- package/extensions/agent-browser/lib/orchestration/browser-run/prepare/network-page-filter.ts +0 -116
- package/extensions/agent-browser/lib/orchestration/browser-run/prepare/scroll-shims.ts +0 -147
- package/extensions/agent-browser/lib/orchestration/browser-run/prepare/snapshot-filter.ts +0 -183
- package/extensions/agent-browser/lib/orchestration/browser-run/prepare/wait-timeouts.ts +0 -58
- package/extensions/agent-browser/lib/orchestration/browser-run/prepare.ts +0 -847
- package/extensions/agent-browser/lib/orchestration/browser-run/process-output.ts +0 -559
- package/extensions/agent-browser/lib/orchestration/browser-run/prompt-guards.ts +0 -47
- package/extensions/agent-browser/lib/orchestration/browser-run/session-artifacts.ts +0 -8
- package/extensions/agent-browser/lib/orchestration/browser-run/session-state.ts +0 -868
- package/extensions/agent-browser/lib/orchestration/browser-run/types.ts +0 -565
- 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 -267
- 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 -728
- /package/{extensions/agent-browser/lib/orchestration/browser-run.ts → dist/extensions/agent-browser/lib/orchestration/browser-run.js} +0 -0
|
@@ -1,241 +0,0 @@
|
|
|
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
|
-
| "artifact-missing"
|
|
33
|
-
| "cleanup-failed"
|
|
34
|
-
| "confirmation-required"
|
|
35
|
-
| "download-not-verified"
|
|
36
|
-
| "missing-binary"
|
|
37
|
-
| "parse-failure"
|
|
38
|
-
| "policy-blocked"
|
|
39
|
-
| "qa-failure"
|
|
40
|
-
| "selector-not-found"
|
|
41
|
-
| "selector-unsupported"
|
|
42
|
-
| "stale-ref"
|
|
43
|
-
| "tab-drift"
|
|
44
|
-
| "timeout"
|
|
45
|
-
| "upstream-error"
|
|
46
|
-
| "validation-error";
|
|
47
|
-
|
|
48
|
-
export interface AgentBrowserResultCategoryDetails {
|
|
49
|
-
failureCategory?: AgentBrowserFailureCategory;
|
|
50
|
-
resultCategory: AgentBrowserResultCategory;
|
|
51
|
-
successCategory?: AgentBrowserSuccessCategory;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export interface AgentBrowserPageChangeSummary {
|
|
55
|
-
artifactCount?: number;
|
|
56
|
-
changeType: "artifact" | "confirmation" | "mutation" | "navigation";
|
|
57
|
-
command?: string;
|
|
58
|
-
nextActionIds?: string[];
|
|
59
|
-
savedFilePath?: string;
|
|
60
|
-
summary: string;
|
|
61
|
-
title?: string;
|
|
62
|
-
url?: string;
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export type FileArtifactKind = "download" | "file" | "har" | "image" | "pdf" | "profile" | "trace" | "video";
|
|
66
|
-
|
|
67
|
-
export type FileArtifactStatus = "missing" | "pending" | "repaired-from-temp" | "saved" | "upstream-temp-only";
|
|
68
|
-
|
|
69
|
-
export interface FileArtifactMetadata {
|
|
70
|
-
absolutePath: string;
|
|
71
|
-
artifactType?: FileArtifactKind;
|
|
72
|
-
command?: string;
|
|
73
|
-
cwd?: string;
|
|
74
|
-
exists?: boolean;
|
|
75
|
-
extension?: string;
|
|
76
|
-
kind: FileArtifactKind;
|
|
77
|
-
mediaType?: string;
|
|
78
|
-
path: string;
|
|
79
|
-
recordingState?: "openRecording";
|
|
80
|
-
requestedPath?: string;
|
|
81
|
-
session?: string;
|
|
82
|
-
sizeBytes?: number;
|
|
83
|
-
status?: FileArtifactStatus;
|
|
84
|
-
subcommand?: string;
|
|
85
|
-
tempPath?: string;
|
|
86
|
-
willExistOnStop?: boolean;
|
|
87
|
-
}
|
|
88
|
-
|
|
89
|
-
export type ArtifactVerificationState = "missing" | "pending" | "unverified" | "verified";
|
|
90
|
-
|
|
91
|
-
export interface ArtifactVerificationEntry {
|
|
92
|
-
absolutePath?: string;
|
|
93
|
-
exists?: boolean;
|
|
94
|
-
kind: FileArtifactKind | "spill";
|
|
95
|
-
limitation?: string;
|
|
96
|
-
mediaType?: string;
|
|
97
|
-
path: string;
|
|
98
|
-
requestedPath?: string;
|
|
99
|
-
recordingState?: "openRecording";
|
|
100
|
-
retentionState?: ArtifactRetentionState;
|
|
101
|
-
sizeBytes?: number;
|
|
102
|
-
state: ArtifactVerificationState;
|
|
103
|
-
status?: FileArtifactStatus;
|
|
104
|
-
storageScope?: ArtifactStorageScope;
|
|
105
|
-
willExistOnStop?: boolean;
|
|
106
|
-
}
|
|
107
|
-
|
|
108
|
-
export interface ArtifactVerificationSummary {
|
|
109
|
-
artifacts: ArtifactVerificationEntry[];
|
|
110
|
-
missingCount: number;
|
|
111
|
-
pendingCount: number;
|
|
112
|
-
unverifiedCount: number;
|
|
113
|
-
verified: boolean;
|
|
114
|
-
verifiedCount: number;
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
export interface SavedFilePresentationDetails {
|
|
118
|
-
command: "download" | "pdf" | "wait";
|
|
119
|
-
kind: "download" | "pdf";
|
|
120
|
-
metadata?: Record<string, unknown>;
|
|
121
|
-
path: string;
|
|
122
|
-
subcommand?: string;
|
|
123
|
-
}
|
|
124
|
-
|
|
125
|
-
export type ArtifactRetentionState = "evicted" | "ephemeral" | "live" | "missing";
|
|
126
|
-
|
|
127
|
-
export type ArtifactStorageScope = "explicit-path" | "persistent-session" | "process-temp";
|
|
128
|
-
|
|
129
|
-
export interface SessionArtifactManifestEntry {
|
|
130
|
-
absolutePath?: string;
|
|
131
|
-
command?: string;
|
|
132
|
-
createdAtMs: number;
|
|
133
|
-
cwd?: string;
|
|
134
|
-
evictedAtMs?: number;
|
|
135
|
-
exists?: boolean;
|
|
136
|
-
extension?: string;
|
|
137
|
-
kind: FileArtifactKind | "spill";
|
|
138
|
-
mediaType?: string;
|
|
139
|
-
path: string;
|
|
140
|
-
requestedPath?: string;
|
|
141
|
-
retentionState: ArtifactRetentionState;
|
|
142
|
-
session?: string;
|
|
143
|
-
sizeBytes?: number;
|
|
144
|
-
storageScope: ArtifactStorageScope;
|
|
145
|
-
subcommand?: string;
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
export interface SessionArtifactManifest {
|
|
149
|
-
entries: SessionArtifactManifestEntry[];
|
|
150
|
-
evictedCount: number;
|
|
151
|
-
liveCount: number;
|
|
152
|
-
maxEntries: number;
|
|
153
|
-
updatedAtMs: number;
|
|
154
|
-
version: 1;
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
export interface BatchStepPresentationDetails {
|
|
158
|
-
artifactVerification?: ArtifactVerificationSummary;
|
|
159
|
-
artifacts?: FileArtifactMetadata[];
|
|
160
|
-
command?: string[];
|
|
161
|
-
commandText: string;
|
|
162
|
-
data?: unknown;
|
|
163
|
-
failureCategory?: AgentBrowserFailureCategory;
|
|
164
|
-
fullOutputPath?: string;
|
|
165
|
-
fullOutputPaths?: string[];
|
|
166
|
-
imagePath?: string;
|
|
167
|
-
imagePaths?: string[];
|
|
168
|
-
index: number;
|
|
169
|
-
networkRouteDiagnostics?: NetworkRouteDiagnostic[];
|
|
170
|
-
nextActions?: AgentBrowserNextAction[];
|
|
171
|
-
pageChangeSummary?: AgentBrowserPageChangeSummary;
|
|
172
|
-
resultCategory: AgentBrowserResultCategory;
|
|
173
|
-
savedFile?: SavedFilePresentationDetails;
|
|
174
|
-
savedFilePath?: string;
|
|
175
|
-
success: boolean;
|
|
176
|
-
successCategory?: AgentBrowserSuccessCategory;
|
|
177
|
-
summary: string;
|
|
178
|
-
text: string;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
export interface BatchFailurePresentationDetails {
|
|
182
|
-
failedStep: BatchStepPresentationDetails;
|
|
183
|
-
failureCount: number;
|
|
184
|
-
successCount: number;
|
|
185
|
-
totalCount: number;
|
|
186
|
-
}
|
|
187
|
-
|
|
188
|
-
export interface ToolPresentation {
|
|
189
|
-
artifactManifest?: SessionArtifactManifest;
|
|
190
|
-
artifactRetentionSummary?: string;
|
|
191
|
-
artifactVerification?: ArtifactVerificationSummary;
|
|
192
|
-
artifacts?: FileArtifactMetadata[];
|
|
193
|
-
batchFailure?: BatchFailurePresentationDetails;
|
|
194
|
-
batchSteps?: BatchStepPresentationDetails[];
|
|
195
|
-
content: Array<{ text: string; type: "text" } | { data: string; mimeType: string; type: "image" }>;
|
|
196
|
-
data?: unknown;
|
|
197
|
-
failureCategory?: AgentBrowserFailureCategory;
|
|
198
|
-
fullOutputPath?: string;
|
|
199
|
-
fullOutputPaths?: string[];
|
|
200
|
-
imagePath?: string;
|
|
201
|
-
imagePaths?: string[];
|
|
202
|
-
networkRouteDiagnostics?: NetworkRouteDiagnostic[];
|
|
203
|
-
nextActions?: AgentBrowserNextAction[];
|
|
204
|
-
pageChangeSummary?: AgentBrowserPageChangeSummary;
|
|
205
|
-
resultCategory?: AgentBrowserResultCategory;
|
|
206
|
-
savedFile?: SavedFilePresentationDetails;
|
|
207
|
-
savedFilePath?: string;
|
|
208
|
-
successCategory?: AgentBrowserSuccessCategory;
|
|
209
|
-
summary: string;
|
|
210
|
-
}
|
|
211
|
-
|
|
212
|
-
export type NetworkFailureImpact = "actionable" | "benign";
|
|
213
|
-
|
|
214
|
-
export interface NetworkFailureClassification {
|
|
215
|
-
impact: NetworkFailureImpact;
|
|
216
|
-
reason: string;
|
|
217
|
-
resourceType?: string;
|
|
218
|
-
status?: number;
|
|
219
|
-
url?: string;
|
|
220
|
-
}
|
|
221
|
-
|
|
222
|
-
export interface NetworkFailureSummary {
|
|
223
|
-
actionableCount: number;
|
|
224
|
-
benignCount: number;
|
|
225
|
-
failures: NetworkFailureClassification[];
|
|
226
|
-
totalCount: number;
|
|
227
|
-
}
|
|
228
|
-
|
|
229
|
-
export interface NetworkRouteRecord {
|
|
230
|
-
mode: "abort" | "body" | "handler" | "unknown";
|
|
231
|
-
pattern: string;
|
|
232
|
-
}
|
|
233
|
-
|
|
234
|
-
export interface NetworkRouteDiagnostic {
|
|
235
|
-
mode: NetworkRouteRecord["mode"];
|
|
236
|
-
reason: "pending-routed-request" | "cors-likely-routed-request" | "unfulfilled-routed-request";
|
|
237
|
-
requestId?: string;
|
|
238
|
-
requestUrl?: string;
|
|
239
|
-
routePattern: string;
|
|
240
|
-
summary: string;
|
|
241
|
-
}
|
|
@@ -1,72 +0,0 @@
|
|
|
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
|
-
}
|
|
@@ -1,195 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Purpose: Parse upstream agent-browser output and turn failure envelopes into actionable error text.
|
|
3
|
-
* Responsibilities: Read inline or spilled stdout, parse observed JSON envelope shapes, normalize batch arrays, and extract the most useful error text from nested upstream failures.
|
|
4
|
-
* Scope: Envelope parsing and error derivation only; content rendering and snapshot compaction live in separate modules.
|
|
5
|
-
* Usage: Imported by the public `lib/results.ts` facade and by tests through that facade.
|
|
6
|
-
* Invariants/Assumptions: Upstream `agent-browser --json` responses follow the observed `{ success, data, error }` envelope shape or the array shape returned by `batch --json`.
|
|
7
|
-
*/
|
|
8
|
-
|
|
9
|
-
import { readFile } from "node:fs/promises";
|
|
10
|
-
|
|
11
|
-
import { isRecord } from "../parsing.js";
|
|
12
|
-
import { detectConfirmationRequired } from "./confirmation.js";
|
|
13
|
-
import type { AgentBrowserBatchResult, AgentBrowserEnvelope } from "./contracts.js";
|
|
14
|
-
import { stringifyUnknown } from "./text.js";
|
|
15
|
-
|
|
16
|
-
function hasStructuredBatchStepFailure(data: unknown): data is AgentBrowserBatchResult[] {
|
|
17
|
-
return Array.isArray(data) && data.some((item) => isRecord(item) && item.success === false);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
async function readEnvelopeSource(options: { stdout: string; stdoutPath?: string }): Promise<string> {
|
|
21
|
-
if (!options.stdoutPath) {
|
|
22
|
-
return options.stdout;
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
try {
|
|
26
|
-
return await readFile(options.stdoutPath, "utf8");
|
|
27
|
-
} catch (error) {
|
|
28
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
29
|
-
throw new Error(`agent-browser output spill file could not be read: ${message}`);
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
function extractEnvelopeErrorText(error: unknown): string | undefined {
|
|
34
|
-
if (typeof error === "string") {
|
|
35
|
-
return error.trim() || undefined;
|
|
36
|
-
}
|
|
37
|
-
if (typeof error === "number" || typeof error === "boolean") {
|
|
38
|
-
return String(error);
|
|
39
|
-
}
|
|
40
|
-
if (Array.isArray(error)) {
|
|
41
|
-
const parts = error.map((item) => extractEnvelopeErrorText(item) ?? stringifyUnknown(item)).filter((item) => item.length > 0);
|
|
42
|
-
return parts.length > 0 ? parts.join("\n") : undefined;
|
|
43
|
-
}
|
|
44
|
-
if (!isRecord(error)) {
|
|
45
|
-
return error == null ? undefined : stringifyUnknown(error);
|
|
46
|
-
}
|
|
47
|
-
|
|
48
|
-
for (const key of ["message", "error", "details", "cause", "stderr"] as const) {
|
|
49
|
-
const value = extractEnvelopeErrorText(error[key]);
|
|
50
|
-
if (value) return value;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const fallback = stringifyUnknown(error).trim();
|
|
54
|
-
return fallback.length > 0 && fallback !== "{}" ? fallback : undefined;
|
|
55
|
-
}
|
|
56
|
-
|
|
57
|
-
export async function parseAgentBrowserEnvelope(options: string | { stdout: string; stdoutPath?: string }): Promise<{
|
|
58
|
-
envelope?: AgentBrowserEnvelope;
|
|
59
|
-
parseError?: string;
|
|
60
|
-
}> {
|
|
61
|
-
let stdout: string;
|
|
62
|
-
try {
|
|
63
|
-
stdout = typeof options === "string" ? options : await readEnvelopeSource(options);
|
|
64
|
-
} catch (error) {
|
|
65
|
-
return { parseError: error instanceof Error ? error.message : String(error) };
|
|
66
|
-
}
|
|
67
|
-
|
|
68
|
-
const trimmed = stdout.trim();
|
|
69
|
-
if (trimmed.length === 0) {
|
|
70
|
-
return { parseError: "agent-browser returned no JSON output." };
|
|
71
|
-
}
|
|
72
|
-
|
|
73
|
-
try {
|
|
74
|
-
const parsed = JSON.parse(trimmed) as AgentBrowserEnvelope | AgentBrowserBatchResult[];
|
|
75
|
-
if (Array.isArray(parsed)) {
|
|
76
|
-
return { envelope: { success: parsed.every((item) => !isRecord(item) || item.success !== false), data: parsed } };
|
|
77
|
-
}
|
|
78
|
-
if (!isRecord(parsed)) {
|
|
79
|
-
return { parseError: "agent-browser returned JSON, but it was not an object envelope." };
|
|
80
|
-
}
|
|
81
|
-
if (!("success" in parsed)) {
|
|
82
|
-
return { parseError: "agent-browser returned an invalid JSON envelope: missing boolean success field." };
|
|
83
|
-
}
|
|
84
|
-
if (typeof parsed.success !== "boolean") {
|
|
85
|
-
return { parseError: "agent-browser returned an invalid JSON envelope: success field must be boolean." };
|
|
86
|
-
}
|
|
87
|
-
if (!Object.hasOwn(parsed, "data")) {
|
|
88
|
-
const { success, error, ...topLevelData } = parsed;
|
|
89
|
-
if (Object.keys(topLevelData).length > 0) {
|
|
90
|
-
return { envelope: { error, success, data: topLevelData } as AgentBrowserEnvelope };
|
|
91
|
-
}
|
|
92
|
-
}
|
|
93
|
-
return { envelope: parsed as AgentBrowserEnvelope };
|
|
94
|
-
} catch (error) {
|
|
95
|
-
const message = error instanceof Error ? error.message : String(error);
|
|
96
|
-
return { parseError: `agent-browser returned invalid JSON: ${message}` };
|
|
97
|
-
}
|
|
98
|
-
}
|
|
99
|
-
|
|
100
|
-
function buildInvocationLabel(options: { command?: string; effectiveArgs?: string[] }): string {
|
|
101
|
-
if (options.effectiveArgs && options.effectiveArgs.length > 0) {
|
|
102
|
-
return `agent-browser ${options.effectiveArgs.join(" ")}`;
|
|
103
|
-
}
|
|
104
|
-
if (options.command && options.command.trim().length > 0) {
|
|
105
|
-
return `agent-browser ${options.command.trim()}`;
|
|
106
|
-
}
|
|
107
|
-
return "agent-browser";
|
|
108
|
-
}
|
|
109
|
-
|
|
110
|
-
function appendWrapperRecoveryHint(message: string, wrapperRecoveryHint?: string): string {
|
|
111
|
-
const hint = wrapperRecoveryHint?.trim();
|
|
112
|
-
return hint ? `${message}\n${hint}` : message;
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
function buildFailureFallback(options: { command?: string; effectiveArgs?: string[]; exitCode: number; wrapperRecoveryHint?: string }): string {
|
|
116
|
-
const invocation = buildInvocationLabel(options);
|
|
117
|
-
const exitSuffix = options.exitCode !== 0 ? ` (exit code ${options.exitCode})` : "";
|
|
118
|
-
return appendWrapperRecoveryHint(`${invocation} reported failure${exitSuffix}.`, options.wrapperRecoveryHint);
|
|
119
|
-
}
|
|
120
|
-
|
|
121
|
-
function buildExitCodeFallback(options: { command?: string; effectiveArgs?: string[]; exitCode: number; wrapperRecoveryHint?: string }): string {
|
|
122
|
-
const invocation = buildInvocationLabel(options);
|
|
123
|
-
return appendWrapperRecoveryHint(`${invocation} exited with code ${options.exitCode}.`, options.wrapperRecoveryHint);
|
|
124
|
-
}
|
|
125
|
-
|
|
126
|
-
function buildWatchdogTimeoutMessage(options: { timeoutMs?: number }): string {
|
|
127
|
-
const timeoutText = options.timeoutMs === undefined ? "the wrapper watchdog" : `the ${options.timeoutMs}ms wrapper watchdog`;
|
|
128
|
-
const ipcTiming = options.timeoutMs !== undefined && options.timeoutMs <= 30_000
|
|
129
|
-
? "before the upstream CLI entered its 30s IPC retry path"
|
|
130
|
-
: "after waiting beyond the upstream CLI's 30s IPC retry window";
|
|
131
|
-
return [
|
|
132
|
-
`agent-browser exceeded ${timeoutText} and was stopped ${ipcTiming}.`,
|
|
133
|
-
"Prefer a condition wait or split long work into shorter calls; for legitimately long opens or captures, pass agent_browser timeoutMs with a bounded higher value and inspect details.timeoutPartialProgress before retrying.",
|
|
134
|
-
].join(" ");
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
function isUpstreamIpcReadTimeoutMessage(message: string): boolean {
|
|
138
|
-
return /Failed to read: Resource temporarily unavailable(?: \(os error \d+\))?.*daemon may be busy or unresponsive/i.test(message);
|
|
139
|
-
}
|
|
140
|
-
|
|
141
|
-
function buildUpstreamIpcReadTimeoutMessage(): string {
|
|
142
|
-
return [
|
|
143
|
-
"agent-browser hit the upstream CLI 30s IPC read timeout while waiting for the daemon response.",
|
|
144
|
-
"The daemon may still be alive; do not blindly retry a non-idempotent command. Prefer a shorter command, split long waits, or retry with sessionMode: \"fresh\" after checking tab list.",
|
|
145
|
-
].join(" ");
|
|
146
|
-
}
|
|
147
|
-
|
|
148
|
-
function maybeAppendStaleRefHint(message: string, args?: string[]): string {
|
|
149
|
-
const usedRef = args?.some((arg) => /^@e\d+\b/.test(arg)) ?? false;
|
|
150
|
-
if (!usedRef || !/could not locate element|element not found|no element/i.test(message)) {
|
|
151
|
-
return message;
|
|
152
|
-
}
|
|
153
|
-
return [
|
|
154
|
-
message,
|
|
155
|
-
"This @ref may be stale after navigation, scrolling, or a DOM update. Run `agent_browser` with `{ \"args\": [\"snapshot\", \"-i\"] }` again and retry with a current ref, or use a stable `find` locator.",
|
|
156
|
-
].join("\n");
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
export function getAgentBrowserErrorText(options: {
|
|
160
|
-
aborted: boolean;
|
|
161
|
-
command?: string;
|
|
162
|
-
effectiveArgs?: string[];
|
|
163
|
-
envelope?: AgentBrowserEnvelope;
|
|
164
|
-
exitCode: number;
|
|
165
|
-
parseError?: string;
|
|
166
|
-
plainTextInspection: boolean;
|
|
167
|
-
spawnError?: Error;
|
|
168
|
-
staleRefArgs?: string[];
|
|
169
|
-
stderr: string;
|
|
170
|
-
timedOut?: boolean;
|
|
171
|
-
timeoutMs?: number;
|
|
172
|
-
wrapperRecoveryHint?: string;
|
|
173
|
-
}): string | undefined {
|
|
174
|
-
const { aborted, envelope, exitCode, parseError, plainTextInspection, spawnError, stderr, timedOut } = options;
|
|
175
|
-
if (plainTextInspection) return undefined;
|
|
176
|
-
if (timedOut) return buildWatchdogTimeoutMessage(options);
|
|
177
|
-
if (aborted) return "agent-browser was aborted.";
|
|
178
|
-
if (spawnError) return spawnError.message;
|
|
179
|
-
if (parseError) return parseError;
|
|
180
|
-
if (envelope?.success === false) {
|
|
181
|
-
if ((hasStructuredBatchStepFailure(envelope.data) || detectConfirmationRequired(envelope.data)) && envelope.error === undefined) {
|
|
182
|
-
return undefined;
|
|
183
|
-
}
|
|
184
|
-
const envelopeErrorText = extractEnvelopeErrorText(envelope.error);
|
|
185
|
-
if (envelopeErrorText && isUpstreamIpcReadTimeoutMessage(envelopeErrorText)) {
|
|
186
|
-
return buildUpstreamIpcReadTimeoutMessage();
|
|
187
|
-
}
|
|
188
|
-
const fallback = envelopeErrorText ?? (stderr.trim() || buildFailureFallback(options));
|
|
189
|
-
return maybeAppendStaleRefHint(fallback, options.staleRefArgs ?? options.effectiveArgs);
|
|
190
|
-
}
|
|
191
|
-
if (exitCode !== 0) {
|
|
192
|
-
return stderr.trim() || buildExitCodeFallback(options);
|
|
193
|
-
}
|
|
194
|
-
return undefined;
|
|
195
|
-
}
|
|
@@ -1,83 +0,0 @@
|
|
|
1
|
-
import { isRecord } from "../parsing.js";
|
|
2
|
-
import { redactSensitiveText } from "../runtime.js";
|
|
3
|
-
import type { NetworkRouteDiagnostic, NetworkRouteRecord } from "./contracts.js";
|
|
4
|
-
import { getStringRecordField, isApiLikeNetworkRequest } from "./network.js";
|
|
5
|
-
|
|
6
|
-
function getArrayField(data: Record<string, unknown>, key: string): unknown[] | undefined {
|
|
7
|
-
const value = data[key];
|
|
8
|
-
return Array.isArray(value) ? value : undefined;
|
|
9
|
-
}
|
|
10
|
-
|
|
11
|
-
function networkRoutePatternMatchesUrl(pattern: string, url: string): boolean {
|
|
12
|
-
if (pattern === url) return true;
|
|
13
|
-
if (pattern.includes("*")) {
|
|
14
|
-
const escaped = pattern.replace(/[.+?^${}()|[\]\\]/g, "\\$&").replace(/\*/g, ".*");
|
|
15
|
-
return new RegExp(`^${escaped}$`).test(url);
|
|
16
|
-
}
|
|
17
|
-
return pattern.length >= 4 && url.includes(pattern);
|
|
18
|
-
}
|
|
19
|
-
|
|
20
|
-
function getSafeRequestId(item: Record<string, unknown>): string | undefined {
|
|
21
|
-
const requestId = getStringRecordField(item, "requestId") ?? getStringRecordField(item, "id");
|
|
22
|
-
if (!requestId || redactSensitiveText(requestId) !== requestId) return undefined;
|
|
23
|
-
return requestId;
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
function getRouteDiagnosticReason(item: Record<string, unknown>, route: NetworkRouteRecord): NetworkRouteDiagnostic["reason"] | undefined {
|
|
27
|
-
const statusMissing = typeof item.status !== "number";
|
|
28
|
-
const error = getStringRecordField(item, "error") ?? getStringRecordField(item, "failureText") ?? getStringRecordField(item, "errorText");
|
|
29
|
-
if (error && /(?:cors|cross-origin|preflight|access-control-allow-origin)/i.test(error)) return "cors-likely-routed-request";
|
|
30
|
-
if (statusMissing && isApiLikeNetworkRequest(item)) return "pending-routed-request";
|
|
31
|
-
if (route.mode !== "abort" && ((typeof item.status === "number" && item.status >= 400) || item.failed === true || typeof error === "string")) return "unfulfilled-routed-request";
|
|
32
|
-
return undefined;
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
export function getNetworkRouteMode(args: string[]): NetworkRouteRecord["mode"] {
|
|
36
|
-
if (args.includes("--abort")) return "abort";
|
|
37
|
-
if (args.includes("--body")) return "body";
|
|
38
|
-
return "handler";
|
|
39
|
-
}
|
|
40
|
-
|
|
41
|
-
export function applyNetworkRouteRecords(routes: NetworkRouteRecord[] | undefined, commandTokens: string[] | undefined, succeeded: boolean): NetworkRouteRecord[] | undefined {
|
|
42
|
-
if (!succeeded || commandTokens?.[0] !== "network") return routes;
|
|
43
|
-
const subcommand = commandTokens[1];
|
|
44
|
-
if (subcommand !== "route" && subcommand !== "unroute") return routes;
|
|
45
|
-
const existing = routes ?? [];
|
|
46
|
-
const pattern = commandTokens[2];
|
|
47
|
-
if (subcommand === "route" && pattern) return [...existing.filter((route) => route.pattern !== pattern), { mode: getNetworkRouteMode(commandTokens), pattern }];
|
|
48
|
-
if (!pattern) return undefined;
|
|
49
|
-
const next = existing.filter((route) => route.pattern !== pattern);
|
|
50
|
-
return next.length > 0 ? next : undefined;
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
export function buildNetworkRouteDiagnostics(data: unknown, routes: NetworkRouteRecord[] | undefined): NetworkRouteDiagnostic[] | undefined {
|
|
54
|
-
if (!routes || routes.length === 0 || !isRecord(data)) return undefined;
|
|
55
|
-
const requests = getArrayField(data, "requests");
|
|
56
|
-
if (!requests) return undefined;
|
|
57
|
-
const diagnostics: NetworkRouteDiagnostic[] = [];
|
|
58
|
-
for (const item of requests) {
|
|
59
|
-
if (!isRecord(item)) continue;
|
|
60
|
-
const url = getStringRecordField(item, "url");
|
|
61
|
-
if (!url) continue;
|
|
62
|
-
const route = routes.find((candidate) => networkRoutePatternMatchesUrl(candidate.pattern, url));
|
|
63
|
-
if (!route) continue;
|
|
64
|
-
const reason = getRouteDiagnosticReason(item, route);
|
|
65
|
-
if (!reason) continue;
|
|
66
|
-
const requestId = getSafeRequestId(item);
|
|
67
|
-
const requestUrl = redactSensitiveText(url);
|
|
68
|
-
const routePattern = redactSensitiveText(route.pattern);
|
|
69
|
-
diagnostics.push({
|
|
70
|
-
mode: route.mode,
|
|
71
|
-
reason,
|
|
72
|
-
...(requestId ? { requestId } : {}),
|
|
73
|
-
requestUrl,
|
|
74
|
-
routePattern,
|
|
75
|
-
summary: reason === "cors-likely-routed-request"
|
|
76
|
-
? `Routed request ${requestId ?? requestUrl} looks CORS/preflight-related for route ${routePattern}.`
|
|
77
|
-
: reason === "unfulfilled-routed-request"
|
|
78
|
-
? `Routed request ${requestId ?? requestUrl} failed instead of returning the configured route ${routePattern}.`
|
|
79
|
-
: `Routed request ${requestId ?? requestUrl} is still pending/no-status for route ${routePattern}.`,
|
|
80
|
-
});
|
|
81
|
-
}
|
|
82
|
-
return diagnostics.length > 0 ? diagnostics.slice(0, 5) : undefined;
|
|
83
|
-
}
|
|
@@ -1,78 +0,0 @@
|
|
|
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
|
-
export 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
|
-
export 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
|
-
export function isNetworkArtifactNoiseRequest(request: Record<string, unknown>): boolean {
|
|
32
|
-
const url = getStringRecordField(request, "url") ?? "";
|
|
33
|
-
const resourceType = (getStringRecordField(request, "resourceType") ?? getStringRecordField(request, "mimeType") ?? "").toLowerCase();
|
|
34
|
-
return /^data:image\//i.test(url) || (url.startsWith("data:") && resourceType.includes("image"));
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
function isBenignAssetFailure(request: Record<string, unknown>, url: string | undefined, resourceType: string | undefined): boolean {
|
|
38
|
-
const path = getNetworkRequestUrlPath(url);
|
|
39
|
-
if (!path) return false;
|
|
40
|
-
const normalizedResourceType = resourceType?.toLowerCase();
|
|
41
|
-
return /(?:^|\/)(?:favicon(?:[-.\w]*)?\.(?:ico|png|svg)|apple-touch-icon(?:[-.\w]*)?\.png)$/i.test(path)
|
|
42
|
-
&& (request.status === 404 || request.failed === true || typeof request.error === "string")
|
|
43
|
-
&& (!normalizedResourceType || ["image", "img", "other"].includes(normalizedResourceType) || normalizedResourceType.startsWith("image/"));
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
export function isApiLikeNetworkRequest(request: Record<string, unknown>): boolean {
|
|
47
|
-
const method = (getStringRecordField(request, "method") ?? "GET").toUpperCase();
|
|
48
|
-
const resourceType = (getStringRecordField(request, "resourceType") ?? "").toLowerCase();
|
|
49
|
-
const mimeType = (getStringRecordField(request, "mimeType") ?? "").toLowerCase();
|
|
50
|
-
const path = getNetworkRequestUrlPath(getStringRecordField(request, "url")) ?? "";
|
|
51
|
-
return resourceType === "fetch" || resourceType === "xhr" || mimeType.includes("json") || /\/(?:api|graphql|rpc)(?:\/|$)/i.test(path) || !["GET", "HEAD"].includes(method);
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
export function classifyNetworkRequestFailure(request: Record<string, unknown>): NetworkFailureClassification | undefined {
|
|
55
|
-
if (!isFailedNetworkRequest(request)) return undefined;
|
|
56
|
-
const url = getStringRecordField(request, "url");
|
|
57
|
-
const resourceType = getStringRecordField(request, "resourceType") ?? getStringRecordField(request, "mimeType");
|
|
58
|
-
const status = typeof request.status === "number" ? request.status : undefined;
|
|
59
|
-
if (isBenignAssetFailure(request, url, resourceType)) {
|
|
60
|
-
return { impact: "benign", reason: "low-impact browser icon asset", resourceType, status, url };
|
|
61
|
-
}
|
|
62
|
-
return { impact: "actionable", reason: "document, script, API, or non-benign request failure", resourceType, status, url };
|
|
63
|
-
}
|
|
64
|
-
|
|
65
|
-
export function summarizeNetworkFailures(requests: unknown[]): NetworkFailureSummary {
|
|
66
|
-
const failures = requests.flatMap((request) => {
|
|
67
|
-
if (!isRecord(request) || isNetworkArtifactNoiseRequest(request)) return [];
|
|
68
|
-
const classification = classifyNetworkRequestFailure(request);
|
|
69
|
-
return classification ? [classification] : [];
|
|
70
|
-
});
|
|
71
|
-
const benignCount = failures.filter((failure) => failure.impact === "benign").length;
|
|
72
|
-
return {
|
|
73
|
-
actionableCount: failures.length - benignCount,
|
|
74
|
-
benignCount,
|
|
75
|
-
failures,
|
|
76
|
-
totalCount: failures.length,
|
|
77
|
-
};
|
|
78
|
-
}
|