/** * Wire-level constants. No free strings anywhere in Reticle reference these directly — * every string/number that crosses the browser <-> bridge <-> agent boundary is named here. */ export declare const RETICLE_DEFAULT_PORT = 4400; export declare const RETICLE_WS_PATH = "/reticle"; /** Agent↔server MCP wire paths — served by the daemon HTTP plane, forwarded by the stdio proxy. */ export declare const MCP_SSE_PATH = "/mcp/sse"; export declare const MCP_MESSAGE_PATH = "/mcp/message"; /** * The name this MCP server answers to — in its own `serverInfo` handshake, and in the registration * an installer writes into an agent's config. Those must be the same string or the agent registers * one server and talks to another. * * It lives here because it is an identity that crosses the wire, not an installer detail. It used to * live in `init/`, which made the MCP handshake — squarely on the library path — reach into the * install-time surface for a single string, and dragged that whole subtree along behind it for any * consumer embedding the engine as a library. */ export declare const MCP_SERVER_NAME = "reticle"; /** * SSE event name the daemon writes to every open MCP stream immediately before it shuts itself down. * * A stream that simply ends looks identical from the proxy whether the daemon retired on schedule or * died under it, so every planned shutdown was counted as an outage the agent suffered. The proxy * cannot infer the difference — the daemon is the only thing that knows, and this is it saying so. * A custom event name rather than a `message`: the proxy forwards `message` frames to the client * verbatim, and this is addressed to the proxy, not to the agent. */ export declare const MCP_SHUTDOWN_EVENT = "reticle-shutdown"; /** Local-only daemon introspection — `reticle status` GETs this for sessions + health at a glance. */ export declare const STATUS_PATH = "/status"; /** * Local-only drive request — `reticle drive ` POSTs `{url}` here when a daemon already owns the * bridge port, and gets back the pooled session that daemon opened. The CLI asks instead of binding, * so the two never fight over the port. Same trust tier as STATUS_PATH. */ export declare const DRIVE_PATH = "/drive"; export declare const RETICLE_PROTOCOL_VERSION = 1; /** * The oldest protocol version this build still talks to. * * A range rather than a single number. An exact door refuses a peer one version out, which is * correct when both sides ship together and wrong the moment anything else implements this -- an * SDK a user has not upgraded yet is not an incompatible SDK. * * Equal to the current version today, so nothing changes yet. It exists so that raising the current * version is a decision about what to keep supporting, rather than an automatic break. */ export declare const RETICLE_MIN_PROTOCOL_VERSION = 1; /** * The host a CLIENT names when it dials the bridge, as opposed to the address the bridge BINDS. * * They are deliberately different values and both are correct. The daemon binds `127.0.0.1` so it can * never be reached off-host; a client says `localhost`, which is the readable name every doc, log * line and error message uses. The gap between them is covered by the IPv6 loopback alias * (`loopback-alias.ts`), which serves `[::1]` too, so `localhost` reaches the daemon whichever family * a platform resolves first. * * Named rather than inlined because it was inlined, and three generators then wrote their own: CRA * emitted `127.0.0.1`, the Astro helper and the Next plugin each spelled out `localhost`. A default * argument is not a single source of truth if it can be bypassed by typing the value. */ export declare const RETICLE_CLIENT_HOST = "localhost"; /** * The one place the bridge WebSocket URL is built. The SDK connect default, the vite/next snippet * generators, and the CLI's inject-connect all call this instead of hand-writing `ws://…${path}` — * so the wire string can never drift across the four call sites. Host defaults to `localhost` (the * dev app connects from the browser); pass it only for a non-default bind. */ export declare function bridgeWsUrl(port?: number, host?: string): string; /** * Namespaced URL params a pooled/headless launcher appends to the app URL so the app's own SDK adopts * the lease's identity (session + project) on connect — no app code changes. Wire contract shared by * the server (BrowserPool/lease tools) and the browser SDK; namespaced to avoid clashing with the * app's own query params. */ export declare const RETICLE_URL_PARAM: { readonly SESSION: "__reticle_session"; readonly PROJECT: "__reticle_project"; /** * "Reticle opened this page for itself" — and nothing more. * * Deliberately NOT the session stamp. `resolveConnectIdentity` reads SESSION as the session id * when the app names none, so marking a drive with it would rename every driven session to one * shared constant and make two concurrent drives collide. This carries the one fact the page * needs (nobody is sitting in front of it) without touching identity. */ readonly OPENED: "__reticle_opened"; }; /** The loopback bind address. The daemon/bridge bind here by default — never expose Reticle off-host. */ export declare const LOOPBACK_HOST = "127.0.0.1"; /** * Every environment variable Reticle reads, named once. A misspelled inline env string silently * disables the control it gates (e.g. a typo'd `RETICLE_TOKEN` would disable auth) — so the names live * here and nowhere else. The values are the literal process.env keys. */ export declare const ReticleEnv: { /** Shared-secret the browser SDK must present in HELLO; absent ⇒ loopback-trust only. */ readonly TOKEN: "RETICLE_TOKEN"; /** Bridge bind host. Defaults to loopback; setting anything else is opt-in remote exposure. */ readonly HOST: "RETICLE_HOST"; /** Comma-separated WS Origin allow-list for the bridge. */ readonly ALLOWED_ORIGINS: "RETICLE_ALLOWED_ORIGINS"; /** Bridge/daemon WS port override. */ readonly PORT: "RETICLE_PORT"; /** Daemon state — pidfiles, discovery registry, logs. Defaults to `~/.reticle`. Overridable * because a read-only $HOME (sandboxed agent, locked-down Windows profile, container) otherwise * makes the daemon unstartable with a raw EACCES naming nothing. Reported by a Windows user. */ readonly STATE_DIR: "RETICLE_STATE_DIR"; /** Attach to an already-running browser over CDP instead of launching one. */ readonly CDP_URL: "RETICLE_CDP_URL"; /** Max simultaneous leased headless contexts in the browser pool (resource cap). */ readonly MAX_CONTEXTS: "RETICLE_MAX_CONTEXTS"; /** * Inbound events per second before the bridge starts SAMPLING (never disconnecting). * * Raise it for a legitimately busy app — a streaming dashboard, a live grid — rather than accept * partial coverage. Free and local: the daemon runs on the same machine, so a higher ceiling costs * nothing to anyone. */ readonly MAX_MESSAGES_PER_SECOND: "RETICLE_MAX_MESSAGES_PER_SECOND"; /** Bearer token required by the optional `reticle serve --http` verify endpoint. */ readonly VERIFY_TOKEN: "RETICLE_VERIFY_TOKEN"; /** Ms of continuous idleness (no agent, no browser session, no lease) before the daemon self-exits; * `0` disables. Keeps Reticle from lingering on a user's machine after the editor closes. */ readonly IDLE_SHUTDOWN: "RETICLE_IDLE_SHUTDOWN_MS"; /** Idle re-check cadence (default 30s). Overridable so daemon-lifecycle-test can watch a full * exit/wake cycle in seconds rather than minutes. */ readonly IDLE_CHECK: "RETICLE_IDLE_CHECK_MS"; /** * Grace for a daemon with an agent ATTACHED. Longer than the base on purpose: quiet with a client * present means a slow install or a thinking human, not an unwanted daemon — a flat 5 minutes was * killing live runs mid-install. Derived from the base when unset. */ readonly IDLE_ATTACHED: "RETICLE_IDLE_ATTACHED_MS"; /** * How often the daemon writes `reticle_daemon_alive`, so a GAP in its log is evidence it died. * * Overridable for the same reason the idle windows are: a spec that has to prove "a killed daemon * is distinguishable from a tidy one" cannot wait 30 seconds per beat, and a spec that instead * re-implements the cadence is insensitive to the thing it claims to guard. */ readonly HEARTBEAT: "RETICLE_HEARTBEAT_MS"; /** Directory holding the auto-provisioned pairing token. Defaults to ~/.reticle; relocatable for CI. */ readonly PAIRING_TOKEN_DIR: "RETICLE_PAIRING_TOKEN_DIR"; /** Force the durable causal journal off (`0`/`false`/`off`) or on (`1`/`true`/`on`); default on. */ readonly JOURNAL: "RETICLE_JOURNAL"; /** * Verbose internal flow tracing for people working ON Reticle (`1`/`true`/`on`); default off. * * Distinct from the journal, which records what the AGENT did to the app. This records what * RETICLE did to answer it: one line per internal stage, with its duration and nesting, so a * developer can see which code a tool call actually went through and where the time went. * Off by default and free when off — a trace on every tool call is a cost on the hot path. */ readonly TRACE: "RETICLE_TRACE"; /** * How many consecutive reconnects the MCP proxy attempts before it stops retrying and goes * dormant. Overridable for the same reason the idle windows are: the real budget takes MINUTES to * exhaust, so the one spec that proves the proxy SURVIVES exhaustion could not run at all without * shortening it. A budget nobody can reach in a test is a budget nobody tests. */ readonly RECONNECT_ATTEMPTS: "RETICLE_RECONNECT_ATTEMPTS"; /** Quiet window before an abandoned MCP stdio proxy exits; `0` disables the watcher. */ readonly MCP_PROXY_IDLE: "RETICLE_MCP_PROXY_IDLE_MS"; /** * API key for the harness — the model that drives the app when no coding agent is in the loop. * * The standard Anthropic name, on purpose: a machine that can already run a coding agent can * already run the harness, with nothing to configure. Absent ⇒ the harness is simply unavailable * and every other part of Reticle is unaffected. */ readonly HARNESS_KEY: "ANTHROPIC_API_KEY"; /** Model the harness drives with. Defaults to a small one — see `DEFAULT_HARNESS_MODEL`. */ readonly HARNESS_MODEL: "RETICLE_HARNESS_MODEL"; /** Base URL for the harness's model API, for a proxy or a gateway. */ readonly HARNESS_BASE_URL: "RETICLE_HARNESS_BASE_URL"; /** Hard ceiling on harness model turns in one drive. Bounds cost, not value. */ readonly HARNESS_MAX_STEPS: "RETICLE_HARNESS_MAX_STEPS"; }; /** Hard transport bounds shared by the browser and bridge. */ export declare const TRANSPORT_LIMITS: { readonly MAX_MESSAGE_BYTES: number; /** * Inbound events per second before the bridge SAMPLES rather than records everything. * * The cap exists to stop a PATHOLOGICAL page (an animation loop firing DOM mutations every frame), * not to throttle a busy one: an ordinary React app with an active query cache passes 1000/s, and a * sampled window cannot support a verdict, so too low a cap answers `unknown` with * `unclean_capture` on the first `act_and_wait` after an install (#316). * * The ceiling it defends is cheap — the daemon is on the same machine and a typical event is a few * hundred bytes, so 20000/s is single-digit MB/s over loopback. Raising it does not raise what a * runaway page can make the bridge HOLD: memory is bounded independently by the ring buffer, which * evicts on `RING_BUFFER_DEFAULTS.MAX_BYTES` (this same constant through that alias) as well as on * a count and an age. The rate cap defends parse cost, the ring buffer defends memory, and they are * not substitutes. */ readonly MAX_MESSAGES_PER_SECOND: 20000; readonly MAX_SESSIONS: 32; readonly MAX_PENDING_CONNECTIONS: 16; readonly HELLO_TIMEOUT_MS: 5000; readonly MAX_BUFFER_BYTES: number; readonly MAX_SESSION_ID_LENGTH: 128; readonly MAX_URL_LENGTH: 4096; readonly MAX_TITLE_LENGTH: 512; readonly MAX_ADAPTERS: 32; readonly MAX_CONTRACT_NAMES: 128; readonly MAX_ADAPTER_NAME_LENGTH: 128; readonly MAX_TOKEN_LENGTH: 512; readonly MAX_COMMAND_ID_LENGTH: 128; readonly MAX_COMMAND_NAME_LENGTH: 128; readonly MAX_REF_LENGTH: 128; readonly MAX_ERROR_LENGTH: 4096; /** Cap on a captured stack trace before it crosses the wire — the console observer and both React * error hooks (error-boundary, hydration-error) all truncate to this, so it is one fact. */ readonly MAX_STACK_LENGTH: 4000; readonly MAX_SERIALIZE_DEPTH: 8; readonly MAX_COLLECTION_ITEMS: 200; readonly MAX_OBJECT_KEYS: 200; readonly MAX_STRING_LENGTH: number; /** Human review marks: the note the human types when flagging a mistake on the page. */ readonly MAX_MARK_NOTE_LENGTH: 2000; /** Human review marks: the legible element label that pins the mark (e.g. "Submit button"). */ readonly MAX_MARK_LABEL_LENGTH: 256; }; /** Replacement used when sensitive data is removed before crossing the bridge. */ export declare const REDACTED_VALUE = "[REDACTED]"; /** Explicit opt-in argument required for potentially destructive actions. */ export declare const DANGEROUS_ACTION_CONFIRM_ARG = "confirmDangerous"; /** * Opt-in argument for a TRUSTED native click, for the handlers a synthetic one cannot satisfy — * file pickers, clipboard, anything gated on `isTrusted`. Only `reticle_act` can honour it: the * native driver is a pointer gesture at coordinates, and the act-then-wait tool drives the page * through the SDK instead. */ export declare const NATIVE_INPUT_ARG = "native"; /** Schema version stamped onto compiled replay programs. */ export declare const REPLAY_PROGRAM_VERSION = 1; /** The git-checked Reticle workspace directory + its layout. No free strings. */ export declare const ReticleDir: { readonly ROOT: ".reticle"; readonly CONTRACT_FILE: "contract.json"; readonly FLOWS_SUBDIR: "flows"; readonly BASELINES_SUBDIR: "baselines"; /** cross-run memory — outcomes of past runs (the "did it behave like last time?" file). */ readonly PROJECT_FILE: "project.json"; /** * the user's own record of what Reticle has done for them — .reticle/impact.json. * * Local only, never uploaded, and deliberately NOT part of telemetry: telemetry answers questions * about the product; this answers the user's question about their own work. */ readonly IMPACT_FILE: "impact.json"; /** what changes were SUPPOSED to make true —.reticle/intent.json (git-checked, reviewed) */ readonly INTENT_FILE: "intent.json"; /** opt-in pixel baselines —.reticle/visual/.png + .diff.png. */ readonly VISUAL_SUBDIR: "visual"; /** verification-run artifacts —.reticle/runs/.json (the OEM/CI-consumable verdict). */ readonly RUNS_SUBDIR: "runs"; /** fail-to-pass bug capsules —.reticle/capsules/.json (a minimal failing flow + its evidence). */ readonly CAPSULES_SUBDIR: "capsules"; /** durable causal journal —.reticle/sessions//{events,actions}.jsonl (the substrate). */ readonly SESSIONS_SUBDIR: "sessions"; /** append-only event ledger inside a session dir (one ReticleEvent per line). */ readonly JOURNAL_EVENTS_FILE: "events.jsonl"; /** append-only action ledger inside a session dir (one JournalAction per line). */ readonly JOURNAL_ACTIONS_FILE: "actions.jsonl"; /** learned expected-envelopes per route, accumulated across runs (the deviation-report baseline). */ readonly ENVELOPES_FILE: "envelopes.json"; /** learned ambient (action-less churn) region map — excluded from settle/summaries/envelopes. */ readonly AMBIENT_FILE: "ambient.json"; /** per-flow flake ledger — replay outcomes that decide intermittent-failure quarantine. */ readonly FLAKE_FILE: "flake.json"; /** * the project's cloud binding — .reticle/cloud.json, written by `reticle link`. Git-checked and * non-secret: the project id, the API origin, and where its dashboard lives. The KEY lives in * ~/.reticle/credentials.json instead, because that one must never reach a repository. */ readonly CLOUD_LINK_FILE: "cloud.json"; /** * local sync bookkeeping — .reticle/cloud-state.json. The pull cursor, when each half last ran, * and the last error. NOT git-checked: it describes this machine's conversation with the server, * and committing one machine's cursor would make every other machine skip what it had not seen. */ readonly CLOUD_STATE_FILE: "cloud-state.json"; /** * triage decisions pulled BACK from the dashboard — .reticle/issues.json. What a human said about * a defect ("resolved", "not a bug"), so the HUD stops showing it and the next run does not * re-report it as though nobody had looked. */ readonly ISSUES_FILE: "issues.json"; /** Per-flow assertion tiers recorded on each PASSING replay — the gate's anti-downgrade baseline. */ readonly TIERS_FILE: "assertion-tiers.json"; /** * what to run when something happens — .reticle/hooks.json. Git-checked on purpose: a hook is a * decision the whole team shares, the same way a git hook or a package script is, and one that * only exists on the machine that wrote it is a rule nobody else is following. * * It names COMMANDS, so it is exactly as trusted as `package.json` scripts in the same repository * and no more: opening a repo does not run them, and Reticle runs one only when the event it is * attached to actually happens. The event payload is handed over on stdin, never interpolated into * the command line, so a defect's own text can never become part of a command. */ readonly HOOKS_FILE: "hooks.json"; /** * Auto-provisioned bridge pairing token, stored at ~/.reticle/pairing-token (mode 0600). Written by * the daemon, read Node-side by the build plugins to inject into connect. A browser sandbox cannot * read it, so a rogue localhost app can't present it — that's what stops cross-app session hijack. */ readonly PAIRING_TOKEN_FILE: "pairing-token"; }; /** * Structured reasons a screenshot/visual-diff could not produce a verdict (never * thrown as free strings). The visual layer is OPT-IN and CDP/Playwright-driven — it is NEVER * bundled into the always-on browser SDK — so NO_PROVIDER is the common "you must `reticle drive`" case. */ export declare const VisualReason: { readonly NO_PROVIDER: "no-visual-provider"; readonly CAPTURE_FAILED: "capture-failed"; readonly BASELINE_MISSING: "baseline-missing"; readonly DIMENSION_MISMATCH: "dimension-mismatch"; readonly FULL_PAGE_UNSUPPORTED: "full-page-unsupported"; readonly NOT_COMPOSITED: "window-not-composited"; }; export type VisualReason = (typeof VisualReason)[keyof typeof VisualReason]; /** Actionable companion to NO_PROVIDER — the visual layer needs a driven browser. */ export declare const VISUAL_NO_PROVIDER_RECOMMENDATION = "visual capture needs a driven browser \u2014 start with `reticle drive ` or set RETICLE_CDP_URL; the always-on SDK does not ship a screenshotter"; /** Default per-pixel color-distance threshold (pixelmatch 0..1; higher = more lenient). */ export declare const VISUAL_PIXEL_THRESHOLD = 0.1; /** * Autonomous "smart monkey" anomaly classes reticle_crawl reports after clicking a * reachable control. Named so the agent (and tests) branch on cause, never on message text. */ /** * Bounds for reticle_scroll_to — how many viewport scrolls to try before giving up on * a virtualized/windowed list (which only renders visible rows, so a plain reticle_query misses * off-screen items). Each scroll advances ~one viewport; the loop also stops early at the list end. */ export declare const SCROLL_FIND_DEFAULTS: { readonly MAX_SCROLLS: 20; }; /** Bounds so a crawl always terminates and each click has time to settle. */ export declare const CRAWL_DEFAULTS: { /** Max controls clicked in one crawl (then `truncated:true`). */ readonly MAX_STEPS: 25; /** ms to wait for a click's reaction to land in the buffer before classifying. */ readonly SETTLE_MS: 300; /** HTTP status at/above which a response counts as a failed request. */ readonly FAILED_STATUS: 400; }; /** Default max wait for reticle_assert / reticle_wait_for / reticle_act_and_wait when the caller gives * no `timeout_ms`. One fact, shared by the server tools and the @reticlehq/test matchers so the test * package's assumption can't silently diverge from the server default. */ export declare const DEFAULT_ASSERT_TIMEOUT_MS = 4000; /** How long to wait between npm registry update checks (24 h). */ export declare const UpdateCheckIntervalMs: number; /** Schema version stamped into contract.json so a reader can reject/upgrade old files. */ export declare const CONTRACT_FILE_VERSION = 1; /** Arg key on reticle_capabilities selecting the on-disk contract over the live session. */ export declare const FROM_DISK_ARG = "fromDisk"; /** Structured outcome when reading contract.json fails (never thrown to the agent). */ export declare const ContractReadError: { readonly MISSING: "contract-missing"; readonly MALFORMED: "contract-malformed"; }; export type ContractReadError = (typeof ContractReadError)[keyof typeof ContractReadError]; /** Bounds for the per-session ring buffer. */ export declare const RING_BUFFER_DEFAULTS: { readonly MAX_EVENTS: 2000; readonly MAX_AGE_MS: 60000; readonly MAX_BYTES: number; }; /** Normalized event types pushed into the ring buffer. */ export declare const EventType: { readonly DOM_ADDED: "dom.added"; readonly DOM_REMOVED: "dom.removed"; readonly DOM_ATTR: "dom.attr"; readonly DOM_TEXT: "dom.text"; readonly NET_REQUEST: "net.request"; readonly NET_PENDING: "net.pending"; /** An SSE (EventSource) or WebSocket frame — a message on a long-lived streaming connection. */ readonly NET_STREAM: "net.stream"; /** A web-perf metric a screenshot can't verify: LCP, cumulative layout shift, or a long task. */ readonly PERF: "perf"; readonly ROUTE_CHANGE: "route.change"; readonly CONSOLE_LOG: "console.log"; readonly CONSOLE_WARN: "console.warn"; readonly CONSOLE_ERROR: "console.error"; readonly CONSOLE_INFO: "console.info"; readonly CONSOLE_DEBUG: "console.debug"; readonly ERROR_UNCAUGHT: "error.uncaught"; readonly VISIBLE_SHOWN: "visible.shown"; readonly ANIM_START: "anim.start"; readonly ANIM_END: "anim.end"; readonly SCROLL_POSITION: "scroll.position"; readonly REVEAL_SHOWN: "reveal.shown"; readonly SIGNAL: "signal"; readonly STATE_CHANGE: "state.change"; /** a write to localStorage/sessionStorage/cookies — `data: { area, key, old?, new? }` (values redacted). */ readonly STORAGE_CHANGE: "storage.change"; /** page-level visibility/focus health (distinct from element-level VISIBLE_*). */ readonly PAGE_HEALTH: "page.health"; /** * synthetic: the page called window.open, so the consequence of what was just clicked may live in * another browsing context this one cannot observe (an OAuth popup is the archetype). * `data: { href }` — the URL the page asked to open, when it named one. */ readonly CONTEXT_OPENED: "context.opened"; /** * The app opened a native `alert`/`confirm`/`prompt` while Reticle was driving it. * * Recorded because Reticle ANSWERS these rather than letting them block — a native dialog halts * the main thread, and the SDK's own message pump is on that thread, so one `confirm` behind a * driven click made the tab permanently unresponsive with no recovery from inside the session. * Answering silently would trade a wedge for an invisible one, so the question the app asked, and * the answer given, ride out as an event. */ readonly DIALOG_OPENED: "dialog.opened"; /** aggregated React commits over a throttle window (dev builds) — `data: { commits }`. Commit storms / * wasted re-renders show up here without a per-render flood. */ readonly RENDER_COMMIT: "render.commit"; /** element focus moved — `data: { to, from, toBody }`. Focus dropping to body after an act is a regression. */ readonly FOCUS_CHANGE: "focus.change"; /** browser → bridge: a human recording compiled in-page. */ readonly FLOW_RECORDED: "flow.recorded"; /** synthetic: browser transport queue overflowed; events were dropped. `data: { dropped: number }`. */ readonly TRANSPORT_OVERFLOW: "transport.overflow"; /** * synthetic: a per-channel cap truncated a batch (e.g. a DOM mutation flood). `data: { channel, dropped }`. * Marks downstream rollups/envelopes as built on incomplete data — a ledger that lies at scale is worse * than no ledger, so truncation is never silent. */ readonly TRUNCATED: "truncated"; /** * synthetic: the SDK detected a region it CANNOT observe (a cross-origin iframe, a closed shadow root). * `data: { kind: BlindSpotKind, count }`. Surfaced on results as `coverage: partial` so a green never * implies it saw everything. */ readonly BLIND_SPOT: "blind-spot"; /** synthetic: the SDK ITSELF failed (an observer threw). `data: { site, message, errorType }`. * Rides the existing bridge — no outbound request. See browser/observers/sdk-failure.ts. */ readonly SDK_FAILED: "sdk.failed"; /** * synthetic (driven only): CDP/Playwright-authoritative network detail for a response the in-page * fetch/XHR wrapper also saw — full response headers + authoritative status/mimeType the page-side * wrapper can't reach. `data: { url, method?, status, headers, resourceType? }`. Merged onto the * matching in-page NET_REQUEST so the driven view never loses fidelity to an outside-in tool. */ readonly NET_DETAIL: "net.detail"; /** * Live-control: browser → bridge. A human acted on the presenter panel. * `data: { kind: HumanControlKind; text?: string }`. Rides the existing EventMessage. */ readonly HUMAN_CONTROL: "human.control"; /** * Human review: browser → bridge. A human pinned a mistake to an element on the running page * (the "annotate the bug where you see it" loop). `data` narrows to HumanMarkDataSchema — a note * plus a re-resolvable element anchor (and its source file:line when the framework stamped one) so * the agent that drains the mark knows exactly which element and which source to fix. */ readonly HUMAN_MARK: "human.mark"; /** * The app produced a FILE — a Blob handed to `URL.createObjectURL`, usually saved by clicking an * anchor with `download`. `data: { filename?, mimeType, bytes, lines?, preview? }`. The one artifact * class no outside-the-browser tool can inspect: it never crosses the network, so there is no * request to intercept. See `observers/download.ts` for the defect that motivated it. */ readonly DOWNLOAD: "download"; }; export type EventType = (typeof EventType)[keyof typeof EventType]; /** * Named phenomena the perception layer detects deterministically over the journal — capsules and * deviation reports lead with these names, not raw events. The library grows in later releases; the * mechanism (versioned matchers + evidence templates) lands now. */ export declare const PhenomenonType: { /** An act dispatched but the app did nothing — no DOM/net/route/signal in its window. */ readonly DEAD_CLICK: "dead-click"; /** A click landed before hydration attached handlers — a silent no-op. */ readonly PRE_HYDRATION_CLICK: "pre-hydration-click"; /** A 5xx response that occurred while the page was hidden — looks fine, isn't. */ readonly HIDDEN_500: "hidden-500"; /** A request started and never completed within the window — a hung/in-flight request. */ readonly HUNG_REQUEST: "hung-request"; /** A React error boundary caught and swallowed — a fine-looking fallback over a broken feature. */ readonly SWALLOWED_ERROR: "swallowed-error"; }; export type PhenomenonType = (typeof PhenomenonType)[keyof typeof PhenomenonType]; /** Signal the React adapter fires once hydration commits (handlers attached). Shared browser↔server. */ export declare const RETICLE_HYDRATION_SIGNAL = "reticle:hydration-complete"; /** * The registered-store name the React adapter uses for render stats, read via reticle_state. It crosses * the wire as a store id AND the browser SDK special-cases it as reticle-owned (so it isn't flagged as * an unregistered store), so the name is one fact in core — a rename in the adapter must not silently * stop the SDK's owned-store matching. */ export declare const RETICLE_RENDERS_STORE = "__reticle_renders"; /** Global the render pre-hook parks its commit buffer on (see the vite plugin's RENDER_PREHOOK_SOURCE). */ export declare const RETICLE_RENDER_PREHOOK = "__reticleRenderPreHook"; /** * Signal the React adapter fires when an error boundary catches (dev-only). Carried on the signal channel * (the SDK's public emit surface) so the server sees a boundary that swallowed — the purest "looks fine, * isn't", invisible to every other channel. `data: { message, stack?, componentStack? }`. */ export declare const RETICLE_ERROR_BOUNDARY_SIGNAL = "reticle:error-boundary"; /** * Signal the React adapter fires on a hydration mismatch — server-rendered markup that disagrees with the * client's first render. React reports these as *recoverable* errors (`hydrateRoot(el, App, * { onRecoverableError })`); a mismatch silently discards the SSR DOM and re-renders on the client, so the * page "looks fine" while event handlers, form state, or scroll position were lost — invisible to DOM/ * network/console. `data: { message, stack?, componentStack? }`. */ export declare const RETICLE_HYDRATION_ERROR_SIGNAL = "reticle:hydration-error"; /** The observation channel a TRUNCATED event names, so downstream knows WHICH data is incomplete. */ export declare const TruncationChannel: { readonly DOM: "dom"; }; export type TruncationChannel = (typeof TruncationChannel)[keyof typeof TruncationChannel]; /** * How an event was linked to the action it is attributed to. `window` means the SDK stamped the * currently-active action's id onto every event observed between that action's dispatch and its * settle — a time-window heuristic, not proven dataflow. It is the only tier available until * commit-stream linking upgrades it; the label stays on so a chain is never presented as dataflow * truth (see plan risk register). */ export declare const EventAttribution: { readonly WINDOW: "window"; }; export type EventAttribution = (typeof EventAttribution)[keyof typeof EventAttribution]; /** The web-perf metrics carried in an EventType.PERF event's `metric` field. */ export declare const PerfMetric: { /** Largest Contentful Paint (ms). */ readonly LCP: "lcp"; /** Cumulative Layout Shift (unitless, running sum). */ readonly CLS: "cls"; /** A long task blocking the main thread (ms). */ readonly LONGTASK: "longtask"; }; export type PerfMetric = (typeof PerfMetric)[keyof typeof PerfMetric]; /** Which input path executed an action — native (CDP/Playwright) vs synthetic dispatchEvent. */ export declare const InputMode: { readonly REAL: "real"; readonly SYNTHETIC: "synthetic"; }; export type InputMode = (typeof InputMode)[keyof typeof InputMode]; /** * Why a pointer action ran SYNTHETIC even though a real-input provider is configured. Attached as * `inputModeReason` so a real→synthetic fallback is never silent (field bug #2) — the agent can * tell "I couldn't locate the element" from "the page isn't correlated to a CDP target". */ export declare const InputModeReason: { readonly NOT_POINTER: "not-a-pointer-action"; readonly SYNTHETIC_CLICK_PREFERRED: "synthetic-click-preferred"; readonly PAGE_NOT_CORRELATED: "page-not-correlated-to-a-cdp-target"; readonly ELEMENT_NOT_LOCATABLE: "element-not-locatable"; readonly DRAG_TARGET_UNRESOLVED: "drag-target-unresolved"; readonly PROVIDER_DECLINED: "provider-declined"; readonly PROVIDER_ERROR: "provider-error"; /** * No real-input provider is configured at all, and the caller explicitly asked for one with * `native:true`. Only emitted on that explicit ask: without a provider EVERY action is synthetic, * so annotating all of them would put a reason on the most-used tool in the product for no gain. * An agent that asked for a trusted click, though, has an expectation to correct — and this is * permanent for the session, not a transient downgrade, so it should stop asking. */ readonly NOT_CONFIGURED: "real-input-not-configured-for-this-session"; }; export type InputModeReason = (typeof InputModeReason)[keyof typeof InputModeReason]; /** Best-effort caveats attached to action results so the agent can interpret a no-op. */ export declare const ActionWarning: { readonly HOVER_NATIVE_ENTER_LEAVE: "target has enter/leave handlers; synthetic hover may not trigger them — expect no state change"; /** real-input provider was available but failed; the action fell back to synthetic dispatch. */ readonly REAL_INPUT_FELL_BACK: "real-input provider was available but failed; fell back to synthetic dispatch"; /** * The click point was covered by another element. Synthetic dispatch still delivered the event to * your target, but a real user could NOT click it — treat the target as visually blocked, not * actionable. Scroll it into a clear area or dismiss the overlay on top. */ readonly CLICK_OCCLUDED: "target is visually occluded by another element; a real user could not click it (synthetic dispatch still delivered the event) — dismiss the overlay or scroll the target clear"; /** * A document-key press (Escape, Tab, a modifier shortcut) was dispatched at the focused * element or the document. No named target was given, so the effect must not claim one. */ readonly GLOBAL_PRESS: "press landed on the focused element or document — no named target was given, so this effect does not claim one"; }; export type ActionWarning = (typeof ActionWarning)[keyof typeof ActionWarning]; /** Failure modes when Reticle launches/drives its own browser (`reticle drive`). */ export declare const DriveErrorCode: { readonly PLAYWRIGHT_MISSING: "playwright_missing"; readonly LAUNCH_FAILED: "launch_failed"; readonly NAVIGATE_FAILED: "navigate_failed"; }; export type DriveErrorCode = (typeof DriveErrorCode)[keyof typeof DriveErrorCode]; /** Human-facing message when the optional playwright dep is absent. */ export declare const DRIVE_PLAYWRIGHT_MISSING_MSG = "reticle drive needs the optional 'playwright' package \u2014 install it: pnpm add -D playwright && npx playwright install chromium"; /** Actions the executor can perform against a ref. */ /** * The console levels an agent can filter by, DERIVED from the console EventTypes rather than * retyped. `reticle_console { level }` matches by building `console.${level}`, so any list written * out by hand is one rename away from filtering everything into an empty result — which reads as * "no errors on this page". */ export declare const CONSOLE_LEVEL_PREFIX = "console."; export declare const CONSOLE_LEVELS: string[]; export declare const ActionType: { readonly CLICK: "click"; readonly DBLCLICK: "dblclick"; readonly HOVER: "hover"; readonly FOCUS: "focus"; readonly BLUR: "blur"; readonly FILL: "fill"; readonly TYPE: "type"; readonly CLEAR: "clear"; readonly SELECT: "select"; readonly CHECK: "check"; readonly UNCHECK: "uncheck"; readonly SUBMIT: "submit"; readonly PRESS: "press"; readonly UPLOAD: "upload"; readonly SCROLL_INTO_VIEW: "scrollIntoView"; /** * Scroll a container (or the document) by a delta, in either axis and EITHER DIRECTION. * * `scrollIntoView` puts a known element on screen; this is the gesture for looking around when you * do not know what you are looking for yet — back up to what scrolled past, or move sideways on a * wide table. Until now the only scroll available stepped DOWN, so a page could be walked in one * direction and never back, and `inspect` reported `scrollLeft`/`scrollWidth` that nothing could act on. */ readonly SCROLL: "scroll"; readonly DRAG: "drag"; /** * A TOUCH tap: pointerdown(touch) → touchstart → touchend → pointerup → click. * * Not a click with another name. A handler bound to `touchstart`, or one that branches on * `event.pointerType`, never runs for a mouse click — so mobile-web behaviour, swipe/press * affordances and touch-only controls were undriveable. `args.holdMs` makes it a LONG PRESS, the * gesture behind context menus and reorder handles on touch. */ readonly TAP: "tap"; /** * Page zoom, as a fraction (1 = 100%). * * Real browser zoom is not scriptable from inside the page, so this needs a driven browser and * REFUSES without one rather than faking it with CSS — see the note on `hover`, which refuses for * the same reason. A layout that breaks at 200% is a real and common defect; a CSS transform that * merely looks like zoom would report it caught while the layout viewport never changed. */ readonly ZOOM: "zoom"; readonly WEBMCP: "webmcp"; }; export type ActionType = (typeof ActionType)[keyof typeof ActionType]; /** Why an action's settle wait ended without a real animation frame. */ export declare const SettleReason: { readonly TIMEOUT: "timeout"; readonly THROTTLED: "throttled"; }; export type SettleReason = (typeof SettleReason)[keyof typeof SettleReason]; /** Outcome reasons for a bounded component-state read. Store reads never use these. */ export declare const ComponentStateReason: { readonly UNAVAILABLE: "component-state-unavailable"; }; export type ComponentStateReason = (typeof ComponentStateReason)[keyof typeof ComponentStateReason]; /** * Result of a component-state read attempt, discriminated on `ok`. Crosses * browser -> bridge -> agent as `result.component`, so the contract lives in protocol. * Always JSON-serializable: hook values are sanitized (no functions/DOM nodes/cycles). */ export interface ComponentStateResult { ok: boolean; reason?: ComponentStateReason; /** Component display name, when known. */ component?: string; /** Positional, JSON-safe hook states. */ hooks?: unknown[]; /** * Present ONLY when `hooks` is a PROJECTION — effect entries were removed. A trim is never silent: * without this, a hook list short by three entries reads as the component's complete hook list. */ truncation?: { droppedItems: number; note: string; }; } /** Element states the assertion engine can check. */ export declare const ElementState: { readonly VISIBLE: "visible"; readonly HIDDEN: "hidden"; readonly ENABLED: "enabled"; readonly DISABLED: "disabled"; readonly CHECKED: "checked"; readonly EXPANDED: "expanded"; readonly FOCUSED: "focused"; readonly PRESENT: "present"; /** * Inside the viewport right now (getBoundingClientRect intersects the window). Distinct from * `visible`, which folds only aria-hidden/[hidden]/display/visibility/opacity and so is already * true for content below the fold of a scrolling container. Without this, `scrollIntoView` is * ungradeable: the target satisfied `visible`/`present` before the scroll, so act_and_wait * returns already_true. (#398) */ readonly IN_VIEWPORT: "inViewport"; }; export type ElementState = (typeof ElementState)[keyof typeof ElementState]; /** Query strategies, aligned with Testing Library semantics. */ export declare const QueryBy: { readonly ROLE: "role"; readonly TEXT: "text"; readonly LABEL: "label"; readonly PLACEHOLDER: "placeholder"; readonly TESTID: "testid"; readonly ALT: "alt"; /** Resolve by component identity / source location (auto-anchors — addresses any element with * no hand-added testid). Pair with ElementQuery.component and/or.source. */ readonly COMPONENT: "component"; }; export type QueryBy = (typeof QueryBy)[keyof typeof QueryBy]; /** Commands the bridge sends to the browser SDK (the `name` field of a CommandMessage). */ export declare const ReticleCommand: { readonly SNAPSHOT: "snapshot"; readonly QUERY: "query"; readonly MATCH: "match"; readonly INSPECT: "inspect"; readonly ACT: "act"; readonly ACT_SEQUENCE: "act_sequence"; readonly ANIMATIONS: "animations"; readonly NARRATE: "narrate"; readonly CLOCK: "clock"; readonly CAPABILITIES: "capabilities"; readonly STATE_READ: "state_read"; /** Read localStorage / sessionStorage / readable cookies (sensitive keys redacted). */ readonly STORAGE_READ: "storage_read"; /** scroll a ref's nearest scrollable container by ~a viewport (virtualized lists). */ readonly SCROLL: "scroll"; /** Session lifecycle: agent tunes the presenter session (e.g. idle-end timeout) for the app's needs. */ readonly SESSION_CONFIG: "session_config"; /** * Live-control: bridge → browser. Pushes the current session state to the panel so an * AGENT-driven pause/end keeps the presenter in sync. `args: { state, text? }`. */ readonly PRESENTER: "presenter"; /** * Bridge -> browser: the user's own impact record, so the HUD can show what Reticle has done for * them without the page asking for it. `args: { snapshot: ImpactSnapshot }`. Local data on a * local socket - it is the same file the report is stored in, not a network fetch. */ readonly IMPACT: "impact"; /** * Ask the DESKTOP shell to photograph its own window and return `{ png: }`. * * A desktop webview has no CDP endpoint, so pixels must come from the runtime itself. Electron's * `webContents.capturePage()` reads the window's backing store, which is why this beats capturing * a screen region: it is correct even when the window is behind the editor, and needs no * screen-recording permission. Answered only when the app installed the capture helper. */ readonly CAPTURE: "capture"; /** Navigate the page to a new URL. `args: { url: string }`. */ readonly NAVIGATE: "navigate"; /** Reload the page. `args: { hard?: boolean }` — hard clears the cache via location replace trick. */ readonly REFRESH: "refresh"; /** * Bridge → browser: the saved flows the human can replay from the panel. * `args: { flows: [{ name, start? }] }` — `start` is the first step's testid anchor, a page hint the * HUD uses to show a flow only where it can begin. Absent when the first step isn't testid-anchored. */ readonly FLOWS: "flows"; }; export type ReticleCommand = (typeof ReticleCommand)[keyof typeof ReticleCommand]; /** Presenter intent shown to the human watcher: is the agent reading or acting? */ export declare const PresenterMode: { readonly IDLE: "idle"; readonly READING: "reading"; readonly ACTING: "acting"; }; export type PresenterMode = (typeof PresenterMode)[keyof typeof PresenterMode]; /** Snapshot rendering modes. */ export declare const SnapshotMode: { readonly FULL: "full"; readonly INTERACTIVE: "interactive"; readonly STATUS: "status"; }; export type SnapshotMode = (typeof SnapshotMode)[keyof typeof SnapshotMode]; /** Top-level envelope discriminator for messages on the WS channel. */ export declare const MessageKind: { readonly HELLO: "hello"; readonly COMMAND: "command"; readonly COMMAND_RESULT: "command_result"; readonly EVENT: "event"; }; export type MessageKind = (typeof MessageKind)[keyof typeof MessageKind]; /** * WHY a lease came back `ready: false` — the two situations that were one word. * * `ready: false` meant exactly one thing on the mint path: the SDK never dialled in, so the app * probably does not embed `@reticlehq/core`. A REUSED lease can fail readiness a second way, and it * is the opposite problem: an SDK did dial in, and has since stopped answering. The next action * differs — check the install versus recover the tab — so the two get names rather than sharing a * bare `false`. */ export declare const LeaseNotReadyReason: { /** No SDK dialled in within the wait. The install is the thing to look at. */ readonly SDK_NEVER_DIALLED: "sdk_never_dialled"; /** One dialled in and stopped answering: the tab is attached but wedged. */ readonly SDK_STOPPED_ANSWERING: "sdk_stopped_answering"; }; export type LeaseNotReadyReason = (typeof LeaseNotReadyReason)[keyof typeof LeaseNotReadyReason];