pi-agent-browser-native 0.9.3 → 0.9.5

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 (394) hide show
  1. package/CHANGELOG.md +106 -5
  2. package/README.md +58 -791
  3. package/dist/extensions/agent-browser/index.js +43101 -2031
  4. package/dist/extensions/agent-browser/lib/argv-descriptor.js +49 -26
  5. package/dist/extensions/agent-browser/lib/argv-grammar.js +82 -93
  6. package/dist/extensions/agent-browser/lib/bash-guard.js +129 -126
  7. package/dist/extensions/agent-browser/lib/batch-lifecycle.js +112 -51
  8. package/dist/extensions/agent-browser/lib/browser-execution-claim-codec.js +61 -0
  9. package/dist/extensions/agent-browser/lib/browser-execution-claim-storage.js +232 -0
  10. package/dist/extensions/agent-browser/lib/browser-execution-claims.js +202 -0
  11. package/dist/extensions/agent-browser/lib/browser-execution-identity.js +77 -0
  12. package/dist/extensions/agent-browser/lib/browser-journal-branch.js +98 -0
  13. package/dist/extensions/agent-browser/lib/browser-journal-contracts.js +1 -0
  14. package/dist/extensions/agent-browser/lib/browser-journal-projection.js +221 -0
  15. package/dist/extensions/agent-browser/lib/browser-journal-reader.js +89 -0
  16. package/dist/extensions/agent-browser/lib/browser-journal-replay.js +246 -0
  17. package/dist/extensions/agent-browser/lib/browser-journal.js +103 -394
  18. package/dist/extensions/agent-browser/lib/browser-legacy-pages.js +220 -0
  19. package/dist/extensions/agent-browser/lib/browser-legacy-projection.js +282 -0
  20. package/dist/extensions/agent-browser/lib/browser-legacy-types.js +1 -0
  21. package/dist/extensions/agent-browser/lib/browser-session-conversion-stream.js +217 -0
  22. package/dist/extensions/agent-browser/lib/browser-session-conversion.js +80 -335
  23. package/dist/extensions/agent-browser/lib/browser-transcript-contracts.js +35 -0
  24. package/dist/extensions/agent-browser/lib/browser-transcript-validation.js +166 -0
  25. package/dist/extensions/agent-browser/lib/browser-transcript.js +137 -82
  26. package/dist/extensions/agent-browser/lib/browser-value-parsing.js +15 -0
  27. package/dist/extensions/agent-browser/lib/command-capabilities.js +340 -0
  28. package/dist/extensions/agent-browser/lib/command-policy.js +107 -88
  29. package/dist/extensions/agent-browser/lib/command-taxonomy.js +49 -322
  30. package/dist/extensions/agent-browser/lib/config-policy.js +138 -343
  31. package/dist/extensions/agent-browser/lib/config-providers.js +71 -0
  32. package/dist/extensions/agent-browser/lib/config-validation.js +231 -0
  33. package/dist/extensions/agent-browser/lib/config.js +20 -8
  34. package/dist/extensions/agent-browser/lib/electron/cdp.js +20 -8
  35. package/dist/extensions/agent-browser/lib/electron/cleanup.js +128 -83
  36. package/dist/extensions/agent-browser/lib/electron/discovery-darwin.js +109 -0
  37. package/dist/extensions/agent-browser/lib/electron/discovery-desktop.js +136 -0
  38. package/dist/extensions/agent-browser/lib/electron/discovery-evidence.js +90 -0
  39. package/dist/extensions/agent-browser/lib/electron/discovery-linux.js +93 -0
  40. package/dist/extensions/agent-browser/lib/electron/discovery-packages.js +162 -0
  41. package/dist/extensions/agent-browser/lib/electron/discovery-types.js +2 -0
  42. package/dist/extensions/agent-browser/lib/electron/discovery.js +140 -549
  43. package/dist/extensions/agent-browser/lib/electron/launch-monitor.js +102 -0
  44. package/dist/extensions/agent-browser/lib/electron/launch-process.js +209 -0
  45. package/dist/extensions/agent-browser/lib/electron/launch-types.js +2 -0
  46. package/dist/extensions/agent-browser/lib/electron/launch.js +261 -355
  47. package/dist/extensions/agent-browser/lib/executable-path.js +21 -12
  48. package/dist/extensions/agent-browser/lib/execution-cwd.js +35 -18
  49. package/dist/extensions/agent-browser/lib/fs-utils.js +1 -9
  50. package/dist/extensions/agent-browser/lib/input-modes/electron.js +162 -121
  51. package/dist/extensions/agent-browser/lib/input-modes/job.js +123 -317
  52. package/dist/extensions/agent-browser/lib/input-modes/lookup-network-evidence.js +116 -0
  53. package/dist/extensions/agent-browser/lib/input-modes/lookup-plan.js +132 -0
  54. package/dist/extensions/agent-browser/lib/input-modes/lookup-redaction.js +57 -0
  55. package/dist/extensions/agent-browser/lib/input-modes/lookup-source-evidence.js +151 -0
  56. package/dist/extensions/agent-browser/lib/input-modes/lookup-workspace.js +142 -0
  57. package/dist/extensions/agent-browser/lib/input-modes/lookups.js +58 -412
  58. package/dist/extensions/agent-browser/lib/input-modes/params.js +86 -40
  59. package/dist/extensions/agent-browser/lib/input-modes/qa-analysis.js +268 -0
  60. package/dist/extensions/agent-browser/lib/input-modes/qa-presentation.js +117 -0
  61. package/dist/extensions/agent-browser/lib/input-modes/script-child.js +396 -0
  62. package/dist/extensions/agent-browser/lib/input-modes/script-protocol.js +86 -0
  63. package/dist/extensions/agent-browser/lib/input-modes/script-types.js +1 -0
  64. package/dist/extensions/agent-browser/lib/input-modes/script-validation.js +60 -0
  65. package/dist/extensions/agent-browser/lib/input-modes/script.js +60 -409
  66. package/dist/extensions/agent-browser/lib/input-modes/semantic-action.js +218 -128
  67. package/dist/extensions/agent-browser/lib/input-modes/shared.js +15 -4
  68. package/dist/extensions/agent-browser/lib/input-modes/types.js +56 -7
  69. package/dist/extensions/agent-browser/lib/json-schema.js +22 -10
  70. package/dist/extensions/agent-browser/lib/launch-scoped-flags.js +17 -5
  71. package/dist/extensions/agent-browser/lib/managed-session-policy-lock.js +92 -425
  72. package/dist/extensions/agent-browser/lib/managed-session-posix-storage.js +58 -0
  73. package/dist/extensions/agent-browser/lib/managed-session-project-identity.js +244 -0
  74. package/dist/extensions/agent-browser/lib/managed-session-restore-context.js +84 -0
  75. package/dist/extensions/agent-browser/lib/managed-session-restore-policy.js +119 -0
  76. package/dist/extensions/agent-browser/lib/managed-session-restore-state.js +83 -0
  77. package/dist/extensions/agent-browser/lib/managed-session-restore.js +168 -300
  78. package/dist/extensions/agent-browser/lib/managed-session-snapshot-records.js +277 -0
  79. package/dist/extensions/agent-browser/lib/managed-session-snapshots.js +128 -305
  80. package/dist/extensions/agent-browser/lib/managed-session-storage.js +56 -253
  81. package/dist/extensions/agent-browser/lib/native-confirmation.js +140 -0
  82. package/dist/extensions/agent-browser/lib/native-output.js +43 -9
  83. package/dist/extensions/agent-browser/lib/orchestration/batch-stdin.js +106 -59
  84. package/dist/extensions/agent-browser/lib/orchestration/browser-run/artifact-merge.js +14 -6
  85. package/dist/extensions/agent-browser/lib/orchestration/browser-run/artifact-paths.js +109 -57
  86. package/dist/extensions/agent-browser/lib/orchestration/browser-run/browser-observation-result.js +82 -0
  87. package/dist/extensions/agent-browser/lib/orchestration/browser-run/click-dispatch.js +164 -60
  88. package/dist/extensions/agent-browser/lib/orchestration/browser-run/diagnostic-values.js +31 -0
  89. package/dist/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.js +152 -781
  90. package/dist/extensions/agent-browser/lib/orchestration/browser-run/electron-observation.js +203 -0
  91. package/dist/extensions/agent-browser/lib/orchestration/browser-run/electron-text-diagnostics.js +109 -0
  92. package/dist/extensions/agent-browser/lib/orchestration/browser-run/fill-diagnostics.js +119 -0
  93. package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result-action-recovery.js +147 -0
  94. package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result-actions.js +144 -0
  95. package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result-content.js +123 -0
  96. package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result-contracts.js +1 -0
  97. package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result-details.js +156 -0
  98. package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result-electron.js +187 -0
  99. package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result-evidence.js +39 -0
  100. package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result-missing-binary.js +89 -0
  101. package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result-recovery.js +97 -0
  102. package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result-redaction.js +106 -0
  103. package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result-timeout.js +110 -0
  104. package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result.js +41 -538
  105. package/dist/extensions/agent-browser/lib/orchestration/browser-run/index.js +199 -154
  106. package/dist/extensions/agent-browser/lib/orchestration/browser-run/interaction-diagnostics.js +290 -0
  107. package/dist/extensions/agent-browser/lib/orchestration/browser-run/managed-session-close.js +167 -0
  108. package/dist/extensions/agent-browser/lib/orchestration/browser-run/managed-session-daemon-inspection.js +108 -0
  109. package/dist/extensions/agent-browser/lib/orchestration/browser-run/managed-session-daemon-policy.js +127 -205
  110. package/dist/extensions/agent-browser/lib/orchestration/browser-run/native-helper-observation.js +65 -0
  111. package/dist/extensions/agent-browser/lib/orchestration/browser-run/observation-types.js +1 -0
  112. package/dist/extensions/agent-browser/lib/orchestration/browser-run/overlay-diagnostics.js +120 -0
  113. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/network-page-filter.js +113 -30
  114. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/scroll-shims.js +109 -28
  115. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/snapshot-filter-data.js +150 -0
  116. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/snapshot-filter.js +147 -253
  117. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/snapshot-rendered-text.js +147 -0
  118. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/wait-timeouts.js +59 -24
  119. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare-artifacts.js +159 -0
  120. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare-command.js +131 -0
  121. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare-contracts.js +1 -0
  122. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare-daemon.js +93 -0
  123. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare-failure.js +42 -0
  124. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare-guards.js +242 -0
  125. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare-input.js +75 -0
  126. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare-observations.js +92 -0
  127. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare-page-selection.js +254 -0
  128. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare-page-state.js +121 -0
  129. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare-refs.js +130 -0
  130. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare-resources.js +114 -0
  131. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare-semantic.js +121 -0
  132. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare-session-plan.js +222 -0
  133. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare-timeouts.js +68 -0
  134. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare.js +176 -1123
  135. package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-analysis.js +191 -0
  136. package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-confirmation.js +152 -0
  137. package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-diagnostics.js +182 -0
  138. package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-electron.js +150 -0
  139. package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-lifecycle-contracts.js +1 -0
  140. package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-lifecycle-phase-contracts.js +1 -0
  141. package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-lifecycle-phase.js +115 -0
  142. package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-lifecycle.js +256 -0
  143. package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-managed-contracts.js +1 -0
  144. package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-managed.js +256 -0
  145. package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-native-phase-contracts.js +1 -0
  146. package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-native-phase.js +93 -0
  147. package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-native.js +113 -0
  148. package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-navigation-contracts.js +1 -0
  149. package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-navigation.js +316 -0
  150. package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-ownership.js +28 -0
  151. package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-page-phase-contracts.js +1 -0
  152. package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-page-phase.js +142 -0
  153. package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-presentation-contracts.js +1 -0
  154. package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-presentation.js +333 -0
  155. package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-publication-phase-contracts.js +1 -0
  156. package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-publication-phase.js +71 -0
  157. package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-publication.js +201 -0
  158. package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-receipts.js +183 -0
  159. package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-refs.js +200 -0
  160. package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-tabs.js +142 -0
  161. package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output.js +18 -968
  162. package/dist/extensions/agent-browser/lib/orchestration/browser-run/prompt-guards.js +23 -13
  163. package/dist/extensions/agent-browser/lib/orchestration/browser-run/recording-receipt-reconciliation.js +198 -0
  164. package/dist/extensions/agent-browser/lib/orchestration/browser-run/recording-recovery-contracts.js +1 -0
  165. package/dist/extensions/agent-browser/lib/orchestration/browser-run/recording-recovery-presentation.js +154 -0
  166. package/dist/extensions/agent-browser/lib/orchestration/browser-run/recording-recovery.js +68 -151
  167. package/dist/extensions/agent-browser/lib/orchestration/browser-run/screenshot-capture.js +14 -0
  168. package/dist/extensions/agent-browser/lib/orchestration/browser-run/screenshot-observation.js +169 -46
  169. package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-state-commands.js +130 -0
  170. package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-state-electron.js +212 -0
  171. package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-state-observations.js +61 -0
  172. package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-state-outcomes.js +187 -0
  173. package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-state-refs.js +169 -0
  174. package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-state-tabs.js +228 -0
  175. package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-state.js +77 -765
  176. package/dist/extensions/agent-browser/lib/orchestration/browser-run/text-visibility-diagnostics.js +182 -0
  177. package/dist/extensions/agent-browser/lib/orchestration/browser-run/timeout-diagnostics.js +256 -0
  178. package/dist/extensions/agent-browser/lib/orchestration/browser-run/timeout-progress.js +108 -0
  179. package/dist/extensions/agent-browser/lib/orchestration/code-artifact-observation.js +39 -0
  180. package/dist/extensions/agent-browser/lib/orchestration/code-image-observation.js +119 -0
  181. package/dist/extensions/agent-browser/lib/orchestration/electron-host/branch.js +93 -0
  182. package/dist/extensions/agent-browser/lib/orchestration/electron-host/cleanup-result.js +58 -0
  183. package/dist/extensions/agent-browser/lib/orchestration/electron-host/cleanup.js +78 -0
  184. package/dist/extensions/agent-browser/lib/orchestration/electron-host/contracts.js +1 -0
  185. package/dist/extensions/agent-browser/lib/orchestration/electron-host/discovery.js +106 -0
  186. package/dist/extensions/agent-browser/lib/orchestration/electron-host/index.js +48 -821
  187. package/dist/extensions/agent-browser/lib/orchestration/electron-host/policy.js +86 -0
  188. package/dist/extensions/agent-browser/lib/orchestration/electron-host/probe-collection.js +68 -0
  189. package/dist/extensions/agent-browser/lib/orchestration/electron-host/probe-data.js +169 -0
  190. package/dist/extensions/agent-browser/lib/orchestration/electron-host/probe-presentation.js +182 -0
  191. package/dist/extensions/agent-browser/lib/orchestration/electron-host/probe.js +161 -0
  192. package/dist/extensions/agent-browser/lib/orchestration/electron-host/status.js +167 -0
  193. package/dist/extensions/agent-browser/lib/orchestration/execution-queue.js +22 -10
  194. package/dist/extensions/agent-browser/lib/orchestration/extension-artifact-preflight.js +211 -0
  195. package/dist/extensions/agent-browser/lib/orchestration/extension-artifacts.js +23 -0
  196. package/dist/extensions/agent-browser/lib/orchestration/extension-attachment-replay.js +99 -0
  197. package/dist/extensions/agent-browser/lib/orchestration/extension-branch-restore.js +275 -0
  198. package/dist/extensions/agent-browser/lib/orchestration/extension-code.js +280 -0
  199. package/dist/extensions/agent-browser/lib/orchestration/extension-command-admission.js +303 -0
  200. package/dist/extensions/agent-browser/lib/orchestration/extension-command-output.js +36 -0
  201. package/dist/extensions/agent-browser/lib/orchestration/extension-command-result.js +335 -0
  202. package/dist/extensions/agent-browser/lib/orchestration/extension-command-run.js +227 -0
  203. package/dist/extensions/agent-browser/lib/orchestration/extension-command-state.js +1 -0
  204. package/dist/extensions/agent-browser/lib/orchestration/extension-electron-ownership.js +204 -0
  205. package/dist/extensions/agent-browser/lib/orchestration/extension-execution-locks.js +139 -0
  206. package/dist/extensions/agent-browser/lib/orchestration/extension-execution-queue.js +24 -0
  207. package/dist/extensions/agent-browser/lib/orchestration/extension-host-coordination.js +98 -0
  208. package/dist/extensions/agent-browser/lib/orchestration/extension-host-run.js +295 -0
  209. package/dist/extensions/agent-browser/lib/orchestration/extension-intent-replay.js +41 -0
  210. package/dist/extensions/agent-browser/lib/orchestration/extension-invocation.js +7 -0
  211. package/dist/extensions/agent-browser/lib/orchestration/extension-lifecycle.js +102 -0
  212. package/dist/extensions/agent-browser/lib/orchestration/extension-managed-ownership.js +133 -0
  213. package/dist/extensions/agent-browser/lib/orchestration/extension-observation.js +120 -0
  214. package/dist/extensions/agent-browser/lib/orchestration/extension-prompt.js +180 -0
  215. package/dist/extensions/agent-browser/lib/orchestration/extension-recording.js +290 -0
  216. package/dist/extensions/agent-browser/lib/orchestration/extension-resource-contracts.js +1 -0
  217. package/dist/extensions/agent-browser/lib/orchestration/extension-resource-replay.js +269 -0
  218. package/dist/extensions/agent-browser/lib/orchestration/extension-result-state.js +113 -0
  219. package/dist/extensions/agent-browser/lib/orchestration/extension-runtime.js +146 -0
  220. package/dist/extensions/agent-browser/lib/orchestration/extension-script-leases.js +58 -0
  221. package/dist/extensions/agent-browser/lib/orchestration/extension-tool-boundary.js +62 -0
  222. package/dist/extensions/agent-browser/lib/orchestration/extension-version-check.js +54 -0
  223. package/dist/extensions/agent-browser/lib/orchestration/input-plan-types.js +1 -0
  224. package/dist/extensions/agent-browser/lib/orchestration/input-plan.js +261 -185
  225. package/dist/extensions/agent-browser/lib/orchestration/input-redaction.js +62 -0
  226. package/dist/extensions/agent-browser/lib/orchestration/native-config.js +144 -0
  227. package/dist/extensions/agent-browser/lib/orchestration/native-root-launch.js +77 -0
  228. package/dist/extensions/agent-browser/lib/orchestration/native-session-defaults.js +210 -157
  229. package/dist/extensions/agent-browser/lib/orchestration/operation-paths.js +124 -58
  230. package/dist/extensions/agent-browser/lib/orchestration/output-file.js +197 -99
  231. package/dist/extensions/agent-browser/lib/orchestration/script-mode.js +199 -96
  232. package/dist/extensions/agent-browser/lib/owned-managed-session-context.js +55 -0
  233. package/dist/extensions/agent-browser/lib/page-target-batch.js +118 -0
  234. package/dist/extensions/agent-browser/lib/page-target-guards.js +50 -0
  235. package/dist/extensions/agent-browser/lib/page-target-navigation.js +104 -0
  236. package/dist/extensions/agent-browser/lib/page-target-validation.js +55 -246
  237. package/dist/extensions/agent-browser/lib/parsing.js +13 -4
  238. package/dist/extensions/agent-browser/lib/pi-tool-rendering.js +133 -68
  239. package/dist/extensions/agent-browser/lib/playbook.js +16 -12
  240. package/dist/extensions/agent-browser/lib/process-child.js +284 -0
  241. package/dist/extensions/agent-browser/lib/process-environment.js +13 -6
  242. package/dist/extensions/agent-browser/lib/process-errors.js +23 -0
  243. package/dist/extensions/agent-browser/lib/process-identity.js +47 -15
  244. package/dist/extensions/agent-browser/lib/process-socket-storage.js +256 -0
  245. package/dist/extensions/agent-browser/lib/process-stdout.js +74 -0
  246. package/dist/extensions/agent-browser/lib/process.js +145 -536
  247. package/dist/extensions/agent-browser/lib/prompt-artifacts.js +287 -0
  248. package/dist/extensions/agent-browser/lib/prompt-policy.js +15 -194
  249. package/dist/extensions/agent-browser/lib/read-command.js +41 -0
  250. package/dist/extensions/agent-browser/lib/read-confirmation.js +131 -68
  251. package/dist/extensions/agent-browser/lib/recording-reservations.js +118 -45
  252. package/dist/extensions/agent-browser/lib/redaction-fields.js +75 -0
  253. package/dist/extensions/agent-browser/lib/redaction-json.js +149 -0
  254. package/dist/extensions/agent-browser/lib/redaction-url.js +88 -0
  255. package/dist/extensions/agent-browser/lib/results/action-contracts.js +1 -0
  256. package/dist/extensions/agent-browser/lib/results/action-recommendations.js +28 -271
  257. package/dist/extensions/agent-browser/lib/results/artifact-contracts.js +1 -0
  258. package/dist/extensions/agent-browser/lib/results/artifact-manifest-validation.js +133 -0
  259. package/dist/extensions/agent-browser/lib/results/artifact-manifest.js +96 -94
  260. package/dist/extensions/agent-browser/lib/results/artifact-recommendations.js +73 -0
  261. package/dist/extensions/agent-browser/lib/results/categories.js +111 -54
  262. package/dist/extensions/agent-browser/lib/results/confirmation.js +29 -13
  263. package/dist/extensions/agent-browser/lib/results/editable-ref-evidence.js +62 -27
  264. package/dist/extensions/agent-browser/lib/results/electron-recommendations.js +83 -0
  265. package/dist/extensions/agent-browser/lib/results/envelope-decoding.js +48 -0
  266. package/dist/extensions/agent-browser/lib/results/envelope.js +90 -67
  267. package/dist/extensions/agent-browser/lib/results/evidence-contracts.js +1 -0
  268. package/dist/extensions/agent-browser/lib/results/failure-recommendations.js +180 -0
  269. package/dist/extensions/agent-browser/lib/results/network-routes.js +86 -45
  270. package/dist/extensions/agent-browser/lib/results/network.js +35 -12
  271. package/dist/extensions/agent-browser/lib/results/next-actions.js +49 -19
  272. package/dist/extensions/agent-browser/lib/results/presentation/artifact-files.js +320 -0
  273. package/dist/extensions/agent-browser/lib/results/presentation/artifact-format.js +138 -0
  274. package/dist/extensions/agent-browser/lib/results/presentation/artifact-images.js +113 -0
  275. package/dist/extensions/agent-browser/lib/results/presentation/artifact-verification.js +145 -0
  276. package/dist/extensions/agent-browser/lib/results/presentation/artifacts.js +99 -555
  277. package/dist/extensions/agent-browser/lib/results/presentation/batch-contracts.js +1 -0
  278. package/dist/extensions/agent-browser/lib/results/presentation/batch-recording.js +118 -0
  279. package/dist/extensions/agent-browser/lib/results/presentation/batch-redaction.js +68 -0
  280. package/dist/extensions/agent-browser/lib/results/presentation/batch-rendering.js +135 -0
  281. package/dist/extensions/agent-browser/lib/results/presentation/batch-step.js +227 -0
  282. package/dist/extensions/agent-browser/lib/results/presentation/batch-summary.js +91 -0
  283. package/dist/extensions/agent-browser/lib/results/presentation/batch.js +91 -460
  284. package/dist/extensions/agent-browser/lib/results/presentation/browser-profile-recovery.js +9 -5
  285. package/dist/extensions/agent-browser/lib/results/presentation/command-presenters.js +216 -0
  286. package/dist/extensions/agent-browser/lib/results/presentation/common.js +26 -9
  287. package/dist/extensions/agent-browser/lib/results/presentation/content.js +86 -29
  288. package/dist/extensions/agent-browser/lib/results/presentation/diagnostic-a11y.js +67 -0
  289. package/dist/extensions/agent-browser/lib/results/presentation/diagnostic-health.js +79 -0
  290. package/dist/extensions/agent-browser/lib/results/presentation/diagnostic-identity.js +199 -0
  291. package/dist/extensions/agent-browser/lib/results/presentation/diagnostic-logs.js +75 -0
  292. package/dist/extensions/agent-browser/lib/results/presentation/diagnostic-network-actions.js +216 -0
  293. package/dist/extensions/agent-browser/lib/results/presentation/diagnostic-network.js +124 -0
  294. package/dist/extensions/agent-browser/lib/results/presentation/diagnostic-redaction.js +64 -0
  295. package/dist/extensions/agent-browser/lib/results/presentation/diagnostic-storage.js +209 -0
  296. package/dist/extensions/agent-browser/lib/results/presentation/diagnostic-stream.js +69 -0
  297. package/dist/extensions/agent-browser/lib/results/presentation/diagnostic-summary.js +222 -0
  298. package/dist/extensions/agent-browser/lib/results/presentation/diagnostics.js +103 -1052
  299. package/dist/extensions/agent-browser/lib/results/presentation/early-result.js +70 -0
  300. package/dist/extensions/agent-browser/lib/results/presentation/errors.js +130 -60
  301. package/dist/extensions/agent-browser/lib/results/presentation/input-contracts.js +1 -0
  302. package/dist/extensions/agent-browser/lib/results/presentation/large-output.js +60 -145
  303. package/dist/extensions/agent-browser/lib/results/presentation/navigation.js +147 -88
  304. package/dist/extensions/agent-browser/lib/results/presentation/notices.js +75 -0
  305. package/dist/extensions/agent-browser/lib/results/presentation/observation-contracts.js +1 -0
  306. package/dist/extensions/agent-browser/lib/results/presentation/observation-rendering.js +127 -0
  307. package/dist/extensions/agent-browser/lib/results/presentation/outcomes.js +120 -0
  308. package/dist/extensions/agent-browser/lib/results/presentation/output-completion.js +77 -0
  309. package/dist/extensions/agent-browser/lib/results/presentation/output-spill.js +51 -0
  310. package/dist/extensions/agent-browser/lib/results/presentation/registry.js +116 -240
  311. package/dist/extensions/agent-browser/lib/results/presentation/semantic-action.js +35 -21
  312. package/dist/extensions/agent-browser/lib/results/presentation/skills.js +109 -73
  313. package/dist/extensions/agent-browser/lib/results/presentation/source.js +109 -0
  314. package/dist/extensions/agent-browser/lib/results/presentation.js +17 -261
  315. package/dist/extensions/agent-browser/lib/results/recommendation-contracts.js +1 -0
  316. package/dist/extensions/agent-browser/lib/results/recording.js +52 -14
  317. package/dist/extensions/agent-browser/lib/results/recovery-actions.js +14 -5
  318. package/dist/extensions/agent-browser/lib/results/recovery-next-actions.js +30 -22
  319. package/dist/extensions/agent-browser/lib/results/selector-recovery-target.js +83 -0
  320. package/dist/extensions/agent-browser/lib/results/selector-recovery.js +119 -119
  321. package/dist/extensions/agent-browser/lib/results/snapshot-compact.js +266 -0
  322. package/dist/extensions/agent-browser/lib/results/snapshot-content.js +32 -0
  323. package/dist/extensions/agent-browser/lib/results/snapshot-high-value-controls.js +104 -71
  324. package/dist/extensions/agent-browser/lib/results/snapshot-refs.js +31 -11
  325. package/dist/extensions/agent-browser/lib/results/snapshot-segments.js +125 -66
  326. package/dist/extensions/agent-browser/lib/results/snapshot-spill.js +26 -7
  327. package/dist/extensions/agent-browser/lib/results/snapshot.js +28 -248
  328. package/dist/extensions/agent-browser/lib/results/text.js +16 -5
  329. package/dist/extensions/agent-browser/lib/runtime-args-validation.js +133 -0
  330. package/dist/extensions/agent-browser/lib/runtime-compatibility.js +81 -0
  331. package/dist/extensions/agent-browser/lib/runtime-contracts.js +1 -0
  332. package/dist/extensions/agent-browser/lib/runtime-redaction.js +118 -0
  333. package/dist/extensions/agent-browser/lib/runtime-replay-events.js +144 -0
  334. package/dist/extensions/agent-browser/lib/runtime-session-identity.js +76 -0
  335. package/dist/extensions/agent-browser/lib/runtime-session-plan.js +114 -0
  336. package/dist/extensions/agent-browser/lib/runtime-session-replay.js +123 -0
  337. package/dist/extensions/agent-browser/lib/runtime-tab-correction.js +65 -0
  338. package/dist/extensions/agent-browser/lib/runtime-timeouts.js +28 -0
  339. package/dist/extensions/agent-browser/lib/runtime.js +137 -1041
  340. package/dist/extensions/agent-browser/lib/session-identity.js +51 -0
  341. package/dist/extensions/agent-browser/lib/session-page-observation.js +357 -0
  342. package/dist/extensions/agent-browser/lib/session-page-state.js +178 -333
  343. package/dist/extensions/agent-browser/lib/session-page-types.js +1 -0
  344. package/dist/extensions/agent-browser/lib/temp-artifact-budget.js +81 -0
  345. package/dist/extensions/agent-browser/lib/temp-mutation-queue.js +7 -0
  346. package/dist/extensions/agent-browser/lib/temp-root-ownership.js +234 -0
  347. package/dist/extensions/agent-browser/lib/temp-root.js +211 -0
  348. package/dist/extensions/agent-browser/lib/temp.js +33 -446
  349. package/dist/extensions/agent-browser/lib/tool-surface.js +94 -24
  350. package/dist/extensions/agent-browser/lib/upstream-version.js +3 -2
  351. package/dist/extensions/agent-browser/lib/web-search-providers.js +193 -0
  352. package/dist/extensions/agent-browser/lib/web-search-request.js +102 -0
  353. package/dist/extensions/agent-browser/lib/web-search-results.js +281 -0
  354. package/dist/extensions/agent-browser/lib/web-search-schema.js +82 -0
  355. package/dist/extensions/agent-browser/lib/web-search.js +124 -614
  356. package/dist/extensions/agent-browser/lib/webmcp-observation.js +13 -6
  357. package/dist/extensions/agent-browser/lib/windows-stock-launcher.js +63 -37
  358. package/dist/extensions/agent-browser/script-worker.js +80 -43
  359. package/dist/scripts/agent-browser-target.mjs +4 -2
  360. package/docs/ARCHITECTURE.md +19 -6
  361. package/docs/CODE_QUALITY.md +267 -0
  362. package/docs/COMMAND_REFERENCE.md +272 -241
  363. package/docs/ELECTRON.md +77 -46
  364. package/docs/REFERENCE.md +869 -0
  365. package/docs/RELEASE.md +25 -22
  366. package/docs/REQUIREMENTS.md +2 -1
  367. package/docs/SUPPORT_MATRIX.md +81 -79
  368. package/docs/TOOL_CONTRACT.md +158 -125
  369. package/docs/platform-smoke.md +8 -8
  370. package/package.json +144 -120
  371. package/scripts/agent-browser-capability-baseline.mjs +361 -957
  372. package/scripts/agent-browser-target.mjs +6 -2
  373. package/scripts/build.mjs +37 -8
  374. package/scripts/capability-core-commands.mjs +193 -0
  375. package/scripts/capability-options.mjs +283 -0
  376. package/scripts/capability-samples.mjs +133 -0
  377. package/scripts/capability-section.mjs +11 -0
  378. package/scripts/config.mjs +235 -132
  379. package/scripts/convert-browser-session.mjs +30 -9
  380. package/scripts/doctor-sources.mjs +195 -0
  381. package/scripts/doctor.mjs +94 -249
  382. package/scripts/platform-smoke/artifacts.mjs +38 -11
  383. package/scripts/platform-smoke/commands.mjs +104 -0
  384. package/scripts/platform-smoke/crabbox-runner.mjs +167 -80
  385. package/scripts/platform-smoke/doctor-support.mjs +157 -0
  386. package/scripts/platform-smoke/doctor-windows.mjs +230 -0
  387. package/scripts/platform-smoke/doctor.mjs +212 -320
  388. package/scripts/platform-smoke/lease-evidence.mjs +132 -0
  389. package/scripts/platform-smoke/linux-image/Dockerfile +8 -2
  390. package/scripts/platform-smoke/suite-checks.mjs +98 -0
  391. package/scripts/platform-smoke/suite-evidence.mjs +281 -0
  392. package/scripts/platform-smoke/targets.mjs +154 -472
  393. package/scripts/platform-smoke.mjs +55 -40
  394. package/scripts/prepare.mjs +16 -7
@@ -1,122 +1,88 @@
1
1
  import { AsyncLocalStorage } from "node:async_hooks";
2
- import { createHash, randomInt, randomUUID } from "node:crypto";
3
- import { lstat, mkdir, readFile, readdir, realpath, rename, rm, writeFile } from "node:fs/promises";
4
- import { tmpdir } from "node:os";
5
- import { basename, dirname, join } from "node:path";
6
- import { canonicalizeAgentBrowserNamespace, foldAgentBrowserFilesystemIdentity } from "./argv-grammar.js";
7
- import { getAgentBrowserSocketDirValidationError, resolveAgentBrowserSocketDir } from "./process.js";
8
- import { processStartIdentitiesMatch, readProcessStartIdentity } from "./process-identity.js";
9
- const POLICY_LOCK_WAIT_MS = 1_000;
10
- const POLICY_LOCK_RETRY_MS = 10;
11
- const POLICY_LOCK_MAX_BYTES = 1_048_576; // One claim may cover an entire owned-browser cleanup set.
12
- const LOCK_OWNER_FILE = "owner.json";
13
- const LOCK_TICKET_FILE = "ticket.json";
14
- const STALE_CLAIM_MIN_AGE_MS = 30 * 60 * 1_000;
15
- const POLICY_GC_BATCH_SIZE = 8;
16
- let lastPolicyGcName;
17
- function getCoordinationDirectory(platform = process.platform) {
18
- if (platform !== "win32") {
19
- const uid = typeof process.getuid === "function" ? process.getuid() : undefined;
20
- return platform === "android"
21
- ? join(tmpdir(), `pi-agent-browser-policy${uid === undefined ? "" : `-${uid}`}`)
22
- : `/tmp/pi-agent-browser-policy${uid === undefined ? "" : `-${uid}`}`;
23
- }
24
- const user = process.env.USERNAME ?? process.env.USER ?? "unknown";
25
- const suffix = createHash("sha256").update(user).digest("hex").slice(0, 12);
26
- return join(tmpdir(), `pi-agent-browser-policy-${suffix}`);
27
- }
28
- async function ensureCoordinationDirectory(path, platform) {
29
- try {
30
- try {
31
- await mkdir(path, { mode: 0o700 });
32
- }
33
- catch (error) {
34
- if (error.code !== "EEXIST")
35
- return false;
36
- }
37
- const entry = await lstat(path);
38
- if (entry.isSymbolicLink() || !entry.isDirectory())
39
- return false;
40
- if (platform !== "win32") {
41
- const uid = typeof process.getuid === "function" ? process.getuid() : undefined;
42
- if (uid === undefined || entry.uid !== uid || (entry.mode & 0o077) !== 0)
43
- return false;
44
- }
45
- return true;
46
- }
47
- catch {
48
- return false;
49
- }
50
- }
51
- export async function resolveBrowserExecutionIdentity(options) {
52
- const socketDir = resolveAgentBrowserSocketDir(options);
53
- const namespace = canonicalizeAgentBrowserNamespace(options.namespace);
54
- let socketContext;
55
- if (process.platform === "win32") {
56
- // Native Windows can reach the same namespace/session TCP daemon from
57
- // different storage roots. Do not split those callers into separate locks.
58
- socketContext = `win32-native:${namespace ?? ""}`;
59
- }
60
- else {
61
- if (socketDir === undefined)
62
- throw new Error("Browser execution coordination requires the native socket directory.");
63
- const error = await getAgentBrowserSocketDirValidationError(socketDir);
64
- if (error)
65
- throw new Error(`Browser execution coordination cannot use socket storage: ${error}.`);
66
- const root = await realpath(socketDir);
67
- socketContext = namespace ? join(root, "namespaces", namespace, "run") : root;
68
- }
69
- return {
70
- socketContext: foldAgentBrowserFilesystemIdentity(socketContext, process.platform),
71
- ...(options.sessionName !== undefined ? { sessionName: foldAgentBrowserFilesystemIdentity(options.sessionName, process.platform) } : {}),
72
- };
73
- }
74
- export function getBrowserExecutionLockPath(identity) {
75
- const digest = createHash("sha256").update(identity.socketContext).digest("hex");
76
- return join(getCoordinationDirectory(), `.pi-agent-browser-policy-${digest}.lock-v4`);
77
- }
2
+ import { acquireExecutionClaim, } from "./browser-execution-claims.js";
3
+ import { resolveBrowserExecutionIdentity, } from "./browser-execution-identity.js";
4
+ export { resolveBrowserExecutionIdentity, getBrowserExecutionLockPath, } from "./browser-execution-identity.js";
78
5
  function groupExecutionIdentities(identities) {
79
- if (identities.length === 0)
6
+ if (identities.length === 0) {
80
7
  throw new Error("Browser execution coordination requires at least one identity.");
8
+ }
81
9
  const groups = new Map();
82
10
  for (const { socketContext, sessionName } of identities) {
83
- if (sessionName === undefined)
11
+ if (sessionName === undefined) {
84
12
  groups.set(socketContext, null);
13
+ }
85
14
  else if (groups.get(socketContext) !== null) {
86
15
  const sessions = groups.get(socketContext) ?? new Set();
87
16
  sessions.add(sessionName);
88
17
  groups.set(socketContext, sessions);
89
18
  }
90
19
  }
91
- return [...groups.keys()].sort().map(socketContext => {
20
+ return [...groups.keys()].sort().map((socketContext) => {
92
21
  const sessions = groups.get(socketContext);
93
22
  return { socketContext, sessionNames: sessions ? [...sessions].sort() : null };
94
23
  });
95
24
  }
96
25
  const executionScope = new AsyncLocalStorage();
26
+ function identityCovers(held, identity) {
27
+ if (held.socketContext !== identity.socketContext) {
28
+ return false;
29
+ }
30
+ const heldSessions = held.sessionNames;
31
+ if (heldSessions === null) {
32
+ return true;
33
+ }
34
+ const innerSessions = identity.sessionNames;
35
+ return innerSessions !== null && innerSessions.every((session) => heldSessions.includes(session));
36
+ }
97
37
  function scopeCovers(outer, inner) {
98
- return inner.every(identity => outer.some(held => held.socketContext === identity.socketContext
99
- && (held.sessionNames === null || identity.sessionNames !== null && identity.sessionNames.every(session => held.sessionNames.includes(session)))));
38
+ return inner.every((identity) => outer.some((held) => identityCovers(held, identity)));
39
+ }
40
+ async function acquireClaimsInOrder(identities, budget, remember) {
41
+ for (const identity of identities) {
42
+ // Acquiring sorted native socket contexts in order prevents lock-order cycles.
43
+ // oxlint-disable-next-line no-await-in-loop
44
+ const lock = await acquireExecutionClaim({
45
+ identity,
46
+ deadline: budget.deadline,
47
+ signal: budget.signal,
48
+ });
49
+ if (lock) {
50
+ remember(lock);
51
+ }
52
+ budget.check();
53
+ if (!lock) {
54
+ throw new Error("Browser execution coordination is unavailable or busy; no browser command was run.");
55
+ }
56
+ }
57
+ }
58
+ async function runInScope(identities, signal, run) {
59
+ const scope = { identities, active: true, signal, children: Promise.resolve() };
60
+ try {
61
+ return await executionScope.run(scope, () => run(signal));
62
+ }
63
+ finally {
64
+ scope.active = false;
65
+ await scope.children;
66
+ }
100
67
  }
101
- /**
102
- * Hold across every helper and action (or a complete code cell), not each CLI call.
103
- * Nested siblings are serialized; recursive inner calls and managed policy checks borrow
104
- * the claims. Declare every old/new identity up front for replacement or multi-browser cleanup;
105
- * omit sessionName for close --all. Upgrading mid-operation would allow a lock-order cycle.
106
- * Sessions sharing a socket context use ONE set-valued claim: acquiring them separately
107
- * could deadlock against a namespace-wide claimant between the first and second session.
108
- * Cancellation never releases a still-running callback. The callback must pass its signal
109
- * to subprocesses and await their termination; finally drains started children before release.
110
- */
68
+ /** Hold across helpers/actions or a complete code cell. Nested siblings serialize and recursive
69
+ * calls borrow existing claims. Declare every replacement/cleanup identity up front: upgrades
70
+ * mid-operation would permit lock-order cycles. One set-valued claim per socket context prevents
71
+ * namespace-wide contenders from deadlocking between individual session acquisitions.
72
+ * Cancellation does not release running callbacks: they must pass the signal to subprocesses
73
+ * and await termination. Finally drains started children before releasing the native claims. */
111
74
  export async function withBrowserExecutionLocks(options, run) {
112
- if (!Number.isFinite(options.deadline))
75
+ if (!Number.isFinite(options.deadline)) {
113
76
  throw new Error("Browser execution coordination requires a finite deadline.");
77
+ }
114
78
  const identities = groupExecutionIdentities(options.identities);
115
79
  const parent = executionScope.getStore();
116
- if (parent && !parent.active)
80
+ if (parent && !parent.active) {
117
81
  throw new Error("Browser execution scope has already finished.");
118
- if (parent && !scopeCovers(parent.identities, identities))
82
+ }
83
+ if (parent && !scopeCovers(parent.identities, identities)) {
119
84
  throw new Error("Cannot change or upgrade browser identity inside an execution lock; acquire every required identity before starting.");
85
+ }
120
86
  const controller = new AbortController();
121
87
  const signals = [controller.signal, options.signal, parent?.signal].filter((signal) => signal !== undefined);
122
88
  const signal = AbortSignal.any(signals);
@@ -124,264 +90,51 @@ export async function withBrowserExecutionLocks(options, run) {
124
90
  const remaining = options.deadline - Date.now();
125
91
  const timer = setTimeout(expire, Math.max(0, Math.min(remaining, 2_147_483_647)));
126
92
  const checkBudget = () => {
127
- if (Date.now() >= options.deadline)
93
+ if (Date.now() >= options.deadline) {
128
94
  expire();
95
+ }
129
96
  signal.throwIfAborted();
130
97
  };
131
98
  const locks = [];
132
99
  try {
133
100
  checkBudget();
134
101
  if (!parent) {
135
- for (const identity of identities) {
136
- const lock = await acquireExecutionClaim({ identity, deadline: options.deadline, signal });
137
- if (lock)
138
- locks.push(lock);
139
- checkBudget();
140
- if (!lock)
141
- throw new Error("Browser execution coordination is unavailable or busy; no browser command was run.");
142
- }
102
+ await acquireClaimsInOrder(identities, { deadline: options.deadline, signal, check: checkBudget }, (lock) => {
103
+ locks.push(lock);
104
+ });
143
105
  }
144
106
  const execute = async () => {
145
107
  checkBudget();
146
- if (options.waitOnly)
108
+ if (options.waitOnly === true) {
147
109
  clearTimeout(timer);
148
- const scope = { identities: parent?.identities ?? identities, active: true, signal, children: Promise.resolve() };
149
- try {
150
- return await executionScope.run(scope, () => run(signal));
151
- }
152
- finally {
153
- scope.active = false;
154
- await scope.children;
155
110
  }
111
+ return runInScope(parent?.identities ?? identities, signal, run);
156
112
  };
157
- if (!parent)
113
+ if (!parent) {
158
114
  return await execute();
115
+ }
159
116
  const result = parent.children.then(execute);
117
+ // The caller owns this result; the parent drains both outcomes before releasing claims.
160
118
  parent.children = result.then(() => undefined, () => undefined);
161
119
  return await result;
162
120
  }
163
121
  finally {
164
122
  clearTimeout(timer);
165
- await Promise.all(locks.reverse().map(lock => lock.release()));
123
+ await Promise.all(locks.reverse().map((lock) => lock.release()));
166
124
  }
167
125
  }
168
126
  export function withBrowserExecutionLock(options, run) {
169
- return withBrowserExecutionLocks({ identities: [options.identity], signal: options.signal, deadline: options.deadline, waitOnly: options.waitOnly }, run);
170
- }
171
- function parseOwner(content) {
172
- if (Buffer.byteLength(content) > POLICY_LOCK_MAX_BYTES)
173
- return undefined;
174
- try {
175
- const parsed = JSON.parse(content);
176
- return parsed.version === 4
177
- && (parsed.sessionNames === null || Array.isArray(parsed.sessionNames) && parsed.sessionNames.length > 0 && parsed.sessionNames.every(session => typeof session === "string"))
178
- && Number.isSafeInteger(parsed.pid) && (parsed.pid ?? 0) > 0
179
- && typeof parsed.startIdentity === "string" && parsed.startIdentity.length > 0
180
- && typeof parsed.token === "string" && parsed.token.length > 0
181
- && !parsed.token.includes("/") && !parsed.token.includes("\\") && !parsed.token.includes("\0")
182
- ? parsed
183
- : undefined;
184
- }
185
- catch {
186
- return undefined;
187
- }
188
- }
189
- function parseTicket(content, token) {
190
- if (Buffer.byteLength(content) > POLICY_LOCK_MAX_BYTES)
191
- return undefined;
192
- try {
193
- const parsed = JSON.parse(content);
194
- return parsed.version === 4
195
- && parsed.token === token
196
- && Number.isSafeInteger(parsed.ticket)
197
- && (parsed.ticket ?? 0) > 0
198
- ? parsed.ticket
199
- : undefined;
200
- }
201
- catch {
202
- return undefined;
203
- }
204
- }
205
- async function readClaim(path) {
206
- try {
207
- const directory = await lstat(path);
208
- const ownerPath = join(path, LOCK_OWNER_FILE);
209
- const ownerEntry = await lstat(ownerPath);
210
- if (!directory.isDirectory() || directory.isSymbolicLink() || !ownerEntry.isFile() || ownerEntry.isSymbolicLink())
211
- return undefined;
212
- if (ownerEntry.size > POLICY_LOCK_MAX_BYTES)
213
- return undefined;
214
- if (process.platform !== "win32") {
215
- const uid = typeof process.getuid === "function" ? process.getuid() : undefined;
216
- if (uid === undefined || directory.uid !== uid || ownerEntry.uid !== uid || (directory.mode & 0o077) !== 0 || (ownerEntry.mode & 0o177) !== 0)
217
- return undefined;
218
- }
219
- const owner = parseOwner(await readFile(ownerPath, "utf8"));
220
- if (!owner)
221
- return undefined;
222
- const ticketPath = join(path, LOCK_TICKET_FILE);
223
- try {
224
- const ticketEntry = await lstat(ticketPath);
225
- if (!ticketEntry.isFile() || ticketEntry.isSymbolicLink() || ticketEntry.size > POLICY_LOCK_MAX_BYTES)
226
- return undefined;
227
- if (process.platform !== "win32") {
228
- const uid = typeof process.getuid === "function" ? process.getuid() : undefined;
229
- if (uid === undefined || ticketEntry.uid !== uid || (ticketEntry.mode & 0o177) !== 0)
230
- return undefined;
231
- }
232
- const ticket = parseTicket(await readFile(ticketPath, "utf8"), owner.token);
233
- return ticket === undefined ? undefined : { owner, path, ticket };
234
- }
235
- catch (error) {
236
- return error.code === "ENOENT" ? { owner, path, ticket: null } : undefined;
237
- }
238
- }
239
- catch {
240
- return undefined;
241
- }
242
- }
243
- async function readClaims(basePath) {
244
- const directory = dirname(basePath);
245
- const prefix = `${basename(basePath)}.claim-`;
246
- let names;
247
- try {
248
- names = await readdir(directory);
249
- }
250
- catch {
251
- return undefined;
252
- }
253
- const claims = [];
254
- for (const name of names.filter((candidate) => candidate.startsWith(prefix))) {
255
- const path = join(directory, name);
256
- const claim = await readClaim(path);
257
- if (!claim) {
258
- try {
259
- await lstat(path);
260
- }
261
- catch (error) {
262
- if (error.code === "ENOENT")
263
- continue;
264
- }
265
- return undefined;
266
- }
267
- if (name !== `${prefix}${claim.owner.token}`)
268
- return undefined;
269
- claims.push(claim);
270
- }
271
- return claims;
272
- }
273
- async function ownerAlive(owner, budget) {
274
- try {
275
- process.kill(owner.pid, 0);
276
- }
277
- catch (error) {
278
- const code = error.code;
279
- if (code === "ESRCH")
280
- return false;
281
- if (code !== "EPERM")
282
- return undefined;
283
- }
284
- const current = await readProcessStartIdentity(owner.pid, process.platform, budget);
285
- return current === undefined ? undefined : processStartIdentitiesMatch(owner.startIdentity, current);
286
- }
287
- async function removeClaimOwnedBy(path, token, deadline = 0) {
288
- const movedPath = join(dirname(path), `.pi-agent-browser-policy-remove-${token}-${randomUUID()}`);
289
- while (true) {
290
- const current = await readClaim(path);
291
- if (current?.owner.token !== token)
292
- return false;
293
- try {
294
- await rename(path, movedPath);
295
- break;
296
- }
297
- catch (error) {
298
- const code = error.code;
299
- if (code === "ENOENT")
300
- return true;
301
- // Windows may refuse a directory move while a contender reads its
302
- // metadata. Revalidate before retrying. Owned-claim cleanup gets
303
- // its own bounded window, independent of the acquisition wait.
304
- if (process.platform !== "win32" || code !== "EPERM" || Date.now() >= deadline)
305
- return false;
306
- await waitForRetry();
307
- if (Date.now() >= deadline)
308
- return false;
309
- }
310
- }
311
- const moved = await readClaim(movedPath);
312
- if (moved?.owner.token !== token) {
313
- try {
314
- await rename(movedPath, path);
315
- }
316
- catch { }
317
- return false;
318
- }
319
- await rm(movedPath, { force: true, recursive: true });
320
- return true;
321
- }
322
- async function cleanDeadPolicyArtifacts(directory, budget) {
323
- let names;
324
- try {
325
- names = await readdir(directory);
326
- }
327
- catch {
328
- return;
329
- }
330
- // ponytail: list/sort all names; stream directories if enumeration becomes the bottleneck.
331
- const candidates = names.filter((name) => name.startsWith(".pi-agent-browser-policy-remove-")
332
- || /^\.pi-agent-browser-policy-[a-f0-9]{64}\.lock-v4\.(?:candidate|claim)-/.test(name)).sort();
333
- if (candidates.length === 0)
334
- return;
335
- const start = lastPolicyGcName === undefined ? randomInt(candidates.length)
336
- : Math.max(0, candidates.findIndex((name) => name > lastPolicyGcName));
337
- const batch = Array.from({ length: Math.min(POLICY_GC_BATCH_SIZE, candidates.length) }, (_, offset) => candidates[(start + offset) % candidates.length]);
338
- lastPolicyGcName = batch.at(-1);
339
- for (const name of batch) {
340
- if (budget.signal?.aborted || Date.now() >= (budget.deadline ?? Infinity))
341
- return;
342
- const path = join(directory, name);
343
- const published = name.includes(".lock-v4.claim-");
344
- if (published) {
345
- const entry = await lstat(path).catch(() => undefined);
346
- if (!entry || entry.mtimeMs >= Date.now() - STALE_CLAIM_MIN_AGE_MS)
347
- continue;
348
- }
349
- const claim = await readClaim(path);
350
- if (!claim || (published && !name.endsWith(`.claim-${claim.owner.token}`)))
351
- continue;
352
- if (await ownerAlive(claim.owner, budget) === false)
353
- await removeClaimOwnedBy(path, claim.owner.token).catch(() => false);
354
- }
355
- }
356
- function claimPrecedes(left, right) {
357
- if (left.ticket === null)
358
- return true;
359
- if (right.ticket === null)
360
- return false;
361
- return left.ticket < right.ticket || (left.ticket === right.ticket && left.owner.token < right.owner.token);
362
- }
363
- async function hasPublishedLaterTicket(claim, ownClaim) {
364
- if (claim.ticket !== null)
365
- return false;
366
- const current = await readClaim(claim.path);
367
- return current?.owner.token === claim.owner.token && !claimPrecedes(current, ownClaim);
368
- }
369
- function waitForRetry(signal) {
370
- return new Promise((resolve) => {
371
- if (signal?.aborted)
372
- return resolve();
373
- const timer = setTimeout(done, POLICY_LOCK_RETRY_MS);
374
- function done() {
375
- clearTimeout(timer);
376
- signal?.removeEventListener("abort", done);
377
- resolve();
378
- }
379
- signal?.addEventListener("abort", done, { once: true });
380
- });
127
+ return withBrowserExecutionLocks({
128
+ identities: [options.identity],
129
+ signal: options.signal,
130
+ deadline: options.deadline,
131
+ waitOnly: options.waitOnly,
132
+ }, run);
381
133
  }
382
134
  export async function acquireManagedSessionPolicyLock(options) {
383
- if (options.signal?.aborted)
135
+ if (options.signal?.aborted === true) {
384
136
  return undefined;
137
+ }
385
138
  let identity;
386
139
  try {
387
140
  identity = await resolveBrowserExecutionIdentity({ ...options, ownedManagedSession: true });
@@ -391,108 +144,22 @@ export async function acquireManagedSessionPolicyLock(options) {
391
144
  }
392
145
  const scope = executionScope.getStore();
393
146
  if (scope) {
394
- if (!scope.active || !scopeCovers(scope.identities, groupExecutionIdentities([identity])))
147
+ if (!scope.active || !scopeCovers(scope.identities, groupExecutionIdentities([identity]))) {
395
148
  return undefined;
396
- return { release: async () => undefined };
397
- }
398
- return acquireExecutionClaim({ identity: groupExecutionIdentities([identity])[0], signal: options.signal, timeoutMs: options.timeoutMs });
399
- }
400
- async function acquireExecutionClaim(options) {
401
- if (options.signal?.aborted)
402
- return undefined;
403
- const deadline = options.deadline ?? Date.now() + (options.timeoutMs ?? POLICY_LOCK_WAIT_MS);
404
- // timeoutMs: 0 retains the existing single-attempt policy probe contract.
405
- const budget = { signal: options.signal, deadline: options.timeoutMs === 0 ? undefined : deadline };
406
- const platform = process.platform;
407
- const directory = getCoordinationDirectory(platform);
408
- if (!await ensureCoordinationDirectory(directory, platform))
409
- return undefined;
410
- const basePath = getBrowserExecutionLockPath(options.identity);
411
- const token = randomUUID();
412
- const startIdentity = await readProcessStartIdentity(process.pid, process.platform, budget);
413
- if (!startIdentity)
414
- return undefined;
415
- const owner = { pid: process.pid, startIdentity, token, sessionNames: options.identity.sessionNames, version: 4 };
416
- const ownerContent = JSON.stringify(owner);
417
- // Never publish metadata our reader cannot validate and ownership-safely remove.
418
- if (Buffer.byteLength(ownerContent) > POLICY_LOCK_MAX_BYTES)
419
- return undefined;
420
- const candidatePath = `${basePath}.candidate-${token}`;
421
- const claimPath = `${basePath}.claim-${token}`;
422
- let claimPublished = false;
423
- let lockAcquired = false;
424
- try {
425
- await mkdir(candidatePath, { mode: 0o700 });
426
- await writeFile(join(candidatePath, LOCK_OWNER_FILE), ownerContent, { encoding: "utf8", flag: "wx", mode: 0o600 });
427
- await rename(candidatePath, claimPath);
428
- claimPublished = true;
429
- const initialClaims = await readClaims(basePath);
430
- if (!initialClaims)
431
- return undefined;
432
- const maxTicket = initialClaims.reduce((max, claim) => claim.ticket === null ? max : Math.max(max, claim.ticket), 0);
433
- if (!Number.isSafeInteger(maxTicket + 1))
434
- return undefined;
435
- const ticket = { ticket: maxTicket + 1, token, version: 4 };
436
- const ticketCandidatePath = join(claimPath, `.ticket-${token}.tmp`);
437
- await writeFile(ticketCandidatePath, JSON.stringify(ticket), { encoding: "utf8", flag: "wx", mode: 0o600 });
438
- await rename(ticketCandidatePath, join(claimPath, LOCK_TICKET_FILE));
439
- while (!options.signal?.aborted) {
440
- const claims = await readClaims(basePath);
441
- if (!claims)
442
- return undefined;
443
- const ownClaim = claims.find((claim) => claim.owner.token === token);
444
- if (!ownClaim || ownClaim.ticket !== ticket.ticket)
445
- return undefined;
446
- let blocked = false;
447
- for (const claim of claims) {
448
- const disjoint = owner.sessionNames !== null && claim.owner.sessionNames !== null
449
- && !owner.sessionNames.some(session => claim.owner.sessionNames.includes(session));
450
- if (claim.owner.token === token || disjoint || !claimPrecedes(claim, ownClaim))
451
- continue;
452
- // Choosing is transient: a later published ticket must not make
453
- // its predecessor wait on it using an earlier null snapshot.
454
- if (await hasPublishedLaterTicket(claim, ownClaim))
455
- continue;
456
- const alive = await ownerAlive(claim.owner, budget);
457
- if (alive === false) {
458
- await removeClaimOwnedBy(claim.path, claim.owner.token);
459
- continue;
460
- }
461
- // A native identity query can outlive the owner's entire critical
462
- // section. Its result (including unknown after exit) does not make
463
- // an already released immutable claim a current blocker.
464
- try {
465
- await lstat(claim.path);
466
- }
467
- catch (error) {
468
- if (error.code === "ENOENT")
469
- continue;
470
- }
471
- // Publication can also complete during the native identity query.
472
- if (await hasPublishedLaterTicket(claim, ownClaim))
473
- continue;
474
- blocked = true;
475
- break;
476
- }
477
- if (!blocked) {
478
- await cleanDeadPolicyArtifacts(directory, budget);
479
- if (options.signal?.aborted || (budget.deadline !== undefined && Date.now() >= budget.deadline))
480
- return undefined;
481
- lockAcquired = true;
482
- return { release: async () => { await removeClaimOwnedBy(claimPath, token, Date.now() + POLICY_LOCK_WAIT_MS); } };
483
- }
484
- if (Date.now() >= deadline)
485
- return undefined;
486
- await waitForRetry(options.signal);
487
149
  }
488
- return undefined;
150
+ return {
151
+ release: async () => {
152
+ /* The outer execution owns release of this borrowed claim. */
153
+ },
154
+ };
489
155
  }
490
- catch {
156
+ const grouped = groupExecutionIdentities([identity]).at(0);
157
+ if (grouped === undefined) {
491
158
  return undefined;
492
159
  }
493
- finally {
494
- await rm(candidatePath, { force: true, recursive: true }).catch(() => undefined);
495
- if (claimPublished && !lockAcquired)
496
- await removeClaimOwnedBy(claimPath, token, Date.now() + POLICY_LOCK_WAIT_MS);
497
- }
160
+ return acquireExecutionClaim({
161
+ identity: grouped,
162
+ signal: options.signal,
163
+ timeoutMs: options.timeoutMs,
164
+ });
498
165
  }
@@ -0,0 +1,58 @@
1
+ import { lstatSync } from "node:fs";
2
+ import { join, parse } from "node:path";
3
+ import { getCurrentProcessUid } from "./process-identity.js";
4
+ function androidSystemAncestor(path, entry, platform) {
5
+ return (platform === "android" &&
6
+ (path === "/data" || path === "/data/data") &&
7
+ entry.uid === 1000 &&
8
+ (entry.mode & 0o002) === 0);
9
+ }
10
+ function androidAppDirectory(entry, uid, platform) {
11
+ return (platform === "android" && entry.uid === uid && entry.gid === uid && (entry.mode & 0o002) === 0);
12
+ }
13
+ function ancestorModeTrusted(entry, android) {
14
+ const rootSticky = entry.uid === 0 && (entry.mode & 0o1000) !== 0;
15
+ return (entry.mode & 0o022) === 0 || rootSticky || android.system || android.app;
16
+ }
17
+ function ancestorTrusted(path, entry, uid, platform) {
18
+ if (entry.isSymbolicLink() || !entry.isDirectory()) {
19
+ return false;
20
+ }
21
+ const system = androidSystemAncestor(path, entry, platform);
22
+ if (!system && entry.uid !== 0 && entry.uid !== uid) {
23
+ return false;
24
+ }
25
+ return ancestorModeTrusted(entry, { system, app: androidAppDirectory(entry, uid, platform) });
26
+ }
27
+ export function isTrustedPosixDirectory(path, requireCurrentOwner, platform = process.platform) {
28
+ const uid = getCurrentProcessUid();
29
+ if (uid === undefined) {
30
+ return false;
31
+ }
32
+ const root = parse(path).root;
33
+ let cursor = root;
34
+ for (const component of path.slice(root.length).split("/").filter(Boolean)) {
35
+ cursor = join(cursor, component);
36
+ try {
37
+ if (!ancestorTrusted(cursor, lstatSync(cursor), uid, platform)) {
38
+ return false;
39
+ }
40
+ }
41
+ catch {
42
+ return false;
43
+ }
44
+ }
45
+ try {
46
+ return !requireCurrentOwner || lstatSync(path).uid === uid;
47
+ }
48
+ catch {
49
+ return false;
50
+ }
51
+ }
52
+ export function isTrustedCurrentFile(entry, platform, forbiddenMode) {
53
+ if (platform === "win32") {
54
+ return true;
55
+ }
56
+ const uid = getCurrentProcessUid();
57
+ return uid !== undefined && entry.uid === uid && (entry.mode & forbiddenMode) === 0;
58
+ }