[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"skill:01M698QV3YFJ0PPJAW2QTPCZX9":3,"skill-same:01M698QV3YFJ0PPJAW2QTPCZX9":65},{"skillNo":4,"name":5,"nameZh":6,"description":7,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":15,"categories":16,"keywords":22,"publishedAt":37,"linkOnly":38,"markdownContent":39,"markdownBody":40,"frontMatter":41,"refName":42,"commitSha":43,"skillDirectory":44,"forksCount":45,"repositoryHtmlUrl":46,"repositoryHomepageUrl":47,"ownerHtmlUrl":48,"ownerBlogUrl":47,"ownerDescription":49,"updatedAt":50,"install":51,"resources":64},"01M698QV3YFJ0PPJAW2QTPCZX9","winnode",null,"Invoke and troubleshoot OpenClaw Windows-node commands through the local winnode CLI and MCP endpoint, including command argument schemas and A2UI JSONL calls.","openclaw\u002Fopenclaw-windows-node","openclaw","https:\u002F\u002Favatars.githubusercontent.com\u002Fu\u002F252820863?v=4","01M698P3FSAZ6B4R928ZF19KPT","01M698Q6SND6RHZDPP8ZM0NSP7",2197,"MIT","https:\u002F\u002Fgithub.com\u002Fopenclaw\u002Fopenclaw-windows-node\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fwinnode\u002FSKILL.md",[17],{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000023","Skill与智能体开发","SCENARIO","使用场景",[5,23,9,24,25,26,27,28,29,30,31,32,33,34,35,36],"MCP","local","invoke","jsonl","cli","argument","command","node","troubleshoot","commands","endpoint","windows","calls","a2ui","2026-11-10 10:12:30",false,"---\nname: winnode\ndescription: Invoke and troubleshoot OpenClaw Windows-node commands through the local winnode CLI and MCP endpoint, including command argument schemas and A2UI JSONL calls.\n---\n\n\u003C!--\n  REGENERATE-ME-WHEN-CAPABILITIES-CHANGE\n\n  The list of supported commands below is checked at CI time against the live\n  capability surface (see SkillMdDriftTests). When a capability is added,\n  removed, or renamed in src\u002FOpenClaw.Shared\u002FMcp\u002FMcpToolBridge.cs\n  (CommandDescriptions), update this document so the drift test stays green -\n  the test compares command identifiers, so prose can still be tweaked by hand.\n-->\n\n# winnode skill reference\n\n`winnode.exe` invokes OpenClaw Windows-node commands on the local tray over a\nloopback MCP HTTP endpoint (default `http:\u002F\u002F127.0.0.1:8765\u002F`). Enable\n**Local MCP Server** in Settings > Permissions > Integrations before calling.\nA connection refused on that port means the tray is not listening. Do not\nreinstall the CLI. Start the tray with `EnableMcpServer` true, then point\n`OPENCLAW_TRAY_DATA_DIR` at that tray's data directory so the CLI loads\n`mcp-token.txt`. `browser.proxy` stays off `tools\u002Flist` until the app has a\ngateway client, a shared token, and a verified browser endpoint. The call\nthen returns `Unknown tool: browser.proxy`.\n\nThis document is the agent-facing reference: every supported command, its\nargument shape, and the A2UI v0.8 JSONL grammar. It is shipped alongside\n`winnode.exe` so an agent can read it once and emit token-efficient calls.\n\n---\n\n## Invocation shape\n\n```text\nwinnode --command \u003Cname> [--params '\u003Cjson-object>'] [--invoke-timeout \u003Cms>] [--identity release|dev]\nwinnode --list-tools [--mcp-url \u003Curl>|--mcp-port \u003Cport>] [--identity release|dev]\n```\n\n- `--command` (required) - node command (e.g. `system.which`, `canvas.a2ui.push`).\n- `--list-tools` - query the live MCP server's `tools\u002Flist` method and print the\n  advertised tools. Useful when settings-gated capabilities differ from this\n  static reference.\n- `--params` - single JSON **object** string, default `{}`. Must be a JSON object,\n  not an array or scalar. **`--params @\u003Cpath>`** loads the JSON object from a\n  file on disk (useful for big A2UI payloads \u002F `canvas.eval` scripts).\n- `--invoke-timeout` - milliseconds, default 15000, max 600000 (10 min). HTTP\n  timeout adds a 5s buffer.\n- `--node` - accepted for parity with `openclaw nodes invoke`; **ignored**\n  locally. Safe to copy\u002Fpaste from gateway-side commands.\n- `--idempotency-key` - accepted for parity; **ignored**, and the CLI emits a\n  `[winnode] WARN` to stderr because local MCP does *not* dedupe retries -\n  re-running a command after a transient failure can double-execute side\n  effects. If you need idempotency, target the gateway, not winnode.\n- `--mcp-url \u003Curl>` \u002F `--mcp-port \u003Cport>` - override the endpoint. Falls back to\n  `OPENCLAW_MCP_PORT` env var, then port 8765. `--mcp-port` must be in\n  `[1, 65535]`; out of range fails with exit code 2.\n- `--mcp-token \u003Ctoken>` - bearer token override (testing \u002F explicit only). The\n  literal value is **visible to other same-user processes via the OS process\n  listing** (`Get-CimInstance Win32_Process | Select CommandLine`,\n  Process Explorer, etc.). The CLI emits a stderr warning when this flag is\n  used. **Prefer `OPENCLAW_MCP_TOKEN` (env var) or the on-disk\n  `%APPDATA%\\OpenClawTray\\mcp-token.txt`** which the release tray writes when\n  MCP is enabled. Both `OPENCLAW_MCP_TOKEN` and the on-disk file should\n  themselves be treated as sensitive operational secrets.\n- `--identity release|dev` - selects which tray profile supplies the default\n  on-disk MCP token. Defaults to `OPENCLAW_APP_IDENTITY`, then `release`.\n  Use `--identity dev` for a side-by-side dev tray; its default token path is\n  `%APPDATA%\\OpenClawTray-Dev\\mcp-token.txt`. `OPENCLAW_TRAY_DATA_DIR` still\n  wins for isolated runs and points directly at the data folder.\n- `--verbose` - log endpoint + ignored flags to stderr. Without `--verbose`,\n  HTTP error bodies are emitted only as the first line; with `--verbose`, the\n  full body is shown (after sanitization + token-shape redaction).\n- `--input-file \u003Cpath>` - raw local input for `file.create`. Computes size and\n  SHA256, checks any caller-supplied values, and streams bounded messages.\n- `--output-file \u003Cnew-path>` - raw local output for `file.fetch`; requires\n  explicit positive `maxBytes`. Never overwrites. Validates returned size and\n  SHA256 before publishing its owned partial file at the requested destination.\n  Both options use one `--invoke-timeout` deadline, preserve supplied expected\n  identity, and use ordinary preflight only to fill missing binding\u002Fpath fields.\n\n**Output contract:** stdout receives the capability payload as pretty-printed\nJSON (matches `openclaw nodes invoke`). stderr receives errors. Exit code:\n\n| Code | Meaning |\n|------|---------|\n| 0    | Success |\n| 1    | Tool error, JSON-RPC error, transport failure, or HTTP non-2xx |\n| 2    | Argument error (missing\u002Finvalid flags, bad `--params` JSON, out-of-range port\u002Ftimeout, non-http URL) |\n\n**Off-loopback safety:** when `--mcp-url` points at a non-loopback host, the\nCLI **refuses to send the auto-loaded local MCP token** (and warns on stderr).\nAn explicitly supplied `--mcp-token` is honored with a warning. This preserves\nthe loopback-only threat model the tray's MCP server relies on.\n\n---\n\n## Commands\n\n## Files and directories\n\nAll eight commands use the **existing Node Sandbox configuration and executor**,\nthe same as `system.run`. Supported Windows runs the fixed worker through MXC.\nPositively unsupported Windows uses explicit uncontained compatibility, with no\nSandbox filesystem\u002Fnetwork enforcement; failed probes, SDK or launch errors do\nnot downgrade. Existing Node\u002FMCP enablement applies, without new file permission\nswitches or shell exec approval prompts. Remote Gateway trust\u002Fallowlists and\nadmin checks remain independent. Settings changes apply to new operations only.\n\nUnary reads\u002Farchives default to 8 MiB and cap at 16 MiB. Writes\u002Fuploads cap at\n16 MiB decoded. CLI file envelopes use a command-specific bounded HTTP allowance,\nnot an increase to every tool's request\u002Fresponse limit. Ordinary MCP clients can\nuse unary and preflight calls without the optional framed extension.\n\n### file.stat\nInspect metadata: `{ \"path\": \"C:\\\\Data\\\\sample.txt\" }`.\nOptional `followSymlinks`, `preflightOnly`, `expectedCanonicalPath`,\n`expectedBinding`. Returns `{ok,path,type,size,mtimeMs,binding}` and\nan opaque identity `binding`. Preserve returned bindings without rewriting IDs.\nOrdinary local drive paths are supported. Reparse points, Cloud Files hydration,\ncase-sensitive directories and `followSymlinks:true` fail explicitly.\n\n### file.fetch\nUnary read: `{ \"path\": \"C:\\\\Data\\\\sample.txt\", \"maxBytes\": 16777216 }`.\nReturns `{ok,path,size,mimeType,base64,sha256,binding}`. Optional shared identity\nfields and `preflightOnly`. For binary, require `transport:\"binary\"`, explicit\npositive `maxBytes`, an existing `expectedBinding`, and framed IO.\n\n```powershell\nwinnode --command file.fetch --params '{\"path\":\"C:\\\\Data\\\\sample.bin\",\"maxBytes\":52428800}' --output-file .\\received.bin --invoke-timeout 60000\n```\n\n### dir.list\n`{ \"path\": \"C:\\\\Data\", \"maxEntries\": 200, \"pageToken\": \"0\" }`.\nReturns entries, `nextPageToken`, path and binding. Page size caps at 5000.\nChildren blocked by the current Sandbox policy are omitted, and paging counts\nonly visible entries. A blocked target directory still returns a permission\nerror. Optional `preflightOnly`, `followSymlinks` and shared identity fields.\n\n### dir.fetch\n`{ \"path\": \"C:\\\\Data\", \"maxBytes\": 16777216 }`.\nReturns `tarBase64`, `tarBytes`, `sha256`, `fileCount`, entries and binding.\nExpanded archive data caps at 64 MiB; entry count caps at 5000. Optional\n`preflightOnly`, `followSymlinks` and shared identity fields.\n\n### file.write\n`{ \"path\": \"C:\\\\Data\\\\sample.txt\", \"contentBase64\": \"YWJj\", \"overwrite\": false }`.\nAccepts canonical standard\u002Fbase64url, padded or unpadded. Optional\n`createParents`, `rejectHardlinks`, `expectedSha256`,\n`expectedCanonicalPath`, `expectedBinding`, `preflightOnly`.\nReturns `{ok,path,size,sha256,overwritten,binding}`. No overwrite by default.\n\n### file.create\nRequires `path`, `sizeBytes`, `expectedSha256`. Optional `maxBytes` (default\n50 MiB), `createParents`, identity fields and `preflightOnly`.\nPreflight needs no channel; real creation streams messages of at most 1 MiB,\nwaits for an `ack` after each, and sends an empty message for EOF.\nReturns `{ok,path,size,sha256,status:\"created\",binding}` when created. An existing\ntarget returns `status:\"exists\"` without size\u002Fhash fields and is not read back\nto verify content equality. Never overwrites.\n\n```powershell\nwinnode --command file.create --params '{\"path\":\"C:\\\\Data\\\\new.bin\"}' --input-file .\\source.bin --invoke-timeout 60000\n```\n\n### fs.listDir\nDirectory navigation, with optional `{ \"path\": \"C:\\\\Data\" }`. Remote calls are\nGateway-admin-gated; local calls use the existing MCP bearer. Neither grants\nelevation or bypasses contained data access. Children blocked by the current\nSandbox policy are omitted; requesting a blocked directory itself still fails.\n\n### terminal.upload\n`{ \"name\": \"sample.txt\", \"contentBase64\": \"YWJj\" }`.\nRequires canonical padded standard base64. Returns `{path,size}`.\nThe raw node command stages an upload without a terminal session. Remote calls\nrequire genuine Gateway admin authorization. The app schedules retained cleanup;\nthe same mode-selected worker scans and deletes only identity-owned upload data.\nNew uploads use automatic private storage belonging to the current Companion\nprofile, outside its settings and runtime-code folders. Downloads and the legacy\nupload-location setting receive no automatic grant. Contained ordinary commands\ncan read all retained uploads in that profile after ownership verification, but\ncannot modify them through the automatic grant. Only upload\u002Fcleanup operations\nreceive managed-storage write access. Explicit read-only\u002FNo access restrictions\nstill apply, including an inherited read-only policy. This is profile-scoped\nsharing, not per-file or per-conversation isolation.\n\nThe app establishes only the exact private profile boundary as trusted metadata\nbefore launching the contained worker. It checks current policy and reserves\ncleanup ownership first. Uploaded bytes and cleanup still run inside the worker,\nwithout granting write access to the boundary's parent. Cancellation or launch\nfailure can leave an owned empty boundary for later safe reuse. Unbound or\nunrecognized ownership requires review, not automatic adoption or deletion.\n\nThe committed transfer budget defaults to 50 MiB and cannot exceed 1024 MiB;\nvalid existing settings remain effective. This command still has the tighter\n16 MiB decoded unary limit. Retention remains bounded to 24 hours, 64 upload\ndirectories and 256 MiB. Cleanup of remembered legacy roots still requires their\ncurrent folder permissions and ownership identity. A blocked cleanup is not a\nreason to retry a completed upload.\n\n**Error semantics:** the six plugin commands retain the Gateway outer success\nenvelope with inner `{ok:false,code,message}`. The two admin commands use outer\nerrors. MCP converts typed semantic failure to `isError:true`; `winnode` exits 1.\nAn explicit binary\u002Fcreate call without a channel returns `DUPLEX_REQUIRED`.\nSee [local framed transport](..\u002F..\u002F..\u002Fdocs\u002FMCP_MODE.md#file-channel-extension-v1)\nfor the optional authenticated lease protocol. It is not standard MCP streaming.\n\n### system.notify\nShow a Windows toast notification.\n```\n{\"title\": \"OpenClaw\", \"body\": \"string\", \"subtitle\": \"string\", \"sound\": true}\n```\nReturns `{ \"sent\": true }`. All fields optional except `body` in practice.\n\n### system.run\nExecute canonical argv. Subject to the local exec approval policy at\n`%APPDATA%\\OpenClawTray\\exec-approvals.json`.\n\nSupported Windows uses `Microsoft.Mxc.Sdk` 1.0.0 containment. There is no Off\nsetting. Positively unsupported Windows instead runs approved argv uncontained,\nwith a persistent Node Sandbox warning and disabled sandbox permission controls.\nFile\u002Fnetwork\u002Fclipboard\u002FUI restrictions are not enforced there; approvals,\ndeadline, bounded output and revalidation remain active. Missing SDK components,\nunknown\u002Ffailed probes or native execution failures never downgrade. On supported\nWindows, unsupported\u002Funavailable policy and stale\nauthorization are tool errors (`isError=true`). Balanced reads the OS user profile\nand resolved personal\u002Fredirected folders; Open reads\u002Fwrites the same scope.\nNeither grants a whole drive or other users. Projects outside that scope need\nexplicit Custom grants. Readonly profile plus selected write children is\nsupported. Volume roots are never granted, even readonly. Missing required\nlocations block with diagnostics; optional unavailable OneDrive is not included.\nProtected OpenClaw settings,\nSSH, browser-profile and PSReadLine roots remain excluded in every scope.\nExplicit cwd must already be granted; omitted cwd uses private scratch.\nSettings affect new commands only. Pending requests revalidate before spawn.\nCancellation dismisses approval, releases the prompt lock, and explicitly\nterminates the owned SDK process, including a late native launch.\n```\n{\n  \"command\": [\"executable\", \"arg\", ...], \u002F\u002F required\n  \"rawCommand\": \"string\",                \u002F\u002F optional display metadata\n  \"cwd\":     \"string\",\n  \"timeoutMs\": 30000\n}\n```\nShell behavior must be explicit in the argv, for example\n`{\"command\":[\"cmd.exe\",\"\u002Fd\",\"\u002Fs\",\"\u002Fc\",\"echo hello\"],\"rawCommand\":\"echo hello\"}`.\nThe gateway's `exec host=node` path performs this wrapping automatically.\nNon-empty custom `env` is rejected until environment values can be identity-bound\nand shown safely during approval.\n\n#### Migration from the pre-V2 low-level contract\n\nThe V2 node boundary intentionally rejects string-form `command` with\n`command-array-required`. Update raw MCP, `node.invoke`, plugin, and direct\n`winnode` callers to send the shell transport explicitly:\n\n```json\n\u002F\u002F Before\n{\"command\":\"echo hello\",\"shell\":\"cmd\"}\n\n\u002F\u002F V2\n{\"command\":[\"cmd.exe\",\"\u002Fd\",\"\u002Fs\",\"\u002Fc\",\"echo hello\"],\"rawCommand\":\"echo hello\"}\n```\n\nPowerShell callers must likewise name `powershell.exe` or `pwsh.exe` and its\n`-Command` arguments explicitly. Remove custom `env` from the request; V2 rejects\nnon-empty environments rather than approving an executable under hidden process\nconfiguration.\nExited commands return `{ stdout, stderr, exitCode, timedOut, success, durationMs }`.\nRetained output is bounded by UTF-8 bytes per stream, including any truncation\nmarker, while discarded output continues to drain. Removing policy on exit\ndoes not undo writes to granted folders.\n\n### system.run.prepare\nPre-flight a `system.run` invocation. Same args as `system.run`. Returns the\nparsed plan (`argv`, `cwd`, `rawCommand`, `agentId`, `sessionKey`) without\nexecuting.\n\n### system.which\nResolve binary names to absolute paths.\n```\n{\"bins\": [\"git\", \"node\", \"powershell\"]}\n```\nReturns `{ \"bins\": { \"git\": \"C:\\\\...\", ... } }`. Names not found are omitted.\n\n### system.execApprovals.get\nNo params. Returns the active V2 snapshot:\n`{ path, exists, hash, file: { version, defaults: { security, ask, askFallback, autoAllowSkills }, agents: { \"\u003CagentId>\": { security, ask, askFallback, autoAllowSkills, allowlist: [{ id, pattern, lastUsedAt?, lastResolvedPath? }] } } } }`.\n`security` is `deny|allowlist|full`; `ask` is `off|on-miss|always|deny`;\n`askFallback` is `deny|allowlist|full`. Socket credentials are never returned.\nPass `hash` as `baseHash` when calling `system.execApprovals.set`.\n\n### system.execApprovals.set\nReplace the full V2 file using compare-and-swap. `baseHash` is required and must\nequal the latest hash returned by `system.execApprovals.get`.\n```\n{\n  \"baseHash\": \"\u003Chash-from-get>\",\n  \"file\": {\n    \"version\": 1,\n    \"defaults\": {\n      \"security\": \"allowlist\",\n      \"ask\": \"on-miss\",\n      \"askFallback\": \"deny\",\n      \"autoAllowSkills\": false\n    },\n    \"agents\": {\n      \"main\": {\n        \"security\": \"allowlist\",\n        \"ask\": \"on-miss\",\n        \"askFallback\": \"deny\",\n        \"autoAllowSkills\": false,\n        \"allowlist\": []\n      }\n    }\n  }\n}\n```\nReturns the updated snapshot. A remote update may preserve or remove existing\nallowlist grants, but it cannot add or change grants or set full access. Missing or\nstale hashes are rejected. This command changes future `system.run` authorization\ndecisions. The `winnode` path is local loopback MCP, so no gateway scope applies.\nRemote operators must use the gateway `exec.approvals.node.*` control-plane\nmethods, which are separately gated by gateway admin scope.\n\n### canvas.present\nOpen the WebView2 canvas window.\n```\n{\n  \"url\":   \"string\",          \u002F\u002F OR \"html\": \"string\"\n  \"html\":  \"string\",\n  \"width\": 800, \"height\": 600,\n  \"x\": -1, \"y\": -1,           \u002F\u002F -1 centers\n  \"title\": \"Canvas\",\n  \"alwaysOnTop\": false\n}\n```\nReturns `{ \"presented\": true }`.\n\n### canvas.hide\nNo params. Hides the canvas without destroying state.\n\n### canvas.navigate\n```\n{\"url\": \"https:\u002F\u002F...\"}    \u002F\u002F also accepts file:\u002F\u002F or local canvas paths\n```\n\n### canvas.eval\n```\n{\"script\": \"document.title\"}    \u002F\u002F also accepts \"javaScript\" or \"javascript\"\n```\nReturns the evaluated result.\n\n### canvas.snapshot\n```\n{\"format\": \"png|jpeg\", \"maxWidth\": 1200, \"quality\": 80}\n```\nReturns `{ format, base64 }`.\n\n### canvas.a2ui.push\nRender an A2UI v0.8 surface in the canvas. The canvas window opens\nautomatically - no `canvas.present` required.\n```\n{\n  \"jsonl\":     \"string\",   \u002F\u002F OR jsonlPath\n  \"jsonlPath\": \"string\",   \u002F\u002F must live under %TEMP%\n  \"props\":     {}           \u002F\u002F optional\n}\n```\nReturns `{ \"pushed\": true }`. **See A2UI grammar below.**\n\n### canvas.a2ui.pushJSONL\nAlias of `canvas.a2ui.push`. It uses the identical handler, size\u002Fline caps, and\n`jsonl`\u002F`jsonlPath` contract. It exists for naming parity, not as a separate\nstreaming path.\n\n### canvas.a2ui.reset\nNo params. Clears any rendered surfaces. Returns `{ \"reset\": true }`.\n\n### canvas.a2ui.dump\nNo params. Returns the current surface graph for introspection. **Read-all:**\nthis exposes every currently-rendered surface - operators should treat it as\nequivalent to a screenshot of every open A2UI surface.\n\n### canvas.caps\nNo params. Returns renderer capabilities (renderer, snapshot, a2ui version).\n\n### screen.snapshot\n```\n{\n  \"format\": \"png|jpeg\", \"maxWidth\": 1920, \"quality\": 80,\n  \"monitor\": 0, \"screenIndex\": 0,    \u002F\u002F 0 = primary\n  \"includePointer\": true\n}\n```\nReturns `{ format, width, height, base64, image }` (image is a `data:` URL).\nThe first use requires remembered user consent. Every invocation displays a\nvisible notification before screen capture begins.\n\n### screen.record\n```\n{\n  \"durationMs\": 5000,         \u002F\u002F required, max 300000\n  \"format\": \"mp4|webm\",\n  \"monitor\": 0, \"screenIndex\": 0,\n  \"maxWidth\": 1920, \"fps\": 30\n}\n```\nReturns `{ format, durationMs, base64 }`.\n\n### camera.list\nNo params. Returns `{ cameras: [{ deviceId, name, isDefault }] }`.\n\n### camera.snap\n```\n{\"deviceId\": \"string\", \"format\": \"jpeg|png\", \"maxWidth\": 1280, \"quality\": 80}\n```\nReturns `{ format, width, height, base64 }`. `deviceId` defaults to system\ndefault camera.\nThe first use requires remembered user consent. Every invocation displays a\nvisible notification before camera capture begins.\n\n### camera.clip\n```\n{\n  \"deviceId\": \"string\",       \u002F\u002F optional\n  \"durationMs\": 3000,         \u002F\u002F required, max 60000\n  \"format\": \"mp4|webm\",\n  \"maxWidth\": 1280\n}\n```\nReturns `{ format, durationMs, base64 }`.\n\n## Speech-to-text (stt.*)\n\nLocal Whisper.net runs on this device - no audio leaves the box. The\nmodel is downloaded on first use; until then every `stt.*` call returns\na clear error pointing the caller at the Voice Settings page.\n**Privacy-sensitive: requires `NodeSttEnabled` in tray Settings.**\n\n### stt.transcribe\nBounded fixed-duration mic capture + transcription.\n```\n{\n  \"maxDurationMs\": 5000,      \u002F\u002F required, > 0, max 30000\n  \"language\": \"en\"            \u002F\u002F optional BCP-47 tag or \"auto\" - falls back to SttLanguage setting\n}\n```\nReturns `{ transcribed, text, durationMs, language, engineEffective: \"whisper\" }`.\n\n### stt.listen\nMic capture with voice-activity detection. Returns when the user stops\nspeaking or after `timeoutMs`. Result is the full silence-bounded\nutterance (all Whisper segments concatenated), not a partial first\nsegment.\n```\n{\n  \"timeoutMs\": 30000,         \u002F\u002F optional, default 30000, range 1000..120000\n  \"language\": \"auto\"          \u002F\u002F optional BCP-47 tag or \"auto\"\n}\n```\nReturns `{ text, language, durationMs, segments[{ text, startMs, endMs }], engineEffective: \"whisper\" }`.\n\n### stt.status\nEngine readiness. No params. Carries no PII (no transcript history,\nno language history, no device IDs, no model paths).\nReturns `{ engine: \"whisper\", readiness, modelDownloadProgress, isListenWithVadSupported, isBoundedTranscribeSupported }`\nwhere `readiness` ∈ `\"ready\" | \"initializing\" | \"model-downloading\" | \"model-not-downloaded\" | \"unavailable\"`.\n\n## Text-to-speech (tts.*)\n\nFive providers - Kokoro (local neural via kokoro.cpp, default), Piper (local\nneural via Sherpa-ONNX), Windows built-in speech, ElevenLabs (cloud), and\nMiniMax (cloud). Provider + per-provider voice are configured in tray Settings.\nWhile Kokoro is the provider and TTS is enabled, the tray downloads the selected\nKokoro voice pack in the background on unmetered connections; until it is ready,\n`tts.status` reports `voice-not-downloaded` and speech falls back to Windows.\n\n### tts.speak\nSpeak text aloud on the Windows node.\n```\n{\n  \"text\": \"string\",           \u002F\u002F required\n  \"provider\": \"kokoro|piper|windows|elevenlabs|minimax\",  \u002F\u002F optional; omit to use TtsProvider setting\n  \"voiceId\": \"string\",        \u002F\u002F optional, overrides the per-provider configured voice\n  \"model\": \"string\",          \u002F\u002F optional, cloud providers only\n  \"interrupt\": false          \u002F\u002F default false; true cuts off any in-progress playback\n}\n```\nWhen `provider` is omitted and the configured provider isn't usable (no\ncloud API key, Kokoro voice pack or Piper voice not downloaded), the node\nfalls back to Windows TTS so playback still happens. Explicit `provider` requests stay strict and\ndo not silently reroute. Returns `{ spoken, provider, requestedProvider, fellBack, contentType, durationMs }`\nwhere `provider` is the provider that actually spoke.\n\n### tts.status\nTTS provider readiness. No params. Carries no PII (no voice ids, no key\nfragments, no device names).\nReturns `{ configuredProvider, effectiveProvider, willFallBack, providers[{ provider, readiness, isReady }] }`\nwhere `effectiveProvider` is what would run now after fallback and `readiness`\n∈ `\"ready\" | \"needs-api-key\" | \"needs-voice\" | \"voice-not-downloaded\" | \"unavailable\"`.\nThe configured\u002Feffective view reflects configured defaults only; explicit\n`tts.speak` provider requests stay strict and may not match the default\nsnapshot.\n\n## Local model inference (ollama.*)\n\nRuns against a separately installed Windows Ollama service (not the\napp-managed Local AI gateway provider). Any paired active gateway, local\nor remote, may invoke it once enabled. **Requires\n`NodeOllamaInferenceEnabled` in tray Settings > Permissions (opt-in,\ndefault off).**\n\n### ollama.models\nList locally installed Ollama models. Read-only inventory - no prompt\nor chat content. No params.\nReturns `{ provider, models[{ name, size, modifiedAt, family, parameterSize, quantization, contextWindow, capabilities, loaded }] }`.\n\n### ollama.chat\nSend a single-turn chat prompt to a local Ollama model.\n**Privacy and resource sensitive** - sends prompt content to a locally\nrunning model and consumes CPU\u002FGPU. Only one chat runs at a time per node;\na concurrent call while one is in flight returns an error.\n```\n{\n  \"model\": \"string\",          \u002F\u002F required\n  \"prompt\": \"string\",         \u002F\u002F required, max 128000 chars\n  \"system\": \"string\",         \u002F\u002F optional, max 32000 chars\n  \"temperature\": 0.7,         \u002F\u002F optional, 0..2\n  \"maxTokens\": 512,           \u002F\u002F optional, default 512, max 8192\n  \"timeoutMs\": 120000         \u002F\u002F optional, default 120000, max 600000\n}\n```\nReturns `{ provider, model, response, usage?: { promptTokens, completionTokens }, timings?: { loadMs, totalMs } }`.\n\nNewer gateways with the bundled Ollama plugin expose these commands through\nthe `node_inference` agent tool. Older gateways can invoke them through the\ngeneric `nodes` tool after both exact command names are added to\n`gateway.nodes.allowCommands`. Feature-detect the node's effective commands;\ndo not infer support from the gateway version.\n\n## App control (app.*)\n\nRead-only and small write operations targeting the running tray. Used\nby the command palette and by automation that wants to drive the UI.\n\n### app.navigate\nNavigate Workspace or the Settings companion to a specific page.\n```json\n{\"page\": \"workspace:home\"}\n```\nReturns `{ navigated, page }`. `home` and `chat` open Workspace chat;\n`workspace:notifications` opens Workspace notifications. Unprefixed routes\nsuch as `sessions`, `settings`, and `connection` open the Settings companion.\nDeprecated Workspace routes return Home. Unknown `workspace:` routes return\na tool error before any window is opened or navigation is forwarded.\n\n### app.status\nCurrent connection \u002F node state.\nNo params. Returns `{ connectionStatus, overallState, operatorState, nodeState, nodeConnected, nodePaired, nodePendingApproval, nodeError, gatewayVersion, sessionCount, nodeCount }`.\nFor agent-facing connection troubleshooting, prefer `app.connection.status`.\n\n### app.sessions\nActive sessions, optionally filtered by agent.\n```\n{\"agentId\": \"string\"}        \u002F\u002F optional\n```\nReturns array of `{ Key, Status, Model, AgeText, tokens }`.\n\n### app.agents\nList agents from the connected gateway. No params. Returns the raw\nagents JSON array.\n\n### app.nodes\nList connected nodes and their capabilities. No params. Returns array\nof `{ DisplayName, NodeId, IsOnline, Platform, CapabilityCount }`.\n\n### app.config.get\nRead gateway configuration value at a dot-path.\n```\n{\"path\": \"string\"}           \u002F\u002F optional; omit to fetch the full config tree\n```\nReturns the config subtree (or full config) as JSON.\n\n### app.settings.get\nRead a local app setting by name.\n```\n{\"name\": \"string\"}           \u002F\u002F required\n```\nReturns the setting value (type depends on the setting).\n\n### app.settings.set\nSet a local app setting, persist it, and apply the same reconnect\u002Freload behavior as saving settings in the app UI.\n```\n{\"name\": \"string\", \"value\": \"string\"}  \u002F\u002F both required\n```\nReturns `{ name, value }`; runtime apply failures surface as tool errors.\n\n### app.menu\nGet tray menu state (status, session count, node count). No params.\nReturns array of menu items; the status item includes `status`, `overallState`, `nodeState`, and `nodeError`.\n\n### app.search\nSearch locally available navigation destinations without opening them or making\nGateway requests. Settings results identify their destination rather than toggle\nanything. Message snippets are excluded.\n```\n{\"query\": \"string\"}          \u002F\u002F required\n```\nReturns up to 10 `{ Title, Subtitle, Icon, Group, OpensDashboard, HasDashboardCounterpart }`.\n\n### app.dashboard.url\nBuild the same gateway dashboard URL the tray opens.\n```\n{\"path\": \"string\"}           \u002F\u002F optional\n```\nReturns `{ url, credentialSource, usesSharedGatewayToken, hasTokenQuery }`.\n\n## App connection diagnostics and setup (app.connection.*)\n\nLocal MCP-only tools for connection diagnostics, setup, pairing approvals, and\ntargeted reconnects. These tools are not advertised to the remote gateway node\ntransport. Read-only diagnostics redact token values and expose credential\npresence\u002Foutcome only.\n\n### app.connection.status\nRead-only connection diagnostics for agents and CLIs. No params. Returns:\n`{ schemaVersion, connectionState, effectiveMode, legacyConnectionStatus, gateway, protocol, operator, node, mcp, browserProxy, pendingActions, retry, diagnostics }`.\n\nThe payload includes the active gateway id\u002Fname\u002Furl, operator and node role\nstates, credential sources\u002Fstatuses, MCP enabled\u002Frunning\u002Ferror state, browser\nproxy shared-token caveat, pending approval commands, retry hints inferred from\nrecent diagnostics, and recent connection diagnostic events. `effectiveMode`\nreflects Settings mode (`EnableNodeMode` \u002F `EnableMcpServer`); `node.intended`\nreflects the manager snapshot plus current Node mode setting. `gateway.packageVersion`\nis the installed Gateway npm package version reported by `hello-ok`; it is\nseparate from the wire protocol. `protocol` reports the accepted Gateway wire\nprotocol, the Windows-supported current\u002Fminimum\u002Fmaximum values (currently\n4\u002F3\u002F4), normalized compatibility\n(`compatible`, `gateway_too_old`, `gateway_too_new`, `mismatch`, or `unknown`),\nthe operator\u002Fnode source, available Gateway protocol expectations, and whether\nthe condition is retryable. During an active mismatch, `selectedProtocol` comes\nonly from that connection attempt and is otherwise null, even if cached Gateway\nself information remains available.\n\n### app.connection.gateways\nRead-only saved gateway diagnostics. No params. Returns:\n`{ activeGatewayId, count, gateways[] }`.\n\nEach gateway includes id\u002Fname\u002Furl, active flag, local\u002Fv2 flags, lastConnected,\ncredential presence booleans (`hasSharedGatewayToken`, `hasBootstrapToken`),\nbrowser-control port\u002Fcaveat, and SSH tunnel metadata. Token values are never\nreturned.\n\n### app.connection.applySetupCode\nApply a setup or QR code and connect the tray to that gateway.\n```\n{\"setupCode\": \"string\"}      \u002F\u002F required\n```\nReturns `{ outcome, error, gatewayUrl, connected }`. Side effects: creates or\nupdates a gateway record, sets it active, persists credentials from the setup\ncode, and asks `GatewayConnectionManager` to connect.\n\n### app.connection.connectSharedToken\nConnect with a shared gateway token.\n```\n{\n  \"gatewayUrl\": \"string\",    \u002F\u002F required\n  \"token\": \"string\"          \u002F\u002F required\n}\n```\nReturns `{ outcome, error, gatewayUrl, connected }`. Side effects: creates or\nupdates a gateway record, stores the shared token on that record, sets it active,\nand asks `GatewayConnectionManager` to connect.\n\n### app.connection.pendingApprovals\nRead pending device\u002Fnode pairing approvals from the connected gateway. No params.\nReturns `{ connected, error, totalPending, devicePending[], nodePending[] }`.\n\n### app.connection.approveDevicePairing\nApprove a pending device pairing request.\n```\n{\"requestId\": \"string\"}      \u002F\u002F required; \"id\" alias accepted\n```\nReturns the refreshed pending approvals payload plus `{ decision }`. Side effect:\ncalls the connected operator client's device-pair approval RPC.\n\n### app.connection.rejectDevicePairing\nReject a pending device pairing request.\n```\n{\"requestId\": \"string\"}      \u002F\u002F required; \"id\" alias accepted\n```\nReturns the refreshed pending approvals payload plus `{ decision }`. Side effect:\ncalls the connected operator client's device-pair rejection RPC.\n\n### app.connection.approveNodePairing\nApprove a pending Windows node pairing or command-trust request.\n```\n{\"requestId\": \"string\"}      \u002F\u002F required; \"id\" alias accepted\n```\nReturns the refreshed pending approvals payload plus `{ decision }`. Side effect:\ncalls the connected operator client's node-pair approval RPC.\n\n### app.connection.rejectNodePairing\nReject a pending Windows node pairing or command-trust request.\n```\n{\"requestId\": \"string\"}      \u002F\u002F required; \"id\" alias accepted\n```\nReturns the refreshed pending approvals payload plus `{ decision }`. Side effect:\ncalls the connected operator client's node-pair rejection RPC.\n\n### app.connection.reconnect\nReconnect the active gateway through `GatewayConnectionManager`. No params.\nReturns `{ reconnected, error? }`. Side effect: disconnects and reconnects the\noperator\u002Fnode lifecycle for the active gateway.\n\n### app.connection.reconnectNode\nReconnect only the Windows node role for the active gateway through\n`GatewayConnectionManager`. No params. Returns `{ reconnected, error? }`.\nSide effect: restarts node connection intent without changing the active gateway.\n\n### app.chat.snapshot\nRead the current native chat snapshot for local automation and diagnostics.\n**READ-ALL:** returns recent chat text from the selected timeline and outgoing\nqueue text.\n```\n{\"threadId\": \"string\"}       \u002F\u002F optional; \"sessionKey\" alias also accepted\n```\nReturns `{ connectionStatus, defaultThreadId, composeTarget, threads, queue, selectedTimeline }`.\n\n### app.chat.send\nSend a message through the same native chat provider used by the Chat UI.\n```\n{\n  \"message\": \"string\",       \u002F\u002F required\n  \"threadId\": \"string\"       \u002F\u002F optional; defaults to compose\u002Fdefault thread\n}\n```\nReturns `{ sent, threadId, entryCount, turnActive, error? }`.\n\n### app.chat.reset\nReset the target chat session through the gateway `sessions.reset` path.\n```\n{\"threadId\": \"string\"}       \u002F\u002F optional; \"sessionKey\" alias also accepted\n```\nWhen `threadId`\u002F`sessionKey` is omitted this resets the current compose\u002Fdefault\nthread, which usually means the active chat session. Returns `{ reset, threadId,\nerror? }`.\n\n### app.chat.queue.list\nList native chat outgoing queue entries.\n**READ-ALL:** returns queued message text.\n```\n{\"threadId\": \"string\"}       \u002F\u002F optional; \"sessionKey\" alias also accepted\n```\nWhen `threadId`\u002F`sessionKey` is omitted this returns all queued threads. Returns:\n`{ defaultThreadId, requestedThreadId, totalCount, selectedThread, threads }`\nwhere each thread has `{ threadId, count, messages }`, and each message has\n`{ id, text, createdAt, sendState, errorText, canCancel }`. Queue message IDs\nare ephemeral UI\u002Fprovider IDs; read them from the current `app.chat.queue.list`\nor `app.chat.snapshot` response and do not cache them across app lifetimes.\n\n### app.chat.queue.cancel\nCancel\u002Fremove one native chat outgoing queue entry before it is sent.\n```\n{\n  \"queuedMessageId\": \"string\", \u002F\u002F required; from the current app.chat.queue.list or app.chat.snapshot queue\n  \"threadId\": \"string\"         \u002F\u002F required; \"sessionKey\" alias also accepted\n}\n```\nOnly `Queued` and `Failed` entries can be removed. `Sending` entries may already\nhave reached the gateway and are not canceled. Returns\n`{ canceled, threadId, queuedMessageId, remainingCount, error? }`.\n\n## Location (location.*)\n\n### location.get\nGet the device's current geographic location.\n```\n{\n  \"accuracy\": \"default|high\",  \u002F\u002F optional, default \"default\"\n  \"maxAge\": 30000,             \u002F\u002F ms; return a cached fix if younger than this\n  \"locationTimeout\": 10000     \u002F\u002F ms; fail if no fix within this time\n}\n```\nReturns `{ latitude, longitude, accuracy (meters), timestamp (ms) }`.\nRequires the Location capability to be enabled and OS location permission granted to the app.\nThe first use also requires remembered OpenClaw consent. Every invocation\ndisplays a visible notification before location access begins.\nError `LOCATION_PERMISSION_REQUIRED` if the user has not granted location access.\n\n## Device (device.*)\n\n### device.info\nGet static device metadata. No params.\nReturns `{ deviceName, modelIdentifier, systemName, systemVersion, appVersion, appBuild, locale }`.\n\n### device.status\nGet live system health data.\n```\n{\n  \"sections\": [\"os\",\"cpu\",\"memory\",\"disk\",\"battery\"]  \u002F\u002F optional; omit for all\n}\n```\nReturns a map with `collectedAt` (ISO-8601 string) and one key per section.\nEach section may contain `{ error: \"collection failed\" }` if data was unavailable.\nLegacy fields always present: `thermal`, `storage`, `network`, `uptimeSeconds`.\n\nBattery sub-object: `{ level, state (\"charging\"|\"discharging\"|\"unknown\"), lowPowerModeEnabled }`.\n\n**Privacy note**: `device.status` reveals battery level, network type, and disk usage.\nAgents should request only the sections they need.\n\n## Browser control proxy (browser.*)\n\n### browser.proxy\nProxy an HTTP request to the local OpenClaw browser control host (Chrome DevTools Protocol server) running on gateway port + 2.\n```\n{\n  \"path\": \"\u002Fjson\u002Flist\",        \u002F\u002F required - local control path\n  \"method\": \"GET\",             \u002F\u002F optional, default GET; allowed: GET|POST|DELETE\n  \"body\": {},                  \u002F\u002F JSON object, for POST\u002FDELETE\n  \"query\": {},                 \u002F\u002F appended as query-string params\n  \"profile\": \"Default\",        \u002F\u002F optional browser profile name\n  \"timeoutMs\": 20000           \u002F\u002F optional, max 120000\n}\n```\nReturns `{ result, files? }` - `files` is an array of `{ path, base64, mimeType }` if the response referenced local file paths.\n\nRequires the gateway URL to have an explicit port (e.g. `ws:\u002F\u002Flocalhost:8080`).\nThe browser control host must be running locally on `127.0.0.1:\u003CgatewayPort + 2>`.\n\nListed only when `BrowserProxyActivation` is `RegistrationBlock.None`: the\ntoggle is on, a gateway client is attached, the shared gateway token is set,\nand the browser endpoint is verified. MCP-only startup (`EnableMcpServer`\ntrue, `EnableNodeMode` false) does not attach a gateway client, so this\ncommand stays off `tools\u002Flist` and the call returns `Unknown tool: browser.proxy`.\n\n---\n\n## A2UI v0.8 grammar (for canvas.a2ui.push)\n\nThe `jsonl` argument is a string of newline-separated JSON-RPC-like messages.\nThree message kinds are supported. **createSurface and v0.9 messages are\nrejected.**\n\n### Message kinds\n\n```jsonc\n\u002F\u002F 1. Declare components for a surface (creates the surface if new).\n{\"surfaceUpdate\": {\n  \"surfaceId\": \"string\",\n  \"components\": [ ComponentDef, ... ]\n}}\n\n\u002F\u002F 2. Pick the root component and (optionally) styles. Send AFTER surfaceUpdate.\n{\"beginRendering\": {\n  \"surfaceId\": \"string\",\n  \"root\": \"componentId\",\n  \"styles\": { \"primaryColor\": \"#FF6F61\", \"radius\": 8.0, \"spacing\": 12.0 }\n}}\n\n\u002F\u002F 3. Seed\u002Fupdate the data model bound by Path() values.\n{\"dataModelUpdate\": {\n  \"surfaceId\": \"string\",\n  \"contents\": [\n    {\"key\": \"headline\", \"valueString\":  \"Hi\"},\n    {\"key\": \"agreed\",   \"valueBoolean\": false},\n    {\"key\": \"volume\",   \"valueNumber\":  20.0}\n  ]\n}}\n\n\u002F\u002F 4. (Optional) Remove a surface.\n{\"deleteSurface\": {\"surfaceId\": \"string\"}}\n```\n\n### ComponentDef\n\n```jsonc\n{\"id\": \"uniqueId\", \"component\": {\"\u003CComponentName>\": { ...props }}}\n```\n\n### Value bindings (inside a component prop)\n\n| Form                          | Meaning                                |\n|-------------------------------|----------------------------------------|\n| `{\"literalString\": \"x\"}`      | Literal string                         |\n| `{\"path\": \"\u002Fkey\"}`            | Read\u002Fwrite the data model              |\n| Plain string `\"x\"`            | Component-id reference (e.g. `child`)  |\n| Plain number \u002F bool           | Used directly for numeric\u002Fbool props   |\n\n### Component catalog\n\n| Category     | Name          | Notable props |\n|--------------|---------------|---------------|\n| Container    | `Row`         | `children: {explicitList: [\"id\", ...]}` |\n| Container    | `Column`      | `children: {explicitList: [\"id\", ...]}` |\n| Container    | `List`        | `children`, `dataBinding` |\n| Container    | `Card`        | `child: \"id\"` |\n| Container    | `Tabs`        | `tabItems: [{title, child}]` |\n| Container    | `Modal`       | `child` |\n| Container    | `Divider`     | `axis: \"horizontal\" | \"vertical\"` |\n| Display      | `Text`        | `text: Lit\u002FPath`, `usageHint: \"h1|h2|h3|h4|h5|body|caption\"` |\n| Display      | `Image`       | `url: Lit\u002FPath`, `fit: \"contain|cover|fill|none\"`, `usageHint` |\n| Display      | `Icon`        | `name: Lit(\"settings\"\\|...)` |\n| Display      | `Video`       | `url`, `autoplay`, `controls` |\n| Display      | `AudioPlayer` | `url`, `controls` |\n| Interactive  | `Button`      | `child`, `primary: bool`, `action: {name, ...context}` |\n| Interactive  | `CheckBox`    | `label: Lit\u002FPath`, `value: Path` |\n| Interactive  | `TextField`   | `value: Path`, `textFieldType: \"shortText|longText|obscured\"` |\n| Interactive  | `DateTimeInput` | `value: Path`, `mode: \"date|time|datetime\"` |\n| Interactive  | `MultipleChoice` | `value: Path`, `options: [{value, label}]` |\n| Interactive  | `Slider`      | `value: Path`, `minValue`, `maxValue`, `step` |\n\nLit\u002FPath = the value-binding shapes from the previous section.\n\n### Minimal \"hello world\" payload\n\n```\n{\"surfaceUpdate\":{\"surfaceId\":\"hello\",\"components\":[{\"id\":\"helloText\",\"component\":{\"Text\":{\"text\":{\"literalString\":\"Hello, world!\"},\"usageHint\":\"h1\"}}}]}}\n{\"beginRendering\":{\"surfaceId\":\"hello\",\"root\":\"helloText\"}}\n```\n\nPass this as the `jsonl` value (a single JSON string with `\\n` between messages).\n\n---\n\n## Token-efficient call patterns\n\n1. **Skip `--node` \u002F `--idempotency-key`** - they're ignored locally; including\n   them just costs tokens. `--idempotency-key` triggers a stderr warning.\n2. **Omit `--params` when the command takes no args** (`camera.list`,\n   `canvas.hide`, `canvas.a2ui.reset`, `canvas.a2ui.dump`, `canvas.caps`,\n   `system.execApprovals.get`).\n3. **Large A2UI payloads** - write the JSONL to a file under the system temp\n   directory and pass `{\"jsonlPath\": \"\u003Cpath>\"}`. The capability rejects paths\n   outside `%TEMP%`. Or pass `--params @\u003Cpath>` to load the entire JSON\n   argument object from disk.\n4. **Big binary results (snapshots, captures)** - output is base64 in stdout.\n   Pipe to a file (`> capture.json`) instead of letting the agent read it\n   inline.\n5. **Errors are exit-code-driven** - check `$LASTEXITCODE` (or `$?` in bash)\n   first, then read stderr only on non-zero. Exit 2 = your call is malformed.\n6. **Debug with `--verbose`, not by sharing transcripts** - without\n   `--verbose` the CLI shows only the first line of an HTTP error body and\n   redacts long base64url runs. With `--verbose` it shows the full sanitized\n   body. Treat any verbose output as containing potentially sensitive paths\n   or partial command output before pasting it elsewhere.\n\n## What's NOT exposed\n- Pairing \u002F device approval (gateway concept; doesn't apply locally).\n- `chat.send`, `sessions.list`, `usage.list`, `node.list` - these belong to the\n  operator-side `OpenClaw.Cli.exe`, not `winnode.exe`.\n- Idempotency. The gateway de-dupes retries against `--idempotency-key`; local\n  MCP does not. Retrying a `system.run` \u002F `system.notify` \u002F `canvas.present`\n  call after a transient failure can double-execute the side effect.\n- Wildcards in `--command`. The MCP server has an explicit allowlist; unknown\n  commands return `Unknown tool: \u003Cname>`.\n","\u003C!--\n  REGENERATE-ME-WHEN-CAPABILITIES-CHANGE\n\n  The list of supported commands below is checked at CI time against the live\n  capability surface (see SkillMdDriftTests). When a capability is added,\n  removed, or renamed in src\u002FOpenClaw.Shared\u002FMcp\u002FMcpToolBridge.cs\n  (CommandDescriptions), update this document so the drift test stays green -\n  the test compares command identifiers, so prose can still be tweaked by hand.\n-->\n\n# winnode skill reference\n\n`winnode.exe` invokes OpenClaw Windows-node commands on the local tray over a\nloopback MCP HTTP endpoint (default `http:\u002F\u002F127.0.0.1:8765\u002F`). Enable\n**Local MCP Server** in Settings > Permissions > Integrations before calling.\nA connection refused on that port means the tray is not listening. Do not\nreinstall the CLI. Start the tray with `EnableMcpServer` true, then point\n`OPENCLAW_TRAY_DATA_DIR` at that tray's data directory so the CLI loads\n`mcp-token.txt`. `browser.proxy` stays off `tools\u002Flist` until the app has a\ngateway client, a shared token, and a verified browser endpoint. The call\nthen returns `Unknown tool: browser.proxy`.\n\nThis document is the agent-facing reference: every supported command, its\nargument shape, and the A2UI v0.8 JSONL grammar. It is shipped alongside\n`winnode.exe` so an agent can read it once and emit token-efficient calls.\n\n---\n\n## Invocation shape\n\n```text\nwinnode --command \u003Cname> [--params '\u003Cjson-object>'] [--invoke-timeout \u003Cms>] [--identity release|dev]\nwinnode --list-tools [--mcp-url \u003Curl>|--mcp-port \u003Cport>] [--identity release|dev]\n```\n\n- `--command` (required) - node command (e.g. `system.which`, `canvas.a2ui.push`).\n- `--list-tools` - query the live MCP server's `tools\u002Flist` method and print the\n  advertised tools. Useful when settings-gated capabilities differ from this\n  static reference.\n- `--params` - single JSON **object** string, default `{}`. Must be a JSON object,\n  not an array or scalar. **`--params @\u003Cpath>`** loads the JSON object from a\n  file on disk (useful for big A2UI payloads \u002F `canvas.eval` scripts).\n- `--invoke-timeout` - milliseconds, default 15000, max 600000 (10 min). HTTP\n  timeout adds a 5s buffer.\n- `--node` - accepted for parity with `openclaw nodes invoke`; **ignored**\n  locally. Safe to copy\u002Fpaste from gateway-side commands.\n- `--idempotency-key` - accepted for parity; **ignored**, and the CLI emits a\n  `[winnode] WARN` to stderr because local MCP does *not* dedupe retries -\n  re-running a command after a transient failure can double-execute side\n  effects. If you need idempotency, target the gateway, not winnode.\n- `--mcp-url \u003Curl>` \u002F `--mcp-port \u003Cport>` - override the endpoint. Falls back to\n  `OPENCLAW_MCP_PORT` env var, then port 8765. `--mcp-port` must be in\n  `[1, 65535]`; out of range fails with exit code 2.\n- `--mcp-token \u003Ctoken>` - bearer token override (testing \u002F explicit only). The\n  literal value is **visible to other same-user processes via the OS process\n  listing** (`Get-CimInstance Win32_Process | Select CommandLine`,\n  Process Explorer, etc.). The CLI emits a stderr warning when this flag is\n  used. **Prefer `OPENCLAW_MCP_TOKEN` (env var) or the on-disk\n  `%APPDATA%\\OpenClawTray\\mcp-token.txt`** which the release tray writes when\n  MCP is enabled. Both `OPENCLAW_MCP_TOKEN` and the on-disk file should\n  themselves be treated as sensitive operational secrets.\n- `--identity release|dev` - selects which tray profile supplies the default\n  on-disk MCP token. Defaults to `OPENCLAW_APP_IDENTITY`, then `release`.\n  Use `--identity dev` for a side-by-side dev tray; its default token path is\n  `%APPDATA%\\OpenClawTray-Dev\\mcp-token.txt`. `OPENCLAW_TRAY_DATA_DIR` still\n  wins for isolated runs and points directly at the data folder.\n- `--verbose` - log endpoint + ignored flags to stderr. Without `--verbose`,\n  HTTP error bodies are emitted only as the first line; with `--verbose`, the\n  full body is shown (after sanitization + token-shape redaction).\n- `--input-file \u003Cpath>` - raw local input for `file.create`. Computes size and\n  SHA256, checks any caller-supplied values, and streams bounded messages.\n- `--output-file \u003Cnew-path>` - raw local output for `file.fetch`; requires\n  explicit positive `maxBytes`. Never overwrites. Validates returned size and\n  SHA256 before publishing its owned partial file at the requested destination.\n  Both options use one `--invoke-timeout` deadline, preserve supplied expected\n  identity, and use ordinary preflight only to fill missing binding\u002Fpath fields.\n\n**Output contract:** stdout receives the capability payload as pretty-printed\nJSON (matches `openclaw nodes invoke`). stderr receives errors. Exit code:\n\n| Code | Meaning |\n|------|---------|\n| 0    | Success |\n| 1    | Tool error, JSON-RPC error, transport failure, or HTTP non-2xx |\n| 2    | Argument error (missing\u002Finvalid flags, bad `--params` JSON, out-of-range port\u002Ftimeout, non-http URL) |\n\n**Off-loopback safety:** when `--mcp-url` points at a non-loopback host, the\nCLI **refuses to send the auto-loaded local MCP token** (and warns on stderr).\nAn explicitly supplied `--mcp-token` is honored with a warning. This preserves\nthe loopback-only threat model the tray's MCP server relies on.\n\n---\n\n## Commands\n\n## Files and directories\n\nAll eight commands use the **existing Node Sandbox configuration and executor**,\nthe same as `system.run`. Supported Windows runs the fixed worker through MXC.\nPositively unsupported Windows uses explicit uncontained compatibility, with no\nSandbox filesystem\u002Fnetwork enforcement; failed probes, SDK or launch errors do\nnot downgrade. Existing Node\u002FMCP enablement applies, without new file permission\nswitches or shell exec approval prompts. Remote Gateway trust\u002Fallowlists and\nadmin checks remain independent. Settings changes apply to new operations only.\n\nUnary reads\u002Farchives default to 8 MiB and cap at 16 MiB. Writes\u002Fuploads cap at\n16 MiB decoded. CLI file envelopes use a command-specific bounded HTTP allowance,\nnot an increase to every tool's request\u002Fresponse limit. Ordinary MCP clients can\nuse unary and preflight calls without the optional framed extension.\n\n### file.stat\nInspect metadata: `{ \"path\": \"C:\\\\Data\\\\sample.txt\" }`.\nOptional `followSymlinks`, `preflightOnly`, `expectedCanonicalPath`,\n`expectedBinding`. Returns `{ok,path,type,size,mtimeMs,binding}` and\nan opaque identity `binding`. Preserve returned bindings without rewriting IDs.\nOrdinary local drive paths are supported. Reparse points, Cloud Files hydration,\ncase-sensitive directories and `followSymlinks:true` fail explicitly.\n\n### file.fetch\nUnary read: `{ \"path\": \"C:\\\\Data\\\\sample.txt\", \"maxBytes\": 16777216 }`.\nReturns `{ok,path,size,mimeType,base64,sha256,binding}`. Optional shared identity\nfields and `preflightOnly`. For binary, require `transport:\"binary\"`, explicit\npositive `maxBytes`, an existing `expectedBinding`, and framed IO.\n\n```powershell\nwinnode --command file.fetch --params '{\"path\":\"C:\\\\Data\\\\sample.bin\",\"maxBytes\":52428800}' --output-file .\\received.bin --invoke-timeout 60000\n```\n\n### dir.list\n`{ \"path\": \"C:\\\\Data\", \"maxEntries\": 200, \"pageToken\": \"0\" }`.\nReturns entries, `nextPageToken`, path and binding. Page size caps at 5000.\nChildren blocked by the current Sandbox policy are omitted, and paging counts\nonly visible entries. A blocked target directory still returns a permission\nerror. Optional `preflightOnly`, `followSymlinks` and shared identity fields.\n\n### dir.fetch\n`{ \"path\": \"C:\\\\Data\", \"maxBytes\": 16777216 }`.\nReturns `tarBase64`, `tarBytes`, `sha256`, `fileCount`, entries and binding.\nExpanded archive data caps at 64 MiB; entry count caps at 5000. Optional\n`preflightOnly`, `followSymlinks` and shared identity fields.\n\n### file.write\n`{ \"path\": \"C:\\\\Data\\\\sample.txt\", \"contentBase64\": \"YWJj\", \"overwrite\": false }`.\nAccepts canonical standard\u002Fbase64url, padded or unpadded. Optional\n`createParents`, `rejectHardlinks`, `expectedSha256`,\n`expectedCanonicalPath`, `expectedBinding`, `preflightOnly`.\nReturns `{ok,path,size,sha256,overwritten,binding}`. No overwrite by default.\n\n### file.create\nRequires `path`, `sizeBytes`, `expectedSha256`. Optional `maxBytes` (default\n50 MiB), `createParents`, identity fields and `preflightOnly`.\nPreflight needs no channel; real creation streams messages of at most 1 MiB,\nwaits for an `ack` after each, and sends an empty message for EOF.\nReturns `{ok,path,size,sha256,status:\"created\",binding}` when created. An existing\ntarget returns `status:\"exists\"` without size\u002Fhash fields and is not read back\nto verify content equality. Never overwrites.\n\n```powershell\nwinnode --command file.create --params '{\"path\":\"C:\\\\Data\\\\new.bin\"}' --input-file .\\source.bin --invoke-timeout 60000\n```\n\n### fs.listDir\nDirectory navigation, with optional `{ \"path\": \"C:\\\\Data\" }`. Remote calls are\nGateway-admin-gated; local calls use the existing MCP bearer. Neither grants\nelevation or bypasses contained data access. Children blocked by the current\nSandbox policy are omitted; requesting a blocked directory itself still fails.\n\n### terminal.upload\n`{ \"name\": \"sample.txt\", \"contentBase64\": \"YWJj\" }`.\nRequires canonical padded standard base64. Returns `{path,size}`.\nThe raw node command stages an upload without a terminal session. Remote calls\nrequire genuine Gateway admin authorization. The app schedules retained cleanup;\nthe same mode-selected worker scans and deletes only identity-owned upload data.\nNew uploads use automatic private storage belonging to the current Companion\nprofile, outside its settings and runtime-code folders. Downloads and the legacy\nupload-location setting receive no automatic grant. Contained ordinary commands\ncan read all retained uploads in that profile after ownership verification, but\ncannot modify them through the automatic grant. Only upload\u002Fcleanup operations\nreceive managed-storage write access. Explicit read-only\u002FNo access restrictions\nstill apply, including an inherited read-only policy. This is profile-scoped\nsharing, not per-file or per-conversation isolation.\n\nThe app establishes only the exact private profile boundary as trusted metadata\nbefore launching the contained worker. It checks current policy and reserves\ncleanup ownership first. Uploaded bytes and cleanup still run inside the worker,\nwithout granting write access to the boundary's parent. Cancellation or launch\nfailure can leave an owned empty boundary for later safe reuse. Unbound or\nunrecognized ownership requires review, not automatic adoption or deletion.\n\nThe committed transfer budget defaults to 50 MiB and cannot exceed 1024 MiB;\nvalid existing settings remain effective. This command still has the tighter\n16 MiB decoded unary limit. Retention remains bounded to 24 hours, 64 upload\ndirectories and 256 MiB. Cleanup of remembered legacy roots still requires their\ncurrent folder permissions and ownership identity. A blocked cleanup is not a\nreason to retry a completed upload.\n\n**Error semantics:** the six plugin commands retain the Gateway outer success\nenvelope with inner `{ok:false,code,message}`. The two admin commands use outer\nerrors. MCP converts typed semantic failure to `isError:true`; `winnode` exits 1.\nAn explicit binary\u002Fcreate call without a channel returns `DUPLEX_REQUIRED`.\nSee [local framed transport](..\u002F..\u002F..\u002Fdocs\u002FMCP_MODE.md#file-channel-extension-v1)\nfor the optional authenticated lease protocol. It is not standard MCP streaming.\n\n### system.notify\nShow a Windows toast notification.\n```\n{\"title\": \"OpenClaw\", \"body\": \"string\", \"subtitle\": \"string\", \"sound\": true}\n```\nReturns `{ \"sent\": true }`. All fields optional except `body` in practice.\n\n### system.run\nExecute canonical argv. Subject to the local exec approval policy at\n`%APPDATA%\\OpenClawTray\\exec-approvals.json`.\n\nSupported Windows uses `Microsoft.Mxc.Sdk` 1.0.0 containment. There is no Off\nsetting. Positively unsupported Windows instead runs approved argv uncontained,\nwith a persistent Node Sandbox warning and disabled sandbox permission controls.\nFile\u002Fnetwork\u002Fclipboard\u002FUI restrictions are not enforced there; approvals,\ndeadline, bounded output and revalidation remain active. Missing SDK components,\nunknown\u002Ffailed probes or native execution failures never downgrade. On supported\nWindows, unsupported\u002Funavailable policy and stale\nauthorization are tool errors (`isError=true`). Balanced reads the OS user profile\nand resolved personal\u002Fredirected folders; Open reads\u002Fwrites the same scope.\nNeither grants a whole drive or other users. Projects outside that scope need\nexplicit Custom grants. Readonly profile plus selected write children is\nsupported. Volume roots are never granted, even readonly. Missing required\nlocations block with diagnostics; optional unavailable OneDrive is not included.\nProtected OpenClaw settings,\nSSH, browser-profile and PSReadLine roots remain excluded in every scope.\nExplicit cwd must already be granted; omitted cwd uses private scratch.\nSettings affect new commands only. Pending requests revalidate before spawn.\nCancellation dismisses approval, releases the prompt lock, and explicitly\nterminates the owned SDK process, including a late native launch.\n```\n{\n  \"command\": [\"executable\", \"arg\", ...], \u002F\u002F required\n  \"rawCommand\": \"string\",                \u002F\u002F optional display metadata\n  \"cwd\":     \"string\",\n  \"timeoutMs\": 30000\n}\n```\nShell behavior must be explicit in the argv, for example\n`{\"command\":[\"cmd.exe\",\"\u002Fd\",\"\u002Fs\",\"\u002Fc\",\"echo hello\"],\"rawCommand\":\"echo hello\"}`.\nThe gateway's `exec host=node` path performs this wrapping automatically.\nNon-empty custom `env` is rejected until environment values can be identity-bound\nand shown safely during approval.\n\n#### Migration from the pre-V2 low-level contract\n\nThe V2 node boundary intentionally rejects string-form `command` with\n`command-array-required`. Update raw MCP, `node.invoke`, plugin, and direct\n`winnode` callers to send the shell transport explicitly:\n\n```json\n\u002F\u002F Before\n{\"command\":\"echo hello\",\"shell\":\"cmd\"}\n\n\u002F\u002F V2\n{\"command\":[\"cmd.exe\",\"\u002Fd\",\"\u002Fs\",\"\u002Fc\",\"echo hello\"],\"rawCommand\":\"echo hello\"}\n```\n\nPowerShell callers must likewise name `powershell.exe` or `pwsh.exe` and its\n`-Command` arguments explicitly. Remove custom `env` from the request; V2 rejects\nnon-empty environments rather than approving an executable under hidden process\nconfiguration.\nExited commands return `{ stdout, stderr, exitCode, timedOut, success, durationMs }`.\nRetained output is bounded by UTF-8 bytes per stream, including any truncation\nmarker, while discarded output continues to drain. Removing policy on exit\ndoes not undo writes to granted folders.\n\n### system.run.prepare\nPre-flight a `system.run` invocation. Same args as `system.run`. Returns the\nparsed plan (`argv`, `cwd`, `rawCommand`, `agentId`, `sessionKey`) without\nexecuting.\n\n### system.which\nResolve binary names to absolute paths.\n```\n{\"bins\": [\"git\", \"node\", \"powershell\"]}\n```\nReturns `{ \"bins\": { \"git\": \"C:\\\\...\", ... } }`. Names not found are omitted.\n\n### system.execApprovals.get\nNo params. Returns the active V2 snapshot:\n`{ path, exists, hash, file: { version, defaults: { security, ask, askFallback, autoAllowSkills }, agents: { \"\u003CagentId>\": { security, ask, askFallback, autoAllowSkills, allowlist: [{ id, pattern, lastUsedAt?, lastResolvedPath? }] } } } }`.\n`security` is `deny|allowlist|full`; `ask` is `off|on-miss|always|deny`;\n`askFallback` is `deny|allowlist|full`. Socket credentials are never returned.\nPass `hash` as `baseHash` when calling `system.execApprovals.set`.\n\n### system.execApprovals.set\nReplace the full V2 file using compare-and-swap. `baseHash` is required and must\nequal the latest hash returned by `system.execApprovals.get`.\n```\n{\n  \"baseHash\": \"\u003Chash-from-get>\",\n  \"file\": {\n    \"version\": 1,\n    \"defaults\": {\n      \"security\": \"allowlist\",\n      \"ask\": \"on-miss\",\n      \"askFallback\": \"deny\",\n      \"autoAllowSkills\": false\n    },\n    \"agents\": {\n      \"main\": {\n        \"security\": \"allowlist\",\n        \"ask\": \"on-miss\",\n        \"askFallback\": \"deny\",\n        \"autoAllowSkills\": false,\n        \"allowlist\": []\n      }\n    }\n  }\n}\n```\nReturns the updated snapshot. A remote update may preserve or remove existing\nallowlist grants, but it cannot add or change grants or set full access. Missing or\nstale hashes are rejected. This command changes future `system.run` authorization\ndecisions. The `winnode` path is local loopback MCP, so no gateway scope applies.\nRemote operators must use the gateway `exec.approvals.node.*` control-plane\nmethods, which are separately gated by gateway admin scope.\n\n### canvas.present\nOpen the WebView2 canvas window.\n```\n{\n  \"url\":   \"string\",          \u002F\u002F OR \"html\": \"string\"\n  \"html\":  \"string\",\n  \"width\": 800, \"height\": 600,\n  \"x\": -1, \"y\": -1,           \u002F\u002F -1 centers\n  \"title\": \"Canvas\",\n  \"alwaysOnTop\": false\n}\n```\nReturns `{ \"presented\": true }`.\n\n### canvas.hide\nNo params. Hides the canvas without destroying state.\n\n### canvas.navigate\n```\n{\"url\": \"https:\u002F\u002F...\"}    \u002F\u002F also accepts file:\u002F\u002F or local canvas paths\n```\n\n### canvas.eval\n```\n{\"script\": \"document.title\"}    \u002F\u002F also accepts \"javaScript\" or \"javascript\"\n```\nReturns the evaluated result.\n\n### canvas.snapshot\n```\n{\"format\": \"png|jpeg\", \"maxWidth\": 1200, \"quality\": 80}\n```\nReturns `{ format, base64 }`.\n\n### canvas.a2ui.push\nRender an A2UI v0.8 surface in the canvas. The canvas window opens\nautomatically - no `canvas.present` required.\n```\n{\n  \"jsonl\":     \"string\",   \u002F\u002F OR jsonlPath\n  \"jsonlPath\": \"string\",   \u002F\u002F must live under %TEMP%\n  \"props\":     {}           \u002F\u002F optional\n}\n```\nReturns `{ \"pushed\": true }`. **See A2UI grammar below.**\n\n### canvas.a2ui.pushJSONL\nAlias of `canvas.a2ui.push`. It uses the identical handler, size\u002Fline caps, and\n`jsonl`\u002F`jsonlPath` contract. It exists for naming parity, not as a separate\nstreaming path.\n\n### canvas.a2ui.reset\nNo params. Clears any rendered surfaces. Returns `{ \"reset\": true }`.\n\n### canvas.a2ui.dump\nNo params. Returns the current surface graph for introspection. **Read-all:**\nthis exposes every currently-rendered surface - operators should treat it as\nequivalent to a screenshot of every open A2UI surface.\n\n### canvas.caps\nNo params. Returns renderer capabilities (renderer, snapshot, a2ui version).\n\n### screen.snapshot\n```\n{\n  \"format\": \"png|jpeg\", \"maxWidth\": 1920, \"quality\": 80,\n  \"monitor\": 0, \"screenIndex\": 0,    \u002F\u002F 0 = primary\n  \"includePointer\": true\n}\n```\nReturns `{ format, width, height, base64, image }` (image is a `data:` URL).\nThe first use requires remembered user consent. Every invocation displays a\nvisible notification before screen capture begins.\n\n### screen.record\n```\n{\n  \"durationMs\": 5000,         \u002F\u002F required, max 300000\n  \"format\": \"mp4|webm\",\n  \"monitor\": 0, \"screenIndex\": 0,\n  \"maxWidth\": 1920, \"fps\": 30\n}\n```\nReturns `{ format, durationMs, base64 }`.\n\n### camera.list\nNo params. Returns `{ cameras: [{ deviceId, name, isDefault }] }`.\n\n### camera.snap\n```\n{\"deviceId\": \"string\", \"format\": \"jpeg|png\", \"maxWidth\": 1280, \"quality\": 80}\n```\nReturns `{ format, width, height, base64 }`. `deviceId` defaults to system\ndefault camera.\nThe first use requires remembered user consent. Every invocation displays a\nvisible notification before camera capture begins.\n\n### camera.clip\n```\n{\n  \"deviceId\": \"string\",       \u002F\u002F optional\n  \"durationMs\": 3000,         \u002F\u002F required, max 60000\n  \"format\": \"mp4|webm\",\n  \"maxWidth\": 1280\n}\n```\nReturns `{ format, durationMs, base64 }`.\n\n## Speech-to-text (stt.*)\n\nLocal Whisper.net runs on this device - no audio leaves the box. The\nmodel is downloaded on first use; until then every `stt.*` call returns\na clear error pointing the caller at the Voice Settings page.\n**Privacy-sensitive: requires `NodeSttEnabled` in tray Settings.**\n\n### stt.transcribe\nBounded fixed-duration mic capture + transcription.\n```\n{\n  \"maxDurationMs\": 5000,      \u002F\u002F required, > 0, max 30000\n  \"language\": \"en\"            \u002F\u002F optional BCP-47 tag or \"auto\" - falls back to SttLanguage setting\n}\n```\nReturns `{ transcribed, text, durationMs, language, engineEffective: \"whisper\" }`.\n\n### stt.listen\nMic capture with voice-activity detection. Returns when the user stops\nspeaking or after `timeoutMs`. Result is the full silence-bounded\nutterance (all Whisper segments concatenated), not a partial first\nsegment.\n```\n{\n  \"timeoutMs\": 30000,         \u002F\u002F optional, default 30000, range 1000..120000\n  \"language\": \"auto\"          \u002F\u002F optional BCP-47 tag or \"auto\"\n}\n```\nReturns `{ text, language, durationMs, segments[{ text, startMs, endMs }], engineEffective: \"whisper\" }`.\n\n### stt.status\nEngine readiness. No params. Carries no PII (no transcript history,\nno language history, no device IDs, no model paths).\nReturns `{ engine: \"whisper\", readiness, modelDownloadProgress, isListenWithVadSupported, isBoundedTranscribeSupported }`\nwhere `readiness` ∈ `\"ready\" | \"initializing\" | \"model-downloading\" | \"model-not-downloaded\" | \"unavailable\"`.\n\n## Text-to-speech (tts.*)\n\nFive providers - Kokoro (local neural via kokoro.cpp, default), Piper (local\nneural via Sherpa-ONNX), Windows built-in speech, ElevenLabs (cloud), and\nMiniMax (cloud). Provider + per-provider voice are configured in tray Settings.\nWhile Kokoro is the provider and TTS is enabled, the tray downloads the selected\nKokoro voice pack in the background on unmetered connections; until it is ready,\n`tts.status` reports `voice-not-downloaded` and speech falls back to Windows.\n\n### tts.speak\nSpeak text aloud on the Windows node.\n```\n{\n  \"text\": \"string\",           \u002F\u002F required\n  \"provider\": \"kokoro|piper|windows|elevenlabs|minimax\",  \u002F\u002F optional; omit to use TtsProvider setting\n  \"voiceId\": \"string\",        \u002F\u002F optional, overrides the per-provider configured voice\n  \"model\": \"string\",          \u002F\u002F optional, cloud providers only\n  \"interrupt\": false          \u002F\u002F default false; true cuts off any in-progress playback\n}\n```\nWhen `provider` is omitted and the configured provider isn't usable (no\ncloud API key, Kokoro voice pack or Piper voice not downloaded), the node\nfalls back to Windows TTS so playback still happens. Explicit `provider` requests stay strict and\ndo not silently reroute. Returns `{ spoken, provider, requestedProvider, fellBack, contentType, durationMs }`\nwhere `provider` is the provider that actually spoke.\n\n### tts.status\nTTS provider readiness. No params. Carries no PII (no voice ids, no key\nfragments, no device names).\nReturns `{ configuredProvider, effectiveProvider, willFallBack, providers[{ provider, readiness, isReady }] }`\nwhere `effectiveProvider` is what would run now after fallback and `readiness`\n∈ `\"ready\" | \"needs-api-key\" | \"needs-voice\" | \"voice-not-downloaded\" | \"unavailable\"`.\nThe configured\u002Feffective view reflects configured defaults only; explicit\n`tts.speak` provider requests stay strict and may not match the default\nsnapshot.\n\n## Local model inference (ollama.*)\n\nRuns against a separately installed Windows Ollama service (not the\napp-managed Local AI gateway provider). Any paired active gateway, local\nor remote, may invoke it once enabled. **Requires\n`NodeOllamaInferenceEnabled` in tray Settings > Permissions (opt-in,\ndefault off).**\n\n### ollama.models\nList locally installed Ollama models. Read-only inventory - no prompt\nor chat content. No params.\nReturns `{ provider, models[{ name, size, modifiedAt, family, parameterSize, quantization, contextWindow, capabilities, loaded }] }`.\n\n### ollama.chat\nSend a single-turn chat prompt to a local Ollama model.\n**Privacy and resource sensitive** - sends prompt content to a locally\nrunning model and consumes CPU\u002FGPU. Only one chat runs at a time per node;\na concurrent call while one is in flight returns an error.\n```\n{\n  \"model\": \"string\",          \u002F\u002F required\n  \"prompt\": \"string\",         \u002F\u002F required, max 128000 chars\n  \"system\": \"string\",         \u002F\u002F optional, max 32000 chars\n  \"temperature\": 0.7,         \u002F\u002F optional, 0..2\n  \"maxTokens\": 512,           \u002F\u002F optional, default 512, max 8192\n  \"timeoutMs\": 120000         \u002F\u002F optional, default 120000, max 600000\n}\n```\nReturns `{ provider, model, response, usage?: { promptTokens, completionTokens }, timings?: { loadMs, totalMs } }`.\n\nNewer gateways with the bundled Ollama plugin expose these commands through\nthe `node_inference` agent tool. Older gateways can invoke them through the\ngeneric `nodes` tool after both exact command names are added to\n`gateway.nodes.allowCommands`. Feature-detect the node's effective commands;\ndo not infer support from the gateway version.\n\n## App control (app.*)\n\nRead-only and small write operations targeting the running tray. Used\nby the command palette and by automation that wants to drive the UI.\n\n### app.navigate\nNavigate Workspace or the Settings companion to a specific page.\n```json\n{\"page\": \"workspace:home\"}\n```\nReturns `{ navigated, page }`. `home` and `chat` open Workspace chat;\n`workspace:notifications` opens Workspace notifications. Unprefixed routes\nsuch as `sessions`, `settings`, and `connection` open the Settings companion.\nDeprecated Workspace routes return Home. Unknown `workspace:` routes return\na tool error before any window is opened or navigation is forwarded.\n\n### app.status\nCurrent connection \u002F node state.\nNo params. Returns `{ connectionStatus, overallState, operatorState, nodeState, nodeConnected, nodePaired, nodePendingApproval, nodeError, gatewayVersion, sessionCount, nodeCount }`.\nFor agent-facing connection troubleshooting, prefer `app.connection.status`.\n\n### app.sessions\nActive sessions, optionally filtered by agent.\n```\n{\"agentId\": \"string\"}        \u002F\u002F optional\n```\nReturns array of `{ Key, Status, Model, AgeText, tokens }`.\n\n### app.agents\nList agents from the connected gateway. No params. Returns the raw\nagents JSON array.\n\n### app.nodes\nList connected nodes and their capabilities. No params. Returns array\nof `{ DisplayName, NodeId, IsOnline, Platform, CapabilityCount }`.\n\n### app.config.get\nRead gateway configuration value at a dot-path.\n```\n{\"path\": \"string\"}           \u002F\u002F optional; omit to fetch the full config tree\n```\nReturns the config subtree (or full config) as JSON.\n\n### app.settings.get\nRead a local app setting by name.\n```\n{\"name\": \"string\"}           \u002F\u002F required\n```\nReturns the setting value (type depends on the setting).\n\n### app.settings.set\nSet a local app setting, persist it, and apply the same reconnect\u002Freload behavior as saving settings in the app UI.\n```\n{\"name\": \"string\", \"value\": \"string\"}  \u002F\u002F both required\n```\nReturns `{ name, value }`; runtime apply failures surface as tool errors.\n\n### app.menu\nGet tray menu state (status, session count, node count). No params.\nReturns array of menu items; the status item includes `status`, `overallState`, `nodeState`, and `nodeError`.\n\n### app.search\nSearch locally available navigation destinations without opening them or making\nGateway requests. Settings results identify their destination rather than toggle\nanything. Message snippets are excluded.\n```\n{\"query\": \"string\"}          \u002F\u002F required\n```\nReturns up to 10 `{ Title, Subtitle, Icon, Group, OpensDashboard, HasDashboardCounterpart }`.\n\n### app.dashboard.url\nBuild the same gateway dashboard URL the tray opens.\n```\n{\"path\": \"string\"}           \u002F\u002F optional\n```\nReturns `{ url, credentialSource, usesSharedGatewayToken, hasTokenQuery }`.\n\n## App connection diagnostics and setup (app.connection.*)\n\nLocal MCP-only tools for connection diagnostics, setup, pairing approvals, and\ntargeted reconnects. These tools are not advertised to the remote gateway node\ntransport. Read-only diagnostics redact token values and expose credential\npresence\u002Foutcome only.\n\n### app.connection.status\nRead-only connection diagnostics for agents and CLIs. No params. Returns:\n`{ schemaVersion, connectionState, effectiveMode, legacyConnectionStatus, gateway, protocol, operator, node, mcp, browserProxy, pendingActions, retry, diagnostics }`.\n\nThe payload includes the active gateway id\u002Fname\u002Furl, operator and node role\nstates, credential sources\u002Fstatuses, MCP enabled\u002Frunning\u002Ferror state, browser\nproxy shared-token caveat, pending approval commands, retry hints inferred from\nrecent diagnostics, and recent connection diagnostic events. `effectiveMode`\nreflects Settings mode (`EnableNodeMode` \u002F `EnableMcpServer`); `node.intended`\nreflects the manager snapshot plus current Node mode setting. `gateway.packageVersion`\nis the installed Gateway npm package version reported by `hello-ok`; it is\nseparate from the wire protocol. `protocol` reports the accepted Gateway wire\nprotocol, the Windows-supported current\u002Fminimum\u002Fmaximum values (currently\n4\u002F3\u002F4), normalized compatibility\n(`compatible`, `gateway_too_old`, `gateway_too_new`, `mismatch`, or `unknown`),\nthe operator\u002Fnode source, available Gateway protocol expectations, and whether\nthe condition is retryable. During an active mismatch, `selectedProtocol` comes\nonly from that connection attempt and is otherwise null, even if cached Gateway\nself information remains available.\n\n### app.connection.gateways\nRead-only saved gateway diagnostics. No params. Returns:\n`{ activeGatewayId, count, gateways[] }`.\n\nEach gateway includes id\u002Fname\u002Furl, active flag, local\u002Fv2 flags, lastConnected,\ncredential presence booleans (`hasSharedGatewayToken`, `hasBootstrapToken`),\nbrowser-control port\u002Fcaveat, and SSH tunnel metadata. Token values are never\nreturned.\n\n### app.connection.applySetupCode\nApply a setup or QR code and connect the tray to that gateway.\n```\n{\"setupCode\": \"string\"}      \u002F\u002F required\n```\nReturns `{ outcome, error, gatewayUrl, connected }`. Side effects: creates or\nupdates a gateway record, sets it active, persists credentials from the setup\ncode, and asks `GatewayConnectionManager` to connect.\n\n### app.connection.connectSharedToken\nConnect with a shared gateway token.\n```\n{\n  \"gatewayUrl\": \"string\",    \u002F\u002F required\n  \"token\": \"string\"          \u002F\u002F required\n}\n```\nReturns `{ outcome, error, gatewayUrl, connected }`. Side effects: creates or\nupdates a gateway record, stores the shared token on that record, sets it active,\nand asks `GatewayConnectionManager` to connect.\n\n### app.connection.pendingApprovals\nRead pending device\u002Fnode pairing approvals from the connected gateway. No params.\nReturns `{ connected, error, totalPending, devicePending[], nodePending[] }`.\n\n### app.connection.approveDevicePairing\nApprove a pending device pairing request.\n```\n{\"requestId\": \"string\"}      \u002F\u002F required; \"id\" alias accepted\n```\nReturns the refreshed pending approvals payload plus `{ decision }`. Side effect:\ncalls the connected operator client's device-pair approval RPC.\n\n### app.connection.rejectDevicePairing\nReject a pending device pairing request.\n```\n{\"requestId\": \"string\"}      \u002F\u002F required; \"id\" alias accepted\n```\nReturns the refreshed pending approvals payload plus `{ decision }`. Side effect:\ncalls the connected operator client's device-pair rejection RPC.\n\n### app.connection.approveNodePairing\nApprove a pending Windows node pairing or command-trust request.\n```\n{\"requestId\": \"string\"}      \u002F\u002F required; \"id\" alias accepted\n```\nReturns the refreshed pending approvals payload plus `{ decision }`. Side effect:\ncalls the connected operator client's node-pair approval RPC.\n\n### app.connection.rejectNodePairing\nReject a pending Windows node pairing or command-trust request.\n```\n{\"requestId\": \"string\"}      \u002F\u002F required; \"id\" alias accepted\n```\nReturns the refreshed pending approvals payload plus `{ decision }`. Side effect:\ncalls the connected operator client's node-pair rejection RPC.\n\n### app.connection.reconnect\nReconnect the active gateway through `GatewayConnectionManager`. No params.\nReturns `{ reconnected, error? }`. Side effect: disconnects and reconnects the\noperator\u002Fnode lifecycle for the active gateway.\n\n### app.connection.reconnectNode\nReconnect only the Windows node role for the active gateway through\n`GatewayConnectionManager`. No params. Returns `{ reconnected, error? }`.\nSide effect: restarts node connection intent without changing the active gateway.\n\n### app.chat.snapshot\nRead the current native chat snapshot for local automation and diagnostics.\n**READ-ALL:** returns recent chat text from the selected timeline and outgoing\nqueue text.\n```\n{\"threadId\": \"string\"}       \u002F\u002F optional; \"sessionKey\" alias also accepted\n```\nReturns `{ connectionStatus, defaultThreadId, composeTarget, threads, queue, selectedTimeline }`.\n\n### app.chat.send\nSend a message through the same native chat provider used by the Chat UI.\n```\n{\n  \"message\": \"string\",       \u002F\u002F required\n  \"threadId\": \"string\"       \u002F\u002F optional; defaults to compose\u002Fdefault thread\n}\n```\nReturns `{ sent, threadId, entryCount, turnActive, error? }`.\n\n### app.chat.reset\nReset the target chat session through the gateway `sessions.reset` path.\n```\n{\"threadId\": \"string\"}       \u002F\u002F optional; \"sessionKey\" alias also accepted\n```\nWhen `threadId`\u002F`sessionKey` is omitted this resets the current compose\u002Fdefault\nthread, which usually means the active chat session. Returns `{ reset, threadId,\nerror? }`.\n\n### app.chat.queue.list\nList native chat outgoing queue entries.\n**READ-ALL:** returns queued message text.\n```\n{\"threadId\": \"string\"}       \u002F\u002F optional; \"sessionKey\" alias also accepted\n```\nWhen `threadId`\u002F`sessionKey` is omitted this returns all queued threads. Returns:\n`{ defaultThreadId, requestedThreadId, totalCount, selectedThread, threads }`\nwhere each thread has `{ threadId, count, messages }`, and each message has\n`{ id, text, createdAt, sendState, errorText, canCancel }`. Queue message IDs\nare ephemeral UI\u002Fprovider IDs; read them from the current `app.chat.queue.list`\nor `app.chat.snapshot` response and do not cache them across app lifetimes.\n\n### app.chat.queue.cancel\nCancel\u002Fremove one native chat outgoing queue entry before it is sent.\n```\n{\n  \"queuedMessageId\": \"string\", \u002F\u002F required; from the current app.chat.queue.list or app.chat.snapshot queue\n  \"threadId\": \"string\"         \u002F\u002F required; \"sessionKey\" alias also accepted\n}\n```\nOnly `Queued` and `Failed` entries can be removed. `Sending` entries may already\nhave reached the gateway and are not canceled. Returns\n`{ canceled, threadId, queuedMessageId, remainingCount, error? }`.\n\n## Location (location.*)\n\n### location.get\nGet the device's current geographic location.\n```\n{\n  \"accuracy\": \"default|high\",  \u002F\u002F optional, default \"default\"\n  \"maxAge\": 30000,             \u002F\u002F ms; return a cached fix if younger than this\n  \"locationTimeout\": 10000     \u002F\u002F ms; fail if no fix within this time\n}\n```\nReturns `{ latitude, longitude, accuracy (meters), timestamp (ms) }`.\nRequires the Location capability to be enabled and OS location permission granted to the app.\nThe first use also requires remembered OpenClaw consent. Every invocation\ndisplays a visible notification before location access begins.\nError `LOCATION_PERMISSION_REQUIRED` if the user has not granted location access.\n\n## Device (device.*)\n\n### device.info\nGet static device metadata. No params.\nReturns `{ deviceName, modelIdentifier, systemName, systemVersion, appVersion, appBuild, locale }`.\n\n### device.status\nGet live system health data.\n```\n{\n  \"sections\": [\"os\",\"cpu\",\"memory\",\"disk\",\"battery\"]  \u002F\u002F optional; omit for all\n}\n```\nReturns a map with `collectedAt` (ISO-8601 string) and one key per section.\nEach section may contain `{ error: \"collection failed\" }` if data was unavailable.\nLegacy fields always present: `thermal`, `storage`, `network`, `uptimeSeconds`.\n\nBattery sub-object: `{ level, state (\"charging\"|\"discharging\"|\"unknown\"), lowPowerModeEnabled }`.\n\n**Privacy note**: `device.status` reveals battery level, network type, and disk usage.\nAgents should request only the sections they need.\n\n## Browser control proxy (browser.*)\n\n### browser.proxy\nProxy an HTTP request to the local OpenClaw browser control host (Chrome DevTools Protocol server) running on gateway port + 2.\n```\n{\n  \"path\": \"\u002Fjson\u002Flist\",        \u002F\u002F required - local control path\n  \"method\": \"GET\",             \u002F\u002F optional, default GET; allowed: GET|POST|DELETE\n  \"body\": {},                  \u002F\u002F JSON object, for POST\u002FDELETE\n  \"query\": {},                 \u002F\u002F appended as query-string params\n  \"profile\": \"Default\",        \u002F\u002F optional browser profile name\n  \"timeoutMs\": 20000           \u002F\u002F optional, max 120000\n}\n```\nReturns `{ result, files? }` - `files` is an array of `{ path, base64, mimeType }` if the response referenced local file paths.\n\nRequires the gateway URL to have an explicit port (e.g. `ws:\u002F\u002Flocalhost:8080`).\nThe browser control host must be running locally on `127.0.0.1:\u003CgatewayPort + 2>`.\n\nListed only when `BrowserProxyActivation` is `RegistrationBlock.None`: the\ntoggle is on, a gateway client is attached, the shared gateway token is set,\nand the browser endpoint is verified. MCP-only startup (`EnableMcpServer`\ntrue, `EnableNodeMode` false) does not attach a gateway client, so this\ncommand stays off `tools\u002Flist` and the call returns `Unknown tool: browser.proxy`.\n\n---\n\n## A2UI v0.8 grammar (for canvas.a2ui.push)\n\nThe `jsonl` argument is a string of newline-separated JSON-RPC-like messages.\nThree message kinds are supported. **createSurface and v0.9 messages are\nrejected.**\n\n### Message kinds\n\n```jsonc\n\u002F\u002F 1. Declare components for a surface (creates the surface if new).\n{\"surfaceUpdate\": {\n  \"surfaceId\": \"string\",\n  \"components\": [ ComponentDef, ... ]\n}}\n\n\u002F\u002F 2. Pick the root component and (optionally) styles. Send AFTER surfaceUpdate.\n{\"beginRendering\": {\n  \"surfaceId\": \"string\",\n  \"root\": \"componentId\",\n  \"styles\": { \"primaryColor\": \"#FF6F61\", \"radius\": 8.0, \"spacing\": 12.0 }\n}}\n\n\u002F\u002F 3. Seed\u002Fupdate the data model bound by Path() values.\n{\"dataModelUpdate\": {\n  \"surfaceId\": \"string\",\n  \"contents\": [\n    {\"key\": \"headline\", \"valueString\":  \"Hi\"},\n    {\"key\": \"agreed\",   \"valueBoolean\": false},\n    {\"key\": \"volume\",   \"valueNumber\":  20.0}\n  ]\n}}\n\n\u002F\u002F 4. (Optional) Remove a surface.\n{\"deleteSurface\": {\"surfaceId\": \"string\"}}\n```\n\n### ComponentDef\n\n```jsonc\n{\"id\": \"uniqueId\", \"component\": {\"\u003CComponentName>\": { ...props }}}\n```\n\n### Value bindings (inside a component prop)\n\n| Form                          | Meaning                                |\n|-------------------------------|----------------------------------------|\n| `{\"literalString\": \"x\"}`      | Literal string                         |\n| `{\"path\": \"\u002Fkey\"}`            | Read\u002Fwrite the data model              |\n| Plain string `\"x\"`            | Component-id reference (e.g. `child`)  |\n| Plain number \u002F bool           | Used directly for numeric\u002Fbool props   |\n\n### Component catalog\n\n| Category     | Name          | Notable props |\n|--------------|---------------|---------------|\n| Container    | `Row`         | `children: {explicitList: [\"id\", ...]}` |\n| Container    | `Column`      | `children: {explicitList: [\"id\", ...]}` |\n| Container    | `List`        | `children`, `dataBinding` |\n| Container    | `Card`        | `child: \"id\"` |\n| Container    | `Tabs`        | `tabItems: [{title, child}]` |\n| Container    | `Modal`       | `child` |\n| Container    | `Divider`     | `axis: \"horizontal\" | \"vertical\"` |\n| Display      | `Text`        | `text: Lit\u002FPath`, `usageHint: \"h1|h2|h3|h4|h5|body|caption\"` |\n| Display      | `Image`       | `url: Lit\u002FPath`, `fit: \"contain|cover|fill|none\"`, `usageHint` |\n| Display      | `Icon`        | `name: Lit(\"settings\"\\|...)` |\n| Display      | `Video`       | `url`, `autoplay`, `controls` |\n| Display      | `AudioPlayer` | `url`, `controls` |\n| Interactive  | `Button`      | `child`, `primary: bool`, `action: {name, ...context}` |\n| Interactive  | `CheckBox`    | `label: Lit\u002FPath`, `value: Path` |\n| Interactive  | `TextField`   | `value: Path`, `textFieldType: \"shortText|longText|obscured\"` |\n| Interactive  | `DateTimeInput` | `value: Path`, `mode: \"date|time|datetime\"` |\n| Interactive  | `MultipleChoice` | `value: Path`, `options: [{value, label}]` |\n| Interactive  | `Slider`      | `value: Path`, `minValue`, `maxValue`, `step` |\n\nLit\u002FPath = the value-binding shapes from the previous section.\n\n### Minimal \"hello world\" payload\n\n```\n{\"surfaceUpdate\":{\"surfaceId\":\"hello\",\"components\":[{\"id\":\"helloText\",\"component\":{\"Text\":{\"text\":{\"literalString\":\"Hello, world!\"},\"usageHint\":\"h1\"}}}]}}\n{\"beginRendering\":{\"surfaceId\":\"hello\",\"root\":\"helloText\"}}\n```\n\nPass this as the `jsonl` value (a single JSON string with `\\n` between messages).\n\n---\n\n## Token-efficient call patterns\n\n1. **Skip `--node` \u002F `--idempotency-key`** - they're ignored locally; including\n   them just costs tokens. `--idempotency-key` triggers a stderr warning.\n2. **Omit `--params` when the command takes no args** (`camera.list`,\n   `canvas.hide`, `canvas.a2ui.reset`, `canvas.a2ui.dump`, `canvas.caps`,\n   `system.execApprovals.get`).\n3. **Large A2UI payloads** - write the JSONL to a file under the system temp\n   directory and pass `{\"jsonlPath\": \"\u003Cpath>\"}`. The capability rejects paths\n   outside `%TEMP%`. Or pass `--params @\u003Cpath>` to load the entire JSON\n   argument object from disk.\n4. **Big binary results (snapshots, captures)** - output is base64 in stdout.\n   Pipe to a file (`> capture.json`) instead of letting the agent read it\n   inline.\n5. **Errors are exit-code-driven** - check `$LASTEXITCODE` (or `$?` in bash)\n   first, then read stderr only on non-zero. Exit 2 = your call is malformed.\n6. **Debug with `--verbose`, not by sharing transcripts** - without\n   `--verbose` the CLI shows only the first line of an HTTP error body and\n   redacts long base64url runs. With `--verbose` it shows the full sanitized\n   body. Treat any verbose output as containing potentially sensitive paths\n   or partial command output before pasting it elsewhere.\n\n## What's NOT exposed\n- Pairing \u002F device approval (gateway concept; doesn't apply locally).\n- `chat.send`, `sessions.list`, `usage.list`, `node.list` - these belong to the\n  operator-side `OpenClaw.Cli.exe`, not `winnode.exe`.\n- Idempotency. The gateway de-dupes retries against `--idempotency-key`; local\n  MCP does not. Retrying a `system.run` \u002F `system.notify` \u002F `canvas.present`\n  call after a transient failure can double-execute the side effect.\n- Wildcards in `--command`. The MCP server has an explicit allowlist; unknown\n  commands return `Unknown tool: \u003Cname>`.\n",{"name":5,"description":7},"main","a6ff6a164b20835bd5d77200c9134578dd7ce0c6",".agents\u002Fskills\u002Fwinnode",348,"https:\u002F\u002Fgithub.com\u002Fopenclaw\u002Fopenclaw-windows-node","https:\u002F\u002Fopenclaw.ai","https:\u002F\u002Fgithub.com\u002Fopenclaw","Your personal, open source AI assistant.","2026-11-14 02:54:19",{"source":8,"skillName":5,"commands":52,"prompt":62,"zipAvailable":63},[53,56,59],{"tool":54,"command":55},"npx","npx skills add openclaw\u002Fopenclaw-windows-node --skill winnode",{"tool":57,"command":58},"bunx","bunx skills add openclaw\u002Fopenclaw-windows-node --skill winnode",{"tool":60,"command":61},"pnpm","pnpm dlx skills add openclaw\u002Fopenclaw-windows-node --skill winnode","安装技能，技能地址：https:\u002F\u002Fgithub.com\u002Fopenclaw\u002Fopenclaw-windows-node\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fwinnode\u002FSKILL.md",true,[],[66,91,108,127,146,164],{"skillNo":67,"name":68,"nameZh":6,"description":69,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":70,"categories":71,"keywords":72,"publishedAt":37},"01M6KZCXK5PCYWWW5FCVDYPAXK","retrigger-flaky-ci","Triage failed CI on a pull request or the active branch, decide whether the failures come from its changes, and rerun only failures that are unrelated. Use when asked to retrigger, rerun, or kick CI on a PR with failing checks.","https:\u002F\u002Fgithub.com\u002Fopenclaw\u002Fopenclaw-windows-node\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fretrigger-flaky-ci\u002FSKILL.md",[],[73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90],"retrigger flaky ci","ci","when","unrelated","failing","rerun","use","triage","only","decide","request","active","pull request","pr","changes","branch","with","pull",{"skillNo":92,"name":93,"nameZh":6,"description":94,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":95,"categories":96,"keywords":97,"publishedAt":37},"01M698QMQN5TQES4R7ZPTYAZF5","technical-documentation","Write, review, or restructure Windows node documentation and agent instructions using current source, task-focused explanations, and explicit fact preservation.","https:\u002F\u002Fgithub.com\u002Fopenclaw\u002Fopenclaw-windows-node\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Ftechnical-documentation\u002FSKILL.md",[],[98,99,100,101,34,102,103,30,104,105,106,107],"technical documentation","technical","documentation","agent instructions","source","preservation","write","instructions","review","task",{"skillNo":109,"name":110,"nameZh":6,"description":111,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":112,"categories":113,"keywords":114,"publishedAt":37},"01M698QKPXJ8ZG34MYHPPCAT4T","pr-simplify","Simplify your own Windows-node change to its smallest complete fix while preserving distinct regressions, platform contracts, and required proof. Use after implementation before PR preparation, after large repairs, or when asked to shrink a change or remove slop.","https:\u002F\u002Fgithub.com\u002Fopenclaw\u002Fopenclaw-windows-node\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fpr-simplify\u002FSKILL.md",[],[115,86,116,117,118,119,120,121,30,122,75,79,123,124,125,34,126],"pr simplify","simplify","contracts","slop","proof","implementation","preparation","change","fix","complete","remove","platform",{"skillNo":128,"name":129,"nameZh":6,"description":130,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":131,"categories":132,"keywords":136,"publishedAt":37},"01M698QJEA384YPM8R9CPZ1YFD","openclaw-testing","Choose proportional OpenClaw Windows node tests, builds, packaging checks, and runtime proof, then diagnose failures without weakening required gates.","https:\u002F\u002Fgithub.com\u002Fopenclaw\u002Fopenclaw-windows-node\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fopenclaw-testing\u002FSKILL.md",[133],{"categoryNo":134,"categoryName":135,"taxonomyType":20,"taxonomyTypeDesc":21},"01JBSEEDCAT000000000000008","测试与质量",[137,9,138,34,119,30,139,140,141,142,143,144,145],"testing","openclaw testing","gates","runtime proof","tests","choose","packaging","runtime","diagnose",{"skillNo":147,"name":148,"nameZh":6,"description":149,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":150,"categories":151,"keywords":153,"publishedAt":37},"01M698QH7JVVA09769ER9P2TKY","openclaw-proof-validation","Plan and collect OpenClaw Windows validation\u002Fproof: tests, rubber-duck review, UI evidence, MCP output, and gateway runtime proof.","https:\u002F\u002Fgithub.com\u002Fopenclaw\u002Fopenclaw-windows-node\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fopenclaw-proof-validation\u002FSKILL.md",[152],{"categoryNo":18,"categoryName":19,"taxonomyType":20,"taxonomyTypeDesc":21},[154,9,155,119,156,34,157,23,144,158,159,160,161,162,106,140,141,163],"openclaw proof validation","validation","evidence","gateway","duck","ui","ui evidence","plan","output","collect",{"skillNo":165,"name":166,"nameZh":6,"description":167,"descriptionZh":6,"repositoryFullName":8,"ownerLogin":9,"ownerAvatarUrl":10,"ownerAccountNo":11,"ownerDisplayName":9,"repositoryNo":12,"starsCount":13,"licenseSpdxId":14,"licenseFileUrl":6,"sourceHtmlUrl":168,"categories":169,"keywords":175,"publishedAt":37},"01M698QFXW9PDK8YZ467MBW5P1","openclaw-debugging","Debug OpenClaw Windows Companion, setup, Gateway connection, node capabilities, packaged permissions, updates, installers, and system.run by selecting the failing boundary and proof path before changing code.","https:\u002F\u002Fgithub.com\u002Fopenclaw\u002Fopenclaw-windows-node\u002Fblob\u002Fmain\u002F.agents\u002Fskills\u002Fopenclaw-debugging\u002FSKILL.md",[170],{"categoryNo":171,"categoryName":172,"taxonomyType":173,"taxonomyTypeDesc":174},"01JBSEEDCAT000000000000030","开发工程师","PROFESSION","适用职业",[176,177,9,34,178,119,179,180,181,182,157,183,184,30,185,186,187,188,189,77],"openclaw debugging","debugging","setup","connection","boundary","debug","system","path","companion","run","permissions","capabilities","code","updates"]