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
@@ -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
+ }