Imported from p697/clawket (
packages/bridge-runtime/AGENTS.md). Install upstream withnpx skills add p697/clawket --skill bridge-runtime. Copyright stays with the author.
Bridge Runtime
OpenClaw, Hermes, and local-model runtime implementations for the Clawket Bridge. Keep backend identity separate from Relay transport state.
Module Map
Codex native roster previews request summary turns, never full tool transcripts; full history remains an explicit history operation. Local lifecycle authentication must remain usable after native failure, while ordinary client handshakes still require native health. See ../../docs/3.0/connection-incident-2026-09-28.md.
| Path | Responsibility |
|---|---|
src/protocol.ts |
Bridge control frames and connect metadata parsing |
src/frame-limit.ts |
Shared 8 MiB WebSocket frame boundary |
src/relay-session.ts |
Backend-neutral Relay connection attempts, health evidence, heartbeat timeout, and reconnect backoff state |
src/openclaw.ts |
Installed OpenClaw discovery, credentials, setup-code, permissions, and diagnostics |
src/openclaw/skill-documents.ts |
Scoped OpenClaw SKILL.md read/write compatibility for authenticated local Gateway client channels |
src/openclaw/runtime.ts |
OpenClaw BridgeRuntime, on-demand Gateway lifecycle, and Bridge-owned connect metadata termination |
src/hermes/index.ts |
HermesLocalBridge assembly, lifecycle, and request dispatch |
src/hermes/http-server.ts |
Local Hermes HTTP/WebSocket server, authentication, health, and connection handling |
src/hermes/session-store.ts |
Bridge-owned session metadata and session-ID rotation |
src/hermes/native-sessions.ts |
Read-only native SessionDB listing/history and history cursors |
src/hermes/usage-ledger.ts |
Clawket usage/cost ledger plus read-only native usage reconciliation |
src/hermes/commands.ts |
Global /model, /thinking, /reasoning, and /fast behavior |
src/hermes/cron.ts |
Hermes cron validation and command mapping |
src/hermes/management.ts |
Hermes skills and other management surfaces |
src/hermes/stream-mapping.ts |
Image input, /v1/runs streaming, protocol events, and active-run cancellation |
src/hermes/python-runner.ts |
Hermes Python resolution and isolated subprocess execution |
src/hermes/gateway-process.ts |
Clawket-managed gateway spawn and its ownership evidence |
src/hermes/relay.ts |
Hermes Relay transport, probes, reconnect, and runtime health |
src/hermes/internal.ts |
Hermes-only constants, normalizers, and module composition helpers |
Keep each Hermes implementation file and each Hermes test file at or below 1,200 lines. Split by responsibility; do not compress formatting or merge tests to meet the limit.
Session and Storage Boundaries
- Open native SQLite databases with URI
mode=roandPRAGMA query_only = ON. Native sessions are read-only: do not rename, reset, delete, shadow, or tombstone them. - A native read failure degrades to Bridge-owned sessions and returns a warning; it must not make the Bridge unavailable.
- Persist only Bridge-owned session metadata. Do not copy native transcripts into the Clawket store. Reset cancels active work and rotates the Bridge session ID instead of mutating a native record.
- Stop, reset, and delete must deterministically release active runs and session resources owned by Clawket.
- Hermes session listings expose additive
lastActivityAt(null for no human message), paired with the last user or textual assistant preview. Rename/reset metadata and tool/system messages must not advance it. Preserve legacyupdatedAt; native reads remain read-only and transcript-free in persisted Clawket metadata. Mobile consumers on older Bridges retain their legacy fallback. - Preserve Hermes message deltas byte-for-byte, including whitespace-only tokens, newlines and indentation. Identifier normalizers that trim strings must never process streamed text; active history and final text must concatenate the same deltas.
Protocol Boundaries
- Advertise
bridge.capabilities.v2andhermes.multi-session.v2only while their complete behavior is implemented. Hermes handshake and health surfaces must agree; missing capability metadata keeps v1 behavior. - OpenClaw's closed RequestFrame schema does not accept top-level
meta. New Apps request Bridge capabilities through the existingconnect.params.capsarray so old Bridges remain usable. Accept that array and the pre-release top-level metadata format; consume/remove the latter before the Gateway leg. Advertise Bridge capabilities only on the matching negotiated success response. bridgeVersionmeans the running Clawket CLI package version. Normalize it before publication, include it only on a negotiated successful OpenClaw connect response or a Hermes health surface, and omit it for blank/invalid values and legacy peers. Never forward a Gateway-suppliedbridgeVersionor infer it from the Gateway'sserver.version.- Encode Hermes image attachments as one OpenAI-style user message whose content contains the text part followed by
image_urldata-URL parts; keep the current user turn out ofconversation_history. Reject malformed or unsupported input before starting a run. chat.abortowns anAbortControllerfor the matching/v1/runsrequest and emits one terminal rawchatevent withstate: 'aborted'; the App maps that state tochatAborted. Do not report success while leaving the run active.- Model flag parsing accepts both the legacy three-field and newer five-field Hermes tuples; consume only the needed fields and preserve global scope. Python subprocess failures must not put command scripts or payloads into client error messages.
- Validate all
hermes.cron.jobs.createparameters before invoking Hermes. Model selection remains global, never session-scoped. - OpenClaw diagnostics accept legacy
checksand currentfindingsreports, including nonzero CLI exits. Preserve finding severity and repair hints; never discard an unrecognized report into an empty issue list.
Python Resolution
Resolve the Hermes Python executable in this order:
- Explicit
hermesPythonPathruntime option. HERMES_PYTHON_PATH.<hermesSourcePath>/.venv/bin/python.<hermesSourcePath>/venv/bin/python.<hermesHomePath>/venvs/hermes-dev/bin/python.pythonon Windows,python3on POSIX.
Windows virtual environments use Scripts/python.exe in the same candidate order.
The shared Hermes Python runner must yield the Node event loop, bound duration/concurrency, cancel owned children on stop, and keep errors credential-free. Serialize whole configuration mutations while health bypasses their queue; cancellation during asynchronous history preparation or terminal hydration must not start work or restore stale replies. Skills helpers support both the legacy tools module and current split agent utilities through the shared compatibility preamble.
Subprocesses set HERMES_HOME and prepend hermesSourcePath to PYTHONPATH. Do not mutate the external Hermes checkout.
Installation discovery honors explicit runtime options and HERMES_SOURCE_PATH / HERMES_COMMAND. Otherwise prefer the current official ~/.local/share/hermes-agent checkout, retain the legacy ~/.hermes/hermes-agent fallback, and find the executable on PATH or at ~/.local/bin/hermes. CLI detection and runtime discovery share this resolver.
The managed Hermes API uses an explicit API key when supplied, otherwise a deterministic SHA-256 derivation of the persisted Bridge token, API URL and Hermes home scope. Pass it to the owned gateway as API_SERVER_KEY; never print it. Ordinary re-pairs reuse the persisted token; a new token (first pairing, reset) deliberately rotates the key, so do not persist a machine-wide key that outlives reset. Readiness probes use authenticated /v1/models, not the public health endpoint. Handle child spawn errors without crashing the Bridge.
An already-running API returning 401/403 is a credential mismatch. Replace it only with positive evidence: each spawn records ~/.clawket/hermes-gateway-owner.json (PID, OS start time, API URL, Hermes home; no credential), and a mismatch may start a replacement only while that record names the same live gateway run process in this scope. The replacement uses gateway run --replace, so Hermes retires its own previous instance; never signal the old PID or infer ownership from environment, command line or port. Without proof, keep the gateway and report the fixed /health hermesApiIssue (credential_mismatch or unreachable). Platforms without a readable process start time never auto-replace.
Cloud Relay sockets advertise relay.owner-pong.v1 and enable the fallback only when that socket's authenticated relay.ready also advertises it. For OpenClaw, Hermes, Codex, Claude Code and Pi, authenticated Relay full-client presence selects a 5-second probe cadence only after owner-pong negotiation; unknown or zero presence retains the original 10-second OpenClaw or 15-second SDK/Hermes cadence, and explicitly shorter configured intervals remain shorter. Presence is not foreground state or health. Validate counts as integers from 0 through 128 with no forwarded source/target identity; OpenClaw primary channel owners may instead use a bounded unique Relay diagnostic-UUID list. Reschedule only sooner from the existing timer anchor, so flapping cannot postpone a probe; never cancel or reset a pending cycle when the count changes. Dispose presence/timers with the socket. Local-model timing is unchanged. For these negotiated sockets, send an exact-nonce protocol ping first; after 1 second without proof, hedge with one application echo. Both share the original deadline capped at 5 seconds and bounded by monotonic and wall clocks; preserve explicitly shorter deadlines without adding a second timeout. A valid current-cycle, current-transfer-generation reply on either leg cancels the entire cycle; late losing replies cannot affect its successor. Once expiry is observed, latch socket failure before callback/disposal so buffered transfers cannot revive it. Healthy protocol pongs before the hedge produce no application traffic. Keep Bridge hedge start/confirmation diagnostics to one pair per socket per minute; failures stay immediate. Hermes must not dispatch an untracked protocol nonce while a transfer check already owns the cycle. Legacy peers keep their existing interval/timeout rules. This negotiated owner/channel policy does not change the mobile client-pong three-interval floor. Only the matching current-socket reply or a valid protocol pong confirms the round trip; ordinary traffic never extends this deadline. Stop/replacement clears pending timers and negotiation. Old Relays retain existing liveness behavior; negotiated OpenClaw owners and client channels track independent round-trip evidence while preserving their activity/routing state. The echo proves Relay transport reachability, never native backend readiness. A negotiated socket with a confirmed protocol pong or exact echo may spend one 0–250 ms jittered first retry after abnormal transport loss (1006), never exceeding the existing delay. Preserve the same instance identity; successor sockets need fresh round-trip evidence to gain that option. Explicit replacement, owner-lease conflicts, overload, authentication failures and manual stop retain their existing behavior. This reconnects transport only: never replay writes, start another native owner or treat transport evidence as backend readiness. Hermes keeps its matched protocol pong and independent local Bridge health probes. Ignore frames from replaced cloud sockets.
Negotiated relay.transfer-hint.v1 protects a same-socket large frame (128 KiB through 8 MiB) from the short owner-pong watchdog. A local send or server-authored relay.transfer-start starts one absolute 90-second allowance, bounded independently by monotonic and wall-clock deadlines. Use the earlier remaining deadline so suspension cannot pause the budget and wall rollback cannot extend it; latch observed expiry per window and advance its probe generation. Pending nonce deadlines use both clocks too, so a pre-sleep pong cannot beat a delayed timer. Later transfers advance a generation without extending that deadline; expired allowances stay locked until an independent exact round trip. Only a current-socket protocol nonce or application echo sent after the latest transfer generation can clear it early. Track hinted inbound frames separately from unconfirmed outbound transfers. Exact complete inbound-frame delivery ends only that inbound head-of-line allowance, without changing its generation, granting health or clearing concurrent uploads. After an admitted outgoing frame or completed incoming frame, coalesce a fresh exact echo at no more than one per second instead of waiting for the periodic cadence. Keep absolute-window expiry separate from active head-of-line protection; completion must not revive an expired window. Send callbacks and ordinary traffic are not health evidence. Apply the same policy to all five Agent owners and OpenClaw secondary channels; preserve old Relay timing and the public frame limit. Local native stdio/IPC requests do not inherit this network allowance.
An explicit current-owner replacement (4010/duplicate_socket or 4001/replaced_by_new_bridge) yields the Hermes Relay runtime until an explicit restart; otherwise two runtimes sharing persisted pairing identity can evict each other forever. Close local transport and cancel probes/retries, report the replacement, and keep ordinary network/dead/orphan-socket recovery. A late replacement event from an already retired socket must not stop its successor.
Ignore late frames from replaced local Bridge sockets too. Cloud status probes are abortable, bounded to 10 seconds and owned by the requesting socket; stop/replacement cancels them. Only an explicit hasBridge: false from the current probe may recycle Relay, never a late result or malformed status payload.
Only an explicit validated Relay client-count of zero may suppress local periodic tick/health events on the cloud leg. Preserve real messages, responses, local probes and Relay ping/pong. Reset presence to unknown on every new cloud socket so old servers and reconnects keep forwarding safely.
Test Boundary
npm run test:requiredis self-contained and must not inspect a developer home directory or external checkout.- Tests that inspect the external read-only Hermes checkout must use the
*.integration.test.tssuffix. They may verify behavior but may not modify source, state, tests, or scripts outside this repository. npm testis the broad suite and includes both self-contained and integration tests. Do not silently skip an external integration test or replace it with a stub to obtain a green run.- Keep tests beside the module they cover and add regressions for both OpenClaw and Hermes whenever shared Relay or frame behavior changes.
- Export only runtime contracts consumed outside their implementation module. Keep implementation-only helpers and record shapes private so the published surface does not grow accidentally.
OpenClaw handshake recovery
A forwarded challenge with client demand but no connect request has an 8-second watchdog. Suspend it during bootstrap credential issuance and cancel it on connect, disconnect, replacement, or stop. On expiry recycle the Relay owner transport so legacy cloud routing also resets; preserve reconnect backoff until authenticated health. Ignore events from replaced sockets. Log only stage, stable failure code and durations; never log challenge nonces or credentials.
Managed CLI runtimes opt into bridge.client-sockets.v1. With a supporting Relay, each authenticated client socket gets a child runtime with its own local Gateway handshake; the owner only coordinates lifecycle and restricted pairing. Retire children on socket incarnation removal, owner loss, or stop. Never reuse an authenticated Gateway socket for another client. Legacy runtime consumers and older Relays retain the v1 path.
In negotiated client-channel mode, pairing approve/reject must target a live child owning the request. If that child has gone, return an unavailable-request error; never fall back to the owner's unhandshaken Gateway. Legacy mode keeps its existing routing.
Local model runtime
src/local-model/ implements OpenAI-compatible streaming, one durable main conversation, live vision probing and the isolated Preview Relay owner. Persist before acknowledgement, reject conflicting idempotency keys, cancel the actual upstream request, and never replay interrupted runs. Model selection is global and excludes in-flight generation. Do not advertise Agent tools. See ../../docs/3.0/15-local-model.md.
Local-model health probes must not cold-load an unloaded llama.cpp router preset. Explicit selection owns the longer model-load timeout; ordinary probes fail with an actionable message.
Local-model Relay must bound application readiness after every WebSocket upgrade, retain backoff until authenticated relay.ready, ignore replaced socket callbacks, and clear all reconnect/readiness/heartbeat timers on stop. Diagnostic output is limited to fixed event names, stable error codes, retry counts and durations; never include close reason text or authentication URLs.
Relay network configuration
CLAWKET_RELAY_PROXY_URL explicitly enables HTTP(S) CONNECT for cloud Relay sockets across all three Bridge runtimes. Never apply it to local Gateway/model sockets or infer a proxy from unrelated environment variables. Validate once during runtime construction, redact invalid values, and bound the Relay handshake to 15 seconds.
OpenClaw Relay heartbeat expiry logs bounded idle/timeout/scheduler-delay durations and queued-frame count without payloads. A pong from a retired socket must not refresh the current watchdog or reset backoff. These diagnostics do not add cloud requests or relax expiry.
An OpenClaw secondary channel upgrade rejected with HTTP 409 refers to an unavailable client incarnation. Retire that child instead of retrying the same diagnostic ID; fresh owner client.sockets IDs create new children. Owner upgrade failures and transient non-409 failures retain bounded reconnect backoff. The incident log showed five futile 409 retries after the 13:36 UTC challenge watchdog; no retry is necessary for an identity the Relay has retired.
Managed Hermes API recovery
An explicit user stop must POST to the scoped Hermes /v1/runs/{run_id}/stop before ending its local event stream. Bound the request; unsupported or failed upstream stop must remain visible and must not falsely report cancellation. An accepted stop request is not proof all tool processes have exited.
After an API child owned by the current runtime exits, health checks may restart it with a 30-second initial cooldown and exponential backoff capped at five minutes. Coalesce health refreshes; guard asynchronous completions against stop. Never replace a live child, a warm externally owned API, or an API rejecting credentials unless the gateway ownership record above proves Clawket started it. Recovery is local and must not create additional Relay probes.
Hermes /v1/runs timestamps and durations use seconds; convert them to the millisecond protocol once at the stream boundary. Native tool IDs alias to live Bridge IDs during history reconciliation, including still-running calls when name, complete arguments and timestamp match uniquely; never guess between ambiguous calls. Persist only bounded identity metadata (512 aliases per session), validate on load and clear on reset; do not persist duplicate transcript content. Never emit a second tool row solely because native persistence used another ID, including after a Bridge restart.
Hermes history includes session-scoped hasActiveRun and the existing inFlightRun snapshot (run ID, partial text, start time, abortability). Read live ownership after asynchronous history/model reads so a finished or cancelled run cannot be resurrected. Keep these snapshots in memory; they add no polling or persisted transcript copy.
Hermes history may include additive toolCallAliases (native ID to live ID) from the bounded confirmed alias store, restricted to tools represented on that page. This lets newer clients reconcile older cached identities; older clients may ignore the field. Never infer aliases from truncated previews.
OpenClaw skill documents
Legacy bootstrap issuance must inspect native credential storage read-only. If SQLite owns device_bootstrap_tokens, return the existing bootstrap error so old Apps can use their QR token/password and normal device approval; never write unused JSON credentials or modify SQLite. Keep official mobile setup and JSON-only hosts unchanged. Load the prefix-only node:sqlite builtin lazily through createRequire so the Node 20-targeted CLI bundle preserves its name.
Only negotiated isolated full-client channels to a loopback Gateway may supply missing skills.get / skills.content.update methods in the successful handshake's features.methods. Preserve native methods and leave remote/legacy forwarding unchanged. Each operation resolves the key through that same authenticated socket's Agent-scoped skills.status; never trust client paths or another session's report. Read permission is mandatory; writes additionally require operator.admin and a nonbundled workspace/managed skill. Only the default SKILL.md is writable. Auxiliary scripts/references are read-only, resolved beneath the same authenticated skill directory; reject hidden/unlisted paths, traversal and symlink components. Listing is bounded to 512 visited entries and five directory levels; validate the opened inode again before returning content. Bound documents to 1 MiB and four in-flight status lookups with ten-second deadlines; reject symlinks, hardlinks, non-files, invalid UTF-8 and binary content. Preserve exact source, replace writes atomically, redact filesystem errors, and dispose pending work on challenge, replacement, disconnect or stop. No cloud or Hermes source changes are required.
Close the read descriptor before atomically replacing a skill document so Windows can rename the temporary file. Keep descriptor inspection inside its cleanup guard and revalidate the source inode before replacement.
Hermes mobile workflows
Native skill installation uses the installed Hermes Hub with an explicit source and owner, retains its security scan, never forces overwrite, and verifies the installed status before returning success. Do not modify the external checkout. run-control.ts negotiates /v1/capabilities and restricts steering to the exact Bridge-owned active run and session; do not fall back to a new run or replay an uncertain request. Every health/handshake surface must publish the same negotiated capabilities.
Serialize native skill installation and reject existing Hub records or target directories, including manual installs. Advertise installation only with the native target-validation hook. A subprocess-local compatibility shim qualifies only the exact ClawHub resource/download requests with the requested owner; require matching returned owner metadata and verify the installed directory rather than its display name. Preserve native quarantine/scanning and never treat an older lock entry as a successful new install.
Skill source read/write payloads preserve every character, including leading/trailing whitespace and final newlines. Identifier trimming helpers must never normalize document content; otherwise version restore and conflict checks falsely reject the App's own saves.
Resolve Hermes Hub provenance by the contained install path, not a display name. Hub removals must use the native Hub uninstaller so its registry and audit remain consistent; manual skills retain native skill-manager deletion. Reject malformed, escaped or ambiguous registries before deletion, preserve native refusals, and never report an unknown deletion result as success.
Hermes documents require a positively detected native extractor. Bound count, decoded bytes, expanded Office archives and extracted text; never install converters during a request. Preserve the same enriched user content across native/local history for deduplication, while the App renders compact attachment names. Exact-run approvals require negotiated native support and a pending request ID; only acknowledge a decision after the matching native response. Retain unsuccessful requests, expire on run termination, and never resolve every request implicitly. Return accepted-but-unused steering as draft recovery, never replay it automatically.
Image sends retain only an in-memory image count beside clean local text. For history matching, project the native one-marker-per-image representation and keep the existing one-to-one boundary/time matching; never strip literal markers from user prose, merge repeated sends, or persist image bytes/native transcripts in the Bridge store.
Native Hermes model health
Advertise hermes.model-health.v1 only after the native configuration/auth/doctor modules import successfully. model.health returns bounded provider credential states and optional native doctor probe results, never keys, endpoint details or raw exception text. Reading does not probe; explicit probes are coalesced and rate limited. Keep the operation inside Clawket-owned code and do not modify Hermes source or configuration.
Hermes approvals use a null deadline when native events omit it; never fabricate a timeout. Native approval_not_pending / approval_not_active retires the exact request, while transport errors retain it. Cron local-only delivery is native local, not the client protocol's none; normalize legacy writes and repair that exact legacy value before an explicitly requested run. Native processed/skipped/blocked results and output headers are distinct from successful Agent execution; malformed outcomes fail closed and unknown output formats stay unknown.
Native Cron output reads are confined to regular, bounded UTF-8 files under one validated job directory; reject traversal, symlinks and hardlinks. hermes.cron-model.v1 requires native signature support for model/provider pins; clear pins explicitly and never change the global chat model. Native history exposes stable session-scoped message IDs. A native stopping response is only an acknowledgement: preserve the stream/active run and publish cancellation only after a confirmed terminal state or event.
After an event stream ends or fails, poll the exact native run status with abortable, bounded requests and capped backoff. Preserve active ownership until a confirmed terminal status; partial text and completed tools are never completion evidence. Stop/reset/runtime disposal must cancel recovery waits.
Native tool history must preserve structured failures, including nonzero exit codes and interrupted/cancelled results. Never hardcode persisted tool results to success or infer failure from ordinary prose containing error words.
When a native run event stream disconnects, exact-run status polling also recovers waiting_for_approval requests. Require matching run IDs in both envelopes, retain resolved-request tombstones for that active run, and never revive acknowledged consent from a stale snapshot. This does not claim process-restart recovery of unowned native runs.
When the installed native run handler positively supports session-history resume and the matching native session has history, omit explicit conversation_history: Hermes stringifies that legacy field and loses structured tool IDs. Preserve the old request for unverified versions and locally seeded sessions. Recover only the exact bounded legacy Bridge tool-call repr during read-only history projection; never mutate the native database or evaluate arbitrary text.
On-demand session files
clawket.files.list/read offers bounded assistant-referenced files from the same session under verified local workspaces (Hermes: configured local terminal cwd and outputs). Keep opaque expiring handles, per-session scope, regular-file/size/type checks, and mutation detection on every chunk. Never accept a caller-provided filesystem path, spool file bytes, or add cloud storage. OpenClaw exposes this only on authenticated isolated loopback Gateway channels with native history/workspace read capability. Dispose handles on channel shutdown; Hermes clears them on reset/delete/stop.
Session previews
Codex, Claude Code and Pi negotiate health.sessionCatalogSync: 1 for bounded sessions.sync; legacy sessions.list stays an array with its existing tolerance. Keep at most two immutable complete snapshots per service scope, each at most 8 MiB/10,000 rows, and all full pages/deltas at most 64 KiB. Page continuation reads only the frozen snapshot; unknown bases use a new first page and expired pages return an explicit marker. Failed, malformed or truncated native scans never publish removals or replace the last snapshot. Preserve known native history lookups through incomplete scans, with only bounded positive same-scope additions; complete scans or explicit confirmed management operations may remove lookup entries. Retained metadata never substitutes for current project/ownership checks or drives name/archive reconciliation. A successful create/rename/archive/reset/delete fences pending catalog refreshes; an obsolete read may join one fresh read, never publish old data or retry indefinitely. Frozen pages remain immutable. Pi may retain an individually unreadable known row only while complete directory enumeration still names its file, including rows outside the rolling read window. See ../../docs/3.1/session-catalog-sync.md.
Codex alone advertises sessionCatalogPageIndex: 1 for opt-in frozen page indexes. Generate at most 512 exact continuation offsets from the same complete snapshot; include the index within the first page’s 64 KiB budget or omit it for a near-limit first row. Ordinary requests, deltas, retained pages and other backend advertisements keep their existing contract. See ../../docs/3.1/session-catalog-sync.md.
Codex session descriptors publish optional model only when native/index metadata is a string. Omit null or other types without dropping the conversation, inventing a default, rewriting an existing index or changing native settings. Apply the same projection to full/legacy catalogs and indexed live/archive descriptors; newly indexed native metadata uses that optional-string boundary. This does not relax catalog identity, completeness, size or Mobile validation rules.
Local Codex/Pi/Claude WebSocket listeners bound unauthenticated plus authenticated sockets to 32. Reject cross-origin browser upgrades before native work while preserving absent Origin and React Native Android's same-endpoint HTTP(S) Origin. Token authentication remains mandatory; this policy does not encrypt LAN transport. Retired heartbeat sockets must release their liveness entries.
Codex, Claude Code and Pi session previews show the last visible user or assistant message (never tools, thoughts, system events or a native first-prompt/title shortcut). Normalize whitespace/Markdown to at most 160 Unicode characters; an image-only user turn uses đź“·. Keep preview activity tied to the chosen message. Native catalog enrichment is bounded to recently active sessions and cached by native update version; large Claude transcripts use a bounded read-only tail rather than full SDK parsing. A failed tail read must not break session listing. Claude previews remain ephemeral metadata, not persisted native transcripts. See ../../docs/3.1/session-previews.md.
Pi RPC runtime
src/pi/ owns independent Pi RPC processes and private sessions for an explicitly configured project. Preserve the installed Pi's configuration and trust; never add automatic approval flags. Native JSONL v3 sessions (including an explicit native session directory) are read-only and can only be copied into a private branch. Remote requests use opaque session IDs, never filesystem paths or arbitrary RPC commands. Ordinary extension questions are not execution approvals.
Persist input fingerprints before acknowledging, serialize session mutations, never replay uncertain prompts, and keep Pi stdin open across phone disconnections. agent_settled is terminal; agent_end is not. Stop releases only owned children. Real-Pi tests are explicit integration tests and use isolated credentials/state plus a deterministic local model endpoint; required CI tests do not require Pi or inspect user home state.
Pi’s landing record must publish kind: main together with the Agent’s mainSessionKey; additional private/native sessions remain direct. Free main-chat access must not depend on a paid history entitlement.
Pi reset rotates private storage and clears transcript preview/activity and stale model metadata while preserving acceptance fingerprints; old network retries must never repopulate the reset conversation.
While an accepted Pi extension command is waiting before agent_start, history projects its in-memory input after completed native entries so phone recovery anchors the pending run to the current turn. Retire that projection on agent start or settlement; never write synthetic commands into native Pi JSONL.
Pi Relay owner-lease conflicts (HTTP 409) retry every two seconds within the startup readiness deadline; other failures retain exponential backoff. Only relay.ready resets recovery state, and retired socket events cannot affect the replacement.
Claude Code Agent SDK
src/claude-code/ implements the independent Claude Code runtime using the official SDK and an explicitly selected installed CLI. Native discovery/history reads are read-only; an explicit send or model selection may resume a released imported session using its original ID/cwd. Require fresh native ownership evidence plus a machine-wide Clawket writer lock before starting; release imported processes after each settled turn. Retain native source, opaque key and disabled rename/reset/delete actions. Opaque mappings and acceptance fingerprints may be stored, transcripts and Claude credentials may not. Live ownership includes idle owners and unknown states fail closed. Keep native consent and question identities, respect abort signals, and declare only supported dialogs. See ../../docs/3.1/claude-code.md; do not change another backend's process or native ownership protocol.
Model choices retain native aliases and optional resolved IDs. Exclude exact native model-switch and interruption envelopes from human chat history without stripping ordinary text discussing commands.
Default Claude executable discovery prefers the newest runnable Desktop Code host runtime in macOS/Windows user data, then PATH/native CLI. The GUI and VM guest are not SDK executables. Explicit commands override automatic discovery; selected-runtime version/startup failures must not silently retry another installation. Use that same executable for SDK sessions and native owner queries. Layouts, minimum version and explicit real-model verification are documented in ../../docs/3.1/claude-code.md.
Codex App Server
Native thread provenance does not imply a remote owner after an authorized local resume. Keep subsequent turns and settings on the owning App Server, publish ownership to followers, and repeat owner discovery after process restart. Settings-only resume uses the same ownership proof as continuation. A Bridge-created thread may recover after a proven pre-dispatch Desktop-broker failure only with matching idle history and an explicitly verified native version whose atomic writer lock rejects even an idle competing owner; imported threads and unknown versions still require explicit no-owner. Uncertain dispatch never opens another writer.
Cold direct sends must apply that same proof before persisting a receipt or dispatching a turn; recovery must not depend on opening the model picker first. A failed broker preflight may attempt only the audited atomic resume path, with native settings verification, and cannot turn unknown/active ownership into permission to send.
src/codex/ owns one stdio App Server per pairing configuration. Device pairing selects per-thread cwd from saved projects/native thread metadata; legacy project pairing retains its single cwd. Native thread history remains in Codex storage; only private metadata and prompt fingerprints belong to Clawket. Keep remote methods allowlisted and project IDs opaque. Use exact native turn IDs for steering, stopping and approvals. Never equate a stop acknowledgement or missing RPC response with completion. Preserve pending consent while the phone disconnects, retire it on authoritative resolution, and refuse unsupported interactions. Default new conversations to project sandboxing and one-time consent; full access requires explicit session-scoped user selection and authoritative native confirmation. Never silently bypass permissions or terminate another Codex client.
Never resume a Codex thread that has never submitted a turn after App Server restart: empty native rollouts are not durable. Recreate only that empty case, preserving chosen model; accepted or uncertain inputs retain their original native identity. Native reasoning selection changes session configuration without generating a slash-command prompt.
Codex preserves the selected native reasoning level across owned-process restart. Never rename/archive an unmaterialized native thread solely because its ephemeral ID was indexed; retain the local title until a real thread is available.
Completed native catalog reads reconcile the name and archive state of already indexed Codex conversations only for a positively returned matching thread/cwd. Explicit active/archived list membership is evidence; absence is not. Missing, null or empty native names do not erase a local label or replace it with the first prompt. Fence in-flight management changes and late catalog pages by record revision, including reset/delete, without changing run ownership, settings or prompt receipts. For materialized, inactive records missing from a complete active catalog, verify positive archived membership with at most two 100-item pages per refresh, shared across all candidate projects. Cache negative/unknown checks for 30 seconds with a bounded signature invalidated by candidate identities or management revisions. Do not scan archives unconditionally or read histories for management metadata.
Native collaboration-mode confirmation treats null developer instructions as selection of Codex's built-in default/plan preset; still compare the complete mode, model, effort and all other settings, and compare explicit instructions exactly. Accept preset expansion only after dispatch, never as proof that cached custom instructions satisfy a new null request. Preserve newer authoritative settings notifications after a rejected or uncertain write for truthful readback/follower snapshots; never report an unmatched write as applied or retain only the stale pre-write state.
Desktop settings IPC accepts audited v1/v2 semantics. Send the next-turn settings subset as v2; downgrade once to v1 only after an exact native pre-dispatch request-version-mismatch on the same live IPC connection. Never retry uncertain writes, mismatched responses or version-specific operations. Check supported conditional predicates inside the serialized settings mutation; reject unknown conditions and active-turn changes. Named permission profiles must pass native effective-profile confirmation, including when supplied by a Desktop turn request.
Native failed turns retain a stable system notice in both live completion and paginated history. Use the native turn ID and completion time, fixed allowlisted error copy, and never provider error bodies. Recognize the exact bounded ChatGPT unsupported-model refusal and offer model selection/update instead of an unchanged retry; unknown errors stay generic. A turn that is still active or has an unknown status is not a successful completion. Desktop turn settings confirmation and prompt dispatch share one session mutation queue so another client cannot insert a permission change between them.
Audited Codex cold resume does not inherit service tier or anonymous permission profiles automatically. Restore them from the latest matching native ThreadSettingsApplied event using a bounded read-only rollout tail; verify thread, cwd, provider and file stability. New permission choices use native named profiles. Restore a native named profile by its ID so a removed or managed-restricted profile fails explicitly; recognize old anonymous profiles only when they exactly match an audited standard policy. Unknown policies must not fall back to global Full access. Verify effective resume permissions before sending, and retain a send guard until an explicit permission selection resolves a mismatch. Never overwrite a newer native choice with cached Bridge state. A confirmed explicit speed preference may fill the first-turn persistence gap only when the complete matching native file proves no settings event exists. Missing, truncated or over-budget evidence is not proof of absence. Preserve native model inheritance and writer ownership checks.
Codex automatic executable discovery prefers a runnable desktop-bundled runtime in macOS ~/Applications or /Applications Codex/ChatGPT bundles, falling back to PATH only when no such runtime exists (owner decision 2026-09-30). Explicit commands remain overrides. Discovery and RPC startup share the resolver; the same owned App Server supplies the model catalog and new conversations. App presence alone is not readiness. Never fall back after a selected executable fails version/protocol checks, copy credentials, or attach to the desktop's stdio process.
Codex model selectors query the configured executable's native model/list on every request, consume bounded pagination and retain native picker visibility. Do not inject model names or use another installation's cache as a catalog. Supported desktop-bundled executables may have SemVer prerelease/build suffixes; version recognition is not a substitute for protocol readiness or integration tests. Never silently replace the user's selected executable to obtain more models.
Codex Relay owner-lease conflicts do not increment network-failure backoff. Bound readiness separately from socket-open and allow a lease expiry plus transient handshake failure; keep the CLI parent's startup deadline longer than Relay readiness.
Codex device continuity uses versioned local Desktop IPC with bounded frames and snapshots. Its private local frames are capped at 16 MiB, with a 7 MiB history budget and a final projected-state byte check to preserve native duplicated image inputs. This does not change the public 8 MiB Relay/App frame limit; oversize history must fail explicitly without terminating an active native task. Follow only requested conversations; reject foreign hosts, invalid patches and unknown revisions. Preserve canonical turn IDs and the start-turn result envelope. Never retry an uncertain write through another owner. Native metadata stays read-only; original-thread continuation is distinct from branching. Group user-input fields by their original IDs, preserve options/custom input, and keep cancellation pending until native termination. Phone dismissal is never an answer. Only the exact pending command/file/permission request can be answered; unsupported or secret interactions fail closed.
Codex does not create a landing record at startup. Advertise entryMode: sessions with an empty mainSessionKey; all owned records, including former landing records, are ordinary deletable conversations. Preserve existing IDs/history. A sessionless model list reads the native catalog without creating or selecting a thread; model mutation requires an explicit session.
Claude device discovery supplements native session directories with bounded, read-only project keys from the standard ~/.claude.json; never expose configuration values or use this fallback across a custom CLAUDE_CONFIG_DIR. Explicit project pairings do not read the global project registry. Untitled Clawket-owned Claude chats take a bounded first-prompt title when accepting their first send; keep explicit/native titles and roll back metadata if acceptance persistence fails.
Independent SDK Relay runtimes reject valid requests beyond their bounded in-flight capacity with an explicit BRIDGE_BUSY response before native dispatch. Do not silently drop excess requests or turn a transient overload into a phone handshake timeout. Preserve socket-incarnation fencing for late results.
Claude, Codex and Pi publish question/approval attention in session metadata, including cold roster snapshots, and clear it on native resolution/termination. The shared interaction-attention projection is display-only and never authorizes a tool. Claude file consent shows the exact target and proposed contents/diff while returning the unchanged native input on one-time approval.
SDK Relay heartbeat termination logs fixed cause, pong idle duration, scheduler delay and queued byte count without credentials or payloads. Retain 15-second idle/unknown probes and replacement fencing; validated negotiated active-client cadence follows the shared rule above. Legacy peers recycle after three consecutive unanswered intervals; negotiated Codex/Claude/Pi owners use the independent protocol deadline and bounded application echo above. A current-socket pong or matching echo resets the miss budget. A delayed/recovered probe is distinct from a disconnect, and unexplained transport closures must not be relabeled as proven heartbeat failures.
Codex skill catalogs resolve optional session scope to an already authorized native/owned project. Reject unknown sessions and mismatched returned project rows; absence of session context retains the configured default project.
Codex Desktop IPC deadlines must outlast native owner discovery plus dispatch (currently 10 seconds each). Imported threads require explicit no-owner for local fallback; the verified native-writer-lock exception above is limited to Bridge-created threads and failure before dispatch. Routed timeouts, disconnects and generic handler failures retain uncertain dispatch. Do not clear the writer fence or resend after an ambiguous error.
After a Desktop start acknowledgement confirms the turn ID, reconcile an already received fresh terminal snapshot for that exact turn and current Bridge run. Fast authentication failures can precede the acknowledgement. Stale snapshots, other turns and incomplete status cannot end the run; reconciliation never dispatches another turn.
Claude metadata commits must flush the writable exclusive-create handle before atomic rename; reopening it read-only fails fsync on Windows. Preserve the old index on write/flush failure. Cross-platform IPC tests use Windows named pipes and platform-native path comparisons.
Claude service project scope uses realpathSync.native to match the catalog’s asynchronous native realpath; Windows 8.3 and full paths must not create different scope/cwd identities on restart.
Codex native RPC diagnostics emit only a fixed lifecycle/failure reason, pending count and optional frame byte count. Forward them to the Clawket-owned local log without methods, params, transcripts, stderr, native error strings, paths or IDs. Logging failures must not interrupt request settlement or process cleanup; a timeout retains uncertain dispatch and never authorizes replay.
Negotiated chat.promptStatus is a read-only lookup of a bounded, durable input fingerprint. recorded proves Bridge receipt only, never native execution/completion; unknown never authorizes automatic resend. Codex echoes use exact native client message IDs. Pi may persist an exact native entry mapping only with the owned process/session, pre-dispatch leaf, ordered acknowledgement and a single unambiguous native user entry; any branching, extension ordering, persistence or bounded-read uncertainty leaves the receipt unresolved. Store identity metadata only, never duplicate transcript text.
OpenClaw attachment transfer
src/openclaw/artifacts.ts terminates negotiated artifact reads only on authenticated isolated local Gateway client channels. Obtain transcript authority through native artifacts.get/download; never accept caller paths/URLs, forward owner credentials, follow redirects or persist payloads. Preserve the 8 MiB frame limit with inline-size preflight; managed-file buffers, chunk sizes, timeouts and disposal are bounded as documented in ../../docs/3.1/openclaw-attachments.md. Keep existing workspace-file and other backend behavior unchanged.
SDK and Hermes delivered attachments
src/delivered-artifacts.ts projects explicit assistant file references inside the session's verified project (Hermes: configured local cwd/outputs). Exclude fenced examples, user/tool arguments, private paths and filesystem links. Only a successful native Codex image-generation result or an assistant image block may supply inline image bytes; ordinary tool screenshots are not delivery. Negotiate artifact operations separately from image-input support, retain opaque session-scoped IDs, bounded chunk reads and mutation checks, and retire handles on reset/delete/stop. Keep cloud storage unchanged; RAM buffers and native-page recovery metadata are bounded. Keep legacy wire text intact; only the additive artifactDisplayText projection strips links actually converted to attachments. See ../../docs/3.1/backend-attachments.md.
Pi history retains legacy inline assistant image bytes unless the reader explicitly sends artifacts: true; new-client handle projection must not remove an older client's existing image display.
Session list activity
Codex/Claude/local-model negotiate sessionActivity: 1 and bound sessions.activity to 32 authorized catalog keys. Codex temporary Desktop follows share the existing 64-thread limit, renew for 45 seconds and independently expire stale evidence; they cannot mutate indexed records, emit chat/history content or acquire a writer. Releasing a temporary follow preserves an opened chat's follow. Codex direct activity events are window/lease-gated per requesting socket; Relay uses only existing origin-routed RPC responses so a legacy active phone cannot receive the new event. Claude uses its official read-only owner roster; idle is still owned. See session activity for source and lifecycle boundaries.
Codex profile.* management is a bounded explicit allowlist, independent of thread ownership. Native defaults require user-layer CAS, canonical config comparison, no session hot reload and confirmed readback; uncertain writes never retry. Documents use retired-on-restart opaque handles, native-discovered scope, 128 KiB regular UTF-8 single-link files, hash conflicts and atomic writes. MCP/plugins expose read-only sanitized projections; absent quota remains unknown. See ../../docs/3.1/codex-profile.md and the explicit isolated test:codex-profile-integration command.
Update admission
Runtime services expose actual Bridge package version additively in authenticated health. UpdateAdmission counts in-flight dispatch before checking active native/queued work and closes admission synchronously before a private local updater stop. Keep the shared local-model admission across direct and Relay services. Never expose update control through phone/Relay RPCs. See ../../docs/bridge-updates.md.
