pi-agent-browser-native 0.2.48 → 0.2.50

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.
Files changed (189) hide show
  1. package/CHANGELOG.md +27 -1
  2. package/README.md +21 -11
  3. package/dist/extensions/agent-browser/index.js +808 -0
  4. package/dist/extensions/agent-browser/lib/argv-descriptor.js +71 -0
  5. package/dist/extensions/agent-browser/lib/argv-grammar.js +121 -0
  6. package/dist/extensions/agent-browser/lib/bash-guard.js +190 -0
  7. package/dist/extensions/agent-browser/lib/command-policy.js +85 -0
  8. package/dist/extensions/agent-browser/lib/command-taxonomy.js +302 -0
  9. package/dist/extensions/agent-browser/lib/config-policy.js +669 -0
  10. package/dist/extensions/agent-browser/lib/config.js +122 -0
  11. package/dist/extensions/agent-browser/lib/electron/cdp.js +51 -0
  12. package/dist/extensions/agent-browser/lib/electron/cleanup.js +212 -0
  13. package/dist/extensions/agent-browser/lib/electron/discovery.js +633 -0
  14. package/dist/extensions/agent-browser/lib/electron/launch.js +351 -0
  15. package/{extensions/agent-browser/lib/electron/text.ts → dist/extensions/agent-browser/lib/electron/text.js} +5 -5
  16. package/dist/extensions/agent-browser/lib/executable-path.js +20 -0
  17. package/dist/extensions/agent-browser/lib/fs-utils.js +18 -0
  18. package/dist/extensions/agent-browser/lib/input-modes/electron.js +165 -0
  19. package/dist/extensions/agent-browser/lib/input-modes/job.js +519 -0
  20. package/dist/extensions/agent-browser/lib/input-modes/lookups.js +440 -0
  21. package/dist/extensions/agent-browser/lib/input-modes/params.js +164 -0
  22. package/dist/extensions/agent-browser/lib/input-modes/semantic-action.js +119 -0
  23. package/dist/extensions/agent-browser/lib/input-modes/shared.js +42 -0
  24. package/dist/extensions/agent-browser/lib/input-modes/types.js +21 -0
  25. package/dist/extensions/agent-browser/lib/input-modes.js +10 -0
  26. package/dist/extensions/agent-browser/lib/json-schema.js +58 -0
  27. package/dist/extensions/agent-browser/lib/launch-scoped-flags.js +59 -0
  28. package/dist/extensions/agent-browser/lib/navigation-policy.js +83 -0
  29. package/dist/extensions/agent-browser/lib/orchestration/batch-stdin.js +62 -0
  30. package/dist/extensions/agent-browser/lib/orchestration/browser-run/artifact-paths.js +39 -0
  31. package/dist/extensions/agent-browser/lib/orchestration/browser-run/click-dispatch.js +276 -0
  32. package/dist/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.js +909 -0
  33. package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result.js +443 -0
  34. package/dist/extensions/agent-browser/lib/orchestration/browser-run/index.js +47 -0
  35. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/direct-anchor-download.js +141 -0
  36. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/network-page-filter.js +108 -0
  37. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/scroll-shims.js +112 -0
  38. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/snapshot-filter.js +158 -0
  39. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/wait-timeouts.js +54 -0
  40. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare.js +762 -0
  41. package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output.js +491 -0
  42. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prompt-guards.js +40 -0
  43. package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-artifacts.js +5 -0
  44. package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-state.js +731 -0
  45. package/dist/extensions/agent-browser/lib/orchestration/browser-run/types.js +1 -0
  46. package/dist/extensions/agent-browser/lib/orchestration/electron-host/index.js +718 -0
  47. package/dist/extensions/agent-browser/lib/orchestration/input-plan.js +247 -0
  48. package/dist/extensions/agent-browser/lib/orchestration/output-file.js +68 -0
  49. package/{extensions/agent-browser/lib/parsing.ts → dist/extensions/agent-browser/lib/parsing.js} +12 -11
  50. package/dist/extensions/agent-browser/lib/pi-tool-rendering.js +241 -0
  51. package/dist/extensions/agent-browser/lib/playbook.js +121 -0
  52. package/dist/extensions/agent-browser/lib/process.js +363 -0
  53. package/dist/extensions/agent-browser/lib/prompt-policy.js +91 -0
  54. package/dist/extensions/agent-browser/lib/results/action-recommendations.js +220 -0
  55. package/dist/extensions/agent-browser/lib/results/artifact-manifest.js +111 -0
  56. package/{extensions/agent-browser/lib/results/artifact-state.ts → dist/extensions/agent-browser/lib/results/artifact-state.js} +4 -8
  57. package/dist/extensions/agent-browser/lib/results/categories.js +76 -0
  58. package/dist/extensions/agent-browser/lib/results/confirmation.js +63 -0
  59. package/dist/extensions/agent-browser/lib/results/contracts.js +8 -0
  60. package/dist/extensions/agent-browser/lib/results/editable-ref-evidence.js +74 -0
  61. package/dist/extensions/agent-browser/lib/results/envelope.js +166 -0
  62. package/dist/extensions/agent-browser/lib/results/network-routes.js +92 -0
  63. package/dist/extensions/agent-browser/lib/results/network.js +73 -0
  64. package/dist/extensions/agent-browser/lib/results/next-actions.js +72 -0
  65. package/dist/extensions/agent-browser/lib/results/presentation/artifacts.js +515 -0
  66. package/dist/extensions/agent-browser/lib/results/presentation/batch.js +397 -0
  67. package/dist/extensions/agent-browser/lib/results/presentation/browser-profile-recovery.js +55 -0
  68. package/dist/extensions/agent-browser/lib/results/presentation/common.js +46 -0
  69. package/dist/extensions/agent-browser/lib/results/presentation/content.js +24 -0
  70. package/dist/extensions/agent-browser/lib/results/presentation/diagnostics.js +956 -0
  71. package/dist/extensions/agent-browser/lib/results/presentation/errors.js +205 -0
  72. package/dist/extensions/agent-browser/lib/results/presentation/large-output.js +134 -0
  73. package/dist/extensions/agent-browser/lib/results/presentation/navigation.js +159 -0
  74. package/dist/extensions/agent-browser/lib/results/presentation/registry.js +216 -0
  75. package/dist/extensions/agent-browser/lib/results/presentation/semantic-action.js +104 -0
  76. package/dist/extensions/agent-browser/lib/results/presentation/skills.js +152 -0
  77. package/dist/extensions/agent-browser/lib/results/presentation.js +177 -0
  78. package/dist/extensions/agent-browser/lib/results/recovery-actions.js +107 -0
  79. package/dist/extensions/agent-browser/lib/results/recovery-next-actions.js +50 -0
  80. package/dist/extensions/agent-browser/lib/results/selector-recovery.js +225 -0
  81. package/{extensions/agent-browser/lib/results/shared.ts → dist/extensions/agent-browser/lib/results/shared.js} +0 -1
  82. package/dist/extensions/agent-browser/lib/results/snapshot-high-value-controls.js +208 -0
  83. package/dist/extensions/agent-browser/lib/results/snapshot-refs.js +78 -0
  84. package/dist/extensions/agent-browser/lib/results/snapshot-segments.js +331 -0
  85. package/dist/extensions/agent-browser/lib/results/snapshot-spill.js +40 -0
  86. package/dist/extensions/agent-browser/lib/results/snapshot.js +264 -0
  87. package/dist/extensions/agent-browser/lib/results/text.js +40 -0
  88. package/{extensions/agent-browser/lib/results.ts → dist/extensions/agent-browser/lib/results.js} +2 -32
  89. package/dist/extensions/agent-browser/lib/runtime.js +855 -0
  90. package/dist/extensions/agent-browser/lib/session-page-state.js +411 -0
  91. package/dist/extensions/agent-browser/lib/string-enum-schema.js +13 -0
  92. package/dist/extensions/agent-browser/lib/temp.js +498 -0
  93. package/dist/extensions/agent-browser/lib/web-search.js +562 -0
  94. package/docs/ARCHITECTURE.md +5 -5
  95. package/docs/COMMAND_REFERENCE.md +4 -4
  96. package/docs/RELEASE.md +22 -11
  97. package/docs/REQUIREMENTS.md +1 -1
  98. package/docs/SUPPORT_MATRIX.md +5 -4
  99. package/docs/TOOL_CONTRACT.md +1 -1
  100. package/package.json +9 -5
  101. package/scripts/config.mjs +14 -20
  102. package/scripts/doctor.mjs +8 -7
  103. package/extensions/agent-browser/index.ts +0 -961
  104. package/extensions/agent-browser/lib/argv-descriptor.ts +0 -90
  105. package/extensions/agent-browser/lib/argv-grammar.ts +0 -128
  106. package/extensions/agent-browser/lib/bash-guard.ts +0 -205
  107. package/extensions/agent-browser/lib/command-policy.ts +0 -71
  108. package/extensions/agent-browser/lib/command-taxonomy.ts +0 -336
  109. package/extensions/agent-browser/lib/config-policy.js +0 -690
  110. package/extensions/agent-browser/lib/config.ts +0 -211
  111. package/extensions/agent-browser/lib/electron/cdp.ts +0 -69
  112. package/extensions/agent-browser/lib/electron/cleanup.ts +0 -235
  113. package/extensions/agent-browser/lib/electron/discovery.ts +0 -710
  114. package/extensions/agent-browser/lib/electron/launch.ts +0 -499
  115. package/extensions/agent-browser/lib/executable-path.ts +0 -19
  116. package/extensions/agent-browser/lib/fs-utils.ts +0 -18
  117. package/extensions/agent-browser/lib/input-modes/electron.ts +0 -170
  118. package/extensions/agent-browser/lib/input-modes/job.ts +0 -527
  119. package/extensions/agent-browser/lib/input-modes/lookups.ts +0 -447
  120. package/extensions/agent-browser/lib/input-modes/params.ts +0 -205
  121. package/extensions/agent-browser/lib/input-modes/semantic-action.ts +0 -127
  122. package/extensions/agent-browser/lib/input-modes/shared.ts +0 -46
  123. package/extensions/agent-browser/lib/input-modes/types.ts +0 -225
  124. package/extensions/agent-browser/lib/input-modes.ts +0 -45
  125. package/extensions/agent-browser/lib/json-schema.ts +0 -73
  126. package/extensions/agent-browser/lib/launch-scoped-flags.ts +0 -67
  127. package/extensions/agent-browser/lib/navigation-policy.ts +0 -95
  128. package/extensions/agent-browser/lib/orchestration/batch-stdin.ts +0 -65
  129. package/extensions/agent-browser/lib/orchestration/browser-run/artifact-paths.ts +0 -44
  130. package/extensions/agent-browser/lib/orchestration/browser-run/click-dispatch.ts +0 -280
  131. package/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.ts +0 -914
  132. package/extensions/agent-browser/lib/orchestration/browser-run/final-result.ts +0 -521
  133. package/extensions/agent-browser/lib/orchestration/browser-run/index.ts +0 -53
  134. package/extensions/agent-browser/lib/orchestration/browser-run/prepare/direct-anchor-download.ts +0 -158
  135. package/extensions/agent-browser/lib/orchestration/browser-run/prepare/network-page-filter.ts +0 -116
  136. package/extensions/agent-browser/lib/orchestration/browser-run/prepare/scroll-shims.ts +0 -147
  137. package/extensions/agent-browser/lib/orchestration/browser-run/prepare/snapshot-filter.ts +0 -183
  138. package/extensions/agent-browser/lib/orchestration/browser-run/prepare/wait-timeouts.ts +0 -58
  139. package/extensions/agent-browser/lib/orchestration/browser-run/prepare.ts +0 -847
  140. package/extensions/agent-browser/lib/orchestration/browser-run/process-output.ts +0 -559
  141. package/extensions/agent-browser/lib/orchestration/browser-run/prompt-guards.ts +0 -47
  142. package/extensions/agent-browser/lib/orchestration/browser-run/session-artifacts.ts +0 -8
  143. package/extensions/agent-browser/lib/orchestration/browser-run/session-state.ts +0 -868
  144. package/extensions/agent-browser/lib/orchestration/browser-run/types.ts +0 -565
  145. package/extensions/agent-browser/lib/orchestration/electron-host/index.ts +0 -855
  146. package/extensions/agent-browser/lib/orchestration/input-plan.ts +0 -375
  147. package/extensions/agent-browser/lib/orchestration/output-file.ts +0 -86
  148. package/extensions/agent-browser/lib/pi-tool-rendering.ts +0 -267
  149. package/extensions/agent-browser/lib/playbook.ts +0 -142
  150. package/extensions/agent-browser/lib/process.ts +0 -516
  151. package/extensions/agent-browser/lib/prompt-policy.ts +0 -105
  152. package/extensions/agent-browser/lib/results/action-recommendations.ts +0 -264
  153. package/extensions/agent-browser/lib/results/artifact-manifest.ts +0 -111
  154. package/extensions/agent-browser/lib/results/categories.ts +0 -106
  155. package/extensions/agent-browser/lib/results/confirmation.ts +0 -76
  156. package/extensions/agent-browser/lib/results/contracts.ts +0 -241
  157. package/extensions/agent-browser/lib/results/editable-ref-evidence.ts +0 -72
  158. package/extensions/agent-browser/lib/results/envelope.ts +0 -195
  159. package/extensions/agent-browser/lib/results/network-routes.ts +0 -83
  160. package/extensions/agent-browser/lib/results/network.ts +0 -78
  161. package/extensions/agent-browser/lib/results/next-actions.ts +0 -117
  162. package/extensions/agent-browser/lib/results/presentation/artifacts.ts +0 -588
  163. package/extensions/agent-browser/lib/results/presentation/batch.ts +0 -450
  164. package/extensions/agent-browser/lib/results/presentation/browser-profile-recovery.ts +0 -67
  165. package/extensions/agent-browser/lib/results/presentation/common.ts +0 -53
  166. package/extensions/agent-browser/lib/results/presentation/content.ts +0 -36
  167. package/extensions/agent-browser/lib/results/presentation/diagnostics.ts +0 -923
  168. package/extensions/agent-browser/lib/results/presentation/errors.ts +0 -227
  169. package/extensions/agent-browser/lib/results/presentation/large-output.ts +0 -182
  170. package/extensions/agent-browser/lib/results/presentation/navigation.ts +0 -184
  171. package/extensions/agent-browser/lib/results/presentation/registry.ts +0 -242
  172. package/extensions/agent-browser/lib/results/presentation/semantic-action.ts +0 -131
  173. package/extensions/agent-browser/lib/results/presentation/skills.ts +0 -143
  174. package/extensions/agent-browser/lib/results/presentation.ts +0 -257
  175. package/extensions/agent-browser/lib/results/recovery-actions.ts +0 -139
  176. package/extensions/agent-browser/lib/results/recovery-next-actions.ts +0 -71
  177. package/extensions/agent-browser/lib/results/selector-recovery.ts +0 -320
  178. package/extensions/agent-browser/lib/results/snapshot-high-value-controls.ts +0 -273
  179. package/extensions/agent-browser/lib/results/snapshot-refs.ts +0 -100
  180. package/extensions/agent-browser/lib/results/snapshot-segments.ts +0 -366
  181. package/extensions/agent-browser/lib/results/snapshot-spill.ts +0 -63
  182. package/extensions/agent-browser/lib/results/snapshot.ts +0 -329
  183. package/extensions/agent-browser/lib/results/text.ts +0 -40
  184. package/extensions/agent-browser/lib/runtime.ts +0 -988
  185. package/extensions/agent-browser/lib/session-page-state.ts +0 -512
  186. package/extensions/agent-browser/lib/string-enum-schema.ts +0 -20
  187. package/extensions/agent-browser/lib/temp.ts +0 -577
  188. package/extensions/agent-browser/lib/web-search.ts +0 -728
  189. /package/{extensions/agent-browser/lib/orchestration/browser-run.ts → dist/extensions/agent-browser/lib/orchestration/browser-run.js} +0 -0
@@ -1,366 +0,0 @@
1
- /**
2
- * Purpose: Parse raw agent-browser snapshot text into previewable sections.
3
- * Responsibilities: Identify signal lines, rank content segments, build structured previews, and provide raw-outline fallback previews.
4
- * Scope: Snapshot text/section planning only; ref enrichment, high-value control ranking, spill files, and final presentation live elsewhere.
5
- * Usage: Snapshot presentation uses this module before selecting displayed refs.
6
- * Invariants/Assumptions: Parsing is best-effort and must stay resilient to upstream snapshot text format changes.
7
- */
8
-
9
- import type { SnapshotLineRefInfo, SnapshotRefEntry } from "./snapshot-refs.js";
10
- import { normalizeWhitespace, truncateText } from "./text.js";
11
-
12
- const SNAPSHOT_MAX_ADDITIONAL_SECTIONS = 2;
13
- const SNAPSHOT_FALLBACK_PREVIEW_MAX_LINES = 12;
14
- const SNAPSHOT_LINE_MAX_CHARS = 140;
15
-
16
- const SNAPSHOT_SIGNAL_ROLES = new Set([
17
- "article",
18
- "banner",
19
- "button",
20
- "checkbox",
21
- "combobox",
22
- "dialog",
23
- "gridcell",
24
- "heading",
25
- "link",
26
- "listitem",
27
- "main",
28
- "menu",
29
- "menuitem",
30
- "navigation",
31
- "option",
32
- "radio",
33
- "region",
34
- "row",
35
- "searchbox",
36
- "tab",
37
- "textbox",
38
- ]);
39
-
40
- const SNAPSHOT_SEGMENT_ROOT_ROLES = new Set(["article", "dialog", "heading", "main", "menu", "region"]);
41
-
42
- const SNAPSHOT_ROLE_PRIORITY: Record<string, number> = {
43
- article: 0,
44
- main: 1,
45
- dialog: 2,
46
- menu: 3,
47
- region: 4,
48
- heading: 5,
49
- searchbox: 6,
50
- textbox: 7,
51
- combobox: 8,
52
- button: 9,
53
- checkbox: 10,
54
- radio: 11,
55
- tab: 12,
56
- option: 13,
57
- link: 14,
58
- listitem: 14,
59
- row: 15,
60
- gridcell: 16,
61
- navigation: 17,
62
- generic: 99,
63
- unknown: 100,
64
- };
65
-
66
- const SNAPSHOT_NOISE_NAME_PATTERNS = [
67
- /^skip to /i,
68
- /^ad$/i,
69
- /^don't want to see ads\??$/i,
70
- /keyboard shortcuts/i,
71
- /\bpromoted\b/i,
72
- /\bsponsored\b/i,
73
- ];
74
-
75
- const SNAPSHOT_CHROME_SECTION_PATTERNS = [
76
- /^primary$/i,
77
- /^footer$/i,
78
- /^navigation$/i,
79
- /\bwhat['’]?s happening\b/i,
80
- /\brelevant people\b/i,
81
- /\btrending\b/i,
82
- /\brelated\b/i,
83
- /\brecommended\b/i,
84
- /\bsuggested\b/i,
85
- ];
86
-
87
- export interface SnapshotLine extends SnapshotLineRefInfo {
88
- depth: number;
89
- headingLevel?: number;
90
- index: number;
91
- name: string;
92
- raw: string;
93
- ref?: string;
94
- role: string;
95
- }
96
-
97
- export interface SnapshotSegment {
98
- endIndexExclusive: number;
99
- lines: SnapshotLine[];
100
- root: SnapshotLine;
101
- score: number;
102
- startIndex: number;
103
- }
104
-
105
- export interface SnapshotPreview {
106
- omittedCount: number;
107
- refIds: string[];
108
- lines: string[];
109
- }
110
-
111
- export function getSnapshotRolePriority(role: string): number {
112
- return SNAPSHOT_ROLE_PRIORITY[role] ?? 50;
113
- }
114
-
115
- export function parseSnapshotLines(snapshot: string): SnapshotLine[] {
116
- return snapshot
117
- .split("\n")
118
- .filter((line) => line.length > 0)
119
- .map((raw, index) => {
120
- const trimmed = raw.trimStart();
121
- const depth = Math.floor(((raw.match(/^\s*/) ?? [""])[0].length ?? 0) / 2);
122
- const role = trimmed.match(/^[-*]\s+([^\s"]+)/)?.[1] ?? "unknown";
123
- const name = normalizeWhitespace(trimmed.match(/"([^"]*)"/)?.[1] ?? "");
124
- const ref = trimmed.match(/\bref=([^,\]\s]+)/)?.[1];
125
- const headingLevel = trimmed.match(/\blevel=(\d+)/)?.[1];
126
- return {
127
- depth,
128
- headingLevel: headingLevel ? Number(headingLevel) : undefined,
129
- index,
130
- name,
131
- raw,
132
- ref,
133
- role,
134
- } satisfies SnapshotLine;
135
- });
136
- }
137
-
138
- export function isNoiseName(name: string): boolean {
139
- return SNAPSHOT_NOISE_NAME_PATTERNS.some((pattern) => pattern.test(name));
140
- }
141
-
142
- export function isChromeSectionName(name: string): boolean {
143
- return SNAPSHOT_CHROME_SECTION_PATTERNS.some((pattern) => pattern.test(name));
144
- }
145
-
146
- function isNoiseSnapshotLine(line: SnapshotLine): boolean {
147
- if (line.name.length > 0 && isNoiseName(line.name)) return true;
148
- const loweredRaw = line.raw.toLowerCase();
149
- return loweredRaw.includes("promoted") || loweredRaw.includes("sponsored");
150
- }
151
-
152
- function isPotentialSegmentRootLine(line: SnapshotLine): boolean {
153
- if (!SNAPSHOT_SEGMENT_ROOT_ROLES.has(line.role)) return false;
154
- if (isNoiseSnapshotLine(line)) return false;
155
- if (line.role === "heading") {
156
- return line.name.length > 0 && (line.headingLevel ?? 99) <= 3;
157
- }
158
- if (line.role === "region") {
159
- return line.name.length > 0;
160
- }
161
- return true;
162
- }
163
-
164
- function scoreSegment(segment: SnapshotSegment): number {
165
- const { root } = segment;
166
- const distinctRefs = new Set(segment.lines.flatMap((line) => (line.ref ? [line.ref] : []))).size;
167
- let score = 0;
168
-
169
- score += 120 - getSnapshotRolePriority(root.role) * 8;
170
- score += Math.min(distinctRefs, 16);
171
- score += Math.min(segment.lines.length, 12);
172
- score -= Math.min(root.index, 60) / 3;
173
- score -= root.depth * 6;
174
-
175
- if (root.role === "heading") {
176
- if (root.headingLevel === 1) score += 40;
177
- else if (root.headingLevel === 2) score += 22;
178
- else if (root.headingLevel === 3) score += 12;
179
- }
180
- if (root.name.length > 0) score += 10;
181
- if (root.name.length <= 2) score -= 18;
182
- if (isChromeSectionName(root.name)) score -= 45;
183
- if (isNoiseName(root.name)) score -= 1000;
184
- return score;
185
- }
186
-
187
- export function buildSnapshotSegments(snapshotLines: SnapshotLine[]): SnapshotSegment[] {
188
- const roots: SnapshotLine[] = [];
189
- const stack: SnapshotLine[] = [];
190
-
191
- for (const line of snapshotLines) {
192
- stack.length = line.depth;
193
- if (isPotentialSegmentRootLine(line)) {
194
- const normalizedName = normalizeWhitespace(line.name.toLowerCase());
195
- let duplicateAncestor: SnapshotLine | undefined;
196
- for (let index = stack.length - 1; index >= 0; index -= 1) {
197
- const ancestor = stack[index];
198
- if (
199
- normalizedName.length > 0 &&
200
- normalizeWhitespace(ancestor.name.toLowerCase()) === normalizedName &&
201
- SNAPSHOT_SEGMENT_ROOT_ROLES.has(ancestor.role)
202
- ) {
203
- duplicateAncestor = ancestor;
204
- break;
205
- }
206
- }
207
- if (!duplicateAncestor) {
208
- roots.push(line);
209
- }
210
- }
211
- stack[line.depth] = line;
212
- }
213
-
214
- return roots.map((root, index) => {
215
- let endIndexExclusive = snapshotLines.length;
216
- for (let nextIndex = index + 1; nextIndex < roots.length; nextIndex += 1) {
217
- const candidate = roots[nextIndex];
218
- if (candidate.depth <= root.depth) {
219
- endIndexExclusive = candidate.index;
220
- break;
221
- }
222
- }
223
- const lines = snapshotLines.slice(root.index, endIndexExclusive);
224
- const segment: SnapshotSegment = {
225
- endIndexExclusive,
226
- lines,
227
- root,
228
- score: 0,
229
- startIndex: root.index,
230
- };
231
- segment.score = scoreSegment(segment);
232
- return segment;
233
- });
234
- }
235
-
236
- export function choosePrimarySegment(segments: SnapshotSegment[]): SnapshotSegment | undefined {
237
- if (segments.length === 0) return undefined;
238
- return (
239
- segments.find((segment) => segment.root.role === "main" || segment.root.role === "article") ??
240
- segments.find((segment) => segment.root.role === "heading" && segment.root.headingLevel === 1) ??
241
- segments.find((segment) => segment.score >= 90) ??
242
- [...segments].sort((left, right) => right.score - left.score || left.startIndex - right.startIndex)[0]
243
- );
244
- }
245
-
246
- export function chooseAdditionalSegments(segments: SnapshotSegment[], primary: SnapshotSegment | undefined): SnapshotSegment[] {
247
- if (!primary) return [];
248
-
249
- const seenNames = new Set<string>([normalizeWhitespace(primary.root.name.toLowerCase())]);
250
- const rankedCandidates = segments
251
- .filter((segment) => segment !== primary && segment.score >= 45)
252
- .sort((left, right) => {
253
- const leftDistance = Math.abs(left.startIndex - primary.startIndex);
254
- const rightDistance = Math.abs(right.startIndex - primary.startIndex);
255
- if (leftDistance !== rightDistance) return leftDistance - rightDistance;
256
- if (right.score !== left.score) return right.score - left.score;
257
- return left.startIndex - right.startIndex;
258
- });
259
-
260
- const chosen: SnapshotSegment[] = [];
261
- for (const segment of rankedCandidates) {
262
- if (chosen.length >= SNAPSHOT_MAX_ADDITIONAL_SECTIONS) break;
263
- if (isChromeSectionName(segment.root.name)) continue;
264
- if (segment.root.role === "heading" && segment.root.name.length <= 2) continue;
265
- const nameKey = normalizeWhitespace(segment.root.name.toLowerCase());
266
- if (nameKey && seenNames.has(nameKey)) continue;
267
- chosen.push(segment);
268
- if (nameKey) seenNames.add(nameKey);
269
- }
270
-
271
- return chosen.sort((left, right) => left.startIndex - right.startIndex);
272
- }
273
-
274
- export function getMeaningfulSegmentLines(segment: SnapshotSegment): SnapshotLine[] {
275
- return segment.lines.filter((line) => {
276
- if (isNoiseSnapshotLine(line)) return false;
277
- if (line.role === "generic" && !line.ref && line.name.length === 0) return false;
278
- if (line.role === "link" && line.name.length === 0) return false;
279
- return true;
280
- });
281
- }
282
-
283
- function formatPreviewLine(line: SnapshotLine, baseDepth: number): string {
284
- const leadingWhitespace = (line.raw.match(/^\s*/) ?? [""])[0].length;
285
- const stripChars = Math.min(leadingWhitespace, Math.max(0, baseDepth) * 2);
286
- return truncateText(line.raw.slice(stripChars), SNAPSHOT_LINE_MAX_CHARS);
287
- }
288
-
289
- export function buildSegmentPreview(segment: SnapshotSegment, maxLines: number): SnapshotPreview {
290
- const meaningfulLines = getMeaningfulSegmentLines(segment);
291
- if (meaningfulLines.length === 0) {
292
- return { omittedCount: 0, refIds: [], lines: [] };
293
- }
294
-
295
- const previewLines: SnapshotLine[] = [];
296
- const previewRefIds = new Set<string>();
297
- const seenPreviewKeys = new Set<string>();
298
- const rootDepth = segment.root.depth;
299
-
300
- for (const line of meaningfulLines) {
301
- if (previewLines.length >= maxLines) break;
302
- if (line !== segment.root) {
303
- const relativeDepth = line.depth - rootDepth;
304
- if (segment.root.role !== "heading" && relativeDepth > 2) continue;
305
- if (segment.root.name.length > 0 && line.name === segment.root.name && (line.role === "heading" || line.role === "link")) {
306
- continue;
307
- }
308
- }
309
-
310
- const key = `${line.role}:${line.name}:${line.ref ?? ""}:${line.depth}`;
311
- if (seenPreviewKeys.has(key)) continue;
312
- seenPreviewKeys.add(key);
313
- previewLines.push(line);
314
- if (line.ref) previewRefIds.add(line.ref);
315
- }
316
-
317
- return {
318
- omittedCount: Math.max(0, meaningfulLines.length - previewLines.length),
319
- refIds: [...previewRefIds],
320
- lines: previewLines.map((line) => formatPreviewLine(line, rootDepth)),
321
- };
322
- }
323
-
324
- export function buildFallbackSnapshotOutline(snapshotLines: SnapshotLine[]): SnapshotPreview {
325
- const selected = new Set<number>();
326
- for (let index = 0; index < snapshotLines.length && selected.size < 4; index += 1) {
327
- if (!isNoiseSnapshotLine(snapshotLines[index])) selected.add(index);
328
- }
329
- for (let index = 0; index < snapshotLines.length && selected.size < SNAPSHOT_FALLBACK_PREVIEW_MAX_LINES; index += 1) {
330
- const line = snapshotLines[index];
331
- if (isNoiseSnapshotLine(line)) continue;
332
- if (SNAPSHOT_SIGNAL_ROLES.has(line.role) || line.ref || line.name.length > 0) {
333
- selected.add(index);
334
- }
335
- }
336
- const chosenLines = [...selected]
337
- .sort((left, right) => left - right)
338
- .slice(0, SNAPSHOT_FALLBACK_PREVIEW_MAX_LINES)
339
- .map((index) => snapshotLines[index]);
340
- return {
341
- omittedCount: Math.max(0, snapshotLines.length - chosenLines.length),
342
- refIds: chosenLines.flatMap((line) => (line.ref ? [line.ref] : [])),
343
- lines: chosenLines.map((line) => truncateText(line.raw, SNAPSHOT_LINE_MAX_CHARS)),
344
- };
345
- }
346
-
347
- export function buildRefLineOrderMap(snapshotLines: SnapshotLine[]): Map<string, number> {
348
- const map = new Map<string, number>();
349
- for (const line of snapshotLines) {
350
- if (!line.ref || map.has(line.ref)) continue;
351
- map.set(line.ref, line.index);
352
- }
353
- return map;
354
- }
355
-
356
- export function canUseStructuredSnapshotPreview(snapshotLines: SnapshotLine[], refEntries: SnapshotRefEntry[]): boolean {
357
- if (snapshotLines.length === 0) return false;
358
- const linesWithRecognizedRoles = snapshotLines.filter((line) => line.role !== "unknown").length;
359
- const linesWithNames = snapshotLines.filter((line) => line.name.length > 0).length;
360
- const parsedRefIds = new Set(snapshotLines.flatMap((line) => (line.ref ? [line.ref] : [])));
361
- return (
362
- linesWithRecognizedRoles >= Math.min(snapshotLines.length, 3) ||
363
- linesWithNames >= Math.min(snapshotLines.length, 3) ||
364
- parsedRefIds.size >= Math.min(refEntries.length, 3)
365
- );
366
- }
@@ -1,63 +0,0 @@
1
- /**
2
- * Purpose: Persist full compacted snapshot payloads when model-facing output is shortened.
3
- * Responsibilities: Write persistent or secure-temp snapshot spill files and merge spill retention metadata into the session artifact manifest.
4
- * Scope: Snapshot spill artifact lifecycle only; preview planning and presentation text live in snapshot.ts and sibling modules.
5
- * Usage: Snapshot presentation calls these helpers after deciding a snapshot is too large for inline output.
6
- * Invariants/Assumptions: Explicit full-output paths are reported but not deleted here; retention state mirrors the backing storage scope.
7
- */
8
-
9
- import type { PersistentSessionArtifactEviction, PersistentSessionArtifactStore } from "../temp.js";
10
- import { writePersistentSessionArtifactFile, writeSecureTempFile } from "../temp.js";
11
- import {
12
- buildEvictedSessionArtifactEntries,
13
- formatSessionArtifactRetentionSummary,
14
- mergeSessionArtifactManifest,
15
- } from "./artifact-manifest.js";
16
- import type { SessionArtifactManifest, SessionArtifactManifestEntry } from "./contracts.js";
17
-
18
- const SNAPSHOT_SPILL_FILE_PREFIX = "pi-agent-browser-snapshot";
19
-
20
- export interface SnapshotSpillWriteResult {
21
- evictedArtifacts: PersistentSessionArtifactEviction[];
22
- path: string;
23
- storageScope: "persistent-session" | "process-temp";
24
- }
25
-
26
- export async function writeSnapshotSpillFile(
27
- data: Record<string, unknown>,
28
- persistentArtifactStore: PersistentSessionArtifactStore | undefined,
29
- ): Promise<SnapshotSpillWriteResult> {
30
- const options = {
31
- content: JSON.stringify(data, null, 2),
32
- prefix: SNAPSHOT_SPILL_FILE_PREFIX,
33
- suffix: ".json",
34
- };
35
- if (persistentArtifactStore) {
36
- const result = await writePersistentSessionArtifactFile({ ...options, store: persistentArtifactStore });
37
- return { ...result, storageScope: "persistent-session" };
38
- }
39
- return { evictedArtifacts: [], path: await writeSecureTempFile(options), storageScope: "process-temp" };
40
- }
41
-
42
- export function applySnapshotArtifactManifest(options: {
43
- baseManifest?: SessionArtifactManifest;
44
- command?: string;
45
- fullOutputPath?: string;
46
- spill?: SnapshotSpillWriteResult;
47
- }): { artifactManifest?: SessionArtifactManifest; artifactRetentionSummary?: string } {
48
- if (!options.fullOutputPath || !options.spill) return {};
49
- const nowMs = Date.now();
50
- const entries: SessionArtifactManifestEntry[] = [
51
- {
52
- command: options.command,
53
- createdAtMs: nowMs,
54
- kind: "spill",
55
- path: options.fullOutputPath,
56
- retentionState: options.spill.storageScope === "persistent-session" ? "live" : "ephemeral",
57
- storageScope: options.spill.storageScope,
58
- },
59
- ...buildEvictedSessionArtifactEntries(options.spill.evictedArtifacts, nowMs),
60
- ];
61
- const artifactManifest = mergeSessionArtifactManifest({ base: options.baseManifest, entries, nowMs });
62
- return artifactManifest ? { artifactManifest, artifactRetentionSummary: formatSessionArtifactRetentionSummary(artifactManifest) } : {};
63
- }