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