Full Transcript
In this section, we'll explore the internal data flow, focusing on rendering envelopes, communication protocols, and observability. Understanding these components is key to managing data efficiently within complex systems. In this section, we'll explore the overall structure of Open Claw's message processing pipeline and how to effectively use this internal chapter to extend or modify its behavior. Open Claw treats each message as a pipeline, starting with parsing rich text into an intermediate representation, then chunking large content, rendering for specific channels, wrapping with delivery metadata, validating with protocol schemas, and finally emitting UX signals and telemetry. This clear separation helps keep responsibilities organized and maintainable. The intermediate representation centralizes semantic structure for consistent rendering across channels. Chunking happens before rendering to respect size limits. Envelopes handle delivery layer concerns like timestamps without altering message content. Validation ensures protocol compliance before sending, and telemetry signals track usage and typing events separately. Later in this chapter, you'll find concrete extension points that let you customize parsing, chunking, rendering, envelope formatting, protocol validation, typing indicators, and usage tracking. This function converts raw Markdown input into a structured intermediate representation, preserving key elements like mentions, code blocks, and media as distinct nodes for downstream processing. Chunking breaks the intermediate representation into manageable pieces based on token or byte limits, ensuring messages fit channel constraints while maintaining session continuity. This renderer converts each IR chunk into channel safe markup using marker hints to handle fallbacks and attachments, ensuring consistent display across different platforms. Envelope formatting manages timestamp display using UTC storage and applies time zone offsets at delivery time, preventing stored transcripts from mixing display time formatting with raw data. The protocol schema registry defines the structure of gateway frames and events. Running PNPM protocol check validates your changes against these schemas to prevent contract regressions. Typing indicators are controlled by configuration settings that determine how and when typing events are emitted during the run life cycle, enhancing user experience without cluttering renderers. Usage hooks collect metrics on cache reads and writes, token consumption per provider, and expose snapshot endpoints to monitor resource usage and cost effectively. Here's a concise checklist to guide your implementation from parsing to telemetry integration, ensuring a robust and maintainable message pipeline. Start by parsing input into the intermediate representation and verify that basic constructs like mentions and code blocks round trip correctly to maintain data integrity. Implement chunking tailored to the size constraints of your target channels to ensure messages are delivered without truncation or errors. Create renderers that consume IR chunks and produce safe markup along with any necessary attachments, adapting to each channel's capabilities and restrictions. Encapsulate rendered content in envelopes, formatting timestamps from UTC, and including time zone metadata to ensure accurate display for recipients. Use the type box schemas to validate outgoing frames and integrate protocol checks into your continuous integration to catch issues early. Emit typing indicators and update usage counters at key lifecycle events like run, enqueue, and completion, keeping these concerns separate from rendering logic. Before you start, keep in mind some important warnings about time zone handling and schema validation to avoid common pitfalls. Always store timestamps in UTC and apply time zone formatting only at delivery time to prevent inconsistencies in stored message data. Treat any failures in schema validation as critical blockers, since they indicate protocol contract violations that must be resolved before deployment. In this section, we'll explore how markdown content is processed and rendered across different platforms. We'll see how a shared intermediate representation helps maintain consistency and simplifies rendering. Agents and tools generate markdown, but each platform has unique rendering capabilities. Open Claw addresses this by parsing markdown once into a compact intermediate representation or IR, which preserves text and formatting spans. Then, renderers convert this IR to the specific channel format, avoiding repeated parsing. Let's look at a simple markdown example used by agents and tools. This sample includes bold text and a hyperlink. Here is the exact markdown input. It says "Hello world" with "world" in bold followed by a link labeled "docs" pointing to the Open Claw documentation. The markdown parser converts this input into a JSON-based intermediate representation. This IR separates plain text from formatting spans like styles and links, making it easier to render consistently across channels. This JSON shows the IR. The plain text is stored in style spans mark world as bold from character six to 11. The link span covers docs from character 19 to 29 with its URL. This structure guides rendering on any platform. Now, let's review some important rules about the IR and what they mean in practice for rendering and testing. The markdown is parsed once into the IR, which contains text and spans. All channel renderers use this IR instead of re-parsing markdown, ensuring consistent behavior and easier testing across platforms. Offsets and spans use UTF-16 code units because some platforms, like Signal and mobile APIs, index text this way. Using other offsets like code points or bytes can misalign styling, especially for characters outside the basic multilingual plane. The parser does not automatically convert bare URLs into links. This prevents double linking issues because channel renderers apply their own link formatting according to their rules. Next, let's discuss how chunking the text works and how the IR preserves formatting across chunks. Chunking splits the plain text in the IR before rendering to ensure inline formatting spans aren't broken across chunks. Spans crossing chunk boundaries are sliced and reopened in the next chunk, preserving continuous formatting. Now, let's cover how different channels handle rendering and some important platform-specific considerations. Slack uses markdown tokens and requires preserving angle bracket tokens for mentions and links. It also has special escaping rules, so blindly escaping the whole string can break mentions. Telegram renders markdown as HTML tags. User content must be escaped outside allowed tags since unescaped characters like or and will break rendering. Use helper functions that emit only safe tags for formatting. Signal cannot carry HTML so it renders plain text plus an array of style ranges using UTF-16 offsets. Spoiler markers are parsed only for signal and produce spoiler style ranges. Other channels treat them as literal text. Table rendering varies by channel or account. You can convert tables into fence code blocks, flatten them into bullet lists, or leave them as markdown tables. This is configured per channel or account. This configuration example shows Discord converting tables to code blocks while the work account leaves tables untouched. This flexibility helps adapt to channel capabilities. Here's a step-by-step checklist to follow when adding or updating a channel renderer. First, parse markdown once to get the IR. Next, chunk the IR text while slicing spans properly. Then implement the renderer that maps styles and links to the channel format, reopening slice spans and applying escaping. Finally, integrate the renderer and add tests covering chunking and delivery. When adding tests, focus on key behaviors to ensure robust rendering across channels. Make sure tests verify that trailing newlines and fence code blocks are preserved after rendering. Tests should validate UTF-16 offsets for signal style ranges including examples with surrogate pairs to catch indexing errors. Verify that spoiler markers produce spoiler style ranges in signal but remain literal text in other channels. Confirm that links are not double linked since auto link is disabled in the parser and handled by renderers. Finally, let's review some important warnings to avoid common pitfalls in rendering. Never assume JavaScript string indices match API indices. Always convert to UTF-16 offsets when producing style ranges, especially for signal. Improper escaping can break Telegram's HTML rendering or disable Slack tokens. Always use channel-specific helpers instead of raw string replacements. Chunking must happen before rendering. Chunking rendered text risks splitting inline markup and breaking formatting. Following this pipeline ensures consistent formatting, reduces parsing bugs per channel, and makes testing straightforward by verifying IR correctness once, and then checking rendered outputs. In this section, we'll explore how Open Claw manages message envelopes and handles time zones. You'll learn how timestamps and routing hints are formatted and configured for clarity and consistency. Open Claw wraps every inbound message in a host local envelope that includes a compact, human-readable timestamp and minimal routing hints. This envelope is created by the gateway before the message reaches the agent or is stored. By default, timestamps have minute precision, providing a stable and readable time anchor in transcripts and model inputs. Here is an example of what a raw message envelope looks like in logs or transcripts. It shows the provider timestamp with time zone and a message text. This illustrates a typical envelope format, including the provider name, the date and time with time zone, followed by the actual message content. You can customize how these envelopes are formatted by adjusting settings in agents.defaults. Let's review the key configuration options and their possible values. The envelope time zone setting controls which time zone the envelope timestamps use. You can choose UTC, the host local time zone, the user's time zone, or specify any valid IANA time zone string like America/Chicago. The envelope timestamp option toggles whether the absolute timestamp is included in the envelope. Turning it off removes the timestamp portion. The envelope elapse setting controls whether an elapse time suffix, like +2M, is appended for recent follow-up messages, helping to track relative timing. Here's a copy-pastable JSON snippet that sets the envelope time zone to local, enables timestamps, and includes elapse time suffixes. This configuration snippet sets the envelope time zone to local, turns on the timestamp, and enables the elapse time suffix. Adjust these as needed to fit your environment. Let's look at some examples showing how different envelope settings appear in practice, illustrating the impact of time zone and timestamp options. This example shows the default envelope format using the host local time zone, providing a familiar timestamp for local users. Here, the timestamp includes the date, time, and the PST time zone label, along with the provider and user information. This example uses a fixed IANA or GMT style time zone label, which is useful for clarity across different regions and daylight saving changes. Notice the timestamp here includes the GMT+1 offset, making the time zone explicit and unambiguous. This example shows an envelope with an elapsed time suffix and then ISO 8601 UTC timestamp, which is very precise and useful for tooling and logs. Here, the +2m indicates this message was sent 2 minutes after the previous one, and the timestamp is an ISO 8601 UTC format. Open Claw normalizes timestamps from providers by attaching two consistent fields, timestamp ms and timestamp UTC. This helps avoid ambiguity and simplifies programmatic use. The timestamp ms field provides the time as epic milliseconds in UTC, which is ideal for precise calculations and comparisons. The timestamp UTC field gives the time as an ISO 8601 formatted string in UTC, which is human-readable and standardized. For any programmatic logic, it's best to use timestamp ms or timestamp UTC rather than parsing envelope text. This avoids errors from provider differences and daylight saving time issues. If you want the model to reason explicitly in a user's local time zone, set agents.defaults.user_timezone to the appropriate IANA time zone string. This example sets the user's time zone to America/Chicago, ensuring that time-related reasoning aligns with that local time. Be careful. If you set envelope time zone to user without defining user time zone, Open Claw will fall back to the host time zone at runtime. This might not match the true user's preferred time zone. Finally, the system prompt includes a current date and time section that respects agents.defaults.time_format and time zone settings. Choose user or an explicit IANA time zone for user-relative scheduling, or use host local or UTC for server-tied transcripts. In this section, we'll explore how type box schema is defined the structure of messages exchanged over the gateway websocket protocol. You'll see how the client and server communicate using strictly typed frames. The client and gateway communicate through a small set of well-defined frames. The conversation starts with the connect request from the client, followed by a hello okay response from the server. After that, the server can send events and the client can send further requests. Here's the minimal handshake and a simple health check sequence. This diagram shows the message flow. The client sends a connect request, the server replies with hello okay, then emits an event named tick. The client requests a health check and receives a health response. This sequence represents the basic protocol interaction. The connect frame must be valid strict JSON and is the first message the client sends. The gateway will reject or ignore any other frames until this connect handshake succeeds. Here is an example connect request frame. It specifies the protocol version range and client metadata like ID, display name, version, platform, mode, and instance ID. This information initiates the session. A successful hello okay response includes the negotiated protocol version, server version, and connection ID, a features list of supported methods and events, a snapshot of current state, and policy hints like max payload size. The features list is a conservative capability hint, not a full API contract. This is the exact hello okay response frame. Note the protocol version, server info, supported methods and events, and policy parameters. This frame confirms the connection and server capabilities. Once connected, the client can send requests like health checks to verify server status. This keeps the connection alive and confirms responsiveness. This is a minimal health request frame. It simply asks the server to report its health status. The server responds with a matching response frame indicating the health status. This confirms the server is operational. Here is the health response frame. It shows the request succeeded and the server is healthy. Server initiated events use a specific event frame format, including the event name, payload data, and a sequence number for ordering. This example event frame shows a tick event with a timestamp payload and a sequence number. Events keep the client informed of server-side changes. Here is a simple node.js client that connects to the gateway, performs the connect handshake, then sends a health request. It logs the health response and closes the connection. This example omits error handling and reconnection logic. This code creates a WebSocket client that connects to the gateway. On open, it sends a connect request with client info. When it receives a successful connect response, it sends a health request. Upon receiving the health response, it logs the payload and closes the connection. All frame payloads are defined using TypeBox schemas. TypeBox is a TypeScript first library that generates JSON schema, TypeScript types, and runtime AJV validator from a single source of truth. These are example TypeBox schemas defining parameters and results for a system echo method. They specify required fields and disallow extra properties for strict validation. The defined schemas are exported into a protocol registry, which maps method names to their respective parameter and result schemas. This centralizes schema management. Here the system echo parameter and result schemas are registered under their respective keys in the protocol map. You can derive TypeScript static types from TypeBox schemas using the static utility. This provides compile time type safety aligned with the runtime schema. These type aliases extract static TypeScript types from the system echo schemas for use throughout your code base. At runtime, AJV validators are compiled from the TypeBox schemas. These validators check incoming data before your handler logic processes it, ensuring data integrity. This line compiles an AJV validator for the system echo param schema TypeScript. Use this to validate request parameters before handling. Request handlers receive validated parameters and send responses. Handlers are registered in a registry keyed by method name for dispatching incoming requests. This example handler for the system that echo method extracts the text parameter and responds with an object confirming the echo. The respond function sends the response frame back to the client. Developers run code generators to produce protocol artifacts and verify that generated files are committed and consistent with the source schemas. The command pnpm protocol check run generation tasks and checks that generated protocol files match the committed versions in source control. This process generates JSON schema files and macOS Swift models, ensuring all generated artifacts are up to date and consistent with the protocol source. When adding a new RPC method, follow this checklist to ensure proper schema definition, validation, and testing. First, define the parameter and result schemas using type box and export them in the protocol registry to make them part of the API surface. Create static type aliases for your schemas to enable compile-time type checking in TypeScript. Run the protocol check command to generate artifacts and verify they are properly committed to source control. Implement the RPC handler, register it by method name, and use AJV validators to check parameters before processing. Write unit tests for schema validation and handler logic, plus integration tests that run through the full connect request-response flow. Both client and server must always validate payloads with AJV. Never trust unvalidated data. Also, the features list in hello okay is conservative and should be used only for capability discovery, not as a full API contract. Let's explore how Open Claw manages typing indicators, including the different modes, their interactions, and how to override default behaviors for better user experience. Open Claw decides when to show typing activity using a few configuration keys and runtime signals. Without setting a typing mode, it uses legacy rules. Direct and mention group chats start typing immediately, while unmentioned group chats begin typing only when message text streams. Heartbeat or housekeeping runs never trigger typing. To avoid ambiguity, you can send explicit typing mode in agents.defaults. The four options are never, message, thinking, and instant. These modes determine how early typing indicators appear during a run. The order of typing modes from earliest to latest start is never, message, thinking, then instant. This helps you understand when typing indicators will activate. The never mode disables typing indicators completely. So, no typing activity is shown regardless of message state. In message mode, typing starts only when a message text begins streaming, unless the reply is silent, which we'll discuss shortly. Thinking mode triggers typing only when the run emits reasoning deltas, requiring streaming reasoning support in your provider or runtime. Instant mode shows typing indicators right away as soon as the run begins, regardless of any message or reasoning output. Here is a JSON snippet setting the global typing behavior to thinking mode and defining the typing indicator refresh interval to 6 seconds. This configuration sets the agent's typing mode to thinking and refreshes the typing indicator every 6 seconds while active. Thinking mode depends on the run emitting reasoning deltas, which requires enabling streaming reasoning in your provider or runtime. Without this, typing won't start in thinking mode. Message mode normally starts typing when message text streams. However, if the reply matches the configured silent token exactly, typing is suppressed to avoid showing typing for silent replies. The typing interval seconds setting controls how often the typing indicator refreshes once active. It doesn't affect when typing starts. The default is 6 seconds, but can be overridden per session. You can override typing behavior at the session level. This example forces message mode and shortens the typing refresh interval to 4 seconds for a more responsive experience. This JSON snippet shows the session level override that sets typing mode to message and reduces the refresh interval to 4 seconds. Let's review some key operational tips and a quick checklist to ensure your typing indicators behave as expected. If typing indicators never show, verify your agents.defaults.typing_mode and any session overrides. Ensure typing mode isn't set to never. If thinking mode doesn't trigger, confirm your run emits reasoning deltas with reasoning level set to stream and that your provider supports the streaming. If typing shows, but you expect silence, check that the reply payload isn't exactly the silent token, considering case-insensitive matching. Remember to test typing indicators in direct chats and group chats, since legacy behavior treats mentions in direct chats differently from unmentioned groups. Heartbeat or maintenance runs intentionally disable typing indicators to avoid false typing signals during background processes. To ensure predictable user experience and avoid unexpected typing bursts, always use explicit typing mode settings and session level overrides tailored to your channels. Let's explore how OpenClaw tracks and displays usage data reported by providers. This section explains the design choices and how usage information is presented consistently. OpenClaw relies entirely on usage data reported directly by providers. It polls their usage or quota endpoints and shows that data to users without estimating costs. This avoids confusion and ensures billing matches what the provider knows. Providers report usage in different formats. Some show consumed counts, others remaining quota or token balances. Open Claw normalizes all these into a consistent percent left display computed from the provider's own fields, not estimates. You will find usage information presented in three key areas within Open Claw's interface, making it easy to check your account status wherever you work. Quick commands in chat, such as {slash} status or {slash} usage, provide immediate feedback with a normalized percent left and raw usage data when available. This helps users monitor usage without leaving the conversation. The command line interface supports usage queries like Open Claw status, usage, which can output detailed provider fields in JSON format for automation or integration purposes. On macOS, the menu bar and control UI display the percent left with a tooltip showing provider name and last update time, keeping usage info visible and up-to-date. Open Claw resolves credentials primarily via off profiles, falling back to environment variables or config keys. Provider adapters must use the same order to ensure usage data matches the identity used for API calls and billing. Some providers invert usage percentages, reporting percent remaining as usage or vice versa. Adapter implementers should verify semantics and provide a usage method returning both raw fields and a canonical percent left to keep the UI consistent. In this section, we'll bring everything together with a clear checklist for making extensions and pointers for troubleshooting common issues. This will help you make changes safely and verify them efficiently. Always create small, repeatable plans for each change. The checklists are organized to help you edit, validate, and rollback safely. Each ends with minimal tests to catch common mistakes early. Let's start with changes related to channel renderers, whether adding or modifying them. These are responsible for converting markdown IR into channel-specific formats. Place your renderer code in the appropriate plugin or built-in module and register it using API that register channel renderer with the same ID and capability shape to ensure proper integration. Make sure your renderer accepts markdown IR node shapes and returns the correctly formatted string or array for the target channel, respecting the expected contract. Create unit tests that convert typical markdown IR nodes like links and code blocks, asserting the expected output for your target platform to catch issues early. Deploy your changes to a development gateway and restart it or run gateway. Watch in dev mode to test your renderer in a live environment. Next, run a few minimal validation tests to ensure everything works as intended before moving forward. Run your renderer's unit tests to verify that markdown IR is converted correctly and consistently. Send a small test message through the gateway using the control UI or a test client and confirm the rendered output appears correctly in the target channel or transcript. Now, let's cover changes involving envelopes and time zones, such as timestamp formatting and time zone display. Keep the envelope structure stable by preserving envelope.timestamp, envelope.tz if present, and the canonical ISO timestamp format used in transcripts. When changing time zone or normalization, update both the envelope formatting code and the session write path to keep persisted transcript possible. If you change the persisted timestamp shape, add migration or compatibility checks to avoid breaking existing data. Run minimal validation tests to confirm your timestamp and time zone changes work correctly. Restart the gateway and send messages from clients in different time zones. Inspect session files to confirm timestamps are ISO formatted and consistent. Run Open Cloud Doctor to ensure no configuration diagnostics report timestamp or schema errors before deploying. Next, we cover adding new protocol methods for gateway websocket or HTTP RPC, including schema and handler implementation. Define the new request and response types using type box schemas and register the schemas in the protocol catalog for validation. Implement the handler wired into the gateway frame router, ensuring AJV validation runs on incoming requests to maintain protocol integrity. Run the repository protocol verifier with PNPM protocol. Check to validate schema changes and regenerate protocol artifacts automatically. Perform minimal validation tests to ensure your protocol changes are correctly integrated and validated. Run PNPM protocol. Check locally and fix any schema mismatches before pushing changes to avoid integration issues. Start a development gateway and exercise the new method via a websocket client, verifying that requests and responses pass validation and appear correctly in logs. Now, let's discuss changes related to typing indicators, which are important UX signals for user interactions. Configure typing mode and typing interval seconds in agent defaults and ensure channel renderers and client adapters respect typing start and stop events. If you change the emit cadence, keep throttle and jitter rules conservative to prevent flooding channels with excessive typing events. Run minimal validation tests to confirm typing indicator behavior is correct and respects configured limits. Update your configuration, restart the gateway, and trigger a long-running response to observe typing start and stop events in the control UI or client. Check gateway logs to verify typing events are present and do not exceed the configured rate limits to ensure proper behavior. Next, we cover changes to usage adapters and telemetry, focusing on token counts and cost reporting. Add an adapter that implements usage footprint snapshots and emits the same envelope format used by existing providers for consistency. Make sure token count calculations follow the canonical provider and model references and that usage exposes tokens and cost fields expected by dashboards. Run minimal validation tests to verify your usage adapter reports data correctly. Run a few model calls under the dev gateway and confirm that {slash} status and {slash} usage endpoints include your adapter's data as expected. Validate that downstream dashboards and tests correctly ingest the numeric fields your adapter provides to ensure full telemetry integration. Finally, some important warnings and continuous integration guardrails to keep your changes safe and maintainable. Never commit regenerated protocol artifacts without running PNPM protocol check first. Always include protocol. Check and see I to catch schema drift early. When changing persisted formats like transcripts or envelopes, add compatibility tests and back up your state before deploying to production to avoid data loss. After restarting the gateway, run Open Cloud Doctor to catch common runtime misconfigurations early, preventing user-facing regressions.