import { CloudClient } from "../cloud/client.js"; import type { AgentLlm } from "../cloud/types.js"; import { SessionState } from "../session/state.js"; export interface TraceConfig { /** Directory to write per-session JSONL span files into. */ dir: string; } export interface HandleSnapshotDeps { cloud: CloudClient | null; session: SessionState; bypass: boolean; /** * Privacy Shield bypass engaged (off-switch + ack). When true, redaction is * skipped and the raw snapshot + url are sent with `client_redacted: false` * and `privacy_shield_bypass: true`. Distinct from `bypass` (codec bypass); * if both are set the codec bypass short-circuits the POST and this is moot. */ privacyShieldBypass?: boolean; /** * Off-switch seen without the ack — a half-configured bypass. The Shield * stays on; the connector warns once per process that the ack is also * required. Mutually exclusive with `privacyShieldBypass`. */ privacyShieldBypassIncomplete?: boolean; log?: Logger; /** When set, redact in span-capture mode and append spans to JSONL. Off by default. */ trace?: TraceConfig; /** * Optional agent-LLM metadata. When set, sent on every cloud POST so * the service-side tokenizer picks the customer's provider; when * absent, the service falls back to an approximate tokenizer. The * cloud only acts on the first snapshot of a session — sending it * every request is intentional and stateless on the connector side. */ agentLlm?: AgentLlm; } export interface Logger { info(event: string, fields?: Record): void; warn(event: string, fields?: Record): void; error(event: string, fields?: Record): void; } /** * Connector-measured timings for a single snapshot. Populated only on * outcomes where a cloud round-trip succeeded (`compressed`, `pass_through`) * — these are the rows that have a corresponding `usage_events` entry on * the server side, so a telemetry POST has a join target. Bypass / no-yaml * / cloud-unreachable / privacy-fail-closed paths skip telemetry entirely. * * `client_round_trip_ms` and `upstream_ms` are added by the caller (the * proxy server's CallTool handler) since the snapshot handler doesn't see * either boundary; this draft only carries the two it can measure itself. */ export interface SnapshotTelemetryDraft { session_id: string; step: number; redaction_ms: number; cloud_ms: number; } export interface HandleSnapshotResult { text: string; outcome: "no_yaml" | "compressed" | "pass_through" | "bypass" | "cloud_unreachable" | "privacy_fail_closed"; stats?: Record; telemetry?: SnapshotTelemetryDraft; } /** * Hot-path handler for a `browser_snapshot` response from upstream Playwright MCP. * * 1. Extract the YAML + URL from the MCP text. * 2. Run Privacy Shield on snapshot body + url. Skipped only when the Privacy * Shield bypass is engaged (off-switch + ack) — distinct from JDC_BYPASS. * 3. If JDC_BYPASS or cloud is null: return the redacted snapshot unchanged. * 4. Otherwise POST to the cloud codec and splice compressed_output in. * 5. On any transient failure (network / 5xx / 429), degrade to the already-redacted * snapshot with `codec_unreachable` log. * * Privacy Shield error handling: * - Default fail-closed: returns `privacy_fail_closed` result — caller MUST block. * - JDC_PRIVACY_FAIL_OPEN=1 is handled inside the engine; if it's set we get the * original snapshot back with empty stats + a critical log already emitted. */ export declare function handleSnapshot(mcpResponseText: string, deps: HandleSnapshotDeps): Promise;