@reticlehq/server 2.13.0 → 2.14.0
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/SKILL.md +8 -18
- package/bin/reticle.js +3 -0
- package/dist/bridge/auth-failure-reason.js +12 -1
- package/dist/bridge/bridge-security.d.ts +8 -0
- package/dist/bridge/bridge-security.js +6 -1
- package/dist/bridge/bridge.d.ts +3 -0
- package/dist/bridge/bridge.js +23 -1
- package/dist/bridge/bridge.test-harness.d.ts +10 -1
- package/dist/bridge/bridge.test-harness.js +7 -1
- package/dist/bridge/pairing-token.d.ts +17 -0
- package/dist/bridge/pairing-token.js +26 -0
- package/dist/bridge/token-auth.d.ts +7 -0
- package/dist/bridge/token-auth.js +7 -1
- package/dist/capsule/capsule-store.d.ts +4 -4
- package/dist/capsule/capsule.d.ts +1 -1
- package/dist/capsule/capsule.js +3 -3
- package/dist/capsule/causal-summary.d.ts +21 -1
- package/dist/capsule/causal-summary.js +8 -6
- package/dist/capsule/divergence.d.ts +1 -1
- package/dist/capsule/divergence.js +26 -10
- package/dist/chromium-launch-options.d.ts +2 -1
- package/dist/cli/adhoc-verdict.d.ts +58 -0
- package/dist/cli/adhoc-verdict.js +115 -0
- package/dist/cli/chromium-hint.d.ts +18 -4
- package/dist/cli/chromium-hint.js +54 -6
- package/dist/cli/cli-doctor.js +1 -2
- package/dist/cli/cli-flow-commands.d.ts +16 -2
- package/dist/cli/cli-flow-commands.js +28 -9
- package/dist/cli/cli-parse-feedback.js +1 -1
- package/dist/cli/cli-parse.d.ts +2 -1
- package/dist/cli/cli-parse.js +38 -0
- package/dist/cli/cli-port.d.ts +5 -13
- package/dist/cli/cli-port.js +5 -38
- package/dist/cli/cli-update-commands.js +4 -4
- package/dist/cli/cli-verify.d.ts +24 -7
- package/dist/cli/cli-verify.js +87 -13
- package/dist/cli/cloud-keystore.d.ts +2 -1
- package/dist/cli/cloud-login.d.ts +2 -1
- package/dist/cli/cloud-session.d.ts +2 -1
- package/dist/cli/cloud-session.js +1 -1
- package/dist/cli/daemon-start-options.d.ts +2 -1
- package/dist/cli/device-flow.d.ts +2 -2
- package/dist/cli/doctor-daemon-line.d.ts +2 -1
- package/dist/cli/doctor-project-line.d.ts +2 -1
- package/dist/cli/doctor-rows.d.ts +2 -1
- package/dist/cli/drive-attach.d.ts +4 -3
- package/dist/cli/gate-hook-message.d.ts +2 -1
- package/dist/cli/status-next-action.js +10 -11
- package/dist/cli.d.ts +1 -0
- package/dist/cli.js +21 -9
- package/dist/cloud/account-state.d.ts +20 -0
- package/dist/cloud/account-state.js +65 -0
- package/dist/cloud/cloud-sync.d.ts +46 -3
- package/dist/cloud/cloud-sync.js +116 -0
- package/dist/cloud/sync-cycle.d.ts +5 -4
- package/dist/cloud/sync-cycle.js +1 -1
- package/dist/cloud/sync-daemon.d.ts +3 -19
- package/dist/cloud/sync-daemon.js +2 -2
- package/dist/crawl/crawl-tools.js +1 -1
- package/dist/crawl/crawl.d.ts +5 -5
- package/dist/crawl/crawl.js +138 -4
- package/dist/daemon/daemon-resilience.d.ts +3 -2
- package/dist/daemon/daemon-resilience.js +43 -4
- package/dist/daemon/daemon-resolve.d.ts +0 -9
- package/dist/daemon/daemon-resolve.js +2 -2
- package/dist/daemon/dev-servers.d.ts +1 -1
- package/dist/daemon/dev-servers.js +1 -1
- package/dist/daemon/loopback-alias.d.ts +2 -1
- package/dist/daemon/startup-failure.d.ts +11 -0
- package/dist/daemon/startup-failure.js +46 -0
- package/dist/events/contradictions.d.ts +10 -16
- package/dist/events/contradictions.js +149 -17
- package/dist/events/declared.d.ts +13 -1
- package/dist/events/declared.js +59 -3
- package/dist/events/echo-mismatch.js +75 -6
- package/dist/events/lineage.d.ts +52 -0
- package/dist/events/lineage.js +122 -0
- package/dist/events/net-evidence.d.ts +21 -0
- package/dist/events/net-evidence.js +81 -0
- package/dist/events/predicate-console.d.ts +13 -0
- package/dist/events/predicate-console.js +138 -0
- package/dist/events/predicate-element.d.ts +13 -0
- package/dist/events/predicate-element.js +214 -0
- package/dist/events/predicate-eval.d.ts +15 -3
- package/dist/events/predicate-eval.js +128 -110
- package/dist/events/predicate-parse.js +34 -1
- package/dist/events/predicate-request-body.d.ts +50 -0
- package/dist/events/predicate-request-body.js +120 -0
- package/dist/events/predicate-route.d.ts +21 -1
- package/dist/events/predicate-route.js +149 -21
- package/dist/events/predicate-schema.d.ts +8 -1
- package/dist/events/predicate-schema.js +34 -0
- package/dist/events/predicate.d.ts +2 -0
- package/dist/events/predicate.js +52 -159
- package/dist/events/split-text-miss.d.ts +6 -4
- package/dist/events/split-text-miss.js +12 -5
- package/dist/flows/affected-tools.js +22 -9
- package/dist/flows/annotate.d.ts +2 -1
- package/dist/flows/annotate.js +19 -1
- package/dist/flows/coverage.d.ts +2 -2
- package/dist/flows/flake.d.ts +3 -3
- package/dist/flows/flow-expect-grammar.d.ts +33 -0
- package/dist/flows/flow-expect-grammar.js +155 -0
- package/dist/flows/flow-replay-run.d.ts +1 -1
- package/dist/flows/flow-replay-run.js +28 -15
- package/dist/flows/flow-replay.d.ts +10 -0
- package/dist/flows/flow-replay.js +57 -22
- package/dist/flows/flow-secret-field.d.ts +14 -0
- package/dist/flows/flow-secret-field.js +22 -0
- package/dist/flows/flow-step-runners.d.ts +1 -0
- package/dist/flows/flow-step-runners.js +91 -34
- package/dist/flows/flow-success.js +22 -0
- package/dist/flows/flow-tools.js +27 -8
- package/dist/flows/flow-upload-resolve.d.ts +23 -0
- package/dist/flows/flow-upload-resolve.js +46 -0
- package/dist/flows/flows.d.ts +15 -0
- package/dist/flows/flows.js +17 -14
- package/dist/flows/git-changed.d.ts +23 -4
- package/dist/flows/git-changed.js +32 -6
- package/dist/flows/heal-run.js +2 -2
- package/dist/flows/predicate-to-expect.d.ts +3 -2
- package/dist/flows/predicate-to-expect.js +11 -5
- package/dist/flows/recording-backtrack.d.ts +24 -0
- package/dist/flows/recording-backtrack.js +73 -0
- package/dist/flows/recordings.d.ts +6 -0
- package/dist/flows/replay-halt.d.ts +26 -0
- package/dist/flows/replay-halt.js +30 -0
- package/dist/flows/replay.d.ts +0 -8
- package/dist/flows/replay.js +15 -3
- package/dist/flows/suite-flakes.d.ts +2 -1
- package/dist/flows/verify-change-tools.js +16 -4
- package/dist/honesty/already-true.d.ts +14 -0
- package/dist/honesty/already-true.js +47 -4
- package/dist/honesty/blind-spots.d.ts +3 -1
- package/dist/honesty/blind-spots.js +24 -1
- package/dist/honesty/body-capture-remedy.d.ts +42 -0
- package/dist/honesty/body-capture-remedy.js +95 -0
- package/dist/honesty/feature-capture.d.ts +3 -2
- package/dist/honesty/feature-capture.js +1 -1
- package/dist/honesty/gap-ledger.d.ts +1 -1
- package/dist/honesty/gap-ledger.js +1 -1
- package/dist/honesty/instrumentation-gaps.d.ts +8 -1
- package/dist/honesty/instrumentation-gaps.js +12 -2
- package/dist/honesty/observability.d.ts +4 -3
- package/dist/honesty/tool-hit-rate.d.ts +3 -2
- package/dist/honesty/uncaptured-bodies.d.ts +3 -1
- package/dist/honesty/uncaptured-bodies.js +6 -2
- package/dist/honesty/unsettled.d.ts +9 -0
- package/dist/honesty/unsettled.js +45 -0
- package/dist/honesty/verified.d.ts +32 -1
- package/dist/honesty/verified.js +37 -10
- package/dist/impact/impact-store.d.ts +5 -14
- package/dist/impact/impact-store.js +49 -6
- package/dist/index.js +1 -1
- package/dist/input/network-detail.d.ts +26 -1
- package/dist/input/network-detail.js +44 -12
- package/dist/input/network-mock-tools.js +24 -17
- package/dist/input/real-input.js +1 -1
- package/dist/input/viewport-tools.js +25 -13
- package/dist/intent/inline-intent.d.ts +0 -10
- package/dist/intent/inline-intent.js +1 -1
- package/dist/intent/intent-shard-store.d.ts +3 -2
- package/dist/intent/intent-shard-store.js +1 -1
- package/dist/intent/intent-shard.d.ts +14 -14
- package/dist/intent/intent-shard.js +4 -4
- package/dist/intent/intent-store.d.ts +1 -1
- package/dist/intent/intent-store.js +1 -1
- package/dist/intent/intent-subject.d.ts +2 -1
- package/dist/intent/open-intents.d.ts +3 -2
- package/dist/license/license.d.ts +4 -4
- package/dist/license/license.js +1 -1
- package/dist/mcp/attach-memory.d.ts +2 -1
- package/dist/mcp/client-identity.d.ts +2 -1
- package/dist/mcp/mcp-outage.d.ts +1 -1
- package/dist/mcp/mcp-outage.js +1 -1
- package/dist/mcp/mcp-proxy.d.ts +3 -1
- package/dist/mcp/mcp-proxy.js +32 -7
- package/dist/mcp/mcp.js +3 -1
- package/dist/mcp/proxy-handshake.d.ts +2 -1
- package/dist/mcp/proxy-idle-exit.d.ts +45 -0
- package/dist/mcp/proxy-idle-exit.js +94 -0
- package/dist/mcp/server-instructions.d.ts +2 -1
- package/dist/memory/project-memory.d.ts +2 -3
- package/dist/memory/project-memory.js +1 -1
- package/dist/oracles/propose-consequences.js +5 -5
- package/dist/oracles/self-instrument.d.ts +2 -2
- package/dist/oracles/self-instrument.js +1 -1
- package/dist/platform.d.ts +3 -5
- package/dist/platform.js +3 -5
- package/dist/pool/browser-pool.d.ts +92 -0
- package/dist/pool/browser-pool.js +119 -5
- package/dist/pool/playwright-launcher.js +29 -2
- package/dist/pool/storage-seed.d.ts +23 -0
- package/dist/pool/storage-seed.js +198 -0
- package/dist/project/artifact-root.d.ts +2 -1
- package/dist/project/memory-fs.d.ts +2 -1
- package/dist/project/reticle-dir.js +1 -1
- package/dist/project/session-root.d.ts +8 -0
- package/dist/project/session-root.js +9 -2
- package/dist/runs/reticle-runner.d.ts +14 -1
- package/dist/runs/reticle-runner.js +39 -2
- package/dist/session/attachment-history.d.ts +2 -1
- package/dist/session/fake-session.d.ts +37 -0
- package/dist/session/fake-session.js +81 -0
- package/dist/session/lease-visibility.d.ts +2 -1
- package/dist/session/live-control-tools.js +6 -0
- package/dist/session/no-session-diagnosis.d.ts +17 -1
- package/dist/session/no-session-diagnosis.js +32 -6
- package/dist/session/no-session-next-action.d.ts +14 -0
- package/dist/session/no-session-next-action.js +18 -0
- package/dist/session/no-session-watch.js +46 -3
- package/dist/session/registered-projects.d.ts +54 -0
- package/dist/session/registered-projects.js +73 -0
- package/dist/session/review-store.d.ts +2 -1
- package/dist/session/session-health.d.ts +62 -1
- package/dist/session/session-health.js +82 -2
- package/dist/session/session-manager.d.ts +11 -1
- package/dist/session/session-manager.js +18 -3
- package/dist/session/session-replaced.d.ts +24 -0
- package/dist/session/session-replaced.js +29 -0
- package/dist/session/session-successor.js +20 -4
- package/dist/session/session.d.ts +13 -5
- package/dist/session/session.js +31 -23
- package/dist/setup/agent-configs.d.ts +3 -2
- package/dist/setup/agent-writer.d.ts +3 -2
- package/dist/setup/approval-migration.d.ts +2 -1
- package/dist/setup/approval-migration.js +1 -1
- package/dist/setup/auto-approve.d.ts +11 -19
- package/dist/setup/auto-approve.js +15 -12
- package/dist/{init → setup}/confirm.js +1 -1
- package/dist/setup/desktop-shape.d.ts +3 -2
- package/dist/setup/dev-server-wait.d.ts +8 -0
- package/dist/setup/dev-server-wait.js +3 -1
- package/dist/setup/drive-agent.d.ts +2 -3
- package/dist/setup/drive-agent.js +1 -1
- package/dist/setup/drive-plan.d.ts +3 -2
- package/dist/setup/ensure-daemon.d.ts +2 -1
- package/dist/setup/existing-app.d.ts +12 -0
- package/dist/setup/existing-app.js +13 -0
- package/dist/setup/init-host.d.ts +11 -0
- package/dist/setup/init-host.js +26 -0
- package/dist/setup/init-runtime.d.ts +4 -10
- package/dist/setup/init-runtime.js +23 -16
- package/dist/setup/license-key.d.ts +2 -1
- package/dist/setup/listeners.d.ts +2 -1
- package/dist/setup/loopback-probe-urls.d.ts +18 -0
- package/dist/setup/loopback-probe-urls.js +52 -0
- package/dist/setup/node-effects.d.ts +3 -0
- package/dist/setup/node-effects.js +26 -11
- package/dist/setup/page-probe.d.ts +5 -0
- package/dist/setup/relaunch.d.ts +2 -1
- package/dist/setup/run-setup.d.ts +5 -1
- package/dist/setup/run-setup.js +155 -48
- package/dist/setup/setup-command.d.ts +3 -2
- package/dist/setup/setup-command.js +8 -0
- package/dist/setup/setup-options.d.ts +5 -3
- package/dist/setup/setup-options.js +3 -2
- package/dist/setup/wait-progress.d.ts +28 -0
- package/dist/setup/wait-progress.js +43 -0
- package/dist/telemetry/app-instrumented.d.ts +2 -1
- package/dist/telemetry/app-instrumented.js +1 -1
- package/dist/telemetry/automation-hint.d.ts +1 -1
- package/dist/telemetry/automation-hint.js +1 -1
- package/dist/telemetry/cli-telemetry.js +1 -1
- package/dist/telemetry/connect-failure.d.ts +1 -1
- package/dist/telemetry/connect-failure.js +1 -1
- package/dist/telemetry/crash-cause.d.ts +3 -2
- package/dist/telemetry/crash-cause.js +1 -1
- package/dist/telemetry/daemon-telemetry.d.ts +3 -2
- package/dist/telemetry/daemon-telemetry.js +1 -1
- package/dist/telemetry/error-class.d.ts +3 -2
- package/dist/telemetry/error-class.js +1 -1
- package/dist/telemetry/feedback-cli.js +1 -1
- package/dist/telemetry/feedback-context.d.ts +3 -2
- package/dist/telemetry/feedback-context.js +5 -4
- package/dist/telemetry/feedback-local.d.ts +3 -2
- package/dist/telemetry/feedback.d.ts +4 -3
- package/dist/telemetry/feedback.js +1 -1
- package/dist/telemetry/git-facts.d.ts +2 -2
- package/dist/telemetry/git-facts.js +1 -1
- package/dist/telemetry/identify.d.ts +1 -1
- package/dist/telemetry/identify.js +1 -1
- package/dist/telemetry/init-telemetry.d.ts +2 -14
- package/dist/telemetry/init-telemetry.js +4 -13
- package/dist/telemetry/install-source.d.ts +7 -13
- package/dist/telemetry/install-source.js +7 -29
- package/dist/telemetry/license-activation.d.ts +3 -2
- package/dist/telemetry/license-activation.js +1 -1
- package/dist/telemetry/machine-snapshot.d.ts +1 -1
- package/dist/telemetry/mcp-connection.js +1 -1
- package/dist/telemetry/project-profile.d.ts +0 -14
- package/dist/telemetry/project-profile.js +1 -1
- package/dist/telemetry/proxy-telemetry.js +1 -1
- package/dist/telemetry/session-metrics.d.ts +1 -1
- package/dist/telemetry/session-metrics.js +1 -1
- package/dist/telemetry/telemetry.d.ts +1 -17
- package/dist/telemetry/telemetry.js +4 -4
- package/dist/telemetry/tool-refused.d.ts +1 -1
- package/dist/telemetry/tool-refused.js +1 -1
- package/dist/tools/act-observation.d.ts +3 -2
- package/dist/tools/act-preflight.js +8 -3
- package/dist/tools/act-target.d.ts +4 -0
- package/dist/tools/act-target.js +14 -5
- package/dist/tools/act-tools.d.ts +0 -2
- package/dist/tools/act-tools.js +45 -15
- package/dist/tools/assert-source.d.ts +30 -0
- package/dist/tools/assert-source.js +21 -0
- package/dist/tools/assert-verdict.d.ts +1 -1
- package/dist/tools/assert-verdict.js +14 -4
- package/dist/tools/browser-tools.js +28 -8
- package/dist/tools/complete-snapshot.d.ts +3 -2
- package/dist/tools/dynamic-tools.js +8 -0
- package/dist/tools/error-recovery.d.ts +10 -0
- package/dist/tools/error-recovery.js +23 -0
- package/dist/tools/feedback-tools.js +1 -1
- package/dist/tools/invoke-tool.js +2 -0
- package/dist/tools/lease-tools.d.ts +48 -20
- package/dist/tools/lease-tools.js +311 -76
- package/dist/tools/lineage-tools.d.ts +14 -0
- package/dist/tools/lineage-tools.js +64 -0
- package/dist/tools/merge-tools.js +1 -3
- package/dist/tools/navigate-arrival.d.ts +45 -1
- package/dist/tools/navigate-arrival.js +39 -6
- package/dist/tools/navigate-result.d.ts +8 -1
- package/dist/tools/navigate-result.js +14 -4
- package/dist/tools/numeric-bounds.d.ts +0 -11
- package/dist/tools/numeric-bounds.js +4 -4
- package/dist/tools/observe-tools.js +22 -26
- package/dist/tools/observed-nothing.d.ts +9 -0
- package/dist/tools/observed-nothing.js +2 -1
- package/dist/tools/read-tools.js +12 -17
- package/dist/tools/real-input-attempt.d.ts +14 -6
- package/dist/tools/real-input-attempt.js +75 -18
- package/dist/tools/reconcile-tools.js +2 -1
- package/dist/tools/reload-result.d.ts +6 -1
- package/dist/tools/reload-result.js +7 -2
- package/dist/tools/resolve-target.d.ts +7 -1
- package/dist/tools/resolve-target.js +72 -9
- package/dist/tools/settle-in-flight.d.ts +1 -1
- package/dist/tools/settle-in-flight.js +9 -2
- package/dist/tools/snapshot-delta.d.ts +24 -1
- package/dist/tools/snapshot-delta.js +66 -7
- package/dist/tools/tool-names.d.ts +1 -0
- package/dist/tools/tool-names.js +1 -0
- package/dist/tools/tool-surface.js +20 -2
- package/dist/tools/tools.js +120 -11
- package/dist/tools/verify-next-baton.d.ts +2 -1
- package/dist/update/sdk-sync.d.ts +1 -1
- package/dist/update/sdk-sync.js +1 -1
- package/dist/update/updater.js +1 -1
- package/dist/version/sdk-fix.d.ts +2 -1
- package/dist/version/version-skew.d.ts +0 -8
- package/dist/version/version-skew.js +1 -1
- package/docs/README.md +8 -6
- package/docs/actions.mdx +1 -1
- package/docs/agent-cheatsheet.md +4 -2
- package/docs/architecture.md +1 -1
- package/docs/cli/mcp.mdx +3 -1
- package/docs/cli/watch.mdx +1 -1
- package/docs/cli.mdx +1 -1
- package/docs/desktop.mdx +1 -1
- package/docs/docs.json +8 -3
- package/docs/faq.mdx +9 -0
- package/docs/first-drive.md +136 -0
- package/docs/frameworks.mdx +29 -7
- package/docs/gate-plan.md +2 -2
- package/docs/gates.md +19 -11
- package/docs/getting-started.md +7 -1
- package/docs/http-transport.md +164 -0
- package/docs/index.mdx +5 -3
- package/docs/install-manual.mdx +1 -1
- package/docs/packages/tauri.mdx +1 -1
- package/docs/packages.mdx +15 -3
- package/docs/platform-integration.md +1 -1
- package/docs/predicates.mdx +2 -0
- package/docs/system-map.md +2 -2
- package/docs/telemetry-contract.md +4 -4
- package/docs/telemetry-events.mdx +1 -1
- package/docs/telemetry.md +3 -3
- package/docs/tools-navigate.mdx +14 -6
- package/docs/usage.md +4 -4
- package/package.json +7 -4
- package/dist/init/agent-rules.d.ts +0 -116
- package/dist/init/agent-rules.js +0 -279
- package/dist/init/app-choice.d.ts +0 -20
- package/dist/init/app-choice.js +0 -25
- package/dist/init/astro-host.d.ts +0 -21
- package/dist/init/astro-host.js +0 -27
- package/dist/init/astro-patch.d.ts +0 -33
- package/dist/init/astro-patch.js +0 -300
- package/dist/init/capabilities.d.ts +0 -40
- package/dist/init/capabilities.js +0 -150
- package/dist/init/closing-hint.d.ts +0 -36
- package/dist/init/closing-hint.js +0 -120
- package/dist/init/connect-steps.d.ts +0 -2
- package/dist/init/connect-steps.js +0 -50
- package/dist/init/cra.d.ts +0 -79
- package/dist/init/cra.js +0 -178
- package/dist/init/csp-check.d.ts +0 -38
- package/dist/init/csp-check.js +0 -107
- package/dist/init/csp-doctor.d.ts +0 -32
- package/dist/init/csp-doctor.js +0 -54
- package/dist/init/desktop-doctor.d.ts +0 -41
- package/dist/init/desktop-doctor.js +0 -198
- package/dist/init/detect.d.ts +0 -96
- package/dist/init/detect.js +0 -209
- package/dist/init/dev-script.d.ts +0 -50
- package/dist/init/dev-script.js +0 -66
- package/dist/init/format-generated.d.ts +0 -18
- package/dist/init/format-generated.js +0 -39
- package/dist/init/install-hint.d.ts +0 -9
- package/dist/init/install-hint.js +0 -29
- package/dist/init/mcp-clients.d.ts +0 -89
- package/dist/init/mcp-clients.js +0 -299
- package/dist/init/mcp-registered.d.ts +0 -12
- package/dist/init/mcp-registered.js +0 -14
- package/dist/init/mcp.d.ts +0 -89
- package/dist/init/mcp.js +0 -129
- package/dist/init/next-patch.d.ts +0 -39
- package/dist/init/next-patch.js +0 -196
- package/dist/init/node-io.d.ts +0 -7
- package/dist/init/node-io.js +0 -117
- package/dist/init/non-js-project.d.ts +0 -27
- package/dist/init/non-js-project.js +0 -84
- package/dist/init/patch-kind.d.ts +0 -20
- package/dist/init/patch-kind.js +0 -10
- package/dist/init/plan-framework.d.ts +0 -64
- package/dist/init/plan-framework.js +0 -565
- package/dist/init/plan.d.ts +0 -242
- package/dist/init/plan.js +0 -703
- package/dist/init/preflight.d.ts +0 -28
- package/dist/init/preflight.js +0 -38
- package/dist/init/project-id.d.ts +0 -12
- package/dist/init/project-id.js +0 -25
- package/dist/init/refresh-rules.d.ts +0 -39
- package/dist/init/refresh-rules.js +0 -51
- package/dist/init/run.d.ts +0 -117
- package/dist/init/run.js +0 -806
- package/dist/init/slash-command.d.ts +0 -27
- package/dist/init/slash-command.js +0 -132
- package/dist/init/snippets.d.ts +0 -134
- package/dist/init/snippets.js +0 -572
- package/dist/init/vite-config.d.ts +0 -18
- package/dist/init/vite-config.js +0 -114
- package/dist/init/windows-quote.d.ts +0 -38
- package/dist/init/windows-quote.js +0 -70
- package/dist/init/workspace-apps.d.ts +0 -30
- package/dist/init/workspace-apps.js +0 -178
- package/dist/project/remember-project.d.ts +0 -24
- package/dist/project/remember-project.js +0 -40
- /package/dist/{init → setup}/confirm.d.ts +0 -0
|
@@ -11,6 +11,7 @@
|
|
|
11
11
|
*/
|
|
12
12
|
/** The minimal page surface the pool drives. Real Playwright `Page` satisfies this. */
|
|
13
13
|
import { unreachableUrlIn } from '@reticlehq/core';
|
|
14
|
+
import { seedStorageInto } from './storage-seed.js';
|
|
14
15
|
/** Default lease time-to-live: a lease untouched for this long is presumed orphaned and reclaimed. */
|
|
15
16
|
export const DEFAULT_LEASE_TTL_MS = 5 * 60_000;
|
|
16
17
|
/** Default cap on how long a lease's initial navigation may take before it fails (frees the slot). */
|
|
@@ -147,7 +148,26 @@ export class BrowserPool {
|
|
|
147
148
|
return;
|
|
148
149
|
this.#aliases.set(registeredId, leaseId);
|
|
149
150
|
}
|
|
150
|
-
/**
|
|
151
|
+
/**
|
|
152
|
+
* The lease this id refers to: itself, or whatever it is an alias for.
|
|
153
|
+
*
|
|
154
|
+
* EVERY AGENT-FACING LOOKUP MUST GO THROUGH THIS. A leased page runs the SDK, which dials and
|
|
155
|
+
* registers under its OWN session id, so the id `reticle_sessions` shows an agent is routinely the
|
|
156
|
+
* alias rather than the lease key. Four capabilities used the raw map instead — screenshots,
|
|
157
|
+
* hover, network mocks and the dial-failure diagnostic — and every one of them refused for a
|
|
158
|
+
* lease that was alive and working, addressed by the only id the agent had been given.
|
|
159
|
+
*
|
|
160
|
+
* `reticle_network_mock` is where that surfaced: it reported `no-cdp-provider` in every
|
|
161
|
+
* configuration a reporter could reach, including after `reticle drive`, and the recommendation it
|
|
162
|
+
* printed ("start with `reticle drive <url>`") could not fix it because the provider was never the
|
|
163
|
+
* problem. Their conclusion was that error-path verification was impossible without shipping
|
|
164
|
+
* failure-injection code inside the application — which is exactly what an external mock exists to
|
|
165
|
+
* avoid.
|
|
166
|
+
*
|
|
167
|
+
* `acquire` and `#release` are the two exceptions and must stay raw: the first is registering the
|
|
168
|
+
* lease key itself before any alias can exist, and the second must delete that key rather than
|
|
169
|
+
* whatever it points at.
|
|
170
|
+
*/
|
|
151
171
|
#leaseIdOf(sessionId) {
|
|
152
172
|
return this.#active.has(sessionId) ? sessionId : (this.#aliases.get(sessionId) ?? sessionId);
|
|
153
173
|
}
|
|
@@ -172,7 +192,7 @@ export class BrowserPool {
|
|
|
172
192
|
* reaper take it out from under a diff.
|
|
173
193
|
*/
|
|
174
194
|
async screenshotLease(sessionId, opts = {}) {
|
|
175
|
-
const lease = this.#active.get(sessionId);
|
|
195
|
+
const lease = this.#active.get(this.#leaseIdOf(sessionId));
|
|
176
196
|
if (lease === undefined || lease.page.screenshot === undefined)
|
|
177
197
|
return undefined;
|
|
178
198
|
this.touch(sessionId);
|
|
@@ -193,7 +213,7 @@ export class BrowserPool {
|
|
|
193
213
|
* Touches the lease like any other tool call, so hovering keeps it alive.
|
|
194
214
|
*/
|
|
195
215
|
async hoverLease(sessionId, x, y) {
|
|
196
|
-
const lease = this.#active.get(sessionId);
|
|
216
|
+
const lease = this.#active.get(this.#leaseIdOf(sessionId));
|
|
197
217
|
if (lease === undefined || lease.page.hover === undefined)
|
|
198
218
|
return false;
|
|
199
219
|
this.touch(sessionId);
|
|
@@ -205,6 +225,46 @@ export class BrowserPool {
|
|
|
205
225
|
return false;
|
|
206
226
|
}
|
|
207
227
|
}
|
|
228
|
+
/**
|
|
229
|
+
* Install network mocks on a leased page, or false when this session is not a lease or cannot
|
|
230
|
+
* intercept. Same optional-capability contract as screenshotLease: absence must read as "could not
|
|
231
|
+
* mock", never as a stub that applied to nothing.
|
|
232
|
+
*
|
|
233
|
+
* Touches the lease like any other tool call, so mocking keeps it alive.
|
|
234
|
+
*/
|
|
235
|
+
/**
|
|
236
|
+
* Resize a leased page's viewport, or report that this page cannot be resized.
|
|
237
|
+
*
|
|
238
|
+
* Alias-resolved like every other agent-facing capability — see `#leaseIdOf`.
|
|
239
|
+
*
|
|
240
|
+
* Touches the lease like any other tool call, so resizing keeps it alive.
|
|
241
|
+
*/
|
|
242
|
+
async setViewportLease(sessionId, size) {
|
|
243
|
+
const lease = this.#active.get(this.#leaseIdOf(sessionId));
|
|
244
|
+
if (lease === undefined || lease.page.setViewport === undefined)
|
|
245
|
+
return false;
|
|
246
|
+
this.touch(sessionId);
|
|
247
|
+
try {
|
|
248
|
+
await lease.page.setViewport(size);
|
|
249
|
+
return true;
|
|
250
|
+
}
|
|
251
|
+
catch {
|
|
252
|
+
return false;
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
async setMocksLease(sessionId, rules) {
|
|
256
|
+
const lease = this.#active.get(this.#leaseIdOf(sessionId));
|
|
257
|
+
if (lease === undefined || lease.page.installMocks === undefined)
|
|
258
|
+
return false;
|
|
259
|
+
this.touch(sessionId);
|
|
260
|
+
try {
|
|
261
|
+
await lease.page.installMocks(rules);
|
|
262
|
+
return true;
|
|
263
|
+
}
|
|
264
|
+
catch {
|
|
265
|
+
return false;
|
|
266
|
+
}
|
|
267
|
+
}
|
|
208
268
|
async sweepExpired() {
|
|
209
269
|
const now = this.#now();
|
|
210
270
|
const expired = [...this.#active.entries()]
|
|
@@ -259,6 +319,7 @@ export class BrowserPool {
|
|
|
259
319
|
const sessionId = opts.sessionId ?? this.#genId();
|
|
260
320
|
let context;
|
|
261
321
|
let pending;
|
|
322
|
+
let pendingDialogMessage;
|
|
262
323
|
try {
|
|
263
324
|
const browser = await this.#ensureBrowser();
|
|
264
325
|
context = await browser.newContext();
|
|
@@ -281,23 +342,69 @@ export class BrowserPool {
|
|
|
281
342
|
else
|
|
282
343
|
pending = dialled; // logged during goto, before the lease is registered below
|
|
283
344
|
});
|
|
284
|
-
|
|
345
|
+
// Same "listen before goto" reasoning as onConsole above: a dialog opened during the initial
|
|
346
|
+
// navigation (an app that confirms something in an onload handler) must still be dismissed, not
|
|
347
|
+
// just ones opened after the lease is registered.
|
|
348
|
+
page.onDialog?.((dialog) => {
|
|
349
|
+
const active = this.#active.get(sessionId);
|
|
350
|
+
if (active !== undefined)
|
|
351
|
+
active.lastDialogMessage = dialog.message;
|
|
352
|
+
else
|
|
353
|
+
pendingDialogMessage = dialog.message;
|
|
354
|
+
void dialog.dismiss();
|
|
355
|
+
});
|
|
356
|
+
let seedHandle;
|
|
357
|
+
let checkSeedError;
|
|
358
|
+
if (opts.seedStorage !== undefined) {
|
|
359
|
+
const seedResult = await seedStorageInto(context, page, url, opts.seedStorage);
|
|
360
|
+
seedHandle = seedResult?.handle;
|
|
361
|
+
checkSeedError = seedResult?.checkError;
|
|
362
|
+
}
|
|
363
|
+
let navRes;
|
|
364
|
+
try {
|
|
365
|
+
navRes = await page.goto(url, { timeoutMs: this.#navTimeout });
|
|
366
|
+
checkSeedError?.();
|
|
367
|
+
}
|
|
368
|
+
finally {
|
|
369
|
+
if (seedHandle !== undefined) {
|
|
370
|
+
await seedHandle.dispose().catch(() => undefined);
|
|
371
|
+
}
|
|
372
|
+
}
|
|
285
373
|
// The browser can crash WHILE goto is resolving; #onCrash then clears #active and zeroes
|
|
286
374
|
// #occupied. Registering the lease now would resurrect a dead entry against a crashed browser with
|
|
287
375
|
// the slot count out of sync (drifting below #active.size, eventually exceeding the cap). If we're
|
|
288
376
|
// no longer the live browser, bail — the catch below closes the context and returns the slot.
|
|
289
377
|
if (this.#browser !== browser)
|
|
290
378
|
throw new Error('browser crashed during navigation');
|
|
379
|
+
let navStatus;
|
|
380
|
+
if (null !== navRes && 'object' === typeof navRes && 'status' in navRes) {
|
|
381
|
+
const s = navRes.status;
|
|
382
|
+
if ('function' === typeof s) {
|
|
383
|
+
try {
|
|
384
|
+
const code = s.call(navRes);
|
|
385
|
+
if ('number' === typeof code)
|
|
386
|
+
navStatus = code;
|
|
387
|
+
}
|
|
388
|
+
catch {
|
|
389
|
+
// ignore
|
|
390
|
+
}
|
|
391
|
+
}
|
|
392
|
+
else if ('number' === typeof s) {
|
|
393
|
+
navStatus = s;
|
|
394
|
+
}
|
|
395
|
+
}
|
|
291
396
|
this.#active.set(sessionId, {
|
|
292
397
|
context,
|
|
293
398
|
page,
|
|
294
399
|
url,
|
|
295
400
|
touchedAt: this.#now(),
|
|
296
401
|
...(pending === undefined ? {} : { dialFailureUrl: pending }),
|
|
402
|
+
...(pendingDialogMessage === undefined ? {} : { lastDialogMessage: pendingDialogMessage }),
|
|
297
403
|
});
|
|
298
404
|
return {
|
|
299
405
|
sessionId,
|
|
300
406
|
url,
|
|
407
|
+
...(navStatus !== undefined ? { navStatus } : {}),
|
|
301
408
|
release: () => this.#release(sessionId),
|
|
302
409
|
};
|
|
303
410
|
}
|
|
@@ -316,7 +423,14 @@ export class BrowserPool {
|
|
|
316
423
|
* Undefined means the page said nothing — never that it dialled the right place.
|
|
317
424
|
*/
|
|
318
425
|
dialFailureUrl(sessionId) {
|
|
319
|
-
return this.#active.get(sessionId)?.dialFailureUrl;
|
|
426
|
+
return this.#active.get(this.#leaseIdOf(sessionId))?.dialFailureUrl;
|
|
427
|
+
}
|
|
428
|
+
/**
|
|
429
|
+
* The message of the most recent native dialog (`confirm`/`alert`/`prompt`) auto-dismissed on this
|
|
430
|
+
* lease, if any. Diagnostic only — the dialog itself is already gone by the time this is readable.
|
|
431
|
+
*/
|
|
432
|
+
lastDialogMessage(sessionId) {
|
|
433
|
+
return this.#active.get(this.#leaseIdOf(sessionId))?.lastDialogMessage;
|
|
320
434
|
}
|
|
321
435
|
/** Close every context and the browser. Pending waiters are rejected (the pool is terminal now). */
|
|
322
436
|
async shutdown() {
|
|
@@ -5,11 +5,12 @@
|
|
|
5
5
|
*
|
|
6
6
|
* Headless by default — the pool exists for fault-tolerant headless multi-agent testing.
|
|
7
7
|
*/
|
|
8
|
-
import { BrowserLaunchKind } from '@reticlehq/core';
|
|
8
|
+
import { BrowserLaunchKind } from '@reticlehq/core/telemetry';
|
|
9
9
|
import { chromiumLaunchOptions } from '../chromium-launch-options.js';
|
|
10
10
|
import { getSessionMetrics } from '../telemetry/session-metrics.js';
|
|
11
11
|
import { classifyConnectFailure } from '../telemetry/connect-failure.js';
|
|
12
|
-
import { chromiumInstallCommand, bundledPlaywrightVersion } from '../cli/chromium-hint.js';
|
|
12
|
+
import { chromiumInstallCommand, chromiumInstallDepsCommand, bundledPlaywrightVersion, } from '../cli/chromium-hint.js';
|
|
13
|
+
import { installNetworkMocks } from '../input/network-mock.js';
|
|
13
14
|
/**
|
|
14
15
|
* How a leased tab navigates. Pure, and exported, because the decision in it is worth a test while
|
|
15
16
|
* the rest of this file needs a real Chromium.
|
|
@@ -49,10 +50,22 @@ function wrapBrowser(browser) {
|
|
|
49
50
|
await page.mouse.move(x + 1, y);
|
|
50
51
|
await page.mouse.move(x, y);
|
|
51
52
|
},
|
|
53
|
+
installMocks: (rules) => installNetworkMocks(page, [...rules]),
|
|
54
|
+
setViewport: (size) => page.setViewportSize(size),
|
|
52
55
|
onCrash: (handler) => page.on('crash', handler),
|
|
53
56
|
onConsole: (handler) => page.on('console', (msg) => handler(msg.text())),
|
|
57
|
+
onDialog: (handler) => page.on('dialog', (dialog) => handler({ message: dialog.message(), dismiss: () => dialog.dismiss() })),
|
|
58
|
+
addInitScript: async (script, arg) => {
|
|
59
|
+
const handle = (await page.addInitScript(script, arg));
|
|
60
|
+
const dispose = handle?.dispose;
|
|
61
|
+
if ('function' !== typeof dispose) {
|
|
62
|
+
throw new Error('Storage seeding failed: Playwright page.addInitScript did not return a disposable handle');
|
|
63
|
+
}
|
|
64
|
+
return { dispose: () => dispose() };
|
|
65
|
+
},
|
|
54
66
|
};
|
|
55
67
|
},
|
|
68
|
+
addCookies: (cookies) => context.addCookies(cookies),
|
|
56
69
|
close: () => context.close(),
|
|
57
70
|
};
|
|
58
71
|
},
|
|
@@ -69,6 +82,14 @@ function wrapBrowser(browser) {
|
|
|
69
82
|
* two cannot suggest different commands for the same missing browser.
|
|
70
83
|
*/
|
|
71
84
|
const CHROMIUM_MISSING_HINT = `Chromium is not installed for Playwright. Run: ${chromiumInstallCommand(bundledPlaywrightVersion())}`;
|
|
85
|
+
/**
|
|
86
|
+
* A DIFFERENT first-run failure from the one above: the browser binary is already there, but the
|
|
87
|
+
* host is missing the OS-level shared libraries (e.g. `libnspr4.so`) Playwright's own
|
|
88
|
+
* host-requirement check refuses to launch without. `npx playwright install chromium` does nothing
|
|
89
|
+
* for this — it re-downloads a binary that was never the problem — so this needs its own hint
|
|
90
|
+
* pointing at `install-deps`, pinned the same way and for the same reason as the one above.
|
|
91
|
+
*/
|
|
92
|
+
const CHROMIUM_MISSING_DEPS_HINT = `Chromium is installed, but the host system is missing shared-library dependencies it needs. Run: ${chromiumInstallDepsCommand(bundledPlaywrightVersion())}`;
|
|
72
93
|
/** A Launcher that boots a real headless Chromium and adapts it to the pool's interface. */
|
|
73
94
|
export function playwrightLauncher(opts = {}) {
|
|
74
95
|
const headless = opts.headless ?? true;
|
|
@@ -91,6 +112,12 @@ export function playwrightLauncher(opts = {}) {
|
|
|
91
112
|
settle(classifyConnectFailure(err));
|
|
92
113
|
// Turn Playwright's raw "Executable doesn't exist" into the one command that fixes it.
|
|
93
114
|
const msg = err instanceof Error ? err.message : String(err);
|
|
115
|
+
// Checked FIRST: Playwright's host-requirement validation error also contains
|
|
116
|
+
// "browserType.launch", so the generic check below would otherwise misdiagnose it as a
|
|
117
|
+
// missing binary and send the user through a fix that changes nothing.
|
|
118
|
+
if (/host system is missing dependencies/i.test(msg)) {
|
|
119
|
+
throw new Error(CHROMIUM_MISSING_DEPS_HINT);
|
|
120
|
+
}
|
|
94
121
|
if (/executable doesn.?t exist|playwright install|browsertype\.launch/i.test(msg)) {
|
|
95
122
|
throw new Error(CHROMIUM_MISSING_HINT);
|
|
96
123
|
}
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type SeedStorage } from '@reticlehq/core';
|
|
2
|
+
import type { InitScriptHandle, PooledContext, PooledCookie, PooledPage } from './browser-pool.js';
|
|
3
|
+
export declare function targetOriginOf(navUrl: string): string | undefined;
|
|
4
|
+
export declare function normalizeCookies(cookies: SeedStorage['cookies'], navUrl: string): PooledCookie[];
|
|
5
|
+
export interface StorageSeedResult {
|
|
6
|
+
handle?: InitScriptHandle;
|
|
7
|
+
checkError?: () => void;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Seed initial storage state (cookies, localStorage, sessionStorage) into a leased context and page
|
|
11
|
+
* before the first navigation happens.
|
|
12
|
+
*
|
|
13
|
+
* Cookies are injected into the BrowserContext cookie jar so the initial HTTP GET to the target URL
|
|
14
|
+
* already carries the Cookie header (including httpOnly session cookies).
|
|
15
|
+
*
|
|
16
|
+
* Local and session storage are injected via an init script evaluated after document creation but
|
|
17
|
+
* before any inline or bundled application scripts run. The init script is scoped strictly to the
|
|
18
|
+
* top-level target origin document (not child iframes or external redirect origins).
|
|
19
|
+
*
|
|
20
|
+
* Returns a handle that allows the caller to dispose of the init script once the initial navigation
|
|
21
|
+
* has resolved, ensuring one-time semantics (reloads or future navigations do not reapply the seed).
|
|
22
|
+
*/
|
|
23
|
+
export declare function seedStorageInto(context: PooledContext, page: PooledPage, targetUrl: string, seed: SeedStorage): Promise<StorageSeedResult | void>;
|
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
import { REDACTED_VALUE, scrubKnownSecrets } from '@reticlehq/core';
|
|
2
|
+
export function targetOriginOf(navUrl) {
|
|
3
|
+
try {
|
|
4
|
+
const origin = new URL(navUrl).origin;
|
|
5
|
+
return origin !== 'null' ? origin : undefined;
|
|
6
|
+
}
|
|
7
|
+
catch {
|
|
8
|
+
return undefined;
|
|
9
|
+
}
|
|
10
|
+
}
|
|
11
|
+
export function normalizeCookies(cookies, navUrl) {
|
|
12
|
+
if (cookies === undefined)
|
|
13
|
+
return [];
|
|
14
|
+
let baseUrl;
|
|
15
|
+
try {
|
|
16
|
+
const u = new URL(navUrl);
|
|
17
|
+
baseUrl = `${u.protocol}//${u.host}/`;
|
|
18
|
+
}
|
|
19
|
+
catch {
|
|
20
|
+
baseUrl = undefined;
|
|
21
|
+
}
|
|
22
|
+
const out = [];
|
|
23
|
+
if (Array.isArray(cookies)) {
|
|
24
|
+
for (const c of cookies) {
|
|
25
|
+
const cookieObj = {
|
|
26
|
+
name: c.name,
|
|
27
|
+
value: c.value,
|
|
28
|
+
};
|
|
29
|
+
if (c.domain !== undefined)
|
|
30
|
+
cookieObj.domain = c.domain;
|
|
31
|
+
if (c.path !== undefined) {
|
|
32
|
+
cookieObj.path = c.path;
|
|
33
|
+
}
|
|
34
|
+
else if (c.domain !== undefined && c.url === undefined) {
|
|
35
|
+
cookieObj.path = '/';
|
|
36
|
+
}
|
|
37
|
+
if (c.url !== undefined) {
|
|
38
|
+
cookieObj.url = c.url;
|
|
39
|
+
}
|
|
40
|
+
else if (c.domain === undefined && baseUrl !== undefined) {
|
|
41
|
+
cookieObj.url = baseUrl;
|
|
42
|
+
}
|
|
43
|
+
if (c.httpOnly !== undefined)
|
|
44
|
+
cookieObj.httpOnly = c.httpOnly;
|
|
45
|
+
if (c.secure !== undefined)
|
|
46
|
+
cookieObj.secure = c.secure;
|
|
47
|
+
if (c.sameSite !== undefined)
|
|
48
|
+
cookieObj.sameSite = c.sameSite;
|
|
49
|
+
if (c.expires !== undefined)
|
|
50
|
+
cookieObj.expires = c.expires;
|
|
51
|
+
out.push(cookieObj);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
else if ('object' === typeof cookies && null !== cookies) {
|
|
55
|
+
for (const [name, value] of Object.entries(cookies)) {
|
|
56
|
+
const cookieObj = {
|
|
57
|
+
name,
|
|
58
|
+
value,
|
|
59
|
+
};
|
|
60
|
+
if (baseUrl !== undefined) {
|
|
61
|
+
cookieObj.url = baseUrl;
|
|
62
|
+
}
|
|
63
|
+
else {
|
|
64
|
+
cookieObj.path = '/';
|
|
65
|
+
}
|
|
66
|
+
out.push(cookieObj);
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
return out;
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* Seed initial storage state (cookies, localStorage, sessionStorage) into a leased context and page
|
|
73
|
+
* before the first navigation happens.
|
|
74
|
+
*
|
|
75
|
+
* Cookies are injected into the BrowserContext cookie jar so the initial HTTP GET to the target URL
|
|
76
|
+
* already carries the Cookie header (including httpOnly session cookies).
|
|
77
|
+
*
|
|
78
|
+
* Local and session storage are injected via an init script evaluated after document creation but
|
|
79
|
+
* before any inline or bundled application scripts run. The init script is scoped strictly to the
|
|
80
|
+
* top-level target origin document (not child iframes or external redirect origins).
|
|
81
|
+
*
|
|
82
|
+
* Returns a handle that allows the caller to dispose of the init script once the initial navigation
|
|
83
|
+
* has resolved, ensuring one-time semantics (reloads or future navigations do not reapply the seed).
|
|
84
|
+
*/
|
|
85
|
+
export async function seedStorageInto(context, page, targetUrl, seed) {
|
|
86
|
+
const hasCookies = seed.cookies !== undefined &&
|
|
87
|
+
(Array.isArray(seed.cookies) ? seed.cookies.length > 0 : Object.keys(seed.cookies).length > 0);
|
|
88
|
+
const hasLocal = seed.local !== undefined && Object.keys(seed.local).length > 0;
|
|
89
|
+
const hasSession = seed.session !== undefined && Object.keys(seed.session).length > 0;
|
|
90
|
+
if (hasCookies && context.addCookies === undefined) {
|
|
91
|
+
throw new Error('Storage seeding failed: browser context does not support cookie seeding');
|
|
92
|
+
}
|
|
93
|
+
if ((hasLocal || hasSession) && page.addInitScript === undefined) {
|
|
94
|
+
throw new Error('Storage seeding failed: page does not support storage seeding via init script');
|
|
95
|
+
}
|
|
96
|
+
if (hasCookies && context.addCookies !== undefined) {
|
|
97
|
+
const cookiesToSet = normalizeCookies(seed.cookies, targetUrl);
|
|
98
|
+
if (cookiesToSet.length > 0) {
|
|
99
|
+
try {
|
|
100
|
+
await context.addCookies(cookiesToSet);
|
|
101
|
+
}
|
|
102
|
+
catch (err) {
|
|
103
|
+
let rawMsg = err instanceof Error ? err.message : String(err);
|
|
104
|
+
for (const c of cookiesToSet) {
|
|
105
|
+
if (undefined !== c.value && 'string' === typeof c.value && 0 !== c.value.length) {
|
|
106
|
+
const escaped = c.value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
|
|
107
|
+
rawMsg = rawMsg.replace(new RegExp(`(^|[^a-zA-Z0-9])${escaped}(?=$|[^a-zA-Z0-9])`, 'g'), `$1${REDACTED_VALUE}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
rawMsg = scrubKnownSecrets(rawMsg);
|
|
111
|
+
throw new Error(`Storage seeding failed: cookie injection failed (${rawMsg})`);
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
}
|
|
115
|
+
let handle;
|
|
116
|
+
let capturedError;
|
|
117
|
+
if (hasLocal || hasSession) {
|
|
118
|
+
page.onConsole?.((text) => {
|
|
119
|
+
if (text.startsWith('__reticle_seed_error:')) {
|
|
120
|
+
capturedError = text.slice('__reticle_seed_error:'.length);
|
|
121
|
+
}
|
|
122
|
+
});
|
|
123
|
+
const targetOrigin = targetOriginOf(targetUrl);
|
|
124
|
+
const added = await page.addInitScript?.(({ local, session, targetOrigin, }) => {
|
|
125
|
+
const win = (globalThis.window ??
|
|
126
|
+
globalThis);
|
|
127
|
+
// Only seed the top-level document frame
|
|
128
|
+
if (win.top !== win)
|
|
129
|
+
return;
|
|
130
|
+
// Only seed when the page is at the intended target origin
|
|
131
|
+
if (undefined !== targetOrigin && win.location?.origin !== targetOrigin)
|
|
132
|
+
return;
|
|
133
|
+
let seedError;
|
|
134
|
+
if (undefined !== local) {
|
|
135
|
+
try {
|
|
136
|
+
const storage = win.localStorage;
|
|
137
|
+
if (!storage) {
|
|
138
|
+
seedError = 'localStorage is not available';
|
|
139
|
+
}
|
|
140
|
+
else {
|
|
141
|
+
for (const [k, v] of Object.entries(local)) {
|
|
142
|
+
storage.setItem(k, v);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
catch (err) {
|
|
147
|
+
let errName = 'write error';
|
|
148
|
+
if (null !== err && 'object' === typeof err && 'name' in err) {
|
|
149
|
+
const name = err.name;
|
|
150
|
+
if ('string' === typeof name) {
|
|
151
|
+
errName = name;
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
seedError = `localStorage write failed: ${errName}`;
|
|
155
|
+
}
|
|
156
|
+
}
|
|
157
|
+
if (undefined === seedError && undefined !== session) {
|
|
158
|
+
try {
|
|
159
|
+
const storage = win.sessionStorage;
|
|
160
|
+
if (!storage) {
|
|
161
|
+
seedError = 'sessionStorage is not available';
|
|
162
|
+
}
|
|
163
|
+
else {
|
|
164
|
+
for (const [k, v] of Object.entries(session)) {
|
|
165
|
+
storage.setItem(k, v);
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
catch (err) {
|
|
170
|
+
let errName = 'write error';
|
|
171
|
+
if (null !== err && 'object' === typeof err && 'name' in err) {
|
|
172
|
+
const name = err.name;
|
|
173
|
+
if ('string' === typeof name) {
|
|
174
|
+
errName = name;
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
seedError = `sessionStorage write failed: ${errName}`;
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
if (undefined !== seedError) {
|
|
181
|
+
console.error(`__reticle_seed_error:${seedError}`);
|
|
182
|
+
}
|
|
183
|
+
}, { local: seed.local, session: seed.session, targetOrigin });
|
|
184
|
+
if (undefined === added || 'function' !== typeof added.dispose) {
|
|
185
|
+
throw new Error('Storage seeding failed: init script disposal is not supported by page');
|
|
186
|
+
}
|
|
187
|
+
handle = added;
|
|
188
|
+
}
|
|
189
|
+
const checkError = () => {
|
|
190
|
+
if (capturedError !== undefined) {
|
|
191
|
+
throw new Error(`Storage seeding failed: ${capturedError}`);
|
|
192
|
+
}
|
|
193
|
+
};
|
|
194
|
+
return {
|
|
195
|
+
...(handle !== undefined ? { handle } : {}),
|
|
196
|
+
checkError,
|
|
197
|
+
};
|
|
198
|
+
}
|
|
@@ -48,7 +48,7 @@ export declare const ArtifactRootReason: {
|
|
|
48
48
|
readonly AMBIGUOUS: "ambiguous";
|
|
49
49
|
};
|
|
50
50
|
export type ArtifactRootReason = (typeof ArtifactRootReason)[keyof typeof ArtifactRootReason];
|
|
51
|
-
|
|
51
|
+
interface ArtifactRootQuery {
|
|
52
52
|
/** The connected session's HELLO projectId, when it sent one. */
|
|
53
53
|
projectId: string | undefined;
|
|
54
54
|
/** Every project this machine knows about. Supplied, not gathered here — this stays pure. */
|
|
@@ -81,3 +81,4 @@ export declare function resolveArtifactRoot(query: ArtifactRootQuery): ArtifactR
|
|
|
81
81
|
* the one confirmed a moment ago is the better survivor.
|
|
82
82
|
*/
|
|
83
83
|
export declare function projectCandidatesFrom(discovery: ConfigDiscovery, registry: ProjectRegistry): ProjectCandidate[];
|
|
84
|
+
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import type { FileSystemPort } from './fs-port.js';
|
|
2
|
-
|
|
2
|
+
interface MemoryFs {
|
|
3
3
|
fs: FileSystemPort;
|
|
4
4
|
/** Every file written, keyed by normalised absolute path. */
|
|
5
5
|
written: Map<string, string>;
|
|
@@ -7,3 +7,4 @@ export interface MemoryFs {
|
|
|
7
7
|
dirs: Set<string>;
|
|
8
8
|
}
|
|
9
9
|
export declare function createMemoryFs(): MemoryFs;
|
|
10
|
+
export {};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { join } from 'node:path';
|
|
2
|
-
import { AppRuntime } from '@reticlehq/core';
|
|
2
|
+
import { AppRuntime } from '@reticlehq/core/telemetry';
|
|
3
3
|
import { CONTRACT_FILE_VERSION, ContractFileSchema, ContractReadError, FLOW_NAME_PATTERN, ReticleDir, } from '@reticlehq/core';
|
|
4
4
|
export function reticleDirPaths(root) {
|
|
5
5
|
return {
|
|
@@ -13,3 +13,11 @@ import type { ToolDeps } from '../tools/tool-kit.js';
|
|
|
13
13
|
* behaviour every call site had before the resolver existed.
|
|
14
14
|
*/
|
|
15
15
|
export declare function sessionRoot(deps: ToolDeps, sessionId: string | undefined): string;
|
|
16
|
+
/**
|
|
17
|
+
* The connected session's projectId, or undefined when there is no session to ask.
|
|
18
|
+
*
|
|
19
|
+
* `sessions.resolve` throws when nothing is connected, when the id names no session, and when
|
|
20
|
+
* several are connected and none was named. All three mean the same thing here — we cannot tell
|
|
21
|
+
* which project this call is about — and none of them is a reason to fail the caller's tool.
|
|
22
|
+
*/
|
|
23
|
+
export declare function sessionProjectId(deps: ToolDeps, sessionId: string | undefined): string | undefined;
|
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* behaviour every call site had before the resolver existed.
|
|
13
13
|
*/
|
|
14
14
|
export function sessionRoot(deps, sessionId) {
|
|
15
|
-
return deps.artifactRootFor?.(
|
|
15
|
+
return deps.artifactRootFor?.(sessionProjectId(deps, sessionId)).root ?? deps.reticleRoot;
|
|
16
16
|
}
|
|
17
17
|
/**
|
|
18
18
|
* The connected session's projectId, or undefined when there is no session to ask.
|
|
@@ -21,7 +21,14 @@ export function sessionRoot(deps, sessionId) {
|
|
|
21
21
|
* several are connected and none was named. All three mean the same thing here — we cannot tell
|
|
22
22
|
* which project this call is about — and none of them is a reason to fail the caller's tool.
|
|
23
23
|
*/
|
|
24
|
-
|
|
24
|
+
/*
|
|
25
|
+
* Exported alongside `sessionRoot`, and that is the point rather than a convenience. A caller that
|
|
26
|
+
* takes the ROOT from the session and the PROJECT ID from somewhere else has resolved half an
|
|
27
|
+
* address, which is the defect the comment above predicted and `loadNamedFlows` then shipped: it
|
|
28
|
+
* read the id from `readProjectId(process.cwd())` while its root came from here. Both halves come
|
|
29
|
+
* from this file so they cannot disagree again.
|
|
30
|
+
*/
|
|
31
|
+
export function sessionProjectId(deps, sessionId) {
|
|
25
32
|
try {
|
|
26
33
|
return deps.sessions.resolve(sessionId).projectId;
|
|
27
34
|
}
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
* top of this — the orchestration and the verdict live here so the MCP path and the API produce the
|
|
10
10
|
* same artifact byte-for-byte.
|
|
11
11
|
*/
|
|
12
|
-
import type
|
|
12
|
+
import { type FlowReplayResult, type ReticleVerificationRun, type RunId, type VerifyProgressEvent } from '@reticlehq/core';
|
|
13
13
|
import { type VerificationRunInput } from './build-verification-run.js';
|
|
14
14
|
import { type ChangedFileInput, type RiskPolicy } from './risk-classify.js';
|
|
15
15
|
/** The live capabilities ReticleRunner needs. Injected so tests pass fakes (no CDP, no session). */
|
|
@@ -23,6 +23,14 @@ export interface RunnerPort {
|
|
|
23
23
|
/** Injected run-id generator (no Math.random in logic) — the live adapter supplies a branded uuid. */
|
|
24
24
|
newRunId(): RunId;
|
|
25
25
|
}
|
|
26
|
+
/**
|
|
27
|
+
* Narration for a run in flight. Optional, and never load-bearing.
|
|
28
|
+
*
|
|
29
|
+
* A verification is silent for its whole duration, so anything watching from outside cannot tell a
|
|
30
|
+
* run that is working from one that has died. This is the seam that makes the difference visible.
|
|
31
|
+
* It reports what is HAPPENING; the artifact remains the only record of what was PROVED.
|
|
32
|
+
*/
|
|
33
|
+
export type VerifyProgressListener = (event: VerifyProgressEvent) => void;
|
|
26
34
|
/** Run metadata the caller supplies; flows + verdict are produced by verify. */
|
|
27
35
|
export interface VerifyOptions {
|
|
28
36
|
names?: string[];
|
|
@@ -34,6 +42,11 @@ export interface VerifyOptions {
|
|
|
34
42
|
changedFiles?: ChangedFileInput[];
|
|
35
43
|
/** Which touched surfaces block the verdict (require human confirmation). */
|
|
36
44
|
policy?: RiskPolicy;
|
|
45
|
+
/**
|
|
46
|
+
* Called as each flow starts and finishes. Best-effort by contract: a listener that throws is
|
|
47
|
+
* swallowed, because a REPORTER must never be able to fail the thing it is reporting on.
|
|
48
|
+
*/
|
|
49
|
+
onProgress?: VerifyProgressListener;
|
|
37
50
|
}
|
|
38
51
|
export declare class ReticleRunner {
|
|
39
52
|
#private;
|
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
* same artifact byte-for-byte.
|
|
11
11
|
*/
|
|
12
12
|
import { reportRunTelemetry } from '../telemetry/run-telemetry.js';
|
|
13
|
+
import { boundFlowName, RunFlowStatus, VerifyPhase, } from '@reticlehq/core';
|
|
13
14
|
import { buildVerificationRun } from './build-verification-run.js';
|
|
14
15
|
import { mapReplayToFlowResult } from './replay-mapping.js';
|
|
15
16
|
import { buildRepairPackets } from './repair-prompt.js';
|
|
@@ -26,14 +27,50 @@ export class ReticleRunner {
|
|
|
26
27
|
*/
|
|
27
28
|
async verify(opts) {
|
|
28
29
|
const names = opts.names ?? (await this.#port.listFlows());
|
|
30
|
+
/*
|
|
31
|
+
* Emitting NEVER throws into the run. A dashboard, a log or an editor is watching, and none of
|
|
32
|
+
* them is worth failing a verification for — so a listener that blows up is dropped here rather
|
|
33
|
+
* than unwinding a suite somebody is waiting on.
|
|
34
|
+
*/
|
|
35
|
+
const emit = (event) => {
|
|
36
|
+
try {
|
|
37
|
+
opts.onProgress?.(event);
|
|
38
|
+
}
|
|
39
|
+
catch {
|
|
40
|
+
/* a reporter cannot fail the thing it reports on */
|
|
41
|
+
}
|
|
42
|
+
};
|
|
43
|
+
const total = names.length;
|
|
44
|
+
// Announced BEFORE the loop: "step 3" without "of 12" does not answer the only question a
|
|
45
|
+
// person watching is actually asking, which is whether to keep waiting.
|
|
46
|
+
emit({ phase: VerifyPhase.FLOWS_FOUND, total, at: this.#port.now() });
|
|
29
47
|
const replays = [];
|
|
30
48
|
const flows = [];
|
|
31
|
-
for (const name of names) {
|
|
49
|
+
for (const [index, name] of names.entries()) {
|
|
32
50
|
const start = this.#port.now();
|
|
51
|
+
emit({
|
|
52
|
+
phase: VerifyPhase.FLOW_STARTED,
|
|
53
|
+
index,
|
|
54
|
+
total,
|
|
55
|
+
name: boundFlowName(name),
|
|
56
|
+
at: start,
|
|
57
|
+
});
|
|
33
58
|
const replay = await this.#port.replayFlow(name);
|
|
34
59
|
replays.push(replay);
|
|
35
|
-
|
|
60
|
+
const mapped = mapReplayToFlowResult(replay, this.#port.now() - start);
|
|
61
|
+
flows.push(mapped);
|
|
62
|
+
emit({
|
|
63
|
+
phase: VerifyPhase.FLOW_FINISHED,
|
|
64
|
+
index,
|
|
65
|
+
total,
|
|
66
|
+
name: boundFlowName(name),
|
|
67
|
+
// A convenience for colouring a row while the run is live. The VERDICT is computed from the
|
|
68
|
+
// graded artifact below and never from this.
|
|
69
|
+
ok: mapped.status === RunFlowStatus.PASS,
|
|
70
|
+
at: this.#port.now(),
|
|
71
|
+
});
|
|
36
72
|
}
|
|
73
|
+
emit({ phase: VerifyPhase.GRADING, total, at: this.#port.now() });
|
|
37
74
|
const changedFiles = classifyChangedFiles(opts.changedFiles ?? []);
|
|
38
75
|
const risks = buildRisks(changedFiles, opts.policy);
|
|
39
76
|
const failurePackets = buildRepairPackets(replays);
|
|
@@ -16,7 +16,7 @@
|
|
|
16
16
|
* needs to wait four seconds to check a four-second gap is a test nobody runs.
|
|
17
17
|
*/
|
|
18
18
|
/** What an agent needs to know about the continuity of one session. */
|
|
19
|
-
|
|
19
|
+
interface Attachment {
|
|
20
20
|
/** How long THIS attachment has lasted. Resets on reconnect — it is not the session's lifetime. */
|
|
21
21
|
connectedSinceMs: number;
|
|
22
22
|
/** How many times this session dropped and came back. Zero means it never left. */
|
|
@@ -44,3 +44,4 @@ export declare class AttachmentHistory {
|
|
|
44
44
|
*/
|
|
45
45
|
of(sessionId: string): Attachment | undefined;
|
|
46
46
|
}
|
|
47
|
+
export {};
|