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.
- package/CHANGELOG.md +106 -5
- package/README.md +58 -791
- package/dist/extensions/agent-browser/index.js +43101 -2031
- package/dist/extensions/agent-browser/lib/argv-descriptor.js +49 -26
- package/dist/extensions/agent-browser/lib/argv-grammar.js +82 -93
- package/dist/extensions/agent-browser/lib/bash-guard.js +129 -126
- package/dist/extensions/agent-browser/lib/batch-lifecycle.js +112 -51
- package/dist/extensions/agent-browser/lib/browser-execution-claim-codec.js +61 -0
- package/dist/extensions/agent-browser/lib/browser-execution-claim-storage.js +232 -0
- package/dist/extensions/agent-browser/lib/browser-execution-claims.js +202 -0
- package/dist/extensions/agent-browser/lib/browser-execution-identity.js +77 -0
- package/dist/extensions/agent-browser/lib/browser-journal-branch.js +98 -0
- package/dist/extensions/agent-browser/lib/browser-journal-contracts.js +1 -0
- package/dist/extensions/agent-browser/lib/browser-journal-projection.js +221 -0
- package/dist/extensions/agent-browser/lib/browser-journal-reader.js +89 -0
- package/dist/extensions/agent-browser/lib/browser-journal-replay.js +246 -0
- package/dist/extensions/agent-browser/lib/browser-journal.js +103 -394
- package/dist/extensions/agent-browser/lib/browser-legacy-pages.js +220 -0
- package/dist/extensions/agent-browser/lib/browser-legacy-projection.js +282 -0
- package/dist/extensions/agent-browser/lib/browser-legacy-types.js +1 -0
- package/dist/extensions/agent-browser/lib/browser-session-conversion-stream.js +217 -0
- package/dist/extensions/agent-browser/lib/browser-session-conversion.js +80 -335
- package/dist/extensions/agent-browser/lib/browser-transcript-contracts.js +35 -0
- package/dist/extensions/agent-browser/lib/browser-transcript-validation.js +166 -0
- package/dist/extensions/agent-browser/lib/browser-transcript.js +137 -82
- package/dist/extensions/agent-browser/lib/browser-value-parsing.js +15 -0
- package/dist/extensions/agent-browser/lib/command-capabilities.js +340 -0
- package/dist/extensions/agent-browser/lib/command-policy.js +107 -88
- package/dist/extensions/agent-browser/lib/command-taxonomy.js +49 -322
- package/dist/extensions/agent-browser/lib/config-policy.js +138 -343
- package/dist/extensions/agent-browser/lib/config-providers.js +71 -0
- package/dist/extensions/agent-browser/lib/config-validation.js +231 -0
- package/dist/extensions/agent-browser/lib/config.js +20 -8
- package/dist/extensions/agent-browser/lib/electron/cdp.js +20 -8
- package/dist/extensions/agent-browser/lib/electron/cleanup.js +128 -83
- package/dist/extensions/agent-browser/lib/electron/discovery-darwin.js +109 -0
- package/dist/extensions/agent-browser/lib/electron/discovery-desktop.js +136 -0
- package/dist/extensions/agent-browser/lib/electron/discovery-evidence.js +90 -0
- package/dist/extensions/agent-browser/lib/electron/discovery-linux.js +93 -0
- package/dist/extensions/agent-browser/lib/electron/discovery-packages.js +162 -0
- package/dist/extensions/agent-browser/lib/electron/discovery-types.js +2 -0
- package/dist/extensions/agent-browser/lib/electron/discovery.js +140 -549
- package/dist/extensions/agent-browser/lib/electron/launch-monitor.js +102 -0
- package/dist/extensions/agent-browser/lib/electron/launch-process.js +209 -0
- package/dist/extensions/agent-browser/lib/electron/launch-types.js +2 -0
- package/dist/extensions/agent-browser/lib/electron/launch.js +261 -355
- package/dist/extensions/agent-browser/lib/executable-path.js +21 -12
- package/dist/extensions/agent-browser/lib/execution-cwd.js +35 -18
- package/dist/extensions/agent-browser/lib/fs-utils.js +1 -9
- package/dist/extensions/agent-browser/lib/input-modes/electron.js +162 -121
- package/dist/extensions/agent-browser/lib/input-modes/job.js +123 -317
- package/dist/extensions/agent-browser/lib/input-modes/lookup-network-evidence.js +116 -0
- package/dist/extensions/agent-browser/lib/input-modes/lookup-plan.js +132 -0
- package/dist/extensions/agent-browser/lib/input-modes/lookup-redaction.js +57 -0
- package/dist/extensions/agent-browser/lib/input-modes/lookup-source-evidence.js +151 -0
- package/dist/extensions/agent-browser/lib/input-modes/lookup-workspace.js +142 -0
- package/dist/extensions/agent-browser/lib/input-modes/lookups.js +58 -412
- package/dist/extensions/agent-browser/lib/input-modes/params.js +86 -40
- package/dist/extensions/agent-browser/lib/input-modes/qa-analysis.js +268 -0
- package/dist/extensions/agent-browser/lib/input-modes/qa-presentation.js +117 -0
- package/dist/extensions/agent-browser/lib/input-modes/script-child.js +396 -0
- package/dist/extensions/agent-browser/lib/input-modes/script-protocol.js +86 -0
- package/dist/extensions/agent-browser/lib/input-modes/script-types.js +1 -0
- package/dist/extensions/agent-browser/lib/input-modes/script-validation.js +60 -0
- package/dist/extensions/agent-browser/lib/input-modes/script.js +60 -409
- package/dist/extensions/agent-browser/lib/input-modes/semantic-action.js +218 -128
- package/dist/extensions/agent-browser/lib/input-modes/shared.js +15 -4
- package/dist/extensions/agent-browser/lib/input-modes/types.js +56 -7
- package/dist/extensions/agent-browser/lib/json-schema.js +22 -10
- package/dist/extensions/agent-browser/lib/launch-scoped-flags.js +17 -5
- package/dist/extensions/agent-browser/lib/managed-session-policy-lock.js +92 -425
- package/dist/extensions/agent-browser/lib/managed-session-posix-storage.js +58 -0
- package/dist/extensions/agent-browser/lib/managed-session-project-identity.js +244 -0
- package/dist/extensions/agent-browser/lib/managed-session-restore-context.js +84 -0
- package/dist/extensions/agent-browser/lib/managed-session-restore-policy.js +119 -0
- package/dist/extensions/agent-browser/lib/managed-session-restore-state.js +83 -0
- package/dist/extensions/agent-browser/lib/managed-session-restore.js +168 -300
- package/dist/extensions/agent-browser/lib/managed-session-snapshot-records.js +277 -0
- package/dist/extensions/agent-browser/lib/managed-session-snapshots.js +128 -305
- package/dist/extensions/agent-browser/lib/managed-session-storage.js +56 -253
- package/dist/extensions/agent-browser/lib/native-confirmation.js +140 -0
- package/dist/extensions/agent-browser/lib/native-output.js +43 -9
- package/dist/extensions/agent-browser/lib/orchestration/batch-stdin.js +106 -59
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/artifact-merge.js +14 -6
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/artifact-paths.js +109 -57
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/browser-observation-result.js +82 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/click-dispatch.js +164 -60
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/diagnostic-values.js +31 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/diagnostics.js +152 -781
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/electron-observation.js +203 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/electron-text-diagnostics.js +109 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/fill-diagnostics.js +119 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result-action-recovery.js +147 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result-actions.js +144 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result-content.js +123 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result-contracts.js +1 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result-details.js +156 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result-electron.js +187 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result-evidence.js +39 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result-missing-binary.js +89 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result-recovery.js +97 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result-redaction.js +106 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result-timeout.js +110 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/final-result.js +41 -538
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/index.js +199 -154
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/interaction-diagnostics.js +290 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/managed-session-close.js +167 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/managed-session-daemon-inspection.js +108 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/managed-session-daemon-policy.js +127 -205
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/native-helper-observation.js +65 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/observation-types.js +1 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/overlay-diagnostics.js +120 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/network-page-filter.js +113 -30
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/scroll-shims.js +109 -28
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/snapshot-filter-data.js +150 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/snapshot-filter.js +147 -253
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/snapshot-rendered-text.js +147 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare/wait-timeouts.js +59 -24
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare-artifacts.js +159 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare-command.js +131 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare-contracts.js +1 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare-daemon.js +93 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare-failure.js +42 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare-guards.js +242 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare-input.js +75 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare-observations.js +92 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare-page-selection.js +254 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare-page-state.js +121 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare-refs.js +130 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare-resources.js +114 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare-semantic.js +121 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare-session-plan.js +222 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare-timeouts.js +68 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prepare.js +176 -1123
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-analysis.js +191 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-confirmation.js +152 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-diagnostics.js +182 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-electron.js +150 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-lifecycle-contracts.js +1 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-lifecycle-phase-contracts.js +1 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-lifecycle-phase.js +115 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-lifecycle.js +256 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-managed-contracts.js +1 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-managed.js +256 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-native-phase-contracts.js +1 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-native-phase.js +93 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-native.js +113 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-navigation-contracts.js +1 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-navigation.js +316 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-ownership.js +28 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-page-phase-contracts.js +1 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-page-phase.js +142 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-presentation-contracts.js +1 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-presentation.js +333 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-publication-phase-contracts.js +1 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-publication-phase.js +71 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-publication.js +201 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-receipts.js +183 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-refs.js +200 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output-tabs.js +142 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/process-output.js +18 -968
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/prompt-guards.js +23 -13
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/recording-receipt-reconciliation.js +198 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/recording-recovery-contracts.js +1 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/recording-recovery-presentation.js +154 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/recording-recovery.js +68 -151
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/screenshot-capture.js +14 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/screenshot-observation.js +169 -46
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-state-commands.js +130 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-state-electron.js +212 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-state-observations.js +61 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-state-outcomes.js +187 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-state-refs.js +169 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-state-tabs.js +228 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/session-state.js +77 -765
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/text-visibility-diagnostics.js +182 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/timeout-diagnostics.js +256 -0
- package/dist/extensions/agent-browser/lib/orchestration/browser-run/timeout-progress.js +108 -0
- package/dist/extensions/agent-browser/lib/orchestration/code-artifact-observation.js +39 -0
- package/dist/extensions/agent-browser/lib/orchestration/code-image-observation.js +119 -0
- package/dist/extensions/agent-browser/lib/orchestration/electron-host/branch.js +93 -0
- package/dist/extensions/agent-browser/lib/orchestration/electron-host/cleanup-result.js +58 -0
- package/dist/extensions/agent-browser/lib/orchestration/electron-host/cleanup.js +78 -0
- package/dist/extensions/agent-browser/lib/orchestration/electron-host/contracts.js +1 -0
- package/dist/extensions/agent-browser/lib/orchestration/electron-host/discovery.js +106 -0
- package/dist/extensions/agent-browser/lib/orchestration/electron-host/index.js +48 -821
- package/dist/extensions/agent-browser/lib/orchestration/electron-host/policy.js +86 -0
- package/dist/extensions/agent-browser/lib/orchestration/electron-host/probe-collection.js +68 -0
- package/dist/extensions/agent-browser/lib/orchestration/electron-host/probe-data.js +169 -0
- package/dist/extensions/agent-browser/lib/orchestration/electron-host/probe-presentation.js +182 -0
- package/dist/extensions/agent-browser/lib/orchestration/electron-host/probe.js +161 -0
- package/dist/extensions/agent-browser/lib/orchestration/electron-host/status.js +167 -0
- package/dist/extensions/agent-browser/lib/orchestration/execution-queue.js +22 -10
- package/dist/extensions/agent-browser/lib/orchestration/extension-artifact-preflight.js +211 -0
- package/dist/extensions/agent-browser/lib/orchestration/extension-artifacts.js +23 -0
- package/dist/extensions/agent-browser/lib/orchestration/extension-attachment-replay.js +99 -0
- package/dist/extensions/agent-browser/lib/orchestration/extension-branch-restore.js +275 -0
- package/dist/extensions/agent-browser/lib/orchestration/extension-code.js +280 -0
- package/dist/extensions/agent-browser/lib/orchestration/extension-command-admission.js +303 -0
- package/dist/extensions/agent-browser/lib/orchestration/extension-command-output.js +36 -0
- package/dist/extensions/agent-browser/lib/orchestration/extension-command-result.js +335 -0
- package/dist/extensions/agent-browser/lib/orchestration/extension-command-run.js +227 -0
- package/dist/extensions/agent-browser/lib/orchestration/extension-command-state.js +1 -0
- package/dist/extensions/agent-browser/lib/orchestration/extension-electron-ownership.js +204 -0
- package/dist/extensions/agent-browser/lib/orchestration/extension-execution-locks.js +139 -0
- package/dist/extensions/agent-browser/lib/orchestration/extension-execution-queue.js +24 -0
- package/dist/extensions/agent-browser/lib/orchestration/extension-host-coordination.js +98 -0
- package/dist/extensions/agent-browser/lib/orchestration/extension-host-run.js +295 -0
- package/dist/extensions/agent-browser/lib/orchestration/extension-intent-replay.js +41 -0
- package/dist/extensions/agent-browser/lib/orchestration/extension-invocation.js +7 -0
- package/dist/extensions/agent-browser/lib/orchestration/extension-lifecycle.js +102 -0
- package/dist/extensions/agent-browser/lib/orchestration/extension-managed-ownership.js +133 -0
- package/dist/extensions/agent-browser/lib/orchestration/extension-observation.js +120 -0
- package/dist/extensions/agent-browser/lib/orchestration/extension-prompt.js +180 -0
- package/dist/extensions/agent-browser/lib/orchestration/extension-recording.js +290 -0
- package/dist/extensions/agent-browser/lib/orchestration/extension-resource-contracts.js +1 -0
- package/dist/extensions/agent-browser/lib/orchestration/extension-resource-replay.js +269 -0
- package/dist/extensions/agent-browser/lib/orchestration/extension-result-state.js +113 -0
- package/dist/extensions/agent-browser/lib/orchestration/extension-runtime.js +146 -0
- package/dist/extensions/agent-browser/lib/orchestration/extension-script-leases.js +58 -0
- package/dist/extensions/agent-browser/lib/orchestration/extension-tool-boundary.js +62 -0
- package/dist/extensions/agent-browser/lib/orchestration/extension-version-check.js +54 -0
- package/dist/extensions/agent-browser/lib/orchestration/input-plan-types.js +1 -0
- package/dist/extensions/agent-browser/lib/orchestration/input-plan.js +261 -185
- package/dist/extensions/agent-browser/lib/orchestration/input-redaction.js +62 -0
- package/dist/extensions/agent-browser/lib/orchestration/native-config.js +144 -0
- package/dist/extensions/agent-browser/lib/orchestration/native-root-launch.js +77 -0
- package/dist/extensions/agent-browser/lib/orchestration/native-session-defaults.js +210 -157
- package/dist/extensions/agent-browser/lib/orchestration/operation-paths.js +124 -58
- package/dist/extensions/agent-browser/lib/orchestration/output-file.js +197 -99
- package/dist/extensions/agent-browser/lib/orchestration/script-mode.js +199 -96
- package/dist/extensions/agent-browser/lib/owned-managed-session-context.js +55 -0
- package/dist/extensions/agent-browser/lib/page-target-batch.js +118 -0
- package/dist/extensions/agent-browser/lib/page-target-guards.js +50 -0
- package/dist/extensions/agent-browser/lib/page-target-navigation.js +104 -0
- package/dist/extensions/agent-browser/lib/page-target-validation.js +55 -246
- package/dist/extensions/agent-browser/lib/parsing.js +13 -4
- package/dist/extensions/agent-browser/lib/pi-tool-rendering.js +133 -68
- package/dist/extensions/agent-browser/lib/playbook.js +16 -12
- package/dist/extensions/agent-browser/lib/process-child.js +284 -0
- package/dist/extensions/agent-browser/lib/process-environment.js +13 -6
- package/dist/extensions/agent-browser/lib/process-errors.js +23 -0
- package/dist/extensions/agent-browser/lib/process-identity.js +47 -15
- package/dist/extensions/agent-browser/lib/process-socket-storage.js +256 -0
- package/dist/extensions/agent-browser/lib/process-stdout.js +74 -0
- package/dist/extensions/agent-browser/lib/process.js +145 -536
- package/dist/extensions/agent-browser/lib/prompt-artifacts.js +287 -0
- package/dist/extensions/agent-browser/lib/prompt-policy.js +15 -194
- package/dist/extensions/agent-browser/lib/read-command.js +41 -0
- package/dist/extensions/agent-browser/lib/read-confirmation.js +131 -68
- package/dist/extensions/agent-browser/lib/recording-reservations.js +118 -45
- package/dist/extensions/agent-browser/lib/redaction-fields.js +75 -0
- package/dist/extensions/agent-browser/lib/redaction-json.js +149 -0
- package/dist/extensions/agent-browser/lib/redaction-url.js +88 -0
- package/dist/extensions/agent-browser/lib/results/action-contracts.js +1 -0
- package/dist/extensions/agent-browser/lib/results/action-recommendations.js +28 -271
- package/dist/extensions/agent-browser/lib/results/artifact-contracts.js +1 -0
- package/dist/extensions/agent-browser/lib/results/artifact-manifest-validation.js +133 -0
- package/dist/extensions/agent-browser/lib/results/artifact-manifest.js +96 -94
- package/dist/extensions/agent-browser/lib/results/artifact-recommendations.js +73 -0
- package/dist/extensions/agent-browser/lib/results/categories.js +111 -54
- package/dist/extensions/agent-browser/lib/results/confirmation.js +29 -13
- package/dist/extensions/agent-browser/lib/results/editable-ref-evidence.js +62 -27
- package/dist/extensions/agent-browser/lib/results/electron-recommendations.js +83 -0
- package/dist/extensions/agent-browser/lib/results/envelope-decoding.js +48 -0
- package/dist/extensions/agent-browser/lib/results/envelope.js +90 -67
- package/dist/extensions/agent-browser/lib/results/evidence-contracts.js +1 -0
- package/dist/extensions/agent-browser/lib/results/failure-recommendations.js +180 -0
- package/dist/extensions/agent-browser/lib/results/network-routes.js +86 -45
- package/dist/extensions/agent-browser/lib/results/network.js +35 -12
- package/dist/extensions/agent-browser/lib/results/next-actions.js +49 -19
- package/dist/extensions/agent-browser/lib/results/presentation/artifact-files.js +320 -0
- package/dist/extensions/agent-browser/lib/results/presentation/artifact-format.js +138 -0
- package/dist/extensions/agent-browser/lib/results/presentation/artifact-images.js +113 -0
- package/dist/extensions/agent-browser/lib/results/presentation/artifact-verification.js +145 -0
- package/dist/extensions/agent-browser/lib/results/presentation/artifacts.js +99 -555
- package/dist/extensions/agent-browser/lib/results/presentation/batch-contracts.js +1 -0
- package/dist/extensions/agent-browser/lib/results/presentation/batch-recording.js +118 -0
- package/dist/extensions/agent-browser/lib/results/presentation/batch-redaction.js +68 -0
- package/dist/extensions/agent-browser/lib/results/presentation/batch-rendering.js +135 -0
- package/dist/extensions/agent-browser/lib/results/presentation/batch-step.js +227 -0
- package/dist/extensions/agent-browser/lib/results/presentation/batch-summary.js +91 -0
- package/dist/extensions/agent-browser/lib/results/presentation/batch.js +91 -460
- package/dist/extensions/agent-browser/lib/results/presentation/browser-profile-recovery.js +9 -5
- package/dist/extensions/agent-browser/lib/results/presentation/command-presenters.js +216 -0
- package/dist/extensions/agent-browser/lib/results/presentation/common.js +26 -9
- package/dist/extensions/agent-browser/lib/results/presentation/content.js +86 -29
- package/dist/extensions/agent-browser/lib/results/presentation/diagnostic-a11y.js +67 -0
- package/dist/extensions/agent-browser/lib/results/presentation/diagnostic-health.js +79 -0
- package/dist/extensions/agent-browser/lib/results/presentation/diagnostic-identity.js +199 -0
- package/dist/extensions/agent-browser/lib/results/presentation/diagnostic-logs.js +75 -0
- package/dist/extensions/agent-browser/lib/results/presentation/diagnostic-network-actions.js +216 -0
- package/dist/extensions/agent-browser/lib/results/presentation/diagnostic-network.js +124 -0
- package/dist/extensions/agent-browser/lib/results/presentation/diagnostic-redaction.js +64 -0
- package/dist/extensions/agent-browser/lib/results/presentation/diagnostic-storage.js +209 -0
- package/dist/extensions/agent-browser/lib/results/presentation/diagnostic-stream.js +69 -0
- package/dist/extensions/agent-browser/lib/results/presentation/diagnostic-summary.js +222 -0
- package/dist/extensions/agent-browser/lib/results/presentation/diagnostics.js +103 -1052
- package/dist/extensions/agent-browser/lib/results/presentation/early-result.js +70 -0
- package/dist/extensions/agent-browser/lib/results/presentation/errors.js +130 -60
- package/dist/extensions/agent-browser/lib/results/presentation/input-contracts.js +1 -0
- package/dist/extensions/agent-browser/lib/results/presentation/large-output.js +60 -145
- package/dist/extensions/agent-browser/lib/results/presentation/navigation.js +147 -88
- package/dist/extensions/agent-browser/lib/results/presentation/notices.js +75 -0
- package/dist/extensions/agent-browser/lib/results/presentation/observation-contracts.js +1 -0
- package/dist/extensions/agent-browser/lib/results/presentation/observation-rendering.js +127 -0
- package/dist/extensions/agent-browser/lib/results/presentation/outcomes.js +120 -0
- package/dist/extensions/agent-browser/lib/results/presentation/output-completion.js +77 -0
- package/dist/extensions/agent-browser/lib/results/presentation/output-spill.js +51 -0
- package/dist/extensions/agent-browser/lib/results/presentation/registry.js +116 -240
- package/dist/extensions/agent-browser/lib/results/presentation/semantic-action.js +35 -21
- package/dist/extensions/agent-browser/lib/results/presentation/skills.js +109 -73
- package/dist/extensions/agent-browser/lib/results/presentation/source.js +109 -0
- package/dist/extensions/agent-browser/lib/results/presentation.js +17 -261
- package/dist/extensions/agent-browser/lib/results/recommendation-contracts.js +1 -0
- package/dist/extensions/agent-browser/lib/results/recording.js +52 -14
- package/dist/extensions/agent-browser/lib/results/recovery-actions.js +14 -5
- package/dist/extensions/agent-browser/lib/results/recovery-next-actions.js +30 -22
- package/dist/extensions/agent-browser/lib/results/selector-recovery-target.js +83 -0
- package/dist/extensions/agent-browser/lib/results/selector-recovery.js +119 -119
- package/dist/extensions/agent-browser/lib/results/snapshot-compact.js +266 -0
- package/dist/extensions/agent-browser/lib/results/snapshot-content.js +32 -0
- package/dist/extensions/agent-browser/lib/results/snapshot-high-value-controls.js +104 -71
- package/dist/extensions/agent-browser/lib/results/snapshot-refs.js +31 -11
- package/dist/extensions/agent-browser/lib/results/snapshot-segments.js +125 -66
- package/dist/extensions/agent-browser/lib/results/snapshot-spill.js +26 -7
- package/dist/extensions/agent-browser/lib/results/snapshot.js +28 -248
- package/dist/extensions/agent-browser/lib/results/text.js +16 -5
- package/dist/extensions/agent-browser/lib/runtime-args-validation.js +133 -0
- package/dist/extensions/agent-browser/lib/runtime-compatibility.js +81 -0
- package/dist/extensions/agent-browser/lib/runtime-contracts.js +1 -0
- package/dist/extensions/agent-browser/lib/runtime-redaction.js +118 -0
- package/dist/extensions/agent-browser/lib/runtime-replay-events.js +144 -0
- package/dist/extensions/agent-browser/lib/runtime-session-identity.js +76 -0
- package/dist/extensions/agent-browser/lib/runtime-session-plan.js +114 -0
- package/dist/extensions/agent-browser/lib/runtime-session-replay.js +123 -0
- package/dist/extensions/agent-browser/lib/runtime-tab-correction.js +65 -0
- package/dist/extensions/agent-browser/lib/runtime-timeouts.js +28 -0
- package/dist/extensions/agent-browser/lib/runtime.js +137 -1041
- package/dist/extensions/agent-browser/lib/session-identity.js +51 -0
- package/dist/extensions/agent-browser/lib/session-page-observation.js +357 -0
- package/dist/extensions/agent-browser/lib/session-page-state.js +178 -333
- package/dist/extensions/agent-browser/lib/session-page-types.js +1 -0
- package/dist/extensions/agent-browser/lib/temp-artifact-budget.js +81 -0
- package/dist/extensions/agent-browser/lib/temp-mutation-queue.js +7 -0
- package/dist/extensions/agent-browser/lib/temp-root-ownership.js +234 -0
- package/dist/extensions/agent-browser/lib/temp-root.js +211 -0
- package/dist/extensions/agent-browser/lib/temp.js +33 -446
- package/dist/extensions/agent-browser/lib/tool-surface.js +94 -24
- package/dist/extensions/agent-browser/lib/upstream-version.js +3 -2
- package/dist/extensions/agent-browser/lib/web-search-providers.js +193 -0
- package/dist/extensions/agent-browser/lib/web-search-request.js +102 -0
- package/dist/extensions/agent-browser/lib/web-search-results.js +281 -0
- package/dist/extensions/agent-browser/lib/web-search-schema.js +82 -0
- package/dist/extensions/agent-browser/lib/web-search.js +124 -614
- package/dist/extensions/agent-browser/lib/webmcp-observation.js +13 -6
- package/dist/extensions/agent-browser/lib/windows-stock-launcher.js +63 -37
- package/dist/extensions/agent-browser/script-worker.js +80 -43
- package/dist/scripts/agent-browser-target.mjs +4 -2
- package/docs/ARCHITECTURE.md +19 -6
- package/docs/CODE_QUALITY.md +267 -0
- package/docs/COMMAND_REFERENCE.md +272 -241
- package/docs/ELECTRON.md +77 -46
- package/docs/REFERENCE.md +869 -0
- package/docs/RELEASE.md +25 -22
- package/docs/REQUIREMENTS.md +2 -1
- package/docs/SUPPORT_MATRIX.md +81 -79
- package/docs/TOOL_CONTRACT.md +158 -125
- package/docs/platform-smoke.md +8 -8
- package/package.json +144 -120
- package/scripts/agent-browser-capability-baseline.mjs +361 -957
- package/scripts/agent-browser-target.mjs +6 -2
- package/scripts/build.mjs +37 -8
- package/scripts/capability-core-commands.mjs +193 -0
- package/scripts/capability-options.mjs +283 -0
- package/scripts/capability-samples.mjs +133 -0
- package/scripts/capability-section.mjs +11 -0
- package/scripts/config.mjs +235 -132
- package/scripts/convert-browser-session.mjs +30 -9
- package/scripts/doctor-sources.mjs +195 -0
- package/scripts/doctor.mjs +94 -249
- package/scripts/platform-smoke/artifacts.mjs +38 -11
- package/scripts/platform-smoke/commands.mjs +104 -0
- package/scripts/platform-smoke/crabbox-runner.mjs +167 -80
- package/scripts/platform-smoke/doctor-support.mjs +157 -0
- package/scripts/platform-smoke/doctor-windows.mjs +230 -0
- package/scripts/platform-smoke/doctor.mjs +212 -320
- package/scripts/platform-smoke/lease-evidence.mjs +132 -0
- package/scripts/platform-smoke/linux-image/Dockerfile +8 -2
- package/scripts/platform-smoke/suite-checks.mjs +98 -0
- package/scripts/platform-smoke/suite-evidence.mjs +281 -0
- package/scripts/platform-smoke/targets.mjs +154 -472
- package/scripts/platform-smoke.mjs +55 -40
- package/scripts/prepare.mjs +16 -7
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { readdir, rm, stat } from "node:fs/promises";
|
|
2
|
+
import { join } from "node:path";
|
|
3
|
+
import { parsePositiveInteger } from "./parsing.js";
|
|
4
|
+
const DEFAULT_MAX_BYTES = 32 * 1_024 * 1_024;
|
|
5
|
+
export function getTempArtifactByteLength(content) {
|
|
6
|
+
return typeof content === "string" ? Buffer.byteLength(content) : content.byteLength;
|
|
7
|
+
}
|
|
8
|
+
export async function listArtifactFiles(directory, excludedNames = new Set()) {
|
|
9
|
+
const entries = await readdir(directory, { withFileTypes: true }).catch(() => []);
|
|
10
|
+
const files = [];
|
|
11
|
+
for (const entry of entries) {
|
|
12
|
+
if (!entry.isFile() || excludedNames.has(entry.name)) {
|
|
13
|
+
continue;
|
|
14
|
+
}
|
|
15
|
+
const path = join(directory, entry.name);
|
|
16
|
+
// Bound filesystem work and retain the original ordered scan before eviction.
|
|
17
|
+
// oxlint-disable-next-line no-await-in-loop
|
|
18
|
+
const stats = await stat(path).catch(() => undefined);
|
|
19
|
+
if (stats?.isFile() === true) {
|
|
20
|
+
files.push({ mtimeMs: stats.mtimeMs, path, size: stats.size });
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
return files;
|
|
24
|
+
}
|
|
25
|
+
export function getSecureTempRootMaxBytes(env = process.env) {
|
|
26
|
+
return parsePositiveInteger(env.PI_AGENT_BROWSER_TEMP_ROOT_MAX_BYTES) ?? DEFAULT_MAX_BYTES;
|
|
27
|
+
}
|
|
28
|
+
export function getPersistentSessionArtifactMaxBytes(env = process.env) {
|
|
29
|
+
if (env.PI_AGENT_BROWSER_SESSION_ARTIFACT_MAX_BYTES?.trim() === "0") {
|
|
30
|
+
return 0;
|
|
31
|
+
}
|
|
32
|
+
return parsePositiveInteger(env.PI_AGENT_BROWSER_SESSION_ARTIFACT_MAX_BYTES) ?? DEFAULT_MAX_BYTES;
|
|
33
|
+
}
|
|
34
|
+
export async function assertSecureTempRootBudget(tempRoot, additionalBytes) {
|
|
35
|
+
if (additionalBytes <= 0) {
|
|
36
|
+
return;
|
|
37
|
+
}
|
|
38
|
+
const files = await listArtifactFiles(tempRoot, new Set([".pi-agent-browser-owner.json"]));
|
|
39
|
+
const currentBytes = files.reduce((total, file) => total + file.size, 0);
|
|
40
|
+
const maxBytes = getSecureTempRootMaxBytes();
|
|
41
|
+
const nextBytes = currentBytes + additionalBytes;
|
|
42
|
+
if (nextBytes > maxBytes) {
|
|
43
|
+
throw new Error(`pi-agent-browser temp spill budget exceeded (${nextBytes} bytes > ${maxBytes} byte limit).`);
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
export async function prunePersistentSessionArtifactsToBudget(sessionArtifactDir, additionalBytes, protectedPaths) {
|
|
47
|
+
if (additionalBytes <= 0) {
|
|
48
|
+
return [];
|
|
49
|
+
}
|
|
50
|
+
const maxBytes = getPersistentSessionArtifactMaxBytes();
|
|
51
|
+
if (maxBytes === 0) {
|
|
52
|
+
return [];
|
|
53
|
+
}
|
|
54
|
+
const files = await listArtifactFiles(sessionArtifactDir);
|
|
55
|
+
let totalBytes = files.reduce((total, file) => total + file.size, 0);
|
|
56
|
+
if (totalBytes + additionalBytes <= maxBytes) {
|
|
57
|
+
return [];
|
|
58
|
+
}
|
|
59
|
+
const evictedArtifacts = [];
|
|
60
|
+
files.sort((left, right) => {
|
|
61
|
+
const time = left.mtimeMs - right.mtimeMs;
|
|
62
|
+
return time === 0 ? left.path.localeCompare(right.path) : time;
|
|
63
|
+
});
|
|
64
|
+
for (const file of files) {
|
|
65
|
+
if (protectedPaths.has(file.path)) {
|
|
66
|
+
continue;
|
|
67
|
+
}
|
|
68
|
+
// Evict oldest first and stop immediately once the required budget is available.
|
|
69
|
+
// oxlint-disable-next-line no-await-in-loop
|
|
70
|
+
const removed = await rm(file.path, { force: true }).then(() => true, () => false);
|
|
71
|
+
if (!removed) {
|
|
72
|
+
continue;
|
|
73
|
+
}
|
|
74
|
+
evictedArtifacts.push({ mtimeMs: file.mtimeMs, path: file.path, sizeBytes: file.size });
|
|
75
|
+
totalBytes -= file.size;
|
|
76
|
+
if (totalBytes + additionalBytes <= maxBytes) {
|
|
77
|
+
return evictedArtifacts;
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
throw new Error(`pi-agent-browser persisted spill budget exceeded (${totalBytes + additionalBytes} bytes > ${maxBytes} byte limit).`);
|
|
81
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
let tempMutationQueue = Promise.resolve();
|
|
2
|
+
/** One queue owns all root cleanup, preservation, spill writes and persistent eviction. */
|
|
3
|
+
export function enqueueTempMutation(task) {
|
|
4
|
+
const nextTask = tempMutationQueue.then(task, task);
|
|
5
|
+
tempMutationQueue = nextTask.then(() => undefined, () => undefined);
|
|
6
|
+
return nextTask;
|
|
7
|
+
}
|
|
@@ -0,0 +1,234 @@
|
|
|
1
|
+
import { chmod, readFile, readdir, rm, stat, writeFile } from "node:fs/promises";
|
|
2
|
+
import { basename, dirname, join, resolve } from "node:path";
|
|
3
|
+
import { isRecord } from "./parsing.js";
|
|
4
|
+
import { processStartIdentitiesMatch, readProcessStartIdentity } from "./process-identity.js";
|
|
5
|
+
import { getErrorCode } from "./process-errors.js";
|
|
6
|
+
export const TEMP_ROOT_MARKER_FILE_NAME = ".pi-agent-browser-owner.json";
|
|
7
|
+
const TEMP_ROOT_MARKER_KIND = "pi-agent-browser-temp-root";
|
|
8
|
+
const TEMP_ROOT_MARKER_VERSION = 2;
|
|
9
|
+
export function getCurrentProcessUid() {
|
|
10
|
+
return typeof process.getuid === "function" ? process.getuid() : undefined;
|
|
11
|
+
}
|
|
12
|
+
function isPositiveFiniteNumber(value) {
|
|
13
|
+
return typeof value === "number" && Number.isFinite(value) && value > 0;
|
|
14
|
+
}
|
|
15
|
+
function isProtectedTempChildName(value) {
|
|
16
|
+
if (typeof value !== "string") {
|
|
17
|
+
return false;
|
|
18
|
+
}
|
|
19
|
+
if (value === "" || value === "." || value === ".." || value === TEMP_ROOT_MARKER_FILE_NAME) {
|
|
20
|
+
return false;
|
|
21
|
+
}
|
|
22
|
+
if (value.includes("/") || value.includes("\\")) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
return basename(value) === value;
|
|
26
|
+
}
|
|
27
|
+
function isTempRootOwnershipRecord(value) {
|
|
28
|
+
if (!isRecord(value)) {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
if (value.kind !== TEMP_ROOT_MARKER_KIND || value.version !== TEMP_ROOT_MARKER_VERSION) {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
if (!isPositiveFiniteNumber(value.createdAtMs)) {
|
|
35
|
+
return false;
|
|
36
|
+
}
|
|
37
|
+
return validLeaseAndProcess(value) && validOwnershipAndChildren(value);
|
|
38
|
+
}
|
|
39
|
+
function validLeaseAndProcess(value) {
|
|
40
|
+
if (value.leaseUpdatedAtMs !== undefined && !isPositiveFiniteNumber(value.leaseUpdatedAtMs)) {
|
|
41
|
+
return false;
|
|
42
|
+
}
|
|
43
|
+
if (value.ownerPid !== undefined &&
|
|
44
|
+
(typeof value.ownerPid !== "number" ||
|
|
45
|
+
!Number.isSafeInteger(value.ownerPid) ||
|
|
46
|
+
value.ownerPid <= 0)) {
|
|
47
|
+
return false;
|
|
48
|
+
}
|
|
49
|
+
return (value.ownerProcessStartIdentity === undefined ||
|
|
50
|
+
(typeof value.ownerProcessStartIdentity === "string" &&
|
|
51
|
+
value.ownerProcessStartIdentity.trim() !== ""));
|
|
52
|
+
}
|
|
53
|
+
function validOwnershipAndChildren(value) {
|
|
54
|
+
if (value.ownerUid !== undefined &&
|
|
55
|
+
(typeof value.ownerUid !== "number" ||
|
|
56
|
+
!Number.isSafeInteger(value.ownerUid) ||
|
|
57
|
+
value.ownerUid < 0)) {
|
|
58
|
+
return false;
|
|
59
|
+
}
|
|
60
|
+
if (value.protectedChildNames === undefined) {
|
|
61
|
+
return true;
|
|
62
|
+
}
|
|
63
|
+
if (!Array.isArray(value.protectedChildNames)) {
|
|
64
|
+
return false;
|
|
65
|
+
}
|
|
66
|
+
const names = value.protectedChildNames;
|
|
67
|
+
return names.every(isProtectedTempChildName);
|
|
68
|
+
}
|
|
69
|
+
export async function readTempRootOwnershipMarker(tempRoot) {
|
|
70
|
+
try {
|
|
71
|
+
const markerText = await readFile(join(tempRoot, TEMP_ROOT_MARKER_FILE_NAME), "utf8");
|
|
72
|
+
const parsed = JSON.parse(markerText);
|
|
73
|
+
return isTempRootOwnershipRecord(parsed) ? parsed : undefined;
|
|
74
|
+
}
|
|
75
|
+
catch {
|
|
76
|
+
return undefined;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
export function getProtectedTempChildName(tempRoot, childPath) {
|
|
80
|
+
const normalizedTempRoot = resolve(tempRoot);
|
|
81
|
+
const normalizedChildPath = resolve(childPath);
|
|
82
|
+
if (dirname(normalizedChildPath) !== normalizedTempRoot) {
|
|
83
|
+
return undefined;
|
|
84
|
+
}
|
|
85
|
+
const childName = basename(normalizedChildPath);
|
|
86
|
+
// A string can still be an unsafe child name; validation checks separators and reserved names.
|
|
87
|
+
// oxlint-disable-next-line typescript/no-unnecessary-condition
|
|
88
|
+
return isProtectedTempChildName(childName) ? childName : undefined;
|
|
89
|
+
}
|
|
90
|
+
function normalizeProtectedChildNames(names) {
|
|
91
|
+
return [...new Set([...names].filter(isProtectedTempChildName))].sort();
|
|
92
|
+
}
|
|
93
|
+
export function getPersistedProtectedChildPaths(tempRoot, ownershipMarker) {
|
|
94
|
+
const normalizedTempRoot = resolve(tempRoot);
|
|
95
|
+
return new Set((ownershipMarker?.protectedChildNames ?? []).map((childName) => resolve(join(normalizedTempRoot, childName))));
|
|
96
|
+
}
|
|
97
|
+
async function writeTempRootOwnershipMarkerRecord(tempRoot, markerRecord, options = {}) {
|
|
98
|
+
const markerPath = join(tempRoot, TEMP_ROOT_MARKER_FILE_NAME);
|
|
99
|
+
await writeFile(markerPath, JSON.stringify(markerRecord, null, 2), {
|
|
100
|
+
encoding: "utf8",
|
|
101
|
+
flag: options.flag,
|
|
102
|
+
mode: 0o600,
|
|
103
|
+
});
|
|
104
|
+
await chmod(markerPath, 0o600).catch(() => undefined);
|
|
105
|
+
return markerPath;
|
|
106
|
+
}
|
|
107
|
+
export async function persistProtectedTempChildren(tempRoot, protectedChildren) {
|
|
108
|
+
if (protectedChildren.size === 0) {
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
const ownershipMarker = await readTempRootOwnershipMarker(tempRoot);
|
|
112
|
+
if (!ownershipMarker) {
|
|
113
|
+
return;
|
|
114
|
+
}
|
|
115
|
+
const childNames = normalizeProtectedChildNames([
|
|
116
|
+
...(ownershipMarker.protectedChildNames ?? []),
|
|
117
|
+
...[...protectedChildren]
|
|
118
|
+
.map((path) => getProtectedTempChildName(tempRoot, path))
|
|
119
|
+
.filter((childName) => childName !== undefined),
|
|
120
|
+
]);
|
|
121
|
+
if (childNames.length === 0) {
|
|
122
|
+
return;
|
|
123
|
+
}
|
|
124
|
+
await writeTempRootOwnershipMarkerRecord(tempRoot, {
|
|
125
|
+
...ownershipMarker,
|
|
126
|
+
leaseUpdatedAtMs: Date.now(),
|
|
127
|
+
protectedChildNames: childNames,
|
|
128
|
+
version: TEMP_ROOT_MARKER_VERSION,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
export async function getExistingProtectedChildren(tempRoot, protectedChildren) {
|
|
132
|
+
const normalizedTempRoot = resolve(tempRoot);
|
|
133
|
+
const existingChildren = new Set();
|
|
134
|
+
for (const path of protectedChildren) {
|
|
135
|
+
const normalizedPath = resolve(path);
|
|
136
|
+
if (dirname(normalizedPath) !== normalizedTempRoot) {
|
|
137
|
+
continue;
|
|
138
|
+
}
|
|
139
|
+
if (
|
|
140
|
+
// Scan in bounded sequence instead of opening an unbounded number of filesystem requests.
|
|
141
|
+
// oxlint-disable-next-line no-await-in-loop
|
|
142
|
+
await stat(normalizedPath).then((stats) => stats.isDirectory(), () => false)) {
|
|
143
|
+
existingChildren.add(normalizedPath);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
return existingChildren;
|
|
147
|
+
}
|
|
148
|
+
export async function removeTempRootChildrenExcept(tempRoot, protectedChildren) {
|
|
149
|
+
const entries = await readdir(tempRoot, { withFileTypes: true }).catch(() => []);
|
|
150
|
+
await Promise.all(entries.map(async (entry) => {
|
|
151
|
+
if (entry.name === TEMP_ROOT_MARKER_FILE_NAME) {
|
|
152
|
+
return;
|
|
153
|
+
}
|
|
154
|
+
const entryPath = join(tempRoot, entry.name);
|
|
155
|
+
if (protectedChildren.has(resolve(entryPath))) {
|
|
156
|
+
return;
|
|
157
|
+
}
|
|
158
|
+
await rm(entryPath, { force: true, recursive: true }).catch(() => undefined);
|
|
159
|
+
}));
|
|
160
|
+
}
|
|
161
|
+
async function getProcessStartIdentity(pid) {
|
|
162
|
+
return pid === undefined ? undefined : await readProcessStartIdentity(pid);
|
|
163
|
+
}
|
|
164
|
+
export async function writeSecureTempRootOwnershipMarker(tempRoot, options = {}) {
|
|
165
|
+
const createdAtMs = options.createdAtMs ?? Date.now();
|
|
166
|
+
const ownerPid = options.ownerPid ?? process.pid;
|
|
167
|
+
const markerRecord = {
|
|
168
|
+
createdAtMs,
|
|
169
|
+
kind: TEMP_ROOT_MARKER_KIND,
|
|
170
|
+
leaseUpdatedAtMs: options.leaseUpdatedAtMs ?? createdAtMs,
|
|
171
|
+
ownerPid,
|
|
172
|
+
ownerProcessStartIdentity: options.ownerProcessStartIdentity ?? (await getProcessStartIdentity(ownerPid)),
|
|
173
|
+
ownerUid: getCurrentProcessUid(),
|
|
174
|
+
version: TEMP_ROOT_MARKER_VERSION,
|
|
175
|
+
};
|
|
176
|
+
return await writeTempRootOwnershipMarkerRecord(tempRoot, markerRecord, { flag: "wx" });
|
|
177
|
+
}
|
|
178
|
+
export async function refreshSecureTempRootLease(tempRoot) {
|
|
179
|
+
const ownershipMarker = await readTempRootOwnershipMarker(tempRoot);
|
|
180
|
+
if (!ownershipMarker) {
|
|
181
|
+
return;
|
|
182
|
+
}
|
|
183
|
+
if (ownershipMarker.ownerPid !== process.pid) {
|
|
184
|
+
return;
|
|
185
|
+
}
|
|
186
|
+
const currentUid = getCurrentProcessUid();
|
|
187
|
+
if (currentUid !== undefined &&
|
|
188
|
+
ownershipMarker.ownerUid !== undefined &&
|
|
189
|
+
ownershipMarker.ownerUid !== currentUid) {
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
const currentProcessStartIdentity = await getProcessStartIdentity(process.pid);
|
|
193
|
+
if (ownershipMarker.ownerProcessStartIdentity !== undefined &&
|
|
194
|
+
currentProcessStartIdentity !== undefined) {
|
|
195
|
+
const identitiesMatch = processStartIdentitiesMatch(ownershipMarker.ownerProcessStartIdentity, currentProcessStartIdentity);
|
|
196
|
+
if (!identitiesMatch) {
|
|
197
|
+
return;
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
const refreshedMarker = {
|
|
201
|
+
...ownershipMarker,
|
|
202
|
+
leaseUpdatedAtMs: Date.now(),
|
|
203
|
+
ownerPid: process.pid,
|
|
204
|
+
ownerProcessStartIdentity: currentProcessStartIdentity ?? ownershipMarker.ownerProcessStartIdentity,
|
|
205
|
+
ownerUid: currentUid,
|
|
206
|
+
version: TEMP_ROOT_MARKER_VERSION,
|
|
207
|
+
};
|
|
208
|
+
await writeTempRootOwnershipMarkerRecord(tempRoot, refreshedMarker);
|
|
209
|
+
}
|
|
210
|
+
export async function getMarkerOwnerLiveness(ownershipMarker) {
|
|
211
|
+
const pid = ownershipMarker.ownerPid;
|
|
212
|
+
if (pid === undefined) {
|
|
213
|
+
return "unknown";
|
|
214
|
+
}
|
|
215
|
+
try {
|
|
216
|
+
process.kill(pid, 0);
|
|
217
|
+
}
|
|
218
|
+
catch (error) {
|
|
219
|
+
const code = getErrorCode(error);
|
|
220
|
+
if (code === "ESRCH") {
|
|
221
|
+
return "dead";
|
|
222
|
+
}
|
|
223
|
+
if (code !== "EPERM") {
|
|
224
|
+
return "unknown";
|
|
225
|
+
}
|
|
226
|
+
}
|
|
227
|
+
const currentProcessStartIdentity = await getProcessStartIdentity(pid);
|
|
228
|
+
if (ownershipMarker.ownerProcessStartIdentity === undefined ||
|
|
229
|
+
currentProcessStartIdentity === undefined) {
|
|
230
|
+
return "unknown";
|
|
231
|
+
}
|
|
232
|
+
const identitiesMatch = processStartIdentitiesMatch(ownershipMarker.ownerProcessStartIdentity, currentProcessStartIdentity);
|
|
233
|
+
return identitiesMatch ? "alive" : "dead";
|
|
234
|
+
}
|
|
@@ -0,0 +1,211 @@
|
|
|
1
|
+
import { randomInt } from "node:crypto";
|
|
2
|
+
import { existsSync, readdirSync, rmSync } from "node:fs";
|
|
3
|
+
import { chmod, mkdtemp, readdir, rm, stat } from "node:fs/promises";
|
|
4
|
+
import { tmpdir } from "node:os";
|
|
5
|
+
import { dirname, join, resolve } from "node:path";
|
|
6
|
+
import { enqueueTempMutation } from "./temp-mutation-queue.js";
|
|
7
|
+
import { TEMP_ROOT_MARKER_FILE_NAME, getCurrentProcessUid, readTempRootOwnershipMarker, getProtectedTempChildName, getPersistedProtectedChildPaths, persistProtectedTempChildren, getExistingProtectedChildren, removeTempRootChildrenExcept, getMarkerOwnerLiveness, refreshSecureTempRootLease, writeSecureTempRootOwnershipMarker, } from "./temp-root-ownership.js";
|
|
8
|
+
export { refreshSecureTempRootLease, writeSecureTempRootOwnershipMarker, } from "./temp-root-ownership.js";
|
|
9
|
+
const TEMP_ROOT_PREFIX = "pi-agent-browser-";
|
|
10
|
+
const STALE_TEMP_ROOT_MAX_AGE_MS = 24 * 60 * 60 * 1_000;
|
|
11
|
+
const STALE_TEMP_ROOT_BATCH_SIZE = 8;
|
|
12
|
+
let lastTempGcName;
|
|
13
|
+
let sessionTempRootPromise;
|
|
14
|
+
let exitCleanupRegistered = false;
|
|
15
|
+
const ownedTempRoots = new Set();
|
|
16
|
+
const protectedTempChildren = new Set();
|
|
17
|
+
function markerMatchesUid(marker, uid) {
|
|
18
|
+
return uid === undefined || marker.ownerUid === undefined || marker.ownerUid === uid;
|
|
19
|
+
}
|
|
20
|
+
async function pruneTempRoot(path, currentUid, cutoffTime) {
|
|
21
|
+
const marker = await readTempRootOwnershipMarker(path);
|
|
22
|
+
if (!marker || !markerMatchesUid(marker, currentUid)) {
|
|
23
|
+
return;
|
|
24
|
+
}
|
|
25
|
+
if ((marker.leaseUpdatedAtMs ?? marker.createdAtMs) >= cutoffTime) {
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
// Preserve ambiguous owner liveness; never delete another live process's files.
|
|
29
|
+
if ((await getMarkerOwnerLiveness(marker)) !== "dead") {
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
const stats = await stat(path).catch(() => undefined);
|
|
33
|
+
if (stats?.isDirectory() !== true) {
|
|
34
|
+
return;
|
|
35
|
+
}
|
|
36
|
+
const protectedChildren = await getExistingProtectedChildren(path, getPersistedProtectedChildPaths(path, marker));
|
|
37
|
+
if (protectedChildren.size > 0) {
|
|
38
|
+
await removeTempRootChildrenExcept(path, protectedChildren);
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
await rm(path, { force: true, recursive: true }).catch(() => {
|
|
42
|
+
/* Stale-root cleanup is best-effort. */
|
|
43
|
+
});
|
|
44
|
+
}
|
|
45
|
+
async function pruneStaleTempRoots(currentTempRoot) {
|
|
46
|
+
const entries = await readdir(tmpdir(), { withFileTypes: true }).catch(() => []);
|
|
47
|
+
const cutoffTime = Date.now() - STALE_TEMP_ROOT_MAX_AGE_MS;
|
|
48
|
+
const currentUid = getCurrentProcessUid();
|
|
49
|
+
// ponytail: list/sort all names; stream directories if enumeration becomes the bottleneck.
|
|
50
|
+
const candidates = entries
|
|
51
|
+
.filter((entry) => entry.isDirectory() && entry.name.startsWith(TEMP_ROOT_PREFIX))
|
|
52
|
+
.map((entry) => entry.name)
|
|
53
|
+
.sort();
|
|
54
|
+
if (candidates.length === 0) {
|
|
55
|
+
return;
|
|
56
|
+
}
|
|
57
|
+
const previousName = lastTempGcName;
|
|
58
|
+
const start = previousName === undefined
|
|
59
|
+
? randomInt(candidates.length)
|
|
60
|
+
: Math.max(0, candidates.findIndex((name) => name > previousName));
|
|
61
|
+
const batch = Array.from({ length: Math.min(STALE_TEMP_ROOT_BATCH_SIZE, candidates.length) }, (_, offset) => candidates[(start + offset) % candidates.length]);
|
|
62
|
+
lastTempGcName = batch.at(-1);
|
|
63
|
+
await Promise.all(batch.map(async (name) => {
|
|
64
|
+
const path = join(tmpdir(), name);
|
|
65
|
+
if (path !== currentTempRoot) {
|
|
66
|
+
await pruneTempRoot(path, currentUid, cutoffTime);
|
|
67
|
+
}
|
|
68
|
+
}));
|
|
69
|
+
}
|
|
70
|
+
function getProtectedChildrenForRoot(tempRoot) {
|
|
71
|
+
const normalizedTempRoot = resolve(tempRoot);
|
|
72
|
+
return new Set([...protectedTempChildren].filter((path) => dirname(path) === normalizedTempRoot && existsSync(path)));
|
|
73
|
+
}
|
|
74
|
+
function removeTempRootChildrenExceptSync(tempRoot, protectedChildren) {
|
|
75
|
+
for (const entry of readdirSync(tempRoot, { withFileTypes: true })) {
|
|
76
|
+
if (entry.name === TEMP_ROOT_MARKER_FILE_NAME) {
|
|
77
|
+
continue;
|
|
78
|
+
}
|
|
79
|
+
const entryPath = join(tempRoot, entry.name);
|
|
80
|
+
if (protectedChildren.has(resolve(entryPath))) {
|
|
81
|
+
continue;
|
|
82
|
+
}
|
|
83
|
+
rmSync(entryPath, { force: true, recursive: true });
|
|
84
|
+
}
|
|
85
|
+
}
|
|
86
|
+
function registerExitCleanup() {
|
|
87
|
+
if (exitCleanupRegistered) {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
exitCleanupRegistered = true;
|
|
91
|
+
process.once("exit", () => {
|
|
92
|
+
for (const tempRoot of ownedTempRoots) {
|
|
93
|
+
try {
|
|
94
|
+
const protectedChildren = getProtectedChildrenForRoot(tempRoot);
|
|
95
|
+
if (protectedChildren.size === 0) {
|
|
96
|
+
rmSync(tempRoot, { force: true, recursive: true });
|
|
97
|
+
}
|
|
98
|
+
else {
|
|
99
|
+
removeTempRootChildrenExceptSync(tempRoot, protectedChildren);
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
catch {
|
|
103
|
+
/* Best-effort process-exit cleanup. */
|
|
104
|
+
}
|
|
105
|
+
}
|
|
106
|
+
});
|
|
107
|
+
}
|
|
108
|
+
export async function preserveSecureTempDirectory(path) {
|
|
109
|
+
await enqueueTempMutation(async () => {
|
|
110
|
+
const childPath = resolve(path);
|
|
111
|
+
const tempRoot = dirname(childPath);
|
|
112
|
+
if (!ownedTempRoots.has(tempRoot) ||
|
|
113
|
+
getProtectedTempChildName(tempRoot, childPath) === undefined ||
|
|
114
|
+
!(await stat(childPath)).isDirectory()) {
|
|
115
|
+
throw new Error(`Cannot preserve ${path}; expected an existing child directory of a currently owned temp root.`);
|
|
116
|
+
}
|
|
117
|
+
protectedTempChildren.add(childPath);
|
|
118
|
+
await persistProtectedTempChildren(tempRoot, new Set([childPath]));
|
|
119
|
+
if (!getPersistedProtectedChildPaths(tempRoot, await readTempRootOwnershipMarker(tempRoot)).has(childPath)) {
|
|
120
|
+
throw new Error(`Could not persist temp directory preservation for ${path}.`);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
function rememberPreservedPaths(tempRoot, paths) {
|
|
125
|
+
for (const path of paths) {
|
|
126
|
+
const childName = getProtectedTempChildName(tempRoot, path);
|
|
127
|
+
if (childName !== undefined && childName.length > 0) {
|
|
128
|
+
protectedTempChildren.add(resolve(join(tempRoot, childName)));
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
function forgetMissingPreservedPaths(tempRoot, preservedChildren) {
|
|
133
|
+
for (const path of protectedTempChildren) {
|
|
134
|
+
if (dirname(path) === tempRoot && !preservedChildren.has(path)) {
|
|
135
|
+
protectedTempChildren.delete(path);
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
export async function cleanupSecureTempArtifacts(options = {}) {
|
|
140
|
+
await enqueueTempMutation(async () => {
|
|
141
|
+
const tempRoot = await sessionTempRootPromise?.catch(() => undefined);
|
|
142
|
+
if (tempRoot === undefined || tempRoot.length === 0) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
const normalizedTempRoot = resolve(tempRoot);
|
|
146
|
+
rememberPreservedPaths(normalizedTempRoot, options.preservePaths ?? []);
|
|
147
|
+
const preservedChildren = await getExistingProtectedChildren(normalizedTempRoot, protectedTempChildren);
|
|
148
|
+
forgetMissingPreservedPaths(normalizedTempRoot, preservedChildren);
|
|
149
|
+
if (preservedChildren.size === 0) {
|
|
150
|
+
sessionTempRootPromise = undefined;
|
|
151
|
+
ownedTempRoots.delete(tempRoot);
|
|
152
|
+
await rm(tempRoot, { force: true, recursive: true }).catch(() => {
|
|
153
|
+
/* Cleanup failure cannot revive retired ownership. */
|
|
154
|
+
});
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
await persistProtectedTempChildren(tempRoot, preservedChildren);
|
|
158
|
+
await removeTempRootChildrenExcept(tempRoot, preservedChildren);
|
|
159
|
+
await refreshSecureTempRootLease(tempRoot).catch(() => {
|
|
160
|
+
/* Cleanup retains existing ownership evidence on failed renewal. */
|
|
161
|
+
});
|
|
162
|
+
});
|
|
163
|
+
}
|
|
164
|
+
async function allocateSessionTempRoot() {
|
|
165
|
+
const tempRoot = await mkdtemp(join(tmpdir(), TEMP_ROOT_PREFIX));
|
|
166
|
+
try {
|
|
167
|
+
await chmod(tempRoot, 0o700).catch(() => {
|
|
168
|
+
/* mkdtemp already creates a private root. */
|
|
169
|
+
});
|
|
170
|
+
await writeSecureTempRootOwnershipMarker(tempRoot);
|
|
171
|
+
ownedTempRoots.add(tempRoot);
|
|
172
|
+
registerExitCleanup();
|
|
173
|
+
return tempRoot;
|
|
174
|
+
}
|
|
175
|
+
catch (error) {
|
|
176
|
+
await rm(tempRoot, { force: true, recursive: true }).catch(() => {
|
|
177
|
+
/* Preserve the allocation failure if private-root cleanup also fails. */
|
|
178
|
+
});
|
|
179
|
+
throw error;
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
export async function getSessionTempRoot() {
|
|
183
|
+
sessionTempRootPromise ??= allocateSessionTempRoot().catch((error) => {
|
|
184
|
+
sessionTempRootPromise = undefined;
|
|
185
|
+
throw error;
|
|
186
|
+
});
|
|
187
|
+
const tempRoot = await sessionTempRootPromise;
|
|
188
|
+
await refreshSecureTempRootLease(tempRoot).catch(() => {
|
|
189
|
+
/* A failed refresh leaves the existing marker. */
|
|
190
|
+
});
|
|
191
|
+
await pruneStaleTempRoots(tempRoot).catch(() => {
|
|
192
|
+
/* GC cannot prevent using the owned root. */
|
|
193
|
+
});
|
|
194
|
+
return tempRoot;
|
|
195
|
+
}
|
|
196
|
+
export async function getSecureTempChildDirectoryValidationError(path, childPrefix) {
|
|
197
|
+
const parentDirectory = dirname(path);
|
|
198
|
+
const childName = path.slice(parentDirectory.length + 1);
|
|
199
|
+
if (!childName.startsWith(childPrefix)) {
|
|
200
|
+
return `Refusing to remove ${path}; expected wrapper temp child prefix ${childPrefix}.`;
|
|
201
|
+
}
|
|
202
|
+
const marker = await readTempRootOwnershipMarker(parentDirectory);
|
|
203
|
+
if (!marker) {
|
|
204
|
+
return `Refusing to remove ${path}; parent directory is not a pi-agent-browser owned temp root.`;
|
|
205
|
+
}
|
|
206
|
+
const currentUid = getCurrentProcessUid();
|
|
207
|
+
if (!markerMatchesUid(marker, currentUid)) {
|
|
208
|
+
return `Refusing to remove ${path}; parent temp root is owned by uid ${marker.ownerUid ?? "unknown"}, not current uid ${currentUid ?? "unknown"}.`;
|
|
209
|
+
}
|
|
210
|
+
return undefined;
|
|
211
|
+
}
|