Render HTML on a screen
display_render_htmlInstalls self-contained HTML on one authorized screen and returns its display operation. Inputs: device; html; optional title and source_label; optional page_id; security_mode (one of the screen's support.security_modes); runtime (required_api "0" gives the page window.AISurface, "none" a plain page); ttl_seconds (display lifetime, null: none); client_request_id (repeating a request with the same id returns the same operation). The screen's support field lists its security modes, runtime APIs and size limits. page_id (optional, a name of the caller's choosing such as "weather") becomes the page's view_id and each render a new revision of it: sending the same page_id again shows the new revision in that page's place instead of adding another page to its page history (an earlier revision that holds no saved answers is replaced); without page_id every render is a new page. Each revision has its own saved state, which starts empty: answers saved in an earlier revision reach a new one only through initial_state. The screen's account agents, and Wi-Fi clients joined to its account, update each other's pages with the same page_id; another sender's page with that page_id stays as it is. A runtime API 0 page must call AISurface.ready() and then AISurface.signalReady({ health: "ok" }) within a few seconds of loading (6 s in the current app): a page that does not signal ready is not shown and its render fails with DEVICE_REPORTED_FAILURE. On slow phone-owned screens signalReady's answer arrives only once the page is shown (10-15 s on an Android 5 tablet over the internet); older app versions end the wait after 5 s with RUNTIME_NOT_READY although the page is then shown. A page whose controls wait for that answer stays unusable meanwhile, or for good after the timeout. Until the page is shown, emit() fails with VIEW_NOT_ACTIVE (on older app versions state calls too), and a later call succeeds. AISurface calls return Promises: setState(key, value) saves one value (key: a lowercase letter, then lowercase letters, digits, _ . -, at most 64 characters, like "answer"; value: any JSON), replaceState({ key: value, ... }) replaces all saved values, getState(key?) reads one or all, removeState(key), emit(type, data) records an event, getContext() returns the page context, on(event, listener) subscribes to host events. setState resolves once the value is saved on the screen. A coded refusal (INVALID_STATE_KEY, STATE_STORAGE_FULL, VIEW_NOT_ACTIVE) means this write was not saved. A mutation timeout (RUNTIME_NOT_READY with retryable: false) has an uncertain outcome: the write may have committed before its reply was lost. An authorized getState readback can confirm the current value; a failed readback leaves it uncertain, and a newer edit can supersede the timed-out value. Repeating an event-producing action can duplicate its effect. A page that shows "Saved" before the Promise resolves can show it for an answer that was lost. getState("choice") resolves to the saved value itself, such as "Plate" (not { choice: "Plate" }), or null for a key with no saved value; getState() resolves to one object of every saved key and value. After a reload the page shows what its own script rebuilds at load: text derived from an answer, such as the price of the chosen item, appears again when the script computes it from the value getState returns. Page state (AISurface.setState) is kept on the screen with the page and is the saved state the agent reads; a phone restart can drop events nobody has read yet, so state is the durable record of an answer. ttl_seconds sets the page's display lifetime on the screen (null: no lifetime), after which the screen returns to its Start page. A lifetime does not keep the display lit: the phone turns its display off after its own screen timeout unless the owner's Keep screen awake setting is on (screen_settings.keep_screen_awake in the screen's status). Emoji come from the screen's own system font, which holds only the emoji that existed when its Android version shipped; an emoji the font lacks shows as an empty box. Words and basic symbols (✓ ★ → • ⚠ ♥ ☀) display on every screen. Measured fonts: Android 5 (android_api 21 and 22) shows only the original 2010 emoji, for example ☕ 🍕 🍝 🍲 ✅ 😀, and boxes for every emoji added since 2014, such as 🌶 🌮 🥑 🥢 🥡; Android 8.0 (android_api 26) shows emoji up to 2017 and boxes for those added since 2018, such as 🧁 🥳 🧋; Android 16 (android_api 36) showed every emoji tested, up to 2020. android_api is in each screen's listing and status; null means not reported, so the screen may be old. A page shorter than its screen sits at the top of the page area, so its controls reach the lower part of the screen only when the page fills the area's height, for example a full-height container (min-height: 100vh) with the controls last, above the menu button's covered height. A person's tap opens only http and https links. Other link schemes, such as sms:, tel: and mailto:, stay blocked, so a page cannot start a message, call or email; a page can show the recipient and text for the person to send themselves. Status: queued, received and persisted precede applied; only applied means the screen shows the page, and the returned operation_id identifies the operation in later status reads. A page's scripts run again each time the screen loads it again (an app restart, recovery after a crash, showing it again from the page history), so a value a script saves at load replaces an answer saved after it. initial_state (1-64 saved-state keys with JSON values, at most 768 bytes) is written into the new page's saved state once, when the screen first admits that page and before its scripts run; later loads leave it as it is, and getState and the page's saved-state read return it until the page saves another value. A revised page carries an earlier answer this way and shows it from getState; without initial_state it starts with no saved answers. For example, after the saved state of page_id "bookclub-vote" reads values {"book": "Emma"}, the revision sent with page_id "bookclub-vote" and initial_state {"book": "Emma"} reads "Emma" from getState("book") at load and can show it selected; the person's next saved choice then replaces it, and later loads keep that choice. Screens whose support.render_fields lists initial_state accept it; others answer DEVICE_CAPABILITY_UNSUPPORTED, and the page can be sent without it. Answers saved on a screen stay with that page on the screen and reach the agent only as that page's saved state; no screen tool writes them into another system, such as a school platform, a calendar or a shop. The result's page_warnings (absent when there are none) name problems found in the page before delivery: an inline script with a syntax error, or one the screen's browser cannot parse (judged from its reported webview_version; Android 5's WebView 37 reads only ES5, without arrow functions, let/const, classes or async), a runtime API 0 page with no AISurface.signalReady call, speechSynthesis, SpeechRecognition, getUserMedia or a file input (no screen spoke from a page in tests, and pages get no camera, microphone, photo or file), AISurface.on with a name that is no host event, and AISurface or --ais-overlay-* in a page without runtime API 0. The page is delivered either way. Where the screen's support.limits.operations_retained is set, the service keeps only that many recent operations for the screen. A repeated request with the same client_request_id returns the original operation while it is kept; after it has been removed, the same request is a new operation and shows the page again.
Tool call template
Replace each placeholder with a value matching the input schema. Only required parameters are included; add optional parameters as needed.
{
"id": 1,
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"arguments": {
"device": "<device>",
"html": "<html>"
},
"name": "display_render_html"
}
}Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| html | Yes | ||
| title | No | ||
| device | Yes | ||
| page_id | No | The page's name of the caller's choosing, which becomes its view_id. A later send with the same page_id is a new revision shown in that page's place; each revision has its own saved state, which starts empty unless initial_state supplies earlier answers. | |
| runtime | No | ||
| ttl_seconds | No | Seconds the screen shows the page before it returns to its Start page (null: until another page replaces it). It does not keep the display lit: the phone turns its display off after its own screen timeout unless the owner's Keep screen awake setting is on (screen_settings.keep_screen_awake in the screen's status). | |
| source_label | No | ||
| initial_state | No | Saved answers the new page starts with, written once when the screen first admits it, before its scripts run. A new revision of a page (the same page_id) receives no answers from earlier revisions otherwise. Example: the saved state of page_id "bookclub-vote" held values {"book": "Emma"}; the revision sent with page_id "bookclub-vote" and initial_state {"book": "Emma"} reads "Emma" from getState("book") and can show it selected. | |
| security_mode | No | normal | |
| replace_policy | No | activate_when_healthy | |
| client_request_id | No |
Output Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||