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,411 @@
1
+ /**
2
+ * Purpose: Own wrapper-known per-session browser page target, ref snapshot, invalidation, and pinning state.
3
+ * Responsibilities: Restore state from persisted tool details, apply ordered tab/ref updates atomically, and expose order-free public state views to the extension entrypoint.
4
+ * Scope: Session page state only; browser process execution, tab probing, and presentation policies stay in the extension entrypoint.
5
+ * Usage: `index.ts` creates one store per Pi session lifecycle and records observations through update tokens.
6
+ * Invariants/Assumptions: One tool-call update token must govern all page-state observations from that invocation; stale overlapping updates must not overwrite newer state.
7
+ */
8
+ import { isCloseCommand, isReadOnlyDiagnosticSessionTargetCommand } from "./command-taxonomy.js";
9
+ import { isRecord } from "./parsing.js";
10
+ import { getEditableRefEvidence } from "./results/editable-ref-evidence.js";
11
+ import { enrichSnapshotRefEntries, getSnapshotRefEntries } from "./results/snapshot-refs.js";
12
+ import { parseSnapshotLines } from "./results/snapshot-segments.js";
13
+ export function normalizeComparableUrl(url) {
14
+ const normalizedUrl = url?.trim();
15
+ if (!normalizedUrl) {
16
+ return undefined;
17
+ }
18
+ try {
19
+ const parsedUrl = new URL(normalizedUrl);
20
+ parsedUrl.hash = "";
21
+ return parsedUrl.toString();
22
+ }
23
+ catch {
24
+ return undefined;
25
+ }
26
+ }
27
+ export function normalizeSessionTabTarget(target) {
28
+ if (!target) {
29
+ return undefined;
30
+ }
31
+ const url = normalizeComparableUrl(target.url);
32
+ if (!url) {
33
+ return undefined;
34
+ }
35
+ const title = target.title?.trim();
36
+ return { title: title && title.length > 0 ? title : undefined, url };
37
+ }
38
+ export function isAboutBlankUrl(url) {
39
+ return normalizeComparableUrl(url) === "about:blank";
40
+ }
41
+ export function isAboutBlankSessionTabTarget(target) {
42
+ return isAboutBlankUrl(target?.url);
43
+ }
44
+ export function commandExplicitlyTargetsAboutBlank(commandTokens) {
45
+ return commandTokens.some((token) => isAboutBlankUrl(token));
46
+ }
47
+ export function targetsMatch(left, right) {
48
+ if (!left || !right)
49
+ return true;
50
+ return normalizeComparableUrl(left.url) === normalizeComparableUrl(right.url);
51
+ }
52
+ function extractStringResultField(data, fieldName) {
53
+ if (typeof data === "string") {
54
+ if (fieldName === "value")
55
+ return data;
56
+ const text = data.trim();
57
+ return text.length > 0 ? text : undefined;
58
+ }
59
+ if (!isRecord(data) || typeof data[fieldName] !== "string") {
60
+ return undefined;
61
+ }
62
+ if (fieldName === "value")
63
+ return data[fieldName];
64
+ const text = data[fieldName].trim();
65
+ return text.length > 0 ? text : undefined;
66
+ }
67
+ export function extractSessionTabTargetFromData(data) {
68
+ const directTarget = normalizeSessionTabTarget({
69
+ title: extractStringResultField(data, "title"),
70
+ url: extractStringResultField(data, "url"),
71
+ });
72
+ if (directTarget) {
73
+ return directTarget;
74
+ }
75
+ if (isRecord(data) && typeof data.origin === "string") {
76
+ return normalizeSessionTabTarget({ url: data.origin });
77
+ }
78
+ return undefined;
79
+ }
80
+ function extractBatchResultCommand(item) {
81
+ return Array.isArray(item.command) ? item.command.filter((token) => typeof token === "string") : [];
82
+ }
83
+ export function extractSessionTabTargetFromCommandData(commandTokens, data) {
84
+ const [command, subcommand] = commandTokens;
85
+ return isReadOnlyDiagnosticSessionTargetCommand(command, subcommand) ? undefined : extractSessionTabTargetFromData(data);
86
+ }
87
+ export function extractSessionTabTargetFromBatchResults(data) {
88
+ if (!Array.isArray(data)) {
89
+ return undefined;
90
+ }
91
+ let currentTarget;
92
+ let pendingTitle;
93
+ for (const item of data) {
94
+ if (!isRecord(item) || item.success === false) {
95
+ continue;
96
+ }
97
+ const [name, subcommand] = extractBatchResultCommand(item);
98
+ const result = item.result;
99
+ if (name === "get" && subcommand === "title") {
100
+ pendingTitle = extractStringResultField(result, "title");
101
+ continue;
102
+ }
103
+ if (name === "get" && subcommand === "url") {
104
+ const url = extractStringResultField(result, "url");
105
+ const target = normalizeSessionTabTarget({ title: pendingTitle, url });
106
+ if (target) {
107
+ currentTarget = target;
108
+ }
109
+ pendingTitle = undefined;
110
+ continue;
111
+ }
112
+ const resultTarget = extractSessionTabTargetFromCommandData([name, subcommand].filter((token) => token !== undefined), result);
113
+ if (resultTarget) {
114
+ currentTarget = resultTarget;
115
+ }
116
+ pendingTitle = undefined;
117
+ }
118
+ return currentTarget;
119
+ }
120
+ export function deriveSessionTabTarget(options) {
121
+ if (isCloseCommand(options.command)) {
122
+ return undefined;
123
+ }
124
+ const commandDataTarget = isReadOnlyDiagnosticSessionTargetCommand(options.command, options.subcommand)
125
+ ? undefined
126
+ : extractSessionTabTargetFromData(options.data);
127
+ return (normalizeSessionTabTarget(options.navigationSummary) ??
128
+ extractSessionTabTargetFromBatchResults(options.data) ??
129
+ commandDataTarget ??
130
+ options.previousTarget);
131
+ }
132
+ function batchContainsOnlyReadOnlyDiagnosticTargets(data) {
133
+ if (!Array.isArray(data) || data.length === 0) {
134
+ return false;
135
+ }
136
+ return data.every((item) => {
137
+ if (!isRecord(item))
138
+ return false;
139
+ const [command, subcommand] = extractBatchResultCommand(item);
140
+ return isReadOnlyDiagnosticSessionTargetCommand(command, subcommand);
141
+ });
142
+ }
143
+ function getRestoredSessionTabTarget(details, command, subcommand) {
144
+ if (isReadOnlyDiagnosticSessionTargetCommand(command, subcommand)) {
145
+ return undefined;
146
+ }
147
+ const storedTarget = isRecord(details.sessionTabTarget)
148
+ ? normalizeSessionTabTarget({
149
+ title: typeof details.sessionTabTarget.title === "string" ? details.sessionTabTarget.title : undefined,
150
+ url: typeof details.sessionTabTarget.url === "string" ? details.sessionTabTarget.url : undefined,
151
+ })
152
+ : undefined;
153
+ if (command !== "batch") {
154
+ return storedTarget;
155
+ }
156
+ const batchTarget = extractSessionTabTargetFromBatchResults(details.data);
157
+ if (batchTarget) {
158
+ return batchTarget;
159
+ }
160
+ if (isRecord(details.compiledNetworkSourceLookup) || batchContainsOnlyReadOnlyDiagnosticTargets(details.data)) {
161
+ return undefined;
162
+ }
163
+ return storedTarget;
164
+ }
165
+ function extractRefSnapshotRefs(data) {
166
+ if (!isRecord(data) || !isRecord(data.refs))
167
+ return undefined;
168
+ const snapshotLines = typeof data.snapshot === "string" ? parseSnapshotLines(data.snapshot) : [];
169
+ const lineByRef = new Map(snapshotLines.flatMap((line) => line.ref ? [[line.ref, line.raw]] : []));
170
+ const entries = enrichSnapshotRefEntries(getSnapshotRefEntries(data), snapshotLines);
171
+ const refs = Object.fromEntries(entries.flatMap((entry) => {
172
+ if (!/^e\d+$/.test(entry.id) || entry.role.length === 0)
173
+ return [];
174
+ const isContentEditable = getEditableRefEvidence({ ref: entry.refData, text: lineByRef.get(entry.id) });
175
+ return [[entry.id, { ...(isContentEditable === true ? { isContentEditable: true } : {}), ...(entry.isEditable !== undefined ? { isEditable: entry.isEditable } : {}), name: entry.name, role: entry.role }]];
176
+ }));
177
+ return Object.keys(refs).length > 0 ? refs : undefined;
178
+ }
179
+ export function extractRefSnapshotFromData(data) {
180
+ if (!isRecord(data))
181
+ return undefined;
182
+ const refs = extractRefSnapshotRefs(data);
183
+ return {
184
+ refIds: isRecord(data.refs) ? Object.keys(data.refs).filter((refId) => /^e\d+$/.test(refId)) : [],
185
+ ...(refs ? { refs } : {}),
186
+ target: extractSessionTabTargetFromData(data),
187
+ };
188
+ }
189
+ function getBatchResultFailureText(item) {
190
+ const result = isRecord(item.result) ? item.result : undefined;
191
+ const parts = [item.error, result?.error, typeof item.result === "string" ? item.result : undefined]
192
+ .filter((part) => typeof part === "string" && part.trim().length > 0);
193
+ return parts.length > 0 ? parts.join("\n") : undefined;
194
+ }
195
+ export function buildNoActivePageRefSnapshotInvalidation() {
196
+ return {
197
+ reason: "no-active-page",
198
+ summary: "The latest snapshot for this session reported No active page. Old page-scoped refs are invalid until snapshot -i succeeds.",
199
+ };
200
+ }
201
+ export function isNoActivePageSnapshotFailure(command, text) {
202
+ return command === "snapshot" && /\bno active page\b/i.test(text ?? "");
203
+ }
204
+ export function extractLatestRefSnapshotStateFromBatchResults(data) {
205
+ if (!Array.isArray(data))
206
+ return undefined;
207
+ let latestState;
208
+ for (const item of data) {
209
+ if (!isRecord(item))
210
+ continue;
211
+ const [name] = extractBatchResultCommand(item);
212
+ if (name !== "snapshot")
213
+ continue;
214
+ if (item.success === false) {
215
+ if (isNoActivePageSnapshotFailure(name, getBatchResultFailureText(item))) {
216
+ latestState = { invalidation: buildNoActivePageRefSnapshotInvalidation() };
217
+ }
218
+ continue;
219
+ }
220
+ const snapshot = extractRefSnapshotFromData(item.result);
221
+ if (snapshot) {
222
+ latestState = { snapshot };
223
+ }
224
+ }
225
+ return latestState;
226
+ }
227
+ function getRestoredRefSnapshotInvalidation(details, command) {
228
+ const invalidation = isRecord(details.refSnapshotInvalidation) ? details.refSnapshotInvalidation : undefined;
229
+ if (invalidation && invalidation.reason === "no-active-page") {
230
+ return buildNoActivePageRefSnapshotInvalidation();
231
+ }
232
+ const errorText = typeof details.error === "string"
233
+ ? details.error
234
+ : typeof details.summary === "string"
235
+ ? details.summary
236
+ : undefined;
237
+ return isNoActivePageSnapshotFailure(command, errorText) ? buildNoActivePageRefSnapshotInvalidation() : undefined;
238
+ }
239
+ function getRestoredRefSnapshot(details) {
240
+ const refSnapshot = isRecord(details.refSnapshot) ? details.refSnapshot : undefined;
241
+ if (!refSnapshot || !Array.isArray(refSnapshot.refIds))
242
+ return undefined;
243
+ const refIds = refSnapshot.refIds.filter((refId) => typeof refId === "string" && /^e\d+$/.test(refId));
244
+ const refRecord = isRecord(refSnapshot.refs) ? refSnapshot.refs : undefined;
245
+ const refEntries = refRecord
246
+ ? Object.fromEntries(refIds.flatMap((refId) => {
247
+ const entry = refRecord[refId];
248
+ if (!isRecord(entry) || typeof entry.name !== "string" || typeof entry.role !== "string")
249
+ return [];
250
+ const isContentEditable = typeof entry.isContentEditable === "boolean" ? entry.isContentEditable : undefined;
251
+ const isEditable = typeof entry.isEditable === "boolean" ? entry.isEditable : undefined;
252
+ return [[refId, { ...(isContentEditable !== undefined ? { isContentEditable } : {}), ...(isEditable !== undefined ? { isEditable } : {}), name: entry.name, role: entry.role }]];
253
+ }))
254
+ : undefined;
255
+ return {
256
+ refIds,
257
+ ...(refEntries && Object.keys(refEntries).length > 0 ? { refs: refEntries } : {}),
258
+ target: isRecord(refSnapshot.target)
259
+ ? normalizeSessionTabTarget({
260
+ title: typeof refSnapshot.target.title === "string" ? refSnapshot.target.title : undefined,
261
+ url: typeof refSnapshot.target.url === "string" ? refSnapshot.target.url : undefined,
262
+ })
263
+ : undefined,
264
+ };
265
+ }
266
+ function getLatestTabTargetOrder(targets) {
267
+ let latestOrder = 0;
268
+ for (const target of targets.values()) {
269
+ latestOrder = Math.max(latestOrder, target.order);
270
+ }
271
+ return latestOrder;
272
+ }
273
+ function getLatestRefStateOrder(snapshots, invalidations) {
274
+ let latestOrder = 0;
275
+ for (const snapshot of snapshots.values())
276
+ latestOrder = Math.max(latestOrder, snapshot.order);
277
+ for (const invalidation of invalidations.values())
278
+ latestOrder = Math.max(latestOrder, invalidation.order);
279
+ return latestOrder;
280
+ }
281
+ function shouldApplyTabTargetUpdate(current, updateOrder) {
282
+ return !current || updateOrder >= current.order;
283
+ }
284
+ function shouldApplyRefStateUpdate(options) {
285
+ const currentOrder = Math.max(options.currentSnapshot?.order ?? 0, options.currentInvalidation?.order ?? 0);
286
+ return options.updateOrder >= currentOrder;
287
+ }
288
+ function stripRefSnapshotOrder(snapshot) {
289
+ return snapshot ? { refIds: snapshot.refIds, ...(snapshot.refs ? { refs: snapshot.refs } : {}), target: snapshot.target } : undefined;
290
+ }
291
+ function stripRefSnapshotInvalidationOrder(invalidation) {
292
+ return invalidation ? { reason: invalidation.reason, summary: invalidation.summary } : undefined;
293
+ }
294
+ export class SessionPageState {
295
+ refSnapshotInvalidations = new Map();
296
+ refSnapshots = new Map();
297
+ tabPinningReasons = new Map();
298
+ tabTargets = new Map();
299
+ updateOrder = 0;
300
+ static fromBranch(branch) {
301
+ const state = new SessionPageState();
302
+ let restoredOrder = 0;
303
+ for (const entry of branch) {
304
+ if (!isRecord(entry) || entry.type !== "message")
305
+ continue;
306
+ const message = isRecord(entry.message) ? entry.message : undefined;
307
+ if (!message || message.toolName !== "agent_browser")
308
+ continue;
309
+ const details = isRecord(message.details) ? message.details : undefined;
310
+ if (!details)
311
+ continue;
312
+ const sessionName = typeof details.sessionName === "string" ? details.sessionName : undefined;
313
+ if (!sessionName)
314
+ continue;
315
+ const command = typeof details.command === "string" ? details.command : undefined;
316
+ const subcommand = typeof details.subcommand === "string" ? details.subcommand : undefined;
317
+ if (isCloseCommand(command) && message.isError !== true) {
318
+ restoredOrder += 1;
319
+ state.clearSession(sessionName);
320
+ continue;
321
+ }
322
+ const tabTarget = getRestoredSessionTabTarget(details, command, subcommand);
323
+ const refSnapshotInvalidation = getRestoredRefSnapshotInvalidation(details, command);
324
+ const refSnapshot = refSnapshotInvalidation ? undefined : getRestoredRefSnapshot(details);
325
+ if (!tabTarget && !refSnapshotInvalidation && !refSnapshot)
326
+ continue;
327
+ restoredOrder += 1;
328
+ if (tabTarget)
329
+ state.tabTargets.set(sessionName, { order: restoredOrder, target: tabTarget });
330
+ if (refSnapshotInvalidation) {
331
+ state.refSnapshots.delete(sessionName);
332
+ state.refSnapshotInvalidations.set(sessionName, { ...refSnapshotInvalidation, order: restoredOrder });
333
+ }
334
+ else if (refSnapshot) {
335
+ state.refSnapshotInvalidations.delete(sessionName);
336
+ state.refSnapshots.set(sessionName, { ...refSnapshot, order: restoredOrder });
337
+ }
338
+ }
339
+ state.updateOrder = Math.max(restoredOrder, getLatestTabTargetOrder(state.tabTargets), getLatestRefStateOrder(state.refSnapshots, state.refSnapshotInvalidations));
340
+ state.tabPinningReasons = new Map([...state.tabTargets.keys()].map((sessionName) => [sessionName, "restore"]));
341
+ return state;
342
+ }
343
+ beginUpdate() {
344
+ this.updateOrder += 1;
345
+ return this.updateOrder;
346
+ }
347
+ reset() {
348
+ this.refSnapshotInvalidations = new Map();
349
+ this.refSnapshots = new Map();
350
+ this.tabPinningReasons = new Map();
351
+ this.tabTargets = new Map();
352
+ this.updateOrder = 0;
353
+ }
354
+ get(sessionName) {
355
+ if (!sessionName)
356
+ return {};
357
+ return {
358
+ pinningReason: this.tabPinningReasons.get(sessionName),
359
+ refSnapshot: stripRefSnapshotOrder(this.refSnapshots.get(sessionName)),
360
+ refSnapshotInvalidation: stripRefSnapshotInvalidationOrder(this.refSnapshotInvalidations.get(sessionName)),
361
+ tabTarget: this.tabTargets.get(sessionName)?.target,
362
+ };
363
+ }
364
+ applyTabTarget(options) {
365
+ const current = this.tabTargets.get(options.sessionName);
366
+ if (!shouldApplyTabTargetUpdate(current, options.update)) {
367
+ return { ...this.get(options.sessionName), applied: false, stale: true };
368
+ }
369
+ this.tabTargets.set(options.sessionName, { order: options.update, target: options.target });
370
+ return { ...this.get(options.sessionName), applied: true };
371
+ }
372
+ applyRefSnapshot(options) {
373
+ if (!shouldApplyRefStateUpdate({
374
+ currentInvalidation: this.refSnapshotInvalidations.get(options.sessionName),
375
+ currentSnapshot: this.refSnapshots.get(options.sessionName),
376
+ updateOrder: options.update,
377
+ })) {
378
+ return { ...this.get(options.sessionName), applied: false, stale: true };
379
+ }
380
+ const snapshot = { ...options.snapshot, target: options.snapshot.target ?? options.fallbackTarget };
381
+ this.refSnapshotInvalidations.delete(options.sessionName);
382
+ this.refSnapshots.set(options.sessionName, { ...snapshot, order: options.update });
383
+ return { ...this.get(options.sessionName), applied: true };
384
+ }
385
+ applyRefSnapshotInvalidation(options) {
386
+ if (!shouldApplyRefStateUpdate({
387
+ currentInvalidation: this.refSnapshotInvalidations.get(options.sessionName),
388
+ currentSnapshot: this.refSnapshots.get(options.sessionName),
389
+ updateOrder: options.update,
390
+ })) {
391
+ return { ...this.get(options.sessionName), applied: false, stale: true };
392
+ }
393
+ this.refSnapshots.delete(options.sessionName);
394
+ this.refSnapshotInvalidations.set(options.sessionName, { ...options.invalidation, order: options.update });
395
+ return { ...this.get(options.sessionName), applied: true };
396
+ }
397
+ clearSession(sessionName) {
398
+ this.refSnapshotInvalidations.delete(sessionName);
399
+ this.refSnapshots.delete(sessionName);
400
+ this.tabPinningReasons.delete(sessionName);
401
+ this.tabTargets.delete(sessionName);
402
+ }
403
+ markPinning(sessionName, reason) {
404
+ this.tabPinningReasons.set(sessionName, reason);
405
+ }
406
+ clearRestorePinning(sessionName) {
407
+ if (this.tabPinningReasons.get(sessionName) === "restore") {
408
+ this.tabPinningReasons.delete(sessionName);
409
+ }
410
+ }
411
+ }
@@ -0,0 +1,13 @@
1
+ /**
2
+ * Purpose: Build compact JSON-schema string enums without importing pi runtime helpers.
3
+ * Responsibilities: Mirror pi-ai StringEnum's `{ type: "string", enum: [...] }` shape while keeping extension startup imports light.
4
+ * Scope: Schema construction only.
5
+ */
6
+ import { JsonSchema } from "./json-schema.js";
7
+ export function StringEnum(values, options) {
8
+ return JsonSchema.Unsafe({
9
+ type: "string",
10
+ enum: [...values],
11
+ ...options,
12
+ });
13
+ }